Coverage Report

Created: 2025-08-26 06:18

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