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