xref: /plan9/sys/src/cmd/gs/src/gxhintn.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1*593dc095SDavid du Colombier /* Copyright (C) 2003 artofcode LLC.  All rights reserved.
2*593dc095SDavid du Colombier 
3*593dc095SDavid du Colombier   This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier   implied.
5*593dc095SDavid du Colombier 
6*593dc095SDavid du Colombier   This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier   modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier   of the license contained in the file LICENSE in this distribution.
9*593dc095SDavid du Colombier 
10*593dc095SDavid du Colombier   For more information about licensing, please refer to
11*593dc095SDavid du Colombier   http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier   commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15*593dc095SDavid du Colombier */
16*593dc095SDavid du Colombier 
17*593dc095SDavid du Colombier /* $Id: gxhintn.c,v 1.61 2005/09/04 20:42:53 leonardo Exp $ */
18*593dc095SDavid du Colombier /* Type 1 hinter, a new algorithm */
19*593dc095SDavid du Colombier 
20*593dc095SDavid du Colombier #include "memory_.h"
21*593dc095SDavid du Colombier #include "math_.h"
22*593dc095SDavid du Colombier #include "gx.h"
23*593dc095SDavid du Colombier #include "gxfixed.h"
24*593dc095SDavid du Colombier #include "gxarith.h"
25*593dc095SDavid du Colombier #include "gstypes.h"
26*593dc095SDavid du Colombier #include "gxmatrix.h"
27*593dc095SDavid du Colombier #include "gxpath.h"
28*593dc095SDavid du Colombier #include "gxfont.h"
29*593dc095SDavid du Colombier #include "gxfont1.h"
30*593dc095SDavid du Colombier #include "gxtype1.h"
31*593dc095SDavid du Colombier #include "gxhintn.h"
32*593dc095SDavid du Colombier #include "gzpath.h"
33*593dc095SDavid du Colombier #include "gserrors.h"
34*593dc095SDavid du Colombier #include "vdtrace.h"
35*593dc095SDavid du Colombier 
36*593dc095SDavid du Colombier /*  todo :
37*593dc095SDavid du Colombier     - Diagonal stems are not hinted;
38*593dc095SDavid du Colombier     - Some fonts have no StdHW, StdWW. Adobe appears to autohint them.
39*593dc095SDavid du Colombier     - Measure Adobe's flattness parameter.
40*593dc095SDavid du Colombier     - Test Adobe compatibility for rotated/skewed glyphs.
41*593dc095SDavid du Colombier  */
42*593dc095SDavid du Colombier 
43*593dc095SDavid du Colombier 
44*593dc095SDavid du Colombier 
45*593dc095SDavid du Colombier /*  Stem processing basics :
46*593dc095SDavid du Colombier     (See the glyph AE in Times-Roman by Adobe.)
47*593dc095SDavid du Colombier 
48*593dc095SDavid du Colombier     0. This supposes that glyph is transformed to device space
49*593dc095SDavid du Colombier        with a random matrix.
50*593dc095SDavid du Colombier 
51*593dc095SDavid du Colombier        All outline poles and all hint commands are stored in arrays
52*593dc095SDavid du Colombier        before staring the exact processing.
53*593dc095SDavid du Colombier 
54*593dc095SDavid du Colombier        HR pole is pole before which stem replacement happens.
55*593dc095SDavid du Colombier 
56*593dc095SDavid du Colombier     1. Stem hints may be primary ones (defined in the beginning of charstring),
57*593dc095SDavid du Colombier        and secondary ones (defined at HR poles). Consider that
58*593dc095SDavid du Colombier        secondary stem hints may be redundant (see AE in Times-Roman).
59*593dc095SDavid du Colombier        Secondary stems are HIGHER priority than basic ones.
60*593dc095SDavid du Colombier 
61*593dc095SDavid du Colombier     2. The range of secondary stem command is from its HR pole to next HR pole.
62*593dc095SDavid du Colombier        The range of primary stem command is entire glyph.
63*593dc095SDavid du Colombier 
64*593dc095SDavid du Colombier     3. The TT interpreter aligned stem3 with centering the middle stem.
65*593dc095SDavid du Colombier 
66*593dc095SDavid du Colombier     4. If a stem boundary corresponds to a pole aligned with an alignment zone,
67*593dc095SDavid du Colombier        pass aligned coordinate to the stem command.
68*593dc095SDavid du Colombier        Use the stem boundary longitude middle point for alignment with
69*593dc095SDavid du Colombier        skewed or rotated matrix. Use standard stem width for computing
70*593dc095SDavid du Colombier        opposite coordinates.
71*593dc095SDavid du Colombier 
72*593dc095SDavid du Colombier     5. If several stems have a same boundary coordinate,
73*593dc095SDavid du Colombier        this boundary gets more priority when aligned.
74*593dc095SDavid du Colombier 
75*593dc095SDavid du Colombier     6. Considering each set of repeating stem commands as a stem complex, pass
76*593dc095SDavid du Colombier        aligned coordinates to opposite boundaries of stem commands.
77*593dc095SDavid du Colombier 
78*593dc095SDavid du Colombier     7. Pass aligned boundary coordinate to poles within stem command range.
79*593dc095SDavid du Colombier        Note that this will pass aligned coordinates back to poles,
80*593dc095SDavid du Colombier        from which stem alignment was taken.
81*593dc095SDavid du Colombier 
82*593dc095SDavid du Colombier     8. Interpolate unaligned poles.
83*593dc095SDavid du Colombier 
84*593dc095SDavid du Colombier     9. After the alignment is done, it is desirable to check for
85*593dc095SDavid du Colombier        anomalous negative contours and fix them, but we have no
86*593dc095SDavid du Colombier        good algorithm for this. The rasterizer must be tolerant
87*593dc095SDavid du Colombier        to such contours (which may have self-crosses, self-contacts,
88*593dc095SDavid du Colombier        or may change to opposite direction).
89*593dc095SDavid du Colombier 
90*593dc095SDavid du Colombier */
91*593dc095SDavid du Colombier 
92*593dc095SDavid du Colombier /*  Dotsection processing basics :
93*593dc095SDavid du Colombier 
94*593dc095SDavid du Colombier     If stem replacement occures, dotsection to be ignored.
95*593dc095SDavid du Colombier     To check this properly, we test whether extremal poles of contour
96*593dc095SDavid du Colombier     were actually aligned with stem hints.
97*593dc095SDavid du Colombier 
98*593dc095SDavid du Colombier     If a contour was aligned with stem hints by both X and Y,
99*593dc095SDavid du Colombier     no special processing required.
100*593dc095SDavid du Colombier 
101*593dc095SDavid du Colombier     Otherwise if dotsection center falls near vstem axis,
102*593dc095SDavid du Colombier     we align it by X with the axis. Otherwise we align
103*593dc095SDavid du Colombier     it by X to half-pixel. Then we align the center by Y to
104*593dc095SDavid du Colombier     half-pixel, and shift entire contour to satisfy
105*593dc095SDavid du Colombier     the alignment of the center.
106*593dc095SDavid du Colombier */
107*593dc095SDavid du Colombier 
108*593dc095SDavid du Colombier /*  vstem3/hstem3 processing basics :
109*593dc095SDavid du Colombier     They are handled by the type 1,2 interpreters (gstype1.c, gstype2.c).
110*593dc095SDavid du Colombier  */
111*593dc095SDavid du Colombier 
112*593dc095SDavid du Colombier /*  flex processing basics :
113*593dc095SDavid du Colombier     With type 1 it is handled with t1_hinter__flex_* functions.
114*593dc095SDavid du Colombier     With type 2 it is handled by gstype2.c .
115*593dc095SDavid du Colombier  */
116*593dc095SDavid du Colombier 
117*593dc095SDavid du Colombier #define VD_DRAW_IMPORT 0 /* CAUTION: with 1 can't close DC on import error */
118*593dc095SDavid du Colombier #define VD_SCALE  (4.0 / 4096.0)
119*593dc095SDavid du Colombier #define VD_SHIFT_X 50
120*593dc095SDavid du Colombier #define VD_SHIFT_Y 100
121*593dc095SDavid du Colombier #define VD_PAINT_POLE_IDS 1
122*593dc095SDavid du Colombier #define VD_IMPORT_COLOR RGB(255, 0, 0)
123*593dc095SDavid du Colombier 
124*593dc095SDavid du Colombier #define ADOBE_OVERSHOOT_COMPATIBILIY 0
125*593dc095SDavid du Colombier #define ADOBE_SHIFT_CHARPATH 0
126*593dc095SDavid du Colombier 
127*593dc095SDavid du Colombier /*  The CONTRAST_STEMS option aligns one of two stem boundaries
128*593dc095SDavid du Colombier     to integral pixel boundary when AlignToPixels = 0.
129*593dc095SDavid du Colombier     It gives more contrast stems, because a bigger part
130*593dc095SDavid du Colombier     of boldness is concentrated in smaller number of pixels.
131*593dc095SDavid du Colombier */
132*593dc095SDavid du Colombier #define CONTRAST_STEMS 1
133*593dc095SDavid du Colombier 
134*593dc095SDavid du Colombier static const char *s_pole_array = "t1_hinter pole array";
135*593dc095SDavid du Colombier static const char *s_zone_array = "t1_hinter zone array";
136*593dc095SDavid du Colombier static const char *s_hint_array = "t1_hinter hint array";
137*593dc095SDavid du Colombier static const char *s_contour_array = "t1_hinter contour array";
138*593dc095SDavid du Colombier static const char *s_hint_range_array = "t1_hinter hint_range array";
139*593dc095SDavid du Colombier static const char *s_stem_snap_array = "t1_hinter stem_snap array";
140*593dc095SDavid du Colombier 
141*593dc095SDavid du Colombier #define member_prt(type, ptr, offset) (type *)((char *)(ptr) + (offset))
142*593dc095SDavid du Colombier 
143*593dc095SDavid du Colombier typedef int32_t int24;
144*593dc095SDavid du Colombier #define HAVE_INT64_T
145*593dc095SDavid du Colombier 
146*593dc095SDavid du Colombier private const unsigned int split_bits = 12;
147*593dc095SDavid du Colombier private const unsigned int max_coord_bits = 24; /* = split_bits * 2 */
148*593dc095SDavid du Colombier private const unsigned int matrix_bits = 19; /* <= sizeof(int) * 8 - 1 - split_bits */
149*593dc095SDavid du Colombier private const unsigned int g2o_bitshift = 12; /* <= matrix_bits + max_coord_bits - (sizeof(int) * 8 + 1) */
150*593dc095SDavid du Colombier private const int32_t FFFFF000 = ~(int32_t)0xFFF; /* = ~(((int32_t)1 << split_bits) - 1) */
151*593dc095SDavid du Colombier /* Constants above must satisfy expressions given in comments. */
152*593dc095SDavid du Colombier 
153*593dc095SDavid du Colombier /* Computes (a*b)>>s, s <= 12 */
mul_shift(int24 a,int19 b,unsigned int s)154*593dc095SDavid du Colombier private inline int32_t mul_shift(int24 a, int19 b, unsigned int s)
155*593dc095SDavid du Colombier {
156*593dc095SDavid du Colombier #ifdef HAVE_INT64_T
157*593dc095SDavid du Colombier     return ( (int64_t)a * (int64_t)b ) >> s; /* unrounded result */
158*593dc095SDavid du Colombier #else
159*593dc095SDavid du Colombier     { /* 32 bit fallback */
160*593dc095SDavid du Colombier         int32_t aa = a & FFFFF000, a0 = a - aa, a1 = aa >> s;
161*593dc095SDavid du Colombier         return ((a0 * b) >> s) + a1 * b; /* unrounded result */
162*593dc095SDavid du Colombier     }
163*593dc095SDavid du Colombier #endif
164*593dc095SDavid du Colombier }
165*593dc095SDavid du Colombier 
166*593dc095SDavid du Colombier /* Computes (a*b)>>s, s <= 12, with rounding */
mul_shift_round(int24 a,int19 b,unsigned int s)167*593dc095SDavid du Colombier private inline int32_t mul_shift_round(int24 a, int19 b, unsigned int s)
168*593dc095SDavid du Colombier {
169*593dc095SDavid du Colombier #ifdef HAVE_INT64_T
170*593dc095SDavid du Colombier     return (( ( (int64_t)a * (int64_t)b ) >> (s - 1)) + 1) >> 1;
171*593dc095SDavid du Colombier #else
172*593dc095SDavid du Colombier     { /* 32 bit version */
173*593dc095SDavid du Colombier         int32_t aa = a & FFFFF000, a0 = a - aa, a1 = aa >> s;
174*593dc095SDavid du Colombier         return ((((a0 * b) >> (s - 1)) + 1) >> 1) + a1 * b; /* rounded result */
175*593dc095SDavid du Colombier     }
176*593dc095SDavid du Colombier #endif
177*593dc095SDavid du Colombier }
178*593dc095SDavid du Colombier 
shift_rounded(int32_t v,unsigned int s)179*593dc095SDavid du Colombier private inline int32_t shift_rounded(int32_t v, unsigned int s)
180*593dc095SDavid du Colombier {   return ((v >> (s - 1)) + 1) >> 1;
181*593dc095SDavid du Colombier }
182*593dc095SDavid du Colombier 
Max(int32_t a,int32_t b)183*593dc095SDavid du Colombier private inline int32_t Max(int32_t a, int32_t b)
184*593dc095SDavid du Colombier {   return a > b ? a : b;
185*593dc095SDavid du Colombier }
186*593dc095SDavid du Colombier 
Min(int32_t a,int32_t b)187*593dc095SDavid du Colombier private inline int32_t Min(int32_t a, int32_t b)
188*593dc095SDavid du Colombier {   return a < b ? a : b;
189*593dc095SDavid du Colombier }
190*593dc095SDavid du Colombier 
rshift(long a,int b)191*593dc095SDavid du Colombier private inline long rshift(long a, int b)
192*593dc095SDavid du Colombier {   return b > 0 ? a << b : a >> -b;
193*593dc095SDavid du Colombier }
urshift(ulong a,int b)194*593dc095SDavid du Colombier private inline ulong urshift(ulong a, int b)
195*593dc095SDavid du Colombier {   return b > 0 ? a << b : a >> -b;
196*593dc095SDavid du Colombier }
197*593dc095SDavid du Colombier /*---------------------- members of matrix classes -------------------------*/
198*593dc095SDavid du Colombier 
double_matrix__set(double_matrix * this,const gs_matrix_fixed * m)199*593dc095SDavid du Colombier private inline void double_matrix__set(double_matrix * this, const gs_matrix_fixed * m)
200*593dc095SDavid du Colombier {   this->xx = m->xx;
201*593dc095SDavid du Colombier     this->xy = m->xy;
202*593dc095SDavid du Colombier     this->yx = m->yx;
203*593dc095SDavid du Colombier     this->yy = m->yy;
204*593dc095SDavid du Colombier }
205*593dc095SDavid du Colombier 
double_matrix__invert_to(const double_matrix * this,double_matrix * m)206*593dc095SDavid du Colombier private inline int double_matrix__invert_to(const double_matrix * this, double_matrix * m)
207*593dc095SDavid du Colombier {   double det = this->xx * this->yy - this->xy * this->yx;
208*593dc095SDavid du Colombier 
209*593dc095SDavid du Colombier     if (fabs(det) * 1000000 < fabs(this->xx) + fabs(this->xy) + fabs(this->yx) + fabs(this->yy))
210*593dc095SDavid du Colombier 	return_error(gs_error_rangecheck);
211*593dc095SDavid du Colombier     m->xx =  this->yy / det;
212*593dc095SDavid du Colombier     m->xy = -this->xy / det;
213*593dc095SDavid du Colombier     m->yx = -this->yx / det;
214*593dc095SDavid du Colombier     m->yy =  this->xx / det;
215*593dc095SDavid du Colombier     return 0;
216*593dc095SDavid du Colombier }
217*593dc095SDavid du Colombier 
fraction_matrix__drop_bits(fraction_matrix * this,unsigned int bits)218*593dc095SDavid du Colombier private void fraction_matrix__drop_bits(fraction_matrix * this, unsigned int bits)
219*593dc095SDavid du Colombier {   this->xx = shift_rounded(this->xx, bits);
220*593dc095SDavid du Colombier     this->xy = shift_rounded(this->xy, bits);
221*593dc095SDavid du Colombier     this->yx = shift_rounded(this->yx, bits);
222*593dc095SDavid du Colombier     this->yy = shift_rounded(this->yy, bits);
223*593dc095SDavid du Colombier     this->denominator >>= bits;
224*593dc095SDavid du Colombier     this->bitshift -= bits;
225*593dc095SDavid du Colombier }
226*593dc095SDavid du Colombier 
fraction_matrix__set(fraction_matrix * this,const double_matrix * pmat)227*593dc095SDavid du Colombier private void fraction_matrix__set(fraction_matrix * this, const double_matrix * pmat)
228*593dc095SDavid du Colombier {   double axx = fabs(pmat->xx), axy = fabs(pmat->xy);
229*593dc095SDavid du Colombier     double ayx = fabs(pmat->xx), ayy = fabs(pmat->xy);
230*593dc095SDavid du Colombier     double scale = max(axx + axy, ayx + ayy);
231*593dc095SDavid du Colombier     int matrix_exp, m;
232*593dc095SDavid du Colombier     double unused = frexp(scale, &matrix_exp);
233*593dc095SDavid du Colombier 
234*593dc095SDavid du Colombier     this->bitshift = matrix_bits - matrix_exp;
235*593dc095SDavid du Colombier     this->denominator = 1 << this->bitshift;
236*593dc095SDavid du Colombier     /* Round towards zero for a better view of mirrored characters : */
237*593dc095SDavid du Colombier     this->xx = (int32_t)(pmat->xx * this->denominator + 0.5);
238*593dc095SDavid du Colombier     this->xy = (int32_t)(pmat->xy * this->denominator + 0.5);
239*593dc095SDavid du Colombier     this->yx = (int32_t)(pmat->yx * this->denominator + 0.5);
240*593dc095SDavid du Colombier     this->yy = (int32_t)(pmat->yy * this->denominator + 0.5);
241*593dc095SDavid du Colombier     m = Max(Max(any_abs(this->xx), any_abs(this->xy)), Max(any_abs(this->yx), any_abs(this->yy)));
242*593dc095SDavid du Colombier     unused = frexp(m, &matrix_exp);
243*593dc095SDavid du Colombier     if (matrix_exp > matrix_bits)
244*593dc095SDavid du Colombier         fraction_matrix__drop_bits(this, matrix_exp - matrix_bits);
245*593dc095SDavid du Colombier }
246*593dc095SDavid du Colombier 
fraction_matrix__to_double(const fraction_matrix * this,double_matrix * pmat)247*593dc095SDavid du Colombier private inline int fraction_matrix__to_double(const fraction_matrix * this, double_matrix * pmat)
248*593dc095SDavid du Colombier {
249*593dc095SDavid du Colombier     if (this->denominator == 0)
250*593dc095SDavid du Colombier 	return_error(gs_error_rangecheck);
251*593dc095SDavid du Colombier     pmat->xx = (double)this->xx / this->denominator;
252*593dc095SDavid du Colombier     pmat->xy = (double)this->xy / this->denominator;
253*593dc095SDavid du Colombier     pmat->yx = (double)this->yx / this->denominator;
254*593dc095SDavid du Colombier     pmat->yy = (double)this->yy / this->denominator;
255*593dc095SDavid du Colombier     return 0;
256*593dc095SDavid du Colombier }
257*593dc095SDavid du Colombier 
fraction_matrix__invert_to(const fraction_matrix * this,fraction_matrix * pmat)258*593dc095SDavid du Colombier private int fraction_matrix__invert_to(const fraction_matrix * this, fraction_matrix * pmat)
259*593dc095SDavid du Colombier {   double_matrix m, M;
260*593dc095SDavid du Colombier     int code;
261*593dc095SDavid du Colombier 
262*593dc095SDavid du Colombier     code = fraction_matrix__to_double(this, &M);
263*593dc095SDavid du Colombier     if (code < 0)
264*593dc095SDavid du Colombier 	return code;
265*593dc095SDavid du Colombier     code = double_matrix__invert_to(&M, &m);
266*593dc095SDavid du Colombier     if (code < 0)
267*593dc095SDavid du Colombier 	return code;
268*593dc095SDavid du Colombier     fraction_matrix__set(pmat, &m);
269*593dc095SDavid du Colombier     return 0;
270*593dc095SDavid du Colombier }
271*593dc095SDavid du Colombier 
fraction_matrix__transform_x(fraction_matrix * this,int24 x,int24 y,unsigned int s)272*593dc095SDavid du Colombier private inline int32_t fraction_matrix__transform_x(fraction_matrix *this, int24 x, int24 y, unsigned int s)
273*593dc095SDavid du Colombier {   return mul_shift_round(x, this->xx, s) + mul_shift_round(y, this->yx, s);
274*593dc095SDavid du Colombier }
fraction_matrix__transform_y(fraction_matrix * this,int24 x,int24 y,unsigned int s)275*593dc095SDavid du Colombier private inline int32_t fraction_matrix__transform_y(fraction_matrix *this, int24 x, int24 y, unsigned int s)
276*593dc095SDavid du Colombier {   return mul_shift_round(x, this->xy, s) + mul_shift_round(y, this->yy, s);
277*593dc095SDavid du Colombier }
278*593dc095SDavid du Colombier 
279*593dc095SDavid du Colombier 
280*593dc095SDavid du Colombier /*--------------------------- friends ------------------------------*/
281*593dc095SDavid du Colombier 
ranger_step_f(int i,int beg,int end)282*593dc095SDavid du Colombier private inline int ranger_step_f(int i, int beg, int end)
283*593dc095SDavid du Colombier {   return (i == end ? beg : i + 1);
284*593dc095SDavid du Colombier }
285*593dc095SDavid du Colombier 
ranger_step_b(int i,int beg,int end)286*593dc095SDavid du Colombier private inline int ranger_step_b(int i, int beg, int end)
287*593dc095SDavid du Colombier {   return (i == beg ? end : i - 1);
288*593dc095SDavid du Colombier }
289*593dc095SDavid du Colombier 
o2d(const t1_hinter * h,t1_hinter_space_coord v)290*593dc095SDavid du Colombier private inline fixed o2d(const t1_hinter *h, t1_hinter_space_coord v)
291*593dc095SDavid du Colombier {
292*593dc095SDavid du Colombier     int s = h->g2o_fraction_bits - _fixed_shift;
293*593dc095SDavid du Colombier 
294*593dc095SDavid du Colombier     if (s >= 1)
295*593dc095SDavid du Colombier 	return ((v >> (h->g2o_fraction_bits - _fixed_shift - 1)) + 1) >> 1;
296*593dc095SDavid du Colombier     else if (s == 0)
297*593dc095SDavid du Colombier 	return v;
298*593dc095SDavid du Colombier     else
299*593dc095SDavid du Colombier 	return v << -s;
300*593dc095SDavid du Colombier }
301*593dc095SDavid du Colombier 
d2o(const t1_hinter * h,t1_hinter_space_coord v)302*593dc095SDavid du Colombier private inline fixed d2o(const t1_hinter *h, t1_hinter_space_coord v)
303*593dc095SDavid du Colombier {   int s = h->g2o_fraction_bits - _fixed_shift;
304*593dc095SDavid du Colombier 
305*593dc095SDavid du Colombier     if (s >= 0)
306*593dc095SDavid du Colombier 	return v << s;
307*593dc095SDavid du Colombier     else
308*593dc095SDavid du Colombier 	return v >> -s;
309*593dc095SDavid du Colombier }
310*593dc095SDavid du Colombier 
g2o(t1_hinter * h,t1_glyph_space_coord gx,t1_glyph_space_coord gy,t1_hinter_space_coord * ox,t1_hinter_space_coord * oy)311*593dc095SDavid du Colombier private inline void g2o(t1_hinter * h, t1_glyph_space_coord gx, t1_glyph_space_coord gy, t1_hinter_space_coord *ox, t1_hinter_space_coord *oy)
312*593dc095SDavid du Colombier {   *ox = fraction_matrix__transform_x(&h->ctmf, gx, gy, g2o_bitshift);
313*593dc095SDavid du Colombier     *oy = fraction_matrix__transform_y(&h->ctmf, gx, gy, g2o_bitshift);
314*593dc095SDavid du Colombier }
315*593dc095SDavid du Colombier 
g2o_dist(t1_glyph_space_coord gd,int19 coef)316*593dc095SDavid du Colombier private inline t1_hinter_space_coord g2o_dist(t1_glyph_space_coord gd, int19 coef)
317*593dc095SDavid du Colombier {   return mul_shift(gd, coef, g2o_bitshift);
318*593dc095SDavid du Colombier }
319*593dc095SDavid du Colombier 
g2d(t1_hinter * h,t1_glyph_space_coord gx,t1_glyph_space_coord gy,fixed * dx,fixed * dy)320*593dc095SDavid du Colombier private inline void g2d(t1_hinter * h, t1_glyph_space_coord gx, t1_glyph_space_coord gy, fixed *dx, fixed *dy)
321*593dc095SDavid du Colombier {   *dx = fraction_matrix__transform_x(&h->ctmf, gx, gy, g2o_bitshift);
322*593dc095SDavid du Colombier     *dy = fraction_matrix__transform_y(&h->ctmf, gx, gy, g2o_bitshift);
323*593dc095SDavid du Colombier     *dx = o2d(h, *dx);
324*593dc095SDavid du Colombier     *dy = o2d(h, *dy);
325*593dc095SDavid du Colombier     *dx += h->orig_dx;
326*593dc095SDavid du Colombier     *dy += h->orig_dy;
327*593dc095SDavid du Colombier }
328*593dc095SDavid du Colombier 
o2g(t1_hinter * h,t1_hinter_space_coord ox,t1_hinter_space_coord oy,t1_glyph_space_coord * gx,t1_glyph_space_coord * gy)329*593dc095SDavid du Colombier private inline void o2g(t1_hinter * h, t1_hinter_space_coord ox, t1_hinter_space_coord oy, t1_glyph_space_coord *gx, t1_glyph_space_coord *gy)
330*593dc095SDavid du Colombier {   *gx = fraction_matrix__transform_x(&h->ctmi, ox, oy, split_bits);
331*593dc095SDavid du Colombier     *gy = fraction_matrix__transform_y(&h->ctmi, ox, oy, split_bits);
332*593dc095SDavid du Colombier     *gx = shift_rounded(*gx, h->g2o_fraction_bits + h->ctmi.bitshift - _fixed_shift - split_bits);
333*593dc095SDavid du Colombier     *gy = shift_rounded(*gy, h->g2o_fraction_bits + h->ctmi.bitshift - _fixed_shift - split_bits);
334*593dc095SDavid du Colombier }
335*593dc095SDavid du Colombier 
o2g_dist(t1_hinter * h,t1_hinter_space_coord od,int19 coef)336*593dc095SDavid du Colombier private inline t1_glyph_space_coord o2g_dist(t1_hinter * h, t1_hinter_space_coord od, int19 coef)
337*593dc095SDavid du Colombier {   return shift_rounded(mul_shift(od, coef, split_bits), h->g2o_fraction_bits + h->ctmi.bitshift - _fixed_shift - split_bits);
338*593dc095SDavid du Colombier }
339*593dc095SDavid du Colombier 
o2g_float(t1_hinter * h,t1_hinter_space_coord ox,t1_hinter_space_coord oy,t1_glyph_space_coord * gx,t1_glyph_space_coord * gy)340*593dc095SDavid du Colombier private inline void o2g_float(t1_hinter * h, t1_hinter_space_coord ox, t1_hinter_space_coord oy, t1_glyph_space_coord *gx, t1_glyph_space_coord *gy)
341*593dc095SDavid du Colombier {   *gx = (long)(((double)ox * h->ctmi.xx + (double)oy * h->ctmi.yx) * fixed_scale / h->g2o_fraction / h->ctmi.denominator);
342*593dc095SDavid du Colombier     *gy = (long)(((double)ox * h->ctmi.xy + (double)oy * h->ctmi.yy) * fixed_scale / h->g2o_fraction / h->ctmi.denominator);
343*593dc095SDavid du Colombier }
344*593dc095SDavid du Colombier 
345*593dc095SDavid du Colombier /* --------------------- t1_hint class members ---------------------*/
346*593dc095SDavid du Colombier 
t1_hint__set_aligned_coord(t1_hint * this,t1_glyph_space_coord gc,t1_pole * pole,enum t1_align_type align,int quality)347*593dc095SDavid du Colombier private void t1_hint__set_aligned_coord(t1_hint * this, t1_glyph_space_coord gc, t1_pole * pole, enum t1_align_type align, int quality)
348*593dc095SDavid du Colombier {   t1_glyph_space_coord g = (this->type == hstem ? pole->gy : pole->gx);
349*593dc095SDavid du Colombier 
350*593dc095SDavid du Colombier #if FINE_STEM_COMPLEXES
351*593dc095SDavid du Colombier     if (any_abs(this->g0 - g) < any_abs(this->g1 - g)) {
352*593dc095SDavid du Colombier         if (this->aligned0 <= align && this->q0 > quality)
353*593dc095SDavid du Colombier             this->ag0 = gc, this->aligned0 = align, this->q0 = quality;
354*593dc095SDavid du Colombier     } else {
355*593dc095SDavid du Colombier         if (this->aligned1 <= align && this->q1 > quality)
356*593dc095SDavid du Colombier             this->ag1 = gc, this->aligned1 = align, this->q1 = quality;
357*593dc095SDavid du Colombier     }
358*593dc095SDavid du Colombier #else
359*593dc095SDavid du Colombier     if (any_abs(this->g0 - g) < any_abs(this->g1 - g)) {
360*593dc095SDavid du Colombier         if (this->aligned0 < align && this->q0 > quality)
361*593dc095SDavid du Colombier             this->ag0 = gc, this->aligned0 = align, this->q0 = quality;
362*593dc095SDavid du Colombier     } else {
363*593dc095SDavid du Colombier         if (this->aligned1 < align && this->q1 > quality)
364*593dc095SDavid du Colombier             this->ag1 = gc, this->aligned1 = align, this->q1 = quality;
365*593dc095SDavid du Colombier     }
366*593dc095SDavid du Colombier #endif
367*593dc095SDavid du Colombier }
368*593dc095SDavid du Colombier 
369*593dc095SDavid du Colombier /* --------------------- t1_hinter class members - debug graphics --------------------*/
370*593dc095SDavid du Colombier 
t1_hinter__paint_glyph(t1_hinter * this,bool aligned)371*593dc095SDavid du Colombier private void t1_hinter__paint_glyph(t1_hinter * this, bool aligned)
372*593dc095SDavid du Colombier {
373*593dc095SDavid du Colombier #ifdef VD_TRACE
374*593dc095SDavid du Colombier #define X(j) *member_prt(t1_glyph_space_coord, &this->pole[j], offset_x)
375*593dc095SDavid du Colombier #define Y(j) *member_prt(t1_glyph_space_coord, &this->pole[j], offset_y)
376*593dc095SDavid du Colombier     t1_glyph_space_coord *p_x = (aligned ? &this->pole[0].ax : &this->pole[0].gx);
377*593dc095SDavid du Colombier     t1_glyph_space_coord *p_y = (aligned ? &this->pole[0].ay : &this->pole[0].gy);
378*593dc095SDavid du Colombier     int offset_x = (char *)p_x - (char *)&this->pole[0];
379*593dc095SDavid du Colombier     int offset_y = (char *)p_y - (char *)&this->pole[0];
380*593dc095SDavid du Colombier     int i, j;
381*593dc095SDavid du Colombier     char buf[15];
382*593dc095SDavid du Colombier 
383*593dc095SDavid du Colombier     if (!vd_enabled)
384*593dc095SDavid du Colombier 	return;
385*593dc095SDavid du Colombier #   if VD_PAINT_POLE_IDS
386*593dc095SDavid du Colombier     for(i = 0; i < this->contour_count; i++) {
387*593dc095SDavid du Colombier         int beg_pole = this->contour[i];
388*593dc095SDavid du Colombier         int end_pole = this->contour[i + 1] - 2;
389*593dc095SDavid du Colombier 
390*593dc095SDavid du Colombier         for(j = beg_pole; j <= end_pole; j++) {
391*593dc095SDavid du Colombier             vd_circle(X(j), Y(j), 3, RGB(0,0,255));
392*593dc095SDavid du Colombier             sprintf(buf, "%d", j);
393*593dc095SDavid du Colombier             vd_text(this->pole[j].gx, this->pole[j].gy, buf, RGB(0,0,0));
394*593dc095SDavid du Colombier             if (this->pole[j + 1].type == offcurve)
395*593dc095SDavid du Colombier                 j+=2;
396*593dc095SDavid du Colombier         }
397*593dc095SDavid du Colombier     }
398*593dc095SDavid du Colombier #   endif
399*593dc095SDavid du Colombier     vd_setcolor(aligned ? RGB(0,255,0) : RGB(0,0,255));
400*593dc095SDavid du Colombier     for(i = 0; i < this->contour_count; i++) {
401*593dc095SDavid du Colombier         int beg_pole = this->contour[i];
402*593dc095SDavid du Colombier         int end_pole = this->contour[i + 1] - 2;
403*593dc095SDavid du Colombier 
404*593dc095SDavid du Colombier         vd_moveto(X(beg_pole), Y(beg_pole));
405*593dc095SDavid du Colombier         for(j = beg_pole + 1; j <= end_pole; j++) {
406*593dc095SDavid du Colombier             if (this->pole[j].type == oncurve) {
407*593dc095SDavid du Colombier                 vd_lineto(X(j), Y(j));
408*593dc095SDavid du Colombier             } else {
409*593dc095SDavid du Colombier                 int jj = (j + 2 > end_pole ? beg_pole : j + 2);
410*593dc095SDavid du Colombier                 vd_curveto(X(j), Y(j), X(j + 1), Y(j + 1), X(jj), Y(jj));
411*593dc095SDavid du Colombier                 j+=2;
412*593dc095SDavid du Colombier             }
413*593dc095SDavid du Colombier         }
414*593dc095SDavid du Colombier         vd_lineto(X(beg_pole), Y(beg_pole));
415*593dc095SDavid du Colombier     }
416*593dc095SDavid du Colombier #undef X
417*593dc095SDavid du Colombier #undef Y
418*593dc095SDavid du Colombier #endif
419*593dc095SDavid du Colombier }
420*593dc095SDavid du Colombier 
t1_hinter__paint_raster_grid(t1_hinter * this)421*593dc095SDavid du Colombier private void  t1_hinter__paint_raster_grid(t1_hinter * this)
422*593dc095SDavid du Colombier {
423*593dc095SDavid du Colombier #ifdef VD_TRACE
424*593dc095SDavid du Colombier     int i;
425*593dc095SDavid du Colombier     double j; /* 'long' can overflow */
426*593dc095SDavid du Colombier     unsigned long c0 = RGB(192, 192, 192), c1 = RGB(64, 64, 64);
427*593dc095SDavid du Colombier     t1_hinter_space_coord min_ox, max_ox, min_oy, max_oy;
428*593dc095SDavid du Colombier     long div_x = this->g2o_fraction, div_xx = div_x << this->log2_pixels_x;
429*593dc095SDavid du Colombier     long div_y = this->g2o_fraction, div_yy = div_y << this->log2_pixels_y;
430*593dc095SDavid du Colombier     long ext_x = div_x * 5;
431*593dc095SDavid du Colombier     long ext_y = div_y * 5;
432*593dc095SDavid du Colombier     long sx = this->orig_ox % div_xx;
433*593dc095SDavid du Colombier     long sy = this->orig_oy % div_yy;
434*593dc095SDavid du Colombier 
435*593dc095SDavid du Colombier     if (!vd_enabled)
436*593dc095SDavid du Colombier 	return;
437*593dc095SDavid du Colombier     g2o(this, this->pole[0].gx, this->pole[0].gy, &min_ox, &min_oy);
438*593dc095SDavid du Colombier     max_ox = min_ox, max_oy = min_oy;
439*593dc095SDavid du Colombier     /* Compute BBox in outliner's space : */
440*593dc095SDavid du Colombier     for (i = 1; i < this->pole_count - 1; i++) {
441*593dc095SDavid du Colombier         t1_hinter_space_coord ox, oy;
442*593dc095SDavid du Colombier 
443*593dc095SDavid du Colombier         g2o(this, this->pole[i].gx, this->pole[i].gy, &ox, &oy);
444*593dc095SDavid du Colombier         min_ox = min(min_ox, ox);
445*593dc095SDavid du Colombier         min_oy = min(min_oy, oy);
446*593dc095SDavid du Colombier         max_ox = max(max_ox, ox);
447*593dc095SDavid du Colombier         max_oy = max(max_oy, oy);
448*593dc095SDavid du Colombier     }
449*593dc095SDavid du Colombier     min_ox -= ext_x;
450*593dc095SDavid du Colombier     min_oy -= ext_y;
451*593dc095SDavid du Colombier     max_ox += ext_x;
452*593dc095SDavid du Colombier     max_oy += ext_y;
453*593dc095SDavid du Colombier     /* Paint columns : */
454*593dc095SDavid du Colombier     for (j = min_ox / div_x * div_x; j < (double)max_ox + div_x; j += div_x) {
455*593dc095SDavid du Colombier         t1_glyph_space_coord gx0, gy0, gx1, gy1;
456*593dc095SDavid du Colombier 	bool pix = ((int)j / div_xx * div_xx == (int)j);
457*593dc095SDavid du Colombier 
458*593dc095SDavid du Colombier         o2g_float(this, (int)j - sx, min_oy - sy, &gx0, &gy0); /* o2g may overflow here due to ext. */
459*593dc095SDavid du Colombier         o2g_float(this, (int)j - sx, max_oy - sy, &gx1, &gy1);
460*593dc095SDavid du Colombier         vd_bar(gx0, gy0, gx1, gy1, 1, (!j ? 0 : pix ? c1 : c0));
461*593dc095SDavid du Colombier     }
462*593dc095SDavid du Colombier     /* Paint rows : */
463*593dc095SDavid du Colombier     for (j = min_oy / div_y * div_y; j < max_oy + div_y; j += div_y) {
464*593dc095SDavid du Colombier         t1_glyph_space_coord gx0, gy0, gx1, gy1;
465*593dc095SDavid du Colombier 	bool pix = ((int)j / div_yy * div_yy == (int)j);
466*593dc095SDavid du Colombier 
467*593dc095SDavid du Colombier         o2g_float(this, min_ox - sx, (int)j - sy, &gx0, &gy0);
468*593dc095SDavid du Colombier         o2g_float(this, max_ox - sx, (int)j - sy, &gx1, &gy1);
469*593dc095SDavid du Colombier         vd_bar(gx0, gy0, gx1, gy1, 1, (!j ? 0 : pix ? c1 : c0));
470*593dc095SDavid du Colombier     }
471*593dc095SDavid du Colombier #endif
472*593dc095SDavid du Colombier }
473*593dc095SDavid du Colombier 
474*593dc095SDavid du Colombier /* --------------------- t1_hinter class members - import --------------------*/
475*593dc095SDavid du Colombier 
t1_hinter__init(t1_hinter * this,gx_path * output_path)476*593dc095SDavid du Colombier void t1_hinter__init(t1_hinter * this, gx_path *output_path)
477*593dc095SDavid du Colombier {   this->max_import_coord = (1 << max_coord_bits);
478*593dc095SDavid du Colombier     this->stem_snap_count[0] = this->stem_snap_count[1] = 0;
479*593dc095SDavid du Colombier     this->zone_count = 0;
480*593dc095SDavid du Colombier     this->pole_count = 0;
481*593dc095SDavid du Colombier     this->hint_count = 0;
482*593dc095SDavid du Colombier     this->contour_count = 0;
483*593dc095SDavid du Colombier     this->hint_range_count = 0;
484*593dc095SDavid du Colombier     this->flex_count = 0;
485*593dc095SDavid du Colombier 
486*593dc095SDavid du Colombier     this->max_contour_count = count_of(this->contour0);
487*593dc095SDavid du Colombier     this->max_zone_count = count_of(this->zone0);
488*593dc095SDavid du Colombier     this->max_pole_count = count_of(this->pole0);
489*593dc095SDavid du Colombier     this->max_hint_count = count_of(this->hint0);
490*593dc095SDavid du Colombier     this->max_hint_range_count = count_of(this->hint_range0);
491*593dc095SDavid du Colombier     this->max_stem_snap_count[0] = count_of(this->stem_snap[0]);
492*593dc095SDavid du Colombier     this->max_stem_snap_count[1] = count_of(this->stem_snap[1]);
493*593dc095SDavid du Colombier 
494*593dc095SDavid du Colombier     this->pole = this->pole0;
495*593dc095SDavid du Colombier     this->hint = this->hint0;
496*593dc095SDavid du Colombier     this->zone = this->zone0;
497*593dc095SDavid du Colombier     this->contour = this->contour0;
498*593dc095SDavid du Colombier     this->hint_range = this->hint_range0;
499*593dc095SDavid du Colombier     this->stem_snap[0] = this->stem_snap0[0];
500*593dc095SDavid du Colombier     this->stem_snap[1] = this->stem_snap0[1];
501*593dc095SDavid du Colombier 
502*593dc095SDavid du Colombier     this->FontType = 1;
503*593dc095SDavid du Colombier     this->ForceBold = false;
504*593dc095SDavid du Colombier     this->base_font_scale = 0;
505*593dc095SDavid du Colombier     this->resolution = 0;
506*593dc095SDavid du Colombier     this->heigt_transform_coef = this->width_transform_coef = 0;
507*593dc095SDavid du Colombier     this->heigt_transform_coef_rat = this->width_transform_coef_rat = 0;
508*593dc095SDavid du Colombier     this->heigt_transform_coef_inv = this->width_transform_coef_inv = 0;
509*593dc095SDavid du Colombier     this->cx = this->cy = 0;
510*593dc095SDavid du Colombier     this->contour[0] = 0;
511*593dc095SDavid du Colombier     this->seac_flag = 0;
512*593dc095SDavid du Colombier     this->keep_stem_width = false;
513*593dc095SDavid du Colombier     this->charpath_flag = false;
514*593dc095SDavid du Colombier     this->grid_fit_x = this->grid_fit_y = true;
515*593dc095SDavid du Colombier     this->output_path = output_path;
516*593dc095SDavid du Colombier     this->memory = (output_path == 0 ? 0 : output_path->memory);
517*593dc095SDavid du Colombier     this->disable_hinting = (this->memory == NULL);
518*593dc095SDavid du Colombier     this->autohinting = false;
519*593dc095SDavid du Colombier 
520*593dc095SDavid du Colombier     this->stem_snap[0][0] = this->stem_snap[1][0] = 100; /* default */
521*593dc095SDavid du Colombier }
522*593dc095SDavid du Colombier 
t1_hinter__free_arrays(t1_hinter * this)523*593dc095SDavid du Colombier private inline void t1_hinter__free_arrays(t1_hinter * this)
524*593dc095SDavid du Colombier {   if (this->pole != this->pole0)
525*593dc095SDavid du Colombier 	gs_free_object(this->memory, this->pole, s_pole_array);
526*593dc095SDavid du Colombier     if (this->hint != this->hint0)
527*593dc095SDavid du Colombier 	gs_free_object(this->memory, this->hint, s_hint_array);
528*593dc095SDavid du Colombier     if (this->zone != this->zone0)
529*593dc095SDavid du Colombier 	gs_free_object(this->memory, this->zone, s_zone_array);
530*593dc095SDavid du Colombier     if (this->contour != this->contour0)
531*593dc095SDavid du Colombier 	gs_free_object(this->memory, this->contour, s_contour_array);
532*593dc095SDavid du Colombier     if (this->hint_range != this->hint_range0)
533*593dc095SDavid du Colombier 	gs_free_object(this->memory, this->hint_range, s_hint_range_array);
534*593dc095SDavid du Colombier     if (this->stem_snap[0] != this->stem_snap0[0])
535*593dc095SDavid du Colombier 	gs_free_object(this->memory, this->stem_snap[0], s_stem_snap_array);
536*593dc095SDavid du Colombier     if (this->stem_snap[1] != this->stem_snap0[1])
537*593dc095SDavid du Colombier 	gs_free_object(this->memory, this->stem_snap[1], s_stem_snap_array);
538*593dc095SDavid du Colombier     this->pole = 0;
539*593dc095SDavid du Colombier     this->hint = 0;
540*593dc095SDavid du Colombier     this->zone = 0;
541*593dc095SDavid du Colombier     this->contour = 0;
542*593dc095SDavid du Colombier     this->hint_range = 0;
543*593dc095SDavid du Colombier     this->stem_snap[0] = this->stem_snap[1] = 0;
544*593dc095SDavid du Colombier }
545*593dc095SDavid du Colombier 
t1_hinter__init_outline(t1_hinter * this)546*593dc095SDavid du Colombier private inline void t1_hinter__init_outline(t1_hinter * this)
547*593dc095SDavid du Colombier {   this->contour_count = 0;
548*593dc095SDavid du Colombier     this->pole_count = 0;
549*593dc095SDavid du Colombier     this->contour[0] = 0;
550*593dc095SDavid du Colombier     this->seac_flag = 0;
551*593dc095SDavid du Colombier     this->hint_count = 0;
552*593dc095SDavid du Colombier     this->primary_hint_count = -1;
553*593dc095SDavid du Colombier     this->suppress_overshoots = false;
554*593dc095SDavid du Colombier     this->path_opened = false;
555*593dc095SDavid du Colombier }
556*593dc095SDavid du Colombier 
t1_hinter__compute_rat_transform_coef(t1_hinter * this)557*593dc095SDavid du Colombier private void t1_hinter__compute_rat_transform_coef(t1_hinter * this)
558*593dc095SDavid du Colombier {
559*593dc095SDavid du Colombier     /* Round towards zero for a better view of mirrored characters : */
560*593dc095SDavid du Colombier     this->heigt_transform_coef_rat = (int19)(this->heigt_transform_coef * this->ctmf.denominator + 0.5);
561*593dc095SDavid du Colombier     this->width_transform_coef_rat = (int19)(this->width_transform_coef * this->ctmf.denominator + 0.5);
562*593dc095SDavid du Colombier     this->heigt_transform_coef_inv = (int19)(this->ctmi.denominator / this->heigt_transform_coef + 0.5);
563*593dc095SDavid du Colombier     this->width_transform_coef_inv = (int19)(this->ctmi.denominator / this->width_transform_coef + 0.5);
564*593dc095SDavid du Colombier }
565*593dc095SDavid du Colombier 
t1_hinter__adjust_matrix_precision(t1_hinter * this,fixed xx,fixed yy)566*593dc095SDavid du Colombier private inline void t1_hinter__adjust_matrix_precision(t1_hinter * this, fixed xx, fixed yy)
567*593dc095SDavid du Colombier {   fixed x = any_abs(xx), y = any_abs(yy);
568*593dc095SDavid du Colombier     fixed c = (x > y ? x : y);
569*593dc095SDavid du Colombier 
570*593dc095SDavid du Colombier     while (c >= this->max_import_coord) {
571*593dc095SDavid du Colombier 	/* Reduce the precision of ctmf to allow products to fit into 32 bits : */
572*593dc095SDavid du Colombier 	this->max_import_coord <<= 1;
573*593dc095SDavid du Colombier 	fraction_matrix__drop_bits(&this->ctmf, 1);
574*593dc095SDavid du Colombier 	fraction_matrix__drop_bits(&this->ctmi, 1);
575*593dc095SDavid du Colombier 	this->g2o_fraction_bits -= 1;
576*593dc095SDavid du Colombier 	this->g2o_fraction >>= 1;
577*593dc095SDavid du Colombier 	t1_hinter__compute_rat_transform_coef(this);
578*593dc095SDavid du Colombier     }
579*593dc095SDavid du Colombier     if (this->ctmf.denominator == 0) {
580*593dc095SDavid du Colombier 	/* ctmf should be degenerate. */
581*593dc095SDavid du Colombier 	this->ctmf.denominator = 1;
582*593dc095SDavid du Colombier     }
583*593dc095SDavid du Colombier }
584*593dc095SDavid du Colombier 
t1_hinter__set_origin(t1_hinter * this,fixed dx,fixed dy)585*593dc095SDavid du Colombier private inline void t1_hinter__set_origin(t1_hinter * this, fixed dx, fixed dy)
586*593dc095SDavid du Colombier {
587*593dc095SDavid du Colombier     fixed align_x = rshift(fixed_1, (this->align_to_pixels ? (int)this->log2_pixels_x : this->log2_subpixels_x));
588*593dc095SDavid du Colombier     fixed align_y = rshift(fixed_1, (this->align_to_pixels ? (int)this->log2_pixels_y : this->log2_subpixels_y));
589*593dc095SDavid du Colombier 
590*593dc095SDavid du Colombier     this->orig_dx = (dx + align_x / 2) & ~(align_x - 1);
591*593dc095SDavid du Colombier     this->orig_dy = (dy + align_y / 2) & ~(align_y - 1);
592*593dc095SDavid du Colombier     t1_hinter__adjust_matrix_precision(this, this->orig_dx, this->orig_dy);
593*593dc095SDavid du Colombier     this->orig_ox = d2o(this, this->orig_dx);
594*593dc095SDavid du Colombier     this->orig_oy = d2o(this, this->orig_dy);
595*593dc095SDavid du Colombier #   if ADOBE_SHIFT_CHARPATH
596*593dc095SDavid du Colombier         /*  Adobe CPSI rounds coordinates for 'charpath' :
597*593dc095SDavid du Colombier             X to trunc(x+0.5)
598*593dc095SDavid du Colombier             Y to trunc(y)+0.5
599*593dc095SDavid du Colombier         */
600*593dc095SDavid du Colombier         if (this->charpath_flag) {
601*593dc095SDavid du Colombier             this->orig_dx += fixed_half;
602*593dc095SDavid du Colombier             this->orig_dx &= ~(fixed_1 - 1);
603*593dc095SDavid du Colombier             this->orig_dy &= ~(fixed_1 - 1);
604*593dc095SDavid du Colombier             this->orig_dy += fixed_half;
605*593dc095SDavid du Colombier         } else {
606*593dc095SDavid du Colombier             this->orig_dy += fixed_1;
607*593dc095SDavid du Colombier 	    /* Adobe CPSI does this, not sure why. */
608*593dc095SDavid du Colombier             /* fixme : check bbox of cached bitmap. */
609*593dc095SDavid du Colombier         }
610*593dc095SDavid du Colombier #   endif
611*593dc095SDavid du Colombier }
612*593dc095SDavid du Colombier 
t1_hinter__set_mapping(t1_hinter * this,gs_matrix_fixed * ctm,gs_matrix * FontMatrix,gs_matrix * baseFontMatrix,int log2_pixels_x,int log2_pixels_y,int log2_subpixels_x,int log2_subpixels_y,fixed origin_x,fixed origin_y,bool align_to_pixels)613*593dc095SDavid du Colombier int t1_hinter__set_mapping(t1_hinter * this, gs_matrix_fixed * ctm,
614*593dc095SDavid du Colombier 		    gs_matrix * FontMatrix, gs_matrix * baseFontMatrix,
615*593dc095SDavid du Colombier 		    int log2_pixels_x, int log2_pixels_y,
616*593dc095SDavid du Colombier 		    int log2_subpixels_x, int log2_subpixels_y,
617*593dc095SDavid du Colombier 		    fixed origin_x, fixed origin_y, bool align_to_pixels)
618*593dc095SDavid du Colombier {   float axx = fabs(ctm->xx), axy = fabs(ctm->xy);
619*593dc095SDavid du Colombier     float ayx = fabs(ctm->xx), ayy = fabs(ctm->xy);
620*593dc095SDavid du Colombier     float scale = max(axx + axy, ayx + ayy);
621*593dc095SDavid du Colombier     double_matrix CTM;
622*593dc095SDavid du Colombier     int code;
623*593dc095SDavid du Colombier 
624*593dc095SDavid du Colombier     this->disable_hinting |= (scale < 1/1024. || scale > 4);
625*593dc095SDavid du Colombier     this->log2_pixels_x = log2_pixels_x;
626*593dc095SDavid du Colombier     this->log2_pixels_y = log2_pixels_y;
627*593dc095SDavid du Colombier     this->log2_subpixels_x = log2_subpixels_x;
628*593dc095SDavid du Colombier     this->log2_subpixels_y = log2_subpixels_y;
629*593dc095SDavid du Colombier     double_matrix__set(&CTM, ctm);
630*593dc095SDavid du Colombier     fraction_matrix__set(&this->ctmf, &CTM);
631*593dc095SDavid du Colombier     this->g2o_fraction_bits = this->ctmf.bitshift - g2o_bitshift + _fixed_shift;
632*593dc095SDavid du Colombier     if (this->g2o_fraction_bits > max_coord_bits) {
633*593dc095SDavid du Colombier         fraction_matrix__drop_bits(&this->ctmf, this->g2o_fraction_bits - max_coord_bits);
634*593dc095SDavid du Colombier         this->g2o_fraction_bits = max_coord_bits;
635*593dc095SDavid du Colombier     }
636*593dc095SDavid du Colombier     if (this->ctmf.denominator != 0) {
637*593dc095SDavid du Colombier 	code = fraction_matrix__invert_to(&this->ctmf, &this->ctmi); /* Note: ctmi is inversion of ctmf, not ctm. */
638*593dc095SDavid du Colombier 	if (code < 0)
639*593dc095SDavid du Colombier 	    return code;
640*593dc095SDavid du Colombier 	this->g2o_fraction = 1 << this->g2o_fraction_bits;
641*593dc095SDavid du Colombier 	/* Note : possibly we'll adjust the matrix precision dynamically
642*593dc095SDavid du Colombier 	   with adjust_matrix_precision while importing the glyph. */
643*593dc095SDavid du Colombier 	if (this->g2o_fraction == 0)
644*593dc095SDavid du Colombier     	    return_error(gs_error_limitcheck);
645*593dc095SDavid du Colombier     }
646*593dc095SDavid du Colombier     if (this->ctmf.denominator == 0 || this->ctmi.denominator == 0) {
647*593dc095SDavid du Colombier 	/* ctmf should be degenerate. */
648*593dc095SDavid du Colombier     	this->disable_hinting = true;
649*593dc095SDavid du Colombier 	this->ctmf.denominator = 1;
650*593dc095SDavid du Colombier     }
651*593dc095SDavid du Colombier     {   /* height_transform_coef is scaling factor for the
652*593dc095SDavid du Colombier            distance between horizontal lines while transformation.
653*593dc095SDavid du Colombier            width_transform_coef defines similarly.
654*593dc095SDavid du Colombier         */
655*593dc095SDavid du Colombier         double_matrix m;
656*593dc095SDavid du Colombier         double vp, sp, div_x, div_y;
657*593dc095SDavid du Colombier 
658*593dc095SDavid du Colombier         code = fraction_matrix__to_double(&this->ctmf, &m);
659*593dc095SDavid du Colombier 	if (code < 0)
660*593dc095SDavid du Colombier 	    return code;
661*593dc095SDavid du Colombier         vp = any_abs(m.xx * m.yy - m.yx * m.yx);
662*593dc095SDavid du Colombier         sp = any_abs(m.xx * m.yx + m.xy * m.yy);
663*593dc095SDavid du Colombier         div_x = hypot(m.xx, m.xy);
664*593dc095SDavid du Colombier         div_y = hypot(m.yx, m.yy);
665*593dc095SDavid du Colombier         if (vp != 0 && div_x != 0 && div_y != 0) {
666*593dc095SDavid du Colombier             this->heigt_transform_coef = vp / div_x;
667*593dc095SDavid du Colombier             this->width_transform_coef = vp / div_y;
668*593dc095SDavid du Colombier 	    t1_hinter__compute_rat_transform_coef(this);
669*593dc095SDavid du Colombier             this->keep_stem_width = (sp <= vp / 3); /* small skew */
670*593dc095SDavid du Colombier         }
671*593dc095SDavid du Colombier     }
672*593dc095SDavid du Colombier     {   /* Compute font size and resolution : */
673*593dc095SDavid du Colombier         gs_point p0, p1, p2;
674*593dc095SDavid du Colombier         double d0, d1, d2;
675*593dc095SDavid du Colombier 
676*593dc095SDavid du Colombier         gs_distance_transform(0, 1, baseFontMatrix, &p0);
677*593dc095SDavid du Colombier         gs_distance_transform(0, 1, FontMatrix, &p1);
678*593dc095SDavid du Colombier         gs_distance_transform(0, 1, (gs_matrix *)ctm, &p2);
679*593dc095SDavid du Colombier         d0 = hypot(p0.x, p0.y);
680*593dc095SDavid du Colombier         d1 = hypot(p1.x, p1.y);
681*593dc095SDavid du Colombier         d2 = hypot(p2.x, p2.y);
682*593dc095SDavid du Colombier         this->base_font_scale = d0;
683*593dc095SDavid du Colombier         this->font_size =  floor(d1 / d0 * 10000 + 0.5) / 10000;
684*593dc095SDavid du Colombier         this->resolution = floor(d2 / d1 * 10000000 + 0.5) / 10000000;
685*593dc095SDavid du Colombier 	/*
686*593dc095SDavid du Colombier 	 * fixme: base_font_scale, font_size and resolution are computed wrongly
687*593dc095SDavid du Colombier 	 * for any of the following cases :
688*593dc095SDavid du Colombier 	 *
689*593dc095SDavid du Colombier 	 * 1. CIDFontType0C with FontMatrix=[0.001 0 0 0.001 0 0] gives 1/1000 size.
690*593dc095SDavid du Colombier 	 * A known example : CIDembedded.pdf . We could obtain the Type 9 FontMatrix
691*593dc095SDavid du Colombier 	 * in type1_exec_init from penum->fstack.
692*593dc095SDavid du Colombier 	 *
693*593dc095SDavid du Colombier 	 * 2. See comment in pdf_font_orig_matrix.
694*593dc095SDavid du Colombier 	 *
695*593dc095SDavid du Colombier 	 * Currently we don't use these values with a regular build.
696*593dc095SDavid du Colombier 	 * The ADOBE_OVERSHOOT_COMPATIBILIY build needs to fix them.
697*593dc095SDavid du Colombier 	 */
698*593dc095SDavid du Colombier     }
699*593dc095SDavid du Colombier     if (1 || /* Doesn't work - see comment above. Using this->disable_hinting instead. */
700*593dc095SDavid du Colombier 	    this->resolution * this->font_size >= 2) {
701*593dc095SDavid du Colombier 	/* Enable the grid fitting separately for axes : */
702*593dc095SDavid du Colombier 	this->grid_fit_y = (any_abs(this->ctmf.xy) * 10 < any_abs(this->ctmf.xx) ||
703*593dc095SDavid du Colombier 			    any_abs(this->ctmf.xx) * 10 < any_abs(this->ctmf.xy));
704*593dc095SDavid du Colombier 	this->grid_fit_x = (any_abs(this->ctmf.yx) * 10 < any_abs(this->ctmf.yy) ||
705*593dc095SDavid du Colombier 			    any_abs(this->ctmf.yy) * 10 < any_abs(this->ctmf.yx));
706*593dc095SDavid du Colombier     } else {
707*593dc095SDavid du Colombier 	/* Disable the grid fitting for very small fonts. */
708*593dc095SDavid du Colombier 	this->grid_fit_x = this->grid_fit_y = false;
709*593dc095SDavid du Colombier     }
710*593dc095SDavid du Colombier     this->transposed = (any_abs(this->ctmf.xy) * 10 > any_abs(this->ctmf.xx));
711*593dc095SDavid du Colombier     this->align_to_pixels = align_to_pixels;
712*593dc095SDavid du Colombier     t1_hinter__set_origin(this, origin_x, origin_y);
713*593dc095SDavid du Colombier     return 0;
714*593dc095SDavid du Colombier }
715*593dc095SDavid du Colombier 
t1_hinter__make_zone(t1_hinter * this,t1_zone * zone,float * blues,enum t1_zone_type type,t1_glyph_space_coord blue_fuzz)716*593dc095SDavid du Colombier private void t1_hinter__make_zone(t1_hinter * this, t1_zone *zone, float * blues, enum t1_zone_type type, t1_glyph_space_coord blue_fuzz)
717*593dc095SDavid du Colombier {   t1_glyph_space_coord d = 0;
718*593dc095SDavid du Colombier 
719*593dc095SDavid du Colombier     zone->type = type;
720*593dc095SDavid du Colombier     zone->y           = float2fixed(blues[0] + d);
721*593dc095SDavid du Colombier     zone->overshoot_y = float2fixed(blues[1] + d);
722*593dc095SDavid du Colombier     zone->y_min = min(zone->y, zone->overshoot_y) - blue_fuzz;
723*593dc095SDavid du Colombier     zone->y_max = max(zone->y, zone->overshoot_y) + blue_fuzz;
724*593dc095SDavid du Colombier     if (type == botzone ? zone->overshoot_y > zone->y : zone->overshoot_y < zone->y) {
725*593dc095SDavid du Colombier         int v = zone->overshoot_y; zone->overshoot_y = zone->y; zone->y = v;
726*593dc095SDavid du Colombier     }
727*593dc095SDavid du Colombier     t1_hinter__adjust_matrix_precision(this, zone->y_min, zone->y_max);
728*593dc095SDavid du Colombier }
729*593dc095SDavid du Colombier 
t1_hinter__realloc_array(gs_memory_t * mem,void ** a,void * a0,int * max_count,int elem_size,int enhancement,const char * cname)730*593dc095SDavid du Colombier private bool t1_hinter__realloc_array(gs_memory_t *mem, void **a, void *a0, int *max_count, int elem_size, int enhancement, const char *cname)
731*593dc095SDavid du Colombier {
732*593dc095SDavid du Colombier     void *aa = gs_alloc_bytes(mem, (*max_count + enhancement * 2) * elem_size, cname);
733*593dc095SDavid du Colombier 
734*593dc095SDavid du Colombier     if (aa == NULL)
735*593dc095SDavid du Colombier 	return true;
736*593dc095SDavid du Colombier     memcpy(aa, *a, *max_count * elem_size);
737*593dc095SDavid du Colombier     if (*a != a0)
738*593dc095SDavid du Colombier 	gs_free_object(mem, *a, cname);
739*593dc095SDavid du Colombier     *a = aa;
740*593dc095SDavid du Colombier     *max_count += enhancement * 2;
741*593dc095SDavid du Colombier     return false;
742*593dc095SDavid du Colombier }
743*593dc095SDavid du Colombier 
t1_hinter__set_alignment_zones(t1_hinter * this,float * blues,int count,enum t1_zone_type type,bool family)744*593dc095SDavid du Colombier private int t1_hinter__set_alignment_zones(t1_hinter * this, float * blues, int count, enum t1_zone_type type, bool family)
745*593dc095SDavid du Colombier {   int count2 = count / 2, i, j;
746*593dc095SDavid du Colombier 
747*593dc095SDavid du Colombier     if (!family) {
748*593dc095SDavid du Colombier         /* Store zones : */
749*593dc095SDavid du Colombier         if (count2 + this->zone_count >= this->max_zone_count)
750*593dc095SDavid du Colombier 	    if(t1_hinter__realloc_array(this->memory, (void **)&this->zone, this->zone0, &this->max_zone_count,
751*593dc095SDavid du Colombier 	                                sizeof(this->zone0) / count_of(this->zone0),
752*593dc095SDavid du Colombier 					max(T1_MAX_ALIGNMENT_ZONES, count), s_zone_array))
753*593dc095SDavid du Colombier     		return_error(gs_error_VMerror);
754*593dc095SDavid du Colombier         for (i = 0; i < count2; i++)
755*593dc095SDavid du Colombier             t1_hinter__make_zone(this, &this->zone[this->zone_count + i], blues + i + i, type, this->blue_fuzz);
756*593dc095SDavid du Colombier         this->zone_count += count2;
757*593dc095SDavid du Colombier     } else {
758*593dc095SDavid du Colombier         /* Replace with family zones if allowed : */
759*593dc095SDavid du Colombier         t1_zone zone;
760*593dc095SDavid du Colombier         for (i = 0; i < count2; i++) {
761*593dc095SDavid du Colombier             t1_hinter__make_zone(this, &zone, blues + i, type, this->blue_fuzz);
762*593dc095SDavid du Colombier             for (j = 0; j<this->zone_count; j++) {
763*593dc095SDavid du Colombier                 t1_zone *zone1 = &this->zone[j];
764*593dc095SDavid du Colombier                 if (any_abs(zone.y -           zone1->y          ) * this->heigt_transform_coef <= 1 &&
765*593dc095SDavid du Colombier                     any_abs(zone.overshoot_y - zone1->overshoot_y) * this->heigt_transform_coef <= 1)
766*593dc095SDavid du Colombier                     *zone1 = zone;
767*593dc095SDavid du Colombier             }
768*593dc095SDavid du Colombier         }
769*593dc095SDavid du Colombier     }
770*593dc095SDavid du Colombier     return 0;
771*593dc095SDavid du Colombier }
772*593dc095SDavid du Colombier 
t1_hinter__set_stem_snap(t1_hinter * this,float * value,int count,unsigned short hv)773*593dc095SDavid du Colombier private int t1_hinter__set_stem_snap(t1_hinter * this, float * value, int count, unsigned short hv)
774*593dc095SDavid du Colombier {   int count0 = this->stem_snap_count[hv], i;
775*593dc095SDavid du Colombier 
776*593dc095SDavid du Colombier     if (count + count0 >= this->max_stem_snap_count[hv])
777*593dc095SDavid du Colombier 	if(t1_hinter__realloc_array(this->memory, (void **)&this->stem_snap[hv], this->stem_snap0[hv], &this->max_stem_snap_count[hv],
778*593dc095SDavid du Colombier 	                                sizeof(this->stem_snap0[0]) / count_of(this->stem_snap0[0]),
779*593dc095SDavid du Colombier 					max(T1_MAX_STEM_SNAPS, count), s_stem_snap_array))
780*593dc095SDavid du Colombier     	    return_error(gs_error_VMerror);
781*593dc095SDavid du Colombier     for (i = 0; i < count; i++)
782*593dc095SDavid du Colombier         this->stem_snap[hv][count0 + i] = float2fixed(value[i]);
783*593dc095SDavid du Colombier     this->stem_snap_count[hv] += count;
784*593dc095SDavid du Colombier     return 0;
785*593dc095SDavid du Colombier }
786*593dc095SDavid du Colombier 
enable_draw_import(void)787*593dc095SDavid du Colombier private void enable_draw_import(void)
788*593dc095SDavid du Colombier {   /* CAUTION: can't close DC on import error */
789*593dc095SDavid du Colombier     vd_get_dc('h');
790*593dc095SDavid du Colombier     vd_set_shift(VD_SHIFT_X, VD_SHIFT_Y);
791*593dc095SDavid du Colombier     vd_set_scale(VD_SCALE);
792*593dc095SDavid du Colombier     vd_set_origin(0,0);
793*593dc095SDavid du Colombier     vd_erase(RGB(255, 255, 255));
794*593dc095SDavid du Colombier     vd_setcolor(VD_IMPORT_COLOR);
795*593dc095SDavid du Colombier     vd_setlinewidth(0);
796*593dc095SDavid du Colombier }
797*593dc095SDavid du Colombier 
t1_hinter__set_font_data(t1_hinter * this,int FontType,gs_type1_data * pdata,bool no_grid_fitting)798*593dc095SDavid du Colombier int t1_hinter__set_font_data(t1_hinter * this, int FontType, gs_type1_data *pdata, bool no_grid_fitting)
799*593dc095SDavid du Colombier {   int code;
800*593dc095SDavid du Colombier 
801*593dc095SDavid du Colombier     t1_hinter__init_outline(this);
802*593dc095SDavid du Colombier     this->FontType = FontType;
803*593dc095SDavid du Colombier     this->BlueScale = pdata->BlueScale;
804*593dc095SDavid du Colombier     this->blue_shift = float2fixed(pdata->BlueShift);
805*593dc095SDavid du Colombier     this->blue_fuzz  = float2fixed(pdata->BlueFuzz);
806*593dc095SDavid du Colombier     this->suppress_overshoots = (this->BlueScale > this->heigt_transform_coef / (1 << this->log2_pixels_y) - 0.00020417);
807*593dc095SDavid du Colombier     this->overshoot_threshold = (this->heigt_transform_coef != 0 ? (t1_glyph_space_coord)(fixed_half * (1 << this->log2_pixels_y) / this->heigt_transform_coef) : 0);
808*593dc095SDavid du Colombier     this->ForceBold = pdata->ForceBold;
809*593dc095SDavid du Colombier     this->disable_hinting |= no_grid_fitting;
810*593dc095SDavid du Colombier     this->charpath_flag = no_grid_fitting;
811*593dc095SDavid du Colombier     if (vd_enabled && (VD_DRAW_IMPORT || this->disable_hinting))
812*593dc095SDavid du Colombier 	enable_draw_import();
813*593dc095SDavid du Colombier     if (this->disable_hinting)
814*593dc095SDavid du Colombier 	return 0;
815*593dc095SDavid du Colombier     code = t1_hinter__set_alignment_zones(this, pdata->OtherBlues.values, pdata->OtherBlues.count, botzone, false);
816*593dc095SDavid du Colombier     if (code >= 0)
817*593dc095SDavid du Colombier 	code = t1_hinter__set_alignment_zones(this, pdata->BlueValues.values, min(2, pdata->BlueValues.count), botzone, false);
818*593dc095SDavid du Colombier     if (code >= 0)
819*593dc095SDavid du Colombier 	code = t1_hinter__set_alignment_zones(this, pdata->BlueValues.values + 2, pdata->BlueValues.count - 2, topzone, false);
820*593dc095SDavid du Colombier     if (code >= 0)
821*593dc095SDavid du Colombier 	code = t1_hinter__set_alignment_zones(this, pdata->FamilyOtherBlues.values, pdata->FamilyOtherBlues.count, botzone, true);
822*593dc095SDavid du Colombier     if (code >= 0)
823*593dc095SDavid du Colombier 	code = t1_hinter__set_alignment_zones(this, pdata->FamilyBlues.values, min(2, pdata->FamilyBlues.count), botzone, true);
824*593dc095SDavid du Colombier     if (code >= 0)
825*593dc095SDavid du Colombier 	code = t1_hinter__set_alignment_zones(this, pdata->FamilyBlues.values + 2, pdata->FamilyBlues.count - 2, topzone, true);
826*593dc095SDavid du Colombier     if (code >= 0)
827*593dc095SDavid du Colombier 	code = t1_hinter__set_stem_snap(this, pdata->StdHW.values, pdata->StdHW.count, 0);
828*593dc095SDavid du Colombier     if (code >= 0)
829*593dc095SDavid du Colombier 	code = t1_hinter__set_stem_snap(this, pdata->StdVW.values, pdata->StdVW.count, 1);
830*593dc095SDavid du Colombier     if (code >= 0)
831*593dc095SDavid du Colombier 	code = t1_hinter__set_stem_snap(this, pdata->StemSnapH.values, pdata->StemSnapH.count, 0);
832*593dc095SDavid du Colombier     if (code >= 0)
833*593dc095SDavid du Colombier 	code = t1_hinter__set_stem_snap(this, pdata->StemSnapV.values, pdata->StemSnapV.count, 1);
834*593dc095SDavid du Colombier     return code;
835*593dc095SDavid du Colombier }
836*593dc095SDavid du Colombier 
t1_hinter__set_font42_data(t1_hinter * this,int FontType,gs_type42_data * pdata,bool no_grid_fitting)837*593dc095SDavid du Colombier int t1_hinter__set_font42_data(t1_hinter * this, int FontType, gs_type42_data *pdata, bool no_grid_fitting)
838*593dc095SDavid du Colombier {
839*593dc095SDavid du Colombier     t1_hinter__init_outline(this);
840*593dc095SDavid du Colombier     this->FontType = FontType;
841*593dc095SDavid du Colombier     this->BlueScale = 0.039625;	/* A Type 1 spec default. */
842*593dc095SDavid du Colombier     this->blue_shift = 7;	/* A Type 1 spec default. */
843*593dc095SDavid du Colombier     this->blue_fuzz  = 1;	/* A Type 1 spec default. */
844*593dc095SDavid du Colombier     this->suppress_overshoots = (this->BlueScale > this->heigt_transform_coef / (1 << this->log2_pixels_y) - 0.00020417);
845*593dc095SDavid du Colombier     this->overshoot_threshold = (this->heigt_transform_coef != 0 ? (t1_glyph_space_coord)(fixed_half * (1 << this->log2_pixels_y) / this->heigt_transform_coef) : 0);
846*593dc095SDavid du Colombier     this->ForceBold = false;
847*593dc095SDavid du Colombier     this->disable_hinting |= no_grid_fitting;
848*593dc095SDavid du Colombier     this->charpath_flag = no_grid_fitting;
849*593dc095SDavid du Colombier     this->autohinting = true;
850*593dc095SDavid du Colombier     if (vd_enabled && (VD_DRAW_IMPORT || this->disable_hinting))
851*593dc095SDavid du Colombier 	enable_draw_import();
852*593dc095SDavid du Colombier     if (this->disable_hinting)
853*593dc095SDavid du Colombier 	return 0;
854*593dc095SDavid du Colombier     /* Currently we don't provice alignments zones or stem snap. */
855*593dc095SDavid du Colombier     return 0;
856*593dc095SDavid du Colombier }
857*593dc095SDavid du Colombier 
t1_hinter__can_add_pole(t1_hinter * this,t1_pole ** pole)858*593dc095SDavid du Colombier private inline int t1_hinter__can_add_pole(t1_hinter * this, t1_pole **pole)
859*593dc095SDavid du Colombier {   if (this->pole_count >= this->max_pole_count)
860*593dc095SDavid du Colombier         if(t1_hinter__realloc_array(this->memory, (void **)&this->pole, this->pole0, &this->max_pole_count,
861*593dc095SDavid du Colombier 				    sizeof(this->pole0) / count_of(this->pole0), T1_MAX_POLES, s_pole_array))
862*593dc095SDavid du Colombier 	    return_error(gs_error_VMerror);
863*593dc095SDavid du Colombier     *pole = &this->pole[this->pole_count];
864*593dc095SDavid du Colombier     return 0;
865*593dc095SDavid du Colombier }
866*593dc095SDavid du Colombier 
t1_hinter__add_pole(t1_hinter * this,t1_glyph_space_coord xx,t1_glyph_space_coord yy,enum t1_pole_type type)867*593dc095SDavid du Colombier private inline int t1_hinter__add_pole(t1_hinter * this, t1_glyph_space_coord xx, t1_glyph_space_coord yy, enum t1_pole_type type)
868*593dc095SDavid du Colombier {   t1_pole *pole;
869*593dc095SDavid du Colombier     int code = t1_hinter__can_add_pole(this, &pole);
870*593dc095SDavid du Colombier 
871*593dc095SDavid du Colombier     if (code < 0)
872*593dc095SDavid du Colombier 	return code;
873*593dc095SDavid du Colombier     pole->gx = pole->ax = this->cx += xx;
874*593dc095SDavid du Colombier     pole->gy = pole->ay = this->cy += yy;
875*593dc095SDavid du Colombier     pole->ox = pole->oy = 0;
876*593dc095SDavid du Colombier     pole->type = type;
877*593dc095SDavid du Colombier     pole->contour_index = this->contour_count;
878*593dc095SDavid du Colombier     pole->aligned_x = pole->aligned_y = unaligned;
879*593dc095SDavid du Colombier     this->pole_count++;
880*593dc095SDavid du Colombier     return 0;
881*593dc095SDavid du Colombier }
882*593dc095SDavid du Colombier 
t1_hinter__sbw(t1_hinter * this,fixed sbx,fixed sby,fixed wx,fixed wy)883*593dc095SDavid du Colombier int t1_hinter__sbw(t1_hinter * this, fixed sbx, fixed sby, fixed wx,  fixed wy)
884*593dc095SDavid du Colombier {   t1_hinter__adjust_matrix_precision(this, sbx, sby);
885*593dc095SDavid du Colombier     t1_hinter__adjust_matrix_precision(this, wx, wy);
886*593dc095SDavid du Colombier     this->cx = this->orig_gx = this->subglyph_orig_gx = sbx;
887*593dc095SDavid du Colombier     this->cy = this->orig_gy = this->subglyph_orig_gy = sby;
888*593dc095SDavid du Colombier     this->width_gx = wx;
889*593dc095SDavid du Colombier     this->width_gy = wy;
890*593dc095SDavid du Colombier     return 0;
891*593dc095SDavid du Colombier }
892*593dc095SDavid du Colombier 
t1_hinter__sbw_seac(t1_hinter * this,fixed sbx,fixed sby)893*593dc095SDavid du Colombier int t1_hinter__sbw_seac(t1_hinter * this, fixed sbx, fixed sby)
894*593dc095SDavid du Colombier {   t1_hinter__adjust_matrix_precision(this, sbx, sby);
895*593dc095SDavid du Colombier     this->cx = this->subglyph_orig_gx = this->orig_gx + sbx;
896*593dc095SDavid du Colombier     this->cy = this->subglyph_orig_gy = this->orig_gy + sby;
897*593dc095SDavid du Colombier     return 0;
898*593dc095SDavid du Colombier }
899*593dc095SDavid du Colombier 
t1_hinter__rmoveto(t1_hinter * this,fixed xx,fixed yy)900*593dc095SDavid du Colombier int t1_hinter__rmoveto(t1_hinter * this, fixed xx, fixed yy)
901*593dc095SDavid du Colombier {   int code;
902*593dc095SDavid du Colombier 
903*593dc095SDavid du Colombier     t1_hinter__adjust_matrix_precision(this, xx, yy);
904*593dc095SDavid du Colombier     if (this->flex_count == 0) {
905*593dc095SDavid du Colombier 	if (this->disable_hinting) {
906*593dc095SDavid du Colombier 	    t1_glyph_space_coord gx = this->cx += xx;
907*593dc095SDavid du Colombier 	    t1_glyph_space_coord gy = this->cy += yy;
908*593dc095SDavid du Colombier 	    fixed fx, fy;
909*593dc095SDavid du Colombier 
910*593dc095SDavid du Colombier 	    if (this->path_opened) {
911*593dc095SDavid du Colombier 		code = gx_path_close_subpath(this->output_path);
912*593dc095SDavid du Colombier 		if (code < 0)
913*593dc095SDavid du Colombier 		    return code;
914*593dc095SDavid du Colombier 		this->path_opened = false;
915*593dc095SDavid du Colombier 	    }
916*593dc095SDavid du Colombier 	    g2d(this, gx, gy, &fx, &fy);
917*593dc095SDavid du Colombier 	    code = gx_path_add_point(this->output_path, fx, fy);
918*593dc095SDavid du Colombier 	    vd_circle(this->cx, this->cy, 2, RGB(255, 0, 0));
919*593dc095SDavid du Colombier 	    vd_moveto(this->cx, this->cy);
920*593dc095SDavid du Colombier 	    if (this->flex_count == 0) {
921*593dc095SDavid du Colombier 		this->bx = this->cx;
922*593dc095SDavid du Colombier 		this->by = this->cy;
923*593dc095SDavid du Colombier 	    }
924*593dc095SDavid du Colombier 	    return code;
925*593dc095SDavid du Colombier 	}
926*593dc095SDavid du Colombier 	if (this->pole_count > 0 && this->pole[this->pole_count - 1].type == moveto)
927*593dc095SDavid du Colombier 	    this->pole_count--;
928*593dc095SDavid du Colombier 	if (this->pole_count > 0 && this->pole[this->pole_count - 1].type != closepath) {
929*593dc095SDavid du Colombier 	    code = t1_hinter__closepath(this);
930*593dc095SDavid du Colombier 	    if (code < 0)
931*593dc095SDavid du Colombier 		return code;
932*593dc095SDavid du Colombier 	}
933*593dc095SDavid du Colombier     }
934*593dc095SDavid du Colombier     code = t1_hinter__add_pole(this, xx, yy, moveto);
935*593dc095SDavid du Colombier     if (this->flex_count == 0) {
936*593dc095SDavid du Colombier 	this->bx = this->cx;
937*593dc095SDavid du Colombier 	this->by = this->cy;
938*593dc095SDavid du Colombier     }
939*593dc095SDavid du Colombier     vd_circle(this->cx, this->cy, 2, RGB(255, 0, 0));
940*593dc095SDavid du Colombier     vd_moveto(this->cx, this->cy);
941*593dc095SDavid du Colombier     return code;
942*593dc095SDavid du Colombier }
943*593dc095SDavid du Colombier 
t1_hinter__skip_degenerate_segnment(t1_hinter * this,int npoles)944*593dc095SDavid du Colombier private inline void t1_hinter__skip_degenerate_segnment(t1_hinter * this, int npoles)
945*593dc095SDavid du Colombier {   /* Degenerate segments amy appear due to import shift with bbox > 4096 */
946*593dc095SDavid du Colombier     int contour_beg = this->contour[this->contour_count], i;
947*593dc095SDavid du Colombier 
948*593dc095SDavid du Colombier     if (contour_beg >= this->pole_count - npoles)
949*593dc095SDavid du Colombier 	return;
950*593dc095SDavid du Colombier     for (i = this->pole_count - npoles - 1; i < this->pole_count - 1; i++)
951*593dc095SDavid du Colombier 	if (this->pole[i].ax != this->cx || this->pole[i].ay != this->cy)
952*593dc095SDavid du Colombier 	    return;
953*593dc095SDavid du Colombier     this->pole_count -= npoles;
954*593dc095SDavid du Colombier }
955*593dc095SDavid du Colombier 
t1_hinter__rlineto(t1_hinter * this,fixed xx,fixed yy)956*593dc095SDavid du Colombier int t1_hinter__rlineto(t1_hinter * this, fixed xx, fixed yy)
957*593dc095SDavid du Colombier {
958*593dc095SDavid du Colombier     t1_hinter__adjust_matrix_precision(this, xx, yy);
959*593dc095SDavid du Colombier     if (this->disable_hinting) {
960*593dc095SDavid du Colombier 	t1_glyph_space_coord gx = this->cx += xx;
961*593dc095SDavid du Colombier 	t1_glyph_space_coord gy = this->cy += yy;
962*593dc095SDavid du Colombier 	fixed fx, fy;
963*593dc095SDavid du Colombier 
964*593dc095SDavid du Colombier 	vd_lineto(this->cx, this->cy);
965*593dc095SDavid du Colombier 	this->path_opened = true;
966*593dc095SDavid du Colombier 	g2d(this, gx, gy, &fx, &fy);
967*593dc095SDavid du Colombier 	return gx_path_add_line(this->output_path, fx, fy);
968*593dc095SDavid du Colombier     } else {
969*593dc095SDavid du Colombier 	int code = t1_hinter__add_pole(this, xx, yy, oncurve);
970*593dc095SDavid du Colombier 
971*593dc095SDavid du Colombier 	if (code < 0)
972*593dc095SDavid du Colombier 	    return code;
973*593dc095SDavid du Colombier 	vd_lineto(this->cx, this->cy);
974*593dc095SDavid du Colombier 	t1_hinter__skip_degenerate_segnment(this, 1);
975*593dc095SDavid du Colombier 	return 0;
976*593dc095SDavid du Colombier     }
977*593dc095SDavid du Colombier }
978*593dc095SDavid du Colombier 
t1_hinter__rcurveto(t1_hinter * this,fixed xx0,fixed yy0,fixed xx1,fixed yy1,fixed xx2,fixed yy2)979*593dc095SDavid du Colombier int t1_hinter__rcurveto(t1_hinter * this, fixed xx0, fixed yy0, fixed xx1, fixed yy1, fixed xx2, fixed yy2)
980*593dc095SDavid du Colombier {
981*593dc095SDavid du Colombier     t1_hinter__adjust_matrix_precision(this, xx0, yy0);
982*593dc095SDavid du Colombier     t1_hinter__adjust_matrix_precision(this, xx1, yy1);
983*593dc095SDavid du Colombier     t1_hinter__adjust_matrix_precision(this, xx2, yy2);
984*593dc095SDavid du Colombier     if (this->disable_hinting) {
985*593dc095SDavid du Colombier 	t1_glyph_space_coord gx0 = this->cx += xx0;
986*593dc095SDavid du Colombier 	t1_glyph_space_coord gy0 = this->cy += yy0;
987*593dc095SDavid du Colombier 	t1_glyph_space_coord gx1 = this->cx += xx1;
988*593dc095SDavid du Colombier 	t1_glyph_space_coord gy1 = this->cy += yy1;
989*593dc095SDavid du Colombier 	t1_glyph_space_coord gx2 = this->cx += xx2;
990*593dc095SDavid du Colombier 	t1_glyph_space_coord gy2 = this->cy += yy2;
991*593dc095SDavid du Colombier 	fixed fx0, fy0, fx1, fy1, fx2, fy2;
992*593dc095SDavid du Colombier 
993*593dc095SDavid du Colombier 	vd_curveto(gx0, gy0, gx1, gy1, gx2, gy2);
994*593dc095SDavid du Colombier 	this->path_opened = true;
995*593dc095SDavid du Colombier 	g2d(this, gx0, gy0, &fx0, &fy0);
996*593dc095SDavid du Colombier 	g2d(this, gx1, gy1, &fx1, &fy1);
997*593dc095SDavid du Colombier 	g2d(this, gx2, gy2, &fx2, &fy2);
998*593dc095SDavid du Colombier 	return gx_path_add_curve(this->output_path, fx0, fy0, fx1, fy1, fx2, fy2);
999*593dc095SDavid du Colombier     } else {
1000*593dc095SDavid du Colombier 	int code;
1001*593dc095SDavid du Colombier 
1002*593dc095SDavid du Colombier 	code = t1_hinter__add_pole(this, xx0, yy0, offcurve);
1003*593dc095SDavid du Colombier 	if (code < 0)
1004*593dc095SDavid du Colombier 	    return code;
1005*593dc095SDavid du Colombier 	code = t1_hinter__add_pole(this, xx1, yy1, offcurve);
1006*593dc095SDavid du Colombier 	if (code < 0)
1007*593dc095SDavid du Colombier 	    return code;
1008*593dc095SDavid du Colombier 	code = t1_hinter__add_pole(this, xx2, yy2, oncurve);
1009*593dc095SDavid du Colombier 	if (code < 0)
1010*593dc095SDavid du Colombier 	    return code;
1011*593dc095SDavid du Colombier 	vd_curveto(this->pole[this->pole_count - 3].gx, this->pole[this->pole_count - 3].gy,
1012*593dc095SDavid du Colombier 		   this->pole[this->pole_count - 2].gx, this->pole[this->pole_count - 2].gy,
1013*593dc095SDavid du Colombier 		   this->cx, this->cy);
1014*593dc095SDavid du Colombier 	t1_hinter__skip_degenerate_segnment(this, 3);
1015*593dc095SDavid du Colombier 	return 0;
1016*593dc095SDavid du Colombier     }
1017*593dc095SDavid du Colombier }
1018*593dc095SDavid du Colombier 
t1_hinter__setcurrentpoint(t1_hinter * this,fixed xx,fixed yy)1019*593dc095SDavid du Colombier void t1_hinter__setcurrentpoint(t1_hinter * this, fixed xx, fixed yy)
1020*593dc095SDavid du Colombier {
1021*593dc095SDavid du Colombier     t1_hinter__adjust_matrix_precision(this, xx, yy);
1022*593dc095SDavid du Colombier     if (this->FontType != 2) {
1023*593dc095SDavid du Colombier 	/* We use this function to set a subglyph origin
1024*593dc095SDavid du Colombier 	   for composite glyphs in Type 2 fonts.
1025*593dc095SDavid du Colombier 	 */
1026*593dc095SDavid du Colombier 	this->cx = xx;
1027*593dc095SDavid du Colombier 	this->cy = yy;
1028*593dc095SDavid du Colombier     } else if (this->cx != xx || this->cy != yy) {
1029*593dc095SDavid du Colombier 	/* Type 1 spec reads : "The setcurrentpoint command is used only
1030*593dc095SDavid du Colombier 	   in conjunction with results from OtherSubrs procedures."
1031*593dc095SDavid du Colombier 	   We guess that such cases don't cause a real coordinate change
1032*593dc095SDavid du Colombier 	   (our testbase shows that). But we met a font
1033*593dc095SDavid du Colombier 	   (see comparefiles/type1-ce1_setcurrentpoint.ps) which use
1034*593dc095SDavid du Colombier 	   setcurrentpoint immediately before moveto, with no conjunction
1035*593dc095SDavid du Colombier 	   with OtherSubrs. (The check above is debug purpose only.)
1036*593dc095SDavid du Colombier 	 */
1037*593dc095SDavid du Colombier 	this->cx = xx;
1038*593dc095SDavid du Colombier 	this->cy = yy;
1039*593dc095SDavid du Colombier     }
1040*593dc095SDavid du Colombier }
1041*593dc095SDavid du Colombier 
t1_hinter__closepath(t1_hinter * this)1042*593dc095SDavid du Colombier int t1_hinter__closepath(t1_hinter * this)
1043*593dc095SDavid du Colombier {   if (this->disable_hinting) {
1044*593dc095SDavid du Colombier 	vd_lineto(this->bx, this->by);
1045*593dc095SDavid du Colombier 	this->path_opened = false;
1046*593dc095SDavid du Colombier         return gx_path_close_subpath(this->output_path);
1047*593dc095SDavid du Colombier     } else {
1048*593dc095SDavid du Colombier 	int contour_beg = this->contour[this->contour_count], code;
1049*593dc095SDavid du Colombier 
1050*593dc095SDavid du Colombier 	if (contour_beg == this->pole_count)
1051*593dc095SDavid du Colombier 	    return 0; /* maybe a single trailing moveto */
1052*593dc095SDavid du Colombier 	if (vd_enabled && (VD_DRAW_IMPORT || this->disable_hinting)) {
1053*593dc095SDavid du Colombier 	    vd_setcolor(VD_IMPORT_COLOR);
1054*593dc095SDavid du Colombier 	    vd_setlinewidth(0);
1055*593dc095SDavid du Colombier 	    vd_lineto(this->bx, this->by);
1056*593dc095SDavid du Colombier 	}
1057*593dc095SDavid du Colombier 	if (this->bx == this->cx && this->by == this->cy) {
1058*593dc095SDavid du Colombier 	    /* Don't create degenerate segment */
1059*593dc095SDavid du Colombier 	    this->pole[this->pole_count - 1].type = closepath;
1060*593dc095SDavid du Colombier 	} else {
1061*593dc095SDavid du Colombier 	    t1_glyph_space_coord cx = this->cx, cy = this->cy;
1062*593dc095SDavid du Colombier 
1063*593dc095SDavid du Colombier 	    this->cx = this->bx;
1064*593dc095SDavid du Colombier 	    this->cy = this->by;
1065*593dc095SDavid du Colombier 	    code = t1_hinter__add_pole(this, 0, 0, closepath);
1066*593dc095SDavid du Colombier 	    if (code < 0)
1067*593dc095SDavid du Colombier 		return code;
1068*593dc095SDavid du Colombier 	    this->cx = cx;
1069*593dc095SDavid du Colombier 	    this->cy = cy;
1070*593dc095SDavid du Colombier 	}
1071*593dc095SDavid du Colombier 	this->contour_count++;
1072*593dc095SDavid du Colombier 	if (this->contour_count >= this->max_contour_count)
1073*593dc095SDavid du Colombier 	    if(t1_hinter__realloc_array(this->memory, (void **)&this->contour, this->contour0, &this->max_contour_count,
1074*593dc095SDavid du Colombier 					sizeof(this->contour0) / count_of(this->contour0), T1_MAX_CONTOURS, s_contour_array))
1075*593dc095SDavid du Colombier 		return_error(gs_error_VMerror);
1076*593dc095SDavid du Colombier 	this->contour[this->contour_count] = this->pole_count;
1077*593dc095SDavid du Colombier         return 0;
1078*593dc095SDavid du Colombier     }
1079*593dc095SDavid du Colombier }
1080*593dc095SDavid du Colombier 
t1_hinter__can_add_hint(t1_hinter * this,t1_hint ** hint)1081*593dc095SDavid du Colombier private inline int t1_hinter__can_add_hint(t1_hinter * this, t1_hint **hint)
1082*593dc095SDavid du Colombier {   if (this->hint_count >= this->max_hint_count)
1083*593dc095SDavid du Colombier         if(t1_hinter__realloc_array(this->memory, (void **)&this->hint, this->hint0, &this->max_hint_count,
1084*593dc095SDavid du Colombier 				    sizeof(this->hint0) / count_of(this->hint0), T1_MAX_HINTS, s_hint_array))
1085*593dc095SDavid du Colombier 	    return_error(gs_error_VMerror);
1086*593dc095SDavid du Colombier     *hint = &this->hint[this->hint_count];
1087*593dc095SDavid du Colombier     return 0;
1088*593dc095SDavid du Colombier }
1089*593dc095SDavid du Colombier 
t1_hinter__flex_beg(t1_hinter * this)1090*593dc095SDavid du Colombier int t1_hinter__flex_beg(t1_hinter * this)
1091*593dc095SDavid du Colombier {   if (this->flex_count != 0)
1092*593dc095SDavid du Colombier 	return_error(gs_error_invalidfont);
1093*593dc095SDavid du Colombier     this->flex_count++;
1094*593dc095SDavid du Colombier     if (this->disable_hinting)
1095*593dc095SDavid du Colombier 	return t1_hinter__rmoveto(this, 0, 0);
1096*593dc095SDavid du Colombier     return 0;
1097*593dc095SDavid du Colombier }
1098*593dc095SDavid du Colombier 
t1_hinter__flex_point(t1_hinter * this)1099*593dc095SDavid du Colombier int t1_hinter__flex_point(t1_hinter * this)
1100*593dc095SDavid du Colombier {   if (this->flex_count == 0)
1101*593dc095SDavid du Colombier 	return_error(gs_error_invalidfont);
1102*593dc095SDavid du Colombier     this->flex_count++;
1103*593dc095SDavid du Colombier     return 0;
1104*593dc095SDavid du Colombier }
1105*593dc095SDavid du Colombier 
t1_hinter__flex_end(t1_hinter * this,fixed flex_height)1106*593dc095SDavid du Colombier int t1_hinter__flex_end(t1_hinter * this, fixed flex_height)
1107*593dc095SDavid du Colombier {   t1_pole *pole0, *pole1, *pole4;
1108*593dc095SDavid du Colombier     t1_hinter_space_coord ox, oy;
1109*593dc095SDavid du Colombier     const int32_t div_x = this->g2o_fraction << this->log2_pixels_x;
1110*593dc095SDavid du Colombier     const int32_t div_y = this->g2o_fraction << this->log2_pixels_y;
1111*593dc095SDavid du Colombier 
1112*593dc095SDavid du Colombier     if (this->flex_count != 8)
1113*593dc095SDavid du Colombier 	return_error(gs_error_invalidfont);
1114*593dc095SDavid du Colombier     /* We've got 8 poles accumulated in pole array. */
1115*593dc095SDavid du Colombier     pole0 = &this->pole[this->pole_count - 8];
1116*593dc095SDavid du Colombier     pole1 = &this->pole[this->pole_count - 7];
1117*593dc095SDavid du Colombier     pole4 = &this->pole[this->pole_count - 4];
1118*593dc095SDavid du Colombier     g2o(this, pole4->gx - pole1->gx, pole4->gy - pole1->gy, &ox, &oy);
1119*593dc095SDavid du Colombier     if (any_abs(ox) > div_x * fixed2float(flex_height) / 100 ||
1120*593dc095SDavid du Colombier 	any_abs(oy) > div_y * fixed2float(flex_height) / 100) {
1121*593dc095SDavid du Colombier 	/* do with curves */
1122*593dc095SDavid du Colombier 	vd_moveto (pole0[0].gx, pole0[0].gy);
1123*593dc095SDavid du Colombier 	vd_curveto(pole0[2].gx, pole0[2].gy, pole0[3].gx, pole0[3].gy, pole0[4].gx, pole0[4].gy);
1124*593dc095SDavid du Colombier 	vd_curveto(pole0[5].gx, pole0[5].gy, pole0[6].gx, pole0[6].gy, pole0[7].gx, pole0[7].gy);
1125*593dc095SDavid du Colombier 	if (this->disable_hinting) {
1126*593dc095SDavid du Colombier 	    fixed fx0, fy0, fx1, fy1, fx2, fy2;
1127*593dc095SDavid du Colombier 	    int code;
1128*593dc095SDavid du Colombier 
1129*593dc095SDavid du Colombier 	    g2d(this, pole0[2].gx, pole0[2].gy, &fx0, &fy0);
1130*593dc095SDavid du Colombier 	    g2d(this, pole0[3].gx, pole0[3].gy, &fx1, &fy1);
1131*593dc095SDavid du Colombier 	    g2d(this, pole0[4].gx, pole0[4].gy, &fx2, &fy2);
1132*593dc095SDavid du Colombier 	    code = gx_path_add_curve(this->output_path, fx0, fy0, fx1, fy1, fx2, fy2);
1133*593dc095SDavid du Colombier 	    if (code < 0)
1134*593dc095SDavid du Colombier 		return code;
1135*593dc095SDavid du Colombier 	    g2d(this, pole0[5].gx, pole0[5].gy, &fx0, &fy0);
1136*593dc095SDavid du Colombier 	    g2d(this, pole0[6].gx, pole0[6].gy, &fx1, &fy1);
1137*593dc095SDavid du Colombier 	    g2d(this, pole0[7].gx, pole0[7].gy, &fx2, &fy2);
1138*593dc095SDavid du Colombier 	    this->flex_count = 0;
1139*593dc095SDavid du Colombier 	    this->pole_count = 0;
1140*593dc095SDavid du Colombier 	    return gx_path_add_curve(this->output_path, fx0, fy0, fx1, fy1, fx2, fy2);
1141*593dc095SDavid du Colombier 	} else {
1142*593dc095SDavid du Colombier 	    memmove(pole1, pole1 + 1, (sizeof(this->pole0) / count_of(this->pole0)) * 7);
1143*593dc095SDavid du Colombier 	    pole0[1].type = pole0[2].type = offcurve;
1144*593dc095SDavid du Colombier 	    pole0[3].type = oncurve;
1145*593dc095SDavid du Colombier 	    pole0[4].type = pole0[5].type = offcurve;
1146*593dc095SDavid du Colombier 	    pole0[6].type = oncurve;
1147*593dc095SDavid du Colombier 	    this->pole_count--;
1148*593dc095SDavid du Colombier 	}
1149*593dc095SDavid du Colombier     } else {
1150*593dc095SDavid du Colombier 	/* do with line */
1151*593dc095SDavid du Colombier 	vd_moveto(pole0[0].gx, pole0[0].gy);
1152*593dc095SDavid du Colombier 	vd_lineto(pole0[7].gx, pole0[7].gy);
1153*593dc095SDavid du Colombier 	if (this->disable_hinting) {
1154*593dc095SDavid du Colombier 	    fixed fx, fy;
1155*593dc095SDavid du Colombier 
1156*593dc095SDavid du Colombier 	    g2d(this, pole0[7].gx, pole0[7].gy, &fx, &fy);
1157*593dc095SDavid du Colombier 	    this->flex_count = 0;
1158*593dc095SDavid du Colombier 	    this->pole_count = 0;
1159*593dc095SDavid du Colombier 	    return gx_path_add_line(this->output_path, fx, fy);
1160*593dc095SDavid du Colombier 	} else {
1161*593dc095SDavid du Colombier 	    pole0[1] = pole0[7];
1162*593dc095SDavid du Colombier 	    pole0[1].type = oncurve;
1163*593dc095SDavid du Colombier 	    this->pole_count -= 6;
1164*593dc095SDavid du Colombier 	}
1165*593dc095SDavid du Colombier     }
1166*593dc095SDavid du Colombier     this->flex_count = 0;
1167*593dc095SDavid du Colombier     return 0;
1168*593dc095SDavid du Colombier }
1169*593dc095SDavid du Colombier 
t1_hinter__can_add_hint_range(t1_hinter * this,t1_hint_range ** hint_range)1170*593dc095SDavid du Colombier private inline int t1_hinter__can_add_hint_range(t1_hinter * this, t1_hint_range **hint_range)
1171*593dc095SDavid du Colombier {   if (this->hint_range_count >= this->max_hint_range_count)
1172*593dc095SDavid du Colombier         if(t1_hinter__realloc_array(this->memory, (void **)&this->hint_range, this->hint_range0, &this->max_hint_range_count,
1173*593dc095SDavid du Colombier 				    sizeof(this->hint_range0) / count_of(this->hint_range0), T1_MAX_HINTS, s_hint_range_array))
1174*593dc095SDavid du Colombier 	    return_error(gs_error_VMerror);
1175*593dc095SDavid du Colombier     *hint_range = &this->hint_range[this->hint_range_count];
1176*593dc095SDavid du Colombier     return 0;
1177*593dc095SDavid du Colombier }
1178*593dc095SDavid du Colombier 
t1_hinter__hint_mask(t1_hinter * this,byte * mask)1179*593dc095SDavid du Colombier int t1_hinter__hint_mask(t1_hinter * this, byte *mask)
1180*593dc095SDavid du Colombier {   int hint_count, i;
1181*593dc095SDavid du Colombier 
1182*593dc095SDavid du Colombier     if (this->disable_hinting)
1183*593dc095SDavid du Colombier 	return 0;
1184*593dc095SDavid du Colombier     hint_count = this->hint_count;
1185*593dc095SDavid du Colombier 
1186*593dc095SDavid du Colombier     for(i = 0; i < hint_count; i++) {
1187*593dc095SDavid du Colombier 	bool activate = (mask != NULL && (mask[i >> 3] & (0x80 >> (i & 7))) != 0);
1188*593dc095SDavid du Colombier 	t1_hint *hint = &this->hint[i];
1189*593dc095SDavid du Colombier 
1190*593dc095SDavid du Colombier 	if (activate) {
1191*593dc095SDavid du Colombier 	    if (hint->range_index != -1 &&
1192*593dc095SDavid du Colombier 		(this->hint_range[hint->range_index].end_pole == -1 ||
1193*593dc095SDavid du Colombier 		 this->hint_range[hint->range_index].end_pole == this->pole_count)) {
1194*593dc095SDavid du Colombier 		 /* continie the range */
1195*593dc095SDavid du Colombier 		this->hint_range[hint->range_index].end_pole = -1;
1196*593dc095SDavid du Colombier 	    } else {
1197*593dc095SDavid du Colombier 		/* add new range */
1198*593dc095SDavid du Colombier 		t1_hint_range *hint_range;
1199*593dc095SDavid du Colombier 		int code = t1_hinter__can_add_hint_range(this, &hint_range);
1200*593dc095SDavid du Colombier 
1201*593dc095SDavid du Colombier 		if (code < 0)
1202*593dc095SDavid du Colombier 		   return code;
1203*593dc095SDavid du Colombier 		hint_range->beg_pole = this->pole_count;
1204*593dc095SDavid du Colombier 		hint_range->end_pole = -1;
1205*593dc095SDavid du Colombier 		hint_range->next = hint->range_index;
1206*593dc095SDavid du Colombier 		hint->range_index = this->hint_range_count;
1207*593dc095SDavid du Colombier 		this->hint_range_count++;
1208*593dc095SDavid du Colombier 	    }
1209*593dc095SDavid du Colombier 	} else {
1210*593dc095SDavid du Colombier 	    if (hint->range_index != -1 &&
1211*593dc095SDavid du Colombier 		this->hint_range[hint->range_index].end_pole == -1) {
1212*593dc095SDavid du Colombier 		/* deactivate */
1213*593dc095SDavid du Colombier 		this->hint_range[hint->range_index].end_pole = this->pole_count;
1214*593dc095SDavid du Colombier 	    } else
1215*593dc095SDavid du Colombier 		DO_NOTHING;
1216*593dc095SDavid du Colombier 	}
1217*593dc095SDavid du Colombier     }
1218*593dc095SDavid du Colombier     return 0;
1219*593dc095SDavid du Colombier }
1220*593dc095SDavid du Colombier 
t1_hinter__drop_hints(t1_hinter * this)1221*593dc095SDavid du Colombier int t1_hinter__drop_hints(t1_hinter * this)
1222*593dc095SDavid du Colombier {   if (this->disable_hinting)
1223*593dc095SDavid du Colombier 	return 0;
1224*593dc095SDavid du Colombier     if (this->primary_hint_count == -1)
1225*593dc095SDavid du Colombier 	this->primary_hint_count = this->hint_range_count;
1226*593dc095SDavid du Colombier     return t1_hinter__hint_mask(this, NULL);
1227*593dc095SDavid du Colombier }
1228*593dc095SDavid du Colombier 
t1_hinter__stem(t1_hinter * this,enum t1_hint_type type,unsigned short stem3_index,fixed v0,fixed v1,int side_mask)1229*593dc095SDavid du Colombier private inline int t1_hinter__stem(t1_hinter * this, enum t1_hint_type type, unsigned short stem3_index
1230*593dc095SDavid du Colombier                                                   , fixed v0, fixed v1, int side_mask)
1231*593dc095SDavid du Colombier {   t1_hint *hint;
1232*593dc095SDavid du Colombier     t1_glyph_space_coord s = (type == hstem ? this->subglyph_orig_gy : this->subglyph_orig_gx);
1233*593dc095SDavid du Colombier     t1_glyph_space_coord g0 = s + v0;
1234*593dc095SDavid du Colombier     t1_glyph_space_coord g1 = s + v0 + v1;
1235*593dc095SDavid du Colombier     t1_hint_range *range;
1236*593dc095SDavid du Colombier     int i, code;
1237*593dc095SDavid du Colombier 
1238*593dc095SDavid du Colombier     t1_hinter__adjust_matrix_precision(this, (side_mask & 1 ? g0 : g1), (side_mask & 2 ? g1 : g0));
1239*593dc095SDavid du Colombier     for (i = 0; i < this->hint_count; i++)
1240*593dc095SDavid du Colombier 	if (this->hint[i].type == type &&
1241*593dc095SDavid du Colombier 		this->hint[i].g0 == g0 && this->hint[i].g1 == g1 &&
1242*593dc095SDavid du Colombier 		this->hint[i].side_mask == side_mask)
1243*593dc095SDavid du Colombier 	    break;
1244*593dc095SDavid du Colombier     if (i < this->hint_count)
1245*593dc095SDavid du Colombier 	hint = &this->hint[i];
1246*593dc095SDavid du Colombier     else {
1247*593dc095SDavid du Colombier 	code = t1_hinter__can_add_hint(this, &hint);
1248*593dc095SDavid du Colombier 	if (code < 0)
1249*593dc095SDavid du Colombier 	    return code;
1250*593dc095SDavid du Colombier 	hint->type = type;
1251*593dc095SDavid du Colombier 	hint->g0 = hint->ag0 = g0;
1252*593dc095SDavid du Colombier 	hint->g1 = hint->ag1 = g1;
1253*593dc095SDavid du Colombier 	hint->aligned0 = hint->aligned1 = unaligned;
1254*593dc095SDavid du Colombier 	hint->q0 = hint->q1 = max_int;
1255*593dc095SDavid du Colombier 	hint->b0 = hint->b1 = false;
1256*593dc095SDavid du Colombier 	hint->stem3_index = stem3_index;
1257*593dc095SDavid du Colombier 	hint->range_index = -1;
1258*593dc095SDavid du Colombier 	hint->side_mask = side_mask;
1259*593dc095SDavid du Colombier     }
1260*593dc095SDavid du Colombier     code = t1_hinter__can_add_hint_range(this, &range);
1261*593dc095SDavid du Colombier     if (code < 0)
1262*593dc095SDavid du Colombier 	return code;
1263*593dc095SDavid du Colombier     range->contour_index = this->contour_count;
1264*593dc095SDavid du Colombier     range->beg_pole = this->pole_count;
1265*593dc095SDavid du Colombier     range->end_pole = -1;
1266*593dc095SDavid du Colombier     range->next = hint->range_index;
1267*593dc095SDavid du Colombier     hint->range_index = range - this->hint_range;
1268*593dc095SDavid du Colombier     if (i >= this->hint_count)
1269*593dc095SDavid du Colombier 	this->hint_count++;
1270*593dc095SDavid du Colombier     this->hint_range_count++;
1271*593dc095SDavid du Colombier     return 0;
1272*593dc095SDavid du Colombier }
1273*593dc095SDavid du Colombier 
t1_hinter__dotsection(t1_hinter * this)1274*593dc095SDavid du Colombier int t1_hinter__dotsection(t1_hinter * this)
1275*593dc095SDavid du Colombier {   if (this->pole_count == 0 || this->pole[this->pole_count - 1].type != moveto)
1276*593dc095SDavid du Colombier         return 0; /* We store beginning dotsection hints only. */
1277*593dc095SDavid du Colombier     if (this->disable_hinting)
1278*593dc095SDavid du Colombier 	return 0;
1279*593dc095SDavid du Colombier     return t1_hinter__stem(this, dot, 0, 0, 0, 0);
1280*593dc095SDavid du Colombier }
1281*593dc095SDavid du Colombier 
1282*593dc095SDavid du Colombier 
t1_hinter__hstem(t1_hinter * this,fixed x0,fixed x1)1283*593dc095SDavid du Colombier int t1_hinter__hstem(t1_hinter * this, fixed x0, fixed x1)
1284*593dc095SDavid du Colombier {   if (this->disable_hinting)
1285*593dc095SDavid du Colombier 	return 0;
1286*593dc095SDavid du Colombier     return t1_hinter__stem(this, hstem, 0, x0, x1, 3);
1287*593dc095SDavid du Colombier }
1288*593dc095SDavid du Colombier 
t1_hinter__overall_hstem(t1_hinter * this,fixed x0,fixed x1,int side_mask)1289*593dc095SDavid du Colombier int t1_hinter__overall_hstem(t1_hinter * this, fixed x0, fixed x1, int side_mask)
1290*593dc095SDavid du Colombier {   /* True Type autohinting only. */
1291*593dc095SDavid du Colombier     if (this->disable_hinting)
1292*593dc095SDavid du Colombier 	return 0;
1293*593dc095SDavid du Colombier     return t1_hinter__stem(this, hstem, 0, x0, x1, side_mask);
1294*593dc095SDavid du Colombier }
1295*593dc095SDavid du Colombier 
t1_hinter__vstem(t1_hinter * this,fixed y0,fixed y1)1296*593dc095SDavid du Colombier int t1_hinter__vstem(t1_hinter * this, fixed y0, fixed y1)
1297*593dc095SDavid du Colombier {   if (this->disable_hinting)
1298*593dc095SDavid du Colombier 	return 0;
1299*593dc095SDavid du Colombier     return t1_hinter__stem(this, vstem, 0, y0, y1, 3);
1300*593dc095SDavid du Colombier }
1301*593dc095SDavid du Colombier 
t1_hinter__hstem3(t1_hinter * this,fixed x0,fixed x1,fixed x2,fixed x3,fixed x4,fixed x5)1302*593dc095SDavid du Colombier int t1_hinter__hstem3(t1_hinter * this, fixed x0, fixed x1, fixed x2, fixed x3, fixed x4, fixed x5)
1303*593dc095SDavid du Colombier {   int code;
1304*593dc095SDavid du Colombier 
1305*593dc095SDavid du Colombier     if (this->disable_hinting)
1306*593dc095SDavid du Colombier 	return 0;
1307*593dc095SDavid du Colombier     code = t1_hinter__stem(this, hstem, 1, x0, x1, 3);
1308*593dc095SDavid du Colombier     if (code < 0)
1309*593dc095SDavid du Colombier 	return code;
1310*593dc095SDavid du Colombier     code = t1_hinter__stem(this, hstem, 2, x2, x3, 3);
1311*593dc095SDavid du Colombier     if (code < 0)
1312*593dc095SDavid du Colombier 	return code;
1313*593dc095SDavid du Colombier     return t1_hinter__stem(this, hstem, 3, x4, x5, 3);
1314*593dc095SDavid du Colombier }
1315*593dc095SDavid du Colombier 
t1_hinter__vstem3(t1_hinter * this,fixed y0,fixed y1,fixed y2,fixed y3,fixed y4,fixed y5)1316*593dc095SDavid du Colombier int t1_hinter__vstem3(t1_hinter * this, fixed y0, fixed y1, fixed y2, fixed y3, fixed y4, fixed y5)
1317*593dc095SDavid du Colombier {   int code;
1318*593dc095SDavid du Colombier 
1319*593dc095SDavid du Colombier     if (this->disable_hinting)
1320*593dc095SDavid du Colombier 	return 0;
1321*593dc095SDavid du Colombier     code = t1_hinter__stem(this, vstem, 1, y0, y1, 3);
1322*593dc095SDavid du Colombier     if (code < 0)
1323*593dc095SDavid du Colombier 	return code;
1324*593dc095SDavid du Colombier     code = t1_hinter__stem(this, vstem, 2, y2, y3, 3);
1325*593dc095SDavid du Colombier     if (code < 0)
1326*593dc095SDavid du Colombier 	return code;
1327*593dc095SDavid du Colombier     return t1_hinter__stem(this, vstem, 3, y4, y5, 3);
1328*593dc095SDavid du Colombier }
1329*593dc095SDavid du Colombier 
t1_hinter__endchar(t1_hinter * this,bool seac_flag)1330*593dc095SDavid du Colombier int t1_hinter__endchar(t1_hinter * this, bool seac_flag)
1331*593dc095SDavid du Colombier {   this->seac_flag = seac_flag;
1332*593dc095SDavid du Colombier     return 0;
1333*593dc095SDavid du Colombier }
1334*593dc095SDavid du Colombier 
1335*593dc095SDavid du Colombier /* --------------------- t1_hinter class members - accessories --------------------*/
1336*593dc095SDavid du Colombier 
t1_hinter__is_x_fitting(t1_hinter * this)1337*593dc095SDavid du Colombier int t1_hinter__is_x_fitting(t1_hinter * this)
1338*593dc095SDavid du Colombier {   return this->grid_fit_x;
1339*593dc095SDavid du Colombier }
1340*593dc095SDavid du Colombier 
1341*593dc095SDavid du Colombier /* --------------------- t1_hinter class members - the hinting --------------------*/
1342*593dc095SDavid du Colombier 
t1_hinter__segment_beg(t1_hinter * this,int pole_index)1343*593dc095SDavid du Colombier private inline int t1_hinter__segment_beg(t1_hinter * this, int pole_index)
1344*593dc095SDavid du Colombier {   int contour_index = this->pole[pole_index].contour_index;
1345*593dc095SDavid du Colombier     int beg_contour_pole = this->contour[contour_index];
1346*593dc095SDavid du Colombier     int end_contour_pole = this->contour[contour_index + 1] - 2;
1347*593dc095SDavid du Colombier     int prev = ranger_step_b(pole_index, beg_contour_pole, end_contour_pole);
1348*593dc095SDavid du Colombier 
1349*593dc095SDavid du Colombier     while (this->pole[prev].type == offcurve)
1350*593dc095SDavid du Colombier         prev = ranger_step_b(prev, beg_contour_pole, end_contour_pole);
1351*593dc095SDavid du Colombier     return prev;
1352*593dc095SDavid du Colombier }
1353*593dc095SDavid du Colombier 
t1_hinter__segment_end(t1_hinter * this,int pole_index)1354*593dc095SDavid du Colombier private inline int t1_hinter__segment_end(t1_hinter * this, int pole_index)
1355*593dc095SDavid du Colombier {   int contour_index = this->pole[pole_index].contour_index;
1356*593dc095SDavid du Colombier     int beg_contour_pole = this->contour[contour_index];
1357*593dc095SDavid du Colombier     int end_contour_pole = this->contour[contour_index + 1] - 2;
1358*593dc095SDavid du Colombier     int next = ranger_step_f(pole_index, beg_contour_pole, end_contour_pole);
1359*593dc095SDavid du Colombier 
1360*593dc095SDavid du Colombier     while (this->pole[next].type == offcurve)
1361*593dc095SDavid du Colombier         next = ranger_step_f(next, beg_contour_pole, end_contour_pole);
1362*593dc095SDavid du Colombier     return next;
1363*593dc095SDavid du Colombier }
1364*593dc095SDavid du Colombier 
t1_hinter__compute_y_span(t1_hinter * this)1365*593dc095SDavid du Colombier private void t1_hinter__compute_y_span(t1_hinter * this)
1366*593dc095SDavid du Colombier {
1367*593dc095SDavid du Colombier     int n = this->pole_count - 1;
1368*593dc095SDavid du Colombier     int i;
1369*593dc095SDavid du Colombier 
1370*593dc095SDavid du Colombier     if (n > 1) {
1371*593dc095SDavid du Colombier 	/* For non-space characters ignore the trailing moveto.
1372*593dc095SDavid du Colombier 	   Rather it could give a baseline,
1373*593dc095SDavid du Colombier 	   it is not guaranteedly good,
1374*593dc095SDavid du Colombier 	   and doesn't allow a stable recognition
1375*593dc095SDavid du Colombier 	   of the upper side of a dot, comma, etc.. */
1376*593dc095SDavid du Colombier 	n--;
1377*593dc095SDavid du Colombier     }
1378*593dc095SDavid du Colombier     this->ymin = this->ymax = this->pole[0].gy;
1379*593dc095SDavid du Colombier     for (i = 0; i < n; i++) {
1380*593dc095SDavid du Colombier 	if (this->ymin > this->pole[i].gy)
1381*593dc095SDavid du Colombier 	    this->ymin = this->pole[i].gy;
1382*593dc095SDavid du Colombier 	if (this->ymax < this->pole[i].gy)
1383*593dc095SDavid du Colombier 	    this->ymax = this->pole[i].gy;
1384*593dc095SDavid du Colombier     }
1385*593dc095SDavid du Colombier     this->ymid = (this->ymax + this->ymin) / 2;
1386*593dc095SDavid du Colombier }
1387*593dc095SDavid du Colombier 
t1_hinter__simplify_representation(t1_hinter * this)1388*593dc095SDavid du Colombier private void t1_hinter__simplify_representation(t1_hinter * this)
1389*593dc095SDavid du Colombier {   int i, j;
1390*593dc095SDavid du Colombier 
1391*593dc095SDavid du Colombier     /*  moveto's were needed to decode path correctly.
1392*593dc095SDavid du Colombier         We don't need them so far.
1393*593dc095SDavid du Colombier         Replace 'moveto' with 'oncurve' :
1394*593dc095SDavid du Colombier     */
1395*593dc095SDavid du Colombier     if (this->pole_count <= 1) {
1396*593dc095SDavid du Colombier 	return; /* An empty glyph (only a trailing moveto). */
1397*593dc095SDavid du Colombier     }
1398*593dc095SDavid du Colombier     for (i = 0; i <= this->contour_count; i++)
1399*593dc095SDavid du Colombier         if (this->pole[this->contour[i]].type == moveto)
1400*593dc095SDavid du Colombier             this->pole[this->contour[i]].type = oncurve;
1401*593dc095SDavid du Colombier     /* Remove hints which are disabled with !grid_fit_x, !grid_fit_y.
1402*593dc095SDavid du Colombier      * We can't do before import is completed due to hint mask commands.
1403*593dc095SDavid du Colombier      */
1404*593dc095SDavid du Colombier     if (!this->grid_fit_x || !this->grid_fit_y) {
1405*593dc095SDavid du Colombier 	for (i = j = 0; i < this->hint_count; i++)
1406*593dc095SDavid du Colombier 	    if ((this->hint[i].type == vstem && !this->grid_fit_x) ||
1407*593dc095SDavid du Colombier 		(this->hint[i].type == hstem && !this->grid_fit_y)) {
1408*593dc095SDavid du Colombier 		continue; /* skip it. */
1409*593dc095SDavid du Colombier 	    } else {
1410*593dc095SDavid du Colombier 		this->hint[j] = this->hint[i];
1411*593dc095SDavid du Colombier 		j++;
1412*593dc095SDavid du Colombier 	    }
1413*593dc095SDavid du Colombier 	this->hint_count = j;
1414*593dc095SDavid du Colombier     }
1415*593dc095SDavid du Colombier }
1416*593dc095SDavid du Colombier 
t1_hinter__is_small_angle(t1_hinter * this,int pole_index0,int pole_index1,long tan_x,long tan_y,int alpha,int alpha_div,int * quality)1417*593dc095SDavid du Colombier private inline bool t1_hinter__is_small_angle(t1_hinter * this, int pole_index0, int pole_index1,
1418*593dc095SDavid du Colombier 	long tan_x, long tan_y, int alpha, int alpha_div, int *quality)
1419*593dc095SDavid du Colombier {   long gx = this->pole[pole_index1].gx - this->pole[pole_index0].gx;
1420*593dc095SDavid du Colombier     long gy = this->pole[pole_index1].gy - this->pole[pole_index0].gy;
1421*593dc095SDavid du Colombier     long vp = mul_shift(gx, tan_y, _fixed_shift) - mul_shift(gy, tan_x, _fixed_shift);
1422*593dc095SDavid du Colombier     long sp = mul_shift(gx, tan_x, _fixed_shift) + mul_shift(gy, tan_y, _fixed_shift);
1423*593dc095SDavid du Colombier     long vp1 = any_abs(vp), sp1 = any_abs(sp);
1424*593dc095SDavid du Colombier 
1425*593dc095SDavid du Colombier     if (gx == 0 && gy == 0) {
1426*593dc095SDavid du Colombier 	*quality = max_int;
1427*593dc095SDavid du Colombier 	return false;
1428*593dc095SDavid du Colombier     }
1429*593dc095SDavid du Colombier     if (vp1 >= sp1) {
1430*593dc095SDavid du Colombier 	*quality = max_int;
1431*593dc095SDavid du Colombier 	return false;
1432*593dc095SDavid du Colombier     }
1433*593dc095SDavid du Colombier     if (vp1 / alpha_div > sp1 / alpha) {
1434*593dc095SDavid du Colombier 	*quality = max_int;
1435*593dc095SDavid du Colombier 	return false;
1436*593dc095SDavid du Colombier     }
1437*593dc095SDavid du Colombier     *quality = vp1 * 100 / sp1; /* The best quality is 0. */
1438*593dc095SDavid du Colombier     return true;
1439*593dc095SDavid du Colombier }
1440*593dc095SDavid du Colombier 
t1_hinter__is_conjugated(t1_hinter * this,int pole_index)1441*593dc095SDavid du Colombier private inline bool t1_hinter__is_conjugated(t1_hinter * this, int pole_index)
1442*593dc095SDavid du Colombier {   int prev = t1_hinter__segment_beg(this, pole_index);
1443*593dc095SDavid du Colombier     int next = t1_hinter__segment_end(this, pole_index);
1444*593dc095SDavid du Colombier     long gx0 = this->pole[prev].gx - this->pole[pole_index].gx;
1445*593dc095SDavid du Colombier     long gy0 = this->pole[prev].gy - this->pole[pole_index].gy;
1446*593dc095SDavid du Colombier     long gx1 = this->pole[next].gx - this->pole[pole_index].gx;
1447*593dc095SDavid du Colombier     long gy1 = this->pole[next].gy - this->pole[pole_index].gy;
1448*593dc095SDavid du Colombier     long vp = gx0 * gy1 - gy0 * gx1;
1449*593dc095SDavid du Colombier     long sp = gx0 * gy1 - gy0 * gx1;
1450*593dc095SDavid du Colombier 
1451*593dc095SDavid du Colombier     if (sp > 0)
1452*593dc095SDavid du Colombier         return false;
1453*593dc095SDavid du Colombier     if (vp == 0)
1454*593dc095SDavid du Colombier         return true;
1455*593dc095SDavid du Colombier     return any_abs(vp) < -sp / 1000; /* The threshold is taken from scratch. */
1456*593dc095SDavid du Colombier }
1457*593dc095SDavid du Colombier 
t1_hinter__next_contour_pole(t1_hinter * this,int pole_index)1458*593dc095SDavid du Colombier private inline bool t1_hinter__next_contour_pole(t1_hinter * this, int pole_index)
1459*593dc095SDavid du Colombier {   int contour_index = this->pole[pole_index].contour_index;
1460*593dc095SDavid du Colombier     int beg_contour_pole = this->contour[contour_index];
1461*593dc095SDavid du Colombier     int end_contour_pole = this->contour[contour_index + 1] - 2;
1462*593dc095SDavid du Colombier 
1463*593dc095SDavid du Colombier     return ranger_step_f(pole_index, beg_contour_pole, end_contour_pole);
1464*593dc095SDavid du Colombier }
1465*593dc095SDavid du Colombier 
t1_hinter__is_good_tangent(t1_hinter * this,int pole_index,long tan_x,long tan_y,int * quality)1466*593dc095SDavid du Colombier private inline bool t1_hinter__is_good_tangent(t1_hinter * this, int pole_index, long tan_x, long tan_y, int *quality)
1467*593dc095SDavid du Colombier {   int contour_index = this->pole[pole_index].contour_index;
1468*593dc095SDavid du Colombier     int beg_contour_pole = this->contour[contour_index];
1469*593dc095SDavid du Colombier     int end_contour_pole = this->contour[contour_index + 1] - 2, prev, next;
1470*593dc095SDavid du Colombier     int const alpha = 9, alpha_div = 10;
1471*593dc095SDavid du Colombier     int quality0, quality1;
1472*593dc095SDavid du Colombier     bool good0, good1;
1473*593dc095SDavid du Colombier 
1474*593dc095SDavid du Colombier     prev = ranger_step_b(pole_index, beg_contour_pole, end_contour_pole);
1475*593dc095SDavid du Colombier     good0 = t1_hinter__is_small_angle(this, prev, pole_index, tan_x, tan_y, alpha, alpha_div, &quality0);
1476*593dc095SDavid du Colombier     if (quality0 == 0) {
1477*593dc095SDavid du Colombier 	*quality = 0;
1478*593dc095SDavid du Colombier 	return true;
1479*593dc095SDavid du Colombier     }
1480*593dc095SDavid du Colombier     next = ranger_step_f(pole_index, beg_contour_pole, end_contour_pole);
1481*593dc095SDavid du Colombier     good1 = t1_hinter__is_small_angle(this, next, pole_index, tan_x, tan_y, alpha, alpha_div, &quality1);
1482*593dc095SDavid du Colombier     *quality = min(quality0, quality1);
1483*593dc095SDavid du Colombier     return good0 || good1;
1484*593dc095SDavid du Colombier }
1485*593dc095SDavid du Colombier 
t1_hinter__compute_type1_stem_ranges(t1_hinter * this)1486*593dc095SDavid du Colombier private void t1_hinter__compute_type1_stem_ranges(t1_hinter * this)
1487*593dc095SDavid du Colombier {   int j;
1488*593dc095SDavid du Colombier     int end_range_pole = this->pole_count - 3;
1489*593dc095SDavid du Colombier     int primary_hint_count = this->primary_hint_count;
1490*593dc095SDavid du Colombier 
1491*593dc095SDavid du Colombier     if (this->hint_count == 0)
1492*593dc095SDavid du Colombier 	return;
1493*593dc095SDavid du Colombier     if (primary_hint_count == -1)
1494*593dc095SDavid du Colombier 	primary_hint_count = this->hint_range_count;
1495*593dc095SDavid du Colombier     /*  After the decoding, hint commands refer to the last pole before HR occures.
1496*593dc095SDavid du Colombier 	Move pointers to the beginning segment pole, so as they
1497*593dc095SDavid du Colombier 	rerer to oncurve pole :
1498*593dc095SDavid du Colombier     */
1499*593dc095SDavid du Colombier     for (j = 0; j < this->hint_range_count; j++)
1500*593dc095SDavid du Colombier 	if (this->hint_range[j].beg_pole > this->contour[this->hint_range[j].contour_index])
1501*593dc095SDavid du Colombier 	    this->hint_range[j].beg_pole = t1_hinter__segment_beg(this, this->hint_range[j].beg_pole);
1502*593dc095SDavid du Colombier     /* Process primary hints - ranges are entire glyph : */
1503*593dc095SDavid du Colombier     for(j = 0; j < primary_hint_count; j++)      {
1504*593dc095SDavid du Colombier         this->hint_range[j].beg_pole = 0;
1505*593dc095SDavid du Colombier         this->hint_range[j].end_pole = end_range_pole;
1506*593dc095SDavid du Colombier     }
1507*593dc095SDavid du Colombier     /* Process secondary hints - ranges until HR or until contour end : */
1508*593dc095SDavid du Colombier     for(; j < this->hint_range_count; j++)      {
1509*593dc095SDavid du Colombier         if (this->hint_range[j].end_pole == -1)
1510*593dc095SDavid du Colombier 	    this->hint_range[j].end_pole = this->contour[this->hint_range[j].contour_index + 1] - 1;
1511*593dc095SDavid du Colombier     }
1512*593dc095SDavid du Colombier     /*  Note that ranges of primary hints may include a tail of the hint array
1513*593dc095SDavid du Colombier         due to multiple contours. Primary hints have a lesser priority,
1514*593dc095SDavid du Colombier 	so apply them first, and possibly recover later.
1515*593dc095SDavid du Colombier     */
1516*593dc095SDavid du Colombier }
1517*593dc095SDavid du Colombier 
t1_hinter__compute_type2_stem_ranges(t1_hinter * this)1518*593dc095SDavid du Colombier private void t1_hinter__compute_type2_stem_ranges(t1_hinter * this)
1519*593dc095SDavid du Colombier {   int i;
1520*593dc095SDavid du Colombier 
1521*593dc095SDavid du Colombier     for (i = 0; i < this->hint_range_count; i++)
1522*593dc095SDavid du Colombier 	if (this->hint_range[i].end_pole == -1)
1523*593dc095SDavid du Colombier 	    this->hint_range[i].end_pole = this->pole_count - 2;
1524*593dc095SDavid du Colombier }
1525*593dc095SDavid du Colombier 
t1_hinter__is_stem_boundary_near(t1_hinter * this,const t1_hint * hint,t1_glyph_space_coord g,int boundary)1526*593dc095SDavid du Colombier private bool t1_hinter__is_stem_boundary_near(t1_hinter * this, const t1_hint *hint,
1527*593dc095SDavid du Colombier 		t1_glyph_space_coord g, int boundary)
1528*593dc095SDavid du Colombier {
1529*593dc095SDavid du Colombier     t1_glyph_space_coord const fuzz = this->blue_fuzz; /* comparefiles/tpc2.ps */
1530*593dc095SDavid du Colombier 
1531*593dc095SDavid du Colombier     return any_abs(g - (boundary ? hint->g1 : hint->g0)) <= fuzz;
1532*593dc095SDavid du Colombier }
1533*593dc095SDavid du Colombier 
t1_hinter__is_stem_hint_applicable(t1_hinter * this,t1_hint * hint,int pole_index,int * quality)1534*593dc095SDavid du Colombier private int t1_hinter__is_stem_hint_applicable(t1_hinter * this, t1_hint *hint, int pole_index, int *quality)
1535*593dc095SDavid du Colombier {   /* We don't check hint->side_mask because the unused coord should be outside the design bbox. */
1536*593dc095SDavid du Colombier     int k;
1537*593dc095SDavid du Colombier 
1538*593dc095SDavid du Colombier     if (hint->type == hstem
1539*593dc095SDavid du Colombier 	    && ((k = 1, t1_hinter__is_stem_boundary_near(this, hint, this->pole[pole_index].gy, 0)) ||
1540*593dc095SDavid du Colombier 	        (k = 2, t1_hinter__is_stem_boundary_near(this, hint, this->pole[pole_index].gy, 1)))
1541*593dc095SDavid du Colombier             && t1_hinter__is_good_tangent(this, pole_index, 1, 0, quality))
1542*593dc095SDavid du Colombier         return k;
1543*593dc095SDavid du Colombier     if (hint->type == vstem
1544*593dc095SDavid du Colombier 	    && ((k = 1, t1_hinter__is_stem_boundary_near(this, hint, this->pole[pole_index].gx, 0)) ||
1545*593dc095SDavid du Colombier 	        (k = 2, t1_hinter__is_stem_boundary_near(this, hint, this->pole[pole_index].gx, 1)))
1546*593dc095SDavid du Colombier             && t1_hinter__is_good_tangent(this, pole_index, 0, 1, quality))
1547*593dc095SDavid du Colombier         return k;
1548*593dc095SDavid du Colombier     return 0;
1549*593dc095SDavid du Colombier }
1550*593dc095SDavid du Colombier 
t1_hinter__find_zone(t1_hinter * this,t1_glyph_space_coord pole_y,bool curve,bool convex,bool concave)1551*593dc095SDavid du Colombier private t1_zone * t1_hinter__find_zone(t1_hinter * this, t1_glyph_space_coord pole_y, bool curve, bool convex, bool concave)
1552*593dc095SDavid du Colombier {   bool maybe_top = !curve || convex;
1553*593dc095SDavid du Colombier     bool maybe_bot = !curve || concave;
1554*593dc095SDavid du Colombier     int i;
1555*593dc095SDavid du Colombier 
1556*593dc095SDavid du Colombier     for (i = 0; i < this->zone_count; i++) {
1557*593dc095SDavid du Colombier         t1_zone *zone = &this->zone[i];
1558*593dc095SDavid du Colombier         if ((maybe_top && zone->type == topzone) || (maybe_bot && zone->type == botzone))
1559*593dc095SDavid du Colombier             if (zone->y_min <= pole_y && pole_y <= zone->y_max)
1560*593dc095SDavid du Colombier                 return zone;
1561*593dc095SDavid du Colombier     }
1562*593dc095SDavid du Colombier     return NULL;
1563*593dc095SDavid du Colombier     /*todo: optimize narrowing the search range */
1564*593dc095SDavid du Colombier }
1565*593dc095SDavid du Colombier 
t1_hinter__align_to_grid__general(t1_hinter * this,int32_t unit,t1_glyph_space_coord gx,t1_glyph_space_coord gy,t1_hinter_space_coord * pdx,t1_hinter_space_coord * pdy,bool align_to_pixels,bool absolute)1566*593dc095SDavid du Colombier private void t1_hinter__align_to_grid__general(t1_hinter * this, int32_t unit,
1567*593dc095SDavid du Colombier 	    t1_glyph_space_coord gx, t1_glyph_space_coord gy,
1568*593dc095SDavid du Colombier 	    t1_hinter_space_coord *pdx, t1_hinter_space_coord *pdy,
1569*593dc095SDavid du Colombier 	    bool align_to_pixels, bool absolute)
1570*593dc095SDavid du Colombier {
1571*593dc095SDavid du Colombier     long div_x = rshift(unit, (align_to_pixels ? (int)this->log2_pixels_x : this->log2_subpixels_x));
1572*593dc095SDavid du Colombier     long div_y = rshift(unit, (align_to_pixels ? (int)this->log2_pixels_y : this->log2_subpixels_y));
1573*593dc095SDavid du Colombier     t1_hinter_space_coord ox, oy, dx, dy;
1574*593dc095SDavid du Colombier 
1575*593dc095SDavid du Colombier     g2o(this, gx, gy, &ox, &oy);
1576*593dc095SDavid du Colombier     if (absolute) {
1577*593dc095SDavid du Colombier 	ox += this->orig_ox;
1578*593dc095SDavid du Colombier 	oy += this->orig_oy;
1579*593dc095SDavid du Colombier     }
1580*593dc095SDavid du Colombier     dx = ox % div_x;
1581*593dc095SDavid du Colombier     dy = oy % div_y; /* So far dx and dy are 19 bits */
1582*593dc095SDavid du Colombier     if (dx > div_x / 2 )
1583*593dc095SDavid du Colombier         dx = - div_x + dx;
1584*593dc095SDavid du Colombier     else if (dx < - div_x / 2)
1585*593dc095SDavid du Colombier         dx = div_x + dx;
1586*593dc095SDavid du Colombier     if (dy > div_y / 2)
1587*593dc095SDavid du Colombier         dy = - div_y + dy;
1588*593dc095SDavid du Colombier     else if (dy < - div_y / 2)
1589*593dc095SDavid du Colombier         dy = div_y + dy;
1590*593dc095SDavid du Colombier     *pdx = dx;
1591*593dc095SDavid du Colombier     *pdy = dy;
1592*593dc095SDavid du Colombier }
1593*593dc095SDavid du Colombier 
t1_hinter__align_to_grid__final(t1_hinter * this,t1_glyph_space_coord * x,t1_glyph_space_coord * y,t1_hinter_space_coord dx,t1_hinter_space_coord dy)1594*593dc095SDavid du Colombier private void t1_hinter__align_to_grid__final(t1_hinter * this,
1595*593dc095SDavid du Colombier 	    t1_glyph_space_coord *x, t1_glyph_space_coord *y,
1596*593dc095SDavid du Colombier 	    t1_hinter_space_coord dx, t1_hinter_space_coord dy)
1597*593dc095SDavid du Colombier {
1598*593dc095SDavid du Colombier     t1_glyph_space_coord gxd, gyd;
1599*593dc095SDavid du Colombier 
1600*593dc095SDavid du Colombier     o2g(this, dx, dy, &gxd, &gyd);
1601*593dc095SDavid du Colombier     if (this->grid_fit_x) {
1602*593dc095SDavid du Colombier 	*x -= gxd;
1603*593dc095SDavid du Colombier 	*x = (*x + 7) & ~15; /* Round to suppress small noise : */
1604*593dc095SDavid du Colombier     }
1605*593dc095SDavid du Colombier     if (this->grid_fit_y) {
1606*593dc095SDavid du Colombier 	*y -= gyd;
1607*593dc095SDavid du Colombier 	*y = (*y + 7) & ~15; /* Round to suppress small noise : */
1608*593dc095SDavid du Colombier     }
1609*593dc095SDavid du Colombier }
1610*593dc095SDavid du Colombier 
t1_hinter__find_best_standard_width(t1_hinter * this,t1_glyph_space_coord w,bool horiz)1611*593dc095SDavid du Colombier private int t1_hinter__find_best_standard_width(t1_hinter * this, t1_glyph_space_coord w, bool horiz)
1612*593dc095SDavid du Colombier {   int k = (horiz ? 0 : 1), m = 0, i;
1613*593dc095SDavid du Colombier     long d = any_abs(this->stem_snap[k][0] - w);
1614*593dc095SDavid du Colombier 
1615*593dc095SDavid du Colombier     for (i = 1; i < this->stem_snap_count[k]; i++) {
1616*593dc095SDavid du Colombier         long dd = any_abs(this->stem_snap[k][i] - w);
1617*593dc095SDavid du Colombier 
1618*593dc095SDavid du Colombier         if(d > dd) {
1619*593dc095SDavid du Colombier             d = dd;
1620*593dc095SDavid du Colombier             m = i;
1621*593dc095SDavid du Colombier         }
1622*593dc095SDavid du Colombier     }
1623*593dc095SDavid du Colombier     return 0;
1624*593dc095SDavid du Colombier }
1625*593dc095SDavid du Colombier 
t1_hinter__align_to_grid(t1_hinter * this,int32_t unit,t1_glyph_space_coord * x,t1_glyph_space_coord * y,bool align_to_pixels)1626*593dc095SDavid du Colombier private void t1_hinter__align_to_grid(t1_hinter * this, int32_t unit,
1627*593dc095SDavid du Colombier 	    t1_glyph_space_coord *x, t1_glyph_space_coord *y, bool align_to_pixels)
1628*593dc095SDavid du Colombier {   if (unit > 0) {
1629*593dc095SDavid du Colombier         t1_hinter_space_coord dx, dy;
1630*593dc095SDavid du Colombier 
1631*593dc095SDavid du Colombier 	t1_hinter__align_to_grid__general(this, unit, *x, *y, &dx, &dy, align_to_pixels, align_to_pixels);
1632*593dc095SDavid du Colombier 	t1_hinter__align_to_grid__final(this, x, y, dx, dy);
1633*593dc095SDavid du Colombier     }
1634*593dc095SDavid du Colombier }
1635*593dc095SDavid du Colombier 
t1_hinter__align_stem_width(t1_hinter * this,t1_glyph_space_coord * pgw,const t1_hint * hint)1636*593dc095SDavid du Colombier private void t1_hinter__align_stem_width(t1_hinter * this, t1_glyph_space_coord *pgw, const t1_hint *hint)
1637*593dc095SDavid du Colombier {   /* fixme : optimize : move pixel_o_x, pixel_o_y, pixel_gw, pixel_gh to t1_hinter_s. */
1638*593dc095SDavid du Colombier     int32_t pixel_o_x = rshift(this->g2o_fraction, (this->align_to_pixels ? (int)this->log2_pixels_x : this->log2_subpixels_x));
1639*593dc095SDavid du Colombier     int32_t pixel_o_y = rshift(this->g2o_fraction, (this->align_to_pixels ? (int)this->log2_pixels_y : this->log2_subpixels_y));
1640*593dc095SDavid du Colombier #if OPPOSITE_STEM_COORD_BUG_FIX
1641*593dc095SDavid du Colombier     t1_glyph_space_coord pixel_gw = any_abs(o2g_dist(this, pixel_o_x, this->heigt_transform_coef_inv));
1642*593dc095SDavid du Colombier     t1_glyph_space_coord pixel_gh = any_abs(o2g_dist(this, pixel_o_y, this->width_transform_coef_inv));
1643*593dc095SDavid du Colombier #else
1644*593dc095SDavid du Colombier     t1_glyph_space_coord pixel_gh = any_abs(o2g_dist(this, pixel_o_x, this->heigt_transform_coef_inv));
1645*593dc095SDavid du Colombier     t1_glyph_space_coord pixel_gw = any_abs(o2g_dist(this, pixel_o_y, this->width_transform_coef_inv));
1646*593dc095SDavid du Colombier #endif
1647*593dc095SDavid du Colombier     bool horiz = (hint->type == hstem);
1648*593dc095SDavid du Colombier     t1_glyph_space_coord pixel_g = (horiz ? pixel_gh : pixel_gw);
1649*593dc095SDavid du Colombier     int19 cf = (horiz ? this->heigt_transform_coef_rat : this->width_transform_coef_rat);
1650*593dc095SDavid du Colombier     int19 ci = (horiz ? this->heigt_transform_coef_inv : this->width_transform_coef_inv);
1651*593dc095SDavid du Colombier     /* Note : cf, ci ignore the sign of the transform. */
1652*593dc095SDavid du Colombier     t1_glyph_space_coord gw = *pgw;
1653*593dc095SDavid du Colombier     int j;
1654*593dc095SDavid du Colombier 
1655*593dc095SDavid du Colombier     if (this->keep_stem_width && cf != 0 && ci != 0) {
1656*593dc095SDavid du Colombier 	fixed pixel_o = (this->transposed ^ horiz ? pixel_o_y : pixel_o_x);
1657*593dc095SDavid du Colombier         t1_hinter_space_coord ow = g2o_dist(gw, cf);
1658*593dc095SDavid du Colombier         int19 e = ow % pixel_o; /* Pixel rounding error */
1659*593dc095SDavid du Colombier         t1_glyph_space_coord ge0 = o2g_dist(this, -e, ci);
1660*593dc095SDavid du Colombier         t1_glyph_space_coord ge1 = o2g_dist(this, pixel_o - e, ci);
1661*593dc095SDavid du Colombier         t1_glyph_space_coord ww;
1662*593dc095SDavid du Colombier 
1663*593dc095SDavid du Colombier         if (ow < pixel_o)
1664*593dc095SDavid du Colombier             ge0 = ge1; /* Never round to zero */
1665*593dc095SDavid du Colombier         ww = gw + (e < pixel_o / 2 ? ge0 : ge1);
1666*593dc095SDavid du Colombier         if (this->stem_snap_count[horiz ? 0 : 1] != 0) {
1667*593dc095SDavid du Colombier             /* Try choosing standard stem width : */
1668*593dc095SDavid du Colombier             /* todo: optimize: sort StdW for faster search; don't lookup StdW if obviousely inapplicable. */
1669*593dc095SDavid du Colombier             t1_glyph_space_coord d = pixel_g;
1670*593dc095SDavid du Colombier             int stdw_index0 = t1_hinter__find_best_standard_width(this, gw + ge0, horiz);
1671*593dc095SDavid du Colombier             int stdw_index1 = t1_hinter__find_best_standard_width(this, gw + ge1, horiz);
1672*593dc095SDavid du Colombier             t1_glyph_space_coord w0 = this->stem_snap[horiz ? 0 : 1][stdw_index0];
1673*593dc095SDavid du Colombier             t1_glyph_space_coord w1 = this->stem_snap[horiz ? 0 : 1][stdw_index1];
1674*593dc095SDavid du Colombier             t1_glyph_space_coord thr0 = pixel_g * 70 / 100, thr1 = pixel_g * 35 / 100;
1675*593dc095SDavid du Colombier             t1_glyph_space_coord  W[4];
1676*593dc095SDavid du Colombier             t1_hinter_space_coord E[4];
1677*593dc095SDavid du Colombier             int k = 0;
1678*593dc095SDavid du Colombier 
1679*593dc095SDavid du Colombier             if (gw - thr0 <= w0 && w0 <= gw + thr1) {
1680*593dc095SDavid du Colombier                 t1_hinter_space_coord ow0 = g2o_dist(w0, cf);
1681*593dc095SDavid du Colombier                 int19 e0 = ow0 % pixel_o;
1682*593dc095SDavid du Colombier 
1683*593dc095SDavid du Colombier                 W[0] = w0, W[1] = w0;
1684*593dc095SDavid du Colombier                 E[0]= - e0, E[1] = pixel_o - e0;
1685*593dc095SDavid du Colombier                 k=2;
1686*593dc095SDavid du Colombier             }
1687*593dc095SDavid du Colombier             if (stdw_index0 != stdw_index1 && gw - thr0 <= w1 && w1 <= gw + thr1) {
1688*593dc095SDavid du Colombier                 t1_hinter_space_coord ow1 = g2o_dist(w1, cf);
1689*593dc095SDavid du Colombier                 int19 e1 = ow1 % pixel_o;
1690*593dc095SDavid du Colombier 
1691*593dc095SDavid du Colombier                 W[k] = w1, W[k + 1] = w1;
1692*593dc095SDavid du Colombier                 E[k]= - e1, E[k + 1] = pixel_o - e1;
1693*593dc095SDavid du Colombier                 k+=2;
1694*593dc095SDavid du Colombier             }
1695*593dc095SDavid du Colombier             for (j = 0; j < k; j++) {
1696*593dc095SDavid du Colombier                 t1_glyph_space_coord D = o2g_dist(this, E[j], ci), DD = any_abs(D);
1697*593dc095SDavid du Colombier 
1698*593dc095SDavid du Colombier                 if (d >= DD && W[j] + D >= pixel_g) {
1699*593dc095SDavid du Colombier                     d = DD;
1700*593dc095SDavid du Colombier                     ww = W[j] + D;
1701*593dc095SDavid du Colombier                 }
1702*593dc095SDavid du Colombier             }
1703*593dc095SDavid du Colombier         }
1704*593dc095SDavid du Colombier         *pgw = ww;
1705*593dc095SDavid du Colombier     }
1706*593dc095SDavid du Colombier }
1707*593dc095SDavid du Colombier 
t1_hinter__align_stem_to_grid(t1_hinter * this,int32_t unit,t1_glyph_space_coord * x0,t1_glyph_space_coord * y0,t1_glyph_space_coord x1,t1_glyph_space_coord y1,bool align_to_pixels,const t1_hint * hint)1708*593dc095SDavid du Colombier private void t1_hinter__align_stem_to_grid(t1_hinter * this, int32_t unit,
1709*593dc095SDavid du Colombier 	    t1_glyph_space_coord *x0, t1_glyph_space_coord *y0,
1710*593dc095SDavid du Colombier 	    t1_glyph_space_coord  x1, t1_glyph_space_coord  y1,
1711*593dc095SDavid du Colombier 	    bool align_to_pixels, const t1_hint *hint)
1712*593dc095SDavid du Colombier {   /* Implemented for Bug 687578 "T1 hinter disturbs stem width". */
1713*593dc095SDavid du Colombier     /* fixme: optimize. */
1714*593dc095SDavid du Colombier     if (unit > 0) {
1715*593dc095SDavid du Colombier 	bool horiz = (hint->type == hstem);
1716*593dc095SDavid du Colombier 	t1_glyph_space_coord gw = (horiz ? y1 - *y0 : x1 - *x0);
1717*593dc095SDavid du Colombier 	t1_glyph_space_coord GW = any_abs(gw), GW0 = GW;
1718*593dc095SDavid du Colombier 	bool positive = (gw >= 0);
1719*593dc095SDavid du Colombier 	int19 cf = (horiz ? this->heigt_transform_coef_rat : this->width_transform_coef_rat);
1720*593dc095SDavid du Colombier         t1_hinter_space_coord dx0, dy0, dx1, dy1, dgw;
1721*593dc095SDavid du Colombier 
1722*593dc095SDavid du Colombier 	t1_hinter__align_to_grid__general(this, unit, *x0, *y0, &dx0, &dy0, align_to_pixels, align_to_pixels);
1723*593dc095SDavid du Colombier 	t1_hinter__align_to_grid__general(this, unit,  x1,  y1, &dx1, &dy1, align_to_pixels, align_to_pixels);
1724*593dc095SDavid du Colombier 	t1_hinter__align_stem_width(this, &GW, hint);
1725*593dc095SDavid du Colombier 	dgw = g2o_dist(GW - GW0, cf);
1726*593dc095SDavid du Colombier 	if ((horiz ? this->ctmf.yy : this->ctmf.xx) < 0)
1727*593dc095SDavid du Colombier 	    dgw = - dgw;
1728*593dc095SDavid du Colombier 	if (horiz) {
1729*593dc095SDavid du Colombier 	    t1_hinter_space_coord ddy1 = (positive ? dy0 - dgw : dy0 + dgw);
1730*593dc095SDavid du Colombier 	    t1_hinter_space_coord ddy0 = (positive ? dy1 + dgw : dy1 - dgw);
1731*593dc095SDavid du Colombier 
1732*593dc095SDavid du Colombier 	    if (any_abs(dy0 + ddy1) > any_abs(dy1 + ddy0))
1733*593dc095SDavid du Colombier 		dy0 = ddy0;
1734*593dc095SDavid du Colombier 	} else {
1735*593dc095SDavid du Colombier 	    t1_hinter_space_coord ddx1 = (positive ? dx0 - dgw : dx0 + dgw);
1736*593dc095SDavid du Colombier 	    t1_hinter_space_coord ddx0 = (positive ? dx1 + dgw : dx1 - dgw);
1737*593dc095SDavid du Colombier 
1738*593dc095SDavid du Colombier 	    if (any_abs(dx0 + ddx1) > any_abs(dx1 + ddx0))
1739*593dc095SDavid du Colombier 		dx0 = ddx0;
1740*593dc095SDavid du Colombier 	}
1741*593dc095SDavid du Colombier 	t1_hinter__align_to_grid__final(this, x0, y0, dx0, dy0);
1742*593dc095SDavid du Colombier     }
1743*593dc095SDavid du Colombier }
1744*593dc095SDavid du Colombier 
1745*593dc095SDavid du Colombier #if ADOBE_OVERSHOOT_COMPATIBILIY
g2o_dist_blue(t1_hinter * h,t1_glyph_space_coord gw)1746*593dc095SDavid du Colombier private inline t1_hinter_space_coord g2o_dist_blue(t1_hinter * h, t1_glyph_space_coord gw)
1747*593dc095SDavid du Colombier {   double W = fixed2float(gw);
1748*593dc095SDavid du Colombier     double w = W * (h->resolution * h->font_size * h->base_font_scale - h->BlueScale) + 1;
1749*593dc095SDavid du Colombier 
1750*593dc095SDavid du Colombier     return (t1_hinter_space_coord)(w * h->g2o_fraction);
1751*593dc095SDavid du Colombier     /* todo : exclude floating point */
1752*593dc095SDavid du Colombier }
1753*593dc095SDavid du Colombier 
t1_hinter__add_overshoot(t1_hinter * this,t1_zone * zone,t1_glyph_space_coord * x,t1_glyph_space_coord * y)1754*593dc095SDavid du Colombier private void t1_hinter__add_overshoot(t1_hinter * this, t1_zone * zone, t1_glyph_space_coord * x, t1_glyph_space_coord * y)
1755*593dc095SDavid du Colombier {   t1_glyph_space_coord gy = *y;
1756*593dc095SDavid du Colombier     /* t1_glyph_space_coord gw = any_abs(zone->overshoot_y - zone->y); */
1757*593dc095SDavid du Colombier     t1_glyph_space_coord gw = any_abs(gy - zone->y);
1758*593dc095SDavid du Colombier     t1_hinter_space_coord ow = g2o_dist_blue(this, gw);
1759*593dc095SDavid du Colombier     t1_hinter_space_coord ow1 = ow / this->g2o_fraction * this->g2o_fraction;
1760*593dc095SDavid du Colombier     t1_glyph_space_coord gw1 = o2g_dist(this, ow1, this->heigt_transform_coef_inv);
1761*593dc095SDavid du Colombier 
1762*593dc095SDavid du Colombier     *y = zone->y + (zone->type == topzone ? gw1 : -gw1);
1763*593dc095SDavid du Colombier }
1764*593dc095SDavid du Colombier #endif
1765*593dc095SDavid du Colombier 
t1_hinter__compute_aligned_coord(t1_hinter * this,t1_glyph_space_coord * gc,int segment_index,fixed t,const t1_hint * hint,enum t1_align_type align0)1766*593dc095SDavid du Colombier private enum t1_align_type t1_hinter__compute_aligned_coord(t1_hinter * this,
1767*593dc095SDavid du Colombier 	    t1_glyph_space_coord * gc, int segment_index, fixed t, const t1_hint *hint,
1768*593dc095SDavid du Colombier 	    enum t1_align_type align0)
1769*593dc095SDavid du Colombier {   /* Returns true, if alignment zone is applied. */
1770*593dc095SDavid du Colombier     /* t is 0 or 0.5, and it is always 0 for curves. */
1771*593dc095SDavid du Colombier     bool horiz = (hint->type == hstem);
1772*593dc095SDavid du Colombier     enum t1_align_type align = align0;
1773*593dc095SDavid du Colombier     t1_glyph_space_coord gx = this->pole[segment_index].gx, gx0;
1774*593dc095SDavid du Colombier     t1_glyph_space_coord gy = this->pole[segment_index].gy, gy0;
1775*593dc095SDavid du Colombier     t1_glyph_space_coord gc0 = (horiz ? gy : gx);
1776*593dc095SDavid du Colombier     bool align_by_stem = ALIGN_BY_STEM_MIDDLE
1777*593dc095SDavid du Colombier 		&& align0 == unaligned	 /* Force aligning outer boundaries
1778*593dc095SDavid du Colombier 					    from the TT spot analyzer. */
1779*593dc095SDavid du Colombier 		&& hint->b0 && hint->b1; /* It's a real stem. Contrary
1780*593dc095SDavid du Colombier 					    033-52-5873.pdf uses single hint boundaries
1781*593dc095SDavid du Colombier 					    to mark top|bottom sides of a glyph,
1782*593dc095SDavid du Colombier 					    but their opposite boundaries are dummy coordinates,
1783*593dc095SDavid du Colombier 					    which don't correspond to poles. */
1784*593dc095SDavid du Colombier 
1785*593dc095SDavid du Colombier     /*  Compute point of specified segment by parameter t : */
1786*593dc095SDavid du Colombier     if (t) {
1787*593dc095SDavid du Colombier         int next = t1_hinter__segment_end(this, segment_index);
1788*593dc095SDavid du Colombier         t1_glyph_space_coord gx1 = this->pole[next].gx;
1789*593dc095SDavid du Colombier         t1_glyph_space_coord gy1 = this->pole[next].gy;
1790*593dc095SDavid du Colombier 
1791*593dc095SDavid du Colombier         gx = (gx + gx1) / 2;
1792*593dc095SDavid du Colombier         gy = (gy + gy1) / 2;
1793*593dc095SDavid du Colombier     }
1794*593dc095SDavid du Colombier     gx0 = gx;
1795*593dc095SDavid du Colombier     gy0 = gy;
1796*593dc095SDavid du Colombier     vd_circle(gx, gy, 7, RGB(255,0,0));
1797*593dc095SDavid du Colombier     if (horiz) {
1798*593dc095SDavid du Colombier         t1_pole * pole = &this->pole[segment_index];
1799*593dc095SDavid du Colombier         int contour_index = pole->contour_index;
1800*593dc095SDavid du Colombier         int beg_contour_pole = this->contour[contour_index];
1801*593dc095SDavid du Colombier         int end_contour_pole = this->contour[contour_index + 1] - 2;
1802*593dc095SDavid du Colombier         int prev1 = ranger_step_b(segment_index, beg_contour_pole, end_contour_pole);
1803*593dc095SDavid du Colombier         int prev2 = ranger_step_b(prev1        , beg_contour_pole, end_contour_pole);
1804*593dc095SDavid du Colombier         int next1 = ranger_step_f(segment_index, beg_contour_pole, end_contour_pole);
1805*593dc095SDavid du Colombier         int next2 = ranger_step_f(next1        , beg_contour_pole, end_contour_pole);
1806*593dc095SDavid du Colombier         bool forwd_horiz = (any_abs(this->pole[next1].gy - pole->gy) <=
1807*593dc095SDavid du Colombier 		max(this->blue_fuzz, any_abs(this->pole[next1].gx - pole->gx) / 10));
1808*593dc095SDavid du Colombier         bool bckwd_horiz = (any_abs(this->pole[prev1].gy - pole->gy) <=
1809*593dc095SDavid du Colombier 		max(this->blue_fuzz, any_abs(this->pole[prev1].gx - pole->gx) / 10));
1810*593dc095SDavid du Colombier         bool maximum = (this->pole[next1].gy - pole->gy < 0 &&
1811*593dc095SDavid du Colombier 			this->pole[prev1].gy - pole->gy < 0);
1812*593dc095SDavid du Colombier         bool minimum = (this->pole[next1].gy - pole->gy > 0 &&
1813*593dc095SDavid du Colombier 			this->pole[prev1].gy - pole->gy > 0);
1814*593dc095SDavid du Colombier 
1815*593dc095SDavid du Colombier         if (forwd_horiz || bckwd_horiz || maximum || minimum) {
1816*593dc095SDavid du Colombier             bool forwd_curve = (this->pole[next1].type == offcurve);
1817*593dc095SDavid du Colombier             bool bckwd_curve = (this->pole[prev1].type == offcurve);
1818*593dc095SDavid du Colombier             bool curve = (bckwd_curve && forwd_curve);
1819*593dc095SDavid du Colombier             bool convex  = (curve && this->pole[prev2].gy <= pole->gy &&
1820*593dc095SDavid du Colombier                                      this->pole[next2].gy <= pole->gy);
1821*593dc095SDavid du Colombier             bool concave = (curve && this->pole[prev2].gy >= pole->gy &&
1822*593dc095SDavid du Colombier                                      this->pole[next2].gy >= pole->gy);
1823*593dc095SDavid du Colombier             t1_zone *zone = t1_hinter__find_zone(this, pole->gy, curve || maximum || minimum,
1824*593dc095SDavid du Colombier 						convex || maximum, concave || minimum);
1825*593dc095SDavid du Colombier 
1826*593dc095SDavid du Colombier             if (zone != NULL &&
1827*593dc095SDavid du Colombier 		   (forwd_horiz || bckwd_horiz ||
1828*593dc095SDavid du Colombier 		    (maximum && zone->type == topzone) ||
1829*593dc095SDavid du Colombier 		    (minimum && zone->type == botzone))) {
1830*593dc095SDavid du Colombier                 if (this->suppress_overshoots)
1831*593dc095SDavid du Colombier #                   if ADOBE_OVERSHOOT_COMPATIBILIY
1832*593dc095SDavid du Colombier                         gy = (zone->type == topzone ? zone->overshoot_y : zone->y);
1833*593dc095SDavid du Colombier #                   else
1834*593dc095SDavid du Colombier                         gy = zone->y;
1835*593dc095SDavid du Colombier #                   endif
1836*593dc095SDavid du Colombier                 else {
1837*593dc095SDavid du Colombier                     t1_glyph_space_coord s = zone->y - pole->gy;
1838*593dc095SDavid du Colombier                     if (zone->type == topzone)
1839*593dc095SDavid du Colombier                         s = -s;
1840*593dc095SDavid du Colombier                     if (!curve && s < this->overshoot_threshold)
1841*593dc095SDavid du Colombier                         gy = zone->y;
1842*593dc095SDavid du Colombier                     else if (s > this->overshoot_threshold) {
1843*593dc095SDavid du Colombier                         t1_glyph_space_coord ss = this->overshoot_threshold * 2;
1844*593dc095SDavid du Colombier 
1845*593dc095SDavid du Colombier                         if (s < ss) /* Enforce overshoot : */
1846*593dc095SDavid du Colombier                             gy = (zone->type == topzone ? zone->y + ss : zone->y - ss);
1847*593dc095SDavid du Colombier                         else {
1848*593dc095SDavid du Colombier #                           if ADOBE_OVERSHOOT_COMPATIBILIY
1849*593dc095SDavid du Colombier                                 t1_hinter__add_overshoot(this, zone, &gx, &gy);
1850*593dc095SDavid du Colombier #                           endif
1851*593dc095SDavid du Colombier                         }
1852*593dc095SDavid du Colombier 		    }
1853*593dc095SDavid du Colombier                 }
1854*593dc095SDavid du Colombier                 align = (zone->type == topzone ? topzn : botzn);
1855*593dc095SDavid du Colombier 		align_by_stem = false;
1856*593dc095SDavid du Colombier             }
1857*593dc095SDavid du Colombier         }
1858*593dc095SDavid du Colombier     }
1859*593dc095SDavid du Colombier     vd_circle(gx, gy, 7, RGB(0,255,0));
1860*593dc095SDavid du Colombier     if (align_by_stem) {
1861*593dc095SDavid du Colombier 	t1_glyph_space_coord gx1, gy1;
1862*593dc095SDavid du Colombier 
1863*593dc095SDavid du Colombier 	if (horiz) {
1864*593dc095SDavid du Colombier 	    bool b0 = t1_hinter__is_stem_boundary_near(this, hint, gy, 0);
1865*593dc095SDavid du Colombier 	    bool b1 = t1_hinter__is_stem_boundary_near(this, hint, gy, 1);
1866*593dc095SDavid du Colombier 
1867*593dc095SDavid du Colombier 	    gx1 = gx;
1868*593dc095SDavid du Colombier 	    if (b0 && !b1)
1869*593dc095SDavid du Colombier 		gy1 = hint->g1, align_by_stem = true;
1870*593dc095SDavid du Colombier 	    else if (!b0 && b1)
1871*593dc095SDavid du Colombier 		gy1 = hint->g0, align_by_stem = true;
1872*593dc095SDavid du Colombier 	    else
1873*593dc095SDavid du Colombier 		gy1 = 0; /* Quiet the compiler. */
1874*593dc095SDavid du Colombier 	} else {
1875*593dc095SDavid du Colombier 	    bool b0 = t1_hinter__is_stem_boundary_near(this, hint, gx, 0);
1876*593dc095SDavid du Colombier 	    bool b1 = t1_hinter__is_stem_boundary_near(this, hint, gx, 1);
1877*593dc095SDavid du Colombier 
1878*593dc095SDavid du Colombier 	    gy1 = gy;
1879*593dc095SDavid du Colombier 	    if (b0 && !b1)
1880*593dc095SDavid du Colombier 		gx1 = hint->g1, align_by_stem = true;
1881*593dc095SDavid du Colombier 	    else if (!b0 && b1)
1882*593dc095SDavid du Colombier 		gx1 = hint->g0, align_by_stem = true;
1883*593dc095SDavid du Colombier 	    else
1884*593dc095SDavid du Colombier 		gx1 = 0; /* Quiet the compiler. */
1885*593dc095SDavid du Colombier 	}
1886*593dc095SDavid du Colombier 	if (align_by_stem)
1887*593dc095SDavid du Colombier 	    t1_hinter__align_stem_to_grid(this, this->g2o_fraction, &gx, &gy, gx1, gy1,
1888*593dc095SDavid du Colombier 		    CONTRAST_STEMS || this->align_to_pixels, hint);
1889*593dc095SDavid du Colombier     }
1890*593dc095SDavid du Colombier     if (!align_by_stem)
1891*593dc095SDavid du Colombier 	t1_hinter__align_to_grid(this, this->g2o_fraction, &gx, &gy,
1892*593dc095SDavid du Colombier 			    CONTRAST_STEMS || this->align_to_pixels);
1893*593dc095SDavid du Colombier     vd_circle(gx, gy, 7, RGB(0,0,255));
1894*593dc095SDavid du Colombier     *gc = gc0 + (horiz ? gy - gy0 : gx - gx0);
1895*593dc095SDavid du Colombier     return (align == unaligned ? aligned : align);
1896*593dc095SDavid du Colombier }
1897*593dc095SDavid du Colombier 
t1_hinter__find_stem_middle(t1_hinter * this,fixed * t,int pole_index,bool horiz)1898*593dc095SDavid du Colombier private int t1_hinter__find_stem_middle(t1_hinter * this, fixed *t, int pole_index, bool horiz)
1899*593dc095SDavid du Colombier {   /* We assume proper glyphs, see Type 1 spec, chapter 4. */
1900*593dc095SDavid du Colombier     int next = t1_hinter__next_contour_pole(this, pole_index);
1901*593dc095SDavid du Colombier     const int alpha = 10;
1902*593dc095SDavid du Colombier     int quality;
1903*593dc095SDavid du Colombier     bool curve = this->pole[next].type == offcurve;
1904*593dc095SDavid du Colombier     bool continuing = (horiz ? t1_hinter__is_small_angle(this, next, pole_index, 1, 0, alpha, 1, &quality)
1905*593dc095SDavid du Colombier                              : t1_hinter__is_small_angle(this, next, pole_index, 0, 1, alpha, 1, &quality));
1906*593dc095SDavid du Colombier 
1907*593dc095SDavid du Colombier     *t = (!curve && continuing ? fixed_half : 0);
1908*593dc095SDavid du Colombier     return pole_index;
1909*593dc095SDavid du Colombier }
1910*593dc095SDavid du Colombier 
t1_hinter__skip_stem(t1_hinter * this,int pole_index,bool horiz)1911*593dc095SDavid du Colombier private int t1_hinter__skip_stem(t1_hinter * this, int pole_index, bool horiz)
1912*593dc095SDavid du Colombier {   /* We assume proper glyphs, see Type 1 spec, chapter 4. */
1913*593dc095SDavid du Colombier     int i = pole_index;
1914*593dc095SDavid du Colombier     int next_pole = t1_hinter__next_contour_pole(this, i);
1915*593dc095SDavid du Colombier     int next_segm = t1_hinter__segment_end(this, i);
1916*593dc095SDavid du Colombier     long tan_x = (horiz ? 1 : 0);
1917*593dc095SDavid du Colombier     long tan_y = (horiz ? 0 : 1);
1918*593dc095SDavid du Colombier     int quality;
1919*593dc095SDavid du Colombier 
1920*593dc095SDavid du Colombier     while (t1_hinter__is_small_angle(this, i, next_pole, tan_x, tan_y, 1000, 1, &quality) && /* The threshold is taken from scratch. */
1921*593dc095SDavid du Colombier            t1_hinter__is_small_angle(this, i, next_segm, tan_x, tan_y, 1000, 1, &quality)) {
1922*593dc095SDavid du Colombier         i = t1_hinter__segment_end(this, i);
1923*593dc095SDavid du Colombier 	if (i == pole_index) {
1924*593dc095SDavid du Colombier 	    /* An invalid glyph with <=2 segments in the contour with no angles. */
1925*593dc095SDavid du Colombier 	    break;
1926*593dc095SDavid du Colombier 	}
1927*593dc095SDavid du Colombier         next_pole = t1_hinter__next_contour_pole(this, i);
1928*593dc095SDavid du Colombier         next_segm = t1_hinter__segment_end(this, i);
1929*593dc095SDavid du Colombier     }
1930*593dc095SDavid du Colombier     return i;
1931*593dc095SDavid du Colombier }
1932*593dc095SDavid du Colombier 
t1_hinter__mark_existing_stems(t1_hinter * this)1933*593dc095SDavid du Colombier private void t1_hinter__mark_existing_stems(t1_hinter * this)
1934*593dc095SDavid du Colombier {   /* fixme: Duplicated code with t1_hinter__align_stem_commands. */
1935*593dc095SDavid du Colombier     int i, j, jj, k;
1936*593dc095SDavid du Colombier 
1937*593dc095SDavid du Colombier     for(i = 0; i < this->hint_count; i++)
1938*593dc095SDavid du Colombier         if (this->hint[i].type == vstem || this->hint[i].type == hstem)
1939*593dc095SDavid du Colombier 	    for (k = this->hint[i].range_index; k != -1; k = this->hint_range[k].next) {
1940*593dc095SDavid du Colombier 		int beg_range_pole = this->hint_range[k].beg_pole;
1941*593dc095SDavid du Colombier 		int end_range_pole = this->hint_range[k].end_pole;
1942*593dc095SDavid du Colombier 		int quality;
1943*593dc095SDavid du Colombier 
1944*593dc095SDavid du Colombier 		if (this->pole[beg_range_pole].type == closepath) {
1945*593dc095SDavid du Colombier 		    /* A workaround for a buggy font from the Bug 687393,
1946*593dc095SDavid du Colombier 		       which defines a range with 'closepath' only. */
1947*593dc095SDavid du Colombier 		    beg_range_pole++;
1948*593dc095SDavid du Colombier 		    if (beg_range_pole > end_range_pole)
1949*593dc095SDavid du Colombier 			continue;
1950*593dc095SDavid du Colombier 		}
1951*593dc095SDavid du Colombier 		for (j = beg_range_pole; j <= end_range_pole;) {
1952*593dc095SDavid du Colombier 		    int k = t1_hinter__is_stem_hint_applicable(this, &this->hint[i], j, &quality);
1953*593dc095SDavid du Colombier 		    if (k == 1)
1954*593dc095SDavid du Colombier 			this->hint[i].b0 = true;
1955*593dc095SDavid du Colombier 		    else if (k == 2)
1956*593dc095SDavid du Colombier 			this->hint[i].b1 = true;
1957*593dc095SDavid du Colombier 		    {   /* Step to the next pole in the range : */
1958*593dc095SDavid du Colombier 			jj = j;
1959*593dc095SDavid du Colombier 			j = t1_hinter__segment_end(this, j);
1960*593dc095SDavid du Colombier 			if (j <= jj) /* Rolled over contour end ? */
1961*593dc095SDavid du Colombier 			    j = this->contour[this->pole[j].contour_index + 1]; /* Go to the next contour. */
1962*593dc095SDavid du Colombier 		    }
1963*593dc095SDavid du Colombier 		}
1964*593dc095SDavid du Colombier 	    }
1965*593dc095SDavid du Colombier }
1966*593dc095SDavid du Colombier 
t1_hinter__align_stem_commands(t1_hinter * this)1967*593dc095SDavid du Colombier private void t1_hinter__align_stem_commands(t1_hinter * this)
1968*593dc095SDavid du Colombier {   int i, j, jj, k;
1969*593dc095SDavid du Colombier 
1970*593dc095SDavid du Colombier     for(i = 0; i < this->hint_count; i++)
1971*593dc095SDavid du Colombier         if (this->hint[i].type == vstem || this->hint[i].type == hstem)
1972*593dc095SDavid du Colombier 	    for (k = this->hint[i].range_index; k != -1; k = this->hint_range[k].next) {
1973*593dc095SDavid du Colombier 		int beg_range_pole = this->hint_range[k].beg_pole;
1974*593dc095SDavid du Colombier 		int end_range_pole = this->hint_range[k].end_pole;
1975*593dc095SDavid du Colombier 		bool horiz = (this->hint[i].type == hstem);
1976*593dc095SDavid du Colombier 		int quality = max_int;
1977*593dc095SDavid du Colombier 
1978*593dc095SDavid du Colombier 		if (this->pole[beg_range_pole].type == closepath) {
1979*593dc095SDavid du Colombier 		    /* A workaround for a buggy font from the Bug 687393,
1980*593dc095SDavid du Colombier 		       which defines a range with 'closepath' only. */
1981*593dc095SDavid du Colombier 		    beg_range_pole++;
1982*593dc095SDavid du Colombier 		    if (beg_range_pole > end_range_pole)
1983*593dc095SDavid du Colombier 			continue;
1984*593dc095SDavid du Colombier 		}
1985*593dc095SDavid du Colombier 		for (j = beg_range_pole; j <= end_range_pole;) {
1986*593dc095SDavid du Colombier 		    if (t1_hinter__is_stem_hint_applicable(this, &this->hint[i], j, &quality)) {
1987*593dc095SDavid du Colombier 			fixed t; /* Type 1 spec implies that it is 0 for curves, 0.5 for bars */
1988*593dc095SDavid du Colombier 			int segment_index = t1_hinter__find_stem_middle(this, &t, j, horiz);
1989*593dc095SDavid du Colombier 			t1_glyph_space_coord gc;
1990*593dc095SDavid du Colombier 			enum t1_align_type align = unaligned;
1991*593dc095SDavid du Colombier 
1992*593dc095SDavid du Colombier 			if (this->hint[i].side_mask != 3) {
1993*593dc095SDavid du Colombier 			    /* An overal hint from the True Type autohinter. */
1994*593dc095SDavid du Colombier 			    align = (this->hint[i].side_mask & 2 ? topzn : botzn);
1995*593dc095SDavid du Colombier 			} else if (this->autohinting && horiz) {
1996*593dc095SDavid du Colombier 			    if (this->pole[segment_index].gy == this->hint[i].g0)
1997*593dc095SDavid du Colombier #				if TT_AUTOHINT_TOPZONE_BUG_FIX
1998*593dc095SDavid du Colombier 				    align = (this->hint[i].g0 > this->hint[i].g1 ? topzn : botzn);
1999*593dc095SDavid du Colombier #				else
2000*593dc095SDavid du Colombier 				    align = (this->hint[i].g0 > this->hint[i].g0 ? topzn : botzn);
2001*593dc095SDavid du Colombier #				endif
2002*593dc095SDavid du Colombier 			}
2003*593dc095SDavid du Colombier 			align = t1_hinter__compute_aligned_coord(this, &gc,
2004*593dc095SDavid du Colombier 				    segment_index, t, &this->hint[i], align);
2005*593dc095SDavid du Colombier 			vd_square(this->pole[segment_index].gx, this->pole[segment_index].gy,
2006*593dc095SDavid du Colombier 				    (horiz ? 7 : 9), (i < this->primary_hint_count ? RGB(0,0,255) : RGB(0,255,0)));
2007*593dc095SDavid du Colombier 			/* todo: optimize: primary commands don't need to align, if suppressed by secondary ones. */
2008*593dc095SDavid du Colombier 			t1_hint__set_aligned_coord(&this->hint[i], gc, &this->pole[j], align, quality);
2009*593dc095SDavid du Colombier 			jj = j;
2010*593dc095SDavid du Colombier 			j = t1_hinter__skip_stem(this, j, horiz);
2011*593dc095SDavid du Colombier 			if (j < jj) { /* Rolled over contour end ? */
2012*593dc095SDavid du Colombier 			    j = this->contour[this->pole[j].contour_index + 1]; /* Go to the next contour. */
2013*593dc095SDavid du Colombier 			    continue;
2014*593dc095SDavid du Colombier 			}
2015*593dc095SDavid du Colombier 		    }
2016*593dc095SDavid du Colombier 		    {   /* Step to the next pole in the range : */
2017*593dc095SDavid du Colombier 			jj = j;
2018*593dc095SDavid du Colombier 			j = t1_hinter__segment_end(this, j);
2019*593dc095SDavid du Colombier 			if (j <= jj) /* Rolled over contour end ? */
2020*593dc095SDavid du Colombier 			    j = this->contour[this->pole[j].contour_index + 1]; /* Go to the next contour. */
2021*593dc095SDavid du Colombier 		    }
2022*593dc095SDavid du Colombier 		}
2023*593dc095SDavid du Colombier 	    }
2024*593dc095SDavid du Colombier }
2025*593dc095SDavid du Colombier 
t1_hinter__unfix_opposite_to_common(t1_hinter * this)2026*593dc095SDavid du Colombier private void t1_hinter__unfix_opposite_to_common(t1_hinter * this)
2027*593dc095SDavid du Colombier {    /* Implemented for Bug 687578 "T1 hinter disturbs stem width". */
2028*593dc095SDavid du Colombier     int i, j, k, m, n;
2029*593dc095SDavid du Colombier     t1_glyph_space_coord d, md;
2030*593dc095SDavid du Colombier     t1_glyph_space_coord *p_ci, *p_cj, *p_agj, agm;
2031*593dc095SDavid du Colombier     enum t1_align_type *p_aj, *p_ai, *p_oi, *p_oj, am;
2032*593dc095SDavid du Colombier 
2033*593dc095SDavid du Colombier     for (k = 0; k < 2; k++) { /* g0, g1 */
2034*593dc095SDavid du Colombier 	/* Since the number of stems in a complex is usually small,
2035*593dc095SDavid du Colombier 	   we don't care about redundant computations. */
2036*593dc095SDavid du Colombier 	for(i = 0; i < this->hint_count; i++) {
2037*593dc095SDavid du Colombier 	    if (this->hint[i].type == vstem || this->hint[i].type == hstem) {
2038*593dc095SDavid du Colombier 		p_ai = (!k ? &this->hint[i].aligned0 : &this->hint[i].aligned1);
2039*593dc095SDavid du Colombier 		p_oi = (!k ? &this->hint[i].aligned1 : &this->hint[i].aligned0);
2040*593dc095SDavid du Colombier 		if (*p_ai > weak && *p_ai == *p_oi) {
2041*593dc095SDavid du Colombier 		    p_ci = (!k ? &this->hint[i].g0 : &this->hint[i].g1);
2042*593dc095SDavid du Colombier 		    md = any_abs(this->hint[i].g1 - this->hint[i].g0);
2043*593dc095SDavid du Colombier 		    m = i;
2044*593dc095SDavid du Colombier 		    am = *p_ai;
2045*593dc095SDavid du Colombier 		    agm = (!k ? this->hint[m].ag0 : this->hint[m].ag1);
2046*593dc095SDavid du Colombier 		    n = 0;
2047*593dc095SDavid du Colombier 		    for(j = 0; j < this->hint_count; j++) {
2048*593dc095SDavid du Colombier 			if (j != i && this->hint[i].type == this->hint[j].type) {
2049*593dc095SDavid du Colombier 			    p_cj = (!k ? &this->hint[j].g0 : &this->hint[j].g1);
2050*593dc095SDavid du Colombier 			    if (*p_ci == *p_cj) {
2051*593dc095SDavid du Colombier 				n++;
2052*593dc095SDavid du Colombier 				p_aj = (!k ? &this->hint[j].aligned0 : &this->hint[j].aligned1);
2053*593dc095SDavid du Colombier 				d = any_abs(this->hint[j].g1 - this->hint[j].g0);
2054*593dc095SDavid du Colombier 				if (am < *p_aj) {
2055*593dc095SDavid du Colombier 				    md = d;
2056*593dc095SDavid du Colombier 				    m = j;
2057*593dc095SDavid du Colombier 				    am = *p_aj;
2058*593dc095SDavid du Colombier 				    agm = (!k ? this->hint[m].ag0 : this->hint[m].ag1);
2059*593dc095SDavid du Colombier 				} if (md < d) {
2060*593dc095SDavid du Colombier 				    md = d;
2061*593dc095SDavid du Colombier 				    m = j;
2062*593dc095SDavid du Colombier 				}
2063*593dc095SDavid du Colombier 			    }
2064*593dc095SDavid du Colombier 			}
2065*593dc095SDavid du Colombier 		    }
2066*593dc095SDavid du Colombier 		    if (n) {
2067*593dc095SDavid du Colombier 			for(j = 0; j < this->hint_count; j++) {
2068*593dc095SDavid du Colombier 			    p_cj = (!k ? &this->hint[j].g0 : &this->hint[j].g1);
2069*593dc095SDavid du Colombier 			    if (*p_ci == *p_cj) {
2070*593dc095SDavid du Colombier 				p_aj = (!k ? &this->hint[j].aligned0 : &this->hint[j].aligned1);
2071*593dc095SDavid du Colombier 				p_oj = (!k ? &this->hint[j].aligned1 : &this->hint[j].aligned0);
2072*593dc095SDavid du Colombier 				p_agj = (!k ? &this->hint[j].ag0 : &this->hint[j].ag1);
2073*593dc095SDavid du Colombier 				*p_aj = am;
2074*593dc095SDavid du Colombier 				if (*p_oj == aligned)
2075*593dc095SDavid du Colombier 				    *p_oj = weak;
2076*593dc095SDavid du Colombier 				*p_agj = agm;
2077*593dc095SDavid du Colombier 			    }
2078*593dc095SDavid du Colombier 			}
2079*593dc095SDavid du Colombier 		    }
2080*593dc095SDavid du Colombier 		}
2081*593dc095SDavid du Colombier 	    }
2082*593dc095SDavid du Colombier 	}
2083*593dc095SDavid du Colombier     }
2084*593dc095SDavid du Colombier }
2085*593dc095SDavid du Colombier 
t1_hinter__compute_opposite_stem_coords(t1_hinter * this)2086*593dc095SDavid du Colombier private void t1_hinter__compute_opposite_stem_coords(t1_hinter * this)
2087*593dc095SDavid du Colombier {   int i;
2088*593dc095SDavid du Colombier 
2089*593dc095SDavid du Colombier     for (i = 0; i < this->hint_count; i++)
2090*593dc095SDavid du Colombier         if ((this->hint[i].type == vstem || this->hint[i].type == hstem)) {
2091*593dc095SDavid du Colombier             t1_glyph_space_coord ag0 = this->hint[i].ag0;
2092*593dc095SDavid du Colombier             t1_glyph_space_coord ag1 = this->hint[i].ag1;
2093*593dc095SDavid du Colombier             enum t1_align_type aligned0 = this->hint[i].aligned0;
2094*593dc095SDavid du Colombier             enum t1_align_type aligned1 = this->hint[i].aligned1;
2095*593dc095SDavid du Colombier             t1_glyph_space_coord gw;
2096*593dc095SDavid du Colombier 
2097*593dc095SDavid du Colombier             gw = any_abs(this->hint[i].g1 - this->hint[i].g0);
2098*593dc095SDavid du Colombier 	    t1_hinter__align_stem_width(this, &gw, &this->hint[i]);
2099*593dc095SDavid du Colombier             if (this->hint[i].g1 - this->hint[i].g0 < 0)
2100*593dc095SDavid du Colombier                 gw = -gw;
2101*593dc095SDavid du Colombier             if (aligned0 > aligned1)
2102*593dc095SDavid du Colombier                 ag1 = ag0 + gw;
2103*593dc095SDavid du Colombier             else if (aligned0 < aligned1)
2104*593dc095SDavid du Colombier                 ag0 = ag1 - gw;
2105*593dc095SDavid du Colombier             else {
2106*593dc095SDavid du Colombier                 t1_glyph_space_coord d0 = any_abs(ag0 - this->hint[i].g0);
2107*593dc095SDavid du Colombier                 t1_glyph_space_coord d1 = any_abs(ag1 - this->hint[i].g1);
2108*593dc095SDavid du Colombier 
2109*593dc095SDavid du Colombier 		if (aligned0 == topzn || aligned1 == topzn)
2110*593dc095SDavid du Colombier 		    if (gw > 0)
2111*593dc095SDavid du Colombier 			ag0 = ag1 - gw;
2112*593dc095SDavid du Colombier 		    else
2113*593dc095SDavid du Colombier 			ag1 = ag0 + gw;
2114*593dc095SDavid du Colombier 		else if (aligned0 == botzn || aligned1 == botzn)
2115*593dc095SDavid du Colombier 		    if (gw < 0)
2116*593dc095SDavid du Colombier 			ag0 = ag1 - gw;
2117*593dc095SDavid du Colombier 		    else
2118*593dc095SDavid du Colombier 			ag1 = ag0 + gw;
2119*593dc095SDavid du Colombier 		else if (this->hint[i].type == hstem &&
2120*593dc095SDavid du Colombier 			min(any_abs(this->hint[i].g0 - this->ymid), any_abs(this->hint[i].g1 - this->ymid)) >
2121*593dc095SDavid du Colombier 			(this->ymax - this->ymin) / 5) {
2122*593dc095SDavid du Colombier 		    if ((this->hint[i].g1 + this->hint[i].g0) / 2 > this->ymid)
2123*593dc095SDavid du Colombier 			ag0 = ag1 - gw;
2124*593dc095SDavid du Colombier 		    else
2125*593dc095SDavid du Colombier 			ag1 = ag0 + gw;
2126*593dc095SDavid du Colombier 		} else {
2127*593dc095SDavid du Colombier 		    if (d0 < d1)
2128*593dc095SDavid du Colombier 			ag1 = ag0 + gw;
2129*593dc095SDavid du Colombier 		    else
2130*593dc095SDavid du Colombier 			ag0 = ag1 - gw;
2131*593dc095SDavid du Colombier 		}
2132*593dc095SDavid du Colombier             }
2133*593dc095SDavid du Colombier 	    this->hint[i].ag0 = ag0;
2134*593dc095SDavid du Colombier 	    this->hint[i].ag1 = ag1;
2135*593dc095SDavid du Colombier         }
2136*593dc095SDavid du Colombier }
2137*593dc095SDavid du Colombier 
t1_hinter__align_stem_poles(t1_hinter * this)2138*593dc095SDavid du Colombier private void t1_hinter__align_stem_poles(t1_hinter * this)
2139*593dc095SDavid du Colombier {   int i, j, k;
2140*593dc095SDavid du Colombier     t1_glyph_space_coord const fuzz = this->blue_fuzz; /* comparefiles/tpc2.ps */
2141*593dc095SDavid du Colombier 
2142*593dc095SDavid du Colombier     for (i = 0; i < this->hint_count; i++)
2143*593dc095SDavid du Colombier         if (this->hint[i].type == vstem || this->hint[i].type == hstem)
2144*593dc095SDavid du Colombier 	    for (k = this->hint[i].range_index; k != -1; k = this->hint_range[k].next) {
2145*593dc095SDavid du Colombier 		t1_hint * hint = &this->hint[i];
2146*593dc095SDavid du Colombier 		int beg_range_pole = this->hint_range[k].beg_pole;
2147*593dc095SDavid du Colombier 		int end_range_pole = this->hint_range[k].end_pole;
2148*593dc095SDavid du Colombier 		bool horiz = (hint->type == hstem);
2149*593dc095SDavid du Colombier 		t1_glyph_space_coord ag0 = this->hint[i].ag0, ag1 = this->hint[i].ag1;
2150*593dc095SDavid du Colombier 		enum t1_align_type aligned0 = hint->aligned0, aligned1 = hint->aligned1;
2151*593dc095SDavid du Colombier 
2152*593dc095SDavid du Colombier 		for (j = beg_range_pole; j <= end_range_pole; j++) {
2153*593dc095SDavid du Colombier 		    t1_pole * pole = &this->pole[j];
2154*593dc095SDavid du Colombier 
2155*593dc095SDavid du Colombier 		    if (pole->type != oncurve)
2156*593dc095SDavid du Colombier 			continue;
2157*593dc095SDavid du Colombier 		    if (!horiz && pole->aligned_x > aligned0 && any_abs(pole->gx - hint->g0) <= fuzz)
2158*593dc095SDavid du Colombier 			ag0 = pole->ax, aligned0 = pole->aligned_x;
2159*593dc095SDavid du Colombier 		    else if (!horiz && pole->aligned_x > aligned1 && any_abs(pole->gx - hint->g1) <= fuzz)
2160*593dc095SDavid du Colombier 			ag1 = pole->ax, aligned1 = pole->aligned_x;
2161*593dc095SDavid du Colombier 		    else if ( horiz && pole->aligned_y > aligned0 && any_abs(pole->gy - hint->g0) <= fuzz)
2162*593dc095SDavid du Colombier 			ag0 = pole->ay, aligned0 = pole->aligned_y;
2163*593dc095SDavid du Colombier 		    else if ( horiz && pole->aligned_y > aligned1 && any_abs(pole->gy - hint->g1) <= fuzz)
2164*593dc095SDavid du Colombier 			ag1 = pole->ay, aligned1 = pole->aligned_y;
2165*593dc095SDavid du Colombier 		    /* We could check for horizontality/verticality here,
2166*593dc095SDavid du Colombier 		       but some fonts have unaligned serifs.
2167*593dc095SDavid du Colombier 		    */
2168*593dc095SDavid du Colombier 		}
2169*593dc095SDavid du Colombier 		if (ag0 == hint->ag0 || ag1 == hint->ag1) {
2170*593dc095SDavid du Colombier 		    if (ag0 != hint->ag0)
2171*593dc095SDavid du Colombier 			ag1 += ag0 - hint->ag0;
2172*593dc095SDavid du Colombier 		    else
2173*593dc095SDavid du Colombier 			ag0 += ag1 - hint->ag1;
2174*593dc095SDavid du Colombier 		}
2175*593dc095SDavid du Colombier 		for (j = beg_range_pole; j <= end_range_pole; j++) {
2176*593dc095SDavid du Colombier 		    t1_pole * pole = &this->pole[j];
2177*593dc095SDavid du Colombier 
2178*593dc095SDavid du Colombier 		    if (pole->type != oncurve)
2179*593dc095SDavid du Colombier 			continue;
2180*593dc095SDavid du Colombier 		    if (!horiz && any_abs(pole->gx - hint->g0) <= fuzz)
2181*593dc095SDavid du Colombier 			pole->ax = ag0, pole->aligned_x = aligned0;
2182*593dc095SDavid du Colombier 		    else if (!horiz && any_abs(pole->gx - hint->g1) <= fuzz)
2183*593dc095SDavid du Colombier 			pole->ax = ag1, pole->aligned_x = aligned1;
2184*593dc095SDavid du Colombier 		    else if ( horiz && any_abs(pole->gy - hint->g0) <= fuzz)
2185*593dc095SDavid du Colombier 			pole->ay = ag0, pole->aligned_y = aligned0;
2186*593dc095SDavid du Colombier 		    else if ( horiz && any_abs(pole->gy - hint->g1) <= fuzz)
2187*593dc095SDavid du Colombier 			pole->ay = ag1, pole->aligned_y = aligned1;
2188*593dc095SDavid du Colombier 		}
2189*593dc095SDavid du Colombier 	    }
2190*593dc095SDavid du Colombier }
2191*593dc095SDavid du Colombier 
t1_hinter__find_vstem_by_center(t1_hinter * this,t1_glyph_space_coord gx)2192*593dc095SDavid du Colombier private t1_hint * t1_hinter__find_vstem_by_center(t1_hinter * this, t1_glyph_space_coord gx)
2193*593dc095SDavid du Colombier {   /* Find vstem with axis near gx : */
2194*593dc095SDavid du Colombier     int i;
2195*593dc095SDavid du Colombier     t1_hint * hint = NULL;
2196*593dc095SDavid du Colombier     t1_glyph_space_coord dx = fixed_1;
2197*593dc095SDavid du Colombier 
2198*593dc095SDavid du Colombier     for (i = 0; i < this->hint_count; i++)
2199*593dc095SDavid du Colombier         if (this->hint[i].type == vstem) {
2200*593dc095SDavid du Colombier             t1_glyph_space_coord d = any_abs(gx - (this->hint[i].ag0 + this->hint[i].ag1) / 2);
2201*593dc095SDavid du Colombier 
2202*593dc095SDavid du Colombier             if (dx > d) {
2203*593dc095SDavid du Colombier                 dx = d;
2204*593dc095SDavid du Colombier                 hint = &this->hint[i];
2205*593dc095SDavid du Colombier             }
2206*593dc095SDavid du Colombier         }
2207*593dc095SDavid du Colombier     return hint;
2208*593dc095SDavid du Colombier }
2209*593dc095SDavid du Colombier 
t1_hinter__process_dotsection(t1_hinter * this,int beg_pole,int end_pole)2210*593dc095SDavid du Colombier private void t1_hinter__process_dotsection(t1_hinter * this, int beg_pole, int end_pole)
2211*593dc095SDavid du Colombier {   /*  Since source outline must have oncurve poles at XY extremes,
2212*593dc095SDavid du Colombier         we compute bounding box from poles.
2213*593dc095SDavid du Colombier     */
2214*593dc095SDavid du Colombier     int i;
2215*593dc095SDavid du Colombier     t1_glyph_space_coord min_gx = this->pole[beg_pole].gx, min_gy = this->pole[beg_pole].gy;
2216*593dc095SDavid du Colombier     t1_glyph_space_coord max_gx = min_gx, max_gy = min_gy;
2217*593dc095SDavid du Colombier     t1_glyph_space_coord center_gx, center_gy, center_agx, center_agy;
2218*593dc095SDavid du Colombier     t1_glyph_space_coord sx, sy;
2219*593dc095SDavid du Colombier     bool aligned_min_x = false, aligned_min_y = false, aligned_max_x = false, aligned_max_y = false;
2220*593dc095SDavid du Colombier     bool aligned_x, aligned_y;
2221*593dc095SDavid du Colombier 
2222*593dc095SDavid du Colombier     for (i = beg_pole + 1; i <= end_pole; i++) {
2223*593dc095SDavid du Colombier         t1_glyph_space_coord gx = this->pole[i].gx, gy = this->pole[i].gy;
2224*593dc095SDavid du Colombier 
2225*593dc095SDavid du Colombier         min_gx = min(min_gx, gx);
2226*593dc095SDavid du Colombier         min_gy = min(min_gy, gy);
2227*593dc095SDavid du Colombier         max_gx = max(max_gx, gx);
2228*593dc095SDavid du Colombier         max_gy = max(max_gy, gy);
2229*593dc095SDavid du Colombier     }
2230*593dc095SDavid du Colombier     for (i = beg_pole; i <= end_pole; i++) {
2231*593dc095SDavid du Colombier         if (this->pole[i].gx == min_gx)
2232*593dc095SDavid du Colombier             aligned_min_x |= this->pole[i].aligned_x;
2233*593dc095SDavid du Colombier         if (this->pole[i].gy == min_gy)
2234*593dc095SDavid du Colombier             aligned_min_y |= this->pole[i].aligned_y;
2235*593dc095SDavid du Colombier         if (this->pole[i].gx == max_gx)
2236*593dc095SDavid du Colombier             aligned_max_x |= this->pole[i].aligned_x;
2237*593dc095SDavid du Colombier         if (this->pole[i].gy == max_gy)
2238*593dc095SDavid du Colombier             aligned_max_y |= this->pole[i].aligned_y;
2239*593dc095SDavid du Colombier     }
2240*593dc095SDavid du Colombier     aligned_x = aligned_min_x && aligned_max_x;
2241*593dc095SDavid du Colombier     aligned_y = aligned_min_y && aligned_max_y;
2242*593dc095SDavid du Colombier     if (aligned_x && aligned_y)
2243*593dc095SDavid du Colombier         return; /* The contour was aligned with stem commands - nothing to do. */
2244*593dc095SDavid du Colombier     center_gx = center_agx = (min_gx + max_gx) / 2;
2245*593dc095SDavid du Colombier     center_gy = center_agy = (min_gy + max_gy) / 2;
2246*593dc095SDavid du Colombier     vd_circle(center_agx, center_agy, 7, RGB(255,0,0));
2247*593dc095SDavid du Colombier     if (!aligned_x) {
2248*593dc095SDavid du Colombier         /* Heuristic : apply vstem if it is close to the center : */
2249*593dc095SDavid du Colombier         t1_hint * hint = t1_hinter__find_vstem_by_center(this, center_gx);
2250*593dc095SDavid du Colombier         if (hint != NULL) {
2251*593dc095SDavid du Colombier             center_agx = (hint->ag0 + hint->ag1) / 2; /* Align with vstem */
2252*593dc095SDavid du Colombier             aligned_x = true;
2253*593dc095SDavid du Colombier         }
2254*593dc095SDavid du Colombier     }
2255*593dc095SDavid du Colombier     vd_circle(center_agx, center_agy, 7, RGB(0,255,0));
2256*593dc095SDavid du Colombier     t1_hinter__align_to_grid(this, this->g2o_fraction / 2, &center_agx, &center_agy,
2257*593dc095SDavid du Colombier 				CONTRAST_STEMS || this->align_to_pixels);
2258*593dc095SDavid du Colombier     vd_circle(center_agx, center_agy, 7, RGB(0,0,255));
2259*593dc095SDavid du Colombier     sx = center_agx - center_gx;
2260*593dc095SDavid du Colombier     sy = center_agy - center_gy;
2261*593dc095SDavid du Colombier     if (aligned_x)
2262*593dc095SDavid du Colombier         sx = 0;
2263*593dc095SDavid du Colombier     if (aligned_y)
2264*593dc095SDavid du Colombier         sy = 0;
2265*593dc095SDavid du Colombier     /* Shift the contour (sets alignment flags to prevent interpolation) : */
2266*593dc095SDavid du Colombier     for (i = beg_pole; i <= end_pole; i++) {
2267*593dc095SDavid du Colombier         this->pole[i].ax = this->pole[i].gx + sx;
2268*593dc095SDavid du Colombier         this->pole[i].ay = this->pole[i].gy + sy;
2269*593dc095SDavid du Colombier         this->pole[i].aligned_x |= !aligned_x; /* Prevent interpolation if we aligned it here. */
2270*593dc095SDavid du Colombier         this->pole[i].aligned_y |= !aligned_y;
2271*593dc095SDavid du Colombier     }
2272*593dc095SDavid du Colombier }
2273*593dc095SDavid du Colombier 
t1_hinter__process_dotsections(t1_hinter * this)2274*593dc095SDavid du Colombier private void t1_hinter__process_dotsections(t1_hinter * this)
2275*593dc095SDavid du Colombier {   int i;
2276*593dc095SDavid du Colombier 
2277*593dc095SDavid du Colombier     for(i=0; i<this->hint_count; i++)
2278*593dc095SDavid du Colombier         if (this->hint[i].type == dot) {
2279*593dc095SDavid du Colombier             int contour_index = this->hint_range[this->hint[i].range_index].contour_index;
2280*593dc095SDavid du Colombier             int beg_pole = this->contour[contour_index];
2281*593dc095SDavid du Colombier             int end_pole = this->contour[contour_index] - 2;
2282*593dc095SDavid du Colombier 
2283*593dc095SDavid du Colombier             t1_hinter__process_dotsection(this, beg_pole, end_pole);
2284*593dc095SDavid du Colombier         }
2285*593dc095SDavid du Colombier }
2286*593dc095SDavid du Colombier 
t1_hinter__interpolate_other_poles(t1_hinter * this)2287*593dc095SDavid du Colombier private void t1_hinter__interpolate_other_poles(t1_hinter * this)
2288*593dc095SDavid du Colombier {   int i, j, k;
2289*593dc095SDavid du Colombier 
2290*593dc095SDavid du Colombier     for (k = 0; k<2; k++) { /* X, Y */
2291*593dc095SDavid du Colombier         t1_glyph_space_coord *p_gc = (!k ? &this->pole[0].gx : &this->pole[0].gy);
2292*593dc095SDavid du Colombier         t1_glyph_space_coord *p_wc = (!k ? &this->pole[0].gy : &this->pole[0].gx);
2293*593dc095SDavid du Colombier         t1_glyph_space_coord *p_ac = (!k ? &this->pole[0].ax : &this->pole[0].ay);
2294*593dc095SDavid du Colombier         enum t1_align_type *p_f = (!k ? &this->pole[0].aligned_x : &this->pole[0].aligned_y);
2295*593dc095SDavid du Colombier         int offset_gc = (char *)p_gc - (char *)&this->pole[0];
2296*593dc095SDavid du Colombier         int offset_wc = (char *)p_wc - (char *)&this->pole[0];
2297*593dc095SDavid du Colombier         int offset_ac = (char *)p_ac - (char *)&this->pole[0];
2298*593dc095SDavid du Colombier         int offset_f  = (char *)p_f -  (char *)&this->pole[0];
2299*593dc095SDavid du Colombier 
2300*593dc095SDavid du Colombier         for (i = 0; i < this->contour_count; i++) {
2301*593dc095SDavid du Colombier             int beg_contour_pole = this->contour[i];
2302*593dc095SDavid du Colombier             int end_contour_pole = this->contour[i + 1] - 2;
2303*593dc095SDavid du Colombier             int range_beg;
2304*593dc095SDavid du Colombier 
2305*593dc095SDavid du Colombier             for (j = beg_contour_pole; j <= end_contour_pole; j++)
2306*593dc095SDavid du Colombier                 if (*member_prt(enum t1_align_type, &this->pole[j], offset_f))
2307*593dc095SDavid du Colombier                     break;
2308*593dc095SDavid du Colombier             if (j > end_contour_pole)
2309*593dc095SDavid du Colombier                 continue;
2310*593dc095SDavid du Colombier             range_beg = j;
2311*593dc095SDavid du Colombier             do {
2312*593dc095SDavid du Colombier                 int start_pole = j, stop_pole = -1;
2313*593dc095SDavid du Colombier                 t1_glyph_space_coord min_a, max_a;
2314*593dc095SDavid du Colombier 		t1_glyph_space_coord min_g, max_g, g0, g1, a0, a1;
2315*593dc095SDavid du Colombier 		int min_i = start_pole, max_i = start_pole, cut_l, l;
2316*593dc095SDavid du Colombier 		bool moved = false;
2317*593dc095SDavid du Colombier 
2318*593dc095SDavid du Colombier 		do {
2319*593dc095SDavid du Colombier 		    int min_l = 0, max_l = 0, jp;
2320*593dc095SDavid du Colombier 		    int min_w, max_w, w0;
2321*593dc095SDavid du Colombier 
2322*593dc095SDavid du Colombier 		    g0 = *member_prt(t1_glyph_space_coord, &this->pole[start_pole], offset_gc);
2323*593dc095SDavid du Colombier 		    w0 = *member_prt(t1_glyph_space_coord, &this->pole[start_pole], offset_wc);
2324*593dc095SDavid du Colombier 		    a0 = *member_prt(t1_glyph_space_coord, &this->pole[start_pole], offset_ac);
2325*593dc095SDavid du Colombier 		    min_g = g0;
2326*593dc095SDavid du Colombier 		    max_g = g0;
2327*593dc095SDavid du Colombier 		    min_w = max_w = w0;
2328*593dc095SDavid du Colombier 		    jp = start_pole;
2329*593dc095SDavid du Colombier 		    for (j = ranger_step_f(start_pole,  beg_contour_pole, end_contour_pole), l = 1;
2330*593dc095SDavid du Colombier 			 j != start_pole;
2331*593dc095SDavid du Colombier 			 j = ranger_step_f(j,  beg_contour_pole, end_contour_pole), l++) {
2332*593dc095SDavid du Colombier 			t1_glyph_space_coord g = * member_prt(t1_glyph_space_coord, &this->pole[j], offset_gc);
2333*593dc095SDavid du Colombier 			t1_glyph_space_coord w = * member_prt(t1_glyph_space_coord, &this->pole[j], offset_wc);
2334*593dc095SDavid du Colombier 
2335*593dc095SDavid du Colombier 			if (min_g > g)
2336*593dc095SDavid du Colombier 			    min_g = g, min_i = j, min_l = l;
2337*593dc095SDavid du Colombier 			if (max_g < g)
2338*593dc095SDavid du Colombier 			    max_g = g, max_i = j, max_l = l;
2339*593dc095SDavid du Colombier 			if (min_w > w)
2340*593dc095SDavid du Colombier 			    min_w = w;
2341*593dc095SDavid du Colombier 			if (max_w < w)
2342*593dc095SDavid du Colombier 			    max_w = w;
2343*593dc095SDavid du Colombier 			if (*member_prt(enum t1_align_type, &this->pole[j], offset_f))
2344*593dc095SDavid du Colombier 			    break;
2345*593dc095SDavid du Colombier 			if (j == stop_pole)
2346*593dc095SDavid du Colombier 			    break;
2347*593dc095SDavid du Colombier 			jp = j;
2348*593dc095SDavid du Colombier 		    }
2349*593dc095SDavid du Colombier 		    stop_pole = j;
2350*593dc095SDavid du Colombier 		    cut_l = l;
2351*593dc095SDavid du Colombier 		    g1 = * member_prt(t1_glyph_space_coord, &this->pole[stop_pole], offset_gc);
2352*593dc095SDavid du Colombier 		    a1 = * member_prt(t1_glyph_space_coord, &this->pole[stop_pole], offset_ac);
2353*593dc095SDavid du Colombier 
2354*593dc095SDavid du Colombier 		    if (start_pole != stop_pole)
2355*593dc095SDavid du Colombier 			if (any_abs(g0 - g1) >= any_abs(a0 - a1) / 10)
2356*593dc095SDavid du Colombier 			    if (any_abs(max_g - min_g) <= any_abs(max_w - min_w) / 4)
2357*593dc095SDavid du Colombier 				break; /* OK to interpolate. */
2358*593dc095SDavid du Colombier 		    /* else break at an extremal pole : */
2359*593dc095SDavid du Colombier 		    if (min_i != start_pole && min_l < cut_l && min_g != g0 && min_g != g1)
2360*593dc095SDavid du Colombier 			stop_pole = min_i, cut_l = min_l;
2361*593dc095SDavid du Colombier 		    if (max_i != start_pole && max_l < cut_l && max_g != g0 && max_g != g1)
2362*593dc095SDavid du Colombier 			stop_pole = max_i, cut_l = max_l;
2363*593dc095SDavid du Colombier 		} while (cut_l < l);
2364*593dc095SDavid du Colombier                     /* Now start_pole and end_pole point to the contour interval to interpolate. */
2365*593dc095SDavid du Colombier 		if (g0 < g1) {
2366*593dc095SDavid du Colombier 		    min_g = g0;
2367*593dc095SDavid du Colombier 		    max_g = g1;
2368*593dc095SDavid du Colombier 		    min_a = a0;
2369*593dc095SDavid du Colombier 		    max_a = a1;
2370*593dc095SDavid du Colombier 		} else {
2371*593dc095SDavid du Colombier 		    min_g = g1;
2372*593dc095SDavid du Colombier 		    max_g = g0;
2373*593dc095SDavid du Colombier 		    min_a = a1;
2374*593dc095SDavid du Colombier 		    max_a = a0;
2375*593dc095SDavid du Colombier 		}
2376*593dc095SDavid du Colombier                 for (j = start_pole; ;
2377*593dc095SDavid du Colombier                      j = ranger_step_f(j,  beg_contour_pole, end_contour_pole)) {
2378*593dc095SDavid du Colombier                     t1_glyph_space_coord g = * member_prt(t1_glyph_space_coord, &this->pole[j], offset_gc);
2379*593dc095SDavid du Colombier 
2380*593dc095SDavid du Colombier                     if (g <= min_g)
2381*593dc095SDavid du Colombier                         * member_prt(t1_glyph_space_coord, &this->pole[j], offset_ac) =
2382*593dc095SDavid du Colombier 			* member_prt(t1_glyph_space_coord, &this->pole[j], offset_gc) + (min_a - min_g);
2383*593dc095SDavid du Colombier                     else if (g >= max_g)
2384*593dc095SDavid du Colombier                         * member_prt(t1_glyph_space_coord, &this->pole[j], offset_ac) =
2385*593dc095SDavid du Colombier 			* member_prt(t1_glyph_space_coord, &this->pole[j], offset_gc) + (max_a - max_g);
2386*593dc095SDavid du Colombier                     if(moved && j == stop_pole)
2387*593dc095SDavid du Colombier                         break;
2388*593dc095SDavid du Colombier 		    moved = true;
2389*593dc095SDavid du Colombier                 }
2390*593dc095SDavid du Colombier                 if (min_g < max_g) {
2391*593dc095SDavid du Colombier                     int24 div = max_g - min_g;
2392*593dc095SDavid du Colombier                     int24 mul = max_a - min_a;
2393*593dc095SDavid du Colombier                     /*  Due to glyph coordinate definition, div is not smaller than 2^12.
2394*593dc095SDavid du Colombier 
2395*593dc095SDavid du Colombier                         In the following cycle we need to compute x*mul/div for 24-bit integers,
2396*593dc095SDavid du Colombier                         We replace this expression with x*u/2^12 where u = mul*2^12/div
2397*593dc095SDavid du Colombier                         (note that it's an approximation with relative precision 2^-12).
2398*593dc095SDavid du Colombier 
2399*593dc095SDavid du Colombier                         If mul or div are big, we drop 5 bits to fit them into int19.
2400*593dc095SDavid du Colombier                         Note that it's another approximation with relative precision 2^-14.
2401*593dc095SDavid du Colombier                         Let now they are m0 and d.
2402*593dc095SDavid du Colombier 
2403*593dc095SDavid du Colombier                         Then we compute :
2404*593dc095SDavid du Colombier 
2405*593dc095SDavid du Colombier                         q1 = m0 / d, r1 = m0 % d, m1 = r1 << 12;   // r1 < 2^19, m0 < 2^12
2406*593dc095SDavid du Colombier                         q2 = m1 / d, r2 = m1 % d, m2 = r2 << 12;   // r2 < 2^19, m1 < 2^12
2407*593dc095SDavid du Colombier                         q3 = m2 / d, r3 = m2 % d, m3 = r3 << 12;   // r3 < 2^19, m2 < 2^12
2408*593dc095SDavid du Colombier                         and so on.
2409*593dc095SDavid du Colombier 
2410*593dc095SDavid du Colombier                         We have :
2411*593dc095SDavid du Colombier 
2412*593dc095SDavid du Colombier                         u = ((q1 + (q2 >> 12) + (q3 >> 24) + ...) << 12
2413*593dc095SDavid du Colombier                           = (q1 << 12) + q2 + (q3 >> 12) + ...
2414*593dc095SDavid du Colombier                           = (q1 << 12) + q2 .
2415*593dc095SDavid du Colombier 
2416*593dc095SDavid du Colombier                         Thus we got pretty nice formula without iterations. Implementing it below.
2417*593dc095SDavid du Colombier                     */
2418*593dc095SDavid du Colombier                     int24 m0 = mul, d = div, q1, q2, r1, m1, u;
2419*593dc095SDavid du Colombier 
2420*593dc095SDavid du Colombier                     if (m0 >= (1 << 19) || d >= (1 << 19))
2421*593dc095SDavid du Colombier                         m0 >>= 5, d >>= 5;
2422*593dc095SDavid du Colombier                     q1 = m0 / d, r1 = m0 % d, m1 = r1 << 12;
2423*593dc095SDavid du Colombier                     q2 = m1 / d;
2424*593dc095SDavid du Colombier                     u = (q1 << 12) + q2;
2425*593dc095SDavid du Colombier                     for (j = ranger_step_f(start_pole,  beg_contour_pole, end_contour_pole); j != stop_pole;
2426*593dc095SDavid du Colombier                          j = ranger_step_f(j,  beg_contour_pole, end_contour_pole)) {
2427*593dc095SDavid du Colombier                         t1_glyph_space_coord g = *member_prt(t1_glyph_space_coord, &this->pole[j], offset_gc);
2428*593dc095SDavid du Colombier 
2429*593dc095SDavid du Colombier                         if (min_g < g && g < max_g) {
2430*593dc095SDavid du Colombier                             t1_glyph_space_coord *a = member_prt(t1_glyph_space_coord, &this->pole[j], offset_ac);
2431*593dc095SDavid du Colombier                             t1_glyph_space_coord x = g - min_g;
2432*593dc095SDavid du Colombier                             t1_glyph_space_coord h = mul_shift(x, u, 12); /* It is x*u/2^12 */
2433*593dc095SDavid du Colombier 
2434*593dc095SDavid du Colombier                             /* h = (int24)(x * (double)mul / div + 0.5); Uncomment this to disable our tricks. */
2435*593dc095SDavid du Colombier                             *a = min_a + h;
2436*593dc095SDavid du Colombier                         }
2437*593dc095SDavid du Colombier                     }
2438*593dc095SDavid du Colombier                 }
2439*593dc095SDavid du Colombier                 j = stop_pole;
2440*593dc095SDavid du Colombier             } while (j != range_beg);
2441*593dc095SDavid du Colombier         }
2442*593dc095SDavid du Colombier     }
2443*593dc095SDavid du Colombier }
2444*593dc095SDavid du Colombier 
t1_hinter__export(t1_hinter * this)2445*593dc095SDavid du Colombier private int t1_hinter__export(t1_hinter * this)
2446*593dc095SDavid du Colombier {   int i, j, code;
2447*593dc095SDavid du Colombier     fixed fx, fy;
2448*593dc095SDavid du Colombier 
2449*593dc095SDavid du Colombier     for(i = 0; ; i++) {
2450*593dc095SDavid du Colombier         int beg_pole = this->contour[i];
2451*593dc095SDavid du Colombier         int end_pole = this->contour[i + 1] - 2;
2452*593dc095SDavid du Colombier         t1_pole *pole = & this->pole[beg_pole];
2453*593dc095SDavid du Colombier 
2454*593dc095SDavid du Colombier         g2d(this, pole->ax, pole->ay, &fx, &fy);
2455*593dc095SDavid du Colombier         code = gx_path_add_point(this->output_path, fx, fy);
2456*593dc095SDavid du Colombier 	if (code < 0)
2457*593dc095SDavid du Colombier 	    return code;
2458*593dc095SDavid du Colombier 	if (i >= this->contour_count)
2459*593dc095SDavid du Colombier 	    break;
2460*593dc095SDavid du Colombier 	vd_setcolor(RGB(255,0,0));
2461*593dc095SDavid du Colombier         vd_moveto(fx,fy);
2462*593dc095SDavid du Colombier         for(j = beg_pole + 1; j <= end_pole; j++) {
2463*593dc095SDavid du Colombier             pole = & this->pole[j];
2464*593dc095SDavid du Colombier             g2d(this, pole->ax, pole->ay, &fx, &fy);
2465*593dc095SDavid du Colombier             if (pole->type == oncurve) {
2466*593dc095SDavid du Colombier                 code = gx_path_add_line(this->output_path, fx, fy);
2467*593dc095SDavid du Colombier 		if (code < 0)
2468*593dc095SDavid du Colombier 		    return code;
2469*593dc095SDavid du Colombier                 vd_setcolor(RGB(255,0,0));
2470*593dc095SDavid du Colombier                 vd_lineto(fx,fy);
2471*593dc095SDavid du Colombier             } else {
2472*593dc095SDavid du Colombier                 int j1 = j + 1, j2 = (j + 2 > end_pole ? beg_pole : j + 2);
2473*593dc095SDavid du Colombier                 fixed fx1, fy1, fx2, fy2;
2474*593dc095SDavid du Colombier 
2475*593dc095SDavid du Colombier                 g2d(this, this->pole[j1].ax, this->pole[j1].ay, &fx1, &fy1);
2476*593dc095SDavid du Colombier                 g2d(this, this->pole[j2].ax, this->pole[j2].ay, &fx2, &fy2);
2477*593dc095SDavid du Colombier                 code = gx_path_add_curve(this->output_path, fx, fy, fx1, fy1, fx2, fy2);
2478*593dc095SDavid du Colombier 		if (code < 0)
2479*593dc095SDavid du Colombier 		    return code;
2480*593dc095SDavid du Colombier                 vd_setcolor(RGB(255,0,0));
2481*593dc095SDavid du Colombier                 vd_curveto(fx,fy,fx1,fy1,fx2,fy2);
2482*593dc095SDavid du Colombier                 j+=2;
2483*593dc095SDavid du Colombier             }
2484*593dc095SDavid du Colombier         }
2485*593dc095SDavid du Colombier         code = gx_path_close_subpath(this->output_path);
2486*593dc095SDavid du Colombier 	if (code < 0)
2487*593dc095SDavid du Colombier 	    return code;
2488*593dc095SDavid du Colombier     }
2489*593dc095SDavid du Colombier     return 0;
2490*593dc095SDavid du Colombier }
2491*593dc095SDavid du Colombier 
t1_hinter__add_trailing_moveto(t1_hinter * this)2492*593dc095SDavid du Colombier private int t1_hinter__add_trailing_moveto(t1_hinter * this)
2493*593dc095SDavid du Colombier {   t1_glyph_space_coord gx = this->width_gx, gy = this->width_gy;
2494*593dc095SDavid du Colombier 
2495*593dc095SDavid du Colombier #if 0 /* This appears wrong due to several reasons :
2496*593dc095SDavid du Colombier 	 1. With TextAlphaBits=1, AlignToPixels must have no effect.
2497*593dc095SDavid du Colombier 	 2. ashow, awidthshow must add the width before alignment.
2498*593dc095SDavid du Colombier 	 4. In the PDF interpreter, Tc must add before alignment.
2499*593dc095SDavid du Colombier 	 5. Since a character origin is aligned,
2500*593dc095SDavid du Colombier 	    rounding its width doesn't affect subsequent characters.
2501*593dc095SDavid du Colombier 	 6. When the character size is smaller than half pixel width,
2502*593dc095SDavid du Colombier 	    glyph widths round to zero, causing overlapped glyphs.
2503*593dc095SDavid du Colombier 	    (Bug 687719 "PDFWRITE corrupts letter spacing/placement").
2504*593dc095SDavid du Colombier        */
2505*593dc095SDavid du Colombier     if (this->align_to_pixels)
2506*593dc095SDavid du Colombier 	t1_hinter__align_to_grid(this, this->g2o_fraction, &gx, &gy, this->align_to_pixels);
2507*593dc095SDavid du Colombier #endif
2508*593dc095SDavid du Colombier     return t1_hinter__rmoveto(this, gx - this->cx, gy - this->cy);
2509*593dc095SDavid du Colombier }
2510*593dc095SDavid du Colombier 
t1_hinter__endglyph(t1_hinter * this)2511*593dc095SDavid du Colombier int t1_hinter__endglyph(t1_hinter * this)
2512*593dc095SDavid du Colombier {   int code;
2513*593dc095SDavid du Colombier 
2514*593dc095SDavid du Colombier     if (!vd_enabled) { /* Maybe enabled in t1_hinter__set_mapping. */
2515*593dc095SDavid du Colombier 	vd_get_dc('h');
2516*593dc095SDavid du Colombier 	vd_set_shift(VD_SHIFT_X, VD_SHIFT_Y);
2517*593dc095SDavid du Colombier 	vd_set_scale(VD_SCALE);
2518*593dc095SDavid du Colombier 	vd_set_origin(0, 0);
2519*593dc095SDavid du Colombier 	if (!VD_DRAW_IMPORT && !this->disable_hinting)
2520*593dc095SDavid du Colombier 	    vd_erase(RGB(255, 255, 255));
2521*593dc095SDavid du Colombier     }
2522*593dc095SDavid du Colombier     if (vd_enabled && this->g2o_fraction != 0 && !this->disable_hinting)
2523*593dc095SDavid du Colombier 	t1_hinter__paint_raster_grid(this);
2524*593dc095SDavid du Colombier     code = t1_hinter__add_trailing_moveto(this);
2525*593dc095SDavid du Colombier     if (code < 0)
2526*593dc095SDavid du Colombier 	goto exit;
2527*593dc095SDavid du Colombier     t1_hinter__compute_y_span(this);
2528*593dc095SDavid du Colombier     t1_hinter__simplify_representation(this);
2529*593dc095SDavid du Colombier     if (!this->disable_hinting && (this->grid_fit_x || this->grid_fit_y)) {
2530*593dc095SDavid du Colombier 	t1_hinter__paint_glyph(this, false);
2531*593dc095SDavid du Colombier 	if (this->FontType == 1)
2532*593dc095SDavid du Colombier 	    t1_hinter__compute_type1_stem_ranges(this);
2533*593dc095SDavid du Colombier 	else
2534*593dc095SDavid du Colombier 	    t1_hinter__compute_type2_stem_ranges(this);
2535*593dc095SDavid du Colombier 	if (FINE_STEM_COMPLEXES)
2536*593dc095SDavid du Colombier 	    t1_hinter__mark_existing_stems(this);
2537*593dc095SDavid du Colombier         t1_hinter__align_stem_commands(this);
2538*593dc095SDavid du Colombier 	if (FINE_STEM_COMPLEXES)
2539*593dc095SDavid du Colombier 	    t1_hinter__unfix_opposite_to_common(this);
2540*593dc095SDavid du Colombier         t1_hinter__compute_opposite_stem_coords(this);
2541*593dc095SDavid du Colombier         /* stem3 was processed in the Type 1 interpreter. */
2542*593dc095SDavid du Colombier         t1_hinter__align_stem_poles(this);
2543*593dc095SDavid du Colombier         t1_hinter__process_dotsections(this);
2544*593dc095SDavid du Colombier         t1_hinter__interpolate_other_poles(this);
2545*593dc095SDavid du Colombier         t1_hinter__paint_glyph(this, true);
2546*593dc095SDavid du Colombier     }
2547*593dc095SDavid du Colombier    if (vd_enabled) {
2548*593dc095SDavid du Colombier         double_matrix m;
2549*593dc095SDavid du Colombier 
2550*593dc095SDavid du Colombier         fraction_matrix__to_double(&this->ctmi, &m);
2551*593dc095SDavid du Colombier         vd_set_scaleXY(vd_get_scale_x * m.xx, vd_get_scale_y * m.yy);
2552*593dc095SDavid du Colombier         vd_set_origin(this->orig_dx, this->orig_dy);
2553*593dc095SDavid du Colombier         /*  fixme : general transform requires changes to vdtrace.
2554*593dc095SDavid du Colombier 	    Current implementation paints exported rotated glyph in wrong coordinates.
2555*593dc095SDavid du Colombier 	*/
2556*593dc095SDavid du Colombier     }
2557*593dc095SDavid du Colombier     if (this->pole_count) {
2558*593dc095SDavid du Colombier 	code = t1_hinter__export(this);
2559*593dc095SDavid du Colombier 	if (code < 0)
2560*593dc095SDavid du Colombier 	    return code;
2561*593dc095SDavid du Colombier     }
2562*593dc095SDavid du Colombier exit:
2563*593dc095SDavid du Colombier     t1_hinter__free_arrays(this);
2564*593dc095SDavid du Colombier     vd_release_dc;
2565*593dc095SDavid du Colombier     return 0;
2566*593dc095SDavid du Colombier }
2567