diff --git a/CHANGES.md b/CHANGES.md index 9a4d5db..d6baa49 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,8 @@ v1.6.0 - YYYY-MM-DD - Added explicit support for warning messages (Issue #118) - Added `pdfioFileCreateFontObjFromData` function for embedding fonts in memory (Issue #120) +- Added `pdfioContentBeginMarked` and `pdfioContentEndMarked` functions for + creating tagged PDF content (Issue #123) - Added `pdfioFileGetLanguage` and `pdfioFileSetLanguage` functions for getting and setting the default/primary language of a PDF file (Issue #124) - Now add default grayscale, RGB, and CMYK profile resources to pages as needed diff --git a/pdfio-content.c b/pdfio-content.c index de7da63..b22509f 100644 --- a/pdfio-content.c +++ b/pdfio-content.c @@ -95,7 +95,10 @@ static void ttf_error_cb(pdfio_file_t *pdf, const char *message); #ifndef HAVE_LIBPNG static unsigned update_png_crc(unsigned crc, const unsigned char *buffer, size_t length); #endif // !HAVE_LIBPNG +static bool write_array(pdfio_stream_t *st, pdfio_array_t *a); +static bool write_dict(pdfio_stream_t *st, pdfio_dict_t *dict); static bool write_string(pdfio_stream_t *st, bool unicode, const char *s, bool *newline); +static bool write_value(pdfio_stream_t *st, _pdfio_value_t *v); // @@ -458,6 +461,43 @@ pdfioArrayCreateColorFromStandard( } +// +// 'pdfioContentBeginMarked()' - Start marked content with an optional dictionary. +// +// This function starts an area of marked content with an optional dictionary. +// It must be paired with a call to the @link pdfioContentEndMarked@ function. +// +// @since PDFio 1.6@ +// + +bool // O - `true` on success, `false` on failure +pdfioContentBeginMarked( + pdfio_stream_t *st, // I - Stream + const char *name, // I - Name of marked content + pdfio_dict_t *dict) // I - Dictionary of parameters or `NULL` if none +{ + if (!st || !name) + return (false); + + if (!pdfioStreamPrintf(st, "%N", name)) + return (false); + + if (dict) + { + // Write dictionary before BDC operator... + if (!write_dict(st, dict)) + return (false); + + return (pdfioStreamPuts(st, "BDC\n")); + } + else + { + // No dictionary so use the BMC operator... + return (pdfioStreamPuts(st, " BMC\n")); + } +} + + // // 'pdfioContentClip()' - Clip output to the current path. // @@ -491,6 +531,23 @@ pdfioContentDrawImage( } +// +// 'pdfioContentEndMarked()' - End marked content. +// +// This function ends an area of marked content that was started using the +// @link pdfioContentBeginMarked@ function. +// +// @since PDFio 1.6@ +// + +bool // O - `true` on success, `false` on failure +pdfioContentEndMarked( + pdfio_stream_t *st) // I - Stream +{ + return (pdfioStreamPuts(st, "EMC\n")); +} + + // // 'pdfioContentFill()' - Fill the current path. // @@ -3784,6 +3841,65 @@ update_png_crc( #endif // !HAVE_LIBPNG +// +// 'write_array()' - Write an array value. +// + +static bool // O - `true` on success, `false` on error +write_array(pdfio_stream_t *st, // I - Stream + pdfio_array_t *a) // I - Array +{ + size_t i; // Looping var + _pdfio_value_t *v; // Current value + + + // Arrays are surrounded by square brackets ([ ... ]) + if (!pdfioStreamPuts(st, "[")) + return (false); + + // Write each value... + for (i = a->num_values, v = a->values; i > 0; i --, v ++) + { + if (!write_value(st, v)) + return (false); + } + + // Closing bracket... + return (pdfioStreamPuts(st, "]")); +} + + +// +// 'write_dict()' - Write a dictionary value. +// + +static bool // O - `true` on success, `false` on error +write_dict(pdfio_stream_t *st, // I - Stream + pdfio_dict_t *dict) // I - Dictionary +{ + size_t i; // Looping var + _pdfio_pair_t *pair; // Current key/value pair + + + // Dictionaries are bounded by "<<" and ">>"... + if (!pdfioStreamPuts(st, "<<")) + return (false); + + // Write all of the key/value pairs... + for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++) + { + if (!pdfioStreamPrintf(st, "%N", pair->key)) + return (false); + + if (!write_value(st, &pair->value)) + return (false); + } + + // Close it up... + return (pdfioStreamPuts(st, ">>")); +} + + // // 'write_string()' - Write a PDF string. // @@ -3890,3 +4006,82 @@ write_string(pdfio_stream_t *st, // I - Stream return (pdfioStreamPuts(st, unicode ? ">" : ")")); } + + +// +// 'write_value()' - Write a PDF value. +// + +static bool // O - `true` on success, `false` on error +write_value(pdfio_stream_t *st, // I - Stream + _pdfio_value_t *v) // I - Value +{ + switch (v->type) + { + default : + return (false); + + case PDFIO_VALTYPE_ARRAY : + return (write_array(st, v->value.array)); + + case PDFIO_VALTYPE_BINARY : + { + size_t databytes; // Bytes to write + uint8_t *dataptr; // Pointer into data + + if (!pdfioStreamPuts(st, "<")) + return (false); + + for (dataptr = v->value.binary.data, databytes = v->value.binary.datalen; databytes > 1; databytes -= 2, dataptr += 2) + { + if (!pdfioStreamPrintf(st, "%02X%02X", dataptr[0], dataptr[1])) + return (false); + } + + if (databytes > 0 && !pdfioStreamPrintf(st, "%02X", dataptr[0])) + return (false); + + return (pdfioStreamPuts(st, ">")); + } + + case PDFIO_VALTYPE_BOOLEAN : + if (v->value.boolean) + return (pdfioStreamPuts(st, " true")); + else + return (pdfioStreamPuts(st, " false")); + + case PDFIO_VALTYPE_DATE : + { + struct tm date; // Date values + char datestr[32]; // Formatted date value + +#ifdef _WIN32 + gmtime_s(&date, &v->value.date); +#else + gmtime_r(&v->value.date, &date); +#endif // _WIN32 + + snprintf(datestr, sizeof(datestr), "D:%04d%02d%02d%02d%02d%02dZ", date.tm_year + 1900, date.tm_mon + 1, date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec); + + return (pdfioStreamPrintf(st, "%S", datestr)); + } + + case PDFIO_VALTYPE_DICT : + return (write_dict(st, v->value.dict)); + + case PDFIO_VALTYPE_INDIRECT : + return (pdfioStreamPrintf(st, " %lu %u R", (unsigned long)v->value.indirect.number, v->value.indirect.generation)); + + case PDFIO_VALTYPE_NAME : + return (pdfioStreamPrintf(st, "%N", v->value.name)); + + case PDFIO_VALTYPE_NULL : + return (pdfioStreamPuts(st, " null")); + + case PDFIO_VALTYPE_NUMBER : + return (pdfioStreamPrintf(st, " %.6f", v->value.number)); + + case PDFIO_VALTYPE_STRING : + return (pdfioStreamPrintf(st, "%S", v->value.string)); + } +} diff --git a/pdfio-content.h b/pdfio-content.h index 9d2a864..0c325c5 100644 --- a/pdfio-content.h +++ b/pdfio-content.h @@ -69,8 +69,10 @@ extern pdfio_array_t *pdfioArrayCreateColorFromPrimaries(pdfio_file_t *pdf, size extern pdfio_array_t *pdfioArrayCreateColorFromStandard(pdfio_file_t *pdf, size_t num_colors, pdfio_cs_t cs); // PDF content drawing functions... +extern bool pdfioContentBeginMarked(pdfio_stream_t *st, const char *name, pdfio_dict_t *dict) _PDFIO_PUBLIC; extern bool pdfioContentClip(pdfio_stream_t *st, bool even_odd) _PDFIO_PUBLIC; extern bool pdfioContentDrawImage(pdfio_stream_t *st, const char *name, double x, double y, double w, double h) _PDFIO_PUBLIC; +extern bool pdfioContentEndMarked(pdfio_stream_t *st) _PDFIO_PUBLIC; extern bool pdfioContentFill(pdfio_stream_t *st, bool even_odd) _PDFIO_PUBLIC; extern bool pdfioContentFillAndStroke(pdfio_stream_t *st, bool even_odd) _PDFIO_PUBLIC; extern bool pdfioContentMatrixConcat(pdfio_stream_t *st, pdfio_matrix_t m) _PDFIO_PUBLIC;