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> */
30 /* from file `ftobjs.h' (2011-Mar-28) from FreeType */
31 typedef struct FT_Slot_InternalRec_
33 TA_GlyphLoader loader
;
35 FT_Bool glyph_transformed
;
36 FT_Matrix glyph_matrix
;
37 FT_Vector glyph_delta
;
39 } FT_GlyphSlot_InternalRec
;
42 /* initialize glyph loader */
45 ta_loader_init(TA_Loader loader
)
47 memset(loader
, 0, sizeof (TA_LoaderRec
));
49 ta_glyph_hints_init(&loader
->hints
);
51 _ta_debug_hints
= &loader
->hints
;
53 return TA_GlyphLoader_New(&loader
->gloader
);
57 /* reset glyph loader and compute globals if necessary */
60 ta_loader_reset(TA_Loader loader
,
62 FT_UInt fallback_script
)
64 FT_Error error
= FT_Err_Ok
;
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
);
77 face
->autohint
.data
= (FT_Pointer
)loader
->globals
;
78 face
->autohint
.finalizer
= (FT_Generic_Finalizer
)ta_face_globals_free
;
86 /* finalize glyph loader */
89 ta_loader_done(TA_Loader loader
)
91 ta_glyph_hints_done(&loader
->hints
);
94 loader
->globals
= NULL
;
97 _ta_debug_hints
= NULL
;
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' */
108 ta_loader_load_g(TA_Loader loader
,
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
;
121 FT_Slot_Internal internal
= slot
->internal
;
125 error
= FT_Load_Glyph(face
, glyph_index
, load_flags
);
130 loader
->transformed
= internal
->glyph_transformed
;
131 if (loader
->transformed
)
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
);
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
);
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
,
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)
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
,
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 */
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
234 if (loader
->pp2
.x
<= edge2
->pos
238 slot
->lsb_delta
= loader
->pp1
.x
- pp1x_uh
;
239 slot
->rsb_delta
= loader
->pp2
.x
- pp2x_uh
;
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
;
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
);
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
);
285 memcpy(gloader
->current
.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
++)
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 */
303 subglyph
= gloader
->base
.subglyphs
+ num_base_subgs
+ nn
;
308 num_base_points
= gloader
->base
.outline
.n_points
;
310 error
= ta_loader_load_g(loader
, scaler
, subglyph
->index
,
311 load_flags
, depth
+ 1);
315 /* recompute subglyph pointer */
316 subglyph
= gloader
->base
.subglyphs
+ num_base_subgs
+ nn
;
318 if (subglyph
->flags
& FT_SUBGLYPH_FLAG_USE_MY_METRICS
)
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
);
346 if (!(subglyph
->flags
& FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES
))
348 FT_Int k
= subglyph
->arg1
;
349 FT_UInt l
= subglyph
->arg2
;
354 if (start_point
+ k
>= num_base_points
355 || l
>= (FT_UInt
)num_new_points
)
357 error
= FT_Err_Invalid_Composite
;
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
;
373 x
= FT_MulFix(subglyph
->arg1
, hints
->x_scale
) + hints
->x_delta
;
374 y
= FT_MulFix(subglyph
->arg2
, hints
->y_scale
) + hints
->y_delta
;
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
);
394 /* we don't support other formats (yet?) */
395 error
= FT_Err_Unimplemented_Feature
;
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
);
417 /* we must translate our final outline by -pp1.x */
418 /* and compute the new metrics */
420 FT_Outline_Translate(&gloader
->base
.outline
, -loader
->pp1
.x
, 0);
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 (FT_IS_FIXED_WIDTH(slot
->face
)
441 || (ta_face_globals_is_digit(loader
->globals
, glyph_index
)
442 && metrics
->digits_have_same_width
))
444 slot
->metrics
.horiAdvance
= FT_MulFix(slot
->metrics
.horiAdvance
,
445 metrics
->scaler
.x_scale
);
447 /* set delta values to 0, otherwise code that uses them */
448 /* is going to ruin the fixed advance width */
454 /* non-spacing glyphs must stay as-is */
455 if (slot
->metrics
.horiAdvance
)
456 slot
->metrics
.horiAdvance
= loader
->pp2
.x
- loader
->pp1
.x
;
459 slot
->metrics
.vertAdvance
= FT_MulFix(slot
->metrics
.vertAdvance
,
460 metrics
->scaler
.y_scale
);
462 slot
->metrics
.horiAdvance
= TA_PIX_ROUND(slot
->metrics
.horiAdvance
);
463 slot
->metrics
.vertAdvance
= TA_PIX_ROUND(slot
->metrics
.vertAdvance
);
466 /* now copy outline into glyph slot */
467 TA_GlyphLoader_Rewind(internal
->loader
);
468 error
= TA_GlyphLoader_CopyPoints(internal
->loader
, gloader
);
472 slot
->outline
= internal
->loader
->base
.outline
;
473 slot
->format
= FT_GLYPH_FORMAT_OUTLINE
;
478 ta_debug_hinter
= hinter
;
489 ta_loader_load_glyph(TA_Loader loader
,
492 FT_UInt32 load_flags
)
495 FT_Size size
= face
->size
;
497 FT_UInt fallback_script
;
501 return FT_Err_Invalid_Argument
;
503 memset(&scaler
, 0, sizeof (TA_ScalerRec
));
506 scaler
.x_scale
= size
->metrics
.x_scale
;
507 scaler
.x_delta
= 0; /* XXX: TODO: add support for sub-pixel hinting */
508 scaler
.y_scale
= size
->metrics
.y_scale
;
509 scaler
.y_delta
= 0; /* XXX: TODO: add support for sub-pixel hinting */
511 scaler
.render_mode
= FT_LOAD_TARGET_MODE(load_flags
);
512 scaler
.flags
= 0; /* XXX: fix this */
514 /* XXX this is an ugly hack of ttfautohint: */
515 /* bit 30 and 31 of `load_flags' specify the fallback script */
516 fallback_script
= load_flags
>> 30;
518 /* note that the fallback script can't be changed anymore */
519 /* after the first call of `ta_loader_load_glyph' */
520 error
= ta_loader_reset(loader
, face
, fallback_script
);
523 TA_ScriptMetrics metrics
;
527 #ifdef FT_OPTION_AUTOFIT2
528 /* XXX: undocumented hook to activate the latin2 hinter */
529 if (load_flags
& (1UL << 20))
533 error
= ta_face_globals_get_metrics(loader
->globals
, gindex
,
537 loader
->metrics
= metrics
;
539 if (metrics
->clazz
->script_metrics_scale
)
540 metrics
->clazz
->script_metrics_scale(metrics
, &scaler
);
542 metrics
->scaler
= scaler
;
544 load_flags
|= FT_LOAD_NO_SCALE
545 | FT_LOAD_IGNORE_TRANSFORM
;
546 load_flags
&= ~FT_LOAD_RENDER
;
548 if (metrics
->clazz
->script_hints_init
)
550 error
= metrics
->clazz
->script_hints_init(&loader
->hints
,
556 error
= ta_loader_load_g(loader
, &scaler
, gindex
, load_flags
, 0);
565 ta_loader_register_hints_recorder(TA_Loader loader
,
566 TA_Hints_Recorder hints_recorder
,
569 loader
->hints
.recorder
= hints_recorder
;
570 loader
->hints
.user
= user
;
573 /* end of taloader.c */