]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/transcode.c
aab8416428b58a794d26ae4259a73e0cad735dc8
[thirdparty/cups.git] / cups / transcode.c
1 /*
2 * "$Id: transcode.c 6649 2007-07-11 21:46:42Z mike $"
3 *
4 * Transcoding support for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
19 * _cupsCharmapFlush() - Flush all character set maps out of cache.
20 * _cupsCharmapFree() - Free a character set map.
21 * _cupsCharmapGet() - Get a character set map.
22 * cupsCharsetToUTF8() - Convert legacy character set to UTF-8.
23 * cupsUTF8ToCharset() - Convert UTF-8 to legacy character set.
24 * cupsUTF8ToUTF32() - Convert UTF-8 to UTF-32.
25 * cupsUTF32ToUTF8() - Convert UTF-32 to UTF-8.
26 * compare_wide() - Compare key for wide (VBCS) match.
27 * conv_sbcs_to_utf8() - Convert legacy SBCS to UTF-8.
28 * conv_utf8_to_sbcs() - Convert UTF-8 to legacy SBCS.
29 * conv_utf8_to_vbcs() - Convert UTF-8 to legacy DBCS/VBCS.
30 * conv_vbcs_to_utf8() - Convert legacy DBCS/VBCS to UTF-8.
31 * free_sbcs_charmap() - Free memory used by a single byte character set.
32 * free_vbcs_charmap() - Free memory used by a variable byte character set.
33 * get_charmap() - Lookup or get a character set map (private).
34 * get_charmap_count() - Count lines in a charmap file.
35 * get_sbcs_charmap() - Get SBCS Charmap.
36 * get_vbcs_charmap() - Get DBCS/VBCS Charmap.
37 */
38
39 /*
40 * Include necessary headers...
41 */
42
43 #include "globals.h"
44 #include "debug.h"
45 #include <limits.h>
46 #include <stdlib.h>
47 #include <errno.h>
48 #include <time.h>
49
50
51 /*
52 * Local globals...
53 */
54
55 #ifdef HAVE_PTHREAD_H
56 static pthread_mutex_t map_mutex = PTHREAD_MUTEX_INITIALIZER;
57 /* Mutex to control access to maps */
58 #endif /* HAVE_PTHREAD_H */
59 static _cups_cmap_t *cmap_cache = NULL;
60 /* SBCS Charmap Cache */
61 static _cups_vmap_t *vmap_cache = NULL;
62 /* VBCS Charmap Cache */
63
64
65 /*
66 * Local functions...
67 */
68
69 static int compare_wide(const void *k1, const void *k2);
70 static int conv_sbcs_to_utf8(cups_utf8_t *dest,
71 const cups_sbcs_t *src,
72 int maxout,
73 const cups_encoding_t encoding);
74 static int conv_utf8_to_sbcs(cups_sbcs_t *dest,
75 const cups_utf8_t *src,
76 int maxout,
77 const cups_encoding_t encoding);
78 static int conv_utf8_to_vbcs(cups_sbcs_t *dest,
79 const cups_utf8_t *src,
80 int maxout,
81 const cups_encoding_t encoding);
82 static int conv_vbcs_to_utf8(cups_utf8_t *dest,
83 const cups_sbcs_t *src,
84 int maxout,
85 const cups_encoding_t encoding);
86 static void free_sbcs_charmap(_cups_cmap_t *sbcs);
87 static void free_vbcs_charmap(_cups_vmap_t *vbcs);
88 static void *get_charmap(const cups_encoding_t encoding);
89 static int get_charmap_count(cups_file_t *fp);
90 static _cups_cmap_t *get_sbcs_charmap(const cups_encoding_t encoding,
91 const char *filename);
92 static _cups_vmap_t *get_vbcs_charmap(const cups_encoding_t encoding,
93 const char *filename);
94
95
96 /*
97 * '_cupsCharmapFlush()' - Flush all character set maps out of cache.
98 */
99
100 void
101 _cupsCharmapFlush(void)
102 {
103 _cups_cmap_t *cmap, /* Legacy SBCS / Unicode Charset Map */
104 *cnext; /* Next Legacy SBCS Charset Map */
105 _cups_vmap_t *vmap, /* Legacy VBCS / Unicode Charset Map */
106 *vnext; /* Next Legacy VBCS Charset Map */
107
108
109 #ifdef HAVE_PTHREAD_H
110 pthread_mutex_lock(&map_mutex);
111 #endif /* HAVE_PTHREAD_H */
112
113 /*
114 * Loop through SBCS charset map cache, free all memory...
115 */
116
117 for (cmap = cmap_cache; cmap; cmap = cnext)
118 {
119 cnext = cmap->next;
120
121 free_sbcs_charmap(cmap);
122 }
123
124 cmap_cache = NULL;
125
126 /*
127 * Loop through DBCS/VBCS charset map cache, free all memory...
128 */
129
130 for (vmap = vmap_cache; vmap; vmap = vnext)
131 {
132 vnext = vmap->next;
133
134 free_vbcs_charmap(vmap);
135
136 free(vmap);
137 }
138
139 vmap_cache = NULL;
140
141 #ifdef HAVE_PTHREAD_H
142 pthread_mutex_unlock(&map_mutex);
143 #endif /* HAVE_PTHREAD_H */
144 }
145
146
147 /*
148 * '_cupsCharmapFree()' - Free a character set map.
149 *
150 * This does not actually free; use '_cupsCharmapFlush()' for that.
151 */
152
153 void
154 _cupsCharmapFree(
155 const cups_encoding_t encoding) /* I - Encoding */
156 {
157 _cups_cmap_t *cmap; /* Legacy SBCS / Unicode Charset Map */
158 _cups_vmap_t *vmap; /* Legacy VBCS / Unicode Charset Map */
159
160
161 /*
162 * See if we already have this SBCS charset map loaded...
163 */
164
165 #ifdef HAVE_PTHREAD_H
166 pthread_mutex_lock(&map_mutex);
167 #endif /* HAVE_PTHREAD_H */
168
169 for (cmap = cmap_cache; cmap; cmap = cmap->next)
170 {
171 if (cmap->encoding == encoding)
172 {
173 if (cmap->used > 0)
174 cmap->used --;
175 break;
176 }
177 }
178
179 /*
180 * See if we already have this DBCS/VBCS charset map loaded...
181 */
182
183 for (vmap = vmap_cache; vmap; vmap = vmap->next)
184 {
185 if (vmap->encoding == encoding)
186 {
187 if (vmap->used > 0)
188 vmap->used --;
189 break;
190 }
191 }
192
193 #ifdef HAVE_PTHREAD_H
194 pthread_mutex_unlock(&map_mutex);
195 #endif /* HAVE_PTHREAD_H */
196 }
197
198
199 /*
200 * '_cupsCharmapGet()' - Get a character set map.
201 *
202 * This code handles single-byte (SBCS), double-byte (DBCS), and
203 * variable-byte (VBCS) character sets _without_ charset escapes...
204 * This code does not handle multiple-byte character sets (MBCS)
205 * (such as ISO-2022-JP) with charset switching via escapes...
206 */
207
208 void * /* O - Charset map pointer */
209 _cupsCharmapGet(
210 const cups_encoding_t encoding) /* I - Encoding */
211 {
212 void *charmap; /* Charset map pointer */
213
214
215 DEBUG_printf(("_cupsCharmapGet(encoding=%d)\n", encoding));
216
217 /*
218 * Check for valid arguments...
219 */
220
221 if (encoding < 0 || encoding >= CUPS_ENCODING_VBCS_END)
222 {
223 DEBUG_puts(" Bad encoding, returning NULL!");
224 return (NULL);
225 }
226
227 /*
228 * Lookup or get the charset map pointer and return...
229 */
230
231 #ifdef HAVE_PTHREAD_H
232 pthread_mutex_lock(&map_mutex);
233 #endif /* HAVE_PTHREAD_H */
234
235 charmap = get_charmap(encoding);
236
237 #ifdef HAVE_PTHREAD_H
238 pthread_mutex_unlock(&map_mutex);
239 #endif /* HAVE_PTHREAD_H */
240
241 return (charmap);
242 }
243
244
245 /*
246 * 'cupsCharsetToUTF8()' - Convert legacy character set to UTF-8.
247 *
248 * This code handles single-byte (SBCS), double-byte (DBCS), and
249 * variable-byte (VBCS) character sets _without_ charset escapes...
250 * This code does not handle multiple-byte character sets (MBCS)
251 * (such as ISO-2022-JP) with charset switching via escapes...
252 */
253
254 int /* O - Count or -1 on error */
255 cupsCharsetToUTF8(
256 cups_utf8_t *dest, /* O - Target string */
257 const char *src, /* I - Source string */
258 const int maxout, /* I - Max output */
259 const cups_encoding_t encoding) /* I - Encoding */
260 {
261 int bytes; /* Number of bytes converted */
262
263
264 /*
265 * Check for valid arguments...
266 */
267
268 DEBUG_printf(("cupsCharsetToUTF8(dest=%p, src=\"%s\", maxout=%d, encoding=%d)\n",
269 dest, src, maxout, encoding));
270
271 if (dest)
272 *dest = '\0';
273
274 if (!dest || !src || maxout < 1 || maxout > CUPS_MAX_USTRING)
275 {
276 DEBUG_puts(" Bad arguments, returning -1");
277 return (-1);
278 }
279
280 /*
281 * Handle identity conversions...
282 */
283
284 if (encoding == CUPS_UTF8 ||
285 encoding < 0 || encoding >= CUPS_ENCODING_VBCS_END)
286 {
287 strlcpy((char *)dest, src, maxout);
288 return ((int)strlen((char *)dest));
289 }
290
291 /*
292 * Handle ISO-8859-1 to UTF-8 directly...
293 */
294
295 if (encoding == CUPS_ISO8859_1)
296 {
297 int ch; /* Character from string */
298 cups_utf8_t *destptr, /* Pointer into UTF-8 buffer */
299 *destend; /* End of UTF-8 buffer */
300
301
302 destptr = dest;
303 destend = dest + maxout - 2;
304
305 while (*src && destptr < destend)
306 {
307 ch = *src++ & 255;
308
309 if (ch & 128)
310 {
311 *destptr++ = 0xc0 | (ch >> 6);
312 *destptr++ = 0x80 | (ch & 0x3f);
313 }
314 else
315 *destptr++ = ch;
316 }
317
318 *destptr = '\0';
319
320 return ((int)(destptr - dest));
321 }
322
323 /*
324 * Convert input legacy charset to UTF-8...
325 */
326
327 #ifdef HAVE_PTHREAD_H
328 pthread_mutex_lock(&map_mutex);
329 #endif /* HAVE_PTHREAD_H */
330
331 if (encoding < CUPS_ENCODING_SBCS_END)
332 bytes = conv_sbcs_to_utf8(dest, (cups_sbcs_t *)src, maxout, encoding);
333 else if (encoding < CUPS_ENCODING_VBCS_END)
334 bytes = conv_vbcs_to_utf8(dest, (cups_sbcs_t *)src, maxout, encoding);
335 else
336 {
337 DEBUG_puts(" Bad encoding, returning -1");
338 bytes = -1;
339 }
340
341 #ifdef HAVE_PTHREAD_H
342 pthread_mutex_unlock(&map_mutex);
343 #endif /* HAVE_PTHREAD_H */
344
345 return (bytes);
346 }
347
348
349 /*
350 * 'cupsUTF8ToCharset()' - Convert UTF-8 to legacy character set.
351 *
352 * This code handles single-byte (SBCS), double-byte (DBCS), and
353 * variable-byte (VBCS) character sets _without_ charset escapes...
354 * This code does not handle multiple-byte character sets (MBCS)
355 * (such as ISO-2022-JP) with charset switching via escapes...
356 */
357
358 int /* O - Count or -1 on error */
359 cupsUTF8ToCharset(
360 char *dest, /* O - Target string */
361 const cups_utf8_t *src, /* I - Source string */
362 const int maxout, /* I - Max output */
363 const cups_encoding_t encoding) /* I - Encoding */
364 {
365 int bytes; /* Number of bytes converted */
366
367
368 /*
369 * Check for valid arguments...
370 */
371
372 if (!dest || !src || maxout < 1 || maxout > CUPS_MAX_USTRING)
373 {
374 if (dest)
375 *dest = '\0';
376
377 return (-1);
378 }
379
380 /*
381 * Handle identity conversions...
382 */
383
384 if (encoding == CUPS_UTF8 ||
385 encoding < 0 || encoding >= CUPS_ENCODING_VBCS_END)
386 {
387 strlcpy(dest, (char *)src, maxout);
388 return ((int)strlen(dest));
389 }
390
391 /*
392 * Handle UTF-8 to ISO-8859-1 directly...
393 */
394
395 if (encoding == CUPS_ISO8859_1)
396 {
397 int ch; /* Character from string */
398 char *destptr, /* Pointer into ISO-8859-1 buffer */
399 *destend; /* End of ISO-8859-1 buffer */
400
401
402 destptr = dest;
403 destend = dest + maxout - 1;
404
405 while (*src && destptr < destend)
406 {
407 ch = *src++;
408
409 if ((ch & 0xe0) == 0xc0)
410 {
411 ch = ((ch & 0x1f) << 6) | (*src++ & 0x3f);
412
413 if (ch < 256)
414 *destptr++ = ch;
415 else
416 *destptr++ = '?';
417 }
418 else if ((ch & 0xf0) == 0xe0 ||
419 (ch & 0xf8) == 0xf0)
420 *destptr++ = '?';
421 else if (!(ch & 0x80))
422 *destptr++ = ch;
423 }
424
425 *destptr = '\0';
426
427 return ((int)(destptr - dest));
428 }
429
430 /*
431 * Convert input UTF-8 to legacy charset...
432 */
433
434 #ifdef HAVE_PTHREAD_H
435 pthread_mutex_lock(&map_mutex);
436 #endif /* HAVE_PTHREAD_H */
437
438 if (encoding < CUPS_ENCODING_SBCS_END)
439 bytes = conv_utf8_to_sbcs((cups_sbcs_t *)dest, src, maxout, encoding);
440 else if (encoding < CUPS_ENCODING_VBCS_END)
441 bytes = conv_utf8_to_vbcs((cups_sbcs_t *)dest, src, maxout, encoding);
442 else
443 bytes = -1;
444
445 #ifdef HAVE_PTHREAD_H
446 pthread_mutex_unlock(&map_mutex);
447 #endif /* HAVE_PTHREAD_H */
448
449 return (bytes);
450 }
451
452
453 /*
454 * 'cupsUTF8ToUTF32()' - Convert UTF-8 to UTF-32.
455 *
456 * 32-bit UTF-32 (actually 21-bit) maps to UTF-8 as follows...
457 *
458 * UTF-32 char UTF-8 char(s)
459 * --------------------------------------------------
460 * 0 to 127 = 0xxxxxxx (US-ASCII)
461 * 128 to 2047 = 110xxxxx 10yyyyyy
462 * 2048 to 65535 = 1110xxxx 10yyyyyy 10zzzzzz
463 * > 65535 = 11110xxx 10yyyyyy 10zzzzzz 10xxxxxx
464 *
465 * UTF-32 prohibits chars beyond Plane 16 (> 0x10ffff) in UCS-4,
466 * which would convert to five- or six-octet UTF-8 sequences...
467 */
468
469 int /* O - Count or -1 on error */
470 cupsUTF8ToUTF32(
471 cups_utf32_t *dest, /* O - Target string */
472 const cups_utf8_t *src, /* I - Source string */
473 const int maxout) /* I - Max output */
474 {
475 int i; /* Looping variable */
476 cups_utf8_t ch; /* Character value */
477 cups_utf8_t next; /* Next character value */
478 cups_utf32_t ch32; /* UTF-32 character value */
479
480
481 /*
482 * Check for valid arguments and clear output...
483 */
484
485 if (dest)
486 *dest = 0;
487
488 if (!dest || !src || maxout < 1 || maxout > CUPS_MAX_USTRING)
489 return (-1);
490
491 /*
492 * Convert input UTF-8 to output UTF-32 (and insert BOM)...
493 */
494
495 *dest++ = 0xfeff;
496
497 for (i = maxout - 1; *src && i > 0; i --)
498 {
499 ch = *src++;
500
501 /*
502 * Convert UTF-8 character(s) to UTF-32 character...
503 */
504
505 if (!(ch & 0x80))
506 {
507 /*
508 * One-octet UTF-8 <= 127 (US-ASCII)...
509 */
510
511 *dest++ = ch;
512 continue;
513 }
514 else if ((ch & 0xe0) == 0xc0)
515 {
516 /*
517 * Two-octet UTF-8 <= 2047 (Latin-x)...
518 */
519
520 next = *src++;
521 if (!next)
522 return (-1);
523
524 ch32 = ((ch & 0x1f) << 6) | (next & 0x3f);
525
526 /*
527 * Check for non-shortest form (invalid UTF-8)...
528 */
529
530 if (ch32 < 0x80)
531 return (-1);
532
533 *dest++ = ch32;
534 }
535 else if ((ch & 0xf0) == 0xe0)
536 {
537 /*
538 * Three-octet UTF-8 <= 65535 (Plane 0 - BMP)...
539 */
540
541 next = *src++;
542 if (!next)
543 return (-1);
544
545 ch32 = ((ch & 0x0f) << 6) | (next & 0x3f);
546
547 next = *src++;
548 if (!next)
549 return (-1);
550
551 ch32 = (ch32 << 6) | (next & 0x3f);
552
553 /*
554 * Check for non-shortest form (invalid UTF-8)...
555 */
556
557 if (ch32 < 0x800)
558 return (-1);
559
560 *dest++ = ch32;
561 }
562 else if ((ch & 0xf8) == 0xf0)
563 {
564 /*
565 * Four-octet UTF-8...
566 */
567
568 next = *src++;
569 if (!next)
570 return (-1);
571
572 ch32 = ((ch & 0x07) << 6) | (next & 0x3f);
573
574 next = *src++;
575 if (!next)
576 return (-1);
577
578 ch32 = (ch32 << 6) | (next & 0x3f);
579
580 next = *src++;
581 if (!next)
582 return (-1);
583
584 ch32 = (ch32 << 6) | (next & 0x3f);
585
586 /*
587 * Check for non-shortest form (invalid UTF-8)...
588 */
589
590 if (ch32 < 0x10000)
591 return (-1);
592
593 *dest++ = ch32;
594 }
595 else
596 {
597 /*
598 * More than 4-octet (invalid UTF-8 sequence)...
599 */
600
601 return (-1);
602 }
603
604 /*
605 * Check for UTF-16 surrogate (illegal UTF-8)...
606 */
607
608 if (ch32 >= 0xd800 && ch32 <= 0xdfff)
609 return (-1);
610 }
611
612 *dest = 0;
613
614 return (i);
615 }
616
617
618 /*
619 * 'cupsUTF32ToUTF8()' - Convert UTF-32 to UTF-8.
620 *
621 * 32-bit UTF-32 (actually 21-bit) maps to UTF-8 as follows...
622 *
623 * UTF-32 char UTF-8 char(s)
624 * --------------------------------------------------
625 * 0 to 127 = 0xxxxxxx (US-ASCII)
626 * 128 to 2047 = 110xxxxx 10yyyyyy
627 * 2048 to 65535 = 1110xxxx 10yyyyyy 10zzzzzz
628 * > 65535 = 11110xxx 10yyyyyy 10zzzzzz 10xxxxxx
629 *
630 * UTF-32 prohibits chars beyond Plane 16 (> 0x10ffff) in UCS-4,
631 * which would convert to five- or six-octet UTF-8 sequences...
632 */
633
634 int /* O - Count or -1 on error */
635 cupsUTF32ToUTF8(
636 cups_utf8_t *dest, /* O - Target string */
637 const cups_utf32_t *src, /* I - Source string */
638 const int maxout) /* I - Max output */
639 {
640 cups_utf8_t *start; /* Start of destination string */
641 int i; /* Looping variable */
642 int swap; /* Byte-swap input to output */
643 cups_utf32_t ch; /* Character value */
644
645
646 /*
647 * Check for valid arguments and clear output...
648 */
649
650 if (dest)
651 *dest = '\0';
652
653 if (!dest || !src || maxout < 1)
654 return (-1);
655
656 /*
657 * Check for leading BOM in UTF-32 and inverted BOM...
658 */
659
660 start = dest;
661 swap = *src == 0xfffe0000;
662
663 if (*src == 0xfffe0000 || *src == 0xfeff)
664 src ++;
665
666 /*
667 * Convert input UTF-32 to output UTF-8...
668 */
669
670 for (i = maxout - 1; *src && i > 0;)
671 {
672 ch = *src++;
673
674 /*
675 * Byte swap input UTF-32, if necessary...
676 * (only byte-swapping 24 of 32 bits)
677 */
678
679 if (swap)
680 ch = ((ch >> 24) | ((ch >> 8) & 0xff00) | ((ch << 8) & 0xff0000));
681
682 /*
683 * Check for beyond Plane 16 (invalid UTF-32)...
684 */
685
686 if (ch > 0x10ffff)
687 return (-1);
688
689 /*
690 * Convert UTF-32 character to UTF-8 character(s)...
691 */
692
693 if (ch < 0x80)
694 {
695 /*
696 * One-octet UTF-8 <= 127 (US-ASCII)...
697 */
698
699 *dest++ = (cups_utf8_t)ch;
700 i --;
701 }
702 else if (ch < 0x800)
703 {
704 /*
705 * Two-octet UTF-8 <= 2047 (Latin-x)...
706 */
707
708 if (i < 2)
709 return (-1);
710
711 *dest++ = (cups_utf8_t)(0xc0 | ((ch >> 6) & 0x1f));
712 *dest++ = (cups_utf8_t)(0x80 | (ch & 0x3f));
713 i -= 2;
714 }
715 else if (ch < 0x10000)
716 {
717 /*
718 * Three-octet UTF-8 <= 65535 (Plane 0 - BMP)...
719 */
720
721 if (i < 3)
722 return (-1);
723
724 *dest++ = (cups_utf8_t)(0xe0 | ((ch >> 12) & 0x0f));
725 *dest++ = (cups_utf8_t)(0x80 | ((ch >> 6) & 0x3f));
726 *dest++ = (cups_utf8_t)(0x80 | (ch & 0x3f));
727 i -= 3;
728 }
729 else
730 {
731 /*
732 * Four-octet UTF-8...
733 */
734
735 if (i < 4)
736 return (-1);
737
738 *dest++ = (cups_utf8_t)(0xf0 | ((ch >> 18) & 0x07));
739 *dest++ = (cups_utf8_t)(0x80 | ((ch >> 12) & 0x3f));
740 *dest++ = (cups_utf8_t)(0x80 | ((ch >> 6) & 0x3f));
741 *dest++ = (cups_utf8_t)(0x80 | (ch & 0x3f));
742 i -= 4;
743 }
744 }
745
746 *dest = '\0';
747
748 return ((int)(dest - start));
749 }
750
751
752 /*
753 * 'compare_wide()' - Compare key for wide (VBCS) match.
754 */
755
756 static int
757 compare_wide(const void *k1, /* I - Key char */
758 const void *k2) /* I - Map char */
759 {
760 cups_vbcs_t key; /* Legacy key character */
761 cups_vbcs_t map; /* Legacy map character */
762
763
764 key = *((cups_vbcs_t *)k1);
765 map = ((_cups_wide2uni_t *)k2)->widechar;
766
767 return ((int)(key - map));
768 }
769
770
771 /*
772 * 'conv_sbcs_to_utf8()' - Convert legacy SBCS to UTF-8.
773 */
774
775 static int /* O - Count or -1 on error */
776 conv_sbcs_to_utf8(
777 cups_utf8_t *dest, /* O - Target string */
778 const cups_sbcs_t *src, /* I - Source string */
779 int maxout, /* I - Max output */
780 const cups_encoding_t encoding) /* I - Encoding */
781 {
782 _cups_cmap_t *cmap; /* Legacy SBCS / Unicode Charset Map */
783 cups_ucs2_t *crow; /* Pointer to UCS-2 row in 'char2uni' */
784 cups_sbcs_t legchar; /* Legacy character value */
785 cups_utf32_t work[CUPS_MAX_USTRING], /* Internal UCS-4 string */
786 *workptr; /* Pointer into string */
787
788
789 /*
790 * Find legacy charset map in cache...
791 */
792
793 if ((cmap = (_cups_cmap_t *)get_charmap(encoding)) == NULL)
794 return (-1);
795
796 /*
797 * Convert input legacy charset to internal UCS-4 (and insert BOM)...
798 */
799
800 work[0] = 0xfeff;
801 for (workptr = work + 1; *src && workptr < (work + CUPS_MAX_USTRING - 1);)
802 {
803 legchar = *src++;
804
805 /*
806 * Convert ASCII verbatim (optimization)...
807 */
808
809 if (legchar < 0x80)
810 *workptr++ = (cups_utf32_t)legchar;
811 else
812 {
813 /*
814 * Convert unknown character to Replacement Character...
815 */
816
817 crow = cmap->char2uni + legchar;
818
819 if (!*crow)
820 *workptr++ = 0xfffd;
821 else
822 *workptr++ = (cups_utf32_t)*crow;
823 }
824 }
825
826 *workptr = 0;
827
828 /*
829 * Convert internal UCS-4 to output UTF-8 (and delete BOM)...
830 */
831
832 cmap->used --;
833
834 return (cupsUTF32ToUTF8(dest, work, maxout));
835 }
836
837
838 /*
839 * 'conv_utf8_to_sbcs()' - Convert UTF-8 to legacy SBCS.
840 */
841
842 static int /* O - Count or -1 on error */
843 conv_utf8_to_sbcs(
844 cups_sbcs_t *dest, /* O - Target string */
845 const cups_utf8_t *src, /* I - Source string */
846 int maxout, /* I - Max output */
847 const cups_encoding_t encoding) /* I - Encoding */
848 {
849 cups_sbcs_t *start; /* Start of destination string */
850 _cups_cmap_t *cmap; /* Legacy SBCS / Unicode Charset Map */
851 cups_sbcs_t *srow; /* Pointer to SBCS row in 'uni2char' */
852 cups_utf32_t unichar; /* Character value */
853 cups_utf32_t work[CUPS_MAX_USTRING], /* Internal UCS-4 string */
854 *workptr; /* Pointer into string */
855
856
857 /*
858 * Find legacy charset map in cache...
859 */
860
861 if ((cmap = (_cups_cmap_t *)get_charmap(encoding)) == NULL)
862 return (-1);
863
864 /*
865 * Convert input UTF-8 to internal UCS-4 (and insert BOM)...
866 */
867
868 if (cupsUTF8ToUTF32(work, src, CUPS_MAX_USTRING) < 0)
869 return (-1);
870
871 /*
872 * Convert internal UCS-4 to SBCS legacy charset (and delete BOM)...
873 */
874
875 for (workptr = work + 1, start = dest; *workptr && maxout > 1; maxout --)
876 {
877 unichar = *workptr++;
878 if (!unichar)
879 break;
880
881 /*
882 * Convert ASCII verbatim (optimization)...
883 */
884
885 if (unichar < 0x80)
886 {
887 *dest++ = (cups_sbcs_t)unichar;
888 continue;
889 }
890
891 /*
892 * Convert unknown character to visible replacement...
893 */
894
895 srow = cmap->uni2char[(int)((unichar >> 8) & 0xff)];
896
897 if (srow)
898 srow += (int)(unichar & 0xff);
899
900 if (!srow || !*srow)
901 *dest++ = '?';
902 else
903 *dest++ = *srow;
904 }
905
906 *dest = '\0';
907
908 cmap->used --;
909
910 return ((int)(dest - start));
911 }
912
913
914 /*
915 * 'conv_utf8_to_vbcs()' - Convert UTF-8 to legacy DBCS/VBCS.
916 */
917
918 static int /* O - Count or -1 on error */
919 conv_utf8_to_vbcs(
920 cups_sbcs_t *dest, /* O - Target string */
921 const cups_utf8_t *src, /* I - Source string */
922 int maxout, /* I - Max output */
923 const cups_encoding_t encoding) /* I - Encoding */
924 {
925 cups_sbcs_t *start; /* Start of destination string */
926 _cups_vmap_t *vmap; /* Legacy DBCS / Unicode Charset Map */
927 cups_vbcs_t *vrow; /* Pointer to VBCS row in 'uni2char' */
928 cups_utf32_t unichar; /* Character value */
929 cups_vbcs_t legchar; /* Legacy character value */
930 cups_utf32_t work[CUPS_MAX_USTRING], /* Internal UCS-4 string */
931 *workptr; /* Pointer into string */
932
933
934 /*
935 * Find legacy charset map in cache...
936 */
937
938 if ((vmap = (_cups_vmap_t *)get_charmap(encoding)) == NULL)
939 return (-1);
940
941 /*
942 * Convert input UTF-8 to internal UCS-4 (and insert BOM)...
943 */
944
945 if (cupsUTF8ToUTF32(work, src, CUPS_MAX_USTRING) < 0)
946 return (-1);
947
948 /*
949 * Convert internal UCS-4 to VBCS legacy charset (and delete BOM)...
950 */
951
952 for (start = dest, workptr = work + 1; *workptr && maxout > 1; maxout --)
953 {
954 unichar = *workptr++;
955 if (!unichar)
956 break;
957
958 /*
959 * Convert ASCII verbatim (optimization)...
960 */
961
962 if (unichar < 0x80)
963 {
964 *dest++ = (cups_sbcs_t)unichar;
965 continue;
966 }
967
968 /*
969 * Convert unknown character to visible replacement...
970 */
971
972 vrow = vmap->uni2char[(int)((unichar >> 8) & 0xff)];
973
974 if (vrow)
975 vrow += (int)(unichar & 0xff);
976
977 if (!vrow || !*vrow)
978 legchar = (cups_vbcs_t)'?';
979 else
980 legchar = (cups_vbcs_t)*vrow;
981
982 /*
983 * Save n-byte legacy character...
984 */
985
986 if (legchar > 0xffffff)
987 {
988 if (maxout < 5)
989 return (-1);
990
991 *dest++ = (cups_sbcs_t)(legchar >> 24);
992 *dest++ = (cups_sbcs_t)(legchar >> 16);
993 *dest++ = (cups_sbcs_t)(legchar >> 8);
994 *dest++ = (cups_sbcs_t)legchar;
995
996 maxout -= 3;
997 }
998 else if (legchar > 0xffff)
999 {
1000 if (maxout < 4)
1001 return (-1);
1002
1003 *dest++ = (cups_sbcs_t)(legchar >> 16);
1004 *dest++ = (cups_sbcs_t)(legchar >> 8);
1005 *dest++ = (cups_sbcs_t)legchar;
1006
1007 maxout -= 2;
1008 }
1009 else if (legchar > 0xff)
1010 {
1011 *dest++ = (cups_sbcs_t)(legchar >> 8);
1012 *dest++ = (cups_sbcs_t)legchar;
1013
1014 maxout --;
1015 }
1016 }
1017
1018 *dest = '\0';
1019
1020 vmap->used --;
1021
1022 return ((int)(dest - start));
1023 }
1024
1025
1026 /*
1027 * 'conv_vbcs_to_utf8()' - Convert legacy DBCS/VBCS to UTF-8.
1028 */
1029
1030 static int /* O - Count or -1 on error */
1031 conv_vbcs_to_utf8(
1032 cups_utf8_t *dest, /* O - Target string */
1033 const cups_sbcs_t *src, /* I - Source string */
1034 int maxout, /* I - Max output */
1035 const cups_encoding_t encoding) /* I - Encoding */
1036 {
1037 _cups_vmap_t *vmap; /* Legacy VBCS / Unicode Charset Map */
1038 cups_ucs2_t *crow; /* Pointer to UCS-2 row in 'char2uni' */
1039 _cups_wide2uni_t *wide2uni; /* Pointer to row in 'wide2uni' */
1040 cups_sbcs_t leadchar; /* Lead char of n-byte legacy char */
1041 cups_vbcs_t legchar; /* Legacy character value */
1042 cups_utf32_t work[CUPS_MAX_USTRING], /* Internal UCS-4 string */
1043 *workptr; /* Pointer into string */
1044
1045
1046 /*
1047 * Find legacy charset map in cache...
1048 */
1049
1050 if ((vmap = (_cups_vmap_t *)get_charmap(encoding)) == NULL)
1051 return (-1);
1052
1053 /*
1054 * Convert input legacy charset to internal UCS-4 (and insert BOM)...
1055 */
1056
1057 work[0] = 0xfeff;
1058 for (workptr = work + 1; *src && workptr < (work + CUPS_MAX_USTRING - 1);)
1059 {
1060 legchar = *src++;
1061 leadchar = (cups_sbcs_t)legchar;
1062
1063 /*
1064 * Convert ASCII verbatim (optimization)...
1065 */
1066
1067 if (legchar < 0x80)
1068 {
1069 *workptr++ = (cups_utf32_t)legchar;
1070 continue;
1071 }
1072
1073 /*
1074 * Convert 2-byte legacy character...
1075 */
1076
1077 if (vmap->lead2char[(int)leadchar] == leadchar)
1078 {
1079 if (!*src)
1080 return (-1);
1081
1082 legchar = (legchar << 8) | *src++;
1083
1084 /*
1085 * Convert unknown character to Replacement Character...
1086 */
1087
1088 crow = vmap->char2uni[(int)((legchar >> 8) & 0xff)];
1089 if (crow)
1090 crow += (int) (legchar & 0xff);
1091
1092 if (!crow || !*crow)
1093 *workptr++ = 0xfffd;
1094 else
1095 *workptr++ = (cups_utf32_t)*crow;
1096 continue;
1097 }
1098
1099 /*
1100 * Fetch 3-byte or 4-byte legacy character...
1101 */
1102
1103 if (vmap->lead3char[(int)leadchar] == leadchar)
1104 {
1105 if (!*src || !src[1])
1106 return (-1);
1107
1108 legchar = (legchar << 8) | *src++;
1109 legchar = (legchar << 8) | *src++;
1110 }
1111 else if (vmap->lead4char[(int)leadchar] == leadchar)
1112 {
1113 if (!*src || !src[1] || !src[2])
1114 return (-1);
1115
1116 legchar = (legchar << 8) | *src++;
1117 legchar = (legchar << 8) | *src++;
1118 legchar = (legchar << 8) | *src++;
1119 }
1120 else
1121 return (-1);
1122
1123 /*
1124 * Find 3-byte or 4-byte legacy character...
1125 */
1126
1127 wide2uni = (_cups_wide2uni_t *)bsearch(&legchar,
1128 vmap->wide2uni,
1129 vmap->widecount,
1130 sizeof(_cups_wide2uni_t),
1131 compare_wide);
1132
1133 /*
1134 * Convert unknown character to Replacement Character...
1135 */
1136
1137 if (!wide2uni || !wide2uni->unichar)
1138 *workptr++ = 0xfffd;
1139 else
1140 *workptr++ = wide2uni->unichar;
1141 }
1142
1143 *workptr = 0;
1144
1145 vmap->used --;
1146
1147 /*
1148 * Convert internal UCS-4 to output UTF-8 (and delete BOM)...
1149 */
1150
1151 return (cupsUTF32ToUTF8(dest, work, maxout));
1152 }
1153
1154
1155 /*
1156 * 'free_sbcs_charmap()' - Free memory used by a single byte character set.
1157 */
1158
1159 static void
1160 free_sbcs_charmap(_cups_cmap_t *cmap) /* I - Character set */
1161 {
1162 int i; /* Looping variable */
1163
1164
1165 for (i = 0; i < 256; i ++)
1166 if (cmap->uni2char[i])
1167 free(cmap->uni2char[i]);
1168
1169 free(cmap);
1170 }
1171
1172
1173 /*
1174 * 'free_vbcs_charmap()' - Free memory used by a variable byte character set.
1175 */
1176
1177 static void
1178 free_vbcs_charmap(_cups_vmap_t *vmap) /* I - Character set */
1179 {
1180 int i; /* Looping variable */
1181
1182
1183 for (i = 0; i < 256; i ++)
1184 if (vmap->char2uni[i])
1185 free(vmap->char2uni[i]);
1186
1187 for (i = 0; i < 256; i ++)
1188 if (vmap->uni2char[i])
1189 free(vmap->uni2char[i]);
1190
1191 if (vmap->wide2uni)
1192 free(vmap->wide2uni);
1193
1194 free(vmap);
1195 }
1196
1197
1198 /*
1199 * 'get_charmap()' - Lookup or get a character set map (private).
1200 *
1201 * This code handles single-byte (SBCS), double-byte (DBCS), and
1202 * variable-byte (VBCS) character sets _without_ charset escapes...
1203 * This code does not handle multiple-byte character sets (MBCS)
1204 * (such as ISO-2022-JP) with charset switching via escapes...
1205 */
1206
1207
1208 static void * /* O - Charset map pointer */
1209 get_charmap(
1210 const cups_encoding_t encoding) /* I - Encoding */
1211 {
1212 char filename[1024]; /* Filename for charset map file */
1213 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
1214
1215
1216 /*
1217 * Get the data directory and charset map name...
1218 */
1219
1220 snprintf(filename, sizeof(filename), "%s/charmaps/%s.txt",
1221 cg->cups_datadir, _cupsEncodingName(encoding));
1222
1223 DEBUG_printf((" filename=\"%s\"\n", filename));
1224
1225 /*
1226 * Read charset map input file into cache...
1227 */
1228
1229 if (encoding < CUPS_ENCODING_SBCS_END)
1230 return (get_sbcs_charmap(encoding, filename));
1231 else if (encoding < CUPS_ENCODING_VBCS_END)
1232 return (get_vbcs_charmap(encoding, filename));
1233 else
1234 return (NULL);
1235 }
1236
1237
1238 /*
1239 * 'get_charmap_count()' - Count lines in a charmap file.
1240 */
1241
1242 static int /* O - Count or -1 on error */
1243 get_charmap_count(cups_file_t *fp) /* I - File to read from */
1244 {
1245 int count; /* Number of lines */
1246 char line[256]; /* Line from input map file */
1247
1248
1249 /*
1250 * Count lines in map input file...
1251 */
1252
1253 count = 0;
1254
1255 while (cupsFileGets(fp, line, sizeof(line)))
1256 if (line[0] == '0')
1257 count ++;
1258
1259 /*
1260 * Return the number of lines...
1261 */
1262
1263 if (count > 0)
1264 return (count);
1265 else
1266 return (-1);
1267 }
1268
1269
1270 /*
1271 * 'get_sbcs_charmap()' - Get SBCS Charmap.
1272 */
1273
1274 static _cups_cmap_t * /* O - Charmap or 0 on error */
1275 get_sbcs_charmap(
1276 const cups_encoding_t encoding, /* I - Charmap Encoding */
1277 const char *filename) /* I - Charmap Filename */
1278 {
1279 unsigned long legchar; /* Legacy character value */
1280 cups_utf32_t unichar; /* Unicode character value */
1281 _cups_cmap_t *cmap; /* Legacy SBCS / Unicode Charset Map */
1282 cups_file_t *fp; /* Charset map file pointer */
1283 char *s; /* Line parsing pointer */
1284 cups_ucs2_t *crow; /* Pointer to UCS-2 row in 'char2uni' */
1285 cups_sbcs_t *srow; /* Pointer to SBCS row in 'uni2char' */
1286 char line[256]; /* Line from charset map file */
1287
1288
1289 /*
1290 * See if we already have this SBCS charset map loaded...
1291 */
1292
1293 for (cmap = cmap_cache; cmap; cmap = cmap->next)
1294 {
1295 if (cmap->encoding == encoding)
1296 {
1297 cmap->used ++;
1298 DEBUG_printf((" returning existing cmap=%p\n", cmap));
1299
1300 return ((void *)cmap);
1301 }
1302 }
1303
1304 /*
1305 * Open SBCS charset map input file...
1306 */
1307
1308 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1309 return (NULL);
1310
1311 /*
1312 * Allocate memory for SBCS charset map...
1313 */
1314
1315 if ((cmap = (_cups_cmap_t *)calloc(1, sizeof(_cups_cmap_t))) == NULL)
1316 {
1317 cupsFileClose(fp);
1318 DEBUG_puts(" Unable to allocate memory!");
1319
1320 return (NULL);
1321 }
1322
1323 cmap->used ++;
1324 cmap->encoding = encoding;
1325
1326 /*
1327 * Save SBCS charset map into memory for transcoding...
1328 */
1329
1330 while (cupsFileGets(fp, line, sizeof(line)))
1331 {
1332 if (line[0] != '0')
1333 continue;
1334
1335 legchar = strtol(line, &s, 16);
1336 if (legchar < 0 || legchar > 0xff)
1337 goto sbcs_error;
1338
1339 unichar = strtol(s, NULL, 16);
1340 if (unichar < 0 || unichar > 0xffff)
1341 goto sbcs_error;
1342
1343 /*
1344 * Save legacy to Unicode mapping in direct lookup table...
1345 */
1346
1347 crow = cmap->char2uni + legchar;
1348 *crow = (cups_ucs2_t)(unichar & 0xffff);
1349
1350 /*
1351 * Save Unicode to legacy mapping in indirect lookup table...
1352 */
1353
1354 srow = cmap->uni2char[(unichar >> 8) & 0xff];
1355 if (!srow)
1356 {
1357 srow = (cups_sbcs_t *)calloc(256, sizeof(cups_sbcs_t));
1358 if (!srow)
1359 goto sbcs_error;
1360
1361 cmap->uni2char[(unichar >> 8) & 0xff] = srow;
1362 }
1363
1364 srow += unichar & 0xff;
1365
1366 /*
1367 * Convert Replacement Character to visible replacement...
1368 */
1369
1370 if (unichar == 0xfffd)
1371 legchar = (unsigned long)'?';
1372
1373 /*
1374 * First (oldest) legacy character uses Unicode mapping cell...
1375 */
1376
1377 if (!*srow)
1378 *srow = (cups_sbcs_t)legchar;
1379 }
1380
1381 cupsFileClose(fp);
1382
1383 /*
1384 * Add it to the cache and return...
1385 */
1386
1387 cmap->next = cmap_cache;
1388 cmap_cache = cmap;
1389
1390 DEBUG_printf((" returning new cmap=%p\n", cmap));
1391
1392 return (cmap);
1393
1394 /*
1395 * If we get here, there was an error in the cmap file...
1396 */
1397
1398 sbcs_error:
1399
1400 free_sbcs_charmap(cmap);
1401
1402 cupsFileClose(fp);
1403
1404 DEBUG_puts(" Error, returning NULL!");
1405
1406 return (NULL);
1407 }
1408
1409
1410 /*
1411 * 'get_vbcs_charmap()' - Get DBCS/VBCS Charmap.
1412 */
1413
1414 static _cups_vmap_t * /* O - Charmap or 0 on error */
1415 get_vbcs_charmap(
1416 const cups_encoding_t encoding, /* I - Charmap Encoding */
1417 const char *filename) /* I - Charmap Filename */
1418 {
1419 _cups_vmap_t *vmap; /* Legacy VBCS / Unicode Charset Map */
1420 cups_ucs2_t *crow; /* Pointer to UCS-2 row in 'char2uni' */
1421 cups_vbcs_t *vrow; /* Pointer to VBCS row in 'uni2char' */
1422 _cups_wide2uni_t *wide2uni; /* Pointer to row in 'wide2uni' */
1423 cups_sbcs_t leadchar; /* Lead char of 2-byte legacy char */
1424 unsigned long legchar; /* Legacy character value */
1425 cups_utf32_t unichar; /* Unicode character value */
1426 int mapcount; /* Count of lines in charmap file */
1427 cups_file_t *fp; /* Charset map file pointer */
1428 char *s; /* Line parsing pointer */
1429 char line[256]; /* Line from charset map file */
1430 int i; /* Loop variable */
1431 int legacy; /* 32-bit legacy char */
1432
1433
1434 DEBUG_printf(("get_vbcs_charmap(encoding=%d, filename=\"%s\")\n",
1435 encoding, filename));
1436
1437 /*
1438 * See if we already have this DBCS/VBCS charset map loaded...
1439 */
1440
1441 for (vmap = vmap_cache; vmap; vmap = vmap->next)
1442 {
1443 if (vmap->encoding == encoding)
1444 {
1445 vmap->used ++;
1446 DEBUG_printf((" returning existing vmap=%p\n", vmap));
1447
1448 return ((void *)vmap);
1449 }
1450 }
1451
1452 /*
1453 * Open VBCS charset map input file...
1454 */
1455
1456 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1457 {
1458 DEBUG_printf((" Unable to open file: %s\n", strerror(errno)));
1459
1460 return (NULL);
1461 }
1462
1463 /*
1464 * Count lines in charmap file...
1465 */
1466
1467 if ((mapcount = get_charmap_count(fp)) <= 0)
1468 {
1469 DEBUG_puts(" Unable to get charmap count!");
1470
1471 return (NULL);
1472 }
1473
1474 DEBUG_printf((" mapcount=%d\n", mapcount));
1475
1476 /*
1477 * Allocate memory for DBCS/VBCS charset map...
1478 */
1479
1480 if ((vmap = (_cups_vmap_t *)calloc(1, sizeof(_cups_vmap_t))) == NULL)
1481 {
1482 cupsFileClose(fp);
1483 DEBUG_puts(" Unable to allocate memory!");
1484
1485 return (NULL);
1486 }
1487
1488 vmap->used ++;
1489 vmap->encoding = encoding;
1490
1491 /*
1492 * Save DBCS/VBCS charset map into memory for transcoding...
1493 */
1494
1495 leadchar = 0;
1496 wide2uni = NULL;
1497
1498 cupsFileRewind(fp);
1499
1500 i = 0;
1501 legacy = 0;
1502
1503 while (cupsFileGets(fp, line, sizeof(line)))
1504 {
1505 if (line[0] != '0')
1506 continue;
1507
1508 legchar = strtoul(line, &s, 16);
1509 if (legchar == ULONG_MAX)
1510 goto vbcs_error;
1511
1512 unichar = strtol(s, NULL, 16);
1513 if (unichar < 0 || unichar > 0xffff)
1514 goto vbcs_error;
1515
1516 i ++;
1517
1518 /* DEBUG_printf((" i=%d, legchar=0x%08lx, unichar=0x%04x\n", i,
1519 legchar, (unsigned)unichar)); */
1520
1521 /*
1522 * Save lead char of 2/3/4-byte legacy char...
1523 */
1524
1525 if (legchar > 0xff && legchar <= 0xffff)
1526 {
1527 leadchar = (cups_sbcs_t)(legchar >> 8);
1528 vmap->lead2char[leadchar] = leadchar;
1529 }
1530
1531 if (legchar > 0xffff && legchar <= 0xffffff)
1532 {
1533 leadchar = (cups_sbcs_t)(legchar >> 16);
1534 vmap->lead3char[leadchar] = leadchar;
1535 }
1536
1537 if (legchar > 0xffffff)
1538 {
1539 leadchar = (cups_sbcs_t)(legchar >> 24);
1540 vmap->lead4char[leadchar] = leadchar;
1541 }
1542
1543 /*
1544 * Save Legacy to Unicode mapping...
1545 */
1546
1547 if (legchar <= 0xffff)
1548 {
1549 /*
1550 * Save DBCS 16-bit to Unicode mapping in indirect lookup table...
1551 */
1552
1553 crow = vmap->char2uni[(int)leadchar];
1554 if (!crow)
1555 {
1556 crow = (cups_ucs2_t *)calloc(256, sizeof(cups_ucs2_t));
1557 if (!crow)
1558 goto vbcs_error;
1559
1560 vmap->char2uni[(int)leadchar] = crow;
1561 }
1562
1563 crow[(int)(legchar & 0xff)] = (cups_ucs2_t)unichar;
1564 }
1565 else
1566 {
1567 /*
1568 * Save VBCS 32-bit to Unicode mapping in sorted list table...
1569 */
1570
1571 if (!legacy)
1572 {
1573 legacy = 1;
1574 vmap->widecount = (mapcount - i + 1);
1575 wide2uni = (_cups_wide2uni_t *)calloc(vmap->widecount,
1576 sizeof(_cups_wide2uni_t));
1577 if (!wide2uni)
1578 goto vbcs_error;
1579
1580 vmap->wide2uni = wide2uni;
1581 }
1582
1583 wide2uni->widechar = (cups_vbcs_t)legchar;
1584 wide2uni->unichar = (cups_ucs2_t)unichar;
1585 wide2uni ++;
1586 }
1587
1588 /*
1589 * Save Unicode to legacy mapping in indirect lookup table...
1590 */
1591
1592 vrow = vmap->uni2char[(int)((unichar >> 8) & 0xff)];
1593 if (!vrow)
1594 {
1595 vrow = (cups_vbcs_t *)calloc(256, sizeof(cups_vbcs_t));
1596 if (!vrow)
1597 goto vbcs_error;
1598
1599 vmap->uni2char[(int) ((unichar >> 8) & 0xff)] = vrow;
1600 }
1601
1602 vrow += (int)(unichar & 0xff);
1603
1604 /*
1605 * Convert Replacement Character to visible replacement...
1606 */
1607
1608 if (unichar == 0xfffd)
1609 legchar = (unsigned long)'?';
1610
1611 /*
1612 * First (oldest) legacy character uses Unicode mapping cell...
1613 */
1614
1615 if (!*vrow)
1616 *vrow = (cups_vbcs_t)legchar;
1617 }
1618
1619 vmap->charcount = (i - vmap->widecount);
1620
1621 cupsFileClose(fp);
1622
1623 /*
1624 * Add it to the cache and return...
1625 */
1626
1627 vmap->next = vmap_cache;
1628 vmap_cache = vmap;
1629
1630 DEBUG_printf((" returning new vmap=%p\n", vmap));
1631
1632 return (vmap);
1633
1634 /*
1635 * If we get here, the file contains errors...
1636 */
1637
1638 vbcs_error:
1639
1640 free_vbcs_charmap(vmap);
1641
1642 cupsFileClose(fp);
1643
1644 DEBUG_puts(" Error, returning NULL!");
1645
1646 return (NULL);
1647 }
1648
1649
1650 /*
1651 * End of "$Id: transcode.c 6649 2007-07-11 21:46:42Z mike $"
1652 */