mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-15 17:18:05 +02:00
Fixes cairo issue #907. In the xref table, the generation number is limited to 65535 (2^16 - 1) and it's used to identify objects in object streams. When generating a pdf with more than 65536 objects in object streams, the generation number overflows and restarts at 0, which causes invalid references in the xref table.
178 lines
4.6 KiB
C
178 lines
4.6 KiB
C
/*
|
|
* 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)
|