1 | /**************************************************************************** |
2 | * |
3 | * pfrobjs.c |
4 | * |
5 | * FreeType PFR object methods (body). |
6 | * |
7 | * Copyright (C) 2002-2023 by |
8 | * David Turner, Robert Wilhelm, and Werner Lemberg. |
9 | * |
10 | * This file is part of the FreeType project, and may only be used, |
11 | * modified, and distributed under the terms of the FreeType project |
12 | * license, LICENSE.TXT. By continuing to use, modify, or distribute |
13 | * this file you indicate that you have read the license and |
14 | * understand and accept it fully. |
15 | * |
16 | */ |
17 | |
18 | |
19 | #include "pfrobjs.h" |
20 | #include "pfrload.h" |
21 | #include "pfrgload.h" |
22 | #include "pfrcmap.h" |
23 | #include "pfrsbit.h" |
24 | #include <freetype/ftoutln.h> |
25 | #include <freetype/internal/ftdebug.h> |
26 | #include <freetype/internal/ftcalc.h> |
27 | #include <freetype/ttnameid.h> |
28 | |
29 | #include "pfrerror.h" |
30 | |
31 | #undef FT_COMPONENT |
32 | #define FT_COMPONENT pfr |
33 | |
34 | |
35 | /*************************************************************************/ |
36 | /*************************************************************************/ |
37 | /***** *****/ |
38 | /***** FACE OBJECT METHODS *****/ |
39 | /***** *****/ |
40 | /*************************************************************************/ |
41 | /*************************************************************************/ |
42 | |
43 | FT_LOCAL_DEF( void ) |
44 | pfr_face_done( FT_Face pfrface ) /* PFR_Face */ |
45 | { |
46 | PFR_Face face = (PFR_Face)pfrface; |
47 | FT_Memory memory; |
48 | |
49 | |
50 | if ( !face ) |
51 | return; |
52 | |
53 | memory = pfrface->memory; |
54 | |
55 | /* we don't want dangling pointers */ |
56 | pfrface->family_name = NULL; |
57 | pfrface->style_name = NULL; |
58 | |
59 | /* finalize the physical font record */ |
60 | pfr_phy_font_done( &face->phy_font, memory ); |
61 | |
62 | /* no need to finalize the logical font or the header */ |
63 | FT_FREE( pfrface->available_sizes ); |
64 | } |
65 | |
66 | |
67 | FT_LOCAL_DEF( FT_Error ) |
68 | pfr_face_init( FT_Stream stream, |
69 | FT_Face pfrface, |
70 | FT_Int face_index, |
71 | FT_Int num_params, |
72 | FT_Parameter* params ) |
73 | { |
74 | PFR_Face face = (PFR_Face)pfrface; |
75 | FT_Error error; |
76 | |
77 | FT_UNUSED( num_params ); |
78 | FT_UNUSED( params ); |
79 | |
80 | |
81 | FT_TRACE2(( "PFR driver\n" )); |
82 | |
83 | /* load the header and check it */ |
84 | error = pfr_header_load( &face->header, stream ); |
85 | if ( error ) |
86 | { |
87 | FT_TRACE2(( " not a PFR font\n" )); |
88 | error = FT_THROW( Unknown_File_Format ); |
89 | goto Exit; |
90 | } |
91 | |
92 | if ( !pfr_header_check( &face->header ) ) |
93 | { |
94 | FT_TRACE2(( " not a PFR font\n" )); |
95 | error = FT_THROW( Unknown_File_Format ); |
96 | goto Exit; |
97 | } |
98 | |
99 | /* check face index */ |
100 | { |
101 | FT_Long num_faces; |
102 | |
103 | |
104 | error = pfr_log_font_count( stream, |
105 | face->header.log_dir_offset, |
106 | &num_faces ); |
107 | if ( error ) |
108 | goto Exit; |
109 | |
110 | pfrface->num_faces = num_faces; |
111 | } |
112 | |
113 | if ( face_index < 0 ) |
114 | goto Exit; |
115 | |
116 | if ( ( face_index & 0xFFFF ) >= pfrface->num_faces ) |
117 | { |
118 | FT_ERROR(( "pfr_face_init: invalid face index\n" )); |
119 | error = FT_THROW( Invalid_Argument ); |
120 | goto Exit; |
121 | } |
122 | |
123 | /* load the face */ |
124 | error = pfr_log_font_load( |
125 | &face->log_font, |
126 | stream, |
127 | (FT_UInt)( face_index & 0xFFFF ), |
128 | face->header.log_dir_offset, |
129 | FT_BOOL( face->header.phy_font_max_size_high ) ); |
130 | if ( error ) |
131 | goto Exit; |
132 | |
133 | /* load the physical font descriptor */ |
134 | error = pfr_phy_font_load( &face->phy_font, stream, |
135 | face->log_font.phys_offset, |
136 | face->log_font.phys_size ); |
137 | if ( error ) |
138 | goto Exit; |
139 | |
140 | /* set up all root face fields */ |
141 | { |
142 | PFR_PhyFont phy_font = &face->phy_font; |
143 | |
144 | |
145 | pfrface->face_index = face_index & 0xFFFF; |
146 | pfrface->num_glyphs = (FT_Long)phy_font->num_chars + 1; |
147 | |
148 | pfrface->face_flags |= FT_FACE_FLAG_SCALABLE; |
149 | |
150 | /* if gps_offset == 0 for all characters, we */ |
151 | /* assume that the font only contains bitmaps */ |
152 | { |
153 | FT_UInt nn; |
154 | |
155 | |
156 | for ( nn = 0; nn < phy_font->num_chars; nn++ ) |
157 | if ( phy_font->chars[nn].gps_offset != 0 ) |
158 | break; |
159 | |
160 | if ( nn == phy_font->num_chars ) |
161 | { |
162 | if ( phy_font->num_strikes > 0 ) |
163 | pfrface->face_flags &= ~FT_FACE_FLAG_SCALABLE; |
164 | else |
165 | { |
166 | FT_ERROR(( "pfr_face_init: font doesn't contain glyphs\n" )); |
167 | error = FT_THROW( Invalid_File_Format ); |
168 | goto Exit; |
169 | } |
170 | } |
171 | } |
172 | |
173 | if ( !( phy_font->flags & PFR_PHY_PROPORTIONAL ) ) |
174 | pfrface->face_flags |= FT_FACE_FLAG_FIXED_WIDTH; |
175 | |
176 | if ( phy_font->flags & PFR_PHY_VERTICAL ) |
177 | pfrface->face_flags |= FT_FACE_FLAG_VERTICAL; |
178 | else |
179 | pfrface->face_flags |= FT_FACE_FLAG_HORIZONTAL; |
180 | |
181 | if ( phy_font->num_strikes > 0 ) |
182 | pfrface->face_flags |= FT_FACE_FLAG_FIXED_SIZES; |
183 | |
184 | if ( phy_font->num_kern_pairs > 0 ) |
185 | pfrface->face_flags |= FT_FACE_FLAG_KERNING; |
186 | |
187 | /* If no family name was found in the `undocumented' auxiliary |
188 | * data, use the font ID instead. This sucks but is better than |
189 | * nothing. |
190 | */ |
191 | pfrface->family_name = phy_font->family_name; |
192 | if ( !pfrface->family_name ) |
193 | pfrface->family_name = phy_font->font_id; |
194 | |
195 | /* note that the style name can be NULL in certain PFR fonts, |
196 | * probably meaning `Regular' |
197 | */ |
198 | pfrface->style_name = phy_font->style_name; |
199 | |
200 | pfrface->num_fixed_sizes = 0; |
201 | pfrface->available_sizes = NULL; |
202 | |
203 | pfrface->bbox = phy_font->bbox; |
204 | pfrface->units_per_EM = (FT_UShort)phy_font->outline_resolution; |
205 | pfrface->ascender = (FT_Short) phy_font->bbox.yMax; |
206 | pfrface->descender = (FT_Short) phy_font->bbox.yMin; |
207 | |
208 | pfrface->height = (FT_Short)( ( pfrface->units_per_EM * 12 ) / 10 ); |
209 | if ( pfrface->height < pfrface->ascender - pfrface->descender ) |
210 | pfrface->height = (FT_Short)( pfrface->ascender - pfrface->descender ); |
211 | |
212 | if ( phy_font->num_strikes > 0 ) |
213 | { |
214 | FT_UInt n, count = phy_font->num_strikes; |
215 | FT_Bitmap_Size* size; |
216 | PFR_Strike strike; |
217 | FT_Memory memory = pfrface->memory; |
218 | |
219 | |
220 | if ( FT_QNEW_ARRAY( pfrface->available_sizes, count ) ) |
221 | goto Exit; |
222 | |
223 | size = pfrface->available_sizes; |
224 | strike = phy_font->strikes; |
225 | for ( n = 0; n < count; n++, size++, strike++ ) |
226 | { |
227 | size->height = (FT_Short)strike->y_ppm; |
228 | size->width = (FT_Short)strike->x_ppm; |
229 | size->size = (FT_Pos)( strike->y_ppm << 6 ); |
230 | size->x_ppem = (FT_Pos)( strike->x_ppm << 6 ); |
231 | size->y_ppem = (FT_Pos)( strike->y_ppm << 6 ); |
232 | } |
233 | pfrface->num_fixed_sizes = (FT_Int)count; |
234 | } |
235 | |
236 | /* now compute maximum advance width */ |
237 | if ( ( phy_font->flags & PFR_PHY_PROPORTIONAL ) == 0 ) |
238 | pfrface->max_advance_width = (FT_Short)phy_font->standard_advance; |
239 | else |
240 | { |
241 | FT_Int max = 0; |
242 | FT_UInt count = phy_font->num_chars; |
243 | PFR_Char gchar = phy_font->chars; |
244 | |
245 | |
246 | for ( ; count > 0; count--, gchar++ ) |
247 | { |
248 | if ( max < gchar->advance ) |
249 | max = gchar->advance; |
250 | } |
251 | |
252 | pfrface->max_advance_width = (FT_Short)max; |
253 | } |
254 | |
255 | pfrface->max_advance_height = pfrface->height; |
256 | |
257 | pfrface->underline_position = (FT_Short)( -pfrface->units_per_EM / 10 ); |
258 | pfrface->underline_thickness = (FT_Short)( pfrface->units_per_EM / 30 ); |
259 | |
260 | /* create charmap */ |
261 | { |
262 | FT_CharMapRec charmap; |
263 | |
264 | |
265 | charmap.face = pfrface; |
266 | charmap.platform_id = TT_PLATFORM_MICROSOFT; |
267 | charmap.encoding_id = TT_MS_ID_UNICODE_CS; |
268 | charmap.encoding = FT_ENCODING_UNICODE; |
269 | |
270 | error = FT_CMap_New( &pfr_cmap_class_rec, NULL, &charmap, NULL ); |
271 | } |
272 | |
273 | /* check whether we have loaded any kerning pairs */ |
274 | if ( phy_font->num_kern_pairs ) |
275 | pfrface->face_flags |= FT_FACE_FLAG_KERNING; |
276 | } |
277 | |
278 | Exit: |
279 | return error; |
280 | } |
281 | |
282 | |
283 | /*************************************************************************/ |
284 | /*************************************************************************/ |
285 | /***** *****/ |
286 | /***** SLOT OBJECT METHOD *****/ |
287 | /***** *****/ |
288 | /*************************************************************************/ |
289 | /*************************************************************************/ |
290 | |
291 | FT_LOCAL_DEF( FT_Error ) |
292 | pfr_slot_init( FT_GlyphSlot pfrslot ) /* PFR_Slot */ |
293 | { |
294 | PFR_Slot slot = (PFR_Slot)pfrslot; |
295 | FT_GlyphLoader loader = pfrslot->internal->loader; |
296 | |
297 | |
298 | pfr_glyph_init( &slot->glyph, loader ); |
299 | |
300 | return 0; |
301 | } |
302 | |
303 | |
304 | FT_LOCAL_DEF( void ) |
305 | pfr_slot_done( FT_GlyphSlot pfrslot ) /* PFR_Slot */ |
306 | { |
307 | PFR_Slot slot = (PFR_Slot)pfrslot; |
308 | |
309 | |
310 | pfr_glyph_done( &slot->glyph ); |
311 | } |
312 | |
313 | |
314 | FT_LOCAL_DEF( FT_Error ) |
315 | pfr_slot_load( FT_GlyphSlot pfrslot, /* PFR_Slot */ |
316 | FT_Size pfrsize, /* PFR_Size */ |
317 | FT_UInt gindex, |
318 | FT_Int32 load_flags ) |
319 | { |
320 | PFR_Slot slot = (PFR_Slot)pfrslot; |
321 | PFR_Size size = (PFR_Size)pfrsize; |
322 | FT_Error error; |
323 | PFR_Face face = (PFR_Face)pfrslot->face; |
324 | PFR_Char gchar; |
325 | FT_Outline* outline = &pfrslot->outline; |
326 | FT_ULong gps_offset; |
327 | |
328 | |
329 | FT_TRACE1(( "pfr_slot_load: glyph index %d\n" , gindex )); |
330 | |
331 | if ( gindex > 0 ) |
332 | gindex--; |
333 | |
334 | if ( !face || gindex >= face->phy_font.num_chars ) |
335 | { |
336 | error = FT_THROW( Invalid_Argument ); |
337 | goto Exit; |
338 | } |
339 | |
340 | /* try to load an embedded bitmap */ |
341 | if ( !( load_flags & ( FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ) ) ) |
342 | { |
343 | error = pfr_slot_load_bitmap( |
344 | slot, |
345 | size, |
346 | gindex, |
347 | ( load_flags & FT_LOAD_BITMAP_METRICS_ONLY ) != 0 ); |
348 | if ( !error ) |
349 | goto Exit; |
350 | } |
351 | |
352 | if ( load_flags & FT_LOAD_SBITS_ONLY ) |
353 | { |
354 | error = FT_THROW( Invalid_Argument ); |
355 | goto Exit; |
356 | } |
357 | |
358 | gchar = face->phy_font.chars + gindex; |
359 | pfrslot->format = FT_GLYPH_FORMAT_OUTLINE; |
360 | outline->n_points = 0; |
361 | outline->n_contours = 0; |
362 | gps_offset = face->header.gps_section_offset; |
363 | |
364 | /* load the glyph outline (FT_LOAD_NO_RECURSE isn't supported) */ |
365 | error = pfr_glyph_load( &slot->glyph, face->root.stream, |
366 | gps_offset, gchar->gps_offset, gchar->gps_size ); |
367 | |
368 | if ( !error ) |
369 | { |
370 | FT_BBox cbox; |
371 | FT_Glyph_Metrics* metrics = &pfrslot->metrics; |
372 | FT_Pos advance; |
373 | FT_UInt em_metrics, em_outline; |
374 | FT_Bool scaling; |
375 | |
376 | |
377 | scaling = FT_BOOL( !( load_flags & FT_LOAD_NO_SCALE ) ); |
378 | |
379 | /* copy outline data */ |
380 | *outline = slot->glyph.loader->base.outline; |
381 | |
382 | outline->flags &= ~FT_OUTLINE_OWNER; |
383 | outline->flags |= FT_OUTLINE_REVERSE_FILL; |
384 | |
385 | if ( pfrsize->metrics.y_ppem < 24 ) |
386 | outline->flags |= FT_OUTLINE_HIGH_PRECISION; |
387 | |
388 | /* compute the advance vector */ |
389 | metrics->horiAdvance = 0; |
390 | metrics->vertAdvance = 0; |
391 | |
392 | advance = gchar->advance; |
393 | em_metrics = face->phy_font.metrics_resolution; |
394 | em_outline = face->phy_font.outline_resolution; |
395 | |
396 | if ( em_metrics != em_outline ) |
397 | advance = FT_MulDiv( advance, |
398 | (FT_Long)em_outline, |
399 | (FT_Long)em_metrics ); |
400 | |
401 | if ( face->phy_font.flags & PFR_PHY_VERTICAL ) |
402 | metrics->vertAdvance = advance; |
403 | else |
404 | metrics->horiAdvance = advance; |
405 | |
406 | pfrslot->linearHoriAdvance = metrics->horiAdvance; |
407 | pfrslot->linearVertAdvance = metrics->vertAdvance; |
408 | |
409 | /* make up vertical metrics(?) */ |
410 | metrics->vertBearingX = 0; |
411 | metrics->vertBearingY = 0; |
412 | |
413 | #if 0 /* some fonts seem to be broken here! */ |
414 | |
415 | /* Apply the font matrix, if any. */ |
416 | /* TODO: Test existing fonts with unusual matrix */ |
417 | /* whether we have to adjust Units per EM. */ |
418 | { |
419 | FT_Matrix font_matrix; |
420 | |
421 | |
422 | font_matrix.xx = face->log_font.matrix[0] << 8; |
423 | font_matrix.yx = face->log_font.matrix[1] << 8; |
424 | font_matrix.xy = face->log_font.matrix[2] << 8; |
425 | font_matrix.yy = face->log_font.matrix[3] << 8; |
426 | |
427 | FT_Outline_Transform( outline, &font_matrix ); |
428 | } |
429 | #endif |
430 | |
431 | /* scale when needed */ |
432 | if ( scaling ) |
433 | { |
434 | FT_Int n; |
435 | FT_Fixed x_scale = pfrsize->metrics.x_scale; |
436 | FT_Fixed y_scale = pfrsize->metrics.y_scale; |
437 | FT_Vector* vec = outline->points; |
438 | |
439 | |
440 | /* scale outline points */ |
441 | for ( n = 0; n < outline->n_points; n++, vec++ ) |
442 | { |
443 | vec->x = FT_MulFix( vec->x, x_scale ); |
444 | vec->y = FT_MulFix( vec->y, y_scale ); |
445 | } |
446 | |
447 | /* scale the advance */ |
448 | metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale ); |
449 | metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale ); |
450 | } |
451 | |
452 | /* compute the rest of the metrics */ |
453 | FT_Outline_Get_CBox( outline, &cbox ); |
454 | |
455 | metrics->width = cbox.xMax - cbox.xMin; |
456 | metrics->height = cbox.yMax - cbox.yMin; |
457 | metrics->horiBearingX = cbox.xMin; |
458 | metrics->horiBearingY = cbox.yMax - metrics->height; |
459 | } |
460 | |
461 | Exit: |
462 | return error; |
463 | } |
464 | |
465 | |
466 | /*************************************************************************/ |
467 | /*************************************************************************/ |
468 | /***** *****/ |
469 | /***** KERNING METHOD *****/ |
470 | /***** *****/ |
471 | /*************************************************************************/ |
472 | /*************************************************************************/ |
473 | |
474 | FT_LOCAL_DEF( FT_Error ) |
475 | pfr_face_get_kerning( FT_Face pfrface, /* PFR_Face */ |
476 | FT_UInt glyph1, |
477 | FT_UInt glyph2, |
478 | FT_Vector* kerning ) |
479 | { |
480 | PFR_Face face = (PFR_Face)pfrface; |
481 | FT_Error error = FT_Err_Ok; |
482 | PFR_PhyFont phy_font = &face->phy_font; |
483 | FT_UInt32 code1, code2, pair; |
484 | |
485 | |
486 | kerning->x = 0; |
487 | kerning->y = 0; |
488 | |
489 | /* PFR indexing skips .notdef, which becomes UINT_MAX */ |
490 | glyph1--; |
491 | glyph2--; |
492 | |
493 | /* check the array bounds, .notdef is automatically out */ |
494 | if ( glyph1 >= phy_font->num_chars || |
495 | glyph2 >= phy_font->num_chars ) |
496 | goto Exit; |
497 | |
498 | /* convert glyph indices to character codes */ |
499 | code1 = phy_font->chars[glyph1].char_code; |
500 | code2 = phy_font->chars[glyph2].char_code; |
501 | pair = PFR_KERN_INDEX( code1, code2 ); |
502 | |
503 | /* now search the list of kerning items */ |
504 | { |
505 | PFR_KernItem item = phy_font->kern_items; |
506 | FT_Stream stream = pfrface->stream; |
507 | |
508 | |
509 | for ( ; item; item = item->next ) |
510 | { |
511 | if ( pair >= item->pair1 && pair <= item->pair2 ) |
512 | goto FoundPair; |
513 | } |
514 | goto Exit; |
515 | |
516 | FoundPair: /* we found an item, now parse it and find the value if any */ |
517 | if ( FT_STREAM_SEEK( item->offset ) || |
518 | FT_FRAME_ENTER( item->pair_count * item->pair_size ) ) |
519 | goto Exit; |
520 | |
521 | { |
522 | FT_UInt count = item->pair_count; |
523 | FT_UInt size = item->pair_size; |
524 | FT_UInt power = 1 << FT_MSB( count ); |
525 | FT_UInt probe = power * size; |
526 | FT_UInt = count - power; |
527 | FT_Byte* base = stream->cursor; |
528 | FT_Bool twobytes = FT_BOOL( item->flags & PFR_KERN_2BYTE_CHAR ); |
529 | FT_Bool twobyte_adj = FT_BOOL( item->flags & PFR_KERN_2BYTE_ADJ ); |
530 | FT_Byte* p; |
531 | FT_UInt32 cpair; |
532 | |
533 | |
534 | if ( extra > 0 ) |
535 | { |
536 | p = base + extra * size; |
537 | |
538 | if ( twobytes ) |
539 | cpair = FT_NEXT_ULONG( p ); |
540 | else |
541 | cpair = PFR_NEXT_KPAIR( p ); |
542 | |
543 | if ( cpair == pair ) |
544 | goto Found; |
545 | |
546 | if ( cpair < pair ) |
547 | { |
548 | if ( twobyte_adj ) |
549 | p += 2; |
550 | else |
551 | p++; |
552 | base = p; |
553 | } |
554 | } |
555 | |
556 | while ( probe > size ) |
557 | { |
558 | probe >>= 1; |
559 | p = base + probe; |
560 | |
561 | if ( twobytes ) |
562 | cpair = FT_NEXT_ULONG( p ); |
563 | else |
564 | cpair = PFR_NEXT_KPAIR( p ); |
565 | |
566 | if ( cpair == pair ) |
567 | goto Found; |
568 | |
569 | if ( cpair < pair ) |
570 | base += probe; |
571 | } |
572 | |
573 | p = base; |
574 | |
575 | if ( twobytes ) |
576 | cpair = FT_NEXT_ULONG( p ); |
577 | else |
578 | cpair = PFR_NEXT_KPAIR( p ); |
579 | |
580 | if ( cpair == pair ) |
581 | { |
582 | FT_Int value; |
583 | |
584 | |
585 | Found: |
586 | if ( twobyte_adj ) |
587 | value = FT_PEEK_SHORT( p ); |
588 | else |
589 | value = p[0]; |
590 | |
591 | kerning->x = item->base_adj + value; |
592 | } |
593 | } |
594 | |
595 | FT_FRAME_EXIT(); |
596 | } |
597 | |
598 | Exit: |
599 | return error; |
600 | } |
601 | |
602 | |
603 | /* END */ |
604 | |