xref: /plan9/sys/src/cmd/gs/src/gdevpsfm.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 2000 Aladdin Enterprises.  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: gdevpsfm.c,v 1.15 2004/08/19 19:33:09 stefan Exp $ */
18 /* Write a CMap */
19 #include "gx.h"
20 #include "gserrors.h"
21 #include "gxfcmap.h"
22 #include "stream.h"
23 #include "spprint.h"
24 #include "spsdf.h"
25 #include "gdevpsf.h"
26 #include "memory_.h"
27 
28 /* ---------------- Utilities ---------------- */
29 
30 typedef struct cmap_operators_s {
31     const char *beginchar;
32     const char *endchar;
33     const char *beginrange;
34     const char *endrange;
35 } cmap_operators_t;
36 private const cmap_operators_t
37   cmap_cid_operators = {
38     "begincidchar\n", "endcidchar\n",
39     "begincidrange\n", "endcidrange\n"
40   },
41   cmap_notdef_operators = {
42     "beginnotdefchar\n", "endnotdefchar\n",
43     "beginnotdefrange\n", "endnotdefrange\n"
44   };
45 
46 /* Write a gs_string with a prefix. */
47 private void
pput_string_entry(stream * s,const char * prefix,const gs_const_string * pstr)48 pput_string_entry(stream *s, const char *prefix, const gs_const_string *pstr)
49 {
50     stream_puts(s, prefix);
51     stream_write(s, pstr->data, pstr->size);
52 }
53 
54 /* Write a hex string. */
55 private void
pput_hex(stream * s,const byte * pcid,int size)56 pput_hex(stream *s, const byte *pcid, int size)
57 {
58     int i;
59     static const char *const hex_digits = "0123456789abcdef";
60 
61     for (i = 0; i < size; ++i) {
62 	stream_putc(s, hex_digits[pcid[i] >> 4]);
63 	stream_putc(s, hex_digits[pcid[i] & 0xf]);
64     }
65 }
66 
67 /* Write a list of code space ranges. */
68 private void
cmap_put_ranges(stream * s,const gx_code_space_range_t * pcsr,int count)69 cmap_put_ranges(stream *s, const gx_code_space_range_t *pcsr, int count)
70 {
71     int i;
72 
73     pprintd1(s, "%d begincodespacerange\n", count);
74     for (i = 0; i < count; ++i, ++pcsr) {
75 	stream_puts(s, "<");
76 	pput_hex(s, pcsr->first, pcsr->size);
77 	stream_puts(s, "><");
78 	pput_hex(s, pcsr->last, pcsr->size);
79 	stream_puts(s, ">\n");
80     }
81     stream_puts(s, "endcodespacerange\n");
82 }
83 
84 /* Write one CIDSystemInfo dictionary. */
85 private void
cmap_put_system_info(stream * s,const gs_cid_system_info_t * pcidsi)86 cmap_put_system_info(stream *s, const gs_cid_system_info_t *pcidsi)
87 {
88     if (cid_system_info_is_null(pcidsi)) {
89 	stream_puts(s, " null ");
90     } else {
91 	stream_puts(s, " 3 dict dup begin\n");
92 	stream_puts(s, "/Registry ");
93 	s_write_ps_string(s, pcidsi->Registry.data, pcidsi->Registry.size, 0);
94 	stream_puts(s, " def\n/Ordering ");
95 	s_write_ps_string(s, pcidsi->Ordering.data, pcidsi->Ordering.size, 0);
96 	pprintd1(s, " def\n/Supplement %d def\nend ", pcidsi->Supplement);
97     }
98 }
99 
100 /* Write one code map. */
101 private int
cmap_put_code_map(const gs_memory_t * mem,stream * s,int which,const gs_cmap_t * pcmap,const cmap_operators_t * pcmo,psf_put_name_chars_proc_t put_name_chars,int font_index_only)102 cmap_put_code_map(const gs_memory_t *mem,
103 		  stream *s, int which, const gs_cmap_t *pcmap,
104 		  const cmap_operators_t *pcmo,
105 		  psf_put_name_chars_proc_t put_name_chars,
106 		  int font_index_only)
107 {
108     /* For simplicity, produce one entry for each lookup range. */
109     gs_cmap_lookups_enum_t lenum;
110     int font_index = (pcmap->num_fonts <= 1 ? 0 : -1);
111     int code;
112 
113     for (gs_cmap_lookups_enum_init(pcmap, which, &lenum);
114 	 (code = gs_cmap_enum_next_lookup(&lenum)) == 0; ) {
115 	gs_cmap_lookups_enum_t counter;
116 	int num_entries = 0;
117 	int gi;
118 
119 	if (font_index_only >= 0 && lenum.entry.font_index != font_index_only)
120 	    continue;
121 	if (font_index_only < 0 && lenum.entry.font_index != font_index) {
122 	    pprintd1(s, "%d usefont\n", lenum.entry.font_index);
123 	    font_index = lenum.entry.font_index;
124 	}
125 	/* Count the number of entries in this lookup range. */
126 	counter = lenum;
127 	while (gs_cmap_enum_next_entry(&counter) == 0)
128 	    ++num_entries;
129 	for (gi = 0; gi < num_entries; gi += 100) {
130 	    int i = gi, ni = min(i + 100, num_entries);
131 	    const char *end;
132 
133 	    pprintd1(s, "%d ", ni - i);
134 	    if (lenum.entry.key_is_range) {
135 		if (lenum.entry.value_type == CODE_VALUE_CID || lenum.entry.value_type == CODE_VALUE_NOTDEF) {
136 		    stream_puts(s, pcmo->beginrange);
137 		    end = pcmo->endrange;
138 		} else {	/* must be def, not notdef */
139 		    stream_puts(s, "beginbfrange\n");
140 		    end = "endbfrange\n";
141 		}
142 	    } else {
143 		if (lenum.entry.value_type == CODE_VALUE_CID || lenum.entry.value_type == CODE_VALUE_NOTDEF) {
144 		    stream_puts(s, pcmo->beginchar);
145 		    end = pcmo->endchar;
146 		} else {	/* must be def, not notdef */
147 		    stream_puts(s, "beginbfchar\n");
148 		    end = "endbfchar\n";
149 		}
150 	    }
151 	    for (; i < ni; ++i) {
152 		int j;
153 		long value;
154 		int value_size;
155 
156 		DISCARD(gs_cmap_enum_next_entry(&lenum)); /* can't fail */
157 		value_size = lenum.entry.value.size;
158 		for (j = 0; j <= lenum.entry.key_is_range; ++j) {
159 		    stream_putc(s, '<');
160 		    pput_hex(s, lenum.entry.key[j], lenum.entry.key_size);
161 		    stream_putc(s, '>');
162 		}
163 		for (j = 0, value = 0; j < value_size; ++j)
164 		    value = (value << 8) + lenum.entry.value.data[j];
165 		switch (lenum.entry.value_type) {
166 		case CODE_VALUE_CID:
167 		case CODE_VALUE_NOTDEF:
168 		    pprintld1(s, "%ld", value);
169 		    break;
170 		case CODE_VALUE_CHARS:
171 		    stream_putc(s, '<');
172 		    pput_hex(s, lenum.entry.value.data, value_size);
173 		    stream_putc(s, '>');
174 		    break;
175 		case CODE_VALUE_GLYPH: {
176 		    gs_const_string str;
177 		    int code = pcmap->glyph_name(mem, (gs_glyph)value, &str,
178 						 pcmap->glyph_name_data);
179 
180 		    if (code < 0)
181 			return code;
182 		    stream_putc(s, '/');
183 		    code = put_name_chars(s, str.data, str.size);
184 		    if (code < 0)
185 			return code;
186 		}
187 		    break;
188 		default:	/* not possible */
189 		    return_error(gs_error_unregistered);
190 		}
191 		stream_putc(s, '\n');
192 	    }
193 	    stream_puts(s, end);
194 	}
195     }
196     return code;
197 }
198 
199 /* ---------------- Main program ---------------- */
200 
201 /* Write a CMap in its standard (source) format. */
202 int
psf_write_cmap(const gs_memory_t * mem,stream * s,const gs_cmap_t * pcmap,psf_put_name_chars_proc_t put_name_chars,const gs_const_string * alt_cmap_name,int font_index_only)203 psf_write_cmap(const gs_memory_t *mem,
204 	       stream *s, const gs_cmap_t *pcmap,
205 	       psf_put_name_chars_proc_t put_name_chars,
206 	       const gs_const_string *alt_cmap_name, int font_index_only)
207 {
208     const gs_const_string *const cmap_name =
209 	(alt_cmap_name ? alt_cmap_name : &pcmap->CMapName);
210     const gs_cid_system_info_t *const pcidsi = pcmap->CIDSystemInfo;
211 
212     switch (pcmap->CMapType) {
213     case 0: case 1: case 2:
214 	break;
215     default:
216 	return_error(gs_error_rangecheck);
217     }
218 
219     /* Write the header. */
220 
221     if (!pcmap->ToUnicode) {
222 	stream_puts(s, "%!PS-Adobe-3.0 Resource-CMap\n");
223 	stream_puts(s, "%%DocumentNeededResources: ProcSet (CIDInit)\n");
224 	stream_puts(s, "%%IncludeResource: ProcSet (CIDInit)\n");
225 	pput_string_entry(s, "%%BeginResource: CMap (", cmap_name);
226 	pput_string_entry(s, ")\n%%Title: (", cmap_name);
227 	pput_string_entry(s, " ", &pcidsi->Registry);
228 	pput_string_entry(s, " ", &pcidsi->Ordering);
229 	pprintd1(s, " %d)\n", pcidsi->Supplement);
230 	pprintg1(s, "%%%%Version: %g\n", pcmap->CMapVersion);
231     }
232     stream_puts(s, "/CIDInit /ProcSet findresource begin\n");
233     stream_puts(s, "12 dict begin\nbegincmap\n");
234 
235     /* Write the fixed entries. */
236 
237     pprintd1(s, "/CMapType %d def\n", pcmap->CMapType);
238     if (!pcmap->ToUnicode) {
239 	pprintg1(s, "/CMapVersion %g def\n", pcmap->CMapVersion);
240 	stream_puts(s, "/CMapName/");
241 	put_name_chars(s, cmap_name->data, cmap_name->size);
242 	stream_puts(s, " def\n");
243 	stream_puts(s, "/CIDSystemInfo");
244 	if (font_index_only >= 0 && font_index_only < pcmap->num_fonts) {
245 	    cmap_put_system_info(s, pcidsi + font_index_only);
246 	} else if (pcmap->num_fonts == 1) {
247 	    cmap_put_system_info(s, pcidsi);
248 	} else {
249 	    int i;
250 
251 	    pprintd1(s, " %d array\n", pcmap->num_fonts);
252 	    for (i = 0; i < pcmap->num_fonts; ++i) {
253 		pprintd1(s, "dup %d", i);
254 		cmap_put_system_info(s, pcidsi + i);
255 		stream_puts(s, "put\n");
256 	    }
257 	}
258 	stream_puts(s, " def\n");
259 	if (uid_is_XUID(&pcmap->uid)) {
260 	    uint i, n = uid_XUID_size(&pcmap->uid);
261 	    const long *values = uid_XUID_values(&pcmap->uid);
262 
263 	    stream_puts(s, "/XUID [");
264 	    for (i = 0; i < n; ++i)
265 		pprintld1(s, " %ld", values[i]);
266 	    stream_puts(s, "] def\n");
267 	}
268 	pprintld1(s, "/UIDOffset %ld def\n", pcmap->UIDOffset);
269 	pprintd1(s, "/WMode %d def\n", pcmap->WMode);
270     }
271 
272     /* Write the code space ranges. */
273 
274     {
275 	gs_cmap_ranges_enum_t renum;
276 #define MAX_RANGES 100
277 	gx_code_space_range_t ranges[MAX_RANGES];
278 	int code, count = 0;
279 
280 	for (gs_cmap_ranges_enum_init(pcmap, &renum);
281 	     (code = gs_cmap_enum_next_range(&renum)) == 0; ) {
282 	    if (count == MAX_RANGES) {
283 		cmap_put_ranges(s, ranges, count);
284 		count = 0;
285 	    }
286 	    ranges[count++] = renum.range;
287 	}
288 	if (code < 0)
289 	    return code;
290 	if (count)
291 	    cmap_put_ranges(s, ranges, count);
292 #undef MAX_RANGES
293     }
294 
295     /* Write the code and notdef data. */
296 
297     {
298 	int code;
299 
300 	code = cmap_put_code_map(mem, s, 1, pcmap, &cmap_notdef_operators,
301 			         put_name_chars, font_index_only);
302 	if (code < 0)
303 	    return code;
304 	code = cmap_put_code_map(mem, s, 0, pcmap, &cmap_cid_operators,
305 			         put_name_chars, font_index_only);
306 	if (code < 0)
307 	    return code;
308     }
309 
310     /* Write the trailer. */
311 
312     stream_puts(s, "endcmap\n");
313     stream_puts(s, "CMapName currentdict /CMap defineresource pop\nend end\n");
314     if (!pcmap->ToUnicode) {
315 	stream_puts(s, "%%EndResource\n");
316 	stream_puts(s, "%%EOF\n");
317     }
318 
319     return 0;
320 }
321 
322