/src/libreoffice/vcl/source/outdev/textline.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 <sal/types.h> |
21 | | #include <basegfx/matrix/b2dhommatrixtools.hxx> |
22 | | #include <basegfx/polygon/WaveLine.hxx> |
23 | | #include <tools/helpers.hxx> |
24 | | #include <o3tl/hash_combine.hxx> |
25 | | #include <o3tl/lru_map.hxx> |
26 | | #include <comphelper/configuration.hxx> |
27 | | #include <tools/lazydelete.hxx> |
28 | | #include <vcl/dropcache.hxx> |
29 | | #include <vcl/metaact.hxx> |
30 | | #include <vcl/settings.hxx> |
31 | | #include <vcl/virdev.hxx> |
32 | | #include <vcl/skia/SkiaHelper.hxx> |
33 | | |
34 | | #include <drawmode.hxx> |
35 | | #include <salgdi.hxx> |
36 | | #include <impglyphitem.hxx> |
37 | | |
38 | | #include <cassert> |
39 | | |
40 | 282k | #define UNDERLINE_LAST LINESTYLE_BOLDWAVE |
41 | 170k | #define STRIKEOUT_LAST STRIKEOUT_X |
42 | | |
43 | | namespace { |
44 | | struct WavyLineCache final : public CacheOwner |
45 | | { |
46 | | WavyLineCache() |
47 | | #if defined __cpp_lib_memory_resource |
48 | 0 | : m_aItems(10, &GetMemoryResource()) |
49 | | #else |
50 | | : m_aItems(10) |
51 | | #endif |
52 | 0 | { |
53 | 0 | } |
54 | | |
55 | | bool find( Color aLineColor, size_t nLineWidth, size_t nWaveHeight, size_t nWordWidth, Bitmap& rOutput ) |
56 | 0 | { |
57 | 0 | Key aKey = { nWaveHeight, sal_uInt32(aLineColor) }; |
58 | 0 | auto item = m_aItems.find( aKey ); |
59 | 0 | if ( item == m_aItems.end() ) |
60 | 0 | return false; |
61 | | // needs update |
62 | 0 | if ( item->second.m_aLineWidth != nLineWidth || item->second.m_aWordWidth < nWordWidth ) |
63 | 0 | { |
64 | 0 | return false; |
65 | 0 | } |
66 | 0 | rOutput = item->second.m_Bitmap; |
67 | 0 | return true; |
68 | 0 | } |
69 | | |
70 | | void insert( const Bitmap& aBitmap, const Color& aLineColor, const size_t nLineWidth, const size_t nWaveHeight, const size_t nWordWidth, Bitmap& rOutput ) |
71 | 0 | { |
72 | 0 | Key aKey = { nWaveHeight, sal_uInt32(aLineColor) }; |
73 | 0 | m_aItems.insert( std::pair< Key, WavyLineCacheItem>( aKey, { nLineWidth, nWordWidth, aBitmap } ) ); |
74 | 0 | rOutput = aBitmap; |
75 | 0 | } |
76 | | |
77 | 0 | virtual OUString getCacheName() const override { return "WavyLineCache"; } |
78 | | |
79 | | virtual bool dropCaches() override |
80 | 0 | { |
81 | 0 | m_aItems.clear(); |
82 | 0 | return true; |
83 | 0 | } |
84 | | |
85 | | virtual void dumpState(rtl::OStringBuffer& rState) override |
86 | 0 | { |
87 | 0 | rState.append("\nWavyLineCache:\t"); |
88 | 0 | rState.append(static_cast<sal_Int32>(m_aItems.size())); |
89 | 0 | } |
90 | | |
91 | | private: |
92 | | struct WavyLineCacheItem |
93 | | { |
94 | | size_t m_aLineWidth; |
95 | | size_t m_aWordWidth; |
96 | | Bitmap m_Bitmap; |
97 | | }; |
98 | | |
99 | | struct Key |
100 | | { |
101 | | size_t m_aFirst; |
102 | | size_t m_aSecond; |
103 | | bool operator ==( const Key& rOther ) const |
104 | 0 | { |
105 | 0 | return ( m_aFirst == rOther.m_aFirst && m_aSecond == rOther.m_aSecond ); |
106 | 0 | } |
107 | | }; |
108 | | |
109 | | struct Hash |
110 | | { |
111 | | size_t operator() ( const Key& rKey ) const |
112 | 0 | { |
113 | 0 | size_t aSeed = 0; |
114 | 0 | o3tl::hash_combine(aSeed, rKey.m_aFirst); |
115 | 0 | o3tl::hash_combine(aSeed, rKey.m_aSecond); |
116 | 0 | return aSeed; |
117 | 0 | } |
118 | | }; |
119 | | |
120 | | o3tl::lru_map< Key, WavyLineCacheItem, Hash > m_aItems; |
121 | | }; |
122 | | } |
123 | | |
124 | | void OutputDevice::ImplInitTextLineSize() |
125 | 0 | { |
126 | 0 | mpFontInstance->mxFontMetric->ImplInitTextLineSize( this ); |
127 | 0 | } |
128 | | |
129 | | void OutputDevice::ImplInitAboveTextLineSize() |
130 | 0 | { |
131 | 0 | mpFontInstance->mxFontMetric->ImplInitAboveTextLineSize( this ); |
132 | 0 | } |
133 | | |
134 | | void OutputDevice::ImplDrawWavePixel( tools::Long nOriginX, tools::Long nOriginY, |
135 | | tools::Long nCurX, tools::Long nCurY, |
136 | | tools::Long nWidth, |
137 | | Degree10 nOrientation, |
138 | | SalGraphics* pGraphics, |
139 | | const OutputDevice& rOutDev, |
140 | | tools::Long nPixWidth, tools::Long nPixHeight ) |
141 | 1.22M | { |
142 | 1.22M | if (nOrientation) |
143 | 1.18M | { |
144 | 1.18M | Point aPoint( nOriginX, nOriginY ); |
145 | 1.18M | aPoint.RotateAround( nCurX, nCurY, nOrientation ); |
146 | 1.18M | } |
147 | | |
148 | 1.22M | if (shouldDrawWavePixelAsRect(nWidth)) |
149 | 60.2k | { |
150 | 60.2k | pGraphics->DrawRect( nCurX, nCurY, nPixWidth, nPixHeight, rOutDev ); |
151 | 60.2k | } |
152 | 1.16M | else |
153 | 1.16M | { |
154 | 1.16M | pGraphics->DrawPixel( nCurX, nCurY, rOutDev ); |
155 | 1.16M | } |
156 | 1.22M | } |
157 | | |
158 | | bool OutputDevice::shouldDrawWavePixelAsRect(tools::Long nLineWidth) const |
159 | 1.22M | { |
160 | 1.22M | if (nLineWidth > 1) |
161 | 60.2k | return true; |
162 | | |
163 | 1.16M | return false; |
164 | 1.22M | } |
165 | | |
166 | | void OutputDevice::SetWaveLineColors(Color const& rColor, tools::Long nLineWidth) |
167 | 624 | { |
168 | | // On printers that output pixel via DrawRect() |
169 | 624 | if (nLineWidth > 1) |
170 | 36 | { |
171 | 36 | if (mbLineColor || mbInitLineColor) |
172 | 36 | { |
173 | 36 | mpGraphics->SetLineColor(); |
174 | 36 | mbInitLineColor = true; |
175 | 36 | } |
176 | | |
177 | 36 | mpGraphics->SetFillColor( rColor ); |
178 | 36 | mbInitFillColor = true; |
179 | 36 | } |
180 | 588 | else |
181 | 588 | { |
182 | 588 | mpGraphics->SetLineColor( rColor ); |
183 | 588 | mbInitLineColor = true; |
184 | 588 | } |
185 | 624 | } |
186 | | |
187 | | Size OutputDevice::GetWaveLineSize(tools::Long nLineWidth) const |
188 | 624 | { |
189 | 624 | if (nLineWidth > 1) |
190 | 36 | return Size(nLineWidth, ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY); |
191 | | |
192 | 588 | return Size(1, 1); |
193 | 624 | } |
194 | | |
195 | | void OutputDevice::ImplDrawWaveLine( tools::Long nBaseX, tools::Long nBaseY, |
196 | | tools::Long nDistX, tools::Long nDistY, |
197 | | tools::Long nWidth, tools::Long nHeight, |
198 | | tools::Long nLineWidth, Degree10 nOrientation, |
199 | | const Color& rColor ) |
200 | 624 | { |
201 | 624 | if ( !nHeight ) |
202 | 0 | return; |
203 | | |
204 | 624 | tools::Long nStartX = nBaseX + nDistX; |
205 | 624 | tools::Long nStartY = nBaseY + nDistY; |
206 | | |
207 | | // If the height is 1 pixel, it's enough output a line |
208 | 624 | if ( (nLineWidth == 1) && (nHeight == 1) ) |
209 | 0 | { |
210 | 0 | mpGraphics->SetLineColor( rColor ); |
211 | 0 | mbInitLineColor = true; |
212 | |
|
213 | 0 | tools::Long nEndX = nStartX+nWidth; |
214 | 0 | tools::Long nEndY = nStartY; |
215 | 0 | if ( nOrientation ) |
216 | 0 | { |
217 | 0 | Point aOriginPt( nBaseX, nBaseY ); |
218 | 0 | aOriginPt.RotateAround( nStartX, nStartY, nOrientation ); |
219 | 0 | aOriginPt.RotateAround( nEndX, nEndY, nOrientation ); |
220 | 0 | } |
221 | 0 | mpGraphics->DrawLine( nStartX, nStartY, nEndX, nEndY, *this ); |
222 | 0 | } |
223 | 624 | else |
224 | 624 | { |
225 | 624 | tools::Long nCurX = nStartX; |
226 | 624 | tools::Long nCurY = nStartY; |
227 | 624 | tools::Long nDiffX = 2; |
228 | 624 | tools::Long nDiffY = nHeight-1; |
229 | 624 | tools::Long nCount = nWidth; |
230 | 624 | tools::Long nOffY = -1; |
231 | | |
232 | 624 | SetWaveLineColors(rColor, nLineWidth); |
233 | 624 | Size aSize(GetWaveLineSize(nLineWidth)); |
234 | | |
235 | 624 | tools::Long nPixWidth = aSize.Width(); |
236 | 624 | tools::Long nPixHeight = aSize.Height(); |
237 | | |
238 | 624 | if ( !nDiffY ) |
239 | 24 | { |
240 | 34.7k | while ( nWidth ) |
241 | 34.7k | { |
242 | 34.7k | ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nLineWidth, nOrientation, |
243 | 34.7k | mpGraphics, *this, |
244 | 34.7k | nPixWidth, nPixHeight ); |
245 | 34.7k | nCurX++; |
246 | 34.7k | nWidth--; |
247 | 34.7k | } |
248 | 24 | } |
249 | 600 | else |
250 | 600 | { |
251 | 600 | nCurY += nDiffY; |
252 | 600 | tools::Long nFreq = nCount / (nDiffX+nDiffY); |
253 | 218k | while ( nFreq-- ) |
254 | 217k | { |
255 | 873k | for( tools::Long i = nDiffY; i; --i ) |
256 | 655k | { |
257 | 655k | ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nLineWidth, nOrientation, |
258 | 655k | mpGraphics, *this, |
259 | 655k | nPixWidth, nPixHeight ); |
260 | 655k | nCurX++; |
261 | 655k | nCurY += nOffY; |
262 | 655k | } |
263 | 653k | for( tools::Long i = nDiffX; i; --i ) |
264 | 435k | { |
265 | 435k | ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nLineWidth, nOrientation, |
266 | 435k | mpGraphics, *this, |
267 | 435k | nPixWidth, nPixHeight ); |
268 | 435k | nCurX++; |
269 | 435k | } |
270 | 217k | nOffY = -nOffY; |
271 | 217k | } |
272 | 600 | nFreq = nCount % (nDiffX+nDiffY); |
273 | 600 | if ( nFreq ) |
274 | 342 | { |
275 | 103k | for( tools::Long i = nDiffY; i && nFreq; --i, --nFreq ) |
276 | 103k | { |
277 | 103k | ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nLineWidth, nOrientation, |
278 | 103k | mpGraphics, *this, |
279 | 103k | nPixWidth, nPixHeight ); |
280 | 103k | nCurX++; |
281 | 103k | nCurY += nOffY; |
282 | | |
283 | 103k | } |
284 | 444 | for( tools::Long i = nDiffX; i && nFreq; --i, --nFreq ) |
285 | 102 | { |
286 | 102 | ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nLineWidth, nOrientation, |
287 | 102 | mpGraphics, *this, |
288 | 102 | nPixWidth, nPixHeight ); |
289 | 102 | nCurX++; |
290 | 102 | } |
291 | 342 | } |
292 | 600 | } |
293 | 624 | } |
294 | 624 | } |
295 | | |
296 | | void OutputDevice::ImplDrawWaveTextLine( tools::Long nBaseX, tools::Long nBaseY, |
297 | | tools::Long nDistX, tools::Long nDistY, |
298 | | tools::Long nWidth, tools::Long nLayoutWidth, |
299 | | FontLineStyle eTextLine, |
300 | | Color aColor, |
301 | | bool bIsAbove ) |
302 | 58.2k | { |
303 | 58.2k | static bool bFuzzing = comphelper::IsFuzzing(); |
304 | 58.2k | if (bFuzzing && nLayoutWidth > 10000) |
305 | 57.6k | { |
306 | 57.6k | SAL_WARN("vcl.gdi", "drawLine, skipping suspicious WaveTextLine of length: " |
307 | 57.6k | << nLayoutWidth << " for fuzzing performance"); |
308 | 57.6k | return; |
309 | 57.6k | } |
310 | | |
311 | 589 | LogicalFontInstance* pFontInstance = mpFontInstance.get(); |
312 | 589 | tools::Long nLineHeight; |
313 | 589 | tools::Long nLinePos; |
314 | | |
315 | 589 | if ( bIsAbove ) |
316 | 547 | { |
317 | 547 | nLineHeight = pFontInstance->mxFontMetric->GetAboveWavelineUnderlineSize(); |
318 | 547 | nLinePos = pFontInstance->mxFontMetric->GetAboveWavelineUnderlineOffset(); |
319 | 547 | } |
320 | 42 | else |
321 | 42 | { |
322 | 42 | nLineHeight = pFontInstance->mxFontMetric->GetWavelineUnderlineSize(); |
323 | 42 | nLinePos = pFontInstance->mxFontMetric->GetWavelineUnderlineOffset(); |
324 | 42 | } |
325 | 589 | if ( (eTextLine == LINESTYLE_SMALLWAVE) && (nLineHeight > 3) ) |
326 | 388 | nLineHeight = 3; |
327 | | |
328 | 589 | tools::Long nLineWidth = mnDPIX / 300; |
329 | 589 | if ( !nLineWidth ) |
330 | 589 | nLineWidth = 1; |
331 | | |
332 | 589 | if ( eTextLine == LINESTYLE_BOLDWAVE ) |
333 | 36 | nLineWidth *= 2; |
334 | | |
335 | 589 | nLinePos += nDistY - (nLineHeight / 2); |
336 | | |
337 | 589 | tools::Long nLineWidthHeight = ((nLineWidth * mnDPIX) + (mnDPIY / 2)) / mnDPIY; |
338 | 589 | if ( eTextLine == LINESTYLE_DOUBLEWAVE ) |
339 | 35 | { |
340 | 35 | tools::Long nOrgLineHeight = nLineHeight; |
341 | 35 | nLineHeight /= 3; |
342 | 35 | if ( nLineHeight < 2 ) |
343 | 10 | { |
344 | 10 | if ( nOrgLineHeight > 1 ) |
345 | 10 | nLineHeight = 2; |
346 | 0 | else |
347 | 0 | nLineHeight = 1; |
348 | 10 | } |
349 | | |
350 | 35 | tools::Long nLineDY = nOrgLineHeight-(nLineHeight*2); |
351 | 35 | if ( nLineDY < nLineWidthHeight ) |
352 | 10 | nLineDY = nLineWidthHeight; |
353 | | |
354 | 35 | tools::Long nLineDY2 = nLineDY/2; |
355 | 35 | if ( !nLineDY2 ) |
356 | 10 | nLineDY2 = 1; |
357 | | |
358 | 35 | nLinePos -= nLineWidthHeight-nLineDY2; |
359 | 35 | ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight, |
360 | 35 | nLineWidth, mpFontInstance->mnOrientation, aColor ); |
361 | 35 | nLinePos += nLineWidthHeight+nLineDY; |
362 | 35 | ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight, |
363 | 35 | nLineWidth, mpFontInstance->mnOrientation, aColor ); |
364 | 35 | } |
365 | 554 | else |
366 | 554 | { |
367 | 554 | nLinePos -= nLineWidthHeight/2; |
368 | 554 | ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight, |
369 | 554 | nLineWidth, mpFontInstance->mnOrientation, aColor ); |
370 | 554 | } |
371 | 589 | } |
372 | | |
373 | | void OutputDevice::ImplDrawStraightTextLine( tools::Long nBaseX, tools::Long nBaseY, |
374 | | tools::Long nDistX, tools::Long nDistY, tools::Long nWidth, |
375 | | FontLineStyle eTextLine, |
376 | | Color aColor, |
377 | | bool bIsAbove ) |
378 | 298k | { |
379 | 298k | static bool bFuzzing = comphelper::IsFuzzing(); |
380 | 298k | if (bFuzzing && nWidth > 25000) |
381 | 16.4k | { |
382 | 16.4k | SAL_WARN("vcl.gdi", "drawLine, skipping suspicious TextLine of length: " |
383 | 16.4k | << nWidth << " for fuzzing performance"); |
384 | 16.4k | return; |
385 | 16.4k | } |
386 | | |
387 | 282k | LogicalFontInstance* pFontInstance = mpFontInstance.get(); |
388 | 282k | tools::Long nLineHeight = 0; |
389 | 282k | tools::Long nLinePos = 0; |
390 | 282k | tools::Long nLinePos2 = 0; |
391 | | |
392 | 282k | const tools::Long nY = nDistY; |
393 | | |
394 | 282k | if ( eTextLine > UNDERLINE_LAST ) |
395 | 135k | eTextLine = LINESTYLE_SINGLE; |
396 | | |
397 | 282k | switch ( eTextLine ) |
398 | 282k | { |
399 | 136k | case LINESTYLE_SINGLE: |
400 | 139k | case LINESTYLE_DOTTED: |
401 | 140k | case LINESTYLE_DASH: |
402 | 141k | case LINESTYLE_LONGDASH: |
403 | 141k | case LINESTYLE_DASHDOT: |
404 | 141k | case LINESTYLE_DASHDOTDOT: |
405 | 141k | if ( bIsAbove ) |
406 | 65.4k | { |
407 | 65.4k | nLineHeight = pFontInstance->mxFontMetric->GetAboveUnderlineSize(); |
408 | 65.4k | nLinePos = nY + pFontInstance->mxFontMetric->GetAboveUnderlineOffset(); |
409 | 65.4k | } |
410 | 76.0k | else |
411 | 76.0k | { |
412 | 76.0k | nLineHeight = pFontInstance->mxFontMetric->GetUnderlineSize(); |
413 | 76.0k | nLinePos = nY + pFontInstance->mxFontMetric->GetUnderlineOffset(); |
414 | 76.0k | } |
415 | 141k | break; |
416 | 3 | case LINESTYLE_BOLD: |
417 | 1.95k | case LINESTYLE_BOLDDOTTED: |
418 | 2.03k | case LINESTYLE_BOLDDASH: |
419 | 2.73k | case LINESTYLE_BOLDLONGDASH: |
420 | 4.79k | case LINESTYLE_BOLDDASHDOT: |
421 | 10.2k | case LINESTYLE_BOLDDASHDOTDOT: |
422 | 10.2k | if ( bIsAbove ) |
423 | 4.02k | { |
424 | 4.02k | nLineHeight = pFontInstance->mxFontMetric->GetAboveBoldUnderlineSize(); |
425 | 4.02k | nLinePos = nY + pFontInstance->mxFontMetric->GetAboveBoldUnderlineOffset(); |
426 | 4.02k | } |
427 | 6.22k | else |
428 | 6.22k | { |
429 | 6.22k | nLineHeight = pFontInstance->mxFontMetric->GetBoldUnderlineSize(); |
430 | 6.22k | nLinePos = nY + pFontInstance->mxFontMetric->GetBoldUnderlineOffset(); |
431 | 6.22k | } |
432 | 10.2k | break; |
433 | 615 | case LINESTYLE_DOUBLE: |
434 | 615 | if ( bIsAbove ) |
435 | 274 | { |
436 | 274 | nLineHeight = pFontInstance->mxFontMetric->GetAboveDoubleUnderlineSize(); |
437 | 274 | nLinePos = nY + pFontInstance->mxFontMetric->GetAboveDoubleUnderlineOffset1(); |
438 | 274 | nLinePos2 = nY + pFontInstance->mxFontMetric->GetAboveDoubleUnderlineOffset2(); |
439 | 274 | } |
440 | 341 | else |
441 | 341 | { |
442 | 341 | nLineHeight = pFontInstance->mxFontMetric->GetDoubleUnderlineSize(); |
443 | 341 | nLinePos = nY + pFontInstance->mxFontMetric->GetDoubleUnderlineOffset1(); |
444 | 341 | nLinePos2 = nY + pFontInstance->mxFontMetric->GetDoubleUnderlineOffset2(); |
445 | 341 | } |
446 | 615 | break; |
447 | 130k | default: |
448 | 130k | break; |
449 | 282k | } |
450 | | |
451 | 282k | if ( !nLineHeight ) |
452 | 130k | return; |
453 | | |
454 | 152k | if ( mbLineColor || mbInitLineColor ) |
455 | 147k | { |
456 | 147k | mpGraphics->SetLineColor(); |
457 | 147k | mbInitLineColor = true; |
458 | 147k | } |
459 | 152k | mpGraphics->SetFillColor( aColor ); |
460 | 152k | mbInitFillColor = true; |
461 | | |
462 | 152k | tools::Long nLeft = nDistX; |
463 | | |
464 | 152k | switch ( eTextLine ) |
465 | 152k | { |
466 | 136k | case LINESTYLE_SINGLE: |
467 | 136k | case LINESTYLE_BOLD: |
468 | 136k | ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); |
469 | 136k | break; |
470 | 615 | case LINESTYLE_DOUBLE: |
471 | 615 | ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); |
472 | 615 | ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight ); |
473 | 615 | break; |
474 | 3.26k | case LINESTYLE_DOTTED: |
475 | 5.22k | case LINESTYLE_BOLDDOTTED: |
476 | 5.22k | { |
477 | 5.22k | tools::Long nDotWidth = nLineHeight*mnDPIY; |
478 | 5.22k | nDotWidth += mnDPIY/2; |
479 | 5.22k | nDotWidth /= mnDPIY; |
480 | | |
481 | 5.22k | tools::Long nTempWidth = nDotWidth; |
482 | 5.22k | tools::Long nEnd = nLeft+nWidth; |
483 | 1.82M | while ( nLeft < nEnd ) |
484 | 1.82M | { |
485 | 1.82M | if ( nLeft+nTempWidth > nEnd ) |
486 | 1.69k | nTempWidth = nEnd-nLeft; |
487 | | |
488 | 1.82M | ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight ); |
489 | 1.82M | nLeft += nDotWidth*2; |
490 | 1.82M | } |
491 | 5.22k | } |
492 | 5.22k | break; |
493 | 711 | case LINESTYLE_DASH: |
494 | 1.52k | case LINESTYLE_LONGDASH: |
495 | 1.59k | case LINESTYLE_BOLDDASH: |
496 | 2.30k | case LINESTYLE_BOLDLONGDASH: |
497 | 2.30k | { |
498 | 2.30k | tools::Long nDotWidth = nLineHeight*mnDPIY; |
499 | 2.30k | nDotWidth += mnDPIY/2; |
500 | 2.30k | nDotWidth /= mnDPIY; |
501 | | |
502 | 2.30k | tools::Long nMinDashWidth; |
503 | 2.30k | tools::Long nMinSpaceWidth; |
504 | 2.30k | tools::Long nSpaceWidth; |
505 | 2.30k | tools::Long nDashWidth; |
506 | 2.30k | if ( (eTextLine == LINESTYLE_LONGDASH) || |
507 | 1.49k | (eTextLine == LINESTYLE_BOLDLONGDASH) ) |
508 | 1.52k | { |
509 | 1.52k | nMinDashWidth = nDotWidth*6; |
510 | 1.52k | nMinSpaceWidth = nDotWidth*2; |
511 | 1.52k | nDashWidth = 200; |
512 | 1.52k | nSpaceWidth = 100; |
513 | 1.52k | } |
514 | 783 | else |
515 | 783 | { |
516 | 783 | nMinDashWidth = nDotWidth*4; |
517 | 783 | nMinSpaceWidth = (nDotWidth*150)/100; |
518 | 783 | nDashWidth = 100; |
519 | 783 | nSpaceWidth = 50; |
520 | 783 | } |
521 | 2.30k | nDashWidth = o3tl::convert(nDashWidth * mnDPIX, o3tl::Length::mm100, o3tl::Length::in); |
522 | 2.30k | nSpaceWidth = o3tl::convert(nSpaceWidth * mnDPIX, o3tl::Length::mm100, o3tl::Length::in); |
523 | | // DashWidth will be increased if the line is getting too thick |
524 | | // in proportion to the line's length |
525 | 2.30k | if ( nDashWidth < nMinDashWidth ) |
526 | 1.61k | nDashWidth = nMinDashWidth; |
527 | 2.30k | if ( nSpaceWidth < nMinSpaceWidth ) |
528 | 1.60k | nSpaceWidth = nMinSpaceWidth; |
529 | | |
530 | 2.30k | tools::Long nTempWidth = nDashWidth; |
531 | 2.30k | tools::Long nEnd = nLeft+nWidth; |
532 | 122k | while ( nLeft < nEnd ) |
533 | 120k | { |
534 | 120k | if ( nLeft+nTempWidth > nEnd ) |
535 | 1.40k | nTempWidth = nEnd-nLeft; |
536 | 120k | ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight ); |
537 | 120k | nLeft += nDashWidth+nSpaceWidth; |
538 | 120k | } |
539 | 2.30k | } |
540 | 2.30k | break; |
541 | 3 | case LINESTYLE_DASHDOT: |
542 | 2.06k | case LINESTYLE_BOLDDASHDOT: |
543 | 2.06k | { |
544 | 2.06k | tools::Long nDotWidth = nLineHeight*mnDPIY; |
545 | 2.06k | nDotWidth += mnDPIY/2; |
546 | 2.06k | nDotWidth /= mnDPIY; |
547 | | |
548 | 2.06k | tools::Long nDashWidth = o3tl::convert(100 * mnDPIX, o3tl::Length::mm100, o3tl::Length::in); |
549 | 2.06k | tools::Long nMinDashWidth = nDotWidth*4; |
550 | | // DashWidth will be increased if the line is getting too thick |
551 | | // in proportion to the line's length |
552 | 2.06k | if ( nDashWidth < nMinDashWidth ) |
553 | 2.06k | nDashWidth = nMinDashWidth; |
554 | | |
555 | 2.06k | tools::Long nTempDotWidth = nDotWidth; |
556 | 2.06k | tools::Long nTempDashWidth = nDashWidth; |
557 | 2.06k | tools::Long nEnd = nLeft+nWidth; |
558 | 246k | while ( nLeft < nEnd ) |
559 | 244k | { |
560 | 244k | if ( nLeft+nTempDotWidth > nEnd ) |
561 | 528 | nTempDotWidth = nEnd-nLeft; |
562 | | |
563 | 244k | ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight ); |
564 | 244k | nLeft += nDotWidth*2; |
565 | 244k | if ( nLeft > nEnd ) |
566 | 818 | break; |
567 | | |
568 | 243k | if ( nLeft+nTempDashWidth > nEnd ) |
569 | 793 | nTempDashWidth = nEnd-nLeft; |
570 | | |
571 | 243k | ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight ); |
572 | 243k | nLeft += nDashWidth+nDotWidth; |
573 | 243k | } |
574 | 2.06k | } |
575 | 2.06k | break; |
576 | 231 | case LINESTYLE_DASHDOTDOT: |
577 | 5.68k | case LINESTYLE_BOLDDASHDOTDOT: |
578 | 5.68k | { |
579 | 5.68k | tools::Long nDotWidth = nLineHeight*mnDPIY; |
580 | 5.68k | nDotWidth += mnDPIY/2; |
581 | 5.68k | nDotWidth /= mnDPIY; |
582 | | |
583 | 5.68k | tools::Long nDashWidth = o3tl::convert(100 * mnDPIX, o3tl::Length::mm100, o3tl::Length::in); |
584 | 5.68k | tools::Long nMinDashWidth = nDotWidth*4; |
585 | | // DashWidth will be increased if the line is getting too thick |
586 | | // in proportion to the line's length |
587 | 5.68k | if ( nDashWidth < nMinDashWidth ) |
588 | 5.66k | nDashWidth = nMinDashWidth; |
589 | | |
590 | 5.68k | tools::Long nTempDotWidth = nDotWidth; |
591 | 5.68k | tools::Long nTempDashWidth = nDashWidth; |
592 | 5.68k | tools::Long nEnd = nLeft+nWidth; |
593 | 14.9k | while ( nLeft < nEnd ) |
594 | 13.4k | { |
595 | 13.4k | if ( nLeft+nTempDotWidth > nEnd ) |
596 | 2.93k | nTempDotWidth = nEnd-nLeft; |
597 | | |
598 | 13.4k | ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight ); |
599 | 13.4k | nLeft += nDotWidth*2; |
600 | 13.4k | if ( nLeft > nEnd ) |
601 | 3.62k | break; |
602 | | |
603 | 9.85k | if ( nLeft+nTempDotWidth > nEnd ) |
604 | 434 | nTempDotWidth = nEnd-nLeft; |
605 | | |
606 | 9.85k | ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight ); |
607 | 9.85k | nLeft += nDotWidth*2; |
608 | 9.85k | if ( nLeft > nEnd ) |
609 | 607 | break; |
610 | | |
611 | 9.24k | if ( nLeft+nTempDashWidth > nEnd ) |
612 | 1.41k | nTempDashWidth = nEnd-nLeft; |
613 | | |
614 | 9.24k | ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight ); |
615 | 9.24k | nLeft += nDashWidth+nDotWidth; |
616 | 9.24k | } |
617 | 5.68k | } |
618 | 5.68k | break; |
619 | 0 | default: |
620 | 0 | break; |
621 | 152k | } |
622 | 152k | } |
623 | | |
624 | | void OutputDevice::ImplDrawStrikeoutLine( tools::Long nBaseX, tools::Long nBaseY, |
625 | | tools::Long nDistX, tools::Long nDistY, tools::Long nWidth, |
626 | | FontStrikeout eStrikeout, |
627 | | Color aColor ) |
628 | 170k | { |
629 | 170k | LogicalFontInstance* pFontInstance = mpFontInstance.get(); |
630 | 170k | tools::Long nLineHeight = 0; |
631 | 170k | tools::Long nLinePos = 0; |
632 | 170k | tools::Long nLinePos2 = 0; |
633 | | |
634 | 170k | tools::Long nY = nDistY; |
635 | | |
636 | 170k | if ( eStrikeout > STRIKEOUT_LAST ) |
637 | 153k | eStrikeout = STRIKEOUT_SINGLE; |
638 | | |
639 | 170k | switch ( eStrikeout ) |
640 | 170k | { |
641 | 153k | case STRIKEOUT_SINGLE: |
642 | 153k | nLineHeight = pFontInstance->mxFontMetric->GetStrikeoutSize(); |
643 | 153k | nLinePos = nY + pFontInstance->mxFontMetric->GetStrikeoutOffset(); |
644 | 153k | break; |
645 | 1 | case STRIKEOUT_BOLD: |
646 | 1 | nLineHeight = pFontInstance->mxFontMetric->GetBoldStrikeoutSize(); |
647 | 1 | nLinePos = nY + pFontInstance->mxFontMetric->GetBoldStrikeoutOffset(); |
648 | 1 | break; |
649 | 285 | case STRIKEOUT_DOUBLE: |
650 | 285 | nLineHeight = pFontInstance->mxFontMetric->GetDoubleStrikeoutSize(); |
651 | 285 | nLinePos = nY + pFontInstance->mxFontMetric->GetDoubleStrikeoutOffset1(); |
652 | 285 | nLinePos2 = nY + pFontInstance->mxFontMetric->GetDoubleStrikeoutOffset2(); |
653 | 285 | break; |
654 | 16.9k | default: |
655 | 16.9k | break; |
656 | 170k | } |
657 | | |
658 | 170k | if ( !nLineHeight ) |
659 | 16.9k | return; |
660 | | |
661 | 153k | if ( mbLineColor || mbInitLineColor ) |
662 | 152k | { |
663 | 152k | mpGraphics->SetLineColor(); |
664 | 152k | mbInitLineColor = true; |
665 | 152k | } |
666 | 153k | mpGraphics->SetFillColor( aColor ); |
667 | 153k | mbInitFillColor = true; |
668 | | |
669 | 153k | const tools::Long& nLeft = nDistX; |
670 | | |
671 | 153k | switch ( eStrikeout ) |
672 | 153k | { |
673 | 153k | case STRIKEOUT_SINGLE: |
674 | 153k | case STRIKEOUT_BOLD: |
675 | 153k | ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); |
676 | 153k | break; |
677 | 285 | case STRIKEOUT_DOUBLE: |
678 | 285 | ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight ); |
679 | 285 | ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight ); |
680 | 285 | break; |
681 | 0 | default: |
682 | 0 | break; |
683 | 153k | } |
684 | 153k | } |
685 | | |
686 | | void OutputDevice::ImplDrawStrikeoutChar( tools::Long nBaseX, tools::Long nBaseY, |
687 | | tools::Long nDistX, tools::Long nDistY, tools::Long nWidth, |
688 | | FontStrikeout eStrikeout, |
689 | | Color aColor ) |
690 | 7.99k | { |
691 | | // See qadevOOo/testdocs/StrikeThrough.odt for examples if you need |
692 | | // to tweak this |
693 | 7.99k | if (!nWidth) |
694 | 604 | return; |
695 | | |
696 | | // prepare string for strikeout measurement |
697 | 7.38k | const char cStrikeoutChar = eStrikeout == STRIKEOUT_SLASH ? '/' : 'X'; |
698 | 7.38k | static const int nTestStrLen = 4; |
699 | 7.38k | static const int nMaxStrikeStrLen = 2048; |
700 | 7.38k | sal_Unicode aChars[nMaxStrikeStrLen+1]; // +1 for valgrind... |
701 | | |
702 | 36.9k | for( int i = 0; i < nTestStrLen; ++i) |
703 | 29.5k | aChars[i] = cStrikeoutChar; |
704 | | |
705 | 7.38k | const OUString aStrikeoutTest(aChars, nTestStrLen); |
706 | | |
707 | | // calculate approximation of strikeout atom size |
708 | 7.38k | tools::Long nStrikeoutWidth = 0; |
709 | 7.38k | std::unique_ptr<SalLayout> pLayout = ImplLayout( aStrikeoutTest, 0, nTestStrLen ); |
710 | 7.38k | if( pLayout ) |
711 | 7.38k | { |
712 | 7.38k | nStrikeoutWidth = pLayout->GetTextWidth() / nTestStrLen; |
713 | 7.38k | } |
714 | 7.38k | if( nStrikeoutWidth <= 0 ) // sanity check |
715 | 132 | return; |
716 | | |
717 | 7.25k | int nStrikeStrLen = (nWidth+(nStrikeoutWidth-1)) / nStrikeoutWidth; |
718 | 7.25k | if( nStrikeStrLen > nMaxStrikeStrLen ) |
719 | 1.18k | nStrikeStrLen = nMaxStrikeStrLen; |
720 | 6.07k | else if (nStrikeStrLen < 0) |
721 | 14 | nStrikeStrLen = 0; |
722 | | |
723 | | // build the strikeout string |
724 | 2.95M | for( int i = nTestStrLen; i < nStrikeStrLen; ++i) |
725 | 2.95M | aChars[i] = cStrikeoutChar; |
726 | | |
727 | 7.25k | const OUString aStrikeoutText(aChars, nStrikeStrLen); |
728 | | |
729 | 7.25k | if( mpFontInstance->mnOrientation ) |
730 | 4.65k | { |
731 | 4.65k | Point aOriginPt(0, 0); |
732 | 4.65k | aOriginPt.RotateAround( nDistX, nDistY, mpFontInstance->mnOrientation ); |
733 | 4.65k | } |
734 | | |
735 | 7.25k | nBaseX += nDistX; |
736 | 7.25k | nBaseY += nDistY; |
737 | | |
738 | | // strikeout text has to be left aligned |
739 | 7.25k | vcl::text::ComplexTextLayoutFlags nOrigTLM = mnTextLayoutMode; |
740 | 7.25k | mnTextLayoutMode = vcl::text::ComplexTextLayoutFlags::BiDiStrong; |
741 | 7.25k | pLayout = ImplLayout( aStrikeoutText, 0, aStrikeoutText.getLength() ); |
742 | 7.25k | mnTextLayoutMode = nOrigTLM; |
743 | | |
744 | 7.25k | if( !pLayout ) |
745 | 0 | return; |
746 | | |
747 | | // draw the strikeout text |
748 | 7.25k | const Color aOldColor = GetTextColor(); |
749 | 7.25k | SetTextColor( aColor ); |
750 | 7.25k | ImplInitTextColor(); |
751 | | |
752 | 7.25k | pLayout->DrawBase() = basegfx::B2DPoint(nBaseX + mnTextOffX, nBaseY + mnTextOffY); |
753 | | |
754 | 7.25k | tools::Rectangle aPixelRect; |
755 | 7.25k | aPixelRect.SetLeft( nBaseX+mnTextOffX ); |
756 | 7.25k | aPixelRect.SetRight( aPixelRect.Left()+nWidth ); |
757 | 7.25k | aPixelRect.SetBottom( nBaseY+mpFontInstance->mxFontMetric->GetDescent() ); |
758 | 7.25k | aPixelRect.SetTop( nBaseY-mpFontInstance->mxFontMetric->GetAscent() ); |
759 | | |
760 | 7.25k | if (mpFontInstance->mnOrientation) |
761 | 4.65k | { |
762 | 4.65k | tools::Polygon aPoly( aPixelRect ); |
763 | 4.65k | aPoly.Rotate( Point(nBaseX+mnTextOffX, nBaseY+mnTextOffY), mpFontInstance->mnOrientation); |
764 | 4.65k | aPixelRect = aPoly.GetBoundRect(); |
765 | 4.65k | } |
766 | | |
767 | 7.25k | Push( vcl::PushFlags::CLIPREGION ); |
768 | 7.25k | IntersectClipRegion( PixelToLogic(aPixelRect) ); |
769 | 7.25k | if( mbInitClipRegion ) |
770 | 7.25k | InitClipRegion(); |
771 | | |
772 | 7.25k | pLayout->DrawText( *mpGraphics ); |
773 | | |
774 | 7.25k | Pop(); |
775 | | |
776 | 7.25k | SetTextColor( aOldColor ); |
777 | 7.25k | ImplInitTextColor(); |
778 | 7.25k | } |
779 | | |
780 | | void OutputDevice::ImplDrawTextLine( tools::Long nX, tools::Long nY, |
781 | | tools::Long nDistX, double nWidth, |
782 | | double nLayoutWidth, |
783 | | FontStrikeout eStrikeout, |
784 | | FontLineStyle eUnderline, |
785 | | FontLineStyle eOverline, |
786 | | bool bUnderlineAbove ) |
787 | 182k | { |
788 | 182k | if ( !nWidth ) |
789 | 3.60k | return; |
790 | | |
791 | 178k | Color aStrikeoutColor = GetTextColor(); |
792 | 178k | Color aUnderlineColor = GetTextLineColor(); |
793 | 178k | Color aOverlineColor = GetOverlineColor(); |
794 | 178k | bool bStrikeoutDone = false; |
795 | 178k | bool bUnderlineDone = false; |
796 | 178k | bool bOverlineDone = false; |
797 | | |
798 | 178k | if ( IsRTLEnabled() ) |
799 | 0 | { |
800 | 0 | tools::Long nXAdd = nWidth - nDistX; |
801 | 0 | if( mpFontInstance->mnOrientation ) |
802 | 0 | nXAdd = basegfx::fround<tools::Long>( nXAdd * cos( toRadians(mpFontInstance->mnOrientation) ) ); |
803 | |
|
804 | 0 | nX += nXAdd - 1; |
805 | 0 | } |
806 | | |
807 | 178k | if ( !IsTextLineColor() ) |
808 | 155k | aUnderlineColor = GetTextColor(); |
809 | | |
810 | 178k | if ( !IsOverlineColor() ) |
811 | 155k | aOverlineColor = GetTextColor(); |
812 | | |
813 | 178k | if ( (eUnderline == LINESTYLE_SMALLWAVE) || |
814 | 178k | (eUnderline == LINESTYLE_WAVE) || |
815 | 178k | (eUnderline == LINESTYLE_DOUBLEWAVE) || |
816 | 178k | (eUnderline == LINESTYLE_BOLDWAVE) ) |
817 | 44 | { |
818 | 44 | ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, nLayoutWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); |
819 | 44 | bUnderlineDone = true; |
820 | 44 | } |
821 | 178k | if ( (eOverline == LINESTYLE_SMALLWAVE) || |
822 | 121k | (eOverline == LINESTYLE_WAVE) || |
823 | 121k | (eOverline == LINESTYLE_DOUBLEWAVE) || |
824 | 120k | (eOverline == LINESTYLE_BOLDWAVE) ) |
825 | 58.1k | { |
826 | 58.1k | ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, nLayoutWidth, eOverline, aOverlineColor, true ); |
827 | 58.1k | bOverlineDone = true; |
828 | 58.1k | } |
829 | | |
830 | 178k | if ( (eStrikeout == STRIKEOUT_SLASH) || |
831 | 173k | (eStrikeout == STRIKEOUT_X) ) |
832 | 7.99k | { |
833 | 7.99k | ImplDrawStrikeoutChar( nX, nY, nDistX, 0, nWidth, eStrikeout, aStrikeoutColor ); |
834 | 7.99k | bStrikeoutDone = true; |
835 | 7.99k | } |
836 | | |
837 | 178k | if ( !bUnderlineDone ) |
838 | 178k | ImplDrawStraightTextLine( nX, nY, nDistX, 0, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); |
839 | | |
840 | 178k | if ( !bOverlineDone ) |
841 | 120k | ImplDrawStraightTextLine( nX, nY, nDistX, 0, nWidth, eOverline, aOverlineColor, true ); |
842 | | |
843 | 178k | if ( !bStrikeoutDone ) |
844 | 170k | ImplDrawStrikeoutLine( nX, nY, nDistX, 0, nWidth, eStrikeout, aStrikeoutColor ); |
845 | 178k | } |
846 | | |
847 | | void OutputDevice::ImplDrawTextLines( SalLayout& rSalLayout, FontStrikeout eStrikeout, |
848 | | FontLineStyle eUnderline, FontLineStyle eOverline, |
849 | | bool bWordLine, bool bUnderlineAbove ) |
850 | 14.5k | { |
851 | 14.5k | double nLayoutWidth = rSalLayout.GetTextWidth(); |
852 | 14.5k | if( bWordLine ) |
853 | 9.93k | { |
854 | | // draw everything relative to the layout base point |
855 | 9.93k | const basegfx::B2DPoint aStartPt = rSalLayout.DrawBase(); |
856 | | |
857 | | // calculate distance of each word from the base point |
858 | 9.93k | basegfx::B2DPoint aPos; |
859 | 9.93k | double nDist = 0; |
860 | 9.93k | double nWidth = 0; |
861 | 9.93k | const GlyphItem* pGlyph; |
862 | 9.93k | int nStart = 0; |
863 | 9.52M | while (rSalLayout.GetNextGlyph(&pGlyph, aPos, nStart)) |
864 | 9.51M | { |
865 | | // calculate the boundaries of each word |
866 | 9.51M | if (!pGlyph->IsSpacing()) |
867 | 9.14M | { |
868 | 9.14M | if( !nWidth ) |
869 | 214k | { |
870 | | // get the distance to the base point (as projected to baseline) |
871 | 214k | nDist = aPos.getX() - aStartPt.getX(); |
872 | 214k | if( mpFontInstance->mnOrientation ) |
873 | 194k | { |
874 | 194k | const double nDY = aPos.getY() - aStartPt.getY(); |
875 | 194k | const double fRad = toRadians(mpFontInstance->mnOrientation); |
876 | 194k | nDist = basegfx::fround<tools::Long>(nDist * cos(fRad) - nDY * sin(fRad)); |
877 | 194k | } |
878 | 214k | } |
879 | | |
880 | | // update the length of the textline |
881 | 9.14M | nWidth += pGlyph->newWidth(); |
882 | 9.14M | } |
883 | 369k | else if( nWidth > 0 ) |
884 | 169k | { |
885 | | // draw the textline for each word |
886 | 169k | ImplDrawTextLine( aStartPt.getX(), aStartPt.getY(), nDist, nWidth, nLayoutWidth, |
887 | 169k | eStrikeout, eUnderline, eOverline, bUnderlineAbove ); |
888 | 169k | nWidth = 0; |
889 | 169k | } |
890 | 9.51M | } |
891 | | |
892 | | // draw textline for the last word |
893 | 9.93k | if( nWidth > 0 ) |
894 | 6.71k | { |
895 | 6.71k | ImplDrawTextLine( aStartPt.getX(), aStartPt.getY(), nDist, nWidth, nLayoutWidth, |
896 | 6.71k | eStrikeout, eUnderline, eOverline, bUnderlineAbove ); |
897 | 6.71k | } |
898 | 9.93k | } |
899 | 4.64k | else |
900 | 4.64k | { |
901 | 4.64k | basegfx::B2DPoint aStartPt = rSalLayout.GetDrawPosition(); |
902 | 4.64k | ImplDrawTextLine( aStartPt.getX(), aStartPt.getY(), 0, |
903 | 4.64k | nLayoutWidth, nLayoutWidth, |
904 | 4.64k | eStrikeout, eUnderline, eOverline, bUnderlineAbove ); |
905 | 4.64k | } |
906 | 14.5k | } |
907 | | |
908 | | void OutputDevice::ImplDrawMnemonicLine( tools::Long nX, tools::Long nY, tools::Long nWidth ) |
909 | 271 | { |
910 | 271 | tools::Long nBaseX = nX; |
911 | 271 | if( /*HasMirroredGraphics() &&*/ IsRTLEnabled() ) |
912 | 0 | { |
913 | | // revert the hack that will be done later in ImplDrawTextLine |
914 | 0 | nX = nBaseX - nWidth - (nX - nBaseX - 1); |
915 | 0 | } |
916 | | |
917 | 271 | ImplDrawTextLine( nX, nY, 0, nWidth, nWidth, STRIKEOUT_NONE, LINESTYLE_SINGLE, LINESTYLE_NONE, false ); |
918 | 271 | } |
919 | | |
920 | | void OutputDevice::SetTextLineColor() |
921 | 506k | { |
922 | 506k | if ( mpMetaFile ) |
923 | 2 | mpMetaFile->AddAction( new MetaTextLineColorAction( Color(), false ) ); |
924 | | |
925 | 506k | maTextLineColor = COL_TRANSPARENT; |
926 | 506k | } |
927 | | |
928 | | void OutputDevice::SetTextLineColor( const Color& rColor ) |
929 | 12.8k | { |
930 | 12.8k | Color aColor(vcl::drawmode::GetTextColor(rColor, GetDrawMode(), GetSettings().GetStyleSettings())); |
931 | | |
932 | 12.8k | if ( mpMetaFile ) |
933 | 10 | mpMetaFile->AddAction( new MetaTextLineColorAction( aColor, true ) ); |
934 | | |
935 | 12.8k | maTextLineColor = aColor; |
936 | 12.8k | } |
937 | | |
938 | | void OutputDevice::SetOverlineColor() |
939 | 507k | { |
940 | 507k | if ( mpMetaFile ) |
941 | 2 | mpMetaFile->AddAction( new MetaOverlineColorAction( Color(), false ) ); |
942 | | |
943 | 507k | maOverlineColor = COL_TRANSPARENT; |
944 | 507k | } |
945 | | |
946 | | void OutputDevice::SetOverlineColor( const Color& rColor ) |
947 | 13.9k | { |
948 | 13.9k | Color aColor(vcl::drawmode::GetTextColor(rColor, GetDrawMode(), GetSettings().GetStyleSettings())); |
949 | | |
950 | 13.9k | if ( mpMetaFile ) |
951 | 10 | mpMetaFile->AddAction( new MetaOverlineColorAction( aColor, true ) ); |
952 | | |
953 | 13.9k | maOverlineColor = aColor; |
954 | 13.9k | } |
955 | | |
956 | | void OutputDevice::DrawTextLine( const Point& rPos, tools::Long nWidth, |
957 | | FontStrikeout eStrikeout, |
958 | | FontLineStyle eUnderline, |
959 | | FontLineStyle eOverline ) |
960 | 2.09k | { |
961 | 2.09k | assert(!is_double_buffered_window()); |
962 | | |
963 | 2.09k | if ( mpMetaFile ) |
964 | 0 | mpMetaFile->AddAction( new MetaTextLineAction( rPos, nWidth, eStrikeout, eUnderline, eOverline ) ); |
965 | | |
966 | 2.09k | if ( ((eUnderline == LINESTYLE_NONE) || (eUnderline == LINESTYLE_DONTKNOW)) && |
967 | 1.73k | ((eOverline == LINESTYLE_NONE) || (eOverline == LINESTYLE_DONTKNOW)) && |
968 | 1.55k | ((eStrikeout == STRIKEOUT_NONE) || (eStrikeout == STRIKEOUT_DONTKNOW)) ) |
969 | 1.41k | { |
970 | 1.41k | return; |
971 | 1.41k | } |
972 | 676 | if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() ) |
973 | 0 | return; |
974 | | |
975 | 676 | if( mbInitClipRegion ) |
976 | 73 | InitClipRegion(); |
977 | | |
978 | 676 | if( mbOutputClipped ) |
979 | 52 | return; |
980 | | |
981 | | // initialize font if needed to get text offsets |
982 | | // TODO: only needed for mnTextOff!=(0,0) |
983 | 624 | if (!InitFont()) |
984 | 0 | return; |
985 | | |
986 | 624 | Point aPos = ImplLogicToDevicePixel( rPos ); |
987 | 624 | double fWidth = ImplLogicWidthToDeviceSubPixel(nWidth); |
988 | 624 | aPos += Point( mnTextOffX, mnTextOffY ); |
989 | 624 | ImplDrawTextLine( aPos.X(), aPos.X(), 0, fWidth, fWidth, eStrikeout, eUnderline, eOverline, /*bUnderlineAbove*/false ); |
990 | 624 | } |
991 | | |
992 | | void OutputDevice::DrawWaveLine(const Point& rStartPos, const Point& rEndPos, tools::Long nLineWidth, tools::Long nWaveHeight) |
993 | 0 | { |
994 | 0 | assert(!is_double_buffered_window()); |
995 | |
|
996 | 0 | if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() ) |
997 | 0 | return; |
998 | | |
999 | | // we need a graphics |
1000 | 0 | if( !mpGraphics && !AcquireGraphics() ) |
1001 | 0 | return; |
1002 | 0 | assert(mpGraphics); |
1003 | |
|
1004 | 0 | if ( mbInitClipRegion ) |
1005 | 0 | InitClipRegion(); |
1006 | |
|
1007 | 0 | if ( mbOutputClipped ) |
1008 | 0 | return; |
1009 | | |
1010 | 0 | if (!InitFont()) |
1011 | 0 | return; |
1012 | | |
1013 | 0 | Point aStartPt = ImplLogicToDevicePixel(rStartPos); |
1014 | 0 | Point aEndPt = ImplLogicToDevicePixel(rEndPos); |
1015 | |
|
1016 | 0 | tools::Long nStartX = aStartPt.X(); |
1017 | 0 | tools::Long nStartY = aStartPt.Y(); |
1018 | 0 | tools::Long nEndX = aEndPt.X(); |
1019 | 0 | tools::Long nEndY = aEndPt.Y(); |
1020 | 0 | double fOrientation = 0.0; |
1021 | | |
1022 | | // handle rotation |
1023 | 0 | if (nStartY != nEndY || nStartX > nEndX) |
1024 | 0 | { |
1025 | 0 | fOrientation = basegfx::rad2deg(std::atan2(nStartY - nEndY, nEndX - nStartX)); |
1026 | | // un-rotate the end point |
1027 | 0 | aStartPt.RotateAround(nEndX, nEndY, Degree10(static_cast<sal_Int16>(-fOrientation * 10.0))); |
1028 | 0 | } |
1029 | | |
1030 | | // Handle HiDPI |
1031 | 0 | float fScaleFactor = GetDPIScaleFactor(); |
1032 | 0 | if (fScaleFactor > 1.0f) |
1033 | 0 | { |
1034 | 0 | nWaveHeight *= fScaleFactor; |
1035 | |
|
1036 | 0 | nStartY += fScaleFactor - 1; // Shift down additional pixel(s) to create more visual separation. |
1037 | | |
1038 | | // odd heights look better than even |
1039 | 0 | if (nWaveHeight % 2 == 0) |
1040 | 0 | { |
1041 | 0 | nWaveHeight--; |
1042 | 0 | } |
1043 | 0 | } |
1044 | | |
1045 | | // #109280# make sure the waveline does not exceed the descent to avoid paint problems |
1046 | 0 | LogicalFontInstance* pFontInstance = mpFontInstance.get(); |
1047 | 0 | if (nWaveHeight > pFontInstance->mxFontMetric->GetWavelineUnderlineSize() |
1048 | | // tdf#153223 polyline with lineheight >0 not drawn when skia is off |
1049 | | #ifdef MACOSX |
1050 | | || !SkiaHelper::isVCLSkiaEnabled() |
1051 | | #endif |
1052 | 0 | ) |
1053 | 0 | { |
1054 | 0 | nWaveHeight = pFontInstance->mxFontMetric->GetWavelineUnderlineSize(); |
1055 | | // tdf#124848 hairline |
1056 | 0 | nLineWidth = 0; |
1057 | 0 | } |
1058 | |
|
1059 | 0 | if ( fOrientation == 0.0 ) |
1060 | 0 | { |
1061 | 0 | static tools::DeleteOnDeinit< WavyLineCache > snLineCache {}; |
1062 | 0 | if ( !snLineCache.get() ) |
1063 | 0 | return; |
1064 | 0 | WavyLineCache& rLineCache = *snLineCache.get(); |
1065 | 0 | Bitmap aWavylinebmp; |
1066 | 0 | if ( !rLineCache.find( GetLineColor(), nLineWidth, nWaveHeight, nEndX - nStartX, aWavylinebmp ) ) |
1067 | 0 | { |
1068 | 0 | size_t nWordLength = nEndX - nStartX; |
1069 | | // start with something big to avoid updating it frequently |
1070 | 0 | nWordLength = nWordLength < 1024 ? 1024 : nWordLength; |
1071 | 0 | ScopedVclPtrInstance< VirtualDevice > pVirtDev( *this, DeviceFormat::WITH_ALPHA ); |
1072 | 0 | pVirtDev->SetOutputSizePixel( Size( nWordLength, nWaveHeight * 2 ), false ); |
1073 | 0 | pVirtDev->SetLineColor( GetLineColor() ); |
1074 | 0 | pVirtDev->SetBackground( Wallpaper( COL_TRANSPARENT ) ); |
1075 | 0 | pVirtDev->Erase(); |
1076 | 0 | pVirtDev->SetAntialiasing( AntialiasingFlags::Enable ); |
1077 | 0 | pVirtDev->ImplDrawWaveLineBezier( 0, 0, nWordLength, 0, nWaveHeight, fOrientation, nLineWidth ); |
1078 | 0 | Bitmap aBitmap(pVirtDev->GetBitmap(Point(0, 0), pVirtDev->GetOutputSize())); |
1079 | |
|
1080 | 0 | rLineCache.insert( aBitmap, GetLineColor(), nLineWidth, nWaveHeight, nWordLength, aWavylinebmp ); |
1081 | 0 | } |
1082 | 0 | if ( aWavylinebmp.ImplGetSalBitmap() != nullptr ) |
1083 | 0 | { |
1084 | 0 | Size _size( nEndX - nStartX, aWavylinebmp.GetSizePixel().Height() ); |
1085 | 0 | DrawBitmap(Point( rStartPos.X(), rStartPos.Y() ), PixelToLogic( _size ), Point(), _size, aWavylinebmp); |
1086 | 0 | } |
1087 | 0 | return; |
1088 | 0 | } |
1089 | | |
1090 | 0 | ImplDrawWaveLineBezier( nStartX, nStartY, nEndX, nEndY, nWaveHeight, fOrientation, nLineWidth ); |
1091 | 0 | } |
1092 | | |
1093 | | void OutputDevice::ImplDrawWaveLineBezier(tools::Long nStartX, tools::Long nStartY, tools::Long nEndX, tools::Long nEndY, tools::Long nWaveHeight, double fOrientation, tools::Long nLineWidth) |
1094 | 0 | { |
1095 | | // we need a graphics |
1096 | 0 | if( !mpGraphics && !AcquireGraphics() ) |
1097 | 0 | return; |
1098 | 0 | assert(mpGraphics); |
1099 | |
|
1100 | 0 | if ( mbInitClipRegion ) |
1101 | 0 | InitClipRegion(); |
1102 | |
|
1103 | 0 | if ( mbOutputClipped ) |
1104 | 0 | return; |
1105 | | |
1106 | 0 | if (!InitFont()) |
1107 | 0 | return; |
1108 | | |
1109 | 0 | const basegfx::B2DRectangle aWaveLineRectangle(nStartX, nStartY, nEndX, nEndY + nWaveHeight); |
1110 | 0 | const basegfx::B2DPolygon aWaveLinePolygon = basegfx::createWaveLinePolygon(aWaveLineRectangle); |
1111 | 0 | const basegfx::B2DHomMatrix aRotationMatrix = basegfx::utils::createRotateAroundPoint(nStartX, nStartY, basegfx::deg2rad(-fOrientation)); |
1112 | 0 | const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline); |
1113 | |
|
1114 | 0 | mpGraphics->SetLineColor(GetLineColor()); |
1115 | 0 | mpGraphics->DrawPolyLine( |
1116 | 0 | aRotationMatrix, |
1117 | 0 | aWaveLinePolygon, |
1118 | 0 | 0.0, |
1119 | 0 | nLineWidth, |
1120 | 0 | nullptr, // MM01 |
1121 | 0 | basegfx::B2DLineJoin::NONE, |
1122 | 0 | css::drawing::LineCap_BUTT, |
1123 | 0 | basegfx::deg2rad(15.0), |
1124 | 0 | bPixelSnapHairline, |
1125 | 0 | *this); |
1126 | 0 | } |
1127 | | |
1128 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |