mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-25 11:22:50 +00:00
334 lines
9.8 KiB
C++
334 lines
9.8 KiB
C++
![]() |
/*
|
||
|
This file is part of the KDE libraries
|
||
|
|
||
|
The Original Code is WOFF font packaging code.
|
||
|
Copyright (C) 2009 Mozilla Corporation
|
||
|
|
||
|
Contributor(s):
|
||
|
Jonathan Kew <jfkthame@gmail.com>
|
||
|
Germain Garand <germain@ebooksfrance.org>
|
||
|
|
||
|
This library is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU Library General Public
|
||
|
License as published by the Free Software Foundation; either
|
||
|
version 2 of the License, or (at your option) any later version.
|
||
|
|
||
|
This library 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
|
||
|
Library General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Library General Public License
|
||
|
along with this library; see the file COPYING.LIB. If not, write to
|
||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
|
Boston, MA 02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
#include "woff-private.h"
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <zlib.h>
|
||
|
|
||
|
using namespace WOFF;
|
||
|
|
||
|
/* 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;
|
||
|
}
|
||
|
|
||
|
static int sanityCheck(const char* woffData, int _woffLen)
|
||
|
{
|
||
|
quint32 woffLen = _woffLen;
|
||
|
const woffHeader * header;
|
||
|
quint16 numTables, i;
|
||
|
const woffDirEntry * dirEntry;
|
||
|
quint32 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) {
|
||
|
quint32 offs = READ32BE(dirEntry->offset);
|
||
|
quint32 orig = READ32BE(dirEntry->origLen);
|
||
|
quint32 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;
|
||
|
}
|
||
|
|
||
|
int WOFF::getDecodedSize(const char* woffData, int woffLen, int* pStatus)
|
||
|
{
|
||
|
int status = eWOFF_ok;
|
||
|
quint32 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 char* woffData, char* sfntData, int* pActualSfntLen, int* 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;
|
||
|
quint16 numTables;
|
||
|
quint16 tableIndex;
|
||
|
quint16 order;
|
||
|
const woffDirEntry * woffDir;
|
||
|
quint32 totalLen;
|
||
|
sfntHeader * newHeader;
|
||
|
quint16 searchRange, rangeShift, entrySelector;
|
||
|
quint32 offset;
|
||
|
sfntDirEntry * sfntDir;
|
||
|
quint32 headOffset = 0, headLength = 0;
|
||
|
sfntHeadTable * head;
|
||
|
quint32 csum = 0;
|
||
|
const quint32 * csumPtr;
|
||
|
quint32 oldCheckSumAdjustment;
|
||
|
quint32 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) {
|
||
|
quint32 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 quint32 *)sfntData;
|
||
|
while (csumPtr < (const quint32 *)(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 WOFF::decodeToBuffer(const char* woffData, int woffLen, char* sfntData, int bufferLen,
|
||
|
int* pActualSfntLen, int* pStatus)
|
||
|
{
|
||
|
quint32 status = eWOFF_ok;
|
||
|
quint32 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 ((unsigned)bufferLen < totalLen) {
|
||
|
FAIL(eWOFF_buffer_too_small);
|
||
|
}
|
||
|
|
||
|
woffDecodeToBufferInternal(woffData, sfntData, pActualSfntLen, pStatus);
|
||
|
return;
|
||
|
|
||
|
failure:
|
||
|
if (pActualSfntLen) {
|
||
|
*pActualSfntLen = 0;
|
||
|
}
|
||
|
if (pStatus) {
|
||
|
*pStatus = status;
|
||
|
}
|
||
|
}
|