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