xref: /plan9/sys/src/cmd/gs/src/gsfcmap1.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 2002 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: gsfcmap1.c,v 1.7 2004/08/04 19:36:12 stefan Exp $ */
18 /* Adobe-based CMap character decoding */
19 #include "memory_.h"
20 #include "string_.h"
21 #include "gx.h"
22 #include "gserrors.h"
23 #include "gsstruct.h"
24 #include "gsutil.h"		/* for gs_next_ids */
25 #include "gxfcmap1.h"
26 
27 /* Get a big-endian integer. */
28 inline private ulong
bytes2int(const byte * p,int n)29 bytes2int(const byte *p, int n)
30 {
31     ulong v = 0;
32     int i;
33 
34     for (i = 0; i < n; ++i)
35         v = (v << 8) + p[i];
36     return v;
37 }
38 
39 /* ---------------- GC descriptors ---------------- */
40 
41 public_st_cmap_adobe1();
42 /* Because lookup ranges can be elements of arrays, */
43 /* their enum_ptrs procedure must never return 0 prematurely. */
44 private
45 ENUM_PTRS_WITH(cmap_lookup_range_enum_ptrs,
46                gx_cmap_lookup_range_t *pclr) return 0;
47 case 0:
48     if (pclr->value_type == CODE_VALUE_GLYPH) {
49         const byte *pv = pclr->values.data;
50 	int size = pclr->value_size;
51         int k;
52 
53         for (k = 0; k < pclr->num_entries; ++k, pv += size) {
54             gs_glyph glyph = bytes2int(pv, size);
55 
56             pclr->cmap->mark_glyph(mem, glyph, pclr->cmap->mark_glyph_data);
57         }
58     }
59     return ENUM_OBJ(pclr->cmap);
60 case 1: return ENUM_STRING(&pclr->keys);
61 case 2: return ENUM_STRING(&pclr->values);
62 ENUM_PTRS_END
63 private
64 RELOC_PTRS_WITH(cmap_lookup_range_reloc_ptrs, gx_cmap_lookup_range_t *pclr)
65     RELOC_VAR(pclr->cmap);
66     RELOC_STRING_VAR(pclr->keys);
67     RELOC_STRING_VAR(pclr->values);
68 RELOC_PTRS_END
69 public_st_cmap_lookup_range();
70 public_st_cmap_lookup_range_element();
71 
72 /* ---------------- Procedures ---------------- */
73 
74     /* ------ Decoding ------ */
75 
76 /*
77  * multi-dimensional range comparator
78  */
79 
80 private void
print_msg_str_in_range(const byte * str,const byte * key_lo,const byte * key_hi,int key_size)81 print_msg_str_in_range(const byte *str,
82                        const byte *key_lo, const byte *key_hi,
83                        int key_size)
84 {
85     debug_print_string_hex(str, key_size);
86     dlprintf(" in ");
87     debug_print_string_hex(key_lo, key_size);
88     dlprintf(" - ");
89     debug_print_string_hex(key_hi, key_size);
90     dlprintf("\n");
91 }
92 
93 private int
gs_cmap_get_shortest_chr(const gx_code_map_t * pcmap,uint * pfidx)94 gs_cmap_get_shortest_chr(const gx_code_map_t * pcmap, uint *pfidx)
95 {
96     int i;
97     int len_shortest = MAX_CMAP_CODE_SIZE;
98     uint fidx_shortest = 0; /* font index for this fallback */
99 
100     for (i = pcmap->num_lookup - 1; i >= 0; --i) {
101         const gx_cmap_lookup_range_t *pclr = &pcmap->lookup[i];
102         if ((pclr->key_prefix_size + pclr->key_size) <= len_shortest) {
103            len_shortest = (pclr->key_prefix_size + pclr->key_size);
104            fidx_shortest = pclr->font_index;
105         }
106     }
107 
108     *pfidx = fidx_shortest;
109     return len_shortest;
110 }
111 
112 /*
113  * multi-dimensional relative position calculator
114  *
115  * Returns offset of the given CID, considering CID range
116  * as array of CIDs (the last index changes fastest).
117  */
118 private int
gs_multidim_CID_offset(const byte * key_str,const byte * key_lo,const byte * key_hi,int key_size)119 gs_multidim_CID_offset(const byte *key_str,
120                         const byte *key_lo, const byte *key_hi,
121 			int key_size)
122 {
123 
124     int i;	/* index for current dimension */
125     int CID_offset = 0;
126 
127     if (gs_debug_c('J')) {
128         dlprintf("[J]gmCo()         calc CID_offset for 0x");
129         print_msg_str_in_range(key_str, key_lo, key_hi, key_size);
130     }
131 
132     for (i = 0; i < key_size; i++)
133         CID_offset = CID_offset * (key_hi[i] - key_lo[i] + 1) +
134             key_str[i] - key_lo[i];
135 
136     if_debug1('J', "[J]gmCo()         CID_offset = %d\n", CID_offset);
137     return CID_offset;
138 }
139 
140 /*
141  * Decode a character from a string using a code map, updating the index.
142  * Return 0 for a CID or name, N > 0 for a character code where N is the
143  * number of bytes in the code, or an error.  Store the decoded bytes in
144  * *pchr.  For undefined characters, set *pglyph = gs_no_glyph and return 0.
145  */
146 private int
code_map_decode_next_multidim_regime(const gx_code_map_t * pcmap,const gs_const_string * pstr,uint * pindex,uint * pfidx,gs_char * pchr,gs_glyph * pglyph)147 code_map_decode_next_multidim_regime(const gx_code_map_t * pcmap,
148                      const gs_const_string * pstr,
149                      uint * pindex, uint * pfidx,
150                      gs_char * pchr, gs_glyph * pglyph)
151 {
152     const byte *str = pstr->data + *pindex;
153     uint ssize = pstr->size - *pindex;
154     /*
155      * The keys are not sorted due to 'usecmap'.  Possible optimization :
156      * merge and sort keys in 'zbuildcmap', then use binary search here.
157      * This would be valuable for UniJIS-UTF8-H, which contains about 7000
158      * keys.
159      */
160     int i;
161 
162     /*
163      * In the fallback of CMap decoding procedure, there is "partial matching".
164      * For detail, refer PostScript Ref. Manual v3 at the end of Fonts chapter.
165      */
166 
167     /* "pm" stands for partial match (not pointer), temporal use. */
168     int pm_maxlen = 0;		/* partial match: max length */
169     int pm_index = *pindex;	/* partial match: ptr index (in str) */
170     uint pm_fidx = *pfidx;	/* partial match: ptr font index */
171     gs_char pm_chr = *pchr;	/* partial match: ptr character */
172 
173     *pchr = '\0';
174 
175     if (gs_debug_c('J')) {
176         dlprintf("[J]CMDNmr() is called: str=(");
177         debug_print_string_hex(str, ssize);
178         dlprintf3(") @ 0x%lx ssize=%d, %d ranges to check\n",
179                        str, ssize, pcmap->num_lookup);
180     }
181 
182     for (i = pcmap->num_lookup - 1; i >= 0; --i) {
183 	/* main loop - scan the map passed via pcmap */
184 	/* reverse scan order due to 'usecmap' */
185 
186         const gx_cmap_lookup_range_t *pclr = &pcmap->lookup[i];
187         int pre_size = pclr->key_prefix_size, key_size = pclr->key_size,
188             chr_size = pre_size + key_size;
189 
190         int j = 0;
191 	/* length of the given byte stream is shorter than
192          * chr-length of current range, no need for further check,
193          * skip to the next range.
194          */
195         if (ssize < chr_size)
196             continue;
197 
198         if (0 < pre_size) {
199             const byte * prefix = pclr->key_prefix;
200             /* check partial match in prefix */
201             for (j = 0; j < pre_size; j++)
202                if (prefix[j] != str[j])
203                    break;
204 
205             if (0 == j)			/* no match, skip to next i */
206                 continue;
207             else if (j < pre_size) {	/* not exact, partial match */
208                 if (gs_debug_c('J')) {
209                     dlprintf("[J]CMDNmr() partial match with prefix:");
210                     print_msg_str_in_range(str, prefix,
211                                                 prefix, pre_size);
212                 }
213 
214                 if (pm_maxlen < j) {
215                     pm_maxlen = chr_size;
216                     pm_chr = bytes2int(str, chr_size);
217                     pm_index = (*pindex) + chr_size;
218                     pm_fidx = pclr->font_index;
219                 }
220                 continue ; /* no need to check key, skip to next i */
221             }
222 
223             if (gs_debug_c('J')) {
224                 dlprintf("[J]CMDNmr()   full match with prefix:");
225                 print_msg_str_in_range(str, prefix, prefix, pre_size);
226             }
227 
228         } /* if (0 < pre_size) */
229 
230         /* full match in prefix. check key */
231         {
232             const byte *key = pclr->keys.data;
233             int step = key_size;
234             int k, l;
235             const byte *pvalue = NULL;
236 
237 	    /* when range is "range", 2 keys for lo-end and hi-end
238              * are stacked. So twice the step. current "key" points
239              * lo-end of current range, and the pointer for hi-end
240              * is calculated by (key + step - key_size).
241              */
242 
243             if (pclr->key_is_range)
244 		step <<=1; 	/* step = step * 2; */
245 
246             for (k = 0; k < pclr->num_entries; ++k, key += step) {
247 
248                 if_debug0('j', "[j]CMDNmr()     check key:");
249                 if (gs_debug_c('j'))
250                     print_msg_str_in_range(str + pre_size,
251                         key, key + step - key_size, key_size) ;
252 
253                 for (l = 0; l < key_size; l++) {
254                     byte c = str[l + pre_size];
255                     if (c < key[l] || c > key[step - key_size + l])
256                         break;
257                 }
258 
259 		if (pm_maxlen < pre_size + l) {
260                     pm_maxlen = chr_size;
261                     pm_chr = bytes2int(str, chr_size);
262                     pm_index = (*pindex) + chr_size;
263                     pm_fidx = pclr->font_index;
264                 }
265                 if (l == key_size)
266                         break;
267 	    }
268 
269             /* all keys are tried, but found no match. */
270             /* go to next prefix. */
271             if (k == pclr->num_entries)
272                 continue;
273 
274             /* We have a match.  Return the result. */
275             *pchr = bytes2int(str, chr_size);
276             *pindex += chr_size;
277             *pfidx = pclr->font_index;
278             pvalue = pclr->values.data + k * pclr->value_size;
279 
280             if (gs_debug_c('J')) {
281                 dlprintf("[J]CMDNmr()     full matched pvalue=(");
282                 debug_print_string_hex(pvalue, pclr->value_size);
283                 dlprintf(")\n");
284             }
285 
286             switch (pclr->value_type) {
287             case CODE_VALUE_CID:
288                 *pglyph = gs_min_cid_glyph +
289                     bytes2int(pvalue, pclr->value_size) +
290                     gs_multidim_CID_offset(str + pre_size,
291                         key, key + step - key_size, key_size);
292                 return 0;
293             case CODE_VALUE_NOTDEF:
294                 *pglyph = gs_min_cid_glyph +
295                     bytes2int(pvalue, pclr->value_size);
296                 return 0;
297             case CODE_VALUE_GLYPH:
298                 *pglyph = bytes2int(pvalue, pclr->value_size);
299                 return 0;
300             case CODE_VALUE_CHARS:
301                 *pglyph =
302                     bytes2int(pvalue, pclr->value_size) +
303                     bytes2int(str + pre_size, key_size) -
304                     bytes2int(key, key_size);
305                 return pclr->value_size;
306             default:            /* shouldn't happen */
307                 return_error(gs_error_rangecheck);
308             }
309         }
310     }
311     /* No mapping. */
312     *pchr = pm_chr;
313     *pindex = pm_index;
314     *pfidx = pm_fidx;
315     *pglyph = gs_no_glyph;
316     if (gs_debug_c('J')) {
317         dlprintf("[J]CMDNmr()     no full match, use partial match for (");
318         debug_print_string_hex(str, pm_maxlen);
319         dlprintf(")\n");
320     }
321     return 0;
322 }
323 
324 /*
325  * Decode a character from a string using a CMap.
326  * Return like code_map_decode_next.
327  * At present, the range specification by (begin|end)codespacerange
328  * is not used in this function. Therefore, this function accepts
329  * some invalid CMap which def & undef maps exceed the codespacerange.
330  * It should be checked in this function, or some procedure in gs_cmap.ps.
331  */
332 private int
gs_cmap_adobe1_decode_next(const gs_cmap_t * pcmap_in,const gs_const_string * pstr,uint * pindex,uint * pfidx,gs_char * pchr,gs_glyph * pglyph)333 gs_cmap_adobe1_decode_next(const gs_cmap_t * pcmap_in,
334 			   const gs_const_string * pstr,
335 			   uint * pindex, uint * pfidx,
336 			   gs_char * pchr, gs_glyph * pglyph)
337 {
338     const gs_cmap_adobe1_t *pcmap = (const gs_cmap_adobe1_t *)pcmap_in;
339     uint save_index = *pindex;
340     int code;
341 
342     uint pm_index;
343     uint pm_fidx;
344     gs_char pm_chr;
345 
346     /* For first, check defined map */
347     if_debug0('J', "[J]GCDN() check def CMap\n");
348     code =
349         code_map_decode_next_multidim_regime(&pcmap->def, pstr, pindex, pfidx, pchr, pglyph);
350 
351     /* This is defined character */
352     if (code != 0 || *pglyph != gs_no_glyph)
353         return code;
354 
355     /* In here, this is NOT defined character */
356     /* save partially matched results */
357     pm_index = *pindex;
358     pm_fidx = *pfidx;
359     pm_chr = *pchr;
360 
361     /* check notdef map. */
362     if_debug0('J', "[J]GCDN() check notdef CMap\n");
363     *pindex = save_index;
364     code =
365 	code_map_decode_next_multidim_regime(&pcmap->notdef, pstr, pindex, pfidx, pchr, pglyph);
366 
367     /* This is defined "notdef" character. */
368     if (code != 0 || *pglyph != gs_no_glyph)
369         return code;
370 
371     /*
372      * This is undefined in def & undef maps,
373      * use partially matched result with default notdef (CID = 0).
374      */
375     if (save_index < pm_index) {
376 
377 	/* there was some partially matched */
378 
379         *pglyph = gs_min_cid_glyph;	/* CID = 0 */
380         *pindex = pm_index;
381         *pfidx = pm_fidx;
382         *pchr = '\0';
383          return 0; /* should return some error for partial matched .notdef? */
384     }
385     else {
386 	/* no match */
387 
388 	/* Even partial match is failed.
389          * Getting the shortest length from defined characters,
390          * and take the leading bytes (with same length of the shortest
391          * defined chr) as an unidentified character: CID = 0.
392 	 * Also this procedure is specified in PS Ref. Manual v3,
393          * at the end of Fonts chapter.
394          */
395 
396 	const byte *str = pstr->data + save_index;
397 	uint ssize = pstr->size - save_index;
398 	int chr_size_shortest =
399 		gs_cmap_get_shortest_chr(&pcmap->def, pfidx);
400 
401 	if (chr_size_shortest <= ssize) {
402             *pglyph = gs_min_cid_glyph;	/* CID = 0, this is CMap fallback */
403             *pindex = save_index + chr_size_shortest;
404 	    *pchr = '\0';
405             if (gs_debug_c('J')) {
406                 dlprintf1("[J]GCDN() no partial match, skip %d byte (",
407                                                chr_size_shortest);
408                 debug_print_string_hex(str, chr_size_shortest);
409                 dlprintf(")\n");
410             }
411             return 0; /* should return some error for fallback .notdef? */
412 	}
413 	else {
414             /* Undecodable string is shorter than the shortest character,
415              * there's no way except to return error.
416              */
417             if (gs_debug_c('J')) {
418                 dlprintf2("[J]GCDN() left data in buffer (%d) is shorter than shortest defined character (%d)\n",
419                   ssize, chr_size_shortest);
420             }
421             *pglyph = gs_no_glyph;
422             return_error(gs_error_rangecheck);
423 	}
424     }
425 }
426 
427     /* ------ Initialization/creation ------ */
428 
429 /*
430  * Allocate and initialize an Adobe1 CMap.  The caller must still fill in
431  * the code space ranges, lookup tables, keys, and values.
432  */
433 
434 private int
adobe1_next_range(gs_cmap_ranges_enum_t * penum)435 adobe1_next_range(gs_cmap_ranges_enum_t *penum)
436 {
437     const gs_cmap_adobe1_t *const pcmap =
438 	(const gs_cmap_adobe1_t *)penum->cmap;
439 
440     if (penum->index >= pcmap->code_space.num_ranges)
441 	return 1;
442     penum->range = pcmap->code_space.ranges[penum->index++];
443     return 0;
444 }
445 private const gs_cmap_ranges_enum_procs_t adobe1_range_procs = {
446     adobe1_next_range
447 };
448 private void
gs_cmap_adobe1_enum_ranges(const gs_cmap_t * pcmap,gs_cmap_ranges_enum_t * pre)449 gs_cmap_adobe1_enum_ranges(const gs_cmap_t *pcmap, gs_cmap_ranges_enum_t *pre)
450 {
451     gs_cmap_ranges_enum_setup(pre, pcmap, &adobe1_range_procs);
452 }
453 private int
adobe1_next_lookup(gs_cmap_lookups_enum_t * penum,const gx_code_map_t * pcm)454 adobe1_next_lookup(gs_cmap_lookups_enum_t *penum, const gx_code_map_t *pcm)
455 {
456     const gx_cmap_lookup_range_t *lookup = &pcm->lookup[penum->index[0]];
457 
458     if (penum->index[0] >= pcm->num_lookup)
459 	return 1;
460     penum->entry.key_size = lookup->key_prefix_size + lookup->key_size;
461     penum->entry.key_is_range = lookup->key_is_range;
462     penum->entry.value_type = lookup->value_type;
463     penum->entry.value.size = lookup->value_size;
464     penum->entry.font_index = lookup->font_index;
465     penum->index[0]++;
466     penum->index[1] = 0;
467     return 0;
468 }
469 private int
adobe1_next_lookup_def(gs_cmap_lookups_enum_t * penum)470 adobe1_next_lookup_def(gs_cmap_lookups_enum_t *penum)
471 {
472     return adobe1_next_lookup(penum,
473 			&((const gs_cmap_adobe1_t *)penum->cmap)->def);
474 }
475 private int
adobe1_next_lookup_notdef(gs_cmap_lookups_enum_t * penum)476 adobe1_next_lookup_notdef(gs_cmap_lookups_enum_t *penum)
477 {
478     return adobe1_next_lookup(penum,
479 			&((const gs_cmap_adobe1_t *)penum->cmap)->notdef);
480 }
481 private int
adobe1_next_entry(gs_cmap_lookups_enum_t * penum,const gx_code_map_t * pcm)482 adobe1_next_entry(gs_cmap_lookups_enum_t *penum, const gx_code_map_t *pcm)
483 {
484     const gx_cmap_lookup_range_t *lookup = &pcm->lookup[penum->index[0] - 1];
485     int psize = lookup->key_prefix_size;
486     int ksize = lookup->key_size;
487     const byte *key =
488 	lookup->keys.data + penum->index[1] * ksize *
489 	(lookup->key_is_range ? 2 : 1);
490     int i;
491 
492     if (penum->index[1] >= lookup->num_entries)
493 	return 1;
494     if (psize + ksize > MAX_CMAP_CODE_SIZE)
495 	return_error(gs_error_rangecheck);
496     for (i = 0; i < 2; ++i, key += ksize) {
497 	memcpy(penum->entry.key[i], lookup->key_prefix, psize);
498 	memcpy(penum->entry.key[i] + psize, key, ksize);
499     }
500     penum->entry.value.data =
501 	lookup->values.data + penum->index[1] * lookup->value_size;
502     penum->entry.value.size = lookup->value_size;
503     penum->index[1]++;
504     return 0;
505 }
506 private int
adobe1_next_entry_def(gs_cmap_lookups_enum_t * penum)507 adobe1_next_entry_def(gs_cmap_lookups_enum_t *penum)
508 {
509     return adobe1_next_entry(penum,
510 			&((const gs_cmap_adobe1_t *)penum->cmap)->def);
511 }
512 private int
adobe1_next_entry_notdef(gs_cmap_lookups_enum_t * penum)513 adobe1_next_entry_notdef(gs_cmap_lookups_enum_t *penum)
514 {
515     return adobe1_next_entry(penum,
516 			&((const gs_cmap_adobe1_t *)penum->cmap)->notdef);
517 }
518 private const gs_cmap_lookups_enum_procs_t adobe1_lookup_def_procs = {
519     adobe1_next_lookup_def, adobe1_next_entry_def
520 };
521 private const gs_cmap_lookups_enum_procs_t adobe1_lookup_notdef_procs = {
522     adobe1_next_lookup_notdef, adobe1_next_entry_notdef
523 };
524 private void
gs_cmap_adobe1_enum_lookups(const gs_cmap_t * pcmap,int which,gs_cmap_lookups_enum_t * pre)525 gs_cmap_adobe1_enum_lookups(const gs_cmap_t *pcmap, int which,
526 			    gs_cmap_lookups_enum_t *pre)
527 {
528     gs_cmap_lookups_enum_setup(pre, pcmap,
529 			       (which ? &adobe1_lookup_notdef_procs :
530 				&adobe1_lookup_def_procs));
531 }
532 
533 private const gs_cmap_procs_t cmap_adobe1_procs = {
534     gs_cmap_adobe1_decode_next,
535     gs_cmap_adobe1_enum_ranges,
536     gs_cmap_adobe1_enum_lookups,
537     gs_cmap_compute_identity
538 };
539 
540 int
gs_cmap_adobe1_alloc(gs_cmap_adobe1_t ** ppcmap,int wmode,const byte * map_name,uint name_size,uint num_fonts,uint num_ranges,uint num_lookups,uint keys_size,uint values_size,const gs_cid_system_info_t * pcidsi_in,gs_memory_t * mem)541 gs_cmap_adobe1_alloc(gs_cmap_adobe1_t **ppcmap, int wmode,
542 		     const byte *map_name, uint name_size,
543 		     uint num_fonts, uint num_ranges, uint num_lookups,
544 		     uint keys_size, uint values_size,
545 		     const gs_cid_system_info_t *pcidsi_in, gs_memory_t *mem)
546 {
547     gs_cmap_t *pcmap;
548     gs_cmap_adobe1_t *pcmap1;
549     gx_code_space_range_t *ranges = (gx_code_space_range_t *)
550 	gs_alloc_byte_array(mem, num_ranges, sizeof(gx_code_space_range_t),
551 			    "gs_cmap_alloc(code space ranges)");
552     gx_cmap_lookup_range_t *lookups =
553 	(num_lookups == 0 ? NULL :
554 	 gs_alloc_struct_array(mem, num_lookups, gx_cmap_lookup_range_t,
555 			       &st_cmap_lookup_range,
556 			       "gs_cmap_alloc(lookup ranges)"));
557     byte *keys =
558 	(keys_size == 0 ? NULL :
559 	 gs_alloc_string(mem, keys_size, "gs_cmap_alloc(keys)"));
560     byte *values =
561 	(values_size == 0 ? NULL :
562 	 gs_alloc_string(mem, values_size, "gs_cmap_alloc(values)"));
563     int code =
564 	gs_cmap_alloc(&pcmap, &st_cmap_adobe1, wmode, map_name, name_size,
565 		      pcidsi_in, num_fonts, &cmap_adobe1_procs, mem);
566     uint i;
567 
568     if (code < 0 || ranges == 0 || (num_lookups != 0 && lookups == 0) ||
569 	(keys_size != 0 && keys == 0) || (values_size != 0 && values == 0)) {
570 	gs_free_string(mem, values, values_size, "gs_cmap_alloc(values)");
571 	gs_free_string(mem, keys, keys_size, "gs_cmap_alloc(keys)");
572 	gs_free_object(mem, lookups, "gs_cmap_alloc(lookup ranges)");
573 	gs_free_object(mem, ranges, "gs_cmap_alloc(code space ranges)");
574 	return_error(gs_error_VMerror);
575     }
576     *ppcmap = pcmap1 = (gs_cmap_adobe1_t *)pcmap;
577     pcmap1->code_space.ranges = ranges;
578     pcmap1->code_space.num_ranges = num_ranges;
579     if (num_lookups > 0) {
580 	for (i = 0; i < num_lookups; ++i) {
581 	    memset(&lookups[i], 0, sizeof(*lookups));
582 	    lookups[i].cmap = pcmap1;
583 	}
584 	lookups[0].keys.data = keys;
585 	lookups[0].keys.size = keys_size;
586 	lookups[0].values.data = values;
587 	lookups[0].values.size = values_size;
588     }
589     pcmap1->def.lookup = lookups;
590     pcmap1->def.num_lookup = num_lookups;
591     pcmap1->notdef.lookup = 0;
592     pcmap1->notdef.num_lookup = 0;
593     /* no mark_glyph, mark_glyph_data, glyph_name, glyph_name_data */
594     return 0;
595 }
596