diff --git a/my_pdfa_test.c b/my_pdfa_test.c new file mode 100644 index 0000000..9fd7ab1 --- /dev/null +++ b/my_pdfa_test.c @@ -0,0 +1,140 @@ +// 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/my_test_executable b/my_test_executable new file mode 100755 index 0000000..dd60ba7 Binary files /dev/null and b/my_test_executable differ diff --git a/pdfio-file.c b/pdfio-file.c index a44fa17..2516962 100644 --- a/pdfio-file.c +++ b/pdfio-file.c @@ -1114,8 +1114,9 @@ pdfioFileOpen( char message[8192]; // Message string temp.filename = (char *)filename; - snprintf(message, sizeof(message), "Unable to allocate memory for PDF file - %s", strerror(errno)); + snprintf(message, sizeof(message), "Unable to allocate memory for PDF file: %s", strerror(errno)); (error_cb)(&temp, message, error_cbdata); + return (NULL); } @@ -1328,28 +1329,30 @@ pdfioFileSetPermissions( if (!pdf) return (false); + // 1. First, check if this is a PDF/A file trying to use encryption. This check must be first. if (pdf->pdfa != _PDFIO_PDFA_NONE && encryption != PDFIO_ENCRYPTION_NONE) { _pdfioFileError(pdf, "Encryption is not allowed for PDF/A files."); return (false); } + // 2. If no encryption is being applied anyway, we are done. + if (encryption == PDFIO_ENCRYPTION_NONE) + return (true); + // 3. Check if it's too late in the file creation process to set permissions. if (pdf->num_objs > 3) // First three objects are pages, info, and root { _pdfioFileError(pdf, "You must call pdfioFileSetPermissions before adding any objects."); return (false); } - if (encryption == PDFIO_ENCRYPTION_NONE) - return (true); - + // 4. If all checks pass, proceed with locking the document. pdf->encrypt_metadata = true; return (_pdfioCryptoLock(pdf, permissions, encryption, owner_password, user_password)); } - // // 'pdfioFileSetSubject()' - Set the subject for a PDF file. // @@ -1535,10 +1538,9 @@ create_common( if (!error_cb) { error_cb = _pdfioFileDefaultError; - error_cbdata = NULL;i - (error_cb)(&temp,message,error_cbdata) + error_cbdata = NULL; - return (NULL) + return (NULL); } // Allocate a PDF file structure... @@ -1602,7 +1604,7 @@ create_common( else { actual_version = version; - pdf->pdfa = _PDFIO_PDFA_NONe; + pdf->pdfa = _PDFIO_PDFA_NONE; } pdf->version = strdup(actual_version); @@ -1638,14 +1640,19 @@ create_common( // Write a standard PDF header... if (!strncmp(version, "PCLm-", 5)) { + // PCLm has a special header format if (!_pdfioFilePrintf(pdf, "%%PDF-1.4\n%%%s\n", version)) goto error; } - else if (!_pdfioFilePrintf(pdf, "%%PDF-%s\n%%\342\343\317\323\n", version)) + else { - goto error; + // For all other PDFs, including all PDF/A versions, write the header + // with the binary comment. + if (!_pdfioFilePrintf(pdf, "%%PDF-%s\n%%\342\343\317\323\n", pdf->version)) + goto error; } + // Create the pages object... if ((dict = pdfioDictCreate(pdf)) == NULL) goto error; diff --git a/test-pdfa-1b.pdf b/test-pdfa-1b.pdf new file mode 100644 index 0000000..dfcb4a3 Binary files /dev/null and b/test-pdfa-1b.pdf differ diff --git a/test-pdfa-2b.pdf b/test-pdfa-2b.pdf new file mode 100644 index 0000000..268c5f8 Binary files /dev/null and b/test-pdfa-2b.pdf differ diff --git a/test-pdfa-2u.pdf b/test-pdfa-2u.pdf new file mode 100644 index 0000000..6655ad5 Binary files /dev/null and b/test-pdfa-2u.pdf differ diff --git a/test-pdfa-3b.pdf b/test-pdfa-3b.pdf new file mode 100644 index 0000000..e8a1160 Binary files /dev/null and b/test-pdfa-3b.pdf differ diff --git a/test-pdfa-3u.pdf b/test-pdfa-3u.pdf new file mode 100644 index 0000000..61e83d1 Binary files /dev/null and b/test-pdfa-3u.pdf differ diff --git a/test-pdfa-4.pdf b/test-pdfa-4.pdf new file mode 100644 index 0000000..821b4b5 Binary files /dev/null and b/test-pdfa-4.pdf differ diff --git a/test-pdfa-fail.pdf b/test-pdfa-fail.pdf new file mode 100644 index 0000000..39656d2 --- /dev/null +++ b/test-pdfa-fail.pdf @@ -0,0 +1,49 @@ +%PDF-1.4 +% +4 0 obj +<> +stream + + + + + 2025-09-30T06:11:15Z + 2025-09-30T06:11:15Z + + + pdfio/1.6.0 + + + application/pdf + + + 1 + B + + + + + +endstream +endobj +2 0 obj +<> +endobj +1 0 obj +<> +endobj +3 0 obj +<>]/Pages 1 0 R/Type/Catalog>> +endobj +xref +0 5 +0000000000 65535 f +0000001130 00000 n +0000001056 00000 n +0000001176 00000 n +0000000015 00000 n +trailer +<<7498FA525DF95B5E7A90092930B04369>]/Info 2 0 R/Root 3 0 R/Size 5>> +startxref +1364 +%%EOF diff --git a/testcompilance b/testcompilance new file mode 100755 index 0000000..26a1d4d Binary files /dev/null and b/testcompilance differ diff --git a/testpdfacompilance b/testpdfacompilance new file mode 100755 index 0000000..26a1d4d Binary files /dev/null and b/testpdfacompilance differ diff --git a/testpdfacompilance.c b/testpdfacompilance.c deleted file mode 100644 index 88bd449..0000000 --- a/testpdfacompilance.c +++ /dev/null @@ -1,127 +0,0 @@ -// -// Test program from pdf-a -// -// This file is specifically designed to test the PDF/A creation feature -// - -#include "pdfio.h" -#include "pdfio-content.h" -#include -#include - -// Local Function - -static bool create_pdfa_test_file(const char *filename, const char *pdfa_version); -static int test_pdfa(void); - -// 'create_pdfa_test_file()' -> A helper function to generate a simple -// PDF/A file - - -static bool // 0 - 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; // Ouput 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 - - // Let the user know what we're doing - snprintf(text, sizeof(text), "This is a compliance test for %s.", pdfa_version); - printf(" Creating '%s' for %s compilance check ... \n", filename, pdfa_version); - - // Create the PDF/A file using the specified version string - if ((pdf = pdfioFileCreate(filename, pdfa_version, &media_box, NULL, NULL, NULL)) == NULL) - { - fprintf(stderr, " ERROR: Unable to create '%s'.\n",filename); - return (false); - } - - // Add some basic content to make it a valid, non-empty PDF - font = pdfioFileCreateFontObjFromBase(pdf,"Helvetica"); - 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); - - // Close the stream and the file to finalize and save it - 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 // 0 - 0 on success, 1 on error -test_pdfa(void) -{ - int status = 0; // Overall test status - pdfio_file_t *fail_pdf; // PDF for failure test - pdfio_rect_t media_box = {0.0, 0.0, 612.0, 792.0 }; - - - puts (" ----- Running PDF/A Generation Tests --- \n"); - - // --- Positive Test Cases: Generate one file for each conformance level --- - 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; - - // --- Navigate test case: Ensure encryption is blocked -- - 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,NULL,NULL)) == NULL) - { - fputs(" ERROR: Unable to create temporary file for encryption test.\n", stderr); - return (1); - } - - // This call MUST fail because encryption is not allowed in PDF/A - if (pdfioFileSetPermissions(fail_pdf, PDFIO_PERMISSION_ALL, PDFIO_ENCRYPTION_RC4_128, "owner", "user")) - { - fputs(" ERROR: pdfioFileSetPermission succeeded but should have FAILED!\n",stderr); - status = 1; // Mark the test suite as failed - } - else - { - puts(" SUCCESS: COrrectly blocked encryption for PDF/A file as expected."); - } - pdfioFileClose(fail_pdf); - - //--- Final Summary---- - puts("\n-------------------------"); - if (status == 0) - puts(" All PDF/A test passed."); - else - puts(" One or more PDF/A tests FAILED."); - puts("\n--------------------------------\n"); - - return (status); -} - - -// 'main()' - -int // 0 - Exit status -main(void) -{ - return (test_pdfa()); -} - - -