diff options
Diffstat (limited to 'drawinglayer/source/tools/emfphelperdata.cxx')
-rw-r--r-- | drawinglayer/source/tools/emfphelperdata.cxx | 1403 |
1 files changed, 748 insertions, 655 deletions
diff --git a/drawinglayer/source/tools/emfphelperdata.cxx b/drawinglayer/source/tools/emfphelperdata.cxx index d55babc0e960..1d95f0a8f38a 100644 --- a/drawinglayer/source/tools/emfphelperdata.cxx +++ b/drawinglayer/source/tools/emfphelperdata.cxx @@ -29,11 +29,13 @@ #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> @@ -78,6 +80,9 @@ namespace emfplushelper case EmfPlusRecordTypeDrawRects: return "EmfPlusRecordTypeDrawRects"; case EmfPlusRecordTypeFillPolygon: return "EmfPlusRecordTypeFillPolygon"; case EmfPlusRecordTypeDrawLines: return "EmfPlusRecordTypeDrawLines"; + case EmfPlusRecordTypeFillClosedCurve: return "EmfPlusRecordTypeFillClosedCurve"; + case EmfPlusRecordTypeDrawClosedCurve: return "EmfPlusRecordTypeDrawClosedCurve"; + case EmfPlusRecordTypeDrawCurve: return "EmfPlusRecordTypeDrawCurve"; case EmfPlusRecordTypeFillEllipse: return "EmfPlusRecordTypeFillEllipse"; case EmfPlusRecordTypeDrawEllipse: return "EmfPlusRecordTypeDrawEllipse"; case EmfPlusRecordTypeFillPie: return "EmfPlusRecordTypeFillPie"; @@ -224,33 +229,33 @@ namespace emfplushelper { } - float EmfPlusHelperData::getUnitToPixelMultiplier(const UnitType aUnitType, const sal_uInt32 aDPI) + double EmfPlusHelperData::unitToPixel(double n, sal_uInt32 aUnitType, Direction d) { - switch (aUnitType) + switch (static_cast<UnitType>(aUnitType)) { case UnitTypePixel: - return 1.0f; + return n; case UnitTypePoint: - return aDPI / 72.0; + return o3tl::convert(n, o3tl::Length::pt, o3tl::Length::in) * DPI(d); case UnitTypeInch: - return aDPI; + return n * DPI(d); case UnitTypeMillimeter: - return aDPI / 25.4; + return o3tl::convert(n, o3tl::Length::mm, o3tl::Length::in) * DPI(d); case UnitTypeDocument: - return aDPI / 300.0; + return n * DPI(d) / 300.0; case UnitTypeWorld: case UnitTypeDisplay: - SAL_WARN("drawinglayer", "EMF+\t Converting to World/Display."); - return 1.0f; + SAL_WARN("drawinglayer.emf", "EMF+\t Converting to World/Display."); + return n; default: - SAL_WARN("drawinglayer", "EMF+\tTODO Unimplemented support of Unit Type: 0x" << std::hex << aUnitType); - return 1.0f; + SAL_WARN("drawinglayer.emf", "EMF+\tTODO Unimplemented support of Unit Type: 0x" << std::hex << aUnitType); + return n; } } @@ -258,9 +263,9 @@ namespace emfplushelper { sal_uInt16 objecttype = flags & 0x7f00; sal_uInt16 index = flags & 0xff; - SAL_INFO("drawinglayer", "EMF+ Object: " << emfObjectToName(objecttype) << " (0x" << objecttype << ")"); - SAL_INFO("drawinglayer", "EMF+\tObject slot: " << index); - SAL_INFO("drawinglayer", "EMF+\tFlags: " << (flags & 0xff00)); + 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) { @@ -276,21 +281,20 @@ namespace emfplushelper EMFPPen *pen = new EMFPPen(); maEMFPObjects[index].reset(pen); pen->Read(rObjectStream, *this); - pen->penWidth = pen->penWidth * getUnitToPixelMultiplier(static_cast<UnitType>(pen->penUnit), mnHDPI); + pen->penWidth = unitToPixel(pen->penWidth, pen->penUnit, Direction::horizontal); break; } case EmfPlusObjectTypePath: { - sal_uInt32 header, pathFlags; - sal_Int32 points; - - rObjectStream.ReadUInt32(header).ReadInt32(points).ReadUInt32(pathFlags); - SAL_INFO("drawinglayer", "EMF+\t\tHeader: 0x" << std::hex << header); - SAL_INFO("drawinglayer", "EMF+\t\tPoints: " << std::dec << points); - SAL_INFO("drawinglayer", "EMF+\t\tAdditional flags: 0x" << std::hex << pathFlags << std::dec); - EMFPPath *path = new EMFPPath(points); + 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, pathFlags); + path->Read(rObjectStream, aPathPointFlags); break; } case EmfPlusObjectTypeRegion: @@ -321,7 +325,7 @@ namespace emfplushelper font->fontFlags = 0; font->Read(rObjectStream); // tdf#113624 Convert unit to Pixels - font->emSize = font->emSize * getUnitToPixelMultiplier(static_cast<UnitType>(font->sizeUnit), mnHDPI); + font->emSize = unitToPixel(font->emSize, font->sizeUnit, Direction::horizontal); break; } @@ -341,12 +345,12 @@ namespace emfplushelper } case EmfPlusObjectTypeCustomLineCap: { - SAL_WARN("drawinglayer", "EMF+\t TODO Object type 'custom line cap' not yet implemented"); + SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object type 'custom line cap' not yet implemented"); break; } default: { - SAL_WARN("drawinglayer", "EMF+\t TODO Object unhandled flags: 0x" << std::hex << (flags & 0xff00) << std::dec); + SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object unhandled flags: 0x" << std::hex << (flags & 0xff00) << std::dec); } } } @@ -358,7 +362,7 @@ namespace emfplushelper // 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+\t\t TODO Relative coordinates bit detected. Implement parse EMFPlusPointR"); + SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Relative coordinates bit detected. Implement parse EMFPlusPointR"); } if (flags & 0x4000) @@ -425,7 +429,7 @@ namespace emfplushelper { if (mnPixX == 0 || mnPixY == 0) { - SAL_WARN("drawinglayer", "dimensions in pixels is 0"); + SAL_WARN("drawinglayer.emf", "dimensions in pixels is 0"); return; } // Call when mnMmX/mnMmY/mnPixX/mnPixY/mnFrameLeft/mnFrameTop/maWorldTransform/ changes. @@ -442,6 +446,10 @@ namespace emfplushelper 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 @@ -457,12 +465,14 @@ namespace emfplushelper color = Color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff, (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff); } - else // we use a pen + else // we use a brush { - const EMFPPen* pen = static_cast<EMFPPen*>(maEMFPObjects[brushIndexOrColor & 0xff].get()); - if (pen) + const EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get()); + if (brush) { - color = pen->GetColor(); + 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; @@ -475,7 +485,7 @@ namespace emfplushelper if ( iter != map.end() ) { map.erase( iter ); - SAL_INFO("drawinglayer", "EMF+\t\tStack index: " << index << " found and erased"); + SAL_INFO("drawinglayer.emf", "EMF+\t\tStack index: " << index << " found and erased"); } wmfemfhelper::PropertyHolder state = mrPropertyHolders.Current(); @@ -484,203 +494,175 @@ namespace emfplushelper map[ index ] = state; } - void EmfPlusHelperData::GraphicStatePop(GraphicStateMap& map, sal_Int32 index, wmfemfhelper::PropertyHolder& rState) + void EmfPlusHelperData::GraphicStatePop(GraphicStateMap& map, sal_Int32 index) { - GraphicStateMap::iterator iter = map.find( index ); + GraphicStateMap::iterator iter = map.find(index); - if ( iter != map.end() ) + if (iter != map.end()) { wmfemfhelper::PropertyHolder state = iter->second; maWorldTransform = state.getTransformation(); - rState.setClipPolyPolygon( state.getClipPolyPolygon() ); + 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+\t\tStack index: " << index << " found, maWorldTransform: " << maWorldTransform); + SAL_INFO("drawinglayer.emf", + "EMF+\t\tStack index: " << index + << " found, maWorldTransform: " << maWorldTransform); } } - void EmfPlusHelperData::EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon, sal_uInt32 penIndex) + drawinglayer::attribute::LineStartEndAttribute + EmfPlusHelperData::CreateLineEnd(const sal_Int32 aCap, const float aPenWidth) const { - const EMFPPen* pen = dynamic_cast<EMFPPen*>(maEMFPObjects[penIndex & 0xff].get()); - SAL_WARN_IF(!pen, "drawinglayer", "emf+ missing pen"); - - if (!(pen && polygon.count())) - return; - - // we need a line join attribute - basegfx::B2DLineJoin lineJoin = basegfx::B2DLineJoin::Round; - if (pen->penDataFlags & EmfPlusPenDataJoin) // additional line join information + const double pw = mdExtractedYScale * aPenWidth; + if (aCap == LineCapTypeSquare) { - lineJoin = static_cast<basegfx::B2DLineJoin>(EMFPPen::lcl_convertLineJoinType(pen->lineJoin)); + 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); } - - // we need a line cap attribute - css::drawing::LineCap lineCap = css::drawing::LineCap_BUTT; - if (pen->penDataFlags & EmfPlusPenDataStartCap) // additional line cap information + else if (aCap == LineCapTypeRound) { - lineCap = static_cast<css::drawing::LineCap>(EMFPPen::lcl_convertStrokeCap(pen->startCap)); - SAL_WARN_IF(pen->startCap != pen->endCap, "drawinglayer", "emf+ pen uses different start and end cap"); + 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); } - - const double transformedPenWidth = maMapTransform.get(0, 0) * pen->penWidth; - drawinglayer::attribute::LineAttribute lineAttribute(pen->GetColor().getBColor(), - transformedPenWidth, - lineJoin, - lineCap); - - drawinglayer::attribute::StrokeAttribute aStrokeAttribute; - if (pen->penDataFlags & EmfPlusPenDataLineStyle && pen->dashStyle != EmfPlusLineStyleCustom) // pen has a predefined line style + else if (aCap == LineCapTypeTriangle) { - // short writing - const double pw = maMapTransform.get(1, 1) * pen->penWidth; - // taken from the old cppcanvas implementation and multiplied with pen width - const std::vector<double> dash = { 3*pw, 3*pw }; - const std::vector<double> dot = { pw, 3*pw }; - const std::vector<double> dashdot = { 3*pw, 3*pw, pw, 3*pw }; - const std::vector<double> dashdotdot = { 3*pw, 3*pw, pw, 3*pw, pw, 3*pw }; - - switch (pen->dashStyle) - { - case EmfPlusLineStyleSolid: // do nothing special, use default stroke attribute - break; - case EmfPlusLineStyleDash: - aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(dash); - break; - case EmfPlusLineStyleDot: - aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(dot); - break; - case EmfPlusLineStyleDashDot: - aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(dashdot); - break; - case EmfPlusLineStyleDashDotDot: - aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(dashdotdot); - break; - } + 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 (pen->penDataFlags & EmfPlusPenDataDashedLine) // pen has a custom dash line + else if (aCap == LineCapTypeSquareAnchor) { - // StrokeAttribute needs a double vector while the pen provides a float vector - std::vector<double> aPattern(pen->dashPattern.size()); - for (size_t i=0; i<aPattern.size(); i++) - { - // convert from float to double and multiply with the adjusted pen width - aPattern[i] = maMapTransform.get(1, 1) * pen->penWidth * pen->dashPattern[i]; - } - aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(aPattern); + 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); } - - if (!pen->GetColor().IsTransparent()) + else if (aCap == LineCapTypeRoundAnchor) { - mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D>( - polygon, - lineAttribute, - aStrokeAttribute)); + 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 + else if (aCap == LineCapTypeDiamondAnchor) { - const drawinglayer::primitive2d::Primitive2DReference aPrimitive( - new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( - polygon, - lineAttribute, - aStrokeAttribute)); - - mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::UnifiedTransparencePrimitive2D>( - drawinglayer::primitive2d::Primitive2DContainer { aPrimitive }, - (255 - pen->GetColor().GetAlpha()) / 255.0)); + 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); } - - if ((pen->penDataFlags & EmfPlusPenDataCustomStartCap) && (pen->customStartCap->polygon.begin()->count() > 1)) + else if (aCap == LineCapTypeArrowAnchor) { - SAL_WARN("drawinglayer", "EMF+\tCustom Start Line Cap"); - ::basegfx::B2DPolyPolygon startCapPolygon(pen->customStartCap->polygon); - - // get the gradient of the first line in the polypolygon - double x1 = polygon.begin()->getB2DPoint(0).getX(); - double y1 = polygon.begin()->getB2DPoint(0).getY(); - double x2 = polygon.begin()->getB2DPoint(1).getX(); - double y2 = polygon.begin()->getB2DPoint(1).getY(); - - if ((x2 - x1) != 0) - { - double gradient = (y2 - y1) / (x2 - x1); - - // now we get the angle that we need to rotate the arrow by - double angle = (M_PI / 2) - atan(gradient); + 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(); + } - // rotate the arrow - startCapPolygon.transform(basegfx::utils::createRotateB2DHomMatrix(angle)); - } + 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"); - startCapPolygon.transform(maMapTransform); + if (!(pen && polygon.count())) + return; - basegfx::B2DHomMatrix tran(pen->penWidth, 0.0, polygon.begin()->getB2DPoint(0).getX(), - 0.0, pen->penWidth, polygon.begin()->getB2DPoint(0).getY()); - startCapPolygon.transform(tran); + 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); - if (pen->customStartCap->mbIsFilled) - { - mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::PolyPolygonColorPrimitive2D>( - startCapPolygon, - pen->GetColor().getBColor())); - } + 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 - { - mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D>( - startCapPolygon, - lineAttribute, - aStrokeAttribute)); - } + aStart = EmfPlusHelperData::CreateLineEnd(pen->startCap, pen->penWidth); } - if ((pen->penDataFlags & EmfPlusPenDataCustomEndCap) && (pen->customEndCap->polygon.begin()->count() > 1)) + drawinglayer::attribute::LineStartEndAttribute aEnd; + if (pen->penDataFlags & EmfPlusPenDataEndCap) { - SAL_WARN("drawinglayer", "EMF+\tCustom End Line Cap"); - - ::basegfx::B2DPolyPolygon endCapPolygon(pen->customEndCap->polygon); - - // get the gradient of the first line in the polypolygon - double x1 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getX(); - double y1 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getY(); - double x2 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 2).getX(); - double y2 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 2).getY(); + 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 ((x2 - x1) != 0) + if (pen->GetColor().IsTransparent()) + { + drawinglayer::primitive2d::Primitive2DContainer aContainer; + if (aStart.isDefault() && aEnd.isDefault()) + aContainer.append( + new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( + polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale))); + else { - double gradient = (y2 - y1) / (x2 - x1); - - // now we get the angle that we need to rotate the arrow by - double angle = (M_PI / 2) - atan(gradient); - - // rotate the arrow - endCapPolygon.transform(basegfx::utils::createRotateB2DHomMatrix(angle)); + aContainer.resize(polygon.count()); + for (sal_uInt32 i = 0; i < polygon.count(); i++) + aContainer[i] = + new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D( + polygon.getB2DPolygon(i), lineAttribute, + pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd); } - - endCapPolygon.transform(maMapTransform); - basegfx::B2DHomMatrix tran(pen->penWidth, 0.0, polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getX(), - 0.0, pen->penWidth, polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getY()); - endCapPolygon.transform(tran); - - if (pen->customEndCap->mbIsFilled) - { + 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( - std::make_unique<drawinglayer::primitive2d::PolyPolygonColorPrimitive2D>( - endCapPolygon, - pen->GetColor().getBColor())); - } + new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( + polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale))); else - { - mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D>( - endCapPolygon, - lineAttribute, - aStrokeAttribute)); - } + 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); @@ -695,7 +677,7 @@ namespace emfplushelper { // not transparent mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::PolyPolygonColorPrimitive2D>( + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( polygon, color.getBColor())); } @@ -707,7 +689,7 @@ namespace emfplushelper color.getBColor())); mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::UnifiedTransparencePrimitive2D>( + new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( drawinglayer::primitive2d::Primitive2DContainer { aPrimitive }, (255 - color.GetAlpha()) / 255.0)); } @@ -720,7 +702,7 @@ namespace emfplushelper if (isColor) // use Color { - SAL_INFO("drawinglayer", "EMF+\t\t Fill polygon, ARGB color: 0x" << std::hex << brushIndexOrColor << std::dec); + 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. @@ -733,8 +715,8 @@ namespace emfplushelper } else // use Brush { - EMFPBrush* brush = static_cast<EMFPBrush*>( maEMFPObjects[brushIndexOrColor & 0xff].get() ); - SAL_INFO("drawinglayer", "EMF+\t\t Fill polygon, brush slot: " << brushIndexOrColor << " (brush type: " << (brush ? brush->GetType() : -1) << ")"); + 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 ) @@ -787,20 +769,20 @@ namespace emfplushelper // temporal solution: create a solid colored polygon // TODO create a 'real' hatching primitive mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::PolyPolygonColorPrimitive2D>( + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( polygon, fillColor.getBColor())); } else if (brush->type == BrushTypeTextureFill) { - SAL_WARN("drawinglayer", "EMF+\tTODO: implement BrushTypeTextureFill brush"); + 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+\t TODO Implement displaying BrushTypePathGradient with Boundary: "); + SAL_WARN("drawinglayer.emf", "EMF+\t TODO Implement displaying BrushTypePathGradient with Boundary: "); } ::basegfx::B2DHomMatrix aTextureTransformation; @@ -821,63 +803,36 @@ namespace emfplushelper if (brush->blendPositions) { - SAL_INFO("drawinglayer", "EMF+\t\tUse blend"); + SAL_INFO("drawinglayer.emf", "EMF+\t\tUse blend"); // store the blendpoints in the vector - for (int i = 0; i < brush->blendPoints; i++) + for (sal_uInt32 i = 0; i < brush->blendPoints; i++) { - double aBlendPoint; + const double aBlendPoint = brush->blendPositions[i]; basegfx::BColor aColor; - if (brush->type == BrushTypeLinearGradient) - { - aBlendPoint = brush->blendPositions [i]; - } - else - { - // seems like SvgRadialGradientPrimitive2D needs doubled, inverted radius - aBlendPoint = 2. * ( 1. - brush->blendPositions [i] ); - } - 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() ); + 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+\t\tUse color blend"); + SAL_INFO("drawinglayer.emf", "EMF+\t\tUse color blend"); // store the colorBlends in the vector - for (int i = 0; i < brush->colorblendPoints; i++) + for (sal_uInt32 i = 0; i < brush->colorblendPoints; i++) { - double aBlendPoint; - basegfx::BColor aColor; - if (brush->type == BrushTypeLinearGradient) - { - aBlendPoint = brush->colorblendPositions [i]; - } - else - { - // seems like SvgRadialGradientPrimitive2D needs doubled, inverted radius - aBlendPoint = 2. * ( 1. - brush->colorblendPositions [i] ); - } - aColor = brush->colorblendColors[i].getBColor(); - aVector.emplace_back(aBlendPoint, aColor, brush->colorblendColors[i].GetAlpha() / 255.0 ); + 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 { - if (brush->type == BrushTypeLinearGradient) - { - aVector.emplace_back(0.0, aStartColor, brush->solidColor.GetAlpha() / 255.0); - aVector.emplace_back(1.0, aEndColor, brush->secondColor.GetAlpha() / 255.0); - } - else // again, here reverse - { - aVector.emplace_back(0.0, aEndColor, brush->secondColor.GetAlpha() / 255.0); - aVector.emplace_back(1.0, aStartColor, brush->solidColor.GetAlpha() / 255.0); - } + 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 @@ -918,30 +873,30 @@ namespace emfplushelper // create the same one used for SVG mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::SvgLinearGradientPrimitive2D>( + new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D( aTextureTransformation, polygon, - aVector, + 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( - std::make_unique<drawinglayer::primitive2d::SvgRadialGradientPrimitive2D>( + new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D( aTextureTransformation, polygon, - aVector, + std::move(aVector), aCenterPoint, - 0.5, // relative radius - true, // use UnitCoordinates to stretch the gradient - drawinglayer::primitive2d::SpreadMethod::Repeat, + 0.7, // relative radius little bigger to cover all elements + true, // use UnitCoordinates to stretch the gradient + drawinglayer::primitive2d::SpreadMethod::Pad, nullptr)); } } @@ -952,11 +907,7 @@ namespace emfplushelper SvMemoryStream& rMS, wmfemfhelper::TargetHolders& rTargetHolders, wmfemfhelper::PropertyHolders& rPropertyHolders) - : maBaseTransform(), - maWorldTransform(), - maMapTransform(), - maEMFPObjects(), - mfPageScale(0.0), + : mfPageScale(0.0), mnOriginX(0), mnOriginY(0), mnHDPI(0), @@ -973,17 +924,18 @@ namespace emfplushelper mnMmY(0), mbMultipart(false), mMFlags(0), - mMStream(), + mdExtractedXScale(1.0), + mdExtractedYScale(1.0), mrTargetHolders(rTargetHolders), mrPropertyHolders(rPropertyHolders), bIsGetDCProcessing(false) { rMS.ReadInt32(mnFrameLeft).ReadInt32(mnFrameTop).ReadInt32(mnFrameRight).ReadInt32(mnFrameBottom); - SAL_INFO("drawinglayer", "EMF+ picture frame: " << mnFrameLeft << "," << mnFrameTop << " - " << mnFrameRight << "," << 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+ ref device pixel size: " << mnPixX << "x" << mnPixY << " mm size: " << mnMmX << "x" << 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+ base transform: " << maBaseTransform); + SAL_INFO("drawinglayer.emf", "EMF+ base transform: " << maBaseTransform); mappingChanged(); } @@ -1003,14 +955,8 @@ namespace emfplushelper } case EmfPlusCombineModeIntersect: { - if (leftPolygon.count()) - { - aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon( - leftPolygon, - rightPolygon, - true, - false); - } + aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon( + leftPolygon, rightPolygon, true, false); break; } case EmfPlusCombineModeUnion: @@ -1046,7 +992,7 @@ namespace emfplushelper sal_uInt64 length = rMS.GetSize(); if (length < 12) - SAL_WARN("drawinglayer", "length is less than required header size"); + 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) @@ -1061,30 +1007,40 @@ namespace emfplushelper if (size < 12) { - SAL_WARN("drawinglayer", "Size field is less than 12 bytes"); + SAL_WARN("drawinglayer.emf", "Size field is less than 12 bytes"); break; } else if (size > length) { - SAL_WARN("drawinglayer", "Size field is greater than bytes left"); + SAL_WARN("drawinglayer.emf", "Size field is greater than bytes left"); break; } if (dataSize > (size - 12)) { - SAL_WARN("drawinglayer", "DataSize field is greater than Size-12"); + SAL_WARN("drawinglayer.emf", "DataSize field is greater than Size-12"); break; } - SAL_INFO("drawinglayer", "EMF+ " << emfTypeToName(type) << " (0x" << std::hex << type << ")" << std::dec); - SAL_INFO("drawinglayer", "EMF+\t record size: " << size); - SAL_INFO("drawinglayer", "EMF+\t flags: 0x" << std::hex << flags << std::dec); - SAL_INFO("drawinglayer", "EMF+\t data size: " << dataSize); + 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) { - SAL_INFO("drawinglayer", "EMF+\t reset the current clipping region for the world space to infinity."); - wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, mrPropertyHolders); + 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))) @@ -1100,13 +1056,13 @@ namespace emfplushelper // 1st 4 bytes are TotalObjectSize mMStream.WriteBytes(static_cast<const char *>(rMS.GetData()) + rMS.Tell() + 4, dataSize - 4); - SAL_INFO("drawinglayer", "EMF+ read next object part size: " << size << " type: " << type << " flags: " << flags << " data size: " << dataSize); + 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+ multipart record flags: " << mMFlags); + SAL_INFO("drawinglayer.emf", "EMF+ multipart record flags: " << mMFlags); mMStream.Seek(0); processObjectRecord(mMStream, mMFlags, 0, true); } @@ -1120,14 +1076,15 @@ namespace emfplushelper { case EmfPlusRecordTypeHeader: { - sal_uInt32 header, version; - - rMS.ReadUInt32(header).ReadUInt32(version).ReadUInt32(mnHDPI).ReadUInt32(mnVDPI); - SAL_INFO("drawinglayer", "EMF+\tHeader: 0x" << std::hex << header); - SAL_INFO("drawinglayer", "EMF+\tVersion: " << std::dec << version); - SAL_INFO("drawinglayer", "EMF+\tHorizontal DPI: " << mnHDPI); - SAL_INFO("drawinglayer", "EMF+\tVertical DPI: " << mnVDPI); - SAL_INFO("drawinglayer", "EMF+\tDual: " << ((flags & 1) ? "true" : "false")); + sal_uInt32 version, emfPlusFlags; + SAL_INFO("drawinglayer.emf", "EMF+\tDual: " << ((flags & 1) ? "true" : "false")); + + rMS.ReadUInt32(version).ReadUInt32(emfPlusFlags).ReadUInt32(mnHDPI).ReadUInt32(mnVDPI); + SAL_INFO("drawinglayer.emf", "EMF+\tVersion: 0x" << std::hex << version); + SAL_INFO("drawinglayer.emf", "EMF+\tEmf+ Flags: 0x" << emfPlusFlags << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\tMetafile was recorded with a reference device context for " << ((emfPlusFlags & 1) ? "video display" : "printer")); + SAL_INFO("drawinglayer.emf", "EMF+\tHorizontal DPI: " << mnHDPI); + SAL_INFO("drawinglayer.emf", "EMF+\tVertical DPI: " << mnVDPI); break; } case EmfPlusRecordTypeEndOfFile: @@ -1140,7 +1097,7 @@ namespace emfplushelper unsigned char data; OUString hexdata; - SAL_INFO("drawinglayer", "EMF+\tDatasize: 0x" << std::hex << dataSize << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\tDatasize: 0x" << std::hex << dataSize << std::dec); for (sal_uInt32 i=0; i<dataSize; i++) { @@ -1156,14 +1113,15 @@ namespace emfplushelper hexdata += "0x" + padding + OUString::number(data, 16) + " "; } - SAL_INFO("drawinglayer", "EMF+\t" << hexdata); + SAL_INFO("drawinglayer.emf", "EMF+\t" << hexdata); #endif break; } case EmfPlusRecordTypeGetDC: { bIsGetDCProcessing = true; - SAL_INFO("drawinglayer", "EMF+\tAlready used in svtools wmf/emf filter parser"); + aGetDCState = mrPropertyHolders.Current(); + SAL_INFO("drawinglayer.emf", "EMF+\tAlready used in svtools wmf/emf filter parser"); break; } case EmfPlusRecordTypeObject: @@ -1183,21 +1141,21 @@ namespace emfplushelper if (type == EmfPlusRecordTypeFillPie) { rMS.ReadUInt32(brushIndexOrColor); - SAL_INFO("drawinglayer", "EMF+\t FillPie colorOrIndex: " << brushIndexOrColor); + SAL_INFO("drawinglayer.emf", "EMF+\t FillPie colorOrIndex: " << brushIndexOrColor); } else if (type == EmfPlusRecordTypeDrawPie) { - SAL_INFO("drawinglayer", "EMF+\t DrawPie"); + SAL_INFO("drawinglayer.emf", "EMF+\t DrawPie"); } else { - SAL_INFO("drawinglayer", "EMF+\t DrawArc"); + 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+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh); + SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh); startAngle = basegfx::deg2rad(startAngle); sweepAngle = basegfx::deg2rad(sweepAngle); float endAngle = startAngle + sweepAngle; @@ -1218,7 +1176,7 @@ namespace emfplushelper std::swap(endAngle, startAngle); } - SAL_INFO("drawinglayer", "EMF+\t Adjusted angles: start " << + 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); @@ -1244,13 +1202,13 @@ namespace emfplushelper sal_uInt32 index = flags & 0xff; sal_uInt32 brushIndexOrColor; rMS.ReadUInt32(brushIndexOrColor); - SAL_INFO("drawinglayer", "EMF+ FillPath slot: " << index); + 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+\tEmfPlusRecordTypeFillPath missing path"); + SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillPath missing path"); } break; case EmfPlusRecordTypeFillRegion: @@ -1258,9 +1216,13 @@ namespace emfplushelper sal_uInt32 index = flags & 0xff; sal_uInt32 brushIndexOrColor; rMS.ReadUInt32(brushIndexOrColor); - SAL_INFO("drawinglayer", "EMF+\t FillRegion slot: " << index); + SAL_INFO("drawinglayer.emf", "EMF+\t FillRegion slot: " << index); - EMFPPlusFillPolygon(static_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get())->regionPolyPolygon, flags & 0x8000, brushIndexOrColor); + 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: @@ -1276,10 +1238,10 @@ namespace emfplushelper rMS.ReadUInt32(brushIndexOrColor); } - SAL_INFO("drawinglayer", "EMF+\t " << (type == EmfPlusRecordTypeFillEllipse ? "Fill" : "Draw") << "Ellipse slot: " << (flags & 0xff)); + 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+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh); + 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)); @@ -1295,26 +1257,26 @@ namespace emfplushelper { // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used sal_uInt32 brushIndexOrColor = 999; - sal_Int32 rectangles; + ::basegfx::B2DPolyPolygon polyPolygon; + sal_uInt32 rectangles; + float x, y, width, height; const bool isColor = (flags & 0x8000); ::basegfx::B2DPolygon polygon; if (EmfPlusRecordTypeFillRects == type) { - SAL_INFO("drawinglayer", "EMF+\t FillRects"); + SAL_INFO("drawinglayer.emf", "EMF+\t FillRects"); rMS.ReadUInt32(brushIndexOrColor); - SAL_INFO("drawinglayer", "EMF+\t" << (isColor ? "color" : "brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\t" << (isColor ? "color" : "brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec); } else { - SAL_INFO("drawinglayer", "EMF+\t DrawRects"); + SAL_INFO("drawinglayer.emf", "EMF+\t DrawRects"); } - rMS.ReadInt32(rectangles); - - for (int i = 0; i < rectangles; i++) + rMS.ReadUInt32(rectangles); + for (sal_uInt32 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)); @@ -1323,39 +1285,35 @@ namespace emfplushelper polygon.append(Map(x, y + height)); polygon.setClosed(true); - SAL_INFO("drawinglayer", "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); + SAL_INFO("drawinglayer.emf", "EMF+\t\t rectangle: " << x << ", "<< y << " " << width << "x" << height); + polyPolygon.append(polygon); } + if (type == EmfPlusRecordTypeFillRects) + EMFPPlusFillPolygon(polyPolygon, isColor, brushIndexOrColor); + else + EMFPPlusDrawPolygon(polyPolygon, flags & 0xff); break; } case EmfPlusRecordTypeFillPolygon: { - const sal_uInt8 index = flags & 0xff; - sal_uInt32 brushIndexOrColor; - sal_Int32 points; + sal_uInt32 brushIndexOrColor, points; rMS.ReadUInt32(brushIndexOrColor); - rMS.ReadInt32(points); - SAL_INFO("drawinglayer", "EMF+\t FillPolygon in slot: " << index << " points: " << points); - SAL_INFO("drawinglayer", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << " : 0x" << std::hex << brushIndexOrColor << std::dec); + 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+\t DrawLines in slot: " << (flags & 0xff) << " points: " << points); + SAL_INFO("drawinglayer.emf", "EMF+\t Points: " << points); EMFPPath path(points, true); path.Read(rMS, flags); @@ -1369,13 +1327,13 @@ namespace emfplushelper { sal_uInt32 penIndex; rMS.ReadUInt32(penIndex); - SAL_INFO("drawinglayer", "EMF+\t Pen: " << 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", "\t\tEmfPlusRecordTypeDrawPath missing path"); + SAL_WARN("drawinglayer.emf", "\t\tEmfPlusRecordTypeDrawPath missing path"); break; } @@ -1383,46 +1341,98 @@ namespace emfplushelper { 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+\t DrawBeziers slot: " << (flags & 0xff)); - SAL_INFO("drawinglayer", "EMF+\t Number of points: " << aCount); - SAL_WARN_IF((aCount - 1) % 3 != 0, "drawinglayer", "EMF+\t Bezier Draw not support number of points other than 4, 7, 10, 13, 16..."); + 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+\t Bezier Draw does not support less than 4 points. Number of points: " << aCount); + 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); - + aPolygon.append(Map(x1, y1)); + 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+\t Bezier points: " << x1 << "," << y1 << " " << x2 << "," << y2 << " " << x3 << "," << y3 << " " << x4 << "," << y4); - - aStartPoint = Map(x1, y1); - aControlPointA = Map(x2, y2); - aControlPointB = Map(x3, y3); - aEndPoint = Map(x4, y4); - - ::basegfx::B2DCubicBezier cubicBezier(aStartPoint, aControlPointA, aControlPointB, aEndPoint); - cubicBezier.adaptiveSubdivideByDistance(aPolygon, 10.0); + SAL_INFO("drawinglayer.emf", + "EMF+\t Bezier points: " << x2 << "," << y2 << " " << x3 << "," + << y3 << " " << x4 << "," << y4); + aPolygon.appendBezierSegment(Map(x2, y2), Map(x3, y3), Map(x4, y4)); + } + EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff); + break; + } + case EmfPlusRecordTypeDrawCurve: + { + sal_uInt32 aOffset, aNumSegments, points; + float aTension; + rMS.ReadFloat(aTension); + rMS.ReadUInt32(aOffset); + rMS.ReadUInt32(aNumSegments); + rMS.ReadUInt32(points); + SAL_WARN("drawinglayer.emf", + "EMF+\t Tension: " << aTension << " Offset: " << aOffset + << " NumSegments: " << aNumSegments + << " Points: " << points); - EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff); + EMFPPath path(points, true); + path.Read(rMS, flags); - // The ending coordinate of one Bezier curve is the starting coordinate of the next. - x1 = x4; - y1 = y4; + if (points >= 2) + EMFPPlusDrawPolygon( + path.GetCardinalSpline(*this, aTension, aOffset, aNumSegments), + flags & 0xff); + else + SAL_WARN("drawinglayer.emf", "Not enough number of points"); + 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_INFO("drawinglayer.emf", + "EMF+\t " << (flags & 0x8000 ? "Color" : "Brush index") << " : 0x" + << std::hex << brushIndexOrColor << std::dec); + if (points < 3) + { + SAL_WARN("drawinglayer.emf", "Not enough number of points"); + break; } + EMFPPath path(points, true); + path.Read(rMS, flags); + if (type == EmfPlusRecordTypeFillClosedCurve) + EMFPPlusFillPolygon(path.GetClosedCardinalSpline(*this, aTension), + flags & 0x8000, brushIndexOrColor); + else + EMFPPlusDrawPolygon(path.GetClosedCardinalSpline(*this, aTension), + flags & 0xff); break; } case EmfPlusRecordTypeDrawImage: @@ -1431,20 +1441,30 @@ namespace emfplushelper sal_uInt32 imageAttributesId; sal_Int32 sourceUnit; rMS.ReadUInt32(imageAttributesId).ReadInt32(sourceUnit); - SAL_INFO("drawinglayer", "EMF+\t " << (type == EmfPlusRecordTypeDrawImagePoints ? "DrawImagePoints" : "DrawImage") << " image attributes Id: " << imageAttributesId << " source unit: " << sourceUnit); - SAL_INFO("drawinglayer", "EMF+\t TODO: use image attributes"); - - // For DrawImage and DrawImagePoints, source unit of measurement type must be 1 pixel - if (sourceUnit == UnitTypePixel && maEMFPObjects[flags & 0xff]) + 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) { - EMFPImage& image = *static_cast<EMFPImage *>(maEMFPObjects[flags & 0xff].get()); float sx, sy, sw, sh; ReadRectangle(rMS, sx, sy, sw, sh); - ::tools::Rectangle aSource(Point(sx, sy), Size(sw, sh)); - SAL_INFO("drawinglayer", "EMF+\t " << (type == EmfPlusRecordTypeDrawImagePoints ? "DrawImagePoints" : "DrawImage") << " source rectangle: " << sx << "," << sy << " " << sw << "x" << sh); - ::basegfx::B2DPoint aDstPoint; - ::basegfx::B2DSize aDstSize; + ::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) @@ -1453,251 +1473,290 @@ namespace emfplushelper rMS.ReadUInt32(aCount); // Number of points used by DrawImagePoints. Exactly 3 points must be specified. - if(aCount == 3) - { - 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+\t destination points: P1:" << x1 << "," << y1 << " P2:" << x2 << "," << y2 << " P3:" << x3 << "," << y3); - - aDstPoint = ::basegfx::B2DPoint(x1, y1); - aDstSize = ::basegfx::B2DSize(x2 - x1, y3 - y1); - fShearX = x3 - x1; - fShearY = y2 - y1; - } - else + if (aCount != 3) { - SAL_WARN("drawinglayer", "EMF+\t DrawImagePoints Wrong EMF+ file. Expected 3 points, received: "<< aCount); + 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) - { - float dx, dy, dw, dh; ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000)); - SAL_INFO("drawinglayer", "EMF+\t destination rectangle: " << dx << "," << dy << " " << dw << "x" << dh); - aDstPoint = ::basegfx::B2DPoint(dx, dy); - aDstSize = ::basegfx::B2DSize(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()); + 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 (image.type == ImageDataTypeBitmap) + 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"); + const ::basegfx::B2DPoint aDstPoint(dx, dy); + const ::basegfx::B2DSize aDstSize(dw, dh); + + const basegfx::B2DHomMatrix aTransformMatrix + = maMapTransform + * basegfx::B2DHomMatrix( + /* Row 0, Column 0 */ aDstSize.getWidth(), + /* Row 0, Column 1 */ fShearX, + /* Row 0, Column 2 */ aDstPoint.getX(), + /* Row 1, Column 0 */ fShearY, + /* Row 1, Column 1 */ aDstSize.getHeight(), + /* Row 1, Column 2 */ aDstPoint.getY()); + + if (image->type == ImageDataTypeBitmap) { - BitmapEx aBmp(image.graphic.GetBitmapEx()); + BitmapEx aBmp(image->graphic.GetBitmapEx()); aBmp.Crop(aSource); - Size aSize(aBmp.GetSizePixel()); - SAL_INFO("drawinglayer", "EMF+\t Bitmap size: " << aSize.Width() << "x" << aSize.Height()); + aSize = aBmp.GetSizePixel(); if (aSize.Width() > 0 && aSize.Height() > 0) { mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::BitmapPrimitive2D>( - VCLUnoHelper::CreateVCLXBitmap(aBmp), - aTransformMatrix)); + new drawinglayer::primitive2d::BitmapPrimitive2D( + aBmp, aTransformMatrix)); } else - { - SAL_WARN("drawinglayer", "EMF+\t warning: empty bitmap"); - break; - } + SAL_WARN("drawinglayer.emf", "EMF+\t warning: empty bitmap"); } - else if (image.type == ImageDataTypeMetafile) + else if (image->type == ImageDataTypeMetafile) { - GDIMetaFile aGDI(image.graphic.GetGDIMetaFile()); + GDIMetaFile aGDI(image->graphic.GetGDIMetaFile()); aGDI.Clip(aSource); mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::MetafilePrimitive2D>(aTransformMatrix, aGDI)); + new drawinglayer::primitive2d::MetafilePrimitive2D(aTransformMatrix, + aGDI)); } } else { - SAL_WARN("drawinglayer", "EMF+\tDrawImage(Points) Wrong EMF+ file. Only Unit Type Pixel is support by EMF+ specification for DrawImage(Points)"); + 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; - sal_uInt32 formatId; - sal_uInt32 stringLength; + sal_uInt32 brushId, formatId, stringLength; rMS.ReadUInt32(brushId).ReadUInt32(formatId).ReadUInt32(stringLength); - SAL_INFO("drawinglayer", "EMF+\t FontId: " << OUString::number(flags & 0xFF)); - SAL_INFO("drawinglayer", "EMF+\t BrushId: " << BrushIDToString(flags, brushId)); - SAL_INFO("drawinglayer", "EMF+\t FormatId: " << formatId); - SAL_INFO("drawinglayer", "EMF+\t Length: " << stringLength); - - if (flags & 0x8000) + 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) { - // read the layout rectangle - float lx, ly, lw, lh; - rMS.ReadFloat(lx).ReadFloat(ly).ReadFloat(lw).ReadFloat(lh); - - SAL_INFO("drawinglayer", "EMF+\t DrawString layoutRect: " << lx << "," << ly << " - " << lw << "x" << lh); - // parse the string - const OUString text = read_uInt16s_ToOUString(rMS, stringLength); - SAL_INFO("drawinglayer", "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 = static_cast< EMFPFont* >( maEMFPObjects[flags & 0xff].get() ); - if (!font) - { - break; - } - mrPropertyHolders.Current().setFont(vcl::Font(font->family, Size(font->emSize, font->emSize))); + 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; - 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; - if (stringFormat) - { - SAL_WARN_IF(stringFormat->DirectionRightToLeft(), "drawinglayer", "EMF+\t DrawString Alignment TODO For a right-to-left layout rectangle, the origin should be at the upper right."); - if (stringFormat->stringAlignment == StringAlignmentNear) + 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) + 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 * font->emSize - 0.3 * font->emSize * stringLength; - } else if (stringFormat->stringAlignment == StringAlignmentFar) + 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 - 0.6 * font->emSize * stringLength; - } + 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; - LanguageTag aLanguageTag(static_cast< LanguageType >(stringFormat->language)); - locale = aLanguageTag.getLocale(); - } - 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(); + } - // use system default - locale = Application::GetSettings().GetLanguageTag().getLocale(); - } + const basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix( + ::basegfx::B2DVector(font->emSize, font->emSize), + ::basegfx::B2DPoint(lx + stringAlignmentHorizontalOffset, + ly + stringAlignmentVerticalOffset)); - const basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix( - ::basegfx::B2DSize(font->emSize, font->emSize), - ::basegfx::B2DPoint(lx + stringAlignmentHorizontalOffset, ly + font->emSize)); + Color uncorrectedColor = EMFPGetBrushColorOrARGBColor(flags, brushId); + Color color; - 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; + } - if (mbSetTextContrast) + 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()) { - 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); + 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 { - color = uncorrectedColor; + 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()); } - - mrPropertyHolders.Current().setTextColor(color.getBColor()); - mrPropertyHolders.Current().setTextColorActive(true); - - if (color.GetAlpha() > 0) + drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText); + if (color.IsTransparent()) { - std::vector<double> emptyVector; - drawinglayer::primitive2d::BasePrimitive2D* pBaseText = nullptr; - if (font->Underline() || font->Strikeout()) - { - pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D( - transformMatrix, - text, - 0, // text always starts at 0 - stringLength, - emptyVector, // EMF-PLUS has no DX-array - fontAttribute, - locale, - 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, - 0, // text always starts at 0 - stringLength, - 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( - std::make_unique<drawinglayer::primitive2d::TransformPrimitive2D>( - maMapTransform, - drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } )); + aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( + drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText }, + (255 - color.GetAlpha()) / 255.0); } - } - else - { - SAL_WARN("drawinglayer", "EMF+\t DrawString TODO - drawing with brush not yet supported"); + + mrTargetHolders.Current().append( + new drawinglayer::primitive2d::TransformPrimitive2D( + maMapTransform, + drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } )); } break; } case EmfPlusRecordTypeSetPageTransform: { rMS.ReadFloat(mfPageScale); - SAL_INFO("drawinglayer", "EMF+\t Scale: " << mfPageScale << " unit: " << UnitTypeToString(flags)); + SAL_INFO("drawinglayer.emf", "EMF+\t Scale: " << mfPageScale << " unit: " << UnitTypeToString(flags)); if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld)) { - SAL_WARN("drawinglayer", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by SetPageTransform in EMF+ specification."); + 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); + mnMmX = std::round(unitToPixel(static_cast<double>(mnMmX) * mfPageScale, flags, Direction::horizontal)); + mnMmY = std::round(unitToPixel(static_cast<double>(mnMmY) * mfPageScale, flags, Direction::vertical)); mappingChanged(); } break; @@ -1705,7 +1764,7 @@ namespace emfplushelper case EmfPlusRecordTypeSetRenderingOrigin: { rMS.ReadInt32(mnOriginX).ReadInt32(mnOriginY); - SAL_INFO("drawinglayer", "EMF+\t SetRenderingOrigin, [x,y]: " << mnOriginX << "," << mnOriginY); + SAL_INFO("drawinglayer.emf", "EMF+\t SetRenderingOrigin, [x,y]: " << mnOriginX << "," << mnOriginY); break; } case EmfPlusRecordTypeSetTextContrast: @@ -1716,51 +1775,51 @@ namespace emfplushelper mbSetTextContrast = true; mnTextContrast = flags & 0xFFF; SAL_WARN_IF(mnTextContrast > UPPERGAMMA || mnTextContrast < LOWERGAMMA, - "drawinglayer", "EMF+\t Gamma value is not with bounds 1000 to 2200, value is " << mnTextContrast); + "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+\t Text contrast: " << (mnTextContrast / 1000) << " gamma"); + SAL_INFO("drawinglayer.emf", "EMF+\t Text contrast: " << (mnTextContrast / 1000) << " gamma"); break; } case EmfPlusRecordTypeSetTextRenderingHint: { sal_uInt8 nTextRenderingHint = (flags & 0xFF) >> 1; - SAL_INFO("drawinglayer", "EMF+\t Text rendering hint: " << TextRenderingHintToString(nTextRenderingHint)); - SAL_WARN("drawinglayer", "EMF+\t TODO SetTextRenderingHint"); + 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+\t Antialiasing: " << (bUseAntiAlias ? "enabled" : "disabled")); - SAL_INFO("drawinglayer", "EMF+\t Smoothing mode: " << SmoothingModeToString(nSmoothingMode)); - SAL_WARN("drawinglayer", "EMF+\t TODO SetAntiAliasMode"); + 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+\t Interpolation mode: " << InterpolationModeToString(nInterpolationMode)); - SAL_WARN("drawinglayer", "EMF+\t TODO InterpolationMode"); + 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+\t Pixel offset mode: " << PixelOffsetModeToString(flags)); - SAL_WARN("drawinglayer", "EMF+\t TODO SetPixelOffsetMode"); + 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+\t TODO SetCompositingQuality"); + SAL_INFO("drawinglayer.emf", "EMF+\t TODO SetCompositingQuality"); break; } case EmfPlusRecordTypeSave: { sal_uInt32 stackIndex; rMS.ReadUInt32(stackIndex); - SAL_INFO("drawinglayer", "EMF+\t Save stack index: " << stackIndex); + SAL_INFO("drawinglayer.emf", "EMF+\t Save stack index: " << stackIndex); GraphicStatePush(mGSStack, stackIndex); @@ -1770,36 +1829,36 @@ namespace emfplushelper { sal_uInt32 stackIndex; rMS.ReadUInt32(stackIndex); - SAL_INFO("drawinglayer", "EMF+\t Restore stack index: " << stackIndex); + SAL_INFO("drawinglayer.emf", "EMF+\t Restore stack index: " << stackIndex); - GraphicStatePop(mGSStack, stackIndex, mrPropertyHolders.Current()); + GraphicStatePop(mGSStack, stackIndex); break; } case EmfPlusRecordTypeBeginContainer: { float dx, dy, dw, dh; ReadRectangle(rMS, dx, dy, dw, dh); - SAL_INFO("drawinglayer", "EMF+\t Dest RectData: " << dx << "," << dy << " " << dw << "x" << 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+\t Source RectData: " << sx << "," << sy << " " << sw << "x" << sh); + SAL_INFO("drawinglayer.emf", "EMF+\t Source RectData: " << sx << "," << sy << " " << sw << "x" << sh); sal_uInt32 stackIndex; rMS.ReadUInt32(stackIndex); - SAL_INFO("drawinglayer", "EMF+\t Begin Container stack index: " << stackIndex << ", PageUnit: " << flags); + SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container stack index: " << stackIndex << ", PageUnit: " << flags); if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld)) { - SAL_WARN("drawinglayer", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by BeginContainer in EMF+ specification."); + 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) ); + unitToPixel(static_cast<double>(dw) / sw, flags, Direction::horizontal), + unitToPixel(static_cast<double>(dh) / sh, flags, Direction::vertical), + unitToPixel(static_cast<double>(dx) - sx, flags, Direction::horizontal), + unitToPixel(static_cast<double>(dy) - sy, flags, Direction::vertical)); maWorldTransform *= transform; mappingChanged(); break; @@ -1808,7 +1867,7 @@ namespace emfplushelper { sal_uInt32 stackIndex; rMS.ReadUInt32(stackIndex); - SAL_INFO("drawinglayer", "EMF+\t Begin Container No Params stack index: " << stackIndex); + SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container No Params stack index: " << stackIndex); GraphicStatePush(mGSContainerStack, stackIndex); break; @@ -1817,33 +1876,33 @@ namespace emfplushelper { sal_uInt32 stackIndex; rMS.ReadUInt32(stackIndex); - SAL_INFO("drawinglayer", "EMF+\t End Container stack index: " << stackIndex); + SAL_INFO("drawinglayer.emf", "EMF+\t End Container stack index: " << stackIndex); - GraphicStatePop(mGSContainerStack, stackIndex, mrPropertyHolders.Current()); + GraphicStatePop(mGSContainerStack, stackIndex); break; } case EmfPlusRecordTypeSetWorldTransform: { - SAL_INFO("drawinglayer", "EMF+\t SetWorldTransform, Post multiply: " << bool(flags & 0x2000)); + SAL_INFO("drawinglayer.emf", "EMF+\t SetWorldTransform, Post multiply: " << bool(flags & 0x2000)); readXForm(rMS, maWorldTransform); mappingChanged(); - SAL_INFO("drawinglayer", "EMF+\t\t: " << maWorldTransform); + SAL_INFO("drawinglayer.emf", "EMF+\t\t: " << maWorldTransform); break; } case EmfPlusRecordTypeResetWorldTransform: { maWorldTransform.identity(); - SAL_INFO("drawinglayer", "EMF+\t World transform: " << maWorldTransform); + SAL_INFO("drawinglayer.emf", "EMF+\t World transform: " << maWorldTransform); mappingChanged(); break; } case EmfPlusRecordTypeMultiplyWorldTransform: { - SAL_INFO("drawinglayer", "EMF+\t MultiplyWorldTransform, post multiply: " << bool(flags & 0x2000)); + SAL_INFO("drawinglayer.emf", "EMF+\t MultiplyWorldTransform, post multiply: " << bool(flags & 0x2000)); basegfx::B2DHomMatrix transform; readXForm(rMS, transform); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t Transform matrix: " << transform); if (flags & 0x2000) @@ -1860,13 +1919,13 @@ namespace emfplushelper mappingChanged(); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t World transform matrix: " << maWorldTransform); break; } case EmfPlusRecordTypeTranslateWorldTransform: { - SAL_INFO("drawinglayer", "EMF+\t TranslateWorldTransform, Post multiply: " << bool(flags & 0x2000)); + SAL_INFO("drawinglayer.emf", "EMF+\t TranslateWorldTransform, Post multiply: " << bool(flags & 0x2000)); basegfx::B2DHomMatrix transform; float eDx, eDy; @@ -1874,7 +1933,7 @@ namespace emfplushelper transform.set(0, 2, eDx); transform.set(1, 2, eDy); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t Translate matrix: " << transform); if (flags & 0x2000) @@ -1891,7 +1950,7 @@ namespace emfplushelper mappingChanged(); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t World transform matrix: " << maWorldTransform); break; } @@ -1903,9 +1962,9 @@ namespace emfplushelper transform.set(0, 0, eSx); transform.set(1, 1, eSy); - SAL_INFO("drawinglayer", "EMF+\t ScaleWorldTransform Sx: " << eSx << + SAL_INFO("drawinglayer.emf", "EMF+\t ScaleWorldTransform Sx: " << eSx << " Sy: " << eSy << ", Post multiply:" << bool(flags & 0x2000)); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t World transform matrix: " << maWorldTransform); if (flags & 0x2000) @@ -1922,7 +1981,7 @@ namespace emfplushelper mappingChanged(); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t World transform matrix: " << maWorldTransform); break; } @@ -1932,20 +1991,20 @@ namespace emfplushelper float eAngle; rMS.ReadFloat(eAngle); - SAL_INFO("drawinglayer", "EMF+\t RotateWorldTransform Angle: " << 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", + SAL_INFO("drawinglayer.emf", "EMF+\t " << maWorldTransform); break; } case EmfPlusRecordTypeResetClip: { - SAL_INFO("drawinglayer", "EMF+ ResetClip"); + 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. @@ -1954,74 +2013,107 @@ namespace emfplushelper break; } case EmfPlusRecordTypeSetClipRect: - { - int combineMode = (flags >> 8) & 0xf; - - SAL_INFO("drawinglayer", "EMF+\t SetClipRect combine mode: " << combineMode); - - float dx, dy, dw, dh; - ReadRectangle(rMS, dx, dy, dw, dh); - SAL_INFO("drawinglayer", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh); - ::basegfx::B2DPoint mappedPoint1(Map(dx, dy)); - ::basegfx::B2DPoint mappedPoint2(Map(dx + dw, dy + dh)); - - ::basegfx::B2DPolyPolygon polyPolygon( - ::basegfx::utils::createPolygonFromRect( - ::basegfx::B2DRectangle( - mappedPoint1.getX(), - mappedPoint1.getY(), - mappedPoint2.getX(), - mappedPoint2.getY()))); - - HandleNewClipRegion(combineClip(mrPropertyHolders.Current().getClipPolyPolygon(), - combineMode, polyPolygon), mrTargetHolders, mrPropertyHolders); - break; - } case EmfPlusRecordTypeSetClipPath: + case EmfPlusRecordTypeSetClipRegion: { int combineMode = (flags >> 8) & 0xf; - SAL_INFO("drawinglayer", "EMF+\t SetClipPath combine mode: " << combineMode); - SAL_INFO("drawinglayer", "EMF+\t Path in slot: " << (flags & 0xff)); - - EMFPPath *path = static_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get()); - if (!path) + ::basegfx::B2DPolyPolygon polyPolygon; + if (type == EmfPlusRecordTypeSetClipRect) { - SAL_WARN("drawinglayer", "EMF+\t TODO Unable to find path in slot: " << (flags & 0xff)); - break; + 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)); - ::basegfx::B2DPolyPolygon& clipPoly(path->GetPolygon(*this)); - - HandleNewClipRegion(combineClip(mrPropertyHolders.Current().getClipPolyPolygon(), - combineMode, clipPoly), mrTargetHolders, mrPropertyHolders); - break; - } - case EmfPlusRecordTypeSetClipRegion: - { - int combineMode = (flags >> 8) & 0xf; - SAL_INFO("drawinglayer", "EMF+\t Region in slot: " << (flags & 0xff)); - SAL_INFO("drawinglayer", "EMF+\t Combine mode: " << combineMode); - EMFPRegion *region = static_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get()); - if (!region) + 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_WARN("drawinglayer", "EMF+\t TODO Unable to find region in slot: " << (flags & 0xff)); - break; + 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; } - - HandleNewClipRegion(combineClip(mrPropertyHolders.Current().getClipPolyPolygon(), - combineMode, region->regionPolyPolygon), mrTargetHolders, mrPropertyHolders); + 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+\tOffset x:" << dx << ", y:" << dy); + SAL_INFO("drawinglayer.emf", "EMF+\tOffset x:" << dx << ", y:" << dy); basegfx::B2DPolyPolygon aPolyPolygon( mrPropertyHolders.Current().getClipPolyPolygon()); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t PolyPolygon before translate: " << aPolyPolygon); basegfx::B2DPoint aOffset = Map(dx, dy); @@ -2030,7 +2122,7 @@ namespace emfplushelper transformMatrix.set(1, 2, aOffset.getY()); aPolyPolygon.transform(transformMatrix); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t PolyPolygon after translate: " << aPolyPolygon << ", mapped offset x" << aOffset.getX() << ", mapped offset y" << aOffset.getY()); HandleNewClipRegion(aPolyPolygon, mrTargetHolders, mrPropertyHolders); @@ -2043,22 +2135,22 @@ namespace emfplushelper sal_uInt32 hasMatrix; sal_uInt32 glyphsCount; rMS.ReadUInt32(brushIndexOrColor).ReadUInt32(optionFlags).ReadUInt32(hasMatrix).ReadUInt32(glyphsCount); - SAL_INFO("drawinglayer", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec); - SAL_INFO("drawinglayer", "EMF+\t Option flags: 0x" << std::hex << optionFlags << std::dec); - SAL_INFO("drawinglayer", "EMF+\t Has matrix: " << hasMatrix); - SAL_INFO("drawinglayer", "EMF+\t Glyphs: " << 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+\t DrawDriverString string: " << text); + 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+\t\t glyphPosition[" << i << "]: " << charsPosX[i] << "," << charsPosY[i]); + SAL_INFO("drawinglayer.emf", "EMF+\t\t glyphPosition[" << i << "]: " << charsPosX[i] << "," << charsPosY[i]); } basegfx::B2DHomMatrix transform; @@ -2066,11 +2158,11 @@ namespace emfplushelper if (hasMatrix) { readXForm(rMS, transform); - SAL_INFO("drawinglayer", "EMF+\tmatrix: " << transform); + SAL_INFO("drawinglayer.emf", "EMF+\tmatrix: " << transform); } // get the font from the flags - EMFPFont *font = static_cast< EMFPFont* >( maEMFPObjects[flags & 0xff].get() ); + EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get()); if (!font) { break; @@ -2090,7 +2182,6 @@ namespace emfplushelper false); // BiDiStrong const Color color = EMFPGetBrushColorOrARGBColor(flags, brushIndexOrColor); - std::vector<double> aDXArray; // dummy for DX array (not used) // generate TextSimplePortionPrimitive2Ds or TextDecoratedPortionPrimitive2D // for all portions of text with the same charsPosY values @@ -2103,7 +2194,7 @@ namespace emfplushelper aLength++; // generate the DX-Array - aDXArray.clear(); + std::vector<double> aDXArray; for (size_t i = 0; i < aLength - 1; i++) { aDXArray.push_back(charsPosX[pos + i + 1] - charsPosX[pos]); @@ -2112,13 +2203,13 @@ namespace emfplushelper aDXArray.push_back(0); basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix( - ::basegfx::B2DSize(font->emSize, font->emSize), + ::basegfx::B2DVector(font->emSize, font->emSize), ::basegfx::B2DPoint(charsPosX[pos], charsPosY[pos])); if (hasMatrix) transformMatrix *= transform; if (color.GetAlpha() > 0) { - drawinglayer::primitive2d::BasePrimitive2D* pBaseText = nullptr; + rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText; if (font->Underline() || font->Strikeout()) { pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D( @@ -2126,7 +2217,8 @@ namespace emfplushelper text, pos, // take character at current pos aLength, // use determined length - aDXArray, // generated DXArray + std::move(aDXArray), // generated DXArray + {}, fontAttribute, Application::GetSettings().GetLanguageTag().getLocale(), color.getBColor(), @@ -2145,7 +2237,8 @@ namespace emfplushelper text, pos, // take character at current pos aLength, // use determined length - aDXArray, // generated DXArray + std::move(aDXArray), // generated DXArray + {}, fontAttribute, Application::GetSettings().GetLanguageTag().getLocale(), color.getBColor()); @@ -2158,7 +2251,7 @@ namespace emfplushelper (255 - color.GetAlpha()) / 255.0); } mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::TransformPrimitive2D>( + new drawinglayer::primitive2d::TransformPrimitive2D( maMapTransform, drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } )); } @@ -2169,13 +2262,13 @@ namespace emfplushelper } else { - SAL_WARN("drawinglayer", "EMF+\tTODO: fonts (non-unicode glyphs chars)"); + SAL_WARN("drawinglayer.emf", "EMF+\tTODO: fonts (non-unicode glyphs chars)"); } break; } default: { - SAL_WARN("drawinglayer", "EMF+ TODO unhandled record type: 0x" << std::hex << type << std::dec); + SAL_WARN("drawinglayer.emf", "EMF+ TODO unhandled record type: 0x" << std::hex << type << std::dec); } } } @@ -2188,7 +2281,7 @@ namespace emfplushelper } else { - SAL_WARN("drawinglayer", "ImplRenderer::processEMFPlus: " + SAL_WARN("drawinglayer.emf", "ImplRenderer::processEMFPlus: " "size " << size << " > length " << length); length = 0; } |