Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

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