From e9d5e082afe3a5e272f7aff0718b902d6f5eed1d Mon Sep 17 00:00:00 2001 From: Michael R Sweet Date: Mon, 21 Jun 2021 07:58:23 -0400 Subject: [PATCH] Save work on Unicode font support - still something isn't quite right. --- pdfio-content.c | 520 +++++++++++++++++++++++++++++------------------- pdfio-content.h | 6 +- pdfio-stream.c | 20 ++ pdfio.h | 1 + testpdfio.c | 80 ++++---- ttf.c | 32 +++ ttf.h | 2 + 7 files changed, 417 insertions(+), 244 deletions(-) diff --git a/pdfio-content.c b/pdfio-content.c index eca276e..436eebf 100644 --- a/pdfio-content.c +++ b/pdfio-content.c @@ -51,6 +51,42 @@ const double pdfioSRGBWhitePoint[3] = { 0.9505, 1.0, 1.0890 }; #define _PDFIO_PNG_TYPE_GRAYA 4 // Grayscale + alpha #define _PDFIO_PNG_TYPE_RGBA 6 // RGB + alpha +static int _pdfio_cp1252[] = // CP1252-specific character mapping +{ + 0x20AC, + 0x0000, + 0x201A, + 0x0192, + 0x201E, + 0x2026, + 0x2020, + 0x2021, + 0x02C6, + 0x2030, + 0x0160, + 0x2039, + 0x0152, + 0x0000, + 0x017D, + 0x0000, + 0x0000, + 0x2018, + 0x2019, + 0x201C, + 0x201D, + 0x2022, + 0x2013, + 0x2014, + 0x02DC, + 0x2122, + 0x0161, + 0x203A, + 0x0153, + 0x0000, + 0x017E, + 0x0178 +}; + // // Local types... @@ -67,7 +103,7 @@ static pdfio_obj_t *copy_jpeg(pdfio_dict_t *dict, int fd); static pdfio_obj_t *copy_png(pdfio_dict_t *dict, int fd); static void ttf_error_cb(pdfio_file_t *pdf, const char *message); static unsigned update_png_crc(unsigned crc, const unsigned char *buffer, size_t length); -static bool write_string(pdfio_stream_t *st, const char *s, bool *newline); +static bool write_string(pdfio_stream_t *st, bool unicode, const char *s, bool *newline); // @@ -1015,17 +1051,22 @@ pdfioContentTextNextLine( // // 'pdfioContentTextShow()' - Show text. // +// This function shows some text in a PDF content stream. The "unicode" argument +// specifies that the current font maps to full Unicode. The "s" argument +// specifies a UTF-8 encoded string. +// bool // O - `true` on success, `false` on failure pdfioContentTextShow( pdfio_stream_t *st, // I - Stream + bool unicode, // I - Unicode text? const char *s) // I - String to show { bool newline = false; // New line? // Write the string... - if (!write_string(st, s, &newline)) + if (!write_string(st, unicode, s, &newline)) return (false); // Draw it... @@ -1039,10 +1080,15 @@ pdfioContentTextShow( // // 'pdfioContentTextShowf()' - Show formatted text. // +// This function shows some text in a PDF content stream. The "unicode" argument +// specifies that the current font maps to full Unicode. The "format" argument +// specifies a UTF-8 encoded `printf`-style format string. +// bool pdfioContentTextShowf( pdfio_stream_t *st, // I - Stream + bool unicode, // I - Unicode text? const char *format, // I - `printf`-style format string ...) // I - Additional arguments as needed { @@ -1057,7 +1103,7 @@ pdfioContentTextShowf( va_end(ap); // Write the string... - if (!write_string(st, buffer, &newline)) + if (!write_string(st, unicode, buffer, &newline)) return (false); // Draw it... @@ -1071,10 +1117,15 @@ pdfioContentTextShowf( // // 'pdfioContentTextShowJustified()' - Show justified text. // +// This function shows some text in a PDF content stream. The "unicode" argument +// specifies that the current font maps to full Unicode. The "fragments" +// argument specifies an array of UTF-8 encoded strings. +// bool // O - `true` on success, `false` on failure pdfioContentTextShowJustified( pdfio_stream_t *st, // I - Stream + bool unicode, // I - Unicode text? size_t num_fragments, // I - Number of text fragments const double *offsets, // I - Text offsets before fragments const char * const *fragments) // I - Text fragments @@ -1096,7 +1147,7 @@ pdfioContentTextShowJustified( if (fragments[i]) { - if (!write_string(st, fragments[i], NULL)) + if (!write_string(st, unicode, fragments[i], NULL)) return (false); } } @@ -1126,6 +1177,9 @@ pdfioContentTextShowJustified( // - `Times-Roman` // - `ZapfDingbats` // +// Base fonts always use the Windows CP1252 (ISO-8859-1 with additional +// characters such as the Euro symbol) subset of Unicode. +// pdfio_obj_t * // O - Font object pdfioFileCreateFontObjFromBase( @@ -1154,12 +1208,18 @@ pdfioFileCreateFontObjFromBase( // // 'pdfioFileCreateFontObjFromFile()' - Add a font object to a PDF file. // +// This function embeds a TrueType/OpenType font into a PDF file. The +// "unicode" parameter controls whether the font is encoded for two-byte +// characters (potentially full Unicode, but more typically a subset) +// or to only support the Windows CP1252 (ISO-8859-1 with additional +// characters such as the Euro symbol) subset of Unicode. +// pdfio_obj_t * // O - Font object pdfioFileCreateFontObjFromFile( pdfio_file_t *pdf, // I - PDF file const char *filename, // I - Filename - bool unicode) // I - Unicode font? + bool unicode) // I - Force Unicode { ttf_t *font; // TrueType font int ch, // Current character @@ -1167,12 +1227,13 @@ pdfioFileCreateFontObjFromFile( lastch; // Last character ttf_rect_t bounds; // Font bounds pdfio_dict_t *dict, // Font dictionary - *desc; // Font descriptor + *desc, // Font descriptor + *file; // Font file dictionary pdfio_obj_t *obj, // Font object *desc_obj, // Font descriptor object - *widths_obj; // Font widths object + *file_obj; // Font file object + const char *basefont; // Base font name pdfio_array_t *bbox; // Font bounding box array - pdfio_array_t *widths; // Font widths array pdfio_stream_t *st; // Font stream int fd; // File unsigned char buffer[16384]; // Read buffer @@ -1201,102 +1262,23 @@ pdfioFileCreateFontObjFromFile( return (NULL); } - // Create the font descriptor dictionary and object... - if ((bbox = pdfioArrayCreate(pdf)) == NULL) + // Create the font file dictionary and object... + if ((file = pdfioDictCreate(pdf)) == NULL) { ttfDelete(font); close(fd); return (NULL); } - ttfGetBounds(font, &bounds); + pdfioDictSetName(file, "Filter", "FlateDecode"); - pdfioArrayAppendNumber(bbox, bounds.left); - pdfioArrayAppendNumber(bbox, bounds.bottom); - pdfioArrayAppendNumber(bbox, bounds.right); - pdfioArrayAppendNumber(bbox, bounds.top); - - if ((desc = pdfioDictCreate(pdf)) == NULL) - { - ttfDelete(font); - close(fd); - return (NULL); - } - - pdfioDictSetName(desc, "Type", "FontDescriptor"); - pdfioDictSetName(desc, "FontName", pdfioStringCreate(pdf, ttfGetPostScriptName(font))); - pdfioDictSetName(desc, "FontFamily", pdfioStringCreate(pdf, ttfGetFamily(font))); - pdfioDictSetNumber(desc, "Flags", ttfIsFixedPitch(font) ? 0x21 : 0x20); - pdfioDictSetArray(desc, "FontBBox", bbox); - pdfioDictSetNumber(desc, "ItalicAngle", ttfGetItalicAngle(font)); - pdfioDictSetNumber(desc, "Ascent", ttfGetAscent(font)); - pdfioDictSetNumber(desc, "Descent", ttfGetDescent(font)); - pdfioDictSetNumber(desc, "CapHeight", ttfGetCapHeight(font)); - pdfioDictSetNumber(desc, "XHeight", ttfGetXHeight(font)); - // Note: No TrueType value exists for this but PDF requires it, so we - // calculate a value from 50 to 250... - pdfioDictSetNumber(desc, "StemV", ttfGetWeight(font) / 4 + 25); - - if ((desc_obj = pdfioFileCreateObj(pdf, desc)) == NULL) - { - ttfDelete(font); - close(fd); - return (NULL); - } - - pdfioObjClose(desc_obj); - - // Create the widths array and object... - if ((widths = pdfioArrayCreate(pdf)) == NULL) - { - ttfDelete(font); - close(fd); - return (NULL); - } - - firstch = 32; - lastch = unicode ? 65535 : 255; - - for (ch = firstch; ch <= lastch; ch ++) - pdfioArrayAppendNumber(widths, ttfGetWidth(font, ch)); - - if ((widths_obj = pdfioFileCreateArrayObj(pdf, widths)) == NULL) - { - ttfDelete(font); - close(fd); - return (NULL); - } - - pdfioObjClose(widths_obj); - - // Create the font object... - if ((dict = pdfioDictCreate(pdf)) == NULL) - { - ttfDelete(font); - close(fd); - return (NULL); - } - - pdfioDictSetName(dict, "Type", "Font"); - pdfioDictSetName(dict, "Subtype", "TrueType"); - pdfioDictSetName(dict, "BaseFont", pdfioStringCreate(pdf, ttfGetPostScriptName(font))); - pdfioDictSetName(dict, "Encoding", "WinAnsiEncoding"); // TODO: Fix encoding for unicode - pdfioDictSetName(dict, "Filter", "FlateDecode"); - - pdfioDictSetObj(dict, "FontDescriptor", desc_obj); - pdfioDictSetNumber(dict, "FirstChar", firstch); - pdfioDictSetNumber(dict, "LastChar", lastch); - pdfioDictSetObj(dict, "Widths", widths_obj); - - ttfDelete(font); - - if ((obj = pdfioFileCreateObj(pdf, dict)) == NULL) + if ((file_obj = pdfioFileCreateObj(pdf, file)) == NULL) { close(fd); return (NULL); } - if ((st = pdfioObjCreateStream(obj, PDFIO_FILTER_FLATE)) == NULL) + if ((st = pdfioObjCreateStream(file_obj, PDFIO_FILTER_FLATE)) == NULL) { close(fd); return (NULL); @@ -1315,6 +1297,189 @@ pdfioFileCreateFontObjFromFile( close(fd); pdfioStreamClose(st); + // Create the font descriptor dictionary and object... + if ((bbox = pdfioArrayCreate(pdf)) == NULL) + { + ttfDelete(font); + return (NULL); + } + + ttfGetBounds(font, &bounds); + + pdfioArrayAppendNumber(bbox, bounds.left); + pdfioArrayAppendNumber(bbox, bounds.bottom); + pdfioArrayAppendNumber(bbox, bounds.right); + pdfioArrayAppendNumber(bbox, bounds.top); + + if ((desc = pdfioDictCreate(pdf)) == NULL) + { + ttfDelete(font); + return (NULL); + } + + basefont = pdfioStringCreate(pdf, ttfGetPostScriptName(font)); + + pdfioDictSetName(desc, "Type", "FontDescriptor"); + pdfioDictSetName(desc, "FontName", basefont); + pdfioDictSetObj(desc, "FontFile2", file_obj); + pdfioDictSetNumber(desc, "Flags", ttfIsFixedPitch(font) ? 0x21 : 0x20); + pdfioDictSetArray(desc, "FontBBox", bbox); + pdfioDictSetNumber(desc, "ItalicAngle", ttfGetItalicAngle(font)); + pdfioDictSetNumber(desc, "Ascent", ttfGetAscent(font)); + pdfioDictSetNumber(desc, "Descent", ttfGetDescent(font)); + pdfioDictSetNumber(desc, "CapHeight", ttfGetCapHeight(font)); + pdfioDictSetNumber(desc, "XHeight", ttfGetXHeight(font)); + // Note: No TrueType value exists for this but PDF requires it, so we + // calculate a value from 50 to 250... + pdfioDictSetNumber(desc, "StemV", ttfGetWeight(font) / 4 + 25); + + if ((desc_obj = pdfioFileCreateObj(pdf, desc)) == NULL) + { + ttfDelete(font); + return (NULL); + } + + pdfioObjClose(desc_obj); + + if (unicode) + { + // Unicode (CID) font... + pdfio_dict_t *type2; // CIDFontType2 font dictionary + pdfio_obj_t *type2_obj; // CIDFontType2 font object + pdfio_array_t *descendants; // Decendant font list + pdfio_dict_t *sidict; // CIDSystemInfo dictionary + + // Create a CIDFontType2 dictionary for the Unicode font... + if ((type2 = pdfioDictCreate(pdf)) == NULL) + { + ttfDelete(font); + return (NULL); + } + + if ((sidict = pdfioDictCreate(pdf)) == NULL) + { + ttfDelete(font); + return (NULL); + } + + // CIDSystemInfo mapping to Adobe Identity (Unicode) + pdfioDictSetString(sidict, "Registry", "Adobe"); + pdfioDictSetString(sidict, "Ordering", "Identity"); + pdfioDictSetNumber(sidict, "Supplement", 0); + + // Then the dictionary for the CID base font... + pdfioDictSetName(type2, "Type", "Font"); + pdfioDictSetName(type2, "Subtype", "CIDFontType2"); + pdfioDictSetName(type2, "BaseFont", basefont); + pdfioDictSetDict(type2, "CIDSystemInfo", sidict); + pdfioDictSetName(type2, "CIDToGIDMap", "Identity"); + pdfioDictSetObj(type2, "FontDescriptor", desc_obj); + + if ((type2_obj = pdfioFileCreateObj(pdf, type2)) == NULL) + { + ttfDelete(font); + return (NULL); + } + + pdfioObjClose(type2_obj); + + // Create a Type 0 font object... + if ((descendants = pdfioArrayCreate(pdf)) == NULL) + { + ttfDelete(font); + return (NULL); + } + + pdfioArrayAppendObj(descendants, type2_obj); + + if ((dict = pdfioDictCreate(pdf)) == NULL) + { + ttfDelete(font); + return (NULL); + } + + pdfioDictSetName(dict, "Type", "Font"); + pdfioDictSetName(dict, "Subtype", "Type0"); + pdfioDictSetName(dict, "BaseFont", basefont); + pdfioDictSetArray(dict, "DescendantFonts", descendants); + pdfioDictSetName(dict, "Encoding", "Identity-H"); + + if ((obj = pdfioFileCreateObj(pdf, dict)) == NULL) + return (NULL); + + pdfioObjClose(obj); + } + else + { + // Simple (CP1282 or custom encoding) 8-bit font... + pdfio_array_t *widths; // Font widths array + pdfio_obj_t *widths_obj; // Font widths object + + // Create the widths array and object... + if ((widths = pdfioArrayCreate(pdf)) == NULL) + { + ttfDelete(font); + return (NULL); + } + + firstch = ttfGetMinChar(font); + lastch = ttfGetMaxChar(font); + + if (lastch < 255) + { + // Provide widths for all characters... + for (ch = firstch; ch <= lastch; ch ++) + pdfioArrayAppendNumber(widths, ttfGetWidth(font, ch)); + } + else + { + // Provide widths only for CP1252 characters... + lastch = 255; + + for (ch = firstch; ch < 128; ch ++) + pdfioArrayAppendNumber(widths, ttfGetWidth(font, ch)); + for (; ch < 160; ch ++) + pdfioArrayAppendNumber(widths, ttfGetWidth(font, _pdfio_cp1252[ch - 128])); + for (; ch <= lastch && ch < 128; ch ++) + pdfioArrayAppendNumber(widths, ttfGetWidth(font, ch)); + } + + if ((widths_obj = pdfioFileCreateArrayObj(pdf, widths)) == NULL) + { + ttfDelete(font); + return (NULL); + } + + pdfioObjClose(widths_obj); + + // Create a TrueType font object... + if ((dict = pdfioDictCreate(pdf)) == NULL) + { + ttfDelete(font); + return (NULL); + } + + pdfioDictSetName(dict, "Type", "Font"); + pdfioDictSetName(dict, "Subtype", "TrueType"); + pdfioDictSetName(dict, "BaseFont", basefont); + pdfioDictSetName(dict, "Encoding", "WinAnsi"); + + pdfioDictSetObj(dict, "FontDescriptor", desc_obj); + pdfioDictSetNumber(dict, "FirstChar", firstch); + pdfioDictSetNumber(dict, "LastChar", lastch); + pdfioDictSetObj(dict, "Widths", widths_obj); + + if ((obj = pdfioFileCreateObj(pdf, dict)) == NULL) + { + ttfDelete(font); + return (NULL); + } + + pdfioObjClose(obj); + } + + ttfDelete(font); + return (obj); } @@ -2229,140 +2394,93 @@ update_png_crc( static bool // O - `true` on success, `false` otherwise write_string(pdfio_stream_t *st, // I - Stream + bool unicode, // I - Unicode text? const char *s, // I - String bool *newline) // O - Ends with a newline? { + int ch; // Unicode character const char *ptr; // Pointer into string - // Determine whether this is Unicode or just ASCII... + // Start the string... + if (!pdfioStreamPuts(st, unicode ? "<" : "(")) + return (false); + + // Loop through the string, handling UTF-8 as needed... for (ptr = s; *ptr; ptr ++) { - if (*ptr & 0x80) + if ((*ptr & 0xe0) == 0xc0) { - // UTF-8, allow Unicode up to 255... - if ((*ptr & 0xe0) == 0xc0 && (*ptr & 0x3f) <= 3 && (ptr[1] & 0xc0) == 0x80) - { - ptr ++; - continue; - } - + // Two-byte UTF-8 + ch = ((ptr[0] & 0x1f) << 6) | (ptr[1] & 0x3f); + ptr ++; + } + else if ((*ptr & 0xf0) == 0xe0) + { + // Three-byte UTF-8 + ch = ((ptr[0] & 0x0f) << 12) | ((ptr[1] & 0x3f) << 6) | (ptr[2] & 0x3f); + ptr += 2; + } + else if ((*ptr & 0xf8) == 0xf0) + { + // Four-byte UTF-8 + ch = ((ptr[0] & 0x07) << 18) | ((ptr[1] & 0x3f) << 12) | ((ptr[2] & 0x3f) << 6) | (ptr[3] & 0x3f); + ptr += 3; + } + else if (*ptr == '\n' && newline) + { + *newline = true; break; } - } + else + ch = *ptr & 255; - if (*ptr) - { - // Unicode string... - int ch; // Unicode character - - if (!pdfioStreamPuts(st, "<")) - return (false); - - for (ptr = s; *ptr; ptr ++) + if (unicode) { - if ((*ptr & 0xe0) == 0xc0) - { - // Two-byte UTF-8 - ch = ((ptr[0] & 0x1f) << 6) | (ptr[1] & 0x3f); - ptr ++; - } - else if ((*ptr & 0xf0) == 0xe0) - { - // Three-byte UTF-8 - ch = ((ptr[0] & 0x0f) << 12) | ((ptr[1] & 0x3f) << 6) | (ptr[2] & 0x3f); - ptr += 2; - } - else if ((*ptr & 0xf8) == 0xf0) - { - // Four-byte UTF-8 - ch = ((ptr[0] & 0x07) << 18) | ((ptr[1] & 0x3f) << 12) | ((ptr[2] & 0x3f) << 6) | (ptr[3] & 0x3f); - ptr += 3; - } - else if (*ptr == '\n' && newline) - { - *newline = true; - break; - } - else - ch = *ptr & 255; - // Write a two-byte character... if (!pdfioStreamPrintf(st, "%04X", ch)) - return (false); + return (false); } - if (!pdfioStreamPuts(st, ">")) - return (false); - } - else - { - // ASCII string... - const char *start = s; // Start of fragment - - if (!pdfioStreamPuts(st, "(")) - return (false); - - for (ptr = start; *ptr; ptr ++) + else { - if (*ptr == '\n' && newline) + // Write a one-byte character... + if (ch == '\\' || ch == '(' || ch == ')' || ch < ' ') { - if (ptr > start) + // Escaped character... + if (ch < ' ') { - if (!pdfioStreamWrite(st, start, (size_t)(ptr - start))) - return (false); - - start = ptr + 1; - } - - *newline = true; - break; - } - else if ((*ptr & 0xe0) == 0xc0) - { - // Two-byte UTF-8 - unsigned char ch = (unsigned char)(((ptr[0] & 0x1f) << 6) | (ptr[1] & 0x3f)); - // Unicode character - - if (ptr > start) - { - if (!pdfioStreamWrite(st, start, (size_t)(ptr - start))) - return (false); - } - - if (!pdfioStreamWrite(st, &ch, 1)) - return (false); - - ptr ++; - start = ptr + 1; - } - else if (*ptr == '\\' || *ptr == '(' || *ptr == ')' || *ptr < ' ') - { - if (ptr > start) - { - if (!pdfioStreamWrite(st, start, (size_t)(ptr - start))) - return (false); - } - - start = ptr + 1; - - if (*ptr < ' ') - { - if (!pdfioStreamPrintf(st, "\\%03o", *ptr)) + if (!pdfioStreamPrintf(st, "\\%03o", ch)) return (false); } - else if (!pdfioStreamPrintf(st, "\\%c", *ptr)) + else if (!pdfioStreamPrintf(st, "\\%c", ch)) return (false); } - } + else + { + // Non-escaped character... + if (ch > 255) + { + // Try mapping from Unicode to CP1252... + int i; // Looping var - if (ptr > start) - { - if (!pdfioStreamPrintf(st, "%s)", start)) - return (false); + for (i = 0; i < (int)(sizeof(_pdfio_cp1252) / sizeof(_pdfio_cp1252[0])); i ++) + { + if (ch == _pdfio_cp1252[i]) + { + ch = i + 128; + break; + } + } + + if (ch > 255) + ch = '?'; // Unsupported chars map to ? + } + + // Write the character... + pdfioStreamPutChar(st, ch); + } } - else if (!pdfioStreamPuts(st, ")")) - return (false); } - return (true); + return (pdfioStreamPuts(st, unicode ? ">" : ")")); } diff --git a/pdfio-content.h b/pdfio-content.h index 39854c2..1226d21 100644 --- a/pdfio-content.h +++ b/pdfio-content.h @@ -139,9 +139,9 @@ extern bool pdfioContentTextEnd(pdfio_stream_t *st) PDFIO_PUBLIC; extern bool pdfioContentTextMoveLine(pdfio_stream_t *st, double tx, double ty) PDFIO_PUBLIC; extern bool pdfioContentTextMoveTo(pdfio_stream_t *st, double tx, double ty) PDFIO_PUBLIC; extern bool pdfioContentTextNextLine(pdfio_stream_t *st) PDFIO_PUBLIC; -extern bool pdfioContentTextShow(pdfio_stream_t *st, const char *s) PDFIO_PUBLIC; -extern bool pdfioContentTextShowf(pdfio_stream_t *st, const char *format, ...) PDFIO_PUBLIC PDFIO_FORMAT(2,3); -extern bool pdfioContentTextShowJustified(pdfio_stream_t *st, size_t num_fragments, const double *offsets, const char * const *fragments) PDFIO_PUBLIC; +extern bool pdfioContentTextShow(pdfio_stream_t *st, bool unicode, const char *s) PDFIO_PUBLIC; +extern bool pdfioContentTextShowf(pdfio_stream_t *st, bool unicode, const char *format, ...) PDFIO_PUBLIC PDFIO_FORMAT(3,4); +extern bool pdfioContentTextShowJustified(pdfio_stream_t *st, bool unicode, size_t num_fragments, const double *offsets, const char * const *fragments) PDFIO_PUBLIC; // Resource helpers... extern pdfio_obj_t *pdfioFileCreateFontObjFromBase(pdfio_file_t *pdf, const char *name) PDFIO_PUBLIC; diff --git a/pdfio-stream.c b/pdfio-stream.c index f9624dc..0b8de57 100644 --- a/pdfio-stream.c +++ b/pdfio-stream.c @@ -556,6 +556,26 @@ pdfioStreamPrintf( } +// +// '()' - Write a single character to a stream. +// + +bool // O - `true` on success, `false` on failure +pdfioStreamPutChar(pdfio_stream_t *st, // I - Stream + int ch) // I - Character +{ + char buffer[1]; // Write buffer + + + if (!st || st->pdf->mode != _PDFIO_MODE_WRITE) + return (false); + + buffer[0] = (char)ch; + + return (pdfioStreamWrite(st, buffer, 1)); +} + + // // 'pdfioStreamPuts()' - Write a literal string to a stream. // diff --git a/pdfio.h b/pdfio.h index 0221d5a..b98cc26 100644 --- a/pdfio.h +++ b/pdfio.h @@ -181,6 +181,7 @@ extern bool pdfioStreamConsume(pdfio_stream_t *st, size_t bytes) PDFIO_PUBLIC; extern bool pdfioStreamGetToken(pdfio_stream_t *st, char *buffer, size_t bufsize) PDFIO_PUBLIC; extern ssize_t pdfioStreamPeek(pdfio_stream_t *st, void *buffer, size_t bytes) PDFIO_PUBLIC; extern bool pdfioStreamPrintf(pdfio_stream_t *st, const char *format, ...) PDFIO_PUBLIC PDFIO_FORMAT(2,3); +extern bool pdfioStreamPutChar(pdfio_stream_t *st, int ch) PDFIO_PUBLIC; extern bool pdfioStreamPuts(pdfio_stream_t *st, const char *s) PDFIO_PUBLIC; extern ssize_t pdfioStreamRead(pdfio_stream_t *st, void *buffer, size_t bytes) PDFIO_PUBLIC; extern bool pdfioStreamWrite(pdfio_stream_t *st, const void *buffer, size_t bytes) PDFIO_PUBLIC; diff --git a/testpdfio.c b/testpdfio.c index cc7eaba..47405fa 100644 --- a/testpdfio.c +++ b/testpdfio.c @@ -388,7 +388,7 @@ draw_image(pdfio_stream_t *st, return (1); printf("pdfioContentTextShow(\"%s\"): ", label); - if (pdfioContentTextShow(st, label)) + if (pdfioContentTextShow(st, false, label)) puts("PASS"); else return (1); @@ -829,7 +829,7 @@ write_color_test(pdfio_file_t *pdf, // I - PDF file goto error; fputs("pdfioContentTextShow(\"AdobeRGB\"): ", stdout); - if (pdfioContentTextShow(st, "AdobeRGB")) + if (pdfioContentTextShow(st, false, "AdobeRGB")) puts("PASS"); else goto error; @@ -841,7 +841,7 @@ write_color_test(pdfio_file_t *pdf, // I - PDF file goto error; fputs("pdfioContentTextShow(\"DisplayP3\"): ", stdout); - if (pdfioContentTextShow(st, "DisplayP3")) + if (pdfioContentTextShow(st, false, "DisplayP3")) puts("PASS"); else goto error; @@ -853,7 +853,7 @@ write_color_test(pdfio_file_t *pdf, // I - PDF file goto error; fputs("pdfioContentTextShow(\"sRGB\"): ", stdout); - if (pdfioContentTextShow(st, "sRGB")) + if (pdfioContentTextShow(st, false, "sRGB")) puts("PASS"); else goto error; @@ -865,7 +865,7 @@ write_color_test(pdfio_file_t *pdf, // I - PDF file goto error; fputs("pdfioContentTextShow(\"ProPhotoRGB\"): ", stdout); - if (pdfioContentTextShow(st, "ProPhotoRGB")) + if (pdfioContentTextShow(st, false, "ProPhotoRGB")) puts("PASS"); else goto error; @@ -877,7 +877,7 @@ write_color_test(pdfio_file_t *pdf, // I - PDF file goto error; fputs("pdfioContentTextShow(\"DeviceCMYK\"): ", stdout); - if (pdfioContentTextShow(st, "DeviceCMYK")) + if (pdfioContentTextShow(st, false, "DeviceCMYK")) puts("PASS"); else goto error; @@ -1049,7 +1049,7 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file { "Welcome\n", "Welkom\n", -// "ḫaṣānu\n", + "ḫaṣānu\n", "Mayad-ayad nga pad-abot\n", "Mir se vjên\n", "Mirë se vjen\n", @@ -1058,15 +1058,15 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file "Ghini vinit!\n", "Bienveníu\n", "Miro peicak\n", -// "Xoş gəlmişsiniz!\n", + "Xoş gəlmişsiniz!\n", "Salamat datang\n", -// "Сәләм бирем!\n", + "Сәләм бирем!\n", "Menjuah-juah!\n", -// "Še das d' kemma bisd\n", + "Še das d' kemma bisd\n", "Mwaiseni\n", "Maogmáng Pag-abót\n", "Welkam\n", -// "Dobrodošli\n", + "Dobrodošli\n", "Degemer mat\n", "Benvingut\n", "Maayong pag-abot\n", @@ -1074,7 +1074,7 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file "Bienvenida\n", "Bien binidu\n", "Bienbenidu\n", -// "Hóʔą\n", + "Hóʔą\n", "Boolkhent!\n", "Kopivosian do kinoikatan\n", "Malipayeng Pag-abot!\n", @@ -1085,7 +1085,7 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file "Emedi\n", "Welkumin\n", "Tere tulemast\n", -// "Woé zɔ\n", + "Woé zɔ\n", "Bienveníu\n", "Vælkomin\n", "Bula\n", @@ -1098,8 +1098,8 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file "Benvignût\n", "Benvido\n", "Willkommen\n", -// "Ἀσπάζομαι!\n", -// "Καλώς Ήρθες\n", + "Ἀσπάζομαι!\n", + "Καλώς Ήρθες\n", "Tikilluarit\n", "Byen venu\n", "Sannu da zuwa\n", @@ -1110,7 +1110,7 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file "Üdvözlet\n", "Selamat datai\n", "Velkomin\n", -// "Nnọọ\n", + "Nnọọ\n", "Selamat datang\n", "Qaimarutin\n", "Fáilte\n", @@ -1119,11 +1119,11 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file "Murakaza neza\n", "Mauri\n", "Tu be xér hatî ye!\n", -// "Taŋyáŋ yahí\n", + "Taŋyáŋ yahí\n", "Salve\n", -// "Laipni lūdzam\n", + "Laipni lūdzam\n", "Wilkóm\n", -// "Sveiki atvykę\n", + "Sveiki atvykę\n", "Willkamen\n", "Mu amuhezwi\n", "Tukusanyukidde\n", @@ -1131,15 +1131,15 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file "Swagatam\n", "Tonga soa\n", "Selamat datang\n", -// "Merħba\n", -// "B’a’ntulena\n", + "Merħba\n", + "B’a’ntulena\n", "Failt ort\n", "Haere mai\n", "mai\n", -// "Pjila’si\n", + "Pjila’si\n", "Benvegnüu\n", "Ne y kena\n", -// "Ximopanōltih\n", + "Ximopanōltih\n", "Yá'át'ééh\n", "Siyalemukela\n", "Siyalemukela\n", @@ -1148,7 +1148,7 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file "Velkommen\n", "Benvengut!\n", "Bon bini\n", -// "Witam Cię\n", + "Witam Cię\n", "Bem-vindo\n", "Haykuykuy!\n", "T'aves baxtalo\n", @@ -1160,7 +1160,7 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file "Mauya\n", "Bon vinutu\n", "Vitaj\n", -// "Dobrodošli\n", + "Dobrodošli\n", "Soo dhowow\n", "Witaj\n", "Bienvenido\n", @@ -1179,8 +1179,8 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file "Lek oy li la tale\n", "amogetswe\n", "Tempokani\n", -// "Hoş geldin\n", -// "Koş geldiniz\n", + "Hoş geldin\n", + "Koş geldiniz\n", "Ulufale mai!\n", "Xush kelibsiz\n", "Benvignùo\n", @@ -1191,14 +1191,14 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file "Croeso\n", "Merhbe\n", "Wamkelekile\n", -// "Märr-ŋamathirri\n", -// "Ẹ ku abọ\n", + "Märr-ŋamathirri\n", + "Ẹ ku abọ\n", "Kíimak 'oolal\n", "Ngiyakwemukela\n" }; fputs("pdfioFileCreateFontObjFromFile(OpenSans-Regular.ttf): ", stdout); - if ((opensans = pdfioFileCreateFontObjFromFile(pdf, "testfiles/OpenSans-Regular.ttf", false)) != NULL) + if ((opensans = pdfioFileCreateFontObjFromFile(pdf, "testfiles/OpenSans-Regular.ttf", true)) != NULL) puts("PASS"); else return (1); @@ -1267,7 +1267,7 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file } printf("pdfioContentTextShow(\"%s\"): ", welcomes[i]); - if (pdfioContentTextShow(st, welcomes[i])) + if (pdfioContentTextShow(st, true, welcomes[i])) puts("PASS"); else return (1); @@ -1329,7 +1329,7 @@ write_header_footer( return (1); printf("pdfioContentTextShow(\"%s\"): ", title); - if (pdfioContentTextShow(st, title)) + if (pdfioContentTextShow(st, false, title)) puts("PASS"); else return (1); @@ -1347,7 +1347,7 @@ write_header_footer( return (1); printf("pdfioContentTextShowf(\"%d\"): ", number); - if (pdfioContentTextShowf(st, "%d", number)) + if (pdfioContentTextShowf(st, false, "%d", number)) puts("PASS"); else return (1); @@ -1740,7 +1740,7 @@ write_png_test(pdfio_file_t *pdf, // I - PDF file goto error; fputs("pdfioContentTextShow(\"PNG RGB Color\"): ", stdout); - if (pdfioContentTextShow(st, "PNG RGB Color")) + if (pdfioContentTextShow(st, false, "PNG RGB Color")) puts("PASS"); else goto error; @@ -1752,7 +1752,7 @@ write_png_test(pdfio_file_t *pdf, // I - PDF file goto error; fputs("pdfioContentTextShow(\"PNG Gray\"): ", stdout); - if (pdfioContentTextShow(st, "PNG Gray")) + if (pdfioContentTextShow(st, false, "PNG Gray")) puts("PASS"); else goto error; @@ -1764,7 +1764,7 @@ write_png_test(pdfio_file_t *pdf, // I - PDF file goto error; fputs("pdfioContentTextShow(\"PNG Indexed\"): ", stdout); - if (pdfioContentTextShow(st, "PNG Indexed")) + if (pdfioContentTextShow(st, false, "PNG Indexed")) puts("PASS"); else goto error; @@ -1936,7 +1936,7 @@ write_text_test(pdfio_file_t *pdf, // I - PDF file if (!pdfioContentSetFillColorDeviceGray(st, 0.75)) goto error; - if (!pdfioContentTextShowf(st, "%3d ", flinenum)) + if (!pdfioContentTextShowf(st, false, "%3d ", flinenum)) goto error; if (!pdfioContentSetFillColorDeviceGray(st, 0.0)) goto error; @@ -1948,15 +1948,15 @@ write_text_test(pdfio_file_t *pdf, // I - PDF file temp[80] = '\n'; temp[81] = '\0'; - if (!pdfioContentTextShow(st, temp)) + if (!pdfioContentTextShow(st, false, temp)) goto error; - if (!pdfioContentTextShowf(st, " %s", line + 80)) + if (!pdfioContentTextShowf(st, false, " %s", line + 80)) goto error; plinenum ++; } - else if (!pdfioContentTextShow(st, line)) + else if (!pdfioContentTextShow(st, false, line)) goto error; plinenum ++; diff --git a/ttf.c b/ttf.c index 23c5d42..e0bc1fb 100644 --- a/ttf.c +++ b/ttf.c @@ -187,6 +187,8 @@ struct _ttf_s char *postscript_name; // PostScript name string char *version; // Font version string bool is_fixed; // Is this a fixed-width font? + int max_char, // Last character in font + min_char; // First character in font _ttf_metric_t *widths[TTF_FONT_MAX_CHAR / 256]; // Character metrics (sparse array) float units; // Width units @@ -447,6 +449,8 @@ ttfCreate(const char *filename, // I - Filename font->x_height = 3 * font->ascent / 5; // Build a sparse glyph widths table... + font->min_char = -1; + for (i = 0; i < num_cmap; i ++) { if (cmap[i] >= 0) @@ -454,6 +458,12 @@ ttfCreate(const char *filename, // I - Filename int bin = i / 256, // Sub-array bin glyph = cmap[i]; // Glyph index + // Update min/max... + if (font->min_char < 0) + font->min_char = i; + + font->max_char = i; + // Allocate a sub-array as needed... if (!font->widths[bin]) font->widths[bin] = (_ttf_metric_t *)calloc(256, sizeof(_ttf_metric_t)); @@ -720,6 +730,28 @@ ttfGetItalicAngle(ttf_t *font) // I - Font } +// +// 'ttfGetMaxChar()' - Get the last character in the font. +// + +int // O - Last character in font +ttfGetMaxChar(ttf_t *font) // I - Font +{ + return (font ? font->max_char : 0); +} + + +// +// 'ttfGetMinChar()' - Get the first character in the font. +// + +int // O - First character in font +ttfGetMinChar(ttf_t *font) // I - Font +{ + return (font ? font->min_char : 0); +} + + // // 'ttfGetNumFonts()' - Get the number of fonts in this collection. // diff --git a/ttf.h b/ttf.h index d8d9c1d..e3e9b94 100644 --- a/ttf.h +++ b/ttf.h @@ -95,6 +95,8 @@ extern int ttfGetDescent(ttf_t *font); extern ttf_rect_t *ttfGetExtents(ttf_t *font, float size, const char *s, ttf_rect_t *extents); extern const char *ttfGetFamily(ttf_t *font); extern float ttfGetItalicAngle(ttf_t *font); +extern int ttfGetMaxChar(ttf_t *font); +extern int ttfGetMinChar(ttf_t *font); extern size_t ttfGetNumFonts(ttf_t *font); extern const char *ttfGetPostScriptName(ttf_t *font); extern ttf_stretch_t ttfGetStretch(ttf_t *font);