RIP, Vernon...
[ttfautohint.git] / lib / taloader.c
blob4b2aae85b1b6c51ac85c05256cbc2f932af35e9b
1 /* taloader.c */
3 /*
4 * Copyright (C) 2011-2016 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 typedef struct FT_Slot_InternalRec_
33 TA_GlyphLoader loader;
34 FT_UInt flags;
35 FT_Bool glyph_transformed;
36 FT_Matrix glyph_matrix;
37 FT_Vector glyph_delta;
38 void* glyph_hints;
39 } FT_GlyphSlot_InternalRec;
42 /* initialize glyph loader */
44 FT_Error
45 ta_loader_init(FONT* font)
47 TA_Loader loader = font->loader;
50 memset(loader, 0, sizeof (TA_LoaderRec));
52 ta_glyph_hints_init(&loader->hints);
53 #ifdef TA_DEBUG
54 _ta_debug_hints = &loader->hints;
55 #endif
56 return TA_GlyphLoader_New(&loader->gloader);
60 /* reset glyph loader and compute globals if necessary */
62 FT_Error
63 ta_loader_reset(FONT* font,
64 FT_Face face)
66 FT_Error error = FT_Err_Ok;
67 TA_Loader loader = font->loader;
70 loader->face = face;
71 loader->globals = (TA_FaceGlobals)face->autohint.data;
73 TA_GlyphLoader_Rewind(loader->gloader);
75 if (loader->globals == NULL)
77 error = ta_face_globals_new(face, &loader->globals, font);
78 if (!error)
80 face->autohint.data = (FT_Pointer)loader->globals;
81 face->autohint.finalizer = (FT_Generic_Finalizer)ta_face_globals_free;
85 return error;
89 /* finalize glyph loader */
91 void
92 ta_loader_done(FONT* font)
94 TA_Loader loader = font->loader;
97 ta_glyph_hints_done(&loader->hints);
99 loader->face = NULL;
100 loader->globals = NULL;
102 #ifdef TA_DEBUG
103 _ta_debug_hints = NULL;
104 #endif
105 TA_GlyphLoader_Done(loader->gloader);
106 loader->gloader = NULL;
110 /* load a single glyph component; this routine calls itself recursively, */
111 /* if necessary, and does the main work of `ta_loader_load_glyph' */
113 static FT_Error
114 ta_loader_load_g(TA_Loader loader,
115 TA_Scaler scaler,
116 FT_UInt glyph_index,
117 FT_Int32 load_flags,
118 FT_UInt depth)
120 FT_Error error;
121 FT_Face face = loader->face;
122 TA_GlyphLoader gloader = loader->gloader;
123 TA_StyleMetrics metrics = loader->metrics;
124 TA_GlyphHints hints = &loader->hints;
125 FT_GlyphSlot slot = face->glyph;
126 #if 0
127 FT_Slot_Internal internal = slot->internal;
128 #endif
129 FT_Int32 flags;
132 flags = load_flags | FT_LOAD_LINEAR_DESIGN;
133 error = FT_Load_Glyph(face, glyph_index, flags);
134 if (error)
135 goto Exit;
137 #if 0
138 loader->transformed = internal->glyph_transformed;
139 if (loader->transformed)
141 FT_Matrix inverse;
144 loader->trans_matrix = internal->glyph_matrix;
145 loader->trans_delta = internal->glyph_delta;
147 inverse = loader->trans_matrix;
148 if (!FT_Matrix_Invert(&inverse))
149 FT_Vector_Transform(&loader->trans_delta, &inverse);
151 #endif
153 switch (slot->format)
155 case FT_GLYPH_FORMAT_OUTLINE:
156 /* translate the loaded glyph when an internal transform is needed */
157 if (loader->transformed)
158 FT_Outline_Translate(&slot->outline,
159 loader->trans_delta.x,
160 loader->trans_delta.y);
162 /* copy the outline points in the loader's current extra points */
163 /* which are used to keep original glyph coordinates */
164 error = TA_GLYPHLOADER_CHECK_POINTS(gloader,
165 slot->outline.n_points + 4,
166 slot->outline.n_contours);
167 if (error)
168 goto Exit;
170 memcpy(gloader->current.outline.points,
171 slot->outline.points,
172 (size_t)slot->outline.n_points * sizeof (FT_Vector));
173 memcpy(gloader->current.outline.contours,
174 slot->outline.contours,
175 (size_t)slot->outline.n_contours * sizeof (short));
176 memcpy(gloader->current.outline.tags,
177 slot->outline.tags,
178 (size_t)slot->outline.n_points * sizeof (char));
180 gloader->current.outline.n_points = slot->outline.n_points;
181 gloader->current.outline.n_contours = slot->outline.n_contours;
183 /* compute original horizontal phantom points */
184 /* (and ignore vertical ones) */
185 loader->pp1.x = hints->x_delta;
186 loader->pp1.y = hints->y_delta;
187 loader->pp2.x = FT_MulFix(slot->metrics.horiAdvance,
188 hints->x_scale) + hints->x_delta;
189 loader->pp2.y = hints->y_delta;
191 /* be sure to check for spacing glyphs */
192 if (slot->outline.n_points == 0)
193 goto Hint_Metrics;
195 /* now load the slot image into the auto-outline */
196 /* and run the automatic hinting process */
198 TA_StyleClass style_class = metrics->style_class;
199 TA_WritingSystemClass writing_system_class =
200 ta_writing_system_classes[style_class->writing_system];
203 if (writing_system_class->style_hints_apply)
204 writing_system_class->style_hints_apply(glyph_index,
205 hints,
206 &gloader->current.outline,
207 metrics);
210 /* we now need to adjust the metrics according to the change in */
211 /* width/positioning that occurred during the hinting process */
212 if (scaler->render_mode != FT_RENDER_MODE_LIGHT)
214 FT_Pos old_rsb, old_lsb, new_lsb;
215 FT_Pos pp1x_uh, pp2x_uh;
216 TA_AxisHints axis = &hints->axis[TA_DIMENSION_HORZ];
218 TA_Edge edge1 = axis->edges; /* leftmost edge */
219 TA_Edge edge2 = edge1 + axis->num_edges - 1; /* rightmost edge */
222 if (axis->num_edges > 1 && TA_HINTS_DO_ADVANCE(hints))
224 old_rsb = loader->pp2.x - edge2->opos;
225 old_lsb = edge1->opos;
226 new_lsb = edge1->pos;
228 /* remember unhinted values to later account */
229 /* for rounding errors */
230 pp1x_uh = new_lsb - old_lsb;
231 pp2x_uh = edge2->pos + old_rsb;
233 /* prefer too much space over too little space */
234 /* for very small sizes */
235 if (old_lsb < 24)
236 pp1x_uh -= 8;
237 if (old_rsb < 24)
238 pp2x_uh += 8;
240 loader->pp1.x = TA_PIX_ROUND(pp1x_uh);
241 loader->pp2.x = TA_PIX_ROUND(pp2x_uh);
243 if (loader->pp1.x >= new_lsb
244 && old_lsb > 0)
245 loader->pp1.x -= 64;
246 if (loader->pp2.x <= edge2->pos
247 && old_rsb > 0)
248 loader->pp2.x += 64;
250 slot->lsb_delta = loader->pp1.x - pp1x_uh;
251 slot->rsb_delta = loader->pp2.x - pp2x_uh;
253 else
255 FT_Pos pp1x = loader->pp1.x;
256 FT_Pos pp2x = loader->pp2.x;
259 loader->pp1.x = TA_PIX_ROUND(pp1x);
260 loader->pp2.x = TA_PIX_ROUND(pp2x);
262 slot->lsb_delta = loader->pp1.x - pp1x;
263 slot->rsb_delta = loader->pp2.x - pp2x;
266 else
268 FT_Pos pp1x = loader->pp1.x;
269 FT_Pos pp2x = loader->pp2.x;
272 loader->pp1.x = TA_PIX_ROUND(pp1x + hints->xmin_delta);
273 loader->pp2.x = TA_PIX_ROUND(pp2x + hints->xmax_delta);
275 slot->lsb_delta = loader->pp1.x - pp1x;
276 slot->rsb_delta = loader->pp2.x - pp2x;
279 /* good, we simply add the glyph to our loader's base */
280 TA_GlyphLoader_Add(gloader);
281 break;
283 case FT_GLYPH_FORMAT_COMPOSITE:
285 FT_UInt nn, num_subglyphs = slot->num_subglyphs;
286 FT_UInt num_base_subgs, start_point;
287 TA_SubGlyph subglyph;
290 start_point = (FT_UInt)gloader->base.outline.n_points;
292 /* first of all, copy the subglyph descriptors in the glyph loader */
293 error = TA_GlyphLoader_CheckSubGlyphs(gloader, num_subglyphs);
294 if (error)
295 goto Exit;
297 memcpy(gloader->current.subglyphs,
298 slot->subglyphs,
299 num_subglyphs * sizeof (TA_SubGlyphRec));
301 gloader->current.num_subglyphs = num_subglyphs;
302 num_base_subgs = gloader->base.num_subglyphs;
304 /* now read each subglyph independently */
305 for (nn = 0; nn < num_subglyphs; nn++)
307 FT_Vector pp1, pp2;
308 FT_Pos x, y;
309 FT_UInt num_points, num_new_points, num_base_points;
312 /* gloader.current.subglyphs can change during glyph loading due */
313 /* to re-allocation -- we must recompute the current subglyph on */
314 /* each iteration */
315 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
317 pp1 = loader->pp1;
318 pp2 = loader->pp2;
320 num_base_points = (FT_UInt)gloader->base.outline.n_points;
322 error = ta_loader_load_g(loader, scaler, (FT_UInt)subglyph->index,
323 load_flags, depth + 1);
324 if (error)
325 goto Exit;
327 /* recompute subglyph pointer */
328 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
330 if (!(subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS))
332 loader->pp1 = pp1;
333 loader->pp2 = pp2;
336 num_points = (FT_UInt)gloader->base.outline.n_points;
337 num_new_points = num_points - num_base_points;
339 /* now perform the transformation required for this subglyph */
340 if (subglyph->flags & (FT_SUBGLYPH_FLAG_SCALE
341 | FT_SUBGLYPH_FLAG_XY_SCALE
342 | FT_SUBGLYPH_FLAG_2X2))
344 FT_Vector* cur = gloader->base.outline.points + num_base_points;
345 FT_Vector* limit = cur + num_new_points;
348 for (; cur < limit; cur++)
349 FT_Vector_Transform(cur, &subglyph->transform);
352 /* apply offset */
353 if (!(subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES))
355 FT_UInt k = (FT_UInt)subglyph->arg1;
356 FT_UInt l = (FT_UInt)subglyph->arg2;
357 FT_Vector* p1;
358 FT_Vector* p2;
361 if (start_point + k >= num_base_points
362 || l >= num_new_points)
364 error = FT_Err_Invalid_Composite;
365 goto Exit;
368 l += num_base_points;
370 /* for now, only use the current point coordinates; */
371 /* we eventually may consider another approach */
372 p1 = gloader->base.outline.points + start_point + k;
373 p2 = gloader->base.outline.points + start_point + l;
375 x = p1->x - p2->x;
376 y = p1->y - p2->y;
378 else
380 x = FT_MulFix(subglyph->arg1, hints->x_scale) + hints->x_delta;
381 y = FT_MulFix(subglyph->arg2, hints->y_scale) + hints->y_delta;
383 x = TA_PIX_ROUND(x);
384 y = TA_PIX_ROUND(y);
388 FT_Outline dummy = gloader->base.outline;
391 dummy.points += num_base_points;
392 dummy.n_points = (short)num_new_points;
394 FT_Outline_Translate(&dummy, x, y);
398 break;
400 default:
401 /* we don't support other formats (yet?) */
402 error = FT_Err_Unimplemented_Feature;
405 Hint_Metrics:
406 if (depth == 0)
408 FT_BBox bbox;
409 FT_Vector vvector;
412 vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX;
413 vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY;
414 vvector.x = FT_MulFix(vvector.x, metrics->scaler.x_scale);
415 vvector.y = FT_MulFix(vvector.y, metrics->scaler.y_scale);
417 /* transform the hinted outline if needed */
418 if (loader->transformed)
420 FT_Outline_Transform(&gloader->base.outline, &loader->trans_matrix);
421 FT_Vector_Transform(&vvector, &loader->trans_matrix);
423 #if 1
424 /* we must translate our final outline by -pp1.x */
425 /* and compute the new metrics */
426 if (loader->pp1.x)
427 FT_Outline_Translate(&gloader->base.outline, -loader->pp1.x, 0);
428 #endif
429 FT_Outline_Get_CBox(&gloader->base.outline, &bbox);
431 bbox.xMin = TA_PIX_FLOOR(bbox.xMin);
432 bbox.yMin = TA_PIX_FLOOR(bbox.yMin);
433 bbox.xMax = TA_PIX_CEIL(bbox.xMax);
434 bbox.yMax = TA_PIX_CEIL(bbox.yMax);
436 slot->metrics.width = bbox.xMax - bbox.xMin;
437 slot->metrics.height = bbox.yMax - bbox.yMin;
438 slot->metrics.horiBearingX = bbox.xMin;
439 slot->metrics.horiBearingY = bbox.yMax;
441 slot->metrics.vertBearingX = TA_PIX_FLOOR(bbox.xMin + vvector.x);
442 slot->metrics.vertBearingY = TA_PIX_FLOOR(bbox.yMax + vvector.y);
444 /* for mono-width fonts (like Andale, Courier, etc.) we need */
445 /* to keep the original rounded advance width; ditto for */
446 /* digits if all have the same advance width */
447 if (scaler->render_mode != FT_RENDER_MODE_LIGHT
448 && (FT_IS_FIXED_WIDTH(slot->face)
449 || (ta_face_globals_is_digit(loader->globals, glyph_index)
450 && metrics->digits_have_same_width)))
452 slot->metrics.horiAdvance = FT_MulFix(slot->metrics.horiAdvance,
453 metrics->scaler.x_scale);
455 /* set delta values to 0, otherwise code that uses them */
456 /* is going to ruin the fixed advance width */
457 slot->lsb_delta = 0;
458 slot->rsb_delta = 0;
460 else
462 /* non-spacing glyphs must stay as-is */
463 if (slot->metrics.horiAdvance)
464 slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
467 slot->metrics.vertAdvance = FT_MulFix(slot->metrics.vertAdvance,
468 metrics->scaler.y_scale);
470 slot->metrics.horiAdvance = TA_PIX_ROUND(slot->metrics.horiAdvance);
471 slot->metrics.vertAdvance = TA_PIX_ROUND(slot->metrics.vertAdvance);
473 #if 0
474 /* now copy outline into glyph slot */
475 TA_GlyphLoader_Rewind(internal->loader);
476 error = TA_GlyphLoader_CopyPoints(internal->loader, gloader);
477 if (error)
478 goto Exit;
480 /* reassign all outline fields except flags to protect them */
481 slot->outline.n_contours = internal->loader->base.outline.n_contours;
482 slot->outline.n_points = internal->loader->base.outline.n_points;
483 slot->outline.points = internal->loader->base.outline.points;
484 slot->outline.tags = internal->loader->base.outline.tags;
485 slot->outline.contours = internal->loader->base.outline.contours;
487 slot->format = FT_GLYPH_FORMAT_OUTLINE;
488 #endif
491 Exit:
492 return error;
496 /* load a glyph */
498 FT_Error
499 ta_loader_load_glyph(FONT* font,
500 FT_Face face,
501 FT_UInt gindex,
502 FT_Int32 load_flags)
504 FT_Error error;
505 FT_Size size = face->size;
506 TA_Loader loader = font->loader;
507 TA_ScalerRec scaler;
510 if (!size)
511 return FT_Err_Invalid_Size_Handle;
513 memset(&scaler, 0, sizeof (TA_ScalerRec));
515 scaler.face = face;
516 scaler.x_scale = size->metrics.x_scale;
517 scaler.x_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */
518 scaler.y_scale = size->metrics.y_scale;
519 scaler.y_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */
521 scaler.render_mode = FT_LOAD_TARGET_MODE(load_flags);
522 scaler.flags = 0; /* XXX: fix this */
524 /* XXX this is an ugly hack of ttfautohint: */
525 /* bit 29 triggers vertical hinting only */
526 if (load_flags & (1 << 29))
527 scaler.flags |= TA_SCALER_FLAG_NO_HORIZONTAL;
529 /* note that the fallback style can't be changed anymore */
530 /* after the first call of `ta_loader_load_glyph' */
531 error = ta_loader_reset(font, face);
532 if (!error)
534 TA_StyleMetrics metrics;
535 FT_UInt options = TA_STYLE_NONE_DFLT;
538 #ifdef FT_OPTION_AUTOFIT2
539 /* XXX: undocumented hook to activate the latin2 hinter */
540 if (load_flags & (1UL << 20))
541 options = TA_STYLE_LTN2_DFLT;
542 #endif
544 error = ta_face_globals_get_metrics(loader->globals, gindex,
545 options, &metrics);
546 if (!error)
548 TA_StyleClass style_class = metrics->style_class;
549 TA_WritingSystemClass writing_system_class =
550 ta_writing_system_classes[style_class->writing_system];
553 loader->metrics = metrics;
555 if (writing_system_class->style_metrics_scale)
556 writing_system_class->style_metrics_scale(metrics, &scaler);
557 else
558 metrics->scaler = scaler;
560 load_flags |= FT_LOAD_NO_SCALE
561 | FT_LOAD_IGNORE_TRANSFORM;
562 load_flags &= ~FT_LOAD_RENDER;
564 if (writing_system_class->style_hints_init)
566 error = writing_system_class->style_hints_init(&loader->hints,
567 metrics);
568 if (error)
569 goto Exit;
572 error = ta_loader_load_g(loader, &scaler, gindex, load_flags, 0);
575 Exit:
576 return error;
580 void
581 ta_loader_register_hints_recorder(TA_Loader loader,
582 TA_Hints_Recorder hints_recorder,
583 void* user)
585 loader->hints.recorder = hints_recorder;
586 loader->hints.user = user;
589 /* end of taloader.c */