/src/libreoffice/drawinglayer/source/primitive2d/textlayoutdevice.cxx
Line | Count | Source |
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 <com/sun/star/uno/XComponentContext.hpp> |
25 | | |
26 | | #include <basegfx/matrix/b2dhommatrixtools.hxx> |
27 | | #include <drawinglayer/attribute/fontattribute.hxx> |
28 | | #include <drawinglayer/primitive2d/textlayoutdevice.hxx> |
29 | | #include <comphelper/processfactory.hxx> |
30 | | #include <comphelper/unique_disposing_ptr.hxx> |
31 | | #include <osl/diagnose.h> |
32 | | #include <tools/gen.hxx> |
33 | | #include <vcl/kernarray.hxx> |
34 | | #include <vcl/timer.hxx> |
35 | | #include <vcl/virdev.hxx> |
36 | | #include <vcl/font.hxx> |
37 | | #include <vcl/metric.hxx> |
38 | | #include <i18nlangtag/languagetag.hxx> |
39 | | #include <vcl/svapp.hxx> |
40 | | #include <vcl/vcllayout.hxx> |
41 | | #include <vcl/glyphitemcache.hxx> |
42 | | |
43 | | namespace drawinglayer::primitive2d |
44 | | { |
45 | | namespace |
46 | | { |
47 | | class ImpTimedRefDev; |
48 | | |
49 | | // VDev RevDevice provider |
50 | | |
51 | | //the scoped_timed_RefDev owns an ImpTimeRefDev and releases it on dtor |
52 | | //or disposing of the default XComponentContext which causes the underlying |
53 | | //OutputDevice to get released |
54 | | |
55 | | //The ImpTimerRefDev itself, if the timeout ever gets hit, will call |
56 | | //reset on the scoped_timed_RefDev to release the ImpTimerRefDev early |
57 | | //if it's unused for a few minutes |
58 | | class scoped_timed_RefDev : public comphelper::unique_disposing_ptr<ImpTimedRefDev> |
59 | | { |
60 | | public: |
61 | | scoped_timed_RefDev() |
62 | 5 | : comphelper::unique_disposing_ptr<ImpTimedRefDev>( |
63 | 5 | (css::uno::Reference<css::lang::XComponent>( |
64 | 5 | ::comphelper::getProcessComponentContext(), css::uno::UNO_QUERY_THROW))) |
65 | 5 | { |
66 | 5 | } |
67 | | }; |
68 | | |
69 | | class the_scoped_timed_RefDev : public rtl::Static<scoped_timed_RefDev, the_scoped_timed_RefDev> |
70 | | { |
71 | | }; |
72 | | |
73 | | class ImpTimedRefDev : public Timer |
74 | | { |
75 | | scoped_timed_RefDev& mrOwnerOfMe; |
76 | | VclPtr<VirtualDevice> mpVirDev; |
77 | | sal_uInt32 mnUseCount; |
78 | | |
79 | | public: |
80 | | explicit ImpTimedRefDev(scoped_timed_RefDev& rOwnerofMe); |
81 | | virtual ~ImpTimedRefDev() override; |
82 | | virtual void Invoke() override; |
83 | | |
84 | | VirtualDevice& acquireVirtualDevice(); |
85 | | void releaseVirtualDevice(); |
86 | | }; |
87 | | |
88 | | ImpTimedRefDev::ImpTimedRefDev(scoped_timed_RefDev& rOwnerOfMe) |
89 | 5 | : Timer("drawinglayer ImpTimedRefDev destroy mpVirDev") |
90 | 5 | , mrOwnerOfMe(rOwnerOfMe) |
91 | 5 | , mpVirDev(nullptr) |
92 | 5 | , mnUseCount(0) |
93 | 5 | { |
94 | 5 | SetTimeout(3L * 60L * 1000L); // three minutes |
95 | 5 | Start(); |
96 | 5 | } |
97 | | |
98 | | ImpTimedRefDev::~ImpTimedRefDev() |
99 | 5 | { |
100 | 5 | OSL_ENSURE(0 == mnUseCount, "destruction of a still used ImpTimedRefDev (!)"); |
101 | 5 | const SolarMutexGuard aSolarGuard; |
102 | 5 | mpVirDev.disposeAndClear(); |
103 | 5 | } |
104 | | |
105 | | void ImpTimedRefDev::Invoke() |
106 | 0 | { |
107 | | // for obvious reasons, do not call anything after this |
108 | 0 | mrOwnerOfMe.reset(); |
109 | 0 | } |
110 | | |
111 | | VirtualDevice& ImpTimedRefDev::acquireVirtualDevice() |
112 | 1.61k | { |
113 | 1.61k | if (!mpVirDev) |
114 | 5 | { |
115 | 5 | mpVirDev = VclPtr<VirtualDevice>::Create(); |
116 | 5 | mpVirDev->SetReferenceDevice(VirtualDevice::RefDevMode::MSO1); |
117 | 5 | } |
118 | | |
119 | 1.61k | if (!mnUseCount) |
120 | 1.61k | { |
121 | 1.61k | Stop(); |
122 | 1.61k | } |
123 | | |
124 | 1.61k | mnUseCount++; |
125 | | |
126 | 1.61k | return *mpVirDev; |
127 | 1.61k | } |
128 | | |
129 | | void ImpTimedRefDev::releaseVirtualDevice() |
130 | 1.61k | { |
131 | 1.61k | OSL_ENSURE(mnUseCount, "mismatch call number to releaseVirtualDevice() (!)"); |
132 | 1.61k | mnUseCount--; |
133 | | |
134 | 1.61k | if (!mnUseCount) |
135 | 1.61k | { |
136 | 1.61k | Start(); |
137 | 1.61k | } |
138 | 1.61k | } |
139 | | |
140 | | VirtualDevice& acquireGlobalVirtualDevice() |
141 | 1.61k | { |
142 | 1.61k | scoped_timed_RefDev& rStdRefDevice = the_scoped_timed_RefDev::get(); |
143 | | |
144 | 1.61k | if (!rStdRefDevice) |
145 | 5 | rStdRefDevice.reset(new ImpTimedRefDev(rStdRefDevice)); |
146 | | |
147 | 1.61k | return rStdRefDevice->acquireVirtualDevice(); |
148 | 1.61k | } |
149 | | |
150 | | void releaseGlobalVirtualDevice() |
151 | 1.61k | { |
152 | 1.61k | scoped_timed_RefDev& rStdRefDevice = the_scoped_timed_RefDev::get(); |
153 | | |
154 | 1.61k | OSL_ENSURE(rStdRefDevice, |
155 | 1.61k | "releaseGlobalVirtualDevice() without prior acquireGlobalVirtualDevice() call(!)"); |
156 | 1.61k | rStdRefDevice->releaseVirtualDevice(); |
157 | 1.61k | } |
158 | | |
159 | | } // end of anonymous namespace |
160 | | |
161 | | TextLayouterDevice::TextLayouterDevice() |
162 | 1.61k | : mrDevice(acquireGlobalVirtualDevice()) |
163 | 1.61k | { |
164 | | // tdf#168002 activate SubpixelPositioning for al TextLayouterDevice-calls |
165 | 1.61k | mrDevice.setSubpixelPositioning(true); |
166 | 1.61k | } |
167 | | |
168 | | TextLayouterDevice::~TextLayouterDevice() |
169 | 1.61k | { |
170 | 1.61k | suppress_fun_call_w_exception(releaseGlobalVirtualDevice()); |
171 | 1.61k | } |
172 | | |
173 | | void TextLayouterDevice::setFont(const vcl::Font& rFont) |
174 | 1.61k | { |
175 | 1.61k | mrDevice.SetFont(rFont); |
176 | 1.61k | mnFontScalingFixX = 1.0; |
177 | 1.61k | mnFontScalingFixY = 1.0; |
178 | 1.61k | } |
179 | | |
180 | | void TextLayouterDevice::setFontAttribute(const attribute::FontAttribute& rFontAttribute, |
181 | | double fFontScaleX, double fFontScaleY, |
182 | | const css::lang::Locale& rLocale) |
183 | 1.61k | { |
184 | 1.61k | vcl::Font aFont |
185 | 1.61k | = getVclFontFromFontAttribute(rFontAttribute, fFontScaleX, fFontScaleY, 0.0, rLocale); |
186 | 1.61k | setFont(aFont); |
187 | 1.61k | Size aFontSize = aFont.GetFontSize(); |
188 | 1.61k | if (aFontSize.Height()) |
189 | 1.61k | { |
190 | 1.61k | mnFontScalingFixY = fFontScaleY / aFontSize.Height(); |
191 | | // aFontSize.Width() is 0 for uninformly scaled fonts: see getVclFontFromFontAttribute |
192 | 1.61k | mnFontScalingFixX |
193 | 1.61k | = fFontScaleX / (aFontSize.Width() ? aFontSize.Width() : aFontSize.Height()); |
194 | 1.61k | } |
195 | 0 | else |
196 | 0 | { |
197 | 0 | mnFontScalingFixX = mnFontScalingFixY = 1.0; |
198 | 0 | } |
199 | 1.61k | } |
200 | | |
201 | | void TextLayouterDevice::setLayoutMode(vcl::text::ComplexTextLayoutFlags nTextLayoutMode) |
202 | 0 | { |
203 | 0 | mrDevice.SetLayoutMode(nTextLayoutMode); |
204 | 0 | } |
205 | | |
206 | | vcl::text::ComplexTextLayoutFlags TextLayouterDevice::getLayoutMode() const |
207 | 0 | { |
208 | 0 | return mrDevice.GetLayoutMode(); |
209 | 0 | } |
210 | | |
211 | | void TextLayouterDevice::setTextColor(const basegfx::BColor& rColor) |
212 | 0 | { |
213 | 0 | mrDevice.SetTextColor(Color(rColor)); |
214 | 0 | } |
215 | | |
216 | | double TextLayouterDevice::getOverlineOffset() const |
217 | 0 | { |
218 | 0 | const ::FontMetric aMetric = mrDevice.GetFontMetric(); |
219 | 0 | double fRet = (aMetric.GetInternalLeading() / 2.0) - aMetric.GetAscent(); |
220 | 0 | return fRet * mnFontScalingFixY; |
221 | 0 | } |
222 | | |
223 | | double TextLayouterDevice::getUnderlineOffset() const |
224 | 0 | { |
225 | 0 | const ::FontMetric aMetric = mrDevice.GetFontMetric(); |
226 | 0 | double fRet = aMetric.GetDescent() / 2.0; |
227 | 0 | return fRet * mnFontScalingFixY; |
228 | 0 | } |
229 | | |
230 | | double TextLayouterDevice::getStrikeoutOffset() const |
231 | 0 | { |
232 | 0 | const ::FontMetric aMetric = mrDevice.GetFontMetric(); |
233 | 0 | double fRet = (aMetric.GetAscent() - aMetric.GetInternalLeading()) / 3.0; |
234 | 0 | return fRet * mnFontScalingFixY; |
235 | 0 | } |
236 | | |
237 | | double TextLayouterDevice::getOverlineHeight() const |
238 | 0 | { |
239 | 0 | const ::FontMetric aMetric = mrDevice.GetFontMetric(); |
240 | 0 | double fRet = aMetric.GetInternalLeading() / 2.5; |
241 | 0 | return fRet * mnFontScalingFixY; |
242 | 0 | } |
243 | | |
244 | | double TextLayouterDevice::getUnderlineHeight() const |
245 | 0 | { |
246 | 0 | const ::FontMetric aMetric = mrDevice.GetFontMetric(); |
247 | 0 | double fRet = aMetric.GetDescent() / 4.0; |
248 | 0 | return fRet * mnFontScalingFixY; |
249 | 0 | } |
250 | | |
251 | | double TextLayouterDevice::getTextHeight() const |
252 | 0 | { |
253 | 0 | return mrDevice.GetTextHeightDouble() * mnFontScalingFixY; |
254 | 0 | } |
255 | | |
256 | | double TextLayouterDevice::getTextWidth(const OUString& rText, sal_uInt32 nIndex, |
257 | | sal_uInt32 nLength) const |
258 | 0 | { |
259 | 0 | return mrDevice.GetTextWidthDouble(rText, nIndex, nLength) * mnFontScalingFixX; |
260 | 0 | } |
261 | | |
262 | | void TextLayouterDevice::getTextOutlines(basegfx::B2DPolyPolygonVector& rB2DPolyPolyVector, |
263 | | const OUString& rText, sal_uInt32 nIndex, |
264 | | sal_uInt32 nLength, const std::vector<double>& rDXArray, |
265 | | const std::vector<sal_Bool>& rKashidaArray) const |
266 | 400 | { |
267 | 400 | const sal_uInt32 nDXArrayCount(rDXArray.size()); |
268 | 400 | sal_uInt32 nTextLength(nLength); |
269 | 400 | const sal_uInt32 nStringLength(rText.getLength()); |
270 | | |
271 | 400 | if (nTextLength + nIndex > nStringLength) |
272 | 0 | { |
273 | 0 | nTextLength = nStringLength - nIndex; |
274 | 0 | } |
275 | | |
276 | 400 | if (nDXArrayCount) |
277 | 400 | { |
278 | 400 | OSL_ENSURE(nDXArrayCount == nTextLength, |
279 | 400 | "DXArray size does not correspond to text portion size (!)"); |
280 | | |
281 | 400 | mrDevice.GetTextOutlines(rB2DPolyPolyVector, rText, nIndex, nIndex, nLength, 0, rDXArray, |
282 | 400 | rKashidaArray); |
283 | 400 | } |
284 | 0 | else |
285 | 0 | { |
286 | 0 | mrDevice.GetTextOutlines(rB2DPolyPolyVector, rText, nIndex, nIndex, nLength); |
287 | 0 | } |
288 | 400 | if (!rtl_math_approxEqual(mnFontScalingFixY, 1.0) |
289 | 400 | || !rtl_math_approxEqual(mnFontScalingFixX, 1.0)) |
290 | 0 | { |
291 | 0 | auto scale = basegfx::utils::createScaleB2DHomMatrix(mnFontScalingFixX, mnFontScalingFixY); |
292 | 0 | for (auto& poly : rB2DPolyPolyVector) |
293 | 0 | poly.transform(scale); |
294 | 0 | } |
295 | 400 | } |
296 | | |
297 | | basegfx::B2DRange TextLayouterDevice::getTextBoundRect(const OUString& rText, sal_uInt32 nIndex, |
298 | | sal_uInt32 nLength) const |
299 | 1.21k | { |
300 | 1.21k | sal_uInt32 nTextLength(nLength); |
301 | 1.21k | const sal_uInt32 nStringLength(rText.getLength()); |
302 | | |
303 | 1.21k | if (nTextLength + nIndex > nStringLength) |
304 | 0 | { |
305 | 0 | nTextLength = nStringLength - nIndex; |
306 | 0 | } |
307 | | |
308 | 1.21k | if (nTextLength) |
309 | 1.21k | { |
310 | 1.21k | basegfx::B2DRange aRect; |
311 | 1.21k | mrDevice.GetTextBoundRect(aRect, rText, nIndex, nIndex, nLength); |
312 | 1.21k | if (!rtl_math_approxEqual(mnFontScalingFixY, 1.0) |
313 | 1.21k | || !rtl_math_approxEqual(mnFontScalingFixX, 1.0)) |
314 | 0 | { |
315 | 0 | aRect.transform( |
316 | 0 | basegfx::utils::createScaleB2DHomMatrix(mnFontScalingFixX, mnFontScalingFixY)); |
317 | 0 | } |
318 | 1.21k | return aRect; |
319 | 1.21k | } |
320 | | |
321 | 0 | return basegfx::B2DRange(); |
322 | 1.21k | } |
323 | | |
324 | | double TextLayouterDevice::getFontAscent() const |
325 | 0 | { |
326 | 0 | const ::FontMetric aMetric = mrDevice.GetFontMetric(); |
327 | 0 | return aMetric.GetAscent() * mnFontScalingFixY; |
328 | 0 | } |
329 | | |
330 | | double TextLayouterDevice::getFontDescent() const |
331 | 0 | { |
332 | 0 | const ::FontMetric aMetric = mrDevice.GetFontMetric(); |
333 | 0 | return aMetric.GetDescent() * mnFontScalingFixY; |
334 | 0 | } |
335 | | |
336 | | void TextLayouterDevice::addTextRectActions(const ::tools::Rectangle& rRectangle, |
337 | | const OUString& rText, DrawTextFlags nStyle, |
338 | | GDIMetaFile& rGDIMetaFile) const |
339 | 0 | { |
340 | 0 | mrDevice.AddTextRectActions(rRectangle, rText, nStyle, rGDIMetaFile); |
341 | 0 | } |
342 | | |
343 | | std::vector<double> TextLayouterDevice::getTextArray(const OUString& rText, sal_uInt32 nIndex, |
344 | | sal_uInt32 nLength, bool bCaret) const |
345 | 0 | { |
346 | 0 | std::vector<double> aRetval; |
347 | 0 | sal_uInt32 nTextLength(nLength); |
348 | 0 | const sal_uInt32 nStringLength(rText.getLength()); |
349 | |
|
350 | 0 | if (nTextLength + nIndex > nStringLength) |
351 | 0 | { |
352 | 0 | nTextLength = nStringLength - nIndex; |
353 | 0 | } |
354 | |
|
355 | 0 | if (nTextLength) |
356 | 0 | { |
357 | 0 | KernArray aArray; |
358 | 0 | mrDevice.GetTextArray(rText, &aArray, nIndex, nTextLength, bCaret); |
359 | 0 | aRetval.reserve(aArray.size()); |
360 | 0 | for (size_t i = 0, nEnd = aArray.size(); i < nEnd; ++i) |
361 | 0 | aRetval.push_back(aArray[i] * mnFontScalingFixX); |
362 | 0 | } |
363 | |
|
364 | 0 | return aRetval; |
365 | 0 | } |
366 | | |
367 | | std::unique_ptr<SalLayout> |
368 | | TextLayouterDevice::getSalLayout(const OUString& rText, sal_uInt32 nIndex, sal_uInt32 nLength, |
369 | | const basegfx::B2DPoint& rStartPoint, const KernArray& rDXArray, |
370 | | std::span<const sal_Bool> pKashidaAry) const |
371 | 0 | { |
372 | 0 | const SalLayoutGlyphs* pGlyphs( |
373 | 0 | SalLayoutGlyphsCache::self()->GetLayoutGlyphs(&mrDevice, rText, nIndex, nLength)); |
374 | 0 | const Point aStartPoint(basegfx::fround<tools::Long>(rStartPoint.getX()), |
375 | 0 | basegfx::fround<tools::Long>(rStartPoint.getY())); |
376 | 0 | return mrDevice.ImplLayout(rText, nIndex, nLength, aStartPoint, 0, rDXArray, pKashidaAry, |
377 | 0 | SalLayoutFlags::NONE, nullptr, pGlyphs); |
378 | 0 | } |
379 | | |
380 | | void TextLayouterDevice::createEmphasisMarks( |
381 | | const SalLayout& rSalLayout, TextEmphasisMark aTextEmphasisMark, bool bAbove, |
382 | | const std::function<void(const basegfx::B2DPoint&, const basegfx::B2DPolyPolygon&, bool, |
383 | | const tools::Rectangle&, const tools::Rectangle&)>& rCallback) const |
384 | 0 | { |
385 | 0 | FontEmphasisMark nEmphasisMark(FontEmphasisMark::NONE); |
386 | 0 | double fEmphasisHeight(getTextHeight() * (250.0 / 1000.0)); |
387 | |
|
388 | 0 | switch (aTextEmphasisMark) |
389 | 0 | { |
390 | 0 | case TEXT_FONT_EMPHASIS_MARK_DOT: |
391 | 0 | nEmphasisMark = FontEmphasisMark::Dot; |
392 | 0 | break; |
393 | 0 | case TEXT_FONT_EMPHASIS_MARK_CIRCLE: |
394 | 0 | nEmphasisMark = FontEmphasisMark::Circle; |
395 | 0 | break; |
396 | 0 | case TEXT_FONT_EMPHASIS_MARK_DISC: |
397 | 0 | nEmphasisMark = FontEmphasisMark::Disc; |
398 | 0 | break; |
399 | 0 | case TEXT_FONT_EMPHASIS_MARK_ACCENT: |
400 | 0 | nEmphasisMark = FontEmphasisMark::Accent; |
401 | 0 | break; |
402 | 0 | default: |
403 | 0 | break; |
404 | 0 | } |
405 | | |
406 | 0 | if (bAbove) |
407 | 0 | nEmphasisMark |= FontEmphasisMark::PosAbove; |
408 | 0 | else |
409 | 0 | nEmphasisMark |= FontEmphasisMark::PosBelow; |
410 | |
|
411 | 0 | mrDevice.createEmphasisMarks(nEmphasisMark, static_cast<tools::Long>(fEmphasisHeight), |
412 | 0 | rSalLayout, rCallback); |
413 | 0 | } |
414 | | |
415 | | // helper methods for vcl font handling |
416 | | |
417 | | vcl::Font getVclFontFromFontAttribute(const attribute::FontAttribute& rFontAttribute, |
418 | | double fFontScaleX, double fFontScaleY, double fFontRotation, |
419 | | const css::lang::Locale& rLocale) |
420 | 2.42k | { |
421 | | // detect FontScaling |
422 | 2.42k | const sal_uInt32 nHeight(basegfx::fround(fabs(fFontScaleY))); |
423 | 2.42k | const sal_uInt32 nWidth(basegfx::fround(fabs(fFontScaleX))); |
424 | 2.42k | const bool bFontIsScaled(nHeight != nWidth); |
425 | | |
426 | | #ifdef _WIN32 |
427 | | // for WIN32 systems, start with creating an unscaled font. If FontScaling |
428 | | // is wanted, that width needs to be adapted using FontMetric again to get a |
429 | | // width of the unscaled font |
430 | | vcl::Font aRetval(rFontAttribute.getFamilyName(), rFontAttribute.getStyleName(), |
431 | | Size(0, nHeight)); |
432 | | #else |
433 | | // for non-WIN32 systems things are easier since these accept a Font creation |
434 | | // with initially nWidth != nHeight for FontScaling. Despite that, use zero for |
435 | | // FontWidth when no scaling is used to explicitly have that zero when e.g. the |
436 | | // Font would be recorded in a MetaFile (The MetaFile FontAction WILL record a |
437 | | // set FontWidth; import that in a WIN32 system, and trouble is there) |
438 | 2.42k | vcl::Font aRetval(rFontAttribute.getFamilyName(), rFontAttribute.getStyleName(), |
439 | 2.42k | Size(bFontIsScaled ? std::max<sal_uInt32>(nWidth, 1) : 0, nHeight)); |
440 | 2.42k | #endif |
441 | | // define various other FontAttribute |
442 | 2.42k | aRetval.SetAlignment(ALIGN_BASELINE); |
443 | 2.42k | aRetval.SetCharSet(rFontAttribute.getSymbol() ? RTL_TEXTENCODING_SYMBOL |
444 | 2.42k | : RTL_TEXTENCODING_UNICODE); |
445 | 2.42k | aRetval.SetVertical(rFontAttribute.getVertical()); |
446 | 2.42k | aRetval.SetWeight(static_cast<FontWeight>(rFontAttribute.getWeight())); |
447 | 2.42k | aRetval.SetItalic(rFontAttribute.getItalic() ? ITALIC_NORMAL : ITALIC_NONE); |
448 | 2.42k | aRetval.SetOutline(rFontAttribute.getOutline()); |
449 | 2.42k | aRetval.SetPitch(rFontAttribute.getMonospaced() ? PITCH_FIXED : PITCH_VARIABLE); |
450 | 2.42k | aRetval.SetLanguage(LanguageTag::convertToLanguageType(rLocale, false)); |
451 | | |
452 | | #ifdef _WIN32 |
453 | | // for WIN32 systems, correct the FontWidth if FontScaling is used |
454 | | if (bFontIsScaled && nHeight > 0) |
455 | | { |
456 | | const FontMetric aUnscaledFontMetric( |
457 | | Application::GetDefaultDevice()->GetFontMetric(aRetval)); |
458 | | |
459 | | if (aUnscaledFontMetric.GetAverageFontWidth() > 0) |
460 | | { |
461 | | const double fScaleFactor(static_cast<double>(nWidth) / static_cast<double>(nHeight)); |
462 | | const sal_uInt32 nScaledWidth(basegfx::fround( |
463 | | static_cast<double>(aUnscaledFontMetric.GetAverageFontWidth()) * fScaleFactor)); |
464 | | aRetval.SetAverageFontWidth(nScaledWidth); |
465 | | } |
466 | | } |
467 | | #endif |
468 | | // handle FontRotation (if defined) |
469 | 2.42k | if (!basegfx::fTools::equalZero(fFontRotation)) |
470 | 8 | { |
471 | 8 | int aRotate10th(-basegfx::rad2deg<10>(fFontRotation)); |
472 | 8 | aRetval.SetOrientation(Degree10(aRotate10th % 3600)); |
473 | 8 | } |
474 | | |
475 | 2.42k | return aRetval; |
476 | 2.42k | } |
477 | | |
478 | | attribute::FontAttribute getFontAttributeFromVclFont(basegfx::B2DVector& o_rSize, |
479 | | const vcl::Font& rFont, bool bRTL, |
480 | | bool bBiDiStrong) |
481 | 1.21k | { |
482 | 1.21k | const attribute::FontAttribute aRetval( |
483 | 1.21k | rFont.GetFamilyName(), rFont.GetStyleName(), static_cast<sal_uInt16>(rFont.GetWeight()), |
484 | 1.21k | RTL_TEXTENCODING_SYMBOL == rFont.GetCharSet(), rFont.IsVertical(), |
485 | 1.21k | ITALIC_NONE != rFont.GetItalic(), PITCH_FIXED == rFont.GetPitch(), rFont.IsOutline(), bRTL, |
486 | 1.21k | bBiDiStrong); |
487 | | // TODO: eKerning |
488 | | |
489 | | // set FontHeight and init to no FontScaling |
490 | 1.21k | o_rSize.setY(std::max<tools::Long>(rFont.GetFontSize().getHeight(), 0)); |
491 | 1.21k | o_rSize.setX(o_rSize.getY()); |
492 | | |
493 | | #ifdef _WIN32 |
494 | | // for WIN32 systems, the FontScaling at the Font is detected by |
495 | | // checking that FontWidth != 0. When FontScaling is used, WIN32 |
496 | | // needs to do extra stuff to detect the correct width (since it's |
497 | | // zero and not equal the font height) and its relationship to |
498 | | // the height |
499 | | if (rFont.GetFontSize().getWidth() > 0) |
500 | | { |
501 | | vcl::Font aUnscaledFont(rFont); |
502 | | aUnscaledFont.SetAverageFontWidth(0); |
503 | | const FontMetric aUnscaledFontMetric( |
504 | | Application::GetDefaultDevice()->GetFontMetric(aUnscaledFont)); |
505 | | |
506 | | if (aUnscaledFontMetric.GetAverageFontWidth() > 0) |
507 | | { |
508 | | const double fScaleFactor( |
509 | | static_cast<double>(rFont.GetFontSize().getWidth()) |
510 | | / static_cast<double>(aUnscaledFontMetric.GetAverageFontWidth())); |
511 | | o_rSize.setX(fScaleFactor * o_rSize.getY()); |
512 | | } |
513 | | } |
514 | | #else |
515 | | // For non-WIN32 systems the detection is the same, but the value |
516 | | // is easier achieved since width == height is interpreted as no |
517 | | // scaling. Ergo, Width == 0 means width == height, and width != 0 |
518 | | // means the scaling is in the direct relation of width to height |
519 | 1.21k | if (rFont.GetFontSize().getWidth() > 0) |
520 | 10 | { |
521 | 10 | o_rSize.setX(static_cast<double>(rFont.GetFontSize().getWidth())); |
522 | 10 | } |
523 | 1.21k | #endif |
524 | 1.21k | return aRetval; |
525 | 1.21k | } |
526 | | |
527 | | } // end of namespace |
528 | | |
529 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |