bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / fontsubset / ttcr.cxx
blob84a0375c808751812e1aeb51b37a9ca99166e134
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 * TrueTypeCreator method implementation
24 #include <iomanip>
25 #include <assert.h>
27 #include <sal/log.hxx>
29 #include "ttcr.hxx"
30 #include <string.h>
32 namespace vcl
36 * Private Data Types
39 struct TableEntry {
40 sal_uInt32 tag;
41 sal_uInt32 length;
42 sal_uInt8 *data;
45 /*- Data access macros for data stored in big-endian or little-endian format */
46 static sal_Int16 GetInt16( const sal_uInt8* ptr, sal_uInt32 offset)
48 assert(ptr != nullptr);
49 sal_Int16 t = (ptr+offset)[0] << 8 | (ptr+offset)[1];
50 return t;
53 static sal_uInt16 GetUInt16( const sal_uInt8* ptr, sal_uInt32 offset)
55 assert(ptr != nullptr);
56 sal_uInt16 t = (ptr+offset)[0] << 8 | (ptr+offset)[1];
57 return t;
60 static void PutInt16(sal_Int16 val, sal_uInt8 *ptr, sal_uInt32 offset)
62 assert(ptr != nullptr);
64 ptr[offset] = static_cast<sal_uInt8>((val >> 8) & 0xFF);
65 ptr[offset+1] = static_cast<sal_uInt8>(val & 0xFF);
68 static void PutUInt16(sal_uInt16 val, sal_uInt8 *ptr, sal_uInt32 offset)
70 assert(ptr != nullptr);
72 ptr[offset] = static_cast<sal_uInt8>((val >> 8) & 0xFF);
73 ptr[offset+1] = static_cast<sal_uInt8>(val & 0xFF);
76 static void PutUInt32(sal_uInt32 val, sal_uInt8 *ptr, sal_uInt32 offset)
78 assert(ptr != nullptr);
80 ptr[offset] = static_cast<sal_uInt8>((val >> 24) & 0xFF);
81 ptr[offset+1] = static_cast<sal_uInt8>((val >> 16) & 0xFF);
82 ptr[offset+2] = static_cast<sal_uInt8>((val >> 8) & 0xFF);
83 ptr[offset+3] = static_cast<sal_uInt8>(val & 0xFF);
86 static int TableEntryCompareF(const void *l, const void *r)
88 sal_uInt32 const ltag(static_cast<TableEntry const*>(l)->tag);
89 sal_uInt32 const rtag(static_cast<TableEntry const*>(r)->tag);
90 return (ltag == rtag) ? 0 : (ltag < rtag) ? -1 : 1;
93 namespace {
94 struct NameRecordCompareF
96 bool operator()(const NameRecord& l, const NameRecord& r) const
98 if (l.platformID != r.platformID) {
99 return l.platformID < r.platformID;
100 } else if (l.encodingID != r.encodingID) {
101 return l.encodingID < r.encodingID;
102 } else if (l.languageID != r.languageID) {
103 return l.languageID < r.languageID;
104 } else if (l.nameID != r.nameID) {
105 return l.nameID < r.nameID;
107 return false;
112 static sal_uInt32 CheckSum(sal_uInt32 *ptr, sal_uInt32 length)
114 sal_uInt32 sum = 0;
115 sal_uInt32 *endptr = ptr + ((length + 3) & sal_uInt32(~3)) / 4;
117 while (ptr < endptr) sum += *ptr++;
119 return sum;
123 * Public functions
126 TrueTypeCreator::TrueTypeCreator(sal_uInt32 _tag)
128 this->m_tag = _tag;
131 void TrueTypeCreator::AddTable(std::unique_ptr<TrueTypeTable> table)
133 if (table != nullptr) {
134 this->m_tables.push_back(std::move(table));
138 void TrueTypeCreator::RemoveTable(sal_uInt32 tableTag)
140 for (auto it = this->m_tables.begin(); it != this->m_tables.end(); )
142 if ((*it)->m_tag == tableTag)
144 it = this->m_tables.erase(it);
146 else
147 ++it;
151 SFErrCodes TrueTypeCreator::StreamToMemory(std::vector<sal_uInt8>& rOutBuffer)
153 sal_uInt16 searchRange=1, entrySelector=0, rangeShift;
154 sal_uInt32 s, offset, checkSumAdjustment = 0;
155 sal_uInt32 *p;
156 sal_uInt8 *head = nullptr; /* saved pointer to the head table data for checkSumAdjustment calculation */
158 if (this->m_tables.empty())
159 return SFErrCodes::TtFormat;
161 ProcessTables();
163 /* ProcessTables() adds 'loca' and 'hmtx' */
165 sal_uInt16 numTables = this->m_tables.size();
167 std::unique_ptr<TableEntry[]> te(new TableEntry[numTables]);
169 int teIdx = 0;
170 for (auto const & e : this->m_tables)
172 e->GetRawData(&te[teIdx]);
173 ++teIdx;
176 qsort(te.get(), numTables, sizeof(TableEntry), TableEntryCompareF);
178 do {
179 searchRange *= 2;
180 entrySelector++;
181 } while (searchRange <= numTables);
183 searchRange *= 8;
184 entrySelector--;
185 rangeShift = numTables * 16 - searchRange;
187 s = offset = 12 + 16 * numTables;
189 for (int i = 0; i < numTables; ++i) {
190 s += (te[i].length + 3) & sal_uInt32(~3);
191 /* if ((te[i].length & 3) != 0) s += (4 - (te[i].length & 3)) & 3; */
194 rOutBuffer.resize(s);
195 sal_uInt8* ttf = rOutBuffer.data();
197 /* Offset Table */
198 PutUInt32(this->m_tag, ttf, 0);
199 PutUInt16(numTables, ttf, 4);
200 PutUInt16(searchRange, ttf, 6);
201 PutUInt16(entrySelector, ttf, 8);
202 PutUInt16(rangeShift, ttf, 10);
204 /* Table Directory */
205 for (int i = 0; i < numTables; ++i) {
206 PutUInt32(te[i].tag, ttf + 12, 16 * i);
207 PutUInt32(CheckSum(reinterpret_cast<sal_uInt32 *>(te[i].data), te[i].length), ttf + 12, 16 * i + 4);
208 PutUInt32(offset, ttf + 12, 16 * i + 8);
209 PutUInt32(te[i].length, ttf + 12, 16 * i + 12);
211 if (te[i].tag == T_head) {
212 head = ttf + offset;
215 memcpy(ttf+offset, te[i].data, (te[i].length + 3) & sal_uInt32(~3) );
216 offset += (te[i].length + 3) & sal_uInt32(~3);
217 /* if ((te[i].length & 3) != 0) offset += (4 - (te[i].length & 3)) & 3; */
220 te.reset();
222 p = reinterpret_cast<sal_uInt32 *>(ttf);
223 for (int i = 0; i < static_cast<int>(s) / 4; ++i) checkSumAdjustment += p[i];
224 PutUInt32(0xB1B0AFBA - checkSumAdjustment, head, 8);
226 return SFErrCodes::Ok;
230 * TrueTypeTable private methods
233 /* Table data points to
234 * --------------------------------------------
235 * generic tdata_generic struct
236 * 'head' HEAD_Length bytes of memory
237 * 'hhea' HHEA_Length bytes of memory
238 * 'loca' tdata_loca struct
239 * 'maxp' MAXP_Version1Length bytes of memory
240 * 'glyf' list of GlyphData structs (defined in sft.h)
241 * 'name' list of NameRecord structs (defined in sft.h)
242 * 'post' tdata_post struct
246 #define CMAP_SUBTABLE_INIT 10
247 #define CMAP_SUBTABLE_INCR 10
249 namespace {
251 struct CmapSubTable {
252 sal_uInt32 id; /* subtable ID (platform/encoding ID) */
253 std::vector<std::pair<sal_uInt32, sal_uInt32>> mappings; /* character to glyph mapping array */
258 struct table_cmap {
259 sal_uInt32 n; /* number of used CMAP sub-tables */
260 sal_uInt32 m; /* number of allocated CMAP sub-tables */
261 std::unique_ptr<CmapSubTable[]> s; /* sorted array of sub-tables */
264 struct tdata_loca {
265 sal_uInt32 nbytes; /* number of bytes in loca table */
266 std::unique_ptr<sal_uInt8[]> ptr; /* pointer to the data */
269 /* allocate memory for a TT table */
270 static std::unique_ptr<sal_uInt8[]> ttmalloc(sal_uInt32 nbytes)
272 sal_uInt32 n = (nbytes + 3) & sal_uInt32(~3);
273 return std::make_unique<sal_uInt8[]>(n);
276 TrueTypeTable::~TrueTypeTable() {}
278 TrueTypeTableGeneric::~TrueTypeTableGeneric()
282 TrueTypeTableHead::~TrueTypeTableHead()
286 TrueTypeTableHhea::~TrueTypeTableHhea()
290 TrueTypeTableLoca::~TrueTypeTableLoca()
294 TrueTypeTableMaxp::~TrueTypeTableMaxp()
298 TrueTypeTableGlyf::~TrueTypeTableGlyf()
302 TrueTypeTableCmap::~TrueTypeTableCmap()
306 TrueTypeTableName::~TrueTypeTableName()
310 TrueTypeTablePost::~TrueTypeTablePost()
312 if (m_format == 0x00030000) {
313 /* do nothing */
314 } else {
315 SAL_WARN("vcl.fonts", "Unsupported format of a 'post' table: "
316 << std::setfill('0')
317 << std::setw(8)
318 << std::hex
319 << std::uppercase
320 << static_cast<int>(m_format) << ".");
325 int TrueTypeTableGeneric::GetRawData(TableEntry* te)
327 te->data = this->m_ptr.get();
328 te->length = this->m_nbytes;
329 te->tag = this->m_tag;
331 return TTCR_OK;
334 int TrueTypeTableHead::GetRawData(TableEntry* te)
336 te->length = HEAD_Length;
337 te->data = this->m_head.get();
338 te->tag = T_head;
340 return TTCR_OK;
343 int TrueTypeTableHhea::GetRawData(TableEntry* te)
345 te->length = HHEA_Length;
346 te->data = this->m_hhea.get();
347 te->tag = T_hhea;
349 return TTCR_OK;
352 int TrueTypeTableLoca::GetRawData(TableEntry* te)
354 assert(this->m_loca != nullptr);
356 if (m_loca->nbytes == 0) return TTCR_ZEROGLYPHS;
358 te->data = m_loca->ptr.get();
359 te->length = m_loca->nbytes;
360 te->tag = T_loca;
362 return TTCR_OK;
365 int TrueTypeTableMaxp::GetRawData(TableEntry* te)
367 te->length = MAXP_Version1Length;
368 te->data = this->m_maxp.get();
369 te->tag = T_maxp;
371 return TTCR_OK;
374 int TrueTypeTableGlyf::GetRawData(TableEntry* te)
376 sal_uInt32 n, nbytes = 0;
377 /* sal_uInt16 curID = 0; */ /* to check if glyph IDs are sequential and start from zero */
379 te->data = nullptr;
380 te->length = 0;
381 te->tag = 0;
383 if (m_list.size() == 0) return TTCR_ZEROGLYPHS;
385 for (const std::unique_ptr<GlyphData>& pGlyph : m_list)
387 /* if (((GlyphData *) listCurrent(l))->glyphID != curID++) return TTCR_GLYPHSEQ; */
388 nbytes += pGlyph->nbytes;
391 m_rawdata = ttmalloc(nbytes);
393 auto p = m_rawdata.get();
394 for (const std::unique_ptr<GlyphData>& pGlyph : m_list)
396 n = pGlyph->nbytes;
397 if (n != 0) {
398 memcpy(p, pGlyph->ptr.get(), n);
399 p += n;
403 te->length = nbytes;
404 te->data = m_rawdata.get();
405 te->tag = T_glyf;
407 return TTCR_OK;
410 /* cmap packers */
411 static std::unique_ptr<sal_uInt8[]> PackCmapType0(CmapSubTable const *s, sal_uInt32 *length)
413 std::unique_ptr<sal_uInt8[]> ptr(new sal_uInt8[262]);
414 sal_uInt8 *p = ptr.get() + 6;
416 PutUInt16(0, ptr.get(), 0);
417 PutUInt16(262, ptr.get(), 2);
418 PutUInt16(0, ptr.get(), 4);
420 for (sal_uInt32 i = 0; i < 256; i++) {
421 sal_uInt16 g = 0;
422 for (const auto& [ch, glyph] : s->mappings) {
423 if (ch == i) {
424 g = static_cast<sal_uInt16>(glyph);
427 p[i] = static_cast<sal_uInt8>(g);
429 *length = 262;
430 return ptr;
433 static std::unique_ptr<sal_uInt8[]> PackCmapType6(CmapSubTable const *s, sal_uInt32 *length)
435 std::unique_ptr<sal_uInt8[]> ptr(new sal_uInt8[s->mappings.size()*2 + 10]);
436 sal_uInt8 *p = ptr.get() + 10;
438 PutUInt16(6, ptr.get(), 0);
439 PutUInt16(static_cast<sal_uInt16>(s->mappings.size()*2+10), ptr.get(), 2);
440 PutUInt16(0, ptr.get(), 4);
441 PutUInt16(0, ptr.get(), 6);
442 PutUInt16(static_cast<sal_uInt16>(s->mappings.size()), ptr.get(), 8 );
444 for (size_t i = 0; i < s->mappings.size(); i++) {
445 sal_uInt16 g = 0;
446 for (const auto& [ch, glyph] : s->mappings) {
447 if (ch == i) {
448 g = static_cast<sal_uInt16>(glyph);
451 PutUInt16( g, p, 2*i );
453 *length = s->mappings.size()*2+10;
454 return ptr;
457 /* XXX it only handles Format 0 encoding tables */
458 static std::unique_ptr<sal_uInt8[]> PackCmap(CmapSubTable const *s, sal_uInt32 *length)
460 if (s->mappings.back().second > 0xff)
461 return PackCmapType6(s, length);
462 else
463 return PackCmapType0(s, length);
466 int TrueTypeTableCmap::GetRawData(TableEntry* te)
468 sal_uInt32 i;
469 sal_uInt32 tlen = 0;
470 sal_uInt32 l;
471 sal_uInt32 cmapsize;
472 sal_uInt8 *cmap;
473 sal_uInt32 coffset;
475 assert(m_cmap);
476 assert(m_cmap->n != 0);
478 std::unique_ptr<std::unique_ptr<sal_uInt8[]>[]> subtables(new std::unique_ptr<sal_uInt8[]>[m_cmap->n]);
479 std::unique_ptr<sal_uInt32[]> sizes(new sal_uInt32[m_cmap->n]);
481 for (i = 0; i < m_cmap->n; i++) {
482 subtables[i] = PackCmap(m_cmap->s.get()+i, &l);
483 sizes[i] = l;
484 tlen += l;
487 cmapsize = tlen + 4 + 8 * m_cmap->n;
488 this->m_rawdata = ttmalloc(cmapsize);
489 cmap = this->m_rawdata.get();
491 PutUInt16(0, cmap, 0);
492 PutUInt16(static_cast<sal_uInt16>(m_cmap->n), cmap, 2);
493 coffset = 4 + m_cmap->n * 8;
495 for (i = 0; i < m_cmap->n; i++) {
496 PutUInt16(static_cast<sal_uInt16>(m_cmap->s[i].id >> 16), cmap + 4, i * 8);
497 PutUInt16(static_cast<sal_uInt16>(m_cmap->s[i].id & 0xFF), cmap + 4, 2 + i * 8);
498 PutUInt32(coffset, cmap + 4, 4 + i * 8);
499 memcpy(cmap + coffset, subtables[i].get(), sizes[i]);
500 subtables[i].reset();
501 coffset += sizes[i];
504 subtables.reset();
505 sizes.reset();
507 te->data = cmap;
508 te->length = cmapsize;
509 te->tag = T_cmap;
511 return TTCR_OK;
514 int TrueTypeTableName::GetRawData(TableEntry* te)
516 sal_Int16 i=0, n; /* number of Name Records */
517 int stringLen = 0;
518 sal_uInt8 *p1, *p2;
520 te->data = nullptr;
521 te->length = 0;
522 te->tag = 0;
524 if ((n = static_cast<sal_Int16>(m_list.size())) == 0) return TTCR_NONAMES;
526 std::vector<NameRecord> nr = m_list;
528 for (const NameRecord & rName : m_list)
529 stringLen += rName.sptr.size();
531 if (stringLen > 65535) {
532 return TTCR_NAMETOOLONG;
535 std::sort(nr.begin(), nr.end(), NameRecordCompareF());
537 int nameLen = stringLen + 12 * n + 6;
538 std::unique_ptr<sal_uInt8[]> name = ttmalloc(nameLen);
540 PutUInt16(0, name.get(), 0);
541 PutUInt16(n, name.get(), 2);
542 PutUInt16(static_cast<sal_uInt16>(6 + 12 * n), name.get(), 4);
544 p1 = name.get() + 6;
545 p2 = p1 + 12 * n;
547 for (i = 0; i < n; i++) {
548 PutUInt16(nr[i].platformID, p1, 0);
549 PutUInt16(nr[i].encodingID, p1, 2);
550 PutUInt16(static_cast<sal_uInt16>(nr[i].languageID), p1, 4);
551 PutUInt16(nr[i].nameID, p1, 6);
552 PutUInt16(nr[i].sptr.size(), p1, 8);
553 PutUInt16(static_cast<sal_uInt16>(p2 - (name.get() + 6 + 12 * n)), p1, 10);
554 if (nr[i].sptr.size()) {
555 memcpy(p2, nr[i].sptr.data(), nr[i].sptr.size());
557 /* {int j; for(j=0; j<nr[i].slen; j++) printf("%c", nr[i].sptr[j]); printf("\n"); }; */
558 p2 += nr[i].sptr.size();
559 p1 += 12;
562 nr.clear();
563 this->m_rawdata = std::move(name);
565 te->data = this->m_rawdata.get();
566 te->length = static_cast<sal_uInt16>(nameLen);
567 te->tag = T_name;
569 /*{int j; for(j=0; j<nameLen; j++) printf("%c", name[j]); }; */
571 return TTCR_OK;
574 int TrueTypeTablePost::GetRawData(TableEntry* te)
576 std::unique_ptr<sal_uInt8[]> post;
577 sal_uInt32 postLen = 0;
578 int ret;
580 this->m_rawdata.reset();
582 if (m_format == 0x00030000) {
583 postLen = 32;
584 post = ttmalloc(postLen);
585 PutUInt32(0x00030000, post.get(), 0);
586 PutUInt32(m_italicAngle, post.get(), 4);
587 PutUInt16(m_underlinePosition, post.get(), 8);
588 PutUInt16(m_underlineThickness, post.get(), 10);
589 PutUInt16(static_cast<sal_uInt16>(m_isFixedPitch), post.get(), 12);
590 ret = TTCR_OK;
591 } else {
592 SAL_WARN("vcl.fonts", "Unrecognized format of a post table: "
593 << std::setfill('0')
594 << std::setw(8)
595 << std::hex
596 << std::uppercase
597 << static_cast<int>(m_format) << ".");
598 ret = TTCR_POSTFORMAT;
601 this->m_rawdata = std::move(post);
602 te->data = this->m_rawdata.get();
603 te->length = postLen;
604 te->tag = T_post;
606 return ret;
610 * TrueTypeTable public methods
613 /* Note: Type42 fonts only need these tables:
614 * head, hhea, loca, maxp, cvt, prep, glyf, hmtx, fpgm
616 * Microsoft required tables
617 * cmap, glyf, head, hhea, hmtx, loca, maxp, name, post, OS/2
619 * Apple required tables
620 * cmap, glyf, head, hhea, hmtx, loca, maxp, name, post
624 TrueTypeTableGeneric::TrueTypeTableGeneric(sal_uInt32 tag,
625 sal_uInt32 nbytes,
626 const sal_uInt8* ptr)
627 : TrueTypeTable(tag),
628 m_nbytes(nbytes)
630 if (nbytes) {
631 m_ptr = ttmalloc(nbytes);
632 memcpy(m_ptr.get(), ptr, nbytes);
636 TrueTypeTableGeneric::TrueTypeTableGeneric(sal_uInt32 tag,
637 sal_uInt32 nbytes,
638 std::unique_ptr<sal_uInt8[]> ptr)
639 : TrueTypeTable(tag),
640 m_nbytes(nbytes)
642 if (nbytes) {
643 m_ptr = std::move(ptr);
647 TrueTypeTableHead::TrueTypeTableHead(sal_uInt32 fontRevision,
648 sal_uInt16 flags,
649 sal_uInt16 unitsPerEm,
650 const sal_uInt8* created,
651 sal_uInt16 macStyle,
652 sal_uInt16 lowestRecPPEM,
653 sal_Int16 fontDirectionHint)
654 : TrueTypeTable(T_head)
655 , m_head(ttmalloc(HEAD_Length))
657 assert(created != nullptr);
659 sal_uInt8* ptr = m_head.get();
661 PutUInt32(0x00010000, ptr, 0); /* version */
662 PutUInt32(fontRevision, ptr, 4);
663 PutUInt32(0x5F0F3CF5, ptr, 12); /* magic number */
664 PutUInt16(flags, ptr, 16);
665 PutUInt16(unitsPerEm, ptr, 18);
666 memcpy(ptr+20, created, 8); /* Created Long Date */
667 memset(ptr+28, 0, 8); /* Modified Long Date */
668 PutUInt16(macStyle, ptr, 44);
669 PutUInt16(lowestRecPPEM, ptr, 46);
670 PutUInt16(fontDirectionHint, ptr, 48);
671 PutUInt16(0, ptr, 52); /* glyph data format: 0 */
674 TrueTypeTableHhea::TrueTypeTableHhea(sal_Int16 ascender,
675 sal_Int16 descender,
676 sal_Int16 linegap,
677 sal_Int16 caretSlopeRise,
678 sal_Int16 caretSlopeRun)
679 : TrueTypeTable(T_hhea),
680 m_hhea(ttmalloc(HHEA_Length))
682 sal_uInt8* ptr = m_hhea.get();
684 PutUInt32(0x00010000, ptr, 0); /* version */
685 PutUInt16(ascender, ptr, 4);
686 PutUInt16(descender, ptr, 6);
687 PutUInt16(linegap, ptr, 8);
688 PutUInt16(caretSlopeRise, ptr, 18);
689 PutUInt16(caretSlopeRun, ptr, 20);
690 PutUInt16(0, ptr, 22); /* reserved 1 */
691 PutUInt16(0, ptr, 24); /* reserved 2 */
692 PutUInt16(0, ptr, 26); /* reserved 3 */
693 PutUInt16(0, ptr, 28); /* reserved 4 */
694 PutUInt16(0, ptr, 30); /* reserved 5 */
695 PutUInt16(0, ptr, 32); /* metricDataFormat */
698 TrueTypeTableLoca::TrueTypeTableLoca()
699 : TrueTypeTable(T_loca),
700 m_loca(new tdata_loca)
702 this->m_loca->nbytes = 0;
703 this->m_loca->ptr = nullptr;
706 TrueTypeTableMaxp::TrueTypeTableMaxp( const sal_uInt8* maxp, int size)
707 : TrueTypeTable(T_maxp)
709 this->m_maxp = ttmalloc(MAXP_Version1Length);
711 if (maxp && size == MAXP_Version1Length) {
712 memcpy(this->m_maxp.get(), maxp, MAXP_Version1Length);
716 TrueTypeTableGlyf::TrueTypeTableGlyf()
717 : TrueTypeTable(T_glyf)
721 TrueTypeTableCmap::TrueTypeTableCmap()
722 : TrueTypeTable(T_cmap)
723 , m_cmap(new table_cmap)
725 m_cmap->n = 0;
726 m_cmap->m = CMAP_SUBTABLE_INIT;
727 m_cmap->s.reset(new CmapSubTable[CMAP_SUBTABLE_INIT]);
730 TrueTypeTableName::TrueTypeTableName(std::vector<NameRecord> nr)
731 : TrueTypeTable(T_name)
732 , m_list(std::move(nr))
736 TrueTypeTablePost::TrueTypeTablePost(sal_Int32 format,
737 sal_Int32 italicAngle,
738 sal_Int16 underlinePosition,
739 sal_Int16 underlineThickness,
740 sal_uInt32 isFixedPitch)
741 : TrueTypeTable(T_post)
743 assert(format == 0x00030000); /* Only format 3.0 is supported at this time */
745 m_format = format;
746 m_italicAngle = italicAngle;
747 m_underlinePosition = underlinePosition;
748 m_underlineThickness = underlineThickness;
749 m_isFixedPitch = isFixedPitch;
752 void TrueTypeTableCmap::cmapAdd(sal_uInt32 id, sal_uInt32 c, sal_uInt32 g)
754 sal_uInt32 i, found;
755 CmapSubTable *s;
757 assert(m_cmap);
758 s = m_cmap->s.get(); assert(s != nullptr);
760 found = 0;
762 for (i = 0; i < m_cmap->n; i++) {
763 if (s[i].id == id) {
764 found = 1;
765 break;
769 if (!found) {
770 if (m_cmap->n == m_cmap->m) {
771 std::unique_ptr<CmapSubTable[]> tmp(new CmapSubTable[m_cmap->m + CMAP_SUBTABLE_INCR]);
772 for (sal_uInt32 j = 0; j != m_cmap->m; ++j) {
773 tmp[j] = std::move(s[j]);
775 m_cmap->m += CMAP_SUBTABLE_INCR;
776 s = tmp.get();
777 m_cmap->s = std::move(tmp);
780 for (i = 0; i < m_cmap->n; i++) {
781 if (s[i].id > id) break;
784 if (i < m_cmap->n) {
785 for (sal_uInt32 j = m_cmap->n; j != i; --j) {
786 s[j + 1] = std::move(s[j]);
790 m_cmap->n++;
792 s[i].id = id;
795 s[i].mappings.emplace_back(c, g);
798 sal_uInt32 TrueTypeTableGlyf::glyfAdd(std::unique_ptr<GlyphData> glyphdata, AbstractTrueTypeFont *fnt)
800 sal_uInt32 currentID;
801 int ret, n, ncomponents;
803 if (!glyphdata) return sal_uInt32(~0);
805 std::vector< sal_uInt32 > glyphlist;
807 ncomponents = GetTTGlyphComponents(fnt, glyphdata->glyphID, glyphlist);
809 if (m_list.size() > 0) {
810 ret = n = m_list.back()->newID + 1;
811 } else {
812 ret = n = 0;
814 glyphdata->newID = n++;
815 m_list.push_back(std::move(glyphdata));
817 if (ncomponents > 1 && glyphlist.size() > 1 )
819 std::vector< sal_uInt32 >::const_iterator it = glyphlist.begin();
820 ++it;
821 /* glyphData->glyphID is always the first glyph on the list */
824 int found = 0;
825 currentID = *it;
826 /* XXX expensive! should be rewritten with sorted arrays! */
827 for (const std::unique_ptr<GlyphData>& pGlyph : m_list)
829 if (pGlyph->glyphID == currentID) {
830 found = 1;
831 break;
835 if (!found) {
836 std::unique_ptr<GlyphData> gd = GetTTRawGlyphData(fnt, currentID);
837 gd->newID = n++;
838 m_list.push_back(std::move(gd));
840 } while( ++it != glyphlist.end() );
843 return ret;
846 TrueTypeTable *TrueTypeCreator::FindTable(sal_uInt32 tableTag)
848 for (const std::unique_ptr<TrueTypeTable>& p : this->m_tables)
849 if (p->m_tag == tableTag) {
850 return p.get();
853 return nullptr;
856 /* This function processes all the tables and synchronizes them before creating
857 * the output TrueType stream.
859 * *** It adds two TrueType tables to the font: 'loca' and 'hmtx' ***
861 * It does:
863 * - Re-numbers glyph IDs and creates 'glyf', 'loca', and 'hmtx' tables.
864 * - Calculates xMin, yMin, xMax, and yMax and stores values in 'head' table.
865 * - Stores indexToLocFormat in 'head'
866 * - updates 'maxp' table
867 * - Calculates advanceWidthMax, minLSB, minRSB, xMaxExtent and numberOfHMetrics
868 * in 'hhea' table
871 void TrueTypeCreator::ProcessTables()
873 TrueTypeTableHhea *hhea = nullptr;
874 TrueTypeTableMaxp *maxp = nullptr;
875 TrueTypeTableHead *head = nullptr;
876 std::unique_ptr<TrueTypeTableLoca> loca;
877 TrueTypeTableGlyf *glyf = nullptr;
878 sal_uInt32 nGlyphs, locaLen = 0, glyfLen = 0;
879 sal_Int16 xMin = 0, yMin = 0, xMax = 0, yMax = 0;
880 sal_uInt32 i = 0;
881 sal_Int16 indexToLocFormat;
882 std::unique_ptr<sal_uInt8[]> hmtxPtr;
883 sal_uInt8 *hheaPtr;
884 sal_uInt32 hmtxSize;
885 sal_uInt8 *p1, *p2;
886 sal_uInt16 maxPoints = 0, maxContours = 0, maxCompositePoints = 0, maxCompositeContours = 0;
887 int nlsb = 0;
888 std::unique_ptr<sal_uInt32[]> gid; /* array of old glyphIDs */
890 glyf = static_cast<TrueTypeTableGlyf*>(FindTable(T_glyf));
891 std::vector<std::unique_ptr<GlyphData>>& glyphlist = glyf->m_list;
892 nGlyphs = glyphlist.size();
893 if (!nGlyphs)
895 SAL_WARN("vcl.fonts", "no glyphs found in ProcessTables");
896 return;
898 gid.reset(new sal_uInt32[nGlyphs]);
900 RemoveTable(T_loca);
901 RemoveTable(T_hmtx);
903 /* XXX Need to make sure that composite glyphs do not break during glyph renumbering */
905 for (const std::unique_ptr<GlyphData>& gd : glyphlist)
907 glyfLen += gd->nbytes;
908 /* XXX if (gd->nbytes & 1) glyfLen++; */
910 assert(gd->newID == i);
911 gid[i++] = gd->glyphID;
912 /* gd->glyphID = i++; */
914 /* printf("IDs: %d %d.\n", gd->glyphID, gd->newID); */
916 if (gd->nbytes >= 10) {
917 sal_Int16 z = GetInt16(gd->ptr.get(), 2);
918 if (z < xMin) xMin = z;
920 z = GetInt16(gd->ptr.get(), 4);
921 if (z < yMin) yMin = z;
923 z = GetInt16(gd->ptr.get(), 6);
924 if (z > xMax) xMax = z;
926 z = GetInt16(gd->ptr.get(), 8);
927 if (z > yMax) yMax = z;
930 if (!gd->compflag) { /* non-composite glyph */
931 if (gd->npoints > maxPoints) maxPoints = gd->npoints;
932 if (gd->ncontours > maxContours) maxContours = gd->ncontours;
933 } else { /* composite glyph */
934 if (gd->npoints > maxCompositePoints) maxCompositePoints = gd->npoints;
935 if (gd->ncontours > maxCompositeContours) maxCompositeContours = gd->ncontours;
940 indexToLocFormat = (glyfLen / 2 > 0xFFFF) ? 1 : 0;
941 locaLen = indexToLocFormat ? (nGlyphs + 1) << 2 : (nGlyphs + 1) << 1;
943 std::unique_ptr<sal_uInt8[]> glyfPtr = ttmalloc(glyfLen);
944 std::unique_ptr<sal_uInt8[]> locaPtr = ttmalloc(locaLen);
945 std::unique_ptr<TTSimpleGlyphMetrics[]> met(new TTSimpleGlyphMetrics[nGlyphs]);
946 i = 0;
948 p1 = glyfPtr.get();
949 p2 = locaPtr.get();
950 for (const std::unique_ptr<GlyphData>& gd : glyphlist)
952 if (gd->compflag && gd->nbytes > 10) { /* re-number all components */
953 sal_uInt16 flags, index;
954 sal_uInt8 *ptr = gd->ptr.get() + 10;
955 size_t nRemaining = gd->nbytes - 10;
956 do {
957 if (nRemaining < 4)
959 SAL_WARN("vcl.fonts", "truncated font");
960 break;
962 flags = GetUInt16(ptr, 0);
963 index = GetUInt16(ptr, 2);
965 /* XXX use the sorted array of old to new glyphID mapping and do a binary search */
966 sal_uInt32 j;
967 for (j = 0; j < nGlyphs; j++) {
968 if (gid[j] == index) {
969 break;
972 /* printf("X: %d -> %d.\n", index, j); */
974 PutUInt16(static_cast<sal_uInt16>(j), ptr, 2);
976 ptr += 4;
977 nRemaining -= 4;
979 sal_uInt32 nAdvance = 0;
980 if (flags & ARG_1_AND_2_ARE_WORDS) {
981 nAdvance += 4;
982 } else {
983 nAdvance += 2;
986 if (flags & WE_HAVE_A_SCALE) {
987 nAdvance += 2;
988 } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
989 nAdvance += 4;
990 } else if (flags & WE_HAVE_A_TWO_BY_TWO) {
991 nAdvance += 8;
994 if (nRemaining < nAdvance)
996 SAL_WARN("vcl.fonts", "truncated font");
997 break;
1000 ptr += nAdvance;
1001 nRemaining -= nAdvance;
1003 } while (flags & MORE_COMPONENTS);
1006 if (gd->nbytes != 0) {
1007 memcpy(p1, gd->ptr.get(), gd->nbytes);
1009 if (indexToLocFormat == 1) {
1010 PutUInt32(p1 - glyfPtr.get(), p2, 0);
1011 p2 += 4;
1012 } else {
1013 PutUInt16(static_cast<sal_uInt16>((p1 - glyfPtr.get()) >> 1), p2, 0);
1014 p2 += 2;
1016 p1 += gd->nbytes;
1018 /* fill the array of metrics */
1019 met[i].adv = gd->aw;
1020 met[i].sb = gd->lsb;
1021 i++;
1024 gid.reset();
1026 if (indexToLocFormat == 1) {
1027 PutUInt32(p1 - glyfPtr.get(), p2, 0);
1028 } else {
1029 PutUInt16(static_cast<sal_uInt16>((p1 - glyfPtr.get()) >> 1), p2, 0);
1032 glyf->m_rawdata = std::move(glyfPtr);
1034 loca.reset(new TrueTypeTableLoca());
1035 loca->m_loca->ptr = std::move(locaPtr);
1036 loca->m_loca->nbytes = locaLen;
1038 AddTable(std::move(loca));
1040 head = static_cast<TrueTypeTableHead*>(FindTable(T_head));
1041 sal_uInt8* const pHeadData = head->m_head.get();
1042 PutInt16(xMin, pHeadData, HEAD_xMin_offset);
1043 PutInt16(yMin, pHeadData, HEAD_yMin_offset);
1044 PutInt16(xMax, pHeadData, HEAD_xMax_offset);
1045 PutInt16(yMax, pHeadData, HEAD_yMax_offset);
1046 PutInt16(indexToLocFormat, pHeadData, HEAD_indexToLocFormat_offset);
1048 maxp = static_cast<TrueTypeTableMaxp*>(FindTable(T_maxp));
1050 sal_uInt8* const pMaxpData = maxp->m_maxp.get();
1051 PutUInt16(static_cast<sal_uInt16>(nGlyphs), pMaxpData, MAXP_numGlyphs_offset);
1052 PutUInt16(maxPoints, pMaxpData, MAXP_maxPoints_offset);
1053 PutUInt16(maxContours, pMaxpData, MAXP_maxContours_offset);
1054 PutUInt16(maxCompositePoints, pMaxpData, MAXP_maxCompositePoints_offset);
1055 PutUInt16(maxCompositeContours, pMaxpData, MAXP_maxCompositeContours_offset);
1058 * Generate an htmx table and update hhea table
1060 hhea = static_cast<TrueTypeTableHhea*>(FindTable(T_hhea)); assert(hhea != nullptr);
1061 hheaPtr = hhea->m_hhea.get();
1062 if (nGlyphs > 2) {
1063 for (i = nGlyphs - 1; i > 0; i--) {
1064 if (met[i].adv != met[i-1].adv) break;
1066 nlsb = nGlyphs - 1 - i;
1068 hmtxSize = (nGlyphs - nlsb) * 4 + nlsb * 2;
1069 hmtxPtr = ttmalloc(hmtxSize);
1070 p1 = hmtxPtr.get();
1072 for (i = 0; i < nGlyphs; i++) {
1073 if (i < nGlyphs - nlsb) {
1074 PutUInt16(met[i].adv, p1, 0);
1075 PutUInt16(met[i].sb, p1, 2);
1076 p1 += 4;
1077 } else {
1078 PutUInt16(met[i].sb, p1, 0);
1079 p1 += 2;
1083 AddTable(std::make_unique<TrueTypeTableGeneric>(T_hmtx, hmtxSize, std::move(hmtxPtr)));
1084 PutUInt16(static_cast<sal_uInt16>(nGlyphs - nlsb), hheaPtr, 34);
1088 * TrueTypeCreator destructor. It calls destructors for all TrueTypeTables added to it.
1090 TrueTypeCreator::~TrueTypeCreator()
1094 } // namespace vcl
1096 #ifdef TEST_TTCR
1097 static sal_uInt32 mkTag(sal_uInt8 a, sal_uInt8 b, sal_uInt8 c, sal_uInt8 d) {
1098 return (a << 24) | (b << 16) | (c << 8) | d;
1101 int main()
1103 TrueTypeCreator *ttcr;
1104 sal_uInt8 *t1, *t2, *t3, *t4, *t5, *t6;
1106 TrueTypeCreatorNewEmpty(mkTag('t','r','u','e'), &ttcr);
1108 t1 = malloc(1000); memset(t1, 'a', 1000);
1109 t2 = malloc(2000); memset(t2, 'b', 2000);
1110 t3 = malloc(3000); memset(t3, 'c', 3000);
1111 t4 = malloc(4000); memset(t4, 'd', 4000);
1112 t5 = malloc(5000); memset(t5, 'e', 5000);
1113 t6 = malloc(6000); memset(t6, 'f', 6000);
1115 AddTable(ttcr, TrueTypeTableNew(T_maxp, 1000, t1));
1116 AddTable(ttcr, TrueTypeTableNew(T_OS2, 2000, t2));
1117 AddTable(ttcr, TrueTypeTableNew(T_cmap, 3000, t3));
1118 AddTable(ttcr, TrueTypeTableNew(T_loca, 4000, t4));
1119 AddTable(ttcr, TrueTypeTableNew(T_hhea, 5000, t5));
1120 AddTable(ttcr, TrueTypeTableNew(T_glyf, 6000, t6));
1122 free(t1);
1123 free(t2);
1124 free(t3);
1125 free(t4);
1126 free(t5);
1127 free(t6);
1129 StreamToFile(ttcr, "ttcrout.ttf");
1131 TrueTypeCreatorDispose(ttcr);
1132 return 0;
1134 #endif
1136 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */