/src/ghostpdl/base/gxttfb.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* A bridge to True Type interpreter. */ |
18 | | |
19 | | #include "gx.h" |
20 | | #include "gxfont.h" |
21 | | #include "gxfont42.h" |
22 | | #include "gxttfb.h" |
23 | | #include "gxfixed.h" |
24 | | #include "gxpath.h" |
25 | | #include "gxfcache.h" |
26 | | #include "gxmatrix.h" |
27 | | #include "gxhintn.h" |
28 | | #include "gzpath.h" |
29 | | #include "ttfmemd.h" |
30 | | #include "gsstruct.h" |
31 | | #include "gserrors.h" |
32 | | #include "gsfont.h" |
33 | | #include "gdebug.h" |
34 | | #include "memory_.h" |
35 | | #include "math_.h" |
36 | | #include "gxgstate.h" |
37 | | #include "gxpaint.h" |
38 | | #include "gzspotan.h" |
39 | | #include <stdarg.h> |
40 | | |
41 | | gs_public_st_composite(st_gx_ttfReader, gx_ttfReader, |
42 | | "gx_ttfReader", gx_ttfReader_enum_ptrs, gx_ttfReader_reloc_ptrs); |
43 | | |
44 | | static |
45 | 0 | ENUM_PTRS_WITH(gx_ttfReader_enum_ptrs, gx_ttfReader *mptr) |
46 | 0 | { |
47 | | /* The fields 'pfont' and 'glyph_data' may contain pointers from global |
48 | | to local memory ( see a comment in gxttfb.h). |
49 | | They must be NULL when a garbager is invoked. |
50 | | Due to that we don't enumerate and don't relocate them. |
51 | | */ |
52 | 0 | DISCARD(mptr); |
53 | 0 | return 0; |
54 | 0 | } |
55 | 0 | ENUM_PTR(0, gx_ttfReader, memory); |
56 | 0 | ENUM_PTRS_END |
57 | | |
58 | 0 | static RELOC_PTRS_WITH(gx_ttfReader_reloc_ptrs, gx_ttfReader *mptr) |
59 | 0 | DISCARD(mptr); |
60 | 0 | RELOC_PTR(gx_ttfReader, memory); |
61 | 0 | RELOC_PTRS_END |
62 | | |
63 | | static bool gx_ttfReader__Eof(ttfReader *self) |
64 | 1.72M | { |
65 | 1.72M | gx_ttfReader *r = (gx_ttfReader *)self; |
66 | | |
67 | 1.72M | if (r->extra_glyph_index != -1) |
68 | 57.7k | return r->pos >= r->glyph_data.bits.size; |
69 | | /* We can't know whether pfont->data.string_proc has more bytes, |
70 | | so we never report Eof for it. */ |
71 | 1.66M | return false; |
72 | 1.72M | } |
73 | | |
74 | | static void gx_ttfReader__Read(ttfReader *self, void *p, int n) |
75 | 6.42M | { |
76 | 6.42M | gx_ttfReader *r = (gx_ttfReader *)self; |
77 | 6.42M | const byte *q; |
78 | | |
79 | 6.42M | if (r->error >= 0) { |
80 | 6.28M | if (r->extra_glyph_index != -1) { |
81 | 4.45M | q = r->glyph_data.bits.data + r->pos; |
82 | 4.45M | r->error = ((r->pos >= r->glyph_data.bits.size || |
83 | 4.45M | r->glyph_data.bits.size - r->pos < n) ? |
84 | 4.45M | gs_note_error(gs_error_invalidfont) : 0); |
85 | 4.45M | if (r->error == 0) |
86 | 4.45M | memcpy(p, q, n); |
87 | 4.45M | } else { |
88 | 1.82M | unsigned int cnt; |
89 | 1.82M | r->error = 0; |
90 | | |
91 | 1.82M | for (cnt = 0; cnt < (uint)n; cnt += r->error) { |
92 | 1.82M | r->error = r->pfont->data.string_proc(r->pfont, (ulong)r->pos + cnt, (ulong)n - cnt, &q); |
93 | 1.82M | if (r->error < 0) |
94 | 0 | break; |
95 | 1.82M | else if ( r->error == 0) { |
96 | 1.82M | memcpy((char *)p + cnt, q, n - cnt); |
97 | 1.82M | break; |
98 | 1.82M | } else { |
99 | 0 | memcpy((char *)p + cnt, q, r->error); |
100 | 0 | } |
101 | 1.82M | } |
102 | 1.82M | } |
103 | 6.28M | } |
104 | 6.42M | if (r->error < 0) { |
105 | 145k | memset(p, 0, n); |
106 | 145k | return; |
107 | 145k | } |
108 | 6.28M | r->pos += n; |
109 | 6.28M | } |
110 | | |
111 | | static void gx_ttfReader__Seek(ttfReader *self, int nPos) |
112 | 72.6k | { |
113 | 72.6k | gx_ttfReader *r = (gx_ttfReader *)self; |
114 | | |
115 | 72.6k | r->pos = nPos; |
116 | 72.6k | } |
117 | | |
118 | | static int gx_ttfReader__Tell(ttfReader *self) |
119 | 100k | { |
120 | 100k | gx_ttfReader *r = (gx_ttfReader *)self; |
121 | | |
122 | 100k | return r->pos; |
123 | 100k | } |
124 | | |
125 | | static bool gx_ttfReader__Error(ttfReader *self) |
126 | 151k | { |
127 | 151k | gx_ttfReader *r = (gx_ttfReader *)self; |
128 | | |
129 | 151k | return r->error; |
130 | 151k | } |
131 | | |
132 | | static int gx_ttfReader__LoadGlyph(ttfReader *self, int glyph_index, const byte **p, int *size) |
133 | 56.9k | { |
134 | 56.9k | gx_ttfReader *r = (gx_ttfReader *)self; |
135 | 56.9k | gs_font_type42 *pfont = r->pfont; |
136 | 56.9k | int code; |
137 | | |
138 | 56.9k | if (r->extra_glyph_index != -1) |
139 | 0 | return 0; /* We only maintain a single glyph buffer. |
140 | | It's enough because ttfOutliner__BuildGlyphOutline |
141 | | is optimized for that, and pfont->data.get_outline |
142 | | implements a charstring cache. */ |
143 | 56.9k | r->glyph_data.memory = pfont->memory; |
144 | 56.9k | code = pfont->data.get_outline(pfont, (uint)glyph_index, &r->glyph_data); |
145 | 56.9k | r->extra_glyph_index = glyph_index; |
146 | 56.9k | r->pos = 0; |
147 | 56.9k | if (code < 0) |
148 | 0 | r->error = code; |
149 | 56.9k | else if (code > 0) { |
150 | | /* Should not happen. */ |
151 | 0 | r->error = gs_note_error(gs_error_unregistered); |
152 | 56.9k | } else { |
153 | 56.9k | *p = r->glyph_data.bits.data; |
154 | 56.9k | *size = r->glyph_data.bits.size; |
155 | 56.9k | } |
156 | 56.9k | return 2; /* found */ |
157 | 56.9k | } |
158 | | |
159 | | static void gx_ttfReader__ReleaseGlyph(ttfReader *self, int glyph_index) |
160 | 58.0k | { |
161 | 58.0k | gx_ttfReader *r = (gx_ttfReader *)self; |
162 | | |
163 | 58.0k | if (r->extra_glyph_index != glyph_index) |
164 | 1.05k | return; |
165 | 56.9k | r->extra_glyph_index = -1; |
166 | 56.9k | gs_glyph_data_free(&r->glyph_data, "gx_ttfReader__ReleaseExtraGlyph"); |
167 | 56.9k | } |
168 | | |
169 | | static void gx_ttfReader__Reset(gx_ttfReader *self) |
170 | 58.1k | { |
171 | 58.1k | if (self->extra_glyph_index != -1) { |
172 | 0 | self->extra_glyph_index = -1; |
173 | 0 | gs_glyph_data_free(&self->glyph_data, "gx_ttfReader__Reset"); |
174 | 0 | } |
175 | 58.1k | self->error = 0; |
176 | 58.1k | self->pos = 0; |
177 | 58.1k | } |
178 | | |
179 | | gx_ttfReader *gx_ttfReader__create(gs_memory_t *mem) |
180 | 2.40k | { |
181 | 2.40k | gx_ttfReader *r = gs_alloc_struct(mem, gx_ttfReader, &st_gx_ttfReader, "gx_ttfReader__create"); |
182 | | |
183 | 2.40k | if (r != NULL) { |
184 | 2.40k | r->super.Eof = gx_ttfReader__Eof; |
185 | 2.40k | r->super.Read = gx_ttfReader__Read; |
186 | 2.40k | r->super.Seek = gx_ttfReader__Seek; |
187 | 2.40k | r->super.Tell = gx_ttfReader__Tell; |
188 | 2.40k | r->super.Error = gx_ttfReader__Error; |
189 | 2.40k | r->super.LoadGlyph = gx_ttfReader__LoadGlyph; |
190 | 2.40k | r->super.ReleaseGlyph = gx_ttfReader__ReleaseGlyph; |
191 | 2.40k | r->pos = 0; |
192 | 2.40k | r->error = 0; |
193 | 2.40k | r->extra_glyph_index = -1; |
194 | 2.40k | memset(&r->glyph_data, 0, sizeof(r->glyph_data)); |
195 | 2.40k | r->pfont = NULL; |
196 | 2.40k | r->memory = mem; |
197 | 2.40k | gx_ttfReader__Reset(r); |
198 | 2.40k | } |
199 | 2.40k | return r; |
200 | 2.40k | } |
201 | | |
202 | | void gx_ttfReader__destroy(gx_ttfReader *self) |
203 | 2.40k | { |
204 | 2.40k | gs_free_object(self->memory, self, "gx_ttfReader__destroy"); |
205 | 2.40k | } |
206 | | |
207 | | static int |
208 | | gx_ttfReader__default_get_metrics(const ttfReader *ttf, uint glyph_index, bool bVertical, |
209 | | short *sideBearing, unsigned short *nAdvance) |
210 | 57.1k | { |
211 | 57.1k | gx_ttfReader *self = (gx_ttfReader *)ttf; |
212 | 57.1k | float sbw[4]; |
213 | 57.1k | int sbw_offset = bVertical; |
214 | 57.1k | int code; |
215 | 57.1k | int factor = self->pfont->data.unitsPerEm; |
216 | | |
217 | 57.1k | code = self->pfont->data.get_metrics(self->pfont, glyph_index, bVertical, sbw); |
218 | 57.1k | if (code < 0) |
219 | 261 | return code; |
220 | | /* Due to an obsolete convention, simple_glyph_metrics scales |
221 | | the metrics into 1x1 rectangle as Postscript like. |
222 | | In same time, the True Type interpreter needs |
223 | | the original design units. |
224 | | Undo the scaling here with accurate rounding. */ |
225 | 56.9k | *sideBearing = (short)floor(sbw[0 + sbw_offset] * factor + 0.5); |
226 | 56.9k | *nAdvance = (short)floor(sbw[2 + sbw_offset] * factor + 0.5); |
227 | 56.9k | return 0; |
228 | 57.1k | } |
229 | | |
230 | | void gx_ttfReader__set_font(gx_ttfReader *self, gs_font_type42 *pfont) |
231 | 116k | { |
232 | 116k | self->pfont = pfont; |
233 | 116k | self->super.get_metrics = gx_ttfReader__default_get_metrics; |
234 | 116k | } |
235 | | |
236 | | /*----------------------------------------------*/ |
237 | | |
238 | | static void DebugRepaint(ttfFont *ttf) |
239 | 0 | { |
240 | 0 | } |
241 | | |
242 | | #ifdef DEBUG |
243 | | static int DebugPrint(ttfFont *ttf, const char *fmt, ...) |
244 | | { |
245 | | char buf[500]; |
246 | | va_list args; |
247 | | int count; |
248 | | |
249 | | if (gs_debug_c('Y')) { |
250 | | va_start(args, fmt); |
251 | | count = vsnprintf(buf, sizeof(buf), fmt, args); |
252 | | /* NB: moved debug output from stdout to stderr |
253 | | */ |
254 | | errwrite(ttf->DebugMem, buf, count); |
255 | | va_end(args); |
256 | | } |
257 | | return 0; |
258 | | } |
259 | | #endif |
260 | | |
261 | | static void WarnBadInstruction(gs_font_type42 *pfont, int glyph_index) |
262 | 511 | { |
263 | 511 | char buf[gs_font_name_max + 1]; |
264 | 511 | int l; |
265 | 511 | gs_font_type42 *base_font = pfont; |
266 | | |
267 | 511 | while ((gs_font_type42 *)base_font->base != base_font) |
268 | 0 | base_font = (gs_font_type42 *)base_font->base; |
269 | 511 | if (!base_font->data.warning_bad_instruction) { |
270 | 511 | l = min(sizeof(buf) - 1, base_font->font_name.size); |
271 | 511 | memcpy(buf, base_font->font_name.chars, l); |
272 | 511 | buf[l] = 0; |
273 | 511 | if (glyph_index >= 0) |
274 | 0 | emprintf2(pfont->memory, |
275 | 511 | "Failed to interpret TT instructions for glyph index %d of font %s. " |
276 | 511 | "Continue ignoring instructions of the font.\n", |
277 | 511 | glyph_index, buf); |
278 | 511 | else |
279 | 511 | emprintf1(pfont->memory, |
280 | 511 | "Failed to interpret TT instructions in font %s. " |
281 | 511 | "Continue ignoring instructions of the font.\n", |
282 | 511 | buf); |
283 | 511 | base_font->data.warning_bad_instruction = true; |
284 | 511 | } |
285 | 511 | } |
286 | | |
287 | | static void WarnPatented(gs_font_type42 *pfont, ttfFont *ttf, const char *txt) |
288 | 5.08k | { |
289 | 5.08k | if (!ttf->design_grid) { |
290 | 0 | char buf[gs_font_name_max + 1]; |
291 | 0 | int l; |
292 | 0 | gs_font_type42 *base_font = pfont; |
293 | |
|
294 | 0 | while ((gs_font_type42 *)base_font->base != base_font) |
295 | 0 | base_font = (gs_font_type42 *)base_font->base; |
296 | 0 | if (!base_font->data.warning_patented) { |
297 | 0 | l = min(sizeof(buf) - 1, base_font->font_name.size); |
298 | 0 | memcpy(buf, base_font->font_name.chars, l); |
299 | 0 | buf[l] = 0; |
300 | 0 | emprintf2(pfont->memory, |
301 | 0 | "%s %s requires a patented True Type interpreter.\n", |
302 | 0 | txt, |
303 | 0 | buf); |
304 | 0 | base_font->data.warning_patented = true; |
305 | 0 | } |
306 | 0 | } |
307 | 5.08k | } |
308 | | |
309 | | /*----------------------------------------------*/ |
310 | | |
311 | | struct gx_ttfMemory_s { |
312 | | ttfMemory super; |
313 | | gs_memory_t *memory; |
314 | | }; |
315 | | |
316 | | gs_private_st_simple(st_gx_ttfMemory, gx_ttfMemory, "gx_ttfMemory"); |
317 | | /* st_gx_ttfMemory::memory points to a root. */ |
318 | | |
319 | | static void *gx_ttfMemory__alloc_bytes(ttfMemory *self, int size, const char *cname) |
320 | 44.5k | { |
321 | 44.5k | gs_memory_t *mem = ((gx_ttfMemory *)self)->memory; |
322 | | |
323 | 44.5k | return gs_alloc_bytes(mem, size, cname); |
324 | 44.5k | } |
325 | | |
326 | | static void *gx_ttfMemory__alloc_struct(ttfMemory *self, const ttfMemoryDescriptor *d, const char *cname) |
327 | 7.44k | { |
328 | 7.44k | gs_memory_t *mem = ((gx_ttfMemory *)self)->memory; |
329 | | |
330 | 7.44k | return mem->procs.alloc_struct(mem, (const gs_memory_struct_type_t *)d, cname); |
331 | 7.44k | } |
332 | | |
333 | | static void gx_ttfMemory__free(ttfMemory *self, void *p, const char *cname) |
334 | 81.5k | { |
335 | 81.5k | gs_memory_t *mem = ((gx_ttfMemory *)self)->memory; |
336 | | |
337 | 81.5k | gs_free_object(mem, p, cname); |
338 | 81.5k | } |
339 | | |
340 | | /*----------------------------------------------*/ |
341 | | |
342 | | static inline float reminder(float v, int x) |
343 | 116k | { |
344 | 116k | return ((v / x) - floor(v / x)) * x; |
345 | 116k | } |
346 | | |
347 | | static void decompose_matrix(const gs_font_type42 *pfont, const gs_matrix * char_tm, |
348 | | const gs_log2_scale_point *log2_scale, bool design_grid, |
349 | | gs_point *char_size, gs_point *subpix_origin, gs_matrix *post_transform, bool *dg) |
350 | 58.1k | { |
351 | | /* |
352 | | * char_tm maps to subpixels. |
353 | | */ |
354 | | /* |
355 | | * We use a Free Type 1 True Type interpreter, which cannot perform |
356 | | * a grid-fitting with skewing/rotation. It appears acceptable |
357 | | * because we want to minimize invocations of patented instructions. |
358 | | * We believe that skewing/rotation requires the patented intrivial cases |
359 | | * of projection/freedom vectors. |
360 | | */ |
361 | 58.1k | int scale_x = 1 << log2_scale->x; |
362 | 58.1k | int scale_y = 1 << log2_scale->y; |
363 | 58.1k | bool atp = gs_currentaligntopixels(pfont->dir); |
364 | 58.1k | bool design_grid1; |
365 | | |
366 | 58.1k | char_size->x = hypot(char_tm->xx, char_tm->xy); |
367 | 58.1k | char_size->y = hypot(char_tm->yx, char_tm->yy); |
368 | 58.1k | if (char_size->x <= 2 && char_size->y <= 2) { |
369 | | /* Disable the grid fitting for very small fonts. */ |
370 | 0 | design_grid1 = true; |
371 | 0 | } else |
372 | 58.1k | design_grid1 = design_grid || !(gs_currentgridfittt(pfont->dir) & 1); |
373 | 58.1k | *dg = design_grid1; |
374 | 58.1k | subpix_origin->x = (atp ? 0 : reminder(char_tm->tx, scale_x) / scale_x); |
375 | 58.1k | subpix_origin->y = (atp ? 0 : reminder(char_tm->ty, scale_y) / scale_y); |
376 | 58.1k | post_transform->xx = char_tm->xx / (design_grid1 ? 1 : char_size->x); |
377 | 58.1k | post_transform->xy = char_tm->xy / (design_grid1 ? 1 : char_size->x); |
378 | 58.1k | post_transform->yx = char_tm->yx / (design_grid1 ? 1 : char_size->y); |
379 | 58.1k | post_transform->yy = char_tm->yy / (design_grid1 ? 1 : char_size->y); |
380 | 58.1k | post_transform->tx = char_tm->tx - subpix_origin->x; |
381 | 58.1k | post_transform->ty = char_tm->ty - subpix_origin->y; |
382 | 58.1k | } |
383 | | |
384 | | /*----------------------------------------------*/ |
385 | | |
386 | | ttfFont *ttfFont__create(gs_font_dir *dir) |
387 | 2.40k | { |
388 | 2.40k | gs_memory_t *mem = dir->memory->stable_memory; |
389 | 2.40k | ttfFont *ttf; |
390 | | |
391 | 2.40k | if (dir->ttm == NULL) { |
392 | 1.31k | gx_ttfMemory *m = gs_alloc_struct(mem, gx_ttfMemory, &st_gx_ttfMemory, "ttfFont__create(gx_ttfMemory)"); |
393 | | |
394 | 1.31k | if (!m) |
395 | 0 | return 0; |
396 | 1.31k | m->super.alloc_struct = gx_ttfMemory__alloc_struct; |
397 | 1.31k | m->super.alloc_bytes = gx_ttfMemory__alloc_bytes; |
398 | 1.31k | m->super.free = gx_ttfMemory__free; |
399 | 1.31k | m->memory = mem; |
400 | 1.31k | dir->ttm = m; |
401 | 1.31k | } |
402 | 2.40k | if(ttfInterpreter__obtain(&dir->ttm->super, &dir->tti)) |
403 | 0 | return 0; |
404 | 2.40k | if(gx_san__obtain(mem, &dir->san)) |
405 | 0 | return 0; |
406 | 2.40k | ttf = gs_alloc_struct(mem, ttfFont, &st_ttfFont, "ttfFont__create"); |
407 | 2.40k | if (ttf == NULL) |
408 | 0 | return 0; |
409 | | #ifdef DEBUG |
410 | | ttfFont__init(ttf, &dir->ttm->super, DebugRepaint, |
411 | | (gs_debug_c('Y') ? DebugPrint : NULL), mem); |
412 | | #else |
413 | 2.40k | ttfFont__init(ttf, &dir->ttm->super, DebugRepaint, NULL, mem); |
414 | 2.40k | #endif |
415 | | |
416 | 2.40k | return ttf; |
417 | 2.40k | } |
418 | | |
419 | | void ttfFont__destroy(ttfFont *self, gs_font_dir *dir) |
420 | 2.40k | { |
421 | 2.40k | gs_memory_t *mem = dir->memory->stable_memory; |
422 | | |
423 | 2.40k | ttfFont__finit(self); |
424 | 2.40k | gs_free_object(mem, self, "ttfFont__destroy"); |
425 | 2.40k | ttfInterpreter__release(&dir->tti); |
426 | 2.40k | gx_san__release(&dir->san); |
427 | 2.40k | if (dir->tti == NULL && dir->ttm != NULL) { |
428 | 1.31k | gs_free_object(mem, dir->ttm, "ttfFont__destroy(gx_ttfMemory)"); |
429 | 1.31k | dir->ttm = NULL; |
430 | 1.31k | } |
431 | 2.40k | } |
432 | | |
433 | | int ttfFont__Open_aux(ttfFont *self, ttfInterpreter *tti, gx_ttfReader *r, gs_font_type42 *pfont, |
434 | | const gs_matrix * char_tm, const gs_log2_scale_point *log2_scale, |
435 | | bool design_grid) |
436 | 2.40k | { |
437 | 2.40k | gs_point char_size, subpix_origin; |
438 | 2.40k | gs_matrix post_transform; |
439 | | /* |
440 | | * Ghostscript proceses a TTC index in gs/lib/gs_ttf.ps, |
441 | | * and *pfont already adjusted to it. |
442 | | * Therefore TTC headers never comes here. |
443 | | */ |
444 | 2.40k | unsigned int nTTC = 0; |
445 | 2.40k | bool dg; |
446 | | |
447 | 2.40k | decompose_matrix(pfont, char_tm, log2_scale, design_grid, &char_size, &subpix_origin, &post_transform, &dg); |
448 | 2.40k | switch(ttfFont__Open(tti, self, &r->super, nTTC, char_size.x, char_size.y, dg)) { |
449 | 1.78k | case fNoError: |
450 | 1.78k | return 0; |
451 | 0 | case fMemoryError: |
452 | 0 | return_error(gs_error_VMerror); |
453 | 0 | case fUnimplemented: |
454 | 0 | return_error(gs_error_unregistered); |
455 | 511 | case fBadInstruction: |
456 | 511 | WarnBadInstruction(pfont, -1); |
457 | 511 | goto recover; |
458 | 111 | case fPatented: |
459 | 111 | WarnPatented(pfont, self, "The font"); |
460 | 622 | recover: |
461 | 622 | self->patented = true; |
462 | 622 | return 0; |
463 | 0 | default: |
464 | 0 | { int code = r->super.Error(&r->super); |
465 | |
|
466 | 0 | if (code < 0) |
467 | 0 | return code; |
468 | 0 | return_error(gs_error_invalidfont); |
469 | 0 | } |
470 | 2.40k | } |
471 | 2.40k | } |
472 | | |
473 | | /*----------------------------------------------*/ |
474 | | |
475 | | typedef struct gx_ttfExport_s { |
476 | | ttfExport super; |
477 | | gx_path *path; |
478 | | gs_fixed_point w; |
479 | | int error; |
480 | | bool monotonize; |
481 | | } gx_ttfExport; |
482 | | |
483 | | static void gx_ttfExport__MoveTo(ttfExport *self, FloatPoint *p) |
484 | 66.6k | { |
485 | 66.6k | gx_ttfExport *e = (gx_ttfExport *)self; |
486 | | |
487 | 66.6k | if (e->error >= 0) |
488 | 66.6k | e->error = gx_path_add_point(e->path, float2fixed(p->x), float2fixed(p->y)); |
489 | 66.6k | } |
490 | | |
491 | | static void gx_ttfExport__LineTo(ttfExport *self, FloatPoint *p) |
492 | 296k | { |
493 | 296k | gx_ttfExport *e = (gx_ttfExport *)self; |
494 | | |
495 | 296k | if (e->error >= 0) |
496 | 296k | e->error = gx_path_add_line_notes(e->path, float2fixed(p->x), float2fixed(p->y), sn_none); |
497 | 296k | } |
498 | | |
499 | | static void gx_ttfExport__CurveTo(ttfExport *self, FloatPoint *p0, FloatPoint *p1, FloatPoint *p2) |
500 | 967k | { |
501 | 967k | gx_ttfExport *e = (gx_ttfExport *)self; |
502 | | |
503 | 967k | if (e->error >= 0) { |
504 | 967k | if (e->monotonize) { |
505 | 0 | curve_segment s; |
506 | |
|
507 | 0 | s.notes = sn_none; |
508 | 0 | s.p1.x = float2fixed(p0->x), s.p1.y = float2fixed(p0->y), |
509 | 0 | s.p2.x = float2fixed(p1->x), s.p2.y = float2fixed(p1->y), |
510 | 0 | s.pt.x = float2fixed(p2->x), s.pt.y = float2fixed(p2->y); |
511 | 0 | e->error = gx_curve_monotonize(e->path, &s); |
512 | 0 | } else |
513 | 967k | e->error = gx_path_add_curve_notes(e->path, float2fixed(p0->x), float2fixed(p0->y), |
514 | 967k | float2fixed(p1->x), float2fixed(p1->y), |
515 | 967k | float2fixed(p2->x), float2fixed(p2->y), sn_none); |
516 | 967k | } |
517 | 967k | } |
518 | | |
519 | | static void gx_ttfExport__Close(ttfExport *self) |
520 | 66.6k | { |
521 | 66.6k | gx_ttfExport *e = (gx_ttfExport *)self; |
522 | | |
523 | 66.6k | if (e->error >= 0) |
524 | 66.6k | e->error = gx_path_close_subpath_notes(e->path, sn_none); |
525 | 66.6k | } |
526 | | |
527 | | static void gx_ttfExport__Point(ttfExport *self, FloatPoint *p, bool bOnCurve, bool bNewPath) |
528 | 0 | { |
529 | | /* Never called. */ |
530 | 0 | } |
531 | | |
532 | | static void gx_ttfExport__SetWidth(ttfExport *self, FloatPoint *p) |
533 | 54.1k | { |
534 | 54.1k | gx_ttfExport *e = (gx_ttfExport *)self; |
535 | | |
536 | 54.1k | e->w.x = float2fixed(p->x); |
537 | 54.1k | e->w.y = float2fixed(p->y); |
538 | 54.1k | } |
539 | | |
540 | | static void gx_ttfExport__DebugPaint(ttfExport *self) |
541 | 0 | { |
542 | 0 | } |
543 | | |
544 | | /*----------------------------------------------*/ |
545 | | |
546 | | static int |
547 | | path_to_hinter(t1_hinter *h, gx_path *path) |
548 | 0 | { int code; |
549 | 0 | gs_path_enum penum; |
550 | 0 | gs_fixed_point pts[3]; |
551 | 0 | gs_fixed_point p = {0, 0}; /* initialize to avoid a warning */ |
552 | 0 | bool first = true; |
553 | 0 | int op; |
554 | |
|
555 | 0 | code = gx_path_enum_init(&penum, path); |
556 | 0 | if (code < 0) |
557 | 0 | return code; |
558 | 0 | while ((op = gx_path_enum_next(&penum, pts)) != 0) { |
559 | 0 | switch (op) { |
560 | 0 | case gs_pe_moveto: |
561 | 0 | if (first) { |
562 | 0 | first = false; |
563 | 0 | p = pts[0]; |
564 | 0 | code = t1_hinter__rmoveto(h, p.x, p.y); |
565 | 0 | } else |
566 | 0 | code = t1_hinter__rmoveto(h, pts[0].x - p.x, pts[0].y - p.y); |
567 | 0 | break; |
568 | 0 | case gs_pe_lineto: |
569 | 0 | case gs_pe_gapto: |
570 | 0 | code = t1_hinter__rlineto(h, pts[0].x - p.x, pts[0].y - p.y); |
571 | 0 | break; |
572 | 0 | case gs_pe_curveto: |
573 | 0 | code = t1_hinter__rcurveto(h, pts[0].x - p.x, pts[0].y - p.y, |
574 | 0 | pts[1].x - pts[0].x, pts[1].y - pts[0].y, |
575 | 0 | pts[2].x - pts[1].x, pts[2].y - pts[1].y); |
576 | 0 | pts[0] = pts[2]; |
577 | 0 | break; |
578 | 0 | case gs_pe_closepath: |
579 | 0 | code = t1_hinter__closepath(h); |
580 | 0 | break; |
581 | 0 | default: |
582 | 0 | return_error(gs_error_unregistered); |
583 | 0 | } |
584 | 0 | if (code < 0) |
585 | 0 | return code; |
586 | 0 | p = pts[0]; |
587 | 0 | } |
588 | 0 | return 0; |
589 | 0 | } |
590 | | |
591 | 0 | #define exch(a,b) a^=b; b^=a; a^=b; |
592 | | |
593 | | static void |
594 | | transpose_path(gx_path *path) |
595 | 0 | { segment *s = (segment *)path->first_subpath; |
596 | |
|
597 | 0 | exch(path->bbox.p.x, path->bbox.p.y); |
598 | 0 | exch(path->bbox.q.x, path->bbox.q.y); |
599 | 0 | for (; s; s = s->next) { |
600 | 0 | if (s->type == s_curve) { |
601 | 0 | curve_segment *c = (curve_segment *)s; |
602 | |
|
603 | 0 | exch(c->p1.x, c->p1.y); |
604 | 0 | exch(c->p2.x, c->p2.y); |
605 | 0 | } |
606 | 0 | exch(s->pt.x, s->pt.y); |
607 | 0 | } |
608 | 0 | } |
609 | | |
610 | | typedef struct { |
611 | | t1_hinter super; |
612 | | int transpose; |
613 | | fixed midx; |
614 | | } t1_hinter_aux; |
615 | | |
616 | | static int |
617 | | stem_hint_handler(void *client_data, gx_san_sect *ss) |
618 | 0 | { |
619 | 0 | t1_hinter_aux *h = (t1_hinter_aux *)client_data; |
620 | |
|
621 | 0 | if (ss->side_mask == 3) { |
622 | | /* Orient horizontal hints to help with top/bottom alignment zones. |
623 | | Otherwize glyphs may get a random height due to serif adjustsment. */ |
624 | 0 | if (ss->xl > h->midx && h->transpose) |
625 | 0 | return (h->transpose ? t1_hinter__hstem : t1_hinter__vstem) |
626 | 0 | (&h->super, ss->xr, ss->xl - ss->xr); |
627 | 0 | else |
628 | 0 | return (h->transpose ? t1_hinter__hstem : t1_hinter__vstem) |
629 | 0 | (&h->super, ss->xl, ss->xr - ss->xl); |
630 | 0 | } else |
631 | 0 | return t1_hinter__overall_hstem(&h->super, ss->xl, ss->xr - ss->xl, ss->side_mask); |
632 | 0 | } |
633 | | |
634 | 0 | #define OVERALL_HINT 0 /* Overall hints help to emulate Type 1 alignment zones |
635 | | (except for overshoot suppression.) |
636 | | For example, without it comparefiles/type42_glyph_index.ps |
637 | | some glyphs have different height due to |
638 | | serifs are aligned in same way as horizontal stems, |
639 | | but both sides of a stem have same priority. |
640 | | |
641 | | This stuff appears low useful, because horizontal |
642 | | hint orientation performs this job perfectly. |
643 | | fixme : remove. |
644 | | fixme : remove side_mask from gxhintn.c . |
645 | | */ |
646 | | |
647 | | static int grid_fit(gx_device_spot_analyzer *padev, gx_path *path, |
648 | | gs_font_type42 *pfont, const gs_log2_scale_point *pscale, gx_ttfExport *e, ttfOutliner *o) |
649 | 0 | { |
650 | | /* Not completed yet. */ |
651 | 0 | gs_gstate gs_stub; |
652 | 0 | gx_fill_params params; |
653 | 0 | gx_device_color devc_stub; |
654 | 0 | int code; |
655 | 0 | t1_hinter_aux h; |
656 | 0 | gs_matrix m, fm, fmb; |
657 | 0 | gs_matrix_fixed ctm_temp; |
658 | 0 | bool atp = gs_currentaligntopixels(pfont->dir); |
659 | 0 | int FontType = 1; /* Will apply Type 1 hinter. */ |
660 | 0 | fixed sbx = 0, sby = 0; /* stub */ |
661 | 0 | double scale = 1.0 / o->pFont->nUnitsPerEm; |
662 | 0 | gs_fixed_rect bbox; |
663 | |
|
664 | 0 | m.xx = o->post_transform.a; |
665 | 0 | m.xy = o->post_transform.b; |
666 | 0 | m.yx = o->post_transform.c; |
667 | 0 | m.yy = o->post_transform.d; |
668 | 0 | m.tx = o->post_transform.tx; |
669 | 0 | m.ty = o->post_transform.ty; |
670 | 0 | code = gs_matrix_fixed_from_matrix(&ctm_temp, &m); |
671 | 0 | if (code < 0) |
672 | 0 | return code; |
673 | 0 | code = gs_matrix_scale(&pfont->FontMatrix, scale, scale, &fm); |
674 | 0 | if (code < 0) |
675 | 0 | return code; |
676 | 0 | code = gs_matrix_scale(&pfont->base->FontMatrix, scale, scale, &fmb); |
677 | 0 | if (code < 0) |
678 | 0 | return code; |
679 | 0 | t1_hinter__init(&h.super, path); /* Will export to */ |
680 | 0 | code = t1_hinter__set_mapping(&h.super, &ctm_temp, |
681 | 0 | &fm, &fmb, |
682 | 0 | pscale->x, pscale->x, 0, 0, |
683 | 0 | ctm_temp.tx_fixed, ctm_temp.ty_fixed, atp); |
684 | 0 | if (code < 0) |
685 | 0 | return code; |
686 | 0 | if (!h.super.disable_hinting) { |
687 | 0 | o->post_transform.a = o->post_transform.d = 1; |
688 | 0 | o->post_transform.b = o->post_transform.c = 0; |
689 | 0 | o->post_transform.tx = o->post_transform.ty = 0; |
690 | 0 | code = ttfOutliner__DrawGlyphOutline(o); |
691 | 0 | if (code < 0) |
692 | 0 | return code; |
693 | 0 | code = t1_hinter__set_font42_data(&h.super, FontType, &pfont->data, false); |
694 | 0 | if (code < 0) |
695 | 0 | return code; |
696 | 0 | code = t1_hinter__sbw(&h.super, sbx, sby, e->w.x, e->w.y); |
697 | 0 | if (code < 0) |
698 | 0 | return code; |
699 | 0 | code = gx_path_bbox(path, &bbox); |
700 | 0 | if (code < 0) |
701 | 0 | return code; |
702 | 0 | memset(&gs_stub, 0, sizeof(gs_stub)); |
703 | 0 | gs_stub.memory = padev->memory; |
704 | 0 | set_nonclient_dev_color(&devc_stub, 1); |
705 | 0 | params.rule = gx_rule_winding_number; |
706 | 0 | params.adjust.x = params.adjust.y = 0; |
707 | 0 | params.flatness = fixed2float(max(bbox.q.x - bbox.p.x, bbox.q.y - bbox.p.y)) / 100.0; |
708 | |
|
709 | 0 | for (h.transpose = 0; h.transpose < 2; h.transpose++) { |
710 | 0 | h.midx = (padev->xmin + padev->xmax) / 2; |
711 | 0 | if (h.transpose) |
712 | 0 | transpose_path(path); |
713 | 0 | gx_san_begin(padev); |
714 | 0 | code = dev_proc(padev, fill_path)((gx_device *)padev, |
715 | 0 | &gs_stub, path, ¶ms, &devc_stub, NULL); |
716 | 0 | gx_san_end(padev); |
717 | 0 | if (code >= 0) |
718 | 0 | code = gx_san_generate_stems(padev, OVERALL_HINT && h.transpose, |
719 | 0 | &h, stem_hint_handler); |
720 | 0 | if (h.transpose) |
721 | 0 | transpose_path(path); |
722 | 0 | if (code < 0) |
723 | 0 | return code; |
724 | 0 | } |
725 | | |
726 | | /* fixme : Storing hints permanently would be useful. |
727 | | Note that if (gftt & 1), the outline and hints are already scaled. |
728 | | */ |
729 | 0 | code = path_to_hinter(&h.super, path); |
730 | 0 | if (code < 0) |
731 | 0 | return code; |
732 | 0 | code = gx_path_new(path); |
733 | 0 | if (code < 0) |
734 | 0 | return code; |
735 | 0 | code = t1_hinter__endglyph(&h.super); |
736 | 0 | } else { |
737 | 0 | code = ttfOutliner__DrawGlyphOutline(o); |
738 | 0 | if (code < 0) |
739 | 0 | return e->error; |
740 | 0 | } |
741 | 0 | return code; |
742 | 0 | } |
743 | | |
744 | | int gx_ttf_outline(ttfFont *ttf, gx_ttfReader *r, gs_font_type42 *pfont, int glyph_index, |
745 | | const gs_matrix *m, const gs_log2_scale_point *pscale, |
746 | | gx_path *path, bool design_grid) |
747 | 55.6k | { |
748 | 55.6k | gx_ttfExport e; |
749 | 55.6k | ttfOutliner o; |
750 | 55.6k | gs_point char_size, subpix_origin; |
751 | 55.6k | gs_matrix post_transform; |
752 | | /* Ghostscript proceses a TTC index in gs/lib/gs_ttf.ps, */ |
753 | | /* so that TTC never comes here. */ |
754 | 55.6k | FloatMatrix m1; |
755 | 55.6k | bool dg; |
756 | 55.6k | uint gftt = gs_currentgridfittt(pfont->dir); |
757 | 55.6k | bool ttin = (gftt & 1); |
758 | 55.6k | int code; |
759 | | /* gs_currentgridfittt values (binary) : |
760 | | 00 - no grid fitting; |
761 | | 01 - Grid fit with TT interpreter; On failure warn and render unhinted. |
762 | | 10 - Interpret in the design grid and then autohint. |
763 | | 11 - Grid fit with TT interpreter; On failure render autohinted. |
764 | | */ |
765 | 55.6k | bool auth = (gftt & 2); |
766 | | |
767 | 55.6k | decompose_matrix(pfont, m, pscale, design_grid, &char_size, &subpix_origin, &post_transform, &dg); |
768 | 55.6k | m1.a = post_transform.xx; |
769 | 55.6k | m1.b = post_transform.xy; |
770 | 55.6k | m1.c = post_transform.yx; |
771 | 55.6k | m1.d = post_transform.yy; |
772 | 55.6k | m1.tx = post_transform.tx; |
773 | 55.6k | m1.ty = post_transform.ty; |
774 | 55.6k | e.super.bPoints = false; |
775 | 55.6k | e.super.bOutline = true; |
776 | 55.6k | e.super.MoveTo = gx_ttfExport__MoveTo; |
777 | 55.6k | e.super.LineTo = gx_ttfExport__LineTo; |
778 | 55.6k | e.super.CurveTo = gx_ttfExport__CurveTo; |
779 | 55.6k | e.super.Close = gx_ttfExport__Close; |
780 | 55.6k | e.super.Point = gx_ttfExport__Point; |
781 | 55.6k | e.super.SetWidth = gx_ttfExport__SetWidth; |
782 | 55.6k | e.super.DebugPaint = gx_ttfExport__DebugPaint; |
783 | 55.6k | e.error = 0; |
784 | 55.6k | e.path = path; |
785 | 55.6k | e.w.x = 0; |
786 | 55.6k | e.w.y = 0; |
787 | 55.6k | e.monotonize = auth; |
788 | 55.6k | gx_ttfReader__Reset(r); |
789 | 55.6k | ttfOutliner__init(&o, ttf, &r->super, &e.super, true, false, pfont->WMode != 0); |
790 | 55.6k | switch(ttfOutliner__Outline(&o, glyph_index, subpix_origin.x, subpix_origin.y, &m1)) { |
791 | 0 | case fBadInstruction: |
792 | 0 | WarnBadInstruction(pfont, glyph_index); |
793 | 0 | goto recover; |
794 | 4.96k | case fPatented: |
795 | | /* The returned outline did not apply a bytecode (it is not grid-fitted). */ |
796 | 4.96k | if (!auth) |
797 | 4.96k | WarnPatented(pfont, ttf, "Some glyphs of the font"); |
798 | 4.96k | recover : |
799 | 4.96k | if (!design_grid && auth) |
800 | 0 | return grid_fit(pfont->dir->san, path, pfont, pscale, &e, &o); |
801 | | /* Falls through. */ |
802 | 54.1k | case fNoError: |
803 | 54.1k | if (!design_grid && !ttin && auth) |
804 | 0 | return grid_fit(pfont->dir->san, path, pfont, pscale, &e, &o); |
805 | 54.1k | code = ttfOutliner__DrawGlyphOutline(&o); |
806 | 54.1k | if (code < 0) |
807 | 0 | return code; |
808 | 54.1k | return 0; |
809 | 0 | case fMemoryError: |
810 | 0 | return_error(gs_error_VMerror); |
811 | 0 | case fUnimplemented: |
812 | 0 | return_error(gs_error_unregistered); |
813 | 1.55k | case fBadFontData: |
814 | 1.55k | return_error(gs_error_invalidfont); |
815 | 0 | default: |
816 | 0 | { int code = r->super.Error(&r->super); |
817 | |
|
818 | 0 | if (code < 0) |
819 | 0 | return code; |
820 | 0 | return_error(gs_error_invalidfont); |
821 | 0 | } |
822 | 55.6k | } |
823 | 55.6k | } |