Commit | Line | Data |
---|---|---|
ae4278ee HD |
1 | /* |
2 | * PostScript driver Type42 font functions | |
3 | * | |
4 | * Copyright 2002 Huw D M Davies for CodeWeavers | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 | */ | |
20 | #include <string.h> | |
21 | #include <stdlib.h> | |
e37c6e18 | 22 | #include <stdarg.h> |
ae4278ee HD |
23 | #include <stdio.h> |
24 | #include <assert.h> | |
3efacb0a | 25 | |
e37c6e18 | 26 | #include "windef.h" |
3efacb0a FG |
27 | #include "winbase.h" |
28 | #include "winerror.h" | |
29 | #include "wingdi.h" | |
ae4278ee | 30 | #include "winspool.h" |
3efacb0a | 31 | |
ae4278ee HD |
32 | #include "psdrv.h" |
33 | #include "wine/debug.h" | |
ae4278ee HD |
34 | #include "config.h" |
35 | #include "wine/port.h" | |
36 | ||
37 | WINE_DEFAULT_DEBUG_CHANNEL(psdrv); | |
38 | ||
39 | ||
40 | #define GET_BE_WORD(ptr) MAKEWORD( ((BYTE *)(ptr))[1], ((BYTE *)(ptr))[0] ) | |
41 | #define GET_BE_DWORD(ptr) ((DWORD)MAKELONG( GET_BE_WORD(&((WORD *)(ptr))[1]), \ | |
42 | GET_BE_WORD(&((WORD *)(ptr))[0]) )) | |
43 | ||
44 | #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \ | |
45 | ( ( (DWORD)_x4 << 24 ) | \ | |
46 | ( (DWORD)_x3 << 16 ) | \ | |
47 | ( (DWORD)_x2 << 8 ) | \ | |
48 | (DWORD)_x1 ) | |
49 | ||
50 | typedef struct { | |
51 | DWORD MS_tag; | |
52 | DWORD len, check; | |
53 | BYTE *data; | |
54 | BOOL write; | |
55 | } OTTable; | |
56 | ||
57 | const OTTable tables_templ[] = { | |
58 | { MS_MAKE_TAG('c','v','t',' '), 0, 0, NULL, TRUE }, | |
59 | { MS_MAKE_TAG('f','p','g','m'), 0, 0, NULL, TRUE }, | |
60 | { MS_MAKE_TAG('g','d','i','r'), 0, 0, NULL, TRUE }, | |
61 | { MS_MAKE_TAG('g','l','y','f'), 0, 0, NULL, FALSE }, | |
62 | { MS_MAKE_TAG('h','e','a','d'), 0, 0, NULL, TRUE }, | |
63 | { MS_MAKE_TAG('h','h','e','a'), 0, 0, NULL, TRUE }, | |
5d275962 | 64 | { MS_MAKE_TAG('h','m','t','x'), 0, 0, NULL, TRUE }, |
14c90e8e | 65 | { MS_MAKE_TAG('l','o','c','a'), 0, 0, NULL, TRUE }, |
ae4278ee HD |
66 | { MS_MAKE_TAG('m','a','x','p'), 0, 0, NULL, TRUE }, |
67 | { MS_MAKE_TAG('p','r','e','p'), 0, 0, NULL, TRUE }, | |
68 | { 0, 0, 0, NULL, 0 } | |
69 | }; | |
70 | ||
71 | struct tagTYPE42 { | |
72 | OTTable tables[sizeof(tables_templ)/sizeof(tables_templ[0])]; | |
73 | int glyf_tab, loca_tab, head_tab; /* indices of glyf, loca and head tables */ | |
14c90e8e HD |
74 | int hmtx_tab, maxp_tab; |
75 | int num_of_written_tables; | |
ae4278ee HD |
76 | DWORD glyph_sent_size; |
77 | BOOL *glyph_sent; | |
78 | DWORD emsize; | |
14c90e8e | 79 | DWORD *glyf_blocks; |
ae4278ee HD |
80 | }; |
81 | ||
82 | #define GLYPH_SENT_INC 128 | |
83 | ||
84 | #define FLIP_ORDER(x) \ | |
85 | ( ( ((x) & 0xff) << 24) | \ | |
86 | ( ((x) & 0xff00) << 8) | \ | |
87 | ( ((x) & 0xff0000) >> 8) | \ | |
88 | ( ((x) & 0xff000000) >> 24) ) | |
89 | ||
90 | ||
91 | /* Some flags for composite glyphs. See glyf table in OT spec */ | |
92 | #define ARG_1_AND_2_ARE_WORDS (1L << 0) | |
93 | #define WE_HAVE_A_SCALE (1L << 3) | |
94 | #define MORE_COMPONENTS (1L << 5) | |
95 | #define WE_HAVE_AN_X_AND_Y_SCALE (1L << 6) | |
96 | #define WE_HAVE_A_TWO_BY_TWO (1L << 7) | |
97 | ||
5d275962 | 98 | |
ae4278ee HD |
99 | static BOOL LoadTable(HDC hdc, OTTable *table) |
100 | { | |
101 | int i; | |
102 | ||
103 | if(table->MS_tag == MS_MAKE_TAG('g','d','i','r')) return TRUE; | |
104 | table->len = GetFontData(hdc, table->MS_tag, 0, NULL, 0); | |
105 | table->data = HeapAlloc(GetProcessHeap(), 0, (table->len + 3) & ~3 ); | |
106 | memset(table->data + ((table->len - 1) & ~3), 0, sizeof(DWORD)); | |
107 | GetFontData(hdc, table->MS_tag, 0, table->data, table->len); | |
108 | table->check = 0; | |
109 | for(i = 0; i < (table->len + 3) / 4; i++) | |
110 | table->check += FLIP_ORDER(*((DWORD*)(table->data) + i)); | |
111 | return TRUE; | |
112 | } | |
113 | ||
14c90e8e HD |
114 | static BOOL get_glyf_pos(TYPE42 *t42, DWORD index, DWORD *start, DWORD *end) |
115 | { | |
116 | WORD loca_format = GET_BE_WORD(t42->tables[t42->head_tab].data + 50); | |
117 | TRACE("loca_format = %d\n", loca_format); | |
118 | switch(loca_format) { | |
119 | case 0: | |
120 | *start = GET_BE_WORD(((WORD*)t42->tables[t42->loca_tab].data) + index); | |
121 | *start <<= 1; | |
122 | *end = GET_BE_WORD(((WORD*)t42->tables[t42->loca_tab].data) + index + 1); | |
123 | *end <<= 1; | |
124 | break; | |
125 | case 1: | |
126 | *start = GET_BE_DWORD(((DWORD*)t42->tables[t42->loca_tab].data) + index); | |
127 | *end = GET_BE_DWORD(((DWORD*)t42->tables[t42->loca_tab].data) + index + 1); | |
128 | break; | |
129 | default: | |
130 | ERR("Unknown loca_format %d\n", loca_format); | |
131 | return FALSE; | |
132 | } | |
133 | return TRUE; | |
134 | } | |
ae4278ee | 135 | |
ef2ac7ac HD |
136 | TYPE42 *T42_download_header(PSDRV_PDEVICE *physDev, char *ps_name, |
137 | RECT *bbox, UINT emsize) | |
ae4278ee | 138 | { |
14c90e8e | 139 | DWORD i, j, tablepos, nb_blocks, glyf_off = 0, loca_off = 0, cur_off; |
ae4278ee | 140 | WORD num_of_tables = sizeof(tables_templ) / sizeof(tables_templ[0]) - 1; |
ae4278ee HD |
141 | char *buf; |
142 | TYPE42 *t42; | |
b95693c6 | 143 | const char start[] = /* name, fontbbox */ |
ae4278ee HD |
144 | "25 dict begin\n" |
145 | " /FontName /%s def\n" | |
146 | " /Encoding 256 array 0 1 255{1 index exch /.notdef put} for\n" | |
147 | " def\n" | |
148 | " /PaintType 0 def\n" | |
149 | " /FontMatrix [1 0 0 1 0 0] def\n" | |
150 | " /FontBBox [%f %f %f %f] def\n" | |
151 | " /FontType 42 def\n" | |
152 | " /CharStrings 256 dict begin\n" | |
153 | " /.notdef 0 def\n" | |
154 | " currentdict end def\n" | |
ae4278ee | 155 | " /sfnts [\n"; |
b95693c6 DT |
156 | const char TT_offset_table[] = "<00010000%04x%04x%04x%04x\n"; |
157 | const char TT_table_dir_entry[] = "%08lx%08lx%08lx%08lx\n"; | |
158 | const char storage[] ="]\nhavetype42gdir{pop}{{string} forall}ifelse\n"; | |
159 | const char end[] = "] def\n" | |
14c90e8e | 160 | "havetype42gdir{/GlyphDirectory 256 dict def\n" |
ef2ac7ac | 161 | " sfnts 0 get dup %ld (locx) putinterval %ld (glfx) putinterval}if\n" |
ae4278ee HD |
162 | "currentdict end dup /FontName get exch definefont pop\n"; |
163 | ||
164 | ||
165 | t42 = HeapAlloc(GetProcessHeap(), 0, sizeof(*t42)); | |
166 | memcpy(t42->tables, tables_templ, sizeof(tables_templ)); | |
167 | t42->loca_tab = t42->glyf_tab = t42->head_tab = t42->hmtx_tab = -1; | |
ef2ac7ac | 168 | t42->emsize = emsize; |
14c90e8e | 169 | t42->num_of_written_tables = 0; |
ae4278ee HD |
170 | |
171 | for(i = 0; i < num_of_tables; i++) { | |
172 | LoadTable(physDev->hdc, t42->tables + i); | |
5d275962 | 173 | if(t42->tables[i].len > 0xffff && t42->tables[i].write) break; |
14c90e8e | 174 | if(t42->tables[i].write) t42->num_of_written_tables++; |
ae4278ee HD |
175 | if(t42->tables[i].MS_tag == MS_MAKE_TAG('l','o','c','a')) |
176 | t42->loca_tab = i; | |
177 | else if(t42->tables[i].MS_tag == MS_MAKE_TAG('g','l','y','f')) | |
178 | t42->glyf_tab = i; | |
179 | else if(t42->tables[i].MS_tag == MS_MAKE_TAG('h','e','a','d')) | |
180 | t42->head_tab = i; | |
181 | else if(t42->tables[i].MS_tag == MS_MAKE_TAG('h','m','t','x')) | |
182 | t42->hmtx_tab = i; | |
14c90e8e HD |
183 | else if(t42->tables[i].MS_tag == MS_MAKE_TAG('m','a','x','p')) |
184 | t42->maxp_tab = i; | |
ae4278ee | 185 | } |
5d275962 HD |
186 | if(i < num_of_tables) { |
187 | TRACE("Table %ld has length %ld. Will use Type 1 font instead.\n", i, t42->tables[i].len); | |
188 | T42_free(t42); | |
189 | return NULL; | |
190 | } | |
191 | ||
192 | t42->glyph_sent_size = GLYPH_SENT_INC; | |
193 | t42->glyph_sent = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, | |
194 | t42->glyph_sent_size * | |
195 | sizeof(*(t42->glyph_sent))); | |
ae4278ee HD |
196 | |
197 | buf = HeapAlloc(GetProcessHeap(), 0, sizeof(start) + strlen(ps_name) + | |
198 | 100); | |
199 | ||
200 | sprintf(buf, start, ps_name, | |
ef2ac7ac HD |
201 | (float)bbox->left / emsize, (float)bbox->bottom / emsize, |
202 | (float)bbox->right / emsize, (float)bbox->top / emsize); | |
ae4278ee HD |
203 | |
204 | PSDRV_WriteSpool(physDev, buf, strlen(buf)); | |
205 | ||
14c90e8e HD |
206 | t42->num_of_written_tables++; /* explicitly add glyf */ |
207 | sprintf(buf, TT_offset_table, t42->num_of_written_tables, | |
208 | t42->num_of_written_tables, t42->num_of_written_tables, t42->num_of_written_tables); | |
ae4278ee HD |
209 | |
210 | PSDRV_WriteSpool(physDev, buf, strlen(buf)); | |
211 | ||
14c90e8e HD |
212 | tablepos = 12 + t42->num_of_written_tables * 16; |
213 | cur_off = 12; | |
ae4278ee HD |
214 | for(i = 0; i < num_of_tables; i++) { |
215 | if(!t42->tables[i].write) continue; | |
216 | sprintf(buf, TT_table_dir_entry, FLIP_ORDER(t42->tables[i].MS_tag), | |
217 | t42->tables[i].check, t42->tables[i].len ? tablepos : 0, | |
218 | t42->tables[i].len); | |
219 | PSDRV_WriteSpool(physDev, buf, strlen(buf)); | |
220 | tablepos += ((t42->tables[i].len + 3) & ~3); | |
14c90e8e HD |
221 | if(t42->tables[i].MS_tag == MS_MAKE_TAG('l','o','c','a')) |
222 | loca_off = cur_off; | |
223 | cur_off += 16; | |
ae4278ee | 224 | } |
14c90e8e HD |
225 | sprintf(buf, TT_table_dir_entry, FLIP_ORDER(t42->tables[t42->glyf_tab].MS_tag), |
226 | t42->tables[t42->glyf_tab].check, tablepos, t42->tables[t42->glyf_tab].len); | |
227 | PSDRV_WriteSpool(physDev, buf, strlen(buf)); | |
228 | PSDRV_WriteSpool(physDev, "00>\n", 4); /* add an extra byte for old PostScript rips */ | |
229 | glyf_off = cur_off; | |
ae4278ee HD |
230 | |
231 | for(i = 0; i < num_of_tables; i++) { | |
232 | if(t42->tables[i].len == 0 || !t42->tables[i].write) continue; | |
233 | PSDRV_WriteSpool(physDev, "<", 1); | |
234 | for(j = 0; j < ((t42->tables[i].len + 3) & ~3); j++) { | |
235 | sprintf(buf, "%02x", t42->tables[i].data[j]); | |
236 | PSDRV_WriteSpool(physDev, buf, strlen(buf)); | |
237 | if(j % 16 == 15) PSDRV_WriteSpool(physDev, "\n", 1); | |
238 | } | |
14c90e8e HD |
239 | PSDRV_WriteSpool(physDev, "00>\n", 4); /* add an extra byte for old PostScript rips */ |
240 | } | |
241 | ||
242 | /* glyf_blocks is a 0 terminated list, holding the start offset of each block. For simplicity | |
243 | glyf_blocks[0] is 0 */ | |
244 | nb_blocks = 2; | |
245 | t42->glyf_blocks = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (nb_blocks + 1) * sizeof(DWORD)); | |
246 | for(i = 0; i < GET_BE_WORD(t42->tables[t42->maxp_tab].data + 4); i++) { | |
247 | DWORD start, end, size; | |
248 | get_glyf_pos(t42, i, &start, &end); | |
249 | size = end - t42->glyf_blocks[nb_blocks-2]; | |
250 | if(size > 0x2000 && t42->glyf_blocks[nb_blocks-1] % 4 == 0) { | |
251 | nb_blocks++; | |
252 | t42->glyf_blocks = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, | |
253 | t42->glyf_blocks, (nb_blocks + 1) * sizeof(DWORD)); | |
254 | } | |
255 | t42->glyf_blocks[nb_blocks-1] = end; | |
ae4278ee HD |
256 | } |
257 | ||
14c90e8e HD |
258 | PSDRV_WriteSpool(physDev, "[ ", 2); |
259 | for(i = 1; t42->glyf_blocks[i]; i++) { | |
260 | sprintf(buf,"%ld ", t42->glyf_blocks[i] - t42->glyf_blocks[i-1] + 1); | |
261 | /* again add one byte for old PostScript rips */ | |
262 | PSDRV_WriteSpool(physDev, buf, strlen(buf)); | |
263 | if(i % 8 == 0) | |
264 | PSDRV_WriteSpool(physDev, "\n", 1); | |
265 | } | |
266 | PSDRV_WriteSpool(physDev, storage, sizeof(storage) - 1); | |
267 | sprintf(buf, end, loca_off, glyf_off); | |
268 | PSDRV_WriteSpool(physDev, buf, strlen(buf)); | |
ae4278ee HD |
269 | HeapFree(GetProcessHeap(), 0, buf); |
270 | return t42; | |
271 | } | |
272 | ||
273 | ||
274 | ||
275 | ||
276 | BOOL T42_download_glyph(PSDRV_PDEVICE *physDev, DOWNLOAD *pdl, DWORD index, | |
277 | char *glyph_name) | |
278 | { | |
279 | DWORD start, end, i; | |
280 | char *buf; | |
281 | TYPE42 *t42; | |
ae4278ee HD |
282 | WORD awidth; |
283 | short lsb; | |
5d275962 | 284 | |
b95693c6 | 285 | const char glyph_def[] = |
14c90e8e HD |
286 | "/%s findfont exch 1 index\n" |
287 | "havetype42gdir\n" | |
149bb9e6 | 288 | "{/GlyphDirectory get begin %ld exch def end}\n" |
14c90e8e HD |
289 | "{/sfnts get 4 index get 3 index 2 index putinterval pop}\n" |
290 | "ifelse\n" | |
5d275962 HD |
291 | "/CharStrings get\n" |
292 | "begin\n" | |
149bb9e6 | 293 | " /%s %ld def\n" |
14c90e8e HD |
294 | "end\n" |
295 | "pop pop\n"; | |
ae4278ee HD |
296 | |
297 | TRACE("%ld %s\n", index, glyph_name); | |
298 | assert(pdl->type == Type42); | |
299 | t42 = pdl->typeinfo.Type42; | |
300 | ||
301 | if(index < t42->glyph_sent_size) { | |
302 | if(t42->glyph_sent[index]) | |
303 | return TRUE; | |
304 | } else { | |
305 | t42->glyph_sent_size = (index / GLYPH_SENT_INC + 1) * GLYPH_SENT_INC; | |
306 | t42->glyph_sent = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, | |
307 | t42->glyph_sent, | |
308 | t42->glyph_sent_size * sizeof(*(t42->glyph_sent))); | |
309 | } | |
310 | ||
311 | buf = HeapAlloc(GetProcessHeap(), 0, sizeof(glyph_def) + | |
312 | strlen(pdl->ps_name) + 100); | |
313 | ||
14c90e8e | 314 | if(!get_glyf_pos(t42, index, &start, &end)) return FALSE; |
ae4278ee HD |
315 | TRACE("start = %lx end = %lx\n", start, end); |
316 | ||
317 | awidth = GET_BE_WORD(t42->tables[t42->hmtx_tab].data + index * 4); | |
318 | lsb = GET_BE_WORD(t42->tables[t42->hmtx_tab].data + index * 4 + 2); | |
319 | ||
320 | if(GET_BE_WORD(t42->tables[t42->glyf_tab].data + start) == 0xffff) { | |
321 | /* Composite glyph */ | |
322 | char *sg_start = t42->tables[t42->glyf_tab].data + start + 10; | |
323 | DWORD sg_flags, sg_index; | |
324 | char sg_name[MAX_G_NAME + 1]; | |
325 | ||
326 | do { | |
327 | sg_flags = GET_BE_WORD(sg_start); | |
328 | sg_index = GET_BE_WORD(sg_start + 2); | |
329 | ||
330 | TRACE("Sending subglyph %04lx for glyph %04lx\n", sg_index, index); | |
331 | get_glyph_name(physDev->hdc, sg_index, sg_name); | |
332 | T42_download_glyph(physDev, pdl, sg_index, sg_name); | |
333 | sg_start += 4; | |
334 | if(sg_flags & ARG_1_AND_2_ARE_WORDS) | |
335 | sg_start += 4; | |
336 | else | |
337 | sg_start += 2; | |
338 | if(sg_flags & WE_HAVE_A_SCALE) | |
339 | sg_start += 2; | |
340 | else if(sg_flags & WE_HAVE_AN_X_AND_Y_SCALE) | |
341 | sg_start += 4; | |
342 | else if(sg_flags & WE_HAVE_A_TWO_BY_TWO) | |
343 | sg_start += 8; | |
344 | } while(sg_flags & MORE_COMPONENTS); | |
345 | } | |
346 | ||
14c90e8e HD |
347 | for(i = 1; t42->glyf_blocks[i]; i++) |
348 | if(start < t42->glyf_blocks[i]) break; | |
349 | /* we don't have a string for the gdir and glyf tables, but we do have a | |
350 | string for the TT header. So the offset we need is tables - 2 */ | |
351 | sprintf(buf, "%ld %ld\n", t42->num_of_written_tables - 2 + i, start - t42->glyf_blocks[i-1]); | |
ae4278ee | 352 | PSDRV_WriteSpool(physDev, buf, strlen(buf)); |
14c90e8e | 353 | |
ae4278ee HD |
354 | PSDRV_WriteSpool(physDev, "<", 1); |
355 | for(i = start; i < end; i++) { | |
356 | sprintf(buf, "%02x", *(t42->tables[t42->glyf_tab].data + i)); | |
357 | PSDRV_WriteSpool(physDev, buf, strlen(buf)); | |
358 | if((i - start) % 16 == 15) | |
359 | PSDRV_WriteSpool(physDev, "\n", 1); | |
360 | } | |
361 | PSDRV_WriteSpool(physDev, ">\n", 2); | |
5d275962 | 362 | sprintf(buf, glyph_def, pdl->ps_name, index, glyph_name, index); |
ae4278ee HD |
363 | PSDRV_WriteSpool(physDev, buf, strlen(buf)); |
364 | ||
365 | t42->glyph_sent[index] = TRUE; | |
366 | HeapFree(GetProcessHeap(), 0, buf); | |
367 | return TRUE; | |
368 | } | |
369 | ||
370 | void T42_free(TYPE42 *t42) | |
371 | { | |
372 | OTTable *table; | |
373 | for(table = t42->tables; table->MS_tag; table++) | |
5d275962 HD |
374 | if(table->data) HeapFree(GetProcessHeap(), 0, table->data); |
375 | if(t42->glyph_sent) HeapFree(GetProcessHeap(), 0, t42->glyph_sent); | |
14c90e8e | 376 | if(t42->glyf_blocks) HeapFree(GetProcessHeap(), 0, t42->glyf_blocks); |
ae4278ee HD |
377 | HeapFree(GetProcessHeap(), 0, t42); |
378 | return; | |
379 | } |