Add code to properly scale glyphs not handled by the autohinter.
[ttfautohint.git] / src / taloader.c
blob7cad239bc496c74481831cffc9c029e7b3a3f953
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 #if 0
107 FT_Slot_Internal internal = slot->internal;
108 #endif
111 error = FT_Load_Glyph(face, glyph_index, load_flags);
112 if (error)
113 goto Exit;
115 #if 0
116 loader->transformed = internal->glyph_transformed;
117 if (loader->transformed)
119 FT_Matrix inverse;
122 loader->trans_matrix = internal->glyph_matrix;
123 loader->trans_delta = internal->glyph_delta;
125 inverse = loader->trans_matrix;
126 FT_Matrix_Invert(&inverse);
127 FT_Vector_Transform(&loader->trans_delta, &inverse);
129 #endif
131 /* set linear metrics */
132 slot->linearHoriAdvance = slot->metrics.horiAdvance;
133 slot->linearVertAdvance = slot->metrics.vertAdvance;
135 switch (slot->format)
137 case FT_GLYPH_FORMAT_OUTLINE:
138 /* translate the loaded glyph when an internal transform is needed */
139 if (loader->transformed)
140 FT_Outline_Translate(&slot->outline,
141 loader->trans_delta.x,
142 loader->trans_delta.y);
144 /* copy the outline points in the loader's current extra points */
145 /* which are used to keep original glyph coordinates */
146 error = TA_GLYPHLOADER_CHECK_POINTS(gloader,
147 slot->outline.n_points + 4,
148 slot->outline.n_contours);
149 if (error)
150 goto Exit;
152 memcpy(gloader->current.outline.points,
153 slot->outline.points,
154 slot->outline.n_points * sizeof (FT_Vector));
155 memcpy(gloader->current.outline.contours,
156 slot->outline.contours,
157 slot->outline.n_contours * sizeof (short));
158 memcpy(gloader->current.outline.tags,
159 slot->outline.tags,
160 slot->outline.n_points * sizeof (char));
162 gloader->current.outline.n_points = slot->outline.n_points;
163 gloader->current.outline.n_contours = slot->outline.n_contours;
165 /* compute original horizontal phantom points */
166 /* (and ignore vertical ones) */
167 loader->pp1.x = hints->x_delta;
168 loader->pp1.y = hints->y_delta;
169 loader->pp2.x = FT_MulFix(slot->metrics.horiAdvance,
170 hints->x_scale) + hints->x_delta;
171 loader->pp2.y = hints->y_delta;
173 /* be sure to check for spacing glyphs */
174 if (slot->outline.n_points == 0)
175 goto Hint_Metrics;
177 /* now load the slot image into the auto-outline */
178 /* and run the automatic hinting process */
179 if (metrics->clazz->script_hints_apply)
180 metrics->clazz->script_hints_apply(hints,
181 &gloader->current.outline,
182 metrics);
184 /* we now need to adjust the metrics according to the change in */
185 /* width/positioning that occurred during the hinting process */
186 if (scaler->render_mode != FT_RENDER_MODE_LIGHT)
188 FT_Pos old_rsb, old_lsb, new_lsb;
189 FT_Pos pp1x_uh, pp2x_uh;
190 TA_AxisHints axis = &hints->axis[TA_DIMENSION_HORZ];
192 TA_Edge edge1 = axis->edges; /* leftmost edge */
193 TA_Edge edge2 = edge1 + axis->num_edges - 1; /* rightmost edge */
196 if (axis->num_edges > 1 && TA_HINTS_DO_ADVANCE(hints))
198 old_rsb = loader->pp2.x - edge2->opos;
199 old_lsb = edge1->opos;
200 new_lsb = edge1->pos;
202 /* remember unhinted values to later account */
203 /* for rounding errors */
204 pp1x_uh = new_lsb - old_lsb;
205 pp2x_uh = edge2->pos + old_rsb;
207 /* prefer too much space over too little space */
208 /* for very small sizes */
209 if (old_lsb < 24)
210 pp1x_uh -= 8;
211 if (old_rsb < 24)
212 pp2x_uh += 8;
214 loader->pp1.x = TA_PIX_ROUND(pp1x_uh);
215 loader->pp2.x = TA_PIX_ROUND(pp2x_uh);
217 if (loader->pp1.x >= new_lsb
218 && old_lsb > 0)
219 loader->pp1.x -= 64;
220 if (loader->pp2.x <= edge2->pos
221 && old_rsb > 0)
222 loader->pp2.x += 64;
224 slot->lsb_delta = loader->pp1.x - pp1x_uh;
225 slot->rsb_delta = loader->pp2.x - pp2x_uh;
227 else
229 FT_Pos pp1x = loader->pp1.x;
230 FT_Pos pp2x = loader->pp2.x;
233 loader->pp1.x = TA_PIX_ROUND(pp1x);
234 loader->pp2.x = TA_PIX_ROUND(pp2x);
236 slot->lsb_delta = loader->pp1.x - pp1x;
237 slot->rsb_delta = loader->pp2.x - pp2x;
240 else
242 FT_Pos pp1x = loader->pp1.x;
243 FT_Pos pp2x = loader->pp2.x;
246 loader->pp1.x = TA_PIX_ROUND(pp1x + hints->xmin_delta);
247 loader->pp2.x = TA_PIX_ROUND(pp2x + hints->xmax_delta);
249 slot->lsb_delta = loader->pp1.x - pp1x;
250 slot->rsb_delta = loader->pp2.x - pp2x;
253 /* good, we simply add the glyph to our loader's base */
254 TA_GlyphLoader_Add(gloader);
255 break;
257 case FT_GLYPH_FORMAT_COMPOSITE:
259 FT_UInt nn, num_subglyphs = slot->num_subglyphs;
260 FT_UInt num_base_subgs, start_point;
261 TA_SubGlyph subglyph;
264 start_point = gloader->base.outline.n_points;
266 /* first of all, copy the subglyph descriptors in the glyph loader */
267 error = TA_GlyphLoader_CheckSubGlyphs(gloader, num_subglyphs);
268 if (error)
269 goto Exit;
271 memcpy(gloader->current.subglyphs,
272 slot->subglyphs,
273 num_subglyphs * sizeof (TA_SubGlyphRec));
275 gloader->current.num_subglyphs = num_subglyphs;
276 num_base_subgs = gloader->base.num_subglyphs;
278 /* now read each subglyph independently */
279 for (nn = 0; nn < num_subglyphs; nn++)
281 FT_Vector pp1, pp2;
282 FT_Pos x, y;
283 FT_UInt num_points, num_new_points, num_base_points;
286 /* gloader.current.subglyphs can change during glyph loading due */
287 /* to re-allocation -- we must recompute the current subglyph on */
288 /* each iteration */
289 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
291 pp1 = loader->pp1;
292 pp2 = loader->pp2;
294 num_base_points = gloader->base.outline.n_points;
296 error = ta_loader_load_g(loader, scaler, subglyph->index,
297 load_flags, depth + 1);
298 if (error)
299 goto Exit;
301 /* recompute subglyph pointer */
302 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
304 if (subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS)
306 pp1 = loader->pp1;
307 pp2 = loader->pp2;
309 else
311 loader->pp1 = pp1;
312 loader->pp2 = pp2;
315 num_points = gloader->base.outline.n_points;
316 num_new_points = num_points - num_base_points;
318 /* now perform the transformation required for this subglyph */
319 if (subglyph->flags & (FT_SUBGLYPH_FLAG_SCALE
320 | FT_SUBGLYPH_FLAG_XY_SCALE
321 | FT_SUBGLYPH_FLAG_2X2))
323 FT_Vector* cur = gloader->base.outline.points + num_base_points;
324 FT_Vector* limit = cur + num_new_points;
327 for (; cur < limit; cur++)
328 FT_Vector_Transform(cur, &subglyph->transform);
331 /* apply offset */
332 if (!(subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES))
334 FT_Int k = subglyph->arg1;
335 FT_UInt l = subglyph->arg2;
336 FT_Vector* p1;
337 FT_Vector* p2;
340 if (start_point + k >= num_base_points
341 || l >= (FT_UInt)num_new_points)
343 error = FT_Err_Invalid_Composite;
344 goto Exit;
347 l += num_base_points;
349 /* for now, only use the current point coordinates; */
350 /* we eventually may consider another approach */
351 p1 = gloader->base.outline.points + start_point + k;
352 p2 = gloader->base.outline.points + start_point + l;
354 x = p1->x - p2->x;
355 y = p1->y - p2->y;
357 else
359 x = FT_MulFix(subglyph->arg1, hints->x_scale) + hints->x_delta;
360 y = FT_MulFix(subglyph->arg2, hints->y_scale) + hints->y_delta;
362 x = TA_PIX_ROUND(x);
363 y = TA_PIX_ROUND(y);
367 FT_Outline dummy = gloader->base.outline;
370 dummy.points += num_base_points;
371 dummy.n_points = (short)num_new_points;
373 FT_Outline_Translate(&dummy, x, y);
377 break;
379 default:
380 /* we don't support other formats (yet?) */
381 error = FT_Err_Unimplemented_Feature;
384 Hint_Metrics:
385 if (depth == 0)
387 FT_BBox bbox;
388 FT_Vector vvector;
391 vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX;
392 vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY;
393 vvector.x = FT_MulFix(vvector.x, metrics->scaler.x_scale);
394 vvector.y = FT_MulFix(vvector.y, metrics->scaler.y_scale);
396 /* transform the hinted outline if needed */
397 if (loader->transformed)
399 FT_Outline_Transform(&gloader->base.outline, &loader->trans_matrix);
400 FT_Vector_Transform(&vvector, &loader->trans_matrix);
402 #if 1
403 /* we must translate our final outline by -pp1.x */
404 /* and compute the new metrics */
405 if (loader->pp1.x)
406 FT_Outline_Translate(&gloader->base.outline, -loader->pp1.x, 0);
407 #endif
408 FT_Outline_Get_CBox(&gloader->base.outline, &bbox);
410 bbox.xMin = TA_PIX_FLOOR(bbox.xMin);
411 bbox.yMin = TA_PIX_FLOOR(bbox.yMin);
412 bbox.xMax = TA_PIX_CEIL(bbox.xMax);
413 bbox.yMax = TA_PIX_CEIL(bbox.yMax);
415 slot->metrics.width = bbox.xMax - bbox.xMin;
416 slot->metrics.height = bbox.yMax - bbox.yMin;
417 slot->metrics.horiBearingX = bbox.xMin;
418 slot->metrics.horiBearingY = bbox.yMax;
420 slot->metrics.vertBearingX = TA_PIX_FLOOR(bbox.xMin + vvector.x);
421 slot->metrics.vertBearingY = TA_PIX_FLOOR(bbox.yMax + vvector.y);
423 /* for mono-width fonts (like Andale, Courier, etc.) we need */
424 /* to keep the original rounded advance width; ditto for */
425 /* digits if all have the same advance width */
426 if (FT_IS_FIXED_WIDTH(slot->face)
427 || (ta_face_globals_is_digit(loader->globals, glyph_index)
428 && metrics->digits_have_same_width))
430 slot->metrics.horiAdvance = FT_MulFix(slot->metrics.horiAdvance,
431 metrics->scaler.x_scale);
433 /* set delta values to 0, otherwise code that uses them */
434 /* is going to ruin the fixed advance width */
435 slot->lsb_delta = 0;
436 slot->rsb_delta = 0;
438 else
440 /* non-spacing glyphs must stay as-is */
441 if (slot->metrics.horiAdvance)
442 slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
445 slot->metrics.vertAdvance = FT_MulFix(slot->metrics.vertAdvance,
446 metrics->scaler.y_scale);
448 slot->metrics.horiAdvance = TA_PIX_ROUND(slot->metrics.horiAdvance);
449 slot->metrics.vertAdvance = TA_PIX_ROUND(slot->metrics.vertAdvance);
451 #if 0
452 /* now copy outline into glyph slot */
453 TA_GlyphLoader_Rewind(internal->loader);
454 error = TA_GlyphLoader_CopyPoints(internal->loader, gloader);
455 if (error)
456 goto Exit;
458 slot->outline = internal->loader->base.outline;
459 slot->format = FT_GLYPH_FORMAT_OUTLINE;
460 #endif
463 #ifdef DEBUG_HINTER
464 ta_debug_hinter = hinter;
465 #endif
467 Exit:
468 return error;
472 /* load a glyph */
474 FT_Error
475 ta_loader_load_glyph(TA_Loader loader,
476 FT_Face face,
477 FT_UInt gindex,
478 FT_UInt32 load_flags)
480 FT_Error error;
481 FT_Size size = face->size;
482 TA_ScalerRec scaler;
485 if (!size)
486 return FT_Err_Invalid_Argument;
488 memset(&scaler, 0, sizeof (TA_ScalerRec));
490 scaler.face = face;
491 scaler.x_scale = size->metrics.x_scale;
492 scaler.x_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */
493 scaler.y_scale = size->metrics.y_scale;
494 scaler.y_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */
496 scaler.render_mode = FT_LOAD_TARGET_MODE(load_flags);
497 scaler.flags = 0; /* XXX: fix this */
499 error = ta_loader_reset(loader, face);
500 if (!error)
502 TA_ScriptMetrics metrics;
503 FT_UInt options = 0;
506 #ifdef FT_OPTION_AUTOFIT2
507 /* XXX: undocumented hook to activate the latin2 hinter */
508 if (load_flags & (1UL << 20))
509 options = 2;
510 #endif
512 error = ta_face_globals_get_metrics(loader->globals, gindex,
513 options, &metrics);
514 if (!error)
516 loader->metrics = metrics;
518 if (metrics->clazz->script_metrics_scale)
519 metrics->clazz->script_metrics_scale(metrics, &scaler);
520 else
521 metrics->scaler = scaler;
523 load_flags |= FT_LOAD_NO_SCALE
524 | FT_LOAD_IGNORE_TRANSFORM;
525 load_flags &= ~FT_LOAD_RENDER;
527 if (metrics->clazz->script_hints_init)
529 error = metrics->clazz->script_hints_init(&loader->hints,
530 metrics);
531 if (error)
532 goto Exit;
535 error = ta_loader_load_g(loader, &scaler, gindex, load_flags, 0);
538 Exit:
539 return error;
543 void
544 ta_loader_register_hints_recorder(TA_Loader loader,
545 TA_Hints_Recorder hints_recorder,
546 void *user)
548 loader->hints.recorder = hints_recorder;
549 loader->hints.user = user;
552 /* end of taloader.c */