summaryrefslogtreecommitdiffstats
path: root/drawinglayer/source/tools/emfphelperdata.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /drawinglayer/source/tools/emfphelperdata.cxx
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drawinglayer/source/tools/emfphelperdata.cxx')
-rw-r--r--drawinglayer/source/tools/emfphelperdata.cxx2268
1 files changed, 2268 insertions, 0 deletions
diff --git a/drawinglayer/source/tools/emfphelperdata.cxx b/drawinglayer/source/tools/emfphelperdata.cxx
new file mode 100644
index 000000000..aaa9b1bf7
--- /dev/null
+++ b/drawinglayer/source/tools/emfphelperdata.cxx
@@ -0,0 +1,2268 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "emfpcustomlinecap.hxx"
+#include "emfphelperdata.hxx"
+#include "emfpbrush.hxx"
+#include "emfppen.hxx"
+#include "emfppath.hxx"
+#include "emfpregion.hxx"
+#include "emfpimage.hxx"
+#include "emfpimageattributes.hxx"
+#include "emfpfont.hxx"
+#include "emfpstringformat.hxx"
+#include <basegfx/curve/b2dcubicbezier.hxx>
+#include <wmfemfhelper.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/attribute/fontattribute.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <basegfx/color/bcolormodifier.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <algorithm>
+
+namespace emfplushelper
+{
+
+ enum
+ {
+ WrapModeTile = 0x00000000,
+ WrapModeTileFlipX = 0x00000001,
+ WrapModeTileFlipY = 0x00000002,
+ WrapModeTileFlipXY = 0x00000003,
+ WrapModeClamp = 0x00000004
+ };
+
+ const char* emfTypeToName(sal_uInt16 type)
+ {
+ switch (type)
+ {
+ case EmfPlusRecordTypeHeader: return "EmfPlusRecordTypeHeader";
+ case EmfPlusRecordTypeEndOfFile: return "EmfPlusRecordTypeEndOfFile";
+ case EmfPlusRecordTypeComment: return "EmfPlusRecordTypeComment";
+ case EmfPlusRecordTypeGetDC: return "EmfPlusRecordTypeGetDC";
+ case EmfPlusRecordTypeObject: return "EmfPlusRecordTypeObject";
+ case EmfPlusRecordTypeFillRects: return "EmfPlusRecordTypeFillRects";
+ case EmfPlusRecordTypeDrawRects: return "EmfPlusRecordTypeDrawRects";
+ case EmfPlusRecordTypeFillPolygon: return "EmfPlusRecordTypeFillPolygon";
+ case EmfPlusRecordTypeDrawLines: return "EmfPlusRecordTypeDrawLines";
+ case EmfPlusRecordTypeFillClosedCurve: return "EmfPlusRecordTypeFillClosedCurve";
+ case EmfPlusRecordTypeFillEllipse: return "EmfPlusRecordTypeFillEllipse";
+ case EmfPlusRecordTypeDrawEllipse: return "EmfPlusRecordTypeDrawEllipse";
+ case EmfPlusRecordTypeFillPie: return "EmfPlusRecordTypeFillPie";
+ case EmfPlusRecordTypeDrawPie: return "EmfPlusRecordTypeDrawPie";
+ case EmfPlusRecordTypeDrawArc: return "EmfPlusRecordTypeDrawArc";
+ case EmfPlusRecordTypeFillRegion: return "EmfPlusRecordTypeFillRegion";
+ case EmfPlusRecordTypeFillPath: return "EmfPlusRecordTypeFillPath";
+ case EmfPlusRecordTypeDrawPath: return "EmfPlusRecordTypeDrawPath";
+ case EmfPlusRecordTypeDrawBeziers: return "EmfPlusRecordTypeDrawBeziers";
+ case EmfPlusRecordTypeDrawClosedCurve: return "EmfPlusRecordTypeDrawClosedCurve";
+ case EmfPlusRecordTypeDrawImage: return "EmfPlusRecordTypeDrawImage";
+ case EmfPlusRecordTypeDrawImagePoints: return "EmfPlusRecordTypeDrawImagePoints";
+ case EmfPlusRecordTypeDrawString: return "EmfPlusRecordTypeDrawString";
+ case EmfPlusRecordTypeSetRenderingOrigin: return "EmfPlusRecordTypeSetRenderingOrigin";
+ case EmfPlusRecordTypeSetAntiAliasMode: return "EmfPlusRecordTypeSetAntiAliasMode";
+ case EmfPlusRecordTypeSetTextRenderingHint: return "EmfPlusRecordTypeSetTextRenderingHint";
+ case EmfPlusRecordTypeSetTextContrast: return "EmfPlusRecordTypeSetTextContrast";
+ case EmfPlusRecordTypeSetInterpolationMode: return "EmfPlusRecordTypeSetInterpolationMode";
+ case EmfPlusRecordTypeSetPixelOffsetMode: return "EmfPlusRecordTypeSetPixelOffsetMode";
+ case EmfPlusRecordTypeSetCompositingQuality: return "EmfPlusRecordTypeSetCompositingQuality";
+ case EmfPlusRecordTypeSave: return "EmfPlusRecordTypeSave";
+ case EmfPlusRecordTypeRestore: return "EmfPlusRecordTypeRestore";
+ case EmfPlusRecordTypeBeginContainer: return "EmfPlusRecordTypeBeginContainer";
+ case EmfPlusRecordTypeBeginContainerNoParams: return "EmfPlusRecordTypeBeginContainerNoParams";
+ case EmfPlusRecordTypeEndContainer: return "EmfPlusRecordTypeEndContainer";
+ case EmfPlusRecordTypeSetWorldTransform: return "EmfPlusRecordTypeSetWorldTransform";
+ case EmfPlusRecordTypeResetWorldTransform: return "EmfPlusRecordTypeResetWorldTransform";
+ case EmfPlusRecordTypeMultiplyWorldTransform: return "EmfPlusRecordTypeMultiplyWorldTransform";
+ case EmfPlusRecordTypeTranslateWorldTransform: return "EmfPlusRecordTypeTranslateWorldTransform";
+ case EmfPlusRecordTypeScaleWorldTransform: return "EmfPlusRecordTypeScaleWorldTransform";
+ case EmfPlusRecordTypeSetPageTransform: return "EmfPlusRecordTypeSetPageTransform";
+ case EmfPlusRecordTypeResetClip: return "EmfPlusRecordTypeResetClip";
+ case EmfPlusRecordTypeSetClipRect: return "EmfPlusRecordTypeSetClipRect";
+ case EmfPlusRecordTypeSetClipPath: return "EmfPlusRecordTypeSetClipPath";
+ case EmfPlusRecordTypeSetClipRegion: return "EmfPlusRecordTypeSetClipRegion";
+ case EmfPlusRecordTypeOffsetClip: return "EmfPlusRecordTypeOffsetClip";
+ case EmfPlusRecordTypeDrawDriverString: return "EmfPlusRecordTypeDrawDriverString";
+ }
+ return "";
+ }
+
+ static OUString emfObjectToName(sal_uInt16 type)
+ {
+ switch (type)
+ {
+ case EmfPlusObjectTypeBrush: return "EmfPlusObjectTypeBrush";
+ case EmfPlusObjectTypePen: return "EmfPlusObjectTypePen";
+ case EmfPlusObjectTypePath: return "EmfPlusObjectTypePath";
+ case EmfPlusObjectTypeRegion: return "EmfPlusObjectTypeRegion";
+ case EmfPlusObjectTypeImage: return "EmfPlusObjectTypeImage";
+ case EmfPlusObjectTypeFont: return "EmfPlusObjectTypeFont";
+ case EmfPlusObjectTypeStringFormat: return "EmfPlusObjectTypeStringFormat";
+ case EmfPlusObjectTypeImageAttributes: return "EmfPlusObjectTypeImageAttributes";
+ case EmfPlusObjectTypeCustomLineCap: return "EmfPlusObjectTypeCustomLineCap";
+ }
+ return "";
+ }
+
+ static OUString PixelOffsetModeToString(sal_uInt16 nPixelOffset)
+ {
+ switch (nPixelOffset)
+ {
+ case PixelOffsetMode::PixelOffsetModeDefault: return "PixelOffsetModeDefault";
+ case PixelOffsetMode::PixelOffsetModeHighSpeed: return "PixelOffsetModeHighSpeed";
+ case PixelOffsetMode::PixelOffsetModeHighQuality: return "PixelOffsetModeHighQuality";
+ case PixelOffsetMode::PixelOffsetModeNone: return "PixelOffsetModeNone";
+ case PixelOffsetMode::PixelOffsetModeHalf: return "PixelOffsetModeHalf";
+ }
+ return "";
+ }
+
+ static OUString SmoothingModeToString(sal_uInt16 nSmoothMode)
+ {
+ switch (nSmoothMode)
+ {
+ case SmoothingMode::SmoothingModeDefault: return "SmoothingModeDefault";
+ case SmoothingMode::SmoothingModeHighSpeed: return "SmoothModeHighSpeed";
+ case SmoothingMode::SmoothingModeHighQuality: return "SmoothingModeHighQuality";
+ case SmoothingMode::SmoothingModeNone: return "SmoothingModeNone";
+ case SmoothingMode::SmoothingModeAntiAlias8x4: return "SmoothingModeAntiAlias8x4";
+ case SmoothingMode::SmoothingModeAntiAlias8x8: return "SmoothingModeAntiAlias8x8";
+ }
+ return "";
+ }
+
+ static OUString TextRenderingHintToString(sal_uInt16 nHint)
+ {
+ switch (nHint)
+ {
+ case TextRenderingHint::TextRenderingHintSystemDefault: return "TextRenderingHintSystemDefault";
+ case TextRenderingHint::TextRenderingHintSingleBitPerPixelGridFit: return "TextRenderingHintSingleBitPerPixelGridFit";
+ case TextRenderingHint::TextRenderingHintSingleBitPerPixel: return "TextRenderingHintSingleBitPerPixel";
+ case TextRenderingHint::TextRenderingHintAntialiasGridFit: return "TextRenderingHintAntialiasGridFit";
+ case TextRenderingHint::TextRenderingHintAntialias: return "TextRenderingHintAntialias";
+ case TextRenderingHint::TextRenderingHintClearTypeGridFit: return "TextRenderingHintClearTypeGridFit";
+ }
+ return "";
+ }
+
+ static OUString InterpolationModeToString(sal_uInt16 nMode)
+ {
+ switch (nMode)
+ {
+ case InterpolationMode::InterpolationModeDefault: return "InterpolationModeDefault";
+ case InterpolationMode::InterpolationModeLowQuality: return "InterpolationModeLowQuality";
+ case InterpolationMode::InterpolationModeHighQuality: return "InterpolationModeHighQuality";
+ case InterpolationMode::InterpolationModeBilinear: return "InterpolationModeBilinear";
+ case InterpolationMode::InterpolationModeBicubic: return "InterpolationModeBicubic";
+ case InterpolationMode::InterpolationModeNearestNeighbor: return "InterpolationModeNearestNeighbor";
+ case InterpolationMode::InterpolationModeHighQualityBilinear: return "InterpolationModeHighQualityBilinear";
+ case InterpolationMode::InterpolationModeHighQualityBicubic: return "InterpolationModeHighQualityBicubic";
+ }
+ return "";
+ }
+
+ OUString UnitTypeToString(sal_uInt16 nType)
+ {
+ switch (nType)
+ {
+ case UnitTypeWorld: return "UnitTypeWorld";
+ case UnitTypeDisplay: return "UnitTypeDisplay";
+ case UnitTypePixel: return "UnitTypePixel";
+ case UnitTypePoint: return "UnitTypePoint";
+ case UnitTypeInch: return "UnitTypeInch";
+ case UnitTypeDocument: return "UnitTypeDocument";
+ case UnitTypeMillimeter: return "UnitTypeMillimeter";
+ }
+ return "";
+ }
+
+ static bool IsBrush(sal_uInt16 flags)
+ {
+ return (!((flags >> 15) & 0x0001));
+ }
+
+ static OUString BrushIDToString(sal_uInt16 flags, sal_uInt32 brushid)
+ {
+ if (IsBrush(flags))
+ return "EmfPlusBrush ID: " + OUString::number(brushid);
+ else
+ return "ARGB: 0x" + OUString::number(brushid, 16);
+ }
+
+ EMFPObject::~EMFPObject()
+ {
+ }
+
+ float EmfPlusHelperData::getUnitToPixelMultiplier(const UnitType aUnitType, const sal_uInt32 aDPI)
+ {
+ switch (aUnitType)
+ {
+ case UnitTypePixel:
+ return 1.0f;
+
+ case UnitTypePoint:
+ return aDPI / 72.0;
+
+ case UnitTypeInch:
+ return aDPI;
+
+ case UnitTypeMillimeter:
+ return aDPI / 25.4;
+
+ case UnitTypeDocument:
+ return aDPI / 300.0;
+
+ case UnitTypeWorld:
+ case UnitTypeDisplay:
+ SAL_WARN("drawinglayer.emf", "EMF+\t Converting to World/Display.");
+ return 1.0f;
+
+ default:
+ SAL_WARN("drawinglayer.emf", "EMF+\tTODO Unimplemented support of Unit Type: 0x" << std::hex << aUnitType);
+ return 1.0f;
+ }
+ }
+
+ void EmfPlusHelperData::processObjectRecord(SvMemoryStream& rObjectStream, sal_uInt16 flags, sal_uInt32 dataSize, bool bUseWholeStream)
+ {
+ sal_uInt16 objecttype = flags & 0x7f00;
+ sal_uInt16 index = flags & 0xff;
+ SAL_INFO("drawinglayer.emf", "EMF+ Object: " << emfObjectToName(objecttype) << " (0x" << objecttype << ")");
+ SAL_INFO("drawinglayer.emf", "EMF+\tObject slot: " << index);
+ SAL_INFO("drawinglayer.emf", "EMF+\tFlags: " << (flags & 0xff00));
+
+ switch (objecttype)
+ {
+ case EmfPlusObjectTypeBrush:
+ {
+ EMFPBrush *brush = new EMFPBrush();
+ maEMFPObjects[index].reset(brush);
+ brush->Read(rObjectStream, *this);
+ break;
+ }
+ case EmfPlusObjectTypePen:
+ {
+ EMFPPen *pen = new EMFPPen();
+ maEMFPObjects[index].reset(pen);
+ pen->Read(rObjectStream, *this);
+ pen->penWidth = pen->penWidth * getUnitToPixelMultiplier(static_cast<UnitType>(pen->penUnit), mnHDPI);
+ break;
+ }
+ case EmfPlusObjectTypePath:
+ {
+ sal_uInt32 aVersion, aPathPointCount, aPathPointFlags;
+
+ rObjectStream.ReadUInt32(aVersion).ReadUInt32(aPathPointCount).ReadUInt32(aPathPointFlags);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tVersion: 0x" << std::hex << aVersion);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tNumber of points: " << std::dec << aPathPointCount);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tPath point flags: 0x" << std::hex << aPathPointFlags << std::dec);
+ EMFPPath *path = new EMFPPath(aPathPointCount);
+ maEMFPObjects[index].reset(path);
+ path->Read(rObjectStream, aPathPointFlags);
+ break;
+ }
+ case EmfPlusObjectTypeRegion:
+ {
+ EMFPRegion *region = new EMFPRegion();
+ maEMFPObjects[index].reset(region);
+ region->ReadRegion(rObjectStream, *this);
+ break;
+ }
+ case EmfPlusObjectTypeImage:
+ {
+ EMFPImage *image = new EMFPImage;
+ maEMFPObjects[index].reset(image);
+ image->type = 0;
+ image->width = 0;
+ image->height = 0;
+ image->stride = 0;
+ image->pixelFormat = 0;
+ image->Read(rObjectStream, dataSize, bUseWholeStream);
+ break;
+ }
+ case EmfPlusObjectTypeFont:
+ {
+ EMFPFont *font = new EMFPFont;
+ maEMFPObjects[index].reset(font);
+ font->emSize = 0;
+ font->sizeUnit = 0;
+ font->fontFlags = 0;
+ font->Read(rObjectStream);
+ // tdf#113624 Convert unit to Pixels
+ font->emSize = font->emSize * getUnitToPixelMultiplier(static_cast<UnitType>(font->sizeUnit), mnHDPI);
+
+ break;
+ }
+ case EmfPlusObjectTypeStringFormat:
+ {
+ EMFPStringFormat *stringFormat = new EMFPStringFormat();
+ maEMFPObjects[index].reset(stringFormat);
+ stringFormat->Read(rObjectStream);
+ break;
+ }
+ case EmfPlusObjectTypeImageAttributes:
+ {
+ EMFPImageAttributes *imageAttributes = new EMFPImageAttributes();
+ maEMFPObjects[index].reset(imageAttributes);
+ imageAttributes->Read(rObjectStream);
+ break;
+ }
+ case EmfPlusObjectTypeCustomLineCap:
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object type 'custom line cap' not yet implemented");
+ break;
+ }
+ default:
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object unhandled flags: 0x" << std::hex << (flags & 0xff00) << std::dec);
+ }
+ }
+ }
+
+ void EmfPlusHelperData::ReadPoint(SvStream& s, float& x, float& y, sal_uInt32 flags)
+ {
+ if (flags & 0x800)
+ {
+ // specifies a location in the coordinate space that is relative to
+ // the location specified by the previous element in the array. In the case of the first element in
+ // PointData, a previous location at coordinates (0,0) is assumed.
+ SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Relative coordinates bit detected. Implement parse EMFPlusPointR");
+ }
+
+ if (flags & 0x4000)
+ {
+ sal_Int16 ix, iy;
+
+ s.ReadInt16(ix).ReadInt16(iy);
+
+ x = ix;
+ y = iy;
+ }
+ else
+ {
+ s.ReadFloat(x).ReadFloat(y);
+ }
+ }
+
+ void EmfPlusHelperData::ReadRectangle(SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed)
+ {
+ if (bCompressed)
+ {
+ sal_Int16 ix, iy, iw, ih;
+
+ s.ReadInt16(ix).ReadInt16(iy).ReadInt16(iw).ReadInt16(ih);
+
+ x = ix;
+ y = iy;
+ width = iw;
+ height = ih;
+ }
+ else
+ {
+ s.ReadFloat(x).ReadFloat(y).ReadFloat(width).ReadFloat(height);
+ }
+ }
+
+ bool EmfPlusHelperData::readXForm(SvStream& rIn, basegfx::B2DHomMatrix& rTarget)
+ {
+ rTarget.identity();
+
+ if (sizeof(float) != 4)
+ {
+ OSL_FAIL("EnhWMFReader::sizeof( float ) != 4");
+ return false;
+ }
+ else
+ {
+ float eM11(0.0);
+ float eM12(0.0);
+ float eM21(0.0);
+ float eM22(0.0);
+ float eDx(0.0);
+ float eDy(0.0);
+ rIn.ReadFloat(eM11).ReadFloat(eM12).ReadFloat(eM21).ReadFloat(eM22).ReadFloat(eDx).ReadFloat(eDy);
+ rTarget = basegfx::B2DHomMatrix(
+ eM11, eM21, eDx,
+ eM12, eM22, eDy);
+ }
+
+ return true;
+ }
+
+ void EmfPlusHelperData::mappingChanged()
+ {
+ if (mnPixX == 0 || mnPixY == 0)
+ {
+ SAL_WARN("drawinglayer.emf", "dimensions in pixels is 0");
+ return;
+ }
+ // Call when mnMmX/mnMmY/mnPixX/mnPixY/mnFrameLeft/mnFrameTop/maWorldTransform/ changes.
+ // Currently not used are mnHDPI/mnVDPI/mnFrameRight/mnFrameBottom. *If* these should
+ // be used in the future, this method will need to be called.
+ //
+ // Re-calculate maMapTransform to contain the complete former transformation so that
+ // it can be applied by a single matrix multiplication or be added to an encapsulated
+ // primitive later
+ //
+ // To evtl. correct and see where this came from, please compare with the implementations
+ // of EmfPlusHelperData::MapToDevice and EmfPlusHelperData::Map* in prev versions
+ maMapTransform = maWorldTransform;
+ maMapTransform *= basegfx::utils::createScaleTranslateB2DHomMatrix(100.0 * mnMmX / mnPixX, 100.0 * mnMmY / mnPixY,
+ double(-mnFrameLeft), double(-mnFrameTop));
+ maMapTransform *= maBaseTransform;
+
+ // Used only for performance optimization, to do not calculate it every line draw
+ mdExtractedXScale = std::hypot(maMapTransform.a(), maMapTransform.b());
+ mdExtractedYScale = std::hypot(maMapTransform.c(), maMapTransform.d());
+ }
+
+ ::basegfx::B2DPoint EmfPlusHelperData::Map(double ix, double iy) const
+ {
+ // map in one step using complete MapTransform (see mappingChanged)
+ return maMapTransform * ::basegfx::B2DPoint(ix, iy);
+ }
+
+ Color EmfPlusHelperData::EMFPGetBrushColorOrARGBColor(const sal_uInt16 flags, const sal_uInt32 brushIndexOrColor) const {
+ Color color;
+ if (flags & 0x8000) // we use a color
+ {
+ color = Color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff,
+ (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff);
+ }
+ else // we use a brush
+ {
+ const EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
+ if (brush)
+ {
+ color = brush->GetColor();
+ if (brush->type != BrushTypeSolidColor)
+ SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Brush other than solid color is not supported");
+ }
+ }
+ return color;
+ }
+
+ void EmfPlusHelperData::GraphicStatePush(GraphicStateMap& map, sal_Int32 index)
+ {
+ GraphicStateMap::iterator iter = map.find( index );
+
+ if ( iter != map.end() )
+ {
+ map.erase( iter );
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tStack index: " << index << " found and erased");
+ }
+
+ wmfemfhelper::PropertyHolder state = mrPropertyHolders.Current();
+ // tdf#112500 We need to save world transform somehow, during graphic state push
+ state.setTransformation(maWorldTransform);
+ map[ index ] = state;
+ }
+
+ void EmfPlusHelperData::GraphicStatePop(GraphicStateMap& map, sal_Int32 index)
+ {
+ GraphicStateMap::iterator iter = map.find(index);
+
+ if (iter != map.end())
+ {
+ wmfemfhelper::PropertyHolder state = iter->second;
+
+ maWorldTransform = state.getTransformation();
+ if (state.getClipPolyPolygonActive())
+ {
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Restore clipping region to saved in index: " << index);
+ wmfemfhelper::HandleNewClipRegion(state.getClipPolyPolygon(), mrTargetHolders,
+ mrPropertyHolders);
+ }
+ else
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping");
+ wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders,
+ mrPropertyHolders);
+ }
+ mappingChanged();
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t\tStack index: " << index
+ << " found, maWorldTransform: " << maWorldTransform);
+ }
+ }
+
+ drawinglayer::attribute::LineStartEndAttribute
+ EmfPlusHelperData::CreateLineEnd(const sal_Int32 aCap, const float aPenWidth) const
+ {
+ const double pw = mdExtractedYScale * aPenWidth;
+ if (aCap == LineCapTypeSquare)
+ {
+ basegfx::B2DPolygon aCapPolygon(
+ { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+ }
+ else if (aCap == LineCapTypeRound)
+ {
+ basegfx::B2DPolygon aCapPolygon(
+ { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.9236, -0.3827},
+ {0.7071, -0.7071}, {0.3827, -0.9236}, {0.0, -1.0}, {-0.3827, -0.9236},
+ {-0.7071, -0.7071}, {-0.9236, -0.3827}, {-1.0, 0.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+ }
+ else if (aCap == LineCapTypeTriangle)
+ {
+ basegfx::B2DPolygon aCapPolygon(
+ { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.0, -1.0}, {-1.0, 0.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+ }
+ else if (aCap == LineCapTypeSquareAnchor)
+ {
+ basegfx::B2DPolygon aCapPolygon(
+ { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ 1.5 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+ }
+ else if (aCap == LineCapTypeRoundAnchor)
+ {
+ const basegfx::B2DPolygon aCapPolygon
+ = ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(0.0, 0.0), 1.0, 1.0);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+ }
+ else if (aCap == LineCapTypeDiamondAnchor)
+ {
+ basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 0.0}, {0.5, 0.5},
+ {0.5, 1.0}, {-0.5, 1.0}, {-0.5, 0.5},
+ {-1.0, 0.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+ }
+ else if (aCap == LineCapTypeArrowAnchor)
+ {
+ basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+ }
+ return drawinglayer::attribute::LineStartEndAttribute();
+ }
+
+ void EmfPlusHelperData::EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon,
+ sal_uInt32 penIndex)
+ {
+ const EMFPPen* pen = dynamic_cast<EMFPPen*>(maEMFPObjects[penIndex & 0xff].get());
+ SAL_WARN_IF(!pen, "drawinglayer.emf", "emf+ missing pen");
+
+ if (!(pen && polygon.count()))
+ return;
+
+ const double transformedPenWidth = mdExtractedYScale * pen->penWidth;
+ drawinglayer::attribute::LineAttribute lineAttribute(
+ pen->GetColor().getBColor(), transformedPenWidth, pen->maLineJoin,
+ css::drawing::LineCap_BUTT, //TODO implement PenDataDashedLineCap support here
+ pen->fMiterMinimumAngle);
+
+ drawinglayer::attribute::LineStartEndAttribute aStart;
+ if (pen->penDataFlags & EmfPlusPenDataStartCap)
+ {
+ if ((pen->penDataFlags & EmfPlusPenDataCustomStartCap)
+ && (pen->customStartCap->polygon.begin()->count() > 1))
+ aStart = drawinglayer::attribute::LineStartEndAttribute(
+ pen->customStartCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale
+ * pen->customStartCap->widthScale * pen->penWidth,
+ pen->customStartCap->polygon, false);
+ else
+ aStart = EmfPlusHelperData::CreateLineEnd(pen->startCap, pen->penWidth);
+ }
+
+ drawinglayer::attribute::LineStartEndAttribute aEnd;
+ if (pen->penDataFlags & EmfPlusPenDataEndCap)
+ {
+ if ((pen->penDataFlags & EmfPlusPenDataCustomEndCap)
+ && (pen->customEndCap->polygon.begin()->count() > 1))
+ aEnd = drawinglayer::attribute::LineStartEndAttribute(
+ pen->customEndCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale
+ * pen->customEndCap->widthScale * pen->penWidth,
+ pen->customEndCap->polygon, false);
+ else
+ aEnd = EmfPlusHelperData::CreateLineEnd(pen->endCap, pen->penWidth);
+ }
+
+ if (pen->GetColor().IsTransparent())
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aContainer;
+ if (aStart.isDefault() && aEnd.isDefault())
+ aContainer.append(drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
+ polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale))));
+ else
+ {
+ aContainer.resize(polygon.count());
+ for (sal_uInt32 i = 0; i < polygon.count(); i++)
+ aContainer[i] = drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
+ polygon.getB2DPolygon(i), lineAttribute,
+ pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd));
+ }
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ std::move(aContainer), (255 - pen->GetColor().GetAlpha()) / 255.0));
+ }
+ else
+ {
+ if (aStart.isDefault() && aEnd.isDefault())
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
+ polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale)));
+ else
+ for (sal_uInt32 i = 0; i < polygon.count(); i++)
+ {
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
+ polygon.getB2DPolygon(i), lineAttribute,
+ pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd));
+ }
+ }
+ mrPropertyHolders.Current().setLineColor(pen->GetColor().getBColor());
+ mrPropertyHolders.Current().setLineColorActive(true);
+ mrPropertyHolders.Current().setFillColorActive(false);
+ }
+
+ void EmfPlusHelperData::EMFPPlusFillPolygonSolidColor(const ::basegfx::B2DPolyPolygon& polygon, Color const& color)
+ {
+ if (color.GetAlpha() == 0)
+ return;
+
+ if (!color.IsTransparent())
+ {
+ // not transparent
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ polygon,
+ color.getBColor()));
+ }
+ else
+ {
+ const drawinglayer::primitive2d::Primitive2DReference aPrimitive(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ polygon,
+ color.getBColor()));
+
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ drawinglayer::primitive2d::Primitive2DContainer { aPrimitive },
+ (255 - color.GetAlpha()) / 255.0));
+ }
+ }
+
+ void EmfPlusHelperData::EMFPPlusFillPolygon(const ::basegfx::B2DPolyPolygon& polygon, const bool isColor, const sal_uInt32 brushIndexOrColor)
+ {
+ if (!polygon.count())
+ return;
+
+ if (isColor) // use Color
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, ARGB color: 0x" << std::hex << brushIndexOrColor << std::dec);
+
+ // EMF Alpha (1 byte): An 8-bit unsigned integer that specifies the transparency of the background,
+ // ranging from 0 for completely transparent to 0xFF for completely opaque.
+ const Color color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff, (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff);
+ EMFPPlusFillPolygonSolidColor(polygon, color);
+
+ mrPropertyHolders.Current().setFillColor(color.getBColor());
+ mrPropertyHolders.Current().setFillColorActive(true);
+ mrPropertyHolders.Current().setLineColorActive(false);
+ }
+ else // use Brush
+ {
+ EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, brush slot: " << brushIndexOrColor << " (brush type: " << (brush ? brush->GetType() : -1) << ")");
+
+ // give up in case something wrong happened
+ if( !brush )
+ return;
+
+ mrPropertyHolders.Current().setFillColorActive(false);
+ mrPropertyHolders.Current().setLineColorActive(false);
+
+ if (brush->type == BrushTypeSolidColor)
+ {
+ Color fillColor = brush->solidColor;
+ EMFPPlusFillPolygonSolidColor(polygon, fillColor);
+ }
+ else if (brush->type == BrushTypeHatchFill)
+ {
+ // EMF+ like hatching is currently not supported. These are just color blends which serve as an approximation for some of them
+ // for the others the hatch "background" color (secondColor in brush) is used.
+
+ bool isHatchBlend = true;
+ double blendFactor = 0.0;
+
+ switch (brush->hatchStyle)
+ {
+ case HatchStyle05Percent: blendFactor = 0.05; break;
+ case HatchStyle10Percent: blendFactor = 0.10; break;
+ case HatchStyle20Percent: blendFactor = 0.20; break;
+ case HatchStyle25Percent: blendFactor = 0.25; break;
+ case HatchStyle30Percent: blendFactor = 0.30; break;
+ case HatchStyle40Percent: blendFactor = 0.40; break;
+ case HatchStyle50Percent: blendFactor = 0.50; break;
+ case HatchStyle60Percent: blendFactor = 0.60; break;
+ case HatchStyle70Percent: blendFactor = 0.70; break;
+ case HatchStyle75Percent: blendFactor = 0.75; break;
+ case HatchStyle80Percent: blendFactor = 0.80; break;
+ case HatchStyle90Percent: blendFactor = 0.90; break;
+ default:
+ isHatchBlend = false;
+ break;
+ }
+ Color fillColor;
+ if (isHatchBlend)
+ {
+ fillColor = brush->solidColor;
+ fillColor.Merge(brush->secondColor, static_cast<sal_uInt8>(255 * blendFactor));
+ }
+ else
+ {
+ fillColor = brush->secondColor;
+ }
+ // temporal solution: create a solid colored polygon
+ // TODO create a 'real' hatching primitive
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ polygon,
+ fillColor.getBColor()));
+ }
+ else if (brush->type == BrushTypeTextureFill)
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\tTODO: implement BrushTypeTextureFill brush");
+ }
+ else if (brush->type == BrushTypePathGradient || brush->type == BrushTypeLinearGradient)
+
+ {
+ if (brush->type == BrushTypePathGradient && !(brush->additionalFlags & 0x1))
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t TODO Implement displaying BrushTypePathGradient with Boundary: ");
+ }
+ ::basegfx::B2DHomMatrix aTextureTransformation;
+
+ if (brush->hasTransformation) {
+ aTextureTransformation = brush->brush_transformation;
+
+ // adjust aTextureTransformation for our world space:
+ // -> revert the mapping -> apply the transformation -> map back
+ basegfx::B2DHomMatrix aInvertedMapTrasform(maMapTransform);
+ aInvertedMapTrasform.invert();
+ aTextureTransformation = maMapTransform * aTextureTransformation * aInvertedMapTrasform;
+ }
+
+ // select the stored colors
+ const basegfx::BColor aStartColor = brush->solidColor.getBColor();
+ const basegfx::BColor aEndColor = brush->secondColor.getBColor();
+ drawinglayer::primitive2d::SvgGradientEntryVector aVector;
+
+ if (brush->blendPositions)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tUse blend");
+
+ // store the blendpoints in the vector
+ for (sal_uInt32 i = 0; i < brush->blendPoints; i++)
+ {
+ const double aBlendPoint = brush->blendPositions[i];
+ basegfx::BColor aColor;
+ aColor.setGreen(aStartColor.getGreen() + brush->blendFactors[i] * (aEndColor.getGreen() - aStartColor.getGreen()));
+ aColor.setBlue (aStartColor.getBlue() + brush->blendFactors[i] * (aEndColor.getBlue() - aStartColor.getBlue()));
+ aColor.setRed (aStartColor.getRed() + brush->blendFactors[i] * (aEndColor.getRed() - aStartColor.getRed()));
+ const double aAlpha = brush->solidColor.GetAlpha() + brush->blendFactors[i] * (brush->secondColor.GetAlpha() - brush->solidColor.GetAlpha());
+ aVector.emplace_back(aBlendPoint, aColor, aAlpha / 255.0);
+ }
+ }
+ else if (brush->colorblendPositions)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tUse color blend");
+
+ // store the colorBlends in the vector
+ for (sal_uInt32 i = 0; i < brush->colorblendPoints; i++)
+ {
+ const double aBlendPoint = brush->colorblendPositions[i];
+ const basegfx::BColor aColor = brush->colorblendColors[i].getBColor();
+ aVector.emplace_back(aBlendPoint, aColor, brush->colorblendColors[i].GetAlpha() / 255.0);
+ }
+ }
+ else // ok, no extra points: just start and end
+ {
+ aVector.emplace_back(0.0, aStartColor, brush->solidColor.GetAlpha() / 255.0);
+ aVector.emplace_back(1.0, aEndColor, brush->secondColor.GetAlpha() / 255.0);
+ }
+
+ // get the polygon range to be able to map the start/end/center point correctly
+ // therefore, create a mapping and invert it
+ basegfx::B2DRange aPolygonRange= polygon.getB2DRange();
+ basegfx::B2DHomMatrix aPolygonTransformation = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aPolygonRange.getWidth(),aPolygonRange.getHeight(),
+ aPolygonRange.getMinX(), aPolygonRange.getMinY());
+ aPolygonTransformation.invert();
+
+ if (brush->type == BrushTypeLinearGradient)
+ {
+ // support for public enum EmfPlusWrapMode
+ basegfx::B2DPoint aStartPoint = Map(brush->firstPointX, 0.0);
+ aStartPoint = aPolygonTransformation * aStartPoint;
+ basegfx::B2DPoint aEndPoint = Map(brush->firstPointX + brush->aWidth, 0.0);
+ aEndPoint = aPolygonTransformation * aEndPoint;
+
+ // support for public enum EmfPlusWrapMode
+ drawinglayer::primitive2d::SpreadMethod aSpreadMethod(drawinglayer::primitive2d::SpreadMethod::Pad);
+ switch(brush->wrapMode)
+ {
+ case WrapModeTile:
+ case WrapModeTileFlipY:
+ {
+ aSpreadMethod = drawinglayer::primitive2d::SpreadMethod::Repeat;
+ break;
+ }
+ case WrapModeTileFlipX:
+ case WrapModeTileFlipXY:
+ {
+ aSpreadMethod = drawinglayer::primitive2d::SpreadMethod::Reflect;
+ break;
+ }
+ default:
+ break;
+ }
+
+ // create the same one used for SVG
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D(
+ aTextureTransformation,
+ polygon,
+ std::move(aVector),
+ aStartPoint,
+ aEndPoint,
+ false, // do not use UnitCoordinates
+ aSpreadMethod));
+ }
+ else // BrushTypePathGradient
+ { // TODO The PathGradient is not implemented, and Radial Gradient is used instead
+ basegfx::B2DPoint aCenterPoint = Map(brush->firstPointX, brush->firstPointY);
+ aCenterPoint = aPolygonTransformation * aCenterPoint;
+
+ // create the same one used for SVG
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D(
+ aTextureTransformation,
+ polygon,
+ std::move(aVector),
+ aCenterPoint,
+ 0.7, // relative radius little bigger to cover all elements
+ true, // use UnitCoordinates to stretch the gradient
+ drawinglayer::primitive2d::SpreadMethod::Pad,
+ nullptr));
+ }
+ }
+ }
+ }
+
+ EmfPlusHelperData::EmfPlusHelperData(
+ SvMemoryStream& rMS,
+ wmfemfhelper::TargetHolders& rTargetHolders,
+ wmfemfhelper::PropertyHolders& rPropertyHolders)
+ : mfPageScale(0.0),
+ mnOriginX(0),
+ mnOriginY(0),
+ mnHDPI(0),
+ mnVDPI(0),
+ mbSetTextContrast(false),
+ mnTextContrast(0),
+ mnFrameLeft(0),
+ mnFrameTop(0),
+ mnFrameRight(0),
+ mnFrameBottom(0),
+ mnPixX(0),
+ mnPixY(0),
+ mnMmX(0),
+ mnMmY(0),
+ mbMultipart(false),
+ mMFlags(0),
+ mrTargetHolders(rTargetHolders),
+ mrPropertyHolders(rPropertyHolders),
+ bIsGetDCProcessing(false)
+ {
+ rMS.ReadInt32(mnFrameLeft).ReadInt32(mnFrameTop).ReadInt32(mnFrameRight).ReadInt32(mnFrameBottom);
+ SAL_INFO("drawinglayer.emf", "EMF+ picture frame: " << mnFrameLeft << "," << mnFrameTop << " - " << mnFrameRight << "," << mnFrameBottom);
+ rMS.ReadInt32(mnPixX).ReadInt32(mnPixY).ReadInt32(mnMmX).ReadInt32(mnMmY);
+ SAL_INFO("drawinglayer.emf", "EMF+ ref device pixel size: " << mnPixX << "x" << mnPixY << " mm size: " << mnMmX << "x" << mnMmY);
+ readXForm(rMS, maBaseTransform);
+ SAL_INFO("drawinglayer.emf", "EMF+ base transform: " << maBaseTransform);
+ mappingChanged();
+ }
+
+ EmfPlusHelperData::~EmfPlusHelperData()
+ {
+ }
+
+ ::basegfx::B2DPolyPolygon EmfPlusHelperData::combineClip(::basegfx::B2DPolyPolygon const & leftPolygon, int combineMode, ::basegfx::B2DPolyPolygon const & rightPolygon)
+ {
+ basegfx::B2DPolyPolygon aClippedPolyPolygon;
+ switch (combineMode)
+ {
+ case EmfPlusCombineModeReplace:
+ {
+ aClippedPolyPolygon = rightPolygon;
+ break;
+ }
+ case EmfPlusCombineModeIntersect:
+ {
+ aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon(
+ leftPolygon, rightPolygon, true, false);
+ break;
+ }
+ case EmfPlusCombineModeUnion:
+ {
+ aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationOr(leftPolygon, rightPolygon);
+ break;
+ }
+ case EmfPlusCombineModeXOR:
+ {
+ aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationXor(leftPolygon, rightPolygon);
+ break;
+ }
+ case EmfPlusCombineModeExclude:
+ {
+ // Replaces the existing region with the part of itself that is not in the new region.
+ aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(leftPolygon, rightPolygon);
+ break;
+ }
+ case EmfPlusCombineModeComplement:
+ {
+ // Replaces the existing region with the part of the new region that is not in the existing region.
+ aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(rightPolygon, leftPolygon);
+ break;
+ }
+ }
+ return aClippedPolyPolygon;
+ }
+
+ void EmfPlusHelperData::processEmfPlusData(
+ SvMemoryStream& rMS,
+ const drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/)
+ {
+ sal_uInt64 length = rMS.GetSize();
+
+ if (length < 12)
+ SAL_WARN("drawinglayer.emf", "length is less than required header size");
+
+ // 12 is minimal valid EMF+ record size; remaining bytes are padding
+ while (length >= 12)
+ {
+ sal_uInt16 type, flags;
+ sal_uInt32 size, dataSize;
+ sal_uInt64 next;
+
+ rMS.ReadUInt16(type).ReadUInt16(flags).ReadUInt32(size).ReadUInt32(dataSize);
+
+ next = rMS.Tell() + (size - 12);
+
+ if (size < 12)
+ {
+ SAL_WARN("drawinglayer.emf", "Size field is less than 12 bytes");
+ break;
+ }
+ else if (size > length)
+ {
+ SAL_WARN("drawinglayer.emf", "Size field is greater than bytes left");
+ break;
+ }
+
+ if (dataSize > (size - 12))
+ {
+ SAL_WARN("drawinglayer.emf", "DataSize field is greater than Size-12");
+ break;
+ }
+
+ SAL_INFO("drawinglayer.emf", "EMF+ " << emfTypeToName(type) << " (0x" << std::hex << type << ")" << std::dec);
+ SAL_INFO("drawinglayer.emf", "EMF+\t record size: " << size);
+ SAL_INFO("drawinglayer.emf", "EMF+\t flags: 0x" << std::hex << flags << std::dec);
+ SAL_INFO("drawinglayer.emf", "EMF+\t data size: " << dataSize);
+
+ if (bIsGetDCProcessing)
+ {
+ if (aGetDCState.getClipPolyPolygonActive())
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t Restore region to GetDC saved");
+ wmfemfhelper::HandleNewClipRegion(aGetDCState.getClipPolyPolygon(), mrTargetHolders,
+ mrPropertyHolders);
+ }
+ else
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping");
+ wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders,
+ mrPropertyHolders);
+ }
+ bIsGetDCProcessing = false;
+ }
+ if (type == EmfPlusRecordTypeObject && ((mbMultipart && (flags & 0x7fff) == (mMFlags & 0x7fff)) || (flags & 0x8000)))
+ {
+ if (!mbMultipart)
+ {
+ mbMultipart = true;
+ mMFlags = flags;
+ mMStream.Seek(0);
+ }
+
+ OSL_ENSURE(dataSize >= 4, "No room for TotalObjectSize in EmfPlusContinuedObjectRecord");
+
+ // 1st 4 bytes are TotalObjectSize
+ mMStream.WriteBytes(static_cast<const char *>(rMS.GetData()) + rMS.Tell() + 4, dataSize - 4);
+ SAL_INFO("drawinglayer.emf", "EMF+ read next object part size: " << size << " type: " << type << " flags: " << flags << " data size: " << dataSize);
+ }
+ else
+ {
+ if (mbMultipart)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+ multipart record flags: " << mMFlags);
+ mMStream.Seek(0);
+ processObjectRecord(mMStream, mMFlags, 0, true);
+ }
+
+ mbMultipart = false;
+ }
+
+ if (type != EmfPlusRecordTypeObject || !(flags & 0x8000))
+ {
+ switch (type)
+ {
+ case EmfPlusRecordTypeHeader:
+ {
+ sal_uInt32 header, version;
+
+ rMS.ReadUInt32(header).ReadUInt32(version).ReadUInt32(mnHDPI).ReadUInt32(mnVDPI);
+ SAL_INFO("drawinglayer.emf", "EMF+\tHeader: 0x" << std::hex << header);
+ SAL_INFO("drawinglayer.emf", "EMF+\tVersion: " << std::dec << version);
+ SAL_INFO("drawinglayer.emf", "EMF+\tHorizontal DPI: " << mnHDPI);
+ SAL_INFO("drawinglayer.emf", "EMF+\tVertical DPI: " << mnVDPI);
+ SAL_INFO("drawinglayer.emf", "EMF+\tDual: " << ((flags & 1) ? "true" : "false"));
+ break;
+ }
+ case EmfPlusRecordTypeEndOfFile:
+ {
+ break;
+ }
+ case EmfPlusRecordTypeComment:
+ {
+#if OSL_DEBUG_LEVEL > 1
+ unsigned char data;
+ OUString hexdata;
+
+ SAL_INFO("drawinglayer.emf", "EMF+\tDatasize: 0x" << std::hex << dataSize << std::dec);
+
+ for (sal_uInt32 i=0; i<dataSize; i++)
+ {
+ rMS.ReadUChar(data);
+
+ if (i % 16 == 0)
+ hexdata += "\n";
+
+ OUString padding;
+ if ((data & 0xF0) == 0)
+ padding = "0";
+
+ hexdata += "0x" + padding + OUString::number(data, 16) + " ";
+ }
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t" << hexdata);
+#endif
+ break;
+ }
+ case EmfPlusRecordTypeGetDC:
+ {
+ bIsGetDCProcessing = true;
+ aGetDCState = mrPropertyHolders.Current();
+ SAL_INFO("drawinglayer.emf", "EMF+\tAlready used in svtools wmf/emf filter parser");
+ break;
+ }
+ case EmfPlusRecordTypeObject:
+ {
+ processObjectRecord(rMS, flags, dataSize);
+ break;
+ }
+ case EmfPlusRecordTypeFillPie:
+ case EmfPlusRecordTypeDrawPie:
+ case EmfPlusRecordTypeDrawArc:
+ {
+ float startAngle, sweepAngle;
+
+ // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
+ sal_uInt32 brushIndexOrColor = 999;
+
+ if (type == EmfPlusRecordTypeFillPie)
+ {
+ rMS.ReadUInt32(brushIndexOrColor);
+ SAL_INFO("drawinglayer.emf", "EMF+\t FillPie colorOrIndex: " << brushIndexOrColor);
+ }
+ else if (type == EmfPlusRecordTypeDrawPie)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t DrawPie");
+ }
+ else
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t DrawArc");
+ }
+
+ rMS.ReadFloat(startAngle).ReadFloat(sweepAngle);
+ float dx, dy, dw, dh;
+ ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
+ SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
+ startAngle = basegfx::deg2rad(startAngle);
+ sweepAngle = basegfx::deg2rad(sweepAngle);
+ float endAngle = startAngle + sweepAngle;
+ startAngle = fmodf(startAngle, static_cast<float>(M_PI * 2));
+
+ if (startAngle < 0.0)
+ {
+ startAngle += static_cast<float>(M_PI * 2.0);
+ }
+ endAngle = fmodf(endAngle, static_cast<float>(M_PI * 2.0));
+
+ if (endAngle < 0.0)
+ {
+ endAngle += static_cast<float>(M_PI * 2.0);
+ }
+ if (sweepAngle < 0)
+ {
+ std::swap(endAngle, startAngle);
+ }
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t Adjusted angles: start " <<
+ basegfx::rad2deg(startAngle) << ", end: " << basegfx::rad2deg(endAngle) <<
+ " startAngle: " << startAngle << " sweepAngle: " << sweepAngle);
+ const ::basegfx::B2DPoint centerPoint(dx + 0.5 * dw, dy + 0.5 * dh);
+ ::basegfx::B2DPolygon polygon(
+ ::basegfx::utils::createPolygonFromEllipseSegment(centerPoint,
+ 0.5 * dw, 0.5 * dh,
+ startAngle, endAngle));
+ if (type != EmfPlusRecordTypeDrawArc)
+ {
+ polygon.append(centerPoint);
+ polygon.setClosed(true);
+ }
+ ::basegfx::B2DPolyPolygon polyPolygon(polygon);
+ polyPolygon.transform(maMapTransform);
+ if (type == EmfPlusRecordTypeFillPie)
+ EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor);
+ else
+ EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
+ }
+ break;
+ case EmfPlusRecordTypeFillPath:
+ {
+ sal_uInt32 index = flags & 0xff;
+ sal_uInt32 brushIndexOrColor;
+ rMS.ReadUInt32(brushIndexOrColor);
+ SAL_INFO("drawinglayer.emf", "EMF+ FillPath slot: " << index);
+
+ EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[index].get());
+ if (path)
+ EMFPPlusFillPolygon(path->GetPolygon(*this), flags & 0x8000, brushIndexOrColor);
+ else
+ SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillPath missing path");
+ }
+ break;
+ case EmfPlusRecordTypeFillRegion:
+ {
+ sal_uInt32 index = flags & 0xff;
+ sal_uInt32 brushIndexOrColor;
+ rMS.ReadUInt32(brushIndexOrColor);
+ SAL_INFO("drawinglayer.emf", "EMF+\t FillRegion slot: " << index);
+
+ EMFPRegion* region = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get());
+ if (region)
+ EMFPPlusFillPolygon(region->regionPolyPolygon, flags & 0x8000, brushIndexOrColor);
+ else
+ SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillRegion missing region");
+ }
+ break;
+ case EmfPlusRecordTypeDrawEllipse:
+ case EmfPlusRecordTypeFillEllipse:
+ {
+ // Intentionally very bogus initial value to avoid MSVC complaining about potentially uninitialized local
+ // variable. As long as the code stays as intended, this variable will be assigned a (real) value in the case
+ // when it is later used.
+ sal_uInt32 brushIndexOrColor = 1234567;
+
+ if (type == EmfPlusRecordTypeFillEllipse)
+ {
+ rMS.ReadUInt32(brushIndexOrColor);
+ }
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t " << (type == EmfPlusRecordTypeFillEllipse ? "Fill" : "Draw") << "Ellipse slot: " << (flags & 0xff));
+ float dx, dy, dw, dh;
+ ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
+ SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
+ ::basegfx::B2DPolyPolygon polyPolygon(
+ ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(dx + 0.5 * dw, dy + 0.5 * dh),
+ 0.5 * dw, 0.5 * dh));
+ polyPolygon.transform(maMapTransform);
+ if (type == EmfPlusRecordTypeFillEllipse)
+ EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor);
+ else
+ EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
+ }
+ break;
+ case EmfPlusRecordTypeFillRects:
+ case EmfPlusRecordTypeDrawRects:
+ {
+ // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
+ sal_uInt32 brushIndexOrColor = 999;
+ sal_Int32 rectangles;
+ const bool isColor = (flags & 0x8000);
+ ::basegfx::B2DPolygon polygon;
+
+ if (EmfPlusRecordTypeFillRects == type)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t FillRects");
+ rMS.ReadUInt32(brushIndexOrColor);
+ SAL_INFO("drawinglayer.emf", "EMF+\t" << (isColor ? "color" : "brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec);
+ }
+ else
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t DrawRects");
+ }
+
+ rMS.ReadInt32(rectangles);
+
+ for (int i = 0; i < rectangles; i++)
+ {
+ float x, y, width, height;
+ ReadRectangle(rMS, x, y, width, height, bool(flags & 0x4000));
+ polygon.clear();
+ polygon.append(Map(x, y));
+ polygon.append(Map(x + width, y));
+ polygon.append(Map(x + width, y + height));
+ polygon.append(Map(x, y + height));
+ polygon.setClosed(true);
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t rectangle: " << x << ", "<< y << " " << width << "x" << height);
+
+ ::basegfx::B2DPolyPolygon polyPolygon(polygon);
+ if (type == EmfPlusRecordTypeFillRects)
+ EMFPPlusFillPolygon(polyPolygon, isColor, brushIndexOrColor);
+ else
+ EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
+ }
+ break;
+ }
+ case EmfPlusRecordTypeFillPolygon:
+ {
+ sal_uInt32 brushIndexOrColor, points;
+
+ rMS.ReadUInt32(brushIndexOrColor);
+ rMS.ReadUInt32(points);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Points: " << points);
+ SAL_INFO("drawinglayer.emf", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << " : 0x" << std::hex << brushIndexOrColor << std::dec);
+
+ EMFPPath path(points, true);
+ path.Read(rMS, flags);
+
+ EMFPPlusFillPolygon(path.GetPolygon(*this), flags & 0x8000, brushIndexOrColor);
+ break;
+ }
+ case EmfPlusRecordTypeDrawLines:
+ {
+ sal_uInt32 points;
+ rMS.ReadUInt32(points);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Points: " << points);
+ EMFPPath path(points, true);
+ path.Read(rMS, flags);
+
+ // 0x2000 bit indicates whether to draw an extra line between the last point
+ // and the first point, to close the shape.
+ EMFPPlusDrawPolygon(path.GetPolygon(*this, true, (flags & 0x2000)), flags);
+
+ break;
+ }
+ case EmfPlusRecordTypeDrawPath:
+ {
+ sal_uInt32 penIndex;
+ rMS.ReadUInt32(penIndex);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Pen: " << penIndex);
+
+ EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get());
+ if (path)
+ EMFPPlusDrawPolygon(path->GetPolygon(*this), penIndex);
+ else
+ SAL_WARN("drawinglayer.emf", "\t\tEmfPlusRecordTypeDrawPath missing path");
+
+ break;
+ }
+ case EmfPlusRecordTypeDrawBeziers:
+ {
+ sal_uInt32 aCount;
+ float x1, y1, x2, y2, x3, y3, x4, y4;
+ ::basegfx::B2DPoint aStartPoint, aControlPointA, aControlPointB, aEndPoint;
+ ::basegfx::B2DPolygon aPolygon;
+ rMS.ReadUInt32(aCount);
+ SAL_INFO("drawinglayer.emf", "EMF+\t DrawBeziers slot: " << (flags & 0xff));
+ SAL_INFO("drawinglayer.emf", "EMF+\t Number of points: " << aCount);
+ SAL_WARN_IF((aCount - 1) % 3 != 0, "drawinglayer.emf",
+ "EMF+\t Bezier Draw not support number of points other than 4, 7, "
+ "10, 13, 16...");
+
+ if (aCount < 4)
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t Bezier Draw does not support less "
+ "than 4 points. Number of points: "
+ << aCount);
+ break;
+ }
+
+ ReadPoint(rMS, x1, y1, flags);
+ // We need to add first starting point
+ aStartPoint = Map(x1, y1);
+ aPolygon.append(aStartPoint);
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Bezier starting point: " << x1 << "," << y1);
+ for (sal_uInt32 i = 4; i <= aCount; i += 3)
+ {
+ ReadPoint(rMS, x2, y2, flags);
+ ReadPoint(rMS, x3, y3, flags);
+ ReadPoint(rMS, x4, y4, flags);
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Bezier points: " << x2 << "," << y2 << " " << x3 << ","
+ << y3 << " " << x4 << "," << y4);
+
+ aControlPointA = Map(x2, y2);
+ aControlPointB = Map(x3, y3);
+ aEndPoint = Map(x4, y4);
+ aPolygon.appendBezierSegment(aControlPointA, aControlPointB, aEndPoint);
+ // The ending coordinate of one Bezier curve is the starting coordinate of the next.
+ aStartPoint = aEndPoint;
+ }
+ EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff);
+ break;
+ }
+ case EmfPlusRecordTypeDrawClosedCurve:
+ case EmfPlusRecordTypeFillClosedCurve:
+ {
+ // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
+ sal_uInt32 brushIndexOrColor = 999, points;
+ float aTension;
+ if (type == EmfPlusRecordTypeFillClosedCurve)
+ {
+ rMS.ReadUInt32(brushIndexOrColor);
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Fill Mode: " << (flags & 0x2000 ? "Winding" : "Alternate"));
+ }
+ rMS.ReadFloat(aTension);
+ rMS.ReadUInt32(points);
+ SAL_WARN("drawinglayer.emf",
+ "EMF+\t Tension: " << aTension << " Points: " << points);
+ SAL_WARN_IF(aTension != 0, "drawinglayer.emf",
+ "EMF+\t TODO Add support for tension different than 0");
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t " << (flags & 0x8000 ? "Color" : "Brush index") << " : 0x"
+ << std::hex << brushIndexOrColor << std::dec);
+
+ EMFPPath path(points, true);
+ path.Read(rMS, flags);
+ if (type == EmfPlusRecordTypeFillClosedCurve)
+ EMFPPlusFillPolygon(path.GetPolygon(*this, /* bMapIt */ true,
+ /*bAddLineToCloseShape */ true),
+ flags & 0x8000, brushIndexOrColor);
+ else
+ EMFPPlusDrawPolygon(path.GetPolygon(*this, /* bMapIt */ true,
+ /*bAddLineToCloseShape */ true),
+ flags & 0xff);
+ break;
+ }
+ case EmfPlusRecordTypeDrawImage:
+ case EmfPlusRecordTypeDrawImagePoints:
+ {
+ sal_uInt32 imageAttributesId;
+ sal_Int32 sourceUnit;
+ rMS.ReadUInt32(imageAttributesId).ReadInt32(sourceUnit);
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t " << (type == EmfPlusRecordTypeDrawImage ? "DrawImage"
+ : "DrawImagePoints")
+ << " image attributes Id: " << imageAttributesId
+ << " source unit: " << sourceUnit);
+ SAL_INFO("drawinglayer.emf", "EMF+\t TODO: use image attributes");
+
+ // Source unit of measurement type must be 1 pixel
+ if (EMFPImage* image = sourceUnit == UnitTypePixel ?
+ dynamic_cast<EMFPImage*>(maEMFPObjects[flags & 0xff].get()) :
+ nullptr)
+ {
+ float sx, sy, sw, sh;
+ ReadRectangle(rMS, sx, sy, sw, sh);
+
+ ::tools::Rectangle aSource(Point(sx, sy), Size(sw + 1, sh + 1));
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t "
+ << (type == EmfPlusRecordTypeDrawImage ? "DrawImage"
+ : "DrawImagePoints")
+ << " source rectangle: " << sx << "," << sy << " " << sw << "x"
+ << sh);
+
+ float dx(0.), dy(0.), dw(0.), dh(0.);
+ double fShearX = 0.0;
+ double fShearY = 0.0;
+ if (type == EmfPlusRecordTypeDrawImagePoints)
+ {
+ sal_uInt32 aCount;
+ rMS.ReadUInt32(aCount);
+
+ // Number of points used by DrawImagePoints. Exactly 3 points must be specified.
+ if (aCount != 3)
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t Wrong EMF+ file. Expected "
+ "3 points, received: "
+ << aCount);
+ break;
+ }
+ float x1, y1, x2, y2, x3, y3;
+
+ ReadPoint(rMS, x1, y1, flags); // upper-left point
+ ReadPoint(rMS, x2, y2, flags); // upper-right
+ ReadPoint(rMS, x3, y3, flags); // lower-left
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t destination points: "
+ << x1 << "," << y1 << " " << x2 << ","
+ << y2 << " " << x3 << "," << y3);
+ dx = x1;
+ dy = y2;
+ dw = x2 - x1;
+ dh = y3 - y1;
+ fShearX = x3 - x1;
+ fShearY = y2 - y1;
+ }
+ else if (type == EmfPlusRecordTypeDrawImage)
+ ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Rectangle: " << dx << "," << dy << " " << dw << "x" << dh);
+ Size aSize;
+ if (image->type == ImageDataTypeBitmap)
+ {
+ aSize = image->graphic.GetBitmapEx().GetSizePixel();
+ SAL_INFO("drawinglayer.emf", "EMF+\t Bitmap size: " << aSize.Width()
+ << "x"
+ << aSize.Height());
+ if (sx < 0)
+ {
+ // If src position is negative then we need shift image to right
+ dx = dx + ((-sx) / sw) * dw;
+ if (sx + sw <= aSize.Width())
+ dw = ((sw + sx) / sw) * dw;
+ else
+ dw = (aSize.Width() / sw) * dw;
+ }
+ else if (sx + sw > aSize.Width())
+ // If the src image is smaller that what we want to cut, then we need to scale down
+ dw = ((aSize.Width() - sx) / sw) * dw;
+
+ if (sy < 0)
+ {
+ dy = dy + ((-sy) / sh) * dh;
+ if (sy + sh <= aSize.Height())
+ dh = ((sh + sy) / sh) * dh;
+ else
+ dh = (aSize.Height() / sh) * dh;
+ }
+ else if (sy + sh > aSize.Height())
+ dh = ((aSize.Height() - sy) / sh) * dh;
+ }
+ else
+ SAL_INFO(
+ "drawinglayer.emf",
+ "EMF+\t TODO: Add support for SrcRect to ImageDataTypeMetafile");
+ ::basegfx::B2DPoint aDstPoint(dx, dy);
+ ::basegfx::B2DSize aDstSize(dw, dh);
+
+ const basegfx::B2DHomMatrix aTransformMatrix
+ = maMapTransform
+ * basegfx::B2DHomMatrix(
+ /* Row 0, Column 0 */ aDstSize.getX(),
+ /* Row 0, Column 1 */ fShearX,
+ /* Row 0, Column 2 */ aDstPoint.getX(),
+ /* Row 1, Column 0 */ fShearY,
+ /* Row 1, Column 1 */ aDstSize.getY(),
+ /* Row 1, Column 2 */ aDstPoint.getY());
+
+ if (image->type == ImageDataTypeBitmap)
+ {
+ BitmapEx aBmp(image->graphic.GetBitmapEx());
+ aBmp.Crop(aSource);
+ aSize = aBmp.GetSizePixel();
+ if (aSize.Width() > 0 && aSize.Height() > 0)
+ {
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(aBmp), aTransformMatrix));
+ }
+ else
+ SAL_WARN("drawinglayer.emf", "EMF+\t warning: empty bitmap");
+ }
+ else if (image->type == ImageDataTypeMetafile)
+ {
+ GDIMetaFile aGDI(image->graphic.GetGDIMetaFile());
+ aGDI.Clip(aSource);
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::MetafilePrimitive2D(aTransformMatrix,
+ aGDI));
+ }
+ }
+ else
+ {
+ SAL_WARN("drawinglayer.emf",
+ "EMF+\tDrawImage(Points) Wrong EMF+ file. Only Unit Type Pixel is "
+ "support by EMF+ specification for DrawImage(Points)");
+ }
+ break;
+ }
+ case EmfPlusRecordTypeDrawString:
+ {
+ sal_uInt32 brushId, formatId, stringLength;
+ rMS.ReadUInt32(brushId).ReadUInt32(formatId).ReadUInt32(stringLength);
+ SAL_INFO("drawinglayer.emf", "EMF+\t FontId: " << OUString::number(flags & 0xFF));
+ SAL_INFO("drawinglayer.emf", "EMF+\t BrushId: " << BrushIDToString(flags, brushId));
+ SAL_INFO("drawinglayer.emf", "EMF+\t FormatId: " << formatId);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Length: " << stringLength);
+
+ // read the layout rectangle
+ float lx, ly, lw, lh;
+ rMS.ReadFloat(lx).ReadFloat(ly).ReadFloat(lw).ReadFloat(lh);
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t DrawString layoutRect: " << lx << "," << ly << " - " << lw << "x" << lh);
+ // parse the string
+ const OUString text = read_uInt16s_ToOUString(rMS, stringLength);
+ SAL_INFO("drawinglayer.emf", "EMF+\t DrawString string: " << text);
+ // get the stringFormat from the Object table ( this is OPTIONAL and may be nullptr )
+ const EMFPStringFormat *stringFormat = dynamic_cast<EMFPStringFormat*>(maEMFPObjects[formatId & 0xff].get());
+ // get the font from the flags
+ const EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get());
+ if (!font)
+ {
+ break;
+ }
+ mrPropertyHolders.Current().setFont(vcl::Font(font->family, Size(font->emSize, font->emSize)));
+
+ drawinglayer::attribute::FontAttribute fontAttribute(
+ font->family, // font family
+ "", // (no) font style
+ font->Bold() ? 8u : 1u, // weight: 8 = bold
+ font->family == "SYMBOL", // symbol
+ stringFormat && stringFormat->DirectionVertical(), // vertical
+ font->Italic(), // italic
+ false, // monospaced
+ false, // outline = false, no such thing in MS-EMFPLUS
+ stringFormat && stringFormat->DirectionRightToLeft(), // right-to-left
+ false); // BiDiStrong
+
+ css::lang::Locale locale;
+ double stringAlignmentHorizontalOffset = 0.0;
+ double stringAlignmentVerticalOffset = font->emSize;
+ if (stringFormat)
+ {
+ LanguageTag aLanguageTag(static_cast<LanguageType>(stringFormat->language));
+ locale = aLanguageTag.getLocale();
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
+
+ aTextLayouter.setFontAttribute(fontAttribute, font->emSize,
+ font->emSize, locale);
+
+ double fTextWidth = aTextLayouter.getTextWidth(text, 0, stringLength);
+ SAL_WARN_IF(stringFormat->DirectionRightToLeft(), "drawinglayer.emf",
+ "EMF+\t DrawString Alignment TODO For a right-to-left layout rectangle, the origin should be at the upper right.");
+ if (stringFormat->stringAlignment == StringAlignmentNear)
+ // Alignment is to the left side of the layout rectangle (lx, ly, lw, lh)
+ stringAlignmentHorizontalOffset = stringFormat->leadingMargin * font->emSize;
+ else if (stringFormat->stringAlignment == StringAlignmentCenter)
+ // Alignment is centered between the origin and extent of the layout rectangle
+ stringAlignmentHorizontalOffset = 0.5 * lw + (stringFormat->leadingMargin - stringFormat->trailingMargin) * font->emSize - 0.5 * fTextWidth;
+ else if (stringFormat->stringAlignment == StringAlignmentFar)
+ // Alignment is to the right side of the layout rectangle
+ stringAlignmentHorizontalOffset = lw - stringFormat->trailingMargin * font->emSize - fTextWidth;
+
+ if (stringFormat->lineAlign == StringAlignmentNear)
+ stringAlignmentVerticalOffset = font->emSize;
+ else if (stringFormat->lineAlign == StringAlignmentCenter)
+ stringAlignmentVerticalOffset = 0.5 * lh + 0.5 * font->emSize;
+ else if (stringFormat->lineAlign == StringAlignmentFar)
+ stringAlignmentVerticalOffset = lh;
+ }
+ else
+ {
+ // By default LeadingMargin is 1/6 inch
+ // TODO for typographic fonts set value to 0.
+ stringAlignmentHorizontalOffset = 16.0;
+
+ // use system default
+ locale = Application::GetSettings().GetLanguageTag().getLocale();
+ }
+
+ const basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ ::basegfx::B2DSize(font->emSize, font->emSize),
+ ::basegfx::B2DPoint(lx + stringAlignmentHorizontalOffset,
+ ly + stringAlignmentVerticalOffset));
+
+ Color uncorrectedColor = EMFPGetBrushColorOrARGBColor(flags, brushId);
+ Color color;
+
+ if (mbSetTextContrast)
+ {
+ const auto gammaVal = mnTextContrast / 1000;
+ const basegfx::BColorModifier_gamma gamma(gammaVal);
+
+ // gamma correct transparency color
+ sal_uInt16 alpha = uncorrectedColor.GetAlpha();
+ alpha = std::clamp(std::pow(alpha, 1.0 / gammaVal), 0.0, 1.0) * 255;
+
+ basegfx::BColor modifiedColor(gamma.getModifiedColor(uncorrectedColor.getBColor()));
+ color.SetRed(modifiedColor.getRed() * 255);
+ color.SetGreen(modifiedColor.getGreen() * 255);
+ color.SetBlue(modifiedColor.getBlue() * 255);
+ color.SetAlpha(alpha);
+ }
+ else
+ {
+ color = uncorrectedColor;
+ }
+
+ mrPropertyHolders.Current().setTextColor(color.getBColor());
+ mrPropertyHolders.Current().setTextColorActive(true);
+
+ if (color.GetAlpha() > 0)
+ {
+ std::vector<double> emptyVector;
+ rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText;
+ if (font->Underline() || font->Strikeout())
+ {
+ pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
+ transformMatrix,
+ text,
+ 0, // text always starts at 0
+ stringLength,
+ std::move(emptyVector), // EMF-PLUS has no DX-array
+ fontAttribute,
+ locale,
+ color.getBColor(), // Font Color
+ COL_TRANSPARENT, // Fill Color
+ color.getBColor(), // OverlineColor
+ color.getBColor(), // TextlineColor
+ drawinglayer::primitive2d::TEXT_LINE_NONE,
+ font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
+ false,
+ font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE);
+ }
+ else
+ {
+ pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ transformMatrix,
+ text,
+ 0, // text always starts at 0
+ stringLength,
+ std::move(emptyVector), // EMF-PLUS has no DX-array
+ fontAttribute,
+ locale,
+ color.getBColor());
+ }
+ drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText);
+ if (color.IsTransparent())
+ {
+ aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText },
+ (255 - color.GetAlpha()) / 255.0);
+ }
+
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ maMapTransform,
+ drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } ));
+ }
+ break;
+ }
+ case EmfPlusRecordTypeSetPageTransform:
+ {
+ rMS.ReadFloat(mfPageScale);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Scale: " << mfPageScale << " unit: " << UnitTypeToString(flags));
+
+ if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld))
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by SetPageTransform in EMF+ specification.");
+ }
+ else
+ {
+ mnMmX *= mfPageScale * getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnHDPI);
+ mnMmY *= mfPageScale * getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnVDPI);
+ mappingChanged();
+ }
+ break;
+ }
+ case EmfPlusRecordTypeSetRenderingOrigin:
+ {
+ rMS.ReadInt32(mnOriginX).ReadInt32(mnOriginY);
+ SAL_INFO("drawinglayer.emf", "EMF+\t SetRenderingOrigin, [x,y]: " << mnOriginX << "," << mnOriginY);
+ break;
+ }
+ case EmfPlusRecordTypeSetTextContrast:
+ {
+ const sal_uInt16 LOWERGAMMA = 1000;
+ const sal_uInt16 UPPERGAMMA = 2200;
+
+ mbSetTextContrast = true;
+ mnTextContrast = flags & 0xFFF;
+ SAL_WARN_IF(mnTextContrast > UPPERGAMMA || mnTextContrast < LOWERGAMMA,
+ "drawinglayer.emf", "EMF+\t Gamma value is not with bounds 1000 to 2200, value is " << mnTextContrast);
+ mnTextContrast = std::min(mnTextContrast, UPPERGAMMA);
+ mnTextContrast = std::max(mnTextContrast, LOWERGAMMA);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Text contrast: " << (mnTextContrast / 1000) << " gamma");
+ break;
+ }
+ case EmfPlusRecordTypeSetTextRenderingHint:
+ {
+ sal_uInt8 nTextRenderingHint = (flags & 0xFF) >> 1;
+ SAL_INFO("drawinglayer.emf", "EMF+\t Text rendering hint: " << TextRenderingHintToString(nTextRenderingHint));
+ SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetTextRenderingHint");
+ break;
+ }
+ case EmfPlusRecordTypeSetAntiAliasMode:
+ {
+ bool bUseAntiAlias = (flags & 0x0001);
+ sal_uInt8 nSmoothingMode = (flags & 0xFE00) >> 1;
+ SAL_INFO("drawinglayer.emf", "EMF+\t Antialiasing: " << (bUseAntiAlias ? "enabled" : "disabled"));
+ SAL_INFO("drawinglayer.emf", "EMF+\t Smoothing mode: " << SmoothingModeToString(nSmoothingMode));
+ SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetAntiAliasMode");
+ break;
+ }
+ case EmfPlusRecordTypeSetInterpolationMode:
+ {
+ sal_uInt16 nInterpolationMode = flags & 0xFF;
+ SAL_INFO("drawinglayer.emf", "EMF+\t Interpolation mode: " << InterpolationModeToString(nInterpolationMode));
+ SAL_WARN("drawinglayer.emf", "EMF+\t TODO InterpolationMode");
+ break;
+ }
+ case EmfPlusRecordTypeSetPixelOffsetMode:
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t Pixel offset mode: " << PixelOffsetModeToString(flags));
+ SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetPixelOffsetMode");
+ break;
+ }
+ case EmfPlusRecordTypeSetCompositingQuality:
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t TODO SetCompositingQuality");
+ break;
+ }
+ case EmfPlusRecordTypeSave:
+ {
+ sal_uInt32 stackIndex;
+ rMS.ReadUInt32(stackIndex);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Save stack index: " << stackIndex);
+
+ GraphicStatePush(mGSStack, stackIndex);
+
+ break;
+ }
+ case EmfPlusRecordTypeRestore:
+ {
+ sal_uInt32 stackIndex;
+ rMS.ReadUInt32(stackIndex);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Restore stack index: " << stackIndex);
+
+ GraphicStatePop(mGSStack, stackIndex);
+ break;
+ }
+ case EmfPlusRecordTypeBeginContainer:
+ {
+ float dx, dy, dw, dh;
+ ReadRectangle(rMS, dx, dy, dw, dh);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Dest RectData: " << dx << "," << dy << " " << dw << "x" << dh);
+
+ float sx, sy, sw, sh;
+ ReadRectangle(rMS, sx, sy, sw, sh);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Source RectData: " << sx << "," << sy << " " << sw << "x" << sh);
+
+ sal_uInt32 stackIndex;
+ rMS.ReadUInt32(stackIndex);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container stack index: " << stackIndex << ", PageUnit: " << flags);
+
+ if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld))
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by BeginContainer in EMF+ specification.");
+ break;
+ }
+ const float aPageScaleX = getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnHDPI);
+ const float aPageScaleY = getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnVDPI);
+ GraphicStatePush(mGSContainerStack, stackIndex);
+ const basegfx::B2DHomMatrix transform = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aPageScaleX * ( dw / sw ), aPageScaleY * ( dh / sh ),
+ aPageScaleX * ( dx - sx ), aPageScaleY * ( dy - sy) );
+ maWorldTransform *= transform;
+ mappingChanged();
+ break;
+ }
+ case EmfPlusRecordTypeBeginContainerNoParams:
+ {
+ sal_uInt32 stackIndex;
+ rMS.ReadUInt32(stackIndex);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container No Params stack index: " << stackIndex);
+
+ GraphicStatePush(mGSContainerStack, stackIndex);
+ break;
+ }
+ case EmfPlusRecordTypeEndContainer:
+ {
+ sal_uInt32 stackIndex;
+ rMS.ReadUInt32(stackIndex);
+ SAL_INFO("drawinglayer.emf", "EMF+\t End Container stack index: " << stackIndex);
+
+ GraphicStatePop(mGSContainerStack, stackIndex);
+ break;
+ }
+ case EmfPlusRecordTypeSetWorldTransform:
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t SetWorldTransform, Post multiply: " << bool(flags & 0x2000));
+ readXForm(rMS, maWorldTransform);
+ mappingChanged();
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t: " << maWorldTransform);
+ break;
+ }
+ case EmfPlusRecordTypeResetWorldTransform:
+ {
+ maWorldTransform.identity();
+ SAL_INFO("drawinglayer.emf", "EMF+\t World transform: " << maWorldTransform);
+ mappingChanged();
+ break;
+ }
+ case EmfPlusRecordTypeMultiplyWorldTransform:
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t MultiplyWorldTransform, post multiply: " << bool(flags & 0x2000));
+ basegfx::B2DHomMatrix transform;
+ readXForm(rMS, transform);
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Transform matrix: " << transform);
+
+ if (flags & 0x2000)
+ {
+ // post multiply
+ maWorldTransform *= transform;
+ }
+ else
+ {
+ // pre multiply
+ transform *= maWorldTransform;
+ maWorldTransform = transform;
+ }
+
+ mappingChanged();
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t World transform matrix: " << maWorldTransform);
+ break;
+ }
+ case EmfPlusRecordTypeTranslateWorldTransform:
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t TranslateWorldTransform, Post multiply: " << bool(flags & 0x2000));
+
+ basegfx::B2DHomMatrix transform;
+ float eDx, eDy;
+ rMS.ReadFloat(eDx).ReadFloat(eDy);
+ transform.set(0, 2, eDx);
+ transform.set(1, 2, eDy);
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Translate matrix: " << transform);
+
+ if (flags & 0x2000)
+ {
+ // post multiply
+ maWorldTransform *= transform;
+ }
+ else
+ {
+ // pre multiply
+ transform *= maWorldTransform;
+ maWorldTransform = transform;
+ }
+
+ mappingChanged();
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t World transform matrix: " << maWorldTransform);
+ break;
+ }
+ case EmfPlusRecordTypeScaleWorldTransform:
+ {
+ basegfx::B2DHomMatrix transform;
+ float eSx, eSy;
+ rMS.ReadFloat(eSx).ReadFloat(eSy);
+ transform.set(0, 0, eSx);
+ transform.set(1, 1, eSy);
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t ScaleWorldTransform Sx: " << eSx <<
+ " Sy: " << eSy << ", Post multiply:" << bool(flags & 0x2000));
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t World transform matrix: " << maWorldTransform);
+
+ if (flags & 0x2000)
+ {
+ // post multiply
+ maWorldTransform *= transform;
+ }
+ else
+ {
+ // pre multiply
+ transform *= maWorldTransform;
+ maWorldTransform = transform;
+ }
+
+ mappingChanged();
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t World transform matrix: " << maWorldTransform);
+ break;
+ }
+ case EmfPlusRecordTypeRotateWorldTransform:
+ {
+ // Angle of rotation in degrees
+ float eAngle;
+ rMS.ReadFloat(eAngle);
+
+ SAL_INFO("drawinglayer.emf", "EMF+\t RotateWorldTransform Angle: " << eAngle <<
+ ", post multiply: " << bool(flags & 0x2000));
+ // Skipping flags & 0x2000
+ // For rotation transformation there is no difference between post and pre multiply
+ maWorldTransform.rotate(basegfx::deg2rad(eAngle));
+ mappingChanged();
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t " << maWorldTransform);
+ break;
+ }
+ case EmfPlusRecordTypeResetClip:
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+ ResetClip");
+ // We don't need to read anything more, as Size needs to be set 0x0000000C
+ // and DataSize must be set to 0.
+
+ // Resets the current clipping region for the world space to infinity.
+ HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, mrPropertyHolders);
+ break;
+ }
+ case EmfPlusRecordTypeSetClipRect:
+ case EmfPlusRecordTypeSetClipPath:
+ case EmfPlusRecordTypeSetClipRegion:
+ {
+ int combineMode = (flags >> 8) & 0xf;
+ ::basegfx::B2DPolyPolygon polyPolygon;
+ if (type == EmfPlusRecordTypeSetClipRect)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t SetClipRect");
+
+ float dx, dy, dw, dh;
+ ReadRectangle(rMS, dx, dy, dw, dh);
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
+ ::basegfx::B2DPoint mappedPoint1(Map(dx, dy));
+ ::basegfx::B2DPoint mappedPoint2(Map(dx + dw, dy + dh));
+
+ polyPolygon
+ = ::basegfx::B2DPolyPolygon(::basegfx::utils::createPolygonFromRect(
+ ::basegfx::B2DRectangle(mappedPoint1.getX(), mappedPoint1.getY(),
+ mappedPoint2.getX(), mappedPoint2.getY())));
+ }
+ else if (type == EmfPlusRecordTypeSetClipPath)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\tSetClipPath " << (flags & 0xff));
+
+ EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get());
+ if (!path)
+ {
+ SAL_WARN("drawinglayer.emf",
+ "EMF+\t TODO Unable to find path in slot: " << (flags & 0xff));
+ break;
+ }
+ polyPolygon = path->GetPolygon(*this);
+ }
+ else if (type == EmfPlusRecordTypeSetClipRegion)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t Region in slot: " << (flags & 0xff));
+ EMFPRegion* region
+ = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get());
+ if (!region)
+ {
+ SAL_WARN(
+ "drawinglayer.emf",
+ "EMF+\t TODO Unable to find region in slot: " << (flags & 0xff));
+ break;
+ }
+ polyPolygon = region->regionPolyPolygon;
+ }
+ SAL_INFO("drawinglayer.emf", "EMF+\t Combine mode: " << combineMode);
+ ::basegfx::B2DPolyPolygon aClippedPolyPolygon;
+ if (mrPropertyHolders.Current().getClipPolyPolygonActive())
+ {
+ aClippedPolyPolygon
+ = combineClip(mrPropertyHolders.Current().getClipPolyPolygon(),
+ combineMode, polyPolygon);
+ }
+ else
+ {
+ //Combine with infinity
+ switch (combineMode)
+ {
+ case EmfPlusCombineModeReplace:
+ case EmfPlusCombineModeIntersect:
+ {
+ aClippedPolyPolygon = polyPolygon;
+ break;
+ }
+ case EmfPlusCombineModeUnion:
+ {
+ // Disable clipping as the clipping is infinity
+ aClippedPolyPolygon = ::basegfx::B2DPolyPolygon();
+ break;
+ }
+ case EmfPlusCombineModeXOR:
+ case EmfPlusCombineModeComplement:
+ {
+ //TODO It is not correct and it should be fixed
+ aClippedPolyPolygon = polyPolygon;
+ break;
+ }
+ case EmfPlusCombineModeExclude:
+ {
+ //TODO It is not correct and it should be fixed
+ aClippedPolyPolygon = ::basegfx::B2DPolyPolygon();
+ break;
+ }
+ }
+ }
+ HandleNewClipRegion(aClippedPolyPolygon, mrTargetHolders, mrPropertyHolders);
+ break;
+ }
+ case EmfPlusRecordTypeOffsetClip:
+ {
+ float dx, dy;
+ rMS.ReadFloat(dx).ReadFloat(dy);
+ SAL_INFO("drawinglayer.emf", "EMF+\tOffset x:" << dx << ", y:" << dy);
+
+ basegfx::B2DPolyPolygon aPolyPolygon(
+ mrPropertyHolders.Current().getClipPolyPolygon());
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t PolyPolygon before translate: " << aPolyPolygon);
+
+ basegfx::B2DPoint aOffset = Map(dx, dy);
+ basegfx::B2DHomMatrix transformMatrix;
+ transformMatrix.set(0, 2, aOffset.getX());
+ transformMatrix.set(1, 2, aOffset.getY());
+ aPolyPolygon.transform(transformMatrix);
+
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t PolyPolygon after translate: " << aPolyPolygon <<
+ ", mapped offset x" << aOffset.getX() << ", mapped offset y" << aOffset.getY());
+ HandleNewClipRegion(aPolyPolygon, mrTargetHolders, mrPropertyHolders);
+ break;
+ }
+ case EmfPlusRecordTypeDrawDriverString:
+ {
+ sal_uInt32 brushIndexOrColor;
+ sal_uInt32 optionFlags;
+ sal_uInt32 hasMatrix;
+ sal_uInt32 glyphsCount;
+ rMS.ReadUInt32(brushIndexOrColor).ReadUInt32(optionFlags).ReadUInt32(hasMatrix).ReadUInt32(glyphsCount);
+ SAL_INFO("drawinglayer.emf", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Option flags: 0x" << std::hex << optionFlags << std::dec);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Has matrix: " << hasMatrix);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Glyphs: " << glyphsCount);
+
+ if ((optionFlags & 1) && glyphsCount > 0)
+ {
+ std::unique_ptr<float[]> charsPosX(new float[glyphsCount]);
+ std::unique_ptr<float[]> charsPosY(new float[glyphsCount]);
+ OUString text = read_uInt16s_ToOUString(rMS, glyphsCount);
+ SAL_INFO("drawinglayer.emf", "EMF+\t DrawDriverString string: " << text);
+
+ for (sal_uInt32 i = 0; i<glyphsCount; i++)
+ {
+ rMS.ReadFloat(charsPosX[i]).ReadFloat(charsPosY[i]);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t glyphPosition[" << i << "]: " << charsPosX[i] << "," << charsPosY[i]);
+ }
+
+ basegfx::B2DHomMatrix transform;
+
+ if (hasMatrix)
+ {
+ readXForm(rMS, transform);
+ SAL_INFO("drawinglayer.emf", "EMF+\tmatrix: " << transform);
+ }
+
+ // get the font from the flags
+ EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get());
+ if (!font)
+ {
+ break;
+ }
+ // done reading
+
+ drawinglayer::attribute::FontAttribute fontAttribute(
+ font->family, // font family
+ "", // (no) font style
+ font->Bold() ? 8u : 1u, // weight: 8 = bold
+ font->family == "SYMBOL", // symbol
+ optionFlags & 0x2, // vertical
+ font->Italic(), // italic
+ false, // monospaced
+ false, // outline = false, no such thing in MS-EMFPLUS
+ false, // right-to-left
+ false); // BiDiStrong
+
+ const Color color = EMFPGetBrushColorOrARGBColor(flags, brushIndexOrColor);
+
+ // generate TextSimplePortionPrimitive2Ds or TextDecoratedPortionPrimitive2D
+ // for all portions of text with the same charsPosY values
+ sal_uInt32 pos = 0;
+ while (pos < glyphsCount)
+ {
+ //determine the current length
+ sal_uInt32 aLength = 1;
+ while (pos + aLength < glyphsCount && std::abs( charsPosY[pos + aLength] - charsPosY[pos] ) < std::numeric_limits< float >::epsilon())
+ aLength++;
+
+ // generate the DX-Array
+ std::vector<double> aDXArray;
+ for (size_t i = 0; i < aLength - 1; i++)
+ {
+ aDXArray.push_back(charsPosX[pos + i + 1] - charsPosX[pos]);
+ }
+ // last entry
+ aDXArray.push_back(0);
+
+ basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ ::basegfx::B2DSize(font->emSize, font->emSize),
+ ::basegfx::B2DPoint(charsPosX[pos], charsPosY[pos]));
+ if (hasMatrix)
+ transformMatrix *= transform;
+ if (color.GetAlpha() > 0)
+ {
+ rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText;
+ if (font->Underline() || font->Strikeout())
+ {
+ pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
+ transformMatrix,
+ text,
+ pos, // take character at current pos
+ aLength, // use determined length
+ std::move(aDXArray), // generated DXArray
+ fontAttribute,
+ Application::GetSettings().GetLanguageTag().getLocale(),
+ color.getBColor(),
+ COL_TRANSPARENT,
+ color.getBColor(),
+ color.getBColor(),
+ drawinglayer::primitive2d::TEXT_LINE_NONE,
+ font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
+ false,
+ font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE);
+ }
+ else
+ {
+ pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ transformMatrix,
+ text,
+ pos, // take character at current pos
+ aLength, // use determined length
+ std::move(aDXArray), // generated DXArray
+ fontAttribute,
+ Application::GetSettings().GetLanguageTag().getLocale(),
+ color.getBColor());
+ }
+ drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText);
+ if (color.IsTransparent())
+ {
+ aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText },
+ (255 - color.GetAlpha()) / 255.0);
+ }
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ maMapTransform,
+ drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } ));
+ }
+
+ // update pos
+ pos += aLength;
+ }
+ }
+ else
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\tTODO: fonts (non-unicode glyphs chars)");
+ }
+ break;
+ }
+ default:
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+ TODO unhandled record type: 0x" << std::hex << type << std::dec);
+ }
+ }
+ }
+
+ rMS.Seek(next);
+
+ if (size <= length)
+ {
+ length -= size;
+ }
+ else
+ {
+ SAL_WARN("drawinglayer.emf", "ImplRenderer::processEMFPlus: "
+ "size " << size << " > length " << length);
+ length = 0;
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */