Coverage Report

Created: 2022-10-31 07:00

/src/cups/cups/transcode.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Transcoding support for CUPS.
3
 *
4
 * Copyright 2007-2014 by Apple Inc.
5
 * Copyright 1997-2007 by Easy Software Products.
6
 *
7
 * These coded instructions, statements, and computer programs are the
8
 * property of Apple Inc. and are protected by Federal copyright
9
 * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
10
 * which should have been included with this file.  If this file is
11
 * missing or damaged, see the license at "http://www.cups.org/".
12
 *
13
 * This file is subject to the Apple OS-Developed Software exception.
14
 */
15
16
/*
17
 * Include necessary headers...
18
 */
19
20
#include "cups-private.h"
21
#include <limits.h>
22
#include <time.h>
23
#ifdef HAVE_ICONV_H
24
#  include <iconv.h>
25
#endif /* HAVE_ICONV_H */
26
27
28
/*
29
 * Local globals...
30
 */
31
32
#ifdef HAVE_ICONV_H
33
static _cups_mutex_t  map_mutex = _CUPS_MUTEX_INITIALIZER;
34
          /* Mutex to control access to maps */
35
static iconv_t    map_from_utf8 = (iconv_t)-1;
36
          /* Convert from UTF-8 to charset */
37
static iconv_t    map_to_utf8 = (iconv_t)-1;
38
          /* Convert from charset to UTF-8 */
39
static cups_encoding_t  map_encoding = CUPS_AUTO_ENCODING;
40
          /* Which charset is cached */
41
#endif /* HAVE_ICONV_H */
42
43
44
/*
45
 * '_cupsCharmapFlush()' - Flush all character set maps out of cache.
46
 */
47
48
void
49
_cupsCharmapFlush(void)
50
0
{
51
0
#ifdef HAVE_ICONV_H
52
0
  if (map_from_utf8 != (iconv_t)-1)
53
0
  {
54
0
    iconv_close(map_from_utf8);
55
0
    map_from_utf8 = (iconv_t)-1;
56
0
  }
57
58
0
  if (map_to_utf8 != (iconv_t)-1)
59
0
  {
60
0
    iconv_close(map_to_utf8);
61
0
    map_to_utf8 = (iconv_t)-1;
62
0
  }
63
64
0
  map_encoding = CUPS_AUTO_ENCODING;
65
0
#endif /* HAVE_ICONV_H */
66
0
}
67
68
69
/*
70
 * 'cupsCharsetToUTF8()' - Convert legacy character set to UTF-8.
71
 */
72
73
int         /* O - Count or -1 on error */
74
cupsCharsetToUTF8(
75
    cups_utf8_t           *dest,  /* O - Target string */
76
    const char            *src,   /* I - Source string */
77
    const int             maxout, /* I - Max output */
78
    const cups_encoding_t encoding) /* I - Encoding */
79
0
{
80
0
  cups_utf8_t *destptr;   /* Pointer into UTF-8 buffer */
81
0
#ifdef HAVE_ICONV_H
82
0
  size_t  srclen,     /* Length of source string */
83
0
    outBytesLeft;   /* Bytes remaining in output buffer */
84
0
#endif /* HAVE_ICONV_H */
85
86
87
 /*
88
  * Check for valid arguments...
89
  */
90
91
0
  DEBUG_printf(("2cupsCharsetToUTF8(dest=%p, src=\"%s\", maxout=%d, encoding=%d)", (void *)dest, src, maxout, encoding));
92
93
0
  if (!dest || !src || maxout < 1)
94
0
  {
95
0
    if (dest)
96
0
      *dest = '\0';
97
98
0
    DEBUG_puts("3cupsCharsetToUTF8: Bad arguments, returning -1");
99
0
    return (-1);
100
0
  }
101
102
 /*
103
  * Handle identity conversions...
104
  */
105
106
0
  if (encoding == CUPS_UTF8 || encoding <= CUPS_US_ASCII ||
107
0
      encoding >= CUPS_ENCODING_VBCS_END)
108
0
  {
109
0
    strlcpy((char *)dest, src, (size_t)maxout);
110
0
    return ((int)strlen((char *)dest));
111
0
  }
112
113
 /*
114
  * Handle ISO-8859-1 to UTF-8 directly...
115
  */
116
117
0
  destptr = dest;
118
119
0
  if (encoding == CUPS_ISO8859_1)
120
0
  {
121
0
    int   ch;     /* Character from string */
122
0
    cups_utf8_t *destend;   /* End of UTF-8 buffer */
123
124
125
0
    destend = dest + maxout - 2;
126
127
0
    while (*src && destptr < destend)
128
0
    {
129
0
      ch = *src++ & 255;
130
131
0
      if (ch & 128)
132
0
      {
133
0
  *destptr++ = (cups_utf8_t)(0xc0 | (ch >> 6));
134
0
  *destptr++ = (cups_utf8_t)(0x80 | (ch & 0x3f));
135
0
      }
136
0
      else
137
0
  *destptr++ = (cups_utf8_t)ch;
138
0
    }
139
140
0
    *destptr = '\0';
141
142
0
    return ((int)(destptr - dest));
143
0
  }
144
145
 /*
146
  * Convert input legacy charset to UTF-8...
147
  */
148
149
0
#ifdef HAVE_ICONV_H
150
0
  _cupsMutexLock(&map_mutex);
151
152
0
  if (map_encoding != encoding)
153
0
  {
154
0
    char  toset[1024];    /* Destination character set */
155
156
0
    _cupsCharmapFlush();
157
158
0
    snprintf(toset, sizeof(toset), "%s//IGNORE", _cupsEncodingName(encoding));
159
160
0
    map_encoding  = encoding;
161
0
    map_from_utf8 = iconv_open(_cupsEncodingName(encoding), "UTF-8");
162
0
    map_to_utf8   = iconv_open("UTF-8", toset);
163
0
  }
164
165
0
  if (map_to_utf8 != (iconv_t)-1)
166
0
  {
167
0
    char *altdestptr = (char *)dest;  /* Silence bogus GCC type-punned */
168
169
0
    srclen       = strlen(src);
170
0
    outBytesLeft = (size_t)maxout - 1;
171
172
0
    iconv(map_to_utf8, (char **)&src, &srclen, &altdestptr, &outBytesLeft);
173
0
    *altdestptr = '\0';
174
175
0
    _cupsMutexUnlock(&map_mutex);
176
177
0
    return ((int)(altdestptr - (char *)dest));
178
0
  }
179
180
0
  _cupsMutexUnlock(&map_mutex);
181
0
#endif /* HAVE_ICONV_H */
182
183
 /*
184
  * No iconv() support, so error out...
185
  */
186
187
0
  *destptr = '\0';
188
189
0
  return (-1);
190
0
}
191
192
193
/*
194
 * 'cupsUTF8ToCharset()' - Convert UTF-8 to legacy character set.
195
 */
196
197
int         /* O - Count or -1 on error */
198
cupsUTF8ToCharset(
199
    char      *dest,  /* O - Target string */
200
    const cups_utf8_t   *src,   /* I - Source string */
201
    const int     maxout, /* I - Max output */
202
    const cups_encoding_t encoding) /* I - Encoding */
203
0
{
204
0
  char    *destptr;   /* Pointer into destination */
205
0
#ifdef HAVE_ICONV_H
206
0
  size_t  srclen,     /* Length of source string */
207
0
    outBytesLeft;   /* Bytes remaining in output buffer */
208
0
#endif /* HAVE_ICONV_H */
209
210
211
 /*
212
  * Check for valid arguments...
213
  */
214
215
0
  if (!dest || !src || maxout < 1)
216
0
  {
217
0
    if (dest)
218
0
      *dest = '\0';
219
220
0
    return (-1);
221
0
  }
222
223
 /*
224
  * Handle identity conversions...
225
  */
226
227
0
  if (encoding == CUPS_UTF8 ||
228
0
      encoding >= CUPS_ENCODING_VBCS_END)
229
0
  {
230
0
    strlcpy(dest, (char *)src, (size_t)maxout);
231
0
    return ((int)strlen(dest));
232
0
  }
233
234
 /*
235
  * Handle UTF-8 to ISO-8859-1 directly...
236
  */
237
238
0
  destptr = dest;
239
240
0
  if (encoding == CUPS_ISO8859_1 || encoding <= CUPS_US_ASCII)
241
0
  {
242
0
    int   ch,     /* Character from string */
243
0
    maxch;      /* Maximum character for charset */
244
0
    char  *destend;   /* End of ISO-8859-1 buffer */
245
246
0
    maxch   = encoding == CUPS_ISO8859_1 ? 256 : 128;
247
0
    destend = dest + maxout - 1;
248
249
0
    while (*src && destptr < destend)
250
0
    {
251
0
      ch = *src++;
252
253
0
      if ((ch & 0xe0) == 0xc0)
254
0
      {
255
0
  ch = ((ch & 0x1f) << 6) | (*src++ & 0x3f);
256
257
0
  if (ch < maxch)
258
0
          *destptr++ = (char)ch;
259
0
  else
260
0
          *destptr++ = '?';
261
0
      }
262
0
      else if ((ch & 0xf0) == 0xe0 ||
263
0
               (ch & 0xf8) == 0xf0)
264
0
        *destptr++ = '?';
265
0
      else if (!(ch & 0x80))
266
0
  *destptr++ = (char)ch;
267
0
    }
268
269
0
    *destptr = '\0';
270
271
0
    return ((int)(destptr - dest));
272
0
  }
273
274
0
#ifdef HAVE_ICONV_H
275
 /*
276
  * Convert input UTF-8 to legacy charset...
277
  */
278
279
0
  _cupsMutexLock(&map_mutex);
280
281
0
  if (map_encoding != encoding)
282
0
  {
283
0
    char  toset[1024];    /* Destination character set */
284
285
0
    _cupsCharmapFlush();
286
287
0
    snprintf(toset, sizeof(toset), "%s//IGNORE", _cupsEncodingName(encoding));
288
289
0
    map_encoding  = encoding;
290
0
    map_from_utf8 = iconv_open(_cupsEncodingName(encoding), "UTF-8");
291
0
    map_to_utf8   = iconv_open("UTF-8", toset);
292
0
  }
293
294
0
  if (map_from_utf8 != (iconv_t)-1)
295
0
  {
296
0
    char *altsrc = (char *)src;   /* Silence bogus GCC type-punned */
297
298
0
    srclen       = strlen((char *)src);
299
0
    outBytesLeft = (size_t)maxout - 1;
300
301
0
    iconv(map_from_utf8, &altsrc, &srclen, &destptr, &outBytesLeft);
302
0
    *destptr = '\0';
303
304
0
    _cupsMutexUnlock(&map_mutex);
305
306
0
    return ((int)(destptr - dest));
307
0
  }
308
309
0
  _cupsMutexUnlock(&map_mutex);
310
0
#endif /* HAVE_ICONV_H */
311
312
 /*
313
  * No iconv() support, so error out...
314
  */
315
316
0
  *destptr = '\0';
317
318
0
  return (-1);
319
0
}
320
321
322
/*
323
 * 'cupsUTF8ToUTF32()' - Convert UTF-8 to UTF-32.
324
 *
325
 * 32-bit UTF-32 (actually 21-bit) maps to UTF-8 as follows...
326
 *
327
 *   UTF-32 char     UTF-8 char(s)
328
 *   --------------------------------------------------
329
 *    0 to 127 = 0xxxxxxx (US-ASCII)
330
 *     128 to 2047 = 110xxxxx 10yyyyyy
331
 *   2048 to 65535 = 1110xxxx 10yyyyyy 10zzzzzz
332
 *     > 65535 = 11110xxx 10yyyyyy 10zzzzzz 10xxxxxx
333
 *
334
 * UTF-32 prohibits chars beyond Plane 16 (> 0x10ffff) in UCS-4,
335
 * which would convert to five- or six-octet UTF-8 sequences...
336
 */
337
338
int         /* O - Count or -1 on error */
339
cupsUTF8ToUTF32(
340
    cups_utf32_t      *dest,    /* O - Target string */
341
    const cups_utf8_t *src,   /* I - Source string */
342
    const int         maxout)   /* I - Max output */
343
0
{
344
0
  int   i;      /* Looping variable */
345
0
  cups_utf8_t ch;     /* Character value */
346
0
  cups_utf8_t next;     /* Next character value */
347
0
  cups_utf32_t  ch32;     /* UTF-32 character value */
348
349
350
 /*
351
  * Check for valid arguments and clear output...
352
  */
353
354
0
  DEBUG_printf(("2cupsUTF8ToUTF32(dest=%p, src=\"%s\", maxout=%d)", (void *)dest, src, maxout));
355
356
0
  if (dest)
357
0
    *dest = 0;
358
359
0
  if (!dest || !src || maxout < 1 || maxout > CUPS_MAX_USTRING)
360
0
  {
361
0
    DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad arguments)");
362
363
0
    return (-1);
364
0
  }
365
366
 /*
367
  * Convert input UTF-8 to output UTF-32...
368
  */
369
370
0
  for (i = maxout - 1; *src && i > 0; i --)
371
0
  {
372
0
    ch = *src++;
373
374
   /*
375
    * Convert UTF-8 character(s) to UTF-32 character...
376
    */
377
378
0
    if (!(ch & 0x80))
379
0
    {
380
     /*
381
      * One-octet UTF-8 <= 127 (US-ASCII)...
382
      */
383
384
0
      *dest++ = ch;
385
386
0
      DEBUG_printf(("4cupsUTF8ToUTF32: %02x => %08X", src[-1], ch));
387
0
      continue;
388
0
    }
389
0
    else if ((ch & 0xe0) == 0xc0)
390
0
    {
391
     /*
392
      * Two-octet UTF-8 <= 2047 (Latin-x)...
393
      */
394
395
0
      next = *src++;
396
0
      if ((next & 0xc0) != 0x80)
397
0
      {
398
0
        DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
399
400
0
  return (-1);
401
0
      }
402
403
0
      ch32 = (cups_utf32_t)((ch & 0x1f) << 6) | (cups_utf32_t)(next & 0x3f);
404
405
     /*
406
      * Check for non-shortest form (invalid UTF-8)...
407
      */
408
409
0
      if (ch32 < 0x80)
410
0
      {
411
0
        DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
412
413
0
  return (-1);
414
0
      }
415
416
0
      *dest++ = ch32;
417
418
0
      DEBUG_printf(("4cupsUTF8ToUTF32: %02x %02x => %08X",
419
0
                    src[-2], src[-1], (unsigned)ch32));
420
0
    }
421
0
    else if ((ch & 0xf0) == 0xe0)
422
0
    {
423
     /*
424
      * Three-octet UTF-8 <= 65535 (Plane 0 - BMP)...
425
      */
426
427
0
      next = *src++;
428
0
      if ((next & 0xc0) != 0x80)
429
0
      {
430
0
        DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
431
432
0
  return (-1);
433
0
      }
434
435
0
      ch32 = (cups_utf32_t)((ch & 0x0f) << 6) | (cups_utf32_t)(next & 0x3f);
436
437
0
      next = *src++;
438
0
      if ((next & 0xc0) != 0x80)
439
0
      {
440
0
        DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
441
442
0
  return (-1);
443
0
      }
444
445
0
      ch32 = (ch32 << 6) | (cups_utf32_t)(next & 0x3f);
446
447
     /*
448
      * Check for non-shortest form (invalid UTF-8)...
449
      */
450
451
0
      if (ch32 < 0x800)
452
0
      {
453
0
        DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
454
455
0
  return (-1);
456
0
      }
457
458
0
      *dest++ = ch32;
459
460
0
      DEBUG_printf(("4cupsUTF8ToUTF32: %02x %02x %02x => %08X",
461
0
                    src[-3], src[-2], src[-1], (unsigned)ch32));
462
0
    }
463
0
    else if ((ch & 0xf8) == 0xf0)
464
0
    {
465
     /*
466
      * Four-octet UTF-8...
467
      */
468
469
0
      next = *src++;
470
0
      if ((next & 0xc0) != 0x80)
471
0
      {
472
0
        DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
473
474
0
  return (-1);
475
0
      }
476
477
0
      ch32 = (cups_utf32_t)((ch & 0x07) << 6) | (cups_utf32_t)(next & 0x3f);
478
479
0
      next = *src++;
480
0
      if ((next & 0xc0) != 0x80)
481
0
      {
482
0
        DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
483
484
0
  return (-1);
485
0
      }
486
487
0
      ch32 = (ch32 << 6) | (cups_utf32_t)(next & 0x3f);
488
489
0
      next = *src++;
490
0
      if ((next & 0xc0) != 0x80)
491
0
      {
492
0
        DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
493
494
0
  return (-1);
495
0
      }
496
497
0
      ch32 = (ch32 << 6) | (cups_utf32_t)(next & 0x3f);
498
499
     /*
500
      * Check for non-shortest form (invalid UTF-8)...
501
      */
502
503
0
      if (ch32 < 0x10000)
504
0
      {
505
0
        DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
506
507
0
  return (-1);
508
0
      }
509
510
0
      *dest++ = ch32;
511
512
0
      DEBUG_printf(("4cupsUTF8ToUTF32: %02x %02x %02x %02x => %08X",
513
0
                    src[-4], src[-3], src[-2], src[-1], (unsigned)ch32));
514
0
    }
515
0
    else
516
0
    {
517
     /*
518
      * More than 4-octet (invalid UTF-8 sequence)...
519
      */
520
521
0
      DEBUG_puts("3cupsUTF8ToUTF32: Returning -1 (bad UTF-8 sequence)");
522
523
0
      return (-1);
524
0
    }
525
526
   /*
527
    * Check for UTF-16 surrogate (illegal UTF-8)...
528
    */
529
530
0
    if (ch32 >= 0xd800 && ch32 <= 0xdfff)
531
0
      return (-1);
532
0
  }
533
534
0
  *dest = 0;
535
536
0
  DEBUG_printf(("3cupsUTF8ToUTF32: Returning %d characters", maxout - 1 - i));
537
538
0
  return (maxout - 1 - i);
539
0
}
540
541
542
/*
543
 * 'cupsUTF32ToUTF8()' - Convert UTF-32 to UTF-8.
544
 *
545
 * 32-bit UTF-32 (actually 21-bit) maps to UTF-8 as follows...
546
 *
547
 *   UTF-32 char     UTF-8 char(s)
548
 *   --------------------------------------------------
549
 *    0 to 127 = 0xxxxxxx (US-ASCII)
550
 *     128 to 2047 = 110xxxxx 10yyyyyy
551
 *   2048 to 65535 = 1110xxxx 10yyyyyy 10zzzzzz
552
 *     > 65535 = 11110xxx 10yyyyyy 10zzzzzz 10xxxxxx
553
 *
554
 * UTF-32 prohibits chars beyond Plane 16 (> 0x10ffff) in UCS-4,
555
 * which would convert to five- or six-octet UTF-8 sequences...
556
 */
557
558
int         /* O - Count or -1 on error */
559
cupsUTF32ToUTF8(
560
    cups_utf8_t        *dest,   /* O - Target string */
561
    const cups_utf32_t *src,    /* I - Source string */
562
    const int          maxout)    /* I - Max output */
563
0
{
564
0
  cups_utf8_t *start;     /* Start of destination string */
565
0
  int   i;      /* Looping variable */
566
0
  int   swap;     /* Byte-swap input to output */
567
0
  cups_utf32_t  ch;     /* Character value */
568
569
570
 /*
571
  * Check for valid arguments and clear output...
572
  */
573
574
0
  DEBUG_printf(("2cupsUTF32ToUTF8(dest=%p, src=%p, maxout=%d)", (void *)dest, (void *)src, maxout));
575
576
0
  if (dest)
577
0
    *dest = '\0';
578
579
0
  if (!dest || !src || maxout < 1)
580
0
  {
581
0
    DEBUG_puts("3cupsUTF32ToUTF8: Returning -1 (bad args)");
582
583
0
    return (-1);
584
0
  }
585
586
 /*
587
  * Check for leading BOM in UTF-32 and inverted BOM...
588
  */
589
590
0
  start = dest;
591
0
  swap  = *src == 0xfffe0000;
592
593
0
  DEBUG_printf(("4cupsUTF32ToUTF8: swap=%d", swap));
594
595
0
  if (*src == 0xfffe0000 || *src == 0xfeff)
596
0
    src ++;
597
598
 /*
599
  * Convert input UTF-32 to output UTF-8...
600
  */
601
602
0
  for (i = maxout - 1; *src && i > 0;)
603
0
  {
604
0
    ch = *src++;
605
606
   /*
607
    * Byte swap input UTF-32, if necessary...
608
    * (only byte-swapping 24 of 32 bits)
609
    */
610
611
0
    if (swap)
612
0
      ch = ((ch >> 24) | ((ch >> 8) & 0xff00) | ((ch << 8) & 0xff0000));
613
614
   /*
615
    * Check for beyond Plane 16 (invalid UTF-32)...
616
    */
617
618
0
    if (ch > 0x10ffff)
619
0
    {
620
0
      DEBUG_puts("3cupsUTF32ToUTF8: Returning -1 (character out of range)");
621
622
0
      return (-1);
623
0
    }
624
625
   /*
626
    * Convert UTF-32 character to UTF-8 character(s)...
627
    */
628
629
0
    if (ch < 0x80)
630
0
    {
631
     /*
632
      * One-octet UTF-8 <= 127 (US-ASCII)...
633
      */
634
635
0
      *dest++ = (cups_utf8_t)ch;
636
0
      i --;
637
638
0
      DEBUG_printf(("4cupsUTF32ToUTF8: %08x => %02x", (unsigned)ch, dest[-1]));
639
0
    }
640
0
    else if (ch < 0x800)
641
0
    {
642
     /*
643
      * Two-octet UTF-8 <= 2047 (Latin-x)...
644
      */
645
646
0
      if (i < 2)
647
0
      {
648
0
        DEBUG_puts("3cupsUTF32ToUTF8: Returning -1 (too long 2)");
649
650
0
        return (-1);
651
0
      }
652
653
0
      *dest++ = (cups_utf8_t)(0xc0 | ((ch >> 6) & 0x1f));
654
0
      *dest++ = (cups_utf8_t)(0x80 | (ch & 0x3f));
655
0
      i -= 2;
656
657
0
      DEBUG_printf(("4cupsUTF32ToUTF8: %08x => %02x %02x", (unsigned)ch,
658
0
                    dest[-2], dest[-1]));
659
0
    }
660
0
    else if (ch < 0x10000)
661
0
    {
662
     /*
663
      * Three-octet UTF-8 <= 65535 (Plane 0 - BMP)...
664
      */
665
666
0
      if (i < 3)
667
0
      {
668
0
        DEBUG_puts("3cupsUTF32ToUTF8: Returning -1 (too long 3)");
669
670
0
        return (-1);
671
0
      }
672
673
0
      *dest++ = (cups_utf8_t)(0xe0 | ((ch >> 12) & 0x0f));
674
0
      *dest++ = (cups_utf8_t)(0x80 | ((ch >> 6) & 0x3f));
675
0
      *dest++ = (cups_utf8_t)(0x80 | (ch & 0x3f));
676
0
      i -= 3;
677
678
0
      DEBUG_printf(("4cupsUTF32ToUTF8: %08x => %02x %02x %02x", (unsigned)ch,
679
0
                    dest[-3], dest[-2], dest[-1]));
680
0
    }
681
0
    else
682
0
    {
683
     /*
684
      * Four-octet UTF-8...
685
      */
686
687
0
      if (i < 4)
688
0
      {
689
0
        DEBUG_puts("3cupsUTF32ToUTF8: Returning -1 (too long 4)");
690
691
0
        return (-1);
692
0
      }
693
694
0
      *dest++ = (cups_utf8_t)(0xf0 | ((ch >> 18) & 0x07));
695
0
      *dest++ = (cups_utf8_t)(0x80 | ((ch >> 12) & 0x3f));
696
0
      *dest++ = (cups_utf8_t)(0x80 | ((ch >> 6) & 0x3f));
697
0
      *dest++ = (cups_utf8_t)(0x80 | (ch & 0x3f));
698
0
      i -= 4;
699
700
0
      DEBUG_printf(("4cupsUTF32ToUTF8: %08x => %02x %02x %02x %02x",
701
0
                    (unsigned)ch, dest[-4], dest[-3], dest[-2], dest[-1]));
702
0
    }
703
0
  }
704
705
0
  *dest = '\0';
706
707
0
  DEBUG_printf(("3cupsUTF32ToUTF8: Returning %d", (int)(dest - start)));
708
709
0
  return ((int)(dest - start));
710
0
}