diff --git a/Makefile.am b/Makefile.am index fc995c10f..93b7bf845 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 000000000..a7b0d0239 --- /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); +}