Compare commits

...

6 Commits

Author SHA1 Message Date
Michael R Sweet
06f38edcc7
Add pdfioFileCreateFontObjFromData function (Issue #120) 2025-04-12 16:25:34 -04:00
Michael R Sweet
76c1cc694f
Bump version for start of 1.6.x development. 2025-04-12 15:52:28 -04:00
Michael R Sweet
4219b8fd77
Update release date. 2025-04-12 15:12:07 -04:00
Michael R Sweet
064e7fa473
Fix makesrcdist script. 2025-04-12 15:11:36 -04:00
Michael R Sweet
ea9b7843fc
Bump version in NuGet files and update docos. 2025-04-12 14:38:40 -04:00
Michael R Sweet
755efe08da
Range check dictionary values in pdfioImageGetBytesPerLine (Issue #121) 2025-04-12 14:33:13 -04:00
14 changed files with 963 additions and 565 deletions

View File

@ -2,7 +2,14 @@ Changes in PDFio
================ ================
v1.5.2 - YYYY-MM-DD v1.6.0 - YYYY-MM-DD
-------------------
- Added `pdfioFileCreateFontObjFromData` function for embedding fonts in
memory (Issue #120)
v1.5.2 - 2025-04-12
------------------- -------------------
- Updated maximum allowed PDF string size to 64k (Issue #117) - Updated maximum allowed PDF string size to 64k (Issue #117)
@ -11,6 +18,7 @@ v1.5.2 - YYYY-MM-DD
- Fixed form detection in `pdfioinfo` example code (Issue #114) - Fixed form detection in `pdfioinfo` example code (Issue #114)
- Fixed parsing of certain date/time values (Issue #115) - Fixed parsing of certain date/time values (Issue #115)
- Fixed support for empty name values (Issue #116) - Fixed support for empty name values (Issue #116)
- Fixed range checking in `pdfioImageGetBytesPerLine` (Issue #121)
v1.5.1 - 2025-03-28 v1.5.1 - 2025-03-28

24
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for pdfio 1.5.2. # Generated by GNU Autoconf 2.71 for pdfio 1.6.0.
# #
# Report bugs to <https://github.com/michaelrsweet/pdfio/issues>. # Report bugs to <https://github.com/michaelrsweet/pdfio/issues>.
# #
@ -610,8 +610,8 @@ MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='pdfio' PACKAGE_NAME='pdfio'
PACKAGE_TARNAME='pdfio' PACKAGE_TARNAME='pdfio'
PACKAGE_VERSION='1.5.2' PACKAGE_VERSION='1.6.0'
PACKAGE_STRING='pdfio 1.5.2' PACKAGE_STRING='pdfio 1.6.0'
PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues' PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues'
PACKAGE_URL='https://www.msweet.org/pdfio' PACKAGE_URL='https://www.msweet.org/pdfio'
@ -1295,7 +1295,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing. # Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF cat <<_ACEOF
\`configure' configures pdfio 1.5.2 to adapt to many kinds of systems. \`configure' configures pdfio 1.6.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]... Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1361,7 +1361,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of pdfio 1.5.2:";; short | recursive ) echo "Configuration of pdfio 1.6.0:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@ -1460,7 +1460,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
pdfio configure 1.5.2 pdfio configure 1.6.0
generated by GNU Autoconf 2.71 generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc. Copyright (C) 2021 Free Software Foundation, Inc.
@ -1678,7 +1678,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. running configure, to aid debugging if configure makes a mistake.
It was created by pdfio $as_me 1.5.2, which was It was created by pdfio $as_me 1.6.0, which was
generated by GNU Autoconf 2.71. Invocation command line was generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw $ $0$ac_configure_args_raw
@ -2434,9 +2434,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
PDFIO_VERSION="1.5.2" PDFIO_VERSION="1.6.0"
PDFIO_VERSION_MAJOR="`echo 1.5.2 | awk -F. '{print $1}'`" PDFIO_VERSION_MAJOR="`echo 1.6.0 | awk -F. '{print $1}'`"
PDFIO_VERSION_MINOR="`echo 1.5.2 | awk -F. '{printf("%d\n",$2);}'`" PDFIO_VERSION_MINOR="`echo 1.6.0 | awk -F. '{printf("%d\n",$2);}'`"
@ -5099,7 +5099,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" ac_log="
This file was extended by pdfio $as_me 1.5.2, which was This file was extended by pdfio $as_me 1.6.0, which was
generated by GNU Autoconf 2.71. Invocation command line was generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@ -5155,7 +5155,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped' ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\ ac_cs_version="\\
pdfio config.status 1.5.2 pdfio config.status 1.6.0
configured by $0, generated by GNU Autoconf 2.71, configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"

View File

@ -21,7 +21,7 @@ AC_PREREQ([2.70])
dnl Package name and version... dnl Package name and version...
AC_INIT([pdfio], [1.5.2], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio]) AC_INIT([pdfio], [1.6.0], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio])
PDFIO_VERSION="AC_PACKAGE_VERSION" PDFIO_VERSION="AC_PACKAGE_VERSION"
PDFIO_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`" PDFIO_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`"

View File

@ -1,4 +1,4 @@
.TH pdfio 3 "pdf read/write library" "2025-04-04" "pdf read/write library" .TH pdfio 3 "pdf read/write library" "2025-04-12" "pdf read/write library"
.SH NAME .SH NAME
pdfio \- pdf read/write library pdfio \- pdf read/write library
.SH Introduction .SH Introduction

View File

@ -4787,7 +4787,7 @@ size_t pdfioImageGetBytesPerLine(<a href="#pdfio_obj_t">pdfio_obj_t</a> *obj);</
<td class="description">Image object</td></tr> <td class="description">Image object</td></tr>
</tbody></table> </tbody></table>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description">Number of bytes per line</p> <p class="description">Number of bytes per line or <code>0</code> on error</p>
<h3 class="function"><a id="pdfioImageGetHeight">pdfioImageGetHeight</a></h3> <h3 class="function"><a id="pdfioImageGetHeight">pdfioImageGetHeight</a></h3>
<p class="description">Get the height of an image object.</p> <p class="description">Get the height of an image object.</p>
<p class="code"> <p class="code">

View File

@ -32,11 +32,11 @@ if test $(grep AC_INIT configure.ac | awk '{print $2}') != "[$version],"; then
status=1 status=1
fi fi
if test $(head -4 CHANGES.md | tail -1 | awk '{print $1}') != "v$version"; then if test $(head -5 CHANGES.md | tail -1 | awk '{print $1}') != "v$version"; then
echo "Still need to update CHANGES.md version number." echo "Still need to update CHANGES.md version number."
status=1 status=1
fi fi
if test $(head -4 CHANGES.md | tail -1 | awk '{print $3}') = "YYYY-MM-DD"; then if test $(head -5 CHANGES.md | tail -1 | awk '{print $3}') = "YYYY-MM-DD"; then
echo "Still need to update CHANGES.md release date." echo "Still need to update CHANGES.md release date."
status=1 status=1
fi fi

View File

@ -84,6 +84,7 @@ typedef pdfio_obj_t *(*_pdfio_image_func_t)(pdfio_dict_t *dict, int fd);
static pdfio_obj_t *copy_jpeg(pdfio_dict_t *dict, int fd); 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 pdfio_obj_t *copy_png(pdfio_dict_t *dict, int fd);
static bool create_cp1252(pdfio_file_t *pdf); static bool create_cp1252(pdfio_file_t *pdf);
static pdfio_obj_t *create_font(pdfio_obj_t *file_obj, ttf_t *font, bool unicode);
static pdfio_obj_t *create_image(pdfio_dict_t *dict, const unsigned char *data, size_t width, size_t height, size_t depth, size_t num_colors, bool alpha); static pdfio_obj_t *create_image(pdfio_dict_t *dict, const unsigned char *data, size_t width, size_t height, size_t depth, size_t num_colors, bool alpha);
#ifdef HAVE_LIBPNG #ifdef HAVE_LIBPNG
static void png_error_func(png_structp pp, png_const_charp message); static void png_error_func(png_structp pp, png_const_charp message);
@ -1536,9 +1537,84 @@ pdfioFileCreateFontObjFromBase(
// //
// 'pdfioFileCreateFontObjFromFile()' - Add a font object to a PDF file. // 'pdfioFileCreateFontObjFromData()' - Add a font in memory to a PDF file.
// //
// This function embeds a TrueType/OpenType font into a PDF file. The // This function embeds TrueType/OpenType font data 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
pdfioFileCreateFontObjFromData(
pdfio_file_t *pdf, // I - PDF file
const void *data, // I - Font data in memory
size_t datasize, // I - Size of font in memory
bool unicode) // I - Force Unicode
{
ttf_t *font; // TrueType font
pdfio_obj_t *obj, // Font object
*file_obj = NULL; // File object
pdfio_dict_t *file; // Font file dictionary
pdfio_stream_t *st = NULL; // Font stream
// Range check input...
if (!pdf)
return (NULL);
if (!data || !datasize)
{
_pdfioFileError(pdf, "No TrueType/OpenType data specified.");
return (NULL);
}
// Create a TrueType font object from the data...
if ((font = ttfCreateData(data, datasize, 0, (ttf_err_cb_t)ttf_error_cb, pdf)) == NULL)
return (NULL);
// Create the font file dictionary and object...
if ((file = pdfioDictCreate(pdf)) == NULL)
goto error;
pdfioDictSetName(file, "Filter", "FlateDecode");
if ((file_obj = pdfioFileCreateObj(pdf, file)) == NULL)
goto error;
if ((st = pdfioObjCreateStream(file_obj, PDFIO_FILTER_FLATE)) == NULL)
goto error;
if (!pdfioStreamWrite(st, data, datasize))
goto error;
pdfioStreamClose(st);
// Create the font object...
if ((obj = create_font(file_obj, font, unicode)) == NULL)
ttfDelete(font);
return (obj);
// If we get here we had an unrecoverable error...
error:
if (st)
pdfioStreamClose(st);
else
pdfioObjClose(file_obj);
ttfDelete(font);
return (NULL);
}
//
// 'pdfioFileCreateFontObjFromFile()' - Add a font file to a PDF file.
//
// This function embeds a TrueType/OpenType font file into a PDF file. The
// "unicode" parameter controls whether the font is encoded for two-byte // "unicode" parameter controls whether the font is encoded for two-byte
// characters (potentially full Unicode, but more typically a subset) // characters (potentially full Unicode, but more typically a subset)
// or to only support the Windows CP1252 (ISO-8859-1 with additional // or to only support the Windows CP1252 (ISO-8859-1 with additional
@ -1552,16 +1628,10 @@ pdfioFileCreateFontObjFromFile(
bool unicode) // I - Force Unicode bool unicode) // I - Force Unicode
{ {
ttf_t *font; // TrueType font ttf_t *font; // TrueType font
ttf_rect_t bounds; // Font bounds pdfio_dict_t *file; // Font file dictionary
pdfio_dict_t *dict, // Font dictionary pdfio_obj_t *obj, // Font object
*desc, // Font descriptor *file_obj = NULL; // Font file object
*file; // Font file dictionary pdfio_stream_t *st = NULL; // Font stream
pdfio_obj_t *obj = NULL, // Font object
*desc_obj, // Font descriptor object
*file_obj; // Font file object
const char *basefont; // Base font name
pdfio_array_t *bbox; // Font bounding box array
pdfio_stream_t *st; // Font stream
int fd = -1; // File int fd = -1; // File
unsigned char buffer[16384]; // Read buffer unsigned char buffer[16384]; // Read buffer
ssize_t bytes; // Bytes read ssize_t bytes; // Bytes read
@ -1584,360 +1654,50 @@ pdfioFileCreateFontObjFromFile(
} }
if ((font = ttfCreate(filename, 0, (ttf_err_cb_t)ttf_error_cb, pdf)) == NULL) if ((font = ttfCreate(filename, 0, (ttf_err_cb_t)ttf_error_cb, pdf)) == NULL)
{ goto error;
close(fd);
return (NULL);
}
// Create the font file dictionary and object... // Create the font file dictionary and object...
if ((file = pdfioDictCreate(pdf)) == NULL) if ((file = pdfioDictCreate(pdf)) == NULL)
goto done; goto error;
pdfioDictSetName(file, "Filter", "FlateDecode"); pdfioDictSetName(file, "Filter", "FlateDecode");
if ((file_obj = pdfioFileCreateObj(pdf, file)) == NULL) if ((file_obj = pdfioFileCreateObj(pdf, file)) == NULL)
goto done; goto error;
if ((st = pdfioObjCreateStream(file_obj, PDFIO_FILTER_FLATE)) == NULL) if ((st = pdfioObjCreateStream(file_obj, PDFIO_FILTER_FLATE)) == NULL)
goto done; goto error;
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
{ {
if (!pdfioStreamWrite(st, buffer, (size_t)bytes)) if (!pdfioStreamWrite(st, buffer, (size_t)bytes))
{ goto error;
pdfioStreamClose(st);
goto done;
}
} }
close(fd); close(fd);
fd = -1;
pdfioStreamClose(st);
// Create the font descriptor dictionary and object...
if ((bbox = pdfioArrayCreate(pdf)) == NULL)
goto done;
ttfGetBounds(font, &bounds);
pdfioArrayAppendNumber(bbox, bounds.left);
pdfioArrayAppendNumber(bbox, bounds.bottom);
pdfioArrayAppendNumber(bbox, bounds.right);
pdfioArrayAppendNumber(bbox, bounds.top);
if ((desc = pdfioDictCreate(pdf)) == NULL)
goto done;
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 generic value from 50 to 250 based on the weight...
pdfioDictSetNumber(desc, "StemV", ttfGetWeight(font) / 4 + 25);
if ((desc_obj = pdfioFileCreateObj(pdf, desc)) == NULL)
goto done;
pdfioObjClose(desc_obj);
if (unicode)
{
// Unicode (CID) font...
pdfio_dict_t *cid2gid, // CIDToGIDMap dictionary
*to_unicode; // ToUnicode dictionary
pdfio_obj_t *cid2gid_obj, // CIDToGIDMap object
*to_unicode_obj;// ToUnicode object
size_t i, j, // Looping vars
num_cmap; // Number of CMap entries
const int *cmap; // CMap entries
int min_glyph, // First glyph
max_glyph; // Last glyph
unsigned short glyphs[65536]; // Glyph to Unicode mapping
unsigned char *bufptr, // Pointer into buffer
*bufend; // End of buffer
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
pdfio_array_t *w_array, // Width array
*temp_array; // Temporary width sub-array
int w0, w1; // Widths
// Create a CIDSystemInfo mapping to Adobe UCS2 v0 (Unicode)
if ((sidict = pdfioDictCreate(pdf)) == NULL)
goto done;
pdfioDictSetString(sidict, "Registry", "Adobe");
pdfioDictSetString(sidict, "Ordering", "Identity");
pdfioDictSetNumber(sidict, "Supplement", 0);
// Create a CIDToGIDMap object for the Unicode font...
if ((cid2gid = pdfioDictCreate(pdf)) == NULL)
goto done;
#ifndef DEBUG
pdfioDictSetName(cid2gid, "Filter", "FlateDecode");
#endif // !DEBUG
if ((cid2gid_obj = pdfioFileCreateObj(pdf, cid2gid)) == NULL)
goto done;
#ifdef DEBUG
if ((st = pdfioObjCreateStream(cid2gid_obj, PDFIO_FILTER_NONE)) == NULL)
#else
if ((st = pdfioObjCreateStream(cid2gid_obj, PDFIO_FILTER_FLATE)) == NULL)
#endif // DEBUG
goto done;
cmap = ttfGetCMap(font, &num_cmap);
min_glyph = 65536;
max_glyph = 0;
memset(glyphs, 0, sizeof(glyphs));
PDFIO_DEBUG("pdfioFileCreateFontObjFromFile: num_cmap=%u\n", (unsigned)num_cmap);
for (i = 0, bufptr = buffer, bufend = buffer + sizeof(buffer); i < num_cmap; i ++)
{
PDFIO_DEBUG("pdfioFileCreateFontObjFromFile: cmap[%u]=%d\n", (unsigned)i, cmap[i]);
if (cmap[i] < 0 || cmap[i] >= (int)(sizeof(glyphs) / sizeof(glyphs[0])))
{
// Map undefined glyph to .notdef...
*bufptr++ = 0;
*bufptr++ = 0;
}
else
{
// Map to specified glyph...
*bufptr++ = (unsigned char)(cmap[i] >> 8);
*bufptr++ = (unsigned char)(cmap[i] & 255);
glyphs[cmap[i]] = (unsigned short)i;
if (cmap[i] < min_glyph)
min_glyph = cmap[i];
if (cmap[i] > max_glyph)
max_glyph = cmap[i];
}
if (bufptr >= bufend)
{
// Flush buffer...
if (!pdfioStreamWrite(st, buffer, (size_t)(bufptr - buffer)))
{
pdfioStreamClose(st);
goto done;
}
bufptr = buffer;
}
}
if (bufptr > buffer)
{
// Flush buffer...
if (!pdfioStreamWrite(st, buffer, (size_t)(bufptr - buffer)))
{
pdfioStreamClose(st);
goto done;
}
}
pdfioStreamClose(st); pdfioStreamClose(st);
// ToUnicode mapping object // Create the font object...
to_unicode = pdfioDictCreate(pdf); if ((obj = create_font(file_obj, font, unicode)) == NULL)
pdfioDictSetName(to_unicode, "Type", "CMap"); ttfDelete(font);
pdfioDictSetName(to_unicode, "CMapName", "Adobe-Identity-UCS2");
pdfioDictSetDict(to_unicode, "CIDSystemInfo", sidict);
#ifndef DEBUG return (obj);
pdfioDictSetName(to_unicode, "Filter", "FlateDecode");
#endif // !DEBUG
if ((to_unicode_obj = pdfioFileCreateObj(pdf, to_unicode)) == NULL) // If we get here we had an unrecoverable error...
goto done; error:
#ifdef DEBUG
if ((st = pdfioObjCreateStream(to_unicode_obj, PDFIO_FILTER_NONE)) == NULL)
#else
if ((st = pdfioObjCreateStream(to_unicode_obj, PDFIO_FILTER_FLATE)) == NULL)
#endif // DEBUG
goto done;
pdfioStreamPuts(st,
"stream\n"
"/CIDInit /ProcSet findresource begin\n"
"12 dict begin\n"
"begincmap\n"
"/CIDSystemInfo<<\n"
"/Registry (Adobe)\n"
"/Ordering (UCS2)\n"
"/Supplement 0\n"
">> def\n"
"/CMapName /Adobe-Identity-UCS2 def\n"
"/CMapType 2 def\n"
"1 begincodespacerange\n"
"<0000> <FFFF>\n"
"endcodespacerange\n"
"endcmap\n"
"CMapName currentdict /CMap defineresource pop\n"
"end\n"
"end\n");
pdfioStreamClose(st);
// Create a CIDFontType2 dictionary for the Unicode font...
if ((type2 = pdfioDictCreate(pdf)) == NULL)
goto done;
// Width array
if ((w_array = pdfioArrayCreate(pdf)) == NULL)
goto done;
for (i = 0, w0 = ttfGetWidth(font, 0), w1 = 0; i < 65536; w0 = w1)
{
for (j = 1; (i + j) < 65536; j ++)
{
if ((w1 = ttfGetWidth(font, (int)(i + j))) != w0)
break;
}
if (j >= 4)
{
// Encode a long sequence of zeros...
// Encode a repeating sequence...
pdfioArrayAppendNumber(w_array, (double)i);
pdfioArrayAppendNumber(w_array, (double)(i + j - 1));
pdfioArrayAppendNumber(w_array, w0);
i += j;
}
else
{
// Encode a non-repeating sequence...
pdfioArrayAppendNumber(w_array, (double)i);
if ((temp_array = pdfioArrayCreate(pdf)) == NULL)
goto done;
pdfioArrayAppendNumber(temp_array, w0);
for (i ++; i < 65536 && pdfioArrayGetSize(temp_array) < 8191; i ++, w0 = w1)
{
if ((w1 = ttfGetWidth(font, (int)i)) == w0 && i < 65530)
{
for (j = 1; j < 4; j ++)
{
if (ttfGetWidth(font, (int)(i + j)) != w0)
break;
}
if (j >= 4)
break;
}
pdfioArrayAppendNumber(temp_array, w1);
}
pdfioArrayAppendArray(w_array, temp_array);
}
}
// Then the dictionary for the CID base font...
pdfioDictSetName(type2, "Type", "Font");
pdfioDictSetName(type2, "Subtype", "CIDFontType2");
pdfioDictSetName(type2, "BaseFont", basefont);
pdfioDictSetDict(type2, "CIDSystemInfo", sidict);
pdfioDictSetObj(type2, "CIDToGIDMap", cid2gid_obj);
pdfioDictSetObj(type2, "FontDescriptor", desc_obj);
pdfioDictSetArray(type2, "W", w_array);
if ((type2_obj = pdfioFileCreateObj(pdf, type2)) == NULL)
goto done;
pdfioObjClose(type2_obj);
// Create a Type 0 font object...
if ((descendants = pdfioArrayCreate(pdf)) == NULL)
goto done;
pdfioArrayAppendObj(descendants, type2_obj);
if ((dict = pdfioDictCreate(pdf)) == NULL)
goto done;
pdfioDictSetName(dict, "Type", "Font");
pdfioDictSetName(dict, "Subtype", "Type0");
pdfioDictSetName(dict, "BaseFont", basefont);
pdfioDictSetArray(dict, "DescendantFonts", descendants);
pdfioDictSetName(dict, "Encoding", "Identity-H");
pdfioDictSetObj(dict, "ToUnicode", to_unicode_obj);
if ((obj = pdfioFileCreateObj(pdf, dict)) != NULL)
pdfioObjClose(obj);
}
else
{
// Simple (CP1282 or custom encoding) 8-bit font...
int ch; // Character
pdfio_array_t *w_array; // Widths array
if (ttfGetMaxChar(font) >= 255 && !pdf->cp1252_obj && !create_cp1252(pdf))
goto done;
// Create a TrueType font object...
if ((dict = pdfioDictCreate(pdf)) == NULL)
goto done;
pdfioDictSetName(dict, "Type", "Font");
pdfioDictSetName(dict, "Subtype", "TrueType");
pdfioDictSetName(dict, "BaseFont", basefont);
pdfioDictSetNumber(dict, "FirstChar", 32);
if (ttfGetMaxChar(font) >= 255)
{
pdfioDictSetObj(dict, "Encoding", pdf->cp1252_obj);
pdfioDictSetNumber(dict, "LastChar", 255);
}
else
{
pdfioDictSetNumber(dict, "LastChar", ttfGetMaxChar(font));
}
// Build a Widths array for CP1252/WinAnsiEncoding
if ((w_array = pdfioArrayCreate(pdf)) == NULL)
goto done;
for (ch = 32; ch < 256 && ch < ttfGetMaxChar(font); ch ++)
{
if (ch >= 0x80 && ch < 0xa0)
pdfioArrayAppendNumber(w_array, ttfGetWidth(font, _pdfio_cp1252[ch - 0x80]));
else
pdfioArrayAppendNumber(w_array, ttfGetWidth(font, ch));
}
pdfioDictSetArray(dict, "Widths", w_array);
pdfioDictSetObj(dict, "FontDescriptor", desc_obj);
if ((obj = pdfioFileCreateObj(pdf, dict)) != NULL)
pdfioObjClose(obj);
}
done:
if (fd >= 0) if (fd >= 0)
close(fd); close(fd);
_pdfioObjSetExtension(obj, font, (_pdfio_extfree_t)ttfDelete); if (st)
pdfioStreamClose(st);
else
pdfioObjClose(file_obj);
return (obj); ttfDelete(font);
return (NULL);
} }
@ -2233,7 +1993,7 @@ pdfioFileCreateImageObjFromFile(
// 'pdfioImageGetBytesPerLine()' - Get the number of bytes to read for each line. // 'pdfioImageGetBytesPerLine()' - Get the number of bytes to read for each line.
// //
size_t // O - Number of bytes per line size_t // O - Number of bytes per line or `0` on error
pdfioImageGetBytesPerLine( pdfioImageGetBytesPerLine(
pdfio_obj_t *obj) // I - Image object pdfio_obj_t *obj) // I - Image object
{ {
@ -2279,8 +2039,26 @@ pdfioImageGetBytesPerLine(
colors = 1; colors = 1;
} }
if (width < 0)
{
_pdfioFileError(obj->pdf, "Invalid image width %d.", width);
return (0);
}
else if (bpc != 1 && bpc != 2 && bpc != 4 && bpc != 8 && bpc != 16)
{
_pdfioFileError(obj->pdf, "Invalid image bits per component %d.", bpc);
return (0);
}
else if (colors < 1 || colors > 4)
{
_pdfioFileError(obj->pdf, "Invalid image number of colors %d.", colors);
return (0);
}
else
{
return ((size_t)((width * colors * bpc + 7) / 8)); return ((size_t)((width * colors * bpc + 7) / 8));
} }
}
// //
@ -3289,6 +3067,351 @@ create_cp1252(pdfio_file_t *pdf) // I - PDF file
} }
//
// 'create_font()' - Create the object for a TrueType font.
//
static pdfio_obj_t * // O - Font object
create_font(pdfio_obj_t *file_obj, // I - Font file object
ttf_t *font, // I - TrueType font
bool unicode) // I - Force Unicode
{
ttf_rect_t bounds; // Font bounds
pdfio_dict_t *dict, // Font dictionary
*desc; // Font descriptor
pdfio_obj_t *obj = NULL, // Font object
*desc_obj; // Font descriptor object
const char *basefont; // Base font name
pdfio_array_t *bbox; // Font bounding box array
pdfio_stream_t *st; // Font stream
// Create the font descriptor dictionary and object...
if ((bbox = pdfioArrayCreate(file_obj->pdf)) == NULL)
goto done;
ttfGetBounds(font, &bounds);
pdfioArrayAppendNumber(bbox, bounds.left);
pdfioArrayAppendNumber(bbox, bounds.bottom);
pdfioArrayAppendNumber(bbox, bounds.right);
pdfioArrayAppendNumber(bbox, bounds.top);
if ((desc = pdfioDictCreate(file_obj->pdf)) == NULL)
goto done;
if ((basefont = pdfioStringCreate(file_obj->pdf, ttfGetPostScriptName(font))) == NULL)
goto done;
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 generic value from 50 to 250 based on the weight...
pdfioDictSetNumber(desc, "StemV", ttfGetWeight(font) / 4 + 25);
if ((desc_obj = pdfioFileCreateObj(file_obj->pdf, desc)) == NULL)
goto done;
pdfioObjClose(desc_obj);
if (unicode)
{
// Unicode (CID) font...
pdfio_dict_t *cid2gid, // CIDToGIDMap dictionary
*to_unicode; // ToUnicode dictionary
pdfio_obj_t *cid2gid_obj, // CIDToGIDMap object
*to_unicode_obj;// ToUnicode object
size_t i, j, // Looping vars
num_cmap; // Number of CMap entries
const int *cmap; // CMap entries
int min_glyph, // First glyph
max_glyph; // Last glyph
unsigned short glyphs[65536]; // Glyph to Unicode mapping
unsigned char buffer[16384], // Read buffer
*bufptr, // Pointer into buffer
*bufend; // End of buffer
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
pdfio_array_t *w_array, // Width array
*temp_array; // Temporary width sub-array
int w0, w1; // Widths
// Create a CIDSystemInfo mapping to Adobe UCS2 v0 (Unicode)
if ((sidict = pdfioDictCreate(file_obj->pdf)) == NULL)
goto done;
pdfioDictSetString(sidict, "Registry", "Adobe");
pdfioDictSetString(sidict, "Ordering", "Identity");
pdfioDictSetNumber(sidict, "Supplement", 0);
// Create a CIDToGIDMap object for the Unicode font...
if ((cid2gid = pdfioDictCreate(file_obj->pdf)) == NULL)
goto done;
#ifndef DEBUG
pdfioDictSetName(cid2gid, "Filter", "FlateDecode");
#endif // !DEBUG
if ((cid2gid_obj = pdfioFileCreateObj(file_obj->pdf, cid2gid)) == NULL)
goto done;
#ifdef DEBUG
if ((st = pdfioObjCreateStream(cid2gid_obj, PDFIO_FILTER_NONE)) == NULL)
#else
if ((st = pdfioObjCreateStream(cid2gid_obj, PDFIO_FILTER_FLATE)) == NULL)
#endif // DEBUG
goto done;
cmap = ttfGetCMap(font, &num_cmap);
min_glyph = 65536;
max_glyph = 0;
memset(glyphs, 0, sizeof(glyphs));
PDFIO_DEBUG("create_font: num_cmap=%u\n", (unsigned)num_cmap);
for (i = 0, bufptr = buffer, bufend = buffer + sizeof(buffer); i < num_cmap; i ++)
{
PDFIO_DEBUG("create_font: cmap[%u]=%d\n", (unsigned)i, cmap[i]);
if (cmap[i] < 0 || cmap[i] >= (int)(sizeof(glyphs) / sizeof(glyphs[0])))
{
// Map undefined glyph to .notdef...
*bufptr++ = 0;
*bufptr++ = 0;
}
else
{
// Map to specified glyph...
*bufptr++ = (unsigned char)(cmap[i] >> 8);
*bufptr++ = (unsigned char)(cmap[i] & 255);
glyphs[cmap[i]] = (unsigned short)i;
if (cmap[i] < min_glyph)
min_glyph = cmap[i];
if (cmap[i] > max_glyph)
max_glyph = cmap[i];
}
if (bufptr >= bufend)
{
// Flush buffer...
if (!pdfioStreamWrite(st, buffer, (size_t)(bufptr - buffer)))
{
pdfioStreamClose(st);
goto done;
}
bufptr = buffer;
}
}
if (bufptr > buffer)
{
// Flush buffer...
if (!pdfioStreamWrite(st, buffer, (size_t)(bufptr - buffer)))
{
pdfioStreamClose(st);
goto done;
}
}
pdfioStreamClose(st);
// ToUnicode mapping object
to_unicode = pdfioDictCreate(file_obj->pdf);
pdfioDictSetName(to_unicode, "Type", "CMap");
pdfioDictSetName(to_unicode, "CMapName", "Adobe-Identity-UCS2");
pdfioDictSetDict(to_unicode, "CIDSystemInfo", sidict);
#ifndef DEBUG
pdfioDictSetName(to_unicode, "Filter", "FlateDecode");
#endif // !DEBUG
if ((to_unicode_obj = pdfioFileCreateObj(file_obj->pdf, to_unicode)) == NULL)
goto done;
#ifdef DEBUG
if ((st = pdfioObjCreateStream(to_unicode_obj, PDFIO_FILTER_NONE)) == NULL)
#else
if ((st = pdfioObjCreateStream(to_unicode_obj, PDFIO_FILTER_FLATE)) == NULL)
#endif // DEBUG
goto done;
pdfioStreamPuts(st,
"stream\n"
"/CIDInit /ProcSet findresource begin\n"
"12 dict begin\n"
"begincmap\n"
"/CIDSystemInfo<<\n"
"/Registry (Adobe)\n"
"/Ordering (UCS2)\n"
"/Supplement 0\n"
">> def\n"
"/CMapName /Adobe-Identity-UCS2 def\n"
"/CMapType 2 def\n"
"1 begincodespacerange\n"
"<0000> <FFFF>\n"
"endcodespacerange\n"
"endcmap\n"
"CMapName currentdict /CMap defineresource pop\n"
"end\n"
"end\n");
pdfioStreamClose(st);
// Create a CIDFontType2 dictionary for the Unicode font...
if ((type2 = pdfioDictCreate(file_obj->pdf)) == NULL)
goto done;
// Width array
if ((w_array = pdfioArrayCreate(file_obj->pdf)) == NULL)
goto done;
for (i = 0, w0 = ttfGetWidth(font, 0), w1 = 0; i < 65536; w0 = w1)
{
for (j = 1; (i + j) < 65536; j ++)
{
if ((w1 = ttfGetWidth(font, (int)(i + j))) != w0)
break;
}
if (j >= 4)
{
// Encode a long sequence of zeros...
// Encode a repeating sequence...
pdfioArrayAppendNumber(w_array, (double)i);
pdfioArrayAppendNumber(w_array, (double)(i + j - 1));
pdfioArrayAppendNumber(w_array, w0);
i += j;
}
else
{
// Encode a non-repeating sequence...
pdfioArrayAppendNumber(w_array, (double)i);
if ((temp_array = pdfioArrayCreate(file_obj->pdf)) == NULL)
goto done;
pdfioArrayAppendNumber(temp_array, w0);
for (i ++; i < 65536 && pdfioArrayGetSize(temp_array) < 8191; i ++, w0 = w1)
{
if ((w1 = ttfGetWidth(font, (int)i)) == w0 && i < 65530)
{
for (j = 1; j < 4; j ++)
{
if (ttfGetWidth(font, (int)(i + j)) != w0)
break;
}
if (j >= 4)
break;
}
pdfioArrayAppendNumber(temp_array, w1);
}
pdfioArrayAppendArray(w_array, temp_array);
}
}
// Then the dictionary for the CID base font...
pdfioDictSetName(type2, "Type", "Font");
pdfioDictSetName(type2, "Subtype", "CIDFontType2");
pdfioDictSetName(type2, "BaseFont", basefont);
pdfioDictSetDict(type2, "CIDSystemInfo", sidict);
pdfioDictSetObj(type2, "CIDToGIDMap", cid2gid_obj);
pdfioDictSetObj(type2, "FontDescriptor", desc_obj);
pdfioDictSetArray(type2, "W", w_array);
if ((type2_obj = pdfioFileCreateObj(file_obj->pdf, type2)) == NULL)
goto done;
pdfioObjClose(type2_obj);
// Create a Type 0 font object...
if ((descendants = pdfioArrayCreate(file_obj->pdf)) == NULL)
goto done;
pdfioArrayAppendObj(descendants, type2_obj);
if ((dict = pdfioDictCreate(file_obj->pdf)) == NULL)
goto done;
pdfioDictSetName(dict, "Type", "Font");
pdfioDictSetName(dict, "Subtype", "Type0");
pdfioDictSetName(dict, "BaseFont", basefont);
pdfioDictSetArray(dict, "DescendantFonts", descendants);
pdfioDictSetName(dict, "Encoding", "Identity-H");
pdfioDictSetObj(dict, "ToUnicode", to_unicode_obj);
if ((obj = pdfioFileCreateObj(file_obj->pdf, dict)) != NULL)
pdfioObjClose(obj);
}
else
{
// Simple (CP1282 or custom encoding) 8-bit font...
int ch; // Character
pdfio_array_t *w_array; // Widths array
if (ttfGetMaxChar(font) >= 255 && !file_obj->pdf->cp1252_obj && !create_cp1252(file_obj->pdf))
goto done;
// Create a TrueType font object...
if ((dict = pdfioDictCreate(file_obj->pdf)) == NULL)
goto done;
pdfioDictSetName(dict, "Type", "Font");
pdfioDictSetName(dict, "Subtype", "TrueType");
pdfioDictSetName(dict, "BaseFont", basefont);
pdfioDictSetNumber(dict, "FirstChar", 32);
if (ttfGetMaxChar(font) >= 255)
{
pdfioDictSetObj(dict, "Encoding", file_obj->pdf->cp1252_obj);
pdfioDictSetNumber(dict, "LastChar", 255);
}
else
{
pdfioDictSetNumber(dict, "LastChar", ttfGetMaxChar(font));
}
// Build a Widths array for CP1252/WinAnsiEncoding
if ((w_array = pdfioArrayCreate(file_obj->pdf)) == NULL)
goto done;
for (ch = 32; ch < 256 && ch < ttfGetMaxChar(font); ch ++)
{
if (ch >= 0x80 && ch < 0xa0)
pdfioArrayAppendNumber(w_array, ttfGetWidth(font, _pdfio_cp1252[ch - 0x80]));
else
pdfioArrayAppendNumber(w_array, ttfGetWidth(font, ch));
}
pdfioDictSetArray(dict, "Widths", w_array);
pdfioDictSetObj(dict, "FontDescriptor", desc_obj);
if ((obj = pdfioFileCreateObj(file_obj->pdf, dict)) != NULL)
pdfioObjClose(obj);
}
done:
_pdfioObjSetExtension(obj, font, (_pdfio_extfree_t)ttfDelete);
return (obj);
}
// //
// 'create_image()' - Create an image object from some data. // 'create_image()' - Create an image object from some data.
// //

View File

@ -128,6 +128,7 @@ extern bool pdfioContentTextShowJustified(pdfio_stream_t *st, bool unicode, siz
// Resource helpers... // Resource helpers...
extern pdfio_obj_t *pdfioFileCreateFontObjFromBase(pdfio_file_t *pdf, const char *name) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateFontObjFromBase(pdfio_file_t *pdf, const char *name) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateFontObjFromData(pdfio_file_t *pdf, const void *data, size_t datasize, bool unicode) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateFontObjFromFile(pdfio_file_t *pdf, const char *filename, bool unicode) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateFontObjFromFile(pdfio_file_t *pdf, const char *filename, bool unicode) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateICCObjFromData(pdfio_file_t *pdf, const unsigned char *data, size_t datalen, size_t num_colors) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateICCObjFromData(pdfio_file_t *pdf, const unsigned char *data, size_t datalen, size_t num_colors) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateICCObjFromFile(pdfio_file_t *pdf, const char *filename, size_t num_colors) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateICCObjFromFile(pdfio_file_t *pdf, const char *filename, size_t num_colors) _PDFIO_PUBLIC;

View File

@ -23,9 +23,9 @@ extern "C" {
// Version numbers... // Version numbers...
// //
# define PDFIO_VERSION "1.5.2" # define PDFIO_VERSION "1.6.0"
# define PDFIO_VERSION_MAJOR 1 # define PDFIO_VERSION_MAJOR 1
# define PDFIO_VERSION_MINOR 5 # define PDFIO_VERSION_MINOR 6
// //

View File

@ -3,7 +3,7 @@
<metadata> <metadata>
<id>pdfio_native</id> <id>pdfio_native</id>
<title>PDFio Library for VS2019+</title> <title>PDFio Library for VS2019+</title>
<version>1.5.1</version> <version>1.6.0</version>
<authors>Michael R Sweet</authors> <authors>Michael R Sweet</authors>
<owners>michaelrsweet</owners> <owners>michaelrsweet</owners>
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl> <projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
@ -16,7 +16,7 @@
<copyright>Copyright © 2019-2025 by Michael R Sweet</copyright> <copyright>Copyright © 2019-2025 by Michael R Sweet</copyright>
<tags>pdf file native</tags> <tags>pdf file native</tags>
<dependencies> <dependencies>
<dependency id="pdfio_native.redist" version="1.5.1" /> <dependency id="pdfio_native.redist" version="1.6.0" />
<dependency id="libpng_native.redist" version="1.6.30" /> <dependency id="libpng_native.redist" version="1.6.30" />
<dependency id="zlib_native.redist" version="1.2.11" /> <dependency id="zlib_native.redist" version="1.2.11" />
</dependencies> </dependencies>

View File

@ -3,7 +3,7 @@
<metadata> <metadata>
<id>pdfio_native.redist</id> <id>pdfio_native.redist</id>
<title>PDFio Library for VS2019+</title> <title>PDFio Library for VS2019+</title>
<version>1.5.1</version> <version>1.6.0</version>
<authors>Michael R Sweet</authors> <authors>Michael R Sweet</authors>
<owners>michaelrsweet</owners> <owners>michaelrsweet</owners>
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl> <projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>

View File

@ -3,7 +3,7 @@
// //
// https://github.com/michaelrsweet/ttf // https://github.com/michaelrsweet/ttf
// //
// Copyright © 2018-2024 by Michael R Sweet. // Copyright © 2018-2025 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -14,6 +14,10 @@
// //
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include "ttf.h" #include "ttf.h"
@ -81,9 +85,13 @@ test_font(const char *filename) // I - Font filename
int i, // Looping var int i, // Looping var
errors = 0; // Number of errors errors = 0; // Number of errors
ttf_t *font; // Font ttf_t *font; // Font
struct stat fileinfo; // Font file information
FILE *fp = NULL; // File pointer
void *data = NULL; // Memory buffer for font file
const char *value; // Font (string) value const char *value; // Font (string) value
int intvalue; // Font (integer) value int intvalue; // Font (integer) value
float realvalue; // Font (real) value float realvalue; // Font (real) value
char psname[1024]; // Postscript font name
ttf_rect_t bounds; // Bounds ttf_rect_t bounds; // Bounds
ttf_rect_t extents; // Extents ttf_rect_t extents; // Extents
size_t num_fonts; // Number of fonts size_t num_fonts; // Number of fonts
@ -220,6 +228,9 @@ test_font(const char *filename) // I - Font filename
if ((value = ttfGetPostScriptName(font)) != NULL) if ((value = ttfGetPostScriptName(font)) != NULL)
{ {
printf("PASS (%s)\n", value); printf("PASS (%s)\n", value);
strncpy(psname, value, sizeof(psname) - 1);
psname[sizeof(psname) - 1] = '\0';
} }
else else
{ {
@ -300,6 +311,86 @@ test_font(const char *filename) // I - Font filename
puts("PASS (false)"); puts("PASS (false)");
ttfDelete(font); ttfDelete(font);
font = NULL;
// Now copy the font to memory and open it that way...
printf("fopen(\"%s\", \"rb\"): ", filename);
if ((fp = fopen(filename, "rb")) == NULL)
{
printf("FAIL (%s)\n", strerror(errno));
errors ++;
}
else
{
printf("PASS (%d)\n", fileno(fp));
printf("fstat(%d): ", fileno(fp));
if (fstat(fileno(fp), &fileinfo))
{
printf("FAIL (%s)\n", strerror(errno));
errors ++;
}
else
{
printf("PASS (%lu bytes)\n", (unsigned long)fileinfo.st_size);
fputs("malloc(): ", stdout);
if ((data = malloc((size_t)fileinfo.st_size)) == NULL)
{
printf("FAIL (%s)\n", strerror(errno));
errors ++;
}
else
{
puts("PASS");
fputs("fread(): ", stdout);
if (fread(data, (size_t)fileinfo.st_size, 1, fp) != 1)
{
printf("FAIL (%s)\n", strerror(errno));
errors ++;
}
else
{
puts("PASS");
fputs("ttfCreateData(): ", stdout);
if ((font = ttfCreateData(data, (size_t)fileinfo.st_size, /*idx*/0, error_cb, /*err_data*/NULL)) == NULL)
{
puts("FAIL");
errors ++;
}
else
{
puts("PASS");
fputs("ttfGetPostScriptName: ", stdout);
if ((value = ttfGetPostScriptName(font)) != NULL)
{
if (!strcmp(value, psname))
{
printf("PASS (%s)\n", value);
}
else
{
printf("FAIL (got \"%s\", expected \"%s\")\n", value, psname);
errors ++;
}
}
else
{
puts("FAIL");
errors ++;
}
}
}
}
}
if (fp)
fclose(fp);
free(data);
ttfDelete(font);
}
return (errors); return (errors);
} }

564
ttf.c
View File

@ -177,9 +177,19 @@ typedef struct _ttf_off_names_s // OFF/TTF naming table
unsigned storage_size; // Size of storage area unsigned storage_size; // Size of storage area
} _ttf_off_names_t; } _ttf_off_names_t;
typedef size_t (*_ttf_read_cb_t)(ttf_t *font, void *buffer, size_t bytes);
// Font read callback, returns number of bytes read
typedef bool (*_ttf_seek_cb_t)(ttf_t *font, size_t offset);
// Font seek callback, returns `true` on success
struct _ttf_s struct _ttf_s
{ {
int fd; // File descriptor _ttf_read_cb_t read_cb; // Read callback
_ttf_seek_cb_t seek_cb; // Seek callback
int file_fd; // File descriptor for ttfCreate
const char *data; // Font data for ttfCreateData
size_t data_size; // Size of font data for ttfCreateData
size_t data_offset; // Offset within input
size_t idx; // Font number in file size_t idx; // Font number in file
ttf_err_cb_t err_cb; // Error callback, if any ttf_err_cb_t err_cb; // Error callback, if any
void *err_data; // Error callback data void *err_data; // Error callback data
@ -282,7 +292,12 @@ typedef struct _ttf_off_post_s // PostScript information
// //
static char *copy_name(ttf_t *font, unsigned name_id); static char *copy_name(ttf_t *font, unsigned name_id);
static ttf_t *create_font(const char *filename, const void *data, size_t datasize, size_t idx, ttf_err_cb_t err_cb, void *err_data);
static void errorf(ttf_t *font, const char *message, ...) TTF_FORMAT_ARGS(2,3); static void errorf(ttf_t *font, const char *message, ...) TTF_FORMAT_ARGS(2,3);
static size_t fd_read_cb(ttf_t *font, void *buffer, size_t bytes);
static bool fd_seek_cb(ttf_t *font, size_t offset);
static size_t mem_read_cb(ttf_t *font, void *buffer, size_t bytes);
static bool mem_seek_cb(ttf_t *font, size_t offset);
static bool read_cmap(ttf_t *font); static bool read_cmap(ttf_t *font);
static bool read_head(ttf_t *font, _ttf_off_head_t *head); static bool read_head(ttf_t *font, _ttf_off_head_t *head);
static bool read_hhea(ttf_t *font, _ttf_off_hhea_t *hhea); static bool read_hhea(ttf_t *font, _ttf_off_hhea_t *hhea);
@ -329,15 +344,6 @@ ttfCreate(const char *filename, // I - Filename
ttf_err_cb_t err_cb, // I - Error callback or `NULL` to log to stderr ttf_err_cb_t err_cb, // I - Error callback or `NULL` to log to stderr
void *err_data) // I - Error callback data void *err_data) // I - Error callback data
{ {
ttf_t *font = NULL; // New font object
size_t i; // Looping var
_ttf_metric_t *widths = NULL; // Glyph metrics
_ttf_off_head_t head; // head table
_ttf_off_hhea_t hhea; // hhea table
_ttf_off_os_2_t os_2; // OS/2 table
_ttf_off_post_t post; // PostScript table
TTF_DEBUG("ttfCreate(filename=\"%s\", idx=%u, err_cb=%p, err_data=%p)\n", filename, (unsigned)idx, err_cb, err_data); TTF_DEBUG("ttfCreate(filename=\"%s\", idx=%u, err_cb=%p, err_data=%p)\n", filename, (unsigned)idx, err_cb, err_data);
// Range check input.. // Range check input..
@ -347,178 +353,57 @@ ttfCreate(const char *filename, // I - Filename
return (NULL); return (NULL);
} }
// Allocate memory... // Open and return the font...
if ((font = (ttf_t *)calloc(1, sizeof(ttf_t))) == NULL) return (create_font(filename, /*data*/NULL, /*datasize*/0, idx, err_cb, err_data));
}
//
// 'ttfCreateData()' - Create a new font object from a memory buffer.
//
// This function creates a new font object from a memory buffer. The "data"
// argument specifies a pointer to the first byte of data and the "datasize"
// argument specifies the length of the memory buffer in bytes.
//
// > Note: The caller is responsible for ensuring that the memory buffer is
// > available until the font object is deleted with @link ttfDelete@.
//
// The "idx" argument specifies the font to load from a collection - the first
// font is number `0`. Once created, you can call the @link ttfGetNumFonts@
// function to determine whether the loaded font file is a collection with more
// than one font.
//
// The "err_cb" and "err_data" arguments specify a callback function and data
// pointer for receiving error messages. If `NULL`, errors are sent to the
// `stderr` file. The callback function receives the data pointer and a text
// message string, for example:
//
// ```
// void my_err_cb(void *err_data, const char *message)
// {
// fprintf(stderr, "ERROR: %s\n", message);
// }
// ```
//
ttf_t * // O - New font object
ttfCreateData(const void *data, // I - Buffer
size_t datasize, // I - Size of buffer in bytes
size_t idx, // I - Font number to create in collection (0-based)
ttf_err_cb_t err_cb, // I - Error callback or `NULL` to log to stderr
void *err_data) // I - Error callback data
{
TTF_DEBUG("ttfCreateData(data=%p, datasize=%lu, idx=%u, err_cb=%p, err_data=%p)\n", data, (unsigned long)datasize, (unsigned)idx, err_cb, err_data);
// Range check input..
if (!data || datasize == 0)
{
errno = EINVAL;
return (NULL); return (NULL);
font->idx = idx;
font->err_cb = err_cb;
font->err_data = err_data;
// Open the font file...
if ((font->fd = open(filename, O_RDONLY | O_BINARY)) < 0)
{
errorf(font, "Unable to open '%s': %s", filename, strerror(errno));
goto error;
} }
TTF_DEBUG("ttfCreate: fd=%d\n", font->fd); // Open and return the font...
return (create_font(/*filename*/NULL, data, datasize, idx, err_cb, err_data));
// Read the table of contents and the identifying names...
if (!read_table(font))
goto error;
TTF_DEBUG("ttfCreate: num_entries=%d\n", font->table.num_entries);
if (!read_names(font))
goto error;
TTF_DEBUG("ttfCreate: num_names=%d\n", font->names.num_names);
// Copy key font meta data strings...
font->copyright = copy_name(font, TTF_OFF_Copyright);
font->family = copy_name(font, TTF_OFF_FontFamily);
font->postscript_name = copy_name(font, TTF_OFF_PostScriptName);
font->version = copy_name(font, TTF_OFF_FontVersion);
if (read_post(font, &post))
{
font->italic_angle = post.italicAngle;
font->is_fixed = post.isFixedPitch != 0;
}
TTF_DEBUG("ttfCreate: copyright=\"%s\"\n", font->copyright);
TTF_DEBUG("ttfCreate: family=\"%s\"\n", font->family);
TTF_DEBUG("ttfCreate: postscript_name=\"%s\"\n", font->postscript_name);
TTF_DEBUG("ttfCreate: version=\"%s\"\n", font->version);
TTF_DEBUG("ttfCreate: italic_angle=%g\n", font->italic_angle);
TTF_DEBUG("ttfCreate: is_fixed=%s\n", font->is_fixed ? "true" : "false");
if (!read_cmap(font))
goto error;
if (!read_head(font, &head))
goto error;
font->units = (float)head.unitsPerEm;
font->x_max = head.xMax;
font->x_min = head.xMin;
font->y_max = head.yMax;
font->y_min = head.yMin;
if (head.macStyle & TTF_OFF_macStyle_Italic)
{
if (font->postscript_name && strstr(font->postscript_name, "Oblique"))
font->style = TTF_STYLE_OBLIQUE;
else
font->style = TTF_STYLE_ITALIC;
}
else
font->style = TTF_STYLE_NORMAL;
if (!read_hhea(font, &hhea))
goto error;
font->ascent = hhea.ascender;
font->descent = hhea.descender;
if (read_maxp(font) < 0)
goto error;
if (hhea.numberOfHMetrics > 0)
{
if ((widths = read_hmtx(font, &hhea)) == NULL)
goto error;
}
else
{
errorf(font, "Number of horizontal metrics is 0.");
goto error;
}
if (read_os_2(font, &os_2))
{
// Copy key values from OS/2 table...
static const ttf_stretch_t stretches[] =
{
TTF_STRETCH_ULTRA_CONDENSED, // ultra-condensed
TTF_STRETCH_EXTRA_CONDENSED, // extra-condensed
TTF_STRETCH_CONDENSED, // condensed
TTF_STRETCH_SEMI_CONDENSED, // semi-condensed
TTF_STRETCH_NORMAL, // normal
TTF_STRETCH_SEMI_EXPANDED, // semi-expanded
TTF_STRETCH_EXPANDED, // expanded
TTF_STRETCH_EXTRA_EXPANDED, // extra-expanded
TTF_STRETCH_ULTRA_EXPANDED // ultra-expanded
};
if (os_2.usWidthClass >= 1 && os_2.usWidthClass <= (int)(sizeof(stretches) / sizeof(stretches[0])))
font->stretch = stretches[os_2.usWidthClass - 1];
font->weight = (short)os_2.usWeightClass;
font->cap_height = os_2.sCapHeight;
font->x_height = os_2.sxHeight;
}
else
{
// Default key values since there isn't an OS/2 table...
TTF_DEBUG("ttfCreate: Unable to read OS/2 table.\n");
font->weight = 400;
}
if (font->cap_height == 0)
font->cap_height = font->ascent;
if (font->x_height == 0)
font->x_height = 3 * font->ascent / 5;
// Build a sparse glyph widths table...
font->min_char = -1;
for (i = 0; i < font->num_cmap; i ++)
{
if (font->cmap[i] >= 0)
{
int bin = (int)i / 256, // Sub-array bin
glyph = font->cmap[i]; // Glyph index
// Update min/max...
if (font->min_char < 0)
font->min_char = (int)i;
font->max_char = (int)i;
// Allocate a sub-array as needed...
if (!font->widths[bin])
font->widths[bin] = (_ttf_metric_t *)calloc(256, sizeof(_ttf_metric_t));
// Copy the width of the specified glyph or the last one if we are past
// the end of the table...
if (glyph >= hhea.numberOfHMetrics)
font->widths[bin][i & 255] = widths[hhea.numberOfHMetrics - 1];
else
font->widths[bin][i & 255] = widths[glyph];
}
#ifdef DEBUG
if (i >= ' ' && i < 127 && font->widths[0])
TTF_DEBUG("ttfCreate: width['%c']=%d(%d)\n", (char)i, font->widths[0][i].width, font->widths[0][i].left_bearing);
#endif // DEBUG
}
// Cleanup and return the font...
free(widths);
return (font);
// If we get here something bad happened...
error:
free(widths);
ttfDelete(font);
return (NULL);
} }
@ -537,8 +422,8 @@ ttfDelete(ttf_t *font) // I - Font
return; return;
// Close the font file... // Close the font file...
if (font->fd >= 0) if (font->file_fd >= 0)
close(font->fd); close(font->file_fd);
// Free all memory used... // Free all memory used...
free(font->copyright); free(font->copyright);
@ -1043,6 +928,219 @@ copy_name(ttf_t *font, // I - Font
} }
//
// 'create_font()' - Create a font object from the file or data.
//
static ttf_t *
create_font(const char *filename, // I - Filename of `NULL`
const void *data, // I - Data pointer or `NULL`
size_t datasize, // I - Size of data or 0
size_t idx, // I - Font index
ttf_err_cb_t err_cb, // I - Error callback function
void *err_data) // I - Error callback data
{
ttf_t *font = NULL; // New font object
size_t i; // Looping var
_ttf_metric_t *widths = NULL; // Glyph metrics
_ttf_off_head_t head; // head table
_ttf_off_hhea_t hhea; // hhea table
_ttf_off_os_2_t os_2; // OS/2 table
_ttf_off_post_t post; // PostScript table
TTF_DEBUG("create_font(filename=\"%s\", data=%p, datasize=%lu, idx=%u, err_cb=%p, err_data=%p)\n", filename, data, (unsigned long)datasize, (unsigned)idx, err_cb, err_data);
// Allocate memory...
if ((font = (ttf_t *)calloc(1, sizeof(ttf_t))) == NULL)
return (NULL);
font->idx = idx;
font->err_cb = err_cb;
font->err_data = err_data;
if (filename)
{
// Open the font file...
if ((font->file_fd = open(filename, O_RDONLY | O_BINARY)) < 0)
{
errorf(font, "Unable to open '%s': %s", filename, strerror(errno));
goto error;
}
TTF_DEBUG("create_font: file_fd=%d\n", font->file_fd);
font->read_cb = fd_read_cb;
font->seek_cb = fd_seek_cb;
}
else
{
// Read from memory...
font->file_fd = -1;
font->data = (const char *)data;
font->data_size = datasize;
font->read_cb = mem_read_cb;
font->seek_cb = mem_seek_cb;
}
// Read the table of contents and the identifying names...
if (!read_table(font))
goto error;
TTF_DEBUG("create_font: num_entries=%d\n", font->table.num_entries);
if (!read_names(font))
goto error;
TTF_DEBUG("create_font: num_names=%d\n", font->names.num_names);
// Copy key font meta data strings...
font->copyright = copy_name(font, TTF_OFF_Copyright);
font->family = copy_name(font, TTF_OFF_FontFamily);
font->postscript_name = copy_name(font, TTF_OFF_PostScriptName);
font->version = copy_name(font, TTF_OFF_FontVersion);
if (read_post(font, &post))
{
font->italic_angle = post.italicAngle;
font->is_fixed = post.isFixedPitch != 0;
}
TTF_DEBUG("create_font: copyright=\"%s\"\n", font->copyright);
TTF_DEBUG("create_font: family=\"%s\"\n", font->family);
TTF_DEBUG("create_font: postscript_name=\"%s\"\n", font->postscript_name);
TTF_DEBUG("create_font: version=\"%s\"\n", font->version);
TTF_DEBUG("create_font: italic_angle=%g\n", font->italic_angle);
TTF_DEBUG("create_font: is_fixed=%s\n", font->is_fixed ? "true" : "false");
if (!read_cmap(font))
goto error;
if (!read_head(font, &head))
goto error;
font->units = (float)head.unitsPerEm;
font->x_max = head.xMax;
font->x_min = head.xMin;
font->y_max = head.yMax;
font->y_min = head.yMin;
if (head.macStyle & TTF_OFF_macStyle_Italic)
{
if (font->postscript_name && strstr(font->postscript_name, "Oblique"))
font->style = TTF_STYLE_OBLIQUE;
else
font->style = TTF_STYLE_ITALIC;
}
else
font->style = TTF_STYLE_NORMAL;
if (!read_hhea(font, &hhea))
goto error;
font->ascent = hhea.ascender;
font->descent = hhea.descender;
if (read_maxp(font) < 0)
goto error;
if (hhea.numberOfHMetrics > 0)
{
if ((widths = read_hmtx(font, &hhea)) == NULL)
goto error;
}
else
{
errorf(font, "Number of horizontal metrics is 0.");
goto error;
}
if (read_os_2(font, &os_2))
{
// Copy key values from OS/2 table...
static const ttf_stretch_t stretches[] =
{
TTF_STRETCH_ULTRA_CONDENSED, // ultra-condensed
TTF_STRETCH_EXTRA_CONDENSED, // extra-condensed
TTF_STRETCH_CONDENSED, // condensed
TTF_STRETCH_SEMI_CONDENSED, // semi-condensed
TTF_STRETCH_NORMAL, // normal
TTF_STRETCH_SEMI_EXPANDED, // semi-expanded
TTF_STRETCH_EXPANDED, // expanded
TTF_STRETCH_EXTRA_EXPANDED, // extra-expanded
TTF_STRETCH_ULTRA_EXPANDED // ultra-expanded
};
if (os_2.usWidthClass >= 1 && os_2.usWidthClass <= (int)(sizeof(stretches) / sizeof(stretches[0])))
font->stretch = stretches[os_2.usWidthClass - 1];
font->weight = (short)os_2.usWeightClass;
font->cap_height = os_2.sCapHeight;
font->x_height = os_2.sxHeight;
}
else
{
// Default key values since there isn't an OS/2 table...
TTF_DEBUG("create_font: Unable to read OS/2 table.\n");
font->weight = 400;
}
if (font->cap_height == 0)
font->cap_height = font->ascent;
if (font->x_height == 0)
font->x_height = 3 * font->ascent / 5;
// Build a sparse glyph widths table...
font->min_char = -1;
for (i = 0; i < font->num_cmap; i ++)
{
if (font->cmap[i] >= 0)
{
int bin = (int)i / 256, // Sub-array bin
glyph = font->cmap[i]; // Glyph index
// Update min/max...
if (font->min_char < 0)
font->min_char = (int)i;
font->max_char = (int)i;
// Allocate a sub-array as needed...
if (!font->widths[bin])
font->widths[bin] = (_ttf_metric_t *)calloc(256, sizeof(_ttf_metric_t));
// Copy the width of the specified glyph or the last one if we are past
// the end of the table...
if (glyph >= hhea.numberOfHMetrics)
font->widths[bin][i & 255] = widths[hhea.numberOfHMetrics - 1];
else
font->widths[bin][i & 255] = widths[glyph];
}
#ifdef DEBUG
if (i >= ' ' && i < 127 && font->widths[0])
TTF_DEBUG("create_font: width['%c']=%d(%d)\n", (char)i, font->widths[0][i].width, font->widths[0][i].left_bearing);
#endif // DEBUG
}
// Cleanup and return the font...
free(widths);
return (font);
// If we get here something bad happened...
error:
free(widths);
ttfDelete(font);
return (NULL);
}
// //
// 'errorf()' - Show an error message. // 'errorf()' - Show an error message.
// //
@ -1070,9 +1168,85 @@ errorf(ttf_t *font, // I - Font
} }
/* //
* 'read_cmap()' - Read the cmap table, getting the Unicode mapping table. // 'fd_read_cb()' - Read from a file.
*/ //
static size_t // O - Number of bytes read
fd_read_cb(ttf_t *font, // I - Font
void *buffer, // I - Read buffer
size_t bytes) // I - Number of bytes to read
{
ssize_t rbytes; // Bytes read
if ((rbytes = read(font->file_fd, buffer, bytes)) < 0)
return (0);
return ((size_t)rbytes);
}
//
// 'fd_seek_cb()' - Seek in a file.
//
static bool // O - `true` on success, `false` on failure
fd_seek_cb(ttf_t *font, // I - Font
size_t offset) // I - Offset in data
{
return (lseek(font->file_fd, offset, SEEK_SET) == offset);
}
//
// 'mem_read_cb()' - Read from a memory buffer.
//
static size_t // O - Number of bytes read
mem_read_cb(ttf_t *font, // I - Font
void *buffer, // I - Read buffer
size_t bytes) // I - Number of bytes to read
{
size_t rbytes; // Bytes to copy
if (font->data_offset >= font->data_size)
return (0);
if ((rbytes = font->data_size - font->data_offset) > bytes)
rbytes = bytes;
memcpy(buffer, font->data + font->data_offset, rbytes);
font->data_offset += rbytes;
return (rbytes);
}
//
// 'mem_seek_cb()' - Seek in a memory buffer.
//
static bool // O - `true` on success, `false` on error
mem_seek_cb(ttf_t *font, // I - Font
size_t offset) // I - Offset in data
{
if (offset >= font->data_size)
{
errno = ENXIO;
return (false);
}
font->data_offset = offset;
return (true);
}
//
// 'read_cmap()' - Read the cmap table, getting the Unicode mapping table.
//
static bool // O - `true` on success, `false` on error static bool // O - `true` on success, `false` on error
read_cmap(ttf_t *font) // I - Font read_cmap(ttf_t *font) // I - Font
@ -1219,7 +1393,7 @@ read_cmap(ttf_t *font) // I - Font
return (false); return (false);
} }
if (read(font->fd, bmap, font->num_cmap) != (ssize_t)font->num_cmap) if ((font->read_cb)(font, bmap, font->num_cmap) != font->num_cmap)
{ {
errorf(font, "Unable to read cmap table length at offset %u.", coffset); errorf(font, "Unable to read cmap table length at offset %u.", coffset);
return (false); return (false);
@ -1646,9 +1820,9 @@ read_hhea(ttf_t *font, // I - Font
} }
/* //
* 'read_hmtx()' - Read the horizontal metrics from the font. // 'read_hmtx()' - Read the horizontal metrics from the font.
*/ //
static _ttf_metric_t * // O - Array of glyph metrics static _ttf_metric_t * // O - Array of glyph metrics
read_hmtx(ttf_t *font, // I - Font read_hmtx(ttf_t *font, // I - Font
@ -1774,7 +1948,7 @@ read_names(ttf_t *font) // I - Font
length -= (unsigned)offset; length -= (unsigned)offset;
if (read(font->fd, font->names.storage, length) < 0) if ((font->read_cb)(font, font->names.storage, length) == 0)
{ {
errorf(font, "Unable to read name table: %s", strerror(errno)); errorf(font, "Unable to read name table: %s", strerror(errno));
return (false); return (false);
@ -1823,7 +1997,7 @@ read_os_2(ttf_t *font, // I - Font
/* yStrikeoutOffset */ read_short(font); /* yStrikeoutOffset */ read_short(font);
/* sFamilyClass */ read_short(font); /* sFamilyClass */ read_short(font);
/* panose[10] */ /* panose[10] */
if (read(font->fd, panose, sizeof(panose)) != (ssize_t)sizeof(panose)) if ((font->read_cb)(font, panose, sizeof(panose)) != sizeof(panose))
return (false); return (false);
/* ulUnicodeRange1 */ read_ulong(font); /* ulUnicodeRange1 */ read_ulong(font);
/* ulUnicodeRange2 */ read_ulong(font); /* ulUnicodeRange2 */ read_ulong(font);
@ -1885,7 +2059,7 @@ read_short(ttf_t *font) // I - Font
unsigned char buffer[2]; // Read buffer unsigned char buffer[2]; // Read buffer
if (read(font->fd, buffer, sizeof(buffer)) != sizeof(buffer)) if ((font->read_cb)(font, buffer, sizeof(buffer)) != sizeof(buffer))
return (EOF); return (EOF);
else if (buffer[0] & 0x80) else if (buffer[0] & 0x80)
return (((buffer[0] << 8) | buffer[1]) - 65536); return (((buffer[0] << 8) | buffer[1]) - 65536);
@ -1958,7 +2132,7 @@ read_table(ttf_t *font) // I - Font
TTF_DEBUG("read_table: Offset for font %u is %u.\n", (unsigned)font->idx, temp); TTF_DEBUG("read_table: Offset for font %u is %u.\n", (unsigned)font->idx, temp);
if (lseek(font->fd, temp + 4, SEEK_SET) < 0) if (!(font->seek_cb)(font, temp + 4))
{ {
errorf(font, "Unable to seek to font %u: %s", (unsigned)font->idx, strerror(errno)); errorf(font, "Unable to seek to font %u: %s", (unsigned)font->idx, strerror(errno));
return (false); return (false);
@ -2034,7 +2208,7 @@ read_ulong(ttf_t *font) // I - Font
unsigned char buffer[4]; // Read buffer unsigned char buffer[4]; // Read buffer
if (read(font->fd, buffer, sizeof(buffer)) != sizeof(buffer)) if ((font->read_cb)(font, buffer, sizeof(buffer)) != sizeof(buffer))
return ((unsigned)EOF); return ((unsigned)EOF);
else else
return ((unsigned)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3])); return ((unsigned)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]));
@ -2051,7 +2225,7 @@ read_ushort(ttf_t *font) // I - Font
unsigned char buffer[2]; // Read buffer unsigned char buffer[2]; // Read buffer
if (read(font->fd, buffer, sizeof(buffer)) != sizeof(buffer)) if ((font->read_cb)(font, buffer, sizeof(buffer)) != sizeof(buffer))
return (EOF); return (EOF);
else else
return ((buffer[0] << 8) | buffer[1]); return ((buffer[0] << 8) | buffer[1]);
@ -2078,7 +2252,7 @@ seek_table(ttf_t *font, // I - Font
if (current->tag == tag) if (current->tag == tag)
{ {
// Found it, seek and return... // Found it, seek and return...
if (lseek(font->fd, current->offset + offset, SEEK_SET) == (current->offset + offset)) if ((font->seek_cb)(font, current->offset + offset))
{ {
// Successful seek... // Successful seek...
return (current->length - offset); return (current->length - offset);

3
ttf.h
View File

@ -3,7 +3,7 @@
// //
// https://github.com/michaelrsweet/ttf // https://github.com/michaelrsweet/ttf
// //
// Copyright © 2018-2024 by Michael R Sweet. // Copyright © 2018-2025 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -81,6 +81,7 @@ typedef struct ttf_rect_s // Bounding rectangle
// //
extern ttf_t *ttfCreate(const char *filename, size_t idx, ttf_err_cb_t err_cb, void *err_data); extern ttf_t *ttfCreate(const char *filename, size_t idx, ttf_err_cb_t err_cb, void *err_data);
extern ttf_t *ttfCreateData(const void *data, size_t data_size, size_t idx, ttf_err_cb_t err_cb, void *err_data);
extern void ttfDelete(ttf_t *font); extern void ttfDelete(ttf_t *font);
extern int ttfGetAscent(ttf_t *font); extern int ttfGetAscent(ttf_t *font);
extern ttf_rect_t *ttfGetBounds(ttf_t *font, ttf_rect_t *bounds); extern ttf_rect_t *ttfGetBounds(ttf_t *font, ttf_rect_t *bounds);