/* -*- 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 .
*/
constchar* 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 EmfPlusRecordTypeDrawClosedCurve: return"EmfPlusRecordTypeDrawClosedCurve"; case EmfPlusRecordTypeDrawCurve: return"EmfPlusRecordTypeDrawCurve"; 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 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 u"EmfPlusObjectTypeBrush"_ustr; case EmfPlusObjectTypePen: return u"EmfPlusObjectTypePen"_ustr; case EmfPlusObjectTypePath: return u"EmfPlusObjectTypePath"_ustr; case EmfPlusObjectTypeRegion: return u"EmfPlusObjectTypeRegion"_ustr; case EmfPlusObjectTypeImage: return u"EmfPlusObjectTypeImage"_ustr; case EmfPlusObjectTypeFont: return u"EmfPlusObjectTypeFont"_ustr; case EmfPlusObjectTypeStringFormat: return u"EmfPlusObjectTypeStringFormat"_ustr; case EmfPlusObjectTypeImageAttributes: return u"EmfPlusObjectTypeImageAttributes"_ustr; case EmfPlusObjectTypeCustomLineCap: return u"EmfPlusObjectTypeCustomLineCap"_ustr;
} return u""_ustr;
}
static OUString PixelOffsetModeToString(sal_uInt16 nPixelOffset)
{ switch (nPixelOffset)
{ case PixelOffsetMode::PixelOffsetModeDefault: return u"PixelOffsetModeDefault"_ustr; case PixelOffsetMode::PixelOffsetModeHighSpeed: return u"PixelOffsetModeHighSpeed"_ustr; case PixelOffsetMode::PixelOffsetModeHighQuality: return u"PixelOffsetModeHighQuality"_ustr; case PixelOffsetMode::PixelOffsetModeNone: return u"PixelOffsetModeNone"_ustr; case PixelOffsetMode::PixelOffsetModeHalf: return u"PixelOffsetModeHalf"_ustr;
} return u""_ustr;
}
static OUString SmoothingModeToString(sal_uInt16 nSmoothMode)
{ switch (nSmoothMode)
{ case SmoothingMode::SmoothingModeDefault: return u"SmoothingModeDefault"_ustr; case SmoothingMode::SmoothingModeHighSpeed: return u"SmoothModeHighSpeed"_ustr; case SmoothingMode::SmoothingModeHighQuality: return u"SmoothingModeHighQuality"_ustr; case SmoothingMode::SmoothingModeNone: return u"SmoothingModeNone"_ustr; case SmoothingMode::SmoothingModeAntiAlias8x4: return u"SmoothingModeAntiAlias8x4"_ustr; case SmoothingMode::SmoothingModeAntiAlias8x8: return u"SmoothingModeAntiAlias8x8"_ustr;
} return u""_ustr;
}
static OUString TextRenderingHintToString(sal_uInt16 nHint)
{ switch (nHint)
{ case TextRenderingHint::TextRenderingHintSystemDefault: return u"TextRenderingHintSystemDefault"_ustr; case TextRenderingHint::TextRenderingHintSingleBitPerPixelGridFit: return u"TextRenderingHintSingleBitPerPixelGridFit"_ustr; case TextRenderingHint::TextRenderingHintSingleBitPerPixel: return u"TextRenderingHintSingleBitPerPixel"_ustr; case TextRenderingHint::TextRenderingHintAntialiasGridFit: return u"TextRenderingHintAntialiasGridFit"_ustr; case TextRenderingHint::TextRenderingHintAntialias: return u"TextRenderingHintAntialias"_ustr; case TextRenderingHint::TextRenderingHintClearTypeGridFit: return u"TextRenderingHintClearTypeGridFit"_ustr;
} return u""_ustr;
}
static OUString InterpolationModeToString(sal_uInt16 nMode)
{ switch (nMode)
{ case InterpolationMode::InterpolationModeDefault: return u"InterpolationModeDefault"_ustr; case InterpolationMode::InterpolationModeLowQuality: return u"InterpolationModeLowQuality"_ustr; case InterpolationMode::InterpolationModeHighQuality: return u"InterpolationModeHighQuality"_ustr; case InterpolationMode::InterpolationModeBilinear: return u"InterpolationModeBilinear"_ustr; case InterpolationMode::InterpolationModeBicubic: return u"InterpolationModeBicubic"_ustr; case InterpolationMode::InterpolationModeNearestNeighbor: return u"InterpolationModeNearestNeighbor"_ustr; case InterpolationMode::InterpolationModeHighQualityBilinear: return u"InterpolationModeHighQualityBilinear"_ustr; case InterpolationMode::InterpolationModeHighQualityBicubic: return u"InterpolationModeHighQualityBicubic"_ustr;
} return u""_ustr;
}
OUString UnitTypeToString(sal_uInt16 nType)
{ switch (nType)
{ case UnitTypeWorld: return u"UnitTypeWorld"_ustr; case UnitTypeDisplay: return u"UnitTypeDisplay"_ustr; case UnitTypePixel: return u"UnitTypePixel"_ustr; case UnitTypePoint: return u"UnitTypePoint"_ustr; case UnitTypeInch: return u"UnitTypeInch"_ustr; case UnitTypeDocument: return u"UnitTypeDocument"_ustr; case UnitTypeMillimeter: return u"UnitTypeMillimeter"_ustr;
} return u""_ustr;
}
double EmfPlusHelperData::unitToPixel(double n, sal_uInt32 aUnitType, Direction d)
{ switch (static_cast<UnitType>(aUnitType))
{ case UnitTypePixel: return n;
case UnitTypePoint: return o3tl::convert(n, o3tl::Length::pt, o3tl::Length::in) * DPI(d);
case UnitTypeInch: return n * DPI(d);
case UnitTypeMillimeter: return o3tl::convert(n, o3tl::Length::mm, o3tl::Length::in) * DPI(d);
case UnitTypeDocument: return n * DPI(d) / 300.0;
case UnitTypeWorld: case UnitTypeDisplay:
SAL_WARN("drawinglayer.emf", "EMF+\t Converting to World/Display."); return n;
default:
SAL_WARN("drawinglayer.emf", "EMF+\tTODO Unimplemented support of Unit Type: 0x" << std::hex << aUnitType); return n;
}
}
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 = unitToPixel(pen->penWidth, pen->penUnit, Direction::horizontal); 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 = unitToPixel(font->emSize, font->sizeUnit, Direction::horizontal);
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::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 ] = std::move(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);
}
}
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);
if (brush->type == BrushTypeSolidColor)
{
Color fillColor = brush->solidColor;
EMFPPlusFillPolygonSolidColor(polygon, fillColor);
} elseif (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.
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++)
{ constdouble 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())); constdouble aAlpha = brush->solidColor.GetAlpha() + brush->blendFactors[i] * (brush->secondColor.GetAlpha() - brush->solidColor.GetAlpha());
aVector.emplace_back(aBlendPoint, aColor, aAlpha / 255.0);
}
} elseif (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++)
{ constdouble 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));
}
}
}
}
::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;
}
if (size < 12)
{
SAL_WARN("drawinglayer.emf", "Size field is less than 12 bytes"); break;
} elseif (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;
}
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);
}
// 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);
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;
} elseif (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;
} elseif (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);
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
u""_ustr, // (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
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; elseif (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; elseif (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; elseif (stringFormat->lineAlign == StringAlignmentCenter)
stringAlignmentVerticalOffset = 0.5 * lh + 0.5 * font->emSize; elseif (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();
}
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");
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);
} elseif (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 = std::move(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);
// 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
u""_ustr, // (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::B2DVector(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,
0,
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 } ));
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.