Coverage Report

Created: 2026-03-31 11:00

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