sdext/source/pdfimport/inc/genericelements.hxx | 14 +++++++ sdext/source/pdfimport/inc/treevisiting.hxx | 2 + sdext/source/pdfimport/tree/drawtreevisiting.cxx | 37 +++++++++++++++++++++ sdext/source/pdfimport/tree/drawtreevisiting.hxx | 3 + sdext/source/pdfimport/tree/genericelements.cxx | 6 +++ sdext/source/pdfimport/tree/pdfiprocessor.cxx | 17 +++++++++ sdext/source/pdfimport/tree/writertreevisiting.cxx | 36 ++++++++++++++++++++ sdext/source/pdfimport/tree/writertreevisiting.hxx | 3 + 8 files changed, 117 insertions(+), 1 deletion(-)
New commits: commit 2383fae366c787bc6e08cf5439521674d52fd8c1 Author: Dr. David Alan Gilbert <[email protected]> AuthorDate: Sun Jul 27 21:41:14 2025 +0100 Commit: David Gilbert <[email protected]> CommitDate: Sat Aug 9 15:54:58 2025 +0200 tdf#44729 sdext,pdfimport: Hide transparency groups 'for soft mask' In PDF, 'Transparency groups' are used to group objects for compositing. One variant of those is a transparency group that doesn't actually get directly composited, but is used as a 'soft mask' to modulate the effects of another group when the other group is composited. So far we've not done anything for transparency groups, where softmasks are used that means the softmask gets rendered as a visible object - often a big semi-transparent polygon covering the rest. Add 'group' elements into the tree for Transparency Groups and mark them. During tree walks skip the 'for soft mask' transparency groups. This improves tdf#44729 which prior to this patch is mostly seen as a big black page because it's covered with the soft mask misrendering. Since it also stored the transparency group it also means we can attack the real rendering of those later. Change-Id: Ieb1eb56b15d8def5c842c1d84349173ccca630dd Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188455 Reviewed-by: David Gilbert <[email protected]> Tested-by: Jenkins diff --git a/sdext/source/pdfimport/inc/genericelements.hxx b/sdext/source/pdfimport/inc/genericelements.hxx index d861617fc6a4..2f624c388562 100644 --- a/sdext/source/pdfimport/inc/genericelements.hxx +++ b/sdext/source/pdfimport/inc/genericelements.hxx @@ -178,6 +178,8 @@ namespace pdfi public: virtual void visitedBy( ElementTreeVisitor&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; + bool isTransparencyGroup; + bool isForSoftMask; }; struct TextElement final : public GraphicalElement diff --git a/sdext/source/pdfimport/tree/drawtreevisiting.cxx b/sdext/source/pdfimport/tree/drawtreevisiting.cxx index f9cf67bf179c..0abf16067849 100644 --- a/sdext/source/pdfimport/tree/drawtreevisiting.cxx +++ b/sdext/source/pdfimport/tree/drawtreevisiting.cxx @@ -279,6 +279,14 @@ void DrawXmlEmitter::visit( FrameElement& elem, const std::list< std::unique_ptr void DrawXmlEmitter::visit(GroupElement& elem, const std::list< std::unique_ptr<Element> >::const_iterator&) { + if (elem.isTransparencyGroup && elem.isForSoftMask) + { + // Hack: We don't have a way to do TG or SoftMask yet, but avoiding + // rendering the geometry that's only used for generating the soft + // mask generally gives us better results + return; + } + elem.applyToChildren(*this); } @@ -411,6 +419,13 @@ void DrawXmlOptimizer::visit( FrameElement& elem, const std::list< std::unique_p void DrawXmlOptimizer::visit(GroupElement& elem, const std::list< std::unique_ptr<Element> >::const_iterator&) { + if (elem.isTransparencyGroup && elem.isForSoftMask) + { + // Hack: We don't have a way to do TG or SoftMask yet, but avoiding + // rendering the geometry that's only used for generating the soft + // mask generally gives us better results + return; + } elem.applyToChildren(*this); } @@ -1018,6 +1033,13 @@ void DrawXmlFinalizer::visit( FrameElement& elem, const std::list< std::unique_p void DrawXmlFinalizer::visit(GroupElement& elem, const std::list< std::unique_ptr<Element> >::const_iterator&) { + if (elem.isTransparencyGroup && elem.isForSoftMask) + { + // Hack: We don't have a way to do TG or SoftMask yet, but avoiding + // rendering the geometry that's only used for generating the soft + // mask generally gives us better results + return; + } elem.applyToChildren(*this); } diff --git a/sdext/source/pdfimport/tree/pdfiprocessor.cxx b/sdext/source/pdfimport/tree/pdfiprocessor.cxx index a28b51c37564..3e5bd4215f86 100644 --- a/sdext/source/pdfimport/tree/pdfiprocessor.cxx +++ b/sdext/source/pdfimport/tree/pdfiprocessor.cxx @@ -585,12 +585,27 @@ void PDFIProcessor::intersectClipToStroke(const uno::Reference< rendering::XPoly getCurrentContext().Clip = std::move(aNewClip); } -void PDFIProcessor::beginTransparencyGroup(const bool /*bForSoftMask*/) +void PDFIProcessor::beginTransparencyGroup(const bool bForSoftMask) { + const GraphicsContext& rGC(getCurrentContext()); + const sal_Int32 nGCId = getGCId(rGC); + GroupElement* pGroup = ElementFactory::createGroupElement( m_pCurElement, nGCId ); + pGroup->ZOrder = m_nNextZOrder++; + pGroup->isTransparencyGroup = true; + pGroup->isForSoftMask = bForSoftMask; + pGroup->w = 0; + pGroup->h = 0; + m_pCurElement = pGroup; + } void PDFIProcessor::endTransparencyGroup() { + GroupElement* pGroup = dynamic_cast<GroupElement*>(m_pCurElement); + if( pGroup ) + { + m_pCurElement = pGroup->Parent; + } } void PDFIProcessor::hyperLink( const geometry::RealRectangle2D& rBounds, diff --git a/sdext/source/pdfimport/tree/writertreevisiting.cxx b/sdext/source/pdfimport/tree/writertreevisiting.cxx index cdd9b0cac27e..e267a562c83d 100644 --- a/sdext/source/pdfimport/tree/writertreevisiting.cxx +++ b/sdext/source/pdfimport/tree/writertreevisiting.cxx @@ -304,6 +304,13 @@ void WriterXmlEmitter::visit( FrameElement& elem, const std::list< std::unique_p void WriterXmlEmitter::visit(GroupElement& elem, const std::list< std::unique_ptr<Element> >::const_iterator&) { + if (elem.isTransparencyGroup && elem.isForSoftMask) + { + // Hack: We don't have a way to do TG or SoftMask yet, but avoiding + // rendering the geometry that's only used for generating the soft + // mask generally gives us better results + return; + } elem.applyToChildren(*this); } @@ -442,6 +449,13 @@ void WriterXmlOptimizer::visit( FrameElement& elem, const std::list< std::unique void WriterXmlOptimizer::visit(GroupElement& elem, const std::list< std::unique_ptr<Element> >::const_iterator&) { + if (elem.isTransparencyGroup && elem.isForSoftMask) + { + // Hack: We don't have a way to do TG or SoftMask yet, but avoiding + // rendering the geometry that's only used for generating the soft + // mask generally gives us better results + return; + } elem.applyToChildren(*this); } @@ -1201,6 +1215,13 @@ void WriterXmlFinalizer::visit( FrameElement& elem, const std::list< std::unique void WriterXmlFinalizer::visit(GroupElement& elem, const std::list< std::unique_ptr<Element> >::const_iterator&) { + if (elem.isTransparencyGroup && elem.isForSoftMask) + { + // Hack: We don't have a way to do TG or SoftMask yet, but avoiding + // rendering the geometry that's only used for generating the soft + // mask generally gives us better results + return; + } elem.applyToChildren(*this); } commit e7503531396fd7d47eae4887422a61bb4da5d58e Author: Dr. David Alan Gilbert <[email protected]> AuthorDate: Sun Jul 27 21:36:25 2025 +0100 Commit: David Gilbert <[email protected]> CommitDate: Sat Aug 9 15:54:49 2025 +0200 tdf#44729 sdext,pdfimport: Add Group elements Add a new tree element 'group' which represents a collection of other elements but doesn't necessarily give a specific output node. Later patches will add some flags and use those to change the output. Change-Id: I981d345873436f6f9ffc9de67123f38de1e3d86a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188454 Tested-by: Jenkins Reviewed-by: David Gilbert <[email protected]> diff --git a/sdext/source/pdfimport/inc/genericelements.hxx b/sdext/source/pdfimport/inc/genericelements.hxx index 13350df21796..d861617fc6a4 100644 --- a/sdext/source/pdfimport/inc/genericelements.hxx +++ b/sdext/source/pdfimport/inc/genericelements.hxx @@ -170,6 +170,16 @@ namespace pdfi virtual void visitedBy( ElementTreeVisitor&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; }; + struct GroupElement final : public DrawElement + { + friend class ElementFactory; + GroupElement( Element* pParent, sal_Int32 nGCId ) + : DrawElement( pParent, nGCId ) {} + + public: + virtual void visitedBy( ElementTreeVisitor&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; + }; + struct TextElement final : public GraphicalElement { friend class ElementFactory; @@ -298,6 +308,8 @@ namespace pdfi static FrameElement* createFrameElement( Element* pParent, sal_Int32 nGCId ) { return new FrameElement( pParent, nGCId ); } + static GroupElement* createGroupElement( Element* pParent, sal_Int32 nGCId ) + { return new GroupElement( pParent, nGCId ); } static PolyPolyElement* createPolyPolyElement( Element* pParent, sal_Int32 nGCId, diff --git a/sdext/source/pdfimport/inc/treevisiting.hxx b/sdext/source/pdfimport/inc/treevisiting.hxx index 120166594af3..45407dfea789 100644 --- a/sdext/source/pdfimport/inc/treevisiting.hxx +++ b/sdext/source/pdfimport/inc/treevisiting.hxx @@ -30,6 +30,7 @@ namespace pdfi struct TextElement; struct ParagraphElement; struct FrameElement; + struct GroupElement; struct PolyPolyElement; struct ImageElement; struct PageElement; @@ -47,6 +48,7 @@ namespace pdfi virtual void visit( TextElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) = 0; virtual void visit( ParagraphElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) = 0; virtual void visit( FrameElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) = 0; + virtual void visit( GroupElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) = 0; virtual void visit( PolyPolyElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) = 0; virtual void visit( ImageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) = 0; virtual void visit( PageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) = 0; diff --git a/sdext/source/pdfimport/tree/drawtreevisiting.cxx b/sdext/source/pdfimport/tree/drawtreevisiting.cxx index d99d32bbfa50..f9cf67bf179c 100644 --- a/sdext/source/pdfimport/tree/drawtreevisiting.cxx +++ b/sdext/source/pdfimport/tree/drawtreevisiting.cxx @@ -277,6 +277,11 @@ void DrawXmlEmitter::visit( FrameElement& elem, const std::list< std::unique_ptr m_rEmitContext.rEmitter.endTag( "draw:frame" ); } +void DrawXmlEmitter::visit(GroupElement& elem, const std::list< std::unique_ptr<Element> >::const_iterator&) +{ + elem.applyToChildren(*this); +} + void DrawXmlEmitter::visit( PolyPolyElement& elem, const std::list< std::unique_ptr<Element> >::const_iterator& ) { elem.updateGeometry(); @@ -404,6 +409,11 @@ void DrawXmlOptimizer::visit( FrameElement& elem, const std::list< std::unique_p elem.applyToChildren(*this); } +void DrawXmlOptimizer::visit(GroupElement& elem, const std::list< std::unique_ptr<Element> >::const_iterator&) +{ + elem.applyToChildren(*this); +} + void DrawXmlOptimizer::visit( ImageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) { } @@ -1006,6 +1016,11 @@ void DrawXmlFinalizer::visit( FrameElement& elem, const std::list< std::unique_p elem.applyToChildren(*this); } +void DrawXmlFinalizer::visit(GroupElement& elem, const std::list< std::unique_ptr<Element> >::const_iterator&) +{ + elem.applyToChildren(*this); +} + void DrawXmlFinalizer::visit( ImageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) { } diff --git a/sdext/source/pdfimport/tree/drawtreevisiting.hxx b/sdext/source/pdfimport/tree/drawtreevisiting.hxx index e3ea8e537ff5..9230d8cfedc5 100644 --- a/sdext/source/pdfimport/tree/drawtreevisiting.hxx +++ b/sdext/source/pdfimport/tree/drawtreevisiting.hxx @@ -47,6 +47,7 @@ namespace pdfi virtual void visit( TextElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( ParagraphElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( FrameElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; + virtual void visit( GroupElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( PolyPolyElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( ImageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( PageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; @@ -70,6 +71,7 @@ namespace pdfi virtual void visit( TextElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( ParagraphElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( FrameElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; + virtual void visit( GroupElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( PolyPolyElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( ImageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( PageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; @@ -103,6 +105,7 @@ namespace pdfi virtual void visit( TextElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( ParagraphElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( FrameElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; + virtual void visit( GroupElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( PolyPolyElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( ImageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( PageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; diff --git a/sdext/source/pdfimport/tree/genericelements.cxx b/sdext/source/pdfimport/tree/genericelements.cxx index 4f9d52706884..fa6d951e5da2 100644 --- a/sdext/source/pdfimport/tree/genericelements.cxx +++ b/sdext/source/pdfimport/tree/genericelements.cxx @@ -115,6 +115,12 @@ void FrameElement::visitedBy( ElementTreeVisitor& rVisi rVisitor.visit(*this,rParentIt); } +void GroupElement::visitedBy( ElementTreeVisitor& rVisitor, + const std::list< std::unique_ptr<Element> >::const_iterator& rParentIt ) +{ + rVisitor.visit(*this,rParentIt); +} + void ImageElement::visitedBy( ElementTreeVisitor& rVisitor, const std::list< std::unique_ptr<Element> >::const_iterator& rParentIt) { diff --git a/sdext/source/pdfimport/tree/writertreevisiting.cxx b/sdext/source/pdfimport/tree/writertreevisiting.cxx index 1c5e4c903dce..cdd9b0cac27e 100644 --- a/sdext/source/pdfimport/tree/writertreevisiting.cxx +++ b/sdext/source/pdfimport/tree/writertreevisiting.cxx @@ -302,6 +302,11 @@ void WriterXmlEmitter::visit( FrameElement& elem, const std::list< std::unique_p m_rEmitContext.rEmitter.endTag( "draw:frame" ); } +void WriterXmlEmitter::visit(GroupElement& elem, const std::list< std::unique_ptr<Element> >::const_iterator&) +{ + elem.applyToChildren(*this); +} + void WriterXmlEmitter::visit( PolyPolyElement& elem, const std::list< std::unique_ptr<Element> >::const_iterator& ) { elem.updateGeometry(); @@ -435,6 +440,11 @@ void WriterXmlOptimizer::visit( FrameElement& elem, const std::list< std::unique elem.applyToChildren(*this); } +void WriterXmlOptimizer::visit(GroupElement& elem, const std::list< std::unique_ptr<Element> >::const_iterator&) +{ + elem.applyToChildren(*this); +} + void WriterXmlOptimizer::visit( ImageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) { } @@ -1189,6 +1199,11 @@ void WriterXmlFinalizer::visit( FrameElement& elem, const std::list< std::unique elem.applyToChildren(*this); } +void WriterXmlFinalizer::visit(GroupElement& elem, const std::list< std::unique_ptr<Element> >::const_iterator&) +{ + elem.applyToChildren(*this); +} + void WriterXmlFinalizer::visit( ImageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) { } diff --git a/sdext/source/pdfimport/tree/writertreevisiting.hxx b/sdext/source/pdfimport/tree/writertreevisiting.hxx index b0644dd48154..e24d4f329007 100644 --- a/sdext/source/pdfimport/tree/writertreevisiting.hxx +++ b/sdext/source/pdfimport/tree/writertreevisiting.hxx @@ -49,6 +49,7 @@ namespace pdfi virtual void visit( TextElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( ParagraphElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( FrameElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; + virtual void visit( GroupElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( PolyPolyElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( ImageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( PageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; @@ -76,6 +77,7 @@ namespace pdfi virtual void visit( TextElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( ParagraphElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( FrameElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; + virtual void visit( GroupElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( PolyPolyElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( ImageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( PageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; @@ -101,6 +103,7 @@ namespace pdfi virtual void visit( TextElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( ParagraphElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( FrameElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; + virtual void visit( GroupElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( PolyPolyElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( ImageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override; virtual void visit( PageElement&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override;
