mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-15 16:08:05 +02:00
Merge branch 'issue907' into 'master'
Fix invalid pdf generation with too many objects in object stream See merge request cairo/cairo!642
This commit is contained in:
commit
b13698ff65
3 changed files with 231 additions and 38 deletions
|
|
@ -2310,44 +2310,6 @@ _cairo_pdf_surface_open_object_stream (cairo_pdf_surface_t *surface)
|
|||
return _cairo_output_stream_get_status (surface->object_stream.stream);
|
||||
}
|
||||
|
||||
cairo_int_status_t
|
||||
_cairo_pdf_surface_object_begin (cairo_pdf_surface_t *surface,
|
||||
cairo_pdf_resource_t resource)
|
||||
{
|
||||
cairo_xref_stream_object_t xref_obj;
|
||||
cairo_pdf_object_t *object;
|
||||
cairo_int_status_t status;
|
||||
|
||||
if (surface->object_stream.active) {
|
||||
xref_obj.resource = resource;
|
||||
xref_obj.offset = _cairo_output_stream_get_position (surface->object_stream.stream);
|
||||
status = _cairo_array_append (&surface->object_stream.objects, &xref_obj);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
object = _cairo_array_index (&surface->objects, resource.id - 1);
|
||||
object->type = PDF_OBJECT_COMPRESSED;
|
||||
object->u.compressed_obj.xref_stream = surface->object_stream.resource;
|
||||
object->u.compressed_obj.index = _cairo_array_num_elements (&surface->object_stream.objects) - 1;
|
||||
|
||||
} else {
|
||||
_cairo_pdf_surface_update_object (surface, resource);
|
||||
_cairo_output_stream_printf (surface->output,
|
||||
"%d 0 obj\n",
|
||||
resource.id);
|
||||
}
|
||||
return CAIRO_INT_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
_cairo_pdf_surface_object_end (cairo_pdf_surface_t *surface)
|
||||
{
|
||||
if (!surface->object_stream.active) {
|
||||
_cairo_output_stream_printf (surface->output,
|
||||
"endobj\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
_cairo_xref_stream_object_compare (const void *a,
|
||||
const void *b)
|
||||
|
|
@ -2467,6 +2429,58 @@ _cairo_pdf_surface_close_object_stream (cairo_pdf_surface_t *surface)
|
|||
return _cairo_output_stream_get_status (surface->output);
|
||||
}
|
||||
|
||||
cairo_int_status_t
|
||||
_cairo_pdf_surface_object_begin (cairo_pdf_surface_t *surface,
|
||||
cairo_pdf_resource_t resource)
|
||||
{
|
||||
cairo_xref_stream_object_t xref_obj;
|
||||
cairo_pdf_object_t *object;
|
||||
cairo_int_status_t status;
|
||||
int num_objects;
|
||||
|
||||
if (surface->object_stream.active) {
|
||||
num_objects = _cairo_array_num_elements (&surface->object_stream.objects);
|
||||
if (unlikely (num_objects == 65536)) {
|
||||
// In the XRef table, the generation number is stored as a 16-bit integer.
|
||||
// Therefore, we can only have 65536 objects in a single object stream.
|
||||
// Close the current object stream and open a new one.
|
||||
status = _cairo_pdf_surface_close_object_stream(surface);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
status = _cairo_pdf_surface_open_object_stream(surface);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
}
|
||||
|
||||
xref_obj.resource = resource;
|
||||
xref_obj.offset = _cairo_output_stream_get_position (surface->object_stream.stream);
|
||||
status = _cairo_array_append (&surface->object_stream.objects, &xref_obj);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
object = _cairo_array_index (&surface->objects, resource.id - 1);
|
||||
object->type = PDF_OBJECT_COMPRESSED;
|
||||
object->u.compressed_obj.xref_stream = surface->object_stream.resource;
|
||||
object->u.compressed_obj.index = _cairo_array_num_elements (&surface->object_stream.objects) - 1;
|
||||
|
||||
} else {
|
||||
_cairo_pdf_surface_update_object (surface, resource);
|
||||
_cairo_output_stream_printf (surface->output,
|
||||
"%d 0 obj\n",
|
||||
resource.id);
|
||||
}
|
||||
return CAIRO_INT_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
_cairo_pdf_surface_object_end (cairo_pdf_surface_t *surface)
|
||||
{
|
||||
if (!surface->object_stream.active) {
|
||||
_cairo_output_stream_printf (surface->output,
|
||||
"endobj\n");
|
||||
}
|
||||
}
|
||||
|
||||
static cairo_int_status_t
|
||||
_cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface,
|
||||
const cairo_box_double_t *bbox,
|
||||
|
|
|
|||
|
|
@ -452,6 +452,7 @@ test_quartz_sources = [
|
|||
|
||||
test_pdf_sources = [
|
||||
'pdf-features.c',
|
||||
'pdf-large-objstm.c',
|
||||
'pdf-mime-data.c',
|
||||
'pdf-operators-text.c',
|
||||
'pdf-surface-source.c',
|
||||
|
|
|
|||
178
test/pdf-large-objstm.c
Normal file
178
test/pdf-large-objstm.c
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Copyright © 2023 Adrian Johnson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* Author: Adrian Johnson <ajohnson@redneon.com>
|
||||
*/
|
||||
|
||||
#include "cairo-test.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h> /* __unix__ */
|
||||
#endif
|
||||
#if HAVE_SYS_WAIT_H
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#include <cairo.h>
|
||||
#include <cairo-pdf.h>
|
||||
|
||||
/* Test PDF with a large number of objects to ensure that
|
||||
* object streams are correctly handled.
|
||||
*/
|
||||
|
||||
#define BASENAME "pdf-large-objstm"
|
||||
|
||||
static void
|
||||
draw_link(cairo_t *cr)
|
||||
{
|
||||
cairo_tag_begin(cr, CAIRO_TAG_LINK, "uri='http://www.mozilla.org/'");
|
||||
cairo_move_to (cr, 0, 0);
|
||||
cairo_show_text (cr, "a");
|
||||
cairo_tag_end(cr, CAIRO_TAG_LINK);
|
||||
}
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
static cairo_test_status_t
|
||||
check_contains_string(cairo_test_context_t *ctx, const void *hay, size_t size, const char *needle)
|
||||
{
|
||||
if (memmem(hay, size, needle, strlen(needle)))
|
||||
return CAIRO_TEST_SUCCESS;
|
||||
|
||||
cairo_test_log (ctx, "Failed to find expected string in generated PDF: %s\n", needle);
|
||||
return CAIRO_TEST_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static cairo_test_status_t
|
||||
check_created_pdf(cairo_test_context_t *ctx, const char* filename)
|
||||
{
|
||||
cairo_test_status_t result = CAIRO_TEST_SUCCESS;
|
||||
int fd;
|
||||
struct stat st;
|
||||
#ifdef HAVE_MMAP
|
||||
void *contents;
|
||||
#endif
|
||||
|
||||
fd = open(filename, O_RDONLY, 0);
|
||||
if (fd < 0) {
|
||||
cairo_test_log (ctx, "Failed to open generated PDF file %s: %s\n", filename, strerror(errno));
|
||||
return CAIRO_TEST_FAILURE;
|
||||
}
|
||||
|
||||
if (fstat(fd, &st) == -1)
|
||||
{
|
||||
cairo_test_log (ctx, "Failed to stat generated PDF file %s: %s\n", filename, strerror(errno));
|
||||
close(fd);
|
||||
return CAIRO_TEST_FAILURE;
|
||||
}
|
||||
|
||||
if (st.st_size == 0)
|
||||
{
|
||||
cairo_test_log (ctx, "Generated PDF file %s is empty\n", filename);
|
||||
close(fd);
|
||||
return CAIRO_TEST_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
|
||||
printf("Checking generated PDF file %s\n", filename);
|
||||
|
||||
contents = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (contents == NULL)
|
||||
{
|
||||
cairo_test_log (ctx, "Failed to mmap generated PDF file %s: %s\n", filename, strerror(errno));
|
||||
close(fd);
|
||||
return CAIRO_TEST_FAILURE;
|
||||
}
|
||||
|
||||
/* check metadata */
|
||||
result |= check_contains_string(ctx, contents, st.st_size, "/N 65536");
|
||||
result |= check_contains_string(ctx, contents, st.st_size, "/N 14470");
|
||||
|
||||
munmap(contents, st.st_size);
|
||||
#endif
|
||||
|
||||
close(fd);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static cairo_test_status_t
|
||||
create_pdf (cairo_test_context_t *ctx)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
cairo_test_status_t result;
|
||||
char *filename;
|
||||
const char *path = cairo_test_mkdir (CAIRO_TEST_OUTPUT_DIR) ? CAIRO_TEST_OUTPUT_DIR : ".";
|
||||
|
||||
xasprintf (&filename, "%s/%s.pdf", path, BASENAME);
|
||||
surface = cairo_pdf_surface_create (filename, 100, 100);
|
||||
cr = cairo_create(surface);
|
||||
|
||||
for (int i = 0; i < 40000; i++) {
|
||||
draw_link(cr);
|
||||
}
|
||||
|
||||
cairo_show_page (cr);
|
||||
|
||||
cairo_surface_destroy (surface);
|
||||
cairo_destroy (cr);
|
||||
|
||||
result = check_created_pdf(ctx, filename);
|
||||
|
||||
free (filename);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static cairo_test_status_t
|
||||
preamble (cairo_test_context_t *ctx)
|
||||
{
|
||||
cairo_test_status_t result;
|
||||
|
||||
if (! cairo_test_is_target_enabled (ctx, "pdf"))
|
||||
return CAIRO_TEST_UNTESTED;
|
||||
|
||||
result = create_pdf (ctx);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CAIRO_TEST (pdf_large_objstm,
|
||||
"Test with a lot of PDF objects",
|
||||
"source", /* keywords */
|
||||
NULL, /* requirements */
|
||||
100, 100,
|
||||
preamble, NULL)
|
||||
Loading…
Add table
Reference in a new issue