From 71790bb6c40891a3613c5b9e77b8084ef7d47ef1 Mon Sep 17 00:00:00 2001 From: vididvidid Date: Wed, 1 Oct 2025 07:38:54 +0000 Subject: [PATCH] added the unit testcases for the pdfa feature in the testpdfio and removed the custom one --- .gitignore | 1 + examples/create_tagged_pdf | Bin 16992 -> 0 bytes examples/create_tagged_pdf.c | 77 ------------------- my_pdfa_test.c | 140 ----------------------------------- testpdfio.c | 112 +++++++++++++++++++++++++++- 5 files changed, 112 insertions(+), 218 deletions(-) delete mode 100755 examples/create_tagged_pdf delete mode 100644 examples/create_tagged_pdf.c delete mode 100644 my_pdfa_test.c diff --git a/.gitignore b/.gitignore index 27f4c56..a747500 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ /testpdfio-*.pdf /testttf /x64 +/test-pdfa-* diff --git a/examples/create_tagged_pdf b/examples/create_tagged_pdf deleted file mode 100755 index 9614c993e7f27d9b075550fdb0bf64694904eef9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16992 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 e` -#include -#include - -int main(void) -{ - // 1. Basic PDF setup - pdfio_file_t *pdf; - pdfio_rect_t media_box = {0.0, 0.0, 612.0, 792.0}; // US Letter size - - // Create the PDF file - if ((pdf = pdfioFileCreate("tagged_document.pdf", "2.0", &media_box, &media_box, NULL, NULL)) == NULL) - { - puts("Error: Could not create PDF file."); - return (1); - } - - // 2. Build the Structure Tree Root (StructTreeRoot) - // This is the master "table of contents" for all tags. - pdfio_dict_t *struct_tree_root_dict = pdfioDictCreate(pdf); - pdfioDictSetName(struct_tree_root_dict, "Type", "StructTreeRoot"); - - // Create the top-level document element tag: /Document - pdfio_dict_t *doc_elem_dict = pdfioDictCreate(pdf); - pdfioDictSetName(doc_elem_dict, "Type", "StructElem"); - pdfioDictSetName(doc_elem_dict, "S", "Document"); // 'S' is the structure type - - // Create the paragraph element tag: /P - pdfio_dict_t *p_elem_dict = pdfioDictCreate(pdf); - pdfioDictSetName(p_elem_dict, "Type", "StructElem"); - pdfioDictSetName(p_elem_dict, "S", "P"); // 'S' is the structure type (Paragraph) - pdfioDictSetNumber(p_elem_dict, "K", 0); // 'K' is the content, pointing to MCID 0 on the page - - // Link the paragraph as a child of the document element - pdfio_array_t *doc_kids = pdfioArrayCreate(pdf); - pdfioArrayAppendDict(doc_kids, p_elem_dict); - pdfioDictSetArray(doc_elem_dict, "K", doc_kids); - - // Link the document element as a child of the StructTreeRoot - pdfio_array_t *root_kids = pdfioArrayCreate(pdf); - pdfioArrayAppendDict(root_kids, doc_elem_dict); - pdfioDictSetArray(struct_tree_root_dict, "K", root_kids); - - // Create a PDF object for the StructTreeRoot and link it to the main catalog - pdfio_obj_t *struct_tree_root_obj = pdfioFileCreateObj(pdf, struct_tree_root_dict); - pdfioDictSetObj(pdfioFileGetCatalog(pdf), "StructTreeRoot", struct_tree_root_obj); - - // 3. Create a page and its content - pdfio_dict_t *page_dict = pdfioDictCreate(pdf); - pdfio_obj_t *helvetica = pdfioFileCreateFontObjFromBase(pdf, "Helvetica"); - pdfioPageDictAddFont(page_dict, "F1", helvetica); - - pdfio_stream_t *st = pdfioFileCreatePage(pdf, page_dict); - - // 4. Write the tagged content to the page stream - pdfioContentTextBegin(st); - pdfioContentSetTextFont(st, "F1", 24.0); - pdfioContentTextMoveTo(st, 72.0, 700.0); - - // Create a dictionary for the marked content, specifying the ID - pdfio_dict_t *p_mcid_dict = pdfioDictCreate(pdf); - pdfioDictSetNumber(p_mcid_dict, "MCID", 0); // This ID must match the 'K' value in the StructElem - - // Use the functions from pdfio-content.c to wrap the text - pdfioContentBeginMarked(st, "P", p_mcid_dict); // Start tag for Paragraph - pdfioContentTextShow(st, false, "This is a tagged paragraph."); - pdfioContentEndMarked(st); // End tag - - pdfioContentTextEnd(st); - - // 5. Finalize and close - pdfioStreamClose(st); - pdfioFileClose(pdf); - - puts("Successfully created tagged_document.pdf"); - return (0); -} diff --git a/my_pdfa_test.c b/my_pdfa_test.c deleted file mode 100644 index 9fd7ab1..0000000 --- a/my_pdfa_test.c +++ /dev/null @@ -1,140 +0,0 @@ -// File: my_pdfa_test.c (Final version with embedded font) - -#include "pdfio.h" -#include "pdfio-content.h" -#include -#include - -// -// Local functions... -// -static bool my_error_cb(pdfio_file_t *pdf, const char *message, void *data); -static bool create_pdfa_test_file(const char *filename, const char *pdfa_version); -static int test_pdfa(void); - - -// -// 'my_error_cb()' - A simple error callback to print detailed messages. -// -static bool // O - true to continue, false to stop -my_error_cb(pdfio_file_t *pdf, // I - PDF file - const char *message, // I - Error message - void *data) // I - Callback data (unused) -{ - (void)pdf; // Unused - (void)data; // Unused - fprintf(stderr, "PDFio Error: %s\n", message); - return (false); // Stop on any error -} - - -// -// 'create_pdfa_test_file()' - A helper function to generate a simple PDF/A file. -// -static bool // O - true on success, false on error -create_pdfa_test_file( - const char *filename, // I - Name of the PDF file to create - const char *pdfa_version) // I - PDF/A version string (e.g., "PDF/A-1b") -{ - pdfio_file_t *pdf; // Output PDF file - pdfio_rect_t media_box = { 0.0, 0.0, 612.0, 792.0 }; - // Media box for US Letter - pdfio_obj_t *font; // Font object - pdfio_dict_t *page_dict; // Page dictionary - pdfio_stream_t *st; // Page content stream - char text[256]; // Text to write to page - - snprintf(text, sizeof(text), "This is a compliance test for %s.", pdfa_version); - printf(" Creating '%s' for %s compliance check...\n", filename, pdfa_version); - - if ((pdf = pdfioFileCreate(filename, pdfa_version, &media_box, NULL, my_error_cb, NULL)) == NULL) - { - fprintf(stderr, " ERROR: pdfioFileCreate failed for '%s'.\n", filename); - return (false); - } - - // --- THIS IS THE KEY CHANGE --- - // Instead of using a base font, we now load and embed a font from a file. - if ((font = pdfioFileCreateFontObjFromFile(pdf, "testfiles/OpenSans-Regular.ttf", false)) == NULL) - { - fprintf(stderr, " ERROR: Unable to embed font 'testfiles/OpenSans-Regular.ttf'.\n"); - pdfioFileClose(pdf); - return (false); - } - - page_dict = pdfioDictCreate(pdf); - pdfioPageDictAddFont(page_dict, "F1", font); - st = pdfioFileCreatePage(pdf, page_dict); - - pdfioContentSetTextFont(st, "F1", 12.0); - pdfioContentTextBegin(st); - pdfioContentTextMoveTo(st, 72.0, 720.0); - pdfioContentTextShow(st, false, text); - pdfioContentTextEnd(st); - - pdfioStreamClose(st); - pdfioFileClose(pdf); - - printf(" Successfully created '%s'.\n", filename); - return (true); -} - - -// -// 'test_pdfa()' - The main test runner for the PDF/A feature. -// -static int // O - 0 on success, 1 on error -test_pdfa(void) -{ - int status = 0; - pdfio_file_t *fail_pdf; - pdfio_rect_t media_box = { 0.0, 0.0, 612.0, 792.0 }; - - puts("---- Running PDF/A Generation Tests ----\n"); - - if (!create_pdfa_test_file("test-pdfa-1b.pdf", "PDF/A-1b")) status = 1; - if (!create_pdfa_test_file("test-pdfa-2b.pdf", "PDF/A-2b")) status = 1; - if (!create_pdfa_test_file("test-pdfa-2u.pdf", "PDF/A-2u")) status = 1; - if (!create_pdfa_test_file("test-pdfa-3b.pdf", "PDF/A-3b")) status = 1; - if (!create_pdfa_test_file("test-pdfa-3u.pdf", "PDF/A-3u")) status = 1; - if (!create_pdfa_test_file("test-pdfa-4.pdf", "PDF/A-4")) status = 1; - - puts("\n---- Running PDF/A Encryption Block Test ----\n"); - - printf(" Creating PDF/A file to test encryption failure...\n"); - if ((fail_pdf = pdfioFileCreate("test-pdfa-fail.pdf", "PDF/A-1b", &media_box, NULL, my_error_cb, NULL)) == NULL) - { - fprintf(stderr, " ERROR: pdfioFileCreate failed for encryption test.\n"); - return (1); - } - - if (pdfioFileSetPermissions(fail_pdf, PDFIO_PERMISSION_ALL, PDFIO_ENCRYPTION_RC4_128, "owner", "user")) - { - fputs(" ERROR: pdfioFileSetPermissions succeeded but should have FAILED!\n", stderr); - status = 1; - } - else - { - puts(" SUCCESS: Correctly blocked encryption for PDF/A file as expected."); - } - pdfioFileClose(fail_pdf); - - puts("\n-------------------------------------------"); - if (status == 0) - puts("✅ All PDF/A tests passed."); - else - puts("❌ One or more PDF/A tests FAILED."); - puts("-------------------------------------------\n"); - - return (status); -} - - -// -// 'main()' - Main entry for the test program. -// -int // O - Exit status -main(void) -{ - return (test_pdfa()); -} diff --git a/testpdfio.c b/testpdfio.c index 230ab0a..e3bceb3 100644 --- a/testpdfio.c +++ b/testpdfio.c @@ -51,7 +51,8 @@ static int write_jpeg_test(pdfio_file_t *pdf, const char *title, int number, pdf static int write_png_tests(pdfio_file_t *pdf, int number, pdfio_obj_t *font); static int write_text_test(pdfio_file_t *pdf, int first_page, pdfio_obj_t *font, const char *filename); static int write_unit_file(pdfio_file_t *inpdf, const char *outname, pdfio_file_t *outpdf, size_t *num_pages, size_t *first_image); - +static int do_pdfa_tests(void); +static int create_pdfa_test_file(const char *filename, const char *pdfa_version); // // 'main()' - Main entry for test program. @@ -128,6 +129,109 @@ main(int argc, // I - Number of command-line arguments return (ret); } +// +// 'create_pdfa_test_file()' - A helper function to generate a simple PDF/A file. +// +static int // O - 0 on success, 1 on error +create_pdfa_test_file( + const char *filename, // I - Name of the PDF file to create + const char *pdfa_version) // I - PDF/A version string (e.g., "PDF/A-1b") +{ + pdfio_file_t *pdf; // Output PDF file + pdfio_rect_t media_box = { 0.0, 0.0, 612.0, 792.0 }; + // Media box for US Letter + pdfio_obj_t *font; // Font object + pdfio_dict_t *page_dict; // Page dictionary + pdfio_stream_t *st; // Page content stream + char text[256]; // Text to write to page + bool error = false; // Error flag + + snprintf(text, sizeof(text), "This is a compliance test for %s.", pdfa_version); + + testBegin("Create %s file '%s'", pdfa_version, filename); + + if ((pdf = pdfioFileCreate(filename, pdfa_version, &media_box, NULL, (pdfio_error_cb_t)error_cb, &error)) == NULL) + { + testEnd(false); + return (1); + } + + // Embed a font, which is required for PDF/A + if ((font = pdfioFileCreateFontObjFromFile(pdf, "testfiles/OpenSans-Regular.ttf", false)) == NULL) + { + pdfioFileClose(pdf); + testEnd(false); + return (1); + } + + page_dict = pdfioDictCreate(pdf); + pdfioPageDictAddFont(page_dict, "F1", font); + st = pdfioFileCreatePage(pdf, page_dict); + + pdfioContentSetTextFont(st, "F1", 12.0); + pdfioContentTextBegin(st); + pdfioContentTextMoveTo(st, 72.0, 720.0); + pdfioContentTextShow(st, false, text); + pdfioContentTextEnd(st); + + pdfioStreamClose(st); + + if (pdfioFileClose(pdf)) + { + testEnd(true); + return (0); + } + else + { + testEnd(false); + return (1); + } +} + + +// +// 'do_pdfa_tests()' - Run PDF/A generation and compliance tests. +// +static int // O - 0 on success, 1 on error +do_pdfa_tests(void) +{ + int status = 0; // Overall status + pdfio_file_t *fail_pdf; // PDF file for failure test + pdfio_rect_t media_box = { 0.0, 0.0, 612.0, 792.0 }; + // US Letter media box + bool error = false; // Error flag + + // Test creation of various PDF/A standards + if (create_pdfa_test_file("test-pdfa-1b.pdf", "PDF/A-1b")) status = 1; + if (create_pdfa_test_file("test-pdfa-2b.pdf", "PDF/A-2b")) status = 1; + if (create_pdfa_test_file("test-pdfa-2u.pdf", "PDF/A-2u")) status = 1; + if (create_pdfa_test_file("test-pdfa-3b.pdf", "PDF/A-3b")) status = 1; + if (create_pdfa_test_file("test-pdfa-3u.pdf", "PDF/A-3u")) status = 1; + if (create_pdfa_test_file("test-pdfa-4.pdf", "PDF/A-4")) status = 1; + + // Test that encryption is not allowed for PDF/A files + testBegin("Block encryption for PDF/A file"); + if ((fail_pdf = pdfioFileCreate("test-pdfa-fail.pdf", "PDF/A-1b", &media_box, NULL, (pdfio_error_cb_t)error_cb, &error)) == NULL) + { + testEndMessage(false, "pdfioFileCreate failed for encryption test."); + return (1); + } + + if (pdfioFileSetPermissions(fail_pdf, PDFIO_PERMISSION_ALL, PDFIO_ENCRYPTION_RC4_128, "owner", "user")) + { + testEndMessage(false, "pdfioFileSetPermissions succeeded but should have failed."); + status = 1; + } + else + { + // This is the expected outcome + testEnd(true); + } + pdfioFileClose(fail_pdf); + + return (status); +} + // // 'do_crypto_tests()' - Test the various cryptographic functions in PDFio. @@ -1057,6 +1161,8 @@ do_unit_tests(void) if (do_crypto_tests()) return (1); + + // Create a new PDF file... testBegin("pdfioFileCreate(\"testpdfio-out.pdf\", ...)"); if ((outpdf = pdfioFileCreate("testpdfio-out.pdf", NULL, NULL, NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL) @@ -1222,6 +1328,10 @@ do_unit_tests(void) if (read_unit_file(temppdf, num_pages, first_image, false)) return (1); + // Do PDF/A tests... + if (do_pdfa_tests()) + goto fail; + pdfioFileClose(inpdf); return (0);