summaryrefslogtreecommitdiff
path: root/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx')
-rw-r--r--drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx935
1 files changed, 560 insertions, 375 deletions
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index b679768c9c3b..d93d98fef51a 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -24,27 +24,30 @@
#include <rtl/ustring.hxx>
#include <tools/gen.hxx>
#include <tools/stream.hxx>
-#include <tools/diagnose_ex.h>
+#include <comphelper/diagnose_ex.hxx>
+#include <comphelper/flagguard.hxx>
#include <comphelper/processfactory.hxx>
#include <config_global.h>
#include <basegfx/polygon/b2dpolygonclipper.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dlinegeometry.hxx>
+#include <basegfx/utils/gradienttools.hxx>
#include <vcl/virdev.hxx>
#include <vcl/gdimtf.hxx>
#include <vcl/gradient.hxx>
#include <vcl/graphictools.hxx>
#include <vcl/metaact.hxx>
#include <vcl/graph.hxx> // for PDFExtOutDevData Graphic support
-#include <toolkit/helper/formpdfexport.hxx> // for PDFExtOutDevData Graphic support
+#include <vcl/formpdfexport.hxx> // for PDFExtOutDevData Graphic support
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
@@ -61,6 +64,9 @@
#include <drawinglayer/primitive2d/epsprimitive2d.hxx>
#include <drawinglayer/primitive2d/structuretagprimitive2d.hxx>
#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx> // for Title/Description metadata
+#include <drawinglayer/converters.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <tools/vcompat.hxx>
#include <com/sun/star/awt/XControl.hpp>
#include <com/sun/star/i18n/BreakIterator.hpp>
@@ -220,8 +226,10 @@ VclMetafileProcessor2D::impDumpToMetaFile(const primitive2d::Primitive2DContaine
aPrimitiveRange.transform(maCurrentTransformation);
const tools::Rectangle aPrimitiveRectangle(
- basegfx::fround(aPrimitiveRange.getMinX()), basegfx::fround(aPrimitiveRange.getMinY()),
- basegfx::fround(aPrimitiveRange.getMaxX()), basegfx::fround(aPrimitiveRange.getMaxY()));
+ basegfx::fround<tools::Long>(aPrimitiveRange.getMinX()),
+ basegfx::fround<tools::Long>(aPrimitiveRange.getMinY()),
+ basegfx::fround<tools::Long>(aPrimitiveRange.getMaxX()),
+ basegfx::fround<tools::Long>(aPrimitiveRange.getMaxY()));
ScopedVclPtrInstance<VirtualDevice> aContentVDev;
MapMode aNewMapMode(pLastOutputDevice->GetMapMode());
@@ -256,19 +264,20 @@ void VclMetafileProcessor2D::impConvertFillGradientAttributeToVCLGradient(
Gradient& o_rVCLGradient, const attribute::FillGradientAttribute& rFiGrAtt,
bool bIsTransparenceGradient) const
{
+ const basegfx::BColor aStartColor(rFiGrAtt.getColorStops().front().getStopColor());
+ const basegfx::BColor aEndColor(rFiGrAtt.getColorStops().back().getStopColor());
+
if (bIsTransparenceGradient)
{
// it's about transparence channel intensities (black/white), do not use color modifier
- o_rVCLGradient.SetStartColor(Color(rFiGrAtt.getStartColor()));
- o_rVCLGradient.SetEndColor(Color(rFiGrAtt.getEndColor()));
+ o_rVCLGradient.SetStartColor(Color(aStartColor));
+ o_rVCLGradient.SetEndColor(Color(aEndColor));
}
else
{
// use color modifier to influence start/end color of gradient
- o_rVCLGradient.SetStartColor(
- Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getStartColor())));
- o_rVCLGradient.SetEndColor(
- Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getEndColor())));
+ o_rVCLGradient.SetStartColor(Color(maBColorModifierStack.getModifiedColor(aStartColor)));
+ o_rVCLGradient.SetEndColor(Color(maBColorModifierStack.getModifiedColor(aEndColor)));
}
o_rVCLGradient.SetAngle(
@@ -281,40 +290,7 @@ void VclMetafileProcessor2D::impConvertFillGradientAttributeToVCLGradient(
// defaults for intensity; those were computed into the start/end colors already
o_rVCLGradient.SetStartIntensity(100);
o_rVCLGradient.SetEndIntensity(100);
-
- switch (rFiGrAtt.getStyle())
- {
- default: // attribute::GradientStyle::Linear :
- {
- o_rVCLGradient.SetStyle(GradientStyle::Linear);
- break;
- }
- case attribute::GradientStyle::Axial:
- {
- o_rVCLGradient.SetStyle(GradientStyle::Axial);
- break;
- }
- case attribute::GradientStyle::Radial:
- {
- o_rVCLGradient.SetStyle(GradientStyle::Radial);
- break;
- }
- case attribute::GradientStyle::Elliptical:
- {
- o_rVCLGradient.SetStyle(GradientStyle::Elliptical);
- break;
- }
- case attribute::GradientStyle::Square:
- {
- o_rVCLGradient.SetStyle(GradientStyle::Square);
- break;
- }
- case attribute::GradientStyle::Rect:
- {
- o_rVCLGradient.SetStyle(GradientStyle::Rect);
- break;
- }
- }
+ o_rVCLGradient.SetStyle(rFiGrAtt.getStyle());
}
void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill)
@@ -325,7 +301,7 @@ void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill const* pSvtGr
WriteSvtGraphicFill(aMemStm, *pSvtGraphicFill);
mpMetaFile->AddAction(new MetaCommentAction(
- "XPATHFILL_SEQ_BEGIN", 0, static_cast<const sal_uInt8*>(aMemStm.GetData()),
+ "XPATHFILL_SEQ_BEGIN"_ostr, 0, static_cast<const sal_uInt8*>(aMemStm.GetData()),
aMemStm.TellEnd()));
mnSvtGraphicFillCount++;
}
@@ -336,7 +312,7 @@ void VclMetafileProcessor2D::impEndSvtGraphicFill(SvtGraphicFill const* pSvtGrap
if (pSvtGraphicFill && mnSvtGraphicFillCount)
{
mnSvtGraphicFillCount--;
- mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END"));
+ mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END"_ostr));
}
}
@@ -509,7 +485,7 @@ void VclMetafileProcessor2D::impStartSvtGraphicStroke(SvtGraphicStroke const* pS
WriteSvtGraphicStroke(aMemStm, *pSvtGraphicStroke);
mpMetaFile->AddAction(new MetaCommentAction(
- "XPATHSTROKE_SEQ_BEGIN", 0, static_cast<const sal_uInt8*>(aMemStm.GetData()),
+ "XPATHSTROKE_SEQ_BEGIN"_ostr, 0, static_cast<const sal_uInt8*>(aMemStm.GetData()),
aMemStm.TellEnd()));
mnSvtGraphicStrokeCount++;
}
@@ -520,7 +496,7 @@ void VclMetafileProcessor2D::impEndSvtGraphicStroke(SvtGraphicStroke const* pSvt
if (pSvtGraphicStroke && mnSvtGraphicStrokeCount)
{
mnSvtGraphicStrokeCount--;
- mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END"));
+ mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END"_ostr));
}
}
@@ -546,7 +522,8 @@ void VclMetafileProcessor2D::popList()
}
// init static break iterator
-uno::Reference<css::i18n::XBreakIterator> VclMetafileProcessor2D::mxBreakIterator;
+vcl::DeleteOnDeinit<uno::Reference<css::i18n::XBreakIterator>>
+ VclMetafileProcessor2D::mxBreakIterator;
VclMetafileProcessor2D::VclMetafileProcessor2D(const geometry::ViewInformation2D& rViewInformation,
OutputDevice& rOutDev)
@@ -718,17 +695,7 @@ VclMetafileProcessor2D::~VclMetafileProcessor2D()
- UnoControlPDFExportContact is only created when PDFExtOutDevData is used at the
target and uno control data is created in UnoControlPDFExportContact::do_PaintObject.
- This may be added in primitive MetaFile renderer.
- Adding support...
- OOps, the necessary helper stuff is in svx/source/form/formpdxexport.cxx in namespace
- svxform. Have to talk to FS if this has to be like that. Especially since
- vcl::PDFWriter::AnyWidget is filled out, which is already part of vcl.
- Wrote an eMail to FS, he is on vacation currently. I see no reason why not to move
- that stuff to somewhere else, maybe tools or svtools ?!? We will see...
- Moved to toolkit, so I have to link against it. I tried VCL first, but it did
- not work since VCLUnoHelper::CreateFont is unresolved in VCL (!). Other than the name
- may imply, it is defined in toolkit (!). Since toolkit is linked against VCL itself,
- the lowest movement plane is toolkit.
+ This was added in primitive MetaFile renderer.
Checked form control export, it works well. Done.
- In goodies, in GraphicObject::Draw, when the used Graphic is linked, infos are
@@ -836,8 +803,17 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi
}
case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D:
{
- processPolyPolygonGraphicPrimitive2D(
- static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D&>(rCandidate));
+ if (maBColorModifierStack.count())
+ {
+ // tdf#151104 unfortunately processPolyPolygonGraphicPrimitive2D below
+ // does not support an active BColorModifierStack, so use the default
+ process(rCandidate);
+ }
+ else
+ {
+ processPolyPolygonGraphicPrimitive2D(
+ static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D&>(rCandidate));
+ }
break;
}
case PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D:
@@ -923,17 +899,10 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi
}
case PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D:
{
- RenderObjectInfoPrimitive2D(
+ processObjectInfoPrimitive2D(
static_cast<const primitive2d::ObjectInfoPrimitive2D&>(rCandidate));
break;
}
- case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D:
- case PRIMITIVE2D_ID_GLOWPRIMITIVE2D:
- case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D:
- {
- processPrimitive2DOnPixelProcessor(rCandidate);
- break;
- }
default:
{
// process recursively
@@ -943,6 +912,51 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi
}
}
+void VclMetafileProcessor2D::processObjectInfoPrimitive2D(
+ primitive2d::ObjectInfoPrimitive2D const& rObjectInfoPrimitive2D)
+{
+ // tdf#154982 process content first, so this object overrides any nested one
+ process(rObjectInfoPrimitive2D.getChildren());
+
+ // currently StructureTagPrimitive2D is only used for SdrObjects - have to
+ // avoid adding Alt text if the SdrObject is not actually tagged, as it
+ // would then end up on an unrelated structure element.
+ if (mpCurrentStructureTag && mpCurrentStructureTag->isTaggedSdrObject())
+ {
+ // Create image alternative description from ObjectInfoPrimitive2D info
+ // for PDF export, for the currently active SdrObject's structure element
+ if (mpPDFExtOutDevData->GetIsExportTaggedPDF())
+ {
+ OUString aAlternateDescription;
+
+ if (!rObjectInfoPrimitive2D.getTitle().isEmpty())
+ {
+ aAlternateDescription += rObjectInfoPrimitive2D.getTitle();
+ }
+
+ if (!rObjectInfoPrimitive2D.getDesc().isEmpty())
+ {
+ if (!aAlternateDescription.isEmpty())
+ {
+ aAlternateDescription += " - ";
+ }
+
+ aAlternateDescription += rObjectInfoPrimitive2D.getDesc();
+ }
+
+ // Use SetAlternateText to set it. This will work as long as some
+ // structure is used (see PDFWriterImpl::setAlternateText and
+ // m_nCurrentStructElement - tagged PDF export works with this in
+ // Draw/Impress/Writer, but not in Calc due to too less structure...)
+ //Z maybe add structure to Calc PDF export, may need some BeginGroup/EndGroup stuff ..?
+ if (!aAlternateDescription.isEmpty())
+ {
+ mpPDFExtOutDevData->SetAlternateText(aAlternateDescription);
+ }
+ }
+ }
+}
+
void VclMetafileProcessor2D::processGraphicPrimitive2D(
const primitive2d::GraphicPrimitive2D& rGraphicPrimitive)
{
@@ -1034,38 +1048,6 @@ void VclMetafileProcessor2D::processGraphicPrimitive2D(
sal_Int32(ceil(aCropRange.getMaxX())), sal_Int32(ceil(aCropRange.getMaxY())));
}
- // Create image alternative description from ObjectInfoPrimitive2D info
- // for PDF export
- if (mpPDFExtOutDevData->GetIsExportTaggedPDF() && nullptr != getObjectInfoPrimitive2D())
- {
- OUString aAlternateDescription;
-
- if (!getObjectInfoPrimitive2D()->getTitle().isEmpty())
- {
- aAlternateDescription += getObjectInfoPrimitive2D()->getTitle();
- }
-
- if (!getObjectInfoPrimitive2D()->getDesc().isEmpty())
- {
- if (!aAlternateDescription.isEmpty())
- {
- aAlternateDescription += " - ";
- }
-
- aAlternateDescription += getObjectInfoPrimitive2D()->getDesc();
- }
-
- // Use SetAlternateText to set it. This will work as long as some
- // structure is used (see PDFWriterImpl::setAlternateText and
- // m_nCurrentStructElement - tagged PDF export works with this in
- // Draw/Impress/Writer, but not in Calc due to too less structure...)
- //Z maybe add structure to Calc PDF export, may need some BeginGroup/EndGroup stuff ..?
- if (!aAlternateDescription.isEmpty())
- {
- mpPDFExtOutDevData->SetAlternateText(aAlternateDescription);
- }
- }
-
// #i123295# 3rd param is uncropped rect, 4th is cropped. The primitive has the cropped
// object transformation, thus aCurrentRect *is* the clip region while aCropRect is the expanded,
// uncropped region. Thus, correct order is aCropRect, aCurrentRect
@@ -1089,7 +1071,7 @@ void VclMetafileProcessor2D::processControlPrimitive2D(
uno::Reference<beans::XPropertySetInfo> xPropertyInfo(
xModelProperties.is() ? xModelProperties->getPropertySetInfo()
: uno::Reference<beans::XPropertySetInfo>());
- static const OUStringLiteral sPrintablePropertyName(u"Printable");
+ static constexpr OUString sPrintablePropertyName(u"Printable"_ustr);
if (xPropertyInfo.is() && xPropertyInfo->hasPropertyByName(sPrintablePropertyName))
{
@@ -1108,14 +1090,25 @@ void VclMetafileProcessor2D::processControlPrimitive2D(
if (!bIsPrintableControl)
return;
+ ::std::optional<sal_Int32> oAnchorParent;
+ if (mpPDFExtOutDevData)
+ {
+ if (rControlPrimitive.GetAnchorStructureElementKey())
+ {
+ sal_Int32 const id = mpPDFExtOutDevData->EnsureStructureElement(
+ rControlPrimitive.GetAnchorStructureElementKey());
+ oAnchorParent.emplace(mpPDFExtOutDevData->GetCurrentStructureElement());
+ mpPDFExtOutDevData->SetCurrentStructureElement(id);
+ }
+ }
+
const bool bPDFExport(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportFormFields());
bool bDoProcessRecursively(true);
+ bool bDecorative = (mpCurrentStructureTag && mpCurrentStructureTag->isDecorative());
- if (bPDFExport)
+ if (bPDFExport && !bDecorative)
{
// PDF export. Emulate data handling from UnoControlPDFExportContact
- // I have now moved describePDFControl to toolkit, thus i can implement the PDF
- // form control support now as follows
std::unique_ptr<vcl::PDFWriter::AnyWidget> pPDFControl(
::toolkitform::describePDFControl(rXControl, *mpPDFExtOutDevData));
@@ -1135,9 +1128,37 @@ void VclMetafileProcessor2D::processControlPrimitive2D(
mpOutputDevice->GetMapMode());
pPDFControl->TextFont.SetFontSize(aFontSize);
- mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Form);
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Form);
+ vcl::PDFWriter::StructAttributeValue role;
+ switch (pPDFControl->Type)
+ {
+ case vcl::PDFWriter::PushButton:
+ role = vcl::PDFWriter::Pb;
+ break;
+ case vcl::PDFWriter::RadioButton:
+ role = vcl::PDFWriter::Rb;
+ break;
+ case vcl::PDFWriter::CheckBox:
+ role = vcl::PDFWriter::Cb;
+ break;
+ default: // there is a paucity of roles, tv is the catch-all one
+ role = vcl::PDFWriter::Tv;
+ break;
+ }
+ // ISO 14289-1:2014, Clause: 7.18.4
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Role, role);
+ // ISO 14289-1:2014, Clause: 7.18.1
+ OUString const& rAltText(rControlPrimitive.GetAltText());
+ if (!rAltText.isEmpty())
+ {
+ mpPDFExtOutDevData->SetAlternateText(rAltText);
+ }
mpPDFExtOutDevData->CreateControl(*pPDFControl);
mpPDFExtOutDevData->EndStructureElement();
+ if (oAnchorParent)
+ {
+ mpPDFExtOutDevData->SetCurrentStructureElement(*oAnchorParent);
+ }
// no normal paint needed (see original UnoControlPDFExportContact::do_PaintObject);
// do not process recursively
@@ -1151,6 +1172,31 @@ void VclMetafileProcessor2D::processControlPrimitive2D(
}
}
+ if (!bDoProcessRecursively)
+ {
+ return;
+ }
+
+ if (mpPDFExtOutDevData)
+ { // no corresponding PDF Form, use Figure instead
+ if (!bDecorative)
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Figure);
+ else
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::NonStructElement);
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Placement, vcl::PDFWriter::Block);
+ auto const range(rControlPrimitive.getB2DRange(getViewInformation2D()));
+ tools::Rectangle const aLogicRect(basegfx::fround<tools::Long>(range.getMinX()),
+ basegfx::fround<tools::Long>(range.getMinY()),
+ basegfx::fround<tools::Long>(range.getMaxX()),
+ basegfx::fround<tools::Long>(range.getMaxY()));
+ mpPDFExtOutDevData->SetStructureBoundingBox(aLogicRect);
+ OUString const& rAltText(rControlPrimitive.GetAltText());
+ if (!rAltText.isEmpty() && !bDecorative)
+ {
+ mpPDFExtOutDevData->SetAlternateText(rAltText);
+ }
+ }
+
// #i93169# used flag the wrong way; true means that nothing was done yet
if (bDoProcessRecursively)
{
@@ -1195,6 +1241,15 @@ void VclMetafileProcessor2D::processControlPrimitive2D(
{
process(rControlPrimitive);
}
+
+ if (mpPDFExtOutDevData)
+ {
+ mpPDFExtOutDevData->EndStructureElement();
+ if (oAnchorParent)
+ {
+ mpPDFExtOutDevData->SetCurrentStructureElement(*oAnchorParent);
+ }
+ }
}
void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D(
@@ -1202,7 +1257,7 @@ void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D(
{
// support for FIELD_SEQ_BEGIN, FIELD_SEQ_END and URL. It wraps text primitives (but is not limited to)
// thus do the MetafileAction embedding stuff but just handle recursively.
- const OString aCommentStringCommon("FIELD_SEQ_BEGIN");
+ static constexpr OString aCommentStringCommon("FIELD_SEQ_BEGIN"_ostr);
OUString aURL;
switch (rFieldPrimitive.getType())
@@ -1214,7 +1269,7 @@ void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D(
}
case drawinglayer::primitive2d::FIELD_TYPE_PAGE:
{
- mpMetaFile->AddAction(new MetaCommentAction("FIELD_SEQ_BEGIN;PageField"));
+ mpMetaFile->AddAction(new MetaCommentAction("FIELD_SEQ_BEGIN;PageField"_ostr));
break;
}
case drawinglayer::primitive2d::FIELD_TYPE_URL:
@@ -1238,7 +1293,7 @@ void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D(
process(rContent);
// for the end comment the type is not relevant yet, they are all the same. Just add.
- mpMetaFile->AddAction(new MetaCommentAction("FIELD_SEQ_END"));
+ mpMetaFile->AddAction(new MetaCommentAction("FIELD_SEQ_END"_ostr));
if (!(mpPDFExtOutDevData
&& drawinglayer::primitive2d::FIELD_TYPE_URL == rFieldPrimitive.getType()))
@@ -1251,7 +1306,8 @@ void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D(
static_cast<sal_Int32>(ceil(aViewRange.getMaxX())),
static_cast<sal_Int32>(ceil(aViewRange.getMaxY())));
vcl::PDFExtOutDevBookmarkEntry aBookmark;
- aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic);
+ OUString const content(rFieldPrimitive.getValue("Representation"));
+ aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic, content);
aBookmark.aBookmark = aURL;
std::vector<vcl::PDFExtOutDevBookmarkEntry>& rBookmarks = mpPDFExtOutDevData->GetBookmarks();
rBookmarks.push_back(aBookmark);
@@ -1262,7 +1318,7 @@ void VclMetafileProcessor2D::processTextHierarchyLinePrimitive2D(
{
// process recursively and add MetaFile comment
process(rLinePrimitive);
- mpMetaFile->AddAction(new MetaCommentAction("XTEXT_EOL"));
+ mpMetaFile->AddAction(new MetaCommentAction("XTEXT_EOL"_ostr));
}
void VclMetafileProcessor2D::processTextHierarchyBulletPrimitive2D(
@@ -1272,14 +1328,14 @@ void VclMetafileProcessor2D::processTextHierarchyBulletPrimitive2D(
if (mbInListItem)
{
maListElements.push(vcl::PDFWriter::LILabel);
- mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::LILabel);
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::LILabel);
}
// process recursively and add MetaFile comment
process(rBulletPrimitive);
// in Outliner::PaintBullet(), a MetafileComment for bullets is added, too. The
// "XTEXT_EOC" is used, use here, too.
- mpMetaFile->AddAction(new MetaCommentAction("XTEXT_EOC"));
+ mpMetaFile->AddAction(new MetaCommentAction("XTEXT_EOC"_ostr));
if (mbInListItem)
{
@@ -1295,7 +1351,7 @@ void VclMetafileProcessor2D::processTextHierarchyBulletPrimitive2D(
void VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D(
const primitive2d::TextHierarchyParagraphPrimitive2D& rParagraphPrimitive)
{
- const OString aCommentString("XTEXT_EOP");
+ static constexpr OString aCommentString("XTEXT_EOP"_ostr);
static bool bSuppressPDFExtOutDevDataSupport(false); // loplugin:constvars:ignore
if (nullptr == mpPDFExtOutDevData || bSuppressPDFExtOutDevDataSupport)
@@ -1311,7 +1367,7 @@ void VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D(
{
// No Tagged PDF -> Dump as Paragraph
// Emulate data handling from old ImpEditEngine::Paint
- mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Paragraph);
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Paragraph);
// Process recursively and add MetaFile comment
process(rParagraphPrimitive);
@@ -1337,7 +1393,7 @@ void VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D(
for (sal_Int16 a(mnCurrentOutlineLevel); a != nNewOutlineLevel; ++a)
{
maListElements.push(vcl::PDFWriter::List);
- mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::List);
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::List);
}
}
else // if(nNewOutlineLevel < mnCurrentOutlineLevel)
@@ -1368,13 +1424,13 @@ void VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D(
{
// Dump as ListItem
maListElements.push(vcl::PDFWriter::ListItem);
- mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::ListItem);
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::ListItem);
mbInListItem = true;
}
else
{
// Dump as Paragraph
- mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Paragraph);
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Paragraph);
}
// Process recursively and add MetaFile comment
@@ -1391,7 +1447,7 @@ void VclMetafileProcessor2D::processTextHierarchyBlockPrimitive2D(
const primitive2d::TextHierarchyBlockPrimitive2D& rBlockPrimitive)
{
// add MetaFile comment, process recursively and add MetaFile comment
- mpMetaFile->AddAction(new MetaCommentAction("XTEXT_PAINTSHAPE_BEGIN"));
+ mpMetaFile->AddAction(new MetaCommentAction("XTEXT_PAINTSHAPE_BEGIN"_ostr));
process(rBlockPrimitive);
if (mnCurrentOutlineLevel >= 0)
@@ -1403,7 +1459,7 @@ void VclMetafileProcessor2D::processTextHierarchyBlockPrimitive2D(
}
}
- mpMetaFile->AddAction(new MetaCommentAction("XTEXT_PAINTSHAPE_END"));
+ mpMetaFile->AddAction(new MetaCommentAction("XTEXT_PAINTSHAPE_END"_ostr));
}
void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D(
@@ -1418,7 +1474,7 @@ void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D(
if (mbInListItem && mbBulletPresent)
{
maListElements.push(vcl::PDFWriter::LIBody);
- mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::LIBody);
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::LIBody);
}
// directdraw of text simple portion; use default processing
@@ -1433,12 +1489,13 @@ void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D(
// #i101169# if(pTextDecoratedCandidate)
{
// support for TEXT_ MetaFile actions only for decorated texts
- if (!mxBreakIterator.is())
+ if (!mxBreakIterator.get() || !mxBreakIterator.get()->get())
{
uno::Reference<uno::XComponentContext> xContext(
::comphelper::getProcessComponentContext());
- mxBreakIterator = i18n::BreakIterator::create(xContext);
+ mxBreakIterator.set(i18n::BreakIterator::create(xContext));
}
+ auto& rBreakIterator = *mxBreakIterator.get()->get();
const OUString& rTxt = rTextCandidate.getText();
const sal_Int32 nTextLength(rTextCandidate.getTextLength()); // rTxt.getLength());
@@ -1449,16 +1506,16 @@ void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D(
const sal_Int32 nTextPosition(rTextCandidate.getTextPosition());
sal_Int32 nDone;
- sal_Int32 nNextCellBreak(mxBreakIterator->nextCharacters(
+ sal_Int32 nNextCellBreak(rBreakIterator.nextCharacters(
rTxt, nTextPosition, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 0,
nDone));
- css::i18n::Boundary nNextWordBoundary(mxBreakIterator->getWordBoundary(
+ css::i18n::Boundary nNextWordBoundary(rBreakIterator.getWordBoundary(
rTxt, nTextPosition, rLocale, css::i18n::WordType::ANY_WORD, true));
sal_Int32 nNextSentenceBreak(
- mxBreakIterator->endOfSentence(rTxt, nTextPosition, rLocale));
- const OString aCommentStringA("XTEXT_EOC");
- const OString aCommentStringB("XTEXT_EOW");
- const OString aCommentStringC("XTEXT_EOS");
+ rBreakIterator.endOfSentence(rTxt, nTextPosition, rLocale));
+ static constexpr OStringLiteral aCommentStringA("XTEXT_EOC");
+ static constexpr OStringLiteral aCommentStringB("XTEXT_EOW");
+ static constexpr OStringLiteral aCommentStringC("XTEXT_EOS");
for (sal_Int32 i(nTextPosition); i < nTextPosition + nTextLength; i++)
{
@@ -1467,21 +1524,21 @@ void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D(
{
mpMetaFile->AddAction(
new MetaCommentAction(aCommentStringA, i - nTextPosition));
- nNextCellBreak = mxBreakIterator->nextCharacters(
+ nNextCellBreak = rBreakIterator.nextCharacters(
rTxt, i, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
}
if (i == nNextWordBoundary.endPos)
{
mpMetaFile->AddAction(
new MetaCommentAction(aCommentStringB, i - nTextPosition));
- nNextWordBoundary = mxBreakIterator->getWordBoundary(
+ nNextWordBoundary = rBreakIterator.getWordBoundary(
rTxt, i + 1, rLocale, css::i18n::WordType::ANY_WORD, true);
}
if (i == nNextSentenceBreak)
{
mpMetaFile->AddAction(
new MetaCommentAction(aCommentStringC, i - nTextPosition));
- nNextSentenceBreak = mxBreakIterator->endOfSentence(rTxt, i + 1, rLocale);
+ nNextSentenceBreak = rBreakIterator.endOfSentence(rTxt, i + 1, rLocale);
}
}
}
@@ -1500,9 +1557,11 @@ void VclMetafileProcessor2D::processPolygonHairlinePrimitive2D(
basegfx::B2DPolygon aLeft, aRight;
splitLinePolygon(rBasePolygon, aLeft, aRight);
rtl::Reference<primitive2d::PolygonHairlinePrimitive2D> xPLeft(
- new primitive2d::PolygonHairlinePrimitive2D(aLeft, rHairlinePrimitive.getBColor()));
+ new primitive2d::PolygonHairlinePrimitive2D(std::move(aLeft),
+ rHairlinePrimitive.getBColor()));
rtl::Reference<primitive2d::PolygonHairlinePrimitive2D> xPRight(
- new primitive2d::PolygonHairlinePrimitive2D(aRight, rHairlinePrimitive.getBColor()));
+ new primitive2d::PolygonHairlinePrimitive2D(std::move(aRight),
+ rHairlinePrimitive.getBColor()));
processBasePrimitive2D(*xPLeft);
processBasePrimitive2D(*xPRight);
@@ -1550,10 +1609,12 @@ void VclMetafileProcessor2D::processPolygonStrokePrimitive2D(
basegfx::B2DPolygon aLeft, aRight;
splitLinePolygon(rBasePolygon, aLeft, aRight);
rtl::Reference<primitive2d::PolygonStrokePrimitive2D> xPLeft(
- new primitive2d::PolygonStrokePrimitive2D(aLeft, rStrokePrimitive.getLineAttribute(),
+ new primitive2d::PolygonStrokePrimitive2D(std::move(aLeft),
+ rStrokePrimitive.getLineAttribute(),
rStrokePrimitive.getStrokeAttribute()));
rtl::Reference<primitive2d::PolygonStrokePrimitive2D> xPRight(
- new primitive2d::PolygonStrokePrimitive2D(aRight, rStrokePrimitive.getLineAttribute(),
+ new primitive2d::PolygonStrokePrimitive2D(std::move(aRight),
+ rStrokePrimitive.getLineAttribute(),
rStrokePrimitive.getStrokeAttribute()));
processBasePrimitive2D(*xPLeft);
@@ -1572,7 +1633,7 @@ void VclMetafileProcessor2D::processPolygonStrokePrimitive2D(
const attribute::LineAttribute& rLine = rStrokePrimitive.getLineAttribute();
// create MetaPolyLineActions, but without LineStyle::Dash
- if (basegfx::fTools::more(rLine.getWidth(), 0.0))
+ if (rLine.getWidth() > 0.0)
{
const attribute::StrokeAttribute& rStroke = rStrokePrimitive.getStrokeAttribute();
@@ -1583,7 +1644,7 @@ void VclMetafileProcessor2D::processPolygonStrokePrimitive2D(
// use the transformed line width
LineInfo aLineInfo(LineStyle::Solid,
- basegfx::fround(getTransformedLineWidth(rLine.getWidth())));
+ std::round(getTransformedLineWidth(rLine.getWidth())));
aLineInfo.SetLineJoin(rLine.getLineJoin());
aLineInfo.SetLineCap(rLine.getLineCap());
@@ -1753,6 +1814,16 @@ void VclMetafileProcessor2D::processPolyPolygonGraphicPrimitive2D(
// need to handle PolyPolygonGraphicPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END
basegfx::B2DPolyPolygon aLocalPolyPolygon(rBitmapCandidate.getB2DPolyPolygon());
+ if (!rBitmapCandidate.getDefinitionRange().isEmpty()
+ && aLocalPolyPolygon.getB2DRange() != rBitmapCandidate.getDefinitionRange())
+ {
+ // The range which defines the bitmap fill is defined and different from the
+ // range of the defining geometry (e.g. used for FillStyle UseSlideBackground).
+ // This cannot be done calling vcl, thus use decomposition here directly
+ process(rBitmapCandidate);
+ return;
+ }
+
fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon);
std::unique_ptr<SvtGraphicFill> pSvtGraphicFill;
@@ -1903,7 +1974,7 @@ void VclMetafileProcessor2D::processPolyPolygonHatchPrimitive2D(
aToolsPolyPolygon,
Hatch(aHatchStyle,
Color(maBColorModifierStack.getModifiedColor(rFillHatchAttribute.getColor())),
- basegfx::fround(rFillHatchAttribute.getDistance()),
+ basegfx::fround<tools::Long>(rFillHatchAttribute.getDistance()),
Degree10(basegfx::fround(basegfx::rad2deg<10>(rFillHatchAttribute.getAngle())))));
impEndSvtGraphicFill(pSvtGraphicFill.get());
@@ -1912,42 +1983,126 @@ void VclMetafileProcessor2D::processPolyPolygonHatchPrimitive2D(
void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D(
const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate)
{
- basegfx::B2DVector aScale, aTranslate;
- double fRotate, fShearX;
+ bool useDecompose(false);
- maCurrentTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
+ if (!useDecompose)
+ {
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+
+ maCurrentTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // detect if transformation is rotated, sheared or mirrored in X and/or Y
+ if (!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX)
+ || aScale.getX() < 0.0 || aScale.getY() < 0.0)
+ {
+ // #i121185# When rotation or shear is used, a VCL Gradient cannot be used directly.
+ // This is because VCL Gradient mechanism does *not* support to rotate the gradient
+ // with objects and this case is not expressible in a Metafile (and cannot be added
+ // since the FileFormats used, e.g. *.wmf, do not support it either).
+ // Such cases happen when a graphic object uses a Metafile as graphic information or
+ // a fill style definition uses a Metafile. In this cases the graphic content is
+ // rotated with the graphic or filled object; this is not supported by the target
+ // format of this conversion renderer - Metafiles.
+ // To solve this, not a Gradient is written, but the decomposition of this object
+ // is written to the Metafile. This is the PolyPolygons building the gradient fill.
+ // These will need more space and time, but the result will be as if the Gradient
+ // was rotated with the object.
+ // This mechanism is used by all exporters still not using Primitives (e.g. Print,
+ // Slideshow, Export rto PDF, export to Picture, ...) but relying on Metafile
+ // transfers. One more reason to *change* these to primitives.
+ // BTW: One more example how useful the principles of primitives are; the decomposition
+ // is by definition a simpler, maybe more expensive representation of the same content.
+ useDecompose = true;
+ }
+ }
- if (!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX))
+ // tdf#150551 for PDF export, use the decomposition for better gradient visualization
+ if (!useDecompose && nullptr != mpPDFExtOutDevData)
{
- // #i121185# When rotation or shear is used, a VCL Gradient cannot be used directly.
- // This is because VCL Gradient mechanism does *not* support to rotate the gradient
- // with objects and this case is not expressible in a Metafile (and cannot be added
- // since the FileFormats used, e.g. *.wmf, do not support it either).
- // Such cases happen when a graphic object uses a Metafile as graphic information or
- // a fill style definition uses a Metafile. In this cases the graphic content is
- // rotated with the graphic or filled object; this is not supported by the target
- // format of this conversion renderer - Metafiles.
- // To solve this, not a Gradient is written, but the decomposition of this object
- // is written to the Metafile. This is the PolyPolygons building the gradient fill.
- // These will need more space and time, but the result will be as if the Gradient
- // was rotated with the object.
- // This mechanism is used by all exporters still not using Primitives (e.g. Print,
- // Slideshow, Export rto PDF, export to Picture, ...) but relying on Metafile
- // transfers. One more reason to *change* these to primitives.
- // BTW: One more example how useful the principles of primitives are; the decomposition
- // is by definition a simpler, maybe more expensive representation of the same content.
- process(rGradientCandidate);
- return;
+ useDecompose = true;
}
basegfx::B2DPolyPolygon aLocalPolyPolygon(rGradientCandidate.getB2DPolyPolygon());
- if (aLocalPolyPolygon.getB2DRange() != rGradientCandidate.getDefinitionRange())
+ if (!useDecompose && aLocalPolyPolygon.getB2DRange() != rGradientCandidate.getDefinitionRange())
{
// the range which defines the gradient is different from the range of the
// geometry (used for writer frames). This cannot be done calling vcl, thus use
// decomposition here
+ useDecompose = true;
+ }
+
+ const attribute::FillGradientAttribute& rFillGradient(rGradientCandidate.getFillGradient());
+
+ if (!useDecompose && rFillGradient.cannotBeHandledByVCL())
+ {
+ // MCGR: if we have ColorStops, do not try to fallback to old VCL-Gradient,
+ // that will *not* be capable of representing this properly. Use the
+ // correct decomposition instead
+ useDecompose = true;
+ }
+
+ if (useDecompose)
+ {
+ GDIMetaFile* pMetaFile(mpOutputDevice->GetConnectMetaFile());
+
+ // tdf#155479 only add 'BGRAD_SEQ_BEGIN' if SVG export
+ if (nullptr != pMetaFile && pMetaFile->getSVG())
+ {
+ // write the color stops to a memory stream
+ SvMemoryStream aMemStm;
+ VersionCompatWrite aCompat(aMemStm, 1);
+
+ const basegfx::BColorStops& rColorStops(rFillGradient.getColorStops());
+ sal_uInt16 nTmp(sal::static_int_cast<sal_uInt16>(rColorStops.size()));
+ aMemStm.WriteUInt16(nTmp);
+
+ for (auto const& rCand : rColorStops)
+ {
+ aMemStm.WriteDouble(rCand.getStopOffset());
+ const basegfx::BColor& rColor(rCand.getStopColor());
+ aMemStm.WriteDouble(rColor.getRed());
+ aMemStm.WriteDouble(rColor.getGreen());
+ aMemStm.WriteDouble(rColor.getBlue());
+ }
+
+ // Add a new MetaCommentAction section of type 'BGRAD_SEQ_BEGIN/BGRAD_SEQ_END'
+ // that is capable of holding the new color step information, plus the
+ // already used MetaActionType::GRADIENTEX.
+ // With that combination only places that know about that new BGRAD_SEQ_* will
+ // use it while all others will work on the created decomposition of the
+ // gradient for compatibility - which are single-color filled polygons
+ pMetaFile->AddAction(new MetaCommentAction(
+ "BGRAD_SEQ_BEGIN"_ostr, 0, static_cast<const sal_uInt8*>(aMemStm.GetData()),
+ aMemStm.TellEnd()));
+
+ // create MetaActionType::GRADIENTEX
+ // NOTE: with the new BGRAD_SEQ_* we could use basegfx::B2DPolygon and
+ // basegfx::BGradient here directly, but may have to add streaming OPs
+ // for these, so for now just go with what we use all the time. The real
+ // work for improvement should not go to this 'compromise' but to a real
+ // re-work of the SVG export (or/and others) to no longer work on metafiles
+ // but on UNO API or primitives (whatever fits best to the specific export)
+ fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon);
+ Gradient aVCLGradient;
+ impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rFillGradient, false);
+ aLocalPolyPolygon.transform(maCurrentTransformation);
+ const tools::PolyPolygon aToolsPolyPolygon(
+ getFillPolyPolygon(basegfx::utils::adaptiveSubdivideByAngle(aLocalPolyPolygon)));
+ mpOutputDevice->DrawGradient(aToolsPolyPolygon, aVCLGradient);
+ }
+
+ // use decompose to draw, will create PolyPolygon ColorFill actions
process(rGradientCandidate);
+
+ // tdf#155479 only add 'BGRAD_SEQ_END' if SVG export
+ if (nullptr != pMetaFile && pMetaFile->getSVG())
+ {
+ // close the BGRAD_SEQ_* actions range
+ pMetaFile->AddAction(new MetaCommentAction("BGRAD_SEQ_END"_ostr));
+ }
+
return;
}
@@ -1959,8 +2114,7 @@ void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D(
// it is safest to use the VCL OutputDevice::DrawGradient method which creates those.
// re-create a VCL-gradient from FillGradientPrimitive2D and the needed tools PolyPolygon
Gradient aVCLGradient;
- impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rGradientCandidate.getFillGradient(),
- false);
+ impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rFillGradient, false);
aLocalPolyPolygon.transform(maCurrentTransformation);
// #i82145# ATM VCL printing of gradients using curved shapes does not work,
@@ -1981,16 +2135,16 @@ void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D(
switch (aVCLGradient.GetStyle())
{
- default: // GradientStyle::Linear:
- case GradientStyle::Axial:
+ default: // css::awt::GradientStyle_LINEAR:
+ case css::awt::GradientStyle_AXIAL:
eGrad = SvtGraphicFill::GradientType::Linear;
break;
- case GradientStyle::Radial:
- case GradientStyle::Elliptical:
+ case css::awt::GradientStyle_RADIAL:
+ case css::awt::GradientStyle_ELLIPTICAL:
eGrad = SvtGraphicFill::GradientType::Radial;
break;
- case GradientStyle::Square:
- case GradientStyle::Rect:
+ case css::awt::GradientStyle_SQUARE:
+ case css::awt::GradientStyle_RECT:
eGrad = SvtGraphicFill::GradientType::Rectangular;
break;
}
@@ -2115,7 +2269,7 @@ void VclMetafileProcessor2D::processUnifiedTransparencePrimitive2D(
if (!bForceToMetafile && 1 == rContent.size())
{
- const primitive2d::Primitive2DReference xReference(rContent[0]);
+ const primitive2d::Primitive2DReference xReference(rContent.front());
pPoPoColor = dynamic_cast<const primitive2d::PolyPolygonColorPrimitive2D*>(
xReference.get());
}
@@ -2159,6 +2313,11 @@ void VclMetafileProcessor2D::processUnifiedTransparencePrimitive2D(
// various content, create content-metafile
GDIMetaFile aContentMetafile;
+
+ // tdf#155479 always forward propagate SVG flag for sub-content,
+ // it may contain cannotBeHandledByVCL gradients or transparencyGradients
+ aContentMetafile.setSVG(mpOutputDevice->GetConnectMetaFile()->getSVG());
+
const tools::Rectangle aPrimitiveRectangle(
impDumpToMetaFile(rContent, aContentMetafile));
@@ -2172,7 +2331,7 @@ void VclMetafileProcessor2D::processUnifiedTransparencePrimitive2D(
basegfx::fround(rUniTransparenceCandidate.getTransparence() * 255.0)));
const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl);
- aVCLGradient.SetStyle(GradientStyle::Linear);
+ aVCLGradient.SetStyle(css::awt::GradientStyle_LINEAR);
aVCLGradient.SetStartColor(aTransColor);
aVCLGradient.SetEndColor(aTransColor);
aVCLGradient.SetAngle(0_deg10);
@@ -2202,29 +2361,82 @@ void VclMetafileProcessor2D::processTransparencePrimitive2D(
// FillGradientPrimitive2D and reconstruct the gradient.
// If that detection goes wrong, I have to create a transparence-blended bitmap. Eventually
// do that in stripes, else RenderTransparencePrimitive2D may just be used
- const primitive2d::Primitive2DContainer& rContent = rTransparenceCandidate.getChildren();
- const primitive2d::Primitive2DContainer& rTransparence
- = rTransparenceCandidate.getTransparence();
+ const primitive2d::Primitive2DContainer& rContent(rTransparenceCandidate.getChildren());
+ const primitive2d::Primitive2DContainer& rTransparence(
+ rTransparenceCandidate.getTransparence());
if (rContent.empty() || rTransparence.empty())
return;
// try to identify a single FillGradientPrimitive2D in the
- // transparence part of the primitive
- const primitive2d::FillGradientPrimitive2D* pFiGradient = nullptr;
+ // transparence part of the primitive. The hope is to handle
+ // the more specific case in a better way than the general
+ // TransparencePrimitive2D which has strongly separated
+ // definitions for transparency and content, both completely
+ // free definable by primitives
+ const primitive2d::FillGradientPrimitive2D* pFiGradient(nullptr);
static bool bForceToBigTransparentVDev(false); // loplugin:constvars:ignore
+ // check for single FillGradientPrimitive2D
if (!bForceToBigTransparentVDev && 1 == rTransparence.size())
{
- const primitive2d::Primitive2DReference xReference(rTransparence[0]);
- pFiGradient = dynamic_cast<const primitive2d::FillGradientPrimitive2D*>(xReference.get());
+ pFiGradient = dynamic_cast<const primitive2d::FillGradientPrimitive2D*>(
+ rTransparence.front().get());
+
+ // check also for correct ID to exclude derived implementations
+ if (pFiGradient
+ && PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D != pFiGradient->getPrimitive2DID())
+ pFiGradient = nullptr;
}
- // Check also for correct ID to exclude derived implementations
- if (pFiGradient && PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D == pFiGradient->getPrimitive2DID())
+ // tdf#155479 preps for holding extra-MCGR infos
+ bool bSVGTransparencyColorStops(false);
+ basegfx::BColorStops aSVGTransparencyColorStops;
+
+ // MCGR: tdf#155437 If we have identified a transparency gradient,
+ // check if VCL is able to handle it at all
+ if (nullptr != pFiGradient && pFiGradient->getFillGradient().cannotBeHandledByVCL())
{
- // various content, create content-metafile
+ // If not, reset the pointer and do not make use of this special case.
+ // Adding a gradient in incomplete state that can not be handled by vcl
+ // makes no sense and will knowingly lead to errors, especially with
+ // MCGR extended possibilities. I checked what happens with the
+ // MetaFloatTransparentAction added by OutputDevice::DrawTransparent, but
+ // in most cases it gets converted to bitmap or even ignored, see e.g.
+ // - vcl/source/gdi/pdfwriter_impl2.cxx for PDF export
+ // - vcl/source/filter/wmf/wmfwr.cxx -> does ignore TransparenceGradient completely
+ // - vcl/source/filter/wmf/emfwr.cxx -> same
+ // - vcl/source/filter/eps/eps.cxx -> same
+ // NOTE: Theoretically it would be possible to make the new extended Gradient data
+ // available in metafiles, with the known limitations (not backward comp, all
+ // places using it would need adaption, ...), but combined with knowing that nearly
+ // all usages ignore or render it locally anyways makes that a non-option.
+
+ // tdf#155479 Yepp, as already mentioned above we need to add
+ // some MCGR infos in case of SVG export, prepare that here
+ if (mpOutputDevice->GetConnectMetaFile()->getSVG())
+ {
+ // for SVG, do not use decompose & prep extra data
+ bSVGTransparencyColorStops = true;
+ aSVGTransparencyColorStops = pFiGradient->getFillGradient().getColorStops();
+ }
+ else
+ {
+ // use decomposition
+ pFiGradient = nullptr;
+ }
+ }
+
+ if (nullptr != pFiGradient)
+ {
+ // this combination of Gradient can be expressed/handled by
+ // vcl/metafile, so add it directly. various content, create content-metafile
GDIMetaFile aContentMetafile;
+
+ // tdf#155479 always forward propagate SVG flag for sub-content,
+ // it may contain cannotBeHandledByVCL gradients or transparencyGradients
+ aContentMetafile.setSVG(mpOutputDevice->GetConnectMetaFile()->getSVG());
+
const tools::Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile));
// re-create a VCL-gradient from FillGradientPrimitive2D
@@ -2232,126 +2444,192 @@ void VclMetafileProcessor2D::processTransparencePrimitive2D(
impConvertFillGradientAttributeToVCLGradient(aVCLGradient, pFiGradient->getFillGradient(),
true);
- // render it to VCL
- mpOutputDevice->DrawTransparent(aContentMetafile, aPrimitiveRectangle.TopLeft(),
- aPrimitiveRectangle.GetSize(), aVCLGradient);
- }
- else
- {
- // sub-transparence group. Draw to VDev first.
- // this may get refined to tiling when resolution is too big here
-
- // need to avoid switching off MapMode stuff here; maybe need another
- // tooling class, cannot just do the same as with the pixel renderer.
- // Need to experiment...
-
- // Okay, basic implementation finished and tested. The DPI stuff was hard
- // and not easy to find out that it's needed.
- // Since this will not yet happen normally (as long as no one constructs
- // transparence primitives with non-trivial transparence content) i will for now not
- // refine to tiling here.
-
- basegfx::B2DRange aViewRange(rContent.getB2DRange(getViewInformation2D()));
- aViewRange.transform(maCurrentTransformation);
- const tools::Rectangle aRectLogic(static_cast<sal_Int32>(floor(aViewRange.getMinX())),
- static_cast<sal_Int32>(floor(aViewRange.getMinY())),
- static_cast<sal_Int32>(ceil(aViewRange.getMaxX())),
- static_cast<sal_Int32>(ceil(aViewRange.getMaxY())));
- const tools::Rectangle aRectPixel(mpOutputDevice->LogicToPixel(aRectLogic));
- Size aSizePixel(aRectPixel.GetSize());
- ScopedVclPtrInstance<VirtualDevice> aBufferDevice;
- const sal_uInt32 nMaxSquarePixels(500000);
- const sal_uInt32 nViewVisibleArea(aSizePixel.getWidth() * aSizePixel.getHeight());
- double fReduceFactor(1.0);
-
- if (nViewVisibleArea > nMaxSquarePixels)
- {
- // reduce render size
- fReduceFactor = sqrt(double(nMaxSquarePixels) / static_cast<double>(nViewVisibleArea));
- aSizePixel = Size(
- basegfx::fround(static_cast<double>(aSizePixel.getWidth()) * fReduceFactor),
- basegfx::fround(static_cast<double>(aSizePixel.getHeight()) * fReduceFactor));
- }
-
- if (aBufferDevice->SetOutputSizePixel(aSizePixel))
- {
- // create and set MapModes for target devices
- MapMode aNewMapMode(mpOutputDevice->GetMapMode());
- aNewMapMode.SetOrigin(Point(-aRectLogic.Left(), -aRectLogic.Top()));
- aBufferDevice->SetMapMode(aNewMapMode);
-
- // prepare view transformation for target renderers
- // ATTENTION! Need to apply another scaling because of the potential DPI differences
- // between Printer and VDev (mpOutputDevice and aBufferDevice here).
- // To get the DPI, LogicToPixel from (1,1) from MapUnit::MapInch needs to be used.
- basegfx::B2DHomMatrix aViewTransform(aBufferDevice->GetViewTransformation());
- const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
- const Size aDPINew(aBufferDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
- const double fDPIXChange(static_cast<double>(aDPIOld.getWidth())
- / static_cast<double>(aDPINew.getWidth()));
- const double fDPIYChange(static_cast<double>(aDPIOld.getHeight())
- / static_cast<double>(aDPINew.getHeight()));
-
- if (!basegfx::fTools::equal(fDPIXChange, 1.0)
- || !basegfx::fTools::equal(fDPIYChange, 1.0))
- {
- aViewTransform.scale(fDPIXChange, fDPIYChange);
- }
-
- // also take scaling from Size reduction into account
- if (!basegfx::fTools::equal(fReduceFactor, 1.0))
- {
- aViewTransform.scale(fReduceFactor, fReduceFactor);
- }
-
- // create view information and pixel renderer. Reuse known ViewInformation
- // except new transformation and range
- const geometry::ViewInformation2D aViewInfo(
- getViewInformation2D().getObjectTransformation(), aViewTransform, aViewRange,
- getViewInformation2D().getVisualizedPage(), getViewInformation2D().getViewTime());
-
- VclPixelProcessor2D aBufferProcessor(aViewInfo, *aBufferDevice);
-
- // draw content using pixel renderer
- const Point aEmptyPoint;
- aBufferProcessor.process(rContent);
- const Bitmap aBmContent(aBufferDevice->GetBitmap(aEmptyPoint, aSizePixel));
-
- // draw transparence using pixel renderer
- aBufferDevice->Erase();
- aBufferProcessor.process(rTransparence);
- const AlphaMask aBmAlpha(aBufferDevice->GetBitmap(aEmptyPoint, aSizePixel));
+ if (bSVGTransparencyColorStops)
+ {
+ // tdf#155479 create action directly & add extra
+ // MCGR infos to the metafile, do that by adding - ONLY in
+ // case of SVG export - to the MetaFileAction. For that
+ // reason, do what OutputDevice::DrawTransparent will do,
+ // but locally.
+ // NOTE: That would be good for this whole
+ // VclMetafileProcessor2D anyways to allow to get it
+ // completely independent from OutputDevice in the long run
+ GDIMetaFile* pMetaFile(mpOutputDevice->GetConnectMetaFile());
+ rtl::Reference<::MetaFloatTransparentAction> pAction(
+ new MetaFloatTransparentAction(aContentMetafile, aPrimitiveRectangle.TopLeft(),
+ aPrimitiveRectangle.GetSize(), aVCLGradient));
- // paint
- mpOutputDevice->DrawBitmapEx(aRectLogic.TopLeft(), aRectLogic.GetSize(),
- BitmapEx(aBmContent, aBmAlpha));
+ pAction->addSVGTransparencyColorStops(aSVGTransparencyColorStops);
+ pMetaFile->AddAction(pAction);
}
+ else
+ {
+ // render it to VCL (creates MetaFloatTransparentAction)
+ mpOutputDevice->DrawTransparent(aContentMetafile, aPrimitiveRectangle.TopLeft(),
+ aPrimitiveRectangle.GetSize(), aVCLGradient);
+ }
+ return;
}
+
+ // Here we need to create a correct replacement visualization for the
+ // TransparencePrimitive2D for the target metafile.
+ // I replaced the n'th iteration to convert-to-bitmap which was
+ // used here by using the existing tooling. The orig here was also producing
+ // transparency errors with test-file from tdf#155437 on the right part of the
+ // image.
+ // Just rely on existing tooling doing the right thing in one place, so also
+ // corrections/optimizations can be in one single place
+
+ // Start by getting logic range of content, transform object-to-world, then world-to-view
+ // to get to discrete values ('pixels'). Matrix multiplication is right-to-left (and not
+ // commutative)
+ basegfx::B2DRange aLogicRange(rTransparenceCandidate.getB2DRange(getViewInformation2D()));
+ aLogicRange.transform(mpOutputDevice->GetViewTransformation() * maCurrentTransformation);
+
+ // expand in discrete coordinates to next-bigger 'pixel' boundaries and remember
+ // created discrete range
+ aLogicRange.expand(
+ basegfx::B2DPoint(floor(aLogicRange.getMinX()), floor(aLogicRange.getMinY())));
+ aLogicRange.expand(basegfx::B2DPoint(ceil(aLogicRange.getMaxX()), ceil(aLogicRange.getMaxY())));
+ const basegfx::B2DRange aDiscreteRange(aLogicRange);
+
+ // transform back from discrete to world coordinates: this creates the
+ // pixel-boundaries extended logic range we need to cover all content
+ // reliably
+ aLogicRange.transform(mpOutputDevice->GetInverseViewTransformation());
+
+ // create transform embedding for renderer. Goal is to translate what we
+ // want to paint to top/left 0/0 and the calculated discrete size
+ basegfx::B2DHomMatrix aEmbedding(basegfx::utils::createTranslateB2DHomMatrix(
+ -aLogicRange.getMinX(), -aLogicRange.getMinY()));
+ const double fLogicWidth(
+ basegfx::fTools::equalZero(aLogicRange.getWidth()) ? 1.0 : aLogicRange.getWidth());
+ const double fLogicHeight(
+ basegfx::fTools::equalZero(aLogicRange.getHeight()) ? 1.0 : aLogicRange.getHeight());
+ aEmbedding.scale(aDiscreteRange.getWidth() / fLogicWidth,
+ aDiscreteRange.getHeight() / fLogicHeight);
+
+ // use the whole TransparencePrimitive2D as input (no need to create a new
+ // one with the sub-contents, these are ref-counted) and add to embedding
+ // primitive2d::TransparencePrimitive2D& rTrCand();
+ primitive2d::Primitive2DContainer xEmbedSeq{ &const_cast<primitive2d::TransparencePrimitive2D&>(
+ rTransparenceCandidate) };
+
+ // tdf#158743 when embedding, do not forget to 1st apply the evtl. used
+ // CurrentTransformation (right-to-left, apply that 1st)
+ xEmbedSeq = primitive2d::Primitive2DContainer{ new primitive2d::TransformPrimitive2D(
+ aEmbedding * maCurrentTransformation, std::move(xEmbedSeq)) };
+
+ // use empty ViewInformation & a useful MaximumQuadraticPixels
+ // limitation to paint the content
+ const auto aViewInformation2D(geometry::createViewInformation2D({}));
+ const sal_uInt32 nMaximumQuadraticPixels(500000);
+ const BitmapEx aBitmapEx(convertToBitmapEx(
+ std::move(xEmbedSeq), aViewInformation2D, basegfx::fround(aDiscreteRange.getWidth()),
+ basegfx::fround(aDiscreteRange.getHeight()), nMaximumQuadraticPixels));
+
+ // add to target metafile (will create MetaFloatTransparentAction)
+ mpOutputDevice->DrawBitmapEx(Point(basegfx::fround<tools::Long>(aLogicRange.getMinX()),
+ basegfx::fround<tools::Long>(aLogicRange.getMinY())),
+ Size(basegfx::fround<tools::Long>(aLogicRange.getWidth()),
+ basegfx::fround<tools::Long>(aLogicRange.getHeight())),
+ aBitmapEx);
}
void VclMetafileProcessor2D::processStructureTagPrimitive2D(
const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate)
{
+ ::comphelper::ValueRestorationGuard const g(mpCurrentStructureTag, &rStructureTagCandidate);
+
// structured tag primitive
const vcl::PDFWriter::StructElement& rTagElement(rStructureTagCandidate.getStructureElement());
bool bTagUsed((vcl::PDFWriter::NonStructElement != rTagElement));
+ ::std::optional<sal_Int32> oAnchorParent;
+
+ if (!rStructureTagCandidate.isTaggedSdrObject())
+ {
+ bTagUsed = false;
+ }
if (mpPDFExtOutDevData && bTagUsed)
{
// foreground object: tag as regular structure element
if (!rStructureTagCandidate.isBackground())
{
- mpPDFExtOutDevData->BeginStructureElement(rTagElement);
+ if (rStructureTagCandidate.GetAnchorStructureElementKey() != nullptr)
+ {
+ sal_Int32 const id = mpPDFExtOutDevData->EnsureStructureElement(
+ rStructureTagCandidate.GetAnchorStructureElementKey());
+ oAnchorParent.emplace(mpPDFExtOutDevData->GetCurrentStructureElement());
+ mpPDFExtOutDevData->SetCurrentStructureElement(id);
+ }
+ mpPDFExtOutDevData->WrapBeginStructureElement(rTagElement);
+ switch (rTagElement)
+ {
+ case vcl::PDFWriter::H1:
+ case vcl::PDFWriter::H2:
+ case vcl::PDFWriter::H3:
+ case vcl::PDFWriter::H4:
+ case vcl::PDFWriter::H5:
+ case vcl::PDFWriter::H6:
+ case vcl::PDFWriter::Paragraph:
+ case vcl::PDFWriter::Heading:
+ case vcl::PDFWriter::Caption:
+ case vcl::PDFWriter::BlockQuote:
+ case vcl::PDFWriter::Table:
+ case vcl::PDFWriter::TableRow:
+ case vcl::PDFWriter::Formula:
+ case vcl::PDFWriter::Figure:
+ case vcl::PDFWriter::Annot:
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Placement,
+ vcl::PDFWriter::Block);
+ break;
+ case vcl::PDFWriter::TableData:
+ case vcl::PDFWriter::TableHeader:
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Placement,
+ vcl::PDFWriter::Inline);
+ break;
+ default:
+ break;
+ }
+ switch (rTagElement)
+ {
+ case vcl::PDFWriter::Table:
+ case vcl::PDFWriter::Formula:
+ case vcl::PDFWriter::Figure:
+ case vcl::PDFWriter::Annot:
+ {
+ auto const range(rStructureTagCandidate.getB2DRange(getViewInformation2D()));
+ tools::Rectangle const aLogicRect(
+ basegfx::fround<tools::Long>(range.getMinX()),
+ basegfx::fround<tools::Long>(range.getMinY()),
+ basegfx::fround<tools::Long>(range.getMaxX()),
+ basegfx::fround<tools::Long>(range.getMaxY()));
+ mpPDFExtOutDevData->SetStructureBoundingBox(aLogicRect);
+ break;
+ }
+ default:
+ break;
+ }
+ if (rTagElement == vcl::PDFWriter::Annot)
+ {
+ mpPDFExtOutDevData->SetStructureAnnotIds(rStructureTagCandidate.GetAnnotIds());
+ }
+ if (rTagElement == vcl::PDFWriter::TableHeader)
+ {
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Scope,
+ vcl::PDFWriter::Column);
+ }
}
// background object
else
{
// background image: tag as artifact
if (rStructureTagCandidate.isImage())
- mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::NonStructElement);
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::NonStructElement);
// any other background object: do not tag
else
- bTagUsed = false;
+ assert(false);
}
}
@@ -2362,104 +2640,11 @@ void VclMetafileProcessor2D::processStructureTagPrimitive2D(
{
// write end tag
mpPDFExtOutDevData->EndStructureElement();
- }
-}
-
-VclPtr<VirtualDevice>
-VclMetafileProcessor2D::CreateBufferDevice(const basegfx::B2DRange& rCandidateRange,
- geometry::ViewInformation2D& rViewInfo,
- tools::Rectangle& rRectLogic, Size& rSizePixel) const
-{
- constexpr double fMaxSquarePixels = 500000;
- basegfx::B2DRange aViewRange(rCandidateRange);
- aViewRange.transform(maCurrentTransformation);
- rRectLogic = tools::Rectangle(static_cast<tools::Long>(std::floor(aViewRange.getMinX())),
- static_cast<tools::Long>(std::floor(aViewRange.getMinY())),
- static_cast<tools::Long>(std::ceil(aViewRange.getMaxX())),
- static_cast<tools::Long>(std::ceil(aViewRange.getMaxY())));
- const tools::Rectangle aRectPixel(mpOutputDevice->LogicToPixel(rRectLogic));
- rSizePixel = aRectPixel.GetSize();
- const double fViewVisibleArea(rSizePixel.getWidth() * rSizePixel.getHeight());
- double fReduceFactor(1.0);
-
- if (fViewVisibleArea > fMaxSquarePixels)
- {
- // reduce render size
- fReduceFactor = sqrt(fMaxSquarePixels / fViewVisibleArea);
- rSizePixel = Size(basegfx::fround(rSizePixel.getWidth() * fReduceFactor),
- basegfx::fround(rSizePixel.getHeight() * fReduceFactor));
- }
-
- VclPtrInstance<VirtualDevice> pBufferDevice(DeviceFormat::DEFAULT, DeviceFormat::DEFAULT);
- if (pBufferDevice->SetOutputSizePixel(rSizePixel))
- {
- // create and set MapModes for target devices
- MapMode aNewMapMode(mpOutputDevice->GetMapMode());
- aNewMapMode.SetOrigin(Point(-rRectLogic.Left(), -rRectLogic.Top()));
- pBufferDevice->SetMapMode(aNewMapMode);
-
- // prepare view transformation for target renderers
- // ATTENTION! Need to apply another scaling because of the potential DPI differences
- // between Printer and VDev (mpOutputDevice and pBufferDevice here).
- // To get the DPI, LogicToPixel from (1,1) from MapUnit::MapInch needs to be used.
- basegfx::B2DHomMatrix aViewTransform(pBufferDevice->GetViewTransformation());
- const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
- const Size aDPINew(pBufferDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
- const double fDPIXChange(static_cast<double>(aDPIOld.getWidth())
- / static_cast<double>(aDPINew.getWidth()));
- const double fDPIYChange(static_cast<double>(aDPIOld.getHeight())
- / static_cast<double>(aDPINew.getHeight()));
-
- if (!basegfx::fTools::equal(fDPIXChange, 1.0) || !basegfx::fTools::equal(fDPIYChange, 1.0))
- {
- aViewTransform.scale(fDPIXChange, fDPIYChange);
- }
-
- // also take scaling from Size reduction into account
- if (!basegfx::fTools::equal(fReduceFactor, 1.0))
+ if (oAnchorParent)
{
- aViewTransform.scale(fReduceFactor, fReduceFactor);
+ mpPDFExtOutDevData->SetCurrentStructureElement(*oAnchorParent);
}
-
- // create view information and pixel renderer. Reuse known ViewInformation
- // except new transformation and range
- rViewInfo = geometry::ViewInformation2D(
- getViewInformation2D().getObjectTransformation(), aViewTransform, aViewRange,
- getViewInformation2D().getVisualizedPage(), getViewInformation2D().getViewTime());
- }
- else
- pBufferDevice.disposeAndClear();
-
-#if HAVE_P1155R3
- return pBufferDevice;
-#else
- return std::move(pBufferDevice);
-#endif
-}
-
-void VclMetafileProcessor2D::processPrimitive2DOnPixelProcessor(
- const primitive2d::BasePrimitive2D& rCandidate)
-{
- basegfx::B2DRange aViewRange(rCandidate.getB2DRange(getViewInformation2D()));
- geometry::ViewInformation2D aViewInfo;
- tools::Rectangle aRectLogic;
- Size aSizePixel;
- auto pBufferDevice(CreateBufferDevice(aViewRange, aViewInfo, aRectLogic, aSizePixel));
- if (pBufferDevice)
- {
- VclPixelProcessor2D aBufferProcessor(aViewInfo, *pBufferDevice, maBColorModifierStack);
-
- // draw content using pixel renderer
- primitive2d::Primitive2DReference aRef(
- &const_cast<primitive2d::BasePrimitive2D&>(rCandidate));
- aBufferProcessor.process({ aRef });
- const BitmapEx aBmContent(pBufferDevice->GetBitmapEx(Point(), aSizePixel));
- mpOutputDevice->DrawBitmapEx(aRectLogic.TopLeft(), aRectLogic.GetSize(), aBmContent);
-
- // aBufferProcessor dtor pops state off pBufferDevice pushed on by its ctor, let
- // pBufferDevice live past aBufferProcessor scope to avoid warnings
}
- pBufferDevice.disposeAndClear();
}
} // end of namespace