1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/rendering/PathCapType.hpp>
21 #include <o3tl/safeint.hxx>
22 #include <sal/log.hxx>
23 #include <rtl/ustrbuf.hxx>
25 #include "emfppen.hxx"
26 #include "emfpcustomlinecap.hxx"
28 using namespace ::com::sun::star
;
29 using namespace ::basegfx
;
31 namespace emfplushelper
40 , maLineJoin(basegfx::B2DLineJoin::Miter
)
41 , fMiterMinimumAngle(basegfx::deg2rad(5.0))
46 , customStartCapLen(0)
55 static OUString
PenDataFlagsToString(sal_uInt32 flags
)
57 rtl::OUStringBuffer sFlags
;
59 if (flags
& EmfPlusPenDataTransform
)
60 sFlags
.append("\nEMF+\t\t\tEmfPlusPenDataTransform");
62 if (flags
& EmfPlusPenDataStartCap
)
63 sFlags
.append("\nEMF+\t\t\tEmfPlusPenDataStartCap");
65 if (flags
& EmfPlusPenDataEndCap
)
66 sFlags
.append("\nEMF+\t\t\tEmfPlusPenDataEndCap");
68 if (flags
& EmfPlusPenDataJoin
)
69 sFlags
.append("\nEMF+\t\t\tEmfPlusPenDataJoin");
71 if (flags
& EmfPlusPenDataMiterLimit
)
72 sFlags
.append("\nEMF+\t\t\tEmfPlusPenDataMiterLimit");
74 if (flags
& EmfPlusPenDataLineStyle
)
75 sFlags
.append("\nEMF+\t\t\tEmfPlusPenDataLineStyle");
77 if (flags
& EmfPlusPenDataDashedLineCap
)
78 sFlags
.append("\nEMF+\t\t\tEmfPlusPenDataDashedLineCap");
80 if (flags
& EmfPlusPenDataDashedLineOffset
)
81 sFlags
.append("\nEMF+\t\t\tEmfPlusPenDataDashedLineOffset");
83 if (flags
& EmfPlusPenDataDashedLine
)
84 sFlags
.append("\nEMF+\t\t\tEmfPlusPenDataDashedLine");
86 if (flags
& EmfPlusPenDataAlignment
)
87 sFlags
.append("\nEMF+\t\t\tEmfPlusPenDataAlignment");
89 if (flags
& EmfPlusPenDataCompoundLine
)
90 sFlags
.append("\nEMF+\t\t\tEmfPlusPenDataCompoundLine");
92 if (flags
& EmfPlusPenDataCustomStartCap
)
93 sFlags
.append("\nEMF+\t\t\tEmfPlusPenDataCustomStartCap");
95 if (flags
& EmfPlusPenDataCustomEndCap
)
96 sFlags
.append("\nEMF+\t\t\tEmfPlusPenDataCustomEndCap");
98 return sFlags
.makeStringAndClear();
101 static OUString
LineCapTypeToString(sal_uInt32 linecap
)
105 case LineCapTypeFlat
: return "LineCapTypeFlat";
106 case LineCapTypeSquare
: return "LineCapTypeSquare";
107 case LineCapTypeRound
: return "LineCapTypeRound";
108 case LineCapTypeTriangle
: return "LineCapTypeTriangle";
109 case LineCapTypeNoAnchor
: return "LineCapTypeNoAnchor";
110 case LineCapTypeSquareAnchor
: return "LineCapTypeSquareAnchor";
111 case LineCapTypeRoundAnchor
: return "LineCapTypeRoundAchor";
112 case LineCapTypeDiamondAnchor
: return "LineCapTypeDiamondAnchor";
113 case LineCapTypeArrowAnchor
: return "LineCapTypeArrowAnchor";
114 case LineCapTypeAnchorMask
: return "LineCapTypeAnchorMask";
115 case LineCapTypeCustom
: return "LineCapTypeCustom";
120 static OUString
DashedLineCapTypeToString(sal_uInt32 dashedlinecaptype
)
122 switch (dashedlinecaptype
)
124 case DashedLineCapTypeFlat
: return "DashedLineCapTypeFlat";
125 case DashedLineCapTypeRound
: return "DashedLineCapTypeRound";
126 case DashedLineCapTypeTriangle
: return "DashedLineCapTypeTriangle";
131 static OUString
PenAlignmentToString(sal_uInt32 alignment
)
135 case PenAlignmentCenter
: return "PenAlignmentCenter";
136 case PenAlignmentInset
: return "PenAlignmentInset";
137 case PenAlignmentLeft
: return "PenAlignmentLeft";
138 case PenAlignmentOutset
: return "PenAlignmentOutset";
139 case PenAlignmentRight
: return "PenAlignmentRight";
144 drawinglayer::attribute::StrokeAttribute
145 EMFPPen::GetStrokeAttribute(const double aTransformation
) const
147 if (penDataFlags
& EmfPlusPenDataLineStyle
// pen has a predefined line style
148 && dashStyle
!= EmfPlusLineStyleCustom
)
150 const double pw
= aTransformation
* penWidth
;
153 case EmfPlusLineStyleDash
:
154 // [-loplugin:redundantfcast] false positive:
155 return drawinglayer::attribute::StrokeAttribute({ 3 * pw
, pw
});
156 case EmfPlusLineStyleDot
:
157 // [-loplugin:redundantfcast] false positive:
158 return drawinglayer::attribute::StrokeAttribute({ pw
, pw
});
159 case EmfPlusLineStyleDashDot
:
160 // [-loplugin:redundantfcast] false positive:
161 return drawinglayer::attribute::StrokeAttribute({ 3 * pw
, pw
, pw
, pw
});
162 case EmfPlusLineStyleDashDotDot
:
163 // [-loplugin:redundantfcast] false positive:
164 return drawinglayer::attribute::StrokeAttribute({ 3 * pw
, pw
, pw
, pw
, pw
, pw
});
167 else if (penDataFlags
& EmfPlusPenDataDashedLine
) // pen has a custom dash line
169 const double pw
= aTransformation
* penWidth
;
170 // StrokeAttribute needs a double vector while the pen provides a float vector
171 std::vector
<double> aPattern(dashPattern
.size());
172 for (size_t i
= 0; i
< aPattern
.size(); i
++)
174 // convert from float to double and multiply with the adjusted pen width
175 aPattern
[i
] = pw
* dashPattern
[i
];
177 return drawinglayer::attribute::StrokeAttribute(std::move(aPattern
));
179 // EmfPlusLineStyleSolid: - do nothing special, use default stroke attribute
180 return drawinglayer::attribute::StrokeAttribute();
183 void EMFPPen::Read(SvStream
& s
, EmfPlusHelperData
const & rR
)
185 sal_Int32 lineJoin
= EmfPlusLineJoinTypeMiter
;
186 sal_uInt32 graphicsVersion
, penType
;
187 s
.ReadUInt32(graphicsVersion
).ReadUInt32(penType
).ReadUInt32(penDataFlags
).ReadUInt32(penUnit
).ReadFloat(penWidth
);
188 SAL_INFO("drawinglayer.emf", "EMF+\t\tGraphics version: 0x" << std::hex
<< graphicsVersion
);
189 SAL_INFO("drawinglayer.emf", "EMF+\t\tType: " << penType
);
190 SAL_INFO("drawinglayer.emf", "EMF+\t\tPen data flags: 0x" << penDataFlags
<< PenDataFlagsToString(penDataFlags
));
191 SAL_INFO("drawinglayer.emf", "EMF+\t\tUnit: " << UnitTypeToString(penUnit
));
192 SAL_INFO("drawinglayer.emf", "EMF+\t\tWidth: " << std::dec
<< penWidth
);
194 // If a zero width is specified, a minimum value must be used, which is determined by the units
196 { //TODO Check if these values is correct
197 penWidth
= penUnit
== 0 ? 0.18f
198 : 0.05f
; // 0.05f is taken from old EMF+ implementation (case of Unit == Pixel etc.)
201 if (penDataFlags
& EmfPlusPenDataTransform
)
203 EmfPlusHelperData::readXForm(s
, pen_transformation
);
204 SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO PenDataTransform: " << pen_transformation
);
207 if (penDataFlags
& EmfPlusPenDataStartCap
)
209 s
.ReadInt32(startCap
);
210 SAL_INFO("drawinglayer.emf", "EMF+\t\tstartCap: " << LineCapTypeToString(startCap
) << " (0x" << std::hex
<< startCap
<< ")");
217 if (penDataFlags
& EmfPlusPenDataEndCap
)
220 SAL_INFO("drawinglayer.emf", "EMF+\t\tendCap: " << LineCapTypeToString(endCap
) << " (0x" << std::hex
<< startCap
<< ")");
227 if (penDataFlags
& EmfPlusPenDataJoin
)
229 s
.ReadInt32(lineJoin
);
230 SAL_INFO("drawinglayer.emf", "EMF+\t\t LineJoin: " << lineJoin
);
233 case EmfPlusLineJoinTypeBevel
:
234 maLineJoin
= basegfx::B2DLineJoin::Bevel
;
236 case EmfPlusLineJoinTypeRound
:
237 maLineJoin
= basegfx::B2DLineJoin::Round
;
239 case EmfPlusLineJoinTypeMiter
:
240 case EmfPlusLineJoinTypeMiterClipped
:
241 default: // If nothing set, then apply Miter (based on MS Paint)
242 maLineJoin
= basegfx::B2DLineJoin::Miter
;
247 maLineJoin
= basegfx::B2DLineJoin::Miter
;
249 if (penDataFlags
& EmfPlusPenDataMiterLimit
)
252 s
.ReadFloat(miterLimit
);
254 // EMF+ JoinTypeMiterClipped is working as our B2DLineJoin::Miter
255 // For EMF+ LineJoinTypeMiter we are simulating it by changing angle
256 if (lineJoin
== EmfPlusLineJoinTypeMiter
)
257 miterLimit
= 3.0 * miterLimit
;
258 // asin angle must be in range [-1, 1]
259 if (abs(miterLimit
) > 1.0)
260 fMiterMinimumAngle
= 2.0 * asin(1.0 / miterLimit
);
262 // enable miter limit for all angles
263 fMiterMinimumAngle
= basegfx::deg2rad(180.0);
264 SAL_INFO("drawinglayer.emf",
265 "EMF+\t\t MiterLimit: " << std::dec
<< miterLimit
266 << ", Miter minimum angle (rad): " << fMiterMinimumAngle
);
269 fMiterMinimumAngle
= basegfx::deg2rad(5.0);
272 if (penDataFlags
& EmfPlusPenDataLineStyle
)
274 s
.ReadInt32(dashStyle
);
275 SAL_INFO("drawinglayer.emf", "EMF+\t\tdashStyle: " << DashedLineCapTypeToString(dashStyle
) << " (0x" << std::hex
<< dashStyle
<< ")");
282 if (penDataFlags
& EmfPlusPenDataDashedLineCap
)
284 s
.ReadInt32(dashCap
);
285 SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO PenDataDashedLineCap: 0x" << std::hex
<< dashCap
);
292 if (penDataFlags
& EmfPlusPenDataDashedLineOffset
)
294 s
.ReadFloat(dashOffset
);
295 SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO PenDataDashedLineOffset: 0x" << std::hex
<< dashOffset
);
302 if (penDataFlags
& EmfPlusPenDataDashedLine
)
304 dashStyle
= EmfPlusLineStyleCustom
;
305 sal_uInt32 dashPatternLen
;
307 s
.ReadUInt32(dashPatternLen
);
308 SAL_INFO("drawinglayer.emf", "EMF+\t\t\tdashPatternLen: " << dashPatternLen
);
310 dashPattern
.resize( dashPatternLen
);
312 for (sal_uInt32 i
= 0; i
< dashPatternLen
; i
++)
314 s
.ReadFloat(dashPattern
[i
]);
315 SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tdashPattern[" << i
<< "]: " << dashPattern
[i
]);
319 if (penDataFlags
& EmfPlusPenDataAlignment
)
321 s
.ReadInt32(alignment
);
322 SAL_WARN("drawinglayer.emf", "EMF+\t\t\tTODO PenDataAlignment: " << PenAlignmentToString(alignment
) << " (0x" << std::hex
<< alignment
<< ")");
329 if (penDataFlags
& EmfPlusPenDataCompoundLine
)
331 SAL_WARN("drawinglayer.emf", "EMF+\t\t\tTODO PenDataCompoundLine");
332 sal_uInt32 compoundArrayLen
;
333 s
.ReadUInt32(compoundArrayLen
);
335 compoundArray
.resize(compoundArrayLen
);
337 for (sal_uInt32 i
= 0; i
< compoundArrayLen
; i
++)
339 s
.ReadFloat(compoundArray
[i
]);
340 SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tcompoundArray[" << i
<< "]: " << compoundArray
[i
]);
344 if (penDataFlags
& EmfPlusPenDataCustomStartCap
)
346 s
.ReadUInt32(customStartCapLen
);
347 SAL_INFO("drawinglayer.emf", "EMF+\t\t\tcustomStartCapLen: " << customStartCapLen
);
348 sal_uInt64
const pos
= s
.Tell();
350 customStartCap
.reset( new EMFPCustomLineCap() );
351 customStartCap
->Read(s
, rR
);
353 // maybe we don't read everything yet, play it safe ;-)
354 s
.Seek(pos
+ customStartCapLen
);
358 customStartCapLen
= 0;
361 if (penDataFlags
& EmfPlusPenDataCustomEndCap
)
363 s
.ReadUInt32(customEndCapLen
);
364 SAL_INFO("drawinglayer.emf", "EMF+\t\t\tcustomEndCapLen: " << customEndCapLen
);
365 sal_uInt64
const pos
= s
.Tell();
367 customEndCap
.reset( new EMFPCustomLineCap() );
368 customEndCap
->Read(s
, rR
);
370 // maybe we don't read everything yet, play it safe ;-)
371 s
.Seek(pos
+ customEndCapLen
);
378 EMFPBrush::Read(s
, rR
);
382 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */