From 93b2f860f9fa5c73773013b29d152b1723108140 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 21 Oct 2012 15:09:07 +0530 Subject: [PATCH] Add code to convert between WOFF<->sfnt font files --- setup/extensions.py | 14 +- src/calibre/constants.py | 1 + src/calibre/test_build.py | 5 + src/calibre/utils/fonts/woff/__init__.py | 36 + src/calibre/utils/fonts/woff/main.c | 108 ++ src/calibre/utils/fonts/woff/woff-private.h | 151 +++ src/calibre/utils/fonts/woff/woff.c | 1170 +++++++++++++++++++ src/calibre/utils/fonts/woff/woff.h | 211 ++++ 8 files changed, 1694 insertions(+), 2 deletions(-) create mode 100644 src/calibre/utils/fonts/woff/__init__.py create mode 100644 src/calibre/utils/fonts/woff/main.c create mode 100644 src/calibre/utils/fonts/woff/woff-private.h create mode 100644 src/calibre/utils/fonts/woff/woff.c create mode 100644 src/calibre/utils/fonts/woff/woff.h diff --git a/setup/extensions.py b/setup/extensions.py index 2cae3d857f..8d49f904b7 100644 --- a/setup/extensions.py +++ b/setup/extensions.py @@ -50,9 +50,9 @@ class Extension(object): reflow_sources = glob.glob(os.path.join(SRC, 'calibre', 'ebooks', 'pdf', '*.cpp')) reflow_headers = glob.glob(os.path.join(SRC, 'calibre', 'ebooks', 'pdf', '*.h')) -pdfreflow_libs = [] +woff_libs = ['z'] if iswindows: - pdfreflow_libs = ['advapi32', 'User32', 'Gdi32', 'zlib'] + woff_libs = ['zlib'] icu_libs = ['icudata', 'icui18n', 'icuuc', 'icuio'] icu_cflags = [] @@ -132,6 +132,16 @@ extensions = [ lib_dirs=[fc_lib], error=fc_error), + Extension('woff', + ['calibre/utils/fonts/woff/main.c', + 'calibre/utils/fonts/woff/woff.c'], + headers=[ + 'calibre/utils/fonts/woff/woff.h', + 'calibre/utils/fonts/woff/woff-private.h'], + libraries=woff_libs + ), + + Extension('msdes', ['calibre/utils/msdes/msdesmodule.c', 'calibre/utils/msdes/des.c'], diff --git a/src/calibre/constants.py b/src/calibre/constants.py index fe3c40309e..6bc9995384 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -90,6 +90,7 @@ class Plugins(collections.Mapping): 'icu', 'speedup', 'freetype', + 'woff', ] if iswindows: plugins.extend(['winutil', 'wpd', 'winfonts']) diff --git a/src/calibre/test_build.py b/src/calibre/test_build.py index e6e8611ac5..8bf3427f8c 100644 --- a/src/calibre/test_build.py +++ b/src/calibre/test_build.py @@ -114,6 +114,10 @@ def test_wpd(): else: wpd.uninit() +def test_woff(): + from calibre.utils.fonts.woff import test + test() + def test(): test_plugins() test_lxml() @@ -124,6 +128,7 @@ def test(): test_imaging() test_unrar() test_icu() + test_woff() if iswindows: test_win32() test_winutil() diff --git a/src/calibre/utils/fonts/woff/__init__.py b/src/calibre/utils/fonts/woff/__init__.py new file mode 100644 index 0000000000..8684c03489 --- /dev/null +++ b/src/calibre/utils/fonts/woff/__init__.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2012, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +from calibre.constants import plugins + +def get_woff(): + woff, woff_err = plugins['woff'] + if woff_err: + raise RuntimeError('Failed to load the WOFF plugin: %s'%woff_err) + return woff + +def to_woff(raw): + woff = get_woff() + return woff.to_woff(raw) + +def from_woff(raw): + woff = get_woff() + return woff.from_woff(raw) + +def test(): + sfnt = P('fonts/calibreSymbols.otf', data=True) + woff = to_woff(sfnt) + recon = from_woff(woff) + if recon != sfnt: + raise ValueError('WOFF roundtrip resulted in different sfnt') + +if __name__ == '__main__': + test() + + diff --git a/src/calibre/utils/fonts/woff/main.c b/src/calibre/utils/fonts/woff/main.c new file mode 100644 index 0000000000..827c1365d5 --- /dev/null +++ b/src/calibre/utils/fonts/woff/main.c @@ -0,0 +1,108 @@ +/* + * main.c + * Copyright (C) 2012 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + + +#define _UNICODE +#define UNICODE +#define PY_SSIZE_T_CLEAN +#include + +#include "woff.h" + +static PyObject *WOFFError = NULL; + +static PyObject* woff_err(uint32_t status) { + const char *msg; + switch(status) { + case eWOFF_out_of_memory: + return PyErr_NoMemory(); + case eWOFF_invalid: + msg = "Invalid input data"; break; + case eWOFF_compression_failure: + msg = "Compression failed"; break; + case eWOFF_bad_signature: + msg = "Bad font signature"; break; + case eWOFF_buffer_too_small: + msg = "Buffer too small"; break; + case eWOFF_bad_parameter: + msg = "Bad parameter"; break; + case eWOFF_illegal_order: + msg = "Illegal order of WOFF chunks"; break; + default: + msg = "Unknown Error"; + } + PyErr_SetString(WOFFError, msg); + return NULL; +} + +static PyObject* +to_woff(PyObject *self, PyObject *args) { + const char *sfnt; + char *woff = NULL; + Py_ssize_t sz; + uint32_t wofflen = 0, status = eWOFF_ok; + PyObject *ans; + + if (!PyArg_ParseTuple(args, "s#", &sfnt, &sz)) return NULL; + + woff = (char*)woffEncode((uint8_t*)sfnt, sz, 0, 0, &wofflen, &status); + + if (WOFF_FAILURE(status) || woff == NULL) return woff_err(status); + + ans = Py_BuildValue("s#", woff, wofflen); + free(woff); + return ans; +} + +static PyObject* +from_woff(PyObject *self, PyObject *args) { + const char *woff; + char *sfnt; + Py_ssize_t sz; + uint32_t sfntlen = 0, status = eWOFF_ok; + PyObject *ans; + + if (!PyArg_ParseTuple(args, "s#", &woff, &sz)) return NULL; + + sfnt = (char*)woffDecode((uint8_t*)woff, sz, &sfntlen, &status); + + if (WOFF_FAILURE(status) || sfnt == NULL) return woff_err(status); + ans = Py_BuildValue("s#", sfnt, sfntlen); + free(sfnt); + return ans; +} + +static +PyMethodDef methods[] = { + {"to_woff", (PyCFunction)to_woff, METH_VARARGS, + "to_woff(bytestring) -> Convert the sfnt data in bytestring to WOFF format (returned as a bytestring)." + }, + + {"from_woff", (PyCFunction)from_woff, METH_VARARGS, + "from_woff(bytestring) -> Convert the woff data in bytestring to SFNT format (returned as a bytestring)." + }, + + + {NULL, NULL, 0, NULL} +}; + +PyMODINIT_FUNC +initwoff(void) { + PyObject *m; + + m = Py_InitModule3( + "woff", methods, + "Convert to/from the WOFF<->sfnt font formats" + ); + if (m == NULL) return; + + WOFFError = PyErr_NewException((char*)"woff.WOFFError", NULL, NULL); + if (WOFFError == NULL) return; + PyModule_AddObject(m, "WOFFError", WOFFError); +} + + diff --git a/src/calibre/utils/fonts/woff/woff-private.h b/src/calibre/utils/fonts/woff/woff-private.h new file mode 100644 index 0000000000..8a41aa0c92 --- /dev/null +++ b/src/calibre/utils/fonts/woff/woff-private.h @@ -0,0 +1,151 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is WOFF font packaging code. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jonathan Kew + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef WOFF_PRIVATE_H_ +#define WOFF_PRIVATE_H_ + +#include "woff.h" + +/* private definitions used in the WOFF encoder/decoder functions */ + +/* create an OT tag from 4 characters */ +#define TAG(a,b,c,d) ((a)<<24 | (b)<<16 | (c)<<8 | (d)) + +#define WOFF_SIGNATURE TAG('w','O','F','F') + +#define SFNT_VERSION_CFF TAG('O','T','T','O') +#define SFNT_VERSION_TT 0x00010000 +#define SFNT_VERSION_true TAG('t','r','u','e') + +#define TABLE_TAG_DSIG TAG('D','S','I','G') +#define TABLE_TAG_head TAG('h','e','a','d') +#define TABLE_TAG_bhed TAG('b','h','e','d') + +#define SFNT_CHECKSUM_CALC_CONST 0xB1B0AFBAU /* from the TT/OT spec */ + +#ifdef WOFF_MOZILLA_CLIENT +# include +# define READ32BE(x) PR_ntohl(x) +# define READ16BE(x) PR_ntohs(x) +#else +/* These macros to read values as big-endian only work on "real" variables, + not general expressions, because of the use of &(x), but they are + designed to work on both BE and LE machines without the need for a + configure check. For production code, we might want to replace this + with something more efficient. */ +/* read a 32-bit BigEndian value */ +# define READ32BE(x) ( ( (uint32_t) ((uint8_t*)&(x))[0] << 24 ) + \ + ( (uint32_t) ((uint8_t*)&(x))[1] << 16 ) + \ + ( (uint32_t) ((uint8_t*)&(x))[2] << 8 ) + \ + (uint32_t) ((uint8_t*)&(x))[3] ) +/* read a 16-bit BigEndian value */ +# define READ16BE(x) ( ( (uint16_t) ((uint8_t*)&(x))[0] << 8 ) + \ + (uint16_t) ((uint8_t*)&(x))[1] ) +#endif + +#pragma pack(push,1) + +typedef struct { + uint32_t version; + uint16_t numTables; + uint16_t searchRange; + uint16_t entrySelector; + uint16_t rangeShift; +} sfntHeader; + +typedef struct { + uint32_t tag; + uint32_t checksum; + uint32_t offset; + uint32_t length; +} sfntDirEntry; + +typedef struct { + uint32_t signature; + uint32_t flavor; + uint32_t length; + uint16_t numTables; + uint16_t reserved; + uint32_t totalSfntSize; + uint16_t majorVersion; + uint16_t minorVersion; + uint32_t metaOffset; + uint32_t metaCompLen; + uint32_t metaOrigLen; + uint32_t privOffset; + uint32_t privLen; +} woffHeader; + +typedef struct { + uint32_t tag; + uint32_t offset; + uint32_t compLen; + uint32_t origLen; + uint32_t checksum; +} woffDirEntry; + +typedef struct { + uint32_t version; + uint32_t fontRevision; + uint32_t checkSumAdjustment; + uint32_t magicNumber; + uint16_t flags; + uint16_t unitsPerEm; + uint32_t created[2]; + uint32_t modified[2]; + int16_t xMin; + int16_t yMin; + int16_t xMax; + int16_t yMax; + uint16_t macStyle; + uint16_t lowestRecPpem; + int16_t fontDirectionHint; + int16_t indexToLocFormat; + int16_t glyphDataFormat; +} sfntHeadTable; + +#define HEAD_TABLE_SIZE 54 /* sizeof(sfntHeadTable) may report 56 because of alignment */ + +typedef struct { + uint32_t offset; + uint16_t oldIndex; + uint16_t newIndex; +} tableOrderRec; + +#pragma pack(pop) + +#endif diff --git a/src/calibre/utils/fonts/woff/woff.c b/src/calibre/utils/fonts/woff/woff.c new file mode 100644 index 0000000000..4dcaadb00f --- /dev/null +++ b/src/calibre/utils/fonts/woff/woff.c @@ -0,0 +1,1170 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is WOFF font packaging code. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jonathan Kew + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "woff-private.h" + +#include +#include +#include +#include + +#ifdef WOFF_MOZILLA_CLIENT /* define this when building as part of Gecko */ +# include "prmem.h" +# define malloc PR_Malloc +# define realloc PR_Realloc +# define free PR_Free +#endif + +/* + * Just simple whole-file encoding and decoding functions; a more extensive + * WOFF library could provide support for accessing individual tables from a + * compressed font, alternative options for memory allocation/ownership and + * error handling, etc. + */ + +/* on errors, each function sets a status variable and jumps to failure: */ +#undef FAIL +#define FAIL(err) do { status |= err; goto failure; } while (0) + +/* adjust an offset for longword alignment */ +#define LONGALIGN(x) (((x) + 3) & ~3) + +static int +compareOffsets(const void * lhs, const void * rhs) +{ + const tableOrderRec * a = (const tableOrderRec *) lhs; + const tableOrderRec * b = (const tableOrderRec *) rhs; + /* don't simply return a->offset - b->offset because these are unsigned + offset values; could convert to int, but possible integer overflow */ + return a->offset > b->offset ? 1 : + a->offset < b->offset ? -1 : + 0; +} + +#ifndef WOFF_MOZILLA_CLIENT + +/******************************************************************/ +/* * * * * * * * * * * * * * ENCODING * * * * * * * * * * * * * * */ +/******************************************************************/ + +static uint32_t +calcChecksum(const sfntDirEntry * dirEntry, + const uint8_t * sfntData, uint32_t sfntLen) +{ + /* just returns zero on errors, they will be detected again elsewhere */ + const uint32_t * csumPtr; + const uint32_t * csumEnd; + uint32_t csum = 0; + uint32_t length = LONGALIGN(READ32BE(dirEntry->length)); + uint32_t offset = READ32BE(dirEntry->offset); + uint32_t tag; + if ((offset & 3) != 0) { + return csum; + } + if (length > sfntLen || offset > sfntLen - length) { + return csum; + } + csumPtr = (const uint32_t *) (sfntData + offset); + csumEnd = csumPtr + length / 4; + while (csumPtr < csumEnd) { + csum += READ32BE(*csumPtr); + csumPtr++; + } + tag = READ32BE(dirEntry->tag); + if (tag == TABLE_TAG_head || tag == TABLE_TAG_bhed) { + const sfntHeadTable * head; + if (length < HEAD_TABLE_SIZE) { + return 0; + } + head = (const sfntHeadTable *)(sfntData + offset); + csum -= READ32BE(head->checkSumAdjustment); + } + return csum; +} + +const uint8_t * +woffEncode(const uint8_t * sfntData, uint32_t sfntLen, + uint16_t majorVersion, uint16_t minorVersion, + uint32_t * woffLen, uint32_t * pStatus) +{ + uint8_t * woffData = NULL; + tableOrderRec * tableOrder = NULL; + + uint32_t tableOffset; + uint32_t totalSfntSize; + + uint16_t numOrigTables; + uint16_t numTables; + uint16_t tableIndex; + uint16_t order; + const sfntDirEntry * sfntDir; + uint32_t tableBase; + uint32_t checkSumAdjustment = 0; + woffHeader * newHeader; + uint32_t tag = 0; + uint32_t removedDsigSize = 0; + uint32_t status = eWOFF_ok; + + const sfntHeader * header = (const sfntHeader *) (sfntData); + const sfntHeadTable * head = NULL; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return NULL; + } + + if (READ32BE(header->version) != SFNT_VERSION_TT && + READ32BE(header->version) != SFNT_VERSION_CFF && + READ32BE(header->version) != SFNT_VERSION_true) { + status |= eWOFF_warn_unknown_version; + } + + numOrigTables = READ16BE(header->numTables); + sfntDir = (const sfntDirEntry *) (sfntData + sizeof(sfntHeader)); + + for (tableIndex = 0; tableIndex < numOrigTables; ++tableIndex) { + /* validate table checksums, to figure out if we need to drop DSIG; + also check that table directory is correctly sorted */ + uint32_t prevTag = tag; + uint32_t csum = calcChecksum(&sfntDir[tableIndex], sfntData, sfntLen); + if (csum != READ32BE(sfntDir[tableIndex].checksum)) { + status |= eWOFF_warn_checksum_mismatch; + } + checkSumAdjustment += csum; + tag = READ32BE(sfntDir[tableIndex].tag); + if (tag <= prevTag) { + FAIL(eWOFF_invalid); + } + if (tag == TABLE_TAG_head || tag == TABLE_TAG_bhed) { + if (READ32BE(sfntDir[tableIndex].length) < HEAD_TABLE_SIZE) { + FAIL(eWOFF_invalid); + } + head = (const sfntHeadTable *)(sfntData + + READ32BE(sfntDir[tableIndex].offset)); + } + } + if (!head) { + FAIL(eWOFF_invalid); + } + if ((status & eWOFF_warn_checksum_mismatch) == 0) { + /* no point even checking if we already have an error, + as fixing that will change the overall checksum too */ + const uint32_t * csumPtr = (const uint32_t *) sfntData; + const uint32_t * csumEnd = csumPtr + 3 + 4 * numOrigTables; + while (csumPtr < csumEnd) { + checkSumAdjustment += READ32BE(*csumPtr); + ++csumPtr; + } + checkSumAdjustment = 0xB1B0AFBA - checkSumAdjustment; + if (checkSumAdjustment != READ32BE(head->checkSumAdjustment)) { + status |= eWOFF_warn_checksum_mismatch; + } + } + + /* Fixing checkSumAdjustment is tricky, because if there's a DSIG table, + we're going to have to remove that, which in turn means that table + offsets in the directory will all change. + And recalculating checkSumAdjustment requires taking account of any + individual table checksum corrections, but they have not actually been + applied to the sfnt data at this point. + And finally, we'd need to get the corrected checkSumAdjustment into the + encoded head table (but we can't modify the original sfnt data). + An easier way out seems to be to go ahead and encode the font, knowing + that checkSumAdjustment will be wrong; then (if the status flag + eWOFF_warn_checksum_mismatch is set) we'll decode the font back to + sfnt format. This will fix up the checkSumAdjustment (and return a + warning status). We'll ignore that warning, and then re-encode the + new, cleaned-up sfnt to get the final WOFF data. Perhaps not the most + efficient approach, but it seems simpler than trying to predict the + correct final checkSumAdjustment and incorporate it into the head + table on the fly. */ + + tableOrder = (tableOrderRec *) malloc(numOrigTables * sizeof(tableOrderRec)); + if (!tableOrder) { + FAIL(eWOFF_out_of_memory); + } + for (tableIndex = 0, numTables = 0; + tableIndex < numOrigTables; ++tableIndex) { + if ((status & eWOFF_warn_checksum_mismatch) != 0) { + /* check for DSIG table that we must drop if we're fixing checksums */ + tag = READ32BE(sfntDir[tableIndex].tag); + if (tag == TABLE_TAG_DSIG) { + status |= eWOFF_warn_removed_DSIG; + removedDsigSize = READ32BE(sfntDir[tableIndex].length); + continue; + } + } + tableOrder[numTables].offset = READ32BE(sfntDir[tableIndex].offset); + tableOrder[numTables].oldIndex = tableIndex; + tableOrder[numTables].newIndex = numTables; + ++numTables; + } + qsort(tableOrder, numTables, sizeof(tableOrderRec), compareOffsets); + + /* initially, allocate space for header and directory */ + tableOffset = sizeof(woffHeader) + numTables * sizeof(woffDirEntry); + woffData = (uint8_t *) malloc(tableOffset); + if (!woffData) { + FAIL(eWOFF_out_of_memory); + } + + /* accumulator for total expected size of decoded font */ + totalSfntSize = sizeof(sfntHeader) + numTables * sizeof(sfntDirEntry); + +/* + * We use a macro for this rather than creating a variable because woffData + * will get reallocated during encoding. The macro avoids the risk of using a + * stale pointer, and the compiler should optimize multiple successive uses. + */ +#define WOFFDIR ((woffDirEntry *) (woffData + sizeof(woffHeader))) + + for (order = 0; order < numTables; ++order) { + uLong sourceLen, destLen; + uint32_t sourceOffset; + + uint16_t oldIndex = tableOrder[order].oldIndex; + uint16_t newIndex = tableOrder[order].newIndex; + + WOFFDIR[newIndex].tag = sfntDir[oldIndex].tag; + if ((status & eWOFF_warn_checksum_mismatch) != 0) { + uint32_t csum = calcChecksum(&sfntDir[oldIndex], sfntData, sfntLen); + WOFFDIR[newIndex].checksum = READ32BE(csum); + } else { + WOFFDIR[newIndex].checksum = sfntDir[oldIndex].checksum; + } + WOFFDIR[newIndex].origLen = sfntDir[oldIndex].length; + WOFFDIR[newIndex].offset = READ32BE(tableOffset); + + /* allocate enough space for upper bound of compressed size */ + sourceOffset = READ32BE(sfntDir[oldIndex].offset); + if ((sourceOffset & 3) != 0) { + status |= eWOFF_warn_misaligned_table; + } + sourceLen = READ32BE(sfntDir[oldIndex].length); + if (sourceLen > sfntLen || sourceOffset > sfntLen - sourceLen) { + FAIL(eWOFF_invalid); + } + destLen = LONGALIGN(compressBound(sourceLen)); + woffData = (uint8_t *) realloc(woffData, tableOffset + destLen); + if (!woffData) { + FAIL(eWOFF_out_of_memory); + } + + /* do the compression directly into the WOFF data block */ + if (compress2((Bytef *) (woffData + tableOffset), &destLen, + (const Bytef *) (sfntData + sourceOffset), + sourceLen, 9) != Z_OK) { + FAIL(eWOFF_compression_failure); + } + if (destLen < sourceLen) { + /* compressed table was smaller */ + tableOffset += destLen; + WOFFDIR[newIndex].compLen = READ32BE(destLen); + } else { + /* compression didn't make it smaller, so store original data instead */ + destLen = sourceLen; + /* reallocate to ensure enough space for the table, + plus potential padding after it */ + woffData = (uint8_t *) realloc(woffData, + tableOffset + LONGALIGN(sourceLen)); + if (!woffData) { + FAIL(eWOFF_out_of_memory); + } + /* copy the original data into place */ + memcpy(woffData + tableOffset, + sfntData + READ32BE(sfntDir[oldIndex].offset), sourceLen); + tableOffset += sourceLen; + WOFFDIR[newIndex].compLen = WOFFDIR[newIndex].origLen; + } + + /* we always realloc woffData to a long-aligned size, so this is safe */ + while ((tableOffset & 3) != 0) { + woffData[tableOffset++] = 0; + } + + /* update total size of uncompressed OpenType with table size */ + totalSfntSize += sourceLen; + totalSfntSize = LONGALIGN(totalSfntSize); + } + + if (totalSfntSize > sfntLen) { + if (totalSfntSize > LONGALIGN(sfntLen)) { + FAIL(eWOFF_invalid); + } else { + status |= eWOFF_warn_unpadded_table; + } + } else if (totalSfntSize < sfntLen) { + /* check if the remaining data is a DSIG we're removing; + if so, we're already warning about that */ + if ((status & eWOFF_warn_removed_DSIG) != 0 || + sfntLen - totalSfntSize > + LONGALIGN(removedDsigSize) + sizeof(sfntDirEntry)) { + status |= eWOFF_warn_trailing_data; + } + } + + /* write the header */ + newHeader = (woffHeader *) (woffData); + newHeader->signature = WOFF_SIGNATURE; + newHeader->signature = READ32BE(newHeader->signature); + newHeader->flavor = header->version; + newHeader->length = READ32BE(tableOffset); + newHeader->numTables = READ16BE(numTables); + newHeader->reserved = 0; + newHeader->totalSfntSize = READ32BE(totalSfntSize); + newHeader->majorVersion = READ16BE(majorVersion); + newHeader->minorVersion = READ16BE(minorVersion); + newHeader->metaOffset = 0; + newHeader->metaCompLen = 0; + newHeader->metaOrigLen = 0; + newHeader->privOffset = 0; + newHeader->privLen = 0; + + free(tableOrder); + + if ((status & eWOFF_warn_checksum_mismatch) != 0) { + /* The original font had checksum errors, so we now decode our WOFF data + back to sfnt format (which fixes checkSumAdjustment), then re-encode + to get a clean copy. */ + const uint8_t * cleanSfnt = woffDecode(woffData, tableOffset, + &sfntLen, &status); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + free(woffData); + woffData = (uint8_t *) woffEncode(cleanSfnt, sfntLen, + majorVersion, minorVersion, + &tableOffset, &status); + free((void *) cleanSfnt); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + } + + if (woffLen) { + *woffLen = tableOffset; + } + if (pStatus) { + *pStatus |= status; + } + return woffData; + +failure: + if (tableOrder) { + free(tableOrder); + } + if (woffData) { + free(woffData); + } + if (pStatus) { + *pStatus = status; + } + return NULL; +} + +static const uint8_t * +rebuildWoff(const uint8_t * woffData, uint32_t * woffLen, + const uint8_t * metaData, uint32_t metaCompLen, uint32_t metaOrigLen, + const uint8_t * privData, uint32_t privLen, uint32_t * pStatus) +{ + const woffHeader * origHeader; + const woffDirEntry * woffDir; + uint8_t * newData = NULL; + uint8_t * tableData = NULL; + woffHeader * newHeader; + uint16_t numTables; + uint32_t tableLimit, totalSize, offset; + uint16_t i; + uint32_t status = eWOFF_ok; + + if (*woffLen < sizeof(woffHeader)) { + FAIL(eWOFF_invalid); + } + origHeader = (const woffHeader *) (woffData); + + if (READ32BE(origHeader->signature) != WOFF_SIGNATURE) { + FAIL(eWOFF_bad_signature); + } + + numTables = READ16BE(origHeader->numTables); + woffDir = (const woffDirEntry *) (woffData + sizeof(woffHeader)); + tableLimit = 0; + for (i = 0; i < numTables; ++i) { + uint32_t end = READ32BE(woffDir[i].offset) + READ32BE(woffDir[i].compLen); + if (end > tableLimit) { + tableLimit = end; + } + } + tableLimit = LONGALIGN(tableLimit); + + /* check for broken input (meta/priv data before sfnt tables) */ + offset = READ32BE(origHeader->metaOffset); + if (offset != 0 && offset < tableLimit) { + FAIL(eWOFF_illegal_order); + } + offset = READ32BE(origHeader->privOffset); + if (offset != 0 && offset < tableLimit) { + FAIL(eWOFF_illegal_order); + } + + totalSize = tableLimit; /* already long-aligned */ + if (metaCompLen) { + totalSize += metaCompLen; + } + if (privLen) { + totalSize = LONGALIGN(totalSize) + privLen; + } + newData = malloc(totalSize); + if (!newData) { + FAIL(eWOFF_out_of_memory); + } + + /* copy the header, directory, and sfnt tables */ + memcpy(newData, woffData, tableLimit); + + /* then overwrite the header fields that should be changed */ + newHeader = (woffHeader *) newData; + newHeader->length = READ32BE(totalSize); + newHeader->metaOffset = 0; + newHeader->metaCompLen = 0; + newHeader->metaOrigLen = 0; + newHeader->privOffset = 0; + newHeader->privLen = 0; + + offset = tableLimit; + if (metaData && metaCompLen > 0 && metaOrigLen > 0) { + newHeader->metaOffset = READ32BE(offset); + newHeader->metaCompLen = READ32BE(metaCompLen); + newHeader->metaOrigLen = READ32BE(metaOrigLen); + memcpy(newData + offset, metaData, metaCompLen); + offset += metaCompLen; + } + + if (privData && privLen > 0) { + while ((offset & 3) != 0) { + newData[offset++] = 0; + } + newHeader->privOffset = READ32BE(offset); + newHeader->privLen = READ32BE(privLen); + memcpy(newData + offset, privData, privLen); + offset += privLen; + } + + *woffLen = offset; + free((void *) woffData); + + if (pStatus) { + *pStatus |= status; + } + return newData; + +failure: + if (newData) { + free(newData); + } + if (pStatus) { + *pStatus = status; + } + return NULL; +} + +const uint8_t * +woffSetMetadata(const uint8_t * woffData, uint32_t * woffLen, + const uint8_t * metaData, uint32_t metaLen, + uint32_t * pStatus) +{ + const woffHeader * header; + uLong compLen = 0; + uint8_t * compData = NULL; + const uint8_t * privData = NULL; + uint32_t privLen = 0; + uint32_t status = eWOFF_ok; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return NULL; + } + + if (!woffData || !woffLen) { + FAIL(eWOFF_bad_parameter); + } + + if (*woffLen < sizeof(woffHeader)) { + FAIL(eWOFF_invalid); + } + header = (const woffHeader *) (woffData); + + if (READ32BE(header->signature) != WOFF_SIGNATURE) { + FAIL(eWOFF_bad_signature); + } + + if (header->privOffset != 0 && header->privLen != 0) { + privData = woffData + READ32BE(header->privOffset); + privLen = READ32BE(header->privLen); + if (privData + privLen > woffData + *woffLen) { + FAIL(eWOFF_invalid); + } + } + + if (metaData && metaLen > 0) { + compLen = compressBound(metaLen); + compData = malloc(compLen); + if (!compData) { + FAIL(eWOFF_out_of_memory); + } + + if (compress2((Bytef *) compData, &compLen, + (const Bytef *) metaData, metaLen, 9) != Z_OK) { + FAIL(eWOFF_compression_failure); + } + } + + woffData = rebuildWoff(woffData, woffLen, + compData, compLen, metaLen, + privData, privLen, pStatus); + free(compData); + return woffData; + +failure: + if (compData) { + free(compData); + } + if (pStatus) { + *pStatus = status; + } + return NULL; +} + +const uint8_t * +woffSetPrivateData(const uint8_t * woffData, uint32_t * woffLen, + const uint8_t * privData, uint32_t privLen, + uint32_t * pStatus) +{ + const woffHeader * header; + const uint8_t * metaData = NULL; + uint32_t metaLen = 0; + uint32_t status = eWOFF_ok; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return NULL; + } + + if (!woffData || !woffLen) { + FAIL(eWOFF_bad_parameter); + } + + if (*woffLen < sizeof(woffHeader)) { + FAIL(eWOFF_invalid); + } + header = (const woffHeader *) (woffData); + + if (READ32BE(header->signature) != WOFF_SIGNATURE) { + FAIL(eWOFF_bad_signature); + } + + if (header->metaOffset != 0 && header->metaCompLen != 0) { + metaData = woffData + READ32BE(header->metaOffset); + metaLen = READ32BE(header->metaCompLen); + if (metaData + metaLen > woffData + *woffLen) { + FAIL(eWOFF_invalid); + } + } + + woffData = rebuildWoff(woffData, woffLen, + metaData, metaLen, READ32BE(header->metaOrigLen), + privData, privLen, pStatus); + return woffData; + +failure: + if (pStatus) { + *pStatus = status; + } + return NULL; +} + +#endif /* WOFF_MOZILLA_CLIENT */ + +/******************************************************************/ +/* * * * * * * * * * * * * * DECODING * * * * * * * * * * * * * * */ +/******************************************************************/ + +static uint32_t +sanityCheck(const uint8_t * woffData, uint32_t woffLen) +{ + const woffHeader * header; + uint16_t numTables, i; + const woffDirEntry * dirEntry; + uint32_t tableTotal = 0; + + if (!woffData || !woffLen) { + return eWOFF_bad_parameter; + } + + if (woffLen < sizeof(woffHeader)) { + return eWOFF_invalid; + } + + header = (const woffHeader *) (woffData); + if (READ32BE(header->signature) != WOFF_SIGNATURE) { + return eWOFF_bad_signature; + } + + if (READ32BE(header->length) != woffLen || header->reserved != 0) { + return eWOFF_invalid; + } + + numTables = READ16BE(header->numTables); + if (woffLen < sizeof(woffHeader) + numTables * sizeof(woffDirEntry)) { + return eWOFF_invalid; + } + + dirEntry = (const woffDirEntry *) (woffData + sizeof(woffHeader)); + for (i = 0; i < numTables; ++i) { + uint32_t offs = READ32BE(dirEntry->offset); + uint32_t orig = READ32BE(dirEntry->origLen); + uint32_t comp = READ32BE(dirEntry->compLen); + if (comp > orig || comp > woffLen || offs > woffLen - comp) { + return eWOFF_invalid; + } + orig = (orig + 3) & ~3; + if (tableTotal > 0xffffffffU - orig) { + return eWOFF_invalid; + } + tableTotal += orig; + ++dirEntry; + } + + if (tableTotal > 0xffffffffU - sizeof(sfntHeader) - + numTables * sizeof(sfntDirEntry) || + READ32BE(header->totalSfntSize) != + tableTotal + sizeof(sfntHeader) + numTables * sizeof(sfntDirEntry)) { + return eWOFF_invalid; + } + + return eWOFF_ok; +} + +uint32_t +woffGetDecodedSize(const uint8_t * woffData, uint32_t woffLen, + uint32_t * pStatus) +{ + uint32_t status = eWOFF_ok; + uint32_t totalLen = 0; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return 0; + } + + status = sanityCheck(woffData, woffLen); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + + totalLen = READ32BE(((const woffHeader *) (woffData))->totalSfntSize); + /* totalLen must be correctly rounded up to 4-byte alignment, otherwise + sanityCheck would have failed */ + +failure: + if (pStatus) { + *pStatus = status; + } + return totalLen; +} + +static void +woffDecodeToBufferInternal(const uint8_t * woffData, uint32_t woffLen, + uint8_t * sfntData, uint32_t bufferLen, + uint32_t * pActualSfntLen, uint32_t * pStatus) +{ + /* this is only called after sanityCheck has verified that + (a) basic header fields are ok + (b) all the WOFF table offset/length pairs are valid (within the data) + (c) the sum of original sizes + header/directory matches totalSfntSize + so we don't have to re-check those overflow conditions here */ + tableOrderRec * tableOrder = NULL; + const woffHeader * header; + uint16_t numTables; + uint16_t tableIndex; + uint16_t order; + const woffDirEntry * woffDir; + uint32_t totalLen; + sfntHeader * newHeader; + uint16_t searchRange, rangeShift, entrySelector; + uint32_t offset; + sfntDirEntry * sfntDir; + uint32_t headOffset = 0, headLength = 0; + sfntHeadTable * head; + uint32_t csum = 0; + const uint32_t * csumPtr; + uint32_t oldCheckSumAdjustment; + uint32_t status = eWOFF_ok; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return; + } + + /* check basic header fields */ + header = (const woffHeader *) (woffData); + if (READ32BE(header->flavor) != SFNT_VERSION_TT && + READ32BE(header->flavor) != SFNT_VERSION_CFF && + READ32BE(header->flavor) != SFNT_VERSION_true) { + status |= eWOFF_warn_unknown_version; + } + + numTables = READ16BE(header->numTables); + woffDir = (const woffDirEntry *) (woffData + sizeof(woffHeader)); + + totalLen = READ32BE(header->totalSfntSize); + + /* construct the sfnt header */ + newHeader = (sfntHeader *) (sfntData); + newHeader->version = header->flavor; + newHeader->numTables = READ16BE(numTables); + + /* calculate header fields for binary search */ + searchRange = numTables; + searchRange |= (searchRange >> 1); + searchRange |= (searchRange >> 2); + searchRange |= (searchRange >> 4); + searchRange |= (searchRange >> 8); + searchRange &= ~(searchRange >> 1); + searchRange *= 16; + newHeader->searchRange = READ16BE(searchRange); + rangeShift = numTables * 16 - searchRange; + newHeader->rangeShift = READ16BE(rangeShift); + entrySelector = 0; + while (searchRange > 16) { + ++entrySelector; + searchRange >>= 1; + } + newHeader->entrySelector = READ16BE(entrySelector); + + tableOrder = (tableOrderRec *) malloc(numTables * sizeof(tableOrderRec)); + if (!tableOrder) { + FAIL(eWOFF_out_of_memory); + } + for (tableIndex = 0; tableIndex < numTables; ++tableIndex) { + tableOrder[tableIndex].offset = READ32BE(woffDir[tableIndex].offset); + tableOrder[tableIndex].oldIndex = tableIndex; + } + qsort(tableOrder, numTables, sizeof(tableOrderRec), compareOffsets); + + /* process each table, filling in the sfnt directory */ + offset = sizeof(sfntHeader) + numTables * sizeof(sfntDirEntry); + sfntDir = (sfntDirEntry *) (sfntData + sizeof(sfntHeader)); + for (order = 0; order < numTables; ++order) { + uint32_t origLen, compLen, tag, sourceOffset; + tableIndex = tableOrder[order].oldIndex; + + /* validity of these was confirmed by sanityCheck */ + origLen = READ32BE(woffDir[tableIndex].origLen); + compLen = READ32BE(woffDir[tableIndex].compLen); + sourceOffset = READ32BE(woffDir[tableIndex].offset); + + sfntDir[tableIndex].tag = woffDir[tableIndex].tag; + sfntDir[tableIndex].offset = READ32BE(offset); + sfntDir[tableIndex].length = woffDir[tableIndex].origLen; + sfntDir[tableIndex].checksum = woffDir[tableIndex].checksum; + csum += READ32BE(sfntDir[tableIndex].checksum); + + if (compLen < origLen) { + uLongf destLen = origLen; + if (uncompress((Bytef *)(sfntData + offset), &destLen, + (const Bytef *)(woffData + sourceOffset), + compLen) != Z_OK || destLen != origLen) { + FAIL(eWOFF_compression_failure); + } + } else { + memcpy(sfntData + offset, woffData + sourceOffset, origLen); + } + + /* note that old Mac bitmap-only fonts have no 'head' table + (eg NISC18030.ttf) but a 'bhed' table instead */ + tag = READ32BE(sfntDir[tableIndex].tag); + if (tag == TABLE_TAG_head || tag == TABLE_TAG_bhed) { + headOffset = offset; + headLength = origLen; + } + + offset += origLen; + + while (offset < totalLen && (offset & 3) != 0) { + sfntData[offset++] = 0; + } + } + + if (headOffset > 0) { + /* the font checksum in the 'head' table depends on all the individual + table checksums (collected above), plus the header and directory + which are added in here */ + if (headLength < HEAD_TABLE_SIZE) { + FAIL(eWOFF_invalid); + } + head = (sfntHeadTable *)(sfntData + headOffset); + oldCheckSumAdjustment = READ32BE(head->checkSumAdjustment); + head->checkSumAdjustment = 0; + csumPtr = (const uint32_t *)sfntData; + while (csumPtr < (const uint32_t *)(sfntData + sizeof(sfntHeader) + + numTables * sizeof(sfntDirEntry))) { + csum += READ32BE(*csumPtr); + csumPtr++; + } + csum = SFNT_CHECKSUM_CALC_CONST - csum; + + if (oldCheckSumAdjustment != csum) { + /* if the checksum doesn't match, we fix it; but this will invalidate + any DSIG that may be present */ + status |= eWOFF_warn_checksum_mismatch; + } + head->checkSumAdjustment = READ32BE(csum); + } + + if (pActualSfntLen) { + *pActualSfntLen = totalLen; + } + if (pStatus) { + *pStatus |= status; + } + free(tableOrder); + return; + +failure: + if (tableOrder) { + free(tableOrder); + } + if (pActualSfntLen) { + *pActualSfntLen = 0; + } + if (pStatus) { + *pStatus = status; + } +} + +void +woffDecodeToBuffer(const uint8_t * woffData, uint32_t woffLen, + uint8_t * sfntData, uint32_t bufferLen, + uint32_t * pActualSfntLen, uint32_t * pStatus) +{ + uint32_t status = eWOFF_ok; + uint32_t totalLen; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return; + } + + status = sanityCheck(woffData, woffLen); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + + if (!sfntData) { + FAIL(eWOFF_bad_parameter); + } + + totalLen = READ32BE(((const woffHeader *) (woffData))->totalSfntSize); + if (bufferLen < totalLen) { + FAIL(eWOFF_buffer_too_small); + } + + woffDecodeToBufferInternal(woffData, woffLen, sfntData, bufferLen, + pActualSfntLen, pStatus); + return; + +failure: + if (pActualSfntLen) { + *pActualSfntLen = 0; + } + if (pStatus) { + *pStatus = status; + } +} + +const uint8_t * +woffDecode(const uint8_t * woffData, uint32_t woffLen, + uint32_t * sfntLen, uint32_t * pStatus) +{ + uint32_t status = eWOFF_ok; + uint8_t * sfntData = NULL; + uint32_t bufLen; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return NULL; + } + + status = sanityCheck(woffData, woffLen); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + + bufLen = READ32BE(((const woffHeader *) (woffData))->totalSfntSize); + sfntData = (uint8_t *) malloc(bufLen); + if (!sfntData) { + FAIL(eWOFF_out_of_memory); + } + + woffDecodeToBufferInternal(woffData, woffLen, sfntData, bufLen, + sfntLen, &status); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + + if (pStatus) { + *pStatus |= status; + } + return sfntData; + +failure: + if (sfntData) { + free(sfntData); + } + if (pStatus) { + *pStatus = status; + } + return NULL; +} + +#ifndef WOFF_MOZILLA_CLIENT + +const uint8_t * +woffGetMetadata(const uint8_t * woffData, uint32_t woffLen, + uint32_t * metaLen, uint32_t * pStatus) +{ + const woffHeader * header; + uint32_t offset, compLen; + uLong origLen; + uint8_t * data = NULL; + uint32_t status = eWOFF_ok; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return NULL; + } + + status = sanityCheck(woffData, woffLen); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + + header = (const woffHeader *) (woffData); + + offset = READ32BE(header->metaOffset); + compLen = READ32BE(header->metaCompLen); + origLen = READ32BE(header->metaOrigLen); + if (offset == 0 || compLen == 0 || origLen == 0) { + return NULL; + } + + if (compLen > woffLen || offset > woffLen - compLen) { + FAIL(eWOFF_invalid); + } + + data = malloc(origLen); + if (!data) { + FAIL(eWOFF_out_of_memory); + } + + if (uncompress((Bytef *)data, &origLen, + (const Bytef *)woffData + offset, compLen) != Z_OK || + origLen != READ32BE(header->metaOrigLen)) { + FAIL(eWOFF_compression_failure); + } + + if (metaLen) { + *metaLen = origLen; + } + if (pStatus) { + *pStatus |= status; + } + return data; + +failure: + if (data) { + free(data); + } + if (pStatus) { + *pStatus = status; + } + return NULL; +} + +const uint8_t * +woffGetPrivateData(const uint8_t * woffData, uint32_t woffLen, + uint32_t * privLen, uint32_t * pStatus) +{ + const woffHeader * header; + uint32_t offset, length; + uint8_t * data = NULL; + uint32_t status = eWOFF_ok; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return NULL; + } + + status = sanityCheck(woffData, woffLen); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + + header = (const woffHeader *) (woffData); + + offset = READ32BE(header->privOffset); + length = READ32BE(header->privLen); + if (offset == 0 || length == 0) { + return NULL; + } + + if (length > woffLen || offset > woffLen - length) { + FAIL(eWOFF_invalid); + } + + data = malloc(length); + if (!data) { + FAIL(eWOFF_out_of_memory); + } + + memcpy(data, woffData + offset, length); + + if (privLen) { + *privLen = length; + } + if (pStatus) { + *pStatus |= status; + } + return data; + +failure: + if (data) { + free(data); + } + if (pStatus) { + *pStatus = status; + } + return NULL; +} + +void +woffGetFontVersion(const uint8_t * woffData, uint32_t woffLen, + uint16_t * major, uint16_t * minor, uint32_t * pStatus) +{ + const woffHeader * header; + uint32_t status = eWOFF_ok; + + if (pStatus && WOFF_FAILURE(*pStatus)) { + return; + } + + status = sanityCheck(woffData, woffLen); + if (WOFF_FAILURE(status)) { + FAIL(status); + } + + if (!major || !minor) { + FAIL(eWOFF_bad_parameter); + } + + *major = *minor = 0; + + header = (const woffHeader *) (woffData); + + *major = READ16BE(header->majorVersion); + *minor = READ16BE(header->minorVersion); + +failure: + if (pStatus) { + *pStatus = status; + } +} + +/* utility to print messages corresponding to WOFF encoder/decoder errors */ +void +woffPrintStatus(FILE * f, uint32_t status, const char * prefix) +{ + if (!prefix) { + prefix = ""; + } + if (WOFF_WARNING(status)) { + const char * template = "%sWOFF warning: %s\n"; + if (status & eWOFF_warn_unknown_version) { + fprintf(f, template, prefix, "unrecognized sfnt version"); + } + if (status & eWOFF_warn_checksum_mismatch) { + fprintf(f, template, prefix, "checksum mismatch (corrected)"); + } + if (status & eWOFF_warn_misaligned_table) { + fprintf(f, template, prefix, "misaligned font table"); + } + if (status & eWOFF_warn_trailing_data) { + fprintf(f, template, prefix, "extraneous input data discarded"); + } + if (status & eWOFF_warn_unpadded_table) { + fprintf(f, template, prefix, "final table not correctly padded"); + } + if (status & eWOFF_warn_removed_DSIG) { + fprintf(f, template, prefix, "digital signature (DSIG) table removed"); + } + } + if (WOFF_FAILURE(status)) { + const char * template = "%sWOFF error: %s\n"; + const char * msg; + switch (status & 0xff) { + case eWOFF_out_of_memory: + msg = "memory allocation failure"; + break; + case eWOFF_invalid: + msg = "invalid input font"; + break; + case eWOFF_compression_failure: + msg = "zlib compression/decompression failure"; + break; + case eWOFF_bad_signature: + msg = "incorrect WOFF file signature"; + break; + case eWOFF_buffer_too_small: + msg = "buffer too small"; + break; + case eWOFF_bad_parameter: + msg = "bad parameter to WOFF function"; + break; + case eWOFF_illegal_order: + msg = "incorrect table directory order"; + break; + default: + msg = "unknown internal error"; + break; + } + fprintf(f, template, prefix, msg); + } +} + +#endif /* not WOFF_MOZILLA_CLIENT */ diff --git a/src/calibre/utils/fonts/woff/woff.h b/src/calibre/utils/fonts/woff/woff.h new file mode 100644 index 0000000000..d8c6f55b08 --- /dev/null +++ b/src/calibre/utils/fonts/woff/woff.h @@ -0,0 +1,211 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is WOFF font packaging code. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jonathan Kew + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef WOFF_H_ +#define WOFF_H_ + +/* API for the WOFF encoder and decoder */ + +#ifdef _MSC_VER /* MS VC lacks inttypes.h + but we can make do with a few definitons here */ +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#else +#include +#endif + +#include /* only for FILE, needed for woffPrintStatus */ + +/* error codes returned in the status parameter of WOFF functions */ +enum { + /* Success */ + eWOFF_ok = 0, + + /* Errors: no valid result returned */ + eWOFF_out_of_memory = 1, /* malloc or realloc failed */ + eWOFF_invalid = 2, /* invalid input file (e.g., bad offset) */ + eWOFF_compression_failure = 3, /* error in zlib call */ + eWOFF_bad_signature = 4, /* unrecognized file signature */ + eWOFF_buffer_too_small = 5, /* the provided buffer is too small */ + eWOFF_bad_parameter = 6, /* bad parameter (e.g., null source ptr) */ + eWOFF_illegal_order = 7, /* improperly ordered chunks in WOFF font */ + + /* Warnings: call succeeded but something odd was noticed. + Multiple warnings may be OR'd together. */ + eWOFF_warn_unknown_version = 0x0100, /* unrecognized version of sfnt, + not standard TrueType or CFF */ + eWOFF_warn_checksum_mismatch = 0x0200, /* bad checksum, use with caution; + any DSIG will be invalid */ + eWOFF_warn_misaligned_table = 0x0400, /* table not long-aligned; fixing, + but DSIG will be invalid */ + eWOFF_warn_trailing_data = 0x0800, /* trailing junk discarded, + any DSIG may be invalid */ + eWOFF_warn_unpadded_table = 0x1000, /* sfnt not correctly padded, + any DSIG may be invalid */ + eWOFF_warn_removed_DSIG = 0x2000 /* removed digital signature + while fixing checksum errors */ +}; + +/* Note: status parameters must be initialized to eWOFF_ok before calling + WOFF functions. If the status parameter contains an error code, + functions will return immediately. */ + +#define WOFF_SUCCESS(status) (((uint32_t)(status) & 0xff) == eWOFF_ok) +#define WOFF_FAILURE(status) (!WOFF_SUCCESS(status)) +#define WOFF_WARNING(status) ((uint32_t)(status) & ~0xff) + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef WOFF_DISABLE_ENCODING + +/***************************************************************************** + * Returns a new malloc() block containing the encoded data, or NULL on error; + * caller should free() this when finished with it. + * Returns length of the encoded data in woffLen. + * The new WOFF has no metadata or private block; + * see the following functions to update these elements. + */ +const uint8_t * woffEncode(const uint8_t * sfntData, uint32_t sfntLen, + uint16_t majorVersion, uint16_t minorVersion, + uint32_t * woffLen, uint32_t * status); + + +/***************************************************************************** + * Add the given metadata block to the WOFF font, replacing any existing + * metadata block. The block will be zlib-compressed. + * Metadata is required to be valid XML (use of UTF-8 is recommended), + * though this function does not currently check this. + * The woffData pointer must be a malloc() block (typically from woffEncode); + * it will be freed by this function and a new malloc() block will be returned. + * Returns NULL if an error occurs, in which case the original WOFF is NOT freed. + */ +const uint8_t * woffSetMetadata(const uint8_t * woffData, uint32_t * woffLen, + const uint8_t * metaData, uint32_t metaLen, + uint32_t * status); + + +/***************************************************************************** + * Add the given private data block to the WOFF font, replacing any existing + * private block. The block will NOT be zlib-compressed. + * Private data may be any arbitrary block of bytes; it may be externally + * compressed by the client if desired. + * The woffData pointer must be a malloc() block (typically from woffEncode); + * it will be freed by this function and a new malloc() block will be returned. + * Returns NULL if an error occurs, in which case the original WOFF is NOT freed. + */ +const uint8_t * woffSetPrivateData(const uint8_t * woffData, uint32_t * woffLen, + const uint8_t * privData, uint32_t privLen, + uint32_t * status); + +#endif /* WOFF_DISABLE_ENCODING */ + +/***************************************************************************** + * Returns the size of buffer needed to decode the font (or zero on error). + */ +uint32_t woffGetDecodedSize(const uint8_t * woffData, uint32_t woffLen, + uint32_t * pStatus); + + +/***************************************************************************** + * Decodes WOFF font to a caller-supplied buffer of size bufferLen. + * Returns the actual size of the decoded sfnt data in pActualSfntLen + * (must be <= bufferLen, otherwise an error will be returned). + */ +void woffDecodeToBuffer(const uint8_t * woffData, uint32_t woffLen, + uint8_t * sfntData, uint32_t bufferLen, + uint32_t * pActualSfntLen, uint32_t * pStatus); + + +/***************************************************************************** + * Returns a new malloc() block containing the decoded data, or NULL on error; + * caller should free() this when finished with it. + * Returns length of the decoded data in sfntLen. + */ +const uint8_t * woffDecode(const uint8_t * woffData, uint32_t woffLen, + uint32_t * sfntLen, uint32_t * status); + + +/***************************************************************************** + * Returns a new malloc() block containing the metadata from the WOFF font, + * or NULL if an error occurs or no metadata is present. + * Length of the metadata is returned in metaLen. + * The metadata is decompressed before returning. + */ +const uint8_t * woffGetMetadata(const uint8_t * woffData, uint32_t woffLen, + uint32_t * metaLen, uint32_t * status); + + +/***************************************************************************** + * Returns a new malloc() block containing the private data from the WOFF font, + * or NULL if an error occurs or no private data is present. + * Length of the private data is returned in privLen. + */ +const uint8_t * woffGetPrivateData(const uint8_t * woffData, uint32_t woffLen, + uint32_t * privLen, uint32_t * status); + + +/***************************************************************************** + * Returns the font version numbers from the WOFF font in the major and minor + * parameters. + * Check the status result to know if the function succeeded. + */ +void woffGetFontVersion(const uint8_t * woffData, uint32_t woffLen, + uint16_t * major, uint16_t * minor, + uint32_t * status); + + +/***************************************************************************** + * Utility to print warning and/or error status to the specified FILE*. + * The prefix string will be prepended to each line (ok to pass NULL if no + * prefix is wanted). + * (Provides terse English messages only, not intended for end-user display; + * user-friendly tools should map the status codes to their own messages.) + */ +void woffPrintStatus(FILE * f, uint32_t status, const char * prefix); + + +#ifdef __cplusplus +} +#endif + +#endif