Subversion Repositories Scribus

Rev

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

Rev Author Line No. Line
15250 jghali 1
/*
2
For general Scribus (>=1.3.2) copyright and licensing information please refer
3
to the COPYING file provided with the program. Following this notice may exist
4
a copyright and/or license notice that predates the release of Scribus 1.3.2
5
for which a new license (GPL+exception) is in place.
6
*/
5979 avox 7
 
21563 jghali 8
#include <harfbuzz/hb.h>
9
#include <harfbuzz/hb-ft.h>
10
#include <harfbuzz/hb-ot.h>
11
 
12
 
13
#include <ft2build.h>
14
#include FT_TRUETYPE_TABLES_H
15
 
5979 avox 16
#include "scribusapi.h"
17
#include "fonts/scface.h"
6144 avox 18
#include "text/storytext.h"
5979 avox 19
 
23282 craig 20
ScFace::ScFaceData::ScFaceData() = default;
13805 jghali 21
 
21563 jghali 22
ScFace::ScFaceData::~ScFaceData()
23
{
23282 craig 24
        if (!m_hbFont)
25
                return;
26
        hb_font_destroy(reinterpret_cast<hb_font_t*>(m_hbFont));
27
        m_hbFont = nullptr;
5979 avox 28
}
29
 
21563 jghali 30
static hb_blob_t* referenceTable(hb_face_t*, hb_tag_t tag, void *userData)
31
{
32
        FT_Face ftFace = reinterpret_cast<FT_Face>(userData);
33
        FT_Byte *buffer;
34
        FT_ULong length = 0;
35
 
22518 craig 36
        if (FT_Load_Sfnt_Table(ftFace, tag, 0, nullptr, &length))
37
                return nullptr;
21563 jghali 38
 
39
        buffer = reinterpret_cast<FT_Byte*>(malloc(length));
22518 craig 40
        if (buffer == nullptr)
41
                return nullptr;
21563 jghali 42
 
43
        if (FT_Load_Sfnt_Table(ftFace, tag, 0, buffer, &length))
44
        {
45
                free(buffer);
22518 craig 46
                return nullptr;
21563 jghali 47
        }
48
 
49
        return hb_blob_create((const char *) buffer, length, HB_MEMORY_MODE_WRITABLE, buffer, free);
50
}
51
 
52
void* ScFace::ScFaceData::hbFont()
53
{
54
        if (!m_hbFont)
55
        {
21853 jghali 56
                FT_Face face = ftFace();
57
                if (!face)
22518 craig 58
                        return nullptr;
21563 jghali 59
 
21853 jghali 60
                // #14699: Harfbuzz internal functions apparently do not support font with a single Apple Roman CMap
61
                // Force use of FreeType function in such case
62
                if ((face->num_charmaps == 1) && (face->charmaps[0]->encoding == FT_ENCODING_APPLE_ROMAN))
21563 jghali 63
                {
21853 jghali 64
                        m_hbFont = hb_ft_font_create_referenced(face);
65
                }
66
 
67
                if (!m_hbFont && (formatCode == ScFace::SFNT || formatCode == ScFace::TTCF || formatCode == ScFace::TYPE42))
68
                {
21563 jghali 69
                        // use HarfBuzz internal font functions for formats it supports,
70
                        // gives us more consistent glyph metrics.
21853 jghali 71
                        FT_Reference_Face(face);
72
                        hb_face_t *hbFace = hb_face_create_for_tables(referenceTable, face, (hb_destroy_func_t) FT_Done_Face);
73
                        hb_face_set_index(hbFace, face->face_index);
74
                        hb_face_set_upem(hbFace, face->units_per_EM);
21563 jghali 75
 
76
                        m_hbFont = hb_font_create(hbFace);
77
                        hb_ot_font_set_funcs(reinterpret_cast<hb_font_t*>(m_hbFont));
78
 
79
                        hb_face_destroy(hbFace);
80
                }
21853 jghali 81
 
82
                if (!m_hbFont)
21563 jghali 83
                {
84
                        m_hbFont = hb_ft_font_create_referenced(ftFace());
85
                }
86
        }
87
 
88
        return m_hbFont;
89
}
90
 
20103 avox 91
bool ScFace::ScFaceData::glyphNames(FaceEncoding& /*gList*/) const
5979 avox 92
{
93
        return false;
94
}
95
 
13162 malex 96
QMap<QString,QString> ScFace::ScFaceData::fontDictionary(qreal /*sz*/) const
5979 avox 97
{
98
        return QMap<QString, QString>();
99
}
100
 
20103 avox 101
GlyphMetrics ScFace::ScFaceData::glyphBBox(gid_type gl, qreal sz) const
5979 avox 102
{
23282 craig 103
        GlyphMetrics res{};
21563 jghali 104
        if (gl >= CONTROL_GLYPHS)
22599 craig 105
        {
106
                res.width   = glyphWidth(gl, sz);
15012 jghali 107
                res.ascent  = (gl == 0? ascent(sz) : 0);
9042 avox 108
                res.descent = 0;
109
                return res;
110
        }
22599 craig 111
        if (! m_glyphWidth.contains(gl))
9042 avox 112
                loadGlyph(gl);
113
        const struct GlyphData & data(m_glyphOutline[gl]);
114
        res.width = data.bbox_width * sz;
115
        res.ascent = data.bbox_ascent * sz;
116
        res.descent = data.bbox_descent * sz;  
5979 avox 117
        return res;
118
}
119
 
20103 avox 120
qreal ScFace::ScFaceData::glyphWidth(gid_type gl, qreal size) const
5979 avox 121
{
6978 avox 122
        if (gl >= CONTROL_GLYPHS)
6935 avox 123
                return 0.0;
22599 craig 124
        if (! m_glyphWidth.contains(gl))
5979 avox 125
                loadGlyph(gl);
126
        return m_glyphWidth[gl] * size;
127
}
128
 
23163 craig 129
FPointArray ScFace::ScFaceData::glyphOutline(gid_type gl, qreal size) const
5979 avox 130
{
6978 avox 131
        if (gl >= CONTROL_GLYPHS)
6935 avox 132
                return FPointArray();
22599 craig 133
        if (! m_glyphWidth.contains(gl))
5979 avox 134
                loadGlyph(gl);
135
        FPointArray res = m_glyphOutline[gl].Outlines.copy();
23163 craig 136
        if (size != 1.0)
137
                res.scale(size, size);
5979 avox 138
        return res;
139
}
140
 
23163 craig 141
FPoint ScFace::ScFaceData::glyphOrigin(gid_type gl, qreal size) const
5979 avox 142
{
21563 jghali 143
        if (gl >= CONTROL_GLYPHS)
6935 avox 144
                return FPoint(0,0);
22599 craig 145
        if (! m_glyphWidth.contains(gl))
5979 avox 146
                loadGlyph(gl);
147
        const struct GlyphData & res(m_glyphOutline[gl]);
23163 craig 148
        return FPoint(res.x, res.y) * size;
5979 avox 149
}
150
 
151
/*****
152
   ScFace lifecycle:  unchecked -> loaded -> glyphs checked
153
                               |         \-> broken glyphs
154
                                                           \-> broken
155
   usable() == ! broken
156
   embeddable() == glyphs_checked
157
 
21563 jghali 158
   loadChar/Glyph -> !broken
5979 avox 159
   Glyphs:  width    status
21017 craig 160
            -1000    unknown
5979 avox 161
            -2000    broken
162
            >= 0     ok, outline valid
163
   CharMap:  unicode -> glyph index
20103 avox 164
             gid_type[256][256]
5979 avox 165
   unicode ignores: < 32, ...
166
   unicode emulate: spaces, hyphen, ligatures?, diacritics?
167
 *****/
168
 
20691 craig 169
ScFace::ScFace() :  m_m(new ScFaceData())
5979 avox 170
{
20691 craig 171
        m_m->refs = 1;
172
        m_m->usage = 0;
5979 avox 173
}
174
 
20691 craig 175
ScFace::ScFace(ScFaceData* data) : m_m(data)
5979 avox 176
{
20691 craig 177
        ++(m_m->refs);
178
        m_m->m_cachedStatus = ScFace::UNKNOWN;
5979 avox 179
}
180
 
20691 craig 181
ScFace::ScFace(const ScFace& other) : m_m(other.m_m), m_replacedName(other.m_replacedName), m_replacedInDoc(other.m_replacedInDoc)
5979 avox 182
{
20691 craig 183
        ++(m_m->refs);
5979 avox 184
}
185
 
186
ScFace::~ScFace()
187
{
22599 craig 188
        if ( m_m && --(m_m->refs) == 0 )
189
        {
20691 craig 190
                m_m->unload();
191
                delete m_m;
22599 craig 192
                m_m = nullptr;
5979 avox 193
        }
194
}
195
 
196
ScFace& ScFace::operator=(const ScFace& other)
197
{
20691 craig 198
        if (m_m != other.m_m)
7147 jghali 199
        {
20691 craig 200
                if (other.m_m)
201
                        ++(other.m_m->refs);
22599 craig 202
                if ( m_m && --(m_m->refs) == 0 )
203
                {
20691 craig 204
                        m_m->unload();
205
                        delete m_m;
7147 jghali 206
                }
20691 craig 207
                m_m = other.m_m;
5979 avox 208
        }
20691 craig 209
        m_replacedName = other.m_replacedName;
5985 avox 210
        return *this;
5979 avox 211
}
212
 
213
/** two ScFaces are equal if they either are both NULLFACEs or they
214
agree on family, style, variant and fontpath
215
*/
216
bool ScFace::operator==(const ScFace& other) const
217
{
20691 craig 218
        return m_replacedName == other.m_replacedName &&
6733 avox 219
                ( (isNone() && other.isNone() )
20691 craig 220
                         || (m_m == other.m_m)
221
                         || (m_m->family == other.m_m->family
222
                                 && m_m->style == other.m_m->style
223
                                 && m_m->variant == other.m_m->variant
224
                                 && m_m->fontFile == other.m_m->fontFile
225
                                 && m_m-> faceIndex == other.m_m->faceIndex) );
5979 avox 226
}
227
 
228
const ScFace& ScFace::none()
229
{
230
        static ScFace NONE;
231
        return NONE;
232
}
233
 
21563 jghali 234
bool ScFace::isItalic() const
235
{
236
        if (m_m->status == ScFace::UNKNOWN) {
237
                m_m->load();
238
        }
239
        return m_m->isItalic();
240
}
241
 
242
bool ScFace::isBold() const
243
{
244
        if (m_m->status == ScFace::UNKNOWN) {
245
                m_m->load();
246
        }
247
        return m_m->isBold();
248
}
249
 
19360 jghali 250
bool ScFace::isSymbolic() const
251
{
20691 craig 252
        if (m_m->status == ScFace::UNKNOWN) {
253
                m_m->load();
19360 jghali 254
        }
20691 craig 255
        return m_m->isSymbolic();
19360 jghali 256
}
257
 
17778 jghali 258
QString ScFace::pdfAscentAsString() const
15364 jghali 259
{
20691 craig 260
        if (m_m->status == ScFace::UNKNOWN) {
261
                m_m->load();
15364 jghali 262
        }
20691 craig 263
        return m_m->pdfAscentAsString();
15364 jghali 264
}
265
 
17778 jghali 266
QString ScFace::pdfDescentAsString() const
15364 jghali 267
{
20691 craig 268
        if (m_m->status == ScFace::UNKNOWN) {
269
                m_m->load();
15364 jghali 270
        }
20691 craig 271
        return m_m->pdfDescentAsString();
15364 jghali 272
}
17778 jghali 273
QString ScFace::pdfCapHeightAsString() const
15364 jghali 274
{
20691 craig 275
        if (m_m->status == ScFace::UNKNOWN) {
276
                m_m->load();
15364 jghali 277
        }
20691 craig 278
        return m_m->pdfCapHeightAsString();
15364 jghali 279
}
280
 
17778 jghali 281
QString ScFace::pdfFontBBoxAsString() const
15364 jghali 282
{
20691 craig 283
        if (m_m->status == ScFace::UNKNOWN) {
284
                m_m->load();
15364 jghali 285
        }
20691 craig 286
        return m_m->pdfFontBBoxAsString();
15364 jghali 287
}
288
 
289
QString ScFace::italicAngleAsString() const
290
{
20691 craig 291
        if (m_m->status == ScFace::UNKNOWN) {
292
                m_m->load();
15364 jghali 293
        }
20691 craig 294
        return m_m->italicAngleAsString();
15364 jghali 295
}
296
 
13917 jghali 297
qreal ScFace::ascent(qreal sz) const
298
{
20691 craig 299
        if (m_m->status == ScFace::UNKNOWN) {
300
                m_m->load();
13917 jghali 301
        }
20691 craig 302
        return m_m->ascent(sz);
13917 jghali 303
}
5979 avox 304
 
13917 jghali 305
qreal ScFace::descent(qreal sz) const
306
{
20691 craig 307
        if (m_m->status == ScFace::UNKNOWN) {
308
                m_m->load();
13917 jghali 309
        }
20691 craig 310
        return m_m->descent(sz);
13917 jghali 311
}
312
qreal ScFace::xHeight(qreal sz) const
313
{
20691 craig 314
        if (m_m->status == ScFace::UNKNOWN) {
315
                m_m->load();
13917 jghali 316
        }
20691 craig 317
        return m_m->xHeight(sz);
13917 jghali 318
}
319
 
320
qreal ScFace::capHeight(qreal sz) const
321
{
20691 craig 322
        if (m_m->status == ScFace::UNKNOWN) {
323
                m_m->load();
13917 jghali 324
        }
20691 craig 325
        return m_m->capHeight(sz);
13917 jghali 326
}
327
 
328
qreal ScFace::height(qreal sz) const
329
{
20691 craig 330
        if (m_m->status == ScFace::UNKNOWN) {
331
                m_m->load();
13917 jghali 332
        }
20691 craig 333
        return m_m->height(sz);
13917 jghali 334
}
335
 
336
qreal ScFace::strikeoutPos(qreal sz) const
337
{
20691 craig 338
        if (m_m->status == ScFace::UNKNOWN) {
339
                m_m->load();
13917 jghali 340
        }
20691 craig 341
        return m_m->strikeoutPos(sz);
13917 jghali 342
}
343
 
344
qreal ScFace::underlinePos(qreal sz) const
345
{
20691 craig 346
        if (m_m->status == ScFace::UNKNOWN) {
347
                m_m->load();
13917 jghali 348
        }
20691 craig 349
        return m_m->underlinePos(sz);
13917 jghali 350
}
351
 
352
qreal ScFace::strokeWidth(qreal sz) const
353
{
20691 craig 354
        if (m_m->status == ScFace::UNKNOWN) {
355
                m_m->load();
13917 jghali 356
        }
20691 craig 357
        return m_m->strokeWidth(sz);
13917 jghali 358
}
359
 
360
qreal ScFace::maxAdvanceWidth(qreal sz) const
361
{
20691 craig 362
        if (m_m->status == ScFace::UNKNOWN) {
363
                m_m->load();
13917 jghali 364
        }
20691 craig 365
        return m_m->maxAdvanceWidth(sz);
13917 jghali 366
}
367
 
5979 avox 368
void ScFace::increaseUsage() const
369
{
20691 craig 370
        m_m->usage++;
5979 avox 371
}
372
 
373
void ScFace::decreaseUsage() const
374
{
20691 craig 375
        if (m_m->usage == 1)
5979 avox 376
                unload();
20691 craig 377
        m_m->usage--;
5979 avox 378
}
379
 
380
void ScFace::unload() const
381
{
20691 craig 382
        if (m_m->status >= ScFace::LOADED && usable()) {
383
                m_m->unload();
5979 avox 384
        }
385
        // clear caches
20691 craig 386
        m_m->m_glyphWidth.clear();
387
        m_m->m_glyphOutline.clear();
388
        m_m->status = ScFace::UNKNOWN;
5979 avox 389
}
390
 
391
 
21563 jghali 392
ScFace::gid_type ScFace::emulateGlyph(uint ch) const
6144 avox 393
{
23700 craig 394
        if (ch == SpecialChars::LINEBREAK || ch == SpecialChars::PARSEP
395
                || ch == SpecialChars::FRAMEBREAK || ch == SpecialChars::COLBREAK
6935 avox 396
                || ch == SpecialChars::TAB || ch == SpecialChars::SHYPHEN
22599 craig 397
                || ch == SpecialChars::ZWSPACE || ch == SpecialChars::ZWNBSPACE || ch==SpecialChars::OBJECT)
21563 jghali 398
                return CONTROL_GLYPHS + ch;
22599 craig 399
        if (ch == SpecialChars::NBSPACE)
21563 jghali 400
                return  m_m->char2CMap(' ');
23871 jghali 401
        if (ch == SpecialChars::NNBSPACE)
402
                return  m_m->char2CMap(0x2009); // Use thin space
22721 jghali 403
        if (ch == SpecialChars::NBHYPHEN)
21563 jghali 404
                return hyphenGlyph();
22599 craig 405
        return 0;
6144 avox 406
}
24466 jghali 407
 
21563 jghali 408
ScFace::gid_type ScFace::hyphenGlyph() const
409
{
410
        // Try the typographic hyphen first, then the hyphen-minus
411
        gid_type hyphen = m_m->char2CMap(0x2010);
412
        if (hyphen == 0)
413
                hyphen = m_m->char2CMap('-');
414
        return hyphen;
415
}
6144 avox 416
 
21563 jghali 417
ScFace::gid_type ScFace::hyphenGlyph(const CharStyle& style) const
418
{
419
        if (style.hyphenChar() == 0)
420
                return  SpecialChars::ZWNBSPACE.unicode() + ScFace::CONTROL_GLYPHS;
421
        if (style.hyphenChar() == 0x2010)
422
                return  hyphenGlyph();
423
        return  m_m->char2CMap(style.hyphenChar());
424
}
6144 avox 425
 
21563 jghali 426
double ScFace::hyphenWidth(const CharStyle& style, qreal size) const
5979 avox 427
{
21563 jghali 428
        if (style.hyphenChar() == 0)
429
                return 0;
430
        return glyphBBox(hyphenGlyph(style), size).width;
431
}
432
 
433
QStringList ScFace::fontFeatures() const
434
{
435
        return m_m->fontFeatures;
436
}
437
 
438
ScFace::gid_type ScFace::char2CMap(uint ch) const
439
{
22599 craig 440
        if (m_m->status == ScFace::UNKNOWN)
20691 craig 441
                m_m->load();
9042 avox 442
 
443
        if (ch == SpecialChars::SHYPHEN)
444
                return emulateGlyph(ch);
445
 
20691 craig 446
        gid_type gl = m_m->char2CMap(ch);
19104 jghali 447
 
6144 avox 448
        if (gl == 0)
449
                return emulateGlyph(ch);
22599 craig 450
        return gl;
5979 avox 451
}
452
 
24466 jghali 453
ScFace::cid_type ScFace::glyphIndexToCID(gid_type index) const
454
{
455
        if (m_m->status == ScFace::UNKNOWN)
456
                m_m->load();
5979 avox 457
 
24466 jghali 458
        cid_type cid = m_m->glyphIndexToCID(index);
459
        return cid;
460
}
461
 
5979 avox 462
bool ScFace::canRender(QChar ch) const
463
{
464
        if (!usable())
465
                return false;
22599 craig 466
        gid_type gl = char2CMap(ch.unicode());    //  calls load()
467
        if (gl >= CONTROL_GLYPHS)   //  those are always empty
468
                return true;
469
        if (gl != 0)
470
        {
471
                m_m->loadGlyph(gl);
472
                return !m_m->m_glyphOutline[gl].broken;
5979 avox 473
        }
22599 craig 474
        return false;
5979 avox 475
}
476
 
22144 jghali 477
bool ScFace::embedFont(QByteArray &str)
5979 avox 478
{
22599 craig 479
        if (m_m->status == ScFace::UNKNOWN)
20691 craig 480
                m_m->load();
22144 jghali 481
        return m_m->embedFont(str);
5979 avox 482
}
483
 
20103 avox 484
bool ScFace::glyphNames(FaceEncoding& gList)
5979 avox 485
{
22599 craig 486
        if (m_m->status == ScFace::UNKNOWN)
20691 craig 487
                m_m->load();
488
        return m_m->glyphNames(gList);
5979 avox 489
}
490
 
22144 jghali 491
void ScFace::rawData(QByteArray & bb)
5979 avox 492
{
22599 craig 493
        if (m_m->status == ScFace::UNKNOWN)
20691 craig 494
                m_m->load();
22144 jghali 495
        m_m->rawData(bb);
5979 avox 496
}
497
 
498
void ScFace::checkAllGlyphs()
499
{
22599 craig 500
        if (m_m->status == ScFace::UNKNOWN)
20691 craig 501
                m_m->load();
22599 craig 502
        if (m_m->status != ScFace::LOADED)
5979 avox 503
                return;
22599 craig 504
        for (gid_type gl=0; gl <= m_m->maxGlyph; ++gl)
505
        {
506
                if (! m_m->m_glyphWidth.contains(gl))
507
                {
20691 craig 508
                        m_m->loadGlyph(gl);
509
                        m_m->m_glyphWidth.remove(gl);
510
                        m_m->m_glyphOutline.remove(gl);
5979 avox 511
                }
512
        }
513
}