Fix OTS warning about `maxp.maxSizeOfInstructions`.
[ttfautohint.git] / lib / taloader.c
blob9de65bc44181fe831a65c6555bf88bc7b9488b5c
1 /* taloader.c */
3 /*
4 * Copyright (C) 2011-2022 by Werner Lemberg.
6 * This file is part of the ttfautohint library, and may only be used,
7 * modified, and distributed under the terms given in `COPYING'. By
8 * continuing to use, modify, or distribute this file you indicate that you
9 * have read `COPYING' and understand and accept it fully.
11 * The file `COPYING' mentioned in the previous paragraph is distributed
12 * with the ttfautohint library.
16 /* originally file `afloader.c' (2011-Mar-28) from FreeType */
18 /* heavily modified 2011 by Werner Lemberg <wl@gnu.org> */
20 #include <string.h>
22 #include <ft2build.h>
23 #include FT_GLYPH_H
25 #include "ta.h"
26 #include "tahints.h"
27 #include "taglobal.h"
30 /* from file `ftobjs.h' (2011-Mar-28) from FreeType */
31 #if 0
32 typedef struct FT_Slot_InternalRec_
34 TA_GlyphLoader loader;
35 FT_UInt flags;
36 FT_Bool glyph_transformed;
37 FT_Matrix glyph_matrix;
38 FT_Vector glyph_delta;
39 void* glyph_hints;
40 } FT_GlyphSlot_InternalRec;
41 #endif
44 /* initialize glyph loader */
46 FT_Error
47 ta_loader_init(FONT* font)
49 TA_Loader loader = font->loader;
52 memset(loader, 0, sizeof (TA_LoaderRec));
54 ta_glyph_hints_init(&loader->hints);
55 #ifdef TA_DEBUG
56 _ta_debug_hints = &loader->hints;
57 #endif
58 return TA_GlyphLoader_New(&loader->gloader);
62 /* reset glyph loader and compute globals if necessary */
64 FT_Error
65 ta_loader_reset(FONT* font,
66 FT_Face face)
68 FT_Error error = FT_Err_Ok;
69 TA_Loader loader = font->loader;
72 loader->face = face;
73 loader->globals = (TA_FaceGlobals)face->autohint.data;
75 TA_GlyphLoader_Rewind(loader->gloader);
77 if (!loader->globals)
79 error = ta_face_globals_new(face, &loader->globals, font);
80 if (!error)
82 face->autohint.data = (FT_Pointer)loader->globals;
83 face->autohint.finalizer = (FT_Generic_Finalizer)ta_face_globals_free;
87 return error;
91 /* finalize glyph loader */
93 void
94 ta_loader_done(FONT* font)
96 TA_Loader loader = font->loader;
99 ta_glyph_hints_done(&loader->hints);
101 loader->face = NULL;
102 loader->globals = NULL;
104 #ifdef TA_DEBUG
105 _ta_debug_hints = NULL;
106 #endif
107 TA_GlyphLoader_Done(loader->gloader);
108 loader->gloader = NULL;
112 /* load a single glyph component; this routine calls itself recursively, */
113 /* if necessary, and does the main work of `ta_loader_load_glyph' */
115 static FT_Error
116 ta_loader_load_g(TA_Loader loader,
117 TA_Scaler scaler,
118 FT_UInt glyph_index,
119 FT_Int32 load_flags,
120 FT_UInt depth)
122 FT_Error error;
123 FT_Face face = loader->face;
124 TA_GlyphLoader gloader = loader->gloader;
125 TA_StyleMetrics metrics = loader->metrics;
126 TA_GlyphHints hints = &loader->hints;
127 FT_GlyphSlot slot = face->glyph;
128 #if 0
129 FT_Slot_Internal slot_internal = slot->internal;
130 #endif
131 FT_Int32 flags;
134 flags = load_flags | FT_LOAD_LINEAR_DESIGN;
135 error = FT_Load_Glyph(face, glyph_index, flags);
136 if (error)
137 goto Exit;
139 #if 0
140 loader->transformed = slot_internal->glyph_transformed;
141 if (loader->transformed)
143 FT_Matrix inverse;
146 loader->trans_matrix = slot_internal->glyph_matrix;
147 loader->trans_delta = slot_internal->glyph_delta;
149 inverse = loader->trans_matrix;
150 if (!FT_Matrix_Invert(&inverse))
151 FT_Vector_Transform(&loader->trans_delta, &inverse);
153 #endif
155 switch (slot->format)
157 case FT_GLYPH_FORMAT_OUTLINE:
158 /* translate the loaded glyph when an internal transform is needed */
159 if (loader->transformed)
160 FT_Outline_Translate(&slot->outline,
161 loader->trans_delta.x,
162 loader->trans_delta.y);
164 /* copy the outline points in the loader's current extra points */
165 /* which are used to keep original glyph coordinates */
166 error = TA_GLYPHLOADER_CHECK_POINTS(gloader,
167 slot->outline.n_points + 4,
168 slot->outline.n_contours);
169 if (error)
170 goto Exit;
172 memcpy(gloader->current.outline.points,
173 slot->outline.points,
174 (size_t)slot->outline.n_points * sizeof (FT_Vector));
175 memcpy(gloader->current.outline.contours,
176 slot->outline.contours,
177 (size_t)slot->outline.n_contours * sizeof (short));
178 memcpy(gloader->current.outline.tags,
179 slot->outline.tags,
180 (size_t)slot->outline.n_points * sizeof (char));
182 gloader->current.outline.n_points = slot->outline.n_points;
183 gloader->current.outline.n_contours = slot->outline.n_contours;
185 /* compute original horizontal phantom points */
186 /* (and ignore vertical ones) */
187 loader->pp1.x = hints->x_delta;
188 loader->pp1.y = hints->y_delta;
189 loader->pp2.x = FT_MulFix(slot->metrics.horiAdvance,
190 hints->x_scale) + hints->x_delta;
191 loader->pp2.y = hints->y_delta;
193 /* be sure to check for spacing glyphs */
194 if (slot->outline.n_points == 0)
195 goto Hint_Metrics;
197 /* now load the slot image into the auto-outline */
198 /* and run the automatic hinting process */
200 TA_StyleClass style_class = metrics->style_class;
201 TA_WritingSystemClass writing_system_class =
202 ta_writing_system_classes[style_class->writing_system];
205 if (writing_system_class->style_hints_apply)
206 writing_system_class->style_hints_apply(glyph_index,
207 hints,
208 &gloader->current.outline,
209 metrics);
212 /* we now need to adjust the metrics according to the change in */
213 /* width/positioning that occurred during the hinting process */
214 if (scaler->render_mode != FT_RENDER_MODE_LIGHT)
216 TA_AxisHints axis = &hints->axis[TA_DIMENSION_HORZ];
219 if (axis->num_edges > 1 && TA_HINTS_DO_ADVANCE(hints))
221 TA_Edge edge1 = axis->edges; /* leftmost edge */
222 TA_Edge edge2 = edge1 +
223 axis->num_edges - 1; /* rightmost edge */
225 FT_Pos old_rsb = loader->pp2.x - edge2->opos;
226 /* loader->pp1.x is always zero at this point of time */
227 FT_Pos old_lsb = edge1->opos /* - loader->pp1.x */;
228 FT_Pos new_lsb = edge1->pos;
230 /* remember unhinted values to later account */
231 /* for rounding errors */
232 FT_Pos pp1x_uh = new_lsb - old_lsb;
233 FT_Pos pp2x_uh = edge2->pos + old_rsb;
236 /* prefer too much space over too little space */
237 /* for very small sizes */
239 if (old_lsb < 24)
240 pp1x_uh -= 8;
241 if (old_rsb < 24)
242 pp2x_uh += 8;
244 loader->pp1.x = TA_PIX_ROUND(pp1x_uh);
245 loader->pp2.x = TA_PIX_ROUND(pp2x_uh);
247 if (loader->pp1.x >= new_lsb
248 && old_lsb > 0)
249 loader->pp1.x -= 64;
250 if (loader->pp2.x <= edge2->pos
251 && old_rsb > 0)
252 loader->pp2.x += 64;
254 slot->lsb_delta = loader->pp1.x - pp1x_uh;
255 slot->rsb_delta = loader->pp2.x - pp2x_uh;
257 else
259 FT_Pos pp1x = loader->pp1.x;
260 FT_Pos pp2x = loader->pp2.x;
263 loader->pp1.x = TA_PIX_ROUND(pp1x + hints->xmin_delta);
264 loader->pp2.x = TA_PIX_ROUND(pp2x + hints->xmax_delta);
266 slot->lsb_delta = loader->pp1.x - pp1x;
267 slot->rsb_delta = loader->pp2.x - pp2x;
270 /* `light' mode uses integer advance widths */
271 /* but sets `lsb_delta' and `rsb_delta' */
272 else
274 FT_Pos pp1x = loader->pp1.x;
275 FT_Pos pp2x = loader->pp2.x;
278 loader->pp1.x = TA_PIX_ROUND(pp1x);
279 loader->pp2.x = TA_PIX_ROUND(pp2x);
281 slot->lsb_delta = loader->pp1.x - pp1x;
282 slot->rsb_delta = loader->pp2.x - pp2x;
285 /* good, we simply add the glyph to our loader's base */
286 TA_GlyphLoader_Add(gloader);
287 break;
289 case FT_GLYPH_FORMAT_COMPOSITE:
291 FT_UInt nn, num_subglyphs = slot->num_subglyphs;
292 FT_UInt num_base_subgs, start_point;
293 TA_SubGlyph subglyph;
296 start_point = (FT_UInt)gloader->base.outline.n_points;
298 /* first of all, copy the subglyph descriptors in the glyph loader */
299 error = TA_GlyphLoader_CheckSubGlyphs(gloader, num_subglyphs);
300 if (error)
301 goto Exit;
303 memcpy(gloader->current.subglyphs,
304 slot->subglyphs,
305 num_subglyphs * sizeof (TA_SubGlyphRec));
307 gloader->current.num_subglyphs = num_subglyphs;
309 /* stop processing if list of composites is requested */
310 if ( load_flags & FT_LOAD_NO_RECURSE )
311 goto Exit;
313 num_base_subgs = gloader->base.num_subglyphs;
315 /* now read each subglyph independently */
316 for (nn = 0; nn < num_subglyphs; nn++)
318 FT_Vector pp1, pp2;
319 FT_Pos x, y;
320 FT_UInt num_points, num_new_points, num_base_points;
323 /* gloader.current.subglyphs can change during glyph loading due */
324 /* to re-allocation -- we must recompute the current subglyph on */
325 /* each iteration */
326 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
328 pp1 = loader->pp1;
329 pp2 = loader->pp2;
331 num_base_points = (FT_UInt)gloader->base.outline.n_points;
333 error = ta_loader_load_g(loader, scaler, (FT_UInt)subglyph->index,
334 load_flags, depth + 1);
335 if (error)
336 goto Exit;
338 /* recompute subglyph pointer */
339 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
341 if (!(subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS))
343 loader->pp1 = pp1;
344 loader->pp2 = pp2;
347 num_points = (FT_UInt)gloader->base.outline.n_points;
348 num_new_points = num_points - num_base_points;
350 /* now perform the transformation required for this subglyph */
351 if (subglyph->flags & (FT_SUBGLYPH_FLAG_SCALE
352 | FT_SUBGLYPH_FLAG_XY_SCALE
353 | FT_SUBGLYPH_FLAG_2X2))
355 FT_Vector* cur = gloader->base.outline.points + num_base_points;
356 FT_Vector* limit = cur + num_new_points;
359 for (; cur < limit; cur++)
360 FT_Vector_Transform(cur, &subglyph->transform);
363 /* apply offset */
364 if (!(subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES))
366 FT_UInt k = (FT_UInt)subglyph->arg1;
367 FT_UInt l = (FT_UInt)subglyph->arg2;
368 FT_Vector* p1;
369 FT_Vector* p2;
372 if (start_point + k >= num_base_points
373 || l >= num_new_points)
375 error = FT_Err_Invalid_Composite;
376 goto Exit;
379 l += num_base_points;
381 /* for now, only use the current point coordinates; */
382 /* we eventually may consider another approach */
383 p1 = gloader->base.outline.points + start_point + k;
384 p2 = gloader->base.outline.points + start_point + l;
386 x = p1->x - p2->x;
387 y = p1->y - p2->y;
389 else
391 x = FT_MulFix(subglyph->arg1, hints->x_scale) + hints->x_delta;
392 y = FT_MulFix(subglyph->arg2, hints->y_scale) + hints->y_delta;
394 x = TA_PIX_ROUND(x);
395 y = TA_PIX_ROUND(y);
399 FT_Outline dummy = gloader->base.outline;
402 dummy.points += num_base_points;
403 dummy.n_points = (short)num_new_points;
405 FT_Outline_Translate(&dummy, x, y);
409 break;
411 default:
412 /* we don't support other formats (yet?) */
413 error = FT_Err_Unimplemented_Feature;
416 Hint_Metrics:
417 if (depth == 0)
419 FT_BBox bbox;
420 FT_Vector vvector;
423 vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX;
424 vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY;
425 vvector.x = FT_MulFix(vvector.x, metrics->scaler.x_scale);
426 vvector.y = FT_MulFix(vvector.y, metrics->scaler.y_scale);
428 /* transform the hinted outline if needed */
429 if (loader->transformed)
431 FT_Outline_Transform(&gloader->base.outline, &loader->trans_matrix);
432 FT_Vector_Transform(&vvector, &loader->trans_matrix);
434 #if 1
435 /* we must translate our final outline by -pp1.x */
436 /* and compute the new metrics */
437 if (loader->pp1.x)
438 FT_Outline_Translate(&gloader->base.outline, -loader->pp1.x, 0);
439 #endif
440 FT_Outline_Get_CBox(&gloader->base.outline, &bbox);
442 bbox.xMin = TA_PIX_FLOOR(bbox.xMin);
443 bbox.yMin = TA_PIX_FLOOR(bbox.yMin);
444 bbox.xMax = TA_PIX_CEIL(bbox.xMax);
445 bbox.yMax = TA_PIX_CEIL(bbox.yMax);
447 slot->metrics.width = bbox.xMax - bbox.xMin;
448 slot->metrics.height = bbox.yMax - bbox.yMin;
449 slot->metrics.horiBearingX = bbox.xMin;
450 slot->metrics.horiBearingY = bbox.yMax;
452 slot->metrics.vertBearingX = TA_PIX_FLOOR(bbox.xMin + vvector.x);
453 slot->metrics.vertBearingY = TA_PIX_FLOOR(bbox.yMax + vvector.y);
455 /* for mono-width fonts (like Andale, Courier, etc.) we need */
456 /* to keep the original rounded advance width; ditto for */
457 /* digits if all have the same advance width */
458 if (scaler->render_mode != FT_RENDER_MODE_LIGHT
459 && (FT_IS_FIXED_WIDTH(slot->face)
460 || (ta_face_globals_is_digit(loader->globals, glyph_index)
461 && metrics->digits_have_same_width)))
463 slot->metrics.horiAdvance = FT_MulFix(slot->metrics.horiAdvance,
464 metrics->scaler.x_scale);
466 /* set delta values to 0, otherwise code that uses them */
467 /* is going to ruin the fixed advance width */
468 slot->lsb_delta = 0;
469 slot->rsb_delta = 0;
471 else
473 /* non-spacing glyphs must stay as-is */
474 if (slot->metrics.horiAdvance)
475 slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
478 slot->metrics.vertAdvance = FT_MulFix(slot->metrics.vertAdvance,
479 metrics->scaler.y_scale);
481 slot->metrics.horiAdvance = TA_PIX_ROUND(slot->metrics.horiAdvance);
482 slot->metrics.vertAdvance = TA_PIX_ROUND(slot->metrics.vertAdvance);
484 #if 0
485 /* now copy outline into glyph slot */
486 TA_GlyphLoader_Rewind(internal->loader);
487 error = TA_GlyphLoader_CopyPoints(internal->loader, gloader);
488 if (error)
489 goto Exit;
491 /* reassign all outline fields except flags to protect them */
492 slot->outline.n_contours = internal->loader->base.outline.n_contours;
493 slot->outline.n_points = internal->loader->base.outline.n_points;
494 slot->outline.points = internal->loader->base.outline.points;
495 slot->outline.tags = internal->loader->base.outline.tags;
496 slot->outline.contours = internal->loader->base.outline.contours;
498 slot->format = FT_GLYPH_FORMAT_OUTLINE;
499 #endif
502 Exit:
503 return error;
507 /* load a glyph */
509 FT_Error
510 ta_loader_load_glyph(FONT* font,
511 FT_Face face,
512 FT_UInt gindex,
513 FT_Int32 load_flags)
515 FT_Error error;
516 FT_Size size = face->size;
517 TA_Loader loader = font->loader;
518 TA_ScalerRec scaler;
521 if (!size)
522 return FT_Err_Invalid_Size_Handle;
524 memset(&scaler, 0, sizeof (TA_ScalerRec));
526 scaler.face = face;
527 scaler.x_scale = size->metrics.x_scale;
528 scaler.x_delta = 0;
529 scaler.y_scale = size->metrics.y_scale;
530 scaler.y_delta = 0;
532 scaler.render_mode = FT_LOAD_TARGET_MODE(load_flags);
533 scaler.flags = 0; /* XXX: fix this */
535 /* XXX this is an ugly hack of ttfautohint: */
536 /* bit 29 triggers vertical hinting only */
537 if (load_flags & (1 << 29))
538 scaler.flags |= TA_SCALER_FLAG_NO_HORIZONTAL;
540 /* note that the fallback style can't be changed anymore */
541 /* after the first call of `ta_loader_load_glyph' */
542 error = ta_loader_reset(font, face);
543 if (!error)
545 TA_StyleMetrics metrics;
546 FT_UInt options = TA_STYLE_NONE_DFLT;
549 #ifdef FT_OPTION_AUTOFIT2
550 /* XXX: undocumented hook to activate the latin2 hinter */
551 if (load_flags & (1UL << 20))
552 options = TA_STYLE_LTN2_DFLT;
553 #endif
555 error = ta_face_globals_get_metrics(loader->globals, gindex,
556 options, &metrics);
557 if (!error)
559 TA_StyleClass style_class = metrics->style_class;
560 TA_WritingSystemClass writing_system_class =
561 ta_writing_system_classes[style_class->writing_system];
564 loader->metrics = metrics;
566 if (writing_system_class->style_metrics_scale)
567 writing_system_class->style_metrics_scale(metrics, &scaler);
568 else
569 metrics->scaler = scaler;
571 load_flags |= FT_LOAD_NO_SCALE
572 | FT_LOAD_IGNORE_TRANSFORM;
573 load_flags &= ~FT_LOAD_RENDER;
575 if (writing_system_class->style_hints_init)
577 error = writing_system_class->style_hints_init(&loader->hints,
578 metrics);
579 if (error)
580 goto Exit;
583 error = ta_loader_load_g(loader, &scaler, gindex, load_flags, 0);
586 Exit:
587 return error;
591 void
592 ta_loader_register_hints_recorder(TA_Loader loader,
593 TA_Hints_Recorder hints_recorder,
594 void* user)
596 loader->hints.recorder = hints_recorder;
597 loader->hints.user = user;
600 /* end of taloader.c */