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 <com/sun/star/uno/XComponentContext.hpp>
25 #include <drawinglayer/attribute/fontattribute.hxx>
26 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
27 #include <comphelper/processfactory.hxx>
28 #include <comphelper/unique_disposing_ptr.hxx>
29 #include <osl/diagnose.h>
30 #include <tools/gen.hxx>
31 #include <vcl/canvastools.hxx>
32 #include <vcl/kernarray.hxx>
33 #include <vcl/timer.hxx>
34 #include <vcl/virdev.hxx>
35 #include <vcl/font.hxx>
36 #include <vcl/metric.hxx>
37 #include <i18nlangtag/languagetag.hxx>
38 #include <vcl/svapp.hxx>
40 namespace drawinglayer::primitive2d
46 // VDev RevDevice provider
48 //the scoped_timed_RefDev owns an ImpTimeRefDev and releases it on dtor
49 //or disposing of the default XComponentContext which causes the underlying
50 //OutputDevice to get released
52 //The ImpTimerRefDev itself, if the timeout ever gets hit, will call
53 //reset on the scoped_timed_RefDev to release the ImpTimerRefDev early
54 //if it's unused for a few minutes
55 class scoped_timed_RefDev
: public comphelper::unique_disposing_ptr
<ImpTimedRefDev
>
59 : comphelper::unique_disposing_ptr
<ImpTimedRefDev
>(
60 (css::uno::Reference
<css::lang::XComponent
>(
61 ::comphelper::getProcessComponentContext(), css::uno::UNO_QUERY_THROW
)))
66 class the_scoped_timed_RefDev
: public rtl::Static
<scoped_timed_RefDev
, the_scoped_timed_RefDev
>
70 class ImpTimedRefDev
: public Timer
72 scoped_timed_RefDev
& mrOwnerOfMe
;
73 VclPtr
<VirtualDevice
> mpVirDev
;
74 sal_uInt32 mnUseCount
;
77 explicit ImpTimedRefDev(scoped_timed_RefDev
& rOwnerofMe
);
78 virtual ~ImpTimedRefDev() override
;
79 virtual void Invoke() override
;
81 VirtualDevice
& acquireVirtualDevice();
82 void releaseVirtualDevice();
85 ImpTimedRefDev::ImpTimedRefDev(scoped_timed_RefDev
& rOwnerOfMe
)
86 : Timer("drawinglayer ImpTimedRefDev destroy mpVirDev")
87 , mrOwnerOfMe(rOwnerOfMe
)
91 SetTimeout(3L * 60L * 1000L); // three minutes
95 ImpTimedRefDev::~ImpTimedRefDev()
97 OSL_ENSURE(0 == mnUseCount
, "destruction of a still used ImpTimedRefDev (!)");
98 const SolarMutexGuard aSolarGuard
;
99 mpVirDev
.disposeAndClear();
102 void ImpTimedRefDev::Invoke()
104 // for obvious reasons, do not call anything after this
108 VirtualDevice
& ImpTimedRefDev::acquireVirtualDevice()
112 mpVirDev
= VclPtr
<VirtualDevice
>::Create();
113 mpVirDev
->SetReferenceDevice(VirtualDevice::RefDevMode::MSO1
);
126 void ImpTimedRefDev::releaseVirtualDevice()
128 OSL_ENSURE(mnUseCount
, "mismatch call number to releaseVirtualDevice() (!)");
137 VirtualDevice
& acquireGlobalVirtualDevice()
139 scoped_timed_RefDev
& rStdRefDevice
= the_scoped_timed_RefDev::get();
142 rStdRefDevice
.reset(new ImpTimedRefDev(rStdRefDevice
));
144 return rStdRefDevice
->acquireVirtualDevice();
147 void releaseGlobalVirtualDevice()
149 scoped_timed_RefDev
& rStdRefDevice
= the_scoped_timed_RefDev::get();
151 OSL_ENSURE(rStdRefDevice
,
152 "releaseGlobalVirtualDevice() without prior acquireGlobalVirtualDevice() call(!)");
153 rStdRefDevice
->releaseVirtualDevice();
156 } // end of anonymous namespace
158 TextLayouterDevice::TextLayouterDevice()
159 : mrDevice(acquireGlobalVirtualDevice())
163 TextLayouterDevice::~TextLayouterDevice() COVERITY_NOEXCEPT_FALSE
{ releaseGlobalVirtualDevice(); }
165 void TextLayouterDevice::setFont(const vcl::Font
& rFont
) { mrDevice
.SetFont(rFont
); }
167 void TextLayouterDevice::setFontAttribute(const attribute::FontAttribute
& rFontAttribute
,
168 double fFontScaleX
, double fFontScaleY
,
169 const css::lang::Locale
& rLocale
)
171 setFont(getVclFontFromFontAttribute(rFontAttribute
, fFontScaleX
, fFontScaleY
, 0.0, rLocale
));
174 double TextLayouterDevice::getOverlineOffset() const
176 const ::FontMetric
& rMetric
= mrDevice
.GetFontMetric();
177 double fRet
= (rMetric
.GetInternalLeading() / 2.0) - rMetric
.GetAscent();
181 double TextLayouterDevice::getUnderlineOffset() const
183 const ::FontMetric
& rMetric
= mrDevice
.GetFontMetric();
184 double fRet
= rMetric
.GetDescent() / 2.0;
188 double TextLayouterDevice::getStrikeoutOffset() const
190 const ::FontMetric
& rMetric
= mrDevice
.GetFontMetric();
191 double fRet
= (rMetric
.GetAscent() - rMetric
.GetInternalLeading()) / 3.0;
195 double TextLayouterDevice::getOverlineHeight() const
197 const ::FontMetric
& rMetric
= mrDevice
.GetFontMetric();
198 double fRet
= rMetric
.GetInternalLeading() / 2.5;
202 double TextLayouterDevice::getUnderlineHeight() const
204 const ::FontMetric
& rMetric
= mrDevice
.GetFontMetric();
205 double fRet
= rMetric
.GetDescent() / 4.0;
209 double TextLayouterDevice::getTextHeight() const { return mrDevice
.GetTextHeight(); }
211 double TextLayouterDevice::getTextWidth(const OUString
& rText
, sal_uInt32 nIndex
,
212 sal_uInt32 nLength
) const
214 return mrDevice
.GetTextWidth(rText
, nIndex
, nLength
);
217 void TextLayouterDevice::getTextOutlines(basegfx::B2DPolyPolygonVector
& rB2DPolyPolyVector
,
218 const OUString
& rText
, sal_uInt32 nIndex
,
219 sal_uInt32 nLength
, const std::vector
<double>& rDXArray
,
220 const std::vector
<sal_Bool
>& rKashidaArray
) const
222 const sal_uInt32
nDXArrayCount(rDXArray
.size());
223 sal_uInt32
nTextLength(nLength
);
224 const sal_uInt32
nStringLength(rText
.getLength());
226 if (nTextLength
+ nIndex
> nStringLength
)
228 nTextLength
= nStringLength
- nIndex
;
233 OSL_ENSURE(nDXArrayCount
== nTextLength
,
234 "DXArray size does not correspond to text portion size (!)");
236 KernArray aIntegerDXArray
;
237 aIntegerDXArray
.reserve(nDXArrayCount
);
238 for (sal_uInt32
a(0); a
< nDXArrayCount
; a
++)
239 aIntegerDXArray
.push_back(basegfx::fround(rDXArray
[a
]));
241 mrDevice
.GetTextOutlines(rB2DPolyPolyVector
, rText
, nIndex
, nIndex
, nLength
, 0,
242 aIntegerDXArray
, rKashidaArray
);
246 mrDevice
.GetTextOutlines(rB2DPolyPolyVector
, rText
, nIndex
, nIndex
, nLength
);
250 basegfx::B2DRange
TextLayouterDevice::getTextBoundRect(const OUString
& rText
, sal_uInt32 nIndex
,
251 sal_uInt32 nLength
) const
253 sal_uInt32
nTextLength(nLength
);
254 const sal_uInt32
nStringLength(rText
.getLength());
256 if (nTextLength
+ nIndex
> nStringLength
)
258 nTextLength
= nStringLength
- nIndex
;
263 ::tools::Rectangle aRect
;
265 mrDevice
.GetTextBoundRect(aRect
, rText
, nIndex
, nIndex
, nLength
);
267 // #i104432#, #i102556# take empty results into account
268 if (!aRect
.IsEmpty())
270 return vcl::unotools::b2DRectangleFromRectangle(aRect
);
274 return basegfx::B2DRange();
277 double TextLayouterDevice::getFontAscent() const
279 const ::FontMetric
& rMetric
= mrDevice
.GetFontMetric();
280 return rMetric
.GetAscent();
283 double TextLayouterDevice::getFontDescent() const
285 const ::FontMetric
& rMetric
= mrDevice
.GetFontMetric();
286 return rMetric
.GetDescent();
289 void TextLayouterDevice::addTextRectActions(const ::tools::Rectangle
& rRectangle
,
290 const OUString
& rText
, DrawTextFlags nStyle
,
291 GDIMetaFile
& rGDIMetaFile
) const
293 mrDevice
.AddTextRectActions(rRectangle
, rText
, nStyle
, rGDIMetaFile
);
296 std::vector
<double> TextLayouterDevice::getTextArray(const OUString
& rText
, sal_uInt32 nIndex
,
297 sal_uInt32 nLength
) const
299 std::vector
<double> aRetval
;
300 sal_uInt32
nTextLength(nLength
);
301 const sal_uInt32
nStringLength(rText
.getLength());
303 if (nTextLength
+ nIndex
> nStringLength
)
305 nTextLength
= nStringLength
- nIndex
;
311 mrDevice
.GetTextArray(rText
, &aArray
, nIndex
, nTextLength
);
312 aRetval
.reserve(aArray
.size());
313 for (size_t i
= 0, nEnd
= aArray
.size(); i
< nEnd
; ++i
)
314 aRetval
.push_back(aArray
[i
]);
320 std::vector
<double> TextLayouterDevice::getCaretPositions(const OUString
& rText
, sal_uInt32 nIndex
,
321 sal_uInt32 nLength
) const
323 std::vector
<double> aRetval
;
324 sal_uInt32
nTextLength(nLength
);
325 const sal_uInt32
nStringLength(rText
.getLength());
327 if (nTextLength
+ nIndex
> nStringLength
)
329 nTextLength
= nStringLength
- nIndex
;
334 aRetval
.reserve(2 * nTextLength
);
335 std::vector
<sal_Int32
> aArray(2 * nTextLength
);
336 mrDevice
.GetCaretPositions(rText
, aArray
.data(), nIndex
, nTextLength
);
337 aRetval
.assign(aArray
.begin(), aArray
.end());
343 // helper methods for vcl font handling
345 vcl::Font
getVclFontFromFontAttribute(const attribute::FontAttribute
& rFontAttribute
,
346 double fFontScaleX
, double fFontScaleY
, double fFontRotation
,
347 const css::lang::Locale
& rLocale
)
349 // detect FontScaling
350 const sal_uInt32
nHeight(basegfx::fround(fabs(fFontScaleY
)));
351 const sal_uInt32
nWidth(basegfx::fround(fabs(fFontScaleX
)));
352 const bool bFontIsScaled(nHeight
!= nWidth
);
355 // for WIN32 systems, start with creating an unscaled font. If FontScaling
356 // is wanted, that width needs to be adapted using FontMetric again to get a
357 // width of the unscaled font
358 vcl::Font
aRetval(rFontAttribute
.getFamilyName(), rFontAttribute
.getStyleName(),
361 // for non-WIN32 systems things are easier since these accept a Font creation
362 // with initially nWidth != nHeight for FontScaling. Despite that, use zero for
363 // FontWidth when no scaling is used to explicitly have that zero when e.g. the
364 // Font would be recorded in a MetaFile (The MetaFile FontAction WILL record a
365 // set FontWidth; import that in a WIN32 system, and trouble is there)
366 vcl::Font
aRetval(rFontAttribute
.getFamilyName(), rFontAttribute
.getStyleName(),
367 Size(bFontIsScaled
? std::max
<sal_uInt32
>(nWidth
, 1) : 0, nHeight
));
369 // define various other FontAttribute
370 aRetval
.SetAlignment(ALIGN_BASELINE
);
371 aRetval
.SetCharSet(rFontAttribute
.getSymbol() ? RTL_TEXTENCODING_SYMBOL
372 : RTL_TEXTENCODING_UNICODE
);
373 aRetval
.SetVertical(rFontAttribute
.getVertical());
374 aRetval
.SetWeight(static_cast<FontWeight
>(rFontAttribute
.getWeight()));
375 aRetval
.SetItalic(rFontAttribute
.getItalic() ? ITALIC_NORMAL
: ITALIC_NONE
);
376 aRetval
.SetOutline(rFontAttribute
.getOutline());
377 aRetval
.SetPitch(rFontAttribute
.getMonospaced() ? PITCH_FIXED
: PITCH_VARIABLE
);
378 aRetval
.SetLanguage(LanguageTag::convertToLanguageType(rLocale
, false));
381 // for WIN32 systems, correct the FontWidth if FontScaling is used
382 if (bFontIsScaled
&& nHeight
> 0)
384 const FontMetric
aUnscaledFontMetric(
385 Application::GetDefaultDevice()->GetFontMetric(aRetval
));
387 if (aUnscaledFontMetric
.GetAverageFontWidth() > 0)
389 const double fScaleFactor(static_cast<double>(nWidth
) / static_cast<double>(nHeight
));
390 const sal_uInt32
nScaledWidth(basegfx::fround(
391 static_cast<double>(aUnscaledFontMetric
.GetAverageFontWidth()) * fScaleFactor
));
392 aRetval
.SetAverageFontWidth(nScaledWidth
);
396 // handle FontRotation (if defined)
397 if (!basegfx::fTools::equalZero(fFontRotation
))
399 int aRotate10th(-basegfx::rad2deg
<10>(fFontRotation
));
400 aRetval
.SetOrientation(Degree10(aRotate10th
% 3600));
406 attribute::FontAttribute
getFontAttributeFromVclFont(basegfx::B2DVector
& o_rSize
,
407 const vcl::Font
& rFont
, bool bRTL
,
410 const attribute::FontAttribute
aRetval(
411 rFont
.GetFamilyName(), rFont
.GetStyleName(), static_cast<sal_uInt16
>(rFont
.GetWeight()),
412 RTL_TEXTENCODING_SYMBOL
== rFont
.GetCharSet(), rFont
.IsVertical(),
413 ITALIC_NONE
!= rFont
.GetItalic(), PITCH_FIXED
== rFont
.GetPitch(), rFont
.IsOutline(), bRTL
,
417 // set FontHeight and init to no FontScaling
418 o_rSize
.setY(std::max
<tools::Long
>(rFont
.GetFontSize().getHeight(), 0));
419 o_rSize
.setX(o_rSize
.getY());
422 // for WIN32 systems, the FontScaling at the Font is detected by
423 // checking that FontWidth != 0. When FontScaling is used, WIN32
424 // needs to do extra stuff to detect the correct width (since it's
425 // zero and not equal the font height) and its relationship to
427 if (rFont
.GetFontSize().getWidth() > 0)
429 vcl::Font
aUnscaledFont(rFont
);
430 aUnscaledFont
.SetAverageFontWidth(0);
431 const FontMetric
aUnscaledFontMetric(
432 Application::GetDefaultDevice()->GetFontMetric(aUnscaledFont
));
434 if (aUnscaledFontMetric
.GetAverageFontWidth() > 0)
436 const double fScaleFactor(
437 static_cast<double>(rFont
.GetFontSize().getWidth())
438 / static_cast<double>(aUnscaledFontMetric
.GetAverageFontWidth()));
439 o_rSize
.setX(fScaleFactor
* o_rSize
.getY());
443 // For non-WIN32 systems the detection is the same, but the value
444 // is easier achieved since width == height is interpreted as no
445 // scaling. Ergo, Width == 0 means width == height, and width != 0
446 // means the scaling is in the direct relation of width to height
447 if (rFont
.GetFontSize().getWidth() > 0)
449 o_rSize
.setX(static_cast<double>(rFont
.GetFontSize().getWidth()));
455 } // end of namespace
457 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */