/src/libreoffice/vcl/source/outdev/font.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 <rtl/ustrbuf.hxx> |
23 | | #include <sal/log.hxx> |
24 | | #include <tools/debug.hxx> |
25 | | #include <tools/mapunit.hxx> |
26 | | #include <i18nlangtag/mslangid.hxx> |
27 | | #include <i18nlangtag/lang.h> |
28 | | #include <comphelper/configuration.hxx> |
29 | | |
30 | | #include <vcl/event.hxx> |
31 | | #include <vcl/fontcharmap.hxx> |
32 | | #include <vcl/fntstyle.hxx> |
33 | | #include <vcl/glyphitem.hxx> |
34 | | #include <vcl/metaact.hxx> |
35 | | #include <vcl/metric.hxx> |
36 | | #include <vcl/print.hxx> |
37 | | #include <vcl/rendercontext/AntialiasingFlags.hxx> |
38 | | #include <vcl/rendercontext/GetDefaultFontFlags.hxx> |
39 | | #include <vcl/sysdata.hxx> |
40 | | #include <vcl/virdev.hxx> |
41 | | |
42 | | #include <window.h> |
43 | | #include <font/EmphasisMark.hxx> |
44 | | |
45 | | #include <ImplLayoutArgs.hxx> |
46 | | #include <drawmode.hxx> |
47 | | #include <impfontcache.hxx> |
48 | | #include <font/DirectFontSubstitution.hxx> |
49 | | #include <font/PhysicalFontFaceCollection.hxx> |
50 | | #include <font/PhysicalFontCollection.hxx> |
51 | | #include <font/FeatureCollector.hxx> |
52 | | #include <impglyphitem.hxx> |
53 | | #include <sallayout.hxx> |
54 | | #include <salgdi.hxx> |
55 | | #include <svdata.hxx> |
56 | | |
57 | | #include <unicode/uchar.h> |
58 | | |
59 | | #include <strings.hrc> |
60 | | |
61 | | void OutputDevice::SetFont( const vcl::Font& rNewFont ) |
62 | 23.7M | { |
63 | 23.7M | vcl::Font aFont = vcl::drawmode::GetFont(rNewFont, GetDrawMode(), GetSettings().GetStyleSettings()); |
64 | | |
65 | 23.7M | if ( mpMetaFile ) |
66 | 901k | { |
67 | 901k | mpMetaFile->AddAction( new MetaFontAction( aFont ) ); |
68 | | // the color and alignment actions don't belong here |
69 | | // TODO: get rid of them without breaking anything... |
70 | 901k | mpMetaFile->AddAction( new MetaTextAlignAction( aFont.GetAlignment() ) ); |
71 | 901k | mpMetaFile->AddAction( new MetaTextFillColorAction( aFont.GetFillColor(), !aFont.IsTransparent() ) ); |
72 | 901k | } |
73 | | |
74 | 23.7M | if ( maFont.IsSameInstance( aFont ) ) |
75 | 902k | return; |
76 | | |
77 | | // Optimization MT/HDU: COL_TRANSPARENT means SetFont should ignore the font color, |
78 | | // because SetTextColor() is used for this. |
79 | | // #i28759# maTextColor might have been changed behind our back, commit then, too. |
80 | 22.8M | if( aFont.GetColor() != COL_TRANSPARENT |
81 | 4.39M | && (aFont.GetColor() != maFont.GetColor() || aFont.GetColor() != maTextColor ) ) |
82 | 1.15M | { |
83 | 1.15M | maTextColor = aFont.GetColor(); |
84 | 1.15M | mbInitTextColor = true; |
85 | 1.15M | if( mpMetaFile ) |
86 | 341k | mpMetaFile->AddAction( new MetaTextColorAction( aFont.GetColor() ) ); |
87 | 1.15M | } |
88 | 22.8M | maFont = aFont; |
89 | 22.8M | mbNewFont = true; |
90 | 22.8M | } |
91 | | |
92 | | FontMetric OutputDevice::GetFontMetricFromCollection(int nDevFontIndex) const |
93 | 2.79M | { |
94 | 2.79M | ImplInitFontList(); |
95 | | |
96 | 2.79M | if (nDevFontIndex < GetFontFaceCollectionCount()) |
97 | 2.79M | return FontMetric(*mpFontFaceCollection->Get(nDevFontIndex)); |
98 | | |
99 | 0 | return FontMetric(); |
100 | 2.79M | } |
101 | | |
102 | | int OutputDevice::GetFontFaceCollectionCount() const |
103 | 3.03M | { |
104 | 3.03M | if( !mpFontFaceCollection ) |
105 | 187k | { |
106 | 187k | if (!mxFontCollection) |
107 | 0 | { |
108 | 0 | return 0; |
109 | 0 | } |
110 | | |
111 | 187k | mpFontFaceCollection = mxFontCollection->GetFontFaceCollection(); |
112 | | |
113 | 187k | if (!mpFontFaceCollection->Count()) |
114 | 0 | { |
115 | 0 | mpFontFaceCollection.reset(); |
116 | 0 | return 0; |
117 | 0 | } |
118 | 187k | } |
119 | 3.03M | return mpFontFaceCollection->Count(); |
120 | 3.03M | } |
121 | | |
122 | | const std::unordered_map<OUString, OUString>& OutputDevice::GetFontFamilyAliases() const |
123 | 192k | { |
124 | 192k | ImplInitFontList(); |
125 | 192k | return mxFontCollection->GetFontFamilyNameAliases(); |
126 | 192k | } |
127 | | |
128 | | bool OutputDevice::IsFontAvailable( std::u16string_view rFontName ) const |
129 | 13.4k | { |
130 | 13.4k | ImplInitFontList(); |
131 | 13.4k | vcl::font::PhysicalFontFamily* pFound = mxFontCollection->FindFontFamily( rFontName ); |
132 | 13.4k | return (pFound != nullptr); |
133 | 13.4k | } |
134 | | |
135 | | bool OutputDevice::AddTempDevFont( const OUString& rFileURL, const OUString& rFontName ) |
136 | 0 | { |
137 | 0 | ImplInitFontList(); |
138 | |
|
139 | 0 | if( !mpGraphics && !AcquireGraphics() ) |
140 | 0 | return false; |
141 | 0 | assert(mpGraphics); |
142 | |
|
143 | 0 | bool bRC = mpGraphics->AddTempDevFont( mxFontCollection.get(), rFileURL, rFontName ); |
144 | 0 | if( !bRC ) |
145 | 0 | return false; |
146 | | |
147 | 0 | return true; |
148 | 0 | } |
149 | | |
150 | | bool OutputDevice::RemoveTempDevFont(const OUString& rFileURL, const OUString& rFontName) |
151 | 0 | { |
152 | 0 | if( !mpGraphics && !AcquireGraphics() ) |
153 | 0 | return true; // No graphics -> no fonts used |
154 | 0 | assert(mpGraphics); |
155 | |
|
156 | 0 | return mpGraphics->RemoveTempDevFont(rFileURL, rFontName); |
157 | 0 | } |
158 | | |
159 | | bool OutputDevice::GetFontFeatures(std::vector<vcl::font::Feature>& rFontFeatures) const |
160 | 0 | { |
161 | 0 | if (!ImplNewFont()) |
162 | 0 | return false; |
163 | | |
164 | 0 | LogicalFontInstance* pFontInstance = mpFontInstance.get(); |
165 | 0 | if (!pFontInstance) |
166 | 0 | return false; |
167 | | |
168 | 0 | const LanguageTag& rOfficeLanguage = Application::GetSettings().GetUILanguageTag(); |
169 | |
|
170 | 0 | vcl::font::FeatureCollector aFeatureCollector(pFontInstance->GetFontFace(), rFontFeatures, rOfficeLanguage); |
171 | 0 | aFeatureCollector.collect(); |
172 | |
|
173 | 0 | return true; |
174 | 0 | } |
175 | | |
176 | | FontMetric OutputDevice::GetFontMetric() const |
177 | 27.9M | { |
178 | 27.9M | FontMetric aMetric; |
179 | 27.9M | if (!ImplNewFont()) |
180 | 0 | return aMetric; |
181 | | |
182 | 27.9M | LogicalFontInstance* pFontInstance = mpFontInstance.get(); |
183 | 27.9M | FontMetricDataRef xFontMetric = pFontInstance->mxFontMetric; |
184 | | |
185 | | // prepare metric |
186 | 27.9M | aMetric = maFont; |
187 | | |
188 | | // set aMetric with info from font |
189 | 27.9M | aMetric.SetFamilyName( maFont.GetFamilyName() ); |
190 | 27.9M | aMetric.SetStyleName( xFontMetric->GetStyleName() ); |
191 | 27.9M | aMetric.SetFontSize( PixelToLogic( Size( xFontMetric->GetWidth(), xFontMetric->GetAscent() + xFontMetric->GetDescent() - xFontMetric->GetInternalLeading() ) ) ); |
192 | 27.9M | aMetric.SetCharSet( xFontMetric->IsMicrosoftSymbolEncoded() ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE ); |
193 | 27.9M | aMetric.SetFamily( xFontMetric->GetFamilyType() ); |
194 | 27.9M | aMetric.SetPitch( xFontMetric->GetPitch() ); |
195 | 27.9M | aMetric.SetWeight( xFontMetric->GetWeight() ); |
196 | 27.9M | aMetric.SetItalic( xFontMetric->GetItalic() ); |
197 | 27.9M | aMetric.SetAlignment( TextAlign::ALIGN_TOP ); |
198 | 27.9M | aMetric.SetWidthType( xFontMetric->GetWidthType() ); |
199 | 27.9M | if ( pFontInstance->mnOwnOrientation ) |
200 | 0 | aMetric.SetOrientation( pFontInstance->mnOwnOrientation ); |
201 | 27.9M | else |
202 | 27.9M | aMetric.SetOrientation( xFontMetric->GetOrientation() ); |
203 | | |
204 | | // set remaining metric fields |
205 | 27.9M | aMetric.SetFullstopCenteredFlag( xFontMetric->IsFullstopCentered() ); |
206 | 27.9M | aMetric.SetBulletOffset( xFontMetric->GetBulletOffset() ); |
207 | 27.9M | aMetric.SetAscent( ImplDevicePixelToLogicHeight( xFontMetric->GetAscent() + mnEmphasisAscent ) ); |
208 | 27.9M | aMetric.SetDescent( ImplDevicePixelToLogicHeight( xFontMetric->GetDescent() + mnEmphasisDescent ) ); |
209 | 27.9M | aMetric.SetInternalLeading( ImplDevicePixelToLogicHeight( xFontMetric->GetInternalLeading() + mnEmphasisAscent ) ); |
210 | | // OutputDevice has its own external leading function due to #i60945# |
211 | 27.9M | aMetric.SetExternalLeading( ImplDevicePixelToLogicHeight( GetFontExtLeading() ) ); |
212 | 27.9M | aMetric.SetLineHeight( ImplDevicePixelToLogicHeight( xFontMetric->GetAscent() + xFontMetric->GetDescent() + mnEmphasisAscent + mnEmphasisDescent ) ); |
213 | 27.9M | aMetric.SetSlant( ImplDevicePixelToLogicHeight( xFontMetric->GetSlant() ) ); |
214 | 27.9M | aMetric.SetHangingBaseline( ImplDevicePixelToLogicHeight( xFontMetric->GetHangingBaseline() ) ); |
215 | | |
216 | 27.9M | aMetric.SetUnitEm(ImplDevicePixelToLogicWidth(xFontMetric->GetUnitEm())); |
217 | 27.9M | aMetric.SetHorCJKAdvance(ImplDevicePixelToLogicWidth(xFontMetric->GetHorCJKAdvance())); |
218 | 27.9M | aMetric.SetVertCJKAdvance(ImplDevicePixelToLogicHeight(xFontMetric->GetVertCJKAdvance())); |
219 | | |
220 | | // get miscellaneous data |
221 | 27.9M | aMetric.SetQuality( xFontMetric->GetQuality() ); |
222 | | |
223 | 27.9M | SAL_INFO("vcl.gdi.fontmetric", "OutputDevice::GetFontMetric:" << aMetric); |
224 | | |
225 | 27.9M | return aMetric; |
226 | 27.9M | } |
227 | | |
228 | | FontMetric OutputDevice::GetFontMetric( const vcl::Font& rFont ) const |
229 | 0 | { |
230 | | // select font, query metrics, select original font again |
231 | 0 | vcl::Font aOldFont = GetFont(); |
232 | 0 | const_cast<OutputDevice*>(this)->SetFont( rFont ); |
233 | 0 | FontMetric aMetric( GetFontMetric() ); |
234 | 0 | const_cast<OutputDevice*>(this)->SetFont( aOldFont ); |
235 | 0 | return aMetric; |
236 | 0 | } |
237 | | |
238 | | bool OutputDevice::GetFontCharMap( FontCharMapRef& rxFontCharMap ) const |
239 | 211k | { |
240 | 211k | if (!InitFont()) |
241 | 0 | return false; |
242 | | |
243 | 211k | FontCharMapRef xFontCharMap ( mpGraphics->GetFontCharMap() ); |
244 | 211k | if (!xFontCharMap.is()) |
245 | 0 | rxFontCharMap = FontCharMapRef(new FontCharMap()); |
246 | 211k | else |
247 | 211k | rxFontCharMap = std::move(xFontCharMap); |
248 | | |
249 | 211k | return !rxFontCharMap->IsDefaultMap(); |
250 | 211k | } |
251 | | |
252 | | bool OutputDevice::GetFontCapabilities( vcl::FontCapabilities& rFontCapabilities ) const |
253 | 67.8k | { |
254 | 67.8k | if (!InitFont()) |
255 | 0 | return false; |
256 | 67.8k | return mpGraphics->GetFontCapabilities(rFontCapabilities); |
257 | 67.8k | } |
258 | | |
259 | | tools::Long OutputDevice::GetFontExtLeading() const |
260 | 2.87M | { |
261 | 2.87M | return mpFontInstance->mxFontMetric->GetExternalLeading(); |
262 | 2.87M | } |
263 | | |
264 | | void OutputDevice::ImplClearFontData( const bool bNewFontLists ) |
265 | 0 | { |
266 | | // the currently selected logical font is no longer needed |
267 | 0 | mpFontInstance.clear(); |
268 | |
|
269 | 0 | mbInitFont = true; |
270 | 0 | mbNewFont = true; |
271 | |
|
272 | 0 | if ( bNewFontLists ) |
273 | 0 | { |
274 | 0 | mpFontFaceCollection.reset(); |
275 | | |
276 | | // release all physically selected fonts on this device |
277 | 0 | if( AcquireGraphics() ) |
278 | 0 | mpGraphics->ReleaseFonts(); |
279 | 0 | } |
280 | |
|
281 | 0 | ImplSVData* pSVData = ImplGetSVData(); |
282 | |
|
283 | 0 | if (mxFontCache && mxFontCache != pSVData->maGDIData.mxScreenFontCache) |
284 | 0 | mxFontCache->Invalidate(); |
285 | |
|
286 | 0 | if (bNewFontLists && AcquireGraphics()) |
287 | 0 | { |
288 | 0 | if (mxFontCollection && mxFontCollection != pSVData->maGDIData.mxScreenFontList) |
289 | 0 | mxFontCollection->Clear(); |
290 | 0 | } |
291 | 0 | } |
292 | | |
293 | | void OutputDevice::RefreshFontData( const bool bNewFontLists ) |
294 | 0 | { |
295 | 0 | ImplRefreshFontData( bNewFontLists ); |
296 | 0 | } |
297 | | |
298 | | void OutputDevice::ImplRefreshFontData( const bool bNewFontLists ) |
299 | 0 | { |
300 | 0 | if (bNewFontLists && AcquireGraphics()) |
301 | 0 | mpGraphics->GetDevFontList( mxFontCollection.get() ); |
302 | 0 | } |
303 | | |
304 | | void OutputDevice::ImplUpdateFontData() |
305 | 0 | { |
306 | 0 | ImplClearFontData( true/*bNewFontLists*/ ); |
307 | 0 | ImplRefreshFontData( true/*bNewFontLists*/ ); |
308 | 0 | } |
309 | | |
310 | | void OutputDevice::ImplClearAllFontData(bool bNewFontLists) |
311 | 0 | { |
312 | 0 | ImplSVData* pSVData = ImplGetSVData(); |
313 | |
|
314 | 0 | ImplUpdateFontDataForAllFrames( &OutputDevice::ImplClearFontData, bNewFontLists ); |
315 | | |
316 | | // clear global font lists to have them updated |
317 | 0 | pSVData->maGDIData.mxScreenFontCache->Invalidate(); |
318 | 0 | if ( !bNewFontLists ) |
319 | 0 | return; |
320 | | |
321 | 0 | pSVData->maGDIData.mxScreenFontList->Clear(); |
322 | 0 | vcl::Window * pFrame = pSVData->maFrameData.mpFirstFrame; |
323 | 0 | if (!pFrame) |
324 | 0 | return; |
325 | | |
326 | 0 | if ( pFrame->GetOutDev()->AcquireGraphics() ) |
327 | 0 | { |
328 | 0 | OutputDevice *pDevice = pFrame->GetOutDev(); |
329 | 0 | pDevice->mpGraphics->ClearDevFontCache(); |
330 | 0 | pDevice->mpGraphics->GetDevFontList(pFrame->mpWindowImpl->mpFrameData->mxFontCollection.get()); |
331 | 0 | } |
332 | 0 | } |
333 | | |
334 | | void OutputDevice::ImplRefreshAllFontData(bool bNewFontLists) |
335 | 0 | { |
336 | 0 | ImplUpdateFontDataForAllFrames( &OutputDevice::ImplRefreshFontData, bNewFontLists ); |
337 | 0 | } |
338 | | |
339 | | void OutputDevice::ImplUpdateAllFontData(bool bNewFontLists) |
340 | 0 | { |
341 | 0 | OutputDevice::ImplClearAllFontData(bNewFontLists); |
342 | 0 | OutputDevice::ImplRefreshAllFontData(bNewFontLists); |
343 | 0 | } |
344 | | |
345 | | void OutputDevice::ImplUpdateFontDataForAllFrames( const FontUpdateHandler_t pHdl, const bool bNewFontLists ) |
346 | 0 | { |
347 | 0 | ImplSVData* const pSVData = ImplGetSVData(); |
348 | | |
349 | | // update all windows |
350 | 0 | vcl::Window* pFrame = pSVData->maFrameData.mpFirstFrame; |
351 | 0 | while ( pFrame ) |
352 | 0 | { |
353 | 0 | ( pFrame->GetOutDev()->*pHdl )( bNewFontLists ); |
354 | |
|
355 | 0 | vcl::Window* pSysWin = pFrame->mpWindowImpl->mpFrameData->mpFirstOverlap; |
356 | 0 | while ( pSysWin ) |
357 | 0 | { |
358 | 0 | ( pSysWin->GetOutDev()->*pHdl )( bNewFontLists ); |
359 | 0 | pSysWin = pSysWin->mpWindowImpl->mpNextOverlap; |
360 | 0 | } |
361 | |
|
362 | 0 | pFrame = pFrame->mpWindowImpl->mpFrameData->mpNextFrame; |
363 | 0 | } |
364 | | |
365 | | // update all virtual devices |
366 | 0 | VirtualDevice* pVirDev = pSVData->maGDIData.mpFirstVirDev; |
367 | 0 | while ( pVirDev ) |
368 | 0 | { |
369 | 0 | ( pVirDev->*pHdl )( bNewFontLists ); |
370 | 0 | pVirDev = pVirDev->mpNext; |
371 | 0 | } |
372 | | |
373 | | // update all printers |
374 | 0 | Printer* pPrinter = pSVData->maGDIData.mpFirstPrinter; |
375 | 0 | while ( pPrinter ) |
376 | 0 | { |
377 | 0 | ( pPrinter->*pHdl )( bNewFontLists ); |
378 | 0 | pPrinter = pPrinter->mpNext; |
379 | 0 | } |
380 | 0 | } |
381 | | |
382 | | void OutputDevice::BeginFontSubstitution() |
383 | 0 | { |
384 | 0 | ImplSVData* pSVData = ImplGetSVData(); |
385 | 0 | pSVData->maGDIData.mbFontSubChanged = false; |
386 | 0 | } |
387 | | |
388 | | void OutputDevice::EndFontSubstitution() |
389 | 0 | { |
390 | 0 | ImplSVData* pSVData = ImplGetSVData(); |
391 | 0 | if ( pSVData->maGDIData.mbFontSubChanged ) |
392 | 0 | { |
393 | 0 | ImplUpdateAllFontData( false ); |
394 | |
|
395 | 0 | DataChangedEvent aDCEvt( DataChangedEventType::FONTSUBSTITUTION ); |
396 | 0 | Application::ImplCallEventListenersApplicationDataChanged(&aDCEvt); |
397 | 0 | Application::NotifyAllWindows( aDCEvt ); |
398 | 0 | pSVData->maGDIData.mbFontSubChanged = false; |
399 | 0 | } |
400 | 0 | } |
401 | | |
402 | | void OutputDevice::AddFontSubstitute( const OUString& rFontName, |
403 | | const OUString& rReplaceFontName, |
404 | | AddFontSubstituteFlags nFlags ) |
405 | 0 | { |
406 | 0 | vcl::font::DirectFontSubstitution*& rpSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst; |
407 | 0 | if( !rpSubst ) |
408 | 0 | rpSubst = new vcl::font::DirectFontSubstitution; |
409 | 0 | rpSubst->AddFontSubstitute( rFontName, rReplaceFontName, nFlags ); |
410 | 0 | ImplGetSVData()->maGDIData.mbFontSubChanged = true; |
411 | 0 | } |
412 | | |
413 | | void OutputDevice::RemoveFontsSubstitute() |
414 | 0 | { |
415 | 0 | vcl::font::DirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst; |
416 | 0 | if( pSubst ) |
417 | 0 | pSubst->RemoveFontsSubstitute(); |
418 | 0 | } |
419 | | |
420 | | //hidpi TODO: This routine has hard-coded font-sizes that break places such as DialControl |
421 | | vcl::Font OutputDevice::GetDefaultFont( DefaultFontType nType, LanguageType eLang, |
422 | | GetDefaultFontFlags nFlags, const OutputDevice* pOutDev ) |
423 | 5.28M | { |
424 | 5.28M | static bool bFuzzing = comphelper::IsFuzzing(); |
425 | 5.28M | static bool bAbortOnFontSubstitute = [] { |
426 | 31 | const char* pEnv = getenv("SAL_NON_APPLICATION_FONT_USE"); |
427 | 31 | return pEnv && strcmp(pEnv, "abort") == 0; |
428 | 31 | }(); |
429 | | |
430 | 5.28M | if (!pOutDev && !bFuzzing) // default is NULL |
431 | 0 | pOutDev = Application::GetDefaultDevice(); |
432 | | |
433 | 5.28M | OUString aSearch; |
434 | 5.28M | if (!bFuzzing) |
435 | 0 | { |
436 | 0 | LanguageTag aLanguageTag( |
437 | 0 | ( eLang == LANGUAGE_NONE || eLang == LANGUAGE_SYSTEM || eLang == LANGUAGE_DONTKNOW ) ? |
438 | 0 | Application::GetSettings().GetUILanguageTag() : |
439 | 0 | LanguageTag( eLang )); |
440 | |
|
441 | 0 | utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get(); |
442 | 0 | OUString aDefault = rDefaults.getDefaultFont( aLanguageTag, nType ); |
443 | |
|
444 | 0 | if( !aDefault.isEmpty() ) |
445 | 0 | aSearch = aDefault; |
446 | 0 | else |
447 | 0 | aSearch = rDefaults.getUserInterfaceFont( aLanguageTag ); // use the UI font as a fallback |
448 | | |
449 | | // during cppunit tests with SAL_NON_APPLICATION_FONT_USE set we don't have any bundled fonts |
450 | | // that support the default CTL and CJK languages of Hindi and Chinese, so just pick something |
451 | | // (unsuitable) that does exist, if they get used with SAL_NON_APPLICATION_FONT_USE=abort then |
452 | | // glyph fallback will trigger std::abort |
453 | 0 | if (bAbortOnFontSubstitute) |
454 | 0 | { |
455 | 0 | if (eLang == LANGUAGE_HINDI || eLang == LANGUAGE_CHINESE_SIMPLIFIED) |
456 | 0 | aSearch = "DejaVu Sans"; |
457 | 0 | } |
458 | 0 | } |
459 | 5.28M | else |
460 | 5.28M | aSearch = "Liberation Serif"; |
461 | | |
462 | 5.28M | vcl::Font aFont; |
463 | 5.28M | aFont.SetPitch( PITCH_VARIABLE ); |
464 | | |
465 | 5.28M | switch ( nType ) |
466 | 5.28M | { |
467 | 0 | case DefaultFontType::SANS_UNICODE: |
468 | 0 | case DefaultFontType::UI_SANS: |
469 | 0 | case DefaultFontType::SANS: |
470 | 154k | case DefaultFontType::LATIN_HEADING: |
471 | 223k | case DefaultFontType::LATIN_SPREADSHEET: |
472 | 223k | case DefaultFontType::LATIN_DISPLAY: |
473 | 223k | aFont.SetFamily( FAMILY_SWISS ); |
474 | 223k | break; |
475 | | |
476 | 0 | case DefaultFontType::SERIF: |
477 | 1.38M | case DefaultFontType::LATIN_TEXT: |
478 | 1.46M | case DefaultFontType::LATIN_PRESENTATION: |
479 | 1.46M | aFont.SetFamily( FAMILY_ROMAN ); |
480 | 1.46M | break; |
481 | | |
482 | 233k | case DefaultFontType::FIXED: |
483 | 233k | case DefaultFontType::LATIN_FIXED: |
484 | 233k | case DefaultFontType::UI_FIXED: |
485 | 233k | aFont.SetPitch( PITCH_FIXED ); |
486 | 233k | aFont.SetFamily( FAMILY_MODERN ); |
487 | 233k | break; |
488 | | |
489 | 0 | case DefaultFontType::SYMBOL: |
490 | 0 | aFont.SetCharSet( RTL_TEXTENCODING_SYMBOL ); |
491 | 0 | break; |
492 | | |
493 | 1.38M | case DefaultFontType::CJK_TEXT: |
494 | 1.46M | case DefaultFontType::CJK_PRESENTATION: |
495 | 1.53M | case DefaultFontType::CJK_SPREADSHEET: |
496 | 1.68M | case DefaultFontType::CJK_HEADING: |
497 | 1.68M | case DefaultFontType::CJK_DISPLAY: |
498 | 3.06M | case DefaultFontType::CTL_TEXT: |
499 | 3.14M | case DefaultFontType::CTL_PRESENTATION: |
500 | 3.21M | case DefaultFontType::CTL_SPREADSHEET: |
501 | 3.37M | case DefaultFontType::CTL_HEADING: |
502 | 3.37M | case DefaultFontType::CTL_DISPLAY: |
503 | 3.37M | aFont.SetFamily( FAMILY_SYSTEM ); // don't care, but don't use font subst config later... |
504 | 3.37M | break; |
505 | 5.28M | } |
506 | | |
507 | 5.28M | if ( !aSearch.isEmpty() ) |
508 | 5.28M | { |
509 | 5.28M | aFont.SetFontHeight( 12 ); // corresponds to nDefaultHeight |
510 | 5.28M | aFont.SetWeight( WEIGHT_NORMAL ); |
511 | 5.28M | aFont.SetLanguage( eLang ); |
512 | | |
513 | 5.28M | if ( aFont.GetCharSet() == RTL_TEXTENCODING_DONTKNOW ) |
514 | 5.28M | aFont.SetCharSet( osl_getThreadTextEncoding() ); |
515 | | |
516 | | // Should we only return available fonts on the given device |
517 | 5.28M | if ( pOutDev ) |
518 | 0 | { |
519 | 0 | pOutDev->ImplInitFontList(); |
520 | | |
521 | | // Search Font in the FontList |
522 | 0 | OUString aName; |
523 | 0 | sal_Int32 nIndex = 0; |
524 | 0 | do |
525 | 0 | { |
526 | 0 | vcl::font::PhysicalFontFamily* pFontFamily = pOutDev->mxFontCollection->FindFontFamily( GetNextFontToken( aSearch, nIndex ) ); |
527 | 0 | if( pFontFamily ) |
528 | 0 | { |
529 | 0 | AddTokenFontName( aName, pFontFamily->GetFamilyName() ); |
530 | 0 | if( nFlags & GetDefaultFontFlags::OnlyOne ) |
531 | 0 | break; |
532 | 0 | } |
533 | 0 | } |
534 | 0 | while ( nIndex != -1 ); |
535 | 0 | aFont.SetFamilyName( aName ); |
536 | 0 | } |
537 | | |
538 | | // No Name, then set all names |
539 | 5.28M | if ( aFont.GetFamilyName().isEmpty() ) |
540 | 5.28M | { |
541 | 5.28M | if ( nFlags & GetDefaultFontFlags::OnlyOne ) |
542 | 5.06M | { |
543 | 5.06M | if( !pOutDev ) |
544 | 5.06M | { |
545 | 5.06M | SAL_WARN_IF(!comphelper::IsFuzzing(), "vcl.gdi", "No default window has been set for the application - we really shouldn't be able to get here"); |
546 | 5.06M | aFont.SetFamilyName( aSearch.getToken( 0, ';' ) ); |
547 | 5.06M | } |
548 | 0 | else |
549 | 0 | { |
550 | 0 | pOutDev->ImplInitFontList(); |
551 | |
|
552 | 0 | aFont.SetFamilyName( aSearch ); |
553 | | |
554 | | // convert to pixel height |
555 | 0 | Size aSize = pOutDev->ImplLogicToDevicePixel( aFont.GetFontSize() ); |
556 | 0 | if ( !aSize.Height() ) |
557 | 0 | { |
558 | | // use default pixel height only when logical height is zero |
559 | 0 | if ( aFont.GetFontHeight() ) |
560 | 0 | aSize.setHeight( 1 ); |
561 | 0 | else |
562 | 0 | aSize.setHeight( (12*pOutDev->mnDPIY)/72 ); |
563 | 0 | } |
564 | | |
565 | | // use default width only when logical width is zero |
566 | 0 | if( (0 == aSize.Width()) && (0 != aFont.GetFontSize().Width()) ) |
567 | 0 | aSize.setWidth( 1 ); |
568 | | |
569 | | // get the name of the first available font |
570 | 0 | float fExactHeight = static_cast<float>(aSize.Height()); |
571 | 0 | rtl::Reference<LogicalFontInstance> pFontInstance = pOutDev->mxFontCache->GetFontInstance( pOutDev->mxFontCollection.get(), aFont, aSize, fExactHeight ); |
572 | 0 | if (pFontInstance) |
573 | 0 | { |
574 | 0 | assert(pFontInstance->GetFontFace()); |
575 | 0 | aFont.SetFamilyName(pFontInstance->GetFontFace()->GetFamilyName()); |
576 | 0 | } |
577 | 0 | } |
578 | 5.06M | } |
579 | 221k | else |
580 | 221k | aFont.SetFamilyName( aSearch ); |
581 | 5.28M | } |
582 | 5.28M | } |
583 | | |
584 | | #if OSL_DEBUG_LEVEL > 2 |
585 | | const char* s = "SANS_UNKNOWN"; |
586 | | switch ( nType ) |
587 | | { |
588 | | case DefaultFontType::SANS_UNICODE: s = "SANS_UNICODE"; break; |
589 | | case DefaultFontType::UI_SANS: s = "UI_SANS"; break; |
590 | | |
591 | | case DefaultFontType::SANS: s = "SANS"; break; |
592 | | case DefaultFontType::LATIN_HEADING: s = "LATIN_HEADING"; break; |
593 | | case DefaultFontType::LATIN_SPREADSHEET: s = "LATIN_SPREADSHEET"; break; |
594 | | case DefaultFontType::LATIN_DISPLAY: s = "LATIN_DISPLAY"; break; |
595 | | |
596 | | case DefaultFontType::SERIF: s = "SERIF"; break; |
597 | | case DefaultFontType::LATIN_TEXT: s = "LATIN_TEXT"; break; |
598 | | case DefaultFontType::LATIN_PRESENTATION: s = "LATIN_PRESENTATION"; break; |
599 | | |
600 | | case DefaultFontType::FIXED: s = "FIXED"; break; |
601 | | case DefaultFontType::LATIN_FIXED: s = "LATIN_FIXED"; break; |
602 | | case DefaultFontType::UI_FIXED: s = "UI_FIXED"; break; |
603 | | |
604 | | case DefaultFontType::SYMBOL: s = "SYMBOL"; break; |
605 | | |
606 | | case DefaultFontType::CJK_TEXT: s = "CJK_TEXT"; break; |
607 | | case DefaultFontType::CJK_PRESENTATION: s = "CJK_PRESENTATION"; break; |
608 | | case DefaultFontType::CJK_SPREADSHEET: s = "CJK_SPREADSHEET"; break; |
609 | | case DefaultFontType::CJK_HEADING: s = "CJK_HEADING"; break; |
610 | | case DefaultFontType::CJK_DISPLAY: s = "CJK_DISPLAY"; break; |
611 | | |
612 | | case DefaultFontType::CTL_TEXT: s = "CTL_TEXT"; break; |
613 | | case DefaultFontType::CTL_PRESENTATION: s = "CTL_PRESENTATION"; break; |
614 | | case DefaultFontType::CTL_SPREADSHEET: s = "CTL_SPREADSHEET"; break; |
615 | | case DefaultFontType::CTL_HEADING: s = "CTL_HEADING"; break; |
616 | | case DefaultFontType::CTL_DISPLAY: s = "CTL_DISPLAY"; break; |
617 | | } |
618 | | SAL_INFO("vcl.gdi", |
619 | | "OutputDevice::GetDefaultFont() Type=" << s |
620 | | << " lang=" << eLang |
621 | | << " flags=" << static_cast<int>(nFlags) |
622 | | << " family=\"" << aFont.GetFamilyName() << "\""); |
623 | | #endif |
624 | | |
625 | 5.28M | return aFont; |
626 | 5.28M | } |
627 | | |
628 | | void OutputDevice::ImplInitFontList() const |
629 | 17.5M | { |
630 | 17.5M | if( mxFontCollection->Count() ) |
631 | 17.5M | return; |
632 | | |
633 | 31 | if( !(mpGraphics || AcquireGraphics()) ) |
634 | 0 | return; |
635 | 31 | assert(mpGraphics); |
636 | | |
637 | 31 | SAL_INFO( "vcl.gdi", "OutputDevice::ImplInitFontList()" ); |
638 | 31 | mpGraphics->GetDevFontList(mxFontCollection.get()); |
639 | | |
640 | | // There is absolutely no way there should be no fonts available on the device |
641 | 31 | if( !mxFontCollection->Count() ) |
642 | 0 | { |
643 | 0 | OUString aError( u"Application error: no fonts and no vcl resource found on your system"_ustr ); |
644 | 0 | OUString aResStr(VclResId(SV_ACCESSERROR_NO_FONTS)); |
645 | 0 | if (!aResStr.isEmpty()) |
646 | 0 | aError = aResStr; |
647 | 0 | Application::Abort(aError); |
648 | 0 | } |
649 | 31 | } |
650 | | |
651 | | bool OutputDevice::InitFont() const |
652 | 57.2M | { |
653 | 57.2M | DBG_TESTSOLARMUTEX(); |
654 | | |
655 | 57.2M | if (!ImplNewFont()) |
656 | 0 | return false; |
657 | 57.2M | if (!mpFontInstance) |
658 | 0 | return false; |
659 | 57.2M | if (!mpGraphics) |
660 | 0 | { |
661 | 0 | if (!AcquireGraphics()) |
662 | 0 | return false; |
663 | 0 | } |
664 | 57.2M | else if (!mbInitFont) |
665 | 52.7M | return true; |
666 | | |
667 | 57.2M | assert(mpGraphics); |
668 | 4.47M | mpGraphics->SetFont(mpFontInstance.get(), 0); |
669 | 4.47M | mbInitFont = false; |
670 | 4.47M | return true; |
671 | 57.2M | } |
672 | | |
673 | | const LogicalFontInstance* OutputDevice::GetFontInstance() const |
674 | 11.4M | { |
675 | 11.4M | if (!InitFont()) |
676 | 0 | return nullptr; |
677 | 11.4M | return mpFontInstance.get(); |
678 | 11.4M | } |
679 | | |
680 | | bool OutputDevice::ImplNewFont() const |
681 | 85.2M | { |
682 | 85.2M | DBG_TESTSOLARMUTEX(); |
683 | | |
684 | 85.2M | if ( !mbNewFont ) |
685 | 70.6M | return true; |
686 | | |
687 | | // we need a graphics |
688 | 14.5M | if ( !mpGraphics && !AcquireGraphics() ) |
689 | 0 | { |
690 | 0 | SAL_WARN("vcl.gdi", "OutputDevice::ImplNewFont(): no Graphics, no Font"); |
691 | 0 | return false; |
692 | 0 | } |
693 | 14.5M | assert(mpGraphics); |
694 | | |
695 | 14.5M | ImplInitFontList(); |
696 | | |
697 | | // convert to pixel height |
698 | | // TODO: replace integer based aSize completely with subpixel accurate type |
699 | 14.5M | float fExactHeight = ImplLogicHeightToDeviceSubPixel(maFont.GetFontHeight()); |
700 | 14.5M | Size aSize = ImplLogicToDevicePixel( maFont.GetFontSize() ); |
701 | 14.5M | if ( !aSize.Height() ) |
702 | 14.4k | { |
703 | | // use default pixel height only when logical height is zero |
704 | 14.4k | if ( maFont.GetFontSize().Height() ) |
705 | 512 | aSize.setHeight( 1 ); |
706 | 13.8k | else |
707 | 13.8k | aSize.setHeight( (12*mnDPIY)/72 ); |
708 | 14.4k | fExactHeight = static_cast<float>(aSize.Height()); |
709 | 14.4k | } |
710 | | |
711 | | // select the default width only when logical width is zero |
712 | 14.5M | if( (0 == aSize.Width()) && (0 != maFont.GetFontSize().Width()) ) |
713 | 198 | aSize.setWidth( 1 ); |
714 | | |
715 | | // decide if antialiasing is appropriate |
716 | 14.5M | bool bNonAntialiased(GetAntialiasing() & AntialiasingFlags::DisableText); |
717 | 14.5M | if (!comphelper::IsFuzzing()) |
718 | 0 | { |
719 | 0 | const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); |
720 | 0 | bNonAntialiased |= bool(rStyleSettings.GetDisplayOptions() & DisplayOptions::AADisable); |
721 | 0 | bNonAntialiased |= (int(rStyleSettings.GetAntialiasingMinPixelHeight()) > maFont.GetFontSize().Height()); |
722 | 0 | } |
723 | | |
724 | | // get font entry |
725 | 14.5M | rtl::Reference<LogicalFontInstance> pOldFontInstance = mpFontInstance; |
726 | 14.5M | mpFontInstance = mxFontCache->GetFontInstance(mxFontCollection.get(), maFont, aSize, fExactHeight, bNonAntialiased); |
727 | 14.5M | const bool bNewFontInstance = pOldFontInstance.get() != mpFontInstance.get(); |
728 | 14.5M | pOldFontInstance.clear(); |
729 | | |
730 | 14.5M | LogicalFontInstance* pFontInstance = mpFontInstance.get(); |
731 | | |
732 | 14.5M | if (!pFontInstance) |
733 | 0 | { |
734 | 0 | SAL_WARN("vcl.gdi", "OutputDevice::ImplNewFont(): no LogicalFontInstance, no Font"); |
735 | 0 | return false; |
736 | 0 | } |
737 | | |
738 | | |
739 | | // Compute font size in points for optical sizing. |
740 | 14.5M | if (!pFontInstance->GetPointSize()) |
741 | 219k | { |
742 | 219k | auto nHeight = maFont.GetFontHeight(); |
743 | 219k | auto eFrom = MapToO3tlLength(GetMapMode().GetMapUnit()); |
744 | 219k | float fPointSize = o3tl::convert(float(nHeight), eFrom, o3tl::Length::pt); |
745 | 219k | pFontInstance->SetPointSize(fPointSize); |
746 | 219k | } |
747 | | |
748 | | // mark when lower layers need to get involved |
749 | 14.5M | mbNewFont = false; |
750 | 14.5M | if( bNewFontInstance ) |
751 | 5.31M | mbInitFont = true; |
752 | | |
753 | | // select font when it has not been initialized yet |
754 | 14.5M | if (!pFontInstance->mbInit && InitFont()) |
755 | 210k | { |
756 | | // get metric data from device layers |
757 | 210k | pFontInstance->mbInit = true; |
758 | | |
759 | 210k | pFontInstance->mxFontMetric->SetOrientation( mpFontInstance->GetFontSelectPattern().mnOrientation ); |
760 | 210k | mpGraphics->GetFontMetric( pFontInstance->mxFontMetric, 0 ); |
761 | | |
762 | 210k | pFontInstance->mxFontMetric->ImplInitTextLineSize( this ); |
763 | 210k | pFontInstance->mxFontMetric->ImplInitAboveTextLineSize( this ); |
764 | 210k | pFontInstance->mxFontMetric->ImplInitFlags( this ); |
765 | | |
766 | 210k | pFontInstance->mnLineHeight = pFontInstance->mxFontMetric->GetAscent() + pFontInstance->mxFontMetric->GetDescent(); |
767 | | |
768 | 210k | SetFontOrientation( pFontInstance ); |
769 | 210k | } |
770 | | |
771 | | // calculate EmphasisArea |
772 | 14.5M | mnEmphasisAscent = 0; |
773 | 14.5M | mnEmphasisDescent = 0; |
774 | 14.5M | if ( maFont.GetEmphasisMark() & FontEmphasisMark::Style ) |
775 | 1.88k | { |
776 | 1.88k | FontEmphasisMark nEmphasisMark = maFont.GetEmphasisMarkStyle(); |
777 | 1.88k | tools::Long nEmphasisHeight = (pFontInstance->mnLineHeight*250)/1000; |
778 | 1.88k | if ( nEmphasisHeight < 1 ) |
779 | 429 | nEmphasisHeight = 1; |
780 | 1.88k | if ( nEmphasisMark & FontEmphasisMark::PosBelow ) |
781 | 1.05k | mnEmphasisDescent = nEmphasisHeight; |
782 | 828 | else |
783 | 828 | mnEmphasisAscent = nEmphasisHeight; |
784 | 1.88k | } |
785 | | |
786 | | // calculate text offset depending on TextAlignment |
787 | 14.5M | TextAlign eAlign = maFont.GetAlignment(); |
788 | 14.5M | if ( eAlign == ALIGN_BASELINE ) |
789 | 10.7M | { |
790 | 10.7M | mnTextOffX = 0; |
791 | 10.7M | mnTextOffY = 0; |
792 | 10.7M | } |
793 | 3.84M | else if ( eAlign == ALIGN_TOP ) |
794 | 3.11M | { |
795 | 3.11M | mnTextOffX = 0; |
796 | 3.11M | mnTextOffY = +pFontInstance->mxFontMetric->GetAscent() + mnEmphasisAscent; |
797 | 3.11M | if ( pFontInstance->mnOrientation ) |
798 | 49.5k | { |
799 | 49.5k | Point aOriginPt(0, 0); |
800 | 49.5k | aOriginPt.RotateAround( mnTextOffX, mnTextOffY, pFontInstance->mnOrientation ); |
801 | 49.5k | } |
802 | 3.11M | } |
803 | 738k | else // eAlign == ALIGN_BOTTOM |
804 | 738k | { |
805 | 738k | mnTextOffX = 0; |
806 | 738k | mnTextOffY = -pFontInstance->mxFontMetric->GetDescent() + mnEmphasisDescent; |
807 | 738k | if ( pFontInstance->mnOrientation ) |
808 | 127k | { |
809 | 127k | Point aOriginPt(0, 0); |
810 | 127k | aOriginPt.RotateAround( mnTextOffX, mnTextOffY, pFontInstance->mnOrientation ); |
811 | 127k | } |
812 | 738k | } |
813 | | |
814 | 14.5M | mbTextLines = ((maFont.GetUnderline() != LINESTYLE_NONE) && (maFont.GetUnderline() != LINESTYLE_DONTKNOW)) || |
815 | 14.2M | ((maFont.GetOverline() != LINESTYLE_NONE) && (maFont.GetOverline() != LINESTYLE_DONTKNOW)) || |
816 | 14.2M | ((maFont.GetStrikeout() != STRIKEOUT_NONE) && (maFont.GetStrikeout() != STRIKEOUT_DONTKNOW)); |
817 | 14.5M | mbTextSpecial = maFont.IsShadow() || maFont.IsOutline() || |
818 | 14.1M | (maFont.GetRelief() != FontRelief::NONE); |
819 | | |
820 | | |
821 | 14.5M | bool bRet = true; |
822 | | |
823 | | // #95414# fix for OLE objects which use scale factors very creatively |
824 | 14.5M | if (mbMap && !aSize.Width()) |
825 | 14.3M | bRet = AttemptOLEFontScaleFix(const_cast<vcl::Font&>(maFont), aSize.Height()); |
826 | | |
827 | 14.5M | return bRet; |
828 | 14.5M | } |
829 | | |
830 | | bool OutputDevice::AttemptOLEFontScaleFix(vcl::Font& rFont, tools::Long nHeight) const |
831 | 14.3M | { |
832 | 14.3M | if (maMapRes.mfMapScY == 0.0) |
833 | 0 | return false; |
834 | 14.3M | const double fStretch = maMapRes.mfMapScX / maMapRes.mfMapScY; |
835 | 14.3M | const int nOrigWidth = mpFontInstance->mxFontMetric->GetWidth(); |
836 | 14.3M | const int nNewWidth = static_cast<int>(nOrigWidth * fStretch + 0.5); |
837 | | |
838 | 14.3M | if (nNewWidth == nOrigWidth || nNewWidth == 0) |
839 | 14.3M | return true; |
840 | | |
841 | 1.14k | Size aOrigSize = rFont.GetFontSize(); |
842 | 1.14k | rFont.SetFontSize(Size(nNewWidth, nHeight)); |
843 | 1.14k | mbMap = false; |
844 | 1.14k | mbNewFont = true; |
845 | | |
846 | 1.14k | const bool bRet = ImplNewFont(); // recurse once using stretched width |
847 | | |
848 | 1.14k | mbMap = true; |
849 | 1.14k | rFont.SetFontSize(aOrigSize); |
850 | | |
851 | 1.14k | return bRet; |
852 | 14.3M | } |
853 | | |
854 | | void OutputDevice::SetFontOrientation( LogicalFontInstance* const pFontInstance ) const |
855 | 205k | { |
856 | 205k | if( pFontInstance->GetFontSelectPattern().mnOrientation && !pFontInstance->mxFontMetric->GetOrientation() ) |
857 | 0 | { |
858 | 0 | pFontInstance->mnOwnOrientation = pFontInstance->GetFontSelectPattern().mnOrientation; |
859 | 0 | pFontInstance->mnOrientation = pFontInstance->mnOwnOrientation; |
860 | 0 | } |
861 | 205k | else |
862 | 205k | { |
863 | 205k | pFontInstance->mnOrientation = pFontInstance->mxFontMetric->GetOrientation(); |
864 | 205k | } |
865 | 205k | } |
866 | | |
867 | | void OutputDevice::ImplDrawEmphasisMark( tools::Long nBaseX, tools::Long nX, tools::Long nY, |
868 | | const tools::PolyPolygon& rPolyPoly, bool bPolyLine, |
869 | | const tools::Rectangle& rRect1, const tools::Rectangle& rRect2 ) |
870 | 7.08M | { |
871 | 7.08M | if( IsRTLEnabled() ) |
872 | 0 | nX = nBaseX - (nX - nBaseX - 1); |
873 | | |
874 | 7.08M | nX -= mnOutOffX; |
875 | 7.08M | nY -= mnOutOffY; |
876 | | |
877 | 7.08M | if ( rPolyPoly.Count() ) |
878 | 1.97M | { |
879 | 1.97M | if ( bPolyLine ) |
880 | 651k | { |
881 | 651k | tools::Polygon aPoly = rPolyPoly.GetObject( 0 ); |
882 | 651k | aPoly.Move( nX, nY ); |
883 | 651k | DrawPolyLine( aPoly ); |
884 | 651k | } |
885 | 1.32M | else |
886 | 1.32M | { |
887 | 1.32M | tools::PolyPolygon aPolyPoly = rPolyPoly; |
888 | 1.32M | aPolyPoly.Move( nX, nY ); |
889 | 1.32M | DrawPolyPolygon( aPolyPoly ); |
890 | 1.32M | } |
891 | 1.97M | } |
892 | | |
893 | 7.08M | if ( !rRect1.IsEmpty() ) |
894 | 637k | { |
895 | 637k | tools::Rectangle aRect( Point( nX+rRect1.Left(), |
896 | 637k | nY+rRect1.Top() ), rRect1.GetSize() ); |
897 | 637k | DrawRect( aRect ); |
898 | 637k | } |
899 | | |
900 | 7.08M | if ( !rRect2.IsEmpty() ) |
901 | 326 | { |
902 | 326 | tools::Rectangle aRect( Point( nX+rRect2.Left(), |
903 | 326 | nY+rRect2.Top() ), rRect2.GetSize() ); |
904 | | |
905 | 326 | DrawRect( aRect ); |
906 | 326 | } |
907 | 7.08M | } |
908 | | |
909 | | void OutputDevice::ImplDrawEmphasisMarks( SalLayout& rSalLayout ) |
910 | 10.3k | { |
911 | 10.3k | auto popIt = ScopedPush(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR | vcl::PushFlags::MAPMODE); |
912 | 10.3k | GDIMetaFile* pOldMetaFile = mpMetaFile; |
913 | 10.3k | mpMetaFile = nullptr; |
914 | 10.3k | EnableMapMode( false ); |
915 | | |
916 | 10.3k | FontEmphasisMark nEmphasisMark = maFont.GetEmphasisMarkStyle(); |
917 | 10.3k | tools::Long nEmphasisHeight; |
918 | | |
919 | 10.3k | if ( nEmphasisMark & FontEmphasisMark::PosBelow ) |
920 | 6.32k | nEmphasisHeight = mnEmphasisDescent; |
921 | 4.02k | else |
922 | 4.02k | nEmphasisHeight = mnEmphasisAscent; |
923 | | |
924 | 10.3k | vcl::font::EmphasisMark aEmphasisMark(nEmphasisMark, nEmphasisHeight, GetDPIY()); |
925 | | |
926 | 10.3k | if (aEmphasisMark.IsShapePolyLine()) |
927 | 2.62k | { |
928 | 2.62k | SetLineColor( GetTextColor() ); |
929 | 2.62k | SetFillColor(); |
930 | 2.62k | } |
931 | 7.72k | else |
932 | 7.72k | { |
933 | 7.72k | SetLineColor(); |
934 | 7.72k | SetFillColor( GetTextColor() ); |
935 | 7.72k | } |
936 | | |
937 | 10.3k | Point aOffset(0,0); |
938 | 10.3k | Point aOffsetVert(0,0); |
939 | | |
940 | 10.3k | if ( nEmphasisMark & FontEmphasisMark::PosBelow ) |
941 | 6.32k | { |
942 | 6.32k | aOffset.AdjustY(mpFontInstance->mxFontMetric->GetDescent() + aEmphasisMark.GetYOffset()); |
943 | 6.32k | aOffsetVert = aOffset; |
944 | 6.32k | } |
945 | 4.02k | else |
946 | 4.02k | { |
947 | 4.02k | aOffset.AdjustY(-(mpFontInstance->mxFontMetric->GetAscent() + aEmphasisMark.GetYOffset())); |
948 | | // Todo: use ideographic em-box or ideographic character face information. |
949 | 4.02k | aOffsetVert.AdjustY(-(mpFontInstance->mxFontMetric->GetAscent() + |
950 | 4.02k | mpFontInstance->mxFontMetric->GetDescent() + aEmphasisMark.GetYOffset())); |
951 | 4.02k | } |
952 | | |
953 | 10.3k | tools::Long nEmphasisWidth2 = aEmphasisMark.GetWidth() / 2; |
954 | 10.3k | tools::Long nEmphasisHeight2 = nEmphasisHeight / 2; |
955 | 10.3k | aOffset += Point( nEmphasisWidth2, nEmphasisHeight2 ); |
956 | | |
957 | 10.3k | basegfx::B2DPoint aOutPoint; |
958 | 10.3k | basegfx::B2DRectangle aRectangle; |
959 | 10.3k | const GlyphItem* pGlyph; |
960 | 10.3k | const LogicalFontInstance* pGlyphFont; |
961 | 10.3k | int nStart = 0; |
962 | 7.21M | while (rSalLayout.GetNextGlyph(&pGlyph, aOutPoint, nStart, &pGlyphFont)) |
963 | 7.20M | { |
964 | 7.20M | if (!pGlyph->GetGlyphBoundRect(pGlyphFont, aRectangle)) |
965 | 0 | continue; |
966 | | |
967 | 7.20M | if (!pGlyph->IsSpacing()) |
968 | 7.08M | { |
969 | 7.08M | Point aAdjPoint; |
970 | 7.08M | if (pGlyph->IsVertical()) |
971 | 1.71M | { |
972 | 1.71M | aAdjPoint = aOffsetVert; |
973 | 1.71M | aAdjPoint.AdjustX((-pGlyph->origWidth() + aEmphasisMark.GetWidth()) / 2); |
974 | 1.71M | } |
975 | 5.36M | else |
976 | 5.36M | { |
977 | 5.36M | aAdjPoint = aOffset; |
978 | 5.36M | aAdjPoint.AdjustX(aRectangle.getMinX() + (aRectangle.getWidth() - aEmphasisMark.GetWidth()) / 2 ); |
979 | 5.36M | } |
980 | | |
981 | 7.08M | if ( mpFontInstance->mnOrientation ) |
982 | 5.47M | { |
983 | 5.47M | Point aOriginPt(0, 0); |
984 | 5.47M | aOriginPt.RotateAround( aAdjPoint, mpFontInstance->mnOrientation ); |
985 | 5.47M | } |
986 | 7.08M | aOutPoint.adjustX(aAdjPoint.X() - nEmphasisWidth2); |
987 | 7.08M | aOutPoint.adjustY(aAdjPoint.Y() - nEmphasisHeight2); |
988 | 7.08M | ImplDrawEmphasisMark( rSalLayout.DrawBase().getX(), |
989 | 7.08M | aOutPoint.getX(), aOutPoint.getY(), |
990 | 7.08M | aEmphasisMark.GetShape(), aEmphasisMark.IsShapePolyLine(), |
991 | 7.08M | aEmphasisMark.GetRect1(), aEmphasisMark.GetRect2() ); |
992 | 7.08M | } |
993 | 7.20M | } |
994 | | |
995 | 10.3k | mpMetaFile = pOldMetaFile; |
996 | 10.3k | } |
997 | | |
998 | | std::unique_ptr<SalLayout> OutputDevice::getFallbackLayout( |
999 | | LogicalFontInstance* pLogicalFont, int nFallbackLevel, |
1000 | | vcl::text::ImplLayoutArgs& rLayoutArgs, const SalLayoutGlyphs* pGlyphs) const |
1001 | 0 | { |
1002 | | // we need a graphics |
1003 | 0 | if (!mpGraphics && !AcquireGraphics()) |
1004 | 0 | return nullptr; |
1005 | | |
1006 | 0 | assert(mpGraphics != nullptr); |
1007 | 0 | mpGraphics->SetFont( pLogicalFont, nFallbackLevel ); |
1008 | |
|
1009 | 0 | rLayoutArgs.ResetPos(); |
1010 | 0 | std::unique_ptr<GenericSalLayout> pFallback = mpGraphics->GetTextLayout(nFallbackLevel); |
1011 | |
|
1012 | 0 | if (!pFallback) |
1013 | 0 | return nullptr; |
1014 | | |
1015 | 0 | if (!pFallback->LayoutText(rLayoutArgs, pGlyphs ? pGlyphs->Impl(nFallbackLevel) : nullptr)) |
1016 | 0 | { |
1017 | | // there is no need for a font that couldn't resolve anything |
1018 | 0 | return nullptr; |
1019 | 0 | } |
1020 | | |
1021 | 0 | return pFallback; |
1022 | 0 | } |
1023 | | |
1024 | | bool OutputDevice::ForceFallbackFont(vcl::Font const& rFallbackFont) |
1025 | 0 | { |
1026 | 0 | vcl::Font aOldFont = GetFont(); |
1027 | 0 | SetFont(rFallbackFont); |
1028 | 0 | if (!InitFont()) |
1029 | 0 | return false; |
1030 | | |
1031 | 0 | mpForcedFallbackInstance = mpFontInstance; |
1032 | 0 | SetFont(aOldFont); |
1033 | 0 | if (!InitFont()) |
1034 | 0 | return false; |
1035 | | |
1036 | 0 | if (mpForcedFallbackInstance) |
1037 | 0 | return true; |
1038 | | |
1039 | 0 | return false; |
1040 | 0 | } |
1041 | | |
1042 | | std::unique_ptr<SalLayout> OutputDevice::ImplGlyphFallbackLayout( std::unique_ptr<SalLayout> pSalLayout, |
1043 | | vcl::text::ImplLayoutArgs& rLayoutArgs, const SalLayoutGlyphs* pGlyphs ) const |
1044 | 0 | { |
1045 | | // This function relies on a valid mpFontInstance, if it doesn't exist bail out |
1046 | | // - we'd have crashed later on anyway. At least here we can catch the error in debug |
1047 | | // mode. |
1048 | 0 | if ( !mpFontInstance ) |
1049 | 0 | { |
1050 | 0 | SAL_WARN ("vcl.gdi", "No font entry set in OutputDevice"); |
1051 | 0 | assert(mpFontInstance); |
1052 | 0 | return nullptr; |
1053 | 0 | } |
1054 | | |
1055 | | // prepare multi level glyph fallback |
1056 | 0 | std::unique_ptr<MultiSalLayout> pMultiSalLayout; |
1057 | 0 | ImplLayoutRuns aLayoutRuns = rLayoutArgs.maRuns; |
1058 | 0 | rLayoutArgs.PrepareFallback(nullptr); |
1059 | 0 | rLayoutArgs.mnFlags |= SalLayoutFlags::ForFallback; |
1060 | | |
1061 | | // get list of code units that need glyph fallback |
1062 | 0 | bool bRTL; |
1063 | 0 | int nMinRunPos, nEndRunPos; |
1064 | 0 | OUStringBuffer aMissingCodeBuf(512); |
1065 | 0 | while (rLayoutArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRTL)) |
1066 | 0 | aMissingCodeBuf.append(rLayoutArgs.mrStr.subView(nMinRunPos, nEndRunPos - nMinRunPos)); |
1067 | 0 | rLayoutArgs.ResetPos(); |
1068 | 0 | OUString aMissingCodes = aMissingCodeBuf.makeStringAndClear(); |
1069 | |
|
1070 | 0 | vcl::font::FontSelectPattern aFontSelData(mpFontInstance->GetFontSelectPattern()); |
1071 | 0 | SalLayoutGlyphsImpl* pGlyphsImpl = pGlyphs ? pGlyphs->Impl(1) : nullptr; |
1072 | |
|
1073 | 0 | bool bHasUsedFallback = false; |
1074 | | |
1075 | | // try if fallback fonts support the missing code units |
1076 | 0 | for( int nFallbackLevel = 1; nFallbackLevel < MAX_FALLBACK; ++nFallbackLevel ) |
1077 | 0 | { |
1078 | 0 | rtl::Reference<LogicalFontInstance> pFallbackFont; |
1079 | 0 | if (!bHasUsedFallback && mpForcedFallbackInstance) |
1080 | 0 | { |
1081 | 0 | pFallbackFont = mpForcedFallbackInstance; |
1082 | 0 | bHasUsedFallback = true; |
1083 | 0 | } |
1084 | 0 | else if(pGlyphsImpl != nullptr) |
1085 | 0 | { |
1086 | 0 | pFallbackFont = pGlyphsImpl->GetFont(); |
1087 | 0 | } |
1088 | | |
1089 | | // find a font family suited for glyph fallback |
1090 | | // GetGlyphFallbackFont() needs a valid FontInstance |
1091 | | // if the system-specific glyph fallback is active |
1092 | 0 | OUString oldMissingCodes = aMissingCodes; |
1093 | 0 | if( !pFallbackFont ) |
1094 | 0 | pFallbackFont = mxFontCache->GetGlyphFallbackFont( mxFontCollection.get(), |
1095 | 0 | aFontSelData, mpFontInstance.get(), nFallbackLevel, aMissingCodes ); |
1096 | 0 | if( !pFallbackFont ) |
1097 | 0 | break; |
1098 | | |
1099 | 0 | SAL_INFO("vcl", "Fallback font (level " << nFallbackLevel << "): family: " << pFallbackFont->GetFontFace()->GetFamilyName() |
1100 | 0 | << ", style: " << pFallbackFont->GetFontFace()->GetStyleName()); |
1101 | | |
1102 | 0 | if( nFallbackLevel < MAX_FALLBACK-1) |
1103 | 0 | { |
1104 | | // ignore fallback font if it is the same as the original font |
1105 | | // TODO: This seems broken. Either the font does not provide any of the missing |
1106 | | // codes, in which case the fallback should not select it. Or it does provide |
1107 | | // some of the missing codes, and then why weren't they used the first time? |
1108 | | // This will just loop repeatedly finding the same font (it used to remove |
1109 | | // the found font from mxFontCache, but doesn't do that anymore and I don't |
1110 | | // see how doing that would remove the font from consideration for fallback). |
1111 | 0 | if( mpFontInstance->GetFontFace() == pFallbackFont->GetFontFace()) |
1112 | 0 | { |
1113 | 0 | if(aMissingCodes != oldMissingCodes) |
1114 | 0 | { |
1115 | 0 | SAL_INFO("vcl.gdi", "Font fallback to the same font, but has missing codes"); |
1116 | | // Restore the missing codes if we're not going to use this font. |
1117 | 0 | aMissingCodes = oldMissingCodes; |
1118 | 0 | } |
1119 | 0 | continue; |
1120 | 0 | } |
1121 | 0 | } |
1122 | | |
1123 | | // create and add glyph fallback layout to multilayout |
1124 | 0 | std::unique_ptr<SalLayout> pFallback = getFallbackLayout(pFallbackFont.get(), |
1125 | 0 | nFallbackLevel, rLayoutArgs, pGlyphs); |
1126 | 0 | if (pFallback) |
1127 | 0 | { |
1128 | 0 | if( !pMultiSalLayout ) |
1129 | 0 | pMultiSalLayout.reset( new MultiSalLayout( std::move(pSalLayout) ) ); |
1130 | 0 | pMultiSalLayout->AddFallback(std::move(pFallback), rLayoutArgs.maRuns); |
1131 | 0 | if (nFallbackLevel == MAX_FALLBACK-1) |
1132 | 0 | pMultiSalLayout->SetIncomplete(true); |
1133 | 0 | } |
1134 | |
|
1135 | 0 | if (pGlyphs != nullptr) |
1136 | 0 | pGlyphsImpl = pGlyphs->Impl(nFallbackLevel + 1); |
1137 | | |
1138 | | // break when this fallback was sufficient |
1139 | 0 | if( !rLayoutArgs.PrepareFallback(pGlyphsImpl) ) |
1140 | 0 | break; |
1141 | 0 | } |
1142 | | |
1143 | 0 | if (pMultiSalLayout) // due to missing glyphs, multilevel layout fallback attempted |
1144 | 0 | { |
1145 | | // if it works, use that Layout |
1146 | 0 | if (pMultiSalLayout->LayoutText(rLayoutArgs, nullptr)) |
1147 | 0 | pSalLayout = std::move(pMultiSalLayout); |
1148 | 0 | else |
1149 | 0 | { |
1150 | | // if it doesn't, give up and restore ownership of the pSalLayout |
1151 | | // back to its original state |
1152 | 0 | pSalLayout = pMultiSalLayout->ReleaseBaseLayout(); |
1153 | 0 | } |
1154 | 0 | } |
1155 | | |
1156 | | // restore orig font settings |
1157 | 0 | rLayoutArgs.maRuns = std::move(aLayoutRuns); |
1158 | |
|
1159 | 0 | return pSalLayout; |
1160 | 0 | } |
1161 | | |
1162 | | tools::Long OutputDevice::GetMinKashida() const |
1163 | 4.35k | { |
1164 | 4.35k | if (!ImplNewFont()) |
1165 | 0 | return 0; |
1166 | | |
1167 | 4.35k | auto nKashidaWidth = mpFontInstance->mxFontMetric->GetMinKashida(); |
1168 | 4.35k | if (!mbMap) |
1169 | 0 | nKashidaWidth = std::ceil(nKashidaWidth); |
1170 | | |
1171 | 4.35k | return ImplDevicePixelToLogicWidth(nKashidaWidth); |
1172 | 4.35k | } |
1173 | | |
1174 | | // tdf#163105: Get map of valid kashida positions for a single word |
1175 | | void OutputDevice::GetWordKashidaPositions(const OUString& rText, |
1176 | | std::vector<bool>* pOutMap) const |
1177 | 0 | { |
1178 | 0 | pOutMap->clear(); |
1179 | |
|
1180 | 0 | auto nEnd = rText.getLength(); |
1181 | | |
1182 | | // do layout |
1183 | 0 | std::unique_ptr<SalLayout> pSalLayout = ImplLayout(rText, 0, nEnd); |
1184 | 0 | if (!pSalLayout) |
1185 | 0 | return; |
1186 | | |
1187 | | // tdf#163215: VCL cannot suggest valid kashida positions for certain fonts (e.g. AAT). |
1188 | 0 | if (!pSalLayout->HasFontKashidaPositions()) |
1189 | 0 | return; |
1190 | | |
1191 | 0 | pOutMap->resize(nEnd, false); |
1192 | 0 | for (sal_Int32 i = 0; i < nEnd; ++i) |
1193 | 0 | { |
1194 | 0 | auto nNextPos = i + 1; |
1195 | | |
1196 | | // Skip combining marks to find the next character after this position. |
1197 | 0 | while (nNextPos < nEnd |
1198 | 0 | && u_getIntPropertyValue(rText[nNextPos], UCHAR_JOINING_TYPE) == U_JT_TRANSPARENT) |
1199 | 0 | { |
1200 | 0 | ++nNextPos; |
1201 | 0 | } |
1202 | |
|
1203 | 0 | pOutMap->at(i) = pSalLayout->IsKashidaPosValid(i, nNextPos); |
1204 | 0 | } |
1205 | 0 | } |
1206 | | |
1207 | | bool OutputDevice::GetGlyphBoundRects( const Point& rOrigin, const OUString& rStr, |
1208 | | int nIndex, int nLen, std::vector< tools::Rectangle >& rVector ) const |
1209 | 0 | { |
1210 | 0 | rVector.clear(); |
1211 | |
|
1212 | 0 | if( nIndex >= rStr.getLength() ) |
1213 | 0 | return false; |
1214 | | |
1215 | 0 | if( nLen < 0 || nIndex + nLen >= rStr.getLength() ) |
1216 | 0 | { |
1217 | 0 | nLen = rStr.getLength() - nIndex; |
1218 | 0 | } |
1219 | |
|
1220 | 0 | tools::Rectangle aRect; |
1221 | 0 | for( int i = 0; i < nLen; i++ ) |
1222 | 0 | { |
1223 | 0 | if( !GetTextBoundRect( aRect, rStr, nIndex, nIndex + i, 1 ) ) |
1224 | 0 | break; |
1225 | 0 | aRect.Move( rOrigin.X(), rOrigin.Y() ); |
1226 | 0 | rVector.push_back( aRect ); |
1227 | 0 | } |
1228 | |
|
1229 | 0 | return (nLen == static_cast<int>(rVector.size())); |
1230 | 0 | } |
1231 | | |
1232 | | sal_Int32 OutputDevice::HasGlyphs( const vcl::Font& rTempFont, std::u16string_view rStr, |
1233 | | sal_Int32 nIndex, sal_Int32 nLen ) const |
1234 | 136k | { |
1235 | 136k | if( nIndex >= static_cast<sal_Int32>(rStr.size()) ) |
1236 | 0 | return nIndex; |
1237 | 136k | sal_Int32 nEnd; |
1238 | 136k | if( nLen == -1 ) |
1239 | 136k | nEnd = rStr.size(); |
1240 | 0 | else |
1241 | 0 | nEnd = std::min<sal_Int32>( rStr.size(), nIndex + nLen ); |
1242 | | |
1243 | 136k | SAL_WARN_IF( nIndex >= nEnd, "vcl.gdi", "StartPos >= EndPos?" ); |
1244 | 136k | SAL_WARN_IF( nEnd > static_cast<sal_Int32>(rStr.size()), "vcl.gdi", "String too short" ); |
1245 | | |
1246 | | // to get the map temporarily set font |
1247 | 136k | const vcl::Font aOrigFont = GetFont(); |
1248 | 136k | const_cast<OutputDevice&>(*this).SetFont( rTempFont ); |
1249 | 136k | FontCharMapRef xFontCharMap; |
1250 | 136k | bool bRet = GetFontCharMap( xFontCharMap ); |
1251 | 136k | const_cast<OutputDevice&>(*this).SetFont( aOrigFont ); |
1252 | | |
1253 | | // if fontmap is unknown assume it doesn't have the glyphs |
1254 | 136k | if( !bRet ) |
1255 | 0 | return nIndex; |
1256 | | |
1257 | 155k | for( sal_Int32 i = nIndex; nIndex < nEnd; ++i, ++nIndex ) |
1258 | 136k | if( ! xFontCharMap->HasChar( rStr[i] ) ) |
1259 | 118k | return nIndex; |
1260 | | |
1261 | 18.2k | return -1; |
1262 | 136k | } |
1263 | | |
1264 | 0 | void OutputDevice::ReleaseFontCache() { mxFontCache.reset(); } |
1265 | | |
1266 | 0 | void OutputDevice::ReleaseFontCollection() { mxFontCollection.reset(); } |
1267 | | |
1268 | | void OutputDevice::SetFontCollectionFromSVData() |
1269 | 0 | { |
1270 | 0 | mxFontCollection = ImplGetSVData()->maGDIData.mxScreenFontList->Clone(); |
1271 | 0 | } |
1272 | | |
1273 | | void OutputDevice::ResetNewFontCache() |
1274 | 0 | { |
1275 | 0 | mxFontCache = std::make_shared<ImplFontCache>(); |
1276 | 0 | } |
1277 | | |
1278 | | void OutputDevice::ImplReleaseFonts() |
1279 | 652k | { |
1280 | 652k | mpGraphics->ReleaseFonts(); |
1281 | | |
1282 | 652k | mbNewFont = true; |
1283 | 652k | mbInitFont = true; |
1284 | | |
1285 | 652k | mpFontInstance.clear(); |
1286 | 652k | mpForcedFallbackInstance.clear(); |
1287 | 652k | mpFontFaceCollection.reset(); |
1288 | 652k | } |
1289 | | |
1290 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ |