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