Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
7 andreas 1
/*
157 andreas 2
 * Copyright (C) 2020 to 2022 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
    }
524
 
150 andreas 525
    return -1;
149 andreas 526
}
527
 
7 andreas 528
FONT_STYLE TFont::getStyle(int number)
529
{
530
    DECL_TRACER("TFont::getStyle(int number)");
531
 
532
    map<int, FONT_T>::iterator iter = mFonts.find(number);
533
 
534
    if (iter == mFonts.end())
535
        return FONT_NONE;
536
 
537
    if (iter->second.subfamilyName.compare("Regular") == 0)
538
        return FONT_NORMAL;
539
    else if (iter->second.subfamilyName.compare("Italic") == 0)
540
        return FONT_ITALIC;
541
    else if (iter->second.subfamilyName.compare("Bold") == 0)
542
        return FONT_BOLD;
543
    else if (iter->second.subfamilyName.compare("Bold Italic") == 0)
544
        return FONT_BOLD_ITALIC;
545
 
546
    return FONT_NORMAL;
547
}
548
 
549
FONT_STYLE TFont::getStyle(FONT_T& font)
550
{
551
    DECL_TRACER("TFont::getStyle(int number)");
552
 
553
    if (font.subfamilyName.compare("Regular") == 0)
554
        return FONT_NORMAL;
555
    else if (font.subfamilyName.compare("Italic") == 0)
556
        return FONT_ITALIC;
557
    else if (font.subfamilyName.compare("Bold") == 0)
558
        return FONT_BOLD;
559
    else if (font.subfamilyName.compare("Bold Italic") == 0)
560
        return FONT_BOLD_ITALIC;
561
 
562
    return FONT_NORMAL;
563
}
564
 
40 andreas 565
SkFontStyle TFont::getSkiaStyle(int number)
566
{
567
    DECL_TRACER("TFont::getSkiaStyle(int number)");
7 andreas 568
 
40 andreas 569
    map<int, FONT_T>::iterator iter = mFonts.find(number);
570
 
571
    if (iter == mFonts.end())
163 andreas 572
        return SkFontStyle(SkFontStyle::kNormal_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant);
40 andreas 573
 
574
    if (iter->second.subfamilyName.compare("Regular") == 0)
575
        return SkFontStyle::Normal();
576
    else if (iter->second.subfamilyName.compare("Italic") == 0)
577
        return SkFontStyle::Italic();
578
    else if (iter->second.subfamilyName.compare("Bold") == 0)
579
        return SkFontStyle::Bold();
580
    else if (iter->second.subfamilyName.compare("Bold Italic") == 0)
581
        return SkFontStyle::BoldItalic();
582
 
583
    return SkFontStyle::Normal();
584
}
585
 
7 andreas 586
#define MAX_FACES   10
587
 
588
sk_sp<SkTypeface> TFont::getTypeFace(int number)
589
{
590
    DECL_TRACER("TFont::getTypeFace(int number)");
591
 
592
    map<int, FONT_T>::iterator iter = mFonts.find(number);
593
 
594
    if (iter == mFonts.end())
595
    {
596
        MSG_ERROR("No font with index " << number << " found!");
597
        TError::setError();
164 andreas 598
        return nullptr;
7 andreas 599
    }
600
 
156 andreas 601
    if (!_tfontCache.empty())
602
    {
603
        map<string, sk_sp<SkTypeface> >::iterator itFont = _tfontCache.find(iter->second.file);
604
 
605
        if (itFont != _tfontCache.end() && itFont->second)
606
        {
607
            MSG_DEBUG("Font " << number << ": " << iter->second.file << " was taken from cache.");
608
            return itFont->second;
609
        }
610
        else if (itFont != _tfontCache.end())
611
            _tfontCache.erase(itFont);
612
    }
613
 
157 andreas 614
    mutex_font.lock();
45 andreas 615
    string path;
616
 
617
    if (number < 32)    // System font?
156 andreas 618
    {
45 andreas 619
        path = TConfig::getProjectPath() + "/__system/graphics/fonts/" + iter->second.file;
156 andreas 620
 
621
        if (!isValidFile(path))
622
        {
623
            MSG_WARNING("Seem to miss system fonts ...");
624
            path = TConfig::getProjectPath() + "/fonts/" + iter->second.file;
625
        }
626
    }
45 andreas 627
    else
192 andreas 628
    {
45 andreas 629
        path = TConfig::getProjectPath() + "/fonts/" + iter->second.file;
630
 
192 andreas 631
        if (!fs::exists(path))
632
        {
633
            string pth = TConfig::getProjectPath() + "/__system/fonts/" + iter->second.file;
634
 
635
            if (fs::exists(pth))
636
                path = pth;
637
        }
638
    }
639
 
7 andreas 640
    sk_sp<SkTypeface> tf;
156 andreas 641
    MSG_DEBUG("Loading font \"" << path << "\" ...");
7 andreas 642
 
45 andreas 643
    if (isValidFile(path))
163 andreas 644
    {
645
        try
646
        {
647
            tf = MakeResourceAsTypeface(path.c_str(), iter->second.faceIndex, RESTYPE_FONT);
648
        }
649
        catch(std::exception& e)
650
        {
651
            MSG_ERROR("Error loading font: " << e.what());
652
        }
653
    }
45 andreas 654
    else
655
        MSG_WARNING("File " << path << " is not a valid file or does not exist!");
656
 
7 andreas 657
    if (!tf)
658
    {
156 andreas 659
        string perms = getPermissions(path);
660
        MSG_ERROR("Error loading font \"" << path << "\" [" << perms << "]");
661
        MSG_PROTOCOL("Trying with alternative function ...");
7 andreas 662
        TError::setError();
40 andreas 663
 
664
        tf = SkTypeface::MakeFromName(iter->second.fullName.c_str(), getSkiaStyle(number));
665
 
666
        if (tf)
667
            TError::clear();
668
        else
45 andreas 669
        {
670
            MSG_ERROR("Alternative method failed loading the font " << iter->second.fullName);
156 andreas 671
            MSG_WARNING("Will use a default font instead!");
672
            tf = SkTypeface::MakeDefault();
673
 
674
            if (tf)
675
                TError::clear();
676
            else
677
            {
678
                MSG_ERROR("No default font found!");
157 andreas 679
                mutex_font.unlock();
156 andreas 680
                return tf;
681
            }
45 andreas 682
        }
7 andreas 683
    }
684
    else
685
    {
164 andreas 686
        if (tf->countTables() > 0)
687
        {
688
            _tfontCache.insert(pair<string, sk_sp<SkTypeface> >(iter->second.file, tf));
689
            MSG_DEBUG("Font \"" << path << "\" was loaded successfull.");
690
        }
691
        else
692
        {
693
            MSG_WARNING("Refused to enter invalid typeface into font cache!");
694
        }
7 andreas 695
    }
696
 
697
    SkString sname;
698
    tf->getFamilyName(&sname);
699
 
700
    if (iter->second.name.compare(sname.c_str()) != 0)
701
    {
156 andreas 702
        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 703
        MSG_WARNING("The loaded font \"" << sname.c_str() << "\" is not the wanted font \"" << iter->second.name << "\"!");
704
    }
705
 
706
    FONT_STYLE style = getStyle(iter->second);
156 andreas 707
    bool ret = false;
7 andreas 708
 
709
    if (style == FONT_BOLD && tf->isBold())
156 andreas 710
        ret = true;
7 andreas 711
    else if (style == FONT_ITALIC && tf->isItalic())
156 andreas 712
        ret = true;
7 andreas 713
    else if (style == FONT_BOLD_ITALIC && tf->isBold() && tf->isItalic())
156 andreas 714
        ret = true;
7 andreas 715
    else if (style == FONT_NORMAL && !tf->isBold() && !tf->isItalic())
156 andreas 716
        ret = true;
717
 
718
    if (ret)
157 andreas 719
    {
720
        mutex_font.unlock();
7 andreas 721
        return tf;
157 andreas 722
    }
7 andreas 723
 
724
    MSG_WARNING("The wanted font style " << iter->second.subfamilyName << " was not found!");
157 andreas 725
    mutex_font.unlock();
7 andreas 726
    return tf;
727
}
51 andreas 728
 
729
vector<string> TFont::getFontPathList()
730
{
731
    DECL_TRACER("TFont::getFontPathList()");
732
 
733
    vector<string> list;
734
    string path = TConfig::getProjectPath() + "/fonts";
735
    list.push_back(path);
186 andreas 736
    path = TConfig::getProjectPath() + "/__system/fonts";
737
    list.push_back(path);
51 andreas 738
    path = TConfig::getProjectPath() + "/__system/graphics/fonts";
739
    list.push_back(path);
740
    return list;
741
}
67 andreas 742
 
743
size_t _numTables;
744
FTABLE_CMAP_t _cmapTable;
745
 
746
void TFont::parseCmap(const unsigned char* cmaps)
747
{
748
    DECL_TRACER("TFont::parseCmap(const unsigned char* cmaps)");
749
 
750
    if (!cmaps)
751
        return;
752
 
753
    _cmapTable.version = getUint16(cmaps);
754
    _cmapTable.numSubtables = getUint16(cmaps+sizeof(uint16_t));
755
    MSG_DEBUG("Found version " << _cmapTable.version << ", found " << _cmapTable.numSubtables << " cmap tables.");
756
    _cmapTable.subtables = new FTABLE_SUBTABLE_t[_cmapTable.numSubtables];
757
    size_t pos = sizeof(uint16_t) * 2;
758
 
759
    for (uint16_t i = 0; i < _cmapTable.numSubtables; i++)
760
    {
761
        FTABLE_SUBTABLE_t st;
762
        st.platformID = getUint16(cmaps+pos);
763
        pos += sizeof(uint16_t);
764
        st.platformSpecificID = getUint16(cmaps+pos);
765
        pos += sizeof(uint16_t);
766
        st.offset = getUint32(cmaps+pos);
767
        pos += sizeof(uint32_t);
768
        memmove(&_cmapTable.subtables[i], &st, sizeof(st));
769
        MSG_DEBUG("Table " << (i+1) << ": platformID=" << st.platformID << ", platformSpecificID=" << st.platformSpecificID << ", offset=" << st.offset);
770
    }
771
 
772
    // Get the format and read the mapping
773
    for (uint16_t i = 0; i < _cmapTable.numSubtables; i++)
774
    {
775
        if (_cmapTable.subtables[i].platformID == FTABLE_PID_MACINTOSH)   // We ignore old Macintosh format
776
        {
777
            _cmapTable.subtables[i].format.format = -1;
778
            continue;
779
        }
780
 
781
        FTABLE_FORMATS_t format;
782
        format.format = getUint16(cmaps+_cmapTable.subtables[i].offset);
783
        pos = _cmapTable.subtables[i].offset + sizeof(uint16_t);
784
 
785
        switch(format.format)
786
        {
787
            case 0:
788
                format.fdef.format0.length = getUint16(cmaps+pos);
789
                pos += sizeof(uint16_t);
790
                format.fdef.format0.language = getUint16(cmaps+pos);
791
                pos += sizeof(uint16_t);
792
                memcpy(format.fdef.format0.glyphIndex, cmaps+pos, sizeof(format.fdef.format0.glyphIndex));
793
            break;
794
 
795
            case 4:
796
                format.fdef.format4.length = getUint16(cmaps+pos);
797
                pos += sizeof(uint16_t);
798
                format.fdef.format4.language = getUint16(cmaps+pos);
799
                pos += sizeof(uint16_t);
800
                format.fdef.format4.segCountX2 = getUint16(cmaps+pos);
801
                pos += sizeof(uint16_t);
802
                format.fdef.format4.searchRange = getUint16(cmaps+pos);
803
                pos += sizeof(uint16_t);
804
                format.fdef.format4.entrySelector = getUint16(cmaps+pos);
805
                pos += sizeof(uint16_t);
806
                format.fdef.format4.rangeShift = getUint16(cmaps+pos);
807
                pos += sizeof(uint16_t);
808
                uint16_t segCount = format.fdef.format4.segCountX2 / 2;
809
                format.fdef.format4.endCode = new uint16_t[segCount];
810
 
811
                for (uint16_t j = 0; j < segCount; j++)
812
                {
813
                    format.fdef.format4.endCode[j] = getUint16(cmaps+pos);
814
                    pos += sizeof(uint16_t);
815
                }
816
 
817
                format.fdef.format4.reservedPad = getUint16(cmaps+pos);
818
                pos += sizeof(uint16_t);
819
                format.fdef.format4.startCode = new uint16_t[segCount];
820
 
821
                for (uint16_t j = 0; j < segCount; j++)
822
                {
823
                    format.fdef.format4.startCode[j] = getUint16(cmaps+pos);
824
                    pos += sizeof(uint16_t);
825
                }
826
 
827
                format.fdef.format4.idDelta = new uint16_t[segCount];
828
 
829
                for (uint16_t j = 0; j < segCount; j++)
830
                {
831
                    format.fdef.format4.idDelta[j] = getUint16(cmaps+pos);
832
                    pos += sizeof(uint16_t);
833
                }
834
 
835
                format.fdef.format4.idRangeOffset = new uint16_t[segCount];
836
 
837
                for (uint16_t j = 0; j < segCount; j++)
838
                {
839
                    format.fdef.format4.idRangeOffset[j] = getUint16(cmaps+pos);
840
                    pos += sizeof(uint16_t);
841
                }
842
 
843
                format.fdef.format4.glyphIndexArray = (uint16_t *)(cmaps+pos);
844
                memcpy(&_cmapTable.subtables[i].format, &format, sizeof(FTABLE_FORMATS_t));
845
            break;
846
        }
847
    }
848
}
849
 
850
uint16_t TFont::getGlyphIndex(SkUnichar ch)
851
{
852
    DECL_TRACER("TFont::getGlyphIndex(const char* ch)");
853
 
68 andreas 854
    uint16_t lCh = ch;
67 andreas 855
    bool symbol = false;
856
 
857
    for (uint16_t nTbs = 0; nTbs < _cmapTable.numSubtables; nTbs++)
858
    {
859
        if (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_UNICODE ||
860
            _cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT)
861
        {
862
            if ((_cmapTable.subtables[nTbs].platformID == FTABLE_PID_UNICODE &&_cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_UNI_VERSION1) ||
863
                (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT && _cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_MSC_SYMBOL))
864
                symbol = true;  // Table does not have unicode table mapping (wingding)
865
 
866
            // Find the segment where the wanted character is in.
867
            if (_cmapTable.subtables[nTbs].format.format == 4)
868
            {
869
                FTABLE_FORMAT4_t form = _cmapTable.subtables[nTbs].format.fdef.format4;
870
                uint16_t segCount = form.segCountX2 / 2;
871
                uint16_t segment = 0xffff;
68 andreas 872
                MSG_DEBUG("segCountX2: " << form.segCountX2 << ", # segments: " << segCount);
67 andreas 873
 
874
                for (uint16_t sc = 0; sc < segCount; sc++)
875
                {
876
                    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);
877
 
878
                    if (symbol)
879
                    {
880
                        if (ch <= 0x00ff)
881
                            lCh = ch + (form.startCode[sc] & 0xff00);
882
                        else
883
                            lCh = ch + (form.startCode[sc] & 0xf000);
884
                    }
885
 
886
                    if (lCh >= form.startCode[sc] && lCh <= form.endCode[sc])
887
                    {
888
                        segment = sc;
889
                        break;
890
                    }
891
                }
892
 
893
                if (segment == 0xffff || form.startCode[segment] == 0xffff || form.endCode[segment] == 0xffff)
894
                {
895
                    MSG_WARNING("The character " << std::hex << std::setw(4) << std::setfill('0') << lCh << " is not supported by any segment!" << std::dec);
76 andreas 896
                    continue;
67 andreas 897
                }
898
 
76 andreas 899
                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 900
                uint16_t glyphIndex;
901
 
902
                if (form.idRangeOffset[segment] == 0)
903
                    glyphIndex = form.idDelta[segment] + lCh;
904
                else
68 andreas 905
                {
906
                    uint16_t gArray = getUint16((unsigned char *)(form.glyphIndexArray + (form.idRangeOffset[segment] / 2)));
907
                    MSG_DEBUG("Value from glyphArray: " << std::hex << std::setw(4) << std::setfill('0') << gArray);
67 andreas 908
 
68 andreas 909
                    if (symbol && segment > 0 && gArray != 0)
910
                        glyphIndex = gArray + (lCh - form.startCode[segment]) - 2;
911
                    else
912
                        glyphIndex = gArray / 2 + (lCh - form.startCode[segment]);
913
                }
914
 
67 andreas 915
                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);
916
                return glyphIndex;
917
            }
76 andreas 918
            else
919
            {
920
                MSG_WARNING("Ignoring table with unsupported format " << _cmapTable.subtables[nTbs].format.format);
921
            }
67 andreas 922
        }
923
    }
924
 
925
    return 0xffff;
926
}
927
 
928
void TFont::_freeCmap()
929
{
930
    DECL_TRACER("TFont::_freeCmap()");
931
 
932
    for (uint16_t nTbs = 0; nTbs < _cmapTable.numSubtables; nTbs++)
933
    {
934
        if (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_UNICODE ||
935
            _cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT)
936
        {
937
            if (_cmapTable.subtables[nTbs].format.format == 4)
938
            {
939
                delete[] _cmapTable.subtables[nTbs].format.fdef.format4.endCode;
940
                delete[] _cmapTable.subtables[nTbs].format.fdef.format4.startCode;
941
                delete[] _cmapTable.subtables[nTbs].format.fdef.format4.idDelta;
942
                delete[] _cmapTable.subtables[nTbs].format.fdef.format4.idRangeOffset;
943
            }
944
        }
945
    }
946
 
947
    delete[] _cmapTable.subtables;
948
}
949
 
950
SkGlyphID *TFont::textToGlyphs(const string& str, sk_sp<SkTypeface>& typeFace, size_t *size)
951
{
952
    DECL_TRACER("TFont::textToGlyphs(const string& str, SkTypeface& typeFace)");
953
 
954
    *size = 0;
955
    int tables = typeFace->countTables();
156 andreas 956
 
957
    if (!tables)
958
    {
959
        MSG_ERROR("No tables in typeface!");
960
        return nullptr;
961
    }
962
 
67 andreas 963
    SkFontTableTag *tbTags = new SkFontTableTag[tables];
964
    int tags = typeFace->getTableTags(tbTags);
965
    bool haveCmap = false;
966
    unsigned char *cmaps = nullptr;
967
 
968
    // Find the "cmap" and the "glyph" tables and get them
969
    for (int i = 0; i < tags; i++)
970
    {
971
        SkFontTableTag fttg = tbTags[i];
972
 
973
        if (fttg == FTABLE_cmap)
974
        {
975
            size_t tbSize = typeFace->getTableSize(fttg);
156 andreas 976
 
977
            if (!tbSize)
978
            {
979
                MSG_ERROR("CMAP font table size is 0!");
980
                delete[] tbTags;
981
                return nullptr;
982
            }
983
 
67 andreas 984
            cmaps = new unsigned char[tbSize];
985
            typeFace->getTableData(fttg, 0, tbSize, cmaps);
986
            haveCmap = true;
987
            break;
988
        }
989
    }
990
 
991
    if (!haveCmap)
992
    {
993
        MSG_ERROR("Invalid font. Missing CMAP table!");
156 andreas 994
//        TError::setError();
67 andreas 995
        delete[] tbTags;
996
        return nullptr;
997
    }
998
 
999
    delete[] tbTags;
1000
    parseCmap(cmaps);
1001
 
1002
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
1003
    std::u16string dest = convert.from_bytes(str);
1004
    SkGlyphID *gIds = new SkGlyphID[dest.length()];
1005
    *size = dest.length();
1006
 
1007
    for (size_t i = 0; i < dest.length(); i++)
1008
    {
1009
        SkUnichar uniChar = (SkUnichar)dest[i];
1010
        gIds[i] = getGlyphIndex(uniChar);
1011
    }
1012
 
1013
    _freeCmap();
1014
    delete[] cmaps;
1015
 
1016
    return gIds;
1017
}
1018
 
157 andreas 1019
FONT_TYPE TFont::isSymbol(sk_sp<SkTypeface>& typeFace)
67 andreas 1020
{
1021
    DECL_TRACER("TFont::isSymbol(sk_sp<SkTypeface>& typeFace)");
1022
 
156 andreas 1023
    if (!typeFace)
1024
    {
1025
        MSG_ERROR("Got an empty typeface!");
157 andreas 1026
        return FT_UNKNOWN;
156 andreas 1027
    }
1028
 
1029
    mutex_font.lock();
67 andreas 1030
    int tables = typeFace->countTables();
156 andreas 1031
 
1032
    if (tables <= 0)
1033
    {
1034
        MSG_ERROR("No tables found in typeface!");
1035
        mutex_font.unlock();
157 andreas 1036
        return FT_UNKNOWN;
156 andreas 1037
    }
1038
 
67 andreas 1039
    SkFontTableTag *tbTags = new SkFontTableTag[tables];
1040
    int tags = typeFace->getTableTags(tbTags);
1041
    bool haveCmap = false;
1042
    unsigned char *cmaps = nullptr;
1043
 
156 andreas 1044
    // Find the "cmap" table and get it
67 andreas 1045
    for (int i = 0; i < tags; i++)
1046
    {
1047
        SkFontTableTag fttg = tbTags[i];
1048
 
1049
        if (fttg == FTABLE_cmap)
1050
        {
1051
            size_t tbSize = typeFace->getTableSize(fttg);
156 andreas 1052
 
1053
            if (!tbSize)
1054
            {
1055
                MSG_ERROR("CMAP table has size of 0!");
1056
                delete[] tbTags;
1057
                mutex_font.unlock();
157 andreas 1058
                return FT_UNKNOWN;
156 andreas 1059
            }
1060
 
67 andreas 1061
            cmaps = new unsigned char[tbSize];
1062
            typeFace->getTableData(fttg, 0, tbSize, cmaps);
1063
            haveCmap = true;
1064
            break;
1065
        }
1066
    }
1067
 
1068
    if (!haveCmap)
1069
    {
1070
        MSG_ERROR("Invalid font. Missing CMAP table!");
156 andreas 1071
//        TError::setError();
67 andreas 1072
        delete[] tbTags;
156 andreas 1073
        mutex_font.unlock();
157 andreas 1074
        return FT_UNKNOWN;
67 andreas 1075
    }
1076
 
1077
    delete[] tbTags;
1078
    parseCmap(cmaps);
1079
 
1080
    for (uint16_t nTbs = 0; nTbs < _cmapTable.numSubtables; nTbs++)
1081
    {
1082
        if ((_cmapTable.subtables[nTbs].platformID == FTABLE_PID_UNICODE && _cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_UNI_VERSION1) ||
1083
            (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT && _cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_MSC_SYMBOL))
1084
        {
1085
            _freeCmap();
1086
            delete[] cmaps;
157 andreas 1087
            FONT_TYPE ft = FT_SYMBOL;
1088
 
1089
            if (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT && _cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_MSC_SYMBOL)
1090
                ft = FT_SYM_MS;
1091
 
156 andreas 1092
            mutex_font.unlock();
157 andreas 1093
            return ft;
67 andreas 1094
        }
1095
    }
1096
 
1097
    _freeCmap();
1098
    delete[] cmaps;
156 andreas 1099
    mutex_font.unlock();
157 andreas 1100
    return FT_NORMAL;
67 andreas 1101
}
1102
 
1103
size_t TFont::utf8ToUtf16(const string& str, uint16_t **uni, bool toSymbol)
1104
{
1105
    DECL_TRACER("TFont::utf8ToUtf16(const string& str, uint16_t **uni, bool toSymbol)");
1106
 
1107
    if (str.empty() || !uni)
1108
    {
156 andreas 1109
        if (uni)
1110
            *uni = nullptr;
1111
 
67 andreas 1112
        return 0;
1113
    }
1114
 
1115
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
1116
    std::u16string dest = convert.from_bytes(str);
1117
 
1118
    *uni = new uint16_t[dest.length()+1];
156 andreas 1119
    uint16_t *unicode = *uni;
1120
    memset(unicode, 0, sizeof(uint16_t) * (dest.length()+1));
67 andreas 1121
 
1122
    for (size_t i = 0; i < dest.length(); i++)
1123
    {
156 andreas 1124
        unicode[i] = dest[i];
67 andreas 1125
 
156 andreas 1126
        if (toSymbol && unicode[i] < 0xf000)
1127
            unicode[i] += 0xf000;
67 andreas 1128
    }
1129
 
1130
    return dest.length();
1131
}
156 andreas 1132
 
1133
double TFont::pixelToPoint(int dpi, int pixel)
1134
{
1135
    DECL_TRACER("TFont::pixelToPoint(int dpi, int pixel)");
1136
 
1137
    double size = 0.0138889 * (double)dpi * (double)pixel;
1138
    MSG_DEBUG("Size: " << size << ", dpi: " << dpi << ", pixels: " << pixel);
1139
    return size;
1140
}