KHtml

woff.cpp
1 /*
2  This file is part of the KDE libraries
3 
4  The Original Code is WOFF font packaging code.
5  Copyright (C) 2009 Mozilla Corporation
6 
7  Contributor(s):
8  Jonathan Kew <[email protected]>
9  Germain Garand <[email protected]>
10 
11  This library is free software; you can redistribute it and/or
12  modify it under the terms of the GNU Library General Public
13  License as published by the Free Software Foundation; either
14  version 2 of the License, or (at your option) any later version.
15 
16  This library is distributed in the hope that it will be useful,
17  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  Library General Public License for more details.
20 
21  You should have received a copy of the GNU Library General Public License
22  along with this library; see the file COPYING.LIB. If not, write to
23  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24  Boston, MA 02110-1301, USA.
25 */
26 
27 #include "woff-private.h"
28 #include <string.h>
29 #include <stdlib.h>
30 #include <zlib.h>
31 
32 using namespace WOFF;
33 
34 /* on errors, each function sets a status variable and jumps to failure: */
35 #undef FAIL
36 #define FAIL(err) do { status |= err; goto failure; } while (0)
37 
38 /* adjust an offset for longword alignment */
39 #define LONGALIGN(x) (((x) + 3) & ~3)
40 
41 static int compareOffsets(const void *lhs, const void *rhs)
42 {
43  const tableOrderRec *a = (const tableOrderRec *) lhs;
44  const tableOrderRec *b = (const tableOrderRec *) rhs;
45  /* don't simply return a->offset - b->offset because these are unsigned
46  offset values; could convert to int, but possible integer overflow */
47  return a->offset > b->offset ? 1 :
48  a->offset < b->offset ? -1 :
49  0;
50 }
51 
52 static int sanityCheck(const char *woffData, int _woffLen)
53 {
54  quint32 woffLen = _woffLen;
55  const woffHeader *header;
56  quint16 numTables, i;
57  const woffDirEntry *dirEntry;
58  quint32 tableTotal = 0;
59 
60  if (!woffData || !woffLen) {
61  return eWOFF_bad_parameter;
62  }
63 
64  if (woffLen < sizeof(woffHeader)) {
65  return eWOFF_invalid;
66  }
67 
68  header = (const woffHeader *)(woffData);
69  if (READ32BE(header->signature) != WOFF_SIGNATURE) {
70  return eWOFF_bad_signature;
71  }
72 
73  if (READ32BE(header->length) != woffLen || header->reserved != 0) {
74  return eWOFF_invalid;
75  }
76 
77  numTables = READ16BE(header->numTables);
78  if (woffLen < sizeof(woffHeader) + numTables * sizeof(woffDirEntry)) {
79  return eWOFF_invalid;
80  }
81 
82  dirEntry = (const woffDirEntry *)(woffData + sizeof(woffHeader));
83  for (i = 0; i < numTables; ++i) {
84  quint32 offs = READ32BE(dirEntry->offset);
85  quint32 orig = READ32BE(dirEntry->origLen);
86  quint32 comp = READ32BE(dirEntry->compLen);
87  if (comp > orig || comp > woffLen || offs > woffLen - comp) {
88  return eWOFF_invalid;
89  }
90  orig = (orig + 3) & ~3;
91  if (tableTotal > 0xffffffffU - orig) {
92  return eWOFF_invalid;
93  }
94  tableTotal += orig;
95  ++dirEntry;
96  }
97 
98  if (tableTotal > 0xffffffffU - sizeof(sfntHeader) -
99  numTables * sizeof(sfntDirEntry) ||
100  READ32BE(header->totalSfntSize) !=
101  tableTotal + sizeof(sfntHeader) + numTables * sizeof(sfntDirEntry)) {
102  return eWOFF_invalid;
103  }
104 
105  return eWOFF_ok;
106 }
107 
108 int WOFF::getDecodedSize(const char *woffData, int woffLen, int *pStatus)
109 {
110  int status = eWOFF_ok;
111  quint32 totalLen = 0;
112 
113  if (pStatus && WOFF_FAILURE(*pStatus)) {
114  return 0;
115  }
116 
117  status = sanityCheck(woffData, woffLen);
118  if (WOFF_FAILURE(status)) {
119  FAIL(status);
120  }
121 
122  totalLen = READ32BE(((const woffHeader *)(woffData))->totalSfntSize);
123  /* totalLen must be correctly rounded up to 4-byte alignment, otherwise
124  sanityCheck would have failed */
125 
126 failure:
127  if (pStatus) {
128  *pStatus = status;
129  }
130  return totalLen;
131 }
132 
133 static void woffDecodeToBufferInternal(const char *woffData, char *sfntData, int *pActualSfntLen, int *pStatus)
134 {
135  /* this is only called after sanityCheck has verified that
136  (a) basic header fields are ok
137  (b) all the WOFF table offset/length pairs are valid (within the data)
138  (c) the sum of original sizes + header/directory matches totalSfntSize
139  so we don't have to re-check those overflow conditions here */
140  tableOrderRec *tableOrder = nullptr;
141  const woffHeader *header;
142  quint16 numTables;
143  quint16 tableIndex;
144  quint16 order;
145  const woffDirEntry *woffDir;
146  quint32 totalLen;
147  sfntHeader *newHeader;
148  quint16 searchRange, rangeShift, entrySelector;
149  quint32 offset;
150  sfntDirEntry *sfntDir;
151  quint32 headOffset = 0, headLength = 0;
152  sfntHeadTable *head;
153  quint32 csum = 0;
154  const quint32 *csumPtr;
155  quint32 oldCheckSumAdjustment;
156  quint32 status = eWOFF_ok;
157 
158  if (pStatus && WOFF_FAILURE(*pStatus)) {
159  return;
160  }
161 
162  /* check basic header fields */
163  header = (const woffHeader *)(woffData);
164  if (READ32BE(header->flavor) != SFNT_VERSION_TT &&
165  READ32BE(header->flavor) != SFNT_VERSION_CFF &&
166  READ32BE(header->flavor) != SFNT_VERSION_true) {
167  status |= eWOFF_warn_unknown_version;
168  }
169 
170  numTables = READ16BE(header->numTables);
171  woffDir = (const woffDirEntry *)(woffData + sizeof(woffHeader));
172 
173  totalLen = READ32BE(header->totalSfntSize);
174 
175  /* construct the sfnt header */
176  newHeader = (sfntHeader *)(sfntData);
177  newHeader->version = header->flavor;
178  newHeader->numTables = READ16BE(numTables);
179 
180  /* calculate header fields for binary search */
181  searchRange = numTables;
182  searchRange |= (searchRange >> 1);
183  searchRange |= (searchRange >> 2);
184  searchRange |= (searchRange >> 4);
185  searchRange |= (searchRange >> 8);
186  searchRange &= ~(searchRange >> 1);
187  searchRange *= 16;
188  newHeader->searchRange = READ16BE(searchRange);
189  rangeShift = numTables * 16 - searchRange;
190  newHeader->rangeShift = READ16BE(rangeShift);
191  entrySelector = 0;
192  while (searchRange > 16) {
193  ++entrySelector;
194  searchRange >>= 1;
195  }
196  newHeader->entrySelector = READ16BE(entrySelector);
197 
198  tableOrder = (tableOrderRec *) malloc(numTables * sizeof(tableOrderRec));
199  if (!tableOrder) {
200  FAIL(eWOFF_out_of_memory);
201  }
202  for (tableIndex = 0; tableIndex < numTables; ++tableIndex) {
203  tableOrder[tableIndex].offset = READ32BE(woffDir[tableIndex].offset);
204  tableOrder[tableIndex].oldIndex = tableIndex;
205  }
206  qsort(tableOrder, numTables, sizeof(tableOrderRec), compareOffsets);
207 
208  /* process each table, filling in the sfnt directory */
209  offset = sizeof(sfntHeader) + numTables * sizeof(sfntDirEntry);
210  sfntDir = (sfntDirEntry *)(sfntData + sizeof(sfntHeader));
211  for (order = 0; order < numTables; ++order) {
212  quint32 origLen, compLen, tag, sourceOffset;
213  tableIndex = tableOrder[order].oldIndex;
214 
215  /* validity of these was confirmed by sanityCheck */
216  origLen = READ32BE(woffDir[tableIndex].origLen);
217  compLen = READ32BE(woffDir[tableIndex].compLen);
218  sourceOffset = READ32BE(woffDir[tableIndex].offset);
219 
220  sfntDir[tableIndex].tag = woffDir[tableIndex].tag;
221  sfntDir[tableIndex].offset = READ32BE(offset);
222  sfntDir[tableIndex].length = woffDir[tableIndex].origLen;
223  sfntDir[tableIndex].checksum = woffDir[tableIndex].checksum;
224  csum += READ32BE(sfntDir[tableIndex].checksum);
225 
226  if (compLen < origLen) {
227  uLongf destLen = origLen;
228  if (uncompress((Bytef *)(sfntData + offset), &destLen,
229  (const Bytef *)(woffData + sourceOffset),
230  compLen) != Z_OK || destLen != origLen) {
231  FAIL(eWOFF_compression_failure);
232  }
233  } else {
234  memcpy(sfntData + offset, woffData + sourceOffset, origLen);
235  }
236 
237  /* note that old Mac bitmap-only fonts have no 'head' table
238  (eg NISC18030.ttf) but a 'bhed' table instead */
239  tag = READ32BE(sfntDir[tableIndex].tag);
240  if (tag == TABLE_TAG_head || tag == TABLE_TAG_bhed) {
241  headOffset = offset;
242  headLength = origLen;
243  }
244 
245  offset += origLen;
246 
247  while (offset < totalLen && (offset & 3) != 0) {
248  sfntData[offset++] = 0;
249  }
250  }
251 
252  if (headOffset > 0) {
253  /* the font checksum in the 'head' table depends on all the individual
254  table checksums (collected above), plus the header and directory
255  which are added in here */
256  if (headLength < HEAD_TABLE_SIZE) {
257  FAIL(eWOFF_invalid);
258  }
259  head = (sfntHeadTable *)(sfntData + headOffset);
260  oldCheckSumAdjustment = READ32BE(head->checkSumAdjustment);
261  head->checkSumAdjustment = 0;
262  csumPtr = (const quint32 *)sfntData;
263  while (csumPtr < (const quint32 *)(sfntData + sizeof(sfntHeader) +
264  numTables * sizeof(sfntDirEntry))) {
265  csum += READ32BE(*csumPtr);
266  csumPtr++;
267  }
268  csum = SFNT_CHECKSUM_CALC_CONST - csum;
269 
270  if (oldCheckSumAdjustment != csum) {
271  /* if the checksum doesn't match, we fix it; but this will invalidate
272  any DSIG that may be present */
273  status |= eWOFF_warn_checksum_mismatch;
274  }
275  head->checkSumAdjustment = READ32BE(csum);
276  }
277 
278  if (pActualSfntLen) {
279  *pActualSfntLen = totalLen;
280  }
281  if (pStatus) {
282  *pStatus |= status;
283  }
284  free(tableOrder);
285  return;
286 
287 failure:
288  if (tableOrder) {
289  free(tableOrder);
290  }
291  if (pActualSfntLen) {
292  *pActualSfntLen = 0;
293  }
294  if (pStatus) {
295  *pStatus = status;
296  }
297 }
298 
299 void WOFF::decodeToBuffer(const char *woffData, int woffLen, char *sfntData, int bufferLen,
300  int *pActualSfntLen, int *pStatus)
301 {
302  quint32 status = eWOFF_ok;
303  quint32 totalLen;
304 
305  if (pStatus && WOFF_FAILURE(*pStatus)) {
306  return;
307  }
308 
309  status = sanityCheck(woffData, woffLen);
310  if (WOFF_FAILURE(status)) {
311  FAIL(status);
312  }
313 
314  if (!sfntData) {
315  FAIL(eWOFF_bad_parameter);
316  }
317 
318  totalLen = READ32BE(((const woffHeader *)(woffData))->totalSfntSize);
319  if ((unsigned)bufferLen < totalLen) {
320  FAIL(eWOFF_buffer_too_small);
321  }
322 
323  woffDecodeToBufferInternal(woffData, sfntData, pActualSfntLen, pStatus);
324  return;
325 
326 failure:
327  if (pActualSfntLen) {
328  *pActualSfntLen = 0;
329  }
330  if (pStatus) {
331  *pStatus = status;
332  }
333 }
Definition: woff.h:69
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Oct 26 2021 22:48:10 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.