File indexing completed on 2024-04-28 11:38:33

0001 /*
0002     This file is part of the KDE libraries
0003 
0004     The Original Code is WOFF font packaging code.
0005     Copyright (C) 2009 Mozilla Corporation
0006 
0007     Contributor(s):
0008       Jonathan Kew <jfkthame@gmail.com>
0009       Germain Garand <germain@ebooksfrance.org>
0010 
0011     This library is free software; you can redistribute it and/or
0012     modify it under the terms of the GNU Library General Public
0013     License as published by the Free Software Foundation; either
0014     version 2 of the License, or (at your option) any later version.
0015 
0016     This library is distributed in the hope that it will be useful,
0017     but WITHOUT ANY WARRANTY; without even the implied warranty of
0018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0019     Library General Public License for more details.
0020 
0021     You should have received a copy of the GNU Library General Public License
0022     along with this library; see the file COPYING.LIB.  If not, write to
0023     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0024     Boston, MA 02110-1301, USA.
0025 */
0026 
0027 #include "woff-private.h"
0028 #include <string.h>
0029 #include <stdlib.h>
0030 #include <zlib.h>
0031 
0032 using namespace WOFF;
0033 
0034 /* on errors, each function sets a status variable and jumps to failure: */
0035 #undef FAIL
0036 #define FAIL(err) do { status |= err; goto failure; } while (0)
0037 
0038 /* adjust an offset for longword alignment */
0039 #define LONGALIGN(x) (((x) + 3) & ~3)
0040 
0041 static int compareOffsets(const void *lhs, const void *rhs)
0042 {
0043     const tableOrderRec *a = (const tableOrderRec *) lhs;
0044     const tableOrderRec *b = (const tableOrderRec *) rhs;
0045     /* don't simply return a->offset - b->offset because these are unsigned
0046        offset values; could convert to int, but possible integer overflow */
0047     return a->offset > b->offset ? 1 :
0048            a->offset < b->offset ? -1 :
0049            0;
0050 }
0051 
0052 static int sanityCheck(const char *woffData, int _woffLen)
0053 {
0054     quint32 woffLen = _woffLen;
0055     const woffHeader *header;
0056     quint16 numTables, i;
0057     const woffDirEntry *dirEntry;
0058     quint32 tableTotal = 0;
0059 
0060     if (!woffData || !woffLen) {
0061         return eWOFF_bad_parameter;
0062     }
0063 
0064     if (woffLen < sizeof(woffHeader)) {
0065         return eWOFF_invalid;
0066     }
0067 
0068     header = (const woffHeader *)(woffData);
0069     if (READ32BE(header->signature) != WOFF_SIGNATURE) {
0070         return eWOFF_bad_signature;
0071     }
0072 
0073     if (READ32BE(header->length) != woffLen || header->reserved != 0) {
0074         return eWOFF_invalid;
0075     }
0076 
0077     numTables = READ16BE(header->numTables);
0078     if (woffLen < sizeof(woffHeader) + numTables * sizeof(woffDirEntry)) {
0079         return eWOFF_invalid;
0080     }
0081 
0082     dirEntry = (const woffDirEntry *)(woffData + sizeof(woffHeader));
0083     for (i = 0; i < numTables; ++i) {
0084         quint32 offs = READ32BE(dirEntry->offset);
0085         quint32 orig = READ32BE(dirEntry->origLen);
0086         quint32 comp = READ32BE(dirEntry->compLen);
0087         if (comp > orig || comp > woffLen || offs > woffLen - comp) {
0088             return eWOFF_invalid;
0089         }
0090         orig = (orig + 3) & ~3;
0091         if (tableTotal > 0xffffffffU - orig) {
0092             return eWOFF_invalid;
0093         }
0094         tableTotal += orig;
0095         ++dirEntry;
0096     }
0097 
0098     if (tableTotal > 0xffffffffU - sizeof(sfntHeader) -
0099             numTables * sizeof(sfntDirEntry) ||
0100             READ32BE(header->totalSfntSize) !=
0101             tableTotal + sizeof(sfntHeader) + numTables * sizeof(sfntDirEntry)) {
0102         return eWOFF_invalid;
0103     }
0104 
0105     return eWOFF_ok;
0106 }
0107 
0108 int WOFF::getDecodedSize(const char *woffData, int woffLen, int *pStatus)
0109 {
0110     int status = eWOFF_ok;
0111     quint32 totalLen = 0;
0112 
0113     if (pStatus && WOFF_FAILURE(*pStatus)) {
0114         return 0;
0115     }
0116 
0117     status = sanityCheck(woffData, woffLen);
0118     if (WOFF_FAILURE(status)) {
0119         FAIL(status);
0120     }
0121 
0122     totalLen = READ32BE(((const woffHeader *)(woffData))->totalSfntSize);
0123     /* totalLen must be correctly rounded up to 4-byte alignment, otherwise
0124        sanityCheck would have failed */
0125 
0126 failure:
0127     if (pStatus) {
0128         *pStatus = status;
0129     }
0130     return totalLen;
0131 }
0132 
0133 static void woffDecodeToBufferInternal(const char *woffData, char *sfntData, int *pActualSfntLen, int *pStatus)
0134 {
0135     /* this is only called after sanityCheck has verified that
0136        (a) basic header fields are ok
0137        (b) all the WOFF table offset/length pairs are valid (within the data)
0138        (c) the sum of original sizes + header/directory matches totalSfntSize
0139        so we don't have to re-check those overflow conditions here */
0140     tableOrderRec *tableOrder = nullptr;
0141     const woffHeader *header;
0142     quint16 numTables;
0143     quint16 tableIndex;
0144     quint16 order;
0145     const woffDirEntry *woffDir;
0146     quint32 totalLen;
0147     sfntHeader *newHeader;
0148     quint16 searchRange, rangeShift, entrySelector;
0149     quint32 offset;
0150     sfntDirEntry *sfntDir;
0151     quint32 headOffset = 0, headLength = 0;
0152     sfntHeadTable *head;
0153     quint32 csum = 0;
0154     const quint32 *csumPtr;
0155     quint32 oldCheckSumAdjustment;
0156     quint32 status = eWOFF_ok;
0157 
0158     if (pStatus && WOFF_FAILURE(*pStatus)) {
0159         return;
0160     }
0161 
0162     /* check basic header fields */
0163     header = (const woffHeader *)(woffData);
0164     if (READ32BE(header->flavor) != SFNT_VERSION_TT &&
0165             READ32BE(header->flavor) != SFNT_VERSION_CFF &&
0166             READ32BE(header->flavor) != SFNT_VERSION_true) {
0167         status |= eWOFF_warn_unknown_version;
0168     }
0169 
0170     numTables = READ16BE(header->numTables);
0171     woffDir = (const woffDirEntry *)(woffData + sizeof(woffHeader));
0172 
0173     totalLen = READ32BE(header->totalSfntSize);
0174 
0175     /* construct the sfnt header */
0176     newHeader = (sfntHeader *)(sfntData);
0177     newHeader->version = header->flavor;
0178     newHeader->numTables = READ16BE(numTables);
0179 
0180     /* calculate header fields for binary search */
0181     searchRange = numTables;
0182     searchRange |= (searchRange >> 1);
0183     searchRange |= (searchRange >> 2);
0184     searchRange |= (searchRange >> 4);
0185     searchRange |= (searchRange >> 8);
0186     searchRange &= ~(searchRange >> 1);
0187     searchRange *= 16;
0188     newHeader->searchRange = READ16BE(searchRange);
0189     rangeShift = numTables * 16 - searchRange;
0190     newHeader->rangeShift = READ16BE(rangeShift);
0191     entrySelector = 0;
0192     while (searchRange > 16) {
0193         ++entrySelector;
0194         searchRange >>= 1;
0195     }
0196     newHeader->entrySelector = READ16BE(entrySelector);
0197 
0198     tableOrder = (tableOrderRec *) malloc(numTables * sizeof(tableOrderRec));
0199     if (!tableOrder) {
0200         FAIL(eWOFF_out_of_memory);
0201     }
0202     for (tableIndex = 0; tableIndex < numTables; ++tableIndex) {
0203         tableOrder[tableIndex].offset = READ32BE(woffDir[tableIndex].offset);
0204         tableOrder[tableIndex].oldIndex = tableIndex;
0205     }
0206     qsort(tableOrder, numTables, sizeof(tableOrderRec), compareOffsets);
0207 
0208     /* process each table, filling in the sfnt directory */
0209     offset = sizeof(sfntHeader) + numTables * sizeof(sfntDirEntry);
0210     sfntDir = (sfntDirEntry *)(sfntData + sizeof(sfntHeader));
0211     for (order = 0; order < numTables; ++order) {
0212         quint32 origLen, compLen, tag, sourceOffset;
0213         tableIndex = tableOrder[order].oldIndex;
0214 
0215         /* validity of these was confirmed by sanityCheck */
0216         origLen = READ32BE(woffDir[tableIndex].origLen);
0217         compLen = READ32BE(woffDir[tableIndex].compLen);
0218         sourceOffset = READ32BE(woffDir[tableIndex].offset);
0219 
0220         sfntDir[tableIndex].tag = woffDir[tableIndex].tag;
0221         sfntDir[tableIndex].offset = READ32BE(offset);
0222         sfntDir[tableIndex].length = woffDir[tableIndex].origLen;
0223         sfntDir[tableIndex].checksum = woffDir[tableIndex].checksum;
0224         csum += READ32BE(sfntDir[tableIndex].checksum);
0225 
0226         if (compLen < origLen) {
0227             uLongf destLen = origLen;
0228             if (uncompress((Bytef *)(sfntData + offset), &destLen,
0229                            (const Bytef *)(woffData + sourceOffset),
0230                            compLen) != Z_OK || destLen != origLen) {
0231                 FAIL(eWOFF_compression_failure);
0232             }
0233         } else {
0234             memcpy(sfntData + offset, woffData + sourceOffset, origLen);
0235         }
0236 
0237         /* note that old Mac bitmap-only fonts have no 'head' table
0238            (eg NISC18030.ttf) but a 'bhed' table instead */
0239         tag = READ32BE(sfntDir[tableIndex].tag);
0240         if (tag == TABLE_TAG_head || tag == TABLE_TAG_bhed) {
0241             headOffset = offset;
0242             headLength = origLen;
0243         }
0244 
0245         offset += origLen;
0246 
0247         while (offset < totalLen && (offset & 3) != 0) {
0248             sfntData[offset++] = 0;
0249         }
0250     }
0251 
0252     if (headOffset > 0) {
0253         /* the font checksum in the 'head' table depends on all the individual
0254            table checksums (collected above), plus the header and directory
0255            which are added in here */
0256         if (headLength < HEAD_TABLE_SIZE) {
0257             FAIL(eWOFF_invalid);
0258         }
0259         head = (sfntHeadTable *)(sfntData + headOffset);
0260         oldCheckSumAdjustment = READ32BE(head->checkSumAdjustment);
0261         head->checkSumAdjustment = 0;
0262         csumPtr = (const quint32 *)sfntData;
0263         while (csumPtr < (const quint32 *)(sfntData + sizeof(sfntHeader) +
0264                                            numTables * sizeof(sfntDirEntry))) {
0265             csum += READ32BE(*csumPtr);
0266             csumPtr++;
0267         }
0268         csum = SFNT_CHECKSUM_CALC_CONST - csum;
0269 
0270         if (oldCheckSumAdjustment != csum) {
0271             /* if the checksum doesn't match, we fix it; but this will invalidate
0272                any DSIG that may be present */
0273             status |= eWOFF_warn_checksum_mismatch;
0274         }
0275         head->checkSumAdjustment = READ32BE(csum);
0276     }
0277 
0278     if (pActualSfntLen) {
0279         *pActualSfntLen = totalLen;
0280     }
0281     if (pStatus) {
0282         *pStatus |= status;
0283     }
0284     free(tableOrder);
0285     return;
0286 
0287 failure:
0288     if (tableOrder) {
0289         free(tableOrder);
0290     }
0291     if (pActualSfntLen) {
0292         *pActualSfntLen = 0;
0293     }
0294     if (pStatus) {
0295         *pStatus = status;
0296     }
0297 }
0298 
0299 void WOFF::decodeToBuffer(const char *woffData, int woffLen, char *sfntData, int bufferLen,
0300                           int *pActualSfntLen, int *pStatus)
0301 {
0302     quint32 status = eWOFF_ok;
0303     quint32 totalLen;
0304 
0305     if (pStatus && WOFF_FAILURE(*pStatus)) {
0306         return;
0307     }
0308 
0309     status = sanityCheck(woffData, woffLen);
0310     if (WOFF_FAILURE(status)) {
0311         FAIL(status);
0312     }
0313 
0314     if (!sfntData) {
0315         FAIL(eWOFF_bad_parameter);
0316     }
0317 
0318     totalLen = READ32BE(((const woffHeader *)(woffData))->totalSfntSize);
0319     if ((unsigned)bufferLen < totalLen) {
0320         FAIL(eWOFF_buffer_too_small);
0321     }
0322 
0323     woffDecodeToBufferInternal(woffData, sfntData, pActualSfntLen, pStatus);
0324     return;
0325 
0326 failure:
0327     if (pActualSfntLen) {
0328         *pActualSfntLen = 0;
0329     }
0330     if (pStatus) {
0331         *pStatus = status;
0332     }
0333 }