1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is thebes gfx code.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2007-2009
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Stuart Parmenter <stuart@mozilla.com>
23 : * John Daggett <jdaggett@mozilla.com>
24 : * Jonathan Kew <jfkthame@gmail.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #ifdef MOZ_LOGGING
41 : #define FORCE_PR_LOG /* Allow logging in the release build */
42 : #endif
43 : #include "prlog.h"
44 :
45 : #include "mozilla/Util.h"
46 :
47 : #include "gfxFontUtils.h"
48 :
49 : #include "nsServiceManagerUtils.h"
50 :
51 : #include "mozilla/Preferences.h"
52 :
53 : #include "nsIStreamBufferAccess.h"
54 : #include "nsIUUIDGenerator.h"
55 : #include "nsMemory.h"
56 : #include "nsICharsetConverterManager.h"
57 :
58 : #include "plbase64.h"
59 : #include "prlog.h"
60 :
61 : #include "woff.h"
62 :
63 : #ifdef XP_MACOSX
64 : #include <CoreFoundation/CoreFoundation.h>
65 : #endif
66 :
67 : #ifdef PR_LOGGING
68 :
69 : #define LOG(log, args) PR_LOG(gfxPlatform::GetLog(log), \
70 : PR_LOG_DEBUG, args)
71 :
72 : #endif // PR_LOGGING
73 :
74 : #define NO_RANGE_FOUND 126 // bit 126 in the font unicode ranges is required to be 0
75 :
76 : #define UNICODE_BMP_LIMIT 0x10000
77 :
78 : using namespace mozilla;
79 :
80 : /* Unicode subrange table
81 : * from: http://msdn.microsoft.com/en-us/library/dd374090
82 : *
83 : * Edit the text to extend the initial digit, then use something like:
84 : * perl -pi -e 's/^(\d+)\t([\dA-Fa-f]+)\s+-\s+([\dA-Fa-f]+)\s+\b([a-zA-Z0-9\(\)\- ]+)/ { \1, 0x\2, 0x\3, \"\4\" },/' < unicoderange.txt
85 : * to generate the below list.
86 : */
87 : struct UnicodeRangeTableEntry
88 : {
89 : PRUint8 bit;
90 : PRUint32 start;
91 : PRUint32 end;
92 : const char *info;
93 : };
94 :
95 : static struct UnicodeRangeTableEntry gUnicodeRanges[] = {
96 : { 0, 0x0000, 0x007F, "Basic Latin" },
97 : { 1, 0x0080, 0x00FF, "Latin-1 Supplement" },
98 : { 2, 0x0100, 0x017F, "Latin Extended-A" },
99 : { 3, 0x0180, 0x024F, "Latin Extended-B" },
100 : { 4, 0x0250, 0x02AF, "IPA Extensions" },
101 : { 4, 0x1D00, 0x1D7F, "Phonetic Extensions" },
102 : { 4, 0x1D80, 0x1DBF, "Phonetic Extensions Supplement" },
103 : { 5, 0x02B0, 0x02FF, "Spacing Modifier Letters" },
104 : { 5, 0xA700, 0xA71F, "Modifier Tone Letters" },
105 : { 6, 0x0300, 0x036F, "Combining Diacritical Marks" },
106 : { 6, 0x1DC0, 0x1DFF, "Combining Diacritical Marks Supplement" },
107 : { 7, 0x0370, 0x03FF, "Greek and Coptic" },
108 : { 8, 0x2C80, 0x2CFF, "Coptic" },
109 : { 9, 0x0400, 0x04FF, "Cyrillic" },
110 : { 9, 0x0500, 0x052F, "Cyrillic Supplement" },
111 : { 9, 0x2DE0, 0x2DFF, "Cyrillic Extended-A" },
112 : { 9, 0xA640, 0xA69F, "Cyrillic Extended-B" },
113 : { 10, 0x0530, 0x058F, "Armenian" },
114 : { 11, 0x0590, 0x05FF, "Hebrew" },
115 : { 12, 0xA500, 0xA63F, "Vai" },
116 : { 13, 0x0600, 0x06FF, "Arabic" },
117 : { 13, 0x0750, 0x077F, "Arabic Supplement" },
118 : { 14, 0x07C0, 0x07FF, "NKo" },
119 : { 15, 0x0900, 0x097F, "Devanagari" },
120 : { 16, 0x0980, 0x09FF, "Bengali" },
121 : { 17, 0x0A00, 0x0A7F, "Gurmukhi" },
122 : { 18, 0x0A80, 0x0AFF, "Gujarati" },
123 : { 19, 0x0B00, 0x0B7F, "Oriya" },
124 : { 20, 0x0B80, 0x0BFF, "Tamil" },
125 : { 21, 0x0C00, 0x0C7F, "Telugu" },
126 : { 22, 0x0C80, 0x0CFF, "Kannada" },
127 : { 23, 0x0D00, 0x0D7F, "Malayalam" },
128 : { 24, 0x0E00, 0x0E7F, "Thai" },
129 : { 25, 0x0E80, 0x0EFF, "Lao" },
130 : { 26, 0x10A0, 0x10FF, "Georgian" },
131 : { 26, 0x2D00, 0x2D2F, "Georgian Supplement" },
132 : { 27, 0x1B00, 0x1B7F, "Balinese" },
133 : { 28, 0x1100, 0x11FF, "Hangul Jamo" },
134 : { 29, 0x1E00, 0x1EFF, "Latin Extended Additional" },
135 : { 29, 0x2C60, 0x2C7F, "Latin Extended-C" },
136 : { 29, 0xA720, 0xA7FF, "Latin Extended-D" },
137 : { 30, 0x1F00, 0x1FFF, "Greek Extended" },
138 : { 31, 0x2000, 0x206F, "General Punctuation" },
139 : { 31, 0x2E00, 0x2E7F, "Supplemental Punctuation" },
140 : { 32, 0x2070, 0x209F, "Superscripts And Subscripts" },
141 : { 33, 0x20A0, 0x20CF, "Currency Symbols" },
142 : { 34, 0x20D0, 0x20FF, "Combining Diacritical Marks For Symbols" },
143 : { 35, 0x2100, 0x214F, "Letterlike Symbols" },
144 : { 36, 0x2150, 0x218F, "Number Forms" },
145 : { 37, 0x2190, 0x21FF, "Arrows" },
146 : { 37, 0x27F0, 0x27FF, "Supplemental Arrows-A" },
147 : { 37, 0x2900, 0x297F, "Supplemental Arrows-B" },
148 : { 37, 0x2B00, 0x2BFF, "Miscellaneous Symbols and Arrows" },
149 : { 38, 0x2200, 0x22FF, "Mathematical Operators" },
150 : { 38, 0x27C0, 0x27EF, "Miscellaneous Mathematical Symbols-A" },
151 : { 38, 0x2980, 0x29FF, "Miscellaneous Mathematical Symbols-B" },
152 : { 38, 0x2A00, 0x2AFF, "Supplemental Mathematical Operators" },
153 : { 39, 0x2300, 0x23FF, "Miscellaneous Technical" },
154 : { 40, 0x2400, 0x243F, "Control Pictures" },
155 : { 41, 0x2440, 0x245F, "Optical Character Recognition" },
156 : { 42, 0x2460, 0x24FF, "Enclosed Alphanumerics" },
157 : { 43, 0x2500, 0x257F, "Box Drawing" },
158 : { 44, 0x2580, 0x259F, "Block Elements" },
159 : { 45, 0x25A0, 0x25FF, "Geometric Shapes" },
160 : { 46, 0x2600, 0x26FF, "Miscellaneous Symbols" },
161 : { 47, 0x2700, 0x27BF, "Dingbats" },
162 : { 48, 0x3000, 0x303F, "CJK Symbols And Punctuation" },
163 : { 49, 0x3040, 0x309F, "Hiragana" },
164 : { 50, 0x30A0, 0x30FF, "Katakana" },
165 : { 50, 0x31F0, 0x31FF, "Katakana Phonetic Extensions" },
166 : { 51, 0x3100, 0x312F, "Bopomofo" },
167 : { 50, 0x31A0, 0x31BF, "Bopomofo Extended" },
168 : { 52, 0x3130, 0x318F, "Hangul Compatibility Jamo" },
169 : { 53, 0xA840, 0xA87F, "Phags-pa" },
170 : { 54, 0x3200, 0x32FF, "Enclosed CJK Letters And Months" },
171 : { 55, 0x3300, 0x33FF, "CJK Compatibility" },
172 : { 56, 0xAC00, 0xD7AF, "Hangul Syllables" },
173 : { 57, 0xD800, 0xDFFF, "Non-Plane 0" },
174 : { 58, 0x10900, 0x1091F, "Phoenician" },
175 : { 59, 0x2E80, 0x2EFF, "CJK Radicals Supplement" },
176 : { 59, 0x2F00, 0x2FDF, "Kangxi Radicals" },
177 : { 59, 0x2FF0, 0x2FFF, "Ideographic Description Characters" },
178 : { 59, 0x3190, 0x319F, "Kanbun" },
179 : { 59, 0x3400, 0x4DBF, "CJK Unified Ideographs Extension A" },
180 : { 59, 0x4E00, 0x9FFF, "CJK Unified Ideographs" },
181 : { 59, 0x20000, 0x2A6DF, "CJK Unified Ideographs Extension B" },
182 : { 60, 0xE000, 0xF8FF, "Private Use Area" },
183 : { 61, 0x31C0, 0x31EF, "CJK Strokes" },
184 : { 61, 0xF900, 0xFAFF, "CJK Compatibility Ideographs" },
185 : { 61, 0x2F800, 0x2FA1F, "CJK Compatibility Ideographs Supplement" },
186 : { 62, 0xFB00, 0xFB4F, "Alphabetic Presentation Forms" },
187 : { 63, 0xFB50, 0xFDFF, "Arabic Presentation Forms-A" },
188 : { 64, 0xFE20, 0xFE2F, "Combining Half Marks" },
189 : { 65, 0xFE10, 0xFE1F, "Vertical Forms" },
190 : { 65, 0xFE30, 0xFE4F, "CJK Compatibility Forms" },
191 : { 66, 0xFE50, 0xFE6F, "Small Form Variants" },
192 : { 67, 0xFE70, 0xFEFF, "Arabic Presentation Forms-B" },
193 : { 68, 0xFF00, 0xFFEF, "Halfwidth And Fullwidth Forms" },
194 : { 69, 0xFFF0, 0xFFFF, "Specials" },
195 : { 70, 0x0F00, 0x0FFF, "Tibetan" },
196 : { 71, 0x0700, 0x074F, "Syriac" },
197 : { 72, 0x0780, 0x07BF, "Thaana" },
198 : { 73, 0x0D80, 0x0DFF, "Sinhala" },
199 : { 74, 0x1000, 0x109F, "Myanmar" },
200 : { 75, 0x1200, 0x137F, "Ethiopic" },
201 : { 75, 0x1380, 0x139F, "Ethiopic Supplement" },
202 : { 75, 0x2D80, 0x2DDF, "Ethiopic Extended" },
203 : { 76, 0x13A0, 0x13FF, "Cherokee" },
204 : { 77, 0x1400, 0x167F, "Unified Canadian Aboriginal Syllabics" },
205 : { 78, 0x1680, 0x169F, "Ogham" },
206 : { 79, 0x16A0, 0x16FF, "Runic" },
207 : { 80, 0x1780, 0x17FF, "Khmer" },
208 : { 80, 0x19E0, 0x19FF, "Khmer Symbols" },
209 : { 81, 0x1800, 0x18AF, "Mongolian" },
210 : { 82, 0x2800, 0x28FF, "Braille Patterns" },
211 : { 83, 0xA000, 0xA48F, "Yi Syllables" },
212 : { 83, 0xA490, 0xA4CF, "Yi Radicals" },
213 : { 84, 0x1700, 0x171F, "Tagalog" },
214 : { 84, 0x1720, 0x173F, "Hanunoo" },
215 : { 84, 0x1740, 0x175F, "Buhid" },
216 : { 84, 0x1760, 0x177F, "Tagbanwa" },
217 : { 85, 0x10300, 0x1032F, "Old Italic" },
218 : { 86, 0x10330, 0x1034F, "Gothic" },
219 : { 87, 0x10400, 0x1044F, "Deseret" },
220 : { 88, 0x1D000, 0x1D0FF, "Byzantine Musical Symbols" },
221 : { 88, 0x1D100, 0x1D1FF, "Musical Symbols" },
222 : { 88, 0x1D200, 0x1D24F, "Ancient Greek Musical Notation" },
223 : { 89, 0x1D400, 0x1D7FF, "Mathematical Alphanumeric Symbols" },
224 : { 90, 0xFF000, 0xFFFFD, "Private Use (plane 15)" },
225 : { 90, 0x100000, 0x10FFFD, "Private Use (plane 16)" },
226 : { 91, 0xFE00, 0xFE0F, "Variation Selectors" },
227 : { 91, 0xE0100, 0xE01EF, "Variation Selectors Supplement" },
228 : { 92, 0xE0000, 0xE007F, "Tags" },
229 : { 93, 0x1900, 0x194F, "Limbu" },
230 : { 94, 0x1950, 0x197F, "Tai Le" },
231 : { 95, 0x1980, 0x19DF, "New Tai Lue" },
232 : { 96, 0x1A00, 0x1A1F, "Buginese" },
233 : { 97, 0x2C00, 0x2C5F, "Glagolitic" },
234 : { 98, 0x2D30, 0x2D7F, "Tifinagh" },
235 : { 99, 0x4DC0, 0x4DFF, "Yijing Hexagram Symbols" },
236 : { 100, 0xA800, 0xA82F, "Syloti Nagri" },
237 : { 101, 0x10000, 0x1007F, "Linear B Syllabary" },
238 : { 101, 0x10080, 0x100FF, "Linear B Ideograms" },
239 : { 101, 0x10100, 0x1013F, "Aegean Numbers" },
240 : { 102, 0x10140, 0x1018F, "Ancient Greek Numbers" },
241 : { 103, 0x10380, 0x1039F, "Ugaritic" },
242 : { 104, 0x103A0, 0x103DF, "Old Persian" },
243 : { 105, 0x10450, 0x1047F, "Shavian" },
244 : { 106, 0x10480, 0x104AF, "Osmanya" },
245 : { 107, 0x10800, 0x1083F, "Cypriot Syllabary" },
246 : { 108, 0x10A00, 0x10A5F, "Kharoshthi" },
247 : { 109, 0x1D300, 0x1D35F, "Tai Xuan Jing Symbols" },
248 : { 110, 0x12000, 0x123FF, "Cuneiform" },
249 : { 110, 0x12400, 0x1247F, "Cuneiform Numbers and Punctuation" },
250 : { 111, 0x1D360, 0x1D37F, "Counting Rod Numerals" },
251 : { 112, 0x1B80, 0x1BBF, "Sundanese" },
252 : { 113, 0x1C00, 0x1C4F, "Lepcha" },
253 : { 114, 0x1C50, 0x1C7F, "Ol Chiki" },
254 : { 115, 0xA880, 0xA8DF, "Saurashtra" },
255 : { 116, 0xA900, 0xA92F, "Kayah Li" },
256 : { 117, 0xA930, 0xA95F, "Rejang" },
257 : { 118, 0xAA00, 0xAA5F, "Cham" },
258 : { 119, 0x10190, 0x101CF, "Ancient Symbols" },
259 : { 120, 0x101D0, 0x101FF, "Phaistos Disc" },
260 : { 121, 0x10280, 0x1029F, "Lycian" },
261 : { 121, 0x102A0, 0x102DF, "Carian" },
262 : { 121, 0x10920, 0x1093F, "Lydian" },
263 : { 122, 0x1F000, 0x1F02F, "Mahjong Tiles" },
264 : { 122, 0x1F030, 0x1F09F, "Domino Tiles" }
265 : };
266 :
267 : #pragma pack(1)
268 :
269 : typedef struct {
270 : AutoSwap_PRUint16 format;
271 : AutoSwap_PRUint16 reserved;
272 : AutoSwap_PRUint32 length;
273 : AutoSwap_PRUint32 language;
274 : AutoSwap_PRUint32 numGroups;
275 : } Format12CmapHeader;
276 :
277 : typedef struct {
278 : AutoSwap_PRUint32 startCharCode;
279 : AutoSwap_PRUint32 endCharCode;
280 : AutoSwap_PRUint32 startGlyphId;
281 : } Format12Group;
282 :
283 : #pragma pack()
284 :
285 : #if PR_LOGGING
286 : void
287 0 : gfxSparseBitSet::Dump(const char* aPrefix, eGfxLog aWhichLog) const
288 : {
289 0 : NS_ASSERTION(mBlocks.DebugGetHeader(), "mHdr is null, this is bad");
290 0 : PRUint32 b, numBlocks = mBlocks.Length();
291 :
292 0 : for (b = 0; b < numBlocks; b++) {
293 0 : Block *block = mBlocks[b];
294 0 : if (!block) continue;
295 : char outStr[256];
296 0 : int index = 0;
297 0 : index += sprintf(&outStr[index], "%s u+%6.6x [", aPrefix, (b << BLOCK_INDEX_SHIFT));
298 0 : for (int i = 0; i < 32; i += 4) {
299 0 : for (int j = i; j < i + 4; j++) {
300 0 : PRUint8 bits = block->mBits[j];
301 0 : PRUint8 flip1 = ((bits & 0xaa) >> 1) | ((bits & 0x55) << 1);
302 0 : PRUint8 flip2 = ((flip1 & 0xcc) >> 2) | ((flip1 & 0x33) << 2);
303 0 : PRUint8 flipped = ((flip2 & 0xf0) >> 4) | ((flip2 & 0x0f) << 4);
304 :
305 0 : index += sprintf(&outStr[index], "%2.2x", flipped);
306 : }
307 0 : if (i + 4 != 32) index += sprintf(&outStr[index], " ");
308 : }
309 0 : index += sprintf(&outStr[index], "]");
310 0 : LOG(aWhichLog, ("%s", outStr));
311 : }
312 0 : }
313 : #endif
314 :
315 :
316 : nsresult
317 0 : gfxFontUtils::ReadCMAPTableFormat12(const PRUint8 *aBuf, PRUint32 aLength,
318 : gfxSparseBitSet& aCharacterMap)
319 : {
320 : // Ensure table is large enough that we can safely read the header
321 0 : NS_ENSURE_TRUE(aLength >= sizeof(Format12CmapHeader),
322 : NS_ERROR_GFX_CMAP_MALFORMED);
323 :
324 : // Sanity-check header fields
325 : const Format12CmapHeader *cmap12 =
326 0 : reinterpret_cast<const Format12CmapHeader*>(aBuf);
327 0 : NS_ENSURE_TRUE(PRUint16(cmap12->format) == 12,
328 : NS_ERROR_GFX_CMAP_MALFORMED);
329 0 : NS_ENSURE_TRUE(PRUint16(cmap12->reserved) == 0,
330 : NS_ERROR_GFX_CMAP_MALFORMED);
331 :
332 0 : PRUint32 tablelen = cmap12->length;
333 0 : NS_ENSURE_TRUE(tablelen >= sizeof(Format12CmapHeader) &&
334 : tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED);
335 :
336 0 : NS_ENSURE_TRUE(cmap12->language == 0, NS_ERROR_GFX_CMAP_MALFORMED);
337 :
338 : // Check that the table is large enough for the group array
339 0 : const PRUint32 numGroups = cmap12->numGroups;
340 0 : NS_ENSURE_TRUE((tablelen - sizeof(Format12CmapHeader)) /
341 : sizeof(Format12Group) >= numGroups,
342 : NS_ERROR_GFX_CMAP_MALFORMED);
343 :
344 : // The array of groups immediately follows the subtable header.
345 : const Format12Group *group =
346 0 : reinterpret_cast<const Format12Group*>(aBuf + sizeof(Format12CmapHeader));
347 :
348 : // Check that groups are in correct order and do not overlap,
349 : // and record character coverage in aCharacterMap.
350 0 : PRUint32 prevEndCharCode = 0;
351 0 : for (PRUint32 i = 0; i < numGroups; i++, group++) {
352 0 : const PRUint32 startCharCode = group->startCharCode;
353 0 : const PRUint32 endCharCode = group->endCharCode;
354 0 : NS_ENSURE_TRUE((prevEndCharCode < startCharCode || i == 0) &&
355 : startCharCode <= endCharCode &&
356 : endCharCode <= CMAP_MAX_CODEPOINT,
357 : NS_ERROR_GFX_CMAP_MALFORMED);
358 0 : aCharacterMap.SetRange(startCharCode, endCharCode);
359 0 : prevEndCharCode = endCharCode;
360 : }
361 :
362 0 : aCharacterMap.Compact();
363 :
364 0 : return NS_OK;
365 : }
366 :
367 : nsresult
368 0 : gfxFontUtils::ReadCMAPTableFormat4(const PRUint8 *aBuf, PRUint32 aLength,
369 : gfxSparseBitSet& aCharacterMap)
370 : {
371 : enum {
372 : OffsetFormat = 0,
373 : OffsetLength = 2,
374 : OffsetLanguage = 4,
375 : OffsetSegCountX2 = 6
376 : };
377 :
378 0 : NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 4,
379 : NS_ERROR_GFX_CMAP_MALFORMED);
380 0 : PRUint16 tablelen = ReadShortAt(aBuf, OffsetLength);
381 0 : NS_ENSURE_TRUE(tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED);
382 0 : NS_ENSURE_TRUE(tablelen > 16, NS_ERROR_GFX_CMAP_MALFORMED);
383 :
384 : // This field should normally (except for Mac platform subtables) be zero according to
385 : // the OT spec, but some buggy fonts have lang = 1 (which would be English for MacOS).
386 : // E.g. Arial Narrow Bold, v. 1.1 (Tiger), Arial Unicode MS (see bug 530614).
387 : // So accept either zero or one here; the error should be harmless.
388 0 : NS_ENSURE_TRUE((ReadShortAt(aBuf, OffsetLanguage) & 0xfffe) == 0,
389 : NS_ERROR_GFX_CMAP_MALFORMED);
390 :
391 0 : PRUint16 segCountX2 = ReadShortAt(aBuf, OffsetSegCountX2);
392 0 : NS_ENSURE_TRUE(tablelen >= 16 + (segCountX2 * 4),
393 : NS_ERROR_GFX_CMAP_MALFORMED);
394 :
395 0 : const PRUint16 segCount = segCountX2 / 2;
396 :
397 0 : const PRUint16 *endCounts = reinterpret_cast<const PRUint16*>(aBuf + 14);
398 0 : const PRUint16 *startCounts = endCounts + 1 /* skip one uint16 for reservedPad */ + segCount;
399 0 : const PRUint16 *idDeltas = startCounts + segCount;
400 0 : const PRUint16 *idRangeOffsets = idDeltas + segCount;
401 0 : PRUint16 prevEndCount = 0;
402 0 : for (PRUint16 i = 0; i < segCount; i++) {
403 0 : const PRUint16 endCount = ReadShortAt16(endCounts, i);
404 0 : const PRUint16 startCount = ReadShortAt16(startCounts, i);
405 0 : const PRUint16 idRangeOffset = ReadShortAt16(idRangeOffsets, i);
406 :
407 : // sanity-check range
408 : // This permits ranges to overlap by 1 character, which is strictly
409 : // incorrect but occurs in Baskerville on OS X 10.7 (see bug 689087),
410 : // and appears to be harmless in practice
411 0 : NS_ENSURE_TRUE(startCount >= prevEndCount && startCount <= endCount,
412 : NS_ERROR_GFX_CMAP_MALFORMED);
413 0 : prevEndCount = endCount;
414 :
415 0 : if (idRangeOffset == 0) {
416 0 : aCharacterMap.SetRange(startCount, endCount);
417 : } else {
418 : // const PRUint16 idDelta = ReadShortAt16(idDeltas, i); // Unused: self-documenting.
419 0 : for (PRUint32 c = startCount; c <= endCount; ++c) {
420 0 : if (c == 0xFFFF)
421 0 : break;
422 :
423 : const PRUint16 *gdata = (idRangeOffset/2
424 : + (c - startCount)
425 0 : + &idRangeOffsets[i]);
426 :
427 0 : NS_ENSURE_TRUE((PRUint8*)gdata > aBuf &&
428 : (PRUint8*)gdata < aBuf + aLength,
429 : NS_ERROR_GFX_CMAP_MALFORMED);
430 :
431 : // make sure we have a glyph
432 0 : if (*gdata != 0) {
433 : // The glyph index at this point is:
434 : // glyph = (ReadShortAt16(idDeltas, i) + *gdata) % 65536;
435 :
436 0 : aCharacterMap.set(c);
437 : }
438 : }
439 : }
440 : }
441 :
442 0 : aCharacterMap.Compact();
443 :
444 0 : return NS_OK;
445 : }
446 :
447 : nsresult
448 0 : gfxFontUtils::ReadCMAPTableFormat14(const PRUint8 *aBuf, PRUint32 aLength,
449 : PRUint8*& aTable)
450 : {
451 : enum {
452 : OffsetFormat = 0,
453 : OffsetTableLength = 2,
454 : OffsetNumVarSelectorRecords = 6,
455 : OffsetVarSelectorRecords = 10,
456 :
457 : SizeOfVarSelectorRecord = 11,
458 : VSRecOffsetVarSelector = 0,
459 : VSRecOffsetDefUVSOffset = 3,
460 : VSRecOffsetNonDefUVSOffset = 7,
461 :
462 : SizeOfDefUVSTable = 4,
463 : DefUVSOffsetStartUnicodeValue = 0,
464 : DefUVSOffsetAdditionalCount = 3,
465 :
466 : SizeOfNonDefUVSTable = 5,
467 : NonDefUVSOffsetUnicodeValue = 0,
468 : NonDefUVSOffsetGlyphID = 3
469 : };
470 0 : NS_ENSURE_TRUE(aLength >= OffsetVarSelectorRecords,
471 : NS_ERROR_GFX_CMAP_MALFORMED);
472 :
473 0 : NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 14,
474 : NS_ERROR_GFX_CMAP_MALFORMED);
475 :
476 0 : PRUint32 tablelen = ReadLongAt(aBuf, OffsetTableLength);
477 0 : NS_ENSURE_TRUE(tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED);
478 0 : NS_ENSURE_TRUE(tablelen >= OffsetVarSelectorRecords,
479 : NS_ERROR_GFX_CMAP_MALFORMED);
480 :
481 0 : const PRUint32 numVarSelectorRecords = ReadLongAt(aBuf, OffsetNumVarSelectorRecords);
482 0 : NS_ENSURE_TRUE((tablelen - OffsetVarSelectorRecords) /
483 : SizeOfVarSelectorRecord >= numVarSelectorRecords,
484 : NS_ERROR_GFX_CMAP_MALFORMED);
485 :
486 0 : const PRUint8 *records = aBuf + OffsetVarSelectorRecords;
487 0 : for (PRUint32 i = 0; i < numVarSelectorRecords;
488 : i++, records += SizeOfVarSelectorRecord) {
489 0 : const PRUint32 varSelector = ReadUint24At(records, VSRecOffsetVarSelector);
490 0 : const PRUint32 defUVSOffset = ReadLongAt(records, VSRecOffsetDefUVSOffset);
491 0 : const PRUint32 nonDefUVSOffset = ReadLongAt(records, VSRecOffsetNonDefUVSOffset);
492 0 : NS_ENSURE_TRUE(varSelector <= CMAP_MAX_CODEPOINT &&
493 : defUVSOffset <= tablelen - 4 &&
494 : nonDefUVSOffset <= tablelen - 4,
495 : NS_ERROR_GFX_CMAP_MALFORMED);
496 :
497 0 : if (defUVSOffset) {
498 0 : const PRUint32 numUnicodeValueRanges = ReadLongAt(aBuf, defUVSOffset);
499 0 : NS_ENSURE_TRUE((tablelen - defUVSOffset) /
500 : SizeOfDefUVSTable >= numUnicodeValueRanges,
501 : NS_ERROR_GFX_CMAP_MALFORMED);
502 0 : const PRUint8 *tables = aBuf + defUVSOffset + 4;
503 0 : PRUint32 prevEndUnicode = 0;
504 0 : for (PRUint32 j = 0; j < numUnicodeValueRanges; j++, tables += SizeOfDefUVSTable) {
505 0 : const PRUint32 startUnicode = ReadUint24At(tables, DefUVSOffsetStartUnicodeValue);
506 0 : const PRUint32 endUnicode = startUnicode + tables[DefUVSOffsetAdditionalCount];
507 0 : NS_ENSURE_TRUE((prevEndUnicode < startUnicode || j == 0) &&
508 : endUnicode <= CMAP_MAX_CODEPOINT,
509 : NS_ERROR_GFX_CMAP_MALFORMED);
510 0 : prevEndUnicode = endUnicode;
511 : }
512 : }
513 :
514 0 : if (nonDefUVSOffset) {
515 0 : const PRUint32 numUVSMappings = ReadLongAt(aBuf, nonDefUVSOffset);
516 0 : NS_ENSURE_TRUE((tablelen - nonDefUVSOffset) /
517 : SizeOfNonDefUVSTable >= numUVSMappings,
518 : NS_ERROR_GFX_CMAP_MALFORMED);
519 0 : const PRUint8 *tables = aBuf + nonDefUVSOffset + 4;
520 0 : PRUint32 prevUnicode = 0;
521 0 : for (PRUint32 j = 0; j < numUVSMappings; j++, tables += SizeOfNonDefUVSTable) {
522 0 : const PRUint32 unicodeValue = ReadUint24At(tables, NonDefUVSOffsetUnicodeValue);
523 0 : NS_ENSURE_TRUE((prevUnicode < unicodeValue || j == 0) &&
524 : unicodeValue <= CMAP_MAX_CODEPOINT,
525 : NS_ERROR_GFX_CMAP_MALFORMED);
526 0 : prevUnicode = unicodeValue;
527 : }
528 : }
529 : }
530 :
531 0 : aTable = new PRUint8[tablelen];
532 0 : memcpy(aTable, aBuf, tablelen);
533 :
534 0 : return NS_OK;
535 : }
536 :
537 : // Windows requires fonts to have a format-4 cmap with a Microsoft ID (3). On the Mac, fonts either have
538 : // a format-4 cmap with Microsoft platform/encoding id or they have one with a platformID == Unicode (0)
539 : // For fonts with two format-4 tables, the first one (Unicode platform) is preferred on the Mac.
540 :
541 : #if defined(XP_MACOSX)
542 : #define acceptableFormat4(p,e,k) (((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDMicrosoft && !(k)) || \
543 : ((p) == PLATFORM_ID_UNICODE))
544 :
545 : #define acceptableUCS4Encoding(p, e, k) \
546 : (((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDUCS4ForMicrosoftPlatform) && (k) != 12 || \
547 : ((p) == PLATFORM_ID_UNICODE && \
548 : ((e) == EncodingIDDefaultForUnicodePlatform || (e) >= EncodingIDUCS4ForUnicodePlatform)))
549 : #else
550 : #define acceptableFormat4(p,e,k) ((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDMicrosoft)
551 :
552 : #define acceptableUCS4Encoding(p, e, k) \
553 : ((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDUCS4ForMicrosoftPlatform)
554 : #endif
555 :
556 : #define acceptablePlatform(p) ((p) == PLATFORM_ID_UNICODE || (p) == PLATFORM_ID_MICROSOFT)
557 : #define isSymbol(p,e) ((p) == PLATFORM_ID_MICROSOFT && (e) == EncodingIDSymbol)
558 : #define isUVSEncoding(p, e) ((p) == PLATFORM_ID_UNICODE && (e) == EncodingIDUVSForUnicodePlatform)
559 :
560 : PRUint32
561 0 : gfxFontUtils::FindPreferredSubtable(const PRUint8 *aBuf, PRUint32 aBufLength,
562 : PRUint32 *aTableOffset,
563 : PRUint32 *aUVSTableOffset,
564 : bool *aSymbolEncoding)
565 : {
566 : enum {
567 : OffsetVersion = 0,
568 : OffsetNumTables = 2,
569 : SizeOfHeader = 4,
570 :
571 : TableOffsetPlatformID = 0,
572 : TableOffsetEncodingID = 2,
573 : TableOffsetOffset = 4,
574 : SizeOfTable = 8,
575 :
576 : SubtableOffsetFormat = 0
577 : };
578 : enum {
579 : EncodingIDSymbol = 0,
580 : EncodingIDMicrosoft = 1,
581 : EncodingIDDefaultForUnicodePlatform = 0,
582 : EncodingIDUCS4ForUnicodePlatform = 3,
583 : EncodingIDUVSForUnicodePlatform = 5,
584 : EncodingIDUCS4ForMicrosoftPlatform = 10
585 : };
586 :
587 0 : if (aUVSTableOffset) {
588 0 : *aUVSTableOffset = nsnull;
589 : }
590 :
591 0 : if (!aBuf || aBufLength < SizeOfHeader) {
592 : // cmap table is missing, or too small to contain header fields!
593 0 : return 0;
594 : }
595 :
596 : // PRUint16 version = ReadShortAt(aBuf, OffsetVersion); // Unused: self-documenting.
597 0 : PRUint16 numTables = ReadShortAt(aBuf, OffsetNumTables);
598 0 : if (aBufLength < PRUint32(SizeOfHeader + numTables * SizeOfTable)) {
599 0 : return 0;
600 : }
601 :
602 : // save the format we want here
603 0 : PRUint32 keepFormat = 0;
604 :
605 0 : const PRUint8 *table = aBuf + SizeOfHeader;
606 0 : for (PRUint16 i = 0; i < numTables; ++i, table += SizeOfTable) {
607 0 : const PRUint16 platformID = ReadShortAt(table, TableOffsetPlatformID);
608 0 : if (!acceptablePlatform(platformID))
609 0 : continue;
610 :
611 0 : const PRUint16 encodingID = ReadShortAt(table, TableOffsetEncodingID);
612 0 : const PRUint32 offset = ReadLongAt(table, TableOffsetOffset);
613 0 : if (aBufLength - 2 < offset) {
614 : // this subtable is not valid - beyond end of buffer
615 0 : return 0;
616 : }
617 :
618 0 : const PRUint8 *subtable = aBuf + offset;
619 0 : const PRUint16 format = ReadShortAt(subtable, SubtableOffsetFormat);
620 :
621 0 : if (isSymbol(platformID, encodingID)) {
622 0 : keepFormat = format;
623 0 : *aTableOffset = offset;
624 0 : *aSymbolEncoding = true;
625 0 : break;
626 0 : } else if (format == 4 && acceptableFormat4(platformID, encodingID, keepFormat)) {
627 0 : keepFormat = format;
628 0 : *aTableOffset = offset;
629 0 : *aSymbolEncoding = false;
630 0 : } else if (format == 12 && acceptableUCS4Encoding(platformID, encodingID, keepFormat)) {
631 0 : keepFormat = format;
632 0 : *aTableOffset = offset;
633 0 : *aSymbolEncoding = false;
634 0 : if (platformID > PLATFORM_ID_UNICODE || !aUVSTableOffset || *aUVSTableOffset) {
635 0 : break; // we don't want to try anything else when this format is available.
636 : }
637 0 : } else if (format == 14 && isUVSEncoding(platformID, encodingID) && aUVSTableOffset) {
638 0 : *aUVSTableOffset = offset;
639 0 : if (keepFormat == 12) {
640 0 : break;
641 : }
642 : }
643 : }
644 :
645 0 : return keepFormat;
646 : }
647 :
648 : nsresult
649 0 : gfxFontUtils::ReadCMAP(const PRUint8 *aBuf, PRUint32 aBufLength,
650 : gfxSparseBitSet& aCharacterMap,
651 : PRUint32& aUVSOffset,
652 : bool& aUnicodeFont, bool& aSymbolFont)
653 : {
654 : PRUint32 offset;
655 : bool symbol;
656 : PRUint32 format = FindPreferredSubtable(aBuf, aBufLength,
657 0 : &offset, &aUVSOffset, &symbol);
658 :
659 0 : if (format == 4) {
660 0 : if (symbol) {
661 0 : aUnicodeFont = false;
662 0 : aSymbolFont = true;
663 : } else {
664 0 : aUnicodeFont = true;
665 0 : aSymbolFont = false;
666 : }
667 : return ReadCMAPTableFormat4(aBuf + offset, aBufLength - offset,
668 0 : aCharacterMap);
669 : }
670 :
671 0 : if (format == 12) {
672 0 : aUnicodeFont = true;
673 0 : aSymbolFont = false;
674 : return ReadCMAPTableFormat12(aBuf + offset, aBufLength - offset,
675 0 : aCharacterMap);
676 : }
677 :
678 0 : return NS_ERROR_FAILURE;
679 : }
680 :
681 : #pragma pack(1)
682 :
683 : typedef struct {
684 : AutoSwap_PRUint16 format;
685 : AutoSwap_PRUint16 length;
686 : AutoSwap_PRUint16 language;
687 : AutoSwap_PRUint16 segCountX2;
688 : AutoSwap_PRUint16 searchRange;
689 : AutoSwap_PRUint16 entrySelector;
690 : AutoSwap_PRUint16 rangeShift;
691 :
692 : AutoSwap_PRUint16 arrays[1];
693 : } Format4Cmap;
694 :
695 : typedef struct {
696 : AutoSwap_PRUint16 format;
697 : AutoSwap_PRUint32 length;
698 : AutoSwap_PRUint32 numVarSelectorRecords;
699 :
700 : typedef struct {
701 : AutoSwap_PRUint24 varSelector;
702 : AutoSwap_PRUint32 defaultUVSOffset;
703 : AutoSwap_PRUint32 nonDefaultUVSOffset;
704 : } VarSelectorRecord;
705 :
706 : VarSelectorRecord varSelectorRecords[1];
707 : } Format14Cmap;
708 :
709 : typedef struct {
710 : AutoSwap_PRUint32 numUVSMappings;
711 :
712 : typedef struct {
713 : AutoSwap_PRUint24 unicodeValue;
714 : AutoSwap_PRUint16 glyphID;
715 : } UVSMapping;
716 :
717 : UVSMapping uvsMappings[1];
718 : } NonDefUVSTable;
719 :
720 : #pragma pack()
721 :
722 : PRUint32
723 0 : gfxFontUtils::MapCharToGlyphFormat4(const PRUint8 *aBuf, PRUnichar aCh)
724 : {
725 0 : const Format4Cmap *cmap4 = reinterpret_cast<const Format4Cmap*>(aBuf);
726 : PRUint16 segCount;
727 : const AutoSwap_PRUint16 *endCodes;
728 : const AutoSwap_PRUint16 *startCodes;
729 : const AutoSwap_PRUint16 *idDelta;
730 : const AutoSwap_PRUint16 *idRangeOffset;
731 : PRUint16 probe;
732 : PRUint16 rangeShiftOver2;
733 : PRUint16 index;
734 :
735 0 : segCount = (PRUint16)(cmap4->segCountX2) / 2;
736 :
737 0 : endCodes = &cmap4->arrays[0];
738 0 : startCodes = &cmap4->arrays[segCount + 1]; // +1 for reserved word between arrays
739 0 : idDelta = &startCodes[segCount];
740 0 : idRangeOffset = &idDelta[segCount];
741 :
742 0 : probe = 1 << (PRUint16)(cmap4->entrySelector);
743 0 : rangeShiftOver2 = (PRUint16)(cmap4->rangeShift) / 2;
744 :
745 0 : if ((PRUint16)(startCodes[rangeShiftOver2]) <= aCh) {
746 0 : index = rangeShiftOver2;
747 : } else {
748 0 : index = 0;
749 : }
750 :
751 0 : while (probe > 1) {
752 0 : probe >>= 1;
753 0 : if ((PRUint16)(startCodes[index + probe]) <= aCh) {
754 0 : index += probe;
755 : }
756 : }
757 :
758 0 : if (aCh >= (PRUint16)(startCodes[index]) && aCh <= (PRUint16)(endCodes[index])) {
759 : PRUint16 result;
760 0 : if ((PRUint16)(idRangeOffset[index]) == 0) {
761 0 : result = aCh;
762 : } else {
763 0 : PRUint16 offset = aCh - (PRUint16)(startCodes[index]);
764 : const AutoSwap_PRUint16 *glyphIndexTable =
765 : (const AutoSwap_PRUint16*)((const char*)&idRangeOffset[index] +
766 0 : (PRUint16)(idRangeOffset[index]));
767 0 : result = glyphIndexTable[offset];
768 : }
769 :
770 : // note that this is unsigned 16-bit arithmetic, and may wrap around
771 0 : result += (PRUint16)(idDelta[index]);
772 0 : return result;
773 : }
774 :
775 0 : return 0;
776 : }
777 :
778 : PRUint32
779 0 : gfxFontUtils::MapCharToGlyphFormat12(const PRUint8 *aBuf, PRUint32 aCh)
780 : {
781 : const Format12CmapHeader *cmap12 =
782 0 : reinterpret_cast<const Format12CmapHeader*>(aBuf);
783 :
784 : // We know that numGroups is within range for the subtable size
785 : // because it was checked by ReadCMAPTableFormat12.
786 0 : PRUint32 numGroups = cmap12->numGroups;
787 :
788 : // The array of groups immediately follows the subtable header.
789 : const Format12Group *groups =
790 0 : reinterpret_cast<const Format12Group*>(aBuf + sizeof(Format12CmapHeader));
791 :
792 : // For most efficient binary search, we want to work on a range that
793 : // is a power of 2 so that we can always halve it by shifting.
794 : // So we find the largest power of 2 that is <= numGroups.
795 : // We will offset this range by rangeOffset so as to reach the end
796 : // of the table, provided that doesn't put us beyond the target
797 : // value from the outset.
798 0 : PRUint32 powerOf2 = mozilla::FindHighestBit(numGroups);
799 0 : PRUint32 rangeOffset = numGroups - powerOf2;
800 0 : PRUint32 range = 0;
801 : PRUint32 startCharCode;
802 :
803 0 : if (groups[rangeOffset].startCharCode <= aCh) {
804 0 : range = rangeOffset;
805 : }
806 :
807 : // Repeatedly halve the size of the range until we find the target group
808 0 : while (powerOf2 > 1) {
809 0 : powerOf2 >>= 1;
810 0 : if (groups[range + powerOf2].startCharCode <= aCh) {
811 0 : range += powerOf2;
812 : }
813 : }
814 :
815 : // Check if the character is actually present in the range and return
816 : // the corresponding glyph ID
817 0 : startCharCode = groups[range].startCharCode;
818 0 : if (startCharCode <= aCh && groups[range].endCharCode >= aCh) {
819 0 : return groups[range].startGlyphId + aCh - startCharCode;
820 : }
821 :
822 : // Else it's not present, so return the .notdef glyph
823 0 : return 0;
824 : }
825 :
826 : PRUint16
827 0 : gfxFontUtils::MapUVSToGlyphFormat14(const PRUint8 *aBuf, PRUint32 aCh, PRUint32 aVS)
828 : {
829 0 : const Format14Cmap *cmap14 = reinterpret_cast<const Format14Cmap*>(aBuf);
830 :
831 : // binary search in varSelectorRecords
832 0 : PRUint32 min = 0;
833 0 : PRUint32 max = cmap14->numVarSelectorRecords;
834 0 : PRUint32 nonDefUVSOffset = 0;
835 0 : while (min < max) {
836 0 : PRUint32 index = (min + max) >> 1;
837 0 : PRUint32 varSelector = cmap14->varSelectorRecords[index].varSelector;
838 0 : if (aVS == varSelector) {
839 0 : nonDefUVSOffset = cmap14->varSelectorRecords[index].nonDefaultUVSOffset;
840 0 : break;
841 : }
842 0 : if (aVS < varSelector) {
843 0 : max = index;
844 : } else {
845 0 : min = index + 1;
846 : }
847 : }
848 0 : if (!nonDefUVSOffset) {
849 0 : return 0;
850 : }
851 :
852 : const NonDefUVSTable *table = reinterpret_cast<const NonDefUVSTable*>
853 0 : (aBuf + nonDefUVSOffset);
854 :
855 : // binary search in uvsMappings
856 0 : min = 0;
857 0 : max = table->numUVSMappings;
858 0 : while (min < max) {
859 0 : PRUint32 index = (min + max) >> 1;
860 0 : PRUint32 unicodeValue = table->uvsMappings[index].unicodeValue;
861 0 : if (aCh == unicodeValue) {
862 0 : return table->uvsMappings[index].glyphID;
863 : }
864 0 : if (aCh < unicodeValue) {
865 0 : max = index;
866 : } else {
867 0 : min = index + 1;
868 : }
869 : }
870 :
871 0 : return 0;
872 : }
873 :
874 : PRUint32
875 0 : gfxFontUtils::MapCharToGlyph(const PRUint8 *aBuf, PRUint32 aBufLength,
876 : PRUint32 aCh)
877 : {
878 : PRUint32 offset;
879 : bool symbol;
880 : PRUint32 format = FindPreferredSubtable(aBuf, aBufLength, &offset,
881 0 : nsnull, &symbol);
882 :
883 0 : switch (format) {
884 : case 4:
885 : return aCh < UNICODE_BMP_LIMIT ?
886 0 : MapCharToGlyphFormat4(aBuf + offset, PRUnichar(aCh)) : 0;
887 : case 12:
888 0 : return MapCharToGlyphFormat12(aBuf + offset, aCh);
889 : default:
890 0 : return 0;
891 : }
892 : }
893 :
894 0 : PRUint8 gfxFontUtils::CharRangeBit(PRUint32 ch) {
895 0 : const PRUint32 n = sizeof(gUnicodeRanges) / sizeof(struct UnicodeRangeTableEntry);
896 :
897 0 : for (PRUint32 i = 0; i < n; ++i)
898 0 : if (ch >= gUnicodeRanges[i].start && ch <= gUnicodeRanges[i].end)
899 0 : return gUnicodeRanges[i].bit;
900 :
901 0 : return NO_RANGE_FOUND;
902 : }
903 :
904 0 : void gfxFontUtils::GetPrefsFontList(const char *aPrefName, nsTArray<nsString>& aFontList)
905 : {
906 0 : const PRUnichar kComma = PRUnichar(',');
907 :
908 0 : aFontList.Clear();
909 :
910 : // get the list of single-face font families
911 0 : nsAdoptingString fontlistValue = Preferences::GetString(aPrefName);
912 0 : if (!fontlistValue) {
913 : return;
914 : }
915 :
916 : // append each font name to the list
917 0 : nsAutoString fontname;
918 : const PRUnichar *p, *p_end;
919 0 : fontlistValue.BeginReading(p);
920 0 : fontlistValue.EndReading(p_end);
921 :
922 0 : while (p < p_end) {
923 0 : const PRUnichar *nameStart = p;
924 0 : while (++p != p_end && *p != kComma)
925 : /* nothing */ ;
926 :
927 : // pull out a single name and clean out leading/trailing whitespace
928 0 : fontname = Substring(nameStart, p);
929 0 : fontname.CompressWhitespace(true, true);
930 :
931 : // append it to the list
932 0 : aFontList.AppendElement(fontname);
933 0 : ++p;
934 : }
935 :
936 : }
937 :
938 : // produce a unique font name that is (1) a valid Postscript name and (2) less
939 : // than 31 characters in length. Using AddFontMemResourceEx on Windows fails
940 : // for names longer than 30 characters in length.
941 :
942 : #define MAX_B64_LEN 32
943 :
944 0 : nsresult gfxFontUtils::MakeUniqueUserFontName(nsAString& aName)
945 : {
946 : nsCOMPtr<nsIUUIDGenerator> uuidgen =
947 0 : do_GetService("@mozilla.org/uuid-generator;1");
948 0 : NS_ENSURE_TRUE(uuidgen, NS_ERROR_OUT_OF_MEMORY);
949 :
950 : nsID guid;
951 :
952 : NS_ASSERTION(sizeof(guid) * 2 <= MAX_B64_LEN, "size of nsID has changed!");
953 :
954 0 : nsresult rv = uuidgen->GenerateUUIDInPlace(&guid);
955 0 : NS_ENSURE_SUCCESS(rv, rv);
956 :
957 0 : char guidB64[MAX_B64_LEN] = {0};
958 :
959 0 : if (!PL_Base64Encode(reinterpret_cast<char*>(&guid), sizeof(guid), guidB64))
960 0 : return NS_ERROR_FAILURE;
961 :
962 : // all b64 characters except for '/' are allowed in Postscript names, so convert / ==> -
963 : char *p;
964 0 : for (p = guidB64; *p; p++) {
965 0 : if (*p == '/')
966 0 : *p = '-';
967 : }
968 :
969 0 : aName.Assign(NS_LITERAL_STRING("uf"));
970 0 : aName.AppendASCII(guidB64);
971 0 : return NS_OK;
972 : }
973 :
974 :
975 : // TrueType/OpenType table handling code
976 :
977 : // need byte aligned structs
978 : #pragma pack(1)
979 :
980 : // name table stores set of name record structures, followed by
981 : // large block containing all the strings. name record offset and length
982 : // indicates the offset and length within that block.
983 : // http://www.microsoft.com/typography/otspec/name.htm
984 : struct NameRecordData {
985 : PRUint32 offset;
986 : PRUint32 length;
987 : };
988 :
989 : #pragma pack()
990 :
991 : static bool
992 0 : IsValidSFNTVersion(PRUint32 version)
993 : {
994 : // normally 0x00010000, CFF-style OT fonts == 'OTTO' and Apple TT fonts = 'true'
995 : // 'typ1' is also possible for old Type 1 fonts in a SFNT container but not supported
996 : return version == 0x10000 ||
997 : version == TRUETYPE_TAG('O','T','T','O') ||
998 0 : version == TRUETYPE_TAG('t','r','u','e');
999 : }
1000 :
1001 : // copy and swap UTF-16 values, assume no surrogate pairs, can be in place
1002 : static void
1003 0 : CopySwapUTF16(const PRUint16 *aInBuf, PRUint16 *aOutBuf, PRUint32 aLen)
1004 : {
1005 0 : const PRUint16 *end = aInBuf + aLen;
1006 0 : while (aInBuf < end) {
1007 0 : PRUint16 value = *aInBuf;
1008 0 : *aOutBuf = (value >> 8) | (value & 0xff) << 8;
1009 0 : aOutBuf++;
1010 0 : aInBuf++;
1011 : }
1012 0 : }
1013 :
1014 : static bool
1015 0 : ValidateKernTable(const PRUint8 *aKernTable, PRUint32 aKernLength)
1016 : {
1017 : // -- kern table can cause crashes if invalid, so do some basic sanity-checking
1018 0 : const KernTableVersion0 *kernTable0 = reinterpret_cast<const KernTableVersion0*>(aKernTable);
1019 0 : if (aKernLength < sizeof(KernTableVersion0)) {
1020 0 : return false;
1021 : }
1022 0 : if (PRUint16(kernTable0->version) == 0) {
1023 0 : if (aKernLength < sizeof(KernTableVersion0) +
1024 0 : PRUint16(kernTable0->nTables) * sizeof(KernTableSubtableHeaderVersion0)) {
1025 0 : return false;
1026 : }
1027 : // at least the table is big enough to contain the subtable headers;
1028 : // we could go further and check the actual subtable sizes....
1029 : // for now, assume this is OK
1030 0 : return true;
1031 : }
1032 :
1033 0 : const KernTableVersion1 *kernTable1 = reinterpret_cast<const KernTableVersion1*>(aKernTable);
1034 0 : if (aKernLength < sizeof(KernTableVersion1)) {
1035 0 : return false;
1036 : }
1037 0 : if (kernTable1->version == 0x00010000) {
1038 0 : if (aKernLength < sizeof(KernTableVersion1) +
1039 0 : kernTable1->nTables * sizeof(KernTableSubtableHeaderVersion1)) {
1040 0 : return false;
1041 : }
1042 : // at least the table is big enough to contain the subtable headers;
1043 : // we could go further and check the actual subtable sizes....
1044 : // for now, assume this is OK
1045 0 : return true;
1046 : }
1047 :
1048 : // neither the old Windows version nor the newer Apple one; refuse to use it
1049 0 : return false;
1050 : }
1051 :
1052 : static bool
1053 0 : ValidateLocaTable(const PRUint8* aLocaTable, PRUint32 aLocaLen,
1054 : PRUint32 aGlyfLen, PRInt16 aLocaFormat, PRUint16 aNumGlyphs)
1055 : {
1056 0 : if (aLocaFormat == 0) {
1057 0 : if (aLocaLen < PRUint32(aNumGlyphs + 1) * sizeof(PRUint16)) {
1058 0 : return false;
1059 : }
1060 : const AutoSwap_PRUint16 *p =
1061 0 : reinterpret_cast<const AutoSwap_PRUint16*>(aLocaTable);
1062 0 : PRUint32 prev = 0;
1063 0 : for (PRUint32 i = 0; i <= aNumGlyphs; ++i) {
1064 0 : PRUint32 current = PRUint16(*p++) * 2;
1065 0 : if (current < prev || current > aGlyfLen) {
1066 0 : return false;
1067 : }
1068 0 : prev = current;
1069 : }
1070 0 : return true;
1071 : }
1072 0 : if (aLocaFormat == 1) {
1073 0 : if (aLocaLen < (aNumGlyphs + 1) * sizeof(PRUint32)) {
1074 0 : return false;
1075 : }
1076 : const AutoSwap_PRUint32 *p =
1077 0 : reinterpret_cast<const AutoSwap_PRUint32*>(aLocaTable);
1078 0 : PRUint32 prev = 0;
1079 0 : for (PRUint32 i = 0; i <= aNumGlyphs; ++i) {
1080 0 : PRUint32 current = *p++;
1081 0 : if (current < prev || current > aGlyfLen) {
1082 0 : return false;
1083 : }
1084 0 : prev = current;
1085 : }
1086 0 : return true;
1087 : }
1088 0 : return false;
1089 : }
1090 :
1091 : gfxUserFontType
1092 0 : gfxFontUtils::DetermineFontDataType(const PRUint8 *aFontData, PRUint32 aFontDataLength)
1093 : {
1094 : // test for OpenType font data
1095 : // problem: EOT-Lite with 0x10000 length will look like TrueType!
1096 0 : if (aFontDataLength >= sizeof(SFNTHeader)) {
1097 0 : const SFNTHeader *sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData);
1098 0 : PRUint32 sfntVersion = sfntHeader->sfntVersion;
1099 0 : if (IsValidSFNTVersion(sfntVersion)) {
1100 0 : return GFX_USERFONT_OPENTYPE;
1101 : }
1102 : }
1103 :
1104 : // test for WOFF
1105 0 : if (aFontDataLength >= sizeof(AutoSwap_PRUint32)) {
1106 : const AutoSwap_PRUint32 *version =
1107 0 : reinterpret_cast<const AutoSwap_PRUint32*>(aFontData);
1108 0 : if (PRUint32(*version) == TRUETYPE_TAG('w','O','F','F')) {
1109 0 : return GFX_USERFONT_WOFF;
1110 : }
1111 : }
1112 :
1113 : // tests for other formats here
1114 :
1115 0 : return GFX_USERFONT_UNKNOWN;
1116 : }
1117 :
1118 : bool
1119 0 : gfxFontUtils::ValidateSFNTHeaders(const PRUint8 *aFontData,
1120 : PRUint32 aFontDataLength)
1121 : {
1122 0 : NS_ASSERTION(aFontData, "null font data");
1123 :
1124 0 : PRUint64 dataLength(aFontDataLength);
1125 :
1126 : // read in the sfnt header
1127 0 : if (sizeof(SFNTHeader) > aFontDataLength) {
1128 0 : NS_WARNING("invalid font (insufficient data)");
1129 0 : return false;
1130 : }
1131 :
1132 0 : const SFNTHeader *sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData);
1133 0 : PRUint32 sfntVersion = sfntHeader->sfntVersion;
1134 0 : if (!IsValidSFNTVersion(sfntVersion)) {
1135 0 : NS_WARNING("invalid font (SFNT version)");
1136 0 : return false;
1137 : }
1138 :
1139 : // iterate through the table headers to find the head, name and OS/2 tables
1140 0 : bool foundHead = false, foundOS2 = false, foundName = false;
1141 0 : bool foundGlyphs = false, foundCFF = false, foundKern = false;
1142 0 : bool foundLoca = false, foundMaxp = false;
1143 0 : PRUint32 headOffset = 0, headLen, nameOffset = 0, nameLen, kernOffset = 0,
1144 0 : kernLen = 0, glyfLen = 0, locaOffset = 0, locaLen = 0,
1145 0 : maxpOffset = 0, maxpLen;
1146 : PRUint32 i, numTables;
1147 :
1148 0 : numTables = sfntHeader->numTables;
1149 0 : PRUint32 headerLen = sizeof(SFNTHeader) + sizeof(TableDirEntry) * numTables;
1150 0 : if (headerLen > aFontDataLength) {
1151 0 : NS_WARNING("invalid font (table directory)");
1152 0 : return false;
1153 : }
1154 :
1155 : // table directory entries begin immediately following SFNT header
1156 : const TableDirEntry *dirEntry =
1157 0 : reinterpret_cast<const TableDirEntry*>(aFontData + sizeof(SFNTHeader));
1158 0 : PRUint32 checksum = 0;
1159 :
1160 : // checksum for font = (checksum of header) + (checksum of tables)
1161 : const AutoSwap_PRUint32 *headerData =
1162 0 : reinterpret_cast<const AutoSwap_PRUint32*>(aFontData);
1163 :
1164 : // header length is in bytes, checksum calculated in longwords
1165 0 : for (i = 0; i < (headerLen >> 2); i++, headerData++) {
1166 0 : checksum += *headerData;
1167 : }
1168 :
1169 0 : for (i = 0; i < numTables; i++, dirEntry++) {
1170 :
1171 : // sanity check on offset, length values
1172 0 : if (PRUint64(dirEntry->offset) + PRUint64(dirEntry->length) > dataLength) {
1173 0 : NS_WARNING("invalid font (table directory entry)");
1174 0 : return false;
1175 : }
1176 :
1177 0 : checksum += dirEntry->checkSum;
1178 :
1179 0 : switch (dirEntry->tag) {
1180 :
1181 : case TRUETYPE_TAG('h','e','a','d'):
1182 0 : foundHead = true;
1183 0 : headOffset = dirEntry->offset;
1184 0 : headLen = dirEntry->length;
1185 0 : if (headLen < sizeof(HeadTable)) {
1186 0 : NS_WARNING("invalid font (head table length)");
1187 0 : return false;
1188 : }
1189 0 : break;
1190 :
1191 : case TRUETYPE_TAG('k','e','r','n'):
1192 0 : foundKern = true;
1193 0 : kernOffset = dirEntry->offset;
1194 0 : kernLen = dirEntry->length;
1195 0 : break;
1196 :
1197 : case TRUETYPE_TAG('n','a','m','e'):
1198 0 : foundName = true;
1199 0 : nameOffset = dirEntry->offset;
1200 0 : nameLen = dirEntry->length;
1201 0 : break;
1202 :
1203 : case TRUETYPE_TAG('O','S','/','2'):
1204 0 : foundOS2 = true;
1205 0 : break;
1206 :
1207 : case TRUETYPE_TAG('g','l','y','f'): // TrueType-style quadratic glyph table
1208 0 : foundGlyphs = true;
1209 0 : glyfLen = dirEntry->length;
1210 0 : break;
1211 :
1212 : case TRUETYPE_TAG('l','o','c','a'): // glyph location table
1213 0 : foundLoca = true;
1214 0 : locaOffset = dirEntry->offset;
1215 0 : locaLen = dirEntry->length;
1216 0 : break;
1217 :
1218 : case TRUETYPE_TAG('m','a','x','p'): // max profile
1219 0 : foundMaxp = true;
1220 0 : maxpOffset = dirEntry->offset;
1221 0 : maxpLen = dirEntry->length;
1222 0 : if (maxpLen < sizeof(MaxpTableHeader)) {
1223 0 : NS_WARNING("invalid font (maxp table length)");
1224 0 : return false;
1225 : }
1226 0 : break;
1227 :
1228 : case TRUETYPE_TAG('C','F','F',' '): // PS-style cubic glyph table
1229 0 : foundCFF = true;
1230 0 : break;
1231 :
1232 : default:
1233 0 : break;
1234 : }
1235 :
1236 : }
1237 :
1238 : // simple sanity checks
1239 :
1240 : // -- fonts need head, name, maxp tables
1241 0 : if (!foundHead || !foundName || !foundMaxp) {
1242 0 : NS_WARNING("invalid font (missing head/name/maxp table)");
1243 0 : return false;
1244 : }
1245 :
1246 : // -- on Windows need OS/2 table
1247 : #ifdef XP_WIN
1248 : if (!foundOS2) {
1249 : NS_WARNING("invalid font (missing OS/2 table)");
1250 : return false;
1251 : }
1252 : #endif
1253 :
1254 : // -- head table data
1255 0 : const HeadTable *headData = reinterpret_cast<const HeadTable*>(aFontData + headOffset);
1256 :
1257 0 : if (headData->tableVersionNumber != HeadTable::HEAD_VERSION) {
1258 0 : NS_WARNING("invalid font (head table version)");
1259 0 : return false;
1260 : }
1261 :
1262 0 : if (headData->magicNumber != HeadTable::HEAD_MAGIC_NUMBER) {
1263 0 : NS_WARNING("invalid font (head magic number)");
1264 0 : return false;
1265 : }
1266 :
1267 0 : if (headData->checkSumAdjustment != (HeadTable::HEAD_CHECKSUM_CALC_CONST - checksum)) {
1268 0 : NS_WARNING("invalid font (bad checksum)");
1269 : // Bug 483459 - warn about a bad checksum but allow the font to be
1270 : // used, since a small percentage of fonts don't calculate this
1271 : // correctly and font systems aren't fussy about this
1272 : // return false;
1273 : }
1274 :
1275 : // need glyf or CFF table based on sfnt version
1276 0 : if (sfntVersion == TRUETYPE_TAG('O','T','T','O')) {
1277 0 : if (!foundCFF) {
1278 0 : NS_WARNING("invalid font (missing CFF table)");
1279 0 : return false;
1280 : }
1281 : } else {
1282 0 : if (!foundGlyphs || !foundLoca) {
1283 0 : NS_WARNING("invalid font (missing glyf or loca table)");
1284 0 : return false;
1285 : }
1286 :
1287 : // sanity-check 'loca' offsets
1288 : const MaxpTableHeader *maxpData =
1289 0 : reinterpret_cast<const MaxpTableHeader*>(aFontData + maxpOffset);
1290 0 : if (!ValidateLocaTable(aFontData + locaOffset, locaLen, glyfLen,
1291 : headData->indexToLocFormat,
1292 0 : maxpData->numGlyphs)) {
1293 0 : NS_WARNING("invalid font (loca table offsets)");
1294 0 : return false;
1295 : }
1296 : }
1297 :
1298 : // -- name table data
1299 0 : const NameHeader *nameHeader = reinterpret_cast<const NameHeader*>(aFontData + nameOffset);
1300 :
1301 0 : PRUint32 nameCount = nameHeader->count;
1302 :
1303 : // -- sanity check the number of name records
1304 0 : if (PRUint64(nameCount) * sizeof(NameRecord) + PRUint64(nameOffset) > dataLength) {
1305 0 : NS_WARNING("invalid font (name records)");
1306 0 : return false;
1307 : }
1308 :
1309 : // -- iterate through name records
1310 : const NameRecord *nameRecord = reinterpret_cast<const NameRecord*>
1311 0 : (aFontData + nameOffset + sizeof(NameHeader));
1312 0 : PRUint64 nameStringsBase = PRUint64(nameOffset) + PRUint64(nameHeader->stringOffset);
1313 :
1314 0 : for (i = 0; i < nameCount; i++, nameRecord++) {
1315 0 : PRUint32 namelen = nameRecord->length;
1316 0 : PRUint32 nameoff = nameRecord->offset; // offset from base of string storage
1317 :
1318 0 : if (nameStringsBase + PRUint64(nameoff) + PRUint64(namelen) > dataLength) {
1319 0 : NS_WARNING("invalid font (name table strings)");
1320 0 : return false;
1321 : }
1322 : }
1323 :
1324 : // -- sanity-check the kern table, if present (see bug 487549)
1325 0 : if (foundKern) {
1326 0 : if (!ValidateKernTable(aFontData + kernOffset, kernLen)) {
1327 0 : NS_WARNING("invalid font (kern table)");
1328 0 : return false;
1329 : }
1330 : }
1331 :
1332 : // everything seems consistent
1333 0 : return true;
1334 : }
1335 :
1336 : nsresult
1337 0 : gfxFontUtils::RenameFont(const nsAString& aName, const PRUint8 *aFontData,
1338 : PRUint32 aFontDataLength, FallibleTArray<PRUint8> *aNewFont)
1339 : {
1340 0 : NS_ASSERTION(aNewFont, "null font data array");
1341 :
1342 0 : PRUint64 dataLength(aFontDataLength);
1343 :
1344 : // new name table
1345 : static const PRUint32 neededNameIDs[] = {NAME_ID_FAMILY,
1346 : NAME_ID_STYLE,
1347 : NAME_ID_UNIQUE,
1348 : NAME_ID_FULL,
1349 : NAME_ID_POSTSCRIPT};
1350 :
1351 : // calculate new name table size
1352 0 : PRUint16 nameCount = ArrayLength(neededNameIDs);
1353 :
1354 : // leave room for null-terminator
1355 0 : PRUint16 nameStrLength = (aName.Length() + 1) * sizeof(PRUnichar);
1356 :
1357 : // round name table size up to 4-byte multiple
1358 : PRUint32 nameTableSize = (sizeof(NameHeader) +
1359 : sizeof(NameRecord) * nameCount +
1360 : nameStrLength +
1361 0 : 3) & ~3;
1362 :
1363 0 : if (dataLength + nameTableSize > PR_UINT32_MAX)
1364 0 : return NS_ERROR_FAILURE;
1365 :
1366 : // bug 505386 - need to handle unpadded font length
1367 0 : PRUint32 paddedFontDataSize = (aFontDataLength + 3) & ~3;
1368 0 : PRUint32 adjFontDataSize = paddedFontDataSize + nameTableSize;
1369 :
1370 : // create new buffer: old font data plus new name table
1371 0 : if (!aNewFont->AppendElements(adjFontDataSize))
1372 0 : return NS_ERROR_OUT_OF_MEMORY;
1373 :
1374 : // copy the old font data
1375 0 : PRUint8 *newFontData = reinterpret_cast<PRUint8*>(aNewFont->Elements());
1376 :
1377 : // null the last four bytes in case the font length is not a multiple of 4
1378 0 : memset(newFontData + aFontDataLength, 0, paddedFontDataSize - aFontDataLength);
1379 :
1380 : // copy font data
1381 0 : memcpy(newFontData, aFontData, aFontDataLength);
1382 :
1383 : // null out the last 4 bytes for checksum calculations
1384 0 : memset(newFontData + adjFontDataSize - 4, 0, 4);
1385 :
1386 : NameHeader *nameHeader = reinterpret_cast<NameHeader*>(newFontData +
1387 0 : paddedFontDataSize);
1388 :
1389 : // -- name header
1390 0 : nameHeader->format = 0;
1391 0 : nameHeader->count = nameCount;
1392 0 : nameHeader->stringOffset = sizeof(NameHeader) + nameCount * sizeof(NameRecord);
1393 :
1394 : // -- name records
1395 : PRUint32 i;
1396 0 : NameRecord *nameRecord = reinterpret_cast<NameRecord*>(nameHeader + 1);
1397 :
1398 0 : for (i = 0; i < nameCount; i++, nameRecord++) {
1399 0 : nameRecord->platformID = PLATFORM_ID_MICROSOFT;
1400 0 : nameRecord->encodingID = ENCODING_ID_MICROSOFT_UNICODEBMP;
1401 0 : nameRecord->languageID = LANG_ID_MICROSOFT_EN_US;
1402 0 : nameRecord->nameID = neededNameIDs[i];
1403 0 : nameRecord->offset = 0;
1404 0 : nameRecord->length = nameStrLength;
1405 : }
1406 :
1407 : // -- string data, located after the name records, stored in big-endian form
1408 0 : PRUnichar *strData = reinterpret_cast<PRUnichar*>(nameRecord);
1409 :
1410 0 : const PRUnichar *nameStr = aName.BeginReading();
1411 0 : const PRUnichar *nameStrEnd = aName.EndReading();
1412 0 : while (nameStr < nameStrEnd) {
1413 0 : PRUnichar ch = *nameStr++;
1414 0 : *strData++ = NS_SWAP16(ch);
1415 : }
1416 0 : *strData = 0; // add null termination
1417 :
1418 : // adjust name table header to point to the new name table
1419 0 : SFNTHeader *sfntHeader = reinterpret_cast<SFNTHeader*>(newFontData);
1420 :
1421 : // table directory entries begin immediately following SFNT header
1422 : TableDirEntry *dirEntry =
1423 0 : reinterpret_cast<TableDirEntry*>(newFontData + sizeof(SFNTHeader));
1424 :
1425 0 : PRUint32 numTables = sfntHeader->numTables;
1426 0 : bool foundName = false;
1427 :
1428 0 : for (i = 0; i < numTables; i++, dirEntry++) {
1429 0 : if (dirEntry->tag == TRUETYPE_TAG('n','a','m','e')) {
1430 0 : foundName = true;
1431 0 : break;
1432 : }
1433 : }
1434 :
1435 : // function only called if font validates, so this should always be true
1436 0 : NS_ASSERTION(foundName, "attempt to rename font with no name table");
1437 :
1438 : // note: dirEntry now points to name record
1439 :
1440 : // recalculate name table checksum
1441 0 : PRUint32 checkSum = 0;
1442 0 : AutoSwap_PRUint32 *nameData = reinterpret_cast<AutoSwap_PRUint32*> (nameHeader);
1443 0 : AutoSwap_PRUint32 *nameDataEnd = nameData + (nameTableSize >> 2);
1444 :
1445 0 : while (nameData < nameDataEnd)
1446 0 : checkSum = checkSum + *nameData++;
1447 :
1448 : // adjust name table entry to point to new name table
1449 0 : dirEntry->offset = paddedFontDataSize;
1450 0 : dirEntry->length = nameTableSize;
1451 0 : dirEntry->checkSum = checkSum;
1452 :
1453 : // fix up checksums
1454 0 : PRUint32 checksum = 0;
1455 :
1456 : // checksum for font = (checksum of header) + (checksum of tables)
1457 0 : PRUint32 headerLen = sizeof(SFNTHeader) + sizeof(TableDirEntry) * numTables;
1458 : const AutoSwap_PRUint32 *headerData =
1459 0 : reinterpret_cast<const AutoSwap_PRUint32*>(newFontData);
1460 :
1461 : // header length is in bytes, checksum calculated in longwords
1462 0 : for (i = 0; i < (headerLen >> 2); i++, headerData++) {
1463 0 : checksum += *headerData;
1464 : }
1465 :
1466 0 : PRUint32 headOffset = 0;
1467 0 : dirEntry = reinterpret_cast<TableDirEntry*>(newFontData + sizeof(SFNTHeader));
1468 :
1469 0 : for (i = 0; i < numTables; i++, dirEntry++) {
1470 0 : if (dirEntry->tag == TRUETYPE_TAG('h','e','a','d')) {
1471 0 : headOffset = dirEntry->offset;
1472 : }
1473 0 : checksum += dirEntry->checkSum;
1474 : }
1475 :
1476 0 : NS_ASSERTION(headOffset != 0, "no head table for font");
1477 :
1478 0 : HeadTable *headData = reinterpret_cast<HeadTable*>(newFontData + headOffset);
1479 :
1480 0 : headData->checkSumAdjustment = HeadTable::HEAD_CHECKSUM_CALC_CONST - checksum;
1481 :
1482 0 : return NS_OK;
1483 : }
1484 :
1485 : // This is only called after the basic validity of the downloaded sfnt
1486 : // data has been checked, so it should never fail to find the name table
1487 : // (though it might fail to read it, if memory isn't available);
1488 : // other checks here are just for extra paranoia.
1489 : nsresult
1490 0 : gfxFontUtils::GetFullNameFromSFNT(const PRUint8* aFontData, PRUint32 aLength,
1491 : nsAString& aFullName)
1492 : {
1493 0 : aFullName.AssignLiteral("(MISSING NAME)"); // should always get replaced
1494 :
1495 0 : NS_ENSURE_TRUE(aLength >= sizeof(SFNTHeader), NS_ERROR_UNEXPECTED);
1496 : const SFNTHeader *sfntHeader =
1497 0 : reinterpret_cast<const SFNTHeader*>(aFontData);
1498 : const TableDirEntry *dirEntry =
1499 0 : reinterpret_cast<const TableDirEntry*>(aFontData + sizeof(SFNTHeader));
1500 0 : PRUint32 numTables = sfntHeader->numTables;
1501 0 : NS_ENSURE_TRUE(aLength >=
1502 : sizeof(SFNTHeader) + numTables * sizeof(TableDirEntry),
1503 : NS_ERROR_UNEXPECTED);
1504 0 : bool foundName = false;
1505 0 : for (PRUint32 i = 0; i < numTables; i++, dirEntry++) {
1506 0 : if (dirEntry->tag == TRUETYPE_TAG('n','a','m','e')) {
1507 0 : foundName = true;
1508 0 : break;
1509 : }
1510 : }
1511 :
1512 : // should never fail, as we're only called after font validation succeeded
1513 0 : NS_ENSURE_TRUE(foundName, NS_ERROR_NOT_AVAILABLE);
1514 :
1515 0 : PRUint32 len = dirEntry->length;
1516 0 : NS_ENSURE_TRUE(aLength > len && aLength - len >= dirEntry->offset,
1517 : NS_ERROR_UNEXPECTED);
1518 0 : FallibleTArray<PRUint8> nameTable;
1519 0 : if (!nameTable.SetLength(len)) {
1520 0 : return NS_ERROR_OUT_OF_MEMORY;
1521 : }
1522 0 : memcpy(nameTable.Elements(), aFontData + dirEntry->offset, len);
1523 :
1524 0 : return GetFullNameFromTable(nameTable, aFullName);
1525 : }
1526 :
1527 : nsresult
1528 0 : gfxFontUtils::GetFullNameFromTable(FallibleTArray<PRUint8>& aNameTable,
1529 : nsAString& aFullName)
1530 : {
1531 0 : nsAutoString name;
1532 : nsresult rv =
1533 : gfxFontUtils::ReadCanonicalName(aNameTable,
1534 : gfxFontUtils::NAME_ID_FULL,
1535 0 : name);
1536 0 : if (NS_SUCCEEDED(rv) && !name.IsEmpty()) {
1537 0 : aFullName = name;
1538 0 : return NS_OK;
1539 : }
1540 : rv = gfxFontUtils::ReadCanonicalName(aNameTable,
1541 : gfxFontUtils::NAME_ID_FAMILY,
1542 0 : name);
1543 0 : if (NS_SUCCEEDED(rv) && !name.IsEmpty()) {
1544 0 : nsAutoString styleName;
1545 : rv = gfxFontUtils::ReadCanonicalName(aNameTable,
1546 : gfxFontUtils::NAME_ID_STYLE,
1547 0 : styleName);
1548 0 : if (NS_SUCCEEDED(rv) && !styleName.IsEmpty()) {
1549 0 : name.AppendLiteral(" ");
1550 0 : name.Append(styleName);
1551 0 : aFullName = name;
1552 : }
1553 0 : return NS_OK;
1554 : }
1555 :
1556 0 : return NS_ERROR_NOT_AVAILABLE;
1557 : }
1558 :
1559 : enum {
1560 : #if defined(XP_MACOSX)
1561 : CANONICAL_LANG_ID = gfxFontUtils::LANG_ID_MAC_ENGLISH,
1562 : PLATFORM_ID = gfxFontUtils::PLATFORM_ID_MAC
1563 : #else
1564 : CANONICAL_LANG_ID = gfxFontUtils::LANG_ID_MICROSOFT_EN_US,
1565 : PLATFORM_ID = gfxFontUtils::PLATFORM_ID_MICROSOFT
1566 : #endif
1567 : };
1568 :
1569 : nsresult
1570 0 : gfxFontUtils::ReadNames(FallibleTArray<PRUint8>& aNameTable, PRUint32 aNameID,
1571 : PRInt32 aPlatformID, nsTArray<nsString>& aNames)
1572 : {
1573 0 : return ReadNames(aNameTable, aNameID, LANG_ALL, aPlatformID, aNames);
1574 : }
1575 :
1576 : nsresult
1577 0 : gfxFontUtils::ReadCanonicalName(FallibleTArray<PRUint8>& aNameTable, PRUint32 aNameID,
1578 : nsString& aName)
1579 : {
1580 : nsresult rv;
1581 :
1582 0 : nsTArray<nsString> names;
1583 :
1584 : // first, look for the English name (this will succeed 99% of the time)
1585 0 : rv = ReadNames(aNameTable, aNameID, CANONICAL_LANG_ID, PLATFORM_ID, names);
1586 0 : NS_ENSURE_SUCCESS(rv, rv);
1587 :
1588 : // otherwise, grab names for all languages
1589 0 : if (names.Length() == 0) {
1590 0 : rv = ReadNames(aNameTable, aNameID, LANG_ALL, PLATFORM_ID, names);
1591 0 : NS_ENSURE_SUCCESS(rv, rv);
1592 : }
1593 :
1594 : #if defined(XP_MACOSX)
1595 : // may be dealing with font that only has Microsoft name entries
1596 : if (names.Length() == 0) {
1597 : rv = ReadNames(aNameTable, aNameID, LANG_ID_MICROSOFT_EN_US,
1598 : PLATFORM_ID_MICROSOFT, names);
1599 : NS_ENSURE_SUCCESS(rv, rv);
1600 :
1601 : // getting really desperate now, take anything!
1602 : if (names.Length() == 0) {
1603 : rv = ReadNames(aNameTable, aNameID, LANG_ALL,
1604 : PLATFORM_ID_MICROSOFT, names);
1605 : NS_ENSURE_SUCCESS(rv, rv);
1606 : }
1607 : }
1608 : #endif
1609 :
1610 : // return the first name (99.9% of the time names will
1611 : // contain a single English name)
1612 0 : if (names.Length()) {
1613 0 : aName.Assign(names[0]);
1614 0 : return NS_OK;
1615 : }
1616 :
1617 0 : return NS_ERROR_FAILURE;
1618 : }
1619 :
1620 : // Charsets to use for decoding Mac platform font names.
1621 : // This table is sorted by {encoding, language}, with the wildcard "ANY" being
1622 : // greater than any defined values for each field; we use a binary search on both
1623 : // fields, and fall back to matching only encoding if necessary
1624 :
1625 : // Some "redundant" entries for specific combinations are included such as
1626 : // encoding=roman, lang=english, in order that common entries will be found
1627 : // on the first search.
1628 :
1629 : #define ANY 0xffff
1630 : const gfxFontUtils::MacFontNameCharsetMapping gfxFontUtils::gMacFontNameCharsets[] =
1631 : {
1632 : { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_ENGLISH, "x-mac-roman" },
1633 : { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_ICELANDIC, "x-mac-icelandic" },
1634 : { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_TURKISH, "x-mac-turkish" },
1635 : { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_POLISH, "x-mac-ce" },
1636 : { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_ROMANIAN, "x-mac-romanian" },
1637 : { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_CZECH, "x-mac-ce" },
1638 : { ENCODING_ID_MAC_ROMAN, LANG_ID_MAC_SLOVAK, "x-mac-ce" },
1639 : { ENCODING_ID_MAC_ROMAN, ANY, "x-mac-roman" },
1640 : { ENCODING_ID_MAC_JAPANESE, LANG_ID_MAC_JAPANESE, "Shift_JIS" },
1641 : { ENCODING_ID_MAC_JAPANESE, ANY, "Shift_JIS" },
1642 : { ENCODING_ID_MAC_TRAD_CHINESE, LANG_ID_MAC_TRAD_CHINESE, "Big5" },
1643 : { ENCODING_ID_MAC_TRAD_CHINESE, ANY, "Big5" },
1644 : { ENCODING_ID_MAC_KOREAN, LANG_ID_MAC_KOREAN, "EUC-KR" },
1645 : { ENCODING_ID_MAC_KOREAN, ANY, "EUC-KR" },
1646 : { ENCODING_ID_MAC_ARABIC, LANG_ID_MAC_ARABIC, "x-mac-arabic" },
1647 : { ENCODING_ID_MAC_ARABIC, LANG_ID_MAC_URDU, "x-mac-farsi" },
1648 : { ENCODING_ID_MAC_ARABIC, LANG_ID_MAC_FARSI, "x-mac-farsi" },
1649 : { ENCODING_ID_MAC_ARABIC, ANY, "x-mac-arabic" },
1650 : { ENCODING_ID_MAC_HEBREW, LANG_ID_MAC_HEBREW, "x-mac-hebrew" },
1651 : { ENCODING_ID_MAC_HEBREW, ANY, "x-mac-hebrew" },
1652 : { ENCODING_ID_MAC_GREEK, ANY, "x-mac-greek" },
1653 : { ENCODING_ID_MAC_CYRILLIC, ANY, "x-mac-cyrillic" },
1654 : { ENCODING_ID_MAC_DEVANAGARI, ANY, "x-mac-devanagari"},
1655 : { ENCODING_ID_MAC_GURMUKHI, ANY, "x-mac-gurmukhi" },
1656 : { ENCODING_ID_MAC_GUJARATI, ANY, "x-mac-gujarati" },
1657 : { ENCODING_ID_MAC_SIMP_CHINESE, LANG_ID_MAC_SIMP_CHINESE, "GB2312" },
1658 : { ENCODING_ID_MAC_SIMP_CHINESE, ANY, "GB2312" }
1659 : };
1660 :
1661 : const char* gfxFontUtils::gISOFontNameCharsets[] =
1662 : {
1663 : /* 0 */ "us-ascii" ,
1664 : /* 1 */ nsnull , /* spec says "ISO 10646" but does not specify encoding form! */
1665 : /* 2 */ "ISO-8859-1"
1666 : };
1667 :
1668 : const char* gfxFontUtils::gMSFontNameCharsets[] =
1669 : {
1670 : /* [0] ENCODING_ID_MICROSOFT_SYMBOL */ "" ,
1671 : /* [1] ENCODING_ID_MICROSOFT_UNICODEBMP */ "" ,
1672 : /* [2] ENCODING_ID_MICROSOFT_SHIFTJIS */ "Shift_JIS" ,
1673 : /* [3] ENCODING_ID_MICROSOFT_PRC */ nsnull ,
1674 : /* [4] ENCODING_ID_MICROSOFT_BIG5 */ "Big5" ,
1675 : /* [5] ENCODING_ID_MICROSOFT_WANSUNG */ nsnull ,
1676 : /* [6] ENCODING_ID_MICROSOFT_JOHAB */ "x-johab" ,
1677 : /* [7] reserved */ nsnull ,
1678 : /* [8] reserved */ nsnull ,
1679 : /* [9] reserved */ nsnull ,
1680 : /*[10] ENCODING_ID_MICROSOFT_UNICODEFULL */ ""
1681 : };
1682 :
1683 : #define ARRAY_SIZE(A) (sizeof(A) / sizeof(A[0]))
1684 :
1685 : // Return the name of the charset we should use to decode a font name
1686 : // given the name table attributes.
1687 : // Special return values:
1688 : // "" charset is UTF16BE, no need for a converter
1689 : // nsnull unknown charset, do not attempt conversion
1690 : const char*
1691 0 : gfxFontUtils::GetCharsetForFontName(PRUint16 aPlatform, PRUint16 aScript, PRUint16 aLanguage)
1692 : {
1693 0 : switch (aPlatform)
1694 : {
1695 : case PLATFORM_ID_UNICODE:
1696 0 : return "";
1697 :
1698 : case PLATFORM_ID_MAC:
1699 : {
1700 0 : PRUint32 lo = 0, hi = ARRAY_SIZE(gMacFontNameCharsets);
1701 0 : MacFontNameCharsetMapping searchValue = { aScript, aLanguage, nsnull };
1702 0 : for (PRUint32 i = 0; i < 2; ++i) {
1703 : // binary search; if not found, set language to ANY and try again
1704 0 : while (lo < hi) {
1705 0 : PRUint32 mid = (lo + hi) / 2;
1706 0 : const MacFontNameCharsetMapping& entry = gMacFontNameCharsets[mid];
1707 0 : if (entry < searchValue) {
1708 0 : lo = mid + 1;
1709 0 : continue;
1710 : }
1711 0 : if (searchValue < entry) {
1712 0 : hi = mid;
1713 0 : continue;
1714 : }
1715 : // found a match
1716 0 : return entry.mCharsetName;
1717 : }
1718 :
1719 : // no match, so reset high bound for search and re-try
1720 0 : hi = ARRAY_SIZE(gMacFontNameCharsets);
1721 0 : searchValue.mLanguage = ANY;
1722 : }
1723 : }
1724 0 : break;
1725 :
1726 : case PLATFORM_ID_ISO:
1727 0 : if (aScript < ARRAY_SIZE(gISOFontNameCharsets)) {
1728 0 : return gISOFontNameCharsets[aScript];
1729 : }
1730 0 : break;
1731 :
1732 : case PLATFORM_ID_MICROSOFT:
1733 0 : if (aScript < ARRAY_SIZE(gMSFontNameCharsets)) {
1734 0 : return gMSFontNameCharsets[aScript];
1735 : }
1736 0 : break;
1737 : }
1738 :
1739 0 : return nsnull;
1740 : }
1741 :
1742 : // convert a raw name from the name table to an nsString, if possible;
1743 : // return value indicates whether conversion succeeded
1744 : bool
1745 0 : gfxFontUtils::DecodeFontName(const PRUint8 *aNameData, PRInt32 aByteLen,
1746 : PRUint32 aPlatformCode, PRUint32 aScriptCode,
1747 : PRUint32 aLangCode, nsAString& aName)
1748 : {
1749 0 : NS_ASSERTION(aByteLen > 0, "bad length for font name data");
1750 :
1751 0 : const char *csName = GetCharsetForFontName(aPlatformCode, aScriptCode, aLangCode);
1752 :
1753 0 : if (!csName) {
1754 : // nsnull -> unknown charset
1755 : #ifdef DEBUG
1756 : char warnBuf[128];
1757 0 : if (aByteLen > 64)
1758 0 : aByteLen = 64;
1759 : sprintf(warnBuf, "skipping font name, unknown charset %d:%d:%d for <%.*s>",
1760 0 : aPlatformCode, aScriptCode, aLangCode, aByteLen, aNameData);
1761 0 : NS_WARNING(warnBuf);
1762 : #endif
1763 0 : return false;
1764 : }
1765 :
1766 0 : if (csName[0] == 0) {
1767 : // empty charset name: data is utf16be, no need to instantiate a converter
1768 0 : PRUint32 strLen = aByteLen / 2;
1769 : #ifdef IS_LITTLE_ENDIAN
1770 0 : aName.SetLength(strLen);
1771 : CopySwapUTF16(reinterpret_cast<const PRUint16*>(aNameData),
1772 0 : reinterpret_cast<PRUint16*>(aName.BeginWriting()), strLen);
1773 : #else
1774 : aName.Assign(reinterpret_cast<const PRUnichar*>(aNameData), strLen);
1775 : #endif
1776 0 : return true;
1777 : }
1778 :
1779 : nsresult rv;
1780 : nsCOMPtr<nsICharsetConverterManager> ccm =
1781 0 : do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
1782 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get charset converter manager");
1783 0 : if (NS_FAILED(rv)) {
1784 0 : return false;
1785 : }
1786 :
1787 0 : nsCOMPtr<nsIUnicodeDecoder> decoder;
1788 0 : rv = ccm->GetUnicodeDecoderRawInternal(csName, getter_AddRefs(decoder));
1789 0 : if (NS_FAILED(rv)) {
1790 0 : NS_WARNING("failed to get the decoder for a font name string");
1791 0 : return false;
1792 : }
1793 :
1794 : PRInt32 destLength;
1795 0 : rv = decoder->GetMaxLength(reinterpret_cast<const char*>(aNameData), aByteLen, &destLength);
1796 0 : if (NS_FAILED(rv)) {
1797 0 : NS_WARNING("decoder->GetMaxLength failed, invalid font name?");
1798 0 : return false;
1799 : }
1800 :
1801 : // make space for the converted string
1802 0 : aName.SetLength(destLength);
1803 0 : rv = decoder->Convert(reinterpret_cast<const char*>(aNameData), &aByteLen,
1804 0 : aName.BeginWriting(), &destLength);
1805 0 : if (NS_FAILED(rv)) {
1806 0 : NS_WARNING("decoder->Convert failed, invalid font name?");
1807 0 : return false;
1808 : }
1809 0 : aName.Truncate(destLength); // set the actual length
1810 :
1811 0 : return true;
1812 : }
1813 :
1814 : nsresult
1815 0 : gfxFontUtils::ReadNames(FallibleTArray<PRUint8>& aNameTable, PRUint32 aNameID,
1816 : PRInt32 aLangID, PRInt32 aPlatformID,
1817 : nsTArray<nsString>& aNames)
1818 : {
1819 0 : PRUint32 nameTableLen = aNameTable.Length();
1820 0 : NS_ASSERTION(nameTableLen != 0, "null name table");
1821 :
1822 0 : if (nameTableLen == 0)
1823 0 : return NS_ERROR_FAILURE;
1824 :
1825 0 : PRUint8 *nameTable = aNameTable.Elements();
1826 :
1827 : // -- name table data
1828 0 : const NameHeader *nameHeader = reinterpret_cast<const NameHeader*>(nameTable);
1829 :
1830 0 : PRUint32 nameCount = nameHeader->count;
1831 :
1832 : // -- sanity check the number of name records
1833 0 : if (PRUint64(nameCount) * sizeof(NameRecord) > nameTableLen) {
1834 0 : NS_WARNING("invalid font (name table data)");
1835 0 : return NS_ERROR_FAILURE;
1836 : }
1837 :
1838 : // -- iterate through name records
1839 : const NameRecord *nameRecord
1840 0 : = reinterpret_cast<const NameRecord*>(nameTable + sizeof(NameHeader));
1841 0 : PRUint64 nameStringsBase = PRUint64(nameHeader->stringOffset);
1842 :
1843 : PRUint32 i;
1844 0 : for (i = 0; i < nameCount; i++, nameRecord++) {
1845 : PRUint32 platformID;
1846 :
1847 : // skip over unwanted nameID's
1848 0 : if (PRUint32(nameRecord->nameID) != aNameID)
1849 0 : continue;
1850 :
1851 : // skip over unwanted platform data
1852 0 : platformID = nameRecord->platformID;
1853 0 : if (aPlatformID != PLATFORM_ALL
1854 0 : && PRUint32(nameRecord->platformID) != PLATFORM_ID)
1855 0 : continue;
1856 :
1857 : // skip over unwanted languages
1858 0 : if (aLangID != LANG_ALL
1859 0 : && PRUint32(nameRecord->languageID) != PRUint32(aLangID))
1860 0 : continue;
1861 :
1862 : // add name to names array
1863 :
1864 : // -- calculate string location
1865 0 : PRUint32 namelen = nameRecord->length;
1866 0 : PRUint32 nameoff = nameRecord->offset; // offset from base of string storage
1867 :
1868 0 : if (nameStringsBase + PRUint64(nameoff) + PRUint64(namelen)
1869 : > nameTableLen) {
1870 0 : NS_WARNING("invalid font (name table strings)");
1871 0 : return NS_ERROR_FAILURE;
1872 : }
1873 :
1874 : // -- decode if necessary and make nsString
1875 0 : nsAutoString name;
1876 : nsresult rv;
1877 :
1878 0 : rv = DecodeFontName(nameTable + nameStringsBase + nameoff, namelen,
1879 : platformID, PRUint32(nameRecord->encodingID),
1880 0 : PRUint32(nameRecord->languageID), name);
1881 :
1882 0 : if (NS_FAILED(rv))
1883 0 : continue;
1884 :
1885 : PRUint32 k, numNames;
1886 0 : bool foundName = false;
1887 :
1888 0 : numNames = aNames.Length();
1889 0 : for (k = 0; k < numNames; k++) {
1890 0 : if (name.Equals(aNames[k])) {
1891 0 : foundName = true;
1892 0 : break;
1893 : }
1894 : }
1895 :
1896 0 : if (!foundName)
1897 0 : aNames.AppendElement(name);
1898 :
1899 : }
1900 :
1901 0 : return NS_OK;
1902 : }
1903 :
1904 : #ifdef XP_WIN
1905 :
1906 : // Embedded OpenType (EOT) handling
1907 : // needed for dealing with downloadable fonts on Windows
1908 : //
1909 : // EOT version 0x00020001
1910 : // based on http://www.w3.org/Submission/2008/SUBM-EOT-20080305/
1911 : //
1912 : // EOT header consists of a fixed-size portion containing general font
1913 : // info, followed by a variable-sized portion containing name data,
1914 : // followed by the actual TT/OT font data (non-byte values are always
1915 : // stored in big-endian format)
1916 : //
1917 : // EOT header is stored in *little* endian order!!
1918 :
1919 : #pragma pack(1)
1920 :
1921 : struct EOTFixedHeader {
1922 :
1923 : PRUint32 eotSize; // Total structure length in PRUint8s (including string and font data)
1924 : PRUint32 fontDataSize; // Length of the OpenType font (FontData) in PRUint8s
1925 : PRUint32 version; // Version number of this format - 0x00010000
1926 : PRUint32 flags; // Processing Flags
1927 : PRUint8 panose[10]; // The PANOSE value for this font - See http://www.microsoft.com/typography/otspec/os2.htm#pan
1928 : PRUint8 charset; // In Windows this is derived from TEXTMETRIC.tmCharSet. This value specifies the character set of the font. DEFAULT_CHARSET (0x01) indicates no preference. - See http://msdn2.microsoft.com/en-us/library/ms534202.aspx
1929 : PRUint8 italic; // If the bit for ITALIC is set in OS/2.fsSelection, the value will be 0x01 - See http://www.microsoft.com/typography/otspec/os2.htm#fss
1930 : PRUint32 weight; // The weight value for this font - See http://www.microsoft.com/typography/otspec/os2.htm#wtc
1931 : PRUint16 fsType; // Type flags that provide information about embedding permissions - See http://www.microsoft.com/typography/otspec/os2.htm#fst
1932 : PRUint16 magicNumber; // Magic number for EOT file - 0x504C. Used to check for data corruption.
1933 : PRUint32 unicodeRange1; // OS/2.UnicodeRange1 (bits 0-31) - See http://www.microsoft.com/typography/otspec/os2.htm#ur
1934 : PRUint32 unicodeRange2; // OS/2.UnicodeRange2 (bits 32-63) - See http://www.microsoft.com/typography/otspec/os2.htm#ur
1935 : PRUint32 unicodeRange3; // OS/2.UnicodeRange3 (bits 64-95) - See http://www.microsoft.com/typography/otspec/os2.htm#ur
1936 : PRUint32 unicodeRange4; // OS/2.UnicodeRange4 (bits 96-127) - See http://www.microsoft.com/typography/otspec/os2.htm#ur
1937 : PRUint32 codePageRange1; // CodePageRange1 (bits 0-31) - See http://www.microsoft.com/typography/otspec/os2.htm#cpr
1938 : PRUint32 codePageRange2; // CodePageRange2 (bits 32-63) - See http://www.microsoft.com/typography/otspec/os2.htm#cpr
1939 : PRUint32 checkSumAdjustment; // head.CheckSumAdjustment - See http://www.microsoft.com/typography/otspec/head.htm
1940 : PRUint32 reserved[4]; // Reserved - must be 0
1941 : PRUint16 padding1; // Padding to maintain long alignment. Padding value must always be set to 0x0000.
1942 :
1943 : enum {
1944 : EOT_VERSION = 0x00020001,
1945 : EOT_MAGIC_NUMBER = 0x504c,
1946 : EOT_DEFAULT_CHARSET = 0x01,
1947 : EOT_EMBED_PRINT_PREVIEW = 0x0004,
1948 : EOT_FAMILY_NAME_INDEX = 0, // order of names in variable portion of EOT header
1949 : EOT_STYLE_NAME_INDEX = 1,
1950 : EOT_VERSION_NAME_INDEX = 2,
1951 : EOT_FULL_NAME_INDEX = 3,
1952 : EOT_NUM_NAMES = 4
1953 : };
1954 :
1955 : };
1956 :
1957 : #pragma pack()
1958 :
1959 : // EOT headers are only used on Windows
1960 :
1961 : // EOT variable-sized header (version 0x00020001 - contains 4 name
1962 : // fields, each with the structure):
1963 : //
1964 : // // number of bytes in the name array
1965 : // PRUint16 size;
1966 : // // array of UTF-16 chars, total length = <size> bytes
1967 : // // note: english version of name record string
1968 : // PRUint8 name[size];
1969 : //
1970 : // This structure is used for the following names, each separated by two
1971 : // bytes of padding (always 0 with no padding after the rootString):
1972 : //
1973 : // familyName - based on name ID = 1
1974 : // styleName - based on name ID = 2
1975 : // versionName - based on name ID = 5
1976 : // fullName - based on name ID = 4
1977 : // rootString - used to restrict font usage to a specific domain
1978 : //
1979 :
1980 : #if DEBUG
1981 : static void
1982 : DumpEOTHeader(PRUint8 *aHeader, PRUint32 aHeaderLen)
1983 : {
1984 : PRUint32 offset = 0;
1985 : PRUint8 *ch = aHeader;
1986 :
1987 : printf("\n\nlen == %d\n\n", aHeaderLen);
1988 : while (offset < aHeaderLen) {
1989 : printf("%7.7x ", offset);
1990 : int i;
1991 : for (i = 0; i < 16; i++) {
1992 : printf("%2.2x ", *ch++);
1993 : }
1994 : printf("\n");
1995 : offset += 16;
1996 : }
1997 : }
1998 : #endif
1999 :
2000 : nsresult
2001 : gfxFontUtils::MakeEOTHeader(const PRUint8 *aFontData, PRUint32 aFontDataLength,
2002 : FallibleTArray<PRUint8> *aHeader,
2003 : FontDataOverlay *aOverlay)
2004 : {
2005 : NS_ASSERTION(aFontData && aFontDataLength != 0, "null font data");
2006 : NS_ASSERTION(aHeader, "null header");
2007 : NS_ASSERTION(aHeader->Length() == 0, "non-empty header passed in");
2008 : NS_ASSERTION(aOverlay, "null font overlay struct passed in");
2009 :
2010 : aOverlay->overlaySrc = 0;
2011 :
2012 : if (!aHeader->AppendElements(sizeof(EOTFixedHeader)))
2013 : return NS_ERROR_OUT_OF_MEMORY;
2014 :
2015 : EOTFixedHeader *eotHeader = reinterpret_cast<EOTFixedHeader*>(aHeader->Elements());
2016 : memset(eotHeader, 0, sizeof(EOTFixedHeader));
2017 :
2018 : PRUint32 fontDataSize = aFontDataLength;
2019 :
2020 : // set up header fields
2021 : eotHeader->fontDataSize = fontDataSize;
2022 : eotHeader->version = EOTFixedHeader::EOT_VERSION;
2023 : eotHeader->flags = 0; // don't specify any special processing
2024 : eotHeader->charset = EOTFixedHeader::EOT_DEFAULT_CHARSET;
2025 : eotHeader->fsType = EOTFixedHeader::EOT_EMBED_PRINT_PREVIEW;
2026 : eotHeader->magicNumber = EOTFixedHeader::EOT_MAGIC_NUMBER;
2027 :
2028 : // read in the sfnt header
2029 : if (sizeof(SFNTHeader) > aFontDataLength)
2030 : return NS_ERROR_FAILURE;
2031 :
2032 : const SFNTHeader *sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData);
2033 : if (!IsValidSFNTVersion(sfntHeader->sfntVersion))
2034 : return NS_ERROR_FAILURE;
2035 :
2036 : // iterate through the table headers to find the head, name and OS/2 tables
2037 : bool foundHead = false, foundOS2 = false, foundName = false, foundGlyphs = false;
2038 : PRUint32 headOffset, headLen, nameOffset, nameLen, os2Offset, os2Len;
2039 : PRUint32 i, numTables;
2040 :
2041 : numTables = sfntHeader->numTables;
2042 : if (sizeof(SFNTHeader) + sizeof(TableDirEntry) * numTables > aFontDataLength)
2043 : return NS_ERROR_FAILURE;
2044 :
2045 : PRUint64 dataLength(aFontDataLength);
2046 :
2047 : // table directory entries begin immediately following SFNT header
2048 : const TableDirEntry *dirEntry = reinterpret_cast<const TableDirEntry*>(aFontData + sizeof(SFNTHeader));
2049 :
2050 : for (i = 0; i < numTables; i++, dirEntry++) {
2051 :
2052 : // sanity check on offset, length values
2053 : if (PRUint64(dirEntry->offset) + PRUint64(dirEntry->length) > dataLength)
2054 : return NS_ERROR_FAILURE;
2055 :
2056 : switch (dirEntry->tag) {
2057 :
2058 : case TRUETYPE_TAG('h','e','a','d'):
2059 : foundHead = true;
2060 : headOffset = dirEntry->offset;
2061 : headLen = dirEntry->length;
2062 : if (headLen < sizeof(HeadTable))
2063 : return NS_ERROR_FAILURE;
2064 : break;
2065 :
2066 : case TRUETYPE_TAG('n','a','m','e'):
2067 : foundName = true;
2068 : nameOffset = dirEntry->offset;
2069 : nameLen = dirEntry->length;
2070 : break;
2071 :
2072 : case TRUETYPE_TAG('O','S','/','2'):
2073 : foundOS2 = true;
2074 : os2Offset = dirEntry->offset;
2075 : os2Len = dirEntry->length;
2076 : break;
2077 :
2078 : case TRUETYPE_TAG('g','l','y','f'): // TrueType-style quadratic glyph table
2079 : foundGlyphs = true;
2080 : break;
2081 :
2082 : case TRUETYPE_TAG('C','F','F',' '): // PS-style cubic glyph table
2083 : foundGlyphs = true;
2084 : break;
2085 :
2086 : default:
2087 : break;
2088 : }
2089 :
2090 : if (foundHead && foundName && foundOS2 && foundGlyphs)
2091 : break;
2092 : }
2093 :
2094 : // require these three tables on Windows
2095 : if (!foundHead || !foundName || !foundOS2)
2096 : return NS_ERROR_FAILURE;
2097 :
2098 : // at this point, all table offset/length values are within bounds
2099 :
2100 : // read in the data from those tables
2101 :
2102 : // -- head table data
2103 : const HeadTable *headData = reinterpret_cast<const HeadTable*>(aFontData + headOffset);
2104 :
2105 : if (headData->tableVersionNumber != HeadTable::HEAD_VERSION ||
2106 : headData->magicNumber != HeadTable::HEAD_MAGIC_NUMBER) {
2107 : return NS_ERROR_FAILURE;
2108 : }
2109 :
2110 : eotHeader->checkSumAdjustment = headData->checkSumAdjustment;
2111 :
2112 : // -- name table data
2113 :
2114 : // -- first, read name table header
2115 : const NameHeader *nameHeader = reinterpret_cast<const NameHeader*>(aFontData + nameOffset);
2116 : PRUint32 nameStringsBase = PRUint32(nameHeader->stringOffset);
2117 :
2118 : PRUint32 nameCount = nameHeader->count;
2119 :
2120 : // -- sanity check the number of name records
2121 : if (PRUint64(nameCount) * sizeof(NameRecord) + PRUint64(nameOffset) > dataLength)
2122 : return NS_ERROR_FAILURE;
2123 :
2124 : // bug 496573 -- dummy names in case the font didn't contain English names
2125 : const nsString dummyNames[EOTFixedHeader::EOT_NUM_NAMES] = {
2126 : NS_LITERAL_STRING("Unknown"),
2127 : NS_LITERAL_STRING("Regular"),
2128 : EmptyString(),
2129 : dummyNames[EOTFixedHeader::EOT_FAMILY_NAME_INDEX]
2130 : };
2131 :
2132 : // -- iterate through name records, look for specific name ids with
2133 : // matching platform/encoding/etc. and store offset/lengths
2134 : NameRecordData names[EOTFixedHeader::EOT_NUM_NAMES] = {0};
2135 : const NameRecord *nameRecord = reinterpret_cast<const NameRecord*>(aFontData + nameOffset + sizeof(NameHeader));
2136 : PRUint32 needNames = (1 << EOTFixedHeader::EOT_FAMILY_NAME_INDEX) |
2137 : (1 << EOTFixedHeader::EOT_STYLE_NAME_INDEX) |
2138 : (1 << EOTFixedHeader::EOT_FULL_NAME_INDEX) |
2139 : (1 << EOTFixedHeader::EOT_VERSION_NAME_INDEX);
2140 :
2141 : for (i = 0; i < nameCount; i++, nameRecord++) {
2142 :
2143 : // looking for Microsoft English US name strings, skip others
2144 : if (PRUint32(nameRecord->platformID) != PLATFORM_ID_MICROSOFT ||
2145 : PRUint32(nameRecord->encodingID) != ENCODING_ID_MICROSOFT_UNICODEBMP ||
2146 : PRUint32(nameRecord->languageID) != LANG_ID_MICROSOFT_EN_US)
2147 : continue;
2148 :
2149 : PRUint32 index;
2150 : switch ((PRUint32)nameRecord->nameID) {
2151 :
2152 : case NAME_ID_FAMILY:
2153 : index = EOTFixedHeader::EOT_FAMILY_NAME_INDEX;
2154 : break;
2155 :
2156 : case NAME_ID_STYLE:
2157 : index = EOTFixedHeader::EOT_STYLE_NAME_INDEX;
2158 : break;
2159 :
2160 : case NAME_ID_FULL:
2161 : index = EOTFixedHeader::EOT_FULL_NAME_INDEX;
2162 : break;
2163 :
2164 : case NAME_ID_VERSION:
2165 : index = EOTFixedHeader::EOT_VERSION_NAME_INDEX;
2166 : break;
2167 :
2168 : default:
2169 : continue;
2170 : }
2171 :
2172 : names[index].offset = nameRecord->offset;
2173 : names[index].length = nameRecord->length;
2174 : needNames &= ~(1 << index);
2175 :
2176 : if (needNames == 0)
2177 : break;
2178 : }
2179 :
2180 : // -- expand buffer if needed to include variable-length portion
2181 : PRUint32 eotVariableLength = 0;
2182 : for (i = 0; i < EOTFixedHeader::EOT_NUM_NAMES; i++) {
2183 : if (!(needNames & (1 << i))) {
2184 : eotVariableLength += names[i].length & (~1);
2185 : } else {
2186 : eotVariableLength += dummyNames[i].Length() * sizeof(PRUnichar);
2187 : }
2188 : }
2189 : eotVariableLength += EOTFixedHeader::EOT_NUM_NAMES * (2 /* size */
2190 : + 2 /* padding */) +
2191 : 2 /* null root string size */;
2192 :
2193 : if (!aHeader->AppendElements(eotVariableLength))
2194 : return NS_ERROR_OUT_OF_MEMORY;
2195 :
2196 : // append the string data to the end of the EOT header
2197 : PRUint8 *eotEnd = aHeader->Elements() + sizeof(EOTFixedHeader);
2198 : PRUint32 strOffset, strLen;
2199 :
2200 : for (i = 0; i < EOTFixedHeader::EOT_NUM_NAMES; i++) {
2201 : if (!(needNames & (1 << i))) {
2202 : PRUint32 namelen = names[i].length;
2203 : PRUint32 nameoff = names[i].offset; // offset from base of string storage
2204 :
2205 : // sanity check the name string location
2206 : if (PRUint64(nameOffset) + PRUint64(nameStringsBase) +
2207 : PRUint64(nameoff) + PRUint64(namelen) > dataLength) {
2208 : return NS_ERROR_FAILURE;
2209 : }
2210 :
2211 : strOffset = nameOffset + nameStringsBase + nameoff;
2212 :
2213 : // output 2-byte str size
2214 : strLen = namelen & (~1); // UTF-16 string len must be even
2215 : *((PRUint16*) eotEnd) = PRUint16(strLen);
2216 : eotEnd += 2;
2217 :
2218 : // length is number of UTF-16 chars, not bytes
2219 : CopySwapUTF16(reinterpret_cast<const PRUint16*>(aFontData + strOffset),
2220 : reinterpret_cast<PRUint16*>(eotEnd),
2221 : (strLen >> 1));
2222 : } else {
2223 : // bug 496573 -- English names are not present.
2224 : // supply an artificial one.
2225 : strLen = dummyNames[i].Length() * sizeof(PRUnichar);
2226 : *((PRUint16*) eotEnd) = PRUint16(strLen);
2227 : eotEnd += 2;
2228 :
2229 : memcpy(eotEnd, dummyNames[i].BeginReading(), strLen);
2230 : }
2231 : eotEnd += strLen;
2232 :
2233 : // add 2-byte zero padding to the end of each string
2234 : *eotEnd++ = 0;
2235 : *eotEnd++ = 0;
2236 :
2237 : // Note: Microsoft's WEFT tool produces name strings which
2238 : // include an extra null at the end of each string, in addition
2239 : // to the 2-byte zero padding that separates the string fields.
2240 : // Don't think this is important to imitate...
2241 : }
2242 :
2243 : // append null root string size
2244 : *eotEnd++ = 0;
2245 : *eotEnd++ = 0;
2246 :
2247 : NS_ASSERTION(eotEnd == aHeader->Elements() + aHeader->Length(),
2248 : "header length calculation incorrect");
2249 :
2250 : // bug 496573 -- fonts with a fullname that does not begin with the
2251 : // family name cause the EOT font loading API to hiccup
2252 : PRUint32 famOff = names[EOTFixedHeader::EOT_FAMILY_NAME_INDEX].offset;
2253 : PRUint32 famLen = names[EOTFixedHeader::EOT_FAMILY_NAME_INDEX].length;
2254 : PRUint32 fullOff = names[EOTFixedHeader::EOT_FULL_NAME_INDEX].offset;
2255 : PRUint32 fullLen = names[EOTFixedHeader::EOT_FULL_NAME_INDEX].length;
2256 :
2257 : const PRUint8 *nameStrings = aFontData + nameOffset + nameStringsBase;
2258 :
2259 : // assure that the start of the fullname matches the family name
2260 : if (famLen <= fullLen
2261 : && memcmp(nameStrings + famOff, nameStrings + fullOff, famLen)) {
2262 : aOverlay->overlaySrc = nameOffset + nameStringsBase + famOff;
2263 : aOverlay->overlaySrcLen = famLen;
2264 : aOverlay->overlayDest = nameOffset + nameStringsBase + fullOff;
2265 : }
2266 :
2267 : // -- OS/2 table data
2268 : const OS2Table *os2Data = reinterpret_cast<const OS2Table*>(aFontData + os2Offset);
2269 :
2270 : memcpy(eotHeader->panose, os2Data->panose, sizeof(eotHeader->panose));
2271 :
2272 : eotHeader->italic = (PRUint16) os2Data->fsSelection & 0x01;
2273 : eotHeader->weight = os2Data->usWeightClass;
2274 : eotHeader->unicodeRange1 = os2Data->unicodeRange1;
2275 : eotHeader->unicodeRange2 = os2Data->unicodeRange2;
2276 : eotHeader->unicodeRange3 = os2Data->unicodeRange3;
2277 : eotHeader->unicodeRange4 = os2Data->unicodeRange4;
2278 : eotHeader->codePageRange1 = os2Data->codePageRange1;
2279 : eotHeader->codePageRange2 = os2Data->codePageRange2;
2280 :
2281 : eotHeader->eotSize = aHeader->Length() + fontDataSize;
2282 :
2283 : // DumpEOTHeader(aHeader->Elements(), aHeader->Length());
2284 :
2285 : return NS_OK;
2286 : }
2287 :
2288 : /* static */
2289 : bool
2290 : gfxFontUtils::IsCffFont(const PRUint8* aFontData, bool& hasVertical)
2291 : {
2292 : // this is only called after aFontData has passed basic validation,
2293 : // so we know there is enough data present to allow us to read the version!
2294 : const SFNTHeader *sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData);
2295 :
2296 : PRUint32 i;
2297 : PRUint32 numTables = sfntHeader->numTables;
2298 : const TableDirEntry *dirEntry =
2299 : reinterpret_cast<const TableDirEntry*>(aFontData + sizeof(SFNTHeader));
2300 : hasVertical = false;
2301 : for (i = 0; i < numTables; i++, dirEntry++) {
2302 : if (dirEntry->tag == TRUETYPE_TAG('v','h','e','a')) {
2303 : hasVertical = true;
2304 : break;
2305 : }
2306 : }
2307 :
2308 : return (sfntHeader->sfntVersion == TRUETYPE_TAG('O','T','T','O'));
2309 : }
2310 :
2311 : #endif
|