More files from FreeType's `autofit' and `base' modules.
[ttfautohint.git] / src / taloader.c
blob31c7386d1a24f57381d06833f4fc87f870ef8e8e
1 /* taloader.c */
3 /* originally file `afloader.c' (2011-Mar-28) from FreeType */
5 /* heavily modified 2011 by Werner Lemberg <wl@gnu.org> */
7 #include <string.h>
9 #include <ft2build.h>
10 #include FT_GLYPH_H
12 #include "taloader.h"
13 #include "tahints.h"
14 #include "taglobal.h"
17 /* from file `ftobjs.h' (2011-Mar-28) from FreeType */
18 typedef struct FT_Slot_InternalRec_
20 TA_GlyphLoader loader;
21 FT_UInt flags;
22 FT_Bool glyph_transformed;
23 FT_Matrix glyph_matrix;
24 FT_Vector glyph_delta;
25 void* glyph_hints;
26 } FT_GlyphSlot_InternalRec;
29 /* initialize glyph loader */
31 FT_Error
32 ta_loader_init(TA_Loader loader)
34 memset(loader, 0, sizeof (TA_LoaderRec));
36 ta_glyph_hints_init(&loader->hints);
37 #ifdef TA_DEBUG
38 _ta_debug_hints = &loader->hints;
39 #endif
40 return TA_GlyphLoader_New(&loader->gloader);
44 /* reset glyph loader and compute globals if necessary */
46 FT_Error
47 ta_loader_reset(TA_Loader loader,
48 FT_Face face)
50 FT_Error error = FT_Err_Ok;
53 loader->face = face;
54 loader->globals = (TA_FaceGlobals)face->autohint.data;
56 TA_GlyphLoader_Rewind(loader->gloader);
58 if (loader->globals == NULL)
60 error = ta_face_globals_new(face, &loader->globals);
61 if (!error)
63 face->autohint.data = (FT_Pointer)loader->globals;
64 face->autohint.finalizer = (FT_Generic_Finalizer)ta_face_globals_free;
68 return error;
72 /* finalize glyph loader */
74 void
75 ta_loader_done(TA_Loader loader)
77 ta_glyph_hints_done(&loader->hints);
79 loader->face = NULL;
80 loader->globals = NULL;
82 #ifdef TA_DEBUG
83 _ta_debug_hints = NULL;
84 #endif
85 TA_GlyphLoader_Done(loader->gloader);
86 loader->gloader = NULL;
90 /* load a single glyph component; this routine calls itself recursively, */
91 /* if necessary, and does the main work of `ta_loader_load_glyph' */
93 static FT_Error
94 ta_loader_load_g(TA_Loader loader,
95 TA_Scaler scaler,
96 FT_UInt glyph_index,
97 FT_Int32 load_flags,
98 FT_UInt depth)
100 FT_Error error;
101 FT_Face face = loader->face;
102 TA_GlyphLoader gloader = loader->gloader;
103 TA_ScriptMetrics metrics = loader->metrics;
104 TA_GlyphHints hints = &loader->hints;
105 FT_GlyphSlot slot = face->glyph;
106 FT_Slot_Internal internal = slot->internal;
109 error = FT_Load_Glyph(face, glyph_index, load_flags);
110 if (error)
111 goto Exit;
113 loader->transformed = internal->glyph_transformed;
114 if (loader->transformed)
116 FT_Matrix inverse;
119 loader->trans_matrix = internal->glyph_matrix;
120 loader->trans_delta = internal->glyph_delta;
122 inverse = loader->trans_matrix;
123 FT_Matrix_Invert(&inverse);
124 FT_Vector_Transform(&loader->trans_delta, &inverse);
127 /* set linear metrics */
128 slot->linearHoriAdvance = slot->metrics.horiAdvance;
129 slot->linearVertAdvance = slot->metrics.vertAdvance;
131 switch (slot->format)
133 case FT_GLYPH_FORMAT_OUTLINE:
134 /* translate the loaded glyph when an internal transform is needed */
135 if (loader->transformed)
136 FT_Outline_Translate(&slot->outline,
137 loader->trans_delta.x,
138 loader->trans_delta.y);
140 /* copy the outline points in the loader's current extra points */
141 /* which are used to keep original glyph coordinates */
142 error = TA_GLYPHLOADER_CHECK_POINTS(gloader,
143 slot->outline.n_points + 4,
144 slot->outline.n_contours);
145 if (error)
146 goto Exit;
148 memcpy(gloader->current.outline.points,
149 slot->outline.points,
150 slot->outline.n_points * sizeof (FT_Vector));
151 memcpy(gloader->current.outline.contours,
152 slot->outline.contours,
153 slot->outline.n_contours * sizeof (short));
154 memcpy(gloader->current.outline.tags,
155 slot->outline.tags,
156 slot->outline.n_points * sizeof (char));
158 gloader->current.outline.n_points = slot->outline.n_points;
159 gloader->current.outline.n_contours = slot->outline.n_contours;
161 /* compute original horizontal phantom points */
162 /* (and ignore vertical ones) */
163 loader->pp1.x = hints->x_delta;
164 loader->pp1.y = hints->y_delta;
165 loader->pp2.x = FT_MulFix(slot->metrics.horiAdvance,
166 hints->x_scale) + hints->x_delta;
167 loader->pp2.y = hints->y_delta;
169 /* be sure to check for spacing glyphs */
170 if (slot->outline.n_points == 0)
171 goto Hint_Metrics;
173 /* now load the slot image into the auto-outline */
174 /* and run the automatic hinting process */
175 if (metrics->clazz->script_hints_apply)
176 metrics->clazz->script_hints_apply(hints,
177 &gloader->current.outline,
178 metrics);
180 /* we now need to adjust the metrics according to the change in */
181 /* width/positioning that occurred during the hinting process */
182 if (scaler->render_mode != FT_RENDER_MODE_LIGHT)
184 FT_Pos old_rsb, old_lsb, new_lsb;
185 FT_Pos pp1x_uh, pp2x_uh;
186 TA_AxisHints axis = &hints->axis[TA_DIMENSION_HORZ];
188 TA_Edge edge1 = axis->edges; /* leftmost edge */
189 TA_Edge edge2 = edge1 + axis->num_edges - 1; /* rightmost edge */
192 if (axis->num_edges > 1 && TA_HINTS_DO_ADVANCE(hints))
194 old_rsb = loader->pp2.x - edge2->opos;
195 old_lsb = edge1->opos;
196 new_lsb = edge1->pos;
198 /* remember unhinted values to later account */
199 /* for rounding errors */
200 pp1x_uh = new_lsb - old_lsb;
201 pp2x_uh = edge2->pos + old_rsb;
203 /* prefer too much space over too little space */
204 /* for very small sizes */
205 if (old_lsb < 24)
206 pp1x_uh -= 8;
207 if (old_rsb < 24)
208 pp2x_uh += 8;
210 loader->pp1.x = TA_PIX_ROUND(pp1x_uh);
211 loader->pp2.x = TA_PIX_ROUND(pp2x_uh);
213 if (loader->pp1.x >= new_lsb
214 && old_lsb > 0)
215 loader->pp1.x -= 64;
216 if (loader->pp2.x <= edge2->pos
217 && old_rsb > 0)
218 loader->pp2.x += 64;
220 slot->lsb_delta = loader->pp1.x - pp1x_uh;
221 slot->rsb_delta = loader->pp2.x - pp2x_uh;
223 else
225 FT_Pos pp1x = loader->pp1.x;
226 FT_Pos pp2x = loader->pp2.x;
229 loader->pp1.x = TA_PIX_ROUND(pp1x);
230 loader->pp2.x = TA_PIX_ROUND(pp2x);
232 slot->lsb_delta = loader->pp1.x - pp1x;
233 slot->rsb_delta = loader->pp2.x - pp2x;
236 else
238 FT_Pos pp1x = loader->pp1.x;
239 FT_Pos pp2x = loader->pp2.x;
242 loader->pp1.x = TA_PIX_ROUND(pp1x + hints->xmin_delta);
243 loader->pp2.x = TA_PIX_ROUND(pp2x + hints->xmax_delta);
245 slot->lsb_delta = loader->pp1.x - pp1x;
246 slot->rsb_delta = loader->pp2.x - pp2x;
249 /* good, we simply add the glyph to our loader's base */
250 TA_GlyphLoader_Add(gloader);
251 break;
253 case FT_GLYPH_FORMAT_COMPOSITE:
255 FT_UInt nn, num_subglyphs = slot->num_subglyphs;
256 FT_UInt num_base_subgs, start_point;
257 TA_SubGlyph subglyph;
260 start_point = gloader->base.outline.n_points;
262 /* first of all, copy the subglyph descriptors in the glyph loader */
263 error = TA_GlyphLoader_CheckSubGlyphs(gloader, num_subglyphs);
264 if (error)
265 goto Exit;
267 memcpy(gloader->current.subglyphs,
268 slot->subglyphs,
269 num_subglyphs * sizeof (TA_SubGlyphRec));
271 gloader->current.num_subglyphs = num_subglyphs;
272 num_base_subgs = gloader->base.num_subglyphs;
274 /* now read each subglyph independently */
275 for (nn = 0; nn < num_subglyphs; nn++)
277 FT_Vector pp1, pp2;
278 FT_Pos x, y;
279 FT_UInt num_points, num_new_points, num_base_points;
282 /* gloader.current.subglyphs can change during glyph loading due */
283 /* to re-allocation -- we must recompute the current subglyph on */
284 /* each iteration */
285 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
287 pp1 = loader->pp1;
288 pp2 = loader->pp2;
290 num_base_points = gloader->base.outline.n_points;
292 error = ta_loader_load_g(loader, scaler, subglyph->index,
293 load_flags, depth + 1);
294 if (error)
295 goto Exit;
297 /* recompute subglyph pointer */
298 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
300 if (subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS)
302 pp1 = loader->pp1;
303 pp2 = loader->pp2;
305 else
307 loader->pp1 = pp1;
308 loader->pp2 = pp2;
311 num_points = gloader->base.outline.n_points;
312 num_new_points = num_points - num_base_points;
314 /* now perform the transformation required for this subglyph */
315 if (subglyph->flags & (FT_SUBGLYPH_FLAG_SCALE
316 | FT_SUBGLYPH_FLAG_XY_SCALE
317 | FT_SUBGLYPH_FLAG_2X2))
319 FT_Vector* cur = gloader->base.outline.points + num_base_points;
320 FT_Vector* limit = cur + num_new_points;
323 for (; cur < limit; cur++)
324 FT_Vector_Transform(cur, &subglyph->transform);
327 /* apply offset */
328 if (!(subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES))
330 FT_Int k = subglyph->arg1;
331 FT_UInt l = subglyph->arg2;
332 FT_Vector* p1;
333 FT_Vector* p2;
336 if (start_point + k >= num_base_points
337 || l >= (FT_UInt)num_new_points)
339 error = FT_Err_Invalid_Composite;
340 goto Exit;
343 l += num_base_points;
345 /* for now, only use the current point coordinates; */
346 /* we eventually may consider another approach */
347 p1 = gloader->base.outline.points + start_point + k;
348 p2 = gloader->base.outline.points + start_point + l;
350 x = p1->x - p2->x;
351 y = p1->y - p2->y;
353 else
355 x = FT_MulFix(subglyph->arg1, hints->x_scale) + hints->x_delta;
356 y = FT_MulFix(subglyph->arg2, hints->y_scale) + hints->y_delta;
358 x = TA_PIX_ROUND(x);
359 y = TA_PIX_ROUND(y);
363 FT_Outline dummy = gloader->base.outline;
366 dummy.points += num_base_points;
367 dummy.n_points = (short)num_new_points;
369 FT_Outline_Translate(&dummy, x, y);
373 break;
375 default:
376 /* we don't support other formats (yet?) */
377 error = FT_Err_Unimplemented_Feature;
380 Hint_Metrics:
381 if (depth == 0)
383 FT_BBox bbox;
384 FT_Vector vvector;
387 vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX;
388 vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY;
389 vvector.x = FT_MulFix(vvector.x, metrics->scaler.x_scale);
390 vvector.y = FT_MulFix(vvector.y, metrics->scaler.y_scale);
392 /* transform the hinted outline if needed */
393 if (loader->transformed)
395 FT_Outline_Transform(&gloader->base.outline, &loader->trans_matrix);
396 FT_Vector_Transform(&vvector, &loader->trans_matrix);
398 #if 1
399 /* we must translate our final outline by -pp1.x */
400 /* and compute the new metrics */
401 if (loader->pp1.x)
402 FT_Outline_Translate(&gloader->base.outline, -loader->pp1.x, 0);
403 #endif
404 FT_Outline_Get_CBox(&gloader->base.outline, &bbox);
406 bbox.xMin = TA_PIX_FLOOR(bbox.xMin);
407 bbox.yMin = TA_PIX_FLOOR(bbox.yMin);
408 bbox.xMax = TA_PIX_CEIL(bbox.xMax);
409 bbox.yMax = TA_PIX_CEIL(bbox.yMax);
411 slot->metrics.width = bbox.xMax - bbox.xMin;
412 slot->metrics.height = bbox.yMax - bbox.yMin;
413 slot->metrics.horiBearingX = bbox.xMin;
414 slot->metrics.horiBearingY = bbox.yMax;
416 slot->metrics.vertBearingX = TA_PIX_FLOOR(bbox.xMin + vvector.x);
417 slot->metrics.vertBearingY = TA_PIX_FLOOR(bbox.yMax + vvector.y);
419 /* for mono-width fonts (like Andale, Courier, etc.) we need */
420 /* to keep the original rounded advance width; ditto for */
421 /* digits if all have the same advance width */
422 if (FT_IS_FIXED_WIDTH(slot->face)
423 || (ta_face_globals_is_digit(loader->globals, glyph_index)
424 && metrics->digits_have_same_width))
426 slot->metrics.horiAdvance = FT_MulFix(slot->metrics.horiAdvance,
427 metrics->scaler.x_scale);
429 /* set delta values to 0, otherwise code that uses them */
430 /* is going to ruin the fixed advance width */
431 slot->lsb_delta = 0;
432 slot->rsb_delta = 0;
434 else
436 /* non-spacing glyphs must stay as-is */
437 if (slot->metrics.horiAdvance)
438 slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
441 slot->metrics.vertAdvance = FT_MulFix(slot->metrics.vertAdvance,
442 metrics->scaler.y_scale);
444 slot->metrics.horiAdvance = TA_PIX_ROUND(slot->metrics.horiAdvance);
445 slot->metrics.vertAdvance = TA_PIX_ROUND(slot->metrics.vertAdvance);
447 /* now copy outline into glyph slot */
448 TA_GlyphLoader_Rewind(internal->loader);
449 error = TA_GlyphLoader_CopyPoints(internal->loader, gloader);
450 if (error)
451 goto Exit;
453 slot->outline = internal->loader->base.outline;
454 slot->format = FT_GLYPH_FORMAT_OUTLINE;
457 #ifdef DEBUG_HINTER
458 ta_debug_hinter = hinter;
459 #endif
461 Exit:
462 return error;
466 /* load a glyph */
468 FT_Error
469 ta_loader_load_glyph(TA_Loader loader,
470 FT_Face face,
471 FT_UInt gindex,
472 FT_UInt32 load_flags)
474 FT_Error error;
475 FT_Size size = face->size;
476 TA_ScalerRec scaler;
479 if (!size)
480 return FT_Err_Invalid_Argument;
482 memset(&scaler, 0, sizeof (TA_ScalerRec));
484 scaler.face = face;
485 scaler.x_scale = size->metrics.x_scale;
486 scaler.x_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */
487 scaler.y_scale = size->metrics.y_scale;
488 scaler.y_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */
490 scaler.render_mode = FT_LOAD_TARGET_MODE(load_flags);
491 scaler.flags = 0; /* XXX: fix this */
493 error = ta_loader_reset(loader, face);
494 if (!error)
496 TA_ScriptMetrics metrics;
497 FT_UInt options = 0;
500 #ifdef FT_OPTION_AUTOFIT2
501 /* XXX: undocumented hook to activate the latin2 hinter */
502 if (load_flags & (1UL << 20))
503 options = 2;
504 #endif
506 error = ta_face_globals_get_metrics(loader->globals, gindex,
507 options, &metrics);
508 if (!error)
510 loader->metrics = metrics;
512 if (metrics->clazz->script_metrics_scale)
513 metrics->clazz->script_metrics_scale(metrics, &scaler);
514 else
515 metrics->scaler = scaler;
517 load_flags |= FT_LOAD_NO_SCALE
518 | FT_LOAD_IGNORE_TRANSFORM;
519 load_flags &= ~FT_LOAD_RENDER;
521 if (metrics->clazz->script_hints_init)
523 error = metrics->clazz->script_hints_init(&loader->hints,
524 metrics);
525 if (error)
526 goto Exit;
529 error = ta_loader_load_g(loader, &scaler, gindex, load_flags, 0);
532 Exit:
533 return error;
536 /* end of taloader.c */