Coverage Report

Created: 2025-07-07 10:01

/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: */