Compare commits

..

No commits in common. "cad8f450ab0d267db33e47c6a171bb0c372a87d8" and "1e6bb710e30e64ae55edb32d248deb21c852ced2" have entirely different histories.

11 changed files with 136 additions and 306 deletions

View File

@ -1,4 +1,4 @@
.TH pdfio 3 "pdf read/write library" "2025-04-23" "pdf read/write library"
.TH pdfio 3 "pdf read/write library" "2025-04-13" "pdf read/write library"
.SH NAME
pdfio \- pdf read/write library
.SH Introduction
@ -325,7 +325,7 @@ where the five arguments to the function are the filename ("myinputfile.pdf"), a
}
.fi
.PP
The error callback is called for both errors and warnings and accepts the pdfio_file_t pointer, a message string, and the callback pointer value. It returns true to continue processing the file or false to stop, for example:
The error callback is called for both errors and warnings and accepts the pdfio_file_t pointer, a message string, and the callback pointer value, for example:
.nf
bool
@ -335,15 +335,12 @@ The error callback is called for both errors and warnings and accepts the pdfio_
fprintf(stderr, "%s: %s\\n", pdfioFileGetName(pdf), message);
// Return true for warning messages (continue) and false for errors (stop)
return (!strncmp(message, "WARNING:", 8));
// Return false to treat warnings as errors
return (false);
}
.fi
.PP
The default error callback (NULL) does the equivalent of the above.
.PP
Note: Many errors are unrecoverable, so PDFio ignores the return value from the error callback and always stops processing the PDF file. Warning messages start with the prefix "WARNING:" while errors have no prefix.
.PP
Each PDF file contains one or more pages. The pdfioFileGetNumPages function returns the number of pages in the file while the pdfioFileGetPage function gets the specified page in the PDF file:
.nf
@ -3913,9 +3910,8 @@ CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" siz
of 8.27x11in (the intersection of US Letter and ISO A4) is used.
.PP
The "error_cb" and "error_cbdata" arguments specify an error handler callback
and its data pointer - if \fBNULL\fR then the default error handler is used that
writes error messages to \fBstderr\fR. The error handler callback should return
\fBtrue\fR to continue writing the PDF file or \fBfalse\fR to stop.
and its data pointer - if \fBNULL\fR the default error handler is used that
writes error messages to \fBstderr\fR.
.SS pdfioFileCreateArrayObj
Create a new object in a PDF file containing an array.
.PP
@ -4156,9 +4152,8 @@ CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" siz
of 8.27x11in (the intersection of US Letter and ISO A4) is used.
.PP
The "error_cb" and "error_cbdata" arguments specify an error handler callback
and its data pointer - if \fBNULL\fR then the default error handler is used that
writes error messages to \fBstderr\fR. The error handler callback should return
\fBtrue\fR to continue writing the PDF file or \fBfalse\fR to stop.
and its data pointer - if \fBNULL\fR the default error handler is used that
writes error messages to \fBstderr\fR.
.PP
.IP 5
\fINote\fR: Files created using this API are slightly larger than those
@ -4397,18 +4392,8 @@ cancel the open. If \fBNULL\fR is specified for the callback function and the
PDF file requires a password, the open will always fail.
.PP
The "error_cb" and "error_cbdata" arguments specify an error handler callback
and its data pointer - if \fBNULL\fR then the default error handler is used that
writes error messages to \fBstderr\fR. The error handler callback should return
\fBtrue\fR to continue reading the PDF file or \fBfalse\fR to stop.
.PP
.IP 5
Note: Error messages starting with "WARNING:" are actually warning
.IP 5
messages - the callback should normally return \fBtrue\fR to allow PDFio to
.IP 5
try to resolve the issue. In addition, some errors are unrecoverable and
.IP 5
ignore the return value of the error callback.
and its data pointer - if \fBNULL\fR the default error handler is used that
writes error messages to \fBstderr\fR.
.SS pdfioFileSetAuthor
Set the author for a PDF file.
.PP

View File

@ -732,7 +732,7 @@ password_cb(<span class="reserved">void</span> *data, <span class="reserved">con
<span class="reserved">return</span> (<span class="string">&quot;Password42&quot;</span>);
}
</code></pre>
<p>The error callback is called for both errors and warnings and accepts the <code>pdfio_file_t</code> pointer, a message string, and the callback pointer value. It returns <code>true</code> to continue processing the file or <code>false</code> to stop, for example:</p>
<p>The error callback is called for both errors and warnings and accepts the <code>pdfio_file_t</code> pointer, a message string, and the callback pointer value, for example:</p>
<pre><code class="language-c"><span class="reserved">bool</span>
error_cb(pdfio_file_t *pdf, <span class="reserved">const</span> <span class="reserved">char</span> *message, <span class="reserved">void</span> *data)
{
@ -740,14 +740,11 @@ error_cb(pdfio_file_t *pdf, <span class="reserved">const</span> <span class="res
fprintf(stderr, <span class="string">&quot;%s: %s\n&quot;</span>, pdfioFileGetName(pdf), message);
<span class="comment">// Return true for warning messages (continue) and false for errors (stop)</span>
<span class="reserved">return</span> (!strncmp(message, <span class="string">&quot;WARNING:&quot;</span>, <span class="number">8</span>));
<span class="comment">// Return false to treat warnings as errors</span>
<span class="reserved">return</span> (<span class="reserved">false</span>);
}
</code></pre>
<p>The default error callback (<code>NULL</code>) does the equivalent of the above.</p>
<blockquote>
<p>Note: Many errors are unrecoverable, so PDFio ignores the return value from the error callback and always stops processing the PDF file. Warning messages start with the prefix &quot;WARNING:&quot; while errors have no prefix.</p>
</blockquote>
<p>Each PDF file contains one or more pages. The <a href="#pdfioFileGetNumPages"><code>pdfioFileGetNumPages</code></a> function returns the number of pages in the file while the <a href="#pdfioFileGetPage"><code>pdfioFileGetPage</code></a> function gets the specified page in the PDF file:</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>
@ -4132,9 +4129,8 @@ CropBox for pages in the PDF file - if <code>NULL</code> then a default &quot;Un
of 8.27x11in (the intersection of US Letter and ISO A4) is used.<br>
<br>
The &quot;error_cb&quot; and &quot;error_cbdata&quot; arguments specify an error handler callback
and its data pointer - if <code>NULL</code> then the default error handler is used that
writes error messages to <code>stderr</code>. The error handler callback should return
<code>true</code> to continue writing the PDF file or <code>false</code> to stop.</p>
and its data pointer - if <code>NULL</code> the default error handler is used that
writes error messages to <code>stderr</code>.</p>
<h3 class="function"><a id="pdfioFileCreateArrayObj">pdfioFileCreateArrayObj</a></h3>
<p class="description">Create a new object in a PDF file containing an array.</p>
<p class="code">
@ -4438,9 +4434,8 @@ CropBox for pages in the PDF file - if <code>NULL</code> then a default &quot;Un
of 8.27x11in (the intersection of US Letter and ISO A4) is used.<br>
<br>
The &quot;error_cb&quot; and &quot;error_cbdata&quot; arguments specify an error handler callback
and its data pointer - if <code>NULL</code> then the default error handler is used that
writes error messages to <code>stderr</code>. The error handler callback should return
<code>true</code> to continue writing the PDF file or <code>false</code> to stop.<br>
and its data pointer - if <code>NULL</code> the default error handler is used that
writes error messages to <code>stderr</code>.<br>
<br>
</p><blockquote>
<em>Note</em>: Files created using this API are slightly larger than those
@ -4777,15 +4772,8 @@ cancel the open. If <code>NULL</code> is specified for the callback function an
PDF file requires a password, the open will always fail.<br>
<br>
The &quot;error_cb&quot; and &quot;error_cbdata&quot; arguments specify an error handler callback
and its data pointer - if <code>NULL</code> then the default error handler is used that
writes error messages to <code>stderr</code>. The error handler callback should return
<code>true</code> to continue reading the PDF file or <code>false</code> to stop.<br>
<br>
</p><blockquote>
Note: Error messages starting with &quot;WARNING:&quot; are actually warning
messages - the callback should normally return <code>true</code> to allow PDFio to
try to resolve the issue. In addition, some errors are unrecoverable and
ignore the return value of the error callback.</blockquote>
and its data pointer - if <code>NULL</code> the default error handler is used that
writes error messages to <code>stderr</code>.</p>
<h3 class="function"><a id="pdfioFileSetAuthor">pdfioFileSetAuthor</a></h3>
<p class="description">Set the author for a PDF file.</p>
<p class="code">

View File

@ -343,8 +343,8 @@ password_cb(void *data, const char *filename)
```
The error callback is called for both errors and warnings and accepts the
`pdfio_file_t` pointer, a message string, and the callback pointer value. It
returns `true` to continue processing the file or `false` to stop, for example:
`pdfio_file_t` pointer, a message string, and the callback pointer value, for
example:
```c
bool
@ -354,17 +354,13 @@ error_cb(pdfio_file_t *pdf, const char *message, void *data)
fprintf(stderr, "%s: %s\n", pdfioFileGetName(pdf), message);
// Return true for warning messages (continue) and false for errors (stop)
return (!strncmp(message, "WARNING:", 8));
// Return false to treat warnings as errors
return (false);
}
```
The default error callback (`NULL`) does the equivalent of the above.
> Note: Many errors are unrecoverable, so PDFio ignores the return value from
> the error callback and always stops processing the PDF file. Warning messages
> start with the prefix "WARNING:" while errors have no prefix.
Each PDF file contains one or more pages. The [`pdfioFileGetNumPages`](@@)
function returns the number of pages in the file while the
[`pdfioFileGetPage`](@@) function gets the specified page in the PDF file:

View File

@ -47,7 +47,7 @@ _pdfioFileConsume(pdfio_file_t *pdf, // I - PDF file
// `false` to halt.
//
bool // O - `false` to stop, `true` to continue
bool // O - `false` to stop
_pdfioFileDefaultError(
pdfio_file_t *pdf, // I - PDF file
const char *message, // I - Error message
@ -57,7 +57,7 @@ _pdfioFileDefaultError(
fprintf(stderr, "%s: %s\n", pdf->filename, message);
return (!strncmp(message, "WARNING:", 8));
return (false);
}
@ -134,20 +134,19 @@ _pdfioFileGetChar(pdfio_file_t *pdf) // I - PDF file
bool // O - `true` on success, `false` on error
_pdfioFileGets(pdfio_file_t *pdf, // I - PDF file
char *buffer, // I - Line buffer
size_t bufsize, // I - Size of line buffer
bool discard) // I - OK to discard excess line chars?
size_t bufsize) // I - Size of line buffer
{
bool eol = false; // End of line?
char *bufptr = buffer, // Pointer into buffer
*bufend = buffer + bufsize - 1; // Pointer to end of buffer
PDFIO_DEBUG("_pdfioFileGets(pdf=%p, buffer=%p, bufsize=%lu, discard=%s) bufpos=%ld, buffer=%p, bufptr=%p, bufend=%p, offset=%lu\n", pdf, buffer, (unsigned long)bufsize, discard ? "true" : "false", (long)pdf->bufpos, pdf->buffer, pdf->bufptr, pdf->bufend, (unsigned long)(pdf->bufpos + (pdf->bufptr - pdf->buffer)));
PDFIO_DEBUG("_pdfioFileGets(pdf=%p, buffer=%p, bufsize=%lu) bufpos=%ld, buffer=%p, bufptr=%p, bufend=%p, offset=%lu\n", pdf, buffer, (unsigned long)bufsize, (long)pdf->bufpos, pdf->buffer, pdf->bufptr, pdf->bufend, (unsigned long)(pdf->bufpos + (pdf->bufptr - pdf->buffer)));
while (!eol)
{
// If there are characters ready in the buffer, use them...
while (!eol && pdf->bufptr < pdf->bufend)
while (!eol && pdf->bufptr < pdf->bufend && bufptr < bufend)
{
char ch = *(pdf->bufptr++); // Next character in buffer
@ -169,10 +168,8 @@ _pdfioFileGets(pdfio_file_t *pdf, // I - PDF file
pdf->bufptr ++;
}
}
else if (bufptr < bufend)
else
*bufptr++ = ch;
else if (!discard)
break;
}
// Fill the read buffer as needed...

View File

@ -643,11 +643,9 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
{
// Issue 118: Discard duplicate key/value pairs, in the future this will
// be a warning message...
_pdfioFileError(pdf, "WARNING: Discarding value for duplicate dictionary key '%s'.", key + 1);
_pdfioValueDelete(&value);
if (_pdfioFileError(pdf, "WARNING: Discarding value for duplicate dictionary key '%s'.", key + 1))
continue;
else
break;
continue;
}
else if (!_pdfioDictSetValue(dict, pdfioStringCreate(pdf, key + 1), &value))
break;

View File

@ -188,9 +188,8 @@ pdfioFileClose(pdfio_file_t *pdf) // I - PDF file
// of 8.27x11in (the intersection of US Letter and ISO A4) is used.
//
// The "error_cb" and "error_cbdata" arguments specify an error handler callback
// and its data pointer - if `NULL` then the default error handler is used that
// writes error messages to `stderr`. The error handler callback should return
// `true` to continue writing the PDF file or `false` to stop.
// and its data pointer - if `NULL` the default error handler is used that
// writes error messages to `stderr`.
//
pdfio_file_t * // O - PDF file or `NULL` on error
@ -427,9 +426,8 @@ _pdfioFileCreateObj(
// of 8.27x11in (the intersection of US Letter and ISO A4) is used.
//
// The "error_cb" and "error_cbdata" arguments specify an error handler callback
// and its data pointer - if `NULL` then the default error handler is used that
// writes error messages to `stderr`. The error handler callback should return
// `true` to continue writing the PDF file or `false` to stop.
// and its data pointer - if `NULL` the default error handler is used that
// writes error messages to `stderr`.
//
// > *Note*: Files created using this API are slightly larger than those
// > created using the @link pdfioFileCreate@ function since stream lengths are
@ -1021,14 +1019,8 @@ pdfioFileGetVersion(
// PDF file requires a password, the open will always fail.
//
// The "error_cb" and "error_cbdata" arguments specify an error handler callback
// and its data pointer - if `NULL` then the default error handler is used that
// writes error messages to `stderr`. The error handler callback should return
// `true` to continue reading the PDF file or `false` to stop.
//
// > Note: Error messages starting with "WARNING:" are actually warning
// > messages - the callback should normally return `true` to allow PDFio to
// > try to resolve the issue. In addition, some errors are unrecoverable and
// > ignore the return value of the error callback.
// and its data pointer - if `NULL` the default error handler is used that
// writes error messages to `stderr`.
//
pdfio_file_t * // O - PDF file
@ -1089,7 +1081,7 @@ pdfioFileOpen(
}
// Read the header from the first line...
if (!_pdfioFileGets(pdf, line, sizeof(line), true))
if (!_pdfioFileGets(pdf, line, sizeof(line)))
goto error;
if ((strncmp(line, "%PDF-1.", 7) && strncmp(line, "%PDF-2.", 7)) || !isdigit(line[7] & 255))
@ -1103,7 +1095,7 @@ pdfioFileOpen(
pdf->version = strdup(line + 5);
// Grab the last 1k of the file to find the start of the xref table...
if (_pdfioFileSeek(pdf, 1 - sizeof(line), SEEK_END) < 0)
if (_pdfioFileSeek(pdf, -1024, SEEK_END) < 0)
{
_pdfioFileError(pdf, "Unable to read startxref data.");
goto error;
@ -1115,34 +1107,26 @@ pdfioFileOpen(
goto error;
}
PDFIO_DEBUG("pdfioOpen: Read %d bytes at end of file.\n", (int)bytes);
line[bytes] = '\0';
end = line + bytes - 9;
for (ptr = line; ptr < end; ptr ++)
{
if (!strncmp(ptr, "startxref", 9) && !strstr(ptr + 9, "startxref") && strtol(ptr + 9, NULL, 10) > 0)
if (!memcmp(ptr, "startxref", 9))
break;
}
if (ptr >= end)
{
if (!_pdfioFileError(pdf, "WARNING: Unable to find start of cross-reference table, will attempt to rebuild."))
goto error;
if (!repair_xref(pdf, password_cb, password_cbdata))
goto error;
_pdfioFileError(pdf, "Unable to find start of xref table.");
goto error;
}
else
xref_offset = (off_t)strtol(ptr + 9, NULL, 10);
if (!load_xref(pdf, xref_offset, password_cb, password_cbdata))
{
PDFIO_DEBUG("pdfioFileOpen: line=%p,ptr=%p(\"%s\")\n", line, ptr, ptr);
xref_offset = (off_t)strtol(ptr + 9, NULL, 10);
PDFIO_DEBUG("pdfioFileOpen: xref_offset=%lu\n", (unsigned long)xref_offset);
if (!load_xref(pdf, xref_offset, password_cb, password_cbdata))
if (!repair_xref(pdf, password_cb, password_cbdata))
goto error;
}
@ -1771,10 +1755,7 @@ load_pages(pdfio_file_t *pdf, // I - PDF file
}
if ((type = pdfioDictGetName(dict, "Type")) == NULL || (strcmp(type, "Pages") && strcmp(type, "Page")))
{
if (!_pdfioFileError(pdf, "WARNING: No Type value for pages object."))
return (false);
}
return (false);
// If there is a Kids array, then this is a parent node and we have to look
// at the child objects...
@ -1841,32 +1822,31 @@ load_xref(
int generation; // Generation number
_pdfio_token_t tb; // Token buffer/stack
off_t line_offset; // Offset to start of line
pdfio_obj_t *pages_obj; // Pages object
while (!done)
{
if (_pdfioFileSeek(pdf, xref_offset, SEEK_SET) != xref_offset)
{
PDFIO_DEBUG("load_xref: Unable to seek to %lu.\n", (unsigned long)xref_offset);
goto repair;
_pdfioFileError(pdf, "Unable to seek to start of xref table.");
return (false);
}
do
{
line_offset = _pdfioFileTell(pdf);
if (!_pdfioFileGets(pdf, line, sizeof(line), true))
if (!_pdfioFileGets(pdf, line, sizeof(line)))
{
PDFIO_DEBUG("load_xref: Unable to read line at offset %lu.\n", (unsigned long)line_offset);
goto repair;
_pdfioFileError(pdf, "Unable to read start of xref table.");
return (false);
}
}
while (!line[0]);
PDFIO_DEBUG("load_xref: line_offset=%lu, line='%s'\n", (unsigned long)line_offset, line);
if (isdigit(line[0] & 255) && strlen(line) > 4 && (!strcmp(line + strlen(line) - 4, " obj") || ((ptr = strstr(line, " obj")) != NULL && (ptr[4] == '<' || isspace(ptr[4])))))
if (isdigit(line[0] & 255) && strlen(line) > 4 && (!strcmp(line + strlen(line) - 4, " obj") || ((ptr = strstr(line, " obj")) != NULL && ptr[4] == '<')))
{
// Cross-reference stream
pdfio_obj_t *obj; // Object
@ -1888,14 +1868,14 @@ load_xref(
if ((number = strtoimax(line, &ptr, 10)) < 1)
{
PDFIO_DEBUG("load_xref: Unable to scan object number.\n");
goto repair;
_pdfioFileError(pdf, "Bad xref table header '%s'.", line);
return (false);
}
if ((generation = (int)strtol(ptr, &ptr, 10)) < 0 || (generation > 65535 && number != 0))
{
PDFIO_DEBUG("load_xref: Unable to scan generation number (%u).\n", (unsigned)generation);
goto repair;
_pdfioFileError(pdf, "Bad xref table header '%s'.", line);
return (false);
}
while (isspace(*ptr & 255))
@ -1903,14 +1883,14 @@ load_xref(
if (strncmp(ptr, "obj", 3))
{
PDFIO_DEBUG("load_xref: No 'obj' after object number and generation (saw '%s').\n", ptr);
goto repair;
_pdfioFileError(pdf, "Bad xref table header '%s'.", line);
return (false);
}
if (_pdfioFileSeek(pdf, line_offset + (off_t)(ptr + 3 - line), SEEK_SET) < 0)
{
PDFIO_DEBUG("load_xref: Unable to seek to start of cross-reference object dictionary.\n");
goto repair;
_pdfioFileError(pdf, "Unable to seek to xref object %lu %u.", (unsigned long)number, (unsigned)generation);
return (false);
}
PDFIO_DEBUG("load_xref: Loading object %lu %u.\n", (unsigned long)number, (unsigned)generation);
@ -1925,21 +1905,21 @@ load_xref(
if (!_pdfioValueRead(pdf, obj, &tb, &trailer, 0))
{
PDFIO_DEBUG("load_xref: Unable to read cross-reference object dictionary.\n");
goto repair;
_pdfioFileError(pdf, "Unable to read cross-reference stream dictionary.");
return (false);
}
else if (trailer.type != PDFIO_VALTYPE_DICT)
{
PDFIO_DEBUG("load_xref: Expected dictionary for cross-reference object (type=%d).", trailer.type);
goto repair;
_pdfioFileError(pdf, "Cross-reference stream does not have a dictionary.");
return (false);
}
obj->value = trailer;
if (!_pdfioTokenGet(&tb, line, sizeof(line)) || strcmp(line, "stream"))
{
PDFIO_DEBUG("load_xref: No stream token after dictionary (got '%s').\n", line);
goto repair;
_pdfioFileError(pdf, "Unable to get stream after xref dictionary.");
return (false);
}
PDFIO_DEBUG("load_xref: tb.bufptr=%p, tb.bufend=%p, tb.bufptr[0]=0x%02x, tb.bufptr[0]=0x%02x\n", tb.bufptr, tb.bufend, tb.bufptr[0], tb.bufptr[1]);
@ -1957,8 +1937,8 @@ load_xref(
if ((w_array = pdfioDictGetArray(trailer.value.dict, "W")) == NULL)
{
PDFIO_DEBUG("load_xref: Missing W array in cross-reference objection dictionary.\n");
goto repair;
_pdfioFileError(pdf, "Cross-reference stream does not have required W key.");
return (false);
}
w[0] = (size_t)pdfioArrayGetNumber(w_array, 0);
@ -1968,18 +1948,16 @@ load_xref(
w_2 = w[0];
w_3 = w[0] + w[1];
PDFIO_DEBUG("W=[%u %u %u], w_total=%u\n", (unsigned)w[0], (unsigned)w[1], (unsigned)w[2], (unsigned)w_total);
if (pdfioArrayGetSize(w_array) > 3 || w[1] == 0 || w[2] > 4 || w[0] > sizeof(buffer) || w[1] > sizeof(buffer) || w[2] > sizeof(buffer) || w_total > sizeof(buffer))
if (w[1] == 0 || w[2] > 4 || w[0] > sizeof(buffer) || w[1] > sizeof(buffer) || w[2] > sizeof(buffer) || w_total > sizeof(buffer))
{
PDFIO_DEBUG("load_xref: Bad W array in cross-reference objection dictionary.\n");
goto repair;
_pdfioFileError(pdf, "Cross-reference stream has invalid W key [%u %u %u].", (unsigned)w[0], (unsigned)w[1], (unsigned)w[2]);
return (false);
}
if ((st = pdfioObjOpenStream(obj, true)) == NULL)
{
PDFIO_DEBUG("load_xref: Unable to open cross-reference stream.\n");
goto repair;
_pdfioFileError(pdf, "Unable to open cross-reference stream.");
return (false);
}
for (index_n = 0; index_n < index_count; index_n += 2)
@ -1999,20 +1977,7 @@ load_xref(
{
count --;
#ifdef DEBUG
if (w_total > 5)
PDFIO_DEBUG("load_xref: number=%u %02X%02X%02X%02X%02X...\n", (unsigned)number, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]);
else if (w_total == 5)
PDFIO_DEBUG("load_xref: number=%u %02X%02X%02X%02X%02X\n", (unsigned)number, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]);
else if (w_total == 4)
PDFIO_DEBUG("load_xref: number=%u %02X%02X%02X%02X\n", (unsigned)number, buffer[0], buffer[1], buffer[2], buffer[3]);
else if (w_total == 3)
PDFIO_DEBUG("load_xref: number=%u %02X%02X%02X\n", (unsigned)number, buffer[0], buffer[1], buffer[2]);
else if (w_total == 2)
PDFIO_DEBUG("load_xref: number=%u %02X%02X\n", (unsigned)number, buffer[0], buffer[1]);
else
PDFIO_DEBUG("load_xref: number=%u %02X\n", (unsigned)number, buffer[0]);
#endif // DEBUG
PDFIO_DEBUG("load_xref: number=%u %02X%02X%02X%02X%02X\n", (unsigned)number, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]);
// Check whether this is an object definition...
if (w[0] > 0)
@ -2094,7 +2059,6 @@ load_xref(
else
{
_pdfioFileError(pdf, "Too many object streams.");
pdfioStreamClose(st);
return (false);
}
}
@ -2103,10 +2067,7 @@ load_xref(
{
// Add this object...
if (!add_obj(pdf, (size_t)number, (unsigned short)generation, (off_t)offset))
{
pdfioStreamClose(st);
return (false);
}
}
number ++;
@ -2154,7 +2115,7 @@ load_xref(
// Offset of current line
PDFIO_DEBUG("load_xref: Reading xref table starting at offset %lu\n", (unsigned long)trailer_offset);
while (_pdfioFileGets(pdf, line, sizeof(line), false))
while (_pdfioFileGets(pdf, line, sizeof(line)))
{
PDFIO_DEBUG("load_xref: '%s' at offset %lu\n", line, (unsigned long)trailer_offset);
@ -2179,8 +2140,8 @@ load_xref(
if (sscanf(line, "%jd%jd", &number, &num_objects) != 2)
{
PDFIO_DEBUG("load_xref: Unable to scan START COUNT from line.\n");
goto repair;
_pdfioFileError(pdf, "Malformed xref table section '%s'.", line);
return (false);
}
// Read this group of objects...
@ -2188,45 +2149,41 @@ load_xref(
{
// Read a line from the file and validate it...
if (_pdfioFileRead(pdf, line, 20) != 20)
{
PDFIO_DEBUG("load_xref: Unable to read 20 byte xref record.\n");
goto repair;
}
return (false);
line[20] = '\0';
if (strcmp(line + 18, "\r\n") && strcmp(line + 18, "\r\r") && strcmp(line + 18, " \n") && strcmp(line + 18, " \r"))
if (strcmp(line + 18, "\r\n") && strcmp(line + 18, " \n") && strcmp(line + 18, " \r"))
{
PDFIO_DEBUG("load_xref: Bad end-of-line <%02X%02X>\n", line[18], line[19]);
goto repair;
_pdfioFileError(pdf, "Malformed xref table entry '%s'.", line);
return (false);
}
line[18] = '\0';
// Parse the line
if ((offset = strtoimax(line, &ptr, 10)) < 0)
{
PDFIO_DEBUG("load_xref: Unable to scan offset.\n");
goto repair;
_pdfioFileError(pdf, "Malformed xref table entry '%s'.", line);
return (false);
}
if ((generation = (int)strtol(ptr, &ptr, 10)) < 0 || (generation > 65535 && offset != 0))
{
PDFIO_DEBUG("load_xref: Unable to scan generation (%u).\n", (unsigned)generation);
goto repair;
_pdfioFileError(pdf, "Malformed xref table entry '%s'.", line);
return (false);
}
if (*ptr != ' ')
{
PDFIO_DEBUG("load_xref: Missing space before type.\n");
goto repair;
_pdfioFileError(pdf, "Malformed xref table entry '%s'.", line);
return (false);
}
ptr ++;
if (*ptr != 'f' && *ptr != 'n')
{
PDFIO_DEBUG("load_xref: Bad type '%c'.\n", *ptr);
goto repair;
_pdfioFileError(pdf, "Malformed xref table entry '%s'.", line);
return (false);
}
if (*ptr == 'f')
@ -2245,21 +2202,21 @@ load_xref(
if (strncmp(line, "trailer", 7))
{
PDFIO_DEBUG("load_xref: No trailer after xref table.\n");
goto repair;
_pdfioFileError(pdf, "Missing trailer.");
return (false);
}
_pdfioTokenInit(&tb, pdf, (_pdfio_tconsume_cb_t)_pdfioFileConsume, (_pdfio_tpeek_cb_t)_pdfioFilePeek, pdf);
if (!_pdfioValueRead(pdf, NULL, &tb, &trailer, 0))
{
PDFIO_DEBUG("load_xref: Unable to read trailer dictionary.\n");
goto repair;
_pdfioFileError(pdf, "Unable to read trailer dictionary.");
return (false);
}
else if (trailer.type != PDFIO_VALTYPE_DICT)
{
PDFIO_DEBUG("load_xref: Trailer not a dictionary (type=%d).\n", trailer.type);
goto repair;
_pdfioFileError(pdf, "Trailer is not a dictionary.");
return (false);
}
PDFIO_DEBUG("load_xref: Got trailer dict.\n");
@ -2281,7 +2238,8 @@ load_xref(
}
else
{
goto repair;
_pdfioFileError(pdf, "Bad xref table header '%s'.", line);
return (false);
}
PDFIO_DEBUG("load_xref: Contents of trailer dictionary:\n");
@ -2310,31 +2268,13 @@ load_xref(
if ((pdf->root_obj = pdfioDictGetObj(pdf->trailer_dict, "Root")) == NULL)
{
PDFIO_DEBUG("load_xref: Missing Root object.\n");
goto repair;
_pdfioFileError(pdf, "Missing Root object.");
return (false);
}
PDFIO_DEBUG("load_xref: Root=%p(%lu)\n", pdf->root_obj, (unsigned long)pdf->root_obj->number);
if ((pages_obj = pdfioDictGetObj(pdfioObjGetDict(pdf->root_obj), "Pages")) == NULL)
{
PDFIO_DEBUG("load_xref: Missing Pages object.\n");
goto repair;
}
PDFIO_DEBUG("load_xref: Pages=%p(%lu)\n", pdf->root_obj, (unsigned long)pdf->root_obj->number);
return (load_pages(pdf, pages_obj, 0));
// If we get here the cross-reference table is busted - try repairing if the
// error callback says to proceed...
repair:
if (_pdfioFileError(pdf, "WARNING: Cross-reference is damaged, will attempt to rebuild."))
return (repair_xref(pdf, password_cb, password_data));
else
return (false);
return (load_pages(pdf, pdfioDictGetObj(pdfioObjGetDict(pdf->root_obj), "Pages"), 0));
}
@ -2348,7 +2288,7 @@ repair_xref(
pdfio_password_cb_t password_cb, // I - Password callback or `NULL` for none
void *password_data) // I - Password callback data, if any
{
char line[1024], // Line from file
char line[16384], // Line from file
*ptr; // Pointer into line
off_t line_offset; // Offset in file
intmax_t number; // Object number
@ -2356,22 +2296,16 @@ repair_xref(
size_t i; // Looping var
size_t num_sobjs = 0; // Number of object streams
pdfio_obj_t *sobjs[16384]; // Object streams to load
pdfio_dict_t *backup_trailer = NULL; // Backup trailer dictionary
pdfio_obj_t *pages_obj; // Pages object
// Clear trailer data...
pdf->trailer_dict = NULL;
pdf->root_obj = NULL;
pdf->info_obj = NULL;
pdf->pages_obj = NULL;
pdf->encrypt_obj = NULL;
// Let caller know something is wrong...
_pdfioFileError(pdf, "WARNING: Cross-reference table is damaged, attempting to rebuild.");
// Read from the beginning of the file, looking for objects...
// Read from the beginning of the file, looking for
if ((line_offset = _pdfioFileSeek(pdf, 0, SEEK_SET)) < 0)
return (false);
while (_pdfioFileGets(pdf, line, sizeof(line), true))
while (_pdfioFileGets(pdf, line, sizeof(line)))
{
// See if this is the start of an object...
if (line[0] >= '1' && line[0] <= '9')
@ -2388,75 +2322,43 @@ repair_xref(
pdfio_obj_t *obj; // Object
_pdfio_token_t tb; // Token buffer/stack
PDFIO_DEBUG("repair_xref: OBJECT %ld %d at offset %ld\n", (long)number, generation, (long)line_offset);
PDFIO_DEBUG("OBJECT %ld %d at offset %ld\n", (long)number, generation, (long)line_offset);
if ((obj = pdfioFileFindObj(pdf, (size_t)number)) != NULL)
{
obj->offset = line_offset;
}
else if ((obj = add_obj(pdf, (size_t)number, (unsigned short)generation, line_offset)) == NULL)
if ((obj = add_obj(pdf, (size_t)number, (unsigned short)generation, line_offset)) == NULL)
{
_pdfioFileError(pdf, "Unable to allocate memory for object.");
return (false);
}
if (ptr[3])
{
// Probably the start of the object dictionary, rewind the file so
// we can read it...
_pdfioFileSeek(pdf, line_offset + (ptr - line + 3), SEEK_SET);
}
_pdfioTokenInit(&tb, pdf, (_pdfio_tconsume_cb_t)_pdfioFileConsume, (_pdfio_tpeek_cb_t)_pdfioFilePeek, pdf);
if (!_pdfioValueRead(pdf, obj, &tb, &obj->value, 0))
{
if (!_pdfioFileError(pdf, "WARNING: Unable to read object dictionary/value."))
return (false);
else
continue;
_pdfioFileError(pdf, "Unable to read cross-reference stream dictionary.");
return (false);
}
if (_pdfioTokenGet(&tb, line, sizeof(line)))
if (_pdfioTokenGet(&tb, line, sizeof(line)) && strcmp(line, "stream"))
{
const char *type = pdfioObjGetType(obj);
// Object type
_pdfioTokenFlush(&tb);
obj->stream_offset = _pdfioFileTell(pdf);
if (type && !strcmp(line, "stream"))
{
// Possible object or XRef stream...
obj->stream_offset = _pdfioFileTell(pdf);
if (!strcmp(type, "ObjStm") && num_sobjs < (sizeof(sobjs) / sizeof(sobjs[0])))
{
PDFIO_DEBUG("repair_xref: Object stream...\n");
sobjs[num_sobjs] = obj;
num_sobjs ++;
}
if (!strcmp(type, "XRef") && !pdf->trailer_dict)
{
// Save the trailer dictionary...
PDFIO_DEBUG("repair_xref: XRef stream...\n");
pdf->trailer_dict = pdfioObjGetDict(obj);
pdf->encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt");
pdf->id_array = pdfioDictGetArray(pdf->trailer_dict, "ID");
}
}
else if (type && !strcmp(line, "endobj"))
if (type && !strcmp(type, "ObjStm") && num_sobjs < (sizeof(sobjs) / sizeof(sobjs[0])))
{
// Possible catalog or pages object...
if (!strcmp(type, "Catalog"))
{
PDFIO_DEBUG("repair_xref: Catalog (root) object...\n");
if (!backup_trailer)
backup_trailer = pdfioDictCreate(pdf);
pdfioDictSetObj(backup_trailer, "Root", obj);
}
sobjs[num_sobjs] = obj;
num_sobjs ++;
}
if (type && !strcmp(type, "XRef") && !pdf->trailer_dict)
{
// Save the trailer dictionary...
pdf->trailer_dict = pdfioObjGetDict(obj);
pdf->encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt");
pdf->id_array = pdfioDictGetArray(pdf->trailer_dict, "ID");
}
}
}
}
@ -2467,8 +2369,6 @@ repair_xref(
_pdfio_token_t tb; // Token buffer/stack
_pdfio_value_t trailer; // Trailer
PDFIO_DEBUG("repair_xref: line=\"%s\"\n", line);
if (line[7])
{
// Probably the start of the trailer dictionary, rewind the file so
@ -2476,7 +2376,7 @@ repair_xref(
_pdfioFileSeek(pdf, line_offset + 7, SEEK_SET);
}
PDFIO_DEBUG("repair_xref: TRAILER at offset %ld\n", (long)line_offset);
PDFIO_DEBUG("TRAILER at offset %ld\n", (long)line_offset);
_pdfioTokenInit(&tb, pdf, (_pdfio_tconsume_cb_t)_pdfioFileConsume, (_pdfio_tpeek_cb_t)_pdfioFilePeek, pdf);
if (!_pdfioValueRead(pdf, NULL, &tb, &trailer, 0))
@ -2492,12 +2392,10 @@ repair_xref(
_pdfioTokenFlush(&tb);
if (_pdfioDictGetValue(trailer.value.dict, "Root"))
if (!pdf->trailer_dict)
{
// Save the trailer dictionary and grab the root (catalog) and info
// objects...
PDFIO_DEBUG("repair_xref: Using this trailer dictionary.\n");
pdf->trailer_dict = trailer.value.dict;
pdf->encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt");
pdf->id_array = pdfioDictGetArray(pdf->trailer_dict, "ID");
@ -2508,18 +2406,11 @@ repair_xref(
line_offset = _pdfioFileTell(pdf);
}
PDFIO_DEBUG("repair_xref: Stopped at line_offset=%lu\n", (unsigned long)line_offset);
if (!pdf->trailer_dict && backup_trailer)
pdf->trailer_dict = backup_trailer;
// If the trailer contains an Encrypt key, try unlocking the file...
if (pdf->encrypt_obj && !_pdfioCryptoUnlock(pdf, password_cb, password_data))
return (false);
// Load any stream objects...
PDFIO_DEBUG("repair_xref: Found %lu stream objects.\n", (unsigned long)num_sobjs);
for (i = 0; i < num_sobjs; i ++)
{
if (!load_obj_stream(sobjs[i]))
@ -2538,16 +2429,8 @@ repair_xref(
PDFIO_DEBUG("repair_xref: Root=%p(%lu)\n", pdf->root_obj, (unsigned long)pdf->root_obj->number);
if ((pages_obj = pdfioDictGetObj(pdfioObjGetDict(pdf->root_obj), "Pages")) == NULL)
{
_pdfioFileError(pdf, "Missing Pages object.");
return (false);
}
PDFIO_DEBUG("repair_xref: Pages=%p(%lu)\n", pages_obj, (unsigned long)pages_obj->number);
// Load pages...
return (load_pages(pdf, pages_obj, 0));
return (load_pages(pdf, pdfioDictGetObj(pdfioObjGetDict(pdf->root_obj), "Pages"), 0));
}

View File

@ -141,7 +141,6 @@ pdfioObjCreateStream(
pdfio_obj_t *obj, // I - Object
pdfio_filter_t filter) // I - Type of compression to apply
{
pdfio_stream_t *st; // Stream
pdfio_obj_t *length_obj = NULL; // Length object, if any
@ -195,13 +194,11 @@ pdfioObjCreateStream(
if (!_pdfioFilePuts(obj->pdf, "stream\n"))
return (NULL);
obj->stream_offset = _pdfioFileTell(obj->pdf);
obj->stream_offset = _pdfioFileTell(obj->pdf);
obj->pdf->current_obj = obj;
// Return the new stream...
if ((st = _pdfioStreamCreate(obj, length_obj, 0, filter)) != NULL)
obj->pdf->current_obj = obj;
return (st);
return (_pdfioStreamCreate(obj, length_obj, 0, filter));
}
@ -537,9 +534,6 @@ pdfio_stream_t * // O - Stream or `NULL` on error
pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object
bool decode) // I - Decode/decompress data?
{
pdfio_stream_t *st; // Stream
// Range check input...
if (!obj)
return (NULL);
@ -562,10 +556,9 @@ pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object
return (NULL);
// Open the stream...
if ((st = _pdfioStreamOpen(obj, decode)) != NULL)
obj->pdf->current_obj = obj;
obj->pdf->current_obj = obj;
return (st);
return (_pdfioStreamOpen(obj, decode));
}

View File

@ -385,7 +385,7 @@ extern bool _pdfioFileError(pdfio_file_t *pdf, const char *format, ...) _PDFIO_
extern pdfio_obj_t *_pdfioFileFindMappedObj(pdfio_file_t *pdf, pdfio_file_t *src_pdf, size_t src_number) _PDFIO_INTERNAL;
extern bool _pdfioFileFlush(pdfio_file_t *pdf) _PDFIO_INTERNAL;
extern int _pdfioFileGetChar(pdfio_file_t *pdf) _PDFIO_INTERNAL;
extern bool _pdfioFileGets(pdfio_file_t *pdf, char *buffer, size_t bufsize, bool discard) _PDFIO_INTERNAL;
extern bool _pdfioFileGets(pdfio_file_t *pdf, char *buffer, size_t bufsize) _PDFIO_INTERNAL;
extern ssize_t _pdfioFilePeek(pdfio_file_t *pdf, void *buffer, size_t bytes) _PDFIO_INTERNAL;
extern bool _pdfioFilePrintf(pdfio_file_t *pdf, const char *format, ...) _PDFIO_INTERNAL;
extern bool _pdfioFilePuts(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;

View File

@ -259,7 +259,7 @@ _pdfioStreamCreate(
{
colors = 1;
}
else if (colors < 0 || colors > 32)
else if (colors < 0 || colors > 4)
{
_pdfioFileError(st->pdf, "Unsupported Colors value %d.", colors);
free(st);
@ -532,7 +532,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
{
colors = 1;
}
else if (colors < 0 || colors > 32)
else if (colors < 0 || colors > 4)
{
_pdfioFileError(st->pdf, "Unsupported Colors value %d.", colors);
goto error;

View File

@ -18,22 +18,12 @@ if test $# = 0; then
fi
for file in $(find "$@" -name \*.pdf -print); do
# Don't worry about test files containing MIME garbage...
(head -4 $file | grep -q Content-Type) && continue;
# Or test files containing MacBinary garbage...
(file $file | grep -q MacBinary) && continue;
# Don't worry about test files that Xpdf can't handle...
pdfinfo $file >/dev/null 2>&1 || continue;
# Run testpdfio to test loading the file...
./testpdfio $file >$file.log 2>&1
if test $? = 0; then
# Passed
rm -f $file.log
else
# Failed, preserve log and write filename to stdout...
echo $file
fi
done

View File

@ -1333,7 +1333,7 @@ error_cb(pdfio_file_t *pdf, // I - PDF file
testMessage("%s", message);
// Continue to catch more errors...
return (true);
return (false);
}