Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 60 additions & 49 deletions pdfgen.c
Original file line number Diff line number Diff line change
Expand Up @@ -1158,18 +1158,21 @@ int pdf_set_font(struct pdf_doc *pdf, const char *font)
return 0;
}

int pdf_set_font_ttf(struct pdf_doc *pdf, const char *path)
const char *pdf_set_font_ttf(struct pdf_doc *pdf, const char *path)
{
FILE *fp = fopen(path, "rb");
if (!fp)
return pdf_set_err(pdf, -errno, "Unable to open font file '%s': %s",
path, strerror(errno));
int err = pdf_set_font_ttf_file(pdf, fp, path);
if (!fp) {
pdf_set_err(pdf, -errno, "Unable to open font file '%s': %s", path,
strerror(errno));
return NULL;
}
const char *res = pdf_set_font_ttf_file(pdf, fp, path);
fclose(fp);
return err;
return res;
}

int pdf_set_font_ttf_file(struct pdf_doc *pdf, FILE *fp, const char *path)
const char *pdf_set_font_ttf_file(struct pdf_doc *pdf, FILE *fp,
const char *path)
{
uint8_t *font_data;
size_t font_data_len;
Expand All @@ -1191,34 +1194,39 @@ int pdf_set_font_ttf_file(struct pdf_doc *pdf, FILE *fp, const char *path)
struct stat st;

if (fstat(fileno(fp), &st) < 0) {
return pdf_set_err(pdf, -errno, "Unable to stat font file '%s': %s",
path, strerror(errno));
pdf_set_err(pdf, -errno, "Unable to stat font file '%s': %s", path,
strerror(errno));
return NULL;
}
font_data_len = (size_t)st.st_size;
font_data = (uint8_t *)malloc(font_data_len);
if (!font_data) {
return pdf_set_err(pdf, -ENOMEM,
"Unable to allocate %zu bytes for font '%s'",
font_data_len, path);
pdf_set_err(pdf, -ENOMEM,
"Unable to allocate %zu bytes for font '%s'",
font_data_len, path);
return NULL;
}
if (fread(font_data, 1, font_data_len, fp) != font_data_len) {
free(font_data);
return pdf_set_err(pdf, -EIO, "Unable to read font file '%s'", path);
pdf_set_err(pdf, -EIO, "Unable to read font file '%s'", path);
return NULL;
}

if (font_data_len < 12) {
free(font_data);
return pdf_set_err(
pdf, -EINVAL, "Font file '%s' is too small to be a TrueType font",
path);
pdf_set_err(pdf, -EINVAL,
"Font file '%s' is too small to be a TrueType font",
path);
return NULL;
}
sfVersion = ttf_be32(font_data);
// TrueType fonts have sfVersion 0x00010000 or 'true' (0x74727565)
if (sfVersion != 0x00010000u && sfVersion != 0x74727565u) {
free(font_data);
return pdf_set_err(
pdf, -EINVAL, "File '%s' is not a TrueType font (version 0x%08x)",
path, sfVersion);
pdf_set_err(pdf, -EINVAL,
"File '%s' is not a TrueType font (version 0x%08x)", path,
sfVersion);
return NULL;
}

head = ttf_find_table(font_data, font_data_len, "head", &head_len);
Expand All @@ -1231,9 +1239,9 @@ int pdf_set_font_ttf_file(struct pdf_doc *pdf, FILE *fp, const char *path)

if (!head || head_len < 54 || !hhea || hhea_len < 36 || !hmtx || !cmap) {
free(font_data);
return pdf_set_err(pdf, -EINVAL,
"Font '%s' is missing required TrueType tables",
path);
pdf_set_err(pdf, -EINVAL,
"Font '%s' is missing required TrueType tables", path);
return NULL;
}

// Parse head table
Expand All @@ -1245,8 +1253,8 @@ int pdf_set_font_ttf_file(struct pdf_doc *pdf, FILE *fp, const char *path)
macStyle = ttf_be16(head + 44);
if (units_per_em == 0) {
free(font_data);
return pdf_set_err(pdf, -EINVAL, "Font '%s' has zero units_per_em",
path);
pdf_set_err(pdf, -EINVAL, "Font '%s' has zero units_per_em", path);
return NULL;
}

// Parse hhea table
Expand Down Expand Up @@ -1318,7 +1326,7 @@ int pdf_set_font_ttf_file(struct pdf_doc *pdf, FILE *fp, const char *path)
if (obj->font.is_ttf && strcmp(obj->font.name, font_name) == 0) {
free(font_data);
pdf->current_font = obj;
return 0;
return obj->font.name;
}
last_font_index = obj->font.index;
}
Expand All @@ -1331,40 +1339,42 @@ int pdf_set_font_ttf_file(struct pdf_doc *pdf, FILE *fp, const char *path)
if (!cmap_subtable || cmap_subtable_len == 0 ||
cmap_subtable_len > font_data_len) {
free(font_data);
return pdf_set_err(pdf, -EINVAL,
"Font '%s' has no usable cmap subtable", path);
pdf_set_err(pdf, -EINVAL, "Font '%s' has no usable cmap subtable",
path);
return NULL;
}
uint8_t *cmap_copy = (uint8_t *)malloc(cmap_subtable_len);
if (!cmap_copy) {
free(font_data);
return pdf_set_err(pdf, -ENOMEM,
"Unable to allocate cmap subtable for font '%s'",
path);
pdf_set_err(pdf, -ENOMEM,
"Unable to allocate cmap subtable for font '%s'", path);
return NULL;
}
// ensure subtable fits in cmap bounds safely before blind copy
if (cmap_subtable_len > (cmap + cmap_len) - cmap_subtable) {
free(cmap_copy);
free(font_data);
return pdf_set_err(pdf, -EINVAL,
"Font '%s' cmap subtable length invalid", path);
pdf_set_err(pdf, -EINVAL, "Font '%s' cmap subtable length invalid",
path);
return NULL;
}
memcpy(cmap_copy, cmap_subtable, cmap_subtable_len);

if (hmtx_len == 0 || hmtx_len > font_data_len) {
free(cmap_copy);
free(font_data);
return pdf_set_err(pdf, -EINVAL, "Font '%s' has invalid hmtx_len",
path);
pdf_set_err(pdf, -EINVAL, "Font '%s' has invalid hmtx_len", path);
return NULL;
}

// Make a copy of the hmtx table for runtime advance-width lookups
uint8_t *hmtx_copy = (uint8_t *)malloc(hmtx_len);
if (!hmtx_copy) {
free(cmap_copy);
free(font_data);
return pdf_set_err(pdf, -ENOMEM,
"Unable to allocate hmtx table for font '%s'",
path);
pdf_set_err(pdf, -ENOMEM,
"Unable to allocate hmtx table for font '%s'", path);
return NULL;
}
memcpy(hmtx_copy, hmtx, hmtx_len);

Expand All @@ -1376,18 +1386,19 @@ int pdf_set_font_ttf_file(struct pdf_doc *pdf, FILE *fp, const char *path)
free(hmtx_copy);
free(cmap_copy);
free(font_data);
return pdf_set_err(pdf, -EINVAL,
"Font '%s' has invalid numberOfHMetrics", path);
pdf_set_err(pdf, -EINVAL, "Font '%s' has invalid numberOfHMetrics",
path);
return NULL;
}
uint16_t *glyph_widths =
(uint16_t *)calloc(numberOfHMetrics, sizeof(uint16_t));
if (!glyph_widths) {
free(hmtx_copy);
free(cmap_copy);
free(font_data);
return pdf_set_err(pdf, -ENOMEM,
"Unable to allocate glyph widths for font '%s'",
path);
pdf_set_err(pdf, -ENOMEM,
"Unable to allocate glyph widths for font '%s'", path);
return NULL;
}
for (uint16_t g = 0; g < numberOfHMetrics; g++) {
uint16_t advance =
Expand All @@ -1406,7 +1417,7 @@ int pdf_set_font_ttf_file(struct pdf_doc *pdf, FILE *fp, const char *path)
free(hmtx_copy);
free(cmap_copy);
free(font_data);
return pdf->errval;
return NULL;
}
dstr_printf(&stream_obj->stream.stream,
"<<\r\n"
Expand All @@ -1420,8 +1431,8 @@ int pdf_set_font_ttf_file(struct pdf_doc *pdf, FILE *fp, const char *path)
free(hmtx_copy);
free(cmap_copy);
free(font_data);
return pdf_set_err(pdf, -ENOMEM,
"Unable to allocate font stream data");
pdf_set_err(pdf, -ENOMEM, "Unable to allocate font stream data");
return NULL;
}
dstr_append(&stream_obj->stream.stream, "\r\nendstream\r\n");
free(font_data);
Expand All @@ -1432,7 +1443,7 @@ int pdf_set_font_ttf_file(struct pdf_doc *pdf, FILE *fp, const char *path)
free(glyph_widths);
free(hmtx_copy);
free(cmap_copy);
return pdf->errval;
return NULL;
}
strncpy(descriptor_obj->font_descriptor.font_name, font_name,
sizeof(descriptor_obj->font_descriptor.font_name) - 1);
Expand Down Expand Up @@ -1461,7 +1472,7 @@ int pdf_set_font_ttf_file(struct pdf_doc *pdf, FILE *fp, const char *path)
free(glyph_widths);
free(hmtx_copy);
free(cmap_copy);
return pdf->errval;
return NULL;
}
strncpy(cid_obj->cid_font.font_name, font_name,
sizeof(cid_obj->cid_font.font_name) - 1);
Expand All @@ -1477,7 +1488,7 @@ int pdf_set_font_ttf_file(struct pdf_doc *pdf, FILE *fp, const char *path)
if (!font_obj) {
free(hmtx_copy);
free(cmap_copy);
return pdf->errval;
return NULL;
}
strncpy(font_obj->font.name, font_name, sizeof(font_obj->font.name) - 1);
font_obj->font.name[sizeof(font_obj->font.name) - 1] = '\0';
Expand All @@ -1493,7 +1504,7 @@ int pdf_set_font_ttf_file(struct pdf_doc *pdf, FILE *fp, const char *path)
font_obj->font.cid_font_index = cid_obj->index;

pdf->current_font = font_obj;
return 0;
return font_obj->font.name;
}

struct pdf_object *pdf_append_page(struct pdf_doc *pdf)
Expand Down
10 changes: 6 additions & 4 deletions pdfgen.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,9 +338,10 @@ int pdf_set_font(struct pdf_doc *pdf, const char *font);
* and will remain until pdf_set_font or pdf_set_font_ttf is called again.
* @param pdf PDF document to update font on
* @param path Filesystem path to a TrueType (.ttf) font file
* @return < 0 on failure, 0 on success
* @return NULL on failure, name of the font to use on success (this is used
* for pdf_get_font_text_width)
*/
int pdf_set_font_ttf(struct pdf_doc *pdf, const char *path);
const char *pdf_set_font_ttf(struct pdf_doc *pdf, const char *path);

/**
* Sets the font to use for text objects by loading a TrueType font file from
Expand All @@ -349,9 +350,10 @@ int pdf_set_font_ttf(struct pdf_doc *pdf, const char *path);
* @param pdf PDF document to update font on
* @param fp TTF font file pointer (must be readable and seekable)
* @param path Name of font for error messages and for internal PDF reference
* @return < 0 on failure, 0 on success
* @return NULL on failure, name of the font to use on success
*/
int pdf_set_font_ttf_file(struct pdf_doc *pdf, FILE *fp, const char *path);
const char *pdf_set_font_ttf_file(struct pdf_doc *pdf, FILE *fp,
const char *path);

/**
* Calculate the width of a given string in the current font
Expand Down
4 changes: 2 additions & 2 deletions tests/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ int main(int argc, char *argv[])
const char *ttf_path = "data/Ithaca.ttf";
pdf_append_page(pdf);
pdf_add_bookmark(pdf, NULL, -1, "TrueType Font (Unicode)");
if (pdf_set_font_ttf(pdf, ttf_path) < 0) {
if (pdf_set_font_ttf(pdf, ttf_path) == NULL) {
fprintf(stderr, "Failed to load TTF font: %s\n",
pdf_get_err(pdf, &err));
pdf_destroy(pdf);
Expand Down Expand Up @@ -346,7 +346,7 @@ int main(int argc, char *argv[])
12, 50, 600, 0, PDF_BLACK, 300, PDF_ALIGN_LEFT, NULL);

// Reload the same font (should reuse the existing object)
if (pdf_set_font_ttf(pdf, ttf_path) < 0) {
if (pdf_set_font_ttf(pdf, ttf_path) == NULL) {
fprintf(stderr, "Failed to reload TTF font\n");
pdf_destroy(pdf);
return -1;
Expand Down
Loading