Compare commits

..

2 Commits

Author SHA1 Message Date
Michael R Sweet
8f2c47cb07
Make sure memory is freed on error conditions. 2024-10-09 15:32:48 -04:00
Michael R Sweet
74dfefdcc1
Update documentation (Issue #77)
- Explain pdfioObjGetSubtype and pdfioObjGetType values
- Provide example code and documentation for accessing common page object values
2024-10-09 15:07:57 -04:00
7 changed files with 344 additions and 15 deletions

View File

@ -6,6 +6,7 @@ v1.3.2 - YYYY-MM-DD
-------------------
- Added some more sanity checks to the TrueType font reader.
- Updated documentation (Issue #77)
- Fixed an issue when opening certain encrypted PDF files (Issue #62)

View File

@ -1,4 +1,4 @@
.TH pdfio 3 "pdf read/write library" "2024-08-05" "pdf read/write library"
.TH pdfio 3 "pdf read/write library" "2024-10-09" "pdf read/write library"
.SH NAME
pdfio \- pdf read/write library
.SH Introduction
@ -218,7 +218,90 @@ Each PDF file contains one or more pages. The pdfioFileGetNumPages function retu
}
.fi
.PP
Each page is represented by a "page tree" object (what pdfioFileGetPage returns) that specifies information about the page and one or more "content" objects that contain the images, fonts, text, and graphics that appear on the page. Use the pdfioPageGetNumStreams and pdfioPageOpenStream functions to access the content streams for each page.
Each page is represented by a "page tree" object (what pdfioFileGetPage returns) that specifies information about the page and one or more "content" objects that contain the images, fonts, text, and graphics that appear on the page. Use the pdfioPageGetNumStreams and pdfioPageOpenStream functions to access the content streams for each page, and pdfioObjGetDict to get the associated page object dictionary. For example, if you want to display the media and crop boxes for a given page:
.nf
pdfio_file_t *pdf; // PDF file
size_t i; // Looping var
size_t count; // Number of pages
pdfio_obj_t *page; // Current page
pdfio_dict_t *dict; // Current page dictionary
pdfio_array_t *media_box; // MediaBox array
double media_values[4]; // MediaBox values
pdfio_array_t *crop_box; // CropBox array
double crop_values[4]; // CropBox values
// Iterate the pages in the PDF file
for (i = 0, count = pdfioFileGetNumPages(pdf); i < count; i ++)
{
page = pdfioFileGetPage(pdf, i);
dict = pdfioObjGetDict(page);
media_box = pdfioDictGetArray(dict, "MediaBox");
media_values[0] = pdfioArrayGetNumber(media_box, 0);
media_values[1] = pdfioArrayGetNumber(media_box, 1);
media_values[2] = pdfioArrayGetNumber(media_box, 2);
media_values[3] = pdfioArrayGetNumber(media_box, 3);
crop_box = pdfioDictGetArray(dict, "CropBox");
crop_values[0] = pdfioArrayGetNumber(crop_box, 0);
crop_values[1] = pdfioArrayGetNumber(crop_box, 1);
crop_values[2] = pdfioArrayGetNumber(crop_box, 2);
crop_values[3] = pdfioArrayGetNumber(crop_box, 3);
printf("Page %u: MediaBox=[%g %g %g %g], CropBox=[%g %g %g %g]\\n",
(unsigned)(i + 1),
media_values[0], media_values[1], media_values[2], media_values[3],
crop_values[0], crop_values[1], crop_values[2], crop_values[3]);
}
.fi
.PP
Page object dictionaries have several (mostly optional) key/value pairs, including:
.IP \(bu 5
.PP
"Annots": An array of annotation dictionaries for the page; use pdfioDictGetArray to get the array
.IP \(bu 5
.PP
"CropBox": The crop box as an array of four numbers for the left, bottom, right, and top coordinates of the target media; use pdfioDictGetArray to get a pointer to the array of numbers
.IP \(bu 5
.PP
"Dur": The number of seconds the page should be displayed; use pdfioDictGetNumber to get the page duration value
.IP \(bu 5
.PP
"Group": The dictionary of transparency group values for the page; use pdfioDictGetDict to get a pointer to the resources dictionary
.IP \(bu 5
.PP
"LastModified": The date and time when this page was last modified; use pdfioDictGetDate to get the Unix time_t value
.IP \(bu 5
.PP
"Parent": The parent page tree node object for this page; use pdfioDictGetObj to get a pointer to the object
.IP \(bu 5
.PP
"MediaBox": The media box as an array of four numbers for the left, bottom, right, and top coordinates of the target media; use pdfioDictGetArray to get a pointer to the array of numbers
.IP \(bu 5
.PP
"Resources": The dictionary of resources for the page; use pdfioDictGetDict to get a pointer to the resources dictionary
.IP \(bu 5
.PP
"Rotate": A number indicating the number of degrees of counter\-clockwise rotation to apply to the page when viewing; use pdfioDictGetNumber to get the rotation angle
.IP \(bu 5
.PP
"Thumb": A thumbnail image object for the page; use pdfioDictGetObj to get a pointer to the thumbnail image object
.IP \(bu 5
.PP
"Trans": The page transition dictionary; use pdfioDictGetDict to get a pointer to the dictionary
.PP
The pdfioFileClose function closes a PDF file and frees all memory that was used for it:
.nf
@ -2869,6 +2952,29 @@ const char * pdfioObjGetSubtype (
pdfio_obj_t *obj
);
.fi
.PP
This function returns an object's PDF subtype name, if any. Common subtype
names include:
.PP
.IP \(bu 5
"CIDFontType0": A CID Type0 font
.IP \(bu 5
"CIDFontType2": A CID TrueType font
.IP \(bu 5
"Image": An image or image mask
.IP \(bu 5
"Form": A fillable form
.IP \(bu 5
"OpenType": An OpenType font
.IP \(bu 5
"Type0": A composite font
.IP \(bu 5
"Type1": A PostScript Type1 font
.IP \(bu 5
"Type3": A PDF Type3 font
.IP \(bu 5
"TrueType": A TrueType font</li>
</ul>
.SS pdfioObjGetType
Get an object's type.
.PP
@ -2877,6 +2983,27 @@ const char * pdfioObjGetType (
pdfio_obj_t *obj
);
.fi
.PP
This function returns an object's PDF type name, if any. Common type names
include:
.PP
.IP \(bu 5
"CMap": A character map for composite fonts
.IP \(bu 5
"Font": An embedded font (\fIpdfioObjGetSubtype\fR will tell you the
font format)
.IP \(bu 5
"FontDescriptor": A font descriptor
.IP \(bu 5
"Page": A (visible) page
.IP \(bu 5
"Pages": A page tree node
.IP \(bu 5
"Template": An invisible template page
.IP \(bu 5
"XObject": An image, image mask, or form (\fIpdfioObjGetSubtype\fR will
tell you which)</li>
</ul>
.SS pdfioObjOpenStream
Open an object's (data) stream for reading.
.PP

View File

@ -1,13 +1,13 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>PDFio Programming Manual v1.3.0</title>
<title>PDFio Programming Manual v1.3.2</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<meta name="generator" content="codedoc v3.7">
<meta name="author" content="Michael R Sweet">
<meta name="language" content="en-US">
<meta name="copyright" content="Copyright © 2021-2024 by Michael R Sweet">
<meta name="version" content="1.3.0">
<meta name="version" content="1.3.2">
<style type="text/css"><!--
body {
background: white;
@ -251,7 +251,7 @@ span.string {
<body>
<div class="header">
<p><img class="title" src="pdfio-512.png"></p>
<h1 class="title">PDFio Programming Manual v1.3.0</h1>
<h1 class="title">PDFio Programming Manual v1.3.2</h1>
<p>Michael R Sweet</p>
<p>Copyright © 2021-2024 by Michael R Sweet</p>
</div>
@ -628,7 +628,66 @@ pdfio_obj_t *page; <span class="comment">// Current page</span>
<span class="comment">// do something with page</span>
}
</code></pre>
<p>Each page is represented by a &quot;page tree&quot; object (what <a href="#pdfioFileGetPage"><code>pdfioFileGetPage</code></a> returns) that specifies information about the page and one or more &quot;content&quot; objects that contain the images, fonts, text, and graphics that appear on the page. Use the <a href="#pdfioPageGetNumStreams"><code>pdfioPageGetNumStreams</code></a> and <a href="#pdfioPageOpenStream"><code>pdfioPageOpenStream</code></a> functions to access the content streams for each page.</p>
<p>Each page is represented by a &quot;page tree&quot; object (what <a href="#pdfioFileGetPage"><code>pdfioFileGetPage</code></a> returns) that specifies information about the page and one or more &quot;content&quot; objects that contain the images, fonts, text, and graphics that appear on the page. Use the <a href="#pdfioPageGetNumStreams"><code>pdfioPageGetNumStreams</code></a> and <a href="#pdfioPageOpenStream"><code>pdfioPageOpenStream</code></a> functions to access the content streams for each page, and <a href="#pdfioObjGetDict"><code>pdfioObjGetDict</code></a> to get the associated page object dictionary. For example, if you want to display the media and crop boxes for a given page:</p>
<pre><code class="language-c">pdfio_file_t *pdf; <span class="comment">// PDF file</span>
size_t i; <span class="comment">// Looping var</span>
size_t count; <span class="comment">// Number of pages</span>
pdfio_obj_t *page; <span class="comment">// Current page</span>
pdfio_dict_t *dict; <span class="comment">// Current page dictionary</span>
pdfio_array_t *media_box; <span class="comment">// MediaBox array</span>
<span class="reserved">double</span> media_values[<span class="number">4</span>]; <span class="comment">// MediaBox values</span>
pdfio_array_t *crop_box; <span class="comment">// CropBox array</span>
<span class="reserved">double</span> crop_values[<span class="number">4</span>]; <span class="comment">// CropBox values</span>
<span class="comment">// Iterate the pages in the PDF file</span>
<span class="reserved">for</span> (i = <span class="number">0</span>, count = pdfioFileGetNumPages(pdf); i &lt; count; i ++)
{
page = pdfioFileGetPage(pdf, i);
dict = pdfioObjGetDict(page);
media_box = pdfioDictGetArray(dict, <span class="string">&quot;MediaBox&quot;</span>);
media_values[<span class="number">0</span>] = pdfioArrayGetNumber(media_box, <span class="number">0</span>);
media_values[<span class="number">1</span>] = pdfioArrayGetNumber(media_box, <span class="number">1</span>);
media_values[<span class="number">2</span>] = pdfioArrayGetNumber(media_box, <span class="number">2</span>);
media_values[<span class="number">3</span>] = pdfioArrayGetNumber(media_box, <span class="number">3</span>);
crop_box = pdfioDictGetArray(dict, <span class="string">&quot;CropBox&quot;</span>);
crop_values[<span class="number">0</span>] = pdfioArrayGetNumber(crop_box, <span class="number">0</span>);
crop_values[<span class="number">1</span>] = pdfioArrayGetNumber(crop_box, <span class="number">1</span>);
crop_values[<span class="number">2</span>] = pdfioArrayGetNumber(crop_box, <span class="number">2</span>);
crop_values[<span class="number">3</span>] = pdfioArrayGetNumber(crop_box, <span class="number">3</span>);
printf(<span class="string">&quot;Page %u: MediaBox=[%g %g %g %g], CropBox=[%g %g %g %g]\n&quot;</span>,
(<span class="reserved">unsigned</span>)(i + <span class="number">1</span>),
media_values[<span class="number">0</span>], media_values[<span class="number">1</span>], media_values[<span class="number">2</span>], media_values[<span class="number">3</span>],
crop_values[<span class="number">0</span>], crop_values[<span class="number">1</span>], crop_values[<span class="number">2</span>], crop_values[<span class="number">3</span>]);
}
</code></pre>
<p>Page object dictionaries have several (mostly optional) key/value pairs, including:</p>
<ul>
<li><p>&quot;Annots&quot;: An array of annotation dictionaries for the page; use <a href="#pdfioDictGetArray"><code>pdfioDictGetArray</code></a> to get the array</p>
</li>
<li><p>&quot;CropBox&quot;: The crop box as an array of four numbers for the left, bottom, right, and top coordinates of the target media; use <a href="#pdfioDictGetArray"><code>pdfioDictGetArray</code></a> to get a pointer to the array of numbers</p>
</li>
<li><p>&quot;Dur&quot;: The number of seconds the page should be displayed; use <a href="#pdfioDictGetNumber"><code>pdfioDictGetNumber</code></a> to get the page duration value</p>
</li>
<li><p>&quot;Group&quot;: The dictionary of transparency group values for the page; use <a href="#pdfioDictGetDict"><code>pdfioDictGetDict</code></a> to get a pointer to the resources dictionary</p>
</li>
<li><p>&quot;LastModified&quot;: The date and time when this page was last modified; use <a href="#pdfioDictGetDate"><code>pdfioDictGetDate</code></a> to get the Unix <code>time_t</code> value</p>
</li>
<li><p>&quot;Parent&quot;: The parent page tree node object for this page; use <a href="#pdfioDictGetObj"><code>pdfioDictGetObj</code></a> to get a pointer to the object</p>
</li>
<li><p>&quot;MediaBox&quot;: The media box as an array of four numbers for the left, bottom, right, and top coordinates of the target media; use <a href="#pdfioDictGetArray"><code>pdfioDictGetArray</code></a> to get a pointer to the array of numbers</p>
</li>
<li><p>&quot;Resources&quot;: The dictionary of resources for the page; use <a href="#pdfioDictGetDict"><code>pdfioDictGetDict</code></a> to get a pointer to the resources dictionary</p>
</li>
<li><p>&quot;Rotate&quot;: A number indicating the number of degrees of counter-clockwise rotation to apply to the page when viewing; use <a href="#pdfioDictGetNumber"><code>pdfioDictGetNumber</code></a> to get the rotation angle</p>
</li>
<li><p>&quot;Thumb&quot;: A thumbnail image object for the page; use <a href="#pdfioDictGetObj"><code>pdfioDictGetObj</code></a> to get a pointer to the thumbnail image object</p>
</li>
<li><p>&quot;Trans&quot;: The page transition dictionary; use <a href="#pdfioDictGetDict"><code>pdfioDictGetDict</code></a> to get a pointer to the dictionary</p>
</li>
</ul>
<p>The <a href="#pdfioFileClose"><code>pdfioFileClose</code></a> function closes a PDF file and frees all memory that was used for it:</p>
<pre><code class="language-c">pdfioFileClose(pdf);
</code></pre>
@ -3490,7 +3549,30 @@ size_t pdfioObjGetNumber(<a href="#pdfio_obj_t">pdfio_obj_t</a> *obj);</p>
<td class="description">Object</td></tr>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">Object subtype</p>
<p class="description">Object subtype name or <code>NULL</code> for none</p>
<h4 class="discussion">Discussion</h4>
<p class="discussion">This function returns an object's PDF subtype name, if any. Common subtype
names include:
</p><ul>
<li>&quot;CIDFontType0&quot;: A CID Type0 font
</li>
<li>&quot;CIDFontType2&quot;: A CID TrueType font
</li>
<li>&quot;Image&quot;: An image or image mask
</li>
<li>&quot;Form&quot;: A fillable form
</li>
<li>&quot;OpenType&quot;: An OpenType font
</li>
<li>&quot;Type0&quot;: A composite font
</li>
<li>&quot;Type1&quot;: A PostScript Type1 font
</li>
<li>&quot;Type3&quot;: A PDF Type3 font
</li>
<li>&quot;TrueType&quot;: A TrueType font</li>
</ul>
<h3 class="function"><a id="pdfioObjGetType">pdfioObjGetType</a></h3>
<p class="description">Get an object's type.</p>
<p class="code">
@ -3501,7 +3583,28 @@ size_t pdfioObjGetNumber(<a href="#pdfio_obj_t">pdfio_obj_t</a> *obj);</p>
<td class="description">Object</td></tr>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">Object type</p>
<p class="description">Object type name or <code>NULL</code> for none</p>
<h4 class="discussion">Discussion</h4>
<p class="discussion">This function returns an object's PDF type name, if any. Common type names
include:
</p><ul>
<li>&quot;CMap&quot;: A character map for composite fonts
</li>
<li>&quot;Font&quot;: An embedded font (<a href="#pdfioObjGetSubtype"><code>pdfioObjGetSubtype</code></a> will tell you the
font format)
</li>
<li>&quot;FontDescriptor&quot;: A font descriptor
</li>
<li>&quot;Page&quot;: A (visible) page
</li>
<li>&quot;Pages&quot;: A page tree node
</li>
<li>&quot;Template&quot;: An invisible template page
</li>
<li>&quot;XObject&quot;: An image, image mask, or form (<a href="#pdfioObjGetSubtype"><code>pdfioObjGetSubtype</code></a> will
tell you which)</li>
</ul>
<h3 class="function"><a id="pdfioObjOpenStream">pdfioObjOpenStream</a></h3>
<p class="description">Open an object's (data) stream for reading.</p>
<p class="code">

View File

@ -202,7 +202,74 @@ Each page is represented by a "page tree" object (what [`pdfioFileGetPage`](@@)
returns) that specifies information about the page and one or more "content"
objects that contain the images, fonts, text, and graphics that appear on the
page. Use the [`pdfioPageGetNumStreams`](@@) and [`pdfioPageOpenStream`](@@)
functions to access the content streams for each page.
functions to access the content streams for each page, and
[`pdfioObjGetDict`](@@) to get the associated page object dictionary. For
example, if you want to display the media and crop boxes for a given page:
```c
pdfio_file_t *pdf; // PDF file
size_t i; // Looping var
size_t count; // Number of pages
pdfio_obj_t *page; // Current page
pdfio_dict_t *dict; // Current page dictionary
pdfio_array_t *media_box; // MediaBox array
double media_values[4]; // MediaBox values
pdfio_array_t *crop_box; // CropBox array
double crop_values[4]; // CropBox values
// Iterate the pages in the PDF file
for (i = 0, count = pdfioFileGetNumPages(pdf); i < count; i ++)
{
page = pdfioFileGetPage(pdf, i);
dict = pdfioObjGetDict(page);
media_box = pdfioDictGetArray(dict, "MediaBox");
media_values[0] = pdfioArrayGetNumber(media_box, 0);
media_values[1] = pdfioArrayGetNumber(media_box, 1);
media_values[2] = pdfioArrayGetNumber(media_box, 2);
media_values[3] = pdfioArrayGetNumber(media_box, 3);
crop_box = pdfioDictGetArray(dict, "CropBox");
crop_values[0] = pdfioArrayGetNumber(crop_box, 0);
crop_values[1] = pdfioArrayGetNumber(crop_box, 1);
crop_values[2] = pdfioArrayGetNumber(crop_box, 2);
crop_values[3] = pdfioArrayGetNumber(crop_box, 3);
printf("Page %u: MediaBox=[%g %g %g %g], CropBox=[%g %g %g %g]\n",
(unsigned)(i + 1),
media_values[0], media_values[1], media_values[2], media_values[3],
crop_values[0], crop_values[1], crop_values[2], crop_values[3]);
}
```
Page object dictionaries have several (mostly optional) key/value pairs,
including:
- "Annots": An array of annotation dictionaries for the page; use
[`pdfioDictGetArray`](@@) to get the array
- "CropBox": The crop box as an array of four numbers for the left, bottom,
right, and top coordinates of the target media; use [`pdfioDictGetArray`](@@)
to get a pointer to the array of numbers
- "Dur": The number of seconds the page should be displayed; use
[`pdfioDictGetNumber`](@@) to get the page duration value
- "Group": The dictionary of transparency group values for the page; use
[`pdfioDictGetDict`](@@) to get a pointer to the resources dictionary
- "LastModified": The date and time when this page was last modified; use
[`pdfioDictGetDate`](@@) to get the Unix `time_t` value
- "Parent": The parent page tree node object for this page; use
[`pdfioDictGetObj`](@@) to get a pointer to the object
- "MediaBox": The media box as an array of four numbers for the left, bottom,
right, and top coordinates of the target media; use [`pdfioDictGetArray`](@@)
to get a pointer to the array of numbers
- "Resources": The dictionary of resources for the page; use
[`pdfioDictGetDict`](@@) to get a pointer to the resources dictionary
- "Rotate": A number indicating the number of degrees of counter-clockwise
rotation to apply to the page when viewing; use [`pdfioDictGetNumber`](@@)
to get the rotation angle
- "Thumb": A thumbnail image object for the page; use [`pdfioDictGetObj`](@@)
to get a pointer to the thumbnail image object
- "Trans": The page transition dictionary; use [`pdfioDictGetDict`](@@) to get
a pointer to the dictionary
The [`pdfioFileClose`](@@) function closes a PDF file and frees all memory that
was used for it:

View File

@ -1,7 +1,7 @@
//
// PDF object functions for PDFio.
//
// Copyright © 2021-2023 by Michael R Sweet.
// Copyright © 2021-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@ -347,8 +347,21 @@ pdfioObjGetNumber(pdfio_obj_t *obj) // I - Object
//
// 'pdfioObjGetSubtype()' - Get an object's subtype.
//
// This function returns an object's PDF subtype name, if any. Common subtype
// names include:
//
// - "CIDFontType0": A CID Type0 font
// - "CIDFontType2": A CID TrueType font
// - "Image": An image or image mask
// - "Form": A fillable form
// - "OpenType": An OpenType font
// - "Type0": A composite font
// - "Type1": A PostScript Type1 font
// - "Type3": A PDF Type3 font
// - "TrueType": A TrueType font
//
const char * // O - Object subtype
const char * // O - Object subtype name or `NULL` for none
pdfioObjGetSubtype(pdfio_obj_t *obj) // I - Object
{
pdfio_dict_t *dict; // Object dictionary
@ -364,8 +377,21 @@ pdfioObjGetSubtype(pdfio_obj_t *obj) // I - Object
//
// 'pdfioObjGetType()' - Get an object's type.
//
// This function returns an object's PDF type name, if any. Common type names
// include:
//
// - "CMap": A character map for composite fonts
// - "Font": An embedded font (@link pdfioObjGetSubtype@ will tell you the
// font format)
// - "FontDescriptor": A font descriptor
// - "Page": A (visible) page
// - "Pages": A page tree node
// - "Template": An invisible template page
// - "XObject": An image, image mask, or form (@link pdfioObjGetSubtype@ will
// tell you which)
//
const char * // O - Object type
const char * // O - Object type name or `NULL` for none
pdfioObjGetType(pdfio_obj_t *obj) // I - Object
{
pdfio_dict_t *dict; // Object dictionary

View File

@ -1064,9 +1064,6 @@ stream_read(pdfio_stream_t *st, // I - Stream
st->flate.next_out = (Bytef *)buffer;
st->flate.avail_out = (uInt)bytes;
avail_in = st->flate.avail_in;
avail_out = st->flate.avail_out;
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
{
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));

8
ttf.c
View File

@ -1309,6 +1309,8 @@ read_cmap(ttf_t *font) // I - Font
if (segment->startCode > segment->endCode)
{
errorf(font, "Bad cmap table segment %u to %u.", segments->startCode, segment->endCode);
free(segments);
free(glyphIdArray);
return (false);
}
@ -1326,6 +1328,8 @@ read_cmap(ttf_t *font) // I - Font
if (font->num_cmap == 0 || font->num_cmap > TTF_FONT_MAX_CHAR)
{
errorf(font, "Invalid cmap table with %u characters.", (unsigned)font->num_cmap);
free(segments);
free(glyphIdArray);
return (false);
}
@ -1422,6 +1426,7 @@ read_cmap(ttf_t *font) // I - Font
if (group->startCharCode > group->endCharCode)
{
errorf(font, "Bad cmap table segment %u to %u.", group->startCharCode, group->endCharCode);
free(groups);
return (false);
}
@ -1436,6 +1441,7 @@ read_cmap(ttf_t *font) // I - Font
if (font->num_cmap == 0 || font->num_cmap > TTF_FONT_MAX_CHAR)
{
errorf(font, "Invalid cmap table with %u characters.", (unsigned)font->num_cmap);
free(groups);
return (false);
}
@ -1511,6 +1517,7 @@ read_cmap(ttf_t *font) // I - Font
if (group->startCharCode > group->endCharCode)
{
errorf(font, "Bad cmap table segment %u to %u.", group->startCharCode, group->endCharCode);
free(groups);
return (false);
}
@ -1525,6 +1532,7 @@ read_cmap(ttf_t *font) // I - Font
if (font->num_cmap == 0 || font->num_cmap > TTF_FONT_MAX_CHAR)
{
errorf(font, "Invalid cmap table with %u characters.", (unsigned)font->num_cmap);
free(groups);
return (false);
}