1 | /* pcfread.c |
2 | |
3 | FreeType font driver for pcf fonts |
4 | |
5 | Copyright 2000-2010, 2012-2014 by |
6 | Francesco Zappa Nardelli |
7 | |
8 | Permission is hereby granted, free of charge, to any person obtaining a copy |
9 | of this software and associated documentation files (the "Software"), to deal |
10 | in the Software without restriction, including without limitation the rights |
11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
12 | copies of the Software, and to permit persons to whom the Software is |
13 | furnished to do so, subject to the following conditions: |
14 | |
15 | The above copyright notice and this permission notice shall be included in |
16 | all copies or substantial portions of the Software. |
17 | |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
24 | THE SOFTWARE. |
25 | */ |
26 | |
27 | |
28 | |
29 | #include <freetype/internal/ftdebug.h> |
30 | #include <freetype/internal/ftstream.h> |
31 | #include <freetype/internal/ftobjs.h> |
32 | |
33 | #include "pcf.h" |
34 | #include "pcfread.h" |
35 | |
36 | #include "pcferror.h" |
37 | |
38 | |
39 | /************************************************************************** |
40 | * |
41 | * The macro FT_COMPONENT is used in trace mode. It is an implicit |
42 | * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log |
43 | * messages during execution. |
44 | */ |
45 | #undef FT_COMPONENT |
46 | #define FT_COMPONENT pcfread |
47 | |
48 | |
49 | #ifdef FT_DEBUG_LEVEL_TRACE |
50 | static const char* const tableNames[] = |
51 | { |
52 | "properties" , |
53 | "accelerators" , |
54 | "metrics" , |
55 | "bitmaps" , |
56 | "ink metrics" , |
57 | "encodings" , |
58 | "swidths" , |
59 | "glyph names" , |
60 | "BDF accelerators" |
61 | }; |
62 | #endif |
63 | |
64 | |
65 | static |
66 | const FT_Frame_Field [] = |
67 | { |
68 | #undef FT_STRUCTURE |
69 | #define FT_STRUCTURE PCF_TocRec |
70 | |
71 | FT_FRAME_START( 8 ), |
72 | FT_FRAME_ULONG_LE( version ), |
73 | FT_FRAME_ULONG_LE( count ), |
74 | FT_FRAME_END |
75 | }; |
76 | |
77 | |
78 | static |
79 | const FT_Frame_Field [] = |
80 | { |
81 | #undef FT_STRUCTURE |
82 | #define FT_STRUCTURE PCF_TableRec |
83 | |
84 | FT_FRAME_START( 16 ), |
85 | FT_FRAME_ULONG_LE( type ), |
86 | FT_FRAME_ULONG_LE( format ), |
87 | FT_FRAME_ULONG_LE( size ), /* rounded up to a multiple of 4 */ |
88 | FT_FRAME_ULONG_LE( offset ), |
89 | FT_FRAME_END |
90 | }; |
91 | |
92 | |
93 | static FT_Error |
94 | pcf_read_TOC( FT_Stream stream, |
95 | PCF_Face face ) |
96 | { |
97 | FT_Error error; |
98 | PCF_Toc toc = &face->toc; |
99 | PCF_Table tables; |
100 | |
101 | FT_Memory memory = FT_FACE( face )->memory; |
102 | FT_UInt n; |
103 | |
104 | FT_ULong size; |
105 | |
106 | |
107 | if ( FT_STREAM_SEEK( 0 ) || |
108 | FT_STREAM_READ_FIELDS( pcf_toc_header, toc ) ) |
109 | return FT_THROW( Cannot_Open_Resource ); |
110 | |
111 | if ( toc->version != PCF_FILE_VERSION || |
112 | toc->count == 0 ) |
113 | return FT_THROW( Invalid_File_Format ); |
114 | |
115 | if ( stream->size < 16 ) |
116 | return FT_THROW( Invalid_File_Format ); |
117 | |
118 | /* we need 16 bytes per TOC entry, */ |
119 | /* and there can be most 9 tables */ |
120 | if ( toc->count > ( stream->size >> 4 ) || |
121 | toc->count > 9 ) |
122 | { |
123 | FT_TRACE0(( "pcf_read_TOC: adjusting number of tables" |
124 | " (from %ld to %ld)\n" , |
125 | toc->count, |
126 | FT_MIN( stream->size >> 4, 9 ) )); |
127 | toc->count = FT_MIN( stream->size >> 4, 9 ); |
128 | } |
129 | |
130 | if ( FT_QNEW_ARRAY( face->toc.tables, toc->count ) ) |
131 | return error; |
132 | |
133 | tables = face->toc.tables; |
134 | for ( n = 0; n < toc->count; n++ ) |
135 | { |
136 | if ( FT_STREAM_READ_FIELDS( pcf_table_header, tables ) ) |
137 | goto Exit; |
138 | tables++; |
139 | } |
140 | |
141 | /* Sort tables and check for overlaps. Because they are almost */ |
142 | /* always ordered already, an in-place bubble sort with simultaneous */ |
143 | /* boundary checking seems appropriate. */ |
144 | tables = face->toc.tables; |
145 | |
146 | for ( n = 0; n < toc->count - 1; n++ ) |
147 | { |
148 | FT_UInt i, have_change; |
149 | |
150 | |
151 | have_change = 0; |
152 | |
153 | for ( i = 0; i < toc->count - 1 - n; i++ ) |
154 | { |
155 | PCF_TableRec tmp; |
156 | |
157 | |
158 | if ( tables[i].offset > tables[i + 1].offset ) |
159 | { |
160 | tmp = tables[i]; |
161 | tables[i] = tables[i + 1]; |
162 | tables[i + 1] = tmp; |
163 | |
164 | have_change = 1; |
165 | } |
166 | |
167 | if ( ( tables[i].size > tables[i + 1].offset ) || |
168 | ( tables[i].offset > tables[i + 1].offset - tables[i].size ) ) |
169 | { |
170 | error = FT_THROW( Invalid_Offset ); |
171 | goto Exit; |
172 | } |
173 | } |
174 | |
175 | if ( !have_change ) |
176 | break; |
177 | } |
178 | |
179 | /* |
180 | * We now check whether the `size' and `offset' values are reasonable: |
181 | * `offset' + `size' must not exceed the stream size. |
182 | * |
183 | * Note, however, that X11's `pcfWriteFont' routine (used by the |
184 | * `bdftopcf' program to create PCF font files) has two special |
185 | * features. |
186 | * |
187 | * - It always assigns the accelerator table a size of 100 bytes in the |
188 | * TOC, regardless of its real size, which can vary between 34 and 72 |
189 | * bytes. |
190 | * |
191 | * - Due to the way the routine is designed, it ships out the last font |
192 | * table with its real size, ignoring the TOC's size value. Since |
193 | * the TOC size values are always rounded up to a multiple of 4, the |
194 | * difference can be up to three bytes for all tables except the |
195 | * accelerator table, for which the difference can be as large as 66 |
196 | * bytes. |
197 | * |
198 | */ |
199 | |
200 | tables = face->toc.tables; |
201 | size = stream->size; |
202 | |
203 | for ( n = 0; n < toc->count - 1; n++ ) |
204 | { |
205 | /* we need two checks to avoid overflow */ |
206 | if ( ( tables->size > size ) || |
207 | ( tables->offset > size - tables->size ) ) |
208 | { |
209 | error = FT_THROW( Invalid_Table ); |
210 | goto Exit; |
211 | } |
212 | tables++; |
213 | } |
214 | |
215 | /* only check `tables->offset' for last table element ... */ |
216 | if ( ( tables->offset > size ) ) |
217 | { |
218 | error = FT_THROW( Invalid_Table ); |
219 | goto Exit; |
220 | } |
221 | /* ... and adjust `tables->size' to the real value if necessary */ |
222 | if ( tables->size > size - tables->offset ) |
223 | tables->size = size - tables->offset; |
224 | |
225 | #ifdef FT_DEBUG_LEVEL_TRACE |
226 | |
227 | { |
228 | FT_UInt i, j; |
229 | const char* name = "?" ; |
230 | |
231 | |
232 | FT_TRACE4(( "pcf_read_TOC:\n" )); |
233 | |
234 | FT_TRACE4(( " number of tables: %ld\n" , face->toc.count )); |
235 | |
236 | tables = face->toc.tables; |
237 | for ( i = 0; i < toc->count; i++ ) |
238 | { |
239 | for ( j = 0; j < sizeof ( tableNames ) / sizeof ( tableNames[0] ); |
240 | j++ ) |
241 | if ( tables[i].type == 1UL << j ) |
242 | name = tableNames[j]; |
243 | |
244 | FT_TRACE4(( " %d: type=%s, format=0x%lX," |
245 | " size=%ld (0x%lX), offset=%ld (0x%lX)\n" , |
246 | i, name, |
247 | tables[i].format, |
248 | tables[i].size, tables[i].size, |
249 | tables[i].offset, tables[i].offset )); |
250 | } |
251 | } |
252 | |
253 | #endif |
254 | |
255 | return FT_Err_Ok; |
256 | |
257 | Exit: |
258 | FT_FREE( face->toc.tables ); |
259 | return error; |
260 | } |
261 | |
262 | |
263 | #define PCF_METRIC_SIZE 12 |
264 | |
265 | static |
266 | const FT_Frame_Field [] = |
267 | { |
268 | #undef FT_STRUCTURE |
269 | #define FT_STRUCTURE PCF_MetricRec |
270 | |
271 | FT_FRAME_START( PCF_METRIC_SIZE ), |
272 | FT_FRAME_SHORT_LE( leftSideBearing ), |
273 | FT_FRAME_SHORT_LE( rightSideBearing ), |
274 | FT_FRAME_SHORT_LE( characterWidth ), |
275 | FT_FRAME_SHORT_LE( ascent ), |
276 | FT_FRAME_SHORT_LE( descent ), |
277 | FT_FRAME_SHORT_LE( attributes ), |
278 | FT_FRAME_END |
279 | }; |
280 | |
281 | |
282 | static |
283 | const FT_Frame_Field [] = |
284 | { |
285 | #undef FT_STRUCTURE |
286 | #define FT_STRUCTURE PCF_MetricRec |
287 | |
288 | FT_FRAME_START( PCF_METRIC_SIZE ), |
289 | FT_FRAME_SHORT( leftSideBearing ), |
290 | FT_FRAME_SHORT( rightSideBearing ), |
291 | FT_FRAME_SHORT( characterWidth ), |
292 | FT_FRAME_SHORT( ascent ), |
293 | FT_FRAME_SHORT( descent ), |
294 | FT_FRAME_SHORT( attributes ), |
295 | FT_FRAME_END |
296 | }; |
297 | |
298 | |
299 | #define PCF_COMPRESSED_METRIC_SIZE 5 |
300 | |
301 | static |
302 | const FT_Frame_Field [] = |
303 | { |
304 | #undef FT_STRUCTURE |
305 | #define FT_STRUCTURE PCF_Compressed_MetricRec |
306 | |
307 | FT_FRAME_START( PCF_COMPRESSED_METRIC_SIZE ), |
308 | FT_FRAME_BYTE( leftSideBearing ), |
309 | FT_FRAME_BYTE( rightSideBearing ), |
310 | FT_FRAME_BYTE( characterWidth ), |
311 | FT_FRAME_BYTE( ascent ), |
312 | FT_FRAME_BYTE( descent ), |
313 | FT_FRAME_END |
314 | }; |
315 | |
316 | |
317 | static FT_Error |
318 | pcf_get_metric( FT_Stream stream, |
319 | FT_ULong format, |
320 | PCF_Metric metric ) |
321 | { |
322 | FT_Error error = FT_Err_Ok; |
323 | |
324 | |
325 | if ( PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) ) |
326 | { |
327 | const FT_Frame_Field* fields; |
328 | |
329 | |
330 | /* parsing normal metrics */ |
331 | fields = ( PCF_BYTE_ORDER( format ) == MSBFirst ) |
332 | ? pcf_metric_msb_header |
333 | : pcf_metric_header; |
334 | |
335 | /* the following sets `error' but doesn't return in case of failure */ |
336 | (void)FT_STREAM_READ_FIELDS( fields, metric ); |
337 | } |
338 | else |
339 | { |
340 | PCF_Compressed_MetricRec compr; |
341 | |
342 | |
343 | /* parsing compressed metrics */ |
344 | if ( FT_STREAM_READ_FIELDS( pcf_compressed_metric_header, &compr ) ) |
345 | goto Exit; |
346 | |
347 | metric->leftSideBearing = (FT_Short)( compr.leftSideBearing - 0x80 ); |
348 | metric->rightSideBearing = (FT_Short)( compr.rightSideBearing - 0x80 ); |
349 | metric->characterWidth = (FT_Short)( compr.characterWidth - 0x80 ); |
350 | metric->ascent = (FT_Short)( compr.ascent - 0x80 ); |
351 | metric->descent = (FT_Short)( compr.descent - 0x80 ); |
352 | metric->attributes = 0; |
353 | } |
354 | |
355 | FT_TRACE5(( " width=%d," |
356 | " lsb=%d, rsb=%d," |
357 | " ascent=%d, descent=%d," |
358 | " attributes=%d\n" , |
359 | metric->characterWidth, |
360 | metric->leftSideBearing, |
361 | metric->rightSideBearing, |
362 | metric->ascent, |
363 | metric->descent, |
364 | metric->attributes )); |
365 | |
366 | Exit: |
367 | return error; |
368 | } |
369 | |
370 | |
371 | static FT_Error |
372 | pcf_seek_to_table_type( FT_Stream stream, |
373 | PCF_Table tables, |
374 | FT_ULong ntables, /* same as PCF_Toc->count */ |
375 | FT_ULong type, |
376 | FT_ULong *aformat, |
377 | FT_ULong *asize ) |
378 | { |
379 | FT_Error error = FT_ERR( Invalid_File_Format ); |
380 | FT_ULong i; |
381 | |
382 | |
383 | for ( i = 0; i < ntables; i++ ) |
384 | if ( tables[i].type == type ) |
385 | { |
386 | if ( stream->pos > tables[i].offset ) |
387 | { |
388 | error = FT_THROW( Invalid_Stream_Skip ); |
389 | goto Fail; |
390 | } |
391 | |
392 | if ( FT_STREAM_SKIP( tables[i].offset - stream->pos ) ) |
393 | { |
394 | error = FT_THROW( Invalid_Stream_Skip ); |
395 | goto Fail; |
396 | } |
397 | |
398 | *asize = tables[i].size; |
399 | *aformat = tables[i].format; |
400 | |
401 | return FT_Err_Ok; |
402 | } |
403 | |
404 | Fail: |
405 | *asize = 0; |
406 | return error; |
407 | } |
408 | |
409 | |
410 | static FT_Bool |
411 | pcf_has_table_type( PCF_Table tables, |
412 | FT_ULong ntables, /* same as PCF_Toc->count */ |
413 | FT_ULong type ) |
414 | { |
415 | FT_ULong i; |
416 | |
417 | |
418 | for ( i = 0; i < ntables; i++ ) |
419 | if ( tables[i].type == type ) |
420 | return TRUE; |
421 | |
422 | return FALSE; |
423 | } |
424 | |
425 | |
426 | #define PCF_PROPERTY_SIZE 9 |
427 | |
428 | static |
429 | const FT_Frame_Field [] = |
430 | { |
431 | #undef FT_STRUCTURE |
432 | #define FT_STRUCTURE PCF_ParsePropertyRec |
433 | |
434 | FT_FRAME_START( PCF_PROPERTY_SIZE ), |
435 | FT_FRAME_LONG_LE( name ), |
436 | FT_FRAME_BYTE ( isString ), |
437 | FT_FRAME_LONG_LE( value ), |
438 | FT_FRAME_END |
439 | }; |
440 | |
441 | |
442 | static |
443 | const FT_Frame_Field [] = |
444 | { |
445 | #undef FT_STRUCTURE |
446 | #define FT_STRUCTURE PCF_ParsePropertyRec |
447 | |
448 | FT_FRAME_START( PCF_PROPERTY_SIZE ), |
449 | FT_FRAME_LONG( name ), |
450 | FT_FRAME_BYTE( isString ), |
451 | FT_FRAME_LONG( value ), |
452 | FT_FRAME_END |
453 | }; |
454 | |
455 | |
456 | FT_LOCAL_DEF( PCF_Property ) |
457 | pcf_find_property( PCF_Face face, |
458 | const FT_String* prop ) |
459 | { |
460 | PCF_Property properties = face->properties; |
461 | FT_Bool found = 0; |
462 | int i; |
463 | |
464 | |
465 | for ( i = 0; i < face->nprops && !found; i++ ) |
466 | { |
467 | if ( !ft_strcmp( properties[i].name, prop ) ) |
468 | found = 1; |
469 | } |
470 | |
471 | if ( found ) |
472 | return properties + i - 1; |
473 | else |
474 | return NULL; |
475 | } |
476 | |
477 | |
478 | static FT_Error |
479 | pcf_get_properties( FT_Stream stream, |
480 | PCF_Face face ) |
481 | { |
482 | PCF_ParseProperty props = NULL; |
483 | PCF_Property properties = NULL; |
484 | FT_ULong nprops, orig_nprops, i; |
485 | FT_ULong format, size; |
486 | FT_Error error; |
487 | FT_Memory memory = FT_FACE( face )->memory; |
488 | FT_ULong string_size; |
489 | FT_String* strings = NULL; |
490 | |
491 | |
492 | error = pcf_seek_to_table_type( stream, |
493 | face->toc.tables, |
494 | face->toc.count, |
495 | PCF_PROPERTIES, |
496 | &format, |
497 | &size ); |
498 | if ( error ) |
499 | goto Bail; |
500 | |
501 | if ( FT_READ_ULONG_LE( format ) ) |
502 | goto Bail; |
503 | |
504 | FT_TRACE4(( "pcf_get_properties:\n" )); |
505 | FT_TRACE4(( " format: 0x%lX (%s)\n" , |
506 | format, |
507 | PCF_BYTE_ORDER( format ) == MSBFirst ? "MSB" : "LSB" )); |
508 | |
509 | if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) ) |
510 | goto Bail; |
511 | |
512 | if ( PCF_BYTE_ORDER( format ) == MSBFirst ) |
513 | (void)FT_READ_ULONG( orig_nprops ); |
514 | else |
515 | (void)FT_READ_ULONG_LE( orig_nprops ); |
516 | if ( error ) |
517 | goto Bail; |
518 | |
519 | FT_TRACE4(( " number of properties: %ld\n" , orig_nprops )); |
520 | |
521 | /* rough estimate */ |
522 | if ( orig_nprops > size / PCF_PROPERTY_SIZE ) |
523 | { |
524 | error = FT_THROW( Invalid_Table ); |
525 | goto Bail; |
526 | } |
527 | |
528 | /* as a heuristic limit to avoid excessive allocation in */ |
529 | /* gzip bombs (i.e., very small, invalid input data that */ |
530 | /* pretends to expand to an insanely large file) we only */ |
531 | /* load the first 256 properties */ |
532 | if ( orig_nprops > 256 ) |
533 | { |
534 | FT_TRACE0(( "pcf_get_properties:" |
535 | " only loading first 256 properties\n" )); |
536 | nprops = 256; |
537 | } |
538 | else |
539 | nprops = orig_nprops; |
540 | |
541 | face->nprops = (int)nprops; |
542 | |
543 | if ( FT_QNEW_ARRAY( props, nprops ) ) |
544 | goto Bail; |
545 | |
546 | for ( i = 0; i < nprops; i++ ) |
547 | { |
548 | if ( PCF_BYTE_ORDER( format ) == MSBFirst ) |
549 | { |
550 | if ( FT_STREAM_READ_FIELDS( pcf_property_msb_header, props + i ) ) |
551 | goto Bail; |
552 | } |
553 | else |
554 | { |
555 | if ( FT_STREAM_READ_FIELDS( pcf_property_header, props + i ) ) |
556 | goto Bail; |
557 | } |
558 | } |
559 | |
560 | /* this skip will only work if we really have an extremely large */ |
561 | /* number of properties; it will fail for fake data, avoiding an */ |
562 | /* unnecessarily large allocation later on */ |
563 | if ( FT_STREAM_SKIP( ( orig_nprops - nprops ) * PCF_PROPERTY_SIZE ) ) |
564 | { |
565 | error = FT_THROW( Invalid_Stream_Skip ); |
566 | goto Bail; |
567 | } |
568 | |
569 | /* pad the property array */ |
570 | /* */ |
571 | /* clever here - nprops is the same as the number of odd-units read, */ |
572 | /* as only isStringProp are odd length (Keith Packard) */ |
573 | /* */ |
574 | if ( orig_nprops & 3 ) |
575 | { |
576 | i = 4 - ( orig_nprops & 3 ); |
577 | if ( FT_STREAM_SKIP( i ) ) |
578 | { |
579 | error = FT_THROW( Invalid_Stream_Skip ); |
580 | goto Bail; |
581 | } |
582 | } |
583 | |
584 | if ( PCF_BYTE_ORDER( format ) == MSBFirst ) |
585 | (void)FT_READ_ULONG( string_size ); |
586 | else |
587 | (void)FT_READ_ULONG_LE( string_size ); |
588 | if ( error ) |
589 | goto Bail; |
590 | |
591 | FT_TRACE4(( " string size: %ld\n" , string_size )); |
592 | |
593 | /* rough estimate */ |
594 | if ( string_size > size - orig_nprops * PCF_PROPERTY_SIZE ) |
595 | { |
596 | error = FT_THROW( Invalid_Table ); |
597 | goto Bail; |
598 | } |
599 | |
600 | /* the strings in the `strings' array are PostScript strings, */ |
601 | /* which can have a maximum length of 65536 characters each */ |
602 | if ( string_size > 16777472 ) /* 256 * (65536 + 1) */ |
603 | { |
604 | FT_TRACE0(( "pcf_get_properties:" |
605 | " loading only 16777472 bytes of strings array\n" )); |
606 | string_size = 16777472; |
607 | } |
608 | |
609 | /* allocate one more byte so that we have a final null byte */ |
610 | if ( FT_QALLOC( strings, string_size + 1 ) || |
611 | FT_STREAM_READ( strings, string_size ) ) |
612 | goto Bail; |
613 | |
614 | strings[string_size] = '\0'; |
615 | |
616 | /* zero out in case of failure */ |
617 | if ( FT_NEW_ARRAY( properties, nprops ) ) |
618 | goto Bail; |
619 | |
620 | face->properties = properties; |
621 | |
622 | FT_TRACE4(( "\n" )); |
623 | for ( i = 0; i < nprops; i++ ) |
624 | { |
625 | FT_Long name_offset = props[i].name; |
626 | |
627 | |
628 | if ( ( name_offset < 0 ) || |
629 | ( (FT_ULong)name_offset > string_size ) ) |
630 | { |
631 | error = FT_THROW( Invalid_Offset ); |
632 | goto Bail; |
633 | } |
634 | |
635 | if ( FT_STRDUP( properties[i].name, strings + name_offset ) ) |
636 | goto Bail; |
637 | |
638 | FT_TRACE4(( " %s:" , properties[i].name )); |
639 | |
640 | properties[i].isString = props[i].isString; |
641 | |
642 | if ( props[i].isString ) |
643 | { |
644 | FT_Long value_offset = props[i].value; |
645 | |
646 | |
647 | if ( ( value_offset < 0 ) || |
648 | ( (FT_ULong)value_offset > string_size ) ) |
649 | { |
650 | error = FT_THROW( Invalid_Offset ); |
651 | goto Bail; |
652 | } |
653 | |
654 | if ( FT_STRDUP( properties[i].value.atom, strings + value_offset ) ) |
655 | goto Bail; |
656 | |
657 | FT_TRACE4(( " `%s'\n" , properties[i].value.atom )); |
658 | } |
659 | else |
660 | { |
661 | properties[i].value.l = props[i].value; |
662 | |
663 | FT_TRACE4(( " %ld\n" , properties[i].value.l )); |
664 | } |
665 | } |
666 | |
667 | error = FT_Err_Ok; |
668 | |
669 | Bail: |
670 | FT_FREE( props ); |
671 | FT_FREE( strings ); |
672 | |
673 | return error; |
674 | } |
675 | |
676 | |
677 | static FT_Error |
678 | pcf_get_metrics( FT_Stream stream, |
679 | PCF_Face face ) |
680 | { |
681 | FT_Error error; |
682 | FT_Memory memory = FT_FACE( face )->memory; |
683 | FT_ULong format, size; |
684 | PCF_Metric metrics = NULL; |
685 | FT_ULong nmetrics, orig_nmetrics, i; |
686 | |
687 | |
688 | error = pcf_seek_to_table_type( stream, |
689 | face->toc.tables, |
690 | face->toc.count, |
691 | PCF_METRICS, |
692 | &format, |
693 | &size ); |
694 | if ( error ) |
695 | return error; |
696 | |
697 | if ( FT_READ_ULONG_LE( format ) ) |
698 | goto Bail; |
699 | |
700 | FT_TRACE4(( "pcf_get_metrics:\n" )); |
701 | FT_TRACE4(( " format: 0x%lX (%s, %s)\n" , |
702 | format, |
703 | PCF_BYTE_ORDER( format ) == MSBFirst ? "MSB" : "LSB" , |
704 | PCF_FORMAT_MATCH( format, PCF_COMPRESSED_METRICS ) ? |
705 | "compressed" : "uncompressed" )); |
706 | |
707 | if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) && |
708 | !PCF_FORMAT_MATCH( format, PCF_COMPRESSED_METRICS ) ) |
709 | return FT_THROW( Invalid_File_Format ); |
710 | |
711 | if ( PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) ) |
712 | { |
713 | if ( PCF_BYTE_ORDER( format ) == MSBFirst ) |
714 | (void)FT_READ_ULONG( orig_nmetrics ); |
715 | else |
716 | (void)FT_READ_ULONG_LE( orig_nmetrics ); |
717 | } |
718 | else |
719 | { |
720 | if ( PCF_BYTE_ORDER( format ) == MSBFirst ) |
721 | (void)FT_READ_USHORT( orig_nmetrics ); |
722 | else |
723 | (void)FT_READ_USHORT_LE( orig_nmetrics ); |
724 | } |
725 | if ( error ) |
726 | return FT_THROW( Invalid_File_Format ); |
727 | |
728 | FT_TRACE4(( " number of metrics: %ld\n" , orig_nmetrics )); |
729 | |
730 | /* rough estimate */ |
731 | if ( PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) ) |
732 | { |
733 | if ( orig_nmetrics > size / PCF_METRIC_SIZE ) |
734 | return FT_THROW( Invalid_Table ); |
735 | } |
736 | else |
737 | { |
738 | if ( orig_nmetrics > size / PCF_COMPRESSED_METRIC_SIZE ) |
739 | return FT_THROW( Invalid_Table ); |
740 | } |
741 | |
742 | if ( !orig_nmetrics ) |
743 | return FT_THROW( Invalid_Table ); |
744 | |
745 | /* |
746 | * PCF is a format from ancient times; Unicode was in its infancy, and |
747 | * widely used two-byte character sets for CJK scripts (Big 5, GB 2312, |
748 | * JIS X 0208, etc.) did have at most 15000 characters. Even the more |
749 | * exotic CNS 11643 and CCCII standards, which were essentially |
750 | * three-byte character sets, provided less then 65536 assigned |
751 | * characters. |
752 | * |
753 | * While technically possible to have a larger number of glyphs in PCF |
754 | * files, we thus limit the number to 65535, taking into account that we |
755 | * synthesize the metrics of glyph 0 to be a copy of the `default |
756 | * character', and that 0xFFFF in the encodings array indicates a |
757 | * missing glyph. |
758 | */ |
759 | if ( orig_nmetrics > 65534 ) |
760 | { |
761 | FT_TRACE0(( "pcf_get_metrics:" |
762 | " only loading first 65534 metrics\n" )); |
763 | nmetrics = 65534; |
764 | } |
765 | else |
766 | nmetrics = orig_nmetrics; |
767 | |
768 | face->nmetrics = nmetrics + 1; |
769 | |
770 | if ( FT_QNEW_ARRAY( face->metrics, face->nmetrics ) ) |
771 | return error; |
772 | |
773 | /* we handle glyph index 0 later on */ |
774 | metrics = face->metrics + 1; |
775 | |
776 | FT_TRACE4(( "\n" )); |
777 | for ( i = 1; i < face->nmetrics; i++, metrics++ ) |
778 | { |
779 | FT_TRACE5(( " idx %ld:" , i )); |
780 | error = pcf_get_metric( stream, format, metrics ); |
781 | |
782 | metrics->bits = 0; |
783 | |
784 | if ( error ) |
785 | break; |
786 | |
787 | /* sanity checks -- those values are used in `PCF_Glyph_Load' to */ |
788 | /* compute a glyph's bitmap dimensions, thus setting them to zero in */ |
789 | /* case of an error disables this particular glyph only */ |
790 | if ( metrics->rightSideBearing < metrics->leftSideBearing || |
791 | metrics->ascent < -metrics->descent ) |
792 | { |
793 | metrics->characterWidth = 0; |
794 | metrics->leftSideBearing = 0; |
795 | metrics->rightSideBearing = 0; |
796 | metrics->ascent = 0; |
797 | metrics->descent = 0; |
798 | |
799 | FT_TRACE0(( "pcf_get_metrics:" |
800 | " invalid metrics for glyph %ld\n" , i )); |
801 | } |
802 | } |
803 | |
804 | if ( error ) |
805 | FT_FREE( face->metrics ); |
806 | |
807 | Bail: |
808 | return error; |
809 | } |
810 | |
811 | |
812 | static FT_Error |
813 | pcf_get_bitmaps( FT_Stream stream, |
814 | PCF_Face face ) |
815 | { |
816 | FT_Error error; |
817 | FT_ULong bitmapSizes[GLYPHPADOPTIONS]; |
818 | FT_ULong format, size, pos; |
819 | FT_ULong nbitmaps, orig_nbitmaps, i, sizebitmaps = 0; |
820 | |
821 | |
822 | error = pcf_seek_to_table_type( stream, |
823 | face->toc.tables, |
824 | face->toc.count, |
825 | PCF_BITMAPS, |
826 | &format, |
827 | &size ); |
828 | if ( error ) |
829 | return error; |
830 | |
831 | error = FT_Stream_EnterFrame( stream, 8 ); |
832 | if ( error ) |
833 | return error; |
834 | |
835 | format = FT_GET_ULONG_LE(); |
836 | if ( PCF_BYTE_ORDER( format ) == MSBFirst ) |
837 | orig_nbitmaps = FT_GET_ULONG(); |
838 | else |
839 | orig_nbitmaps = FT_GET_ULONG_LE(); |
840 | |
841 | FT_Stream_ExitFrame( stream ); |
842 | |
843 | FT_TRACE4(( "pcf_get_bitmaps:\n" )); |
844 | FT_TRACE4(( " format: 0x%lX\n" , format )); |
845 | FT_TRACE4(( " (%s, %s,\n" , |
846 | PCF_BYTE_ORDER( format ) == MSBFirst |
847 | ? "most significant byte first" |
848 | : "least significant byte first" , |
849 | PCF_BIT_ORDER( format ) == MSBFirst |
850 | ? "most significant bit first" |
851 | : "least significant bit first" )); |
852 | FT_TRACE4(( " padding=%d bit%s, scanning=%d bit%s)\n" , |
853 | 8 << PCF_GLYPH_PAD_INDEX( format ), |
854 | ( 8 << PCF_GLYPH_PAD_INDEX( format ) ) == 1 ? "" : "s" , |
855 | 8 << PCF_SCAN_UNIT_INDEX( format ), |
856 | ( 8 << PCF_SCAN_UNIT_INDEX( format ) ) == 1 ? "" : "s" )); |
857 | |
858 | if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) ) |
859 | return FT_THROW( Invalid_File_Format ); |
860 | |
861 | FT_TRACE4(( " number of bitmaps: %ld\n" , orig_nbitmaps )); |
862 | |
863 | /* see comment in `pcf_get_metrics' */ |
864 | if ( orig_nbitmaps > 65534 ) |
865 | { |
866 | FT_TRACE0(( "pcf_get_bitmaps:" |
867 | " only loading first 65534 bitmaps\n" )); |
868 | nbitmaps = 65534; |
869 | } |
870 | else |
871 | nbitmaps = orig_nbitmaps; |
872 | |
873 | /* no extra bitmap for glyph 0 */ |
874 | if ( nbitmaps != face->nmetrics - 1 ) |
875 | return FT_THROW( Invalid_File_Format ); |
876 | |
877 | /* start position of bitmap data */ |
878 | pos = stream->pos + nbitmaps * 4 + 4 * 4; |
879 | |
880 | FT_TRACE5(( "\n" )); |
881 | for ( i = 1; i <= nbitmaps; i++ ) |
882 | { |
883 | FT_ULong offset; |
884 | |
885 | |
886 | if ( PCF_BYTE_ORDER( format ) == MSBFirst ) |
887 | (void)FT_READ_ULONG( offset ); |
888 | else |
889 | (void)FT_READ_ULONG_LE( offset ); |
890 | |
891 | FT_TRACE5(( " bitmap %lu: offset %lu (0x%lX)\n" , |
892 | i, offset, offset )); |
893 | |
894 | /* right now, we only check the offset with a rough estimate; */ |
895 | /* actual bitmaps are only loaded on demand */ |
896 | if ( offset > size ) |
897 | { |
898 | FT_TRACE0(( "pcf_get_bitmaps:" |
899 | " invalid offset to bitmap data of glyph %lu\n" , i )); |
900 | face->metrics[i].bits = pos; |
901 | } |
902 | else |
903 | face->metrics[i].bits = pos + offset; |
904 | } |
905 | if ( error ) |
906 | goto Bail; |
907 | |
908 | for ( i = 0; i < GLYPHPADOPTIONS; i++ ) |
909 | { |
910 | if ( PCF_BYTE_ORDER( format ) == MSBFirst ) |
911 | (void)FT_READ_ULONG( bitmapSizes[i] ); |
912 | else |
913 | (void)FT_READ_ULONG_LE( bitmapSizes[i] ); |
914 | if ( error ) |
915 | goto Bail; |
916 | |
917 | sizebitmaps = bitmapSizes[PCF_GLYPH_PAD_INDEX( format )]; |
918 | |
919 | FT_TRACE4(( " %d-bit padding implies a size of %lu\n" , |
920 | 8 << i, bitmapSizes[i] )); |
921 | } |
922 | |
923 | FT_TRACE4(( " %lu bitmaps, using %d-bit padding\n" , |
924 | nbitmaps, |
925 | 8 << PCF_GLYPH_PAD_INDEX( format ) )); |
926 | FT_TRACE4(( " bitmap size: %lu\n" , sizebitmaps )); |
927 | |
928 | FT_UNUSED( sizebitmaps ); /* only used for debugging */ |
929 | |
930 | face->bitmapsFormat = format; |
931 | |
932 | Bail: |
933 | return error; |
934 | } |
935 | |
936 | |
937 | /* |
938 | * This file uses X11 terminology for PCF data; an `encoding' in X11 speak |
939 | * is the same as a character code in FreeType speak. |
940 | */ |
941 | #define PCF_ENC_SIZE 10 |
942 | |
943 | static |
944 | const FT_Frame_Field [] = |
945 | { |
946 | #undef FT_STRUCTURE |
947 | #define FT_STRUCTURE PCF_EncRec |
948 | |
949 | FT_FRAME_START( PCF_ENC_SIZE ), |
950 | FT_FRAME_USHORT_LE( firstCol ), |
951 | FT_FRAME_USHORT_LE( lastCol ), |
952 | FT_FRAME_USHORT_LE( firstRow ), |
953 | FT_FRAME_USHORT_LE( lastRow ), |
954 | FT_FRAME_USHORT_LE( defaultChar ), |
955 | FT_FRAME_END |
956 | }; |
957 | |
958 | |
959 | static |
960 | const FT_Frame_Field [] = |
961 | { |
962 | #undef FT_STRUCTURE |
963 | #define FT_STRUCTURE PCF_EncRec |
964 | |
965 | FT_FRAME_START( PCF_ENC_SIZE ), |
966 | FT_FRAME_USHORT( firstCol ), |
967 | FT_FRAME_USHORT( lastCol ), |
968 | FT_FRAME_USHORT( firstRow ), |
969 | FT_FRAME_USHORT( lastRow ), |
970 | FT_FRAME_USHORT( defaultChar ), |
971 | FT_FRAME_END |
972 | }; |
973 | |
974 | |
975 | static FT_Error |
976 | pcf_get_encodings( FT_Stream stream, |
977 | PCF_Face face ) |
978 | { |
979 | FT_Error error; |
980 | FT_Memory memory = FT_FACE( face )->memory; |
981 | FT_ULong format, size; |
982 | PCF_Enc enc = &face->enc; |
983 | FT_ULong nencoding; |
984 | FT_UShort* offset; |
985 | FT_UShort defaultCharRow, defaultCharCol; |
986 | FT_UShort encodingOffset, defaultCharEncodingOffset; |
987 | FT_UShort i, j; |
988 | FT_Byte* pos; |
989 | |
990 | |
991 | error = pcf_seek_to_table_type( stream, |
992 | face->toc.tables, |
993 | face->toc.count, |
994 | PCF_BDF_ENCODINGS, |
995 | &format, |
996 | &size ); |
997 | if ( error ) |
998 | goto Bail; |
999 | |
1000 | if ( FT_READ_ULONG_LE( format ) ) |
1001 | goto Bail; |
1002 | |
1003 | FT_TRACE4(( "pcf_get_encodings:\n" )); |
1004 | FT_TRACE4(( " format: 0x%lX (%s)\n" , |
1005 | format, |
1006 | PCF_BYTE_ORDER( format ) == MSBFirst ? "MSB" : "LSB" )); |
1007 | |
1008 | if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) && |
1009 | !PCF_FORMAT_MATCH( format, PCF_BDF_ENCODINGS ) ) |
1010 | return FT_THROW( Invalid_File_Format ); |
1011 | |
1012 | if ( PCF_BYTE_ORDER( format ) == MSBFirst ) |
1013 | { |
1014 | if ( FT_STREAM_READ_FIELDS( pcf_enc_msb_header, enc ) ) |
1015 | goto Bail; |
1016 | } |
1017 | else |
1018 | { |
1019 | if ( FT_STREAM_READ_FIELDS( pcf_enc_header, enc ) ) |
1020 | goto Bail; |
1021 | } |
1022 | |
1023 | FT_TRACE4(( " firstCol 0x%X, lastCol 0x%X\n" , |
1024 | enc->firstCol, enc->lastCol )); |
1025 | FT_TRACE4(( " firstRow 0x%X, lastRow 0x%X\n" , |
1026 | enc->firstRow, enc->lastRow )); |
1027 | FT_TRACE4(( " defaultChar 0x%X\n" , |
1028 | enc->defaultChar )); |
1029 | |
1030 | /* sanity checks; we limit numbers of rows and columns to 256 */ |
1031 | if ( enc->firstCol > enc->lastCol || |
1032 | enc->lastCol > 0xFF || |
1033 | enc->firstRow > enc->lastRow || |
1034 | enc->lastRow > 0xFF ) |
1035 | return FT_THROW( Invalid_Table ); |
1036 | |
1037 | FT_TRACE5(( "\n" )); |
1038 | |
1039 | defaultCharRow = enc->defaultChar >> 8; |
1040 | defaultCharCol = enc->defaultChar & 0xFF; |
1041 | |
1042 | /* validate default character */ |
1043 | if ( defaultCharRow < enc->firstRow || |
1044 | defaultCharRow > enc->lastRow || |
1045 | defaultCharCol < enc->firstCol || |
1046 | defaultCharCol > enc->lastCol ) |
1047 | { |
1048 | enc->defaultChar = enc->firstRow * 256U + enc->firstCol; |
1049 | FT_TRACE0(( "pcf_get_encodings:" |
1050 | " Invalid default character set to %u\n" , |
1051 | enc->defaultChar )); |
1052 | |
1053 | defaultCharRow = enc->firstRow; |
1054 | defaultCharCol = enc->firstCol; |
1055 | } |
1056 | |
1057 | nencoding = (FT_ULong)( enc->lastCol - enc->firstCol + 1 ) * |
1058 | (FT_ULong)( enc->lastRow - enc->firstRow + 1 ); |
1059 | |
1060 | error = FT_Stream_EnterFrame( stream, 2 * nencoding ); |
1061 | if ( error ) |
1062 | goto Bail; |
1063 | |
1064 | /* |
1065 | * FreeType mandates that glyph index 0 is the `undefined glyph', which |
1066 | * PCF calls the `default character'. However, FreeType needs glyph |
1067 | * index 0 to be used for the undefined glyph only, which is is not the |
1068 | * case for PCF. For this reason, we add one slot for glyph index 0 and |
1069 | * simply copy the default character to it. |
1070 | * |
1071 | * `stream->cursor' still points to the beginning of the frame; we can |
1072 | * thus easily get the offset to the default character. |
1073 | */ |
1074 | pos = stream->cursor + |
1075 | 2 * ( ( defaultCharRow - enc->firstRow ) * |
1076 | ( enc->lastCol - enc->firstCol + 1 ) + |
1077 | defaultCharCol - enc->firstCol ); |
1078 | |
1079 | if ( PCF_BYTE_ORDER( format ) == MSBFirst ) |
1080 | defaultCharEncodingOffset = FT_PEEK_USHORT( pos ); |
1081 | else |
1082 | defaultCharEncodingOffset = FT_PEEK_USHORT_LE( pos ); |
1083 | |
1084 | if ( defaultCharEncodingOffset == 0xFFFF ) |
1085 | { |
1086 | FT_TRACE0(( "pcf_get_encodings:" |
1087 | " No glyph for default character,\n" )); |
1088 | FT_TRACE0(( " " |
1089 | " setting it to the first glyph of the font\n" )); |
1090 | defaultCharEncodingOffset = 1; |
1091 | } |
1092 | else |
1093 | { |
1094 | defaultCharEncodingOffset++; |
1095 | |
1096 | if ( defaultCharEncodingOffset >= face->nmetrics ) |
1097 | { |
1098 | FT_TRACE0(( "pcf_get_encodings:" |
1099 | " Invalid glyph index for default character,\n" )); |
1100 | FT_TRACE0(( " " |
1101 | " setting it to the first glyph of the font\n" )); |
1102 | defaultCharEncodingOffset = 1; |
1103 | } |
1104 | } |
1105 | |
1106 | /* copy metrics of default character to index 0 */ |
1107 | face->metrics[0] = face->metrics[defaultCharEncodingOffset]; |
1108 | |
1109 | if ( FT_QNEW_ARRAY( enc->offset, nencoding ) ) |
1110 | goto Bail; |
1111 | |
1112 | /* now loop over all values */ |
1113 | offset = enc->offset; |
1114 | for ( i = enc->firstRow; i <= enc->lastRow; i++ ) |
1115 | { |
1116 | for ( j = enc->firstCol; j <= enc->lastCol; j++ ) |
1117 | { |
1118 | /* X11's reference implementation uses the equivalent to */ |
1119 | /* `FT_GET_SHORT', however PCF fonts with more than 32768 */ |
1120 | /* characters (e.g., `unifont.pcf') clearly show that an */ |
1121 | /* unsigned value is needed. */ |
1122 | if ( PCF_BYTE_ORDER( format ) == MSBFirst ) |
1123 | encodingOffset = FT_GET_USHORT(); |
1124 | else |
1125 | encodingOffset = FT_GET_USHORT_LE(); |
1126 | |
1127 | /* everything is off by 1 due to the artificial glyph 0 */ |
1128 | *offset++ = encodingOffset == 0xFFFF ? 0xFFFF |
1129 | : encodingOffset + 1; |
1130 | } |
1131 | } |
1132 | FT_Stream_ExitFrame( stream ); |
1133 | |
1134 | Bail: |
1135 | return error; |
1136 | } |
1137 | |
1138 | |
1139 | static |
1140 | const FT_Frame_Field [] = |
1141 | { |
1142 | #undef FT_STRUCTURE |
1143 | #define FT_STRUCTURE PCF_AccelRec |
1144 | |
1145 | FT_FRAME_START( 20 ), |
1146 | FT_FRAME_BYTE ( noOverlap ), |
1147 | FT_FRAME_BYTE ( constantMetrics ), |
1148 | FT_FRAME_BYTE ( terminalFont ), |
1149 | FT_FRAME_BYTE ( constantWidth ), |
1150 | FT_FRAME_BYTE ( inkInside ), |
1151 | FT_FRAME_BYTE ( inkMetrics ), |
1152 | FT_FRAME_BYTE ( drawDirection ), |
1153 | FT_FRAME_SKIP_BYTES( 1 ), |
1154 | FT_FRAME_LONG_LE ( fontAscent ), |
1155 | FT_FRAME_LONG_LE ( fontDescent ), |
1156 | FT_FRAME_LONG_LE ( maxOverlap ), |
1157 | FT_FRAME_END |
1158 | }; |
1159 | |
1160 | |
1161 | static |
1162 | const FT_Frame_Field [] = |
1163 | { |
1164 | #undef FT_STRUCTURE |
1165 | #define FT_STRUCTURE PCF_AccelRec |
1166 | |
1167 | FT_FRAME_START( 20 ), |
1168 | FT_FRAME_BYTE ( noOverlap ), |
1169 | FT_FRAME_BYTE ( constantMetrics ), |
1170 | FT_FRAME_BYTE ( terminalFont ), |
1171 | FT_FRAME_BYTE ( constantWidth ), |
1172 | FT_FRAME_BYTE ( inkInside ), |
1173 | FT_FRAME_BYTE ( inkMetrics ), |
1174 | FT_FRAME_BYTE ( drawDirection ), |
1175 | FT_FRAME_SKIP_BYTES( 1 ), |
1176 | FT_FRAME_LONG ( fontAscent ), |
1177 | FT_FRAME_LONG ( fontDescent ), |
1178 | FT_FRAME_LONG ( maxOverlap ), |
1179 | FT_FRAME_END |
1180 | }; |
1181 | |
1182 | |
1183 | static FT_Error |
1184 | pcf_get_accel( FT_Stream stream, |
1185 | PCF_Face face, |
1186 | FT_ULong type ) |
1187 | { |
1188 | FT_ULong format, size; |
1189 | FT_Error error; |
1190 | PCF_Accel accel = &face->accel; |
1191 | |
1192 | |
1193 | error = pcf_seek_to_table_type( stream, |
1194 | face->toc.tables, |
1195 | face->toc.count, |
1196 | type, |
1197 | &format, |
1198 | &size ); |
1199 | if ( error ) |
1200 | goto Bail; |
1201 | |
1202 | if ( FT_READ_ULONG_LE( format ) ) |
1203 | goto Bail; |
1204 | |
1205 | FT_TRACE4(( "pcf_get_accel%s:\n" , |
1206 | type == PCF_BDF_ACCELERATORS ? " (getting BDF accelerators)" |
1207 | : "" )); |
1208 | FT_TRACE4(( " format: 0x%lX (%s, %s)\n" , |
1209 | format, |
1210 | PCF_BYTE_ORDER( format ) == MSBFirst ? "MSB" : "LSB" , |
1211 | PCF_FORMAT_MATCH( format, PCF_ACCEL_W_INKBOUNDS ) ? |
1212 | "accelerated" : "not accelerated" )); |
1213 | |
1214 | if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) && |
1215 | !PCF_FORMAT_MATCH( format, PCF_ACCEL_W_INKBOUNDS ) ) |
1216 | goto Bail; |
1217 | |
1218 | if ( PCF_BYTE_ORDER( format ) == MSBFirst ) |
1219 | { |
1220 | if ( FT_STREAM_READ_FIELDS( pcf_accel_msb_header, accel ) ) |
1221 | goto Bail; |
1222 | } |
1223 | else |
1224 | { |
1225 | if ( FT_STREAM_READ_FIELDS( pcf_accel_header, accel ) ) |
1226 | goto Bail; |
1227 | } |
1228 | |
1229 | FT_TRACE5(( " noOverlap=%s, constantMetrics=%s," |
1230 | " terminalFont=%s, constantWidth=%s\n" , |
1231 | accel->noOverlap ? "yes" : "no" , |
1232 | accel->constantMetrics ? "yes" : "no" , |
1233 | accel->terminalFont ? "yes" : "no" , |
1234 | accel->constantWidth ? "yes" : "no" )); |
1235 | FT_TRACE5(( " inkInside=%s, inkMetrics=%s, drawDirection=%s\n" , |
1236 | accel->inkInside ? "yes" : "no" , |
1237 | accel->inkMetrics ? "yes" : "no" , |
1238 | accel->drawDirection ? "RTL" : "LTR" )); |
1239 | FT_TRACE5(( " fontAscent=%ld, fontDescent=%ld, maxOverlap=%ld\n" , |
1240 | accel->fontAscent, |
1241 | accel->fontDescent, |
1242 | accel->maxOverlap )); |
1243 | |
1244 | /* sanity checks */ |
1245 | if ( FT_ABS( accel->fontAscent ) > 0x7FFF ) |
1246 | { |
1247 | accel->fontAscent = accel->fontAscent < 0 ? -0x7FFF : 0x7FFF; |
1248 | FT_TRACE0(( "pfc_get_accel: clamping font ascent to value %ld\n" , |
1249 | accel->fontAscent )); |
1250 | } |
1251 | if ( FT_ABS( accel->fontDescent ) > 0x7FFF ) |
1252 | { |
1253 | accel->fontDescent = accel->fontDescent < 0 ? -0x7FFF : 0x7FFF; |
1254 | FT_TRACE0(( "pfc_get_accel: clamping font descent to value %ld\n" , |
1255 | accel->fontDescent )); |
1256 | } |
1257 | |
1258 | FT_TRACE5(( " minbounds:" )); |
1259 | error = pcf_get_metric( stream, |
1260 | format & ( ~PCF_FORMAT_MASK ), |
1261 | &(accel->minbounds) ); |
1262 | if ( error ) |
1263 | goto Bail; |
1264 | |
1265 | FT_TRACE5(( " maxbounds:" )); |
1266 | error = pcf_get_metric( stream, |
1267 | format & ( ~PCF_FORMAT_MASK ), |
1268 | &(accel->maxbounds) ); |
1269 | if ( error ) |
1270 | goto Bail; |
1271 | |
1272 | if ( PCF_FORMAT_MATCH( format, PCF_ACCEL_W_INKBOUNDS ) ) |
1273 | { |
1274 | FT_TRACE5(( " ink minbounds:" )); |
1275 | error = pcf_get_metric( stream, |
1276 | format & ( ~PCF_FORMAT_MASK ), |
1277 | &(accel->ink_minbounds) ); |
1278 | if ( error ) |
1279 | goto Bail; |
1280 | |
1281 | FT_TRACE5(( " ink maxbounds:" )); |
1282 | error = pcf_get_metric( stream, |
1283 | format & ( ~PCF_FORMAT_MASK ), |
1284 | &(accel->ink_maxbounds) ); |
1285 | if ( error ) |
1286 | goto Bail; |
1287 | } |
1288 | else |
1289 | { |
1290 | accel->ink_minbounds = accel->minbounds; |
1291 | accel->ink_maxbounds = accel->maxbounds; |
1292 | } |
1293 | |
1294 | Bail: |
1295 | return error; |
1296 | } |
1297 | |
1298 | |
1299 | static FT_Error |
1300 | pcf_interpret_style( PCF_Face pcf ) |
1301 | { |
1302 | FT_Error error = FT_Err_Ok; |
1303 | FT_Face face = FT_FACE( pcf ); |
1304 | FT_Memory memory = face->memory; |
1305 | |
1306 | PCF_Property prop; |
1307 | |
1308 | const char* strings[4] = { NULL, NULL, NULL, NULL }; |
1309 | size_t lengths[4], nn, len; |
1310 | |
1311 | |
1312 | face->style_flags = 0; |
1313 | |
1314 | prop = pcf_find_property( pcf, "SLANT" ); |
1315 | if ( prop && prop->isString && |
1316 | ( *(prop->value.atom) == 'O' || *(prop->value.atom) == 'o' || |
1317 | *(prop->value.atom) == 'I' || *(prop->value.atom) == 'i' ) ) |
1318 | { |
1319 | face->style_flags |= FT_STYLE_FLAG_ITALIC; |
1320 | strings[2] = ( *(prop->value.atom) == 'O' || |
1321 | *(prop->value.atom) == 'o' ) ? "Oblique" |
1322 | : "Italic" ; |
1323 | } |
1324 | |
1325 | prop = pcf_find_property( pcf, "WEIGHT_NAME" ); |
1326 | if ( prop && prop->isString && |
1327 | ( *(prop->value.atom) == 'B' || *(prop->value.atom) == 'b' ) ) |
1328 | { |
1329 | face->style_flags |= FT_STYLE_FLAG_BOLD; |
1330 | strings[1] = "Bold" ; |
1331 | } |
1332 | |
1333 | prop = pcf_find_property( pcf, "SETWIDTH_NAME" ); |
1334 | if ( prop && prop->isString && |
1335 | *(prop->value.atom) && |
1336 | !( *(prop->value.atom) == 'N' || *(prop->value.atom) == 'n' ) ) |
1337 | strings[3] = (const char*)( prop->value.atom ); |
1338 | |
1339 | prop = pcf_find_property( pcf, "ADD_STYLE_NAME" ); |
1340 | if ( prop && prop->isString && |
1341 | *(prop->value.atom) && |
1342 | !( *(prop->value.atom) == 'N' || *(prop->value.atom) == 'n' ) ) |
1343 | strings[0] = (const char*)( prop->value.atom ); |
1344 | |
1345 | for ( len = 0, nn = 0; nn < 4; nn++ ) |
1346 | { |
1347 | lengths[nn] = 0; |
1348 | if ( strings[nn] ) |
1349 | { |
1350 | lengths[nn] = ft_strlen( strings[nn] ); |
1351 | len += lengths[nn] + 1; |
1352 | } |
1353 | } |
1354 | |
1355 | if ( len == 0 ) |
1356 | { |
1357 | strings[0] = "Regular" ; |
1358 | lengths[0] = ft_strlen( strings[0] ); |
1359 | len = lengths[0] + 1; |
1360 | } |
1361 | |
1362 | { |
1363 | char* s; |
1364 | |
1365 | |
1366 | if ( FT_QALLOC( face->style_name, len ) ) |
1367 | return error; |
1368 | |
1369 | s = face->style_name; |
1370 | |
1371 | for ( nn = 0; nn < 4; nn++ ) |
1372 | { |
1373 | const char* src = strings[nn]; |
1374 | |
1375 | |
1376 | len = lengths[nn]; |
1377 | |
1378 | if ( !src ) |
1379 | continue; |
1380 | |
1381 | /* separate elements with a space */ |
1382 | if ( s != face->style_name ) |
1383 | *s++ = ' '; |
1384 | |
1385 | ft_memcpy( s, src, len ); |
1386 | |
1387 | /* need to convert spaces to dashes for */ |
1388 | /* add_style_name and setwidth_name */ |
1389 | if ( nn == 0 || nn == 3 ) |
1390 | { |
1391 | size_t mm; |
1392 | |
1393 | |
1394 | for ( mm = 0; mm < len; mm++ ) |
1395 | if ( s[mm] == ' ' ) |
1396 | s[mm] = '-'; |
1397 | } |
1398 | |
1399 | s += len; |
1400 | } |
1401 | *s = 0; |
1402 | } |
1403 | |
1404 | return error; |
1405 | } |
1406 | |
1407 | |
1408 | FT_LOCAL_DEF( FT_Error ) |
1409 | pcf_load_font( FT_Stream stream, |
1410 | PCF_Face face, |
1411 | FT_Long face_index ) |
1412 | { |
1413 | FT_Face root = FT_FACE( face ); |
1414 | FT_Error error; |
1415 | FT_Memory memory = FT_FACE( face )->memory; |
1416 | FT_Bool hasBDFAccelerators; |
1417 | |
1418 | |
1419 | error = pcf_read_TOC( stream, face ); |
1420 | if ( error ) |
1421 | goto Exit; |
1422 | |
1423 | root->num_faces = 1; |
1424 | root->face_index = 0; |
1425 | |
1426 | /* If we are performing a simple font format check, exit immediately. */ |
1427 | if ( face_index < 0 ) |
1428 | return FT_Err_Ok; |
1429 | |
1430 | error = pcf_get_properties( stream, face ); |
1431 | if ( error ) |
1432 | goto Exit; |
1433 | |
1434 | /* Use the old accelerators if no BDF accelerators are in the file. */ |
1435 | hasBDFAccelerators = pcf_has_table_type( face->toc.tables, |
1436 | face->toc.count, |
1437 | PCF_BDF_ACCELERATORS ); |
1438 | if ( !hasBDFAccelerators ) |
1439 | { |
1440 | error = pcf_get_accel( stream, face, PCF_ACCELERATORS ); |
1441 | if ( error ) |
1442 | goto Exit; |
1443 | } |
1444 | |
1445 | /* metrics */ |
1446 | error = pcf_get_metrics( stream, face ); |
1447 | if ( error ) |
1448 | goto Exit; |
1449 | |
1450 | /* bitmaps */ |
1451 | error = pcf_get_bitmaps( stream, face ); |
1452 | if ( error ) |
1453 | goto Exit; |
1454 | |
1455 | /* encodings */ |
1456 | error = pcf_get_encodings( stream, face ); |
1457 | if ( error ) |
1458 | goto Exit; |
1459 | |
1460 | /* BDF style accelerators (i.e. bounds based on encoded glyphs) */ |
1461 | if ( hasBDFAccelerators ) |
1462 | { |
1463 | error = pcf_get_accel( stream, face, PCF_BDF_ACCELERATORS ); |
1464 | if ( error ) |
1465 | goto Exit; |
1466 | } |
1467 | |
1468 | /* XXX: TO DO: inkmetrics and glyph_names are missing */ |
1469 | |
1470 | /* now construct the face object */ |
1471 | { |
1472 | PCF_Property prop; |
1473 | |
1474 | |
1475 | root->face_flags |= FT_FACE_FLAG_FIXED_SIZES | |
1476 | FT_FACE_FLAG_HORIZONTAL; |
1477 | |
1478 | if ( face->accel.constantWidth ) |
1479 | root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH; |
1480 | |
1481 | if ( FT_SET_ERROR( pcf_interpret_style( face ) ) ) |
1482 | goto Exit; |
1483 | |
1484 | prop = pcf_find_property( face, "FAMILY_NAME" ); |
1485 | if ( prop && prop->isString ) |
1486 | { |
1487 | |
1488 | #ifdef PCF_CONFIG_OPTION_LONG_FAMILY_NAMES |
1489 | |
1490 | PCF_Driver driver = (PCF_Driver)FT_FACE_DRIVER( face ); |
1491 | |
1492 | |
1493 | if ( !driver->no_long_family_names ) |
1494 | { |
1495 | /* Prepend the foundry name plus a space to the family name. */ |
1496 | /* There are many fonts just called `Fixed' which look */ |
1497 | /* completely different, and which have nothing to do with each */ |
1498 | /* other. When selecting `Fixed' in KDE or Gnome one gets */ |
1499 | /* results that appear rather random, the style changes often if */ |
1500 | /* one changes the size and one cannot select some fonts at all. */ |
1501 | /* */ |
1502 | /* We also check whether we have `wide' characters; all put */ |
1503 | /* together, we get family names like `Sony Fixed' or `Misc */ |
1504 | /* Fixed Wide'. */ |
1505 | |
1506 | PCF_Property foundry_prop, point_size_prop, average_width_prop; |
1507 | |
1508 | int l = ft_strlen( prop->value.atom ) + 1; |
1509 | int wide = 0; |
1510 | |
1511 | |
1512 | foundry_prop = pcf_find_property( face, "FOUNDRY" ); |
1513 | point_size_prop = pcf_find_property( face, "POINT_SIZE" ); |
1514 | average_width_prop = pcf_find_property( face, "AVERAGE_WIDTH" ); |
1515 | |
1516 | if ( point_size_prop && average_width_prop ) |
1517 | { |
1518 | if ( average_width_prop->value.l >= point_size_prop->value.l ) |
1519 | { |
1520 | /* This font is at least square shaped or even wider */ |
1521 | wide = 1; |
1522 | l += ft_strlen( " Wide" ); |
1523 | } |
1524 | } |
1525 | |
1526 | if ( foundry_prop && foundry_prop->isString ) |
1527 | { |
1528 | l += ft_strlen( foundry_prop->value.atom ) + 1; |
1529 | |
1530 | if ( FT_QALLOC( root->family_name, l ) ) |
1531 | goto Exit; |
1532 | |
1533 | ft_strcpy( root->family_name, foundry_prop->value.atom ); |
1534 | ft_strcat( root->family_name, " " ); |
1535 | ft_strcat( root->family_name, prop->value.atom ); |
1536 | } |
1537 | else |
1538 | { |
1539 | if ( FT_QALLOC( root->family_name, l ) ) |
1540 | goto Exit; |
1541 | |
1542 | ft_strcpy( root->family_name, prop->value.atom ); |
1543 | } |
1544 | |
1545 | if ( wide ) |
1546 | ft_strcat( root->family_name, " Wide" ); |
1547 | } |
1548 | else |
1549 | |
1550 | #endif /* PCF_CONFIG_OPTION_LONG_FAMILY_NAMES */ |
1551 | |
1552 | { |
1553 | if ( FT_STRDUP( root->family_name, prop->value.atom ) ) |
1554 | goto Exit; |
1555 | } |
1556 | } |
1557 | else |
1558 | root->family_name = NULL; |
1559 | |
1560 | root->num_glyphs = (FT_Long)face->nmetrics; |
1561 | |
1562 | root->num_fixed_sizes = 1; |
1563 | if ( FT_NEW( root->available_sizes ) ) |
1564 | goto Exit; |
1565 | |
1566 | { |
1567 | FT_Bitmap_Size* bsize = root->available_sizes; |
1568 | FT_Short resolution_x = 0, resolution_y = 0; |
1569 | |
1570 | |
1571 | /* for simplicity, we take absolute values of integer properties */ |
1572 | |
1573 | #if 0 |
1574 | bsize->height = face->accel.maxbounds.ascent << 6; |
1575 | #endif |
1576 | |
1577 | #ifdef FT_DEBUG_LEVEL_TRACE |
1578 | if ( face->accel.fontAscent + face->accel.fontDescent < 0 ) |
1579 | FT_TRACE0(( "pcf_load_font: negative height\n" )); |
1580 | #endif |
1581 | if ( FT_ABS( face->accel.fontAscent + |
1582 | face->accel.fontDescent ) > 0x7FFF ) |
1583 | { |
1584 | bsize->height = 0x7FFF; |
1585 | FT_TRACE0(( "pcf_load_font: clamping height to value %d\n" , |
1586 | bsize->height )); |
1587 | } |
1588 | else |
1589 | bsize->height = FT_ABS( (FT_Short)( face->accel.fontAscent + |
1590 | face->accel.fontDescent ) ); |
1591 | |
1592 | prop = pcf_find_property( face, "AVERAGE_WIDTH" ); |
1593 | if ( prop ) |
1594 | { |
1595 | #ifdef FT_DEBUG_LEVEL_TRACE |
1596 | if ( prop->value.l < 0 ) |
1597 | FT_TRACE0(( "pcf_load_font: negative average width\n" )); |
1598 | #endif |
1599 | if ( ( FT_ABS( prop->value.l ) > 0x7FFFL * 10 - 5 ) ) |
1600 | { |
1601 | bsize->width = 0x7FFF; |
1602 | FT_TRACE0(( "pcf_load_font: clamping average width to value %d\n" , |
1603 | bsize->width )); |
1604 | } |
1605 | else |
1606 | bsize->width = FT_ABS( (FT_Short)( ( prop->value.l + 5 ) / 10 ) ); |
1607 | } |
1608 | else |
1609 | { |
1610 | /* this is a heuristical value */ |
1611 | bsize->width = ( bsize->height * 2 + 1 ) / 3; |
1612 | } |
1613 | |
1614 | prop = pcf_find_property( face, "POINT_SIZE" ); |
1615 | if ( prop ) |
1616 | { |
1617 | #ifdef FT_DEBUG_LEVEL_TRACE |
1618 | if ( prop->value.l < 0 ) |
1619 | FT_TRACE0(( "pcf_load_font: negative point size\n" )); |
1620 | #endif |
1621 | /* convert from 722.7 decipoints to 72 points per inch */ |
1622 | if ( FT_ABS( prop->value.l ) > 0x504C2L ) /* 0x7FFF * 72270/7200 */ |
1623 | { |
1624 | bsize->size = 0x7FFF; |
1625 | FT_TRACE0(( "pcf_load_font: clamping point size to value %ld\n" , |
1626 | bsize->size )); |
1627 | } |
1628 | else |
1629 | bsize->size = FT_MulDiv( FT_ABS( prop->value.l ), |
1630 | 64 * 7200, |
1631 | 72270L ); |
1632 | } |
1633 | |
1634 | prop = pcf_find_property( face, "PIXEL_SIZE" ); |
1635 | if ( prop ) |
1636 | { |
1637 | #ifdef FT_DEBUG_LEVEL_TRACE |
1638 | if ( prop->value.l < 0 ) |
1639 | FT_TRACE0(( "pcf_load_font: negative pixel size\n" )); |
1640 | #endif |
1641 | if ( FT_ABS( prop->value.l ) > 0x7FFF ) |
1642 | { |
1643 | bsize->y_ppem = 0x7FFF << 6; |
1644 | FT_TRACE0(( "pcf_load_font: clamping pixel size to value %ld\n" , |
1645 | bsize->y_ppem )); |
1646 | } |
1647 | else |
1648 | bsize->y_ppem = FT_ABS( (FT_Short)prop->value.l ) << 6; |
1649 | } |
1650 | |
1651 | prop = pcf_find_property( face, "RESOLUTION_X" ); |
1652 | if ( prop ) |
1653 | { |
1654 | #ifdef FT_DEBUG_LEVEL_TRACE |
1655 | if ( prop->value.l < 0 ) |
1656 | FT_TRACE0(( "pcf_load_font: negative X resolution\n" )); |
1657 | #endif |
1658 | if ( FT_ABS( prop->value.l ) > 0x7FFF ) |
1659 | { |
1660 | resolution_x = 0x7FFF; |
1661 | FT_TRACE0(( "pcf_load_font: clamping X resolution to value %d\n" , |
1662 | resolution_x )); |
1663 | } |
1664 | else |
1665 | resolution_x = FT_ABS( (FT_Short)prop->value.l ); |
1666 | } |
1667 | |
1668 | prop = pcf_find_property( face, "RESOLUTION_Y" ); |
1669 | if ( prop ) |
1670 | { |
1671 | #ifdef FT_DEBUG_LEVEL_TRACE |
1672 | if ( prop->value.l < 0 ) |
1673 | FT_TRACE0(( "pcf_load_font: negative Y resolution\n" )); |
1674 | #endif |
1675 | if ( FT_ABS( prop->value.l ) > 0x7FFF ) |
1676 | { |
1677 | resolution_y = 0x7FFF; |
1678 | FT_TRACE0(( "pcf_load_font: clamping Y resolution to value %d\n" , |
1679 | resolution_y )); |
1680 | } |
1681 | else |
1682 | resolution_y = FT_ABS( (FT_Short)prop->value.l ); |
1683 | } |
1684 | |
1685 | if ( bsize->y_ppem == 0 ) |
1686 | { |
1687 | bsize->y_ppem = bsize->size; |
1688 | if ( resolution_y ) |
1689 | bsize->y_ppem = FT_MulDiv( bsize->y_ppem, resolution_y, 72 ); |
1690 | } |
1691 | if ( resolution_x && resolution_y ) |
1692 | bsize->x_ppem = FT_MulDiv( bsize->y_ppem, |
1693 | resolution_x, |
1694 | resolution_y ); |
1695 | else |
1696 | bsize->x_ppem = bsize->y_ppem; |
1697 | } |
1698 | |
1699 | /* set up charset */ |
1700 | { |
1701 | PCF_Property charset_registry, charset_encoding; |
1702 | |
1703 | |
1704 | charset_registry = pcf_find_property( face, "CHARSET_REGISTRY" ); |
1705 | charset_encoding = pcf_find_property( face, "CHARSET_ENCODING" ); |
1706 | |
1707 | if ( charset_registry && charset_registry->isString && |
1708 | charset_encoding && charset_encoding->isString ) |
1709 | { |
1710 | if ( FT_STRDUP( face->charset_encoding, |
1711 | charset_encoding->value.atom ) || |
1712 | FT_STRDUP( face->charset_registry, |
1713 | charset_registry->value.atom ) ) |
1714 | goto Exit; |
1715 | } |
1716 | } |
1717 | } |
1718 | |
1719 | Exit: |
1720 | if ( error ) |
1721 | { |
1722 | /* This is done to respect the behaviour of the original */ |
1723 | /* PCF font driver. */ |
1724 | error = FT_THROW( Invalid_File_Format ); |
1725 | } |
1726 | |
1727 | return error; |
1728 | } |
1729 | |
1730 | |
1731 | /* END */ |
1732 | |