File indexing completed on 2023-12-03 07:43:24
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 }