r24469 by jghali - Detect harfbuzz-subset library availability
scribus-commit
scribus-commit at lists.scribus.net
Thu Feb 11 19:12:18 UTC 2021
Author: jghali
Date: Thu Feb 11 19:12:18 2021
New Revision: 24469
URL: http://scribus.net/websvn/listing.php?repname=Scribus&sc=1&rev=24469
Log:
Detect harfbuzz-subset library availability
Modified:
trunk/Scribus/CMakeLists_Dependencies.cmake
trunk/Scribus/config.h.cmake
trunk/Scribus/scribus/CMakeLists.txt
trunk/Scribus/scribus/fonts/sfnt.cpp
trunk/Scribus/win32/msvc2015/win-config.h
trunk/Scribus/win32/msvc2017/win-config.h
trunk/Scribus/win32/msvc2019/win-config.h
Modified: trunk/Scribus/CMakeLists_Dependencies.cmake
URL: http://scribus.net/websvn/diff.php?repname=Scribus&rev=24469&path=/trunk/Scribus/CMakeLists_Dependencies.cmake
==============================================================================
--- trunk/Scribus/CMakeLists_Dependencies.cmake (original)
+++ trunk/Scribus/CMakeLists_Dependencies.cmake Thu Feb 11 19:12:18 2021
@@ -298,3 +298,9 @@
# CTL support
pkg_check_modules(HARFBUZZ REQUIRED harfbuzz>=0.9.42 harfbuzz-icu)
pkg_check_modules(ICU REQUIRED icu-uc)
+
+# OpenType subsetting support
+pkg_check_modules(HARFBUZZ_SUBSET harfbuzz-subset>=2.0.0)
+if (HARFBUZZ_SUBSET_FOUND)
+ set (HAVE_HARFBUZZ_SUBSET ON)
+endif()
Modified: trunk/Scribus/config.h.cmake
URL: http://scribus.net/websvn/diff.php?repname=Scribus&rev=24469&path=/trunk/Scribus/config.h.cmake
==============================================================================
--- trunk/Scribus/config.h.cmake (original)
+++ trunk/Scribus/config.h.cmake Thu Feb 11 19:12:18 2021
@@ -23,6 +23,7 @@
#cmakedefine HAVE_TIFF 1
#cmakedefine HAVE_LIBZ 1
#cmakedefine HAVE_FONTCONFIG 1
+#cmakedefine HAVE_HARFBUZZ_SUBSET 1
#cmakedefine HAVE_PODOFO 1
#cmakedefine HAVE_POPPLER 1
#cmakedefine HAVE_OSG 1
Modified: trunk/Scribus/scribus/CMakeLists.txt
URL: http://scribus.net/websvn/diff.php?repname=Scribus&rev=24469&path=/trunk/Scribus/scribus/CMakeLists.txt
==============================================================================
--- trunk/Scribus/scribus/CMakeLists.txt (original)
+++ trunk/Scribus/scribus/CMakeLists.txt Thu Feb 11 19:12:18 2021
@@ -28,6 +28,7 @@
${CMAKE_CURRENT_SOURCE_DIR}
${FREETYPE_INCLUDE_DIRS}
${HARFBUZZ_INCLUDE_DIRS}
+ ${HARFBUZZ_SUBSET_INCLUDE_DIRS}
)
include(api/CMakeLists.txt)
@@ -1143,6 +1144,7 @@
${CMAKE_CURRENT_BINARY_DIR}/third_party/rtf-qt
${CMAKE_CURRENT_BINARY_DIR}/third_party/zip
${HARFBUZZ_LIBRARY_DIRS}
+ ${HARFBUZZ_SUBSET_LIBRARY_DIRS}
${ICU_LIBRARY_DIRS}
)
@@ -1316,6 +1318,7 @@
${CUPS_LIBRARIES}
${FREETYPE_LIBRARIES}
${HARFBUZZ_LIBRARIES}
+ ${HARFBUZZ_SUBSET_LIBRARIES}
${ICU_LIBRARIES}
${LIBXML2_LIBRARIES}
${TIFF_LIBRARIES}
Modified: trunk/Scribus/scribus/fonts/sfnt.cpp
URL: http://scribus.net/websvn/diff.php?repname=Scribus&rev=24469&path=/trunk/Scribus/scribus/fonts/sfnt.cpp
==============================================================================
--- trunk/Scribus/scribus/fonts/sfnt.cpp (original)
+++ trunk/Scribus/scribus/fonts/sfnt.cpp Thu Feb 11 19:12:18 2021
@@ -1,24 +1,28 @@
-//
-// sfnt.cpp
-// Scribus
-//
-// Created by Andreas Vox on 18.04.15.
-//
-//
-
-#include "fonts/sfnt.h"
-#include "fonts/sfnt_format.h"
-
-#include FT_TRUETYPE_TABLES_H
-#include FT_TRUETYPE_TAGS_H
-#include FT_TRUETYPE_IDS_H
-
-#include <QDebug>
-#include <QScopedPointer>
-
+//
+// sfnt.cpp
+// Scribus
+//
+// Created by Andreas Vox on 18.04.15.
+//
+//
+
+#include "fonts/sfnt.h"
+#include "fonts/sfnt_format.h"
+
+#include FT_TRUETYPE_TABLES_H
+#include FT_TRUETYPE_TAGS_H
+#include FT_TRUETYPE_IDS_H
+
+#include <QDebug>
+#include <QScopedPointer>
+
+#include "scconfig.h"
+
#include <harfbuzz/hb.h>
-#include <harfbuzz/hb-subset.h>
-
+#ifdef HAVE_HARFBUZZ_SUBSET
+#include <harfbuzz/hb-subset.h>
+#endif
+
struct HbBlobDeleter
{
static void cleanup(void *pointer)
@@ -37,6 +41,7 @@
}
};
+#ifdef HAVE_HARFBUZZ_SUBSET
struct HbSubsetInputDeleter
{
static void cleanup(void *pointer)
@@ -44,1197 +49,1198 @@
if (pointer)
hb_subset_input_destroy((hb_subset_input_t*) pointer);
}
-};
-
-namespace sfnt {
-
- uchar byte(const QByteArray & bb, uint pos)
- {
- const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos;
- return pp[0];
- }
-
- quint32 word(const QByteArray & bb, uint pos)
- {
- const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos;
- return pp[0] << 24 | pp[1] << 16 | pp[2] << 8 | pp[3];
- }
-
- void putWord(QByteArray & bb, uint pos, quint32 val)
- {
- unsigned char * pp = reinterpret_cast<unsigned char*>(bb.data()) + pos;
- *pp++ = (val >> 24) & 0xFF;
- *pp++ = (val >> 16) & 0xFF;
- *pp++ = (val >> 8) & 0xFF;
- *pp++ = (val) & 0xFF;
- }
-
- void appendWord(QByteArray & bb, quint32 val)
- {
- uint pos = bb.size();
- bb.resize(pos + 4);
- putWord(bb, pos, val);
- }
-
- quint16 word16(const QByteArray & bb, uint pos)
- {
- const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos;
- return pp[0] << 8 | pp[1];
- }
-
- void putWord16(QByteArray & bb, uint pos, quint16 val)
- {
- unsigned char * pp = reinterpret_cast<unsigned char*>(bb.data()) + pos;
- *pp++ = (val >> 8) & 0xFF;
- *pp++ = (val) & 0xFF;
- }
-
- void appendWord16(QByteArray & bb, quint16 val)
- {
- uint pos = bb.size();
- bb.resize(pos + 2);
- putWord16(bb, pos, val);
- }
-
- QByteArray tag(const QByteArray& bb, uint pos)
- {
- return QByteArray::fromRawData(bb.constData() + pos, 4);
- }
-
- QByteArray tag(uint word)
- {
- QByteArray result;
- result.resize(4);
- result[0] = (word >> 24) & 0xFF;
- result[1] = (word >> 16) & 0xFF;
- result[2] = (word >> 8) & 0xFF;
- result[3] = (word) & 0xFF;
- return result;
- }
-
- bool copy(QByteArray & dst, uint to, const QByteArray & src, uint from, uint len)
- {
- if (!dst.data())
- return false;
- if (!src.data())
- return false;
- if (to + len > static_cast<uint>(dst.size()))
- return false;
- if (from + len > static_cast<uint>(src.size()))
- return false;
-
- memcpy(dst.data() + to, src.data() + from, len);
- return true;
- }
-
- const uint post_format10_names_count = 258;
-
- static const char* post_format10_names[] = {
- ".notdef",
- ".null",
- "nonmarkingreturn",
- "space",
- "exclam",
- "quotedbl",
- "numbersign",
- "dollar",
- "percent",
- "ampersand",
- "quotesingle",
- "parenleft",
- "parenright",
- "asterisk",
- "plus",
- "comma",
- "hyphen",
- "period",
- "slash",
- "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
- "colon",
- "semicolon",
- "less",
- "equal",
- "greater",
- "question",
- "at",
- "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
- "bracketleft",
- "backslash",
- "bracketright",
- "asciicircum",
- "underscore",
- "grave",
- "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
- "braceleft",
- "bar",
- "braceright",
- "asciitilde",
- "Adieresis",
- "Aring",
- "Ccedilla",
- "Eacute",
- "Ntilde",
- "Odieresis",
- "Udieresis",
- "aacute",
- "agrave",
- "acircumflex",
- "adieresis",
- "atilde",
- "aring",
- "ccedilla",
- "eacute",
- "egrave",
- "ecircumflex",
- "edieresis",
- "iacute",
- "igrave",
- "icircumflex",
- "idieresis",
- "ntilde",
- "oacute",
- "ograve",
- "ocircumflex",
- "odieresis",
- "otilde",
- "uacute",
- "ugrave",
- "ucircumflex",
- "udieresis",
- "dagger",
- "degree",
- "cent",
- "sterling",
- "section",
- "bullet",
- "paragraph",
- "germandbls",
- "registered",
- "copyright",
- "trademark",
- "acute",
- "dieresis",
- "notequal",
- "AE",
- "Oslash",
- "infinity",
- "plusminus",
- "lessequal",
- "greaterequal",
- "yen",
- "mu",
- "partialdiff",
- "summation",
- "product",
- "pi",
- "integral",
- "ordfeminine",
- "ordmasculine",
- "Omega",
- "ae",
- "oslash",
- "questiondown",
- "exclamdown",
- "logicalnot",
- "radical",
- "florin",
- "approxequal",
- "Delta",
- "guillemotleft",
- "guillemotright",
- "ellipsis",
- "nonbreakingspace",
- "Agrave",
- "Atilde",
- "Otilde",
- "OE",
- "oe",
- "endash",
- "emdash",
- "quotedblleft",
- "quotedblright",
- "quoteleft",
- "quoteright",
- "divide",
- "lozenge",
- "ydieresis",
- "Ydieresis",
- "fraction",
- "currency",
- "guilsinglleft",
- "guilsinglright",
- "fi",
- "fl",
- "daggerdbl",
- "periodcentered",
- "quotesinglbase",
- "quotedblbase",
- "perthousand",
- "Acircumflex",
- "Ecircumflex",
- "Aacute",
- "Edieresis",
- "Egrave",
- "Iacute",
- "Icircumflex",
- "Idieresis",
- "Igrave",
- "Oacute",
- "Ocircumflex",
- "apple",
- "Ograve",
- "Uacute",
- "Ucircumflex",
- "Ugrave",
- "dotlessi",
- "circumflex",
- "tilde",
- "macron",
- "breve",
- "dotaccent",
- "ring",
- "cedilla",
- "hungarumlaut",
- "ogonek",
- "caron",
- "Lslash",
- "lslash",
- "Scaron",
- "scaron",
- "Zcaron",
- "zcaron",
- "brokenbar",
- "Eth",
- "eth",
- "Yacute",
- "yacute",
- "Thorn",
- "thorn",
- "minus",
- "multiply",
- "onesuperior",
- "twosuperior",
- "threesuperior",
- "onehalf",
- "onequarter",
- "threequarters",
- "franc",
- "Gbreve",
- "gbreve",
- "Idotaccent",
- "Scedilla",
- "scedilla",
- "Cacute",
- "cacute",
- "Ccaron",
- "ccaron",
- "dcroat"
- };
-
- bool PostTable::usable() const
- {
- return m_usable;
- }
-
- void PostTable::setUsable(bool usable)
- {
- m_usable = usable;
- }
-
- QString PostTable::errorMsg() const
- {
- return m_errorMsg;
- }
-
- void PostTable::setErrorMsg(const QString& errorMsg)
- {
- m_errorMsg = errorMsg;
- }
-
- uint PostTable::numberOfGlyphs() const
- {
- if (m_names.length() > 0)
- return m_names.length();
- return post_format10_names_count;
- }
-
- QString PostTable::nameFor(uint glyph) const
- {
- if (glyph < (uint) m_names.length())
- return m_names[glyph];
- if (glyph < sfnt::post_format10_names_count)
- return post_format10_names[glyph];
- return ".notdef";
- }
-
- void PostTable::readFrom(FT_Face face)
- {
- QByteArray postData;
- FT_ULong size = 0;
- int error = FT_Load_Sfnt_Table ( face, TTAG_post , 0, nullptr, &size );
- //qDebug() << "load post" << error << size;
- if (error || size == 0)
- {
- m_errorMsg = "no post table";
- m_usable = false;
- return;
- }
- postData.resize(size);
- error = FT_Load_Sfnt_Table ( face, TTAG_post , 0, reinterpret_cast<FT_Byte*>(postData.data()), &size );
- if (error)
- {
- m_errorMsg = "can't load post table";
- m_usable = false;
- return;
- }
-
- switch (sfnt::word(postData, ttf_post_format))
- {
- case sfnt::post_format10:
- m_usable = true;
- m_names.clear();
- return;
- case sfnt::post_format20:
- break;
- case sfnt::post_format30:
- m_errorMsg = QString("post table has no glyph names");
- m_usable = false;
- return;
- case sfnt::post_format25:
- case sfnt::post_format40:
- default:
- m_errorMsg = QString("unsupported post format %1").arg(sfnt::word(postData,0));
- m_usable = false;
- return;
-
- }
- QMap<QString,uint> usedNames;
- QList<QByteArray> pascalStrings;
-
- uint nrOfGlyphs = sfnt::word16(postData, ttf_post_header_length);
- uint stringPos = ttf_post_header_length + 2 + 2 * nrOfGlyphs;
- while (stringPos < (uint) postData.length())
- {
- int strLen = byte(postData, stringPos);
- ++stringPos;
- pascalStrings.append(postData.mid(stringPos, strLen));
- stringPos += strLen;
- }
- uint pos = ttf_post_header_length + 2;
- for (uint gid = 0; gid < nrOfGlyphs; ++gid)
- {
- uint nameIndex = sfnt::word16(postData, pos);
- pos += 2;
- QString name;
- if (nameIndex < sfnt::post_format10_names_count)
- name = sfnt::post_format10_names[nameIndex];
- else if (nameIndex < pascalStrings.length() + sfnt::post_format10_names_count)
- name = pascalStrings[nameIndex - sfnt::post_format10_names_count];
- else {
- m_usable = false;
- m_errorMsg = QString("missing name %1 for glyph %2").arg(nameIndex).arg(gid);
- return;
- }
- if (name != ".notdef" && name[0] != QChar(0) && usedNames.contains(name))
- {
- m_usable = false;
- m_errorMsg = QString("duplicate name %1 used for glyphs %2 and %3").arg(name).arg(gid).arg(usedNames[name]);
- return;
- }
- usedNames[name] = gid;
- m_names.append(name);
- }
- m_errorMsg = "";
- m_usable = true;
- }
-
- int copyTable(QByteArray& ttf, uint destDirEntry, uint pos, const QByteArray& source, uint dirEntry)
- {
- FT_ULong tag = word(source, dirEntry + ttf_TableRecord_tag);
- uint checksum = word(source, dirEntry + ttf_TableRecord_checkSum);
- uint tableStart = word(source, dirEntry + ttf_TableRecord_offset);
- uint tableSize = word(source, dirEntry + ttf_TableRecord_length);
-
- if (!copy(ttf, pos, source, tableStart, tableSize))
- return -1;
-
- putWord(ttf, destDirEntry + ttf_TableRecord_tag, tag);
- putWord(ttf, destDirEntry + ttf_TableRecord_checkSum, checksum);
- putWord(ttf, destDirEntry + ttf_TableRecord_offset, pos);
- putWord(ttf, destDirEntry + ttf_TableRecord_length, tableSize);
-
- return tableSize;
- }
-
- QByteArray extractFace(const QByteArray& coll, int faceIndex)
- {
- QByteArray result;
-
- const int numFonts = word(coll, ttc_numFonts);
- if (faceIndex >= static_cast<int>(numFonts))
- {
- return result;
- }
-
- uint faceOffset = sfnt::word(coll, ttc_OffsetTables + 4 * faceIndex);
- uint nTables = sfnt::word16(coll, faceOffset + ttf_numtables);
-
- //qDebug() << QObject::tr("extracting face %1 from font %2 (offset=%3, nTables=%4)").arg(faceIndex).arg("collection").arg(faceOffset).arg(nTables);
-
- uint headerLength = ttf_TableRecords + ttf_TableRecord_Size * nTables;
-
- uint tableLengths = 0;
- // sum table lengths incl padding
- for (uint i=0; i < nTables; ++i)
- {
- tableLengths += sfnt::word(coll, faceOffset + ttf_TableRecords + ttf_TableRecord_Size * i + ttf_TableRecord_length);
- tableLengths = (tableLengths+3) & ~3;
- }
- result.resize(headerLength + tableLengths);
- if (!result.data())
- {
- result.resize(0);
- return result;
- }
-
- // write header
- // sDebug(QObject::tr("memcpy header: %1 %2 %3").arg(0).arg(faceOffset).arg(headerLength));
- if (!copy(result, 0, coll, faceOffset, headerLength))
- {
- result.resize(0);
- return result;
- }
- uint pos = headerLength;
- for (uint i=0; i < nTables; ++i)
- {
- uint sourceDirEntry = faceOffset + ttf_TableRecords + ttf_TableRecord_Size * i;
- uint destDirEntry = ttf_TableRecords + ttf_TableRecord_Size * i;
-
- int tableSize = copyTable(result, destDirEntry, pos, coll, sourceDirEntry);
- if (tableSize < 0)
- {
- result.resize(0);
- return result;
- }
- pos += tableSize;
-
- // pad
- while ((pos & 3) != 0)
- result.data()[pos++] = '\0';
- }
- return result;
- }
-
- uint getTableDirEntry(const QByteArray& ttf, const QByteArray& ttfTag)
- {
- uint nTables = word16(ttf, ttf_numtables);
- uint pos = ttf_TableRecords;
- for (uint i=0; i < nTables; ++i)
- {
- if (ttfTag == tag(word(ttf, pos + ttf_TableRecord_tag)))
- {
- return pos;
- }
- pos += ttf_TableRecord_Size;
- }
- return 0;
- }
-
- QByteArray getTable(const QByteArray& ttf, const QByteArray& ttfTag)
- {
- uint pos = getTableDirEntry(ttf, ttfTag);
- if (pos <= 0)
- return QByteArray();
-
- uint offset = word(ttf, pos + ttf_TableRecord_offset);
- uint length = word(ttf, pos + ttf_TableRecord_length);
- return QByteArray::fromRawData(ttf.constData() + offset, length);
- }
-
- QByteArray createTableDir(const QList<QByteArray>& tags)
- {
- QByteArray result;
- uint numTables = tags.length();
- uint tableRecordsSize = numTables * ttf_TableRecord_Size;
- result.resize(ttf_TableRecords + tableRecordsSize);
- uint entrySelector = 0;
- uint searchRange = ttf_TableRecord_Size;
- while (2*searchRange < tableRecordsSize)
- {
- searchRange *= 2;
- ++entrySelector;
- }
- uint rangeShift = tableRecordsSize - searchRange;
-
- putWord(result, ttf_sfnt_version, 0x00010000);
- putWord16(result, ttf_numtables, numTables);
- putWord16(result, ttf_searchRange, searchRange);
- putWord16(result, ttf_entrySelector, entrySelector);
- putWord16(result, ttf_rangeShift, rangeShift);
- for (uint i = 0; i < numTables; ++i)
- {
- copy(result, ttf_TableRecords + i*ttf_TableRecord_Size + ttf_TableRecord_tag, tags[i], 0, 4);
- putWord(result, ttf_TableRecords + i*ttf_TableRecord_Size + ttf_TableRecord_checkSum, 0);
- putWord(result, ttf_TableRecords + i*ttf_TableRecord_Size + ttf_TableRecord_offset, 0);
- putWord(result, ttf_TableRecords + i*ttf_TableRecord_Size +ttf_TableRecord_length, 0);
- }
- return result;
- }
-
- quint32 calcTableChecksum(QByteArray& table)
- {
- quint32 Sum = 0L;
- for (int pos = 0; pos < table.length(); pos += 4)
- Sum += word(table, pos);
- return Sum;
- }
-
- void writeTable(QByteArray& ttf, const QByteArray& tag, QByteArray& table)
- {
- //qDebug() << "writing table" << tag << table.size() << "@" << ttf.size();
- uint length = table.size();
- while (table.size() & 0x3)
- table.append('\0');
- uint offset = ttf.size();
- uint checksum = calcTableChecksum(table);
- uint pos = getTableDirEntry(ttf, tag);
- putWord(ttf, pos + ttf_TableRecord_checkSum, checksum);
- putWord(ttf, pos + ttf_TableRecord_offset, offset);
- putWord(ttf, pos + ttf_TableRecord_length, length);
- ttf.append(table);
- }
-
- bool hasLongLocaFormat(const QByteArray& ttf)
- {
- const QByteArray head = getTable(ttf, "head");
- uint idxToLocFormat = word16(head, ttf_head_indexToLocFormat);
- // qDebug() << "loca format:" << (void*)idxToLocFormat;
- return idxToLocFormat == 1;
- }
-
- QList<quint32> readLoca(const QByteArray& ttf)
- {
- QList<quint32> result;
- const QByteArray loca = getTable(ttf, "loca");
- result.reserve(loca.length());
- if (hasLongLocaFormat(ttf))
- {
- for (int i = 0; i < loca.length(); i+=4)
- result.append(word(loca, i));
- }
- else
- {
- for (int i = 0; i < loca.length(); i+=2)
- result.append(word16(loca, i) * 2);
- }
- return result;
- }
-
- QByteArray writeLoca(const QList<uint>& loca, bool longFormat)
- {
- QByteArray result;
- if (longFormat)
- {
- for (int i=0; i < loca.length(); ++i)
- appendWord(result, loca[i]);
- }
- else
- {
- for (int i=0; i < loca.length(); ++i)
- appendWord16(result, loca[i] / 2);
- }
- return result;
- }
-
- QList<std::pair<qint16,quint16> > readHmtx(const QByteArray& ttf)
- {
- QList<std::pair<qint16,quint16> > result;
- const QByteArray hhea = getTable(ttf, "hhea");
- const QByteArray hmtx = getTable(ttf, "hmtx");
- uint endOfLongHorMetrics = 4 * word16(hhea, ttf_hhea_numOfLongHorMetrics);
- qint16 advance;
- quint16 leftSideBearing;
- uint pos = 0;
- while (pos < endOfLongHorMetrics)
- {
- advance = word16(hmtx, pos);
- leftSideBearing = word16(hmtx, pos+2);
- //qDebug() << pos << "hmtx" << advance << leftSideBearing;
- result.append(std::pair<qint16,quint16>(advance, leftSideBearing));
- pos += 4;
- }
- while (pos < (uint) hmtx.length())
- {
- leftSideBearing = word16(hmtx, pos);
- //qDebug() << pos << "hmtx =" << advance << leftSideBearing;
- result.append(std::pair<qint16,quint16>(advance, leftSideBearing));
- pos += 2;
- }
- return result;
- }
-
- QByteArray writeHmtx(const QList<std::pair<qint16,quint16> >& longHorMetrics)
- {
- QByteArray result;
- QList<std::pair<qint16,quint16> >::const_iterator it;
- //int i = 0;
- for (it = longHorMetrics.cbegin(); it < longHorMetrics.cend(); ++it)
- {
- //qDebug() << "hmtx" << i++ << it->first << it->second;
- appendWord16(result, it->first);
- appendWord16(result, it->second);
- }
- return result;
- }
-
- QMap<uint, uint> readCMap(const QByteArray& ttf)
- {
- QMap<uint,uint> result;
- const QByteArray cmaps = getTable(ttf, "cmap");
-
- uint numSubtables = word16(cmaps, ttf_cmap_numberSubtables);
- uint startOfUnicodeTable = 0;
- uint startOfSymbolTable = 0;
- uint unicodeFormat = 0, symbolFormat = 0;
- uint pos = ttf_cmap_encodings;
- for (uint i = 0; i < numSubtables; ++i)
- {
- uint platform = word16(cmaps, pos + ttf_cmap_encoding_platformID);
- uint encoding = word16(cmaps, pos + ttf_cmap_encoding_platformSpecificID);
- uint offset = word(cmaps, pos + ttf_cmap_encoding_offset);
- uint format = word16(cmaps, offset + ttf_cmapx_format);
- pos += ttf_cmap_encoding_Size;
-
- if (format < 4 || format > 12)
- continue;
- if (platform == 0 || (platform == 3 && encoding == 1))
- {
- startOfUnicodeTable = offset;
- unicodeFormat = format;
- break;
- }
- if (platform == 3 && encoding == 0) // MS Symbol cmap
- {
- startOfSymbolTable = offset;
- symbolFormat = format;
- continue;
- }
- format = 1; // no such format
- }
- // If no unicode cmap was found, fallback on ms symbol cmap
- uint format = unicodeFormat;
- if ((unicodeFormat <= 0) && (symbolFormat > 0))
- {
- startOfUnicodeTable = startOfSymbolTable;
- format = symbolFormat;
- }
- //qDebug() << "reading cmap format" << format;
- switch (format)
- {
- case 4:
- {
- uint segCount2 = word16(cmaps, startOfUnicodeTable + ttf_cmap4_segCountX2);
- uint endCodes = startOfUnicodeTable + ttf_cmap4_EndCodes;
- uint startCodes = endCodes + segCount2 + ttf_cmap4_StartCodes_EndCodes;
- uint idDeltas = startCodes + segCount2 + ttf_cmap4_IdDeltas_StartCodes;
- uint idRangeOffsets = idDeltas + segCount2 + ttf_cmap4_IdRangeOffsets_IdDeltas;
- //uint glyphIndexArray = idRangeOffsets + segCount2 + ttf_cmap4_GlyphIds_IdRangeOffsets;
-
- for (uint seg = 0; seg < segCount2; seg+=2)
- {
- uint start = word16(cmaps, startCodes + seg);
- uint end = word16(cmaps, endCodes + seg);
- uint idDelta = word16(cmaps, idDeltas + seg);
- uint idRangeOffset = word16(cmaps, idRangeOffsets + seg);
- for (uint c = start; c <= end; ++c)
- {
- quint16 glyph;
- if (idRangeOffset > 0)
- {
- uint glyphIndexAdress = idRangeOffset + 2*(c-start) + (idRangeOffsets + seg);
- glyph = word16(cmaps, glyphIndexAdress);
- if (glyph != 0)
- glyph += idDelta;
- }
- else
- {
- glyph = c + idDelta;
- }
- if (!result.contains(c))
- {
- // search would always find the one in the segment with the lower endcode, i.e. earlier segment
- if (c < 256 || glyph == 0)
- //qDebug() << "(" << QChar(c) << "," << glyph << ")";
- result[c] = glyph;
- }
- else
- {
- // nothing to do. No idea if fonts with overlapping cmap4 segments exist, though.
- }
- }
- }
- break;
- }
- case 6:
- {
- uint firstCode = word16(cmaps, startOfUnicodeTable + ttf_cmap6_firstCode);
- uint count = word16(cmaps, startOfUnicodeTable + ttf_cmap6_entryCount);
- pos = word16(cmaps, startOfUnicodeTable + ttf_cmap6_glyphIndexArray);
- for (uint i = 0; i < count; ++i)
- {
- result[firstCode + i] = word16(cmaps, pos);
- pos += 2;
- }
- break;
- }
- case 12:
- {
- uint nGroups = word(cmaps, startOfUnicodeTable + ttf_cmap12_nGroups);
- pos = startOfUnicodeTable + ttf_cmap12_Groups;
- for (uint grp = 0; grp < nGroups; ++grp)
- {
- uint start = word(cmaps, pos + ttf_cmap12_Group_startCharCode);
- uint end = word(cmaps, pos + ttf_cmap12_Group_endCharCode);
- uint gid = word(cmaps, pos + ttf_cmap12_Group_startGlyphCode);
- for (uint c = start; c <= end; ++c)
- {
- result[c] = gid;
- ++gid;
- }
- pos += ttf_cmap12_Group_Size;
- }
- break;
- }
- default:
- {
- qDebug() << "unsupported cmap format" << format;
- break;
- }
- }
- return result;
- }
-
- QByteArray writeCMap(const QMap<uint, uint>& cmap)
- {
- // we always write only one table: platform=3(MS), encoding=1(Unicode 16bit)
- QByteArray result;
- appendWord16(result, 0); // version
- appendWord16(result, 1); // number of subtables
- appendWord16(result, 3); // platformID Microsoft
- appendWord16(result, 1); // encodingID UnicodeBMP
- appendWord(result, result.size() + 4); // offset
-
- // find the segments
-
- QList<uint> chars;
- QMap<uint, uint>::ConstIterator cit;
- //qDebug() << "writing cmap";
- bool cmapHasData = false;
- for (cit = cmap.cbegin(); cit != cmap.cend(); ++cit)
- {
- uint ch = cit.key();
- if (!QChar::requiresSurrogates(ch) && cit.value() != 0)
- {
- //qDebug() << "(" << QChar(cit.key()) << "," << cit.value() << ")";
- chars.append(ch);
- cmapHasData = true;
- }
- // qDebug() << QChar(ch) << QChar::requiresSurrogates(ch) << cit.value();
- }
- std::sort(chars.begin(), chars.end());
-
- QList<quint16> startCodes;
- QList<quint16> endCodes;
- QList<quint16> idDeltas;
- QList<quint16> rangeOffsets;
- if (cmapHasData)
- {
- uint pos = 0;
- do {
- quint16 start = chars[pos];
- quint16 delta = cmap[start] - start;
- quint16 rangeOffset = 0;
- quint16 end = start;
- quint16 next;
- ++pos;
- while (pos < (uint) chars.length() && (next = chars[pos]) == end+1)
- {
- end = next;
- if (delta != (quint16)(cmap[chars[pos]] - next))
- {
- rangeOffset = 1; // will be changed later
- }
- ++pos;
- }
- startCodes.append(start);
- endCodes.append(end);
- idDeltas.append(delta);
- rangeOffsets.append(rangeOffset);
- }
- while (pos < (uint) chars.length());
- }
-
- startCodes.append(0xFFFF);
- endCodes.append(0xFFFF);
- idDeltas.append(1); // makes gid 0
- rangeOffsets.append(0);
-
- // write the tables
-
- uint startOfTable = result.size();
- result.resize(startOfTable + ttf_cmap4_EndCodes);
-
- uint segCount = endCodes.length();
- uint segCountX2 = 2 * segCount;
- uint entrySelector = 0;
- uint searchRange = 2;
- while (searchRange <= segCount)
- {
- ++entrySelector;
- searchRange *= 2;
- }
- putWord16(result, startOfTable + ttf_cmapx_format, 4);
- /* ttf_cmap4_length is set later */
- putWord16(result, startOfTable + ttf_cmap4_language, 0);
- putWord16(result, startOfTable + ttf_cmap4_segCountX2, segCountX2);
- putWord16(result, startOfTable + ttf_cmap4_searchRange, searchRange);
- putWord16(result, startOfTable + ttf_cmap4_entrySelector, entrySelector);
- putWord16(result, startOfTable + ttf_cmap4_rangeShift, segCountX2 - searchRange);
-
- for (uint i = 0; i < segCount; ++i)
- {
- appendWord16(result, endCodes[i]);
- };
- appendWord16(result, 0); // reservedPad
-
- for (uint i = 0; i < segCount; ++i)
- {
- appendWord16(result, startCodes[i]);
- };
- for (uint i = 0; i < segCount; ++i)
- {
- appendWord16(result, idDeltas[i]);
- };
-
-
- uint startOfIdRangeOffsetTable = result.size();
- uint startOfGlyphIndexArray = startOfIdRangeOffsetTable + segCountX2;
- result.resize(startOfGlyphIndexArray);
-
- for (uint i = 0; i < segCount; ++i)
- {
- uint idRangeOffsetAddress = startOfIdRangeOffsetTable + 2*i;
- if (rangeOffsets[i] == 0)
- {
- //quint16 dbg = startCodes[i] + idDeltas[i];
- //qDebug() << QChar(startCodes[i]) << "-" << QChar(endCodes[i]) << "/" << (endCodes[i]-startCodes[i]+1) << "+" << idDeltas[i] << "-->" << dbg;
- putWord16(result, idRangeOffsetAddress, 0);
- }
- else
- {
- quint16 idRangeOffset = result.size() - idRangeOffsetAddress;
- putWord16(result, idRangeOffsetAddress, idRangeOffset);
-
- //qDebug() << QChar(startCodes[i]) << "-" << QChar(endCodes[i]) << "/" << (endCodes[i]-startCodes[i]+1) << "@" << idRangeOffset << "+" << idDeltas[i];
-
- uint startCode = startCodes[i];
- uint segLength = (endCodes[i]-startCode+1);
- for (uint offset = 0; offset < segLength; ++offset)
- {
- quint16 glyph = cmap[startCode + offset];
- if (glyph != 0)
- {
- glyph -= idDeltas[i];
- }
- appendWord16(result, glyph);
- }
- }
- };
-
- putWord16(result, startOfTable + ttf_cmap4_length, result.size() - startOfTable);
- return result;
- }
-
- QList<uint> copyGlyphComponents(QByteArray& destGlyf, const QByteArray& srcGlyf, uint srcOffset,
- QMap<uint,uint>& newForOldGid, uint& nextFreeGid)
- {
- QList<uint> result;
-
- uint destStart = destGlyf.size();
- destGlyf.resize(destStart + ttf_glyf_headerSize);
- copy(destGlyf, destStart, srcGlyf, srcOffset, ttf_glyf_headerSize);
-
- uint pos = srcOffset + ttf_glyf_headerSize;
- bool haveInstructions = false;
- uint flags = 0;
- do {
- /* flags */
- flags = word16(srcGlyf, pos);
- pos += 2;
- haveInstructions |= ((flags & ttf_glyf_ComponentFlag_WE_HAVE_INSTRUCTIONS) != 0);
- appendWord16(destGlyf, flags);
-
- /* glyphindex */
- uint glyphIndex = word16(srcGlyf, pos);
- pos += 2;
- if (!newForOldGid.contains(glyphIndex))
- {
- result.append(glyphIndex);
- newForOldGid[glyphIndex] = nextFreeGid++;
- }
- glyphIndex = newForOldGid[glyphIndex];
- appendWord16(destGlyf, glyphIndex);
-
- /* args */
- if ( flags & ttf_glyf_ComponentFlag_ARG_1_AND_2_ARE_WORDS) {
- appendWord16(destGlyf, word16(srcGlyf, pos)); // arg1
- pos += 2;
- appendWord16(destGlyf, word16(srcGlyf, pos)); // arg2
- pos += 2;
- }
- else {
- appendWord16(destGlyf, word16(srcGlyf, pos)); // arg1and2
- pos += 2;
- }
- if ( flags & ttf_glyf_ComponentFlag_WE_HAVE_A_SCALE ) {
- appendWord16(destGlyf, word16(srcGlyf, pos)); // scale
- pos += 2;
- } else if ( flags & ttf_glyf_ComponentFlag_WE_HAVE_AN_X_AND_Y_SCALE ) {
- appendWord16(destGlyf, word16(srcGlyf, pos)); // xscale
- pos += 2;
- appendWord16(destGlyf, word16(srcGlyf, pos)); // yscale
- pos += 2;
- } else if ( flags & ttf_glyf_ComponentFlag_WE_HAVE_A_TWO_BY_TWO ) {
- appendWord16(destGlyf, word16(srcGlyf, pos)); // xscale
- pos += 2;
- appendWord16(destGlyf, word16(srcGlyf, pos)); // scale01
- pos += 2;
- appendWord16(destGlyf, word16(srcGlyf, pos)); // scale10
- pos += 2;
- appendWord16(destGlyf, word16(srcGlyf, pos)); // yscale
- pos += 2;
- }
- } while ( flags & ttf_glyf_ComponentFlag_MORE_COMPONENTS );
-
- if (haveInstructions)
- {
- uint numInstr = word16(srcGlyf, pos);
- appendWord16(destGlyf, numInstr);
- pos += 2;
- uint destPos = destGlyf.size();
- destGlyf.resize(destPos + numInstr);
- copy(destGlyf, destPos, srcGlyf, pos, numInstr);
- }
-
- return result;
- }
-
- QList<uint> copyGlyph(QList<uint>& destLoca, QByteArray& destGlyf, uint destGid,
- const QList<uint>& srcLoca, const QByteArray& srcGlyf, uint srcGid,
- QMap<uint,uint>& newForOldGid, uint& nextFreeGid)
- {
- QList<uint> compositeElements;
- uint glyphStart = srcLoca[srcGid];
- uint glyphLength = srcLoca[srcGid+1] - glyphStart;
- destLoca.append(destGlyf.size());
- //int i = 0;
- if (glyphLength > 0)
- {
- quint16 nrOfContours = word16(srcGlyf, glyphStart);
- if (nrOfContours <= ttf_glyf_Max_numberOfContours)
- {
- // simple glyph
- uint destStart = destGlyf.size();
- //qDebug() << i++ << ":" << nrOfContours << "contours" << glyphStart << "-->" << destStart << "/" << glyphLength;
- destGlyf.resize(destStart + glyphLength);
- copy(destGlyf, destStart, srcGlyf, glyphStart, glyphLength);
- }
- else
- {
- compositeElements.append(copyGlyphComponents(destGlyf, srcGlyf, glyphStart, newForOldGid, nextFreeGid));
- //qDebug() << i++ << ":" << srcGid << "composite glyph brought" << compositeElements.size() << "more glyphs";
- }
- // Data must be aligned at least on 2 byte boundary, however
- // a 4-byte alignment is recommended by TTF specs for reasons
- // related to CPU and efficiency
- int targetSize = (destGlyf.size() + 3) & ~3;
- if (destGlyf.size() < targetSize)
- {
- destGlyf.reserve(targetSize);
- while (destGlyf.size() < targetSize)
- destGlyf.append('\0');
- }
- }
-
- return compositeElements;
- }
-
- QByteArray subsetFace(const QByteArray& ttf, QList<uint>& glyphs, QMap<uint, uint>& glyphMap)
- {
- QMap<QByteArray,QByteArray> tables;
-
- // qDebug() << "loca table:" << (void*) oldLoca[0] << (void*) oldLoca[1] << (void*) oldLoca[2] << (void*) oldLoca[3] << (void*) oldLoca[4] << (void*) oldLoca[5] << (void*) oldLoca[6] << (void*) oldLoca[7];
-
- QMap<uint, uint> newForOldGid;
- if (glyphs.length() == 0)
- {
- tables["loca"] = getTable(ttf, "loca");
- tables["glyf"] = getTable(ttf, "glyf");
- }
- else
- {
- QList<uint> oldLoca = readLoca(ttf);
- const QByteArray oldGlyf = getTable(ttf, "glyf");
-
- QList<quint32> newLoca;
- QByteArray newGlyf;
- glyphs.removeAll(0);
- glyphs.prepend(0);
-
- for (int i = 0; i < glyphs.length(); ++i)
- {
- uint oldGid = glyphs[i];
- newForOldGid[oldGid] = i;
- }
-
- uint nextFreeGid = glyphs.length();
- for (int i = 0; i < glyphs.length(); ++i)
- {
- uint oldGid = glyphs[i];
- glyphs.append(copyGlyph(newLoca, newGlyf, i,
- oldLoca, oldGlyf, oldGid,
- newForOldGid, nextFreeGid));
- }
- newLoca.append(newGlyf.length());
-
- tables["loca"] = writeLoca(newLoca, hasLongLocaFormat(ttf));
- tables["glyf"] = newGlyf;
- }
-
- QMap<uint,uint> cmap = readCMap(ttf);
- QMap<uint,uint>::iterator it;
- uint firstChar = 0xFFFFFFFF;
- uint lastChar = 0;
- for (it = cmap.begin(); it != cmap.end(); ++it)
- {
- if (glyphs.length() > 0 && !glyphs.contains(it.value()))
- {
- it.value() = 0;
- }
- else if (it.value() != 0)
- {
- if (glyphs.length() > 0)
- {
- //qDebug() << "MAP" << QChar(it.key()) << it.value() << "-->" << newForOldGid[it.value()];
- it.value() = newForOldGid[it.value()];
- }
- if (it.key() < firstChar)
- firstChar = it.key();
- else if (it.key() > lastChar)
- lastChar = it.key();
- }
- }
- tables["cmap"] = writeCMap(cmap);
-
- QByteArray os2 = getTable(ttf, "OS/2");
- if (os2.length() > ttf_os2_usLastCharIndex)
- {
- // TODO: adapt unicode ranges
- putWord16(os2, ttf_os2_usFirstCharIndex, firstChar < 0xFFFF ? firstChar : 0xFFFF);
- putWord16(os2, ttf_os2_usLastCharIndex, lastChar < 0xFFFF ? lastChar : 0xFFFF);
- tables["OS/2"] = os2;
- }
-
- if (glyphs.length() > 0)
- {
- QList<std::pair<qint16, quint16> > oldHmtx = readHmtx(ttf);
- QList<std::pair<qint16, quint16> > newHmtx;
- newHmtx.reserve(glyphs.length() + 1);
- newHmtx.append(std::pair<qint16, quint16>(1234, 123));
- for (int i = 1; i < glyphs.length(); ++i)
- newHmtx.append(newHmtx[0]);
- QMap<uint,uint>::const_iterator iter;
- for (iter = newForOldGid.cbegin(); iter != newForOldGid.cend(); ++iter)
- {
- //qDebug() << "hmtx" << iter.key() << " -> " << iter.value() << "=" << oldHmtx[iter.key()].first;
- newHmtx[iter.value()] = oldHmtx[iter.key()];
- }
- tables["hmtx"] = writeHmtx(newHmtx);
- }
- else
- {
- tables["hmtx"] = getTable(ttf, "hmtx");
- }
-
- QByteArray maxp = getTable(ttf, "maxp");
- if (glyphs.length() > 0)
- {
- putWord16(maxp, ttf_maxp_numGlyphs, glyphs.length());
- }
- tables["maxp"] = maxp;
-
- QByteArray hhea = getTable(ttf, "hhea");
- if (glyphs.length() > 0)
- {
- putWord16(hhea, ttf_hhea_numOfLongHorMetrics, glyphs.length());
- }
- tables["hhea"] = hhea;
-
- QByteArray post = getTable(ttf, "post");
- if (word(post, ttf_post_format) != post_format30)
- {
- putWord(post, ttf_post_format, post_format30);
- post.truncate(ttf_post_header_length);
- }
- tables["post"] = post;
-
- // TODO: kern table
-
- QByteArray name = getTable(ttf, "name");
- if (name.length() > 0)
- tables["name"] = name;
-
- QByteArray prep = getTable(ttf, "prep");
- if (prep.length() > 0)
- tables["prep"] = prep;
-
- QByteArray cvt = getTable(ttf, "cvt ");
- if (cvt.length() > 0)
- tables["cvt "] = cvt;
-
- QByteArray fpgm = getTable(ttf, "fpgm");
- if (fpgm.length() > 0)
- tables["fpgm"] = fpgm;
-
- QByteArray head = getTable(ttf, "head");
- putWord(head, ttf_head_checkSumAdjustment, 0);
- tables["head"] = head;
-
- QByteArray font = createTableDir(tables.keys());
- QMap<QByteArray,QByteArray>::iterator tableP;
- for (tableP = tables.begin(); tableP != tables.end(); ++tableP)
- {
- writeTable(font, tableP.key(), tableP.value());
- }
-
- uint checkSumAdjustment = 0xB1B0AFBA - calcTableChecksum(font);
- uint headTable = getTableDirEntry(font, "head");
- headTable = word(font, headTable + ttf_TableRecord_offset);
- putWord(font, headTable + ttf_head_checkSumAdjustment, checkSumAdjustment);
-
+};
+#endif
+
+namespace sfnt {
+
+ uchar byte(const QByteArray & bb, uint pos)
+ {
+ const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos;
+ return pp[0];
+ }
+
+ quint32 word(const QByteArray & bb, uint pos)
+ {
+ const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos;
+ return pp[0] << 24 | pp[1] << 16 | pp[2] << 8 | pp[3];
+ }
+
+ void putWord(QByteArray & bb, uint pos, quint32 val)
+ {
+ unsigned char * pp = reinterpret_cast<unsigned char*>(bb.data()) + pos;
+ *pp++ = (val >> 24) & 0xFF;
+ *pp++ = (val >> 16) & 0xFF;
+ *pp++ = (val >> 8) & 0xFF;
+ *pp++ = (val) & 0xFF;
+ }
+
+ void appendWord(QByteArray & bb, quint32 val)
+ {
+ uint pos = bb.size();
+ bb.resize(pos + 4);
+ putWord(bb, pos, val);
+ }
+
+ quint16 word16(const QByteArray & bb, uint pos)
+ {
+ const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos;
+ return pp[0] << 8 | pp[1];
+ }
+
+ void putWord16(QByteArray & bb, uint pos, quint16 val)
+ {
+ unsigned char * pp = reinterpret_cast<unsigned char*>(bb.data()) + pos;
+ *pp++ = (val >> 8) & 0xFF;
+ *pp++ = (val) & 0xFF;
+ }
+
+ void appendWord16(QByteArray & bb, quint16 val)
+ {
+ uint pos = bb.size();
+ bb.resize(pos + 2);
+ putWord16(bb, pos, val);
+ }
+
+ QByteArray tag(const QByteArray& bb, uint pos)
+ {
+ return QByteArray::fromRawData(bb.constData() + pos, 4);
+ }
+
+ QByteArray tag(uint word)
+ {
+ QByteArray result;
+ result.resize(4);
+ result[0] = (word >> 24) & 0xFF;
+ result[1] = (word >> 16) & 0xFF;
+ result[2] = (word >> 8) & 0xFF;
+ result[3] = (word) & 0xFF;
+ return result;
+ }
+
+ bool copy(QByteArray & dst, uint to, const QByteArray & src, uint from, uint len)
+ {
+ if (!dst.data())
+ return false;
+ if (!src.data())
+ return false;
+ if (to + len > static_cast<uint>(dst.size()))
+ return false;
+ if (from + len > static_cast<uint>(src.size()))
+ return false;
+
+ memcpy(dst.data() + to, src.data() + from, len);
+ return true;
+ }
+
+ const uint post_format10_names_count = 258;
+
+ static const char* post_format10_names[] = {
+ ".notdef",
+ ".null",
+ "nonmarkingreturn",
+ "space",
+ "exclam",
+ "quotedbl",
+ "numbersign",
+ "dollar",
+ "percent",
+ "ampersand",
+ "quotesingle",
+ "parenleft",
+ "parenright",
+ "asterisk",
+ "plus",
+ "comma",
+ "hyphen",
+ "period",
+ "slash",
+ "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
+ "colon",
+ "semicolon",
+ "less",
+ "equal",
+ "greater",
+ "question",
+ "at",
+ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
+ "bracketleft",
+ "backslash",
+ "bracketright",
+ "asciicircum",
+ "underscore",
+ "grave",
+ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
+ "braceleft",
+ "bar",
+ "braceright",
+ "asciitilde",
+ "Adieresis",
+ "Aring",
+ "Ccedilla",
+ "Eacute",
+ "Ntilde",
+ "Odieresis",
+ "Udieresis",
+ "aacute",
+ "agrave",
+ "acircumflex",
+ "adieresis",
+ "atilde",
+ "aring",
+ "ccedilla",
+ "eacute",
+ "egrave",
+ "ecircumflex",
+ "edieresis",
+ "iacute",
+ "igrave",
+ "icircumflex",
+ "idieresis",
+ "ntilde",
+ "oacute",
+ "ograve",
+ "ocircumflex",
+ "odieresis",
+ "otilde",
+ "uacute",
+ "ugrave",
+ "ucircumflex",
+ "udieresis",
+ "dagger",
+ "degree",
+ "cent",
+ "sterling",
+ "section",
+ "bullet",
+ "paragraph",
+ "germandbls",
+ "registered",
+ "copyright",
+ "trademark",
+ "acute",
+ "dieresis",
+ "notequal",
+ "AE",
+ "Oslash",
+ "infinity",
+ "plusminus",
+ "lessequal",
+ "greaterequal",
+ "yen",
+ "mu",
+ "partialdiff",
+ "summation",
+ "product",
+ "pi",
+ "integral",
+ "ordfeminine",
+ "ordmasculine",
+ "Omega",
+ "ae",
+ "oslash",
+ "questiondown",
+ "exclamdown",
+ "logicalnot",
+ "radical",
+ "florin",
+ "approxequal",
+ "Delta",
+ "guillemotleft",
+ "guillemotright",
+ "ellipsis",
+ "nonbreakingspace",
+ "Agrave",
+ "Atilde",
+ "Otilde",
+ "OE",
+ "oe",
+ "endash",
+ "emdash",
+ "quotedblleft",
+ "quotedblright",
+ "quoteleft",
+ "quoteright",
+ "divide",
+ "lozenge",
+ "ydieresis",
+ "Ydieresis",
+ "fraction",
+ "currency",
+ "guilsinglleft",
+ "guilsinglright",
+ "fi",
+ "fl",
+ "daggerdbl",
+ "periodcentered",
+ "quotesinglbase",
+ "quotedblbase",
+ "perthousand",
+ "Acircumflex",
+ "Ecircumflex",
+ "Aacute",
+ "Edieresis",
+ "Egrave",
+ "Iacute",
+ "Icircumflex",
+ "Idieresis",
+ "Igrave",
+ "Oacute",
+ "Ocircumflex",
+ "apple",
+ "Ograve",
+ "Uacute",
+ "Ucircumflex",
+ "Ugrave",
+ "dotlessi",
+ "circumflex",
+ "tilde",
+ "macron",
+ "breve",
+ "dotaccent",
+ "ring",
+ "cedilla",
+ "hungarumlaut",
+ "ogonek",
+ "caron",
+ "Lslash",
+ "lslash",
+ "Scaron",
+ "scaron",
+ "Zcaron",
+ "zcaron",
+ "brokenbar",
+ "Eth",
+ "eth",
+ "Yacute",
+ "yacute",
+ "Thorn",
+ "thorn",
+ "minus",
+ "multiply",
+ "onesuperior",
+ "twosuperior",
+ "threesuperior",
+ "onehalf",
+ "onequarter",
+ "threequarters",
+ "franc",
+ "Gbreve",
+ "gbreve",
+ "Idotaccent",
+ "Scedilla",
+ "scedilla",
+ "Cacute",
+ "cacute",
+ "Ccaron",
+ "ccaron",
+ "dcroat"
+ };
+
+ bool PostTable::usable() const
+ {
+ return m_usable;
+ }
+
+ void PostTable::setUsable(bool usable)
+ {
+ m_usable = usable;
+ }
+
+ QString PostTable::errorMsg() const
+ {
+ return m_errorMsg;
+ }
+
+ void PostTable::setErrorMsg(const QString& errorMsg)
+ {
+ m_errorMsg = errorMsg;
+ }
+
+ uint PostTable::numberOfGlyphs() const
+ {
+ if (m_names.length() > 0)
+ return m_names.length();
+ return post_format10_names_count;
+ }
+
+ QString PostTable::nameFor(uint glyph) const
+ {
+ if (glyph < (uint) m_names.length())
+ return m_names[glyph];
+ if (glyph < sfnt::post_format10_names_count)
+ return post_format10_names[glyph];
+ return ".notdef";
+ }
+
+ void PostTable::readFrom(FT_Face face)
+ {
+ QByteArray postData;
+ FT_ULong size = 0;
+ int error = FT_Load_Sfnt_Table ( face, TTAG_post , 0, nullptr, &size );
+ //qDebug() << "load post" << error << size;
+ if (error || size == 0)
+ {
+ m_errorMsg = "no post table";
+ m_usable = false;
+ return;
+ }
+ postData.resize(size);
+ error = FT_Load_Sfnt_Table ( face, TTAG_post , 0, reinterpret_cast<FT_Byte*>(postData.data()), &size );
+ if (error)
+ {
+ m_errorMsg = "can't load post table";
+ m_usable = false;
+ return;
+ }
+
+ switch (sfnt::word(postData, ttf_post_format))
+ {
+ case sfnt::post_format10:
+ m_usable = true;
+ m_names.clear();
+ return;
+ case sfnt::post_format20:
+ break;
+ case sfnt::post_format30:
+ m_errorMsg = QString("post table has no glyph names");
+ m_usable = false;
+ return;
+ case sfnt::post_format25:
+ case sfnt::post_format40:
+ default:
+ m_errorMsg = QString("unsupported post format %1").arg(sfnt::word(postData,0));
+ m_usable = false;
+ return;
+
+ }
+ QMap<QString,uint> usedNames;
+ QList<QByteArray> pascalStrings;
+
+ uint nrOfGlyphs = sfnt::word16(postData, ttf_post_header_length);
+ uint stringPos = ttf_post_header_length + 2 + 2 * nrOfGlyphs;
+ while (stringPos < (uint) postData.length())
+ {
+ int strLen = byte(postData, stringPos);
+ ++stringPos;
+ pascalStrings.append(postData.mid(stringPos, strLen));
+ stringPos += strLen;
+ }
+ uint pos = ttf_post_header_length + 2;
+ for (uint gid = 0; gid < nrOfGlyphs; ++gid)
+ {
+ uint nameIndex = sfnt::word16(postData, pos);
+ pos += 2;
+ QString name;
+ if (nameIndex < sfnt::post_format10_names_count)
+ name = sfnt::post_format10_names[nameIndex];
+ else if (nameIndex < pascalStrings.length() + sfnt::post_format10_names_count)
+ name = pascalStrings[nameIndex - sfnt::post_format10_names_count];
+ else {
+ m_usable = false;
+ m_errorMsg = QString("missing name %1 for glyph %2").arg(nameIndex).arg(gid);
+ return;
+ }
+ if (name != ".notdef" && name[0] != QChar(0) && usedNames.contains(name))
+ {
+ m_usable = false;
+ m_errorMsg = QString("duplicate name %1 used for glyphs %2 and %3").arg(name).arg(gid).arg(usedNames[name]);
+ return;
+ }
+ usedNames[name] = gid;
+ m_names.append(name);
+ }
+ m_errorMsg = "";
+ m_usable = true;
+ }
+
+ int copyTable(QByteArray& ttf, uint destDirEntry, uint pos, const QByteArray& source, uint dirEntry)
+ {
+ FT_ULong tag = word(source, dirEntry + ttf_TableRecord_tag);
+ uint checksum = word(source, dirEntry + ttf_TableRecord_checkSum);
+ uint tableStart = word(source, dirEntry + ttf_TableRecord_offset);
+ uint tableSize = word(source, dirEntry + ttf_TableRecord_length);
+
+ if (!copy(ttf, pos, source, tableStart, tableSize))
+ return -1;
+
+ putWord(ttf, destDirEntry + ttf_TableRecord_tag, tag);
+ putWord(ttf, destDirEntry + ttf_TableRecord_checkSum, checksum);
+ putWord(ttf, destDirEntry + ttf_TableRecord_offset, pos);
+ putWord(ttf, destDirEntry + ttf_TableRecord_length, tableSize);
+
+ return tableSize;
+ }
+
+ QByteArray extractFace(const QByteArray& coll, int faceIndex)
+ {
+ QByteArray result;
+
+ const int numFonts = word(coll, ttc_numFonts);
+ if (faceIndex >= static_cast<int>(numFonts))
+ {
+ return result;
+ }
+
+ uint faceOffset = sfnt::word(coll, ttc_OffsetTables + 4 * faceIndex);
+ uint nTables = sfnt::word16(coll, faceOffset + ttf_numtables);
+
+ //qDebug() << QObject::tr("extracting face %1 from font %2 (offset=%3, nTables=%4)").arg(faceIndex).arg("collection").arg(faceOffset).arg(nTables);
+
+ uint headerLength = ttf_TableRecords + ttf_TableRecord_Size * nTables;
+
+ uint tableLengths = 0;
+ // sum table lengths incl padding
+ for (uint i=0; i < nTables; ++i)
+ {
+ tableLengths += sfnt::word(coll, faceOffset + ttf_TableRecords + ttf_TableRecord_Size * i + ttf_TableRecord_length);
+ tableLengths = (tableLengths+3) & ~3;
+ }
+ result.resize(headerLength + tableLengths);
+ if (!result.data())
+ {
+ result.resize(0);
+ return result;
+ }
+
+ // write header
+ // sDebug(QObject::tr("memcpy header: %1 %2 %3").arg(0).arg(faceOffset).arg(headerLength));
+ if (!copy(result, 0, coll, faceOffset, headerLength))
+ {
+ result.resize(0);
+ return result;
+ }
+ uint pos = headerLength;
+ for (uint i=0; i < nTables; ++i)
+ {
+ uint sourceDirEntry = faceOffset + ttf_TableRecords + ttf_TableRecord_Size * i;
+ uint destDirEntry = ttf_TableRecords + ttf_TableRecord_Size * i;
+
+ int tableSize = copyTable(result, destDirEntry, pos, coll, sourceDirEntry);
+ if (tableSize < 0)
+ {
+ result.resize(0);
+ return result;
+ }
+ pos += tableSize;
+
+ // pad
+ while ((pos & 3) != 0)
+ result.data()[pos++] = '\0';
+ }
+ return result;
+ }
+
+ uint getTableDirEntry(const QByteArray& ttf, const QByteArray& ttfTag)
+ {
+ uint nTables = word16(ttf, ttf_numtables);
+ uint pos = ttf_TableRecords;
+ for (uint i=0; i < nTables; ++i)
+ {
+ if (ttfTag == tag(word(ttf, pos + ttf_TableRecord_tag)))
+ {
+ return pos;
+ }
+ pos += ttf_TableRecord_Size;
+ }
+ return 0;
+ }
+
+ QByteArray getTable(const QByteArray& ttf, const QByteArray& ttfTag)
+ {
+ uint pos = getTableDirEntry(ttf, ttfTag);
+ if (pos <= 0)
+ return QByteArray();
+
+ uint offset = word(ttf, pos + ttf_TableRecord_offset);
+ uint length = word(ttf, pos + ttf_TableRecord_length);
+ return QByteArray::fromRawData(ttf.constData() + offset, length);
+ }
+
+ QByteArray createTableDir(const QList<QByteArray>& tags)
+ {
+ QByteArray result;
+ uint numTables = tags.length();
+ uint tableRecordsSize = numTables * ttf_TableRecord_Size;
+ result.resize(ttf_TableRecords + tableRecordsSize);
+ uint entrySelector = 0;
+ uint searchRange = ttf_TableRecord_Size;
+ while (2*searchRange < tableRecordsSize)
+ {
+ searchRange *= 2;
+ ++entrySelector;
+ }
+ uint rangeShift = tableRecordsSize - searchRange;
+
+ putWord(result, ttf_sfnt_version, 0x00010000);
+ putWord16(result, ttf_numtables, numTables);
+ putWord16(result, ttf_searchRange, searchRange);
+ putWord16(result, ttf_entrySelector, entrySelector);
+ putWord16(result, ttf_rangeShift, rangeShift);
+ for (uint i = 0; i < numTables; ++i)
+ {
+ copy(result, ttf_TableRecords + i*ttf_TableRecord_Size + ttf_TableRecord_tag, tags[i], 0, 4);
+ putWord(result, ttf_TableRecords + i*ttf_TableRecord_Size + ttf_TableRecord_checkSum, 0);
+ putWord(result, ttf_TableRecords + i*ttf_TableRecord_Size + ttf_TableRecord_offset, 0);
+ putWord(result, ttf_TableRecords + i*ttf_TableRecord_Size +ttf_TableRecord_length, 0);
+ }
+ return result;
+ }
+
+ quint32 calcTableChecksum(QByteArray& table)
+ {
+ quint32 Sum = 0L;
+ for (int pos = 0; pos < table.length(); pos += 4)
+ Sum += word(table, pos);
+ return Sum;
+ }
+
+ void writeTable(QByteArray& ttf, const QByteArray& tag, QByteArray& table)
+ {
+ //qDebug() << "writing table" << tag << table.size() << "@" << ttf.size();
+ uint length = table.size();
+ while (table.size() & 0x3)
+ table.append('\0');
+ uint offset = ttf.size();
+ uint checksum = calcTableChecksum(table);
+ uint pos = getTableDirEntry(ttf, tag);
+ putWord(ttf, pos + ttf_TableRecord_checkSum, checksum);
+ putWord(ttf, pos + ttf_TableRecord_offset, offset);
+ putWord(ttf, pos + ttf_TableRecord_length, length);
+ ttf.append(table);
+ }
+
+ bool hasLongLocaFormat(const QByteArray& ttf)
+ {
+ const QByteArray head = getTable(ttf, "head");
+ uint idxToLocFormat = word16(head, ttf_head_indexToLocFormat);
+ // qDebug() << "loca format:" << (void*)idxToLocFormat;
+ return idxToLocFormat == 1;
+ }
+
+ QList<quint32> readLoca(const QByteArray& ttf)
+ {
+ QList<quint32> result;
+ const QByteArray loca = getTable(ttf, "loca");
+ result.reserve(loca.length());
+ if (hasLongLocaFormat(ttf))
+ {
+ for (int i = 0; i < loca.length(); i+=4)
+ result.append(word(loca, i));
+ }
+ else
+ {
+ for (int i = 0; i < loca.length(); i+=2)
+ result.append(word16(loca, i) * 2);
+ }
+ return result;
+ }
+
+ QByteArray writeLoca(const QList<uint>& loca, bool longFormat)
+ {
+ QByteArray result;
+ if (longFormat)
+ {
+ for (int i=0; i < loca.length(); ++i)
+ appendWord(result, loca[i]);
+ }
+ else
+ {
+ for (int i=0; i < loca.length(); ++i)
+ appendWord16(result, loca[i] / 2);
+ }
+ return result;
+ }
+
+ QList<std::pair<qint16,quint16> > readHmtx(const QByteArray& ttf)
+ {
+ QList<std::pair<qint16,quint16> > result;
+ const QByteArray hhea = getTable(ttf, "hhea");
+ const QByteArray hmtx = getTable(ttf, "hmtx");
+ uint endOfLongHorMetrics = 4 * word16(hhea, ttf_hhea_numOfLongHorMetrics);
+ qint16 advance;
+ quint16 leftSideBearing;
+ uint pos = 0;
+ while (pos < endOfLongHorMetrics)
+ {
+ advance = word16(hmtx, pos);
+ leftSideBearing = word16(hmtx, pos+2);
+ //qDebug() << pos << "hmtx" << advance << leftSideBearing;
+ result.append(std::pair<qint16,quint16>(advance, leftSideBearing));
+ pos += 4;
+ }
+ while (pos < (uint) hmtx.length())
+ {
+ leftSideBearing = word16(hmtx, pos);
+ //qDebug() << pos << "hmtx =" << advance << leftSideBearing;
+ result.append(std::pair<qint16,quint16>(advance, leftSideBearing));
+ pos += 2;
+ }
+ return result;
+ }
+
+ QByteArray writeHmtx(const QList<std::pair<qint16,quint16> >& longHorMetrics)
+ {
+ QByteArray result;
+ QList<std::pair<qint16,quint16> >::const_iterator it;
+ //int i = 0;
+ for (it = longHorMetrics.cbegin(); it < longHorMetrics.cend(); ++it)
+ {
+ //qDebug() << "hmtx" << i++ << it->first << it->second;
+ appendWord16(result, it->first);
+ appendWord16(result, it->second);
+ }
+ return result;
+ }
+
+ QMap<uint, uint> readCMap(const QByteArray& ttf)
+ {
+ QMap<uint,uint> result;
+ const QByteArray cmaps = getTable(ttf, "cmap");
+
+ uint numSubtables = word16(cmaps, ttf_cmap_numberSubtables);
+ uint startOfUnicodeTable = 0;
+ uint startOfSymbolTable = 0;
+ uint unicodeFormat = 0, symbolFormat = 0;
+ uint pos = ttf_cmap_encodings;
+ for (uint i = 0; i < numSubtables; ++i)
+ {
+ uint platform = word16(cmaps, pos + ttf_cmap_encoding_platformID);
+ uint encoding = word16(cmaps, pos + ttf_cmap_encoding_platformSpecificID);
+ uint offset = word(cmaps, pos + ttf_cmap_encoding_offset);
+ uint format = word16(cmaps, offset + ttf_cmapx_format);
+ pos += ttf_cmap_encoding_Size;
+
+ if (format < 4 || format > 12)
+ continue;
+ if (platform == 0 || (platform == 3 && encoding == 1))
+ {
+ startOfUnicodeTable = offset;
+ unicodeFormat = format;
+ break;
+ }
+ if (platform == 3 && encoding == 0) // MS Symbol cmap
+ {
+ startOfSymbolTable = offset;
+ symbolFormat = format;
+ continue;
+ }
+ format = 1; // no such format
+ }
+ // If no unicode cmap was found, fallback on ms symbol cmap
+ uint format = unicodeFormat;
+ if ((unicodeFormat <= 0) && (symbolFormat > 0))
+ {
+ startOfUnicodeTable = startOfSymbolTable;
+ format = symbolFormat;
+ }
+ //qDebug() << "reading cmap format" << format;
+ switch (format)
+ {
+ case 4:
+ {
+ uint segCount2 = word16(cmaps, startOfUnicodeTable + ttf_cmap4_segCountX2);
+ uint endCodes = startOfUnicodeTable + ttf_cmap4_EndCodes;
+ uint startCodes = endCodes + segCount2 + ttf_cmap4_StartCodes_EndCodes;
+ uint idDeltas = startCodes + segCount2 + ttf_cmap4_IdDeltas_StartCodes;
+ uint idRangeOffsets = idDeltas + segCount2 + ttf_cmap4_IdRangeOffsets_IdDeltas;
+ //uint glyphIndexArray = idRangeOffsets + segCount2 + ttf_cmap4_GlyphIds_IdRangeOffsets;
+
+ for (uint seg = 0; seg < segCount2; seg+=2)
+ {
+ uint start = word16(cmaps, startCodes + seg);
+ uint end = word16(cmaps, endCodes + seg);
+ uint idDelta = word16(cmaps, idDeltas + seg);
+ uint idRangeOffset = word16(cmaps, idRangeOffsets + seg);
+ for (uint c = start; c <= end; ++c)
+ {
+ quint16 glyph;
+ if (idRangeOffset > 0)
+ {
+ uint glyphIndexAdress = idRangeOffset + 2*(c-start) + (idRangeOffsets + seg);
+ glyph = word16(cmaps, glyphIndexAdress);
+ if (glyph != 0)
+ glyph += idDelta;
+ }
+ else
+ {
+ glyph = c + idDelta;
+ }
+ if (!result.contains(c))
+ {
+ // search would always find the one in the segment with the lower endcode, i.e. earlier segment
+ if (c < 256 || glyph == 0)
+ //qDebug() << "(" << QChar(c) << "," << glyph << ")";
+ result[c] = glyph;
+ }
+ else
+ {
+ // nothing to do. No idea if fonts with overlapping cmap4 segments exist, though.
+ }
+ }
+ }
+ break;
+ }
+ case 6:
+ {
+ uint firstCode = word16(cmaps, startOfUnicodeTable + ttf_cmap6_firstCode);
+ uint count = word16(cmaps, startOfUnicodeTable + ttf_cmap6_entryCount);
+ pos = word16(cmaps, startOfUnicodeTable + ttf_cmap6_glyphIndexArray);
+ for (uint i = 0; i < count; ++i)
+ {
+ result[firstCode + i] = word16(cmaps, pos);
+ pos += 2;
+ }
+ break;
+ }
+ case 12:
+ {
+ uint nGroups = word(cmaps, startOfUnicodeTable + ttf_cmap12_nGroups);
+ pos = startOfUnicodeTable + ttf_cmap12_Groups;
+ for (uint grp = 0; grp < nGroups; ++grp)
+ {
+ uint start = word(cmaps, pos + ttf_cmap12_Group_startCharCode);
+ uint end = word(cmaps, pos + ttf_cmap12_Group_endCharCode);
+ uint gid = word(cmaps, pos + ttf_cmap12_Group_startGlyphCode);
+ for (uint c = start; c <= end; ++c)
+ {
+ result[c] = gid;
+ ++gid;
+ }
+ pos += ttf_cmap12_Group_Size;
+ }
+ break;
+ }
+ default:
+ {
+ qDebug() << "unsupported cmap format" << format;
+ break;
+ }
+ }
+ return result;
+ }
+
+ QByteArray writeCMap(const QMap<uint, uint>& cmap)
+ {
+ // we always write only one table: platform=3(MS), encoding=1(Unicode 16bit)
+ QByteArray result;
+ appendWord16(result, 0); // version
+ appendWord16(result, 1); // number of subtables
+ appendWord16(result, 3); // platformID Microsoft
+ appendWord16(result, 1); // encodingID UnicodeBMP
+ appendWord(result, result.size() + 4); // offset
+
+ // find the segments
+
+ QList<uint> chars;
+ QMap<uint, uint>::ConstIterator cit;
+ //qDebug() << "writing cmap";
+ bool cmapHasData = false;
+ for (cit = cmap.cbegin(); cit != cmap.cend(); ++cit)
+ {
+ uint ch = cit.key();
+ if (!QChar::requiresSurrogates(ch) && cit.value() != 0)
+ {
+ //qDebug() << "(" << QChar(cit.key()) << "," << cit.value() << ")";
+ chars.append(ch);
+ cmapHasData = true;
+ }
+ // qDebug() << QChar(ch) << QChar::requiresSurrogates(ch) << cit.value();
+ }
+ std::sort(chars.begin(), chars.end());
+
+ QList<quint16> startCodes;
+ QList<quint16> endCodes;
+ QList<quint16> idDeltas;
+ QList<quint16> rangeOffsets;
+ if (cmapHasData)
+ {
+ uint pos = 0;
+ do {
+ quint16 start = chars[pos];
+ quint16 delta = cmap[start] - start;
+ quint16 rangeOffset = 0;
+ quint16 end = start;
+ quint16 next;
+ ++pos;
+ while (pos < (uint) chars.length() && (next = chars[pos]) == end+1)
+ {
+ end = next;
+ if (delta != (quint16)(cmap[chars[pos]] - next))
+ {
+ rangeOffset = 1; // will be changed later
+ }
+ ++pos;
+ }
+ startCodes.append(start);
+ endCodes.append(end);
+ idDeltas.append(delta);
+ rangeOffsets.append(rangeOffset);
+ }
+ while (pos < (uint) chars.length());
+ }
+
+ startCodes.append(0xFFFF);
+ endCodes.append(0xFFFF);
+ idDeltas.append(1); // makes gid 0
+ rangeOffsets.append(0);
+
+ // write the tables
+
+ uint startOfTable = result.size();
+ result.resize(startOfTable + ttf_cmap4_EndCodes);
+
+ uint segCount = endCodes.length();
+ uint segCountX2 = 2 * segCount;
+ uint entrySelector = 0;
+ uint searchRange = 2;
+ while (searchRange <= segCount)
+ {
+ ++entrySelector;
+ searchRange *= 2;
+ }
+ putWord16(result, startOfTable + ttf_cmapx_format, 4);
+ /* ttf_cmap4_length is set later */
+ putWord16(result, startOfTable + ttf_cmap4_language, 0);
+ putWord16(result, startOfTable + ttf_cmap4_segCountX2, segCountX2);
+ putWord16(result, startOfTable + ttf_cmap4_searchRange, searchRange);
+ putWord16(result, startOfTable + ttf_cmap4_entrySelector, entrySelector);
+ putWord16(result, startOfTable + ttf_cmap4_rangeShift, segCountX2 - searchRange);
+
+ for (uint i = 0; i < segCount; ++i)
+ {
+ appendWord16(result, endCodes[i]);
+ };
+ appendWord16(result, 0); // reservedPad
+
+ for (uint i = 0; i < segCount; ++i)
+ {
+ appendWord16(result, startCodes[i]);
+ };
+ for (uint i = 0; i < segCount; ++i)
+ {
+ appendWord16(result, idDeltas[i]);
+ };
+
+
+ uint startOfIdRangeOffsetTable = result.size();
+ uint startOfGlyphIndexArray = startOfIdRangeOffsetTable + segCountX2;
+ result.resize(startOfGlyphIndexArray);
+
+ for (uint i = 0; i < segCount; ++i)
+ {
+ uint idRangeOffsetAddress = startOfIdRangeOffsetTable + 2*i;
+ if (rangeOffsets[i] == 0)
+ {
+ //quint16 dbg = startCodes[i] + idDeltas[i];
+ //qDebug() << QChar(startCodes[i]) << "-" << QChar(endCodes[i]) << "/" << (endCodes[i]-startCodes[i]+1) << "+" << idDeltas[i] << "-->" << dbg;
+ putWord16(result, idRangeOffsetAddress, 0);
+ }
+ else
+ {
+ quint16 idRangeOffset = result.size() - idRangeOffsetAddress;
+ putWord16(result, idRangeOffsetAddress, idRangeOffset);
+
+ //qDebug() << QChar(startCodes[i]) << "-" << QChar(endCodes[i]) << "/" << (endCodes[i]-startCodes[i]+1) << "@" << idRangeOffset << "+" << idDeltas[i];
+
+ uint startCode = startCodes[i];
+ uint segLength = (endCodes[i]-startCode+1);
+ for (uint offset = 0; offset < segLength; ++offset)
+ {
+ quint16 glyph = cmap[startCode + offset];
+ if (glyph != 0)
+ {
+ glyph -= idDeltas[i];
+ }
+ appendWord16(result, glyph);
+ }
+ }
+ };
+
+ putWord16(result, startOfTable + ttf_cmap4_length, result.size() - startOfTable);
+ return result;
+ }
+
+ QList<uint> copyGlyphComponents(QByteArray& destGlyf, const QByteArray& srcGlyf, uint srcOffset,
+ QMap<uint,uint>& newForOldGid, uint& nextFreeGid)
+ {
+ QList<uint> result;
+
+ uint destStart = destGlyf.size();
+ destGlyf.resize(destStart + ttf_glyf_headerSize);
+ copy(destGlyf, destStart, srcGlyf, srcOffset, ttf_glyf_headerSize);
+
+ uint pos = srcOffset + ttf_glyf_headerSize;
+ bool haveInstructions = false;
+ uint flags = 0;
+ do {
+ /* flags */
+ flags = word16(srcGlyf, pos);
+ pos += 2;
+ haveInstructions |= ((flags & ttf_glyf_ComponentFlag_WE_HAVE_INSTRUCTIONS) != 0);
+ appendWord16(destGlyf, flags);
+
+ /* glyphindex */
+ uint glyphIndex = word16(srcGlyf, pos);
+ pos += 2;
+ if (!newForOldGid.contains(glyphIndex))
+ {
+ result.append(glyphIndex);
+ newForOldGid[glyphIndex] = nextFreeGid++;
+ }
+ glyphIndex = newForOldGid[glyphIndex];
+ appendWord16(destGlyf, glyphIndex);
+
+ /* args */
+ if ( flags & ttf_glyf_ComponentFlag_ARG_1_AND_2_ARE_WORDS) {
+ appendWord16(destGlyf, word16(srcGlyf, pos)); // arg1
+ pos += 2;
+ appendWord16(destGlyf, word16(srcGlyf, pos)); // arg2
+ pos += 2;
+ }
+ else {
+ appendWord16(destGlyf, word16(srcGlyf, pos)); // arg1and2
+ pos += 2;
+ }
+ if ( flags & ttf_glyf_ComponentFlag_WE_HAVE_A_SCALE ) {
+ appendWord16(destGlyf, word16(srcGlyf, pos)); // scale
+ pos += 2;
+ } else if ( flags & ttf_glyf_ComponentFlag_WE_HAVE_AN_X_AND_Y_SCALE ) {
+ appendWord16(destGlyf, word16(srcGlyf, pos)); // xscale
+ pos += 2;
+ appendWord16(destGlyf, word16(srcGlyf, pos)); // yscale
+ pos += 2;
+ } else if ( flags & ttf_glyf_ComponentFlag_WE_HAVE_A_TWO_BY_TWO ) {
+ appendWord16(destGlyf, word16(srcGlyf, pos)); // xscale
+ pos += 2;
+ appendWord16(destGlyf, word16(srcGlyf, pos)); // scale01
+ pos += 2;
+ appendWord16(destGlyf, word16(srcGlyf, pos)); // scale10
+ pos += 2;
+ appendWord16(destGlyf, word16(srcGlyf, pos)); // yscale
+ pos += 2;
+ }
+ } while ( flags & ttf_glyf_ComponentFlag_MORE_COMPONENTS );
+
+ if (haveInstructions)
+ {
+ uint numInstr = word16(srcGlyf, pos);
+ appendWord16(destGlyf, numInstr);
+ pos += 2;
+ uint destPos = destGlyf.size();
+ destGlyf.resize(destPos + numInstr);
+ copy(destGlyf, destPos, srcGlyf, pos, numInstr);
+ }
+
+ return result;
+ }
+
+ QList<uint> copyGlyph(QList<uint>& destLoca, QByteArray& destGlyf, uint destGid,
+ const QList<uint>& srcLoca, const QByteArray& srcGlyf, uint srcGid,
+ QMap<uint,uint>& newForOldGid, uint& nextFreeGid)
+ {
+ QList<uint> compositeElements;
+ uint glyphStart = srcLoca[srcGid];
+ uint glyphLength = srcLoca[srcGid+1] - glyphStart;
+ destLoca.append(destGlyf.size());
+ //int i = 0;
+ if (glyphLength > 0)
+ {
+ quint16 nrOfContours = word16(srcGlyf, glyphStart);
+ if (nrOfContours <= ttf_glyf_Max_numberOfContours)
+ {
+ // simple glyph
+ uint destStart = destGlyf.size();
+ //qDebug() << i++ << ":" << nrOfContours << "contours" << glyphStart << "-->" << destStart << "/" << glyphLength;
+ destGlyf.resize(destStart + glyphLength);
+ copy(destGlyf, destStart, srcGlyf, glyphStart, glyphLength);
+ }
+ else
+ {
+ compositeElements.append(copyGlyphComponents(destGlyf, srcGlyf, glyphStart, newForOldGid, nextFreeGid));
+ //qDebug() << i++ << ":" << srcGid << "composite glyph brought" << compositeElements.size() << "more glyphs";
+ }
+ // Data must be aligned at least on 2 byte boundary, however
+ // a 4-byte alignment is recommended by TTF specs for reasons
+ // related to CPU and efficiency
+ int targetSize = (destGlyf.size() + 3) & ~3;
+ if (destGlyf.size() < targetSize)
+ {
+ destGlyf.reserve(targetSize);
+ while (destGlyf.size() < targetSize)
+ destGlyf.append('\0');
+ }
+ }
+
+ return compositeElements;
+ }
+
+ QByteArray subsetFace(const QByteArray& ttf, QList<uint>& glyphs, QMap<uint, uint>& glyphMap)
+ {
+ QMap<QByteArray,QByteArray> tables;
+
+ // qDebug() << "loca table:" << (void*) oldLoca[0] << (void*) oldLoca[1] << (void*) oldLoca[2] << (void*) oldLoca[3] << (void*) oldLoca[4] << (void*) oldLoca[5] << (void*) oldLoca[6] << (void*) oldLoca[7];
+
+ QMap<uint, uint> newForOldGid;
+ if (glyphs.length() == 0)
+ {
+ tables["loca"] = getTable(ttf, "loca");
+ tables["glyf"] = getTable(ttf, "glyf");
+ }
+ else
+ {
+ QList<uint> oldLoca = readLoca(ttf);
+ const QByteArray oldGlyf = getTable(ttf, "glyf");
+
+ QList<quint32> newLoca;
+ QByteArray newGlyf;
+ glyphs.removeAll(0);
+ glyphs.prepend(0);
+
+ for (int i = 0; i < glyphs.length(); ++i)
+ {
+ uint oldGid = glyphs[i];
+ newForOldGid[oldGid] = i;
+ }
+
+ uint nextFreeGid = glyphs.length();
+ for (int i = 0; i < glyphs.length(); ++i)
+ {
+ uint oldGid = glyphs[i];
+ glyphs.append(copyGlyph(newLoca, newGlyf, i,
+ oldLoca, oldGlyf, oldGid,
+ newForOldGid, nextFreeGid));
+ }
+ newLoca.append(newGlyf.length());
+
+ tables["loca"] = writeLoca(newLoca, hasLongLocaFormat(ttf));
+ tables["glyf"] = newGlyf;
+ }
+
+ QMap<uint,uint> cmap = readCMap(ttf);
+ QMap<uint,uint>::iterator it;
+ uint firstChar = 0xFFFFFFFF;
+ uint lastChar = 0;
+ for (it = cmap.begin(); it != cmap.end(); ++it)
+ {
+ if (glyphs.length() > 0 && !glyphs.contains(it.value()))
+ {
+ it.value() = 0;
+ }
+ else if (it.value() != 0)
+ {
+ if (glyphs.length() > 0)
+ {
+ //qDebug() << "MAP" << QChar(it.key()) << it.value() << "-->" << newForOldGid[it.value()];
+ it.value() = newForOldGid[it.value()];
+ }
+ if (it.key() < firstChar)
+ firstChar = it.key();
+ else if (it.key() > lastChar)
+ lastChar = it.key();
+ }
+ }
+ tables["cmap"] = writeCMap(cmap);
+
+ QByteArray os2 = getTable(ttf, "OS/2");
+ if (os2.length() > ttf_os2_usLastCharIndex)
+ {
+ // TODO: adapt unicode ranges
+ putWord16(os2, ttf_os2_usFirstCharIndex, firstChar < 0xFFFF ? firstChar : 0xFFFF);
+ putWord16(os2, ttf_os2_usLastCharIndex, lastChar < 0xFFFF ? lastChar : 0xFFFF);
+ tables["OS/2"] = os2;
+ }
+
+ if (glyphs.length() > 0)
+ {
+ QList<std::pair<qint16, quint16> > oldHmtx = readHmtx(ttf);
+ QList<std::pair<qint16, quint16> > newHmtx;
+ newHmtx.reserve(glyphs.length() + 1);
+ newHmtx.append(std::pair<qint16, quint16>(1234, 123));
+ for (int i = 1; i < glyphs.length(); ++i)
+ newHmtx.append(newHmtx[0]);
+ QMap<uint,uint>::const_iterator iter;
+ for (iter = newForOldGid.cbegin(); iter != newForOldGid.cend(); ++iter)
+ {
+ //qDebug() << "hmtx" << iter.key() << " -> " << iter.value() << "=" << oldHmtx[iter.key()].first;
+ newHmtx[iter.value()] = oldHmtx[iter.key()];
+ }
+ tables["hmtx"] = writeHmtx(newHmtx);
+ }
+ else
+ {
+ tables["hmtx"] = getTable(ttf, "hmtx");
+ }
+
+ QByteArray maxp = getTable(ttf, "maxp");
+ if (glyphs.length() > 0)
+ {
+ putWord16(maxp, ttf_maxp_numGlyphs, glyphs.length());
+ }
+ tables["maxp"] = maxp;
+
+ QByteArray hhea = getTable(ttf, "hhea");
+ if (glyphs.length() > 0)
+ {
+ putWord16(hhea, ttf_hhea_numOfLongHorMetrics, glyphs.length());
+ }
+ tables["hhea"] = hhea;
+
+ QByteArray post = getTable(ttf, "post");
+ if (word(post, ttf_post_format) != post_format30)
+ {
+ putWord(post, ttf_post_format, post_format30);
+ post.truncate(ttf_post_header_length);
+ }
+ tables["post"] = post;
+
+ // TODO: kern table
+
+ QByteArray name = getTable(ttf, "name");
+ if (name.length() > 0)
+ tables["name"] = name;
+
+ QByteArray prep = getTable(ttf, "prep");
+ if (prep.length() > 0)
+ tables["prep"] = prep;
+
+ QByteArray cvt = getTable(ttf, "cvt ");
+ if (cvt.length() > 0)
+ tables["cvt "] = cvt;
+
+ QByteArray fpgm = getTable(ttf, "fpgm");
+ if (fpgm.length() > 0)
+ tables["fpgm"] = fpgm;
+
+ QByteArray head = getTable(ttf, "head");
+ putWord(head, ttf_head_checkSumAdjustment, 0);
+ tables["head"] = head;
+
+ QByteArray font = createTableDir(tables.keys());
+ QMap<QByteArray,QByteArray>::iterator tableP;
+ for (tableP = tables.begin(); tableP != tables.end(); ++tableP)
+ {
+ writeTable(font, tableP.key(), tableP.value());
+ }
+
+ uint checkSumAdjustment = 0xB1B0AFBA - calcTableChecksum(font);
+ uint headTable = getTableDirEntry(font, "head");
+ headTable = word(font, headTable + ttf_TableRecord_offset);
+ putWord(font, headTable + ttf_head_checkSumAdjustment, checkSumAdjustment);
+
glyphMap.clear();
for (int i = 0; i < glyphs.length(); ++i)
- glyphMap[glyphs[i]] = i;
- // done!
-
- return font;
- }
-
- QByteArray subsetFaceWithHB(const QByteArray& cff, QList<uint> cids, int faceIndex, QMap<uint, uint>& glyphMap)
- {
+ glyphMap[glyphs[i]] = i;
+ // done!
+
+ return font;
+ }
+
+ QByteArray subsetFaceWithHB(const QByteArray& cff, QList<uint> cids, int faceIndex, QMap<uint, uint>& glyphMap)
+ {
glyphMap.clear();
-#if HB_VERSION_ATLEAST(2, 0, 0)
+#ifdef HAVE_HARFBUZZ_SUBSET
QScopedPointer<hb_blob_t, HbBlobDeleter> hbBlob(hb_blob_create(cff.data(), cff.length(), HB_MEMORY_MODE_READONLY, nullptr, nullptr));
if (hbBlob.isNull())
return QByteArray();
@@ -1272,19 +1278,19 @@
for (int i = 0; i < cids.length(); ++i)
glyphMap[cids[i]] = cids[i];
}
- return subset;
-#else
- return QByteArray();
-#endif
- }
-
- bool canSubsetOpenTypeFonts()
- {
-#if HB_VERSION_ATLEAST(2, 0, 0)
- return true;
-#else
- return false;
-#endif
- }
-
-} // namespace sfnt
+ return subset;
+#else
+ return QByteArray();
+#endif
+ }
+
+ bool canSubsetOpenTypeFonts()
+ {
+#ifdef HAVE_HARFBUZZ_SUBSET
+ return true;
+#else
+ return false;
+#endif
+ }
+
+} // namespace sfnt
Modified: trunk/Scribus/win32/msvc2015/win-config.h
URL: http://scribus.net/websvn/diff.php?repname=Scribus&rev=24469&path=/trunk/Scribus/win32/msvc2015/win-config.h
==============================================================================
--- trunk/Scribus/win32/msvc2015/win-config.h (original)
+++ trunk/Scribus/win32/msvc2015/win-config.h Thu Feb 11 19:12:18 2021
@@ -42,6 +42,9 @@
/* Defines if your system has the libfontconfig library */
#undef HAVE_FONTCONFIG
+
+/* Defines if your system has the harfbuzz-subset library */
+#define HAVE_HARFBUZZ_SUBSET 1
/* Define if you have libz */
#define HAVE_LIBZ 1
Modified: trunk/Scribus/win32/msvc2017/win-config.h
URL: http://scribus.net/websvn/diff.php?repname=Scribus&rev=24469&path=/trunk/Scribus/win32/msvc2017/win-config.h
==============================================================================
--- trunk/Scribus/win32/msvc2017/win-config.h (original)
+++ trunk/Scribus/win32/msvc2017/win-config.h Thu Feb 11 19:12:18 2021
@@ -42,6 +42,9 @@
/* Defines if your system has the libfontconfig library */
#undef HAVE_FONTCONFIG
+
+/* Defines if your system has the harfbuzz-subset library */
+#define HAVE_HARFBUZZ_SUBSET 1
/* Define if you have libz */
#define HAVE_LIBZ 1
Modified: trunk/Scribus/win32/msvc2019/win-config.h
URL: http://scribus.net/websvn/diff.php?repname=Scribus&rev=24469&path=/trunk/Scribus/win32/msvc2019/win-config.h
==============================================================================
--- trunk/Scribus/win32/msvc2019/win-config.h (original)
+++ trunk/Scribus/win32/msvc2019/win-config.h Thu Feb 11 19:12:18 2021
@@ -42,6 +42,9 @@
/* Defines if your system has the libfontconfig library */
#undef HAVE_FONTCONFIG
+
+/* Defines if your system has the harfbuzz-subset library */
+#define HAVE_HARFBUZZ_SUBSET 1
/* Define if you have libz */
#define HAVE_LIBZ 1
More information about the scribus-commit
mailing list