/* * 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 */ #include "cairo-test.h" #include #include #include #include #include #include #ifdef HAVE_MMAP #include #endif #ifdef HAVE_UNISTD_H #include /* __unix__ */ #endif #if HAVE_SYS_WAIT_H #include #endif #include #include /* 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)