Coverage Report

Created: 2026-01-09 06:11

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