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