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