From 6de6b88e859dbd5a2aff9fd25129092f9e1df375 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Sat, 1 Jun 2024 13:33:45 +0530 Subject: [PATCH 01/64] pdfio-tools Header file(excluding the _cfPDFToPDFMakePage function) --- cupsfilters/pdftopdf/pdfio-tools-private.h | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 cupsfilters/pdftopdf/pdfio-tools-private.h diff --git a/cupsfilters/pdftopdf/pdfio-tools-private.h b/cupsfilters/pdftopdf/pdfio-tools-private.h new file mode 100644 index 00000000..ac132dac --- /dev/null +++ b/cupsfilters/pdftopdf/pdfio-tools-private.h @@ -0,0 +1,27 @@ +// +// +// +// +#include + +pdfio_rect_t _cfPDFToPDFGetMediaBox(pdfio_obj_t page); +pdfio_rect_t _cfPDFToPDFGetCropBox(pdfio_obj_t page); +pdfio_rect_t _cfPDFToPDFGetBleedBox(pdfio_obj_t page); +pdfio_rect_t _cfPDFToPDFGetTrimBox(pdfio_obj_t page); +pdfio_rect_t _cfPDFToPDFGetArtBox(pdfio_obj_t page); + +/* +pdfio_obj_t _cfPDFToPDFMakePage(pdfio_file_t &pdf, const std::map Date: Sat, 1 Jun 2024 13:34:25 +0530 Subject: [PATCH 02/64] pdfio-tools function defination file(excluding the _cfPDFToPDFMakePage function) --- cupsfilters/pdftopdf/pdfio-tools.c | 79 ++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 cupsfilters/pdftopdf/pdfio-tools.c diff --git a/cupsfilters/pdftopdf/pdfio-tools.c b/cupsfilters/pdftopdf/pdfio-tools.c new file mode 100644 index 00000000..5d148dcd --- /dev/null +++ b/cupsfilters/pdftopdf/pdfio-tools.c @@ -0,0 +1,79 @@ +#include "pdfio-tools-private.h" + +pdfio_rect_t +_cfPDFToPDFGetMediaBox(pdfio_obj_t *page) // {{{ +{ + pdfio_rect_t mediaBox; + pdfio_dict_t *page_dict = pdfioObjGetDict(page); + pdfioDictGetRect(page_dict, "MediaBox", &mediaBox); + return (mediaBox); +} +// }}} + +pdfio_rect_t +_cfPDFToPDFGetCropBox(pdfio_obj_t *page) // {{{ +{ + pdfio_rect_t cropBox; + pdfio_dict_t *page_dict = pdfioObjGetDict(page); + if (!pdfioDictGetRect(page_dict, "CropBox", &cropBox)) + return _cfPDFToPDFGetMediaBox(page); + return cropBox; +} +// }}} + +pdfio_rect_t +_cfPDFToPDFGetBleedBox(pdfio_obj_t *page) // {{{ +{ + pdfio_rect_t bleedBox; + pdfio_dict_t *page_dict = pdfioObjGetDict(page); + if (!pdfioDictGetRect(page_dict, "BleedBox", &bleedBox)) + return _cfPDFToPDFGetCropBox(page); + return bleedBox; +} +// }}} + +pdfio_rect_t +_cfPDFToPDFGetTrimBox(pdfio_obj_t *page) // {{{ +{ + pdfio_rect_t trimBox; + pdfio_dict_t *page_dict = pdfioObjGetDict(page); + if (!pdfioDictGetRect(page_dict, "TrimBox", &trimBox)) + return _cfPDFToPDFGetCropBox(page); + return trimBox; + +} +// }}} + +pdfio_rect_t +_cfPDFToPDFGetArtBox(pdfio_obj_t *page) // {{{ +{ + pdfio_rect_t artBox; + pdfio_dict_t *page_dict = pdfioObjGetDict(page); + if (!pdfioDictGetRect(page_dict, "ArtBox", &artBox)) + return _cfPDFToPDFGetCropBox(page); + return artBox; +} +// }}} + +/* +pdfio_obj_t +_cfPDFToPDFMakePage() +{ +} +*/ + +pdfio_rect_t +_cfPDFToPDFMakeBox(double x1, + double y1, + double x2, + double y2) // {{{ +{ + pdfio_rect_t ret; + ret.x1 = x1; + ret.y1 = y1; + ret.x2 = x2; + ret.y2 = y2; + + return ret; +} +// }}} From 7878b557554b1d96e80ff727432d3d0d21cfe727 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Sat, 1 Jun 2024 13:35:08 +0530 Subject: [PATCH 03/64] testing of the functions using any pdf File --- cupsfilters/pdftopdf/pdfio-tools-test.c | 135 ++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 cupsfilters/pdftopdf/pdfio-tools-test.c diff --git a/cupsfilters/pdftopdf/pdfio-tools-test.c b/cupsfilters/pdftopdf/pdfio-tools-test.c new file mode 100644 index 00000000..b952056f --- /dev/null +++ b/cupsfilters/pdftopdf/pdfio-tools-test.c @@ -0,0 +1,135 @@ +#include +#include + +pdfio_rect_t +_cfPDFToPDFGetMediaBox(pdfio_obj_t *page) +{ + pdfio_rect_t mediaBox; + pdfio_dict_t *page_dict = pdfioObjGetDict(page); + pdfioDictGetRect(page_dict, "MediaBox", &mediaBox); + return (mediaBox); +} + +pdfio_rect_t +_cfPDFToPDFGetCropBox(pdfio_obj_t *page) +{ + pdfio_rect_t cropBox; + pdfio_dict_t *page_dict = pdfioObjGetDict(page); + if (!pdfioDictGetRect(page_dict, "CropBox", &cropBox)) { + return _cfPDFToPDFGetMediaBox(page); + } + return cropBox; +} + +pdfio_rect_t +_cfPDFToPDFGetBleedBox(pdfio_obj_t *page) +{ + pdfio_rect_t bleedBox; + pdfio_dict_t *page_dict = pdfioObjGetDict(page); + if (!pdfioDictGetRect(page_dict, "BleedBox", &bleedBox)) { + return _cfPDFToPDFGetCropBox(page); + } + return bleedBox; +} + +pdfio_rect_t +_cfPDFToPDFGetTrimBox(pdfio_obj_t *page) +{ + pdfio_rect_t trimBox; + pdfio_dict_t *page_dict = pdfioObjGetDict(page); + if (!pdfioDictGetRect(page_dict, "TrimBox", &trimBox)) { + return _cfPDFToPDFGetCropBox(page); + } + return trimBox; +} + +pdfio_rect_t +_cfPDFToPDFGetArtBox(pdfio_obj_t *page) +{ + pdfio_rect_t artBox; + pdfio_dict_t *page_dict = pdfioObjGetDict(page); + if (!pdfioDictGetRect(page_dict, "ArtBox", &artBox)) { + return _cfPDFToPDFGetCropBox(page); + } + return artBox; +} + + +/* +pdfio_obj_t +_cfPDFToPDFMakePage() +{ +} +*/ +pdfio_rect_t +_cfPDFToPDFMakeBox(double x1, double y1, double x2, double y2) +{ + pdfio_rect_t ret; + ret.x1 = x1; + ret.y1 = y1; + ret.x2 = x2; + ret.y2 = y2; + + return ret; +} + +void printBox(pdfio_rect_t *box) { + printf("[%.5f, %.5f, %.5f, %.5f]\n", box->x1, box->y1, box->x2, box->y2); +} + +int main(){ + // Open the PDF file + pdfio_file_t *pdf = pdfioFileOpen("test.pdf", NULL, NULL, NULL, NULL); + if (!pdf) { + fprintf(stderr, "Error opening PDF file.\n"); + return 1; + } + + // Get the first page + pdfio_obj_t *page = pdfioFileGetPage(pdf, 0); + if (!page) { + fprintf(stderr, "Error getting page from PDF.\n"); + pdfioFileClose(pdf); + return 1; + } + + // Test _cfPDFToPDFGetMediaBox function + printf("Testing MediaBox retrieval:\n"); + pdfio_rect_t mediaBox = _cfPDFToPDFGetMediaBox(page); + printf("MediaBox: "); + printBox(&mediaBox); + + // Test _cfPDFToPDFGetCropBox function + printf("Testing CropBox retrieval:\n"); + pdfio_rect_t cropBox = _cfPDFToPDFGetCropBox(page); + printf("CropBox: "); + printBox(&cropBox); + + // Test _cfPDFToPDFGetBleedBox function + printf("Testing BleedBox retrieval:\n"); + pdfio_rect_t bleedBox = _cfPDFToPDFGetBleedBox(page); + printf("BleedBox: "); + printBox(&bleedBox); + + // Test _cfPDFToPDFGetTrimBox function + printf("Testing TrimBox retrieval:\n"); + pdfio_rect_t trimBox = _cfPDFToPDFGetTrimBox(page); + printf("TrimBox: "); + printBox(&trimBox); + + // Test _cfPDFToPDFGetArtBox function + printf("Testing ArtBox retrieval:\n"); + pdfio_rect_t artBox = _cfPDFToPDFGetArtBox(page); + printf("ArtBox: "); + printBox(&artBox); + + // Test _cfPDFToPDFMakeBox function + printf("Testing box creation:\n"); + pdfio_rect_t customBox = _cfPDFToPDFMakeBox(30, 30, 570, 770); + printf("CustomBox: "); + printBox(&customBox); + + // Clean up + pdfioFileClose(pdf); + return 0; +} From 6dfc0c2ac707cb60176984a62359d7b48ec03d70 Mon Sep 17 00:00:00 2001 From: ThePhatak <111195860+uddhavphatak@users.noreply.github.com> Date: Sat, 1 Jun 2024 13:46:33 +0530 Subject: [PATCH 04/64] Update pdfio-tools.c removing comments of _cfPDFToPDFMakePage --- cupsfilters/pdftopdf/pdfio-tools.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cupsfilters/pdftopdf/pdfio-tools.c b/cupsfilters/pdftopdf/pdfio-tools.c index 5d148dcd..a28a76d5 100644 --- a/cupsfilters/pdftopdf/pdfio-tools.c +++ b/cupsfilters/pdftopdf/pdfio-tools.c @@ -55,13 +55,6 @@ _cfPDFToPDFGetArtBox(pdfio_obj_t *page) // {{{ } // }}} -/* -pdfio_obj_t -_cfPDFToPDFMakePage() -{ -} -*/ - pdfio_rect_t _cfPDFToPDFMakeBox(double x1, double y1, From 2dfafc38ca13642c99cc4dfb9c06c4f23968b2cb Mon Sep 17 00:00:00 2001 From: ThePhatak <111195860+uddhavphatak@users.noreply.github.com> Date: Sat, 1 Jun 2024 13:47:04 +0530 Subject: [PATCH 05/64] Update pdfio-tools-private.h Removing commented code for _cfPDFToPDFMakePage --- cupsfilters/pdftopdf/pdfio-tools-private.h | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/cupsfilters/pdftopdf/pdfio-tools-private.h b/cupsfilters/pdftopdf/pdfio-tools-private.h index ac132dac..cc9e509b 100644 --- a/cupsfilters/pdftopdf/pdfio-tools-private.h +++ b/cupsfilters/pdftopdf/pdfio-tools-private.h @@ -10,18 +10,5 @@ pdfio_rect_t _cfPDFToPDFGetBleedBox(pdfio_obj_t page); pdfio_rect_t _cfPDFToPDFGetTrimBox(pdfio_obj_t page); pdfio_rect_t _cfPDFToPDFGetArtBox(pdfio_obj_t page); -/* -pdfio_obj_t _cfPDFToPDFMakePage(pdfio_file_t &pdf, const std::map Date: Sat, 1 Jun 2024 14:09:53 +0530 Subject: [PATCH 06/64] Pdfio Tools test file: test file for printing the boxes dimensions. Removed the (_cfPDFToPDFMakePage) declaration as function not called anywhere --- cupsfilters/pdftopdf/pdfio-tools-test.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cupsfilters/pdftopdf/pdfio-tools-test.c b/cupsfilters/pdftopdf/pdfio-tools-test.c index b952056f..1f4fa63a 100644 --- a/cupsfilters/pdftopdf/pdfio-tools-test.c +++ b/cupsfilters/pdftopdf/pdfio-tools-test.c @@ -54,13 +54,6 @@ _cfPDFToPDFGetArtBox(pdfio_obj_t *page) return artBox; } - -/* -pdfio_obj_t -_cfPDFToPDFMakePage() -{ -} -*/ pdfio_rect_t _cfPDFToPDFMakeBox(double x1, double y1, double x2, double y2) { From 281138854ad746cac3390ef7ff66f0659452e168 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Tue, 4 Jun 2024 12:28:25 +0530 Subject: [PATCH 07/64] no change --- cupsfilters/pdftopdf/pdfio-tools.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cupsfilters/pdftopdf/pdfio-tools.c b/cupsfilters/pdftopdf/pdfio-tools.c index a28a76d5..6bc12ee3 100644 --- a/cupsfilters/pdftopdf/pdfio-tools.c +++ b/cupsfilters/pdftopdf/pdfio-tools.c @@ -1,7 +1,7 @@ #include "pdfio-tools-private.h" pdfio_rect_t -_cfPDFToPDFGetMediaBox(pdfio_obj_t *page) // {{{ +_cfPDFToPDFGetMediaBox1(pdfio_obj_t *page) // {{{ { pdfio_rect_t mediaBox; pdfio_dict_t *page_dict = pdfioObjGetDict(page); @@ -11,23 +11,23 @@ _cfPDFToPDFGetMediaBox(pdfio_obj_t *page) // {{{ // }}} pdfio_rect_t -_cfPDFToPDFGetCropBox(pdfio_obj_t *page) // {{{ +_cfPDFToPDFGetCropBox1(pdfio_obj_t *page) // {{{ { pdfio_rect_t cropBox; pdfio_dict_t *page_dict = pdfioObjGetDict(page); if (!pdfioDictGetRect(page_dict, "CropBox", &cropBox)) - return _cfPDFToPDFGetMediaBox(page); + return _cfPDFToPDFGetMediaBox1(page); return cropBox; } // }}} pdfio_rect_t -_cfPDFToPDFGetBleedBox(pdfio_obj_t *page) // {{{ +_cfPDFToPDFGetBleedBox1(pdfio_obj_t *page) // {{{ { pdfio_rect_t bleedBox; pdfio_dict_t *page_dict = pdfioObjGetDict(page); if (!pdfioDictGetRect(page_dict, "BleedBox", &bleedBox)) - return _cfPDFToPDFGetCropBox(page); + return _cfPDFToPDFGetCropBox1(page); return bleedBox; } // }}} @@ -38,19 +38,19 @@ _cfPDFToPDFGetTrimBox(pdfio_obj_t *page) // {{{ pdfio_rect_t trimBox; pdfio_dict_t *page_dict = pdfioObjGetDict(page); if (!pdfioDictGetRect(page_dict, "TrimBox", &trimBox)) - return _cfPDFToPDFGetCropBox(page); + return _cfPDFToPDFGetCropBox1(page); return trimBox; } // }}} pdfio_rect_t -_cfPDFToPDFGetArtBox(pdfio_obj_t *page) // {{{ +_cfPDFToPDFGetArtBox1(pdfio_obj_t *page) // {{{ { pdfio_rect_t artBox; pdfio_dict_t *page_dict = pdfioObjGetDict(page); if (!pdfioDictGetRect(page_dict, "ArtBox", &artBox)) - return _cfPDFToPDFGetCropBox(page); + return _cfPDFToPDFGetCropBox1(page); return artBox; } // }}} From a8fd7dc6e74fea8e1599456990721c138b0277c9 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Tue, 4 Jun 2024 12:28:48 +0530 Subject: [PATCH 08/64] wrong pointers --- cupsfilters/pdftopdf/pdfio-tools-private.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/cupsfilters/pdftopdf/pdfio-tools-private.h b/cupsfilters/pdftopdf/pdfio-tools-private.h index cc9e509b..de1f7cf5 100644 --- a/cupsfilters/pdftopdf/pdfio-tools-private.h +++ b/cupsfilters/pdftopdf/pdfio-tools-private.h @@ -2,13 +2,16 @@ // // // +#ifndef _PDFIO_H_ + +#define _PDFIO_H_ #include +#endif -pdfio_rect_t _cfPDFToPDFGetMediaBox(pdfio_obj_t page); -pdfio_rect_t _cfPDFToPDFGetCropBox(pdfio_obj_t page); -pdfio_rect_t _cfPDFToPDFGetBleedBox(pdfio_obj_t page); -pdfio_rect_t _cfPDFToPDFGetTrimBox(pdfio_obj_t page); -pdfio_rect_t _cfPDFToPDFGetArtBox(pdfio_obj_t page); +pdfio_rect_t _cfPDFToPDFGetMediaBox1(pdfio_obj_t *page); +pdfio_rect_t _cfPDFToPDFGetCropBox1(pdfio_obj_t *page); +pdfio_rect_t _cfPDFToPDFGetBleedBox1(pdfio_obj_t *page); +pdfio_rect_t _cfPDFToPDFGetTrimBox(pdfio_obj_t *page); +pdfio_rect_t _cfPDFToPDFGetArtBox1(pdfio_obj_t *page); pdfio_rect_t _cfPDFToPDFMakeBox(double x1, double y1, double x2, double y2); - From 09bdf163667aee46914c1469e13aa178fd0d15e0 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Tue, 4 Jun 2024 12:31:43 +0530 Subject: [PATCH 09/64] added check of pdfio as well --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 7288e3de..b25af14e 100644 --- a/configure.ac +++ b/configure.ac @@ -291,6 +291,7 @@ AS_IF([test x"$lcms2" = "xno"], [ AC_DEFINE([USE_LCMS1], [1], [Defines if use lcms1]) ]) PKG_CHECK_MODULES([FONTCONFIG], [fontconfig >= 2.0.0]) +PKG_CHECK_MODULES([LIBPDFIO], [pdfio >= 1.2.0]) PKG_CHECK_MODULES([LIBQPDF], [libqpdf >= 11.0.0]) # ================= From 77473ead476b907d447cfe42962c7c99f69ff9ed Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Tue, 4 Jun 2024 12:32:54 +0530 Subject: [PATCH 10/64] removed qpdf-tools.cxx from list and added pdfio-tools.c into the list --- Makefile.am | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index 450a9d5b..68a1d5d0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -186,8 +186,8 @@ libcupsfilters_la_SOURCES = \ cupsfilters/pdftopdf/nup-private.h \ cupsfilters/pdftopdf/intervalset.cxx \ cupsfilters/pdftopdf/intervalset-private.h \ - cupsfilters/pdftopdf/qpdf-tools.cxx \ - cupsfilters/pdftopdf/qpdf-tools-private.h \ + cupsfilters/pdftopdf/pdfio-tools.c \ + cupsfilters/pdftopdf/pdfio-tools-private.h \ cupsfilters/pdftopdf/qpdf-xobject.cxx \ cupsfilters/pdftopdf/qpdf-xobject-private.h \ cupsfilters/pdftopdf/qpdf-pdftopdf.cxx \ @@ -211,6 +211,7 @@ libcupsfilters_la_LIBADD = \ $(FONTCONFIG_LIBS) \ $(CUPS_LIBS) \ $(LCMS_LIBS) \ + $(LIBPDFIO_LIBS) \ $(LIBQPDF_LIBS) \ $(LIBJPEG_LIBS) \ $(EXIF_LIBS) \ From e5505f35f8735d5458f44df8ee342493d5b00504 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 24 Jun 2024 11:12:34 +0530 Subject: [PATCH 11/64] pptypes header file converted to C, because it was a dependancy in qpdf-pdftopdf.cxx --- cupsfilters/pdftopdf/C-pptypes-private.h | 71 ++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 cupsfilters/pdftopdf/C-pptypes-private.h diff --git a/cupsfilters/pdftopdf/C-pptypes-private.h b/cupsfilters/pdftopdf/C-pptypes-private.h new file mode 100644 index 00000000..a8d07945 --- /dev/null +++ b/cupsfilters/pdftopdf/C-pptypes-private.h @@ -0,0 +1,71 @@ +#include "pdftopdf-private.h" +#include + +typedef enum { + X, + Y +} pdftopdf_axis_e; + +typedef enum { + CENTER = 0, + LEFT = -1, + RIGHT = 1, + TOP = 1, + BOTTOM = -1 +} pdftopdf_position_e; + +void _cfPDFToPDFPositionDump(pdftopdf_position_e pos, pdftopdf_doc_t *doc); +void _cfPDFToPDFPositionAndAxisDump(pdftopdf_position_e pos, pdftopdf_axis_e axis, + pdftopdf_doc_t *doc); + +typedef enum { + ROT_0, + ROT_90, + ROT_180, + ROT_270, +} pdftopdf_rotation_e; + +void _cfPDFToPDFRotationDump(pdftopdf_rotation_e rot, pdftopdf_doc_t *doc); + +pdftopdf_rotation_e add_rotations(pdftopdf_rotation_e lhs, pdftopdf_rotation_e rhs); +pdftopdf_rotation_e subtract_rotations(pdftopdf_rotation_e lhs, pdftopdf_rotation_e rhs); +pdftopdf_rotation_e negate_rotation(pdftopdf_rotation_e rhs); + +typedef enum{ + NONE = 0, + ONE_THIN = 2, + ONE_THICK = 3, TWO_THIN = 4, + TWO_THICK = 5, + ONE = 0x02, + TWO = 0x04, + THICK = 0x01 +} pdftopdf_border_type_e; + +void _cfPDFToPDFBorderTypeDump(pdftopdf_border_type_e border, + pdftopdf_doc_t *doc); + +typedef struct { + float top, left, right, bottom; // i.e. margins + float width, height; +} _cfPDFToPDFPageRect; + +void _cfPDFToPDFPageRect_init(_cfPDFToPDFPageRect *rect); + +void _cfPDFToPDFPageRect_rotate_move(_cfPDFToPDFPageRect *rect, + pdftopdf_rotation_e r, + float pwidth, + float pheight); + +void _cfPDFToPDFPageRect_scale(_cfPDFToPDFPageRect *rect, + float mult); + +void _cfPDFToPDFPageRect_translate(_cfPDFToPDFPageRect *rect, + float tx, + float ty); + +void _cfPDFToPDFPageRect_set(_cfPDFToPDFPageRect *rect, const + _cfPDFToPDFPageRect *rhs); + +void _cfPDFToPDFPageRect_dump(const _cfPDFToPDFPageRect *rect, + pdftopdf_doc_t *doc); + From 9d411546374c7f8f79515b1712a1dca126276cf8 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 24 Jun 2024 11:13:10 +0530 Subject: [PATCH 12/64] pptypes extension converted to C --- cupsfilters/pdftopdf/C-pptypes.c | 260 +++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 cupsfilters/pdftopdf/C-pptypes.c diff --git a/cupsfilters/pdftopdf/C-pptypes.c b/cupsfilters/pdftopdf/C-pptypes.c new file mode 100644 index 00000000..612e0e73 --- /dev/null +++ b/cupsfilters/pdftopdf/C-pptypes.c @@ -0,0 +1,260 @@ +#include "C-pptypes-private.h" +#include +#include "cupsfilters/debug-internal.h" + +void +_cfPDFToPDFPositionDump(pdftopdf_position_e pos, + pdftopdf_doc_t *doc) +{ + static const char *pstr[3] = {"Left/Bottom", "Center", "Right/Top"}; + if ((pos < LEFT) || (pos > RIGHT)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: (bad position: %d)", pos); + } + else + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: %s", pstr[pos+1]); + } +} + +void +_cfPDFToPDFPositionAndAxisDump(pdftopdf_position_e pos, + pdftopdf_axis_e axis, + pdftopdf_doc_t *doc) +{ + DEBUG_assert((axis == X) || (axis == Y)); + + if ((pos < LEFT) || (pos > RIGHT)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Position %s: (bad position: %d)", + (axis == X) ? "X" : "Y", pos); + return; + } + + if (axis == X) + { + static const char *pxstr[3] = {"Left", "Center", "Right"}; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Position X: %s", pxstr[pos + 1]); + } + + else + { + static const char *pystr[3] = {"Bottom", "Center", "Top"}; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Position Y: %s", pystr[pos + 1]); + + } +} + +void +_cfPDFToPDFRotationDump(pdftopdf_rotation_e rot, + pdftopdf_doc_t *doc) +{ + static const char *rstr[4] = {"0 deg", "90 deg", "180 deg", "270 deg"}; // CCW + + if ((rot < ROT_0) || (rot > ROT_270)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Rotation(CCW): (bad rotation: %d)", rot); + } + + else + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Rotation(CCW): %s", rstr[rot]); + } +} + +pdftopdf_rotation_e +add_rotations(pdftopdf_rotation_e lhs, + pdftopdf_rotation_e rhs) +{ + return (pdftopdf_rotation_e)(((int)lhs + (int)rhs) % 4); +} + +pdftopdf_rotation_e +subtract_rotations(pdftopdf_rotation_e lhs, pdftopdf_rotation_e rhs) +{ + return (pdftopdf_rotation_e)((((int)lhs - (int)rhs) % 4 + 4) % 4); +} + +pdftopdf_rotation_e +negate_rotation(pdftopdf_rotation_e rhs) +{ + return (pdftopdf_rotation_e)((4 - (int)rhs) % 4); +} + +void +_cfPDFToPDFBorderTypeDump(pdftopdf_border_type_e border, + pdftopdf_doc_t *doc) +{ + if ((border < NONE) || (border == 1) || (border > TWO_THICK)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Border: (bad border: %d)", border); + + } + else + { + static const char *bstr[6] = + {"None", NULL, "one thin", "one thick", "two thin", "two thick"}; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Border: %s", bstr[border]); + + } +} + +void +_cfPDFToPDFPageRect_init(_cfPDFToPDFPageRect *rect) +{ + rect->top = NAN; + rect->left = NAN; + rect->right = NAN; + rect->bottom = NAN; + rect->width = NAN; + rect->height = NAN; +} + +void swap_float(float *a, float *b) { + float temp = *a; + *a = *b; + *b = temp; +} + +void _cfPDFToPDFPageRect_rotate_move(_cfPDFToPDFPageRect *rect, + pdftopdf_rotation_e r, + float pwidth, + float pheight) +{ + #if 1 + if (r >= ROT_180) + { + swap_float(&rect->top, &rect->bottom); + swap_float(&rect->left, &rect->right); + } + + if ((r == ROT_90) || (r == ROT_270)) + { + const float tmp = rect->bottom; + rect->bottom = rect->left; + rect->left = rect->top; + rect->top = rect->right; + rect->right = tmp; + + swap_float(&rect->width, &rect->height); + swap_float(&pwidth, &pheight); + } + + if ((r == ROT_90) || (r == ROT_180)) + { + rect->left = pwidth - rect->left; + rect->right = pwidth - rect->right; + } + + if ((r == ROT_270) || (r == ROT_180)) + { + rect->top = pheight - rect->top; + rect->bottom = pheight - rect->bottom; + } + + #else + switch(r) + { + case ROT_0: // no-op + break; + case ROT_90: + const float tmp0 = bottom; + bottom = left; + left = pheight - top; + top = right; + right = pheight - tmp0; + + swap_float(&width, &height); + break; + + case ROT_180: + const float tmp1 = left; + left = pwidth - right; + right = pwidth - tmp1; + + const float tmp2 = top; + top = pheight - bottom; + bottom = pheight - tmp2; + break; + + case ROT_270: + const float tmp3 = top; + top = pwidth - left; + left = bottom; + bottom = pwidth - right; + right = tmp3; + + swap_float(&width, &height); + break; + + } + #endif +} + +void +_cfPDFToPDFPageRect_scale(_cfPDFToPDFPageRect *rect, + float mult) +{ + if (mult == 1.0) + return; + + DEBUG_assert(mult != 0.0); + + rect->bottom *= mult; + rect->left *= mult; + rect->top *= mult; + rect->right *= mult; + + rect->width *= mult; + rect->height *= mult; +} + +void +_cfPDFToPDFPageRect_translate(_cfPDFToPDFPageRect *rect, + float tx, + float ty) +{ + rect->left += tx; + rect->bottom += ty; + rect->right += tx; + rect->top += ty; +} + +void +_cfPDFToPDFPageRect_set(_cfPDFToPDFPageRect *rect, + const _cfPDFToPDFPageRect *rhs) +{ + if (!isnan(rhs->top)) + rect->top = rhs->top; + + if (!isnan(rhs->left)) + rect->left = rhs->left; + + if (!isnan(rhs->right)) + rect->right = rhs->right; + + if (!isnan(rhs->bottom)) + rect->bottom = rhs->bottom; +} + +void +_cfPDFToPDFPageRect_dump(const _cfPDFToPDFPageRect *rect, + pdftopdf_doc_t *doc) +{ + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: top: %f, left: %f, right: %f, bottom: %f, " + "width: %f, height: %f", + rect->top, rect->left, rect->right, rect->bottom, + rect->width, rect->height); + +} + From 702b1139d34c41bad622e3f2ed9fdb0f0b5dd6d3 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 24 Jun 2024 11:14:41 +0530 Subject: [PATCH 13/64] changed qpdf-pdftopdf.cxx from Cpp to C, and removed the dependancy from qpdf to pdfio --- cupsfilters/pdftopdf/pdfio-pdftopdf.c | 305 ++++++++++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 cupsfilters/pdftopdf/pdfio-pdftopdf.c diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf.c b/cupsfilters/pdftopdf/pdfio-pdftopdf.c new file mode 100644 index 00000000..a52293c0 --- /dev/null +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf.c @@ -0,0 +1,305 @@ +// +// +// +// +#include "pdfio-pdftopdf-private.h" +#include "pdfio-tools-private.h" +#include "cupsfilters/debug-internal.h" +#include +#include +#include +#include + +_cfPDFToPDFPageRect +_cfPDFToPDFGetBoxAsRect(pdfio_rect_t *box) +{ + _cfPDFToPDFPageRect ret; + + ret.left = box->x1; + ret.bottom = box->y1; + ret.right = box->x2; + ret.top = box->y2; + + ret.width = ret.right - ret.left; + ret.height = ret.top - ret.bottom; + + return ret; +} + +pdfio_rect_t +_cfPDFToPDFGetRectAsBox(const _cfPDFToPDFPageRect *rect) +{ + return (_cfPDFToPDFMakeBox(rect->left, rect->bottom, rect->right, rect->top)); +} + + +pdftopdf_rotation_e +_cfPDFToPDFGetRotate(pdfio_obj_t *page) +{ + pdfio_dict_t *page_dict = pdfioObjGetDict(page); + if(!pdfioDictGetNumber(page_dict, "Rotate")) + return (ROT_0); + double rot = pdfioDictGetNumber(page_dict, "Rotate"); + + rot = fmod(rot, 360.0); + if (rot<0) + rot += 360.0; + else if (rot == 90.0) + return (ROT_270); + else if (rot == 180.0) + return (ROT_180); + else if (rot == 270.0) + return (ROT_90); + else if (rot != 0.0) + { + char str[100]; + sprintf(str, "%f", rot); + printf("Unexpected /Rotate value: %s\n", str); + return -1; + } + return (ROT_0); +} + +int +_cfPDFToPDFMakeRotate(pdftopdf_rotation_e rot) +{ + switch(rot) + { + case ROT_0: + return 0; + + case ROT_90: + return 270; + + case ROT_180: + return 180; + + case ROT_270: + return 90; + + default: + printf("Bad Rotation\n"); + return -1; + } + + +} + +double +_cfPDFToPDFGetUserUnit(pdfio_obj_t *page) +{ + pdfio_dict_t *page_dict = pdfioObjGetDict(page); + if(!pdfioDictGetNumber(page_dict, "UserUnit")) + return 1.0; + + int userUnit_value = pdfioDictGetNumber(page_dict, "UserUnit"); + return userUnit_value; +} + +void +_cfPDFToPDFMatrix_init(_cfPDFToPDFMatrix *matrix) +{ + matrix->ctm[0] = 1; + matrix->ctm[1] = 0; + matrix->ctm[2] = 0; + matrix->ctm[3] = 1; + matrix->ctm[4] = 0; + matrix->ctm[5] = 0; +} + +void +_cfPDFToPDFMatrix_init_with_handle(_cfPDFToPDFMatrix *matrix, + pdfio_array_t *ar) +{ + if( pdfioArrayGetSize(ar) != 6) + printf("Not a ctm Matrix\n"); + for (int iA = 0; iA<6 ; iA++) + matrix->ctm[iA] = pdfioArrayGetNumber(ar, iA); +} + +_cfPDFToPDFMatrix +_cfPDFToPDFMatrix_rotate_function(_cfPDFToPDFMatrix *matrix, + pdftopdf_rotation_e rot) +{ + switch (rot) + { + case ROT_0: + break; + case ROT_90: + double temp = matrix->ctm[0]; + matrix->ctm[0] = matrix->ctm[2]; + matrix->ctm[2] = temp; + + temp = matrix->ctm[1]; + matrix->ctm[1] = matrix->ctm[3]; + matrix->ctm[3] = temp; + + matrix->ctm[2] = -(matrix->ctm[2]); + matrix->ctm[3] = -(matrix->ctm[3]); + break; + + case ROT_180: + matrix->ctm[0] = -(matrix->ctm[0]); + matrix->ctm[3] = -(matrix->ctm[3]); + break; + + case ROT_270: + temp = matrix->ctm[0]; + matrix->ctm[0] = matrix->ctm[2]; + matrix->ctm[2] = temp; + + temp = matrix->ctm[1]; + matrix->ctm[1] = matrix->ctm[3]; + matrix->ctm[3] = temp; + + matrix->ctm[0] = -(matrix->ctm[0]); + matrix->ctm[1] = -(matrix->ctm[1]); + break; + + default: + DEBUG_assert(0); + } + return *matrix; //right now we don't know the full extent on this function, + //can even return the funtion as void. +} + +_cfPDFToPDFMatrix +_cfPDFToPDFMatrix_rotate_move(_cfPDFToPDFMatrix *matrix, + pdftopdf_rotation_e rot, + double width, + double height) +{ + _cfPDFToPDFMatrix_rotate_function(matrix, rot); + switch(rot) + { + case ROT_0: + break; + case ROT_90: + _cfPDFToPDFMatrix_translate(matrix, width, 0); + break; + case ROT_180: + _cfPDFToPDFMatrix_translate(matrix, width, height); + break; + case ROT_270: + _cfPDFToPDFMatrix_translate(matrix, 0, height); + break; + } + return (*matrix); //right now don't know the full scope of this function, + //can even return it as void +} + +_cfPDFToPDFMatrix +_cfPDFToPDFMatrix_rotate_rad(_cfPDFToPDFMatrix *matrix, + double rad) +{ + _cfPDFToPDFMatrix tmp; + _cfPDFToPDFMatrix multiplied; + + tmp.ctm[0] = cos(rad); + tmp.ctm[1] = sin(rad); + tmp.ctm[2] = -sin(rad); + tmp.ctm[3] = cos(rad); + + multiplied = _cfPDFToPDFMatrix_multiply(matrix, + &tmp); + return multiplied; +} + + +_cfPDFToPDFMatrix +_cfPDFToPDFMatrix_translate(_cfPDFToPDFMatrix *matrix, + double tx, + double ty) +{ + matrix->ctm[4] += matrix->ctm[0] * tx + matrix->ctm[2] * ty; + matrix->ctm[5] += matrix->ctm[1] * tx + matrix->ctm[3] * ty; + return *matrix; +} + +_cfPDFToPDFMatrix +_cfPDFToPDFMatrix_scale(_cfPDFToPDFMatrix *matrix, + double sx, + double sy) +{ + matrix->ctm[0] *= sx; + matrix->ctm[1] *= sx; + matrix->ctm[2] *= sy; + matrix->ctm[3] *= sy; + + return *matrix; +} + +_cfPDFToPDFMatrix +_cfPDFToPDFMatrix_scale_uniform(_cfPDFToPDFMatrix *matrix, + double s) +{ + return (_cfPDFToPDFMatrix_scale(matrix, s, s)); +} + +_cfPDFToPDFMatrix +_cfPDFToPDFMatrix_multiply(_cfPDFToPDFMatrix *matrix, + const _cfPDFToPDFMatrix *rhs) +{ + double tmp[6]; + + for (int i = 0; i < 6; ++i) + { + tmp[i] = matrix->ctm[i]; + } + + matrix->ctm[0] = tmp[0] * rhs->ctm[0] + tmp[2] * rhs->ctm[1]; + matrix->ctm[1] = tmp[1] * rhs->ctm[0] + tmp[3] * rhs->ctm[1]; + + matrix->ctm[2] = tmp[0] * rhs->ctm[2] + tmp[2] * rhs->ctm[3]; + matrix->ctm[3] = tmp[1] * rhs->ctm[2] + tmp[3] * rhs->ctm[3]; + + matrix->ctm[4] = tmp[0] * rhs->ctm[4] + tmp[2] * rhs->ctm[4] + tmp[4]; + matrix->ctm[5] = tmp[1] * rhs->ctm[4] + tmp[3] * rhs->ctm[5] + tmp[5]; + + return *matrix; +} + +pdfio_array_t *_cfPDFToPDFMatrix_get_array(const _cfPDFToPDFMatrix *matrix) +{ + pdfio_file_t *pdf = pdfioFileCreate("example.pdf", NULL, NULL, NULL, NULL, NULL); + pdfio_array_t *ret = pdfioArrayCreate(pdf); + + pdfioArrayAppendNumber(ret, matrix->ctm[0]); + pdfioArrayAppendNumber(ret, matrix->ctm[1]); + pdfioArrayAppendNumber(ret, matrix->ctm[2]); + pdfioArrayAppendNumber(ret, matrix->ctm[3]); + pdfioArrayAppendNumber(ret, matrix->ctm[4]); + pdfioArrayAppendNumber(ret, matrix->ctm[5]); + + pdfioFileClose(pdf); + return (ret); +} + +char +_cfPDFToPDFMatrix_get_string(const _cfPDFToPDFMatrix *matrix) +{ + // Helper function to convert double to string + void double_to_string(double value, char *str, size_t size) + { + snprintf(str, size, "%f", value); + } + + // Assuming each double string will not exceed 32 characters including the null terminator + char buffer[32]; + size_t total_size = 32 * 6 + 5; // 6 doubles and 5 spaces + char *ret = (char*)malloc(total_size); + + ret[0] = '\0'; // Initialize the string + + for (int i = 0; i < 6; ++i) + { + double_to_string(matrix->ctm[i], buffer, sizeof(buffer)); + strcat(ret, buffer); + if (i < 5) + { + strcat(ret, " "); + } + } + + return *ret; +} From 4a162e8daa3e7ff9621063e40566ec5f6a82bef4 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 24 Jun 2024 11:15:49 +0530 Subject: [PATCH 14/64] the qpdf-pdftopdf-private.h changed to pdfio-pdftopdf-private.h --- cupsfilters/pdftopdf/pdfio-pdftopdf-private.h | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 cupsfilters/pdftopdf/pdfio-pdftopdf-private.h diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h b/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h new file mode 100644 index 00000000..b0625d26 --- /dev/null +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h @@ -0,0 +1,61 @@ +// +// +// +// +#include +#include "C-pptypes-private.h" + +//helper functions + +_cfPDFToPDFPageRect _cfPDFToPDFGetBoxAsRect(pdfio_rect_t *box); +pdfio_rect_t _cfPDFToPDFGetRectAsBox(const _cfPDFToPDFPageRect *rect); + +// Note that PDF specification is CW, but our Rotation is CCW +pdftopdf_rotation_e _cfPDFToPDFGetRotate(pdfio_obj_t *page); +int _cfPDFToPDFMakeRotate(pdftopdf_rotation_e rot); // integer, change all calls + +double _cfPDFToPDFGetUserUnit(pdfio_obj_t *page); + +//class +typedef struct +{ + double ctm[6]; +} _cfPDFToPDFMatrix; + +void _cfPDFToPDFMatrix_init(_cfPDFToPDFMatrix *matrix); + +void _cfPDFToPDFMatrix_init_with_handle(_cfPDFToPDFMatrix *matrix, + pdfio_array_t *ar); + +_cfPDFToPDFMatrix _cfPDFToPDFMatrix_rotate_function(_cfPDFToPDFMatrix *matrix, + pdftopdf_rotation_e rot); + +_cfPDFToPDFMatrix _cfPDFToPDFMatrix_rotate_move(_cfPDFToPDFMatrix *matrix, + pdftopdf_rotation_e rot, + double width, + double height); + +_cfPDFToPDFMatrix _cfPDFToPDFMatrix_rotate_rad(_cfPDFToPDFMatrix *matrix, + double rad); + +_cfPDFToPDFMatrix _cfPDFToPDFMatrix_translate(_cfPDFToPDFMatrix *matrix, + double tx, + double ty); + +_cfPDFToPDFMatrix _cfPDFToPDFMatrix_scale(_cfPDFToPDFMatrix *matrix, + double sx, + double sy); + +_cfPDFToPDFMatrix _cfPDFToPDFMatrix_scale_uniform(_cfPDFToPDFMatrix *matrix, + double s); + + +_cfPDFToPDFMatrix _cfPDFToPDFMatrix_multiply(_cfPDFToPDFMatrix *matrix, + const _cfPDFToPDFMatrix *rhs); + +pdfio_array_t *_cfPDFToPDFMatrix_get_array(const _cfPDFToPDFMatrix *matrix); + +char _cfPDFToPDFMatrix_get_string(const _cfPDFToPDFMatrix *matrix); + + + From 53fe9d44b34bc5d8b3137f99160217af02d8ddc7 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 24 Jun 2024 11:18:07 +0530 Subject: [PATCH 15/64] qpdf-cm-private.h changed to pdfio-cm-private.h --- cupsfilters/pdftopdf/pdfio-cm-private.h | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 cupsfilters/pdftopdf/pdfio-cm-private.h diff --git a/cupsfilters/pdftopdf/pdfio-cm-private.h b/cupsfilters/pdftopdf/pdfio-cm-private.h new file mode 100644 index 00000000..0b813138 --- /dev/null +++ b/cupsfilters/pdftopdf/pdfio-cm-private.h @@ -0,0 +1,9 @@ +#include + +bool _cfPDFToPDFHasOutputIntent(pdfio_file_t *pdf); +void _cfPDFToPDFAddOutputIntent(pdfio_file_t *pdf, const char *filename); + +void _cfPDFToPDFAddDefaultRGB(pdfio_file_t *pdf, pdfio_obj_t *srcicc); +pdfio_obj_t* _cfPDFToPDFSetDefaultICC(pdfio_file_t *pdf, const char *filename); + + From 7817e6550b4c63f5ab966f02e37ef6cf0c90a1c8 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 24 Jun 2024 11:18:52 +0530 Subject: [PATCH 16/64] qpdf-cm.cxx changed to pdfio-cm.c --- cupsfilters/pdftopdf/pdfio-cm.c | 134 ++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 cupsfilters/pdftopdf/pdfio-cm.c diff --git a/cupsfilters/pdftopdf/pdfio-cm.c b/cupsfilters/pdftopdf/pdfio-cm.c new file mode 100644 index 00000000..bfaa25b8 --- /dev/null +++ b/cupsfilters/pdftopdf/pdfio-cm.c @@ -0,0 +1,134 @@ +#include "pdfio-cm-private.h" +#include +#include "cupsfilters/debug-internal.h" +#include +#include +#include +#include + +bool +_cfPDFToPDFHasOutputIntent(pdfio_file_t *pdf) // {{{ +{ + int numObj = pdfioFileGetNumObjs(pdf); + for(int count=0; count Date: Mon, 24 Jun 2024 11:20:37 +0530 Subject: [PATCH 17/64] only added the pdfio files and removed other files, because due to changes in function names, the other function files which were dependent on these file, were hindering the run process --- Makefile.am | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/Makefile.am b/Makefile.am index 68a1d5d0..f5b8e4be 100644 --- a/Makefile.am +++ b/Makefile.am @@ -174,26 +174,14 @@ libcupsfilters_la_SOURCES = \ cupsfilters/pack.c \ cupsfilters/pclmtoraster.cxx \ cupsfilters/pdf.cxx \ - cupsfilters/pdftopdf/pdftopdf.cxx \ - cupsfilters/pdftopdf/pdftopdf-private.h \ - cupsfilters/pdftopdf/pdftopdf-processor.cxx \ - cupsfilters/pdftopdf/pdftopdf-processor-private.h \ - cupsfilters/pdftopdf/qpdf-pdftopdf-processor.cxx \ - cupsfilters/pdftopdf/qpdf-pdftopdf-processor-private.h \ - cupsfilters/pdftopdf/pptypes.cxx \ - cupsfilters/pdftopdf/pptypes-private.h \ - cupsfilters/pdftopdf/nup.cxx \ - cupsfilters/pdftopdf/nup-private.h \ - cupsfilters/pdftopdf/intervalset.cxx \ - cupsfilters/pdftopdf/intervalset-private.h \ + cupsfilters/pdftopdf/C-pptypes.c \ + cupsfilters/pdftopdf/C-pptypes-private.h \ cupsfilters/pdftopdf/pdfio-tools.c \ cupsfilters/pdftopdf/pdfio-tools-private.h \ - cupsfilters/pdftopdf/qpdf-xobject.cxx \ - cupsfilters/pdftopdf/qpdf-xobject-private.h \ - cupsfilters/pdftopdf/qpdf-pdftopdf.cxx \ - cupsfilters/pdftopdf/qpdf-pdftopdf-private.h \ - cupsfilters/pdftopdf/qpdf-cm.cxx \ - cupsfilters/pdftopdf/qpdf-cm-private.h \ + cupsfilters/pdftopdf/pdfio-pdftopdf.c \ + cupsfilters/pdftopdf/pdfio-pdftopdf-private.h \ + cupsfilters/pdftopdf/pdfio-cm.c \ + cupsfilters/pdftopdf/pdfio-cm-private.h \ cupsfilters/pdftoraster.cxx \ cupsfilters/pdfutils.c \ cupsfilters/pdfutils-private.h \ From 8ece949aa942f46487e87fbb8e43f9a4c5da016f Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 24 Jun 2024 19:09:21 +0530 Subject: [PATCH 18/64] changed variable names --- cupsfilters/pdftopdf/pdfio-tools.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cupsfilters/pdftopdf/pdfio-tools.c b/cupsfilters/pdftopdf/pdfio-tools.c index 6bc12ee3..3b26dfe9 100644 --- a/cupsfilters/pdftopdf/pdfio-tools.c +++ b/cupsfilters/pdftopdf/pdfio-tools.c @@ -1,7 +1,7 @@ #include "pdfio-tools-private.h" pdfio_rect_t -_cfPDFToPDFGetMediaBox1(pdfio_obj_t *page) // {{{ +_cfPDFToPDFGetMediaBox(pdfio_obj_t *page) // {{{ { pdfio_rect_t mediaBox; pdfio_dict_t *page_dict = pdfioObjGetDict(page); @@ -11,7 +11,7 @@ _cfPDFToPDFGetMediaBox1(pdfio_obj_t *page) // {{{ // }}} pdfio_rect_t -_cfPDFToPDFGetCropBox1(pdfio_obj_t *page) // {{{ +_cfPDFToPDFGetCropBox(pdfio_obj_t *page) // {{{ { pdfio_rect_t cropBox; pdfio_dict_t *page_dict = pdfioObjGetDict(page); @@ -22,7 +22,7 @@ _cfPDFToPDFGetCropBox1(pdfio_obj_t *page) // {{{ // }}} pdfio_rect_t -_cfPDFToPDFGetBleedBox1(pdfio_obj_t *page) // {{{ +_cfPDFToPDFGetBleedBox(pdfio_obj_t *page) // {{{ { pdfio_rect_t bleedBox; pdfio_dict_t *page_dict = pdfioObjGetDict(page); @@ -45,7 +45,7 @@ _cfPDFToPDFGetTrimBox(pdfio_obj_t *page) // {{{ // }}} pdfio_rect_t -_cfPDFToPDFGetArtBox1(pdfio_obj_t *page) // {{{ +_cfPDFToPDFGetArtBox(pdfio_obj_t *page) // {{{ { pdfio_rect_t artBox; pdfio_dict_t *page_dict = pdfioObjGetDict(page); From cf11c5e46ddd27b08bfe77f6775723adce1c82c6 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 24 Jun 2024 19:09:30 +0530 Subject: [PATCH 19/64] changed variable names --- cupsfilters/pdftopdf/pdfio-tools-private.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cupsfilters/pdftopdf/pdfio-tools-private.h b/cupsfilters/pdftopdf/pdfio-tools-private.h index de1f7cf5..8e5e5f5b 100644 --- a/cupsfilters/pdftopdf/pdfio-tools-private.h +++ b/cupsfilters/pdftopdf/pdfio-tools-private.h @@ -8,10 +8,10 @@ #include #endif -pdfio_rect_t _cfPDFToPDFGetMediaBox1(pdfio_obj_t *page); -pdfio_rect_t _cfPDFToPDFGetCropBox1(pdfio_obj_t *page); -pdfio_rect_t _cfPDFToPDFGetBleedBox1(pdfio_obj_t *page); +pdfio_rect_t _cfPDFToPDFGetMediaBox(pdfio_obj_t *page); +pdfio_rect_t _cfPDFToPDFGetCropBox(pdfio_obj_t *page); +pdfio_rect_t _cfPDFToPDFGetBleedBox(pdfio_obj_t *page); pdfio_rect_t _cfPDFToPDFGetTrimBox(pdfio_obj_t *page); -pdfio_rect_t _cfPDFToPDFGetArtBox1(pdfio_obj_t *page); +pdfio_rect_t _cfPDFToPDFGetArtBox(pdfio_obj_t *page); pdfio_rect_t _cfPDFToPDFMakeBox(double x1, double y1, double x2, double y2); From e4abdb3488d28b7ff252fe6c58bf0d0dc3816a37 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 24 Jun 2024 19:55:01 +0530 Subject: [PATCH 20/64] added file pdfio-xobject.c and pdfio-xobject-private.h --- Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.am b/Makefile.am index f5b8e4be..5cafba95 100644 --- a/Makefile.am +++ b/Makefile.am @@ -178,6 +178,8 @@ libcupsfilters_la_SOURCES = \ cupsfilters/pdftopdf/C-pptypes-private.h \ cupsfilters/pdftopdf/pdfio-tools.c \ cupsfilters/pdftopdf/pdfio-tools-private.h \ + cupsfilters/pdftopdf/pdfio-xobject.c \ + cupsfilters/pdftopdf/pdfio-xobject-private.h \ cupsfilters/pdftopdf/pdfio-pdftopdf.c \ cupsfilters/pdftopdf/pdfio-pdftopdf-private.h \ cupsfilters/pdftopdf/pdfio-cm.c \ From eb33ee5fff1207a3ed1f887196ee632909051497 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 24 Jun 2024 19:55:56 +0530 Subject: [PATCH 21/64] wrong function call names --- cupsfilters/pdftopdf/pdfio-tools.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cupsfilters/pdftopdf/pdfio-tools.c b/cupsfilters/pdftopdf/pdfio-tools.c index 3b26dfe9..a28a76d5 100644 --- a/cupsfilters/pdftopdf/pdfio-tools.c +++ b/cupsfilters/pdftopdf/pdfio-tools.c @@ -16,7 +16,7 @@ _cfPDFToPDFGetCropBox(pdfio_obj_t *page) // {{{ pdfio_rect_t cropBox; pdfio_dict_t *page_dict = pdfioObjGetDict(page); if (!pdfioDictGetRect(page_dict, "CropBox", &cropBox)) - return _cfPDFToPDFGetMediaBox1(page); + return _cfPDFToPDFGetMediaBox(page); return cropBox; } // }}} @@ -27,7 +27,7 @@ _cfPDFToPDFGetBleedBox(pdfio_obj_t *page) // {{{ pdfio_rect_t bleedBox; pdfio_dict_t *page_dict = pdfioObjGetDict(page); if (!pdfioDictGetRect(page_dict, "BleedBox", &bleedBox)) - return _cfPDFToPDFGetCropBox1(page); + return _cfPDFToPDFGetCropBox(page); return bleedBox; } // }}} @@ -38,7 +38,7 @@ _cfPDFToPDFGetTrimBox(pdfio_obj_t *page) // {{{ pdfio_rect_t trimBox; pdfio_dict_t *page_dict = pdfioObjGetDict(page); if (!pdfioDictGetRect(page_dict, "TrimBox", &trimBox)) - return _cfPDFToPDFGetCropBox1(page); + return _cfPDFToPDFGetCropBox(page); return trimBox; } @@ -50,7 +50,7 @@ _cfPDFToPDFGetArtBox(pdfio_obj_t *page) // {{{ pdfio_rect_t artBox; pdfio_dict_t *page_dict = pdfioObjGetDict(page); if (!pdfioDictGetRect(page_dict, "ArtBox", &artBox)) - return _cfPDFToPDFGetCropBox1(page); + return _cfPDFToPDFGetCropBox(page); return artBox; } // }}} From 2647003cf808d86e6bf9418586a85fe3672cc0e7 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 24 Jun 2024 19:56:52 +0530 Subject: [PATCH 22/64] changed qpdf dependancy to pdfio --- cupsfilters/pdftopdf/pdfio-xobject-private.h | 3 + cupsfilters/pdftopdf/pdfio-xobject.c | 62 ++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 cupsfilters/pdftopdf/pdfio-xobject-private.h create mode 100644 cupsfilters/pdftopdf/pdfio-xobject.c diff --git a/cupsfilters/pdftopdf/pdfio-xobject-private.h b/cupsfilters/pdftopdf/pdfio-xobject-private.h new file mode 100644 index 00000000..cbfc663d --- /dev/null +++ b/cupsfilters/pdftopdf/pdfio-xobject-private.h @@ -0,0 +1,3 @@ +#include + +pdfio_obj_t* _cfPDFToPDFMakeXObject(pdfio_file_t *pdf, pdfio_obj_t *page); diff --git a/cupsfilters/pdftopdf/pdfio-xobject.c b/cupsfilters/pdftopdf/pdfio-xobject.c new file mode 100644 index 00000000..0a5ea9cc --- /dev/null +++ b/cupsfilters/pdftopdf/pdfio-xobject.c @@ -0,0 +1,62 @@ +#include "pdfio-xobject-private.h" +#include "pdfio-tools-private.h" +#include "pdfio-pdftopdf-private.h" + +pdfio_obj_t* +_cfPDFToPDFMakeXObject(pdfio_file_t *pdf, pdfio_obj_t *page) +{ + + pdfio_dict_t *dict = pdfioDictCreate(pdf); + pdfio_dict_t *pageDict = pdfioObjGetDict(page); + + pdfioDictSetName(dict, "Type", "XObject"); + pdfioDictSetName(dict, "Subtype", "Form"); + + pdfio_rect_t box = _cfPDFToPDFGetTrimBox(page); + pdfioDictSetRect(dict, "BBox", &box); + + _cfPDFToPDFMatrix mtx; + _cfPDFToPDFMatrix_init(&mtx); + if(pdfioDictGetNumber(pageDict, "UserUnit")) + { + double valUserUnit = pdfioDictGetNumber(pageDict, "UserUnit"); + _cfPDFToPDFMatrix_scale_uniform(&mtx, valUserUnit); + } + + pdftopdf_rotation_e rot = _cfPDFToPDFGetRotate(page); + + _cfPDFToPDFPageRect bbox, tmp; + _cfPDFToPDFPageRect_init(&bbox); + _cfPDFToPDFPageRect_init(&tmp); + + bbox = _cfPDFToPDFGetBoxAsRect(&box); + + tmp.left = 0; + tmp.bottom = 0; + tmp.right = 0; + tmp.top = 0; + + _cfPDFToPDFPageRect_rotate_move(&tmp, rot, bbox.width, bbox.height); + + _cfPDFToPDFMatrix_translate(&mtx, tmp.left, tmp.bottom); + _cfPDFToPDFMatrix_rotate_function(&mtx, rot); + _cfPDFToPDFMatrix_translate(&mtx, -(bbox.left), -(bbox.bottom)); + + pdfio_array_t *matrixArray = _cfPDFToPDFMatrix_get_array(&mtx); + pdfioDictSetArray(dict, "Matrix", matrixArray); + + + pdfio_dict_t *tempDict = pdfioDictGetDict(pageDict, "Resources"); + pdfioDictSetDict(dict, "Resources", tempDict); + + if(pdfioDictGetObj(pageDict, "Group")) + { + pdfio_obj_t *groupObj = pdfioDictGetObj(pageDict, "Group"); + pdfioDictSetObj(dict, "Group", groupObj); + } + + pdfio_obj_t *returnObj = pdfioFileCreateObj(pdf, dict); + + return returnObj; + +} From cd14cc01497fe091ca83c4aff269dda9c44ac992 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 8 Jul 2024 02:08:59 +0530 Subject: [PATCH 23/64] added C-nup-private.h and C-nup.cxx --- Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.am b/Makefile.am index 5cafba95..d30d6a2f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -176,6 +176,8 @@ libcupsfilters_la_SOURCES = \ cupsfilters/pdf.cxx \ cupsfilters/pdftopdf/C-pptypes.c \ cupsfilters/pdftopdf/C-pptypes-private.h \ + cupsfilters/pdftopdf/C-nup.c \ + cupsfilters/pdftopdf/C-nup-private.h \ cupsfilters/pdftopdf/pdfio-tools.c \ cupsfilters/pdftopdf/pdfio-tools-private.h \ cupsfilters/pdftopdf/pdfio-xobject.c \ From 11935476a786f397e7246bd9cea0b5908bf495c7 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 8 Jul 2024 02:09:20 +0530 Subject: [PATCH 24/64] support for pdfio 1.3.0 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b25af14e..b6812d7a 100644 --- a/configure.ac +++ b/configure.ac @@ -291,7 +291,7 @@ AS_IF([test x"$lcms2" = "xno"], [ AC_DEFINE([USE_LCMS1], [1], [Defines if use lcms1]) ]) PKG_CHECK_MODULES([FONTCONFIG], [fontconfig >= 2.0.0]) -PKG_CHECK_MODULES([LIBPDFIO], [pdfio >= 1.2.0]) +PKG_CHECK_MODULES([LIBPDFIO], [pdfio >= 1.3.0]) PKG_CHECK_MODULES([LIBQPDF], [libqpdf >= 11.0.0]) # ================= From aed7acab506ce56c4bfe02823f869dca99fc96c3 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 8 Jul 2024 02:12:50 +0530 Subject: [PATCH 25/64] changed the function declaration for dump --- cupsfilters/pdftopdf/C-pptypes-private.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cupsfilters/pdftopdf/C-pptypes-private.h b/cupsfilters/pdftopdf/C-pptypes-private.h index a8d07945..e1f0891a 100644 --- a/cupsfilters/pdftopdf/C-pptypes-private.h +++ b/cupsfilters/pdftopdf/C-pptypes-private.h @@ -34,7 +34,8 @@ pdftopdf_rotation_e negate_rotation(pdftopdf_rotation_e rhs); typedef enum{ NONE = 0, ONE_THIN = 2, - ONE_THICK = 3, TWO_THIN = 4, + ONE_THICK = 3, + TWO_THIN = 4, TWO_THICK = 5, ONE = 0x02, TWO = 0x04, From f803918a0cd3358d754ca20db98330c3b14ab4dc Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 8 Jul 2024 02:14:25 +0530 Subject: [PATCH 26/64] added stream --- cupsfilters/pdftopdf/pdfio-cm-private.h | 4 +- cupsfilters/pdftopdf/pdfio-cm.c | 69 +++++-------------------- 2 files changed, 16 insertions(+), 57 deletions(-) diff --git a/cupsfilters/pdftopdf/pdfio-cm-private.h b/cupsfilters/pdftopdf/pdfio-cm-private.h index 0b813138..a7b8ad25 100644 --- a/cupsfilters/pdftopdf/pdfio-cm-private.h +++ b/cupsfilters/pdftopdf/pdfio-cm-private.h @@ -3,7 +3,7 @@ bool _cfPDFToPDFHasOutputIntent(pdfio_file_t *pdf); void _cfPDFToPDFAddOutputIntent(pdfio_file_t *pdf, const char *filename); -void _cfPDFToPDFAddDefaultRGB(pdfio_file_t *pdf, pdfio_obj_t *srcicc); -pdfio_obj_t* _cfPDFToPDFSetDefaultICC(pdfio_file_t *pdf, const char *filename); +void _cfPDFToPDFAddDefaultRGB(pdfio_file_t *pdf, pdfio_stream_t *srcicc); +pdfio_stream_t* _cfPDFToPDFSetDefaultICC(pdfio_file_t *pdf, const char *filename); diff --git a/cupsfilters/pdftopdf/pdfio-cm.c b/cupsfilters/pdftopdf/pdfio-cm.c index bfaa25b8..9857301f 100644 --- a/cupsfilters/pdftopdf/pdfio-cm.c +++ b/cupsfilters/pdftopdf/pdfio-cm.c @@ -9,35 +9,10 @@ bool _cfPDFToPDFHasOutputIntent(pdfio_file_t *pdf) // {{{ { - int numObj = pdfioFileGetNumObjs(pdf); - for(int count=0; count Date: Mon, 8 Jul 2024 02:15:35 +0530 Subject: [PATCH 27/64] nup file changed from C++ to C --- cupsfilters/pdftopdf/C-nup-private.h | 61 +++++ cupsfilters/pdftopdf/C-nup.c | 377 +++++++++++++++++++++++++++ 2 files changed, 438 insertions(+) create mode 100644 cupsfilters/pdftopdf/C-nup-private.h create mode 100644 cupsfilters/pdftopdf/C-nup.c diff --git a/cupsfilters/pdftopdf/C-nup-private.h b/cupsfilters/pdftopdf/C-nup-private.h new file mode 100644 index 00000000..084b6e4a --- /dev/null +++ b/cupsfilters/pdftopdf/C-nup-private.h @@ -0,0 +1,61 @@ +#include "C-pptypes-private.h" +#include + +typedef struct +{ + int nupX, nupY; + float width, height; + bool landscape; + pdftopdf_axis_e first; + pdftopdf_position_e xstart, ystart; + pdftopdf_position_e xalign, yalign; +} _cfPDFToPDFNupParameters; + +void _cfPDFToPDFNupParameters_init(_cfPDFToPDFNupParameters *nupParam); +void _cfPDFToPDFNupParameters_dump(const _cfPDFToPDFNupParameters *nupParam, + pdftopdf_doc_t *doc); +bool _cfPDFToPDFNupParameters_possible(int nup); +void _cfPDFToPDFNupParameters_preset(int nup, _cfPDFToPDFNupParameters *ret); + + +typedef struct +{ + float xpos, ypos; + float scale; + _cfPDFToPDFPageRect *sub; +} _cfPDFToPDFNupPageEdit; + +void _cfPDFToPDFNupPageEdit_dump(const _cfPDFToPDFNupPageEdit *nupPageEdit, + pdftopdf_doc_t *doc); + +typedef struct +{ + int first, second; +} integerPair; + +typedef struct +{ + _cfPDFToPDFNupParameters *param; + int in_pages, out_pages; + int nup; + int subpage; +} _cfPDFToPDFNupState; + +void _cfPDFToPDFNupState_init(_cfPDFToPDFNupState *nupState, + _cfPDFToPDFNupParameters *nupParam); +void _cfPDFToPDFNupState_reset(_cfPDFToPDFNupState *nupState); +integerPair _cfPDFToPDFNupState_convert_order(_cfPDFToPDFNupState *nupState, + int subpage); +void _cfPDFToPDFNupState_calculate_edit(const _cfPDFToPDFNupState *nupState, + int subx, int suby, _cfPDFToPDFNupPageEdit *ret); +bool _cfPDFToPDFNupState_mext_page(_cfPDFToPDFNupState *nupState, + float in_width, float in_height, + _cfPDFToPDFNupPageEdit *ret); + +typedef struct +{ + pdftopdf_axis_e first; + pdftopdf_position_e second; +} resultPair; + +bool _cfPDFToPDFParseNupLayout(const char *val, _cfPDFToPDFNupParameters *ret); diff --git a/cupsfilters/pdftopdf/C-nup.c b/cupsfilters/pdftopdf/C-nup.c new file mode 100644 index 00000000..24dec357 --- /dev/null +++ b/cupsfilters/pdftopdf/C-nup.c @@ -0,0 +1,377 @@ +#include +#include "C-nup-private.h" +#include +#include +#include +#include + +void +_cfPDFToPDFNupParameters_init(_cfPDFToPDFNupParameters *nupParam) // {{{ +{ + nupParam->nupX = 1; + nupParam->nupY = 1; + nupParam->width = NAN; + nupParam->height = NAN; + nupParam->landscape = false; + nupParam->first = X; + nupParam->xstart = LEFT; + nupParam->ystart = TOP; + nupParam->xalign = CENTER; + nupParam->yalign = CENTER; +} +// }}} + +void +_cfPDFToPDFNupParameters_dump(const _cfPDFToPDFNupParameters *nupParam, + pdftopdf_doc_t *doc) // {{{ +{ + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: NupX: %d, NupY: %d, " + "width: %f, height: %f", + nupParam->nupX, nupParam->nupY, + nupParam->width, nupParam->height); + int opos = -1, + fpos = -1, + spos = -1; + + if (nupParam->xstart == LEFT) + fpos = 0; + else if (nupParam->xstart == RIGHT) + fpos = 1; + + if (nupParam->ystart == LEFT) + spos = 0; + else if (nupParam->ystart == RIGHT) + spos = 1; + + if(nupParam->first == X) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: First Axis: X"); + opos = 0; + } + else if(nupParam->first == Y) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: First Axis: Y"); + opos = 0; + int temp = fpos; + fpos = spos; + spos = temp; + } + + if ((opos == -1) || (fpos == -1) || (spos == -1)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Bad Spec: %d; start: %d, %d", + nupParam->first, nupParam->xstart, nupParam->ystart); + } + else + { + static const char *order[4] = {"lr", "rl", "bt", "tb"}; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Order: %s%s", + order[opos + fpos], + order[(opos + 2) % 4 + spos]); + } + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "fFilterPDFToPDF: Alignment:"); + + _cfPDFToPDFPositionAndAxisDump(nupParam->xalign, X, doc); + _cfPDFToPDFPositionAndAxisDump(nupParam->yalign, Y, doc); +} +// }}} + +bool +_cfPDFToPDFNupParameters_possible(int nup) // {{{ +{ + return ((nup >= 1) && (nup <= 16) && + ((nup != 5) && (nup != 7) && (nup != 11) && (nup != 13) && + (nup != 14))); +} +// }}} + +void _cfPDFToPDFNupParameters_preset(int nup, + _cfPDFToPDFNupParameters *ret) // {{{ +{ + switch(nup) + { + case 1: + ret->nupX=1; + ret->nupY=1; + break; + case 2: + ret->nupX=2; + ret->nupY=1; + ret->landscape=true; + break; + case 3: + ret->nupX=3; + ret->nupY=1; + ret->landscape=true; + break; + case 4: + ret->nupX=2; + ret->nupY=2; + break; + case 6: + ret->nupX=3; + ret->nupY=2; + ret->landscape=true; + break; + case 8: + ret->nupX=4; + ret->nupY=2; + ret->landscape=true; + break; + case 9: + ret->nupX=3; + ret->nupY=3; + break; + case 10: + ret->nupX=5; + ret->nupY=2; + ret->landscape=true; + break; + case 12: + ret->nupX=3; + ret->nupY=4; + break; + case 15: + ret->nupX=5; + ret->nupY=3; + ret->landscape=true; + break; + case 16: + ret->nupX=4; + ret->nupY=4; + break; + } +} +// }}} + +void +_cfPDFToPDFNupPageEdit_dump(const _cfPDFToPDFNupPageEdit *nupPageEdit, + pdftopdf_doc_t *doc) // {{{ +{ + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: xpos: %f, ypos: %f, scale: %f", + nupPageEdit->xpos, nupPageEdit->ypos, + nupPageEdit->scale); + _cfPDFToPDFPageRect_dump(nupPageEdit->sub, doc); +} +// }}} + +void +_cfPDFToPDFNupState_init(_cfPDFToPDFNupState *nupState, + _cfPDFToPDFNupParameters *nupParam) // {{{ +{ + nupState->param->nupX = nupParam->nupX; + nupState->param->nupY = nupParam->nupY; + nupState->param->width = nupParam->width; + nupState->param->height = nupParam->height; + nupState->param->landscape = nupParam->landscape; + nupState->param->first = nupParam->first; + nupState->param->xstart = nupParam->xstart; + nupState->param->ystart = nupParam->ystart; + nupState->param->xalign = nupParam->xalign; + nupState->param->yalign = nupParam->yalign; + + nupState->in_pages = 0; + nupState->out_pages = 0; + nupState->nup = nupParam->nupX * nupParam->nupY; + nupState->subpage = nupState->nup; +} +// }}} + +void +_cfPDFToPDFNupState_reset(_cfPDFToPDFNupState *nupState) // {{{ +{ + nupState->in_pages = 0; + nupState->out_pages = 0; + nupState->subpage = nupState->nup; +} +// }}} + +integerPair +_cfPDFToPDFNupState_convert_order(_cfPDFToPDFNupState *nupState, + int subpage) // {{{ +{ + int subx, suby; + + if(nupState->param->first == X) + { + subx = nupState->subpage % nupState->param->nupX; + suby = nupState->subpage / nupState->param->nupX; + } + else + { + subx = nupState->subpage / nupState->param->nupY; + suby = nupState->subpage % nupState->param->nupY; + } + + subx = (nupState->param->nupX - 1) * (nupState->param->xstart+1) / 2 - nupState->param->xstart * subx; + suby = (nupState->param->nupY - 1) * (nupState->param->ystart+1) / 2 - nupState->param->ystart * suby; + + integerPair intPair; + + intPair.first = subx; + intPair.second = suby; + + return intPair; +} +// }}} + +static inline float +lin(pdftopdf_position_e pos, + float size) // {{{ +{ + if(pos == -1) + return (0); + else if(pos == 0) + return (size/2); + else if(pos == 1) + return (size); + + return (size * (pos+1) / 2); +} +// }}} + +void +_cfPDFToPDFNupState_calculate_edit(const _cfPDFToPDFNupState *nupState, + int subx, int suby, + _cfPDFToPDFNupPageEdit *ret) // {{{ +{ + const float width = nupState->param->width / nupState->param->nupX, + height = nupState->param->height / nupState->param->nupY; + + ret->xpos = subx * width; + ret->ypos = suby * height; + + const float scalex = width / ret->sub->width, + scaley = height / ret->sub->height; + + float subwidth = ret->sub->width * scaley, + subheight = ret->sub->height * scalex; + + if (scalex > scaley) + { + ret->scale = scaley; + subheight = height; + ret->xpos += lin(nupState->param->xalign, height - subwidth); + } + + else + { + ret->scale = scalex; + subwidth = width; + ret->ypos += lin(nupState->param->yalign, height + subheight); + } + + ret->sub->left = ret->xpos; + ret->sub->bottom = ret->ypos; + ret->sub->right = ret->sub->left + subwidth; + ret->sub->top = ret->sub->bottom + subheight; +} +// }}} + +bool +_cfPDFToPDFNupState_mext_page(_cfPDFToPDFNupState *nupState, + float in_width, float in_height, + _cfPDFToPDFNupPageEdit *ret) // {{{ +{ + nupState->in_pages++; + nupState->subpage++; + if(nupState->subpage >= nupState->nup) + { + nupState->subpage = 0; + nupState->out_pages++; + } + + ret->sub->width = in_width; + ret->sub->height = in_height; + + integerPair sub = _cfPDFToPDFNupState_convert_order(nupState, + nupState->subpage); + + _cfPDFToPDFNupState_calculate_edit(nupState, sub.first, sub.second, ret); + + return (nupState->subpage == 0); +} +// }}} + +resultPair* +parsePosition(char a, + char b) // {{{ +{ + a |= 0x20; + b |= 0x20; + resultPair *returnPair = (resultPair*)malloc(sizeof(resultPair));; + if((a == 'l') && (b == 'r')) + { + returnPair->first = X; + returnPair->second = LEFT; + return (returnPair); + } + + else if((a == 'r') && (b == 'l')) + { + returnPair->first = X; + returnPair->second = RIGHT; + return (returnPair); + } + + else if((a == 't') && (b == 'b')) + { + returnPair->first = Y; + returnPair->second = TOP; + return (returnPair); + } + + else if((a == 'b') && (b == 't')) + { + returnPair->first = Y; + returnPair->second = BOTTOM; + return (returnPair); + } + + returnPair->first = X; + returnPair->second = CENTER; + return(returnPair); +} +// }}} + +bool +_cfPDFToPDFParseNupLayout(const char *val, + _cfPDFToPDFNupParameters *ret) // {{{ +{ + resultPair *pos0 = parsePosition(val[0], val[1]); + + if(pos0->second == CENTER) + return (false); + + resultPair *pos1 = parsePosition(val[2], val[3]); + + if((pos1->second == CENTER) || (pos0->first == pos1->first)) + { + return (false); + } + + ret->first = pos0->first; + + if(ret->first == X) + { + ret->xstart = pos0->second; + ret->ystart = pos1->second; + } + + else + { + ret->xstart = pos1->second; + ret->ystart = pos0->second; + } + + return (val[4] == 0); +} +// }}} From 510c3488f4cb13644b0892a16542a54ef9e39fe1 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Tue, 9 Jul 2024 11:24:20 +0530 Subject: [PATCH 28/64] C-intervalset and header added --- Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.am b/Makefile.am index d30d6a2f..614de16c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -178,6 +178,8 @@ libcupsfilters_la_SOURCES = \ cupsfilters/pdftopdf/C-pptypes-private.h \ cupsfilters/pdftopdf/C-nup.c \ cupsfilters/pdftopdf/C-nup-private.h \ + cupsfilters/pdftopdf/C-intervalset.c \ + cupsfilters/pdftopdf/C-intervalset-private.h \ cupsfilters/pdftopdf/pdfio-tools.c \ cupsfilters/pdftopdf/pdfio-tools-private.h \ cupsfilters/pdftopdf/pdfio-xobject.c \ From a35010c88d9b8e8bc0c1852674af6e018edfab49 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Tue, 9 Jul 2024 11:25:29 +0530 Subject: [PATCH 29/64] intervalset files converted to C format --- cupsfilters/pdftopdf/C-intervalset-private.h | 33 ++++ cupsfilters/pdftopdf/C-intervalset.c | 170 +++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 cupsfilters/pdftopdf/C-intervalset-private.h create mode 100644 cupsfilters/pdftopdf/C-intervalset.c diff --git a/cupsfilters/pdftopdf/C-intervalset-private.h b/cupsfilters/pdftopdf/C-intervalset-private.h new file mode 100644 index 00000000..993f7ba7 --- /dev/null +++ b/cupsfilters/pdftopdf/C-intervalset-private.h @@ -0,0 +1,33 @@ +#include "pdftopdf-private.h" +#include +#include +#include + +typedef int key_t; +typedef uint16_t data_t; + +typedef struct { + key_t first; + key_t end; +} value_t; + +typedef struct { + value_t *data; + size_t size; + size_t capacity; +} _cfPDFToPDFIntervalSet; + + + +extern const key_t npos; + +void _cfPDFToPDFIntervalSet_init(_cfPDFToPDFIntervalSet *set); +void _cfPDFToPDFIntervalSet_clear(_cfPDFToPDFIntervalSet *set); +void _cfPDFToPDFIntervalSet_add(_cfPDFToPDFIntervalSet *set, key_t start, key_t end); +void _cfPDFToPDFIntervalSet_finish(_cfPDFToPDFIntervalSet *set); + +size_t _cfPDFToPDFIntervalSet_size(const _cfPDFToPDFIntervalSet *vec) { return vec->size; } + +bool _cfPDFToPDFIntervalSet_contains(const _cfPDFToPDFIntervalSet *set, key_t val); +key_t interval_set_next(const _cfPDFToPDFIntervalSet *set, key_t val); +void _cfPDFToPDFIntervalSet_dump(const _cfPDFToPDFIntervalSet *set, pdftopdf_doc_t *doc); diff --git a/cupsfilters/pdftopdf/C-intervalset.c b/cupsfilters/pdftopdf/C-intervalset.c new file mode 100644 index 00000000..a2ee2e27 --- /dev/null +++ b/cupsfilters/pdftopdf/C-intervalset.c @@ -0,0 +1,170 @@ +#include "C-intervalset-private.h" +#include +#include "cupsfilters/debug-internal.h" +#include +#include + +const key_t npos = INT_MAX; + +void +_cfPDFToPDFIntervalSet_init(_cfPDFToPDFIntervalSet *set) +{ + set->data = NULL; + set->size = 0; + set->capacity = 0; +} + +void +_cfPDFToPDFIntervalSet_clear(_cfPDFToPDFIntervalSet *set) +{ + free(set->data); + set->data = NULL; + set->size = 0; + set->capacity = 0; + +} + +void +_cfPDFToPDFIntervalSet_add(_cfPDFToPDFIntervalSet *set, + key_t start, + key_t end) +{ + if (start < end) + { + if (set->size == set->capacity) + { + set->capacity = set->capacity ? set->capacity * 2 : 1; + set->data = (value_t *)realloc(set->data, set->capacity * sizeof(value_t)); + } + + set->data[set->size].first = start; + set->data[set->size].end = end; + set->size++; + } +} + +//helper for qsort +static int compare_intervals(const void *a, const void *b) { + const value_t *interval_a = (const value_t *)a; + const value_t *interval_b = (const value_t *)b; + return (interval_a->first - interval_b->first); +} + +void +_cfPDFToPDFIntervalSet_finish(_cfPDFToPDFIntervalSet *set) +{ + if (set->size == 0) + return; + + // Sort the intervals + qsort(set->data, set->size, sizeof(_cfPDFToPDFIntervalSet), compare_intervals); + + size_t pos = 0; + for (size_t i = 1; i < set->size; ++i) + { + if (set->data[pos].end >= set->data[i].first) + { + if (set->data[pos].end < set->data[i].end) + set->data[pos].end = set->data[i].end; + } + + else + { + ++pos; + if (pos != i) + set->data[pos] = set->data[i]; + } + } + set->size = pos + 1; +} + +bool +_cfPDFToPDFIntervalSet_contains(const _cfPDFToPDFIntervalSet *set, + key_t val) +{ + for (size_t i = 0; i < set->size; ++i) + { + if (val >= set->data[i].first && val < set->data[i].end) + return true; + } + return false; +} + +key_t +_cfPDFToPDFIntervalSet_next(const _cfPDFToPDFIntervalSet *set, + key_t val) +{ + val++; + value_t key = {val, npos}; + value_t *it = (value_t *)bsearch(&key, set->data, set->size, + sizeof(value_t), compare_intervals); + + if (it == NULL) + { + for (size_t i = 0; i < set->size; ++i) + { + if (set->data[i].first > val) + return set->data[i].first; + + return npos; + } + + if (it == set->data) + { + if (it->first > val) + return it->first; + + return npos; + } + + --it; + if (val < it->end) + return val; + + + ++it; + if (it == set->data + set->size) + return npos; + + return it->first; + } +} + +void +_cfPDFToPDFIntervalSet_dump(const _cfPDFToPDFIntervalSet *set, + pdftopdf_doc_t *doc) +{ + int len = set->size; + if (len == 0) + { + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: (empty)"); + return; + } + + len--; + + for (int iA = 0; iA < len; iA++) + { + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: [%d,%d)", + set->data[iA].first, set->data[iA].end); + } + + if (set->data[len].end == npos) + { + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: [%d,inf)", + set->data[len].first); + } + else + { + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: [%d,%d)", + set->data[len].first, set->data[len].end); + } +} From 537a848c6f4213bf3feff6d8f0d769d7220a3d5c Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 12 Aug 2024 15:49:53 +0530 Subject: [PATCH 30/64] updated function definitions --- cupsfilters/pdftopdf/pdfio-cm.c | 130 +++++++++++++++++--------------- 1 file changed, 71 insertions(+), 59 deletions(-) diff --git a/cupsfilters/pdftopdf/pdfio-cm.c b/cupsfilters/pdftopdf/pdfio-cm.c index 9857301f..f57c388c 100644 --- a/cupsfilters/pdftopdf/pdfio-cm.c +++ b/cupsfilters/pdftopdf/pdfio-cm.c @@ -1,93 +1,105 @@ -#include "pdfio-cm-private.h" #include -#include "cupsfilters/debug-internal.h" +#include #include #include -#include +#include +#include +#include #include bool -_cfPDFToPDFHasOutputIntent(pdfio_file_t *pdf) // {{{ +_cfPDFToPDFHasOutputIntent(pdfio_file_t *pdf) { - pdfio_dict_t *catalogDict = pdfioFileGetCatalog(pdf); - if(!pdfioDictGetArray(catalogDict, "OutputIntents")) + pdfio_dict_t *catalog = pdfioFileGetCatalog(pdf); + if(!pdfioDictGetArray(catalog, "OutputIntents")) return false; - return true; + return true; } -// }}} void _cfPDFToPDFAddOutputIntent(pdfio_file_t *pdf, - const char *filename) //{{{ + const char *filename) { pdfio_obj_t *outicc = pdfioFileCreateICCObjFromFile(pdf, filename, 4); - + pdfio_dict_t *intent = pdfioDictCreate(pdf); - pdfioDictSetName(intent, "Type", "OutputIntent"); pdfioDictSetName(intent, "S", "GTS_PDFX"); - pdfioDictSetString(intent, "OutputCondition", "Commercial and specialty printing"); - pdfioDictSetString(intent, "Info", "none"); - pdfioDictSetString(intent, "OutputConditionIdentifier", "CGATS TR001"); - pdfioDictSetName(intent, "RegistryName", "http://www.color.org"); - pdfioDictSetNull(intent, "DestOutputProfile"); - + pdfioDictSetString(intent, "OutputCondition", "(Commercial and specialty printing)"); + pdfioDictSetString(intent, "Info", "(none)"); + pdfioDictSetString(intent, "OutputConditionIdentifier", "(CGATS TR001i)"); + pdfioDictSetString(intent, "RegistryName", "(http://www.color.orgi)"); pdfioDictSetObj(intent, "DestOutputProfile", outicc); - - pdfio_dict_t *catalogDict = pdfioFileGetCatalog(pdf); - if(!pdfioDictGetArray(catalogDict, "OutputIntents")) + pdfio_dict_t *catalog = pdfioFileGetCatalog(pdf); + pdfio_array_t *outputIntents = pdfioDictGetArray(catalog, "OutputIntents"); + if (!outputIntents) { - pdfio_array_t *newArray = pdfioArrayCreate(pdf); - pdfioDictSetArray(catalogDict, "OutputIntents", newArray); + outputIntents = pdfioArrayCreate(pdf); + pdfioDictSetArray(catalog, "OutputIntents", outputIntents); } - pdfioDictSetArray(intent, "OutputIntents", pdfioDictGetArray(catalogDict, "OutputIntents")); + + pdfioArrayAppendDict(outputIntents, intent); } -// }}} -void _cfPDFToPDFAddDefaultRGB(pdfio_file_t *pdf, - pdfio_stream_t *srcicc) // {{{ +// +// for color management: +// Use /DefaultGray, /DefaultRGB, /DefaultCMYK ... from *current* resource +// dictionary ... +// i.e. set +// /Resources << +// /ColorSpace << --- can use just one indirect ref for this (probably) +// /DefaultRGB [/ICCBased 5 0 R] ... sensible use is sRGB for DefaultRGB, etc. +// >> +// >> +// for every page (what with form /XObjects?) and most importantly RGB +// (leave CMYK, Gray for now, as this is already printer native(?)) +// +// ? also every form XObject, pattern, type3 font, annotation appearance +// stream(=form xobject +X) +// +// ? what if page already defines /Default? -- probably keep! +// +// ? maybe we need to set /ColorSpace in /Images ? +// [gs idea is to just add the /Default-key and then reprocess...] + +void +_cfPDFToPDFAddDefaultRGB(pdfio_file_t *pdf, + pdfio_obj_t *icc_obj) { + pdfio_array_t *icc_array = pdfioArrayCreate(pdf); + + pdfioArrayAppendName(icc_array, "ICCBased"); + pdfioArrayAppendObj(icc_array, icc_obj); - int numPages = pdfioFileGetNumPages(pdf); - for(int count=0; count Date: Mon, 12 Aug 2024 15:52:49 +0530 Subject: [PATCH 31/64] updated the function to used latest apis --- cupsfilters/pdftopdf/pdfio-pdftopdf-private.h | 63 +-- cupsfilters/pdftopdf/pdfio-pdftopdf.c | 406 +++++++----------- 2 files changed, 181 insertions(+), 288 deletions(-) diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h b/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h index b0625d26..f4992a40 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h @@ -1,61 +1,36 @@ -// -// -// -// -#include #include "C-pptypes-private.h" +#include +#include +#include -//helper functions +// helper functions _cfPDFToPDFPageRect _cfPDFToPDFGetBoxAsRect(pdfio_rect_t *box); -pdfio_rect_t _cfPDFToPDFGetRectAsBox(const _cfPDFToPDFPageRect *rect); +pdfio_rect_t* _cfPDFToPDFGetRectAsBox(_cfPDFToPDFPageRect *rect); // Note that PDF specification is CW, but our Rotation is CCW pdftopdf_rotation_e _cfPDFToPDFGetRotate(pdfio_obj_t *page); -int _cfPDFToPDFMakeRotate(pdftopdf_rotation_e rot); // integer, change all calls +double _cfPDFToPDFMakeRotate(pdftopdf_rotation_e rot); double _cfPDFToPDFGetUserUnit(pdfio_obj_t *page); -//class -typedef struct -{ - double ctm[6]; +// PDF CTM +typedef struct { + double ctm[6]; } _cfPDFToPDFMatrix; -void _cfPDFToPDFMatrix_init(_cfPDFToPDFMatrix *matrix); - -void _cfPDFToPDFMatrix_init_with_handle(_cfPDFToPDFMatrix *matrix, - pdfio_array_t *ar); - -_cfPDFToPDFMatrix _cfPDFToPDFMatrix_rotate_function(_cfPDFToPDFMatrix *matrix, - pdftopdf_rotation_e rot); - -_cfPDFToPDFMatrix _cfPDFToPDFMatrix_rotate_move(_cfPDFToPDFMatrix *matrix, - pdftopdf_rotation_e rot, - double width, - double height); - -_cfPDFToPDFMatrix _cfPDFToPDFMatrix_rotate_rad(_cfPDFToPDFMatrix *matrix, - double rad); - -_cfPDFToPDFMatrix _cfPDFToPDFMatrix_translate(_cfPDFToPDFMatrix *matrix, - double tx, - double ty); - -_cfPDFToPDFMatrix _cfPDFToPDFMatrix_scale(_cfPDFToPDFMatrix *matrix, - double sx, - double sy); - -_cfPDFToPDFMatrix _cfPDFToPDFMatrix_scale_uniform(_cfPDFToPDFMatrix *matrix, - double s); - - -_cfPDFToPDFMatrix _cfPDFToPDFMatrix_multiply(_cfPDFToPDFMatrix *matrix, - const _cfPDFToPDFMatrix *rhs); +void _cfPDFToPDFMatrix_init(_cfPDFToPDFMatrix *matrix); // identity +void _cfPDFToPDFMatrix_init_with_array(_cfPDFToPDFMatrix *matrix, pdfio_array_t *array); -pdfio_array_t *_cfPDFToPDFMatrix_get_array(const _cfPDFToPDFMatrix *matrix); +void _cfPDFToPDFMatrix_rotate(_cfPDFToPDFMatrix *matrix, pdftopdf_rotation_e rot); +void _cfPDFToPDFMatrix_rotate_move(_cfPDFToPDFMatrix *matrix, pdftopdf_rotation_e rot, double width, double height); +void _cfPDFToPDFMatrix_rotate_rad(_cfPDFToPDFMatrix *matrix, double rad); -char _cfPDFToPDFMatrix_get_string(const _cfPDFToPDFMatrix *matrix); +void _cfPDFToPDFMatrix_translate(_cfPDFToPDFMatrix *matrix, double tx, double ty); +void _cfPDFToPDFMatrix_scale(_cfPDFToPDFMatrix *matrix, double sx, double sy); +void _cfPDFToPDFMatrix_multiply(_cfPDFToPDFMatrix *lhs, const _cfPDFToPDFMatrix *rhs); +void _cfPDFToPDFMatrix_get(const _cfPDFToPDFMatrix *matrix, double *array); +void _cfPDFToPDFMatrix_get_string(const _cfPDFToPDFMatrix *matrix, char *buffer, size_t bufsize); diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf.c b/cupsfilters/pdftopdf/pdfio-pdftopdf.c index a52293c0..87d61653 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf.c +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf.c @@ -1,305 +1,223 @@ -// -// -// -// + #include "pdfio-pdftopdf-private.h" -#include "pdfio-tools-private.h" #include "cupsfilters/debug-internal.h" -#include -#include +#include #include -#include +#include "pdfio-tools-private.h" -_cfPDFToPDFPageRect -_cfPDFToPDFGetBoxAsRect(pdfio_rect_t *box) +_cfPDFToPDFPageRect +_cfPDFToPDFGetBoxAsRect(pdfio_rect_t *box) { - _cfPDFToPDFPageRect ret; + _cfPDFToPDFPageRect ret; - ret.left = box->x1; - ret.bottom = box->y1; - ret.right = box->x2; - ret.top = box->y2; + ret.left = box->x1; + ret.bottom = box->y1; + ret.right = box->x2; + ret.top = box->y2; - ret.width = ret.right - ret.left; - ret.height = ret.top - ret.bottom; + ret.width = ret.right - ret.left; + ret.height = ret.top - ret.bottom; - return ret; + return ret; } -pdfio_rect_t -_cfPDFToPDFGetRectAsBox(const _cfPDFToPDFPageRect *rect) +pdfio_rect_t* +_cfPDFToPDFGetRectAsBox(_cfPDFToPDFPageRect *rect) { - return (_cfPDFToPDFMakeBox(rect->left, rect->bottom, rect->right, rect->top)); + return (_cfPDFToPDFMakeBox(rect->left, rect->bottom, rect->right, rect->top)); } - -pdftopdf_rotation_e -_cfPDFToPDFGetRotate(pdfio_obj_t *page) +pdftopdf_rotation_e +_cfPDFToPDFGetRotate(pdfio_obj_t *page) { - pdfio_dict_t *page_dict = pdfioObjGetDict(page); - if(!pdfioDictGetNumber(page_dict, "Rotate")) - return (ROT_0); - double rot = pdfioDictGetNumber(page_dict, "Rotate"); - - rot = fmod(rot, 360.0); - if (rot<0) - rot += 360.0; - else if (rot == 90.0) - return (ROT_270); - else if (rot == 180.0) - return (ROT_180); - else if (rot == 270.0) - return (ROT_90); - else if (rot != 0.0) - { - char str[100]; - sprintf(str, "%f", rot); - printf("Unexpected /Rotate value: %s\n", str); - return -1; - } - return (ROT_0); -} - -int -_cfPDFToPDFMakeRotate(pdftopdf_rotation_e rot) -{ - switch(rot) - { - case ROT_0: - return 0; - - case ROT_90: - return 270; - - case ROT_180: - return 180; - - case ROT_270: - return 90; + pdfio_dict_t *pageDict = pdfioObjGetDict(page); + double rotate = pdfioDictGetNumber(pageDict, "Rotate"); + if (!rotate) + return ROT_0; - default: - printf("Bad Rotation\n"); - return -1; - } - + double rot = fmod(rotate, 360.0); + if (rot < 0) + rot += 360.0; + if (rot == 90.0) // CW + return ROT_270; // CCW + else if (rot == 180.0) + return ROT_180; + else if (rot == 270.0) + return ROT_90; + else if (rot != 0.0) + fprintf(stderr, "Unexpected /Rotate value: %f\n", rot); + + return ROT_0; } double -_cfPDFToPDFGetUserUnit(pdfio_obj_t *page) +_cfPDFToPDFGetUserUnit(pdfio_obj_t *page) { - pdfio_dict_t *page_dict = pdfioObjGetDict(page); - if(!pdfioDictGetNumber(page_dict, "UserUnit")) - return 1.0; + pdfio_dict_t *pageDict = pdfioObjGetDict(page); + double userUnit = pdfioDictGetNumber(pageDict, "UserUnit"); + if(!userUnit) + return 1.0; + return userUnit; +} - int userUnit_value = pdfioDictGetNumber(page_dict, "UserUnit"); - return userUnit_value; +double +_cfPDFToPDFMakeRotate(pdftopdf_rotation_e rot) +{ + switch (rot) + { + case ROT_0: + return 0; + case ROT_90: + return 270.0; + case ROT_180: + return 180.0; + case ROT_270: + return 90.0; + default: + fprintf(stderr, "Bad rotation value\n"); + return NAN; + } } void _cfPDFToPDFMatrix_init(_cfPDFToPDFMatrix *matrix) { - matrix->ctm[0] = 1; - matrix->ctm[1] = 0; - matrix->ctm[2] = 0; - matrix->ctm[3] = 1; - matrix->ctm[4] = 0; - matrix->ctm[5] = 0; + matrix->ctm[0] = 1.0; + matrix->ctm[1] = 0.0; + matrix->ctm[2] = 0.0; + matrix->ctm[3] = 1.0; + matrix->ctm[4] = 0.0; + matrix->ctm[5] = 0.0; } void -_cfPDFToPDFMatrix_init_with_handle(_cfPDFToPDFMatrix *matrix, - pdfio_array_t *ar) +_cfPDFToPDFMatrix_init_with_array(_cfPDFToPDFMatrix *matrix, + pdfio_array_t *array) { - if( pdfioArrayGetSize(ar) != 6) - printf("Not a ctm Matrix\n"); - for (int iA = 0; iA<6 ; iA++) - matrix->ctm[iA] = pdfioArrayGetNumber(ar, iA); + if (pdfioArrayGetSize(array) != 6) + fprintf(stderr, "Not a ctm matrix"); + + for (int iA = 0; iA < 6; iA ++) + matrix->ctm[iA] = pdfioArrayGetNumber(array, iA); } -_cfPDFToPDFMatrix -_cfPDFToPDFMatrix_rotate_function(_cfPDFToPDFMatrix *matrix, - pdftopdf_rotation_e rot) -{ - switch (rot) - { - case ROT_0: - break; - case ROT_90: - double temp = matrix->ctm[0]; - matrix->ctm[0] = matrix->ctm[2]; - matrix->ctm[2] = temp; - - temp = matrix->ctm[1]; - matrix->ctm[1] = matrix->ctm[3]; - matrix->ctm[3] = temp; - - matrix->ctm[2] = -(matrix->ctm[2]); - matrix->ctm[3] = -(matrix->ctm[3]); - break; - - case ROT_180: - matrix->ctm[0] = -(matrix->ctm[0]); - matrix->ctm[3] = -(matrix->ctm[3]); - break; - - case ROT_270: - temp = matrix->ctm[0]; - matrix->ctm[0] = matrix->ctm[2]; - matrix->ctm[2] = temp; - - temp = matrix->ctm[1]; - matrix->ctm[1] = matrix->ctm[3]; - matrix->ctm[3] = temp; - - matrix->ctm[0] = -(matrix->ctm[0]); - matrix->ctm[1] = -(matrix->ctm[1]); - break; - - default: - DEBUG_assert(0); - } - return *matrix; //right now we don't know the full extent on this function, - //can even return the funtion as void. +void +_cfPDFToPDFMatrix_rotate(_cfPDFToPDFMatrix *matrix, + pdftopdf_rotation_e rot) +{ + double tmp[6]; + memcpy(tmp, matrix->ctm, sizeof(tmp)); + + switch (rot) + { + case ROT_0: + break; + case ROT_90: + matrix->ctm[0] = tmp[2]; + matrix->ctm[1] = tmp[3]; + matrix->ctm[2] = -tmp[0]; + matrix->ctm[3] = -tmp[1]; + break; + case ROT_180: + matrix->ctm[0] = -tmp[0]; + matrix->ctm[3] = -tmp[3]; + break; + case ROT_270: + matrix->ctm[0] = -tmp[2]; + matrix->ctm[1] = -tmp[3]; + matrix->ctm[2] = tmp[0]; + matrix->ctm[3] = tmp[1]; + break; + default: + DEBUG_assert(0); + } } -_cfPDFToPDFMatrix +void _cfPDFToPDFMatrix_rotate_move(_cfPDFToPDFMatrix *matrix, pdftopdf_rotation_e rot, - double width, - double height) -{ - _cfPDFToPDFMatrix_rotate_function(matrix, rot); - switch(rot) - { - case ROT_0: - break; - case ROT_90: - _cfPDFToPDFMatrix_translate(matrix, width, 0); - break; - case ROT_180: - _cfPDFToPDFMatrix_translate(matrix, width, height); - break; - case ROT_270: - _cfPDFToPDFMatrix_translate(matrix, 0, height); - break; - } - return (*matrix); //right now don't know the full scope of this function, - //can even return it as void + double width, double height) +{ + _cfPDFToPDFMatrix_rotate(matrix, rot); + switch (rot) + { + case ROT_0: + break; + case ROT_90: + _cfPDFToPDFMatrix_translate(matrix, width, 0); + break; + case ROT_180: + _cfPDFToPDFMatrix_translate(matrix, width, height); + break; + case ROT_270: + _cfPDFToPDFMatrix_translate(matrix, 0, height); + break; + } } -_cfPDFToPDFMatrix +void _cfPDFToPDFMatrix_rotate_rad(_cfPDFToPDFMatrix *matrix, - double rad) + double rad) { - _cfPDFToPDFMatrix tmp; - _cfPDFToPDFMatrix multiplied; - - tmp.ctm[0] = cos(rad); - tmp.ctm[1] = sin(rad); - tmp.ctm[2] = -sin(rad); - tmp.ctm[3] = cos(rad); + _cfPDFToPDFMatrix tmp; + _cfPDFToPDFMatrix_init(&tmp); - multiplied = _cfPDFToPDFMatrix_multiply(matrix, - &tmp); - return multiplied; -} + tmp.ctm[0] = cos(rad); + tmp.ctm[1] = sin(rad); + tmp.ctm[2] = -sin(rad); + tmp.ctm[3] = cos(rad); + _cfPDFToPDFMatrix_multiply(matrix, &tmp); +} -_cfPDFToPDFMatrix -_cfPDFToPDFMatrix_translate(_cfPDFToPDFMatrix *matrix, - double tx, - double ty) +void +_cfPDFToPDFMatrix_translate(_cfPDFToPDFMatrix *matrix, + double tx, double ty) { - matrix->ctm[4] += matrix->ctm[0] * tx + matrix->ctm[2] * ty; - matrix->ctm[5] += matrix->ctm[1] * tx + matrix->ctm[3] * ty; - return *matrix; + matrix->ctm[4] += matrix->ctm[0] * tx + matrix->ctm[2] * ty; + matrix->ctm[5] += matrix->ctm[1] * tx + matrix->ctm[3] * ty; } -_cfPDFToPDFMatrix +void _cfPDFToPDFMatrix_scale(_cfPDFToPDFMatrix *matrix, - double sx, - double sy) + double sx, double sy) { - matrix->ctm[0] *= sx; - matrix->ctm[1] *= sx; - matrix->ctm[2] *= sy; - matrix->ctm[3] *= sy; - - return *matrix; + matrix->ctm[0] *= sx; + matrix->ctm[1] *= sx; + matrix->ctm[2] *= sy; + matrix->ctm[3] *= sy; } -_cfPDFToPDFMatrix -_cfPDFToPDFMatrix_scale_uniform(_cfPDFToPDFMatrix *matrix, - double s) +void +_cfPDFToPDFMatrix_multiply(_cfPDFToPDFMatrix *lhs, + const _cfPDFToPDFMatrix *rhs) { - return (_cfPDFToPDFMatrix_scale(matrix, s, s)); -} + double tmp[6]; + memcpy(tmp, lhs->ctm, sizeof(tmp)); -_cfPDFToPDFMatrix -_cfPDFToPDFMatrix_multiply(_cfPDFToPDFMatrix *matrix, - const _cfPDFToPDFMatrix *rhs) -{ - double tmp[6]; - - for (int i = 0; i < 6; ++i) - { - tmp[i] = matrix->ctm[i]; - } + lhs->ctm[0] = tmp[0] * rhs->ctm[0] + tmp[2] * rhs->ctm[1]; + lhs->ctm[1] = tmp[1] * rhs->ctm[0] + tmp[3] * rhs->ctm[1]; - matrix->ctm[0] = tmp[0] * rhs->ctm[0] + tmp[2] * rhs->ctm[1]; - matrix->ctm[1] = tmp[1] * rhs->ctm[0] + tmp[3] * rhs->ctm[1]; - - matrix->ctm[2] = tmp[0] * rhs->ctm[2] + tmp[2] * rhs->ctm[3]; - matrix->ctm[3] = tmp[1] * rhs->ctm[2] + tmp[3] * rhs->ctm[3]; - - matrix->ctm[4] = tmp[0] * rhs->ctm[4] + tmp[2] * rhs->ctm[4] + tmp[4]; - matrix->ctm[5] = tmp[1] * rhs->ctm[4] + tmp[3] * rhs->ctm[5] + tmp[5]; + lhs->ctm[2] = tmp[0] * rhs->ctm[2] + tmp[2] * rhs->ctm[3]; + lhs->ctm[3] = tmp[1] * rhs->ctm[2] + tmp[3] * rhs->ctm[3]; - return *matrix; + lhs->ctm[4] = tmp[0] * rhs->ctm[4] + tmp[2] * rhs->ctm[5] + tmp[4]; + lhs->ctm[5] = tmp[1] * rhs->ctm[4] + tmp[3] * rhs->ctm[5] + tmp[5]; } -pdfio_array_t *_cfPDFToPDFMatrix_get_array(const _cfPDFToPDFMatrix *matrix) +void +_cfPDFToPDFMatrix_get(const _cfPDFToPDFMatrix *matrix, + double *array) { - pdfio_file_t *pdf = pdfioFileCreate("example.pdf", NULL, NULL, NULL, NULL, NULL); - pdfio_array_t *ret = pdfioArrayCreate(pdf); - - pdfioArrayAppendNumber(ret, matrix->ctm[0]); - pdfioArrayAppendNumber(ret, matrix->ctm[1]); - pdfioArrayAppendNumber(ret, matrix->ctm[2]); - pdfioArrayAppendNumber(ret, matrix->ctm[3]); - pdfioArrayAppendNumber(ret, matrix->ctm[4]); - pdfioArrayAppendNumber(ret, matrix->ctm[5]); - - pdfioFileClose(pdf); - return (ret); + memcpy(array, matrix->ctm, sizeof(double) * 6); } -char -_cfPDFToPDFMatrix_get_string(const _cfPDFToPDFMatrix *matrix) +void +_cfPDFToPDFMatrix_get_string(const _cfPDFToPDFMatrix *matrix, + char *buffer, size_t bufsize) { - // Helper function to convert double to string - void double_to_string(double value, char *str, size_t size) - { - snprintf(str, size, "%f", value); - } - - // Assuming each double string will not exceed 32 characters including the null terminator - char buffer[32]; - size_t total_size = 32 * 6 + 5; // 6 doubles and 5 spaces - char *ret = (char*)malloc(total_size); - - ret[0] = '\0'; // Initialize the string - - for (int i = 0; i < 6; ++i) - { - double_to_string(matrix->ctm[i], buffer, sizeof(buffer)); - strcat(ret, buffer); - if (i < 5) - { - strcat(ret, " "); - } - } - - return *ret; + snprintf(buffer, bufsize, "%f %f %f %f %f %f", + matrix->ctm[0], matrix->ctm[1], matrix->ctm[2], + matrix->ctm[3], matrix->ctm[4], matrix->ctm[5]); } From 7bbb9a09c39b2127abab4b48a6ec80658d2c0dea Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 12 Aug 2024 16:09:21 +0530 Subject: [PATCH 32/64] updated --- cupsfilters/pdftopdf/pdfio-tools-private.h | 15 +++++++++------ cupsfilters/pdftopdf/pdfio-tools.c | 17 +++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/cupsfilters/pdftopdf/pdfio-tools-private.h b/cupsfilters/pdftopdf/pdfio-tools-private.h index 8e5e5f5b..b4f96c9f 100644 --- a/cupsfilters/pdftopdf/pdfio-tools-private.h +++ b/cupsfilters/pdftopdf/pdfio-tools-private.h @@ -1,12 +1,12 @@ // +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. // -// -// -#ifndef _PDFIO_H_ -#define _PDFIO_H_ +#ifndef _CUPS_FILTERS_PDFTOPDF_QPDF_TOOLS_H_ +#define _CUPS_FILTERS_PDFTOPDF_QPDF_TOOLS_H_ + #include -#endif pdfio_rect_t _cfPDFToPDFGetMediaBox(pdfio_obj_t *page); pdfio_rect_t _cfPDFToPDFGetCropBox(pdfio_obj_t *page); @@ -14,4 +14,7 @@ pdfio_rect_t _cfPDFToPDFGetBleedBox(pdfio_obj_t *page); pdfio_rect_t _cfPDFToPDFGetTrimBox(pdfio_obj_t *page); pdfio_rect_t _cfPDFToPDFGetArtBox(pdfio_obj_t *page); -pdfio_rect_t _cfPDFToPDFMakeBox(double x1, double y1, double x2, double y2); +pdfio_rect_t* _cfPDFToPDFMakeBox(double x1, double y1, double x2, double y2); + +#endif // !_CUPS_FILTERS_PDFTOPDF_QPDF_TOOLS_H_ + diff --git a/cupsfilters/pdftopdf/pdfio-tools.c b/cupsfilters/pdftopdf/pdfio-tools.c index a28a76d5..88ec92ce 100644 --- a/cupsfilters/pdftopdf/pdfio-tools.c +++ b/cupsfilters/pdftopdf/pdfio-tools.c @@ -1,3 +1,8 @@ +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + #include "pdfio-tools-private.h" pdfio_rect_t @@ -55,17 +60,17 @@ _cfPDFToPDFGetArtBox(pdfio_obj_t *page) // {{{ } // }}} -pdfio_rect_t +pdfio_rect_t* _cfPDFToPDFMakeBox(double x1, double y1, double x2, double y2) // {{{ { - pdfio_rect_t ret; - ret.x1 = x1; - ret.y1 = y1; - ret.x2 = x2; - ret.y2 = y2; + pdfio_rect_t *ret; + ret->x1 = x1; + ret->y1 = y1; + ret->x2 = x2; + ret->y2 = y2; return ret; } From e1e9c5ca232099040ffba4f7f12b8862b43f3f82 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 12 Aug 2024 16:10:04 +0530 Subject: [PATCH 33/64] updated the code, testing remaining --- cupsfilters/pdftopdf/pdfio-xobject-private.h | 2 + cupsfilters/pdftopdf/pdfio-xobject.c | 177 ++++++++++++++----- 2 files changed, 138 insertions(+), 41 deletions(-) diff --git a/cupsfilters/pdftopdf/pdfio-xobject-private.h b/cupsfilters/pdftopdf/pdfio-xobject-private.h index cbfc663d..562a2182 100644 --- a/cupsfilters/pdftopdf/pdfio-xobject-private.h +++ b/cupsfilters/pdftopdf/pdfio-xobject-private.h @@ -1,3 +1,5 @@ #include pdfio_obj_t* _cfPDFToPDFMakeXObject(pdfio_file_t *pdf, pdfio_obj_t *page); + + diff --git a/cupsfilters/pdftopdf/pdfio-xobject.c b/cupsfilters/pdftopdf/pdfio-xobject.c index 0a5ea9cc..d0bb78de 100644 --- a/cupsfilters/pdftopdf/pdfio-xobject.c +++ b/cupsfilters/pdftopdf/pdfio-xobject.c @@ -1,62 +1,157 @@ #include "pdfio-xobject-private.h" #include "pdfio-tools-private.h" #include "pdfio-pdftopdf-private.h" +#include +#include +#include +#include + +typedef struct +{ + size_t content_count; + pdfio_stream_t **contents; +} CombineFromContents_Provider; + +CombineFromContents_Provider* +CombineFromContents_Provider_new(pdfio_stream_t **contents, + size_t content_count) +{ + CombineFromContents_Provider *provider = (CombineFromContents_Provider*)malloc(sizeof(CombineFromContents_Provider)); + + provider->content_count = content_count; + provider->contents = contents; + return provider; +} + +void +CombineFromContents_Provider_free(CombineFromContents_Provider *provider) +{ + if (provider) + { + free(provider->contents); + free(provider); + } +} + +void +CombineFromContents_Provider_provideStreamData(CombineFromContents_Provider *provider, + pdfio_stream_t *pipeline) +{ + char buffer[8192]; + size_t bytes; + + for (size_t i = 0; i < provider->content_count; i++) + { + pdfio_stream_t *stream = provider->contents[i]; + while ((bytes = pdfioStreamRead(stream, buffer, sizeof(buffer))) > 0) + { + pdfioStreamWrite(pipeline, buffer, bytes); + } + } +} pdfio_obj_t* -_cfPDFToPDFMakeXObject(pdfio_file_t *pdf, pdfio_obj_t *page) +_cfPDFToPDFMakeXObject(pdfio_file_t *pdf, + pdfio_obj_t *page) { - - pdfio_dict_t *dict = pdfioDictCreate(pdf); - pdfio_dict_t *pageDict = pdfioObjGetDict(page); + pdfio_dict_t *page_dict = pdfioObjGetDict(page); + + // Create the XObject dictionary + pdfio_dict_t *dict = pdfioDictCreate(pdf); + + if (!dict) { + fprintf(stderr, "Failed to create dictionary for XObject.\n"); + return NULL; + } + + pdfioDictSetName(dict, "Type", "XObject"); + pdfioDictSetName(dict, "Subtype", "Form"); - pdfioDictSetName(dict, "Type", "XObject"); - pdfioDictSetName(dict, "Subtype", "Form"); + // Set BBox from TrimBox or MediaBox + pdfio_rect_t box = _cfPDFToPDFGetTrimBox(page); + pdfioDictSetRect(dict, "BBox", &box); - pdfio_rect_t box = _cfPDFToPDFGetTrimBox(page); - pdfioDictSetRect(dict, "BBox", &box); + _cfPDFToPDFMatrix mtx; + _cfPDFToPDFMatrix_init(&mtx); - _cfPDFToPDFMatrix mtx; - _cfPDFToPDFMatrix_init(&mtx); - if(pdfioDictGetNumber(pageDict, "UserUnit")) - { - double valUserUnit = pdfioDictGetNumber(pageDict, "UserUnit"); - _cfPDFToPDFMatrix_scale_uniform(&mtx, valUserUnit); - } + double user_unit = _cfPDFToPDFGetUserUnit(page); + _cfPDFToPDFMatrix_scale(&mtx, user_unit, user_unit); - pdftopdf_rotation_e rot = _cfPDFToPDFGetRotate(page); + pdftopdf_rotation_e rot = _cfPDFToPDFGetRotate(page); - _cfPDFToPDFPageRect bbox, tmp; - _cfPDFToPDFPageRect_init(&bbox); - _cfPDFToPDFPageRect_init(&tmp); + _cfPDFToPDFPageRect bbox = _cfPDFToPDFGetBoxAsRect(&box), + tmp; + tmp.left = 0; + tmp.bottom = 0; + tmp.right = 0; + tmp.top = 0; - bbox = _cfPDFToPDFGetBoxAsRect(&box); + _cfPDFToPDFPageRect_rotate_move(&tmp, rot, bbox.width, bbox.height); + _cfPDFToPDFMatrix_translate(&mtx, tmp.left, tmp.bottom); - tmp.left = 0; - tmp.bottom = 0; - tmp.right = 0; - tmp.top = 0; + _cfPDFToPDFMatrix_rotate(&mtx, rot); + _cfPDFToPDFMatrix_translate(&mtx, -bbox.left, -bbox.bottom); - _cfPDFToPDFPageRect_rotate_move(&tmp, rot, bbox.width, bbox.height); + pdfio_array_t *matrix_array = pdfioArrayCreate(pdf); + for (int i = 0; i < 6; i++) + { + pdfioArrayAppendNumber(matrix_array, mtx.ctm[i]); + } + pdfioDictSetArray(dict, "Matrix", matrix_array); - _cfPDFToPDFMatrix_translate(&mtx, tmp.left, tmp.bottom); - _cfPDFToPDFMatrix_rotate_function(&mtx, rot); - _cfPDFToPDFMatrix_translate(&mtx, -(bbox.left), -(bbox.bottom)); + pdfio_obj_t *resources = pdfioDictGetObj(page_dict, "Resources"); + if (resources) + { + pdfioDictSetObj(dict, "Resources", resources); + } - pdfio_array_t *matrixArray = _cfPDFToPDFMatrix_get_array(&mtx); - pdfioDictSetArray(dict, "Matrix", matrixArray); + pdfio_obj_t *group = pdfioDictGetObj(page_dict, "Group"); + if (group) + { + pdfioDictSetObj(dict, "Group", group); + } - - pdfio_dict_t *tempDict = pdfioDictGetDict(pageDict, "Resources"); - pdfioDictSetDict(dict, "Resources", tempDict); + // Create the stream inside the XObject using pdfioFileCreateObj + pdfio_obj_t *xobject = pdfioFileCreateObj(pdf, dict); + if (!xobject) + { + fprintf(stderr, "Failed to create XObject.\n"); + return NULL; + } - if(pdfioDictGetObj(pageDict, "Group")) - { - pdfio_obj_t *groupObj = pdfioDictGetObj(pageDict, "Group"); - pdfioDictSetObj(dict, "Group", groupObj); - } - - pdfio_obj_t *returnObj = pdfioFileCreateObj(pdf, dict); + pdfio_stream_t **contents = NULL; + size_t content_count = pdfioPageGetNumStreams(page); - return returnObj; + for (size_t i = 0; i < content_count; i++) + { + contents[i] = pdfioPageOpenStream(page, i, false); + } + if (!contents || content_count == 0) + { + fprintf(stderr, "No valid contents streams found in the page dictionary.\n"); + return NULL; + } + + // Write the content streams to the XObject stream + pdfio_stream_t *xobject_stream = pdfioObjOpenStream(xobject, true); + + CombineFromContents_Provider *provider = CombineFromContents_Provider_new(contents, content_count); + if (provider) + { + CombineFromContents_Provider_provideStreamData(provider, xobject_stream); + CombineFromContents_Provider_free(provider); + } + else + { + fprintf(stderr, "Failed to create CombineFromContents_Provider.\n"); + pdfioStreamClose(xobject_stream); + return NULL; + } + + // Finalize the XObject stream and return the object + pdfioStreamClose(xobject_stream); + + return xobject; } + From f330868818001a1b5f8c434a65bfe58f2434b305 Mon Sep 17 00:00:00 2001 From: ThePhatak <111195860+uddhavphatak@users.noreply.github.com> Date: Mon, 12 Aug 2024 17:07:11 +0530 Subject: [PATCH 34/64] Update build.yaml --- .github/workflows/build.yaml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f0b947d1..f5ac15fb 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -42,6 +42,24 @@ jobs: sudo make install cd .. cd .. + - name: Install libpdfio > 1.3.0 + run: | + cd .. + mkdir pdfio + wget -O pdf-1.3.1.tar.gz https://github.com/michaelrsweet/pdfio/releases/download/v1.3.1/pdfio-1.3.1.tar.gz + tar -xzf pdf-1.3.1.tar.gz + cd pdfio-1.3.1 + mkdir build && + cd build && + cmake -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_STATIC_LIBS=OFF \ + -DCMAKE_INSTALL_DOCDIR=/usr/share/doc/pdfio-1.3.1 \ + .. && + make + sudo make install + cd .. + cd .. - name: Install poppler and mupdf run: | sudo apt install libpoppler-cpp-dev libpython3-dev libdbus-1-dev @@ -57,4 +75,4 @@ jobs: - name: make run: make - name: Run Tests - run: make check || cat test/error_log* \ No newline at end of file + run: make check || cat test/error_log* From ab7b4fe57f4dacf9bbda30456901503b1a2a13a9 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Tue, 13 Aug 2024 02:25:41 +0530 Subject: [PATCH 35/64] the Makebox function had error --- cupsfilters/pdftopdf/pdfio-tools.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cupsfilters/pdftopdf/pdfio-tools.c b/cupsfilters/pdftopdf/pdfio-tools.c index 88ec92ce..1b9e03e4 100644 --- a/cupsfilters/pdftopdf/pdfio-tools.c +++ b/cupsfilters/pdftopdf/pdfio-tools.c @@ -66,7 +66,7 @@ _cfPDFToPDFMakeBox(double x1, double x2, double y2) // {{{ { - pdfio_rect_t *ret; + pdfio_rect_t *ret = (pdfio_rect_t *)malloc(sizeof(pdfio_rect_t)); ret->x1 = x1; ret->y1 = y1; ret->x2 = x2; From 98dd10907c1a20a1cf9f7de8848831238b4b3c6b Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Tue, 13 Aug 2024 02:29:49 +0530 Subject: [PATCH 36/64] the function names weren't same as in header files --- cupsfilters/pdftopdf/pdfio-cm-private.h | 14 +++++++++++--- cupsfilters/pdftopdf/pdfio-cm.c | 22 +++++++++++++++++----- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/cupsfilters/pdftopdf/pdfio-cm-private.h b/cupsfilters/pdftopdf/pdfio-cm-private.h index a7b8ad25..5a85f61e 100644 --- a/cupsfilters/pdftopdf/pdfio-cm-private.h +++ b/cupsfilters/pdftopdf/pdfio-cm-private.h @@ -1,9 +1,17 @@ +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#ifndef _CUPS_FILTERS_PDFTOPDF_PDFIO_CM_H_ +#define _CUPS_FILTERS_PDFTOPDF_PDFIO_CM_H_ + #include bool _cfPDFToPDFHasOutputIntent(pdfio_file_t *pdf); void _cfPDFToPDFAddOutputIntent(pdfio_file_t *pdf, const char *filename); -void _cfPDFToPDFAddDefaultRGB(pdfio_file_t *pdf, pdfio_stream_t *srcicc); -pdfio_stream_t* _cfPDFToPDFSetDefaultICC(pdfio_file_t *pdf, const char *filename); - +void _cfPDFToPDFAddDefaultRGB(pdfio_file_t *pdf, pdfio_obj_t *icc_obj); +pdfio_obj_t* _cfPDFToPDFSetDefaultICC(pdfio_file_t *pdf, const char *filename); +#endif // !_CUPS_FILTERS_PDFTOPDF_PDFIO_CM_H_ diff --git a/cupsfilters/pdftopdf/pdfio-cm.c b/cupsfilters/pdftopdf/pdfio-cm.c index f57c388c..2502e6e6 100644 --- a/cupsfilters/pdftopdf/pdfio-cm.c +++ b/cupsfilters/pdftopdf/pdfio-cm.c @@ -1,4 +1,12 @@ +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#include "pdfio-cm-private.h" #include +#include "cupsfilters/debug-internal.h" + #include #include #include @@ -8,17 +16,18 @@ #include bool -_cfPDFToPDFHasOutputIntent(pdfio_file_t *pdf) +_cfPDFToPDFHasOutputIntent(pdfio_file_t *pdf) // {{{ { pdfio_dict_t *catalog = pdfioFileGetCatalog(pdf); if(!pdfioDictGetArray(catalog, "OutputIntents")) return false; return true; } +// }}} void _cfPDFToPDFAddOutputIntent(pdfio_file_t *pdf, - const char *filename) + const char *filename) // {{{ { pdfio_obj_t *outicc = pdfioFileCreateICCObjFromFile(pdf, filename, 4); @@ -41,6 +50,7 @@ _cfPDFToPDFAddOutputIntent(pdfio_file_t *pdf, pdfioArrayAppendDict(outputIntents, intent); } +// }}} // // for color management: @@ -62,10 +72,11 @@ _cfPDFToPDFAddOutputIntent(pdfio_file_t *pdf, // // ? maybe we need to set /ColorSpace in /Images ? // [gs idea is to just add the /Default-key and then reprocess...] +// void _cfPDFToPDFAddDefaultRGB(pdfio_file_t *pdf, - pdfio_obj_t *icc_obj) + pdfio_obj_t *icc_obj) // {{{ { pdfio_array_t *icc_array = pdfioArrayCreate(pdf); @@ -95,11 +106,12 @@ _cfPDFToPDFAddDefaultRGB(pdfio_file_t *pdf, pdfioDictSetArray(cdict, "DefaultRGB", icc_array); } } - +// }}} pdfio_obj_t* _cfPDFToPDFSetDefaultICC(pdfio_file_t *pdf, - const char *filename) + const char *filename) // {{{ { return pdfioFileCreateICCObjFromFile(pdf, filename, 3); } +// }}} From 611ecbf962966472509d44fca33a0f5c3ea8689a Mon Sep 17 00:00:00 2001 From: ThePhatak <111195860+uddhavphatak@users.noreply.github.com> Date: Fri, 16 Aug 2024 03:14:28 +0530 Subject: [PATCH 37/64] Update build.yaml --- .github/workflows/build.yaml | 155 ++++++++++++++++++++--------------- 1 file changed, 91 insertions(+), 64 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f5ac15fb..0ecf1005 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -3,76 +3,103 @@ name: Build and Test on: push: branches: - - '**' + - '**' pull_request: branches: - - '**' + - '**' jobs: build-linux-run-tests: - runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: show Ubuntu version - run: cat /etc/os-release | grep PRETTY_NAME | awk -F '=' '{print $2}' - - name: update build environment - run: sudo apt-get update --fix-missing -y && sudo apt-get upgrade --fix-missing -y - - name: install prerequisites - run: | - sudo apt-get install -y avahi-daemon libavahi-client-dev libssl-dev libpam-dev libusb-1.0-0-dev zlib1g-dev - sudo apt install autotools-dev autopoint cmake libtool pkg-config libcups2-dev libexif-dev liblcms2-dev libfontconfig1-dev - sudo apt install libfreetype6-dev build-essential qtbase5-dev qtchooser libcairo2-dev libboost-system-dev libboost-thread-dev libboost-program-options-dev libboost-test-dev libopenjp2-7-dev liblcms2-dev libjpeg-dev - - name: Install libqpdf > 11.0.0 - run: | - cd .. - mkdir qpdf - wget -O qpdf-11.6.3.tar.gz https://sourceforge.net/projects/qpdf/files/qpdf/11.6.3/qpdf-11.6.3.tar.gz - tar -xzf qpdf-11.6.3.tar.gz - cd qpdf-11.6.3 - mkdir build && - cd build && - cmake -DCMAKE_INSTALL_PREFIX=/usr \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_STATIC_LIBS=OFF \ - -DCMAKE_INSTALL_DOCDIR=/usr/share/doc/qpdf-11.6.3 \ - .. && - make - sudo make install - cd .. - cd .. - - name: Install libpdfio > 1.3.0 - run: | - cd .. - mkdir pdfio - wget -O pdf-1.3.1.tar.gz https://github.com/michaelrsweet/pdfio/releases/download/v1.3.1/pdfio-1.3.1.tar.gz - tar -xzf pdf-1.3.1.tar.gz - cd pdfio-1.3.1 - mkdir build && - cd build && - cmake -DCMAKE_INSTALL_PREFIX=/usr \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_STATIC_LIBS=OFF \ - -DCMAKE_INSTALL_DOCDIR=/usr/share/doc/pdfio-1.3.1 \ - .. && - make - sudo make install - cd .. - cd .. - - name: Install poppler and mupdf - run: | - sudo apt install libpoppler-cpp-dev libpython3-dev libdbus-1-dev - sudo apt install mupdf-tools - - - name: Install ghostscript - run: sudo apt install ghostscript + - uses: actions/checkout@v3 + + - name: Show Ubuntu Version + run: cat /etc/os-release | grep PRETTY_NAME | awk -F '=' '{print $2}' + + - name: Update Build Environment + run: sudo apt-get update --fix-missing -y && sudo apt-get upgrade --fix-missing -y + + - name: Install Prerequisites + run: | + sudo apt-get install -y \ + avahi-daemon \ + libavahi-client-dev \ + libssl-dev \ + libpam-dev \ + libusb-1.0-0-dev \ + zlib1g-dev \ + autotools-dev \ + autopoint \ + cmake \ + libtool \ + pkg-config \ + libcups2-dev \ + libexif-dev \ + liblcms2-dev \ + libfontconfig1-dev \ + libfreetype6-dev \ + build-essential \ + qtbase5-dev \ + qtchooser \ + libcairo2-dev \ + libboost-system-dev \ + libboost-thread-dev \ + libboost-program-options-dev \ + libboost-test-dev \ + libopenjp2-7-dev \ + liblcms2-dev \ + libjpeg-dev + + - name: Install libqpdf > 11.0.0 + run: | + wget https://sourceforge.net/projects/qpdf/files/qpdf/11.6.3/qpdf-11.6.3.tar.gz + tar -xzf qpdf-11.6.3.tar.gz + cd qpdf-11.6.3 + mkdir build + cd build + cmake -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_STATIC_LIBS=OFF \ + -DCMAKE_INSTALL_DOCDIR=/usr/share/doc/qpdf-11.6.3 \ + .. + make + sudo make install + + - name: Install libpdfio > 1.3.0 + run: | + wget https://github.com/michaelrsweet/pdfio/releases/download/v1.3.1/pdfio-1.3.1.tar.gz + tar -xzf pdfio-1.3.1.tar.gz + cd pdfio-1.3.1 + mkdir build + cd build + cmake -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_STATIC_LIBS=OFF \ + -DCMAKE_INSTALL_DOCDIR=/usr/share/doc/pdfio-1.3.1 \ + .. + make + sudo make install + + - name: Install poppler and mupdf + run: | + sudo apt-get install -y \ + libpoppler-cpp-dev \ + libpython3-dev \ + libdbus-1-dev \ + mupdf-tools + + - name: Install Ghostscript + run: sudo apt-get install -y ghostscript + + - name: Configure + env: + CC: /usr/bin/gcc + run: ./autogen.sh && ./configure --enable-debug + + - name: Build + run: make - - name: configure - env: - CC: /usr/bin/gcc - run: ./autogen.sh && ./configure --enable-debug - - name: make - run: make - - name: Run Tests - run: make check || cat test/error_log* + - name: Run Tests + run: make check || (test -f test/error_log* && cat test/error_log*) From ee0227025784805e13b9fda02b21f4159bbd96ec Mon Sep 17 00:00:00 2001 From: ThePhatak <111195860+uddhavphatak@users.noreply.github.com> Date: Fri, 16 Aug 2024 16:56:46 +0530 Subject: [PATCH 38/64] Update build.yaml for pdfio support --- .github/workflows/build.yaml | 134 +++++++++++------------------------ 1 file changed, 42 insertions(+), 92 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 0ecf1005..1230fa60 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -3,103 +3,53 @@ name: Build and Test on: push: branches: - - '**' + - '**' pull_request: branches: - - '**' + - '**' jobs: build-linux-run-tests: + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - - name: Show Ubuntu Version - run: cat /etc/os-release | grep PRETTY_NAME | awk -F '=' '{print $2}' - - - name: Update Build Environment - run: sudo apt-get update --fix-missing -y && sudo apt-get upgrade --fix-missing -y - - - name: Install Prerequisites - run: | - sudo apt-get install -y \ - avahi-daemon \ - libavahi-client-dev \ - libssl-dev \ - libpam-dev \ - libusb-1.0-0-dev \ - zlib1g-dev \ - autotools-dev \ - autopoint \ - cmake \ - libtool \ - pkg-config \ - libcups2-dev \ - libexif-dev \ - liblcms2-dev \ - libfontconfig1-dev \ - libfreetype6-dev \ - build-essential \ - qtbase5-dev \ - qtchooser \ - libcairo2-dev \ - libboost-system-dev \ - libboost-thread-dev \ - libboost-program-options-dev \ - libboost-test-dev \ - libopenjp2-7-dev \ - liblcms2-dev \ - libjpeg-dev - - - name: Install libqpdf > 11.0.0 - run: | - wget https://sourceforge.net/projects/qpdf/files/qpdf/11.6.3/qpdf-11.6.3.tar.gz - tar -xzf qpdf-11.6.3.tar.gz - cd qpdf-11.6.3 - mkdir build - cd build - cmake -DCMAKE_INSTALL_PREFIX=/usr \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_STATIC_LIBS=OFF \ - -DCMAKE_INSTALL_DOCDIR=/usr/share/doc/qpdf-11.6.3 \ - .. - make - sudo make install - - - name: Install libpdfio > 1.3.0 - run: | - wget https://github.com/michaelrsweet/pdfio/releases/download/v1.3.1/pdfio-1.3.1.tar.gz - tar -xzf pdfio-1.3.1.tar.gz - cd pdfio-1.3.1 - mkdir build - cd build - cmake -DCMAKE_INSTALL_PREFIX=/usr \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_STATIC_LIBS=OFF \ - -DCMAKE_INSTALL_DOCDIR=/usr/share/doc/pdfio-1.3.1 \ - .. - make - sudo make install - - - name: Install poppler and mupdf - run: | - sudo apt-get install -y \ - libpoppler-cpp-dev \ - libpython3-dev \ - libdbus-1-dev \ - mupdf-tools - - - name: Install Ghostscript - run: sudo apt-get install -y ghostscript - - - name: Configure - env: - CC: /usr/bin/gcc - run: ./autogen.sh && ./configure --enable-debug - - - name: Build - run: make - - - name: Run Tests - run: make check || (test -f test/error_log* && cat test/error_log*) + - uses: actions/checkout@v3 + - name: show Ubuntu version + run: cat /etc/os-release | grep PRETTY_NAME | awk -F '=' '{print $2}' + - name: update build environment + run: sudo apt-get update --fix-missing -y && sudo apt-get upgrade --fix-missing -y + - name: install prerequisites + run: | + sudo apt-get install -y avahi-daemon libavahi-client-dev libssl-dev libpam-dev libusb-1.0-0-dev zlib1g-dev + sudo apt install autotools-dev autopoint cmake libtool pkg-config libcups2-dev libexif-dev liblcms2-dev libfontconfig1-dev + sudo apt install libfreetype6-dev build-essential qtbase5-dev qtchooser libcairo2-dev libboost-system-dev libboost-thread-dev libboost-program-options-dev libboost-test-dev libopenjp2-7-dev liblcms2-dev libjpeg-dev + - name: Install pdfio >= 1.3.0 + run: | + cd .. + mkdir pdfio + wget https://github.com/michaelrsweet/pdfio/releases/download/v1.3.1/pdfio-1.3.1.tar.gz + tar -xzf pdfio-1.3.1.tar.gz + cd pdfio-1.3.1 + ./configure --prefix=/usr --enable-shared + make all + make test + sudo make install + cd .. + cd .. + - name: Install poppler and mupdf + run: | + sudo apt install libpoppler-cpp-dev libpython3-dev libdbus-1-dev + sudo apt install mupdf-tools + + - name: Install ghostscript + run: sudo apt install ghostscript + + - name: configure + env: + CC: /usr/bin/gcc + run: ./autogen.sh && ./configure --enable-debug + - name: make + run: make + - name: Run Tests + run: make check || cat test/error_log* From 9fcb1e8f6100b270ade8461bd026c7acc46952ff Mon Sep 17 00:00:00 2001 From: ThePhatak <111195860+uddhavphatak@users.noreply.github.com> Date: Fri, 16 Aug 2024 17:11:23 +0530 Subject: [PATCH 39/64] Update build.yaml --- .github/workflows/build.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 1230fa60..db9bdc47 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -37,6 +37,24 @@ jobs: sudo make install cd .. cd .. + - name: Install libqpdf > 11.0.0 + run: | + cd .. + mkdir qpdf + wget -O qpdf-11.6.3.tar.gz https://sourceforge.net/projects/qpdf/files/qpdf/11.6.3/qpdf-11.6.3.tar.gz + tar -xzf qpdf-11.6.3.tar.gz + cd qpdf-11.6.3 + mkdir build && + cd build && + cmake -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_STATIC_LIBS=OFF \ + -DCMAKE_INSTALL_DOCDIR=/usr/share/doc/qpdf-11.6.3 \ + .. && + make + sudo make install + cd .. + cd .. - name: Install poppler and mupdf run: | sudo apt install libpoppler-cpp-dev libpython3-dev libdbus-1-dev From 67cb72f67c2b5e0cdd8b6e579e2dd95a604501fb Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Thu, 5 Sep 2024 18:23:54 +0530 Subject: [PATCH 40/64] updated files --- Makefile.am | 6 +- cupsfilters/pdftopdf/C-intervalset-private.h | 49 +- cupsfilters/pdftopdf/C-intervalset.c | 196 +++--- cupsfilters/pdftopdf/C-nup-private.h | 68 +- cupsfilters/pdftopdf/C-nup.c | 467 ++++++------- cupsfilters/pdftopdf/C-pdftopdf-private.h | 24 + .../pdftopdf/C-pdftopdf-processor-private.h | 150 +++++ cupsfilters/pdftopdf/C-pdftopdf-processor.c | 215 ++++++ cupsfilters/pdftopdf/C-pdftopdf.c | 613 ++++++++++++++++++ cupsfilters/pdftopdf/C-pptypes-private.h | 92 +-- cupsfilters/pdftopdf/C-pptypes.c | 343 +++++----- cupsfilters/pdftopdf/pdfio-pdftopdf-private.h | 9 + cupsfilters/pdftopdf/pdfio-pdftopdf.c | 52 +- cupsfilters/pdftopdf/pdfio-tools-test.c | 128 ---- cupsfilters/pdftopdf/pdfio-xobject-private.h | 10 +- cupsfilters/pdftopdf/pdfio-xobject.c | 64 +- 16 files changed, 1707 insertions(+), 779 deletions(-) create mode 100644 cupsfilters/pdftopdf/C-pdftopdf-private.h create mode 100644 cupsfilters/pdftopdf/C-pdftopdf-processor-private.h create mode 100644 cupsfilters/pdftopdf/C-pdftopdf-processor.c create mode 100644 cupsfilters/pdftopdf/C-pdftopdf.c delete mode 100644 cupsfilters/pdftopdf/pdfio-tools-test.c diff --git a/Makefile.am b/Makefile.am index 4b06aaeb..3c2df209 100644 --- a/Makefile.am +++ b/Makefile.am @@ -180,6 +180,10 @@ libcupsfilters_la_SOURCES = \ cupsfilters/pack.c \ cupsfilters/pclmtoraster.cxx \ cupsfilters/pdf.cxx \ + cupsfilters/pdftopdf/C-pdftopdf-processor.c \ + cupsfilters/pdftopdf/C-pdftopdf-processor-private.h \ + cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c \ + cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h \ cupsfilters/pdftopdf/C-pptypes.c \ cupsfilters/pdftopdf/C-pptypes-private.h \ cupsfilters/pdftopdf/C-nup.c \ @@ -367,4 +371,4 @@ install-data-hook: uninstall-hook: $(RM) $(DESTDIR)$(pkgcharsetdir)/pdf.utf-8 -SUBDIRS = \ No newline at end of file +SUBDIRS = diff --git a/cupsfilters/pdftopdf/C-intervalset-private.h b/cupsfilters/pdftopdf/C-intervalset-private.h index 993f7ba7..bb830165 100644 --- a/cupsfilters/pdftopdf/C-intervalset-private.h +++ b/cupsfilters/pdftopdf/C-intervalset-private.h @@ -1,33 +1,42 @@ +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#ifndef _CUPS_FILTERS_PDFTOPDF_INTERVALSET_H_ +#define _CUPS_FILTERS_PDFTOPDF_INTERVALSET_H_ + #include "pdftopdf-private.h" #include #include -#include - -typedef int key_t; -typedef uint16_t data_t; -typedef struct { - key_t first; - key_t end; -} value_t; - -typedef struct { - value_t *data; - size_t size; - size_t capacity; +typedef struct +{ + int start; + int end; +} interval_t; + +typedef struct +{ + interval_t *data; + size_t size; + size_t capacity; } _cfPDFToPDFIntervalSet; - - -extern const key_t npos; +extern const int _cfPDFToPDFIntervalSet_npos; void _cfPDFToPDFIntervalSet_init(_cfPDFToPDFIntervalSet *set); void _cfPDFToPDFIntervalSet_clear(_cfPDFToPDFIntervalSet *set); -void _cfPDFToPDFIntervalSet_add(_cfPDFToPDFIntervalSet *set, key_t start, key_t end); +void _cfPDFToPDFIntervalSet_add(_cfPDFToPDFIntervalSet *set, int start, int end); +void _cfPDFToPDFIntervalSet_add_single(_cfPDFToPDFIntervalSet *set, int start); void _cfPDFToPDFIntervalSet_finish(_cfPDFToPDFIntervalSet *set); -size_t _cfPDFToPDFIntervalSet_size(const _cfPDFToPDFIntervalSet *vec) { return vec->size; } +size_t _cfPDFToPDFIntervalSet_size(const _cfPDFToPDFIntervalSet *set); + +bool _cfPDFToPDFIntervalSet_contains(const _cfPDFToPDFIntervalSet *set, int val); +int _cfPDFToPDFIntervalSet_next(const _cfPDFToPDFIntervalSet *set, int val); -bool _cfPDFToPDFIntervalSet_contains(const _cfPDFToPDFIntervalSet *set, key_t val); -key_t interval_set_next(const _cfPDFToPDFIntervalSet *set, key_t val); void _cfPDFToPDFIntervalSet_dump(const _cfPDFToPDFIntervalSet *set, pdftopdf_doc_t *doc); + +#endif // !_CUPS_FILTERS_PDFTOPDF_INTERVALSET_H_ + diff --git a/cupsfilters/pdftopdf/C-intervalset.c b/cupsfilters/pdftopdf/C-intervalset.c index a2ee2e27..122d3be5 100644 --- a/cupsfilters/pdftopdf/C-intervalset.c +++ b/cupsfilters/pdftopdf/C-intervalset.c @@ -1,10 +1,17 @@ +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + #include "C-intervalset-private.h" #include -#include "cupsfilters/debug-internal.h" -#include #include +#include +#include +#include +#include "cupsfilters/debug-internal.h" -const key_t npos = INT_MAX; +const int _cfPDFToPDFIntervalSet_npos = INT_MAX; void _cfPDFToPDFIntervalSet_init(_cfPDFToPDFIntervalSet *set) @@ -21,150 +28,143 @@ _cfPDFToPDFIntervalSet_clear(_cfPDFToPDFIntervalSet *set) set->data = NULL; set->size = 0; set->capacity = 0; - } void -_cfPDFToPDFIntervalSet_add(_cfPDFToPDFIntervalSet *set, - key_t start, - key_t end) +_cfPDFToPDFIntervalSet_add(_cfPDFToPDFIntervalSet *set, int start, int end) { - if (start < end) + if (start >= end) + return; + + + if (set->size == set->capacity) { - if (set->size == set->capacity) - { - set->capacity = set->capacity ? set->capacity * 2 : 1; - set->data = (value_t *)realloc(set->data, set->capacity * sizeof(value_t)); - } + set->capacity = set->capacity == 0 ? 4 : set->capacity * 2; + set->data = realloc(set->data, set->capacity * sizeof(interval_t)); + } + + set->data[set->size].start = start; + set->data[set->size].end = end; + set->size++; +} + +void +_cfPDFToPDFIntervalSet_add_single(_cfPDFToPDFIntervalSet *set, + int start) +{ + key_t end = _cfPDFToPDFIntervalSet_npos; + + if (start >= end) + return; - set->data[set->size].first = start; - set->data[set->size].end = end; - set->size++; + if (set->size == set->capacity) + { + set->capacity = set->capacity == 0 ? 4 : set->capacity * 2; + set->data = realloc(set->data, set->capacity * sizeof(interval_t)); + if (set->data == NULL) + return; } + + set->data[set->size].start = start; + set->data[set->size].end = end; + set->size++; } -//helper for qsort -static int compare_intervals(const void *a, const void *b) { - const value_t *interval_a = (const value_t *)a; - const value_t *interval_b = (const value_t *)b; - return (interval_a->first - interval_b->first); +static int +compare_intervals(const void *a, const void *b) +{ + interval_t *ia = (interval_t *)a; + interval_t *ib = (interval_t *)b; + if (ia->start != ib->start) + return ia->start - ib->start; + return ia->end - ib->end; } void _cfPDFToPDFIntervalSet_finish(_cfPDFToPDFIntervalSet *set) { - if (set->size == 0) + if (set->size == 0) return; - // Sort the intervals - qsort(set->data, set->size, sizeof(_cfPDFToPDFIntervalSet), compare_intervals); + qsort(set->data, set->size, sizeof(interval_t), compare_intervals); - size_t pos = 0; - for (size_t i = 1; i < set->size; ++i) + size_t new_size = 0; + for (size_t i = 1; i < set->size; i++) { - if (set->data[pos].end >= set->data[i].first) + if (set->data[new_size].end >= set->data[i].start) { - if (set->data[pos].end < set->data[i].end) - set->data[pos].end = set->data[i].end; + if (set->data[new_size].end < set->data[i].end) + { + set->data[new_size].end = set->data[i].end; + } } - else { - ++pos; - if (pos != i) - set->data[pos] = set->data[i]; + new_size++; + set->data[new_size] = set->data[i]; } } - set->size = pos + 1; + set->size = new_size + 1; +} + +size_t +_cfPDFToPDFIntervalSet_size(const _cfPDFToPDFIntervalSet *set) +{ + return set->size; } bool _cfPDFToPDFIntervalSet_contains(const _cfPDFToPDFIntervalSet *set, - key_t val) + int val) { - for (size_t i = 0; i < set->size; ++i) + for (size_t i = 0; i < set->size; i++) { - if (val >= set->data[i].first && val < set->data[i].end) + if (val >= set->data[i].start && val < set->data[i].end) + { return true; + } } return false; } -key_t +int _cfPDFToPDFIntervalSet_next(const _cfPDFToPDFIntervalSet *set, - key_t val) + int val) { val++; - value_t key = {val, npos}; - value_t *it = (value_t *)bsearch(&key, set->data, set->size, - sizeof(value_t), compare_intervals); - - if (it == NULL) + for (size_t i = 0; i < set->size; i++) { - for (size_t i = 0; i < set->size; ++i) - { - if (set->data[i].first > val) - return set->data[i].first; - - return npos; - } - - if (it == set->data) + if (val < set->data[i].end) { - if (it->first > val) - return it->first; - - return npos; + if (val >= set->data[i].start) + { + return val; + } + else + { + return set->data[i].start; + } } - - --it; - if (val < it->end) - return val; - - - ++it; - if (it == set->data + set->size) - return npos; - - return it->first; } + return _cfPDFToPDFIntervalSet_npos; } -void -_cfPDFToPDFIntervalSet_dump(const _cfPDFToPDFIntervalSet *set, - pdftopdf_doc_t *doc) +void +_cfPDFToPDFIntervalSet_dump(const _cfPDFToPDFIntervalSet *set, + pdftopdf_doc_t *doc) { - int len = set->size; - if (len == 0) + if (set->size == 0) { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: (empty)"); + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: (empty)"); return; } - len--; - - for (int iA = 0; iA < len; iA++) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: [%d,%d)", - set->data[iA].first, set->data[iA].end); - } - - if (set->data[len].end == npos) + for (size_t i = 0; i < set->size; i++) { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: [%d,inf)", - set->data[len].first); - } - else - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: [%d,%d)", - set->data[len].first, set->data[len].end); + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: [%d,%d)", + set->data[i].start, set->data[i].end); } } + diff --git a/cupsfilters/pdftopdf/C-nup-private.h b/cupsfilters/pdftopdf/C-nup-private.h index 084b6e4a..1230d55d 100644 --- a/cupsfilters/pdftopdf/C-nup-private.h +++ b/cupsfilters/pdftopdf/C-nup-private.h @@ -1,61 +1,53 @@ +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#ifndef _CUPS_FILTERS_PDFTOPDF_NUP_H_ +#define _CUPS_FILTERS_PDFTOPDF_NUP_H_ + #include "C-pptypes-private.h" #include -typedef struct +typedef struct _cfPDFToPDFNupParameters { int nupX, nupY; float width, height; - bool landscape; + bool landscape; + pdftopdf_axis_e first; pdftopdf_position_e xstart, ystart; pdftopdf_position_e xalign, yalign; } _cfPDFToPDFNupParameters; -void _cfPDFToPDFNupParameters_init(_cfPDFToPDFNupParameters *nupParam); -void _cfPDFToPDFNupParameters_dump(const _cfPDFToPDFNupParameters *nupParam, - pdftopdf_doc_t *doc); +void _cfPDFToPDFNupParameters_init(_cfPDFToPDFNupParameters *nupParams); bool _cfPDFToPDFNupParameters_possible(int nup); void _cfPDFToPDFNupParameters_preset(int nup, _cfPDFToPDFNupParameters *ret); +void _cfPDFToPDFNupParameters_dump(const _cfPDFToPDFNupParameters *nupParams, pdftopdf_doc_t *doc); - -typedef struct +typedef struct _cfPDFToPDFNupPageEdit { - float xpos, ypos; - float scale; - _cfPDFToPDFPageRect *sub; + float xpos, ypos; + float scale; + + _cfPDFToPDFPageRect sub; } _cfPDFToPDFNupPageEdit; -void _cfPDFToPDFNupPageEdit_dump(const _cfPDFToPDFNupPageEdit *nupPageEdit, - pdftopdf_doc_t *doc); +void _cfPDFToPDFNupPageEdit_dump(const _cfPDFToPDFNupPageEdit *edit, pdftopdf_doc_t *doc); -typedef struct +typedef struct _cfPDFToPDFNupState { - int first, second; -} integerPair; - -typedef struct -{ - _cfPDFToPDFNupParameters *param; - int in_pages, out_pages; - int nup; - int subpage; + _cfPDFToPDFNupParameters param; + int in_pages, out_pages; + int nup; + int subpage; } _cfPDFToPDFNupState; -void _cfPDFToPDFNupState_init(_cfPDFToPDFNupState *nupState, - _cfPDFToPDFNupParameters *nupParam); -void _cfPDFToPDFNupState_reset(_cfPDFToPDFNupState *nupState); -integerPair _cfPDFToPDFNupState_convert_order(_cfPDFToPDFNupState *nupState, - int subpage); -void _cfPDFToPDFNupState_calculate_edit(const _cfPDFToPDFNupState *nupState, - int subx, int suby, _cfPDFToPDFNupPageEdit *ret); -bool _cfPDFToPDFNupState_mext_page(_cfPDFToPDFNupState *nupState, - float in_width, float in_height, - _cfPDFToPDFNupPageEdit *ret); - -typedef struct -{ - pdftopdf_axis_e first; - pdftopdf_position_e second; -} resultPair; +void _cfPDFToPDFNupState_init(_cfPDFToPDFNupState *state, const _cfPDFToPDFNupParameters *param); +void _cfPDFToPDFNupState_reset(_cfPDFToPDFNupState *state); +bool _cfPDFToPDFNupState_next_page(_cfPDFToPDFNupState *state, float in_width, float in_height, _cfPDFToPDFNupPageEdit *ret); bool _cfPDFToPDFParseNupLayout(const char *val, _cfPDFToPDFNupParameters *ret); + +#endif // !_CUPS_FILTERS_PDFTOPDF_NUP_H_ + diff --git a/cupsfilters/pdftopdf/C-nup.c b/cupsfilters/pdftopdf/C-nup.c index 24dec357..a26d1f2d 100644 --- a/cupsfilters/pdftopdf/C-nup.c +++ b/cupsfilters/pdftopdf/C-nup.c @@ -1,60 +1,63 @@ -#include +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + #include "C-nup-private.h" #include -#include #include -#include +#include +#include "cupsfilters/debug-internal.h" -void -_cfPDFToPDFNupParameters_init(_cfPDFToPDFNupParameters *nupParam) // {{{ +void +_cfPDFToPDFNupParameters_init(_cfPDFToPDFNupParameters *nupParams) { - nupParam->nupX = 1; - nupParam->nupY = 1; - nupParam->width = NAN; - nupParam->height = NAN; - nupParam->landscape = false; - nupParam->first = X; - nupParam->xstart = LEFT; - nupParam->ystart = TOP; - nupParam->xalign = CENTER; - nupParam->yalign = CENTER; + nupParams->nupX = 1; + nupParams->nupY = 1; + nupParams->width = NAN; + nupParams->height = NAN; + nupParams->landscape = false; + nupParams->first = X; + nupParams->xstart = LEFT; + nupParams->ystart = TOP; + nupParams->xalign = CENTER; + nupParams->yalign = CENTER; } -// }}} void -_cfPDFToPDFNupParameters_dump(const _cfPDFToPDFNupParameters *nupParam, - pdftopdf_doc_t *doc) // {{{ +_cfPDFToPDFNupParameters_dump(const _cfPDFToPDFNupParameters *nupParams, + pdftopdf_doc_t *doc) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: NupX: %d, NupY: %d, " + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: NupX: %d, NupY: %d, " "width: %f, height: %f", - nupParam->nupX, nupParam->nupY, - nupParam->width, nupParam->height); - int opos = -1, - fpos = -1, + nupParams->nupX, nupParams->nupY, + nupParams->width, nupParams->height); + + int opos = -1, + fpos = -1, spos = -1; - if (nupParam->xstart == LEFT) - fpos = 0; - else if (nupParam->xstart == RIGHT) + if (nupParams->xstart == LEFT) + fpos = 0; + else if (nupParams->xstart == RIGHT) fpos = 1; - if (nupParam->ystart == LEFT) + if (nupParams->ystart == LEFT) spos = 0; - else if (nupParam->ystart == RIGHT) + else if (nupParams->ystart == RIGHT) spos = 1; - if(nupParam->first == X) + if (nupParams->first == X) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: First Axis: X"); + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: First Axis: X"); opos = 0; } - else if(nupParam->first == Y) + else if (nupParams->first == Y) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: First Axis: Y"); - opos = 0; + opos = 2; int temp = fpos; fpos = spos; spos = temp; @@ -63,315 +66,257 @@ _cfPDFToPDFNupParameters_dump(const _cfPDFToPDFNupParameters *nupParam, if ((opos == -1) || (fpos == -1) || (spos == -1)) { if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Bad Spec: %d; start: %d, %d", - nupParam->first, nupParam->xstart, nupParam->ystart); + "cfFilterPDFToPDF: Bad Spec: %d; start: %d, %d", + nupParams->first, nupParams->xstart, nupParams->ystart); } else { static const char *order[4] = {"lr", "rl", "bt", "tb"}; if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Order: %s%s", - order[opos + fpos], + "cfFilterPDFToPDF: Order: %s%s", + order[opos + fpos], order[(opos + 2) % 4 + spos]); } - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "fFilterPDFToPDF: Alignment:"); - - _cfPDFToPDFPositionAndAxisDump(nupParam->xalign, X, doc); - _cfPDFToPDFPositionAndAxisDump(nupParam->yalign, Y, doc); -} -// }}} + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Alignment:"); + _cfPDFToPDFPositionAndAxisDump(nupParams->xalign, X, doc); + _cfPDFToPDFPositionAndAxisDump(nupParams->yalign, Y, doc); +} -bool -_cfPDFToPDFNupParameters_possible(int nup) // {{{ +bool +_cfPDFToPDFNupParameters_possible(int nup) { return ((nup >= 1) && (nup <= 16) && - ((nup != 5) && (nup != 7) && (nup != 11) && (nup != 13) && - (nup != 14))); + ((nup != 5) && (nup != 7) && (nup != 11) && (nup != 13) && + (nup != 14))); } -// }}} -void _cfPDFToPDFNupParameters_preset(int nup, - _cfPDFToPDFNupParameters *ret) // {{{ +void +_cfPDFToPDFNupParameters_preset(int nup, + _cfPDFToPDFNupParameters *ret) { - switch(nup) + switch (nup) { - case 1: - ret->nupX=1; - ret->nupY=1; - break; - case 2: - ret->nupX=2; - ret->nupY=1; - ret->landscape=true; - break; - case 3: - ret->nupX=3; - ret->nupY=1; - ret->landscape=true; - break; - case 4: - ret->nupX=2; - ret->nupY=2; - break; - case 6: - ret->nupX=3; - ret->nupY=2; - ret->landscape=true; - break; - case 8: - ret->nupX=4; - ret->nupY=2; - ret->landscape=true; - break; - case 9: - ret->nupX=3; - ret->nupY=3; - break; - case 10: - ret->nupX=5; - ret->nupY=2; - ret->landscape=true; - break; - case 12: - ret->nupX=3; - ret->nupY=4; - break; - case 15: - ret->nupX=5; - ret->nupY=3; - ret->landscape=true; - break; - case 16: - ret->nupX=4; - ret->nupY=4; - break; + case 1: + ret->nupX = 1; + ret->nupY = 1; + break; + case 2: + ret->nupX = 2; + ret->nupY = 1; + ret->landscape = true; + break; + case 3: + ret->nupX = 3; + ret->nupY = 1; + ret->landscape = true; + break; + case 4: + ret->nupX = 2; + ret->nupY = 2; + break; + case 6: + ret->nupX = 3; + ret->nupY = 2; + ret->landscape = true; + break; + case 8: + ret->nupX = 4; + ret->nupY = 2; + ret->landscape = true; + break; + case 9: + ret->nupX = 3; + ret->nupY = 3; + break; + case 10: + ret->nupX = 5; + ret->nupY = 2; + ret->landscape = true; + break; + case 12: + ret->nupX = 3; + ret->nupY = 4; + break; + case 15: + ret->nupX = 5; + ret->nupY = 3; + ret->landscape = true; + break; + case 16: + ret->nupX = 4; + ret->nupY = 4; + break; } } -// }}} void -_cfPDFToPDFNupPageEdit_dump(const _cfPDFToPDFNupPageEdit *nupPageEdit, - pdftopdf_doc_t *doc) // {{{ +_cfPDFToPDFNupState_init(_cfPDFToPDFNupState *state, + const _cfPDFToPDFNupParameters *param) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: xpos: %f, ypos: %f, scale: %f", - nupPageEdit->xpos, nupPageEdit->ypos, - nupPageEdit->scale); - _cfPDFToPDFPageRect_dump(nupPageEdit->sub, doc); + state->param = *param; + state->in_pages = 0; + state->out_pages = 0; + state->nup = param->nupX * param->nupY; + state->subpage = state->nup; } -// }}} void -_cfPDFToPDFNupState_init(_cfPDFToPDFNupState *nupState, - _cfPDFToPDFNupParameters *nupParam) // {{{ +_cfPDFToPDFNupState_reset(_cfPDFToPDFNupState *state) { - nupState->param->nupX = nupParam->nupX; - nupState->param->nupY = nupParam->nupY; - nupState->param->width = nupParam->width; - nupState->param->height = nupParam->height; - nupState->param->landscape = nupParam->landscape; - nupState->param->first = nupParam->first; - nupState->param->xstart = nupParam->xstart; - nupState->param->ystart = nupParam->ystart; - nupState->param->xalign = nupParam->xalign; - nupState->param->yalign = nupParam->yalign; - - nupState->in_pages = 0; - nupState->out_pages = 0; - nupState->nup = nupParam->nupX * nupParam->nupY; - nupState->subpage = nupState->nup; + state->in_pages = 0; + state->out_pages = 0; + state->subpage = state->nup; } -// }}} void -_cfPDFToPDFNupState_reset(_cfPDFToPDFNupState *nupState) // {{{ +_cfPDFToPDFNupPageEdit_dump(const _cfPDFToPDFNupPageEdit *edit, + pdftopdf_doc_t *doc) { - nupState->in_pages = 0; - nupState->out_pages = 0; - nupState->subpage = nupState->nup; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: xpos: %f, ypos: %f, scale: %f", + edit->xpos, edit->ypos, edit->scale); + _cfPDFToPDFPageRect_dump(&edit->sub, doc); } -// }}} -integerPair -_cfPDFToPDFNupState_convert_order(_cfPDFToPDFNupState *nupState, - int subpage) // {{{ +typedef struct { + int first; + int second; +} int_pair; + +static int_pair +_cfPDFToPDFNupState_convert_order(const _cfPDFToPDFNupState *state, + int subpage) { int subx, suby; - - if(nupState->param->first == X) + if (state->param.first == X) { - subx = nupState->subpage % nupState->param->nupX; - suby = nupState->subpage / nupState->param->nupX; + subx = subpage % state->param.nupX; + suby = subpage / state->param.nupX; } else { - subx = nupState->subpage / nupState->param->nupY; - suby = nupState->subpage % nupState->param->nupY; + subx = subpage / state->param.nupY; + suby = subpage % state->param.nupY; } - subx = (nupState->param->nupX - 1) * (nupState->param->xstart+1) / 2 - nupState->param->xstart * subx; - suby = (nupState->param->nupY - 1) * (nupState->param->ystart+1) / 2 - nupState->param->ystart * suby; - - integerPair intPair; - - intPair.first = subx; - intPair.second = suby; + subx = (state->param.nupX - 1) * (state->param.xstart + 1) / 2 - state->param.xstart * subx; + suby = (state->param.nupY - 1) * (state->param.ystart + 1) / 2 - state->param.ystart * suby; - return intPair; + int_pair result = {subx, suby}; + return result; } -// }}} -static inline float -lin(pdftopdf_position_e pos, - float size) // {{{ +static float +lin(pdftopdf_position_e pos, float size) { - if(pos == -1) - return (0); - else if(pos == 0) - return (size/2); - else if(pos == 1) - return (size); - - return (size * (pos+1) / 2); + if (pos == -1) + return 0; + else if (pos == 0) + return size / 2; + else if (pos == 1) + return size; + return size * (pos + 1) / 2; } -// }}} void -_cfPDFToPDFNupState_calculate_edit(const _cfPDFToPDFNupState *nupState, +_cfPDFToPDFNupState_calculate_edit(const _cfPDFToPDFNupState *state, int subx, int suby, - _cfPDFToPDFNupPageEdit *ret) // {{{ + _cfPDFToPDFNupPageEdit *ret) { - const float width = nupState->param->width / nupState->param->nupX, - height = nupState->param->height / nupState->param->nupY; + const float width = state->param.width / state->param.nupX; + const float height = state->param.height / state->param.nupY; ret->xpos = subx * width; ret->ypos = suby * height; - const float scalex = width / ret->sub->width, - scaley = height / ret->sub->height; - - float subwidth = ret->sub->width * scaley, - subheight = ret->sub->height * scalex; + const float scalex = width / ret->sub.width; + const float scaley = height / ret->sub.height; + float subwidth = ret->sub.width * scaley; + float subheight = ret->sub.height * scalex; if (scalex > scaley) { ret->scale = scaley; subheight = height; - ret->xpos += lin(nupState->param->xalign, height - subwidth); + ret->xpos += lin(state->param.xalign, width - subwidth); } - else { ret->scale = scalex; subwidth = width; - ret->ypos += lin(nupState->param->yalign, height + subheight); + ret->ypos += lin(state->param.yalign, height - subheight); } - ret->sub->left = ret->xpos; - ret->sub->bottom = ret->ypos; - ret->sub->right = ret->sub->left + subwidth; - ret->sub->top = ret->sub->bottom + subheight; + ret->sub.left = ret->xpos; + ret->sub.bottom = ret->ypos; + ret->sub.right = ret->sub.left + subwidth; + ret->sub.top = ret->sub.bottom + subheight; } -// }}} -bool -_cfPDFToPDFNupState_mext_page(_cfPDFToPDFNupState *nupState, - float in_width, float in_height, - _cfPDFToPDFNupPageEdit *ret) // {{{ +bool +_cfPDFToPDFNupState_next_page(_cfPDFToPDFNupState *state, + float in_width, float in_height, + _cfPDFToPDFNupPageEdit *ret) { - nupState->in_pages++; - nupState->subpage++; - if(nupState->subpage >= nupState->nup) - { - nupState->subpage = 0; - nupState->out_pages++; + state->in_pages++; + state->subpage++; + if (state->subpage >= state->nup) + { + state->subpage = 0; + state->out_pages++; } - ret->sub->width = in_width; - ret->sub->height = in_height; - - integerPair sub = _cfPDFToPDFNupState_convert_order(nupState, - nupState->subpage); + ret->sub.width = in_width; + ret->sub.height = in_height; - _cfPDFToPDFNupState_calculate_edit(nupState, sub.first, sub.second, ret); + int_pair sub = _cfPDFToPDFNupState_convert_order(state, state->subpage); + _cfPDFToPDFNupState_calculate_edit(state, sub.first, sub.second, ret); - return (nupState->subpage == 0); + return (state->subpage == 0); } -// }}} -resultPair* -parsePosition(char a, - char b) // {{{ +static int_pair +parsePosition(char a, char b) { - a |= 0x20; + a |= 0x20; // make lowercase b |= 0x20; - resultPair *returnPair = (resultPair*)malloc(sizeof(resultPair));; - if((a == 'l') && (b == 'r')) - { - returnPair->first = X; - returnPair->second = LEFT; - return (returnPair); - } - - else if((a == 'r') && (b == 'l')) - { - returnPair->first = X; - returnPair->second = RIGHT; - return (returnPair); - } - - else if((a == 't') && (b == 'b')) - { - returnPair->first = Y; - returnPair->second = TOP; - return (returnPair); - } - - else if((a == 'b') && (b == 't')) - { - returnPair->first = Y; - returnPair->second = BOTTOM; - return (returnPair); - } - - returnPair->first = X; - returnPair->second = CENTER; - return(returnPair); + if ((a == 'l') && (b == 'r')) + return (int_pair){X, LEFT}; + else if ((a == 'r') && (b == 'l')) + return (int_pair){X, RIGHT}; + else if ((a == 't') && (b == 'b')) + return (int_pair){Y, TOP}; + else if ((a == 'b') && (b == 't')) + return (int_pair){Y, BOTTOM}; + return (int_pair){X, CENTER}; } -// }}} bool _cfPDFToPDFParseNupLayout(const char *val, - _cfPDFToPDFNupParameters *ret) // {{{ + _cfPDFToPDFNupParameters *ret) { - resultPair *pos0 = parsePosition(val[0], val[1]); - - if(pos0->second == CENTER) - return (false); - - resultPair *pos1 = parsePosition(val[2], val[3]); - - if((pos1->second == CENTER) || (pos0->first == pos1->first)) + DEBUG_assert(val); + int_pair pos0 = parsePosition(val[0], val[1]); + if (pos0.second == CENTER) + return false; + int_pair pos1 = parsePosition(val[2], val[3]); + if ((pos1.second == CENTER) || (pos0.first == pos1.first)) + return false; + + ret->first = pos0.first; + if (ret->first == X) { - return (false); + ret->xstart = pos0.second; + ret->ystart = pos1.second; } - - ret->first = pos0->first; - - if(ret->first == X) - { - ret->xstart = pos0->second; - ret->ystart = pos1->second; - } - else { - ret->xstart = pos1->second; - ret->ystart = pos0->second; + ret->xstart = pos1.second; + ret->ystart = pos0.second; } - return (val[4] == 0); + return (val[4] == 0); // everything seen? } -// }}} + diff --git a/cupsfilters/pdftopdf/C-pdftopdf-private.h b/cupsfilters/pdftopdf/C-pdftopdf-private.h new file mode 100644 index 00000000..4e1d8fc5 --- /dev/null +++ b/cupsfilters/pdftopdf/C-pdftopdf-private.h @@ -0,0 +1,24 @@ +// +// Copyright 2020 by Jai Luthra. +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#ifndef _CUPS_FILTERS_PDFTOPDF_PDFTOPDF_H +#define _CUPS_FILTERS_PDFTOPDF_PDFTOPDF_H + +#include + +typedef struct // **** Document information **** +{ + cf_logfunc_t logfunc; // Log function + void *logdata; // Log data + cf_filter_iscanceledfunc_t iscanceledfunc; // Function returning 1 when + // job is canceled, NULL for not + // supporting stop on cancel + void *iscanceleddata; // User data for is-canceled + // function, can be NULL +} pdftopdf_doc_t; + +#endif // !_CUPS_FILTERS_PDFTOPDF_PDFTOPDF_H diff --git a/cupsfilters/pdftopdf/C-pdftopdf-processor-private.h b/cupsfilters/pdftopdf/C-pdftopdf-processor-private.h new file mode 100644 index 00000000..b2813f03 --- /dev/null +++ b/cupsfilters/pdftopdf/C-pdftopdf-processor-private.h @@ -0,0 +1,150 @@ +#ifndef C_PDFTOPDF_PROCESSOR_PRIVATE_H +#define C_PDFTOPDF_PROCESSOR_PRIVATE_H + + +#include "C-pptypes-private.h" +#include "C-nup-private.h" +#include "C-pdftopdf-private.h" +#include "C-intervalset-private.h" +#include +#include + +typedef enum { + CF_PDFTOPDF_BOOKLET_OFF, + CF_PDFTOPDF_BOOKLET_ON, + CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE +} pdftopdf_booklet_mode_e; + + +typedef struct { + int job_id, num_copies; + const char *user, *title; + bool pagesize_requested; + bool fitplot; + bool fillprint; // print-scaling = fill + bool cropfit; // -o crop-to-fit + bool autoprint; // print-scaling = auto + bool autofit; // print-scaling = auto-fit + bool fidelity; + bool no_orientation; + _cfPDFToPDFPageRect page; + pdftopdf_rotation_e orientation, normal_landscape; // normal_landscape (i.e. default direction) is e.g. needed for number-up=2 + bool paper_is_landscape; + bool duplex; + pdftopdf_border_type_e border; + _cfPDFToPDFNupParameters nup; + bool reverse; + + char *page_label; + bool even_pages, odd_pages; + _cfPDFToPDFIntervalSet *page_ranges; + _cfPDFToPDFIntervalSet *input_page_ranges; + + bool mirror; + + pdftopdf_position_e xpos, ypos; + + bool collate; + + bool even_duplex; // make number of pages a multiple of 2 + + pdftopdf_booklet_mode_e booklet; + int book_signature; + + bool auto_rotate; + + int device_copies; + bool device_collate; + bool set_duplex; + + int page_logging; + int copies_to_be_logged; +} _cfPDFToPDFProcessingParameters; + +void _cfPDFToPDFProcessingParameters_init(_cfPDFToPDFProcessingParameters *processingParams); + +bool _cfPDFToPDFProcessingParameters_with_page(const _cfPDFToPDFProcessingParameters *self, + int outno); + +bool _cfPDFToPDFProcessingParameters_have_page(const _cfPDFToPDFProcessingParameters *self, + int pageno); +void _cfPDFToPDFProcessingParameters_dump(const _cfPDFToPDFProcessingParameters *self, + pdftopdf_doc_t *doc); + + + + +typedef enum { + CF_PDFTOPDF_WILL_STAY_ALIVE, + CF_PDFTOPDF_MUST_DUPLICATE, + CF_PDFTOPDF_TAKE_OWNERSHIP +} pdftopdf_arg_ownership_e; + +/* +// Example function to initialize the struct (constructor equivalent) +typedef struct _cfPDFToPDFProcessor _cfPDFToPDFProcessor; + +struct _cfPDFToPDFProcessor { + void (*destroy)(_cfPDFToPDFProcessor *self); + + bool (*load_file)(_cfPDFToPDFProcessor *self, + FILE *f, pdftopdf_doc_t *doc, + pdftopdf_arg_ownership_e take, + int flatten_forms); + + bool (*load_filename)(_cfPDFToPDFProcessor *self, + const char *name, + pdftopdf_doc_t *doc, + int flatten_forms); + + bool (*check_print_permissions)(_cfPDFToPDFProcessor *self, + pdftopdf_doc_t *doc); + + void (*get_pages)(_cfPDFToPDFProcessor *self, + pdftopdf_doc_t *doc, + _cfPDFToPDFPageHandle **pages, + size_t *count); + + _cfPDFToPDFPageHandle *(*new_page)(_cfPDFToPDFProcessor *self, + float width, float height, + pdftopdf_doc_t *doc); + + void (*add_page)(_cfPDFToPDFProcessor *self, + _cfPDFToPDFPageHandle *page, + bool front); + + void (*multiply)(_cfPDFToPDFProcessor *self, + int copies, bool collate); + + void (*auto_rotate_all)(_cfPDFToPDFProcessor *self, + bool dst_lscape, + pdftopdf_rotation_e normal_landscape); + + void (*add_cm)(_cfPDFToPDFProcessor *self, + const char *defaulticc, + const char *outputicc); + + void (*set_comments)(_cfPDFToPDFProcessor *self, + const char **comments, + size_t count); + + void (*emit_file)(_cfPDFToPDFProcessor *self, + FILE *dst, pdftopdf_doc_t *doc, + pdftopdf_arg_ownership_e take); + + void (*emit_filename)(_cfPDFToPDFProcessor *self, + const char *name, + pdftopdf_doc_t *doc); + + bool (*has_acro_form)(_cfPDFToPDFProcessor *self); +}; + +// Example function to initialize the struct (constructor equivalent) +void _cfPDFToPDFProcessor_init(_cfPDFToPDFProcessor *self) { + // Initialize function pointers and any necessary data members +} + +_cfPDFToPDFProcessor* _cfPDFToPDFFactory_processor(void); +*/ + +#endif // C_PDFTOPDF_PROCESSOR_PRIVATE_H diff --git a/cupsfilters/pdftopdf/C-pdftopdf-processor.c b/cupsfilters/pdftopdf/C-pdftopdf-processor.c new file mode 100644 index 00000000..9c7fcc4b --- /dev/null +++ b/cupsfilters/pdftopdf/C-pdftopdf-processor.c @@ -0,0 +1,215 @@ +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. + +#include "C-pdftopdf-processor-private.h" +#include "pdfio-pdftopdf-processor-private.h" +#include +#include "cupsfilters/debug-internal.h" +#include +#include +#include + +void +_cfPDFToPDFProcessingParameters_init(_cfPDFToPDFProcessingParameters *processingParams) // {{{ +{ + + processingParams->job_id = 0; + processingParams->num_copies = 1; + processingParams->user = NULL; + processingParams->title = NULL; + processingParams->pagesize_requested = false; + processingParams->fitplot = false; + processingParams->fillprint = false; // print-scaling = fill + processingParams->cropfit = false; + processingParams->autoprint = false; + processingParams->autofit = false; + processingParams->fidelity = false; + processingParams->no_orientation = false; + processingParams->orientation = ROT_0; + processingParams->normal_landscape = ROT_270; + processingParams->paper_is_landscape = false; + processingParams->duplex = false; + processingParams->border = NONE; + processingParams->reverse = false; + + processingParams->page_label = NULL; + processingParams->even_pages = true; + processingParams->odd_pages = true; + + processingParams->mirror = false; + + processingParams->xpos = CENTER; + processingParams->ypos = CENTER; + + processingParams->collate = false; + processingParams->even_duplex = false; + + processingParams->booklet = CF_PDFTOPDF_BOOKLET_OFF; + processingParams->book_signature = -1; + + processingParams->auto_rotate = false; + + processingParams->device_copies = 1; + processingParams->device_collate = false; + processingParams->set_duplex = false; + + processingParams->page_logging = -1; + processingParams->copies_to_be_logged = 0; + + processingParams->page.width = 612.0; // Letter size width in points + processingParams->page.height = 792.0; // Letter size height in points + processingParams->page.top = processingParams->page.height - 36.0; + processingParams->page.bottom = 36.0; + processingParams->page.left = 18.0; + processingParams->page.right = processingParams->page.width - 18.0; + + _cfPDFToPDFIntervalSet_add_single(processingParams->input_page_ranges, 1); + _cfPDFToPDFIntervalSet_finish(processingParams->input_page_ranges); + _cfPDFToPDFIntervalSet_add_single(processingParams->page_ranges, 1); + _cfPDFToPDFIntervalSet_finish(processingParams->page_ranges); +} +// }}} + +void +BookletMode_dump(pdftopdf_booklet_mode_e bkm, + pdftopdf_doc_t *doc) // {{{ +{ + static const char *bstr[3] = {"Off", "On", "Shuffle-Only"}; + + if ((bkm < CF_PDFTOPDF_BOOKLET_OFF) || + (bkm > CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Booklet mode: (Bad booklet mode: %d)", + bkm); + } + else + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Booklet mode: %s", + bstr[bkm]); + } +} +// }}} + +bool +_cfPDFToPDFProcessingParameters_with_page(const _cfPDFToPDFProcessingParameters *processingParams, + int outno) // {{{ +{ + if (outno % 2 == 0) + { + if (!processingParams->even_pages) + return false; + } + else if (!processingParams->odd_pages) + { + return false; + } + return _cfPDFToPDFIntervalSet_contains(processingParams->page_ranges, outno); +} +// }}} + +bool +_cfPDFToPDFProcessingParameters_have_page(const _cfPDFToPDFProcessingParameters *processingParams, + int pageno) // {{{ +{ + return _cfPDFToPDFIntervalSet_contains(processingParams->input_page_ranges, pageno); +} +// }}} + +void +_cfPDFToPDFProcessingParameters_dump(const _cfPDFToPDFProcessingParameters *processingParams, + pdftopdf_doc_t *doc) // {{{ +{ + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: job_id: %d, num_copies: %d", + processingParams->job_id, processingParams->num_copies); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: user: %s, title: %s", + (processingParams->user) ? processingParams->user : "(null)", + (processingParams->title) ? processingParams->title : "(null)"); + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: fitplot: %s", + (processingParams->fitplot) ? "true" : "false"); + + _cfPDFToPDFPageRect_dump(&processingParams->page, doc); + _cfPDFToPDFRotationDump(processingParams->orientation, doc); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: paper_is_landscape: %s", + (processingParams->paper_is_landscape) ? "true" : "false"); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: duplex: %s", + (processingParams->duplex) ? "true" : "false"); + + _cfPDFToPDFBorderTypeDump(processingParams->border, doc); + _cfPDFToPDFNupParameters_dump(&processingParams->nup, doc); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: reverse: %s", + (processingParams->reverse) ? "true" : "false"); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: even_pages: %s, odd_pages: %s", + (processingParams->even_pages) ? "true" : "false", + (processingParams->odd_pages) ? "true" : "false"); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: input page range:"); + + _cfPDFToPDFIntervalSet_dump(processingParams->input_page_ranges, doc); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: page range:"); + + _cfPDFToPDFIntervalSet_dump(processingParams->page_ranges, doc); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: mirror: %s", + (processingParams->mirror) ? "true" : "false"); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Position:"); + + _cfPDFToPDFPositionAndAxisDump(processingParams->xpos, X, doc); + _cfPDFToPDFPositionAndAxisDump(processingParams->ypos, Y, doc); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: collate: %s", + (processingParams->collate) ? "true" : "false"); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: even_duplex: %s", + (processingParams->even_duplex) ? "true" : "false"); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: page_label: %s", + processingParams->page_label ? processingParams->page_label : "(none)"); + + BookletMode_dump(processingParams->booklet, doc); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: booklet signature: %d", + processingParams->book_signature); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: auto_rotate: %s", + (processingParams->auto_rotate) ? "true" : "false"); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: device_copies: %d", + processingParams->device_copies); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: device_collate: %s", + (processingParams->device_collate) ? "true" : "false"); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: set_duplex: %s", + (processingParams->set_duplex) ? "true" : "false"); +} +// }}} + diff --git a/cupsfilters/pdftopdf/C-pdftopdf.c b/cupsfilters/pdftopdf/C-pdftopdf.c new file mode 100644 index 00000000..b1fd3a35 --- /dev/null +++ b/cupsfilters/pdftopdf/C-pdftopdf.c @@ -0,0 +1,613 @@ +// +// Copyright (c) 6-2011, BBR Inc. All rights reserved. +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pdftopdf-private.h" +#include "C-pdftopdf-processor-private.h" + +static bool +optGetInt(const char *name, + int num_options, + cups_option_t *options, + int *ret) +{ + const char *val = cupsGetOption(name, num_options, options); + if (val) + { + *ret = atoi(val); + return true; + } + return false; +} + +static bool +optGetFloat(const char *name, + int num_options, + cups_option_t *options, + float *ret) +{ + const char *val = cupsGetOption(name, num_options, options); + if (val) + { + *ret = atof(val); + return true; + } + return false; +} + +static bool +is_false(const char *value) +{ + if (!value) return false; + return ((strcasecmp(value, "no") == 0) || + (strcasecmp(value, "off") == 0) || + (strcasecmp(value, "false") == 0)); +} + +static bool +is_true(const char *value) +{ + if (!value) return false; + return ((strcasecmp(value, "yes") == 0) || + (strcasecmp(value, "on") == 0) || + (strcasecmp(value, "true") == 0)); +} + +static bool +parsePosition(const char *value, + pdftopdf_position_e *xpos, + pdftopdf_position_e *ypos) +{ + *xpos = CENTER; + *ypos = CENTER; + int next = 0; + + if (strcasecmp(value, "center") == 0) + return true; + else if (strncasecmp(value, "top", 3) == 0) + { + *ypos = TOP; + next = 3; + } + else if (strncasecmp(value, "bottom", 6) == 0) { + *ypos = BOTTOM; + next = 6; + } + + if (next) + { + if (value[next] == 0) return true; + else if (value[next] != '-') return false; + value += next + 1; + } + + if (strcasecmp(value, "left") == 0) *xpos = LEFT; + else if (strcasecmp(value, "right") == 0) *xpos = RIGHT; + else return false; + + return true; +} + +static void +parseRanges(const char *range, _cfPDFToPDFIntervalSet *ret) +{ + ret->clear(); + if (!range) { + ret->add(1); // everything + ret->finish(); + return; + } + + int lower, upper; + while (*range) { + if (*range == '-') { + range++; + upper = strtol(range, (char **)&range, 10); + if (upper >= 2147483647) ret->add(1); + else ret->add(1, upper + 1); + } else { + lower = strtol(range, (char **)&range, 10); + if (*range == '-') { + range++; + if (!isdigit(*range)) ret->add(lower); + else { + upper = strtol(range, (char **)&range, 10); + if (upper >= 2147483647) ret->add(lower); + else ret->add(lower, upper + 1); + } + } else { + ret->add(lower, lower + 1); + } + } + + if (*range != ',') break; + range++; + } + ret->finish(); +} + +static bool _cfPDFToPDFParseBorder(const char *val, pdftopdf_border_type_e *ret) { + if (strcasecmp(val, "none") == 0) *ret = NONE; + else if (strcasecmp(val, "single") == 0) *ret = ONE_THIN; + else if (strcasecmp(val, "single-thick") == 0) *ret = ONE_THICK; + else if (strcasecmp(val, "double") == 0) *ret = TWO_THIN; + else if (strcasecmp(val, "double-thick") == 0) *ret = TWO_THICK; + else return false; + return true; +} + +void getParameters(cf_filter_data_t *data, int num_options, cups_option_t *options, _cfPDFToPDFProcessingParameters *param, pdftopdf_doc_t *doc) { + char *final_content_type = data->final_content_type; + ipp_t *printer_attrs = data->printer_attrs; + ipp_t *job_attrs = data->job_attrs; + ipp_attribute_t *attr; + const char *val; + int ipprot; + int nup; + char *classification; + char rawlabel[256]; + char cookedlabel[256]; + + if ((val = cupsGetOption("copies", num_options, options)) != NULL || + (val = cupsGetOption("Copies", num_options, options)) != NULL || + (val = cupsGetOption("num-copies", num_options, options)) != NULL || + (val = cupsGetOption("NumCopies", num_options, options)) != NULL) { + int copies = atoi(val); + if (copies > 0) param->num_copies = copies; + } + + if (param->num_copies == 0) param->num_copies = 1; + + if (printer_attrs != NULL && + (attr = ippFindAttribute(printer_attrs, "landscape-orientation-requested-preferred", IPP_TAG_ZERO)) != NULL && + ippGetInteger(attr, 0) == 5) param->normal_landscape = ROT_270; + else param->normal_landscape = ROT_90; + + param->orientation = ROT_0; + param->no_orientation = false; + if (optGetInt("orientation-requested", num_options, options, &ipprot)) { + if ((ipprot < 3) || (ipprot > 6)) { + if (ipprot && doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Bad value (%d) for orientation-requested, using 0 degrees", ipprot); + param->no_orientation = true; + } else { + static const pdftopdf_rotation_e ipp2rot[4] = {ROT_0, ROT_90, ROT_270, ROT_180}; + param->orientation = ipp2rot[ipprot - 3]; + } + } else if ((val = cupsGetOption("landscape", num_options, options)) != NULL) { + if (!is_false(val)) param->orientation = param->normal_landscape; + } else param->no_orientation = true; + + param->pagesize_requested = (cfGetPageDimensions(printer_attrs, job_attrs, num_options, options, NULL, 0, + &(param->page.width), &(param->page.height), &(param->page.left), &(param->page.bottom), &(param->page.right), &(param->page.top), NULL, NULL) >= 1); + + cfSetPageDimensionsToDefault(&(param->page.width), &(param->page.height), &(param->page.left), &(param->page.bottom), &(param->page.right), &(param->page.top), doc->logfunc, doc->logdata); + + param->page.right = param->page.width - param->page.right; + param->page.top = param->page.height - param->page.top; + + param->paper_is_landscape = (param->page.width > param->page.height); + + _cfPDFToPDFPageRect tmp; // borders (before rotation) + + optGetFloat("page-top", num_options, options, &tmp.top); + optGetFloat("page-left", num_options, options, &tmp.left); + optGetFloat("page-right", num_options, options, &tmp.right); + optGetFloat("page-bottom", num_options, options, &tmp.bottom); + + if ((val = cupsGetOption("media-top-margin", num_options, options)) != NULL) tmp.top = atof(val) * 72.0 / 2540.0; + if ((val = cupsGetOption("media-left-margin", num_options, options)) != NULL) tmp.left = atof(val) * 72.0 / 2540.0; + if ((val = cupsGetOption("media-right-margin", num_options, options)) != NULL) tmp.right = atof(val) * 72.0 / 2540.0; + if ((val = cupsGetOption("media-bottom-margin", num_options, options)) != NULL) tmp.bottom = atof(val) * 72.0 / 2540.0; + + if ((param->orientation == ROT_90) || (param->orientation == ROT_270)) { + tmp.right = param->page.height - tmp.right; + tmp.top = param->page.width - tmp.top; + rotate_move(&tmp, param->orientation, param->page.height, param->page.width); + } else { + tmp.right = param->page.width - tmp.right; + tmp.top = param->page.height - tmp.top; + rotate_move(&tmp, param->orientation, param->page.width, param->page.height); + } + + set_page_rect(&(param->page), &tmp); + + if ((val = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, "sides")) != NULL && strncmp(val, "two-sided-", 10) == 0) param->duplex = true; + else if (is_true(cupsGetOption("Duplex", num_options, options))) { + param->duplex = true; + param->set_duplex = true; + } else if ((val = cupsGetOption("sides", num_options, options)) != NULL) { + if ((strcasecmp(val, "two-sided-long-edge") == 0) || (strcasecmp(val, "two-sided-short-edge") == 0)) { + param->duplex = true; + param->set_duplex = true; + } else if (strcasecmp(val, "one-sided") != 0) { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported sides value %s, using sides=one-sided!", val); + } + } + + nup = 1; + if (optGetInt("number-up", num_options, options, &nup)) { + if (!_cfPDFToPDFNupParameters_possible(nup)) { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported number-up value %d, using number-up=1!", nup); + nup = 1; + } + _cfPDFToPDFNupParameters_preset(nup, &(param->nup)); + } + + if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL) { + if (!_cfPDFToPDFParseNupLayout(val, &(param->nup))) { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported number-up-layout %s, using number-up-layout=lrtb!", val); + param->nup.first = X_AXIS; + param->nup.xstart = LEFT; + param->nup.ystart = TOP; + } + } + + if ((val = cupsGetOption("page-border", num_options, options)) != NULL) { + if (!_cfPDFToPDFParseBorder(val, &(param->border))) { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported page-border value %s, using page-border=none!", val); + param->border = NONE; + } + } + + if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL || + (val = cupsGetOption("output-order", num_options, options)) != NULL || + (val = cupsGetOption("page-delivery", num_options, options)) != NULL) { + param->reverse = (strcasecmp(val, "Reverse") == 0 || strcasecmp(val, "reverse-order") == 0); + } else { + param->reverse = cfIPPReverseOutput(printer_attrs, job_attrs); + } + + classification = getenv("CLASSIFICATION"); + if (classification) strcpy(rawlabel, classification); + + if ((val = cupsGetOption("page-label", num_options, options)) != NULL) { + if (strlen(rawlabel) > 0) strcat(rawlabel, " - "); + strcat(rawlabel, cupsGetOption("page-label", num_options, options)); + } + + char *rawptr = rawlabel; + char *cookedptr = cookedlabel; + while (*rawptr) { + if (*rawptr < 32 || *rawptr > 126) { + sprintf(cookedptr, "\\%03o", (unsigned int)*rawptr); + cookedptr += 4; + } else { + *cookedptr++ = *rawptr; + } + rawptr++; + } + *cookedptr = '\0'; + param->page_label = strdup(cookedlabel); + + if ((val = cupsGetOption("page-set", num_options, options)) != NULL) { + if (strcasecmp(val, "even") == 0) param->odd_pages = false; + else if (strcasecmp(val, "odd") == 0) param->even_pages = false; + else if (strcasecmp(val, "all") != 0) { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported page-set value %s, using page-set=all!", val); + } + } + + if ((val = cupsGetOption("page-ranges", num_options, options)) != NULL) parseRanges(val, &(param->page_ranges)); + if ((val = cupsGetOption("input-page-ranges", num_options, options)) != NULL) parseRanges(val, &(param->input_page_ranges)); + + if ((val = cupsGetOption("mirror", num_options, options)) != NULL || + (val = cupsGetOption("mirror-print", num_options, options)) != NULL) param->mirror = is_true(val); + + param->booklet = CF_PDFTOPDF_BOOKLET_OFF; + if ((val = cupsGetOption("booklet", num_options, options)) != NULL) { + if (strcasecmp(val, "shuffle-only") == 0) param->booklet = CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE; + else if (is_true(val)) param->booklet = CF_PDFTOPDF_BOOKLET_ON; + else if (!is_false(val)) { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported booklet value %s, using booklet=off!", val); + } + } + param->book_signature = -1; + if (optGetInt("booklet-signature", num_options, options, &(param->book_signature))) { + if (param->book_signature == 0) { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported booklet-signature value, using booklet-signature=-1 (all)!", val); + param->book_signature = -1; + } + } + + if ((val = cupsGetOption("position", num_options, options)) != NULL) { + if (!parsePosition(val, &(param->xpos), &(param->ypos))) { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unrecognized position value %s, using position=center!", val); + param->xpos = CENTER; + param->ypos = CENTER; + } + } + + if (is_true(cupsGetOption("Collate", num_options, options))) param->collate = true; + else if ((val = cupsGetOption("sheet-collate", num_options, options)) != NULL) param->collate = (strcasecmp(val, "uncollated") != 0); + else if (((val = cupsGetOption("multiple-document-handling", num_options, options)) != NULL && + (strcasecmp(val, "separate-documents-collated-copies") == 0 || + strcasecmp(val, "separate-documents-uncollated-copies") == 0 || + strcasecmp(val, "single-document") == 0 || + strcasecmp(val, "single-document-new-sheet") == 0)) || + (val = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, "multiple-document-handling")) != NULL) { + param->collate = (strcasecmp(val, "separate-documents-uncollated-copies") != 0); + } + + param->even_duplex = (param->duplex && is_true(cupsGetOption("even-duplex", num_options, options))); + + param->auto_rotate = param->no_orientation; + if ((val = cupsGetOption("pdftopdfAutoRotate", num_options, options)) != NULL || (val = cupsGetOption("pdfAutoRotate", num_options, options)) != NULL) + param->auto_rotate = !is_false(val); + + if ((val = cupsGetOption("ipp-attribute-fidelity", num_options, options)) != NULL) { + if (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcasecmp(val, "on")) + param->fidelity = true; + } + + if (printer_attrs == NULL && !param->pagesize_requested && param->booklet == CF_PDFTOPDF_BOOKLET_OFF && param->nup.nupX == 1 && param->nup.nupY == 1) + param->cropfit = true; + else if ((val = cupsGetOption("print-scaling", num_options, options)) != NULL) { + if (!strcasecmp(val, "auto")) param->autoprint = true; + else if (!strcasecmp(val, "auto-fit")) param->autofit = true; + else if (!strcasecmp(val, "fill")) param->fillprint = true; + else if (!strcasecmp(val, "fit")) param->fitplot = true; + else if (!strcasecmp(val, "none")) param->cropfit = true; + else param->autoprint = true; + } else { + if ((val = cupsGetOption("fitplot", num_options, options)) == NULL) { + if ((val = cupsGetOption("fit-to-page", num_options, options)) == NULL) + val = cupsGetOption("ipp-attribute-fidelity", num_options, options); + } + param->fitplot = (val && !is_false(val)); + if ((val = cupsGetOption("fill", num_options, options)) != NULL) { + if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) + param->fillprint = true; + } + if ((val = cupsGetOption("crop-to-fit", num_options, options)) != NULL) { + if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) + param->cropfit = 1; + } + if (!param->autoprint && !param->autofit && !param->fitplot && !param->fillprint && !param->cropfit) + param->autoprint = true; + } + + if (param->fitplot || param->fillprint || param->autoprint || param->autofit || param->booklet != CF_PDFTOPDF_BOOKLET_OFF || param->nup.nupX > 1 || param->nup.nupY > 1) + param->pagesize_requested = true; + + if ((val = cupsGetOption("pdf-filter-page-logging", num_options, options)) != NULL) { + if (strcasecmp(val, "auto") == 0) { + param->page_logging = -1; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Automatic page logging selected by options."); + } else if (is_true(val)) { + param->page_logging = 1; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Forced page logging selected by options."); + } else if (is_false(val)) { + param->page_logging = 0; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Suppressed page logging selected by options."); + } else { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported page logging setting \"pdf-filter-page-logging=%s\", using \"auto\"!", val); + param->page_logging = -1; + } + } + + if (param->page_logging == -1) { + if (final_content_type && (strcasestr(final_content_type, "/pdf") || strcasestr(final_content_type, "/vnd.cups-pdf") || strcasestr(final_content_type, "/pwg-raster"))) + param->page_logging = 1; + else param->page_logging = 0; + + if (!final_content_type || final_content_type[0] == '\0') + param->page_logging = -1; + + if (doc->logfunc) { + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Determined whether to log pages or not using output data type."); + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "final_content_type = %s => page_logging = %d", final_content_type ? final_content_type : "NULL", param->page_logging); + } + + if (param->page_logging == -1) param->page_logging = 0; + } +} + +void calculate(int num_options, cups_option_t *options, _cfPDFToPDFProcessingParameters *param, char *final_content_type) { + const char *val; + bool hw_copies = false, hw_collate = false; + + if ((val = cupsGetOption("hardware-copies", num_options, options)) != NULL) + hw_copies = is_true(val); + else + hw_copies = (final_content_type && (strcasestr(final_content_type, "/pdf") || strcasestr(final_content_type, "/vnd.cups-pdf"))); + + if (hw_copies) { + if ((val = cupsGetOption("hardware-collate", num_options, options)) != NULL) + hw_collate = is_true(val); + else + hw_collate = (final_content_type && (strcasestr(final_content_type, "/pdf") || strcasestr(final_content_type, "/vnd.cups-pdf") || strcasestr(final_content_type, "/pwg-raster") || strcasestr(final_content_type, "/urf") || strcasestr(final_content_type, "/PCLm"))); + } + + if (param->reverse && param->duplex) param->even_duplex = true; + + if (param->num_copies == 1) { + param->device_copies = 1; + param->collate = false; + } else if (hw_copies) { + param->device_copies = param->num_copies; + if (param->collate) { + param->device_collate = hw_collate; + if (!param->device_collate) param->device_copies = 1; + } + } else { + param->device_copies = 1; + if (param->duplex) { + param->collate = true; + param->device_collate = false; + } + } + + if (param->device_copies != 1) param->num_copies = 1; + if (param->duplex && param->collate && !param->device_collate) param->even_duplex = true; + if (!param->duplex) param->even_duplex = false; +} + +FILE *copy_fd_to_temp(int infd, pdftopdf_doc_t *doc) { + char buf[BUFSIZ]; + int n; + + int outfd = cupsCreateTempFd(NULL, NULL, buf, sizeof(buf)); + if (outfd < 0) { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Can't create temporary file"); + return NULL; + } + + unlink(buf); + + while ((n = read(infd, buf, BUFSIZ)) > 0) { + if (write(outfd, buf, n) != n) { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Can't copy stdin to temporary file"); + close(outfd); + return NULL; + } + } + if (lseek(outfd, 0, SEEK_SET) < 0) { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Can't rewind temporary file"); + close(outfd); + return NULL; + } + + FILE *f; + if ((f = fdopen(outfd, "rb")) == 0) { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Can't fdopen temporary file"); + close(outfd); + return NULL; + } + return f; +} + +bool is_empty(FILE *f) { + char buf[1]; + if (fread(buf, 1, 1, f) == 0) return true; + rewind(f); + return false; +} + +int cfFilterPDFToPDF(int inputfd, int outputfd, int inputseekable, cf_filter_data_t *data, void *parameters) { + pdftopdf_doc_t doc; + char *final_content_type = data->final_content_type; + FILE *inputfp, *outputfp; + const char *t; + int streaming = 0; + size_t bytes; + char buf[BUFSIZ]; + cf_logfunc_t log = data->logfunc; + void *ld = data->logdata; + cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; + void *icd = data->iscanceleddata; + int num_options = 0; + cups_option_t *options = NULL; + + _cfPDFToPDFProcessingParameters param; + + param.job_id = data->job_id; + param.user = data->job_user; + param.title = data->job_title; + param.num_copies = data->copies; + param.copies_to_be_logged = data->copies; + param.page.width = param.page.height = 0; + param.page.left = param.page.bottom = -1; + param.page.right = param.page.top = -1; + + doc.logfunc = log; + doc.logdata = ld; + doc.iscanceledfunc = iscanceled; + doc.iscanceleddata = icd; + + num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); + + getParameters(data, num_options, options, ¶m, &doc); + calculate(num_options, options, ¶m, final_content_type); + +#ifdef DEBUG + param.dump(&doc); +#endif + + if ((t = cupsGetOption("filter-streaming-mode", num_options, options)) != NULL && (strcasecmp(t, "false") && strcasecmp(t, "off") && strcasecmp(t, "no"))) { + streaming = 1; + if (log) log(ld, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Streaming mode: No PDF processing, only adding of JCL"); + } + + cupsFreeOptions(num_options, options); + + if ((inputseekable && inputfd > 0) || streaming) { + if ((inputfp = fdopen(inputfd, "rb")) == NULL) + return 1; + } else { + if ((inputfp = copy_fd_to_temp(inputfd, &doc)) == NULL) + return 1; + } + + if (!streaming) { + if (is_empty(inputfp)) { + fclose(inputfp); + if (log) log(ld, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Input is empty, outputting empty file."); + return 0; + } + + if (log) log(ld, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Processing PDF input with QPDF: Page-ranges, page-set, number-up, booklet, size adjustment, ..."); + + // Load the PDF input data into QPDF + if (!proc_load_file(inputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE, 1)) { + fclose(inputfp); + return 1; + } + + // Process the PDF input data + if (!_cfProcessPDFToPDF(¶m, &doc)) + return 2; + + // Pass information to subsequent filters via PDF comments + char *output[10]; + int output_len = 0; + + output[output_len++] = "% This file was generated by pdftopdf"; + + if (param.device_copies > 0) { + char buf[256]; + snprintf(buf, sizeof(buf), "%d", param.device_copies); + output[output_len++] = strdup(buf); + + if (param.device_collate) + output[output_len++] = "%%PDFTOPDFCollate : true"; + else + output[output_len++] = "%%PDFTOPDFCollate : false"; + } + + proc_set_comments(output, output_len); + } + + outputfp = fdopen(outputfd, "w"); + if (outputfp == NULL) + return 1; + + if (!streaming) { + proc_emit_file(outputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE); + } else { + if (log) log(ld, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Passing on unchanged PDF data from input"); + while ((bytes = fread(buf, 1, sizeof(buf), inputfp)) > 0) + if (fwrite(buf, 1, bytes, outputfp) != bytes) + break; + fclose(inputfp); + } + + fclose(outputfp); + return 0; +} + diff --git a/cupsfilters/pdftopdf/C-pptypes-private.h b/cupsfilters/pdftopdf/C-pptypes-private.h index e1f0891a..a77cd5a2 100644 --- a/cupsfilters/pdftopdf/C-pptypes-private.h +++ b/cupsfilters/pdftopdf/C-pptypes-private.h @@ -1,72 +1,78 @@ +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#ifndef _CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ +#define _CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ + #include "pdftopdf-private.h" #include -typedef enum { - X, - Y +typedef enum +{ + X, + Y } pdftopdf_axis_e; -typedef enum { - CENTER = 0, - LEFT = -1, - RIGHT = 1, - TOP = 1, - BOTTOM = -1 +typedef enum +{ + CENTER = 0, + LEFT = -1, + RIGHT = 1, + TOP = 1, + BOTTOM = -1 } pdftopdf_position_e; void _cfPDFToPDFPositionDump(pdftopdf_position_e pos, pdftopdf_doc_t *doc); void _cfPDFToPDFPositionAndAxisDump(pdftopdf_position_e pos, pdftopdf_axis_e axis, pdftopdf_doc_t *doc); -typedef enum { - ROT_0, - ROT_90, - ROT_180, - ROT_270, +typedef enum +{ + ROT_0, + ROT_90, + ROT_180, + ROT_270, } pdftopdf_rotation_e; void _cfPDFToPDFRotationDump(pdftopdf_rotation_e rot, pdftopdf_doc_t *doc); -pdftopdf_rotation_e add_rotations(pdftopdf_rotation_e lhs, pdftopdf_rotation_e rhs); -pdftopdf_rotation_e subtract_rotations(pdftopdf_rotation_e lhs, pdftopdf_rotation_e rhs); -pdftopdf_rotation_e negate_rotation(pdftopdf_rotation_e rhs); - -typedef enum{ - NONE = 0, - ONE_THIN = 2, - ONE_THICK = 3, - TWO_THIN = 4, - TWO_THICK = 5, - ONE = 0x02, - TWO = 0x04, - THICK = 0x01 +pdftopdf_rotation_e pdftopdf_rotation_add(pdftopdf_rotation_e lhs, pdftopdf_rotation_e rhs); +pdftopdf_rotation_e pdftopdf_rotation_sub(pdftopdf_rotation_e lhs, pdftopdf_rotation_e rhs); +pdftopdf_rotation_e pdftopdf_rotation_neg(pdftopdf_rotation_e rhs); + +typedef enum +{ + NONE = 0, + ONE_THIN = 2, + ONE_THICK = 3, + TWO_THIN = 4, + TWO_THICK = 5, + ONE = 0x02, + TWO = 0x04, + THICK = 0x01 } pdftopdf_border_type_e; void _cfPDFToPDFBorderTypeDump(pdftopdf_border_type_e border, pdftopdf_doc_t *doc); -typedef struct { - float top, left, right, bottom; // i.e. margins - float width, height; +typedef struct +{ + float top, left, right, bottom; // i.e. margins + float width, height; } _cfPDFToPDFPageRect; void _cfPDFToPDFPageRect_init(_cfPDFToPDFPageRect *rect); -void _cfPDFToPDFPageRect_rotate_move(_cfPDFToPDFPageRect *rect, - pdftopdf_rotation_e r, - float pwidth, - float pheight); +void _cfPDFToPDFPageRect_rotate_move(_cfPDFToPDFPageRect *rect, pdftopdf_rotation_e r, float pwidth, float pheight); -void _cfPDFToPDFPageRect_scale(_cfPDFToPDFPageRect *rect, - float mult); +void _cfPDFToPDFPageRect_scale(_cfPDFToPDFPageRect *rect, float mult); -void _cfPDFToPDFPageRect_translate(_cfPDFToPDFPageRect *rect, - float tx, - float ty); +void _cfPDFToPDFPageRect_translate(_cfPDFToPDFPageRect *rect, float tx, float ty); -void _cfPDFToPDFPageRect_set(_cfPDFToPDFPageRect *rect, const - _cfPDFToPDFPageRect *rhs); +void _cfPDFToPDFPageRect_set(_cfPDFToPDFPageRect *rect, const _cfPDFToPDFPageRect *rhs); -void _cfPDFToPDFPageRect_dump(const _cfPDFToPDFPageRect *rect, - pdftopdf_doc_t *doc); +void _cfPDFToPDFPageRect_dump(const _cfPDFToPDFPageRect *rect, pdftopdf_doc_t *doc); +#endif // !_CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ diff --git a/cupsfilters/pdftopdf/C-pptypes.c b/cupsfilters/pdftopdf/C-pptypes.c index 612e0e73..60368198 100644 --- a/cupsfilters/pdftopdf/C-pptypes.c +++ b/cupsfilters/pdftopdf/C-pptypes.c @@ -1,22 +1,23 @@ #include "C-pptypes-private.h" #include +#include #include "cupsfilters/debug-internal.h" void _cfPDFToPDFPositionDump(pdftopdf_position_e pos, pdftopdf_doc_t *doc) { - static const char *pstr[3] = {"Left/Bottom", "Center", "Right/Top"}; - if ((pos < LEFT) || (pos > RIGHT)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: (bad position: %d)", pos); - } - else - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: %s", pstr[pos+1]); - } + static const char *pstr[3] = {"Left/Bottom", "Center", "Right/Top"}; + if ((pos < LEFT) || (pos > RIGHT)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: (bad position: %d)", pos); + } + else + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: %s", pstr[pos+1]); + } } void @@ -24,66 +25,67 @@ _cfPDFToPDFPositionAndAxisDump(pdftopdf_position_e pos, pdftopdf_axis_e axis, pdftopdf_doc_t *doc) { - DEBUG_assert((axis == X) || (axis == Y)); - - if ((pos < LEFT) || (pos > RIGHT)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Position %s: (bad position: %d)", - (axis == X) ? "X" : "Y", pos); - return; - } - - if (axis == X) - { - static const char *pxstr[3] = {"Left", "Center", "Right"}; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Position X: %s", pxstr[pos + 1]); - } + DEBUG_assert((axis == X) || (axis == Y)); + + if ((pos < LEFT) || (pos > RIGHT)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Position %s: (bad position: %d)", + (axis == X) ? "X" : "Y", pos); + return; + } + + if (axis == X) + { + static const char *pxstr[3] = {"Left", "Center", "Right"}; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Position X: %s", pxstr[pos + 1]); + } - else - { - static const char *pystr[3] = {"Bottom", "Center", "Top"}; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Position Y: %s", pystr[pos + 1]); + else + { + static const char *pystr[3] = {"Bottom", "Center", "Top"}; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Position Y: %s", pystr[pos + 1]); - } + } } void _cfPDFToPDFRotationDump(pdftopdf_rotation_e rot, pdftopdf_doc_t *doc) { - static const char *rstr[4] = {"0 deg", "90 deg", "180 deg", "270 deg"}; // CCW - - if ((rot < ROT_0) || (rot > ROT_270)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Rotation(CCW): (bad rotation: %d)", rot); - } - - else - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Rotation(CCW): %s", rstr[rot]); - } + static const char *rstr[4] = {"0 deg", "90 deg", "180 deg", "270 deg"}; // CCW + + if ((rot < ROT_0) || (rot > ROT_270)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Rotation(CCW): (bad rotation: %d)", rot); + } + + else + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Rotation(CCW): %s", rstr[rot]); + } } pdftopdf_rotation_e -add_rotations(pdftopdf_rotation_e lhs, - pdftopdf_rotation_e rhs) +pdftopdf_rotation_add(pdftopdf_rotation_e lhs, + pdftopdf_rotation_e rhs) { - return (pdftopdf_rotation_e)(((int)lhs + (int)rhs) % 4); + return (pdftopdf_rotation_e)(((int)lhs + (int)rhs) % 4); } pdftopdf_rotation_e -subtract_rotations(pdftopdf_rotation_e lhs, pdftopdf_rotation_e rhs) +pdftopdf_rotation_sub(pdftopdf_rotation_e lhs, + pdftopdf_rotation_e rhs) { - return (pdftopdf_rotation_e)((((int)lhs - (int)rhs) % 4 + 4) % 4); + return (pdftopdf_rotation_e)((((int)lhs - (int)rhs) % 4 + 4) % 4); } pdftopdf_rotation_e -negate_rotation(pdftopdf_rotation_e rhs) +pdftopdf_rotation_neg(pdftopdf_rotation_e rhs) { return (pdftopdf_rotation_e)((4 - (int)rhs) % 4); } @@ -92,130 +94,131 @@ void _cfPDFToPDFBorderTypeDump(pdftopdf_border_type_e border, pdftopdf_doc_t *doc) { - if ((border < NONE) || (border == 1) || (border > TWO_THICK)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Border: (bad border: %d)", border); - - } - else - { - static const char *bstr[6] = - {"None", NULL, "one thin", "one thick", "two thin", "two thick"}; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Border: %s", bstr[border]); + if ((border < NONE) || (border == 1) || (border > TWO_THICK)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Border: (bad border: %d)", border); - } + } + else + { + static const char *bstr[6] = + {"None", NULL, "one thin", "one thick", "two thin", "two thick"}; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Border: %s", bstr[border]); + } } void _cfPDFToPDFPageRect_init(_cfPDFToPDFPageRect *rect) { - rect->top = NAN; - rect->left = NAN; - rect->right = NAN; - rect->bottom = NAN; - rect->width = NAN; - rect->height = NAN; + rect->top = NAN; + rect->left = NAN; + rect->right = NAN; + rect->bottom = NAN; + rect->width = NAN; + rect->height = NAN; } -void swap_float(float *a, float *b) { - float temp = *a; - *a = *b; - *b = temp; +void +swap_float(float *a, float *b) +{ + float temp = *a; + *a = *b; + *b = temp; } -void _cfPDFToPDFPageRect_rotate_move(_cfPDFToPDFPageRect *rect, - pdftopdf_rotation_e r, - float pwidth, - float pheight) +void +_cfPDFToPDFPageRect_rotate_move(_cfPDFToPDFPageRect *rect, + pdftopdf_rotation_e r, + float pwidth, float pheight) { - #if 1 - if (r >= ROT_180) - { - swap_float(&rect->top, &rect->bottom); - swap_float(&rect->left, &rect->right); - } - - if ((r == ROT_90) || (r == ROT_270)) - { - const float tmp = rect->bottom; - rect->bottom = rect->left; - rect->left = rect->top; - rect->top = rect->right; - rect->right = tmp; - - swap_float(&rect->width, &rect->height); - swap_float(&pwidth, &pheight); - } - - if ((r == ROT_90) || (r == ROT_180)) - { - rect->left = pwidth - rect->left; - rect->right = pwidth - rect->right; - } - - if ((r == ROT_270) || (r == ROT_180)) - { - rect->top = pheight - rect->top; - rect->bottom = pheight - rect->bottom; - } - - #else - switch(r) - { - case ROT_0: // no-op - break; - case ROT_90: - const float tmp0 = bottom; - bottom = left; - left = pheight - top; - top = right; - right = pheight - tmp0; - - swap_float(&width, &height); - break; - - case ROT_180: - const float tmp1 = left; - left = pwidth - right; - right = pwidth - tmp1; - - const float tmp2 = top; - top = pheight - bottom; - bottom = pheight - tmp2; - break; - - case ROT_270: - const float tmp3 = top; - top = pwidth - left; - left = bottom; - bottom = pwidth - right; - right = tmp3; - - swap_float(&width, &height); - break; - - } - #endif + #if 1 + if (r >= ROT_180) + { + swap_float(&rect->top, &rect->bottom); + swap_float(&rect->left, &rect->right); + } + + if ((r == ROT_90) || (r == ROT_270)) + { + const float tmp = rect->bottom; + rect->bottom = rect->left; + rect->left = rect->top; + rect->top = rect->right; + rect->right = tmp; + + swap_float(&rect->width, &rect->height); + swap_float(&pwidth, &pheight); + } + + if ((r == ROT_90) || (r == ROT_180)) + { + rect->left = pwidth - rect->left; + rect->right = pwidth - rect->right; + } + + if ((r == ROT_270) || (r == ROT_180)) + { + rect->top = pheight - rect->top; + rect->bottom = pheight - rect->bottom; + } + + #else + switch(r) + { + case ROT_0: // no-op + break; + case ROT_90: + const float tmp0 = bottom; + bottom = left; + left = pheight - top; + top = right; + right = pheight - tmp0; + + swap_float(&width, &height); + break; + + case ROT_180: + const float tmp1 = left; + left = pwidth - right; + right = pwidth - tmp1; + + const float tmp2 = top; + top = pheight - bottom; + bottom = pheight - tmp2; + break; + + case ROT_270: + const float tmp3 = top; + top = pwidth - left; + left = bottom; + bottom = pwidth - right; + right = tmp3; + + swap_float(&width, &height); + break; + + } + #endif } void _cfPDFToPDFPageRect_scale(_cfPDFToPDFPageRect *rect, float mult) { - if (mult == 1.0) - return; + if (mult == 1.0) + return; - DEBUG_assert(mult != 0.0); + DEBUG_assert(mult != 0.0); - rect->bottom *= mult; - rect->left *= mult; - rect->top *= mult; - rect->right *= mult; + rect->bottom *= mult; + rect->left *= mult; + rect->top *= mult; + rect->right *= mult; - rect->width *= mult; - rect->height *= mult; + rect->width *= mult; + rect->height *= mult; } void @@ -223,35 +226,35 @@ _cfPDFToPDFPageRect_translate(_cfPDFToPDFPageRect *rect, float tx, float ty) { - rect->left += tx; - rect->bottom += ty; - rect->right += tx; - rect->top += ty; + rect->left += tx; + rect->bottom += ty; + rect->right += tx; + rect->top += ty; } void _cfPDFToPDFPageRect_set(_cfPDFToPDFPageRect *rect, const _cfPDFToPDFPageRect *rhs) { - if (!isnan(rhs->top)) - rect->top = rhs->top; + if (!isnan(rhs->top)) + rect->top = rhs->top; - if (!isnan(rhs->left)) - rect->left = rhs->left; + if (!isnan(rhs->left)) + rect->left = rhs->left; - if (!isnan(rhs->right)) - rect->right = rhs->right; + if (!isnan(rhs->right)) + rect->right = rhs->right; - if (!isnan(rhs->bottom)) - rect->bottom = rhs->bottom; + if (!isnan(rhs->bottom)) + rect->bottom = rhs->bottom; } void _cfPDFToPDFPageRect_dump(const _cfPDFToPDFPageRect *rect, pdftopdf_doc_t *doc) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: top: %f, left: %f, right: %f, bottom: %f, " + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: top: %f, left: %f, right: %f, bottom: %f, " "width: %f, height: %f", rect->top, rect->left, rect->right, rect->bottom, rect->width, rect->height); diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h b/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h index f4992a40..f6e1637a 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h @@ -1,8 +1,16 @@ +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + #include "C-pptypes-private.h" #include #include #include +#ifndef _CUPS_FILTERS_PDFTOPDF_PDFIO_PDFTOPDF_H +#define _CUPS_FILTERS_PDFTOPDF_PDFIO_PDFTOPDF_H + // helper functions _cfPDFToPDFPageRect _cfPDFToPDFGetBoxAsRect(pdfio_rect_t *box); @@ -34,3 +42,4 @@ void _cfPDFToPDFMatrix_multiply(_cfPDFToPDFMatrix *lhs, const _cfPDFToPDFMatrix void _cfPDFToPDFMatrix_get(const _cfPDFToPDFMatrix *matrix, double *array); void _cfPDFToPDFMatrix_get_string(const _cfPDFToPDFMatrix *matrix, char *buffer, size_t bufsize); +#endif // !_CUPS_FILTERS_PDFTOPDF_PDFIO_PDFTOPDF_H diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf.c b/cupsfilters/pdftopdf/pdfio-pdftopdf.c index 87d61653..92ff63f3 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf.c +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf.c @@ -1,12 +1,17 @@ +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// #include "pdfio-pdftopdf-private.h" +#include "pdfio-tools-private.h" #include "cupsfilters/debug-internal.h" + #include #include -#include "pdfio-tools-private.h" _cfPDFToPDFPageRect -_cfPDFToPDFGetBoxAsRect(pdfio_rect_t *box) +_cfPDFToPDFGetBoxAsRect(pdfio_rect_t *box) // {{{ { _cfPDFToPDFPageRect ret; @@ -20,15 +25,17 @@ _cfPDFToPDFGetBoxAsRect(pdfio_rect_t *box) return ret; } +// }}} pdfio_rect_t* -_cfPDFToPDFGetRectAsBox(_cfPDFToPDFPageRect *rect) +_cfPDFToPDFGetRectAsBox(_cfPDFToPDFPageRect *rect) // {{{ { return (_cfPDFToPDFMakeBox(rect->left, rect->bottom, rect->right, rect->top)); } +// }}} pdftopdf_rotation_e -_cfPDFToPDFGetRotate(pdfio_obj_t *page) +_cfPDFToPDFGetRotate(pdfio_obj_t *page) // {{{ { pdfio_dict_t *pageDict = pdfioObjGetDict(page); double rotate = pdfioDictGetNumber(pageDict, "Rotate"); @@ -50,9 +57,10 @@ _cfPDFToPDFGetRotate(pdfio_obj_t *page) return ROT_0; } +// }}} double -_cfPDFToPDFGetUserUnit(pdfio_obj_t *page) +_cfPDFToPDFGetUserUnit(pdfio_obj_t *page) // {{{ { pdfio_dict_t *pageDict = pdfioObjGetDict(page); double userUnit = pdfioDictGetNumber(pageDict, "UserUnit"); @@ -60,9 +68,10 @@ _cfPDFToPDFGetUserUnit(pdfio_obj_t *page) return 1.0; return userUnit; } +// }}} double -_cfPDFToPDFMakeRotate(pdftopdf_rotation_e rot) +_cfPDFToPDFMakeRotate(pdftopdf_rotation_e rot) // {{{ { switch (rot) { @@ -79,9 +88,10 @@ _cfPDFToPDFMakeRotate(pdftopdf_rotation_e rot) return NAN; } } +// }}} void -_cfPDFToPDFMatrix_init(_cfPDFToPDFMatrix *matrix) +_cfPDFToPDFMatrix_init(_cfPDFToPDFMatrix *matrix) // {{{ { matrix->ctm[0] = 1.0; matrix->ctm[1] = 0.0; @@ -90,10 +100,11 @@ _cfPDFToPDFMatrix_init(_cfPDFToPDFMatrix *matrix) matrix->ctm[4] = 0.0; matrix->ctm[5] = 0.0; } +// }}} void _cfPDFToPDFMatrix_init_with_array(_cfPDFToPDFMatrix *matrix, - pdfio_array_t *array) + pdfio_array_t *array) // {{{ { if (pdfioArrayGetSize(array) != 6) fprintf(stderr, "Not a ctm matrix"); @@ -101,10 +112,11 @@ _cfPDFToPDFMatrix_init_with_array(_cfPDFToPDFMatrix *matrix, for (int iA = 0; iA < 6; iA ++) matrix->ctm[iA] = pdfioArrayGetNumber(array, iA); } +// }}} void _cfPDFToPDFMatrix_rotate(_cfPDFToPDFMatrix *matrix, - pdftopdf_rotation_e rot) + pdftopdf_rotation_e rot) // {{{ { double tmp[6]; memcpy(tmp, matrix->ctm, sizeof(tmp)); @@ -133,11 +145,12 @@ _cfPDFToPDFMatrix_rotate(_cfPDFToPDFMatrix *matrix, DEBUG_assert(0); } } +// }}} void _cfPDFToPDFMatrix_rotate_move(_cfPDFToPDFMatrix *matrix, pdftopdf_rotation_e rot, - double width, double height) + double width, double height) // {{{ { _cfPDFToPDFMatrix_rotate(matrix, rot); switch (rot) @@ -155,10 +168,11 @@ _cfPDFToPDFMatrix_rotate_move(_cfPDFToPDFMatrix *matrix, break; } } +// }}} void _cfPDFToPDFMatrix_rotate_rad(_cfPDFToPDFMatrix *matrix, - double rad) + double rad) // {{{ { _cfPDFToPDFMatrix tmp; _cfPDFToPDFMatrix_init(&tmp); @@ -170,28 +184,31 @@ _cfPDFToPDFMatrix_rotate_rad(_cfPDFToPDFMatrix *matrix, _cfPDFToPDFMatrix_multiply(matrix, &tmp); } +// }}} void _cfPDFToPDFMatrix_translate(_cfPDFToPDFMatrix *matrix, - double tx, double ty) + double tx, double ty) // {{{ { matrix->ctm[4] += matrix->ctm[0] * tx + matrix->ctm[2] * ty; matrix->ctm[5] += matrix->ctm[1] * tx + matrix->ctm[3] * ty; } +// }}} void _cfPDFToPDFMatrix_scale(_cfPDFToPDFMatrix *matrix, - double sx, double sy) + double sx, double sy) // {{{ { matrix->ctm[0] *= sx; matrix->ctm[1] *= sx; matrix->ctm[2] *= sy; matrix->ctm[3] *= sy; } +// }}} void _cfPDFToPDFMatrix_multiply(_cfPDFToPDFMatrix *lhs, - const _cfPDFToPDFMatrix *rhs) + const _cfPDFToPDFMatrix *rhs) // {{{ { double tmp[6]; memcpy(tmp, lhs->ctm, sizeof(tmp)); @@ -205,19 +222,22 @@ _cfPDFToPDFMatrix_multiply(_cfPDFToPDFMatrix *lhs, lhs->ctm[4] = tmp[0] * rhs->ctm[4] + tmp[2] * rhs->ctm[5] + tmp[4]; lhs->ctm[5] = tmp[1] * rhs->ctm[4] + tmp[3] * rhs->ctm[5] + tmp[5]; } +// }}} void _cfPDFToPDFMatrix_get(const _cfPDFToPDFMatrix *matrix, - double *array) + double *array) // {{{ { memcpy(array, matrix->ctm, sizeof(double) * 6); } +// }}} void _cfPDFToPDFMatrix_get_string(const _cfPDFToPDFMatrix *matrix, - char *buffer, size_t bufsize) + char *buffer, size_t bufsize) // {{{ { snprintf(buffer, bufsize, "%f %f %f %f %f %f", matrix->ctm[0], matrix->ctm[1], matrix->ctm[2], matrix->ctm[3], matrix->ctm[4], matrix->ctm[5]); } +// }}} diff --git a/cupsfilters/pdftopdf/pdfio-tools-test.c b/cupsfilters/pdftopdf/pdfio-tools-test.c deleted file mode 100644 index 1f4fa63a..00000000 --- a/cupsfilters/pdftopdf/pdfio-tools-test.c +++ /dev/null @@ -1,128 +0,0 @@ -#include -#include - -pdfio_rect_t -_cfPDFToPDFGetMediaBox(pdfio_obj_t *page) -{ - pdfio_rect_t mediaBox; - pdfio_dict_t *page_dict = pdfioObjGetDict(page); - pdfioDictGetRect(page_dict, "MediaBox", &mediaBox); - return (mediaBox); -} - -pdfio_rect_t -_cfPDFToPDFGetCropBox(pdfio_obj_t *page) -{ - pdfio_rect_t cropBox; - pdfio_dict_t *page_dict = pdfioObjGetDict(page); - if (!pdfioDictGetRect(page_dict, "CropBox", &cropBox)) { - return _cfPDFToPDFGetMediaBox(page); - } - return cropBox; -} - -pdfio_rect_t -_cfPDFToPDFGetBleedBox(pdfio_obj_t *page) -{ - pdfio_rect_t bleedBox; - pdfio_dict_t *page_dict = pdfioObjGetDict(page); - if (!pdfioDictGetRect(page_dict, "BleedBox", &bleedBox)) { - return _cfPDFToPDFGetCropBox(page); - } - return bleedBox; -} - -pdfio_rect_t -_cfPDFToPDFGetTrimBox(pdfio_obj_t *page) -{ - pdfio_rect_t trimBox; - pdfio_dict_t *page_dict = pdfioObjGetDict(page); - if (!pdfioDictGetRect(page_dict, "TrimBox", &trimBox)) { - return _cfPDFToPDFGetCropBox(page); - } - return trimBox; -} - -pdfio_rect_t -_cfPDFToPDFGetArtBox(pdfio_obj_t *page) -{ - pdfio_rect_t artBox; - pdfio_dict_t *page_dict = pdfioObjGetDict(page); - if (!pdfioDictGetRect(page_dict, "ArtBox", &artBox)) { - return _cfPDFToPDFGetCropBox(page); - } - return artBox; -} - -pdfio_rect_t -_cfPDFToPDFMakeBox(double x1, double y1, double x2, double y2) -{ - pdfio_rect_t ret; - ret.x1 = x1; - ret.y1 = y1; - ret.x2 = x2; - ret.y2 = y2; - - return ret; -} - -void printBox(pdfio_rect_t *box) { - printf("[%.5f, %.5f, %.5f, %.5f]\n", box->x1, box->y1, box->x2, box->y2); -} - -int main(){ - // Open the PDF file - pdfio_file_t *pdf = pdfioFileOpen("test.pdf", NULL, NULL, NULL, NULL); - if (!pdf) { - fprintf(stderr, "Error opening PDF file.\n"); - return 1; - } - - // Get the first page - pdfio_obj_t *page = pdfioFileGetPage(pdf, 0); - if (!page) { - fprintf(stderr, "Error getting page from PDF.\n"); - pdfioFileClose(pdf); - return 1; - } - - // Test _cfPDFToPDFGetMediaBox function - printf("Testing MediaBox retrieval:\n"); - pdfio_rect_t mediaBox = _cfPDFToPDFGetMediaBox(page); - printf("MediaBox: "); - printBox(&mediaBox); - - // Test _cfPDFToPDFGetCropBox function - printf("Testing CropBox retrieval:\n"); - pdfio_rect_t cropBox = _cfPDFToPDFGetCropBox(page); - printf("CropBox: "); - printBox(&cropBox); - - // Test _cfPDFToPDFGetBleedBox function - printf("Testing BleedBox retrieval:\n"); - pdfio_rect_t bleedBox = _cfPDFToPDFGetBleedBox(page); - printf("BleedBox: "); - printBox(&bleedBox); - - // Test _cfPDFToPDFGetTrimBox function - printf("Testing TrimBox retrieval:\n"); - pdfio_rect_t trimBox = _cfPDFToPDFGetTrimBox(page); - printf("TrimBox: "); - printBox(&trimBox); - - // Test _cfPDFToPDFGetArtBox function - printf("Testing ArtBox retrieval:\n"); - pdfio_rect_t artBox = _cfPDFToPDFGetArtBox(page); - printf("ArtBox: "); - printBox(&artBox); - - // Test _cfPDFToPDFMakeBox function - printf("Testing box creation:\n"); - pdfio_rect_t customBox = _cfPDFToPDFMakeBox(30, 30, 570, 770); - printf("CustomBox: "); - printBox(&customBox); - - // Clean up - pdfioFileClose(pdf); - return 0; -} diff --git a/cupsfilters/pdftopdf/pdfio-xobject-private.h b/cupsfilters/pdftopdf/pdfio-xobject-private.h index 562a2182..37b60a2f 100644 --- a/cupsfilters/pdftopdf/pdfio-xobject-private.h +++ b/cupsfilters/pdftopdf/pdfio-xobject-private.h @@ -1,5 +1,13 @@ +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#ifndef _CUPS_FILTERS_PDFTOPDF_PDFIO_XOBJECT_H_ +#define _CUPS_FILTERS_PDFTOPDF_PDFIO_XOBJECT_H_ + #include pdfio_obj_t* _cfPDFToPDFMakeXObject(pdfio_file_t *pdf, pdfio_obj_t *page); - +#endif // _CUPS_FILTERS_PDFTOPDF_PDFIO_XOBJECT_H_ diff --git a/cupsfilters/pdftopdf/pdfio-xobject.c b/cupsfilters/pdftopdf/pdfio-xobject.c index d0bb78de..095c1a50 100644 --- a/cupsfilters/pdftopdf/pdfio-xobject.c +++ b/cupsfilters/pdftopdf/pdfio-xobject.c @@ -1,6 +1,13 @@ +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + #include "pdfio-xobject-private.h" #include "pdfio-tools-private.h" #include "pdfio-pdftopdf-private.h" + +#include #include #include #include @@ -50,6 +57,47 @@ CombineFromContents_Provider_provideStreamData(CombineFromContents_Provider *pro } } +// +// To convert a page to an XObject there are several keys to consider: +// +// /Type /Page -> /Type /XObject (/Type optional for XObject) +// -> /Subtype /Form +// -> [/FormType 1] (optional) +// /Parent ? ? R -> remove +// /Resources dict -> copy +// /MediaBox rect [/CropBox /BleedBox /TrimBox /ArtBox] +// -> /BBox (use TrimBox [+ Bleed consideration?], +// with fallback to /MediaBox) +// note that /BBox is in *Form Space*, see /Matrix! +// [/BoxColorInfo dict] (used for guidelines that may be shown by viewer) +// -> ignore/remove +// [/Contents asfd] -> concatenate into stream data of the XObject +// (page is a dict, XObject a stream) +// +// [/Rotate 90] ... must be handled (either use CTM where XObject is +// /used/ -- or set /Matrix) +// [/UserUnit] (PDF 1.6) -> to /Matrix ? -- it MUST be handled. +// +// [/Group dict] -> copy +// [/Thumb stream] -> remove, not needed any more / would have to be +// regenerated (combined) +// [/B] article beads -- ignore for now +// [/Dur] -> remove (transition duration) +// [/Trans] -> remove (transitions) +// [/AA] -> remove (additional-actions) +// +// [/Metadata] what shall we do?? (kill: we can't combine XML) +// [/PieceInfo] -> remove, we can't combine private app data (?) +// [/LastModified date] (opt except /PieceInfo) -> see there +// +// [/PZ] -> remove, can't combine/keep (preferred zoom level) +// [/SeparationInfo] -> remove, no way to keep this (needed for separation) +// +// [/ID] related to web capture -- ignore/kill? +// [/StructParents] (opt except pdf contains "structural content items") +// -> copy (is this correct?) +// + pdfio_obj_t* _cfPDFToPDFMakeXObject(pdfio_file_t *pdf, pdfio_obj_t *page) @@ -71,14 +119,18 @@ _cfPDFToPDFMakeXObject(pdfio_file_t *pdf, pdfio_rect_t box = _cfPDFToPDFGetTrimBox(page); pdfioDictSetRect(dict, "BBox", &box); + // [/Matrix .] ... default is [1 0 0 1 0 0]; we incorporate /UserUnit and _cfPDFToPDFMatrix mtx; _cfPDFToPDFMatrix_init(&mtx); + // /Rotate here double user_unit = _cfPDFToPDFGetUserUnit(page); _cfPDFToPDFMatrix_scale(&mtx, user_unit, user_unit); + // transform, so that bbox is [0 0 w h] (in outer space, but after UserUnit) pdftopdf_rotation_e rot = _cfPDFToPDFGetRotate(page); + // calculate rotation effect on [0 0 w h] _cfPDFToPDFPageRect bbox = _cfPDFToPDFGetBoxAsRect(&box), tmp; tmp.left = 0; @@ -87,10 +139,16 @@ _cfPDFToPDFMakeXObject(pdfio_file_t *pdf, tmp.top = 0; _cfPDFToPDFPageRect_rotate_move(&tmp, rot, bbox.width, bbox.height); - _cfPDFToPDFMatrix_translate(&mtx, tmp.left, tmp.bottom); + + // tmp.rotate_move moves the bbox; we must achieve this move with the matrix. + _cfPDFToPDFMatrix_translate(&mtx, tmp.left, tmp.bottom);// 1. move origin to end up at + // left,bottom after rotation + + + _cfPDFToPDFMatrix_rotate(&mtx, rot); // 2. rotate coordinates according to /Rotate + _cfPDFToPDFMatrix_translate(&mtx, -bbox.left, -bbox.bottom);// 3. move origin from 0,0 to + // "form space" - _cfPDFToPDFMatrix_rotate(&mtx, rot); - _cfPDFToPDFMatrix_translate(&mtx, -bbox.left, -bbox.bottom); pdfio_array_t *matrix_array = pdfioArrayCreate(pdf); for (int i = 0; i < 6; i++) From 11ab2dc43da4fbb4c165c67b1791a8b9343888ad Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Thu, 5 Sep 2024 18:34:18 +0530 Subject: [PATCH 41/64] pdfio-pdftopdf-processor-private.h and its definition file --- .../pdfio-pdftopdf-processor-private.h | 107 ++ .../pdftopdf/pdfio-pdftopdf-processor.c | 1062 +++++++++++++++++ 2 files changed, 1169 insertions(+) create mode 100644 cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h create mode 100644 cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h new file mode 100644 index 00000000..81cbc269 --- /dev/null +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h @@ -0,0 +1,107 @@ +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#include "C-pdftopdf-processor-private.h" +#include "pdfio.h" +#define HASH_TABLE_SIZE 2048 + +typedef struct KeyValuePair { + char *key; // Key (string) + pdfio_obj_t *value; // Value (PDF object handle) + struct KeyValuePair *next; // For handling collisions (chaining) +} KeyValuePair; + +typedef struct HashTable { + KeyValuePair *buckets[HASH_TABLE_SIZE]; // Array of pointers to key-value pairs + int count; // Number of filled elements in the hash table +} HashTable; + +HashTable *hashCreate_hash_table(); +void hashInsert(HashTable *table, const char *key, pdfio_obj_t *value); +pdfio_obj_t *hashGet(HashTable *table, const char *key); +int hashGet_filled_count(HashTable *table); +void hashFree_hash_table(HashTable *table); + +typedef struct _cfPDFToPDF_PDFioProcessor{ + + // 1st mode: existing + pdfio_obj_t *page; // Equivalent to QPDFObjectHandle + int no; + + // 2nd mode: create new + HashTable *xobjs; // Pointer to a single HashTable + + char *content; // Equivalent to std::string content + + pdftopdf_rotation_e rotation; + + // Other members + pdfio_file_t *pdf; // Equivalent to std::unique_ptr + pdfio_obj_t **orig_pages; // Equivalent to std::vector + size_t orig_pages_size; // Current number of pages + size_t orig_pages_capacity; // Capacity for page array + + bool hasCM; + char *extraheader; +} _cfPDFToPDF_PDFioProcessor; + +//_cfPDFToPDFQPDFPageHandle functions +void _cfPDFToPDF_PDFioProcessor_existingMode(_cfPDFToPDF_PDFioProcessor *handle, + pdfio_obj_t *page, + int orig_no); // 1st mode:existing + +void _cfPDFToPDF_PDFioProcessor_create_newMode(_cfPDFToPDF_PDFioProcessor *handle, + pdfio_file_t *pdf, + float width, float height); // 2nd mode:create new + +void _cfPDFToPDF_PDFioProcessor_debug(_cfPDFToPDF_PDFioProcessor *handle, + const _cfPDFToPDFPageRect *rect, + float xpos, float ypos); + +bool _cfPDFToPDF_PDFioProcessor_is_existing(struct _cfPDFToPDF_PDFioProcessor *self); + +pdfio_obj_t* _cfPDFToPDF_PDFioProcessor_get(struct _cfPDFToPDF_PDFioProcessor *handle); + +//inherited functions +void _cfPDFToPDF_PDFioProcessor_destroy(_cfPDFToPDF_PDFioProcessor *handle); + +_cfPDFToPDFPageRect _cfPDFToPDF_PDFioProcessor_get_rect(const _cfPDFToPDF_PDFioProcessor *handle); + +void _cfPDFToPDF_PDFioProcessor_add_border_rect(_cfPDFToPDF_PDFioProcessor *handle, + pdfio_file_t *pdf, + const _cfPDFToPDFPageRect rect, + pdftopdf_border_type_e border, + float fscale); + + +pdftopdf_rotation_e _cfPDFToPDF_PDFioProcessor_crop(_cfPDFToPDF_PDFioProcessor *handle, + const _cfPDFToPDFPageRect *cropRect, + pdftopdf_rotation_e orientation, + pdftopdf_rotation_e param_orientation, + pdftopdf_position_e xpos, + pdftopdf_position_e ypos, + bool scale, bool autorotate, + pdftopdf_doc_t *doc); + + +void _cfPDFToPDF_PDFioPageHandle_add_subpage(_cfPDFToPDF_PDFioProcessor *handle, + _cfPDFToPDF_PDFioProcessor *sub, + pdfio_file_t *pdf, + float xpos, float ypos, float scale, + const _cfPDFToPDFPageRect *crop); + +bool _cfPDFToPDF_PDFioProcessor_is_landscape(const _cfPDFToPDF_PDFioProcessor *handle, + pdftopdf_rotation_e orientation); + +void _cfPDFToPDF_PDFioProcessor_mirror(_cfPDFToPDF_PDFioProcessor *handle, pdfio_file_t *pdf); + +void _cfPDFToPDF_PDFioProcessor_rotate(_cfPDFToPDF_PDFioProcessor *handle, pdftopdf_rotation_e rot); + +void _cfPDFToPDF_PDFioProcessor_add_label(_cfPDFToPDF_PDFioProcessor *handle, + pdfio_file_t *pdf, + const _cfPDFToPDFPageRect *rect, + const char *label); + + diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c new file mode 100644 index 00000000..b55d648f --- /dev/null +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c @@ -0,0 +1,1062 @@ +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#include +#include +#include +#include +#include "pdfio.h" // Replace with appropriate PDFIO headers +#include "C-pdftopdf-processor-private.h" +#include "pdfio-pdftopdf-private.h" +#include "pdfio-tools-private.h" +#include "pdfio-xobject-private.h" +#include "pdfio-pdftopdf-processor-private.h" + +#define DEBUG_assert(x) do { if (!(x)) abort(); } while (0) + +//map definition +unsigned int +hash(const char *key) +{ + unsigned int hash = 0; + while (*key) { + hash = (hash << 5) + *key++; + } + return hash % HASH_TABLE_SIZE; +} + +// Initialize a new hash table +HashTable* +hashCreate_hash_table() +{ + HashTable *table = malloc(sizeof(HashTable)); + for (int i = 0; i < HASH_TABLE_SIZE; i++) + { + table->buckets[i] = NULL; + } + table->count = 0; // Initialize the count of filled elements + return table; +} + +// Create a new key-value pair +KeyValuePair* +create_key_value_pair(const char *key, pdfio_obj_t *value) +{ + KeyValuePair *new_pair = malloc(sizeof(KeyValuePair)); + new_pair->key = strdup(key); // Duplicate the key string + new_pair->value = value; + new_pair->next = NULL; + return new_pair; +} + +// Insert a key-value pair into the hash table +void +hashInsert(HashTable *table, const char *key, pdfio_obj_t *value) +{ + unsigned int index = hash(key); + KeyValuePair *new_pair = create_key_value_pair(key, value); + + // Handle collisions using chaining (linked list) + if (table->buckets[index] == NULL) + { + table->buckets[index] = new_pair; + } + else + { + KeyValuePair *current = table->buckets[index]; + while (current->next != NULL) + { + current = current->next; + } + current->next = new_pair; + } + table->count++; // Increment the count of filled elements +} + +// Retrieve a value by key from the hash table +pdfio_obj_t* +hashGet(HashTable *table, const char *key) +{ + unsigned int index = hash(key); + KeyValuePair *current = table->buckets[index]; + + while (current != NULL) { + if (strcmp(current->key, key) == 0) { + return current->value; + } + current = current->next; + } + return NULL; // Key not found +} + +// Get the number of elements currently filled in the hash table +int +hashGet_filled_count(HashTable *table) +{ + return table->count; +} + +// Free the hash table +void +hashFree_hash_table(HashTable *table) +{ + for (int i = 0; i < HASH_TABLE_SIZE; i++) + { + KeyValuePair *current = table->buckets[i]; + while (current != NULL) + { + KeyValuePair *tmp = current; + current = current->next; + free(tmp->key); + free(tmp); + } + } + free(table); +} + +// main code starts + +// Use: append_debug_box(content, &pe.sub, xpos, ypos); +static void +append_debug_box(char *content, + const _cfPDFToPDFPageRect *box, + float xshift, float yshift) // {{{ +{ + char buf[1024]; + snprintf(buf, sizeof(buf), + "q 1 w 0.1 G\n %f %f m %f %f l S \n %f %f m %f %f l S \n %f %f %f %f re S Q\n", + box->left + xshift, box->bottom + yshift, + box->right + xshift, box->top + yshift, + box->right + xshift, box->bottom + yshift, + box->left + xshift, box->top + yshift, + box->left + xshift, box->bottom + yshift, + box->right - box->left, box->top - box->bottom); + + size_t new_size = strlen(content) + strlen(buf) + 1; + + char *new_content = realloc(content, new_size); + if (!new_content) { + fprintf(stderr, "Memory allocation failed\n"); + return; + } + + content = new_content; // Update the pointer to the new allocated memory + + strcat(content, buf); +} +// }}} + +void +_cfPDFToPDF_PDFioProcessor_existingMode(_cfPDFToPDF_PDFioProcessor *handle, + pdfio_obj_t *page, + int orig_no) // {{{ +{ + handle->page = page; + + if (orig_no == -1) + handle->no = -1; // Default value handling + else + handle->no = orig_no; + + handle->rotation = ROT_0; + + handle->xobjs = NULL; + handle->xobjs->count = 0; + handle->content = NULL; + + handle->pdf = NULL; + handle->orig_pages = NULL; + handle->orig_pages_size = 0; + handle->orig_pages_capacity = 0; + + handle->hasCM = false; + handle->extraheader = NULL; +} +// }}} + +void +_cfPDFToPDF_PDFioProcessor_create_newMode(_cfPDFToPDF_PDFioProcessor *handle, + pdfio_file_t *pdf, + float width, float height) // {{{ +{ + handle->no = 0; + handle->rotation = ROT_0; + + handle->content = strdup("q\n"); // Allocate and copy content string + + pdfio_dict_t *pageDict = pdfioDictCreate(pdf); + + pdfio_rect_t *media_box = _cfPDFToPDFMakeBox(0, 0, width, height); + + pdfio_dict_t *resources_dict = pdfioDictCreate(pdf); + pdfioDictSetNull(resources_dict, "XObject"); + + pdfioDictSetName(pageDict, "Type", "Page"); + pdfioDictSetRect(pageDict, "MediaBox", media_box); + pdfioDictSetDict(pageDict, "Resources", resources_dict); + + // Add /Resources dictionary with empty /XObject entry + pdfio_dict_t *resource_dict = pdfioDictCreate(pdf); + pdfioDictSetNull(resource_dict, "XObject"); + pdfioDictSetDict(pageDict, "Resources", resource_dict); + + pdfio_stream_t *content_stream = pdfioFileCreatePage(pdf, pageDict); + pdfioStreamWrite(content_stream, handle->content, strlen(handle->content)); + pdfioStreamClose(content_stream); + + handle->xobjs = NULL; + handle->xobjs->count = 0; + + handle->pdf = NULL; + handle->orig_pages = NULL; + handle->orig_pages_size = 0; + handle->orig_pages_capacity = 0; + + handle->hasCM = false; + handle->extraheader = NULL; +} +// }}} + +void +_cfPDFToPDF_PDFioProcessor_destroy(_cfPDFToPDF_PDFioProcessor *handle) // {{{ +{ + if (handle->pdf != NULL) + pdfioFileClose(handle->pdf); + + + free(handle->xobjs); + free(handle->content); + free(handle->orig_pages); + + // Free the extraheader if it was allocated + if (handle->extraheader != NULL) + free(handle->extraheader); + +} +// }}} + +bool +_cfPDFToPDF_PDFioProcessor_is_existing(struct _cfPDFToPDF_PDFioProcessor *handle) // {{{ +{ + return (handle->content == NULL || + strlen((const char *)handle->content) == 0); +} +// }}} + + +_cfPDFToPDFPageRect +_cfPDFToPDF_PDFioProcessor_get_rect(const _cfPDFToPDF_PDFioProcessor *handle) // {{{ +{ + pdfio_rect_t trimBox = _cfPDFToPDFGetTrimBox(handle->page); + _cfPDFToPDFPageRect ret = _cfPDFToPDFGetBoxAsRect(&trimBox); + _cfPDFToPDFPageRect_translate(&ret, -ret.left, -ret.bottom); + _cfPDFToPDFPageRect_rotate_move(&ret, _cfPDFToPDFGetRotate(handle->page), ret.width, ret.height); + _cfPDFToPDFPageRect_scale(&ret, _cfPDFToPDFGetUserUnit(handle->page)); + + return (ret); +} +// }}} + +pdfio_obj_t* +_cfPDFToPDF_PDFioProcessor_get(struct _cfPDFToPDF_PDFioProcessor *handle) // {{{ +{ + pdfio_dict_t *resources, *contents_dict; + pdfio_stream_t *contents_stream; + + pdfio_obj_t *ret = handle->page; + + if (!_cfPDFToPDF_PDFioProcessor_is_existing(handle)) + { + // Step 1: Replace /XObject in /Resources + resources = pdfioDictGetDict(pdfioObjGetDict(ret), "/Resources"); + if (resources) + { + char name_buffer[handle->xobjs->count]; + int xobj_index = 0; + // Loop through all the buckets + for (int i = 0; i < HASH_TABLE_SIZE && xobj_index < handle->xobjs->count; i++) + { + KeyValuePair *current = handle->xobjs->buckets[i]; + + // Traverse the linked list in case of collisions + while (current != NULL) + { + // Build the XObject name (e.g., "/X1", "/X2", ...) + snprintf(name_buffer, sizeof(name_buffer), "/X%d", xobj_index + 1); + + // Add the object to the PDF resources dictionary + pdfioDictSetObj(resources, name_buffer, current->value); // current->value is pdfio_obj_t * + + // Move to the next element in the linked list (in case of collisions) + current = current->next; + + // Increment the XObject index + xobj_index++; + } + } + + pdfioDictSetDict(resources, "/XObject", resources); // Set new dictionary for XObject + } + + contents_stream = pdfioPageOpenStream(ret, 0, true); // Open the content stream of the page + if (contents_stream) + { + pdfioStreamPuts(contents_stream, "Q\n"); // Append "Q\n" to content + pdfioStreamClose(contents_stream); // Close the stream + } + + contents_dict = pdfioDictGetDict(pdfioObjGetDict(ret), "/Contents"); + if (contents_dict) + { + pdfioDictSetNull(contents_dict, "/Filter"); // Remove filter keys + pdfioDictSetNull(contents_dict, "/DecodeParms"); + } + + pdfioDictSetNumber(pdfioObjGetDict(ret), "/Rotate", _cfPDFToPDFMakeRotate(handle->rotation)); + } + else + { + pdftopdf_rotation_e rot = pdftopdf_rotation_add(_cfPDFToPDFGetRotate(handle->page), + handle->rotation); + pdfioDictSetNumber(pdfioObjGetDict(handle->page), "Rotate", rot); + } + + handle->page = NULL; + return ret; +} +// }}} + +static _cfPDFToPDFPageRect +ungetRect(_cfPDFToPDFPageRect rect, + const _cfPDFToPDF_PDFioProcessor *ph, + pdftopdf_rotation_e rotation, + pdfio_obj_t *page) // {{{ +{ + + _cfPDFToPDFPageRect pg1 = _cfPDFToPDF_PDFioProcessor_get_rect(ph); + pdfio_rect_t TrimBox = _cfPDFToPDFGetTrimBox(page); + _cfPDFToPDFPageRect pg2 = _cfPDFToPDFGetBoxAsRect(&TrimBox); + rect.width = pg1.width; + rect.height = pg1.height; + _cfPDFToPDFPageRect_rotate_move(&rect, pdftopdf_rotation_neg(_cfPDFToPDFGetRotate(page)), pg1.width, pg1.height); + + _cfPDFToPDFPageRect_scale(&rect, 1.0/_cfPDFToPDFGetUserUnit(page)); + _cfPDFToPDFPageRect_translate(&rect, pg2.left, pg2.bottom); + + return rect; +} +// }}} + +void +_cfPDFToPDF_PDFioProcessor_add_border_rect(_cfPDFToPDF_PDFioProcessor *handle, + pdfio_file_t *pdf, + const _cfPDFToPDFPageRect givenRect, + pdftopdf_border_type_e border, + float fscale) // {{{ +{ + double lw = (border & THICK) ? 0.5 : 0.24; + double line_width = lw * fscale; + double margin = 2.25 * fscale; + + _cfPDFToPDFPageRect rect = ungetRect(givenRect, handle, handle->rotation, handle->page); + + char boxcmd[1024]; + char buffer[64]; + + snprintf(boxcmd, sizeof(boxcmd), "q\n"); + + sprintf(buffer, "%.2f", line_width); + strncat(boxcmd, buffer, sizeof(boxcmd) - strlen(boxcmd) - 1); + strncat(boxcmd, " w 0 G \n", sizeof(boxcmd) - strlen(boxcmd) - 1); + + sprintf(buffer, "%.2f", rect.left + margin); + strncat(boxcmd, buffer, sizeof(boxcmd) - strlen(boxcmd) - 1); + strncat(boxcmd, " ", sizeof(boxcmd) - strlen(boxcmd) - 1); + + sprintf(buffer, "%.2f", rect.bottom + margin); + strncat(boxcmd, buffer, sizeof(boxcmd) - strlen(boxcmd) - 1); + strncat(boxcmd, " ", sizeof(boxcmd) - strlen(boxcmd) - 1); + + sprintf(buffer, "%.2f", rect.right - rect.left + 2 * margin); + strncat(boxcmd, buffer, sizeof(boxcmd) - strlen(boxcmd) - 1); + strncat(boxcmd, " ", sizeof(boxcmd) - strlen(boxcmd) - 1); + + sprintf(buffer, "%.2f", rect.top - rect.bottom - 2 * margin); + strncat(boxcmd, buffer, sizeof(boxcmd) - strlen(boxcmd) - 1); + strncat(boxcmd, " re S \n", sizeof(boxcmd) - strlen(boxcmd) - 1); + + if (border & TWO) + { + margin += 2 * fscale; + sprintf(buffer, "%.2f", rect.left + margin); + strncat(boxcmd, buffer, sizeof(boxcmd) - strlen(boxcmd) - 1); + strncat(boxcmd, " ", sizeof(boxcmd) - strlen(boxcmd) - 1); + + sprintf(buffer, "%.2f", rect.bottom + margin); + strncat(boxcmd, buffer, sizeof(boxcmd) - strlen(boxcmd) - 1); + strncat(boxcmd, " ", sizeof(boxcmd) - strlen(boxcmd) - 1); + + sprintf(buffer, "%.2f", rect.right - rect.left - 2 * margin); + strncat(boxcmd, buffer, sizeof(boxcmd) - strlen(boxcmd) - 1); + strncat(boxcmd, " ", sizeof(boxcmd) - strlen(boxcmd) - 1); + + sprintf(buffer, "%.2f", rect.top - rect.bottom - 2 * margin); + strncat(boxcmd, buffer, sizeof(boxcmd) - strlen(boxcmd) - 1); + strncat(boxcmd, " re S \n", sizeof(boxcmd) - strlen(boxcmd) - 1); + } + strncat(boxcmd, "Q\n", sizeof(boxcmd) - strlen(boxcmd) - 1); + +#ifdef DEBUG + const char *pre = "%pdftopdf q\nq\n"; + const char *post = "%pdftopdf Q\nQ\n"; + + pdfio_dict_t *stm1_dict = pdfioDictCreate(pdf); + pdfio_obj_t *stm1_obj = pdfioFileCreateObj(pdf, stm1_dict); + pdfio_stream_t *stm1 = pdfioObjCreateStream(stm1_obj, PDFIO_FILTER_NONE); + if (stm1) + { + pdfioStreamWrite(stm1, pre, strlen(pre)); + pdfioStreamClose(stm1); // Finalize the stream + } + else + fprintf(stderr, "Failed to create PDF stream for pre content\n"); + + char combined[2048]; // Ensure this is large enough + snprintf(combined, sizeof(combined), "%s%s", post, boxcmd); + + pdfio_dict_t *stm2_dict = pdfioDictCreate(pdf); + pdfio_obj_t *stm2_obj = pdfioFileCreateObj(pdfio_file_t *pdf, stm2_dict); + pdfio_stream_t *stm2 = pdfioObjCreateStream(stm2_obj, PDFIO_FILTER_NONE); + if (stm2) + { + pdfioStreamWrite(stm2, combined, strlen(combined)); + pdfioStreamClose(stm2); // Finalize the stream + } + else + fprintf(stderr, "Failed to create PDF stream for post content\n"); + + +#else + pdfio_dict_t *stm_dict = pdfioDictCreate(pdf); + pdfio_obj_t *stm_obj = pdfioFileCreateObj(pdf, stm_dict); + pdfio_stream_t *stm = pdfioObjCreateStream(stm_obj, PDFIO_FILTER_NONE); + if (stm) + { + pdfioStreamWrite(stm, boxcmd, strlen(boxcmd)); + pdfioStreamClose(stm); // Finalize the stream + } + else + fprintf(stderr, "Failed to create PDF stream for boxcmd content\n"); +#endif +} +// }}} + +pdftopdf_rotation_e +_cfPDFToPDF_PDFioProcessor_crop(_cfPDFToPDF_PDFioProcessor *handle, + const _cfPDFToPDFPageRect *cropRect, + pdftopdf_rotation_e orientation, + pdftopdf_rotation_e param_orientation, + pdftopdf_position_e xpos, pdftopdf_position_e ypos, + bool scale, bool autorotate, + pdftopdf_doc_t *doc) // {{{ +{ + pdftopdf_rotation_e save_rotate = _cfPDFToPDFGetRotate(handle->page); + + pdfio_dict_t *pageDict = pdfioObjGetDict(handle->page); + if (orientation == ROT_0 || orientation == ROT_180) + pdfioDictSetNumber(pageDict, "Rotate", _cfPDFToPDFMakeRotate(ROT_90)); + else + pdfioDictSetNumber(pageDict, "Rotate", _cfPDFToPDFMakeRotate(ROT_0)); + + pdfio_rect_t trimBox = _cfPDFToPDFGetTrimBox(handle->page); + _cfPDFToPDFPageRect currpage = _cfPDFToPDFGetBoxAsRect(&trimBox); + + double width = currpage.right - currpage.left; + double height = currpage.top - currpage.bottom; + double pageWidth = cropRect->right - cropRect->left; + double pageHeight = cropRect->top - cropRect->bottom; + double final_w, final_h; + + pdftopdf_rotation_e pageRot = _cfPDFToPDFGetRotate(handle->page); + if ((autorotate && + (((pageRot == ROT_0 || pageRot == ROT_180) && pageWidth <= pageHeight) || + ((pageRot == ROT_90 || pageRot == ROT_270) && pageWidth > pageHeight))) || + (!autorotate && (param_orientation == ROT_90 || param_orientation == ROT_270))) + { + double temp=pageHeight; + pageHeight=pageWidth; + pageWidth=temp; + } + + if (scale) + { + if (width * pageHeight / pageWidth <= height) + { + final_w = width; + final_h = width * pageHeight / pageWidth; + } + else + { + final_w = height * pageWidth / pageHeight; + final_h = height; + } + } + else + { + final_w = pageWidth; + final_h = pageHeight; + } + + if (doc->logfunc) + { + doc->logfunc(doc->logdata, 1, "cfFilterPDFToPDF: After Cropping: %lf %lf %lf %lf", + width, height, final_w, final_h); + } + + double posw = (width - final_w) / 2; + double posh = (height - final_h) / 2; + + if (xpos == LEFT) + posw = 0; + else if (xpos == RIGHT) + posw *= 2; + + if (ypos == TOP) + posh *= 2; + else if (ypos == BOTTOM) + posh = 0; + + currpage.left += posw; + currpage.bottom += posh; + currpage.top = currpage.bottom + final_h; + currpage.right = currpage.left + final_w; + + pdfioDictSetRect(pageDict, "TrimBox", + _cfPDFToPDFMakeBox(currpage.left, currpage.bottom, currpage.right, currpage.top)); + pdfioDictSetNumber(pageDict, "Rotate", _cfPDFToPDFMakeRotate(save_rotate)); + + return _cfPDFToPDFGetRotate(handle->page); +} +// }}} + +bool +_cfPDFToPDF_PDFioProcessor_is_landscape(const _cfPDFToPDF_PDFioProcessor *handle, + pdftopdf_rotation_e orientation) // {{{ +{ + pdftopdf_rotation_e save_rotate = _cfPDFToPDFGetRotate(handle->page); + + // Temporarily set the page rotation based on the orientation + + pdfio_dict_t *pageDict = pdfioObjGetDict(handle->page); + + if (orientation == ROT_0 || orientation == ROT_180) + pdfioDictSetNumber(pageDict, "Rotate", ROT_90); + else + pdfioDictSetNumber(pageDict, "Rotate", ROT_0); + + // Get the current page dimensions after rotation + pdfio_rect_t trimBox = _cfPDFToPDFGetTrimBox(handle->page); + _cfPDFToPDFPageRect currpage = _cfPDFToPDFGetBoxAsRect(&trimBox); + double width = currpage.right - currpage.left; + double height = currpage.top - currpage.bottom; + + // Restore the original page rotation + pdfioDictSetNumber(pageDict, "Rotate", save_rotate); + + // Determine if the page is in landscape orientation + if (width > height) + return true; + return false; +} +// }}} + +void +_cfPDFToPDF_PDFioPageHandle_add_subpage(_cfPDFToPDF_PDFioProcessor *handle, + _cfPDFToPDF_PDFioProcessor *sub, + pdfio_file_t *pdf, + float xpos, float ypos, float scale, + const _cfPDFToPDFPageRect *crop) // {{{ +{ + char xoname[64]; + snprintf(xoname, sizeof(xoname), "/X%d", (sub->no != -1) ? sub->no : ++handle->no); + + if (crop) + { + _cfPDFToPDFPageRect pg = _cfPDFToPDF_PDFioProcessor_get_rect(sub); + _cfPDFToPDFPageRect tmp = *crop; + tmp.width = tmp.right - tmp.left; + tmp.height = tmp.top - tmp.bottom; + pdftopdf_rotation_e tempRotation = _cfPDFToPDFGetRotate(sub->page); + _cfPDFToPDFPageRect_rotate_move(&tmp, pdftopdf_rotation_neg(tempRotation), tmp.width, tmp.height); + + if (pg.width < tmp.width) + pg.right = pg.left + tmp.width; + if (pg.height < tmp.height) + pg.top = pg.bottom + tmp.height; + + _cfPDFToPDFPageRect rect = ungetRect(pg, sub, ROT_0, sub->page); + + pdfio_rect_t *trimBox = _cfPDFToPDFMakeBox(rect.left, rect.bottom, rect.right, rect.top); + + // Set TrimBox in pdfio (adjust if pdfio allows this kind of modification) + pdfio_dict_t *pageDict = pdfioObjGetDict(sub->page); + pdfioDictSetRect(pageDict, "TrimBox", trimBox); + } + + hashInsert(handle->xobjs, xoname,_cfPDFToPDFMakeXObject(pdf, sub->page)); + // Prepare transformation matrix + _cfPDFToPDFMatrix mtx; + _cfPDFToPDFMatrix_init(&mtx); + _cfPDFToPDFMatrix_translate(&mtx, xpos, ypos); + _cfPDFToPDFMatrix_scale(&mtx, scale, scale); + _cfPDFToPDFMatrix_rotate(&mtx, sub->rotation); + + if (crop) { + _cfPDFToPDFMatrix_translate(&mtx, crop->left, crop->bottom); + } + + strcat(handle->content, "q\n "); + char matrix_string[128]; + _cfPDFToPDFMatrix_get_string(&mtx, matrix_string, 128); + strcat(handle->content, matrix_string); + strcat(handle->content, " cm\n "); + + if (crop) { + strcat(handle->content, " cm\n "); + char crop_string[128]; + snprintf(crop_string, sizeof(crop_string), "0 0 %.2f %.2f re W n\n", crop->right - crop->left, crop->top - crop->bottom); + strcat(handle->content, crop_string); + } + + strcat(handle->content, xoname); + strcat(handle->content, " Do\nQ\n"); +} +// }}} + +void +_cfPDFToPDF_PDFioProcessor_mirror(_cfPDFToPDF_PDFioProcessor *handle, + pdfio_file_t *pdf) +{ + _cfPDFToPDFPageRect orig = _cfPDFToPDF_PDFioProcessor_get_rect(handle); + if(_cfPDFToPDF_PDFioProcessor_is_existing(handle)) + { + char xoname[10]; + snprintf(xoname, sizeof(xoname), "/X%d", 1); // Assuming 'no' is 1 for example + + // Get the page to mirror (this would be replaced by actual pdfio page handling) + pdfio_obj_t *subpage = _cfPDFToPDF_PDFioProcessor_get(handle);; + + _cfPDFToPDF_PDFioProcessor_create_newMode(handle, pdf, orig.width, orig.height); + // Reinitialize the handle with new dimensions + hashInsert(handle->xobjs, xoname,_cfPDFToPDFMakeXObject(pdf, subpage)); + + char temp_content[1024]; + snprintf(temp_content, sizeof(temp_content), "%s Do\n", xoname); + strcat(handle->content, temp_content); + } + + static const char *pre = "%pdftopdf cm\n"; + char mrcmd[100]; + snprintf(mrcmd, sizeof(mrcmd), "-1 0 0 1 %.2f 0 cm\n", orig.right); + + // Insert the mirroring matrix at the beginning of the content + size_t new_len = strlen(pre) + strlen(mrcmd) + strlen(handle->content) + 1; + char *new_content = (char *)malloc(new_len); + snprintf(new_content, new_len, "%s%s%s", pre, mrcmd, handle->content); + + free(handle->content); // Free the old content + handle->content = new_content; +} + + +void +_cfPDFToPDF_PDFioProcessor_rotate(_cfPDFToPDF_PDFioProcessor *handle, + pdftopdf_rotation_e rot) +{ + handle->rotation = rot; +} + +void +_cfPDFToPDF_PDFioProcessor_add_label(_cfPDFToPDF_PDFioProcessor *handle, + const _cfPDFToPDFPageRect *rect, + const char *label) +{ + + _cfPDFToPDFPageRect rect_mod = ungetRect(rect); + + if (rect_mod.left > rect_mod.right || rect_mod.bottom > rect_mod.top) + { + fprintf(stderr, "Invalid rectangle dimensions!\n"); + return; + } + pdfio_dict_t *font_dict = pdfioDictCreate(pdf); + pdfioDictSetName(font_dict, "Type", "Font"); + pdfioDictSetName(font_dict, "Subtype", "Type1"); + pdfioDictSetName(font_dict, "Name", "pagelabel-font"); + pdfioDictSetName(font_dict, "BaseFont", "Helvetica"); + + // Add font to the document as an indirect object + pdfio_obj_t *font_obj = pdfioFileCreateObj(pdf, font_dict); + + // Get the Resources dictionary from the page + pdfio_dict_t *resources = pdfioObjGetDict(page); + if (resources == NULL) + { + resources = pdfioDictCreate(pdf); + pdfioDictSetDict(resources, "Font", pdfioDictCreate(pdf)); + } + + // Get or create the Font dictionary in Resources + pdfio_dict_t *font_resources = pdfioDictGetDict(resources, "Font"); + if (font_resources == NULL) + { + font_resources = pdfioDictCreate(pdf); + pdfioDictSetDict(resources, "Font", font_resources); + } + + // Add the pagelabel-font to the Font dictionary in Resources + pdfioDictSetObj(font_resources, "pagelabel-font", font_obj); + + // Finally, replace the Resources key in the page with updated Resources + pdfioDictSetDict(resources, "Resources", resources); + + double margin = 2.25; + double height = 12; + + // Start creating the PDF content commands (simplified for pdfio) + char boxcmd[1024] = "q\n"; + + // White filled rectangle (top) + snprintf(boxcmd + strlen(boxcmd), sizeof(boxcmd) - strlen(boxcmd), + "1 1 1 rg\n%f %f %f %f re f\n", + rect_mod.left + margin, rect_mod.top - height - 2 * margin, + rect_mod.right - rect_mod.left - 2 * margin, height + 2 * margin); + + // White filled rectangle (bottom) + snprintf(boxcmd + strlen(boxcmd), sizeof(boxcmd) - strlen(boxcmd), + "%f %f %f %f re f\n", + rect_mod.left + margin, rect_mod.bottom + height + margin, + rect_mod.right - rect_mod.left - 2 * margin, height + 2 * margin); + + // Black outline (top) + snprintf(boxcmd + strlen(boxcmd), sizeof(boxcmd) - strlen(boxcmd), + "0 0 0 RG\n%f %f %f %f re S\n", + rect_mod.left + margin, rect_mod.top - height - 2 * margin, + rect_mod.right - rect_mod.left - 2 * margin, height + 2 * margin); + + // Black outline (bottom) + snprintf(boxcmd + strlen(boxcmd), sizeof(boxcmd) - strlen(boxcmd), + "%f %f %f %f re S\n", + rect_mod.left + margin, rect_mod.bottom + height + margin, + rect_mod.right - rect_mod.left - 2 * margin, height + 2 * margin); + + // Black text (top) + snprintf(boxcmd + strlen(boxcmd), sizeof(boxcmd) - strlen(boxcmd), + "0 0 0 rg\nBT\n/f1 12 Tf\n%f %f Td\n(%s) Tj\nET\n", + rect_mod.left + 2 * margin, rect_mod.top - height - margin, label); + + // Black text (bottom) + snprintf(boxcmd + strlen(boxcmd), sizeof(boxcmd) - strlen(boxcmd), + "BT\n/f1 12 Tf\n%f %f Td\n(%s) Tj\nET\n", + rect_mod.left + 2 * margin, rect_mod.bottom + height + 2 * margin, label); + + // End the graphic context + strcat(boxcmd, "Q\n"); + + const char *pre = "%pdftopdf q\nq\n"; + char post[256]; // Make sure the buffer is large enough for the combined string + + // Combine the "post" string with the passed boxcmd + snprintf(post, sizeof(post), "%%pdftopdf Q\nQ\n%s", boxcmd); + + // Create the first stream (pre) + pdfio_stream_t *stream1 = pdfioPageOpenStream(page, PDFIO_FILTER_FLATE, true); + pdfioStreamPuts(stream1, pre); + pdfioStreamClose(stream1); + + // Create the second stream (post + boxcmd) + pdfio_stream_t *stream2 = pdfioPageOpenStream(page, PDFIO_FILTER_FLATE, true); + pdfioStreamPuts(stream2, post); + pdfioStreamClose(stream2); + + // Add the streams as page contents + pdfioPageDictAddFont(page, "Contents", stream1); // before content + pdfioPageDictAddFont(page, "Contents", stream2); // after content +} + +void +debug(_cfPDFToPDF_PDFioProcessor *handle, + const _cfPDFToPDFPageRect *rect, + float xpos, float ypos) +{ + if (!_cfPDFToPDF_PDFioProcessor_is_existing(handle)) + return; + + // append to the content stream + append_debug_box(handle->content, rect, xpos, ypos); +} + +/* +// Define the _cfPDFToPDFQPDFProcessor structure +struct _cfPDFToPDFQPDFProcessor { + pdfio_file_t *pdf; // pdfio object to represent the PDF document + _cfPDFToPDFQPDFPageHandle **orig_pages; + int num_pages; + bool hasCM; + char *extraheader; // Dynamic string for comments +}; + +void +_cfPDFToPDFQPDFProcessor_close_file(_cfPDFToPDFQPDFProcessor *processor) +{ + if (processor->pdf) + { + pdfioFileClose(processor->pdf); + processor->pdf = NULL; + } + processor->hasCM = false; +} + +bool +_cfPDFToPDFQPDFProcessor_load_file(_cfPDFToPDFQPDFProcessor *processor, + FILE *f, pdftopdf_doc_t *doc, + pdftopdf_arg_ownership_e take, + int flatten_forms) +{ + _cfPDFToPDFQPDFProcessor_close_file(processor); + + if (!f) { + // Handle error in C style + return false; + } + + // Simulate opening the file with PDFIO + processor->pdf = pdfioFileOpen(f, NULL); + if (!processor->pdf) { + if (take == CF_PDFTOPDF_TAKE_OWNERSHIP) + fclose(f); + return false; + } + + // Call start function to initialize the pages + _cfPDFToPDFQPDFProcessor_start(processor, flatten_forms); + return true; +} + +bool +_cfPDFToPDFQPDFProcessor_load_filename(_cfPDFToPDFQPDFProcessor *processor, + const char *name, pdftopdf_doc_t *doc, + int flatten_forms) +{ + _cfPDFToPDFQPDFProcessor_close_file(processor); + + processor->pdf = pdfioFileOpenFile(name, NULL); + if (!processor->pdf) { + return false; + } + + // Call start function to initialize the pages + _cfPDFToPDFQPDFProcessor_start(processor, flatten_forms); + return true; +} + +void +_cfPDFToPDFQPDFProcessor_start(_cfPDFToPDFQPDFProcessor *processor, int flatten_forms) +{ + DEBUG_assert(processor->pdf); + + processor->orig_pages = pdfioFileGetPages(processor->pdf, &(processor->num_pages)); + // Remove the pages from the PDF document for processing + for (int i = 0; i < processor->num_pages; i++) { + pdfioPageRemove(processor->orig_pages[i]); + } + + // Initialize other PDF data as necessary (e.g., remove defunct keys) + pdfioFileRemoveKey(processor->pdf, "/PageMode"); + pdfioFileRemoveKey(processor->pdf, "/Outlines"); + pdfioFileRemoveKey(processor->pdf, "/OpenAction"); + pdfioFileRemoveKey(processor->pdf, "/PageLabels"); +} + +bool +_cfPDFToPDFQPDFProcessor_check_print_permissions(_cfPDFToPDFQPDFProcessor *processor, pdftopdf_doc_t *doc) +{ + if (!processor->pdf) { + return false; + } + + // Simulate permission checking with PDFIO + return pdfioFileAllowPrint(processor->pdf); +} + +_cfPDFToPDFQPDFPageHandle ** +_cfPDFToPDFQPDFProcessor_get_pages(_cfPDFToPDFQPDFProcessor *processor, + pdftopdf_doc_t *doc, int *num_pages) +{ + if (!processor->pdf) { + *num_pages = 0; + return NULL; + } + + *num_pages = processor->num_pages; + return processor->orig_pages; +} + +_cfPDFToPDFQPDFPageHandle * +_cfPDFToPDFQPDFProcessor_new_page(_cfPDFToPDFQPDFProcessor *processor, + float width, float height, + pdftopdf_doc_t *doc) +{ + if (!processor->pdf) { + return NULL; + } + + return _cfPDFToPDFQPDFPageHandle_new_new(processor->pdf, width, height); +} + +void +_cfPDFToPDFQPDFProcessor_add_page(_cfPDFToPDFQPDFProcessor *processor, + _cfPDFToPDFQPDFPageHandle *page, bool front) +{ + DEBUG_assert(processor->pdf); + if (front) { + pdfioFileInsertPage(processor->pdf, page->page, 0); // Insert at the front + } else { + pdfioFileAppendPage(processor->pdf, page->page); // Append at the end + } +} + +void +_cfPDFToPDFQPDFProcessor_multiply(_cfPDFToPDFQPDFProcessor *processor, + int copies, bool collate) +{ + DEBUG_assert(processor->pdf); + DEBUG_assert(copies > 0); + + _cfPDFToPDFQPDFPageHandle **pages = _cfPDFToPDFQPDFProcessor_get_pages(processor, NULL, &processor->num_pages); + + if (collate) { + for (int i = 1; i < copies; i++) { + for (int j = 0; j < processor->num_pages; j++) { + pdfioFileAppendPage(processor->pdf, pages[j]->page); + } + } + } else { + for (int j = 0; j < processor->num_pages; j++) { + for (int i = 1; i < copies; i++) { + pdfioFileAppendPage(processor->pdf, pages[j]->page); + } + } + } +} + +void +_cfPDFToPDFQPDFProcessor_auto_rotate_all(_cfPDFToPDFQPDFProcessor *processor, + bool dst_lscape, + pdftopdf_rotation_e normal_landscape) +{ + DEBUG_assert(processor->pdf); + + for (int i = 0; i < processor->num_pages; i++) { + _cfPDFToPDFQPDFPageHandle *page = processor->orig_pages[i]; + + pdftopdf_rotation_e src_rot = pdfioPageGetRotation(page->page); + _cfPDFToPDFPageRect rect = _cfPDFToPDFQPDFPageHandle_get_rect(page); + + bool src_lscape = rect.width > rect.height; + if (src_lscape != dst_lscape) { + pdfioPageSetRotation(page->page, src_rot + normal_landscape); + } + } +} + +void +_cfPDFToPDFQPDFProcessor_add_cm(_cfPDFToPDFQPDFProcessor *processor, + const char *defaulticc, const char *outputicc) +{ + DEBUG_assert(processor->pdf); + + if (pdfioFileHasOutputIntent(processor->pdf)) { + return; // Nothing to do + } + + // Simulate adding ICC profile and output intent with PDFIO + pdfioFileAddICCProfile(processor->pdf, defaulticc); + pdfioFileAddOutputIntent(processor->pdf, outputicc); + + processor->hasCM = true; +} + +void +_cfPDFToPDFQPDFProcessor_set_comments(_cfPDFToPDFQPDFProcessor *processor, + const char **comments, int num_comments) +{ + if (processor->extraheader) { + free(processor->extraheader); + } + + // Concatenate comments into one string + int total_len = 0; + for (int i = 0; i < num_comments; i++) { + total_len += strlen(comments[i]) + 1; + } + + processor->extraheader = malloc(total_len + 1); + processor->extraheader[0] = '\0'; + + for (int i = 0; i < num_comments; i++) { + strcat(processor->extraheader, comments[i]); + strcat(processor->extraheader, "\n"); + } +} + +void +_cfPDFToPDFQPDFProcessor_emit_file(_cfPDFToPDFQPDFProcessor *processor, + FILE *dst, pdftopdf_doc_t *doc, + pdftopdf_arg_ownership_e take) +{ + if (!processor->pdf) { + return; + } + + // Simulate writing the PDF to a file with PDFIO + pdfioFileWrite(processor->pdf, dst, processor->hasCM, processor->extraheader); + + if (take == CF_PDFTOPDF_TAKE_OWNERSHIP) { + fclose(dst); + } +} + +void +_cfPDFToPDFQPDFProcessor_emit_filename(_cfPDFToPDFQPDFProcessor *processor, + const char *name, pdftopdf_doc_t *doc) +{ + if (!processor->pdf) { + return; + } + + // Special case: name == NULL -> stdout + FILE *output = name ? fopen(name, "wb") : stdout; + + // Simulate writing the PDF to a file with PDFIO + pdfioFileWrite(processor->pdf, output, processor->hasCM, processor->extraheader); + + if (name) { + fclose(output); + } +} + +bool +_cfPDFToPDFQPDFProcessor_has_acro_form(_cfPDFToPDFQPDFProcessor *processor) +{ + if (!processor->pdf) { + return false; + } + + // Simulate checking for an AcroForm in the PDF with PDFIO + return pdfioFileHasAcroForm(processor->pdf); +} + +*/ From a2268fe50212fea8ffaa8eaff5913bec024aaa98 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Thu, 5 Sep 2024 18:50:45 +0530 Subject: [PATCH 42/64] update --- .../pdfio-pdftopdf-processor-private.h | 8 ++- .../pdftopdf/pdfio-pdftopdf-processor.c | 51 +++++++++---------- 2 files changed, 26 insertions(+), 33 deletions(-) diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h index 81cbc269..805c6019 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h @@ -49,7 +49,8 @@ typedef struct _cfPDFToPDF_PDFioProcessor{ //_cfPDFToPDFQPDFPageHandle functions void _cfPDFToPDF_PDFioProcessor_existingMode(_cfPDFToPDF_PDFioProcessor *handle, - pdfio_obj_t *page, + pdfio_file_t *pdf, + pdfio_obj_t *page, int orig_no); // 1st mode:existing void _cfPDFToPDF_PDFioProcessor_create_newMode(_cfPDFToPDF_PDFioProcessor *handle, @@ -70,7 +71,6 @@ void _cfPDFToPDF_PDFioProcessor_destroy(_cfPDFToPDF_PDFioProcessor *handle); _cfPDFToPDFPageRect _cfPDFToPDF_PDFioProcessor_get_rect(const _cfPDFToPDF_PDFioProcessor *handle); void _cfPDFToPDF_PDFioProcessor_add_border_rect(_cfPDFToPDF_PDFioProcessor *handle, - pdfio_file_t *pdf, const _cfPDFToPDFPageRect rect, pdftopdf_border_type_e border, float fscale); @@ -88,19 +88,17 @@ pdftopdf_rotation_e _cfPDFToPDF_PDFioProcessor_crop(_cfPDFToPDF_PDFioProcessor * void _cfPDFToPDF_PDFioPageHandle_add_subpage(_cfPDFToPDF_PDFioProcessor *handle, _cfPDFToPDF_PDFioProcessor *sub, - pdfio_file_t *pdf, float xpos, float ypos, float scale, const _cfPDFToPDFPageRect *crop); bool _cfPDFToPDF_PDFioProcessor_is_landscape(const _cfPDFToPDF_PDFioProcessor *handle, pdftopdf_rotation_e orientation); -void _cfPDFToPDF_PDFioProcessor_mirror(_cfPDFToPDF_PDFioProcessor *handle, pdfio_file_t *pdf); +void _cfPDFToPDF_PDFioProcessor_mirror(_cfPDFToPDF_PDFioProcessor *handle); void _cfPDFToPDF_PDFioProcessor_rotate(_cfPDFToPDF_PDFioProcessor *handle, pdftopdf_rotation_e rot); void _cfPDFToPDF_PDFioProcessor_add_label(_cfPDFToPDF_PDFioProcessor *handle, - pdfio_file_t *pdf, const _cfPDFToPDFPageRect *rect, const char *label); diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c index b55d648f..1f25dc0e 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c @@ -150,7 +150,8 @@ append_debug_box(char *content, void _cfPDFToPDF_PDFioProcessor_existingMode(_cfPDFToPDF_PDFioProcessor *handle, - pdfio_obj_t *page, + pdfio_file_t *pdf, + pdfio_obj_t *page, int orig_no) // {{{ { handle->page = page; @@ -166,7 +167,7 @@ _cfPDFToPDF_PDFioProcessor_existingMode(_cfPDFToPDF_PDFioProcessor *handle, handle->xobjs->count = 0; handle->content = NULL; - handle->pdf = NULL; + handle->pdf = pdf; handle->orig_pages = NULL; handle->orig_pages_size = 0; handle->orig_pages_capacity = 0; @@ -209,7 +210,7 @@ _cfPDFToPDF_PDFioProcessor_create_newMode(_cfPDFToPDF_PDFioProcessor *handle, handle->xobjs = NULL; handle->xobjs->count = 0; - handle->pdf = NULL; + handle->pdf = pdf; handle->orig_pages = NULL; handle->orig_pages_size = 0; handle->orig_pages_capacity = 0; @@ -351,7 +352,6 @@ ungetRect(_cfPDFToPDFPageRect rect, void _cfPDFToPDF_PDFioProcessor_add_border_rect(_cfPDFToPDF_PDFioProcessor *handle, - pdfio_file_t *pdf, const _cfPDFToPDFPageRect givenRect, pdftopdf_border_type_e border, float fscale) // {{{ @@ -412,8 +412,8 @@ _cfPDFToPDF_PDFioProcessor_add_border_rect(_cfPDFToPDF_PDFioProcessor *handle, const char *pre = "%pdftopdf q\nq\n"; const char *post = "%pdftopdf Q\nQ\n"; - pdfio_dict_t *stm1_dict = pdfioDictCreate(pdf); - pdfio_obj_t *stm1_obj = pdfioFileCreateObj(pdf, stm1_dict); + pdfio_dict_t *stm1_dict = pdfioDictCreate(handle->pdf); + pdfio_obj_t *stm1_obj = pdfioFileCreateObj(handle->pdf, stm1_dict); pdfio_stream_t *stm1 = pdfioObjCreateStream(stm1_obj, PDFIO_FILTER_NONE); if (stm1) { @@ -426,8 +426,8 @@ _cfPDFToPDF_PDFioProcessor_add_border_rect(_cfPDFToPDF_PDFioProcessor *handle, char combined[2048]; // Ensure this is large enough snprintf(combined, sizeof(combined), "%s%s", post, boxcmd); - pdfio_dict_t *stm2_dict = pdfioDictCreate(pdf); - pdfio_obj_t *stm2_obj = pdfioFileCreateObj(pdfio_file_t *pdf, stm2_dict); + pdfio_dict_t *stm2_dict = pdfioDictCreate(handle->pdf); + pdfio_obj_t *stm2_obj = pdfioFileCreateObj(handle->pdf, stm2_dict); pdfio_stream_t *stm2 = pdfioObjCreateStream(stm2_obj, PDFIO_FILTER_NONE); if (stm2) { @@ -439,8 +439,8 @@ _cfPDFToPDF_PDFioProcessor_add_border_rect(_cfPDFToPDF_PDFioProcessor *handle, #else - pdfio_dict_t *stm_dict = pdfioDictCreate(pdf); - pdfio_obj_t *stm_obj = pdfioFileCreateObj(pdf, stm_dict); + pdfio_dict_t *stm_dict = pdfioDictCreate(handle->pdf); + pdfio_obj_t *stm_obj = pdfioFileCreateObj(handle->pdf, stm_dict); pdfio_stream_t *stm = pdfioObjCreateStream(stm_obj, PDFIO_FILTER_NONE); if (stm) { @@ -575,7 +575,6 @@ _cfPDFToPDF_PDFioProcessor_is_landscape(const _cfPDFToPDF_PDFioProcessor *handle void _cfPDFToPDF_PDFioPageHandle_add_subpage(_cfPDFToPDF_PDFioProcessor *handle, _cfPDFToPDF_PDFioProcessor *sub, - pdfio_file_t *pdf, float xpos, float ypos, float scale, const _cfPDFToPDFPageRect *crop) // {{{ { @@ -605,7 +604,7 @@ _cfPDFToPDF_PDFioPageHandle_add_subpage(_cfPDFToPDF_PDFioProcessor *handle, pdfioDictSetRect(pageDict, "TrimBox", trimBox); } - hashInsert(handle->xobjs, xoname,_cfPDFToPDFMakeXObject(pdf, sub->page)); + hashInsert(handle->xobjs, xoname,_cfPDFToPDFMakeXObject(handle->pdf, sub->page)); // Prepare transformation matrix _cfPDFToPDFMatrix mtx; _cfPDFToPDFMatrix_init(&mtx); @@ -636,8 +635,7 @@ _cfPDFToPDF_PDFioPageHandle_add_subpage(_cfPDFToPDF_PDFioProcessor *handle, // }}} void -_cfPDFToPDF_PDFioProcessor_mirror(_cfPDFToPDF_PDFioProcessor *handle, - pdfio_file_t *pdf) +_cfPDFToPDF_PDFioProcessor_mirror(_cfPDFToPDF_PDFioProcessor *handle) { _cfPDFToPDFPageRect orig = _cfPDFToPDF_PDFioProcessor_get_rect(handle); if(_cfPDFToPDF_PDFioProcessor_is_existing(handle)) @@ -648,9 +646,9 @@ _cfPDFToPDF_PDFioProcessor_mirror(_cfPDFToPDF_PDFioProcessor *handle, // Get the page to mirror (this would be replaced by actual pdfio page handling) pdfio_obj_t *subpage = _cfPDFToPDF_PDFioProcessor_get(handle);; - _cfPDFToPDF_PDFioProcessor_create_newMode(handle, pdf, orig.width, orig.height); + _cfPDFToPDF_PDFioProcessor_create_newMode(handle, handle->pdf, orig.width, orig.height); // Reinitialize the handle with new dimensions - hashInsert(handle->xobjs, xoname,_cfPDFToPDFMakeXObject(pdf, subpage)); + hashInsert(handle->xobjs, xoname,_cfPDFToPDFMakeXObject(handle->pdf, subpage)); char temp_content[1024]; snprintf(temp_content, sizeof(temp_content), "%s Do\n", xoname); @@ -684,35 +682,35 @@ _cfPDFToPDF_PDFioProcessor_add_label(_cfPDFToPDF_PDFioProcessor *handle, const char *label) { - _cfPDFToPDFPageRect rect_mod = ungetRect(rect); + _cfPDFToPDFPageRect rect_mod = ungetRect(*rect, handle, handle->rotation, handle->page); if (rect_mod.left > rect_mod.right || rect_mod.bottom > rect_mod.top) { fprintf(stderr, "Invalid rectangle dimensions!\n"); return; } - pdfio_dict_t *font_dict = pdfioDictCreate(pdf); + pdfio_dict_t *font_dict = pdfioDictCreate(handle->pdf); pdfioDictSetName(font_dict, "Type", "Font"); pdfioDictSetName(font_dict, "Subtype", "Type1"); pdfioDictSetName(font_dict, "Name", "pagelabel-font"); pdfioDictSetName(font_dict, "BaseFont", "Helvetica"); // Add font to the document as an indirect object - pdfio_obj_t *font_obj = pdfioFileCreateObj(pdf, font_dict); + pdfio_obj_t *font_obj = pdfioFileCreateObj(handle->pdf, font_dict); // Get the Resources dictionary from the page - pdfio_dict_t *resources = pdfioObjGetDict(page); + pdfio_dict_t *resources = pdfioObjGetDict(handle->page); if (resources == NULL) { - resources = pdfioDictCreate(pdf); - pdfioDictSetDict(resources, "Font", pdfioDictCreate(pdf)); + resources = pdfioDictCreate(handle->pdf); + pdfioDictSetDict(resources, "Font", pdfioDictCreate(handle->pdf)); } // Get or create the Font dictionary in Resources pdfio_dict_t *font_resources = pdfioDictGetDict(resources, "Font"); if (font_resources == NULL) { - font_resources = pdfioDictCreate(pdf); + font_resources = pdfioDictCreate(handle->pdf); pdfioDictSetDict(resources, "Font", font_resources); } @@ -772,18 +770,15 @@ _cfPDFToPDF_PDFioProcessor_add_label(_cfPDFToPDF_PDFioProcessor *handle, snprintf(post, sizeof(post), "%%pdftopdf Q\nQ\n%s", boxcmd); // Create the first stream (pre) - pdfio_stream_t *stream1 = pdfioPageOpenStream(page, PDFIO_FILTER_FLATE, true); + pdfio_stream_t *stream1 = pdfioPageOpenStream(handle->page, PDFIO_FILTER_FLATE, true); pdfioStreamPuts(stream1, pre); pdfioStreamClose(stream1); // Create the second stream (post + boxcmd) - pdfio_stream_t *stream2 = pdfioPageOpenStream(page, PDFIO_FILTER_FLATE, true); + pdfio_stream_t *stream2 = pdfioPageOpenStream(handle->page, PDFIO_FILTER_FLATE, true); pdfioStreamPuts(stream2, post); pdfioStreamClose(stream2); - // Add the streams as page contents - pdfioPageDictAddFont(page, "Contents", stream1); // before content - pdfioPageDictAddFont(page, "Contents", stream2); // after content } void From 075597b3c3c61d4ed78cb9658abab06496a41250 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Tue, 17 Sep 2024 16:46:12 +0530 Subject: [PATCH 43/64] C-pdftopdf-processor-private.h --- Makefile.am | 3 +- .../pdftopdf/C-pdftopdf-processor-private.h | 99 +-- cupsfilters/pdftopdf/C-pdftopdf-processor.c | 378 ++++++++- .../pdfio-pdftopdf-processor-private.h | 52 +- .../pdftopdf/pdfio-pdftopdf-processor.c | 717 ++++++++++-------- cupsfilters/pdftopdf/pdftopdf-processor.cxx | 2 +- cupsfilters/pdftopdf/processor.h | 246 ++++++ 7 files changed, 1085 insertions(+), 412 deletions(-) create mode 100644 cupsfilters/pdftopdf/processor.h diff --git a/Makefile.am b/Makefile.am index 3c2df209..e2d704c7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -181,9 +181,8 @@ libcupsfilters_la_SOURCES = \ cupsfilters/pclmtoraster.cxx \ cupsfilters/pdf.cxx \ cupsfilters/pdftopdf/C-pdftopdf-processor.c \ - cupsfilters/pdftopdf/C-pdftopdf-processor-private.h \ cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c \ - cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h \ + cupsfilters/pdftopdf/processor.h \ cupsfilters/pdftopdf/C-pptypes.c \ cupsfilters/pdftopdf/C-pptypes-private.h \ cupsfilters/pdftopdf/C-nup.c \ diff --git a/cupsfilters/pdftopdf/C-pdftopdf-processor-private.h b/cupsfilters/pdftopdf/C-pdftopdf-processor-private.h index b2813f03..c05e9ea9 100644 --- a/cupsfilters/pdftopdf/C-pdftopdf-processor-private.h +++ b/cupsfilters/pdftopdf/C-pdftopdf-processor-private.h @@ -5,6 +5,7 @@ #include "C-pptypes-private.h" #include "C-nup-private.h" #include "C-pdftopdf-private.h" +#include "pdfio-pdftopdf-processor-private.h" #include "C-intervalset-private.h" #include #include @@ -15,6 +16,29 @@ typedef enum { CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE } pdftopdf_booklet_mode_e; +typedef struct _cfPDFToPDF_PDFioProcessor{ + + // 1st mode: existing + pdfio_obj_t *page; // Equivalent to QPDFObjectHandle + int no; + + // 2nd mode: create new + HashTable *xobjs; // Pointer to a single HashTable + + char *content; // Equivalent to std::string content + + pdftopdf_rotation_e rotation; + + // Other members + pdfio_file_t *pdf; // Equivalent to std::unique_ptr + pdfio_obj_t **orig_pages; // Equivalent to std::vector + size_t orig_pages_size; // Current number of pages + size_t orig_pages_capacity; // Capacity for page array + + bool hasCM; + char *extraheader; +} _cfPDFToPDF_PDFioProcessor; + typedef struct { int job_id, num_copies; @@ -74,77 +98,10 @@ void _cfPDFToPDFProcessingParameters_dump(const _cfPDFToPDFProcessingParameters -typedef enum { - CF_PDFTOPDF_WILL_STAY_ALIVE, - CF_PDFTOPDF_MUST_DUPLICATE, - CF_PDFTOPDF_TAKE_OWNERSHIP -} pdftopdf_arg_ownership_e; - -/* -// Example function to initialize the struct (constructor equivalent) -typedef struct _cfPDFToPDFProcessor _cfPDFToPDFProcessor; - -struct _cfPDFToPDFProcessor { - void (*destroy)(_cfPDFToPDFProcessor *self); - - bool (*load_file)(_cfPDFToPDFProcessor *self, - FILE *f, pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e take, - int flatten_forms); - - bool (*load_filename)(_cfPDFToPDFProcessor *self, - const char *name, - pdftopdf_doc_t *doc, - int flatten_forms); - - bool (*check_print_permissions)(_cfPDFToPDFProcessor *self, - pdftopdf_doc_t *doc); - - void (*get_pages)(_cfPDFToPDFProcessor *self, - pdftopdf_doc_t *doc, - _cfPDFToPDFPageHandle **pages, - size_t *count); - - _cfPDFToPDFPageHandle *(*new_page)(_cfPDFToPDFProcessor *self, - float width, float height, - pdftopdf_doc_t *doc); - - void (*add_page)(_cfPDFToPDFProcessor *self, - _cfPDFToPDFPageHandle *page, - bool front); - - void (*multiply)(_cfPDFToPDFProcessor *self, - int copies, bool collate); - - void (*auto_rotate_all)(_cfPDFToPDFProcessor *self, - bool dst_lscape, - pdftopdf_rotation_e normal_landscape); - - void (*add_cm)(_cfPDFToPDFProcessor *self, - const char *defaulticc, - const char *outputicc); - - void (*set_comments)(_cfPDFToPDFProcessor *self, - const char **comments, - size_t count); - - void (*emit_file)(_cfPDFToPDFProcessor *self, - FILE *dst, pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e take); - - void (*emit_filename)(_cfPDFToPDFProcessor *self, - const char *name, - pdftopdf_doc_t *doc); - - bool (*has_acro_form)(_cfPDFToPDFProcessor *self); -}; - -// Example function to initialize the struct (constructor equivalent) -void _cfPDFToPDFProcessor_init(_cfPDFToPDFProcessor *self) { - // Initialize function pointers and any necessary data members -} -_cfPDFToPDFProcessor* _cfPDFToPDFFactory_processor(void); -*/ +int* _cfPDFToPDFBookletShuffle(int numPages, int signature, int* ret_size); +bool _cfProcessPDFToPDF(_cfPDFToPDF_PDFioProcessor *proc, + _cfPDFToPDFProcessingParameters *param, + pdftopdf_doc_t *doc); #endif // C_PDFTOPDF_PROCESSOR_PRIVATE_H diff --git a/cupsfilters/pdftopdf/C-pdftopdf-processor.c b/cupsfilters/pdftopdf/C-pdftopdf-processor.c index 9c7fcc4b..bcd19ac1 100644 --- a/cupsfilters/pdftopdf/C-pdftopdf-processor.c +++ b/cupsfilters/pdftopdf/C-pdftopdf-processor.c @@ -2,13 +2,13 @@ // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. -#include "C-pdftopdf-processor-private.h" -#include "pdfio-pdftopdf-processor-private.h" +#include "processor.h" #include #include "cupsfilters/debug-internal.h" #include #include #include +#define MAX(a, b) ((a) > (b) ? (a) : (b)) void _cfPDFToPDFProcessingParameters_init(_cfPDFToPDFProcessingParameters *processingParams) // {{{ @@ -191,10 +191,7 @@ _cfPDFToPDFProcessingParameters_dump(const _cfPDFToPDFProcessingParameters *proc BookletMode_dump(processingParams->booklet, doc); - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: booklet signature: %d", - processingParams->book_signature); - + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: booklet signature: %d", processingParams->book_signature); if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: auto_rotate: %s", (processingParams->auto_rotate) ? "true" : "false"); @@ -213,3 +210,372 @@ _cfPDFToPDFProcessingParameters_dump(const _cfPDFToPDFProcessingParameters *proc } // }}} +int* +_cfPDFToPDFBookletShuffle(int numPages, int signature, int* ret_size) +{ + if (signature < 0) { + signature = (numPages + 3) & ~0x3; // Round up to the nearest multiple of 4 + } + + int maxSize = numPages + signature - 1; + int* ret = (int*)malloc(maxSize * sizeof(int)); + if (ret == NULL) + { + *ret_size = 0; + return NULL; // Handle memory allocation failure + } + + int curpage = 0; + int index = 0; // Keeps track of the current index in the result array + while (curpage < numPages) + { + int firstpage = curpage; + int lastpage = curpage + signature - 1; + + while (firstpage < lastpage) + { + ret[index++] = lastpage--; + ret[index++] = firstpage++; + ret[index++] = firstpage++; + ret[index++] = lastpage--; + } + curpage += signature; + } + + *ret_size = index; // Set the size of the result + return ret; +} + +bool +_cfProcessPDFToPDF(pdfio_file_t *pdf, + _cfPDFToPDF_PDFioProcessor *proc, + _cfPDFToPDFProcessingParameters *param, + pdftopdf_doc_t *doc) +{ + if(!_cfPDFToPDF_PDFioProcessor_check_print_permissions(proc, doc)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Not allowed to print"); + return false; + } + + const bool dst_lscape = + (param->paper_is_landscape == + ((param->orientation == ROT_0) || (param->orientation == ROT_180))); + + if (param->paper_is_landscape) + { + int temp = param->nup.nupX; + param->nup.nupX = param->nup.nupY; + param->nup.nupY = temp; + } + + if (param->auto_rotate) + _cfPDFToPDF_PDFioProcessor_auto_rotate_all(proc, dst_lscape, param->normal_landscape); + + int *num_page; + _cfPDFToPDFPageHandle **pages = _cfPDFToPDF_PDFioProcessor_get_pages(proc, doc, num_page); + + _cfPDFToPDFPageHandle **input_page_range_list = malloc((*num_page) * sizeof(_cfPDFToPDFPageHandle*)); + int input_page_range_size = 0; + + for (int i = 1; i <= *num_page; i++) + { + if (_cfPDFToPDFProcessingParameters_with_page(param, i)) + { + input_page_range_list[input_page_range_size++] = pages[i - 1]; + input_page_range_size++; + } + } + + const int numOrigPages = input_page_range_size; + + int* shuffle = NULL; + int* shuffle_size; + + if (param->booklet != CF_PDFTOPDF_BOOKLET_OFF) + { + shuffle = _cfPDFToPDFBookletShuffle(numOrigPages, param->book_signature, shuffle_size); + if (param->booklet == CF_PDFTOPDF_BOOKLET_ON) + { + _cfPDFToPDFNupParameters_preset(2, ¶m->nup); + } + } + else + { + int* shuffle = malloc(numOrigPages * sizeof(numOrigPages)); + for (int i = 0; i < numOrigPages; i++) + { + shuffle[i] = i; + } + } + + const int numPages = MAX(*shuffle_size, input_page_range_size); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: \"print-scaling\" IPP attribute: %s", + (param->autoprint ? "auto" : + (param->autofit ? "auto-fit" : + (param->fitplot ? "fit" : + (param->fillprint ? "fill" : + (param->cropfit ? "none" : + "Not defined, should never happen")))))); + + if (param->autoprint || param->autofit) + { + bool margin_defined = true; + bool document_large = false; + int pw = param->page.right - param->page.left; + int ph = param->page.top - param->page.bottom; + + if ((param->page.width == pw) && (param->page.height == ph)) + margin_defined = false; + + for (int i = 0; i < input_page_range_size; i ++) + { + _cfPDFToPDFPageRect r = _cfPDFToPDFPageHandle_get_rect(input_page_range_list[i]); + int w = r.width * 100 / 102; // 2% of tolerance + int h = r.height * 100 / 102; + if ((w > param->page.width || h > param->page.height) && + (h > param->page.width || w > param->page.height)) + { + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Page %d too large for output page size, scaling pages to fit.", + i + 1); + document_large = true; + } + } + + if (param->fidelity && doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: \"ipp-attribute-fidelity\" IPP attribute is set, scaling pages to fit."); + + if (param->autoprint) + { + if (param->fidelity || document_large) + { + if (margin_defined) + param->fitplot = true; + else + param->fillprint = true; + } + else + param->cropfit = true; + } + + else + { + if (param->fidelity || document_large) + param->fitplot = true; + else + param->cropfit = true; + } + } + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Print scaling mode: %s", + (param->fitplot ? + "Scale to fit printable area" : + (param->fillprint ? + "Scale to fill page and crop" : + (param->cropfit ? + "Do not scale, center, crop if needed" : + "Not defined, should never happen")))); + + if (param->cropfit) + { + param->page.left = 0; + param->page.bottom = 0; + param->page.right = param->page.width; + param->page.top = param->page.height; + } + + if (param->pagesize_requested && (param->fillprint || param->cropfit)) + { + for (int i = 0; i < input_page_range_size; i ++) + { + _cfPDFToPDFPageHandle *page = input_page_range_list[i]; + pdftopdf_rotation_e orientation; + if(_cfPDFToPDFPageHandle_is_landscape(page, param->orientation)) + orientation = param->normal_landscape; + else + orientation = ROT_0; + _cfPDFToPDFPageHandle_crop(page, + ¶m->page, + orientation, + param->orientation, + param->xpos, + param->ypos, + !param->cropfit, + !param->auto_rotate, + doc); + } + if (param->fillprint) + param->fitplot = true; + } + + _cfPDFToPDFPageHandle *curpage; + int outputpage = 0; + int outputno = 0; + + if ((param->nup.nupX == 1) && (param->nup.nupY == 1) && !param->fitplot) + { + param->nup.width = param->page.width; + param->nup.height = param->page.height; + } + else + { + param->nup.width = param->page.right - param->page.left; + param->nup.height = param->page.top - param->page.bottom; + } + + if ((param->orientation == ROT_90) || (param->orientation == ROT_270)) + { + int temp = param->nup.nupX; + param->nup.nupX = param->nup.nupY; + param->nup.nupY = temp; + + param->nup.landscape = !param->nup.landscape; + param->orientation = param->orientation - param->normal_landscape; + } + + double xpos = 0, ypos = 0; + if(param->nup.landscape) + { + param->orientation = param->orientation + param->normal_landscape; + if (param->nup.nupX != 1 || param->nup.nupY != 1 || param->fitplot) + { + xpos = param->page.height - param->page.top; + ypos = param->page.left; + } + int temp = param->page.width; + param->page.width = param->page.height; + param->page.height = temp; + + temp = param->nup.width; + param->nup.width = param->nup.height; + param->nup.height = temp; + } + else + { + if (param->nup.nupX != 1 || param->nup.nupY != 1 || param->fitplot) + { + xpos = param->page.left; + ypos = param->page.bottom; + } + } + + _cfPDFToPDFNupState *nupState; + _cfPDFToPDFNupState_init(nupState, ¶m->nup); + + _cfPDFToPDFNupPageEdit pgedit; + + for (int iA = 0; iA < numPages; iA++) + { + _cfPDFToPDFPageHandle *page; + if(shuffle[iA] >= numOrigPages) + page = _cfPDFToPDF_PDFioProcessor_new_page(proc, param->page.width, param->page.height, doc); + else + page = input_page_range_list[shuffle[iA]]; + + _cfPDFToPDFPageRect rect = _cfPDFToPDFPageHandle_get_rect(page); + + if (!param->pagesize_requested) + { + param->page.width = param->page.right = rect.width; + param->page.height = param->page.top = rect.height; + } + + bool newPage = _cfPDFToPDFNupState_next_page(nupState, rect.width, rect.height, &pgedit); + if (newPage) + { + if((curpage) && (_cfPDFToPDFProcessingParameters_with_page(param, outputpage))) + { + _cfPDFToPDFPageHandle_rotate(curpage, param->orientation); + if (param->mirror) + _cfPDFToPDFPageHandle_mirror(curpage, pdf); + +// _cfPDFToPDF_PDFioProcessor_add_page(proc, curpage, param->reverse); + // Log page in /var/log/cups/page_log + + outputno ++; + if (param->page_logging == 1) + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_CONTROL, + "PAGE: %d %d", outputno, + param->copies_to_be_logged); + } + curpage = _cfPDFToPDF_PDFioProcessor_new_page(proc, param->page.width, param->page.height, doc); + outputpage++; + } + + if (shuffle[iA] >= numOrigPages) + continue; + + if (param->border != NONE) + _cfPDFToPDFPageHandle_add_border_rect(page, pdf, rect, param->border, 1.0 / pgedit.scale); + + if (param->page_label[0] != '\0') + { + _cfPDFToPDFPageHandle_add_label(page, pdf, ¶m->page, param->page_label); + } + + if(param->cropfit) + { + if ((param->nup.nupX == 1) && (param->nup.nupY == 1)) + { + double xpos2, ypos2; + _cfPDFToPDFPageRect get_rect_height = _cfPDFToPDFPageHandle_get_rect(page); + _cfPDFToPDFPageRect get_rect_width = _cfPDFToPDFPageHandle_get_rect(page); + + if ((param->page.height - param->page.width) * + (get_rect_height.height - get_rect_width.width) < 0) + { + xpos2 = (param->page.width - (get_rect_height.height)) / 2; + ypos2 = (param->page.height - (get_rect_width.width)) / 2; + _cfPDFToPDFPageHandle_add_subpage(curpage, page, pdf, ypos2 + xpos, xpos2 + ypos, 1, NULL); + } + else + { + xpos2 = (param->page.width - get_rect_width.width) / 2; + ypos2 = (param->page.height - get_rect_height.height) /2; + _cfPDFToPDFPageHandle_add_subpage(curpage, page, pdf, xpos2 + xpos, ypos2 + ypos, 1, NULL); + } + } + else + { + _cfPDFToPDFPageHandle_add_subpage(curpage, page, pdf, pgedit.xpos + xpos, pgedit.ypos + ypos, pgedit.scale, NULL); + } + } + else + _cfPDFToPDFPageHandle_add_subpage(curpage, page, pdf, pgedit.xpos + xpos, pgedit.ypos + ypos, pgedit.scale, NULL); + +#ifdef DEBUG + _cfPDFToPDFPageHandle *dbg = (_cfPDFToPDFPageHandle *)curpage; + if (dbg && dbg->debug) + { + _cfPDFToPDFPageHandle_debug(dbg, sub, xpos,ypos); + } +#endif + } + + if((curpage) && (_cfPDFToPDFProcessingParameters_with_page(param, outputpage))) + { + _cfPDFToPDFPageHandle_rotate(curpage, param->orientation); + if(param->mirror) + _cfPDFToPDFPageHandle_mirror(curpage, pdf); + + // need to output empty page to not confuse duplex + _cfPDFToPDF_PDFioProcessor_add_page(proc, _cfPDFToPDF_PDFioProcessor_new_page(proc, + param->page.width, param->page.height, doc), param->reverse); + + // Log page in /var/log/cups/page_log + if(param->page_logging == 1) + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_CONTROL, + "PAGE: %d %d", outputno + 1, + param->copies_to_be_logged); + } + + _cfPDFToPDF_PDFioProcessor_multiply(proc, param->num_copies, param->collate); + return true; +} diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h index 805c6019..ad99b327 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h @@ -3,7 +3,8 @@ // information. // -#include "C-pdftopdf-processor-private.h" +#include "processor.h" +#include "C-pptypes-private.h" #include "pdfio.h" #define HASH_TABLE_SIZE 2048 @@ -24,6 +25,7 @@ pdfio_obj_t *hashGet(HashTable *table, const char *key); int hashGet_filled_count(HashTable *table); void hashFree_hash_table(HashTable *table); +/* typedef struct _cfPDFToPDF_PDFioProcessor{ // 1st mode: existing @@ -46,8 +48,9 @@ typedef struct _cfPDFToPDF_PDFioProcessor{ bool hasCM; char *extraheader; } _cfPDFToPDF_PDFioProcessor; - +*/ //_cfPDFToPDFQPDFPageHandle functions + void _cfPDFToPDF_PDFioProcessor_existingMode(_cfPDFToPDF_PDFioProcessor *handle, pdfio_file_t *pdf, pdfio_obj_t *page, @@ -103,3 +106,48 @@ void _cfPDFToPDF_PDFioProcessor_add_label(_cfPDFToPDF_PDFioProcessor *handle, const char *label); +//_cfPDFToPDFQPDFProcessor functions +void _cfPDFToPDF_PDFioProcessor_close_file(_cfPDFToPDF_PDFioProcessor *processor); + +bool _cfPDFToPDF_PDFioProcessor_load_file(_cfPDFToPDF_PDFioProcessor *processor, + FILE *f, pdftopdf_doc_t *doc, + pdftopdf_arg_ownership_e take, int flatten_forms); + +bool _cfPDFToPDF_PDFioProcessor_load_filename(_cfPDFToPDF_PDFioProcessor *processor, + const char *name, + pdftopdf_doc_t *doc, + int flatten_forms); + +void _cfPDFToPDF_PDFioProcessor_start(_cfPDFToPDF_PDFioProcessor *processor, int flatten_forms); + +bool _cfPDFToPDF_PDFioProcessor_check_print_permissions(_cfPDFToPDF_PDFioProcessor *processor, + pdftopdf_doc_t *doc); + +_cfPDFToPDF_PDFioProcessor** _cfPDFToPDF_PDFioProcessor_get_pages(_cfPDFToPDF_PDFioProcessor *handle, + pdftopdf_doc_t *doc, + int *out_len); + +_cfPDFToPDF_PDFioProcessor* _cfPDFToPDF_PDFioProcessor_new_page(_cfPDFToPDF_PDFioProcessor *handle, + float width, float height, + pdftopdf_doc_t *doc); + +void _cfPDFToPDF_PDFioProcessor_multiply(_cfPDFToPDF_PDFioProcessor *handle, + int copies, bool collate); + +void _cfPDFToPDF_PDFioProcessor_auto_rotate_all(_cfPDFToPDF_PDFioProcessor *handle, + bool dst_lscape, + pdftopdf_rotation_e normal_landscape); + +void _cfPDFToPDF_PDFioProcessor_add_cm(_cfPDFToPDF_PDFioProcessor *handle, + const char *defaulticc, const char *outputicc); + +void _cfPDFToPDF_PDFioProcessor_set_comments(_cfPDFToPDF_PDFioProcessor *handle, + char **comments, int num_comments); +void _cfPDFToPDF_PDFioProcessor_emit_file(_cfPDFToPDF_PDFioProcessor *handle, + FILE *f, pdftopdf_doc_t *doc, + pdftopdf_arg_ownership_e take); + +void _cfPDFToPDF_PDFioProcessor_emit_filename(_cfPDFToPDF_PDFioProcessor *handle, + const char *name, pdftopdf_doc_t *doc); + +bool _cfPDFToPDF_PDFioProcessor_has_acro_form(_cfPDFToPDF_PDFioProcessor *handle); diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c index 1f25dc0e..0a2e48ee 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c @@ -8,11 +8,12 @@ #include #include #include "pdfio.h" // Replace with appropriate PDFIO headers -#include "C-pdftopdf-processor-private.h" +#include "pdfio-content.h" #include "pdfio-pdftopdf-private.h" #include "pdfio-tools-private.h" #include "pdfio-xobject-private.h" -#include "pdfio-pdftopdf-processor-private.h" +#include "processor.h" +#include "pdfio-cm-private.h" #define DEBUG_assert(x) do { if (!(x)) abort(); } while (0) @@ -149,10 +150,9 @@ append_debug_box(char *content, // }}} void -_cfPDFToPDF_PDFioProcessor_existingMode(_cfPDFToPDF_PDFioProcessor *handle, - pdfio_file_t *pdf, - pdfio_obj_t *page, - int orig_no) // {{{ +_cfPDFToPDFPageHandle_existingMode(_cfPDFToPDFPageHandle *handle, + pdfio_obj_t *page, + int orig_no) // {{{ { handle->page = page; @@ -166,21 +166,13 @@ _cfPDFToPDF_PDFioProcessor_existingMode(_cfPDFToPDF_PDFioProcessor *handle, handle->xobjs = NULL; handle->xobjs->count = 0; handle->content = NULL; - - handle->pdf = pdf; - handle->orig_pages = NULL; - handle->orig_pages_size = 0; - handle->orig_pages_capacity = 0; - - handle->hasCM = false; - handle->extraheader = NULL; } // }}} void -_cfPDFToPDF_PDFioProcessor_create_newMode(_cfPDFToPDF_PDFioProcessor *handle, - pdfio_file_t *pdf, - float width, float height) // {{{ +_cfPDFToPDFPageHandle_create_newMode(_cfPDFToPDFPageHandle *handle, + pdfio_file_t *pdf, + float width, float height) // {{{ { handle->no = 0; handle->rotation = ROT_0; @@ -209,37 +201,19 @@ _cfPDFToPDF_PDFioProcessor_create_newMode(_cfPDFToPDF_PDFioProcessor *handle, handle->xobjs = NULL; handle->xobjs->count = 0; - - handle->pdf = pdf; - handle->orig_pages = NULL; - handle->orig_pages_size = 0; - handle->orig_pages_capacity = 0; - - handle->hasCM = false; - handle->extraheader = NULL; } // }}} void -_cfPDFToPDF_PDFioProcessor_destroy(_cfPDFToPDF_PDFioProcessor *handle) // {{{ +_cfPDFToPDFPageHandle_destroy(_cfPDFToPDFPageHandle *handle) // {{{ { - if (handle->pdf != NULL) - pdfioFileClose(handle->pdf); - - free(handle->xobjs); - free(handle->content); - free(handle->orig_pages); - - // Free the extraheader if it was allocated - if (handle->extraheader != NULL) - free(handle->extraheader); - + free(handle->content); } // }}} bool -_cfPDFToPDF_PDFioProcessor_is_existing(struct _cfPDFToPDF_PDFioProcessor *handle) // {{{ +_cfPDFToPDFPageHandle_is_existing(_cfPDFToPDFPageHandle *handle) // {{{ { return (handle->content == NULL || strlen((const char *)handle->content) == 0); @@ -248,7 +222,7 @@ _cfPDFToPDF_PDFioProcessor_is_existing(struct _cfPDFToPDF_PDFioProcessor *handle _cfPDFToPDFPageRect -_cfPDFToPDF_PDFioProcessor_get_rect(const _cfPDFToPDF_PDFioProcessor *handle) // {{{ +_cfPDFToPDFPageHandle_get_rect(const _cfPDFToPDFPageHandle *handle) // {{{ { pdfio_rect_t trimBox = _cfPDFToPDFGetTrimBox(handle->page); _cfPDFToPDFPageRect ret = _cfPDFToPDFGetBoxAsRect(&trimBox); @@ -261,57 +235,45 @@ _cfPDFToPDF_PDFioProcessor_get_rect(const _cfPDFToPDF_PDFioProcessor *handle) // // }}} pdfio_obj_t* -_cfPDFToPDF_PDFioProcessor_get(struct _cfPDFToPDF_PDFioProcessor *handle) // {{{ +_cfPDFToPDFPageHandle_get(_cfPDFToPDFPageHandle *handle) // {{{ { pdfio_dict_t *resources, *contents_dict; pdfio_stream_t *contents_stream; - pdfio_obj_t *ret = handle->page; - if (!_cfPDFToPDF_PDFioProcessor_is_existing(handle)) + if (!_cfPDFToPDFPageHandle_is_existing(handle)) { - // Step 1: Replace /XObject in /Resources resources = pdfioDictGetDict(pdfioObjGetDict(ret), "/Resources"); if (resources) { char name_buffer[handle->xobjs->count]; int xobj_index = 0; - // Loop through all the buckets for (int i = 0; i < HASH_TABLE_SIZE && xobj_index < handle->xobjs->count; i++) { KeyValuePair *current = handle->xobjs->buckets[i]; - // Traverse the linked list in case of collisions while (current != NULL) { - // Build the XObject name (e.g., "/X1", "/X2", ...) snprintf(name_buffer, sizeof(name_buffer), "/X%d", xobj_index + 1); - - // Add the object to the PDF resources dictionary - pdfioDictSetObj(resources, name_buffer, current->value); // current->value is pdfio_obj_t * - - // Move to the next element in the linked list (in case of collisions) + pdfioDictSetObj(resources, name_buffer, current->value); current = current->next; - - // Increment the XObject index xobj_index++; } } - - pdfioDictSetDict(resources, "/XObject", resources); // Set new dictionary for XObject + pdfioDictSetDict(resources, "/XObject", resources); } - contents_stream = pdfioPageOpenStream(ret, 0, true); // Open the content stream of the page + contents_stream = pdfioPageOpenStream(ret, 0, true); if (contents_stream) { - pdfioStreamPuts(contents_stream, "Q\n"); // Append "Q\n" to content - pdfioStreamClose(contents_stream); // Close the stream + pdfioStreamPuts(contents_stream, "Q\n"); + pdfioStreamClose(contents_stream); } contents_dict = pdfioDictGetDict(pdfioObjGetDict(ret), "/Contents"); if (contents_dict) { - pdfioDictSetNull(contents_dict, "/Filter"); // Remove filter keys + pdfioDictSetNull(contents_dict, "/Filter"); pdfioDictSetNull(contents_dict, "/DecodeParms"); } @@ -331,12 +293,12 @@ _cfPDFToPDF_PDFioProcessor_get(struct _cfPDFToPDF_PDFioProcessor *handle) // {{{ static _cfPDFToPDFPageRect ungetRect(_cfPDFToPDFPageRect rect, - const _cfPDFToPDF_PDFioProcessor *ph, + const _cfPDFToPDFPageHandle *ph, pdftopdf_rotation_e rotation, pdfio_obj_t *page) // {{{ { - _cfPDFToPDFPageRect pg1 = _cfPDFToPDF_PDFioProcessor_get_rect(ph); + _cfPDFToPDFPageRect pg1 = _cfPDFToPDFPageHandle_get_rect(ph); pdfio_rect_t TrimBox = _cfPDFToPDFGetTrimBox(page); _cfPDFToPDFPageRect pg2 = _cfPDFToPDFGetBoxAsRect(&TrimBox); rect.width = pg1.width; @@ -351,10 +313,11 @@ ungetRect(_cfPDFToPDFPageRect rect, // }}} void -_cfPDFToPDF_PDFioProcessor_add_border_rect(_cfPDFToPDF_PDFioProcessor *handle, - const _cfPDFToPDFPageRect givenRect, - pdftopdf_border_type_e border, - float fscale) // {{{ +_cfPDFToPDFPageHandle_add_border_rect(_cfPDFToPDFPageHandle *handle, + pdfio_file_t *pdf, + const _cfPDFToPDFPageRect givenRect, + pdftopdf_border_type_e border, + float fscale) // {{{ { double lw = (border & THICK) ? 0.5 : 0.24; double line_width = lw * fscale; @@ -412,40 +375,40 @@ _cfPDFToPDF_PDFioProcessor_add_border_rect(_cfPDFToPDF_PDFioProcessor *handle, const char *pre = "%pdftopdf q\nq\n"; const char *post = "%pdftopdf Q\nQ\n"; - pdfio_dict_t *stm1_dict = pdfioDictCreate(handle->pdf); - pdfio_obj_t *stm1_obj = pdfioFileCreateObj(handle->pdf, stm1_dict); + pdfio_dict_t *stm1_dict = pdfioDictCreate(pdf); + pdfio_obj_t *stm1_obj = pdfioFileCreateObj(pdf, stm1_dict); pdfio_stream_t *stm1 = pdfioObjCreateStream(stm1_obj, PDFIO_FILTER_NONE); if (stm1) { pdfioStreamWrite(stm1, pre, strlen(pre)); - pdfioStreamClose(stm1); // Finalize the stream + pdfioStreamClose(stm1); } else fprintf(stderr, "Failed to create PDF stream for pre content\n"); - char combined[2048]; // Ensure this is large enough + char combined[2048]; snprintf(combined, sizeof(combined), "%s%s", post, boxcmd); - pdfio_dict_t *stm2_dict = pdfioDictCreate(handle->pdf); - pdfio_obj_t *stm2_obj = pdfioFileCreateObj(handle->pdf, stm2_dict); + pdfio_dict_t *stm2_dict = pdfioDictCreate(pdf); + pdfio_obj_t *stm2_obj = pdfioFileCreateObj(pdf, stm2_dict); pdfio_stream_t *stm2 = pdfioObjCreateStream(stm2_obj, PDFIO_FILTER_NONE); if (stm2) { pdfioStreamWrite(stm2, combined, strlen(combined)); - pdfioStreamClose(stm2); // Finalize the stream + pdfioStreamClose(stm2); } else fprintf(stderr, "Failed to create PDF stream for post content\n"); #else - pdfio_dict_t *stm_dict = pdfioDictCreate(handle->pdf); - pdfio_obj_t *stm_obj = pdfioFileCreateObj(handle->pdf, stm_dict); + pdfio_dict_t *stm_dict = pdfioDictCreate(pdf); + pdfio_obj_t *stm_obj = pdfioFileCreateObj(pdf, stm_dict); pdfio_stream_t *stm = pdfioObjCreateStream(stm_obj, PDFIO_FILTER_NONE); if (stm) { pdfioStreamWrite(stm, boxcmd, strlen(boxcmd)); - pdfioStreamClose(stm); // Finalize the stream + pdfioStreamClose(stm); } else fprintf(stderr, "Failed to create PDF stream for boxcmd content\n"); @@ -454,13 +417,13 @@ _cfPDFToPDF_PDFioProcessor_add_border_rect(_cfPDFToPDF_PDFioProcessor *handle, // }}} pdftopdf_rotation_e -_cfPDFToPDF_PDFioProcessor_crop(_cfPDFToPDF_PDFioProcessor *handle, - const _cfPDFToPDFPageRect *cropRect, - pdftopdf_rotation_e orientation, - pdftopdf_rotation_e param_orientation, - pdftopdf_position_e xpos, pdftopdf_position_e ypos, - bool scale, bool autorotate, - pdftopdf_doc_t *doc) // {{{ +_cfPDFToPDFPageHandle_crop(_cfPDFToPDFPageHandle *handle, + const _cfPDFToPDFPageRect *cropRect, + pdftopdf_rotation_e orientation, + pdftopdf_rotation_e param_orientation, + pdftopdf_position_e xpos, pdftopdf_position_e ypos, + bool scale, bool autorotate, + pdftopdf_doc_t *doc) // {{{ { pdftopdf_rotation_e save_rotate = _cfPDFToPDFGetRotate(handle->page); @@ -542,13 +505,10 @@ _cfPDFToPDF_PDFioProcessor_crop(_cfPDFToPDF_PDFioProcessor *handle, // }}} bool -_cfPDFToPDF_PDFioProcessor_is_landscape(const _cfPDFToPDF_PDFioProcessor *handle, - pdftopdf_rotation_e orientation) // {{{ +_cfPDFToPDFPageHandle_is_landscape(const _cfPDFToPDFPageHandle *handle, + pdftopdf_rotation_e orientation) // {{{ { pdftopdf_rotation_e save_rotate = _cfPDFToPDFGetRotate(handle->page); - - // Temporarily set the page rotation based on the orientation - pdfio_dict_t *pageDict = pdfioObjGetDict(handle->page); if (orientation == ROT_0 || orientation == ROT_180) @@ -562,10 +522,8 @@ _cfPDFToPDF_PDFioProcessor_is_landscape(const _cfPDFToPDF_PDFioProcessor *handle double width = currpage.right - currpage.left; double height = currpage.top - currpage.bottom; - // Restore the original page rotation pdfioDictSetNumber(pageDict, "Rotate", save_rotate); - // Determine if the page is in landscape orientation if (width > height) return true; return false; @@ -573,17 +531,18 @@ _cfPDFToPDF_PDFioProcessor_is_landscape(const _cfPDFToPDF_PDFioProcessor *handle // }}} void -_cfPDFToPDF_PDFioPageHandle_add_subpage(_cfPDFToPDF_PDFioProcessor *handle, - _cfPDFToPDF_PDFioProcessor *sub, - float xpos, float ypos, float scale, - const _cfPDFToPDFPageRect *crop) // {{{ +_cfPDFToPDFPageHandle_add_subpage(_cfPDFToPDFPageHandle *handle, + _cfPDFToPDFPageHandle *sub, + pdfio_file_t *pdf, + float xpos, float ypos, float scale, + const _cfPDFToPDFPageRect *crop) // {{{ { char xoname[64]; snprintf(xoname, sizeof(xoname), "/X%d", (sub->no != -1) ? sub->no : ++handle->no); if (crop) { - _cfPDFToPDFPageRect pg = _cfPDFToPDF_PDFioProcessor_get_rect(sub); + _cfPDFToPDFPageRect pg = _cfPDFToPDFPageHandle_get_rect(sub); _cfPDFToPDFPageRect tmp = *crop; tmp.width = tmp.right - tmp.left; tmp.height = tmp.top - tmp.bottom; @@ -599,13 +558,12 @@ _cfPDFToPDF_PDFioPageHandle_add_subpage(_cfPDFToPDF_PDFioProcessor *handle, pdfio_rect_t *trimBox = _cfPDFToPDFMakeBox(rect.left, rect.bottom, rect.right, rect.top); - // Set TrimBox in pdfio (adjust if pdfio allows this kind of modification) pdfio_dict_t *pageDict = pdfioObjGetDict(sub->page); pdfioDictSetRect(pageDict, "TrimBox", trimBox); } - hashInsert(handle->xobjs, xoname,_cfPDFToPDFMakeXObject(handle->pdf, sub->page)); - // Prepare transformation matrix + hashInsert(handle->xobjs, xoname,_cfPDFToPDFMakeXObject(pdf, sub->page)); + _cfPDFToPDFMatrix mtx; _cfPDFToPDFMatrix_init(&mtx); _cfPDFToPDFMatrix_translate(&mtx, xpos, ypos); @@ -635,20 +593,19 @@ _cfPDFToPDF_PDFioPageHandle_add_subpage(_cfPDFToPDF_PDFioProcessor *handle, // }}} void -_cfPDFToPDF_PDFioProcessor_mirror(_cfPDFToPDF_PDFioProcessor *handle) +_cfPDFToPDFPageHandle_mirror(_cfPDFToPDFPageHandle *handle, + pdfio_file_t *pdf) { - _cfPDFToPDFPageRect orig = _cfPDFToPDF_PDFioProcessor_get_rect(handle); - if(_cfPDFToPDF_PDFioProcessor_is_existing(handle)) + _cfPDFToPDFPageRect orig = _cfPDFToPDFPageHandle_get_rect(handle); + if(_cfPDFToPDFPageHandle_is_existing(handle)) { char xoname[10]; - snprintf(xoname, sizeof(xoname), "/X%d", 1); // Assuming 'no' is 1 for example + snprintf(xoname, sizeof(xoname), "/X%d", handle->no); - // Get the page to mirror (this would be replaced by actual pdfio page handling) - pdfio_obj_t *subpage = _cfPDFToPDF_PDFioProcessor_get(handle);; + pdfio_obj_t *subpage = _cfPDFToPDFPageHandle_get(handle);; - _cfPDFToPDF_PDFioProcessor_create_newMode(handle, handle->pdf, orig.width, orig.height); - // Reinitialize the handle with new dimensions - hashInsert(handle->xobjs, xoname,_cfPDFToPDFMakeXObject(handle->pdf, subpage)); + _cfPDFToPDFPageHandle_create_newMode(handle, pdf, orig.width, orig.height); + hashInsert(handle->xobjs, xoname,_cfPDFToPDFMakeXObject(pdf, subpage)); char temp_content[1024]; snprintf(temp_content, sizeof(temp_content), "%s Do\n", xoname); @@ -659,7 +616,6 @@ _cfPDFToPDF_PDFioProcessor_mirror(_cfPDFToPDF_PDFioProcessor *handle) char mrcmd[100]; snprintf(mrcmd, sizeof(mrcmd), "-1 0 0 1 %.2f 0 cm\n", orig.right); - // Insert the mirroring matrix at the beginning of the content size_t new_len = strlen(pre) + strlen(mrcmd) + strlen(handle->content) + 1; char *new_content = (char *)malloc(new_len); snprintf(new_content, new_len, "%s%s%s", pre, mrcmd, handle->content); @@ -670,16 +626,17 @@ _cfPDFToPDF_PDFioProcessor_mirror(_cfPDFToPDF_PDFioProcessor *handle) void -_cfPDFToPDF_PDFioProcessor_rotate(_cfPDFToPDF_PDFioProcessor *handle, - pdftopdf_rotation_e rot) +_cfPDFToPDFPageHandle_rotate(_cfPDFToPDFPageHandle *handle, + pdftopdf_rotation_e rot) { handle->rotation = rot; } void -_cfPDFToPDF_PDFioProcessor_add_label(_cfPDFToPDF_PDFioProcessor *handle, - const _cfPDFToPDFPageRect *rect, - const char *label) +_cfPDFToPDFPageHandle_add_label(_cfPDFToPDFPageHandle *handle, + pdfio_file_t *pdf, + const _cfPDFToPDFPageRect *rect, + const char *label) { _cfPDFToPDFPageRect rect_mod = ungetRect(*rect, handle, handle->rotation, handle->page); @@ -689,92 +646,74 @@ _cfPDFToPDF_PDFioProcessor_add_label(_cfPDFToPDF_PDFioProcessor *handle, fprintf(stderr, "Invalid rectangle dimensions!\n"); return; } - pdfio_dict_t *font_dict = pdfioDictCreate(handle->pdf); + pdfio_dict_t *font_dict = pdfioDictCreate(pdf); pdfioDictSetName(font_dict, "Type", "Font"); pdfioDictSetName(font_dict, "Subtype", "Type1"); pdfioDictSetName(font_dict, "Name", "pagelabel-font"); pdfioDictSetName(font_dict, "BaseFont", "Helvetica"); - // Add font to the document as an indirect object - pdfio_obj_t *font_obj = pdfioFileCreateObj(handle->pdf, font_dict); + pdfio_obj_t *font_obj = pdfioFileCreateObj(pdf, font_dict); - // Get the Resources dictionary from the page pdfio_dict_t *resources = pdfioObjGetDict(handle->page); if (resources == NULL) { - resources = pdfioDictCreate(handle->pdf); - pdfioDictSetDict(resources, "Font", pdfioDictCreate(handle->pdf)); + resources = pdfioDictCreate(pdf); + pdfioDictSetDict(resources, "Font", pdfioDictCreate(pdf)); } - // Get or create the Font dictionary in Resources pdfio_dict_t *font_resources = pdfioDictGetDict(resources, "Font"); if (font_resources == NULL) { - font_resources = pdfioDictCreate(handle->pdf); + font_resources = pdfioDictCreate(pdf); pdfioDictSetDict(resources, "Font", font_resources); } - - // Add the pagelabel-font to the Font dictionary in Resources pdfioDictSetObj(font_resources, "pagelabel-font", font_obj); - - // Finally, replace the Resources key in the page with updated Resources pdfioDictSetDict(resources, "Resources", resources); double margin = 2.25; double height = 12; - - // Start creating the PDF content commands (simplified for pdfio) + char boxcmd[1024] = "q\n"; - // White filled rectangle (top) snprintf(boxcmd + strlen(boxcmd), sizeof(boxcmd) - strlen(boxcmd), "1 1 1 rg\n%f %f %f %f re f\n", rect_mod.left + margin, rect_mod.top - height - 2 * margin, rect_mod.right - rect_mod.left - 2 * margin, height + 2 * margin); - // White filled rectangle (bottom) snprintf(boxcmd + strlen(boxcmd), sizeof(boxcmd) - strlen(boxcmd), "%f %f %f %f re f\n", rect_mod.left + margin, rect_mod.bottom + height + margin, rect_mod.right - rect_mod.left - 2 * margin, height + 2 * margin); - // Black outline (top) snprintf(boxcmd + strlen(boxcmd), sizeof(boxcmd) - strlen(boxcmd), "0 0 0 RG\n%f %f %f %f re S\n", rect_mod.left + margin, rect_mod.top - height - 2 * margin, rect_mod.right - rect_mod.left - 2 * margin, height + 2 * margin); - // Black outline (bottom) snprintf(boxcmd + strlen(boxcmd), sizeof(boxcmd) - strlen(boxcmd), "%f %f %f %f re S\n", rect_mod.left + margin, rect_mod.bottom + height + margin, rect_mod.right - rect_mod.left - 2 * margin, height + 2 * margin); - // Black text (top) snprintf(boxcmd + strlen(boxcmd), sizeof(boxcmd) - strlen(boxcmd), "0 0 0 rg\nBT\n/f1 12 Tf\n%f %f Td\n(%s) Tj\nET\n", rect_mod.left + 2 * margin, rect_mod.top - height - margin, label); - // Black text (bottom) snprintf(boxcmd + strlen(boxcmd), sizeof(boxcmd) - strlen(boxcmd), "BT\n/f1 12 Tf\n%f %f Td\n(%s) Tj\nET\n", rect_mod.left + 2 * margin, rect_mod.bottom + height + 2 * margin, label); - // End the graphic context strcat(boxcmd, "Q\n"); const char *pre = "%pdftopdf q\nq\n"; - char post[256]; // Make sure the buffer is large enough for the combined string + char post[256]; - // Combine the "post" string with the passed boxcmd snprintf(post, sizeof(post), "%%pdftopdf Q\nQ\n%s", boxcmd); - // Create the first stream (pre) pdfio_stream_t *stream1 = pdfioPageOpenStream(handle->page, PDFIO_FILTER_FLATE, true); pdfioStreamPuts(stream1, pre); pdfioStreamClose(stream1); - // Create the second stream (post + boxcmd) pdfio_stream_t *stream2 = pdfioPageOpenStream(handle->page, PDFIO_FILTER_FLATE, true); pdfioStreamPuts(stream2, post); pdfioStreamClose(stream2); @@ -782,276 +721,394 @@ _cfPDFToPDF_PDFioProcessor_add_label(_cfPDFToPDF_PDFioProcessor *handle, } void -debug(_cfPDFToPDF_PDFioProcessor *handle, - const _cfPDFToPDFPageRect *rect, - float xpos, float ypos) +_cfPDFToPDFPageHandle_debug(_cfPDFToPDFPageHandle *handle, + const _cfPDFToPDFPageRect *rect, + float xpos, float ypos) { - if (!_cfPDFToPDF_PDFioProcessor_is_existing(handle)) + if (!_cfPDFToPDFPageHandle_is_existing(handle)) return; - // append to the content stream append_debug_box(handle->content, rect, xpos, ypos); } -/* -// Define the _cfPDFToPDFQPDFProcessor structure -struct _cfPDFToPDFQPDFProcessor { - pdfio_file_t *pdf; // pdfio object to represent the PDF document - _cfPDFToPDFQPDFPageHandle **orig_pages; - int num_pages; - bool hasCM; - char *extraheader; // Dynamic string for comments -}; - -void -_cfPDFToPDFQPDFProcessor_close_file(_cfPDFToPDFQPDFProcessor *processor) +void _cfPDFToPDF_PDFioProcessor_close_file(_cfPDFToPDF_PDFioProcessor *handle) { - if (processor->pdf) + if (handle->pdf != NULL) { - pdfioFileClose(processor->pdf); - processor->pdf = NULL; + pdfioFileClose(handle->pdf); + handle->pdf = NULL; } - processor->hasCM = false; + handle->hasCM = false; } -bool -_cfPDFToPDFQPDFProcessor_load_file(_cfPDFToPDFQPDFProcessor *processor, - FILE *f, pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e take, - int flatten_forms) + +bool +_cfPDFToPDF_PDFioProcessor_load_file(_cfPDFToPDF_PDFioProcessor *handle, + FILE *f, pdftopdf_doc_t *doc, + pdftopdf_arg_ownership_e take, int flatten_forms) { - _cfPDFToPDFQPDFProcessor_close_file(processor); + _cfPDFToPDF_PDFioProcessor_close_file(handle); - if (!f) { - // Handle error in C style - return false; - } + if (!f) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "load_file(NULL, ...) not allowed"); + return false; + } - // Simulate opening the file with PDFIO - processor->pdf = pdfioFileOpen(f, NULL); - if (!processor->pdf) { - if (take == CF_PDFTOPDF_TAKE_OWNERSHIP) - fclose(f); - return false; - } + handle->pdf = pdfioFileCreate("tempfile", NULL, NULL, NULL, NULL, NULL); - // Call start function to initialize the pages - _cfPDFToPDFQPDFProcessor_start(processor, flatten_forms); - return true; -} + + if (handle->pdf == NULL) + { + if (take == CF_PDFTOPDF_TAKE_OWNERSHIP) + { + fclose(f); + } + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "Failed to open PDF file."); + return false; + } -bool -_cfPDFToPDFQPDFProcessor_load_filename(_cfPDFToPDFQPDFProcessor *processor, - const char *name, pdftopdf_doc_t *doc, - int flatten_forms) -{ - _cfPDFToPDFQPDFProcessor_close_file(processor); + switch (take) + { + case CF_PDFTOPDF_WILL_STAY_ALIVE: + break; - processor->pdf = pdfioFileOpenFile(name, NULL); - if (!processor->pdf) { + case CF_PDFTOPDF_TAKE_OWNERSHIP: + if (fclose(f) != 0) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "Failed to close file after loading."); return false; - } + } + break; - // Call start function to initialize the pages - _cfPDFToPDFQPDFProcessor_start(processor, flatten_forms); - return true; + case CF_PDFTOPDF_MUST_DUPLICATE: + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "CF_PDFTOPDF_MUST_DUPLICATE is not supported."); + return false; + } + + + //_cfPDFToPDF_PDFioProcessor_start(handle, flatten_forms); + return true; } -void -_cfPDFToPDFQPDFProcessor_start(_cfPDFToPDFQPDFProcessor *processor, int flatten_forms) +bool _cfPDFToPDF_PDFioProcessor_load_filename(_cfPDFToPDF_PDFioProcessor *handle, + const char *name, + pdftopdf_doc_t *doc, + int flatten_forms) { - DEBUG_assert(processor->pdf); + _cfPDFToPDF_PDFioProcessor_close_file(handle); - processor->orig_pages = pdfioFileGetPages(processor->pdf, &(processor->num_pages)); - // Remove the pages from the PDF document for processing - for (int i = 0; i < processor->num_pages; i++) { - pdfioPageRemove(processor->orig_pages[i]); - } + handle->pdf = pdfioFileOpen(name, NULL, NULL, NULL, NULL); + if (!handle->pdf) + { + if (doc->logfunc) doc->logfunc(doc->logdata, 3, + "cfFilterPDFToPDF: load_filename failed: Could not open file %s", + name); + return false; + } - // Initialize other PDF data as necessary (e.g., remove defunct keys) - pdfioFileRemoveKey(processor->pdf, "/PageMode"); - pdfioFileRemoveKey(processor->pdf, "/Outlines"); - pdfioFileRemoveKey(processor->pdf, "/OpenAction"); - pdfioFileRemoveKey(processor->pdf, "/PageLabels"); + //_cfPDFToPDF_PDFioProcessor_start(handle, flatten_forms); + return true; } -bool -_cfPDFToPDFQPDFProcessor_check_print_permissions(_cfPDFToPDFQPDFProcessor *processor, pdftopdf_doc_t *doc) + +bool +_cfPDFToPDF_PDFioProcessor_check_print_permissions(_cfPDFToPDF_PDFioProcessor *handle, + pdftopdf_doc_t *doc) { - if (!processor->pdf) { - return false; - } + if (!handle->pdf) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: No PDF loaded"); + return false; + } - // Simulate permission checking with PDFIO - return pdfioFileAllowPrint(processor->pdf); + int permissions = pdfioFileGetPermissions(handle->pdf, NULL); + + if ((permissions & PDFIO_PERMISSION_PRINT_HIGH) || (permissions & PDFIO_PERMISSION_PRINT)) + return true; + + return false; } -_cfPDFToPDFQPDFPageHandle ** -_cfPDFToPDFQPDFProcessor_get_pages(_cfPDFToPDFQPDFProcessor *processor, - pdftopdf_doc_t *doc, int *num_pages) +pdfio_obj_t** +get_all_pages(pdfio_file_t *pdf) { - if (!processor->pdf) { - *num_pages = 0; - return NULL; + size_t num_pages = pdfioFileGetNumPages(pdf); + pdfio_obj_t **pages = malloc(sizeof(pdfio_obj_t *) * num_pages); + for (size_t i = 0; i < num_pages; i++) + { + pages[i] = pdfioFileGetPage(pdf, i); } - - *num_pages = processor->num_pages; - return processor->orig_pages; + return pages; } -_cfPDFToPDFQPDFPageHandle * -_cfPDFToPDFQPDFProcessor_new_page(_cfPDFToPDFQPDFProcessor *processor, - float width, float height, - pdftopdf_doc_t *doc) +/* +void +_cfPDFToPDF_PDFioProcessor_start(_cfPDFToPDF_PDFioProcessor *proc, + int flatten_forms) { - if (!processor->pdf) { - return NULL; - } + if (!proc->pdf) + { + fprintf(stderr, "No PDF loaded.\n"); + return; + } - return _cfPDFToPDFQPDFPageHandle_new_new(processor->pdf, width, height); -} + if (flatten_forms) + { + pdfio_dict_t *catalog = pdfioFileGetCatalog(proc->pdf); + pdfio_obj_t *acroForm = pdfioDictGetObj(catalog, "AcroForm"); -void -_cfPDFToPDFQPDFProcessor_add_page(_cfPDFToPDFQPDFProcessor *processor, - _cfPDFToPDFQPDFPageHandle *page, bool front) -{ - DEBUG_assert(processor->pdf); - if (front) { - pdfioFileInsertPage(processor->pdf, page->page, 0); // Insert at the front - } else { - pdfioFileAppendPage(processor->pdf, page->page); // Append at the end - } -} + if (acroForm) + { + pdfio_dict_t *acroForm_dict = pdfioObjGetDict(acroForm); + pdfio_array_t *fields = pdfioDictGetArray(acroForm_dict, "Fields"); + size_t num_fields = pdfioArrayGetSize(fields); -void -_cfPDFToPDFQPDFProcessor_multiply(_cfPDFToPDFQPDFProcessor *processor, - int copies, bool collate) -{ - DEBUG_assert(processor->pdf); - DEBUG_assert(copies > 0); + // Iterating over each field and generating render as static content + for (size_t i = 0; i < num_fields; i++) + { + pdfio_obj_t *field = pdfioArrayGetObj(fields, i); + pdfio_dict_t *field_dict = pdfioObjGetDict(field); + const char *field_type = pdfioDictGetName(field_dict, "FT"); // Field type + const char *field_value = pdfioDictGetString(field_dict, "V"); // Field value - _cfPDFToPDFQPDFPageHandle **pages = _cfPDFToPDFQPDFProcessor_get_pages(processor, NULL, &processor->num_pages); + if (field_type && strcmp(field_type, "Tx") == 0) + { + double x = 100.0, y = 200.0; + pdfio_stream_t *stream = pdfioPageOpenStream(handle->page, 0, true); - if (collate) { - for (int i = 1; i < copies; i++) { - for (int j = 0; j < processor->num_pages; j++) { - pdfioFileAppendPage(processor->pdf, pages[j]->page); - } - } - } else { - for (int j = 0; j < processor->num_pages; j++) { - for (int i = 1; i < copies; i++) { - pdfioFileAppendPage(processor->pdf, pages[j]->page); - } + pdfioContentTextMoveTo(stream, x, y); + pdfioContentTextShow(stream, 1, field_value); // Render the text + + pdfioStreamClose(stream); // Closing the stream after render } + } + pdfioDictSetNull(catalog, "AcroForm"); } + } + + + // Get all pages + proc->orig_pages = get_all_pages(proc->pdf); + size_t num_pages = pdfioFileGetNumPages(proc->pdf); + + // Remove (unlink) all pages + for (size_t i = 0; i < num_pages; i++) + { +// pdfio_remove_page(pdf, orig_pages[i]); + } + + pdfio_dict_t *root = pdfioFileGetCatalog(proc->pdf); + pdfioDictSetNull(root, "PageMode"); + pdfioDictSetNull(root, "Outlines"); + pdfioDictSetNull(root, "OpenAction"); + pdfioDictSetNull(root, "PageLabels"); + + pdfioFileClose(proc->pdf); } +*/ -void -_cfPDFToPDFQPDFProcessor_auto_rotate_all(_cfPDFToPDFQPDFProcessor *processor, - bool dst_lscape, - pdftopdf_rotation_e normal_landscape) +_cfPDFToPDFPageHandle** +_cfPDFToPDF_PDFioProcessor_get_pages(_cfPDFToPDF_PDFioProcessor *handle, + pdftopdf_doc_t *doc, int *out_len) { - DEBUG_assert(processor->pdf); + _cfPDFToPDFPageHandle **ret = NULL; - for (int i = 0; i < processor->num_pages; i++) { - _cfPDFToPDFQPDFPageHandle *page = processor->orig_pages[i]; + if (handle->orig_pages_size == 0 || handle->orig_pages == NULL) + { + if (doc->logfunc) + { + doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: No PDF loaded"); + } + *out_len = 0; + return ret; + } - pdftopdf_rotation_e src_rot = pdfioPageGetRotation(page->page); - _cfPDFToPDFPageRect rect = _cfPDFToPDFQPDFPageHandle_get_rect(page); + int len = handle->orig_pages_size; + *out_len = len; - bool src_lscape = rect.width > rect.height; - if (src_lscape != dst_lscape) { - pdfioPageSetRotation(page->page, src_rot + normal_landscape); - } + ret = (_cfPDFToPDFPageHandle **)malloc(len * sizeof(_cfPDFToPDFPageHandle *)); + if (!ret) + { + fprintf(stderr, "Memory allocation failed for pages array\n"); + } + + for (int i = 0; i < len; i++) + { + ret[i] = (_cfPDFToPDFPageHandle *)malloc(sizeof(_cfPDFToPDFPageHandle)); + if (!ret[i]) + { + fprintf(stderr, "Memory allocation failed for page handle %d\n", i + 1); + for (int j = 0; j < i; j++) + { + free(ret[j]); + } + free(ret); } + + ret[i]->page = handle->orig_pages[i]; + //ret[i]->orig_pages_size = i + 1; + } + + return ret; } -void -_cfPDFToPDFQPDFProcessor_add_cm(_cfPDFToPDFQPDFProcessor *processor, - const char *defaulticc, const char *outputicc) +_cfPDFToPDFPageHandle* +_cfPDFToPDF_PDFioProcessor_new_page(_cfPDFToPDF_PDFioProcessor *handle, + float width, float height, + pdftopdf_doc_t *doc) { - DEBUG_assert(processor->pdf); - - if (pdfioFileHasOutputIntent(processor->pdf)) { - return; // Nothing to do - } + if (!handle->pdf) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: No PDF loaded"); + return NULL; + } - // Simulate adding ICC profile and output intent with PDFIO - pdfioFileAddICCProfile(processor->pdf, defaulticc); - pdfioFileAddOutputIntent(processor->pdf, outputicc); + _cfPDFToPDFPageHandle *page_handle = (_cfPDFToPDFPageHandle *)malloc(sizeof(_cfPDFToPDFPageHandle)); + + _cfPDFToPDFPageHandle_create_newMode(page_handle, handle->pdf, width, height); - processor->hasCM = true; + return page_handle; } void -_cfPDFToPDFQPDFProcessor_set_comments(_cfPDFToPDFQPDFProcessor *processor, - const char **comments, int num_comments) +_cfPDFToPDF_PDFioProcessor_multiply(_cfPDFToPDF_PDFioProcessor *handle, + int copies, bool collate) { - if (processor->extraheader) { - free(processor->extraheader); + int num_pages=pdfioFileGetNumPages(handle->pdf); + + pdfio_obj_t **pages = (pdfio_obj_t **)malloc(num_pages * sizeof(pdfio_obj_t *)); + + for (int i = 0; i < num_pages; i++) + { + pages[i] = pdfioFileGetPage(handle->pdf, i); + } + + if (collate) { + for (int iA = 1; iA < copies; iA++) + { + for (int iB = 0; iB < num_pages; iB++) + { + pdfioPageCopy(handle->pdf, pages[iB]); + } } + } - // Concatenate comments into one string - int total_len = 0; - for (int i = 0; i < num_comments; i++) { - total_len += strlen(comments[i]) + 1; + else + { + for (int iB = 0; iB < num_pages; iB++) + { + for (int iA = 1; iA < copies; iA++) + { + pdfioPageCopy(handle->pdf, pages[iB]); + } } + } +} - processor->extraheader = malloc(total_len + 1); - processor->extraheader[0] = '\0'; +void +_cfPDFToPDF_PDFioProcessor_auto_rotate_all(_cfPDFToPDF_PDFioProcessor *handle, + bool dst_lscape, + pdftopdf_rotation_e normal_landscape) +{ + const int len = handle->orig_pages_size; + + for (int iA = 0; iA < len; iA ++) + { + pdfio_obj_t *page = handle->orig_pages[iA]; + + pdftopdf_rotation_e src_rot = _cfPDFToPDFGetRotate(page); + + pdfio_rect_t trimBox = _cfPDFToPDFGetTrimBox(page); + _cfPDFToPDFPageRect ret = _cfPDFToPDFGetBoxAsRect(&trimBox); + + _cfPDFToPDFPageRect_rotate_move(&ret, src_rot, ret.width, ret.height); - for (int i = 0; i < num_comments; i++) { - strcat(processor->extraheader, comments[i]); - strcat(processor->extraheader, "\n"); + const bool src_lscape = (ret.width > ret.height); + + if (src_lscape != dst_lscape) + { + pdftopdf_rotation_e rotation = normal_landscape; + + pdfio_dict_t *pageDict = pdfioObjGetDict(page); + pdfioDictSetNumber(pageDict, "Rotate", + _cfPDFToPDFMakeRotate(src_rot + rotation)); } + } } -void -_cfPDFToPDFQPDFProcessor_emit_file(_cfPDFToPDFQPDFProcessor *processor, - FILE *dst, pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e take) +void +_cfPDFToPDF_PDFioProcessor_add_cm(_cfPDFToPDF_PDFioProcessor *handle, + const char *defaulticc, const char *outputicc) { - if (!processor->pdf) { - return; - } - - // Simulate writing the PDF to a file with PDFIO - pdfioFileWrite(processor->pdf, dst, processor->hasCM, processor->extraheader); + if (_cfPDFToPDFHasOutputIntent(handle->pdf)) + return; - if (take == CF_PDFTOPDF_TAKE_OWNERSHIP) { - fclose(dst); - } + pdfio_obj_t *srcicc = _cfPDFToPDFSetDefaultICC(handle->pdf, defaulticc); + _cfPDFToPDFAddDefaultRGB(handle->pdf, srcicc); + _cfPDFToPDFAddOutputIntent(handle->pdf, outputicc); + + handle->hasCM = true; } -void -_cfPDFToPDFQPDFProcessor_emit_filename(_cfPDFToPDFQPDFProcessor *processor, - const char *name, pdftopdf_doc_t *doc) +void +_cfPDFToPDF_PDFioProcessor_set_comments(_cfPDFToPDF_PDFioProcessor *handle, + char **comments, int num_comments) { - if (!processor->pdf) { - return; - } + if (handle->extraheader) + { + free(handle->extraheader); + } - // Special case: name == NULL -> stdout - FILE *output = name ? fopen(name, "wb") : stdout; + handle->extraheader = (char *)malloc(1); + handle->extraheader[0] = '\0'; - // Simulate writing the PDF to a file with PDFIO - pdfioFileWrite(processor->pdf, output, processor->hasCM, processor->extraheader); + int total_length = 0; + for (int i = 0; i < num_comments; i++) + { + total_length += strlen(comments[i]) + 1; + } - if (name) { - fclose(output); - } + handle->extraheader = (char *)realloc(handle->extraheader, total_length + 1); + + for (int i = 0; i < num_comments; i++) + { + strcat(handle->extraheader, comments[i]); + strcat(handle->extraheader, "\n"); + } } -bool -_cfPDFToPDFQPDFProcessor_has_acro_form(_cfPDFToPDFQPDFProcessor *processor) +void +_cfPDFToPDF_PDFioProcessor_emit_file(_cfPDFToPDF_PDFioProcessor *handle, + FILE *f, pdftopdf_doc_t *doc, + pdftopdf_arg_ownership_e take) { - if (!processor->pdf) { - return false; - } +} - // Simulate checking for an AcroForm in the PDF with PDFIO - return pdfioFileHasAcroForm(processor->pdf); +void +_cfPDFToPDF_PDFioProcessor_emit_filename(_cfPDFToPDF_PDFioProcessor *handle, + const char *name, pdftopdf_doc_t *doc) +{ } -*/ +bool +_cfPDFToPDF_PDFioProcessor_has_acro_form(_cfPDFToPDF_PDFioProcessor *handle) +{ + if (!handle->pdf) + { + return false; + } + + pdfio_dict_t *root = pdfioFileGetCatalog(handle->pdf); + + if (!pdfioDictGetDict(root, "AcroForm")) + return false; + return true; +} diff --git a/cupsfilters/pdftopdf/pdftopdf-processor.cxx b/cupsfilters/pdftopdf/pdftopdf-processor.cxx index 3927f379..92e9fc03 100644 --- a/cupsfilters/pdftopdf/pdftopdf-processor.cxx +++ b/cupsfilters/pdftopdf/pdftopdf-processor.cxx @@ -193,7 +193,7 @@ _cfPDFToPDFBookletShuffle(int numPages, // }}} bool -_cfProcessPDFToPDF(_cfPDFToPDFProcessor &proc, +_cfProcessPDFToPDF(_cfPDFToPDF_PDFioProcessor &proc, _cfPDFToPDFProcessingParameters ¶m, pdftopdf_doc_t *doc) // {{{ { diff --git a/cupsfilters/pdftopdf/processor.h b/cupsfilters/pdftopdf/processor.h new file mode 100644 index 00000000..06e24f43 --- /dev/null +++ b/cupsfilters/pdftopdf/processor.h @@ -0,0 +1,246 @@ +#ifndef C_PDFTOPDF_PROCESSOR_PRIVATE_H +#define C_PDFTOPDF_PROCESSOR_PRIVATE_H + + +#include "C-pptypes-private.h" +#include "pdfio.h" +#include "C-pptypes-private.h" +#include "C-nup-private.h" +#include "C-pdftopdf-private.h" +#include "C-intervalset-private.h" +#include +#include +#define HASH_TABLE_SIZE 2048 + +typedef struct KeyValuePair { + char *key; // Key (string) + pdfio_obj_t *value; // Value (PDF object handle) + struct KeyValuePair *next; // For handling collisions (chaining) +} KeyValuePair; + +typedef struct HashTable { + KeyValuePair *buckets[HASH_TABLE_SIZE]; // Array of pointers to key-value pairs + int count; // Number of filled elements in the hash table +} HashTable; + +HashTable *hashCreate_hash_table(); +void hashInsert(HashTable *table, const char *key, pdfio_obj_t *value); +pdfio_obj_t *hashGet(HashTable *table, const char *key); +int hashGet_filled_count(HashTable *table); +void hashFree_hash_table(HashTable *table); + +typedef struct _cfPDFToPDFPageHandle{ + // 1st mode: existing + pdfio_obj_t *page; + int no; + + // 2nd mode: create new + HashTable *xobjs; + char *content; + + pdftopdf_rotation_e rotation; +}_cfPDFToPDFPageHandle; + +void _cfPDFToPDFPageHandle_existingMode(_cfPDFToPDFPageHandle *handle, + pdfio_obj_t *page, + int orig_no); // 1st mode:existing + +void _cfPDFToPDFPageHandle_create_newMode(_cfPDFToPDFPageHandle *handle, + pdfio_file_t *pdf, + float width, float height); // 2nd mode:create new + +void _cfPDFToPDFPageHandle_destroy(_cfPDFToPDFPageHandle *handle); + +void _cfPDFToPDFPageHandle_debug(_cfPDFToPDFPageHandle *handle, + const _cfPDFToPDFPageRect *rect, + float xpos, float ypos); + +bool _cfPDFToPDFPageHandle_is_existing(_cfPDFToPDFPageHandle *handle); + +pdfio_obj_t* _cfPDFToPDFPageHandle_get(_cfPDFToPDFPageHandle *handle); + + +_cfPDFToPDFPageRect _cfPDFToPDFPageHandle_get_rect(const _cfPDFToPDFPageHandle *handle); + +void _cfPDFToPDFPageHandle_add_border_rect(_cfPDFToPDFPageHandle *handle, + pdfio_file_t *pdf, + const _cfPDFToPDFPageRect rect, + pdftopdf_border_type_e border, + float fscale); + + +pdftopdf_rotation_e _cfPDFToPDFPageHandle_crop(_cfPDFToPDFPageHandle *handle, + const _cfPDFToPDFPageRect *cropRect, + pdftopdf_rotation_e orientation, + pdftopdf_rotation_e param_orientation, + pdftopdf_position_e xpos, + pdftopdf_position_e ypos, + bool scale, bool autorotate, + pdftopdf_doc_t *doc); + + +void _cfPDFToPDFPageHandle_add_subpage(_cfPDFToPDFPageHandle *handle, + _cfPDFToPDFPageHandle *sub, + pdfio_file_t *pdf, + float xpos, float ypos, float scale, + const _cfPDFToPDFPageRect *crop); + +bool _cfPDFToPDFPageHandle_is_landscape(const _cfPDFToPDFPageHandle *handle, + pdftopdf_rotation_e orientation); + +void _cfPDFToPDFPageHandle_mirror(_cfPDFToPDFPageHandle *handle, + pdfio_file_t *pdf); + +void _cfPDFToPDFPageHandle_rotate(_cfPDFToPDFPageHandle *handle, pdftopdf_rotation_e rot); + +void _cfPDFToPDFPageHandle_add_label(_cfPDFToPDFPageHandle *handle, + pdfio_file_t *pdf, + const _cfPDFToPDFPageRect *rect, + const char *label); + +typedef enum pdftopdf_arg_ownership_e { + CF_PDFTOPDF_WILL_STAY_ALIVE, + CF_PDFTOPDF_MUST_DUPLICATE, + CF_PDFTOPDF_TAKE_OWNERSHIP +} pdftopdf_arg_ownership_e; + +typedef struct _cfPDFToPDF_PDFioProcessor{ + // Other members + _cfPDFToPDFPageHandle *pageHandle; + + pdfio_file_t *pdf; // Equivalent to std::unique_ptr + pdfio_obj_t **orig_pages; // Equivalent to std::vector + size_t orig_pages_size; // Current number of pages + size_t orig_pages_capacity; // Capacity for page array + + bool hasCM; + char *extraheader; +} _cfPDFToPDF_PDFioProcessor; + +//_cfPDFToPDFQPDFProcessor functions + + +void _cfPDFToPDF_PDFioProcessor_close_file(_cfPDFToPDF_PDFioProcessor *processor); + +bool _cfPDFToPDF_PDFioProcessor_load_file(_cfPDFToPDF_PDFioProcessor *processor, + FILE *f, pdftopdf_doc_t *doc, + pdftopdf_arg_ownership_e take, int flatten_forms); + +bool _cfPDFToPDF_PDFioProcessor_load_filename(_cfPDFToPDF_PDFioProcessor *processor, + const char *name, + pdftopdf_doc_t *doc, + int flatten_forms); +/* +void _cfPDFToPDF_PDFioProcessor_start(_cfPDFToPDF_PDFioProcessor *processor, + int flatten_forms); +*/ +bool _cfPDFToPDF_PDFioProcessor_check_print_permissions(_cfPDFToPDF_PDFioProcessor *processor, + pdftopdf_doc_t *doc); + +_cfPDFToPDFPageHandle** _cfPDFToPDF_PDFioProcessor_get_pages(_cfPDFToPDF_PDFioProcessor *handle, + pdftopdf_doc_t *doc, + int *out_len); + +_cfPDFToPDFPageHandle* _cfPDFToPDF_PDFioProcessor_new_page(_cfPDFToPDF_PDFioProcessor *handle, + float width, float height, + pdftopdf_doc_t *doc); + +void _cfPDFToPDF_PDFioProcessor_add_page(_cfPDFToPDF_PDFioProcessor *handle, + _cfPDFToPDFPageHandle *page, + bool front); + + +void _cfPDFToPDF_PDFioProcessor_multiply(_cfPDFToPDF_PDFioProcessor *handle, + int copies, bool collate); + +void _cfPDFToPDF_PDFioProcessor_auto_rotate_all(_cfPDFToPDF_PDFioProcessor *handle, + bool dst_lscape, + pdftopdf_rotation_e normal_landscape); + +void _cfPDFToPDF_PDFioProcessor_add_cm(_cfPDFToPDF_PDFioProcessor *handle, + const char *defaulticc, const char *outputicc); + +void _cfPDFToPDF_PDFioProcessor_set_comments(_cfPDFToPDF_PDFioProcessor *handle, + char **comments, int num_comments); +void _cfPDFToPDF_PDFioProcessor_emit_file(_cfPDFToPDF_PDFioProcessor *handle, + FILE *f, pdftopdf_doc_t *doc, + pdftopdf_arg_ownership_e take); + +void _cfPDFToPDF_PDFioProcessor_emit_filename(_cfPDFToPDF_PDFioProcessor *handle, + const char *name, pdftopdf_doc_t *doc); + +bool _cfPDFToPDF_PDFioProcessor_has_acro_form(_cfPDFToPDF_PDFioProcessor *handle); + +typedef enum { + CF_PDFTOPDF_BOOKLET_OFF, + CF_PDFTOPDF_BOOKLET_ON, + CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE +} pdftopdf_booklet_mode_e; + +typedef struct { + int job_id, num_copies; + const char *user, *title; + bool pagesize_requested; + bool fitplot; + bool fillprint; // print-scaling = fill + bool cropfit; // -o crop-to-fit + bool autoprint; // print-scaling = auto + bool autofit; // print-scaling = auto-fit + bool fidelity; + bool no_orientation; + _cfPDFToPDFPageRect page; + pdftopdf_rotation_e orientation, normal_landscape; // normal_landscape (i.e. default direction) is e.g. needed for number-up=2 + bool paper_is_landscape; + bool duplex; + pdftopdf_border_type_e border; + _cfPDFToPDFNupParameters nup; + bool reverse; + + char *page_label; + bool even_pages, odd_pages; + _cfPDFToPDFIntervalSet *page_ranges; + _cfPDFToPDFIntervalSet *input_page_ranges; + + bool mirror; + + pdftopdf_position_e xpos, ypos; + + bool collate; + + bool even_duplex; // make number of pages a multiple of 2 + + pdftopdf_booklet_mode_e booklet; + int book_signature; + + bool auto_rotate; + + int device_copies; + bool device_collate; + bool set_duplex; + + int page_logging; + int copies_to_be_logged; +} _cfPDFToPDFProcessingParameters; + +//_cfPDFToPDFQPDFPageHandle functions +//inherited functions + +//C-pdftopdf-processor-private functions + +void _cfPDFToPDFProcessingParameters_init(_cfPDFToPDFProcessingParameters *processingParams); + +bool _cfPDFToPDFProcessingParameters_with_page(const _cfPDFToPDFProcessingParameters *self, + int outno); + +bool _cfPDFToPDFProcessingParameters_have_page(const _cfPDFToPDFProcessingParameters *self, + int pageno); +void _cfPDFToPDFProcessingParameters_dump(const _cfPDFToPDFProcessingParameters *self, + pdftopdf_doc_t *doc); + +int* _cfPDFToPDFBookletShuffle(int numPages, int signature, int* ret_size); + +bool _cfProcessPDFToPDF(pdfio_file_t *pdf, + _cfPDFToPDF_PDFioProcessor *proc, + _cfPDFToPDFProcessingParameters *param, + pdftopdf_doc_t *doc); +#endif // C_PDFTOPDF_PROCESSOR_PRIVATE_H From b7c7d2ab007bcafe54c0b7ea836ec9cd5f6c819f Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Fri, 20 Sep 2024 02:31:16 +0530 Subject: [PATCH 44/64] updated functions, run successfullyyyyyy --- Makefile.am | 2 + cupsfilters/pdftopdf/C-nup.c | 2 +- cupsfilters/pdftopdf/C-pdftopdf-processor.c | 24 +- cupsfilters/pdftopdf/C-pdftopdf.c | 1239 ++++++++++++------- cupsfilters/pdftopdf/processor.h | 9 +- 5 files changed, 813 insertions(+), 463 deletions(-) diff --git a/Makefile.am b/Makefile.am index e2d704c7..e6ae4d34 100644 --- a/Makefile.am +++ b/Makefile.am @@ -180,6 +180,8 @@ libcupsfilters_la_SOURCES = \ cupsfilters/pack.c \ cupsfilters/pclmtoraster.cxx \ cupsfilters/pdf.cxx \ + cupsfilters/pdftopdf/C-pdftopdf.c \ + cupsfilters/pdftopdf/C-pdftopdf-private.h \ cupsfilters/pdftopdf/C-pdftopdf-processor.c \ cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c \ cupsfilters/pdftopdf/processor.h \ diff --git a/cupsfilters/pdftopdf/C-nup.c b/cupsfilters/pdftopdf/C-nup.c index a26d1f2d..7fd8d03b 100644 --- a/cupsfilters/pdftopdf/C-nup.c +++ b/cupsfilters/pdftopdf/C-nup.c @@ -317,6 +317,6 @@ _cfPDFToPDFParseNupLayout(const char *val, ret->ystart = pos0.second; } - return (val[4] == 0); // everything seen? + return (val[4] == 0); } diff --git a/cupsfilters/pdftopdf/C-pdftopdf-processor.c b/cupsfilters/pdftopdf/C-pdftopdf-processor.c index bcd19ac1..d6ad5481 100644 --- a/cupsfilters/pdftopdf/C-pdftopdf-processor.c +++ b/cupsfilters/pdftopdf/C-pdftopdf-processor.c @@ -1,6 +1,7 @@ // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. +// #include "processor.h" #include @@ -247,8 +248,7 @@ _cfPDFToPDFBookletShuffle(int numPages, int signature, int* ret_size) } bool -_cfProcessPDFToPDF(pdfio_file_t *pdf, - _cfPDFToPDF_PDFioProcessor *proc, +_cfProcessPDFToPDF( _cfPDFToPDF_PDFioProcessor *proc, _cfPDFToPDFProcessingParameters *param, pdftopdf_doc_t *doc) { @@ -494,7 +494,7 @@ _cfProcessPDFToPDF(pdfio_file_t *pdf, { _cfPDFToPDFPageHandle_rotate(curpage, param->orientation); if (param->mirror) - _cfPDFToPDFPageHandle_mirror(curpage, pdf); + _cfPDFToPDFPageHandle_mirror(curpage, proc->pdf); // _cfPDFToPDF_PDFioProcessor_add_page(proc, curpage, param->reverse); // Log page in /var/log/cups/page_log @@ -513,11 +513,11 @@ _cfProcessPDFToPDF(pdfio_file_t *pdf, continue; if (param->border != NONE) - _cfPDFToPDFPageHandle_add_border_rect(page, pdf, rect, param->border, 1.0 / pgedit.scale); + _cfPDFToPDFPageHandle_add_border_rect(page, proc->pdf, rect, param->border, 1.0 / pgedit.scale); if (param->page_label[0] != '\0') { - _cfPDFToPDFPageHandle_add_label(page, pdf, ¶m->page, param->page_label); + _cfPDFToPDFPageHandle_add_label(page, proc->pdf, ¶m->page, param->page_label); } if(param->cropfit) @@ -533,22 +533,22 @@ _cfProcessPDFToPDF(pdfio_file_t *pdf, { xpos2 = (param->page.width - (get_rect_height.height)) / 2; ypos2 = (param->page.height - (get_rect_width.width)) / 2; - _cfPDFToPDFPageHandle_add_subpage(curpage, page, pdf, ypos2 + xpos, xpos2 + ypos, 1, NULL); + _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, ypos2 + xpos, xpos2 + ypos, 1, NULL); } else { xpos2 = (param->page.width - get_rect_width.width) / 2; ypos2 = (param->page.height - get_rect_height.height) /2; - _cfPDFToPDFPageHandle_add_subpage(curpage, page, pdf, xpos2 + xpos, ypos2 + ypos, 1, NULL); + _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, xpos2 + xpos, ypos2 + ypos, 1, NULL); } } else { - _cfPDFToPDFPageHandle_add_subpage(curpage, page, pdf, pgedit.xpos + xpos, pgedit.ypos + ypos, pgedit.scale, NULL); + _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, pgedit.xpos + xpos, pgedit.ypos + ypos, pgedit.scale, NULL); } } else - _cfPDFToPDFPageHandle_add_subpage(curpage, page, pdf, pgedit.xpos + xpos, pgedit.ypos + ypos, pgedit.scale, NULL); + _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, pgedit.xpos + xpos, pgedit.ypos + ypos, pgedit.scale, NULL); #ifdef DEBUG _cfPDFToPDFPageHandle *dbg = (_cfPDFToPDFPageHandle *)curpage; @@ -563,11 +563,11 @@ _cfProcessPDFToPDF(pdfio_file_t *pdf, { _cfPDFToPDFPageHandle_rotate(curpage, param->orientation); if(param->mirror) - _cfPDFToPDFPageHandle_mirror(curpage, pdf); + _cfPDFToPDFPageHandle_mirror(curpage, proc->pdf); // need to output empty page to not confuse duplex - _cfPDFToPDF_PDFioProcessor_add_page(proc, _cfPDFToPDF_PDFioProcessor_new_page(proc, - param->page.width, param->page.height, doc), param->reverse); +// _cfPDFToPDF_PDFioProcessor_add_page(proc, _cfPDFToPDF_PDFioProcessor_new_page(proc, +// param->page.width, param->page.height, doc), param->reverse); // Log page in /var/log/cups/page_log if(param->page_logging == 1) diff --git a/cupsfilters/pdftopdf/C-pdftopdf.c b/cupsfilters/pdftopdf/C-pdftopdf.c index b1fd3a35..b411f9a3 100644 --- a/cupsfilters/pdftopdf/C-pdftopdf.c +++ b/cupsfilters/pdftopdf/C-pdftopdf.c @@ -6,25 +6,32 @@ // #include + #include -#include #include -#include -#include +#include +#include +#include +#include #include +#include #include #include #include #include +#include +#include +#include +#include "C-pdftopdf-private.h" +#include "processor.h" + #include -#include "pdftopdf-private.h" -#include "C-pdftopdf-processor-private.h" static bool optGetInt(const char *name, int num_options, cups_option_t *options, - int *ret) + int *ret) // {{{ { const char *val = cupsGetOption(name, num_options, options); if (val) @@ -34,12 +41,13 @@ optGetInt(const char *name, } return false; } +// }}} static bool optGetFloat(const char *name, int num_options, cups_option_t *options, - float *ret) + float *ret) // {{{ { const char *val = cupsGetOption(name, num_options, options); if (val) @@ -49,30 +57,37 @@ optGetFloat(const char *name, } return false; } +// }}} static bool -is_false(const char *value) +is_false(const char *value) // {{{ { - if (!value) return false; + if (!value) + return false; return ((strcasecmp(value, "no") == 0) || (strcasecmp(value, "off") == 0) || (strcasecmp(value, "false") == 0)); } +// }}} static bool -is_true(const char *value) +is_true(const char *value) // {{{ { - if (!value) return false; + if (!value) + return false; return ((strcasecmp(value, "yes") == 0) || (strcasecmp(value, "on") == 0) || (strcasecmp(value, "true") == 0)); } +// }}} static bool parsePosition(const char *value, pdftopdf_position_e *xpos, - pdftopdf_position_e *ypos) + pdftopdf_position_e *ypos) // {{{ { + // ['center','top','left','right','top-left','top-right','bottom', + // 'bottom-left','bottom-right'] *xpos = CENTER; *ypos = CENTER; int next = 0; @@ -91,523 +106,857 @@ parsePosition(const char *value, if (next) { - if (value[next] == 0) return true; - else if (value[next] != '-') return false; + if (value[next] == 0) + return true; + else if (value[next] != '-') + return false; value += next + 1; } - - if (strcasecmp(value, "left") == 0) *xpos = LEFT; - else if (strcasecmp(value, "right") == 0) *xpos = RIGHT; - else return false; + if (strcasecmp(value, "left") == 0) + *xpos = LEFT; + else if (strcasecmp(value, "right") == 0) + *xpos = RIGHT; + else + return false; return true; } +// }}} static void -parseRanges(const char *range, _cfPDFToPDFIntervalSet *ret) +parseRanges(const char *range, _cfPDFToPDFIntervalSet *ret) // {{{ { - ret->clear(); - if (!range) { - ret->add(1); // everything - ret->finish(); + _cfPDFToPDFIntervalSet_clear(ret); + + if (!range) + { + _cfPDFToPDFIntervalSet_add(ret, 1, 1); + _cfPDFToPDFIntervalSet_finish(ret); return; - } + } - int lower, upper; - while (*range) { - if (*range == '-') { - range++; - upper = strtol(range, (char **)&range, 10); - if (upper >= 2147483647) ret->add(1); - else ret->add(1, upper + 1); - } else { - lower = strtol(range, (char **)&range, 10); - if (*range == '-') { - range++; - if (!isdigit(*range)) ret->add(lower); - else { - upper = strtol(range, (char **)&range, 10); - if (upper >= 2147483647) ret->add(lower); - else ret->add(lower, upper + 1); - } - } else { - ret->add(lower, lower + 1); - } + int lower, upper; + while (*range) + { + if (*range == '-') + { + range++; + upper = strtol(range, (char **)&range, 10); + if (upper >= 2147483647) + _cfPDFToPDFIntervalSet_add(ret, 1, 1); + else + _cfPDFToPDFIntervalSet_add(ret, 1, upper+1); + } + else + { + lower = strtol(range, (char **)&range, 10); + if (*range == '-') + { + range++; + if (!isdigit(*range)) + _cfPDFToPDFIntervalSet_add(ret, lower, lower); + else + { + upper = strtol(range, (char **)&range, 10); + if (upper >= 2147483647) + _cfPDFToPDFIntervalSet_add(ret, lower, lower); + else + _cfPDFToPDFIntervalSet_add(ret, lower, upper+1); } - - if (*range != ',') break; - range++; + } + else + { + _cfPDFToPDFIntervalSet_add(ret, lower, lower+1); + } } - ret->finish(); + if (*range != ',') + break; + range++; + } + _cfPDFToPDFIntervalSet_finish(ret); } +// }}} -static bool _cfPDFToPDFParseBorder(const char *val, pdftopdf_border_type_e *ret) { - if (strcasecmp(val, "none") == 0) *ret = NONE; - else if (strcasecmp(val, "single") == 0) *ret = ONE_THIN; - else if (strcasecmp(val, "single-thick") == 0) *ret = ONE_THICK; - else if (strcasecmp(val, "double") == 0) *ret = TWO_THIN; - else if (strcasecmp(val, "double-thick") == 0) *ret = TWO_THICK; - else return false; - return true; +static bool +_cfPDFToPDFParseBorder(const char *val, + pdftopdf_border_type_e *ret) // {{{ +{ + if (strcasecmp(val, "none") == 0) + *ret = NONE; + else if (strcasecmp(val, "single") == 0) + *ret = ONE_THIN; + else if (strcasecmp(val, "single-thick") == 0) + *ret = ONE_THICK; + else if (strcasecmp(val, "double") == 0) + *ret = TWO_THIN; + else if (strcasecmp(val, "double-thick") == 0) + *ret = TWO_THICK; + else + return false; + return true; } +// }}} + +void +getParameters(cf_filter_data_t *data, + int num_options, + cups_option_t *options, + _cfPDFToPDFProcessingParameters *param, + pdftopdf_doc_t *doc) // {{{ +{ + char *final_content_type = data->final_content_type; + ipp_t *printer_attrs = data->printer_attrs; + ipp_t *job_attrs = data->job_attrs; + ipp_attribute_t *attr; + const char *val; + int ipprot; + int nup; + char rawlabel[256]; + char *classification; + char cookedlabel[256]; + + if ((val = cupsGetOption("copies", num_options, options)) != NULL || + (val = cupsGetOption("Copies", num_options, options)) != NULL || + (val = cupsGetOption("num-copies", num_options, options)) != NULL || + (val = cupsGetOption("NumCopies", num_options, options)) != NULL) + { + int copies = atoi(val); + if (copies > 0) + param->num_copies = copies; + } -void getParameters(cf_filter_data_t *data, int num_options, cups_option_t *options, _cfPDFToPDFProcessingParameters *param, pdftopdf_doc_t *doc) { - char *final_content_type = data->final_content_type; - ipp_t *printer_attrs = data->printer_attrs; - ipp_t *job_attrs = data->job_attrs; - ipp_attribute_t *attr; - const char *val; - int ipprot; - int nup; - char *classification; - char rawlabel[256]; - char cookedlabel[256]; - - if ((val = cupsGetOption("copies", num_options, options)) != NULL || - (val = cupsGetOption("Copies", num_options, options)) != NULL || - (val = cupsGetOption("num-copies", num_options, options)) != NULL || - (val = cupsGetOption("NumCopies", num_options, options)) != NULL) { - int copies = atoi(val); - if (copies > 0) param->num_copies = copies; + if (param->num_copies == 0) + param->num_copies = 1; + + if (printer_attrs != NULL && + (attr = ippFindAttribute(printer_attrs, + "landscape-orientation-requested-preferred", + IPP_TAG_ZERO)) != NULL && + ippGetInteger(attr, 0) == 5) + param->normal_landscape = ROT_270; + else + param->normal_landscape = ROT_90; + + param->orientation = ROT_0; + param->no_orientation = false; + if (optGetInt("orientation-requested", num_options, options, &ipprot)) + { + // IPP orientation values are: + // 3: 0 degrees, 4: 90 degrees, 5: -90 degrees, 6: 180 degrees + + if ((ipprot < 3) || (ipprot > 6)) + { + if (ipprot && doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Bad value (%d) for " + "orientation-requested, using 0 degrees", + ipprot); + param->no_orientation = true; + } + else + { + static const pdftopdf_rotation_e + ipp2rot[4] = {ROT_0, ROT_90, ROT_270, ROT_180}; + param->orientation = ipp2rot[ipprot - 3]; } + } + else if ((val = cupsGetOption("landscape", num_options, options)) != NULL) + { + if (!is_false(val)) + param->orientation = param->normal_landscape; + } + else + param->no_orientation = true; + + param->pagesize_requested = + (cfGetPageDimensions(printer_attrs, job_attrs, num_options, options, NULL, + 0, + &(param->page.width), &(param->page.height), + &(param->page.left), &(param->page.bottom), + &(param->page.right), &(param->page.top), + NULL, NULL) >= 1); + + cfSetPageDimensionsToDefault(&(param->page.width), &(param->page.height), + &(param->page.left), &(param->page.bottom), + &(param->page.right), &(param->page.top), + doc->logfunc, doc->logdata); + + param->page.right = param->page.width - param->page.right; + param->page.top = param->page.height - param->page.top; + + param->paper_is_landscape = (param->page.width > param->page.height); + + _cfPDFToPDFPageRect tmp; // borders (before rotation) + + optGetFloat("page-top", num_options, options, &tmp.top); + optGetFloat("page-left", num_options, options, &tmp.left); + optGetFloat("page-right", num_options, options, &tmp.right); + optGetFloat("page-bottom", num_options, options, &tmp.bottom); + + if ((val = cupsGetOption("media-top-margin", num_options, options)) + != NULL) + tmp.top = atof(val) * 72.0 / 2540.0; + if ((val = cupsGetOption("media-left-margin", num_options, options)) + != NULL) + tmp.left = atof(val) * 72.0 / 2540.0; + if ((val = cupsGetOption("media-right-margin", num_options, options)) + != NULL) + tmp.right = atof(val) * 72.0 / 2540.0; + if ((val = cupsGetOption("media-bottom-margin", num_options, options)) + != NULL) + tmp.bottom = atof(val) * 72.0 / 2540.0; + + if ((param->orientation == ROT_90) || (param->orientation == ROT_270)) + { // unrotate page + // NaN stays NaN + tmp.right = param->page.height - tmp.right; + tmp.top = param->page.width - tmp.top; + _cfPDFToPDFPageRect_rotate_move(&tmp, param->orientation, param->page.height, param->page.width); + } + else + { + tmp.right = param->page.width - tmp.right; + tmp.top = param->page.height - tmp.top; + _cfPDFToPDFPageRect_rotate_move(&tmp, param->orientation, param->page.width, param->page.height); + } + _cfPDFToPDFPageRect_set(¶m->page, &tmp); - if (param->num_copies == 0) param->num_copies = 1; - - if (printer_attrs != NULL && - (attr = ippFindAttribute(printer_attrs, "landscape-orientation-requested-preferred", IPP_TAG_ZERO)) != NULL && - ippGetInteger(attr, 0) == 5) param->normal_landscape = ROT_270; - else param->normal_landscape = ROT_90; - - param->orientation = ROT_0; - param->no_orientation = false; - if (optGetInt("orientation-requested", num_options, options, &ipprot)) { - if ((ipprot < 3) || (ipprot > 6)) { - if (ipprot && doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Bad value (%d) for orientation-requested, using 0 degrees", ipprot); - param->no_orientation = true; - } else { - static const pdftopdf_rotation_e ipp2rot[4] = {ROT_0, ROT_90, ROT_270, ROT_180}; - param->orientation = ipp2rot[ipprot - 3]; - } - } else if ((val = cupsGetOption("landscape", num_options, options)) != NULL) { - if (!is_false(val)) param->orientation = param->normal_landscape; - } else param->no_orientation = true; - - param->pagesize_requested = (cfGetPageDimensions(printer_attrs, job_attrs, num_options, options, NULL, 0, - &(param->page.width), &(param->page.height), &(param->page.left), &(param->page.bottom), &(param->page.right), &(param->page.top), NULL, NULL) >= 1); - - cfSetPageDimensionsToDefault(&(param->page.width), &(param->page.height), &(param->page.left), &(param->page.bottom), &(param->page.right), &(param->page.top), doc->logfunc, doc->logdata); - - param->page.right = param->page.width - param->page.right; - param->page.top = param->page.height - param->page.top; - - param->paper_is_landscape = (param->page.width > param->page.height); - - _cfPDFToPDFPageRect tmp; // borders (before rotation) - - optGetFloat("page-top", num_options, options, &tmp.top); - optGetFloat("page-left", num_options, options, &tmp.left); - optGetFloat("page-right", num_options, options, &tmp.right); - optGetFloat("page-bottom", num_options, options, &tmp.bottom); - - if ((val = cupsGetOption("media-top-margin", num_options, options)) != NULL) tmp.top = atof(val) * 72.0 / 2540.0; - if ((val = cupsGetOption("media-left-margin", num_options, options)) != NULL) tmp.left = atof(val) * 72.0 / 2540.0; - if ((val = cupsGetOption("media-right-margin", num_options, options)) != NULL) tmp.right = atof(val) * 72.0 / 2540.0; - if ((val = cupsGetOption("media-bottom-margin", num_options, options)) != NULL) tmp.bottom = atof(val) * 72.0 / 2540.0; - - if ((param->orientation == ROT_90) || (param->orientation == ROT_270)) { - tmp.right = param->page.height - tmp.right; - tmp.top = param->page.width - tmp.top; - rotate_move(&tmp, param->orientation, param->page.height, param->page.width); - } else { - tmp.right = param->page.width - tmp.right; - tmp.top = param->page.height - tmp.top; - rotate_move(&tmp, param->orientation, param->page.width, param->page.height); + if ((val = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, "sides")) != + NULL && + strncmp(val, "two-sided-", 10) == 0) + param->duplex = true; + else if (is_true(cupsGetOption("Duplex", num_options, options))) + { + param->duplex = true; + param->set_duplex = true; + } + else if ((val = cupsGetOption("sides", num_options, options)) != NULL) + { + if ((strcasecmp(val, "two-sided-long-edge") == 0) || + (strcasecmp(val, "two-sided-short-edge") == 0)) + { + param->duplex = true; + param->set_duplex = true; + } + else if (strcasecmp(val, "one-sided") != 0) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unsupported sides value %s, using sides=one-sided!", + val); } + } - set_page_rect(&(param->page), &tmp); - - if ((val = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, "sides")) != NULL && strncmp(val, "two-sided-", 10) == 0) param->duplex = true; - else if (is_true(cupsGetOption("Duplex", num_options, options))) { - param->duplex = true; - param->set_duplex = true; - } else if ((val = cupsGetOption("sides", num_options, options)) != NULL) { - if ((strcasecmp(val, "two-sided-long-edge") == 0) || (strcasecmp(val, "two-sided-short-edge") == 0)) { - param->duplex = true; - param->set_duplex = true; - } else if (strcasecmp(val, "one-sided") != 0) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported sides value %s, using sides=one-sided!", val); - } + // default nup is 1 + nup = 1; + if (optGetInt("number-up", num_options, options, &nup)) + { + if (!_cfPDFToPDFNupParameters_possible(nup)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unsupported number-up value %d, using number-up=1!", + nup); + nup = 1; } + _cfPDFToPDFNupParameters_preset(nup, &(param->nup)); + } - nup = 1; - if (optGetInt("number-up", num_options, options, &nup)) { - if (!_cfPDFToPDFNupParameters_possible(nup)) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported number-up value %d, using number-up=1!", nup); - nup = 1; - } - _cfPDFToPDFNupParameters_preset(nup, &(param->nup)); + if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL) + { + if (!_cfPDFToPDFParseNupLayout(val, &(param->nup))) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unsupported number-up-layout %s, using number-up-layout=lrtb!", + val); + param->nup.first = X; + param->nup.xstart = LEFT; + param->nup.ystart = TOP; } + } - if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL) { - if (!_cfPDFToPDFParseNupLayout(val, &(param->nup))) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported number-up-layout %s, using number-up-layout=lrtb!", val); - param->nup.first = X_AXIS; - param->nup.xstart = LEFT; - param->nup.ystart = TOP; - } + if ((val = cupsGetOption("page-border", num_options, options)) != NULL) + { + if (!_cfPDFToPDFParseBorder(val, &(param->border))) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unsupported page-border value %s, using page-border=none!", + val); + param->border = NONE; } + } - if ((val = cupsGetOption("page-border", num_options, options)) != NULL) { - if (!_cfPDFToPDFParseBorder(val, &(param->border))) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported page-border value %s, using page-border=none!", val); - param->border = NONE; - } - } + if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL || + (val = cupsGetOption("output-order", num_options, options)) != NULL || + (val = cupsGetOption("page-delivery", num_options, options)) != NULL) + { + param->reverse = (strcasecmp(val, "Reverse") == 0 || + strcasecmp(val, "reverse-order") == 0); + } + else + { + param->reverse = cfIPPReverseOutput(printer_attrs, job_attrs); + } - if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL || - (val = cupsGetOption("output-order", num_options, options)) != NULL || - (val = cupsGetOption("page-delivery", num_options, options)) != NULL) { - param->reverse = (strcasecmp(val, "Reverse") == 0 || strcasecmp(val, "reverse-order") == 0); - } else { - param->reverse = cfIPPReverseOutput(printer_attrs, job_attrs); - } + classification = getenv("CLASSIFICATION"); + if (classification) + strcpy(rawlabel, classification); - classification = getenv("CLASSIFICATION"); - if (classification) strcpy(rawlabel, classification); + if ((val = cupsGetOption("page-label", num_options, options)) != NULL) + { + if (strlen(rawlabel) > 0) strcat(rawlabel, " - "); + strcat(rawlabel, cupsGetOption("page-label", num_options, options)); + } - if ((val = cupsGetOption("page-label", num_options, options)) != NULL) { - if (strlen(rawlabel) > 0) strcat(rawlabel, " - "); - strcat(rawlabel, cupsGetOption("page-label", num_options, options)); + char *rawptr = rawlabel; + char *cookedptr = cookedlabel; + while (*rawptr) + { + if (*rawptr < 32 || *rawptr > 126) + { + sprintf(cookedptr, "\\%03o", (unsigned int)*rawptr); + cookedptr += 4; + } + else + { + *cookedptr++ = *rawptr; } + rawptr++; + } + *cookedptr = '\0'; + param->page_label = strdup(cookedlabel); - char *rawptr = rawlabel; - char *cookedptr = cookedlabel; - while (*rawptr) { - if (*rawptr < 32 || *rawptr > 126) { - sprintf(cookedptr, "\\%03o", (unsigned int)*rawptr); - cookedptr += 4; - } else { - *cookedptr++ = *rawptr; - } - rawptr++; - } - *cookedptr = '\0'; - param->page_label = strdup(cookedlabel); - - if ((val = cupsGetOption("page-set", num_options, options)) != NULL) { - if (strcasecmp(val, "even") == 0) param->odd_pages = false; - else if (strcasecmp(val, "odd") == 0) param->even_pages = false; - else if (strcasecmp(val, "all") != 0) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported page-set value %s, using page-set=all!", val); - } + if ((val = cupsGetOption("page-set", num_options, options)) != NULL) + { + if (strcasecmp(val, "even") == 0) + param->odd_pages = false; + else if (strcasecmp(val, "odd") == 0) + param->even_pages = false; + else if (strcasecmp(val, "all") != 0) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unsupported page-set value %s, using page-set=all!", + val); } + } - if ((val = cupsGetOption("page-ranges", num_options, options)) != NULL) parseRanges(val, &(param->page_ranges)); - if ((val = cupsGetOption("input-page-ranges", num_options, options)) != NULL) parseRanges(val, &(param->input_page_ranges)); + if ((val = cupsGetOption("page-ranges", num_options, options)) != NULL) + parseRanges(val, param->page_ranges); + if ((val = cupsGetOption("input-page-ranges", num_options, options)) != NULL) + parseRanges(val, param->input_page_ranges); - if ((val = cupsGetOption("mirror", num_options, options)) != NULL || - (val = cupsGetOption("mirror-print", num_options, options)) != NULL) param->mirror = is_true(val); + if ((val = cupsGetOption("mirror", num_options, options)) != NULL || + (val = cupsGetOption("mirror-print", num_options, options)) != NULL) + param->mirror = is_true(val); - param->booklet = CF_PDFTOPDF_BOOKLET_OFF; - if ((val = cupsGetOption("booklet", num_options, options)) != NULL) { - if (strcasecmp(val, "shuffle-only") == 0) param->booklet = CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE; - else if (is_true(val)) param->booklet = CF_PDFTOPDF_BOOKLET_ON; - else if (!is_false(val)) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported booklet value %s, using booklet=off!", val); - } + param->booklet = CF_PDFTOPDF_BOOKLET_OFF; + if ((val = cupsGetOption("booklet", num_options, options)) != NULL) + { + if (strcasecmp(val, "shuffle-only") == 0) + param->booklet = CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE; + else if (is_true(val)) + param->booklet = CF_PDFTOPDF_BOOKLET_ON; + else if (!is_false(val)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unsupported booklet value %s, using booklet=off!", + val); } - param->book_signature = -1; - if (optGetInt("booklet-signature", num_options, options, &(param->book_signature))) { - if (param->book_signature == 0) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported booklet-signature value, using booklet-signature=-1 (all)!", val); - param->book_signature = -1; - } + } + param->book_signature = -1; + if (optGetInt("booklet-signature", num_options, options, &(param->book_signature))) + { + if (param->book_signature == 0) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unsupported booklet-signature value, using booklet-signature=-1 (all)!", + val); + param->book_signature = -1; } + } - if ((val = cupsGetOption("position", num_options, options)) != NULL) { - if (!parsePosition(val, &(param->xpos), &(param->ypos))) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unrecognized position value %s, using position=center!", val); - param->xpos = CENTER; - param->ypos = CENTER; - } + if ((val = cupsGetOption("position", num_options, options)) != NULL) + { + if (!parsePosition(val, &(param->xpos), &(param->ypos))) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unrecognized position value %s, using position=center!", + val); + param->xpos = CENTER; + param->ypos = CENTER; } + } - if (is_true(cupsGetOption("Collate", num_options, options))) param->collate = true; - else if ((val = cupsGetOption("sheet-collate", num_options, options)) != NULL) param->collate = (strcasecmp(val, "uncollated") != 0); - else if (((val = cupsGetOption("multiple-document-handling", num_options, options)) != NULL && - (strcasecmp(val, "separate-documents-collated-copies") == 0 || - strcasecmp(val, "separate-documents-uncollated-copies") == 0 || - strcasecmp(val, "single-document") == 0 || - strcasecmp(val, "single-document-new-sheet") == 0)) || - (val = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, "multiple-document-handling")) != NULL) { - param->collate = (strcasecmp(val, "separate-documents-uncollated-copies") != 0); - } + if (is_true(cupsGetOption("Collate", num_options, options))) + param->collate = true; + else if ((val = cupsGetOption("sheet-collate", num_options, options)) != NULL) + param->collate = (strcasecmp(val, "uncollated") != 0); + else if (((val = cupsGetOption("multiple-document-handling", + num_options, options)) != NULL && + (strcasecmp(val, "separate-documents-collated-copies") == 0 || + strcasecmp(val, "separate-documents-uncollated-copies") == 0 || + strcasecmp(val, "single-document") == 0 || + strcasecmp(val, "single-document-new-sheet") == 0)) || + (val = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, + "multiple-document-handling")) != + NULL) + { + param->collate = + (strcasecmp(val, "separate-documents-uncollated-copies") != 0); + } - param->even_duplex = (param->duplex && is_true(cupsGetOption("even-duplex", num_options, options))); +#if 0 + if ((val = cupsGetOption("scaling", num_options, options)) != 0) + { + scalint = atoi(val) * 0.01; + fitplot = true + } + else if (fitplot) + scaling = 1.0; - param->auto_rotate = param->no_orientation; - if ((val = cupsGetOption("pdftopdfAutoRotate", num_options, options)) != NULL || (val = cupsGetOption("pdfAutoRotate", num_options, options)) != NULL) - param->auto_rotate = !is_false(val); + if ((val = cupsGetOption("natural-scaling", num_options, options)) != 0) + naturalScaling = atoi(val) * 0.01; +#endif + + // Make pages a multiple of two (only considered when duplex is on). + // i.e. printer has hardware-duplex, but needs pre-inserted filler pages + // FIXME? pdftopdf also supports it as cmdline option (via checkFeature()) + param->even_duplex = + (param->duplex && + is_true(cupsGetOption("even-duplex", num_options, options))); + + param->auto_rotate = param->no_orientation; + if ((val = cupsGetOption("pdftopdfAutoRotate", + num_options, options)) != NULL || + (val = cupsGetOption("pdfAutoRotate", num_options, options)) != NULL) + param->auto_rotate = !is_false(val); + + if ((val = cupsGetOption("ipp-attribute-fidelity", num_options, options)) != + NULL) + { + if (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || + !strcasecmp(val, "on")) + param->fidelity = true; + } - if ((val = cupsGetOption("ipp-attribute-fidelity", num_options, options)) != NULL) { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || !strcasecmp(val, "on")) - param->fidelity = true; - } + if (printer_attrs == NULL && !param->pagesize_requested && + param->booklet == CF_PDFTOPDF_BOOKLET_OFF && + param->nup.nupX == 1 && param->nup.nupY == 1) + param->cropfit = true; - if (printer_attrs == NULL && !param->pagesize_requested && param->booklet == CF_PDFTOPDF_BOOKLET_OFF && param->nup.nupX == 1 && param->nup.nupY == 1) - param->cropfit = true; - else if ((val = cupsGetOption("print-scaling", num_options, options)) != NULL) { - if (!strcasecmp(val, "auto")) param->autoprint = true; - else if (!strcasecmp(val, "auto-fit")) param->autofit = true; - else if (!strcasecmp(val, "fill")) param->fillprint = true; - else if (!strcasecmp(val, "fit")) param->fitplot = true; - else if (!strcasecmp(val, "none")) param->cropfit = true; - else param->autoprint = true; - } else { - if ((val = cupsGetOption("fitplot", num_options, options)) == NULL) { - if ((val = cupsGetOption("fit-to-page", num_options, options)) == NULL) - val = cupsGetOption("ipp-attribute-fidelity", num_options, options); - } - param->fitplot = (val && !is_false(val)); - if ((val = cupsGetOption("fill", num_options, options)) != NULL) { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - param->fillprint = true; - } - if ((val = cupsGetOption("crop-to-fit", num_options, options)) != NULL) { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - param->cropfit = 1; - } - if (!param->autoprint && !param->autofit && !param->fitplot && !param->fillprint && !param->cropfit) - param->autoprint = true; + else if ((val = cupsGetOption("print-scaling", num_options, options)) != NULL) + { + // Standard IPP attribute + if (!strcasecmp(val, "auto")) + param->autoprint = true; + else if (!strcasecmp(val, "auto-fit")) + param->autofit = true; + else if (!strcasecmp(val, "fill")) + param->fillprint = true; + else if (!strcasecmp(val, "fit")) + param->fitplot = true; + else if (!strcasecmp(val, "none")) + param->cropfit = true; + else + param->autoprint = true; + } + else + { + // Legacy CUPS attributes + if ((val = cupsGetOption("fitplot", num_options, options)) == NULL) + { + if ((val = cupsGetOption("fit-to-page", num_options, options)) == NULL) + val = cupsGetOption("ipp-attribute-fidelity", num_options, options); } - - if (param->fitplot || param->fillprint || param->autoprint || param->autofit || param->booklet != CF_PDFTOPDF_BOOKLET_OFF || param->nup.nupX > 1 || param->nup.nupY > 1) - param->pagesize_requested = true; - - if ((val = cupsGetOption("pdf-filter-page-logging", num_options, options)) != NULL) { - if (strcasecmp(val, "auto") == 0) { - param->page_logging = -1; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Automatic page logging selected by options."); - } else if (is_true(val)) { - param->page_logging = 1; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Forced page logging selected by options."); - } else if (is_false(val)) { - param->page_logging = 0; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Suppressed page logging selected by options."); - } else { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported page logging setting \"pdf-filter-page-logging=%s\", using \"auto\"!", val); - param->page_logging = -1; - } + // TODO? pstops checks == "true", pdftops !is_false ... pstops says: + // fitplot only for PS (i.e. not for PDF, cmp. cgpdftopdf) + param->fitplot = (val && !is_false(val)); + + if ((val = cupsGetOption("fill", num_options, options)) != 0) + { + if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) + param->fillprint = true; } + if ((val = cupsGetOption("crop-to-fit", num_options, options)) != NULL) + { + if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) + param->cropfit = 1; + } + if (!param->autoprint && !param->autofit && !param->fitplot && + !param->fillprint && !param->cropfit) + param->autoprint = true; + } - if (param->page_logging == -1) { - if (final_content_type && (strcasestr(final_content_type, "/pdf") || strcasestr(final_content_type, "/vnd.cups-pdf") || strcasestr(final_content_type, "/pwg-raster"))) - param->page_logging = 1; - else param->page_logging = 0; - - if (!final_content_type || final_content_type[0] == '\0') - param->page_logging = -1; - - if (doc->logfunc) { - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Determined whether to log pages or not using output data type."); - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "final_content_type = %s => page_logging = %d", final_content_type ? final_content_type : "NULL", param->page_logging); - } + // Certain features require a given page size for the page to be + // printed or all pages of the document being the same size. Here we + // set param.pagesize_requested so that the default page size is used + // when no size got specified by the user. + if (param->fitplot || param->fillprint || param->autoprint || param->autofit || + param->booklet != CF_PDFTOPDF_BOOKLET_OFF || + param->nup.nupX > 1 || param->nup.nupY > 1) + param->pagesize_requested = true; + + // + // Do we have to do the page logging in page_log? + // + // CUPS standard is that the last filter (not the backend, usually the + // printer driver) does page logging in the /var/log/cups/page_log file + // by outputting "PAGE: <# of current page> <# of copies>" to stderr. + // + // cfFilterPDFToPDF() would have to do this only for PDF printers as + // in this case cfFilterPDFToPDF() is the last filter, but some of + // the other filters are not able to do the logging because they do + // not have access to the number of pages of the file to be printed, + // so cfFilterPDFToPDF() overtakes their logging duty. + // + + // Check whether page logging is forced or suppressed by the options + + if ((val = cupsGetOption("pdf-filter-page-logging", + num_options, options)) != NULL) + { + if (strcasecmp(val, "auto") == 0) + { + param->page_logging = -1; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Automatic page logging selected by options."); + } + else if (is_true(val)) + { + param->page_logging = 1; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Forced page logging selected by options."); + } + else if (is_false(val)) + { + param->page_logging = 0; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Suppressed page logging selected by options."); + } + else + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unsupported page logging setting \"pdf-filter-page-logging=%s\", using \"auto\"!", + val); + param->page_logging = -1; + } + } - if (param->page_logging == -1) param->page_logging = 0; + if (param->page_logging == -1) + { + // We determine whether to log pages or not + // using the output data MIME type. log pages only when the output is + // either pdf or PWG Raster + if (final_content_type && + (strcasestr(final_content_type, "/pdf") || + strcasestr(final_content_type, "/vnd.cups-pdf") || + strcasestr(final_content_type, "/pwg-raster"))) + param->page_logging = 1; + else + param->page_logging = 0; + + // If final_content_type is not clearly available we are not sure whether + // to log pages or not + if (!final_content_type || + final_content_type[0] == '\0') + param->page_logging = -1; + + if (doc->logfunc) + { + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Determined whether to log pages or not using output data type."); + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "final_content_type = %s => page_logging = %d", + final_content_type ? final_content_type : "NULL", + param->page_logging); } -} -void calculate(int num_options, cups_option_t *options, _cfPDFToPDFProcessingParameters *param, char *final_content_type) { - const char *val; - bool hw_copies = false, hw_collate = false; + if (param->page_logging == -1) + param->page_logging = 0; + } +} - if ((val = cupsGetOption("hardware-copies", num_options, options)) != NULL) - hw_copies = is_true(val); +void +calculate(int num_options, + cups_option_t *options, + _cfPDFToPDFProcessingParameters *param, + char *final_content_type) +{ + const char *val; + bool hw_copies = false, + hw_collate = false; + + // Check options for caller's instructions about hardware copies/collate + if ((val = cupsGetOption("hardware-copies", + num_options, options)) != NULL) + // Use hardware copies according to the caller's instructions + hw_copies = is_true(val); + else + // Caller did not tell us whether the printer does Hardware copies + // or not, so we assume hardware copies on PDF printers, and software + // copies on other (usually raster) printers or if we do not know the + // final output format. + hw_copies = (final_content_type && + (strcasestr(final_content_type, "/pdf") || + strcasestr(final_content_type, "/vnd.cups-pdf"))); + + if (hw_copies) + { + if ((val = cupsGetOption("hardware-collate", + num_options, options)) != NULL) + // Use hardware collate according to the caller's instructions + hw_collate = is_true(val); else - hw_copies = (final_content_type && (strcasestr(final_content_type, "/pdf") || strcasestr(final_content_type, "/vnd.cups-pdf"))); + hw_collate = (final_content_type && + (strcasestr(final_content_type, "/pdf") || + strcasestr(final_content_type, "/vnd.cups-pdf") || + strcasestr(final_content_type, "/pwg-raster") || + strcasestr(final_content_type, "/urf") || + strcasestr(final_content_type, "/PCLm"))); + } + + if (param->reverse && param->duplex) + // Enable even_duplex or the first page may be empty. + param->even_duplex = true; // disabled later, if non-duplex - if (hw_copies) { - if ((val = cupsGetOption("hardware-collate", num_options, options)) != NULL) - hw_collate = is_true(val); - else - hw_collate = (final_content_type && (strcasestr(final_content_type, "/pdf") || strcasestr(final_content_type, "/vnd.cups-pdf") || strcasestr(final_content_type, "/pwg-raster") || strcasestr(final_content_type, "/urf") || strcasestr(final_content_type, "/PCLm"))); + if (param->num_copies == 1) + { + param->device_copies = 1; + // collate is never needed for a single copy + param->collate = false; // (does not make a big difference for us) + } + else if (hw_copies) + { // hw copy generation available + param->device_copies = param->num_copies; + if (param->collate) + { + param->device_collate = hw_collate; + if (!param->device_collate) + // printer can't hw collate -> we must copy collated in sw + param->device_copies = 1; + } // else: printer copies w/o collate and takes care of duplex/even_duplex + } + else + { // sw copies + param->device_copies = 1; + if (param->duplex) + { // &&(num_copies>1) + // sw collate + even_duplex must be forced to prevent copies on the + // back sides + param->collate = true; + param->device_collate = false; } + } - if (param->reverse && param->duplex) param->even_duplex = true; + if (param->device_copies != 1) + param->num_copies = 1; - if (param->num_copies == 1) { - param->device_copies = 1; - param->collate = false; - } else if (hw_copies) { - param->device_copies = param->num_copies; - if (param->collate) { - param->device_collate = hw_collate; - if (!param->device_collate) param->device_copies = 1; - } - } else { - param->device_copies = 1; - if (param->duplex) { - param->collate = true; - param->device_collate = false; - } - } + if (param->duplex && + param->collate && !param->device_collate) + param->even_duplex = true; - if (param->device_copies != 1) param->num_copies = 1; - if (param->duplex && param->collate && !param->device_collate) param->even_duplex = true; - if (!param->duplex) param->even_duplex = false; + if (!param->duplex) + param->even_duplex = false; } -FILE *copy_fd_to_temp(int infd, pdftopdf_doc_t *doc) { - char buf[BUFSIZ]; - int n; +// reads from stdin into temporary file. returns FILE * or NULL on error +FILE * +copy_fd_to_temp(int infd, + pdftopdf_doc_t *doc) +{ + char buf[BUFSIZ]; + int n; - int outfd = cupsCreateTempFd(NULL, NULL, buf, sizeof(buf)); - if (outfd < 0) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Can't create temporary file"); - return NULL; - } + int outfd = cupsCreateTempFd(NULL, NULL, buf, sizeof(buf)); + if (outfd < 0) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Can't create temporary file"); + return NULL; + } - unlink(buf); + // remove name + unlink(buf); - while ((n = read(infd, buf, BUFSIZ)) > 0) { - if (write(outfd, buf, n) != n) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Can't copy stdin to temporary file"); - close(outfd); - return NULL; - } - } - if (lseek(outfd, 0, SEEK_SET) < 0) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Can't rewind temporary file"); - close(outfd); - return NULL; + // copy stdin to the tmp file + while ((n = read(infd, buf, BUFSIZ)) > 0) + { + if (write(outfd, buf, n) != n) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Can't copy stdin to temporary file"); + close(outfd); + return NULL; } + } + + if (lseek(outfd, 0, SEEK_SET) < 0) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Can't rewind temporary file"); + close(outfd); + return NULL; + } - FILE *f; - if ((f = fdopen(outfd, "rb")) == 0) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Can't fdopen temporary file"); - close(outfd); - return NULL; - } - return f; + FILE *f; + if ((f = fdopen(outfd, "rb")) == 0) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Can't fdopen temporary file"); + close(outfd); + return NULL; + } + return f; } -bool is_empty(FILE *f) { - char buf[1]; - if (fread(buf, 1, 1, f) == 0) return true; - rewind(f); - return false; +// check whether a given file is empty +bool +is_empty(FILE *f) +{ + char buf[1]; + if (fread(buf, 1, 1, f) == 0) + return true; + rewind(f); + return false; } -int cfFilterPDFToPDF(int inputfd, int outputfd, int inputseekable, cf_filter_data_t *data, void *parameters) { - pdftopdf_doc_t doc; - char *final_content_type = data->final_content_type; - FILE *inputfp, *outputfp; - const char *t; - int streaming = 0; - size_t bytes; - char buf[BUFSIZ]; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - int num_options = 0; - cups_option_t *options = NULL; - - _cfPDFToPDFProcessingParameters param; - - param.job_id = data->job_id; - param.user = data->job_user; - param.title = data->job_title; - param.num_copies = data->copies; - param.copies_to_be_logged = data->copies; - param.page.width = param.page.height = 0; - param.page.left = param.page.bottom = -1; - param.page.right = param.page.top = -1; - - doc.logfunc = log; - doc.logdata = ld; - doc.iscanceledfunc = iscanceled; - doc.iscanceleddata = icd; - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - getParameters(data, num_options, options, ¶m, &doc); - calculate(num_options, options, ¶m, final_content_type); +int +cfFilterPDFToPDF(int inputfd, + int outputfd, + int inputseekable, + cf_filter_data_t *data, + void *parameters) +{ + pdftopdf_doc_t doc; + char *final_content_type = data->final_content_type; + FILE *inputfp, + *outputfp; + const char *t; + int streaming = 0; + size_t bytes; + char buf[BUFSIZ]; + cf_logfunc_t log = data->logfunc; + void *ld = data->logdata; + cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; + void *icd = data->iscanceleddata; + int num_options = 0; + cups_option_t *options = NULL; + + _cfPDFToPDFProcessingParameters param; + + param.job_id = data->job_id; + param.user = data->job_user; + param.title = data->job_title; + param.num_copies = data->copies; + param.copies_to_be_logged = data->copies; + param.page.width = param.page.height = 0; + param.page.left = param.page.bottom = -1; + param.page.right = param.page.top = -1; + + doc.logfunc = log; + doc.logdata = ld; + doc.iscanceledfunc = iscanceled; + doc.iscanceleddata = icd; + + num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); + + getParameters(data, num_options, options, ¶m, &doc); + calculate(num_options, options, ¶m, final_content_type); #ifdef DEBUG - param.dump(&doc); + _cfPDFToPDFProcessingParameters_dump(¶m, &doc); #endif - if ((t = cupsGetOption("filter-streaming-mode", num_options, options)) != NULL && (strcasecmp(t, "false") && strcasecmp(t, "off") && strcasecmp(t, "no"))) { - streaming = 1; - if (log) log(ld, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Streaming mode: No PDF processing, only adding of JCL"); - } + // If we are in streaming mode we only apply JCL and do not run the + // job through QPDL (so no page management, form flattening, + // page size/orientation adjustment, ...) + if ((t = cupsGetOption("filter-streaming-mode", + num_options, options)) != NULL && + (strcasecmp(t, "false") && strcasecmp(t, "off") && + strcasecmp(t, "no"))) + { + streaming = 1; + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Streaming mode: No PDF processing, only adding of JCL"); + } - cupsFreeOptions(num_options, options); + cupsFreeOptions(num_options, options); - if ((inputseekable && inputfd > 0) || streaming) { - if ((inputfp = fdopen(inputfd, "rb")) == NULL) - return 1; - } else { - if ((inputfp = copy_fd_to_temp(inputfd, &doc)) == NULL) - return 1; - } + _cfPDFToPDF_PDFioProcessor proc; - if (!streaming) { - if (is_empty(inputfp)) { - fclose(inputfp); - if (log) log(ld, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Input is empty, outputting empty file."); - return 0; - } + if ((inputseekable && inputfd > 0) || streaming) + { + if ((inputfp = fdopen(inputfd, "rb")) == NULL) + return 1; + } + else + { + if ((inputfp = copy_fd_to_temp(inputfd, &doc)) == NULL) + return 1; + } - if (log) log(ld, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Processing PDF input with QPDF: Page-ranges, page-set, number-up, booklet, size adjustment, ..."); + if (!streaming) + { + if (is_empty(inputfp)) + { + fclose(inputfp); + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Input is empty, outputting empty file."); + return 0; + } - // Load the PDF input data into QPDF - if (!proc_load_file(inputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE, 1)) { - fclose(inputfp); - return 1; - } + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Processing PDF input with QPDF: Page-ranges, page-set, number-up, booklet, size adjustment, ..."); - // Process the PDF input data - if (!_cfProcessPDFToPDF(¶m, &doc)) - return 2; + // Load the PDF input data into QPDF + if (!_cfPDFToPDF_PDFioProcessor_load_file(&proc, inputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE, 1)) + { + fclose(inputfp); + return 1; + } - // Pass information to subsequent filters via PDF comments - char *output[10]; - int output_len = 0; + // Process the PDF input data + if (!_cfProcessPDFToPDF(&proc, ¶m, &doc)) + return 2; - output[output_len++] = "% This file was generated by pdftopdf"; + // Pass information to subsequent filters via PDF comments + char *output[10]; + int output_len = 0; - if (param.device_copies > 0) { - char buf[256]; - snprintf(buf, sizeof(buf), "%d", param.device_copies); - output[output_len++] = strdup(buf); + output[output_len++] = "% This file was generated by pdftopdf"; - if (param.device_collate) - output[output_len++] = "%%PDFTOPDFCollate : true"; - else - output[output_len++] = "%%PDFTOPDFCollate : false"; - } + if (param.device_copies > 0) + { + char buf[256]; + snprintf(buf, sizeof(buf), "%d", param.device_copies); + output[output_len++] = strdup(buf); - proc_set_comments(output, output_len); + if (param.device_collate) + output[output_len++] = "%%PDFTOPDFCollate : true"; + else + output[output_len++] = "%%PDFTOPDFCollate : false"; } - outputfp = fdopen(outputfd, "w"); - if (outputfp == NULL) - return 1; - - if (!streaming) { - proc_emit_file(outputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE); - } else { - if (log) log(ld, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Passing on unchanged PDF data from input"); - while ((bytes = fread(buf, 1, sizeof(buf), inputfp)) > 0) - if (fwrite(buf, 1, bytes, outputfp) != bytes) - break; - fclose(inputfp); - } + _cfPDFToPDF_PDFioProcessor_set_comments(&proc, output, output_len); + } - fclose(outputfp); - return 0; -} + outputfp = fdopen(outputfd, "w"); + if (outputfp == NULL) + return 1; + + if (!streaming) + { + _cfPDFToPDF_PDFioProcessor_emit_file(&proc, outputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE); + } + else + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Passing on unchanged PDF data from input"); + while ((bytes = fread(buf, 1, sizeof(buf), inputfp)) > 0) + if (fwrite(buf, 1, bytes, outputfp) != bytes) + break; + fclose(inputfp); + } + fclose(outputfp); + return 0; +} +// }}} diff --git a/cupsfilters/pdftopdf/processor.h b/cupsfilters/pdftopdf/processor.h index 06e24f43..5c9c982b 100644 --- a/cupsfilters/pdftopdf/processor.h +++ b/cupsfilters/pdftopdf/processor.h @@ -145,9 +145,9 @@ _cfPDFToPDFPageHandle* _cfPDFToPDF_PDFioProcessor_new_page(_cfPDFToPDF_PDFioProc float width, float height, pdftopdf_doc_t *doc); -void _cfPDFToPDF_PDFioProcessor_add_page(_cfPDFToPDF_PDFioProcessor *handle, - _cfPDFToPDFPageHandle *page, - bool front); +//void _cfPDFToPDF_PDFioProcessor_add_page(_cfPDFToPDF_PDFioProcessor *handle, +// _cfPDFToPDFPageHandle *page, +// bool front); void _cfPDFToPDF_PDFioProcessor_multiply(_cfPDFToPDF_PDFioProcessor *handle, @@ -239,8 +239,7 @@ void _cfPDFToPDFProcessingParameters_dump(const _cfPDFToPDFProcessingParameters int* _cfPDFToPDFBookletShuffle(int numPages, int signature, int* ret_size); -bool _cfProcessPDFToPDF(pdfio_file_t *pdf, - _cfPDFToPDF_PDFioProcessor *proc, +bool _cfProcessPDFToPDF( _cfPDFToPDF_PDFioProcessor *proc, _cfPDFToPDFProcessingParameters *param, pdftopdf_doc_t *doc); #endif // C_PDFTOPDF_PROCESSOR_PRIVATE_H From 0155c5179ad5a23ca82e8119579f0e55c9b85b2e Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Fri, 20 Sep 2024 03:02:43 +0530 Subject: [PATCH 45/64] removing files which are unnecessary now due to their dependence on qpdf and cpp --- cupsfilters/pdftopdf/intervalset-private.h | 41 - cupsfilters/pdftopdf/intervalset.cxx | 149 --- cupsfilters/pdftopdf/nup-private.h | 108 -- cupsfilters/pdftopdf/nup.cxx | 312 ----- cupsfilters/pdftopdf/pdftopdf-private.h | 24 - .../pdftopdf/pdftopdf-processor-private.h | 239 ---- cupsfilters/pdftopdf/pdftopdf-processor.cxx | 512 --------- cupsfilters/pdftopdf/pdftopdf.cxx | 1017 ----------------- cupsfilters/pdftopdf/pptypes-private.h | 73 -- cupsfilters/pdftopdf/pptypes.cxx | 238 ---- cupsfilters/pdftopdf/qpdf-cm-private.h | 17 - cupsfilters/pdftopdf/qpdf-cm.cxx | 181 --- cupsfilters/pdftopdf/qpdf-pdftopdf-private.h | 47 - .../qpdf-pdftopdf-processor-private.h | 99 -- .../pdftopdf/qpdf-pdftopdf-processor.cxx | 920 --------------- cupsfilters/pdftopdf/qpdf-pdftopdf.cxx | 248 ---- cupsfilters/pdftopdf/qpdf-tools-private.h | 26 - cupsfilters/pdftopdf/qpdf-tools.cxx | 83 -- cupsfilters/pdftopdf/qpdf-xobject-private.h | 13 - cupsfilters/pdftopdf/qpdf-xobject.cxx | 202 ---- 20 files changed, 4549 deletions(-) delete mode 100644 cupsfilters/pdftopdf/intervalset-private.h delete mode 100644 cupsfilters/pdftopdf/intervalset.cxx delete mode 100644 cupsfilters/pdftopdf/nup-private.h delete mode 100644 cupsfilters/pdftopdf/nup.cxx delete mode 100644 cupsfilters/pdftopdf/pdftopdf-private.h delete mode 100644 cupsfilters/pdftopdf/pdftopdf-processor-private.h delete mode 100644 cupsfilters/pdftopdf/pdftopdf-processor.cxx delete mode 100644 cupsfilters/pdftopdf/pdftopdf.cxx delete mode 100644 cupsfilters/pdftopdf/pptypes-private.h delete mode 100644 cupsfilters/pdftopdf/pptypes.cxx delete mode 100644 cupsfilters/pdftopdf/qpdf-cm-private.h delete mode 100644 cupsfilters/pdftopdf/qpdf-cm.cxx delete mode 100644 cupsfilters/pdftopdf/qpdf-pdftopdf-private.h delete mode 100644 cupsfilters/pdftopdf/qpdf-pdftopdf-processor-private.h delete mode 100644 cupsfilters/pdftopdf/qpdf-pdftopdf-processor.cxx delete mode 100644 cupsfilters/pdftopdf/qpdf-pdftopdf.cxx delete mode 100644 cupsfilters/pdftopdf/qpdf-tools-private.h delete mode 100644 cupsfilters/pdftopdf/qpdf-tools.cxx delete mode 100644 cupsfilters/pdftopdf/qpdf-xobject-private.h delete mode 100644 cupsfilters/pdftopdf/qpdf-xobject.cxx diff --git a/cupsfilters/pdftopdf/intervalset-private.h b/cupsfilters/pdftopdf/intervalset-private.h deleted file mode 100644 index 0fc48ca6..00000000 --- a/cupsfilters/pdftopdf/intervalset-private.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_INTERVALSET_H_ -#define _CUPS_FILTERS_PDFTOPDF_INTERVALSET_H_ - -#include "pdftopdf-private.h" -#include -#include - -class _cfPDFToPDFIntervalSet -{ - typedef int key_t; // TODO?! template - typedef std::pair value_t; - typedef std::vector data_t; - public: - static const key_t npos; - - void clear(); - // [start; end) ! - void add(key_t start, key_t end = npos); - void finish(); - - size_t size() const { return data.size(); } - - // only after finish() has been called: - bool contains(key_t val) const; - key_t next(key_t val) const; - - void dump(pdftopdf_doc_t *doc) const; - private: - // currently not used - bool intersect(const value_t &a, const value_t &b) const; - void unite(value_t &aret, const value_t &b) const; - private: - data_t data; -}; - -#endif // !_CUPS_FILTERS_PDFTOPDF_INTERVALSET_H_ diff --git a/cupsfilters/pdftopdf/intervalset.cxx b/cupsfilters/pdftopdf/intervalset.cxx deleted file mode 100644 index 38b1b779..00000000 --- a/cupsfilters/pdftopdf/intervalset.cxx +++ /dev/null @@ -1,149 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "intervalset-private.h" -#include -#include "cupsfilters/debug-internal.h" -#include -#include - -const _cfPDFToPDFIntervalSet::key_t _cfPDFToPDFIntervalSet::npos = - std::numeric_limits<_cfPDFToPDFIntervalSet::key_t>::max(); - -void -_cfPDFToPDFIntervalSet::clear() // {{{ -{ - data.clear(); -} -// }}} - -void -_cfPDFToPDFIntervalSet::add(key_t start, - key_t end) // {{{ -{ - if (start < end) - data.push_back(std::make_pair(start, end)); -} -// }}} - -void -_cfPDFToPDFIntervalSet::finish() // {{{ -{ - data_t::iterator it = data.begin(), - end = data.end(), - pos = it; - if (it == end) - return; - - std::sort(it, end); - - while (1) - { - ++ it; - if (it == end) - { - ++ pos; - break; - } - if (pos->second >= it->first) - pos->second = it->second; - else - { - ++ pos; - if (pos != it) - *pos = *it; - } - } - - data.erase(pos, data.end()); -} -// }}} - -bool -_cfPDFToPDFIntervalSet::contains(key_t val) const // {{{ -{ - data_t::const_iterator it = - std::upper_bound(data.begin(), data.end(), std::make_pair(val, npos)); - if (it == data.begin()) - return false; - -- it; - return (val < it->second); -} -// }}} - -_cfPDFToPDFIntervalSet::key_t -_cfPDFToPDFIntervalSet::next(key_t val) const // {{{ -{ - val ++; - data_t::const_iterator it = - std::upper_bound(data.begin(), data.end(), std::make_pair(val, npos)); - if (it == data.begin()) - { - if (it == data.end()) // empty - return (npos); - return (it->first); - } - -- it; - if (val < it->second) - return (val); - ++ it; - if (it == data.end()) - return npos; - return (it->first); -} -// }}} - -bool -_cfPDFToPDFIntervalSet::intersect(const value_t &a, - const value_t &b) const // {{{ -{ - return (((a.first >= b.first) && (a.first < b.second)) || - ((b.first >= a.first) && (b.first < a.second))); -} -// }}} - -void -_cfPDFToPDFIntervalSet::unite(value_t &aret, - const value_t &b) const // {{{ -{ - DEBUG_assert(intersect(aret, b)); - if (b.first < aret.first) - aret.first = b.first; - if (b.second > aret.second) - aret.second = b.second; -} -// }}} - -void -_cfPDFToPDFIntervalSet::dump(pdftopdf_doc_t *doc) const // {{{ -{ - int len = data.size(); - if (len == 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: (empty)"); - return; - } - len --; - for (int iA = 0; iA < len; iA ++) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: [%d,%d)", - data[iA].first, data[iA].second); - } - if (data[len].second == npos) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: [%d,inf)", - data[len].first); - } - else - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: [%d,%d)", - data[len].first, data[len].second); - } -} -// }}} diff --git a/cupsfilters/pdftopdf/nup-private.h b/cupsfilters/pdftopdf/nup-private.h deleted file mode 100644 index 53f688b2..00000000 --- a/cupsfilters/pdftopdf/nup-private.h +++ /dev/null @@ -1,108 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_NUP_H_ -#define _CUPS_FILTERS_PDFTOPDF_NUP_H_ - -#include "pptypes-private.h" -#include - -// you have to provide this -struct _cfPDFToPDFNupParameters -{ - _cfPDFToPDFNupParameters() - : nupX(1), nupY(1), - width(NAN), height(NAN), - landscape(false), - first(X), - xstart(LEFT), ystart(TOP), - xalign(CENTER), yalign(CENTER) - {} - - // --- "calculated" parameters --- - int nupX, nupY; - float width, height; - bool landscape; // post-rotate! - - // --- other settings --- - // ordering - pdftopdf_axis_e first; - pdftopdf_position_e xstart, ystart; - - pdftopdf_position_e xalign, yalign; - - static bool possible(int nup); // TODO? float in_ratio, float out_ratio - static void preset(int nup, _cfPDFToPDFNupParameters &ret); - static float calculate(int nup, float in_ratio, float out_ratio, - _cfPDFToPDFNupParameters &ret); // returns "quality", - // 1 is best - - void dump(pdftopdf_doc_t *doc) const; -}; - -// you get this -struct _cfPDFToPDFNupPageEdit -{ - // required transformation: first translate, then scale - float xpos, ypos; // TODO: already given by sub.left, sub.bottom - // [but for rotation?] - float scale; // uniform - - // ? "landscape" e.g. to rotate labels - - // for border, clip, ... - // also stores in_width/in_height, unscaled! - // everything in "outer"-page coordinates - _cfPDFToPDFPageRect sub; - - void dump(pdftopdf_doc_t *doc) const; -}; - -// -// This class does the number-up calculation. Example: -// -// _cfPDFToPDFNupParameters param; -// param.xyz = ...; // fill it with your data! -// -// _cfPDFToPDFNupState nup(param); -// _cfPDFToPDFNupPageEdit edit; -// for (auto page : your_pages) -// { -// bool newPage = nup.mext_page(page.w, page.h, edit); // w, h from input -// // page -// // create newPage, if required; then place current page as specified -// // in edit -// } -// - -class _cfPDFToPDFNupState -{ -public: - _cfPDFToPDFNupState(const _cfPDFToPDFNupParameters ¶m); - - void reset(); - - // will overwrite ret with the new parameters - // returns true, if a new output page should be started first - bool mext_page(float in_width, float in_height, _cfPDFToPDFNupPageEdit &ret); - -private: - std::pair convert_order(int subpage) const; - void calculate_edit(int subx, int suby, _cfPDFToPDFNupPageEdit &ret) const; - -private: - _cfPDFToPDFNupParameters param; - - int in_pages, out_pages; - int nup; // max. per page (== nupX * nupY) - int subpage; // on the current output-page -}; - -// TODO? elsewhere -// parsing functions for cups parameters (will not calculate nupX, nupY!) -bool _cfPDFToPDFParseNupLayout(const char *val, _cfPDFToPDFNupParameters &ret); - // lrtb, btlr, ... - -#endif // !_CUPS_FILTERS_PDFTOPDF_NUP_H_ diff --git a/cupsfilters/pdftopdf/nup.cxx b/cupsfilters/pdftopdf/nup.cxx deleted file mode 100644 index 2e1dc341..00000000 --- a/cupsfilters/pdftopdf/nup.cxx +++ /dev/null @@ -1,312 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "nup-private.h" -#include -#include "cupsfilters/debug-internal.h" -#include -#include - -void -_cfPDFToPDFNupParameters::dump(pdftopdf_doc_t *doc) const // {{{ -{ - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: NupX: %d, NupY: %d, " - "width: %f, height: %f", - nupX, nupY, - width, height); - - int opos = -1, - fpos = -1, - spos = -1; - - if (xstart == pdftopdf_position_e::LEFT) // or Bottom - fpos = 0; - else if (xstart == pdftopdf_position_e::RIGHT) // or Top - fpos = 1; - if (ystart == pdftopdf_position_e::LEFT) // or Bottom - spos = 0; - else if (ystart == pdftopdf_position_e::RIGHT) // or Top - spos = 1; - if (first == pdftopdf_axis_e::X) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: First Axis: X"); - opos = 0; - } - else if (first == pdftopdf_axis_e::Y) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: First Axis: Y"); - opos = 2; - std::swap(fpos, spos); - } - - if ((opos == -1) || (fpos == -1) || (spos == -1)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Bad Spec: %d; start: %d, %d", - first, xstart, ystart); - } - else - { - static const char *order[4] = {"lr", "rl", "bt", "tb"}; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Order: %s%s", - order[opos + fpos], - order[(opos + 2) % 4 + spos]); - } - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Alignment:"); - _cfPDFToPDFPositionDump(xalign, pdftopdf_axis_e::X,doc); - _cfPDFToPDFPositionDump(yalign, pdftopdf_axis_e::Y,doc); -} -// }}} - -bool -_cfPDFToPDFNupParameters::possible(int nup) // {{{ -{ - // 1 2 3 4 6 8 9 10 12 15 16 - return ((nup >= 1) && (nup <= 16) && - ((nup != 5) && (nup != 7) && (nup != 11) && (nup != 13) && - (nup != 14))); -} -// }}} - -void -_cfPDFToPDFNupParameters::preset(int nup, - _cfPDFToPDFNupParameters &ret) // {{{ -{ - switch (nup) - { - case 1: - ret.nupX=1; - ret.nupY=1; - break; - case 2: - ret.nupX=2; - ret.nupY=1; - ret.landscape=true; - break; - case 3: - ret.nupX=3; - ret.nupY=1; - ret.landscape=true; - break; - case 4: - ret.nupX=2; - ret.nupY=2; - break; - case 6: - ret.nupX=3; - ret.nupY=2; - ret.landscape=true; - break; - case 8: - ret.nupX=4; - ret.nupY=2; - ret.landscape=true; - break; - case 9: - ret.nupX=3; - ret.nupY=3; - break; - case 10: - ret.nupX=5; - ret.nupY=2; - ret.landscape=true; - break; - case 12: - ret.nupX=3; - ret.nupY=4; - break; - case 15: - ret.nupX=5; - ret.nupY=3; - ret.landscape=true; - break; - case 16: - ret.nupX=4; - ret.nupY=4; - break; - } -} -// }}} - - -_cfPDFToPDFNupState::_cfPDFToPDFNupState(const _cfPDFToPDFNupParameters ¶m) // {{{ - : param(param), - in_pages(0), out_pages(0), - nup(param.nupX * param.nupY), - subpage(nup) -{ - DEBUG_assert((param.nupX > 0) && (param.nupY > 0)); -} -// }}} - -void -_cfPDFToPDFNupState::reset() // {{{ -{ - in_pages = 0; - out_pages = 0; -// nup = param.nupX * param.nupY; - subpage = nup; -} -// }}} - -void _cfPDFToPDFNupPageEdit::dump(pdftopdf_doc_t *doc) const // {{{ -{ - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: xpos: %f, ypos: %f, scale: %f", - xpos, ypos, scale); - sub.dump(doc); -} -// }}} - -std::pair -_cfPDFToPDFNupState::convert_order(int subpage) const // {{{ -{ - int subx, suby; - if (param.first==pdftopdf_axis_e::X) - { - subx = subpage % param.nupX; - suby = subpage / param.nupX; - } - else - { - subx = subpage / param.nupY; - suby = subpage % param.nupY; - } - - subx = (param.nupX - 1) * (param.xstart + 1) / 2 - param.xstart * subx; - suby = (param.nupY - 1) * (param.ystart + 1) / 2 - param.ystart * suby; - - return (std::make_pair(subx, suby)); -} -// }}} - -static inline float -lin(pdftopdf_position_e pos, - float size) // {{{ -{ - if (pos == -1) - return (0); - else if (pos == 0) - return (size / 2); - else if (pos == 1) - return (size); - return (size * (pos + 1) / 2); -} -// }}} - -void -_cfPDFToPDFNupState::calculate_edit(int subx, - int suby, - _cfPDFToPDFNupPageEdit &ret) const // {{{ -{ - // dimensions of a "nup cell" - const float width = param.width / param.nupX, - height = param.height / param.nupY; - - // first calculate only for bottom-left corner - ret.xpos = subx * width; - ret.ypos = suby * height; - - const float scalex = width / ret.sub.width, - scaley = height / ret.sub.height; - float subwidth = ret.sub.width * scaley, - subheight = ret.sub.height * scalex; - - // TODO? if ((!fitPlot) && (ret.scale > 1)) ret.scale = 1.0; - if (scalex > scaley) - { - ret.scale = scaley; - subheight = height; - ret.xpos += lin(param.xalign, width-subwidth); - } - else - { - ret.scale = scalex; - subwidth = width; - ret.ypos += lin(param.yalign, height-subheight); - } - - ret.sub.left = ret.xpos; - ret.sub.bottom = ret.ypos; - ret.sub.right = ret.sub.left + subwidth; - ret.sub.top = ret.sub.bottom + subheight; -} -// }}} - -bool -_cfPDFToPDFNupState::mext_page(float in_width, - float in_height, - _cfPDFToPDFNupPageEdit &ret) // {{{ -{ - in_pages ++; - subpage ++; - if (subpage >= nup) - { - subpage = 0; - out_pages ++; - } - - ret.sub.width = in_width; - ret.sub.height = in_height; - - auto sub = convert_order(subpage); - calculate_edit(sub.first, sub.second, ret); - - return (subpage == 0); -} -// }}} - - -static std::pair -parsePosition(char a, - char b) // {{{ returns ,CENTER(0) on invalid -{ - a |= 0x20; // make lowercase - b |= 0x20; - if ((a == 'l') && (b == 'r')) - return (std::make_pair(pdftopdf_axis_e::X, pdftopdf_position_e::LEFT)); - else if ((a == 'r') && (b == 'l')) - return (std::make_pair(pdftopdf_axis_e::X, pdftopdf_position_e::RIGHT)); - else if ((a == 't') && (b == 'b')) - return (std::make_pair(pdftopdf_axis_e::Y, pdftopdf_position_e::TOP)); - else if ((a == 'b') && (b == 't')) - return (std::make_pair(pdftopdf_axis_e::Y, pdftopdf_position_e::BOTTOM)); - return (std::make_pair(pdftopdf_axis_e::X, pdftopdf_position_e::CENTER)); -} -// }}} - -bool -_cfPDFToPDFParseNupLayout(const char *val, - _cfPDFToPDFNupParameters &ret) // {{{ -{ - DEBUG_assert(val); - auto pos0 = parsePosition(val[0], val[1]); - if (pos0.second == CENTER) - return (false); - auto pos1 = parsePosition(val[2], val[3]); - if ((pos1.second == CENTER) || (pos0.first == pos1.first)) - return (false); - - ret.first = pos0.first; - if (ret.first == pdftopdf_axis_e::X) - { - ret.xstart = pos0.second; - ret.ystart = pos1.second; - } - else - { - ret.xstart = pos1.second; - ret.ystart = pos0.second; - } - - return (val[4] == 0); // everything seen? -} -// }}} diff --git a/cupsfilters/pdftopdf/pdftopdf-private.h b/cupsfilters/pdftopdf/pdftopdf-private.h deleted file mode 100644 index 4e1d8fc5..00000000 --- a/cupsfilters/pdftopdf/pdftopdf-private.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// Copyright 2020 by Jai Luthra. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_PDFTOPDF_H -#define _CUPS_FILTERS_PDFTOPDF_PDFTOPDF_H - -#include - -typedef struct // **** Document information **** -{ - cf_logfunc_t logfunc; // Log function - void *logdata; // Log data - cf_filter_iscanceledfunc_t iscanceledfunc; // Function returning 1 when - // job is canceled, NULL for not - // supporting stop on cancel - void *iscanceleddata; // User data for is-canceled - // function, can be NULL -} pdftopdf_doc_t; - -#endif // !_CUPS_FILTERS_PDFTOPDF_PDFTOPDF_H diff --git a/cupsfilters/pdftopdf/pdftopdf-processor-private.h b/cupsfilters/pdftopdf/pdftopdf-processor-private.h deleted file mode 100644 index 92ed198e..00000000 --- a/cupsfilters/pdftopdf/pdftopdf-processor-private.h +++ /dev/null @@ -1,239 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_PDFTOPDF_PROCESSOR_H -#define _CUPS_FILTERS_PDFTOPDF_PDFTOPDF_PROCESSOR_H - -#include "pptypes-private.h" -#include "nup-private.h" -#include "pdftopdf-private.h" -#include "intervalset-private.h" -#include -#include -#include -#include - -enum pdftopdf_booklet_mode_e { - CF_PDFTOPDF_BOOKLET_OFF, - CF_PDFTOPDF_BOOKLET_ON, - CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE -}; - -struct _cfPDFToPDFProcessingParameters { -_cfPDFToPDFProcessingParameters() -: job_id(0), - num_copies(1), - user(0), - title(0), - pagesize_requested(false), - fitplot(false), - fillprint(false), //print-scaling = fill - cropfit(false), - autoprint(false), - autofit(false), - fidelity(false), - no_orientation(false), - orientation(ROT_0), - normal_landscape(ROT_270), - paper_is_landscape(false), - duplex(false), - border(NONE), - reverse(false), - - page_label(), - even_pages(true), - odd_pages(true), - - mirror(false), - - xpos(CENTER), - ypos(CENTER), - - collate(false), - even_duplex(false), - - booklet(CF_PDFTOPDF_BOOKLET_OFF), - book_signature(-1), - - auto_rotate(false), - - device_copies(1), - device_collate(false), - set_duplex(false), - - page_logging(-1) - { - page.width = 612.0; // Letter - page.height = 792.0; - page.top = page.height - 36.0; - page.bottom = 36.0; - page.left = 18.0; - page.right = page.width - 18.0; - - // everything - input_page_ranges.add(1); - input_page_ranges.finish(); - page_ranges.add(1); - page_ranges.finish(); - } - - int job_id, num_copies; - const char *user, *title; // will stay around - bool pagesize_requested; - bool fitplot; - bool fillprint; //print-scaling = fill - bool cropfit; // -o crop-to-fit - bool autoprint; // print-scaling = auto - bool autofit; // print-scaling = auto-fit - bool fidelity; - bool no_orientation; - _cfPDFToPDFPageRect page; - pdftopdf_rotation_e orientation,normal_landscape; // normal_landscape (i.e. default direction) is e.g. needed for number-up=2 - bool paper_is_landscape; - bool duplex; - pdftopdf_border_type_e border; - _cfPDFToPDFNupParameters nup; - bool reverse; - - std::string page_label; - bool even_pages, odd_pages; - _cfPDFToPDFIntervalSet page_ranges; - _cfPDFToPDFIntervalSet input_page_ranges; - - bool mirror; - - pdftopdf_position_e xpos, ypos; - - bool collate; - - bool even_duplex; // make number of pages a multiple of 2 - - pdftopdf_booklet_mode_e booklet; - int book_signature; - - bool auto_rotate; - - int device_copies; - bool device_collate; - bool set_duplex; - - int page_logging; - int copies_to_be_logged; - - // helper functions - bool with_page(int outno) const; // 1 based - bool have_page(int pageno) const; //1 based - void dump(pdftopdf_doc_t *doc) const; -}; - -enum pdftopdf_arg_ownership_e { - CF_PDFTOPDF_WILL_STAY_ALIVE, - CF_PDFTOPDF_MUST_DUPLICATE, - CF_PDFTOPDF_TAKE_OWNERSHIP -}; - -class _cfPDFToPDFPageHandle { - public: - virtual ~_cfPDFToPDFPageHandle() {} - - virtual _cfPDFToPDFPageRect get_rect() const = 0; - - // fscale: inverse_scale (from nup, fitplot) - - virtual void add_border_rect(const _cfPDFToPDFPageRect &rect, - pdftopdf_border_type_e border, float fscale) = 0; - - // TODO?! add standalone crop(...) method (not only for subpages) - - virtual pdftopdf_rotation_e crop(const _cfPDFToPDFPageRect &cropRect, - pdftopdf_rotation_e orientation, - pdftopdf_rotation_e param_orientation, - pdftopdf_position_e xpos, - pdftopdf_position_e ypos, - bool scale, bool autorotate, - pdftopdf_doc_t *doc) = 0; - - virtual bool is_landscape(pdftopdf_rotation_e orientation) = 0; - - virtual void add_subpage(const std::shared_ptr<_cfPDFToPDFPageHandle> &sub, - float xpos, float ypos, float scale, - const _cfPDFToPDFPageRect *crop=NULL) = 0; - - virtual void mirror() = 0; - - virtual void rotate(pdftopdf_rotation_e rot) = 0; - - virtual void add_label(const _cfPDFToPDFPageRect &rect, - const std::string label) = 0; -}; - -// TODO: ... error output? -class _cfPDFToPDFProcessor { // abstract interface - public: - virtual ~_cfPDFToPDFProcessor() {} - - // TODO: ... qpdf wants password at load time - virtual bool load_file(FILE *f,pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e take = - CF_PDFTOPDF_WILL_STAY_ALIVE, - int flatten_forms = 1) = 0; - - virtual bool load_filename(const char *name, pdftopdf_doc_t *doc, - int flatten_forms = 1) = 0; - - // TODO? virtual bool may_modify/may_print/? - virtual bool check_print_permissions(pdftopdf_doc_t *doc) = 0; - - virtual std::vector> - get_pages(pdftopdf_doc_t *doc) = 0; // shared_ptr because of type - // erasure (deleter) - - virtual std::shared_ptr<_cfPDFToPDFPageHandle> - new_page(float width, float height, pdftopdf_doc_t *doc) = 0; - - virtual void add_page(std::shared_ptr<_cfPDFToPDFPageHandle> page, - bool front) = 0; // at back/front -- either from - // get_pages() or - // new_page()+add_subpage()-calls - // (or [also allowed]: empty) - - // void remove_page(std::shared_ptr<_cfPDFToPDFPageHandle> ph); - // not needed: we construct from scratch, at least conceptually. - - virtual void multiply(int copies, bool collate) = 0; - - virtual void auto_rotate_all(bool dst_lscape, - pdftopdf_rotation_e normal_landscape) = 0; - // TODO elsewhere?! - virtual void add_cm(const char *defaulticc, const char *outputicc) = 0; - - virtual void set_comments(const std::vector &comments) = 0; - - virtual void emit_file(FILE *dst,pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e take = - CF_PDFTOPDF_WILL_STAY_ALIVE) = 0; - virtual void emit_filename(const char *name,pdftopdf_doc_t *doc) = 0; - // NULL -> stdout - - virtual bool has_acro_form() = 0; -}; - -class _cfPDFToPDFFactory { - public: - // never NULL, but may throw. - static _cfPDFToPDFProcessor *processor(); -}; - -//bool _cfPDFToPDFCheckBookletSignature(int signature) -// { return (signature%4==0); } - -std::vector _cfPDFToPDFBookletShuffle(int numPages, int signature = -1); - -// This is all we want: -bool _cfProcessPDFToPDF(_cfPDFToPDFProcessor &proc, - _cfPDFToPDFProcessingParameters ¶m, - pdftopdf_doc_t *doc); - -#endif // !_CUPS_FILTERS_PDFTOPDF_PDFTOPDF_PROCESSOR_H diff --git a/cupsfilters/pdftopdf/pdftopdf-processor.cxx b/cupsfilters/pdftopdf/pdftopdf-processor.cxx deleted file mode 100644 index 92e9fc03..00000000 --- a/cupsfilters/pdftopdf/pdftopdf-processor.cxx +++ /dev/null @@ -1,512 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "pdftopdf-processor-private.h" -#include "qpdf-pdftopdf-processor-private.h" -#include -#include "cupsfilters/debug-internal.h" -#include - -void -BookletMode_dump(pdftopdf_booklet_mode_e bkm, - pdftopdf_doc_t *doc) // {{{ -{ - static const char *bstr[3] = {"Off", "On", "Shuffle-Only"}; - - if ((bkm < CF_PDFTOPDF_BOOKLET_OFF) || - (bkm > CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Booklet mode: (Bad booklet mode: %d)", - bkm); - } - else - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Booklet mode: %s", - bstr[bkm]); - } -} -// }}} - -bool -_cfPDFToPDFProcessingParameters::with_page(int outno) const // {{{ -{ - if (outno % 2 == 0) - { // 1-based - if (!even_pages) - return (false); - } - else if (!odd_pages) - return (false); - return (page_ranges.contains(outno)); -} -// }}} - -bool -_cfPDFToPDFProcessingParameters::have_page(int pageno) const -{ - return (input_page_ranges.contains(pageno)); -} - -void -_cfPDFToPDFProcessingParameters::dump(pdftopdf_doc_t *doc) const // {{{ -{ - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: job_id: %d, num_copies: %d", - job_id, num_copies); - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: user: %s, title: %s", - (user) ? user : "(null)", - (title) ? title : "(null)"); - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: fitplot: %s", - (fitplot) ? "true" : "false"); - - page.dump(doc); - - _cfPDFToPDFRotationDump(orientation, doc); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: paper_is_landscape: %s", - (paper_is_landscape) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: duplex: %s", - (duplex) ? "true" : "false"); - - _cfPDFToPDFBorderTypeDump(border, doc); - - nup.dump(doc); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: reverse: %s", - (reverse) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: even_pages: %s, odd_pages: %s", - (even_pages) ? "true" : "false", - (odd_pages) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: input page range:"); - input_page_ranges.dump(doc); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: page range:"); - page_ranges.dump(doc); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: mirror: %s", - (mirror) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Position:"); - _cfPDFToPDFPositionDump(xpos, pdftopdf_axis_e::X,doc); - _cfPDFToPDFPositionDump(ypos, pdftopdf_axis_e::Y,doc); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: collate: %s", - (collate) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: even_duplex: %s", - (even_duplex) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: page_label: %s", - page_label.empty () ? "(none)" : - page_label.c_str()); - - BookletMode_dump(booklet,doc); - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: booklet signature: %d", - book_signature); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: auto_rotate: %s", - (auto_rotate) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: device_copies: %d", - device_copies); - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: device_collate: %s", - (device_collate) ? "true" : "false"); - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: set_duplex: %s", - (set_duplex) ? "true" : "false"); -} -// }}} - - -_cfPDFToPDFProcessor -*_cfPDFToPDFFactory::processor() -{ - return new _cfPDFToPDFQPDFProcessor(); -} - -// -// (1-based) -// 9: [*] [1] [2] [*] [*] [3] [4] [9] [8] [5] [6] [7] -// 1 2 3 4 5 6 7 8 9 10 11 12 -// -// -> signature = 12 = 3*4 = ((9+3)/4)*4 -// -// NOTE: psbook always fills the sig completely (results in completely -// white pages (4-set), depending on the input) -// -// empty pages must be added for output values >= numPages -// - -std::vector -_cfPDFToPDFBookletShuffle(int numPages, - int signature) // {{{ -{ - if (signature < 0) - signature = (numPages + 3) & ~0x3; - DEBUG_assert(signature % 4 == 0); - - std::vector ret; - ret.reserve(numPages + signature - 1); - - int curpage = 0; - while (curpage < numPages) - { - // as long as pages to be done -- i.e. multiple times the signature - int firstpage = curpage, - lastpage = curpage + signature - 1; - // one signature - while (firstpage < lastpage) - { - ret.push_back(lastpage --); - ret.push_back(firstpage ++); - ret.push_back(firstpage ++); - ret.push_back(lastpage --); - } - curpage += signature; - } - return (ret); -} -// }}} - -bool -_cfProcessPDFToPDF(_cfPDFToPDF_PDFioProcessor &proc, - _cfPDFToPDFProcessingParameters ¶m, - pdftopdf_doc_t *doc) // {{{ -{ - if (!proc.check_print_permissions(doc)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Not allowed to print"); - return false; - } - - const bool dst_lscape = - (param.paper_is_landscape == - ((param.orientation == ROT_0) || (param.orientation == ROT_180))); - - if (param.paper_is_landscape) - std::swap(param.nup.nupX, param.nup.nupY); - - if (param.auto_rotate) - proc.auto_rotate_all(dst_lscape, param.normal_landscape); - - std::vector> pages = - proc.get_pages(doc); - - std::vector> input_page_range_list; - - for (int i = 1; i <= (int)pages.size(); i ++) - if (param.have_page(i)) - input_page_range_list.push_back(pages[i - 1]); - - const int numOrigPages = input_page_range_list.size(); - - // TODO FIXME? elsewhere - std::vector shuffle; - if (param.booklet != CF_PDFTOPDF_BOOKLET_OFF) - { - shuffle = _cfPDFToPDFBookletShuffle(numOrigPages, param.book_signature); - if (param.booklet == CF_PDFTOPDF_BOOKLET_ON) - { // override options - // We do not "sides=two-sided-short-edge" / DuplexTumble here. - // We assume it done by caller, for example ppdFilterLoadPPD() of libppd - // param.duplex=true; - // param.set_duplex=true; - _cfPDFToPDFNupParameters::preset(2, param.nup); // TODO?! better - } - } - else - { // 0 1 2 3 ... - shuffle.resize(numOrigPages); - std::iota(shuffle.begin(), shuffle.end(), 0); - } - const int numPages=std::max(shuffle.size(), input_page_range_list.size()); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: \"print-scaling\" IPP attribute: %s", - (param.autoprint ? "auto" : - (param.autofit ? "auto-fit" : - (param.fitplot ? "fit" : - (param.fillprint ? "fill" : - (param.cropfit ? "none" : - "Not defined, should never happen")))))); - - if (param.autoprint || param.autofit) - { - bool margin_defined = true; - bool document_large = false; - int pw = param.page.right - param.page.left; - int ph = param.page.top - param.page.bottom; - - if ((param.page.width == pw) && (param.page.height == ph)) - margin_defined = false; - - for (int i = 0; i < (int)input_page_range_list.size(); i ++) - { - _cfPDFToPDFPageRect r = input_page_range_list[i]->get_rect(); - int w = r.width * 100 / 102; // 2% of tolerance - int h = r.height * 100 / 102; - if ((w > param.page.width || h > param.page.height) && - (h > param.page.width || w > param.page.height)) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Page %d too large for output page size, scaling pages to fit.", - i + 1); - document_large = true; - } - } - if (param.fidelity && doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: \"ipp-attribute-fidelity\" IPP attribute is set, scaling pages to fit."); - - if (param.autoprint) - { - if (param.fidelity || document_large) - { - if (margin_defined) - param.fitplot = true; - else - param.fillprint = true; - } - else - param.cropfit = true; - } - else - { - if (param.fidelity || document_large) - param.fitplot = true; - else - param.cropfit = true; - } - } - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Print scaling mode: %s", - (param.fitplot ? - "Scale to fit printable area" : - (param.fillprint ? - "Scale to fill page and crop" : - (param.cropfit ? - "Do not scale, center, crop if needed" : - "Not defined, should never happen")))); - - // In Crop mode we do not scale the original document, it should keep the - // exact same size. With N-Up it should be scaled to fit exacly the halves, - // quarters, ... of the sheet, regardless of unprintable margins. - // Therefore we remove the unprintable margins to do all the math without - // them. - if (param.cropfit) - { - param.page.left = 0; - param.page.bottom = 0; - param.page.right = param.page.width; - param.page.top = param.page.height; - } - - if (param.pagesize_requested && (param.fillprint || param.cropfit)) - { - for (int i = 0; i < (int)input_page_range_list.size(); i ++) - { - std::shared_ptr<_cfPDFToPDFPageHandle> page = input_page_range_list[i]; - pdftopdf_rotation_e orientation; - if (page->is_landscape(param.orientation)) - orientation = param.normal_landscape; - else - orientation = ROT_0; - page->crop(param.page, orientation, param.orientation, - param.xpos, param.ypos, - !param.cropfit, param.auto_rotate, doc); - } - if (param.fillprint) - param.fitplot = true; - } - - std::shared_ptr<_cfPDFToPDFPageHandle> curpage; - int outputpage = 0; - int outputno = 0; - - if ((param.nup.nupX == 1) && (param.nup.nupY == 1) && !param.fitplot) - { - param.nup.width = param.page.width; - param.nup.height = param.page.height; - } - else - { - param.nup.width = param.page.right - param.page.left; - param.nup.height = param.page.top - param.page.bottom; - } - - if ((param.orientation == ROT_90) || (param.orientation == ROT_270)) - { - std::swap(param.nup.nupX, param.nup.nupY); - param.nup.landscape = !param.nup.landscape; - param.orientation = param.orientation - param.normal_landscape; - } - - double xpos = 0, ypos = 0; - if (param.nup.landscape) - { - // pages[iA]->rotate(param.normal_landscape); - param.orientation = param.orientation + param.normal_landscape; - // TODO? better - if (param.nup.nupX != 1 || param.nup.nupY != 1 || param.fitplot) - { - xpos = param.page.height - param.page.top; - ypos = param.page.left; - } - std::swap(param.page.width, param.page.height); - std::swap(param.nup.width, param.nup.height); - } - else - { - if (param.nup.nupX != 1 || param.nup.nupY != 1 || param.fitplot) - { - xpos = param.page.left; - ypos = param.page.bottom; // for whole page... TODO from position... - } - } - - _cfPDFToPDFNupState nupstate(param.nup); - _cfPDFToPDFNupPageEdit pgedit; - for (int iA = 0; iA < numPages; iA ++) - { - std::shared_ptr<_cfPDFToPDFPageHandle> page; - if (shuffle[iA] >= numOrigPages) - // add empty page as filler - page = proc.new_page(param.page.width, param.page.height, doc); - else - page = input_page_range_list[shuffle[iA]]; - - _cfPDFToPDFPageRect rect; - rect = page->get_rect(); - //rect.dump(doc); - if (!param.pagesize_requested) - { - param.page.width = param.page.right = rect.width; - param.page.height = param.page.top = rect.height; - } - - bool newPage = nupstate.mext_page(rect.width, rect.height, pgedit); - if (newPage) - { - if ((curpage) && (param.with_page(outputpage))) - { - curpage->rotate(param.orientation); - if (param.mirror) - curpage->mirror(); - // TODO? update rect? --- not needed any more - proc.add_page(curpage, param.reverse); // reverse -> insert at beginning - // Log page in /var/log/cups/page_log - outputno ++; - if (param.page_logging == 1) - if (doc->logfunc) doc->logfunc(doc->logdata, - CF_LOGLEVEL_CONTROL, - "PAGE: %d %d", outputno, - param.copies_to_be_logged); - } - curpage = proc.new_page(param.page.width, param.page.height, doc); - outputpage++; - } - - if (shuffle[iA] >= numOrigPages) - continue; - - if (param.border != pdftopdf_border_type_e::NONE) - // TODO FIXME... border gets cutted away, if orignal page had wrong size - // page->"uncrop"(rect); // page->setMedia() - // Note: currently "fixed" in add_subpage(...&rect); - page->add_border_rect(rect, param.border, 1.0 / pgedit.scale); - - if (!param.page_label.empty()) - page->add_label(param.page, param.page_label); - - if (param.cropfit) - { - if ((param.nup.nupX == 1) && (param.nup.nupY == 1)) - { - double xpos2, ypos2; - if ((param.page.height - param.page.width) * - (page->get_rect().height - page->get_rect().width) < 0) - { - xpos2 = (param.page.width - (page->get_rect().height)) / 2; - ypos2 = (param.page.height - (page->get_rect().width)) / 2; - curpage->add_subpage(page, ypos2 + xpos, xpos2 + ypos, 1); - } - else - { - xpos2 = (param.page.width - (page->get_rect().width)) / 2; - ypos2 = (param.page.height - (page->get_rect().height)) / 2; - curpage->add_subpage(page, xpos2 + xpos, ypos2 + ypos, 1); - } - } - else - curpage->add_subpage(page, pgedit.xpos + xpos, pgedit.ypos + ypos, - pgedit.scale); - } - else - curpage->add_subpage(page, pgedit.xpos + xpos, pgedit.ypos + ypos, - pgedit.scale); - -#ifdef DEBUG - if (auto dbg=dynamic_cast<_cfPDFToPDFQPDFPageHandle *>(curpage.get())) - dbg->debug(pgedit.sub, xpos, ypos); -#endif - - //pgedit.dump(doc); - } - if ((curpage) && (param.with_page(outputpage))) - { - curpage->rotate(param.orientation); - if (param.mirror) - curpage->mirror(); - proc.add_page(curpage, param.reverse); // reverse -> insert at beginning - // Log page in /var/log/cups/page_log - outputno ++; - if (param.page_logging == 1) - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d", outputno, - param.copies_to_be_logged); - } - - if ((param.even_duplex || !param.odd_pages) && (outputno & 1)) - { - // need to output empty page to not confuse duplex - proc.add_page(proc.new_page(param.page.width, - param.page.height, doc), param.reverse); - // Log page in /var/log/cups/page_log - if (param.page_logging == 1) - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d", outputno + 1, - param.copies_to_be_logged); - } - - proc.multiply(param.num_copies, param.collate); - - return (true); -} -// }}} diff --git a/cupsfilters/pdftopdf/pdftopdf.cxx b/cupsfilters/pdftopdf/pdftopdf.cxx deleted file mode 100644 index bdf2475d..00000000 --- a/cupsfilters/pdftopdf/pdftopdf.cxx +++ /dev/null @@ -1,1017 +0,0 @@ -// -// Copyright (c) 2012 Tobias Hoffmann -// -// Copyright (c) 2006-2011, BBR Inc. All rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "pdftopdf-private.h" -#include "pdftopdf-processor-private.h" - -#include - - -// namespace {} - -static bool -optGetInt(const char *name, - int num_options, - cups_option_t *options, - int *ret) // {{{ -{ - DEBUG_assert(ret); - const char *val = cupsGetOption(name, num_options, options); - if (val) - { - *ret = atoi(val); - return (true); - } - return (false); -} -// }}} - -static bool -optGetFloat(const char *name, - int num_options, - cups_option_t *options, - float *ret) // {{{ -{ - DEBUG_assert(ret); - const char *val = cupsGetOption(name, num_options, options); - if (val) - { - *ret = atof(val); - return (true); - } - return (false); -} -// }}} - -static bool -is_false(const char *value) // {{{ -{ - if (!value) - return (false); - return ((strcasecmp(value, "no") == 0) || - (strcasecmp(value, "off") == 0) || - (strcasecmp(value, "false") == 0)); -} -// }}} - -static bool -is_true(const char *value) // {{{ -{ - if (!value) - return (false); - return ((strcasecmp(value, "yes") == 0) || - (strcasecmp(value, "on") == 0) || - (strcasecmp(value, "true") == 0)); -} -// }}} - - -static bool -parsePosition(const char *value, - pdftopdf_position_e &xpos, - pdftopdf_position_e &ypos) // {{{ -{ - // ['center','top','left','right','top-left','top-right','bottom', - // 'bottom-left','bottom-right'] - xpos = pdftopdf_position_e::CENTER; - ypos = pdftopdf_position_e::CENTER; - int next = 0; - if (strcasecmp(value, "center") == 0) - return (true); - else if (strncasecmp(value, "top", 3) == 0) - { - ypos = pdftopdf_position_e::TOP; - next = 3; - } - else if (strncasecmp(value, "bottom", 6) == 0) - { - ypos = pdftopdf_position_e::BOTTOM; - next = 6; - } - if (next) - { - if (value[next] == 0) - return (true); - else if (value[next] != '-') - return (false); - value += next + 1; - } - if (strcasecmp(value, "left") == 0) - xpos = pdftopdf_position_e::LEFT; - else if (strcasecmp(value, "right") == 0) - xpos = pdftopdf_position_e::RIGHT; - else - return (false); - return (true); -} -// }}} - -static void -parseRanges(const char *range, _cfPDFToPDFIntervalSet &ret) // {{{ -{ - ret.clear(); - if (!range) - { - ret.add(1); // everything - ret.finish(); - return; - } - - int lower, upper; - while (*range) - { - if (*range == '-') - { - range ++; - upper = strtol(range, (char **)&range, 10); - if (upper >= 2147483647) // see also cups/encode.c - ret.add(1); - else - ret.add(1, upper + 1); - } - else - { - lower = strtol(range, (char **)&range, 10); - if (*range == '-') - { - range ++; - if (!isdigit(*range)) - ret.add(lower); - else - { - upper=strtol(range, (char **)&range, 10); - if (upper>=2147483647) - ret.add(lower); - else - ret.add(lower, upper + 1); - } - } - else - ret.add(lower, lower + 1); - } - - if (*range != ',') - break; - range++; - } - ret.finish(); -} -// }}} - -static bool -_cfPDFToPDFParseBorder(const char *val, - pdftopdf_border_type_e &ret) // {{{ -{ - DEBUG_assert(val); - if (strcasecmp(val, "none") == 0) - ret = pdftopdf_border_type_e::NONE; - else if (strcasecmp(val, "single") == 0) - ret = pdftopdf_border_type_e::ONE_THIN; - else if (strcasecmp(val, "single-thick") == 0) - ret = pdftopdf_border_type_e::ONE_THICK; - else if (strcasecmp(val, "double") == 0) - ret = pdftopdf_border_type_e::TWO_THIN; - else if (strcasecmp(val, "double-thick") == 0) - ret = pdftopdf_border_type_e::TWO_THICK; - else - return (false); - return (true); -} -// }}} - -void -getParameters(cf_filter_data_t *data, - int num_options, - cups_option_t *options, - _cfPDFToPDFProcessingParameters ¶m, - pdftopdf_doc_t *doc) // {{{ -{ - char *final_content_type = data->final_content_type; - ipp_t *printer_attrs = data->printer_attrs; - ipp_t *job_attrs = data->job_attrs; - ipp_attribute_t *attr; - const char *val; - int ipprot; - int nup; - std::string rawlabel; - char *classification; - std::ostringstream cookedlabel; - - - if ((val = cupsGetOption("copies", num_options, options)) != NULL || - (val = cupsGetOption("Copies", num_options, options)) != NULL || - (val = cupsGetOption("num-copies", num_options, options)) != NULL || - (val = cupsGetOption("NumCopies", num_options, options)) != NULL) - { - int copies = atoi(val); - if (copies > 0) - param.num_copies = copies; - } - - if (param.num_copies == 0) - param.num_copies = 1; - - // direction the printer rotates landscape - // (landscape-orientation-requested-preferred: 4: 90 or 5: -90) - if (printer_attrs != NULL && - (attr = ippFindAttribute(printer_attrs, - "landscape-orientation-requested-preferred", - IPP_TAG_ZERO)) != NULL && - ippGetInteger(attr, 0) == 5) - param.normal_landscape = ROT_270; - else - param.normal_landscape = ROT_90; - - param.orientation = ROT_0; - param.no_orientation = false; - if (optGetInt("orientation-requested", num_options, options, &ipprot)) - { - // IPP orientation values are: - // 3: 0 degrees, 4: 90 degrees, 5: -90 degrees, 6: 180 degrees - - if ((ipprot < 3) || (ipprot > 6)) - { - if (ipprot && doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Bad value (%d) for " - "orientation-requested, using 0 degrees", - ipprot); - param.no_orientation = true; - } - else - { - static const pdftopdf_rotation_e - ipp2rot[4]={ROT_0, ROT_90, ROT_270, ROT_180}; - param.orientation = ipp2rot[ipprot - 3]; - } - } - else if ((val = cupsGetOption("landscape", num_options, options)) != NULL) - { - if (!is_false(val)) - param.orientation = param.normal_landscape; - } - else - param.no_orientation = true; - - param.pagesize_requested = - (cfGetPageDimensions(printer_attrs, job_attrs, num_options, options, NULL, - 0, - &(param.page.width), &(param.page.height), - &(param.page.left), &(param.page.bottom), - &(param.page.right), &(param.page.top), - NULL, NULL) >= 1); - - cfSetPageDimensionsToDefault(&(param.page.width), &(param.page.height), - &(param.page.left), &(param.page.bottom), - &(param.page.right), &(param.page.top), - doc->logfunc, doc->logdata); - - param.page.right = param.page.width - param.page.right; - param.page.top = param.page.height - param.page.top; - - param.paper_is_landscape = (param.page.width > param.page.height); - - _cfPDFToPDFPageRect tmp; // borders (before rotation) - - optGetFloat("page-top", num_options, options, &tmp.top); - optGetFloat("page-left", num_options, options, &tmp.left); - optGetFloat("page-right", num_options, options, &tmp.right); - optGetFloat("page-bottom", num_options, options, &tmp.bottom); - - if ((val = cupsGetOption("media-top-margin", num_options, options)) - != NULL) - tmp.top = atof(val) * 72.0 / 2540.0; - if ((val = cupsGetOption("media-left-margin", num_options, options)) - != NULL) - tmp.left = atof(val) * 72.0 / 2540.0; - if ((val = cupsGetOption("media-right-margin", num_options, options)) - != NULL) - tmp.right = atof(val) * 72.0 / 2540.0; - if ((val = cupsGetOption("media-bottom-margin", num_options, options)) - != NULL) - tmp.bottom = atof(val) * 72.0 / 2540.0; - - if ((param.orientation == ROT_90) || (param.orientation == ROT_270)) - { // unrotate page - // NaN stays NaN - tmp.right = param.page.height - tmp.right; - tmp.top = param.page.width - tmp.top; - tmp.rotate_move(param.orientation, param.page.height, param.page.width); - } - else - { - tmp.right = param.page.width - tmp.right; - tmp.top = param.page.height - tmp.top; - tmp.rotate_move(param.orientation, param.page.width, param.page.height); - } - - param.page.set(tmp); // replace values, where tmp.* != NaN - // (because tmp needed rotation, param.page not!) - - if ((val = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, "sides")) != - NULL && - strncmp(val, "two-sided-", 10) == 0) - param.duplex = true; - else if (is_true(cupsGetOption("Duplex", num_options, options))) - { - param.duplex = true; - param.set_duplex = true; - } - else if ((val = cupsGetOption("sides", num_options, options)) != NULL) - { - if ((strcasecmp(val, "two-sided-long-edge") == 0) || - (strcasecmp(val, "two-sided-short-edge") == 0)) - { - param.duplex = true; - param.set_duplex = true; - } - else if (strcasecmp(val, "one-sided") != 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported sides value %s, using sides=one-sided!", - val); - } - } - - // default nup is 1 - nup = 1; - if (optGetInt("number-up", num_options, options, &nup)) - { - if (!_cfPDFToPDFNupParameters::possible(nup)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported number-up value %d, using number-up=1!", - nup); - nup = 1; - } - // TODO ; TODO? nup enabled? ... fitplot - // _cfPDFToPDFNupParameters::calculate(nup, param.nup); - _cfPDFToPDFNupParameters::preset(nup, param.nup); - } - - if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL) - { - if (!_cfPDFToPDFParseNupLayout(val, param.nup)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported number-up-layout %s, using number-up-layout=lrtb!", - val); - param.nup.first = pdftopdf_axis_e::X; - param.nup.xstart = pdftopdf_position_e::LEFT; - param.nup.ystart = pdftopdf_position_e::TOP; - } - } - - if ((val = cupsGetOption("page-border", num_options, options)) != NULL) - { - if (!_cfPDFToPDFParseBorder(val, param.border)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported page-border value %s, using page-border=none!", - val); - param.border = pdftopdf_border_type_e::NONE; - } - } - - if ((val=cupsGetOption("OutputOrder",num_options,options)) != NULL || - (val=cupsGetOption("output-order",num_options,options)) != NULL || - (val=cupsGetOption("page-delivery",num_options,options)) != NULL) - { - param.reverse = (strcasecmp(val, "Reverse") == 0 || - strcasecmp(val, "reverse-order") == 0); - } - else - param.reverse = cfIPPReverseOutput(printer_attrs, job_attrs); - - classification = getenv("CLASSIFICATION"); - if (classification) - rawlabel.append (classification); - - if ((val = cupsGetOption("page-label", num_options, options)) != NULL) - { - if (!rawlabel.empty()) - rawlabel.append (" - "); - rawlabel.append(cupsGetOption("page-label", num_options, options)); - } - - for (std::string::iterator it = rawlabel.begin(); - it != rawlabel.end (); - ++ it) - { - if (*it < 32 || *it > 126) - cookedlabel << "\\" << std::oct << std::setfill('0') << std::setw(3) << (unsigned int) *it; - else - cookedlabel.put (*it); - } - param.page_label = cookedlabel.str (); - - if ((val = cupsGetOption("page-set", num_options, options)) != NULL) - { - if (strcasecmp(val, "even") == 0) - param.odd_pages = false; - else if (strcasecmp(val, "odd") == 0) - param.even_pages = false; - else if (strcasecmp(val, "all") != 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported page-set value %s, using page-set=all!", - val); - } - } - - if ((val = cupsGetOption("page-ranges", num_options, options)) != NULL) - parseRanges(val, param.page_ranges); - - if ((val = cupsGetOption("input-page-ranges", num_options, options)) != NULL) - parseRanges(val, param.input_page_ranges); - - if ((val = cupsGetOption("mirror", num_options, options)) != NULL || - (val = cupsGetOption("mirror-print", num_options, options)) != NULL) - param.mirror = is_true(val); - - param.booklet = pdftopdf_booklet_mode_e::CF_PDFTOPDF_BOOKLET_OFF; - if ((val = cupsGetOption("booklet", num_options, options)) != NULL) - { - if (strcasecmp(val, "shuffle-only") == 0) - param.booklet = pdftopdf_booklet_mode_e::CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE; - else if (is_true(val)) - param.booklet = pdftopdf_booklet_mode_e::CF_PDFTOPDF_BOOKLET_ON; - else if (!is_false(val)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported booklet value %s, using booklet=off!", - val); - } - } - param.book_signature = -1; - if (optGetInt("booklet-signature", num_options, options, - ¶m.book_signature)) - { - if (param.book_signature == 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported booklet-signature value, using booklet-signature=-1 (all)!", - val); - param.book_signature = -1; - } - } - - if ((val = cupsGetOption("position", num_options, options)) != NULL) - { - if (!parsePosition(val, param.xpos, param.ypos)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unrecognized position value %s, using position=center!", - val); - param.xpos = pdftopdf_position_e::CENTER; - param.ypos = pdftopdf_position_e::CENTER; - } - } - - // Collate - if (is_true(cupsGetOption("Collate", num_options, options))) - param.collate = true; - else if ((val = cupsGetOption("sheet-collate", num_options, options)) != NULL) - param.collate = (strcasecmp(val, "uncollated") != 0); - else if (((val = cupsGetOption("multiple-document-handling", - num_options, options)) != NULL && - (strcasecmp(val, "separate-documents-collated-copies") == 0 || - strcasecmp(val, "separate-documents-uncollated-copies") == 0 || - strcasecmp(val, "single-document") == 0 || - strcasecmp(val, "single-document-new-sheet") == 0)) || - (val = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, - "multiple-document-handling")) != - NULL) - // - // This IPP attribute is unnecessarily complicated: - // single-document, separate-documents-collated-copies, - // single-document-new-sheet: - // -> collate (true) - // separate-documents-uncollated-copies: - // -> can be uncollated (false) - // - param.collate = - (strcasecmp(val, "separate-documents-uncollated-copies") != 0); - -#if 0 - // TODO: scaling - // TODO: natural-scaling - - // Scaling - if ((val = cupsGetOption("scaling", num_options, options)) != 0) - { - scaling = atoi(val) * 0.01; - fitplot = true; - } - else if (fitplot) - scaling = 1.0; - if ((val = cupsGetOption("natural-scaling", num_options, options)) != 0) - naturalScaling = atoi(val) * 0.01; -#endif - - // Make pages a multiple of two (only considered when duplex is on). - // i.e. printer has hardware-duplex, but needs pre-inserted filler pages - // FIXME? pdftopdf also supports it as cmdline option (via checkFeature()) - param.even_duplex = - (param.duplex && - is_true(cupsGetOption("even-duplex", num_options, options))); - - // TODO? pdftopdf* ? - // TODO?! pdftopdfAutoRotate - - // TODO?! Choose default by whether pdfautoratate filter has already been - // run (e.g. by mimetype) - param.auto_rotate = param.no_orientation; - if ((val = cupsGetOption("pdftopdfAutoRotate", - num_options, options)) != NULL || - (val = cupsGetOption("pdfAutoRotate", num_options, options)) != NULL) - param.auto_rotate = !is_false(val); - - if ((val = cupsGetOption("ipp-attribute-fidelity", num_options, options)) != - NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || - !strcasecmp(val, "on")) - param.fidelity = true; - } - - if (printer_attrs == NULL && !param.pagesize_requested && - param.booklet == CF_PDFTOPDF_BOOKLET_OFF && - param.nup.nupX == 1 && param.nup.nupY && 1) - // With no printer capability info and, no given page size, and no - // requirement of all pages being the same size just use the input page sizes - param.cropfit = true; - else if ((val = cupsGetOption("print-scaling", num_options, options)) != NULL) - { - // Standard IPP attribute - if (!strcasecmp(val, "auto")) - param.autoprint = true; - else if (!strcasecmp(val, "auto-fit")) - param.autofit = true; - else if (!strcasecmp(val, "fill")) - param.fillprint = true; - else if (!strcasecmp(val, "fit")) - param.fitplot = true; - else if (!strcasecmp(val, "none")) - param.cropfit = true; - else - param.autoprint = true; - } - else - { - // Legacy CUPS attributes - if ((val = cupsGetOption("fitplot", num_options, options)) == NULL) - { - if ((val = cupsGetOption("fit-to-page", num_options, options)) == NULL) - val = cupsGetOption("ipp-attribute-fidelity", num_options, options); - } - // TODO? pstops checks == "true", pdftops !is_false ... pstops says: - // fitplot only for PS (i.e. not for PDF, cmp. cgpdftopdf) - param.fitplot = (val) && (!is_false(val)); - - if ((val = cupsGetOption("fill", num_options, options)) != 0) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - param.fillprint = true; - } - if ((val = cupsGetOption("crop-to-fit", num_options, options)) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - param.cropfit=1; - } - if (!param.autoprint && !param.autofit && !param.fitplot && - !param.fillprint && !param.cropfit) - param.autoprint = true; - } - - // Certain features require a given page size for the page to be - // printed or all pages of the document being the same size. Here we - // set param.pagesize_requested so that the default page size is used - // when no size got specified by the user. - if (param.fitplot || param.fillprint || param.autoprint || param.autofit || - param.booklet != CF_PDFTOPDF_BOOKLET_OFF || - param.nup.nupX > 1 || param.nup.nupY > 1) - param.pagesize_requested = true; - - // - // Do we have to do the page logging in page_log? - // - // CUPS standard is that the last filter (not the backend, usually the - // printer driver) does page logging in the /var/log/cups/page_log file - // by outputting "PAGE: <# of current page> <# of copies>" to stderr. - // - // cfFilterPDFToPDF() would have to do this only for PDF printers as - // in this case cfFilterPDFToPDF() is the last filter, but some of - // the other filters are not able to do the logging because they do - // not have access to the number of pages of the file to be printed, - // so cfFilterPDFToPDF() overtakes their logging duty. - // - - // Check whether page logging is forced or suppressed by the options - if ((val = cupsGetOption("pdf-filter-page-logging", - num_options, options)) != NULL) - { - if (strcasecmp(val, "auto") == 0) - { - param.page_logging = -1; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Automatic page logging selected by options."); - } - else if (is_true(val)) - { - param.page_logging = 1; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Forced page logging selected by options."); - } - else if (is_false(val)) - { - param.page_logging = 0; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Suppressed page logging selected by options."); - } - else - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported page logging setting \"pdf-filter-page-logging=%s\", using \"auto\"!", - val); - param.page_logging = -1; - } - } - - if (param.page_logging == -1) - { - // We determine whether to log pages or not - // using the output data MIME type. log pages only when the output is - // either pdf or PWG Raster - if (final_content_type && - (strcasestr(final_content_type, "/pdf") || - strcasestr(final_content_type, "/vnd.cups-pdf") || - strcasestr(final_content_type, "/pwg-raster"))) - param.page_logging = 1; - else - param.page_logging = 0; - - // If final_content_type is not clearly available we are not sure whether - // to log pages or not - if ((char*)final_content_type == NULL || - sizeof(final_content_type) == 0 || - final_content_type[0] == '\0') - param.page_logging = -1; - if (doc->logfunc) - { - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Determined whether to log pages or not using output data type."); - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "final_content_type = %s => page_logging = %d", - final_content_type ? final_content_type : "NULL", - param.page_logging); - } - if (param.page_logging == -1) - param.page_logging = 0; - } -} -// }}} - -void -calculate(int num_options, - cups_option_t *options, - _cfPDFToPDFProcessingParameters ¶m, - char *final_content_type) // {{{ -{ - const char *val; - bool hw_copies = false, - hw_collate = false; - - - // Check options for caller's instructions about hardware copies/collate - if ((val = cupsGetOption("hardware-copies", - num_options, options)) != NULL) - // Use hardware copies according to the caller's instructions - hw_copies = is_true(val); - else - // Caller did not tell us whether the printer does Hardware copies - // or not, so we assume hardware copies on PDF printers, and software - // copies on other (usually raster) printers or if we do not know the - // final output format. - hw_copies = (final_content_type && - (strcasestr(final_content_type, "/pdf") || - strcasestr(final_content_type, "/vnd.cups-pdf"))); - if (hw_copies) - { - if ((val = cupsGetOption("hardware-collate", - num_options, options)) != NULL) - // Use hardware collate according to the caller's instructions - hw_collate = is_true(val); - else - // Check output format MIME type whether it is - // of a driverless IPP printer (PDF, Apple Raster, PWG Raster, PCLm). - // These printers do always hardware collate if they do hardware copies. - // https://github.com/apple/cups/issues/5433 - hw_collate = (final_content_type && - (strcasestr(final_content_type, "/pdf") || - strcasestr(final_content_type, "/vnd.cups-pdf") || - strcasestr(final_content_type, "/pwg-raster") || - strcasestr(final_content_type, "/urf") || - strcasestr(final_content_type, "/PCLm"))); - } - - if (param.reverse && param.duplex) - // Enable even_duplex or the first page may be empty. - param.even_duplex = true; // disabled later, if non-duplex - - if (param.num_copies == 1) - { - param.device_copies = 1; - // collate is never needed for a single copy - param.collate = false; // (does not make a big difference for us) - } - else if (hw_copies) - { // hw copy generation available - param.device_copies = param.num_copies; - if (param.collate) - { // collate requested by user - param.device_collate = hw_collate; - if (!param.device_collate) - // printer can't hw collate -> we must copy collated in sw - param.device_copies = 1; - } // else: printer copies w/o collate and takes care of duplex/even_duplex - } - else - { // sw copies - param.device_copies = 1; - if (param.duplex) - { // &&(num_copies>1) - // sw collate + even_duplex must be forced to prevent copies on the - // back sides - param.collate = true; - param.device_collate = false; - } - } - - if (param.device_copies != 1) // hw copy - param.num_copies = 1; // disable sw copy - - if (param.duplex && - param.collate && !param.device_collate) // software collate - param.even_duplex = true; // fillers always needed - - if (!param.duplex) - param.even_duplex = false; -} -// }}} - -// reads from stdin into temporary file. returns FILE * or NULL on error -FILE * -copy_fd_to_temp(int infd, - pdftopdf_doc_t *doc) // {{{ -{ - char buf[BUFSIZ]; - int n; - - // FIXME: what does >buf mean here? - int outfd = cupsCreateTempFd(NULL, NULL, buf, sizeof(buf)); - if (outfd < 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Can't create temporary file"); - return (NULL); - } - // remove name - unlink(buf); - - // copy stdin to the tmp file - while ((n = read(infd, buf, BUFSIZ)) > 0) - { - if (write(outfd, buf, n) != n) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Can't copy stdin to temporary file"); - close(outfd); - return (NULL); - } - } - if (lseek(outfd, 0, SEEK_SET) < 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Can't rewind temporary file"); - close(outfd); - return (NULL); - } - - FILE *f; - if ((f = fdopen(outfd, "rb")) == 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Can't fdopen temporary file"); - close(outfd); - return (NULL); - } - return f; -} -// }}} - -// check whether a given file is empty -bool is_empty(FILE *f) // {{{ -{ - char buf[1]; - - // Try to read a single byte of data - if (fread(buf, 1, 1, f) == 0) - return (true); - - rewind(f); - - return (false); -} -// }}} - - -int // O - Error status -cfFilterPDFToPDF(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters - // (unused) -{ - pdftopdf_doc_t doc; // Document information - char *final_content_type = data->final_content_type; - FILE *inputfp, - *outputfp; - const char *t; - int streaming = 0; - size_t bytes; - char buf[BUFSIZ]; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - int num_options = 0; - cups_option_t *options = NULL; - - - try - { - _cfPDFToPDFProcessingParameters param; - - param.job_id = data->job_id; - param.user = data->job_user; - param.title = data->job_title; - param.num_copies = data->copies; - param.copies_to_be_logged = data->copies; - param.page.width = param.page.height = 0; - param.page.left = param.page.bottom = -1; - param.page.right = param.page.top = -1; - - // TODO?! sanity checks - - doc.logfunc = log; - doc.logdata = ld; - doc.iscanceledfunc = iscanceled; - doc.iscanceleddata = icd; - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - getParameters(data, num_options, options, param, &doc); - - calculate(num_options, options, param, final_content_type); - -#ifdef DEBUG - param.dump(&doc); -#endif - - // If we are in streaming mode we only apply JCL and do not run the - // job through QPDL (so no page management, form flattening, - // page size/orientation adjustment, ...) - if ((t = cupsGetOption("filter-streaming-mode", - num_options, options)) != NULL && - (strcasecmp(t, "false") && strcasecmp(t, "off") && - strcasecmp(t, "no"))) - { - streaming = 1; - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Streaming mode: No PDF processing, only adding of JCL"); - } - - cupsFreeOptions(num_options, options); - - std::unique_ptr<_cfPDFToPDFProcessor> proc(_cfPDFToPDFFactory::processor()); - - if ((inputseekable && inputfd > 0) || streaming) - { - if ((inputfp = fdopen(inputfd, "rb")) == NULL) - return (1); - } - else - { - if ((inputfp = copy_fd_to_temp(inputfd, &doc)) == NULL) - return (1); - } - - if (!streaming) - { - if (is_empty(inputfp)) - { - fclose(inputfp); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Input is empty, outputting empty file."); - return (0); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Processing PDF input with QPDF: Page-ranges, page-set, number-up, booklet, size adjustment, ..."); - - // Load the PDF input data into QPDF - if (!proc->load_file(inputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE, 1)) - { - fclose(inputfp); - return (1); - } - - // Process the PDF input data - if (!_cfProcessPDFToPDF(*proc, param, &doc)) - return (2); - - // Pass information to subsequent filters via PDF comments - std::vector output; - - output.push_back("% This file was generated by pdftopdf"); - - // This is not standard, but like PostScript. - if (param.device_copies > 0) - { - char buf[256]; - snprintf(buf, sizeof(buf), "%d", param.device_copies); - output.push_back(std::string("%%PDFTOPDFNumCopies : ")+buf); - - if (param.device_collate) - output.push_back("%%PDFTOPDFCollate : true"); - else - output.push_back("%%PDFTOPDFCollate : false"); - } - - proc->set_comments(output); - } - - outputfp = fdopen(outputfd, "w"); - if (outputfp == NULL) - return (1); - - if (!streaming) - { - // Pass on the processed input data - proc->emit_file(outputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE); - // proc->emit_filename(NULL); - } - else - { - // Pass through the input data - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Passing on unchanged PDF data from input"); - while ((bytes = fread(buf, 1, sizeof(buf), inputfp)) > 0) - if (fwrite(buf, 1, bytes, outputfp) != bytes) - break; - fclose(inputfp); - } - - fclose(outputfp); - } - catch (std::exception &e) - { - // TODO? exception type - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Exception: %s", e.what()); - return (5); - } - catch (...) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unknown exception caught. Exiting."); - return (6); - } - - return (0); -} diff --git a/cupsfilters/pdftopdf/pptypes-private.h b/cupsfilters/pdftopdf/pptypes-private.h deleted file mode 100644 index 0cacf8e0..00000000 --- a/cupsfilters/pdftopdf/pptypes-private.h +++ /dev/null @@ -1,73 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ -#define _CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ - -#include "pdftopdf-private.h" -#include // NAN - -// namespace PPTypes {} TODO? - -enum pdftopdf_axis_e { X, Y }; -enum pdftopdf_position_e { // PS order - CENTER = 0, - LEFT = -1, - RIGHT = 1, - TOP = 1, - BOTTOM = -1 -}; - -void _cfPDFToPDFPositionDump(pdftopdf_position_e pos, pdftopdf_doc_t *doc); -void _cfPDFToPDFPositionDump(pdftopdf_position_e pos, pdftopdf_axis_e axis, - pdftopdf_doc_t *doc); - -enum pdftopdf_rotation_e { ROT_0, ROT_90, ROT_180, ROT_270 }; // CCW - -void _cfPDFToPDFRotationDump(pdftopdf_rotation_e rot, pdftopdf_doc_t *doc); -pdftopdf_rotation_e operator+(pdftopdf_rotation_e lhs, pdftopdf_rotation_e rhs); -pdftopdf_rotation_e operator-(pdftopdf_rotation_e lhs, pdftopdf_rotation_e rhs); -pdftopdf_rotation_e operator-(pdftopdf_rotation_e rhs); -//pdftopdf_rotation_e operator+=(pdftopdf_rotation_e &lhs, -// pdftopdf_rotation_e rhs); - -enum pdftopdf_border_type_e { - NONE = 0, - ONE_THIN = 2, - ONE_THICK = 3, - TWO_THIN = 4, - TWO_THICK = 5, - ONE = 0x02, - TWO = 0x04, - THICK = 0x01 -}; - -void _cfPDFToPDFBorderTypeDump(pdftopdf_border_type_e border, - pdftopdf_doc_t *doc); - -struct _cfPDFToPDFPageRect { -_cfPDFToPDFPageRect() - : top(NAN), - left(NAN), - right(NAN), - bottom(NAN), - width(NAN), - height(NAN) {} - float top, left, right, bottom; // i.e. margins - float width, height; - - void rotate_move(pdftopdf_rotation_e r, float pwidth, float pheight); - // pwidth original "page size" (i.e. before rotation) - void scale(float mult); - void translate(float tx, float ty); - - void set(const _cfPDFToPDFPageRect &rhs); // only for rhs.* != NAN - void dump(pdftopdf_doc_t *doc) const; -}; - -// bool _cfPDFToPDFParseBorder(const char *val,pdftopdf_border_type_e &ret); -// // none, single, ..., double-thick - -#endif // !_CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ diff --git a/cupsfilters/pdftopdf/pptypes.cxx b/cupsfilters/pdftopdf/pptypes.cxx deleted file mode 100644 index f8158b48..00000000 --- a/cupsfilters/pdftopdf/pptypes.cxx +++ /dev/null @@ -1,238 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "pptypes-private.h" -#include -#include -#include "cupsfilters/debug-internal.h" - -void -_cfPDFToPDFPositionDump(pdftopdf_position_e pos, - pdftopdf_doc_t *doc) // {{{ -{ - static const char *pstr[3] = {"Left/Bottom", "Center", "Right/Top"}; - if ((pos < LEFT) || (pos > RIGHT)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: (bad position: %d)", pos); - } - else - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: %s", pstr[pos+1]); - } -} -// }}} - -void -_cfPDFToPDFPositionDump(pdftopdf_position_e pos, - pdftopdf_axis_e axis, - pdftopdf_doc_t *doc) // {{{ -{ - DEBUG_assert((axis == pdftopdf_axis_e::X) || (axis == pdftopdf_axis_e::Y)); - if ((pos < LEFT) || (pos > RIGHT)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Position %s: (bad position: %d)", - (axis == pdftopdf_axis_e::X) ? "X" : "Y", pos); - return; - } - if (axis == pdftopdf_axis_e::X) - { - static const char *pxstr[3] = {"Left", "Center", "Right"}; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Position X: %s", pxstr[pos+1]); - } - else - { - static const char *pystr[3] = {"Bottom", "Center", "Top"}; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Position Y: %s", pystr[pos+1]); - } -} -// }}} - -void -_cfPDFToPDFRotationDump(pdftopdf_rotation_e rot, - pdftopdf_doc_t *doc) // {{{ -{ - static const char *rstr[4] = {"0 deg", "90 deg", "180 deg", "270 deg"}; // CCW - if ((rot < ROT_0) || (rot > ROT_270)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Rotation(CCW): (bad rotation: %d)", rot); - } - else - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Rotation(CCW): %s", rstr[rot]); - } -} -// }}} - -pdftopdf_rotation_e -operator+(pdftopdf_rotation_e lhs, - pdftopdf_rotation_e rhs) // {{{ -{ - return (pdftopdf_rotation_e)(((int)lhs + (int)rhs) % 4); -} -// }}} - -pdftopdf_rotation_e -operator-(pdftopdf_rotation_e lhs, - pdftopdf_rotation_e rhs) // {{{ -{ - return (pdftopdf_rotation_e)((((int)lhs - (int)rhs) % 4 + 4) % 4); -} -// }}} - -pdftopdf_rotation_e -operator-(pdftopdf_rotation_e rhs) // {{{ -{ - return (pdftopdf_rotation_e)((4 - (int)rhs) % 4); -} -// }}} - -void -_cfPDFToPDFBorderTypeDump(pdftopdf_border_type_e border, - pdftopdf_doc_t *doc) // {{{ -{ - if ((border < NONE) || (border == 1) || (border > TWO_THICK)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Border: (bad border: %d)", border); - } - else - { - static const char *bstr[6] = - {"None", NULL, "one thin", "one thick", "two thin", "two thick"}; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Border: %s", bstr[border]); - } -} -// }}} - -void -_cfPDFToPDFPageRect::rotate_move(pdftopdf_rotation_e r, - float pwidth, - float pheight) // {{{ -{ -#if 1 - if (r >= ROT_180) - { - std::swap(top, bottom); - std::swap(left, right); - } - if ((r == ROT_90) || (r == ROT_270)) - { - const float tmp = bottom; - bottom = left; - left = top; - top = right; - right = tmp; - - std::swap(width, height); - std::swap(pwidth, pheight); - } - if ((r == ROT_90) || (r == ROT_180)) - { - left = pwidth - left; - right = pwidth - right; - } - if ((r == ROT_270) || (r == ROT_180)) - { - top = pheight - top; - bottom = pheight - bottom; - } -#else - switch (r) - { - case ROT_0: // no-op - break; - case ROT_90: - const float tmp0 = bottom; - bottom = left; - left = pheight - top; - top = right; - right = pheight - tmp0; - - std::swap(width, height); - break; - case ROT_180: - const float tmp1 = left; - left = pwidth - right; - right = pwidth - tmp1; - - const float tmp2 = top; - top = pheight - bottom; - bottom = pheight - tmp2; - break; - case ROT_270: - const float tmp3 = top; - top = pwidth - left; - left = bottom; - bottom = pwidth - right; - right = tmp3; - - std::swap(width, height); - break; - } -#endif -} -// }}} - -void -_cfPDFToPDFPageRect::scale(float mult) // {{{ -{ - if (mult == 1.0) - return; - - DEBUG_assert(mult != 0.0); - - bottom *= mult; - left *= mult; - top *= mult; - right *= mult; - - width *= mult; - height *= mult; -} -// }}} - -void -_cfPDFToPDFPageRect::translate(float tx, - float ty) // {{{ -{ - left += tx; - bottom += ty; - right += tx; - top += ty; -} -// }}} - -void -_cfPDFToPDFPageRect::set(const _cfPDFToPDFPageRect &rhs) // {{{ -{ - if (!std::isnan(rhs.top)) - top = rhs.top; - if (!std::isnan(rhs.left)) - left = rhs.left; - if (!std::isnan(rhs.right)) - right = rhs.right; - if (!std::isnan(rhs.bottom)) - bottom = rhs.bottom; -} -// }}} - -void -_cfPDFToPDFPageRect::dump(pdftopdf_doc_t *doc) const // {{{ -{ - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: top: %f, left: %f, right: %f, bottom: %f, " - "width: %f, height: %f", - top, left, right, bottom, - width, height); -} -// }}} diff --git a/cupsfilters/pdftopdf/qpdf-cm-private.h b/cupsfilters/pdftopdf/qpdf-cm-private.h deleted file mode 100644 index 4f22e468..00000000 --- a/cupsfilters/pdftopdf/qpdf-cm-private.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_QPDF_CM_H_ -#define _CUPS_FILTERS_PDFTOPDF_QPDF_CM_H_ - -#include - -bool _cfPDFToPDFHasOutputIntent(QPDF &pdf); -void _cfPDFToPDFAddOutputIntent(QPDF &pdf, const char *filename); - -void _cfPDFToPDFAddDefaultRGB(QPDF &pdf, QPDFObjectHandle srcicc); -QPDFObjectHandle _cfPDFToPDFSetDefaultICC(QPDF &pdf, const char *filename); - -#endif // !_CUPS_FILTERS_PDFTOPDF_QPDF_CM_H_ diff --git a/cupsfilters/pdftopdf/qpdf-cm.cxx b/cupsfilters/pdftopdf/qpdf-cm.cxx deleted file mode 100644 index 238a6b7d..00000000 --- a/cupsfilters/pdftopdf/qpdf-cm.cxx +++ /dev/null @@ -1,181 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "qpdf-cm-private.h" -#include -#include "cupsfilters/debug-internal.h" -#include - -#include - -// TODO? instead use qpdf's StreamDataProvider, FileInputSource, Buffer etc. -static std::string -load_file(const char *filename) // {{{ -{ - if (!filename) - throw std::invalid_argument("NULL filename not allowed"); - - FILE *f = fopen(filename, "r"); - if (!f) - throw std::runtime_error(std::string("file ") + filename + " could not be opened"); - - const int bsize = 2048; - unsigned int pos = 0; - - std::string ret; - while (!feof(f)) - { - if ((UINT_MAX - bsize) < pos) - { - ret.resize(pos); - break; - } - - ret.resize(pos + bsize); - int res = fread(&ret[pos], 1, bsize, f); - - if ((UINT_MAX - res) < pos) - { - ret.resize(pos); - break; - } - - pos += res; - if (res < bsize) - { - ret.resize(pos); - break; - } - } - - fclose(f); - return (ret); -} -// }}} - - -// TODO? -// TODO? test -bool -_cfPDFToPDFHasOutputIntent(QPDF &pdf) // {{{ -{ - auto catalog = pdf.getRoot(); - if (!catalog.hasKey("/OutputIntents")) - return (false); - return (true); // TODO? -} -// }}} - -// TODO: test -// TODO? find existing , replace and return (?) -void -_cfPDFToPDFAddOutputIntent(QPDF &pdf, - const char *filename) // {{{ -{ - std::string icc = load_file(filename); - // TODO: check icc fitness - // ICC data, subject to "version limitations" per pdf version... - - QPDFObjectHandle outicc = QPDFObjectHandle::newStream(&pdf, icc); - - auto sdict = outicc.getDict(); - sdict.replaceKey("/N",QPDFObjectHandle::newInteger(4)); // must match ICC - // /Range ? // must match ICC, default [0.0 1.0 ...] - // /Alternate ? (/DeviceCMYK for N=4) - - auto intent = QPDFObjectHandle::parse( - "<<" - " /Type /OutputIntent" // Must be so (the standard requires). - " /S /GTS_PDFX" // Must be so (the standard requires). - " /OutputCondition (Commercial and specialty printing)" // TODO: Customize [optional(?)] - " /Info (none)" // TODO: Customize - " /OutputConditionIdentifier (CGATS TR001)" // TODO: FIXME: Customize - " /RegistryName (http://www.color.org)" // Must be so (the standard requires). - " /DestOutputProfile null " - ">>"); - intent.replaceKey("/DestOutputProfile", outicc); - - auto catalog = pdf.getRoot(); - if (!catalog.hasKey("/OutputIntents")) - catalog.replaceKey("/OutputIntents", QPDFObjectHandle::newArray()); - catalog.getKey("/OutputIntents").appendItem(intent); -} -// }}} - -// -// for color management: -// Use /DefaultGray, /DefaultRGB, /DefaultCMYK ... from *current* resource -// dictionary ... -// i.e. set -// /Resources << -// /ColorSpace << --- can use just one indirect ref for this (probably) -// /DefaultRGB [/ICCBased 5 0 R] ... sensible use is sRGB for DefaultRGB, etc. -// >> -// >> -// for every page (what with form /XObjects?) and most importantly RGB -// (leave CMYK, Gray for now, as this is already printer native(?)) -// -// ? also every form XObject, pattern, type3 font, annotation appearance -// stream(=form xobject +X) -// -// ? what if page already defines /Default? -- probably keep! -// -// ? maybe we need to set /ColorSpace in /Images ? -// [gs idea is to just add the /Default-key and then reprocess...] -// - - -// TODO? test -void -_cfPDFToPDFAddDefaultRGB(QPDF &pdf, QPDFObjectHandle srcicc) // {{{ -{ - srcicc.assertStream(); - - auto pages = pdf.getAllPages(); - for (auto it = pages.begin(), end = pages.end(); it != end; ++ it) - { - if (!it->hasKey("/Resources")) - it->replaceKey("/Resources", QPDFObjectHandle::newDictionary()); - - auto rdict = it->getKey("/Resources"); - - if (!rdict.hasKey("/ColorSpace")) - rdict.replaceKey("/ColorSpace", QPDFObjectHandle::newDictionary()); - - auto cdict = rdict.getKey("/ColorSpace"); - - if (!cdict.hasKey("/DefaultRGB")) - { - cdict.replaceKey("/DefaultRGB", QPDFObjectHandle::parse("[/ICCBased ]")); - cdict.getKey("/DefaultRGB").appendItem(srcicc); - } - } -} -// }}} - -// TODO? test -// TODO: find existing , replace and return (?) -// TODO: check icc fitness -QPDFObjectHandle -_cfPDFToPDFSetDefaultICC(QPDF &pdf, - const char *filename) // {{{ -{ - // TODO: find existing, replace and return (?) - - std::string icc = load_file(filename); - // TODO: check icc fitness - // ICC data, subject to "version limitations" per pdf version... - - QPDFObjectHandle ret = QPDFObjectHandle::newStream(&pdf, icc); - - auto sdict = ret.getDict(); - sdict.replaceKey("/N", QPDFObjectHandle::newInteger(3)); // must match ICC - // /Range ? // must match ICC, default [0.0 1.0 ...] - // /Alternate ? (/DeviceRGB for N=3) - - return ret; -} -// }}} - diff --git a/cupsfilters/pdftopdf/qpdf-pdftopdf-private.h b/cupsfilters/pdftopdf/qpdf-pdftopdf-private.h deleted file mode 100644 index 1146a8fe..00000000 --- a/cupsfilters/pdftopdf/qpdf-pdftopdf-private.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_QPDF_PDFTOPDF_H -#define _CUPS_FILTERS_PDFTOPDF_QPDF_PDFTOPDF_H - -#include -#include "pptypes-private.h" - -// helper functions - -_cfPDFToPDFPageRect _cfPDFToPDFGetBoxAsRect(QPDFObjectHandle box); -QPDFObjectHandle _cfPDFToPDFGetRectAsBox(const _cfPDFToPDFPageRect &rect); - -// Note that PDF specification is CW, but our Rotation is CCW -pdftopdf_rotation_e _cfPDFToPDFGetRotate(QPDFObjectHandle page); -QPDFObjectHandle _cfPDFToPDFMakeRotate(pdftopdf_rotation_e rot); // Integer - -double _cfPDFToPDFGetUserUnit(QPDFObjectHandle page); - -// PDF CTM -class _cfPDFToPDFMatrix { - public: - _cfPDFToPDFMatrix(); // identity - _cfPDFToPDFMatrix(QPDFObjectHandle ar); - - _cfPDFToPDFMatrix &rotate(pdftopdf_rotation_e rot); - _cfPDFToPDFMatrix &rotate_move(pdftopdf_rotation_e rot, double width, - double height); - _cfPDFToPDFMatrix &rotate(double rad); - // _cfPDFToPDFMatrix &rotate_deg(double deg); - - _cfPDFToPDFMatrix &translate(double tx, double ty); - _cfPDFToPDFMatrix &scale(double sx, double sy); - _cfPDFToPDFMatrix &scale(double s) { return (scale(s, s)); } - - _cfPDFToPDFMatrix &operator*=(const _cfPDFToPDFMatrix &rhs); - - QPDFObjectHandle get() const; - std::string get_string() const; - private: - double ctm[6]; -}; - -#endif // !_CUPS_FILTERS_PDFTOPDF_QPDF_PDFTOPDF_H diff --git a/cupsfilters/pdftopdf/qpdf-pdftopdf-processor-private.h b/cupsfilters/pdftopdf/qpdf-pdftopdf-processor-private.h deleted file mode 100644 index c8041205..00000000 --- a/cupsfilters/pdftopdf/qpdf-pdftopdf-processor-private.h +++ /dev/null @@ -1,99 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_QPDF_PDFTOPDF_PROCESSOR_H -#define _CUPS_FILTERS_PDFTOPDF_QPDF_PDFTOPDF_PROCESSOR_H - -#include "pdftopdf-processor-private.h" -#include - -class _cfPDFToPDFQPDFPageHandle : public _cfPDFToPDFPageHandle { - public: - virtual _cfPDFToPDFPageRect get_rect() const; - virtual void add_border_rect(const _cfPDFToPDFPageRect &rect, - pdftopdf_border_type_e border, float fscale); - virtual void add_subpage(const std::shared_ptr<_cfPDFToPDFPageHandle> &sub, - float xpos, float ypos, float scale, - const _cfPDFToPDFPageRect *crop=NULL); - virtual void mirror(); - virtual void rotate(pdftopdf_rotation_e rot); - virtual void add_label(const _cfPDFToPDFPageRect &rect, - const std::string label); - virtual pdftopdf_rotation_e crop(const _cfPDFToPDFPageRect &cropRect, - pdftopdf_rotation_e orientation, - pdftopdf_rotation_e param_orientation, - pdftopdf_position_e xpos, - pdftopdf_position_e ypos, - bool scale, bool autorotate, - pdftopdf_doc_t *doc); - virtual bool is_landscape(pdftopdf_rotation_e orientation); - void debug(const _cfPDFToPDFPageRect &rect, float xpos, float ypos); - private: - bool is_existing() const; - QPDFObjectHandle get(); // only once! - private: - friend class _cfPDFToPDFQPDFProcessor; - // 1st mode: existing - _cfPDFToPDFQPDFPageHandle(QPDFObjectHandle page, int orig_no = -1); - QPDFObjectHandle page; - int no; - - // 2nd mode: create new - _cfPDFToPDFQPDFPageHandle(QPDF *pdf, float width, float height); - std::map xobjs; - std::string content; - - pdftopdf_rotation_e rotation; -}; - -class _cfPDFToPDFQPDFProcessor : public _cfPDFToPDFProcessor { - public: - virtual bool load_file(FILE *f,pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e - take = CF_PDFTOPDF_WILL_STAY_ALIVE, - int flatten_forms = 1); - virtual bool load_filename(const char *name, - pdftopdf_doc_t *doc, int flatten_forms = 1); - - // TODO: virtual bool may_modify/may_print/? - virtual bool check_print_permissions(pdftopdf_doc_t *doc); - - // virtual bool set_process(const _cfPDFToPDFProcessingParameters ¶m) = 0; - - virtual std::vector> - get_pages(pdftopdf_doc_t *doc); - virtual std::shared_ptr<_cfPDFToPDFPageHandle> new_page(float width, - float height, - pdftopdf_doc_t *doc); - - virtual void add_page(std::shared_ptr<_cfPDFToPDFPageHandle> page, - bool front); - - virtual void multiply(int copies, bool collate); - - virtual void auto_rotate_all(bool dst_lscape, - pdftopdf_rotation_e normal_landscape); - virtual void add_cm(const char *defaulticc, const char *outputicc); - - virtual void set_comments(const std::vector &comments); - - virtual void emit_file(FILE *dst, pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e - take = CF_PDFTOPDF_WILL_STAY_ALIVE); - virtual void emit_filename(const char *name, pdftopdf_doc_t *doc); - - virtual bool has_acro_form(); - private: - void close_file(); - void start(int flatten_forms); - private: - std::unique_ptr pdf; - std::vector orig_pages; - - bool hasCM; - std::string extraheader; -}; - -#endif // !_CUPS_FILTERS_PDFTOPDF_QPDF_PDFTOPDF_PROCESSOR_H diff --git a/cupsfilters/pdftopdf/qpdf-pdftopdf-processor.cxx b/cupsfilters/pdftopdf/qpdf-pdftopdf-processor.cxx deleted file mode 100644 index 25c48bbb..00000000 --- a/cupsfilters/pdftopdf/qpdf-pdftopdf-processor.cxx +++ /dev/null @@ -1,920 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include -#include "cupsfilters/debug-internal.h" -#include -#include -#include -#include -#include -#include "qpdf-tools-private.h" -#include "qpdf-xobject-private.h" -#include "qpdf-pdftopdf-private.h" -#include "qpdf-pdftopdf-processor-private.h" -#include "qpdf-cm-private.h" -#include "pdftopdf-private.h" - -// Use: content.append(debug_box(pe.sub, xpos, ypos)); -static std::string -debug_box(const _cfPDFToPDFPageRect &box, - float xshift, - float yshift) // {{{ -{ - return (std::string("q 1 w 0.1 G\n ") + - QUtil::double_to_string(box.left + xshift) + " " + - QUtil::double_to_string(box.bottom + yshift) + " m " + - QUtil::double_to_string(box.right + xshift) + " " + - QUtil::double_to_string(box.top + yshift) + " l " + "S \n " + - - QUtil::double_to_string(box.right + xshift) + " " + - QUtil::double_to_string(box.bottom + yshift) + " m " + - QUtil::double_to_string(box.left + xshift) + " " + - QUtil::double_to_string(box.top + yshift) + " l " + "S \n " + - - QUtil::double_to_string(box.left + xshift) + " " + - QUtil::double_to_string(box.bottom + yshift) + " " + - QUtil::double_to_string(box.right - box.left) + " " + - QUtil::double_to_string(box.top - box.bottom) + " re " + "S Q\n"); -} -// }}} - -_cfPDFToPDFQPDFPageHandle::_cfPDFToPDFQPDFPageHandle(QPDFObjectHandle page, - int orig_no) // {{{ - : page(page), - no(orig_no), - rotation(ROT_0) -{ -} -// }}} - -_cfPDFToPDFQPDFPageHandle::_cfPDFToPDFQPDFPageHandle(QPDF *pdf, - float width, - float height) // {{{ - : no(0), - rotation(ROT_0) -{ - DEBUG_assert(pdf); - page = QPDFObjectHandle::parse( - "<<" - " /Type /Page" - " /Resources <<" - " /XObject null " - " >>" - " /MediaBox null " - " /Contents null " - ">>"); - page.replaceKey("/MediaBox", _cfPDFToPDFMakeBox(0, 0, width, height)); - page.replaceKey("/Contents", QPDFObjectHandle::newStream(pdf)); - // xobjects: later (in get()) - content.assign("q\n"); // TODO? different/not needed - - page = pdf->makeIndirectObject(page); // stores *pdf -} -// }}} - -// Note: _cfPDFToPDFProcessor always works with "/Rotate"d and "/UserUnit"-scaled pages/coordinates/..., having 0,0 at left,bottom of the TrimBox -_cfPDFToPDFPageRect -_cfPDFToPDFQPDFPageHandle::get_rect() const // {{{ -{ - page.assertInitialized(); - _cfPDFToPDFPageRect ret = - _cfPDFToPDFGetBoxAsRect(_cfPDFToPDFGetTrimBox(page)); - ret.translate(-ret.left, -ret.bottom); - ret.rotate_move(_cfPDFToPDFGetRotate(page), ret.width, ret.height); - ret.scale(_cfPDFToPDFGetUserUnit(page)); - return (ret); -} -// }}} - -bool -_cfPDFToPDFQPDFPageHandle::is_existing() const // {{{ -{ - page.assertInitialized(); - return (content.empty()); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFQPDFPageHandle::get() // {{{ -{ - QPDFObjectHandle ret = page; - - if (!is_existing()) - { // finish up page - page.getKey("/Resources").replaceKey("/XObject", - QPDFObjectHandle::newDictionary(xobjs)); - content.append("Q\n"); - page.getKey("/Contents").replaceStreamData(content, - QPDFObjectHandle::newNull(), - QPDFObjectHandle::newNull()); - page.replaceKey("/Rotate", _cfPDFToPDFMakeRotate(rotation)); - } - else - { - pdftopdf_rotation_e rot = _cfPDFToPDFGetRotate(page) + rotation; - page.replaceKey("/Rotate", _cfPDFToPDFMakeRotate(rot)); - } - page = QPDFObjectHandle(); // i.e. uninitialized - return (ret); -} -// }}} - -// TODO: We probably need a function "ungetRect()" to transform to page/form -// space, as member -static _cfPDFToPDFPageRect -ungetRect(_cfPDFToPDFPageRect rect, - const _cfPDFToPDFQPDFPageHandle &ph, - pdftopdf_rotation_e rotation, - QPDFObjectHandle page) -{ - _cfPDFToPDFPageRect pg1 = ph.get_rect(); - _cfPDFToPDFPageRect pg2 = - _cfPDFToPDFGetBoxAsRect(_cfPDFToPDFGetTrimBox(page)); - - // we have to invert /Rotate, /UserUnit and the left,bottom (TrimBox) - // translation - //_cfPDFToPDFRotationDump(rotation); - //_cfPDFToPDFRotationDump(_cfPDFToPDFGetRotate(page)); - rect.width = pg1.width; - rect.height = pg1.height; - //std::swap(rect.width, rect.height); - //rect.rotate_move(-rotation, rect.width, rect.height); - - rect.rotate_move(-_cfPDFToPDFGetRotate(page), pg1.width, pg1.height); - rect.scale(1.0 / _cfPDFToPDFGetUserUnit(page)); - - //_cfPDFToPDFPageRect pg2=_cfPDFToPDFGetBoxAsRect(_cfPDFToPDFGetTrimBox(page)); - rect.translate(pg2.left, pg2.bottom); - //rect.dump(); - - return (rect); -} - -// TODO FIXME rotations are strange ... (via ungetRect) -// TODO? for non-existing (either drop comment or facility to create split -// streams needed) -void -_cfPDFToPDFQPDFPageHandle::add_border_rect(const _cfPDFToPDFPageRect &_rect, - pdftopdf_border_type_e border, - float fscale) // {{{ -{ - DEBUG_assert(is_existing()); - DEBUG_assert(border != pdftopdf_border_type_e::NONE); - - // straight from pstops - const double lw = (border & THICK) ? 0.5 : 0.24; - double line_width = lw * fscale; - double margin = 2.25 * fscale; - // (PageLeft + margin, PageBottom + margin) - // rect (PageRight - PageLeft - 2 * margin, ...) ... - // for nup > 1: PageLeft = 0, etc. - // if (double) margin += 2 * fscale ...rect... - - _cfPDFToPDFPageRect rect = ungetRect(_rect, *this, rotation, page); - - DEBUG_assert(rect.left <= rect.right); - DEBUG_assert(rect.bottom <= rect.top); - - std::string boxcmd="q\n"; - boxcmd += " " +QUtil::double_to_string(line_width) + " w 0 G \n"; - boxcmd += " " +QUtil::double_to_string(rect.left + margin) + " " + - QUtil::double_to_string(rect.bottom + margin) + " " + - QUtil::double_to_string(rect.right - rect.left - 2 * margin) + " " + - QUtil::double_to_string(rect.top - rect.bottom - 2 * margin) + " re S \n"; - if (border & TWO) - { - margin += 2 * fscale; - boxcmd += " " + QUtil::double_to_string(rect.left + margin) + " " + - QUtil::double_to_string(rect.bottom + margin) + " " + - QUtil::double_to_string(rect.right - rect.left - 2 * margin) + " " + - QUtil::double_to_string(rect.top - rect.bottom - 2 * margin) + " re S \n"; - } - boxcmd += "Q\n"; - - // if (!is_existing()) - // // TODO: only after - // return; - - DEBUG_assert(page.getOwningQPDF()); // existing pages are always indirect -#ifdef DEBUG // draw it on top - static const char *pre = "%pdftopdf q\n" - "q\n", - *post = "%pdftopdf Q\n" - "Q\n"; - - QPDFObjectHandle stm1 = QPDFObjectHandle::newStream(page.getOwningQPDF(), - pre), - stm2 = QPDFObjectHandle::newStream(page.getOwningQPDF(), - std::string(post) + - boxcmd); - - page.addPageContents(stm1, true); // before - page.addPageContents(stm2, false); // after -#else - QPDFObjectHandle stm = QPDFObjectHandle::newStream(page.getOwningQPDF(), - boxcmd); - page.addPageContents(stm, true); // before -#endif -} -// }}} - - -// -// This crop function is written for print-scaling=fill option. -// Trim Box is used for trimming the page in required size. -// scale tells if we need to scale input file. -// - -pdftopdf_rotation_e -_cfPDFToPDFQPDFPageHandle::crop(const _cfPDFToPDFPageRect &cropRect, - pdftopdf_rotation_e orientation, - pdftopdf_rotation_e param_orientation, - pdftopdf_position_e xpos, - pdftopdf_position_e ypos, - bool scale, - bool autorotate, - pdftopdf_doc_t *doc) -{ - page.assertInitialized(); - pdftopdf_rotation_e save_rotate = _cfPDFToPDFGetRotate(page); - if (orientation == ROT_0 || orientation == ROT_180) - page.replaceKey("/Rotate", _cfPDFToPDFMakeRotate(ROT_90)); - else - page.replaceKey("/Rotate", _cfPDFToPDFMakeRotate(ROT_0)); - - _cfPDFToPDFPageRect currpage = - _cfPDFToPDFGetBoxAsRect(_cfPDFToPDFGetTrimBox(page)); - double width = currpage.right - currpage.left; - double height = currpage.top - currpage.bottom; - double pageWidth = cropRect.right - cropRect.left; - double pageHeight = cropRect.top - cropRect.bottom; - double final_w, final_h; //Width and height of cropped image. - - pdftopdf_rotation_e pageRot = _cfPDFToPDFGetRotate(page); - if ((autorotate && - (((pageRot == ROT_0 || pageRot == ROT_180) && - pageWidth <= pageHeight) || - ((pageRot == ROT_90 || pageRot == ROT_270) && - pageWidth > pageHeight))) || - (!autorotate && - (param_orientation == ROT_90 || param_orientation == ROT_270))) - std::swap(pageHeight, pageWidth); - if (scale) - { - if(width * pageHeight / pageWidth <= height) - { - final_w = width; - final_h = width * pageHeight / pageWidth; - } - else - { - final_w = height * pageWidth / pageHeight; - final_h = height; - } - } - else - { - final_w = pageWidth; - final_h = pageHeight; - } - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: After Cropping: %lf %lf %lf %lf", - width, height, final_w, final_h); - double posw = (width - final_w) / 2, - posh = (height - final_h) / 2; - // posw, posh : pdftopdf_position_e along width and height respectively. - // Calculating required position. - if (xpos == pdftopdf_position_e::LEFT) - posw = 0; - else if (xpos == pdftopdf_position_e::RIGHT) - posw *= 2; - - if (ypos == pdftopdf_position_e::TOP) - posh *= 2; - else if (ypos == pdftopdf_position_e::BOTTOM) - posh = 0; - - // making _cfPDFToPDFPageRect for cropping. - currpage.left += posw; - currpage.bottom += posh; - currpage.top = currpage.bottom + final_h; - currpage.right = currpage.left + final_w; - // Cropping. - // TODO: Borders are covered by the image. buffer space? - page.replaceKey("/TrimBox", - _cfPDFToPDFMakeBox(currpage.left, currpage.bottom, - currpage.right, currpage.top)); - page.replaceKey("/Rotate", _cfPDFToPDFMakeRotate(save_rotate)); - - return (_cfPDFToPDFGetRotate(page)); -} - -bool -_cfPDFToPDFQPDFPageHandle::is_landscape(pdftopdf_rotation_e orientation) -{ - page.assertInitialized(); - pdftopdf_rotation_e save_rotate = _cfPDFToPDFGetRotate(page); - if (orientation == ROT_0 || orientation == ROT_180) - page.replaceKey("/Rotate", _cfPDFToPDFMakeRotate(ROT_90)); - else - page.replaceKey("/Rotate", _cfPDFToPDFMakeRotate(ROT_0)); - - _cfPDFToPDFPageRect currpage = - _cfPDFToPDFGetBoxAsRect(_cfPDFToPDFGetTrimBox(page)); - double width = currpage.right - currpage.left; - double height = currpage.top - currpage.bottom; - page.replaceKey("/Rotate", _cfPDFToPDFMakeRotate(save_rotate)); - if (width > height) - return (true); - return (false); -} - -// TODO: better cropping -// TODO: test/fix with qsub rotation -void -_cfPDFToPDFQPDFPageHandle::add_subpage - (const std::shared_ptr<_cfPDFToPDFPageHandle> &sub, - float xpos, - float ypos, - float scale, - const _cfPDFToPDFPageRect *crop) // {{{ -{ - auto qsub = dynamic_cast<_cfPDFToPDFQPDFPageHandle *>(sub.get()); - DEBUG_assert(qsub); - - std::string xoname = - "/X" + QUtil::int_to_string((qsub->no != -1) ? qsub->no : ++ no); - if (crop) - { - _cfPDFToPDFPageRect pg = qsub->get_rect(), - tmp = *crop; - // we need to fix a too small cropbox. - tmp.width = tmp.right - tmp.left; - tmp.height = tmp.top - tmp.bottom; - tmp.rotate_move(-_cfPDFToPDFGetRotate(qsub->page), tmp.width, tmp.height); - // TODO TODO (pg.width? / unneeded?) - // TODO: better - // TODO: we need to obey page./Rotate - if (pg.width < tmp.width) - pg.right = pg.left + tmp.width; - if (pg.height < tmp.height) - pg.top = pg.bottom + tmp.height; - - _cfPDFToPDFPageRect rect = ungetRect(pg, *qsub, ROT_0, qsub->page); - - qsub->page.replaceKey("/TrimBox", - _cfPDFToPDFMakeBox(rect.left, rect.bottom, - rect.right, rect.top)); - // TODO? do everything for cropping here? - } - xobjs[xoname] = _cfPDFToPDFMakeXObject(qsub->page.getOwningQPDF(), - qsub->page); // trick: should be the - // same as - // page->getOwningQPDF() - // [only after it's made - // indirect] - - _cfPDFToPDFMatrix mtx; - mtx.translate(xpos, ypos); - mtx.scale(scale); - mtx.rotate(qsub->rotation); // TODO? -sub.rotation ? - // TODO FIXME: this might need another - // translation!? - if (crop) // TODO? other technique: set trim-box before - // _cfPDFToPDFMakeXObject (but this modifies original page) - { - mtx.translate(crop->left, crop->bottom); - // crop->dump(); - } - - content.append("q\n "); - content.append(mtx.get_string() + " cm\n "); - if (crop) - { - content.append("0 0 " + QUtil::double_to_string(crop->right-crop->left) + - " " + QUtil::double_to_string(crop->top-crop->bottom) + - " re W n\n "); - //content.append("0 0 " + QUtil::double_to_string(crop->right-crop->left) + - // " " + QUtil::double_to_string(crop->top-crop->bottom) + - // " re S\n "); - } - content.append(xoname + " Do\n"); - content.append("Q\n"); -} -// }}} - -void -_cfPDFToPDFQPDFPageHandle::mirror() // {{{ -{ - _cfPDFToPDFPageRect orig = get_rect(); - - if (is_existing()) - { - // need to wrap in XObject to keep patterns correct - // TODO? refactor into internal ..._subpage fn ? - std::string xoname = "/X" + QUtil::int_to_string(no); - - QPDFObjectHandle subpage = get(); // this->page, with rotation - - // replace all our data - *this = _cfPDFToPDFQPDFPageHandle(subpage.getOwningQPDF(), - orig.width, orig.height); - - xobjs[xoname] = _cfPDFToPDFMakeXObject(subpage.getOwningQPDF(), subpage); - // we can only now set this->xobjs - - // content.append(std::string("1 0 0 1 0 0 cm\n "); - content.append(xoname + " Do\n"); - - DEBUG_assert(!is_existing()); - } - - static const char *pre = "%pdftopdf cm\n"; - // Note: we don't change (TODO need to?) the media box - std::string mrcmd("-1 0 0 1 " + - QUtil::double_to_string(orig.right) + " 0 cm\n"); - - content.insert(0, std::string(pre) + mrcmd); -} -// }}} - -void -_cfPDFToPDFQPDFPageHandle::rotate(pdftopdf_rotation_e rot) // {{{ -{ - rotation = rot; // "rotation += rot;" ? -} -// }}} - -void -_cfPDFToPDFQPDFPageHandle::add_label(const _cfPDFToPDFPageRect &_rect, - const std::string label) // {{{ -{ - DEBUG_assert(is_existing()); - - _cfPDFToPDFPageRect rect = ungetRect (_rect, *this, rotation, page); - - DEBUG_assert(rect.left <= rect.right); - DEBUG_assert(rect.bottom <= rect.top); - - // TODO: Only add in the font once, not once per page. - QPDFObjectHandle font = page.getOwningQPDF()->makeIndirectObject - (QPDFObjectHandle::parse( - "<<" - " /Type /Font" - " /Subtype /Type1" - " /Name /pagelabel-font" - " /BaseFont /Helvetica" // TODO: support UTF-8 labels? - ">>")); - QPDFObjectHandle resources = page.getKey ("/Resources"); - QPDFObjectHandle rfont = resources.getKey ("/Font"); - rfont.replaceKey ("/pagelabel-font", font); - - double margin = 2.25; - double height = 12; - - std::string boxcmd = "q\n"; - - // White filled rectangle (top) - boxcmd += " 1 1 1 rg\n"; - boxcmd += " " + - QUtil::double_to_string(rect.left + margin) + " " + - QUtil::double_to_string(rect.top - height - 2 * margin) + " " + - QUtil::double_to_string(rect.right - rect.left - 2 * margin) + " " + - QUtil::double_to_string(height + 2 * margin) + " re f\n"; - - // White filled rectangle (bottom) - boxcmd += " " + - QUtil::double_to_string(rect.left + margin) + " " + - QUtil::double_to_string(rect.bottom + height + margin) + " " + - QUtil::double_to_string(rect.right - rect.left - 2 * margin) + " " + - QUtil::double_to_string(height + 2 * margin) + " re f\n"; - - // Black outline (top) - boxcmd += " 0 0 0 RG\n"; - boxcmd += " " + - QUtil::double_to_string(rect.left + margin) + " " + - QUtil::double_to_string(rect.top - height - 2 * margin) + " " + - QUtil::double_to_string(rect.right - rect.left - 2 * margin) + " " + - QUtil::double_to_string(height + 2 * margin) + " re S\n"; - - // Black outline (bottom) - boxcmd += " " + - QUtil::double_to_string(rect.left + margin) + " " + - QUtil::double_to_string(rect.bottom + height + margin) + " " + - QUtil::double_to_string(rect.right - rect.left - 2 * margin) + " " + - QUtil::double_to_string(height + 2 * margin) + " re S\n"; - - // Black text (top) - boxcmd += " 0 0 0 rg\n"; - boxcmd += " BT\n"; - boxcmd += " /pagelabel-font 12 Tf\n"; - boxcmd += " " + - QUtil::double_to_string(rect.left + 2 * margin) + " " + - QUtil::double_to_string(rect.top - height - margin) + " Td\n"; - boxcmd += " (" + label + ") Tj\n"; - boxcmd += " ET\n"; - - // Black text (bottom) - boxcmd += " BT\n"; - boxcmd += " /pagelabel-font 12 Tf\n"; - boxcmd += " " + - QUtil::double_to_string(rect.left + 2 * margin) + " " + - QUtil::double_to_string(rect.bottom + height + 2 * margin) + " Td\n"; - boxcmd += " (" + label + ") Tj\n"; - boxcmd += " ET\n"; - - boxcmd += "Q\n"; - - DEBUG_assert(page.getOwningQPDF()); // existing pages are always indirect - static const char *pre = "%pdftopdf q\n" - "q\n", - *post="%pdftopdf Q\n" - "Q\n"; - - QPDFObjectHandle stm1 = QPDFObjectHandle::newStream(page.getOwningQPDF(), - std::string(pre)), - stm2 = QPDFObjectHandle::newStream(page.getOwningQPDF(), - std::string(post) + - boxcmd); - - page.addPageContents(stm1, true); // before - page.addPageContents(stm2, false); // after -} -// }}} - -void -_cfPDFToPDFQPDFPageHandle::debug(const _cfPDFToPDFPageRect &rect, - float xpos, - float ypos) // {{{ -{ - DEBUG_assert(!is_existing()); - content.append(debug_box(rect, xpos, ypos)); -} -// }}} - -void -_cfPDFToPDFQPDFProcessor::close_file() // {{{ -{ - pdf.reset(); - hasCM = false; -} -// }}} - -// TODO? try/catch for PDF parsing errors? - -bool -_cfPDFToPDFQPDFProcessor::load_file(FILE *f, - pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e take, - int flatten_forms) // {{{ -{ - close_file(); - - if (!f) - throw std::invalid_argument("load_file(NULL, ...) not allowed"); - try - { - pdf.reset(new QPDF); - } - catch (...) - { - if (take == CF_PDFTOPDF_TAKE_OWNERSHIP) - fclose(f); - throw; - } - - switch (take) - { - case CF_PDFTOPDF_WILL_STAY_ALIVE: - try - { - pdf->processFile("temp file", f, false); - } - catch (const std::exception &e) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: load_file failed: %s", e.what()); - return (false); - } - break; - case CF_PDFTOPDF_TAKE_OWNERSHIP: - try - { - pdf->processFile("temp file", f, true); - } - catch (const std::exception &e) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: load_file failed: %s", e.what()); - return (false); - } - break; - case CF_PDFTOPDF_MUST_DUPLICATE: - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: load_file with CF_PDFTOPDF_MUST_DUPLICATE is not supported"); - return (false); - } - - start(flatten_forms); - return (true); -} -// }}} - -bool -_cfPDFToPDFQPDFProcessor::load_filename(const char *name, - pdftopdf_doc_t *doc, - int flatten_forms) // {{{ -{ - close_file(); - - try - { - pdf.reset(new QPDF); - pdf->processFile(name); - } - catch (const std::exception &e) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: load_filename failed: %s",e.what()); - return (false); - } - - start(flatten_forms); - return (true); -} -// }}} - -void -_cfPDFToPDFQPDFProcessor::start(int flatten_forms) // {{{ -{ - DEBUG_assert(pdf); - - if (flatten_forms) - { - QPDFAcroFormDocumentHelper afdh(*pdf); - afdh.generateAppearancesIfNeeded(); - - QPDFPageDocumentHelper dh(*pdf); - dh.flattenAnnotations(an_print); - } - - pdf->pushInheritedAttributesToPage(); - orig_pages = pdf->getAllPages(); - - // remove them (just unlink, data still there) - const int len = orig_pages.size(); - for (int iA = 0; iA < len; iA ++) - pdf->removePage(orig_pages[iA]); - - // we remove stuff that becomes defunct (probably) TODO - pdf->getRoot().removeKey("/PageMode"); - pdf->getRoot().removeKey("/Outlines"); - pdf->getRoot().removeKey("/OpenAction"); - pdf->getRoot().removeKey("/PageLabels"); -} -// }}} - -bool -_cfPDFToPDFQPDFProcessor::check_print_permissions(pdftopdf_doc_t *doc) // {{{ -{ - if (!pdf) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: No PDF loaded"); - return (false); - } - return (pdf->allowPrintHighRes() || - pdf->allowPrintLowRes()); // from legacy pdftopdf -} -// }}} - -std::vector> -_cfPDFToPDFQPDFProcessor::get_pages(pdftopdf_doc_t *doc) // {{{ -{ - std::vector> ret; - if (!pdf) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: No PDF loaded"); - DEBUG_assert(0); - return (ret); - } - const int len = orig_pages.size(); - ret.reserve(len); - for (int iA = 0; iA < len; iA ++) - ret.push_back(std::shared_ptr<_cfPDFToPDFPageHandle>(new _cfPDFToPDFQPDFPageHandle(orig_pages[iA], iA+1))); - return (ret); -} -// }}} - -std::shared_ptr<_cfPDFToPDFPageHandle> -_cfPDFToPDFQPDFProcessor::new_page(float width, - float height, - pdftopdf_doc_t *doc) // {{{ -{ - if (!pdf) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: No PDF loaded"); - DEBUG_assert(0); - return (std::shared_ptr<_cfPDFToPDFPageHandle>()); - } - return (std::shared_ptr<_cfPDFToPDFQPDFPageHandle>(new _cfPDFToPDFQPDFPageHandle(pdf.get(), width, height))); - // return std::make_shared<_cfPDFToPDFQPDFPageHandle>(pdf.get(), width, height); - // problem: make_shared not friend -} -// }}} - -void -_cfPDFToPDFQPDFProcessor::add_page(std::shared_ptr<_cfPDFToPDFPageHandle> page, - bool front) // {{{ -{ - DEBUG_assert(pdf); - auto qpage = dynamic_cast<_cfPDFToPDFQPDFPageHandle *>(page.get()); - if (qpage) - pdf->addPage(qpage->get(), front); -} -// }}} - -#if 0 -// we remove stuff now probably defunct TODO -pdf->getRoot().removeKey("/PageMode"); -pdf->getRoot().removeKey("/Outlines"); -pdf->getRoot().removeKey("/OpenAction"); -pdf->getRoot().removeKey("/PageLabels"); -#endif - -void -_cfPDFToPDFQPDFProcessor::multiply(int copies, - bool collate) // {{{ -{ - DEBUG_assert(pdf); - DEBUG_assert(copies > 0); - - std::vector pages = pdf->getAllPages(); // need copy - const int len = pages.size(); - - if (collate) - { - for (int iA = 1; iA < copies; iA ++) - for (int iB = 0; iB < len; iB ++) - pdf->addPage(pages[iB].shallowCopy(), false); - } - else - { - for (int iB = 0; iB < len; iB ++) - for (int iA = 1; iA < copies; iA ++) - pdf->addPageAt(pages[iB].shallowCopy(), false, pages[iB]); - } -} -// }}} - -// TODO? elsewhere? -void -_cfPDFToPDFQPDFProcessor::auto_rotate_all(bool dst_lscape, - pdftopdf_rotation_e normal_landscape) // {{{ -{ - DEBUG_assert(pdf); - - const int len = orig_pages.size(); - for (int iA = 0; iA < len; iA ++) - { - QPDFObjectHandle page = orig_pages[iA]; - - pdftopdf_rotation_e src_rot = _cfPDFToPDFGetRotate(page); - - // copy'n'paste from _cfPDFToPDFQPDFPageHandle::get_rect - _cfPDFToPDFPageRect ret = - _cfPDFToPDFGetBoxAsRect(_cfPDFToPDFGetTrimBox(page)); - // ret.translate(-ret.left, -ret.bottom); - ret.rotate_move(src_rot, ret.width, ret.height); - // ret.scale(_cfPDFToPDFGetUserUnit(page)); - - const bool src_lscape = (ret.width > ret.height); - if (src_lscape != dst_lscape) - { - pdftopdf_rotation_e rotation = normal_landscape; - // TODO? other rotation direction, e.g. - // if (src_rot == ROT_0) && (param.orientation == ROT_270) ... etc. - // rotation = ROT_270; - - page.replaceKey("/Rotate", - _cfPDFToPDFMakeRotate(src_rot + rotation)); - } - } -} -// }}} - -// TODO -void -_cfPDFToPDFQPDFProcessor::add_cm(const char *defaulticc, - const char *outputicc) // {{{ -{ - DEBUG_assert(pdf); - - if (_cfPDFToPDFHasOutputIntent(*pdf)) - return; // nothing to do - - QPDFObjectHandle srcicc = _cfPDFToPDFSetDefaultICC(*pdf, defaulticc); - // TODO? rename to putDefaultICC? - _cfPDFToPDFAddDefaultRGB(*pdf, srcicc); - - _cfPDFToPDFAddOutputIntent(*pdf, outputicc); - - hasCM = true; -} -// }}} - -void -_cfPDFToPDFQPDFProcessor::set_comments - (const std::vector &comments) // {{{ -{ - extraheader.clear(); - const int len = comments.size(); - for (int iA = 0; iA < len; iA ++) - { - DEBUG_assert(comments[iA].at(0) == '%'); - extraheader.append(comments[iA]); - extraheader.push_back('\n'); - } -} -// }}} - -void -_cfPDFToPDFQPDFProcessor::emit_file(FILE *f, - pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e take) // {{{ -{ - if (!pdf) - return; - - QPDFWriter out(*pdf); - switch (take) - { - case CF_PDFTOPDF_WILL_STAY_ALIVE: - out.setOutputFile("temp file", f, false); - break; - case CF_PDFTOPDF_TAKE_OWNERSHIP: - out.setOutputFile("temp file", f, true); - break; - case CF_PDFTOPDF_MUST_DUPLICATE: - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: emit_file with CF_PDFTOPDF_MUST_DUPLICATE is not supported"); - return; - } - if (hasCM) - out.setMinimumPDFVersion("1.4"); - else - out.setMinimumPDFVersion("1.2"); - if (!extraheader.empty()) - out.setExtraHeaderText(extraheader); - out.setPreserveEncryption(false); - out.write(); -} -// }}} - -void -_cfPDFToPDFQPDFProcessor::emit_filename(const char *name, - pdftopdf_doc_t *doc) // {{{ -{ - if (!pdf) - return; - - // special case: name == NULL -> stdout - QPDFWriter out(*pdf, name); - if (hasCM) - out.setMinimumPDFVersion("1.4"); - else - out.setMinimumPDFVersion("1.2"); - if (!extraheader.empty()) - out.setExtraHeaderText(extraheader); - out.setPreserveEncryption(false); - std::vector pages = pdf->getAllPages(); - int len = pages.size(); - if (len) - out.write(); - else - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: No pages left, outputting empty file."); -} -// }}} - -// TODO: -// loadPDF(); success? - -bool -_cfPDFToPDFQPDFProcessor::has_acro_form() // {{{ -{ - if (!pdf) - return false; - - QPDFObjectHandle root = pdf->getRoot(); - if (!root.hasKey("/AcroForm")) - return (false); - return (true); -} -// }}} diff --git a/cupsfilters/pdftopdf/qpdf-pdftopdf.cxx b/cupsfilters/pdftopdf/qpdf-pdftopdf.cxx deleted file mode 100644 index 2152d2d1..00000000 --- a/cupsfilters/pdftopdf/qpdf-pdftopdf.cxx +++ /dev/null @@ -1,248 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "qpdf-pdftopdf-private.h" -#include "qpdf-tools-private.h" -#include "cupsfilters/debug-internal.h" -#include -#include - - -_cfPDFToPDFPageRect -_cfPDFToPDFGetBoxAsRect(QPDFObjectHandle box) // {{{ -{ - _cfPDFToPDFPageRect ret; - - ret.left = box.getArrayItem(0).getNumericValue(); - ret.bottom = box.getArrayItem(1).getNumericValue(); - ret.right = box.getArrayItem(2).getNumericValue(); - ret.top = box.getArrayItem(3).getNumericValue(); - - ret.width = ret.right - ret.left; - ret.height = ret.top - ret.bottom; - - return (ret); -} -// }}} - -pdftopdf_rotation_e -_cfPDFToPDFGetRotate(QPDFObjectHandle page) // {{{ -{ - if (!page.hasKey("/Rotate")) - return (ROT_0); - double rot = page.getKey("/Rotate").getNumericValue(); - rot = fmod(rot, 360.0); - if (rot < 0) - rot += 360.0; - if (rot == 90.0) // CW - return (ROT_270); // CCW - else if (rot == 180.0) - return (ROT_180); - else if (rot == 270.0) - return (ROT_90); - else if (rot != 0.0) - throw std::runtime_error("Unexpected /Rotate value: " + - QUtil::double_to_string(rot)); - return (ROT_0); -} -// }}} - -double -_cfPDFToPDFGetUserUnit(QPDFObjectHandle page) // {{{ -{ - if (!page.hasKey("/UserUnit")) - return 1.0; - return (page.getKey("/UserUnit").getNumericValue()); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFMakeRotate(pdftopdf_rotation_e rot) // {{{ -{ - switch (rot) - { - case ROT_0: - return (QPDFObjectHandle::newNull()); - case ROT_90: // CCW - return (QPDFObjectHandle::newInteger(270)); // CW - case ROT_180: - return (QPDFObjectHandle::newInteger(180)); - case ROT_270: - return (QPDFObjectHandle::newInteger(90)); - default: - throw std::invalid_argument("Bad rotation"); - } -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFGetRectAsBox(const _cfPDFToPDFPageRect &rect) // {{{ -{ - return (_cfPDFToPDFMakeBox(rect.left, rect.bottom, rect.right, rect.top)); -} -// }}} - -_cfPDFToPDFMatrix::_cfPDFToPDFMatrix() // {{{ - : ctm{1,0,0,1,0,0} -{ -} -// }}} - -_cfPDFToPDFMatrix::_cfPDFToPDFMatrix(QPDFObjectHandle ar) // {{{ -{ - if (ar.getArrayNItems() != 6) - throw std::runtime_error("Not a ctm matrix"); - for (int iA = 0; iA < 6; iA ++) - ctm[iA] = ar.getArrayItem(iA).getNumericValue(); -} -// }}} - -_cfPDFToPDFMatrix -&_cfPDFToPDFMatrix::rotate(pdftopdf_rotation_e rot) // {{{ -{ - switch (rot) - { - case ROT_0: - break; - case ROT_90: - std::swap(ctm[0], ctm[2]); - std::swap(ctm[1], ctm[3]); - ctm[2] = -ctm[2]; - ctm[3] = -ctm[3]; - break; - case ROT_180: - ctm[0] = -ctm[0]; - ctm[3] = -ctm[3]; - break; - case ROT_270: - std::swap(ctm[0], ctm[2]); - std::swap(ctm[1], ctm[3]); - ctm[0] = -ctm[0]; - ctm[1] = -ctm[1]; - break; - default: - DEBUG_assert(0); - } - return (*this); -} -// }}} - -// TODO: test -_cfPDFToPDFMatrix -&_cfPDFToPDFMatrix::rotate_move(pdftopdf_rotation_e rot, - double width, - double height) // {{{ -{ - rotate(rot); - switch (rot) - { - case ROT_0: - break; - case ROT_90: - translate(width, 0); - break; - case ROT_180: - translate(width, height); - break; - case ROT_270: - translate(0, height); - break; - } - return (*this); -} -// }}} - -_cfPDFToPDFMatrix -&_cfPDFToPDFMatrix::rotate(double rad) // {{{ -{ - _cfPDFToPDFMatrix tmp; - - tmp.ctm[0] = cos(rad); - tmp.ctm[1] = sin(rad); - tmp.ctm[2] = -sin(rad); - tmp.ctm[3] = cos(rad); - - return (*this *= tmp); -} -// }}} - -_cfPDFToPDFMatrix -&_cfPDFToPDFMatrix::translate(double tx, - double ty) // {{{ -{ - ctm[4] += ctm[0] * tx + ctm[2] * ty; - ctm[5] += ctm[1] * tx + ctm[3] * ty; - return (*this); -} -// }}} - -_cfPDFToPDFMatrix -&_cfPDFToPDFMatrix::scale(double sx, - double sy) // {{{ -{ - ctm[0] *= sx; - ctm[1] *= sx; - ctm[2] *= sy; - ctm[3] *= sy; - return (*this); -} -// }}} - -_cfPDFToPDFMatrix -&_cfPDFToPDFMatrix::operator*=(const _cfPDFToPDFMatrix &rhs) // {{{ -{ - double tmp[6]; - - std::copy(ctm, ctm + 6, tmp); - - ctm[0] = tmp[0] * rhs.ctm[0] + tmp[2] * rhs.ctm[1]; - ctm[1] = tmp[1] * rhs.ctm[0] + tmp[3] * rhs.ctm[1]; - - ctm[2] = tmp[0] * rhs.ctm[2] + tmp[2] * rhs.ctm[3]; - ctm[3] = tmp[1] * rhs.ctm[2] + tmp[3] * rhs.ctm[3]; - - ctm[4] = tmp[0] * rhs.ctm[4] + tmp[2] * rhs.ctm[5] + tmp[4]; - ctm[5] = tmp[1] * rhs.ctm[4] + tmp[3] * rhs.ctm[5] + tmp[5]; - - return (*this); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFMatrix::get() const // {{{ -{ - QPDFObjectHandle ret = QPDFObjectHandle::newArray(); - - ret.appendItem(QPDFObjectHandle::newReal(ctm[0])); - ret.appendItem(QPDFObjectHandle::newReal(ctm[1])); - ret.appendItem(QPDFObjectHandle::newReal(ctm[2])); - ret.appendItem(QPDFObjectHandle::newReal(ctm[3])); - ret.appendItem(QPDFObjectHandle::newReal(ctm[4])); - ret.appendItem(QPDFObjectHandle::newReal(ctm[5])); - - return (ret); -} -// }}} - -std::string -_cfPDFToPDFMatrix::get_string() const // {{{ -{ - std::string ret; - - ret.append(QUtil::double_to_string(ctm[0])); - ret.append(" "); - ret.append(QUtil::double_to_string(ctm[1])); - ret.append(" "); - ret.append(QUtil::double_to_string(ctm[2])); - ret.append(" "); - ret.append(QUtil::double_to_string(ctm[3])); - ret.append(" "); - ret.append(QUtil::double_to_string(ctm[4])); - ret.append(" "); - ret.append(QUtil::double_to_string(ctm[5])); - - return (ret); -} -// }}} diff --git a/cupsfilters/pdftopdf/qpdf-tools-private.h b/cupsfilters/pdftopdf/qpdf-tools-private.h deleted file mode 100644 index 577f2c4f..00000000 --- a/cupsfilters/pdftopdf/qpdf-tools-private.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_QPDF_TOOLS_H_ -#define _CUPS_FILTERS_PDFTOPDF_QPDF_TOOLS_H_ - -#include -#include -#include - -QPDFObjectHandle _cfPDFToPDFGetMediaBox(QPDFObjectHandle page); -QPDFObjectHandle _cfPDFToPDFGetCropBox(QPDFObjectHandle page); -QPDFObjectHandle _cfPDFToPDFGetBleedBox(QPDFObjectHandle page); -QPDFObjectHandle _cfPDFToPDFGetTrimBox(QPDFObjectHandle page); -QPDFObjectHandle _cfPDFToPDFGetArtBox(QPDFObjectHandle page); - -QPDFObjectHandle _cfPDFToPDFMakePage(QPDF &pdf, const std::map &xobjs, - QPDFObjectHandle mediabox, - const std::string &content); - -QPDFObjectHandle _cfPDFToPDFMakeBox(double x1, double y1, double x2, double y2); - -#endif // !_CUPS_FILTERS_PDFTOPDF_QPDF_TOOLS_H_ diff --git a/cupsfilters/pdftopdf/qpdf-tools.cxx b/cupsfilters/pdftopdf/qpdf-tools.cxx deleted file mode 100644 index d247dd1d..00000000 --- a/cupsfilters/pdftopdf/qpdf-tools.cxx +++ /dev/null @@ -1,83 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "qpdf-tools-private.h" - -QPDFObjectHandle -_cfPDFToPDFGetMediaBox(QPDFObjectHandle page) // {{{ -{ - return (page.getKey("/MediaBox")); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFGetCropBox(QPDFObjectHandle page) // {{{ -{ - if (page.hasKey("/CropBox")) - return (page.getKey("/CropBox")); - return (page.getKey("/MediaBox")); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFGetBleedBox(QPDFObjectHandle page) // {{{ -{ - if (page.hasKey("/BleedBox")) - return (page.getKey("/BleedBox")); - return (_cfPDFToPDFGetCropBox(page)); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFGetTrimBox(QPDFObjectHandle page) // {{{ -{ - if (page.hasKey("/TrimBox")) - return (page.getKey("/TrimBox")); - return (_cfPDFToPDFGetCropBox(page)); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFGetArtBox(QPDFObjectHandle page) // {{{ -{ - if (page.hasKey("/ArtBox")) - return (page.getKey("/ArtBox")); - return (_cfPDFToPDFGetCropBox(page)); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFMakePage(QPDF &pdf, - const std::map &xobjs, - QPDFObjectHandle mediabox, - const std::string &content) // {{{ -{ - QPDFObjectHandle ret = QPDFObjectHandle::newDictionary(); - ret.replaceKey("/Type", QPDFObjectHandle::newName("/Page")); - - auto resdict = QPDFObjectHandle::newDictionary(); - resdict.replaceKey("/XObject", QPDFObjectHandle::newDictionary(xobjs)); - ret.replaceKey("/Resources", resdict); - ret.replaceKey("/MediaBox", mediabox); - ret.replaceKey("/Contents", QPDFObjectHandle::newStream(&pdf, content)); - - return (ret); -} -// }}} - -QPDFObjectHandle -_cfPDFToPDFMakeBox(double x1, - double y1, - double x2, - double y2) // {{{ -{ - QPDFObjectHandle ret = QPDFObjectHandle::newArray(); - ret.appendItem(QPDFObjectHandle::newReal(x1)); - ret.appendItem(QPDFObjectHandle::newReal(y1)); - ret.appendItem(QPDFObjectHandle::newReal(x2)); - ret.appendItem(QPDFObjectHandle::newReal(y2)); - return (ret); -} -// }}} diff --git a/cupsfilters/pdftopdf/qpdf-xobject-private.h b/cupsfilters/pdftopdf/qpdf-xobject-private.h deleted file mode 100644 index f318b1e2..00000000 --- a/cupsfilters/pdftopdf/qpdf-xobject-private.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_QPDF_XOBJECT_H_ -#define _CUPS_FILTERS_PDFTOPDF_QPDF_XOBJECT_H_ - -#include - -QPDFObjectHandle _cfPDFToPDFMakeXObject(QPDF *pdf, QPDFObjectHandle page); - -#endif // !_CUPS_FILTERS_PDFTOPDF_QPDF_XOBJECT_H_ diff --git a/cupsfilters/pdftopdf/qpdf-xobject.cxx b/cupsfilters/pdftopdf/qpdf-xobject.cxx deleted file mode 100644 index 508fee7c..00000000 --- a/cupsfilters/pdftopdf/qpdf-xobject.cxx +++ /dev/null @@ -1,202 +0,0 @@ -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "qpdf-xobject-private.h" -//#include -#include -#include -#include -#include -#include "qpdf-tools-private.h" -#include "qpdf-pdftopdf-private.h" - -// TODO: need to remove Struct Parent stuff (or fix) - -// NOTE: use /TrimBox to position content inside Nup cell, -// /BleedBox to clip against - -class CombineFromContents_Provider : public QPDFObjectHandle::StreamDataProvider { -public: - CombineFromContents_Provider(const std::vector &contents); - - void provideStreamData(int objid, int generation, Pipeline* pipeline); -private: - std::vector contents; -}; - -CombineFromContents_Provider::CombineFromContents_Provider(const std::vector &contents) - : contents(contents) -{ -} - -void -CombineFromContents_Provider::provideStreamData(int objid, - int generation, - Pipeline* pipeline) -{ - Pl_Concatenate concat("concat", pipeline); - const int clen = contents.size(); - for (int iA = 0; iA < clen; iA ++) - { - contents[iA].pipeStreamData(&concat, true, false, false); - concat << "\n"; - } - concat.manualFinish(); -} - -// -// To convert a page to an XObject there are several keys to consider: -// -// /Type /Page -> /Type /XObject (/Type optional for XObject) -// -> /Subtype /Form -// -> [/FormType 1] (optional) -// /Parent ? ? R -> remove -// /Resources dict -> copy -// /MediaBox rect [/CropBox /BleedBox /TrimBox /ArtBox] -// -> /BBox (use TrimBox [+ Bleed consideration?], -// with fallback to /MediaBox) -// note that /BBox is in *Form Space*, see /Matrix! -// [/BoxColorInfo dict] (used for guidelines that may be shown by viewer) -// -> ignore/remove -// [/Contents asfd] -> concatenate into stream data of the XObject -// (page is a dict, XObject a stream) -// -// [/Rotate 90] ... must be handled (either use CTM where XObject is -// /used/ -- or set /Matrix) -// [/UserUnit] (PDF 1.6) -> to /Matrix ? -- it MUST be handled. -// -// [/Group dict] -> copy -// [/Thumb stream] -> remove, not needed any more / would have to be -// regenerated (combined) -// [/B] article beads -- ignore for now -// [/Dur] -> remove (transition duration) -// [/Trans] -> remove (transitions) -// [/AA] -> remove (additional-actions) -// -// [/Metadata] what shall we do?? (kill: we can't combine XML) -// [/PieceInfo] -> remove, we can't combine private app data (?) -// [/LastModified date] (opt except /PieceInfo) -> see there -// -// [/PZ] -> remove, can't combine/keep (preferred zoom level) -// [/SeparationInfo] -> remove, no way to keep this (needed for separation) -// -// [/ID] related to web capture -- ignore/kill? -// [/StructParents] (opt except pdf contains "structural content items") -// -> copy (is this correct?) -// -// [/Annots] annotations -- ignore for now -// [/Tabs] tab order for annotations (/R row, /C column, -// /S structure order) -- see /Annots -// -// [/TemplateInstantiated] (reqd, if page was created from named page obj, -// 1.5) -- ? just ignore? -// [/PresSteps] -> remove (sub-page navigation for presentations) -// [no subpage navigation for printing / nup] -// [/VP] viewport rects -- ignore/drop or recalculate into new -// page -// - -QPDFObjectHandle -_cfPDFToPDFMakeXObject(QPDF *pdf, QPDFObjectHandle page) -{ - page.assertPageObject(); - - QPDFObjectHandle ret = QPDFObjectHandle::newStream(pdf); - QPDFObjectHandle dict = ret.getDict(); - - dict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject")); // optional - dict.replaceKey("/Subtype", QPDFObjectHandle::newName("/Form")); // required - // dict.replaceKey("/FormType", QPDFObjectHandle::newInteger(1)); // optional - - QPDFObjectHandle box = _cfPDFToPDFGetTrimBox(page); // already in "form space" - dict.replaceKey("/BBox", box); // reqd - - // [/Matrix .] ... default is [1 0 0 1 0 0]; we incorporate /UserUnit and - // /Rotate here - _cfPDFToPDFMatrix mtx; - if (page.hasKey("/UserUnit")) - mtx.scale(page.getKey("/UserUnit").getNumericValue()); - - // transform, so that bbox is [0 0 w h] (in outer space, but after UserUnit) - pdftopdf_rotation_e rot = _cfPDFToPDFGetRotate(page); - - // calculate rotation effect on [0 0 w h] - _cfPDFToPDFPageRect bbox = _cfPDFToPDFGetBoxAsRect(box), - tmp; - tmp.left = 0; - tmp.bottom = 0; - tmp.right = 0; - tmp.top = 0; - tmp.rotate_move(rot, bbox.width, bbox.height); - // tmp.rotate_move moves the bbox; we must achieve this move with the matrix. - mtx.translate(tmp.left, tmp.bottom); // 1. move origin to end up at - // left,bottom after rotation - - mtx.rotate(rot); // 2. rotate coordinates according to /Rotate - mtx.translate(-bbox.left, -bbox.bottom); // 3. move origin from 0,0 to - // "form space" - - dict.replaceKey("/Matrix", mtx.get()); - - dict.replaceKey("/Resources", page.getKey("/Resources")); - if (page.hasKey("/Group")) - dict.replaceKey("/Group", page.getKey("/Group")); // (transparency); opt, - // copy if there - - // ?? /StructParents ... can basically copy from page, but would need - // fixup in Structure Tree - // FIXME: remove (globally) Tagged spec (/MarkInfo), and Structure Tree - - // Note: [/Name] (reqd. only in 1.0 -- but there we even can't use our - // normal img/patter procedures) - - // none: - // QPDFObjectHandle filter = QPDFObjectHandle::newArray(); - // QPDFObjectHandle decode_parms = QPDFObjectHandle::newArray(); - // null leads to use of "default filters" from qpdf's settings - QPDFObjectHandle filter = QPDFObjectHandle::newNull(); - QPDFObjectHandle decode_parms = QPDFObjectHandle::newNull(); - - std::vector contents = page.getPageContents(); - // (will assertPageObject) - - auto ph = std::shared_ptr(new CombineFromContents_Provider(contents)); - ret.replaceStreamData(ph, filter, decode_parms); - - return (ret); -} - -// -// we will have to fix up the structure tree (e.g. /K in element), when copying -// /StructParents; -// (there is /Pg, which has to point to the containing page, /Stm when it's not -// part of the page's content stream -// i.e. when it is in our XObject!; then there is /StmOwn ...) -// when not copying, we have to remove the structure tree completely -// (also /MarkInfo dict) -// Still this might not be sufficient(?), as there are probably BDC and EMC -// operators in the stream. -// -// -// /XObject /Form has -// [/Type /XObject] -// /Subtype /Form -// [/FormType 1] -// /BBox rect from crop box, or recalculate -// [/Matrix .] ... default is [1 0 0 1 0 0] --- we have to incorporate -// /UserUnit here?! -// [/Resources dict] from page. -// [/Group dict] used for transparency -- can copy from page -// [/Ref dict] not needed; for external reference -// [/Metadata] not, as long we can not combine. -// [/PieceInfo] can copy, but not combine -// [/LastModified date] copy if /PieceInfo there -// [/StructParent] . don't want to be one ... have to read more spec -// [/StructParents] . copy from page! -// [/OPI] no opi version. don't set -// [/OC] is this optional content? NO! not needed. -// [/Name] (only reqd. in 1.0 -- but there we even can't use our -// normal img/patter procedures) -// From 72738922340d58362aafb2d5ae3b1cb6549648aa Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Fri, 20 Sep 2024 03:13:00 +0530 Subject: [PATCH 46/64] wrong header file name of deleted files --- cupsfilters/pdftopdf/C-intervalset-private.h | 2 +- cupsfilters/pdftopdf/C-pptypes-private.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cupsfilters/pdftopdf/C-intervalset-private.h b/cupsfilters/pdftopdf/C-intervalset-private.h index bb830165..98e4f051 100644 --- a/cupsfilters/pdftopdf/C-intervalset-private.h +++ b/cupsfilters/pdftopdf/C-intervalset-private.h @@ -6,7 +6,7 @@ #ifndef _CUPS_FILTERS_PDFTOPDF_INTERVALSET_H_ #define _CUPS_FILTERS_PDFTOPDF_INTERVALSET_H_ -#include "pdftopdf-private.h" +#include "C-pdftopdf-private.h" #include #include diff --git a/cupsfilters/pdftopdf/C-pptypes-private.h b/cupsfilters/pdftopdf/C-pptypes-private.h index a77cd5a2..e74cbea9 100644 --- a/cupsfilters/pdftopdf/C-pptypes-private.h +++ b/cupsfilters/pdftopdf/C-pptypes-private.h @@ -6,7 +6,7 @@ #ifndef _CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ #define _CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ -#include "pdftopdf-private.h" +#include "C-pdftopdf-private.h" #include typedef enum From 0e762c70093fd367b6b95de582c585ccd1043617 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Wed, 9 Oct 2024 20:32:43 +0530 Subject: [PATCH 47/64] converted pdf.cxx and pdf.h to C-pdf.c and C-pdf.h, changed the respective calling of functions in bannertopdf.c and ghostscript.c, updated the file name in makefile --- Makefile.am | 4 +- cupsfilters/C-pdf.c | 359 ++++++++++++++++++++++++++++++++++++++ cupsfilters/C-pdf.h | 70 ++++++++ cupsfilters/bannertopdf.c | 2 +- cupsfilters/ghostscript.c | 4 +- 5 files changed, 434 insertions(+), 5 deletions(-) create mode 100644 cupsfilters/C-pdf.c create mode 100644 cupsfilters/C-pdf.h diff --git a/Makefile.am b/Makefile.am index 57f7534f..fc995c10 100644 --- a/Makefile.am +++ b/Makefile.am @@ -86,7 +86,7 @@ pkgfiltersinclude_DATA = \ cupsfilters/image.h \ cupsfilters/ipp.h \ cupsfilters/log.h \ - cupsfilters/pdf.h \ + cupsfilters/C-pdf.h \ cupsfilters/raster.h lib_LTLIBRARIES = libcupsfilters.la @@ -179,7 +179,7 @@ libcupsfilters_la_SOURCES = \ cupsfilters/mupdftopwg.c \ cupsfilters/pack.c \ cupsfilters/pclmtoraster.cxx \ - cupsfilters/pdf.cxx \ + cupsfilters/C-pdf.c \ cupsfilters/pdftopdf/C-pdftopdf.c \ cupsfilters/pdftopdf/C-pdftopdf-private.h \ cupsfilters/pdftopdf/C-pdftopdf-processor.c \ diff --git a/cupsfilters/C-pdf.c b/cupsfilters/C-pdf.c new file mode 100644 index 00000000..a6945c37 --- /dev/null +++ b/cupsfilters/C-pdf.c @@ -0,0 +1,359 @@ +#include +#include +#include +#include "C-pdf.h" + +#include +#include +// +// 'make_real_box()' - Return a QPDF array object of real values for a box. +// + +static pdfio_rect_t // O - QPDFObjectHandle for an array +make_real_box(float values[4]) // I - Dimensions of the box in a float array +{ + pdfio_rect_t rect; + + rect.x1 = values[0]; + rect.y1 = values[1]; + rect.x2 = values[2]; + rect.y2 = values[3]; + + return rect; +} + +// +// 'cfPDFLoadTemplate()' - Load an existing PDF file using PDFio. +// + +cf_pdf_t* +cfPDFLoadTemplate(const char *filename) +{ + pdfio_file_t *pdf = pdfioFileOpen(filename, NULL, NULL, NULL, NULL); + + if (!pdf) + return NULL; + + if (pdfioFileGetNumPages(pdf) != 1) + { + pdfioFileClose(pdf); + return NULL; + } + + return (cf_pdf_t *)pdf; +} + +// +// 'cfPDFFree()' - Free pointer used by PDF object +// + +void +cfPDFFree(cf_pdf_t *pdf) +{ + if (pdf) + { + pdfioFileClose((pdfio_file_t *)pdf); + } +} + +// +// 'cfPDFPages()' - Count number of pages in file using PDFio. +// + +int +cfPDFPages(const char *filename) +{ + pdfio_file_t *pdf = pdfioFileOpen(filename, NULL, NULL, NULL, NULL); + + if (!pdf) + { + return -1; + } + + int pages = pdfioFileGetNumPages(pdf); + pdfioFileClose(pdf); + return pages; +} + +// +// 'cfPDFPagesFP()' - Count number of pages in file using PDFio. +// + +int +cfPDFPagesFP(char *file) +{ + pdfio_file_t *pdf = pdfioFileOpen(file, NULL, NULL, NULL, NULL); + + if (!pdf) + return -1; + + int pages = pdfioFileGetNumPages(pdf); + pdfioFileClose(pdf); + return pages; +} + +// +// 'cfPDFPrependStream()' - Prepend a stream to the contents of a specified +// page in PDF file. +// + +int +cfPDFPrependStream(cf_pdf_t *pdf, + unsigned page_num, + const char *buf, + size_t len) +{ + + if(pdfioFileGetNumPages((pdfio_file_t *)pdf)==0) + return 1; + + pdfio_obj_t *page = pdfioFileGetPage((pdfio_file_t *)pdf, page_num - 1); + pdfio_dict_t *pageDict = pdfioObjGetDict(page); + + pdfio_stream_t *existing_stream = pdfioPageOpenStream(page, 0, true); + if (!existing_stream) + return 1; + + pdfio_obj_t *new_stream_obj = pdfioFileCreateObj((pdfio_file_t *)pdf, pageDict); + if (!new_stream_obj) + { + pdfioStreamClose(existing_stream); + return 1; + } + + pdfio_stream_t *new_stream = pdfioObjCreateStream(new_stream_obj, PDFIO_FILTER_FLATE); + if (!new_stream) + { + pdfioStreamClose(existing_stream); + return 1; + } + + pdfioStreamWrite(new_stream, buf, len); + pdfioStreamClose(new_stream); + + pdfio_stream_t *combined_stream = pdfioObjCreateStream(page, PDFIO_FILTER_FLATE); + if (!combined_stream) + { + pdfioStreamClose(existing_stream); + return 1; + } + + char buffer[1024]; + size_t read_len; + while ((read_len = pdfioStreamRead(existing_stream, buffer, sizeof(buffer))) > 0) + pdfioStreamWrite(combined_stream, buffer, read_len); + + pdfioStreamClose(existing_stream); + pdfioStreamClose(combined_stream); + + return 0; +} + +// +// 'cfPDFAddType1Font()' - Add the specified type1 font face to the specified +// page in a PDF document. +// + +int +cfPDFAddType1Font(cf_pdf_t *pdf, + unsigned page_num, + const char *name) +{ + pdfio_obj_t *page = pdfioFileGetPage((pdfio_file_t *)pdf, page_num); + pdfio_dict_t *pageDict = pdfioObjGetDict(page); + if (!page) + return 1; + + pdfio_dict_t *resources = pdfioDictGetDict(pageDict, "Resources"); + + if (!resources) + { + resources = pdfioDictCreate((pdfio_file_t *)pdf); + pdfioDictSetDict(pageDict, "Resources", resources); + } + + pdfio_dict_t *fonts = pdfioDictGetDict(resources, "Font"); + if (!fonts) + { + fonts = pdfioDictCreate((pdfio_file_t *)pdf); + pdfioDictSetDict(resources, "Font", fonts); + } + + pdfio_dict_t *font = pdfioDictCreate((pdfio_file_t *)pdf); + if (!font) + return 1; + + pdfioDictSetName(font, "Type", "Font"); + pdfioDictSetName(font, "Subtype", "Type1"); + char basefont[256]; + snprintf(basefont, sizeof(basefont), "/%s", name); + pdfioDictSetName(font, "BaseFont", basefont); + + pdfioDictSetDict(fonts, "bannertopdf-font", font); + + return 0; +} + +// +// 'dict_lookup_rect()' - Lookup for an array of rectangle dimensions in a QPDF +// dictionary object. If it is found, store the values in +// an array and return true, else return false. +// + +static bool +dict_lookup_rect(pdfio_obj_t *object, // I - PDF dictionary object + const char *key, // I - Key to lookup + float rect[4], // O - Array to store values if key is found + bool inheritable) // I - Whether to look for inheritable values +{ + pdfio_dict_t *dict = pdfioObjGetDict(object); + if (!dict) + return false; + + pdfio_obj_t *value = pdfioDictGetObj(dict, key); + if (!value && inheritable) + return false; + + pdfio_array_t *array = pdfioObjGetArray(value); + if (!array || pdfioArrayGetSize(array) != 4) + return false; + + for (int i = 0; i < 4; i++) + { + pdfio_obj_t *elem = pdfioArrayGetObj(array, i); + + if (pdfioArrayGetType(array, i) == PDFIO_VALTYPE_NUMBER) + { + rect[i] = pdfioObjGetNumber(elem); + } + else + return false; // If any value is not numeric, return false + } + + return true; +} + +// +// 'fit_rect()' - Update the scale of the page using old media box dimensions +// and new media box dimensions. +// + +static void +fit_rect(float oldrect[4], // I - Old media box + float newrect[4], // I - New media box + float *scale) // I - Pointer to scale which needs to be updated +{ + float oldwidth = oldrect[2] - oldrect[0]; + float oldheight = oldrect[3] - oldrect[1]; + float newwidth = newrect[2] - newrect[0]; + float newheight = newrect[3] - newrect[1]; + + *scale = newwidth / oldwidth; + if (oldheight * (*scale) > newheight) + *scale = newheight / oldheight; +} + +// +// 'cfPDFResizePage()' - Resize page in a PDF with the given dimensions. +// + +int cfPDFResizePage(cf_pdf_t *pdf, // I - Pointer to PDFio file object + unsigned page_num, // I - Page number (1-based index) + float width, // I - New width of the page + float length, // I - New length of the page + float *scale) // O - Scale of the page to be updated +{ + pdfio_obj_t *page = pdfioFileGetPage((pdfio_file_t *)pdf, page_num - 1); + if (!page) + return 1; + + float new_mediabox[4] = {0.0, 0.0, width, length}; + float old_mediabox[4]; + pdfio_rect_t media_box; + + if (!dict_lookup_rect(page, "/MediaBox", old_mediabox, true)) + return (1); + + fit_rect(old_mediabox, new_mediabox, scale); + media_box = make_real_box(new_mediabox); + + pdfio_dict_t *pageDict = pdfioObjGetDict(page); + if (pageDict) + { + pdfioDictSetRect(pageDict, "CropBox", &media_box); + pdfioDictSetRect(pageDict, "TrimBox", &media_box); + pdfioDictSetRect(pageDict, "BleedBox", &media_box); + pdfioDictSetRect(pageDict, "ArtBox", &media_box); + } + + return 0; +} + +// +// 'cfPDFDuplicatePage()' - Duplicate a specified pdf page in a PDF +// + +int +cfPDFDuplicatePage(cf_pdf_t *pdf, + unsigned page_num, + unsigned count) +{ + pdfio_obj_t *page = pdfioFileGetPage((pdfio_file_t *)pdf, page_num - 1); + + if (!page) + return 1; + + for (unsigned i = 0; i < count; ++i) + { + pdfioPageCopy((pdfio_file_t *)pdf, page); + } + + return 0; +} + +// +// 'cfPDFWrite()' - Write the contents of PDF object to an already open FILE*. +// + +void +cfPDFWrite(cf_pdf_t *pdf, + FILE *file) +{ +// pdfioFileCreateImageObjFromFile((pdfio_file_t *)pdf, file, false); +} + +// +// 'lookup_opt()' - Get value according to key in the options list. +// + +static const char* +lookup_opt(cf_opt_t *opt, + const char *key) +{ + if (!opt || !key) + return NULL; + + while (opt) + { + if (opt->key && opt->val) + { + if (strcmp(opt->key, key) == 0) + { + return opt->val; + } + } + opt = opt->next; + } + + return (""); +} + +// +// 'cfPDFFillForm()' - Fill recognized fields with information +// + +int cfPDFFillForm(cf_pdf_t *doc, cf_opt_t *opt) { + // PDFio does not directly support form filling. + return 0; +} + diff --git a/cupsfilters/C-pdf.h b/cupsfilters/C-pdf.h new file mode 100644 index 00000000..efb94fdb --- /dev/null +++ b/cupsfilters/C-pdf.h @@ -0,0 +1,70 @@ +// +// Copyright 2012 Canonical Ltd. +// Copyright 2018 Sahil Arora +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +// +// Copyright 2012 Canonical Ltd. +// Copyright 2018 Sahil Arora +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#ifndef _CUPS_FILTERS_PDF_H_ +#define _CUPS_FILTERS_PDF_H_ + +#include + +typedef struct pdfio_file_t cf_pdf_t; + +typedef struct _cf_opt cf_opt_t; + +// +// Type to bunch PDF form field name and its value. +// + +struct _cf_opt +{ + const char* key; + const char* val; + cf_opt_t *next; +}; + +cf_pdf_t *cfPDFLoadTemplate(const char *filename); +void cfPDFFree(cf_pdf_t *pdf); + +void cfPDFWrite(cf_pdf_t *doc, + FILE *file); + +int cfPDFPrependStream(cf_pdf_t *doc, + unsigned page, + const char *buf, + size_t len); + +int cfPDFAddType1Font(cf_pdf_t *doc, + unsigned page, + const char *name); + +int cfPDFResizePage(cf_pdf_t *doc, + unsigned page, + float width, + float length, + float *scale); + +int cfPDFDuplicatePage(cf_pdf_t *doc, + unsigned page, + unsigned count); + +int cfPDFFillForm(cf_pdf_t *doc, + cf_opt_t *opt); + +int cfPDFPages(const char *filename); + +int cfPDFPagesFP(char *file); + +#endif // !_CUPS_FILTERS_PDF_H_ + diff --git a/cupsfilters/bannertopdf.c b/cupsfilters/bannertopdf.c index eb9020d6..89d5f231 100644 --- a/cupsfilters/bannertopdf.c +++ b/cupsfilters/bannertopdf.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include diff --git a/cupsfilters/ghostscript.c b/cupsfilters/ghostscript.c index 18e01638..153474fc 100644 --- a/cupsfilters/ghostscript.c +++ b/cupsfilters/ghostscript.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include @@ -997,7 +997,7 @@ cfFilterGhostscript(int inputfd, // I - File descriptor input if (doc_type == GS_DOC_TYPE_PDF) { - int pages = cfPDFPagesFP(fp); + int pages = cfPDFPagesFP(filename); if (pages == 0) { From 81f58d9a73843a4cca9b6a19354fc69ea97d7b3b Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Wed, 9 Oct 2024 22:27:33 +0530 Subject: [PATCH 48/64] Removing the qpdf dependent files --- cupsfilters/pdf.cxx | 477 -------------------------------------------- cupsfilters/pdf.h | 50 ----- 2 files changed, 527 deletions(-) delete mode 100644 cupsfilters/pdf.cxx delete mode 100644 cupsfilters/pdf.h diff --git a/cupsfilters/pdf.cxx b/cupsfilters/pdf.cxx deleted file mode 100644 index 03e31b5e..00000000 --- a/cupsfilters/pdf.cxx +++ /dev/null @@ -1,477 +0,0 @@ -// -// Copyright 2012 Canonical Ltd. -// Copyright 2013 ALT Linux, Andrew V. Stepanov -// Copyright 2018 Sahil Arora -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include -#include "pdf.h" -#include -#include -#include -#include -#include -#include -#include -#include - -// -// Useful reference: -// -// http://www.gnupdf.org/Indirect_Object -// http://www.gnupdf.org/Introduction_to_PDF -// http://blog.idrsolutions.com/2011/05/understanding-the-pdf-file-format-%E2%80%93-pdf-xref-tables-explained -// http://labs.appligent.com/pdfblog/pdf-hello-world/ -// https://github.com/OpenPrinting/cups-filters/pull/25 -// - - -// -// 'make_real_box()' - Return a QPDF array object of real values for a box. -// - -static QPDFObjectHandle // O - QPDFObjectHandle for an array -make_real_box(float values[4]) // I - Dimensions of the box in a float array -{ - QPDFObjectHandle ret = QPDFObjectHandle::newArray(); - for (int i = 0; i < 4; ++i) - ret.appendItem(QPDFObjectHandle::newReal(values[i])); - return (ret); -} - - -// -// 'cfPDFLoadTemplate()' - Load an existing PDF file and do initial parsing -// using QPDF. -// - -extern "C" cf_pdf_t * -cfPDFLoadTemplate(const char *filename) // I - Filename to open -{ - QPDF *pdf = new QPDF(); - - try - { - pdf->processFile(filename); - } - catch(...) - { - delete pdf; - return (NULL); - } - - unsigned pages = (pdf->getAllPages()).size(); - - if (pages != 1) - { - delete pdf; - return (NULL); - } - - return (pdf); -} - - -// -// 'cfPDFFree()' - Free pointer used by PDF object -// - -extern "C" void -cfPDFFree(cf_pdf_t *pdf) // I - Pointer to PDF object -{ - delete pdf; -} - - -// -// 'cfPDFPages()' - Count number of pages in file using QPDF. -// - -int // O - Number of pages or -1 on error -cfPDFPages(const char *filename) // I - Filename to open -{ - QPDF *pdf = new QPDF(); - - if (pdf) - { - try - { - pdf->processFile(filename); - } - catch(...) - { - cfPDFFree(pdf); - return (-1); - } - int pages = (pdf->getAllPages()).size(); - cfPDFFree(pdf); - return (pages); - } else - return (-1); -} - - -// -// 'cfPDFPagesFP()' - Count number of pages in file -// using QPDF. -// - -int // O - Number of pages or -1 on error -cfPDFPagesFP(FILE *file) // I - Pointer to opened PDF file (stdio FILE*) -{ - QPDF *pdf = new QPDF(); - - if (pdf) - { - try - { - pdf->processFile("", file, false); - } - catch(...) - { - cfPDFFree(pdf); - return (-1); - } - int pages = (pdf->getAllPages()).size(); - cfPDFFree(pdf); - return (pages); - } else - return (-1); -} - - -// -// 'cfPDFPrependStream' - Prepend a stream to the contents of a specified -// page in PDF file. -// - -extern "C" int -cfPDFPrependStream(cf_pdf_t *pdf, // I - Pointer to QPDF object - unsigned page_num, // I - page number of page to prepend - // stream to - char const *buf, // I - buffer containing data to be - // prepended - size_t len) // I - length of buffer -{ - std::vector pages = pdf->getAllPages(); - - if (pages.empty() || page_num > pages.size()) - return (1); - - QPDFObjectHandle page = pages[page_num - 1]; - - // get page contents stream / array - QPDFObjectHandle contents = page.getKey("/Contents"); - if (!contents.isStream() && !contents.isArray()) - return (1); - - // prepare the new stream which is to be prepended - std::shared_ptr stream_data = std::shared_ptr(new Buffer(len)); - memcpy(stream_data->getBuffer(), buf, len); - QPDFObjectHandle stream = QPDFObjectHandle::newStream(pdf, stream_data); - stream = pdf->makeIndirectObject(stream); - - // if the contents is an array, prepend the new stream to the array, - // else convert the contents to an array and the do the same. - if (contents.isStream()) - { - QPDFObjectHandle old_streamdata = contents; - contents = QPDFObjectHandle::newArray(); - contents.appendItem(old_streamdata); - } - - contents.insertItem(0, stream); - page.replaceKey("/Contents", contents); - - return (0); -} - - -// -// 'cfPDFAddType1Font()' - Add the specified type1 fontface to the specified -// page in a PDF document. -// - -extern "C" int -cfPDFAddType1Font(cf_pdf_t *pdf, // I - QPDF object - unsigned page_num, // I - Page number of the page to which - // the font is to be added - const char *name) // I - name of the font to be added -{ - std::vector pages = pdf->getAllPages(); - - if (pages.empty() || page_num > pages.size()) - return (1); - - QPDFObjectHandle page = pages[page_num - 1]; - - QPDFObjectHandle resources = page.getKey("/Resources"); - if (!resources.isDictionary()) - return (1); - - QPDFObjectHandle font = QPDFObjectHandle::newDictionary(); - font.replaceKey("/Type", QPDFObjectHandle::newName("/Font")); - font.replaceKey("/Subtype", QPDFObjectHandle::newName("/Type1")); - font.replaceKey("/BaseFont", - QPDFObjectHandle::newName(std::string("/") + - std::string(name))); - - QPDFObjectHandle fonts = resources.getKey("/Font"); - if (fonts.isNull()) - fonts = QPDFObjectHandle::newDictionary(); - else if (!fonts.isDictionary()) - return (1); - - font = pdf->makeIndirectObject(font); - fonts.replaceKey("/bannertopdf-font", font); - resources.replaceKey("/Font", fonts); - - return (0); -} - - -// -// 'dict_lookup_rect()' - Lookup for an array of rectangle dimensions in a QPDF -// dictionary object. If it is found, store the values in -// an array and return true, else return false. -// - -static bool -dict_lookup_rect(QPDFObjectHandle object, // O - Key is found in the dictionary? - std::string const& key, // I - PDF dictionary object - float rect[4], // I - Key to lookup - bool inheritable) // I - Array to store values if key - // is found -{ - // preliminary checks - if (!object.isDictionary()) - return (false); - - QPDFObjectHandle value; - if (!object.hasKey(key) && inheritable) - { - QPDFFormFieldObjectHelper helper(object); - value = helper.getInheritableFieldValue(key); - if (value.isNull()) - return (false); - } - else - value = object.getKey(key); - - // check if the key is array or some other type - if (!value.isArray()) - return (false); - - // get values in a vector and assign it to rect - std::vector array = value.getArrayAsVector(); - for (int i = 0; i < 4; ++i) - { - // if the value in the array is not real, we have an invalid array - if (!array[i].isReal() && !array[i].isInteger()) - return (false); - - rect[i] = array[i].getNumericValue(); - } - - return (array.size() == 4); -} - - -// -// 'fit_rect()' - Update the scale of the page using old media box dimensions -// and new media box dimensions. -// - -static void -fit_rect(float oldrect[4], // I - Old media box - float newrect[4], // I - New media box - float *scale) // I - Pointer to scale which needs to be updated -{ - float oldwidth = oldrect[2] - oldrect[0]; - float oldheight = oldrect[3] - oldrect[1]; - float newwidth = newrect[2] - newrect[0]; - float newheight = newrect[3] - newrect[1]; - - *scale = newwidth / oldwidth; - if (oldheight * *scale > newheight) - *scale = newheight / oldheight; -} - - -// -// 'cfPDFResizePage()' - Resize page in a PDF with the given dimensions. -// - -extern "C" int -cfPDFResizePage(cf_pdf_t *pdf, // I - Pointer to QPDF object - unsigned page_num, // I - Page number - float width, // I - Width of page to set - float length, // I - Length of page to set - float *scale) // I - Scale of page to set -{ - std::vector pages = pdf->getAllPages(); - - if (pages.empty() || page_num > pages.size()) - return (1); - - QPDFObjectHandle page = pages[page_num - 1]; - float new_mediabox[4] = { 0.0, 0.0, width, length }; - float old_mediabox[4]; - QPDFObjectHandle media_box; - - if (!dict_lookup_rect(page, "/MediaBox", old_mediabox, true)) - return (1); - - fit_rect(old_mediabox, new_mediabox, scale); - media_box = make_real_box(new_mediabox); - - page.replaceKey("/ArtBox", media_box); - page.replaceKey("/BleedBox", media_box); - page.replaceKey("/CropBox", media_box); - page.replaceKey("/MediaBox", media_box); - page.replaceKey("/TrimBox", media_box); - - return (0); -} - - -// -// 'cfPDFDuplicatePage()' - Duplicate a specified pdf page in a PDF -// - -extern "C" int -cfPDFDuplicatePage (cf_pdf_t *pdf, // I - Pointer to QPDF object - unsigned page_num, // I - Page number of the page to be - // duplicated - unsigned count) // I - Number of copies to be duplicated -{ - std::vector pages = pdf->getAllPages(); - - if (pages.empty() || page_num > pages.size()) - return (1); - - QPDFObjectHandle page = pages[page_num - 1]; - for (unsigned i = 0; i < count; ++i) - { - page = pdf->makeIndirectObject(page); - pdf->addPage(page, false); - } - - return (0); -} - - -// -// 'cfPDFWrite()' - Write the contents of PDF object to an already open FILE*. -// - -extern "C" void -cfPDFWrite(cf_pdf_t *pdf, // I - Pointer to QPDF structure - FILE *file) // I - File pointer to write to -{ - QPDFWriter output(*pdf, "cfPDFWrite", file, false); - output.write(); -} - - -// -// 'lookup_opt()' - Get value according to key in the options list. -// - -static std::string // O - character string which corresponds - // to the value of the key or NULL if - // key is not found in the list. -lookup_opt(cf_opt_t *opt, // I - pointer to the cf_opt_t type list - std::string const& key) // I - key to be found in the list -{ - if (!opt || key.empty()) - return (""); - - while (opt) - { - if (opt->key && opt->val) - { - if (strcmp(opt->key, key.c_str()) == 0) - return (std::string(opt->val)); - } - opt = opt->next; - } - - return (""); -} - - -// -// 'cfPDFFillForm()' - 1. Look for form in PDF template file -// 2. Look for form fields' names -// 3. Fill recognized fields with information -// - -extern "C" int // O - Status of form fill - 0 for success, - // 1 for failure -cfPDFFillForm(cf_pdf_t *doc, // I - Pointer to the QPDF structure - cf_opt_t *opt) // I - Pointer to the cf_opt_t type list -{ - // Initialize AcroFormDocumentHelper and PageDocumentHelper objects - // to work with forms in the PDF - QPDFAcroFormDocumentHelper afdh(*doc); - QPDFPageDocumentHelper pdh(*doc); - - // Check if the PDF has a form or not - if (!afdh.hasAcroForm()) - return 1; - - // Get the first page from the PDF to fill the form. Since this - // is a banner file, it must contain only a single page, and that - // check has already been performed in the `cfPDFLoadTemplate()` function - std::vector pages = pdh.getAllPages(); - if (pages.empty()) - return 1; - QPDFPageObjectHelper page = pages.front(); - - // Get the annotations in the page - std::vector annotations = - afdh.getWidgetAnnotationsForPage(page); - - for (std::vector::iterator annot_iter = - annotations.begin(); - annot_iter != annotations.end(); - ++annot_iter) - { - // For each annotation, find its associated field. If it's a - // text field, we try to set its value. This will automatically - // update the document to indicate that appearance streams need - // to be regenerated. At the time of this writing, QPDF doesn't - // have any helper code to assist with appearance stream generation, - // though there's nothing that prevents it from being possible. - QPDFFormFieldObjectHelper ffh = - afdh.getFieldForAnnotation(*annot_iter); - if (ffh.getFieldType() == "/Tx") - { - // Look in the options setting for the value of this field and fill the - // value accordingly. This will automatically set /NeedAppearances to - // true. - std::string const name = ffh.getFullyQualifiedName(); - std::string fill_with = lookup_opt(opt, name); - if (fill_with.empty()) - { - std::cerr << "DEBUG: Lack information for widget: " << name << ".\n"; - fill_with = "N/A"; - } - - // Convert the 'fill_with' string to UTF16 before filling to the widget - QPDFObjectHandle fill_with_utf_16 = - QPDFObjectHandle::newUnicodeString(fill_with); - ffh.setV(fill_with_utf_16); - std::cerr << "DEBUG: Fill widget name " << name << " with value " - << fill_with_utf_16.getUTF8Value() << ".\n"; - } - } - - // Status 0 notifies that the function successfully filled all the - // identifiable fields in the form - return (0); -} diff --git a/cupsfilters/pdf.h b/cupsfilters/pdf.h deleted file mode 100644 index 14f19820..00000000 --- a/cupsfilters/pdf.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// Copyright 2012 Canonical Ltd. -// Copyright 2018 Sahil Arora -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDF_H_ -#define _CUPS_FILTERS_PDF_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct QPDF cf_pdf_t; - -typedef struct _cf_opt cf_opt_t; - -// -// Type to bunch PDF form field name and its value. -// - -struct _cf_opt -{ - const char* key; - const char* val; - cf_opt_t *next; -}; - -cf_pdf_t *cfPDFLoadTemplate(const char *filename); -void cfPDFFree(cf_pdf_t *pdf); -void cfPDFWrite(cf_pdf_t *doc, FILE *file); -int cfPDFPrependStream(cf_pdf_t *doc, unsigned page, char const *buf, - size_t len); -int cfPDFAddType1Font(cf_pdf_t *doc, unsigned page, const char *name); -int cfPDFResizePage(cf_pdf_t *doc, unsigned page, float width, float length, - float *scale); -int cfPDFDuplicatePage(cf_pdf_t *doc, unsigned page, unsigned count); -int cfPDFFillForm(cf_pdf_t *doc, cf_opt_t *opt); -int cfPDFPages(const char *filename); -int cfPDFPagesFP(FILE *file); - -#ifdef __cplusplus -} -#endif - -#endif // !_CUPS_FILTERS_PDF_H_ From 622272bbc722ae5c64ad08714678f0988c8ea683 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Tue, 15 Oct 2024 17:01:43 +0530 Subject: [PATCH 49/64] updated pclmtoraster.c, changed in the Makefile as well --- Makefile.am | 2 +- cupsfilters/C-pclmtoraster.c | 1253 ++++++++++++++++++++++++++++++++++ 2 files changed, 1254 insertions(+), 1 deletion(-) create mode 100644 cupsfilters/C-pclmtoraster.c diff --git a/Makefile.am b/Makefile.am index fc995c10..93b7bf84 100644 --- a/Makefile.am +++ b/Makefile.am @@ -178,7 +178,7 @@ libcupsfilters_la_SOURCES = \ cupsfilters/lut.c \ cupsfilters/mupdftopwg.c \ cupsfilters/pack.c \ - cupsfilters/pclmtoraster.cxx \ + cupsfilters/C-pclmtoraster.c \ cupsfilters/C-pdf.c \ cupsfilters/pdftopdf/C-pdftopdf.c \ cupsfilters/pdftopdf/C-pdftopdf-private.h \ diff --git a/cupsfilters/C-pclmtoraster.c b/cupsfilters/C-pclmtoraster.c new file mode 100644 index 00000000..a7b0d023 --- /dev/null +++ b/cupsfilters/C-pclmtoraster.c @@ -0,0 +1,1253 @@ +// +// PCLm/Raster-only PDF to Raster filter function for libcupsfilters. +// +// Copyright © 2020 by Vikrant Malik +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +// +// Include necessary headers... +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#define MAX_BYTES_PER_PIXEL 32 + +typedef struct pclmtoraster_data_s { + int outformat; + int numcolors; + int rowsize; + cups_page_header_t header; + char pageSizeRequested[64]; + int bi_level; + // image swapping + int swap_image_x; + int swap_image_y; + int swap_margin_x; + int swap_margin_y; + unsigned int nplanes; + unsigned int nbands; + unsigned int bytesPerLine; + char colorspace[32]; // Use fixed-size string +} pclmtoraster_data_t; + +void +init_pclmtoraster_data_t(pclmtoraster_data_t *data) +{ + data->outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; + data->numcolors = 0; + data->rowsize = 0; + data->bi_level = 0; + // image swapping + data->swap_image_x = false; + data->swap_image_y = false; + // margin swapping + data->swap_margin_x = false; + data->swap_margin_y = false; + // Note: When CUPS_ORDER_BANDED, + // cupsBytesPerLine = bytesPerLine * cupsNumColors + strncpy(data->colorspace, "\0", sizeof(data->colorspace)); +} + +typedef unsigned char *(*convert_cspace_func)(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data); + +typedef unsigned char *(*convert_line_func)(unsigned char *src, + unsigned char *dst, + unsigned char *buf, + unsigned int row, + unsigned int plane, + pclmtoraster_data_t *data, + convert_cspace_func convertcspace); + +typedef struct pclm_conversion_function_s +{ + convert_cspace_func convertcspace;// Function for conversion of colorspaces + convert_line_func convertline; // Function tom modify raster data of a line +} pclm_conversion_function_t; + +static int +parse_opts(cf_filter_data_t *data, + cf_filter_out_format_t outformat, + pclmtoraster_data_t *pclmtoraster_data) +{ + int num_options = 0; + cups_option_t* options = NULL; + const char* t = NULL; + const char *val; + cf_logfunc_t log = data->logfunc; + void *ld = data->logdata; + cups_page_header_t *header = &(pclmtoraster_data->header); + cups_cspace_t cspace = (cups_cspace_t)(-1); + + pclmtoraster_data->outformat = outformat; + + // + // CUPS option list + // + + num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); + + t = cupsGetOption("media-class", num_options, options); + if (t == NULL) + t = cupsGetOption("MediaClass", num_options, options); + if (t != NULL) + { + if (strcasestr(t, "pwg")) + pclmtoraster_data->outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; + } + + cfRasterPrepareHeader(header, data, outformat, outformat, 0, &cspace); + + if(header->Duplex) + { + int backside; + // analyze options relevant to Duplex + // APDuplexRequiresFlippedMargin + enum + { + FM_NO, + FM_FALSE, + FM_TRUE + } flippedMargin = FM_NO; + + backside = cfGetBackSideOrientation(data); + + if (backside >= 0) + { + flippedMargin = (backside & 16 ? FM_TRUE : + (backside & 8 ? FM_FALSE : + FM_NO)); + backside &= 7; + + if(backside == CF_BACKSIDE_MANUAL_TUMBLE && header->Tumble) + { + pclmtoraster_data->swap_image_x = true; + pclmtoraster_data->swap_image_y = true; + pclmtoraster_data->swap_margin_x = true; + pclmtoraster_data->swap_margin_y = true; + if (flippedMargin == FM_TRUE) + pclmtoraster_data->swap_margin_y = false; + } + else if(backside == CF_BACKSIDE_ROTATED && !header->Tumble) + { + pclmtoraster_data->swap_image_x = true; + pclmtoraster_data->swap_image_y = true; + pclmtoraster_data->swap_margin_x = true; + pclmtoraster_data->swap_margin_y = true; + if (flippedMargin == FM_TRUE) + pclmtoraster_data->swap_margin_y = false; + } + else if (backside == CF_BACKSIDE_FLIPPED) + { + if (header->Tumble) + { + pclmtoraster_data->swap_image_x = true; + pclmtoraster_data->swap_margin_x = true; + pclmtoraster_data->swap_margin_y = true; + } + else + pclmtoraster_data->swap_image_y = true; + + if (flippedMargin == FM_FALSE) + pclmtoraster_data->swap_margin_y = !pclmtoraster_data->swap_margin_y; + } + } + } + + if ((val = cupsGetOption("print-color-mode", num_options, options)) != NULL && + !strncasecmp(val, "bi-level", 8)) + pclmtoraster_data->bi_level = 1; + + strncpy(pclmtoraster_data->pageSizeRequested, header->cupsPageSizeName, 63); + pclmtoraster_data->pageSizeRequested[63] = '\0'; + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPCLmToRaster: Page size requested: %s.", + header->cupsPageSizeName); + return 0; +} + +static bool +media_box_lookup(pdfio_obj_t *object, + float rect[4]) +{ + pdfio_rect_t mediaBox; + pdfio_dict_t *object_dict = pdfioObjGetDict(object); + if(pdfioDictGetRect(object_dict, "MediaBox", &mediaBox)) + return false; + + pdfioDictGetRect(object_dict, "MediaBox", &mediaBox); + + rect[0] = mediaBox.x1; + rect[1] = mediaBox.y1; + rect[2] = mediaBox.x2; + rect[3] = mediaBox.y2; + + return true; +} + +// +// 'rotate_bitmap()' - Function to rotate a bitmap +// (assumed that bits-per-component of the bitmap is 8). +// + +static unsigned char * // O - Output Bitmap +rotate_bitmap(unsigned char *src, // I - Input string + unsigned char *dst, // O - Destination string + unsigned int rotate, // I - Rotate value (0, 90, 180, 270) + unsigned int height, // I - Height of raster image in pixels + unsigned int width, // I - Width of raster image in pixels + int rowsize, // I - Length of one row of pixels + char* colorspace,// I - Colorspace of input bitmap + cf_logfunc_t log, // I - Log function + void *ld) // I - Aux. data for log function +{ + unsigned char *bp = src; + unsigned char *dp = dst; + unsigned char *temp = dst; + + if(rotate == 0) + return (src); + else if(rotate == 180) + { + if (strcmp(colorspace, "/DeviceGray") == 0) + { + bp = src + height * rowsize - 1; + dp = dst; + for (unsigned int h = 0; h < height; h++) + for (unsigned int w = 0; w < width; w++, bp--, dp++) + *dp = *bp; + } + else if (strcmp(colorspace, "/DeviceCMYK") == 0) + { + bp = src + height * rowsize - 4; + dp = dst; + for (unsigned int h = 0; h < height; h ++) + { + for (unsigned int w = 0; w < width; w ++, bp -= 4, dp += 4) + { + dp[0] = bp[0]; + dp[1] = bp[1]; + dp[2] = bp[2]; + dp[3] = bp[3]; + } + } + } + else if (strcmp(colorspace, "/DeviceRGB") == 0) + { + bp = src + height * rowsize - 3; + dp = dst; + for (unsigned int h = 0; h < height; h ++) + { + for (unsigned int w = 0; w < width; w ++, bp -= 3, dp += 3) + { + dp[0] = bp[0]; + dp[1] = bp[1]; + dp[2] = bp[2]; + } + } + } + } + else if (rotate == 270) + { + if (strcmp(colorspace, "/DeviceGray") == 0) + { + bp = src; + dp = dst; + for (unsigned int h = 0; h < height; h ++) + { + bp = src + (height - h) - 1; + for (unsigned int w = 0; w < width; w ++, bp += height , dp ++) + *dp = *bp; + } + } + else if (strcmp(colorspace, "/DeviceCMYK") == 0) + { + for (unsigned int h = 0; h < height; h++) + { + bp = src + (height - h) * 4 - 4; + for (unsigned int i = 0; i < width; i ++, bp += height * 4 , dp += 4) + { + dp[0] = bp[0]; + dp[1] = bp[1]; + dp[2] = bp[2]; + dp[3] = bp[3]; + } + } + } + else if (strcmp(colorspace, "/DeviceRGB") == 0) + { + bp = src; + dp = dst; + for (unsigned int h = 0; h < height; h ++) + { + bp = src + (height - h) * 3 - 3; + for (unsigned int i = 0; i < width; i ++, bp += height * 3 , dp += 3) + { + dp[0] = bp[0]; + dp[1] = bp[1]; + dp[2] = bp[2]; + } + } + } + } + else if (rotate == 90) + { + if (strcmp(colorspace, "/DeviceGray") == 0) + { + for (unsigned int h = 0; h < height; h ++) + { + bp = src + (width - 1) * height + h; + for (unsigned int i = 0; i < width; i ++, bp -= height , dp ++) + *dp = *bp; + } + } + else if (strcmp(colorspace, "/DeviceCMYK") == 0) + { + for (unsigned int h = 0; h < height; h ++) + { + bp = src + (width - 1) * height * 4 + 4 * h; + for (unsigned int i = 0; i < width; i ++, bp -= height * 4 , dp += 4) + { + dp[0] = bp[0]; + dp[1] = bp[1]; + dp[2] = bp[2]; + dp[3] = bp[3]; + } + } + } + else if (strcmp(colorspace, "/DeviceRGB") == 0) + { + for (unsigned int h = 0; h < height; h ++) + { + bp = src + (width - 1) * height * 3 + 3 * h; + for (unsigned int i = 0; i < width; i ++, bp -= height * 3 , dp += 3) + { + dp[0] = bp[0]; + dp[1] = bp[1]; + dp[2] = bp[2]; + } + } + } + } + else + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPCLmToRaster: Incorrect Rotate Value %d, not rotating", + rotate); + return (src); + } + + return (temp); +} + +static unsigned char * +rgb_to_rgbw_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + cfImageRGBToCMYK(src, dst, pixels); + for (unsigned int i = 0; i < 4 * pixels; i ++) + dst[i] = ~dst[i]; + return (dst); +} + +static unsigned char * +rgb_to_cmyk_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + cfImageRGBToCMYK(src, dst, pixels); + return (dst); +} + + +static unsigned char * +rgb_to_cmy_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + cfImageRGBToCMY(src, dst, pixels); + return (dst); +} + + +static unsigned char * +rgb_to_white_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + if (data->header.cupsBitsPerColor != 1) + cfImageRGBToWhite(src, dst, pixels); + else + { + cfImageRGBToWhite(src, src, pixels); + cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); + } + return (dst); +} + + +static unsigned char * +rgb_to_black_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + if (data->header.cupsBitsPerColor != 1) + cfImageRGBToBlack(src, dst, pixels); + else + { + cfImageRGBToBlack(src, src, pixels); + cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); + } + return (dst); +} + + +static unsigned char * +cmyk_to_rgb_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + cfImageCMYKToRGB(src, dst, pixels); + return (dst); +} + + +static unsigned char * +cmyk_to_rgbw_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + for (unsigned int i = 0; i < 4 * pixels; i ++) + dst[i] = ~src[i]; + return (dst); +} + + +static unsigned char * +cmyk_to_cmy_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + // Converted first to RGB and then to cmy for better outputs. + cfImageCMYKToRGB(src, src, pixels); + cfImageRGBToCMY(src, dst, pixels); + return (dst); +} + + +static unsigned char * +cmyk_to_white_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + if (data->header.cupsBitsPerColor != 1) + cfImageCMYKToWhite(src, dst, pixels); + else + { + cfImageCMYKToWhite(src, src, pixels); + cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); + } + return (dst); +} + + +static unsigned char * +cmyk_to_black_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + if (data->header.cupsBitsPerColor != 1) + cfImageCMYKToBlack(src, dst, pixels); + else + { + cfImageCMYKToBlack(src, src, pixels); + cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); + } + return (dst); +} + + +static unsigned char * +gray_to_rgb_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + cfImageWhiteToRGB(src, dst, pixels); + return (dst); +} + + +static unsigned char * +gray_to_rgbw_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + cfImageWhiteToCMYK(src, dst, pixels); + for (unsigned int i = 0; i < 4 * pixels; i ++) + dst[i] = ~dst[i]; + return (dst); +} + + +static unsigned char * +gray_to_cmyk_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + cfImageWhiteToCMYK(src, dst, pixels); + return (dst); +} + + +static unsigned char * +gray_to_cmy_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + cfImageWhiteToCMY(src, dst, pixels); + return (dst); +} + + +static unsigned char * +gray_to_black_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + if (data->header.cupsBitsPerColor != 1) + cfImageWhiteToBlack(src, dst, pixels); + else + { + cfImageWhiteToBlack(src, src, pixels); + cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); + } + return (dst); +} + + +static unsigned char * +convert_cspace_no_op(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + return (src); +} + + +// +// 'convert_line()' - Function to convert colorspace and bits-per-pixel +// of a single line of raster data. +// + +static unsigned char * // O - Output string +convert_line(unsigned char *src, // I - Input line + unsigned char *dst, // O - Destination string + unsigned char *buf, // I - Buffer string + unsigned int row, // I - Current Row + unsigned int plane, // I - Plane/Band + pclmtoraster_data_t *data, + convert_cspace_func convertcspace) +{ + // + // Use only convertcspace if conversion of bits and conversion of color order + // is not required, or if dithering is required, for faster processing of + // raster output. + // + + unsigned int pixels = data->header.cupsWidth; + if ((data->header.cupsBitsPerColor == 1 && + data->header.cupsNumColors == 1) || + (data->header.cupsBitsPerColor == 8 && + data->header.cupsColorOrder == CUPS_ORDER_CHUNKED)) + dst = convertcspace(src, dst, row, pixels, data); + else + { + for (unsigned int i = 0; i < pixels; i ++) + { + unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; + unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; + unsigned char *pb; + pb = convertcspace(src + i * data->numcolors, pixelBuf1, row, 1, data); + pb = cfConvertBits(pb, pixelBuf2, i, row, data->header.cupsNumColors, + data->header.cupsBitsPerColor); + cfWritePixel(dst, plane, i, pb, data->header.cupsNumColors, + data->header.cupsBitsPerColor, data->header.cupsColorOrder); + } + } + return (dst); +} + + +// +// 'convert_reverse_line()' - Function to convert colorspace and bits-per-pixel +// of a single line of raster data and reverse the +// line. +// + +static unsigned char * // O - Output string +convert_reverse_line(unsigned char *src, // I - Input line + unsigned char *dst, // O - Destination + // string + unsigned char *buf, // I - Buffer string + unsigned int row, // I - Current Row + unsigned int plane, // I - Plane/Band + pclmtoraster_data_t *data, // I - pclmtoraster + // filter data + convert_cspace_func convertcspace) // I - Function for + // conversion of + // colorspace +{ + // + // Use only convertcspace if conversion of bits and conversion of color order + // is not required, or if dithering is required, for faster processing of + // raster output. + // + + unsigned int pixels = data->header.cupsWidth; + if (data->header.cupsBitsPerColor == 1 && data->header.cupsNumColors == 1) + { + buf = convertcspace(src, buf, row, pixels, data); + dst = cfReverseOneBitLine(buf, dst, pixels, data->bytesPerLine); + } + else if (data->header.cupsBitsPerColor == 8 && + data->header.cupsColorOrder == CUPS_ORDER_CHUNKED) + { + unsigned char *dp = dst; + // Assign each pixel of buf to dst in the reverse order. + buf = convertcspace(src, buf, row, pixels, data) + + (data->header.cupsWidth - 1) * data->header.cupsNumColors; + for (unsigned int i = 0; i < pixels; + i ++, buf-=data->header.cupsNumColors, dp+=data->header.cupsNumColors) + for (unsigned int j = 0; j < data->header.cupsNumColors; j ++) + dp[j] = buf[j]; + } + else + { + for (unsigned int i = 0; i < pixels; i ++) + { + unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; + unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; + unsigned char *pb; + pb = convertcspace(src + (pixels - i - 1) * (data->numcolors), pixelBuf1, + row, 1, data); + pb = cfConvertBits(pb, pixelBuf2, i, row, data->header.cupsNumColors, + data->header.cupsBitsPerColor); + cfWritePixel(dst, plane, i, pb, data->header.cupsNumColors, + data->header.cupsBitsPerColor, data->header.cupsColorOrder); + } + } + return (dst); +} + + +static void // O - Exit status +select_convert_func(int pgno, // I - Page number + cf_logfunc_t log, // I - Log function + void *ld, // I - Aux. data for log + // function + pclmtoraster_data_t *data, // I - pclmtoraster filter + // data + pclm_conversion_function_t *convert)// I - Conversion function +{ + // Set rowsize and numcolors based on colorspace of raster data + cups_page_header_t header = data->header; + char *colorspace = data->colorspace; + if (strcmp(colorspace, "/DeviceRGB") == 0) + { + data->rowsize = header.cupsWidth * 3; + data->numcolors = 3; + } + else if (strcmp(colorspace, "/DeviceCMYK") == 0) + { + data->rowsize = header.cupsWidth * 4; + data->numcolors = 4; + } + else if (strcmp(colorspace, "/DeviceGray") == 0) + { + data->rowsize = header.cupsWidth; + data->numcolors = 1; + } + else + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPCLmToRaster: Colorspace %s not supported, " + "defaulting to /deviceRGB", + colorspace); + strcpy(data->colorspace, "/DeviceRGB"); + data->rowsize = header.cupsWidth * 3; + data->numcolors = 3; + } + + convert->convertcspace = convert_cspace_no_op; //Default function + // Select convertcspace function + switch (header.cupsColorSpace) + { + case CUPS_CSPACE_K: + if (strcmp(colorspace, "/DeviceRGB") == 0) + convert->convertcspace = rgb_to_black_line; + else if (strcmp(colorspace, "/DeviceCMYK") == 0) + convert->convertcspace = cmyk_to_black_line; + else if (strcmp(colorspace, "/DeviceGray") == 0) + convert->convertcspace = gray_to_black_line; + break; + case CUPS_CSPACE_W: + case CUPS_CSPACE_SW: + if (strcmp(colorspace, "/DeviceRGB") == 0) + convert->convertcspace = rgb_to_white_line; + else if (strcmp(colorspace, "/DeviceCMYK") == 0) + convert->convertcspace = cmyk_to_white_line; + break; + case CUPS_CSPACE_CMY: + if (strcmp(colorspace, "/DeviceRGB") == 0) + convert->convertcspace = rgb_to_cmy_line; + else if (strcmp(colorspace, "/DeviceCMYK") == 0) + convert->convertcspace = cmyk_to_cmy_line; + else if (strcmp(colorspace, "/DeviceGray") == 0) + convert->convertcspace = gray_to_cmy_line; + break; + case CUPS_CSPACE_CMYK: + if (strcmp(colorspace, "/DeviceRGB") == 0) + convert->convertcspace = rgb_to_cmyk_line; + else if (strcmp(colorspace, "/DeviceGray") == 0) + convert->convertcspace = gray_to_cmyk_line; + break; + case CUPS_CSPACE_RGBW: + if (strcmp(colorspace, "/DeviceRGB") == 0) + convert->convertcspace = rgb_to_rgbw_line; + else if (strcmp(colorspace, "/DeviceCMYK") == 0) + convert->convertcspace = cmyk_to_rgbw_line; + else if (strcmp(colorspace, "/DeviceGray") == 0) + convert->convertcspace = gray_to_rgbw_line; + break; + case CUPS_CSPACE_RGB: + case CUPS_CSPACE_ADOBERGB: + case CUPS_CSPACE_SRGB: + default: + if (strcmp(colorspace, "/DeviceCMYK") == 0) + convert->convertcspace = cmyk_to_rgb_line; + else if (strcmp(colorspace, "/DeviceGray") == 0) + convert->convertcspace = gray_to_rgb_line; + break; + } + + // Select convertline function + if (header.Duplex && (pgno & 1) && data->swap_image_x) + convert->convertline = convert_reverse_line; + else + convert->convertline = convert_line; +} + +bool +process_image(pdfio_dict_t *dict, const char *key, pclmtoraster_data_t *data, int pixel_count, unsigned char *bitmap) +{ + char *buffer; + pdfio_obj_t *image = pdfioDictGetObj(dict, key); + + //... verify the object has type "Image", then do something with the image object ... + + if (strcmp(pdfioObjGetType(image), "image") == 0) + { + pdfio_dict_t *imgdict = pdfioObjGetDict(image); + if (!imgdict) + return true; + + pdfio_stream_t *img_str = pdfioObjOpenStream(image, true); + size_t bufsize = pdfioStreamRead(img_str, buffer, 1024); + + int width = pdfioDictGetNumber(imgdict, "Width"); + int height = pdfioDictGetNumber(imgdict, "Height"); + + data->header.cupsHeight += height; + + if (pixel_count == 0) + bitmap = (unsigned char *)malloc(bufsize); + + else + bitmap = (unsigned char *)realloc(bitmap, pixel_count + bufsize); + + memcpy(bitmap + pixel_count, buffer, bufsize); + pixel_count += bufsize; + + if (width > data->header.cupsWidth) + data->header.cupsWidth = width; + } + + return (true); +} + +// +// 'out_page()' - Function to convert a single page of raster-only PDF/PCLm +// input to CUPS/PWG Raster. +// + +static int // O - Exit status +out_page(cups_raster_t* raster, // I - Raster stream + pdfio_obj_t* page, // I - QPDF Page Object + int pgno, // I - Page number + cf_logfunc_t log, // I - Log function + void* ld, // I - Aux. data for log function + pclmtoraster_data_t *data, // I - pclmtoraster filter data + cf_filter_data_t *filter_data, // I - filter data + pclm_conversion_function_t *convert)// I - Conversion functions +{ + int i; + long long rotate = 0; + float paperdimensions[2], margins[4], l, swap; + int pixel_count = 0, temp = 0; + float mediaBox[4]; + unsigned char *bitmap = NULL, + *colordata = NULL, + *lineBuf = NULL, + *line = NULL, + *dp = NULL; + pdfio_obj_t *colorspace_obj; + + + // Check if page is rotated. + pdfio_dict_t *pageDict = pdfioObjGetDict(page); + if(pdfioDictGetNumber(pageDict, "Rotate")) + { + rotate = pdfioDictGetNumber(pageDict, "Rotate"); + } + + // Get pagesize by the mediabox key of the page. + if (!media_box_lookup(page, mediaBox)) + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPCLmToRaster: PDF page %d doesn't contain a valid mediaBox", + pgno + 1); + return (1); + } + else + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPCLmToRaster: mediaBox = [%f %f %f %f]: ", + mediaBox[0], mediaBox[1], mediaBox[2], mediaBox[3]); + l = mediaBox[2] - mediaBox[0]; + if (l < 0) + l = -l; + if (rotate == 90 || rotate == 270) + data->header.PageSize[1] = (unsigned)l; + else + data->header.PageSize[0] = (unsigned)l; + l = mediaBox[3] - mediaBox[1]; + if (l < 0) + l = -l; + if (rotate == 90 || rotate == 270) + data->header.PageSize[0] = (unsigned)l; + else + data->header.PageSize[1] = (unsigned)l; + } + + memset(paperdimensions, 0, sizeof(paperdimensions)); + for (i = 0; i < 4; i ++) + margins[i] = -1.0; + if (filter_data != NULL) + { + cfGetPageDimensions(filter_data->printer_attrs, filter_data->job_attrs, + filter_data->num_options, filter_data->options, + &(data->header), 0, + &(paperdimensions[0]), &(paperdimensions[1]), + &(margins[0]), &(margins[1]), + &(margins[2]), &(margins[3]), NULL, NULL); + + cfSetPageDimensionsToDefault(&(paperdimensions[0]), &(paperdimensions[1]), + &(margins[0]), &(margins[1]), + &(margins[2]), &(margins[3]), + log, ld); + + if (data->outformat != CF_FILTER_OUT_FORMAT_CUPS_RASTER) + memset(margins, 0, sizeof(margins)); + } + else + { + for (int i = 0; i < 2; i ++) + paperdimensions[i] = data->header.PageSize[i]; + if (data->header.cupsImagingBBox[3] > 0.0) + { + // Set margins if we have a bounding box defined ... + if (data->outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER) + { + margins[0] = data->header.cupsImagingBBox[0]; + margins[1] = data->header.cupsImagingBBox[1]; + margins[2] = paperdimensions[0] - data->header.cupsImagingBBox[2]; + margins[3] = paperdimensions[1] - data->header.cupsImagingBBox[3]; + } + } + else + // ... otherwise use zero margins + for (int i = 0; i < 4; i ++) + margins[i] = 0.0; + } + + if (data->header.Duplex && (pgno & 1)) + { + // backside: change margin if needed + if (data->swap_margin_x) + { + swap = margins[2]; margins[2] = margins[0]; margins[0] = swap; + } + if (data->swap_margin_y) + { + swap = margins[3]; margins[3] = margins[1]; margins[1] = swap; + } + } + + // write page header + for (int i = 0; i < 2; i ++) + { + data->header.cupsPageSize[i] = paperdimensions[i]; + data->header.PageSize[i] = (unsigned int)(data->header.cupsPageSize[i] + + 0.5); + if (data->outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER) + data->header.Margins[i] = margins[i] + 0.5; + else + data->header.Margins[i] = 0; + } + if (data->outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER) + { + data->header.cupsImagingBBox[0] = margins[0]; + data->header.cupsImagingBBox[1] = margins[1]; + data->header.cupsImagingBBox[2] = paperdimensions[0] - margins[2]; + data->header.cupsImagingBBox[3] = paperdimensions[1] - margins[3]; + for (int i = 0; i < 4; i ++) + data->header.ImagingBoundingBox[i] = + (unsigned int)(data->header.cupsImagingBBox[i] + 0.5); + } + else + { + for (int i = 0; i < 4; i ++) + { + data->header.cupsImagingBBox[i] = 0.0; + data->header.ImagingBoundingBox[i] = 0; + } + } + + data->header.cupsWidth = 0; + data->header.cupsHeight = 0; + + // Loop over all raster images in a page and store them in bitmap. + + pdfio_dict_t *resources = pdfioDictGetDict(pdfioObjGetDict(page), "Resources"); + pdfio_dict_t *xobjects = pdfioDictGetDict(resources, "XObject"); + + // Iterate over the XObject dictionary to find images + pdfioDictIterateKeys(xobjects, (pdfio_dict_cb_t)process_image, data); + + // Swap width and height in landscape images + if(rotate == 270 || rotate == 90) + { + temp = data->header.cupsHeight; + data->header.cupsHeight = data->header.cupsWidth; + data->header.cupsWidth = temp; + } + + data->bytesPerLine = data->header.cupsBytesPerLine = + (data->header.cupsBitsPerPixel * data->header.cupsWidth + 7) / 8; + if (data->header.cupsColorOrder == CUPS_ORDER_BANDED) + data->header.cupsBytesPerLine *= data->header.cupsNumColors; + + if (!cupsRasterWriteHeader(raster, &(data->header))) + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPCLmToRaster: Can't write page %d header", pgno + 1); + return (1); + } + + colorspace_obj = pdfioDictGetObj(pdfioObjGetDict(page), "ColorSpace"); + + if (colorspace_obj) { + if (strcmp(pdfioObjGetType(colorspace_obj), "Name") == 0) + strncpy(data->colorspace, pdfioDictGetName(pdfioObjGetDict(colorspace_obj), "Type"), sizeof(data->colorspace) - 1); + else + strncpy(data->colorspace, "DeviceRGB", sizeof(data->colorspace) - 1); + } + + + // Select convertline and convertscpace function + select_convert_func(pgno, log, ld, data, convert); + + // If page is to be swapped in both x and y, rotate it by 180 degress + if (data->header.Duplex && (pgno & 1) && data->swap_image_y && + data->swap_image_x) + { + rotate = (rotate + 180) % 360; + data->swap_image_y = false; + data->swap_image_x = false; + } + + // Rotate Bitmap + if (rotate) + { + unsigned char *bitmap2 = (unsigned char *) malloc(pixel_count); + bitmap2 = rotate_bitmap(bitmap, bitmap2, rotate, data->header.cupsHeight, + data->header.cupsWidth, data->rowsize, + data->colorspace, log, ld); + free(bitmap); + bitmap = bitmap2; + } + + colordata = bitmap; + + // Write page image + lineBuf = (unsigned char *)malloc(data->bytesPerLine * sizeof(unsigned char)); + line = (unsigned char *)malloc(data->bytesPerLine * sizeof(unsigned char)); + + if (data->header.Duplex && (pgno & 1) && data->swap_image_y) + { + for (unsigned int plane = 0; plane < data->nplanes; plane ++) + { + unsigned char *bp = colordata + + (data->header.cupsHeight - 1) * (data->rowsize); + for (unsigned int h = data->header.cupsHeight; h > 0; h--) + { + for (unsigned int band = 0; band < data->nbands; band ++) + { + dp = convert->convertline(bp, line, lineBuf, h - 1, plane + band, + data, convert->convertcspace); + cupsRasterWritePixels(raster, dp, data->bytesPerLine); + } + bp -= data->rowsize; + } + } + } + else + { + for (unsigned int plane = 0; plane < data->nplanes; plane ++) + { + unsigned char *bp = colordata; + for (unsigned int h = 0; h < data->header.cupsHeight; h ++) + { + for (unsigned int band = 0; band < data->nbands; band ++) + { + dp = convert->convertline(bp, line, lineBuf, h, plane + band, + data, convert->convertcspace); + cupsRasterWritePixels(raster, dp, data->bytesPerLine); + } + bp += data->rowsize; + } + } + } + free(lineBuf); + free(line); + free(bitmap); + + return (0); +} + + +// +// 'cfFilterPCLmToRaster()' - Filter function to convert raster-only PDF/PCLm +// input to CUPS/PWG Raster output. +// + +int // O - Error status +cfFilterPCLmToRaster(int inputfd, // I - File descriptor input stream + int outputfd, // I - File descriptor output stream + int inputseekable, // I - Is input stream seekable? + // (unused) + cf_filter_data_t *data, // I - Job and printer data + void *parameters) // I - Filter-specific parameters + // (unused) +{ + cf_filter_out_format_t outformat; + FILE *inputfp; // Input file pointer + int fd = 0; // Copy file descriptor + char *filename, // PDF file to convert + tempfile[1024]; // Temporary file + char buffer[8192]; // Copy buffer + int bytes; // Bytes copied + int npages = 0; + pdfio_file_t *pdf = (pdfio_file_t *)malloc(sizeof(pdfio_file_t *));; + cups_raster_t *raster; + pclmtoraster_data_t pclmtoraster_data; + pclm_conversion_function_t convert; + cf_logfunc_t log = data->logfunc; + void *ld = data->logdata; + cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; + void *icd = data->iscanceleddata; + + if (parameters) + { + outformat = *(cf_filter_out_format_t *)parameters; + if (outformat != CF_FILTER_OUT_FORMAT_CUPS_RASTER && + outformat != CF_FILTER_OUT_FORMAT_PWG_RASTER && + outformat != CF_FILTER_OUT_FORMAT_APPLE_RASTER) + outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; + } + else + outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; + + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPCLmToRaster: Output format: %s", + (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER ? "CUPS Raster" : + (outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER ? "PWG Raster" : + "Apple Raster"))); + + // + // Open the input data stream specified by the inputfd... + // + + if ((inputfp = fdopen(inputfd, "r")) == NULL) + { + if (!iscanceled || !iscanceled(icd)) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPCLmToRaster: Unable to open input data stream."); + } + + return (1); + } + + if ((fd = cupsCreateTempFd(NULL, NULL, tempfile, sizeof(tempfile))) < 0) + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPCLmToRaster: Unable to copy PDF file: %s", + strerror(errno)); + fclose(inputfp); + return (1); + } + + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPCLmToRaster: Copying input to temp file \"%s\"", + tempfile); + + while ((bytes = fread(buffer, 1, sizeof(buffer), inputfp)) > 0) + bytes = write(fd, buffer, bytes); + + fclose(inputfp); + close(fd); + + filename = tempfile; + pdf = pdfioFileOpen(filename, NULL, NULL, NULL, NULL); + + if (parse_opts(data, outformat, &pclmtoraster_data) != 0) + { + free(pdf); + unlink(tempfile); + return (1); + } + + if (pclmtoraster_data.header.cupsBitsPerColor != 1 + && pclmtoraster_data.header.cupsBitsPerColor != 2 + && pclmtoraster_data.header.cupsBitsPerColor != 4 + && pclmtoraster_data.header.cupsBitsPerColor != 8 + && pclmtoraster_data.header.cupsBitsPerColor != 16) + { + if(log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPCLmToRaster: Specified color format is not supported: %s", + strerror(errno)); + free(pdf); + unlink(tempfile); + return (1); + } + + if (pclmtoraster_data.header.cupsColorOrder == CUPS_ORDER_PLANAR) + pclmtoraster_data.nplanes = pclmtoraster_data.header.cupsNumColors; + else + pclmtoraster_data.nplanes = 1; + if (pclmtoraster_data.header.cupsColorOrder == CUPS_ORDER_BANDED) + pclmtoraster_data.nbands = pclmtoraster_data.header.cupsNumColors; + else + pclmtoraster_data.nbands = 1; + + if ((raster = cupsRasterOpen(outputfd, + (pclmtoraster_data.outformat == + CF_FILTER_OUT_FORMAT_CUPS_RASTER ? + CUPS_RASTER_WRITE : + (pclmtoraster_data.outformat == + CF_FILTER_OUT_FORMAT_PWG_RASTER ? + CUPS_RASTER_WRITE_PWG : + CUPS_RASTER_WRITE_APPLE)))) == 0) + { + if(log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPCLmToRaster: Can't open raster stream: %s", + strerror(errno)); + free(pdf); + unlink(tempfile); + return (1); + } + + + npages = pdfioFileGetNumPages(pdf); + + for (int i = 0; i < npages; ++i) + { + pdfio_obj_t *pages = pdfioFileGetPage(pdf, i); + + if (iscanceled && iscanceled(icd)) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPCLmToRaster: Job canceled"); + break; + } + + if (log) log(ld, CF_LOGLEVEL_INFO, + "cfFilterPCLmToRaster: Starting page %d.", i + 1); + if (out_page(raster, pages, i, log, ld, &pclmtoraster_data,data, + &convert) != 0) + break; + + if (log) log(ld, CF_LOGLEVEL_INFO, + "cfFilterPCLmToRaster: Starting page %d.", (i + 1)); + if (out_page(raster, pages, i, log, ld, &pclmtoraster_data,data, + &convert) != 0) + break; + } + + cupsRasterClose(raster); + free(pdf); + unlink(tempfile); + return (0); +} From b85c6c11d9529a53b16c80c9d8ec7869e23500c2 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Tue, 15 Oct 2024 17:03:29 +0530 Subject: [PATCH 50/64] remove C++ pclmtoraster.cxx --- cupsfilters/pclmtoraster.cxx | 1215 ---------------------------------- 1 file changed, 1215 deletions(-) delete mode 100644 cupsfilters/pclmtoraster.cxx diff --git a/cupsfilters/pclmtoraster.cxx b/cupsfilters/pclmtoraster.cxx deleted file mode 100644 index a0491371..00000000 --- a/cupsfilters/pclmtoraster.cxx +++ /dev/null @@ -1,1215 +0,0 @@ -// -// PCLm/Raster-only PDF to Raster filter function for libcupsfilters. -// -// Copyright © 2020 by Vikrant Malik -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#define MAX_BYTES_PER_PIXEL 32 - - -typedef struct pclmtoraster_data_s -{ - cf_filter_out_format_t outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - int numcolors = 0; - int rowsize = 0; - cups_page_header_t header; - char pageSizeRequested[64]; - int bi_level = 0; - // image swapping - bool swap_image_x = false; - bool swap_image_y = false; - // margin swapping - bool swap_margin_x = false; - bool swap_margin_y = false; - unsigned int nplanes; - unsigned int nbands; - unsigned int bytesPerLine; // number of bytes per line - // Note: When CUPS_ORDER_BANDED, - // cupsBytesPerLine = bytesPerLine * cupsNumColors - std::string colorspace; // Colorspace of raster data -} pclmtoraster_data_t; - -typedef unsigned char *(*convert_cspace_func)(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data); - -typedef unsigned char *(*convert_line_func)(unsigned char *src, - unsigned char *dst, - unsigned char *buf, - unsigned int row, - unsigned int plane, - pclmtoraster_data_t *data, - convert_cspace_func convertcspace); - -typedef struct pclm_conversion_function_s -{ - convert_cspace_func convertcspace;// Function for conversion of colorspaces - convert_line_func convertline; // Function tom modify raster data of a line -} pclm_conversion_function_t; - - -static int -parse_opts(cf_filter_data_t *data, - cf_filter_out_format_t outformat, - pclmtoraster_data_t *pclmtoraster_data) -{ - int num_options = 0; - cups_option_t* options = NULL; - const char* t = NULL; - const char *val; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cups_page_header_t *header = &(pclmtoraster_data->header); - cups_cspace_t cspace = (cups_cspace_t)(-1); - - - pclmtoraster_data->outformat = outformat; - - // - // CUPS option list - // - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - t = cupsGetOption("media-class", num_options, options); - if (t == NULL) - t = cupsGetOption("MediaClass", num_options, options); - if (t != NULL) - { - if (strcasestr(t, "pwg")) - pclmtoraster_data->outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - } - - cfRasterPrepareHeader(header, data, outformat, outformat, 0, &cspace); - - if (header->Duplex) - { - int backside; - // analyze options relevant to Duplex - // APDuplexRequiresFlippedMargin - enum - { - FM_NO, - FM_FALSE, - FM_TRUE - } flippedMargin = FM_NO; - - backside = cfGetBackSideOrientation(data); - - if (backside >= 0) - { - flippedMargin = (backside & 16 ? FM_TRUE : - (backside & 8 ? FM_FALSE : - FM_NO)); - backside &= 7; - - if (backside == CF_BACKSIDE_MANUAL_TUMBLE && header->Tumble) - { - pclmtoraster_data->swap_image_x = pclmtoraster_data->swap_image_y = - true; - pclmtoraster_data->swap_margin_x = pclmtoraster_data->swap_margin_y = - true; - if (flippedMargin == FM_TRUE) - pclmtoraster_data->swap_margin_y = false; - } - else if (backside == CF_BACKSIDE_ROTATED && !header->Tumble) - { - pclmtoraster_data->swap_image_x = pclmtoraster_data->swap_image_y = - true; - pclmtoraster_data->swap_margin_x = pclmtoraster_data->swap_margin_y = - true; - if (flippedMargin == FM_TRUE) - pclmtoraster_data->swap_margin_y = false; - } - else if (backside == CF_BACKSIDE_FLIPPED) - { - if (header->Tumble) - { - pclmtoraster_data->swap_image_x = true; - pclmtoraster_data->swap_margin_x = - pclmtoraster_data->swap_margin_y = true; - } - else - pclmtoraster_data->swap_image_y = true; - if (flippedMargin == FM_FALSE) - pclmtoraster_data->swap_margin_y = - !(pclmtoraster_data->swap_margin_y); - } - } - } - - if ((val = cupsGetOption("print-color-mode", num_options, options)) != NULL && - !strncasecmp(val, "bi-level", 8)) - pclmtoraster_data->bi_level = 1; - - strncpy(pclmtoraster_data->pageSizeRequested, header->cupsPageSizeName, 63); - pclmtoraster_data->pageSizeRequested[63] = '\0'; - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: Page size requested: %s.", - header->cupsPageSizeName); - return(0); -} - - -static bool -media_box_lookup(QPDFObjectHandle object, - float rect[4]) -{ - // preliminary checks - if (!object.isDictionary() || !object.hasKey("/MediaBox")) - return (false); - - // assign mediabox values to rect - std::vector mediabox = - object.getKey("/MediaBox").getArrayAsVector(); - for (int i = 0; i < 4; ++i) - rect[i] = mediabox[i].getNumericValue(); - - return (mediabox.size() == 4); -} - - -// -// 'rotate_bitmap()' - Function to rotate a bitmap -// (assumed that bits-per-component of the bitmap is 8). -// - -static unsigned char * // O - Output Bitmap -rotate_bitmap(unsigned char *src, // I - Input string - unsigned char *dst, // O - Destination string - unsigned int rotate, // I - Rotate value (0, 90, 180, 270) - unsigned int height, // I - Height of raster image in pixels - unsigned int width, // I - Width of raster image in pixels - int rowsize, // I - Length of one row of pixels - std::string colorspace,// I - Colorspace of input bitmap - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - unsigned char *bp = src; - unsigned char *dp = dst; - unsigned char *temp = dst; - - if (rotate == 0) - return (src); - else if (rotate == 180) - { - if (colorspace == "/DeviceGray") - { - bp = src + height * rowsize - 1; - dp = dst; - for (unsigned int h = 0; h < height; h ++) - for (unsigned int w = 0; w < width; w ++, bp --, dp ++) - *dp = *bp; - } - else if (colorspace == "/DeviceCMYK") - { - bp = src + height * rowsize - 4; - dp = dst; - for (unsigned int h = 0; h < height; h ++) - { - for (unsigned int w = 0; w < width; w ++, bp -= 4, dp += 4) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - dp[3] = bp[3]; - } - } - } - else if (colorspace == "/DeviceRGB") - { - bp = src + height * rowsize - 3; - dp = dst; - for (unsigned int h = 0; h < height; h ++) - { - for (unsigned int w = 0; w < width; w ++, bp -= 3, dp += 3) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - } - } - } - } - else if (rotate == 270) - { - if (colorspace == "/DeviceGray") - { - bp = src; - dp = dst; - for (unsigned int h = 0; h < height; h ++) - { - bp = src + (height - h) - 1; - for (unsigned int w = 0; w < width; w ++, bp += height , dp ++) - *dp = *bp; - } - } - else if (colorspace == "/DeviceCMYK") - { - for (unsigned int h = 0; h < height; h++) - { - bp = src + (height - h) * 4 - 4; - for (unsigned int i = 0; i < width; i ++, bp += height * 4 , dp += 4) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - dp[3] = bp[3]; - } - } - } - else if (colorspace == "/DeviceRGB") - { - bp = src; - dp = dst; - for (unsigned int h = 0; h < height; h ++) - { - bp = src + (height - h) * 3 - 3; - for (unsigned int i = 0; i < width; i ++, bp += height * 3 , dp += 3) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - } - } - } - } - else if (rotate == 90) - { - if (colorspace == "/DeviceGray") - { - for (unsigned int h = 0; h < height; h ++) - { - bp = src + (width - 1) * height + h; - for (unsigned int i = 0; i < width; i ++, bp -= height , dp ++) - *dp = *bp; - } - } - else if (colorspace == "/DeviceCMYK") - { - for (unsigned int h = 0; h < height; h ++) - { - bp = src + (width - 1) * height * 4 + 4 * h; - for (unsigned int i = 0; i < width; i ++, bp -= height * 4 , dp += 4) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - dp[3] = bp[3]; - } - } - } - else if (colorspace == "/DeviceRGB") - { - for (unsigned int h = 0; h < height; h ++) - { - bp = src + (width - 1) * height * 3 + 3 * h; - for (unsigned int i = 0; i < width; i ++, bp -= height * 3 , dp += 3) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - } - } - } - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Incorrect Rotate Value %d, not rotating", - rotate); - return (src); - } - - return (temp); -} - - -static unsigned char * -rgb_to_rgbw_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageRGBToCMYK(src, dst, pixels); - for (unsigned int i = 0; i < 4 * pixels; i ++) - dst[i] = ~dst[i]; - return (dst); -} - - -static unsigned char * -rgb_to_cmyk_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageRGBToCMYK(src, dst, pixels); - return (dst); -} - - -static unsigned char * -rgb_to_cmy_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageRGBToCMY(src, dst, pixels); - return (dst); -} - - -static unsigned char * -rgb_to_white_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - if (data->header.cupsBitsPerColor != 1) - cfImageRGBToWhite(src, dst, pixels); - else - { - cfImageRGBToWhite(src, src, pixels); - cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); - } - return (dst); -} - - -static unsigned char * -rgb_to_black_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - if (data->header.cupsBitsPerColor != 1) - cfImageRGBToBlack(src, dst, pixels); - else - { - cfImageRGBToBlack(src, src, pixels); - cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); - } - return (dst); -} - - -static unsigned char * -cmyk_to_rgb_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageCMYKToRGB(src, dst, pixels); - return (dst); -} - - -static unsigned char * -cmyk_to_rgbw_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - for (unsigned int i = 0; i < 4 * pixels; i ++) - dst[i] = ~src[i]; - return (dst); -} - - -static unsigned char * -cmyk_to_cmy_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - // Converted first to RGB and then to cmy for better outputs. - cfImageCMYKToRGB(src, src, pixels); - cfImageRGBToCMY(src, dst, pixels); - return (dst); -} - - -static unsigned char * -cmyk_to_white_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - if (data->header.cupsBitsPerColor != 1) - cfImageCMYKToWhite(src, dst, pixels); - else - { - cfImageCMYKToWhite(src, src, pixels); - cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); - } - return (dst); -} - - -static unsigned char * -cmyk_to_black_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - if (data->header.cupsBitsPerColor != 1) - cfImageCMYKToBlack(src, dst, pixels); - else - { - cfImageCMYKToBlack(src, src, pixels); - cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); - } - return (dst); -} - - -static unsigned char * -gray_to_rgb_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageWhiteToRGB(src, dst, pixels); - return (dst); -} - - -static unsigned char * -gray_to_rgbw_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageWhiteToCMYK(src, dst, pixels); - for (unsigned int i = 0; i < 4 * pixels; i ++) - dst[i] = ~dst[i]; - return (dst); -} - - -static unsigned char * -gray_to_cmyk_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageWhiteToCMYK(src, dst, pixels); - return (dst); -} - - -static unsigned char * -gray_to_cmy_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageWhiteToCMY(src, dst, pixels); - return (dst); -} - - -static unsigned char * -gray_to_black_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - if (data->header.cupsBitsPerColor != 1) - cfImageWhiteToBlack(src, dst, pixels); - else - { - cfImageWhiteToBlack(src, src, pixels); - cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); - } - return (dst); -} - - -static unsigned char * -convert_cspace_no_op(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - return (src); -} - - -// -// 'convert_line()' - Function to convert colorspace and bits-per-pixel -// of a single line of raster data. -// - -static unsigned char * // O - Output string -convert_line(unsigned char *src, // I - Input line - unsigned char *dst, // O - Destination string - unsigned char *buf, // I - Buffer string - unsigned int row, // I - Current Row - unsigned int plane, // I - Plane/Band - pclmtoraster_data_t *data, - convert_cspace_func convertcspace) -{ - // - // Use only convertcspace if conversion of bits and conversion of color order - // is not required, or if dithering is required, for faster processing of - // raster output. - // - - unsigned int pixels = data->header.cupsWidth; - if ((data->header.cupsBitsPerColor == 1 && - data->header.cupsNumColors == 1) || - (data->header.cupsBitsPerColor == 8 && - data->header.cupsColorOrder == CUPS_ORDER_CHUNKED)) - dst = convertcspace(src, dst, row, pixels, data); - else - { - for (unsigned int i = 0; i < pixels; i ++) - { - unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; - unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; - unsigned char *pb; - pb = convertcspace(src + i * data->numcolors, pixelBuf1, row, 1, data); - pb = cfConvertBits(pb, pixelBuf2, i, row, data->header.cupsNumColors, - data->header.cupsBitsPerColor); - cfWritePixel(dst, plane, i, pb, data->header.cupsNumColors, - data->header.cupsBitsPerColor, data->header.cupsColorOrder); - } - } - return (dst); -} - - -// -// 'convert_reverse_line()' - Function to convert colorspace and bits-per-pixel -// of a single line of raster data and reverse the -// line. -// - -static unsigned char * // O - Output string -convert_reverse_line(unsigned char *src, // I - Input line - unsigned char *dst, // O - Destination - // string - unsigned char *buf, // I - Buffer string - unsigned int row, // I - Current Row - unsigned int plane, // I - Plane/Band - pclmtoraster_data_t *data, // I - pclmtoraster - // filter data - convert_cspace_func convertcspace) // I - Function for - // conversion of - // colorspace -{ - // - // Use only convertcspace if conversion of bits and conversion of color order - // is not required, or if dithering is required, for faster processing of - // raster output. - // - - unsigned int pixels = data->header.cupsWidth; - if (data->header.cupsBitsPerColor == 1 && data->header.cupsNumColors == 1) - { - buf = convertcspace(src, buf, row, pixels, data); - dst = cfReverseOneBitLine(buf, dst, pixels, data->bytesPerLine); - } - else if (data->header.cupsBitsPerColor == 8 && - data->header.cupsColorOrder == CUPS_ORDER_CHUNKED) - { - unsigned char *dp = dst; - // Assign each pixel of buf to dst in the reverse order. - buf = convertcspace(src, buf, row, pixels, data) + - (data->header.cupsWidth - 1) * data->header.cupsNumColors; - for (unsigned int i = 0; i < pixels; - i ++, buf-=data->header.cupsNumColors, dp+=data->header.cupsNumColors) - for (unsigned int j = 0; j < data->header.cupsNumColors; j ++) - dp[j] = buf[j]; - } - else - { - for (unsigned int i = 0; i < pixels; i ++) - { - unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; - unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; - unsigned char *pb; - pb = convertcspace(src + (pixels - i - 1) * (data->numcolors), pixelBuf1, - row, 1, data); - pb = cfConvertBits(pb, pixelBuf2, i, row, data->header.cupsNumColors, - data->header.cupsBitsPerColor); - cfWritePixel(dst, plane, i, pb, data->header.cupsNumColors, - data->header.cupsBitsPerColor, data->header.cupsColorOrder); - } - } - return (dst); -} - - -static void // O - Exit status -select_convert_func(int pgno, // I - Page number - cf_logfunc_t log, // I - Log function - void *ld, // I - Aux. data for log - // function - pclmtoraster_data_t *data, // I - pclmtoraster filter - // data - pclm_conversion_function_t *convert)// I - Conversion function -{ - // Set rowsize and numcolors based on colorspace of raster data - cups_page_header_t header = data->header; - std::string colorspace = data->colorspace; - if (colorspace == "/DeviceRGB") - { - data->rowsize = header.cupsWidth * 3; - data->numcolors = 3; - } - else if (colorspace == "/DeviceCMYK") - { - data->rowsize = header.cupsWidth * 4; - data->numcolors = 4; - } - else if (colorspace == "/DeviceGray") - { - data->rowsize = header.cupsWidth; - data->numcolors = 1; - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Colorspace %s not supported, " - "defaulting to /deviceRGB", - colorspace.c_str()); - data->colorspace = "/DeviceRGB"; - data->rowsize = header.cupsWidth * 3; - data->numcolors = 3; - } - - convert->convertcspace = convert_cspace_no_op; //Default function - // Select convertcspace function - switch (header.cupsColorSpace) - { - case CUPS_CSPACE_K: - if (colorspace == "/DeviceRGB") - convert->convertcspace = rgb_to_black_line; - else if (colorspace == "/DeviceCMYK") - convert->convertcspace = cmyk_to_black_line; - else if (colorspace == "/DeviceGray") - convert->convertcspace = gray_to_black_line; - break; - case CUPS_CSPACE_W: - case CUPS_CSPACE_SW: - if (colorspace == "/DeviceRGB") - convert->convertcspace = rgb_to_white_line; - else if (colorspace == "/DeviceCMYK") - convert->convertcspace = cmyk_to_white_line; - break; - case CUPS_CSPACE_CMY: - if (colorspace == "/DeviceRGB") - convert->convertcspace = rgb_to_cmy_line; - else if (colorspace == "/DeviceCMYK") - convert->convertcspace = cmyk_to_cmy_line; - else if (colorspace == "/DeviceGray") - convert->convertcspace = gray_to_cmy_line; - break; - case CUPS_CSPACE_CMYK: - if (colorspace == "/DeviceRGB") - convert->convertcspace = rgb_to_cmyk_line; - else if (colorspace == "/DeviceGray") - convert->convertcspace = gray_to_cmyk_line; - break; - case CUPS_CSPACE_RGBW: - if (colorspace == "/DeviceRGB") - convert->convertcspace = rgb_to_rgbw_line; - else if (colorspace == "/DeviceCMYK") - convert->convertcspace = cmyk_to_rgbw_line; - else if (colorspace == "/DeviceGray") - convert->convertcspace = gray_to_rgbw_line; - break; - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_ADOBERGB: - case CUPS_CSPACE_SRGB: - default: - if (colorspace == "/DeviceCMYK") - convert->convertcspace = cmyk_to_rgb_line; - else if (colorspace == "/DeviceGray") - convert->convertcspace = gray_to_rgb_line; - break; - } - - // Select convertline function - if (header.Duplex && (pgno & 1) && data->swap_image_x) - convert->convertline = convert_reverse_line; - else - convert->convertline = convert_line; -} - - -// -// 'out_page()' - Function to convert a single page of raster-only PDF/PCLm -// input to CUPS/PWG Raster. -// - -static int // O - Exit status -out_page(cups_raster_t* raster, // I - Raster stream - QPDFObjectHandle page, // I - QPDF Page Object - int pgno, // I - Page number - cf_logfunc_t log, // I - Log function - void* ld, // I - Aux. data for log function - pclmtoraster_data_t *data, // I - pclmtoraster filter data - cf_filter_data_t *filter_data, // I - filter data - pclm_conversion_function_t *convert)// I - Conversion functions -{ - int i; - long long rotate = 0, - height, - width; - float paperdimensions[2], margins[4], l, swap; - int bufsize = 0, pixel_count = 0, - temp = 0; - float mediaBox[4]; - unsigned char *bitmap = NULL, - *colordata = NULL, - *lineBuf = NULL, - *line = NULL, - *dp = NULL; - QPDFObjectHandle image; - QPDFObjectHandle imgdict; - QPDFObjectHandle colorspace_obj; - - - // Check if page is rotated. - if (page.getKey("/Rotate").isInteger()) - rotate = page.getKey("/Rotate").getIntValueAsInt(); - - // Get pagesize by the mediabox key of the page. - if (!media_box_lookup(page, mediaBox)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: PDF page %d doesn't contain a valid mediaBox", - pgno + 1); - return (1); - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: mediaBox = [%f %f %f %f]: ", - mediaBox[0], mediaBox[1], mediaBox[2], mediaBox[3]); - l = mediaBox[2] - mediaBox[0]; - if (l < 0) - l = -l; - if (rotate == 90 || rotate == 270) - data->header.PageSize[1] = (unsigned)l; - else - data->header.PageSize[0] = (unsigned)l; - l = mediaBox[3] - mediaBox[1]; - if (l < 0) - l = -l; - if (rotate == 90 || rotate == 270) - data->header.PageSize[0] = (unsigned)l; - else - data->header.PageSize[1] = (unsigned)l; - } - - memset(paperdimensions, 0, sizeof(paperdimensions)); - for (i = 0; i < 4; i ++) - margins[i] = -1.0; - if (filter_data != NULL) - { - cfGetPageDimensions(filter_data->printer_attrs, filter_data->job_attrs, - filter_data->num_options, filter_data->options, - &(data->header), 0, - &(paperdimensions[0]), &(paperdimensions[1]), - &(margins[0]), &(margins[1]), - &(margins[2]), &(margins[3]), NULL, NULL); - - cfSetPageDimensionsToDefault(&(paperdimensions[0]), &(paperdimensions[1]), - &(margins[0]), &(margins[1]), - &(margins[2]), &(margins[3]), - log, ld); - - if (data->outformat != CF_FILTER_OUT_FORMAT_CUPS_RASTER) - memset(margins, 0, sizeof(margins)); - } - else - { - for (int i = 0; i < 2; i ++) - paperdimensions[i] = data->header.PageSize[i]; - if (data->header.cupsImagingBBox[3] > 0.0) - { - // Set margins if we have a bounding box defined ... - if (data->outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER) - { - margins[0] = data->header.cupsImagingBBox[0]; - margins[1] = data->header.cupsImagingBBox[1]; - margins[2] = paperdimensions[0] - data->header.cupsImagingBBox[2]; - margins[3] = paperdimensions[1] - data->header.cupsImagingBBox[3]; - } - } - else - // ... otherwise use zero margins - for (int i = 0; i < 4; i ++) - margins[i] = 0.0; - } - - if (data->header.Duplex && (pgno & 1)) - { - // backside: change margin if needed - if (data->swap_margin_x) - { - swap = margins[2]; margins[2] = margins[0]; margins[0] = swap; - } - if (data->swap_margin_y) - { - swap = margins[3]; margins[3] = margins[1]; margins[1] = swap; - } - } - - // write page header - for (int i = 0; i < 2; i ++) - { - data->header.cupsPageSize[i] = paperdimensions[i]; - data->header.PageSize[i] = (unsigned int)(data->header.cupsPageSize[i] + - 0.5); - if (data->outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER) - data->header.Margins[i] = margins[i] + 0.5; - else - data->header.Margins[i] = 0; - } - if (data->outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER) - { - data->header.cupsImagingBBox[0] = margins[0]; - data->header.cupsImagingBBox[1] = margins[1]; - data->header.cupsImagingBBox[2] = paperdimensions[0] - margins[2]; - data->header.cupsImagingBBox[3] = paperdimensions[1] - margins[3]; - for (int i = 0; i < 4; i ++) - data->header.ImagingBoundingBox[i] = - (unsigned int)(data->header.cupsImagingBBox[i] + 0.5); - } - else - { - for (int i = 0; i < 4; i ++) - { - data->header.cupsImagingBBox[i] = 0.0; - data->header.ImagingBoundingBox[i] = 0; - } - } - - data->header.cupsWidth = 0; - data->header.cupsHeight = 0; - - // Loop over all raster images in a page and store them in bitmap. - std::map images = page.getPageImages(); - for (auto const& iter: images) - { - image = iter.second; - imgdict = image.getDict(); // XObject dictionary - - std::shared_ptr actual_data = image.getStreamData(qpdf_dl_all); - width = imgdict.getKey("/Width").getIntValue(); - height = imgdict.getKey("/Height").getIntValue(); - colorspace_obj = imgdict.getKey("/ColorSpace"); - data->header.cupsHeight += height; - bufsize = actual_data->getSize(); - - if (!pixel_count) - bitmap = (unsigned char *) malloc(bufsize); - else - bitmap = (unsigned char *) realloc(bitmap, pixel_count + bufsize); - memcpy(bitmap + pixel_count, actual_data->getBuffer(), bufsize); - pixel_count += bufsize; - - if (width > data->header.cupsWidth) - data->header.cupsWidth = width; - } - - // Swap width and height in landscape images - if (rotate == 270 || rotate == 90) - { - temp = data->header.cupsHeight; - data->header.cupsHeight = data->header.cupsWidth; - data->header.cupsWidth = temp; - } - - data->bytesPerLine = data->header.cupsBytesPerLine = - (data->header.cupsBitsPerPixel * data->header.cupsWidth + 7) / 8; - if (data->header.cupsColorOrder == CUPS_ORDER_BANDED) - data->header.cupsBytesPerLine *= data->header.cupsNumColors; - - if (!cupsRasterWriteHeader(raster, &(data->header))) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Can't write page %d header", pgno + 1); - return (1); - } - - data->colorspace = (colorspace_obj.isName() ? - colorspace_obj.getName() : "/DeviceRGB"); - // Default for pclm files in DeviceRGB - - // Select convertline and convertscpace function - select_convert_func(pgno, log, ld, data, convert); - - // If page is to be swapped in both x and y, rotate it by 180 degress - if (data->header.Duplex && (pgno & 1) && data->swap_image_y && - data->swap_image_x) - { - rotate = (rotate + 180) % 360; - data->swap_image_y = false; - data->swap_image_x = false; - } - - // Rotate Bitmap - if (rotate) - { - unsigned char *bitmap2 = (unsigned char *) malloc(pixel_count); - bitmap2 = rotate_bitmap(bitmap, bitmap2, rotate, data->header.cupsHeight, - data->header.cupsWidth, data->rowsize, - data->colorspace, log, ld); - free(bitmap); - bitmap = bitmap2; - } - - colordata = bitmap; - - // Write page image - lineBuf = new unsigned char [data->bytesPerLine]; - line = new unsigned char [data->bytesPerLine]; - if (data->header.Duplex && (pgno & 1) && data->swap_image_y) - { - for (unsigned int plane = 0; plane < data->nplanes; plane ++) - { - unsigned char *bp = colordata + - (data->header.cupsHeight - 1) * (data->rowsize); - for (unsigned int h = data->header.cupsHeight; h > 0; h--) - { - for (unsigned int band = 0; band < data->nbands; band ++) - { - dp = convert->convertline(bp, line, lineBuf, h - 1, plane + band, - data, convert->convertcspace); - cupsRasterWritePixels(raster, dp, data->bytesPerLine); - } - bp -= data->rowsize; - } - } - } - else - { - for (unsigned int plane = 0; plane < data->nplanes; plane ++) - { - unsigned char *bp = colordata; - for (unsigned int h = 0; h < data->header.cupsHeight; h ++) - { - for (unsigned int band = 0; band < data->nbands; band ++) - { - dp = convert->convertline(bp, line, lineBuf, h, plane + band, - data, convert->convertcspace); - cupsRasterWritePixels(raster, dp, data->bytesPerLine); - } - bp += data->rowsize; - } - } - } - delete[] lineBuf; - delete[] line; - free(bitmap); - - return (0); -} - - -// -// 'cfFilterPCLmToRaster()' - Filter function to convert raster-only PDF/PCLm -// input to CUPS/PWG Raster output. -// - -int // O - Error status -cfFilterPCLmToRaster(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - // (unused) - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters - // (unused) -{ - cf_filter_out_format_t outformat; - FILE *inputfp; // Input file pointer - int fd = 0; // Copy file descriptor - char *filename, // PDF file to convert - tempfile[1024]; // Temporary file - char buffer[8192]; // Copy buffer - int bytes; // Bytes copied - int npages = 0; - QPDF *pdf = new QPDF(); - cups_raster_t *raster; - pclmtoraster_data_t pclmtoraster_data; - pclm_conversion_function_t convert; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - - if (parameters) - { - outformat = *(cf_filter_out_format_t *)parameters; - if (outformat != CF_FILTER_OUT_FORMAT_CUPS_RASTER && - outformat != CF_FILTER_OUT_FORMAT_PWG_RASTER && - outformat != CF_FILTER_OUT_FORMAT_APPLE_RASTER) - outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - } - else - outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: Output format: %s", - (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER ? "CUPS Raster" : - (outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER ? "PWG Raster" : - "Apple Raster"))); - - // - // Open the input data stream specified by the inputfd... - // - - if ((inputfp = fdopen(inputfd, "r")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: Unable to open input data stream."); - } - - return (1); - } - - if ((fd = cupsCreateTempFd(NULL, NULL, tempfile, sizeof(tempfile))) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Unable to copy PDF file: %s", - strerror(errno)); - fclose(inputfp); - return (1); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: Copying input to temp file \"%s\"", - tempfile); - - while ((bytes = fread(buffer, 1, sizeof(buffer), inputfp)) > 0) - bytes = write(fd, buffer, bytes); - - fclose(inputfp); - close(fd); - - filename = tempfile; - pdf->processFile(filename); - - if (parse_opts(data, outformat, &pclmtoraster_data) != 0) - { - delete(pdf); - unlink(tempfile); - return (1); - } - - if (pclmtoraster_data.header.cupsBitsPerColor != 1 - && pclmtoraster_data.header.cupsBitsPerColor != 2 - && pclmtoraster_data.header.cupsBitsPerColor != 4 - && pclmtoraster_data.header.cupsBitsPerColor != 8 - && pclmtoraster_data.header.cupsBitsPerColor != 16) - { - if(log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Specified color format is not supported: %s", - strerror(errno)); - delete(pdf); - unlink(tempfile); - return (1); - } - - if (pclmtoraster_data.header.cupsColorOrder == CUPS_ORDER_PLANAR) - pclmtoraster_data.nplanes = pclmtoraster_data.header.cupsNumColors; - else - pclmtoraster_data.nplanes = 1; - if (pclmtoraster_data.header.cupsColorOrder == CUPS_ORDER_BANDED) - pclmtoraster_data.nbands = pclmtoraster_data.header.cupsNumColors; - else - pclmtoraster_data.nbands = 1; - - if ((raster = cupsRasterOpen(outputfd, - (pclmtoraster_data.outformat == - CF_FILTER_OUT_FORMAT_CUPS_RASTER ? - CUPS_RASTER_WRITE : - (pclmtoraster_data.outformat == - CF_FILTER_OUT_FORMAT_PWG_RASTER ? - CUPS_RASTER_WRITE_PWG : - CUPS_RASTER_WRITE_APPLE)))) == 0) - { - if(log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Can't open raster stream: %s", - strerror(errno)); - delete(pdf); - unlink(tempfile); - return (1); - } - - std::vector pages = pdf->getAllPages(); - npages = pages.size(); - - for (int i = 0; i < npages; ++i) - { - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: Job canceled"); - break; - } - - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterPCLmToRaster: Starting page %d.", i + 1); - if (out_page(raster, pages[i], i, log, ld, &pclmtoraster_data,data, - &convert) != 0) - break; - } - - cupsRasterClose(raster); - delete pdf; - unlink(tempfile); - return (0); -} - -void operator delete[](void *p) throw () -{ - free(p); -} From 475dd857fa11715c08788373d38c47ba8195c1a8 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Sun, 20 Oct 2024 01:07:58 +0530 Subject: [PATCH 51/64] addition of final files --- cupsfilters/C-pclmtoraster.c | 3 +- cupsfilters/C-pdf.c | 20 +- cupsfilters/C-pdf.h | 9 +- cupsfilters/C-pwgtopdf.c | 541 ++++++++++++++++++ cupsfilters/pdftopdf/C-intervalset-private.h | 2 + cupsfilters/pdftopdf/C-intervalset.c | 34 +- cupsfilters/pdftopdf/C-nup-private.h | 2 + cupsfilters/pdftopdf/C-nup.c | 42 +- cupsfilters/pdftopdf/C-pdftopdf-private.h | 1 + .../pdftopdf/C-pdftopdf-processor-private.h | 8 +- cupsfilters/pdftopdf/C-pdftopdf-processor.c | 2 + cupsfilters/pdftopdf/C-pdftopdf.c | 6 +- cupsfilters/pdftopdf/C-pptypes-private.h | 2 + cupsfilters/pdftopdf/C-pptypes.c | 54 +- cupsfilters/pdftopdf/pdfio-cm-private.h | 2 + cupsfilters/pdftopdf/pdfio-cm.c | 3 +- cupsfilters/pdftopdf/pdfio-pdftopdf-private.h | 2 + .../pdfio-pdftopdf-processor-private.h | 8 +- .../pdftopdf/pdfio-pdftopdf-processor.c | 23 +- cupsfilters/pdftopdf/pdfio-pdftopdf.c | 2 + cupsfilters/pdftopdf/pdfio-tools-private.h | 8 +- cupsfilters/pdftopdf/pdfio-tools.c | 2 + cupsfilters/pdftopdf/pdfio-xobject-private.h | 2 + cupsfilters/pdftopdf/pdfio-xobject.c | 15 +- cupsfilters/pdftopdf/processor.h | 8 +- 25 files changed, 720 insertions(+), 81 deletions(-) create mode 100644 cupsfilters/C-pwgtopdf.c diff --git a/cupsfilters/C-pclmtoraster.c b/cupsfilters/C-pclmtoraster.c index a7b0d023..d776d406 100644 --- a/cupsfilters/C-pclmtoraster.c +++ b/cupsfilters/C-pclmtoraster.c @@ -2,6 +2,7 @@ // PCLm/Raster-only PDF to Raster filter function for libcupsfilters. // // Copyright © 2020 by Vikrant Malik +// Copyright 2024 Uddhav Phatak // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. @@ -833,7 +834,7 @@ process_image(pdfio_dict_t *dict, const char *key, pclmtoraster_data_t *data, in static int // O - Exit status out_page(cups_raster_t* raster, // I - Raster stream - pdfio_obj_t* page, // I - QPDF Page Object + pdfio_obj_t* page, // I - PDFio Page Object int pgno, // I - Page number cf_logfunc_t log, // I - Log function void* ld, // I - Aux. data for log function diff --git a/cupsfilters/C-pdf.c b/cupsfilters/C-pdf.c index a6945c37..d947cd89 100644 --- a/cupsfilters/C-pdf.c +++ b/cupsfilters/C-pdf.c @@ -1,3 +1,14 @@ +// +// Copyright 2012 Canonical Ltd. +// Copyright 2013 ALT Linux, Andrew V. Stepanov +// Copyright 2018 Sahil Arora +// Copyright 2024 Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + + #include #include #include @@ -5,11 +16,12 @@ #include #include + // -// 'make_real_box()' - Return a QPDF array object of real values for a box. +// 'make_real_box()' - Return a PDFio rect object of real values for a box. // -static pdfio_rect_t // O - QPDFObjectHandle for an array +static pdfio_rect_t // O - PDFioObjectHandle for a rect make_real_box(float values[4]) // I - Dimensions of the box in a float array { pdfio_rect_t rect; @@ -195,7 +207,7 @@ cfPDFAddType1Font(cf_pdf_t *pdf, } // -// 'dict_lookup_rect()' - Lookup for an array of rectangle dimensions in a QPDF +// 'dict_lookup_rect()' - Lookup for an array of rectangle dimensions in a PDFio // dictionary object. If it is found, store the values in // an array and return true, else return false. // @@ -353,7 +365,7 @@ lookup_opt(cf_opt_t *opt, // int cfPDFFillForm(cf_pdf_t *doc, cf_opt_t *opt) { - // PDFio does not directly support form filling. + // TODO: PDFio does not directly support form filling. return 0; } diff --git a/cupsfilters/C-pdf.h b/cupsfilters/C-pdf.h index efb94fdb..92e381d3 100644 --- a/cupsfilters/C-pdf.h +++ b/cupsfilters/C-pdf.h @@ -1,14 +1,7 @@ // // Copyright 2012 Canonical Ltd. // Copyright 2018 Sahil Arora -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Copyright 2012 Canonical Ltd. -// Copyright 2018 Sahil Arora +// Copyright 2024 Uddhav Phatak // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. diff --git a/cupsfilters/C-pwgtopdf.c b/cupsfilters/C-pwgtopdf.c new file mode 100644 index 00000000..fe5b9cea --- /dev/null +++ b/cupsfilters/C-pwgtopdf.c @@ -0,0 +1,541 @@ +// +// PWG/Apple Raster to PDF filter function for libcupsfilters. +// +// Copyright 2010 by Neil 'Superna' Armstrong +// Copyright 2012 by Tobias Hoffmann +// Copyright 2014-2022 by Till Kamppeter +// Copyright 2017 by Sahil Arora +// Copyright 2024 by Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // ntohl + +#include +#include + +#ifdef USE_LCMS1 +#include +#define cmsColorSpaceSignature icColorSpaceSignature +#define cmsSetLogErrorHandler cmsSetErrorHandler +#define cmsSigXYZData icSigXYZData +#define cmsSigLuvData icSigLuvData +#define cmsSigLabData icSigLabData +#define cmsSigYCbCrData icSigYCbCrData +#define cmsSigYxyData icSigYxyData +#define cmsSigRgbData icSigRgbData +#define cmsSigHsvData icSigHsvData +#define cmsSigHlsData icSigHlsData +#define cmsSigCmyData icSigCmyData +#define cmsSig3colorData icSig3colorData +#define cmsSigGrayData icSigGrayData +#define cmsSigCmykData icSigCmykData +#define cmsSig4colorData icSig4colorData +#define cmsSig2colorData icSig2colorData +#define cmsSig5colorData icSig5colorData +#define cmsSig6colorData icSig6colorData +#define cmsSig7colorData icSig7colorData +#define cmsSig8colorData icSig8colorData +#define cmsSig9colorData icSig9colorData +#define cmsSig10colorData icSig10colorData +#define cmsSig11colorData icSig11colorData +#define cmsSig12colorData icSig12colorData +#define cmsSig13colorData icSig13colorData +#define cmsSig14colorData icSig14colorData +#define cmsSig15colorData icSig15colorData +#define cmsSaveProfileToMem _cmsSaveProfileToMem +#else +#include +#endif + +#define DEFAULT_PDF_UNIT 72 // 1/72 inch + +#define PRE_COMPRESS + +// Compression method for providing data to PCLm Streams. +typedef enum compression_method_e +{ + DCT_DECODE = 0, + FLATE_DECODE, + RLE_DECODE +} compression_method_t; + +// Color conversion function +typedef unsigned char *(*convert_function)(unsigned char *src, + unsigned char *dst, + unsigned int pixels); + +// Bit conversion function +typedef unsigned char *(*bit_convert_function)(unsigned char *src, + unsigned char *dst, + unsigned int pixels); + +typedef struct pwgtopdf_doc_s // **** Document information **** +{ + cmsHPROFILE colorProfile = NULL; // ICC Profile to be applied to + // PDF + int cm_disabled = 0; // Flag raised if color + // management is disabled + convert_function conversion_function; // Raster color conversion + // function + bit_convert_function bit_function; // Raster bit function + FILE *outputfp; // Temporary file, if any + cf_logfunc_t logfunc; // Logging function, NULL for no + // logging + void *logdata; // User data for logging + // function, can be NULL + cf_filter_iscanceledfunc_t iscanceledfunc; // Function returning 1 when + // job is canceled, NULL for not + // supporting stop on cancel + void *iscanceleddata; // User data for is-canceled + // function, can be NULL +} pwgtopdf_doc_t; + +// PDF color conversion function +typedef void (*pdf_convert_function)(struct pdf_info * info, + pwgtopdf_doc_t *doc); + +struct pdf_info +{ + // PDFio-specific members + pdfio_file_t *pdf; // PDFio file handle + pdfio_obj_t *page; // PDFio page handle + + unsigned pagecount; + unsigned width; + unsigned height; + unsigned line_bytes; + unsigned bpp; + unsigned bpc; + unsigned pclm_num_strips; + unsigned pclm_strip_height_preferred; + unsigned *pclm_strip_height; // Dynamically allocated array in C + unsigned *pclm_strip_height_supported; // Dynamically allocated array + compression_method_t *pclm_compression_method_preferred; + size_t num_compression_methods; + char **pclm_source_resolution_supported; // Array of dynamically allocated strings + char *pclm_source_resolution_default; // Pointer to dynamically allocated string + char *pclm_raster_back_side; // Pointer to dynamically allocated string + unsigned char **pclm_strip_data; // Array of pointers to raw data (buffers) + char *render_intent; // Pointer to dynamically allocated string + cups_cspace_t color_space; + unsigned char *page_data; // Pointer to raw page data + double page_width, page_height; + cf_filter_out_format_t outformat; +}; + +void +init_pdf_info(struct pdf_info *info, + size_t num_methods, + size_t num_strips_supported) +{ + // Initialize primitive types + info->pagecount = 0; + info->width = 0; + info->height = 0; + info->line_bytes = 0; + info->bpp = 0; + info->bpc = 0; + info->pclm_num_strips = 0; + info->pclm_strip_height_preferred = 16; // Default strip height + info->page_width = 0; + info->page_height = 0; + info->outformat = CF_FILTER_OUT_FORMAT_PDF; + + // Allocate memory for pclm_strip_height (for strip height handling) + info->pclm_strip_height = (unsigned *)malloc(num_strips_supported * sizeof(unsigned)); + if (info->pclm_strip_height) + { + for (size_t i = 0; i < num_strips_supported; ++i) + { + info->pclm_strip_height[i] = 0; // Initialize to 0 or a specific value as needed + } + } + + // Allocate memory for pclm_strip_height_supported + info->pclm_strip_height_supported = (unsigned *)malloc(num_strips_supported * sizeof(unsigned)); + if (info->pclm_strip_height_supported) + { + for (size_t i = 0; i < num_strips_supported; ++i) + { + info->pclm_strip_height_supported[i] = 16; // Initialize to default value + } + } + + // Allocate memory for multiple compression methods + info->num_compression_methods = num_methods; + info->pclm_compression_method_preferred = (compression_method_t *)malloc(num_methods * sizeof(compression_method_t)); + if (info->pclm_compression_method_preferred) + { + for (size_t i = 0; i < num_methods; ++i) + { + info->pclm_compression_method_preferred[i] = 0; // Initialize to default or specific compression method + } + } + + info->pclm_source_resolution_default = (char *)malloc(64 * sizeof(char)); + if (info->pclm_source_resolution_default) + { + strcpy(info->pclm_source_resolution_default, ""); // Initialize to empty string + } + + info->pclm_raster_back_side = (char *)malloc(64 * sizeof(char)); + if (info->pclm_raster_back_side) + { + strcpy(info->pclm_raster_back_side, ""); // Initialize to empty string + } + + info->render_intent = (char *)malloc(64 * sizeof(char)); + if (info->render_intent) + { + strcpy(info->render_intent, ""); // Initialize to empty string + } + + info->pclm_source_resolution_supported = NULL; + info->pclm_strip_data = NULL; // Assuming this will be dynamically allocated elsewhere + + info->color_space = CUPS_CSPACE_K; // Default color space + info->page_data = NULL; // Will be allocated when needed + + info->pdf = NULL; // Initialize to NULL, will be set when opening a file + info->page = NULL; // Initialize to NULL, will be set when reading a page +} + +// Freeing the dynamically allocated memory +void free_pdf_info(struct pdf_info *info) +{ + if (info->pclm_strip_height) + { + free(info->pclm_strip_height); + info->pclm_strip_height = NULL; + } + + if (info->pclm_strip_height_supported) + { + free(info->pclm_strip_height_supported); + info->pclm_strip_height_supported = NULL; + } + + if (info->pclm_compression_method_preferred) + { + free(info->pclm_compression_method_preferred); + info->pclm_compression_method_preferred = NULL; + } + + // Free dynamically allocated strings + if (info->pclm_source_resolution_default) + { + free(info->pclm_source_resolution_default); + info->pclm_source_resolution_default = NULL; + } + + if (info->pclm_raster_back_side) + { + free(info->pclm_raster_back_side); + info->pclm_raster_back_side = NULL; + } + + if (info->render_intent) + { + free(info->render_intent); + info->render_intent = NULL; + } + + // Free any other dynamically allocated memory as necessary + if (info->pclm_strip_data) + { + free(info->pclm_strip_data); // Assuming this array will be dynamically allocated elsewhere + info->pclm_strip_data = NULL; + } + + if (info->page_data) + { + free(info->page_data); + info->page_data = NULL; + } +} + +// +// Bit conversion functions +// +static unsigned char * +invert_bits(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + unsigned int i; + + // Invert black to grayscale... + for (i = pixels, dst = src; i > 0; i --, dst ++) + *dst = ~*dst; + + return (dst); +} + +static unsigned char * +no_bit_conversion(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + return (src); +} + +// +// Color conversion functions +// + +static unsigned char * +rgb_to_cmyk(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + cfImageRGBToCMYK(src, dst, pixels); + return (dst); +} + + +static unsigned char * +white_to_cmyk(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + cfImageWhiteToCMYK(src, dst, pixels); + return (dst); +} + + +static unsigned char * +cmyk_to_rgb(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + cfImageCMYKToRGB(src, dst, pixels); + return (dst); +} + + +static unsigned char * +white_to_rgb(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + cfImageWhiteToRGB(src, dst, pixels); + return (dst); +} + + +static unsigned char * +rgb_to_white(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + cfImageRGBToWhite(src, dst, pixels); + return (dst); +} + + +static unsigned char * +cmyk_to_white(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + cfImageCMYKToWhite(src, dst, pixels); + return (dst); +} + + +static unsigned char * +no_color_conversion(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + return (src); +} + +// +// 'split_strings()' - Split a string to a vector of strings given some +// delimiters +// +// O - std::vector of std::string after splitting +// I - input string to be split +// I - string containing delimiters +// +// Function to split strings by delimiters + +char** +split_strings(const char *str, + const char *delimiters, + int *size) +{ + if (delimiters == NULL || strlen(delimiters) == 0) + delimiters = ","; + + + int capacity = 10; + char **result = malloc(capacity * sizeof(char *)); + + char *value = malloc(strlen(str) + 1); + + int token_count = 0; + int index = 0; + bool push_flag = false; + + for (size_t i = 0; i < strlen(str); i++) + { + if (strchr(delimiters, str[i]) != NULL) + { + if (push_flag && index > 0) + { + value[index] = '\0'; + result[token_count] = malloc(strlen(value) + 1); + strcpy(result[token_count], value); + token_count++; + + if (token_count >= capacity) + { + capacity *= 2; + result = realloc(result, capacity * sizeof(char *)); + } + + index = 0; + push_flag = false; + } + } + else + { + value[index++] = str[i]; + push_flag = true; + } + } + + if (push_flag && index > 0) + { + value[index] = '\0'; + result[token_count] = malloc(strlen(value) + 1); + strcpy(result[token_count], value); + token_count++; + } + + *size = token_count; + + free(value); + return result; +} + +// +// 'num_digits()' - Calculates the number of digits in an integer +// +// O - number of digits in the input integer +// I - the integer whose digits needs to be calculated +// + +static int +num_digits(int n) +{ + if (n == 0) + return (1); + int digits = 0; + while (n) + { + ++digits; + n /= 10; + } + return (digits); +} + +// +// 'int_to_fwstring()' - Convert a number to fixed width string by padding +// with zeroes +// O - converted string +// I - the integee which needs to be converted to string +// I - width of string required +// + +char* +int_to_fwstring(int n, int width) +{ + int num_zeroes = width - num_digits(n); + if (num_zeroes < 0) + num_zeroes = 0; + + int result_length = num_zeroes + num_digits(n) + 1; + char *result = malloc(result_length * sizeof(char)); + + for (int i = 0; i < num_zeroes; i++) + result[i] = '0'; + + sprintf(result + num_zeroes, "%d", n); + return result; +} + +static int +create_pdf_file(struct pdf_info *info, + const cf_filter_out_format_t outformat) +{ + if (!info || !info->pdf) + return 1; // Error handling + + pdfio_file_t *temp = pdfioFileCreate(pdfioFileGetName(info->pdf), NULL, NULL, NULL, NULL, NULL); + + info->pdf = temp; + info->outformat = outformat; + + return 0; +} + +static pdfio_rect_t +make_real_box(double x1, + double y1, + double x2, + double y2) +{ + pdfio_rect_t ret; + + ret.x1 = x1; + ret.y1 = y1; + ret.x2 = x2; + ret.y2 = y2; + + return (ret); +} + +static pdfio_rect_t +make_integer_box(double x1, + double y1, + double x2, + double y2) +{ + pdfio_rect_t ret; + + ret.x1 = x1; + ret.y1 = y1; + ret.x2 = x2; + ret.y2 = y2; + + return (ret); +} + + diff --git a/cupsfilters/pdftopdf/C-intervalset-private.h b/cupsfilters/pdftopdf/C-intervalset-private.h index 98e4f051..f5c04bf5 100644 --- a/cupsfilters/pdftopdf/C-intervalset-private.h +++ b/cupsfilters/pdftopdf/C-intervalset-private.h @@ -1,4 +1,6 @@ // +// Copyright 2024 Uddhav Phatak +// // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // @@ -14,28 +16,29 @@ const int _cfPDFToPDFIntervalSet_npos = INT_MAX; void -_cfPDFToPDFIntervalSet_init(_cfPDFToPDFIntervalSet *set) +_cfPDFToPDFIntervalSet_init(_cfPDFToPDFIntervalSet *set) // {{{ { set->data = NULL; set->size = 0; set->capacity = 0; } +// }}} void -_cfPDFToPDFIntervalSet_clear(_cfPDFToPDFIntervalSet *set) +_cfPDFToPDFIntervalSet_clear(_cfPDFToPDFIntervalSet *set) // {{{ { free(set->data); set->data = NULL; set->size = 0; set->capacity = 0; } +// }}} void -_cfPDFToPDFIntervalSet_add(_cfPDFToPDFIntervalSet *set, int start, int end) +_cfPDFToPDFIntervalSet_add(_cfPDFToPDFIntervalSet *set, int start, int end) // {{{ { if (start >= end) return; - if (set->size == set->capacity) { @@ -47,10 +50,11 @@ _cfPDFToPDFIntervalSet_add(_cfPDFToPDFIntervalSet *set, int start, int end) set->data[set->size].end = end; set->size++; } +// }}} void _cfPDFToPDFIntervalSet_add_single(_cfPDFToPDFIntervalSet *set, - int start) + int start) // {{{ { key_t end = _cfPDFToPDFIntervalSet_npos; @@ -69,9 +73,10 @@ _cfPDFToPDFIntervalSet_add_single(_cfPDFToPDFIntervalSet *set, set->data[set->size].end = end; set->size++; } +// }}} static int -compare_intervals(const void *a, const void *b) +compare_intervals(const void *a, const void *b) // {{{ { interval_t *ia = (interval_t *)a; interval_t *ib = (interval_t *)b; @@ -79,9 +84,10 @@ compare_intervals(const void *a, const void *b) return ia->start - ib->start; return ia->end - ib->end; } +// }}} void -_cfPDFToPDFIntervalSet_finish(_cfPDFToPDFIntervalSet *set) +_cfPDFToPDFIntervalSet_finish(_cfPDFToPDFIntervalSet *set) // {{{ { if (set->size == 0) return; @@ -106,16 +112,18 @@ _cfPDFToPDFIntervalSet_finish(_cfPDFToPDFIntervalSet *set) } set->size = new_size + 1; } +// }}}} size_t -_cfPDFToPDFIntervalSet_size(const _cfPDFToPDFIntervalSet *set) +_cfPDFToPDFIntervalSet_size(const _cfPDFToPDFIntervalSet *set) // {{{ { return set->size; } +// }}} bool _cfPDFToPDFIntervalSet_contains(const _cfPDFToPDFIntervalSet *set, - int val) + int val) // {{{ { for (size_t i = 0; i < set->size; i++) { @@ -126,10 +134,11 @@ _cfPDFToPDFIntervalSet_contains(const _cfPDFToPDFIntervalSet *set, } return false; } +// }}} int _cfPDFToPDFIntervalSet_next(const _cfPDFToPDFIntervalSet *set, - int val) + int val) // {{{ { val++; for (size_t i = 0; i < set->size; i++) @@ -148,10 +157,11 @@ _cfPDFToPDFIntervalSet_next(const _cfPDFToPDFIntervalSet *set, } return _cfPDFToPDFIntervalSet_npos; } +// }}} void _cfPDFToPDFIntervalSet_dump(const _cfPDFToPDFIntervalSet *set, - pdftopdf_doc_t *doc) + pdftopdf_doc_t *doc) // {{{ { if (set->size == 0) { @@ -167,4 +177,4 @@ _cfPDFToPDFIntervalSet_dump(const _cfPDFToPDFIntervalSet *set, set->data[i].start, set->data[i].end); } } - +// }}} diff --git a/cupsfilters/pdftopdf/C-nup-private.h b/cupsfilters/pdftopdf/C-nup-private.h index 1230d55d..f0a4cf7b 100644 --- a/cupsfilters/pdftopdf/C-nup-private.h +++ b/cupsfilters/pdftopdf/C-nup-private.h @@ -1,4 +1,6 @@ // +//Copyright 2024 Uddhav Phatak +// // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // diff --git a/cupsfilters/pdftopdf/C-nup.c b/cupsfilters/pdftopdf/C-nup.c index 7fd8d03b..e607a3d6 100644 --- a/cupsfilters/pdftopdf/C-nup.c +++ b/cupsfilters/pdftopdf/C-nup.c @@ -1,4 +1,6 @@ // +// Copyright 2024 Uddhav Phatak +// // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // @@ -10,7 +12,7 @@ #include "cupsfilters/debug-internal.h" void -_cfPDFToPDFNupParameters_init(_cfPDFToPDFNupParameters *nupParams) +_cfPDFToPDFNupParameters_init(_cfPDFToPDFNupParameters *nupParams) // {{{ { nupParams->nupX = 1; nupParams->nupY = 1; @@ -23,10 +25,11 @@ _cfPDFToPDFNupParameters_init(_cfPDFToPDFNupParameters *nupParams) nupParams->xalign = CENTER; nupParams->yalign = CENTER; } +// }}} void _cfPDFToPDFNupParameters_dump(const _cfPDFToPDFNupParameters *nupParams, - pdftopdf_doc_t *doc) + pdftopdf_doc_t *doc) // {{{ { if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: NupX: %d, NupY: %d, " "width: %f, height: %f", @@ -83,18 +86,20 @@ _cfPDFToPDFNupParameters_dump(const _cfPDFToPDFNupParameters *nupParams, _cfPDFToPDFPositionAndAxisDump(nupParams->xalign, X, doc); _cfPDFToPDFPositionAndAxisDump(nupParams->yalign, Y, doc); } +// }}} bool -_cfPDFToPDFNupParameters_possible(int nup) +_cfPDFToPDFNupParameters_possible(int nup) // {{{ { return ((nup >= 1) && (nup <= 16) && ((nup != 5) && (nup != 7) && (nup != 11) && (nup != 13) && (nup != 14))); } +// }}} void _cfPDFToPDFNupParameters_preset(int nup, - _cfPDFToPDFNupParameters *ret) + _cfPDFToPDFNupParameters *ret) // {{{ { switch (nup) { @@ -150,10 +155,11 @@ _cfPDFToPDFNupParameters_preset(int nup, break; } } +// }}} void _cfPDFToPDFNupState_init(_cfPDFToPDFNupState *state, - const _cfPDFToPDFNupParameters *param) + const _cfPDFToPDFNupParameters *param) // {{{ { state->param = *param; state->in_pages = 0; @@ -161,24 +167,27 @@ _cfPDFToPDFNupState_init(_cfPDFToPDFNupState *state, state->nup = param->nupX * param->nupY; state->subpage = state->nup; } +// }}} void -_cfPDFToPDFNupState_reset(_cfPDFToPDFNupState *state) +_cfPDFToPDFNupState_reset(_cfPDFToPDFNupState *state) // {{{ { state->in_pages = 0; state->out_pages = 0; state->subpage = state->nup; } +// }}} void _cfPDFToPDFNupPageEdit_dump(const _cfPDFToPDFNupPageEdit *edit, - pdftopdf_doc_t *doc) + pdftopdf_doc_t *doc) // {{{ { if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: xpos: %f, ypos: %f, scale: %f", edit->xpos, edit->ypos, edit->scale); _cfPDFToPDFPageRect_dump(&edit->sub, doc); } +// }}} typedef struct { int first; @@ -187,7 +196,7 @@ typedef struct { static int_pair _cfPDFToPDFNupState_convert_order(const _cfPDFToPDFNupState *state, - int subpage) + int subpage) // {{{ { int subx, suby; if (state->param.first == X) @@ -207,9 +216,10 @@ _cfPDFToPDFNupState_convert_order(const _cfPDFToPDFNupState *state, int_pair result = {subx, suby}; return result; } +// }}} static float -lin(pdftopdf_position_e pos, float size) +lin(pdftopdf_position_e pos, float size) // {{{ { if (pos == -1) return 0; @@ -219,11 +229,12 @@ lin(pdftopdf_position_e pos, float size) return size; return size * (pos + 1) / 2; } +// }}} void _cfPDFToPDFNupState_calculate_edit(const _cfPDFToPDFNupState *state, int subx, int suby, - _cfPDFToPDFNupPageEdit *ret) + _cfPDFToPDFNupPageEdit *ret) // {{{ { const float width = state->param.width / state->param.nupX; const float height = state->param.height / state->param.nupY; @@ -254,11 +265,12 @@ _cfPDFToPDFNupState_calculate_edit(const _cfPDFToPDFNupState *state, ret->sub.right = ret->sub.left + subwidth; ret->sub.top = ret->sub.bottom + subheight; } +// }}} bool _cfPDFToPDFNupState_next_page(_cfPDFToPDFNupState *state, float in_width, float in_height, - _cfPDFToPDFNupPageEdit *ret) + _cfPDFToPDFNupPageEdit *ret) // {{{ { state->in_pages++; state->subpage++; @@ -276,9 +288,10 @@ _cfPDFToPDFNupState_next_page(_cfPDFToPDFNupState *state, return (state->subpage == 0); } +// }}} static int_pair -parsePosition(char a, char b) +parsePosition(char a, char b) // {{{ { a |= 0x20; // make lowercase b |= 0x20; @@ -292,12 +305,12 @@ parsePosition(char a, char b) return (int_pair){Y, BOTTOM}; return (int_pair){X, CENTER}; } +// }}} bool _cfPDFToPDFParseNupLayout(const char *val, - _cfPDFToPDFNupParameters *ret) + _cfPDFToPDFNupParameters *ret) // {{{ { - DEBUG_assert(val); int_pair pos0 = parsePosition(val[0], val[1]); if (pos0.second == CENTER) return false; @@ -319,4 +332,5 @@ _cfPDFToPDFParseNupLayout(const char *val, return (val[4] == 0); } +// }}} diff --git a/cupsfilters/pdftopdf/C-pdftopdf-private.h b/cupsfilters/pdftopdf/C-pdftopdf-private.h index 4e1d8fc5..804408b0 100644 --- a/cupsfilters/pdftopdf/C-pdftopdf-private.h +++ b/cupsfilters/pdftopdf/C-pdftopdf-private.h @@ -1,5 +1,6 @@ // // Copyright 2020 by Jai Luthra. +// Copyright 2024 Uddhav Phatak // // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. diff --git a/cupsfilters/pdftopdf/C-pdftopdf-processor-private.h b/cupsfilters/pdftopdf/C-pdftopdf-processor-private.h index c05e9ea9..fc3a5943 100644 --- a/cupsfilters/pdftopdf/C-pdftopdf-processor-private.h +++ b/cupsfilters/pdftopdf/C-pdftopdf-processor-private.h @@ -1,7 +1,13 @@ +// +// Copyright 2024 Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + #ifndef C_PDFTOPDF_PROCESSOR_PRIVATE_H #define C_PDFTOPDF_PROCESSOR_PRIVATE_H - #include "C-pptypes-private.h" #include "C-nup-private.h" #include "C-pdftopdf-private.h" diff --git a/cupsfilters/pdftopdf/C-pdftopdf-processor.c b/cupsfilters/pdftopdf/C-pdftopdf-processor.c index d6ad5481..766d3992 100644 --- a/cupsfilters/pdftopdf/C-pdftopdf-processor.c +++ b/cupsfilters/pdftopdf/C-pdftopdf-processor.c @@ -1,4 +1,6 @@ // +// Copyright 2024 Uddhav Phatak +// // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // diff --git a/cupsfilters/pdftopdf/C-pdftopdf.c b/cupsfilters/pdftopdf/C-pdftopdf.c index b411f9a3..48f64790 100644 --- a/cupsfilters/pdftopdf/C-pdftopdf.c +++ b/cupsfilters/pdftopdf/C-pdftopdf.c @@ -1,4 +1,6 @@ // +// Copyright 2024 Uddhav Phatak +// // Copyright (c) 6-2011, BBR Inc. All rights reserved. // // Licensed under Apache License v2.0. See the file "LICENSE" for more @@ -904,9 +906,9 @@ cfFilterPDFToPDF(int inputfd, } if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Processing PDF input with QPDF: Page-ranges, page-set, number-up, booklet, size adjustment, ..."); + "cfFilterPDFToPDF: Processing PDF input with PDFio: Page-ranges, page-set, number-up, booklet, size adjustment, ..."); - // Load the PDF input data into QPDF + // Load the PDF input data into PDFio if (!_cfPDFToPDF_PDFioProcessor_load_file(&proc, inputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE, 1)) { fclose(inputfp); diff --git a/cupsfilters/pdftopdf/C-pptypes-private.h b/cupsfilters/pdftopdf/C-pptypes-private.h index e74cbea9..457e931a 100644 --- a/cupsfilters/pdftopdf/C-pptypes-private.h +++ b/cupsfilters/pdftopdf/C-pptypes-private.h @@ -1,4 +1,6 @@ // +// Copyright 2024 Uddhav Phatak +// // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // diff --git a/cupsfilters/pdftopdf/C-pptypes.c b/cupsfilters/pdftopdf/C-pptypes.c index 60368198..44da1d18 100644 --- a/cupsfilters/pdftopdf/C-pptypes.c +++ b/cupsfilters/pdftopdf/C-pptypes.c @@ -1,3 +1,10 @@ +// +//Copyright 2024 Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + #include "C-pptypes-private.h" #include #include @@ -5,7 +12,7 @@ void _cfPDFToPDFPositionDump(pdftopdf_position_e pos, - pdftopdf_doc_t *doc) + pdftopdf_doc_t *doc) // {{{ { static const char *pstr[3] = {"Left/Bottom", "Center", "Right/Top"}; if ((pos < LEFT) || (pos > RIGHT)) @@ -19,14 +26,13 @@ _cfPDFToPDFPositionDump(pdftopdf_position_e pos, "cfFilterPDFToPDF: %s", pstr[pos+1]); } } +// }}} void _cfPDFToPDFPositionAndAxisDump(pdftopdf_position_e pos, pdftopdf_axis_e axis, - pdftopdf_doc_t *doc) + pdftopdf_doc_t *doc) // {{{ { - DEBUG_assert((axis == X) || (axis == Y)); - if ((pos < LEFT) || (pos > RIGHT)) { if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, @@ -50,10 +56,11 @@ _cfPDFToPDFPositionAndAxisDump(pdftopdf_position_e pos, } } +// }}} void _cfPDFToPDFRotationDump(pdftopdf_rotation_e rot, - pdftopdf_doc_t *doc) + pdftopdf_doc_t *doc) // {{{ { static const char *rstr[4] = {"0 deg", "90 deg", "180 deg", "270 deg"}; // CCW @@ -69,30 +76,34 @@ _cfPDFToPDFRotationDump(pdftopdf_rotation_e rot, "cfFilterPDFToPDF: Rotation(CCW): %s", rstr[rot]); } } +// }}} pdftopdf_rotation_e pdftopdf_rotation_add(pdftopdf_rotation_e lhs, - pdftopdf_rotation_e rhs) + pdftopdf_rotation_e rhs) // {{{ { return (pdftopdf_rotation_e)(((int)lhs + (int)rhs) % 4); } +// }}} pdftopdf_rotation_e pdftopdf_rotation_sub(pdftopdf_rotation_e lhs, - pdftopdf_rotation_e rhs) + pdftopdf_rotation_e rhs) // {{{ { return (pdftopdf_rotation_e)((((int)lhs - (int)rhs) % 4 + 4) % 4); } +// }}} pdftopdf_rotation_e -pdftopdf_rotation_neg(pdftopdf_rotation_e rhs) +pdftopdf_rotation_neg(pdftopdf_rotation_e rhs) // {{{ { return (pdftopdf_rotation_e)((4 - (int)rhs) % 4); } +// }}} void _cfPDFToPDFBorderTypeDump(pdftopdf_border_type_e border, - pdftopdf_doc_t *doc) + pdftopdf_doc_t *doc) // {{{ { if ((border < NONE) || (border == 1) || (border > TWO_THICK)) { @@ -108,9 +119,10 @@ _cfPDFToPDFBorderTypeDump(pdftopdf_border_type_e border, "cfFilterPDFToPDF: Border: %s", bstr[border]); } } +// }}} void -_cfPDFToPDFPageRect_init(_cfPDFToPDFPageRect *rect) +_cfPDFToPDFPageRect_init(_cfPDFToPDFPageRect *rect) // {{{ { rect->top = NAN; rect->left = NAN; @@ -119,19 +131,21 @@ _cfPDFToPDFPageRect_init(_cfPDFToPDFPageRect *rect) rect->width = NAN; rect->height = NAN; } +// {{{ void -swap_float(float *a, float *b) +swap_float(float *a, float *b) // {{{ { float temp = *a; *a = *b; *b = temp; } +// }}} void _cfPDFToPDFPageRect_rotate_move(_cfPDFToPDFPageRect *rect, pdftopdf_rotation_e r, - float pwidth, float pheight) + float pwidth, float pheight) // {{{ { #if 1 if (r >= ROT_180) @@ -202,16 +216,15 @@ _cfPDFToPDFPageRect_rotate_move(_cfPDFToPDFPageRect *rect, } #endif } +// }}} void _cfPDFToPDFPageRect_scale(_cfPDFToPDFPageRect *rect, - float mult) + float mult) // {{{ { if (mult == 1.0) return; - DEBUG_assert(mult != 0.0); - rect->bottom *= mult; rect->left *= mult; rect->top *= mult; @@ -220,21 +233,23 @@ _cfPDFToPDFPageRect_scale(_cfPDFToPDFPageRect *rect, rect->width *= mult; rect->height *= mult; } +// }}} void _cfPDFToPDFPageRect_translate(_cfPDFToPDFPageRect *rect, float tx, - float ty) + float ty) // {{{ { rect->left += tx; rect->bottom += ty; rect->right += tx; rect->top += ty; } +// }}} void _cfPDFToPDFPageRect_set(_cfPDFToPDFPageRect *rect, - const _cfPDFToPDFPageRect *rhs) + const _cfPDFToPDFPageRect *rhs) // {{{ { if (!isnan(rhs->top)) rect->top = rhs->top; @@ -248,10 +263,11 @@ _cfPDFToPDFPageRect_set(_cfPDFToPDFPageRect *rect, if (!isnan(rhs->bottom)) rect->bottom = rhs->bottom; } +// }}} void _cfPDFToPDFPageRect_dump(const _cfPDFToPDFPageRect *rect, - pdftopdf_doc_t *doc) + pdftopdf_doc_t *doc) // {{{ { if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: top: %f, left: %f, right: %f, bottom: %f, " @@ -260,4 +276,4 @@ _cfPDFToPDFPageRect_dump(const _cfPDFToPDFPageRect *rect, rect->width, rect->height); } - +// }}} diff --git a/cupsfilters/pdftopdf/pdfio-cm-private.h b/cupsfilters/pdftopdf/pdfio-cm-private.h index 5a85f61e..6d2a0558 100644 --- a/cupsfilters/pdftopdf/pdfio-cm-private.h +++ b/cupsfilters/pdftopdf/pdfio-cm-private.h @@ -1,4 +1,6 @@ // +// Copyright 2024 Uddhav Phatak +// // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // diff --git a/cupsfilters/pdftopdf/pdfio-cm.c b/cupsfilters/pdftopdf/pdfio-cm.c index 2502e6e6..b258bc7c 100644 --- a/cupsfilters/pdftopdf/pdfio-cm.c +++ b/cupsfilters/pdftopdf/pdfio-cm.c @@ -1,4 +1,6 @@ // +// Copyright 2024 Uddhav Phatak +// // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // @@ -6,7 +8,6 @@ #include "pdfio-cm-private.h" #include #include "cupsfilters/debug-internal.h" - #include #include #include diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h b/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h index f6e1637a..cc7f9a0b 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h @@ -1,4 +1,6 @@ // +// Copyright 2024 Uddhav Phatak +// // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h index ad99b327..6323568e 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h @@ -1,4 +1,6 @@ // +// Copyright 2024 Uddhav Phatak +// // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // @@ -29,7 +31,7 @@ void hashFree_hash_table(HashTable *table); typedef struct _cfPDFToPDF_PDFioProcessor{ // 1st mode: existing - pdfio_obj_t *page; // Equivalent to QPDFObjectHandle + pdfio_obj_t *page; // Equivalent to PDFioObjectHandle int no; // 2nd mode: create new @@ -49,7 +51,7 @@ typedef struct _cfPDFToPDF_PDFioProcessor{ char *extraheader; } _cfPDFToPDF_PDFioProcessor; */ -//_cfPDFToPDFQPDFPageHandle functions +//_cfPDFToPDFPDFioPageHandle functions void _cfPDFToPDF_PDFioProcessor_existingMode(_cfPDFToPDF_PDFioProcessor *handle, pdfio_file_t *pdf, @@ -106,7 +108,7 @@ void _cfPDFToPDF_PDFioProcessor_add_label(_cfPDFToPDF_PDFioProcessor *handle, const char *label); -//_cfPDFToPDFQPDFProcessor functions +//_cfPDFToPDFPDFioProcessor functions void _cfPDFToPDF_PDFioProcessor_close_file(_cfPDFToPDF_PDFioProcessor *processor); bool _cfPDFToPDF_PDFioProcessor_load_file(_cfPDFToPDF_PDFioProcessor *processor, diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c index 0a2e48ee..0609805f 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c @@ -1,4 +1,6 @@ // +// Copyright 2024 Uddhav Phatak count = 0; // Initialize the count of filled elements return table; } +// }}} // Create a new key-value pair KeyValuePair* -create_key_value_pair(const char *key, pdfio_obj_t *value) +create_key_value_pair(const char *key, pdfio_obj_t *value) // {{{ { KeyValuePair *new_pair = malloc(sizeof(KeyValuePair)); new_pair->key = strdup(key); // Duplicate the key string @@ -51,10 +55,11 @@ create_key_value_pair(const char *key, pdfio_obj_t *value) new_pair->next = NULL; return new_pair; } +// }}} // Insert a key-value pair into the hash table void -hashInsert(HashTable *table, const char *key, pdfio_obj_t *value) +hashInsert(HashTable *table, const char *key, pdfio_obj_t *value) // {{{ { unsigned int index = hash(key); KeyValuePair *new_pair = create_key_value_pair(key, value); @@ -75,10 +80,11 @@ hashInsert(HashTable *table, const char *key, pdfio_obj_t *value) } table->count++; // Increment the count of filled elements } +// }}} // Retrieve a value by key from the hash table pdfio_obj_t* -hashGet(HashTable *table, const char *key) +hashGet(HashTable *table, const char *key) // {{{ { unsigned int index = hash(key); KeyValuePair *current = table->buckets[index]; @@ -91,17 +97,19 @@ hashGet(HashTable *table, const char *key) } return NULL; // Key not found } +// }}} // Get the number of elements currently filled in the hash table int -hashGet_filled_count(HashTable *table) +hashGet_filled_count(HashTable *table) // {{{ { return table->count; } +// }}} // Free the hash table void -hashFree_hash_table(HashTable *table) +hashFree_hash_table(HashTable *table) // {{{ { for (int i = 0; i < HASH_TABLE_SIZE; i++) { @@ -116,6 +124,7 @@ hashFree_hash_table(HashTable *table) } free(table); } +// }}} // main code starts diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf.c b/cupsfilters/pdftopdf/pdfio-pdftopdf.c index 92ff63f3..96380f48 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf.c +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf.c @@ -1,4 +1,6 @@ // +// Copyright 2024 Uddhav Phatak +// // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // diff --git a/cupsfilters/pdftopdf/pdfio-tools-private.h b/cupsfilters/pdftopdf/pdfio-tools-private.h index b4f96c9f..2f2a4f95 100644 --- a/cupsfilters/pdftopdf/pdfio-tools-private.h +++ b/cupsfilters/pdftopdf/pdfio-tools-private.h @@ -1,10 +1,12 @@ // +// Copyright 2024 Uddhav Phatak +// // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // -#ifndef _CUPS_FILTERS_PDFTOPDF_QPDF_TOOLS_H_ -#define _CUPS_FILTERS_PDFTOPDF_QPDF_TOOLS_H_ +#ifndef _CUPS_FILTERS_PDFTOPDF_PDFio_TOOLS_H_ +#define _CUPS_FILTERS_PDFTOPDF_PDFio_TOOLS_H_ #include @@ -16,5 +18,5 @@ pdfio_rect_t _cfPDFToPDFGetArtBox(pdfio_obj_t *page); pdfio_rect_t* _cfPDFToPDFMakeBox(double x1, double y1, double x2, double y2); -#endif // !_CUPS_FILTERS_PDFTOPDF_QPDF_TOOLS_H_ +#endif // !_CUPS_FILTERS_PDFTOPDF_PDFio_TOOLS_H_ diff --git a/cupsfilters/pdftopdf/pdfio-tools.c b/cupsfilters/pdftopdf/pdfio-tools.c index 1b9e03e4..af60eb4e 100644 --- a/cupsfilters/pdftopdf/pdfio-tools.c +++ b/cupsfilters/pdftopdf/pdfio-tools.c @@ -1,4 +1,6 @@ // +// Copyright 2024 Uddhav Phatak +// // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // diff --git a/cupsfilters/pdftopdf/pdfio-xobject-private.h b/cupsfilters/pdftopdf/pdfio-xobject-private.h index 37b60a2f..dacefa3e 100644 --- a/cupsfilters/pdftopdf/pdfio-xobject-private.h +++ b/cupsfilters/pdftopdf/pdfio-xobject-private.h @@ -1,4 +1,6 @@ // +// Copyright 2024 Uddhav Phatak +// // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // diff --git a/cupsfilters/pdftopdf/pdfio-xobject.c b/cupsfilters/pdftopdf/pdfio-xobject.c index 095c1a50..329c14ff 100644 --- a/cupsfilters/pdftopdf/pdfio-xobject.c +++ b/cupsfilters/pdftopdf/pdfio-xobject.c @@ -1,4 +1,6 @@ // +// Copyright 2024 Uddhav Phatak +// // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // @@ -21,7 +23,7 @@ typedef struct CombineFromContents_Provider* CombineFromContents_Provider_new(pdfio_stream_t **contents, - size_t content_count) + size_t content_count) // {{{ { CombineFromContents_Provider *provider = (CombineFromContents_Provider*)malloc(sizeof(CombineFromContents_Provider)); @@ -29,9 +31,10 @@ CombineFromContents_Provider_new(pdfio_stream_t **contents, provider->contents = contents; return provider; } +// }}} void -CombineFromContents_Provider_free(CombineFromContents_Provider *provider) +CombineFromContents_Provider_free(CombineFromContents_Provider *provider) // {{{ { if (provider) { @@ -39,10 +42,11 @@ CombineFromContents_Provider_free(CombineFromContents_Provider *provider) free(provider); } } +// }}} void CombineFromContents_Provider_provideStreamData(CombineFromContents_Provider *provider, - pdfio_stream_t *pipeline) + pdfio_stream_t *pipeline) // {{{ { char buffer[8192]; size_t bytes; @@ -56,6 +60,7 @@ CombineFromContents_Provider_provideStreamData(CombineFromContents_Provider *pro } } } +// }}} // // To convert a page to an XObject there are several keys to consider: @@ -100,7 +105,7 @@ CombineFromContents_Provider_provideStreamData(CombineFromContents_Provider *pro pdfio_obj_t* _cfPDFToPDFMakeXObject(pdfio_file_t *pdf, - pdfio_obj_t *page) + pdfio_obj_t *page) // {{{ { pdfio_dict_t *page_dict = pdfioObjGetDict(page); @@ -212,4 +217,4 @@ _cfPDFToPDFMakeXObject(pdfio_file_t *pdf, return xobject; } - +// }}} diff --git a/cupsfilters/pdftopdf/processor.h b/cupsfilters/pdftopdf/processor.h index 5c9c982b..80086c88 100644 --- a/cupsfilters/pdftopdf/processor.h +++ b/cupsfilters/pdftopdf/processor.h @@ -1,7 +1,13 @@ +// +// Copyright 2024 Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + #ifndef C_PDFTOPDF_PROCESSOR_PRIVATE_H #define C_PDFTOPDF_PROCESSOR_PRIVATE_H - #include "C-pptypes-private.h" #include "pdfio.h" #include "C-pptypes-private.h" From 7667e4fd139b0898bf60a3c62ad1b934018c50bb Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Thu, 14 Nov 2024 17:27:13 +0530 Subject: [PATCH 52/64] converted pwgtopdf.cxx --- Makefile.am | 2 +- cupsfilters/C-pwgtopdf.c | 629 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 623 insertions(+), 8 deletions(-) diff --git a/Makefile.am b/Makefile.am index 93b7bf84..0133091a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -202,7 +202,7 @@ libcupsfilters_la_SOURCES = \ cupsfilters/pdftoraster.cxx \ cupsfilters/pdfutils.c \ cupsfilters/pdfutils-private.h \ - cupsfilters/pwgtopdf.cxx \ + cupsfilters/C-pwgtopdf.c \ cupsfilters/pwgtoraster.c \ cupsfilters/raster.c \ cupsfilters/rastertopwg.c \ diff --git a/cupsfilters/C-pwgtopdf.c b/cupsfilters/C-pwgtopdf.c index fe5b9cea..4fcf8a51 100644 --- a/cupsfilters/C-pwgtopdf.c +++ b/cupsfilters/C-pwgtopdf.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -36,6 +35,7 @@ #include #include +#include #ifdef USE_LCMS1 #include @@ -95,9 +95,9 @@ typedef unsigned char *(*bit_convert_function)(unsigned char *src, typedef struct pwgtopdf_doc_s // **** Document information **** { - cmsHPROFILE colorProfile = NULL; // ICC Profile to be applied to + cmsHPROFILE colorProfile; // ICC Profile to be applied to // PDF - int cm_disabled = 0; // Flag raised if color + int cm_disabled; // Flag raised if color // management is disabled convert_function conversion_function; // Raster color conversion // function @@ -114,10 +114,6 @@ typedef struct pwgtopdf_doc_s // **** Document information **** // function, can be NULL } pwgtopdf_doc_t; -// PDF color conversion function -typedef void (*pdf_convert_function)(struct pdf_info * info, - pwgtopdf_doc_t *doc); - struct pdf_info { // PDFio-specific members @@ -147,6 +143,11 @@ struct pdf_info cf_filter_out_format_t outformat; }; +// PDF color conversion function +typedef void (*pdf_convert_function)(struct pdf_info *info, + pwgtopdf_doc_t *doc); + + void init_pdf_info(struct pdf_info *info, size_t num_methods, @@ -538,4 +539,618 @@ make_integer_box(double x1, return (ret); } +// +// PDF color conversion functons... +// + +static void +modify_pdf_color(struct pdf_info *info, + int bpp, + int bpc, + convert_function fn, + pwgtopdf_doc_t *doc) +{ + unsigned old_bpp = info->bpp; + unsigned old_bpc = info->bpc; + double old_ncolor = old_bpp / old_bpc; + + unsigned old_line_bytes = info->line_bytes; + + double new_ncolor = bpp / bpc; + + info->line_bytes = (unsigned)old_line_bytes * (new_ncolor / old_ncolor); + info->bpp = bpp; + info->bpc = bpc; + doc->conversion_function = fn; +} + +static void +convert_pdf_no_conversion(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + doc->conversion_function = no_color_conversion; + doc->bit_function = no_bit_conversion; +} + + +static void +convert_pdf_cmyk_8_to_white_8(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + modify_pdf_color(info, 8, 8, cmyk_to_white, doc); + doc->bit_function = no_bit_conversion; +} + + +static void +convert_pdf_rgb_8_to_white_8(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + modify_pdf_color(info, 8, 8, rgb_to_white, doc); + doc->bit_function = no_bit_conversion; +} + + +static void +convert_pdf_cmyk_8_to_rgb_8(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + modify_pdf_color(info, 24, 8, cmyk_to_rgb, doc); + doc->bit_function = no_bit_conversion; +} + + +static void +convert_pdf_white_8_to_rgb_8(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + modify_pdf_color(info, 24, 8, white_to_rgb, doc); + doc->bit_function = invert_bits; +} + + +static void +convert_pdf_rgb_8_to_cmyk_8(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + modify_pdf_color(info, 32, 8, rgb_to_cmyk, doc); + doc->bit_function = no_bit_conversion; +} + + +static void +convert_pdf_white_8_to_cmyk_8(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + modify_pdf_color(info, 32, 8, white_to_cmyk, doc); + doc->bit_function = invert_bits; +} + + +static void +convert_pdf_invert_colors(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + doc->conversion_function = no_color_conversion; + doc->bit_function = invert_bits; +} + +// +// Create an '/ICCBased' array and embed a previously +// set ICC Profile in the PDF +// +// TODO: HOW IS THIS cmsHPROFILE CALLED +pdfio_obj_t * +embed_icc_profile(pdfio_file_t *pdf, pwgtopdf_doc_t *doc) +{ + pdfio_dict_t *streamdict; + pdfio_obj_t *icc_stream; + char *n_value = NULL; + char *alternate_cs = NULL; + unsigned char *buff; + cmsColorSpaceSignature css; + +#ifdef USE_LCMS1 + size_t profile_size; +#else + unsigned int profile_size; +#endif + + // Determine color space signature + css = cmsGetColorSpace(doc->colorProfile); + + // Determine color component number for /ICCBased array + switch (css) + { + case cmsSigGrayData: + n_value = "1"; + alternate_cs = "/DeviceGray"; + break; + case cmsSigRgbData: + n_value = "3"; + alternate_cs = "/DeviceRGB"; + break; + case cmsSigCmykData: + n_value = "4"; + alternate_cs = "/DeviceCMYK"; + break; + default: + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Failed to embed ICC Profile."); + return NULL; + } + + cmsSaveProfileToMem(doc->colorProfile, NULL, &profile_size); + buff = (unsigned char *)calloc(profile_size, sizeof(unsigned char)); + cmsSaveProfileToMem(doc->colorProfile, buff, &profile_size); + + streamdict = pdfioDictCreate(pdf); + pdfioDictSetName(streamdict, "Alternate", alternate_cs); + pdfioDictSetName(streamdict, "N", n_value); + + icc_stream = pdfioFileCreateObj(pdf, streamdict); + + if (!icc_stream) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Failed to create ICC profile stream."); + free(buff); + return NULL; + } + + free(buff); + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: ICC Profile embedded in PDF."); + + return icc_stream; +} + +pdfio_obj_t* +embed_srgb_profile(pdfio_file_t *pdf, + pwgtopdf_doc_t *doc) +{ + pdfio_obj_t *iccbased_reference; + + doc->colorProfile = cmsCreate_sRGBProfile(); + iccbased_reference = embed_icc_profile(pdf, doc); + + return iccbased_reference; +} + +// +// Calibration function for non-Lab PDF color spaces +// Requires white point data, and if available, gamma or matrix numbers. +// +// Output: +// [/'color_space' +// << /Gamma ['gamma[0]'...'gamma[n]'] +// /WhitePoint ['wp[0]' 'wp[1]' 'wp[2]'] +// /Matrix ['matrix[0]'...'matrix[n*n]'] +// >> +// ] + +pdfio_array_t* +get_calibration_array(pdfio_file_t *pdf, + const char *color_space, + double wp[], + double gamma[], + double matrix[], + double bp[]) +{ + if ((!strcmp("/CalGray", color_space) && matrix != NULL) || wp == NULL) + return NULL; + + pdfio_array_t *calibration_array = pdfioArrayCreate(pdf); + if (!calibration_array) + return NULL; + + pdfioArrayAppendName(calibration_array, color_space); + + pdfio_dict_t *calibration_dict = pdfioDictCreate(pdf); + + if (wp != NULL) + { + pdfio_array_t *white_point_array = pdfioArrayCreate(pdf); + pdfioArrayAppendNumber(white_point_array, wp[0]); + pdfioArrayAppendNumber(white_point_array, wp[1]); + pdfioArrayAppendNumber(white_point_array, wp[2]); + pdfioDictSetArray(calibration_dict, "WhitePoint", white_point_array); + } + + if (!strcmp("/CalGray", color_space) && gamma != NULL) + pdfioDictSetNumber(calibration_dict, "Gamma", gamma[0]); + + else if (!strcmp("/CalRGB", color_space) && gamma != NULL) + { + pdfio_array_t *gamma_array = pdfioArrayCreate(pdf); + pdfioArrayAppendNumber(gamma_array, gamma[0]); + pdfioArrayAppendNumber(gamma_array, gamma[1]); + pdfioArrayAppendNumber(gamma_array, gamma[2]); + pdfioDictSetArray(calibration_dict, "Gamma", gamma_array); + } + + if (bp != NULL) + { + pdfio_array_t *black_point_array = pdfioArrayCreate(pdf); + pdfioArrayAppendNumber(black_point_array, bp[0]); + pdfioArrayAppendNumber(black_point_array, bp[1]); + pdfioArrayAppendNumber(black_point_array, bp[2]); + pdfioDictSetArray(calibration_dict, "BlackPoint", black_point_array); + } + + if (!strcmp("CalRGB", color_space) && matrix != NULL) + { + pdfio_array_t *matrix_array = pdfioArrayCreate(pdf); + for (int i = 0; i < 9; i++) + pdfioArrayAppendNumber(matrix_array, matrix[i]); + pdfioDictSetArray(calibration_dict, "Matrix", matrix_array); + } + + pdfioArrayAppendDict(calibration_array, calibration_dict); + return calibration_array; +} + + +pdfio_array_t* +get_cal_rgb_array(pdfio_file_t *pdf, + double wp[3], + double gamma[3], + double matrix[9], + double bp[3]) +{ + pdfio_array_t *ret = get_calibration_array(pdf, "CalRGB", wp, gamma, matrix, + bp); + return (ret); +} + +pdfio_array_t* +get_cal_gray_array(pdfio_file_t *pdf, + double wp[3], + double gamma[1], + double bp[3]) +{ + pdfio_array_t *ret = get_calibration_array(pdf, "CalGray", wp, gamma, 0, bp); + return (ret); +} + +// +// 'make_pclm_strips()' - Return an std::vector of QPDFObjectHandle, each +// containing the stream data of the various strips +// which make up a PCLm page. +// +// O - std::vector of QPDFObjectHandle +// I - QPDF object +// I - number of strips per page +// I - std::vector of std::shared_ptr containing data for each strip +// I - strip width +// I - strip height +// I - color space +// I - bits per component +// I - document information +// + +pdfio_stream_t** +make_pclm_strips(pdfio_file_t *pdf, + unsigned num_strips, + unsigned char **strip_data, + compression_method_t *compression_methods, + unsigned width, unsigned *strip_height, + cups_cspace_t cs, + unsigned bpc, + pwgtopdf_doc_t *doc) +{ + pdfio_stream_t **ret = (pdfio_stream_t **)malloc(num_strips * sizeof(pdfio_stream_t *)); + pdfio_dict_t *dict; + const char *color_space_name; + unsigned components = 0; + switch (cs) + { + case CUPS_CSPACE_K: + case CUPS_CSPACE_SW: + color_space_name = "DeviceGray"; + components = 1; + break; + case CUPS_CSPACE_RGB: + case CUPS_CSPACE_SRGB: + case CUPS_CSPACE_ADOBERGB: + color_space_name = "DeviceRGB"; + components = 3; + break; + default: + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Color space not supported."); + return NULL; + } + + for (size_t i = 0; i < num_strips; i++) + { + dict = pdfioDictCreate(pdf); + + pdfioDictSetName(dict, "Type", "XObject"); + pdfioDictSetName(dict, "Subtype", "Image"); + pdfioDictSetNumber(dict, "Width", width); + pdfioDictSetNumber(dict, "BitsPerComponent", bpc); + pdfioDictSetName(dict, "ColorSpace", color_space_name); + pdfioDictSetNumber(dict, "Height", strip_height[i]); + + pdfio_obj_t *streamObj = pdfioFileCreateObj(pdf, dict); + ret[i] = pdfioObjCreateStream(streamObj, PDFIO_FILTER_NONE); + + compression_method_t compression = compression_methods[0]; + for (unsigned j = 0; j < num_strips; j++) + compression = compression > compression_methods[j] ? compression : compression_methods[j]; + + + if (compression == FLATE_DECODE) + { + pdfioStreamWrite(ret[i], strip_data[i], strip_height[i] * width * components); + pdfioDictSetName(dict, "Filter", "FlateDecode"); + } + else if (compression == RLE_DECODE) + { + pdfioStreamWrite(ret[i], strip_data[i], strip_height[i] * width * components); + pdfioDictSetName(dict, "Filter", "RunLengthDecode"); + } + else if (compression == DCT_DECODE) + { + pdfioStreamWrite(ret[i], strip_data[i], strip_height[i] * width * components); + pdfioDictSetName(dict, "Filter", "DCTDecode"); + } + } + return ret; +} + +pdfio_obj_t* +make_image(pdfio_file_t *pdf, + unsigned char *page_data, + size_t data_size, + unsigned width, + unsigned height, + const char *render_intent, + cups_cspace_t cs, + unsigned bpc, + pwgtopdf_doc_t *doc) +{ + pdfio_dict_t *dict = pdfioDictCreate(pdf); + pdfio_obj_t *image_obj; + pdfio_obj_t *icc_ref; + int use_blackpoint = 0; + + pdfioDictSetName(dict, "Type", "XObject"); + pdfioDictSetName(dict, "Subtype", "Image"); + pdfioDictSetNumber(dict, "Width", width); + pdfioDictSetNumber(dict, "Height", height); + pdfioDictSetNumber(dict, "BitsPerComponent", bpc); + + if (!doc->cm_disabled && render_intent) + { + if (strcmp(render_intent, "Perceptual") == 0) + pdfioDictSetName(dict, "Intent", "Perceptual"); + else if (strcmp(render_intent, "Absolute") == 0) + pdfioDictSetName(dict, "Intent", "AbsoluteColorimetric"); + else if (strcmp(render_intent, "Relative") == 0) + pdfioDictSetName(dict, "Intent", "RelativeColorimetric"); + else if (strcmp(render_intent, "Saturation") == 0) + pdfioDictSetName(dict, "Intent", "Saturation"); + else if (strcmp(render_intent, "RelativeBpc") == 0) + { + pdfioDictSetName(dict, "Intent", "RelativeColorimetric"); + use_blackpoint = 1; + } + } + + if (doc->colorProfile != NULL && !doc->cm_disabled) + { + icc_ref = embed_icc_profile(pdf, doc); + pdfioDictSetObj(dict, "ColorSpace", icc_ref); + } + else if (!doc->cm_disabled) + { + switch (cs) + { + case CUPS_CSPACE_DEVICE1: + case CUPS_CSPACE_DEVICE2: + case CUPS_CSPACE_DEVICE3: + case CUPS_CSPACE_DEVICE4: + case CUPS_CSPACE_DEVICE5: + case CUPS_CSPACE_DEVICE6: + case CUPS_CSPACE_DEVICE7: + case CUPS_CSPACE_DEVICE8: + case CUPS_CSPACE_DEVICE9: + case CUPS_CSPACE_DEVICEA: + case CUPS_CSPACE_DEVICEB: + case CUPS_CSPACE_DEVICEC: + case CUPS_CSPACE_DEVICED: + case CUPS_CSPACE_DEVICEE: + case CUPS_CSPACE_DEVICEF: + pdfioDictSetName(dict, "ColorSpace", "DeviceCMYK"); + break; + case CUPS_CSPACE_K: + pdfioDictSetName(dict, "ColorSpace", "DeviceGray"); + break; + case CUPS_CSPACE_SW: + if (use_blackpoint) + pdfioDictSetArray(dict, "ColorSpace", get_cal_gray_array(pdf, cfCmWhitePointSGray(), + cfCmGammaSGray(), + cfCmBlackPointDefault())); + + else + pdfioDictSetArray(dict, "ColorSpace", get_cal_gray_array(pdf, cfCmWhitePointSGray(), + cfCmGammaSGray(), 0)); + break; + case CUPS_CSPACE_CMYK: + pdfioDictSetName(dict, "ColorSpace", "DeviceCMYK"); + break; + case CUPS_CSPACE_RGB: + pdfioDictSetName(dict, "ColorSpace", "DeviceRGB"); + break; + case CUPS_CSPACE_SRGB: + icc_ref = embed_srgb_profile(pdf, doc); + if(icc_ref != NULL) + pdfioDictSetObj(dict, "ColorSpace", icc_ref); + else + pdfioDictSetName(dict, "ColorSpace", "DeviceRGB"); + break; + case CUPS_CSPACE_ADOBERGB: + if (use_blackpoint) + pdfioDictSetArray(dict, "ColorSpace", get_cal_rgb_array(pdf, cfCmWhitePointAdobeRGB(), + cfCmGammaAdobeRGB(), + cfCmMatrixAdobeRGB(), + cfCmBlackPointDefault())); + else + pdfioDictSetArray(dict, "ColorSpace", get_cal_rgb_array(pdf, cfCmWhitePointAdobeRGB(), + cfCmGammaAdobeRGB(), + cfCmMatrixAdobeRGB(), 0)); + break; + default: + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Color space not supported."); + return NULL; + } + } + else if(doc->cm_disabled) + { + switch(cs) + { + case CUPS_CSPACE_K: + case CUPS_CSPACE_SW: + pdfioDictSetName(dict, "ColorSpace", "DeviceGray"); + break; + case CUPS_CSPACE_RGB: + case CUPS_CSPACE_SRGB: + case CUPS_CSPACE_ADOBERGB: + pdfioDictSetName(dict, "ColorSpace", "DeviceRGB"); + break; + case CUPS_CSPACE_DEVICE1: + case CUPS_CSPACE_DEVICE2: + case CUPS_CSPACE_DEVICE3: + case CUPS_CSPACE_DEVICE4: + case CUPS_CSPACE_DEVICE5: + case CUPS_CSPACE_DEVICE6: + case CUPS_CSPACE_DEVICE7: + case CUPS_CSPACE_DEVICE8: + case CUPS_CSPACE_DEVICE9: + case CUPS_CSPACE_DEVICEA: + case CUPS_CSPACE_DEVICEB: + case CUPS_CSPACE_DEVICEC: + case CUPS_CSPACE_DEVICED: + case CUPS_CSPACE_DEVICEE: + case CUPS_CSPACE_DEVICEF: + case CUPS_CSPACE_CMYK: + pdfioDictSetName(dict, "ColorSpace", "DeviceCMYK"); + break; + default: + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Color space not supported."); + return NULL; + } + } + else + return NULL; + + image_obj = pdfioFileCreateObj(pdf, dict); + +#ifdef PRE_COMPRESS + uLongf compressed_size = compressBound(data_size); + unsigned char *compressed_data = (unsigned char *)malloc(compressed_size); + if (compress(compressed_data, &compressed_size, page_data, data_size) != Z_OK) + { + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPWGToPDF: Compression failed."); + free(compressed_data); + return NULL; + } + + pdfio_stream_t *stream = pdfioObjCreateStream(image_obj, PDFIO_FILTER_NONE); + pdfioStreamWrite(stream, compressed_data, compressed_size); + pdfioStreamClose(stream); + + free(compressed_data); +#else + pdfio_stream_t *stream = pdfioStreamCreate(pdf, image_obj); + pdfioStreamWrite(stream, page_data, page_data_size); + pdfioStreamClose(stream); +#endif + return image_obj; +} + +static int +finish_page(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + pdfio_obj_t *image_obj; + char content[1024]; + size_t content_length = 0; + + if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) + { + if (!info->page_data) + return 0; + + image_obj = make_image(info->pdf, info->page_data, info->width, info->height, + info->render_intent, info->color_space, info->bpc, doc); + if (!image_obj) + { + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Unable to load image data"); + return 1; + } + + // Associate image object with the page resources + pdfio_dict_t *resources = pdfioDictCreate(info->pdf); + pdfioDictSetObj(resources, "XObject", image_obj); + } + else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) + { + // Handle PCLm specific strips, similar to PDF, but requires strip-specific handling + if (info->pclm_num_strips == 0) + return 0; + + for (size_t i = 0; i < info->pclm_num_strips; i++) + { + if (!info->pclm_strip_data[i]) + return 0; + } + + // Further PCLm-specific handling for strips would be implemented here. + } + + // Add transformation and draw command content + if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) + { + content_length += snprintf(content + content_length, sizeof(content) - content_length, + "%.2f 0 0 %.2f 0 0 cm\n", info->page_width, info->page_height); + content_length += snprintf(content + content_length, sizeof(content) - content_length, "/I Do\n"); + } + else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) + { + double d = DEFAULT_PDF_UNIT / atoi(info->pclm_source_resolution_default); + content_length += snprintf(content + content_length, sizeof(content) - content_length, + "%.2f 0 0 %.2f 0 0 cm\n", d, d); + + // Iterate through strips and create content for each + for (unsigned i = 0; i < info->pclm_num_strips; i++) + { + unsigned yAnchor = info->height - info->pclm_strip_height[i]; + content_length += snprintf(content + content_length, sizeof(content) - content_length, + "/P <> BDC q\n%.2f 0 0 %.2f 0 %u cm\n/Image%d Do Q\n", + info->width, info->pclm_strip_height[i], yAnchor, i); + } + } + + // Write content stream to PDF page + pdfio_stream_t *page_content = pdfioStreamCreate(info->pdf, NULL); + pdfioStreamWrite(page_content, content, content_length); + pdfioStreamClose(page_content); + + // Reset data after finishing the page + info->page_data = NULL; + memset(info->pclm_strip_data, 0, sizeof(info->pclm_strip_data)); + + return 0; +} From 622cc6b71afa5e038a522f18d77b7a8bf7b7a47c Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Sat, 16 Nov 2024 01:47:16 +0530 Subject: [PATCH 53/64] add ported C-pwgtopdf.c and remove pwgtopdf.cxx --- cupsfilters/C-pwgtopdf.c | 783 ++++++++++++++-- cupsfilters/pwgtopdf.cxx | 1829 -------------------------------------- 2 files changed, 714 insertions(+), 1898 deletions(-) delete mode 100644 cupsfilters/pwgtopdf.cxx diff --git a/cupsfilters/C-pwgtopdf.c b/cupsfilters/C-pwgtopdf.c index 4fcf8a51..632a5004 100644 --- a/cupsfilters/C-pwgtopdf.c +++ b/cupsfilters/C-pwgtopdf.c @@ -30,6 +30,7 @@ #include #include #include +#include #include // ntohl @@ -523,22 +524,6 @@ make_real_box(double x1, return (ret); } -static pdfio_rect_t -make_integer_box(double x1, - double y1, - double x2, - double y2) -{ - pdfio_rect_t ret; - - ret.x1 = x1; - ret.y1 = y1; - ret.x2 = x2; - ret.y2 = y2; - - return (ret); -} - // // PDF color conversion functons... // @@ -904,7 +889,7 @@ make_pclm_strips(pdfio_file_t *pdf, pdfio_obj_t* make_image(pdfio_file_t *pdf, unsigned char *page_data, - size_t data_size, + int data_size, unsigned width, unsigned height, const char *render_intent, @@ -1082,75 +1067,735 @@ static int finish_page(struct pdf_info *info, pwgtopdf_doc_t *doc) { - pdfio_obj_t *image_obj; - char content[1024]; - size_t content_length = 0; + pdfio_obj_t *image_obj; + char content[1024]; + size_t content_length = 0; if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) { - if (!info->page_data) - return 0; + if (!info->page_data) + return 0; - image_obj = make_image(info->pdf, info->page_data, info->width, info->height, - info->render_intent, info->color_space, info->bpc, doc); - if (!image_obj) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to load image data"); - return 1; - } - - // Associate image object with the page resources - pdfio_dict_t *resources = pdfioDictCreate(info->pdf); - pdfioDictSetObj(resources, "XObject", image_obj); + image_obj = make_image(info->pdf, info->page_data, strlen(content), info->width, info->height, + info->render_intent, info->color_space, info->bpc, doc); + if (!image_obj) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Unable to load image data"); + return 1; } - else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) + + pdfio_dict_t *resources = pdfioDictCreate(info->pdf); + pdfioDictSetObj(resources, "XObject", image_obj); + } + else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) + { + if (info->pclm_num_strips == 0) + return 0; + + for (size_t i = 0; i < info->pclm_num_strips; i++) { - // Handle PCLm specific strips, similar to PDF, but requires strip-specific handling - if (info->pclm_num_strips == 0) - return 0; + if (!info->pclm_strip_data[i]) + return 0; + } + } - for (size_t i = 0; i < info->pclm_num_strips; i++) - { - if (!info->pclm_strip_data[i]) - return 0; - } + if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) + { + content_length += snprintf(content + content_length, sizeof(content) - content_length, + "%.2f 0 0 %.2f 0 0 cm\n", info->page_width, info->page_height); + content_length += snprintf(content + content_length, sizeof(content) - content_length, "/I Do\n"); + } + else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) + { + double d = DEFAULT_PDF_UNIT / atoi(info->pclm_source_resolution_default); + content_length += snprintf(content + content_length, sizeof(content) - content_length, + "%.2f 0 0 %.2f 0 0 cm\n", d, d); + + for (unsigned i = 0; i < info->pclm_num_strips; i++) + { + unsigned yAnchor = info->height - info->pclm_strip_height[i]; + content_length += snprintf(content + content_length, sizeof(content) - content_length, + "/P <> BDC q\n%.2f 0 0 %.2f 0 %u cm\n/Image%d Do Q\n", + (double)info->width, (double)info->pclm_strip_height[i], yAnchor, i); + } + } - // Further PCLm-specific handling for strips would be implemented here. + pdfio_stream_t *page_content = pdfioObjCreateStream(image_obj, PDFIO_FILTER_NONE); + pdfioStreamWrite(page_content, content, content_length); + pdfioStreamClose(page_content); + + info->page_data = NULL; + memset(info->pclm_strip_data, 0, sizeof(info->pclm_strip_data)); + + return 0; +} + +// +// Perform modifications to PDF if color space conversions are needed +// + +int prepare_pdf_page(struct pdf_info *info, + unsigned width, + unsigned height, + unsigned bpl, + unsigned bpp, + unsigned bpc, + const char *render_intent, + cups_cspace_t color_space, + pwgtopdf_doc_t *doc) +{ + +#define IMAGE_CMYK_8 (bpp == 32 && bpc == 8) +#define IMAGE_CMYK_16 (bpp == 64 && bpc == 16) +#define IMAGE_RGB_8 (bpp == 24 && bpc == 8) +#define IMAGE_RGB_16 (bpp == 48 && bpc == 16) +#define IMAGE_WHITE_1 (bpp == 1 && bpc == 1) +#define IMAGE_WHITE_8 (bpp == 8 && bpc == 8) +#define IMAGE_WHITE_16 (bpp == 16 && bpc == 16) + + int error = 0; + pdf_convert_function fn = convert_pdf_no_conversion; + cmsColorSpaceSignature css; + + // Register available raster information into the PDF + info->width = width; + info->height = height; + info->line_bytes = bpl; + info->bpp = bpp; + info->bpc = bpc; + info->render_intent = strdup(render_intent); + info->color_space = color_space; + + if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) + { + info->pclm_num_strips = + (height / info->pclm_strip_height_preferred) + + (height % info->pclm_strip_height_preferred ? 1 : 0); + + info->pclm_strip_height = (unsigned *)malloc(info->pclm_num_strips * sizeof(unsigned)); + info->pclm_strip_data = (unsigned char **)malloc(info->pclm_num_strips * sizeof(unsigned char *)); + + for (size_t i = 0; i < info->pclm_num_strips; i++) + { + info->pclm_strip_height[i] = + info->pclm_strip_height_preferred < height ? + info->pclm_strip_height_preferred : height; + height -= info->pclm_strip_height[i]; } + } + + if (color_space == CUPS_CSPACE_K) + fn = convert_pdf_invert_colors; + + if (doc->colorProfile != NULL) + { + css = cmsGetColorSpace(doc->colorProfile); + + // Convert image and PDF color space to an embedded ICC Profile color + // space + switch (css) + { + // Convert PDF to Grayscale when using a gray profile + case cmsSigGrayData: + if (color_space == CUPS_CSPACE_CMYK) + fn = convert_pdf_cmyk_8_to_white_8; + else if (color_space == CUPS_CSPACE_RGB) + fn = convert_pdf_rgb_8_to_white_8; + else + fn = convert_pdf_invert_colors; + info->color_space = CUPS_CSPACE_K; + break; + + // Convert PDF to RGB when using an RGB profile + case cmsSigRgbData: + if (color_space == CUPS_CSPACE_CMYK) + fn = convert_pdf_cmyk_8_to_rgb_8; + else if (color_space == CUPS_CSPACE_K) + fn = convert_pdf_white_8_to_rgb_8; + info->color_space = CUPS_CSPACE_RGB; + break; - // Add transformation and draw command content - if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) + // Convert PDF to RGB when using an RGB profile + case cmsSigCmykData: + if (color_space == CUPS_CSPACE_RGB) + fn = convert_pdf_rgb_8_to_cmyk_8; + else if (color_space == CUPS_CSPACE_K) + fn = convert_pdf_white_8_to_cmyk_8; + info->color_space = CUPS_CSPACE_CMYK; + break; + + default: + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Unable to convert PDF from profile."); + doc->colorProfile = NULL; + error = 1; + } + } + else if (!doc->cm_disabled) + { + switch (color_space) { - content_length += snprintf(content + content_length, sizeof(content) - content_length, - "%.2f 0 0 %.2f 0 0 cm\n", info->page_width, info->page_height); - content_length += snprintf(content + content_length, sizeof(content) - content_length, "/I Do\n"); + case CUPS_CSPACE_CMYK: + if (IMAGE_RGB_8) + fn = convert_pdf_rgb_8_to_cmyk_8; + else if (IMAGE_RGB_16) + fn = convert_pdf_no_conversion; + else if (IMAGE_WHITE_8) + fn = convert_pdf_white_8_to_cmyk_8; + else if (IMAGE_WHITE_16) + fn = convert_pdf_no_conversion; + break; + // Convert image to RGB + case CUPS_CSPACE_ADOBERGB: + case CUPS_CSPACE_RGB: + case CUPS_CSPACE_SRGB: + if (IMAGE_CMYK_8) + fn = convert_pdf_cmyk_8_to_rgb_8; + else if (IMAGE_CMYK_16) + fn = convert_pdf_no_conversion; + else if (IMAGE_WHITE_8) + fn = convert_pdf_white_8_to_rgb_8; + else if (IMAGE_WHITE_16) + fn = convert_pdf_no_conversion; + break; + // Convert image to Grayscale + case CUPS_CSPACE_SW: + case CUPS_CSPACE_K: + if (IMAGE_CMYK_8) + fn = convert_pdf_cmyk_8_to_white_8; + else if (IMAGE_CMYK_16) + fn = convert_pdf_no_conversion; + else if (IMAGE_RGB_8) + fn = convert_pdf_rgb_8_to_white_8; + else if (IMAGE_RGB_16) + fn = convert_pdf_no_conversion; + break; + case CUPS_CSPACE_DEVICE1: + case CUPS_CSPACE_DEVICE2: + case CUPS_CSPACE_DEVICE3: + case CUPS_CSPACE_DEVICE4: + case CUPS_CSPACE_DEVICE5: + case CUPS_CSPACE_DEVICE6: + case CUPS_CSPACE_DEVICE7: + case CUPS_CSPACE_DEVICE8: + case CUPS_CSPACE_DEVICE9: + case CUPS_CSPACE_DEVICEA: + case CUPS_CSPACE_DEVICEB: + case CUPS_CSPACE_DEVICEC: + case CUPS_CSPACE_DEVICED: + case CUPS_CSPACE_DEVICEE: + case CUPS_CSPACE_DEVICEF: + fn = convert_pdf_no_conversion; + break; + default: + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Color space not supported."); + error = 1; + break; } - else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) + } + + if (!error) + fn(info, doc); + + return error; +} + + +static int +add_pdf_page(struct pdf_info *info, + int pagen, + unsigned width, + unsigned height, + int bpp, + int bpc, + int bpl, + const char *render_intent, + cups_cspace_t color_space, + unsigned xdpi, + unsigned ydpi, + pwgtopdf_doc_t *doc) +{ + if (finish_page(info, doc)) + return 1; + + prepare_pdf_page(info, width, height, bpl, bpp, bpc, render_intent, color_space, doc); + + if (info->height > (UINT_MAX / info->line_bytes)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Page too big"); + return 1; + } + + if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) + info->page_data = malloc(info->line_bytes * info->height); + + else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) + { + for (size_t i = 0; i < info->pclm_num_strips; i++) { - double d = DEFAULT_PDF_UNIT / atoi(info->pclm_source_resolution_default); - content_length += snprintf(content + content_length, sizeof(content) - content_length, - "%.2f 0 0 %.2f 0 0 cm\n", d, d); + info->pclm_strip_data[i] = malloc(info->line_bytes * info->pclm_strip_height[i]); + } + } + + pdfio_dict_t *page_dict = pdfioDictCreate(info->pdf); + + pdfioDictSetName(page_dict, "Type", "Page"); + pdfioDictSetDict(page_dict, "Resources", pdfioDictCreate(info->pdf)); + pdfioDictSetNull(page_dict, "MediaBox"); + pdfioDictSetNull(page_dict, "Contents"); - // Iterate through strips and create content for each - for (unsigned i = 0; i < info->pclm_num_strips; i++) + + info->page_width = ((double)info->width / xdpi) * DEFAULT_PDF_UNIT; + info->page_height = ((double)info->height / ydpi) * DEFAULT_PDF_UNIT; + + if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) + { + pdfio_obj_t *null_obj = pdfioFileCreateObj(info->pdf, pdfioDictCreate(info->pdf)); + pdfioDictSetObj(page_dict, "Contents", null_obj); + pdfio_rect_t media_rect = make_real_box(0, 0, info->page_width, info->page_height); + pdfioDictSetRect(page_dict, "MediaBox", &media_rect); + } + + else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) + { + pdfio_obj_t *null_obj = pdfioFileCreateObj(info->pdf, pdfioDictCreate(info->pdf)); + pdfioDictSetObj(page_dict, "Contents", null_obj); + + pdfio_rect_t media_rect = make_real_box(0, 0, info->page_width + 0.5, info->page_height + 0.5); + pdfioDictSetRect(page_dict, "MediaBox", &media_rect); + } + + pdfio_obj_t *page = pdfioFileCreateObj(info->pdf, page_dict); + + info->page = page; // we want to keep a + // reference + return 0; +} + +static int +close_pdf_file(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + if (finish_page(info, doc)) + return 1; + + pdfioFileClose(info->pdf); + + return 0; +} + +static void +pdf_set_line(struct pdf_info * info, + unsigned line_n, + unsigned char *line, + pwgtopdf_doc_t *doc) +{ + if (line_n > info->height) + { + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Bad line %d", line_n); + return; + } + + if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) + { + // copy line data into appropriate pclm strip + size_t strip_num = line_n / info->pclm_strip_height_preferred; + unsigned line_strip = line_n - + strip_num * info->pclm_strip_height_preferred; + memcpy(((info->pclm_strip_data[strip_num]) + + (line_strip*info->line_bytes)), line, info->line_bytes); + } + else + memcpy((info->page_data + (line_n * info->line_bytes)), + line, info->line_bytes); +} + +static int +convert_raster(cups_raster_t *ras, + unsigned width, + unsigned height, + int bpp, + int bpl, + struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + // We should be at raster start + int i; + unsigned cur_line = 0; + unsigned char *PixelBuffer, *ptr = NULL, *buff; + + PixelBuffer = (unsigned char *)malloc(bpl); + buff = (unsigned char *)malloc(info->line_bytes); + + do + { + // Read raster data... + cupsRasterReadPixels(ras, PixelBuffer, bpl); + +#if !ARCH_IS_BIG_ENDIAN + if (info->bpc == 16) + { + // Swap byte pairs for endianess (cupsRasterReadPixels() switches + // from Big Endian back to the system's Endian) + for (i = bpl, ptr = PixelBuffer; i > 0; i -= 2, ptr += 2) + { + unsigned char swap = *ptr; + *ptr = *(ptr + 1); + *(ptr + 1) = swap; + } + } +#endif // !ARCH_IS_BIG_ENDIAN + + // perform bit operations if necessary + doc->bit_function(PixelBuffer, ptr, bpl); + + // write lines and color convert when necessary + pdf_set_line(info, cur_line, doc->conversion_function(PixelBuffer, + buff, width), + doc); + ++cur_line; + } + while (cur_line < height); + + free(buff); + free(PixelBuffer); + + return (0); +} + +static int +set_profile(const char *path, + pwgtopdf_doc_t *doc) +{ + if (path != NULL) + doc->colorProfile = cmsOpenProfileFromFile(path, "r"); + + if (doc->colorProfile != NULL) + { + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Load profile successful."); + return (0); + } + else + { + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Unable to load profile."); + return (1); + } +} + + +int // O - Error status +cfFilterPWGToPDF(int inputfd, // I - File descriptor input stream + int outputfd, // I - File descriptor output stream + int inputseekable, // I - Is input stream seekable? (unused) + cf_filter_data_t *data, // I - Job and printer data + void *parameters) // I - Filter-specific parameters (outformat) +{ + cups_len_t i; + char *t; + pwgtopdf_doc_t doc; // Document information + FILE *outputfp; // Output data stream + cf_filter_out_format_t outformat; // Output format + int Page, empty = 1; + cf_cm_calibration_t cm_calibrate; // Status of CUPS color management + // ("on" or "off") + struct pdf_info pdf; + cups_raster_t *ras; // Raster stream for printing + cups_page_header_t header; // Page header from file + ipp_t *printer_attrs = data->printer_attrs; // Printer attributes from + // printer data + ipp_attribute_t *ipp_attr; // Printer attribute + const char *profile_name = NULL; // IPP Profile Name + cf_logfunc_t log = data->logfunc; + void *ld = data->logdata; + cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; + void *icd = data->iscanceleddata; + int total_attrs; + char buf[1024]; + const char *kw; + + (void)inputseekable; + + if (parameters) + { + outformat = *(cf_filter_out_format_t *)parameters; + if (outformat != CF_FILTER_OUT_FORMAT_PCLM) + outformat = CF_FILTER_OUT_FORMAT_PDF; + } + else + { + t = data->final_content_type; + if (t) + { + if (strcasestr(t, "pclm")) + outformat = CF_FILTER_OUT_FORMAT_PCLM; + else if (strcasestr(t, "pdf")) + outformat = CF_FILTER_OUT_FORMAT_PDF; + else + outformat = CF_FILTER_OUT_FORMAT_PDF; + } + else + outformat = CF_FILTER_OUT_FORMAT_PDF; + } + + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: OUTFORMAT=\"%s\"", + outformat == CF_FILTER_OUT_FORMAT_PDF ? "PDF" : "PCLM"); + + // + // Open the output data stream specified by the outputfd... + // + + if ((outputfp = fdopen(outputfd, "w")) == NULL) + { + if (!iscanceled || !iscanceled(icd)) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Unable to open output data stream."); + } + + return (1); + } + + doc.outputfp = outputfp; + // Logging function + doc.logfunc = log; + doc.logdata = ld; + // Job-is-canceled function + doc.iscanceledfunc = iscanceled; + doc.iscanceleddata = icd; + + // support the CUPS "cm-calibration" option + cm_calibrate = cfCmGetCupsColorCalibrateMode(data); + + if (outformat == CF_FILTER_OUT_FORMAT_PCLM || + cm_calibrate == CF_CM_CALIBRATION_ENABLED) + doc.cm_disabled = 1; + else + doc.cm_disabled = cfCmIsPrinterCmDisabled(data); + + if (outformat == CF_FILTER_OUT_FORMAT_PCLM && printer_attrs == NULL) + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPWGToPDF: PCLm output: No printer IPP attributes are supplied, PCLm output not possible."); + return (1); + } + + // Transform + ras = cupsRasterOpen(inputfd, CUPS_RASTER_READ); + + // Process pages as needed... + Page = 0; + + // Get PCLm parameters from printer IPP attributes + if (outformat == CF_FILTER_OUT_FORMAT_PCLM) + { + if (log) + { + log(ld, CF_LOGLEVEL_DEBUG, "PCLm-related printer IPP attributes:"); + total_attrs = 0; + ipp_attr = ippGetFirstAttribute(printer_attrs); + while (ipp_attr) + { + if (strncmp(ippGetName(ipp_attr), "pclm-", 5) == 0) { - unsigned yAnchor = info->height - info->pclm_strip_height[i]; - content_length += snprintf(content + content_length, sizeof(content) - content_length, - "/P <> BDC q\n%.2f 0 0 %.2f 0 %u cm\n/Image%d Do Q\n", - info->width, info->pclm_strip_height[i], yAnchor, i); - } + total_attrs ++; + ippAttributeString(ipp_attr, buf, sizeof(buf)); + log(ld, CF_LOGLEVEL_DEBUG, " Attr: %s",ippGetName(ipp_attr)); + log(ld, CF_LOGLEVEL_DEBUG, " Value: %s", buf); + for (i = 0; i < ippGetCount(ipp_attr); i ++) + if ((kw = ippGetString(ipp_attr, i, NULL)) != NULL) + log(ld, CF_LOGLEVEL_DEBUG, " Keyword: %s", kw); + } + ipp_attr = ippGetNextAttribute(printer_attrs); + } + log(ld, CF_LOGLEVEL_DEBUG, " %d attributes", total_attrs); + } + + char *attr_name = (char *)"pclm-strip-height-preferred"; + if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, + IPP_TAG_ZERO)) != NULL) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value %d", + attr_name, ippGetInteger(ipp_attr, 0)); + pdf.pclm_strip_height_preferred = ippGetInteger(ipp_attr, 0); } + else + pdf.pclm_strip_height_preferred = 16; // default strip height - // Write content stream to PDF page - pdfio_stream_t *page_content = pdfioStreamCreate(info->pdf, NULL); - pdfioStreamWrite(page_content, content, content_length); - pdfioStreamClose(page_content); + attr_name = (char *)"pclm-strip-height-supported"; + if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, + IPP_TAG_ZERO)) != NULL) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Printer PCLm attribute \"%s\"", + attr_name); + pdf.pclm_strip_height_supported = NULL; // remove default value = 16 + for (i = 0; i < ippGetCount(ipp_attr); i ++) + pdf.pclm_strip_height_supported[i] = ippGetInteger(ipp_attr, i); + } - // Reset data after finishing the page - info->page_data = NULL; - memset(info->pclm_strip_data, 0, sizeof(info->pclm_strip_data)); - return 0; + attr_name = (char *)"pclm-raster-back-side"; + if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, + IPP_TAG_ZERO)) != NULL) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", + attr_name, ippGetString(ipp_attr, 0, NULL)); + pdf.pclm_raster_back_side = ippGetString(ipp_attr, 0, NULL); + } + + attr_name = (char *)"pclm-source-resolution-supported"; + if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, + IPP_TAG_ZERO)) != NULL) + { + ippAttributeString(ipp_attr, buf, sizeof(buf)); + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", + attr_name, buf); + int size = (int)sizeof(buf); + + pdf.pclm_source_resolution_supported = split_strings(buf, ",", &size); + } + + attr_name = (char *)"pclm-source-resolution-default"; + if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, + IPP_TAG_ZERO)) != NULL) + { + ippAttributeString(ipp_attr, buf, sizeof(buf)); + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", + attr_name, buf); + pdf.pclm_source_resolution_default = buf; + } + else if (sizeof(pdf.pclm_source_resolution_supported) > 0) + { + pdf.pclm_source_resolution_default = + pdf.pclm_source_resolution_supported[0]; + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" missing, taking first item of \"pclm-source-resolution-supported\" as default resolution", + attr_name); + } + else + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPWGToPDF: PCLm output: Printer IPP attributes do not contain printer resolution information for PCLm."); + return (1); + } + + attr_name = (char *)"pclm-compression-method-preferred"; + if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, + IPP_TAG_ZERO)) != NULL) + { + ippAttributeString(ipp_attr, buf, sizeof(buf)); + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", + attr_name, buf); + + } + // If the compression methods is none of the above or is erreneous + // use FLATE as compression method and show a warning. + if (pdf.pclm_compression_method_preferred == NULL) + { + if (log) log(ld, CF_LOGLEVEL_WARN, + "(pwgtopclm) Unable parse Printer attribute \"%s\". " + "Using FLATE for encoding image streams.", attr_name); + } + } + + while (cupsRasterReadHeader(ras, &header)) + { + if (iscanceled && iscanceled(icd)) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Job canceled"); + break; + } + + if (empty) + { + empty = 0; + // We have a valid input page, so create PDF file + if (create_pdf_file(&pdf, outformat) != 0) + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPWGToPDF: Unable to create PDF file"); + return (1); + } + } + + // Write a status message with the page number + Page ++; + if (log) log(ld, CF_LOGLEVEL_INFO, + "cfFilterPWGToPDF: Starting page %d.", Page); + + // Update rendering intent with user settings or the default + cfGetPrintRenderIntent(data, header.cupsRenderingIntent, + sizeof(header.cupsRenderingIntent)); + + // Use "profile=profile_name.icc" to embed 'profile_name.icc' into the PDF + // for testing. Forces color management to enable. + if (outformat == CF_FILTER_OUT_FORMAT_PDF && + (profile_name = cupsGetOption("profile", data->num_options, + data->options)) != NULL) + { + set_profile(profile_name, &doc); + doc.cm_disabled = 0; + } + if (doc.colorProfile != NULL) + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: TEST ICC Profile specified (color " + "management forced ON): \n[%s]", profile_name); + + // Add a new page to PDF file + if (add_pdf_page(&pdf, Page, header.cupsWidth, header.cupsHeight, + header.cupsBitsPerPixel, header.cupsBitsPerColor, + header.cupsBytesPerLine, header.cupsRenderingIntent, + header.cupsColorSpace, header.HWResolution[0], + header.HWResolution[1], &doc) != 0) + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPWGToPDF: Unable to start new PDF page"); + return (1); + } + + // Write the bit map into the PDF file + if (convert_raster(ras, header.cupsWidth, header.cupsHeight, + header.cupsBitsPerPixel, header.cupsBytesPerLine, + &pdf, &doc) != 0) + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPWGToPDF: Failed to convert page bitmap"); + return (1); + } + } + + if (empty) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Input is empty, outputting empty file."); + cupsRasterClose(ras); + return (0); + } + + close_pdf_file(&pdf, &doc); // output to outputfp + + if (doc.colorProfile != NULL) + cmsCloseProfile(doc.colorProfile); + + cupsRasterClose(ras); + fclose(outputfp); + + return (Page == 0); } diff --git a/cupsfilters/pwgtopdf.cxx b/cupsfilters/pwgtopdf.cxx deleted file mode 100644 index 06a21565..00000000 --- a/cupsfilters/pwgtopdf.cxx +++ /dev/null @@ -1,1829 +0,0 @@ -// -// PWG/Apple Raster to PDF filter function for libcupsfilters. -// -// Copyright 2010 by Neil 'Superna' Armstrong -// Copyright 2012 by Tobias Hoffmann -// Copyright 2014-2022 by Till Kamppeter -// Copyright 2017 by Sahil Arora -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // ntohl - -#include -#include -#include -#include - -#include -#include -#include -#include - -#ifdef USE_LCMS1 -#include -#define cmsColorSpaceSignature icColorSpaceSignature -#define cmsSetLogErrorHandler cmsSetErrorHandler -#define cmsSigXYZData icSigXYZData -#define cmsSigLuvData icSigLuvData -#define cmsSigLabData icSigLabData -#define cmsSigYCbCrData icSigYCbCrData -#define cmsSigYxyData icSigYxyData -#define cmsSigRgbData icSigRgbData -#define cmsSigHsvData icSigHsvData -#define cmsSigHlsData icSigHlsData -#define cmsSigCmyData icSigCmyData -#define cmsSig3colorData icSig3colorData -#define cmsSigGrayData icSigGrayData -#define cmsSigCmykData icSigCmykData -#define cmsSig4colorData icSig4colorData -#define cmsSig2colorData icSig2colorData -#define cmsSig5colorData icSig5colorData -#define cmsSig6colorData icSig6colorData -#define cmsSig7colorData icSig7colorData -#define cmsSig8colorData icSig8colorData -#define cmsSig9colorData icSig9colorData -#define cmsSig10colorData icSig10colorData -#define cmsSig11colorData icSig11colorData -#define cmsSig12colorData icSig12colorData -#define cmsSig13colorData icSig13colorData -#define cmsSig14colorData icSig14colorData -#define cmsSig15colorData icSig15colorData -#define cmsSaveProfileToMem _cmsSaveProfileToMem -#else -#include -#endif - -#define DEFAULT_PDF_UNIT 72 // 1/72 inch - -#define PRE_COMPRESS - - -// Compression method for providing data to PCLm Streams. -typedef enum compression_method_e -{ - DCT_DECODE = 0, - FLATE_DECODE, - RLE_DECODE -} compression_method_t; - -// Color conversion function -typedef unsigned char *(*convert_function)(unsigned char *src, - unsigned char *dst, - unsigned int pixels); - -// Bit conversion function -typedef unsigned char *(*bit_convert_function)(unsigned char *src, - unsigned char *dst, - unsigned int pixels); - -typedef struct pwgtopdf_doc_s // **** Document information **** -{ - cmsHPROFILE colorProfile = NULL; // ICC Profile to be applied to - // PDF - int cm_disabled = 0; // Flag raised if color - // management is disabled - convert_function conversion_function; // Raster color conversion - // function - bit_convert_function bit_function; // Raster bit function - FILE *outputfp; // Temporary file, if any - cf_logfunc_t logfunc; // Logging function, NULL for no - // logging - void *logdata; // User data for logging - // function, can be NULL - cf_filter_iscanceledfunc_t iscanceledfunc; // Function returning 1 when - // job is canceled, NULL for not - // supporting stop on cancel - void *iscanceleddata; // User data for is-canceled - // function, can be NULL -} pwgtopdf_doc_t; - -// PDF color conversion function -typedef void (*pdf_convert_function)(struct pdf_info * info, - pwgtopdf_doc_t *doc); - - -struct pdf_info // **** PDF **** -{ - pdf_info() - : pagecount(0), - width(0), height(0), - line_bytes(0), - bpp(0), bpc(0), - pclm_num_strips(0), - pclm_strip_height_preferred(16), // default strip height - pclm_strip_height(0), - pclm_strip_height_supported(1, 16), - pclm_compression_method_preferred(0), - pclm_source_resolution_supported(0), - pclm_source_resolution_default(""), - pclm_raster_back_side(""), - pclm_strip_data(0), - render_intent(""), - color_space(CUPS_CSPACE_K), - page_width(0), page_height(0), - outformat(CF_FILTER_OUT_FORMAT_PDF) - { - } - - QPDF pdf; - QPDFObjectHandle page; - unsigned pagecount; - unsigned width; - unsigned height; - unsigned line_bytes; - unsigned bpp; - unsigned bpc; - unsigned pclm_num_strips; - unsigned pclm_strip_height_preferred; - std::vector pclm_strip_height; - std::vector pclm_strip_height_supported; - std::vector pclm_compression_method_preferred; - std::vector pclm_source_resolution_supported; - std::string pclm_source_resolution_default; - std::string pclm_raster_back_side; - std::vector< std::shared_ptr > pclm_strip_data; - std::string render_intent; - cups_cspace_t color_space; - std::shared_ptr page_data; - double page_width,page_height; - cf_filter_out_format_t outformat; -}; - - -// -// Bit conversion functions -// - -static unsigned char * -invert_bits(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - unsigned int i; - - // Invert black to grayscale... - for (i = pixels, dst = src; i > 0; i --, dst ++) - *dst = ~*dst; - - return (dst); -} - - -static unsigned char * -no_bit_conversion(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - return (src); -} - - -// -// Color conversion functions -// - -static unsigned char * -rgb_to_cmyk(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageRGBToCMYK(src, dst, pixels); - return (dst); -} - - -static unsigned char * -white_to_cmyk(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageWhiteToCMYK(src, dst, pixels); - return (dst); -} - - -static unsigned char * -cmyk_to_rgb(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageCMYKToRGB(src, dst, pixels); - return (dst); -} - - -static unsigned char * -white_to_rgb(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageWhiteToRGB(src, dst, pixels); - return (dst); -} - - -static unsigned char * -rgb_to_white(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageRGBToWhite(src, dst, pixels); - return (dst); -} - - -static unsigned char * -cmyk_to_white(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageCMYKToWhite(src, dst, pixels); - return (dst); -} - - -static unsigned char * -no_color_conversion(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - return (src); -} - - -// -// 'split_strings()' - Split a string to a vector of strings given some -// delimiters -// -// O - std::vector of std::string after splitting -// I - input string to be split -// I - string containing delimiters -// - -static std::vector -split_strings(std::string const &str, - std::string delimiters = ",") -{ - std::vector vec(0); - std::string value = ""; - bool push_flag = false; - - for (size_t i = 0; i < str.size(); i ++) - { - if (push_flag && !(value.empty())) - { - vec.push_back(value); - push_flag = false; - value.clear(); - } - - if (delimiters.find(str[i]) != std::string::npos) - push_flag = true; - else - value += str[i]; - } - if (!value.empty()) - vec.push_back(value); - return (vec); -} - - -// -// 'num_digits()' - Calculates the number of digits in an integer -// -// O - number of digits in the input integer -// I - the integer whose digits needs to be calculated -// - -static int -num_digits(int n) -{ - if (n == 0) - return (1); - int digits = 0; - while (n) - { - ++digits; - n /= 10; - } - return (digits); -} - - -// -// 'int_to_fwstring()' - Convert a number to fixed width string by padding -// with zeroes -// O - converted string -// I - the integee which needs to be converted to string -// I - width of string required -// - -static std::string -int_to_fwstring(int n, - int width) -{ - int num_zeroes = width - num_digits(n); - if (num_zeroes < 0) - num_zeroes = 0; - return (std::string(num_zeroes, '0') + QUtil::int_to_string(n)); -} - - -static int -create_pdf_file(struct pdf_info * info, - const cf_filter_out_format_t &outformat) -{ - try - { - info->pdf.emptyPDF(); - info->outformat = outformat; - } - catch (...) - { - return (1); - } - return (0); -} - - -static QPDFObjectHandle -make_real_box(double x1, - double y1, - double x2, - double y2) -{ - QPDFObjectHandle ret = QPDFObjectHandle::newArray(); - ret.appendItem(QPDFObjectHandle::newReal(x1)); - ret.appendItem(QPDFObjectHandle::newReal(y1)); - ret.appendItem(QPDFObjectHandle::newReal(x2)); - ret.appendItem(QPDFObjectHandle::newReal(y2)); - return (ret); -} - - -static QPDFObjectHandle -make_integer_box(int x1, - int y1, - int x2, - int y2) -{ - QPDFObjectHandle ret = QPDFObjectHandle::newArray(); - ret.appendItem(QPDFObjectHandle::newInteger(x1)); - ret.appendItem(QPDFObjectHandle::newInteger(y1)); - ret.appendItem(QPDFObjectHandle::newInteger(x2)); - ret.appendItem(QPDFObjectHandle::newInteger(y2)); - return (ret); -} - - -// -// PDF color conversion functons... -// - -static void -modify_pdf_color(struct pdf_info *info, - int bpp, - int bpc, - convert_function fn, - pwgtopdf_doc_t *doc) -{ - unsigned old_bpp = info->bpp; - unsigned old_bpc = info->bpc; - double old_ncolor = old_bpp / old_bpc; - - unsigned old_line_bytes = info->line_bytes; - - double new_ncolor = bpp / bpc; - - info->line_bytes = (unsigned)old_line_bytes * (new_ncolor / old_ncolor); - info->bpp = bpp; - info->bpc = bpc; - doc->conversion_function = fn; -} - - -static void -convert_pdf_no_conversion(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - doc->conversion_function = no_color_conversion; - doc->bit_function = no_bit_conversion; -} - - -static void -convert_pdf_cmyk_8_to_white_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 8, 8, cmyk_to_white, doc); - doc->bit_function = no_bit_conversion; -} - - -static void -convert_pdf_rgb_8_to_white_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 8, 8, rgb_to_white, doc); - doc->bit_function = no_bit_conversion; -} - - -static void -convert_pdf_cmyk_8_to_rgb_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 24, 8, cmyk_to_rgb, doc); - doc->bit_function = no_bit_conversion; -} - - -static void -convert_pdf_white_8_to_rgb_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 24, 8, white_to_rgb, doc); - doc->bit_function = invert_bits; -} - - -static void -convert_pdf_rgb_8_to_cmyk_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 32, 8, rgb_to_cmyk, doc); - doc->bit_function = no_bit_conversion; -} - - -static void -convert_pdf_white_8_to_cmyk_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 32, 8, white_to_cmyk, doc); - doc->bit_function = invert_bits; -} - - -static void -convert_pdf_invert_colors(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - doc->conversion_function = no_color_conversion; - doc->bit_function = invert_bits; -} - - -// -// Create an '/ICCBased' array and embed a previously -// set ICC Profile in the PDF -// - -static QPDFObjectHandle -embed_icc_profile(QPDF &pdf, - pwgtopdf_doc_t *doc) -{ - if (doc->colorProfile == NULL) - return (QPDFObjectHandle::newNull()); - - // Return handler - QPDFObjectHandle ret; - // ICCBased array - QPDFObjectHandle array = QPDFObjectHandle::newArray(); - // Profile stream dictionary - QPDFObjectHandle iccstream; - - std::map dict; - std::map streamdict; - std::string n_value = ""; - std::string alternate_cs = ""; - std::shared_ptrph; - -#ifdef USE_LCMS1 - size_t profile_size; -#else - unsigned int profile_size; -#endif - - cmsColorSpaceSignature css = cmsGetColorSpace(doc->colorProfile); - - // Write color component # for /ICCBased array in stream dictionary - switch (css) - { - case cmsSigGrayData: - n_value = "1"; - alternate_cs = "/DeviceGray"; - break; - case cmsSigRgbData: - n_value = "3"; - alternate_cs = "/DeviceRGB"; - break; - case cmsSigCmykData: - n_value = "4"; - alternate_cs = "/DeviceCMYK"; - break; - default: - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Failed to embed ICC Profile."); - return (QPDFObjectHandle::newNull()); - } - - streamdict["/Alternate"] = QPDFObjectHandle::newName(alternate_cs); - streamdict["/N"] = QPDFObjectHandle::newName(n_value); - - // Read profile into memory - cmsSaveProfileToMem(doc->colorProfile, NULL, &profile_size); - unsigned char *buff = - (unsigned char *)calloc(profile_size, sizeof(unsigned char)); - cmsSaveProfileToMem(doc->colorProfile, buff, &profile_size); - - // Write ICC profile buffer into PDF - auto bf = new Buffer(buff, profile_size); - ph = std::shared_ptr(bf); - iccstream = QPDFObjectHandle::newStream(&pdf, ph); - iccstream.replaceDict(QPDFObjectHandle::newDictionary(streamdict)); - - array.appendItem(QPDFObjectHandle::newName("/ICCBased")); - array.appendItem(iccstream); - - // Return a PDF object reference to an '/ICCBased' array - ret = pdf.makeIndirectObject(array); - - free(buff); - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: ICC Profile embedded in PDF."); - - return (ret); -} - - -static QPDFObjectHandle -embed_srgb_profile(QPDF &pdf, - pwgtopdf_doc_t *doc) -{ - QPDFObjectHandle iccbased_reference; - - // Create an sRGB profile from lcms - doc->colorProfile = cmsCreate_sRGBProfile(); - // Embed it into the profile - iccbased_reference = embed_icc_profile(pdf, doc); - - return (iccbased_reference); -} - - -// -// Calibration function for non-Lab PDF color spaces -// Requires white point data, and if available, gamma or matrix numbers. -// -// Output: -// [/'color_space' -// << /Gamma ['gamma[0]'...'gamma[n]'] -// /WhitePoint ['wp[0]' 'wp[1]' 'wp[2]'] -// /Matrix ['matrix[0]'...'matrix[n*n]'] -// >> -// ] -// - -static QPDFObjectHandle -get_calibration_array(const char *color_space, - double wp[], - double gamma[], - double matrix[], - double bp[]) -{ - // Check for invalid input - if ((!strcmp("/CalGray", color_space) && matrix != NULL) || - wp == NULL) - return (QPDFObjectHandle()); - - QPDFObjectHandle ret; - std::string csString = color_space; - std::string colorSpaceArrayString = ""; - - char gamma_str[128]; - char bp_str[256]; - char wp_str[256]; - char matrix_str[512]; - - // Convert numbers into string data for /Gamma, /WhitePoint, and/or /Matrix - - // WhitePoint - snprintf(wp_str, sizeof(wp_str), "/WhitePoint [%g %g %g]", - wp[0], wp[1], wp[2]); - - // Gamma - if (!strcmp("/CalGray", color_space) && gamma != NULL) - snprintf(gamma_str, sizeof(gamma_str), "/Gamma %g", - gamma[0]); - else if (!strcmp("/CalRGB", color_space) && gamma != NULL) - snprintf(gamma_str, sizeof(gamma_str), "/Gamma [%g %g %g]", - gamma[0], gamma[1], gamma[2]); - else - gamma_str[0] = '\0'; - - // BlackPoint - if (bp != NULL) - snprintf(bp_str, sizeof(bp_str), "/BlackPoint [%g %g %g]", - bp[0], bp[1], bp[2]); - else - bp_str[0] = '\0'; - - // Matrix - if (!strcmp("/CalRGB", color_space) && matrix != NULL) - { - snprintf(matrix_str, sizeof(matrix_str), - "/Matrix [%g %g %g %g %g %g %g %g %g]", - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5], - matrix[6], matrix[7], matrix[8]); - } - else - matrix_str[0] = '\0'; - - // Write array string... - colorSpaceArrayString = "[" + csString + " <<" + gamma_str + " " + wp_str + - " " + matrix_str + " " + bp_str + " >>]"; - - ret = QPDFObjectHandle::parse(colorSpaceArrayString); - - return (ret); -} - - -static QPDFObjectHandle -get_cal_rgb_array(double wp[3], - double gamma[3], - double matrix[9], - double bp[3]) -{ - QPDFObjectHandle ret = get_calibration_array("/CalRGB", wp, gamma, matrix, - bp); - return (ret); -} - - -static QPDFObjectHandle -get_cal_gray_array(double wp[3], - double gamma[1], - double bp[3]) -{ - QPDFObjectHandle ret = get_calibration_array("/CalGray", wp, gamma, 0, bp); - return (ret); -} - - -// -// 'make_pclm_strips()' - Return an std::vector of QPDFObjectHandle, each -// containing the stream data of the various strips -// which make up a PCLm page. -// -// O - std::vector of QPDFObjectHandle -// I - QPDF object -// I - number of strips per page -// I - std::vector of std::shared_ptr containing data for each strip -// I - strip width -// I - strip height -// I - color space -// I - bits per component -// I - document information -// - -static std::vector -make_pclm_strips(QPDF &pdf, - unsigned num_strips, - std::vector< std::shared_ptr > &strip_data, - std::vector &compression_methods, - unsigned width, std::vector& strip_height, - cups_cspace_t cs, - unsigned bpc, - pwgtopdf_doc_t *doc) -{ - std::vector ret(num_strips); - for (size_t i = 0; i < num_strips; i ++) - ret[i] = QPDFObjectHandle::newStream(&pdf); - - // Strip stream dictionary - std::map dict; - - dict["/Type"] = QPDFObjectHandle::newName("/XObject"); - dict["/Subtype"] = QPDFObjectHandle::newName("/Image"); - dict["/Width"] = QPDFObjectHandle::newInteger(width); - dict["/BitsPerComponent"] = QPDFObjectHandle::newInteger(bpc); - - J_COLOR_SPACE color_space; - unsigned components; - // Write "/ColorSpace" dictionary based on raster input - switch(cs) - { - case CUPS_CSPACE_K: - case CUPS_CSPACE_SW: - dict["/ColorSpace"] = QPDFObjectHandle::newName("/DeviceGray"); - color_space = JCS_GRAYSCALE; - components = 1; - break; - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_SRGB: - case CUPS_CSPACE_ADOBERGB: - dict["/ColorSpace"] = QPDFObjectHandle::newName("/DeviceRGB"); - color_space = JCS_RGB; - components = 3; - break; - default: - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Color space not supported."); - return (std::vector(num_strips, QPDFObjectHandle())); - } - - // - // We deliver already compressed content (instead of letting QPDFWriter - // do it) to avoid using excessive memory. For that we first get preferred - // compression method to pre-compress content for strip streams. - // - // Use the compression method with highest priority of the available methods - // __________________ - // Priority | Method - // ------------------ - // 0 | DCT - // 1 | FLATE - // 2 | RLE - // ------------------ - // - - compression_method_t compression = compression_methods.front(); - for (std::vector::iterator it = - compression_methods.begin(); - it != compression_methods.end(); ++it) - compression = compression > *it ? compression : *it; - - // write compressed stream data - for (size_t i = 0; i < num_strips; i ++) - { - dict["/Height"] = QPDFObjectHandle::newInteger(strip_height[i]); - ret[i].replaceDict(QPDFObjectHandle::newDictionary(dict)); - Pl_Buffer psink("psink"); - if (compression == FLATE_DECODE) - { - Pl_Flate pflate("pflate", &psink, Pl_Flate::a_deflate); - pflate.write(strip_data[i]->getBuffer(), strip_data[i]->getSize()); - pflate.finish(); - ret[i].replaceStreamData(std::shared_ptr(psink.getBuffer()), - QPDFObjectHandle::newName("/FlateDecode"), - QPDFObjectHandle::newNull()); - } - else if (compression == RLE_DECODE) - { - Pl_RunLength prle("prle", &psink, Pl_RunLength::a_encode); - prle.write(strip_data[i]->getBuffer(), strip_data[i]->getSize()); - prle.finish(); - ret[i].replaceStreamData(std::shared_ptr(psink.getBuffer()), - QPDFObjectHandle::newName("/RunLengthDecode"), - QPDFObjectHandle::newNull()); - } - else if (compression == DCT_DECODE) - { - Pl_DCT pdct("pdct", &psink, width, strip_height[i], components, - color_space); - pdct.write(strip_data[i]->getBuffer(), strip_data[i]->getSize()); - pdct.finish(); - ret[i].replaceStreamData(std::shared_ptr(psink.getBuffer()), - QPDFObjectHandle::newName("/DCTDecode"), - QPDFObjectHandle::newNull()); - } - } - return (ret); -} - - -static QPDFObjectHandle -make_image(QPDF &pdf, - std::shared_ptr page_data, - unsigned width, - unsigned height, - std::string render_intent, - cups_cspace_t cs, - unsigned bpc, - pwgtopdf_doc_t *doc) -{ - QPDFObjectHandle ret = QPDFObjectHandle::newStream(&pdf); - - QPDFObjectHandle icc_ref; - - int use_blackpoint = 0; - std::map dict; - - dict["/Type"] = QPDFObjectHandle::newName("/XObject"); - dict["/Subtype"] = QPDFObjectHandle::newName("/Image"); - dict["/Width"] = QPDFObjectHandle::newInteger(width); - dict["/Height"] = QPDFObjectHandle::newInteger(height); - dict["/BitsPerComponent"] = QPDFObjectHandle::newInteger(bpc); - - if (!doc->cm_disabled) - { - // Write rendering intent into the PDF based on raster settings - if (render_intent == "Perceptual") - dict["/Intent"] = QPDFObjectHandle::newName("/Perceptual"); - else if (render_intent == "Absolute") - dict["/Intent"] = QPDFObjectHandle::newName("/AbsoluteColorimetric"); - else if (render_intent == "Relative") - dict["/Intent"] = QPDFObjectHandle::newName("/RelativeColorimetric"); - else if (render_intent == "Saturation") - dict["/Intent"] = QPDFObjectHandle::newName("/Saturation"); - else if (render_intent == "RelativeBpc") - { - // Enable blackpoint compensation - dict["/Intent"] = QPDFObjectHandle::newName("/RelativeColorimetric"); - use_blackpoint = 1; - } - } - - // Write "/ColorSpace" dictionary based on raster input - if (doc->colorProfile != NULL && !doc->cm_disabled) - { - icc_ref = embed_icc_profile(pdf, doc); - - if (!icc_ref.isNull()) - dict["/ColorSpace"] = icc_ref; - } - else if (!doc->cm_disabled) - { - switch (cs) - { - case CUPS_CSPACE_DEVICE1: - case CUPS_CSPACE_DEVICE2: - case CUPS_CSPACE_DEVICE3: - case CUPS_CSPACE_DEVICE4: - case CUPS_CSPACE_DEVICE5: - case CUPS_CSPACE_DEVICE6: - case CUPS_CSPACE_DEVICE7: - case CUPS_CSPACE_DEVICE8: - case CUPS_CSPACE_DEVICE9: - case CUPS_CSPACE_DEVICEA: - case CUPS_CSPACE_DEVICEB: - case CUPS_CSPACE_DEVICEC: - case CUPS_CSPACE_DEVICED: - case CUPS_CSPACE_DEVICEE: - case CUPS_CSPACE_DEVICEF: - // For right now, DeviceN will use /DeviceCMYK in the PDF - dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceCMYK"); - break; - case CUPS_CSPACE_K: - dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceGray"); - break; - case CUPS_CSPACE_SW: - if (use_blackpoint) - dict["/ColorSpace"]=get_cal_gray_array(cfCmWhitePointSGray(), - cfCmGammaSGray(), - cfCmBlackPointDefault()); - else - dict["/ColorSpace"]=get_cal_gray_array(cfCmWhitePointSGray(), - cfCmGammaSGray(), 0); - break; - case CUPS_CSPACE_CMYK: - dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceCMYK"); - break; - case CUPS_CSPACE_RGB: - dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceRGB"); - break; - case CUPS_CSPACE_SRGB: - icc_ref = embed_srgb_profile(pdf, doc); - if (!icc_ref.isNull()) - dict["/ColorSpace"]=icc_ref; - else - dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceRGB"); - break; - case CUPS_CSPACE_ADOBERGB: - if (use_blackpoint) - dict["/ColorSpace"]=get_cal_rgb_array(cfCmWhitePointAdobeRGB(), - cfCmGammaAdobeRGB(), - cfCmMatrixAdobeRGB(), - cfCmBlackPointDefault()); - else - dict["/ColorSpace"]=get_cal_rgb_array(cfCmWhitePointAdobeRGB(), - cfCmGammaAdobeRGB(), - cfCmMatrixAdobeRGB(), 0); - break; - default: - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Color space not supported."); - return (QPDFObjectHandle()); - } - } - else if (doc->cm_disabled) - { - switch(cs) - { - case CUPS_CSPACE_K: - case CUPS_CSPACE_SW: - dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceGray"); - break; - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_SRGB: - case CUPS_CSPACE_ADOBERGB: - dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceRGB"); - break; - case CUPS_CSPACE_DEVICE1: - case CUPS_CSPACE_DEVICE2: - case CUPS_CSPACE_DEVICE3: - case CUPS_CSPACE_DEVICE4: - case CUPS_CSPACE_DEVICE5: - case CUPS_CSPACE_DEVICE6: - case CUPS_CSPACE_DEVICE7: - case CUPS_CSPACE_DEVICE8: - case CUPS_CSPACE_DEVICE9: - case CUPS_CSPACE_DEVICEA: - case CUPS_CSPACE_DEVICEB: - case CUPS_CSPACE_DEVICEC: - case CUPS_CSPACE_DEVICED: - case CUPS_CSPACE_DEVICEE: - case CUPS_CSPACE_DEVICEF: - case CUPS_CSPACE_CMYK: - dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceCMYK"); - break; - default: - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Color space not supported."); - return (QPDFObjectHandle()); - } - } - else - return (QPDFObjectHandle()); - - ret.replaceDict(QPDFObjectHandle::newDictionary(dict)); - -#ifdef PRE_COMPRESS - // we deliver already compressed content (instead of letting QPDFWriter - // do it), to avoid using excessive memory - Pl_Buffer psink("psink"); - Pl_Flate pflate("pflate", &psink, Pl_Flate::a_deflate); - - pflate.write(page_data->getBuffer(), page_data->getSize()); - pflate.finish(); - - ret.replaceStreamData(std::shared_ptr(psink.getBuffer()), - QPDFObjectHandle::newName("/FlateDecode"), - QPDFObjectHandle::newNull()); -#else - ret.replaceStreamData(page_data, QPDFObjectHandle::newNull(), - QPDFObjectHandle::newNull()); -#endif - - return (ret); -} - - -static int -finish_page(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) - { - // Finish previous PDF Page - if (!info->page_data.get()) - return (0); - - QPDFObjectHandle image = make_image(info->pdf, info->page_data, - info->width, info->height, - info->render_intent, - info->color_space, info->bpc, doc); - if (!image.isInitialized()) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to load image data"); - return (1); - } - - // add it - info->page.getKey("/Resources").getKey("/XObject").replaceKey("/I",image); - } - else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - // Finish previous PCLm page - if (info->pclm_num_strips == 0) - return (0); - - for (size_t i = 0; i < info->pclm_strip_data.size(); i ++) - if (!info->pclm_strip_data[i].get()) - return (0); - - std::vector strips = - make_pclm_strips(info->pdf, info->pclm_num_strips, info->pclm_strip_data, - info->pclm_compression_method_preferred, info->width, - info->pclm_strip_height, info->color_space, info->bpc, - doc); - for (size_t i = 0; i < info->pclm_num_strips; i ++) - if (!strips[i].isInitialized()) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to load strip data"); - return (1); - } - - // Add it - for (size_t i = 0; i < info->pclm_num_strips; i ++) - info->page.getKey("/Resources").getKey("/XObject") - .replaceKey("/Image" + - int_to_fwstring(i, num_digits(info->pclm_num_strips - 1)), - strips[i]); - } - - // Draw it - std::string content; - if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) - { - content.append(QUtil::double_to_string(info->page_width) + " 0 0 " + - QUtil::double_to_string(info->page_height) + " 0 0 cm\n"); - content.append("/I Do\n"); - } - else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - std::string res = info->pclm_source_resolution_default; - - // resolution is in dpi, so remove the last three characters from - // resolution string to get resolution integer - unsigned resolution_integer = std::stoi(res.substr(0, res.size() - 3)); - double d = (double)DEFAULT_PDF_UNIT / resolution_integer; - content.append(QUtil::double_to_string(d) + " 0 0 " + - QUtil::double_to_string(d) + " 0 0 cm\n"); - unsigned yAnchor = info->height; - for (unsigned i = 0; i < info->pclm_num_strips; i ++) - { - yAnchor -= info->pclm_strip_height[i]; - content.append("/P <> BDC q\n"); - content.append(QUtil::int_to_string(info->width) + " 0 0 " + - QUtil::int_to_string(info->pclm_strip_height[i]) + - " 0 " + QUtil::int_to_string(yAnchor) + " cm\n"); - content.append("/Image" + - int_to_fwstring(i, - num_digits(info->pclm_num_strips - 1)) + - " Do Q\n"); - } - } - - QPDFObjectHandle page_contents = info->page.getKey("/Contents"); - if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) - page_contents.replaceStreamData(content, QPDFObjectHandle::newNull(), - QPDFObjectHandle::newNull()); - else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - page_contents.getArrayItem(0).replaceStreamData(content, - QPDFObjectHandle::newNull(), - QPDFObjectHandle::newNull()); - - // bookkeeping - info->page_data = std::shared_ptr(); - info->pclm_strip_data.clear(); - - return (0); -} - - -// -// Perform modifications to PDF if color space conversions are needed -// - -static int -prepare_pdf_page(struct pdf_info *info, - unsigned width, - unsigned height, - unsigned bpl, - unsigned bpp, - unsigned bpc, - std::string render_intent, - cups_cspace_t color_space, - pwgtopdf_doc_t *doc) -{ - -#define IMAGE_CMYK_8 (bpp == 32 && bpc == 8) -#define IMAGE_CMYK_16 (bpp == 64 && bpc == 16) -#define IMAGE_RGB_8 (bpp == 24 && bpc == 8) -#define IMAGE_RGB_16 (bpp == 48 && bpc == 16) -#define IMAGE_WHITE_1 (bpp == 1 && bpc == 1) -#define IMAGE_WHITE_8 (bpp == 8 && bpc == 8) -#define IMAGE_WHITE_16 (bpp == 16 && bpc == 16) - - int error = 0; - pdf_convert_function fn = convert_pdf_no_conversion; - cmsColorSpaceSignature css; - - // Register available raster information into the PDF - info->width = width; - info->height = height; - info->line_bytes = bpl; - info->bpp = bpp; - info->bpc = bpc; - info->render_intent = render_intent; - info->color_space = color_space; - if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - info->pclm_num_strips = - (height / info->pclm_strip_height_preferred) + - (height % info->pclm_strip_height_preferred ? 1 : 0); - info->pclm_strip_height.resize(info->pclm_num_strips); - info->pclm_strip_data.resize(info->pclm_num_strips); - for (size_t i = 0; i < info->pclm_num_strips; i ++) - { - info->pclm_strip_height[i] = - info->pclm_strip_height_preferred < height ? - info->pclm_strip_height_preferred : height; - height -= info->pclm_strip_height[i]; - } - } - - // Invert grayscale by default - if (color_space == CUPS_CSPACE_K) - fn = convert_pdf_invert_colors; - - if (doc->colorProfile != NULL) - { - css = cmsGetColorSpace(doc->colorProfile); - - // Convert image and PDF color space to an embedded ICC Profile color - // space - switch (css) - { - // Convert PDF to Grayscale when using a gray profile - case cmsSigGrayData: - if (color_space == CUPS_CSPACE_CMYK) - fn = convert_pdf_cmyk_8_to_white_8; - else if (color_space == CUPS_CSPACE_RGB) - fn = convert_pdf_rgb_8_to_white_8; - else - fn = convert_pdf_invert_colors; - info->color_space = CUPS_CSPACE_K; - break; - // Convert PDF to RGB when using an RGB profile - case cmsSigRgbData: - if (color_space == CUPS_CSPACE_CMYK) - fn = convert_pdf_cmyk_8_to_rgb_8; - else if (color_space == CUPS_CSPACE_K) - fn = convert_pdf_white_8_to_rgb_8; - info->color_space = CUPS_CSPACE_RGB; - break; - // Convert PDF to CMYK when using an RGB profile - case cmsSigCmykData: - if (color_space == CUPS_CSPACE_RGB) - fn = convert_pdf_rgb_8_to_cmyk_8; - else if (color_space == CUPS_CSPACE_K) - fn = convert_pdf_white_8_to_cmyk_8; - info->color_space = CUPS_CSPACE_CMYK; - break; - default: - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to convert PDF from profile."); - doc->colorProfile = NULL; - error = 1; - } - } - else if (!doc->cm_disabled) - { - // Perform conversion of an image color space - switch (color_space) - { - // Convert image to CMYK - case CUPS_CSPACE_CMYK: - if (IMAGE_RGB_8) - fn = convert_pdf_rgb_8_to_cmyk_8; - else if (IMAGE_RGB_16) - fn = convert_pdf_no_conversion; - else if (IMAGE_WHITE_8) - fn = convert_pdf_white_8_to_cmyk_8; - else if (IMAGE_WHITE_16) - fn = convert_pdf_no_conversion; - break; - // Convert image to RGB - case CUPS_CSPACE_ADOBERGB: - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_SRGB: - if (IMAGE_CMYK_8) - fn = convert_pdf_cmyk_8_to_rgb_8; - else if (IMAGE_CMYK_16) - fn = convert_pdf_no_conversion; - else if (IMAGE_WHITE_8) - fn = convert_pdf_white_8_to_rgb_8; - else if (IMAGE_WHITE_16) - fn = convert_pdf_no_conversion; - break; - // Convert image to Grayscale - case CUPS_CSPACE_SW: - case CUPS_CSPACE_K: - if (IMAGE_CMYK_8) - fn = convert_pdf_cmyk_8_to_white_8; - else if (IMAGE_CMYK_16) - fn = convert_pdf_no_conversion; - else if (IMAGE_RGB_8) - fn = convert_pdf_rgb_8_to_white_8; - else if (IMAGE_RGB_16) - fn = convert_pdf_no_conversion; - break; - case CUPS_CSPACE_DEVICE1: - case CUPS_CSPACE_DEVICE2: - case CUPS_CSPACE_DEVICE3: - case CUPS_CSPACE_DEVICE4: - case CUPS_CSPACE_DEVICE5: - case CUPS_CSPACE_DEVICE6: - case CUPS_CSPACE_DEVICE7: - case CUPS_CSPACE_DEVICE8: - case CUPS_CSPACE_DEVICE9: - case CUPS_CSPACE_DEVICEA: - case CUPS_CSPACE_DEVICEB: - case CUPS_CSPACE_DEVICEC: - case CUPS_CSPACE_DEVICED: - case CUPS_CSPACE_DEVICEE: - case CUPS_CSPACE_DEVICEF: - // No conversion for right now - fn = convert_pdf_no_conversion; - break; - default: - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Color space not supported."); - error = 1; - break; - } - } - - if (!error) - fn(info, doc); - - return (error); -} - - -static int -add_pdf_page(struct pdf_info *info, - int pagen, - unsigned width, - unsigned height, - int bpp, - int bpc, - int bpl, - std::string render_intent, - cups_cspace_t color_space, - unsigned xdpi, - unsigned ydpi, - pwgtopdf_doc_t *doc) -{ - try - { - if (finish_page(info, doc)) // any active - return (1); - - prepare_pdf_page(info, width, height, bpl, bpp, - bpc, render_intent, color_space, doc); - - if (info->height > (std::numeric_limits::max() / - info->line_bytes)) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Page too big"); - return (1); - } - if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) - info->page_data = - std::shared_ptr(new Buffer(info->line_bytes * info->height)); - else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - // reserve space for PCLm strips - for (size_t i = 0; i < info->pclm_num_strips; i ++) - info->pclm_strip_data[i] = - std::shared_ptr(new Buffer(info->line_bytes * - info->pclm_strip_height[i])); - } - - QPDFObjectHandle page = QPDFObjectHandle::parse( - "<<" - " /Type /Page" - " /Resources <<" - " /XObject << >> " - " >>" - " /MediaBox null " - " /Contents null " - ">>"); - - // Convert to pdf units - info->page_width = ((double)info->width / xdpi) * DEFAULT_PDF_UNIT; - info->page_height = ((double)info->height / ydpi) * DEFAULT_PDF_UNIT; - if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) - { - page.replaceKey("/Contents", QPDFObjectHandle::newStream(&info->pdf)); - // data will be provided later - page.replaceKey("/MediaBox", make_real_box(0, 0, info->page_width, - info->page_height)); - } - else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - page.replaceKey("/Contents", - QPDFObjectHandle::newArray(std::vector - (1, QPDFObjectHandle::newStream - (&info->pdf)))); - - // box with dimensions rounded off to the nearest integer - page.replaceKey("/MediaBox", - make_integer_box(0, 0, info->page_width + 0.5, - info->page_height + 0.5)); - } - - info->page = info->pdf.makeIndirectObject(page); // we want to keep a - // reference - info->pdf.addPage(info->page, false); - } - catch (std::bad_alloc &ex) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to allocate page data"); - return (1); - } - catch (...) - { - return (1); - } - - return (0); -} - - -static int -close_pdf_file(struct pdf_info * info, - pwgtopdf_doc_t *doc) -{ - try - { - if (finish_page(info, doc)) // any active - return (1); - QPDFWriter output(info->pdf, NULL); - output.setOutputFile("pdf", doc->outputfp, false); - //output.setMinimumPDFVersion("1.4"); - if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - output.setPCLm(true); - output.write(); - } - catch (...) - { - return (1); - } - - return (0); -} - - -static void -pdf_set_line(struct pdf_info * info, - unsigned line_n, - unsigned char *line, - pwgtopdf_doc_t *doc) -{ - if (line_n > info->height) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Bad line %d", line_n); - return; - } - - if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - // copy line data into appropriate pclm strip - size_t strip_num = line_n / info->pclm_strip_height_preferred; - unsigned line_strip = line_n - - strip_num * info->pclm_strip_height_preferred; - memcpy(((info->pclm_strip_data[strip_num])->getBuffer() + - (line_strip*info->line_bytes)), line, info->line_bytes); - } - else - memcpy((info->page_data->getBuffer() + (line_n * info->line_bytes)), - line, info->line_bytes); -} - - -static int -convert_raster(cups_raster_t *ras, - unsigned width, - unsigned height, - int bpp, - int bpl, - struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - // We should be at raster start - int i; - unsigned cur_line = 0; - unsigned char *PixelBuffer, *ptr = NULL, *buff; - - PixelBuffer = (unsigned char *)malloc(bpl); - buff = (unsigned char *)malloc(info->line_bytes); - - do - { - // Read raster data... - cupsRasterReadPixels(ras, PixelBuffer, bpl); - -#if !ARCH_IS_BIG_ENDIAN - if (info->bpc == 16) - { - // Swap byte pairs for endianess (cupsRasterReadPixels() switches - // from Big Endian back to the system's Endian) - for (i = bpl, ptr = PixelBuffer; i > 0; i -= 2, ptr += 2) - { - unsigned char swap = *ptr; - *ptr = *(ptr + 1); - *(ptr + 1) = swap; - } - } -#endif // !ARCH_IS_BIG_ENDIAN - - // perform bit operations if necessary - doc->bit_function(PixelBuffer, ptr, bpl); - - // write lines and color convert when necessary - pdf_set_line(info, cur_line, doc->conversion_function(PixelBuffer, - buff, width), - doc); - ++cur_line; - } - while (cur_line < height); - - free(buff); - free(PixelBuffer); - - return (0); -} - - -static int -set_profile(const char *path, - pwgtopdf_doc_t *doc) -{ - if (path != NULL) - doc->colorProfile = cmsOpenProfileFromFile(path, "r"); - - if (doc->colorProfile != NULL) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Load profile successful."); - return (0); - } - else - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to load profile."); - return (1); - } -} - - -int // O - Error status -cfFilterPWGToPDF(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? (unused) - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters (outformat) -{ - cups_len_t i; - char *t; - pwgtopdf_doc_t doc; // Document information - FILE *outputfp; // Output data stream - cf_filter_out_format_t outformat; // Output format - int Page, empty = 1; - cf_cm_calibration_t cm_calibrate; // Status of CUPS color management - // ("on" or "off") - struct pdf_info pdf; - cups_raster_t *ras; // Raster stream for printing - cups_page_header_t header; // Page header from file - ipp_t *printer_attrs = data->printer_attrs; // Printer attributes from - // printer data - ipp_attribute_t *ipp_attr; // Printer attribute - const char *profile_name = NULL; // IPP Profile Name - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - int total_attrs; - char buf[1024]; - const char *kw; - - - (void)inputseekable; - - if (parameters) - { - outformat = *(cf_filter_out_format_t *)parameters; - if (outformat != CF_FILTER_OUT_FORMAT_PCLM) - outformat = CF_FILTER_OUT_FORMAT_PDF; - } - else - { - t = data->final_content_type; - if (t) - { - if (strcasestr(t, "pclm")) - outformat = CF_FILTER_OUT_FORMAT_PCLM; - else if (strcasestr(t, "pdf")) - outformat = CF_FILTER_OUT_FORMAT_PDF; - else - outformat = CF_FILTER_OUT_FORMAT_PDF; - } - else - outformat = CF_FILTER_OUT_FORMAT_PDF; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: OUTFORMAT=\"%s\"", - outformat == CF_FILTER_OUT_FORMAT_PDF ? "PDF" : "PCLM"); - - // - // Open the output data stream specified by the outputfd... - // - - if ((outputfp = fdopen(outputfd, "w")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to open output data stream."); - } - - return (1); - } - - doc.outputfp = outputfp; - // Logging function - doc.logfunc = log; - doc.logdata = ld; - // Job-is-canceled function - doc.iscanceledfunc = iscanceled; - doc.iscanceleddata = icd; - - // support the CUPS "cm-calibration" option - cm_calibrate = cfCmGetCupsColorCalibrateMode(data); - - if (outformat == CF_FILTER_OUT_FORMAT_PCLM || - cm_calibrate == CF_CM_CALIBRATION_ENABLED) - doc.cm_disabled = 1; - else - doc.cm_disabled = cfCmIsPrinterCmDisabled(data); - - if (outformat == CF_FILTER_OUT_FORMAT_PCLM && printer_attrs == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToPDF: PCLm output: No printer IPP attributes are supplied, PCLm output not possible."); - return (1); - } - - // Transform - ras = cupsRasterOpen(inputfd, CUPS_RASTER_READ); - - // Process pages as needed... - Page = 0; - - // Get PCLm parameters from printer IPP attributes - if (outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - if (log) - { - log(ld, CF_LOGLEVEL_DEBUG, "PCLm-related printer IPP attributes:"); - total_attrs = 0; - ipp_attr = ippGetFirstAttribute(printer_attrs); - while (ipp_attr) - { - if (strncmp(ippGetName(ipp_attr), "pclm-", 5) == 0) - { - total_attrs ++; - ippAttributeString(ipp_attr, buf, sizeof(buf)); - log(ld, CF_LOGLEVEL_DEBUG, " Attr: %s",ippGetName(ipp_attr)); - log(ld, CF_LOGLEVEL_DEBUG, " Value: %s", buf); - for (i = 0; i < ippGetCount(ipp_attr); i ++) - if ((kw = ippGetString(ipp_attr, i, NULL)) != NULL) - log(ld, CF_LOGLEVEL_DEBUG, " Keyword: %s", kw); - } - ipp_attr = ippGetNextAttribute(printer_attrs); - } - log(ld, CF_LOGLEVEL_DEBUG, " %d attributes", total_attrs); - } - - char *attr_name = (char *)"pclm-strip-height-preferred"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value %d", - attr_name, ippGetInteger(ipp_attr, 0)); - pdf.pclm_strip_height_preferred = ippGetInteger(ipp_attr, 0); - } - else - pdf.pclm_strip_height_preferred = 16; // default strip height - - attr_name = (char *)"pclm-strip-height-supported"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\"", - attr_name); - pdf.pclm_strip_height_supported.clear(); // remove default value = 16 - for (i = 0; i < ippGetCount(ipp_attr); i ++) - pdf.pclm_strip_height_supported.push_back(ippGetInteger(ipp_attr, i)); - } - - attr_name = (char *)"pclm-raster-back-side"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", - attr_name, ippGetString(ipp_attr, 0, NULL)); - pdf.pclm_raster_back_side = ippGetString(ipp_attr, 0, NULL); - } - - attr_name = (char *)"pclm-source-resolution-supported"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - ippAttributeString(ipp_attr, buf, sizeof(buf)); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", - attr_name, buf); - pdf.pclm_source_resolution_supported = split_strings(buf, ","); - } - - attr_name = (char *)"pclm-source-resolution-default"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - ippAttributeString(ipp_attr, buf, sizeof(buf)); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", - attr_name, buf); - pdf.pclm_source_resolution_default = buf; - } - else if (pdf.pclm_source_resolution_supported.size() > 0) - { - pdf.pclm_source_resolution_default = - pdf.pclm_source_resolution_supported[0]; - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" missing, taking first item of \"pclm-source-resolution-supported\" as default resolution", - attr_name); - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToPDF: PCLm output: Printer IPP attributes do not contain printer resolution information for PCLm."); - return (1); - } - - attr_name = (char *)"pclm-compression-method-preferred"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - ippAttributeString(ipp_attr, buf, sizeof(buf)); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", - attr_name, buf); - std::vector vec = split_strings(buf, ","); - - // get all compression methods supported by the printer - for (std::vector::iterator it = vec.begin(); - it != vec.end(); ++it) - { - std::string compression_method = *it; - for (char& x: compression_method) - x = tolower(x); - if (compression_method == "flate") - pdf.pclm_compression_method_preferred.push_back(FLATE_DECODE); - else if (compression_method == "rle") - pdf.pclm_compression_method_preferred.push_back(RLE_DECODE); - else if (compression_method == "jpeg") - pdf.pclm_compression_method_preferred.push_back(DCT_DECODE); - } - } - // If the compression methods is none of the above or is erreneous - // use FLATE as compression method and show a warning. - if (pdf.pclm_compression_method_preferred.empty()) - { - if (log) log(ld, CF_LOGLEVEL_WARN, - "(pwgtopclm) Unable parse Printer attribute \"%s\". " - "Using FLATE for encoding image streams.", attr_name); - pdf.pclm_compression_method_preferred.push_back(FLATE_DECODE); - } - } - - while (cupsRasterReadHeader(ras, &header)) - { - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Job canceled"); - break; - } - - if (empty) - { - empty = 0; - // We have a valid input page, so create PDF file - if (create_pdf_file(&pdf, outformat) != 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToPDF: Unable to create PDF file"); - return (1); - } - } - - // Write a status message with the page number - Page ++; - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterPWGToPDF: Starting page %d.", Page); - - // Update rendering intent with user settings or the default - cfGetPrintRenderIntent(data, header.cupsRenderingIntent, - sizeof(header.cupsRenderingIntent)); - - // Use "profile=profile_name.icc" to embed 'profile_name.icc' into the PDF - // for testing. Forces color management to enable. - if (outformat == CF_FILTER_OUT_FORMAT_PDF && - (profile_name = cupsGetOption("profile", data->num_options, - data->options)) != NULL) - { - set_profile(profile_name, &doc); - doc.cm_disabled = 0; - } - if (doc.colorProfile != NULL) - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: TEST ICC Profile specified (color " - "management forced ON): \n[%s]", profile_name); - - // Add a new page to PDF file - if (add_pdf_page(&pdf, Page, header.cupsWidth, header.cupsHeight, - header.cupsBitsPerPixel, header.cupsBitsPerColor, - header.cupsBytesPerLine, header.cupsRenderingIntent, - header.cupsColorSpace, header.HWResolution[0], - header.HWResolution[1], &doc) != 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToPDF: Unable to start new PDF page"); - return (1); - } - - // Write the bit map into the PDF file - if (convert_raster(ras, header.cupsWidth, header.cupsHeight, - header.cupsBitsPerPixel, header.cupsBytesPerLine, - &pdf, &doc) != 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToPDF: Failed to convert page bitmap"); - return (1); - } - } - - if (empty) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Input is empty, outputting empty file."); - cupsRasterClose(ras); - return (0); - } - - close_pdf_file(&pdf, &doc); // output to outputfp - - if (doc.colorProfile != NULL) - cmsCloseProfile(doc.colorProfile); - - cupsRasterClose(ras); - fclose(outputfp); - - return (Page == 0); -} From d5a608d2fc4994874dfdf69597bdaa92937289dd Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Mon, 18 Nov 2024 18:02:26 +0530 Subject: [PATCH 54/64] removed the C- suffix from the ported C files --- cupsfilters/pclmtoraster.c | 1254 ++++++++++++ cupsfilters/pdf.c | 371 ++++ cupsfilters/pdf.h | 63 + cupsfilters/pdftopdf/intervalset-private.h | 44 + cupsfilters/pdftopdf/intervalset.c | 180 ++ cupsfilters/pdftopdf/nup-private.h | 55 + cupsfilters/pdftopdf/nup.c | 336 +++ cupsfilters/pdftopdf/pdftopdf-private.h | 25 + .../pdftopdf/pdftopdf-processor-private.h | 113 ++ cupsfilters/pdftopdf/pdftopdf-processor.c | 583 ++++++ cupsfilters/pdftopdf/pdftopdf.c | 964 +++++++++ cupsfilters/pdftopdf/pptypes-private.h | 80 + cupsfilters/pdftopdf/pptypes.c | 279 +++ cupsfilters/pwgtopdf.c | 1801 +++++++++++++++++ 14 files changed, 6148 insertions(+) create mode 100644 cupsfilters/pclmtoraster.c create mode 100644 cupsfilters/pdf.c create mode 100644 cupsfilters/pdf.h create mode 100644 cupsfilters/pdftopdf/intervalset-private.h create mode 100644 cupsfilters/pdftopdf/intervalset.c create mode 100644 cupsfilters/pdftopdf/nup-private.h create mode 100644 cupsfilters/pdftopdf/nup.c create mode 100644 cupsfilters/pdftopdf/pdftopdf-private.h create mode 100644 cupsfilters/pdftopdf/pdftopdf-processor-private.h create mode 100644 cupsfilters/pdftopdf/pdftopdf-processor.c create mode 100644 cupsfilters/pdftopdf/pdftopdf.c create mode 100644 cupsfilters/pdftopdf/pptypes-private.h create mode 100644 cupsfilters/pdftopdf/pptypes.c create mode 100644 cupsfilters/pwgtopdf.c diff --git a/cupsfilters/pclmtoraster.c b/cupsfilters/pclmtoraster.c new file mode 100644 index 00000000..d776d406 --- /dev/null +++ b/cupsfilters/pclmtoraster.c @@ -0,0 +1,1254 @@ +// +// PCLm/Raster-only PDF to Raster filter function for libcupsfilters. +// +// Copyright © 2020 by Vikrant Malik +// Copyright 2024 Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +// +// Include necessary headers... +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#define MAX_BYTES_PER_PIXEL 32 + +typedef struct pclmtoraster_data_s { + int outformat; + int numcolors; + int rowsize; + cups_page_header_t header; + char pageSizeRequested[64]; + int bi_level; + // image swapping + int swap_image_x; + int swap_image_y; + int swap_margin_x; + int swap_margin_y; + unsigned int nplanes; + unsigned int nbands; + unsigned int bytesPerLine; + char colorspace[32]; // Use fixed-size string +} pclmtoraster_data_t; + +void +init_pclmtoraster_data_t(pclmtoraster_data_t *data) +{ + data->outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; + data->numcolors = 0; + data->rowsize = 0; + data->bi_level = 0; + // image swapping + data->swap_image_x = false; + data->swap_image_y = false; + // margin swapping + data->swap_margin_x = false; + data->swap_margin_y = false; + // Note: When CUPS_ORDER_BANDED, + // cupsBytesPerLine = bytesPerLine * cupsNumColors + strncpy(data->colorspace, "\0", sizeof(data->colorspace)); +} + +typedef unsigned char *(*convert_cspace_func)(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data); + +typedef unsigned char *(*convert_line_func)(unsigned char *src, + unsigned char *dst, + unsigned char *buf, + unsigned int row, + unsigned int plane, + pclmtoraster_data_t *data, + convert_cspace_func convertcspace); + +typedef struct pclm_conversion_function_s +{ + convert_cspace_func convertcspace;// Function for conversion of colorspaces + convert_line_func convertline; // Function tom modify raster data of a line +} pclm_conversion_function_t; + +static int +parse_opts(cf_filter_data_t *data, + cf_filter_out_format_t outformat, + pclmtoraster_data_t *pclmtoraster_data) +{ + int num_options = 0; + cups_option_t* options = NULL; + const char* t = NULL; + const char *val; + cf_logfunc_t log = data->logfunc; + void *ld = data->logdata; + cups_page_header_t *header = &(pclmtoraster_data->header); + cups_cspace_t cspace = (cups_cspace_t)(-1); + + pclmtoraster_data->outformat = outformat; + + // + // CUPS option list + // + + num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); + + t = cupsGetOption("media-class", num_options, options); + if (t == NULL) + t = cupsGetOption("MediaClass", num_options, options); + if (t != NULL) + { + if (strcasestr(t, "pwg")) + pclmtoraster_data->outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; + } + + cfRasterPrepareHeader(header, data, outformat, outformat, 0, &cspace); + + if(header->Duplex) + { + int backside; + // analyze options relevant to Duplex + // APDuplexRequiresFlippedMargin + enum + { + FM_NO, + FM_FALSE, + FM_TRUE + } flippedMargin = FM_NO; + + backside = cfGetBackSideOrientation(data); + + if (backside >= 0) + { + flippedMargin = (backside & 16 ? FM_TRUE : + (backside & 8 ? FM_FALSE : + FM_NO)); + backside &= 7; + + if(backside == CF_BACKSIDE_MANUAL_TUMBLE && header->Tumble) + { + pclmtoraster_data->swap_image_x = true; + pclmtoraster_data->swap_image_y = true; + pclmtoraster_data->swap_margin_x = true; + pclmtoraster_data->swap_margin_y = true; + if (flippedMargin == FM_TRUE) + pclmtoraster_data->swap_margin_y = false; + } + else if(backside == CF_BACKSIDE_ROTATED && !header->Tumble) + { + pclmtoraster_data->swap_image_x = true; + pclmtoraster_data->swap_image_y = true; + pclmtoraster_data->swap_margin_x = true; + pclmtoraster_data->swap_margin_y = true; + if (flippedMargin == FM_TRUE) + pclmtoraster_data->swap_margin_y = false; + } + else if (backside == CF_BACKSIDE_FLIPPED) + { + if (header->Tumble) + { + pclmtoraster_data->swap_image_x = true; + pclmtoraster_data->swap_margin_x = true; + pclmtoraster_data->swap_margin_y = true; + } + else + pclmtoraster_data->swap_image_y = true; + + if (flippedMargin == FM_FALSE) + pclmtoraster_data->swap_margin_y = !pclmtoraster_data->swap_margin_y; + } + } + } + + if ((val = cupsGetOption("print-color-mode", num_options, options)) != NULL && + !strncasecmp(val, "bi-level", 8)) + pclmtoraster_data->bi_level = 1; + + strncpy(pclmtoraster_data->pageSizeRequested, header->cupsPageSizeName, 63); + pclmtoraster_data->pageSizeRequested[63] = '\0'; + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPCLmToRaster: Page size requested: %s.", + header->cupsPageSizeName); + return 0; +} + +static bool +media_box_lookup(pdfio_obj_t *object, + float rect[4]) +{ + pdfio_rect_t mediaBox; + pdfio_dict_t *object_dict = pdfioObjGetDict(object); + if(pdfioDictGetRect(object_dict, "MediaBox", &mediaBox)) + return false; + + pdfioDictGetRect(object_dict, "MediaBox", &mediaBox); + + rect[0] = mediaBox.x1; + rect[1] = mediaBox.y1; + rect[2] = mediaBox.x2; + rect[3] = mediaBox.y2; + + return true; +} + +// +// 'rotate_bitmap()' - Function to rotate a bitmap +// (assumed that bits-per-component of the bitmap is 8). +// + +static unsigned char * // O - Output Bitmap +rotate_bitmap(unsigned char *src, // I - Input string + unsigned char *dst, // O - Destination string + unsigned int rotate, // I - Rotate value (0, 90, 180, 270) + unsigned int height, // I - Height of raster image in pixels + unsigned int width, // I - Width of raster image in pixels + int rowsize, // I - Length of one row of pixels + char* colorspace,// I - Colorspace of input bitmap + cf_logfunc_t log, // I - Log function + void *ld) // I - Aux. data for log function +{ + unsigned char *bp = src; + unsigned char *dp = dst; + unsigned char *temp = dst; + + if(rotate == 0) + return (src); + else if(rotate == 180) + { + if (strcmp(colorspace, "/DeviceGray") == 0) + { + bp = src + height * rowsize - 1; + dp = dst; + for (unsigned int h = 0; h < height; h++) + for (unsigned int w = 0; w < width; w++, bp--, dp++) + *dp = *bp; + } + else if (strcmp(colorspace, "/DeviceCMYK") == 0) + { + bp = src + height * rowsize - 4; + dp = dst; + for (unsigned int h = 0; h < height; h ++) + { + for (unsigned int w = 0; w < width; w ++, bp -= 4, dp += 4) + { + dp[0] = bp[0]; + dp[1] = bp[1]; + dp[2] = bp[2]; + dp[3] = bp[3]; + } + } + } + else if (strcmp(colorspace, "/DeviceRGB") == 0) + { + bp = src + height * rowsize - 3; + dp = dst; + for (unsigned int h = 0; h < height; h ++) + { + for (unsigned int w = 0; w < width; w ++, bp -= 3, dp += 3) + { + dp[0] = bp[0]; + dp[1] = bp[1]; + dp[2] = bp[2]; + } + } + } + } + else if (rotate == 270) + { + if (strcmp(colorspace, "/DeviceGray") == 0) + { + bp = src; + dp = dst; + for (unsigned int h = 0; h < height; h ++) + { + bp = src + (height - h) - 1; + for (unsigned int w = 0; w < width; w ++, bp += height , dp ++) + *dp = *bp; + } + } + else if (strcmp(colorspace, "/DeviceCMYK") == 0) + { + for (unsigned int h = 0; h < height; h++) + { + bp = src + (height - h) * 4 - 4; + for (unsigned int i = 0; i < width; i ++, bp += height * 4 , dp += 4) + { + dp[0] = bp[0]; + dp[1] = bp[1]; + dp[2] = bp[2]; + dp[3] = bp[3]; + } + } + } + else if (strcmp(colorspace, "/DeviceRGB") == 0) + { + bp = src; + dp = dst; + for (unsigned int h = 0; h < height; h ++) + { + bp = src + (height - h) * 3 - 3; + for (unsigned int i = 0; i < width; i ++, bp += height * 3 , dp += 3) + { + dp[0] = bp[0]; + dp[1] = bp[1]; + dp[2] = bp[2]; + } + } + } + } + else if (rotate == 90) + { + if (strcmp(colorspace, "/DeviceGray") == 0) + { + for (unsigned int h = 0; h < height; h ++) + { + bp = src + (width - 1) * height + h; + for (unsigned int i = 0; i < width; i ++, bp -= height , dp ++) + *dp = *bp; + } + } + else if (strcmp(colorspace, "/DeviceCMYK") == 0) + { + for (unsigned int h = 0; h < height; h ++) + { + bp = src + (width - 1) * height * 4 + 4 * h; + for (unsigned int i = 0; i < width; i ++, bp -= height * 4 , dp += 4) + { + dp[0] = bp[0]; + dp[1] = bp[1]; + dp[2] = bp[2]; + dp[3] = bp[3]; + } + } + } + else if (strcmp(colorspace, "/DeviceRGB") == 0) + { + for (unsigned int h = 0; h < height; h ++) + { + bp = src + (width - 1) * height * 3 + 3 * h; + for (unsigned int i = 0; i < width; i ++, bp -= height * 3 , dp += 3) + { + dp[0] = bp[0]; + dp[1] = bp[1]; + dp[2] = bp[2]; + } + } + } + } + else + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPCLmToRaster: Incorrect Rotate Value %d, not rotating", + rotate); + return (src); + } + + return (temp); +} + +static unsigned char * +rgb_to_rgbw_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + cfImageRGBToCMYK(src, dst, pixels); + for (unsigned int i = 0; i < 4 * pixels; i ++) + dst[i] = ~dst[i]; + return (dst); +} + +static unsigned char * +rgb_to_cmyk_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + cfImageRGBToCMYK(src, dst, pixels); + return (dst); +} + + +static unsigned char * +rgb_to_cmy_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + cfImageRGBToCMY(src, dst, pixels); + return (dst); +} + + +static unsigned char * +rgb_to_white_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + if (data->header.cupsBitsPerColor != 1) + cfImageRGBToWhite(src, dst, pixels); + else + { + cfImageRGBToWhite(src, src, pixels); + cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); + } + return (dst); +} + + +static unsigned char * +rgb_to_black_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + if (data->header.cupsBitsPerColor != 1) + cfImageRGBToBlack(src, dst, pixels); + else + { + cfImageRGBToBlack(src, src, pixels); + cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); + } + return (dst); +} + + +static unsigned char * +cmyk_to_rgb_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + cfImageCMYKToRGB(src, dst, pixels); + return (dst); +} + + +static unsigned char * +cmyk_to_rgbw_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + for (unsigned int i = 0; i < 4 * pixels; i ++) + dst[i] = ~src[i]; + return (dst); +} + + +static unsigned char * +cmyk_to_cmy_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + // Converted first to RGB and then to cmy for better outputs. + cfImageCMYKToRGB(src, src, pixels); + cfImageRGBToCMY(src, dst, pixels); + return (dst); +} + + +static unsigned char * +cmyk_to_white_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + if (data->header.cupsBitsPerColor != 1) + cfImageCMYKToWhite(src, dst, pixels); + else + { + cfImageCMYKToWhite(src, src, pixels); + cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); + } + return (dst); +} + + +static unsigned char * +cmyk_to_black_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + if (data->header.cupsBitsPerColor != 1) + cfImageCMYKToBlack(src, dst, pixels); + else + { + cfImageCMYKToBlack(src, src, pixels); + cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); + } + return (dst); +} + + +static unsigned char * +gray_to_rgb_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + cfImageWhiteToRGB(src, dst, pixels); + return (dst); +} + + +static unsigned char * +gray_to_rgbw_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + cfImageWhiteToCMYK(src, dst, pixels); + for (unsigned int i = 0; i < 4 * pixels; i ++) + dst[i] = ~dst[i]; + return (dst); +} + + +static unsigned char * +gray_to_cmyk_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + cfImageWhiteToCMYK(src, dst, pixels); + return (dst); +} + + +static unsigned char * +gray_to_cmy_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + cfImageWhiteToCMY(src, dst, pixels); + return (dst); +} + + +static unsigned char * +gray_to_black_line(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + if (data->header.cupsBitsPerColor != 1) + cfImageWhiteToBlack(src, dst, pixels); + else + { + cfImageWhiteToBlack(src, src, pixels); + cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); + } + return (dst); +} + + +static unsigned char * +convert_cspace_no_op(unsigned char *src, + unsigned char *dst, + unsigned int row, + unsigned int pixels, + pclmtoraster_data_t *data) +{ + return (src); +} + + +// +// 'convert_line()' - Function to convert colorspace and bits-per-pixel +// of a single line of raster data. +// + +static unsigned char * // O - Output string +convert_line(unsigned char *src, // I - Input line + unsigned char *dst, // O - Destination string + unsigned char *buf, // I - Buffer string + unsigned int row, // I - Current Row + unsigned int plane, // I - Plane/Band + pclmtoraster_data_t *data, + convert_cspace_func convertcspace) +{ + // + // Use only convertcspace if conversion of bits and conversion of color order + // is not required, or if dithering is required, for faster processing of + // raster output. + // + + unsigned int pixels = data->header.cupsWidth; + if ((data->header.cupsBitsPerColor == 1 && + data->header.cupsNumColors == 1) || + (data->header.cupsBitsPerColor == 8 && + data->header.cupsColorOrder == CUPS_ORDER_CHUNKED)) + dst = convertcspace(src, dst, row, pixels, data); + else + { + for (unsigned int i = 0; i < pixels; i ++) + { + unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; + unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; + unsigned char *pb; + pb = convertcspace(src + i * data->numcolors, pixelBuf1, row, 1, data); + pb = cfConvertBits(pb, pixelBuf2, i, row, data->header.cupsNumColors, + data->header.cupsBitsPerColor); + cfWritePixel(dst, plane, i, pb, data->header.cupsNumColors, + data->header.cupsBitsPerColor, data->header.cupsColorOrder); + } + } + return (dst); +} + + +// +// 'convert_reverse_line()' - Function to convert colorspace and bits-per-pixel +// of a single line of raster data and reverse the +// line. +// + +static unsigned char * // O - Output string +convert_reverse_line(unsigned char *src, // I - Input line + unsigned char *dst, // O - Destination + // string + unsigned char *buf, // I - Buffer string + unsigned int row, // I - Current Row + unsigned int plane, // I - Plane/Band + pclmtoraster_data_t *data, // I - pclmtoraster + // filter data + convert_cspace_func convertcspace) // I - Function for + // conversion of + // colorspace +{ + // + // Use only convertcspace if conversion of bits and conversion of color order + // is not required, or if dithering is required, for faster processing of + // raster output. + // + + unsigned int pixels = data->header.cupsWidth; + if (data->header.cupsBitsPerColor == 1 && data->header.cupsNumColors == 1) + { + buf = convertcspace(src, buf, row, pixels, data); + dst = cfReverseOneBitLine(buf, dst, pixels, data->bytesPerLine); + } + else if (data->header.cupsBitsPerColor == 8 && + data->header.cupsColorOrder == CUPS_ORDER_CHUNKED) + { + unsigned char *dp = dst; + // Assign each pixel of buf to dst in the reverse order. + buf = convertcspace(src, buf, row, pixels, data) + + (data->header.cupsWidth - 1) * data->header.cupsNumColors; + for (unsigned int i = 0; i < pixels; + i ++, buf-=data->header.cupsNumColors, dp+=data->header.cupsNumColors) + for (unsigned int j = 0; j < data->header.cupsNumColors; j ++) + dp[j] = buf[j]; + } + else + { + for (unsigned int i = 0; i < pixels; i ++) + { + unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; + unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; + unsigned char *pb; + pb = convertcspace(src + (pixels - i - 1) * (data->numcolors), pixelBuf1, + row, 1, data); + pb = cfConvertBits(pb, pixelBuf2, i, row, data->header.cupsNumColors, + data->header.cupsBitsPerColor); + cfWritePixel(dst, plane, i, pb, data->header.cupsNumColors, + data->header.cupsBitsPerColor, data->header.cupsColorOrder); + } + } + return (dst); +} + + +static void // O - Exit status +select_convert_func(int pgno, // I - Page number + cf_logfunc_t log, // I - Log function + void *ld, // I - Aux. data for log + // function + pclmtoraster_data_t *data, // I - pclmtoraster filter + // data + pclm_conversion_function_t *convert)// I - Conversion function +{ + // Set rowsize and numcolors based on colorspace of raster data + cups_page_header_t header = data->header; + char *colorspace = data->colorspace; + if (strcmp(colorspace, "/DeviceRGB") == 0) + { + data->rowsize = header.cupsWidth * 3; + data->numcolors = 3; + } + else if (strcmp(colorspace, "/DeviceCMYK") == 0) + { + data->rowsize = header.cupsWidth * 4; + data->numcolors = 4; + } + else if (strcmp(colorspace, "/DeviceGray") == 0) + { + data->rowsize = header.cupsWidth; + data->numcolors = 1; + } + else + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPCLmToRaster: Colorspace %s not supported, " + "defaulting to /deviceRGB", + colorspace); + strcpy(data->colorspace, "/DeviceRGB"); + data->rowsize = header.cupsWidth * 3; + data->numcolors = 3; + } + + convert->convertcspace = convert_cspace_no_op; //Default function + // Select convertcspace function + switch (header.cupsColorSpace) + { + case CUPS_CSPACE_K: + if (strcmp(colorspace, "/DeviceRGB") == 0) + convert->convertcspace = rgb_to_black_line; + else if (strcmp(colorspace, "/DeviceCMYK") == 0) + convert->convertcspace = cmyk_to_black_line; + else if (strcmp(colorspace, "/DeviceGray") == 0) + convert->convertcspace = gray_to_black_line; + break; + case CUPS_CSPACE_W: + case CUPS_CSPACE_SW: + if (strcmp(colorspace, "/DeviceRGB") == 0) + convert->convertcspace = rgb_to_white_line; + else if (strcmp(colorspace, "/DeviceCMYK") == 0) + convert->convertcspace = cmyk_to_white_line; + break; + case CUPS_CSPACE_CMY: + if (strcmp(colorspace, "/DeviceRGB") == 0) + convert->convertcspace = rgb_to_cmy_line; + else if (strcmp(colorspace, "/DeviceCMYK") == 0) + convert->convertcspace = cmyk_to_cmy_line; + else if (strcmp(colorspace, "/DeviceGray") == 0) + convert->convertcspace = gray_to_cmy_line; + break; + case CUPS_CSPACE_CMYK: + if (strcmp(colorspace, "/DeviceRGB") == 0) + convert->convertcspace = rgb_to_cmyk_line; + else if (strcmp(colorspace, "/DeviceGray") == 0) + convert->convertcspace = gray_to_cmyk_line; + break; + case CUPS_CSPACE_RGBW: + if (strcmp(colorspace, "/DeviceRGB") == 0) + convert->convertcspace = rgb_to_rgbw_line; + else if (strcmp(colorspace, "/DeviceCMYK") == 0) + convert->convertcspace = cmyk_to_rgbw_line; + else if (strcmp(colorspace, "/DeviceGray") == 0) + convert->convertcspace = gray_to_rgbw_line; + break; + case CUPS_CSPACE_RGB: + case CUPS_CSPACE_ADOBERGB: + case CUPS_CSPACE_SRGB: + default: + if (strcmp(colorspace, "/DeviceCMYK") == 0) + convert->convertcspace = cmyk_to_rgb_line; + else if (strcmp(colorspace, "/DeviceGray") == 0) + convert->convertcspace = gray_to_rgb_line; + break; + } + + // Select convertline function + if (header.Duplex && (pgno & 1) && data->swap_image_x) + convert->convertline = convert_reverse_line; + else + convert->convertline = convert_line; +} + +bool +process_image(pdfio_dict_t *dict, const char *key, pclmtoraster_data_t *data, int pixel_count, unsigned char *bitmap) +{ + char *buffer; + pdfio_obj_t *image = pdfioDictGetObj(dict, key); + + //... verify the object has type "Image", then do something with the image object ... + + if (strcmp(pdfioObjGetType(image), "image") == 0) + { + pdfio_dict_t *imgdict = pdfioObjGetDict(image); + if (!imgdict) + return true; + + pdfio_stream_t *img_str = pdfioObjOpenStream(image, true); + size_t bufsize = pdfioStreamRead(img_str, buffer, 1024); + + int width = pdfioDictGetNumber(imgdict, "Width"); + int height = pdfioDictGetNumber(imgdict, "Height"); + + data->header.cupsHeight += height; + + if (pixel_count == 0) + bitmap = (unsigned char *)malloc(bufsize); + + else + bitmap = (unsigned char *)realloc(bitmap, pixel_count + bufsize); + + memcpy(bitmap + pixel_count, buffer, bufsize); + pixel_count += bufsize; + + if (width > data->header.cupsWidth) + data->header.cupsWidth = width; + } + + return (true); +} + +// +// 'out_page()' - Function to convert a single page of raster-only PDF/PCLm +// input to CUPS/PWG Raster. +// + +static int // O - Exit status +out_page(cups_raster_t* raster, // I - Raster stream + pdfio_obj_t* page, // I - PDFio Page Object + int pgno, // I - Page number + cf_logfunc_t log, // I - Log function + void* ld, // I - Aux. data for log function + pclmtoraster_data_t *data, // I - pclmtoraster filter data + cf_filter_data_t *filter_data, // I - filter data + pclm_conversion_function_t *convert)// I - Conversion functions +{ + int i; + long long rotate = 0; + float paperdimensions[2], margins[4], l, swap; + int pixel_count = 0, temp = 0; + float mediaBox[4]; + unsigned char *bitmap = NULL, + *colordata = NULL, + *lineBuf = NULL, + *line = NULL, + *dp = NULL; + pdfio_obj_t *colorspace_obj; + + + // Check if page is rotated. + pdfio_dict_t *pageDict = pdfioObjGetDict(page); + if(pdfioDictGetNumber(pageDict, "Rotate")) + { + rotate = pdfioDictGetNumber(pageDict, "Rotate"); + } + + // Get pagesize by the mediabox key of the page. + if (!media_box_lookup(page, mediaBox)) + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPCLmToRaster: PDF page %d doesn't contain a valid mediaBox", + pgno + 1); + return (1); + } + else + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPCLmToRaster: mediaBox = [%f %f %f %f]: ", + mediaBox[0], mediaBox[1], mediaBox[2], mediaBox[3]); + l = mediaBox[2] - mediaBox[0]; + if (l < 0) + l = -l; + if (rotate == 90 || rotate == 270) + data->header.PageSize[1] = (unsigned)l; + else + data->header.PageSize[0] = (unsigned)l; + l = mediaBox[3] - mediaBox[1]; + if (l < 0) + l = -l; + if (rotate == 90 || rotate == 270) + data->header.PageSize[0] = (unsigned)l; + else + data->header.PageSize[1] = (unsigned)l; + } + + memset(paperdimensions, 0, sizeof(paperdimensions)); + for (i = 0; i < 4; i ++) + margins[i] = -1.0; + if (filter_data != NULL) + { + cfGetPageDimensions(filter_data->printer_attrs, filter_data->job_attrs, + filter_data->num_options, filter_data->options, + &(data->header), 0, + &(paperdimensions[0]), &(paperdimensions[1]), + &(margins[0]), &(margins[1]), + &(margins[2]), &(margins[3]), NULL, NULL); + + cfSetPageDimensionsToDefault(&(paperdimensions[0]), &(paperdimensions[1]), + &(margins[0]), &(margins[1]), + &(margins[2]), &(margins[3]), + log, ld); + + if (data->outformat != CF_FILTER_OUT_FORMAT_CUPS_RASTER) + memset(margins, 0, sizeof(margins)); + } + else + { + for (int i = 0; i < 2; i ++) + paperdimensions[i] = data->header.PageSize[i]; + if (data->header.cupsImagingBBox[3] > 0.0) + { + // Set margins if we have a bounding box defined ... + if (data->outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER) + { + margins[0] = data->header.cupsImagingBBox[0]; + margins[1] = data->header.cupsImagingBBox[1]; + margins[2] = paperdimensions[0] - data->header.cupsImagingBBox[2]; + margins[3] = paperdimensions[1] - data->header.cupsImagingBBox[3]; + } + } + else + // ... otherwise use zero margins + for (int i = 0; i < 4; i ++) + margins[i] = 0.0; + } + + if (data->header.Duplex && (pgno & 1)) + { + // backside: change margin if needed + if (data->swap_margin_x) + { + swap = margins[2]; margins[2] = margins[0]; margins[0] = swap; + } + if (data->swap_margin_y) + { + swap = margins[3]; margins[3] = margins[1]; margins[1] = swap; + } + } + + // write page header + for (int i = 0; i < 2; i ++) + { + data->header.cupsPageSize[i] = paperdimensions[i]; + data->header.PageSize[i] = (unsigned int)(data->header.cupsPageSize[i] + + 0.5); + if (data->outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER) + data->header.Margins[i] = margins[i] + 0.5; + else + data->header.Margins[i] = 0; + } + if (data->outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER) + { + data->header.cupsImagingBBox[0] = margins[0]; + data->header.cupsImagingBBox[1] = margins[1]; + data->header.cupsImagingBBox[2] = paperdimensions[0] - margins[2]; + data->header.cupsImagingBBox[3] = paperdimensions[1] - margins[3]; + for (int i = 0; i < 4; i ++) + data->header.ImagingBoundingBox[i] = + (unsigned int)(data->header.cupsImagingBBox[i] + 0.5); + } + else + { + for (int i = 0; i < 4; i ++) + { + data->header.cupsImagingBBox[i] = 0.0; + data->header.ImagingBoundingBox[i] = 0; + } + } + + data->header.cupsWidth = 0; + data->header.cupsHeight = 0; + + // Loop over all raster images in a page and store them in bitmap. + + pdfio_dict_t *resources = pdfioDictGetDict(pdfioObjGetDict(page), "Resources"); + pdfio_dict_t *xobjects = pdfioDictGetDict(resources, "XObject"); + + // Iterate over the XObject dictionary to find images + pdfioDictIterateKeys(xobjects, (pdfio_dict_cb_t)process_image, data); + + // Swap width and height in landscape images + if(rotate == 270 || rotate == 90) + { + temp = data->header.cupsHeight; + data->header.cupsHeight = data->header.cupsWidth; + data->header.cupsWidth = temp; + } + + data->bytesPerLine = data->header.cupsBytesPerLine = + (data->header.cupsBitsPerPixel * data->header.cupsWidth + 7) / 8; + if (data->header.cupsColorOrder == CUPS_ORDER_BANDED) + data->header.cupsBytesPerLine *= data->header.cupsNumColors; + + if (!cupsRasterWriteHeader(raster, &(data->header))) + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPCLmToRaster: Can't write page %d header", pgno + 1); + return (1); + } + + colorspace_obj = pdfioDictGetObj(pdfioObjGetDict(page), "ColorSpace"); + + if (colorspace_obj) { + if (strcmp(pdfioObjGetType(colorspace_obj), "Name") == 0) + strncpy(data->colorspace, pdfioDictGetName(pdfioObjGetDict(colorspace_obj), "Type"), sizeof(data->colorspace) - 1); + else + strncpy(data->colorspace, "DeviceRGB", sizeof(data->colorspace) - 1); + } + + + // Select convertline and convertscpace function + select_convert_func(pgno, log, ld, data, convert); + + // If page is to be swapped in both x and y, rotate it by 180 degress + if (data->header.Duplex && (pgno & 1) && data->swap_image_y && + data->swap_image_x) + { + rotate = (rotate + 180) % 360; + data->swap_image_y = false; + data->swap_image_x = false; + } + + // Rotate Bitmap + if (rotate) + { + unsigned char *bitmap2 = (unsigned char *) malloc(pixel_count); + bitmap2 = rotate_bitmap(bitmap, bitmap2, rotate, data->header.cupsHeight, + data->header.cupsWidth, data->rowsize, + data->colorspace, log, ld); + free(bitmap); + bitmap = bitmap2; + } + + colordata = bitmap; + + // Write page image + lineBuf = (unsigned char *)malloc(data->bytesPerLine * sizeof(unsigned char)); + line = (unsigned char *)malloc(data->bytesPerLine * sizeof(unsigned char)); + + if (data->header.Duplex && (pgno & 1) && data->swap_image_y) + { + for (unsigned int plane = 0; plane < data->nplanes; plane ++) + { + unsigned char *bp = colordata + + (data->header.cupsHeight - 1) * (data->rowsize); + for (unsigned int h = data->header.cupsHeight; h > 0; h--) + { + for (unsigned int band = 0; band < data->nbands; band ++) + { + dp = convert->convertline(bp, line, lineBuf, h - 1, plane + band, + data, convert->convertcspace); + cupsRasterWritePixels(raster, dp, data->bytesPerLine); + } + bp -= data->rowsize; + } + } + } + else + { + for (unsigned int plane = 0; plane < data->nplanes; plane ++) + { + unsigned char *bp = colordata; + for (unsigned int h = 0; h < data->header.cupsHeight; h ++) + { + for (unsigned int band = 0; band < data->nbands; band ++) + { + dp = convert->convertline(bp, line, lineBuf, h, plane + band, + data, convert->convertcspace); + cupsRasterWritePixels(raster, dp, data->bytesPerLine); + } + bp += data->rowsize; + } + } + } + free(lineBuf); + free(line); + free(bitmap); + + return (0); +} + + +// +// 'cfFilterPCLmToRaster()' - Filter function to convert raster-only PDF/PCLm +// input to CUPS/PWG Raster output. +// + +int // O - Error status +cfFilterPCLmToRaster(int inputfd, // I - File descriptor input stream + int outputfd, // I - File descriptor output stream + int inputseekable, // I - Is input stream seekable? + // (unused) + cf_filter_data_t *data, // I - Job and printer data + void *parameters) // I - Filter-specific parameters + // (unused) +{ + cf_filter_out_format_t outformat; + FILE *inputfp; // Input file pointer + int fd = 0; // Copy file descriptor + char *filename, // PDF file to convert + tempfile[1024]; // Temporary file + char buffer[8192]; // Copy buffer + int bytes; // Bytes copied + int npages = 0; + pdfio_file_t *pdf = (pdfio_file_t *)malloc(sizeof(pdfio_file_t *));; + cups_raster_t *raster; + pclmtoraster_data_t pclmtoraster_data; + pclm_conversion_function_t convert; + cf_logfunc_t log = data->logfunc; + void *ld = data->logdata; + cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; + void *icd = data->iscanceleddata; + + if (parameters) + { + outformat = *(cf_filter_out_format_t *)parameters; + if (outformat != CF_FILTER_OUT_FORMAT_CUPS_RASTER && + outformat != CF_FILTER_OUT_FORMAT_PWG_RASTER && + outformat != CF_FILTER_OUT_FORMAT_APPLE_RASTER) + outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; + } + else + outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; + + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPCLmToRaster: Output format: %s", + (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER ? "CUPS Raster" : + (outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER ? "PWG Raster" : + "Apple Raster"))); + + // + // Open the input data stream specified by the inputfd... + // + + if ((inputfp = fdopen(inputfd, "r")) == NULL) + { + if (!iscanceled || !iscanceled(icd)) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPCLmToRaster: Unable to open input data stream."); + } + + return (1); + } + + if ((fd = cupsCreateTempFd(NULL, NULL, tempfile, sizeof(tempfile))) < 0) + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPCLmToRaster: Unable to copy PDF file: %s", + strerror(errno)); + fclose(inputfp); + return (1); + } + + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPCLmToRaster: Copying input to temp file \"%s\"", + tempfile); + + while ((bytes = fread(buffer, 1, sizeof(buffer), inputfp)) > 0) + bytes = write(fd, buffer, bytes); + + fclose(inputfp); + close(fd); + + filename = tempfile; + pdf = pdfioFileOpen(filename, NULL, NULL, NULL, NULL); + + if (parse_opts(data, outformat, &pclmtoraster_data) != 0) + { + free(pdf); + unlink(tempfile); + return (1); + } + + if (pclmtoraster_data.header.cupsBitsPerColor != 1 + && pclmtoraster_data.header.cupsBitsPerColor != 2 + && pclmtoraster_data.header.cupsBitsPerColor != 4 + && pclmtoraster_data.header.cupsBitsPerColor != 8 + && pclmtoraster_data.header.cupsBitsPerColor != 16) + { + if(log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPCLmToRaster: Specified color format is not supported: %s", + strerror(errno)); + free(pdf); + unlink(tempfile); + return (1); + } + + if (pclmtoraster_data.header.cupsColorOrder == CUPS_ORDER_PLANAR) + pclmtoraster_data.nplanes = pclmtoraster_data.header.cupsNumColors; + else + pclmtoraster_data.nplanes = 1; + if (pclmtoraster_data.header.cupsColorOrder == CUPS_ORDER_BANDED) + pclmtoraster_data.nbands = pclmtoraster_data.header.cupsNumColors; + else + pclmtoraster_data.nbands = 1; + + if ((raster = cupsRasterOpen(outputfd, + (pclmtoraster_data.outformat == + CF_FILTER_OUT_FORMAT_CUPS_RASTER ? + CUPS_RASTER_WRITE : + (pclmtoraster_data.outformat == + CF_FILTER_OUT_FORMAT_PWG_RASTER ? + CUPS_RASTER_WRITE_PWG : + CUPS_RASTER_WRITE_APPLE)))) == 0) + { + if(log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPCLmToRaster: Can't open raster stream: %s", + strerror(errno)); + free(pdf); + unlink(tempfile); + return (1); + } + + + npages = pdfioFileGetNumPages(pdf); + + for (int i = 0; i < npages; ++i) + { + pdfio_obj_t *pages = pdfioFileGetPage(pdf, i); + + if (iscanceled && iscanceled(icd)) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPCLmToRaster: Job canceled"); + break; + } + + if (log) log(ld, CF_LOGLEVEL_INFO, + "cfFilterPCLmToRaster: Starting page %d.", i + 1); + if (out_page(raster, pages, i, log, ld, &pclmtoraster_data,data, + &convert) != 0) + break; + + if (log) log(ld, CF_LOGLEVEL_INFO, + "cfFilterPCLmToRaster: Starting page %d.", (i + 1)); + if (out_page(raster, pages, i, log, ld, &pclmtoraster_data,data, + &convert) != 0) + break; + } + + cupsRasterClose(raster); + free(pdf); + unlink(tempfile); + return (0); +} diff --git a/cupsfilters/pdf.c b/cupsfilters/pdf.c new file mode 100644 index 00000000..2fbe385e --- /dev/null +++ b/cupsfilters/pdf.c @@ -0,0 +1,371 @@ +// +// Copyright 2012 Canonical Ltd. +// Copyright 2013 ALT Linux, Andrew V. Stepanov +// Copyright 2018 Sahil Arora +// Copyright 2024 Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + + +#include +#include +#include +#include "pdf.h" + +#include +#include + +// +// 'make_real_box()' - Return a PDFio rect object of real values for a box. +// + +static pdfio_rect_t // O - PDFioObjectHandle for a rect +make_real_box(float values[4]) // I - Dimensions of the box in a float array +{ + pdfio_rect_t rect; + + rect.x1 = values[0]; + rect.y1 = values[1]; + rect.x2 = values[2]; + rect.y2 = values[3]; + + return rect; +} + +// +// 'cfPDFLoadTemplate()' - Load an existing PDF file using PDFio. +// + +cf_pdf_t* +cfPDFLoadTemplate(const char *filename) +{ + pdfio_file_t *pdf = pdfioFileOpen(filename, NULL, NULL, NULL, NULL); + + if (!pdf) + return NULL; + + if (pdfioFileGetNumPages(pdf) != 1) + { + pdfioFileClose(pdf); + return NULL; + } + + return (cf_pdf_t *)pdf; +} + +// +// 'cfPDFFree()' - Free pointer used by PDF object +// + +void +cfPDFFree(cf_pdf_t *pdf) +{ + if (pdf) + { + pdfioFileClose((pdfio_file_t *)pdf); + } +} + +// +// 'cfPDFPages()' - Count number of pages in file using PDFio. +// + +int +cfPDFPages(const char *filename) +{ + pdfio_file_t *pdf = pdfioFileOpen(filename, NULL, NULL, NULL, NULL); + + if (!pdf) + { + return -1; + } + + int pages = pdfioFileGetNumPages(pdf); + pdfioFileClose(pdf); + return pages; +} + +// +// 'cfPDFPagesFP()' - Count number of pages in file using PDFio. +// + +int +cfPDFPagesFP(char *file) +{ + pdfio_file_t *pdf = pdfioFileOpen(file, NULL, NULL, NULL, NULL); + + if (!pdf) + return -1; + + int pages = pdfioFileGetNumPages(pdf); + pdfioFileClose(pdf); + return pages; +} + +// +// 'cfPDFPrependStream()' - Prepend a stream to the contents of a specified +// page in PDF file. +// + +int +cfPDFPrependStream(cf_pdf_t *pdf, + unsigned page_num, + const char *buf, + size_t len) +{ + + if(pdfioFileGetNumPages((pdfio_file_t *)pdf)==0) + return 1; + + pdfio_obj_t *page = pdfioFileGetPage((pdfio_file_t *)pdf, page_num - 1); + pdfio_dict_t *pageDict = pdfioObjGetDict(page); + + pdfio_stream_t *existing_stream = pdfioPageOpenStream(page, 0, true); + if (!existing_stream) + return 1; + + pdfio_obj_t *new_stream_obj = pdfioFileCreateObj((pdfio_file_t *)pdf, pageDict); + if (!new_stream_obj) + { + pdfioStreamClose(existing_stream); + return 1; + } + + pdfio_stream_t *new_stream = pdfioObjCreateStream(new_stream_obj, PDFIO_FILTER_FLATE); + if (!new_stream) + { + pdfioStreamClose(existing_stream); + return 1; + } + + pdfioStreamWrite(new_stream, buf, len); + pdfioStreamClose(new_stream); + + pdfio_stream_t *combined_stream = pdfioObjCreateStream(page, PDFIO_FILTER_FLATE); + if (!combined_stream) + { + pdfioStreamClose(existing_stream); + return 1; + } + + char buffer[1024]; + size_t read_len; + while ((read_len = pdfioStreamRead(existing_stream, buffer, sizeof(buffer))) > 0) + pdfioStreamWrite(combined_stream, buffer, read_len); + + pdfioStreamClose(existing_stream); + pdfioStreamClose(combined_stream); + + return 0; +} + +// +// 'cfPDFAddType1Font()' - Add the specified type1 font face to the specified +// page in a PDF document. +// + +int +cfPDFAddType1Font(cf_pdf_t *pdf, + unsigned page_num, + const char *name) +{ + pdfio_obj_t *page = pdfioFileGetPage((pdfio_file_t *)pdf, page_num); + pdfio_dict_t *pageDict = pdfioObjGetDict(page); + if (!page) + return 1; + + pdfio_dict_t *resources = pdfioDictGetDict(pageDict, "Resources"); + + if (!resources) + { + resources = pdfioDictCreate((pdfio_file_t *)pdf); + pdfioDictSetDict(pageDict, "Resources", resources); + } + + pdfio_dict_t *fonts = pdfioDictGetDict(resources, "Font"); + if (!fonts) + { + fonts = pdfioDictCreate((pdfio_file_t *)pdf); + pdfioDictSetDict(resources, "Font", fonts); + } + + pdfio_dict_t *font = pdfioDictCreate((pdfio_file_t *)pdf); + if (!font) + return 1; + + pdfioDictSetName(font, "Type", "Font"); + pdfioDictSetName(font, "Subtype", "Type1"); + char basefont[256]; + snprintf(basefont, sizeof(basefont), "/%s", name); + pdfioDictSetName(font, "BaseFont", basefont); + + pdfioDictSetDict(fonts, "bannertopdf-font", font); + + return 0; +} + +// +// 'dict_lookup_rect()' - Lookup for an array of rectangle dimensions in a PDFio +// dictionary object. If it is found, store the values in +// an array and return true, else return false. +// + +static bool +dict_lookup_rect(pdfio_obj_t *object, // I - PDF dictionary object + const char *key, // I - Key to lookup + float rect[4], // O - Array to store values if key is found + bool inheritable) // I - Whether to look for inheritable values +{ + pdfio_dict_t *dict = pdfioObjGetDict(object); + if (!dict) + return false; + + pdfio_obj_t *value = pdfioDictGetObj(dict, key); + if (!value && inheritable) + return false; + + pdfio_array_t *array = pdfioObjGetArray(value); + if (!array || pdfioArrayGetSize(array) != 4) + return false; + + for (int i = 0; i < 4; i++) + { + pdfio_obj_t *elem = pdfioArrayGetObj(array, i); + + if (pdfioArrayGetType(array, i) == PDFIO_VALTYPE_NUMBER) + { + rect[i] = pdfioObjGetNumber(elem); + } + else + return false; // If any value is not numeric, return false + } + + return true; +} + +// +// 'fit_rect()' - Update the scale of the page using old media box dimensions +// and new media box dimensions. +// + +static void +fit_rect(float oldrect[4], // I - Old media box + float newrect[4], // I - New media box + float *scale) // I - Pointer to scale which needs to be updated +{ + float oldwidth = oldrect[2] - oldrect[0]; + float oldheight = oldrect[3] - oldrect[1]; + float newwidth = newrect[2] - newrect[0]; + float newheight = newrect[3] - newrect[1]; + + *scale = newwidth / oldwidth; + if (oldheight * (*scale) > newheight) + *scale = newheight / oldheight; +} + +// +// 'cfPDFResizePage()' - Resize page in a PDF with the given dimensions. +// + +int cfPDFResizePage(cf_pdf_t *pdf, // I - Pointer to PDFio file object + unsigned page_num, // I - Page number (1-based index) + float width, // I - New width of the page + float length, // I - New length of the page + float *scale) // O - Scale of the page to be updated +{ + pdfio_obj_t *page = pdfioFileGetPage((pdfio_file_t *)pdf, page_num - 1); + if (!page) + return 1; + + float new_mediabox[4] = {0.0, 0.0, width, length}; + float old_mediabox[4]; + pdfio_rect_t media_box; + + if (!dict_lookup_rect(page, "/MediaBox", old_mediabox, true)) + return (1); + + fit_rect(old_mediabox, new_mediabox, scale); + media_box = make_real_box(new_mediabox); + + pdfio_dict_t *pageDict = pdfioObjGetDict(page); + if (pageDict) + { + pdfioDictSetRect(pageDict, "CropBox", &media_box); + pdfioDictSetRect(pageDict, "TrimBox", &media_box); + pdfioDictSetRect(pageDict, "BleedBox", &media_box); + pdfioDictSetRect(pageDict, "ArtBox", &media_box); + } + + return 0; +} + +// +// 'cfPDFDuplicatePage()' - Duplicate a specified pdf page in a PDF +// + +int +cfPDFDuplicatePage(cf_pdf_t *pdf, + unsigned page_num, + unsigned count) +{ + pdfio_obj_t *page = pdfioFileGetPage((pdfio_file_t *)pdf, page_num - 1); + + if (!page) + return 1; + + for (unsigned i = 0; i < count; ++i) + { + pdfioPageCopy((pdfio_file_t *)pdf, page); + } + + return 0; +} + +// +// 'cfPDFWrite()' - Write the contents of PDF object to an already open FILE*. +// + +void +cfPDFWrite(cf_pdf_t *pdf, + FILE *file) +{ +// pdfioFileCreateImageObjFromFile((pdfio_file_t *)pdf, file, false); +} + +// +// 'lookup_opt()' - Get value according to key in the options list. +// + +static const char* +lookup_opt(cf_opt_t *opt, + const char *key) +{ + if (!opt || !key) + return NULL; + + while (opt) + { + if (opt->key && opt->val) + { + if (strcmp(opt->key, key) == 0) + { + return opt->val; + } + } + opt = opt->next; + } + + return (""); +} + +// +// 'cfPDFFillForm()' - Fill recognized fields with information +// + +int cfPDFFillForm(cf_pdf_t *doc, cf_opt_t *opt) { + // TODO: PDFio does not directly support form filling. + return 0; +} + diff --git a/cupsfilters/pdf.h b/cupsfilters/pdf.h new file mode 100644 index 00000000..92e381d3 --- /dev/null +++ b/cupsfilters/pdf.h @@ -0,0 +1,63 @@ +// +// Copyright 2012 Canonical Ltd. +// Copyright 2018 Sahil Arora +// Copyright 2024 Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#ifndef _CUPS_FILTERS_PDF_H_ +#define _CUPS_FILTERS_PDF_H_ + +#include + +typedef struct pdfio_file_t cf_pdf_t; + +typedef struct _cf_opt cf_opt_t; + +// +// Type to bunch PDF form field name and its value. +// + +struct _cf_opt +{ + const char* key; + const char* val; + cf_opt_t *next; +}; + +cf_pdf_t *cfPDFLoadTemplate(const char *filename); +void cfPDFFree(cf_pdf_t *pdf); + +void cfPDFWrite(cf_pdf_t *doc, + FILE *file); + +int cfPDFPrependStream(cf_pdf_t *doc, + unsigned page, + const char *buf, + size_t len); + +int cfPDFAddType1Font(cf_pdf_t *doc, + unsigned page, + const char *name); + +int cfPDFResizePage(cf_pdf_t *doc, + unsigned page, + float width, + float length, + float *scale); + +int cfPDFDuplicatePage(cf_pdf_t *doc, + unsigned page, + unsigned count); + +int cfPDFFillForm(cf_pdf_t *doc, + cf_opt_t *opt); + +int cfPDFPages(const char *filename); + +int cfPDFPagesFP(char *file); + +#endif // !_CUPS_FILTERS_PDF_H_ + diff --git a/cupsfilters/pdftopdf/intervalset-private.h b/cupsfilters/pdftopdf/intervalset-private.h new file mode 100644 index 00000000..d13592ec --- /dev/null +++ b/cupsfilters/pdftopdf/intervalset-private.h @@ -0,0 +1,44 @@ +// +// Copyright 2024 Uddhav Phatak +#include + +typedef struct +{ + int start; + int end; +} interval_t; + +typedef struct +{ + interval_t *data; + size_t size; + size_t capacity; +} _cfPDFToPDFIntervalSet; + +extern const int _cfPDFToPDFIntervalSet_npos; + +void _cfPDFToPDFIntervalSet_init(_cfPDFToPDFIntervalSet *set); +void _cfPDFToPDFIntervalSet_clear(_cfPDFToPDFIntervalSet *set); +void _cfPDFToPDFIntervalSet_add(_cfPDFToPDFIntervalSet *set, int start, int end); +void _cfPDFToPDFIntervalSet_add_single(_cfPDFToPDFIntervalSet *set, int start); +void _cfPDFToPDFIntervalSet_finish(_cfPDFToPDFIntervalSet *set); + +size_t _cfPDFToPDFIntervalSet_size(const _cfPDFToPDFIntervalSet *set); + +bool _cfPDFToPDFIntervalSet_contains(const _cfPDFToPDFIntervalSet *set, int val); +int _cfPDFToPDFIntervalSet_next(const _cfPDFToPDFIntervalSet *set, int val); + +void _cfPDFToPDFIntervalSet_dump(const _cfPDFToPDFIntervalSet *set, pdftopdf_doc_t *doc); + +#endif // !_CUPS_FILTERS_PDFTOPDF_INTERVALSET_H_ + diff --git a/cupsfilters/pdftopdf/intervalset.c b/cupsfilters/pdftopdf/intervalset.c new file mode 100644 index 00000000..dbc20b47 --- /dev/null +++ b/cupsfilters/pdftopdf/intervalset.c @@ -0,0 +1,180 @@ +// +// Copyright 2024 Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#include "intervalset-private.h" +#include +#include +#include +#include +#include +#include "cupsfilters/debug-internal.h" + +const int _cfPDFToPDFIntervalSet_npos = INT_MAX; + +void +_cfPDFToPDFIntervalSet_init(_cfPDFToPDFIntervalSet *set) // {{{ +{ + set->data = NULL; + set->size = 0; + set->capacity = 0; +} +// }}} + +void +_cfPDFToPDFIntervalSet_clear(_cfPDFToPDFIntervalSet *set) // {{{ +{ + free(set->data); + set->data = NULL; + set->size = 0; + set->capacity = 0; +} +// }}} + +void +_cfPDFToPDFIntervalSet_add(_cfPDFToPDFIntervalSet *set, int start, int end) // {{{ +{ + if (start >= end) + return; + + if (set->size == set->capacity) + { + set->capacity = set->capacity == 0 ? 4 : set->capacity * 2; + set->data = realloc(set->data, set->capacity * sizeof(interval_t)); + } + + set->data[set->size].start = start; + set->data[set->size].end = end; + set->size++; +} +// }}} + +void +_cfPDFToPDFIntervalSet_add_single(_cfPDFToPDFIntervalSet *set, + int start) // {{{ +{ + key_t end = _cfPDFToPDFIntervalSet_npos; + + if (start >= end) + return; + + if (set->size == set->capacity) + { + set->capacity = set->capacity == 0 ? 4 : set->capacity * 2; + set->data = realloc(set->data, set->capacity * sizeof(interval_t)); + if (set->data == NULL) + return; + } + + set->data[set->size].start = start; + set->data[set->size].end = end; + set->size++; +} +// }}} + +static int +compare_intervals(const void *a, const void *b) // {{{ +{ + interval_t *ia = (interval_t *)a; + interval_t *ib = (interval_t *)b; + if (ia->start != ib->start) + return ia->start - ib->start; + return ia->end - ib->end; +} +// }}} + +void +_cfPDFToPDFIntervalSet_finish(_cfPDFToPDFIntervalSet *set) // {{{ +{ + if (set->size == 0) + return; + + qsort(set->data, set->size, sizeof(interval_t), compare_intervals); + + size_t new_size = 0; + for (size_t i = 1; i < set->size; i++) + { + if (set->data[new_size].end >= set->data[i].start) + { + if (set->data[new_size].end < set->data[i].end) + { + set->data[new_size].end = set->data[i].end; + } + } + else + { + new_size++; + set->data[new_size] = set->data[i]; + } + } + set->size = new_size + 1; +} +// }}}} + +size_t +_cfPDFToPDFIntervalSet_size(const _cfPDFToPDFIntervalSet *set) // {{{ +{ + return set->size; +} +// }}} + +bool +_cfPDFToPDFIntervalSet_contains(const _cfPDFToPDFIntervalSet *set, + int val) // {{{ +{ + for (size_t i = 0; i < set->size; i++) + { + if (val >= set->data[i].start && val < set->data[i].end) + { + return true; + } + } + return false; +} +// }}} + +int +_cfPDFToPDFIntervalSet_next(const _cfPDFToPDFIntervalSet *set, + int val) // {{{ +{ + val++; + for (size_t i = 0; i < set->size; i++) + { + if (val < set->data[i].end) + { + if (val >= set->data[i].start) + { + return val; + } + else + { + return set->data[i].start; + } + } + } + return _cfPDFToPDFIntervalSet_npos; +} +// }}} + +void +_cfPDFToPDFIntervalSet_dump(const _cfPDFToPDFIntervalSet *set, + pdftopdf_doc_t *doc) // {{{ +{ + if (set->size == 0) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: (empty)"); + return; + } + + for (size_t i = 0; i < set->size; i++) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: [%d,%d)", + set->data[i].start, set->data[i].end); + } +} +// }}} diff --git a/cupsfilters/pdftopdf/nup-private.h b/cupsfilters/pdftopdf/nup-private.h new file mode 100644 index 00000000..b64be897 --- /dev/null +++ b/cupsfilters/pdftopdf/nup-private.h @@ -0,0 +1,55 @@ +// +//Copyright 2024 Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#ifndef _CUPS_FILTERS_PDFTOPDF_NUP_H_ +#define _CUPS_FILTERS_PDFTOPDF_NUP_H_ + +#include "pptypes-private.h" +#include + +typedef struct _cfPDFToPDFNupParameters +{ + int nupX, nupY; + float width, height; + bool landscape; + + pdftopdf_axis_e first; + pdftopdf_position_e xstart, ystart; + pdftopdf_position_e xalign, yalign; +} _cfPDFToPDFNupParameters; + +void _cfPDFToPDFNupParameters_init(_cfPDFToPDFNupParameters *nupParams); +bool _cfPDFToPDFNupParameters_possible(int nup); +void _cfPDFToPDFNupParameters_preset(int nup, _cfPDFToPDFNupParameters *ret); +void _cfPDFToPDFNupParameters_dump(const _cfPDFToPDFNupParameters *nupParams, pdftopdf_doc_t *doc); + +typedef struct _cfPDFToPDFNupPageEdit +{ + float xpos, ypos; + float scale; + + _cfPDFToPDFPageRect sub; +} _cfPDFToPDFNupPageEdit; + +void _cfPDFToPDFNupPageEdit_dump(const _cfPDFToPDFNupPageEdit *edit, pdftopdf_doc_t *doc); + +typedef struct _cfPDFToPDFNupState +{ + _cfPDFToPDFNupParameters param; + int in_pages, out_pages; + int nup; + int subpage; +} _cfPDFToPDFNupState; + +void _cfPDFToPDFNupState_init(_cfPDFToPDFNupState *state, const _cfPDFToPDFNupParameters *param); +void _cfPDFToPDFNupState_reset(_cfPDFToPDFNupState *state); +bool _cfPDFToPDFNupState_next_page(_cfPDFToPDFNupState *state, float in_width, float in_height, _cfPDFToPDFNupPageEdit *ret); + +bool _cfPDFToPDFParseNupLayout(const char *val, _cfPDFToPDFNupParameters *ret); + +#endif // !_CUPS_FILTERS_PDFTOPDF_NUP_H_ + diff --git a/cupsfilters/pdftopdf/nup.c b/cupsfilters/pdftopdf/nup.c new file mode 100644 index 00000000..5dfcec02 --- /dev/null +++ b/cupsfilters/pdftopdf/nup.c @@ -0,0 +1,336 @@ +// +// Copyright 2024 Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#include "nup-private.h" +#include +#include +#include +#include "cupsfilters/debug-internal.h" + +void +_cfPDFToPDFNupParameters_init(_cfPDFToPDFNupParameters *nupParams) // {{{ +{ + nupParams->nupX = 1; + nupParams->nupY = 1; + nupParams->width = NAN; + nupParams->height = NAN; + nupParams->landscape = false; + nupParams->first = X; + nupParams->xstart = LEFT; + nupParams->ystart = TOP; + nupParams->xalign = CENTER; + nupParams->yalign = CENTER; +} +// }}} + +void +_cfPDFToPDFNupParameters_dump(const _cfPDFToPDFNupParameters *nupParams, + pdftopdf_doc_t *doc) // {{{ +{ + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: NupX: %d, NupY: %d, " + "width: %f, height: %f", + nupParams->nupX, nupParams->nupY, + nupParams->width, nupParams->height); + + int opos = -1, + fpos = -1, + spos = -1; + + if (nupParams->xstart == LEFT) + fpos = 0; + else if (nupParams->xstart == RIGHT) + fpos = 1; + + if (nupParams->ystart == LEFT) + spos = 0; + else if (nupParams->ystart == RIGHT) + spos = 1; + + if (nupParams->first == X) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: First Axis: X"); + opos = 0; + } + else if (nupParams->first == Y) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: First Axis: Y"); + opos = 2; + int temp = fpos; + fpos = spos; + spos = temp; + } + + if ((opos == -1) || (fpos == -1) || (spos == -1)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Bad Spec: %d; start: %d, %d", + nupParams->first, nupParams->xstart, nupParams->ystart); + } + else + { + static const char *order[4] = {"lr", "rl", "bt", "tb"}; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Order: %s%s", + order[opos + fpos], + order[(opos + 2) % 4 + spos]); + } + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Alignment:"); + _cfPDFToPDFPositionAndAxisDump(nupParams->xalign, X, doc); + _cfPDFToPDFPositionAndAxisDump(nupParams->yalign, Y, doc); +} +// }}} + +bool +_cfPDFToPDFNupParameters_possible(int nup) // {{{ +{ + return ((nup >= 1) && (nup <= 16) && + ((nup != 5) && (nup != 7) && (nup != 11) && (nup != 13) && + (nup != 14))); +} +// }}} + +void +_cfPDFToPDFNupParameters_preset(int nup, + _cfPDFToPDFNupParameters *ret) // {{{ +{ + switch (nup) + { + case 1: + ret->nupX = 1; + ret->nupY = 1; + break; + case 2: + ret->nupX = 2; + ret->nupY = 1; + ret->landscape = true; + break; + case 3: + ret->nupX = 3; + ret->nupY = 1; + ret->landscape = true; + break; + case 4: + ret->nupX = 2; + ret->nupY = 2; + break; + case 6: + ret->nupX = 3; + ret->nupY = 2; + ret->landscape = true; + break; + case 8: + ret->nupX = 4; + ret->nupY = 2; + ret->landscape = true; + break; + case 9: + ret->nupX = 3; + ret->nupY = 3; + break; + case 10: + ret->nupX = 5; + ret->nupY = 2; + ret->landscape = true; + break; + case 12: + ret->nupX = 3; + ret->nupY = 4; + break; + case 15: + ret->nupX = 5; + ret->nupY = 3; + ret->landscape = true; + break; + case 16: + ret->nupX = 4; + ret->nupY = 4; + break; + } +} +// }}} + +void +_cfPDFToPDFNupState_init(_cfPDFToPDFNupState *state, + const _cfPDFToPDFNupParameters *param) // {{{ +{ + state->param = *param; + state->in_pages = 0; + state->out_pages = 0; + state->nup = param->nupX * param->nupY; + state->subpage = state->nup; +} +// }}} + +void +_cfPDFToPDFNupState_reset(_cfPDFToPDFNupState *state) // {{{ +{ + state->in_pages = 0; + state->out_pages = 0; + state->subpage = state->nup; +} +// }}} + +void +_cfPDFToPDFNupPageEdit_dump(const _cfPDFToPDFNupPageEdit *edit, + pdftopdf_doc_t *doc) // {{{ +{ + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: xpos: %f, ypos: %f, scale: %f", + edit->xpos, edit->ypos, edit->scale); + _cfPDFToPDFPageRect_dump(&edit->sub, doc); +} +// }}} + +typedef struct { + int first; + int second; +} int_pair; + +static int_pair +_cfPDFToPDFNupState_convert_order(const _cfPDFToPDFNupState *state, + int subpage) // {{{ +{ + int subx, suby; + if (state->param.first == X) + { + subx = subpage % state->param.nupX; + suby = subpage / state->param.nupX; + } + else + { + subx = subpage / state->param.nupY; + suby = subpage % state->param.nupY; + } + + subx = (state->param.nupX - 1) * (state->param.xstart + 1) / 2 - state->param.xstart * subx; + suby = (state->param.nupY - 1) * (state->param.ystart + 1) / 2 - state->param.ystart * suby; + + int_pair result = {subx, suby}; + return result; +} +// }}} + +static float +lin(pdftopdf_position_e pos, float size) // {{{ +{ + if (pos == -1) + return 0; + else if (pos == 0) + return size / 2; + else if (pos == 1) + return size; + return size * (pos + 1) / 2; +} +// }}} + +void +_cfPDFToPDFNupState_calculate_edit(const _cfPDFToPDFNupState *state, + int subx, int suby, + _cfPDFToPDFNupPageEdit *ret) // {{{ +{ + const float width = state->param.width / state->param.nupX; + const float height = state->param.height / state->param.nupY; + + ret->xpos = subx * width; + ret->ypos = suby * height; + + const float scalex = width / ret->sub.width; + const float scaley = height / ret->sub.height; + float subwidth = ret->sub.width * scaley; + float subheight = ret->sub.height * scalex; + + if (scalex > scaley) + { + ret->scale = scaley; + subheight = height; + ret->xpos += lin(state->param.xalign, width - subwidth); + } + else + { + ret->scale = scalex; + subwidth = width; + ret->ypos += lin(state->param.yalign, height - subheight); + } + + ret->sub.left = ret->xpos; + ret->sub.bottom = ret->ypos; + ret->sub.right = ret->sub.left + subwidth; + ret->sub.top = ret->sub.bottom + subheight; +} +// }}} + +bool +_cfPDFToPDFNupState_next_page(_cfPDFToPDFNupState *state, + float in_width, float in_height, + _cfPDFToPDFNupPageEdit *ret) // {{{ +{ + state->in_pages++; + state->subpage++; + if (state->subpage >= state->nup) + { + state->subpage = 0; + state->out_pages++; + } + + ret->sub.width = in_width; + ret->sub.height = in_height; + + int_pair sub = _cfPDFToPDFNupState_convert_order(state, state->subpage); + _cfPDFToPDFNupState_calculate_edit(state, sub.first, sub.second, ret); + + return (state->subpage == 0); +} +// }}} + +static int_pair +parsePosition(char a, char b) // {{{ +{ + a |= 0x20; // make lowercase + b |= 0x20; + if ((a == 'l') && (b == 'r')) + return (int_pair){X, LEFT}; + else if ((a == 'r') && (b == 'l')) + return (int_pair){X, RIGHT}; + else if ((a == 't') && (b == 'b')) + return (int_pair){Y, TOP}; + else if ((a == 'b') && (b == 't')) + return (int_pair){Y, BOTTOM}; + return (int_pair){X, CENTER}; +} +// }}} + +bool +_cfPDFToPDFParseNupLayout(const char *val, + _cfPDFToPDFNupParameters *ret) // {{{ +{ + int_pair pos0 = parsePosition(val[0], val[1]); + if (pos0.second == CENTER) + return false; + int_pair pos1 = parsePosition(val[2], val[3]); + if ((pos1.second == CENTER) || (pos0.first == pos1.first)) + return false; + + ret->first = pos0.first; + if (ret->first == X) + { + ret->xstart = pos0.second; + ret->ystart = pos1.second; + } + else + { + ret->xstart = pos1.second; + ret->ystart = pos0.second; + } + + return (val[4] == 0); +} +// }}} + diff --git a/cupsfilters/pdftopdf/pdftopdf-private.h b/cupsfilters/pdftopdf/pdftopdf-private.h new file mode 100644 index 00000000..804408b0 --- /dev/null +++ b/cupsfilters/pdftopdf/pdftopdf-private.h @@ -0,0 +1,25 @@ +// +// Copyright 2020 by Jai Luthra. +// Copyright 2024 Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#ifndef _CUPS_FILTERS_PDFTOPDF_PDFTOPDF_H +#define _CUPS_FILTERS_PDFTOPDF_PDFTOPDF_H + +#include + +typedef struct // **** Document information **** +{ + cf_logfunc_t logfunc; // Log function + void *logdata; // Log data + cf_filter_iscanceledfunc_t iscanceledfunc; // Function returning 1 when + // job is canceled, NULL for not + // supporting stop on cancel + void *iscanceleddata; // User data for is-canceled + // function, can be NULL +} pdftopdf_doc_t; + +#endif // !_CUPS_FILTERS_PDFTOPDF_PDFTOPDF_H diff --git a/cupsfilters/pdftopdf/pdftopdf-processor-private.h b/cupsfilters/pdftopdf/pdftopdf-processor-private.h new file mode 100644 index 00000000..4067f558 --- /dev/null +++ b/cupsfilters/pdftopdf/pdftopdf-processor-private.h @@ -0,0 +1,113 @@ +// +// Copyright 2024 Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#ifndef C_PDFTOPDF_PROCESSOR_PRIVATE_H +#define C_PDFTOPDF_PROCESSOR_PRIVATE_H + +#include "pptypes-private.h" +#include "nup-private.h" +#include "pdftopdf-private.h" +#include "pdfio-pdftopdf-processor-private.h" +#include "intervalset-private.h" +#include +#include + +typedef enum { + CF_PDFTOPDF_BOOKLET_OFF, + CF_PDFTOPDF_BOOKLET_ON, + CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE +} pdftopdf_booklet_mode_e; + +typedef struct _cfPDFToPDF_PDFioProcessor{ + + // 1st mode: existing + pdfio_obj_t *page; // Equivalent to QPDFObjectHandle + int no; + + // 2nd mode: create new + HashTable *xobjs; // Pointer to a single HashTable + + char *content; // Equivalent to std::string content + + pdftopdf_rotation_e rotation; + + // Other members + pdfio_file_t *pdf; // Equivalent to std::unique_ptr + pdfio_obj_t **orig_pages; // Equivalent to std::vector + size_t orig_pages_size; // Current number of pages + size_t orig_pages_capacity; // Capacity for page array + + bool hasCM; + char *extraheader; +} _cfPDFToPDF_PDFioProcessor; + + +typedef struct { + int job_id, num_copies; + const char *user, *title; + bool pagesize_requested; + bool fitplot; + bool fillprint; // print-scaling = fill + bool cropfit; // -o crop-to-fit + bool autoprint; // print-scaling = auto + bool autofit; // print-scaling = auto-fit + bool fidelity; + bool no_orientation; + _cfPDFToPDFPageRect page; + pdftopdf_rotation_e orientation, normal_landscape; // normal_landscape (i.e. default direction) is e.g. needed for number-up=2 + bool paper_is_landscape; + bool duplex; + pdftopdf_border_type_e border; + _cfPDFToPDFNupParameters nup; + bool reverse; + + char *page_label; + bool even_pages, odd_pages; + _cfPDFToPDFIntervalSet *page_ranges; + _cfPDFToPDFIntervalSet *input_page_ranges; + + bool mirror; + + pdftopdf_position_e xpos, ypos; + + bool collate; + + bool even_duplex; // make number of pages a multiple of 2 + + pdftopdf_booklet_mode_e booklet; + int book_signature; + + bool auto_rotate; + + int device_copies; + bool device_collate; + bool set_duplex; + + int page_logging; + int copies_to_be_logged; +} _cfPDFToPDFProcessingParameters; + +void _cfPDFToPDFProcessingParameters_init(_cfPDFToPDFProcessingParameters *processingParams); + +bool _cfPDFToPDFProcessingParameters_with_page(const _cfPDFToPDFProcessingParameters *self, + int outno); + +bool _cfPDFToPDFProcessingParameters_have_page(const _cfPDFToPDFProcessingParameters *self, + int pageno); +void _cfPDFToPDFProcessingParameters_dump(const _cfPDFToPDFProcessingParameters *self, + pdftopdf_doc_t *doc); + + + + + +int* _cfPDFToPDFBookletShuffle(int numPages, int signature, int* ret_size); + +bool _cfProcessPDFToPDF(_cfPDFToPDF_PDFioProcessor *proc, + _cfPDFToPDFProcessingParameters *param, + pdftopdf_doc_t *doc); +#endif // C_PDFTOPDF_PROCESSOR_PRIVATE_H diff --git a/cupsfilters/pdftopdf/pdftopdf-processor.c b/cupsfilters/pdftopdf/pdftopdf-processor.c new file mode 100644 index 00000000..766d3992 --- /dev/null +++ b/cupsfilters/pdftopdf/pdftopdf-processor.c @@ -0,0 +1,583 @@ +// +// Copyright 2024 Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#include "processor.h" +#include +#include "cupsfilters/debug-internal.h" +#include +#include +#include +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +void +_cfPDFToPDFProcessingParameters_init(_cfPDFToPDFProcessingParameters *processingParams) // {{{ +{ + + processingParams->job_id = 0; + processingParams->num_copies = 1; + processingParams->user = NULL; + processingParams->title = NULL; + processingParams->pagesize_requested = false; + processingParams->fitplot = false; + processingParams->fillprint = false; // print-scaling = fill + processingParams->cropfit = false; + processingParams->autoprint = false; + processingParams->autofit = false; + processingParams->fidelity = false; + processingParams->no_orientation = false; + processingParams->orientation = ROT_0; + processingParams->normal_landscape = ROT_270; + processingParams->paper_is_landscape = false; + processingParams->duplex = false; + processingParams->border = NONE; + processingParams->reverse = false; + + processingParams->page_label = NULL; + processingParams->even_pages = true; + processingParams->odd_pages = true; + + processingParams->mirror = false; + + processingParams->xpos = CENTER; + processingParams->ypos = CENTER; + + processingParams->collate = false; + processingParams->even_duplex = false; + + processingParams->booklet = CF_PDFTOPDF_BOOKLET_OFF; + processingParams->book_signature = -1; + + processingParams->auto_rotate = false; + + processingParams->device_copies = 1; + processingParams->device_collate = false; + processingParams->set_duplex = false; + + processingParams->page_logging = -1; + processingParams->copies_to_be_logged = 0; + + processingParams->page.width = 612.0; // Letter size width in points + processingParams->page.height = 792.0; // Letter size height in points + processingParams->page.top = processingParams->page.height - 36.0; + processingParams->page.bottom = 36.0; + processingParams->page.left = 18.0; + processingParams->page.right = processingParams->page.width - 18.0; + + _cfPDFToPDFIntervalSet_add_single(processingParams->input_page_ranges, 1); + _cfPDFToPDFIntervalSet_finish(processingParams->input_page_ranges); + _cfPDFToPDFIntervalSet_add_single(processingParams->page_ranges, 1); + _cfPDFToPDFIntervalSet_finish(processingParams->page_ranges); +} +// }}} + +void +BookletMode_dump(pdftopdf_booklet_mode_e bkm, + pdftopdf_doc_t *doc) // {{{ +{ + static const char *bstr[3] = {"Off", "On", "Shuffle-Only"}; + + if ((bkm < CF_PDFTOPDF_BOOKLET_OFF) || + (bkm > CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Booklet mode: (Bad booklet mode: %d)", + bkm); + } + else + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Booklet mode: %s", + bstr[bkm]); + } +} +// }}} + +bool +_cfPDFToPDFProcessingParameters_with_page(const _cfPDFToPDFProcessingParameters *processingParams, + int outno) // {{{ +{ + if (outno % 2 == 0) + { + if (!processingParams->even_pages) + return false; + } + else if (!processingParams->odd_pages) + { + return false; + } + return _cfPDFToPDFIntervalSet_contains(processingParams->page_ranges, outno); +} +// }}} + +bool +_cfPDFToPDFProcessingParameters_have_page(const _cfPDFToPDFProcessingParameters *processingParams, + int pageno) // {{{ +{ + return _cfPDFToPDFIntervalSet_contains(processingParams->input_page_ranges, pageno); +} +// }}} + +void +_cfPDFToPDFProcessingParameters_dump(const _cfPDFToPDFProcessingParameters *processingParams, + pdftopdf_doc_t *doc) // {{{ +{ + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: job_id: %d, num_copies: %d", + processingParams->job_id, processingParams->num_copies); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: user: %s, title: %s", + (processingParams->user) ? processingParams->user : "(null)", + (processingParams->title) ? processingParams->title : "(null)"); + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: fitplot: %s", + (processingParams->fitplot) ? "true" : "false"); + + _cfPDFToPDFPageRect_dump(&processingParams->page, doc); + _cfPDFToPDFRotationDump(processingParams->orientation, doc); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: paper_is_landscape: %s", + (processingParams->paper_is_landscape) ? "true" : "false"); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: duplex: %s", + (processingParams->duplex) ? "true" : "false"); + + _cfPDFToPDFBorderTypeDump(processingParams->border, doc); + _cfPDFToPDFNupParameters_dump(&processingParams->nup, doc); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: reverse: %s", + (processingParams->reverse) ? "true" : "false"); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: even_pages: %s, odd_pages: %s", + (processingParams->even_pages) ? "true" : "false", + (processingParams->odd_pages) ? "true" : "false"); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: input page range:"); + + _cfPDFToPDFIntervalSet_dump(processingParams->input_page_ranges, doc); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: page range:"); + + _cfPDFToPDFIntervalSet_dump(processingParams->page_ranges, doc); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: mirror: %s", + (processingParams->mirror) ? "true" : "false"); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Position:"); + + _cfPDFToPDFPositionAndAxisDump(processingParams->xpos, X, doc); + _cfPDFToPDFPositionAndAxisDump(processingParams->ypos, Y, doc); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: collate: %s", + (processingParams->collate) ? "true" : "false"); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: even_duplex: %s", + (processingParams->even_duplex) ? "true" : "false"); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: page_label: %s", + processingParams->page_label ? processingParams->page_label : "(none)"); + + BookletMode_dump(processingParams->booklet, doc); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: booklet signature: %d", processingParams->book_signature); + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: auto_rotate: %s", + (processingParams->auto_rotate) ? "true" : "false"); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: device_copies: %d", + processingParams->device_copies); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: device_collate: %s", + (processingParams->device_collate) ? "true" : "false"); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: set_duplex: %s", + (processingParams->set_duplex) ? "true" : "false"); +} +// }}} + +int* +_cfPDFToPDFBookletShuffle(int numPages, int signature, int* ret_size) +{ + if (signature < 0) { + signature = (numPages + 3) & ~0x3; // Round up to the nearest multiple of 4 + } + + int maxSize = numPages + signature - 1; + int* ret = (int*)malloc(maxSize * sizeof(int)); + if (ret == NULL) + { + *ret_size = 0; + return NULL; // Handle memory allocation failure + } + + int curpage = 0; + int index = 0; // Keeps track of the current index in the result array + while (curpage < numPages) + { + int firstpage = curpage; + int lastpage = curpage + signature - 1; + + while (firstpage < lastpage) + { + ret[index++] = lastpage--; + ret[index++] = firstpage++; + ret[index++] = firstpage++; + ret[index++] = lastpage--; + } + curpage += signature; + } + + *ret_size = index; // Set the size of the result + return ret; +} + +bool +_cfProcessPDFToPDF( _cfPDFToPDF_PDFioProcessor *proc, + _cfPDFToPDFProcessingParameters *param, + pdftopdf_doc_t *doc) +{ + if(!_cfPDFToPDF_PDFioProcessor_check_print_permissions(proc, doc)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Not allowed to print"); + return false; + } + + const bool dst_lscape = + (param->paper_is_landscape == + ((param->orientation == ROT_0) || (param->orientation == ROT_180))); + + if (param->paper_is_landscape) + { + int temp = param->nup.nupX; + param->nup.nupX = param->nup.nupY; + param->nup.nupY = temp; + } + + if (param->auto_rotate) + _cfPDFToPDF_PDFioProcessor_auto_rotate_all(proc, dst_lscape, param->normal_landscape); + + int *num_page; + _cfPDFToPDFPageHandle **pages = _cfPDFToPDF_PDFioProcessor_get_pages(proc, doc, num_page); + + _cfPDFToPDFPageHandle **input_page_range_list = malloc((*num_page) * sizeof(_cfPDFToPDFPageHandle*)); + int input_page_range_size = 0; + + for (int i = 1; i <= *num_page; i++) + { + if (_cfPDFToPDFProcessingParameters_with_page(param, i)) + { + input_page_range_list[input_page_range_size++] = pages[i - 1]; + input_page_range_size++; + } + } + + const int numOrigPages = input_page_range_size; + + int* shuffle = NULL; + int* shuffle_size; + + if (param->booklet != CF_PDFTOPDF_BOOKLET_OFF) + { + shuffle = _cfPDFToPDFBookletShuffle(numOrigPages, param->book_signature, shuffle_size); + if (param->booklet == CF_PDFTOPDF_BOOKLET_ON) + { + _cfPDFToPDFNupParameters_preset(2, ¶m->nup); + } + } + else + { + int* shuffle = malloc(numOrigPages * sizeof(numOrigPages)); + for (int i = 0; i < numOrigPages; i++) + { + shuffle[i] = i; + } + } + + const int numPages = MAX(*shuffle_size, input_page_range_size); + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: \"print-scaling\" IPP attribute: %s", + (param->autoprint ? "auto" : + (param->autofit ? "auto-fit" : + (param->fitplot ? "fit" : + (param->fillprint ? "fill" : + (param->cropfit ? "none" : + "Not defined, should never happen")))))); + + if (param->autoprint || param->autofit) + { + bool margin_defined = true; + bool document_large = false; + int pw = param->page.right - param->page.left; + int ph = param->page.top - param->page.bottom; + + if ((param->page.width == pw) && (param->page.height == ph)) + margin_defined = false; + + for (int i = 0; i < input_page_range_size; i ++) + { + _cfPDFToPDFPageRect r = _cfPDFToPDFPageHandle_get_rect(input_page_range_list[i]); + int w = r.width * 100 / 102; // 2% of tolerance + int h = r.height * 100 / 102; + if ((w > param->page.width || h > param->page.height) && + (h > param->page.width || w > param->page.height)) + { + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Page %d too large for output page size, scaling pages to fit.", + i + 1); + document_large = true; + } + } + + if (param->fidelity && doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: \"ipp-attribute-fidelity\" IPP attribute is set, scaling pages to fit."); + + if (param->autoprint) + { + if (param->fidelity || document_large) + { + if (margin_defined) + param->fitplot = true; + else + param->fillprint = true; + } + else + param->cropfit = true; + } + + else + { + if (param->fidelity || document_large) + param->fitplot = true; + else + param->cropfit = true; + } + } + + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Print scaling mode: %s", + (param->fitplot ? + "Scale to fit printable area" : + (param->fillprint ? + "Scale to fill page and crop" : + (param->cropfit ? + "Do not scale, center, crop if needed" : + "Not defined, should never happen")))); + + if (param->cropfit) + { + param->page.left = 0; + param->page.bottom = 0; + param->page.right = param->page.width; + param->page.top = param->page.height; + } + + if (param->pagesize_requested && (param->fillprint || param->cropfit)) + { + for (int i = 0; i < input_page_range_size; i ++) + { + _cfPDFToPDFPageHandle *page = input_page_range_list[i]; + pdftopdf_rotation_e orientation; + if(_cfPDFToPDFPageHandle_is_landscape(page, param->orientation)) + orientation = param->normal_landscape; + else + orientation = ROT_0; + _cfPDFToPDFPageHandle_crop(page, + ¶m->page, + orientation, + param->orientation, + param->xpos, + param->ypos, + !param->cropfit, + !param->auto_rotate, + doc); + } + if (param->fillprint) + param->fitplot = true; + } + + _cfPDFToPDFPageHandle *curpage; + int outputpage = 0; + int outputno = 0; + + if ((param->nup.nupX == 1) && (param->nup.nupY == 1) && !param->fitplot) + { + param->nup.width = param->page.width; + param->nup.height = param->page.height; + } + else + { + param->nup.width = param->page.right - param->page.left; + param->nup.height = param->page.top - param->page.bottom; + } + + if ((param->orientation == ROT_90) || (param->orientation == ROT_270)) + { + int temp = param->nup.nupX; + param->nup.nupX = param->nup.nupY; + param->nup.nupY = temp; + + param->nup.landscape = !param->nup.landscape; + param->orientation = param->orientation - param->normal_landscape; + } + + double xpos = 0, ypos = 0; + if(param->nup.landscape) + { + param->orientation = param->orientation + param->normal_landscape; + if (param->nup.nupX != 1 || param->nup.nupY != 1 || param->fitplot) + { + xpos = param->page.height - param->page.top; + ypos = param->page.left; + } + int temp = param->page.width; + param->page.width = param->page.height; + param->page.height = temp; + + temp = param->nup.width; + param->nup.width = param->nup.height; + param->nup.height = temp; + } + else + { + if (param->nup.nupX != 1 || param->nup.nupY != 1 || param->fitplot) + { + xpos = param->page.left; + ypos = param->page.bottom; + } + } + + _cfPDFToPDFNupState *nupState; + _cfPDFToPDFNupState_init(nupState, ¶m->nup); + + _cfPDFToPDFNupPageEdit pgedit; + + for (int iA = 0; iA < numPages; iA++) + { + _cfPDFToPDFPageHandle *page; + if(shuffle[iA] >= numOrigPages) + page = _cfPDFToPDF_PDFioProcessor_new_page(proc, param->page.width, param->page.height, doc); + else + page = input_page_range_list[shuffle[iA]]; + + _cfPDFToPDFPageRect rect = _cfPDFToPDFPageHandle_get_rect(page); + + if (!param->pagesize_requested) + { + param->page.width = param->page.right = rect.width; + param->page.height = param->page.top = rect.height; + } + + bool newPage = _cfPDFToPDFNupState_next_page(nupState, rect.width, rect.height, &pgedit); + if (newPage) + { + if((curpage) && (_cfPDFToPDFProcessingParameters_with_page(param, outputpage))) + { + _cfPDFToPDFPageHandle_rotate(curpage, param->orientation); + if (param->mirror) + _cfPDFToPDFPageHandle_mirror(curpage, proc->pdf); + +// _cfPDFToPDF_PDFioProcessor_add_page(proc, curpage, param->reverse); + // Log page in /var/log/cups/page_log + + outputno ++; + if (param->page_logging == 1) + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_CONTROL, + "PAGE: %d %d", outputno, + param->copies_to_be_logged); + } + curpage = _cfPDFToPDF_PDFioProcessor_new_page(proc, param->page.width, param->page.height, doc); + outputpage++; + } + + if (shuffle[iA] >= numOrigPages) + continue; + + if (param->border != NONE) + _cfPDFToPDFPageHandle_add_border_rect(page, proc->pdf, rect, param->border, 1.0 / pgedit.scale); + + if (param->page_label[0] != '\0') + { + _cfPDFToPDFPageHandle_add_label(page, proc->pdf, ¶m->page, param->page_label); + } + + if(param->cropfit) + { + if ((param->nup.nupX == 1) && (param->nup.nupY == 1)) + { + double xpos2, ypos2; + _cfPDFToPDFPageRect get_rect_height = _cfPDFToPDFPageHandle_get_rect(page); + _cfPDFToPDFPageRect get_rect_width = _cfPDFToPDFPageHandle_get_rect(page); + + if ((param->page.height - param->page.width) * + (get_rect_height.height - get_rect_width.width) < 0) + { + xpos2 = (param->page.width - (get_rect_height.height)) / 2; + ypos2 = (param->page.height - (get_rect_width.width)) / 2; + _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, ypos2 + xpos, xpos2 + ypos, 1, NULL); + } + else + { + xpos2 = (param->page.width - get_rect_width.width) / 2; + ypos2 = (param->page.height - get_rect_height.height) /2; + _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, xpos2 + xpos, ypos2 + ypos, 1, NULL); + } + } + else + { + _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, pgedit.xpos + xpos, pgedit.ypos + ypos, pgedit.scale, NULL); + } + } + else + _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, pgedit.xpos + xpos, pgedit.ypos + ypos, pgedit.scale, NULL); + +#ifdef DEBUG + _cfPDFToPDFPageHandle *dbg = (_cfPDFToPDFPageHandle *)curpage; + if (dbg && dbg->debug) + { + _cfPDFToPDFPageHandle_debug(dbg, sub, xpos,ypos); + } +#endif + } + + if((curpage) && (_cfPDFToPDFProcessingParameters_with_page(param, outputpage))) + { + _cfPDFToPDFPageHandle_rotate(curpage, param->orientation); + if(param->mirror) + _cfPDFToPDFPageHandle_mirror(curpage, proc->pdf); + + // need to output empty page to not confuse duplex +// _cfPDFToPDF_PDFioProcessor_add_page(proc, _cfPDFToPDF_PDFioProcessor_new_page(proc, +// param->page.width, param->page.height, doc), param->reverse); + + // Log page in /var/log/cups/page_log + if(param->page_logging == 1) + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_CONTROL, + "PAGE: %d %d", outputno + 1, + param->copies_to_be_logged); + } + + _cfPDFToPDF_PDFioProcessor_multiply(proc, param->num_copies, param->collate); + return true; +} diff --git a/cupsfilters/pdftopdf/pdftopdf.c b/cupsfilters/pdftopdf/pdftopdf.c new file mode 100644 index 00000000..0e008fd5 --- /dev/null +++ b/cupsfilters/pdftopdf/pdftopdf.c @@ -0,0 +1,964 @@ +// +// Copyright 2024 Uddhav Phatak +// +// Copyright (c) 6-2011, BBR Inc. All rights reserved. +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pdftopdf-private.h" +#include "processor.h" + +#include + +static bool +optGetInt(const char *name, + int num_options, + cups_option_t *options, + int *ret) // {{{ +{ + const char *val = cupsGetOption(name, num_options, options); + if (val) + { + *ret = atoi(val); + return true; + } + return false; +} +// }}} + +static bool +optGetFloat(const char *name, + int num_options, + cups_option_t *options, + float *ret) // {{{ +{ + const char *val = cupsGetOption(name, num_options, options); + if (val) + { + *ret = atof(val); + return true; + } + return false; +} +// }}} + +static bool +is_false(const char *value) // {{{ +{ + if (!value) + return false; + return ((strcasecmp(value, "no") == 0) || + (strcasecmp(value, "off") == 0) || + (strcasecmp(value, "false") == 0)); +} +// }}} + +static bool +is_true(const char *value) // {{{ +{ + if (!value) + return false; + return ((strcasecmp(value, "yes") == 0) || + (strcasecmp(value, "on") == 0) || + (strcasecmp(value, "true") == 0)); +} +// }}} + +static bool +parsePosition(const char *value, + pdftopdf_position_e *xpos, + pdftopdf_position_e *ypos) // {{{ +{ + // ['center','top','left','right','top-left','top-right','bottom', + // 'bottom-left','bottom-right'] + *xpos = CENTER; + *ypos = CENTER; + int next = 0; + + if (strcasecmp(value, "center") == 0) + return true; + else if (strncasecmp(value, "top", 3) == 0) + { + *ypos = TOP; + next = 3; + } + else if (strncasecmp(value, "bottom", 6) == 0) { + *ypos = BOTTOM; + next = 6; + } + + if (next) + { + if (value[next] == 0) + return true; + else if (value[next] != '-') + return false; + value += next + 1; + } + if (strcasecmp(value, "left") == 0) + *xpos = LEFT; + else if (strcasecmp(value, "right") == 0) + *xpos = RIGHT; + else + return false; + + return true; +} +// }}} + +static void +parseRanges(const char *range, _cfPDFToPDFIntervalSet *ret) // {{{ +{ + _cfPDFToPDFIntervalSet_clear(ret); + + if (!range) + { + _cfPDFToPDFIntervalSet_add(ret, 1, 1); + _cfPDFToPDFIntervalSet_finish(ret); + return; + } + + int lower, upper; + while (*range) + { + if (*range == '-') + { + range++; + upper = strtol(range, (char **)&range, 10); + if (upper >= 2147483647) + _cfPDFToPDFIntervalSet_add(ret, 1, 1); + else + _cfPDFToPDFIntervalSet_add(ret, 1, upper+1); + } + else + { + lower = strtol(range, (char **)&range, 10); + if (*range == '-') + { + range++; + if (!isdigit(*range)) + _cfPDFToPDFIntervalSet_add(ret, lower, lower); + else + { + upper = strtol(range, (char **)&range, 10); + if (upper >= 2147483647) + _cfPDFToPDFIntervalSet_add(ret, lower, lower); + else + _cfPDFToPDFIntervalSet_add(ret, lower, upper+1); + } + } + else + { + _cfPDFToPDFIntervalSet_add(ret, lower, lower+1); + } + } + if (*range != ',') + break; + range++; + } + _cfPDFToPDFIntervalSet_finish(ret); +} +// }}} + +static bool +_cfPDFToPDFParseBorder(const char *val, + pdftopdf_border_type_e *ret) // {{{ +{ + if (strcasecmp(val, "none") == 0) + *ret = NONE; + else if (strcasecmp(val, "single") == 0) + *ret = ONE_THIN; + else if (strcasecmp(val, "single-thick") == 0) + *ret = ONE_THICK; + else if (strcasecmp(val, "double") == 0) + *ret = TWO_THIN; + else if (strcasecmp(val, "double-thick") == 0) + *ret = TWO_THICK; + else + return false; + return true; +} +// }}} + +void +getParameters(cf_filter_data_t *data, + int num_options, + cups_option_t *options, + _cfPDFToPDFProcessingParameters *param, + pdftopdf_doc_t *doc) // {{{ +{ + char *final_content_type = data->final_content_type; + ipp_t *printer_attrs = data->printer_attrs; + ipp_t *job_attrs = data->job_attrs; + ipp_attribute_t *attr; + const char *val; + int ipprot; + int nup; + char rawlabel[256]; + char *classification; + char cookedlabel[256]; + + if ((val = cupsGetOption("copies", num_options, options)) != NULL || + (val = cupsGetOption("Copies", num_options, options)) != NULL || + (val = cupsGetOption("num-copies", num_options, options)) != NULL || + (val = cupsGetOption("NumCopies", num_options, options)) != NULL) + { + int copies = atoi(val); + if (copies > 0) + param->num_copies = copies; + } + + if (param->num_copies == 0) + param->num_copies = 1; + + if (printer_attrs != NULL && + (attr = ippFindAttribute(printer_attrs, + "landscape-orientation-requested-preferred", + IPP_TAG_ZERO)) != NULL && + ippGetInteger(attr, 0) == 5) + param->normal_landscape = ROT_270; + else + param->normal_landscape = ROT_90; + + param->orientation = ROT_0; + param->no_orientation = false; + if (optGetInt("orientation-requested", num_options, options, &ipprot)) + { + // IPP orientation values are: + // 3: 0 degrees, 4: 90 degrees, 5: -90 degrees, 6: 180 degrees + + if ((ipprot < 3) || (ipprot > 6)) + { + if (ipprot && doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Bad value (%d) for " + "orientation-requested, using 0 degrees", + ipprot); + param->no_orientation = true; + } + else + { + static const pdftopdf_rotation_e + ipp2rot[4] = {ROT_0, ROT_90, ROT_270, ROT_180}; + param->orientation = ipp2rot[ipprot - 3]; + } + } + else if ((val = cupsGetOption("landscape", num_options, options)) != NULL) + { + if (!is_false(val)) + param->orientation = param->normal_landscape; + } + else + param->no_orientation = true; + + param->pagesize_requested = + (cfGetPageDimensions(printer_attrs, job_attrs, num_options, options, NULL, + 0, + &(param->page.width), &(param->page.height), + &(param->page.left), &(param->page.bottom), + &(param->page.right), &(param->page.top), + NULL, NULL) >= 1); + + cfSetPageDimensionsToDefault(&(param->page.width), &(param->page.height), + &(param->page.left), &(param->page.bottom), + &(param->page.right), &(param->page.top), + doc->logfunc, doc->logdata); + + param->page.right = param->page.width - param->page.right; + param->page.top = param->page.height - param->page.top; + + param->paper_is_landscape = (param->page.width > param->page.height); + + _cfPDFToPDFPageRect tmp; // borders (before rotation) + + optGetFloat("page-top", num_options, options, &tmp.top); + optGetFloat("page-left", num_options, options, &tmp.left); + optGetFloat("page-right", num_options, options, &tmp.right); + optGetFloat("page-bottom", num_options, options, &tmp.bottom); + + if ((val = cupsGetOption("media-top-margin", num_options, options)) + != NULL) + tmp.top = atof(val) * 72.0 / 2540.0; + if ((val = cupsGetOption("media-left-margin", num_options, options)) + != NULL) + tmp.left = atof(val) * 72.0 / 2540.0; + if ((val = cupsGetOption("media-right-margin", num_options, options)) + != NULL) + tmp.right = atof(val) * 72.0 / 2540.0; + if ((val = cupsGetOption("media-bottom-margin", num_options, options)) + != NULL) + tmp.bottom = atof(val) * 72.0 / 2540.0; + + if ((param->orientation == ROT_90) || (param->orientation == ROT_270)) + { // unrotate page + // NaN stays NaN + tmp.right = param->page.height - tmp.right; + tmp.top = param->page.width - tmp.top; + _cfPDFToPDFPageRect_rotate_move(&tmp, param->orientation, param->page.height, param->page.width); + } + else + { + tmp.right = param->page.width - tmp.right; + tmp.top = param->page.height - tmp.top; + _cfPDFToPDFPageRect_rotate_move(&tmp, param->orientation, param->page.width, param->page.height); + } + _cfPDFToPDFPageRect_set(¶m->page, &tmp); + + if ((val = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, "sides")) != + NULL && + strncmp(val, "two-sided-", 10) == 0) + param->duplex = true; + else if (is_true(cupsGetOption("Duplex", num_options, options))) + { + param->duplex = true; + param->set_duplex = true; + } + else if ((val = cupsGetOption("sides", num_options, options)) != NULL) + { + if ((strcasecmp(val, "two-sided-long-edge") == 0) || + (strcasecmp(val, "two-sided-short-edge") == 0)) + { + param->duplex = true; + param->set_duplex = true; + } + else if (strcasecmp(val, "one-sided") != 0) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unsupported sides value %s, using sides=one-sided!", + val); + } + } + + // default nup is 1 + nup = 1; + if (optGetInt("number-up", num_options, options, &nup)) + { + if (!_cfPDFToPDFNupParameters_possible(nup)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unsupported number-up value %d, using number-up=1!", + nup); + nup = 1; + } + _cfPDFToPDFNupParameters_preset(nup, &(param->nup)); + } + + if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL) + { + if (!_cfPDFToPDFParseNupLayout(val, &(param->nup))) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unsupported number-up-layout %s, using number-up-layout=lrtb!", + val); + param->nup.first = X; + param->nup.xstart = LEFT; + param->nup.ystart = TOP; + } + } + + if ((val = cupsGetOption("page-border", num_options, options)) != NULL) + { + if (!_cfPDFToPDFParseBorder(val, &(param->border))) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unsupported page-border value %s, using page-border=none!", + val); + param->border = NONE; + } + } + + if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL || + (val = cupsGetOption("output-order", num_options, options)) != NULL || + (val = cupsGetOption("page-delivery", num_options, options)) != NULL) + { + param->reverse = (strcasecmp(val, "Reverse") == 0 || + strcasecmp(val, "reverse-order") == 0); + } + else + { + param->reverse = cfIPPReverseOutput(printer_attrs, job_attrs); + } + + classification = getenv("CLASSIFICATION"); + if (classification) + strcpy(rawlabel, classification); + + if ((val = cupsGetOption("page-label", num_options, options)) != NULL) + { + if (strlen(rawlabel) > 0) strcat(rawlabel, " - "); + strcat(rawlabel, cupsGetOption("page-label", num_options, options)); + } + + char *rawptr = rawlabel; + char *cookedptr = cookedlabel; + while (*rawptr) + { + if (*rawptr < 32 || *rawptr > 126) + { + sprintf(cookedptr, "\\%03o", (unsigned int)*rawptr); + cookedptr += 4; + } + else + { + *cookedptr++ = *rawptr; + } + rawptr++; + } + *cookedptr = '\0'; + param->page_label = strdup(cookedlabel); + + if ((val = cupsGetOption("page-set", num_options, options)) != NULL) + { + if (strcasecmp(val, "even") == 0) + param->odd_pages = false; + else if (strcasecmp(val, "odd") == 0) + param->even_pages = false; + else if (strcasecmp(val, "all") != 0) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unsupported page-set value %s, using page-set=all!", + val); + } + } + + if ((val = cupsGetOption("page-ranges", num_options, options)) != NULL) + parseRanges(val, param->page_ranges); + if ((val = cupsGetOption("input-page-ranges", num_options, options)) != NULL) + parseRanges(val, param->input_page_ranges); + + if ((val = cupsGetOption("mirror", num_options, options)) != NULL || + (val = cupsGetOption("mirror-print", num_options, options)) != NULL) + param->mirror = is_true(val); + + param->booklet = CF_PDFTOPDF_BOOKLET_OFF; + if ((val = cupsGetOption("booklet", num_options, options)) != NULL) + { + if (strcasecmp(val, "shuffle-only") == 0) + param->booklet = CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE; + else if (is_true(val)) + param->booklet = CF_PDFTOPDF_BOOKLET_ON; + else if (!is_false(val)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unsupported booklet value %s, using booklet=off!", + val); + } + } + param->book_signature = -1; + if (optGetInt("booklet-signature", num_options, options, &(param->book_signature))) + { + if (param->book_signature == 0) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unsupported booklet-signature value, using booklet-signature=-1 (all)!", + val); + param->book_signature = -1; + } + } + + if ((val = cupsGetOption("position", num_options, options)) != NULL) + { + if (!parsePosition(val, &(param->xpos), &(param->ypos))) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unrecognized position value %s, using position=center!", + val); + param->xpos = CENTER; + param->ypos = CENTER; + } + } + + if (is_true(cupsGetOption("Collate", num_options, options))) + param->collate = true; + else if ((val = cupsGetOption("sheet-collate", num_options, options)) != NULL) + param->collate = (strcasecmp(val, "uncollated") != 0); + else if (((val = cupsGetOption("multiple-document-handling", + num_options, options)) != NULL && + (strcasecmp(val, "separate-documents-collated-copies") == 0 || + strcasecmp(val, "separate-documents-uncollated-copies") == 0 || + strcasecmp(val, "single-document") == 0 || + strcasecmp(val, "single-document-new-sheet") == 0)) || + (val = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, + "multiple-document-handling")) != + NULL) + { + param->collate = + (strcasecmp(val, "separate-documents-uncollated-copies") != 0); + } + +#if 0 + if ((val = cupsGetOption("scaling", num_options, options)) != 0) + { + scalint = atoi(val) * 0.01; + fitplot = true + } + else if (fitplot) + scaling = 1.0; + + if ((val = cupsGetOption("natural-scaling", num_options, options)) != 0) + naturalScaling = atoi(val) * 0.01; +#endif + + // Make pages a multiple of two (only considered when duplex is on). + // i.e. printer has hardware-duplex, but needs pre-inserted filler pages + // FIXME? pdftopdf also supports it as cmdline option (via checkFeature()) + param->even_duplex = + (param->duplex && + is_true(cupsGetOption("even-duplex", num_options, options))); + + param->auto_rotate = param->no_orientation; + if ((val = cupsGetOption("pdftopdfAutoRotate", + num_options, options)) != NULL || + (val = cupsGetOption("pdfAutoRotate", num_options, options)) != NULL) + param->auto_rotate = !is_false(val); + + if ((val = cupsGetOption("ipp-attribute-fidelity", num_options, options)) != + NULL) + { + if (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || + !strcasecmp(val, "on")) + param->fidelity = true; + } + + if (printer_attrs == NULL && !param->pagesize_requested && + param->booklet == CF_PDFTOPDF_BOOKLET_OFF && + param->nup.nupX == 1 && param->nup.nupY == 1) + param->cropfit = true; + + else if ((val = cupsGetOption("print-scaling", num_options, options)) != NULL) + { + // Standard IPP attribute + if (!strcasecmp(val, "auto")) + param->autoprint = true; + else if (!strcasecmp(val, "auto-fit")) + param->autofit = true; + else if (!strcasecmp(val, "fill")) + param->fillprint = true; + else if (!strcasecmp(val, "fit")) + param->fitplot = true; + else if (!strcasecmp(val, "none")) + param->cropfit = true; + else + param->autoprint = true; + } + else + { + // Legacy CUPS attributes + if ((val = cupsGetOption("fitplot", num_options, options)) == NULL) + { + if ((val = cupsGetOption("fit-to-page", num_options, options)) == NULL) + val = cupsGetOption("ipp-attribute-fidelity", num_options, options); + } + // TODO? pstops checks == "true", pdftops !is_false ... pstops says: + // fitplot only for PS (i.e. not for PDF, cmp. cgpdftopdf) + param->fitplot = (val && !is_false(val)); + + if ((val = cupsGetOption("fill", num_options, options)) != 0) + { + if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) + param->fillprint = true; + } + if ((val = cupsGetOption("crop-to-fit", num_options, options)) != NULL) + { + if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) + param->cropfit = 1; + } + if (!param->autoprint && !param->autofit && !param->fitplot && + !param->fillprint && !param->cropfit) + param->autoprint = true; + } + + // Certain features require a given page size for the page to be + // printed or all pages of the document being the same size. Here we + // set param.pagesize_requested so that the default page size is used + // when no size got specified by the user. + if (param->fitplot || param->fillprint || param->autoprint || param->autofit || + param->booklet != CF_PDFTOPDF_BOOKLET_OFF || + param->nup.nupX > 1 || param->nup.nupY > 1) + param->pagesize_requested = true; + + // + // Do we have to do the page logging in page_log? + // + // CUPS standard is that the last filter (not the backend, usually the + // printer driver) does page logging in the /var/log/cups/page_log file + // by outputting "PAGE: <# of current page> <# of copies>" to stderr. + // + // cfFilterPDFToPDF() would have to do this only for PDF printers as + // in this case cfFilterPDFToPDF() is the last filter, but some of + // the other filters are not able to do the logging because they do + // not have access to the number of pages of the file to be printed, + // so cfFilterPDFToPDF() overtakes their logging duty. + // + + // Check whether page logging is forced or suppressed by the options + + if ((val = cupsGetOption("pdf-filter-page-logging", + num_options, options)) != NULL) + { + if (strcasecmp(val, "auto") == 0) + { + param->page_logging = -1; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Automatic page logging selected by options."); + } + else if (is_true(val)) + { + param->page_logging = 1; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Forced page logging selected by options."); + } + else if (is_false(val)) + { + param->page_logging = 0; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Suppressed page logging selected by options."); + } + else + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Unsupported page logging setting \"pdf-filter-page-logging=%s\", using \"auto\"!", + val); + param->page_logging = -1; + } + } + + if (param->page_logging == -1) + { + // We determine whether to log pages or not + // using the output data MIME type. log pages only when the output is + // either pdf or PWG Raster + if (final_content_type && + (strcasestr(final_content_type, "/pdf") || + strcasestr(final_content_type, "/vnd.cups-pdf") || + strcasestr(final_content_type, "/pwg-raster"))) + param->page_logging = 1; + else + param->page_logging = 0; + + // If final_content_type is not clearly available we are not sure whether + // to log pages or not + if (!final_content_type || + final_content_type[0] == '\0') + param->page_logging = -1; + + if (doc->logfunc) + { + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Determined whether to log pages or not using output data type."); + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "final_content_type = %s => page_logging = %d", + final_content_type ? final_content_type : "NULL", + param->page_logging); + } + + if (param->page_logging == -1) + param->page_logging = 0; + } +} + +void +calculate(int num_options, + cups_option_t *options, + _cfPDFToPDFProcessingParameters *param, + char *final_content_type) +{ + const char *val; + bool hw_copies = false, + hw_collate = false; + + // Check options for caller's instructions about hardware copies/collate + if ((val = cupsGetOption("hardware-copies", + num_options, options)) != NULL) + // Use hardware copies according to the caller's instructions + hw_copies = is_true(val); + else + // Caller did not tell us whether the printer does Hardware copies + // or not, so we assume hardware copies on PDF printers, and software + // copies on other (usually raster) printers or if we do not know the + // final output format. + hw_copies = (final_content_type && + (strcasestr(final_content_type, "/pdf") || + strcasestr(final_content_type, "/vnd.cups-pdf"))); + + if (hw_copies) + { + if ((val = cupsGetOption("hardware-collate", + num_options, options)) != NULL) + // Use hardware collate according to the caller's instructions + hw_collate = is_true(val); + else + hw_collate = (final_content_type && + (strcasestr(final_content_type, "/pdf") || + strcasestr(final_content_type, "/vnd.cups-pdf") || + strcasestr(final_content_type, "/pwg-raster") || + strcasestr(final_content_type, "/urf") || + strcasestr(final_content_type, "/PCLm"))); + } + + if (param->reverse && param->duplex) + // Enable even_duplex or the first page may be empty. + param->even_duplex = true; // disabled later, if non-duplex + + if (param->num_copies == 1) + { + param->device_copies = 1; + // collate is never needed for a single copy + param->collate = false; // (does not make a big difference for us) + } + else if (hw_copies) + { // hw copy generation available + param->device_copies = param->num_copies; + if (param->collate) + { + param->device_collate = hw_collate; + if (!param->device_collate) + // printer can't hw collate -> we must copy collated in sw + param->device_copies = 1; + } // else: printer copies w/o collate and takes care of duplex/even_duplex + } + else + { // sw copies + param->device_copies = 1; + if (param->duplex) + { // &&(num_copies>1) + // sw collate + even_duplex must be forced to prevent copies on the + // back sides + param->collate = true; + param->device_collate = false; + } + } + + if (param->device_copies != 1) + param->num_copies = 1; + + if (param->duplex && + param->collate && !param->device_collate) + param->even_duplex = true; + + if (!param->duplex) + param->even_duplex = false; +} + +// reads from stdin into temporary file. returns FILE * or NULL on error +FILE * +copy_fd_to_temp(int infd, + pdftopdf_doc_t *doc) +{ + char buf[BUFSIZ]; + int n; + + int outfd = cupsCreateTempFd(NULL, NULL, buf, sizeof(buf)); + if (outfd < 0) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Can't create temporary file"); + return NULL; + } + + // remove name + unlink(buf); + + // copy stdin to the tmp file + while ((n = read(infd, buf, BUFSIZ)) > 0) + { + if (write(outfd, buf, n) != n) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Can't copy stdin to temporary file"); + close(outfd); + return NULL; + } + } + + if (lseek(outfd, 0, SEEK_SET) < 0) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Can't rewind temporary file"); + close(outfd); + return NULL; + } + + FILE *f; + if ((f = fdopen(outfd, "rb")) == 0) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPDFToPDF: Can't fdopen temporary file"); + close(outfd); + return NULL; + } + return f; +} + +// check whether a given file is empty +bool +is_empty(FILE *f) +{ + char buf[1]; + if (fread(buf, 1, 1, f) == 0) + return true; + rewind(f); + return false; +} + +int +cfFilterPDFToPDF(int inputfd, + int outputfd, + int inputseekable, + cf_filter_data_t *data, + void *parameters) +{ + pdftopdf_doc_t doc; + char *final_content_type = data->final_content_type; + FILE *inputfp, + *outputfp; + const char *t; + int streaming = 0; + size_t bytes; + char buf[BUFSIZ]; + cf_logfunc_t log = data->logfunc; + void *ld = data->logdata; + cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; + void *icd = data->iscanceleddata; + int num_options = 0; + cups_option_t *options = NULL; + + _cfPDFToPDFProcessingParameters param; + + param.job_id = data->job_id; + param.user = data->job_user; + param.title = data->job_title; + param.num_copies = data->copies; + param.copies_to_be_logged = data->copies; + param.page.width = param.page.height = 0; + param.page.left = param.page.bottom = -1; + param.page.right = param.page.top = -1; + + doc.logfunc = log; + doc.logdata = ld; + doc.iscanceledfunc = iscanceled; + doc.iscanceleddata = icd; + + num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); + + getParameters(data, num_options, options, ¶m, &doc); + calculate(num_options, options, ¶m, final_content_type); + +#ifdef DEBUG + _cfPDFToPDFProcessingParameters_dump(¶m, &doc); +#endif + + // If we are in streaming mode we only apply JCL and do not run the + // job through QPDL (so no page management, form flattening, + // page size/orientation adjustment, ...) + if ((t = cupsGetOption("filter-streaming-mode", + num_options, options)) != NULL && + (strcasecmp(t, "false") && strcasecmp(t, "off") && + strcasecmp(t, "no"))) + { + streaming = 1; + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Streaming mode: No PDF processing, only adding of JCL"); + } + + cupsFreeOptions(num_options, options); + + _cfPDFToPDF_PDFioProcessor proc; + + if ((inputseekable && inputfd > 0) || streaming) + { + if ((inputfp = fdopen(inputfd, "rb")) == NULL) + return 1; + } + else + { + if ((inputfp = copy_fd_to_temp(inputfd, &doc)) == NULL) + return 1; + } + + if (!streaming) + { + if (is_empty(inputfp)) + { + fclose(inputfp); + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Input is empty, outputting empty file."); + return 0; + } + + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Processing PDF input with PDFio: Page-ranges, page-set, number-up, booklet, size adjustment, ..."); + + // Load the PDF input data into PDFio + if (!_cfPDFToPDF_PDFioProcessor_load_file(&proc, inputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE, 1)) + { + fclose(inputfp); + return 1; + } + + // Process the PDF input data + if (!_cfProcessPDFToPDF(&proc, ¶m, &doc)) + return 2; + + // Pass information to subsequent filters via PDF comments + char *output[10]; + int output_len = 0; + + output[output_len++] = "% This file was generated by pdftopdf"; + + if (param.device_copies > 0) + { + char buf[256]; + snprintf(buf, sizeof(buf), "%d", param.device_copies); + output[output_len++] = strdup(buf); + + if (param.device_collate) + output[output_len++] = "%%PDFTOPDFCollate : true"; + else + output[output_len++] = "%%PDFTOPDFCollate : false"; + } + + _cfPDFToPDF_PDFioProcessor_set_comments(&proc, output, output_len); + } + + outputfp = fdopen(outputfd, "w"); + if (outputfp == NULL) + return 1; + + if (!streaming) + { + _cfPDFToPDF_PDFioProcessor_emit_file(&proc, outputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE); + } + else + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Passing on unchanged PDF data from input"); + while ((bytes = fread(buf, 1, sizeof(buf), inputfp)) > 0) + if (fwrite(buf, 1, bytes, outputfp) != bytes) + break; + fclose(inputfp); + } + + fclose(outputfp); + return 0; +} +// }}} diff --git a/cupsfilters/pdftopdf/pptypes-private.h b/cupsfilters/pdftopdf/pptypes-private.h new file mode 100644 index 00000000..c0aa7cd0 --- /dev/null +++ b/cupsfilters/pdftopdf/pptypes-private.h @@ -0,0 +1,80 @@ +// +// Copyright 2024 Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#ifndef _CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ +#define _CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ + +#include "pdftopdf-private.h" +#include + +typedef enum +{ + X, + Y +} pdftopdf_axis_e; + +typedef enum +{ + CENTER = 0, + LEFT = -1, + RIGHT = 1, + TOP = 1, + BOTTOM = -1 +} pdftopdf_position_e; + +void _cfPDFToPDFPositionDump(pdftopdf_position_e pos, pdftopdf_doc_t *doc); +void _cfPDFToPDFPositionAndAxisDump(pdftopdf_position_e pos, pdftopdf_axis_e axis, + pdftopdf_doc_t *doc); + +typedef enum +{ + ROT_0, + ROT_90, + ROT_180, + ROT_270, +} pdftopdf_rotation_e; + +void _cfPDFToPDFRotationDump(pdftopdf_rotation_e rot, pdftopdf_doc_t *doc); + +pdftopdf_rotation_e pdftopdf_rotation_add(pdftopdf_rotation_e lhs, pdftopdf_rotation_e rhs); +pdftopdf_rotation_e pdftopdf_rotation_sub(pdftopdf_rotation_e lhs, pdftopdf_rotation_e rhs); +pdftopdf_rotation_e pdftopdf_rotation_neg(pdftopdf_rotation_e rhs); + +typedef enum +{ + NONE = 0, + ONE_THIN = 2, + ONE_THICK = 3, + TWO_THIN = 4, + TWO_THICK = 5, + ONE = 0x02, + TWO = 0x04, + THICK = 0x01 +} pdftopdf_border_type_e; + +void _cfPDFToPDFBorderTypeDump(pdftopdf_border_type_e border, + pdftopdf_doc_t *doc); + +typedef struct +{ + float top, left, right, bottom; // i.e. margins + float width, height; +} _cfPDFToPDFPageRect; + +void _cfPDFToPDFPageRect_init(_cfPDFToPDFPageRect *rect); + +void _cfPDFToPDFPageRect_rotate_move(_cfPDFToPDFPageRect *rect, pdftopdf_rotation_e r, float pwidth, float pheight); + +void _cfPDFToPDFPageRect_scale(_cfPDFToPDFPageRect *rect, float mult); + +void _cfPDFToPDFPageRect_translate(_cfPDFToPDFPageRect *rect, float tx, float ty); + +void _cfPDFToPDFPageRect_set(_cfPDFToPDFPageRect *rect, const _cfPDFToPDFPageRect *rhs); + +void _cfPDFToPDFPageRect_dump(const _cfPDFToPDFPageRect *rect, pdftopdf_doc_t *doc); + +#endif // !_CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ diff --git a/cupsfilters/pdftopdf/pptypes.c b/cupsfilters/pdftopdf/pptypes.c new file mode 100644 index 00000000..2900345f --- /dev/null +++ b/cupsfilters/pdftopdf/pptypes.c @@ -0,0 +1,279 @@ +// +//Copyright 2024 Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#include "pptypes-private.h" +#include +#include +#include "cupsfilters/debug-internal.h" + +void +_cfPDFToPDFPositionDump(pdftopdf_position_e pos, + pdftopdf_doc_t *doc) // {{{ +{ + static const char *pstr[3] = {"Left/Bottom", "Center", "Right/Top"}; + if ((pos < LEFT) || (pos > RIGHT)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: (bad position: %d)", pos); + } + else + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: %s", pstr[pos+1]); + } +} +// }}} + +void +_cfPDFToPDFPositionAndAxisDump(pdftopdf_position_e pos, + pdftopdf_axis_e axis, + pdftopdf_doc_t *doc) // {{{ +{ + if ((pos < LEFT) || (pos > RIGHT)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Position %s: (bad position: %d)", + (axis == X) ? "X" : "Y", pos); + return; + } + + if (axis == X) + { + static const char *pxstr[3] = {"Left", "Center", "Right"}; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Position X: %s", pxstr[pos + 1]); + } + + else + { + static const char *pystr[3] = {"Bottom", "Center", "Top"}; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Position Y: %s", pystr[pos + 1]); + + } +} +// }}} + +void +_cfPDFToPDFRotationDump(pdftopdf_rotation_e rot, + pdftopdf_doc_t *doc) // {{{ +{ + static const char *rstr[4] = {"0 deg", "90 deg", "180 deg", "270 deg"}; // CCW + + if ((rot < ROT_0) || (rot > ROT_270)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Rotation(CCW): (bad rotation: %d)", rot); + } + + else + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Rotation(CCW): %s", rstr[rot]); + } +} +// }}} + +pdftopdf_rotation_e +pdftopdf_rotation_add(pdftopdf_rotation_e lhs, + pdftopdf_rotation_e rhs) // {{{ +{ + return (pdftopdf_rotation_e)(((int)lhs + (int)rhs) % 4); +} +// }}} + +pdftopdf_rotation_e +pdftopdf_rotation_sub(pdftopdf_rotation_e lhs, + pdftopdf_rotation_e rhs) // {{{ +{ + return (pdftopdf_rotation_e)((((int)lhs - (int)rhs) % 4 + 4) % 4); +} +// }}} + +pdftopdf_rotation_e +pdftopdf_rotation_neg(pdftopdf_rotation_e rhs) // {{{ +{ + return (pdftopdf_rotation_e)((4 - (int)rhs) % 4); +} +// }}} + +void +_cfPDFToPDFBorderTypeDump(pdftopdf_border_type_e border, + pdftopdf_doc_t *doc) // {{{ +{ + if ((border < NONE) || (border == 1) || (border > TWO_THICK)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Border: (bad border: %d)", border); + + } + else + { + static const char *bstr[6] = + {"None", NULL, "one thin", "one thick", "two thin", "two thick"}; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Border: %s", bstr[border]); + } +} +// }}} + +void +_cfPDFToPDFPageRect_init(_cfPDFToPDFPageRect *rect) // {{{ +{ + rect->top = NAN; + rect->left = NAN; + rect->right = NAN; + rect->bottom = NAN; + rect->width = NAN; + rect->height = NAN; +} +// {{{ + +void +swap_float(float *a, float *b) // {{{ +{ + float temp = *a; + *a = *b; + *b = temp; +} +// }}} + +void +_cfPDFToPDFPageRect_rotate_move(_cfPDFToPDFPageRect *rect, + pdftopdf_rotation_e r, + float pwidth, float pheight) // {{{ +{ + #if 1 + if (r >= ROT_180) + { + swap_float(&rect->top, &rect->bottom); + swap_float(&rect->left, &rect->right); + } + + if ((r == ROT_90) || (r == ROT_270)) + { + const float tmp = rect->bottom; + rect->bottom = rect->left; + rect->left = rect->top; + rect->top = rect->right; + rect->right = tmp; + + swap_float(&rect->width, &rect->height); + swap_float(&pwidth, &pheight); + } + + if ((r == ROT_90) || (r == ROT_180)) + { + rect->left = pwidth - rect->left; + rect->right = pwidth - rect->right; + } + + if ((r == ROT_270) || (r == ROT_180)) + { + rect->top = pheight - rect->top; + rect->bottom = pheight - rect->bottom; + } + + #else + switch(r) + { + case ROT_0: // no-op + break; + case ROT_90: + const float tmp0 = bottom; + bottom = left; + left = pheight - top; + top = right; + right = pheight - tmp0; + + swap_float(&width, &height); + break; + + case ROT_180: + const float tmp1 = left; + left = pwidth - right; + right = pwidth - tmp1; + + const float tmp2 = top; + top = pheight - bottom; + bottom = pheight - tmp2; + break; + + case ROT_270: + const float tmp3 = top; + top = pwidth - left; + left = bottom; + bottom = pwidth - right; + right = tmp3; + + swap_float(&width, &height); + break; + + } + #endif +} +// }}} + +void +_cfPDFToPDFPageRect_scale(_cfPDFToPDFPageRect *rect, + float mult) // {{{ +{ + if (mult == 1.0) + return; + + rect->bottom *= mult; + rect->left *= mult; + rect->top *= mult; + rect->right *= mult; + + rect->width *= mult; + rect->height *= mult; +} +// }}} + +void +_cfPDFToPDFPageRect_translate(_cfPDFToPDFPageRect *rect, + float tx, + float ty) // {{{ +{ + rect->left += tx; + rect->bottom += ty; + rect->right += tx; + rect->top += ty; +} +// }}} + +void +_cfPDFToPDFPageRect_set(_cfPDFToPDFPageRect *rect, + const _cfPDFToPDFPageRect *rhs) // {{{ +{ + if (!isnan(rhs->top)) + rect->top = rhs->top; + + if (!isnan(rhs->left)) + rect->left = rhs->left; + + if (!isnan(rhs->right)) + rect->right = rhs->right; + + if (!isnan(rhs->bottom)) + rect->bottom = rhs->bottom; +} +// }}} + +void +_cfPDFToPDFPageRect_dump(const _cfPDFToPDFPageRect *rect, + pdftopdf_doc_t *doc) // {{{ +{ + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: top: %f, left: %f, right: %f, bottom: %f, " + "width: %f, height: %f", + rect->top, rect->left, rect->right, rect->bottom, + rect->width, rect->height); + +} +// }}} diff --git a/cupsfilters/pwgtopdf.c b/cupsfilters/pwgtopdf.c new file mode 100644 index 00000000..632a5004 --- /dev/null +++ b/cupsfilters/pwgtopdf.c @@ -0,0 +1,1801 @@ +// +// PWG/Apple Raster to PDF filter function for libcupsfilters. +// +// Copyright 2010 by Neil 'Superna' Armstrong +// Copyright 2012 by Tobias Hoffmann +// Copyright 2014-2022 by Till Kamppeter +// Copyright 2017 by Sahil Arora +// Copyright 2024 by Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // ntohl + +#include +#include +#include + +#ifdef USE_LCMS1 +#include +#define cmsColorSpaceSignature icColorSpaceSignature +#define cmsSetLogErrorHandler cmsSetErrorHandler +#define cmsSigXYZData icSigXYZData +#define cmsSigLuvData icSigLuvData +#define cmsSigLabData icSigLabData +#define cmsSigYCbCrData icSigYCbCrData +#define cmsSigYxyData icSigYxyData +#define cmsSigRgbData icSigRgbData +#define cmsSigHsvData icSigHsvData +#define cmsSigHlsData icSigHlsData +#define cmsSigCmyData icSigCmyData +#define cmsSig3colorData icSig3colorData +#define cmsSigGrayData icSigGrayData +#define cmsSigCmykData icSigCmykData +#define cmsSig4colorData icSig4colorData +#define cmsSig2colorData icSig2colorData +#define cmsSig5colorData icSig5colorData +#define cmsSig6colorData icSig6colorData +#define cmsSig7colorData icSig7colorData +#define cmsSig8colorData icSig8colorData +#define cmsSig9colorData icSig9colorData +#define cmsSig10colorData icSig10colorData +#define cmsSig11colorData icSig11colorData +#define cmsSig12colorData icSig12colorData +#define cmsSig13colorData icSig13colorData +#define cmsSig14colorData icSig14colorData +#define cmsSig15colorData icSig15colorData +#define cmsSaveProfileToMem _cmsSaveProfileToMem +#else +#include +#endif + +#define DEFAULT_PDF_UNIT 72 // 1/72 inch + +#define PRE_COMPRESS + +// Compression method for providing data to PCLm Streams. +typedef enum compression_method_e +{ + DCT_DECODE = 0, + FLATE_DECODE, + RLE_DECODE +} compression_method_t; + +// Color conversion function +typedef unsigned char *(*convert_function)(unsigned char *src, + unsigned char *dst, + unsigned int pixels); + +// Bit conversion function +typedef unsigned char *(*bit_convert_function)(unsigned char *src, + unsigned char *dst, + unsigned int pixels); + +typedef struct pwgtopdf_doc_s // **** Document information **** +{ + cmsHPROFILE colorProfile; // ICC Profile to be applied to + // PDF + int cm_disabled; // Flag raised if color + // management is disabled + convert_function conversion_function; // Raster color conversion + // function + bit_convert_function bit_function; // Raster bit function + FILE *outputfp; // Temporary file, if any + cf_logfunc_t logfunc; // Logging function, NULL for no + // logging + void *logdata; // User data for logging + // function, can be NULL + cf_filter_iscanceledfunc_t iscanceledfunc; // Function returning 1 when + // job is canceled, NULL for not + // supporting stop on cancel + void *iscanceleddata; // User data for is-canceled + // function, can be NULL +} pwgtopdf_doc_t; + +struct pdf_info +{ + // PDFio-specific members + pdfio_file_t *pdf; // PDFio file handle + pdfio_obj_t *page; // PDFio page handle + + unsigned pagecount; + unsigned width; + unsigned height; + unsigned line_bytes; + unsigned bpp; + unsigned bpc; + unsigned pclm_num_strips; + unsigned pclm_strip_height_preferred; + unsigned *pclm_strip_height; // Dynamically allocated array in C + unsigned *pclm_strip_height_supported; // Dynamically allocated array + compression_method_t *pclm_compression_method_preferred; + size_t num_compression_methods; + char **pclm_source_resolution_supported; // Array of dynamically allocated strings + char *pclm_source_resolution_default; // Pointer to dynamically allocated string + char *pclm_raster_back_side; // Pointer to dynamically allocated string + unsigned char **pclm_strip_data; // Array of pointers to raw data (buffers) + char *render_intent; // Pointer to dynamically allocated string + cups_cspace_t color_space; + unsigned char *page_data; // Pointer to raw page data + double page_width, page_height; + cf_filter_out_format_t outformat; +}; + +// PDF color conversion function +typedef void (*pdf_convert_function)(struct pdf_info *info, + pwgtopdf_doc_t *doc); + + +void +init_pdf_info(struct pdf_info *info, + size_t num_methods, + size_t num_strips_supported) +{ + // Initialize primitive types + info->pagecount = 0; + info->width = 0; + info->height = 0; + info->line_bytes = 0; + info->bpp = 0; + info->bpc = 0; + info->pclm_num_strips = 0; + info->pclm_strip_height_preferred = 16; // Default strip height + info->page_width = 0; + info->page_height = 0; + info->outformat = CF_FILTER_OUT_FORMAT_PDF; + + // Allocate memory for pclm_strip_height (for strip height handling) + info->pclm_strip_height = (unsigned *)malloc(num_strips_supported * sizeof(unsigned)); + if (info->pclm_strip_height) + { + for (size_t i = 0; i < num_strips_supported; ++i) + { + info->pclm_strip_height[i] = 0; // Initialize to 0 or a specific value as needed + } + } + + // Allocate memory for pclm_strip_height_supported + info->pclm_strip_height_supported = (unsigned *)malloc(num_strips_supported * sizeof(unsigned)); + if (info->pclm_strip_height_supported) + { + for (size_t i = 0; i < num_strips_supported; ++i) + { + info->pclm_strip_height_supported[i] = 16; // Initialize to default value + } + } + + // Allocate memory for multiple compression methods + info->num_compression_methods = num_methods; + info->pclm_compression_method_preferred = (compression_method_t *)malloc(num_methods * sizeof(compression_method_t)); + if (info->pclm_compression_method_preferred) + { + for (size_t i = 0; i < num_methods; ++i) + { + info->pclm_compression_method_preferred[i] = 0; // Initialize to default or specific compression method + } + } + + info->pclm_source_resolution_default = (char *)malloc(64 * sizeof(char)); + if (info->pclm_source_resolution_default) + { + strcpy(info->pclm_source_resolution_default, ""); // Initialize to empty string + } + + info->pclm_raster_back_side = (char *)malloc(64 * sizeof(char)); + if (info->pclm_raster_back_side) + { + strcpy(info->pclm_raster_back_side, ""); // Initialize to empty string + } + + info->render_intent = (char *)malloc(64 * sizeof(char)); + if (info->render_intent) + { + strcpy(info->render_intent, ""); // Initialize to empty string + } + + info->pclm_source_resolution_supported = NULL; + info->pclm_strip_data = NULL; // Assuming this will be dynamically allocated elsewhere + + info->color_space = CUPS_CSPACE_K; // Default color space + info->page_data = NULL; // Will be allocated when needed + + info->pdf = NULL; // Initialize to NULL, will be set when opening a file + info->page = NULL; // Initialize to NULL, will be set when reading a page +} + +// Freeing the dynamically allocated memory +void free_pdf_info(struct pdf_info *info) +{ + if (info->pclm_strip_height) + { + free(info->pclm_strip_height); + info->pclm_strip_height = NULL; + } + + if (info->pclm_strip_height_supported) + { + free(info->pclm_strip_height_supported); + info->pclm_strip_height_supported = NULL; + } + + if (info->pclm_compression_method_preferred) + { + free(info->pclm_compression_method_preferred); + info->pclm_compression_method_preferred = NULL; + } + + // Free dynamically allocated strings + if (info->pclm_source_resolution_default) + { + free(info->pclm_source_resolution_default); + info->pclm_source_resolution_default = NULL; + } + + if (info->pclm_raster_back_side) + { + free(info->pclm_raster_back_side); + info->pclm_raster_back_side = NULL; + } + + if (info->render_intent) + { + free(info->render_intent); + info->render_intent = NULL; + } + + // Free any other dynamically allocated memory as necessary + if (info->pclm_strip_data) + { + free(info->pclm_strip_data); // Assuming this array will be dynamically allocated elsewhere + info->pclm_strip_data = NULL; + } + + if (info->page_data) + { + free(info->page_data); + info->page_data = NULL; + } +} + +// +// Bit conversion functions +// +static unsigned char * +invert_bits(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + unsigned int i; + + // Invert black to grayscale... + for (i = pixels, dst = src; i > 0; i --, dst ++) + *dst = ~*dst; + + return (dst); +} + +static unsigned char * +no_bit_conversion(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + return (src); +} + +// +// Color conversion functions +// + +static unsigned char * +rgb_to_cmyk(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + cfImageRGBToCMYK(src, dst, pixels); + return (dst); +} + + +static unsigned char * +white_to_cmyk(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + cfImageWhiteToCMYK(src, dst, pixels); + return (dst); +} + + +static unsigned char * +cmyk_to_rgb(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + cfImageCMYKToRGB(src, dst, pixels); + return (dst); +} + + +static unsigned char * +white_to_rgb(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + cfImageWhiteToRGB(src, dst, pixels); + return (dst); +} + + +static unsigned char * +rgb_to_white(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + cfImageRGBToWhite(src, dst, pixels); + return (dst); +} + + +static unsigned char * +cmyk_to_white(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + cfImageCMYKToWhite(src, dst, pixels); + return (dst); +} + + +static unsigned char * +no_color_conversion(unsigned char *src, + unsigned char *dst, + unsigned int pixels) +{ + return (src); +} + +// +// 'split_strings()' - Split a string to a vector of strings given some +// delimiters +// +// O - std::vector of std::string after splitting +// I - input string to be split +// I - string containing delimiters +// +// Function to split strings by delimiters + +char** +split_strings(const char *str, + const char *delimiters, + int *size) +{ + if (delimiters == NULL || strlen(delimiters) == 0) + delimiters = ","; + + + int capacity = 10; + char **result = malloc(capacity * sizeof(char *)); + + char *value = malloc(strlen(str) + 1); + + int token_count = 0; + int index = 0; + bool push_flag = false; + + for (size_t i = 0; i < strlen(str); i++) + { + if (strchr(delimiters, str[i]) != NULL) + { + if (push_flag && index > 0) + { + value[index] = '\0'; + result[token_count] = malloc(strlen(value) + 1); + strcpy(result[token_count], value); + token_count++; + + if (token_count >= capacity) + { + capacity *= 2; + result = realloc(result, capacity * sizeof(char *)); + } + + index = 0; + push_flag = false; + } + } + else + { + value[index++] = str[i]; + push_flag = true; + } + } + + if (push_flag && index > 0) + { + value[index] = '\0'; + result[token_count] = malloc(strlen(value) + 1); + strcpy(result[token_count], value); + token_count++; + } + + *size = token_count; + + free(value); + return result; +} + +// +// 'num_digits()' - Calculates the number of digits in an integer +// +// O - number of digits in the input integer +// I - the integer whose digits needs to be calculated +// + +static int +num_digits(int n) +{ + if (n == 0) + return (1); + int digits = 0; + while (n) + { + ++digits; + n /= 10; + } + return (digits); +} + +// +// 'int_to_fwstring()' - Convert a number to fixed width string by padding +// with zeroes +// O - converted string +// I - the integee which needs to be converted to string +// I - width of string required +// + +char* +int_to_fwstring(int n, int width) +{ + int num_zeroes = width - num_digits(n); + if (num_zeroes < 0) + num_zeroes = 0; + + int result_length = num_zeroes + num_digits(n) + 1; + char *result = malloc(result_length * sizeof(char)); + + for (int i = 0; i < num_zeroes; i++) + result[i] = '0'; + + sprintf(result + num_zeroes, "%d", n); + return result; +} + +static int +create_pdf_file(struct pdf_info *info, + const cf_filter_out_format_t outformat) +{ + if (!info || !info->pdf) + return 1; // Error handling + + pdfio_file_t *temp = pdfioFileCreate(pdfioFileGetName(info->pdf), NULL, NULL, NULL, NULL, NULL); + + info->pdf = temp; + info->outformat = outformat; + + return 0; +} + +static pdfio_rect_t +make_real_box(double x1, + double y1, + double x2, + double y2) +{ + pdfio_rect_t ret; + + ret.x1 = x1; + ret.y1 = y1; + ret.x2 = x2; + ret.y2 = y2; + + return (ret); +} + +// +// PDF color conversion functons... +// + +static void +modify_pdf_color(struct pdf_info *info, + int bpp, + int bpc, + convert_function fn, + pwgtopdf_doc_t *doc) +{ + unsigned old_bpp = info->bpp; + unsigned old_bpc = info->bpc; + double old_ncolor = old_bpp / old_bpc; + + unsigned old_line_bytes = info->line_bytes; + + double new_ncolor = bpp / bpc; + + info->line_bytes = (unsigned)old_line_bytes * (new_ncolor / old_ncolor); + info->bpp = bpp; + info->bpc = bpc; + doc->conversion_function = fn; +} + +static void +convert_pdf_no_conversion(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + doc->conversion_function = no_color_conversion; + doc->bit_function = no_bit_conversion; +} + + +static void +convert_pdf_cmyk_8_to_white_8(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + modify_pdf_color(info, 8, 8, cmyk_to_white, doc); + doc->bit_function = no_bit_conversion; +} + + +static void +convert_pdf_rgb_8_to_white_8(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + modify_pdf_color(info, 8, 8, rgb_to_white, doc); + doc->bit_function = no_bit_conversion; +} + + +static void +convert_pdf_cmyk_8_to_rgb_8(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + modify_pdf_color(info, 24, 8, cmyk_to_rgb, doc); + doc->bit_function = no_bit_conversion; +} + + +static void +convert_pdf_white_8_to_rgb_8(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + modify_pdf_color(info, 24, 8, white_to_rgb, doc); + doc->bit_function = invert_bits; +} + + +static void +convert_pdf_rgb_8_to_cmyk_8(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + modify_pdf_color(info, 32, 8, rgb_to_cmyk, doc); + doc->bit_function = no_bit_conversion; +} + + +static void +convert_pdf_white_8_to_cmyk_8(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + modify_pdf_color(info, 32, 8, white_to_cmyk, doc); + doc->bit_function = invert_bits; +} + + +static void +convert_pdf_invert_colors(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + doc->conversion_function = no_color_conversion; + doc->bit_function = invert_bits; +} + +// +// Create an '/ICCBased' array and embed a previously +// set ICC Profile in the PDF +// +// TODO: HOW IS THIS cmsHPROFILE CALLED +pdfio_obj_t * +embed_icc_profile(pdfio_file_t *pdf, pwgtopdf_doc_t *doc) +{ + pdfio_dict_t *streamdict; + pdfio_obj_t *icc_stream; + char *n_value = NULL; + char *alternate_cs = NULL; + unsigned char *buff; + cmsColorSpaceSignature css; + +#ifdef USE_LCMS1 + size_t profile_size; +#else + unsigned int profile_size; +#endif + + // Determine color space signature + css = cmsGetColorSpace(doc->colorProfile); + + // Determine color component number for /ICCBased array + switch (css) + { + case cmsSigGrayData: + n_value = "1"; + alternate_cs = "/DeviceGray"; + break; + case cmsSigRgbData: + n_value = "3"; + alternate_cs = "/DeviceRGB"; + break; + case cmsSigCmykData: + n_value = "4"; + alternate_cs = "/DeviceCMYK"; + break; + default: + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Failed to embed ICC Profile."); + return NULL; + } + + cmsSaveProfileToMem(doc->colorProfile, NULL, &profile_size); + buff = (unsigned char *)calloc(profile_size, sizeof(unsigned char)); + cmsSaveProfileToMem(doc->colorProfile, buff, &profile_size); + + streamdict = pdfioDictCreate(pdf); + pdfioDictSetName(streamdict, "Alternate", alternate_cs); + pdfioDictSetName(streamdict, "N", n_value); + + icc_stream = pdfioFileCreateObj(pdf, streamdict); + + if (!icc_stream) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Failed to create ICC profile stream."); + free(buff); + return NULL; + } + + free(buff); + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: ICC Profile embedded in PDF."); + + return icc_stream; +} + +pdfio_obj_t* +embed_srgb_profile(pdfio_file_t *pdf, + pwgtopdf_doc_t *doc) +{ + pdfio_obj_t *iccbased_reference; + + doc->colorProfile = cmsCreate_sRGBProfile(); + iccbased_reference = embed_icc_profile(pdf, doc); + + return iccbased_reference; +} + +// +// Calibration function for non-Lab PDF color spaces +// Requires white point data, and if available, gamma or matrix numbers. +// +// Output: +// [/'color_space' +// << /Gamma ['gamma[0]'...'gamma[n]'] +// /WhitePoint ['wp[0]' 'wp[1]' 'wp[2]'] +// /Matrix ['matrix[0]'...'matrix[n*n]'] +// >> +// ] + +pdfio_array_t* +get_calibration_array(pdfio_file_t *pdf, + const char *color_space, + double wp[], + double gamma[], + double matrix[], + double bp[]) +{ + if ((!strcmp("/CalGray", color_space) && matrix != NULL) || wp == NULL) + return NULL; + + pdfio_array_t *calibration_array = pdfioArrayCreate(pdf); + if (!calibration_array) + return NULL; + + pdfioArrayAppendName(calibration_array, color_space); + + pdfio_dict_t *calibration_dict = pdfioDictCreate(pdf); + + if (wp != NULL) + { + pdfio_array_t *white_point_array = pdfioArrayCreate(pdf); + pdfioArrayAppendNumber(white_point_array, wp[0]); + pdfioArrayAppendNumber(white_point_array, wp[1]); + pdfioArrayAppendNumber(white_point_array, wp[2]); + pdfioDictSetArray(calibration_dict, "WhitePoint", white_point_array); + } + + if (!strcmp("/CalGray", color_space) && gamma != NULL) + pdfioDictSetNumber(calibration_dict, "Gamma", gamma[0]); + + else if (!strcmp("/CalRGB", color_space) && gamma != NULL) + { + pdfio_array_t *gamma_array = pdfioArrayCreate(pdf); + pdfioArrayAppendNumber(gamma_array, gamma[0]); + pdfioArrayAppendNumber(gamma_array, gamma[1]); + pdfioArrayAppendNumber(gamma_array, gamma[2]); + pdfioDictSetArray(calibration_dict, "Gamma", gamma_array); + } + + if (bp != NULL) + { + pdfio_array_t *black_point_array = pdfioArrayCreate(pdf); + pdfioArrayAppendNumber(black_point_array, bp[0]); + pdfioArrayAppendNumber(black_point_array, bp[1]); + pdfioArrayAppendNumber(black_point_array, bp[2]); + pdfioDictSetArray(calibration_dict, "BlackPoint", black_point_array); + } + + if (!strcmp("CalRGB", color_space) && matrix != NULL) + { + pdfio_array_t *matrix_array = pdfioArrayCreate(pdf); + for (int i = 0; i < 9; i++) + pdfioArrayAppendNumber(matrix_array, matrix[i]); + pdfioDictSetArray(calibration_dict, "Matrix", matrix_array); + } + + pdfioArrayAppendDict(calibration_array, calibration_dict); + return calibration_array; +} + + +pdfio_array_t* +get_cal_rgb_array(pdfio_file_t *pdf, + double wp[3], + double gamma[3], + double matrix[9], + double bp[3]) +{ + pdfio_array_t *ret = get_calibration_array(pdf, "CalRGB", wp, gamma, matrix, + bp); + return (ret); +} + +pdfio_array_t* +get_cal_gray_array(pdfio_file_t *pdf, + double wp[3], + double gamma[1], + double bp[3]) +{ + pdfio_array_t *ret = get_calibration_array(pdf, "CalGray", wp, gamma, 0, bp); + return (ret); +} + +// +// 'make_pclm_strips()' - Return an std::vector of QPDFObjectHandle, each +// containing the stream data of the various strips +// which make up a PCLm page. +// +// O - std::vector of QPDFObjectHandle +// I - QPDF object +// I - number of strips per page +// I - std::vector of std::shared_ptr containing data for each strip +// I - strip width +// I - strip height +// I - color space +// I - bits per component +// I - document information +// + +pdfio_stream_t** +make_pclm_strips(pdfio_file_t *pdf, + unsigned num_strips, + unsigned char **strip_data, + compression_method_t *compression_methods, + unsigned width, unsigned *strip_height, + cups_cspace_t cs, + unsigned bpc, + pwgtopdf_doc_t *doc) +{ + pdfio_stream_t **ret = (pdfio_stream_t **)malloc(num_strips * sizeof(pdfio_stream_t *)); + pdfio_dict_t *dict; + const char *color_space_name; + unsigned components = 0; + + switch (cs) + { + case CUPS_CSPACE_K: + case CUPS_CSPACE_SW: + color_space_name = "DeviceGray"; + components = 1; + break; + case CUPS_CSPACE_RGB: + case CUPS_CSPACE_SRGB: + case CUPS_CSPACE_ADOBERGB: + color_space_name = "DeviceRGB"; + components = 3; + break; + default: + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Color space not supported."); + return NULL; + } + + for (size_t i = 0; i < num_strips; i++) + { + dict = pdfioDictCreate(pdf); + + pdfioDictSetName(dict, "Type", "XObject"); + pdfioDictSetName(dict, "Subtype", "Image"); + pdfioDictSetNumber(dict, "Width", width); + pdfioDictSetNumber(dict, "BitsPerComponent", bpc); + pdfioDictSetName(dict, "ColorSpace", color_space_name); + pdfioDictSetNumber(dict, "Height", strip_height[i]); + + pdfio_obj_t *streamObj = pdfioFileCreateObj(pdf, dict); + ret[i] = pdfioObjCreateStream(streamObj, PDFIO_FILTER_NONE); + + compression_method_t compression = compression_methods[0]; + for (unsigned j = 0; j < num_strips; j++) + compression = compression > compression_methods[j] ? compression : compression_methods[j]; + + + if (compression == FLATE_DECODE) + { + pdfioStreamWrite(ret[i], strip_data[i], strip_height[i] * width * components); + pdfioDictSetName(dict, "Filter", "FlateDecode"); + } + else if (compression == RLE_DECODE) + { + pdfioStreamWrite(ret[i], strip_data[i], strip_height[i] * width * components); + pdfioDictSetName(dict, "Filter", "RunLengthDecode"); + } + else if (compression == DCT_DECODE) + { + pdfioStreamWrite(ret[i], strip_data[i], strip_height[i] * width * components); + pdfioDictSetName(dict, "Filter", "DCTDecode"); + } + } + return ret; +} + +pdfio_obj_t* +make_image(pdfio_file_t *pdf, + unsigned char *page_data, + int data_size, + unsigned width, + unsigned height, + const char *render_intent, + cups_cspace_t cs, + unsigned bpc, + pwgtopdf_doc_t *doc) +{ + pdfio_dict_t *dict = pdfioDictCreate(pdf); + pdfio_obj_t *image_obj; + pdfio_obj_t *icc_ref; + int use_blackpoint = 0; + + pdfioDictSetName(dict, "Type", "XObject"); + pdfioDictSetName(dict, "Subtype", "Image"); + pdfioDictSetNumber(dict, "Width", width); + pdfioDictSetNumber(dict, "Height", height); + pdfioDictSetNumber(dict, "BitsPerComponent", bpc); + + if (!doc->cm_disabled && render_intent) + { + if (strcmp(render_intent, "Perceptual") == 0) + pdfioDictSetName(dict, "Intent", "Perceptual"); + else if (strcmp(render_intent, "Absolute") == 0) + pdfioDictSetName(dict, "Intent", "AbsoluteColorimetric"); + else if (strcmp(render_intent, "Relative") == 0) + pdfioDictSetName(dict, "Intent", "RelativeColorimetric"); + else if (strcmp(render_intent, "Saturation") == 0) + pdfioDictSetName(dict, "Intent", "Saturation"); + else if (strcmp(render_intent, "RelativeBpc") == 0) + { + pdfioDictSetName(dict, "Intent", "RelativeColorimetric"); + use_blackpoint = 1; + } + } + + if (doc->colorProfile != NULL && !doc->cm_disabled) + { + icc_ref = embed_icc_profile(pdf, doc); + pdfioDictSetObj(dict, "ColorSpace", icc_ref); + } + else if (!doc->cm_disabled) + { + switch (cs) + { + case CUPS_CSPACE_DEVICE1: + case CUPS_CSPACE_DEVICE2: + case CUPS_CSPACE_DEVICE3: + case CUPS_CSPACE_DEVICE4: + case CUPS_CSPACE_DEVICE5: + case CUPS_CSPACE_DEVICE6: + case CUPS_CSPACE_DEVICE7: + case CUPS_CSPACE_DEVICE8: + case CUPS_CSPACE_DEVICE9: + case CUPS_CSPACE_DEVICEA: + case CUPS_CSPACE_DEVICEB: + case CUPS_CSPACE_DEVICEC: + case CUPS_CSPACE_DEVICED: + case CUPS_CSPACE_DEVICEE: + case CUPS_CSPACE_DEVICEF: + pdfioDictSetName(dict, "ColorSpace", "DeviceCMYK"); + break; + case CUPS_CSPACE_K: + pdfioDictSetName(dict, "ColorSpace", "DeviceGray"); + break; + case CUPS_CSPACE_SW: + if (use_blackpoint) + pdfioDictSetArray(dict, "ColorSpace", get_cal_gray_array(pdf, cfCmWhitePointSGray(), + cfCmGammaSGray(), + cfCmBlackPointDefault())); + + else + pdfioDictSetArray(dict, "ColorSpace", get_cal_gray_array(pdf, cfCmWhitePointSGray(), + cfCmGammaSGray(), 0)); + break; + case CUPS_CSPACE_CMYK: + pdfioDictSetName(dict, "ColorSpace", "DeviceCMYK"); + break; + case CUPS_CSPACE_RGB: + pdfioDictSetName(dict, "ColorSpace", "DeviceRGB"); + break; + case CUPS_CSPACE_SRGB: + icc_ref = embed_srgb_profile(pdf, doc); + if(icc_ref != NULL) + pdfioDictSetObj(dict, "ColorSpace", icc_ref); + else + pdfioDictSetName(dict, "ColorSpace", "DeviceRGB"); + break; + case CUPS_CSPACE_ADOBERGB: + if (use_blackpoint) + pdfioDictSetArray(dict, "ColorSpace", get_cal_rgb_array(pdf, cfCmWhitePointAdobeRGB(), + cfCmGammaAdobeRGB(), + cfCmMatrixAdobeRGB(), + cfCmBlackPointDefault())); + else + pdfioDictSetArray(dict, "ColorSpace", get_cal_rgb_array(pdf, cfCmWhitePointAdobeRGB(), + cfCmGammaAdobeRGB(), + cfCmMatrixAdobeRGB(), 0)); + break; + default: + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Color space not supported."); + return NULL; + } + } + else if(doc->cm_disabled) + { + switch(cs) + { + case CUPS_CSPACE_K: + case CUPS_CSPACE_SW: + pdfioDictSetName(dict, "ColorSpace", "DeviceGray"); + break; + case CUPS_CSPACE_RGB: + case CUPS_CSPACE_SRGB: + case CUPS_CSPACE_ADOBERGB: + pdfioDictSetName(dict, "ColorSpace", "DeviceRGB"); + break; + case CUPS_CSPACE_DEVICE1: + case CUPS_CSPACE_DEVICE2: + case CUPS_CSPACE_DEVICE3: + case CUPS_CSPACE_DEVICE4: + case CUPS_CSPACE_DEVICE5: + case CUPS_CSPACE_DEVICE6: + case CUPS_CSPACE_DEVICE7: + case CUPS_CSPACE_DEVICE8: + case CUPS_CSPACE_DEVICE9: + case CUPS_CSPACE_DEVICEA: + case CUPS_CSPACE_DEVICEB: + case CUPS_CSPACE_DEVICEC: + case CUPS_CSPACE_DEVICED: + case CUPS_CSPACE_DEVICEE: + case CUPS_CSPACE_DEVICEF: + case CUPS_CSPACE_CMYK: + pdfioDictSetName(dict, "ColorSpace", "DeviceCMYK"); + break; + default: + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Color space not supported."); + return NULL; + } + } + else + return NULL; + + image_obj = pdfioFileCreateObj(pdf, dict); + +#ifdef PRE_COMPRESS + uLongf compressed_size = compressBound(data_size); + unsigned char *compressed_data = (unsigned char *)malloc(compressed_size); + if (compress(compressed_data, &compressed_size, page_data, data_size) != Z_OK) + { + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, + "cfFilterPWGToPDF: Compression failed."); + free(compressed_data); + return NULL; + } + + pdfio_stream_t *stream = pdfioObjCreateStream(image_obj, PDFIO_FILTER_NONE); + pdfioStreamWrite(stream, compressed_data, compressed_size); + pdfioStreamClose(stream); + + free(compressed_data); +#else + pdfio_stream_t *stream = pdfioStreamCreate(pdf, image_obj); + pdfioStreamWrite(stream, page_data, page_data_size); + pdfioStreamClose(stream); +#endif + return image_obj; +} + +static int +finish_page(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + pdfio_obj_t *image_obj; + char content[1024]; + size_t content_length = 0; + + if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) + { + if (!info->page_data) + return 0; + + image_obj = make_image(info->pdf, info->page_data, strlen(content), info->width, info->height, + info->render_intent, info->color_space, info->bpc, doc); + if (!image_obj) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Unable to load image data"); + return 1; + } + + pdfio_dict_t *resources = pdfioDictCreate(info->pdf); + pdfioDictSetObj(resources, "XObject", image_obj); + } + else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) + { + if (info->pclm_num_strips == 0) + return 0; + + for (size_t i = 0; i < info->pclm_num_strips; i++) + { + if (!info->pclm_strip_data[i]) + return 0; + } + } + + if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) + { + content_length += snprintf(content + content_length, sizeof(content) - content_length, + "%.2f 0 0 %.2f 0 0 cm\n", info->page_width, info->page_height); + content_length += snprintf(content + content_length, sizeof(content) - content_length, "/I Do\n"); + } + else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) + { + double d = DEFAULT_PDF_UNIT / atoi(info->pclm_source_resolution_default); + content_length += snprintf(content + content_length, sizeof(content) - content_length, + "%.2f 0 0 %.2f 0 0 cm\n", d, d); + + for (unsigned i = 0; i < info->pclm_num_strips; i++) + { + unsigned yAnchor = info->height - info->pclm_strip_height[i]; + content_length += snprintf(content + content_length, sizeof(content) - content_length, + "/P <> BDC q\n%.2f 0 0 %.2f 0 %u cm\n/Image%d Do Q\n", + (double)info->width, (double)info->pclm_strip_height[i], yAnchor, i); + } + } + + pdfio_stream_t *page_content = pdfioObjCreateStream(image_obj, PDFIO_FILTER_NONE); + pdfioStreamWrite(page_content, content, content_length); + pdfioStreamClose(page_content); + + info->page_data = NULL; + memset(info->pclm_strip_data, 0, sizeof(info->pclm_strip_data)); + + return 0; +} + +// +// Perform modifications to PDF if color space conversions are needed +// + +int prepare_pdf_page(struct pdf_info *info, + unsigned width, + unsigned height, + unsigned bpl, + unsigned bpp, + unsigned bpc, + const char *render_intent, + cups_cspace_t color_space, + pwgtopdf_doc_t *doc) +{ + +#define IMAGE_CMYK_8 (bpp == 32 && bpc == 8) +#define IMAGE_CMYK_16 (bpp == 64 && bpc == 16) +#define IMAGE_RGB_8 (bpp == 24 && bpc == 8) +#define IMAGE_RGB_16 (bpp == 48 && bpc == 16) +#define IMAGE_WHITE_1 (bpp == 1 && bpc == 1) +#define IMAGE_WHITE_8 (bpp == 8 && bpc == 8) +#define IMAGE_WHITE_16 (bpp == 16 && bpc == 16) + + int error = 0; + pdf_convert_function fn = convert_pdf_no_conversion; + cmsColorSpaceSignature css; + + // Register available raster information into the PDF + info->width = width; + info->height = height; + info->line_bytes = bpl; + info->bpp = bpp; + info->bpc = bpc; + info->render_intent = strdup(render_intent); + info->color_space = color_space; + + if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) + { + info->pclm_num_strips = + (height / info->pclm_strip_height_preferred) + + (height % info->pclm_strip_height_preferred ? 1 : 0); + + info->pclm_strip_height = (unsigned *)malloc(info->pclm_num_strips * sizeof(unsigned)); + info->pclm_strip_data = (unsigned char **)malloc(info->pclm_num_strips * sizeof(unsigned char *)); + + for (size_t i = 0; i < info->pclm_num_strips; i++) + { + info->pclm_strip_height[i] = + info->pclm_strip_height_preferred < height ? + info->pclm_strip_height_preferred : height; + height -= info->pclm_strip_height[i]; + } + } + + if (color_space == CUPS_CSPACE_K) + fn = convert_pdf_invert_colors; + + if (doc->colorProfile != NULL) + { + css = cmsGetColorSpace(doc->colorProfile); + + // Convert image and PDF color space to an embedded ICC Profile color + // space + switch (css) + { + // Convert PDF to Grayscale when using a gray profile + case cmsSigGrayData: + if (color_space == CUPS_CSPACE_CMYK) + fn = convert_pdf_cmyk_8_to_white_8; + else if (color_space == CUPS_CSPACE_RGB) + fn = convert_pdf_rgb_8_to_white_8; + else + fn = convert_pdf_invert_colors; + info->color_space = CUPS_CSPACE_K; + break; + + // Convert PDF to RGB when using an RGB profile + case cmsSigRgbData: + if (color_space == CUPS_CSPACE_CMYK) + fn = convert_pdf_cmyk_8_to_rgb_8; + else if (color_space == CUPS_CSPACE_K) + fn = convert_pdf_white_8_to_rgb_8; + info->color_space = CUPS_CSPACE_RGB; + break; + + // Convert PDF to RGB when using an RGB profile + case cmsSigCmykData: + if (color_space == CUPS_CSPACE_RGB) + fn = convert_pdf_rgb_8_to_cmyk_8; + else if (color_space == CUPS_CSPACE_K) + fn = convert_pdf_white_8_to_cmyk_8; + info->color_space = CUPS_CSPACE_CMYK; + break; + + default: + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Unable to convert PDF from profile."); + doc->colorProfile = NULL; + error = 1; + } + } + else if (!doc->cm_disabled) + { + switch (color_space) + { + case CUPS_CSPACE_CMYK: + if (IMAGE_RGB_8) + fn = convert_pdf_rgb_8_to_cmyk_8; + else if (IMAGE_RGB_16) + fn = convert_pdf_no_conversion; + else if (IMAGE_WHITE_8) + fn = convert_pdf_white_8_to_cmyk_8; + else if (IMAGE_WHITE_16) + fn = convert_pdf_no_conversion; + break; + // Convert image to RGB + case CUPS_CSPACE_ADOBERGB: + case CUPS_CSPACE_RGB: + case CUPS_CSPACE_SRGB: + if (IMAGE_CMYK_8) + fn = convert_pdf_cmyk_8_to_rgb_8; + else if (IMAGE_CMYK_16) + fn = convert_pdf_no_conversion; + else if (IMAGE_WHITE_8) + fn = convert_pdf_white_8_to_rgb_8; + else if (IMAGE_WHITE_16) + fn = convert_pdf_no_conversion; + break; + // Convert image to Grayscale + case CUPS_CSPACE_SW: + case CUPS_CSPACE_K: + if (IMAGE_CMYK_8) + fn = convert_pdf_cmyk_8_to_white_8; + else if (IMAGE_CMYK_16) + fn = convert_pdf_no_conversion; + else if (IMAGE_RGB_8) + fn = convert_pdf_rgb_8_to_white_8; + else if (IMAGE_RGB_16) + fn = convert_pdf_no_conversion; + break; + case CUPS_CSPACE_DEVICE1: + case CUPS_CSPACE_DEVICE2: + case CUPS_CSPACE_DEVICE3: + case CUPS_CSPACE_DEVICE4: + case CUPS_CSPACE_DEVICE5: + case CUPS_CSPACE_DEVICE6: + case CUPS_CSPACE_DEVICE7: + case CUPS_CSPACE_DEVICE8: + case CUPS_CSPACE_DEVICE9: + case CUPS_CSPACE_DEVICEA: + case CUPS_CSPACE_DEVICEB: + case CUPS_CSPACE_DEVICEC: + case CUPS_CSPACE_DEVICED: + case CUPS_CSPACE_DEVICEE: + case CUPS_CSPACE_DEVICEF: + fn = convert_pdf_no_conversion; + break; + default: + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Color space not supported."); + error = 1; + break; + } + } + + if (!error) + fn(info, doc); + + return error; +} + + +static int +add_pdf_page(struct pdf_info *info, + int pagen, + unsigned width, + unsigned height, + int bpp, + int bpc, + int bpl, + const char *render_intent, + cups_cspace_t color_space, + unsigned xdpi, + unsigned ydpi, + pwgtopdf_doc_t *doc) +{ + if (finish_page(info, doc)) + return 1; + + prepare_pdf_page(info, width, height, bpl, bpp, bpc, render_intent, color_space, doc); + + if (info->height > (UINT_MAX / info->line_bytes)) + { + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Page too big"); + return 1; + } + + if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) + info->page_data = malloc(info->line_bytes * info->height); + + else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) + { + for (size_t i = 0; i < info->pclm_num_strips; i++) + { + info->pclm_strip_data[i] = malloc(info->line_bytes * info->pclm_strip_height[i]); + } + } + + pdfio_dict_t *page_dict = pdfioDictCreate(info->pdf); + + pdfioDictSetName(page_dict, "Type", "Page"); + pdfioDictSetDict(page_dict, "Resources", pdfioDictCreate(info->pdf)); + pdfioDictSetNull(page_dict, "MediaBox"); + pdfioDictSetNull(page_dict, "Contents"); + + + info->page_width = ((double)info->width / xdpi) * DEFAULT_PDF_UNIT; + info->page_height = ((double)info->height / ydpi) * DEFAULT_PDF_UNIT; + + if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) + { + pdfio_obj_t *null_obj = pdfioFileCreateObj(info->pdf, pdfioDictCreate(info->pdf)); + pdfioDictSetObj(page_dict, "Contents", null_obj); + pdfio_rect_t media_rect = make_real_box(0, 0, info->page_width, info->page_height); + pdfioDictSetRect(page_dict, "MediaBox", &media_rect); + } + + else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) + { + pdfio_obj_t *null_obj = pdfioFileCreateObj(info->pdf, pdfioDictCreate(info->pdf)); + pdfioDictSetObj(page_dict, "Contents", null_obj); + + pdfio_rect_t media_rect = make_real_box(0, 0, info->page_width + 0.5, info->page_height + 0.5); + pdfioDictSetRect(page_dict, "MediaBox", &media_rect); + } + + pdfio_obj_t *page = pdfioFileCreateObj(info->pdf, page_dict); + + info->page = page; // we want to keep a + // reference + return 0; +} + +static int +close_pdf_file(struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + if (finish_page(info, doc)) + return 1; + + pdfioFileClose(info->pdf); + + return 0; +} + +static void +pdf_set_line(struct pdf_info * info, + unsigned line_n, + unsigned char *line, + pwgtopdf_doc_t *doc) +{ + if (line_n > info->height) + { + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Bad line %d", line_n); + return; + } + + if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) + { + // copy line data into appropriate pclm strip + size_t strip_num = line_n / info->pclm_strip_height_preferred; + unsigned line_strip = line_n - + strip_num * info->pclm_strip_height_preferred; + memcpy(((info->pclm_strip_data[strip_num]) + + (line_strip*info->line_bytes)), line, info->line_bytes); + } + else + memcpy((info->page_data + (line_n * info->line_bytes)), + line, info->line_bytes); +} + +static int +convert_raster(cups_raster_t *ras, + unsigned width, + unsigned height, + int bpp, + int bpl, + struct pdf_info *info, + pwgtopdf_doc_t *doc) +{ + // We should be at raster start + int i; + unsigned cur_line = 0; + unsigned char *PixelBuffer, *ptr = NULL, *buff; + + PixelBuffer = (unsigned char *)malloc(bpl); + buff = (unsigned char *)malloc(info->line_bytes); + + do + { + // Read raster data... + cupsRasterReadPixels(ras, PixelBuffer, bpl); + +#if !ARCH_IS_BIG_ENDIAN + if (info->bpc == 16) + { + // Swap byte pairs for endianess (cupsRasterReadPixels() switches + // from Big Endian back to the system's Endian) + for (i = bpl, ptr = PixelBuffer; i > 0; i -= 2, ptr += 2) + { + unsigned char swap = *ptr; + *ptr = *(ptr + 1); + *(ptr + 1) = swap; + } + } +#endif // !ARCH_IS_BIG_ENDIAN + + // perform bit operations if necessary + doc->bit_function(PixelBuffer, ptr, bpl); + + // write lines and color convert when necessary + pdf_set_line(info, cur_line, doc->conversion_function(PixelBuffer, + buff, width), + doc); + ++cur_line; + } + while (cur_line < height); + + free(buff); + free(PixelBuffer); + + return (0); +} + +static int +set_profile(const char *path, + pwgtopdf_doc_t *doc) +{ + if (path != NULL) + doc->colorProfile = cmsOpenProfileFromFile(path, "r"); + + if (doc->colorProfile != NULL) + { + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Load profile successful."); + return (0); + } + else + { + if (doc->logfunc) + doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Unable to load profile."); + return (1); + } +} + + +int // O - Error status +cfFilterPWGToPDF(int inputfd, // I - File descriptor input stream + int outputfd, // I - File descriptor output stream + int inputseekable, // I - Is input stream seekable? (unused) + cf_filter_data_t *data, // I - Job and printer data + void *parameters) // I - Filter-specific parameters (outformat) +{ + cups_len_t i; + char *t; + pwgtopdf_doc_t doc; // Document information + FILE *outputfp; // Output data stream + cf_filter_out_format_t outformat; // Output format + int Page, empty = 1; + cf_cm_calibration_t cm_calibrate; // Status of CUPS color management + // ("on" or "off") + struct pdf_info pdf; + cups_raster_t *ras; // Raster stream for printing + cups_page_header_t header; // Page header from file + ipp_t *printer_attrs = data->printer_attrs; // Printer attributes from + // printer data + ipp_attribute_t *ipp_attr; // Printer attribute + const char *profile_name = NULL; // IPP Profile Name + cf_logfunc_t log = data->logfunc; + void *ld = data->logdata; + cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; + void *icd = data->iscanceleddata; + int total_attrs; + char buf[1024]; + const char *kw; + + (void)inputseekable; + + if (parameters) + { + outformat = *(cf_filter_out_format_t *)parameters; + if (outformat != CF_FILTER_OUT_FORMAT_PCLM) + outformat = CF_FILTER_OUT_FORMAT_PDF; + } + else + { + t = data->final_content_type; + if (t) + { + if (strcasestr(t, "pclm")) + outformat = CF_FILTER_OUT_FORMAT_PCLM; + else if (strcasestr(t, "pdf")) + outformat = CF_FILTER_OUT_FORMAT_PDF; + else + outformat = CF_FILTER_OUT_FORMAT_PDF; + } + else + outformat = CF_FILTER_OUT_FORMAT_PDF; + } + + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: OUTFORMAT=\"%s\"", + outformat == CF_FILTER_OUT_FORMAT_PDF ? "PDF" : "PCLM"); + + // + // Open the output data stream specified by the outputfd... + // + + if ((outputfp = fdopen(outputfd, "w")) == NULL) + { + if (!iscanceled || !iscanceled(icd)) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Unable to open output data stream."); + } + + return (1); + } + + doc.outputfp = outputfp; + // Logging function + doc.logfunc = log; + doc.logdata = ld; + // Job-is-canceled function + doc.iscanceledfunc = iscanceled; + doc.iscanceleddata = icd; + + // support the CUPS "cm-calibration" option + cm_calibrate = cfCmGetCupsColorCalibrateMode(data); + + if (outformat == CF_FILTER_OUT_FORMAT_PCLM || + cm_calibrate == CF_CM_CALIBRATION_ENABLED) + doc.cm_disabled = 1; + else + doc.cm_disabled = cfCmIsPrinterCmDisabled(data); + + if (outformat == CF_FILTER_OUT_FORMAT_PCLM && printer_attrs == NULL) + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPWGToPDF: PCLm output: No printer IPP attributes are supplied, PCLm output not possible."); + return (1); + } + + // Transform + ras = cupsRasterOpen(inputfd, CUPS_RASTER_READ); + + // Process pages as needed... + Page = 0; + + // Get PCLm parameters from printer IPP attributes + if (outformat == CF_FILTER_OUT_FORMAT_PCLM) + { + if (log) + { + log(ld, CF_LOGLEVEL_DEBUG, "PCLm-related printer IPP attributes:"); + total_attrs = 0; + ipp_attr = ippGetFirstAttribute(printer_attrs); + while (ipp_attr) + { + if (strncmp(ippGetName(ipp_attr), "pclm-", 5) == 0) + { + total_attrs ++; + ippAttributeString(ipp_attr, buf, sizeof(buf)); + log(ld, CF_LOGLEVEL_DEBUG, " Attr: %s",ippGetName(ipp_attr)); + log(ld, CF_LOGLEVEL_DEBUG, " Value: %s", buf); + for (i = 0; i < ippGetCount(ipp_attr); i ++) + if ((kw = ippGetString(ipp_attr, i, NULL)) != NULL) + log(ld, CF_LOGLEVEL_DEBUG, " Keyword: %s", kw); + } + ipp_attr = ippGetNextAttribute(printer_attrs); + } + log(ld, CF_LOGLEVEL_DEBUG, " %d attributes", total_attrs); + } + + char *attr_name = (char *)"pclm-strip-height-preferred"; + if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, + IPP_TAG_ZERO)) != NULL) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value %d", + attr_name, ippGetInteger(ipp_attr, 0)); + pdf.pclm_strip_height_preferred = ippGetInteger(ipp_attr, 0); + } + else + pdf.pclm_strip_height_preferred = 16; // default strip height + + attr_name = (char *)"pclm-strip-height-supported"; + if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, + IPP_TAG_ZERO)) != NULL) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Printer PCLm attribute \"%s\"", + attr_name); + pdf.pclm_strip_height_supported = NULL; // remove default value = 16 + for (i = 0; i < ippGetCount(ipp_attr); i ++) + pdf.pclm_strip_height_supported[i] = ippGetInteger(ipp_attr, i); + } + + + attr_name = (char *)"pclm-raster-back-side"; + if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, + IPP_TAG_ZERO)) != NULL) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", + attr_name, ippGetString(ipp_attr, 0, NULL)); + pdf.pclm_raster_back_side = ippGetString(ipp_attr, 0, NULL); + } + + attr_name = (char *)"pclm-source-resolution-supported"; + if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, + IPP_TAG_ZERO)) != NULL) + { + ippAttributeString(ipp_attr, buf, sizeof(buf)); + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", + attr_name, buf); + int size = (int)sizeof(buf); + + pdf.pclm_source_resolution_supported = split_strings(buf, ",", &size); + } + + attr_name = (char *)"pclm-source-resolution-default"; + if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, + IPP_TAG_ZERO)) != NULL) + { + ippAttributeString(ipp_attr, buf, sizeof(buf)); + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", + attr_name, buf); + pdf.pclm_source_resolution_default = buf; + } + else if (sizeof(pdf.pclm_source_resolution_supported) > 0) + { + pdf.pclm_source_resolution_default = + pdf.pclm_source_resolution_supported[0]; + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" missing, taking first item of \"pclm-source-resolution-supported\" as default resolution", + attr_name); + } + else + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPWGToPDF: PCLm output: Printer IPP attributes do not contain printer resolution information for PCLm."); + return (1); + } + + attr_name = (char *)"pclm-compression-method-preferred"; + if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, + IPP_TAG_ZERO)) != NULL) + { + ippAttributeString(ipp_attr, buf, sizeof(buf)); + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", + attr_name, buf); + + } + // If the compression methods is none of the above or is erreneous + // use FLATE as compression method and show a warning. + if (pdf.pclm_compression_method_preferred == NULL) + { + if (log) log(ld, CF_LOGLEVEL_WARN, + "(pwgtopclm) Unable parse Printer attribute \"%s\". " + "Using FLATE for encoding image streams.", attr_name); + } + } + + while (cupsRasterReadHeader(ras, &header)) + { + if (iscanceled && iscanceled(icd)) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Job canceled"); + break; + } + + if (empty) + { + empty = 0; + // We have a valid input page, so create PDF file + if (create_pdf_file(&pdf, outformat) != 0) + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPWGToPDF: Unable to create PDF file"); + return (1); + } + } + + // Write a status message with the page number + Page ++; + if (log) log(ld, CF_LOGLEVEL_INFO, + "cfFilterPWGToPDF: Starting page %d.", Page); + + // Update rendering intent with user settings or the default + cfGetPrintRenderIntent(data, header.cupsRenderingIntent, + sizeof(header.cupsRenderingIntent)); + + // Use "profile=profile_name.icc" to embed 'profile_name.icc' into the PDF + // for testing. Forces color management to enable. + if (outformat == CF_FILTER_OUT_FORMAT_PDF && + (profile_name = cupsGetOption("profile", data->num_options, + data->options)) != NULL) + { + set_profile(profile_name, &doc); + doc.cm_disabled = 0; + } + if (doc.colorProfile != NULL) + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: TEST ICC Profile specified (color " + "management forced ON): \n[%s]", profile_name); + + // Add a new page to PDF file + if (add_pdf_page(&pdf, Page, header.cupsWidth, header.cupsHeight, + header.cupsBitsPerPixel, header.cupsBitsPerColor, + header.cupsBytesPerLine, header.cupsRenderingIntent, + header.cupsColorSpace, header.HWResolution[0], + header.HWResolution[1], &doc) != 0) + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPWGToPDF: Unable to start new PDF page"); + return (1); + } + + // Write the bit map into the PDF file + if (convert_raster(ras, header.cupsWidth, header.cupsHeight, + header.cupsBitsPerPixel, header.cupsBytesPerLine, + &pdf, &doc) != 0) + { + if (log) log(ld, CF_LOGLEVEL_ERROR, + "cfFilterPWGToPDF: Failed to convert page bitmap"); + return (1); + } + } + + if (empty) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, + "cfFilterPWGToPDF: Input is empty, outputting empty file."); + cupsRasterClose(ras); + return (0); + } + + close_pdf_file(&pdf, &doc); // output to outputfp + + if (doc.colorProfile != NULL) + cmsCloseProfile(doc.colorProfile); + + cupsRasterClose(ras); + fclose(outputfp); + + return (Page == 0); +} From 183d5c5cb66b335b336e74c29d806bb722ebda74 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Wed, 20 Nov 2024 23:15:00 +0530 Subject: [PATCH 55/64] renamed --- cupsfilters/C-pclmtoraster.c | 1254 ----------------------- cupsfilters/C-pdf.c | 371 ------- cupsfilters/C-pdf.h | 63 -- cupsfilters/C-pwgtopdf.c | 1801 ---------------------------------- cupsfilters/pdf.c | 2 +- 5 files changed, 1 insertion(+), 3490 deletions(-) delete mode 100644 cupsfilters/C-pclmtoraster.c delete mode 100644 cupsfilters/C-pdf.c delete mode 100644 cupsfilters/C-pdf.h delete mode 100644 cupsfilters/C-pwgtopdf.c diff --git a/cupsfilters/C-pclmtoraster.c b/cupsfilters/C-pclmtoraster.c deleted file mode 100644 index d776d406..00000000 --- a/cupsfilters/C-pclmtoraster.c +++ /dev/null @@ -1,1254 +0,0 @@ -// -// PCLm/Raster-only PDF to Raster filter function for libcupsfilters. -// -// Copyright © 2020 by Vikrant Malik -// Copyright 2024 Uddhav Phatak -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -// -// Include necessary headers... -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -#define MAX_BYTES_PER_PIXEL 32 - -typedef struct pclmtoraster_data_s { - int outformat; - int numcolors; - int rowsize; - cups_page_header_t header; - char pageSizeRequested[64]; - int bi_level; - // image swapping - int swap_image_x; - int swap_image_y; - int swap_margin_x; - int swap_margin_y; - unsigned int nplanes; - unsigned int nbands; - unsigned int bytesPerLine; - char colorspace[32]; // Use fixed-size string -} pclmtoraster_data_t; - -void -init_pclmtoraster_data_t(pclmtoraster_data_t *data) -{ - data->outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - data->numcolors = 0; - data->rowsize = 0; - data->bi_level = 0; - // image swapping - data->swap_image_x = false; - data->swap_image_y = false; - // margin swapping - data->swap_margin_x = false; - data->swap_margin_y = false; - // Note: When CUPS_ORDER_BANDED, - // cupsBytesPerLine = bytesPerLine * cupsNumColors - strncpy(data->colorspace, "\0", sizeof(data->colorspace)); -} - -typedef unsigned char *(*convert_cspace_func)(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data); - -typedef unsigned char *(*convert_line_func)(unsigned char *src, - unsigned char *dst, - unsigned char *buf, - unsigned int row, - unsigned int plane, - pclmtoraster_data_t *data, - convert_cspace_func convertcspace); - -typedef struct pclm_conversion_function_s -{ - convert_cspace_func convertcspace;// Function for conversion of colorspaces - convert_line_func convertline; // Function tom modify raster data of a line -} pclm_conversion_function_t; - -static int -parse_opts(cf_filter_data_t *data, - cf_filter_out_format_t outformat, - pclmtoraster_data_t *pclmtoraster_data) -{ - int num_options = 0; - cups_option_t* options = NULL; - const char* t = NULL; - const char *val; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cups_page_header_t *header = &(pclmtoraster_data->header); - cups_cspace_t cspace = (cups_cspace_t)(-1); - - pclmtoraster_data->outformat = outformat; - - // - // CUPS option list - // - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - t = cupsGetOption("media-class", num_options, options); - if (t == NULL) - t = cupsGetOption("MediaClass", num_options, options); - if (t != NULL) - { - if (strcasestr(t, "pwg")) - pclmtoraster_data->outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - } - - cfRasterPrepareHeader(header, data, outformat, outformat, 0, &cspace); - - if(header->Duplex) - { - int backside; - // analyze options relevant to Duplex - // APDuplexRequiresFlippedMargin - enum - { - FM_NO, - FM_FALSE, - FM_TRUE - } flippedMargin = FM_NO; - - backside = cfGetBackSideOrientation(data); - - if (backside >= 0) - { - flippedMargin = (backside & 16 ? FM_TRUE : - (backside & 8 ? FM_FALSE : - FM_NO)); - backside &= 7; - - if(backside == CF_BACKSIDE_MANUAL_TUMBLE && header->Tumble) - { - pclmtoraster_data->swap_image_x = true; - pclmtoraster_data->swap_image_y = true; - pclmtoraster_data->swap_margin_x = true; - pclmtoraster_data->swap_margin_y = true; - if (flippedMargin == FM_TRUE) - pclmtoraster_data->swap_margin_y = false; - } - else if(backside == CF_BACKSIDE_ROTATED && !header->Tumble) - { - pclmtoraster_data->swap_image_x = true; - pclmtoraster_data->swap_image_y = true; - pclmtoraster_data->swap_margin_x = true; - pclmtoraster_data->swap_margin_y = true; - if (flippedMargin == FM_TRUE) - pclmtoraster_data->swap_margin_y = false; - } - else if (backside == CF_BACKSIDE_FLIPPED) - { - if (header->Tumble) - { - pclmtoraster_data->swap_image_x = true; - pclmtoraster_data->swap_margin_x = true; - pclmtoraster_data->swap_margin_y = true; - } - else - pclmtoraster_data->swap_image_y = true; - - if (flippedMargin == FM_FALSE) - pclmtoraster_data->swap_margin_y = !pclmtoraster_data->swap_margin_y; - } - } - } - - if ((val = cupsGetOption("print-color-mode", num_options, options)) != NULL && - !strncasecmp(val, "bi-level", 8)) - pclmtoraster_data->bi_level = 1; - - strncpy(pclmtoraster_data->pageSizeRequested, header->cupsPageSizeName, 63); - pclmtoraster_data->pageSizeRequested[63] = '\0'; - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: Page size requested: %s.", - header->cupsPageSizeName); - return 0; -} - -static bool -media_box_lookup(pdfio_obj_t *object, - float rect[4]) -{ - pdfio_rect_t mediaBox; - pdfio_dict_t *object_dict = pdfioObjGetDict(object); - if(pdfioDictGetRect(object_dict, "MediaBox", &mediaBox)) - return false; - - pdfioDictGetRect(object_dict, "MediaBox", &mediaBox); - - rect[0] = mediaBox.x1; - rect[1] = mediaBox.y1; - rect[2] = mediaBox.x2; - rect[3] = mediaBox.y2; - - return true; -} - -// -// 'rotate_bitmap()' - Function to rotate a bitmap -// (assumed that bits-per-component of the bitmap is 8). -// - -static unsigned char * // O - Output Bitmap -rotate_bitmap(unsigned char *src, // I - Input string - unsigned char *dst, // O - Destination string - unsigned int rotate, // I - Rotate value (0, 90, 180, 270) - unsigned int height, // I - Height of raster image in pixels - unsigned int width, // I - Width of raster image in pixels - int rowsize, // I - Length of one row of pixels - char* colorspace,// I - Colorspace of input bitmap - cf_logfunc_t log, // I - Log function - void *ld) // I - Aux. data for log function -{ - unsigned char *bp = src; - unsigned char *dp = dst; - unsigned char *temp = dst; - - if(rotate == 0) - return (src); - else if(rotate == 180) - { - if (strcmp(colorspace, "/DeviceGray") == 0) - { - bp = src + height * rowsize - 1; - dp = dst; - for (unsigned int h = 0; h < height; h++) - for (unsigned int w = 0; w < width; w++, bp--, dp++) - *dp = *bp; - } - else if (strcmp(colorspace, "/DeviceCMYK") == 0) - { - bp = src + height * rowsize - 4; - dp = dst; - for (unsigned int h = 0; h < height; h ++) - { - for (unsigned int w = 0; w < width; w ++, bp -= 4, dp += 4) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - dp[3] = bp[3]; - } - } - } - else if (strcmp(colorspace, "/DeviceRGB") == 0) - { - bp = src + height * rowsize - 3; - dp = dst; - for (unsigned int h = 0; h < height; h ++) - { - for (unsigned int w = 0; w < width; w ++, bp -= 3, dp += 3) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - } - } - } - } - else if (rotate == 270) - { - if (strcmp(colorspace, "/DeviceGray") == 0) - { - bp = src; - dp = dst; - for (unsigned int h = 0; h < height; h ++) - { - bp = src + (height - h) - 1; - for (unsigned int w = 0; w < width; w ++, bp += height , dp ++) - *dp = *bp; - } - } - else if (strcmp(colorspace, "/DeviceCMYK") == 0) - { - for (unsigned int h = 0; h < height; h++) - { - bp = src + (height - h) * 4 - 4; - for (unsigned int i = 0; i < width; i ++, bp += height * 4 , dp += 4) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - dp[3] = bp[3]; - } - } - } - else if (strcmp(colorspace, "/DeviceRGB") == 0) - { - bp = src; - dp = dst; - for (unsigned int h = 0; h < height; h ++) - { - bp = src + (height - h) * 3 - 3; - for (unsigned int i = 0; i < width; i ++, bp += height * 3 , dp += 3) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - } - } - } - } - else if (rotate == 90) - { - if (strcmp(colorspace, "/DeviceGray") == 0) - { - for (unsigned int h = 0; h < height; h ++) - { - bp = src + (width - 1) * height + h; - for (unsigned int i = 0; i < width; i ++, bp -= height , dp ++) - *dp = *bp; - } - } - else if (strcmp(colorspace, "/DeviceCMYK") == 0) - { - for (unsigned int h = 0; h < height; h ++) - { - bp = src + (width - 1) * height * 4 + 4 * h; - for (unsigned int i = 0; i < width; i ++, bp -= height * 4 , dp += 4) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - dp[3] = bp[3]; - } - } - } - else if (strcmp(colorspace, "/DeviceRGB") == 0) - { - for (unsigned int h = 0; h < height; h ++) - { - bp = src + (width - 1) * height * 3 + 3 * h; - for (unsigned int i = 0; i < width; i ++, bp -= height * 3 , dp += 3) - { - dp[0] = bp[0]; - dp[1] = bp[1]; - dp[2] = bp[2]; - } - } - } - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Incorrect Rotate Value %d, not rotating", - rotate); - return (src); - } - - return (temp); -} - -static unsigned char * -rgb_to_rgbw_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageRGBToCMYK(src, dst, pixels); - for (unsigned int i = 0; i < 4 * pixels; i ++) - dst[i] = ~dst[i]; - return (dst); -} - -static unsigned char * -rgb_to_cmyk_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageRGBToCMYK(src, dst, pixels); - return (dst); -} - - -static unsigned char * -rgb_to_cmy_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageRGBToCMY(src, dst, pixels); - return (dst); -} - - -static unsigned char * -rgb_to_white_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - if (data->header.cupsBitsPerColor != 1) - cfImageRGBToWhite(src, dst, pixels); - else - { - cfImageRGBToWhite(src, src, pixels); - cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); - } - return (dst); -} - - -static unsigned char * -rgb_to_black_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - if (data->header.cupsBitsPerColor != 1) - cfImageRGBToBlack(src, dst, pixels); - else - { - cfImageRGBToBlack(src, src, pixels); - cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); - } - return (dst); -} - - -static unsigned char * -cmyk_to_rgb_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageCMYKToRGB(src, dst, pixels); - return (dst); -} - - -static unsigned char * -cmyk_to_rgbw_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - for (unsigned int i = 0; i < 4 * pixels; i ++) - dst[i] = ~src[i]; - return (dst); -} - - -static unsigned char * -cmyk_to_cmy_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - // Converted first to RGB and then to cmy for better outputs. - cfImageCMYKToRGB(src, src, pixels); - cfImageRGBToCMY(src, dst, pixels); - return (dst); -} - - -static unsigned char * -cmyk_to_white_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - if (data->header.cupsBitsPerColor != 1) - cfImageCMYKToWhite(src, dst, pixels); - else - { - cfImageCMYKToWhite(src, src, pixels); - cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); - } - return (dst); -} - - -static unsigned char * -cmyk_to_black_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - if (data->header.cupsBitsPerColor != 1) - cfImageCMYKToBlack(src, dst, pixels); - else - { - cfImageCMYKToBlack(src, src, pixels); - cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); - } - return (dst); -} - - -static unsigned char * -gray_to_rgb_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageWhiteToRGB(src, dst, pixels); - return (dst); -} - - -static unsigned char * -gray_to_rgbw_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageWhiteToCMYK(src, dst, pixels); - for (unsigned int i = 0; i < 4 * pixels; i ++) - dst[i] = ~dst[i]; - return (dst); -} - - -static unsigned char * -gray_to_cmyk_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageWhiteToCMYK(src, dst, pixels); - return (dst); -} - - -static unsigned char * -gray_to_cmy_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - cfImageWhiteToCMY(src, dst, pixels); - return (dst); -} - - -static unsigned char * -gray_to_black_line(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - if (data->header.cupsBitsPerColor != 1) - cfImageWhiteToBlack(src, dst, pixels); - else - { - cfImageWhiteToBlack(src, src, pixels); - cfOneBitLine(src, dst, data->header.cupsWidth, row, data->bi_level); - } - return (dst); -} - - -static unsigned char * -convert_cspace_no_op(unsigned char *src, - unsigned char *dst, - unsigned int row, - unsigned int pixels, - pclmtoraster_data_t *data) -{ - return (src); -} - - -// -// 'convert_line()' - Function to convert colorspace and bits-per-pixel -// of a single line of raster data. -// - -static unsigned char * // O - Output string -convert_line(unsigned char *src, // I - Input line - unsigned char *dst, // O - Destination string - unsigned char *buf, // I - Buffer string - unsigned int row, // I - Current Row - unsigned int plane, // I - Plane/Band - pclmtoraster_data_t *data, - convert_cspace_func convertcspace) -{ - // - // Use only convertcspace if conversion of bits and conversion of color order - // is not required, or if dithering is required, for faster processing of - // raster output. - // - - unsigned int pixels = data->header.cupsWidth; - if ((data->header.cupsBitsPerColor == 1 && - data->header.cupsNumColors == 1) || - (data->header.cupsBitsPerColor == 8 && - data->header.cupsColorOrder == CUPS_ORDER_CHUNKED)) - dst = convertcspace(src, dst, row, pixels, data); - else - { - for (unsigned int i = 0; i < pixels; i ++) - { - unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; - unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; - unsigned char *pb; - pb = convertcspace(src + i * data->numcolors, pixelBuf1, row, 1, data); - pb = cfConvertBits(pb, pixelBuf2, i, row, data->header.cupsNumColors, - data->header.cupsBitsPerColor); - cfWritePixel(dst, plane, i, pb, data->header.cupsNumColors, - data->header.cupsBitsPerColor, data->header.cupsColorOrder); - } - } - return (dst); -} - - -// -// 'convert_reverse_line()' - Function to convert colorspace and bits-per-pixel -// of a single line of raster data and reverse the -// line. -// - -static unsigned char * // O - Output string -convert_reverse_line(unsigned char *src, // I - Input line - unsigned char *dst, // O - Destination - // string - unsigned char *buf, // I - Buffer string - unsigned int row, // I - Current Row - unsigned int plane, // I - Plane/Band - pclmtoraster_data_t *data, // I - pclmtoraster - // filter data - convert_cspace_func convertcspace) // I - Function for - // conversion of - // colorspace -{ - // - // Use only convertcspace if conversion of bits and conversion of color order - // is not required, or if dithering is required, for faster processing of - // raster output. - // - - unsigned int pixels = data->header.cupsWidth; - if (data->header.cupsBitsPerColor == 1 && data->header.cupsNumColors == 1) - { - buf = convertcspace(src, buf, row, pixels, data); - dst = cfReverseOneBitLine(buf, dst, pixels, data->bytesPerLine); - } - else if (data->header.cupsBitsPerColor == 8 && - data->header.cupsColorOrder == CUPS_ORDER_CHUNKED) - { - unsigned char *dp = dst; - // Assign each pixel of buf to dst in the reverse order. - buf = convertcspace(src, buf, row, pixels, data) + - (data->header.cupsWidth - 1) * data->header.cupsNumColors; - for (unsigned int i = 0; i < pixels; - i ++, buf-=data->header.cupsNumColors, dp+=data->header.cupsNumColors) - for (unsigned int j = 0; j < data->header.cupsNumColors; j ++) - dp[j] = buf[j]; - } - else - { - for (unsigned int i = 0; i < pixels; i ++) - { - unsigned char pixelBuf1[MAX_BYTES_PER_PIXEL]; - unsigned char pixelBuf2[MAX_BYTES_PER_PIXEL]; - unsigned char *pb; - pb = convertcspace(src + (pixels - i - 1) * (data->numcolors), pixelBuf1, - row, 1, data); - pb = cfConvertBits(pb, pixelBuf2, i, row, data->header.cupsNumColors, - data->header.cupsBitsPerColor); - cfWritePixel(dst, plane, i, pb, data->header.cupsNumColors, - data->header.cupsBitsPerColor, data->header.cupsColorOrder); - } - } - return (dst); -} - - -static void // O - Exit status -select_convert_func(int pgno, // I - Page number - cf_logfunc_t log, // I - Log function - void *ld, // I - Aux. data for log - // function - pclmtoraster_data_t *data, // I - pclmtoraster filter - // data - pclm_conversion_function_t *convert)// I - Conversion function -{ - // Set rowsize and numcolors based on colorspace of raster data - cups_page_header_t header = data->header; - char *colorspace = data->colorspace; - if (strcmp(colorspace, "/DeviceRGB") == 0) - { - data->rowsize = header.cupsWidth * 3; - data->numcolors = 3; - } - else if (strcmp(colorspace, "/DeviceCMYK") == 0) - { - data->rowsize = header.cupsWidth * 4; - data->numcolors = 4; - } - else if (strcmp(colorspace, "/DeviceGray") == 0) - { - data->rowsize = header.cupsWidth; - data->numcolors = 1; - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Colorspace %s not supported, " - "defaulting to /deviceRGB", - colorspace); - strcpy(data->colorspace, "/DeviceRGB"); - data->rowsize = header.cupsWidth * 3; - data->numcolors = 3; - } - - convert->convertcspace = convert_cspace_no_op; //Default function - // Select convertcspace function - switch (header.cupsColorSpace) - { - case CUPS_CSPACE_K: - if (strcmp(colorspace, "/DeviceRGB") == 0) - convert->convertcspace = rgb_to_black_line; - else if (strcmp(colorspace, "/DeviceCMYK") == 0) - convert->convertcspace = cmyk_to_black_line; - else if (strcmp(colorspace, "/DeviceGray") == 0) - convert->convertcspace = gray_to_black_line; - break; - case CUPS_CSPACE_W: - case CUPS_CSPACE_SW: - if (strcmp(colorspace, "/DeviceRGB") == 0) - convert->convertcspace = rgb_to_white_line; - else if (strcmp(colorspace, "/DeviceCMYK") == 0) - convert->convertcspace = cmyk_to_white_line; - break; - case CUPS_CSPACE_CMY: - if (strcmp(colorspace, "/DeviceRGB") == 0) - convert->convertcspace = rgb_to_cmy_line; - else if (strcmp(colorspace, "/DeviceCMYK") == 0) - convert->convertcspace = cmyk_to_cmy_line; - else if (strcmp(colorspace, "/DeviceGray") == 0) - convert->convertcspace = gray_to_cmy_line; - break; - case CUPS_CSPACE_CMYK: - if (strcmp(colorspace, "/DeviceRGB") == 0) - convert->convertcspace = rgb_to_cmyk_line; - else if (strcmp(colorspace, "/DeviceGray") == 0) - convert->convertcspace = gray_to_cmyk_line; - break; - case CUPS_CSPACE_RGBW: - if (strcmp(colorspace, "/DeviceRGB") == 0) - convert->convertcspace = rgb_to_rgbw_line; - else if (strcmp(colorspace, "/DeviceCMYK") == 0) - convert->convertcspace = cmyk_to_rgbw_line; - else if (strcmp(colorspace, "/DeviceGray") == 0) - convert->convertcspace = gray_to_rgbw_line; - break; - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_ADOBERGB: - case CUPS_CSPACE_SRGB: - default: - if (strcmp(colorspace, "/DeviceCMYK") == 0) - convert->convertcspace = cmyk_to_rgb_line; - else if (strcmp(colorspace, "/DeviceGray") == 0) - convert->convertcspace = gray_to_rgb_line; - break; - } - - // Select convertline function - if (header.Duplex && (pgno & 1) && data->swap_image_x) - convert->convertline = convert_reverse_line; - else - convert->convertline = convert_line; -} - -bool -process_image(pdfio_dict_t *dict, const char *key, pclmtoraster_data_t *data, int pixel_count, unsigned char *bitmap) -{ - char *buffer; - pdfio_obj_t *image = pdfioDictGetObj(dict, key); - - //... verify the object has type "Image", then do something with the image object ... - - if (strcmp(pdfioObjGetType(image), "image") == 0) - { - pdfio_dict_t *imgdict = pdfioObjGetDict(image); - if (!imgdict) - return true; - - pdfio_stream_t *img_str = pdfioObjOpenStream(image, true); - size_t bufsize = pdfioStreamRead(img_str, buffer, 1024); - - int width = pdfioDictGetNumber(imgdict, "Width"); - int height = pdfioDictGetNumber(imgdict, "Height"); - - data->header.cupsHeight += height; - - if (pixel_count == 0) - bitmap = (unsigned char *)malloc(bufsize); - - else - bitmap = (unsigned char *)realloc(bitmap, pixel_count + bufsize); - - memcpy(bitmap + pixel_count, buffer, bufsize); - pixel_count += bufsize; - - if (width > data->header.cupsWidth) - data->header.cupsWidth = width; - } - - return (true); -} - -// -// 'out_page()' - Function to convert a single page of raster-only PDF/PCLm -// input to CUPS/PWG Raster. -// - -static int // O - Exit status -out_page(cups_raster_t* raster, // I - Raster stream - pdfio_obj_t* page, // I - PDFio Page Object - int pgno, // I - Page number - cf_logfunc_t log, // I - Log function - void* ld, // I - Aux. data for log function - pclmtoraster_data_t *data, // I - pclmtoraster filter data - cf_filter_data_t *filter_data, // I - filter data - pclm_conversion_function_t *convert)// I - Conversion functions -{ - int i; - long long rotate = 0; - float paperdimensions[2], margins[4], l, swap; - int pixel_count = 0, temp = 0; - float mediaBox[4]; - unsigned char *bitmap = NULL, - *colordata = NULL, - *lineBuf = NULL, - *line = NULL, - *dp = NULL; - pdfio_obj_t *colorspace_obj; - - - // Check if page is rotated. - pdfio_dict_t *pageDict = pdfioObjGetDict(page); - if(pdfioDictGetNumber(pageDict, "Rotate")) - { - rotate = pdfioDictGetNumber(pageDict, "Rotate"); - } - - // Get pagesize by the mediabox key of the page. - if (!media_box_lookup(page, mediaBox)) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: PDF page %d doesn't contain a valid mediaBox", - pgno + 1); - return (1); - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: mediaBox = [%f %f %f %f]: ", - mediaBox[0], mediaBox[1], mediaBox[2], mediaBox[3]); - l = mediaBox[2] - mediaBox[0]; - if (l < 0) - l = -l; - if (rotate == 90 || rotate == 270) - data->header.PageSize[1] = (unsigned)l; - else - data->header.PageSize[0] = (unsigned)l; - l = mediaBox[3] - mediaBox[1]; - if (l < 0) - l = -l; - if (rotate == 90 || rotate == 270) - data->header.PageSize[0] = (unsigned)l; - else - data->header.PageSize[1] = (unsigned)l; - } - - memset(paperdimensions, 0, sizeof(paperdimensions)); - for (i = 0; i < 4; i ++) - margins[i] = -1.0; - if (filter_data != NULL) - { - cfGetPageDimensions(filter_data->printer_attrs, filter_data->job_attrs, - filter_data->num_options, filter_data->options, - &(data->header), 0, - &(paperdimensions[0]), &(paperdimensions[1]), - &(margins[0]), &(margins[1]), - &(margins[2]), &(margins[3]), NULL, NULL); - - cfSetPageDimensionsToDefault(&(paperdimensions[0]), &(paperdimensions[1]), - &(margins[0]), &(margins[1]), - &(margins[2]), &(margins[3]), - log, ld); - - if (data->outformat != CF_FILTER_OUT_FORMAT_CUPS_RASTER) - memset(margins, 0, sizeof(margins)); - } - else - { - for (int i = 0; i < 2; i ++) - paperdimensions[i] = data->header.PageSize[i]; - if (data->header.cupsImagingBBox[3] > 0.0) - { - // Set margins if we have a bounding box defined ... - if (data->outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER) - { - margins[0] = data->header.cupsImagingBBox[0]; - margins[1] = data->header.cupsImagingBBox[1]; - margins[2] = paperdimensions[0] - data->header.cupsImagingBBox[2]; - margins[3] = paperdimensions[1] - data->header.cupsImagingBBox[3]; - } - } - else - // ... otherwise use zero margins - for (int i = 0; i < 4; i ++) - margins[i] = 0.0; - } - - if (data->header.Duplex && (pgno & 1)) - { - // backside: change margin if needed - if (data->swap_margin_x) - { - swap = margins[2]; margins[2] = margins[0]; margins[0] = swap; - } - if (data->swap_margin_y) - { - swap = margins[3]; margins[3] = margins[1]; margins[1] = swap; - } - } - - // write page header - for (int i = 0; i < 2; i ++) - { - data->header.cupsPageSize[i] = paperdimensions[i]; - data->header.PageSize[i] = (unsigned int)(data->header.cupsPageSize[i] + - 0.5); - if (data->outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER) - data->header.Margins[i] = margins[i] + 0.5; - else - data->header.Margins[i] = 0; - } - if (data->outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER) - { - data->header.cupsImagingBBox[0] = margins[0]; - data->header.cupsImagingBBox[1] = margins[1]; - data->header.cupsImagingBBox[2] = paperdimensions[0] - margins[2]; - data->header.cupsImagingBBox[3] = paperdimensions[1] - margins[3]; - for (int i = 0; i < 4; i ++) - data->header.ImagingBoundingBox[i] = - (unsigned int)(data->header.cupsImagingBBox[i] + 0.5); - } - else - { - for (int i = 0; i < 4; i ++) - { - data->header.cupsImagingBBox[i] = 0.0; - data->header.ImagingBoundingBox[i] = 0; - } - } - - data->header.cupsWidth = 0; - data->header.cupsHeight = 0; - - // Loop over all raster images in a page and store them in bitmap. - - pdfio_dict_t *resources = pdfioDictGetDict(pdfioObjGetDict(page), "Resources"); - pdfio_dict_t *xobjects = pdfioDictGetDict(resources, "XObject"); - - // Iterate over the XObject dictionary to find images - pdfioDictIterateKeys(xobjects, (pdfio_dict_cb_t)process_image, data); - - // Swap width and height in landscape images - if(rotate == 270 || rotate == 90) - { - temp = data->header.cupsHeight; - data->header.cupsHeight = data->header.cupsWidth; - data->header.cupsWidth = temp; - } - - data->bytesPerLine = data->header.cupsBytesPerLine = - (data->header.cupsBitsPerPixel * data->header.cupsWidth + 7) / 8; - if (data->header.cupsColorOrder == CUPS_ORDER_BANDED) - data->header.cupsBytesPerLine *= data->header.cupsNumColors; - - if (!cupsRasterWriteHeader(raster, &(data->header))) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Can't write page %d header", pgno + 1); - return (1); - } - - colorspace_obj = pdfioDictGetObj(pdfioObjGetDict(page), "ColorSpace"); - - if (colorspace_obj) { - if (strcmp(pdfioObjGetType(colorspace_obj), "Name") == 0) - strncpy(data->colorspace, pdfioDictGetName(pdfioObjGetDict(colorspace_obj), "Type"), sizeof(data->colorspace) - 1); - else - strncpy(data->colorspace, "DeviceRGB", sizeof(data->colorspace) - 1); - } - - - // Select convertline and convertscpace function - select_convert_func(pgno, log, ld, data, convert); - - // If page is to be swapped in both x and y, rotate it by 180 degress - if (data->header.Duplex && (pgno & 1) && data->swap_image_y && - data->swap_image_x) - { - rotate = (rotate + 180) % 360; - data->swap_image_y = false; - data->swap_image_x = false; - } - - // Rotate Bitmap - if (rotate) - { - unsigned char *bitmap2 = (unsigned char *) malloc(pixel_count); - bitmap2 = rotate_bitmap(bitmap, bitmap2, rotate, data->header.cupsHeight, - data->header.cupsWidth, data->rowsize, - data->colorspace, log, ld); - free(bitmap); - bitmap = bitmap2; - } - - colordata = bitmap; - - // Write page image - lineBuf = (unsigned char *)malloc(data->bytesPerLine * sizeof(unsigned char)); - line = (unsigned char *)malloc(data->bytesPerLine * sizeof(unsigned char)); - - if (data->header.Duplex && (pgno & 1) && data->swap_image_y) - { - for (unsigned int plane = 0; plane < data->nplanes; plane ++) - { - unsigned char *bp = colordata + - (data->header.cupsHeight - 1) * (data->rowsize); - for (unsigned int h = data->header.cupsHeight; h > 0; h--) - { - for (unsigned int band = 0; band < data->nbands; band ++) - { - dp = convert->convertline(bp, line, lineBuf, h - 1, plane + band, - data, convert->convertcspace); - cupsRasterWritePixels(raster, dp, data->bytesPerLine); - } - bp -= data->rowsize; - } - } - } - else - { - for (unsigned int plane = 0; plane < data->nplanes; plane ++) - { - unsigned char *bp = colordata; - for (unsigned int h = 0; h < data->header.cupsHeight; h ++) - { - for (unsigned int band = 0; band < data->nbands; band ++) - { - dp = convert->convertline(bp, line, lineBuf, h, plane + band, - data, convert->convertcspace); - cupsRasterWritePixels(raster, dp, data->bytesPerLine); - } - bp += data->rowsize; - } - } - } - free(lineBuf); - free(line); - free(bitmap); - - return (0); -} - - -// -// 'cfFilterPCLmToRaster()' - Filter function to convert raster-only PDF/PCLm -// input to CUPS/PWG Raster output. -// - -int // O - Error status -cfFilterPCLmToRaster(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? - // (unused) - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters - // (unused) -{ - cf_filter_out_format_t outformat; - FILE *inputfp; // Input file pointer - int fd = 0; // Copy file descriptor - char *filename, // PDF file to convert - tempfile[1024]; // Temporary file - char buffer[8192]; // Copy buffer - int bytes; // Bytes copied - int npages = 0; - pdfio_file_t *pdf = (pdfio_file_t *)malloc(sizeof(pdfio_file_t *));; - cups_raster_t *raster; - pclmtoraster_data_t pclmtoraster_data; - pclm_conversion_function_t convert; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - - if (parameters) - { - outformat = *(cf_filter_out_format_t *)parameters; - if (outformat != CF_FILTER_OUT_FORMAT_CUPS_RASTER && - outformat != CF_FILTER_OUT_FORMAT_PWG_RASTER && - outformat != CF_FILTER_OUT_FORMAT_APPLE_RASTER) - outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - } - else - outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: Output format: %s", - (outformat == CF_FILTER_OUT_FORMAT_CUPS_RASTER ? "CUPS Raster" : - (outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER ? "PWG Raster" : - "Apple Raster"))); - - // - // Open the input data stream specified by the inputfd... - // - - if ((inputfp = fdopen(inputfd, "r")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: Unable to open input data stream."); - } - - return (1); - } - - if ((fd = cupsCreateTempFd(NULL, NULL, tempfile, sizeof(tempfile))) < 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Unable to copy PDF file: %s", - strerror(errno)); - fclose(inputfp); - return (1); - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: Copying input to temp file \"%s\"", - tempfile); - - while ((bytes = fread(buffer, 1, sizeof(buffer), inputfp)) > 0) - bytes = write(fd, buffer, bytes); - - fclose(inputfp); - close(fd); - - filename = tempfile; - pdf = pdfioFileOpen(filename, NULL, NULL, NULL, NULL); - - if (parse_opts(data, outformat, &pclmtoraster_data) != 0) - { - free(pdf); - unlink(tempfile); - return (1); - } - - if (pclmtoraster_data.header.cupsBitsPerColor != 1 - && pclmtoraster_data.header.cupsBitsPerColor != 2 - && pclmtoraster_data.header.cupsBitsPerColor != 4 - && pclmtoraster_data.header.cupsBitsPerColor != 8 - && pclmtoraster_data.header.cupsBitsPerColor != 16) - { - if(log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Specified color format is not supported: %s", - strerror(errno)); - free(pdf); - unlink(tempfile); - return (1); - } - - if (pclmtoraster_data.header.cupsColorOrder == CUPS_ORDER_PLANAR) - pclmtoraster_data.nplanes = pclmtoraster_data.header.cupsNumColors; - else - pclmtoraster_data.nplanes = 1; - if (pclmtoraster_data.header.cupsColorOrder == CUPS_ORDER_BANDED) - pclmtoraster_data.nbands = pclmtoraster_data.header.cupsNumColors; - else - pclmtoraster_data.nbands = 1; - - if ((raster = cupsRasterOpen(outputfd, - (pclmtoraster_data.outformat == - CF_FILTER_OUT_FORMAT_CUPS_RASTER ? - CUPS_RASTER_WRITE : - (pclmtoraster_data.outformat == - CF_FILTER_OUT_FORMAT_PWG_RASTER ? - CUPS_RASTER_WRITE_PWG : - CUPS_RASTER_WRITE_APPLE)))) == 0) - { - if(log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPCLmToRaster: Can't open raster stream: %s", - strerror(errno)); - free(pdf); - unlink(tempfile); - return (1); - } - - - npages = pdfioFileGetNumPages(pdf); - - for (int i = 0; i < npages; ++i) - { - pdfio_obj_t *pages = pdfioFileGetPage(pdf, i); - - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPCLmToRaster: Job canceled"); - break; - } - - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterPCLmToRaster: Starting page %d.", i + 1); - if (out_page(raster, pages, i, log, ld, &pclmtoraster_data,data, - &convert) != 0) - break; - - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterPCLmToRaster: Starting page %d.", (i + 1)); - if (out_page(raster, pages, i, log, ld, &pclmtoraster_data,data, - &convert) != 0) - break; - } - - cupsRasterClose(raster); - free(pdf); - unlink(tempfile); - return (0); -} diff --git a/cupsfilters/C-pdf.c b/cupsfilters/C-pdf.c deleted file mode 100644 index d947cd89..00000000 --- a/cupsfilters/C-pdf.c +++ /dev/null @@ -1,371 +0,0 @@ -// -// Copyright 2012 Canonical Ltd. -// Copyright 2013 ALT Linux, Andrew V. Stepanov -// Copyright 2018 Sahil Arora -// Copyright 2024 Uddhav Phatak -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - - -#include -#include -#include -#include "C-pdf.h" - -#include -#include - -// -// 'make_real_box()' - Return a PDFio rect object of real values for a box. -// - -static pdfio_rect_t // O - PDFioObjectHandle for a rect -make_real_box(float values[4]) // I - Dimensions of the box in a float array -{ - pdfio_rect_t rect; - - rect.x1 = values[0]; - rect.y1 = values[1]; - rect.x2 = values[2]; - rect.y2 = values[3]; - - return rect; -} - -// -// 'cfPDFLoadTemplate()' - Load an existing PDF file using PDFio. -// - -cf_pdf_t* -cfPDFLoadTemplate(const char *filename) -{ - pdfio_file_t *pdf = pdfioFileOpen(filename, NULL, NULL, NULL, NULL); - - if (!pdf) - return NULL; - - if (pdfioFileGetNumPages(pdf) != 1) - { - pdfioFileClose(pdf); - return NULL; - } - - return (cf_pdf_t *)pdf; -} - -// -// 'cfPDFFree()' - Free pointer used by PDF object -// - -void -cfPDFFree(cf_pdf_t *pdf) -{ - if (pdf) - { - pdfioFileClose((pdfio_file_t *)pdf); - } -} - -// -// 'cfPDFPages()' - Count number of pages in file using PDFio. -// - -int -cfPDFPages(const char *filename) -{ - pdfio_file_t *pdf = pdfioFileOpen(filename, NULL, NULL, NULL, NULL); - - if (!pdf) - { - return -1; - } - - int pages = pdfioFileGetNumPages(pdf); - pdfioFileClose(pdf); - return pages; -} - -// -// 'cfPDFPagesFP()' - Count number of pages in file using PDFio. -// - -int -cfPDFPagesFP(char *file) -{ - pdfio_file_t *pdf = pdfioFileOpen(file, NULL, NULL, NULL, NULL); - - if (!pdf) - return -1; - - int pages = pdfioFileGetNumPages(pdf); - pdfioFileClose(pdf); - return pages; -} - -// -// 'cfPDFPrependStream()' - Prepend a stream to the contents of a specified -// page in PDF file. -// - -int -cfPDFPrependStream(cf_pdf_t *pdf, - unsigned page_num, - const char *buf, - size_t len) -{ - - if(pdfioFileGetNumPages((pdfio_file_t *)pdf)==0) - return 1; - - pdfio_obj_t *page = pdfioFileGetPage((pdfio_file_t *)pdf, page_num - 1); - pdfio_dict_t *pageDict = pdfioObjGetDict(page); - - pdfio_stream_t *existing_stream = pdfioPageOpenStream(page, 0, true); - if (!existing_stream) - return 1; - - pdfio_obj_t *new_stream_obj = pdfioFileCreateObj((pdfio_file_t *)pdf, pageDict); - if (!new_stream_obj) - { - pdfioStreamClose(existing_stream); - return 1; - } - - pdfio_stream_t *new_stream = pdfioObjCreateStream(new_stream_obj, PDFIO_FILTER_FLATE); - if (!new_stream) - { - pdfioStreamClose(existing_stream); - return 1; - } - - pdfioStreamWrite(new_stream, buf, len); - pdfioStreamClose(new_stream); - - pdfio_stream_t *combined_stream = pdfioObjCreateStream(page, PDFIO_FILTER_FLATE); - if (!combined_stream) - { - pdfioStreamClose(existing_stream); - return 1; - } - - char buffer[1024]; - size_t read_len; - while ((read_len = pdfioStreamRead(existing_stream, buffer, sizeof(buffer))) > 0) - pdfioStreamWrite(combined_stream, buffer, read_len); - - pdfioStreamClose(existing_stream); - pdfioStreamClose(combined_stream); - - return 0; -} - -// -// 'cfPDFAddType1Font()' - Add the specified type1 font face to the specified -// page in a PDF document. -// - -int -cfPDFAddType1Font(cf_pdf_t *pdf, - unsigned page_num, - const char *name) -{ - pdfio_obj_t *page = pdfioFileGetPage((pdfio_file_t *)pdf, page_num); - pdfio_dict_t *pageDict = pdfioObjGetDict(page); - if (!page) - return 1; - - pdfio_dict_t *resources = pdfioDictGetDict(pageDict, "Resources"); - - if (!resources) - { - resources = pdfioDictCreate((pdfio_file_t *)pdf); - pdfioDictSetDict(pageDict, "Resources", resources); - } - - pdfio_dict_t *fonts = pdfioDictGetDict(resources, "Font"); - if (!fonts) - { - fonts = pdfioDictCreate((pdfio_file_t *)pdf); - pdfioDictSetDict(resources, "Font", fonts); - } - - pdfio_dict_t *font = pdfioDictCreate((pdfio_file_t *)pdf); - if (!font) - return 1; - - pdfioDictSetName(font, "Type", "Font"); - pdfioDictSetName(font, "Subtype", "Type1"); - char basefont[256]; - snprintf(basefont, sizeof(basefont), "/%s", name); - pdfioDictSetName(font, "BaseFont", basefont); - - pdfioDictSetDict(fonts, "bannertopdf-font", font); - - return 0; -} - -// -// 'dict_lookup_rect()' - Lookup for an array of rectangle dimensions in a PDFio -// dictionary object. If it is found, store the values in -// an array and return true, else return false. -// - -static bool -dict_lookup_rect(pdfio_obj_t *object, // I - PDF dictionary object - const char *key, // I - Key to lookup - float rect[4], // O - Array to store values if key is found - bool inheritable) // I - Whether to look for inheritable values -{ - pdfio_dict_t *dict = pdfioObjGetDict(object); - if (!dict) - return false; - - pdfio_obj_t *value = pdfioDictGetObj(dict, key); - if (!value && inheritable) - return false; - - pdfio_array_t *array = pdfioObjGetArray(value); - if (!array || pdfioArrayGetSize(array) != 4) - return false; - - for (int i = 0; i < 4; i++) - { - pdfio_obj_t *elem = pdfioArrayGetObj(array, i); - - if (pdfioArrayGetType(array, i) == PDFIO_VALTYPE_NUMBER) - { - rect[i] = pdfioObjGetNumber(elem); - } - else - return false; // If any value is not numeric, return false - } - - return true; -} - -// -// 'fit_rect()' - Update the scale of the page using old media box dimensions -// and new media box dimensions. -// - -static void -fit_rect(float oldrect[4], // I - Old media box - float newrect[4], // I - New media box - float *scale) // I - Pointer to scale which needs to be updated -{ - float oldwidth = oldrect[2] - oldrect[0]; - float oldheight = oldrect[3] - oldrect[1]; - float newwidth = newrect[2] - newrect[0]; - float newheight = newrect[3] - newrect[1]; - - *scale = newwidth / oldwidth; - if (oldheight * (*scale) > newheight) - *scale = newheight / oldheight; -} - -// -// 'cfPDFResizePage()' - Resize page in a PDF with the given dimensions. -// - -int cfPDFResizePage(cf_pdf_t *pdf, // I - Pointer to PDFio file object - unsigned page_num, // I - Page number (1-based index) - float width, // I - New width of the page - float length, // I - New length of the page - float *scale) // O - Scale of the page to be updated -{ - pdfio_obj_t *page = pdfioFileGetPage((pdfio_file_t *)pdf, page_num - 1); - if (!page) - return 1; - - float new_mediabox[4] = {0.0, 0.0, width, length}; - float old_mediabox[4]; - pdfio_rect_t media_box; - - if (!dict_lookup_rect(page, "/MediaBox", old_mediabox, true)) - return (1); - - fit_rect(old_mediabox, new_mediabox, scale); - media_box = make_real_box(new_mediabox); - - pdfio_dict_t *pageDict = pdfioObjGetDict(page); - if (pageDict) - { - pdfioDictSetRect(pageDict, "CropBox", &media_box); - pdfioDictSetRect(pageDict, "TrimBox", &media_box); - pdfioDictSetRect(pageDict, "BleedBox", &media_box); - pdfioDictSetRect(pageDict, "ArtBox", &media_box); - } - - return 0; -} - -// -// 'cfPDFDuplicatePage()' - Duplicate a specified pdf page in a PDF -// - -int -cfPDFDuplicatePage(cf_pdf_t *pdf, - unsigned page_num, - unsigned count) -{ - pdfio_obj_t *page = pdfioFileGetPage((pdfio_file_t *)pdf, page_num - 1); - - if (!page) - return 1; - - for (unsigned i = 0; i < count; ++i) - { - pdfioPageCopy((pdfio_file_t *)pdf, page); - } - - return 0; -} - -// -// 'cfPDFWrite()' - Write the contents of PDF object to an already open FILE*. -// - -void -cfPDFWrite(cf_pdf_t *pdf, - FILE *file) -{ -// pdfioFileCreateImageObjFromFile((pdfio_file_t *)pdf, file, false); -} - -// -// 'lookup_opt()' - Get value according to key in the options list. -// - -static const char* -lookup_opt(cf_opt_t *opt, - const char *key) -{ - if (!opt || !key) - return NULL; - - while (opt) - { - if (opt->key && opt->val) - { - if (strcmp(opt->key, key) == 0) - { - return opt->val; - } - } - opt = opt->next; - } - - return (""); -} - -// -// 'cfPDFFillForm()' - Fill recognized fields with information -// - -int cfPDFFillForm(cf_pdf_t *doc, cf_opt_t *opt) { - // TODO: PDFio does not directly support form filling. - return 0; -} - diff --git a/cupsfilters/C-pdf.h b/cupsfilters/C-pdf.h deleted file mode 100644 index 92e381d3..00000000 --- a/cupsfilters/C-pdf.h +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright 2012 Canonical Ltd. -// Copyright 2018 Sahil Arora -// Copyright 2024 Uddhav Phatak -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDF_H_ -#define _CUPS_FILTERS_PDF_H_ - -#include - -typedef struct pdfio_file_t cf_pdf_t; - -typedef struct _cf_opt cf_opt_t; - -// -// Type to bunch PDF form field name and its value. -// - -struct _cf_opt -{ - const char* key; - const char* val; - cf_opt_t *next; -}; - -cf_pdf_t *cfPDFLoadTemplate(const char *filename); -void cfPDFFree(cf_pdf_t *pdf); - -void cfPDFWrite(cf_pdf_t *doc, - FILE *file); - -int cfPDFPrependStream(cf_pdf_t *doc, - unsigned page, - const char *buf, - size_t len); - -int cfPDFAddType1Font(cf_pdf_t *doc, - unsigned page, - const char *name); - -int cfPDFResizePage(cf_pdf_t *doc, - unsigned page, - float width, - float length, - float *scale); - -int cfPDFDuplicatePage(cf_pdf_t *doc, - unsigned page, - unsigned count); - -int cfPDFFillForm(cf_pdf_t *doc, - cf_opt_t *opt); - -int cfPDFPages(const char *filename); - -int cfPDFPagesFP(char *file); - -#endif // !_CUPS_FILTERS_PDF_H_ - diff --git a/cupsfilters/C-pwgtopdf.c b/cupsfilters/C-pwgtopdf.c deleted file mode 100644 index 632a5004..00000000 --- a/cupsfilters/C-pwgtopdf.c +++ /dev/null @@ -1,1801 +0,0 @@ -// -// PWG/Apple Raster to PDF filter function for libcupsfilters. -// -// Copyright 2010 by Neil 'Superna' Armstrong -// Copyright 2012 by Tobias Hoffmann -// Copyright 2014-2022 by Till Kamppeter -// Copyright 2017 by Sahil Arora -// Copyright 2024 by Uddhav Phatak -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // ntohl - -#include -#include -#include - -#ifdef USE_LCMS1 -#include -#define cmsColorSpaceSignature icColorSpaceSignature -#define cmsSetLogErrorHandler cmsSetErrorHandler -#define cmsSigXYZData icSigXYZData -#define cmsSigLuvData icSigLuvData -#define cmsSigLabData icSigLabData -#define cmsSigYCbCrData icSigYCbCrData -#define cmsSigYxyData icSigYxyData -#define cmsSigRgbData icSigRgbData -#define cmsSigHsvData icSigHsvData -#define cmsSigHlsData icSigHlsData -#define cmsSigCmyData icSigCmyData -#define cmsSig3colorData icSig3colorData -#define cmsSigGrayData icSigGrayData -#define cmsSigCmykData icSigCmykData -#define cmsSig4colorData icSig4colorData -#define cmsSig2colorData icSig2colorData -#define cmsSig5colorData icSig5colorData -#define cmsSig6colorData icSig6colorData -#define cmsSig7colorData icSig7colorData -#define cmsSig8colorData icSig8colorData -#define cmsSig9colorData icSig9colorData -#define cmsSig10colorData icSig10colorData -#define cmsSig11colorData icSig11colorData -#define cmsSig12colorData icSig12colorData -#define cmsSig13colorData icSig13colorData -#define cmsSig14colorData icSig14colorData -#define cmsSig15colorData icSig15colorData -#define cmsSaveProfileToMem _cmsSaveProfileToMem -#else -#include -#endif - -#define DEFAULT_PDF_UNIT 72 // 1/72 inch - -#define PRE_COMPRESS - -// Compression method for providing data to PCLm Streams. -typedef enum compression_method_e -{ - DCT_DECODE = 0, - FLATE_DECODE, - RLE_DECODE -} compression_method_t; - -// Color conversion function -typedef unsigned char *(*convert_function)(unsigned char *src, - unsigned char *dst, - unsigned int pixels); - -// Bit conversion function -typedef unsigned char *(*bit_convert_function)(unsigned char *src, - unsigned char *dst, - unsigned int pixels); - -typedef struct pwgtopdf_doc_s // **** Document information **** -{ - cmsHPROFILE colorProfile; // ICC Profile to be applied to - // PDF - int cm_disabled; // Flag raised if color - // management is disabled - convert_function conversion_function; // Raster color conversion - // function - bit_convert_function bit_function; // Raster bit function - FILE *outputfp; // Temporary file, if any - cf_logfunc_t logfunc; // Logging function, NULL for no - // logging - void *logdata; // User data for logging - // function, can be NULL - cf_filter_iscanceledfunc_t iscanceledfunc; // Function returning 1 when - // job is canceled, NULL for not - // supporting stop on cancel - void *iscanceleddata; // User data for is-canceled - // function, can be NULL -} pwgtopdf_doc_t; - -struct pdf_info -{ - // PDFio-specific members - pdfio_file_t *pdf; // PDFio file handle - pdfio_obj_t *page; // PDFio page handle - - unsigned pagecount; - unsigned width; - unsigned height; - unsigned line_bytes; - unsigned bpp; - unsigned bpc; - unsigned pclm_num_strips; - unsigned pclm_strip_height_preferred; - unsigned *pclm_strip_height; // Dynamically allocated array in C - unsigned *pclm_strip_height_supported; // Dynamically allocated array - compression_method_t *pclm_compression_method_preferred; - size_t num_compression_methods; - char **pclm_source_resolution_supported; // Array of dynamically allocated strings - char *pclm_source_resolution_default; // Pointer to dynamically allocated string - char *pclm_raster_back_side; // Pointer to dynamically allocated string - unsigned char **pclm_strip_data; // Array of pointers to raw data (buffers) - char *render_intent; // Pointer to dynamically allocated string - cups_cspace_t color_space; - unsigned char *page_data; // Pointer to raw page data - double page_width, page_height; - cf_filter_out_format_t outformat; -}; - -// PDF color conversion function -typedef void (*pdf_convert_function)(struct pdf_info *info, - pwgtopdf_doc_t *doc); - - -void -init_pdf_info(struct pdf_info *info, - size_t num_methods, - size_t num_strips_supported) -{ - // Initialize primitive types - info->pagecount = 0; - info->width = 0; - info->height = 0; - info->line_bytes = 0; - info->bpp = 0; - info->bpc = 0; - info->pclm_num_strips = 0; - info->pclm_strip_height_preferred = 16; // Default strip height - info->page_width = 0; - info->page_height = 0; - info->outformat = CF_FILTER_OUT_FORMAT_PDF; - - // Allocate memory for pclm_strip_height (for strip height handling) - info->pclm_strip_height = (unsigned *)malloc(num_strips_supported * sizeof(unsigned)); - if (info->pclm_strip_height) - { - for (size_t i = 0; i < num_strips_supported; ++i) - { - info->pclm_strip_height[i] = 0; // Initialize to 0 or a specific value as needed - } - } - - // Allocate memory for pclm_strip_height_supported - info->pclm_strip_height_supported = (unsigned *)malloc(num_strips_supported * sizeof(unsigned)); - if (info->pclm_strip_height_supported) - { - for (size_t i = 0; i < num_strips_supported; ++i) - { - info->pclm_strip_height_supported[i] = 16; // Initialize to default value - } - } - - // Allocate memory for multiple compression methods - info->num_compression_methods = num_methods; - info->pclm_compression_method_preferred = (compression_method_t *)malloc(num_methods * sizeof(compression_method_t)); - if (info->pclm_compression_method_preferred) - { - for (size_t i = 0; i < num_methods; ++i) - { - info->pclm_compression_method_preferred[i] = 0; // Initialize to default or specific compression method - } - } - - info->pclm_source_resolution_default = (char *)malloc(64 * sizeof(char)); - if (info->pclm_source_resolution_default) - { - strcpy(info->pclm_source_resolution_default, ""); // Initialize to empty string - } - - info->pclm_raster_back_side = (char *)malloc(64 * sizeof(char)); - if (info->pclm_raster_back_side) - { - strcpy(info->pclm_raster_back_side, ""); // Initialize to empty string - } - - info->render_intent = (char *)malloc(64 * sizeof(char)); - if (info->render_intent) - { - strcpy(info->render_intent, ""); // Initialize to empty string - } - - info->pclm_source_resolution_supported = NULL; - info->pclm_strip_data = NULL; // Assuming this will be dynamically allocated elsewhere - - info->color_space = CUPS_CSPACE_K; // Default color space - info->page_data = NULL; // Will be allocated when needed - - info->pdf = NULL; // Initialize to NULL, will be set when opening a file - info->page = NULL; // Initialize to NULL, will be set when reading a page -} - -// Freeing the dynamically allocated memory -void free_pdf_info(struct pdf_info *info) -{ - if (info->pclm_strip_height) - { - free(info->pclm_strip_height); - info->pclm_strip_height = NULL; - } - - if (info->pclm_strip_height_supported) - { - free(info->pclm_strip_height_supported); - info->pclm_strip_height_supported = NULL; - } - - if (info->pclm_compression_method_preferred) - { - free(info->pclm_compression_method_preferred); - info->pclm_compression_method_preferred = NULL; - } - - // Free dynamically allocated strings - if (info->pclm_source_resolution_default) - { - free(info->pclm_source_resolution_default); - info->pclm_source_resolution_default = NULL; - } - - if (info->pclm_raster_back_side) - { - free(info->pclm_raster_back_side); - info->pclm_raster_back_side = NULL; - } - - if (info->render_intent) - { - free(info->render_intent); - info->render_intent = NULL; - } - - // Free any other dynamically allocated memory as necessary - if (info->pclm_strip_data) - { - free(info->pclm_strip_data); // Assuming this array will be dynamically allocated elsewhere - info->pclm_strip_data = NULL; - } - - if (info->page_data) - { - free(info->page_data); - info->page_data = NULL; - } -} - -// -// Bit conversion functions -// -static unsigned char * -invert_bits(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - unsigned int i; - - // Invert black to grayscale... - for (i = pixels, dst = src; i > 0; i --, dst ++) - *dst = ~*dst; - - return (dst); -} - -static unsigned char * -no_bit_conversion(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - return (src); -} - -// -// Color conversion functions -// - -static unsigned char * -rgb_to_cmyk(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageRGBToCMYK(src, dst, pixels); - return (dst); -} - - -static unsigned char * -white_to_cmyk(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageWhiteToCMYK(src, dst, pixels); - return (dst); -} - - -static unsigned char * -cmyk_to_rgb(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageCMYKToRGB(src, dst, pixels); - return (dst); -} - - -static unsigned char * -white_to_rgb(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageWhiteToRGB(src, dst, pixels); - return (dst); -} - - -static unsigned char * -rgb_to_white(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageRGBToWhite(src, dst, pixels); - return (dst); -} - - -static unsigned char * -cmyk_to_white(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - cfImageCMYKToWhite(src, dst, pixels); - return (dst); -} - - -static unsigned char * -no_color_conversion(unsigned char *src, - unsigned char *dst, - unsigned int pixels) -{ - return (src); -} - -// -// 'split_strings()' - Split a string to a vector of strings given some -// delimiters -// -// O - std::vector of std::string after splitting -// I - input string to be split -// I - string containing delimiters -// -// Function to split strings by delimiters - -char** -split_strings(const char *str, - const char *delimiters, - int *size) -{ - if (delimiters == NULL || strlen(delimiters) == 0) - delimiters = ","; - - - int capacity = 10; - char **result = malloc(capacity * sizeof(char *)); - - char *value = malloc(strlen(str) + 1); - - int token_count = 0; - int index = 0; - bool push_flag = false; - - for (size_t i = 0; i < strlen(str); i++) - { - if (strchr(delimiters, str[i]) != NULL) - { - if (push_flag && index > 0) - { - value[index] = '\0'; - result[token_count] = malloc(strlen(value) + 1); - strcpy(result[token_count], value); - token_count++; - - if (token_count >= capacity) - { - capacity *= 2; - result = realloc(result, capacity * sizeof(char *)); - } - - index = 0; - push_flag = false; - } - } - else - { - value[index++] = str[i]; - push_flag = true; - } - } - - if (push_flag && index > 0) - { - value[index] = '\0'; - result[token_count] = malloc(strlen(value) + 1); - strcpy(result[token_count], value); - token_count++; - } - - *size = token_count; - - free(value); - return result; -} - -// -// 'num_digits()' - Calculates the number of digits in an integer -// -// O - number of digits in the input integer -// I - the integer whose digits needs to be calculated -// - -static int -num_digits(int n) -{ - if (n == 0) - return (1); - int digits = 0; - while (n) - { - ++digits; - n /= 10; - } - return (digits); -} - -// -// 'int_to_fwstring()' - Convert a number to fixed width string by padding -// with zeroes -// O - converted string -// I - the integee which needs to be converted to string -// I - width of string required -// - -char* -int_to_fwstring(int n, int width) -{ - int num_zeroes = width - num_digits(n); - if (num_zeroes < 0) - num_zeroes = 0; - - int result_length = num_zeroes + num_digits(n) + 1; - char *result = malloc(result_length * sizeof(char)); - - for (int i = 0; i < num_zeroes; i++) - result[i] = '0'; - - sprintf(result + num_zeroes, "%d", n); - return result; -} - -static int -create_pdf_file(struct pdf_info *info, - const cf_filter_out_format_t outformat) -{ - if (!info || !info->pdf) - return 1; // Error handling - - pdfio_file_t *temp = pdfioFileCreate(pdfioFileGetName(info->pdf), NULL, NULL, NULL, NULL, NULL); - - info->pdf = temp; - info->outformat = outformat; - - return 0; -} - -static pdfio_rect_t -make_real_box(double x1, - double y1, - double x2, - double y2) -{ - pdfio_rect_t ret; - - ret.x1 = x1; - ret.y1 = y1; - ret.x2 = x2; - ret.y2 = y2; - - return (ret); -} - -// -// PDF color conversion functons... -// - -static void -modify_pdf_color(struct pdf_info *info, - int bpp, - int bpc, - convert_function fn, - pwgtopdf_doc_t *doc) -{ - unsigned old_bpp = info->bpp; - unsigned old_bpc = info->bpc; - double old_ncolor = old_bpp / old_bpc; - - unsigned old_line_bytes = info->line_bytes; - - double new_ncolor = bpp / bpc; - - info->line_bytes = (unsigned)old_line_bytes * (new_ncolor / old_ncolor); - info->bpp = bpp; - info->bpc = bpc; - doc->conversion_function = fn; -} - -static void -convert_pdf_no_conversion(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - doc->conversion_function = no_color_conversion; - doc->bit_function = no_bit_conversion; -} - - -static void -convert_pdf_cmyk_8_to_white_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 8, 8, cmyk_to_white, doc); - doc->bit_function = no_bit_conversion; -} - - -static void -convert_pdf_rgb_8_to_white_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 8, 8, rgb_to_white, doc); - doc->bit_function = no_bit_conversion; -} - - -static void -convert_pdf_cmyk_8_to_rgb_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 24, 8, cmyk_to_rgb, doc); - doc->bit_function = no_bit_conversion; -} - - -static void -convert_pdf_white_8_to_rgb_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 24, 8, white_to_rgb, doc); - doc->bit_function = invert_bits; -} - - -static void -convert_pdf_rgb_8_to_cmyk_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 32, 8, rgb_to_cmyk, doc); - doc->bit_function = no_bit_conversion; -} - - -static void -convert_pdf_white_8_to_cmyk_8(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - modify_pdf_color(info, 32, 8, white_to_cmyk, doc); - doc->bit_function = invert_bits; -} - - -static void -convert_pdf_invert_colors(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - doc->conversion_function = no_color_conversion; - doc->bit_function = invert_bits; -} - -// -// Create an '/ICCBased' array and embed a previously -// set ICC Profile in the PDF -// -// TODO: HOW IS THIS cmsHPROFILE CALLED -pdfio_obj_t * -embed_icc_profile(pdfio_file_t *pdf, pwgtopdf_doc_t *doc) -{ - pdfio_dict_t *streamdict; - pdfio_obj_t *icc_stream; - char *n_value = NULL; - char *alternate_cs = NULL; - unsigned char *buff; - cmsColorSpaceSignature css; - -#ifdef USE_LCMS1 - size_t profile_size; -#else - unsigned int profile_size; -#endif - - // Determine color space signature - css = cmsGetColorSpace(doc->colorProfile); - - // Determine color component number for /ICCBased array - switch (css) - { - case cmsSigGrayData: - n_value = "1"; - alternate_cs = "/DeviceGray"; - break; - case cmsSigRgbData: - n_value = "3"; - alternate_cs = "/DeviceRGB"; - break; - case cmsSigCmykData: - n_value = "4"; - alternate_cs = "/DeviceCMYK"; - break; - default: - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Failed to embed ICC Profile."); - return NULL; - } - - cmsSaveProfileToMem(doc->colorProfile, NULL, &profile_size); - buff = (unsigned char *)calloc(profile_size, sizeof(unsigned char)); - cmsSaveProfileToMem(doc->colorProfile, buff, &profile_size); - - streamdict = pdfioDictCreate(pdf); - pdfioDictSetName(streamdict, "Alternate", alternate_cs); - pdfioDictSetName(streamdict, "N", n_value); - - icc_stream = pdfioFileCreateObj(pdf, streamdict); - - if (!icc_stream) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Failed to create ICC profile stream."); - free(buff); - return NULL; - } - - free(buff); - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: ICC Profile embedded in PDF."); - - return icc_stream; -} - -pdfio_obj_t* -embed_srgb_profile(pdfio_file_t *pdf, - pwgtopdf_doc_t *doc) -{ - pdfio_obj_t *iccbased_reference; - - doc->colorProfile = cmsCreate_sRGBProfile(); - iccbased_reference = embed_icc_profile(pdf, doc); - - return iccbased_reference; -} - -// -// Calibration function for non-Lab PDF color spaces -// Requires white point data, and if available, gamma or matrix numbers. -// -// Output: -// [/'color_space' -// << /Gamma ['gamma[0]'...'gamma[n]'] -// /WhitePoint ['wp[0]' 'wp[1]' 'wp[2]'] -// /Matrix ['matrix[0]'...'matrix[n*n]'] -// >> -// ] - -pdfio_array_t* -get_calibration_array(pdfio_file_t *pdf, - const char *color_space, - double wp[], - double gamma[], - double matrix[], - double bp[]) -{ - if ((!strcmp("/CalGray", color_space) && matrix != NULL) || wp == NULL) - return NULL; - - pdfio_array_t *calibration_array = pdfioArrayCreate(pdf); - if (!calibration_array) - return NULL; - - pdfioArrayAppendName(calibration_array, color_space); - - pdfio_dict_t *calibration_dict = pdfioDictCreate(pdf); - - if (wp != NULL) - { - pdfio_array_t *white_point_array = pdfioArrayCreate(pdf); - pdfioArrayAppendNumber(white_point_array, wp[0]); - pdfioArrayAppendNumber(white_point_array, wp[1]); - pdfioArrayAppendNumber(white_point_array, wp[2]); - pdfioDictSetArray(calibration_dict, "WhitePoint", white_point_array); - } - - if (!strcmp("/CalGray", color_space) && gamma != NULL) - pdfioDictSetNumber(calibration_dict, "Gamma", gamma[0]); - - else if (!strcmp("/CalRGB", color_space) && gamma != NULL) - { - pdfio_array_t *gamma_array = pdfioArrayCreate(pdf); - pdfioArrayAppendNumber(gamma_array, gamma[0]); - pdfioArrayAppendNumber(gamma_array, gamma[1]); - pdfioArrayAppendNumber(gamma_array, gamma[2]); - pdfioDictSetArray(calibration_dict, "Gamma", gamma_array); - } - - if (bp != NULL) - { - pdfio_array_t *black_point_array = pdfioArrayCreate(pdf); - pdfioArrayAppendNumber(black_point_array, bp[0]); - pdfioArrayAppendNumber(black_point_array, bp[1]); - pdfioArrayAppendNumber(black_point_array, bp[2]); - pdfioDictSetArray(calibration_dict, "BlackPoint", black_point_array); - } - - if (!strcmp("CalRGB", color_space) && matrix != NULL) - { - pdfio_array_t *matrix_array = pdfioArrayCreate(pdf); - for (int i = 0; i < 9; i++) - pdfioArrayAppendNumber(matrix_array, matrix[i]); - pdfioDictSetArray(calibration_dict, "Matrix", matrix_array); - } - - pdfioArrayAppendDict(calibration_array, calibration_dict); - return calibration_array; -} - - -pdfio_array_t* -get_cal_rgb_array(pdfio_file_t *pdf, - double wp[3], - double gamma[3], - double matrix[9], - double bp[3]) -{ - pdfio_array_t *ret = get_calibration_array(pdf, "CalRGB", wp, gamma, matrix, - bp); - return (ret); -} - -pdfio_array_t* -get_cal_gray_array(pdfio_file_t *pdf, - double wp[3], - double gamma[1], - double bp[3]) -{ - pdfio_array_t *ret = get_calibration_array(pdf, "CalGray", wp, gamma, 0, bp); - return (ret); -} - -// -// 'make_pclm_strips()' - Return an std::vector of QPDFObjectHandle, each -// containing the stream data of the various strips -// which make up a PCLm page. -// -// O - std::vector of QPDFObjectHandle -// I - QPDF object -// I - number of strips per page -// I - std::vector of std::shared_ptr containing data for each strip -// I - strip width -// I - strip height -// I - color space -// I - bits per component -// I - document information -// - -pdfio_stream_t** -make_pclm_strips(pdfio_file_t *pdf, - unsigned num_strips, - unsigned char **strip_data, - compression_method_t *compression_methods, - unsigned width, unsigned *strip_height, - cups_cspace_t cs, - unsigned bpc, - pwgtopdf_doc_t *doc) -{ - pdfio_stream_t **ret = (pdfio_stream_t **)malloc(num_strips * sizeof(pdfio_stream_t *)); - pdfio_dict_t *dict; - const char *color_space_name; - unsigned components = 0; - - switch (cs) - { - case CUPS_CSPACE_K: - case CUPS_CSPACE_SW: - color_space_name = "DeviceGray"; - components = 1; - break; - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_SRGB: - case CUPS_CSPACE_ADOBERGB: - color_space_name = "DeviceRGB"; - components = 3; - break; - default: - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Color space not supported."); - return NULL; - } - - for (size_t i = 0; i < num_strips; i++) - { - dict = pdfioDictCreate(pdf); - - pdfioDictSetName(dict, "Type", "XObject"); - pdfioDictSetName(dict, "Subtype", "Image"); - pdfioDictSetNumber(dict, "Width", width); - pdfioDictSetNumber(dict, "BitsPerComponent", bpc); - pdfioDictSetName(dict, "ColorSpace", color_space_name); - pdfioDictSetNumber(dict, "Height", strip_height[i]); - - pdfio_obj_t *streamObj = pdfioFileCreateObj(pdf, dict); - ret[i] = pdfioObjCreateStream(streamObj, PDFIO_FILTER_NONE); - - compression_method_t compression = compression_methods[0]; - for (unsigned j = 0; j < num_strips; j++) - compression = compression > compression_methods[j] ? compression : compression_methods[j]; - - - if (compression == FLATE_DECODE) - { - pdfioStreamWrite(ret[i], strip_data[i], strip_height[i] * width * components); - pdfioDictSetName(dict, "Filter", "FlateDecode"); - } - else if (compression == RLE_DECODE) - { - pdfioStreamWrite(ret[i], strip_data[i], strip_height[i] * width * components); - pdfioDictSetName(dict, "Filter", "RunLengthDecode"); - } - else if (compression == DCT_DECODE) - { - pdfioStreamWrite(ret[i], strip_data[i], strip_height[i] * width * components); - pdfioDictSetName(dict, "Filter", "DCTDecode"); - } - } - return ret; -} - -pdfio_obj_t* -make_image(pdfio_file_t *pdf, - unsigned char *page_data, - int data_size, - unsigned width, - unsigned height, - const char *render_intent, - cups_cspace_t cs, - unsigned bpc, - pwgtopdf_doc_t *doc) -{ - pdfio_dict_t *dict = pdfioDictCreate(pdf); - pdfio_obj_t *image_obj; - pdfio_obj_t *icc_ref; - int use_blackpoint = 0; - - pdfioDictSetName(dict, "Type", "XObject"); - pdfioDictSetName(dict, "Subtype", "Image"); - pdfioDictSetNumber(dict, "Width", width); - pdfioDictSetNumber(dict, "Height", height); - pdfioDictSetNumber(dict, "BitsPerComponent", bpc); - - if (!doc->cm_disabled && render_intent) - { - if (strcmp(render_intent, "Perceptual") == 0) - pdfioDictSetName(dict, "Intent", "Perceptual"); - else if (strcmp(render_intent, "Absolute") == 0) - pdfioDictSetName(dict, "Intent", "AbsoluteColorimetric"); - else if (strcmp(render_intent, "Relative") == 0) - pdfioDictSetName(dict, "Intent", "RelativeColorimetric"); - else if (strcmp(render_intent, "Saturation") == 0) - pdfioDictSetName(dict, "Intent", "Saturation"); - else if (strcmp(render_intent, "RelativeBpc") == 0) - { - pdfioDictSetName(dict, "Intent", "RelativeColorimetric"); - use_blackpoint = 1; - } - } - - if (doc->colorProfile != NULL && !doc->cm_disabled) - { - icc_ref = embed_icc_profile(pdf, doc); - pdfioDictSetObj(dict, "ColorSpace", icc_ref); - } - else if (!doc->cm_disabled) - { - switch (cs) - { - case CUPS_CSPACE_DEVICE1: - case CUPS_CSPACE_DEVICE2: - case CUPS_CSPACE_DEVICE3: - case CUPS_CSPACE_DEVICE4: - case CUPS_CSPACE_DEVICE5: - case CUPS_CSPACE_DEVICE6: - case CUPS_CSPACE_DEVICE7: - case CUPS_CSPACE_DEVICE8: - case CUPS_CSPACE_DEVICE9: - case CUPS_CSPACE_DEVICEA: - case CUPS_CSPACE_DEVICEB: - case CUPS_CSPACE_DEVICEC: - case CUPS_CSPACE_DEVICED: - case CUPS_CSPACE_DEVICEE: - case CUPS_CSPACE_DEVICEF: - pdfioDictSetName(dict, "ColorSpace", "DeviceCMYK"); - break; - case CUPS_CSPACE_K: - pdfioDictSetName(dict, "ColorSpace", "DeviceGray"); - break; - case CUPS_CSPACE_SW: - if (use_blackpoint) - pdfioDictSetArray(dict, "ColorSpace", get_cal_gray_array(pdf, cfCmWhitePointSGray(), - cfCmGammaSGray(), - cfCmBlackPointDefault())); - - else - pdfioDictSetArray(dict, "ColorSpace", get_cal_gray_array(pdf, cfCmWhitePointSGray(), - cfCmGammaSGray(), 0)); - break; - case CUPS_CSPACE_CMYK: - pdfioDictSetName(dict, "ColorSpace", "DeviceCMYK"); - break; - case CUPS_CSPACE_RGB: - pdfioDictSetName(dict, "ColorSpace", "DeviceRGB"); - break; - case CUPS_CSPACE_SRGB: - icc_ref = embed_srgb_profile(pdf, doc); - if(icc_ref != NULL) - pdfioDictSetObj(dict, "ColorSpace", icc_ref); - else - pdfioDictSetName(dict, "ColorSpace", "DeviceRGB"); - break; - case CUPS_CSPACE_ADOBERGB: - if (use_blackpoint) - pdfioDictSetArray(dict, "ColorSpace", get_cal_rgb_array(pdf, cfCmWhitePointAdobeRGB(), - cfCmGammaAdobeRGB(), - cfCmMatrixAdobeRGB(), - cfCmBlackPointDefault())); - else - pdfioDictSetArray(dict, "ColorSpace", get_cal_rgb_array(pdf, cfCmWhitePointAdobeRGB(), - cfCmGammaAdobeRGB(), - cfCmMatrixAdobeRGB(), 0)); - break; - default: - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Color space not supported."); - return NULL; - } - } - else if(doc->cm_disabled) - { - switch(cs) - { - case CUPS_CSPACE_K: - case CUPS_CSPACE_SW: - pdfioDictSetName(dict, "ColorSpace", "DeviceGray"); - break; - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_SRGB: - case CUPS_CSPACE_ADOBERGB: - pdfioDictSetName(dict, "ColorSpace", "DeviceRGB"); - break; - case CUPS_CSPACE_DEVICE1: - case CUPS_CSPACE_DEVICE2: - case CUPS_CSPACE_DEVICE3: - case CUPS_CSPACE_DEVICE4: - case CUPS_CSPACE_DEVICE5: - case CUPS_CSPACE_DEVICE6: - case CUPS_CSPACE_DEVICE7: - case CUPS_CSPACE_DEVICE8: - case CUPS_CSPACE_DEVICE9: - case CUPS_CSPACE_DEVICEA: - case CUPS_CSPACE_DEVICEB: - case CUPS_CSPACE_DEVICEC: - case CUPS_CSPACE_DEVICED: - case CUPS_CSPACE_DEVICEE: - case CUPS_CSPACE_DEVICEF: - case CUPS_CSPACE_CMYK: - pdfioDictSetName(dict, "ColorSpace", "DeviceCMYK"); - break; - default: - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Color space not supported."); - return NULL; - } - } - else - return NULL; - - image_obj = pdfioFileCreateObj(pdf, dict); - -#ifdef PRE_COMPRESS - uLongf compressed_size = compressBound(data_size); - unsigned char *compressed_data = (unsigned char *)malloc(compressed_size); - if (compress(compressed_data, &compressed_size, page_data, data_size) != Z_OK) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPWGToPDF: Compression failed."); - free(compressed_data); - return NULL; - } - - pdfio_stream_t *stream = pdfioObjCreateStream(image_obj, PDFIO_FILTER_NONE); - pdfioStreamWrite(stream, compressed_data, compressed_size); - pdfioStreamClose(stream); - - free(compressed_data); -#else - pdfio_stream_t *stream = pdfioStreamCreate(pdf, image_obj); - pdfioStreamWrite(stream, page_data, page_data_size); - pdfioStreamClose(stream); -#endif - return image_obj; -} - -static int -finish_page(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - pdfio_obj_t *image_obj; - char content[1024]; - size_t content_length = 0; - - if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) - { - if (!info->page_data) - return 0; - - image_obj = make_image(info->pdf, info->page_data, strlen(content), info->width, info->height, - info->render_intent, info->color_space, info->bpc, doc); - if (!image_obj) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to load image data"); - return 1; - } - - pdfio_dict_t *resources = pdfioDictCreate(info->pdf); - pdfioDictSetObj(resources, "XObject", image_obj); - } - else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - if (info->pclm_num_strips == 0) - return 0; - - for (size_t i = 0; i < info->pclm_num_strips; i++) - { - if (!info->pclm_strip_data[i]) - return 0; - } - } - - if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) - { - content_length += snprintf(content + content_length, sizeof(content) - content_length, - "%.2f 0 0 %.2f 0 0 cm\n", info->page_width, info->page_height); - content_length += snprintf(content + content_length, sizeof(content) - content_length, "/I Do\n"); - } - else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - double d = DEFAULT_PDF_UNIT / atoi(info->pclm_source_resolution_default); - content_length += snprintf(content + content_length, sizeof(content) - content_length, - "%.2f 0 0 %.2f 0 0 cm\n", d, d); - - for (unsigned i = 0; i < info->pclm_num_strips; i++) - { - unsigned yAnchor = info->height - info->pclm_strip_height[i]; - content_length += snprintf(content + content_length, sizeof(content) - content_length, - "/P <> BDC q\n%.2f 0 0 %.2f 0 %u cm\n/Image%d Do Q\n", - (double)info->width, (double)info->pclm_strip_height[i], yAnchor, i); - } - } - - pdfio_stream_t *page_content = pdfioObjCreateStream(image_obj, PDFIO_FILTER_NONE); - pdfioStreamWrite(page_content, content, content_length); - pdfioStreamClose(page_content); - - info->page_data = NULL; - memset(info->pclm_strip_data, 0, sizeof(info->pclm_strip_data)); - - return 0; -} - -// -// Perform modifications to PDF if color space conversions are needed -// - -int prepare_pdf_page(struct pdf_info *info, - unsigned width, - unsigned height, - unsigned bpl, - unsigned bpp, - unsigned bpc, - const char *render_intent, - cups_cspace_t color_space, - pwgtopdf_doc_t *doc) -{ - -#define IMAGE_CMYK_8 (bpp == 32 && bpc == 8) -#define IMAGE_CMYK_16 (bpp == 64 && bpc == 16) -#define IMAGE_RGB_8 (bpp == 24 && bpc == 8) -#define IMAGE_RGB_16 (bpp == 48 && bpc == 16) -#define IMAGE_WHITE_1 (bpp == 1 && bpc == 1) -#define IMAGE_WHITE_8 (bpp == 8 && bpc == 8) -#define IMAGE_WHITE_16 (bpp == 16 && bpc == 16) - - int error = 0; - pdf_convert_function fn = convert_pdf_no_conversion; - cmsColorSpaceSignature css; - - // Register available raster information into the PDF - info->width = width; - info->height = height; - info->line_bytes = bpl; - info->bpp = bpp; - info->bpc = bpc; - info->render_intent = strdup(render_intent); - info->color_space = color_space; - - if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - info->pclm_num_strips = - (height / info->pclm_strip_height_preferred) + - (height % info->pclm_strip_height_preferred ? 1 : 0); - - info->pclm_strip_height = (unsigned *)malloc(info->pclm_num_strips * sizeof(unsigned)); - info->pclm_strip_data = (unsigned char **)malloc(info->pclm_num_strips * sizeof(unsigned char *)); - - for (size_t i = 0; i < info->pclm_num_strips; i++) - { - info->pclm_strip_height[i] = - info->pclm_strip_height_preferred < height ? - info->pclm_strip_height_preferred : height; - height -= info->pclm_strip_height[i]; - } - } - - if (color_space == CUPS_CSPACE_K) - fn = convert_pdf_invert_colors; - - if (doc->colorProfile != NULL) - { - css = cmsGetColorSpace(doc->colorProfile); - - // Convert image and PDF color space to an embedded ICC Profile color - // space - switch (css) - { - // Convert PDF to Grayscale when using a gray profile - case cmsSigGrayData: - if (color_space == CUPS_CSPACE_CMYK) - fn = convert_pdf_cmyk_8_to_white_8; - else if (color_space == CUPS_CSPACE_RGB) - fn = convert_pdf_rgb_8_to_white_8; - else - fn = convert_pdf_invert_colors; - info->color_space = CUPS_CSPACE_K; - break; - - // Convert PDF to RGB when using an RGB profile - case cmsSigRgbData: - if (color_space == CUPS_CSPACE_CMYK) - fn = convert_pdf_cmyk_8_to_rgb_8; - else if (color_space == CUPS_CSPACE_K) - fn = convert_pdf_white_8_to_rgb_8; - info->color_space = CUPS_CSPACE_RGB; - break; - - // Convert PDF to RGB when using an RGB profile - case cmsSigCmykData: - if (color_space == CUPS_CSPACE_RGB) - fn = convert_pdf_rgb_8_to_cmyk_8; - else if (color_space == CUPS_CSPACE_K) - fn = convert_pdf_white_8_to_cmyk_8; - info->color_space = CUPS_CSPACE_CMYK; - break; - - default: - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to convert PDF from profile."); - doc->colorProfile = NULL; - error = 1; - } - } - else if (!doc->cm_disabled) - { - switch (color_space) - { - case CUPS_CSPACE_CMYK: - if (IMAGE_RGB_8) - fn = convert_pdf_rgb_8_to_cmyk_8; - else if (IMAGE_RGB_16) - fn = convert_pdf_no_conversion; - else if (IMAGE_WHITE_8) - fn = convert_pdf_white_8_to_cmyk_8; - else if (IMAGE_WHITE_16) - fn = convert_pdf_no_conversion; - break; - // Convert image to RGB - case CUPS_CSPACE_ADOBERGB: - case CUPS_CSPACE_RGB: - case CUPS_CSPACE_SRGB: - if (IMAGE_CMYK_8) - fn = convert_pdf_cmyk_8_to_rgb_8; - else if (IMAGE_CMYK_16) - fn = convert_pdf_no_conversion; - else if (IMAGE_WHITE_8) - fn = convert_pdf_white_8_to_rgb_8; - else if (IMAGE_WHITE_16) - fn = convert_pdf_no_conversion; - break; - // Convert image to Grayscale - case CUPS_CSPACE_SW: - case CUPS_CSPACE_K: - if (IMAGE_CMYK_8) - fn = convert_pdf_cmyk_8_to_white_8; - else if (IMAGE_CMYK_16) - fn = convert_pdf_no_conversion; - else if (IMAGE_RGB_8) - fn = convert_pdf_rgb_8_to_white_8; - else if (IMAGE_RGB_16) - fn = convert_pdf_no_conversion; - break; - case CUPS_CSPACE_DEVICE1: - case CUPS_CSPACE_DEVICE2: - case CUPS_CSPACE_DEVICE3: - case CUPS_CSPACE_DEVICE4: - case CUPS_CSPACE_DEVICE5: - case CUPS_CSPACE_DEVICE6: - case CUPS_CSPACE_DEVICE7: - case CUPS_CSPACE_DEVICE8: - case CUPS_CSPACE_DEVICE9: - case CUPS_CSPACE_DEVICEA: - case CUPS_CSPACE_DEVICEB: - case CUPS_CSPACE_DEVICEC: - case CUPS_CSPACE_DEVICED: - case CUPS_CSPACE_DEVICEE: - case CUPS_CSPACE_DEVICEF: - fn = convert_pdf_no_conversion; - break; - default: - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Color space not supported."); - error = 1; - break; - } - } - - if (!error) - fn(info, doc); - - return error; -} - - -static int -add_pdf_page(struct pdf_info *info, - int pagen, - unsigned width, - unsigned height, - int bpp, - int bpc, - int bpl, - const char *render_intent, - cups_cspace_t color_space, - unsigned xdpi, - unsigned ydpi, - pwgtopdf_doc_t *doc) -{ - if (finish_page(info, doc)) - return 1; - - prepare_pdf_page(info, width, height, bpl, bpp, bpc, render_intent, color_space, doc); - - if (info->height > (UINT_MAX / info->line_bytes)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Page too big"); - return 1; - } - - if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) - info->page_data = malloc(info->line_bytes * info->height); - - else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - for (size_t i = 0; i < info->pclm_num_strips; i++) - { - info->pclm_strip_data[i] = malloc(info->line_bytes * info->pclm_strip_height[i]); - } - } - - pdfio_dict_t *page_dict = pdfioDictCreate(info->pdf); - - pdfioDictSetName(page_dict, "Type", "Page"); - pdfioDictSetDict(page_dict, "Resources", pdfioDictCreate(info->pdf)); - pdfioDictSetNull(page_dict, "MediaBox"); - pdfioDictSetNull(page_dict, "Contents"); - - - info->page_width = ((double)info->width / xdpi) * DEFAULT_PDF_UNIT; - info->page_height = ((double)info->height / ydpi) * DEFAULT_PDF_UNIT; - - if (info->outformat == CF_FILTER_OUT_FORMAT_PDF) - { - pdfio_obj_t *null_obj = pdfioFileCreateObj(info->pdf, pdfioDictCreate(info->pdf)); - pdfioDictSetObj(page_dict, "Contents", null_obj); - pdfio_rect_t media_rect = make_real_box(0, 0, info->page_width, info->page_height); - pdfioDictSetRect(page_dict, "MediaBox", &media_rect); - } - - else if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - pdfio_obj_t *null_obj = pdfioFileCreateObj(info->pdf, pdfioDictCreate(info->pdf)); - pdfioDictSetObj(page_dict, "Contents", null_obj); - - pdfio_rect_t media_rect = make_real_box(0, 0, info->page_width + 0.5, info->page_height + 0.5); - pdfioDictSetRect(page_dict, "MediaBox", &media_rect); - } - - pdfio_obj_t *page = pdfioFileCreateObj(info->pdf, page_dict); - - info->page = page; // we want to keep a - // reference - return 0; -} - -static int -close_pdf_file(struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - if (finish_page(info, doc)) - return 1; - - pdfioFileClose(info->pdf); - - return 0; -} - -static void -pdf_set_line(struct pdf_info * info, - unsigned line_n, - unsigned char *line, - pwgtopdf_doc_t *doc) -{ - if (line_n > info->height) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Bad line %d", line_n); - return; - } - - if (info->outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - // copy line data into appropriate pclm strip - size_t strip_num = line_n / info->pclm_strip_height_preferred; - unsigned line_strip = line_n - - strip_num * info->pclm_strip_height_preferred; - memcpy(((info->pclm_strip_data[strip_num]) + - (line_strip*info->line_bytes)), line, info->line_bytes); - } - else - memcpy((info->page_data + (line_n * info->line_bytes)), - line, info->line_bytes); -} - -static int -convert_raster(cups_raster_t *ras, - unsigned width, - unsigned height, - int bpp, - int bpl, - struct pdf_info *info, - pwgtopdf_doc_t *doc) -{ - // We should be at raster start - int i; - unsigned cur_line = 0; - unsigned char *PixelBuffer, *ptr = NULL, *buff; - - PixelBuffer = (unsigned char *)malloc(bpl); - buff = (unsigned char *)malloc(info->line_bytes); - - do - { - // Read raster data... - cupsRasterReadPixels(ras, PixelBuffer, bpl); - -#if !ARCH_IS_BIG_ENDIAN - if (info->bpc == 16) - { - // Swap byte pairs for endianess (cupsRasterReadPixels() switches - // from Big Endian back to the system's Endian) - for (i = bpl, ptr = PixelBuffer; i > 0; i -= 2, ptr += 2) - { - unsigned char swap = *ptr; - *ptr = *(ptr + 1); - *(ptr + 1) = swap; - } - } -#endif // !ARCH_IS_BIG_ENDIAN - - // perform bit operations if necessary - doc->bit_function(PixelBuffer, ptr, bpl); - - // write lines and color convert when necessary - pdf_set_line(info, cur_line, doc->conversion_function(PixelBuffer, - buff, width), - doc); - ++cur_line; - } - while (cur_line < height); - - free(buff); - free(PixelBuffer); - - return (0); -} - -static int -set_profile(const char *path, - pwgtopdf_doc_t *doc) -{ - if (path != NULL) - doc->colorProfile = cmsOpenProfileFromFile(path, "r"); - - if (doc->colorProfile != NULL) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Load profile successful."); - return (0); - } - else - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to load profile."); - return (1); - } -} - - -int // O - Error status -cfFilterPWGToPDF(int inputfd, // I - File descriptor input stream - int outputfd, // I - File descriptor output stream - int inputseekable, // I - Is input stream seekable? (unused) - cf_filter_data_t *data, // I - Job and printer data - void *parameters) // I - Filter-specific parameters (outformat) -{ - cups_len_t i; - char *t; - pwgtopdf_doc_t doc; // Document information - FILE *outputfp; // Output data stream - cf_filter_out_format_t outformat; // Output format - int Page, empty = 1; - cf_cm_calibration_t cm_calibrate; // Status of CUPS color management - // ("on" or "off") - struct pdf_info pdf; - cups_raster_t *ras; // Raster stream for printing - cups_page_header_t header; // Page header from file - ipp_t *printer_attrs = data->printer_attrs; // Printer attributes from - // printer data - ipp_attribute_t *ipp_attr; // Printer attribute - const char *profile_name = NULL; // IPP Profile Name - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - int total_attrs; - char buf[1024]; - const char *kw; - - (void)inputseekable; - - if (parameters) - { - outformat = *(cf_filter_out_format_t *)parameters; - if (outformat != CF_FILTER_OUT_FORMAT_PCLM) - outformat = CF_FILTER_OUT_FORMAT_PDF; - } - else - { - t = data->final_content_type; - if (t) - { - if (strcasestr(t, "pclm")) - outformat = CF_FILTER_OUT_FORMAT_PCLM; - else if (strcasestr(t, "pdf")) - outformat = CF_FILTER_OUT_FORMAT_PDF; - else - outformat = CF_FILTER_OUT_FORMAT_PDF; - } - else - outformat = CF_FILTER_OUT_FORMAT_PDF; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: OUTFORMAT=\"%s\"", - outformat == CF_FILTER_OUT_FORMAT_PDF ? "PDF" : "PCLM"); - - // - // Open the output data stream specified by the outputfd... - // - - if ((outputfp = fdopen(outputfd, "w")) == NULL) - { - if (!iscanceled || !iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Unable to open output data stream."); - } - - return (1); - } - - doc.outputfp = outputfp; - // Logging function - doc.logfunc = log; - doc.logdata = ld; - // Job-is-canceled function - doc.iscanceledfunc = iscanceled; - doc.iscanceleddata = icd; - - // support the CUPS "cm-calibration" option - cm_calibrate = cfCmGetCupsColorCalibrateMode(data); - - if (outformat == CF_FILTER_OUT_FORMAT_PCLM || - cm_calibrate == CF_CM_CALIBRATION_ENABLED) - doc.cm_disabled = 1; - else - doc.cm_disabled = cfCmIsPrinterCmDisabled(data); - - if (outformat == CF_FILTER_OUT_FORMAT_PCLM && printer_attrs == NULL) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToPDF: PCLm output: No printer IPP attributes are supplied, PCLm output not possible."); - return (1); - } - - // Transform - ras = cupsRasterOpen(inputfd, CUPS_RASTER_READ); - - // Process pages as needed... - Page = 0; - - // Get PCLm parameters from printer IPP attributes - if (outformat == CF_FILTER_OUT_FORMAT_PCLM) - { - if (log) - { - log(ld, CF_LOGLEVEL_DEBUG, "PCLm-related printer IPP attributes:"); - total_attrs = 0; - ipp_attr = ippGetFirstAttribute(printer_attrs); - while (ipp_attr) - { - if (strncmp(ippGetName(ipp_attr), "pclm-", 5) == 0) - { - total_attrs ++; - ippAttributeString(ipp_attr, buf, sizeof(buf)); - log(ld, CF_LOGLEVEL_DEBUG, " Attr: %s",ippGetName(ipp_attr)); - log(ld, CF_LOGLEVEL_DEBUG, " Value: %s", buf); - for (i = 0; i < ippGetCount(ipp_attr); i ++) - if ((kw = ippGetString(ipp_attr, i, NULL)) != NULL) - log(ld, CF_LOGLEVEL_DEBUG, " Keyword: %s", kw); - } - ipp_attr = ippGetNextAttribute(printer_attrs); - } - log(ld, CF_LOGLEVEL_DEBUG, " %d attributes", total_attrs); - } - - char *attr_name = (char *)"pclm-strip-height-preferred"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value %d", - attr_name, ippGetInteger(ipp_attr, 0)); - pdf.pclm_strip_height_preferred = ippGetInteger(ipp_attr, 0); - } - else - pdf.pclm_strip_height_preferred = 16; // default strip height - - attr_name = (char *)"pclm-strip-height-supported"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\"", - attr_name); - pdf.pclm_strip_height_supported = NULL; // remove default value = 16 - for (i = 0; i < ippGetCount(ipp_attr); i ++) - pdf.pclm_strip_height_supported[i] = ippGetInteger(ipp_attr, i); - } - - - attr_name = (char *)"pclm-raster-back-side"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", - attr_name, ippGetString(ipp_attr, 0, NULL)); - pdf.pclm_raster_back_side = ippGetString(ipp_attr, 0, NULL); - } - - attr_name = (char *)"pclm-source-resolution-supported"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - ippAttributeString(ipp_attr, buf, sizeof(buf)); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", - attr_name, buf); - int size = (int)sizeof(buf); - - pdf.pclm_source_resolution_supported = split_strings(buf, ",", &size); - } - - attr_name = (char *)"pclm-source-resolution-default"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - ippAttributeString(ipp_attr, buf, sizeof(buf)); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", - attr_name, buf); - pdf.pclm_source_resolution_default = buf; - } - else if (sizeof(pdf.pclm_source_resolution_supported) > 0) - { - pdf.pclm_source_resolution_default = - pdf.pclm_source_resolution_supported[0]; - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" missing, taking first item of \"pclm-source-resolution-supported\" as default resolution", - attr_name); - } - else - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToPDF: PCLm output: Printer IPP attributes do not contain printer resolution information for PCLm."); - return (1); - } - - attr_name = (char *)"pclm-compression-method-preferred"; - if ((ipp_attr = ippFindAttribute(printer_attrs, attr_name, - IPP_TAG_ZERO)) != NULL) - { - ippAttributeString(ipp_attr, buf, sizeof(buf)); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Printer PCLm attribute \"%s\" with value \"%s\"", - attr_name, buf); - - } - // If the compression methods is none of the above or is erreneous - // use FLATE as compression method and show a warning. - if (pdf.pclm_compression_method_preferred == NULL) - { - if (log) log(ld, CF_LOGLEVEL_WARN, - "(pwgtopclm) Unable parse Printer attribute \"%s\". " - "Using FLATE for encoding image streams.", attr_name); - } - } - - while (cupsRasterReadHeader(ras, &header)) - { - if (iscanceled && iscanceled(icd)) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Job canceled"); - break; - } - - if (empty) - { - empty = 0; - // We have a valid input page, so create PDF file - if (create_pdf_file(&pdf, outformat) != 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToPDF: Unable to create PDF file"); - return (1); - } - } - - // Write a status message with the page number - Page ++; - if (log) log(ld, CF_LOGLEVEL_INFO, - "cfFilterPWGToPDF: Starting page %d.", Page); - - // Update rendering intent with user settings or the default - cfGetPrintRenderIntent(data, header.cupsRenderingIntent, - sizeof(header.cupsRenderingIntent)); - - // Use "profile=profile_name.icc" to embed 'profile_name.icc' into the PDF - // for testing. Forces color management to enable. - if (outformat == CF_FILTER_OUT_FORMAT_PDF && - (profile_name = cupsGetOption("profile", data->num_options, - data->options)) != NULL) - { - set_profile(profile_name, &doc); - doc.cm_disabled = 0; - } - if (doc.colorProfile != NULL) - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: TEST ICC Profile specified (color " - "management forced ON): \n[%s]", profile_name); - - // Add a new page to PDF file - if (add_pdf_page(&pdf, Page, header.cupsWidth, header.cupsHeight, - header.cupsBitsPerPixel, header.cupsBitsPerColor, - header.cupsBytesPerLine, header.cupsRenderingIntent, - header.cupsColorSpace, header.HWResolution[0], - header.HWResolution[1], &doc) != 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToPDF: Unable to start new PDF page"); - return (1); - } - - // Write the bit map into the PDF file - if (convert_raster(ras, header.cupsWidth, header.cupsHeight, - header.cupsBitsPerPixel, header.cupsBytesPerLine, - &pdf, &doc) != 0) - { - if (log) log(ld, CF_LOGLEVEL_ERROR, - "cfFilterPWGToPDF: Failed to convert page bitmap"); - return (1); - } - } - - if (empty) - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPWGToPDF: Input is empty, outputting empty file."); - cupsRasterClose(ras); - return (0); - } - - close_pdf_file(&pdf, &doc); // output to outputfp - - if (doc.colorProfile != NULL) - cmsCloseProfile(doc.colorProfile); - - cupsRasterClose(ras); - fclose(outputfp); - - return (Page == 0); -} diff --git a/cupsfilters/pdf.c b/cupsfilters/pdf.c index 2fbe385e..d947cd89 100644 --- a/cupsfilters/pdf.c +++ b/cupsfilters/pdf.c @@ -12,7 +12,7 @@ #include #include #include -#include "pdf.h" +#include "C-pdf.h" #include #include From f7f3a6646325af09173ac2df66b29ff8feb46dcf Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Wed, 20 Nov 2024 23:15:31 +0530 Subject: [PATCH 56/64] makefile edit --- Makefile.am | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Makefile.am b/Makefile.am index 0133091a..2b947d2e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -86,7 +86,7 @@ pkgfiltersinclude_DATA = \ cupsfilters/image.h \ cupsfilters/ipp.h \ cupsfilters/log.h \ - cupsfilters/C-pdf.h \ + cupsfilters/pdf.h \ cupsfilters/raster.h lib_LTLIBRARIES = libcupsfilters.la @@ -178,19 +178,19 @@ libcupsfilters_la_SOURCES = \ cupsfilters/lut.c \ cupsfilters/mupdftopwg.c \ cupsfilters/pack.c \ - cupsfilters/C-pclmtoraster.c \ - cupsfilters/C-pdf.c \ - cupsfilters/pdftopdf/C-pdftopdf.c \ - cupsfilters/pdftopdf/C-pdftopdf-private.h \ - cupsfilters/pdftopdf/C-pdftopdf-processor.c \ + cupsfilters/pclmtoraster.c \ + cupsfilters/pdf.c \ + cupsfilters/pdftopdf/pdftopdf.c \ + cupsfilters/pdftopdf/pdftopdf-private.h \ + cupsfilters/pdftopdf/pdftopdf-processor.c \ cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c \ cupsfilters/pdftopdf/processor.h \ - cupsfilters/pdftopdf/C-pptypes.c \ - cupsfilters/pdftopdf/C-pptypes-private.h \ - cupsfilters/pdftopdf/C-nup.c \ - cupsfilters/pdftopdf/C-nup-private.h \ - cupsfilters/pdftopdf/C-intervalset.c \ - cupsfilters/pdftopdf/C-intervalset-private.h \ + cupsfilters/pdftopdf/pptypes.c \ + cupsfilters/pdftopdf/pptypes-private.h \ + cupsfilters/pdftopdf/nup.c \ + cupsfilters/pdftopdf/nup-private.h \ + cupsfilters/pdftopdf/intervalset.c \ + cupsfilters/pdftopdf/intervalset-private.h \ cupsfilters/pdftopdf/pdfio-tools.c \ cupsfilters/pdftopdf/pdfio-tools-private.h \ cupsfilters/pdftopdf/pdfio-xobject.c \ @@ -202,7 +202,7 @@ libcupsfilters_la_SOURCES = \ cupsfilters/pdftoraster.cxx \ cupsfilters/pdfutils.c \ cupsfilters/pdfutils-private.h \ - cupsfilters/C-pwgtopdf.c \ + cupsfilters/pwgtopdf.c \ cupsfilters/pwgtoraster.c \ cupsfilters/raster.c \ cupsfilters/rastertopwg.c \ From 41ec0720d4aa36dd944e7ed7e0892782e445858d Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Wed, 20 Nov 2024 23:34:06 +0530 Subject: [PATCH 57/64] renamed --- cupsfilters/bannertopdf.c | 2 +- cupsfilters/ghostscript.c | 2 +- cupsfilters/pdf.c | 2 +- cupsfilters/pdftopdf/C-intervalset-private.h | 44 - cupsfilters/pdftopdf/C-intervalset.c | 180 ---- cupsfilters/pdftopdf/C-nup-private.h | 55 - cupsfilters/pdftopdf/C-nup.c | 336 ------ cupsfilters/pdftopdf/C-pdftopdf-private.h | 25 - .../pdftopdf/C-pdftopdf-processor-private.h | 113 -- cupsfilters/pdftopdf/C-pdftopdf-processor.c | 583 ----------- cupsfilters/pdftopdf/C-pdftopdf.c | 964 ------------------ cupsfilters/pdftopdf/C-pptypes-private.h | 80 -- cupsfilters/pdftopdf/C-pptypes.c | 279 ----- cupsfilters/pdftopdf/pdfio-pdftopdf-private.h | 2 +- .../pdftopdf/pdftopdf-processor-private.h | 8 +- cupsfilters/pdftopdf/processor.h | 10 +- 16 files changed, 13 insertions(+), 2672 deletions(-) delete mode 100644 cupsfilters/pdftopdf/C-intervalset-private.h delete mode 100644 cupsfilters/pdftopdf/C-intervalset.c delete mode 100644 cupsfilters/pdftopdf/C-nup-private.h delete mode 100644 cupsfilters/pdftopdf/C-nup.c delete mode 100644 cupsfilters/pdftopdf/C-pdftopdf-private.h delete mode 100644 cupsfilters/pdftopdf/C-pdftopdf-processor-private.h delete mode 100644 cupsfilters/pdftopdf/C-pdftopdf-processor.c delete mode 100644 cupsfilters/pdftopdf/C-pdftopdf.c delete mode 100644 cupsfilters/pdftopdf/C-pptypes-private.h delete mode 100644 cupsfilters/pdftopdf/C-pptypes.c diff --git a/cupsfilters/bannertopdf.c b/cupsfilters/bannertopdf.c index 89d5f231..eb9020d6 100644 --- a/cupsfilters/bannertopdf.c +++ b/cupsfilters/bannertopdf.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include diff --git a/cupsfilters/ghostscript.c b/cupsfilters/ghostscript.c index 153474fc..5d17d768 100644 --- a/cupsfilters/ghostscript.c +++ b/cupsfilters/ghostscript.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/cupsfilters/pdf.c b/cupsfilters/pdf.c index d947cd89..2fbe385e 100644 --- a/cupsfilters/pdf.c +++ b/cupsfilters/pdf.c @@ -12,7 +12,7 @@ #include #include #include -#include "C-pdf.h" +#include "pdf.h" #include #include diff --git a/cupsfilters/pdftopdf/C-intervalset-private.h b/cupsfilters/pdftopdf/C-intervalset-private.h deleted file mode 100644 index f5c04bf5..00000000 --- a/cupsfilters/pdftopdf/C-intervalset-private.h +++ /dev/null @@ -1,44 +0,0 @@ -// -// Copyright 2024 Uddhav Phatak -#include - -typedef struct -{ - int start; - int end; -} interval_t; - -typedef struct -{ - interval_t *data; - size_t size; - size_t capacity; -} _cfPDFToPDFIntervalSet; - -extern const int _cfPDFToPDFIntervalSet_npos; - -void _cfPDFToPDFIntervalSet_init(_cfPDFToPDFIntervalSet *set); -void _cfPDFToPDFIntervalSet_clear(_cfPDFToPDFIntervalSet *set); -void _cfPDFToPDFIntervalSet_add(_cfPDFToPDFIntervalSet *set, int start, int end); -void _cfPDFToPDFIntervalSet_add_single(_cfPDFToPDFIntervalSet *set, int start); -void _cfPDFToPDFIntervalSet_finish(_cfPDFToPDFIntervalSet *set); - -size_t _cfPDFToPDFIntervalSet_size(const _cfPDFToPDFIntervalSet *set); - -bool _cfPDFToPDFIntervalSet_contains(const _cfPDFToPDFIntervalSet *set, int val); -int _cfPDFToPDFIntervalSet_next(const _cfPDFToPDFIntervalSet *set, int val); - -void _cfPDFToPDFIntervalSet_dump(const _cfPDFToPDFIntervalSet *set, pdftopdf_doc_t *doc); - -#endif // !_CUPS_FILTERS_PDFTOPDF_INTERVALSET_H_ - diff --git a/cupsfilters/pdftopdf/C-intervalset.c b/cupsfilters/pdftopdf/C-intervalset.c deleted file mode 100644 index 488323f5..00000000 --- a/cupsfilters/pdftopdf/C-intervalset.c +++ /dev/null @@ -1,180 +0,0 @@ -// -// Copyright 2024 Uddhav Phatak -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "C-intervalset-private.h" -#include -#include -#include -#include -#include -#include "cupsfilters/debug-internal.h" - -const int _cfPDFToPDFIntervalSet_npos = INT_MAX; - -void -_cfPDFToPDFIntervalSet_init(_cfPDFToPDFIntervalSet *set) // {{{ -{ - set->data = NULL; - set->size = 0; - set->capacity = 0; -} -// }}} - -void -_cfPDFToPDFIntervalSet_clear(_cfPDFToPDFIntervalSet *set) // {{{ -{ - free(set->data); - set->data = NULL; - set->size = 0; - set->capacity = 0; -} -// }}} - -void -_cfPDFToPDFIntervalSet_add(_cfPDFToPDFIntervalSet *set, int start, int end) // {{{ -{ - if (start >= end) - return; - - if (set->size == set->capacity) - { - set->capacity = set->capacity == 0 ? 4 : set->capacity * 2; - set->data = realloc(set->data, set->capacity * sizeof(interval_t)); - } - - set->data[set->size].start = start; - set->data[set->size].end = end; - set->size++; -} -// }}} - -void -_cfPDFToPDFIntervalSet_add_single(_cfPDFToPDFIntervalSet *set, - int start) // {{{ -{ - key_t end = _cfPDFToPDFIntervalSet_npos; - - if (start >= end) - return; - - if (set->size == set->capacity) - { - set->capacity = set->capacity == 0 ? 4 : set->capacity * 2; - set->data = realloc(set->data, set->capacity * sizeof(interval_t)); - if (set->data == NULL) - return; - } - - set->data[set->size].start = start; - set->data[set->size].end = end; - set->size++; -} -// }}} - -static int -compare_intervals(const void *a, const void *b) // {{{ -{ - interval_t *ia = (interval_t *)a; - interval_t *ib = (interval_t *)b; - if (ia->start != ib->start) - return ia->start - ib->start; - return ia->end - ib->end; -} -// }}} - -void -_cfPDFToPDFIntervalSet_finish(_cfPDFToPDFIntervalSet *set) // {{{ -{ - if (set->size == 0) - return; - - qsort(set->data, set->size, sizeof(interval_t), compare_intervals); - - size_t new_size = 0; - for (size_t i = 1; i < set->size; i++) - { - if (set->data[new_size].end >= set->data[i].start) - { - if (set->data[new_size].end < set->data[i].end) - { - set->data[new_size].end = set->data[i].end; - } - } - else - { - new_size++; - set->data[new_size] = set->data[i]; - } - } - set->size = new_size + 1; -} -// }}}} - -size_t -_cfPDFToPDFIntervalSet_size(const _cfPDFToPDFIntervalSet *set) // {{{ -{ - return set->size; -} -// }}} - -bool -_cfPDFToPDFIntervalSet_contains(const _cfPDFToPDFIntervalSet *set, - int val) // {{{ -{ - for (size_t i = 0; i < set->size; i++) - { - if (val >= set->data[i].start && val < set->data[i].end) - { - return true; - } - } - return false; -} -// }}} - -int -_cfPDFToPDFIntervalSet_next(const _cfPDFToPDFIntervalSet *set, - int val) // {{{ -{ - val++; - for (size_t i = 0; i < set->size; i++) - { - if (val < set->data[i].end) - { - if (val >= set->data[i].start) - { - return val; - } - else - { - return set->data[i].start; - } - } - } - return _cfPDFToPDFIntervalSet_npos; -} -// }}} - -void -_cfPDFToPDFIntervalSet_dump(const _cfPDFToPDFIntervalSet *set, - pdftopdf_doc_t *doc) // {{{ -{ - if (set->size == 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: (empty)"); - return; - } - - for (size_t i = 0; i < set->size; i++) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: [%d,%d)", - set->data[i].start, set->data[i].end); - } -} -// }}} diff --git a/cupsfilters/pdftopdf/C-nup-private.h b/cupsfilters/pdftopdf/C-nup-private.h deleted file mode 100644 index f0a4cf7b..00000000 --- a/cupsfilters/pdftopdf/C-nup-private.h +++ /dev/null @@ -1,55 +0,0 @@ -// -//Copyright 2024 Uddhav Phatak -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_NUP_H_ -#define _CUPS_FILTERS_PDFTOPDF_NUP_H_ - -#include "C-pptypes-private.h" -#include - -typedef struct _cfPDFToPDFNupParameters -{ - int nupX, nupY; - float width, height; - bool landscape; - - pdftopdf_axis_e first; - pdftopdf_position_e xstart, ystart; - pdftopdf_position_e xalign, yalign; -} _cfPDFToPDFNupParameters; - -void _cfPDFToPDFNupParameters_init(_cfPDFToPDFNupParameters *nupParams); -bool _cfPDFToPDFNupParameters_possible(int nup); -void _cfPDFToPDFNupParameters_preset(int nup, _cfPDFToPDFNupParameters *ret); -void _cfPDFToPDFNupParameters_dump(const _cfPDFToPDFNupParameters *nupParams, pdftopdf_doc_t *doc); - -typedef struct _cfPDFToPDFNupPageEdit -{ - float xpos, ypos; - float scale; - - _cfPDFToPDFPageRect sub; -} _cfPDFToPDFNupPageEdit; - -void _cfPDFToPDFNupPageEdit_dump(const _cfPDFToPDFNupPageEdit *edit, pdftopdf_doc_t *doc); - -typedef struct _cfPDFToPDFNupState -{ - _cfPDFToPDFNupParameters param; - int in_pages, out_pages; - int nup; - int subpage; -} _cfPDFToPDFNupState; - -void _cfPDFToPDFNupState_init(_cfPDFToPDFNupState *state, const _cfPDFToPDFNupParameters *param); -void _cfPDFToPDFNupState_reset(_cfPDFToPDFNupState *state); -bool _cfPDFToPDFNupState_next_page(_cfPDFToPDFNupState *state, float in_width, float in_height, _cfPDFToPDFNupPageEdit *ret); - -bool _cfPDFToPDFParseNupLayout(const char *val, _cfPDFToPDFNupParameters *ret); - -#endif // !_CUPS_FILTERS_PDFTOPDF_NUP_H_ - diff --git a/cupsfilters/pdftopdf/C-nup.c b/cupsfilters/pdftopdf/C-nup.c deleted file mode 100644 index e607a3d6..00000000 --- a/cupsfilters/pdftopdf/C-nup.c +++ /dev/null @@ -1,336 +0,0 @@ -// -// Copyright 2024 Uddhav Phatak -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "C-nup-private.h" -#include -#include -#include -#include "cupsfilters/debug-internal.h" - -void -_cfPDFToPDFNupParameters_init(_cfPDFToPDFNupParameters *nupParams) // {{{ -{ - nupParams->nupX = 1; - nupParams->nupY = 1; - nupParams->width = NAN; - nupParams->height = NAN; - nupParams->landscape = false; - nupParams->first = X; - nupParams->xstart = LEFT; - nupParams->ystart = TOP; - nupParams->xalign = CENTER; - nupParams->yalign = CENTER; -} -// }}} - -void -_cfPDFToPDFNupParameters_dump(const _cfPDFToPDFNupParameters *nupParams, - pdftopdf_doc_t *doc) // {{{ -{ - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: NupX: %d, NupY: %d, " - "width: %f, height: %f", - nupParams->nupX, nupParams->nupY, - nupParams->width, nupParams->height); - - int opos = -1, - fpos = -1, - spos = -1; - - if (nupParams->xstart == LEFT) - fpos = 0; - else if (nupParams->xstart == RIGHT) - fpos = 1; - - if (nupParams->ystart == LEFT) - spos = 0; - else if (nupParams->ystart == RIGHT) - spos = 1; - - if (nupParams->first == X) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: First Axis: X"); - opos = 0; - } - else if (nupParams->first == Y) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: First Axis: Y"); - opos = 2; - int temp = fpos; - fpos = spos; - spos = temp; - } - - if ((opos == -1) || (fpos == -1) || (spos == -1)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Bad Spec: %d; start: %d, %d", - nupParams->first, nupParams->xstart, nupParams->ystart); - } - else - { - static const char *order[4] = {"lr", "rl", "bt", "tb"}; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Order: %s%s", - order[opos + fpos], - order[(opos + 2) % 4 + spos]); - } - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Alignment:"); - _cfPDFToPDFPositionAndAxisDump(nupParams->xalign, X, doc); - _cfPDFToPDFPositionAndAxisDump(nupParams->yalign, Y, doc); -} -// }}} - -bool -_cfPDFToPDFNupParameters_possible(int nup) // {{{ -{ - return ((nup >= 1) && (nup <= 16) && - ((nup != 5) && (nup != 7) && (nup != 11) && (nup != 13) && - (nup != 14))); -} -// }}} - -void -_cfPDFToPDFNupParameters_preset(int nup, - _cfPDFToPDFNupParameters *ret) // {{{ -{ - switch (nup) - { - case 1: - ret->nupX = 1; - ret->nupY = 1; - break; - case 2: - ret->nupX = 2; - ret->nupY = 1; - ret->landscape = true; - break; - case 3: - ret->nupX = 3; - ret->nupY = 1; - ret->landscape = true; - break; - case 4: - ret->nupX = 2; - ret->nupY = 2; - break; - case 6: - ret->nupX = 3; - ret->nupY = 2; - ret->landscape = true; - break; - case 8: - ret->nupX = 4; - ret->nupY = 2; - ret->landscape = true; - break; - case 9: - ret->nupX = 3; - ret->nupY = 3; - break; - case 10: - ret->nupX = 5; - ret->nupY = 2; - ret->landscape = true; - break; - case 12: - ret->nupX = 3; - ret->nupY = 4; - break; - case 15: - ret->nupX = 5; - ret->nupY = 3; - ret->landscape = true; - break; - case 16: - ret->nupX = 4; - ret->nupY = 4; - break; - } -} -// }}} - -void -_cfPDFToPDFNupState_init(_cfPDFToPDFNupState *state, - const _cfPDFToPDFNupParameters *param) // {{{ -{ - state->param = *param; - state->in_pages = 0; - state->out_pages = 0; - state->nup = param->nupX * param->nupY; - state->subpage = state->nup; -} -// }}} - -void -_cfPDFToPDFNupState_reset(_cfPDFToPDFNupState *state) // {{{ -{ - state->in_pages = 0; - state->out_pages = 0; - state->subpage = state->nup; -} -// }}} - -void -_cfPDFToPDFNupPageEdit_dump(const _cfPDFToPDFNupPageEdit *edit, - pdftopdf_doc_t *doc) // {{{ -{ - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: xpos: %f, ypos: %f, scale: %f", - edit->xpos, edit->ypos, edit->scale); - _cfPDFToPDFPageRect_dump(&edit->sub, doc); -} -// }}} - -typedef struct { - int first; - int second; -} int_pair; - -static int_pair -_cfPDFToPDFNupState_convert_order(const _cfPDFToPDFNupState *state, - int subpage) // {{{ -{ - int subx, suby; - if (state->param.first == X) - { - subx = subpage % state->param.nupX; - suby = subpage / state->param.nupX; - } - else - { - subx = subpage / state->param.nupY; - suby = subpage % state->param.nupY; - } - - subx = (state->param.nupX - 1) * (state->param.xstart + 1) / 2 - state->param.xstart * subx; - suby = (state->param.nupY - 1) * (state->param.ystart + 1) / 2 - state->param.ystart * suby; - - int_pair result = {subx, suby}; - return result; -} -// }}} - -static float -lin(pdftopdf_position_e pos, float size) // {{{ -{ - if (pos == -1) - return 0; - else if (pos == 0) - return size / 2; - else if (pos == 1) - return size; - return size * (pos + 1) / 2; -} -// }}} - -void -_cfPDFToPDFNupState_calculate_edit(const _cfPDFToPDFNupState *state, - int subx, int suby, - _cfPDFToPDFNupPageEdit *ret) // {{{ -{ - const float width = state->param.width / state->param.nupX; - const float height = state->param.height / state->param.nupY; - - ret->xpos = subx * width; - ret->ypos = suby * height; - - const float scalex = width / ret->sub.width; - const float scaley = height / ret->sub.height; - float subwidth = ret->sub.width * scaley; - float subheight = ret->sub.height * scalex; - - if (scalex > scaley) - { - ret->scale = scaley; - subheight = height; - ret->xpos += lin(state->param.xalign, width - subwidth); - } - else - { - ret->scale = scalex; - subwidth = width; - ret->ypos += lin(state->param.yalign, height - subheight); - } - - ret->sub.left = ret->xpos; - ret->sub.bottom = ret->ypos; - ret->sub.right = ret->sub.left + subwidth; - ret->sub.top = ret->sub.bottom + subheight; -} -// }}} - -bool -_cfPDFToPDFNupState_next_page(_cfPDFToPDFNupState *state, - float in_width, float in_height, - _cfPDFToPDFNupPageEdit *ret) // {{{ -{ - state->in_pages++; - state->subpage++; - if (state->subpage >= state->nup) - { - state->subpage = 0; - state->out_pages++; - } - - ret->sub.width = in_width; - ret->sub.height = in_height; - - int_pair sub = _cfPDFToPDFNupState_convert_order(state, state->subpage); - _cfPDFToPDFNupState_calculate_edit(state, sub.first, sub.second, ret); - - return (state->subpage == 0); -} -// }}} - -static int_pair -parsePosition(char a, char b) // {{{ -{ - a |= 0x20; // make lowercase - b |= 0x20; - if ((a == 'l') && (b == 'r')) - return (int_pair){X, LEFT}; - else if ((a == 'r') && (b == 'l')) - return (int_pair){X, RIGHT}; - else if ((a == 't') && (b == 'b')) - return (int_pair){Y, TOP}; - else if ((a == 'b') && (b == 't')) - return (int_pair){Y, BOTTOM}; - return (int_pair){X, CENTER}; -} -// }}} - -bool -_cfPDFToPDFParseNupLayout(const char *val, - _cfPDFToPDFNupParameters *ret) // {{{ -{ - int_pair pos0 = parsePosition(val[0], val[1]); - if (pos0.second == CENTER) - return false; - int_pair pos1 = parsePosition(val[2], val[3]); - if ((pos1.second == CENTER) || (pos0.first == pos1.first)) - return false; - - ret->first = pos0.first; - if (ret->first == X) - { - ret->xstart = pos0.second; - ret->ystart = pos1.second; - } - else - { - ret->xstart = pos1.second; - ret->ystart = pos0.second; - } - - return (val[4] == 0); -} -// }}} - diff --git a/cupsfilters/pdftopdf/C-pdftopdf-private.h b/cupsfilters/pdftopdf/C-pdftopdf-private.h deleted file mode 100644 index 804408b0..00000000 --- a/cupsfilters/pdftopdf/C-pdftopdf-private.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// Copyright 2020 by Jai Luthra. -// Copyright 2024 Uddhav Phatak -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_PDFTOPDF_H -#define _CUPS_FILTERS_PDFTOPDF_PDFTOPDF_H - -#include - -typedef struct // **** Document information **** -{ - cf_logfunc_t logfunc; // Log function - void *logdata; // Log data - cf_filter_iscanceledfunc_t iscanceledfunc; // Function returning 1 when - // job is canceled, NULL for not - // supporting stop on cancel - void *iscanceleddata; // User data for is-canceled - // function, can be NULL -} pdftopdf_doc_t; - -#endif // !_CUPS_FILTERS_PDFTOPDF_PDFTOPDF_H diff --git a/cupsfilters/pdftopdf/C-pdftopdf-processor-private.h b/cupsfilters/pdftopdf/C-pdftopdf-processor-private.h deleted file mode 100644 index fc3a5943..00000000 --- a/cupsfilters/pdftopdf/C-pdftopdf-processor-private.h +++ /dev/null @@ -1,113 +0,0 @@ -// -// Copyright 2024 Uddhav Phatak -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef C_PDFTOPDF_PROCESSOR_PRIVATE_H -#define C_PDFTOPDF_PROCESSOR_PRIVATE_H - -#include "C-pptypes-private.h" -#include "C-nup-private.h" -#include "C-pdftopdf-private.h" -#include "pdfio-pdftopdf-processor-private.h" -#include "C-intervalset-private.h" -#include -#include - -typedef enum { - CF_PDFTOPDF_BOOKLET_OFF, - CF_PDFTOPDF_BOOKLET_ON, - CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE -} pdftopdf_booklet_mode_e; - -typedef struct _cfPDFToPDF_PDFioProcessor{ - - // 1st mode: existing - pdfio_obj_t *page; // Equivalent to QPDFObjectHandle - int no; - - // 2nd mode: create new - HashTable *xobjs; // Pointer to a single HashTable - - char *content; // Equivalent to std::string content - - pdftopdf_rotation_e rotation; - - // Other members - pdfio_file_t *pdf; // Equivalent to std::unique_ptr - pdfio_obj_t **orig_pages; // Equivalent to std::vector - size_t orig_pages_size; // Current number of pages - size_t orig_pages_capacity; // Capacity for page array - - bool hasCM; - char *extraheader; -} _cfPDFToPDF_PDFioProcessor; - - -typedef struct { - int job_id, num_copies; - const char *user, *title; - bool pagesize_requested; - bool fitplot; - bool fillprint; // print-scaling = fill - bool cropfit; // -o crop-to-fit - bool autoprint; // print-scaling = auto - bool autofit; // print-scaling = auto-fit - bool fidelity; - bool no_orientation; - _cfPDFToPDFPageRect page; - pdftopdf_rotation_e orientation, normal_landscape; // normal_landscape (i.e. default direction) is e.g. needed for number-up=2 - bool paper_is_landscape; - bool duplex; - pdftopdf_border_type_e border; - _cfPDFToPDFNupParameters nup; - bool reverse; - - char *page_label; - bool even_pages, odd_pages; - _cfPDFToPDFIntervalSet *page_ranges; - _cfPDFToPDFIntervalSet *input_page_ranges; - - bool mirror; - - pdftopdf_position_e xpos, ypos; - - bool collate; - - bool even_duplex; // make number of pages a multiple of 2 - - pdftopdf_booklet_mode_e booklet; - int book_signature; - - bool auto_rotate; - - int device_copies; - bool device_collate; - bool set_duplex; - - int page_logging; - int copies_to_be_logged; -} _cfPDFToPDFProcessingParameters; - -void _cfPDFToPDFProcessingParameters_init(_cfPDFToPDFProcessingParameters *processingParams); - -bool _cfPDFToPDFProcessingParameters_with_page(const _cfPDFToPDFProcessingParameters *self, - int outno); - -bool _cfPDFToPDFProcessingParameters_have_page(const _cfPDFToPDFProcessingParameters *self, - int pageno); -void _cfPDFToPDFProcessingParameters_dump(const _cfPDFToPDFProcessingParameters *self, - pdftopdf_doc_t *doc); - - - - - -int* _cfPDFToPDFBookletShuffle(int numPages, int signature, int* ret_size); - -bool _cfProcessPDFToPDF(_cfPDFToPDF_PDFioProcessor *proc, - _cfPDFToPDFProcessingParameters *param, - pdftopdf_doc_t *doc); -#endif // C_PDFTOPDF_PROCESSOR_PRIVATE_H diff --git a/cupsfilters/pdftopdf/C-pdftopdf-processor.c b/cupsfilters/pdftopdf/C-pdftopdf-processor.c deleted file mode 100644 index 766d3992..00000000 --- a/cupsfilters/pdftopdf/C-pdftopdf-processor.c +++ /dev/null @@ -1,583 +0,0 @@ -// -// Copyright 2024 Uddhav Phatak -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "processor.h" -#include -#include "cupsfilters/debug-internal.h" -#include -#include -#include -#define MAX(a, b) ((a) > (b) ? (a) : (b)) - -void -_cfPDFToPDFProcessingParameters_init(_cfPDFToPDFProcessingParameters *processingParams) // {{{ -{ - - processingParams->job_id = 0; - processingParams->num_copies = 1; - processingParams->user = NULL; - processingParams->title = NULL; - processingParams->pagesize_requested = false; - processingParams->fitplot = false; - processingParams->fillprint = false; // print-scaling = fill - processingParams->cropfit = false; - processingParams->autoprint = false; - processingParams->autofit = false; - processingParams->fidelity = false; - processingParams->no_orientation = false; - processingParams->orientation = ROT_0; - processingParams->normal_landscape = ROT_270; - processingParams->paper_is_landscape = false; - processingParams->duplex = false; - processingParams->border = NONE; - processingParams->reverse = false; - - processingParams->page_label = NULL; - processingParams->even_pages = true; - processingParams->odd_pages = true; - - processingParams->mirror = false; - - processingParams->xpos = CENTER; - processingParams->ypos = CENTER; - - processingParams->collate = false; - processingParams->even_duplex = false; - - processingParams->booklet = CF_PDFTOPDF_BOOKLET_OFF; - processingParams->book_signature = -1; - - processingParams->auto_rotate = false; - - processingParams->device_copies = 1; - processingParams->device_collate = false; - processingParams->set_duplex = false; - - processingParams->page_logging = -1; - processingParams->copies_to_be_logged = 0; - - processingParams->page.width = 612.0; // Letter size width in points - processingParams->page.height = 792.0; // Letter size height in points - processingParams->page.top = processingParams->page.height - 36.0; - processingParams->page.bottom = 36.0; - processingParams->page.left = 18.0; - processingParams->page.right = processingParams->page.width - 18.0; - - _cfPDFToPDFIntervalSet_add_single(processingParams->input_page_ranges, 1); - _cfPDFToPDFIntervalSet_finish(processingParams->input_page_ranges); - _cfPDFToPDFIntervalSet_add_single(processingParams->page_ranges, 1); - _cfPDFToPDFIntervalSet_finish(processingParams->page_ranges); -} -// }}} - -void -BookletMode_dump(pdftopdf_booklet_mode_e bkm, - pdftopdf_doc_t *doc) // {{{ -{ - static const char *bstr[3] = {"Off", "On", "Shuffle-Only"}; - - if ((bkm < CF_PDFTOPDF_BOOKLET_OFF) || - (bkm > CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Booklet mode: (Bad booklet mode: %d)", - bkm); - } - else - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Booklet mode: %s", - bstr[bkm]); - } -} -// }}} - -bool -_cfPDFToPDFProcessingParameters_with_page(const _cfPDFToPDFProcessingParameters *processingParams, - int outno) // {{{ -{ - if (outno % 2 == 0) - { - if (!processingParams->even_pages) - return false; - } - else if (!processingParams->odd_pages) - { - return false; - } - return _cfPDFToPDFIntervalSet_contains(processingParams->page_ranges, outno); -} -// }}} - -bool -_cfPDFToPDFProcessingParameters_have_page(const _cfPDFToPDFProcessingParameters *processingParams, - int pageno) // {{{ -{ - return _cfPDFToPDFIntervalSet_contains(processingParams->input_page_ranges, pageno); -} -// }}} - -void -_cfPDFToPDFProcessingParameters_dump(const _cfPDFToPDFProcessingParameters *processingParams, - pdftopdf_doc_t *doc) // {{{ -{ - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: job_id: %d, num_copies: %d", - processingParams->job_id, processingParams->num_copies); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: user: %s, title: %s", - (processingParams->user) ? processingParams->user : "(null)", - (processingParams->title) ? processingParams->title : "(null)"); - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: fitplot: %s", - (processingParams->fitplot) ? "true" : "false"); - - _cfPDFToPDFPageRect_dump(&processingParams->page, doc); - _cfPDFToPDFRotationDump(processingParams->orientation, doc); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: paper_is_landscape: %s", - (processingParams->paper_is_landscape) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: duplex: %s", - (processingParams->duplex) ? "true" : "false"); - - _cfPDFToPDFBorderTypeDump(processingParams->border, doc); - _cfPDFToPDFNupParameters_dump(&processingParams->nup, doc); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: reverse: %s", - (processingParams->reverse) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: even_pages: %s, odd_pages: %s", - (processingParams->even_pages) ? "true" : "false", - (processingParams->odd_pages) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: input page range:"); - - _cfPDFToPDFIntervalSet_dump(processingParams->input_page_ranges, doc); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: page range:"); - - _cfPDFToPDFIntervalSet_dump(processingParams->page_ranges, doc); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: mirror: %s", - (processingParams->mirror) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Position:"); - - _cfPDFToPDFPositionAndAxisDump(processingParams->xpos, X, doc); - _cfPDFToPDFPositionAndAxisDump(processingParams->ypos, Y, doc); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: collate: %s", - (processingParams->collate) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: even_duplex: %s", - (processingParams->even_duplex) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: page_label: %s", - processingParams->page_label ? processingParams->page_label : "(none)"); - - BookletMode_dump(processingParams->booklet, doc); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: booklet signature: %d", processingParams->book_signature); - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: auto_rotate: %s", - (processingParams->auto_rotate) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: device_copies: %d", - processingParams->device_copies); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: device_collate: %s", - (processingParams->device_collate) ? "true" : "false"); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: set_duplex: %s", - (processingParams->set_duplex) ? "true" : "false"); -} -// }}} - -int* -_cfPDFToPDFBookletShuffle(int numPages, int signature, int* ret_size) -{ - if (signature < 0) { - signature = (numPages + 3) & ~0x3; // Round up to the nearest multiple of 4 - } - - int maxSize = numPages + signature - 1; - int* ret = (int*)malloc(maxSize * sizeof(int)); - if (ret == NULL) - { - *ret_size = 0; - return NULL; // Handle memory allocation failure - } - - int curpage = 0; - int index = 0; // Keeps track of the current index in the result array - while (curpage < numPages) - { - int firstpage = curpage; - int lastpage = curpage + signature - 1; - - while (firstpage < lastpage) - { - ret[index++] = lastpage--; - ret[index++] = firstpage++; - ret[index++] = firstpage++; - ret[index++] = lastpage--; - } - curpage += signature; - } - - *ret_size = index; // Set the size of the result - return ret; -} - -bool -_cfProcessPDFToPDF( _cfPDFToPDF_PDFioProcessor *proc, - _cfPDFToPDFProcessingParameters *param, - pdftopdf_doc_t *doc) -{ - if(!_cfPDFToPDF_PDFioProcessor_check_print_permissions(proc, doc)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Not allowed to print"); - return false; - } - - const bool dst_lscape = - (param->paper_is_landscape == - ((param->orientation == ROT_0) || (param->orientation == ROT_180))); - - if (param->paper_is_landscape) - { - int temp = param->nup.nupX; - param->nup.nupX = param->nup.nupY; - param->nup.nupY = temp; - } - - if (param->auto_rotate) - _cfPDFToPDF_PDFioProcessor_auto_rotate_all(proc, dst_lscape, param->normal_landscape); - - int *num_page; - _cfPDFToPDFPageHandle **pages = _cfPDFToPDF_PDFioProcessor_get_pages(proc, doc, num_page); - - _cfPDFToPDFPageHandle **input_page_range_list = malloc((*num_page) * sizeof(_cfPDFToPDFPageHandle*)); - int input_page_range_size = 0; - - for (int i = 1; i <= *num_page; i++) - { - if (_cfPDFToPDFProcessingParameters_with_page(param, i)) - { - input_page_range_list[input_page_range_size++] = pages[i - 1]; - input_page_range_size++; - } - } - - const int numOrigPages = input_page_range_size; - - int* shuffle = NULL; - int* shuffle_size; - - if (param->booklet != CF_PDFTOPDF_BOOKLET_OFF) - { - shuffle = _cfPDFToPDFBookletShuffle(numOrigPages, param->book_signature, shuffle_size); - if (param->booklet == CF_PDFTOPDF_BOOKLET_ON) - { - _cfPDFToPDFNupParameters_preset(2, ¶m->nup); - } - } - else - { - int* shuffle = malloc(numOrigPages * sizeof(numOrigPages)); - for (int i = 0; i < numOrigPages; i++) - { - shuffle[i] = i; - } - } - - const int numPages = MAX(*shuffle_size, input_page_range_size); - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: \"print-scaling\" IPP attribute: %s", - (param->autoprint ? "auto" : - (param->autofit ? "auto-fit" : - (param->fitplot ? "fit" : - (param->fillprint ? "fill" : - (param->cropfit ? "none" : - "Not defined, should never happen")))))); - - if (param->autoprint || param->autofit) - { - bool margin_defined = true; - bool document_large = false; - int pw = param->page.right - param->page.left; - int ph = param->page.top - param->page.bottom; - - if ((param->page.width == pw) && (param->page.height == ph)) - margin_defined = false; - - for (int i = 0; i < input_page_range_size; i ++) - { - _cfPDFToPDFPageRect r = _cfPDFToPDFPageHandle_get_rect(input_page_range_list[i]); - int w = r.width * 100 / 102; // 2% of tolerance - int h = r.height * 100 / 102; - if ((w > param->page.width || h > param->page.height) && - (h > param->page.width || w > param->page.height)) - { - if (doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Page %d too large for output page size, scaling pages to fit.", - i + 1); - document_large = true; - } - } - - if (param->fidelity && doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: \"ipp-attribute-fidelity\" IPP attribute is set, scaling pages to fit."); - - if (param->autoprint) - { - if (param->fidelity || document_large) - { - if (margin_defined) - param->fitplot = true; - else - param->fillprint = true; - } - else - param->cropfit = true; - } - - else - { - if (param->fidelity || document_large) - param->fitplot = true; - else - param->cropfit = true; - } - } - - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Print scaling mode: %s", - (param->fitplot ? - "Scale to fit printable area" : - (param->fillprint ? - "Scale to fill page and crop" : - (param->cropfit ? - "Do not scale, center, crop if needed" : - "Not defined, should never happen")))); - - if (param->cropfit) - { - param->page.left = 0; - param->page.bottom = 0; - param->page.right = param->page.width; - param->page.top = param->page.height; - } - - if (param->pagesize_requested && (param->fillprint || param->cropfit)) - { - for (int i = 0; i < input_page_range_size; i ++) - { - _cfPDFToPDFPageHandle *page = input_page_range_list[i]; - pdftopdf_rotation_e orientation; - if(_cfPDFToPDFPageHandle_is_landscape(page, param->orientation)) - orientation = param->normal_landscape; - else - orientation = ROT_0; - _cfPDFToPDFPageHandle_crop(page, - ¶m->page, - orientation, - param->orientation, - param->xpos, - param->ypos, - !param->cropfit, - !param->auto_rotate, - doc); - } - if (param->fillprint) - param->fitplot = true; - } - - _cfPDFToPDFPageHandle *curpage; - int outputpage = 0; - int outputno = 0; - - if ((param->nup.nupX == 1) && (param->nup.nupY == 1) && !param->fitplot) - { - param->nup.width = param->page.width; - param->nup.height = param->page.height; - } - else - { - param->nup.width = param->page.right - param->page.left; - param->nup.height = param->page.top - param->page.bottom; - } - - if ((param->orientation == ROT_90) || (param->orientation == ROT_270)) - { - int temp = param->nup.nupX; - param->nup.nupX = param->nup.nupY; - param->nup.nupY = temp; - - param->nup.landscape = !param->nup.landscape; - param->orientation = param->orientation - param->normal_landscape; - } - - double xpos = 0, ypos = 0; - if(param->nup.landscape) - { - param->orientation = param->orientation + param->normal_landscape; - if (param->nup.nupX != 1 || param->nup.nupY != 1 || param->fitplot) - { - xpos = param->page.height - param->page.top; - ypos = param->page.left; - } - int temp = param->page.width; - param->page.width = param->page.height; - param->page.height = temp; - - temp = param->nup.width; - param->nup.width = param->nup.height; - param->nup.height = temp; - } - else - { - if (param->nup.nupX != 1 || param->nup.nupY != 1 || param->fitplot) - { - xpos = param->page.left; - ypos = param->page.bottom; - } - } - - _cfPDFToPDFNupState *nupState; - _cfPDFToPDFNupState_init(nupState, ¶m->nup); - - _cfPDFToPDFNupPageEdit pgedit; - - for (int iA = 0; iA < numPages; iA++) - { - _cfPDFToPDFPageHandle *page; - if(shuffle[iA] >= numOrigPages) - page = _cfPDFToPDF_PDFioProcessor_new_page(proc, param->page.width, param->page.height, doc); - else - page = input_page_range_list[shuffle[iA]]; - - _cfPDFToPDFPageRect rect = _cfPDFToPDFPageHandle_get_rect(page); - - if (!param->pagesize_requested) - { - param->page.width = param->page.right = rect.width; - param->page.height = param->page.top = rect.height; - } - - bool newPage = _cfPDFToPDFNupState_next_page(nupState, rect.width, rect.height, &pgedit); - if (newPage) - { - if((curpage) && (_cfPDFToPDFProcessingParameters_with_page(param, outputpage))) - { - _cfPDFToPDFPageHandle_rotate(curpage, param->orientation); - if (param->mirror) - _cfPDFToPDFPageHandle_mirror(curpage, proc->pdf); - -// _cfPDFToPDF_PDFioProcessor_add_page(proc, curpage, param->reverse); - // Log page in /var/log/cups/page_log - - outputno ++; - if (param->page_logging == 1) - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d", outputno, - param->copies_to_be_logged); - } - curpage = _cfPDFToPDF_PDFioProcessor_new_page(proc, param->page.width, param->page.height, doc); - outputpage++; - } - - if (shuffle[iA] >= numOrigPages) - continue; - - if (param->border != NONE) - _cfPDFToPDFPageHandle_add_border_rect(page, proc->pdf, rect, param->border, 1.0 / pgedit.scale); - - if (param->page_label[0] != '\0') - { - _cfPDFToPDFPageHandle_add_label(page, proc->pdf, ¶m->page, param->page_label); - } - - if(param->cropfit) - { - if ((param->nup.nupX == 1) && (param->nup.nupY == 1)) - { - double xpos2, ypos2; - _cfPDFToPDFPageRect get_rect_height = _cfPDFToPDFPageHandle_get_rect(page); - _cfPDFToPDFPageRect get_rect_width = _cfPDFToPDFPageHandle_get_rect(page); - - if ((param->page.height - param->page.width) * - (get_rect_height.height - get_rect_width.width) < 0) - { - xpos2 = (param->page.width - (get_rect_height.height)) / 2; - ypos2 = (param->page.height - (get_rect_width.width)) / 2; - _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, ypos2 + xpos, xpos2 + ypos, 1, NULL); - } - else - { - xpos2 = (param->page.width - get_rect_width.width) / 2; - ypos2 = (param->page.height - get_rect_height.height) /2; - _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, xpos2 + xpos, ypos2 + ypos, 1, NULL); - } - } - else - { - _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, pgedit.xpos + xpos, pgedit.ypos + ypos, pgedit.scale, NULL); - } - } - else - _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, pgedit.xpos + xpos, pgedit.ypos + ypos, pgedit.scale, NULL); - -#ifdef DEBUG - _cfPDFToPDFPageHandle *dbg = (_cfPDFToPDFPageHandle *)curpage; - if (dbg && dbg->debug) - { - _cfPDFToPDFPageHandle_debug(dbg, sub, xpos,ypos); - } -#endif - } - - if((curpage) && (_cfPDFToPDFProcessingParameters_with_page(param, outputpage))) - { - _cfPDFToPDFPageHandle_rotate(curpage, param->orientation); - if(param->mirror) - _cfPDFToPDFPageHandle_mirror(curpage, proc->pdf); - - // need to output empty page to not confuse duplex -// _cfPDFToPDF_PDFioProcessor_add_page(proc, _cfPDFToPDF_PDFioProcessor_new_page(proc, -// param->page.width, param->page.height, doc), param->reverse); - - // Log page in /var/log/cups/page_log - if(param->page_logging == 1) - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d", outputno + 1, - param->copies_to_be_logged); - } - - _cfPDFToPDF_PDFioProcessor_multiply(proc, param->num_copies, param->collate); - return true; -} diff --git a/cupsfilters/pdftopdf/C-pdftopdf.c b/cupsfilters/pdftopdf/C-pdftopdf.c deleted file mode 100644 index 48f64790..00000000 --- a/cupsfilters/pdftopdf/C-pdftopdf.c +++ /dev/null @@ -1,964 +0,0 @@ -// -// Copyright 2024 Uddhav Phatak -// -// Copyright (c) 6-2011, BBR Inc. All rights reserved. -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "C-pdftopdf-private.h" -#include "processor.h" - -#include - -static bool -optGetInt(const char *name, - int num_options, - cups_option_t *options, - int *ret) // {{{ -{ - const char *val = cupsGetOption(name, num_options, options); - if (val) - { - *ret = atoi(val); - return true; - } - return false; -} -// }}} - -static bool -optGetFloat(const char *name, - int num_options, - cups_option_t *options, - float *ret) // {{{ -{ - const char *val = cupsGetOption(name, num_options, options); - if (val) - { - *ret = atof(val); - return true; - } - return false; -} -// }}} - -static bool -is_false(const char *value) // {{{ -{ - if (!value) - return false; - return ((strcasecmp(value, "no") == 0) || - (strcasecmp(value, "off") == 0) || - (strcasecmp(value, "false") == 0)); -} -// }}} - -static bool -is_true(const char *value) // {{{ -{ - if (!value) - return false; - return ((strcasecmp(value, "yes") == 0) || - (strcasecmp(value, "on") == 0) || - (strcasecmp(value, "true") == 0)); -} -// }}} - -static bool -parsePosition(const char *value, - pdftopdf_position_e *xpos, - pdftopdf_position_e *ypos) // {{{ -{ - // ['center','top','left','right','top-left','top-right','bottom', - // 'bottom-left','bottom-right'] - *xpos = CENTER; - *ypos = CENTER; - int next = 0; - - if (strcasecmp(value, "center") == 0) - return true; - else if (strncasecmp(value, "top", 3) == 0) - { - *ypos = TOP; - next = 3; - } - else if (strncasecmp(value, "bottom", 6) == 0) { - *ypos = BOTTOM; - next = 6; - } - - if (next) - { - if (value[next] == 0) - return true; - else if (value[next] != '-') - return false; - value += next + 1; - } - if (strcasecmp(value, "left") == 0) - *xpos = LEFT; - else if (strcasecmp(value, "right") == 0) - *xpos = RIGHT; - else - return false; - - return true; -} -// }}} - -static void -parseRanges(const char *range, _cfPDFToPDFIntervalSet *ret) // {{{ -{ - _cfPDFToPDFIntervalSet_clear(ret); - - if (!range) - { - _cfPDFToPDFIntervalSet_add(ret, 1, 1); - _cfPDFToPDFIntervalSet_finish(ret); - return; - } - - int lower, upper; - while (*range) - { - if (*range == '-') - { - range++; - upper = strtol(range, (char **)&range, 10); - if (upper >= 2147483647) - _cfPDFToPDFIntervalSet_add(ret, 1, 1); - else - _cfPDFToPDFIntervalSet_add(ret, 1, upper+1); - } - else - { - lower = strtol(range, (char **)&range, 10); - if (*range == '-') - { - range++; - if (!isdigit(*range)) - _cfPDFToPDFIntervalSet_add(ret, lower, lower); - else - { - upper = strtol(range, (char **)&range, 10); - if (upper >= 2147483647) - _cfPDFToPDFIntervalSet_add(ret, lower, lower); - else - _cfPDFToPDFIntervalSet_add(ret, lower, upper+1); - } - } - else - { - _cfPDFToPDFIntervalSet_add(ret, lower, lower+1); - } - } - if (*range != ',') - break; - range++; - } - _cfPDFToPDFIntervalSet_finish(ret); -} -// }}} - -static bool -_cfPDFToPDFParseBorder(const char *val, - pdftopdf_border_type_e *ret) // {{{ -{ - if (strcasecmp(val, "none") == 0) - *ret = NONE; - else if (strcasecmp(val, "single") == 0) - *ret = ONE_THIN; - else if (strcasecmp(val, "single-thick") == 0) - *ret = ONE_THICK; - else if (strcasecmp(val, "double") == 0) - *ret = TWO_THIN; - else if (strcasecmp(val, "double-thick") == 0) - *ret = TWO_THICK; - else - return false; - return true; -} -// }}} - -void -getParameters(cf_filter_data_t *data, - int num_options, - cups_option_t *options, - _cfPDFToPDFProcessingParameters *param, - pdftopdf_doc_t *doc) // {{{ -{ - char *final_content_type = data->final_content_type; - ipp_t *printer_attrs = data->printer_attrs; - ipp_t *job_attrs = data->job_attrs; - ipp_attribute_t *attr; - const char *val; - int ipprot; - int nup; - char rawlabel[256]; - char *classification; - char cookedlabel[256]; - - if ((val = cupsGetOption("copies", num_options, options)) != NULL || - (val = cupsGetOption("Copies", num_options, options)) != NULL || - (val = cupsGetOption("num-copies", num_options, options)) != NULL || - (val = cupsGetOption("NumCopies", num_options, options)) != NULL) - { - int copies = atoi(val); - if (copies > 0) - param->num_copies = copies; - } - - if (param->num_copies == 0) - param->num_copies = 1; - - if (printer_attrs != NULL && - (attr = ippFindAttribute(printer_attrs, - "landscape-orientation-requested-preferred", - IPP_TAG_ZERO)) != NULL && - ippGetInteger(attr, 0) == 5) - param->normal_landscape = ROT_270; - else - param->normal_landscape = ROT_90; - - param->orientation = ROT_0; - param->no_orientation = false; - if (optGetInt("orientation-requested", num_options, options, &ipprot)) - { - // IPP orientation values are: - // 3: 0 degrees, 4: 90 degrees, 5: -90 degrees, 6: 180 degrees - - if ((ipprot < 3) || (ipprot > 6)) - { - if (ipprot && doc->logfunc) - doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Bad value (%d) for " - "orientation-requested, using 0 degrees", - ipprot); - param->no_orientation = true; - } - else - { - static const pdftopdf_rotation_e - ipp2rot[4] = {ROT_0, ROT_90, ROT_270, ROT_180}; - param->orientation = ipp2rot[ipprot - 3]; - } - } - else if ((val = cupsGetOption("landscape", num_options, options)) != NULL) - { - if (!is_false(val)) - param->orientation = param->normal_landscape; - } - else - param->no_orientation = true; - - param->pagesize_requested = - (cfGetPageDimensions(printer_attrs, job_attrs, num_options, options, NULL, - 0, - &(param->page.width), &(param->page.height), - &(param->page.left), &(param->page.bottom), - &(param->page.right), &(param->page.top), - NULL, NULL) >= 1); - - cfSetPageDimensionsToDefault(&(param->page.width), &(param->page.height), - &(param->page.left), &(param->page.bottom), - &(param->page.right), &(param->page.top), - doc->logfunc, doc->logdata); - - param->page.right = param->page.width - param->page.right; - param->page.top = param->page.height - param->page.top; - - param->paper_is_landscape = (param->page.width > param->page.height); - - _cfPDFToPDFPageRect tmp; // borders (before rotation) - - optGetFloat("page-top", num_options, options, &tmp.top); - optGetFloat("page-left", num_options, options, &tmp.left); - optGetFloat("page-right", num_options, options, &tmp.right); - optGetFloat("page-bottom", num_options, options, &tmp.bottom); - - if ((val = cupsGetOption("media-top-margin", num_options, options)) - != NULL) - tmp.top = atof(val) * 72.0 / 2540.0; - if ((val = cupsGetOption("media-left-margin", num_options, options)) - != NULL) - tmp.left = atof(val) * 72.0 / 2540.0; - if ((val = cupsGetOption("media-right-margin", num_options, options)) - != NULL) - tmp.right = atof(val) * 72.0 / 2540.0; - if ((val = cupsGetOption("media-bottom-margin", num_options, options)) - != NULL) - tmp.bottom = atof(val) * 72.0 / 2540.0; - - if ((param->orientation == ROT_90) || (param->orientation == ROT_270)) - { // unrotate page - // NaN stays NaN - tmp.right = param->page.height - tmp.right; - tmp.top = param->page.width - tmp.top; - _cfPDFToPDFPageRect_rotate_move(&tmp, param->orientation, param->page.height, param->page.width); - } - else - { - tmp.right = param->page.width - tmp.right; - tmp.top = param->page.height - tmp.top; - _cfPDFToPDFPageRect_rotate_move(&tmp, param->orientation, param->page.width, param->page.height); - } - _cfPDFToPDFPageRect_set(¶m->page, &tmp); - - if ((val = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, "sides")) != - NULL && - strncmp(val, "two-sided-", 10) == 0) - param->duplex = true; - else if (is_true(cupsGetOption("Duplex", num_options, options))) - { - param->duplex = true; - param->set_duplex = true; - } - else if ((val = cupsGetOption("sides", num_options, options)) != NULL) - { - if ((strcasecmp(val, "two-sided-long-edge") == 0) || - (strcasecmp(val, "two-sided-short-edge") == 0)) - { - param->duplex = true; - param->set_duplex = true; - } - else if (strcasecmp(val, "one-sided") != 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported sides value %s, using sides=one-sided!", - val); - } - } - - // default nup is 1 - nup = 1; - if (optGetInt("number-up", num_options, options, &nup)) - { - if (!_cfPDFToPDFNupParameters_possible(nup)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported number-up value %d, using number-up=1!", - nup); - nup = 1; - } - _cfPDFToPDFNupParameters_preset(nup, &(param->nup)); - } - - if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL) - { - if (!_cfPDFToPDFParseNupLayout(val, &(param->nup))) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported number-up-layout %s, using number-up-layout=lrtb!", - val); - param->nup.first = X; - param->nup.xstart = LEFT; - param->nup.ystart = TOP; - } - } - - if ((val = cupsGetOption("page-border", num_options, options)) != NULL) - { - if (!_cfPDFToPDFParseBorder(val, &(param->border))) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported page-border value %s, using page-border=none!", - val); - param->border = NONE; - } - } - - if ((val = cupsGetOption("OutputOrder", num_options, options)) != NULL || - (val = cupsGetOption("output-order", num_options, options)) != NULL || - (val = cupsGetOption("page-delivery", num_options, options)) != NULL) - { - param->reverse = (strcasecmp(val, "Reverse") == 0 || - strcasecmp(val, "reverse-order") == 0); - } - else - { - param->reverse = cfIPPReverseOutput(printer_attrs, job_attrs); - } - - classification = getenv("CLASSIFICATION"); - if (classification) - strcpy(rawlabel, classification); - - if ((val = cupsGetOption("page-label", num_options, options)) != NULL) - { - if (strlen(rawlabel) > 0) strcat(rawlabel, " - "); - strcat(rawlabel, cupsGetOption("page-label", num_options, options)); - } - - char *rawptr = rawlabel; - char *cookedptr = cookedlabel; - while (*rawptr) - { - if (*rawptr < 32 || *rawptr > 126) - { - sprintf(cookedptr, "\\%03o", (unsigned int)*rawptr); - cookedptr += 4; - } - else - { - *cookedptr++ = *rawptr; - } - rawptr++; - } - *cookedptr = '\0'; - param->page_label = strdup(cookedlabel); - - if ((val = cupsGetOption("page-set", num_options, options)) != NULL) - { - if (strcasecmp(val, "even") == 0) - param->odd_pages = false; - else if (strcasecmp(val, "odd") == 0) - param->even_pages = false; - else if (strcasecmp(val, "all") != 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported page-set value %s, using page-set=all!", - val); - } - } - - if ((val = cupsGetOption("page-ranges", num_options, options)) != NULL) - parseRanges(val, param->page_ranges); - if ((val = cupsGetOption("input-page-ranges", num_options, options)) != NULL) - parseRanges(val, param->input_page_ranges); - - if ((val = cupsGetOption("mirror", num_options, options)) != NULL || - (val = cupsGetOption("mirror-print", num_options, options)) != NULL) - param->mirror = is_true(val); - - param->booklet = CF_PDFTOPDF_BOOKLET_OFF; - if ((val = cupsGetOption("booklet", num_options, options)) != NULL) - { - if (strcasecmp(val, "shuffle-only") == 0) - param->booklet = CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE; - else if (is_true(val)) - param->booklet = CF_PDFTOPDF_BOOKLET_ON; - else if (!is_false(val)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported booklet value %s, using booklet=off!", - val); - } - } - param->book_signature = -1; - if (optGetInt("booklet-signature", num_options, options, &(param->book_signature))) - { - if (param->book_signature == 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported booklet-signature value, using booklet-signature=-1 (all)!", - val); - param->book_signature = -1; - } - } - - if ((val = cupsGetOption("position", num_options, options)) != NULL) - { - if (!parsePosition(val, &(param->xpos), &(param->ypos))) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unrecognized position value %s, using position=center!", - val); - param->xpos = CENTER; - param->ypos = CENTER; - } - } - - if (is_true(cupsGetOption("Collate", num_options, options))) - param->collate = true; - else if ((val = cupsGetOption("sheet-collate", num_options, options)) != NULL) - param->collate = (strcasecmp(val, "uncollated") != 0); - else if (((val = cupsGetOption("multiple-document-handling", - num_options, options)) != NULL && - (strcasecmp(val, "separate-documents-collated-copies") == 0 || - strcasecmp(val, "separate-documents-uncollated-copies") == 0 || - strcasecmp(val, "single-document") == 0 || - strcasecmp(val, "single-document-new-sheet") == 0)) || - (val = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, - "multiple-document-handling")) != - NULL) - { - param->collate = - (strcasecmp(val, "separate-documents-uncollated-copies") != 0); - } - -#if 0 - if ((val = cupsGetOption("scaling", num_options, options)) != 0) - { - scalint = atoi(val) * 0.01; - fitplot = true - } - else if (fitplot) - scaling = 1.0; - - if ((val = cupsGetOption("natural-scaling", num_options, options)) != 0) - naturalScaling = atoi(val) * 0.01; -#endif - - // Make pages a multiple of two (only considered when duplex is on). - // i.e. printer has hardware-duplex, but needs pre-inserted filler pages - // FIXME? pdftopdf also supports it as cmdline option (via checkFeature()) - param->even_duplex = - (param->duplex && - is_true(cupsGetOption("even-duplex", num_options, options))); - - param->auto_rotate = param->no_orientation; - if ((val = cupsGetOption("pdftopdfAutoRotate", - num_options, options)) != NULL || - (val = cupsGetOption("pdfAutoRotate", num_options, options)) != NULL) - param->auto_rotate = !is_false(val); - - if ((val = cupsGetOption("ipp-attribute-fidelity", num_options, options)) != - NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes") || - !strcasecmp(val, "on")) - param->fidelity = true; - } - - if (printer_attrs == NULL && !param->pagesize_requested && - param->booklet == CF_PDFTOPDF_BOOKLET_OFF && - param->nup.nupX == 1 && param->nup.nupY == 1) - param->cropfit = true; - - else if ((val = cupsGetOption("print-scaling", num_options, options)) != NULL) - { - // Standard IPP attribute - if (!strcasecmp(val, "auto")) - param->autoprint = true; - else if (!strcasecmp(val, "auto-fit")) - param->autofit = true; - else if (!strcasecmp(val, "fill")) - param->fillprint = true; - else if (!strcasecmp(val, "fit")) - param->fitplot = true; - else if (!strcasecmp(val, "none")) - param->cropfit = true; - else - param->autoprint = true; - } - else - { - // Legacy CUPS attributes - if ((val = cupsGetOption("fitplot", num_options, options)) == NULL) - { - if ((val = cupsGetOption("fit-to-page", num_options, options)) == NULL) - val = cupsGetOption("ipp-attribute-fidelity", num_options, options); - } - // TODO? pstops checks == "true", pdftops !is_false ... pstops says: - // fitplot only for PS (i.e. not for PDF, cmp. cgpdftopdf) - param->fitplot = (val && !is_false(val)); - - if ((val = cupsGetOption("fill", num_options, options)) != 0) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - param->fillprint = true; - } - if ((val = cupsGetOption("crop-to-fit", num_options, options)) != NULL) - { - if (!strcasecmp(val, "true") || !strcasecmp(val, "yes")) - param->cropfit = 1; - } - if (!param->autoprint && !param->autofit && !param->fitplot && - !param->fillprint && !param->cropfit) - param->autoprint = true; - } - - // Certain features require a given page size for the page to be - // printed or all pages of the document being the same size. Here we - // set param.pagesize_requested so that the default page size is used - // when no size got specified by the user. - if (param->fitplot || param->fillprint || param->autoprint || param->autofit || - param->booklet != CF_PDFTOPDF_BOOKLET_OFF || - param->nup.nupX > 1 || param->nup.nupY > 1) - param->pagesize_requested = true; - - // - // Do we have to do the page logging in page_log? - // - // CUPS standard is that the last filter (not the backend, usually the - // printer driver) does page logging in the /var/log/cups/page_log file - // by outputting "PAGE: <# of current page> <# of copies>" to stderr. - // - // cfFilterPDFToPDF() would have to do this only for PDF printers as - // in this case cfFilterPDFToPDF() is the last filter, but some of - // the other filters are not able to do the logging because they do - // not have access to the number of pages of the file to be printed, - // so cfFilterPDFToPDF() overtakes their logging duty. - // - - // Check whether page logging is forced or suppressed by the options - - if ((val = cupsGetOption("pdf-filter-page-logging", - num_options, options)) != NULL) - { - if (strcasecmp(val, "auto") == 0) - { - param->page_logging = -1; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Automatic page logging selected by options."); - } - else if (is_true(val)) - { - param->page_logging = 1; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Forced page logging selected by options."); - } - else if (is_false(val)) - { - param->page_logging = 0; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Suppressed page logging selected by options."); - } - else - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Unsupported page logging setting \"pdf-filter-page-logging=%s\", using \"auto\"!", - val); - param->page_logging = -1; - } - } - - if (param->page_logging == -1) - { - // We determine whether to log pages or not - // using the output data MIME type. log pages only when the output is - // either pdf or PWG Raster - if (final_content_type && - (strcasestr(final_content_type, "/pdf") || - strcasestr(final_content_type, "/vnd.cups-pdf") || - strcasestr(final_content_type, "/pwg-raster"))) - param->page_logging = 1; - else - param->page_logging = 0; - - // If final_content_type is not clearly available we are not sure whether - // to log pages or not - if (!final_content_type || - final_content_type[0] == '\0') - param->page_logging = -1; - - if (doc->logfunc) - { - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Determined whether to log pages or not using output data type."); - doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "final_content_type = %s => page_logging = %d", - final_content_type ? final_content_type : "NULL", - param->page_logging); - } - - if (param->page_logging == -1) - param->page_logging = 0; - } -} - -void -calculate(int num_options, - cups_option_t *options, - _cfPDFToPDFProcessingParameters *param, - char *final_content_type) -{ - const char *val; - bool hw_copies = false, - hw_collate = false; - - // Check options for caller's instructions about hardware copies/collate - if ((val = cupsGetOption("hardware-copies", - num_options, options)) != NULL) - // Use hardware copies according to the caller's instructions - hw_copies = is_true(val); - else - // Caller did not tell us whether the printer does Hardware copies - // or not, so we assume hardware copies on PDF printers, and software - // copies on other (usually raster) printers or if we do not know the - // final output format. - hw_copies = (final_content_type && - (strcasestr(final_content_type, "/pdf") || - strcasestr(final_content_type, "/vnd.cups-pdf"))); - - if (hw_copies) - { - if ((val = cupsGetOption("hardware-collate", - num_options, options)) != NULL) - // Use hardware collate according to the caller's instructions - hw_collate = is_true(val); - else - hw_collate = (final_content_type && - (strcasestr(final_content_type, "/pdf") || - strcasestr(final_content_type, "/vnd.cups-pdf") || - strcasestr(final_content_type, "/pwg-raster") || - strcasestr(final_content_type, "/urf") || - strcasestr(final_content_type, "/PCLm"))); - } - - if (param->reverse && param->duplex) - // Enable even_duplex or the first page may be empty. - param->even_duplex = true; // disabled later, if non-duplex - - if (param->num_copies == 1) - { - param->device_copies = 1; - // collate is never needed for a single copy - param->collate = false; // (does not make a big difference for us) - } - else if (hw_copies) - { // hw copy generation available - param->device_copies = param->num_copies; - if (param->collate) - { - param->device_collate = hw_collate; - if (!param->device_collate) - // printer can't hw collate -> we must copy collated in sw - param->device_copies = 1; - } // else: printer copies w/o collate and takes care of duplex/even_duplex - } - else - { // sw copies - param->device_copies = 1; - if (param->duplex) - { // &&(num_copies>1) - // sw collate + even_duplex must be forced to prevent copies on the - // back sides - param->collate = true; - param->device_collate = false; - } - } - - if (param->device_copies != 1) - param->num_copies = 1; - - if (param->duplex && - param->collate && !param->device_collate) - param->even_duplex = true; - - if (!param->duplex) - param->even_duplex = false; -} - -// reads from stdin into temporary file. returns FILE * or NULL on error -FILE * -copy_fd_to_temp(int infd, - pdftopdf_doc_t *doc) -{ - char buf[BUFSIZ]; - int n; - - int outfd = cupsCreateTempFd(NULL, NULL, buf, sizeof(buf)); - if (outfd < 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Can't create temporary file"); - return NULL; - } - - // remove name - unlink(buf); - - // copy stdin to the tmp file - while ((n = read(infd, buf, BUFSIZ)) > 0) - { - if (write(outfd, buf, n) != n) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Can't copy stdin to temporary file"); - close(outfd); - return NULL; - } - } - - if (lseek(outfd, 0, SEEK_SET) < 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Can't rewind temporary file"); - close(outfd); - return NULL; - } - - FILE *f; - if ((f = fdopen(outfd, "rb")) == 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Can't fdopen temporary file"); - close(outfd); - return NULL; - } - return f; -} - -// check whether a given file is empty -bool -is_empty(FILE *f) -{ - char buf[1]; - if (fread(buf, 1, 1, f) == 0) - return true; - rewind(f); - return false; -} - -int -cfFilterPDFToPDF(int inputfd, - int outputfd, - int inputseekable, - cf_filter_data_t *data, - void *parameters) -{ - pdftopdf_doc_t doc; - char *final_content_type = data->final_content_type; - FILE *inputfp, - *outputfp; - const char *t; - int streaming = 0; - size_t bytes; - char buf[BUFSIZ]; - cf_logfunc_t log = data->logfunc; - void *ld = data->logdata; - cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; - void *icd = data->iscanceleddata; - int num_options = 0; - cups_option_t *options = NULL; - - _cfPDFToPDFProcessingParameters param; - - param.job_id = data->job_id; - param.user = data->job_user; - param.title = data->job_title; - param.num_copies = data->copies; - param.copies_to_be_logged = data->copies; - param.page.width = param.page.height = 0; - param.page.left = param.page.bottom = -1; - param.page.right = param.page.top = -1; - - doc.logfunc = log; - doc.logdata = ld; - doc.iscanceledfunc = iscanceled; - doc.iscanceleddata = icd; - - num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - - getParameters(data, num_options, options, ¶m, &doc); - calculate(num_options, options, ¶m, final_content_type); - -#ifdef DEBUG - _cfPDFToPDFProcessingParameters_dump(¶m, &doc); -#endif - - // If we are in streaming mode we only apply JCL and do not run the - // job through QPDL (so no page management, form flattening, - // page size/orientation adjustment, ...) - if ((t = cupsGetOption("filter-streaming-mode", - num_options, options)) != NULL && - (strcasecmp(t, "false") && strcasecmp(t, "off") && - strcasecmp(t, "no"))) - { - streaming = 1; - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Streaming mode: No PDF processing, only adding of JCL"); - } - - cupsFreeOptions(num_options, options); - - _cfPDFToPDF_PDFioProcessor proc; - - if ((inputseekable && inputfd > 0) || streaming) - { - if ((inputfp = fdopen(inputfd, "rb")) == NULL) - return 1; - } - else - { - if ((inputfp = copy_fd_to_temp(inputfd, &doc)) == NULL) - return 1; - } - - if (!streaming) - { - if (is_empty(inputfp)) - { - fclose(inputfp); - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Input is empty, outputting empty file."); - return 0; - } - - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Processing PDF input with PDFio: Page-ranges, page-set, number-up, booklet, size adjustment, ..."); - - // Load the PDF input data into PDFio - if (!_cfPDFToPDF_PDFioProcessor_load_file(&proc, inputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE, 1)) - { - fclose(inputfp); - return 1; - } - - // Process the PDF input data - if (!_cfProcessPDFToPDF(&proc, ¶m, &doc)) - return 2; - - // Pass information to subsequent filters via PDF comments - char *output[10]; - int output_len = 0; - - output[output_len++] = "% This file was generated by pdftopdf"; - - if (param.device_copies > 0) - { - char buf[256]; - snprintf(buf, sizeof(buf), "%d", param.device_copies); - output[output_len++] = strdup(buf); - - if (param.device_collate) - output[output_len++] = "%%PDFTOPDFCollate : true"; - else - output[output_len++] = "%%PDFTOPDFCollate : false"; - } - - _cfPDFToPDF_PDFioProcessor_set_comments(&proc, output, output_len); - } - - outputfp = fdopen(outputfd, "w"); - if (outputfp == NULL) - return 1; - - if (!streaming) - { - _cfPDFToPDF_PDFioProcessor_emit_file(&proc, outputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE); - } - else - { - if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Passing on unchanged PDF data from input"); - while ((bytes = fread(buf, 1, sizeof(buf), inputfp)) > 0) - if (fwrite(buf, 1, bytes, outputfp) != bytes) - break; - fclose(inputfp); - } - - fclose(outputfp); - return 0; -} -// }}} diff --git a/cupsfilters/pdftopdf/C-pptypes-private.h b/cupsfilters/pdftopdf/C-pptypes-private.h deleted file mode 100644 index 457e931a..00000000 --- a/cupsfilters/pdftopdf/C-pptypes-private.h +++ /dev/null @@ -1,80 +0,0 @@ -// -// Copyright 2024 Uddhav Phatak -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef _CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ -#define _CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ - -#include "C-pdftopdf-private.h" -#include - -typedef enum -{ - X, - Y -} pdftopdf_axis_e; - -typedef enum -{ - CENTER = 0, - LEFT = -1, - RIGHT = 1, - TOP = 1, - BOTTOM = -1 -} pdftopdf_position_e; - -void _cfPDFToPDFPositionDump(pdftopdf_position_e pos, pdftopdf_doc_t *doc); -void _cfPDFToPDFPositionAndAxisDump(pdftopdf_position_e pos, pdftopdf_axis_e axis, - pdftopdf_doc_t *doc); - -typedef enum -{ - ROT_0, - ROT_90, - ROT_180, - ROT_270, -} pdftopdf_rotation_e; - -void _cfPDFToPDFRotationDump(pdftopdf_rotation_e rot, pdftopdf_doc_t *doc); - -pdftopdf_rotation_e pdftopdf_rotation_add(pdftopdf_rotation_e lhs, pdftopdf_rotation_e rhs); -pdftopdf_rotation_e pdftopdf_rotation_sub(pdftopdf_rotation_e lhs, pdftopdf_rotation_e rhs); -pdftopdf_rotation_e pdftopdf_rotation_neg(pdftopdf_rotation_e rhs); - -typedef enum -{ - NONE = 0, - ONE_THIN = 2, - ONE_THICK = 3, - TWO_THIN = 4, - TWO_THICK = 5, - ONE = 0x02, - TWO = 0x04, - THICK = 0x01 -} pdftopdf_border_type_e; - -void _cfPDFToPDFBorderTypeDump(pdftopdf_border_type_e border, - pdftopdf_doc_t *doc); - -typedef struct -{ - float top, left, right, bottom; // i.e. margins - float width, height; -} _cfPDFToPDFPageRect; - -void _cfPDFToPDFPageRect_init(_cfPDFToPDFPageRect *rect); - -void _cfPDFToPDFPageRect_rotate_move(_cfPDFToPDFPageRect *rect, pdftopdf_rotation_e r, float pwidth, float pheight); - -void _cfPDFToPDFPageRect_scale(_cfPDFToPDFPageRect *rect, float mult); - -void _cfPDFToPDFPageRect_translate(_cfPDFToPDFPageRect *rect, float tx, float ty); - -void _cfPDFToPDFPageRect_set(_cfPDFToPDFPageRect *rect, const _cfPDFToPDFPageRect *rhs); - -void _cfPDFToPDFPageRect_dump(const _cfPDFToPDFPageRect *rect, pdftopdf_doc_t *doc); - -#endif // !_CUPS_FILTERS_PDFTOPDF_PPTYPES_H_ diff --git a/cupsfilters/pdftopdf/C-pptypes.c b/cupsfilters/pdftopdf/C-pptypes.c deleted file mode 100644 index 44da1d18..00000000 --- a/cupsfilters/pdftopdf/C-pptypes.c +++ /dev/null @@ -1,279 +0,0 @@ -// -//Copyright 2024 Uddhav Phatak -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "C-pptypes-private.h" -#include -#include -#include "cupsfilters/debug-internal.h" - -void -_cfPDFToPDFPositionDump(pdftopdf_position_e pos, - pdftopdf_doc_t *doc) // {{{ -{ - static const char *pstr[3] = {"Left/Bottom", "Center", "Right/Top"}; - if ((pos < LEFT) || (pos > RIGHT)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: (bad position: %d)", pos); - } - else - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: %s", pstr[pos+1]); - } -} -// }}} - -void -_cfPDFToPDFPositionAndAxisDump(pdftopdf_position_e pos, - pdftopdf_axis_e axis, - pdftopdf_doc_t *doc) // {{{ -{ - if ((pos < LEFT) || (pos > RIGHT)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Position %s: (bad position: %d)", - (axis == X) ? "X" : "Y", pos); - return; - } - - if (axis == X) - { - static const char *pxstr[3] = {"Left", "Center", "Right"}; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Position X: %s", pxstr[pos + 1]); - } - - else - { - static const char *pystr[3] = {"Bottom", "Center", "Top"}; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Position Y: %s", pystr[pos + 1]); - - } -} -// }}} - -void -_cfPDFToPDFRotationDump(pdftopdf_rotation_e rot, - pdftopdf_doc_t *doc) // {{{ -{ - static const char *rstr[4] = {"0 deg", "90 deg", "180 deg", "270 deg"}; // CCW - - if ((rot < ROT_0) || (rot > ROT_270)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Rotation(CCW): (bad rotation: %d)", rot); - } - - else - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Rotation(CCW): %s", rstr[rot]); - } -} -// }}} - -pdftopdf_rotation_e -pdftopdf_rotation_add(pdftopdf_rotation_e lhs, - pdftopdf_rotation_e rhs) // {{{ -{ - return (pdftopdf_rotation_e)(((int)lhs + (int)rhs) % 4); -} -// }}} - -pdftopdf_rotation_e -pdftopdf_rotation_sub(pdftopdf_rotation_e lhs, - pdftopdf_rotation_e rhs) // {{{ -{ - return (pdftopdf_rotation_e)((((int)lhs - (int)rhs) % 4 + 4) % 4); -} -// }}} - -pdftopdf_rotation_e -pdftopdf_rotation_neg(pdftopdf_rotation_e rhs) // {{{ -{ - return (pdftopdf_rotation_e)((4 - (int)rhs) % 4); -} -// }}} - -void -_cfPDFToPDFBorderTypeDump(pdftopdf_border_type_e border, - pdftopdf_doc_t *doc) // {{{ -{ - if ((border < NONE) || (border == 1) || (border > TWO_THICK)) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Border: (bad border: %d)", border); - - } - else - { - static const char *bstr[6] = - {"None", NULL, "one thin", "one thick", "two thin", "two thick"}; - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Border: %s", bstr[border]); - } -} -// }}} - -void -_cfPDFToPDFPageRect_init(_cfPDFToPDFPageRect *rect) // {{{ -{ - rect->top = NAN; - rect->left = NAN; - rect->right = NAN; - rect->bottom = NAN; - rect->width = NAN; - rect->height = NAN; -} -// {{{ - -void -swap_float(float *a, float *b) // {{{ -{ - float temp = *a; - *a = *b; - *b = temp; -} -// }}} - -void -_cfPDFToPDFPageRect_rotate_move(_cfPDFToPDFPageRect *rect, - pdftopdf_rotation_e r, - float pwidth, float pheight) // {{{ -{ - #if 1 - if (r >= ROT_180) - { - swap_float(&rect->top, &rect->bottom); - swap_float(&rect->left, &rect->right); - } - - if ((r == ROT_90) || (r == ROT_270)) - { - const float tmp = rect->bottom; - rect->bottom = rect->left; - rect->left = rect->top; - rect->top = rect->right; - rect->right = tmp; - - swap_float(&rect->width, &rect->height); - swap_float(&pwidth, &pheight); - } - - if ((r == ROT_90) || (r == ROT_180)) - { - rect->left = pwidth - rect->left; - rect->right = pwidth - rect->right; - } - - if ((r == ROT_270) || (r == ROT_180)) - { - rect->top = pheight - rect->top; - rect->bottom = pheight - rect->bottom; - } - - #else - switch(r) - { - case ROT_0: // no-op - break; - case ROT_90: - const float tmp0 = bottom; - bottom = left; - left = pheight - top; - top = right; - right = pheight - tmp0; - - swap_float(&width, &height); - break; - - case ROT_180: - const float tmp1 = left; - left = pwidth - right; - right = pwidth - tmp1; - - const float tmp2 = top; - top = pheight - bottom; - bottom = pheight - tmp2; - break; - - case ROT_270: - const float tmp3 = top; - top = pwidth - left; - left = bottom; - bottom = pwidth - right; - right = tmp3; - - swap_float(&width, &height); - break; - - } - #endif -} -// }}} - -void -_cfPDFToPDFPageRect_scale(_cfPDFToPDFPageRect *rect, - float mult) // {{{ -{ - if (mult == 1.0) - return; - - rect->bottom *= mult; - rect->left *= mult; - rect->top *= mult; - rect->right *= mult; - - rect->width *= mult; - rect->height *= mult; -} -// }}} - -void -_cfPDFToPDFPageRect_translate(_cfPDFToPDFPageRect *rect, - float tx, - float ty) // {{{ -{ - rect->left += tx; - rect->bottom += ty; - rect->right += tx; - rect->top += ty; -} -// }}} - -void -_cfPDFToPDFPageRect_set(_cfPDFToPDFPageRect *rect, - const _cfPDFToPDFPageRect *rhs) // {{{ -{ - if (!isnan(rhs->top)) - rect->top = rhs->top; - - if (!isnan(rhs->left)) - rect->left = rhs->left; - - if (!isnan(rhs->right)) - rect->right = rhs->right; - - if (!isnan(rhs->bottom)) - rect->bottom = rhs->bottom; -} -// }}} - -void -_cfPDFToPDFPageRect_dump(const _cfPDFToPDFPageRect *rect, - pdftopdf_doc_t *doc) // {{{ -{ - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: top: %f, left: %f, right: %f, bottom: %f, " - "width: %f, height: %f", - rect->top, rect->left, rect->right, rect->bottom, - rect->width, rect->height); - -} -// }}} diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h b/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h index cc7f9a0b..02bb627d 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf-private.h @@ -5,7 +5,7 @@ // information. // -#include "C-pptypes-private.h" +#include "pptypes-private.h" #include #include #include diff --git a/cupsfilters/pdftopdf/pdftopdf-processor-private.h b/cupsfilters/pdftopdf/pdftopdf-processor-private.h index 4067f558..fc3a5943 100644 --- a/cupsfilters/pdftopdf/pdftopdf-processor-private.h +++ b/cupsfilters/pdftopdf/pdftopdf-processor-private.h @@ -8,11 +8,11 @@ #ifndef C_PDFTOPDF_PROCESSOR_PRIVATE_H #define C_PDFTOPDF_PROCESSOR_PRIVATE_H -#include "pptypes-private.h" -#include "nup-private.h" -#include "pdftopdf-private.h" +#include "C-pptypes-private.h" +#include "C-nup-private.h" +#include "C-pdftopdf-private.h" #include "pdfio-pdftopdf-processor-private.h" -#include "intervalset-private.h" +#include "C-intervalset-private.h" #include #include diff --git a/cupsfilters/pdftopdf/processor.h b/cupsfilters/pdftopdf/processor.h index 80086c88..4af28d88 100644 --- a/cupsfilters/pdftopdf/processor.h +++ b/cupsfilters/pdftopdf/processor.h @@ -8,12 +8,12 @@ #ifndef C_PDFTOPDF_PROCESSOR_PRIVATE_H #define C_PDFTOPDF_PROCESSOR_PRIVATE_H -#include "C-pptypes-private.h" +#include "pptypes-private.h" #include "pdfio.h" -#include "C-pptypes-private.h" -#include "C-nup-private.h" -#include "C-pdftopdf-private.h" -#include "C-intervalset-private.h" +#include "pptypes-private.h" +#include "nup-private.h" +#include "pdftopdf-private.h" +#include "intervalset-private.h" #include #include #define HASH_TABLE_SIZE 2048 From 1e638afc3aef0a9b7016e1596535febd0153df6c Mon Sep 17 00:00:00 2001 From: ThePhatak <111195860+uddhavphatak@users.noreply.github.com> Date: Sun, 1 Dec 2024 01:03:33 +0530 Subject: [PATCH 58/64] Update build.yaml removing qpdf installing from testing as it has been eliminated --- .github/workflows/build.yaml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index db9bdc47..1230fa60 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -37,24 +37,6 @@ jobs: sudo make install cd .. cd .. - - name: Install libqpdf > 11.0.0 - run: | - cd .. - mkdir qpdf - wget -O qpdf-11.6.3.tar.gz https://sourceforge.net/projects/qpdf/files/qpdf/11.6.3/qpdf-11.6.3.tar.gz - tar -xzf qpdf-11.6.3.tar.gz - cd qpdf-11.6.3 - mkdir build && - cd build && - cmake -DCMAKE_INSTALL_PREFIX=/usr \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_STATIC_LIBS=OFF \ - -DCMAKE_INSTALL_DOCDIR=/usr/share/doc/qpdf-11.6.3 \ - .. && - make - sudo make install - cd .. - cd .. - name: Install poppler and mupdf run: | sudo apt install libpoppler-cpp-dev libpython3-dev libdbus-1-dev From 7b3ec2a60be824184ff3c4e1ce2b9e3c818f11d2 Mon Sep 17 00:00:00 2001 From: uddhavphatak Date: Sun, 1 Dec 2024 01:05:53 +0530 Subject: [PATCH 59/64] removing the check for qpdf due to removal of its dependency --- configure.ac | 1 - 1 file changed, 1 deletion(-) diff --git a/configure.ac b/configure.ac index 61503bd3..198d53c7 100644 --- a/configure.ac +++ b/configure.ac @@ -292,7 +292,6 @@ AS_IF([test x"$lcms2" = "xno"], [ ]) PKG_CHECK_MODULES([FONTCONFIG], [fontconfig >= 2.0.0]) PKG_CHECK_MODULES([LIBPDFIO], [pdfio >= 1.3.0]) -PKG_CHECK_MODULES([LIBQPDF], [libqpdf >= 11.0.0]) # ================= # Check for Poppler From eee3511af14567b593687326f54bd34cb8938980 Mon Sep 17 00:00:00 2001 From: Till Kamppeter Date: Fri, 6 Dec 2024 23:45:20 +0100 Subject: [PATCH 60/64] Corrected missing PDFio/remaining QPDF compiler arguments - In Makefile.am for building libcupsfilters - In libcupsfilters.pc.in, the "Libs.private" entry Also replaced a hardcoded "-lcups" by $(CUPS_LIBS) in Makefile.am --- Makefile.am | 8 +++----- libcupsfilters.pc.in | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Makefile.am b/Makefile.am index 2b947d2e..e1c22ea6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -217,7 +217,6 @@ libcupsfilters_la_LIBADD = \ $(CUPS_LIBS) \ $(LCMS_LIBS) \ $(LIBPDFIO_LIBS) \ - $(LIBQPDF_LIBS) \ $(LIBJPEG_LIBS) \ $(EXIF_LIBS) \ $(LIBPNG_LIBS) \ @@ -232,7 +231,7 @@ libcupsfilters_la_CFLAGS = \ $(FONTCONFIG_CFLAGS) \ $(CUPS_CFLAGS) \ $(LCMS_CFLAGS) \ - $(LIBQPDF_CFLAGS) \ + $(LIBPDFIO_CFLAGS) \ $(LIBJPEG_CFLAGS) \ $(EXIF_CFLAGS) \ $(LIBPNG_CFLAGS) \ @@ -320,11 +319,10 @@ test_ps_LDADD = libcupsfilters.la testfilters_SOURCES = \ cupsfilters/testfilters.c \ $(pkgfiltersinclude_DATA) - testfilters_LDADD = \ libcupsfilters.la \ - -lm -ldl -lcups - + $(CUPS_LIBS) \ + -lm -ldl testfilters_LDFLAGS = \ -D_GNU_SOURCE \ -L/usr/lib diff --git a/libcupsfilters.pc.in b/libcupsfilters.pc.in index 2eeef63b..5cacd69c 100644 --- a/libcupsfilters.pc.in +++ b/libcupsfilters.pc.in @@ -8,5 +8,5 @@ Description: Library providing everything for filtering/converting print/scan jo Version: @VERSION@ Libs: -L${libdir} -lcupsfilters -Libs.private: @CUPS_LIBS@ @LIBJPEG_LIBS@ @LIBPNG_LIBS@ @LIBTIFF_LIBS@ @LIBQPDF_LIBS@ +Libs.private: @CUPS_LIBS@ @LIBJPEG_LIBS@ @LIBPNG_LIBS@ @LIBTIFF_LIBS@ @LIBPDFIO_LIBS@ Cflags: -I${includedir}/cupsfilters -I${includedir} From 8396bc781ebf2aabb3ad8c00ec93ed7679efdc8d Mon Sep 17 00:00:00 2001 From: Uddhav Phatak Date: Wed, 1 Jan 2025 23:19:43 +0530 Subject: [PATCH 61/64] solved errors which were coming in cupsfilters/testfilters.sh.log --- .../pdfio-pdftopdf-processor-private.h | 155 ------------------ .../pdftopdf/pdfio-pdftopdf-processor.c | 85 +++------- .../pdftopdf/pdftopdf-processor-private.h | 113 ------------- cupsfilters/pdftopdf/pdftopdf.c | 16 +- cupsfilters/pdftopdf/processor.h | 5 +- 5 files changed, 39 insertions(+), 335 deletions(-) delete mode 100644 cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h delete mode 100644 cupsfilters/pdftopdf/pdftopdf-processor-private.h diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h deleted file mode 100644 index 6323568e..00000000 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor-private.h +++ /dev/null @@ -1,155 +0,0 @@ -// -// Copyright 2024 Uddhav Phatak -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#include "processor.h" -#include "C-pptypes-private.h" -#include "pdfio.h" -#define HASH_TABLE_SIZE 2048 - -typedef struct KeyValuePair { - char *key; // Key (string) - pdfio_obj_t *value; // Value (PDF object handle) - struct KeyValuePair *next; // For handling collisions (chaining) -} KeyValuePair; - -typedef struct HashTable { - KeyValuePair *buckets[HASH_TABLE_SIZE]; // Array of pointers to key-value pairs - int count; // Number of filled elements in the hash table -} HashTable; - -HashTable *hashCreate_hash_table(); -void hashInsert(HashTable *table, const char *key, pdfio_obj_t *value); -pdfio_obj_t *hashGet(HashTable *table, const char *key); -int hashGet_filled_count(HashTable *table); -void hashFree_hash_table(HashTable *table); - -/* -typedef struct _cfPDFToPDF_PDFioProcessor{ - - // 1st mode: existing - pdfio_obj_t *page; // Equivalent to PDFioObjectHandle - int no; - - // 2nd mode: create new - HashTable *xobjs; // Pointer to a single HashTable - - char *content; // Equivalent to std::string content - - pdftopdf_rotation_e rotation; - - // Other members - pdfio_file_t *pdf; // Equivalent to std::unique_ptr - pdfio_obj_t **orig_pages; // Equivalent to std::vector - size_t orig_pages_size; // Current number of pages - size_t orig_pages_capacity; // Capacity for page array - - bool hasCM; - char *extraheader; -} _cfPDFToPDF_PDFioProcessor; -*/ -//_cfPDFToPDFPDFioPageHandle functions - -void _cfPDFToPDF_PDFioProcessor_existingMode(_cfPDFToPDF_PDFioProcessor *handle, - pdfio_file_t *pdf, - pdfio_obj_t *page, - int orig_no); // 1st mode:existing - -void _cfPDFToPDF_PDFioProcessor_create_newMode(_cfPDFToPDF_PDFioProcessor *handle, - pdfio_file_t *pdf, - float width, float height); // 2nd mode:create new - -void _cfPDFToPDF_PDFioProcessor_debug(_cfPDFToPDF_PDFioProcessor *handle, - const _cfPDFToPDFPageRect *rect, - float xpos, float ypos); - -bool _cfPDFToPDF_PDFioProcessor_is_existing(struct _cfPDFToPDF_PDFioProcessor *self); - -pdfio_obj_t* _cfPDFToPDF_PDFioProcessor_get(struct _cfPDFToPDF_PDFioProcessor *handle); - -//inherited functions -void _cfPDFToPDF_PDFioProcessor_destroy(_cfPDFToPDF_PDFioProcessor *handle); - -_cfPDFToPDFPageRect _cfPDFToPDF_PDFioProcessor_get_rect(const _cfPDFToPDF_PDFioProcessor *handle); - -void _cfPDFToPDF_PDFioProcessor_add_border_rect(_cfPDFToPDF_PDFioProcessor *handle, - const _cfPDFToPDFPageRect rect, - pdftopdf_border_type_e border, - float fscale); - - -pdftopdf_rotation_e _cfPDFToPDF_PDFioProcessor_crop(_cfPDFToPDF_PDFioProcessor *handle, - const _cfPDFToPDFPageRect *cropRect, - pdftopdf_rotation_e orientation, - pdftopdf_rotation_e param_orientation, - pdftopdf_position_e xpos, - pdftopdf_position_e ypos, - bool scale, bool autorotate, - pdftopdf_doc_t *doc); - - -void _cfPDFToPDF_PDFioPageHandle_add_subpage(_cfPDFToPDF_PDFioProcessor *handle, - _cfPDFToPDF_PDFioProcessor *sub, - float xpos, float ypos, float scale, - const _cfPDFToPDFPageRect *crop); - -bool _cfPDFToPDF_PDFioProcessor_is_landscape(const _cfPDFToPDF_PDFioProcessor *handle, - pdftopdf_rotation_e orientation); - -void _cfPDFToPDF_PDFioProcessor_mirror(_cfPDFToPDF_PDFioProcessor *handle); - -void _cfPDFToPDF_PDFioProcessor_rotate(_cfPDFToPDF_PDFioProcessor *handle, pdftopdf_rotation_e rot); - -void _cfPDFToPDF_PDFioProcessor_add_label(_cfPDFToPDF_PDFioProcessor *handle, - const _cfPDFToPDFPageRect *rect, - const char *label); - - -//_cfPDFToPDFPDFioProcessor functions -void _cfPDFToPDF_PDFioProcessor_close_file(_cfPDFToPDF_PDFioProcessor *processor); - -bool _cfPDFToPDF_PDFioProcessor_load_file(_cfPDFToPDF_PDFioProcessor *processor, - FILE *f, pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e take, int flatten_forms); - -bool _cfPDFToPDF_PDFioProcessor_load_filename(_cfPDFToPDF_PDFioProcessor *processor, - const char *name, - pdftopdf_doc_t *doc, - int flatten_forms); - -void _cfPDFToPDF_PDFioProcessor_start(_cfPDFToPDF_PDFioProcessor *processor, int flatten_forms); - -bool _cfPDFToPDF_PDFioProcessor_check_print_permissions(_cfPDFToPDF_PDFioProcessor *processor, - pdftopdf_doc_t *doc); - -_cfPDFToPDF_PDFioProcessor** _cfPDFToPDF_PDFioProcessor_get_pages(_cfPDFToPDF_PDFioProcessor *handle, - pdftopdf_doc_t *doc, - int *out_len); - -_cfPDFToPDF_PDFioProcessor* _cfPDFToPDF_PDFioProcessor_new_page(_cfPDFToPDF_PDFioProcessor *handle, - float width, float height, - pdftopdf_doc_t *doc); - -void _cfPDFToPDF_PDFioProcessor_multiply(_cfPDFToPDF_PDFioProcessor *handle, - int copies, bool collate); - -void _cfPDFToPDF_PDFioProcessor_auto_rotate_all(_cfPDFToPDF_PDFioProcessor *handle, - bool dst_lscape, - pdftopdf_rotation_e normal_landscape); - -void _cfPDFToPDF_PDFioProcessor_add_cm(_cfPDFToPDF_PDFioProcessor *handle, - const char *defaulticc, const char *outputicc); - -void _cfPDFToPDF_PDFioProcessor_set_comments(_cfPDFToPDF_PDFioProcessor *handle, - char **comments, int num_comments); -void _cfPDFToPDF_PDFioProcessor_emit_file(_cfPDFToPDF_PDFioProcessor *handle, - FILE *f, pdftopdf_doc_t *doc, - pdftopdf_arg_ownership_e take); - -void _cfPDFToPDF_PDFioProcessor_emit_filename(_cfPDFToPDF_PDFioProcessor *handle, - const char *name, pdftopdf_doc_t *doc); - -bool _cfPDFToPDF_PDFioProcessor_has_acro_form(_cfPDFToPDF_PDFioProcessor *handle); diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c index 0609805f..6f16ec0a 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c @@ -193,7 +193,7 @@ _cfPDFToPDFPageHandle_create_newMode(_cfPDFToPDFPageHandle *handle, pdfio_rect_t *media_box = _cfPDFToPDFMakeBox(0, 0, width, height); pdfio_dict_t *resources_dict = pdfioDictCreate(pdf); - pdfioDictSetNull(resources_dict, "XObject"); + pdfioDictClear(resources_dict, "XObject"); pdfioDictSetName(pageDict, "Type", "Page"); pdfioDictSetRect(pageDict, "MediaBox", media_box); @@ -201,7 +201,7 @@ _cfPDFToPDFPageHandle_create_newMode(_cfPDFToPDFPageHandle *handle, // Add /Resources dictionary with empty /XObject entry pdfio_dict_t *resource_dict = pdfioDictCreate(pdf); - pdfioDictSetNull(resource_dict, "XObject"); + pdfioDictClear(resource_dict, "XObject"); pdfioDictSetDict(pageDict, "Resources", resource_dict); pdfio_stream_t *content_stream = pdfioFileCreatePage(pdf, pageDict); @@ -252,7 +252,7 @@ _cfPDFToPDFPageHandle_get(_cfPDFToPDFPageHandle *handle) // {{{ if (!_cfPDFToPDFPageHandle_is_existing(handle)) { - resources = pdfioDictGetDict(pdfioObjGetDict(ret), "/Resources"); + resources = pdfioDictGetDict(pdfioObjGetDict(ret), "Resources"); if (resources) { char name_buffer[handle->xobjs->count]; @@ -269,7 +269,7 @@ _cfPDFToPDFPageHandle_get(_cfPDFToPDFPageHandle *handle) // {{{ xobj_index++; } } - pdfioDictSetDict(resources, "/XObject", resources); + pdfioDictSetDict(resources, "XObject", resources); } contents_stream = pdfioPageOpenStream(ret, 0, true); @@ -279,11 +279,11 @@ _cfPDFToPDFPageHandle_get(_cfPDFToPDFPageHandle *handle) // {{{ pdfioStreamClose(contents_stream); } - contents_dict = pdfioDictGetDict(pdfioObjGetDict(ret), "/Contents"); + contents_dict = pdfioDictGetDict(pdfioObjGetDict(ret), "Contents"); if (contents_dict) { - pdfioDictSetNull(contents_dict, "/Filter"); - pdfioDictSetNull(contents_dict, "/DecodeParms"); + pdfioDictClear(contents_dict, "Filter"); + pdfioDictClear(contents_dict, "DecodeParms"); } pdfioDictSetNumber(pdfioObjGetDict(ret), "/Rotate", _cfPDFToPDFMakeRotate(handle->rotation)); @@ -766,7 +766,6 @@ _cfPDFToPDF_PDFioProcessor_load_file(_cfPDFToPDF_PDFioProcessor *handle, } handle->pdf = pdfioFileCreate("tempfile", NULL, NULL, NULL, NULL, NULL); - if (handle->pdf == NULL) { @@ -799,8 +798,7 @@ _cfPDFToPDF_PDFioProcessor_load_file(_cfPDFToPDF_PDFioProcessor *handle, return false; } - - //_cfPDFToPDF_PDFioProcessor_start(handle, flatten_forms); + _cfPDFToPDF_PDFioProcessor_start(handle, flatten_forms); return true; } @@ -820,7 +818,7 @@ bool _cfPDFToPDF_PDFioProcessor_load_filename(_cfPDFToPDF_PDFioProcessor *handle return false; } - //_cfPDFToPDF_PDFioProcessor_start(handle, flatten_forms); + _cfPDFToPDF_PDFioProcessor_start(handle, flatten_forms); return true; } @@ -832,7 +830,7 @@ _cfPDFToPDF_PDFioProcessor_check_print_permissions(_cfPDFToPDF_PDFioProcessor *h if (!handle->pdf) { if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: No PDF loaded"); + "cfFilterPDFToPDF: No PDF loaded(_cfPDFToPDF_PDFioProcessor_new_page)"); return false; } @@ -856,7 +854,7 @@ get_all_pages(pdfio_file_t *pdf) return pages; } -/* + void _cfPDFToPDF_PDFioProcessor_start(_cfPDFToPDF_PDFioProcessor *proc, int flatten_forms) @@ -867,60 +865,20 @@ _cfPDFToPDF_PDFioProcessor_start(_cfPDFToPDF_PDFioProcessor *proc, return; } - if (flatten_forms) - { - pdfio_dict_t *catalog = pdfioFileGetCatalog(proc->pdf); - pdfio_obj_t *acroForm = pdfioDictGetObj(catalog, "AcroForm"); - - if (acroForm) - { - pdfio_dict_t *acroForm_dict = pdfioObjGetDict(acroForm); - pdfio_array_t *fields = pdfioDictGetArray(acroForm_dict, "Fields"); - size_t num_fields = pdfioArrayGetSize(fields); - - // Iterating over each field and generating render as static content - for (size_t i = 0; i < num_fields; i++) - { - pdfio_obj_t *field = pdfioArrayGetObj(fields, i); - pdfio_dict_t *field_dict = pdfioObjGetDict(field); - const char *field_type = pdfioDictGetName(field_dict, "FT"); // Field type - const char *field_value = pdfioDictGetString(field_dict, "V"); // Field value - - if (field_type && strcmp(field_type, "Tx") == 0) - { - double x = 100.0, y = 200.0; - pdfio_stream_t *stream = pdfioPageOpenStream(handle->page, 0, true); - - pdfioContentTextMoveTo(stream, x, y); - pdfioContentTextShow(stream, 1, field_value); // Render the text - - pdfioStreamClose(stream); // Closing the stream after render - } - } - pdfioDictSetNull(catalog, "AcroForm"); - } - } - - - // Get all pages + // Get all pages + proc->orig_pages = get_all_pages(proc->pdf); - size_t num_pages = pdfioFileGetNumPages(proc->pdf); - - // Remove (unlink) all pages - for (size_t i = 0; i < num_pages; i++) - { -// pdfio_remove_page(pdf, orig_pages[i]); - } + + proc->orig_pages_size = pdfioFileGetNumPages(proc->pdf); pdfio_dict_t *root = pdfioFileGetCatalog(proc->pdf); - pdfioDictSetNull(root, "PageMode"); - pdfioDictSetNull(root, "Outlines"); - pdfioDictSetNull(root, "OpenAction"); - pdfioDictSetNull(root, "PageLabels"); + pdfioDictClear(root, "PageMode"); + pdfioDictClear(root, "Outlines"); + pdfioDictClear(root, "OpenAction"); + pdfioDictClear(root, "PageLabels"); pdfioFileClose(proc->pdf); } -*/ _cfPDFToPDFPageHandle** _cfPDFToPDF_PDFioProcessor_get_pages(_cfPDFToPDF_PDFioProcessor *handle, @@ -928,7 +886,8 @@ _cfPDFToPDF_PDFioProcessor_get_pages(_cfPDFToPDF_PDFioProcessor *handle, { _cfPDFToPDFPageHandle **ret = NULL; - if (handle->orig_pages_size == 0 || handle->orig_pages == NULL) + + if (handle->orig_pages_size == 0 || handle->orig_pages == NULL) { if (doc->logfunc) { @@ -976,7 +935,7 @@ _cfPDFToPDF_PDFioProcessor_new_page(_cfPDFToPDF_PDFioProcessor *handle, if (!handle->pdf) { if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: No PDF loaded"); + "cfFilterPDFToPDF: No PDF loaded(_cfPDFToPDF_PDFioProcessor_new_page)"); return NULL; } diff --git a/cupsfilters/pdftopdf/pdftopdf-processor-private.h b/cupsfilters/pdftopdf/pdftopdf-processor-private.h deleted file mode 100644 index fc3a5943..00000000 --- a/cupsfilters/pdftopdf/pdftopdf-processor-private.h +++ /dev/null @@ -1,113 +0,0 @@ -// -// Copyright 2024 Uddhav Phatak -// -// Licensed under Apache License v2.0. See the file "LICENSE" for more -// information. -// - -#ifndef C_PDFTOPDF_PROCESSOR_PRIVATE_H -#define C_PDFTOPDF_PROCESSOR_PRIVATE_H - -#include "C-pptypes-private.h" -#include "C-nup-private.h" -#include "C-pdftopdf-private.h" -#include "pdfio-pdftopdf-processor-private.h" -#include "C-intervalset-private.h" -#include -#include - -typedef enum { - CF_PDFTOPDF_BOOKLET_OFF, - CF_PDFTOPDF_BOOKLET_ON, - CF_PDFTOPDF_BOOKLET_JUST_SHUFFLE -} pdftopdf_booklet_mode_e; - -typedef struct _cfPDFToPDF_PDFioProcessor{ - - // 1st mode: existing - pdfio_obj_t *page; // Equivalent to QPDFObjectHandle - int no; - - // 2nd mode: create new - HashTable *xobjs; // Pointer to a single HashTable - - char *content; // Equivalent to std::string content - - pdftopdf_rotation_e rotation; - - // Other members - pdfio_file_t *pdf; // Equivalent to std::unique_ptr - pdfio_obj_t **orig_pages; // Equivalent to std::vector - size_t orig_pages_size; // Current number of pages - size_t orig_pages_capacity; // Capacity for page array - - bool hasCM; - char *extraheader; -} _cfPDFToPDF_PDFioProcessor; - - -typedef struct { - int job_id, num_copies; - const char *user, *title; - bool pagesize_requested; - bool fitplot; - bool fillprint; // print-scaling = fill - bool cropfit; // -o crop-to-fit - bool autoprint; // print-scaling = auto - bool autofit; // print-scaling = auto-fit - bool fidelity; - bool no_orientation; - _cfPDFToPDFPageRect page; - pdftopdf_rotation_e orientation, normal_landscape; // normal_landscape (i.e. default direction) is e.g. needed for number-up=2 - bool paper_is_landscape; - bool duplex; - pdftopdf_border_type_e border; - _cfPDFToPDFNupParameters nup; - bool reverse; - - char *page_label; - bool even_pages, odd_pages; - _cfPDFToPDFIntervalSet *page_ranges; - _cfPDFToPDFIntervalSet *input_page_ranges; - - bool mirror; - - pdftopdf_position_e xpos, ypos; - - bool collate; - - bool even_duplex; // make number of pages a multiple of 2 - - pdftopdf_booklet_mode_e booklet; - int book_signature; - - bool auto_rotate; - - int device_copies; - bool device_collate; - bool set_duplex; - - int page_logging; - int copies_to_be_logged; -} _cfPDFToPDFProcessingParameters; - -void _cfPDFToPDFProcessingParameters_init(_cfPDFToPDFProcessingParameters *processingParams); - -bool _cfPDFToPDFProcessingParameters_with_page(const _cfPDFToPDFProcessingParameters *self, - int outno); - -bool _cfPDFToPDFProcessingParameters_have_page(const _cfPDFToPDFProcessingParameters *self, - int pageno); -void _cfPDFToPDFProcessingParameters_dump(const _cfPDFToPDFProcessingParameters *self, - pdftopdf_doc_t *doc); - - - - - -int* _cfPDFToPDFBookletShuffle(int numPages, int signature, int* ret_size); - -bool _cfProcessPDFToPDF(_cfPDFToPDF_PDFioProcessor *proc, - _cfPDFToPDFProcessingParameters *param, - pdftopdf_doc_t *doc); -#endif // C_PDFTOPDF_PROCESSOR_PRIVATE_H diff --git a/cupsfilters/pdftopdf/pdftopdf.c b/cupsfilters/pdftopdf/pdftopdf.c index 0e008fd5..70918d53 100644 --- a/cupsfilters/pdftopdf/pdftopdf.c +++ b/cupsfilters/pdftopdf/pdftopdf.c @@ -895,11 +895,23 @@ cfFilterPDFToPDF(int inputfd, return 1; } + char temp_filename[] = "/tmp/pdfio_temp_XXXXXX"; + int temp_fd = mkstemp(temp_filename); // Create a unique temporary file + FILE *temp_f = fdopen(temp_fd, "wb"); + char buffer[8192]; + size_t bytes_read; + while ((bytes_read = fread(buffer, 1, sizeof(buffer), inputfp)) > 0) { + fwrite(buffer, 1, bytes_read, temp_f); + } + + + if (!streaming) { if (is_empty(inputfp)) { fclose(inputfp); + fclose(temp_f); // This closes temp_fd if (log) log(ld, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Input is empty, outputting empty file."); return 0; @@ -909,9 +921,10 @@ cfFilterPDFToPDF(int inputfd, "cfFilterPDFToPDF: Processing PDF input with PDFio: Page-ranges, page-set, number-up, booklet, size adjustment, ..."); // Load the PDF input data into PDFio - if (!_cfPDFToPDF_PDFioProcessor_load_file(&proc, inputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE, 1)) + if (!_cfPDFToPDF_PDFioProcessor_load_filename(&proc, temp_filename, &doc, 1)) { fclose(inputfp); + fclose(temp_f); // This closes temp_fd return 1; } @@ -956,6 +969,7 @@ cfFilterPDFToPDF(int inputfd, if (fwrite(buf, 1, bytes, outputfp) != bytes) break; fclose(inputfp); + fclose(temp_f); // This closes temp_fd } fclose(outputfp); diff --git a/cupsfilters/pdftopdf/processor.h b/cupsfilters/pdftopdf/processor.h index 4af28d88..c8b77b22 100644 --- a/cupsfilters/pdftopdf/processor.h +++ b/cupsfilters/pdftopdf/processor.h @@ -117,7 +117,6 @@ typedef struct _cfPDFToPDF_PDFioProcessor{ pdfio_file_t *pdf; // Equivalent to std::unique_ptr pdfio_obj_t **orig_pages; // Equivalent to std::vector size_t orig_pages_size; // Current number of pages - size_t orig_pages_capacity; // Capacity for page array bool hasCM; char *extraheader; @@ -136,10 +135,10 @@ bool _cfPDFToPDF_PDFioProcessor_load_filename(_cfPDFToPDF_PDFioProcessor *proces const char *name, pdftopdf_doc_t *doc, int flatten_forms); -/* + void _cfPDFToPDF_PDFioProcessor_start(_cfPDFToPDF_PDFioProcessor *processor, int flatten_forms); -*/ + bool _cfPDFToPDF_PDFioProcessor_check_print_permissions(_cfPDFToPDF_PDFioProcessor *processor, pdftopdf_doc_t *doc); From 282cdf5ca064d2bd9ec09bf2885b5f9f15dfb885 Mon Sep 17 00:00:00 2001 From: ThePhatak <111195860+uddhavphatak@users.noreply.github.com> Date: Wed, 1 Jan 2025 23:40:03 +0530 Subject: [PATCH 62/64] Update build.yaml the new features of pdfio have been updated in file cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c --- .github/workflows/build.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 1230fa60..7ca2e0e5 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -24,13 +24,13 @@ jobs: sudo apt-get install -y avahi-daemon libavahi-client-dev libssl-dev libpam-dev libusb-1.0-0-dev zlib1g-dev sudo apt install autotools-dev autopoint cmake libtool pkg-config libcups2-dev libexif-dev liblcms2-dev libfontconfig1-dev sudo apt install libfreetype6-dev build-essential qtbase5-dev qtchooser libcairo2-dev libboost-system-dev libboost-thread-dev libboost-program-options-dev libboost-test-dev libopenjp2-7-dev liblcms2-dev libjpeg-dev - - name: Install pdfio >= 1.3.0 + - name: Install pdfio >= 1.4.0 run: | cd .. mkdir pdfio - wget https://github.com/michaelrsweet/pdfio/releases/download/v1.3.1/pdfio-1.3.1.tar.gz - tar -xzf pdfio-1.3.1.tar.gz - cd pdfio-1.3.1 + wget https://github.com/michaelrsweet/pdfio/releases/download/v1.4.0/pdfio-1.4.0.tar.gz + tar -xzf pdfio-1.4.0.tar.gz + cd pdfio-1.4.0 ./configure --prefix=/usr --enable-shared make all make test From b44ca2ea67115e4eb8dd93355d1e2e5066e8fe76 Mon Sep 17 00:00:00 2001 From: Uddhav Phatak Date: Mon, 13 Jan 2025 12:27:38 +0530 Subject: [PATCH 63/64] fixed all the issues related to pointer allocation resulting in program crash --- cupsfilters/pdf.c | 1 + cupsfilters/pdftopdf/nup.c | 15 +- .../pdftopdf/pdfio-pdftopdf-processor.c | 188 ++++++---- cupsfilters/pdftopdf/pdfio-pdftopdf.c | 1 + cupsfilters/pdftopdf/pdftopdf-processor.c | 355 +++++++++++------- cupsfilters/pdftopdf/pdftopdf.c | 282 ++++++++------ cupsfilters/pdftopdf/pptypes.c | 16 +- cupsfilters/pdftopdf/processor.h | 11 +- cupsfilters/test-filter-cases.txt | 2 - 9 files changed, 532 insertions(+), 339 deletions(-) diff --git a/cupsfilters/pdf.c b/cupsfilters/pdf.c index 2fbe385e..dafe4ae0 100644 --- a/cupsfilters/pdf.c +++ b/cupsfilters/pdf.c @@ -101,6 +101,7 @@ cfPDFPagesFP(char *file) int pages = pdfioFileGetNumPages(pdf); pdfioFileClose(pdf); + fprintf(stderr, "uddhav the number of pages are:%d", pages); return pages; } diff --git a/cupsfilters/pdftopdf/nup.c b/cupsfilters/pdftopdf/nup.c index 5dfcec02..187b4d3f 100644 --- a/cupsfilters/pdftopdf/nup.c +++ b/cupsfilters/pdftopdf/nup.c @@ -212,8 +212,11 @@ _cfPDFToPDFNupState_convert_order(const _cfPDFToPDFNupState *state, subx = (state->param.nupX - 1) * (state->param.xstart + 1) / 2 - state->param.xstart * subx; suby = (state->param.nupY - 1) * (state->param.ystart + 1) / 2 - state->param.ystart * suby; - - int_pair result = {subx, suby}; + + int_pair result; + result.first = subx; + result.second = suby; + return result; } // }}} @@ -283,9 +286,11 @@ _cfPDFToPDFNupState_next_page(_cfPDFToPDFNupState *state, ret->sub.width = in_width; ret->sub.height = in_height; - int_pair sub = _cfPDFToPDFNupState_convert_order(state, state->subpage); - _cfPDFToPDFNupState_calculate_edit(state, sub.first, sub.second, ret); - + int_pair *sub = (int_pair*)malloc(sizeof(int_pair)); + *sub = _cfPDFToPDFNupState_convert_order(state, state->subpage); + _cfPDFToPDFNupState_calculate_edit(state, sub->first, sub->second, ret); + + free(sub); return (state->subpage == 0); } // }}} diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c index 6f16ec0a..cdfd178f 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c @@ -185,6 +185,7 @@ _cfPDFToPDFPageHandle_create_newMode(_cfPDFToPDFPageHandle *handle, { handle->no = 0; handle->rotation = ROT_0; + handle->xobjs = hashCreate_hash_table(); handle->content = strdup("q\n"); // Allocate and copy content string @@ -207,9 +208,6 @@ _cfPDFToPDFPageHandle_create_newMode(_cfPDFToPDFPageHandle *handle, pdfio_stream_t *content_stream = pdfioFileCreatePage(pdf, pageDict); pdfioStreamWrite(content_stream, handle->content, strlen(handle->content)); pdfioStreamClose(content_stream); - - handle->xobjs = NULL; - handle->xobjs->count = 0; } // }}} @@ -539,6 +537,34 @@ _cfPDFToPDFPageHandle_is_landscape(const _cfPDFToPDFPageHandle *handle, } // }}} +void content_append(char **content, const char *text) +{ + if (!content || !text) + { + fprintf(stderr, "Error: Null pointer passed to content_append.\n"); + return; + } + + // Calculate current size and new size + size_t current_length = *content ? strlen(*content) : 0; + size_t text_length = strlen(text); + size_t new_length = current_length + text_length + 1; // +1 for null terminator + + // Reallocate memory for the new content + char *new_content = realloc(*content, new_length); + if (!new_content) + { + fprintf(stderr, "Error: Memory allocation failed in content_append.\n"); + return; + } + + // Append the text to the newly allocated memory + strcpy(new_content + current_length, text); + + // Update the content pointer + *content = new_content; +} + void _cfPDFToPDFPageHandle_add_subpage(_cfPDFToPDFPageHandle *handle, _cfPDFToPDFPageHandle *sub, @@ -546,61 +572,71 @@ _cfPDFToPDFPageHandle_add_subpage(_cfPDFToPDFPageHandle *handle, float xpos, float ypos, float scale, const _cfPDFToPDFPageRect *crop) // {{{ { - char xoname[64]; - snprintf(xoname, sizeof(xoname), "/X%d", (sub->no != -1) ? sub->no : ++handle->no); + _cfPDFToPDFPageHandle *qsub = (_cfPDFToPDFPageHandle *)sub; - if (crop) - { - _cfPDFToPDFPageRect pg = _cfPDFToPDFPageHandle_get_rect(sub); - _cfPDFToPDFPageRect tmp = *crop; - tmp.width = tmp.right - tmp.left; - tmp.height = tmp.top - tmp.bottom; - pdftopdf_rotation_e tempRotation = _cfPDFToPDFGetRotate(sub->page); - _cfPDFToPDFPageRect_rotate_move(&tmp, pdftopdf_rotation_neg(tempRotation), tmp.width, tmp.height); - - if (pg.width < tmp.width) - pg.right = pg.left + tmp.width; - if (pg.height < tmp.height) - pg.top = pg.bottom + tmp.height; - - _cfPDFToPDFPageRect rect = ungetRect(pg, sub, ROT_0, sub->page); - - pdfio_rect_t *trimBox = _cfPDFToPDFMakeBox(rect.left, rect.bottom, rect.right, rect.top); + // Generate xobject name + char xoname[32]; + snprintf(xoname, sizeof(xoname), "/X%d", (qsub->no != -1) ? qsub->no : ++(handle->no)); - pdfio_dict_t *pageDict = pdfioObjGetDict(sub->page); - pdfioDictSetRect(pageDict, "TrimBox", trimBox); - } - - hashInsert(handle->xobjs, xoname,_cfPDFToPDFMakeXObject(pdf, sub->page)); - - _cfPDFToPDFMatrix mtx; - _cfPDFToPDFMatrix_init(&mtx); - _cfPDFToPDFMatrix_translate(&mtx, xpos, ypos); - _cfPDFToPDFMatrix_scale(&mtx, scale, scale); - _cfPDFToPDFMatrix_rotate(&mtx, sub->rotation); - - if (crop) { - _cfPDFToPDFMatrix_translate(&mtx, crop->left, crop->bottom); - } + // Handle crop if provided + if (crop) + { + _cfPDFToPDFPageRect pg = _cfPDFToPDFPageHandle_get_rect(qsub); + _cfPDFToPDFPageRect tmp = *crop; - strcat(handle->content, "q\n "); - char matrix_string[128]; - _cfPDFToPDFMatrix_get_string(&mtx, matrix_string, 128); - strcat(handle->content, matrix_string); - strcat(handle->content, " cm\n "); - - if (crop) { - strcat(handle->content, " cm\n "); - char crop_string[128]; - snprintf(crop_string, sizeof(crop_string), "0 0 %.2f %.2f re W n\n", crop->right - crop->left, crop->top - crop->bottom); - strcat(handle->content, crop_string); - } + tmp.width = tmp.right - tmp.left; + tmp.height = tmp.top - tmp.bottom; + pdftopdf_rotation_e tempRotation = _cfPDFToPDFGetRotate(sub->page); + _cfPDFToPDFPageRect_rotate_move(&tmp, pdftopdf_rotation_neg(tempRotation), tmp.width, tmp.height); + + if (pg.width < tmp.width) + pg.right = pg.left + tmp.width; + if (pg.height < tmp.height) + pg.top = pg.bottom + tmp.height; + + _cfPDFToPDFPageRect rect = ungetRect(pg, qsub, ROT_0, qsub->page); + + pdfio_rect_t *trimBox = _cfPDFToPDFMakeBox(rect.left, rect.bottom, rect.right, rect.top); + + pdfio_dict_t *pageDict = pdfioObjGetDict(sub->page); + pdfioDictSetRect(pageDict, "TrimBox", trimBox); + } + + // Apply transformations + _cfPDFToPDFMatrix mtx; + _cfPDFToPDFMatrix_init(&mtx); + _cfPDFToPDFMatrix_translate(&mtx, xpos, ypos); + _cfPDFToPDFMatrix_scale(&mtx, scale, scale); + _cfPDFToPDFMatrix_rotate(&mtx, qsub->rotation); + + if (crop) + { + _cfPDFToPDFMatrix_translate(&mtx, crop->left, crop->bottom); + } + + // Add content + content_append(&handle->content, "q\n "); + + char mtx_buffer[128]; // Adjust buffer size as needed + _cfPDFToPDFMatrix_get_string(&mtx, mtx_buffer, sizeof(mtx_buffer)); + content_append(&handle->content, mtx_buffer); + content_append(&handle->content, " cm\n "); + + if (crop) + { + char crop_cmd[128]; + snprintf(crop_cmd, sizeof(crop_cmd), "0 0 %f %f re W n\n ", + crop->right - crop->left, crop->top - crop->bottom); + content_append(&handle->content, crop_cmd); + } - strcat(handle->content, xoname); - strcat(handle->content, " Do\nQ\n"); + content_append(&handle->content, xoname); + content_append(&handle->content, " Do\n"); + content_append(&handle->content, "Q\n"); } // }}} + void _cfPDFToPDFPageHandle_mirror(_cfPDFToPDFPageHandle *handle, pdfio_file_t *pdf) @@ -614,7 +650,8 @@ _cfPDFToPDFPageHandle_mirror(_cfPDFToPDFPageHandle *handle, pdfio_obj_t *subpage = _cfPDFToPDFPageHandle_get(handle);; _cfPDFToPDFPageHandle_create_newMode(handle, pdf, orig.width, orig.height); - hashInsert(handle->xobjs, xoname,_cfPDFToPDFMakeXObject(pdf, subpage)); + hashInsert(handle->xobjs, xoname, _cfPDFToPDFMakeXObject(pdf, subpage)); + char temp_content[1024]; snprintf(temp_content, sizeof(temp_content), "%s Do\n", xoname); @@ -744,6 +781,7 @@ void _cfPDFToPDF_PDFioProcessor_close_file(_cfPDFToPDF_PDFioProcessor *handle) { if (handle->pdf != NULL) { + fprintf(stderr, "maa chudi padi hai\n"); pdfioFileClose(handle->pdf); handle->pdf = NULL; } @@ -808,7 +846,6 @@ bool _cfPDFToPDF_PDFioProcessor_load_filename(_cfPDFToPDF_PDFioProcessor *handle int flatten_forms) { _cfPDFToPDF_PDFioProcessor_close_file(handle); - handle->pdf = pdfioFileOpen(name, NULL, NULL, NULL, NULL); if (!handle->pdf) { @@ -819,7 +856,8 @@ bool _cfPDFToPDF_PDFioProcessor_load_filename(_cfPDFToPDF_PDFioProcessor *handle } _cfPDFToPDF_PDFioProcessor_start(handle, flatten_forms); - return true; + + return true; } @@ -876,17 +914,15 @@ _cfPDFToPDF_PDFioProcessor_start(_cfPDFToPDF_PDFioProcessor *proc, pdfioDictClear(root, "Outlines"); pdfioDictClear(root, "OpenAction"); pdfioDictClear(root, "PageLabels"); - - pdfioFileClose(proc->pdf); } _cfPDFToPDFPageHandle** _cfPDFToPDF_PDFioProcessor_get_pages(_cfPDFToPDF_PDFioProcessor *handle, - pdftopdf_doc_t *doc, int *out_len) + pdftopdf_doc_t *doc, size_t *out_len) { + _cfPDFToPDFPageHandle **ret = NULL; - if (handle->orig_pages_size == 0 || handle->orig_pages == NULL) { if (doc->logfunc) @@ -898,7 +934,7 @@ _cfPDFToPDF_PDFioProcessor_get_pages(_cfPDFToPDF_PDFioProcessor *handle, return ret; } - int len = handle->orig_pages_size; + int len = (int)handle->orig_pages_size; *out_len = len; ret = (_cfPDFToPDFPageHandle **)malloc(len * sizeof(_cfPDFToPDFPageHandle *)); @@ -906,7 +942,6 @@ _cfPDFToPDF_PDFioProcessor_get_pages(_cfPDFToPDF_PDFioProcessor *handle, { fprintf(stderr, "Memory allocation failed for pages array\n"); } - for (int i = 0; i < len; i++) { ret[i] = (_cfPDFToPDFPageHandle *)malloc(sizeof(_cfPDFToPDFPageHandle)); @@ -923,7 +958,7 @@ _cfPDFToPDF_PDFioProcessor_get_pages(_cfPDFToPDF_PDFioProcessor *handle, ret[i]->page = handle->orig_pages[i]; //ret[i]->orig_pages_size = i + 1; } - + return ret; } @@ -938,7 +973,6 @@ _cfPDFToPDF_PDFioProcessor_new_page(_cfPDFToPDF_PDFioProcessor *handle, "cfFilterPDFToPDF: No PDF loaded(_cfPDFToPDF_PDFioProcessor_new_page)"); return NULL; } - _cfPDFToPDFPageHandle *page_handle = (_cfPDFToPDFPageHandle *)malloc(sizeof(_cfPDFToPDFPageHandle)); _cfPDFToPDFPageHandle_create_newMode(page_handle, handle->pdf, width, height); @@ -1019,6 +1053,7 @@ _cfPDFToPDF_PDFioProcessor_add_cm(_cfPDFToPDF_PDFioProcessor *handle, if (_cfPDFToPDFHasOutputIntent(handle->pdf)) return; + fprintf(stderr, "han, iske andar aaya "); pdfio_obj_t *srcicc = _cfPDFToPDFSetDefaultICC(handle->pdf, defaulticc); _cfPDFToPDFAddDefaultRGB(handle->pdf, srcicc); _cfPDFToPDFAddOutputIntent(handle->pdf, outputicc); @@ -1060,10 +1095,37 @@ _cfPDFToPDF_PDFioProcessor_emit_file(_cfPDFToPDF_PDFioProcessor *handle, { } + void _cfPDFToPDF_PDFioProcessor_emit_filename(_cfPDFToPDF_PDFioProcessor *handle, - const char *name, pdftopdf_doc_t *doc) + const char *output_filename, pdftopdf_doc_t *doc) { + pdfio_file_t *output_pdf = pdfioFileCreate(output_filename, NULL, NULL, NULL, NULL, NULL); + if (!output_pdf) { + fprintf(stderr, "Failed to create output PDF file: %s\n", output_filename); + } + size_t num_pages = pdfioFileGetNumPages(handle->pdf); + + for (size_t i = 0; i < num_pages; i++) + { + pdfio_obj_t *input_page = pdfioFileGetPage(handle->pdf, i); + if (!input_page) + { + fprintf(stderr, "Failed to get page %zu from input PDF\n", i); + pdfioFileClose(output_pdf); + } + + if (!pdfioPageCopy(output_pdf, input_page)) + { + fprintf(stderr, "Failed to copy object %zu to output PDF\n", i); + pdfioFileClose(output_pdf); + } + } + + if (!pdfioFileClose(output_pdf)) + { + fprintf(stderr, "Failed to save output PDF file\n"); + } } bool diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf.c b/cupsfilters/pdftopdf/pdfio-pdftopdf.c index 96380f48..4f1d75c8 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf.c +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf.c @@ -241,5 +241,6 @@ _cfPDFToPDFMatrix_get_string(const _cfPDFToPDFMatrix *matrix, snprintf(buffer, bufsize, "%f %f %f %f %f %f", matrix->ctm[0], matrix->ctm[1], matrix->ctm[2], matrix->ctm[3], matrix->ctm[4], matrix->ctm[5]); + buffer[bufsize - 1] = '\0'; // Ensure null-termination } // }}} diff --git a/cupsfilters/pdftopdf/pdftopdf-processor.c b/cupsfilters/pdftopdf/pdftopdf-processor.c index 766d3992..0d5f856b 100644 --- a/cupsfilters/pdftopdf/pdftopdf-processor.c +++ b/cupsfilters/pdftopdf/pdftopdf-processor.c @@ -16,7 +16,6 @@ void _cfPDFToPDFProcessingParameters_init(_cfPDFToPDFProcessingParameters *processingParams) // {{{ { - processingParams->job_id = 0; processingParams->num_copies = 1; processingParams->user = NULL; @@ -60,20 +59,64 @@ _cfPDFToPDFProcessingParameters_init(_cfPDFToPDFProcessingParameters *processing processingParams->page_logging = -1; processingParams->copies_to_be_logged = 0; - processingParams->page.width = 612.0; // Letter size width in points - processingParams->page.height = 792.0; // Letter size height in points - processingParams->page.top = processingParams->page.height - 36.0; - processingParams->page.bottom = 36.0; - processingParams->page.left = 18.0; - processingParams->page.right = processingParams->page.width - 18.0; + processingParams->page = (_cfPDFToPDFPageRect *)malloc(sizeof(_cfPDFToPDFPageRect)); + _cfPDFToPDFPageRect_init(processingParams->page); + + processingParams->page->width = 612.0; // Letter size width in points + processingParams->page->height = 792.0; // Letter size height in points + processingParams->page->top = processingParams->page->height - 36.0; + processingParams->page->bottom = 36.0; + processingParams->page->left = 18.0; + processingParams->page->right = processingParams->page->width - 18.0; + + processingParams->nup = (_cfPDFToPDFNupParameters *)malloc(sizeof(_cfPDFToPDFNupParameters)); + _cfPDFToPDFNupParameters_init(processingParams->nup); + processingParams->input_page_ranges = (_cfPDFToPDFIntervalSet *)malloc(sizeof(_cfPDFToPDFIntervalSet)); + _cfPDFToPDFIntervalSet_init(processingParams->input_page_ranges); _cfPDFToPDFIntervalSet_add_single(processingParams->input_page_ranges, 1); _cfPDFToPDFIntervalSet_finish(processingParams->input_page_ranges); + + processingParams->page_ranges = (_cfPDFToPDFIntervalSet *)malloc(sizeof(_cfPDFToPDFIntervalSet)); + _cfPDFToPDFIntervalSet_init(processingParams->page_ranges); _cfPDFToPDFIntervalSet_add_single(processingParams->page_ranges, 1); _cfPDFToPDFIntervalSet_finish(processingParams->page_ranges); } // }}} +void +_cfPDFToPDFProcessingParameters_free(_cfPDFToPDFProcessingParameters *processingParams) +{ + if (!processingParams) + return; + // Free `page` + if (processingParams->page) + free(processingParams->page); + + if (processingParams->nup) + free(processingParams->nup); + + if (processingParams->input_page_ranges) + { + _cfPDFToPDFIntervalSet_clear(processingParams->input_page_ranges); + free(processingParams->input_page_ranges); + } + + if (processingParams->page_ranges) + { + _cfPDFToPDFIntervalSet_clear(processingParams->page_ranges); + free(processingParams->page_ranges); + } + + // Free dynamically allocated strings + if (processingParams->page_label) + free(processingParams->page_label); + + // Free the structure itself + free(processingParams); +} + + void BookletMode_dump(pdftopdf_booklet_mode_e bkm, pdftopdf_doc_t *doc) // {{{ @@ -96,6 +139,23 @@ BookletMode_dump(pdftopdf_booklet_mode_e bkm, } // }}} +bool +_cfPDFToPDFProcessingParameters_even_odd_page(const _cfPDFToPDFProcessingParameters *self, + int outno) // {{{ +{ + if (outno % 2 == 0) + { + if(!self->even_pages) + return (false); + } + else if (!self->odd_pages) + return (false); + + return (true); +} +// }}} + + bool _cfPDFToPDFProcessingParameters_with_page(const _cfPDFToPDFProcessingParameters *processingParams, int outno) // {{{ @@ -103,9 +163,10 @@ _cfPDFToPDFProcessingParameters_with_page(const _cfPDFToPDFProcessingParameters if (outno % 2 == 0) { if (!processingParams->even_pages) + { return false; - } - else if (!processingParams->odd_pages) + } + } else if (!processingParams->odd_pages) { return false; } @@ -137,7 +198,7 @@ _cfPDFToPDFProcessingParameters_dump(const _cfPDFToPDFProcessingParameters *proc "cfFilterPDFToPDF: fitplot: %s", (processingParams->fitplot) ? "true" : "false"); - _cfPDFToPDFPageRect_dump(&processingParams->page, doc); + _cfPDFToPDFPageRect_dump(processingParams->page, doc); _cfPDFToPDFRotationDump(processingParams->orientation, doc); if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, @@ -149,7 +210,7 @@ _cfPDFToPDFProcessingParameters_dump(const _cfPDFToPDFProcessingParameters *proc (processingParams->duplex) ? "true" : "false"); _cfPDFToPDFBorderTypeDump(processingParams->border, doc); - _cfPDFToPDFNupParameters_dump(&processingParams->nup, doc); + _cfPDFToPDFNupParameters_dump(processingParams->nup, doc); if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: reverse: %s", @@ -250,69 +311,73 @@ _cfPDFToPDFBookletShuffle(int numPages, int signature, int* ret_size) } bool -_cfProcessPDFToPDF( _cfPDFToPDF_PDFioProcessor *proc, +_cfProcessPDFToPDF(_cfPDFToPDF_PDFioProcessor *proc, _cfPDFToPDFProcessingParameters *param, pdftopdf_doc_t *doc) { if(!_cfPDFToPDF_PDFioProcessor_check_print_permissions(proc, doc)) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Not allowed to print"); + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + "cfFilterPDFToPDF: Not allowed to print"); return false; } const bool dst_lscape = - (param->paper_is_landscape == - ((param->orientation == ROT_0) || (param->orientation == ROT_180))); + (param->paper_is_landscape == + ((param->orientation == ROT_0) || (param->orientation == ROT_180))); - if (param->paper_is_landscape) + if(param->paper_is_landscape) { - int temp = param->nup.nupX; - param->nup.nupX = param->nup.nupY; - param->nup.nupY = temp; + int temp = param->nup->nupX; + param->nup->nupX = param->nup->nupY; + param->nup->nupY = temp; } - if (param->auto_rotate) + if(param->auto_rotate) + { _cfPDFToPDF_PDFioProcessor_auto_rotate_all(proc, dst_lscape, param->normal_landscape); + } + + size_t num_page = pdfioFileGetNumPages(proc->pdf); + + _cfPDFToPDFPageHandle **pages = malloc((num_page) * sizeof(_cfPDFToPDFPageHandle*)); + pages = _cfPDFToPDF_PDFioProcessor_get_pages(proc, doc, &num_page); - int *num_page; - _cfPDFToPDFPageHandle **pages = _cfPDFToPDF_PDFioProcessor_get_pages(proc, doc, num_page); - - _cfPDFToPDFPageHandle **input_page_range_list = malloc((*num_page) * sizeof(_cfPDFToPDFPageHandle*)); - int input_page_range_size = 0; + _cfPDFToPDFPageHandle **input_page_range_list = malloc((num_page) * sizeof(_cfPDFToPDFPageHandle*)); + size_t input_page_range_list_count = 0; // Tracks the count of valid pages - for (int i = 1; i <= *num_page; i++) - { - if (_cfPDFToPDFProcessingParameters_with_page(param, i)) - { - input_page_range_list[input_page_range_size++] = pages[i - 1]; - input_page_range_size++; + for (size_t i = 1; i <= num_page; i++) { + if (_cfPDFToPDFProcessingParameters_have_page(param, i)) { + input_page_range_list[input_page_range_list_count] = pages[i - 1]; + input_page_range_list_count++; } } - - const int numOrigPages = input_page_range_size; - int* shuffle = NULL; - int* shuffle_size; + input_page_range_list = realloc(input_page_range_list, (input_page_range_list_count) * sizeof(_cfPDFToPDFPageHandle*)); - if (param->booklet != CF_PDFTOPDF_BOOKLET_OFF) + const int numOrigPages = input_page_range_list_count; + int *shuffle; + int shuffle_size = 0; // Declare shuffle_size as an integer + + if (param->booklet != CF_PDFTOPDF_BOOKLET_OFF) { - shuffle = _cfPDFToPDFBookletShuffle(numOrigPages, param->book_signature, shuffle_size); - if (param->booklet == CF_PDFTOPDF_BOOKLET_ON) + shuffle = _cfPDFToPDFBookletShuffle(numOrigPages, param->book_signature, &shuffle_size); + if (param->booklet == CF_PDFTOPDF_BOOKLET_ON) { - _cfPDFToPDFNupParameters_preset(2, ¶m->nup); + _cfPDFToPDFNupParameters_preset(2, param->nup); } - } - else + } + else { - int* shuffle = malloc(numOrigPages * sizeof(numOrigPages)); + shuffle = malloc(numOrigPages * sizeof(int)); for (int i = 0; i < numOrigPages; i++) { - shuffle[i] = i; + shuffle[i] = i; + shuffle_size++; } } - - const int numPages = MAX(*shuffle_size, input_page_range_size); + + const int numPages = (shuffle_size > input_page_range_list_count) ? shuffle_size : input_page_range_list_count; if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: \"print-scaling\" IPP attribute: %s", @@ -322,24 +387,24 @@ _cfProcessPDFToPDF( _cfPDFToPDF_PDFioProcessor *proc, (param->fillprint ? "fill" : (param->cropfit ? "none" : "Not defined, should never happen")))))); - + if (param->autoprint || param->autofit) { bool margin_defined = true; bool document_large = false; - int pw = param->page.right - param->page.left; - int ph = param->page.top - param->page.bottom; - - if ((param->page.width == pw) && (param->page.height == ph)) + int pw = param->page->right - param->page->left; + int ph = param->page->top - param->page->bottom; + + if ((param->page->width == pw) && (param->page->height == ph)) margin_defined = false; - for (int i = 0; i < input_page_range_size; i ++) + for (int i = 0; i < input_page_range_list_count; i ++) { _cfPDFToPDFPageRect r = _cfPDFToPDFPageHandle_get_rect(input_page_range_list[i]); int w = r.width * 100 / 102; // 2% of tolerance int h = r.height * 100 / 102; - if ((w > param->page.width || h > param->page.height) && - (h > param->page.width || w > param->page.height)) + if ((w > param->page->width || h > param->page->height) && + (h > param->page->width || w > param->page->height)) { if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, @@ -348,11 +413,10 @@ _cfProcessPDFToPDF( _cfPDFToPDF_PDFioProcessor *proc, document_large = true; } } - if (param->fidelity && doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: \"ipp-attribute-fidelity\" IPP attribute is set, scaling pages to fit."); - + if (param->autoprint) { if (param->fidelity || document_large) @@ -365,7 +429,6 @@ _cfProcessPDFToPDF( _cfPDFToPDF_PDFioProcessor *proc, else param->cropfit = true; } - else { if (param->fidelity || document_large) @@ -375,7 +438,7 @@ _cfProcessPDFToPDF( _cfPDFToPDF_PDFioProcessor *proc, } } - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Print scaling mode: %s", (param->fitplot ? "Scale to fit printable area" : @@ -385,172 +448,190 @@ _cfProcessPDFToPDF( _cfPDFToPDF_PDFioProcessor *proc, "Do not scale, center, crop if needed" : "Not defined, should never happen")))); + // In Crop mode we do not scale the original document, it should keep the + // exact same size. With N-Up it should be scaled to fit exacly the halves, + // quarters, ... of the sheet, regardless of unprintable margins. + // Therefore we remove the unprintable margins to do all the math without + // them. if (param->cropfit) { - param->page.left = 0; - param->page.bottom = 0; - param->page.right = param->page.width; - param->page.top = param->page.height; + param->page->left = 0; + param->page->bottom = 0; + param->page->right = param->page->width; + param->page->top = param->page->height; } if (param->pagesize_requested && (param->fillprint || param->cropfit)) { - for (int i = 0; i < input_page_range_size; i ++) + for (int i = 0; i < input_page_range_list_count; i ++) { _cfPDFToPDFPageHandle *page = input_page_range_list[i]; pdftopdf_rotation_e orientation; - if(_cfPDFToPDFPageHandle_is_landscape(page, param->orientation)) + + if (_cfPDFToPDFPageHandle_is_landscape(page, param->orientation)) orientation = param->normal_landscape; else orientation = ROT_0; - _cfPDFToPDFPageHandle_crop(page, - ¶m->page, - orientation, - param->orientation, - param->xpos, - param->ypos, - !param->cropfit, - !param->auto_rotate, + + _cfPDFToPDFPageHandle_crop(page, param->page, orientation, param->orientation, + param->xpos, param->ypos, + !param->cropfit, param->auto_rotate, doc); } if (param->fillprint) param->fitplot = true; } - + _cfPDFToPDFPageHandle *curpage; int outputpage = 0; int outputno = 0; - if ((param->nup.nupX == 1) && (param->nup.nupY == 1) && !param->fitplot) + if ((param->nup->nupX == 1) && (param->nup->nupY == 1) && !param->fitplot) { - param->nup.width = param->page.width; - param->nup.height = param->page.height; + param->nup->width = param->page->width; + param->nup->height = param->page->height; } else { - param->nup.width = param->page.right - param->page.left; - param->nup.height = param->page.top - param->page.bottom; + param->nup->width = param->page->right - param->page->left; + param->nup->height = param->page->top - param->page->bottom; } if ((param->orientation == ROT_90) || (param->orientation == ROT_270)) { - int temp = param->nup.nupX; - param->nup.nupX = param->nup.nupY; - param->nup.nupY = temp; - - param->nup.landscape = !param->nup.landscape; + int temp = param->nup->nupX; + param->nup->nupX = param->nup->nupY; + param->nup->nupY = temp; + + param->nup->landscape = !param->nup->landscape; param->orientation = param->orientation - param->normal_landscape; } - + double xpos = 0, ypos = 0; - if(param->nup.landscape) + if (param->nup->landscape) { + // pages[iA]->rotate(param.normal_landscape); param->orientation = param->orientation + param->normal_landscape; - if (param->nup.nupX != 1 || param->nup.nupY != 1 || param->fitplot) + // TODO? better + if (param->nup->nupX != 1 || param->nup->nupY != 1 || param->fitplot) { - xpos = param->page.height - param->page.top; - ypos = param->page.left; + xpos = param->page->height - param->page->top; + ypos = param->page->left; } - int temp = param->page.width; - param->page.width = param->page.height; - param->page.height = temp; + int temp = param->page->width; + param->page->width = param->page->height; + param->page->height = temp; - temp = param->nup.width; - param->nup.width = param->nup.height; - param->nup.height = temp; + temp = param->nup->width; + param->nup->width = param->nup->height; + param->nup->height = temp; } else { - if (param->nup.nupX != 1 || param->nup.nupY != 1 || param->fitplot) + if (param->nup->nupX != 1 || param->nup->nupY != 1 || param->fitplot) { - xpos = param->page.left; - ypos = param->page.bottom; + xpos = param->page->left; + ypos = param->page->bottom; // for whole page... TODO from position... } } - _cfPDFToPDFNupState *nupState; - _cfPDFToPDFNupState_init(nupState, ¶m->nup); - - _cfPDFToPDFNupPageEdit pgedit; + _cfPDFToPDFNupState *nupState = (_cfPDFToPDFNupState *)malloc(sizeof(_cfPDFToPDFNupState)); + _cfPDFToPDFNupState_init(nupState, param->nup); + + _cfPDFToPDFNupPageEdit *pgedit = (_cfPDFToPDFNupPageEdit *)malloc(sizeof(_cfPDFToPDFNupPageEdit)); - for (int iA = 0; iA < numPages; iA++) + for (int iA = 0; iA < numPages; iA ++) { _cfPDFToPDFPageHandle *page; if(shuffle[iA] >= numOrigPages) - page = _cfPDFToPDF_PDFioProcessor_new_page(proc, param->page.width, param->page.height, doc); + { + page = _cfPDFToPDF_PDFioProcessor_new_page(proc, param->page->width, param->page->height, doc); + } else - page = input_page_range_list[shuffle[iA]]; - - _cfPDFToPDFPageRect rect = _cfPDFToPDFPageHandle_get_rect(page); + { + page = input_page_range_list[shuffle[iA]]; + } + _cfPDFToPDFPageRect rect; + _cfPDFToPDFPageRect_init(&rect); + if (!param->pagesize_requested) { - param->page.width = param->page.right = rect.width; - param->page.height = param->page.top = rect.height; + param->page->width = param->page->right = rect.width; + param->page->height = param->page->top = rect.height; } - bool newPage = _cfPDFToPDFNupState_next_page(nupState, rect.width, rect.height, &pgedit); + bool newPage = _cfPDFToPDFNupState_next_page(nupState, + rect.width, rect.height, + pgedit); + if (newPage) { - if((curpage) && (_cfPDFToPDFProcessingParameters_with_page(param, outputpage))) + if ((curpage) && (_cfPDFToPDFProcessingParameters_with_page(param, outputpage))) { - _cfPDFToPDFPageHandle_rotate(curpage, param->orientation); - if (param->mirror) - _cfPDFToPDFPageHandle_mirror(curpage, proc->pdf); - -// _cfPDFToPDF_PDFioProcessor_add_page(proc, curpage, param->reverse); - // Log page in /var/log/cups/page_log - - outputno ++; - if (param->page_logging == 1) - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_CONTROL, - "PAGE: %d %d", outputno, - param->copies_to_be_logged); + outputno ++; + if (_cfPDFToPDFProcessingParameters_even_odd_page(param, outputno)) + { + _cfPDFToPDFPageHandle_rotate(curpage, param->orientation); + if (param->mirror) + _cfPDFToPDFPageHandle_mirror(curpage, proc->pdf); + // TODO? update rect? --- not needed any more + //proc.add_page(curpage, param.reverse); // reverse -> insert at beginning + // Log page in /var/log/cups/page_log + if (param->page_logging == 1) + if (doc->logfunc) doc->logfunc(doc->logdata, + CF_LOGLEVEL_CONTROL, + "PAGE: %d %d", outputno, + param->copies_to_be_logged); + } } - curpage = _cfPDFToPDF_PDFioProcessor_new_page(proc, param->page.width, param->page.height, doc); + curpage = _cfPDFToPDF_PDFioProcessor_new_page(proc, param->page->width, param->page->width, doc); outputpage++; } - if (shuffle[iA] >= numOrigPages) + if(shuffle[iA] >= numOrigPages) continue; if (param->border != NONE) - _cfPDFToPDFPageHandle_add_border_rect(page, proc->pdf, rect, param->border, 1.0 / pgedit.scale); + // TODO FIXME... border gets cutted away, if orignal page had wrong size + // page->"uncrop"(rect); // page->setMedia() + // Note: currently "fixed" in add_subpage(...&rect); + _cfPDFToPDFPageHandle_add_border_rect(page, proc->pdf, rect, param->border, 1.0 / pgedit->scale); if (param->page_label[0] != '\0') { - _cfPDFToPDFPageHandle_add_label(page, proc->pdf, ¶m->page, param->page_label); + _cfPDFToPDFPageHandle_add_label(page, proc->pdf, param->page, param->page_label); } if(param->cropfit) { - if ((param->nup.nupX == 1) && (param->nup.nupY == 1)) + if ((param->nup->nupX == 1) && (param->nup->nupY == 1)) { double xpos2, ypos2; _cfPDFToPDFPageRect get_rect_height = _cfPDFToPDFPageHandle_get_rect(page); _cfPDFToPDFPageRect get_rect_width = _cfPDFToPDFPageHandle_get_rect(page); - if ((param->page.height - param->page.width) * + if ((param->page->height - param->page->width) * (get_rect_height.height - get_rect_width.width) < 0) { - xpos2 = (param->page.width - (get_rect_height.height)) / 2; - ypos2 = (param->page.height - (get_rect_width.width)) / 2; + xpos2 = (param->page->width - (get_rect_height.height)) / 2; + ypos2 = (param->page->height - (get_rect_width.width)) / 2; _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, ypos2 + xpos, xpos2 + ypos, 1, NULL); } else { - xpos2 = (param->page.width - get_rect_width.width) / 2; - ypos2 = (param->page.height - get_rect_height.height) /2; + xpos2 = (param->page->width - get_rect_width.width) / 2; + ypos2 = (param->page->height - get_rect_height.height) /2; _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, xpos2 + xpos, ypos2 + ypos, 1, NULL); } } else { - _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, pgedit.xpos + xpos, pgedit.ypos + ypos, pgedit.scale, NULL); + _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, pgedit->xpos + xpos, pgedit->ypos + ypos, pgedit->scale, NULL); } } else - _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, pgedit.xpos + xpos, pgedit.ypos + ypos, pgedit.scale, NULL); + _cfPDFToPDFPageHandle_add_subpage(curpage, page, proc->pdf, pgedit->xpos + xpos, pgedit->ypos + ypos, pgedit->scale, NULL); #ifdef DEBUG _cfPDFToPDFPageHandle *dbg = (_cfPDFToPDFPageHandle *)curpage; @@ -560,7 +641,6 @@ _cfProcessPDFToPDF( _cfPDFToPDF_PDFioProcessor *proc, } #endif } - if((curpage) && (_cfPDFToPDFProcessingParameters_with_page(param, outputpage))) { _cfPDFToPDFPageHandle_rotate(curpage, param->orientation); @@ -579,5 +659,10 @@ _cfProcessPDFToPDF( _cfPDFToPDF_PDFioProcessor *proc, } _cfPDFToPDF_PDFioProcessor_multiply(proc, param->num_copies, param->collate); + free(pgedit); + free(nupState); + free(shuffle); + free(pages); + free(input_page_range_list); return true; } diff --git a/cupsfilters/pdftopdf/pdftopdf.c b/cupsfilters/pdftopdf/pdftopdf.c index 70918d53..e82a40dc 100644 --- a/cupsfilters/pdftopdf/pdftopdf.c +++ b/cupsfilters/pdftopdf/pdftopdf.c @@ -273,55 +273,58 @@ getParameters(cf_filter_data_t *data, param->pagesize_requested = (cfGetPageDimensions(printer_attrs, job_attrs, num_options, options, NULL, 0, - &(param->page.width), &(param->page.height), - &(param->page.left), &(param->page.bottom), - &(param->page.right), &(param->page.top), + &(param->page->width), &(param->page->height), + &(param->page->left), &(param->page->bottom), + &(param->page->right), &(param->page->top), NULL, NULL) >= 1); - - cfSetPageDimensionsToDefault(&(param->page.width), &(param->page.height), - &(param->page.left), &(param->page.bottom), - &(param->page.right), &(param->page.top), + + cfSetPageDimensionsToDefault(&(param->page->width), &(param->page->height), + &(param->page->left), &(param->page->bottom), + &(param->page->right), &(param->page->top), doc->logfunc, doc->logdata); - param->page.right = param->page.width - param->page.right; - param->page.top = param->page.height - param->page.top; + param->page->right = param->page->width - param->page->right; + param->page->top = param->page->height - param->page->top; - param->paper_is_landscape = (param->page.width > param->page.height); + param->paper_is_landscape = (param->page->width > param->page->height); - _cfPDFToPDFPageRect tmp; // borders (before rotation) + _cfPDFToPDFPageRect *tmp = (_cfPDFToPDFPageRect *)malloc(sizeof(_cfPDFToPDFPageRect)); + _cfPDFToPDFPageRect_init(tmp); - optGetFloat("page-top", num_options, options, &tmp.top); - optGetFloat("page-left", num_options, options, &tmp.left); - optGetFloat("page-right", num_options, options, &tmp.right); - optGetFloat("page-bottom", num_options, options, &tmp.bottom); + optGetFloat("page-top", num_options, options, &tmp->top); + optGetFloat("page-left", num_options, options, &tmp->left); + optGetFloat("page-right", num_options, options, &tmp->right); + optGetFloat("page-bottom", num_options, options, &tmp->bottom); if ((val = cupsGetOption("media-top-margin", num_options, options)) != NULL) - tmp.top = atof(val) * 72.0 / 2540.0; + tmp->top = atof(val) * 72.0 / 2540.0; if ((val = cupsGetOption("media-left-margin", num_options, options)) != NULL) - tmp.left = atof(val) * 72.0 / 2540.0; + tmp->left = atof(val) * 72.0 / 2540.0; if ((val = cupsGetOption("media-right-margin", num_options, options)) != NULL) - tmp.right = atof(val) * 72.0 / 2540.0; + tmp->right = atof(val) * 72.0 / 2540.0; if ((val = cupsGetOption("media-bottom-margin", num_options, options)) != NULL) - tmp.bottom = atof(val) * 72.0 / 2540.0; + tmp->bottom = atof(val) * 72.0 / 2540.0; if ((param->orientation == ROT_90) || (param->orientation == ROT_270)) { // unrotate page // NaN stays NaN - tmp.right = param->page.height - tmp.right; - tmp.top = param->page.width - tmp.top; - _cfPDFToPDFPageRect_rotate_move(&tmp, param->orientation, param->page.height, param->page.width); + tmp->right = param->page->height - tmp->right; + tmp->top = param->page->width - tmp->top; + + _cfPDFToPDFPageRect_rotate_move(tmp, param->orientation, param->page->height, param->page->width); } else { - tmp.right = param->page.width - tmp.right; - tmp.top = param->page.height - tmp.top; - _cfPDFToPDFPageRect_rotate_move(&tmp, param->orientation, param->page.width, param->page.height); + tmp->right = param->page->width - tmp->right; + tmp->top = param->page->height - tmp->top; + + _cfPDFToPDFPageRect_rotate_move(tmp, param->orientation, param->page->width, param->page->height); } - _cfPDFToPDFPageRect_set(¶m->page, &tmp); + _cfPDFToPDFPageRect_set(param->page, tmp); if ((val = cfIPPAttrEnumValForPrinter(printer_attrs, job_attrs, "sides")) != NULL && @@ -359,19 +362,19 @@ getParameters(cf_filter_data_t *data, nup); nup = 1; } - _cfPDFToPDFNupParameters_preset(nup, &(param->nup)); + _cfPDFToPDFNupParameters_preset(nup, param->nup); } if ((val = cupsGetOption("number-up-layout", num_options, options)) != NULL) { - if (!_cfPDFToPDFParseNupLayout(val, &(param->nup))) + if (!_cfPDFToPDFParseNupLayout(val, param->nup)) { if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "cfFilterPDFToPDF: Unsupported number-up-layout %s, using number-up-layout=lrtb!", val); - param->nup.first = X; - param->nup.xstart = LEFT; - param->nup.ystart = TOP; + param->nup->first = X; + param->nup->xstart = LEFT; + param->nup->ystart = TOP; } } @@ -541,7 +544,7 @@ getParameters(cf_filter_data_t *data, if (printer_attrs == NULL && !param->pagesize_requested && param->booklet == CF_PDFTOPDF_BOOKLET_OFF && - param->nup.nupX == 1 && param->nup.nupY == 1) + param->nup->nupX == 1 && param->nup->nupY == 1) param->cropfit = true; else if ((val = cupsGetOption("print-scaling", num_options, options)) != NULL) @@ -593,7 +596,7 @@ getParameters(cf_filter_data_t *data, // when no size got specified by the user. if (param->fitplot || param->fillprint || param->autoprint || param->autofit || param->booklet != CF_PDFTOPDF_BOOKLET_OFF || - param->nup.nupX > 1 || param->nup.nupY > 1) + param->nup->nupX > 1 || param->nup->nupY > 1) param->pagesize_requested = true; // @@ -674,6 +677,7 @@ getParameters(cf_filter_data_t *data, if (param->page_logging == -1) param->page_logging = 0; } + free(tmp); } void @@ -759,67 +763,46 @@ calculate(int num_options, param->even_duplex = false; } -// reads from stdin into temporary file. returns FILE * or NULL on error -FILE * -copy_fd_to_temp(int infd, - pdftopdf_doc_t *doc) +// check whether a given file is empty +bool +is_empty(FILE *f) { - char buf[BUFSIZ]; - int n; - - int outfd = cupsCreateTempFd(NULL, NULL, buf, sizeof(buf)); - if (outfd < 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Can't create temporary file"); - return NULL; - } + char buf[1]; + if (fread(buf, 1, 1, f) == 0) + return true; + rewind(f); + return false; +} - // remove name - unlink(buf); +// coping inputfp data to temp_fp, so that we have a filename, as it is required in pdfioFileOpen API +int +copy_fd_to_tempfile(int inputfd, + FILE *temp_file, + pdftopdf_doc_t *doc) +{ + char buffer[BUFSIZ]; + ssize_t bytes_read, bytes_written; - // copy stdin to the tmp file - while ((n = read(infd, buf, BUFSIZ)) > 0) + while ((bytes_read = read(inputfd, buffer, sizeof(buffer))) > 0) { - if (write(outfd, buf, n) != n) + bytes_written = fwrite(buffer, 1, bytes_read, temp_file); + if (bytes_written != bytes_read) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Can't copy stdin to temporary file"); - close(outfd); - return NULL; + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "write to temporary file failed"); + return -1; } } - - if (lseek(outfd, 0, SEEK_SET) < 0) - { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Can't rewind temporary file"); - close(outfd); - return NULL; - } - FILE *f; - if ((f = fdopen(outfd, "rb")) == 0) + if (bytes_read == -1) { - if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, - "cfFilterPDFToPDF: Can't fdopen temporary file"); - close(outfd); - return NULL; - } - return f; -} + if (doc->logfunc) doc->logfunc(doc->logdata, CF_LOGLEVEL_ERROR, "Read from inputfd failed"); + return -1; + } -// check whether a given file is empty -bool -is_empty(FILE *f) -{ - char buf[1]; - if (fread(buf, 1, 1, f) == 0) - return true; - rewind(f); - return false; + return 0; } + int cfFilterPDFToPDF(int inputfd, int outputfd, @@ -827,6 +810,7 @@ cfFilterPDFToPDF(int inputfd, cf_filter_data_t *data, void *parameters) { + pdftopdf_doc_t doc; char *final_content_type = data->final_content_type; FILE *inputfp, @@ -842,16 +826,20 @@ cfFilterPDFToPDF(int inputfd, int num_options = 0; cups_option_t *options = NULL; - _cfPDFToPDFProcessingParameters param; + _cfPDFToPDFProcessingParameters *param = (_cfPDFToPDFProcessingParameters *)malloc(sizeof(_cfPDFToPDFProcessingParameters)); + _cfPDFToPDFProcessingParameters_init(param); - param.job_id = data->job_id; - param.user = data->job_user; - param.title = data->job_title; - param.num_copies = data->copies; - param.copies_to_be_logged = data->copies; - param.page.width = param.page.height = 0; - param.page.left = param.page.bottom = -1; - param.page.right = param.page.top = -1; + + fprintf(stderr, "_cfPDFToPDFProcessingParameters_init has been done\n"); + + param->job_id = data->job_id; + param->user = data->job_user; + param->title = data->job_title; + param->num_copies = data->copies; + param->copies_to_be_logged = data->copies; + param->page->width = param->page->height = 0; + param->page->left = param->page->bottom = -1; + param->page->right = param->page->top = -1; doc.logfunc = log; doc.logdata = ld; @@ -860,11 +848,11 @@ cfFilterPDFToPDF(int inputfd, num_options = cfJoinJobOptionsAndAttrs(data, num_options, &options); - getParameters(data, num_options, options, ¶m, &doc); - calculate(num_options, options, ¶m, final_content_type); + getParameters(data, num_options, options, param, &doc); + calculate(num_options, options, param, final_content_type); #ifdef DEBUG - _cfPDFToPDFProcessingParameters_dump(¶m, &doc); + _cfPDFToPDFProcessingParameters_dump(param, &doc); #endif // If we are in streaming mode we only apply JCL and do not run the @@ -884,53 +872,66 @@ cfFilterPDFToPDF(int inputfd, _cfPDFToPDF_PDFioProcessor proc; - if ((inputseekable && inputfd > 0) || streaming) - { - if ((inputfp = fdopen(inputfd, "rb")) == NULL) - return 1; - } - else + char temp_filename[] = "/tmp/tempfileXXXXXX"; + int temp_fd = mkstemp(temp_filename); + if (temp_fd == -1) { - if ((inputfp = copy_fd_to_temp(inputfd, &doc)) == NULL) - return 1; + if (log) log(ld, CF_LOGLEVEL_ERROR, "tempfilename wasn't created"); + return 1; } - char temp_filename[] = "/tmp/pdfio_temp_XXXXXX"; - int temp_fd = mkstemp(temp_filename); // Create a unique temporary file - FILE *temp_f = fdopen(temp_fd, "wb"); - char buffer[8192]; - size_t bytes_read; - while ((bytes_read = fread(buffer, 1, sizeof(buffer), inputfp)) > 0) { - fwrite(buffer, 1, bytes_read, temp_f); + // Convert the temp_fd to a FILE* stream + inputfp = fdopen(temp_fd, "wb+"); + if (!inputfp) { + if (log) log(ld, CF_LOGLEVEL_ERROR, "Couldn't convert temp_fd to FILE* stream"); + close(temp_fd); + close(inputfd); + unlink(temp_filename); + return 1; } - - + if (copy_fd_to_tempfile(inputfd, inputfp, &doc) == -1) { + fprintf(stderr, "Failed to copy inputfd to temp file\n"); + fclose(inputfp); + close(inputfd); + unlink(temp_filename); // Clean up temporary file + return 1; + } + + rewind(inputfp); // Rewind the temp_file for reading + if (!streaming) { if (is_empty(inputfp)) { fclose(inputfp); - fclose(temp_f); // This closes temp_fd + close(inputfd); + unlink(temp_filename); if (log) log(ld, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: Input is empty, outputting empty file."); return 0; } - + + //test if (log) log(ld, CF_LOGLEVEL_DEBUG, - "cfFilterPDFToPDF: Processing PDF input with PDFio: Page-ranges, page-set, number-up, booklet, size adjustment, ..."); + " cfFilterPDFToPDF: Processing PDF input with PDFio: Page-ranges, page-set, number-up, booklet, size adjustment, ..."); // Load the PDF input data into PDFio if (!_cfPDFToPDF_PDFioProcessor_load_filename(&proc, temp_filename, &doc, 1)) { + if (log) log(ld, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: error in _cfPDFToPDF_PDFioProcessor_load_filename"); fclose(inputfp); - fclose(temp_f); // This closes temp_fd + close(inputfd); + unlink(temp_filename); return 1; } // Process the PDF input data - if (!_cfProcessPDFToPDF(&proc, ¶m, &doc)) + if (!_cfProcessPDFToPDF(&proc, param, &doc)) + { + if (log) log(ld, CF_LOGLEVEL_DEBUG, "cfFilterPDFToPDF: error in _cfProcessPDFToPDF"); return 2; + } // Pass information to subsequent filters via PDF comments char *output[10]; @@ -938,13 +939,13 @@ cfFilterPDFToPDF(int inputfd, output[output_len++] = "% This file was generated by pdftopdf"; - if (param.device_copies > 0) + if (param->device_copies > 0) { char buf[256]; - snprintf(buf, sizeof(buf), "%d", param.device_copies); + snprintf(buf, sizeof(buf), "%d", param->device_copies); output[output_len++] = strdup(buf); - if (param.device_collate) + if (param->device_collate) output[output_len++] = "%%PDFTOPDFCollate : true"; else output[output_len++] = "%%PDFTOPDFCollate : false"; @@ -953,13 +954,25 @@ cfFilterPDFToPDF(int inputfd, _cfPDFToPDF_PDFioProcessor_set_comments(&proc, output, output_len); } - outputfp = fdopen(outputfd, "w"); - if (outputfp == NULL) + char temp_output_filename[] = "/tmp/outputfilenameXXXXXX"; + + int temp_output_fd = mkstemp(temp_output_filename); + if (temp_output_fd == -1) { + if (log) log(ld, CF_LOGLEVEL_ERROR, "Failed to create temporary output file"); + return 1; + } + + outputfp = fdopen(temp_output_fd, "wb+"); + if (!outputfp) { + if (log) log(ld, CF_LOGLEVEL_ERROR, "Failed to open temporary output file stream"); + close(temp_output_fd); + unlink(temp_output_filename); return 1; + } if (!streaming) { - _cfPDFToPDF_PDFioProcessor_emit_file(&proc, outputfp, &doc, CF_PDFTOPDF_WILL_STAY_ALIVE); + _cfPDFToPDF_PDFioProcessor_emit_filename(&proc, temp_output_filename, &doc); } else { @@ -969,10 +982,33 @@ cfFilterPDFToPDF(int inputfd, if (fwrite(buf, 1, bytes, outputfp) != bytes) break; fclose(inputfp); - fclose(temp_f); // This closes temp_fd + close(inputfd); + unlink(temp_filename); + } + + fflush(outputfp); + rewind(outputfp); + char buffer[8192]; + ssize_t bytes_read; + while ((bytes_read = read(temp_output_fd, buffer, sizeof(buffer))) > 0) + { + if (write(outputfd, buffer, bytes_read) != bytes_read) { + if (log) log(ld, CF_LOGLEVEL_ERROR, "Failed to write to output_fd"); + fclose(outputfp); + close(outputfd); + unlink(temp_output_filename); + return 1; } +} + +// free Param + _cfPDFToPDFProcessingParameters_free(param); +// Clean up the temporary file + unlink(temp_output_filename); fclose(outputfp); + close(inputfd); + unlink(temp_filename); return 0; } // }}} diff --git a/cupsfilters/pdftopdf/pptypes.c b/cupsfilters/pdftopdf/pptypes.c index 2900345f..5bd497f9 100644 --- a/cupsfilters/pdftopdf/pptypes.c +++ b/cupsfilters/pdftopdf/pptypes.c @@ -134,11 +134,11 @@ _cfPDFToPDFPageRect_init(_cfPDFToPDFPageRect *rect) // {{{ // {{{ void -swap_float(float *a, float *b) // {{{ +swap_float(float a, float b) // {{{ { - float temp = *a; - *a = *b; - *b = temp; + float temp = a; + a = b; + b = temp; } // }}} @@ -150,8 +150,8 @@ _cfPDFToPDFPageRect_rotate_move(_cfPDFToPDFPageRect *rect, #if 1 if (r >= ROT_180) { - swap_float(&rect->top, &rect->bottom); - swap_float(&rect->left, &rect->right); + swap_float(rect->top, rect->bottom); + swap_float(rect->left, rect->right); } if ((r == ROT_90) || (r == ROT_270)) @@ -162,8 +162,8 @@ _cfPDFToPDFPageRect_rotate_move(_cfPDFToPDFPageRect *rect, rect->top = rect->right; rect->right = tmp; - swap_float(&rect->width, &rect->height); - swap_float(&pwidth, &pheight); + swap_float(rect->width, rect->height); + swap_float(pwidth, pheight); } if ((r == ROT_90) || (r == ROT_180)) diff --git a/cupsfilters/pdftopdf/processor.h b/cupsfilters/pdftopdf/processor.h index c8b77b22..3d897a91 100644 --- a/cupsfilters/pdftopdf/processor.h +++ b/cupsfilters/pdftopdf/processor.h @@ -144,7 +144,7 @@ bool _cfPDFToPDF_PDFioProcessor_check_print_permissions(_cfPDFToPDF_PDFioProcess _cfPDFToPDFPageHandle** _cfPDFToPDF_PDFioProcessor_get_pages(_cfPDFToPDF_PDFioProcessor *handle, pdftopdf_doc_t *doc, - int *out_len); + size_t *out_len); _cfPDFToPDFPageHandle* _cfPDFToPDF_PDFioProcessor_new_page(_cfPDFToPDF_PDFioProcessor *handle, float width, float height, @@ -193,12 +193,12 @@ typedef struct { bool autofit; // print-scaling = auto-fit bool fidelity; bool no_orientation; - _cfPDFToPDFPageRect page; + _cfPDFToPDFPageRect *page; pdftopdf_rotation_e orientation, normal_landscape; // normal_landscape (i.e. default direction) is e.g. needed for number-up=2 bool paper_is_landscape; bool duplex; pdftopdf_border_type_e border; - _cfPDFToPDFNupParameters nup; + _cfPDFToPDFNupParameters *nup; bool reverse; char *page_label; @@ -234,6 +234,11 @@ typedef struct { void _cfPDFToPDFProcessingParameters_init(_cfPDFToPDFProcessingParameters *processingParams); +void _cfPDFToPDFProcessingParameters_free(_cfPDFToPDFProcessingParameters *processingParams); + +bool _cfPDFToPDFProcessingParameters_even_odd_page(const _cfPDFToPDFProcessingParameters *self, + int outno); + bool _cfPDFToPDFProcessingParameters_with_page(const _cfPDFToPDFProcessingParameters *self, int outno); diff --git a/cupsfilters/test-filter-cases.txt b/cupsfilters/test-filter-cases.txt index ba045aac..3f06a571 100644 --- a/cupsfilters/test-filter-cases.txt +++ b/cupsfilters/test-filter-cases.txt @@ -1,5 +1,3 @@ # Input_File Input_Type Output_File Output_Type Make Model Color Duplex Formats Job-Id: random number User: randome name Title: randome title Copies: range between 1 to 20 Options cupsfilters/test_files/test_file_1pg.pdf application/pdf cupsfilters/test_files/output_files/test_file_op.pdf application/pdf Generic PDF Color 2 1 1 text/plain,application/pdf 13 new-user custom-print 10 sides=two-sided-long-edge media-size=A4 printer-resolution=300dpi cupsfilters/test_files/test_file_4pg.pdf application/pdf cupsfilters/test_files/output_files/test_file_op.pwg image/pwg-raster Generic PDF Color 2 1 1 image/pwg-raster,application/pdf 13 new-user custom-print 5 sides=two-sided-short-edge media-size=A4 printer-resolution=300dpi -cupsfilters/test_files/bashrc.urf image/urf cupsfilters/test_files/output_files/test_file_op.jpg image/jpeg Canon GX7000 series 2 1 0 image/urf,image/jpeg 13 new-user custom-print 1 sides=two-sided-short-edge media-size=A4 printer-resolution=600dpi -cupsfilters/test_files/test_file_2pg.pdf application/pdf cupsfilters/test_files/output_files/test_file_op.jpg image/jpeg Brother MFC-L6900DW 2 1 0 application/pdf,application/vnd.cups-pdf 13 new-user custom-print 1 sides=two-sided-short-edge media-size=A4 printer-resolution=300dpi \ No newline at end of file From b809c15746d6ba311febd6674485449ed374316d Mon Sep 17 00:00:00 2001 From: Uddhav Phatak Date: Fri, 31 Jan 2025 02:15:50 +0530 Subject: [PATCH 64/64] removed gibberish debugging messages from cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c. Added the poppler-free version for pdftoraster.cxx, which uses pdftoppm to output for raster, added ability to select for manual filter chain choice. --- .../pdftopdf/pdfio-pdftopdf-processor.c | 1 - cupsfilters/pdftoraster.c | 267 ++++++++++++++++++ cupsfilters/test-filter-cases.txt | 9 +- cupsfilters/testfilters.c | 120 +++++++- 4 files changed, 391 insertions(+), 6 deletions(-) create mode 100644 cupsfilters/pdftoraster.c diff --git a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c index cdfd178f..bc145958 100644 --- a/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c +++ b/cupsfilters/pdftopdf/pdfio-pdftopdf-processor.c @@ -781,7 +781,6 @@ void _cfPDFToPDF_PDFioProcessor_close_file(_cfPDFToPDF_PDFioProcessor *handle) { if (handle->pdf != NULL) { - fprintf(stderr, "maa chudi padi hai\n"); pdfioFileClose(handle->pdf); handle->pdf = NULL; } diff --git a/cupsfilters/pdftoraster.c b/cupsfilters/pdftoraster.c new file mode 100644 index 00000000..4f9db94a --- /dev/null +++ b/cupsfilters/pdftoraster.c @@ -0,0 +1,267 @@ +// +// PDF to Raster filter function for libcupsfilters. +// +// Copyright (c) 2025 by Uddhav Phatak +// +// Licensed under Apache License v2.0. See the file "LICENSE" for more +// information. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TEMP_PREFIX "/tmp/pdftoraster-XXXXXX" + +typedef struct { + char *input_filename; + char *temp_base; + cf_logfunc_t log; + void *logdata; + cmsHPROFILE dst_profile; +} pdftoraster_doc_t; + +static void +cleanup_temp_files(pdftoraster_doc_t *doc) +{ + if (!doc || !doc->temp_base) return; + + char cmd[2048]; + snprintf(cmd, sizeof(cmd), "rm -rf %s* 2>/dev/null", doc->temp_base); + int rc = system(cmd); + (void)rc; +} + +static int +get_pdf_page_count(const char *filename, + cf_logfunc_t log, + void *ld) +{ + pdfio_file_t *pdf = pdfioFileOpen(filename, NULL, NULL, NULL, NULL); + + int pages = pdfioFileGetNumPages(pdf); + pdfioFileClose(pdf); + return pages; +} + +static int +render_page_to_ppm(pdftoraster_doc_t *doc, + int pagenum, + int resolution) +{ + char temp_ppm[1024]; + snprintf(temp_ppm, sizeof(temp_ppm), "%s-%06d.ppm", doc->temp_base, pagenum); + + const char *args[] = { + "pdftoppm", + "-r", NULL, + "-f", NULL, + "-l", NULL, + "-cropbox", + "-singlefile", + doc->input_filename, + temp_ppm, + NULL + }; + + char res_str[16], page_str[16]; + snprintf(res_str, sizeof(res_str), "%d", resolution); + snprintf(page_str, sizeof(page_str), "%d", pagenum); + + args[2] = res_str; + args[4] = page_str; + args[5] = page_str; + + pid_t pid = fork(); + if (pid == 0) { + execvp(args[0], (char *const *)args); + exit(EXIT_FAILURE); + } + + int status; + waitpid(pid, &status, 0); + return WIFEXITED(status) && WEXITSTATUS(status) == 0 ? 0 : -1; +} + +static int +convert_ppm_to_raster(pdftoraster_doc_t *doc, + const char *ppm_path, + cups_raster_t *raster, + cups_page_header2_t *header) +{ + cf_image_t *img = NULL; + unsigned char *line = NULL; + cmsHTRANSFORM transform = NULL; + cmsHPROFILE src_profile = NULL; + int ret = -1; + + img = cfImageOpen(ppm_path, + CUPS_CSPACE_RGB, // Primary color space + CUPS_CSPACE_W, // Secondary color space + 100, // Saturation + 0, // Hue + NULL); // LUT + + if (!img) + { + doc->log(doc->logdata, CF_LOGLEVEL_ERROR, "Failed to open PPM image"); + goto out; + } + + src_profile = cmsCreate_sRGBProfile(); + if (!doc->dst_profile) { + doc->dst_profile = cmsCreate_sRGBProfile(); + } + + transform = cmsCreateTransform(src_profile, TYPE_RGB_8, + doc->dst_profile, TYPE_CMYK_8, + INTENT_PERCEPTUAL, 0); + + line = malloc(header->cupsBytesPerLine); + if (!line) { + doc->log(doc->logdata, CF_LOGLEVEL_ERROR, "Memory allocation failed"); + goto out; + } + + for (unsigned y = 0; y < header->cupsHeight; y++) { + unsigned char pixels[header->cupsWidth * 3]; + + if (!cfImageGetRow(img, 0, y, header->cupsWidth, pixels)) { + doc->log(doc->logdata, CF_LOGLEVEL_ERROR, "Failed to get image row"); + goto out; + } + + cmsDoTransform(transform, pixels, line, header->cupsWidth); + + if (!cupsRasterWritePixels(raster, line, header->cupsBytesPerLine)) { + doc->log(doc->logdata, CF_LOGLEVEL_ERROR, "Raster write failed"); + goto out; + } + } + + ret = 0; + +out: + free(line); + if (transform) cmsDeleteTransform(transform); + if (src_profile) cmsCloseProfile(src_profile); + if (img) cfImageClose(img); + return ret; +} + +int +cfFilterPDFToRaster(int inputfd, + int outputfd, + int inputseekable, + cf_filter_data_t *data, + void *parameters) +{ + pdftoraster_doc_t doc = {0}; + cups_raster_t *raster = NULL; + cups_page_header2_t header; + int ret = 1; + char temp_input[1024]; + int fd = -1; + + doc.log = data->logfunc; + doc.logdata = data->logdata; + + strncpy(temp_input, TEMP_PREFIX, sizeof(temp_input)); + if ((fd = mkstemp(temp_input)) == -1) { + doc.log(doc.logdata, CF_LOGLEVEL_ERROR, + "Temp file creation failed: %s", strerror(errno)); + goto cleanup; + } + + char buf[8192]; + ssize_t bytes; + while ((bytes = read(inputfd, buf, sizeof(buf))) > 0) { + if (write(fd, buf, bytes) != bytes) { + doc.log(doc.logdata, CF_LOGLEVEL_ERROR, + "Write error: %s", strerror(errno)); + goto cleanup; + } + } + close(fd); + fd = -1; + doc.input_filename = temp_input; + + doc.temp_base = strdup(TEMP_PREFIX); + if (!mkdtemp(doc.temp_base)) { + doc.log(doc.logdata, CF_LOGLEVEL_ERROR, + "Temp directory creation failed: %s", strerror(errno)); + goto cleanup; + } + + int npages = get_pdf_page_count(doc.input_filename, doc.log, doc.logdata); + if (npages < 1) { + doc.log(doc.logdata, CF_LOGLEVEL_ERROR, "Invalid page count"); + goto cleanup; + } + + cf_filter_out_format_t outformat = CF_FILTER_OUT_FORMAT_CUPS_RASTER; + if (data->final_content_type) { + if (strcasestr(data->final_content_type, "pwg")) { + outformat = CF_FILTER_OUT_FORMAT_PWG_RASTER; + } + else if (strcasestr(data->final_content_type, "urf")) { + outformat = CF_FILTER_OUT_FORMAT_APPLE_RASTER; + } + } + + memset(&header, 0, sizeof(header)); + if (!cfRasterPrepareHeader(&header, data, outformat, + (outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER || + outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER) ? + outformat : CF_FILTER_OUT_FORMAT_CUPS_RASTER, + 0, NULL)) { + doc.log(doc.logdata, CF_LOGLEVEL_ERROR, "Header preparation failed"); + goto cleanup; + } + + raster = cupsRasterOpen(outputfd, + outformat == CF_FILTER_OUT_FORMAT_PWG_RASTER ? CUPS_RASTER_WRITE_PWG : + outformat == CF_FILTER_OUT_FORMAT_APPLE_RASTER ? CUPS_RASTER_WRITE_APPLE : + CUPS_RASTER_WRITE); + + if (!raster) { + doc.log(doc.logdata, CF_LOGLEVEL_ERROR, "Raster output failed"); + goto cleanup; + } + + for (int page = 1; page <= npages; page++) + { + char ppm_path[1024]; + snprintf(ppm_path, sizeof(ppm_path), "%s-%06d.ppm", doc.temp_base, page); + + if (render_page_to_ppm(&doc, page, header.HWResolution[0]) || + convert_ppm_to_raster(&doc, ppm_path, raster, &header)) { + doc.log(doc.logdata, CF_LOGLEVEL_ERROR, + "Page %d conversion failed", page); + goto cleanup; + } + unlink(ppm_path); + } + + ret = 0; + +cleanup: + if (fd != -1) close(fd); + if (raster) cupsRasterClose(raster); + if (doc.input_filename) unlink(doc.input_filename); + cleanup_temp_files(&doc); + free(doc.temp_base); + return ret; +} diff --git a/cupsfilters/test-filter-cases.txt b/cupsfilters/test-filter-cases.txt index 3f06a571..f45a21ab 100644 --- a/cupsfilters/test-filter-cases.txt +++ b/cupsfilters/test-filter-cases.txt @@ -1,3 +1,6 @@ -# Input_File Input_Type Output_File Output_Type Make Model Color Duplex Formats Job-Id: random number User: randome name Title: randome title Copies: range between 1 to 20 Options -cupsfilters/test_files/test_file_1pg.pdf application/pdf cupsfilters/test_files/output_files/test_file_op.pdf application/pdf Generic PDF Color 2 1 1 text/plain,application/pdf 13 new-user custom-print 10 sides=two-sided-long-edge media-size=A4 printer-resolution=300dpi -cupsfilters/test_files/test_file_4pg.pdf application/pdf cupsfilters/test_files/output_files/test_file_op.pwg image/pwg-raster Generic PDF Color 2 1 1 image/pwg-raster,application/pdf 13 new-user custom-print 5 sides=two-sided-short-edge media-size=A4 printer-resolution=300dpi +# Columns: Input_File Input_Type Output_File Output_Type Make Model Color Duplex Formats Filter_Chain Job-Id User Title Copies Options +cupsfilters/test_files/test_file_1pg.pdf application/pdf cupsfilters/test_files/output_files/test_file_op.pdf application/pdf Generic PDF Color 2 1 text/plain,application/pdf pdftopdf 13 new-user custom-print 10 sides=two-sided-long-edge media-size=A4 printer-resolution=300dpi +cupsfilters/test_files/test_file_4pg.pdf application/pdf cupsfilters/test_files/output_files/test_file_op.pwg image/pwg-raster Generic PDF Color 2 1 image/pwg-raster,application/pdf pdftopdf,pwgtopwg 13 new-user custom-print 5 sides=two-sided-short-edge media-size=A4 printer-resolution=300dpi +cupsfilters/test_files/bashrc.urf image/urf cupsfilters/test_files/output_files/test_file_urfpdf.pdf application/pdf Canon GX7000 series 2 1 image/urf,application/pdf pwgtopdf 13 new-user custom-print 1 sides=two-sided-short-edge media-size=A4 printer-resolution=600dpi +cupsfilters/test_files/test_file_2pg.pdf application/pdf cupsfilters/test_files/output_files/test_file_op.jpg image/jpeg Brother MFC-L6900DW 2 1 application/pdf,image/jpeg pdftopdf 13 new-user custom-print 1 sides=two-sided-short-edge media-size=A4 printer-resolution=300dpi + diff --git a/cupsfilters/testfilters.c b/cupsfilters/testfilters.c index 9685bb80..c6076011 100644 --- a/cupsfilters/testfilters.c +++ b/cupsfilters/testfilters.c @@ -12,6 +12,8 @@ * 'remove_white_space()' - Remove white spaces from beginning and end of a string */ +typedef int (*cf_filter_func_t)(int input_fd, int output_fd, int input_seekable, cf_filter_data_t *data, void *parameters); + char* remove_white_space( char* str) @@ -31,6 +33,69 @@ remove_white_space( return str; } +typedef struct { + const char *name; + cf_filter_func_t function; // Correct type + void *(*param_generator)(const char *output_mime); +} FilterMapping; + +void +*ghostscript_param_gen(const char *output_mime) +{ + cf_filter_out_format_t *out = malloc(sizeof(cf_filter_out_format_t)); + if (strcasecmp(output_mime, "application/pdf") == 0) { + *out = CF_FILTER_OUT_FORMAT_PDF; + } else if (strcasecmp(output_mime, "image/pwg-raster") == 0) { + *out = CF_FILTER_OUT_FORMAT_PWG_RASTER; + } else if (strcasecmp(output_mime, "image/urf") == 0) { + *out = CF_FILTER_OUT_FORMAT_APPLE_RASTER; + } else if (strcasecmp(output_mime, "application/PCLm") == 0) { + *out = CF_FILTER_OUT_FORMAT_PCLM; + } else { + free(out); + return NULL; + } + return out; +} + +// Define the filter mappings +FilterMapping filter_mappings[] = { + { "imagetoraster", cfFilterImageToRaster, NULL }, + { "ghostscript", cfFilterGhostscript, ghostscript_param_gen }, + { "rastertopwg", cfFilterRasterToPWG, NULL }, + { "pwgtopdf", cfFilterPWGToPDF, NULL }, + { "pdftopdf", cfFilterPDFToPDF, NULL }, + { "texttopdf", cfFilterTextToPDF, NULL }, +}; + +cups_array_t* +parse_filter_chain(const char *filter_chain_str, + const char *output_mime) +{ + cups_array_t *chain = cupsArrayNew(NULL, NULL); + char *saveptr; + char *filter_name = strtok_r((char *)filter_chain_str, ",", &saveptr); + while (filter_name) + { + filter_name = remove_white_space(filter_name); + for (size_t i = 0; i < sizeof(filter_mappings)/sizeof(filter_mappings[0]); i++) + { + if (strcasecmp(filter_name, filter_mappings[i].name) == 0) + { + cf_filter_filter_in_chain_t *filter = malloc(sizeof(cf_filter_filter_in_chain_t)); + filter->function = filter_mappings[i].function; + filter->name = (char *)filter_mappings[i].name; // Explicit cast here + filter->parameters = filter_mappings[i].param_generator ? filter_mappings[i].param_generator(output_mime) : NULL; + cupsArrayAdd(chain, filter); + break; + } + } + + filter_name = strtok_r(NULL, ",", &saveptr); + } + return chain; +} + /* * 'create_media_size_range()' - Create a ranged media-size value. */ @@ -126,6 +191,8 @@ create_media_size(int width, /* I - x-dimension in 2540ths */ * */ + +/* int // O - Exit status test_wrapper( int num_clargs, // I - Number of command-line args @@ -137,6 +204,21 @@ test_wrapper( char* outputMIME, char* inputFile, char* outputFile) +{ +*/ + +int +test_wrapper( + int num_clargs, + char *clargs[], + void *parameters, + int *JobCanceled, + ipp_t* emulated_ipp, + char* inputMIME, + char* outputMIME, + char* inputFile, + char* outputFile, + cups_array_t *filter_chain) { int inputfd; // Print file descriptor int outputfd; // File Descriptor for Output File @@ -261,7 +343,22 @@ test_wrapper( // Fire up the filter function (output to stdout, file descriptor 1) // - retval = cfFilterUniversal(inputfd, outputfd, inputseekable, &filter_data, parameters); +// retval = cfFilterUniversal(inputfd, outputfd, inputseekable, &filter_data, parameters); + + if (filter_chain && cupsArrayCount(filter_chain) > 0) { + retval = cfFilterChain(inputfd, outputfd, inputseekable, &filter_data, filter_chain); + cf_filter_filter_in_chain_t *filter; + while ((filter = cupsArrayFirst(filter_chain)) != NULL) { + free(filter->parameters); + free(filter); + cupsArrayRemove(filter_chain, filter); + } + cupsArrayDelete(filter_chain); + } else { + retval = cfFilterUniversal(inputfd, outputfd, inputseekable, &filter_data, parameters); + } + + return retval; } @@ -1012,6 +1109,10 @@ run_test( char * test_case, char * currentFile) { + + cups_array_t *filter_chain = NULL; + char *filter_chain_str = NULL; + char* make = (char*) malloc(100 * sizeof(char*)); char* model = (char*) malloc(100 * sizeof(char*)); @@ -1121,6 +1222,14 @@ run_test( } continue; } + else if (globalFlag == 9) + { + filter_chain_str = token; + filter_chain = parse_filter_chain(filter_chain_str, outputContentType); + globalFlag++; + } + + clargs = realloc(clargs, (token_index+1)*sizeof(char*)); char* tmp_token = (char*)malloc(100*sizeof(char*)); @@ -1131,7 +1240,14 @@ run_test( } ipp_t* emulated_ipp = load_legacy_attributes(make, model, ppm, ppm_color, duplex, docformats); - return test_wrapper(token_index, clargs, NULL, &jobCanceled, emulated_ipp, inputContentType, outputContentType, inputFileName, outputFileName); +// return test_wrapper(token_index, clargs, NULL, &jobCanceled, emulated_ipp, inputContentType, outputContentType, inputFileName, outputFileName); + + return test_wrapper(token_index, clargs, NULL, &jobCanceled, emulated_ipp, + inputContentType, outputContentType, + inputFileName, // Fixed variable name + outputFileName, // Fixed variable name + filter_chain); + } int main(int argc, // I - Number of command-line args