Subversion Repositories tpanel

Rev

Rev 463 | 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);
486 andreas 338
    else
339
        xml.setEncoding(ENC_UTF8);
462 andreas 340
 
446 andreas 341
    if (!xml.parse())
342
    {
343
        mutex_font.unlock();
344
        return;
345
    }
346
 
347
    int depth = 0;
348
    size_t index = 0;
349
    size_t oldIndex = 0;
350
 
351
    if (xml.getElementIndex("fontList", &depth) == TExpat::npos)
352
    {
353
        MSG_DEBUG("File does not contain the element \"fontList\"!");
354
        TError::setError();
355
        mutex_font.unlock();
356
        return;
357
    }
358
 
359
    depth++;
462 andreas 360
    int fnumber = 33;
446 andreas 361
 
362
    while ((index = xml.getNextElementIndex("font", depth)) != TExpat::npos)
363
    {
364
        string name, content;
365
        FONT_T ft;
462 andreas 366
        vector<ATTRIBUTE_t> attrs;
446 andreas 367
 
462 andreas 368
        if (!mIsTP5)
369
        {
370
            attrs = xml.getAttributes(index);
371
 
372
            if (!attrs.empty())
373
                ft.number = xml.getAttributeInt("number", attrs);
374
            else
375
            {
376
                MSG_ERROR("Element font contains no or invalid attribute!");
377
                TError::setError();
378
                mutex_font.unlock();
379
                return;
380
            }
381
        }
446 andreas 382
        else
383
        {
462 andreas 384
            ft.number = fnumber;
385
            fnumber++;
446 andreas 386
        }
387
 
388
        while ((index = xml.getNextElementFromIndex(index, &name, &content, &attrs)) != TExpat::npos)
389
        {
390
            string e = name;
391
 
392
            if (e.compare("file") == 0)
393
                ft.file = content;
394
            else if (e.compare("fileSize") == 0)
395
                ft.fileSize = xml.convertElementToInt(content);
396
            else if (e.compare("faceIndex") == 0)
397
                ft.faceIndex = xml.convertElementToInt(content);
398
            else if (e.compare("name") == 0)
399
                ft.name = content;
400
            else if (e.compare("subfamilyName") == 0)
401
                ft.subfamilyName = content;
402
            else if (e.compare("fullName") == 0)
403
                ft.fullName = content;
404
            else if (e.compare("size") == 0)
405
                ft.size = xml.convertElementToInt(content);
406
            else if (e.compare("usageCount") == 0)
407
                ft.usageCount = xml.convertElementToInt(content);
408
 
409
            oldIndex = index;
410
        }
411
 
412
        mFonts.insert(pair<int, FONT_T>(ft.number, ft));
413
 
414
        if (index == TExpat::npos)
415
            index = oldIndex + 1;
416
 
417
        xml.setIndex(index);
418
    }
419
 
420
    mutex_font.unlock();
421
}
422
 
423
bool TFont::systemFonts(bool setup)
424
{
425
    DECL_TRACER("TFont::systemFonts(bool setup)");
426
 
427
    string path;
428
 
429
    if (setup)
462 andreas 430
        path = makeFileName(TConfig::getSystemProjectPath(), "/" + mFontFile);
446 andreas 431
    else
462 andreas 432
        path = makeFileName(TConfig::getSystemProjectPath(), "/graphics/" + mFontFile);
446 andreas 433
 
434
    if (!isValidFile())
435
    {
436
        MSG_ERROR("File " << path << " doesn't exist or is not readable!");
437
        TError::setError();
438
        return false;
439
    }
440
 
441
    TExpat xml(path);
442
 
462 andreas 443
    if (!mIsTP5)
444
        xml.setEncoding(ENC_CP1250);
486 andreas 445
    else
446
        xml.setEncoding(ENC_UTF8);
462 andreas 447
 
446 andreas 448
    if (!xml.parse())
449
        return false;
450
 
451
    int depth = 0;
452
    size_t index = 0;
453
    size_t oldIndex = 0;
454
 
455
    if (xml.getElementIndex("fontList", &depth) == TExpat::npos)
456
    {
457
        MSG_DEBUG("File does not contain the element \"fontList\"!");
458
        TError::setError();
459
        return false;
460
    }
461
 
462
    depth++;
462 andreas 463
    int fnumber = 1;
446 andreas 464
 
465
    while ((index = xml.getNextElementIndex("font", depth)) != TExpat::npos)
466
    {
467
        string name, content;
468
        FONT_T ft;
462 andreas 469
        vector<ATTRIBUTE_t> attrs;
446 andreas 470
 
462 andreas 471
        if (!mIsTP5)
472
        {
473
            attrs = xml.getAttributes(index);
474
 
475
            if (!attrs.empty())
476
                ft.number = xml.getAttributeInt("number", attrs);
477
            else
478
            {
479
                MSG_ERROR("Element font contains no or invalid attribute!");
480
                TError::setError();
481
                return false;
482
            }
483
        }
446 andreas 484
        else
485
        {
462 andreas 486
            ft.number = fnumber;
487
            fnumber++;
446 andreas 488
        }
489
 
490
        while ((index = xml.getNextElementFromIndex(index, &name, &content, &attrs)) != TExpat::npos)
491
        {
492
            string e = name;
493
 
494
            if (e.compare("file") == 0)
495
                ft.file = content;
496
            else if (e.compare("fileSize") == 0)
497
                ft.fileSize = xml.convertElementToInt(content);
498
            else if (e.compare("faceIndex") == 0)
499
                ft.faceIndex = xml.convertElementToInt(content);
500
            else if (e.compare("name") == 0)
501
                ft.name = content;
502
            else if (e.compare("subfamilyName") == 0)
503
                ft.subfamilyName = content;
504
            else if (e.compare("fullName") == 0)
505
                ft.fullName = content;
506
            else if (e.compare("size") == 0)
507
                ft.size = xml.convertElementToInt(content);
508
            else if (e.compare("usageCount") == 0)
509
                ft.usageCount = xml.convertElementToInt(content);
510
 
511
            oldIndex = index;
512
        }
462 andreas 513
#if __cplusplus < 201703L
446 andreas 514
        mFonts.insert(pair<int, FONT_T>(ft.number, ft));
462 andreas 515
#else
516
        mFonts.insert_or_assign(ft.number, ft);
517
#endif
446 andreas 518
        if (index == TExpat::npos)
519
            index = oldIndex + 1;
520
 
521
        xml.setIndex(index);
522
    }
523
 
524
    return true;
525
}
526
 
527
FONT_T TFont::getFont(int number)
528
{
529
    DECL_TRACER("TFont::getFont(int number)");
530
 
531
    if (mFonts.size() == 0)
532
    {
533
        MSG_WARNING("No fonts found!");
534
        return FONT_T();
535
    }
536
 
537
    map<int, FONT_T>::iterator iter = mFonts.find(number);
538
 
539
    if (iter == mFonts.end())
540
    {
541
        MSG_WARNING("No font with number " << number << " found!");
542
        return FONT_T();
543
    }
544
 
545
    return iter->second;
546
}
547
 
548
int TFont::getFontIDfromFile(const string& file)
549
{
550
    DECL_TRACER("TFont::getFontIDfromFile(const string& file)");
551
 
552
    if (mFonts.size() == 0)
553
    {
554
        MSG_WARNING("No fonts found!");
555
        return -1;
556
    }
557
 
558
    map<int, FONT_T>::iterator iter;
559
 
560
    for (iter = mFonts.begin(); iter != mFonts.end(); ++iter)
561
    {
562
        if (iter->second.file == file)
563
            return iter->first;
564
    }
565
#if TESTMODE == 1
566
    MSG_WARNING("There is no font file \"" << file << "\" found!");
567
#endif
568
    return -1;
569
}
570
 
571
int TFont::getFontIDfromName(const string &name)
572
{
573
    DECL_TRACER("TFont::getFontIDfromName(const string &name)");
574
 
575
    if (mFonts.size() == 0)
576
    {
577
        MSG_WARNING("No fonts found!");
578
        return -1;
579
    }
580
 
581
    map<int, FONT_T>::iterator iter;
582
 
583
    for (iter = mFonts.begin(); iter != mFonts.end(); ++iter)
584
    {
585
        if (iter->second.name == name)
586
            return iter->first;
587
    }
588
#if TESTMODE == 1
589
    MSG_WARNING("There is no font name \"" << name << "\" found!");
590
#endif
591
    return -1;
592
}
593
 
594
FONT_STYLE TFont::getStyle(int number)
595
{
596
    DECL_TRACER("TFont::getStyle(int number)");
597
 
598
    map<int, FONT_T>::iterator iter = mFonts.find(number);
599
 
600
    if (iter == mFonts.end())
601
        return FONT_NONE;
602
 
603
    if (iter->second.subfamilyName.compare("Regular") == 0)
604
        return FONT_NORMAL;
605
    else if (iter->second.subfamilyName.compare("Italic") == 0)
606
        return FONT_ITALIC;
607
    else if (iter->second.subfamilyName.compare("Bold") == 0)
608
        return FONT_BOLD;
609
    else if (iter->second.subfamilyName.compare("Bold Italic") == 0)
610
        return FONT_BOLD_ITALIC;
611
 
612
    return FONT_NORMAL;
613
}
614
 
615
FONT_STYLE TFont::getStyle(FONT_T& font)
616
{
617
    DECL_TRACER("TFont::getStyle(int number)");
618
 
619
    if (font.subfamilyName.compare("Regular") == 0)
620
        return FONT_NORMAL;
621
    else if (font.subfamilyName.compare("Italic") == 0)
622
        return FONT_ITALIC;
623
    else if (font.subfamilyName.compare("Bold") == 0)
624
        return FONT_BOLD;
625
    else if (font.subfamilyName.compare("Bold Italic") == 0)
626
        return FONT_BOLD_ITALIC;
627
 
628
    return FONT_NORMAL;
629
}
630
 
631
SkFontStyle TFont::getSkiaStyle(int number)
632
{
633
    DECL_TRACER("TFont::getSkiaStyle(int number)");
634
 
635
    map<int, FONT_T>::iterator iter = mFonts.find(number);
636
 
637
    if (iter == mFonts.end())
638
        return SkFontStyle(SkFontStyle::kNormal_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant);
639
 
640
    if (iter->second.subfamilyName.compare("Regular") == 0)
641
        return SkFontStyle::Normal();
642
    else if (iter->second.subfamilyName.compare("Italic") == 0)
643
        return SkFontStyle::Italic();
644
    else if (iter->second.subfamilyName.compare("Bold") == 0)
645
        return SkFontStyle::Bold();
646
    else if (iter->second.subfamilyName.compare("Bold Italic") == 0)
647
        return SkFontStyle::BoldItalic();
648
 
649
    return SkFontStyle::Normal();
650
}
651
 
652
#define MAX_FACES   10
653
 
462 andreas 654
sk_sp<SkTypeface> TFont::getTypeFace(const string& ff)
655
{
656
    DECL_TRACER("TFont::getTypeFace(const string& ff)");
657
 
658
    map<int, FONT_T>::iterator iter;
659
 
660
    for (iter = mFonts.begin(); iter != mFonts.end(); ++iter)
661
    {
662
        if (iter->second.file == ff)
663
            return getTypeFace(iter->first);
664
    }
665
 
666
    return nullptr;
667
}
668
 
669
 
446 andreas 670
sk_sp<SkTypeface> TFont::getTypeFace(int number)
671
{
672
    DECL_TRACER("TFont::getTypeFace(int number)");
673
 
674
    map<int, FONT_T>::iterator iter = mFonts.find(number);
675
 
676
    if (iter == mFonts.end())
677
    {
678
        MSG_ERROR("No font with index " << number << " found!");
679
        TError::setError();
680
        return nullptr;
681
    }
682
 
683
    if (!_tfontCache.empty())
684
    {
685
        map<string, sk_sp<SkTypeface> >::iterator itFont = _tfontCache.find(iter->second.file);
686
 
687
        if (itFont != _tfontCache.end() && itFont->second)
688
        {
689
            MSG_DEBUG("Font " << number << ": " << iter->second.file << " was taken from cache.");
690
            return itFont->second;
691
        }
692
        else if (itFont != _tfontCache.end())
693
            _tfontCache.erase(itFont);
694
    }
695
 
696
    mutex_font.lock();
697
    string path;
698
 
699
    if (number < 32)    // System font?
700
    {
701
        path = TConfig::getProjectPath() + "/__system/graphics/fonts/" + iter->second.file;
702
 
703
        if (!isValidFile(path))
704
        {
705
            MSG_WARNING("Seem to miss system fonts ...");
706
            path = TConfig::getProjectPath() + "/fonts/" + iter->second.file;
707
        }
708
    }
709
    else
710
    {
711
        path = TConfig::getProjectPath() + "/fonts/" + iter->second.file;
712
 
713
        if (!fs::exists(path))
714
        {
715
            string pth = TConfig::getProjectPath() + "/__system/fonts/" + iter->second.file;
716
 
717
            if (fs::exists(pth))
718
                path = pth;
719
        }
720
    }
721
 
722
    sk_sp<SkTypeface> tf;
723
    MSG_DEBUG("Loading font \"" << path << "\" ...");
724
 
725
    if (isValidFile(path))
726
    {
727
        try
728
        {
729
            tf = MakeResourceAsTypeface(path.c_str(), iter->second.faceIndex, RESTYPE_FONT);
730
        }
731
        catch(std::exception& e)
732
        {
733
            MSG_ERROR("Error loading font: " << e.what());
734
        }
735
    }
736
    else
737
        MSG_WARNING("File " << path << " is not a valid file or does not exist!");
738
 
739
    if (!tf)
740
    {
741
        string perms = getPermissions(path);
742
        MSG_ERROR("Error loading font \"" << path << "\" [" << perms << "]");
743
        MSG_PROTOCOL("Trying with alternative function ...");
744
        TError::setError();
745
 
746
        tf = SkTypeface::MakeFromName(iter->second.fullName.c_str(), getSkiaStyle(number));
747
 
748
        if (tf)
749
            TError::clear();
750
        else
751
        {
752
            MSG_ERROR("Alternative method failed loading the font " << iter->second.fullName);
753
            MSG_WARNING("Will use a default font instead!");
754
            tf = SkTypeface::MakeDefault();
755
 
756
            if (tf)
757
                TError::clear();
758
            else
759
            {
760
                MSG_ERROR("No default font found!");
761
                mutex_font.unlock();
762
                return tf;
763
            }
764
        }
765
    }
766
    else
767
    {
768
        if (tf->countTables() > 0)
769
        {
770
            _tfontCache.insert(pair<string, sk_sp<SkTypeface> >(iter->second.file, tf));
771
            MSG_DEBUG("Font \"" << path << "\" was loaded successfull.");
772
        }
773
        else
774
        {
775
            MSG_WARNING("Refused to enter invalid typeface into font cache!");
776
        }
777
    }
778
 
779
    SkString sname;
780
    tf->getFamilyName(&sname);
781
 
782
    if (iter->second.name.compare(sname.c_str()) != 0)
783
    {
784
        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"));
785
        MSG_WARNING("The loaded font \"" << sname.c_str() << "\" is not the wanted font \"" << iter->second.name << "\"!");
786
    }
787
 
788
    FONT_STYLE style = getStyle(iter->second);
789
    bool ret = false;
790
 
791
    if (style == FONT_BOLD && tf->isBold())
792
        ret = true;
793
    else if (style == FONT_ITALIC && tf->isItalic())
794
        ret = true;
795
    else if (style == FONT_BOLD_ITALIC && tf->isBold() && tf->isItalic())
796
        ret = true;
797
    else if (style == FONT_NORMAL && !tf->isBold() && !tf->isItalic())
798
        ret = true;
799
 
800
    if (ret)
801
    {
802
        mutex_font.unlock();
803
        return tf;
804
    }
805
 
806
    MSG_WARNING("The wanted font style " << iter->second.subfamilyName << " was not found!");
807
    mutex_font.unlock();
808
    return tf;
809
}
810
 
811
vector<string> TFont::getFontPathList()
812
{
813
    DECL_TRACER("TFont::getFontPathList()");
814
 
815
    vector<string> list;
816
    string path = TConfig::getProjectPath() + "/fonts";
817
    list.push_back(path);
818
    path = TConfig::getProjectPath() + "/__system/fonts";
819
    list.push_back(path);
820
    path = TConfig::getProjectPath() + "/__system/graphics/fonts";
821
    list.push_back(path);
822
    return list;
823
}
824
 
825
size_t _numTables;
826
FTABLE_CMAP_t _cmapTable;
827
 
828
void TFont::parseCmap(const unsigned char* cmaps)
829
{
830
    DECL_TRACER("TFont::parseCmap(const unsigned char* cmaps)");
831
 
832
    if (!cmaps)
833
        return;
834
 
835
    _cmapTable.version = getUint16(cmaps);
836
    _cmapTable.numSubtables = getUint16(cmaps+sizeof(uint16_t));
837
    MSG_DEBUG("Found version " << _cmapTable.version << ", found " << _cmapTable.numSubtables << " cmap tables.");
838
    _cmapTable.subtables = new FTABLE_SUBTABLE_t[_cmapTable.numSubtables];
839
    size_t pos = sizeof(uint16_t) * 2;
840
 
841
    for (uint16_t i = 0; i < _cmapTable.numSubtables; i++)
842
    {
843
        FTABLE_SUBTABLE_t st;
844
        st.platformID = getUint16(cmaps+pos);
845
        pos += sizeof(uint16_t);
846
        st.platformSpecificID = getUint16(cmaps+pos);
847
        pos += sizeof(uint16_t);
848
        st.offset = getUint32(cmaps+pos);
849
        pos += sizeof(uint32_t);
850
        memmove(&_cmapTable.subtables[i], &st, sizeof(st));
851
        MSG_DEBUG("Table " << (i+1) << ": platformID=" << st.platformID << ", platformSpecificID=" << st.platformSpecificID << ", offset=" << st.offset);
852
    }
853
 
854
    // Get the format and read the mapping
855
    for (uint16_t i = 0; i < _cmapTable.numSubtables; i++)
856
    {
857
        if (_cmapTable.subtables[i].platformID == FTABLE_PID_MACINTOSH)   // We ignore old Macintosh format
858
        {
859
            _cmapTable.subtables[i].format.format = -1;
860
            continue;
861
        }
862
 
863
        FTABLE_FORMATS_t format;
864
        format.format = getUint16(cmaps+_cmapTable.subtables[i].offset);
865
        pos = _cmapTable.subtables[i].offset + sizeof(uint16_t);
866
 
867
        switch(format.format)
868
        {
869
            case 0:
870
                format.fdef.format0.length = getUint16(cmaps+pos);
871
                pos += sizeof(uint16_t);
872
                format.fdef.format0.language = getUint16(cmaps+pos);
873
                pos += sizeof(uint16_t);
874
                memcpy(format.fdef.format0.glyphIndex, cmaps+pos, sizeof(format.fdef.format0.glyphIndex));
875
            break;
876
 
877
            case 4:
878
                format.fdef.format4.length = getUint16(cmaps+pos);
879
                pos += sizeof(uint16_t);
880
                format.fdef.format4.language = getUint16(cmaps+pos);
881
                pos += sizeof(uint16_t);
882
                format.fdef.format4.segCountX2 = getUint16(cmaps+pos);
883
                pos += sizeof(uint16_t);
884
                format.fdef.format4.searchRange = getUint16(cmaps+pos);
885
                pos += sizeof(uint16_t);
886
                format.fdef.format4.entrySelector = getUint16(cmaps+pos);
887
                pos += sizeof(uint16_t);
888
                format.fdef.format4.rangeShift = getUint16(cmaps+pos);
889
                pos += sizeof(uint16_t);
890
                uint16_t segCount = format.fdef.format4.segCountX2 / 2;
891
                format.fdef.format4.endCode = new uint16_t[segCount];
892
 
893
                for (uint16_t j = 0; j < segCount; j++)
894
                {
895
                    format.fdef.format4.endCode[j] = getUint16(cmaps+pos);
896
                    pos += sizeof(uint16_t);
897
                }
898
 
899
                format.fdef.format4.reservedPad = getUint16(cmaps+pos);
900
                pos += sizeof(uint16_t);
901
                format.fdef.format4.startCode = new uint16_t[segCount];
902
 
903
                for (uint16_t j = 0; j < segCount; j++)
904
                {
905
                    format.fdef.format4.startCode[j] = getUint16(cmaps+pos);
906
                    pos += sizeof(uint16_t);
907
                }
908
 
909
                format.fdef.format4.idDelta = new uint16_t[segCount];
910
 
911
                for (uint16_t j = 0; j < segCount; j++)
912
                {
913
                    format.fdef.format4.idDelta[j] = getUint16(cmaps+pos);
914
                    pos += sizeof(uint16_t);
915
                }
916
 
917
                format.fdef.format4.idRangeOffset = new uint16_t[segCount];
918
 
919
                for (uint16_t j = 0; j < segCount; j++)
920
                {
921
                    format.fdef.format4.idRangeOffset[j] = getUint16(cmaps+pos);
922
                    pos += sizeof(uint16_t);
923
                }
924
 
925
                format.fdef.format4.glyphIndexArray = (uint16_t *)(cmaps+pos);
926
                memcpy(&_cmapTable.subtables[i].format, &format, sizeof(FTABLE_FORMATS_t));
927
            break;
928
        }
929
    }
930
}
931
 
932
uint16_t TFont::getGlyphIndex(SkUnichar ch)
933
{
934
    DECL_TRACER("TFont::getGlyphIndex(const char* ch)");
935
 
936
    uint16_t lCh = ch;
937
    bool symbol = false;
938
 
939
    for (uint16_t nTbs = 0; nTbs < _cmapTable.numSubtables; nTbs++)
940
    {
941
        if (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_UNICODE ||
942
            _cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT)
943
        {
944
            if ((_cmapTable.subtables[nTbs].platformID == FTABLE_PID_UNICODE &&_cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_UNI_VERSION1) ||
945
                (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT && _cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_MSC_SYMBOL))
946
                symbol = true;  // Table does not have unicode table mapping (wingding)
947
 
948
            // Find the segment where the wanted character is in.
949
            if (_cmapTable.subtables[nTbs].format.format == 4)
950
            {
951
                FTABLE_FORMAT4_t form = _cmapTable.subtables[nTbs].format.fdef.format4;
952
                uint16_t segCount = form.segCountX2 / 2;
953
                uint16_t segment = 0xffff;
954
                MSG_DEBUG("segCountX2: " << form.segCountX2 << ", # segments: " << segCount);
955
 
956
                for (uint16_t sc = 0; sc < segCount; sc++)
957
                {
958
                    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);
959
 
960
                    if (symbol)
961
                    {
962
                        if (ch <= 0x00ff)
963
                            lCh = ch + (form.startCode[sc] & 0xff00);
964
                        else
965
                            lCh = ch + (form.startCode[sc] & 0xf000);
966
                    }
967
 
968
                    if (lCh >= form.startCode[sc] && lCh <= form.endCode[sc])
969
                    {
970
                        segment = sc;
971
                        break;
972
                    }
973
                }
974
 
975
                if (segment == 0xffff || form.startCode[segment] == 0xffff || form.endCode[segment] == 0xffff)
976
                {
977
                    MSG_WARNING("The character " << std::hex << std::setw(4) << std::setfill('0') << lCh << " is not supported by any segment!" << std::dec);
978
                    continue;
979
                }
980
 
981
                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);
982
                uint16_t glyphIndex;
983
 
984
                if (form.idRangeOffset[segment] == 0)
985
                    glyphIndex = form.idDelta[segment] + lCh;
986
                else
987
                {
988
                    uint16_t gArray = getUint16((unsigned char *)(form.glyphIndexArray + (form.idRangeOffset[segment] / 2)));
989
                    MSG_DEBUG("Value from glyphArray: " << std::hex << std::setw(4) << std::setfill('0') << gArray);
990
 
991
                    if (symbol && segment > 0 && gArray != 0)
992
                        glyphIndex = gArray + (lCh - form.startCode[segment]) - 2;
993
                    else
994
                        glyphIndex = gArray / 2 + (lCh - form.startCode[segment]);
995
                }
996
 
997
                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);
998
                return glyphIndex;
999
            }
1000
            else
1001
            {
1002
                MSG_WARNING("Ignoring table with unsupported format " << _cmapTable.subtables[nTbs].format.format);
1003
            }
1004
        }
1005
    }
1006
 
1007
    return 0xffff;
1008
}
1009
 
1010
void TFont::_freeCmap()
1011
{
1012
    DECL_TRACER("TFont::_freeCmap()");
1013
 
1014
    for (uint16_t nTbs = 0; nTbs < _cmapTable.numSubtables; nTbs++)
1015
    {
1016
        if (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_UNICODE ||
1017
            _cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT)
1018
        {
1019
            if (_cmapTable.subtables[nTbs].format.format == 4)
1020
            {
1021
                delete[] _cmapTable.subtables[nTbs].format.fdef.format4.endCode;
1022
                delete[] _cmapTable.subtables[nTbs].format.fdef.format4.startCode;
1023
                delete[] _cmapTable.subtables[nTbs].format.fdef.format4.idDelta;
1024
                delete[] _cmapTable.subtables[nTbs].format.fdef.format4.idRangeOffset;
1025
            }
1026
        }
1027
    }
1028
 
1029
    delete[] _cmapTable.subtables;
1030
}
1031
 
1032
SkGlyphID *TFont::textToGlyphs(const string& str, sk_sp<SkTypeface>& typeFace, size_t *size)
1033
{
1034
    DECL_TRACER("TFont::textToGlyphs(const string& str, SkTypeface& typeFace)");
1035
 
1036
    *size = 0;
1037
    int tables = typeFace->countTables();
1038
 
1039
    if (!tables)
1040
    {
1041
        MSG_ERROR("No tables in typeface!");
1042
        return nullptr;
1043
    }
1044
 
1045
    SkFontTableTag *tbTags = new SkFontTableTag[tables];
1046
    int tags = typeFace->getTableTags(tbTags);
1047
    bool haveCmap = false;
1048
    unsigned char *cmaps = nullptr;
1049
 
1050
    // Find the "cmap" and the "glyph" tables and get them
1051
    for (int i = 0; i < tags; i++)
1052
    {
1053
        SkFontTableTag fttg = tbTags[i];
1054
 
1055
        if (fttg == FTABLE_cmap)
1056
        {
1057
            size_t tbSize = typeFace->getTableSize(fttg);
1058
 
1059
            if (!tbSize)
1060
            {
1061
                MSG_ERROR("CMAP font table size is 0!");
1062
                delete[] tbTags;
1063
                return nullptr;
1064
            }
1065
 
1066
            cmaps = new unsigned char[tbSize];
1067
            typeFace->getTableData(fttg, 0, tbSize, cmaps);
1068
            haveCmap = true;
1069
            break;
1070
        }
1071
    }
1072
 
1073
    if (!haveCmap)
1074
    {
1075
        MSG_ERROR("Invalid font. Missing CMAP table!");
1076
//        TError::setError();
1077
        delete[] tbTags;
1078
        return nullptr;
1079
    }
1080
 
1081
    delete[] tbTags;
1082
    parseCmap(cmaps);
1083
 
1084
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
1085
    std::u16string dest = convert.from_bytes(str);
1086
    SkGlyphID *gIds = new SkGlyphID[dest.length()];
1087
    *size = dest.length();
1088
 
1089
    for (size_t i = 0; i < dest.length(); i++)
1090
    {
1091
        SkUnichar uniChar = (SkUnichar)dest[i];
1092
        gIds[i] = getGlyphIndex(uniChar);
1093
    }
1094
 
1095
    _freeCmap();
1096
    delete[] cmaps;
1097
 
1098
    return gIds;
1099
}
1100
 
1101
FONT_TYPE TFont::isSymbol(sk_sp<SkTypeface>& typeFace)
1102
{
1103
    DECL_TRACER("TFont::isSymbol(sk_sp<SkTypeface>& typeFace)");
1104
 
1105
    if (!typeFace)
1106
    {
1107
        MSG_ERROR("Got an empty typeface!");
1108
        return FT_UNKNOWN;
1109
    }
1110
 
1111
    mutex_font.lock();
1112
    int tables = typeFace->countTables();
1113
 
1114
    if (tables <= 0)
1115
    {
1116
        MSG_ERROR("No tables found in typeface!");
1117
        mutex_font.unlock();
1118
        return FT_UNKNOWN;
1119
    }
1120
 
1121
    SkFontTableTag *tbTags = new SkFontTableTag[tables];
1122
    int tags = typeFace->getTableTags(tbTags);
1123
    bool haveCmap = false;
1124
    unsigned char *cmaps = nullptr;
1125
 
1126
    // Find the "cmap" table and get it
1127
    for (int i = 0; i < tags; i++)
1128
    {
1129
        SkFontTableTag fttg = tbTags[i];
1130
 
1131
        if (fttg == FTABLE_cmap)
1132
        {
1133
            size_t tbSize = typeFace->getTableSize(fttg);
1134
 
1135
            if (!tbSize)
1136
            {
1137
                MSG_ERROR("CMAP table has size of 0!");
1138
                delete[] tbTags;
1139
                mutex_font.unlock();
1140
                return FT_UNKNOWN;
1141
            }
1142
 
1143
            cmaps = new unsigned char[tbSize];
1144
            typeFace->getTableData(fttg, 0, tbSize, cmaps);
1145
            haveCmap = true;
1146
            break;
1147
        }
1148
    }
1149
 
1150
    if (!haveCmap)
1151
    {
1152
        MSG_ERROR("Invalid font. Missing CMAP table!");
1153
//        TError::setError();
1154
        delete[] tbTags;
1155
        mutex_font.unlock();
1156
        return FT_UNKNOWN;
1157
    }
1158
 
1159
    delete[] tbTags;
1160
    parseCmap(cmaps);
1161
 
1162
    for (uint16_t nTbs = 0; nTbs < _cmapTable.numSubtables; nTbs++)
1163
    {
1164
        if ((_cmapTable.subtables[nTbs].platformID == FTABLE_PID_UNICODE && _cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_UNI_VERSION1) ||
1165
            (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT && _cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_MSC_SYMBOL))
1166
        {
1167
            _freeCmap();
1168
            delete[] cmaps;
1169
            FONT_TYPE ft = FT_SYMBOL;
1170
 
1171
            if (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT && _cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_MSC_SYMBOL)
1172
                ft = FT_SYM_MS;
1173
 
1174
            mutex_font.unlock();
1175
            return ft;
1176
        }
1177
    }
1178
 
1179
    _freeCmap();
1180
    delete[] cmaps;
1181
    mutex_font.unlock();
1182
    return FT_NORMAL;
1183
}
1184
 
1185
size_t TFont::utf8ToUtf16(const string& str, uint16_t **uni, bool toSymbol)
1186
{
1187
    DECL_TRACER("TFont::utf8ToUtf16(const string& str, uint16_t **uni, bool toSymbol)");
1188
 
1189
    if (str.empty() || !uni)
1190
    {
1191
        if (uni)
1192
            *uni = nullptr;
1193
 
1194
        return 0;
1195
    }
1196
 
1197
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
1198
    std::u16string dest = convert.from_bytes(str);
1199
 
1200
    *uni = new uint16_t[dest.length()+1];
1201
    uint16_t *unicode = *uni;
1202
    memset(unicode, 0, sizeof(uint16_t) * (dest.length()+1));
1203
 
1204
    for (size_t i = 0; i < dest.length(); i++)
1205
    {
1206
        unicode[i] = dest[i];
1207
 
1208
        if (toSymbol && unicode[i] < 0xf000)
1209
            unicode[i] += 0xf000;
1210
    }
1211
 
1212
    return dest.length();
1213
}
1214
 
1215
double TFont::pixelToPoint(int dpi, int pixel)
1216
{
1217
    DECL_TRACER("TFont::pixelToPoint(int dpi, int pixel)");
1218
 
1219
    double size = 0.0138889 * (double)dpi * (double)pixel;
1220
    MSG_DEBUG("Size: " << size << ", dpi: " << dpi << ", pixels: " << pixel);
1221
    return size;
1222
}