Line data Source code
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/config.h>
21 :
22 : #include <algorithm>
23 :
24 : #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
25 : #include <comphelper/processfactory.hxx>
26 : #include <comphelper/unique_disposing_ptr.hxx>
27 : #include <vcl/timer.hxx>
28 : #include <vcl/virdev.hxx>
29 : #include <vcl/font.hxx>
30 : #include <vcl/metric.hxx>
31 : #include <i18nlangtag/languagetag.hxx>
32 : #include <drawinglayer/primitive2d/textprimitive2d.hxx>
33 : #include <vcl/svapp.hxx>
34 :
35 :
36 : // VDev RevDevice provider
37 :
38 : namespace
39 : {
40 : class ImpTimedRefDev;
41 :
42 : //the scoped_timed_RefDev owns a ImpTimeRefDev and releases it on dtor
43 : //or disposing of the default XComponentContext which causes the underlying
44 : //OutputDevice to get released
45 :
46 : //The ImpTimerRefDev itself, if the timeout ever gets hit, will call
47 : //reset on the scoped_timed_RefDev to release the ImpTimerRefDev early
48 : //if its unused for a few minutes
49 51 : class scoped_timed_RefDev : public comphelper::unique_disposing_ptr<ImpTimedRefDev>
50 : {
51 : public:
52 51 : scoped_timed_RefDev() : comphelper::unique_disposing_ptr<ImpTimedRefDev>((::com::sun::star::uno::Reference<com::sun::star::lang::XComponent>(::comphelper::getProcessComponentContext(), ::com::sun::star::uno::UNO_QUERY_THROW)))
53 : {
54 51 : }
55 : };
56 :
57 : class the_scoped_timed_RefDev : public rtl::Static<scoped_timed_RefDev, the_scoped_timed_RefDev> {};
58 :
59 : class ImpTimedRefDev : public Timer
60 : {
61 : scoped_timed_RefDev& mrOwnerOfMe;
62 : VclPtr<VirtualDevice> mpVirDev;
63 : sal_uInt32 mnUseCount;
64 :
65 : public:
66 : explicit ImpTimedRefDev(scoped_timed_RefDev& rOwnerofMe);
67 : virtual ~ImpTimedRefDev();
68 : virtual void Invoke() SAL_OVERRIDE;
69 :
70 : VirtualDevice& acquireVirtualDevice();
71 : void releaseVirtualDevice();
72 : };
73 :
74 52 : ImpTimedRefDev::ImpTimedRefDev(scoped_timed_RefDev& rOwnerOfMe)
75 : : mrOwnerOfMe(rOwnerOfMe),
76 : mpVirDev(0L),
77 52 : mnUseCount(0L)
78 : {
79 52 : SetTimeout(3L * 60L * 1000L); // three minutes
80 52 : Start();
81 52 : }
82 :
83 156 : ImpTimedRefDev::~ImpTimedRefDev()
84 : {
85 : OSL_ENSURE(0L == mnUseCount, "destruction of a still used ImpTimedRefDev (!)");
86 52 : const SolarMutexGuard aGuard;
87 52 : mpVirDev.disposeAndClear();
88 104 : }
89 :
90 1 : void ImpTimedRefDev::Invoke()
91 : {
92 : // for obvious reasons, do not call anything after this
93 1 : mrOwnerOfMe.reset();
94 1 : }
95 :
96 28367 : VirtualDevice& ImpTimedRefDev::acquireVirtualDevice()
97 : {
98 28367 : if(!mpVirDev)
99 : {
100 52 : mpVirDev = VclPtr<VirtualDevice>::Create();
101 52 : mpVirDev->SetReferenceDevice( VirtualDevice::REFDEV_MODE_MSO1 );
102 : }
103 :
104 28367 : if(!mnUseCount)
105 : {
106 28367 : Stop();
107 : }
108 :
109 28367 : mnUseCount++;
110 :
111 28367 : return *mpVirDev;
112 : }
113 :
114 28367 : void ImpTimedRefDev::releaseVirtualDevice()
115 : {
116 : OSL_ENSURE(mnUseCount, "mismatch call number to releaseVirtualDevice() (!)");
117 28367 : mnUseCount--;
118 :
119 28367 : if(!mnUseCount)
120 : {
121 28367 : Start();
122 : }
123 28367 : }
124 : } // end of anonymous namespace
125 :
126 :
127 : // access to one global ImpTimedRefDev incarnation in namespace drawinglayer::primitive
128 :
129 : namespace drawinglayer
130 : {
131 : namespace primitive2d
132 : {
133 : // static methods here
134 28367 : VirtualDevice& acquireGlobalVirtualDevice()
135 : {
136 28367 : scoped_timed_RefDev& rStdRefDevice = the_scoped_timed_RefDev::get();
137 :
138 28367 : if(!rStdRefDevice)
139 52 : rStdRefDevice.reset(new ImpTimedRefDev(rStdRefDevice));
140 :
141 28367 : return rStdRefDevice->acquireVirtualDevice();
142 : }
143 :
144 28367 : void releaseGlobalVirtualDevice()
145 : {
146 28367 : scoped_timed_RefDev& rStdRefDevice = the_scoped_timed_RefDev::get();
147 :
148 : OSL_ENSURE(rStdRefDevice, "releaseGlobalVirtualDevice() without prior acquireGlobalVirtualDevice() call(!)");
149 28367 : rStdRefDevice->releaseVirtualDevice();
150 28367 : }
151 :
152 28367 : TextLayouterDevice::TextLayouterDevice()
153 28367 : : mrDevice(acquireGlobalVirtualDevice())
154 : {
155 28367 : }
156 :
157 28367 : TextLayouterDevice::~TextLayouterDevice()
158 : {
159 28367 : releaseGlobalVirtualDevice();
160 28367 : }
161 :
162 27438 : void TextLayouterDevice::setFont(const vcl::Font& rFont)
163 : {
164 27438 : mrDevice.SetFont( rFont );
165 27438 : }
166 :
167 27407 : void TextLayouterDevice::setFontAttribute(
168 : const attribute::FontAttribute& rFontAttribute,
169 : double fFontScaleX,
170 : double fFontScaleY,
171 : const ::com::sun::star::lang::Locale& rLocale)
172 : {
173 : setFont(getVclFontFromFontAttribute(
174 : rFontAttribute,
175 : fFontScaleX,
176 : fFontScaleY,
177 : 0.0,
178 27407 : rLocale));
179 27407 : }
180 :
181 1840 : double TextLayouterDevice::getOverlineOffset() const
182 : {
183 1840 : const ::FontMetric& rMetric = mrDevice.GetFontMetric();
184 1840 : double fRet = (rMetric.GetIntLeading() / 2.0) - rMetric.GetAscent();
185 1840 : return fRet;
186 : }
187 :
188 2187 : double TextLayouterDevice::getUnderlineOffset() const
189 : {
190 2187 : const ::FontMetric& rMetric = mrDevice.GetFontMetric();
191 2187 : double fRet = rMetric.GetDescent() / 2.0;
192 2187 : return fRet;
193 : }
194 :
195 1033 : double TextLayouterDevice::getStrikeoutOffset() const
196 : {
197 1033 : const ::FontMetric& rMetric = mrDevice.GetFontMetric();
198 1033 : double fRet = (rMetric.GetAscent() - rMetric.GetIntLeading()) / 3.0;
199 1033 : return fRet;
200 : }
201 :
202 1840 : double TextLayouterDevice::getOverlineHeight() const
203 : {
204 1840 : const ::FontMetric& rMetric = mrDevice.GetFontMetric();
205 1840 : double fRet = rMetric.GetIntLeading() / 2.5;
206 1840 : return fRet;
207 : }
208 :
209 3220 : double TextLayouterDevice::getUnderlineHeight() const
210 : {
211 3220 : const ::FontMetric& rMetric = mrDevice.GetFontMetric();
212 3220 : double fRet = rMetric.GetDescent() / 4.0;
213 3220 : return fRet;
214 : }
215 :
216 0 : double TextLayouterDevice::getTextHeight() const
217 : {
218 0 : return mrDevice.GetTextHeight();
219 : }
220 :
221 47 : double TextLayouterDevice::getTextWidth(
222 : const OUString& rText,
223 : sal_uInt32 nIndex,
224 : sal_uInt32 nLength) const
225 : {
226 47 : return mrDevice.GetTextWidth(rText, nIndex, nLength);
227 : }
228 :
229 923 : bool TextLayouterDevice::getTextOutlines(
230 : basegfx::B2DPolyPolygonVector& rB2DPolyPolyVector,
231 : const OUString& rText,
232 : sal_uInt32 nIndex,
233 : sal_uInt32 nLength,
234 : const ::std::vector< double >& rDXArray) const
235 : {
236 923 : const sal_uInt32 nDXArrayCount(rDXArray.size());
237 923 : sal_uInt32 nTextLength(nLength);
238 923 : const sal_uInt32 nStringLength(rText.getLength());
239 :
240 923 : if(nTextLength + nIndex > nStringLength)
241 : {
242 0 : nTextLength = nStringLength - nIndex;
243 : }
244 :
245 923 : if(nDXArrayCount)
246 : {
247 : OSL_ENSURE(nDXArrayCount == nTextLength, "DXArray size does not correspond to text portion size (!)");
248 923 : std::vector< long > aIntegerDXArray(nDXArrayCount);
249 :
250 3396 : for(sal_uInt32 a(0); a < nDXArrayCount; a++)
251 : {
252 2473 : aIntegerDXArray[a] = basegfx::fround(rDXArray[a]);
253 : }
254 :
255 : return mrDevice.GetTextOutlines(
256 : rB2DPolyPolyVector,
257 : rText,
258 : nIndex,
259 : nIndex,
260 : nLength,
261 : true,
262 : 0,
263 923 : &(aIntegerDXArray[0]));
264 : }
265 : else
266 : {
267 : return mrDevice.GetTextOutlines(
268 : rB2DPolyPolyVector,
269 : rText,
270 : nIndex,
271 : nIndex,
272 : nLength,
273 : true,
274 : 0,
275 0 : 0);
276 : }
277 : }
278 :
279 23989 : basegfx::B2DRange TextLayouterDevice::getTextBoundRect(
280 : const OUString& rText,
281 : sal_uInt32 nIndex,
282 : sal_uInt32 nLength) const
283 : {
284 23989 : sal_uInt32 nTextLength(nLength);
285 23989 : const sal_uInt32 nStringLength(rText.getLength());
286 :
287 23989 : if(nTextLength + nIndex > nStringLength)
288 : {
289 0 : nTextLength = nStringLength - nIndex;
290 : }
291 :
292 23989 : if(nTextLength)
293 : {
294 23989 : Rectangle aRect;
295 :
296 : mrDevice.GetTextBoundRect(
297 : aRect,
298 : rText,
299 : nIndex,
300 : nIndex,
301 23989 : nLength);
302 :
303 : // #i104432#, #i102556# take empty results into account
304 23989 : if(!aRect.IsEmpty())
305 : {
306 : return basegfx::B2DRange(
307 45732 : aRect.Left(), aRect.Top(),
308 68598 : aRect.Right(), aRect.Bottom());
309 : }
310 : }
311 :
312 1123 : return basegfx::B2DRange();
313 : }
314 :
315 31 : double TextLayouterDevice::getFontAscent() const
316 : {
317 31 : const ::FontMetric& rMetric = mrDevice.GetFontMetric();
318 31 : return rMetric.GetAscent();
319 : }
320 :
321 0 : double TextLayouterDevice::getFontDescent() const
322 : {
323 0 : const ::FontMetric& rMetric = mrDevice.GetFontMetric();
324 0 : return rMetric.GetDescent();
325 : }
326 :
327 0 : void TextLayouterDevice::addTextRectActions(
328 : const Rectangle& rRectangle,
329 : const OUString& rText,
330 : DrawTextFlags nStyle,
331 : GDIMetaFile& rGDIMetaFile) const
332 : {
333 : mrDevice.AddTextRectActions(
334 0 : rRectangle, rText, nStyle, rGDIMetaFile);
335 0 : }
336 :
337 0 : ::std::vector< double > TextLayouterDevice::getTextArray(
338 : const OUString& rText,
339 : sal_uInt32 nIndex,
340 : sal_uInt32 nLength) const
341 : {
342 0 : ::std::vector< double > aRetval;
343 0 : sal_uInt32 nTextLength(nLength);
344 0 : const sal_uInt32 nStringLength(rText.getLength());
345 :
346 0 : if(nTextLength + nIndex > nStringLength)
347 : {
348 0 : nTextLength = nStringLength - nIndex;
349 : }
350 :
351 0 : if(nTextLength)
352 : {
353 0 : aRetval.reserve(nTextLength);
354 0 : ::std::vector<long> aArray(nTextLength);
355 0 : mrDevice.GetTextArray(rText, &aArray[0], nIndex, nLength);
356 0 : aRetval.assign(aArray.begin(), aArray.end());
357 : }
358 :
359 0 : return aRetval;
360 : }
361 :
362 : } // end of namespace primitive2d
363 : } // end of namespace drawinglayer
364 :
365 :
366 : // helper methods for vcl font handling
367 :
368 : namespace drawinglayer
369 : {
370 : namespace primitive2d
371 : {
372 42010 : vcl::Font getVclFontFromFontAttribute(
373 : const attribute::FontAttribute& rFontAttribute,
374 : double fFontScaleX,
375 : double fFontScaleY,
376 : double fFontRotation,
377 : const ::com::sun::star::lang::Locale& rLocale)
378 : {
379 : // detect FontScaling
380 42010 : const sal_uInt32 nHeight(basegfx::fround(fabs(fFontScaleY)));
381 42010 : const sal_uInt32 nWidth(basegfx::fround(fabs(fFontScaleX)));
382 42010 : const bool bFontIsScaled(nHeight != nWidth);
383 :
384 : #ifdef WIN32
385 : // for WIN32 systems, start with creating an unscaled font. If FontScaling
386 : // is wanted, that width needs to be adapted using FontMetric again to get a
387 : // width of the unscaled font
388 : vcl::Font aRetval(
389 : rFontAttribute.getFamilyName(),
390 : rFontAttribute.getStyleName(),
391 : Size(0, nHeight));
392 : #else
393 : // for non-WIN32 systems things are easier since these accept a Font creation
394 : // with initially nWidth != nHeight for FontScaling. Despite that, use zero for
395 : // FontWidth when no scaling is used to explicitly have that zero when e.g. the
396 : // Font would be recorded in a MetaFile (The MetaFile FontAction WILL record a
397 : // set FontWidth; import that in a WIN32 system, and trouble is there)
398 : vcl::Font aRetval(
399 42010 : rFontAttribute.getFamilyName(),
400 42010 : rFontAttribute.getStyleName(),
401 84020 : Size(bFontIsScaled ? std::max<sal_uInt32>(nWidth, 1) : 0, nHeight));
402 : #endif
403 : // define various other FontAttribute
404 42010 : aRetval.SetAlign(ALIGN_BASELINE);
405 42010 : aRetval.SetCharSet(rFontAttribute.getSymbol() ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE);
406 42010 : aRetval.SetVertical(rFontAttribute.getVertical());
407 42010 : aRetval.SetWeight(static_cast<FontWeight>(rFontAttribute.getWeight()));
408 42010 : aRetval.SetItalic(rFontAttribute.getItalic() ? ITALIC_NORMAL : ITALIC_NONE);
409 42010 : aRetval.SetOutline(rFontAttribute.getOutline());
410 42010 : aRetval.SetPitch(rFontAttribute.getMonospaced() ? PITCH_FIXED : PITCH_VARIABLE);
411 42010 : aRetval.SetLanguage(LanguageTag::convertToLanguageType( rLocale, false));
412 :
413 : #ifdef WIN32
414 : // for WIN32 systems, correct the FontWidth if FontScaling is used
415 : if(bFontIsScaled && nHeight > 0)
416 : {
417 : const FontMetric aUnscaledFontMetric(Application::GetDefaultDevice()->GetFontMetric(aRetval));
418 :
419 : if(aUnscaledFontMetric.GetWidth() > 0)
420 : {
421 : const double fScaleFactor((double)nWidth / (double)nHeight);
422 : const sal_uInt32 nScaledWidth(basegfx::fround((double)aUnscaledFontMetric.GetWidth() * fScaleFactor));
423 : aRetval.SetWidth(nScaledWidth);
424 : }
425 : }
426 : #endif
427 : // handle FontRotation (if defined)
428 42010 : if(!basegfx::fTools::equalZero(fFontRotation))
429 : {
430 9 : sal_Int16 aRotate10th((sal_Int16)(fFontRotation * (-1800.0/F_PI)));
431 9 : aRetval.SetOrientation(aRotate10th % 3600);
432 : }
433 :
434 42010 : return aRetval;
435 : }
436 :
437 23658 : attribute::FontAttribute getFontAttributeFromVclFont(
438 : basegfx::B2DVector& o_rSize,
439 : const vcl::Font& rFont,
440 : bool bRTL,
441 : bool bBiDiStrong)
442 : {
443 : const attribute::FontAttribute aRetval(
444 23658 : rFont.GetName(),
445 23658 : rFont.GetStyleName(),
446 23658 : static_cast<sal_uInt16>(rFont.GetWeight()),
447 23658 : RTL_TEXTENCODING_SYMBOL == rFont.GetCharSet(),
448 23658 : rFont.IsVertical(),
449 23658 : ITALIC_NONE != rFont.GetItalic(),
450 23658 : PITCH_FIXED == rFont.GetPitch(),
451 23658 : rFont.IsOutline(),
452 : bRTL,
453 189264 : bBiDiStrong);
454 : // TODO: eKerning
455 :
456 : // set FontHeight and init to no FontScaling
457 23658 : o_rSize.setY(rFont.GetSize().getHeight() > 0 ? rFont.GetSize().getHeight() : 0);
458 23658 : o_rSize.setX(o_rSize.getY());
459 :
460 : #ifdef WIN32
461 : // for WIN32 systems, the FontScaling at the Font is detected by
462 : // checking that FontWidth != 0. When FontScaling is used, WIN32
463 : // needs to do extra stuff to detect the correct width (since it's
464 : // zero and not equal the font height) and its relationship to
465 : // the height
466 : if(rFont.GetSize().getWidth() > 0)
467 : {
468 : vcl::Font aUnscaledFont(rFont);
469 : aUnscaledFont.SetWidth(0);
470 : const FontMetric aUnscaledFontMetric(Application::GetDefaultDevice()->GetFontMetric(aUnscaledFont));
471 :
472 : if(aUnscaledFontMetric.GetWidth() > 0)
473 : {
474 : const double fScaleFactor((double)rFont.GetSize().getWidth() / (double)aUnscaledFontMetric.GetWidth());
475 : o_rSize.setX(fScaleFactor * o_rSize.getY());
476 : }
477 : }
478 : #else
479 : // For non-WIN32 systems the detection is the same, but the value
480 : // is easier achieved since width == height is interpreted as no
481 : // scaling. Ergo, Width == 0 means width == height, and width != 0
482 : // means the scaling is in the direct relation of width to height
483 23658 : if(rFont.GetSize().getWidth() > 0)
484 : {
485 1618 : o_rSize.setX((double)rFont.GetSize().getWidth());
486 : }
487 : #endif
488 23658 : return aRetval;
489 : }
490 : } // end of namespace primitive2d
491 : } // end of namespace drawinglayer
492 :
493 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|