diff options
author | Armin.Le.Grand (CIB) <Armin.Le.Grand@me.com> | 2020-03-03 11:34:45 +0100 |
---|---|---|
committer | Thorsten Behrens <thorsten.behrens@allotropia.de> | 2021-04-09 23:21:27 +0200 |
commit | 1a9ef8b0e3fb77944dcd7223f90345645c7e7410 (patch) | |
tree | 5567044a35317d3405e69183afc2c83a79998ff9 /vcl | |
parent | 84092ae74d5ebb0685a6fe6b69cc7a544478b197 (diff) |
tdf#130150 Improve clipping in PDF export
For more info and discusson please have a look
at the task. It reverts the change from tdf#99680
which did a wrong paradigm change in how clip in
Region(s) is defined and tries to fix the
underlying error in a more correct way.
This includes problems noted in tdf#44388 and
tdf#113449.
This is a decent improvement, but - due to dealing
with numerical problems - not yet the whole healing.
Still thinking about how to solve this for good.
Adapted PdfExportTest::testTdf99680() and
PdfExportTest::testTdf99680_2() as needed, empty
clip regions are allowed again. Added comments, too.
Had to change solvePolygonOperationAnd to work
on ranges if both inputs *are* ranges. The AND-case
is then completely solvable. Also increased geometry
for transformations of clip geometries - may help
later.
Change-Id: I2370447597faa6efb81d58ee31c63654e304262e
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/89874
Tested-by: Jenkins
Reviewed-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/qa/cppunit/pdfexport/pdfexport.cxx | 28 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.cxx | 54 |
2 files changed, 56 insertions, 26 deletions
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx index f46f94b10b4c..ff5f877b050b 100644 --- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx +++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx @@ -811,15 +811,17 @@ void PdfExportTest::testTdf99680() aZCodec.Decompress(rObjectStream, aUncompressed); CPPUNIT_ASSERT(aZCodec.EndCompression()); - // Make sure there are no empty clipping regions. - OString aEmptyRegion("0 0 m h W* n"); - auto pStart = static_cast<const char*>(aUncompressed.GetData()); - const char* pEnd = pStart + aUncompressed.GetSize(); - auto it = std::search(pStart, pEnd, aEmptyRegion.getStr(), aEmptyRegion.getStr() + aEmptyRegion.getLength()); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Empty clipping region detected!", it, pEnd); + // tdf#130150 See infos in task - short: tdf#99680 was not the + // correct fix, so empty clip regions are valid - allow again in tests + // Make sure there are no empty clipping regions. + // OString aEmptyRegion("0 0 m h W* n"); + // auto it = std::search(pStart, pEnd, aEmptyRegion.getStr(), aEmptyRegion.getStr() + aEmptyRegion.getLength()); + // CPPUNIT_ASSERT_EQUAL_MESSAGE("Empty clipping region detected!", it, pEnd); // Count save graphic state (q) and restore (Q) operators // and ensure their amount is equal + auto pStart = static_cast<const char*>(aUncompressed.GetData()); + const char* pEnd = pStart + aUncompressed.GetSize(); size_t nSaveCount = std::count(pStart, pEnd, 'q'); size_t nRestoreCount = std::count(pStart, pEnd, 'Q'); CPPUNIT_ASSERT_EQUAL_MESSAGE("Save/restore graphic state operators count mismatch!", nSaveCount, nRestoreCount); @@ -850,15 +852,17 @@ void PdfExportTest::testTdf99680_2() aZCodec.Decompress(rObjectStream, aUncompressed); CPPUNIT_ASSERT(aZCodec.EndCompression()); - // Make sure there are no empty clipping regions. - OString aEmptyRegion("0 0 m h W* n"); - auto pStart = static_cast<const char*>(aUncompressed.GetData()); - const char* pEnd = pStart + aUncompressed.GetSize(); - auto it = std::search(pStart, pEnd, aEmptyRegion.getStr(), aEmptyRegion.getStr() + aEmptyRegion.getLength()); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Empty clipping region detected!", it, pEnd); + // tdf#130150 See infos in task - short: tdf#99680 was not the + // correct fix, so empty clip regions are valid - allow again in tests + // Make sure there are no empty clipping regions. + // OString aEmptyRegion("0 0 m h W* n"); + // auto it = std::search(pStart, pEnd, aEmptyRegion.getStr(), aEmptyRegion.getStr() + aEmptyRegion.getLength()); + // CPPUNIT_ASSERT_EQUAL_MESSAGE("Empty clipping region detected!", it, pEnd); // Count save graphic state (q) and restore (Q) operators // and ensure their amount is equal + auto pStart = static_cast<const char*>(aUncompressed.GetData()); + const char* pEnd = pStart + aUncompressed.GetSize(); size_t nSaveCount = std::count(pStart, pEnd, 'q'); size_t nRestoreCount = std::count(pStart, pEnd, 'Q'); CPPUNIT_ASSERT_EQUAL_MESSAGE("Save/restore graphic state operators count mismatch!", nSaveCount, nRestoreCount); diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index e1186fa22f32..1a5f772af2e5 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -9849,8 +9849,15 @@ void PDFWriterImpl::updateGraphicsState(Mode const mode) if ( rNewState.m_aClipRegion.count() ) { m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine ); - aLine.append( "W* n\n" ); } + else + { + // tdf#130150 Need to revert tdf#99680, that breaks the + // rule that an set but empty clip-region clips everything + // aka draws nothing -> nothing is in an empty clip-region + aLine.append( "0 0 m h " ); // NULL clip, i.e. nothing visible + } + aLine.append( "W* n\n" ); rNewState.m_aMapMode = aNewMapMode; SetMapMode( rNewState.m_aMapMode ); @@ -9995,8 +10002,12 @@ void PDFWriterImpl::setMapMode( const MapMode& rMapMode ) void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion ) { - basegfx::B2DPolyPolygon aRegion = LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ); - aRegion = PixelToLogic( aRegion, m_aMapMode ); + // tdf#130150 improve coordinate manipulations to double precision transformations + const basegfx::B2DHomMatrix aCurrentTransform( + GetInverseViewTransformation(m_aMapMode) * GetViewTransformation(m_aGraphicsStack.front().m_aMapMode)); + basegfx::B2DPolyPolygon aRegion(rRegion); + + aRegion.transform(aCurrentTransform); m_aGraphicsStack.front().m_aClipRegion = aRegion; m_aGraphicsStack.front().m_bClipRegion = true; m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsStateUpdateFlags::ClipRegion; @@ -10006,16 +10017,26 @@ void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY ) { if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() ) { - Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode, - m_aMapMode, - this, - Point( nX, nY ) ) ); - aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode, - m_aMapMode, - this, - Point() ); + // tdf#130150 improve coordinate manipulations to double precision transformations + basegfx::B2DHomMatrix aConvertA; + + if(MapUnit::MapPixel == m_aGraphicsStack.front().m_aMapMode.GetMapUnit()) + { + aConvertA = GetInverseViewTransformation(m_aMapMode); + } + else + { + aConvertA = LogicToLogic(m_aGraphicsStack.front().m_aMapMode, m_aMapMode); + } + + basegfx::B2DPoint aB2DPointA(nX, nY); + basegfx::B2DPoint aB2DPointB(0.0, 0.0); + aB2DPointA *= aConvertA; + aB2DPointB *= aConvertA; + aB2DPointA -= aB2DPointB; basegfx::B2DHomMatrix aMat; - aMat.translate( aPoint.X(), aPoint.Y() ); + + aMat.translate(aB2DPointA.getX(), aB2DPointA.getY()); m_aGraphicsStack.front().m_aClipRegion.transform( aMat ); m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsStateUpdateFlags::ClipRegion; } @@ -10030,9 +10051,14 @@ void PDFWriterImpl::intersectClipRegion( const tools::Rectangle& rRect ) void PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion ) { - basegfx::B2DPolyPolygon aRegion( LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) ); - aRegion = PixelToLogic( aRegion, m_aMapMode ); + // tdf#130150 improve coordinate manipulations to double precision transformations + const basegfx::B2DHomMatrix aCurrentTransform( + GetInverseViewTransformation(m_aMapMode) * GetViewTransformation(m_aGraphicsStack.front().m_aMapMode)); + basegfx::B2DPolyPolygon aRegion(rRegion); + + aRegion.transform(aCurrentTransform); m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsStateUpdateFlags::ClipRegion; + if( m_aGraphicsStack.front().m_bClipRegion ) { basegfx::B2DPolyPolygon aOld( basegfx::utils::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) ); |