From ec64af8b20ba6284c307f6f1d715e12f4197bf6b Mon Sep 17 00:00:00 2001 From: Michael R Sweet Date: Sun, 13 Apr 2025 14:16:53 -0400 Subject: [PATCH] Add pdfioFileAddOutputIntent API (Issue #104) --- CHANGES.md | 6 ++- pdfio-content.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ pdfio-content.h | 1 + pdfio-file.c | 4 ++ 4 files changed, 106 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3a008ae..7ae6863 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,10 +9,12 @@ v1.6.0 - YYYY-MM-DD (Issue #104) - Added CMYK JPEG support with embedded ICC profiles or using the CGATS001 profile (Issue #104) -- Now add default grayscale, RGB, and CMYK profile resources to pages as needed - (Issue #104) +- Added `pdfioFileAddOutputIntent` function to adding output intent information + to a PDF file (Issue #104) - Added `pdfioFileCreateFontObjFromData` function for embedding fonts in memory (Issue #120) +- Now add default grayscale, RGB, and CMYK profile resources to pages as needed + (Issue #104) v1.5.3 - YYYY-MM-DD diff --git a/pdfio-content.c b/pdfio-content.c index 3360230..da313dd 100644 --- a/pdfio-content.c +++ b/pdfio-content.c @@ -1489,6 +1489,103 @@ pdfioContentTextShowJustified( } +// +// 'pdfioFileAddOutputIntent()' - Add an OutputIntent to a file. +// +// This function adds an OutputIntent dictionary to the PDF file catalog. +// The "subtype" argument specifies the intent subtype and is typically +// "GTS_PDFX" for PDF/X, "GTS_PDFA1" for PDF/A, or "ISO_PDFE1" for PDF/E. +// Passing `NULL` defaults the subtype to "GTS_PDFA1". +// +// The "condition" argument specifies a short name for the output intent, while +// the "info" argument specifies a longer description for the output intent. +// Both can be `NULL` to omit this information. +// +// The "cond_id" argument specifies a unique identifier such as a registration +// ("CGATS001") or color space name ("sRGB"). The "reg_name" argument provides +// a URL for the identifier. +// +// The "profile" argument specifies an ICC profile object for the output +// condition. If `NULL`, the PDF consumer will attempt to look up the correct +// profile using the "cond_id" value. +// +// @since PDFio 1.6@ +// + +void +pdfioFileAddOutputIntent( + pdfio_file_t *pdf, // I - PDF file + const char *subtype, // I - Intent subtype (standard) + const char *condition, // I - Condition name or `NULL` for none + const char *cond_id, // I - Identifier such as registration name or `NULL` for none + const char *reg_name, // I - Registry URL or `NULL` for none + const char *info, // I - Description or `NULL` for none + pdfio_obj_t *profile) // I - ICC profile object or `NULL` for none +{ + pdfio_array_t *output_intents; // OutputIntents array in catalog + pdfio_dict_t *intent; // Current output intent + + + // Range check input... + if (!pdf) + return; + + if (!subtype) + { + _pdfioFileError(pdf, "Output intent subtype cannot be NULL."); + return; + } + + // Get the OutputIntents array... + if ((output_intents = pdfioDictGetArray(pdfioObjGetDict(pdf->info_obj), "OutputIntents")) != NULL) + { + // See if we already have an intent for the given subtype... + size_t i, // Looping var + count; // Number of output intents + + for (i = 0, count = pdfioArrayGetSize(output_intents); i < count; i ++) + { + if ((intent = pdfioArrayGetDict(output_intents, i)) != NULL) + { + const char *csubtype = pdfioDictGetName(intent, "S"); + // Current subtype + + if (csubtype && !strcmp(csubtype, subtype)) + return; + } + } + } + else + { + // Create the OutputIntents array... + if ((output_intents = pdfioArrayCreate(pdf)) == NULL) + return; + + pdfioDictSetArray(pdfioObjGetDict(pdf->info_obj), "OutputIntents", output_intents); + } + + // Create an intent dictionary... + if ((intent = pdfioDictCreate(pdf)) == NULL) + return; + + pdfioDictSetName(intent, "Type", "OutputIntent"); + pdfioDictSetName(intent, "S", pdfioStringCreate(pdf, subtype)); + if (condition) + pdfioDictSetString(intent, "OutputCondition", pdfioStringCreate(pdf, condition)); + if (cond_id) + pdfioDictSetString(intent, "OutputConditionIdentifier", pdfioStringCreate(pdf, cond_id)); + if (reg_name) + pdfioDictSetString(intent, "RegistryName", pdfioStringCreate(pdf, reg_name)); + if (info) + pdfioDictSetString(intent, "Info", pdfioStringCreate(pdf, info)); + if (profile) + pdfioDictSetObj(intent, "DestOutputProfile", profile); + + // Add the dictionary to the output intents... + pdfioArrayAppendDict(output_intents, intent); +} + + // // 'pdfioFileCreateBaseFontObj()' - Create one of the base 14 PDF fonts. // diff --git a/pdfio-content.h b/pdfio-content.h index 4f2ad20..9d2a864 100644 --- a/pdfio-content.h +++ b/pdfio-content.h @@ -128,6 +128,7 @@ extern bool pdfioContentTextShowf(pdfio_stream_t *st, bool unicode, const char extern bool pdfioContentTextShowJustified(pdfio_stream_t *st, bool unicode, size_t num_fragments, const double *offsets, const char * const *fragments) _PDFIO_PUBLIC; // Resource helpers... +extern void pdfioFileAddOutputIntent(pdfio_file_t *pdf, const char *subtype, const char *condition, const char *cond_id, const char *reg_name, const char *info, pdfio_obj_t *profile) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateFontObjFromBase(pdfio_file_t *pdf, const char *name) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateFontObjFromData(pdfio_file_t *pdf, const void *data, size_t datasize, bool unicode) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateFontObjFromFile(pdfio_file_t *pdf, const char *filename, bool unicode) _PDFIO_PUBLIC; diff --git a/pdfio-file.c b/pdfio-file.c index f7516b4..b6c41b4 100644 --- a/pdfio-file.c +++ b/pdfio-file.c @@ -124,6 +124,10 @@ pdfioFileClose(pdfio_file_t *pdf) // I - PDF file { ret = false; + // Add default OutputIntent for PDF/A CMYK printing... + pdfioFileAddOutputIntent(pdf, /*subtype*/"GTS_PDFA1", /*condition*/"CMYK", /*cond_id*/"CGATS001", /*reg_name*/NULL, /*info*/"CMYK Printing", /*profile*/pdf->cgats001_obj); + + // Close and write out the last bits... if (pdfioObjClose(pdf->info_obj) && write_pages(pdf) && pdfioObjClose(pdf->root_obj) && write_trailer(pdf)) ret = _pdfioFileFlush(pdf); }