/src/libreoffice/emfio/source/reader/mtftools.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
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/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
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 . |
18 | | */ |
19 | | |
20 | | #include <mtftools.hxx> |
21 | | |
22 | | #include <cstdlib> |
23 | | #include <memory> |
24 | | #include <basegfx/matrix/b2dhommatrix.hxx> |
25 | | #include <basegfx/polygon/b2dlinegeometry.hxx> |
26 | | #include <basegfx/polygon/b2dpolypolygoncutter.hxx> |
27 | | #include <basegfx/polygon/b2dpolypolygontools.hxx> |
28 | | #include <tools/mapunit.hxx> |
29 | | #include <vcl/metric.hxx> |
30 | | #include <vcl/graphictools.hxx> |
31 | | #include <vcl/BitmapTools.hxx> |
32 | | #include <vcl/metaact.hxx> |
33 | | #include <vcl/wall.hxx> |
34 | | #include <vcl/region.hxx> |
35 | | #include <vcl/canvastools.hxx> |
36 | | #include <vcl/svapp.hxx> |
37 | | #include <tools/stream.hxx> |
38 | | #include <rtl/tencinfo.h> |
39 | | #include <sal/log.hxx> |
40 | | #include <osl/diagnose.h> |
41 | | #include <vcl/virdev.hxx> |
42 | | #include <o3tl/safeint.hxx> |
43 | | #include <comphelper/configuration.hxx> |
44 | | #include <unotools/defaultencoding.hxx> |
45 | | #include <unotools/wincodepage.hxx> |
46 | | |
47 | | #if OSL_DEBUG_LEVEL > 1 |
48 | | #define EMFP_DEBUG(x) x |
49 | | #else |
50 | | #define EMFP_DEBUG(x) |
51 | | #endif |
52 | | |
53 | | namespace emfio |
54 | | { |
55 | | SvStream& operator >> (SvStream& rInStream, XForm& rXForm) |
56 | 50.1k | { |
57 | 50.1k | if (sizeof(float) != 4) |
58 | 0 | { |
59 | 0 | OSL_FAIL("EmfReader::sizeof( float ) != 4"); |
60 | 0 | rXForm = XForm(); |
61 | 0 | } |
62 | 50.1k | else |
63 | 50.1k | { |
64 | 50.1k | rInStream.ReadFloat(rXForm.eM11); |
65 | 50.1k | rInStream.ReadFloat(rXForm.eM12); |
66 | 50.1k | rInStream.ReadFloat(rXForm.eM21); |
67 | 50.1k | rInStream.ReadFloat(rXForm.eM22); |
68 | 50.1k | rInStream.ReadFloat(rXForm.eDx); |
69 | 50.1k | rInStream.ReadFloat(rXForm.eDy); |
70 | 50.1k | if (std::isnan(rXForm.eM11) || |
71 | 46.9k | std::isnan(rXForm.eM12) || |
72 | 43.9k | std::isnan(rXForm.eM21) || |
73 | 41.5k | std::isnan(rXForm.eM22) || |
74 | 39.7k | std::isnan(rXForm.eDx) || |
75 | 38.6k | std::isnan(rXForm.eDy)) |
76 | 12.4k | { |
77 | 12.4k | SAL_WARN("emfio", "XForm member isnan, ignoring"); |
78 | 12.4k | rXForm = XForm(); |
79 | 12.4k | } |
80 | 50.1k | } |
81 | 50.1k | return rInStream; |
82 | 50.1k | } |
83 | | |
84 | | Hatch mapWindowsHatch(sal_uInt32 nHatchIndex, const Color& rColor) |
85 | 1.33k | { |
86 | 1.33k | HatchStyle eStyle; |
87 | 1.33k | Degree10 nAngle; |
88 | 1.33k | switch (nHatchIndex) |
89 | 1.33k | { |
90 | 938 | case 0: // HS_HORIZONTAL |
91 | 938 | eStyle = HatchStyle::Single; nAngle = 0_deg10; break; |
92 | 185 | case 1: // HS_VERTICAL |
93 | 185 | eStyle = HatchStyle::Single; nAngle = Degree10(900); break; |
94 | 105 | case 2: // HS_FDIAGONAL |
95 | 105 | eStyle = HatchStyle::Single; nAngle = Degree10(1350); break; |
96 | 10 | case 3: // HS_BDIAGONAL |
97 | 10 | eStyle = HatchStyle::Single; nAngle = Degree10(450); break; |
98 | 6 | case 4: // HS_CROSS |
99 | 6 | eStyle = HatchStyle::Double; nAngle = 0_deg10; break; |
100 | 95 | case 5: // HS_DIAGCROSS |
101 | 95 | eStyle = HatchStyle::Double; nAngle = Degree10(450); break; |
102 | 0 | default: |
103 | 0 | SAL_WARN("emfio", "Unknown hatch index: " << nHatchIndex); |
104 | 0 | eStyle = HatchStyle::Single; nAngle = 0_deg10; break; |
105 | 1.33k | } |
106 | 1.33k | return Hatch(eStyle, rColor, 50, nAngle); |
107 | 1.33k | } |
108 | | |
109 | | void WinMtfClipPath::intersectClip( const basegfx::B2DPolyPolygon& rPolyPolygon ) |
110 | 0 | { |
111 | 0 | maClip.intersectPolyPolygon(rPolyPolygon); |
112 | 0 | } |
113 | | |
114 | | void WinMtfClipPath::excludeClip( const basegfx::B2DPolyPolygon& rPolyPolygon ) |
115 | 0 | { |
116 | 0 | maClip.subtractPolyPolygon(rPolyPolygon); |
117 | 0 | } |
118 | | |
119 | | void WinMtfClipPath::setClipPath( const basegfx::B2DPolyPolygon& rB2DPoly, RegionMode nClippingMode ) |
120 | 0 | { |
121 | 0 | switch ( nClippingMode ) |
122 | 0 | { |
123 | 0 | case RegionMode::RGN_OR : |
124 | 0 | maClip.unionPolyPolygon(rB2DPoly); |
125 | 0 | break; |
126 | 0 | case RegionMode::RGN_XOR : |
127 | 0 | maClip.xorPolyPolygon(rB2DPoly); |
128 | 0 | break; |
129 | 0 | case RegionMode::RGN_DIFF : |
130 | 0 | maClip.subtractPolyPolygon(rB2DPoly); |
131 | 0 | break; |
132 | 0 | case RegionMode::RGN_AND : |
133 | 0 | maClip.intersectPolyPolygon(rB2DPoly); |
134 | 0 | break; |
135 | 0 | case RegionMode::RGN_COPY : |
136 | 0 | maClip = basegfx::utils::B2DClipState(rB2DPoly); |
137 | 0 | break; |
138 | 0 | } |
139 | 0 | } |
140 | | |
141 | | void WinMtfClipPath::moveClipRegion( const Size& rSize ) |
142 | 0 | { |
143 | 0 | basegfx::B2DHomMatrix aTranslate; |
144 | 0 | aTranslate.translate(rSize.Width(), rSize.Height()); |
145 | 0 | maClip.transform(aTranslate); |
146 | 0 | } |
147 | | |
148 | | void WinMtfClipPath::setDefaultClipPath() |
149 | 339 | { |
150 | | // Empty clip region - everything visible |
151 | 339 | maClip = basegfx::utils::B2DClipState(); |
152 | 339 | } |
153 | | |
154 | | basegfx::B2DPolyPolygon const & WinMtfClipPath::getClipPath() const |
155 | 0 | { |
156 | 0 | return maClip.getClipPoly(); |
157 | 0 | } |
158 | | |
159 | | void WinMtfPathObj::AddPoint( const Point& rPoint ) |
160 | 354k | { |
161 | 354k | if ( bClosed ) |
162 | 345k | Insert( tools::Polygon() ); |
163 | 354k | tools::Polygon& rPoly = static_cast<tools::PolyPolygon&>(*this)[ Count() - 1 ]; |
164 | 354k | rPoly.Insert( rPoly.GetSize(), rPoint ); |
165 | 354k | bClosed = false; |
166 | 354k | } |
167 | | |
168 | | void WinMtfPathObj::AddPolyLine( const tools::Polygon& rPolyLine ) |
169 | 95.9k | { |
170 | 95.9k | if ( bClosed ) |
171 | 76.5k | Insert( tools::Polygon() ); |
172 | 95.9k | tools::Polygon& rPoly = static_cast<tools::PolyPolygon&>(*this)[ Count() - 1 ]; |
173 | 95.9k | rPoly.Insert( rPoly.GetSize(), rPolyLine ); |
174 | 95.9k | bClosed = false; |
175 | 95.9k | } |
176 | | |
177 | | void WinMtfPathObj::AddPolygon( const tools::Polygon& rPoly ) |
178 | 8.43k | { |
179 | 8.43k | Insert( rPoly ); |
180 | 8.43k | bClosed = true; |
181 | 8.43k | } |
182 | | |
183 | | void WinMtfPathObj::AddPolyPolygon( const tools::PolyPolygon& rPolyPoly ) |
184 | 218 | { |
185 | 218 | sal_uInt16 i, nCount = rPolyPoly.Count(); |
186 | 1.26k | for ( i = 0; i < nCount; i++ ) |
187 | 1.05k | Insert( rPolyPoly[ i ] ); |
188 | 218 | bClosed = true; |
189 | 218 | } |
190 | | |
191 | | void WinMtfPathObj::ClosePath() |
192 | 416k | { |
193 | 416k | if ( Count() ) |
194 | 415k | { |
195 | 415k | tools::Polygon& rPoly = static_cast<tools::PolyPolygon&>(*this)[ Count() - 1 ]; |
196 | 415k | if ( rPoly.GetSize() > 2 ) |
197 | 77.9k | { |
198 | 77.9k | Point aFirst( rPoly[ 0 ] ); |
199 | 77.9k | if ( aFirst != rPoly[ rPoly.GetSize() - 1 ] ) |
200 | 39.0k | rPoly.Insert( rPoly.GetSize(), aFirst ); |
201 | 77.9k | } |
202 | 415k | } |
203 | 416k | bClosed = true; |
204 | 416k | } |
205 | | |
206 | | WinMtfFontStyle::WinMtfFontStyle( LOGFONTW const & rFont ) |
207 | 30.3k | { |
208 | 30.3k | rtl_TextEncoding eCharSet; |
209 | 30.3k | if ((rFont.alfFaceName == "Symbol") |
210 | 27.8k | || (rFont.alfFaceName == "MT Extra")) |
211 | 2.51k | eCharSet = RTL_TEXTENCODING_SYMBOL; |
212 | 27.8k | else if ((rFont.lfCharSet == DEFAULT_CHARSET) || (rFont.lfCharSet == OEM_CHARSET)) |
213 | 1.74k | eCharSet = utl_getWinTextEncodingFromLangStr(utl_getLocaleForGlobalDefaultEncoding(), |
214 | 1.74k | rFont.lfCharSet == OEM_CHARSET); |
215 | 26.0k | else |
216 | 26.0k | eCharSet = rtl_getTextEncodingFromWindowsCharset( rFont.lfCharSet ); |
217 | 30.3k | if ( eCharSet == RTL_TEXTENCODING_DONTKNOW ) |
218 | 6.30k | eCharSet = RTL_TEXTENCODING_MS_1252; |
219 | 30.3k | aFont.SetCharSet( eCharSet ); |
220 | 30.3k | aFont.SetFamilyName( rFont.alfFaceName ); |
221 | 30.3k | FontFamily eFamily; |
222 | 30.3k | switch ( rFont.lfPitchAndFamily >> 4 & 0x0f ) |
223 | 30.3k | { |
224 | 5.71k | case FamilyFont::FF_ROMAN: |
225 | 5.71k | eFamily = FAMILY_ROMAN; |
226 | 5.71k | break; |
227 | | |
228 | 5.81k | case FamilyFont::FF_SWISS: |
229 | 5.81k | eFamily = FAMILY_SWISS; |
230 | 5.81k | break; |
231 | | |
232 | 178 | case FamilyFont::FF_MODERN: |
233 | 178 | eFamily = FAMILY_MODERN; |
234 | 178 | break; |
235 | | |
236 | 116 | case FamilyFont::FF_SCRIPT: |
237 | 116 | eFamily = FAMILY_SCRIPT; |
238 | 116 | break; |
239 | | |
240 | 47 | case FamilyFont::FF_DECORATIVE: |
241 | 47 | eFamily = FAMILY_DECORATIVE; |
242 | 47 | break; |
243 | | |
244 | 18.4k | default: |
245 | 18.4k | eFamily = FAMILY_DONTKNOW; |
246 | 18.4k | break; |
247 | 30.3k | } |
248 | 30.3k | aFont.SetFamily( eFamily ); |
249 | | |
250 | 30.3k | FontPitch ePitch; |
251 | 30.3k | switch ( rFont.lfPitchAndFamily & 0x0f ) |
252 | 30.3k | { |
253 | 1.35k | case FIXED_PITCH: |
254 | 1.35k | ePitch = PITCH_FIXED; |
255 | 1.35k | break; |
256 | | |
257 | 13.3k | case DEFAULT_PITCH: |
258 | 18.6k | case VARIABLE_PITCH: |
259 | 28.9k | default: |
260 | 28.9k | ePitch = PITCH_VARIABLE; |
261 | 28.9k | break; |
262 | 30.3k | } |
263 | 30.3k | aFont.SetPitch( ePitch ); |
264 | | |
265 | 30.3k | FontWeight eWeight; |
266 | 30.3k | if (rFont.lfWeight == 0) // default weight SHOULD be used |
267 | 3.82k | eWeight = WEIGHT_DONTKNOW; |
268 | 26.5k | else if (rFont.lfWeight <= FW_THIN) |
269 | 10.8k | eWeight = WEIGHT_THIN; |
270 | 15.6k | else if( rFont.lfWeight <= FW_ULTRALIGHT ) |
271 | 414 | eWeight = WEIGHT_ULTRALIGHT; |
272 | 15.2k | else if( rFont.lfWeight <= FW_LIGHT ) |
273 | 578 | eWeight = WEIGHT_LIGHT; |
274 | 14.6k | else if( rFont.lfWeight < FW_MEDIUM ) |
275 | 7.75k | eWeight = WEIGHT_NORMAL; |
276 | 6.91k | else if( rFont.lfWeight == FW_MEDIUM ) |
277 | 19 | eWeight = WEIGHT_MEDIUM; |
278 | 6.89k | else if( rFont.lfWeight <= FW_SEMIBOLD ) |
279 | 306 | eWeight = WEIGHT_SEMIBOLD; |
280 | 6.59k | else if( rFont.lfWeight <= FW_BOLD ) |
281 | 2.11k | eWeight = WEIGHT_BOLD; |
282 | 4.47k | else if( rFont.lfWeight <= FW_ULTRABOLD ) |
283 | 217 | eWeight = WEIGHT_ULTRABOLD; |
284 | 4.25k | else |
285 | 4.25k | eWeight = WEIGHT_BLACK; |
286 | 30.3k | aFont.SetWeight( eWeight ); |
287 | | |
288 | 30.3k | if( rFont.lfItalic ) |
289 | 17.5k | aFont.SetItalic( ITALIC_NORMAL ); |
290 | | |
291 | 30.3k | if( rFont.lfUnderline ) |
292 | 11.1k | aFont.SetUnderline( LINESTYLE_SINGLE ); |
293 | | |
294 | 30.3k | if( rFont.lfStrikeOut ) |
295 | 15.4k | aFont.SetStrikeout( STRIKEOUT_SINGLE ); |
296 | | |
297 | 30.3k | aFont.SetOrientation( Degree10(static_cast<sal_Int16>(rFont.lfEscapement)) ); |
298 | | |
299 | 30.3k | Size aFontSize( rFont.lfWidth, rFont.lfHeight ); |
300 | 30.3k | if ( rFont.lfHeight > 0 ) |
301 | 4.01k | { |
302 | | // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading |
303 | 4.01k | SolarMutexGuard aGuard; |
304 | 4.01k | ScopedVclPtrInstance< VirtualDevice > pVDev; |
305 | | // converting the cell height into a font height |
306 | 4.01k | aFont.SetFontSize( aFontSize ); |
307 | 4.01k | pVDev->SetFont( aFont ); |
308 | 4.01k | FontMetric aMetric( pVDev->GetFontMetric() ); |
309 | 4.01k | tools::Long nHeight = static_cast<tools::Long>(aMetric.GetAscent()) + aMetric.GetDescent(); |
310 | 4.01k | if (nHeight) |
311 | 4.01k | { |
312 | 4.01k | double fHeight = (static_cast<double>(aFontSize.Height()) * rFont.lfHeight ) / nHeight; |
313 | 4.01k | aFontSize.setHeight( static_cast<sal_Int32>( fHeight + 0.5 ) ); |
314 | 4.01k | } |
315 | 4.01k | } |
316 | | |
317 | | // Convert height to positive |
318 | 30.3k | aFontSize.setHeight( std::abs(aFontSize.Height()) ); |
319 | 30.3k | aFont.SetFontSize(aFontSize); |
320 | | |
321 | | // tdf#127471 adapt nFontWidth from Windows-like notation to |
322 | | // NormedFontScaling if used for text scaling |
323 | 30.3k | const bool bFontScaledHorizontally(aFontSize.Width() != 0 && aFontSize.Width() != aFontSize.Height()); |
324 | | |
325 | 30.3k | if(bFontScaledHorizontally) |
326 | 6.54k | { |
327 | | // tdf#127471 nFontWidth is the Windows FontScaling, need to convert |
328 | | // to notation relative to FontHeight. |
329 | 6.54k | const tools::Long nAverageFontWidth(aFont.GetOrCalculateAverageFontWidth()); |
330 | | |
331 | 6.54k | if(nAverageFontWidth > 0) |
332 | 6.54k | { |
333 | 6.54k | const double fScaleFactor(static_cast<double>(aFontSize.Height()) / static_cast<double>(nAverageFontWidth)); |
334 | 6.54k | aFont.SetFontWidth(static_cast<tools::Long>(static_cast<double>(aFontSize.Width()) * fScaleFactor)); |
335 | 6.54k | } |
336 | 6.54k | } |
337 | 30.3k | }; |
338 | | |
339 | 30.3k | WinMtfFontStyle::~WinMtfFontStyle() = default; |
340 | | |
341 | | // tdf#127471 |
342 | | ScaledFontDetectCorrectHelper::ScaledFontDetectCorrectHelper() |
343 | 22.6k | { |
344 | 22.6k | } |
345 | | |
346 | | void ScaledFontDetectCorrectHelper::endCurrentMetaFontAction() |
347 | 278k | { |
348 | 278k | if(maCurrentMetaFontAction.is() && !maAlternativeFontScales.empty()) |
349 | 3.43k | { |
350 | | // create average corrected FontScale value and count |
351 | | // positive/negative hits |
352 | 3.43k | sal_uInt32 nPositive(0); |
353 | 3.43k | sal_uInt32 nNegative(0); |
354 | 3.43k | double fAverage(0.0); |
355 | | |
356 | 3.43k | for(double fPart : maAlternativeFontScales) |
357 | 3.51k | { |
358 | 3.51k | if(fPart < 0.0) |
359 | 2.70k | { |
360 | 2.70k | nNegative++; |
361 | 2.70k | fAverage += -fPart; |
362 | 2.70k | } |
363 | 814 | else |
364 | 814 | { |
365 | 814 | nPositive++; |
366 | 814 | fAverage += fPart; |
367 | 814 | } |
368 | 3.51k | } |
369 | | |
370 | 3.43k | fAverage /= static_cast<double>(maAlternativeFontScales.size()); |
371 | | |
372 | 3.43k | if(nPositive >= nNegative) |
373 | 737 | { |
374 | | // correction intended, it is probably an old imported file |
375 | 737 | maPositiveIdentifiedCases.emplace_back(maCurrentMetaFontAction, fAverage); |
376 | 737 | } |
377 | 2.70k | else |
378 | 2.70k | { |
379 | | // correction not favorable in the majority of cases for this Font, still |
380 | | // remember to have a weight in the last decision for correction |
381 | 2.70k | maNegativeIdentifiedCases.emplace_back(maCurrentMetaFontAction, fAverage); |
382 | 2.70k | } |
383 | 3.43k | } |
384 | | |
385 | 278k | maCurrentMetaFontAction.clear(); |
386 | 278k | maAlternativeFontScales.clear(); |
387 | 278k | } |
388 | | |
389 | | void ScaledFontDetectCorrectHelper::newCurrentMetaFontAction(const rtl::Reference<MetaFontAction>& rNewMetaFontAction) |
390 | 9.10k | { |
391 | 9.10k | maCurrentMetaFontAction.clear(); |
392 | 9.10k | maAlternativeFontScales.clear(); |
393 | | |
394 | 9.10k | if(!rNewMetaFontAction.is()) |
395 | 0 | return; |
396 | | |
397 | | // check 1st criteria for FontScale active. We usually write this, |
398 | | // so this will already sort out most situations |
399 | 9.10k | const vcl::Font& rCandidate(rNewMetaFontAction->GetFont()); |
400 | | |
401 | 9.10k | if(0 != rCandidate.GetFontWidth()) |
402 | 5.31k | { |
403 | 5.31k | const tools::Long nUnscaledAverageFontWidth(rCandidate.GetOrCalculateAverageFontWidth()); |
404 | | |
405 | | // check 2nd (system-dependent) criteria for FontScale |
406 | 5.31k | if(nUnscaledAverageFontWidth != rCandidate.GetFontHeight()) |
407 | 5.24k | { |
408 | | // FontScale is active, remember and use as current |
409 | 5.24k | maCurrentMetaFontAction = rNewMetaFontAction; |
410 | 5.24k | } |
411 | 5.31k | } |
412 | 9.10k | } |
413 | | |
414 | | void ScaledFontDetectCorrectHelper::evaluateAlternativeFontScale(OUString const & rText, tools::Long nImportedTextLength) |
415 | 8.36k | { |
416 | 8.36k | if(!maCurrentMetaFontAction.is()) |
417 | 4.42k | return; |
418 | | |
419 | 3.94k | SolarMutexGuard aGuard; // VirtualDevice is not thread-safe |
420 | 3.94k | ScopedVclPtrInstance< VirtualDevice > pTempVirtualDevice; |
421 | | |
422 | | // calculate measured TextLength |
423 | 3.94k | const vcl::Font& rFontCandidate(maCurrentMetaFontAction->GetFont()); |
424 | 3.94k | pTempVirtualDevice->SetFont(rFontCandidate); |
425 | 3.94k | tools::Long nMeasuredTextLength(pTempVirtualDevice->GetTextWidth(rText)); |
426 | | // on failure, use original length |
427 | 3.94k | if (!nMeasuredTextLength) |
428 | 423 | nMeasuredTextLength = nImportedTextLength; |
429 | | |
430 | | // compare expected and imported TextLengths |
431 | 3.94k | if (nImportedTextLength == nMeasuredTextLength) |
432 | 424 | return; |
433 | | |
434 | 3.51k | const double fFactorText(static_cast<double>(nImportedTextLength) / static_cast<double>(nMeasuredTextLength)); |
435 | 3.51k | const double fFactorTextPercent(fabs(1.0 - fFactorText) * 100.0); |
436 | | |
437 | | // if we assume that loaded file was written on old linux, we have to |
438 | | // back-convert the scale value: it was already adapted at font import (see |
439 | | // WinMtfFontStyle constructor). It was wrongly assumed to be Windows-style |
440 | | // FontScaling, so we need to revert that to get back to the needed FontScale |
441 | 3.51k | const tools::Long nUnscaledAverageFontWidth(rFontCandidate.GetOrCalculateAverageFontWidth()); |
442 | 3.51k | const tools::Long nScaledAverageFontWidth(rFontCandidate.GetFontWidth()); |
443 | 3.51k | const double fScaleFactor(static_cast<double>(nUnscaledAverageFontWidth) / static_cast<double>(rFontCandidate.GetFontHeight())); |
444 | 3.51k | const double fCorrectedAverageFontWidth(static_cast<double>(nScaledAverageFontWidth) * fScaleFactor); |
445 | 3.51k | tools::Long nCorrectedTextLength(0); |
446 | | |
447 | 3.51k | { // do in own scope, only need nUnscaledAverageFontWidth |
448 | 3.51k | vcl::Font rFontCandidate2(rFontCandidate); |
449 | 3.51k | rFontCandidate2.SetFontWidth(static_cast<tools::Long>(fCorrectedAverageFontWidth)); |
450 | 3.51k | pTempVirtualDevice->SetFont(rFontCandidate2); |
451 | 3.51k | nCorrectedTextLength = pTempVirtualDevice->GetTextWidth(rText); |
452 | | // on failure, use original length |
453 | 3.51k | if (!nCorrectedTextLength) |
454 | 25 | nCorrectedTextLength = nImportedTextLength; |
455 | 3.51k | } |
456 | | |
457 | 3.51k | const double fFactorCorrectedText(static_cast<double>(nImportedTextLength) / static_cast<double>(nCorrectedTextLength)); |
458 | 3.51k | const double fFactorCorrectedTextPercent(fabs(1.0 - fFactorCorrectedText) * 100.0); |
459 | | |
460 | | // If FactorCorrectedText fits better than FactorText this is probably |
461 | | // an import of an old EMF/WMF written by LibreOffice on a non-Windows (unx) system |
462 | | // and should be corrected. |
463 | | // Usually in tested cases this lies inside 5% of range, so detecting this just using |
464 | | // fFactorTextPercent inside 5% -> no old file |
465 | | // fFactorCorrectedTextPercent inside 5% -> is old file |
466 | | // works not too bad, but there are some strange not so often used fonts where that |
467 | | // values do deviate, so better just compare if old corrected would fit better than |
468 | | // the uncorrected case, that is usually safe. |
469 | 3.51k | if(fFactorCorrectedTextPercent < fFactorTextPercent) |
470 | 388 | { |
471 | 388 | maAlternativeFontScales.push_back(fCorrectedAverageFontWidth); |
472 | 388 | } |
473 | 3.12k | else |
474 | 3.12k | { |
475 | | // also push, but negative to remember non-fitting case |
476 | 3.12k | maAlternativeFontScales.push_back(-fCorrectedAverageFontWidth); |
477 | 3.12k | } |
478 | 3.51k | } |
479 | | |
480 | | void ScaledFontDetectCorrectHelper::applyAlternativeFontScale() |
481 | 256k | { |
482 | | // make sure last evtl. detected current FontAction gets added to identified cases |
483 | 256k | endCurrentMetaFontAction(); |
484 | | |
485 | | // Take final decision to correct FontScaling for this imported Metafile or not. |
486 | | // It is possible to weight positive against negative cases, so to only finally |
487 | | // correct when more positive cases were detected. |
488 | | // But that would be inconsequent and wrong. *If* the detected case is an old import |
489 | | // the whole file was written with wrong FontScale values and all Font actions |
490 | | // need to be corrected. Thus, for now, correct all when there are/is positive |
491 | | // cases detected. |
492 | | // On the other hand it *may* be that for some strange fonts there is a false-positive |
493 | | // in the positive cases, so at least insist on positive cases being more than negative. |
494 | | // Still, do then correct *all* cases. |
495 | 256k | if(!maPositiveIdentifiedCases.empty() |
496 | 565 | && maPositiveIdentifiedCases.size() >= maNegativeIdentifiedCases.size()) |
497 | 551 | { |
498 | 551 | for(std::pair<rtl::Reference<MetaFontAction>, double>& rCandidate : maPositiveIdentifiedCases) |
499 | 718 | { |
500 | 718 | rCandidate.first->correctFontScale(static_cast<tools::Long>(rCandidate.second)); |
501 | 718 | } |
502 | 551 | for(std::pair<rtl::Reference<MetaFontAction>, double>& rCandidate : maNegativeIdentifiedCases) |
503 | 35 | { |
504 | 35 | rCandidate.first->correctFontScale(static_cast<tools::Long>(rCandidate.second)); |
505 | 35 | } |
506 | 551 | } |
507 | | |
508 | 256k | maPositiveIdentifiedCases.clear(); |
509 | 256k | maNegativeIdentifiedCases.clear(); |
510 | 256k | } |
511 | | |
512 | | Color MtfTools::ReadColor() |
513 | 38.1k | { |
514 | 38.1k | sal_uInt32 nColor(0); |
515 | 38.1k | mpInputStream->ReadUInt32( nColor ); |
516 | 38.1k | Color aColor( COL_BLACK ); |
517 | 38.1k | if ( ( nColor & 0xFFFF0000 ) == 0x01000000 ) |
518 | 738 | { |
519 | 738 | size_t index = nColor & 0x0000FFFF; |
520 | 738 | if ( index < maPalette.aPaletteColors.size() ) |
521 | 327 | aColor = maPalette.aPaletteColors[ index ]; |
522 | 411 | else |
523 | 738 | SAL_INFO( "emfio", "\t\t Palette index out of range: " << index ); |
524 | 738 | } |
525 | 37.3k | else |
526 | 37.3k | aColor = Color( static_cast<sal_uInt8>( nColor ), static_cast<sal_uInt8>( nColor >> 8 ), static_cast<sal_uInt8>( nColor >> 16 ) ); |
527 | | |
528 | 38.1k | SAL_INFO("emfio", "\t\tColor: " << aColor); |
529 | 38.1k | return aColor; |
530 | 38.1k | }; |
531 | | |
532 | | Point MtfTools::ImplScale(const Point& rPoint) // Hack to set varying defaults for incompletely defined files. |
533 | 0 | { |
534 | 0 | if (!mbIsMapDevSet) |
535 | 0 | return Point(rPoint.X() * UNDOCUMENTED_WIN_RCL_RELATION - mrclFrame.Left(), |
536 | 0 | rPoint.Y() * UNDOCUMENTED_WIN_RCL_RELATION - mrclFrame.Top()); |
537 | 0 | else |
538 | 0 | return rPoint; |
539 | 0 | } |
540 | | |
541 | | Point MtfTools::ImplMap( const Point& rPt ) |
542 | 4.80M | { |
543 | 4.80M | if ( mnWinExtX && mnWinExtY ) |
544 | 4.78M | { |
545 | 4.78M | double fX = rPt.X(); |
546 | 4.78M | double fY = rPt.Y(); |
547 | | |
548 | 4.78M | double fX2 = fX * maXForm.eM11 + fY * maXForm.eM21 + maXForm.eDx; |
549 | 4.78M | double fY2 = fX * maXForm.eM12 + fY * maXForm.eM22 + maXForm.eDy; |
550 | | |
551 | 4.78M | if ( meGfxMode == GraphicsMode::GM_COMPATIBLE ) |
552 | 4.78M | { |
553 | 4.78M | fX2 -= mnWinOrgX; |
554 | 4.78M | fY2 -= mnWinOrgY; |
555 | | |
556 | 4.78M | switch( meMapMode ) |
557 | 4.78M | { |
558 | 1.37k | case MappingMode::MM_LOENGLISH : |
559 | 1.37k | { |
560 | 1.37k | fX2 = o3tl::convert(fX2, o3tl::Length::in100, o3tl::Length::mm100); |
561 | 1.37k | fY2 = o3tl::convert(-fY2, o3tl::Length::in100, o3tl::Length::mm100); |
562 | 1.37k | } |
563 | 1.37k | break; |
564 | 784 | case MappingMode::MM_HIENGLISH : |
565 | 784 | { |
566 | 784 | fX2 = o3tl::convert(fX2, o3tl::Length::in1000, o3tl::Length::mm100); |
567 | 784 | fY2 = o3tl::convert(-fY2, o3tl::Length::in1000, o3tl::Length::mm100); |
568 | 784 | } |
569 | 784 | break; |
570 | 891 | case MappingMode::MM_TWIPS: |
571 | 891 | { |
572 | 891 | fX2 = o3tl::convert(fX2, o3tl::Length::twip, o3tl::Length::mm100); |
573 | 891 | fY2 = o3tl::convert(-fY2, o3tl::Length::twip, o3tl::Length::mm100); |
574 | 891 | } |
575 | 891 | break; |
576 | 439 | case MappingMode::MM_LOMETRIC : |
577 | 439 | { |
578 | 439 | fX2 = o3tl::convert(fX2, o3tl::Length::mm10, o3tl::Length::mm100); |
579 | 439 | fY2 = o3tl::convert(-fY2, o3tl::Length::mm10, o3tl::Length::mm100); |
580 | 439 | } |
581 | 439 | break; |
582 | 2.35k | case MappingMode::MM_HIMETRIC : // in hundredth of a millimeter |
583 | 2.35k | { |
584 | 2.35k | fY2 *= -1; |
585 | 2.35k | } |
586 | 2.35k | break; |
587 | 4.77M | default : |
588 | 4.77M | { |
589 | 4.77M | if (mnPixX == 0 || mnPixY == 0) |
590 | 140k | { |
591 | 140k | SAL_WARN("emfio", "invalid scaling factor"); |
592 | 140k | return Point(); |
593 | 140k | } |
594 | 4.63M | else |
595 | 4.63M | { |
596 | 4.63M | if ( meMapMode != MappingMode::MM_TEXT ) |
597 | 905k | { |
598 | 905k | fX2 /= mnWinExtX; |
599 | 905k | fY2 /= mnWinExtY; |
600 | 905k | fX2 *= mnDevWidth; |
601 | 905k | fY2 *= mnDevHeight; |
602 | 905k | } |
603 | 4.63M | fX2 *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); |
604 | 4.63M | fY2 *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); |
605 | 4.63M | } |
606 | 4.77M | } |
607 | 4.63M | break; |
608 | 4.78M | } |
609 | | |
610 | 4.64M | double nDevOrgX = mnDevOrgX; |
611 | 4.64M | if (mnPixX) |
612 | 4.63M | nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); |
613 | 4.64M | fX2 += nDevOrgX; |
614 | 4.64M | double nDevOrgY = mnDevOrgY; |
615 | 4.64M | if (mnPixY) |
616 | 4.63M | nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); |
617 | 4.64M | fY2 += nDevOrgY; |
618 | | |
619 | 4.64M | fX2 -= mrclFrame.Left(); |
620 | 4.64M | fY2 -= mrclFrame.Top(); |
621 | 4.64M | } |
622 | 4.64M | return Point(basegfx::fround<tools::Long>(fX2), basegfx::fround<tools::Long>(fY2)); |
623 | 4.78M | } |
624 | 26.9k | else |
625 | 26.9k | return Point(); |
626 | 4.80M | }; |
627 | | |
628 | | Size MtfTools::ImplMap(const Size& rSz, bool bDoWorldTransform) |
629 | 219k | { |
630 | 219k | if ( mnWinExtX && mnWinExtY ) |
631 | 212k | { |
632 | | // #i121382# apply the whole WorldTransform, else a rotation will be misinterpreted |
633 | 212k | double fWidth, fHeight; |
634 | 212k | if (bDoWorldTransform) |
635 | 184k | { |
636 | 184k | fWidth = rSz.Width() * maXForm.eM11 + rSz.Height() * maXForm.eM21; |
637 | 184k | fHeight = rSz.Width() * maXForm.eM12 + rSz.Height() * maXForm.eM22; |
638 | 184k | } |
639 | 28.4k | else |
640 | 28.4k | { |
641 | | //take the scale, but not the rotation |
642 | 28.4k | basegfx::B2DHomMatrix aMatrix(maXForm.eM11, maXForm.eM12, 0, |
643 | 28.4k | maXForm.eM21, maXForm.eM22, 0); |
644 | 28.4k | basegfx::B2DTuple aScale, aTranslate; |
645 | 28.4k | double fRotate, fShearX; |
646 | 28.4k | if (!aMatrix.decompose(aScale, aTranslate, fRotate, fShearX)) |
647 | 388 | { |
648 | 388 | aScale.setX(1.0); |
649 | 388 | aScale.setY(1.0); |
650 | 388 | } |
651 | 28.4k | fWidth = rSz.Width() * aScale.getX(); |
652 | 28.4k | fHeight = rSz.Height() * aScale.getY(); |
653 | 28.4k | } |
654 | | |
655 | 212k | if ( meGfxMode == GraphicsMode::GM_COMPATIBLE ) |
656 | 212k | { |
657 | 212k | switch( meMapMode ) |
658 | 212k | { |
659 | 972 | case MappingMode::MM_LOENGLISH : |
660 | 972 | { |
661 | 972 | fWidth = o3tl::convert(fWidth, o3tl::Length::in100, o3tl::Length::mm100); |
662 | 972 | fHeight = o3tl::convert(-fHeight, o3tl::Length::in100, o3tl::Length::mm100); |
663 | 972 | } |
664 | 972 | break; |
665 | 558 | case MappingMode::MM_HIENGLISH : |
666 | 558 | { |
667 | 558 | fWidth = o3tl::convert(fWidth, o3tl::Length::in1000, o3tl::Length::mm100); |
668 | 558 | fHeight = o3tl::convert(-fHeight, o3tl::Length::in1000, o3tl::Length::mm100); |
669 | 558 | } |
670 | 558 | break; |
671 | 1.84k | case MappingMode::MM_LOMETRIC : |
672 | 1.84k | { |
673 | 1.84k | fWidth = o3tl::convert(fWidth, o3tl::Length::mm10, o3tl::Length::mm100); |
674 | 1.84k | fHeight = o3tl::convert(-fHeight, o3tl::Length::mm10, o3tl::Length::mm100); |
675 | 1.84k | } |
676 | 1.84k | break; |
677 | 2.81k | case MappingMode::MM_HIMETRIC : // in hundredth of millimeters |
678 | 2.81k | { |
679 | 2.81k | fHeight *= -1; |
680 | 2.81k | } |
681 | 2.81k | break; |
682 | 77 | case MappingMode::MM_TWIPS: |
683 | 77 | { |
684 | 77 | fWidth = o3tl::convert(fWidth, o3tl::Length::twip, o3tl::Length::mm100); |
685 | 77 | fHeight = o3tl::convert(-fHeight, o3tl::Length::twip, o3tl::Length::mm100); |
686 | 77 | } |
687 | 77 | break; |
688 | 206k | default : |
689 | 206k | { |
690 | 206k | if (mnPixX == 0 || mnPixY == 0) |
691 | 4.43k | { |
692 | 4.43k | SAL_WARN("emfio", "invalid scaling factor"); |
693 | 4.43k | return Size(); |
694 | 4.43k | } |
695 | 202k | else |
696 | 202k | { |
697 | 202k | if ( meMapMode != MappingMode::MM_TEXT ) |
698 | 111k | { |
699 | 111k | fWidth /= mnWinExtX; |
700 | 111k | fHeight /= mnWinExtY; |
701 | 111k | fWidth *= mnDevWidth; |
702 | 111k | fHeight *= mnDevHeight; |
703 | 111k | } |
704 | 202k | fWidth *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); |
705 | 202k | fHeight *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); |
706 | 202k | } |
707 | 206k | } |
708 | 202k | break; |
709 | 212k | } |
710 | 212k | } |
711 | 208k | return Size(basegfx::fround<tools::Long>(fWidth), basegfx::fround<tools::Long>(fHeight)); |
712 | 212k | } |
713 | 6.99k | else |
714 | 6.99k | return Size(); |
715 | 219k | } |
716 | | |
717 | | tools::Rectangle MtfTools::ImplMap( const tools::Rectangle& rRect ) |
718 | 61.3k | { |
719 | 61.3k | tools::Rectangle aRect; |
720 | 61.3k | aRect.SetPos(ImplMap(rRect.TopLeft())); |
721 | 61.3k | aRect.SaturatingSetSize(ImplMap(rRect.GetSize())); |
722 | 61.3k | return aRect; |
723 | 61.3k | } |
724 | | |
725 | | void MtfTools::ImplMap( vcl::Font& rFont ) |
726 | 30.3k | { |
727 | | // !!! HACK: we now always set the width to zero because the OS width is interpreted differently; |
728 | | // must later be made portable in SV (KA 1996-02-08) |
729 | 30.3k | Size aFontSize = ImplMap (rFont.GetFontSize(), false); |
730 | | |
731 | 30.3k | const auto nHeight = aFontSize.Height(); |
732 | 30.3k | if (nHeight < 0) |
733 | 1.52k | aFontSize.setHeight( o3tl::saturating_toggle_sign(nHeight) ); |
734 | | |
735 | 30.3k | rFont.SetFontSize( aFontSize ); |
736 | | |
737 | 30.3k | sal_Int32 nResult; |
738 | 30.3k | const bool bFail = o3tl::checked_multiply(mnWinExtX, mnWinExtY, nResult); |
739 | 30.3k | if (!bFail && nResult < 0) |
740 | 454 | rFont.SetOrientation( 3600_deg10 - rFont.GetOrientation() ); |
741 | 30.3k | } |
742 | | |
743 | | tools::Polygon& MtfTools::ImplMap( tools::Polygon& rPolygon ) |
744 | 151k | { |
745 | 151k | sal_uInt16 nPoints = rPolygon.GetSize(); |
746 | 4.41M | for ( sal_uInt16 i = 0; i < nPoints; i++ ) |
747 | 4.26M | { |
748 | 4.26M | rPolygon[ i ] = ImplMap( rPolygon[ i ] ); |
749 | 4.26M | } |
750 | 151k | return rPolygon; |
751 | 151k | } |
752 | | |
753 | | void MtfTools::ImplScale( tools::Polygon& rPolygon ) |
754 | 0 | { |
755 | 0 | sal_uInt16 nPoints = rPolygon.GetSize(); |
756 | 0 | for ( sal_uInt16 i = 0; i < nPoints; i++ ) |
757 | 0 | { |
758 | 0 | rPolygon[ i ] = ImplScale( rPolygon[ i ] ); |
759 | 0 | } |
760 | 0 | } |
761 | | |
762 | | tools::PolyPolygon& MtfTools::ImplScale( tools::PolyPolygon& rPolyPolygon ) |
763 | 0 | { |
764 | 0 | sal_uInt16 nPolys = rPolyPolygon.Count(); |
765 | 0 | for (sal_uInt16 i = 0; i < nPolys; ++i) |
766 | 0 | { |
767 | 0 | ImplScale(rPolyPolygon[i]); |
768 | 0 | } |
769 | 0 | return rPolyPolygon; |
770 | 0 | } |
771 | | |
772 | | tools::PolyPolygon& MtfTools::ImplMap( tools::PolyPolygon& rPolyPolygon ) |
773 | 5.98k | { |
774 | 5.98k | sal_uInt16 nPolys = rPolyPolygon.Count(); |
775 | 14.8k | for ( sal_uInt16 i = 0; i < nPolys; ImplMap( rPolyPolygon[ i++ ] ) ) ; |
776 | 5.98k | return rPolyPolygon; |
777 | 5.98k | } |
778 | | |
779 | | void MtfTools::SelectObject( sal_uInt32 nIndex ) |
780 | 85.1k | { |
781 | 85.1k | if ( nIndex & ENHMETA_STOCK_OBJECT ) |
782 | 13.7k | { |
783 | 13.7k | SAL_INFO ( "emfio", "\t\t ENHMETA_STOCK_OBJECT, StockObject Enumeration: 0x" << std::hex << nIndex ); |
784 | 13.7k | StockObject nStockId = static_cast<StockObject>(nIndex & 0xFF); |
785 | 13.7k | switch( nStockId ) |
786 | 13.7k | { |
787 | 2.63k | case StockObject::WHITE_BRUSH : |
788 | 2.63k | { |
789 | 2.63k | maFillStyle = WinMtfFillStyle( COL_WHITE ); |
790 | 2.63k | mbFillStyleSelected = true; |
791 | 2.63k | } |
792 | 2.63k | break; |
793 | 394 | case StockObject::LTGRAY_BRUSH : |
794 | 394 | { |
795 | 394 | maFillStyle = WinMtfFillStyle( COL_LIGHTGRAY ); |
796 | 394 | mbFillStyleSelected = true; |
797 | 394 | } |
798 | 394 | break; |
799 | 325 | case StockObject::GRAY_BRUSH : |
800 | 325 | { |
801 | 325 | maFillStyle = WinMtfFillStyle( COL_GRAY ); |
802 | 325 | mbFillStyleSelected = true; |
803 | 325 | } |
804 | 325 | break; |
805 | 75 | case StockObject::DKGRAY_BRUSH : |
806 | 75 | { |
807 | 75 | maFillStyle = WinMtfFillStyle( COL_GRAY7 ); |
808 | 75 | mbFillStyleSelected = true; |
809 | 75 | } |
810 | 75 | break; |
811 | 406 | case StockObject::BLACK_BRUSH : |
812 | 406 | { |
813 | 406 | maFillStyle = WinMtfFillStyle( COL_BLACK ); |
814 | 406 | mbFillStyleSelected = true; |
815 | 406 | } |
816 | 406 | break; |
817 | 1.05k | case StockObject::NULL_BRUSH : |
818 | 1.05k | { |
819 | 1.05k | maFillStyle = WinMtfFillStyle( COL_TRANSPARENT, true ); |
820 | 1.05k | mbFillStyleSelected = true; |
821 | 1.05k | } |
822 | 1.05k | break; |
823 | 472 | case StockObject::WHITE_PEN : |
824 | 472 | { |
825 | 472 | maLineStyle = WinMtfLineStyle(COL_WHITE, PS_COSMETIC, 1); |
826 | 472 | } |
827 | 472 | break; |
828 | 2.94k | case StockObject::BLACK_PEN : |
829 | 2.94k | { |
830 | 2.94k | maLineStyle = WinMtfLineStyle(COL_BLACK, PS_COSMETIC, 1); |
831 | 2.94k | } |
832 | 2.94k | break; |
833 | 1.94k | case StockObject::NULL_PEN : |
834 | 1.94k | { |
835 | 1.94k | maLineStyle = WinMtfLineStyle( COL_TRANSPARENT, true ); |
836 | 1.94k | } |
837 | 1.94k | break; |
838 | 3.49k | default: |
839 | 3.49k | break; |
840 | 13.7k | } |
841 | 13.7k | } |
842 | 71.4k | else |
843 | 71.4k | { |
844 | 71.4k | nIndex &= 0xffff; // safety check: don't allow index to be > 65535 |
845 | | |
846 | 71.4k | GDIObj *pGDIObj = nullptr; |
847 | | |
848 | 71.4k | if ( nIndex < mvGDIObj.size() ) |
849 | 67.6k | pGDIObj = mvGDIObj[ nIndex ].get(); |
850 | | |
851 | 71.4k | if ( pGDIObj ) |
852 | 64.2k | { |
853 | | |
854 | 64.2k | SAL_INFO ( "emfio", "\t\t Index: " << nIndex ); |
855 | 64.2k | if (const auto pen = dynamic_cast<WinMtfLineStyle*>(pGDIObj)) |
856 | 19.6k | { |
857 | 19.6k | maLineStyle = *pen; |
858 | 19.6k | SAL_INFO ( "emfio", "\t Line Style, Color: 0x" << std::hex << maLineStyle.aLineColor |
859 | 19.6k | << ", Weight: " << maLineStyle.aLineInfo.GetWidth() ); |
860 | 19.6k | } |
861 | 44.6k | else if (const auto brush = dynamic_cast<WinMtfFillStyle*>( |
862 | 44.6k | pGDIObj)) |
863 | 16.3k | { |
864 | 16.3k | maFillStyle = *brush; |
865 | 16.3k | mbFillStyleSelected = true; |
866 | 16.3k | SAL_INFO("emfio", "\t\tBrush Object, Index: " << nIndex << ", Color: " << maFillStyle.aFillColor); |
867 | 16.3k | } |
868 | 28.3k | else if (const auto font = dynamic_cast<WinMtfFontStyle*>( |
869 | 28.3k | pGDIObj)) |
870 | 26.0k | { |
871 | 26.0k | maFont = font->aFont; |
872 | 26.0k | SAL_INFO("emfio", "\t\tFont Object, Index: " << nIndex << ", Font: " << maFont.GetFamilyName() << " " << maFont.GetStyleName()); |
873 | 26.0k | } |
874 | 2.23k | else if (const auto palette = dynamic_cast<WinMtfPalette*>( |
875 | 2.23k | pGDIObj)) |
876 | 1.93k | { |
877 | 1.93k | maPalette = palette->aPaletteColors; |
878 | 1.93k | SAL_INFO("emfio", "\t\tPalette Object, Index: " << nIndex << ", Number of colours: " << maPalette.aPaletteColors.size() ); |
879 | 1.93k | } |
880 | 64.2k | } |
881 | 7.18k | else |
882 | 7.18k | { |
883 | 7.18k | SAL_WARN("emfio", "Warning: Unable to find Object with index:" << nIndex); |
884 | 7.18k | } |
885 | 71.4k | } |
886 | 85.1k | } |
887 | | |
888 | | void MtfTools::SetTextLayoutMode( vcl::text::ComplexTextLayoutFlags nTextLayoutMode ) |
889 | 58.2k | { |
890 | 58.2k | mnTextLayoutMode = nTextLayoutMode; |
891 | 58.2k | } |
892 | | |
893 | | void MtfTools::SetArcDirection(bool bClockWise) |
894 | 579 | { |
895 | 579 | SAL_INFO("emfio", "\t\t Arc direction: " << (bClockWise ? "ClockWise" : "CounterClockWise")); |
896 | 579 | mbClockWiseArcDirection = bClockWise; |
897 | 579 | } |
898 | | |
899 | | void MtfTools::SetBkMode( BackgroundMode nMode ) |
900 | 4.53k | { |
901 | 4.53k | if (mnBkMode != nMode) |
902 | 2.74k | { |
903 | 2.74k | mnBkMode = nMode; |
904 | | // The hatch fill background color emitted by UpdateFillStyle |
905 | | // depends on bk mode: opaque -> bk color, transparent -> none. |
906 | | // Invalidate maLatestFillStyle so UpdateFillStyle re-emits a |
907 | | // fresh MetaFillColorAction even when the brush is otherwise |
908 | | // unchanged. |
909 | 2.74k | maLatestFillStyle.aFillColor = Color(0x12, 0x34, 0x56); |
910 | 2.74k | } |
911 | 4.53k | } |
912 | | |
913 | | void MtfTools::SetPolyFillMode( sal_uInt32 nPolyFillMode ) |
914 | 3.26k | { |
915 | 3.26k | mnPolyFillMode = nPolyFillMode; |
916 | 3.26k | } |
917 | | |
918 | | void MtfTools::SetBkColor( const Color& rColor ) |
919 | 5.33k | { |
920 | 5.33k | maBkColor = rColor; |
921 | 5.33k | } |
922 | | |
923 | | void MtfTools::SetTextColor( const Color& rColor ) |
924 | 6.40k | { |
925 | 6.40k | maTextColor = rColor; |
926 | 6.40k | } |
927 | | |
928 | | void MtfTools::SetTextAlign( sal_uInt32 nAlign ) |
929 | 6.19k | { |
930 | 6.19k | mnTextAlign = nAlign; |
931 | 6.19k | } |
932 | | |
933 | | void MtfTools::ImplResizeObjectArry( sal_uInt32 nNewEntrys ) |
934 | 12.1k | { |
935 | 12.1k | mvGDIObj.resize(nNewEntrys); |
936 | 12.1k | } |
937 | | |
938 | | // Convert a PolyPolygon so that nonzero/winding fill rule gives the same visual |
939 | | // result when rendered with even-odd fill rule (which is all VCL supports). |
940 | | static tools::PolyPolygon CreateWindingFillPolyPolygon(const tools::PolyPolygon& rPolyPoly) |
941 | 7.20k | { |
942 | 7.20k | basegfx::B2DPolyPolygon aB2D = rPolyPoly.getB2DPolyPolygon(); |
943 | | // A filled polygon is closed whether or not its last point repeats its first |
944 | 7.20k | aB2D.setClosed(true); |
945 | 7.20k | return tools::PolyPolygon(basegfx::utils::createNonzeroConform(aB2D)); |
946 | 7.20k | } |
947 | | |
948 | | void MtfTools::ImplDrawClippedPolyPolygon( const tools::PolyPolygon& rPolyPoly ) |
949 | 0 | { |
950 | 0 | if ( !rPolyPoly.Count() ) |
951 | 0 | return; |
952 | | |
953 | 0 | ImplSetNonPersistentLineColorTransparenz(); |
954 | |
|
955 | 0 | if (mnPolyFillMode == 2) // WINDING |
956 | 0 | { |
957 | 0 | tools::PolyPolygon aFill = CreateWindingFillPolyPolygon(rPolyPoly); |
958 | 0 | mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( aFill ) ); |
959 | 0 | return; |
960 | 0 | } |
961 | | |
962 | 0 | if ( rPolyPoly.Count() == 1 ) |
963 | 0 | { |
964 | 0 | if ( rPolyPoly.IsRect() ) |
965 | 0 | mpGDIMetaFile->AddAction( new MetaRectAction( rPolyPoly.GetBoundRect() ) ); |
966 | 0 | else |
967 | 0 | { |
968 | 0 | tools::Polygon aPoly( rPolyPoly[ 0 ] ); |
969 | 0 | sal_uInt16 nCount = aPoly.GetSize(); |
970 | 0 | if ( nCount ) |
971 | 0 | { |
972 | 0 | if ( aPoly[ nCount - 1 ] != aPoly[ 0 ] ) |
973 | 0 | { |
974 | 0 | Point aPoint( aPoly[ 0 ] ); |
975 | 0 | aPoly.Insert( nCount, aPoint ); |
976 | 0 | } |
977 | 0 | mpGDIMetaFile->AddAction( new MetaPolygonAction( std::move(aPoly) ) ); |
978 | 0 | } |
979 | 0 | } |
980 | 0 | } |
981 | 0 | else |
982 | 0 | mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPoly ) ); |
983 | |
|
984 | 0 | if (maLatestFillStyle.aType == WinMtfFillStyleType::Hatch) |
985 | 0 | mpGDIMetaFile->AddAction( new MetaHatchAction( rPolyPoly, maLatestFillStyle.aHatch ) ); |
986 | 0 | } |
987 | | |
988 | | void MtfTools::CreateObject( std::unique_ptr<GDIObj> pObject ) |
989 | 43.8k | { |
990 | 43.8k | if ( pObject ) |
991 | 43.8k | { |
992 | 43.8k | const auto pLineStyle = dynamic_cast<WinMtfLineStyle*>(pObject.get()); |
993 | 43.8k | const auto pFontStyle = dynamic_cast<WinMtfFontStyle*>(pObject.get()); |
994 | | |
995 | 43.8k | if ( pFontStyle ) |
996 | 24.5k | { |
997 | 24.5k | if (pFontStyle->aFont.GetFontHeight() == 0) |
998 | 1.80k | pFontStyle->aFont.SetFontHeight(423); |
999 | 24.5k | ImplMap(pFontStyle->aFont); // defaulting to 12pt |
1000 | 24.5k | } |
1001 | 19.3k | else if ( pLineStyle ) |
1002 | 7.96k | { |
1003 | 7.96k | Size aSize(pLineStyle->aLineInfo.GetWidth(), 0); |
1004 | 7.96k | aSize = ImplMap(aSize); |
1005 | 7.96k | pLineStyle->aLineInfo.SetWidth(aSize.Width()); |
1006 | 7.96k | } |
1007 | 43.8k | } |
1008 | 43.8k | std::vector<std::unique_ptr<GDIObj>>::size_type nIndex; |
1009 | 55.1k | for ( nIndex = mnLowestFreeGDIObj; nIndex < mvGDIObj.size(); nIndex++ ) |
1010 | 47.2k | { |
1011 | 47.2k | if ( !mvGDIObj[ nIndex ] ) |
1012 | 35.9k | break; |
1013 | 47.2k | } |
1014 | 43.8k | if ( nIndex > 0xffff ) // the same ceiling an indexed object has |
1015 | 0 | { |
1016 | 0 | SAL_WARN( "emfio", "too many objects" ); |
1017 | 0 | return; |
1018 | 0 | } |
1019 | 43.8k | if ( nIndex == mvGDIObj.size() ) |
1020 | 7.89k | ImplResizeObjectArry( mvGDIObj.size() + 16 ); |
1021 | | |
1022 | 43.8k | mvGDIObj[ nIndex ] = std::move(pObject); |
1023 | 43.8k | mnLowestFreeGDIObj = nIndex + 1; |
1024 | 43.8k | } |
1025 | | |
1026 | | void MtfTools::CreateObjectIndexed( sal_uInt32 nIndex, std::unique_ptr<GDIObj> pObject ) |
1027 | 29.4k | { |
1028 | 29.4k | if ( ( nIndex & ENHMETA_STOCK_OBJECT ) != 0 ) |
1029 | 183 | return; |
1030 | | |
1031 | 29.2k | nIndex &= 0xffff; // safety check: do not allow index to be > 65535 |
1032 | 29.2k | if ( pObject ) |
1033 | 29.2k | { |
1034 | 29.2k | const auto pLineStyle = dynamic_cast<WinMtfLineStyle*>(pObject.get()); |
1035 | 29.2k | const auto pFontStyle = dynamic_cast<WinMtfFontStyle*>(pObject.get()); |
1036 | 29.2k | if ( pFontStyle ) |
1037 | 5.77k | { |
1038 | 5.77k | if (pFontStyle->aFont.GetFontHeight() == 0) |
1039 | 1.08k | pFontStyle->aFont.SetFontHeight(423); |
1040 | 5.77k | ImplMap(pFontStyle->aFont); |
1041 | 5.77k | } |
1042 | 23.4k | else if ( pLineStyle ) |
1043 | 5.47k | { |
1044 | 5.47k | Size aSize(pLineStyle->aLineInfo.GetWidth(), 0); |
1045 | 5.47k | pLineStyle->aLineInfo.SetWidth( ImplMap(aSize).Width() ); |
1046 | | |
1047 | 5.47k | if ( pLineStyle->aLineInfo.GetStyle() == LineStyle::Dash ) |
1048 | 1.53k | { |
1049 | 1.53k | aSize.AdjustWidth(1 ); |
1050 | 1.53k | tools::Long nDashLen, nDotLen = ImplMap( aSize ).Width(); |
1051 | 1.53k | const bool bFail = o3tl::checked_multiply<tools::Long>(nDotLen, 3, nDashLen); |
1052 | 1.53k | if (!bFail) |
1053 | 1.43k | { |
1054 | 1.43k | pLineStyle->aLineInfo.SetDistance( nDotLen ); |
1055 | 1.43k | pLineStyle->aLineInfo.SetDotLen( nDotLen ); |
1056 | 1.43k | pLineStyle->aLineInfo.SetDashLen( nDotLen * 3 ); |
1057 | 1.43k | } |
1058 | 93 | else |
1059 | 93 | { |
1060 | 93 | SAL_WARN("emfio", "DotLen too long: " << nDotLen); |
1061 | 93 | } |
1062 | 1.53k | } |
1063 | 5.47k | } |
1064 | 29.2k | } |
1065 | 29.2k | if ( nIndex >= mvGDIObj.size() ) |
1066 | 4.22k | ImplResizeObjectArry( nIndex + 16 ); |
1067 | | |
1068 | 29.2k | if ( !pObject && nIndex < mnLowestFreeGDIObj ) |
1069 | 0 | mnLowestFreeGDIObj = nIndex; |
1070 | | |
1071 | 29.2k | mvGDIObj[ nIndex ] = std::move(pObject); |
1072 | 29.2k | } |
1073 | | |
1074 | | void MtfTools::CreateObject() |
1075 | 3.90k | { |
1076 | 3.90k | CreateObject(std::make_unique<GDIObj>()); |
1077 | 3.90k | } |
1078 | | |
1079 | | void MtfTools::DeleteObject( sal_uInt32 nIndex ) |
1080 | 19.6k | { |
1081 | 19.6k | if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 ) |
1082 | 19.6k | { |
1083 | 19.6k | if ( nIndex < mvGDIObj.size() ) |
1084 | 18.9k | { |
1085 | 18.9k | mvGDIObj[ nIndex ].reset(); |
1086 | 18.9k | if ( nIndex < mnLowestFreeGDIObj ) |
1087 | 8.66k | mnLowestFreeGDIObj = nIndex; |
1088 | 18.9k | } |
1089 | 19.6k | } |
1090 | 19.6k | } |
1091 | | |
1092 | | void MtfTools::IntersectClipRect( const tools::Rectangle& rRect ) |
1093 | 11.9k | { |
1094 | 11.9k | if (comphelper::IsFuzzing()) |
1095 | 11.9k | return; |
1096 | 0 | mbClipNeedsUpdate=true; |
1097 | 0 | if ((rRect.Left()-rRect.Right()==0) && (rRect.Top()-rRect.Bottom()==0)) |
1098 | 0 | { |
1099 | 0 | return; // empty rectangles cause trouble |
1100 | 0 | } |
1101 | 0 | tools::Polygon aPoly( rRect ); |
1102 | 0 | const tools::PolyPolygon aPolyPolyRect( ImplMap( aPoly ) ); |
1103 | 0 | maClipPath.intersectClip( aPolyPolyRect.getB2DPolyPolygon() ); |
1104 | 0 | } |
1105 | | |
1106 | | void MtfTools::ExcludeClipRect( const tools::Rectangle& rRect ) |
1107 | 549 | { |
1108 | 549 | if (comphelper::IsFuzzing()) |
1109 | 549 | return; |
1110 | 0 | mbClipNeedsUpdate=true; |
1111 | 0 | tools::Polygon aPoly( rRect ); |
1112 | 0 | const tools::PolyPolygon aPolyPolyRect( ImplMap( aPoly ) ); |
1113 | 0 | maClipPath.excludeClip( aPolyPolyRect.getB2DPolyPolygon() ); |
1114 | 0 | } |
1115 | | |
1116 | | void MtfTools::MoveClipRegion( const Size& rSize ) |
1117 | 161 | { |
1118 | 161 | if (comphelper::IsFuzzing()) |
1119 | 161 | return; |
1120 | 0 | mbClipNeedsUpdate=true; |
1121 | 0 | maClipPath.moveClipRegion( ImplMap( rSize ) ); |
1122 | 0 | } |
1123 | | |
1124 | | void MtfTools::SetClipPath( const tools::PolyPolygon& rPolyPolygon, RegionMode eClippingMode, bool bIsMapped ) |
1125 | 808 | { |
1126 | 808 | if (comphelper::IsFuzzing()) |
1127 | 808 | return; |
1128 | 0 | mbClipNeedsUpdate = true; |
1129 | 0 | tools::PolyPolygon aPolyPolygon(rPolyPolygon); |
1130 | |
|
1131 | 0 | if (!bIsMapped) |
1132 | 0 | { |
1133 | 0 | if (!mbIsMapDevSet && (meMapMode == MappingMode::MM_ISOTROPIC || meMapMode == MappingMode::MM_ANISOTROPIC)) |
1134 | 0 | aPolyPolygon = ImplScale(aPolyPolygon); |
1135 | 0 | else |
1136 | 0 | aPolyPolygon = ImplMap(aPolyPolygon); |
1137 | 0 | } |
1138 | 0 | maClipPath.setClipPath(aPolyPolygon.getB2DPolyPolygon(), eClippingMode); |
1139 | 0 | } |
1140 | | |
1141 | | void MtfTools::SetDefaultClipPath() |
1142 | 339 | { |
1143 | 339 | mbClipNeedsUpdate = true; |
1144 | 339 | maClipPath.setDefaultClipPath(); |
1145 | 339 | } |
1146 | | |
1147 | | MtfTools::MtfTools( GDIMetaFile& rGDIMetaFile, SvStream& rStreamWMF) |
1148 | 22.6k | : mnLatestTextAlign(90), |
1149 | 22.6k | mnTextAlign(TextAlignmentMode::TA_LEFT | TextAlignmentMode::TA_TOP | TextAlignmentMode::TA_NOUPDATECP), |
1150 | 22.6k | maLatestBkColor(ColorTransparency, 0x12345678), |
1151 | 22.6k | maBkColor(COL_WHITE), |
1152 | 22.6k | mnLatestTextLayoutMode(vcl::text::ComplexTextLayoutFlags::Default), |
1153 | 22.6k | mnTextLayoutMode(vcl::text::ComplexTextLayoutFlags::Default), |
1154 | 22.6k | mnLatestBkMode(BackgroundMode::NONE), |
1155 | 22.6k | mnBkMode(BackgroundMode::OPAQUE), |
1156 | 22.6k | meLatestRasterOp(RasterOp::Invert), |
1157 | 22.6k | meRasterOp(RasterOp::OverPaint), |
1158 | 22.6k | mnLowestFreeGDIObj(0), |
1159 | | mnRop(), |
1160 | 22.6k | meGfxMode(GraphicsMode::GM_COMPATIBLE), |
1161 | 22.6k | meMapMode(MappingMode::MM_TEXT), |
1162 | 22.6k | mnDevOrgX(0), |
1163 | 22.6k | mnDevOrgY(0), |
1164 | 22.6k | mnDevWidth(1), |
1165 | 22.6k | mnDevHeight(1), |
1166 | 22.6k | mnWinOrgX(0), |
1167 | 22.6k | mnWinOrgY(0), |
1168 | 22.6k | mnWinExtX(1), |
1169 | 22.6k | mnWinExtY(1), |
1170 | 22.6k | mnPixX(100), |
1171 | 22.6k | mnPixY(100), |
1172 | 22.6k | mnMillX(1), |
1173 | 22.6k | mnMillY(1), |
1174 | 22.6k | mpGDIMetaFile(&rGDIMetaFile), |
1175 | 22.6k | mpInputStream(&rStreamWMF), |
1176 | 22.6k | mnStartPos(0), |
1177 | 22.6k | mnEndPos(0), |
1178 | 22.6k | mbNopMode(false), |
1179 | 22.6k | mbClockWiseArcDirection(false), |
1180 | 22.6k | mbFillStyleSelected(false), |
1181 | 22.6k | mnPolyFillMode(1), // ALTERNATE |
1182 | 22.6k | mbClipNeedsUpdate(true), |
1183 | 22.6k | mbComplexClip(false), |
1184 | 22.6k | mbIsMapWinSet(false), |
1185 | 22.6k | mbIsMapDevSet(false) |
1186 | 22.6k | { |
1187 | 22.6k | SvLockBytes *pLB = mpInputStream->GetLockBytes(); |
1188 | | |
1189 | 22.6k | if (pLB) |
1190 | 22.5k | { |
1191 | 22.5k | pLB->SetSynchronMode(); |
1192 | 22.5k | } |
1193 | | |
1194 | 22.6k | mnStartPos = mpInputStream->Tell(); |
1195 | 22.6k | SetDevOrg(Point()); |
1196 | | |
1197 | 22.6k | mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::CLIPREGION ) ); // The original clipregion has to be on top |
1198 | | // of the stack so it can always be restored |
1199 | | // this is necessary to be able to support |
1200 | | // SetClipRgn( NULL ) and similar ClipRgn actions (SJ) |
1201 | | |
1202 | 22.6k | maFont.SetFamilyName( u"Arial"_ustr ); // sj: #i57205#, we do have some scaling problems if using |
1203 | 22.6k | maFont.SetCharSet( RTL_TEXTENCODING_MS_1252 ); // the default font then most times a x11 font is used, we |
1204 | 22.6k | maFont.SetFontHeight( 423 ); // will prevent this defining a font |
1205 | | |
1206 | 22.6k | maLatestLineStyle.aLineColor = Color( 0x12, 0x34, 0x56 ); |
1207 | 22.6k | maLatestFillStyle.aFillColor = Color( 0x12, 0x34, 0x56 ); |
1208 | | |
1209 | 22.6k | mnRop = WMFRasterOp::Black; |
1210 | 22.6k | meRasterOp = RasterOp::OverPaint; |
1211 | 22.6k | mpGDIMetaFile->AddAction( new MetaRasterOpAction( RasterOp::OverPaint ) ); |
1212 | 22.6k | } |
1213 | | |
1214 | | MtfTools::~MtfTools() |
1215 | 22.6k | { |
1216 | 22.6k | mpGDIMetaFile->AddAction( new MetaPopAction() ); |
1217 | 22.6k | mpGDIMetaFile->SetPrefMapMode(MapMode(MapUnit::Map100thMM)); |
1218 | 22.6k | if ( mrclFrame.IsEmpty() ) |
1219 | 11.6k | mpGDIMetaFile->SetPrefSize( Size( mnDevWidth, mnDevHeight ) ); |
1220 | 11.0k | else |
1221 | 11.0k | mpGDIMetaFile->SetPrefSize( mrclFrame.GetSize() ); |
1222 | 22.6k | } |
1223 | | |
1224 | | void MtfTools::UpdateClipRegion() |
1225 | 735k | { |
1226 | 735k | if (!mbClipNeedsUpdate) |
1227 | 722k | return; |
1228 | | |
1229 | 12.9k | mbClipNeedsUpdate = false; |
1230 | 12.9k | mbComplexClip = false; |
1231 | | |
1232 | 12.9k | mpGDIMetaFile->AddAction( new MetaPopAction() ); // taking the original clipregion |
1233 | 12.9k | mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::CLIPREGION ) ); |
1234 | | |
1235 | | // skip for 'no clipping at all' case |
1236 | 12.9k | if( maClipPath.isEmpty() ) |
1237 | 12.9k | return; |
1238 | | |
1239 | 0 | const basegfx::B2DPolyPolygon& rClipPoly( maClipPath.getClipPath() ); |
1240 | |
|
1241 | 0 | mbComplexClip = rClipPoly.count() > 1 |
1242 | 0 | || !basegfx::utils::isRectangle(rClipPoly); |
1243 | | |
1244 | | // This makes cases like tdf#45820 work in reasonable time. |
1245 | 0 | if (mbComplexClip) |
1246 | 0 | { |
1247 | 0 | mpGDIMetaFile->AddAction( |
1248 | 0 | new MetaISectRegionClipRegionAction( |
1249 | 0 | vcl::Region(rClipPoly))); |
1250 | 0 | mbComplexClip = false; |
1251 | 0 | } |
1252 | 0 | else |
1253 | 0 | { |
1254 | 0 | mpGDIMetaFile->AddAction( |
1255 | 0 | new MetaISectRectClipRegionAction( |
1256 | 0 | vcl::unotools::rectangleFromB2DRectangle( |
1257 | 0 | rClipPoly.getB2DRange()))); |
1258 | 0 | } |
1259 | 0 | } |
1260 | | |
1261 | | bool MtfTools::ImplEmitLineHatch(const tools::Polygon& rPoly) |
1262 | 457k | { |
1263 | 457k | if (!maLineStyle.bHasHatch || maLineStyle.aLineInfo.GetWidth() <= 0) |
1264 | 438k | return false; |
1265 | | |
1266 | 18.3k | basegfx::B2DPolyPolygon aArea = basegfx::utils::createAreaGeometry( |
1267 | 18.3k | rPoly.getB2DPolygon(), |
1268 | 18.3k | maLineStyle.aLineInfo.GetWidth() / 2.0, |
1269 | 18.3k | maLineStyle.aLineInfo.GetLineJoin(), |
1270 | 18.3k | maLineStyle.aLineInfo.GetLineCap()); |
1271 | 18.3k | tools::PolyPolygon aPP(aArea); |
1272 | | |
1273 | | // Fill the stroke area with background color (opaque) or transparent |
1274 | 18.3k | mpGDIMetaFile->AddAction(new MetaPushAction(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR)); |
1275 | 18.3k | mpGDIMetaFile->AddAction(new MetaLineColorAction(Color(), false)); |
1276 | 18.3k | if (mnBkMode == BackgroundMode::OPAQUE) |
1277 | 18.1k | mpGDIMetaFile->AddAction(new MetaFillColorAction(maBkColor, true)); |
1278 | 274 | else |
1279 | 274 | mpGDIMetaFile->AddAction(new MetaFillColorAction(Color(), false)); |
1280 | 18.3k | mpGDIMetaFile->AddAction(new MetaPolyPolygonAction(aPP)); |
1281 | 18.3k | mpGDIMetaFile->AddAction(new MetaPopAction()); |
1282 | | |
1283 | | // Draw the hatch pattern on top |
1284 | 18.3k | mpGDIMetaFile->AddAction(new MetaHatchAction(aPP, maLineStyle.aHatch)); |
1285 | 18.3k | return true; |
1286 | 457k | } |
1287 | | |
1288 | | void MtfTools::ImplEmitPatternFill(const tools::PolyPolygon& rPolyPoly) |
1289 | 6.03k | { |
1290 | 6.03k | const tools::Rectangle aBound = rPolyPoly.GetBoundRect(); |
1291 | 6.03k | if (aBound.IsEmpty() || maLatestFillStyle.aBmp.IsEmpty()) |
1292 | 3.76k | return; |
1293 | | |
1294 | 2.26k | Wallpaper aWallpaper(maLatestFillStyle.aBmp); |
1295 | 2.26k | aWallpaper.SetStyle(WallpaperStyle::Tile); |
1296 | | |
1297 | | // Clip the tiled wallpaper to the polypoly so non-rectangular fills |
1298 | | // (and edge-tile bleed at the bounds) stay inside the shape. |
1299 | 2.26k | mpGDIMetaFile->AddAction(new MetaPushAction(vcl::PushFlags::CLIPREGION)); |
1300 | 2.26k | mpGDIMetaFile->AddAction(new MetaISectRegionClipRegionAction(vcl::Region(rPolyPoly))); |
1301 | 2.26k | mpGDIMetaFile->AddAction(new MetaWallpaperAction(aBound, aWallpaper)); |
1302 | 2.26k | mpGDIMetaFile->AddAction(new MetaPopAction()); |
1303 | 2.26k | } |
1304 | | |
1305 | | void MtfTools::ImplSetNonPersistentLineColorTransparenz() |
1306 | 62.2k | { |
1307 | 62.2k | WinMtfLineStyle aTransparentLine( COL_TRANSPARENT, true ); |
1308 | 62.2k | if ( ! ( maLatestLineStyle == aTransparentLine ) ) |
1309 | 20.0k | { |
1310 | 20.0k | maLatestLineStyle = aTransparentLine; |
1311 | 20.0k | mpGDIMetaFile->AddAction( new MetaLineColorAction( aTransparentLine.aLineColor, !aTransparentLine.bTransparent ) ); |
1312 | 20.0k | } |
1313 | 62.2k | } |
1314 | | |
1315 | | void MtfTools::ResetInternalState() |
1316 | 515 | { |
1317 | 515 | maLatestBkColor = Color(0x12, 0x34, 0x56); |
1318 | 515 | mnLatestBkMode = BackgroundMode::NONE; |
1319 | | |
1320 | 515 | maLatestFillStyle.aFillColor = Color(0x12, 0x34, 0x56); |
1321 | 515 | maLatestLineStyle.aLineColor = Color(0x12, 0x34, 0x56); |
1322 | | |
1323 | 515 | meLatestRasterOp = RasterOp::Invert; |
1324 | | |
1325 | 515 | mnLatestTextAlign = 90; |
1326 | 515 | mnLatestTextLayoutMode = vcl::text::ComplexTextLayoutFlags::Default; |
1327 | 515 | } |
1328 | | |
1329 | | void MtfTools::UpdateLineStyle() |
1330 | 74.3k | { |
1331 | 74.3k | if (!( maLatestLineStyle == maLineStyle ) ) |
1332 | 29.9k | { |
1333 | 29.9k | maLatestLineStyle = maLineStyle; |
1334 | 29.9k | mpGDIMetaFile->AddAction( new MetaLineColorAction( maLineStyle.aLineColor, !maLineStyle.bTransparent ) ); |
1335 | 29.9k | } |
1336 | 74.3k | } |
1337 | | |
1338 | | void MtfTools::UpdateFillStyle() |
1339 | 128k | { |
1340 | 128k | if ( !mbFillStyleSelected ) // SJ: #i57205# taking care of bkcolor if no brush is selected |
1341 | 101k | maFillStyle = WinMtfFillStyle( maBkColor, mnBkMode == BackgroundMode::Transparent ); |
1342 | 128k | if (!( maLatestFillStyle == maFillStyle ) ) |
1343 | 17.2k | { |
1344 | 17.2k | maLatestFillStyle = maFillStyle; |
1345 | 17.2k | if (maFillStyle.aType == WinMtfFillStyleType::Solid) |
1346 | 14.9k | mpGDIMetaFile->AddAction( new MetaFillColorAction( maFillStyle.aFillColor, !maFillStyle.bTransparent ) ); |
1347 | 2.28k | else if (maFillStyle.aType == WinMtfFillStyleType::Hatch) |
1348 | 239 | { |
1349 | 239 | if (mnBkMode == BackgroundMode::OPAQUE) |
1350 | 199 | mpGDIMetaFile->AddAction( new MetaFillColorAction( maBkColor, true ) ); |
1351 | 40 | else |
1352 | 40 | mpGDIMetaFile->AddAction( new MetaFillColorAction( Color(), false ) ); |
1353 | 239 | } |
1354 | 17.2k | } |
1355 | 128k | } |
1356 | | |
1357 | | WMFRasterOp MtfTools::SetRasterOp( WMFRasterOp nRasterOp ) |
1358 | 152k | { |
1359 | 152k | WMFRasterOp nRetROP = mnRop; |
1360 | 152k | if ( nRasterOp != mnRop ) |
1361 | 147k | { |
1362 | 147k | mnRop = nRasterOp; |
1363 | | |
1364 | 147k | if ( mbNopMode && ( nRasterOp != WMFRasterOp::Nop ) ) |
1365 | 1.50k | { // changing modes from WMFRasterOp::Nop so set pen and brush |
1366 | 1.50k | maFillStyle = maNopFillStyle; |
1367 | 1.50k | maLineStyle = maNopLineStyle; |
1368 | 1.50k | mbNopMode = false; |
1369 | 1.50k | } |
1370 | 147k | switch( nRasterOp ) |
1371 | 147k | { |
1372 | 13.0k | case WMFRasterOp::Not: |
1373 | 13.0k | meRasterOp = RasterOp::Invert; |
1374 | 13.0k | break; |
1375 | | |
1376 | 14.1k | case WMFRasterOp::XorPen: |
1377 | 14.1k | meRasterOp = RasterOp::Xor; |
1378 | 14.1k | break; |
1379 | | |
1380 | 1.52k | case WMFRasterOp::Nop: |
1381 | 1.52k | { |
1382 | 1.52k | meRasterOp = RasterOp::OverPaint; |
1383 | 1.52k | if( !mbNopMode ) |
1384 | 1.52k | { |
1385 | 1.52k | maNopFillStyle = maFillStyle; |
1386 | 1.52k | maNopLineStyle = maLineStyle; |
1387 | 1.52k | maFillStyle = WinMtfFillStyle( COL_TRANSPARENT, true ); |
1388 | 1.52k | maLineStyle = WinMtfLineStyle( COL_TRANSPARENT, true ); |
1389 | 1.52k | mbNopMode = true; |
1390 | 1.52k | } |
1391 | 1.52k | } |
1392 | 1.52k | break; |
1393 | | |
1394 | 119k | default: |
1395 | 119k | meRasterOp = RasterOp::OverPaint; |
1396 | 119k | break; |
1397 | 147k | } |
1398 | 147k | } |
1399 | 152k | if ( nRetROP != nRasterOp ) |
1400 | 147k | mpGDIMetaFile->AddAction( new MetaRasterOpAction( meRasterOp ) ); |
1401 | 152k | return nRetROP; |
1402 | 152k | }; |
1403 | | |
1404 | | void MtfTools::StrokeAndFillPath( bool bStroke, bool bFill ) |
1405 | 14.1k | { |
1406 | 14.1k | if ( !maPathObj.Count() ) |
1407 | 6.93k | return; |
1408 | | |
1409 | 7.19k | UpdateClipRegion(); |
1410 | 7.19k | UpdateLineStyle(); |
1411 | 7.19k | UpdateFillStyle(); |
1412 | 7.19k | bool bWindingFill = (mnPolyFillMode == 2); |
1413 | 7.19k | if ( bFill ) |
1414 | 4.70k | { |
1415 | | // When using WINDING fill mode, we convert the geometry so it renders |
1416 | | // correctly with even-odd fill. We must also disable line color to avoid |
1417 | | // stroking internal edges of the converted fill geometry. |
1418 | 4.70k | if ( !bStroke || bWindingFill ) |
1419 | 4.12k | { |
1420 | 4.12k | mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::LINECOLOR ) ); |
1421 | 4.12k | mpGDIMetaFile->AddAction( new MetaLineColorAction( Color(), false ) ); |
1422 | 4.12k | } |
1423 | 4.70k | if (bWindingFill) |
1424 | 2.51k | { |
1425 | 2.51k | tools::PolyPolygon aFill = CreateWindingFillPolyPolygon(maPathObj); |
1426 | 2.51k | mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( aFill ) ); |
1427 | 2.51k | } |
1428 | 2.19k | else |
1429 | 2.19k | { |
1430 | 2.19k | if ( maPathObj.Count() == 1 ) |
1431 | 1.68k | mpGDIMetaFile->AddAction( new MetaPolygonAction( maPathObj.GetObject( 0 ) ) ); |
1432 | 504 | else |
1433 | 504 | mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( maPathObj ) ); |
1434 | 2.19k | } |
1435 | | |
1436 | 4.70k | if (maLatestFillStyle.aType == WinMtfFillStyleType::Hatch) |
1437 | 342 | mpGDIMetaFile->AddAction( new MetaHatchAction( maPathObj, maLatestFillStyle.aHatch ) ); |
1438 | | |
1439 | 4.70k | if ( !bStroke || bWindingFill ) |
1440 | 4.12k | mpGDIMetaFile->AddAction( new MetaPopAction() ); |
1441 | 4.70k | } |
1442 | | // tdf#142014 By default the stroke is made with hairline. If width is bigger, we need to use PolyLineAction |
1443 | 7.19k | if ( bStroke ) |
1444 | 4.17k | { |
1445 | | // bFill is drawing hairstyle line. So we need to draw it only when the width is different than 0 |
1446 | | // When using WINDING fill mode, line was disabled for the fill, so always draw stroke |
1447 | 4.17k | if ( !bFill || bWindingFill || maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) |
1448 | 3.76k | { |
1449 | 3.76k | sal_uInt16 i, nCount = maPathObj.Count(); |
1450 | 425k | for ( i = 0; i < nCount; i++ ) |
1451 | 421k | { |
1452 | 421k | if (!ImplEmitLineHatch( maPathObj[ i ] )) |
1453 | 404k | mpGDIMetaFile->AddAction( new MetaPolyLineAction( maPathObj[ i ], maLineStyle.aLineInfo ) ); |
1454 | 421k | } |
1455 | 3.76k | } |
1456 | 4.17k | } |
1457 | 7.19k | ClearPath(); |
1458 | 7.19k | } |
1459 | | |
1460 | | void MtfTools::DrawPixel( const Point& rSource, const Color& rColor ) |
1461 | 1.48k | { |
1462 | 1.48k | mpGDIMetaFile->AddAction( new MetaPixelAction( ImplMap( rSource), rColor ) ); |
1463 | 1.48k | } |
1464 | | |
1465 | | void MtfTools::MoveTo( const Point& rPoint, bool bRecordPath ) |
1466 | 14.6k | { |
1467 | 14.6k | Point aDest( ImplMap( rPoint ) ); |
1468 | 14.6k | if ( bRecordPath ) |
1469 | 10.0k | { |
1470 | | // fdo#57353 create new subpath for subsequent moves |
1471 | 10.0k | if ( maPathObj.Count() ) |
1472 | 7.92k | if ( maPathObj[ maPathObj.Count() - 1 ].GetSize() ) |
1473 | 4.75k | maPathObj.Insert( tools::Polygon() ); |
1474 | 10.0k | maPathObj.AddPoint( aDest ); |
1475 | 10.0k | } |
1476 | 14.6k | maActPos = aDest; |
1477 | 14.6k | } |
1478 | | |
1479 | | void MtfTools::LineTo( const Point& rPoint, bool bRecordPath ) |
1480 | 354k | { |
1481 | 354k | UpdateClipRegion(); |
1482 | 354k | Point aDest( ImplMap( rPoint ) ); |
1483 | 354k | if ( bRecordPath ) |
1484 | 344k | maPathObj.AddPoint( aDest ); |
1485 | 10.1k | else |
1486 | 10.1k | { |
1487 | 10.1k | UpdateLineStyle(); |
1488 | 10.1k | tools::Polygon aLinePoly(2); |
1489 | 10.1k | aLinePoly.SetPoint(maActPos, 0); |
1490 | 10.1k | aLinePoly.SetPoint(aDest, 1); |
1491 | 10.1k | if (!ImplEmitLineHatch(aLinePoly)) |
1492 | 9.77k | mpGDIMetaFile->AddAction( new MetaLineAction( maActPos, aDest, maLineStyle.aLineInfo ) ); |
1493 | 10.1k | } |
1494 | 354k | maActPos = aDest; |
1495 | 354k | } |
1496 | | |
1497 | | void MtfTools::DrawRectWithBGColor(const tools::Rectangle& rRect) |
1498 | 6.97k | { |
1499 | 6.97k | WinMtfFillStyle aFillStyleBackup = maFillStyle; |
1500 | 6.97k | bool bTransparentBackup = maLineStyle.bTransparent; |
1501 | 6.97k | BackgroundMode mnBkModeBackup = mnBkMode; |
1502 | | |
1503 | 6.97k | const tools::Polygon aPoly( rRect ); |
1504 | 6.97k | maLineStyle.bTransparent = true; |
1505 | 6.97k | maFillStyle = maBkColor; |
1506 | 6.97k | mnBkMode = BackgroundMode::OPAQUE; |
1507 | 6.97k | ImplSetNonPersistentLineColorTransparenz(); |
1508 | 6.97k | DrawPolygon(aPoly, false); |
1509 | 6.97k | mnBkMode = mnBkModeBackup; // The rectangle needs to be always drawned even if mode is transparent |
1510 | 6.97k | maFillStyle = std::move(aFillStyleBackup); |
1511 | 6.97k | maLineStyle.bTransparent = bTransparentBackup; |
1512 | 6.97k | } |
1513 | | |
1514 | | void MtfTools::DrawRect( const tools::Rectangle& rRect, bool bEdge ) |
1515 | 45.6k | { |
1516 | 45.6k | UpdateClipRegion(); |
1517 | 45.6k | UpdateFillStyle(); |
1518 | | |
1519 | 45.6k | if ( mbComplexClip ) |
1520 | 0 | { |
1521 | 0 | tools::Polygon aPoly( ImplMap( rRect ) ); |
1522 | 0 | tools::PolyPolygon aPolyPolyRect( aPoly ); |
1523 | 0 | tools::PolyPolygon aDest; |
1524 | 0 | tools::PolyPolygon(maClipPath.getClipPath()).GetIntersection( aPolyPolyRect, aDest ); |
1525 | 0 | ImplDrawClippedPolyPolygon( aDest ); |
1526 | 0 | } |
1527 | 45.6k | else |
1528 | 45.6k | { |
1529 | 45.6k | const bool bPattern = (maLatestFillStyle.aType == WinMtfFillStyleType::Pattern); |
1530 | 45.6k | if ( bEdge ) |
1531 | 1.51k | { |
1532 | 1.51k | if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) |
1533 | 957 | { |
1534 | 957 | ImplSetNonPersistentLineColorTransparenz(); |
1535 | 957 | if ( bPattern ) |
1536 | 18 | ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( ImplMap( rRect ) ) ) ); |
1537 | 939 | else |
1538 | 939 | mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) ); |
1539 | 957 | UpdateLineStyle(); |
1540 | 957 | tools::Polygon aEdgePoly( ImplMap( rRect ) ); |
1541 | 957 | if (!ImplEmitLineHatch( aEdgePoly )) |
1542 | 957 | mpGDIMetaFile->AddAction( new MetaPolyLineAction( aEdgePoly, maLineStyle.aLineInfo ) ); |
1543 | 957 | } |
1544 | 560 | else |
1545 | 560 | { |
1546 | 560 | UpdateLineStyle(); |
1547 | 560 | if ( bPattern ) |
1548 | 13 | ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( ImplMap( rRect ) ) ) ); |
1549 | 547 | else |
1550 | 547 | mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) ); |
1551 | 560 | } |
1552 | 1.51k | } |
1553 | 44.1k | else |
1554 | 44.1k | { |
1555 | 44.1k | ImplSetNonPersistentLineColorTransparenz(); |
1556 | 44.1k | if ( bPattern ) |
1557 | 872 | ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( ImplMap( rRect ) ) ) ); |
1558 | 43.2k | else |
1559 | 43.2k | mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) ); |
1560 | 44.1k | } |
1561 | | |
1562 | 45.6k | if (maLatestFillStyle.aType == WinMtfFillStyleType::Hatch) |
1563 | 2.17k | { |
1564 | 2.17k | tools::PolyPolygon aPolyPoly( tools::Polygon( ImplMap( rRect ) ) ); |
1565 | 2.17k | mpGDIMetaFile->AddAction( new MetaHatchAction( aPolyPoly, maLatestFillStyle.aHatch ) ); |
1566 | 2.17k | } |
1567 | 45.6k | } |
1568 | 45.6k | } |
1569 | | |
1570 | | void MtfTools::DrawRoundRect( const tools::Rectangle& rRect, const Size& rSize ) |
1571 | 2.31k | { |
1572 | 2.31k | UpdateClipRegion(); |
1573 | 2.31k | UpdateLineStyle(); |
1574 | 2.31k | UpdateFillStyle(); |
1575 | 2.31k | if (maLatestFillStyle.aType == WinMtfFillStyleType::Pattern) |
1576 | 373 | { |
1577 | 373 | tools::Polygon aRoundRectPoly( rRect, rSize.Width(), rSize.Height() ); |
1578 | 373 | ImplMap( aRoundRectPoly ); |
1579 | 373 | ImplEmitPatternFill( tools::PolyPolygon( aRoundRectPoly ) ); |
1580 | 373 | } |
1581 | 1.94k | else |
1582 | 1.94k | mpGDIMetaFile->AddAction( new MetaRoundRectAction( ImplMap( rRect ), std::abs( ImplMap( rSize ).Width() ), std::abs( ImplMap( rSize ).Height() ) ) ); |
1583 | | // tdf#142139 Wrong line width during WMF import |
1584 | 2.31k | if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) |
1585 | 1.84k | { |
1586 | 1.84k | tools::Polygon aRoundRectPoly( rRect, rSize.Width(), rSize.Height() ); |
1587 | 1.84k | ImplMap( aRoundRectPoly ); |
1588 | 1.84k | if (!ImplEmitLineHatch( aRoundRectPoly )) |
1589 | 1.84k | mpGDIMetaFile->AddAction( new MetaPolyLineAction( aRoundRectPoly, maLineStyle.aLineInfo ) ); |
1590 | 1.84k | } |
1591 | 2.31k | } |
1592 | | |
1593 | | void MtfTools::DrawEllipse( const tools::Rectangle& rRect ) |
1594 | 3.64k | { |
1595 | 3.64k | UpdateClipRegion(); |
1596 | 3.64k | UpdateFillStyle(); |
1597 | | |
1598 | 3.64k | const bool bPattern = (maLatestFillStyle.aType == WinMtfFillStyleType::Pattern); |
1599 | 3.64k | const Point aCenter( ImplMap( rRect.Center() ) ); |
1600 | 3.64k | const Size aRad( ImplMap( Size( rRect.GetWidth() / 2, rRect.GetHeight() / 2 ) ) ); |
1601 | | |
1602 | 3.64k | if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) |
1603 | 1.33k | { |
1604 | 1.33k | ImplSetNonPersistentLineColorTransparenz(); |
1605 | 1.33k | if ( bPattern ) |
1606 | 98 | ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( aCenter, aRad.Width(), aRad.Height() ) ) ); |
1607 | 1.23k | else |
1608 | 1.23k | mpGDIMetaFile->AddAction( new MetaEllipseAction( ImplMap( rRect ) ) ); |
1609 | 1.33k | UpdateLineStyle(); |
1610 | 1.33k | tools::Polygon aEllipsePoly( aCenter, aRad.Width(), aRad.Height() ); |
1611 | 1.33k | if (!ImplEmitLineHatch( aEllipsePoly )) |
1612 | 1.33k | mpGDIMetaFile->AddAction( new MetaPolyLineAction( aEllipsePoly, maLineStyle.aLineInfo ) ); |
1613 | 1.33k | } |
1614 | 2.31k | else |
1615 | 2.31k | { |
1616 | 2.31k | UpdateLineStyle(); |
1617 | 2.31k | if ( bPattern ) |
1618 | 44 | ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( aCenter, aRad.Width(), aRad.Height() ) ) ); |
1619 | 2.26k | else |
1620 | 2.26k | mpGDIMetaFile->AddAction( new MetaEllipseAction( ImplMap( rRect ) ) ); |
1621 | 2.31k | } |
1622 | 3.64k | } |
1623 | | |
1624 | | void MtfTools::DrawArc( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd, bool bTo ) |
1625 | 3.18k | { |
1626 | 3.18k | UpdateClipRegion(); |
1627 | 3.18k | UpdateLineStyle(); |
1628 | 3.18k | UpdateFillStyle(); |
1629 | | |
1630 | 3.18k | tools::Rectangle aRect( ImplMap( rRect ) ); |
1631 | 3.18k | Point aStart( ImplMap( rStart ) ); |
1632 | 3.18k | Point aEnd( ImplMap( rEnd ) ); |
1633 | | |
1634 | 3.18k | if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) |
1635 | 2.39k | { |
1636 | 2.39k | if ( aStart == aEnd ) |
1637 | 1.19k | { // SJ: #i53768# if start & end is identical, then we have to draw a full ellipse |
1638 | 1.19k | Point aCenter( aRect.Center() ); |
1639 | 1.19k | Size aRad( aRect.GetWidth() / 2, aRect.GetHeight() / 2 ); |
1640 | 1.19k | tools::Polygon aArcPoly( aCenter, aRad.Width(), aRad.Height() ); |
1641 | 1.19k | if (!ImplEmitLineHatch( aArcPoly )) |
1642 | 1.19k | mpGDIMetaFile->AddAction( new MetaPolyLineAction( aArcPoly, maLineStyle.aLineInfo ) ); |
1643 | 1.19k | } |
1644 | 1.20k | else |
1645 | 1.20k | { |
1646 | 1.20k | tools::Polygon aArcPoly( aRect, aStart, aEnd, PolyStyle::Arc ); |
1647 | 1.20k | if (!ImplEmitLineHatch( aArcPoly )) |
1648 | 1.20k | mpGDIMetaFile->AddAction( new MetaPolyLineAction( aArcPoly, maLineStyle.aLineInfo ) ); |
1649 | 1.20k | } |
1650 | 2.39k | } |
1651 | 788 | else |
1652 | 788 | mpGDIMetaFile->AddAction( new MetaArcAction( aRect, aStart, aEnd ) ); |
1653 | | |
1654 | 3.18k | if ( bTo ) |
1655 | 0 | maActPos = aEnd; |
1656 | 3.18k | } |
1657 | | |
1658 | | void MtfTools::DrawPie( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd ) |
1659 | 1.36k | { |
1660 | 1.36k | UpdateClipRegion(); |
1661 | 1.36k | UpdateFillStyle(); |
1662 | | |
1663 | 1.36k | tools::Rectangle aRect( ImplMap( rRect ) ); |
1664 | 1.36k | Point aStart( ImplMap( rStart ) ); |
1665 | 1.36k | Point aEnd( ImplMap( rEnd ) ); |
1666 | 1.36k | const bool bPattern = (maLatestFillStyle.aType == WinMtfFillStyleType::Pattern); |
1667 | | |
1668 | 1.36k | if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) |
1669 | 942 | { |
1670 | 942 | ImplSetNonPersistentLineColorTransparenz(); |
1671 | 942 | if ( bPattern ) |
1672 | 36 | ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Pie ) ) ); |
1673 | 906 | else |
1674 | 906 | mpGDIMetaFile->AddAction( new MetaPieAction( aRect, aStart, aEnd ) ); |
1675 | 942 | UpdateLineStyle(); |
1676 | 942 | { |
1677 | 942 | tools::Polygon aPiePoly( aRect, aStart, aEnd, PolyStyle::Pie ); |
1678 | 942 | if (!ImplEmitLineHatch( aPiePoly )) |
1679 | 942 | mpGDIMetaFile->AddAction( new MetaPolyLineAction( aPiePoly, maLineStyle.aLineInfo ) ); |
1680 | 942 | } |
1681 | 942 | } |
1682 | 426 | else |
1683 | 426 | { |
1684 | 426 | UpdateLineStyle(); |
1685 | 426 | if ( bPattern ) |
1686 | 33 | ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Pie ) ) ); |
1687 | 393 | else |
1688 | 393 | mpGDIMetaFile->AddAction( new MetaPieAction( aRect, aStart, aEnd ) ); |
1689 | 426 | } |
1690 | 1.36k | } |
1691 | | |
1692 | | void MtfTools::DrawChord( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd ) |
1693 | 839 | { |
1694 | 839 | UpdateClipRegion(); |
1695 | 839 | UpdateFillStyle(); |
1696 | | |
1697 | 839 | tools::Rectangle aRect( ImplMap( rRect ) ); |
1698 | 839 | Point aStart( ImplMap( rStart ) ); |
1699 | 839 | Point aEnd( ImplMap( rEnd ) ); |
1700 | 839 | const bool bPattern = (maLatestFillStyle.aType == WinMtfFillStyleType::Pattern); |
1701 | | |
1702 | 839 | if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) |
1703 | 384 | { |
1704 | 384 | ImplSetNonPersistentLineColorTransparenz(); |
1705 | 384 | if ( bPattern ) |
1706 | 8 | ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Chord ) ) ); |
1707 | 376 | else |
1708 | 376 | mpGDIMetaFile->AddAction( new MetaChordAction( aRect, aStart, aEnd ) ); |
1709 | 384 | UpdateLineStyle(); |
1710 | 384 | { |
1711 | 384 | tools::Polygon aChordPoly( aRect, aStart, aEnd, PolyStyle::Chord ); |
1712 | 384 | if (!ImplEmitLineHatch( aChordPoly )) |
1713 | 384 | mpGDIMetaFile->AddAction( new MetaPolyLineAction( aChordPoly, maLineStyle.aLineInfo ) ); |
1714 | 384 | } |
1715 | 384 | } |
1716 | 455 | else |
1717 | 455 | { |
1718 | 455 | UpdateLineStyle(); |
1719 | 455 | if ( bPattern ) |
1720 | 9 | ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Chord ) ) ); |
1721 | 446 | else |
1722 | 446 | mpGDIMetaFile->AddAction( new MetaChordAction( aRect, aStart, aEnd ) ); |
1723 | 455 | } |
1724 | 839 | } |
1725 | | |
1726 | | void MtfTools::DrawPolygon( tools::Polygon rPolygon, bool bRecordPath ) |
1727 | 36.7k | { |
1728 | 36.7k | UpdateClipRegion(); |
1729 | 36.7k | ImplMap( rPolygon ); |
1730 | 36.7k | if ( bRecordPath ) |
1731 | 8.43k | maPathObj.AddPolygon( rPolygon ); |
1732 | 28.3k | else |
1733 | 28.3k | { |
1734 | 28.3k | UpdateFillStyle(); |
1735 | | |
1736 | 28.3k | if ( mbComplexClip ) |
1737 | 0 | { |
1738 | 0 | tools::PolyPolygon aPolyPoly( rPolygon ); |
1739 | 0 | auto tmp = maClipPath.getClip(); |
1740 | 0 | tmp.intersectPolyPolygon(aPolyPoly.getB2DPolyPolygon()); |
1741 | 0 | tools::PolyPolygon aDest(tmp.getClipPoly()); |
1742 | 0 | ImplDrawClippedPolyPolygon( aDest ); |
1743 | 0 | } |
1744 | 28.3k | else |
1745 | 28.3k | { |
1746 | 28.3k | if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) |
1747 | 4.68k | { |
1748 | 4.68k | sal_uInt16 nCount = rPolygon.GetSize(); |
1749 | 4.68k | if ( nCount ) |
1750 | 3.06k | { |
1751 | 3.06k | if ( rPolygon[ nCount - 1 ] != rPolygon[ 0 ] ) |
1752 | 309 | { |
1753 | 309 | Point aPoint( rPolygon[ 0 ] ); |
1754 | 309 | rPolygon.Insert( nCount, aPoint ); |
1755 | 309 | } |
1756 | 3.06k | } |
1757 | 4.68k | ImplSetNonPersistentLineColorTransparenz(); |
1758 | 4.68k | if (mnPolyFillMode == 2) // WINDING |
1759 | 686 | { |
1760 | 686 | tools::PolyPolygon aFill = CreateWindingFillPolyPolygon(tools::PolyPolygon(rPolygon)); |
1761 | 686 | mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( aFill ) ); |
1762 | 686 | } |
1763 | 3.99k | else |
1764 | 3.99k | { |
1765 | 3.99k | mpGDIMetaFile->AddAction( new MetaPolygonAction( rPolygon ) ); |
1766 | 3.99k | } |
1767 | 4.68k | if (maLatestFillStyle.aType == WinMtfFillStyleType::Hatch) |
1768 | 510 | mpGDIMetaFile->AddAction( new MetaHatchAction( tools::PolyPolygon( rPolygon ), maLatestFillStyle.aHatch ) ); |
1769 | 4.68k | UpdateLineStyle(); |
1770 | 4.68k | if (!ImplEmitLineHatch( rPolygon )) |
1771 | 4.60k | mpGDIMetaFile->AddAction( new MetaPolyLineAction( rPolygon, maLineStyle.aLineInfo ) ); |
1772 | 4.68k | } |
1773 | 23.6k | else |
1774 | 23.6k | { |
1775 | 23.6k | UpdateLineStyle(); |
1776 | | |
1777 | 23.6k | if (maLatestFillStyle.aType == WinMtfFillStyleType::Hatch) |
1778 | 238 | { |
1779 | 238 | mpGDIMetaFile->AddAction( new MetaPolygonAction( rPolygon ) ); |
1780 | 238 | mpGDIMetaFile->AddAction( new MetaHatchAction( tools::PolyPolygon( std::move(rPolygon) ), maLatestFillStyle.aHatch ) ); |
1781 | 238 | } |
1782 | 23.4k | else if (maLatestFillStyle.aType != WinMtfFillStyleType::Pattern) |
1783 | 20.0k | { |
1784 | 20.0k | if (mnPolyFillMode == 2) // WINDING |
1785 | 2.84k | { |
1786 | 2.84k | tools::PolyPolygon aFill = CreateWindingFillPolyPolygon(tools::PolyPolygon(rPolygon)); |
1787 | 2.84k | ImplSetNonPersistentLineColorTransparenz(); |
1788 | 2.84k | mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( aFill ) ); |
1789 | 2.84k | UpdateLineStyle(); |
1790 | | // Stroke with original polygon, closing it for the polyline |
1791 | 2.84k | sal_uInt16 nCount = rPolygon.GetSize(); |
1792 | 2.84k | if (nCount && rPolygon[nCount - 1] != rPolygon[0]) |
1793 | 471 | { |
1794 | 471 | Point aPoint(rPolygon[0]); |
1795 | 471 | rPolygon.Insert(nCount, aPoint); |
1796 | 471 | } |
1797 | 2.84k | if (!ImplEmitLineHatch( rPolygon )) |
1798 | 2.84k | mpGDIMetaFile->AddAction(new MetaPolyLineAction(rPolygon, maLineStyle.aLineInfo)); |
1799 | 2.84k | } |
1800 | 17.2k | else |
1801 | 17.2k | mpGDIMetaFile->AddAction( new MetaPolygonAction( std::move(rPolygon) ) ); |
1802 | 20.0k | } |
1803 | 3.32k | else |
1804 | 3.32k | { |
1805 | 3.32k | ImplEmitPatternFill( tools::PolyPolygon( std::move(rPolygon) ) ); |
1806 | 3.32k | } |
1807 | | |
1808 | 23.6k | } |
1809 | 28.3k | } |
1810 | 28.3k | } |
1811 | 36.7k | } |
1812 | | |
1813 | | void MtfTools::DrawPolyPolygon( tools::PolyPolygon& rPolyPolygon, bool bRecordPath ) |
1814 | 5.98k | { |
1815 | 5.98k | UpdateClipRegion(); |
1816 | | |
1817 | 5.98k | ImplMap( rPolyPolygon ); |
1818 | | |
1819 | 5.98k | if ( bRecordPath ) |
1820 | 218 | maPathObj.AddPolyPolygon( rPolyPolygon ); |
1821 | 5.76k | else |
1822 | 5.76k | { |
1823 | 5.76k | UpdateFillStyle(); |
1824 | | |
1825 | 5.76k | if ( mbComplexClip ) |
1826 | 0 | { |
1827 | 0 | tools::PolyPolygon aDest; |
1828 | 0 | tools::PolyPolygon(maClipPath.getClipPath()).GetIntersection( rPolyPolygon, aDest ); |
1829 | 0 | ImplDrawClippedPolyPolygon( aDest ); |
1830 | 0 | } |
1831 | 5.76k | else |
1832 | 5.76k | { |
1833 | 5.76k | UpdateLineStyle(); |
1834 | 5.76k | if (mnPolyFillMode == 2) // WINDING |
1835 | 1.16k | { |
1836 | | // Convert geometry so even-odd rendering matches winding fill. |
1837 | | // Disable line to avoid stroking internal resolved edges. |
1838 | 1.16k | tools::PolyPolygon aFill = CreateWindingFillPolyPolygon(rPolyPolygon); |
1839 | 1.16k | mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::LINECOLOR ) ); |
1840 | 1.16k | mpGDIMetaFile->AddAction( new MetaLineColorAction( Color(), false ) ); |
1841 | 1.16k | mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( aFill ) ); |
1842 | 1.16k | mpGDIMetaFile->AddAction( new MetaPopAction() ); |
1843 | 1.16k | if (maLatestFillStyle.aType == WinMtfFillStyleType::Hatch) |
1844 | 20 | mpGDIMetaFile->AddAction( new MetaHatchAction( rPolyPolygon, maLatestFillStyle.aHatch ) ); |
1845 | | // Stroke with original geometry |
1846 | 3.26k | for (sal_uInt16 nPoly = 0; nPoly < rPolyPolygon.Count(); ++nPoly) |
1847 | 2.10k | { |
1848 | 2.10k | if (!ImplEmitLineHatch( rPolyPolygon[nPoly] )) |
1849 | 2.10k | mpGDIMetaFile->AddAction(new MetaPolyLineAction(rPolyPolygon[nPoly], maLineStyle.aLineInfo)); |
1850 | 2.10k | } |
1851 | 1.16k | } |
1852 | 4.60k | else |
1853 | 4.60k | { |
1854 | 4.60k | if (maLatestFillStyle.aType == WinMtfFillStyleType::Pattern) |
1855 | 1.20k | ImplEmitPatternFill( rPolyPolygon ); |
1856 | 3.39k | else |
1857 | 3.39k | mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPolygon ) ); |
1858 | 4.60k | if (maLatestFillStyle.aType == WinMtfFillStyleType::Hatch) |
1859 | 36 | mpGDIMetaFile->AddAction( new MetaHatchAction( rPolyPolygon, maLatestFillStyle.aHatch ) ); |
1860 | 4.60k | if (maLineStyle.aLineInfo.GetWidth() > 0 || maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash) |
1861 | 1.05k | { |
1862 | 2.18k | for (sal_uInt16 nPoly = 0; nPoly < rPolyPolygon.Count(); ++nPoly) |
1863 | 1.13k | { |
1864 | 1.13k | if (!ImplEmitLineHatch( rPolyPolygon[nPoly] )) |
1865 | 862 | mpGDIMetaFile->AddAction(new MetaPolyLineAction(rPolyPolygon[nPoly], maLineStyle.aLineInfo)); |
1866 | 1.13k | } |
1867 | 1.05k | } |
1868 | 4.60k | } |
1869 | 5.76k | } |
1870 | 5.76k | } |
1871 | 5.98k | } |
1872 | | |
1873 | | void MtfTools::DrawPolyLine( tools::Polygon rPolygon, bool bTo, bool bRecordPath ) |
1874 | 18.5k | { |
1875 | 18.5k | UpdateClipRegion(); |
1876 | | |
1877 | 18.5k | sal_uInt16 nPoints = rPolygon.GetSize(); |
1878 | 18.5k | if (nPoints < 1) |
1879 | 9.99k | return; |
1880 | | |
1881 | 8.56k | ImplMap( rPolygon ); |
1882 | 8.56k | if ( bTo ) |
1883 | 4.36k | { |
1884 | 4.36k | rPolygon[ 0 ] = maActPos; |
1885 | 4.36k | maActPos = rPolygon[ rPolygon.GetSize() - 1 ]; |
1886 | 4.36k | } |
1887 | 8.56k | if ( bRecordPath ) |
1888 | 1.69k | maPathObj.AddPolyLine( rPolygon ); |
1889 | 6.87k | else |
1890 | 6.87k | { |
1891 | 6.87k | UpdateLineStyle(); |
1892 | 6.87k | if (!ImplEmitLineHatch( rPolygon )) |
1893 | 6.25k | mpGDIMetaFile->AddAction( new MetaPolyLineAction( std::move(rPolygon), maLineStyle.aLineInfo ) ); |
1894 | 6.87k | } |
1895 | 8.56k | } |
1896 | | |
1897 | | static bool AllowDim(tools::Long nDim) |
1898 | 18.5k | { |
1899 | 18.5k | static bool bFuzzing = comphelper::IsFuzzing(); |
1900 | 18.5k | if (bFuzzing) |
1901 | 18.5k | { |
1902 | 18.5k | if (nDim > 0x20000000 || nDim < -0x20000000) |
1903 | 3.19k | { |
1904 | 3.19k | SAL_WARN("vcl", "skipping huge dimension: " << nDim); |
1905 | 3.19k | return false; |
1906 | 3.19k | } |
1907 | 18.5k | } |
1908 | 15.3k | return true; |
1909 | 18.5k | } |
1910 | | |
1911 | | static bool AllowPoint(const Point& rPoint) |
1912 | 10.5k | { |
1913 | 10.5k | return AllowDim(rPoint.X()) && AllowDim(rPoint.Y()); |
1914 | 10.5k | } |
1915 | | |
1916 | | void MtfTools::DrawPolyBezier( tools::Polygon rPolygon, bool bTo, bool bRecordPath ) |
1917 | 99.8k | { |
1918 | 99.8k | sal_uInt16 nPoints = rPolygon.GetSize(); |
1919 | 99.8k | if ( ( nPoints < 4 ) || ( ( ( nPoints - 4 ) % 3 ) != 0 ) ) |
1920 | 5.31k | { |
1921 | 5.31k | SAL_WARN("emfio", |
1922 | 5.31k | "EMF file error: Number of Bezier points is not set of three"); |
1923 | 5.31k | return; |
1924 | 5.31k | } |
1925 | 94.5k | UpdateClipRegion(); |
1926 | 94.5k | ImplMap( rPolygon ); |
1927 | 94.5k | if ( bTo ) |
1928 | 94.4k | { |
1929 | 94.4k | rPolygon[ 0 ] = maActPos; |
1930 | 94.4k | maActPos = rPolygon[ nPoints - 1 ]; |
1931 | 94.4k | } |
1932 | 94.5k | sal_uInt16 i; |
1933 | 191k | for ( i = 0; ( i + 2 ) < nPoints; ) |
1934 | 97.3k | { |
1935 | 97.3k | rPolygon.SetFlags( i++, PolyFlags::Normal ); |
1936 | 97.3k | rPolygon.SetFlags( i++, PolyFlags::Control ); |
1937 | 97.3k | rPolygon.SetFlags( i++, PolyFlags::Control ); |
1938 | 97.3k | } |
1939 | 94.5k | if ( bRecordPath ) |
1940 | 94.2k | maPathObj.AddPolyLine( rPolygon ); |
1941 | 280 | else |
1942 | 280 | { |
1943 | 280 | UpdateLineStyle(); |
1944 | 280 | if (!ImplEmitLineHatch( rPolygon )) |
1945 | 237 | mpGDIMetaFile->AddAction( new MetaPolyLineAction( std::move(rPolygon), maLineStyle.aLineInfo ) ); |
1946 | 280 | } |
1947 | 94.5k | } |
1948 | | |
1949 | | void MtfTools::DrawText(Point& rPosition, OUString const & rText, KernArray* pDXArry, tools::Long* pDYArry, const float fXScale, const float fYScale, bool bRecordPath, GraphicsMode nGfxMode) |
1950 | 46.7k | { |
1951 | 46.7k | UpdateClipRegion(); |
1952 | 46.7k | rPosition = ImplMap( rPosition ); |
1953 | 46.7k | GraphicsMode nOldGfxMode = GetGfxMode(); |
1954 | 46.7k | SetGfxMode( GraphicsMode::GM_COMPATIBLE ); |
1955 | | |
1956 | 46.7k | if (pDXArry) |
1957 | 12.4k | { |
1958 | 12.4k | sal_Int64 nSumX = 0, nSumY = 0; |
1959 | 54.9k | for (sal_Int32 i = 0; i < rText.getLength(); i++ ) |
1960 | 42.4k | { |
1961 | 42.4k | nSumX += (*pDXArry)[i]; |
1962 | | |
1963 | | // #i121382# Map DXArray using WorldTransform |
1964 | 42.4k | const Size aSizeX(ImplMap(Size(nSumX, 0))); |
1965 | 42.4k | const basegfx::B2DVector aVectorX(aSizeX.Width(), aSizeX.Height()); |
1966 | 42.4k | (*pDXArry)[i] = aVectorX.getLength() * (nSumX >= 0 ? 1 : -1); |
1967 | | |
1968 | 42.4k | if (pDYArry) |
1969 | 10.5k | { |
1970 | 10.5k | nSumY += pDYArry[i]; |
1971 | | |
1972 | 10.5k | const Size aSizeY(ImplMap(Size(0, nSumY))); |
1973 | 10.5k | const basegfx::B2DVector aVectorY(aSizeY.Width(), aSizeY.Height()); |
1974 | | // Reverse Y |
1975 | 10.5k | pDYArry[i] = basegfx::fround<tools::Long>(aVectorY.getLength()); |
1976 | 10.5k | pDYArry[i] *= (nSumY >= 0 ? -1 : 1); |
1977 | 10.5k | } |
1978 | 42.4k | } |
1979 | 12.4k | } |
1980 | 46.7k | if ( mnLatestTextLayoutMode != mnTextLayoutMode ) |
1981 | 5.68k | { |
1982 | 5.68k | mnLatestTextLayoutMode = mnTextLayoutMode; |
1983 | 5.68k | mpGDIMetaFile->AddAction( new MetaLayoutModeAction( mnTextLayoutMode ) ); |
1984 | 5.68k | } |
1985 | 46.7k | SetGfxMode(nGfxMode); |
1986 | 46.7k | TextAlign eTextAlign; |
1987 | 46.7k | if (mnTextAlign & TA_BASELINE) |
1988 | 21.9k | eTextAlign = ALIGN_BASELINE; |
1989 | 24.7k | else if (mnTextAlign & TA_BOTTOM) |
1990 | 695 | eTextAlign = ALIGN_BOTTOM; |
1991 | 24.0k | else |
1992 | 24.0k | eTextAlign = ALIGN_TOP; |
1993 | 46.7k | bool bChangeFont = false; |
1994 | 46.7k | if ( mnLatestTextAlign != mnTextAlign ) |
1995 | 5.36k | { |
1996 | 5.36k | bChangeFont = true; |
1997 | | |
1998 | 5.36k | if ((mnLatestTextAlign & TA_RTLREADING) != (mnTextAlign & TA_RTLREADING)) |
1999 | 535 | { |
2000 | 535 | auto nFlags = vcl::text::ComplexTextLayoutFlags::Default; |
2001 | 535 | if (mnTextAlign & TA_RTLREADING) |
2002 | 302 | { |
2003 | 302 | nFlags = vcl::text::ComplexTextLayoutFlags::BiDiRtl |
2004 | 302 | | vcl::text::ComplexTextLayoutFlags::TextOriginLeft; |
2005 | 302 | } |
2006 | | |
2007 | 535 | mpGDIMetaFile->AddAction(new MetaLayoutModeAction(nFlags)); |
2008 | 535 | } |
2009 | | |
2010 | 5.36k | mnLatestTextAlign = mnTextAlign; |
2011 | 5.36k | mpGDIMetaFile->AddAction( new MetaTextAlignAction( eTextAlign ) ); |
2012 | 5.36k | } |
2013 | 46.7k | if ( maLatestTextColor != maTextColor ) |
2014 | 590 | { |
2015 | 590 | bChangeFont = true; |
2016 | 590 | maLatestTextColor = maTextColor; |
2017 | 590 | mpGDIMetaFile->AddAction( new MetaTextColorAction( maTextColor ) ); |
2018 | 590 | } |
2019 | 46.7k | bool bChangeFillColor = false; |
2020 | 46.7k | if ( maLatestBkColor != maBkColor ) |
2021 | 5.29k | { |
2022 | 5.29k | bChangeFillColor = true; |
2023 | 5.29k | maLatestBkColor = maBkColor; |
2024 | 5.29k | } |
2025 | 46.7k | if ( mnLatestBkMode != mnBkMode ) |
2026 | 7.54k | { |
2027 | 7.54k | bChangeFillColor = true; |
2028 | 7.54k | mnLatestBkMode = mnBkMode; |
2029 | 7.54k | } |
2030 | 46.7k | if ( bChangeFillColor ) |
2031 | 8.20k | { |
2032 | 8.20k | bChangeFont = true; |
2033 | 8.20k | mpGDIMetaFile->AddAction( new MetaTextFillColorAction( maFont.GetFillColor(), !maFont.IsTransparent() ) ); |
2034 | 8.20k | } |
2035 | | // Create a local copy of the current font to apply transient modifications |
2036 | | // (such as color, alignment, and custom scaling) before recording it to the metafile. |
2037 | 46.7k | vcl::Font aTmp( maFont ); |
2038 | 46.7k | aTmp.SetColor( maTextColor ); |
2039 | | |
2040 | 46.7k | if( mnBkMode == BackgroundMode::Transparent ) |
2041 | 22.2k | { |
2042 | 22.2k | aTmp.SetFillColor( COL_TRANSPARENT ); |
2043 | 22.2k | aTmp.SetTransparent( true ); |
2044 | 22.2k | } |
2045 | 24.5k | else |
2046 | 24.5k | { |
2047 | 24.5k | aTmp.SetFillColor( maBkColor ); |
2048 | 24.5k | aTmp.SetTransparent( false ); |
2049 | 24.5k | } |
2050 | | |
2051 | 46.7k | aTmp.SetAlignment( eTextAlign ); |
2052 | | |
2053 | 46.7k | if ( nGfxMode == GraphicsMode::GM_ADVANCED ) |
2054 | 637 | { |
2055 | | // check whether there is a font rotation applied via transformation |
2056 | 637 | Point aP1( ImplMap( Point() ) ); |
2057 | 637 | Point aP2( ImplMap( Point( 0, 100 ) ) ); |
2058 | 637 | aP2.setX(o3tl::saturating_sub(aP2.X(), aP1.X())); |
2059 | 637 | aP2.setY(o3tl::saturating_sub(aP2.Y(), aP1.Y())); |
2060 | 637 | double fX = aP2.X(); |
2061 | 637 | double fY = aP2.Y(); |
2062 | 637 | if ( fX ) |
2063 | 448 | { |
2064 | 448 | double fOrientation = basegfx::rad2deg(acos(fX / std::hypot(fX, fY))); |
2065 | 448 | if ( fY > 0 ) |
2066 | 28 | fOrientation = 360 - fOrientation; |
2067 | 448 | fOrientation += 90; |
2068 | 448 | fOrientation *= 10; |
2069 | 448 | aTmp.SetOrientation( aTmp.GetOrientation() + Degree10( static_cast<sal_Int16>(fOrientation) ) ); |
2070 | 448 | } |
2071 | 637 | } |
2072 | 46.1k | else if (nGfxMode == GraphicsMode::GM_COMPATIBLE) |
2073 | 35.9k | { |
2074 | 35.9k | if (fXScale != 0.0) |
2075 | 35.4k | { |
2076 | | // As we changing only font width, we are skipping if scales have the same values |
2077 | 35.4k | const bool bNeedsWidthScale = (std::fabs(fYScale) != std::fabs(fXScale)); |
2078 | 35.4k | if (bNeedsWidthScale) |
2079 | 1.37k | { |
2080 | 1.37k | Size aFontSize = aTmp.GetFontSize(); |
2081 | 1.37k | const float fTestWidthScale = std::fabs(fYScale / fXScale); |
2082 | | |
2083 | | // If Width is 0, the font is scaled proportionally based on Height. |
2084 | 1.37k | if (aFontSize.Width() == 0) |
2085 | 1.26k | aFontSize.setWidth(basegfx::fround<tools::Long>(aFontSize.Height() * fTestWidthScale)); |
2086 | 112 | else |
2087 | 112 | aFontSize.setWidth(basegfx::fround<tools::Long>(aFontSize.Width() * fTestWidthScale)); |
2088 | 1.37k | aTmp.SetFontSize(aFontSize); |
2089 | 1.37k | bChangeFont = true; |
2090 | 1.37k | } |
2091 | 35.4k | } |
2092 | | |
2093 | 35.9k | if ((fYScale < 0.0) && (fXScale < 0.0)) |
2094 | 78 | { |
2095 | | // Both scales negative = 180 degree rotation. vcl::Font handles this perfectly. |
2096 | 78 | aTmp.SetOrientation(aTmp.GetOrientation() + Degree10(1800)); |
2097 | 78 | } |
2098 | 35.8k | else if ((fYScale < 0.0) || (fXScale < 0.0)) |
2099 | 48 | { |
2100 | | // Single-axis mirroring. |
2101 | | // In GM_COMPATIBLE, text glyphs are NOT physically mirrored. |
2102 | | // However, the flipped coordinate system reverses the rotation direction. |
2103 | | // Inverting the angle (360 degrees - current angle) fixes the text direction. |
2104 | 48 | aTmp.SetOrientation(Degree10(3600) - aTmp.GetOrientation()); |
2105 | | |
2106 | | // TODO: True single-axis glyph mirroring would require a MapMode transform here |
2107 | 48 | } |
2108 | 35.9k | } |
2109 | | |
2110 | 46.7k | if( mnTextAlign & ( TA_UPDATECP | TA_RIGHT_CENTER ) ) |
2111 | 2.18k | { |
2112 | | // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading |
2113 | 2.18k | SolarMutexGuard aGuard; |
2114 | 2.18k | ScopedVclPtrInstance< VirtualDevice > pVDev; |
2115 | 2.18k | sal_Int32 nTextWidth; |
2116 | 2.18k | Point aActPosDelta; |
2117 | 2.18k | pVDev->SetMapMode( MapMode( MapUnit::Map100thMM ) ); |
2118 | 2.18k | pVDev->SetFont( maFont ); |
2119 | 2.18k | const sal_uInt32 nLen = pDXArry ? rText.getLength() : 0; |
2120 | 2.18k | if (nLen) |
2121 | 643 | { |
2122 | 643 | nTextWidth = pVDev->GetTextWidth( OUString(rText[ nLen - 1 ]) ); |
2123 | 643 | if( nLen > 1 ) |
2124 | 150 | nTextWidth += (*pDXArry)[ nLen - 2 ]; |
2125 | | // tdf#39894: We should consider the distance to next character cell origin |
2126 | 643 | aActPosDelta.setX( (*pDXArry)[ nLen - 1 ] ); |
2127 | 643 | if ( pDYArry ) |
2128 | 138 | { |
2129 | 138 | aActPosDelta.setY( pDYArry[ nLen - 1 ] ); |
2130 | 138 | } |
2131 | 643 | } |
2132 | 1.54k | else |
2133 | 1.54k | { |
2134 | 1.54k | nTextWidth = pVDev->GetTextWidth( rText ); |
2135 | 1.54k | aActPosDelta.setX( nTextWidth ); |
2136 | 1.54k | } |
2137 | | |
2138 | 2.18k | if( mnTextAlign & TA_UPDATECP ) |
2139 | 1.17k | rPosition = maActPos; |
2140 | | |
2141 | 2.18k | if (mnTextAlign & TA_RIGHT_CENTER) |
2142 | 1.08k | { |
2143 | 1.08k | Point aDisplacement(((mnTextAlign & TA_RIGHT_CENTER) == TA_CENTER) ? nTextWidth >> 1: nTextWidth, 0); |
2144 | 1.08k | Point().RotateAround(aDisplacement, maFont.GetOrientation()); |
2145 | 1.08k | rPosition -= aDisplacement; |
2146 | 1.08k | } |
2147 | | |
2148 | 2.18k | if( mnTextAlign & TA_UPDATECP ) |
2149 | 1.17k | { |
2150 | 1.17k | Point().RotateAround(aActPosDelta, maFont.GetOrientation()); |
2151 | 1.17k | maActPos = rPosition + aActPosDelta; |
2152 | 1.17k | } |
2153 | 2.18k | } |
2154 | | |
2155 | 46.7k | if(bChangeFont || (maLatestFont != aTmp)) |
2156 | 21.4k | { |
2157 | 21.4k | maLatestFont = aTmp; |
2158 | 21.4k | rtl::Reference<MetaFontAction> aNewMetaFontAction(new MetaFontAction(aTmp)); |
2159 | | |
2160 | | // tdf#127471 end evtl active MetaFontAction scale corrector detector/collector |
2161 | 21.4k | maScaledFontHelper.endCurrentMetaFontAction(); |
2162 | | |
2163 | | // !bRecordPath: else no MetaTextArrayAction will be created |
2164 | | // nullptr != pDXArry: detection only possible when text size is given |
2165 | | // rText.getLength(): no useful check without text |
2166 | 21.4k | if(!bRecordPath && nullptr != pDXArry && 0 != rText.getLength()) |
2167 | 9.10k | { |
2168 | 9.10k | maScaledFontHelper.newCurrentMetaFontAction(aNewMetaFontAction); |
2169 | 9.10k | } |
2170 | | |
2171 | 21.4k | mpGDIMetaFile->AddAction( aNewMetaFontAction ); |
2172 | 21.4k | mpGDIMetaFile->AddAction( new MetaTextAlignAction( aTmp.GetAlignment() ) ); |
2173 | 21.4k | mpGDIMetaFile->AddAction( new MetaTextColorAction( aTmp.GetColor() ) ); |
2174 | 21.4k | mpGDIMetaFile->AddAction( new MetaTextFillColorAction( aTmp.GetFillColor(), !aTmp.IsTransparent() ) ); |
2175 | 21.4k | } |
2176 | | |
2177 | 46.7k | if ( bRecordPath ) |
2178 | 8.53k | { |
2179 | | // TODO |
2180 | 8.53k | } |
2181 | 38.2k | else |
2182 | 38.2k | { |
2183 | 38.2k | if ( pDXArry && pDYArry ) |
2184 | 4.11k | { |
2185 | 14.6k | for (sal_Int32 i = 0; i < rText.getLength(); ++i) |
2186 | 10.5k | { |
2187 | 10.5k | Point aCharDisplacement( i ? (*pDXArry)[i-1] : 0, i ? pDYArry[i-1] : 0 ); |
2188 | 10.5k | if (AllowPoint(aCharDisplacement)) |
2189 | 7.32k | { |
2190 | 7.32k | Point().RotateAround(aCharDisplacement, maFont.GetOrientation()); |
2191 | 7.32k | mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition + aCharDisplacement, OUString( rText[i] ), KernArraySpan(), {}, 0, 1 ) ); |
2192 | 7.32k | } |
2193 | 10.5k | } |
2194 | 4.11k | } |
2195 | 34.0k | else |
2196 | 34.0k | { |
2197 | | /* because text without dx array is badly scaled, we |
2198 | | will create such an array if necessary */ |
2199 | 34.0k | KernArraySpan pDX; |
2200 | 34.0k | KernArray aMyDXArray; |
2201 | 34.0k | if (pDXArry) |
2202 | 8.36k | { |
2203 | 8.36k | pDX = *pDXArry; |
2204 | | // only useful when we have an imported DXArray |
2205 | 8.36k | if(!rText.isEmpty()) |
2206 | 8.36k | { |
2207 | 8.36k | maScaledFontHelper.evaluateAlternativeFontScale( |
2208 | 8.36k | rText, |
2209 | 8.36k | (*pDXArry)[rText.getLength() - 1] // extract imported TextLength |
2210 | 8.36k | ); |
2211 | 8.36k | } |
2212 | 8.36k | } |
2213 | 25.7k | else |
2214 | 25.7k | { |
2215 | | // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading |
2216 | 25.7k | SolarMutexGuard aGuard; |
2217 | 25.7k | ScopedVclPtrInstance< VirtualDevice > pVDev; |
2218 | 25.7k | pVDev->SetMapMode(MapMode(MapUnit::Map100thMM)); |
2219 | 25.7k | pVDev->SetFont( maLatestFont ); |
2220 | 25.7k | pVDev->GetTextArray( rText, &aMyDXArray, 0, rText.getLength()); |
2221 | 25.7k | pDX = aMyDXArray; |
2222 | 25.7k | } |
2223 | 34.0k | mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition, rText, pDX, {}, 0, rText.getLength() ) ); |
2224 | 34.0k | } |
2225 | 38.2k | } |
2226 | 46.7k | SetGfxMode( nOldGfxMode ); |
2227 | 46.7k | } |
2228 | | |
2229 | | bool MtfTools::DrawGlyphs( Point& rPosition, const std::vector<sal_uInt16>& rGlyphIds, |
2230 | | KernArray* pDXArry, tools::Long* pDYArry, |
2231 | | const float fXScale, const float fYScale, |
2232 | | bool bRecordPath, GraphicsMode nGfxMode ) |
2233 | 3.17k | { |
2234 | 3.17k | if (rGlyphIds.empty()) |
2235 | 906 | return true; |
2236 | | |
2237 | | // Check that the required font is actually available — glyph indices |
2238 | | // are font-specific and meaningless if a different font is substituted. |
2239 | 2.26k | { |
2240 | 2.26k | SolarMutexGuard aGuard; |
2241 | 2.26k | ScopedVclPtrInstance< VirtualDevice > pVDev; |
2242 | 2.26k | if ( !pVDev->IsFontAvailable( maFont.GetFamilyName() ) ) |
2243 | 1.17k | { |
2244 | 1.17k | SAL_WARN("emfio", "ETO_GLYPH_INDEX: font '" |
2245 | 1.17k | << maFont.GetFamilyName() |
2246 | 1.17k | << "' not available, falling back to text path, which will be a " |
2247 | 1.17k | "garbled nonsense"); |
2248 | 1.17k | return false; |
2249 | 1.17k | } |
2250 | 2.26k | } |
2251 | | |
2252 | 1.09k | UpdateClipRegion(); |
2253 | 1.09k | rPosition = ImplMap( rPosition ); |
2254 | 1.09k | GraphicsMode nOldGfxMode = GetGfxMode(); |
2255 | 1.09k | SetGfxMode( GraphicsMode::GM_COMPATIBLE ); |
2256 | | |
2257 | | // Map DX/DY arrays (same as DrawText) |
2258 | 1.09k | sal_Int32 nLen = static_cast<sal_Int32>(rGlyphIds.size()); |
2259 | 1.09k | if (pDXArry) |
2260 | 104 | { |
2261 | 104 | sal_Int64 nSumX = 0, nSumY = 0; |
2262 | 1.94k | for (sal_Int32 i = 0; i < nLen; i++) |
2263 | 1.84k | { |
2264 | 1.84k | nSumX += (*pDXArry)[i]; |
2265 | 1.84k | const Size aSizeX(ImplMap(Size(nSumX, 0))); |
2266 | 1.84k | const basegfx::B2DVector aVectorX(aSizeX.Width(), aSizeX.Height()); |
2267 | 1.84k | (*pDXArry)[i] = aVectorX.getLength() * (nSumX >= 0 ? 1 : -1); |
2268 | | |
2269 | 1.84k | if (pDYArry) |
2270 | 177 | { |
2271 | 177 | nSumY += pDYArry[i]; |
2272 | 177 | const Size aSizeY(ImplMap(Size(0, nSumY))); |
2273 | 177 | const basegfx::B2DVector aVectorY(aSizeY.Width(), aSizeY.Height()); |
2274 | 177 | pDYArry[i] = basegfx::fround<tools::Long>(aVectorY.getLength()); |
2275 | 177 | pDYArry[i] *= (nSumY >= 0 ? -1 : 1); |
2276 | 177 | } |
2277 | 1.84k | } |
2278 | 104 | } |
2279 | | |
2280 | | // Text layout mode (same as DrawText) |
2281 | 1.09k | if ( mnLatestTextLayoutMode != mnTextLayoutMode ) |
2282 | 120 | { |
2283 | 120 | mnLatestTextLayoutMode = mnTextLayoutMode; |
2284 | 120 | mpGDIMetaFile->AddAction( new MetaLayoutModeAction( mnTextLayoutMode ) ); |
2285 | 120 | } |
2286 | 1.09k | SetGfxMode(nGfxMode); |
2287 | | |
2288 | | // Text alignment (same as DrawText) |
2289 | 1.09k | TextAlign eTextAlign; |
2290 | 1.09k | if (mnTextAlign & TA_BASELINE) |
2291 | 995 | eTextAlign = ALIGN_BASELINE; |
2292 | 97 | else if (mnTextAlign & TA_BOTTOM) |
2293 | 7 | eTextAlign = ALIGN_BOTTOM; |
2294 | 90 | else |
2295 | 90 | eTextAlign = ALIGN_TOP; |
2296 | | |
2297 | 1.09k | bool bChangeFont = false; |
2298 | 1.09k | if ( mnLatestTextAlign != mnTextAlign ) |
2299 | 390 | { |
2300 | 390 | bChangeFont = true; |
2301 | 390 | mnLatestTextAlign = mnTextAlign; |
2302 | 390 | mpGDIMetaFile->AddAction( new MetaTextAlignAction( eTextAlign ) ); |
2303 | 390 | } |
2304 | 1.09k | if ( maLatestTextColor != maTextColor ) |
2305 | 404 | { |
2306 | 404 | bChangeFont = true; |
2307 | 404 | maLatestTextColor = maTextColor; |
2308 | 404 | mpGDIMetaFile->AddAction( new MetaTextColorAction( maTextColor ) ); |
2309 | 404 | } |
2310 | 1.09k | bool bChangeFillColor = false; |
2311 | 1.09k | if ( maLatestBkColor != maBkColor ) |
2312 | 356 | { |
2313 | 356 | bChangeFillColor = true; |
2314 | 356 | maLatestBkColor = maBkColor; |
2315 | 356 | } |
2316 | 1.09k | if ( mnLatestBkMode != mnBkMode ) |
2317 | 398 | { |
2318 | 398 | bChangeFillColor = true; |
2319 | 398 | mnLatestBkMode = mnBkMode; |
2320 | 398 | } |
2321 | 1.09k | if ( bChangeFillColor ) |
2322 | 398 | { |
2323 | 398 | bChangeFont = true; |
2324 | 398 | mpGDIMetaFile->AddAction( new MetaTextFillColorAction( maFont.GetFillColor(), !maFont.IsTransparent() ) ); |
2325 | 398 | } |
2326 | 1.09k | vcl::Font aTmp( maFont ); |
2327 | 1.09k | aTmp.SetColor( maTextColor ); |
2328 | | |
2329 | 1.09k | if( mnBkMode == BackgroundMode::Transparent ) |
2330 | 140 | { |
2331 | 140 | aTmp.SetFillColor( COL_TRANSPARENT ); |
2332 | 140 | aTmp.SetTransparent( true ); |
2333 | 140 | } |
2334 | 952 | else |
2335 | 952 | { |
2336 | 952 | aTmp.SetFillColor( maBkColor ); |
2337 | 952 | aTmp.SetTransparent( false ); |
2338 | 952 | } |
2339 | | |
2340 | 1.09k | aTmp.SetAlignment( eTextAlign ); |
2341 | | |
2342 | 1.09k | if ( nGfxMode == GraphicsMode::GM_ADVANCED ) |
2343 | 1 | { |
2344 | 1 | Point aP1( ImplMap( Point() ) ); |
2345 | 1 | Point aP2( ImplMap( Point( 0, 100 ) ) ); |
2346 | 1 | aP2.setX(o3tl::saturating_sub(aP2.X(), aP1.X())); |
2347 | 1 | aP2.setY(o3tl::saturating_sub(aP2.Y(), aP1.Y())); |
2348 | 1 | double fX = aP2.X(); |
2349 | 1 | double fY = aP2.Y(); |
2350 | 1 | if ( fX ) |
2351 | 0 | { |
2352 | 0 | double fOrientation = basegfx::rad2deg(acos(fX / std::hypot(fX, fY))); |
2353 | 0 | if ( fY > 0 ) |
2354 | 0 | fOrientation = 360 - fOrientation; |
2355 | 0 | fOrientation += 90; |
2356 | 0 | fOrientation *= 10; |
2357 | 0 | aTmp.SetOrientation( aTmp.GetOrientation() + Degree10( static_cast<sal_Int16>(fOrientation) ) ); |
2358 | 0 | } |
2359 | 1 | } |
2360 | 1.09k | else if (nGfxMode == GraphicsMode::GM_COMPATIBLE) |
2361 | 3 | { |
2362 | 3 | if (fXScale != 0.0) |
2363 | 2 | { |
2364 | 2 | const bool bNeedsWidthScale = (std::fabs(fYScale) != std::fabs(fXScale)); |
2365 | 2 | if (bNeedsWidthScale) |
2366 | 2 | { |
2367 | 2 | Size aFontSize = aTmp.GetFontSize(); |
2368 | 2 | const float fTestWidthScale = std::fabs(fYScale / fXScale); |
2369 | | |
2370 | 2 | if (aFontSize.Width() == 0) |
2371 | 2 | aFontSize.setWidth(basegfx::fround<tools::Long>(aFontSize.Height() * fTestWidthScale)); |
2372 | 0 | else |
2373 | 0 | aFontSize.setWidth(basegfx::fround<tools::Long>(aFontSize.Width() * fTestWidthScale)); |
2374 | 2 | aTmp.SetFontSize(aFontSize); |
2375 | 2 | bChangeFont = true; |
2376 | 2 | } |
2377 | 2 | } |
2378 | | |
2379 | 3 | if ((fYScale < 0.0) && (fXScale < 0.0)) |
2380 | 0 | { |
2381 | 0 | aTmp.SetOrientation(aTmp.GetOrientation() + Degree10(1800)); |
2382 | 0 | } |
2383 | 3 | else if ((fYScale < 0.0) || (fXScale < 0.0)) |
2384 | 0 | { |
2385 | 0 | aTmp.SetOrientation(Degree10(3600) - aTmp.GetOrientation()); |
2386 | 0 | } |
2387 | 3 | } |
2388 | | |
2389 | 1.09k | if( mnTextAlign & ( TA_UPDATECP | TA_RIGHT_CENTER ) ) |
2390 | 1.01k | { |
2391 | 1.01k | SolarMutexGuard aGuard; |
2392 | 1.01k | ScopedVclPtrInstance< VirtualDevice > pVDev; |
2393 | 1.01k | sal_Int32 nTextWidth; |
2394 | 1.01k | Point aActPosDelta; |
2395 | 1.01k | pVDev->SetMapMode( MapMode( MapUnit::Map100thMM ) ); |
2396 | 1.01k | pVDev->SetFont( maFont ); |
2397 | 1.01k | if (nLen && pDXArry) |
2398 | 53 | { |
2399 | | // Approximate width of last glyph |
2400 | 53 | nTextWidth = pVDev->GetTextWidth( u"M"_ustr ); |
2401 | 53 | if( nLen > 1 ) |
2402 | 51 | nTextWidth += (*pDXArry)[ nLen - 2 ]; |
2403 | 53 | aActPosDelta.setX( (*pDXArry)[ nLen - 1 ] ); |
2404 | 53 | if ( pDYArry ) |
2405 | 20 | aActPosDelta.setY( pDYArry[ nLen - 1 ] ); |
2406 | 53 | } |
2407 | 963 | else |
2408 | 963 | { |
2409 | 963 | nTextWidth = 0; |
2410 | 963 | aActPosDelta.setX( nTextWidth ); |
2411 | 963 | } |
2412 | | |
2413 | 1.01k | if( mnTextAlign & TA_UPDATECP ) |
2414 | 1.00k | rPosition = maActPos; |
2415 | | |
2416 | 1.01k | if (mnTextAlign & TA_RIGHT_CENTER) |
2417 | 20 | { |
2418 | 20 | Point aDisplacement(((mnTextAlign & TA_RIGHT_CENTER) == TA_CENTER) ? nTextWidth >> 1: nTextWidth, 0); |
2419 | 20 | Point().RotateAround(aDisplacement, maFont.GetOrientation()); |
2420 | 20 | rPosition -= aDisplacement; |
2421 | 20 | } |
2422 | | |
2423 | 1.01k | if( mnTextAlign & TA_UPDATECP ) |
2424 | 1.00k | { |
2425 | 1.00k | Point().RotateAround(aActPosDelta, maFont.GetOrientation()); |
2426 | 1.00k | maActPos = rPosition + aActPosDelta; |
2427 | 1.00k | } |
2428 | 1.01k | } |
2429 | | |
2430 | 1.09k | if(bChangeFont || (maLatestFont != aTmp)) |
2431 | 769 | { |
2432 | 769 | maLatestFont = aTmp; |
2433 | 769 | rtl::Reference<MetaFontAction> aNewMetaFontAction(new MetaFontAction(aTmp)); |
2434 | 769 | maScaledFontHelper.endCurrentMetaFontAction(); |
2435 | 769 | mpGDIMetaFile->AddAction( aNewMetaFontAction ); |
2436 | 769 | mpGDIMetaFile->AddAction( new MetaTextAlignAction( aTmp.GetAlignment() ) ); |
2437 | 769 | mpGDIMetaFile->AddAction( new MetaTextColorAction( aTmp.GetColor() ) ); |
2438 | 769 | mpGDIMetaFile->AddAction( new MetaTextFillColorAction( aTmp.GetFillColor(), !aTmp.IsTransparent() ) ); |
2439 | 769 | } |
2440 | | |
2441 | | // Convert glyph indices to polypolygon outlines |
2442 | 1.09k | if ( !bRecordPath ) |
2443 | 1.07k | { |
2444 | 1.07k | SolarMutexGuard aGuard; |
2445 | 1.07k | ScopedVclPtrInstance< VirtualDevice > pVDev; |
2446 | 1.07k | pVDev->SetMapMode( MapMode( MapUnit::Map100thMM ) ); |
2447 | 1.07k | pVDev->SetFont( maLatestFont ); |
2448 | | |
2449 | | // GetGlyphOutlines() returns outlines in the OutputDevice's |
2450 | | // logical coordinates; since the VirtualDevice is set to |
2451 | | // Map100thMM above, outlines come back in 100th-mm — no |
2452 | | // additional scaling is needed. |
2453 | 1.07k | std::vector<sal_uInt32> aGlyphIds32(rGlyphIds.begin(), rGlyphIds.end()); |
2454 | 1.07k | basegfx::B2DPolyPolygonVector aOutlines; |
2455 | 1.07k | pVDev->GetGlyphOutlines(aGlyphIds32.data(), nLen, aOutlines); |
2456 | | |
2457 | | // Emit each glyph as a separate filled PolyPolygon. We must NOT |
2458 | | // combine all glyphs into a single PolyPolygon because the even-odd |
2459 | | // fill rule would cancel out overlapping regions where Arabic |
2460 | | // connected glyphs overlap, leaving visible white gaps. |
2461 | 1.07k | bool bHasGlyphs = false; |
2462 | 239k | for (sal_Int32 i = 0; i < nLen && i < static_cast<sal_Int32>(aOutlines.size()); ++i) |
2463 | 238k | { |
2464 | 238k | basegfx::B2DPolyPolygon aGlyph = aOutlines[i]; |
2465 | 238k | if (aGlyph.count() == 0) |
2466 | 173k | continue; |
2467 | | |
2468 | | // Position glyph: glyph 0 at origin, glyph i at DX[i-1] |
2469 | 64.6k | double fDX = 0, fDY = 0; |
2470 | 64.6k | if (pDXArry && i > 0) |
2471 | 1.60k | fDX = (*pDXArry)[i - 1]; |
2472 | 64.6k | if (pDYArry && i > 0) |
2473 | 118 | fDY = pDYArry[i - 1]; |
2474 | | |
2475 | 64.6k | basegfx::B2DHomMatrix aMat; |
2476 | 64.6k | if (fDX != 0 || fDY != 0) |
2477 | 810 | aMat.translate(fDX, fDY); |
2478 | | |
2479 | | // Translate to text position |
2480 | 64.6k | aMat.translate(rPosition.X(), rPosition.Y()); |
2481 | 64.6k | aGlyph.transform(aMat); |
2482 | | |
2483 | 64.6k | tools::PolyPolygon aGlyphPoly(aGlyph); |
2484 | | // Use aTmp's orientation so scale-induced rotation (GM_COMPATIBLE |
2485 | | // with negative fXScale/fYScale, or GM_ADVANCED world transform) |
2486 | | // is applied to the emitted polygons. |
2487 | 64.6k | if (aTmp.GetOrientation() != 0_deg10) |
2488 | 63.4k | { |
2489 | 192k | for (sal_uInt16 p = 0; p < aGlyphPoly.Count(); ++p) |
2490 | 129k | aGlyphPoly[p].Rotate(rPosition, aTmp.GetOrientation()); |
2491 | 63.4k | } |
2492 | | |
2493 | 64.6k | if (!bHasGlyphs) |
2494 | 1.07k | { |
2495 | | // Emit Push/FillColor/LineColor once before the first glyph |
2496 | 1.07k | mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR ) ); |
2497 | 1.07k | mpGDIMetaFile->AddAction( new MetaFillColorAction( maTextColor, true ) ); |
2498 | 1.07k | mpGDIMetaFile->AddAction( new MetaLineColorAction( Color(), false ) ); |
2499 | 1.07k | bHasGlyphs = true; |
2500 | 1.07k | } |
2501 | | |
2502 | 64.6k | mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( aGlyphPoly ) ); |
2503 | 64.6k | } |
2504 | | |
2505 | 1.07k | if (bHasGlyphs) |
2506 | 1.07k | mpGDIMetaFile->AddAction( new MetaPopAction() ); |
2507 | 1.07k | } |
2508 | | |
2509 | 1.09k | SetGfxMode( nOldGfxMode ); |
2510 | 1.09k | return true; |
2511 | 2.26k | } |
2512 | | |
2513 | | void MtfTools::ImplDrawBitmap( const Point& rPos, const Size& rSize, const Bitmap& rBitmap ) |
2514 | 36.6k | { |
2515 | 36.6k | Bitmap aBmp( rBitmap ); |
2516 | 36.6k | if ( mbComplexClip ) |
2517 | 0 | { |
2518 | 0 | vcl::bitmap::DrawAndClipBitmap(rPos, rSize, rBitmap, aBmp, maClipPath.getClipPath()); |
2519 | 0 | } |
2520 | | |
2521 | 36.6k | if ( aBmp.HasAlpha() ) |
2522 | 13.6k | mpGDIMetaFile->AddAction( new MetaBmpExScaleAction( rPos, rSize, aBmp ) ); |
2523 | 23.0k | else |
2524 | 23.0k | mpGDIMetaFile->AddAction( new MetaBmpScaleAction( rPos, rSize, aBmp ) ); |
2525 | 36.6k | } |
2526 | | |
2527 | | void MtfTools::ResolveBitmapActions( std::vector<BSaveStruct>& rSaveList ) |
2528 | 37.4k | { |
2529 | 37.4k | UpdateClipRegion(); |
2530 | | |
2531 | 37.4k | size_t nObjects = rSaveList.size(); |
2532 | 37.4k | size_t nObjectsLeft = nObjects; |
2533 | | |
2534 | 88.2k | while ( nObjectsLeft ) |
2535 | 50.7k | { |
2536 | 50.7k | size_t i; |
2537 | 50.7k | size_t nObjectsOfSameSize = 0; |
2538 | 50.7k | size_t nObjectStartIndex = nObjects - nObjectsLeft; |
2539 | | |
2540 | 50.7k | BSaveStruct* pSave = &rSaveList[nObjectStartIndex]; |
2541 | 50.7k | tools::Rectangle aRect( pSave->aOutRect ); |
2542 | | |
2543 | 91.7k | for ( i = nObjectStartIndex; i < nObjects; ) |
2544 | 54.2k | { |
2545 | 54.2k | nObjectsOfSameSize++; |
2546 | 54.2k | if ( ++i < nObjects ) |
2547 | 16.8k | { |
2548 | 16.8k | pSave = &rSaveList[i]; |
2549 | 16.8k | if ( pSave->aOutRect != aRect ) |
2550 | 13.2k | break; |
2551 | 16.8k | } |
2552 | 54.2k | } |
2553 | 50.7k | Point aPos( ImplMap( aRect.TopLeft() ) ); |
2554 | 50.7k | Size aSize( ImplMap( aRect.GetSize() ) ); |
2555 | | |
2556 | 104k | for ( i = nObjectStartIndex; i < ( nObjectStartIndex + nObjectsOfSameSize ); i++ ) |
2557 | 54.0k | { |
2558 | 54.0k | pSave = &rSaveList[i]; |
2559 | | |
2560 | 54.0k | sal_uInt32 nWinRop = pSave->nWinRop; |
2561 | 54.0k | sal_uInt8 nRasterOperation = static_cast<sal_uInt8>( nWinRop >> 16 ); |
2562 | | |
2563 | 54.0k | sal_uInt32 nUsed = 0; |
2564 | 54.0k | if ( ( nRasterOperation & 0xf ) != ( nRasterOperation >> 4 ) ) |
2565 | 14.4k | nUsed |= 1; // pattern is used |
2566 | 54.0k | if ( ( nRasterOperation & 0x33 ) != ( ( nRasterOperation & 0xcc ) >> 2 ) ) |
2567 | 24.2k | nUsed |= 2; // source is used |
2568 | 54.0k | if ( ( nRasterOperation & 0xaa ) != ( ( nRasterOperation & 0x55 ) << 1 ) ) |
2569 | 23.3k | nUsed |= 4; // destination is used |
2570 | | |
2571 | 54.0k | if ( (nUsed & 1) && (( nUsed & 2 ) == 0) && nWinRop != PATINVERT ) |
2572 | 670 | { // patterns aren't well supported yet |
2573 | 670 | WMFRasterOp nOldRop = SetRasterOp( WMFRasterOp::NONE ); // in this case nRasterOperation is either 0 or 0xff |
2574 | 670 | UpdateFillStyle(); |
2575 | 670 | DrawRect( aRect, false ); |
2576 | 670 | SetRasterOp( nOldRop ); |
2577 | 670 | } |
2578 | 53.4k | else |
2579 | 53.4k | { |
2580 | 53.4k | bool bDrawn = false; |
2581 | | |
2582 | 53.4k | if ( i == nObjectStartIndex ) // optimizing, sometimes it is possible to create just one transparent bitmap |
2583 | 50.0k | { |
2584 | 50.0k | if ( nObjectsOfSameSize == 2 ) |
2585 | 1.62k | { |
2586 | 1.62k | BSaveStruct* pSave2 = &rSaveList[i + 1]; |
2587 | 1.62k | if ( ( pSave->aBmp.GetPrefSize() == pSave2->aBmp.GetPrefSize() ) && |
2588 | 1.54k | ( pSave->aBmp.GetPrefMapMode() == pSave2->aBmp.GetPrefMapMode() ) ) |
2589 | 1.28k | { |
2590 | | // TODO: Strictly speaking, we should |
2591 | | // check whether mask is monochrome, and |
2592 | | // whether image is black (upper branch) |
2593 | | // or white (lower branch). Otherwise, the |
2594 | | // effect is not the same as a masked |
2595 | | // bitmap. |
2596 | 1.28k | if ( ( nWinRop == SRCPAINT ) && ( pSave2->nWinRop == SRCAND ) ) |
2597 | 160 | { |
2598 | 160 | Bitmap aMask( pSave->aBmp.CreateColorBitmap() ); |
2599 | 160 | aMask.Invert(); |
2600 | 160 | Bitmap aBmpEx( pSave2->aBmp.CreateColorBitmap(), aMask ); |
2601 | 160 | ImplDrawBitmap( aPos, aSize, aBmpEx ); |
2602 | 160 | bDrawn = true; |
2603 | 160 | i++; |
2604 | 160 | } |
2605 | | // #i20085# This is just the other way |
2606 | | // around as above. Only difference: mask |
2607 | | // is inverted |
2608 | 1.12k | else if ( ( nWinRop == SRCAND ) && ( pSave2->nWinRop == SRCPAINT ) ) |
2609 | 39 | { |
2610 | 39 | const Bitmap aMask( pSave->aBmp.CreateColorBitmap() ); |
2611 | 39 | Bitmap aBmpEx( pSave2->aBmp.CreateColorBitmap(), aMask ); |
2612 | 39 | ImplDrawBitmap( aPos, aSize, aBmpEx ); |
2613 | 39 | bDrawn = true; |
2614 | 39 | i++; |
2615 | 39 | } |
2616 | | // tdf#90539 |
2617 | 1.08k | else if ( ( nWinRop == SRCAND ) && ( pSave2->nWinRop == SRCINVERT ) ) |
2618 | 0 | { |
2619 | 0 | const Bitmap aMask( pSave->aBmp.CreateColorBitmap() ); |
2620 | 0 | Bitmap aBmpEx( pSave2->aBmp.CreateColorBitmap(), aMask ); |
2621 | 0 | ImplDrawBitmap( aPos, aSize, aBmpEx ); |
2622 | 0 | bDrawn = true; |
2623 | 0 | i++; |
2624 | 0 | } |
2625 | 1.28k | } |
2626 | 1.62k | } |
2627 | 50.0k | } |
2628 | | |
2629 | 53.4k | if ( !bDrawn ) |
2630 | 53.2k | { |
2631 | 53.2k | Push(); |
2632 | 53.2k | WMFRasterOp nOldRop = SetRasterOp( WMFRasterOp::CopyPen ); |
2633 | 53.2k | Bitmap aBitmap( pSave->aBmp.CreateColorBitmap() ); |
2634 | 53.2k | sal_uInt32 nOperation = ( nRasterOperation & 0xf ); |
2635 | 53.2k | switch( nOperation ) |
2636 | 53.2k | { |
2637 | 2.97k | case 0x1 : |
2638 | 10.2k | case 0xe : |
2639 | 10.2k | { |
2640 | 10.2k | if(pSave->aBmp.HasAlpha()) |
2641 | 1.92k | { |
2642 | 1.92k | ImplDrawBitmap( aPos, aSize, pSave->aBmp ); |
2643 | 1.92k | } |
2644 | 8.29k | else |
2645 | 8.29k | { |
2646 | 8.29k | SetRasterOp( WMFRasterOp::XorPen ); |
2647 | 8.29k | ImplDrawBitmap( aPos, aSize, aBitmap ); |
2648 | 8.29k | SetRasterOp( WMFRasterOp::CopyPen ); |
2649 | 8.29k | Bitmap aMask( aBitmap ); |
2650 | 8.29k | aMask.Invert(); |
2651 | 8.29k | Bitmap aBmpEx( aBitmap, aMask ); |
2652 | 8.29k | ImplDrawBitmap( aPos, aSize, aBmpEx ); |
2653 | 8.29k | if ( nOperation == 0x1 ) |
2654 | 2.97k | { |
2655 | 2.97k | SetRasterOp( WMFRasterOp::Not ); |
2656 | 2.97k | DrawRect( aRect, false ); |
2657 | 2.97k | } |
2658 | 8.29k | } |
2659 | 10.2k | } |
2660 | 10.2k | break; |
2661 | 3.69k | case 0x7 : |
2662 | 5.49k | case 0x8 : |
2663 | 5.49k | { |
2664 | 5.49k | Bitmap aMask( aBitmap ); |
2665 | 5.49k | if ( ( nUsed & 1 ) && ( nRasterOperation & 0xb0 ) == 0xb0 ) // pattern used |
2666 | 3.81k | { |
2667 | 3.81k | aBitmap.Convert( BmpConversion::N24Bit ); |
2668 | 3.81k | aBitmap.Erase( maFillStyle.aFillColor ); |
2669 | 3.81k | } |
2670 | 5.49k | Bitmap aBmpEx( aBitmap, aMask ); |
2671 | 5.49k | ImplDrawBitmap( aPos, aSize, aBmpEx ); |
2672 | 5.49k | if ( nOperation == 0x7 ) |
2673 | 3.69k | { |
2674 | 3.69k | SetRasterOp( WMFRasterOp::Not ); |
2675 | 3.69k | DrawRect( aRect, false ); |
2676 | 3.69k | } |
2677 | 5.49k | } |
2678 | 5.49k | break; |
2679 | | |
2680 | 1.35k | case 0x4 : |
2681 | 2.21k | case 0xb : |
2682 | 2.21k | { |
2683 | 2.21k | SetRasterOp( WMFRasterOp::Not ); |
2684 | 2.21k | DrawRect( aRect, false ); |
2685 | 2.21k | SetRasterOp( WMFRasterOp::CopyPen ); |
2686 | 2.21k | Bitmap aMask( aBitmap ); |
2687 | 2.21k | aBitmap.Invert(); |
2688 | 2.21k | Bitmap aBmpEx( aBitmap, aMask ); |
2689 | 2.21k | ImplDrawBitmap( aPos, aSize, aBmpEx ); |
2690 | 2.21k | SetRasterOp( WMFRasterOp::XorPen ); |
2691 | 2.21k | ImplDrawBitmap( aPos, aSize, aBitmap ); |
2692 | 2.21k | if ( nOperation == 0xb ) |
2693 | 865 | { |
2694 | 865 | SetRasterOp( WMFRasterOp::Not ); |
2695 | 865 | DrawRect( aRect, false ); |
2696 | 865 | } |
2697 | 2.21k | } |
2698 | 2.21k | break; |
2699 | | |
2700 | 1.25k | case 0x2 : |
2701 | 2.77k | case 0xd : |
2702 | 2.77k | { |
2703 | 2.77k | Bitmap aMask( aBitmap ); |
2704 | 2.77k | aMask.Invert(); |
2705 | 2.77k | Bitmap aBmpEx( aBitmap, aMask ); |
2706 | 2.77k | ImplDrawBitmap( aPos, aSize, aBmpEx ); |
2707 | 2.77k | SetRasterOp( WMFRasterOp::XorPen ); |
2708 | 2.77k | ImplDrawBitmap( aPos, aSize, aBitmap ); |
2709 | 2.77k | if ( nOperation == 0xd ) |
2710 | 1.51k | { |
2711 | 1.51k | SetRasterOp( WMFRasterOp::Not ); |
2712 | 1.51k | DrawRect( aRect, false ); |
2713 | 1.51k | } |
2714 | 2.77k | } |
2715 | 2.77k | break; |
2716 | 597 | case 0x6 : |
2717 | 855 | case 0x9 : |
2718 | 855 | { |
2719 | 855 | SetRasterOp( WMFRasterOp::XorPen ); |
2720 | 855 | ImplDrawBitmap( aPos, aSize, aBitmap ); |
2721 | 855 | if ( nOperation == 0x9 ) |
2722 | 258 | { |
2723 | 258 | SetRasterOp( WMFRasterOp::Not ); |
2724 | 258 | DrawRect( aRect, false ); |
2725 | 258 | } |
2726 | 855 | } |
2727 | 855 | break; |
2728 | | |
2729 | 27.1k | case 0x0 : // WHITENESS |
2730 | 29.2k | case 0xf : // BLACKNESS |
2731 | 29.2k | { // in this case nRasterOperation is either 0 or 0xff |
2732 | 29.2k | maFillStyle = WinMtfFillStyle( Color( nRasterOperation, nRasterOperation, nRasterOperation ) ); |
2733 | 29.2k | UpdateFillStyle(); |
2734 | 29.2k | DrawRect( aRect, false ); |
2735 | 29.2k | } |
2736 | 29.2k | break; |
2737 | | |
2738 | 247 | case 0x3 : // only source is used |
2739 | 1.62k | case 0xc : |
2740 | 1.62k | { |
2741 | 1.62k | if ( nRasterOperation == 0x33 ) |
2742 | 97 | aBitmap.Invert(); |
2743 | 1.62k | if (pSave->m_bForceAlpha) |
2744 | 1.02k | { |
2745 | 1.02k | ImplDrawBitmap(aPos, aSize, pSave->aBmp); |
2746 | 1.02k | } |
2747 | 595 | else |
2748 | 595 | { |
2749 | 595 | ImplDrawBitmap(aPos, aSize, aBitmap); |
2750 | 595 | } |
2751 | 1.62k | } |
2752 | 1.62k | break; |
2753 | | |
2754 | 671 | case 0x5 : // only destination is used |
2755 | 671 | { |
2756 | 671 | SetRasterOp( WMFRasterOp::Not ); |
2757 | 671 | DrawRect( aRect, false ); |
2758 | 671 | } |
2759 | 671 | break; |
2760 | | |
2761 | 107 | case 0xa : // no operation |
2762 | 107 | break; |
2763 | 53.2k | } |
2764 | 53.2k | SetRasterOp( nOldRop ); |
2765 | 53.2k | Pop(); |
2766 | 53.2k | } |
2767 | 53.4k | } |
2768 | 54.0k | } |
2769 | 50.7k | nObjectsLeft -= nObjectsOfSameSize; |
2770 | 50.7k | } |
2771 | | |
2772 | 37.4k | rSaveList.clear(); |
2773 | 37.4k | } |
2774 | | |
2775 | | void MtfTools::SetDevOrg( const Point& rPoint ) |
2776 | 25.4k | { |
2777 | 25.4k | mnDevOrgX = rPoint.X(); |
2778 | 25.4k | mnDevOrgY = rPoint.Y(); |
2779 | 25.4k | } |
2780 | | |
2781 | | void MtfTools::SetDevOrgOffset( sal_Int32 nXAdd, sal_Int32 nYAdd ) |
2782 | 479 | { |
2783 | 479 | mnDevOrgX += nXAdd; |
2784 | 479 | mnDevOrgY += nYAdd; |
2785 | 479 | } |
2786 | | |
2787 | | void MtfTools::SetDevExt( const Size& rSize ,bool regular) |
2788 | 32.1k | { |
2789 | 32.1k | if ( !(rSize.Width() && rSize.Height()) ) |
2790 | 1.34k | return; |
2791 | | |
2792 | 30.8k | switch( meMapMode ) |
2793 | 30.8k | { |
2794 | 4.76k | case MappingMode::MM_ISOTROPIC : |
2795 | 28.5k | case MappingMode::MM_ANISOTROPIC : |
2796 | 28.5k | { |
2797 | 28.5k | mnDevWidth = rSize.Width(); |
2798 | 28.5k | mnDevHeight = rSize.Height(); |
2799 | 28.5k | break; |
2800 | 4.76k | } |
2801 | | |
2802 | | //do nothing |
2803 | 2.22k | default: |
2804 | 2.22k | break; |
2805 | 30.8k | } |
2806 | 30.8k | if (regular) |
2807 | 26.4k | { |
2808 | 26.4k | mbIsMapDevSet=true; |
2809 | 26.4k | } |
2810 | 30.8k | } |
2811 | | |
2812 | | void MtfTools::ScaleDevExt(double fX, double fY) |
2813 | 5.01k | { |
2814 | 5.01k | mnDevWidth = basegfx::fround(mnDevWidth * fX); |
2815 | 5.01k | mnDevHeight = basegfx::fround(mnDevHeight * fY); |
2816 | 5.01k | } |
2817 | | |
2818 | | void MtfTools::SetWinOrg( const Point& rPoint , bool bIsEMF) |
2819 | 34.4k | { |
2820 | 34.4k | mnWinOrgX = rPoint.X(); |
2821 | 34.4k | mnWinOrgY = rPoint.Y(); |
2822 | 34.4k | if (bIsEMF) |
2823 | 8.68k | { |
2824 | 8.68k | SetDevByWin(); |
2825 | 8.68k | } |
2826 | 34.4k | mbIsMapWinSet=true; |
2827 | 34.4k | } |
2828 | | |
2829 | | void MtfTools::SetWinOrgOffset( sal_Int32 nXAdd, sal_Int32 nYAdd ) |
2830 | 215 | { |
2831 | 215 | mnWinOrgX += nXAdd; |
2832 | 215 | mnWinOrgY += nYAdd; |
2833 | 215 | } |
2834 | | |
2835 | | void MtfTools::SetDevByWin() //mnWinExt...-stuff has to be assigned before. |
2836 | 11.2k | { |
2837 | 11.2k | if (!mbIsMapDevSet) |
2838 | 9.45k | { |
2839 | 9.45k | if ( meMapMode == MappingMode::MM_ISOTROPIC ) //TODO: WHAT ABOUT ANISOTROPIC??? |
2840 | 6.45k | { |
2841 | 6.45k | sal_Int32 nX, nY; |
2842 | 6.45k | if (o3tl::checked_add(mnWinExtX, mnWinOrgX, nX) || o3tl::checked_sub(mnWinExtY, mnWinOrgY, nY)) |
2843 | 1.27k | return; |
2844 | 5.17k | Size aSize(nX >> MS_FIXPOINT_BITCOUNT_28_4, -(nY >> MS_FIXPOINT_BITCOUNT_28_4)); |
2845 | 5.17k | SetDevExt(aSize, false); |
2846 | 5.17k | } |
2847 | 9.45k | } |
2848 | 11.2k | } |
2849 | | |
2850 | | void MtfTools::SetWinExt(const Size& rSize, bool bIsEMF) |
2851 | 30.3k | { |
2852 | 30.3k | if (!(rSize.Width() && rSize.Height())) |
2853 | 2.63k | return; |
2854 | | |
2855 | 27.7k | switch( meMapMode ) |
2856 | 27.7k | { |
2857 | 2.09k | case MappingMode::MM_ISOTROPIC : |
2858 | 25.5k | case MappingMode::MM_ANISOTROPIC : |
2859 | 25.5k | { |
2860 | 25.5k | mnWinExtX = rSize.Width(); |
2861 | 25.5k | mnWinExtY = rSize.Height(); |
2862 | 25.5k | if (bIsEMF) |
2863 | 2.60k | { |
2864 | 2.60k | SetDevByWin(); |
2865 | 2.60k | } |
2866 | 25.5k | mbIsMapWinSet = true; |
2867 | 25.5k | break; |
2868 | 2.09k | } |
2869 | | |
2870 | 2.13k | default: |
2871 | | //do nothing |
2872 | 2.13k | break; |
2873 | 27.7k | } |
2874 | 27.7k | } |
2875 | | |
2876 | | void MtfTools::ScaleWinExt(double fX, double fY) |
2877 | 4.08k | { |
2878 | 4.08k | mnWinExtX = basegfx::fround(mnWinExtX * fX); |
2879 | 4.08k | mnWinExtY = basegfx::fround(mnWinExtY * fY); |
2880 | 4.08k | } |
2881 | | |
2882 | | void MtfTools::SetrclBounds( const tools::Rectangle& rRect ) |
2883 | 10.9k | { |
2884 | 10.9k | mrclBounds = rRect; |
2885 | 10.9k | } |
2886 | | |
2887 | | void MtfTools::SetrclFrame( const tools::Rectangle& rRect ) |
2888 | 11.0k | { |
2889 | 11.0k | mrclFrame = rRect; |
2890 | 11.0k | } |
2891 | | |
2892 | | void MtfTools::SetRefPix( const Size& rSize ) |
2893 | 10.9k | { |
2894 | 10.9k | mnPixX = rSize.Width(); |
2895 | 10.9k | mnPixY = rSize.Height(); |
2896 | 10.9k | } |
2897 | | |
2898 | | void MtfTools::SetRefMill( const Size& rSize ) |
2899 | 10.9k | { |
2900 | 10.9k | mnMillX = rSize.Width(); |
2901 | 10.9k | mnMillY = rSize.Height(); |
2902 | 10.9k | } |
2903 | | |
2904 | | void MtfTools::SetMapMode( MappingMode nMapMode ) |
2905 | 16.1k | { |
2906 | 16.1k | meMapMode = nMapMode; |
2907 | 16.1k | if ( nMapMode == MappingMode::MM_TEXT && !mbIsMapWinSet ) |
2908 | 463 | { |
2909 | 463 | mnWinExtX = mnDevWidth; |
2910 | 463 | mnWinExtY = mnDevHeight; |
2911 | 463 | } |
2912 | 15.6k | else if ( meMapMode == MappingMode::MM_HIMETRIC ) |
2913 | 1.18k | { |
2914 | 1.18k | sal_Int32 nWinExtX, nWinExtY; |
2915 | 1.18k | if (o3tl::checked_multiply<sal_Int32>(mnMillX, 100, nWinExtX) || |
2916 | 995 | o3tl::checked_multiply<sal_Int32>(mnMillY, 100, nWinExtY)) |
2917 | 547 | { |
2918 | 547 | return; |
2919 | 547 | } |
2920 | 635 | mnWinExtX = nWinExtX; |
2921 | 635 | mnWinExtY = nWinExtY; |
2922 | 635 | } |
2923 | 16.1k | } |
2924 | | |
2925 | | void MtfTools::SetWorldTransform( const XForm& rXForm ) |
2926 | 1.06k | { |
2927 | 1.06k | maXForm.eM11 = rXForm.eM11; |
2928 | 1.06k | maXForm.eM12 = rXForm.eM12; |
2929 | 1.06k | maXForm.eM21 = rXForm.eM21; |
2930 | 1.06k | maXForm.eM22 = rXForm.eM22; |
2931 | 1.06k | maXForm.eDx = rXForm.eDx; |
2932 | 1.06k | maXForm.eDy = rXForm.eDy; |
2933 | 1.06k | } |
2934 | | |
2935 | | void MtfTools::ModifyWorldTransform( const XForm& rXForm, ModifyWorldTransformMode nMode ) |
2936 | 4.50k | { |
2937 | 4.50k | switch( nMode ) |
2938 | 4.50k | { |
2939 | 1.21k | case ModifyWorldTransformMode::MWT_IDENTITY : |
2940 | 1.21k | { |
2941 | 1.21k | maXForm.eM11 = maXForm.eM22 = 1.0f; |
2942 | 1.21k | maXForm.eM12 = maXForm.eM21 = maXForm.eDx = maXForm.eDy = 0.0f; |
2943 | 1.21k | break; |
2944 | 0 | } |
2945 | | |
2946 | 397 | case ModifyWorldTransformMode::MWT_RIGHTMULTIPLY : |
2947 | 1.40k | case ModifyWorldTransformMode::MWT_LEFTMULTIPLY : |
2948 | 1.40k | { |
2949 | 1.40k | const XForm* pLeft; |
2950 | 1.40k | const XForm* pRight; |
2951 | | |
2952 | 1.40k | if ( nMode == ModifyWorldTransformMode::MWT_LEFTMULTIPLY ) |
2953 | 1.01k | { |
2954 | 1.01k | pLeft = &rXForm; |
2955 | 1.01k | pRight = &maXForm; |
2956 | 1.01k | } |
2957 | 397 | else |
2958 | 397 | { |
2959 | 397 | pLeft = &maXForm; |
2960 | 397 | pRight = &rXForm; |
2961 | 397 | } |
2962 | | |
2963 | 1.40k | float aF[3][3]; |
2964 | 1.40k | float bF[3][3]; |
2965 | 1.40k | float cF[3][3]; |
2966 | | |
2967 | 1.40k | aF[0][0] = pLeft->eM11; |
2968 | 1.40k | aF[0][1] = pLeft->eM12; |
2969 | 1.40k | aF[0][2] = 0; |
2970 | 1.40k | aF[1][0] = pLeft->eM21; |
2971 | 1.40k | aF[1][1] = pLeft->eM22; |
2972 | 1.40k | aF[1][2] = 0; |
2973 | 1.40k | aF[2][0] = pLeft->eDx; |
2974 | 1.40k | aF[2][1] = pLeft->eDy; |
2975 | 1.40k | aF[2][2] = 1; |
2976 | | |
2977 | 1.40k | bF[0][0] = pRight->eM11; |
2978 | 1.40k | bF[0][1] = pRight->eM12; |
2979 | 1.40k | bF[0][2] = 0; |
2980 | 1.40k | bF[1][0] = pRight->eM21; |
2981 | 1.40k | bF[1][1] = pRight->eM22; |
2982 | 1.40k | bF[1][2] = 0; |
2983 | 1.40k | bF[2][0] = pRight->eDx; |
2984 | 1.40k | bF[2][1] = pRight->eDy; |
2985 | 1.40k | bF[2][2] = 1; |
2986 | | |
2987 | 1.40k | int i, j, k; |
2988 | 5.63k | for ( i = 0; i < 3; i++ ) |
2989 | 4.22k | { |
2990 | 16.8k | for ( j = 0; j < 3; j++ ) |
2991 | 12.6k | { |
2992 | 12.6k | cF[i][j] = 0; |
2993 | 50.6k | for ( k = 0; k < 3; k++ ) |
2994 | 38.0k | cF[i][j] += aF[i][k] * bF[k][j]; |
2995 | 12.6k | } |
2996 | 4.22k | } |
2997 | 1.40k | maXForm.eM11 = cF[0][0]; |
2998 | 1.40k | maXForm.eM12 = cF[0][1]; |
2999 | 1.40k | maXForm.eM21 = cF[1][0]; |
3000 | 1.40k | maXForm.eM22 = cF[1][1]; |
3001 | 1.40k | maXForm.eDx = cF[2][0]; |
3002 | 1.40k | maXForm.eDy = cF[2][1]; |
3003 | 1.40k | break; |
3004 | 397 | } |
3005 | 237 | case ModifyWorldTransformMode::MWT_SET: |
3006 | 237 | { |
3007 | 237 | SetWorldTransform(rXForm); |
3008 | 237 | break; |
3009 | 397 | } |
3010 | 4.50k | } |
3011 | 4.50k | } |
3012 | | |
3013 | | void MtfTools::Push() // !! to be able to access the original ClipRegion it |
3014 | 75.5k | { // is not allowed to use the MetaPushAction() |
3015 | 75.5k | UpdateClipRegion(); // (the original clip region is on top of the stack) (SJ) |
3016 | 75.5k | auto pSave = std::make_shared<SaveStruct>(); |
3017 | | |
3018 | 75.5k | pSave->aLineStyle = maLineStyle; |
3019 | 75.5k | pSave->aFillStyle = maFillStyle; |
3020 | | |
3021 | 75.5k | pSave->aFont = maFont; |
3022 | 75.5k | pSave->aTextColor = maTextColor; |
3023 | 75.5k | pSave->nTextAlign = mnTextAlign; |
3024 | 75.5k | pSave->nTextLayoutMode = mnTextLayoutMode; |
3025 | 75.5k | pSave->eMapMode = meMapMode; |
3026 | 75.5k | pSave->eGfxMode = meGfxMode; |
3027 | 75.5k | pSave->nBkMode = mnBkMode; |
3028 | 75.5k | pSave->aBkColor = maBkColor; |
3029 | 75.5k | pSave->bClockWiseArcDirection = mbClockWiseArcDirection; |
3030 | 75.5k | pSave->bFillStyleSelected = mbFillStyleSelected; |
3031 | 75.5k | pSave->nPolyFillMode = mnPolyFillMode; |
3032 | | |
3033 | 75.5k | pSave->aActPos = maActPos; |
3034 | 75.5k | pSave->aXForm = maXForm; |
3035 | 75.5k | pSave->eRasterOp = meRasterOp; |
3036 | | |
3037 | 75.5k | pSave->nWinOrgX = mnWinOrgX; |
3038 | 75.5k | pSave->nWinOrgY = mnWinOrgY; |
3039 | 75.5k | pSave->nWinExtX = mnWinExtX; |
3040 | 75.5k | pSave->nWinExtY = mnWinExtY; |
3041 | 75.5k | pSave->nDevOrgX = mnDevOrgX; |
3042 | 75.5k | pSave->nDevOrgY = mnDevOrgY; |
3043 | 75.5k | pSave->nDevWidth = mnDevWidth; |
3044 | 75.5k | pSave->nDevHeight = mnDevHeight; |
3045 | | |
3046 | 75.5k | pSave->maPathObj = maPathObj; |
3047 | 75.5k | pSave->maClipPath = maClipPath; |
3048 | | |
3049 | 75.5k | SAL_INFO("emfio", "\t\t GfxMode: " << static_cast<sal_uInt32>(meGfxMode)); |
3050 | 75.5k | SAL_INFO("emfio", "\t\t MapMode: " << static_cast<sal_uInt32>(meMapMode)); |
3051 | 75.5k | SAL_INFO("emfio", "\t\t WinOrg: " << mnWinOrgX << ", " << mnWinOrgY); |
3052 | 75.5k | SAL_INFO("emfio", "\t\t WinExt: " << mnWinExtX << " x " << mnWinExtY); |
3053 | 75.5k | SAL_INFO("emfio", "\t\t DevOrg: " << mnDevOrgX << ", " << mnDevOrgY); |
3054 | 75.5k | SAL_INFO("emfio", "\t\t DevWidth/Height: " << mnDevWidth << " x " << mnDevHeight); |
3055 | 75.5k | SAL_INFO("emfio", "\t\t LineStyle: " << maLineStyle.aLineColor << " FillStyle: " << maFillStyle.aFillColor ); |
3056 | 75.5k | mvSaveStack.push_back(std::move(pSave)); |
3057 | 75.5k | } |
3058 | | |
3059 | | void MtfTools::Pop( const sal_Int32 nSavedDC ) |
3060 | 68.9k | { |
3061 | 68.9k | if ( nSavedDC == 0 ) |
3062 | 29 | return; |
3063 | | |
3064 | 68.9k | sal_Int32 aIndex; |
3065 | 68.9k | if ( nSavedDC < 0 ) // WMF/EMF, if negative, nSavedDC represents an instance relative to the current state. |
3066 | 68.4k | aIndex = static_cast< sal_Int32 >( mvSaveStack.size() ) + nSavedDC; |
3067 | 548 | else |
3068 | 548 | aIndex = nSavedDC; // WMF, if positive, nSavedDC represents a specific instance of the state to be restored. |
3069 | 68.9k | if( aIndex < 0 ) |
3070 | 1.81k | { |
3071 | 1.81k | mvSaveStack.clear(); |
3072 | 1.81k | return; |
3073 | 1.81k | } |
3074 | 67.1k | if( mvSaveStack.empty() || ( aIndex >= static_cast< sal_Int32 >( mvSaveStack.size() ) ) ) |
3075 | 425 | return; |
3076 | | |
3077 | 66.7k | mvSaveStack.resize( aIndex + 1 ); |
3078 | | // Backup the current data on the stack |
3079 | 66.7k | std::shared_ptr<SaveStruct>& pSave( mvSaveStack.back() ); |
3080 | | |
3081 | 66.7k | maLineStyle = pSave->aLineStyle; |
3082 | 66.7k | maFillStyle = pSave->aFillStyle; |
3083 | | |
3084 | 66.7k | maFont = pSave->aFont; |
3085 | 66.7k | maTextColor = pSave->aTextColor; |
3086 | 66.7k | mnTextAlign = pSave->nTextAlign; |
3087 | 66.7k | mnTextLayoutMode = pSave->nTextLayoutMode; |
3088 | 66.7k | mnBkMode = pSave->nBkMode; |
3089 | 66.7k | meGfxMode = pSave->eGfxMode; |
3090 | 66.7k | meMapMode = pSave->eMapMode; |
3091 | 66.7k | maBkColor = pSave->aBkColor; |
3092 | 66.7k | mbClockWiseArcDirection = pSave->bClockWiseArcDirection; |
3093 | 66.7k | mbFillStyleSelected = pSave->bFillStyleSelected; |
3094 | 66.7k | mnPolyFillMode = pSave->nPolyFillMode; |
3095 | | |
3096 | 66.7k | maActPos = pSave->aActPos; |
3097 | 66.7k | maXForm = pSave->aXForm; |
3098 | 66.7k | meRasterOp = pSave->eRasterOp; |
3099 | | |
3100 | 66.7k | mnWinOrgX = pSave->nWinOrgX; |
3101 | 66.7k | mnWinOrgY = pSave->nWinOrgY; |
3102 | 66.7k | mnWinExtX = pSave->nWinExtX; |
3103 | 66.7k | mnWinExtY = pSave->nWinExtY; |
3104 | 66.7k | mnDevOrgX = pSave->nDevOrgX; |
3105 | 66.7k | mnDevOrgY = pSave->nDevOrgY; |
3106 | 66.7k | mnDevWidth = pSave->nDevWidth; |
3107 | 66.7k | mnDevHeight = pSave->nDevHeight; |
3108 | | |
3109 | 66.7k | maPathObj = pSave->maPathObj; |
3110 | 66.7k | if ( ! ( maClipPath == pSave->maClipPath ) ) |
3111 | 0 | { |
3112 | 0 | maClipPath = pSave->maClipPath; |
3113 | 0 | mbClipNeedsUpdate = true; |
3114 | 0 | } |
3115 | 66.7k | if ( meLatestRasterOp != meRasterOp ) |
3116 | 5.77k | { |
3117 | 5.77k | mpGDIMetaFile->AddAction( new MetaRasterOpAction( meRasterOp ) ); |
3118 | 5.77k | meLatestRasterOp = meRasterOp; |
3119 | 5.77k | } |
3120 | | |
3121 | 66.7k | SAL_INFO("emfio", "\t\t GfxMode: " << static_cast<sal_uInt32>(meGfxMode)); |
3122 | 66.7k | SAL_INFO("emfio", "\t\t MapMode: " << static_cast<sal_uInt32>(meMapMode)); |
3123 | 66.7k | SAL_INFO("emfio", "\t\t WinOrg: " << mnWinOrgX << ", " << mnWinOrgY); |
3124 | 66.7k | SAL_INFO("emfio", "\t\t WinExt: " << mnWinExtX << " x " << mnWinExtY); |
3125 | 66.7k | SAL_INFO("emfio", "\t\t DevOrg: " << mnDevOrgX << ", " << mnDevOrgY); |
3126 | 66.7k | SAL_INFO("emfio", "\t\t DevWidth/Height: " << mnDevWidth << " x " << mnDevHeight); |
3127 | 66.7k | SAL_INFO("emfio", "\t\t LineStyle: " << maLineStyle.aLineColor << " FillStyle: " << maFillStyle.aFillColor ); |
3128 | 66.7k | mvSaveStack.pop_back(); |
3129 | 66.7k | } |
3130 | | |
3131 | | void MtfTools::AddFromGDIMetaFile( GDIMetaFile& rGDIMetaFile ) |
3132 | 41 | { |
3133 | 41 | rGDIMetaFile.Play( *mpGDIMetaFile ); |
3134 | 41 | } |
3135 | | |
3136 | | void MtfTools::PassEMFPlusHeaderInfo() |
3137 | 1.84k | { |
3138 | 1.84k | EMFP_DEBUG(printf ("\t\t\tadd EMF_PLUS header info\n")); |
3139 | | |
3140 | 1.84k | SvMemoryStream mem; |
3141 | 1.84k | sal_Int32 nLeft, nRight, nTop, nBottom; |
3142 | | |
3143 | 1.84k | nLeft = mrclFrame.Left(); |
3144 | 1.84k | nTop = mrclFrame.Top(); |
3145 | 1.84k | nRight = mrclFrame.Right(); |
3146 | 1.84k | nBottom = mrclFrame.Bottom(); |
3147 | | |
3148 | | // emf header info |
3149 | 1.84k | mem.WriteInt32( nLeft ).WriteInt32( nTop ).WriteInt32( nRight ).WriteInt32( nBottom ); |
3150 | 1.84k | mem.WriteInt32( mnPixX ).WriteInt32( mnPixY ).WriteInt32( mnMillX ).WriteInt32( mnMillY ); |
3151 | | |
3152 | 1.84k | float one, zero; |
3153 | | |
3154 | 1.84k | one = 1; |
3155 | 1.84k | zero = 0; |
3156 | | |
3157 | | // add transformation matrix to be used in vcl's metaact.cxx for |
3158 | | // rotate and scale operations |
3159 | 1.84k | mem.WriteFloat( one ).WriteFloat( zero ).WriteFloat( zero ).WriteFloat( one ).WriteFloat( zero ).WriteFloat( zero ); |
3160 | | |
3161 | | // need to flush the stream, otherwise GetEndOfData will return 0 |
3162 | | // on windows where the function parameters are probably resolved in reverse order |
3163 | 1.84k | mem.Flush(); |
3164 | | |
3165 | 1.84k | mpGDIMetaFile->AddAction( new MetaCommentAction( "EMF_PLUS_HEADER_INFO"_ostr, 0, static_cast<const sal_uInt8*>(mem.GetData()), mem.GetEndOfData() ) ); |
3166 | 1.84k | mpGDIMetaFile->UseCanvas( true ); |
3167 | 1.84k | } |
3168 | | |
3169 | | void MtfTools::PassEMFPlus( void const * pBuffer, sal_uInt32 nLength ) |
3170 | 7.18k | { |
3171 | 7.18k | EMFP_DEBUG(printf ("\t\t\tadd EMF_PLUS comment length %04x\n",(unsigned int) nLength)); |
3172 | 7.18k | mpGDIMetaFile->AddAction( new MetaCommentAction( "EMF_PLUS"_ostr, 0, static_cast<const sal_uInt8*>(pBuffer), nLength ) ); |
3173 | 7.18k | } |
3174 | | } |
3175 | | |
3176 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |