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