/*
* Copyright (C) 2016 The Android Open Source Project
* Copyright (C) 2016 Mopria Alliance, Inc.
* Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "../../media.h"
#include <PCLmGenerator.h>
#include <assert.h>
#include <math.h>
#include <zlib.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <genPCLm.h>
#define TAG "genPCLm"
#define STRIP_HEIGHT 16
#define JPEG_QUALITY 100
#define TEMP_BUFF_SIZE 10000000
#define DEFAULT_OUTBUFF_SIZE 64*5120*3*10
#define STANDARD_SCALE_FOR_PDF 72.0
#define KID_STRING_SIZE 1000
#define CATALOG_OBJ_NUMBER 1
#define PAGES_OBJ_NUMBER 2
#define ADOBE_RGB_SIZE 284
#define rgb_2_gray(r, g, b) (ubyte)(0.299*(double)r+0.587*(double)g+0.114*(double)b)
static PCLmSUserSettingsType PCLmSSettings;
/*
* Shift the strip image right in the strip buffer by leftMargin pixels.
*
* Assumptions: The strip buffer was allocated large enough to handle the shift; if not
* then the image data on the right will get clipped.
*
* We allocate a full strip (height and width), but then only copy numLinesThisCall from
* the original buffer to the newly allocated buffer. This pads the strips for JPEG processing.
*/
static ubyte *shiftStripByLeftMargin(ubyte *ptrToStrip, sint32 currSourceWidth,
sint32 currStripHeight, sint32 numLinesThisCall, sint32 currMediaWidth, sint32 leftMargin,
colorSpaceDisposition destColorSpace) {
ubyte *fromPtr, *toPtr, *newStrip;
sint32 scanLineWidth;
if (destColorSpace == grayScale) {
scanLineWidth = currMediaWidth;
// Allocate a full strip
newStrip = (ubyte *) malloc(scanLineWidth * currStripHeight);
memset(newStrip, 0xff, scanLineWidth * currStripHeight);
for (int i = 0; i < numLinesThisCall; i++) {
toPtr = newStrip + leftMargin + (i * currMediaWidth);
fromPtr = ptrToStrip + (i * currSourceWidth);
memcpy(toPtr, fromPtr, currSourceWidth);
}
} else {
scanLineWidth = currMediaWidth * 3;
sint32 srcScanlineWidth = currSourceWidth * 3;
sint32 shiftAmount = leftMargin * 3;
newStrip = (ubyte *) malloc(scanLineWidth * currStripHeight);
memset(newStrip, 0xff, scanLineWidth * currStripHeight);
for (int i = 0; i < numLinesThisCall; i++) {
toPtr = newStrip + shiftAmount + (i * scanLineWidth);
fromPtr = ptrToStrip + (i * srcScanlineWidth);
memcpy(toPtr, fromPtr, srcScanlineWidth);
}
}
return newStrip;
}
#ifdef SUPPORT_WHITE_STRIPS
bool PCLmGenerator::isWhiteStrip(void *pInBuffer, int inBufferSize) {
uint32 *ptr = (uint32 *) pInBuffer;
for (int i = 0; i < inBufferSize / 4; i++, ptr++) {
if (*ptr != 0xffffffff) {
return false;
}
}
return true;
}
#endif
void PCLmGenerator::Cleanup(void) {
if (allocatedOutputBuffer) {
free(allocatedOutputBuffer);
allocatedOutputBuffer = NULL;
currOutBuffSize = 0;
}
if (leftoverScanlineBuffer) {
free(leftoverScanlineBuffer);
leftoverScanlineBuffer = NULL;
}
if (scratchBuffer) {
free(scratchBuffer);
scratchBuffer = NULL;
}
if (xRefTable) {
free(xRefTable);
xRefTable = NULL;
}
if (KidsArray) {
free(KidsArray);
KidsArray = NULL;
}
}
int PCLmGenerator::errorOutAndCleanUp() {
Cleanup();
jobOpen = job_errored;
return genericFailure;
}
static sint32 startXRef = 0;
static sint32 endXRef = 0;
/*
* DO NOT EDIT UNTIL YOU READ THE HEADER FILE DESCRIPTION.
*/
void PCLmGenerator::fixXRef() {
if (!startXRef || !mirrorBackside) {
return;
}
if (currDuplexDisposition == duplex_longEdge && !(pageCount % 2) && mirrorBackside) {
assert(startXRef);
sint32 start = startXRef;
sint32 end = endXRef - 1;
sint32 aSize = endXRef - startXRef - 1;
sint32 *tmpArray = (sint32 *) malloc(aSize * 20);
sint32 xRefI = startXRef;
for (int i = 0; i < aSize + 1; i++, xRefI++) {
*(tmpArray + i) = xRefTable[xRefI + 1] - xRefTable[xRefI];
}
// Reorder header and image sizes
for (int i = 0; i < aSize + 1; i += 2, xRefI++) {
sint32 t = *(tmpArray + i);
*(tmpArray + i) = *(tmpArray + i + 1);
*(tmpArray + i + 1) = t;
}
xRefI = aSize;
for (int i = start + 1, j = aSize; i < end + 2; i++, start++, xRefI--, j--) {
xRefTable[i] = (xRefTable[i - 1] + *(tmpArray + j));
}
for (int i = startXRef + 2; i < endXRef; i++) {
xRefTable[i] += 2;
}
sint32 k = endXRef - 1;
int i;
sint32 lSize = (endXRef - startXRef) / 2;
for (i = startXRef; i < startXRef + lSize; i++, k--) {
sint32 t = xRefTable[i];
xRefTable[i] = xRefTable[k];
xRefTable[k] = t;
}
free(tmpArray);
}
startXRef = 0;
}
bool PCLmGenerator::addXRef(sint32 xRefObj) {
#define XREF_ARRAY_SIZE 100
if (!xRefTable) {
xRefTable = (sint32 *) malloc(XREF_ARRAY_SIZE * sizeof(sint32));
assert(xRefTable);
xRefTable[0] = 0;
xRefIndex++;
}
xRefTable[xRefIndex] = xRefObj;
xRefIndex++;
if (!(xRefIndex % XREF_ARRAY_SIZE)) {
xRefTable = (sint32 *) realloc(xRefTable, (((xRefIndex + XREF_ARRAY_SIZE) *
sizeof(sint32))));
}
return true;
}
bool PCLmGenerator::addKids(sint32 kidObj) {
#define KID_ARRAY_SIZE 20
if (!KidsArray) {
KidsArray = (sint32 *) malloc(KID_ARRAY_SIZE * sizeof(sint32));
assert(KidsArray);
}
KidsArray[numKids] = kidObj;
numKids++;
if (!(numKids % KID_ARRAY_SIZE)) {
KidsArray = (sint32 *) realloc(KidsArray, ((numKids + KID_ARRAY_SIZE) * sizeof(sint32)));
}
return true;
}
void PCLmGenerator::initOutBuff(char *buff, sint32 size) {
currBuffPtr = outBuffPtr = buff;
outBuffSize = size;
totalBytesWrittenToCurrBuff = 0;
memset(buff, 0, size);
}
void PCLmGenerator::writeStr2OutBuff(char *str) {
sint32 strSize = strlen(str);
// Make sure we have enough room for the copy
char *maxSize = currBuffPtr + strSize;
assert(maxSize - outBuffPtr < outBuffSize);
memcpy(currBuffPtr, str, strSize);
currBuffPtr += strSize;
totalBytesWrittenToCurrBuff += strSize;
totalBytesWrittenToPCLmFile += strSize;
}
void PCLmGenerator::write2Buff(ubyte *buff, int buffSize) {
char *maxSize = currBuffPtr + buffSize;
if (maxSize - outBuffPtr > outBuffSize) {
assert(0);
}
memcpy(currBuffPtr, buff, buffSize);
currBuffPtr += buffSize;
totalBytesWrittenToCurrBuff += buffSize;
totalBytesWrittenToPCLmFile += buffSize;
}
int PCLmGenerator::statOutputFileSize() {
addXRef(totalBytesWrittenToPCLmFile);
return (1);
}
void PCLmGenerator::writePDFGrammarTrailer(int imageWidth, int imageHeight) {
int i;
char KidsString[KID_STRING_SIZE];
sprintf(pOutStr, "%%============= PCLm: FileBody: Object 1 - Catalog\n");
writeStr2OutBuff(pOutStr);
statOutputFileSize();
sprintf(pOutStr, "%d 0 obj\n", CATALOG_OBJ_NUMBER);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "<<\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Type /Catalog\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Pages %d 0 R\n", PAGES_OBJ_NUMBER);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, ">>\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "endobj\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%%============= PCLm: FileBody: Object 2 - page tree \n");
writeStr2OutBuff(pOutStr);
statOutputFileSize();
sprintf(pOutStr, "%d 0 obj\n", PAGES_OBJ_NUMBER);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "<<\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Count %ld\n", numKids);
writeStr2OutBuff(pOutStr);
// Define the Kids for this document as an indirect array
sprintf(KidsString, "/Kids [ ");
writeStr2OutBuff(KidsString);
for (i = 0; i < numKids; i++) {
sprintf(KidsString, "%ld 0 R ", KidsArray[i]);
writeStr2OutBuff(KidsString);
}
sprintf(KidsString, "]\n");
writeStr2OutBuff(KidsString);
sprintf(pOutStr, "/Type /Pages\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, ">>\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "endobj\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%%============= PCLm: cross-reference section: object 0, 6 entries\n");
writeStr2OutBuff(pOutStr);
statOutputFileSize();
// Fix up the xref table for backside duplex
fixXRef();
xRefStart = xRefIndex - 1;
sprintf(pOutStr, "xref\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "0 %d\n", 1);
writeStr2OutBuff(pOutStr);
// Note the attempt to write exactly 20 bytes
sprintf(pOutStr, "0000000000 65535 f \n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%d %ld\n", PAGES_OBJ_NUMBER + 1, xRefIndex - 4);
writeStr2OutBuff(pOutStr);
for (i = 1; i < xRefIndex - 3; i++) {
sprintf(pOutStr, "%010ld %05d n \n", xRefTable[i], 0);
writeStr2OutBuff(pOutStr);
}
// sprintf(pOutStr,"<</AIMetaData 32 0 R/AIPDFPrivateData1 33 0 R/AIPDFPrivateData10 34 0\n");
// Now add the catalog and page object
sprintf(pOutStr, "%d 2\n", CATALOG_OBJ_NUMBER);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%010ld %05d n \n", xRefTable[xRefIndex - 3], 0);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%010ld %05d n \n", xRefTable[xRefIndex - 2], 0);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%%============= PCLm: File Trailer\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "trailer\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "<<\n");
writeStr2OutBuff(pOutStr);
// sprintf(pOutStr,"/Info %d 0\n", infoObj); writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Size %ld\n", xRefIndex - 1);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Root %d 0 R\n", CATALOG_OBJ_NUMBER);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, ">>\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "startxref\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%ld\n", xRefTable[xRefStart]);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%%%%EOF\n");
writeStr2OutBuff(pOutStr);
}
bool PCLmGenerator::injectAdobeRGBCS() {
if (adobeRGBCS_firstTime) {
// We need to inject the ICC object for AdobeRGB
sprintf(pOutStr, "%%============= PCLm: ICC Profile\n");
writeStr2OutBuff(pOutStr);
statOutputFileSize();
sprintf(pOutStr, "%ld 0 obj\n", objCounter);
objCounter++;
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "[/ICCBased %ld 0 R]\n", objCounter);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "endobj\n");
writeStr2OutBuff(pOutStr);
statOutputFileSize();
sprintf(pOutStr, "%ld 0 obj\n", objCounter);
objCounter++;
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "<<\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/N 3\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Alternate /DeviceRGB\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Length %u\n", ADOBE_RGB_SIZE + 1);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Filter /FlateDecode\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, ">>\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "stream\n");
writeStr2OutBuff(pOutStr);
FILE *inFile;
if (!(inFile = fopen("flate_colorspace.bin", "rb"))) {
fprintf(stderr, "can't open %s\n", "flate_colorspace.bin");
return 0;
}
ubyte *buffIn = (unsigned char *) malloc(ADOBE_RGB_SIZE);
assert(buffIn);
sint32 bytesRead = fread(buffIn, 1, ADOBE_RGB_SIZE, inFile);
assert(bytesRead == ADOBE_RGB_SIZE);
fclose(inFile);
write2Buff(buffIn, bytesRead);
if (buffIn) {
free(buffIn);
}
sprintf(pOutStr, "\nendstream\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "endobj\n");
writeStr2OutBuff(pOutStr);
}
adobeRGBCS_firstTime = false;
return true;
}
bool PCLmGenerator::colorConvertSource(colorSpaceDisposition srcCS, colorSpaceDisposition dstCS,
ubyte *strip, sint32 stripWidth, sint32 stripHeight) {
if (srcCS == deviceRGB && dstCS == grayScale) {
// Do an inplace conversion from RGB -> 8 bpp gray
ubyte *srcPtr = strip;
ubyte *dstPtr = strip;
for (int h = 0; h < stripHeight; h++) {
for (int w = 0; w < stripWidth; w++, dstPtr++, srcPtr += 3) {
*dstPtr = (ubyte) rgb_2_gray(*srcPtr, *(srcPtr + 1), *(srcPtr + 2));
}
}
dstNumComponents = 1;
} else {
assert(0);
}
return true;
}
int PCLmGenerator::injectRLEStrip(ubyte *RLEBuffer, int numBytes, int imageWidth, int imageHeight,
colorSpaceDisposition destColorSpace, bool whiteStrip) {
bool printedImageTransform = false;
if (currDuplexDisposition == duplex_longEdge && !(pageCount % 2) && mirrorBackside) {
if (!startXRef) {
startXRef = xRefIndex;
}
injectImageTransform();
printedImageTransform = true;
}
if (destColorSpace == adobeRGB) {
injectAdobeRGBCS();
}
// Inject LZ compressed image into PDF file
sprintf(pOutStr, "%%============= PCLm: FileBody: Strip Stream: RLE Image \n");
writeStr2OutBuff(pOutStr);
statOutputFileSize();
if (currDuplexDisposition == duplex_longEdge && !(pageCount % 2) && mirrorBackside) {
sprintf(pOutStr, "%ld 0 obj\n", objCounter - 1);
objCounter++;
writeStr2OutBuff(pOutStr);
} else {
sprintf(pOutStr, "%ld 0 obj\n", objCounter);
objCounter++;
writeStr2OutBuff(pOutStr);
}
sprintf(pOutStr, "<<\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Width %d\n", imageWidth);
writeStr2OutBuff(pOutStr);
if (destColorSpace == deviceRGB) {
sprintf(pOutStr, "/ColorSpace /DeviceRGB\n");
writeStr2OutBuff(pOutStr);
} else if (destColorSpace == adobeRGB) {
sprintf(pOutStr, "/ColorSpace 5 0 R\n");
writeStr2OutBuff(pOutStr);
} else {
sprintf(pOutStr, "/ColorSpace /DeviceGray\n");
writeStr2OutBuff(pOutStr);
}
sprintf(pOutStr, "/Height %d\n", imageHeight);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Filter /RunLengthDecode\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Subtype /Image\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Length %d\n", numBytes);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Type /XObject\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/BitsPerComponent 8\n");
writeStr2OutBuff(pOutStr);
#ifdef SUPPORT_WHITE_STRIPS
if (whiteStrip) {
sprintf(pOutStr, "/Name /WhiteStrip\n");
writeStr2OutBuff(pOutStr);
} else {
sprintf(pOutStr, "/Name /ColorStrip\n");
writeStr2OutBuff(pOutStr);
}
#endif
sprintf(pOutStr, ">>\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "stream\n");
writeStr2OutBuff(pOutStr);
// Write the zlib compressed strip to the PDF output file
write2Buff(RLEBuffer, numBytes);
sprintf(pOutStr, "\nendstream\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "endobj\n");
writeStr2OutBuff(pOutStr);
if (!printedImageTransform) {
injectImageTransform();
}
endXRef = xRefIndex;
return (1);
}
int PCLmGenerator::injectLZStrip(ubyte *LZBuffer, int numBytes, int imageWidth, int imageHeight,
colorSpaceDisposition destColorSpace, bool whiteStrip) {
bool printedImageTransform = false;
if (currDuplexDisposition == duplex_longEdge && !(pageCount % 2) && mirrorBackside) {
if (!startXRef) {
startXRef = xRefIndex;
}
injectImageTransform();
printedImageTransform = true;
}
if (destColorSpace == adobeRGB) {
injectAdobeRGBCS();
}
// Inject LZ compressed image into PDF file
sprintf(pOutStr, "%%============= PCLm: FileBody: Strip Stream: zlib Image \n");
writeStr2OutBuff(pOutStr);
statOutputFileSize();
if (currDuplexDisposition == duplex_longEdge && !(pageCount % 2) && mirrorBackside) {
sprintf(pOutStr, "%ld 0 obj\n", objCounter - 1);
objCounter++;
writeStr2OutBuff(pOutStr);
} else {
sprintf(pOutStr, "%ld 0 obj\n", objCounter);
objCounter++;
writeStr2OutBuff(pOutStr);
}
sprintf(pOutStr, "<<\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Width %d\n", imageWidth);
writeStr2OutBuff(pOutStr);
if (destColorSpace == deviceRGB) {
sprintf(pOutStr, "/ColorSpace /DeviceRGB\n");
writeStr2OutBuff(pOutStr);
} else if (destColorSpace == adobeRGB) {
sprintf(pOutStr, "/ColorSpace 5 0 R\n");
writeStr2OutBuff(pOutStr);
} else {
sprintf(pOutStr, "/ColorSpace /DeviceGray\n");
writeStr2OutBuff(pOutStr);
}
sprintf(pOutStr, "/Height %d\n", imageHeight);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Filter /FlateDecode\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Subtype /Image\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Length %d\n", numBytes);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Type /XObject\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/BitsPerComponent 8\n");
writeStr2OutBuff(pOutStr);
#ifdef SUPPORT_WHITE_STRIPS
if (whiteStrip) {
sprintf(pOutStr, "/Name /WhiteStrip\n");
writeStr2OutBuff(pOutStr);
} else {
sprintf(pOutStr, "/Name /ColorStrip\n");
writeStr2OutBuff(pOutStr);
}
#endif
sprintf(pOutStr, ">>\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "stream\n");
writeStr2OutBuff(pOutStr);
// Write the zlib compressed strip to the PDF output file
write2Buff(LZBuffer, numBytes);
sprintf(pOutStr, "\nendstream\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "endobj\n");
writeStr2OutBuff(pOutStr);
if (!printedImageTransform) {
injectImageTransform();
}
endXRef = xRefIndex;
return (1);
}
void PCLmGenerator::injectImageTransform() {
char str[512];
int strLength;
sprintf(str, "q /image Do Q\n");
strLength = strlen(str);
// Output image transformation information
sprintf(pOutStr, "%%============= PCLm: Object - Image Transformation \n");
writeStr2OutBuff(pOutStr);
statOutputFileSize();
if (currDuplexDisposition == duplex_longEdge && !(pageCount % 2) && mirrorBackside) {
sprintf(pOutStr, "%ld 0 obj\n", objCounter + 1);
objCounter++;
writeStr2OutBuff(pOutStr);
} else {
sprintf(pOutStr, "%ld 0 obj\n", objCounter);
objCounter++;
writeStr2OutBuff(pOutStr);
}
sprintf(pOutStr, "<<\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Length %d\n", strLength);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, ">>\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "stream\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%s", str);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "endstream\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "endobj\n");
writeStr2OutBuff(pOutStr);
}
int PCLmGenerator::injectJPEG(char *jpeg_Buff, int imageWidth, int imageHeight, int numCompBytes,
colorSpaceDisposition destColorSpace, bool whiteStrip) {
char str[512];
bool printedImageTransform = false;
if (currDuplexDisposition == duplex_longEdge && !(pageCount % 2) && mirrorBackside) {
if (!startXRef) {
startXRef = xRefIndex;
}
injectImageTransform();
printedImageTransform = true;
}
yPosition += imageHeight;
if (destColorSpace == adobeRGB) {
injectAdobeRGBCS();
}
// Inject PDF JPEG into output file
sprintf(pOutStr, "%%============= PCLm: FileBody: Strip Stream: jpeg Image \n");
writeStr2OutBuff(pOutStr);
statOutputFileSize();
if (currDuplexDisposition == duplex_longEdge && !(pageCount % 2) && mirrorBackside) {
sprintf(pOutStr, "%ld 0 obj\n", objCounter - 1);
objCounter++;
writeStr2OutBuff(pOutStr);
} else {
sprintf(pOutStr, "%ld 0 obj\n", objCounter);
objCounter++;
writeStr2OutBuff(pOutStr);
}
sprintf(pOutStr, "<<\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Width %d\n", imageWidth);
writeStr2OutBuff(pOutStr);
if (destColorSpace == deviceRGB) {
sprintf(pOutStr, "/ColorSpace /DeviceRGB\n");
writeStr2OutBuff(pOutStr);
} else if (destColorSpace == adobeRGB) {
sprintf(pOutStr, "/ColorSpace 5 0 R\n");
writeStr2OutBuff(pOutStr);
} else {
sprintf(pOutStr, "/ColorSpace /DeviceGray\n");
writeStr2OutBuff(pOutStr);
}
sprintf(pOutStr, "/Height %d\n", imageHeight);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Filter /DCTDecode\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Subtype /Image\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Length %d\n", numCompBytes);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Type /XObject\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/BitsPerComponent 8\n");
writeStr2OutBuff(pOutStr);
#ifdef SUPPORT_WHITE_STRIPS
if (whiteStrip) {
sprintf(pOutStr, "/Name /WhiteStrip\n");
writeStr2OutBuff(pOutStr);
} else {
sprintf(pOutStr, "/Name /ColorStrip\n");
writeStr2OutBuff(pOutStr);
}
#endif
sprintf(pOutStr, ">>\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "stream\n");
writeStr2OutBuff(pOutStr);
write2Buff((ubyte *) jpeg_Buff, numCompBytes);
sprintf(pOutStr, "\nendstream\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "endobj\n");
writeStr2OutBuff(pOutStr);
sprintf(str, "q /image Do Q\n");
if (!printedImageTransform) {
injectImageTransform();
}
endXRef = xRefIndex;
return (1);
}
/*
* Writes str to buffer if the size of buffer is less than TEMP_BUFF_SIZE
*/
void writeStr2Buff(char *buffer, char *str) {
int buffSize;
char *buffPos;
buffSize = strlen(buffer) + strlen(str);
if (buffSize > TEMP_BUFF_SIZE) {
assert(0);
}
buffSize = strlen(buffer);
buffPos = buffer + buffSize;
sprintf(buffPos, "%s", str);
buffSize = strlen(buffer);
if (buffSize > TEMP_BUFF_SIZE) {
printf("tempBuff size exceeded: buffSize=%d\n", buffSize);
assert(0);
}
}
void PCLmGenerator::writePDFGrammarPage(int imageWidth, int imageHeight, int numStrips,
colorSpaceDisposition destColorSpace) {
int i, imageRef = objCounter + 2, buffSize;
int yAnchor;
char str[512];
char *tempBuffer;
int startImageIndex = 0;
int numLinesLeft = 0;
if (destColorSpace == adobeRGB && 1 == pageCount) {
imageRef += 2; // Add 2 for AdobeRGB
}
tempBuffer = (char *) malloc(TEMP_BUFF_SIZE);
assert(tempBuffer);
memset(tempBuffer, 0x0, TEMP_BUFF_SIZE);
sprintf(pOutStr, "%%============= PCLm: FileBody: Object 3 - page object\n");
writeStr2OutBuff(pOutStr);
statOutputFileSize();
sprintf(pOutStr, "%ld 0 obj\n", objCounter);
writeStr2OutBuff(pOutStr);
addKids(objCounter);
objCounter++;
sprintf(pOutStr, "<<\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Type /Page\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Parent %d 0 R\n", PAGES_OBJ_NUMBER);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Resources <<\n");
writeStr2OutBuff(pOutStr);
// sprintf(pOutStr,"/ProcSet [ /PDF /ImageC ]\n"); writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/XObject <<\n");
writeStr2OutBuff(pOutStr);
if (topMarginInPix) {
for (i = 0; i < numFullInjectedStrips; i++, startImageIndex++) {
sprintf(str, "/Image%d %d 0 R\n", startImageIndex, imageRef);
sprintf(pOutStr, "%s", str);
writeStr2OutBuff(pOutStr);
imageRef += 2;
}
if (numPartialScanlinesToInject) {
sprintf(str, "/Image%d %d 0 R\n", startImageIndex, imageRef);
sprintf(pOutStr, "%s", str);
writeStr2OutBuff(pOutStr);
imageRef += 2;
startImageIndex++;
}
}
for (i = startImageIndex; i < numStrips + startImageIndex; i++) {
sprintf(str, "/Image%d %d 0 R\n", i, imageRef);
// sprintf(pOutStr,"/ImageA 4 0 R /ImageB 6 0 R >>\n"); writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%s", str);
writeStr2OutBuff(pOutStr);
imageRef += 2;
}
sprintf(pOutStr, ">>\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, ">>\n");
writeStr2OutBuff(pOutStr);
if (currMediaOrientationDisposition == landscapeOrientation) {
pageOrigin = mediaWidth;
sprintf(pOutStr, "/MediaBox [ 0 0 %d %d ]\n", mediaHeight, mediaWidth);
writeStr2OutBuff(pOutStr);
} else {
pageOrigin = mediaHeight;
sprintf(pOutStr, "/MediaBox [ 0 0 %d %d ]\n", mediaWidth, mediaHeight);
writeStr2OutBuff(pOutStr);
}
sprintf(pOutStr, "/Contents [ %ld 0 R ]\n", objCounter);
writeStr2OutBuff(pOutStr);
#ifdef PIECEINFO_SUPPORTED
sprintf(pOutStr,"/PieceInfo <</HPAddition %d 0 R >> \n",9997); writeStr2OutBuff(pOutStr);
#endif
sprintf(pOutStr, ">>\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "endobj\n");
writeStr2OutBuff(pOutStr);
// Create the FileBody stream first, so we know the Length of the stream
if (reverseOrder) {
yAnchor = 0;
} else {
yAnchor = (int) ((pageOrigin * STANDARD_SCALE) + 0.99); // Round up
}
// Setup the CTM so that we can send device-resolution coordinates
sprintf(pOutStr,
"%%Image Transformation Matrix: width, skewX, skewY, height, xAnchor, yAnchor\n");
writeStr2OutBuff(pOutStr);
sprintf(str, "%f 0 0 %f 0 0 cm\n", STANDARD_SCALE_FOR_PDF / currRenderResolutionInteger,
STANDARD_SCALE_FOR_PDF / currRenderResolutionInteger);
writeStr2Buff(tempBuffer, str);
startImageIndex = 0;
if (topMarginInPix) {
for (i = 0; i < numFullInjectedStrips; i++) {
if (reverseOrder) {
yAnchor += numFullScanlinesToInject;
} else {
yAnchor -= numFullScanlinesToInject;
}
sprintf(str, "/P <</MCID 0>> BDC q\n");
writeStr2Buff(tempBuffer, str);
sprintf(str, "%%Image Transformation Matrix: width, skewX, skewY, height, "
"xAnchor, yAnchor\n");
writeStr2Buff(tempBuffer, str);
sprintf(str, "%d 0 0 %ld 0 %d cm\n", imageWidth * scaleFactor,
numFullScanlinesToInject * scaleFactor, yAnchor * scaleFactor);
writeStr2Buff(tempBuffer, str);
sprintf(str, "/Image%d Do Q\n", startImageIndex);
writeStr2Buff(tempBuffer, str);
startImageIndex++;
}
if (numPartialScanlinesToInject) {
if (reverseOrder) {
yAnchor += numPartialScanlinesToInject;
} else {
yAnchor -= numPartialScanlinesToInject;
}
sprintf(str, "/P <</MCID 0>> BDC q\n");
writeStr2Buff(tempBuffer, str);
sprintf(str, "%%Image Transformation Matrix: width, skewX, skewY, height, xAnchor, "
"yAnchor\n");
writeStr2Buff(tempBuffer, str);
sprintf(str, "%d 0 0 %ld 0 %d cm\n", imageWidth * scaleFactor,
numPartialScanlinesToInject * scaleFactor, yAnchor * scaleFactor);
writeStr2Buff(tempBuffer, str);
sprintf(str, "/Image%d Do Q\n", startImageIndex);
writeStr2Buff(tempBuffer, str);
startImageIndex++;
}
}
for (i = startImageIndex; i < numStrips + startImageIndex; i++) {
// last strip may have less lines than currStripHeight. Update yAnchor using left over lines
if (i == (numStrips + startImageIndex - 1)) {
numLinesLeft = currSourceHeight - ((numStrips - 1) * currStripHeight);
if (reverseOrder) {
yAnchor += numLinesLeft;
} else {
yAnchor -= numLinesLeft;
}
} else {
if (reverseOrder) {
yAnchor += currStripHeight;
} else {
yAnchor -= currStripHeight;
}
}
sprintf(str, "/P <</MCID 0>> BDC q\n");
writeStr2Buff(tempBuffer, str);
sprintf(str,
"%%Image Transformation Matrix: width, skewX, skewY, height, xAnchor, yAnchor\n");
writeStr2Buff(tempBuffer, str);
// last strip may have less lines than currStripHeight
if (i == (numStrips + startImageIndex - 1)) {
sprintf(str, "%d 0 0 %d 0 %d cm\n", imageWidth * scaleFactor,
numLinesLeft * scaleFactor, yAnchor * scaleFactor);
writeStr2Buff(tempBuffer, str);
} else if (yAnchor < 0) {
sint32 newH = currStripHeight + yAnchor;
sprintf(str, "%d 0 0 %ld 0 %d cm\n", imageWidth * scaleFactor, newH * scaleFactor,
0 * scaleFactor);
writeStr2Buff(tempBuffer, str);
} else {
sprintf(str, "%d 0 0 %ld 0 %d cm\n", imageWidth * scaleFactor,
currStripHeight * scaleFactor, yAnchor * scaleFactor);
writeStr2Buff(tempBuffer, str);
}
sprintf(str, "/Image%d Do Q\n", i);
writeStr2Buff(tempBuffer, str);
}
// Resulting buffer size
buffSize = strlen(tempBuffer);
sprintf(pOutStr, "%%============= PCLm: FileBody: Page Content Stream object\n");
writeStr2OutBuff(pOutStr);
statOutputFileSize();
sprintf(pOutStr, "%ld 0 obj\n", objCounter);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "<<\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "/Length %d\n", buffSize);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, ">>\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "stream\n");
writeStr2OutBuff(pOutStr);
// Now write the FileBody stream
write2Buff((ubyte *) tempBuffer, buffSize);
sprintf(pOutStr, "endstream\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "endobj\n");
writeStr2OutBuff(pOutStr);
objCounter++;
if (tempBuffer) {
free(tempBuffer);
}
}
/*
* Mirrors the source image in preparation for backside duplex support
*/
static bool prepImageForBacksideDuplex(ubyte *imagePtr, sint32 imageHeight, sint32 imageWidth,
sint32 numComponents) {
sint32 numBytes = imageHeight * imageWidth * numComponents;
ubyte *head, *tail, t0, t1, t2;
if (numComponents == 3) {
for (head = imagePtr, tail = imagePtr + numBytes - 1; tail > head;) {
t0 = *head;
t1 = *(head + 1);
t2 = *(head + 2);
*head = *(tail - 2);
*(head + 1) = *(tail - 1);
*(head + 2) = *(tail - 0);
*tail = t2;
*(tail - 1) = t1;
*(tail - 2) = t0;
head += 3;
tail -= 3;
}
} else {
for (head = imagePtr, tail = imagePtr + numBytes; tail > head;) {
t0 = *head;
*head = *tail;
*tail = t0;
head++;
tail--;
}
}
//origTail++;
return true;
}
bool PCLmGenerator::getInputBinString(jobInputBin bin, char *returnStr) {
returnStr[0] = '\0';
switch (bin) {
case alternate:
strcpy(returnStr, "alternate");
break;
case alternate_roll:
strcpy(returnStr, "alternate_roll");
break;
case auto_select:
strcpy(returnStr, "auto_select");
break;
case bottom:
strcpy(returnStr, "bottom");
break;
case center:
strcpy(returnStr, "center");
break;
case disc:
strcpy(returnStr, "disc");
break;
case envelope:
strcpy(returnStr, "envelope");
break;
case hagaki:
strcpy(returnStr, "hagaki");
break;
case large_capacity:
strcpy(returnStr, "large_capacity");
break;
case left:
strcpy(returnStr, "left");
break;
case main_tray:
strcpy(returnStr, "main_tray");
break;
case main_roll:
strcpy(returnStr, "main_roll");
break;
case manual:
strcpy(returnStr, "manual");
break;
case middle:
strcpy(returnStr, "middle");
break;
case photo:
strcpy(returnStr, "photo");
break;
case rear:
strcpy(returnStr, "rear");
break;
case right:
strcpy(returnStr, "right");
break;
case side:
strcpy(returnStr, "side");
break;
case top:
strcpy(returnStr, "top");
break;
case tray_1:
strcpy(returnStr, "tray_1");
break;
case tray_2:
strcpy(returnStr, "tray_2");
break;
case tray_3:
strcpy(returnStr, "tray_3");
break;
case tray_4:
strcpy(returnStr, "tray_4");
break;
case tray_5:
strcpy(returnStr, "tray_5");
break;
case tray_N:
strcpy(returnStr, "tray_N");
break;
default:
assert(0);
break;
}
return true;
}
bool PCLmGenerator::getOutputBin(jobOutputBin bin, char *returnStr) {
if (returnStr) {
returnStr[0] = '\0';
}
switch (bin) {
case top_output:
strcpy(returnStr, "top_output");
break;
case middle_output:
strcpy(returnStr, "middle_output");
break;
case bottom_output:
strcpy(returnStr, "bottom_output");
break;
case side_output:
strcpy(returnStr, "side_output");
break;
case center_output:
strcpy(returnStr, "center_output");
break;
case rear_output:
strcpy(returnStr, "rear_output");
break;
case face_up:
strcpy(returnStr, "face_up");
break;
case face_down:
strcpy(returnStr, "face_down");
break;
case large_capacity_output:
strcpy(returnStr, "large_capacity_output");
break;
case stacker_N:
strcpy(returnStr, "stacker_N");
break;
case mailbox_N:
strcpy(returnStr, "mailbox_N");
break;
case tray_1_output:
strcpy(returnStr, "tray_1_output");
break;
case tray_2_output:
strcpy(returnStr, "tray_2_output");
break;
case tray_3_output:
strcpy(returnStr, "tray_3_output");
break;
case tray_4_output:
strcpy(returnStr, "tray_4_output");
break;
default:
assert(0);
break;
}
return true;
}
void PCLmGenerator::writeJobTicket() {
// Write JobTicket
char inputBin[256];
char outputBin[256];
if (!m_pPCLmSSettings) {
return;
}
getInputBinString(m_pPCLmSSettings->userInputBin, &inputBin[0]);
getOutputBin(m_pPCLmSSettings->userOutputBin, &outputBin[0]);
strcpy(inputBin, inputBin);
strcpy(outputBin, outputBin);
sprintf(pOutStr, "%% genPCLm (Ver: %f)\n", PCLM_Ver);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%%============= Job Ticket =============\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%% PCLmS-Job-Ticket\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%% job-ticket-version: 0.1\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%% epcl-version: 1.01\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%% JobSection\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%% job-id: xxx\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%% MediaHandlingSection\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%% media-size-name: %s\n", currMediaName);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%% media-type: %s\n", m_pPCLmSSettings->userMediaType);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%% media-source: %s\n", inputBin);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%% sides: xxx\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%% output-bin: %s\n", outputBin);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%% RenderingSection\n");
writeStr2OutBuff(pOutStr);
if (currCompressionDisposition == compressDCT) {
sprintf(pOutStr, "%% pclm-compression-method: JPEG\n");
writeStr2OutBuff(pOutStr);
} else if (currCompressionDisposition == compressFlate) {
sprintf(pOutStr, "%% pclm-compression-method: FLATE\n");
writeStr2OutBuff(pOutStr);
} else {
sprintf(pOutStr, "%% pclm-compression-method: RLE\n");
writeStr2OutBuff(pOutStr);
}
sprintf(pOutStr, "%% strip-height: %ld\n", currStripHeight);
writeStr2OutBuff(pOutStr);
if (destColorSpace == deviceRGB) {
sprintf(pOutStr, "%% print-color-mode: deviceRGB\n");
writeStr2OutBuff(pOutStr);
} else if (destColorSpace == adobeRGB) {
sprintf(pOutStr, "%% print-color-mode: adobeRGB\n");
writeStr2OutBuff(pOutStr);
} else if (destColorSpace == grayScale) {
sprintf(pOutStr, "%% print-color-mode: gray\n");
writeStr2OutBuff(pOutStr);
}
sprintf(pOutStr, "%% print-quality: %d\n", m_pPCLmSSettings->userPageQuality);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%% printer-resolution: %d\n", currRenderResolutionInteger);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%% print-content-optimized: xxx\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%% orientation-requested: %d\n", m_pPCLmSSettings->userOrientation);
writeStr2OutBuff(pOutStr);
if (PCLmSSettings.userCopies == 0) {
PCLmSSettings.userCopies = 1;
}
sprintf(pOutStr, "%% copies: %d\n", m_pPCLmSSettings->userCopies);
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%% pclm-raster-back-side: xxx\n");
writeStr2OutBuff(pOutStr);
if (currRenderResolutionInteger) {
sprintf(pOutStr, "%% margins-pre-applied: TRUE\n");
writeStr2OutBuff(pOutStr);
} else {
sprintf(pOutStr, "%% margins-pre-applied: FALSE\n");
writeStr2OutBuff(pOutStr);
}
sprintf(pOutStr, "%% PCLmS-Job-Ticket-End\n");
writeStr2OutBuff(pOutStr);
}
void PCLmGenerator::writePDFGrammarHeader() {
// sprintf(pOutStr,"%%============= PCLm: File Header \n"); writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%%PDF-1.7\n");
writeStr2OutBuff(pOutStr);
sprintf(pOutStr, "%%PCLm 1.0\n");
writeStr2OutBuff(pOutStr);
}
int PCLmGenerator::RLEEncodeImage(ubyte *in, ubyte *out, int inLength) {
ubyte *imgPtr = in;
ubyte *endPtr = in + inLength;
ubyte *origOut = out;
ubyte c;
sint32 cnt = 0;
while (imgPtr < endPtr) {
c = *imgPtr++;
cnt = 1;
// Figure out how many repeating bytes are in the image
while (*imgPtr == c && cnt < inLength) {
if (imgPtr > endPtr) {
break;
}
cnt++;
imgPtr++;
}
if (cnt > 1) {
/* If cnt > 1, then output repeating byte specification
* The syntax is "byte-count repeateByte", where byte-count is 257-byte-count.
* Since the cnt value is a byte, if the repeateCnt is > 128 then we need to put
* out multiple repeat-blocks (Referred to as method 1) range is 128-256
*/
while (cnt > 128) {
*out++ = 129; // i.e. 257-129==128
*out++ = c;
cnt -= 128;
}
// Now handle the repeats that are < 128
if (cnt) {
*out++ = (257 - cnt); // i.e. cnt==2: 257-255=2
*out++ = c;
}
} else {
/* If cnt==1, then this is a literal run - no repeating bytes found.
* The syntax is "byte-count literal-run", where byte-count is < 128 and
* literal-run is the non-repeating bytes of the input stream.
* Referred to as method 2, range is 0-127
*/
ubyte *start, *p;
sint32 i;
start = (imgPtr - 1); // The first byte of the literal run
// Now find the end of the literal run
for (cnt = 1, p = start; *p != *imgPtr; p++, imgPtr++, cnt++) {
if (imgPtr >= endPtr) break;
}
if (!(imgPtr == endPtr)) {
imgPtr--;
// imgPtr incremented 1 too many
}
cnt--;
// Blocks of literal bytes can't exceed 128 bytes, so output multiple
// literal-run blocks if > 128
while (cnt > 128) {
*out++ = 127;
for (i = 0; i < 128; i++) {
*out++ = *start++;
}
cnt -= 128;
}
// Now output the leftover literal run
*out++ = cnt - 1;
for (i = 0; i < cnt; i++) {
*out++ = *start++;
}
}
}
// Now, write the end-of-compression marker (byte 128) into the output stream
*out++ = 128;
// Return the compressed size
return ((int) (out - origOut));
}
PCLmGenerator::PCLmGenerator() {
strcpy(currMediaName, "LETTER");
currDuplexDisposition = simplex;
currCompressionDisposition = compressDCT;
currMediaOrientationDisposition = portraitOrientation;
currRenderResolution = res600;
currStripHeight = STRIP_HEIGHT;
// Default media h/w to letter specification
mediaWidthInPixels = 0;
mediaHeightInPixels = 0;
mediaWidth = 612;
mediaHeight = 792;
destColorSpace = deviceRGB;
sourceColorSpace = deviceRGB;
scaleFactor = 1;
jobOpen = job_closed;
scratchBuffer = NULL;
pageCount = 0;
currRenderResolutionInteger = 600;
STANDARD_SCALE = (float) currRenderResolutionInteger / (float) STANDARD_SCALE_FOR_PDF;
yPosition = 0;
numKids = 0;
// XRefTable storage
xRefIndex = 0;
xRefStart = 0;
objCounter = PAGES_OBJ_NUMBER + 1;
totalBytesWrittenToPCLmFile = 0;
// Initialize first index in xRefTable
xRefTable = NULL;
KidsArray = NULL;
// Initialize the output Buffer
allocatedOutputBuffer = NULL;
// Initialize the leftover scanline logic
leftoverScanlineBuffer = 0;
adobeRGBCS_firstTime = true;
mirrorBackside = true;
topMarginInPix = 0;
leftMarginInPix = 0;
m_pPCLmSSettings = NULL;
}
PCLmGenerator::~PCLmGenerator() {
Cleanup();
}
int PCLmGenerator::StartJob(void **pOutBuffer, int *iOutBufferSize) {
/* Allocate the output buffer; we don't know much at this point, so make the output buffer size
* the worst case dimensions; when we get a startPage, we will resize it appropriately
*/
outBuffSize = DEFAULT_OUTBUFF_SIZE;
*iOutBufferSize = outBuffSize;
*pOutBuffer = (ubyte *) malloc(outBuffSize); // This multipliy by 10 needs to be removed...
if (NULL == *pOutBuffer) {
return (errorOutAndCleanUp());
}
currOutBuffSize = outBuffSize;
if (NULL == *pOutBuffer) {
return (errorOutAndCleanUp());
}
allocatedOutputBuffer = *pOutBuffer;
initOutBuff((char *) *pOutBuffer, outBuffSize);
writePDFGrammarHeader();
*iOutBufferSize = totalBytesWrittenToCurrBuff;
jobOpen = job_open;
return success;
}
int PCLmGenerator::EndJob(void **pOutBuffer, int *iOutBufferSize) {
if (NULL == allocatedOutputBuffer) {
return (errorOutAndCleanUp());
}
*pOutBuffer = allocatedOutputBuffer;
initOutBuff((char *) *pOutBuffer, outBuffSize);
// Write PDF trailer
writePDFGrammarTrailer(currSourceWidth, currSourceHeight);
*iOutBufferSize = totalBytesWrittenToCurrBuff;
jobOpen = job_closed;
if (xRefTable) {
free(xRefTable);
xRefTable = NULL;
}
if (KidsArray) {
free(KidsArray);
KidsArray = NULL;
}
return success;
}
int PCLmGenerator::StartPage(PCLmPageSetup *PCLmPageContent, void **pOutBuffer,
int *iOutBufferSize) {
int numImageStrips;
// Save the resolution information
currRenderResolution = PCLmPageContent->destinationResolution;
*pOutBuffer = allocatedOutputBuffer;
if (currRenderResolution == res300) {
currRenderResolutionInteger = 300;
} else if (currRenderResolution == res600) {
currRenderResolutionInteger = 600;
} else if (currRenderResolution == res1200) {
currRenderResolutionInteger = 1200;
} else {
assert(0);
}
// Recalculate STANDARD_SCALE to reflect the job resolution
STANDARD_SCALE = (float) currRenderResolutionInteger / (float) STANDARD_SCALE_FOR_PDF;
// Use the values set by the caller
currSourceWidth = PCLmPageContent->SourceWidthPixels;
currSourceHeight = PCLmPageContent->SourceHeightPixels;
// Save off the media information
mediaWidth = (int) (PCLmPageContent->mediaWidth);
mediaHeight = (int) (PCLmPageContent->mediaHeight);
// Use the values set by the caller
mediaWidthInPixels = PCLmPageContent->mediaWidthInPixels;
mediaHeightInPixels = PCLmPageContent->mediaHeightInPixels;
topMarginInPix = (int) (((PCLmPageContent->mediaHeightOffset / STANDARD_SCALE_FOR_PDF) *
currRenderResolutionInteger) + 0.50);
leftMarginInPix = (int) (((PCLmPageContent->mediaWidthOffset / STANDARD_SCALE_FOR_PDF) *
currRenderResolutionInteger) + 0.50);
if (topMarginInPix % 16) {
// Round to nearest 16 scanline boundary to ensure decompressability.
int i = topMarginInPix % 16;
if (i < (16 / 2)) {
topMarginInPix -= i;
} else {
topMarginInPix += (16 - i);
}
}
if (leftMarginInPix % 16) {
// Round to nearest 16 scanline boundary to ensure decompressability.
int i = leftMarginInPix % 16;
if (i < (16 / 2)) {
leftMarginInPix -= i;
} else {
leftMarginInPix += (16 - i);
}
}
currCompressionDisposition = PCLmPageContent->compTypeRequested;
if (strlen(PCLmPageContent->mediaSizeName)) {
strcpy(currMediaName, PCLmPageContent->mediaSizeName);
}
currStripHeight = PCLmPageContent->stripHeight;
if (!currStripHeight) {
numImageStrips = 1;
currStripHeight = currSourceHeight;
} else {
// Need to know how many strips will be inserted into PDF file
float numImageStripsReal = ceil((float) currSourceHeight / (float) currStripHeight);
numImageStrips = (int) numImageStripsReal;
}
if (PCLmPageContent->srcColorSpaceSpefication == grayScale) {
srcNumComponents = 1;
} else {
srcNumComponents = 3;
}
if (PCLmPageContent->dstColorSpaceSpefication == grayScale) {
dstNumComponents = 1;
} else {
dstNumComponents = 3;
}
currDuplexDisposition = PCLmPageContent->duplexDisposition;
destColorSpace = PCLmPageContent->dstColorSpaceSpefication;
// Calculate how large the output buffer needs to be based upon the page specifications
int tmp_outBuffSize = mediaWidthInPixels * currStripHeight * dstNumComponents;
if (tmp_outBuffSize > currOutBuffSize) {
// Realloc the pOutBuffer to the correct size
*pOutBuffer = realloc(*pOutBuffer, tmp_outBuffSize);
if (*pOutBuffer == NULL) {
// realloc failed and prev buffer not freed
return errorOutAndCleanUp();
}
outBuffSize = currOutBuffSize = tmp_outBuffSize;
allocatedOutputBuffer = *pOutBuffer;
if (NULL == allocatedOutputBuffer) {
return (errorOutAndCleanUp());
}
}
initOutBuff((char *) *pOutBuffer, outBuffSize);
// Keep track of the page count
pageCount++;
// If we are on a backside and doing duplex, prep for reverse strip order
if (currDuplexDisposition == duplex_longEdge && !(pageCount % 2) && mirrorBackside) {
reverseOrder = true;
} else {
reverseOrder = false;
}
// Calculate the number of injected strips, if any
if (topMarginInPix) {
if (topMarginInPix <= currStripHeight) {
numFullInjectedStrips = 1;
numFullScanlinesToInject = topMarginInPix;
numPartialScanlinesToInject = 0;
} else {
numFullInjectedStrips = topMarginInPix / currStripHeight;
numFullScanlinesToInject = currStripHeight;
numPartialScanlinesToInject =
topMarginInPix - (numFullInjectedStrips * currStripHeight);
}
}
writeJobTicket();
writePDFGrammarPage(mediaWidthInPixels, mediaHeightInPixels, numImageStrips, destColorSpace);
*iOutBufferSize = totalBytesWrittenToCurrBuff;
if (!scratchBuffer) {
// We need to pad the scratchBuffer size to allow for compression expansion (RLE can create
// compressed segments that are slightly larger than the source.
size_t len = (size_t) currStripHeight * mediaWidthInPixels * srcNumComponents * 2;
scratchBuffer = (ubyte *) malloc(len);
if (!scratchBuffer) {
return errorOutAndCleanUp();
}
}
mirrorBackside = PCLmPageContent->mirrorBackside;
firstStrip = true;
return success;
}
int PCLmGenerator::EndPage(void **pOutBuffer, int *iOutBufferSize) {
*pOutBuffer = allocatedOutputBuffer;
initOutBuff((char *) *pOutBuffer, outBuffSize);
*iOutBufferSize = totalBytesWrittenToCurrBuff;
// Free up the scratchbuffer at endpage, to allow the next page to have a different size
if (scratchBuffer) {
free(scratchBuffer);
scratchBuffer = NULL;
}
return success;
}
int PCLmGenerator::Encapsulate(void *pInBuffer, int inBufferSize, int thisHeight,
void **pOutBuffer, int *iOutBufferSize) {
int numCompBytes;
int scanlineWidth = mediaWidthInPixels * srcNumComponents;
int numLinesThisCall = thisHeight;
void *savedInBufferPtr = NULL;
void *tmpBuffer = NULL;
void *localInBuffer;
ubyte *newStripPtr = NULL;
if (leftoverScanlineBuffer) {
ubyte *whereAreWe;
sint32 scanlinesThisTime;
// The leftover scanlines have already been processed (color-converted and flipped), so
// just put them into the output buffer.
// Allocate a temporary buffer to copy leftover and new data into
tmpBuffer = malloc(scanlineWidth * currStripHeight);
if (!tmpBuffer) {
return (errorOutAndCleanUp());
}
// Copy leftover scanlines into tmpBuffer
memcpy(tmpBuffer, leftoverScanlineBuffer, scanlineWidth * numLeftoverScanlines);
whereAreWe = (ubyte *) tmpBuffer + (scanlineWidth * numLeftoverScanlines);
scanlinesThisTime = currStripHeight - numLeftoverScanlines;
// Copy enough scanlines from the real inBuffer to fill out the tmpBuffer
memcpy(whereAreWe, pInBuffer, scanlinesThisTime * scanlineWidth);
// Now copy the remaining scanlines from pInBuffer to the leftoverBuffer
numLeftoverScanlines = thisHeight - scanlinesThisTime;
assert(leftoverScanlineBuffer);
whereAreWe = (ubyte *) pInBuffer + (scanlineWidth * numLeftoverScanlines);
memcpy(leftoverScanlineBuffer, whereAreWe, scanlineWidth * numLeftoverScanlines);
numLinesThisCall = thisHeight = currStripHeight;
savedInBufferPtr = pInBuffer;
localInBuffer = tmpBuffer;
} else {
localInBuffer = pInBuffer;
}
if (thisHeight > currStripHeight) {
// Copy raw raster into leftoverScanlineBuffer
ubyte *ptr;
numLeftoverScanlines = thisHeight - currStripHeight;
leftoverScanlineBuffer = malloc(scanlineWidth * numLeftoverScanlines);
if (!leftoverScanlineBuffer) {
return (errorOutAndCleanUp());
}
ptr = (ubyte *) localInBuffer + scanlineWidth * numLeftoverScanlines;
memcpy(leftoverScanlineBuffer, ptr, scanlineWidth * numLeftoverScanlines);
thisHeight = currStripHeight;
}
if (NULL == allocatedOutputBuffer) {
if (tmpBuffer) {
free(tmpBuffer);
}
return (errorOutAndCleanUp());
}
*pOutBuffer = allocatedOutputBuffer;
initOutBuff((char *) *pOutBuffer, outBuffSize);
if (currDuplexDisposition == duplex_longEdge && !(pageCount % 2)) {
if (mirrorBackside) {
prepImageForBacksideDuplex((ubyte *) localInBuffer, numLinesThisCall, currSourceWidth,
srcNumComponents);
}
}
if (destColorSpace == grayScale &&
(sourceColorSpace == deviceRGB || sourceColorSpace == adobeRGB)) {
colorConvertSource(sourceColorSpace, grayScale, (ubyte *) localInBuffer, currSourceWidth,
numLinesThisCall);
// Adjust the scanline width accordingly
scanlineWidth = mediaWidthInPixels * dstNumComponents;
}
if (leftMarginInPix) {
newStripPtr = shiftStripByLeftMargin((ubyte *) localInBuffer, currSourceWidth,
currStripHeight, numLinesThisCall, mediaWidthInPixels, leftMarginInPix,
destColorSpace);
}
bool whiteStrip = false;
#ifdef SUPPORT_WHITE_STRIPS
if (!firstStrip) {
// PCLm does not print a blank page if all the strips are marked as "/Name /WhiteStrip"
// so only apply /WhiteStrip to strips after the first
whiteStrip = isWhiteStrip(pInBuffer, thisHeight * currSourceWidth * srcNumComponents);
}
#endif
if (currCompressionDisposition == compressDCT) {
if (firstStrip && topMarginInPix) {
ubyte whitePt = 0xff;
ubyte *tmpStrip = (ubyte *) malloc(scanlineWidth * topMarginInPix);
memset(tmpStrip, whitePt, scanlineWidth * topMarginInPix);
for (sint32 stripCntr = 0; stripCntr < numFullInjectedStrips; stripCntr++) {
write_JPEG_Buff(scratchBuffer, JPEG_QUALITY, mediaWidthInPixels,
(sint32) numFullScanlinesToInject, tmpStrip, currRenderResolutionInteger,
destColorSpace, &numCompBytes);
injectJPEG((char *) scratchBuffer, mediaWidthInPixels,
(sint32) numFullScanlinesToInject, numCompBytes, destColorSpace, true);
}
if (numPartialScanlinesToInject) {
// Handle the leftover strip
write_JPEG_Buff(scratchBuffer, JPEG_QUALITY, mediaWidthInPixels,
numPartialScanlinesToInject, tmpStrip, currRenderResolutionInteger,
destColorSpace, &numCompBytes);
injectJPEG((char *) scratchBuffer, mediaWidthInPixels, numPartialScanlinesToInject,
numCompBytes, destColorSpace, true);
}
free(tmpStrip);
}
firstStrip = false;
// We are always going to compress the full strip height, even though the image may be less;
// this allows the compressed images to be symmetric
if (numLinesThisCall < currStripHeight) {
sint32 numLeftoverBytes = (currStripHeight - numLinesThisCall) * currSourceWidth * 3;
sint32 numImagedBytes = numLinesThisCall * currSourceWidth * 3;
// End-of-page: we have to white-out the unused section of the source image
memset((ubyte *) localInBuffer + numImagedBytes, 0xff, numLeftoverBytes);
}
if (newStripPtr) {
write_JPEG_Buff(scratchBuffer, JPEG_QUALITY, mediaWidthInPixels, currStripHeight,
newStripPtr, currRenderResolutionInteger, destColorSpace, &numCompBytes);
free(newStripPtr);
newStripPtr = NULL;
} else {
write_JPEG_Buff(scratchBuffer, JPEG_QUALITY, mediaWidthInPixels, currStripHeight,
(JSAMPLE *) localInBuffer, currRenderResolutionInteger, destColorSpace,
&numCompBytes);
}
injectJPEG((char *) scratchBuffer, mediaWidthInPixels, currStripHeight, numCompBytes,
destColorSpace, whiteStrip);
} else if (currCompressionDisposition == compressFlate) {
uint32 len = numLinesThisCall * scanlineWidth;
uLongf destSize = len;
int result;
if (firstStrip && topMarginInPix) {
ubyte whitePt = 0xff;
// We need to inject a blank image-strip with a height==topMarginInPix
ubyte *tmpStrip = (ubyte *) malloc(scanlineWidth * topMarginInPix);
uLongf tmpDestSize = destSize;
memset(tmpStrip, whitePt, scanlineWidth * topMarginInPix);
for (sint32 stripCntr = 0; stripCntr < numFullInjectedStrips; stripCntr++) {
result = compress(scratchBuffer, &tmpDestSize, (const Bytef *) tmpStrip,
scanlineWidth * numFullScanlinesToInject);
injectLZStrip(scratchBuffer, tmpDestSize, mediaWidthInPixels,
numFullScanlinesToInject, destColorSpace, true);
}
if (numPartialScanlinesToInject) {
result = compress(scratchBuffer, &tmpDestSize, (const Bytef *) tmpStrip,
scanlineWidth * numPartialScanlinesToInject);
injectLZStrip(scratchBuffer, tmpDestSize, mediaWidthInPixels,
numPartialScanlinesToInject, destColorSpace, true);
}
free(tmpStrip);
}
firstStrip = false;
if (newStripPtr) {
result = compress(scratchBuffer, &destSize, (const Bytef *) newStripPtr,
scanlineWidth * numLinesThisCall);
free(newStripPtr);
newStripPtr = NULL;
} else {
// Dump the source data
result = compress(scratchBuffer, &destSize, (const Bytef *) localInBuffer,
scanlineWidth * numLinesThisCall);
}
injectLZStrip(scratchBuffer, destSize, mediaWidthInPixels, numLinesThisCall, destColorSpace,
whiteStrip);
} else if (currCompressionDisposition == compressRLE) {
int compSize;
if (firstStrip && topMarginInPix) {
ubyte whitePt = 0xff;
// We need to inject a blank image-strip with a height==topMarginInPix
ubyte *tmpStrip = (ubyte *) malloc(scanlineWidth * topMarginInPix);
memset(tmpStrip, whitePt, scanlineWidth * topMarginInPix);
for (sint32 stripCntr = 0; stripCntr < numFullInjectedStrips; stripCntr++) {
compSize = RLEEncodeImage(tmpStrip, scratchBuffer,
scanlineWidth * numFullScanlinesToInject);
injectRLEStrip(scratchBuffer, compSize, mediaWidthInPixels,
numFullScanlinesToInject, destColorSpace, true);
}
if (numPartialScanlinesToInject) {
compSize = RLEEncodeImage(tmpStrip, scratchBuffer,
scanlineWidth * numPartialScanlinesToInject);
injectRLEStrip(scratchBuffer, compSize, mediaWidthInPixels,
numPartialScanlinesToInject, destColorSpace, true);
}
free(tmpStrip);
}
firstStrip = false;
if (newStripPtr) {
compSize = RLEEncodeImage(newStripPtr, scratchBuffer,
scanlineWidth * numLinesThisCall);
free(newStripPtr);
newStripPtr = NULL;
} else {
compSize = RLEEncodeImage((ubyte *) localInBuffer, scratchBuffer,
scanlineWidth * numLinesThisCall);
}
injectRLEStrip(scratchBuffer, compSize, mediaWidthInPixels, numLinesThisCall,
destColorSpace, whiteStrip);
} else {
assert(0);
}
*iOutBufferSize = totalBytesWrittenToCurrBuff;
if (savedInBufferPtr) {
pInBuffer = savedInBufferPtr;
}
if (tmpBuffer) {
free(tmpBuffer);
}
if (newStripPtr) {
free(newStripPtr);
}
return success;
}
int PCLmGenerator::GetPclmMediaDimensions(const char *mediaRequested,
PCLmPageSetup *myPageInfo) {
int i = 0;
int result = 99;
int iRenderResolutionInteger = 0;
if (myPageInfo->destinationResolution == res300) {
iRenderResolutionInteger = 300;
} else if (myPageInfo->destinationResolution == res600) {
iRenderResolutionInteger = 600;
} else if (myPageInfo->destinationResolution == res1200) {
iRenderResolutionInteger = 1200;
} else {
assert(0);
}
for (i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) {
if (strcasecmp(mediaRequested, SupportedMediaSizes[i].PCL6Name) == 0) {
myPageInfo->mediaWidth = floorf(
_MI_TO_POINTS(SupportedMediaSizes[i].WidthInInches));
myPageInfo->mediaHeight = floorf(
_MI_TO_POINTS(SupportedMediaSizes[i].HeightInInches));
myPageInfo->mediaWidthInPixels = floorf(
_MI_TO_PIXELS(SupportedMediaSizes[i].WidthInInches,
iRenderResolutionInteger));
myPageInfo->mediaHeightInPixels = floorf(
_MI_TO_PIXELS(SupportedMediaSizes[i].HeightInInches,
iRenderResolutionInteger));
result = i;
break; // we found a match, so break out of loop
}
}
if (i == SUPPORTED_MEDIA_SIZE_COUNT) {
// media size not found, defaulting to letter
printf("PCLmGenerator get_pclm_media_size(): media size, %s, NOT FOUND, setting to letter",
mediaRequested);
result = GetPclmMediaDimensions("LETTER", myPageInfo);
}
return result;
}
void PCLmGenerator::FreeBuffer(void *pBuffer) {
if (jobOpen == job_closed && pBuffer) {
if (pBuffer == allocatedOutputBuffer) {
allocatedOutputBuffer = NULL;
}
free(pBuffer);
}
}