[gui] Pass missing command line flags.
[ttfautohint.git] / lib / taloader.c
blob30b64eb6c4921733d3abcc3d5c665c622da81fdb
1 /* taloader.c */
3 /*
4 * Copyright (C) 2011-2012 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 "taloader.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(TA_Loader loader)
47 memset(loader, 0, sizeof (TA_LoaderRec));
49 ta_glyph_hints_init(&loader->hints);
50 #ifdef TA_DEBUG
51 _ta_debug_hints = &loader->hints;
52 #endif
53 return TA_GlyphLoader_New(&loader->gloader);
57 /* reset glyph loader and compute globals if necessary */
59 FT_Error
60 ta_loader_reset(TA_Loader loader,
61 FT_Face face,
62 FT_UInt fallback_script)
64 FT_Error error = FT_Err_Ok;
67 loader->face = face;
68 loader->globals = (TA_FaceGlobals)face->autohint.data;
70 TA_GlyphLoader_Rewind(loader->gloader);
72 if (loader->globals == NULL)
74 error = ta_face_globals_new(face, &loader->globals, fallback_script);
75 if (!error)
77 face->autohint.data = (FT_Pointer)loader->globals;
78 face->autohint.finalizer = (FT_Generic_Finalizer)ta_face_globals_free;
82 return error;
86 /* finalize glyph loader */
88 void
89 ta_loader_done(TA_Loader loader)
91 ta_glyph_hints_done(&loader->hints);
93 loader->face = NULL;
94 loader->globals = NULL;
96 #ifdef TA_DEBUG
97 _ta_debug_hints = NULL;
98 #endif
99 TA_GlyphLoader_Done(loader->gloader);
100 loader->gloader = NULL;
104 /* load a single glyph component; this routine calls itself recursively, */
105 /* if necessary, and does the main work of `ta_loader_load_glyph' */
107 static FT_Error
108 ta_loader_load_g(TA_Loader loader,
109 TA_Scaler scaler,
110 FT_UInt glyph_index,
111 FT_Int32 load_flags,
112 FT_UInt depth)
114 FT_Error error;
115 FT_Face face = loader->face;
116 TA_GlyphLoader gloader = loader->gloader;
117 TA_ScriptMetrics metrics = loader->metrics;
118 TA_GlyphHints hints = &loader->hints;
119 FT_GlyphSlot slot = face->glyph;
120 #if 0
121 FT_Slot_Internal internal = slot->internal;
122 #endif
125 error = FT_Load_Glyph(face, glyph_index, load_flags);
126 if (error)
127 goto Exit;
129 #if 0
130 loader->transformed = internal->glyph_transformed;
131 if (loader->transformed)
133 FT_Matrix inverse;
136 loader->trans_matrix = internal->glyph_matrix;
137 loader->trans_delta = internal->glyph_delta;
139 inverse = loader->trans_matrix;
140 FT_Matrix_Invert(&inverse);
141 FT_Vector_Transform(&loader->trans_delta, &inverse);
143 #endif
145 /* set linear metrics */
146 slot->linearHoriAdvance = slot->metrics.horiAdvance;
147 slot->linearVertAdvance = slot->metrics.vertAdvance;
149 switch (slot->format)
151 case FT_GLYPH_FORMAT_OUTLINE:
152 /* translate the loaded glyph when an internal transform is needed */
153 if (loader->transformed)
154 FT_Outline_Translate(&slot->outline,
155 loader->trans_delta.x,
156 loader->trans_delta.y);
158 /* copy the outline points in the loader's current extra points */
159 /* which are used to keep original glyph coordinates */
160 error = TA_GLYPHLOADER_CHECK_POINTS(gloader,
161 slot->outline.n_points + 4,
162 slot->outline.n_contours);
163 if (error)
164 goto Exit;
166 memcpy(gloader->current.outline.points,
167 slot->outline.points,
168 slot->outline.n_points * sizeof (FT_Vector));
169 memcpy(gloader->current.outline.contours,
170 slot->outline.contours,
171 slot->outline.n_contours * sizeof (short));
172 memcpy(gloader->current.outline.tags,
173 slot->outline.tags,
174 slot->outline.n_points * sizeof (char));
176 gloader->current.outline.n_points = slot->outline.n_points;
177 gloader->current.outline.n_contours = slot->outline.n_contours;
179 /* compute original horizontal phantom points */
180 /* (and ignore vertical ones) */
181 loader->pp1.x = hints->x_delta;
182 loader->pp1.y = hints->y_delta;
183 loader->pp2.x = FT_MulFix(slot->metrics.horiAdvance,
184 hints->x_scale) + hints->x_delta;
185 loader->pp2.y = hints->y_delta;
187 /* be sure to check for spacing glyphs */
188 if (slot->outline.n_points == 0)
189 goto Hint_Metrics;
191 /* now load the slot image into the auto-outline */
192 /* and run the automatic hinting process */
193 if (metrics->clazz->script_hints_apply)
194 metrics->clazz->script_hints_apply(hints,
195 &gloader->current.outline,
196 metrics);
198 /* we now need to adjust the metrics according to the change in */
199 /* width/positioning that occurred during the hinting process */
200 if (scaler->render_mode != FT_RENDER_MODE_LIGHT)
202 FT_Pos old_rsb, old_lsb, new_lsb;
203 FT_Pos pp1x_uh, pp2x_uh;
204 TA_AxisHints axis = &hints->axis[TA_DIMENSION_HORZ];
206 TA_Edge edge1 = axis->edges; /* leftmost edge */
207 TA_Edge edge2 = edge1 + axis->num_edges - 1; /* rightmost edge */
210 if (axis->num_edges > 1 && TA_HINTS_DO_ADVANCE(hints))
212 old_rsb = loader->pp2.x - edge2->opos;
213 old_lsb = edge1->opos;
214 new_lsb = edge1->pos;
216 /* remember unhinted values to later account */
217 /* for rounding errors */
218 pp1x_uh = new_lsb - old_lsb;
219 pp2x_uh = edge2->pos + old_rsb;
221 /* prefer too much space over too little space */
222 /* for very small sizes */
223 if (old_lsb < 24)
224 pp1x_uh -= 8;
225 if (old_rsb < 24)
226 pp2x_uh += 8;
228 loader->pp1.x = TA_PIX_ROUND(pp1x_uh);
229 loader->pp2.x = TA_PIX_ROUND(pp2x_uh);
231 if (loader->pp1.x >= new_lsb
232 && old_lsb > 0)
233 loader->pp1.x -= 64;
234 if (loader->pp2.x <= edge2->pos
235 && old_rsb > 0)
236 loader->pp2.x += 64;
238 slot->lsb_delta = loader->pp1.x - pp1x_uh;
239 slot->rsb_delta = loader->pp2.x - pp2x_uh;
241 else
243 FT_Pos pp1x = loader->pp1.x;
244 FT_Pos pp2x = loader->pp2.x;
247 loader->pp1.x = TA_PIX_ROUND(pp1x);
248 loader->pp2.x = TA_PIX_ROUND(pp2x);
250 slot->lsb_delta = loader->pp1.x - pp1x;
251 slot->rsb_delta = loader->pp2.x - pp2x;
254 else
256 FT_Pos pp1x = loader->pp1.x;
257 FT_Pos pp2x = loader->pp2.x;
260 loader->pp1.x = TA_PIX_ROUND(pp1x + hints->xmin_delta);
261 loader->pp2.x = TA_PIX_ROUND(pp2x + hints->xmax_delta);
263 slot->lsb_delta = loader->pp1.x - pp1x;
264 slot->rsb_delta = loader->pp2.x - pp2x;
267 /* good, we simply add the glyph to our loader's base */
268 TA_GlyphLoader_Add(gloader);
269 break;
271 case FT_GLYPH_FORMAT_COMPOSITE:
273 FT_UInt nn, num_subglyphs = slot->num_subglyphs;
274 FT_UInt num_base_subgs, start_point;
275 TA_SubGlyph subglyph;
278 start_point = gloader->base.outline.n_points;
280 /* first of all, copy the subglyph descriptors in the glyph loader */
281 error = TA_GlyphLoader_CheckSubGlyphs(gloader, num_subglyphs);
282 if (error)
283 goto Exit;
285 memcpy(gloader->current.subglyphs,
286 slot->subglyphs,
287 num_subglyphs * sizeof (TA_SubGlyphRec));
289 gloader->current.num_subglyphs = num_subglyphs;
290 num_base_subgs = gloader->base.num_subglyphs;
292 /* now read each subglyph independently */
293 for (nn = 0; nn < num_subglyphs; nn++)
295 FT_Vector pp1, pp2;
296 FT_Pos x, y;
297 FT_UInt num_points, num_new_points, num_base_points;
300 /* gloader.current.subglyphs can change during glyph loading due */
301 /* to re-allocation -- we must recompute the current subglyph on */
302 /* each iteration */
303 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
305 pp1 = loader->pp1;
306 pp2 = loader->pp2;
308 num_base_points = gloader->base.outline.n_points;
310 error = ta_loader_load_g(loader, scaler, subglyph->index,
311 load_flags, depth + 1);
312 if (error)
313 goto Exit;
315 /* recompute subglyph pointer */
316 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
318 if (subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS)
320 pp1 = loader->pp1;
321 pp2 = loader->pp2;
323 else
325 loader->pp1 = pp1;
326 loader->pp2 = pp2;
329 num_points = gloader->base.outline.n_points;
330 num_new_points = num_points - num_base_points;
332 /* now perform the transformation required for this subglyph */
333 if (subglyph->flags & (FT_SUBGLYPH_FLAG_SCALE
334 | FT_SUBGLYPH_FLAG_XY_SCALE
335 | FT_SUBGLYPH_FLAG_2X2))
337 FT_Vector* cur = gloader->base.outline.points + num_base_points;
338 FT_Vector* limit = cur + num_new_points;
341 for (; cur < limit; cur++)
342 FT_Vector_Transform(cur, &subglyph->transform);
345 /* apply offset */
346 if (!(subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES))
348 FT_Int k = subglyph->arg1;
349 FT_UInt l = subglyph->arg2;
350 FT_Vector* p1;
351 FT_Vector* p2;
354 if (start_point + k >= num_base_points
355 || l >= (FT_UInt)num_new_points)
357 error = FT_Err_Invalid_Composite;
358 goto Exit;
361 l += num_base_points;
363 /* for now, only use the current point coordinates; */
364 /* we eventually may consider another approach */
365 p1 = gloader->base.outline.points + start_point + k;
366 p2 = gloader->base.outline.points + start_point + l;
368 x = p1->x - p2->x;
369 y = p1->y - p2->y;
371 else
373 x = FT_MulFix(subglyph->arg1, hints->x_scale) + hints->x_delta;
374 y = FT_MulFix(subglyph->arg2, hints->y_scale) + hints->y_delta;
376 x = TA_PIX_ROUND(x);
377 y = TA_PIX_ROUND(y);
381 FT_Outline dummy = gloader->base.outline;
384 dummy.points += num_base_points;
385 dummy.n_points = (short)num_new_points;
387 FT_Outline_Translate(&dummy, x, y);
391 break;
393 default:
394 /* we don't support other formats (yet?) */
395 error = FT_Err_Unimplemented_Feature;
398 Hint_Metrics:
399 if (depth == 0)
401 FT_BBox bbox;
402 FT_Vector vvector;
405 vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX;
406 vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY;
407 vvector.x = FT_MulFix(vvector.x, metrics->scaler.x_scale);
408 vvector.y = FT_MulFix(vvector.y, metrics->scaler.y_scale);
410 /* transform the hinted outline if needed */
411 if (loader->transformed)
413 FT_Outline_Transform(&gloader->base.outline, &loader->trans_matrix);
414 FT_Vector_Transform(&vvector, &loader->trans_matrix);
416 #if 1
417 /* we must translate our final outline by -pp1.x */
418 /* and compute the new metrics */
419 if (loader->pp1.x)
420 FT_Outline_Translate(&gloader->base.outline, -loader->pp1.x, 0);
421 #endif
422 FT_Outline_Get_CBox(&gloader->base.outline, &bbox);
424 bbox.xMin = TA_PIX_FLOOR(bbox.xMin);
425 bbox.yMin = TA_PIX_FLOOR(bbox.yMin);
426 bbox.xMax = TA_PIX_CEIL(bbox.xMax);
427 bbox.yMax = TA_PIX_CEIL(bbox.yMax);
429 slot->metrics.width = bbox.xMax - bbox.xMin;
430 slot->metrics.height = bbox.yMax - bbox.yMin;
431 slot->metrics.horiBearingX = bbox.xMin;
432 slot->metrics.horiBearingY = bbox.yMax;
434 slot->metrics.vertBearingX = TA_PIX_FLOOR(bbox.xMin + vvector.x);
435 slot->metrics.vertBearingY = TA_PIX_FLOOR(bbox.yMax + vvector.y);
437 /* for mono-width fonts (like Andale, Courier, etc.) we need */
438 /* to keep the original rounded advance width; ditto for */
439 /* digits if all have the same advance width */
440 if (scaler->render_mode != FT_RENDER_MODE_LIGHT
441 && (FT_IS_FIXED_WIDTH(slot->face)
442 || (ta_face_globals_is_digit(loader->globals, glyph_index)
443 && metrics->digits_have_same_width)))
445 slot->metrics.horiAdvance = FT_MulFix(slot->metrics.horiAdvance,
446 metrics->scaler.x_scale);
448 /* set delta values to 0, otherwise code that uses them */
449 /* is going to ruin the fixed advance width */
450 slot->lsb_delta = 0;
451 slot->rsb_delta = 0;
453 else
455 /* non-spacing glyphs must stay as-is */
456 if (slot->metrics.horiAdvance)
457 slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
460 slot->metrics.vertAdvance = FT_MulFix(slot->metrics.vertAdvance,
461 metrics->scaler.y_scale);
463 slot->metrics.horiAdvance = TA_PIX_ROUND(slot->metrics.horiAdvance);
464 slot->metrics.vertAdvance = TA_PIX_ROUND(slot->metrics.vertAdvance);
466 #if 0
467 /* now copy outline into glyph slot */
468 TA_GlyphLoader_Rewind(internal->loader);
469 error = TA_GlyphLoader_CopyPoints(internal->loader, gloader);
470 if (error)
471 goto Exit;
473 /* reassign all outline fields except flags to protect them */
474 slot->outline.n_contours = internal->loader->base.outline.n_contours;
475 slot->outline.n_points = internal->loader->base.outline.n_points;
476 slot->outline.points = internal->loader->base.outline.points;
477 slot->outline.tags = internal->loader->base.outline.tags;
478 slot->outline.contours = internal->loader->base.outline.contours;
480 slot->format = FT_GLYPH_FORMAT_OUTLINE;
481 #endif
484 Exit:
485 return error;
489 /* load a glyph */
491 FT_Error
492 ta_loader_load_glyph(TA_Loader loader,
493 FT_Face face,
494 FT_UInt gindex,
495 FT_Int32 load_flags)
497 FT_Error error;
498 FT_Size size = face->size;
499 TA_ScalerRec scaler;
500 FT_UInt fallback_script;
503 if (!size)
504 return FT_Err_Invalid_Argument;
506 memset(&scaler, 0, sizeof (TA_ScalerRec));
508 scaler.face = face;
509 scaler.x_scale = size->metrics.x_scale;
510 scaler.x_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */
511 scaler.y_scale = size->metrics.y_scale;
512 scaler.y_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */
514 scaler.render_mode = FT_LOAD_TARGET_MODE(load_flags);
515 scaler.flags = 0; /* XXX: fix this */
517 /* XXX this is an ugly hack of ttfautohint: */
518 /* bit 30 and 31 of `load_flags' specify the fallback script, */
519 /* and bit 29 holds the `ignore_x_height' value */
520 fallback_script = load_flags >> 30;
521 if (load_flags & (1 << 29))
522 scaler.flags |= TA_SCALER_FLAG_INCREASE_X_HEIGHT;
524 /* note that the fallback script can't be changed anymore */
525 /* after the first call of `ta_loader_load_glyph' */
526 error = ta_loader_reset(loader, face, fallback_script);
527 if (!error)
529 TA_ScriptMetrics metrics;
530 FT_UInt options = 0;
533 #ifdef FT_OPTION_AUTOFIT2
534 /* XXX: undocumented hook to activate the latin2 hinter */
535 if (load_flags & (1UL << 20))
536 options = 2;
537 #endif
539 error = ta_face_globals_get_metrics(loader->globals, gindex,
540 options, &metrics);
541 if (!error)
543 loader->metrics = metrics;
545 if (metrics->clazz->script_metrics_scale)
546 metrics->clazz->script_metrics_scale(metrics, &scaler);
547 else
548 metrics->scaler = scaler;
550 load_flags |= FT_LOAD_NO_SCALE
551 | FT_LOAD_IGNORE_TRANSFORM;
552 load_flags &= ~FT_LOAD_RENDER;
554 if (metrics->clazz->script_hints_init)
556 error = metrics->clazz->script_hints_init(&loader->hints,
557 metrics);
558 if (error)
559 goto Exit;
562 error = ta_loader_load_g(loader, &scaler, gindex, load_flags, 0);
565 Exit:
566 return error;
570 void
571 ta_loader_register_hints_recorder(TA_Loader loader,
572 TA_Hints_Recorder hints_recorder,
573 void* user)
575 loader->hints.recorder = hints_recorder;
576 loader->hints.user = user;
579 /* end of taloader.c */