1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
24 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
25 #include <comphelper/processfactory.hxx>
26 #include <comphelper/unique_disposing_ptr.hxx>
27 #include <tools/gen.hxx>
28 #include <vcl/canvastools.hxx>
29 #include <vcl/timer.hxx>
30 #include <vcl/virdev.hxx>
31 #include <vcl/font.hxx>
32 #include <vcl/metric.hxx>
33 #include <i18nlangtag/languagetag.hxx>
34 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
35 #include <vcl/svapp.hxx>
36 #include <o3tl/deleter.hxx>
39 // VDev RevDevice provider
45 //the scoped_timed_RefDev owns an ImpTimeRefDev and releases it on dtor
46 //or disposing of the default XComponentContext which causes the underlying
47 //OutputDevice to get released
49 //The ImpTimerRefDev itself, if the timeout ever gets hit, will call
50 //reset on the scoped_timed_RefDev to release the ImpTimerRefDev early
51 //if its unused for a few minutes
52 class scoped_timed_RefDev
: public comphelper::unique_disposing_ptr
<ImpTimedRefDev
>
55 scoped_timed_RefDev() : comphelper::unique_disposing_ptr
<ImpTimedRefDev
>((css::uno::Reference
<css::lang::XComponent
>(::comphelper::getProcessComponentContext(), css::uno::UNO_QUERY_THROW
)))
60 class the_scoped_timed_RefDev
: public rtl::Static
<scoped_timed_RefDev
, the_scoped_timed_RefDev
> {};
62 class ImpTimedRefDev
: public Timer
64 scoped_timed_RefDev
& mrOwnerOfMe
;
65 VclPtr
<VirtualDevice
> mpVirDev
;
66 sal_uInt32 mnUseCount
;
69 explicit ImpTimedRefDev(scoped_timed_RefDev
& rOwnerofMe
);
70 virtual ~ImpTimedRefDev() override
;
71 virtual void Invoke() override
;
73 VirtualDevice
& acquireVirtualDevice();
74 void releaseVirtualDevice();
77 ImpTimedRefDev::ImpTimedRefDev(scoped_timed_RefDev
& rOwnerOfMe
)
78 : Timer( "drawinglayer ImpTimedRefDev destroy mpVirDev" ),
79 mrOwnerOfMe(rOwnerOfMe
),
83 SetTimeout(3L * 60L * 1000L); // three minutes
87 ImpTimedRefDev::~ImpTimedRefDev()
89 OSL_ENSURE(0 == mnUseCount
, "destruction of a still used ImpTimedRefDev (!)");
90 const SolarMutexGuard aSolarGuard
;
91 mpVirDev
.disposeAndClear();
94 void ImpTimedRefDev::Invoke()
96 // for obvious reasons, do not call anything after this
100 VirtualDevice
& ImpTimedRefDev::acquireVirtualDevice()
104 mpVirDev
= VclPtr
<VirtualDevice
>::Create();
105 mpVirDev
->SetReferenceDevice( VirtualDevice::RefDevMode::MSO1
);
118 void ImpTimedRefDev::releaseVirtualDevice()
120 OSL_ENSURE(mnUseCount
, "mismatch call number to releaseVirtualDevice() (!)");
128 } // end of anonymous namespace
131 // access to one global ImpTimedRefDev incarnation in namespace drawinglayer::primitive
133 namespace drawinglayer
135 namespace primitive2d
137 // static methods here
138 static VirtualDevice
& acquireGlobalVirtualDevice()
140 scoped_timed_RefDev
& rStdRefDevice
= the_scoped_timed_RefDev::get();
143 rStdRefDevice
.reset(new ImpTimedRefDev(rStdRefDevice
));
145 return rStdRefDevice
->acquireVirtualDevice();
148 static void releaseGlobalVirtualDevice()
150 scoped_timed_RefDev
& rStdRefDevice
= the_scoped_timed_RefDev::get();
152 OSL_ENSURE(rStdRefDevice
, "releaseGlobalVirtualDevice() without prior acquireGlobalVirtualDevice() call(!)");
153 rStdRefDevice
->releaseVirtualDevice();
156 TextLayouterDevice::TextLayouterDevice()
158 mrDevice(acquireGlobalVirtualDevice())
162 TextLayouterDevice::~TextLayouterDevice() COVERITY_NOEXCEPT_FALSE
164 releaseGlobalVirtualDevice();
167 void TextLayouterDevice::setFont(const vcl::Font
& rFont
)
169 mrDevice
.SetFont( rFont
);
172 void TextLayouterDevice::setFontAttribute(
173 const attribute::FontAttribute
& rFontAttribute
,
176 const css::lang::Locale
& rLocale
)
178 setFont(getVclFontFromFontAttribute(
186 double TextLayouterDevice::getOverlineOffset() const
188 const ::FontMetric
& rMetric
= mrDevice
.GetFontMetric();
189 double fRet
= (rMetric
.GetInternalLeading() / 2.0) - rMetric
.GetAscent();
193 double TextLayouterDevice::getUnderlineOffset() const
195 const ::FontMetric
& rMetric
= mrDevice
.GetFontMetric();
196 double fRet
= rMetric
.GetDescent() / 2.0;
200 double TextLayouterDevice::getStrikeoutOffset() const
202 const ::FontMetric
& rMetric
= mrDevice
.GetFontMetric();
203 double fRet
= (rMetric
.GetAscent() - rMetric
.GetInternalLeading()) / 3.0;
207 double TextLayouterDevice::getOverlineHeight() const
209 const ::FontMetric
& rMetric
= mrDevice
.GetFontMetric();
210 double fRet
= rMetric
.GetInternalLeading() / 2.5;
214 double TextLayouterDevice::getUnderlineHeight() const
216 const ::FontMetric
& rMetric
= mrDevice
.GetFontMetric();
217 double fRet
= rMetric
.GetDescent() / 4.0;
221 double TextLayouterDevice::getTextHeight() const
223 return mrDevice
.GetTextHeight();
226 double TextLayouterDevice::getTextWidth(
227 const OUString
& rText
,
229 sal_uInt32 nLength
) const
231 return mrDevice
.GetTextWidth(rText
, nIndex
, nLength
);
234 void TextLayouterDevice::getTextOutlines(
235 basegfx::B2DPolyPolygonVector
& rB2DPolyPolyVector
,
236 const OUString
& rText
,
239 const std::vector
< double >& rDXArray
) const
241 const sal_uInt32
nDXArrayCount(rDXArray
.size());
242 sal_uInt32
nTextLength(nLength
);
243 const sal_uInt32
nStringLength(rText
.getLength());
245 if(nTextLength
+ nIndex
> nStringLength
)
247 nTextLength
= nStringLength
- nIndex
;
252 OSL_ENSURE(nDXArrayCount
== nTextLength
, "DXArray size does not correspond to text portion size (!)");
253 std::vector
< long > aIntegerDXArray(nDXArrayCount
);
255 for(sal_uInt32
a(0); a
< nDXArrayCount
; a
++)
257 aIntegerDXArray
[a
] = basegfx::fround(rDXArray
[a
]);
260 mrDevice
.GetTextOutlines(
267 aIntegerDXArray
.data());
271 mrDevice
.GetTextOutlines(
280 basegfx::B2DRange
TextLayouterDevice::getTextBoundRect(
281 const OUString
& rText
,
283 sal_uInt32 nLength
) const
285 sal_uInt32
nTextLength(nLength
);
286 const sal_uInt32
nStringLength(rText
.getLength());
288 if(nTextLength
+ nIndex
> nStringLength
)
290 nTextLength
= nStringLength
- nIndex
;
295 ::tools::Rectangle aRect
;
297 mrDevice
.GetTextBoundRect(
304 // #i104432#, #i102556# take empty results into account
307 return vcl::unotools::b2DRectangleFromRectangle(aRect
);
311 return basegfx::B2DRange();
314 double TextLayouterDevice::getFontAscent() const
316 const ::FontMetric
& rMetric
= mrDevice
.GetFontMetric();
317 return rMetric
.GetAscent();
320 double TextLayouterDevice::getFontDescent() const
322 const ::FontMetric
& rMetric
= mrDevice
.GetFontMetric();
323 return rMetric
.GetDescent();
326 void TextLayouterDevice::addTextRectActions(
327 const ::tools::Rectangle
& rRectangle
,
328 const OUString
& rText
,
329 DrawTextFlags nStyle
,
330 GDIMetaFile
& rGDIMetaFile
) const
332 mrDevice
.AddTextRectActions(
333 rRectangle
, rText
, nStyle
, rGDIMetaFile
);
336 std::vector
< double > TextLayouterDevice::getTextArray(
337 const OUString
& rText
,
339 sal_uInt32 nLength
) const
341 std::vector
< double > aRetval
;
342 sal_uInt32
nTextLength(nLength
);
343 const sal_uInt32
nStringLength(rText
.getLength());
345 if(nTextLength
+ nIndex
> nStringLength
)
347 nTextLength
= nStringLength
- nIndex
;
352 aRetval
.reserve(nTextLength
);
353 std::vector
<long> aArray(nTextLength
);
354 mrDevice
.GetTextArray(rText
, aArray
.data(), nIndex
, nLength
);
355 aRetval
.assign(aArray
.begin(), aArray
.end());
361 } // end of namespace primitive2d
362 } // end of namespace drawinglayer
365 // helper methods for vcl font handling
367 namespace drawinglayer
369 namespace primitive2d
371 vcl::Font
getVclFontFromFontAttribute(
372 const attribute::FontAttribute
& rFontAttribute
,
375 double fFontRotation
,
376 const css::lang::Locale
& rLocale
)
378 // detect FontScaling
379 const sal_uInt32
nHeight(basegfx::fround(fabs(fFontScaleY
)));
380 const sal_uInt32
nWidth(basegfx::fround(fabs(fFontScaleX
)));
381 const bool bFontIsScaled(nHeight
!= nWidth
);
384 // for WIN32 systems, start with creating an unscaled font. If FontScaling
385 // is wanted, that width needs to be adapted using FontMetric again to get a
386 // width of the unscaled font
388 rFontAttribute
.getFamilyName(),
389 rFontAttribute
.getStyleName(),
392 // for non-WIN32 systems things are easier since these accept a Font creation
393 // with initially nWidth != nHeight for FontScaling. Despite that, use zero for
394 // FontWidth when no scaling is used to explicitly have that zero when e.g. the
395 // Font would be recorded in a MetaFile (The MetaFile FontAction WILL record a
396 // set FontWidth; import that in a WIN32 system, and trouble is there)
398 rFontAttribute
.getFamilyName(),
399 rFontAttribute
.getStyleName(),
400 Size(bFontIsScaled
? std::max
<sal_uInt32
>(nWidth
, 1) : 0, nHeight
));
402 // define various other FontAttribute
403 aRetval
.SetAlignment(ALIGN_BASELINE
);
404 aRetval
.SetCharSet(rFontAttribute
.getSymbol() ? RTL_TEXTENCODING_SYMBOL
: RTL_TEXTENCODING_UNICODE
);
405 aRetval
.SetVertical(rFontAttribute
.getVertical());
406 aRetval
.SetWeight(static_cast<FontWeight
>(rFontAttribute
.getWeight()));
407 aRetval
.SetItalic(rFontAttribute
.getItalic() ? ITALIC_NORMAL
: ITALIC_NONE
);
408 aRetval
.SetOutline(rFontAttribute
.getOutline());
409 aRetval
.SetPitch(rFontAttribute
.getMonospaced() ? PITCH_FIXED
: PITCH_VARIABLE
);
410 aRetval
.SetLanguage(LanguageTag::convertToLanguageType( rLocale
, false));
412 // handle FontRotation (if defined)
413 if(!basegfx::fTools::equalZero(fFontRotation
))
415 sal_Int16
aRotate10th(static_cast<sal_Int16
>(fFontRotation
* (-1800.0/F_PI
)));
416 aRetval
.SetOrientation(aRotate10th
% 3600);
422 attribute::FontAttribute
getFontAttributeFromVclFont(
423 basegfx::B2DVector
& o_rSize
,
424 const vcl::Font
& rFont
,
428 const attribute::FontAttribute
aRetval(
429 rFont
.GetFamilyName(),
430 rFont
.GetStyleName(),
431 static_cast<sal_uInt16
>(rFont
.GetWeight()),
432 RTL_TEXTENCODING_SYMBOL
== rFont
.GetCharSet(),
434 ITALIC_NONE
!= rFont
.GetItalic(),
435 PITCH_FIXED
== rFont
.GetPitch(),
441 // set FontHeight and init to no FontScaling
442 o_rSize
.setY(std::max
<long>(rFont
.GetFontSize().getHeight(), 0));
443 o_rSize
.setX(o_rSize
.getY());
446 // for WIN32 systems, the FontScaling at the Font is detected by
447 // checking that FontWidth != 0. When FontScaling is used, WIN32
448 // needs to do extra stuff to detect the correct width (since it's
449 // zero and not equal the font height) and its relationship to
451 if(rFont
.GetFontSize().getWidth() > 0)
453 vcl::Font
aUnscaledFont(rFont
);
454 aUnscaledFont
.SetAverageFontWidth(0);
455 const FontMetric
aUnscaledFontMetric(Application::GetDefaultDevice()->GetFontMetric(aUnscaledFont
));
457 if(aUnscaledFontMetric
.GetAverageFontWidth() > 0)
459 const double fScaleFactor(static_cast<double>(rFont
.GetFontSize().getWidth()) / static_cast<double>(aUnscaledFontMetric
.GetAverageFontWidth()));
460 o_rSize
.setX(fScaleFactor
* o_rSize
.getY());
464 // For non-WIN32 systems the detection is the same, but the value
465 // is easier achieved since width == height is interpreted as no
466 // scaling. Ergo, Width == 0 means width == height, and width != 0
467 // means the scaling is in the direct relation of width to height
468 if(rFont
.GetFontSize().getWidth() > 0)
470 o_rSize
.setX(static_cast<double>(rFont
.GetFontSize().getWidth()));
475 } // end of namespace primitive2d
476 } // end of namespace drawinglayer
478 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */