1 | /* |
2 | * Copyright 2000 Computing Research Labs, New Mexico State University |
3 | * Copyright 2001-2014 |
4 | * Francesco Zappa Nardelli |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a |
7 | * copy of this software and associated documentation files (the "Software"), |
8 | * to deal in the Software without restriction, including without limitation |
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
10 | * and/or sell copies of the Software, and to permit persons to whom the |
11 | * Software is furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in |
14 | * all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY |
20 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT |
21 | * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR |
22 | * THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
23 | */ |
24 | |
25 | /************************************************************************** |
26 | * |
27 | * This file is based on bdf.c,v 1.22 2000/03/16 20:08:50 |
28 | * |
29 | * taken from Mark Leisher's xmbdfed package |
30 | * |
31 | */ |
32 | |
33 | |
34 | |
35 | #include <freetype/freetype.h> |
36 | #include <freetype/internal/ftdebug.h> |
37 | #include <freetype/internal/ftstream.h> |
38 | #include <freetype/internal/ftobjs.h> |
39 | |
40 | #include "bdf.h" |
41 | #include "bdferror.h" |
42 | |
43 | |
44 | /************************************************************************** |
45 | * |
46 | * The macro FT_COMPONENT is used in trace mode. It is an implicit |
47 | * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log |
48 | * messages during execution. |
49 | */ |
50 | #undef FT_COMPONENT |
51 | #define FT_COMPONENT bdflib |
52 | |
53 | |
54 | #define BUFSIZE 128 |
55 | |
56 | |
57 | /************************************************************************** |
58 | * |
59 | * Default BDF font options. |
60 | * |
61 | */ |
62 | |
63 | |
64 | static const bdf_options_t bdf_opts_ = |
65 | { |
66 | 1, /* Correct metrics. */ |
67 | 1, /* Preserve unencoded glyphs. */ |
68 | 0, /* Preserve comments. */ |
69 | BDF_PROPORTIONAL /* Default spacing. */ |
70 | }; |
71 | |
72 | |
73 | /************************************************************************** |
74 | * |
75 | * Builtin BDF font properties. |
76 | * |
77 | */ |
78 | |
79 | /* List of most properties that might appear in a font. Doesn't include */ |
80 | /* the RAW_* and AXIS_* properties in X11R6 polymorphic fonts. */ |
81 | |
82 | static const bdf_property_t bdf_properties_[] = |
83 | { |
84 | { "ADD_STYLE_NAME" , BDF_ATOM, 1, { 0 } }, |
85 | { "AVERAGE_WIDTH" , BDF_INTEGER, 1, { 0 } }, |
86 | { "AVG_CAPITAL_WIDTH" , BDF_INTEGER, 1, { 0 } }, |
87 | { "AVG_LOWERCASE_WIDTH" , BDF_INTEGER, 1, { 0 } }, |
88 | { "CAP_HEIGHT" , BDF_INTEGER, 1, { 0 } }, |
89 | { "CHARSET_COLLECTIONS" , BDF_ATOM, 1, { 0 } }, |
90 | { "CHARSET_ENCODING" , BDF_ATOM, 1, { 0 } }, |
91 | { "CHARSET_REGISTRY" , BDF_ATOM, 1, { 0 } }, |
92 | { "COMMENT" , BDF_ATOM, 1, { 0 } }, |
93 | { "COPYRIGHT" , BDF_ATOM, 1, { 0 } }, |
94 | { "DEFAULT_CHAR" , BDF_CARDINAL, 1, { 0 } }, |
95 | { "DESTINATION" , BDF_CARDINAL, 1, { 0 } }, |
96 | { "DEVICE_FONT_NAME" , BDF_ATOM, 1, { 0 } }, |
97 | { "END_SPACE" , BDF_INTEGER, 1, { 0 } }, |
98 | { "FACE_NAME" , BDF_ATOM, 1, { 0 } }, |
99 | { "FAMILY_NAME" , BDF_ATOM, 1, { 0 } }, |
100 | { "FIGURE_WIDTH" , BDF_INTEGER, 1, { 0 } }, |
101 | { "FONT" , BDF_ATOM, 1, { 0 } }, |
102 | { "FONTNAME_REGISTRY" , BDF_ATOM, 1, { 0 } }, |
103 | { "FONT_ASCENT" , BDF_INTEGER, 1, { 0 } }, |
104 | { "FONT_DESCENT" , BDF_INTEGER, 1, { 0 } }, |
105 | { "FOUNDRY" , BDF_ATOM, 1, { 0 } }, |
106 | { "FULL_NAME" , BDF_ATOM, 1, { 0 } }, |
107 | { "ITALIC_ANGLE" , BDF_INTEGER, 1, { 0 } }, |
108 | { "MAX_SPACE" , BDF_INTEGER, 1, { 0 } }, |
109 | { "MIN_SPACE" , BDF_INTEGER, 1, { 0 } }, |
110 | { "NORM_SPACE" , BDF_INTEGER, 1, { 0 } }, |
111 | { "NOTICE" , BDF_ATOM, 1, { 0 } }, |
112 | { "PIXEL_SIZE" , BDF_INTEGER, 1, { 0 } }, |
113 | { "POINT_SIZE" , BDF_INTEGER, 1, { 0 } }, |
114 | { "QUAD_WIDTH" , BDF_INTEGER, 1, { 0 } }, |
115 | { "RAW_ASCENT" , BDF_INTEGER, 1, { 0 } }, |
116 | { "RAW_AVERAGE_WIDTH" , BDF_INTEGER, 1, { 0 } }, |
117 | { "RAW_AVG_CAPITAL_WIDTH" , BDF_INTEGER, 1, { 0 } }, |
118 | { "RAW_AVG_LOWERCASE_WIDTH" , BDF_INTEGER, 1, { 0 } }, |
119 | { "RAW_CAP_HEIGHT" , BDF_INTEGER, 1, { 0 } }, |
120 | { "RAW_DESCENT" , BDF_INTEGER, 1, { 0 } }, |
121 | { "RAW_END_SPACE" , BDF_INTEGER, 1, { 0 } }, |
122 | { "RAW_FIGURE_WIDTH" , BDF_INTEGER, 1, { 0 } }, |
123 | { "RAW_MAX_SPACE" , BDF_INTEGER, 1, { 0 } }, |
124 | { "RAW_MIN_SPACE" , BDF_INTEGER, 1, { 0 } }, |
125 | { "RAW_NORM_SPACE" , BDF_INTEGER, 1, { 0 } }, |
126 | { "RAW_PIXEL_SIZE" , BDF_INTEGER, 1, { 0 } }, |
127 | { "RAW_POINT_SIZE" , BDF_INTEGER, 1, { 0 } }, |
128 | { "RAW_PIXELSIZE" , BDF_INTEGER, 1, { 0 } }, |
129 | { "RAW_POINTSIZE" , BDF_INTEGER, 1, { 0 } }, |
130 | { "RAW_QUAD_WIDTH" , BDF_INTEGER, 1, { 0 } }, |
131 | { "RAW_SMALL_CAP_SIZE" , BDF_INTEGER, 1, { 0 } }, |
132 | { "RAW_STRIKEOUT_ASCENT" , BDF_INTEGER, 1, { 0 } }, |
133 | { "RAW_STRIKEOUT_DESCENT" , BDF_INTEGER, 1, { 0 } }, |
134 | { "RAW_SUBSCRIPT_SIZE" , BDF_INTEGER, 1, { 0 } }, |
135 | { "RAW_SUBSCRIPT_X" , BDF_INTEGER, 1, { 0 } }, |
136 | { "RAW_SUBSCRIPT_Y" , BDF_INTEGER, 1, { 0 } }, |
137 | { "RAW_SUPERSCRIPT_SIZE" , BDF_INTEGER, 1, { 0 } }, |
138 | { "RAW_SUPERSCRIPT_X" , BDF_INTEGER, 1, { 0 } }, |
139 | { "RAW_SUPERSCRIPT_Y" , BDF_INTEGER, 1, { 0 } }, |
140 | { "RAW_UNDERLINE_POSITION" , BDF_INTEGER, 1, { 0 } }, |
141 | { "RAW_UNDERLINE_THICKNESS" , BDF_INTEGER, 1, { 0 } }, |
142 | { "RAW_X_HEIGHT" , BDF_INTEGER, 1, { 0 } }, |
143 | { "RELATIVE_SETWIDTH" , BDF_CARDINAL, 1, { 0 } }, |
144 | { "RELATIVE_WEIGHT" , BDF_CARDINAL, 1, { 0 } }, |
145 | { "RESOLUTION" , BDF_INTEGER, 1, { 0 } }, |
146 | { "RESOLUTION_X" , BDF_CARDINAL, 1, { 0 } }, |
147 | { "RESOLUTION_Y" , BDF_CARDINAL, 1, { 0 } }, |
148 | { "SETWIDTH_NAME" , BDF_ATOM, 1, { 0 } }, |
149 | { "SLANT" , BDF_ATOM, 1, { 0 } }, |
150 | { "SMALL_CAP_SIZE" , BDF_INTEGER, 1, { 0 } }, |
151 | { "SPACING" , BDF_ATOM, 1, { 0 } }, |
152 | { "STRIKEOUT_ASCENT" , BDF_INTEGER, 1, { 0 } }, |
153 | { "STRIKEOUT_DESCENT" , BDF_INTEGER, 1, { 0 } }, |
154 | { "SUBSCRIPT_SIZE" , BDF_INTEGER, 1, { 0 } }, |
155 | { "SUBSCRIPT_X" , BDF_INTEGER, 1, { 0 } }, |
156 | { "SUBSCRIPT_Y" , BDF_INTEGER, 1, { 0 } }, |
157 | { "SUPERSCRIPT_SIZE" , BDF_INTEGER, 1, { 0 } }, |
158 | { "SUPERSCRIPT_X" , BDF_INTEGER, 1, { 0 } }, |
159 | { "SUPERSCRIPT_Y" , BDF_INTEGER, 1, { 0 } }, |
160 | { "UNDERLINE_POSITION" , BDF_INTEGER, 1, { 0 } }, |
161 | { "UNDERLINE_THICKNESS" , BDF_INTEGER, 1, { 0 } }, |
162 | { "WEIGHT" , BDF_CARDINAL, 1, { 0 } }, |
163 | { "WEIGHT_NAME" , BDF_ATOM, 1, { 0 } }, |
164 | { "X_HEIGHT" , BDF_INTEGER, 1, { 0 } }, |
165 | { "_MULE_BASELINE_OFFSET" , BDF_INTEGER, 1, { 0 } }, |
166 | { "_MULE_RELATIVE_COMPOSE" , BDF_INTEGER, 1, { 0 } }, |
167 | }; |
168 | |
169 | static const unsigned long |
170 | num_bdf_properties_ = sizeof ( bdf_properties_ ) / |
171 | sizeof ( bdf_properties_[0] ); |
172 | |
173 | |
174 | /* An auxiliary macro to parse properties, to be used in conditionals. */ |
175 | /* It behaves like `strncmp' but also tests the following character */ |
176 | /* whether it is a whitespace or null. */ |
177 | /* `property' is a constant string of length `n' to compare with. */ |
178 | #define _bdf_strncmp( name, property, n ) \ |
179 | ( ft_strncmp( name, property, n ) || \ |
180 | !( name[n] == ' ' || \ |
181 | name[n] == '\0' || \ |
182 | name[n] == '\n' || \ |
183 | name[n] == '\r' || \ |
184 | name[n] == '\t' ) ) |
185 | |
186 | /* Auto correction messages. */ |
187 | #define ACMSG1 "FONT_ASCENT property missing. " \ |
188 | "Added `FONT_ASCENT %hd'.\n" |
189 | #define ACMSG2 "FONT_DESCENT property missing. " \ |
190 | "Added `FONT_DESCENT %hd'.\n" |
191 | #define ACMSG3 "Font width != actual width. Old: %d New: %d.\n" |
192 | #define ACMSG4 "Font left bearing != actual left bearing. " \ |
193 | "Old: %hd New: %hd.\n" |
194 | #define ACMSG5 "Font ascent != actual ascent. Old: %hd New: %hd.\n" |
195 | #define ACMSG6 "Font descent != actual descent. Old: %d New: %d.\n" |
196 | #define ACMSG7 "Font height != actual height. Old: %d New: %d.\n" |
197 | #define ACMSG8 "Glyph scalable width (SWIDTH) adjustments made.\n" |
198 | #define ACMSG9 "SWIDTH field missing at line %ld. Set automatically.\n" |
199 | #define ACMSG10 "DWIDTH field missing at line %ld. Set to glyph width.\n" |
200 | #define ACMSG11 "SIZE bits per pixel field adjusted to %hd.\n" |
201 | #define ACMSG13 "Glyph %lu extra rows removed.\n" |
202 | #define ACMSG14 "Glyph %lu extra columns removed.\n" |
203 | #define ACMSG15 "Incorrect glyph count: %ld indicated but %ld found.\n" |
204 | #define ACMSG16 "Glyph %lu missing columns padded with zero bits.\n" |
205 | #define ACMSG17 "Adjusting number of glyphs to %ld.\n" |
206 | |
207 | /* Error messages. */ |
208 | #define ERRMSG1 "[line %ld] Missing `%s' line.\n" |
209 | #define ERRMSG2 "[line %ld] Font header corrupted or missing fields.\n" |
210 | #define ERRMSG3 "[line %ld] Font glyphs corrupted or missing fields.\n" |
211 | #define ERRMSG4 "[line %ld] BBX too big.\n" |
212 | #define ERRMSG5 "[line %ld] `%s' value too big.\n" |
213 | #define ERRMSG6 "[line %ld] Input line too long.\n" |
214 | #define ERRMSG7 "[line %ld] Font name too long.\n" |
215 | #define ERRMSG8 "[line %ld] Invalid `%s' value.\n" |
216 | #define ERRMSG9 "[line %ld] Invalid keyword.\n" |
217 | |
218 | /* Debug messages. */ |
219 | #define DBGMSG1 " [%6ld] %s" /* no \n */ |
220 | #define DBGMSG2 " (0x%lX)\n" |
221 | |
222 | |
223 | /************************************************************************** |
224 | * |
225 | * Utility types and functions. |
226 | * |
227 | */ |
228 | |
229 | |
230 | /* Function type for parsing lines of a BDF font. */ |
231 | |
232 | typedef FT_Error |
233 | (*bdf_line_func_t_)( char* line, |
234 | unsigned long linelen, |
235 | unsigned long lineno, |
236 | void* call_data, |
237 | void* client_data ); |
238 | |
239 | |
240 | /* List structure for splitting lines into fields. */ |
241 | |
242 | typedef struct bdf_list_t__ |
243 | { |
244 | char** field; |
245 | unsigned long size; |
246 | unsigned long used; |
247 | FT_Memory memory; |
248 | |
249 | } bdf_list_t_; |
250 | |
251 | |
252 | /* Structure used while loading BDF fonts. */ |
253 | |
254 | typedef struct bdf_parse_t__ |
255 | { |
256 | unsigned long flags; |
257 | unsigned long cnt; |
258 | unsigned long row; |
259 | |
260 | short minlb; |
261 | short maxlb; |
262 | short maxrb; |
263 | short maxas; |
264 | short maxds; |
265 | |
266 | short rbearing; |
267 | |
268 | char* glyph_name; |
269 | long glyph_enc; |
270 | |
271 | bdf_font_t* font; |
272 | bdf_options_t* opts; |
273 | |
274 | bdf_list_t_ list; |
275 | |
276 | FT_Memory memory; |
277 | unsigned long size; /* the stream size */ |
278 | |
279 | } bdf_parse_t_; |
280 | |
281 | |
282 | #define setsbit( m, cc ) \ |
283 | ( m[(FT_Byte)(cc) >> 3] |= (FT_Byte)( 1 << ( (cc) & 7 ) ) ) |
284 | #define sbitset( m, cc ) \ |
285 | ( m[(FT_Byte)(cc) >> 3] & ( 1 << ( (cc) & 7 ) ) ) |
286 | |
287 | |
288 | static void |
289 | bdf_list_init_( bdf_list_t_* list, |
290 | FT_Memory memory ) |
291 | { |
292 | FT_ZERO( list ); |
293 | list->memory = memory; |
294 | } |
295 | |
296 | |
297 | static void |
298 | bdf_list_done_( bdf_list_t_* list ) |
299 | { |
300 | FT_Memory memory = list->memory; |
301 | |
302 | |
303 | if ( memory ) |
304 | { |
305 | FT_FREE( list->field ); |
306 | FT_ZERO( list ); |
307 | } |
308 | } |
309 | |
310 | |
311 | static FT_Error |
312 | bdf_list_ensure_( bdf_list_t_* list, |
313 | unsigned long num_items ) /* same as bdf_list_t_.used */ |
314 | { |
315 | FT_Error error = FT_Err_Ok; |
316 | |
317 | |
318 | if ( num_items > list->size ) |
319 | { |
320 | unsigned long oldsize = list->size; /* same as bdf_list_t_.size */ |
321 | unsigned long newsize = oldsize + ( oldsize >> 1 ) + 5; |
322 | unsigned long bigsize = (unsigned long)( FT_INT_MAX / sizeof ( char* ) ); |
323 | FT_Memory memory = list->memory; |
324 | |
325 | |
326 | if ( oldsize == bigsize ) |
327 | { |
328 | error = FT_THROW( Out_Of_Memory ); |
329 | goto Exit; |
330 | } |
331 | else if ( newsize < oldsize || newsize > bigsize ) |
332 | newsize = bigsize; |
333 | |
334 | if ( FT_QRENEW_ARRAY( list->field, oldsize, newsize ) ) |
335 | goto Exit; |
336 | |
337 | list->size = newsize; |
338 | } |
339 | |
340 | Exit: |
341 | return error; |
342 | } |
343 | |
344 | |
345 | static void |
346 | bdf_list_shift_( bdf_list_t_* list, |
347 | unsigned long n ) |
348 | { |
349 | unsigned long i, u; |
350 | |
351 | |
352 | if ( list == NULL || list->used == 0 || n == 0 ) |
353 | return; |
354 | |
355 | if ( n >= list->used ) |
356 | { |
357 | list->used = 0; |
358 | return; |
359 | } |
360 | |
361 | for ( u = n, i = 0; u < list->used; i++, u++ ) |
362 | list->field[i] = list->field[u]; |
363 | list->used -= n; |
364 | } |
365 | |
366 | |
367 | /* An empty string for empty fields. */ |
368 | |
369 | static const char empty[] = "" ; /* XXX eliminate this */ |
370 | |
371 | |
372 | static char * |
373 | bdf_list_join_( bdf_list_t_* list, |
374 | int c, |
375 | unsigned long *alen ) |
376 | { |
377 | unsigned long i, j; |
378 | char* dp; |
379 | |
380 | |
381 | *alen = 0; |
382 | |
383 | if ( list == NULL || list->used == 0 ) |
384 | return NULL; |
385 | |
386 | dp = list->field[0]; |
387 | for ( i = j = 0; i < list->used; i++ ) |
388 | { |
389 | char* fp = list->field[i]; |
390 | |
391 | |
392 | while ( *fp ) |
393 | dp[j++] = *fp++; |
394 | |
395 | if ( i + 1 < list->used ) |
396 | dp[j++] = (char)c; |
397 | } |
398 | if ( dp != empty ) |
399 | dp[j] = 0; |
400 | |
401 | *alen = j; |
402 | return dp; |
403 | } |
404 | |
405 | |
406 | /* The code below ensures that we have at least 4 + 1 `field' */ |
407 | /* elements in `list' (which are possibly NULL) so that we */ |
408 | /* don't have to check the number of fields in most cases. */ |
409 | |
410 | static FT_Error |
411 | bdf_list_split_( bdf_list_t_* list, |
412 | const char* separators, |
413 | char* line, |
414 | unsigned long linelen ) |
415 | { |
416 | unsigned long final_empty; |
417 | int mult; |
418 | const char *sp, *end; |
419 | char *ep; |
420 | char seps[32]; |
421 | FT_Error error = FT_Err_Ok; |
422 | |
423 | |
424 | /* Initialize the list. */ |
425 | list->used = 0; |
426 | if ( list->size ) |
427 | { |
428 | list->field[0] = (char*)empty; |
429 | list->field[1] = (char*)empty; |
430 | list->field[2] = (char*)empty; |
431 | list->field[3] = (char*)empty; |
432 | list->field[4] = (char*)empty; |
433 | } |
434 | |
435 | /* If the line is empty, then simply return. */ |
436 | if ( linelen == 0 || line[0] == 0 ) |
437 | goto Exit; |
438 | |
439 | /* In the original code, if the `separators' parameter is NULL or */ |
440 | /* empty, the list is split into individual bytes. We don't need */ |
441 | /* this, so an error is signaled. */ |
442 | if ( separators == NULL || *separators == 0 ) |
443 | { |
444 | error = FT_THROW( Invalid_Argument ); |
445 | goto Exit; |
446 | } |
447 | |
448 | /* Prepare the separator bitmap. */ |
449 | FT_MEM_ZERO( seps, 32 ); |
450 | |
451 | /* If the very last character of the separator string is a plus, then */ |
452 | /* set the `mult' flag to indicate that multiple separators should be */ |
453 | /* collapsed into one. */ |
454 | for ( mult = 0, sp = separators; sp && *sp; sp++ ) |
455 | { |
456 | if ( *sp == '+' && *( sp + 1 ) == 0 ) |
457 | mult = 1; |
458 | else |
459 | setsbit( seps, *sp ); |
460 | } |
461 | |
462 | /* Break the line up into fields. */ |
463 | for ( final_empty = 0, sp = ep = line, end = sp + linelen; |
464 | sp < end && *sp; ) |
465 | { |
466 | /* Collect everything that is not a separator. */ |
467 | for ( ; *ep && !sbitset( seps, *ep ); ep++ ) |
468 | ; |
469 | |
470 | /* Resize the list if necessary. */ |
471 | if ( list->used == list->size ) |
472 | { |
473 | error = bdf_list_ensure_( list, list->used + 1 ); |
474 | if ( error ) |
475 | goto Exit; |
476 | } |
477 | |
478 | /* Assign the field appropriately. */ |
479 | list->field[list->used++] = ( ep > sp ) ? (char*)sp : (char*)empty; |
480 | |
481 | sp = ep; |
482 | |
483 | if ( mult ) |
484 | { |
485 | /* If multiple separators should be collapsed, do it now by */ |
486 | /* setting all the separator characters to 0. */ |
487 | for ( ; *ep && sbitset( seps, *ep ); ep++ ) |
488 | *ep = 0; |
489 | } |
490 | else if ( *ep != 0 ) |
491 | /* Don't collapse multiple separators by making them 0, so just */ |
492 | /* make the one encountered 0. */ |
493 | *ep++ = 0; |
494 | |
495 | final_empty = ( ep > sp && *ep == 0 ); |
496 | sp = ep; |
497 | } |
498 | |
499 | /* Finally, NULL-terminate the list. */ |
500 | if ( list->used + final_empty >= list->size ) |
501 | { |
502 | error = bdf_list_ensure_( list, list->used + final_empty + 1 ); |
503 | if ( error ) |
504 | goto Exit; |
505 | } |
506 | |
507 | if ( final_empty ) |
508 | list->field[list->used++] = (char*)empty; |
509 | |
510 | list->field[list->used] = NULL; |
511 | |
512 | Exit: |
513 | return error; |
514 | } |
515 | |
516 | |
517 | #define NO_SKIP 256 /* this value cannot be stored in a 'char' */ |
518 | |
519 | |
520 | static FT_Error |
521 | bdf_readstream_( FT_Stream stream, |
522 | bdf_line_func_t_ callback, |
523 | void* client_data, |
524 | unsigned long *lno ) |
525 | { |
526 | bdf_line_func_t_ cb; |
527 | unsigned long lineno, buf_size; |
528 | int refill, hold, to_skip; |
529 | ptrdiff_t bytes, start, end, cursor, avail; |
530 | char* buf = NULL; |
531 | FT_Memory memory = stream->memory; |
532 | FT_Error error = FT_Err_Ok; |
533 | |
534 | |
535 | if ( callback == NULL ) |
536 | { |
537 | error = FT_THROW( Invalid_Argument ); |
538 | goto Exit; |
539 | } |
540 | |
541 | /* initial size and allocation of the input buffer */ |
542 | buf_size = 1024; |
543 | |
544 | if ( FT_QALLOC( buf, buf_size ) ) |
545 | goto Exit; |
546 | |
547 | cb = callback; |
548 | lineno = 1; |
549 | buf[0] = 0; |
550 | start = 0; |
551 | avail = 0; |
552 | cursor = 0; |
553 | refill = 1; |
554 | to_skip = NO_SKIP; |
555 | bytes = 0; /* make compiler happy */ |
556 | |
557 | for (;;) |
558 | { |
559 | if ( refill ) |
560 | { |
561 | bytes = (ptrdiff_t)FT_Stream_TryRead( |
562 | stream, (FT_Byte*)buf + cursor, |
563 | buf_size - (unsigned long)cursor ); |
564 | avail = cursor + bytes; |
565 | cursor = 0; |
566 | refill = 0; |
567 | } |
568 | |
569 | end = start; |
570 | |
571 | /* should we skip an optional character like \n or \r? */ |
572 | if ( start < avail && buf[start] == to_skip ) |
573 | { |
574 | start += 1; |
575 | to_skip = NO_SKIP; |
576 | continue; |
577 | } |
578 | |
579 | /* try to find the end of the line */ |
580 | while ( end < avail && buf[end] != '\n' && buf[end] != '\r' ) |
581 | end++; |
582 | |
583 | /* if we hit the end of the buffer, try shifting its content */ |
584 | /* or even resizing it */ |
585 | if ( end >= avail ) |
586 | { |
587 | if ( bytes == 0 ) |
588 | { |
589 | /* last line in file doesn't end in \r or \n; */ |
590 | /* ignore it then exit */ |
591 | if ( lineno == 1 ) |
592 | error = FT_THROW( Missing_Startfont_Field ); |
593 | break; |
594 | } |
595 | |
596 | if ( start == 0 ) |
597 | { |
598 | /* this line is definitely too long; try resizing the input */ |
599 | /* buffer a bit to handle it. */ |
600 | FT_ULong new_size; |
601 | |
602 | |
603 | if ( buf_size >= 65536UL ) /* limit ourselves to 64KByte */ |
604 | { |
605 | if ( lineno == 1 ) |
606 | error = FT_THROW( Missing_Startfont_Field ); |
607 | else |
608 | { |
609 | FT_ERROR(( "bdf_readstream_: " ERRMSG6, lineno )); |
610 | error = FT_THROW( Invalid_Argument ); |
611 | } |
612 | goto Exit; |
613 | } |
614 | |
615 | new_size = buf_size * 2; |
616 | if ( FT_QREALLOC( buf, buf_size, new_size ) ) |
617 | goto Exit; |
618 | |
619 | cursor = avail; |
620 | buf_size = new_size; |
621 | } |
622 | else |
623 | { |
624 | bytes = avail - start; |
625 | |
626 | FT_MEM_MOVE( buf, buf + start, bytes ); |
627 | |
628 | cursor = bytes; |
629 | start = 0; |
630 | } |
631 | refill = 1; |
632 | continue; |
633 | } |
634 | |
635 | /* Temporarily NUL-terminate the line. */ |
636 | hold = buf[end]; |
637 | buf[end] = 0; |
638 | |
639 | /* XXX: Use encoding independent value for 0x1A */ |
640 | if ( buf[start] != '#' && buf[start] != 0x1A && end > start ) |
641 | { |
642 | error = (*cb)( buf + start, (unsigned long)( end - start ), lineno, |
643 | (void*)&cb, client_data ); |
644 | /* Redo if we have encountered CHARS without properties. */ |
645 | if ( error == -1 ) |
646 | error = (*cb)( buf + start, (unsigned long)( end - start ), lineno, |
647 | (void*)&cb, client_data ); |
648 | if ( error ) |
649 | break; |
650 | } |
651 | |
652 | lineno += 1; |
653 | buf[end] = (char)hold; |
654 | start = end + 1; |
655 | |
656 | if ( hold == '\n' ) |
657 | to_skip = '\r'; |
658 | else if ( hold == '\r' ) |
659 | to_skip = '\n'; |
660 | else |
661 | to_skip = NO_SKIP; |
662 | } |
663 | |
664 | *lno = lineno; |
665 | |
666 | Exit: |
667 | FT_FREE( buf ); |
668 | return error; |
669 | } |
670 | |
671 | |
672 | /* XXX: make this work with EBCDIC also */ |
673 | |
674 | static const unsigned char a2i[128] = |
675 | { |
676 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
677 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
678 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
679 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
680 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00, |
681 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, |
682 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
683 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
684 | 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, |
685 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
686 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
687 | }; |
688 | |
689 | static const unsigned char ddigits[32] = |
690 | { |
691 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, |
692 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
693 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
694 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
695 | }; |
696 | |
697 | static const unsigned char hdigits[32] = |
698 | { |
699 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, |
700 | 0x7E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, |
701 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
702 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
703 | }; |
704 | |
705 | |
706 | /* Routine to convert a decimal ASCII string to an unsigned long integer. */ |
707 | static unsigned long |
708 | bdf_atoul_( const char* s ) |
709 | { |
710 | unsigned long v; |
711 | |
712 | |
713 | if ( s == NULL || *s == 0 ) |
714 | return 0; |
715 | |
716 | for ( v = 0; sbitset( ddigits, *s ); s++ ) |
717 | { |
718 | if ( v < ( FT_ULONG_MAX - 9 ) / 10 ) |
719 | v = v * 10 + a2i[(int)*s]; |
720 | else |
721 | { |
722 | v = FT_ULONG_MAX; |
723 | break; |
724 | } |
725 | } |
726 | |
727 | return v; |
728 | } |
729 | |
730 | |
731 | /* Routine to convert a decimal ASCII string to a signed long integer. */ |
732 | static long |
733 | bdf_atol_( const char* s ) |
734 | { |
735 | long v, neg; |
736 | |
737 | |
738 | if ( s == NULL || *s == 0 ) |
739 | return 0; |
740 | |
741 | /* Check for a minus sign. */ |
742 | neg = 0; |
743 | if ( *s == '-' ) |
744 | { |
745 | s++; |
746 | neg = 1; |
747 | } |
748 | |
749 | for ( v = 0; sbitset( ddigits, *s ); s++ ) |
750 | { |
751 | if ( v < ( FT_LONG_MAX - 9 ) / 10 ) |
752 | v = v * 10 + a2i[(int)*s]; |
753 | else |
754 | { |
755 | v = FT_LONG_MAX; |
756 | break; |
757 | } |
758 | } |
759 | |
760 | return ( !neg ) ? v : -v; |
761 | } |
762 | |
763 | |
764 | /* Routine to convert a decimal ASCII string to an unsigned short integer. */ |
765 | static unsigned short |
766 | bdf_atous_( const char* s ) |
767 | { |
768 | unsigned short v; |
769 | |
770 | |
771 | if ( s == NULL || *s == 0 ) |
772 | return 0; |
773 | |
774 | for ( v = 0; sbitset( ddigits, *s ); s++ ) |
775 | { |
776 | if ( v < ( FT_USHORT_MAX - 9 ) / 10 ) |
777 | v = (unsigned short)( v * 10 + a2i[(int)*s] ); |
778 | else |
779 | { |
780 | v = FT_USHORT_MAX; |
781 | break; |
782 | } |
783 | } |
784 | |
785 | return v; |
786 | } |
787 | |
788 | |
789 | /* Routine to convert a decimal ASCII string to a signed short integer. */ |
790 | static short |
791 | bdf_atos_( const char* s ) |
792 | { |
793 | short v, neg; |
794 | |
795 | |
796 | if ( s == NULL || *s == 0 ) |
797 | return 0; |
798 | |
799 | /* Check for a minus. */ |
800 | neg = 0; |
801 | if ( *s == '-' ) |
802 | { |
803 | s++; |
804 | neg = 1; |
805 | } |
806 | |
807 | for ( v = 0; sbitset( ddigits, *s ); s++ ) |
808 | { |
809 | if ( v < ( SHRT_MAX - 9 ) / 10 ) |
810 | v = (short)( v * 10 + a2i[(int)*s] ); |
811 | else |
812 | { |
813 | v = SHRT_MAX; |
814 | break; |
815 | } |
816 | } |
817 | |
818 | return (short)( ( !neg ) ? v : -v ); |
819 | } |
820 | |
821 | |
822 | /* Routine to compare two glyphs by encoding so they can be sorted. */ |
823 | FT_COMPARE_DEF( int ) |
824 | by_encoding( const void* a, |
825 | const void* b ) |
826 | { |
827 | bdf_glyph_t *c1, *c2; |
828 | |
829 | |
830 | c1 = (bdf_glyph_t *)a; |
831 | c2 = (bdf_glyph_t *)b; |
832 | |
833 | if ( c1->encoding < c2->encoding ) |
834 | return -1; |
835 | |
836 | if ( c1->encoding > c2->encoding ) |
837 | return 1; |
838 | |
839 | return 0; |
840 | } |
841 | |
842 | |
843 | static FT_Error |
844 | bdf_create_property( const char* name, |
845 | int format, |
846 | bdf_font_t* font ) |
847 | { |
848 | size_t n; |
849 | bdf_property_t* p; |
850 | FT_Memory memory = font->memory; |
851 | FT_Error error = FT_Err_Ok; |
852 | |
853 | |
854 | /* First check whether the property has */ |
855 | /* already been added or not. If it has, then */ |
856 | /* simply ignore it. */ |
857 | if ( ft_hash_str_lookup( name, &(font->proptbl) ) ) |
858 | goto Exit; |
859 | |
860 | if ( FT_QRENEW_ARRAY( font->user_props, |
861 | font->nuser_props, |
862 | font->nuser_props + 1 ) ) |
863 | goto Exit; |
864 | |
865 | p = font->user_props + font->nuser_props; |
866 | |
867 | n = ft_strlen( name ) + 1; |
868 | if ( n > FT_LONG_MAX ) |
869 | return FT_THROW( Invalid_Argument ); |
870 | |
871 | if ( FT_QALLOC( p->name, n ) ) |
872 | goto Exit; |
873 | |
874 | FT_MEM_COPY( (char *)p->name, name, n ); |
875 | |
876 | p->format = format; |
877 | p->builtin = 0; |
878 | p->value.atom = NULL; /* nothing is ever stored here */ |
879 | |
880 | n = num_bdf_properties_ + font->nuser_props; |
881 | |
882 | error = ft_hash_str_insert( p->name, n, &(font->proptbl), memory ); |
883 | if ( error ) |
884 | goto Exit; |
885 | |
886 | font->nuser_props++; |
887 | |
888 | Exit: |
889 | return error; |
890 | } |
891 | |
892 | |
893 | static bdf_property_t* |
894 | bdf_get_property( const char* name, |
895 | bdf_font_t* font ) |
896 | { |
897 | size_t* propid; |
898 | |
899 | |
900 | if ( name == NULL || *name == 0 ) |
901 | return NULL; |
902 | |
903 | if ( ( propid = ft_hash_str_lookup( name, &(font->proptbl) ) ) == NULL ) |
904 | return NULL; |
905 | |
906 | if ( *propid >= num_bdf_properties_ ) |
907 | return font->user_props + ( *propid - num_bdf_properties_ ); |
908 | |
909 | return (bdf_property_t*)bdf_properties_ + *propid; |
910 | } |
911 | |
912 | |
913 | /************************************************************************** |
914 | * |
915 | * BDF font file parsing flags and functions. |
916 | * |
917 | */ |
918 | |
919 | |
920 | /* Parse flags. */ |
921 | |
922 | #define BDF_START_ 0x0001U |
923 | #define BDF_FONT_NAME_ 0x0002U |
924 | #define BDF_SIZE_ 0x0004U |
925 | #define BDF_FONT_BBX_ 0x0008U |
926 | #define BDF_PROPS_ 0x0010U |
927 | #define BDF_GLYPHS_ 0x0020U |
928 | #define BDF_GLYPH_ 0x0040U |
929 | #define BDF_ENCODING_ 0x0080U |
930 | #define BDF_SWIDTH_ 0x0100U |
931 | #define BDF_DWIDTH_ 0x0200U |
932 | #define BDF_BBX_ 0x0400U |
933 | #define BDF_BITMAP_ 0x0800U |
934 | |
935 | #define BDF_SWIDTH_ADJ_ 0x1000U |
936 | |
937 | #define BDF_GLYPH_BITS_ ( BDF_GLYPH_ | \ |
938 | BDF_ENCODING_ | \ |
939 | BDF_SWIDTH_ | \ |
940 | BDF_DWIDTH_ | \ |
941 | BDF_BBX_ | \ |
942 | BDF_BITMAP_ ) |
943 | |
944 | #define BDF_GLYPH_WIDTH_CHECK_ 0x40000000UL |
945 | #define BDF_GLYPH_HEIGHT_CHECK_ 0x80000000UL |
946 | |
947 | |
948 | static FT_Error |
949 | ( bdf_font_t* font, |
950 | const char* , |
951 | unsigned long len ) |
952 | { |
953 | char* cp; |
954 | FT_Memory memory = font->memory; |
955 | FT_Error error = FT_Err_Ok; |
956 | |
957 | |
958 | if ( FT_QRENEW_ARRAY( font->comments, |
959 | font->comments_len, |
960 | font->comments_len + len + 1 ) ) |
961 | goto Exit; |
962 | |
963 | cp = font->comments + font->comments_len; |
964 | |
965 | FT_MEM_COPY( cp, comment, len ); |
966 | cp[len] = '\0'; |
967 | |
968 | font->comments_len += len + 1; |
969 | |
970 | Exit: |
971 | return error; |
972 | } |
973 | |
974 | |
975 | /* Set the spacing from the font name if it exists, or set it to the */ |
976 | /* default specified in the options. */ |
977 | static FT_Error |
978 | bdf_set_default_spacing_( bdf_font_t* font, |
979 | bdf_options_t* opts, |
980 | unsigned long lineno ) |
981 | { |
982 | size_t len; |
983 | char name[256]; |
984 | bdf_list_t_ list; |
985 | FT_Memory memory; |
986 | FT_Error error = FT_Err_Ok; |
987 | |
988 | FT_UNUSED( lineno ); /* only used in debug mode */ |
989 | |
990 | |
991 | if ( font == NULL || font->name == NULL || font->name[0] == 0 ) |
992 | { |
993 | error = FT_THROW( Invalid_Argument ); |
994 | goto Exit; |
995 | } |
996 | |
997 | memory = font->memory; |
998 | |
999 | bdf_list_init_( &list, memory ); |
1000 | |
1001 | font->spacing = opts->font_spacing; |
1002 | |
1003 | len = ft_strlen( font->name ) + 1; |
1004 | /* Limit ourselves to 256 characters in the font name. */ |
1005 | if ( len >= 256 ) |
1006 | { |
1007 | FT_ERROR(( "bdf_set_default_spacing_: " ERRMSG7, lineno )); |
1008 | error = FT_THROW( Invalid_Argument ); |
1009 | goto Exit; |
1010 | } |
1011 | |
1012 | FT_MEM_COPY( name, font->name, len ); |
1013 | |
1014 | error = bdf_list_split_( &list, "-" , name, (unsigned long)len ); |
1015 | if ( error ) |
1016 | goto Fail; |
1017 | |
1018 | if ( list.used == 15 ) |
1019 | { |
1020 | switch ( list.field[11][0] ) |
1021 | { |
1022 | case 'C': |
1023 | case 'c': |
1024 | font->spacing = BDF_CHARCELL; |
1025 | break; |
1026 | case 'M': |
1027 | case 'm': |
1028 | font->spacing = BDF_MONOWIDTH; |
1029 | break; |
1030 | case 'P': |
1031 | case 'p': |
1032 | font->spacing = BDF_PROPORTIONAL; |
1033 | break; |
1034 | } |
1035 | } |
1036 | |
1037 | Fail: |
1038 | bdf_list_done_( &list ); |
1039 | |
1040 | Exit: |
1041 | return error; |
1042 | } |
1043 | |
1044 | |
1045 | /* Determine whether the property is an atom or not. If it is, then */ |
1046 | /* clean it up so the double quotes are removed if they exist. */ |
1047 | static int |
1048 | bdf_is_atom_( char* line, |
1049 | unsigned long linelen, |
1050 | char** name, |
1051 | char** value, |
1052 | bdf_font_t* font ) |
1053 | { |
1054 | int hold; |
1055 | char *sp, *ep; |
1056 | bdf_property_t* p; |
1057 | |
1058 | |
1059 | sp = ep = line; |
1060 | |
1061 | while ( *ep && *ep != ' ' && *ep != '\t' ) |
1062 | ep++; |
1063 | |
1064 | hold = *ep; |
1065 | *ep = '\0'; |
1066 | |
1067 | p = bdf_get_property( sp, font ); |
1068 | |
1069 | /* If the property exists and is not an atom, just return here. */ |
1070 | if ( p && p->format != BDF_ATOM ) |
1071 | { |
1072 | *ep = (char)hold; /* Undo NUL-termination. */ |
1073 | return 0; |
1074 | } |
1075 | |
1076 | *name = sp; |
1077 | |
1078 | /* The property is an atom. Trim all leading and trailing whitespace */ |
1079 | /* and double quotes for the atom value. */ |
1080 | sp = ep; |
1081 | ep = line + linelen; |
1082 | |
1083 | /* Trim the leading whitespace if it exists. */ |
1084 | if ( sp < ep ) |
1085 | do |
1086 | sp++; |
1087 | while ( *sp == ' ' || *sp == '\t' ); |
1088 | |
1089 | /* Trim the leading double quote if it exists. */ |
1090 | if ( *sp == '"' ) |
1091 | sp++; |
1092 | |
1093 | *value = sp; |
1094 | |
1095 | /* Trim the trailing whitespace if it exists. */ |
1096 | if ( sp < ep ) |
1097 | do |
1098 | *ep-- = '\0'; |
1099 | while ( *ep == ' ' || *ep == '\t' ); |
1100 | |
1101 | /* Trim the trailing double quote if it exists. */ |
1102 | if ( *ep == '"' ) |
1103 | *ep = '\0'; |
1104 | |
1105 | return 1; |
1106 | } |
1107 | |
1108 | |
1109 | static FT_Error |
1110 | bdf_add_property_( bdf_font_t* font, |
1111 | const char* name, |
1112 | char* value, |
1113 | unsigned long lineno ) |
1114 | { |
1115 | size_t* propid; |
1116 | bdf_property_t *prop, *fp; |
1117 | FT_Memory memory = font->memory; |
1118 | FT_Error error = FT_Err_Ok; |
1119 | |
1120 | FT_UNUSED( lineno ); /* only used in debug mode */ |
1121 | |
1122 | |
1123 | /* First, check whether the property already exists in the font. */ |
1124 | if ( ( propid = ft_hash_str_lookup( name, |
1125 | (FT_Hash)font->internal ) ) != NULL ) |
1126 | { |
1127 | /* The property already exists in the font, so simply replace */ |
1128 | /* the value of the property with the current value. */ |
1129 | fp = font->props + *propid; |
1130 | |
1131 | switch ( fp->format ) |
1132 | { |
1133 | case BDF_ATOM: |
1134 | /* Delete the current atom if it exists. */ |
1135 | FT_FREE( fp->value.atom ); |
1136 | |
1137 | if ( value && value[0] != 0 ) |
1138 | { |
1139 | if ( FT_STRDUP( fp->value.atom, value ) ) |
1140 | goto Exit; |
1141 | } |
1142 | break; |
1143 | |
1144 | case BDF_INTEGER: |
1145 | fp->value.l = bdf_atol_( value ); |
1146 | break; |
1147 | |
1148 | case BDF_CARDINAL: |
1149 | fp->value.ul = bdf_atoul_( value ); |
1150 | break; |
1151 | |
1152 | default: |
1153 | ; |
1154 | } |
1155 | |
1156 | goto Exit; |
1157 | } |
1158 | |
1159 | /* See whether this property type exists yet or not. */ |
1160 | /* If not, create it. */ |
1161 | propid = ft_hash_str_lookup( name, &(font->proptbl) ); |
1162 | if ( !propid ) |
1163 | { |
1164 | error = bdf_create_property( name, BDF_ATOM, font ); |
1165 | if ( error ) |
1166 | goto Exit; |
1167 | propid = ft_hash_str_lookup( name, &(font->proptbl) ); |
1168 | } |
1169 | |
1170 | /* Allocate another property if this is overflowing. */ |
1171 | if ( font->props_used == font->props_size ) |
1172 | { |
1173 | if ( FT_QRENEW_ARRAY( font->props, |
1174 | font->props_size, |
1175 | font->props_size + 1 ) ) |
1176 | goto Exit; |
1177 | |
1178 | font->props_size++; |
1179 | } |
1180 | |
1181 | if ( *propid >= num_bdf_properties_ ) |
1182 | prop = font->user_props + ( *propid - num_bdf_properties_ ); |
1183 | else |
1184 | prop = (bdf_property_t*)bdf_properties_ + *propid; |
1185 | |
1186 | fp = font->props + font->props_used; |
1187 | |
1188 | fp->name = prop->name; |
1189 | fp->format = prop->format; |
1190 | fp->builtin = prop->builtin; |
1191 | |
1192 | switch ( prop->format ) |
1193 | { |
1194 | case BDF_ATOM: |
1195 | fp->value.atom = NULL; |
1196 | if ( value && value[0] ) |
1197 | { |
1198 | if ( FT_STRDUP( fp->value.atom, value ) ) |
1199 | goto Exit; |
1200 | } |
1201 | break; |
1202 | |
1203 | case BDF_INTEGER: |
1204 | fp->value.l = bdf_atol_( value ); |
1205 | break; |
1206 | |
1207 | case BDF_CARDINAL: |
1208 | fp->value.ul = bdf_atoul_( value ); |
1209 | break; |
1210 | } |
1211 | |
1212 | /* If the property happens to be a comment, then it doesn't need */ |
1213 | /* to be added to the internal hash table. */ |
1214 | if ( _bdf_strncmp( name, "COMMENT" , 7 ) != 0 ) |
1215 | { |
1216 | /* Add the property to the font property table. */ |
1217 | error = ft_hash_str_insert( fp->name, |
1218 | font->props_used, |
1219 | (FT_Hash)font->internal, |
1220 | memory ); |
1221 | if ( error ) |
1222 | goto Exit; |
1223 | } |
1224 | |
1225 | font->props_used++; |
1226 | |
1227 | /* Some special cases need to be handled here. The DEFAULT_CHAR */ |
1228 | /* property needs to be located if it exists in the property list, the */ |
1229 | /* FONT_ASCENT and FONT_DESCENT need to be assigned if they are */ |
1230 | /* present, and the SPACING property should override the default */ |
1231 | /* spacing. */ |
1232 | if ( _bdf_strncmp( name, "DEFAULT_CHAR" , 12 ) == 0 ) |
1233 | font->default_char = fp->value.ul; |
1234 | else if ( _bdf_strncmp( name, "FONT_ASCENT" , 11 ) == 0 ) |
1235 | font->font_ascent = fp->value.l; |
1236 | else if ( _bdf_strncmp( name, "FONT_DESCENT" , 12 ) == 0 ) |
1237 | font->font_descent = fp->value.l; |
1238 | else if ( _bdf_strncmp( name, "SPACING" , 7 ) == 0 ) |
1239 | { |
1240 | if ( !fp->value.atom ) |
1241 | { |
1242 | FT_ERROR(( "bdf_add_property_: " ERRMSG8, lineno, "SPACING" )); |
1243 | error = FT_THROW( Invalid_File_Format ); |
1244 | goto Exit; |
1245 | } |
1246 | |
1247 | if ( fp->value.atom[0] == 'p' || fp->value.atom[0] == 'P' ) |
1248 | font->spacing = BDF_PROPORTIONAL; |
1249 | else if ( fp->value.atom[0] == 'm' || fp->value.atom[0] == 'M' ) |
1250 | font->spacing = BDF_MONOWIDTH; |
1251 | else if ( fp->value.atom[0] == 'c' || fp->value.atom[0] == 'C' ) |
1252 | font->spacing = BDF_CHARCELL; |
1253 | } |
1254 | |
1255 | Exit: |
1256 | return error; |
1257 | } |
1258 | |
1259 | |
1260 | static const unsigned char nibble_mask[8] = |
1261 | { |
1262 | 0xFF, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE |
1263 | }; |
1264 | |
1265 | |
1266 | static FT_Error |
1267 | bdf_parse_end_( char* line, |
1268 | unsigned long linelen, |
1269 | unsigned long lineno, |
1270 | void* call_data, |
1271 | void* client_data ) |
1272 | { |
1273 | /* a no-op; we ignore everything after `ENDFONT' */ |
1274 | |
1275 | FT_UNUSED( line ); |
1276 | FT_UNUSED( linelen ); |
1277 | FT_UNUSED( lineno ); |
1278 | FT_UNUSED( call_data ); |
1279 | FT_UNUSED( client_data ); |
1280 | |
1281 | return FT_Err_Ok; |
1282 | } |
1283 | |
1284 | |
1285 | /* Actually parse the glyph info and bitmaps. */ |
1286 | static FT_Error |
1287 | bdf_parse_glyphs_( char* line, |
1288 | unsigned long linelen, |
1289 | unsigned long lineno, |
1290 | void* call_data, |
1291 | void* client_data ) |
1292 | { |
1293 | int c, mask_index; |
1294 | char* s; |
1295 | unsigned char* bp; |
1296 | unsigned long i, slen, nibbles; |
1297 | |
1298 | bdf_line_func_t_* next; |
1299 | bdf_parse_t_* p; |
1300 | bdf_glyph_t* glyph; |
1301 | bdf_font_t* font; |
1302 | |
1303 | FT_Memory memory; |
1304 | FT_Error error = FT_Err_Ok; |
1305 | |
1306 | FT_UNUSED( lineno ); /* only used in debug mode */ |
1307 | |
1308 | |
1309 | next = (bdf_line_func_t_ *)call_data; |
1310 | p = (bdf_parse_t_ *) client_data; |
1311 | |
1312 | font = p->font; |
1313 | memory = font->memory; |
1314 | |
1315 | /* Check for a comment. */ |
1316 | if ( _bdf_strncmp( line, "COMMENT" , 7 ) == 0 ) |
1317 | { |
1318 | if ( p->opts->keep_comments ) |
1319 | { |
1320 | linelen -= 7; |
1321 | |
1322 | s = line + 7; |
1323 | if ( *s != 0 ) |
1324 | { |
1325 | s++; |
1326 | linelen--; |
1327 | } |
1328 | error = bdf_add_comment_( p->font, s, linelen ); |
1329 | } |
1330 | goto Exit; |
1331 | } |
1332 | |
1333 | /* The very first thing expected is the number of glyphs. */ |
1334 | if ( !( p->flags & BDF_GLYPHS_ ) ) |
1335 | { |
1336 | if ( _bdf_strncmp( line, "CHARS" , 5 ) != 0 ) |
1337 | { |
1338 | FT_ERROR(( "bdf_parse_glyphs_: " ERRMSG1, lineno, "CHARS" )); |
1339 | error = FT_THROW( Missing_Chars_Field ); |
1340 | goto Exit; |
1341 | } |
1342 | |
1343 | error = bdf_list_split_( &p->list, " +" , line, linelen ); |
1344 | if ( error ) |
1345 | goto Exit; |
1346 | p->cnt = font->glyphs_size = bdf_atoul_( p->list.field[1] ); |
1347 | |
1348 | /* We need at least 20 bytes per glyph. */ |
1349 | if ( p->cnt > p->size / 20 ) |
1350 | { |
1351 | p->cnt = font->glyphs_size = p->size / 20; |
1352 | FT_TRACE2(( "bdf_parse_glyphs_: " ACMSG17, p->cnt )); |
1353 | } |
1354 | |
1355 | /* Make sure the number of glyphs is non-zero. */ |
1356 | if ( p->cnt == 0 ) |
1357 | font->glyphs_size = 64; |
1358 | |
1359 | /* Limit ourselves to 1,114,112 glyphs in the font (this is the */ |
1360 | /* number of code points available in Unicode). */ |
1361 | if ( p->cnt >= 0x110000UL ) |
1362 | { |
1363 | FT_ERROR(( "bdf_parse_glyphs_: " ERRMSG5, lineno, "CHARS" )); |
1364 | error = FT_THROW( Invalid_Argument ); |
1365 | goto Exit; |
1366 | } |
1367 | |
1368 | if ( FT_NEW_ARRAY( font->glyphs, font->glyphs_size ) ) |
1369 | goto Exit; |
1370 | |
1371 | p->flags |= BDF_GLYPHS_; |
1372 | |
1373 | goto Exit; |
1374 | } |
1375 | |
1376 | /* Check for the ENDFONT field. */ |
1377 | if ( _bdf_strncmp( line, "ENDFONT" , 7 ) == 0 ) |
1378 | { |
1379 | if ( p->flags & BDF_GLYPH_BITS_ ) |
1380 | { |
1381 | /* Missing ENDCHAR field. */ |
1382 | FT_ERROR(( "bdf_parse_glyphs_: " ERRMSG1, lineno, "ENDCHAR" )); |
1383 | error = FT_THROW( Corrupted_Font_Glyphs ); |
1384 | goto Exit; |
1385 | } |
1386 | |
1387 | /* Sort the glyphs by encoding. */ |
1388 | ft_qsort( (char *)font->glyphs, |
1389 | font->glyphs_used, |
1390 | sizeof ( bdf_glyph_t ), |
1391 | by_encoding ); |
1392 | |
1393 | p->flags &= ~BDF_START_; |
1394 | *next = bdf_parse_end_; |
1395 | |
1396 | goto Exit; |
1397 | } |
1398 | |
1399 | /* Check for the ENDCHAR field. */ |
1400 | if ( _bdf_strncmp( line, "ENDCHAR" , 7 ) == 0 ) |
1401 | { |
1402 | p->glyph_enc = 0; |
1403 | p->flags &= ~BDF_GLYPH_BITS_; |
1404 | |
1405 | goto Exit; |
1406 | } |
1407 | |
1408 | /* Check whether a glyph is being scanned but should be */ |
1409 | /* ignored because it is an unencoded glyph. */ |
1410 | if ( ( p->flags & BDF_GLYPH_ ) && |
1411 | p->glyph_enc == -1 && |
1412 | p->opts->keep_unencoded == 0 ) |
1413 | goto Exit; |
1414 | |
1415 | /* Check for the STARTCHAR field. */ |
1416 | if ( _bdf_strncmp( line, "STARTCHAR" , 9 ) == 0 ) |
1417 | { |
1418 | if ( p->flags & BDF_GLYPH_BITS_ ) |
1419 | { |
1420 | /* Missing ENDCHAR field. */ |
1421 | FT_ERROR(( "bdf_parse_glyphs_: " ERRMSG1, lineno, "ENDCHAR" )); |
1422 | error = FT_THROW( Missing_Startchar_Field ); |
1423 | goto Exit; |
1424 | } |
1425 | |
1426 | /* Set the character name in the parse info first until the */ |
1427 | /* encoding can be checked for an unencoded character. */ |
1428 | FT_FREE( p->glyph_name ); |
1429 | |
1430 | error = bdf_list_split_( &p->list, " +" , line, linelen ); |
1431 | if ( error ) |
1432 | goto Exit; |
1433 | |
1434 | bdf_list_shift_( &p->list, 1 ); |
1435 | |
1436 | s = bdf_list_join_( &p->list, ' ', &slen ); |
1437 | |
1438 | if ( !s ) |
1439 | { |
1440 | FT_ERROR(( "bdf_parse_glyphs_: " ERRMSG8, lineno, "STARTCHAR" )); |
1441 | error = FT_THROW( Invalid_File_Format ); |
1442 | goto Exit; |
1443 | } |
1444 | |
1445 | if ( FT_QALLOC( p->glyph_name, slen + 1 ) ) |
1446 | goto Exit; |
1447 | |
1448 | FT_MEM_COPY( p->glyph_name, s, slen + 1 ); |
1449 | |
1450 | p->flags |= BDF_GLYPH_; |
1451 | |
1452 | FT_TRACE4(( DBGMSG1, lineno, s )); |
1453 | |
1454 | goto Exit; |
1455 | } |
1456 | |
1457 | /* Check for the ENCODING field. */ |
1458 | if ( _bdf_strncmp( line, "ENCODING" , 8 ) == 0 ) |
1459 | { |
1460 | if ( !( p->flags & BDF_GLYPH_ ) ) |
1461 | { |
1462 | /* Missing STARTCHAR field. */ |
1463 | FT_ERROR(( "bdf_parse_glyphs_: " ERRMSG1, lineno, "STARTCHAR" )); |
1464 | error = FT_THROW( Missing_Startchar_Field ); |
1465 | goto Exit; |
1466 | } |
1467 | |
1468 | error = bdf_list_split_( &p->list, " +" , line, linelen ); |
1469 | if ( error ) |
1470 | goto Exit; |
1471 | |
1472 | p->glyph_enc = bdf_atol_( p->list.field[1] ); |
1473 | |
1474 | /* Normalize negative encoding values. The specification only */ |
1475 | /* allows -1, but we can be more generous here. */ |
1476 | if ( p->glyph_enc < -1 ) |
1477 | p->glyph_enc = -1; |
1478 | |
1479 | /* Check for alternative encoding format. */ |
1480 | if ( p->glyph_enc == -1 && p->list.used > 2 ) |
1481 | p->glyph_enc = bdf_atol_( p->list.field[2] ); |
1482 | |
1483 | if ( p->glyph_enc < -1 || p->glyph_enc >= 0x110000L ) |
1484 | p->glyph_enc = -1; |
1485 | |
1486 | FT_TRACE4(( DBGMSG2, p->glyph_enc )); |
1487 | |
1488 | if ( p->glyph_enc >= 0 ) |
1489 | { |
1490 | /* Make sure there are enough glyphs allocated in case the */ |
1491 | /* number of characters happen to be wrong. */ |
1492 | if ( font->glyphs_used == font->glyphs_size ) |
1493 | { |
1494 | if ( FT_RENEW_ARRAY( font->glyphs, |
1495 | font->glyphs_size, |
1496 | font->glyphs_size + 64 ) ) |
1497 | goto Exit; |
1498 | |
1499 | font->glyphs_size += 64; |
1500 | } |
1501 | |
1502 | glyph = font->glyphs + font->glyphs_used++; |
1503 | glyph->name = p->glyph_name; |
1504 | glyph->encoding = (unsigned long)p->glyph_enc; |
1505 | |
1506 | /* Reset the initial glyph info. */ |
1507 | p->glyph_name = NULL; |
1508 | } |
1509 | else |
1510 | { |
1511 | /* Unencoded glyph. Check whether it should */ |
1512 | /* be added or not. */ |
1513 | if ( p->opts->keep_unencoded ) |
1514 | { |
1515 | /* Allocate the next unencoded glyph. */ |
1516 | if ( font->unencoded_used == font->unencoded_size ) |
1517 | { |
1518 | if ( FT_RENEW_ARRAY( font->unencoded , |
1519 | font->unencoded_size, |
1520 | font->unencoded_size + 4 ) ) |
1521 | goto Exit; |
1522 | |
1523 | font->unencoded_size += 4; |
1524 | } |
1525 | |
1526 | glyph = font->unencoded + font->unencoded_used; |
1527 | glyph->name = p->glyph_name; |
1528 | glyph->encoding = font->unencoded_used++; |
1529 | |
1530 | /* Reset the initial glyph info. */ |
1531 | p->glyph_name = NULL; |
1532 | } |
1533 | else |
1534 | { |
1535 | /* Free up the glyph name if the unencoded shouldn't be */ |
1536 | /* kept. */ |
1537 | FT_FREE( p->glyph_name ); |
1538 | } |
1539 | } |
1540 | |
1541 | /* Clear the flags that might be added when width and height are */ |
1542 | /* checked for consistency. */ |
1543 | p->flags &= ~( BDF_GLYPH_WIDTH_CHECK_ | BDF_GLYPH_HEIGHT_CHECK_ ); |
1544 | |
1545 | p->flags |= BDF_ENCODING_; |
1546 | |
1547 | goto Exit; |
1548 | } |
1549 | |
1550 | if ( !( p->flags & BDF_ENCODING_ ) ) |
1551 | goto Missing_Encoding; |
1552 | |
1553 | /* Point at the glyph being constructed. */ |
1554 | if ( p->glyph_enc == -1 ) |
1555 | glyph = font->unencoded + ( font->unencoded_used - 1 ); |
1556 | else |
1557 | glyph = font->glyphs + ( font->glyphs_used - 1 ); |
1558 | |
1559 | /* Check whether a bitmap is being constructed. */ |
1560 | if ( p->flags & BDF_BITMAP_ ) |
1561 | { |
1562 | /* If there are more rows than are specified in the glyph metrics, */ |
1563 | /* ignore the remaining lines. */ |
1564 | if ( p->row >= (unsigned long)glyph->bbx.height ) |
1565 | { |
1566 | if ( !( p->flags & BDF_GLYPH_HEIGHT_CHECK_ ) ) |
1567 | { |
1568 | FT_TRACE2(( "bdf_parse_glyphs_: " ACMSG13, glyph->encoding )); |
1569 | p->flags |= BDF_GLYPH_HEIGHT_CHECK_; |
1570 | } |
1571 | |
1572 | goto Exit; |
1573 | } |
1574 | |
1575 | /* Only collect the number of nibbles indicated by the glyph */ |
1576 | /* metrics. If there are more columns, they are simply ignored. */ |
1577 | nibbles = glyph->bpr << 1; |
1578 | bp = glyph->bitmap + p->row * glyph->bpr; |
1579 | |
1580 | for ( i = 0; i < nibbles; i++ ) |
1581 | { |
1582 | c = line[i]; |
1583 | if ( !sbitset( hdigits, c ) ) |
1584 | break; |
1585 | *bp = (FT_Byte)( ( *bp << 4 ) + a2i[c] ); |
1586 | if ( i + 1 < nibbles && ( i & 1 ) ) |
1587 | *++bp = 0; |
1588 | } |
1589 | |
1590 | /* If any line has not enough columns, */ |
1591 | /* indicate they have been padded with zero bits. */ |
1592 | if ( i < nibbles && |
1593 | !( p->flags & BDF_GLYPH_WIDTH_CHECK_ ) ) |
1594 | { |
1595 | FT_TRACE2(( "bdf_parse_glyphs_: " ACMSG16, glyph->encoding )); |
1596 | p->flags |= BDF_GLYPH_WIDTH_CHECK_; |
1597 | } |
1598 | |
1599 | /* Remove possible garbage at the right. */ |
1600 | mask_index = ( glyph->bbx.width * p->font->bpp ) & 7; |
1601 | if ( glyph->bbx.width ) |
1602 | *bp &= nibble_mask[mask_index]; |
1603 | |
1604 | /* If any line has extra columns, indicate they have been removed. */ |
1605 | if ( i == nibbles && |
1606 | sbitset( hdigits, line[nibbles] ) && |
1607 | !( p->flags & BDF_GLYPH_WIDTH_CHECK_ ) ) |
1608 | { |
1609 | FT_TRACE2(( "bdf_parse_glyphs_: " ACMSG14, glyph->encoding )); |
1610 | p->flags |= BDF_GLYPH_WIDTH_CHECK_; |
1611 | } |
1612 | |
1613 | p->row++; |
1614 | goto Exit; |
1615 | } |
1616 | |
1617 | /* Expect the SWIDTH (scalable width) field next. */ |
1618 | if ( _bdf_strncmp( line, "SWIDTH" , 6 ) == 0 ) |
1619 | { |
1620 | error = bdf_list_split_( &p->list, " +" , line, linelen ); |
1621 | if ( error ) |
1622 | goto Exit; |
1623 | |
1624 | glyph->swidth = bdf_atous_( p->list.field[1] ); |
1625 | p->flags |= BDF_SWIDTH_; |
1626 | |
1627 | goto Exit; |
1628 | } |
1629 | |
1630 | /* Expect the DWIDTH (device width) field next. */ |
1631 | if ( _bdf_strncmp( line, "DWIDTH" , 6 ) == 0 ) |
1632 | { |
1633 | error = bdf_list_split_( &p->list, " +" , line, linelen ); |
1634 | if ( error ) |
1635 | goto Exit; |
1636 | |
1637 | glyph->dwidth = bdf_atous_( p->list.field[1] ); |
1638 | |
1639 | if ( !( p->flags & BDF_SWIDTH_ ) ) |
1640 | { |
1641 | /* Missing SWIDTH field. Emit an auto correction message and set */ |
1642 | /* the scalable width from the device width. */ |
1643 | FT_TRACE2(( "bdf_parse_glyphs_: " ACMSG9, lineno )); |
1644 | |
1645 | glyph->swidth = (unsigned short)FT_MulDiv( |
1646 | glyph->dwidth, 72000L, |
1647 | (FT_Long)( font->point_size * |
1648 | font->resolution_x ) ); |
1649 | } |
1650 | |
1651 | p->flags |= BDF_DWIDTH_; |
1652 | goto Exit; |
1653 | } |
1654 | |
1655 | /* Expect the BBX field next. */ |
1656 | if ( _bdf_strncmp( line, "BBX" , 3 ) == 0 ) |
1657 | { |
1658 | error = bdf_list_split_( &p->list, " +" , line, linelen ); |
1659 | if ( error ) |
1660 | goto Exit; |
1661 | |
1662 | glyph->bbx.width = bdf_atous_( p->list.field[1] ); |
1663 | glyph->bbx.height = bdf_atous_( p->list.field[2] ); |
1664 | glyph->bbx.x_offset = bdf_atos_( p->list.field[3] ); |
1665 | glyph->bbx.y_offset = bdf_atos_( p->list.field[4] ); |
1666 | |
1667 | /* Generate the ascent and descent of the character. */ |
1668 | glyph->bbx.ascent = (short)( glyph->bbx.height + glyph->bbx.y_offset ); |
1669 | glyph->bbx.descent = (short)( -glyph->bbx.y_offset ); |
1670 | |
1671 | /* Determine the overall font bounding box as the characters are */ |
1672 | /* loaded so corrections can be done later if indicated. */ |
1673 | p->maxas = (short)FT_MAX( glyph->bbx.ascent, p->maxas ); |
1674 | p->maxds = (short)FT_MAX( glyph->bbx.descent, p->maxds ); |
1675 | |
1676 | p->rbearing = (short)( glyph->bbx.width + glyph->bbx.x_offset ); |
1677 | |
1678 | p->maxrb = (short)FT_MAX( p->rbearing, p->maxrb ); |
1679 | p->minlb = (short)FT_MIN( glyph->bbx.x_offset, p->minlb ); |
1680 | p->maxlb = (short)FT_MAX( glyph->bbx.x_offset, p->maxlb ); |
1681 | |
1682 | if ( !( p->flags & BDF_DWIDTH_ ) ) |
1683 | { |
1684 | /* Missing DWIDTH field. Emit an auto correction message and set */ |
1685 | /* the device width to the glyph width. */ |
1686 | FT_TRACE2(( "bdf_parse_glyphs_: " ACMSG10, lineno )); |
1687 | glyph->dwidth = glyph->bbx.width; |
1688 | } |
1689 | |
1690 | /* If the BDF_CORRECT_METRICS flag is set, then adjust the SWIDTH */ |
1691 | /* value if necessary. */ |
1692 | if ( p->opts->correct_metrics ) |
1693 | { |
1694 | /* Determine the point size of the glyph. */ |
1695 | unsigned short sw = (unsigned short)FT_MulDiv( |
1696 | glyph->dwidth, 72000L, |
1697 | (FT_Long)( font->point_size * |
1698 | font->resolution_x ) ); |
1699 | |
1700 | |
1701 | if ( sw != glyph->swidth ) |
1702 | { |
1703 | glyph->swidth = sw; |
1704 | |
1705 | p->flags |= BDF_SWIDTH_ADJ_; |
1706 | } |
1707 | } |
1708 | |
1709 | p->flags |= BDF_BBX_; |
1710 | goto Exit; |
1711 | } |
1712 | |
1713 | /* And finally, gather up the bitmap. */ |
1714 | if ( _bdf_strncmp( line, "BITMAP" , 6 ) == 0 ) |
1715 | { |
1716 | unsigned long bitmap_size; |
1717 | |
1718 | |
1719 | if ( !( p->flags & BDF_BBX_ ) ) |
1720 | { |
1721 | /* Missing BBX field. */ |
1722 | FT_ERROR(( "bdf_parse_glyphs_: " ERRMSG1, lineno, "BBX" )); |
1723 | error = FT_THROW( Missing_Bbx_Field ); |
1724 | goto Exit; |
1725 | } |
1726 | |
1727 | /* Allocate enough space for the bitmap. */ |
1728 | glyph->bpr = ( glyph->bbx.width * p->font->bpp + 7 ) >> 3; |
1729 | |
1730 | bitmap_size = glyph->bpr * glyph->bbx.height; |
1731 | if ( glyph->bpr > 0xFFFFU || bitmap_size > 0xFFFFU ) |
1732 | { |
1733 | FT_ERROR(( "bdf_parse_glyphs_: " ERRMSG4, lineno )); |
1734 | error = FT_THROW( Bbx_Too_Big ); |
1735 | goto Exit; |
1736 | } |
1737 | else |
1738 | glyph->bytes = (unsigned short)bitmap_size; |
1739 | |
1740 | if ( FT_ALLOC( glyph->bitmap, glyph->bytes ) ) |
1741 | goto Exit; |
1742 | |
1743 | p->row = 0; |
1744 | p->flags |= BDF_BITMAP_; |
1745 | |
1746 | goto Exit; |
1747 | } |
1748 | |
1749 | FT_ERROR(( "bdf_parse_glyphs_: " ERRMSG9, lineno )); |
1750 | error = FT_THROW( Invalid_File_Format ); |
1751 | goto Exit; |
1752 | |
1753 | Missing_Encoding: |
1754 | /* Missing ENCODING field. */ |
1755 | FT_ERROR(( "bdf_parse_glyphs_: " ERRMSG1, lineno, "ENCODING" )); |
1756 | error = FT_THROW( Missing_Encoding_Field ); |
1757 | |
1758 | Exit: |
1759 | if ( error && ( p->flags & BDF_GLYPH_ ) ) |
1760 | FT_FREE( p->glyph_name ); |
1761 | |
1762 | return error; |
1763 | } |
1764 | |
1765 | |
1766 | /* Load the font properties. */ |
1767 | static FT_Error |
1768 | bdf_parse_properties_( char* line, |
1769 | unsigned long linelen, |
1770 | unsigned long lineno, |
1771 | void* call_data, |
1772 | void* client_data ) |
1773 | { |
1774 | unsigned long vlen; |
1775 | bdf_line_func_t_* next; |
1776 | bdf_parse_t_* p; |
1777 | char* name; |
1778 | char* value; |
1779 | char nbuf[BUFSIZE]; |
1780 | FT_Error error = FT_Err_Ok; |
1781 | |
1782 | FT_UNUSED( lineno ); |
1783 | |
1784 | |
1785 | next = (bdf_line_func_t_ *)call_data; |
1786 | p = (bdf_parse_t_ *) client_data; |
1787 | |
1788 | /* Check for the end of the properties. */ |
1789 | if ( _bdf_strncmp( line, "ENDPROPERTIES" , 13 ) == 0 ) |
1790 | { |
1791 | /* If the FONT_ASCENT or FONT_DESCENT properties have not been */ |
1792 | /* encountered yet, then make sure they are added as properties and */ |
1793 | /* make sure they are set from the font bounding box info. */ |
1794 | /* */ |
1795 | /* This is *always* done regardless of the options, because X11 */ |
1796 | /* requires these two fields to compile fonts. */ |
1797 | if ( bdf_get_font_property( p->font, "FONT_ASCENT" ) == 0 ) |
1798 | { |
1799 | p->font->font_ascent = p->font->bbx.ascent; |
1800 | ft_snprintf( nbuf, BUFSIZE, "%hd" , p->font->bbx.ascent ); |
1801 | error = bdf_add_property_( p->font, "FONT_ASCENT" , |
1802 | nbuf, lineno ); |
1803 | if ( error ) |
1804 | goto Exit; |
1805 | |
1806 | FT_TRACE2(( "bdf_parse_properties_: " ACMSG1, p->font->bbx.ascent )); |
1807 | } |
1808 | |
1809 | if ( bdf_get_font_property( p->font, "FONT_DESCENT" ) == 0 ) |
1810 | { |
1811 | p->font->font_descent = p->font->bbx.descent; |
1812 | ft_snprintf( nbuf, BUFSIZE, "%hd" , p->font->bbx.descent ); |
1813 | error = bdf_add_property_( p->font, "FONT_DESCENT" , |
1814 | nbuf, lineno ); |
1815 | if ( error ) |
1816 | goto Exit; |
1817 | |
1818 | FT_TRACE2(( "bdf_parse_properties_: " ACMSG2, p->font->bbx.descent )); |
1819 | } |
1820 | |
1821 | p->flags &= ~BDF_PROPS_; |
1822 | *next = bdf_parse_glyphs_; |
1823 | |
1824 | goto Exit; |
1825 | } |
1826 | |
1827 | /* Ignore the _XFREE86_GLYPH_RANGES properties. */ |
1828 | if ( _bdf_strncmp( line, "_XFREE86_GLYPH_RANGES" , 21 ) == 0 ) |
1829 | goto Exit; |
1830 | |
1831 | /* Handle COMMENT fields and properties in a special way to preserve */ |
1832 | /* the spacing. */ |
1833 | if ( _bdf_strncmp( line, "COMMENT" , 7 ) == 0 ) |
1834 | { |
1835 | name = value = line; |
1836 | value += 7; |
1837 | if ( *value ) |
1838 | *value++ = 0; |
1839 | error = bdf_add_property_( p->font, name, value, lineno ); |
1840 | if ( error ) |
1841 | goto Exit; |
1842 | } |
1843 | else if ( bdf_is_atom_( line, linelen, &name, &value, p->font ) ) |
1844 | { |
1845 | error = bdf_add_property_( p->font, name, value, lineno ); |
1846 | if ( error ) |
1847 | goto Exit; |
1848 | } |
1849 | else |
1850 | { |
1851 | error = bdf_list_split_( &p->list, " +" , line, linelen ); |
1852 | if ( error ) |
1853 | goto Exit; |
1854 | name = p->list.field[0]; |
1855 | |
1856 | bdf_list_shift_( &p->list, 1 ); |
1857 | value = bdf_list_join_( &p->list, ' ', &vlen ); |
1858 | |
1859 | error = bdf_add_property_( p->font, name, value, lineno ); |
1860 | if ( error ) |
1861 | goto Exit; |
1862 | } |
1863 | |
1864 | Exit: |
1865 | return error; |
1866 | } |
1867 | |
1868 | |
1869 | /* Load the font header. */ |
1870 | static FT_Error |
1871 | bdf_parse_start_( char* line, |
1872 | unsigned long linelen, |
1873 | unsigned long lineno, |
1874 | void* call_data, |
1875 | void* client_data ) |
1876 | { |
1877 | unsigned long slen; |
1878 | bdf_line_func_t_* next; |
1879 | bdf_parse_t_* p; |
1880 | bdf_font_t* font; |
1881 | char *s; |
1882 | |
1883 | FT_Memory memory = NULL; |
1884 | FT_Error error = FT_Err_Ok; |
1885 | |
1886 | FT_UNUSED( lineno ); /* only used in debug mode */ |
1887 | |
1888 | |
1889 | next = (bdf_line_func_t_ *)call_data; |
1890 | p = (bdf_parse_t_ *) client_data; |
1891 | |
1892 | if ( p->font ) |
1893 | memory = p->font->memory; |
1894 | |
1895 | /* Check for a comment. This is done to handle those fonts that have */ |
1896 | /* comments before the STARTFONT line for some reason. */ |
1897 | if ( _bdf_strncmp( line, "COMMENT" , 7 ) == 0 ) |
1898 | { |
1899 | if ( p->opts->keep_comments && p->font ) |
1900 | { |
1901 | linelen -= 7; |
1902 | |
1903 | s = line + 7; |
1904 | if ( *s != 0 ) |
1905 | { |
1906 | s++; |
1907 | linelen--; |
1908 | } |
1909 | error = bdf_add_comment_( p->font, s, linelen ); |
1910 | } |
1911 | goto Exit; |
1912 | } |
1913 | |
1914 | if ( !( p->flags & BDF_START_ ) ) |
1915 | { |
1916 | memory = p->memory; |
1917 | |
1918 | if ( _bdf_strncmp( line, "STARTFONT" , 9 ) != 0 ) |
1919 | { |
1920 | /* we don't emit an error message since this code gets */ |
1921 | /* explicitly caught one level higher */ |
1922 | error = FT_THROW( Missing_Startfont_Field ); |
1923 | goto Exit; |
1924 | } |
1925 | |
1926 | p->flags = BDF_START_; |
1927 | font = p->font = NULL; |
1928 | |
1929 | if ( FT_NEW( font ) ) |
1930 | goto Exit; |
1931 | p->font = font; |
1932 | |
1933 | font->memory = p->memory; |
1934 | |
1935 | { /* setup */ |
1936 | size_t i; |
1937 | bdf_property_t* prop; |
1938 | |
1939 | |
1940 | error = ft_hash_str_init( &(font->proptbl), memory ); |
1941 | if ( error ) |
1942 | goto Exit; |
1943 | for ( i = 0, prop = (bdf_property_t*)bdf_properties_; |
1944 | i < num_bdf_properties_; i++, prop++ ) |
1945 | { |
1946 | error = ft_hash_str_insert( prop->name, i, |
1947 | &(font->proptbl), memory ); |
1948 | if ( error ) |
1949 | goto Exit; |
1950 | } |
1951 | } |
1952 | |
1953 | if ( FT_QALLOC( p->font->internal, sizeof ( FT_HashRec ) ) ) |
1954 | goto Exit; |
1955 | error = ft_hash_str_init( (FT_Hash)p->font->internal, memory ); |
1956 | if ( error ) |
1957 | goto Exit; |
1958 | p->font->spacing = p->opts->font_spacing; |
1959 | p->font->default_char = ~0UL; |
1960 | |
1961 | goto Exit; |
1962 | } |
1963 | |
1964 | /* Check for the start of the properties. */ |
1965 | if ( _bdf_strncmp( line, "STARTPROPERTIES" , 15 ) == 0 ) |
1966 | { |
1967 | if ( !( p->flags & BDF_FONT_BBX_ ) ) |
1968 | { |
1969 | /* Missing the FONTBOUNDINGBOX field. */ |
1970 | FT_ERROR(( "bdf_parse_start_: " ERRMSG1, lineno, "FONTBOUNDINGBOX" )); |
1971 | error = FT_THROW( Missing_Fontboundingbox_Field ); |
1972 | goto Exit; |
1973 | } |
1974 | |
1975 | error = bdf_list_split_( &p->list, " +" , line, linelen ); |
1976 | if ( error ) |
1977 | goto Exit; |
1978 | |
1979 | /* at this point, `p->font' can't be NULL */ |
1980 | p->cnt = p->font->props_size = bdf_atoul_( p->list.field[1] ); |
1981 | /* We need at least 4 bytes per property. */ |
1982 | if ( p->cnt > p->size / 4 ) |
1983 | { |
1984 | p->font->props_size = 0; |
1985 | |
1986 | FT_ERROR(( "bdf_parse_glyphs_: " ERRMSG5, lineno, "STARTPROPERTIES" )); |
1987 | error = FT_THROW( Invalid_Argument ); |
1988 | goto Exit; |
1989 | } |
1990 | |
1991 | if ( FT_NEW_ARRAY( p->font->props, p->cnt ) ) |
1992 | { |
1993 | p->font->props_size = 0; |
1994 | goto Exit; |
1995 | } |
1996 | |
1997 | p->flags |= BDF_PROPS_; |
1998 | *next = bdf_parse_properties_; |
1999 | |
2000 | goto Exit; |
2001 | } |
2002 | |
2003 | /* Check for the FONTBOUNDINGBOX field. */ |
2004 | if ( _bdf_strncmp( line, "FONTBOUNDINGBOX" , 15 ) == 0 ) |
2005 | { |
2006 | if ( !( p->flags & BDF_SIZE_ ) ) |
2007 | { |
2008 | /* Missing the SIZE field. */ |
2009 | FT_ERROR(( "bdf_parse_start_: " ERRMSG1, lineno, "SIZE" )); |
2010 | error = FT_THROW( Missing_Size_Field ); |
2011 | goto Exit; |
2012 | } |
2013 | |
2014 | error = bdf_list_split_( &p->list, " +" , line, linelen ); |
2015 | if ( error ) |
2016 | goto Exit; |
2017 | |
2018 | p->font->bbx.width = bdf_atous_( p->list.field[1] ); |
2019 | p->font->bbx.height = bdf_atous_( p->list.field[2] ); |
2020 | |
2021 | p->font->bbx.x_offset = bdf_atos_( p->list.field[3] ); |
2022 | p->font->bbx.y_offset = bdf_atos_( p->list.field[4] ); |
2023 | |
2024 | p->font->bbx.ascent = (short)( p->font->bbx.height + |
2025 | p->font->bbx.y_offset ); |
2026 | |
2027 | p->font->bbx.descent = (short)( -p->font->bbx.y_offset ); |
2028 | |
2029 | p->flags |= BDF_FONT_BBX_; |
2030 | |
2031 | goto Exit; |
2032 | } |
2033 | |
2034 | /* The next thing to check for is the FONT field. */ |
2035 | if ( _bdf_strncmp( line, "FONT" , 4 ) == 0 ) |
2036 | { |
2037 | error = bdf_list_split_( &p->list, " +" , line, linelen ); |
2038 | if ( error ) |
2039 | goto Exit; |
2040 | bdf_list_shift_( &p->list, 1 ); |
2041 | |
2042 | s = bdf_list_join_( &p->list, ' ', &slen ); |
2043 | |
2044 | if ( !s ) |
2045 | { |
2046 | FT_ERROR(( "bdf_parse_start_: " ERRMSG8, lineno, "FONT" )); |
2047 | error = FT_THROW( Invalid_File_Format ); |
2048 | goto Exit; |
2049 | } |
2050 | |
2051 | /* Allowing multiple `FONT' lines (which is invalid) doesn't hurt... */ |
2052 | FT_FREE( p->font->name ); |
2053 | |
2054 | if ( FT_QALLOC( p->font->name, slen + 1 ) ) |
2055 | goto Exit; |
2056 | FT_MEM_COPY( p->font->name, s, slen + 1 ); |
2057 | |
2058 | /* If the font name is an XLFD name, set the spacing to the one in */ |
2059 | /* the font name. If there is no spacing fall back on the default. */ |
2060 | error = bdf_set_default_spacing_( p->font, p->opts, lineno ); |
2061 | if ( error ) |
2062 | goto Exit; |
2063 | |
2064 | p->flags |= BDF_FONT_NAME_; |
2065 | |
2066 | goto Exit; |
2067 | } |
2068 | |
2069 | /* Check for the SIZE field. */ |
2070 | if ( _bdf_strncmp( line, "SIZE" , 4 ) == 0 ) |
2071 | { |
2072 | if ( !( p->flags & BDF_FONT_NAME_ ) ) |
2073 | { |
2074 | /* Missing the FONT field. */ |
2075 | FT_ERROR(( "bdf_parse_start_: " ERRMSG1, lineno, "FONT" )); |
2076 | error = FT_THROW( Missing_Font_Field ); |
2077 | goto Exit; |
2078 | } |
2079 | |
2080 | error = bdf_list_split_( &p->list, " +" , line, linelen ); |
2081 | if ( error ) |
2082 | goto Exit; |
2083 | |
2084 | p->font->point_size = bdf_atoul_( p->list.field[1] ); |
2085 | p->font->resolution_x = bdf_atoul_( p->list.field[2] ); |
2086 | p->font->resolution_y = bdf_atoul_( p->list.field[3] ); |
2087 | |
2088 | /* Check for the bits per pixel field. */ |
2089 | if ( p->list.used == 5 ) |
2090 | { |
2091 | unsigned short bpp; |
2092 | |
2093 | |
2094 | bpp = bdf_atous_( p->list.field[4] ); |
2095 | |
2096 | /* Only values 1, 2, 4, 8 are allowed for greymap fonts. */ |
2097 | if ( bpp > 4 ) |
2098 | p->font->bpp = 8; |
2099 | else if ( bpp > 2 ) |
2100 | p->font->bpp = 4; |
2101 | else if ( bpp > 1 ) |
2102 | p->font->bpp = 2; |
2103 | else |
2104 | p->font->bpp = 1; |
2105 | |
2106 | if ( p->font->bpp != bpp ) |
2107 | FT_TRACE2(( "bdf_parse_start_: " ACMSG11, p->font->bpp )); |
2108 | } |
2109 | else |
2110 | p->font->bpp = 1; |
2111 | |
2112 | p->flags |= BDF_SIZE_; |
2113 | |
2114 | goto Exit; |
2115 | } |
2116 | |
2117 | /* Check for the CHARS field -- font properties are optional */ |
2118 | if ( _bdf_strncmp( line, "CHARS" , 5 ) == 0 ) |
2119 | { |
2120 | char nbuf[BUFSIZE]; |
2121 | |
2122 | |
2123 | if ( !( p->flags & BDF_FONT_BBX_ ) ) |
2124 | { |
2125 | /* Missing the FONTBOUNDINGBOX field. */ |
2126 | FT_ERROR(( "bdf_parse_start_: " ERRMSG1, lineno, "FONTBOUNDINGBOX" )); |
2127 | error = FT_THROW( Missing_Fontboundingbox_Field ); |
2128 | goto Exit; |
2129 | } |
2130 | |
2131 | /* Add the two standard X11 properties which are required */ |
2132 | /* for compiling fonts. */ |
2133 | p->font->font_ascent = p->font->bbx.ascent; |
2134 | ft_snprintf( nbuf, BUFSIZE, "%hd" , p->font->bbx.ascent ); |
2135 | error = bdf_add_property_( p->font, "FONT_ASCENT" , |
2136 | nbuf, lineno ); |
2137 | if ( error ) |
2138 | goto Exit; |
2139 | FT_TRACE2(( "bdf_parse_properties_: " ACMSG1, p->font->bbx.ascent )); |
2140 | |
2141 | p->font->font_descent = p->font->bbx.descent; |
2142 | ft_snprintf( nbuf, BUFSIZE, "%hd" , p->font->bbx.descent ); |
2143 | error = bdf_add_property_( p->font, "FONT_DESCENT" , |
2144 | nbuf, lineno ); |
2145 | if ( error ) |
2146 | goto Exit; |
2147 | FT_TRACE2(( "bdf_parse_properties_: " ACMSG2, p->font->bbx.descent )); |
2148 | |
2149 | *next = bdf_parse_glyphs_; |
2150 | |
2151 | /* A special return value. */ |
2152 | error = -1; |
2153 | goto Exit; |
2154 | } |
2155 | |
2156 | FT_ERROR(( "bdf_parse_start_: " ERRMSG9, lineno )); |
2157 | error = FT_THROW( Invalid_File_Format ); |
2158 | |
2159 | Exit: |
2160 | return error; |
2161 | } |
2162 | |
2163 | |
2164 | /************************************************************************** |
2165 | * |
2166 | * API. |
2167 | * |
2168 | */ |
2169 | |
2170 | |
2171 | FT_LOCAL_DEF( FT_Error ) |
2172 | bdf_load_font( FT_Stream stream, |
2173 | FT_Memory memory, |
2174 | bdf_options_t* opts, |
2175 | bdf_font_t* *font ) |
2176 | { |
2177 | unsigned long lineno = 0; /* make compiler happy */ |
2178 | bdf_parse_t_ *p = NULL; |
2179 | |
2180 | FT_Error error = FT_Err_Ok; |
2181 | |
2182 | |
2183 | if ( FT_NEW( p ) ) |
2184 | goto Exit; |
2185 | |
2186 | p->opts = (bdf_options_t*)( opts ? opts : &bdf_opts_ ); |
2187 | p->minlb = 32767; |
2188 | p->size = stream->size; |
2189 | p->memory = memory; /* only during font creation */ |
2190 | |
2191 | bdf_list_init_( &p->list, memory ); |
2192 | |
2193 | error = bdf_readstream_( stream, bdf_parse_start_, |
2194 | (void *)p, &lineno ); |
2195 | if ( error ) |
2196 | goto Fail; |
2197 | |
2198 | if ( p->font ) |
2199 | { |
2200 | /* If the font is not proportional, set the font's monowidth */ |
2201 | /* field to the width of the font bounding box. */ |
2202 | |
2203 | if ( p->font->spacing != BDF_PROPORTIONAL ) |
2204 | p->font->monowidth = p->font->bbx.width; |
2205 | |
2206 | /* If the number of glyphs loaded is not that of the original count, */ |
2207 | /* indicate the difference. */ |
2208 | if ( p->cnt != p->font->glyphs_used + p->font->unencoded_used ) |
2209 | { |
2210 | FT_TRACE2(( "bdf_load_font: " ACMSG15, p->cnt, |
2211 | p->font->glyphs_used + p->font->unencoded_used )); |
2212 | } |
2213 | |
2214 | /* Once the font has been loaded, adjust the overall font metrics if */ |
2215 | /* necessary. */ |
2216 | if ( p->opts->correct_metrics != 0 && |
2217 | ( p->font->glyphs_used > 0 || p->font->unencoded_used > 0 ) ) |
2218 | { |
2219 | if ( p->maxrb - p->minlb != p->font->bbx.width ) |
2220 | { |
2221 | FT_TRACE2(( "bdf_load_font: " ACMSG3, |
2222 | p->font->bbx.width, p->maxrb - p->minlb )); |
2223 | p->font->bbx.width = (unsigned short)( p->maxrb - p->minlb ); |
2224 | } |
2225 | |
2226 | if ( p->font->bbx.x_offset != p->minlb ) |
2227 | { |
2228 | FT_TRACE2(( "bdf_load_font: " ACMSG4, |
2229 | p->font->bbx.x_offset, p->minlb )); |
2230 | p->font->bbx.x_offset = p->minlb; |
2231 | } |
2232 | |
2233 | if ( p->font->bbx.ascent != p->maxas ) |
2234 | { |
2235 | FT_TRACE2(( "bdf_load_font: " ACMSG5, |
2236 | p->font->bbx.ascent, p->maxas )); |
2237 | p->font->bbx.ascent = p->maxas; |
2238 | } |
2239 | |
2240 | if ( p->font->bbx.descent != p->maxds ) |
2241 | { |
2242 | FT_TRACE2(( "bdf_load_font: " ACMSG6, |
2243 | p->font->bbx.descent, p->maxds )); |
2244 | p->font->bbx.descent = p->maxds; |
2245 | p->font->bbx.y_offset = (short)( -p->maxds ); |
2246 | } |
2247 | |
2248 | if ( p->maxas + p->maxds != p->font->bbx.height ) |
2249 | { |
2250 | FT_TRACE2(( "bdf_load_font: " ACMSG7, |
2251 | p->font->bbx.height, p->maxas + p->maxds )); |
2252 | p->font->bbx.height = (unsigned short)( p->maxas + p->maxds ); |
2253 | } |
2254 | |
2255 | if ( p->flags & BDF_SWIDTH_ADJ_ ) |
2256 | FT_TRACE2(( "bdf_load_font: " ACMSG8 )); |
2257 | } |
2258 | } |
2259 | |
2260 | if ( p->flags & BDF_START_ ) |
2261 | { |
2262 | /* The ENDFONT field was never reached or did not exist. */ |
2263 | if ( !( p->flags & BDF_GLYPHS_ ) ) |
2264 | { |
2265 | /* Error happened while parsing header. */ |
2266 | FT_ERROR(( "bdf_load_font: " ERRMSG2, lineno )); |
2267 | error = FT_THROW( Corrupted_Font_Header ); |
2268 | goto Fail; |
2269 | } |
2270 | else |
2271 | { |
2272 | /* Error happened when parsing glyphs. */ |
2273 | FT_ERROR(( "bdf_load_font: " ERRMSG3, lineno )); |
2274 | error = FT_THROW( Corrupted_Font_Glyphs ); |
2275 | goto Fail; |
2276 | } |
2277 | } |
2278 | |
2279 | if ( !p->font && !error ) |
2280 | error = FT_THROW( Invalid_File_Format ); |
2281 | |
2282 | *font = p->font; |
2283 | |
2284 | Exit: |
2285 | if ( p ) |
2286 | { |
2287 | bdf_list_done_( &p->list ); |
2288 | |
2289 | FT_FREE( p->glyph_name ); |
2290 | FT_FREE( p ); |
2291 | } |
2292 | |
2293 | return error; |
2294 | |
2295 | Fail: |
2296 | bdf_free_font( p->font ); |
2297 | |
2298 | FT_FREE( p->font ); |
2299 | |
2300 | goto Exit; |
2301 | } |
2302 | |
2303 | |
2304 | FT_LOCAL_DEF( void ) |
2305 | bdf_free_font( bdf_font_t* font ) |
2306 | { |
2307 | bdf_property_t* prop; |
2308 | unsigned long i; |
2309 | bdf_glyph_t* glyphs; |
2310 | FT_Memory memory; |
2311 | |
2312 | |
2313 | if ( font == NULL ) |
2314 | return; |
2315 | |
2316 | memory = font->memory; |
2317 | |
2318 | FT_FREE( font->name ); |
2319 | |
2320 | /* Free up the internal hash table of property names. */ |
2321 | if ( font->internal ) |
2322 | { |
2323 | ft_hash_str_free( (FT_Hash)font->internal, memory ); |
2324 | FT_FREE( font->internal ); |
2325 | } |
2326 | |
2327 | /* Free up the comment info. */ |
2328 | FT_FREE( font->comments ); |
2329 | |
2330 | /* Free up the properties. */ |
2331 | for ( i = 0; i < font->props_size; i++ ) |
2332 | { |
2333 | if ( font->props[i].format == BDF_ATOM ) |
2334 | FT_FREE( font->props[i].value.atom ); |
2335 | } |
2336 | |
2337 | FT_FREE( font->props ); |
2338 | |
2339 | /* Free up the character info. */ |
2340 | for ( i = 0, glyphs = font->glyphs; |
2341 | i < font->glyphs_used; i++, glyphs++ ) |
2342 | { |
2343 | FT_FREE( glyphs->name ); |
2344 | FT_FREE( glyphs->bitmap ); |
2345 | } |
2346 | |
2347 | for ( i = 0, glyphs = font->unencoded; i < font->unencoded_used; |
2348 | i++, glyphs++ ) |
2349 | { |
2350 | FT_FREE( glyphs->name ); |
2351 | FT_FREE( glyphs->bitmap ); |
2352 | } |
2353 | |
2354 | FT_FREE( font->glyphs ); |
2355 | FT_FREE( font->unencoded ); |
2356 | |
2357 | /* bdf_cleanup */ |
2358 | ft_hash_str_free( &(font->proptbl), memory ); |
2359 | |
2360 | /* Free up the user defined properties. */ |
2361 | for ( prop = font->user_props, i = 0; |
2362 | i < font->nuser_props; i++, prop++ ) |
2363 | FT_FREE( prop->name ); |
2364 | |
2365 | FT_FREE( font->user_props ); |
2366 | |
2367 | /* FREE( font ); */ /* XXX Fixme */ |
2368 | } |
2369 | |
2370 | |
2371 | FT_LOCAL_DEF( bdf_property_t * ) |
2372 | bdf_get_font_property( bdf_font_t* font, |
2373 | const char* name ) |
2374 | { |
2375 | size_t* propid; |
2376 | |
2377 | |
2378 | if ( font == NULL || font->props_size == 0 || name == NULL || *name == 0 ) |
2379 | return 0; |
2380 | |
2381 | propid = ft_hash_str_lookup( name, (FT_Hash)font->internal ); |
2382 | |
2383 | return propid ? ( font->props + *propid ) : 0; |
2384 | } |
2385 | |
2386 | |
2387 | /* END */ |
2388 | |