Subversion Repositories tpanel

Rev

Rev 462 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
446 andreas 1
/*
463 andreas 2
 * Copyright (C) 2020 to 2024 by Andreas Theofilu <andreas@theosys.at>
446 andreas 3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software Foundation,
16
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
17
 */
18
 
19
#include <codecvt>
20
#include <mutex>
21
 
22
#if __cplusplus < 201402L
23
#   error "This module requires at least C++14 standard!"
24
#else
25
#   if __cplusplus < 201703L
26
#       include <experimental/filesystem>
27
namespace fs = std::experimental::filesystem;
28
#       warning "Support for C++14 and experimental filesystem will be removed in a future version!"
29
#   else
30
#       include <filesystem>
31
#       ifdef __ANDROID__
32
namespace fs = std::__fs::filesystem;
33
#       else
34
namespace fs = std::filesystem;
35
#       endif
36
#   endif
37
#endif
38
 
39
#include "tresources.h"
40
#include "tfont.h"
41
#include "texpat++.h"
42
#include "tconfig.h"
43
#include "terror.h"
44
#include "tnameformat.h"
45
 
46
#include <include/core/SkFontStyle.h>
47
 
48
#define FTABLE_DSIG     0x44534947
49
#define FTABLE_EBDT     0x45424454
50
#define FTABLE_EBLC     0x45424c43
51
#define FTABLE_GDEF     0x47444546
52
#define FTABLE_GPOS     0x47504f53
53
#define FTABLE_GSUB     0x47535542
54
#define FTABLE_LTSH     0x4c545348
55
#define FTABLE_OS_2     0x4f532f32
56
#define FTABLE_VDMX     0x56444d58
57
#define FTABLE_cmap     0x636d6170
58
#define FTABLE_cvt      0x63767420
59
#define FTABLE_fpgm     0x6670676d
60
#define FTABLE_gasp     0x67617370
61
#define FTABLE_glyf     0x676c7966
62
#define FTABLE_head     0x68656164
63
#define FTABLE_hhea     0x68686561
64
#define FTABLE_hmtx     0x686d7478
65
#define FTABLE_kern     0x6b65726e
66
#define FTABLE_loca     0x6c6f6361
67
#define FTABLE_maxp     0x6d617870
68
#define FTABLE_name     0x6e616d65
69
#define FTABLE_post     0x706f7374
70
#define FTABLE_prep     0x70726570
71
 
72
#define FTABLE_PID_UNICODE          0
73
#define FTABLE_PID_MACINTOSH        1
74
#define FTABLE_PID_MICROSOFT        3
75
 
76
#define FTABLE_SID_UNI_VERSION1     0
77
#define FTABLE_SID_UNI_VERSION2     1
78
#define FTABLE_SID_UNI_ISO10646     2
79
#define FTABLE_SID_UNI_UNI2BMP      3
80
#define FTABLE_SID_UNI_UNI2         4
81
#define FTABLE_SID_UNI_UNIVS        5
82
#define FTABLE_SID_UNI_LASTRES      6
83
 
84
#define FTABLE_SID_MSC_SYMBOL       0
85
#define FTABLE_SID_MSC_UNICODE      1
86
#define FTABLE_SID_MSC_SHIFTJIS     2
87
#define FTABLE_SID_MSC_PRC          3
88
#define FTABLE_SID_MSC_BIGFIVE      4
89
#define FTABLE_SID_MSC_JOHAB        5
90
#define FTABLE_SID_MSC_UNIUCS4      10
91
 
92
typedef struct FTABLE_FORMAT0_t
93
{
94
    uint16_t length;
95
    uint16_t language;
96
    unsigned char glyphIndex[256];
97
}FTABLE_FORMAT0_t;
98
 
99
typedef struct FTABLE_FORMAT4_t
100
{
101
    uint16_t length;
102
    uint16_t language;
103
    uint16_t segCountX2;
104
    uint16_t searchRange;
105
    uint16_t entrySelector;
106
    uint16_t rangeShift;
107
    uint16_t *endCode;
108
    uint16_t reservedPad;
109
    uint16_t *startCode;
110
    uint16_t *idDelta;
111
    uint16_t *idRangeOffset;
112
    uint16_t *glyphIndexArray;
113
}FTABLE_FORMAT4_t;
114
 
115
typedef struct FTABLE_FORMATS_t
116
{
117
    uint16_t format{0};
118
    union fdef
119
    {
120
        FTABLE_FORMAT0_t format0;
121
        FTABLE_FORMAT4_t format4;
122
    }fdef;
123
}FTABLE_FORMATS_t;
124
 
125
typedef struct FTABLE_SUBTABLE_t
126
{
127
    uint16_t platformID;
128
    uint16_t platformSpecificID;
129
    uint32_t offset;
130
    FTABLE_FORMATS_t format;
131
}FTABLE_SUBTABLE_t;
132
 
133
typedef struct FTABLE_CMAP_t
134
{
135
    uint16_t version{0};
136
    uint16_t numSubtables{0};
137
    FTABLE_SUBTABLE_t *subtables;
138
}FTABLE_CMAP_t;
139
 
140
using std::string;
141
using std::vector;
142
using std::map;
143
using std::pair;
144
using std::mutex;
145
using namespace Expat;
146
 
147
mutex mutex_font;
148
 
149
// This is an internal used font cache
150
map<string, sk_sp<SkTypeface> > _tfontCache;
151
 
462 andreas 152
TFont::TFont(const string& fname, bool tp)
153
    : mIsTP5(tp),
154
      mFontFile(fname)
446 andreas 155
{
462 andreas 156
    DECL_TRACER("TFont::TFont(const string& fname, bool tp)");
446 andreas 157
    initialize();
158
}
159
 
160
TFont::~TFont()
161
{
162
    DECL_TRACER("TFont::~TFont()");
163
}
164
 
165
void TFont::initialize()
166
{
167
    DECL_TRACER("TFont::initialize()");
168
 
169
    mutex_font.lock();
170
 
171
    if (mFonts.size() > 0)
172
        mFonts.clear();
173
 
174
    // System fonts first
175
    FONT_T font;
176
 
177
    if (!systemFonts())
178
    {
179
        TError::clear();
180
        MSG_INFO("Initializing virtual system fonts because no system files installed!");
181
 
182
        font.number = 1;
183
        font.file = "cour.ttf";
184
        font.faceIndex = 1;
185
        font.fullName = "Courier New";
186
        font.name = "Courier New";
187
        font.size = 9;
188
        font.subfamilyName = "normal";
189
        font.fileSize = 0;
190
        font.usageCount = 0;
191
        mFonts.insert(pair<int, FONT_T>(font.number, font));
192
 
193
        font.number = 2;
194
        font.faceIndex = 2;
195
        font.size = 12;
196
        mFonts.insert(pair<int, FONT_T>(font.number, font));
197
 
198
        font.number = 3;
199
        font.faceIndex = 3;
200
        font.size = 18;
201
        mFonts.insert(pair<int, FONT_T>(font.number, font));
202
 
203
        font.number = 4;
204
        font.faceIndex = 4;
205
        font.size = 26;
206
        mFonts.insert(pair<int, FONT_T>(font.number, font));
207
 
208
        font.number = 5;
209
        font.faceIndex = 5;
210
        font.size = 32;
211
        mFonts.insert(pair<int, FONT_T>(font.number, font));
212
 
213
        font.number = 6;
214
        font.faceIndex = 6;
215
        font.size = 18;
216
        mFonts.insert(pair<int, FONT_T>(font.number, font));
217
 
218
        font.number = 7;
219
        font.faceIndex = 7;
220
        font.size = 26;
221
        mFonts.insert(pair<int, FONT_T>(font.number, font));
222
 
223
        font.number = 8;
224
        font.faceIndex = 8;
225
        font.size = 34;
226
        mFonts.insert(pair<int, FONT_T>(font.number, font));
227
 
228
        font.number = 9;
229
        font.file = "Amxbold_.ttf";
230
        font.faceIndex = 9;
231
        font.fullName = "AMX Bold";
232
        font.name = "AMX Bold";
233
        font.size = 14;
234
        font.subfamilyName = "bold";
235
        mFonts.insert(pair<int, FONT_T>(font.number, font));
236
 
237
        font.number = 10;
238
        font.faceIndex = 10;
239
        font.size = 20;
240
        mFonts.insert(pair<int, FONT_T>(font.number, font));
241
 
242
        font.number = 11;
243
        font.faceIndex = 11;
244
        font.size = 36;
245
        mFonts.insert(pair<int, FONT_T>(font.number, font));
246
 
247
        font.number = 19;
248
        font.file = "arial.ttf";
249
        font.faceIndex = 19;
250
        font.fullName = "Arial";
251
        font.name = "Arial";
252
        font.size = 9;
253
        font.subfamilyName = "normal";
254
        mFonts.insert(pair<int, FONT_T>(font.number, font));
255
 
256
        font.number = 20;
257
        font.faceIndex = 20;
258
        font.size = 10;
259
        mFonts.insert(pair<int, FONT_T>(font.number, font));
260
 
261
        font.number = 21;
262
        font.faceIndex = 21;
263
        font.size = 12;
264
        mFonts.insert(pair<int, FONT_T>(font.number, font));
265
 
266
        font.number = 22;
267
        font.faceIndex = 22;
268
        font.size = 14;
269
        mFonts.insert(pair<int, FONT_T>(font.number, font));
270
 
271
        font.number = 23;
272
        font.faceIndex = 23;
273
        font.size = 16;
274
        mFonts.insert(pair<int, FONT_T>(font.number, font));
275
 
276
        font.number = 24;
277
        font.faceIndex = 24;
278
        font.size = 18;
279
        mFonts.insert(pair<int, FONT_T>(font.number, font));
280
 
281
        font.number = 25;
282
        font.faceIndex = 25;
283
        font.size = 20;
284
        mFonts.insert(pair<int, FONT_T>(font.number, font));
285
 
286
        font.number = 26;
287
        font.faceIndex = 26;
288
        font.size = 24;
289
        mFonts.insert(pair<int, FONT_T>(font.number, font));
290
 
291
        font.number = 27;
292
        font.faceIndex = 27;
293
        font.size = 36;
294
        mFonts.insert(pair<int, FONT_T>(font.number, font));
295
 
296
        font.number = 28;
297
        font.file = "arialbd.ttf";
298
        font.faceIndex = 28;
299
        font.fullName = "Arial Bold";
300
        font.name = "Arial Bold";
301
        font.size = 10;
302
        font.subfamilyName = "bold";
303
        mFonts.insert(pair<int, FONT_T>(font.number, font));
304
 
305
        font.number = 29;
306
        font.faceIndex = 29;
307
        font.size = 8;
308
        mFonts.insert(pair<int, FONT_T>(font.number, font));
309
    }
310
 
463 andreas 311
    if (mFontFile.empty())
312
    {
313
        MSG_ERROR("Got no font file name!");
314
        return;
315
    }
316
 
446 andreas 317
    // Now the fonts for setup pages
318
    systemFonts(true);
319
 
320
    // read the individual fonts from file
321
    TError::clear();
322
    string projectPath = TConfig::getProjectPath();
323
 
462 andreas 324
    string path = makeFileName(projectPath, mFontFile);
325
 
446 andreas 326
    if (!isValidFile())
327
    {
328
        MSG_ERROR("File " << path << " doesn't exist or is not readable!");
329
        TError::setError();
330
        mutex_font.unlock();
331
        return;
332
    }
333
 
334
    TExpat xml(path);
335
 
462 andreas 336
    if (!mIsTP5)
337
        xml.setEncoding(ENC_CP1250);
338
 
446 andreas 339
    if (!xml.parse())
340
    {
341
        mutex_font.unlock();
342
        return;
343
    }
344
 
345
    int depth = 0;
346
    size_t index = 0;
347
    size_t oldIndex = 0;
348
 
349
    if (xml.getElementIndex("fontList", &depth) == TExpat::npos)
350
    {
351
        MSG_DEBUG("File does not contain the element \"fontList\"!");
352
        TError::setError();
353
        mutex_font.unlock();
354
        return;
355
    }
356
 
357
    depth++;
462 andreas 358
    int fnumber = 33;
446 andreas 359
 
360
    while ((index = xml.getNextElementIndex("font", depth)) != TExpat::npos)
361
    {
362
        string name, content;
363
        FONT_T ft;
462 andreas 364
        vector<ATTRIBUTE_t> attrs;
446 andreas 365
 
462 andreas 366
        if (!mIsTP5)
367
        {
368
            attrs = xml.getAttributes(index);
369
 
370
            if (!attrs.empty())
371
                ft.number = xml.getAttributeInt("number", attrs);
372
            else
373
            {
374
                MSG_ERROR("Element font contains no or invalid attribute!");
375
                TError::setError();
376
                mutex_font.unlock();
377
                return;
378
            }
379
        }
446 andreas 380
        else
381
        {
462 andreas 382
            ft.number = fnumber;
383
            fnumber++;
446 andreas 384
        }
385
 
386
        while ((index = xml.getNextElementFromIndex(index, &name, &content, &attrs)) != TExpat::npos)
387
        {
388
            string e = name;
389
 
390
            if (e.compare("file") == 0)
391
                ft.file = content;
392
            else if (e.compare("fileSize") == 0)
393
                ft.fileSize = xml.convertElementToInt(content);
394
            else if (e.compare("faceIndex") == 0)
395
                ft.faceIndex = xml.convertElementToInt(content);
396
            else if (e.compare("name") == 0)
397
                ft.name = content;
398
            else if (e.compare("subfamilyName") == 0)
399
                ft.subfamilyName = content;
400
            else if (e.compare("fullName") == 0)
401
                ft.fullName = content;
402
            else if (e.compare("size") == 0)
403
                ft.size = xml.convertElementToInt(content);
404
            else if (e.compare("usageCount") == 0)
405
                ft.usageCount = xml.convertElementToInt(content);
406
 
407
            oldIndex = index;
408
        }
409
 
410
        mFonts.insert(pair<int, FONT_T>(ft.number, ft));
411
 
412
        if (index == TExpat::npos)
413
            index = oldIndex + 1;
414
 
415
        xml.setIndex(index);
416
    }
417
 
418
    mutex_font.unlock();
419
}
420
 
421
bool TFont::systemFonts(bool setup)
422
{
423
    DECL_TRACER("TFont::systemFonts(bool setup)");
424
 
425
    string path;
426
 
427
    if (setup)
462 andreas 428
        path = makeFileName(TConfig::getSystemProjectPath(), "/" + mFontFile);
446 andreas 429
    else
462 andreas 430
        path = makeFileName(TConfig::getSystemProjectPath(), "/graphics/" + mFontFile);
446 andreas 431
 
432
    if (!isValidFile())
433
    {
434
        MSG_ERROR("File " << path << " doesn't exist or is not readable!");
435
        TError::setError();
436
        return false;
437
    }
438
 
439
    TExpat xml(path);
440
 
462 andreas 441
    if (!mIsTP5)
442
        xml.setEncoding(ENC_CP1250);
443
 
446 andreas 444
    if (!xml.parse())
445
        return false;
446
 
447
    int depth = 0;
448
    size_t index = 0;
449
    size_t oldIndex = 0;
450
 
451
    if (xml.getElementIndex("fontList", &depth) == TExpat::npos)
452
    {
453
        MSG_DEBUG("File does not contain the element \"fontList\"!");
454
        TError::setError();
455
        return false;
456
    }
457
 
458
    depth++;
462 andreas 459
    int fnumber = 1;
446 andreas 460
 
461
    while ((index = xml.getNextElementIndex("font", depth)) != TExpat::npos)
462
    {
463
        string name, content;
464
        FONT_T ft;
462 andreas 465
        vector<ATTRIBUTE_t> attrs;
446 andreas 466
 
462 andreas 467
        if (!mIsTP5)
468
        {
469
            attrs = xml.getAttributes(index);
470
 
471
            if (!attrs.empty())
472
                ft.number = xml.getAttributeInt("number", attrs);
473
            else
474
            {
475
                MSG_ERROR("Element font contains no or invalid attribute!");
476
                TError::setError();
477
                return false;
478
            }
479
        }
446 andreas 480
        else
481
        {
462 andreas 482
            ft.number = fnumber;
483
            fnumber++;
446 andreas 484
        }
485
 
486
        while ((index = xml.getNextElementFromIndex(index, &name, &content, &attrs)) != TExpat::npos)
487
        {
488
            string e = name;
489
 
490
            if (e.compare("file") == 0)
491
                ft.file = content;
492
            else if (e.compare("fileSize") == 0)
493
                ft.fileSize = xml.convertElementToInt(content);
494
            else if (e.compare("faceIndex") == 0)
495
                ft.faceIndex = xml.convertElementToInt(content);
496
            else if (e.compare("name") == 0)
497
                ft.name = content;
498
            else if (e.compare("subfamilyName") == 0)
499
                ft.subfamilyName = content;
500
            else if (e.compare("fullName") == 0)
501
                ft.fullName = content;
502
            else if (e.compare("size") == 0)
503
                ft.size = xml.convertElementToInt(content);
504
            else if (e.compare("usageCount") == 0)
505
                ft.usageCount = xml.convertElementToInt(content);
506
 
507
            oldIndex = index;
508
        }
462 andreas 509
#if __cplusplus < 201703L
446 andreas 510
        mFonts.insert(pair<int, FONT_T>(ft.number, ft));
462 andreas 511
#else
512
        mFonts.insert_or_assign(ft.number, ft);
513
#endif
446 andreas 514
        if (index == TExpat::npos)
515
            index = oldIndex + 1;
516
 
517
        xml.setIndex(index);
518
    }
519
 
520
    return true;
521
}
522
 
523
FONT_T TFont::getFont(int number)
524
{
525
    DECL_TRACER("TFont::getFont(int number)");
526
 
527
    if (mFonts.size() == 0)
528
    {
529
        MSG_WARNING("No fonts found!");
530
        return FONT_T();
531
    }
532
 
533
    map<int, FONT_T>::iterator iter = mFonts.find(number);
534
 
535
    if (iter == mFonts.end())
536
    {
537
        MSG_WARNING("No font with number " << number << " found!");
538
        return FONT_T();
539
    }
540
 
541
    return iter->second;
542
}
543
 
544
int TFont::getFontIDfromFile(const string& file)
545
{
546
    DECL_TRACER("TFont::getFontIDfromFile(const string& file)");
547
 
548
    if (mFonts.size() == 0)
549
    {
550
        MSG_WARNING("No fonts found!");
551
        return -1;
552
    }
553
 
554
    map<int, FONT_T>::iterator iter;
555
 
556
    for (iter = mFonts.begin(); iter != mFonts.end(); ++iter)
557
    {
558
        if (iter->second.file == file)
559
            return iter->first;
560
    }
561
#if TESTMODE == 1
562
    MSG_WARNING("There is no font file \"" << file << "\" found!");
563
#endif
564
    return -1;
565
}
566
 
567
int TFont::getFontIDfromName(const string &name)
568
{
569
    DECL_TRACER("TFont::getFontIDfromName(const string &name)");
570
 
571
    if (mFonts.size() == 0)
572
    {
573
        MSG_WARNING("No fonts found!");
574
        return -1;
575
    }
576
 
577
    map<int, FONT_T>::iterator iter;
578
 
579
    for (iter = mFonts.begin(); iter != mFonts.end(); ++iter)
580
    {
581
        if (iter->second.name == name)
582
            return iter->first;
583
    }
584
#if TESTMODE == 1
585
    MSG_WARNING("There is no font name \"" << name << "\" found!");
586
#endif
587
    return -1;
588
}
589
 
590
FONT_STYLE TFont::getStyle(int number)
591
{
592
    DECL_TRACER("TFont::getStyle(int number)");
593
 
594
    map<int, FONT_T>::iterator iter = mFonts.find(number);
595
 
596
    if (iter == mFonts.end())
597
        return FONT_NONE;
598
 
599
    if (iter->second.subfamilyName.compare("Regular") == 0)
600
        return FONT_NORMAL;
601
    else if (iter->second.subfamilyName.compare("Italic") == 0)
602
        return FONT_ITALIC;
603
    else if (iter->second.subfamilyName.compare("Bold") == 0)
604
        return FONT_BOLD;
605
    else if (iter->second.subfamilyName.compare("Bold Italic") == 0)
606
        return FONT_BOLD_ITALIC;
607
 
608
    return FONT_NORMAL;
609
}
610
 
611
FONT_STYLE TFont::getStyle(FONT_T& font)
612
{
613
    DECL_TRACER("TFont::getStyle(int number)");
614
 
615
    if (font.subfamilyName.compare("Regular") == 0)
616
        return FONT_NORMAL;
617
    else if (font.subfamilyName.compare("Italic") == 0)
618
        return FONT_ITALIC;
619
    else if (font.subfamilyName.compare("Bold") == 0)
620
        return FONT_BOLD;
621
    else if (font.subfamilyName.compare("Bold Italic") == 0)
622
        return FONT_BOLD_ITALIC;
623
 
624
    return FONT_NORMAL;
625
}
626
 
627
SkFontStyle TFont::getSkiaStyle(int number)
628
{
629
    DECL_TRACER("TFont::getSkiaStyle(int number)");
630
 
631
    map<int, FONT_T>::iterator iter = mFonts.find(number);
632
 
633
    if (iter == mFonts.end())
634
        return SkFontStyle(SkFontStyle::kNormal_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant);
635
 
636
    if (iter->second.subfamilyName.compare("Regular") == 0)
637
        return SkFontStyle::Normal();
638
    else if (iter->second.subfamilyName.compare("Italic") == 0)
639
        return SkFontStyle::Italic();
640
    else if (iter->second.subfamilyName.compare("Bold") == 0)
641
        return SkFontStyle::Bold();
642
    else if (iter->second.subfamilyName.compare("Bold Italic") == 0)
643
        return SkFontStyle::BoldItalic();
644
 
645
    return SkFontStyle::Normal();
646
}
647
 
648
#define MAX_FACES   10
649
 
462 andreas 650
sk_sp<SkTypeface> TFont::getTypeFace(const string& ff)
651
{
652
    DECL_TRACER("TFont::getTypeFace(const string& ff)");
653
 
654
    map<int, FONT_T>::iterator iter;
655
 
656
    for (iter = mFonts.begin(); iter != mFonts.end(); ++iter)
657
    {
658
        if (iter->second.file == ff)
659
            return getTypeFace(iter->first);
660
    }
661
 
662
    return nullptr;
663
}
664
 
665
 
446 andreas 666
sk_sp<SkTypeface> TFont::getTypeFace(int number)
667
{
668
    DECL_TRACER("TFont::getTypeFace(int number)");
669
 
670
    map<int, FONT_T>::iterator iter = mFonts.find(number);
671
 
672
    if (iter == mFonts.end())
673
    {
674
        MSG_ERROR("No font with index " << number << " found!");
675
        TError::setError();
676
        return nullptr;
677
    }
678
 
679
    if (!_tfontCache.empty())
680
    {
681
        map<string, sk_sp<SkTypeface> >::iterator itFont = _tfontCache.find(iter->second.file);
682
 
683
        if (itFont != _tfontCache.end() && itFont->second)
684
        {
685
            MSG_DEBUG("Font " << number << ": " << iter->second.file << " was taken from cache.");
686
            return itFont->second;
687
        }
688
        else if (itFont != _tfontCache.end())
689
            _tfontCache.erase(itFont);
690
    }
691
 
692
    mutex_font.lock();
693
    string path;
694
 
695
    if (number < 32)    // System font?
696
    {
697
        path = TConfig::getProjectPath() + "/__system/graphics/fonts/" + iter->second.file;
698
 
699
        if (!isValidFile(path))
700
        {
701
            MSG_WARNING("Seem to miss system fonts ...");
702
            path = TConfig::getProjectPath() + "/fonts/" + iter->second.file;
703
        }
704
    }
705
    else
706
    {
707
        path = TConfig::getProjectPath() + "/fonts/" + iter->second.file;
708
 
709
        if (!fs::exists(path))
710
        {
711
            string pth = TConfig::getProjectPath() + "/__system/fonts/" + iter->second.file;
712
 
713
            if (fs::exists(pth))
714
                path = pth;
715
        }
716
    }
717
 
718
    sk_sp<SkTypeface> tf;
719
    MSG_DEBUG("Loading font \"" << path << "\" ...");
720
 
721
    if (isValidFile(path))
722
    {
723
        try
724
        {
725
            tf = MakeResourceAsTypeface(path.c_str(), iter->second.faceIndex, RESTYPE_FONT);
726
        }
727
        catch(std::exception& e)
728
        {
729
            MSG_ERROR("Error loading font: " << e.what());
730
        }
731
    }
732
    else
733
        MSG_WARNING("File " << path << " is not a valid file or does not exist!");
734
 
735
    if (!tf)
736
    {
737
        string perms = getPermissions(path);
738
        MSG_ERROR("Error loading font \"" << path << "\" [" << perms << "]");
739
        MSG_PROTOCOL("Trying with alternative function ...");
740
        TError::setError();
741
 
742
        tf = SkTypeface::MakeFromName(iter->second.fullName.c_str(), getSkiaStyle(number));
743
 
744
        if (tf)
745
            TError::clear();
746
        else
747
        {
748
            MSG_ERROR("Alternative method failed loading the font " << iter->second.fullName);
749
            MSG_WARNING("Will use a default font instead!");
750
            tf = SkTypeface::MakeDefault();
751
 
752
            if (tf)
753
                TError::clear();
754
            else
755
            {
756
                MSG_ERROR("No default font found!");
757
                mutex_font.unlock();
758
                return tf;
759
            }
760
        }
761
    }
762
    else
763
    {
764
        if (tf->countTables() > 0)
765
        {
766
            _tfontCache.insert(pair<string, sk_sp<SkTypeface> >(iter->second.file, tf));
767
            MSG_DEBUG("Font \"" << path << "\" was loaded successfull.");
768
        }
769
        else
770
        {
771
            MSG_WARNING("Refused to enter invalid typeface into font cache!");
772
        }
773
    }
774
 
775
    SkString sname;
776
    tf->getFamilyName(&sname);
777
 
778
    if (iter->second.name.compare(sname.c_str()) != 0)
779
    {
780
        MSG_WARNING("Found font name \"" << sname.c_str() << "\" with attributes: bold=" << ((tf->isBold())?"TRUE":"FALSE") << ", italic=" << ((tf->isItalic())?"TRUE":"FALSE") << ", fixed=" << ((tf->isFixedPitch())?"TRUE":"FALSE"));
781
        MSG_WARNING("The loaded font \"" << sname.c_str() << "\" is not the wanted font \"" << iter->second.name << "\"!");
782
    }
783
 
784
    FONT_STYLE style = getStyle(iter->second);
785
    bool ret = false;
786
 
787
    if (style == FONT_BOLD && tf->isBold())
788
        ret = true;
789
    else if (style == FONT_ITALIC && tf->isItalic())
790
        ret = true;
791
    else if (style == FONT_BOLD_ITALIC && tf->isBold() && tf->isItalic())
792
        ret = true;
793
    else if (style == FONT_NORMAL && !tf->isBold() && !tf->isItalic())
794
        ret = true;
795
 
796
    if (ret)
797
    {
798
        mutex_font.unlock();
799
        return tf;
800
    }
801
 
802
    MSG_WARNING("The wanted font style " << iter->second.subfamilyName << " was not found!");
803
    mutex_font.unlock();
804
    return tf;
805
}
806
 
807
vector<string> TFont::getFontPathList()
808
{
809
    DECL_TRACER("TFont::getFontPathList()");
810
 
811
    vector<string> list;
812
    string path = TConfig::getProjectPath() + "/fonts";
813
    list.push_back(path);
814
    path = TConfig::getProjectPath() + "/__system/fonts";
815
    list.push_back(path);
816
    path = TConfig::getProjectPath() + "/__system/graphics/fonts";
817
    list.push_back(path);
818
    return list;
819
}
820
 
821
size_t _numTables;
822
FTABLE_CMAP_t _cmapTable;
823
 
824
void TFont::parseCmap(const unsigned char* cmaps)
825
{
826
    DECL_TRACER("TFont::parseCmap(const unsigned char* cmaps)");
827
 
828
    if (!cmaps)
829
        return;
830
 
831
    _cmapTable.version = getUint16(cmaps);
832
    _cmapTable.numSubtables = getUint16(cmaps+sizeof(uint16_t));
833
    MSG_DEBUG("Found version " << _cmapTable.version << ", found " << _cmapTable.numSubtables << " cmap tables.");
834
    _cmapTable.subtables = new FTABLE_SUBTABLE_t[_cmapTable.numSubtables];
835
    size_t pos = sizeof(uint16_t) * 2;
836
 
837
    for (uint16_t i = 0; i < _cmapTable.numSubtables; i++)
838
    {
839
        FTABLE_SUBTABLE_t st;
840
        st.platformID = getUint16(cmaps+pos);
841
        pos += sizeof(uint16_t);
842
        st.platformSpecificID = getUint16(cmaps+pos);
843
        pos += sizeof(uint16_t);
844
        st.offset = getUint32(cmaps+pos);
845
        pos += sizeof(uint32_t);
846
        memmove(&_cmapTable.subtables[i], &st, sizeof(st));
847
        MSG_DEBUG("Table " << (i+1) << ": platformID=" << st.platformID << ", platformSpecificID=" << st.platformSpecificID << ", offset=" << st.offset);
848
    }
849
 
850
    // Get the format and read the mapping
851
    for (uint16_t i = 0; i < _cmapTable.numSubtables; i++)
852
    {
853
        if (_cmapTable.subtables[i].platformID == FTABLE_PID_MACINTOSH)   // We ignore old Macintosh format
854
        {
855
            _cmapTable.subtables[i].format.format = -1;
856
            continue;
857
        }
858
 
859
        FTABLE_FORMATS_t format;
860
        format.format = getUint16(cmaps+_cmapTable.subtables[i].offset);
861
        pos = _cmapTable.subtables[i].offset + sizeof(uint16_t);
862
 
863
        switch(format.format)
864
        {
865
            case 0:
866
                format.fdef.format0.length = getUint16(cmaps+pos);
867
                pos += sizeof(uint16_t);
868
                format.fdef.format0.language = getUint16(cmaps+pos);
869
                pos += sizeof(uint16_t);
870
                memcpy(format.fdef.format0.glyphIndex, cmaps+pos, sizeof(format.fdef.format0.glyphIndex));
871
            break;
872
 
873
            case 4:
874
                format.fdef.format4.length = getUint16(cmaps+pos);
875
                pos += sizeof(uint16_t);
876
                format.fdef.format4.language = getUint16(cmaps+pos);
877
                pos += sizeof(uint16_t);
878
                format.fdef.format4.segCountX2 = getUint16(cmaps+pos);
879
                pos += sizeof(uint16_t);
880
                format.fdef.format4.searchRange = getUint16(cmaps+pos);
881
                pos += sizeof(uint16_t);
882
                format.fdef.format4.entrySelector = getUint16(cmaps+pos);
883
                pos += sizeof(uint16_t);
884
                format.fdef.format4.rangeShift = getUint16(cmaps+pos);
885
                pos += sizeof(uint16_t);
886
                uint16_t segCount = format.fdef.format4.segCountX2 / 2;
887
                format.fdef.format4.endCode = new uint16_t[segCount];
888
 
889
                for (uint16_t j = 0; j < segCount; j++)
890
                {
891
                    format.fdef.format4.endCode[j] = getUint16(cmaps+pos);
892
                    pos += sizeof(uint16_t);
893
                }
894
 
895
                format.fdef.format4.reservedPad = getUint16(cmaps+pos);
896
                pos += sizeof(uint16_t);
897
                format.fdef.format4.startCode = new uint16_t[segCount];
898
 
899
                for (uint16_t j = 0; j < segCount; j++)
900
                {
901
                    format.fdef.format4.startCode[j] = getUint16(cmaps+pos);
902
                    pos += sizeof(uint16_t);
903
                }
904
 
905
                format.fdef.format4.idDelta = new uint16_t[segCount];
906
 
907
                for (uint16_t j = 0; j < segCount; j++)
908
                {
909
                    format.fdef.format4.idDelta[j] = getUint16(cmaps+pos);
910
                    pos += sizeof(uint16_t);
911
                }
912
 
913
                format.fdef.format4.idRangeOffset = new uint16_t[segCount];
914
 
915
                for (uint16_t j = 0; j < segCount; j++)
916
                {
917
                    format.fdef.format4.idRangeOffset[j] = getUint16(cmaps+pos);
918
                    pos += sizeof(uint16_t);
919
                }
920
 
921
                format.fdef.format4.glyphIndexArray = (uint16_t *)(cmaps+pos);
922
                memcpy(&_cmapTable.subtables[i].format, &format, sizeof(FTABLE_FORMATS_t));
923
            break;
924
        }
925
    }
926
}
927
 
928
uint16_t TFont::getGlyphIndex(SkUnichar ch)
929
{
930
    DECL_TRACER("TFont::getGlyphIndex(const char* ch)");
931
 
932
    uint16_t lCh = ch;
933
    bool symbol = false;
934
 
935
    for (uint16_t nTbs = 0; nTbs < _cmapTable.numSubtables; nTbs++)
936
    {
937
        if (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_UNICODE ||
938
            _cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT)
939
        {
940
            if ((_cmapTable.subtables[nTbs].platformID == FTABLE_PID_UNICODE &&_cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_UNI_VERSION1) ||
941
                (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT && _cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_MSC_SYMBOL))
942
                symbol = true;  // Table does not have unicode table mapping (wingding)
943
 
944
            // Find the segment where the wanted character is in.
945
            if (_cmapTable.subtables[nTbs].format.format == 4)
946
            {
947
                FTABLE_FORMAT4_t form = _cmapTable.subtables[nTbs].format.fdef.format4;
948
                uint16_t segCount = form.segCountX2 / 2;
949
                uint16_t segment = 0xffff;
950
                MSG_DEBUG("segCountX2: " << form.segCountX2 << ", # segments: " << segCount);
951
 
952
                for (uint16_t sc = 0; sc < segCount; sc++)
953
                {
954
                    MSG_DEBUG("Table: " << nTbs << ": Checking range " << std::hex << std::setw(4) << std::setfill('0') << form.startCode[sc] << " to " << std::setw(4) << std::setfill('0') << form.endCode[sc] << std::dec);
955
 
956
                    if (symbol)
957
                    {
958
                        if (ch <= 0x00ff)
959
                            lCh = ch + (form.startCode[sc] & 0xff00);
960
                        else
961
                            lCh = ch + (form.startCode[sc] & 0xf000);
962
                    }
963
 
964
                    if (lCh >= form.startCode[sc] && lCh <= form.endCode[sc])
965
                    {
966
                        segment = sc;
967
                        break;
968
                    }
969
                }
970
 
971
                if (segment == 0xffff || form.startCode[segment] == 0xffff || form.endCode[segment] == 0xffff)
972
                {
973
                    MSG_WARNING("The character " << std::hex << std::setw(4) << std::setfill('0') << lCh << " is not supported by any segment!" << std::dec);
974
                    continue;
975
                }
976
 
977
                MSG_DEBUG("Table: " << (nTbs+1) << ": idRangeOffset: " << std::hex << std::setw(4) << std::setfill('0') << form.idRangeOffset[segment] << ", idDelta: " << std::setw(4) << std::setfill('0') << form.idDelta[segment] << std::dec);
978
                uint16_t glyphIndex;
979
 
980
                if (form.idRangeOffset[segment] == 0)
981
                    glyphIndex = form.idDelta[segment] + lCh;
982
                else
983
                {
984
                    uint16_t gArray = getUint16((unsigned char *)(form.glyphIndexArray + (form.idRangeOffset[segment] / 2)));
985
                    MSG_DEBUG("Value from glyphArray: " << std::hex << std::setw(4) << std::setfill('0') << gArray);
986
 
987
                    if (symbol && segment > 0 && gArray != 0)
988
                        glyphIndex = gArray + (lCh - form.startCode[segment]) - 2;
989
                    else
990
                        glyphIndex = gArray / 2 + (lCh - form.startCode[segment]);
991
                }
992
 
993
                MSG_DEBUG("Found index 0x" << std::hex << std::setw(4) << std::setfill('0') << glyphIndex << " for unichar 0x" << std::setw(4) << std::setfill('0') << lCh << std::dec);
994
                return glyphIndex;
995
            }
996
            else
997
            {
998
                MSG_WARNING("Ignoring table with unsupported format " << _cmapTable.subtables[nTbs].format.format);
999
            }
1000
        }
1001
    }
1002
 
1003
    return 0xffff;
1004
}
1005
 
1006
void TFont::_freeCmap()
1007
{
1008
    DECL_TRACER("TFont::_freeCmap()");
1009
 
1010
    for (uint16_t nTbs = 0; nTbs < _cmapTable.numSubtables; nTbs++)
1011
    {
1012
        if (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_UNICODE ||
1013
            _cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT)
1014
        {
1015
            if (_cmapTable.subtables[nTbs].format.format == 4)
1016
            {
1017
                delete[] _cmapTable.subtables[nTbs].format.fdef.format4.endCode;
1018
                delete[] _cmapTable.subtables[nTbs].format.fdef.format4.startCode;
1019
                delete[] _cmapTable.subtables[nTbs].format.fdef.format4.idDelta;
1020
                delete[] _cmapTable.subtables[nTbs].format.fdef.format4.idRangeOffset;
1021
            }
1022
        }
1023
    }
1024
 
1025
    delete[] _cmapTable.subtables;
1026
}
1027
 
1028
SkGlyphID *TFont::textToGlyphs(const string& str, sk_sp<SkTypeface>& typeFace, size_t *size)
1029
{
1030
    DECL_TRACER("TFont::textToGlyphs(const string& str, SkTypeface& typeFace)");
1031
 
1032
    *size = 0;
1033
    int tables = typeFace->countTables();
1034
 
1035
    if (!tables)
1036
    {
1037
        MSG_ERROR("No tables in typeface!");
1038
        return nullptr;
1039
    }
1040
 
1041
    SkFontTableTag *tbTags = new SkFontTableTag[tables];
1042
    int tags = typeFace->getTableTags(tbTags);
1043
    bool haveCmap = false;
1044
    unsigned char *cmaps = nullptr;
1045
 
1046
    // Find the "cmap" and the "glyph" tables and get them
1047
    for (int i = 0; i < tags; i++)
1048
    {
1049
        SkFontTableTag fttg = tbTags[i];
1050
 
1051
        if (fttg == FTABLE_cmap)
1052
        {
1053
            size_t tbSize = typeFace->getTableSize(fttg);
1054
 
1055
            if (!tbSize)
1056
            {
1057
                MSG_ERROR("CMAP font table size is 0!");
1058
                delete[] tbTags;
1059
                return nullptr;
1060
            }
1061
 
1062
            cmaps = new unsigned char[tbSize];
1063
            typeFace->getTableData(fttg, 0, tbSize, cmaps);
1064
            haveCmap = true;
1065
            break;
1066
        }
1067
    }
1068
 
1069
    if (!haveCmap)
1070
    {
1071
        MSG_ERROR("Invalid font. Missing CMAP table!");
1072
//        TError::setError();
1073
        delete[] tbTags;
1074
        return nullptr;
1075
    }
1076
 
1077
    delete[] tbTags;
1078
    parseCmap(cmaps);
1079
 
1080
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
1081
    std::u16string dest = convert.from_bytes(str);
1082
    SkGlyphID *gIds = new SkGlyphID[dest.length()];
1083
    *size = dest.length();
1084
 
1085
    for (size_t i = 0; i < dest.length(); i++)
1086
    {
1087
        SkUnichar uniChar = (SkUnichar)dest[i];
1088
        gIds[i] = getGlyphIndex(uniChar);
1089
    }
1090
 
1091
    _freeCmap();
1092
    delete[] cmaps;
1093
 
1094
    return gIds;
1095
}
1096
 
1097
FONT_TYPE TFont::isSymbol(sk_sp<SkTypeface>& typeFace)
1098
{
1099
    DECL_TRACER("TFont::isSymbol(sk_sp<SkTypeface>& typeFace)");
1100
 
1101
    if (!typeFace)
1102
    {
1103
        MSG_ERROR("Got an empty typeface!");
1104
        return FT_UNKNOWN;
1105
    }
1106
 
1107
    mutex_font.lock();
1108
    int tables = typeFace->countTables();
1109
 
1110
    if (tables <= 0)
1111
    {
1112
        MSG_ERROR("No tables found in typeface!");
1113
        mutex_font.unlock();
1114
        return FT_UNKNOWN;
1115
    }
1116
 
1117
    SkFontTableTag *tbTags = new SkFontTableTag[tables];
1118
    int tags = typeFace->getTableTags(tbTags);
1119
    bool haveCmap = false;
1120
    unsigned char *cmaps = nullptr;
1121
 
1122
    // Find the "cmap" table and get it
1123
    for (int i = 0; i < tags; i++)
1124
    {
1125
        SkFontTableTag fttg = tbTags[i];
1126
 
1127
        if (fttg == FTABLE_cmap)
1128
        {
1129
            size_t tbSize = typeFace->getTableSize(fttg);
1130
 
1131
            if (!tbSize)
1132
            {
1133
                MSG_ERROR("CMAP table has size of 0!");
1134
                delete[] tbTags;
1135
                mutex_font.unlock();
1136
                return FT_UNKNOWN;
1137
            }
1138
 
1139
            cmaps = new unsigned char[tbSize];
1140
            typeFace->getTableData(fttg, 0, tbSize, cmaps);
1141
            haveCmap = true;
1142
            break;
1143
        }
1144
    }
1145
 
1146
    if (!haveCmap)
1147
    {
1148
        MSG_ERROR("Invalid font. Missing CMAP table!");
1149
//        TError::setError();
1150
        delete[] tbTags;
1151
        mutex_font.unlock();
1152
        return FT_UNKNOWN;
1153
    }
1154
 
1155
    delete[] tbTags;
1156
    parseCmap(cmaps);
1157
 
1158
    for (uint16_t nTbs = 0; nTbs < _cmapTable.numSubtables; nTbs++)
1159
    {
1160
        if ((_cmapTable.subtables[nTbs].platformID == FTABLE_PID_UNICODE && _cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_UNI_VERSION1) ||
1161
            (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT && _cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_MSC_SYMBOL))
1162
        {
1163
            _freeCmap();
1164
            delete[] cmaps;
1165
            FONT_TYPE ft = FT_SYMBOL;
1166
 
1167
            if (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT && _cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_MSC_SYMBOL)
1168
                ft = FT_SYM_MS;
1169
 
1170
            mutex_font.unlock();
1171
            return ft;
1172
        }
1173
    }
1174
 
1175
    _freeCmap();
1176
    delete[] cmaps;
1177
    mutex_font.unlock();
1178
    return FT_NORMAL;
1179
}
1180
 
1181
size_t TFont::utf8ToUtf16(const string& str, uint16_t **uni, bool toSymbol)
1182
{
1183
    DECL_TRACER("TFont::utf8ToUtf16(const string& str, uint16_t **uni, bool toSymbol)");
1184
 
1185
    if (str.empty() || !uni)
1186
    {
1187
        if (uni)
1188
            *uni = nullptr;
1189
 
1190
        return 0;
1191
    }
1192
 
1193
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
1194
    std::u16string dest = convert.from_bytes(str);
1195
 
1196
    *uni = new uint16_t[dest.length()+1];
1197
    uint16_t *unicode = *uni;
1198
    memset(unicode, 0, sizeof(uint16_t) * (dest.length()+1));
1199
 
1200
    for (size_t i = 0; i < dest.length(); i++)
1201
    {
1202
        unicode[i] = dest[i];
1203
 
1204
        if (toSymbol && unicode[i] < 0xf000)
1205
            unicode[i] += 0xf000;
1206
    }
1207
 
1208
    return dest.length();
1209
}
1210
 
1211
double TFont::pixelToPoint(int dpi, int pixel)
1212
{
1213
    DECL_TRACER("TFont::pixelToPoint(int dpi, int pixel)");
1214
 
1215
    double size = 0.0138889 * (double)dpi * (double)pixel;
1216
    MSG_DEBUG("Size: " << size << ", dpi: " << dpi << ", pixels: " << pixel);
1217
    return size;
1218
}