summaryrefslogtreecommitdiff
path: root/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/otfcmap.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/otfcmap.cc')
-rw-r--r--Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/otfcmap.cc474
1 files changed, 474 insertions, 0 deletions
diff --git a/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/otfcmap.cc b/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/otfcmap.cc
new file mode 100644
index 00000000000..93b90b3e60a
--- /dev/null
+++ b/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/otfcmap.cc
@@ -0,0 +1,474 @@
+// -*- related-file-name: "../include/efont/otfcmap.hh" -*-
+
+/* otfcmap.{cc,hh} -- OpenType cmap table
+ *
+ * Copyright (c) 2002-2012 Eddie Kohler
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <efont/otfcmap.hh>
+#include <lcdf/error.hh>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <efont/otfdata.hh> // for ntohl()
+
+#define USHORT_AT(d) (Data::u16_aligned(d))
+#define SHORT_AT(d) (Data::s16_aligned(d))
+#define ULONG_AT(d) (Data::u32_aligned(d))
+
+namespace Efont { namespace OpenType {
+
+Cmap::Cmap(const String &s, ErrorHandler *errh)
+ : _str(s)
+{
+ _str.align(4);
+ _error = parse_header(errh ? errh : ErrorHandler::silent_handler());
+}
+
+int
+Cmap::parse_header(ErrorHandler *errh)
+{
+ // HEADER FORMAT:
+ // USHORT version
+ // USHORT numTables
+ int len = _str.length();
+ const uint8_t *data = _str.udata();
+ if (HEADER_SIZE > len)
+ return errh->error("OTF cmap too small"), -EFAULT;
+ if (!(data[0] == '\000' && data[1] == '\000'))
+ return errh->error("bad cmap version number"), -ERANGE;
+ _ntables = USHORT_AT(data + 2);
+ if (_ntables == 0)
+ return errh->error("OTF cmap contains no tables"), -EINVAL;
+ if (HEADER_SIZE + ENCODING_SIZE * _ntables > len)
+ return errh->error("cmap directory out of range"), -EFAULT;
+
+ // ENCODING ENTRY FORMAT:
+ // USHORT platformID
+ // USHORT encodingID
+ // ULONG offset
+ int last_platform = -1;
+ int last_encoding = -1;
+ int last_language = -1;
+ _first_unicode_table = -1;
+ for (int i = 0; i < _ntables; i++) {
+ int loc = HEADER_SIZE + ENCODING_SIZE * i;
+ int platform = USHORT_AT(data + loc);
+ int encoding = USHORT_AT(data + loc + 2);
+ uint32_t offset = ULONG_AT(data + loc + 4);
+ if (offset + 8 > (uint32_t) len) {
+ length_error:
+ return errh->error("encoding data for entry %d out of range", i);
+ }
+ int format = USHORT_AT(data + offset);
+ int language;
+ if (format == F_BYTE || format == F_HIBYTE || format == F_SEGMENTED
+ || format == F_TRIMMED) {
+ if (USHORT_AT(data + offset + 2) < 6)
+ goto length_error;
+ language = USHORT_AT(data + offset + 4);
+ } else if (format == F_HIBYTE32 || format == F_TRIMMED32
+ || format == F_SEGMENTED32) {
+ if (offset + 12 > (uint32_t) len || ULONG_AT(data + offset + 4) < 12)
+ goto length_error;
+ language = ULONG_AT(data + offset + 8);
+ } else
+ continue;
+ if (!(platform > last_platform
+ || (platform == last_platform
+ && (encoding > last_encoding
+ || (encoding == last_encoding
+ && language > last_language)))))
+ return errh->error("subtables out of order at entry %d", i);
+ if ((platform == 0 || (platform == 3 && encoding == 1))
+ && _first_unicode_table < 0)
+ _first_unicode_table = i;
+ last_platform = platform, last_encoding = encoding, last_language = language;
+ }
+
+ _table_error.assign(_ntables, -2);
+ return 0;
+}
+
+int
+Cmap::first_table(int platform, int encoding) const
+{
+ if (error() < 0)
+ return -1;
+ const uint8_t *data = _str.udata();
+ data += HEADER_SIZE;
+ for (int i = 0; i < _ntables; i++, data += ENCODING_SIZE) {
+ int p = USHORT_AT(data), e = USHORT_AT(data + 2);
+ if (platform == p && (encoding < 0 || encoding == e))
+ return i;
+ }
+ return -1;
+}
+
+int
+Cmap::check_table(int t, ErrorHandler *errh) const
+{
+ if (!errh)
+ errh = ErrorHandler::silent_handler();
+ if (t == USE_FIRST_UNICODE_TABLE && _first_unicode_table == -1) {
+ errh->warning("font appears not to support Unicode");
+ _first_unicode_table = 0;
+ }
+ if (t == USE_FIRST_UNICODE_TABLE)
+ t = _first_unicode_table;
+ if (_error < 0 || t < 0 || t >= _ntables)
+ return errh->error("no such table");
+ if (_table_error[t] != -2)
+ return _table_error[t];
+ _table_error[t] = -1;
+
+ const uint8_t *data = table_data(t);
+ uint32_t left = _str.uend() - data;
+ int format = USHORT_AT(data);
+ uint32_t length = 0; // value not used
+
+ switch (format) {
+
+ case F_BYTE:
+ if (left < 4
+ || (length = USHORT_AT(data + 2)) > left
+ || length != 259)
+ return errh->error("bad table %d length (format %d)", t, format);
+ break;
+
+ case F_HIBYTE:
+ if (left < 4
+ || (length = USHORT_AT(data + 2)) > left
+ || length < 524)
+ return errh->error("bad table %d length (format %d)", t, format);
+ for (int hi_byte = 0; hi_byte < 256; hi_byte++)
+ if (uint32_t subh_key = USHORT_AT(data + 6 + 2 * hi_byte)) {
+ if ((subh_key & 7) || HIBYTE_SUBHEADERS + subh_key + 8 > length)
+ return errh->error("bad table %d subheader %d offset (format 2)", t, hi_byte);
+ const uint8_t *subh = data + HIBYTE_SUBHEADERS + subh_key;
+ int firstCode = USHORT_AT(subh);
+ int entryCount = USHORT_AT(subh + 2);
+ int idRangeOffset = USHORT_AT(subh + 6);
+ if (firstCode + entryCount > 256 || entryCount == 0)
+ return errh->error("bad table %d subheader %d contents (format 2)", t, hi_byte);
+ if ((HIBYTE_SUBHEADERS + subh_key + 6) // pos[idRangeOffset]
+ + idRangeOffset + entryCount * 2 > length)
+ return errh->error("bad table %d subheader %d length (format 2)", t, hi_byte);
+ }
+ break;
+
+ case F_SEGMENTED: {
+ if (left < 4
+ || (length = USHORT_AT(data + 2)) > left
+ || length < 16)
+ return errh->error("bad table %d length (format %d)", t, format);
+ int segCountX2 = USHORT_AT(data + 6);
+ int searchRange = USHORT_AT(data + 8);
+ int entrySelector = USHORT_AT(data + 10);
+ int rangeShift = USHORT_AT(data + 12);
+ if ((segCountX2 & 1)
+ || segCountX2 == 0
+ || (searchRange & (searchRange - 1)) /* not a power of 2? */
+ || searchRange <= segCountX2/2
+ || (searchRange>>1) > segCountX2/2
+ || 1 << (entrySelector + 1) != searchRange
+ || rangeShift != segCountX2 - searchRange)
+ return errh->error("bad table %d segment counts (format 4)", t);
+ uint32_t segCount = segCountX2 >> 1;
+ if (length < 16 + 8 * segCount)
+ return errh->error("bad table %d length (format %d)", t, format);
+ const uint8_t *endCodes = data + 14;
+ const uint8_t *startCodes = endCodes + 2 + segCountX2;
+ const uint8_t *idDeltas = startCodes + segCountX2;
+ const uint8_t *idRangeOffsets = idDeltas + segCountX2;
+ int last_end = 0;
+ for (int i = 0; i < segCountX2; i += 2) {
+ int endCode = USHORT_AT(endCodes + i);
+ int startCode = USHORT_AT(startCodes + i);
+ /* int idDelta = SHORT_AT(idDeltas + i); // no need to check */
+ int idRangeOffset = USHORT_AT(idRangeOffsets + i);
+ if (endCode < startCode || startCode < last_end)
+ return errh->error("bad table %d overlapping range %d (format %d)", t, i/2, format);
+ if (idRangeOffset
+ && idRangeOffsets + i + idRangeOffset + (endCode - startCode)*2 + 2 > data + length)
+ return errh->error("bad table %d range %d length (format 4)", t, i/2);
+ last_end = endCode + 1;
+ }
+ if (USHORT_AT(endCodes + segCountX2 - 2) != 0xFFFF)
+ return errh->error("bad table %d incorrect final endCode (format 4)", t);
+ break;
+ }
+
+ case F_TRIMMED: {
+ if (left < 4
+ || (length = USHORT_AT(data + 2)) > left
+ || length < 10)
+ return errh->error("bad table %d length (format %d)", t, format);
+ uint32_t entryCount = USHORT_AT(data + 8);
+ if (10 + entryCount * 2 > length)
+ return errh->error("bad table %d length (format %d)", t, format);
+ break;
+ }
+
+ case F_SEGMENTED32: {
+ if (left < 8
+ || (length = ULONG_AT(data + 4)) > left
+ || length < 16)
+ return errh->error("bad table %d length (format %d)", t, format);
+ uint32_t nGroups = ULONG_AT(data + 16);
+ if ((length - 16) / 12 < nGroups)
+ return errh->error("bad table %d length (format %d)", t, format);
+ uint32_t last_post_end = 0;
+ data += 16;
+ for (uint32_t i = 0; i < nGroups; i++, data += 12) {
+ uint32_t startCharCode = ULONG_AT(data);
+ uint32_t endCharCode = ULONG_AT(data + 4);
+ if (startCharCode < last_post_end || endCharCode < startCharCode)
+ return errh->error("bad table %d overlapping range %d (format %d)", t, i, format);
+ last_post_end = endCharCode + 1;
+ }
+ break;
+ }
+
+ case F_HIBYTE32:
+ case F_TRIMMED32:
+ default:
+ return errh->error("bad table %d unsupported format %d", t, format);
+
+ }
+
+ _table_error[t] = t;
+ return t;
+}
+
+Glyph
+Cmap::map_table(int t, uint32_t uni, ErrorHandler *errh) const
+{
+ if ((t = check_table(t, errh)) < 0)
+ return 0;
+
+ const uint8_t *data = table_data(t);
+ switch (USHORT_AT(data)) {
+
+ case F_BYTE:
+ if (uni < 256)
+ return data[6 + uni];
+ else
+ return 0;
+
+ case F_HIBYTE: {
+ if (uni >= 65536)
+ return 0;
+ int hi_byte = (uni >> 8) & 255;
+ int subh = USHORT_AT(data + 6 + hi_byte * 2);
+ if (subh == 0 && hi_byte) // XXX?
+ return 0;
+ data += 524 + subh;
+ int firstCode = USHORT_AT(data);
+ int entryCount = USHORT_AT(data + 2);
+ int idDelta = SHORT_AT(data + 4);
+ int idRangeOffset = USHORT_AT(data + 6);
+ int lo_byte = uni & 255;
+ if (lo_byte < firstCode || lo_byte >= firstCode + entryCount)
+ return 0;
+ int answer = USHORT_AT(data + 6 + idRangeOffset + (lo_byte - firstCode) * 2);
+ if (answer == 0)
+ return 0;
+ return (answer + idDelta) & 65535;
+ }
+
+ case F_SEGMENTED: {
+ if (uni >= 65536)
+ return 0;
+ int segCount = USHORT_AT(data + 6) >> 1;
+ const uint8_t *endCounts = data + 14;
+ const uint8_t *startCounts = endCounts + (segCount << 1) + 2;
+ const uint8_t *idDeltas = startCounts + (segCount << 1);
+ const uint8_t *idRangeOffsets = idDeltas + (segCount << 1);
+ int l = 0, r = segCount;
+ while (l < r) {
+ int m = l + (r - l) / 2;
+ uint32_t endCount = USHORT_AT(endCounts + (m << 1));
+ uint32_t startCount = USHORT_AT(startCounts + (m << 1));
+ if (uni < startCount)
+ r = m;
+ else if (uni <= endCount) {
+ int idDelta = SHORT_AT(idDeltas + (m << 1));
+ int idRangeOffset = USHORT_AT(idRangeOffsets + (m << 1));
+ if (idRangeOffset == 0)
+ return (idDelta + uni) & 65535;
+ int g = USHORT_AT(idRangeOffsets + (m << 1) + idRangeOffset + ((uni - startCount) << 1));
+ if (g == 0)
+ return 0;
+ return (idDelta + g) & 65535;
+ } else
+ l = m + 1;
+ }
+ return 0;
+ }
+
+ case F_TRIMMED: {
+ uint32_t firstCode = USHORT_AT(data + 6);
+ uint32_t entryCount = USHORT_AT(data + 8);
+ if (uni < firstCode || uni >= firstCode + entryCount)
+ return 0;
+ return USHORT_AT(data + 10 + ((uni - firstCode) << 1));
+ }
+
+ case F_SEGMENTED32: {
+ uint32_t nGroups = ULONG_AT(data + 12);
+ uint32_t l = 0, r = nGroups;
+ const uint8_t *groups = data + 16;
+ while (l < r) {
+ uint32_t m = l + (r - l) / 2;
+ uint32_t startCharCode = ULONG_AT(groups + m * 12);
+ uint32_t endCharCode = ULONG_AT(groups + m * 12 + 4);
+ if (uni < startCharCode)
+ r = m;
+ else if (uni <= endCharCode)
+ return ULONG_AT(groups + m * 12 + 8) + uni - startCharCode;
+ else
+ l = m + 1;
+ }
+ return 0;
+ }
+
+ default:
+ return 0;
+
+ }
+}
+
+void
+Cmap::dump_table(int t, Vector<uint32_t> &g2c, ErrorHandler *errh) const
+{
+ if ((t = check_table(t, errh)) < 0)
+ return;
+
+ const uint8_t *data = table_data(t);
+ switch (USHORT_AT(data)) {
+
+ case F_BYTE:
+ g2c.resize(256, 0);
+ for (int c = 0; c < 256; c++)
+ if (int g = data[6 + c])
+ if (!g2c[g])
+ g2c[g] = c;
+ break;
+
+ case F_HIBYTE:
+ assert(USHORT_AT(data + 6) == 0);
+ for (int hi_byte = 0; hi_byte < 256; hi_byte++) {
+ int subh = USHORT_AT(data + 6 + hi_byte * 4);
+ if (subh == 0 && hi_byte > 0)
+ continue;
+ const uint8_t *tdata = data + 524 + subh;
+ int firstCode = USHORT_AT(tdata);
+ int entryCount = USHORT_AT(tdata + 2);
+ int idDelta = SHORT_AT(tdata + 4);
+ int idRangeOffset = USHORT_AT(tdata + 6);
+ const uint8_t *gdata = tdata + 6 + idRangeOffset;
+ for (int i = 0; i < entryCount; i++)
+ if (int g = USHORT_AT(gdata + (i << 1))) {
+ g = (idDelta + g) & 65535;
+ if (g >= g2c.size())
+ g2c.resize(g + 1, 0);
+ if (!g2c[g])
+ g2c[g] = (hi_byte << 8) + firstCode + i;
+ }
+ }
+ break;
+
+ case F_SEGMENTED: {
+ int segCountX2 = USHORT_AT(data + 6);
+ const uint8_t *endCounts = data + 14;
+ const uint8_t *startCounts = endCounts + segCountX2 + 2;
+ const uint8_t *idDeltas = startCounts + segCountX2;
+ const uint8_t *idRangeOffsets = idDeltas + segCountX2;
+ for (int i = 0; i < segCountX2; i += 2) {
+ int endCount = USHORT_AT(endCounts + i);
+ int startCount = USHORT_AT(startCounts + i);
+ int idDelta = SHORT_AT(idDeltas + i);
+ int idRangeOffset = USHORT_AT(idRangeOffsets + i);
+ if (idRangeOffset == 0) {
+ for (int c = startCount; c <= endCount; c++) {
+ Glyph g = (c + idDelta) & 65535;
+ if (g >= g2c.size())
+ g2c.resize(g + 1, 0);
+ if (!g2c[g])
+ g2c[g] = c;
+ }
+ } else {
+ const uint8_t *gdata = idRangeOffsets + i + idRangeOffset;
+ for (int c = startCount; c <= endCount; c++, gdata += 2)
+ if (int g = USHORT_AT(gdata)) {
+ g = (g + idDelta) & 65535;
+ if (g >= g2c.size())
+ g2c.resize(g + 1, 0);
+ if (!g2c[g])
+ g2c[g] = c;
+ }
+ }
+ }
+ break;
+ }
+
+ case F_TRIMMED: {
+ int firstCode = USHORT_AT(data + 6);
+ int entryCount = USHORT_AT(data + 8);
+ for (int i = 0; i < entryCount; i++)
+ if (int g = USHORT_AT(data + 10 + (i << 1))) {
+ if (g >= g2c.size())
+ g2c.resize(g + 1, 0);
+ if (!g2c[g])
+ g2c[g] = firstCode + i;
+ }
+ break;
+ }
+
+ case F_SEGMENTED32: {
+ uint32_t nGroups = ULONG_AT(data + 12);
+ const uint8_t *groups = data + 16;
+ for (uint32_t i = 0; i < nGroups; i++, groups += 12) {
+ uint32_t startCharCode = ULONG_AT(groups);
+ uint32_t nCharCodes = ULONG_AT(groups + 4) - startCharCode;
+ uint32_t startGlyphID = ULONG_AT(groups + 8);
+ if (startGlyphID + nCharCodes >= (uint32_t) g2c.size())
+ g2c.resize(startGlyphID + nCharCodes + 1, 0);
+ for (uint32_t i = 0; i <= nCharCodes; i++)
+ if (!g2c[startGlyphID + i])
+ g2c[startGlyphID + i] = startCharCode + i;
+ }
+ break;
+ }
+
+ default:
+ break;
+
+ }
+}
+
+int
+Cmap::map_uni(const Vector<uint32_t> &vin, Vector<Glyph> &vout) const
+{
+ int t;
+ if ((t = check_table(USE_FIRST_UNICODE_TABLE)) < 0)
+ return -1;
+ vout.resize(vin.size(), 0);
+ for (int i = 0; i < vin.size(); i++)
+ vout[i] = map_table(t, vin[i]);
+ return 0;
+}
+
+}}