diff options
Diffstat (limited to 'drawinglayer/source/tools/emfppen.cxx')
-rw-r--r-- | drawinglayer/source/tools/emfppen.cxx | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/drawinglayer/source/tools/emfppen.cxx b/drawinglayer/source/tools/emfppen.cxx new file mode 100644 index 000000000..adfee3bd3 --- /dev/null +++ b/drawinglayer/source/tools/emfppen.cxx @@ -0,0 +1,382 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/rendering/PathCapType.hpp> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> +#include <rtl/ustrbuf.hxx> + +#include "emfppen.hxx" +#include "emfpcustomlinecap.hxx" + +using namespace ::com::sun::star; +using namespace ::basegfx; + +namespace emfplushelper +{ + + EMFPPen::EMFPPen() + : penDataFlags(0) + , penUnit(0) + , penWidth(0.0) + , startCap(0) + , endCap(0) + , maLineJoin(basegfx::B2DLineJoin::Miter) + , fMiterMinimumAngle(basegfx::deg2rad(5.0)) + , dashStyle(0) + , dashCap(0) + , dashOffset(0.0) + , alignment(0) + , customStartCapLen(0) + , customEndCapLen(0) + { + } + + EMFPPen::~EMFPPen() + { + } + + static OUString PenDataFlagsToString(sal_uInt32 flags) + { + rtl::OUStringBuffer sFlags; + + if (flags & EmfPlusPenDataTransform) + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataTransform"); + + if (flags & EmfPlusPenDataStartCap) + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataStartCap"); + + if (flags & EmfPlusPenDataEndCap) + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataEndCap"); + + if (flags & EmfPlusPenDataJoin) + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataJoin"); + + if (flags & EmfPlusPenDataMiterLimit) + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataMiterLimit"); + + if (flags & EmfPlusPenDataLineStyle) + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataLineStyle"); + + if (flags & EmfPlusPenDataDashedLineCap) + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataDashedLineCap"); + + if (flags & EmfPlusPenDataDashedLineOffset) + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataDashedLineOffset"); + + if (flags & EmfPlusPenDataDashedLine) + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataDashedLine"); + + if (flags & EmfPlusPenDataAlignment) + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataAlignment"); + + if (flags & EmfPlusPenDataCompoundLine) + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataCompoundLine"); + + if (flags & EmfPlusPenDataCustomStartCap) + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataCustomStartCap"); + + if (flags & EmfPlusPenDataCustomEndCap) + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataCustomEndCap"); + + return sFlags.makeStringAndClear(); + } + + static OUString LineCapTypeToString(sal_uInt32 linecap) + { + switch (linecap) + { + case LineCapTypeFlat: return "LineCapTypeFlat"; + case LineCapTypeSquare: return "LineCapTypeSquare"; + case LineCapTypeRound: return "LineCapTypeRound"; + case LineCapTypeTriangle: return "LineCapTypeTriangle"; + case LineCapTypeNoAnchor: return "LineCapTypeNoAnchor"; + case LineCapTypeSquareAnchor: return "LineCapTypeSquareAnchor"; + case LineCapTypeRoundAnchor: return "LineCapTypeRoundAchor"; + case LineCapTypeDiamondAnchor: return "LineCapTypeDiamondAnchor"; + case LineCapTypeArrowAnchor: return "LineCapTypeArrowAnchor"; + case LineCapTypeAnchorMask: return "LineCapTypeAnchorMask"; + case LineCapTypeCustom: return "LineCapTypeCustom"; + } + return ""; + } + + static OUString DashedLineCapTypeToString(sal_uInt32 dashedlinecaptype) + { + switch (dashedlinecaptype) + { + case DashedLineCapTypeFlat: return "DashedLineCapTypeFlat"; + case DashedLineCapTypeRound: return "DashedLineCapTypeRound"; + case DashedLineCapTypeTriangle: return "DashedLineCapTypeTriangle"; + } + return ""; + } + + static OUString PenAlignmentToString(sal_uInt32 alignment) + { + switch (alignment) + { + case PenAlignmentCenter: return "PenAlignmentCenter"; + case PenAlignmentInset: return "PenAlignmentInset"; + case PenAlignmentLeft: return "PenAlignmentLeft"; + case PenAlignmentOutset: return "PenAlignmentOutset"; + case PenAlignmentRight: return "PenAlignmentRight"; + } + return ""; + } + + drawinglayer::attribute::StrokeAttribute + EMFPPen::GetStrokeAttribute(const double aTransformation) const + { + if (penDataFlags & EmfPlusPenDataLineStyle // pen has a predefined line style + && dashStyle != EmfPlusLineStyleCustom) + { + const double pw = aTransformation * penWidth; + switch (dashStyle) + { + case EmfPlusLineStyleDash: + // [-loplugin:redundantfcast] false positive: + return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw }); + case EmfPlusLineStyleDot: + // [-loplugin:redundantfcast] false positive: + return drawinglayer::attribute::StrokeAttribute({ pw, pw }); + case EmfPlusLineStyleDashDot: + // [-loplugin:redundantfcast] false positive: + return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw, pw, pw }); + case EmfPlusLineStyleDashDotDot: + // [-loplugin:redundantfcast] false positive: + return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw, pw, pw, pw, pw }); + } + } + else if (penDataFlags & EmfPlusPenDataDashedLine) // pen has a custom dash line + { + const double pw = aTransformation * penWidth; + // StrokeAttribute needs a double vector while the pen provides a float vector + std::vector<double> aPattern(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] = pw * dashPattern[i]; + } + return drawinglayer::attribute::StrokeAttribute(std::move(aPattern)); + } + // EmfPlusLineStyleSolid: - do nothing special, use default stroke attribute + return drawinglayer::attribute::StrokeAttribute(); + } + + void EMFPPen::Read(SvStream& s, EmfPlusHelperData const & rR) + { + sal_Int32 lineJoin = EmfPlusLineJoinTypeMiter; + sal_uInt32 graphicsVersion, penType; + s.ReadUInt32(graphicsVersion).ReadUInt32(penType).ReadUInt32(penDataFlags).ReadUInt32(penUnit).ReadFloat(penWidth); + SAL_INFO("drawinglayer.emf", "EMF+\t\tGraphics version: 0x" << std::hex << graphicsVersion); + SAL_INFO("drawinglayer.emf", "EMF+\t\tType: " << penType); + SAL_INFO("drawinglayer.emf", "EMF+\t\tPen data flags: 0x" << penDataFlags << PenDataFlagsToString(penDataFlags)); + SAL_INFO("drawinglayer.emf", "EMF+\t\tUnit: " << UnitTypeToString(penUnit)); + SAL_INFO("drawinglayer.emf", "EMF+\t\tWidth: " << std::dec << penWidth); + + // If a zero width is specified, a minimum value must be used, which is determined by the units + if (penWidth == 0.0) + { //TODO Check if these values is correct + penWidth = penUnit == 0 ? 0.18f + : 0.05f; // 0.05f is taken from old EMF+ implementation (case of Unit == Pixel etc.) + } + + if (penDataFlags & EmfPlusPenDataTransform) + { + EmfPlusHelperData::readXForm(s, pen_transformation); + SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO PenDataTransform: " << pen_transformation); + } + + if (penDataFlags & EmfPlusPenDataStartCap) + { + s.ReadInt32(startCap); + SAL_INFO("drawinglayer.emf", "EMF+\t\tstartCap: " << LineCapTypeToString(startCap) << " (0x" << std::hex << startCap << ")"); + } + else + { + startCap = 0; + } + + if (penDataFlags & EmfPlusPenDataEndCap) + { + s.ReadInt32(endCap); + SAL_INFO("drawinglayer.emf", "EMF+\t\tendCap: " << LineCapTypeToString(endCap) << " (0x" << std::hex << startCap << ")"); + } + else + { + endCap = 0; + } + + if (penDataFlags & EmfPlusPenDataJoin) + { + s.ReadInt32(lineJoin); + SAL_INFO("drawinglayer.emf", "EMF+\t\t LineJoin: " << lineJoin); + switch (lineJoin) + { + case EmfPlusLineJoinTypeBevel: + maLineJoin = basegfx::B2DLineJoin::Bevel; + break; + case EmfPlusLineJoinTypeRound: + maLineJoin = basegfx::B2DLineJoin::Round; + break; + case EmfPlusLineJoinTypeMiter: + case EmfPlusLineJoinTypeMiterClipped: + default: // If nothing set, then apply Miter (based on MS Paint) + maLineJoin = basegfx::B2DLineJoin::Miter; + break; + } + } + else + maLineJoin = basegfx::B2DLineJoin::Miter; + + if (penDataFlags & EmfPlusPenDataMiterLimit) + { + float miterLimit; + s.ReadFloat(miterLimit); + + // EMF+ JoinTypeMiterClipped is working as our B2DLineJoin::Miter + // For EMF+ LineJoinTypeMiter we are simulating it by changing angle + if (lineJoin == EmfPlusLineJoinTypeMiter) + miterLimit = 3.0 * miterLimit; + // asin angle must be in range [-1, 1] + if (abs(miterLimit) > 1.0) + fMiterMinimumAngle = 2.0 * asin(1.0 / miterLimit); + else + // enable miter limit for all angles + fMiterMinimumAngle = basegfx::deg2rad(180.0); + SAL_INFO("drawinglayer.emf", + "EMF+\t\t MiterLimit: " << std::dec << miterLimit + << ", Miter minimum angle (rad): " << fMiterMinimumAngle); + } + else + fMiterMinimumAngle = basegfx::deg2rad(5.0); + + + if (penDataFlags & EmfPlusPenDataLineStyle) + { + s.ReadInt32(dashStyle); + SAL_INFO("drawinglayer.emf", "EMF+\t\tdashStyle: " << DashedLineCapTypeToString(dashStyle) << " (0x" << std::hex << dashStyle << ")"); + } + else + { + dashStyle = 0; + } + + if (penDataFlags & EmfPlusPenDataDashedLineCap) + { + s.ReadInt32(dashCap); + SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO PenDataDashedLineCap: 0x" << std::hex << dashCap); + } + else + { + dashCap = 0; + } + + if (penDataFlags & EmfPlusPenDataDashedLineOffset) + { + s.ReadFloat(dashOffset); + SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO PenDataDashedLineOffset: 0x" << std::hex << dashOffset); + } + else + { + dashOffset = 0; + } + + if (penDataFlags & EmfPlusPenDataDashedLine) + { + dashStyle = EmfPlusLineStyleCustom; + sal_uInt32 dashPatternLen; + + s.ReadUInt32(dashPatternLen); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\tdashPatternLen: " << dashPatternLen); + + dashPattern.resize( dashPatternLen ); + + for (sal_uInt32 i = 0; i < dashPatternLen; i++) + { + s.ReadFloat(dashPattern[i]); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tdashPattern[" << i << "]: " << dashPattern[i]); + } + } + + if (penDataFlags & EmfPlusPenDataAlignment) + { + s.ReadInt32(alignment); + SAL_WARN("drawinglayer.emf", "EMF+\t\t\tTODO PenDataAlignment: " << PenAlignmentToString(alignment) << " (0x" << std::hex << alignment << ")"); + } + else + { + alignment = 0; + } + + if (penDataFlags & EmfPlusPenDataCompoundLine) + { + SAL_WARN("drawinglayer.emf", "EMF+\t\t\tTODO PenDataCompoundLine"); + sal_uInt32 compoundArrayLen; + s.ReadUInt32(compoundArrayLen); + + compoundArray.resize(compoundArrayLen); + + for (sal_uInt32 i = 0; i < compoundArrayLen; i++) + { + s.ReadFloat(compoundArray[i]); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tcompoundArray[" << i << "]: " << compoundArray[i]); + } + } + + if (penDataFlags & EmfPlusPenDataCustomStartCap) + { + s.ReadUInt32(customStartCapLen); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\tcustomStartCapLen: " << customStartCapLen); + sal_uInt64 const pos = s.Tell(); + + customStartCap.reset( new EMFPCustomLineCap() ); + customStartCap->Read(s, rR); + + // maybe we don't read everything yet, play it safe ;-) + s.Seek(pos + customStartCapLen); + } + else + { + customStartCapLen = 0; + } + + if (penDataFlags & EmfPlusPenDataCustomEndCap) + { + s.ReadUInt32(customEndCapLen); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\tcustomEndCapLen: " << customEndCapLen); + sal_uInt64 const pos = s.Tell(); + + customEndCap.reset( new EMFPCustomLineCap() ); + customEndCap->Read(s, rR); + + // maybe we don't read everything yet, play it safe ;-) + s.Seek(pos + customEndCapLen); + } + else + { + customEndCapLen = 0; + } + + EMFPBrush::Read(s, rR); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |