4 * Copyright © 2022 by Apple Inc.
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * see <https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf> for complete spec
10 * see <https://zlib.net/manual.html> for zlib documentation
13 * g++ -c -g -Os -o rastertopdf.o rastertopdf.cpp
14 * cc -o rasterToPDF rastertopdf.o -lz -lstdc++ `cups-config --libs`
16 * To build without zlib: This will produce very large pdf files.
17 * g++ -DDeflateData=0 -c -g -Os -o rastertopdf.o rastertopdf.cpp
18 * cc -o rasterToPDF rastertopdf.o -lstdc++ `cups-config --libs`
21 #include <cups/cups.h>
22 #include <cups/raster.h>
23 #include <cups/backend.h>
35 static_assert(false, "__has_include not supported");
38 #if ( !defined(DeflateData) )
39 #if __has_include(<zlib.h>)
46 #if __has_include(<zlib.h>)
49 #warning 'zlib.h' does not exits.
55 static int Canceled
= 0; /* Has the current job been canceled? */
58 static int rasterToPDFColorSpace( cups_cspace_t colorSpace
, int bitsPerPixel
, int *bitsPerComponent
, char *cs
, size_t csLen
)
64 *bitsPerComponent
= bitsPerPixel
;
65 strncpy( cs
, "[/CalGray << /Gamma 2.2 /WhitePoint[ 0.9505 1.0 1.089 ] >>]", csLen
);
69 case CUPS_CSPACE_SRGB
:
70 *bitsPerComponent
= bitsPerPixel
/3;
71 strncpy( cs
, "[/CalRGB <<\n"
72 " /Gamma[ 2.2 2.2 2.2 ]\n"
73 " /Matrix[ 0.4124 0.2126 0.0193\n"
74 " 0.3576 0.7152 0.1192\n"
75 " 0.1805 0.0722 0.9505 ]\n"
76 " /WhitePoint[ 0.9505 1.0 1.089 ]\n"
81 // AirPrint only requires sRGB and 2.2 gray.
82 // NOTE: This is not a general solution.
83 fprintf(stderr
, "DEBUG: Unsupported colorspace %u.\n", colorSpace
);
89 static void compressImageData(const unsigned char *inData
,
91 unsigned char **outData
,
94 if (outData
== NULL
|| outSize
== NULL
)
96 fprintf(stderr
, "Invalid Parameters, Line:%d\n", __LINE__
);
102 *outSize
= compressBound( (uLongf
)inSize
);
103 *outData
= (unsigned char *)malloc( *outSize
);
105 if (*outData
!= NULL
) err
= compress( *outData
, outSize
, inData
, inSize
);
109 fprintf( stderr
, "Failed to %s data, Line:%d\n", (*outData
? "compress" : "allocate"), __LINE__
);
111 if (*outData
) free( *outData
);
113 *outData
= (unsigned char *)inData
;
117 *outData
= (unsigned char *)inData
;
122 // MARK: - PDF Stuff -
123 static long writeImageObject(FILE *pdfFile
,
124 unsigned int imageReference
,
128 int bitsPerComponent
,
130 const unsigned char *rasterData
,
131 size_t rasterDataSize
)
133 unsigned char *data
= NULL
;
135 long objectOffset
= 0;
137 compressImageData( rasterData
, rasterDataSize
, &data
, &size
);
139 fprintf(pdfFile
, "\n%u 0 obj\n", imageReference
);
140 objectOffset
= ftell(pdfFile
);
141 fprintf(pdfFile
, "<< /Type /XObject\n"
147 " /BitsPerComponent %d\n"
148 " /Length %zu\n", width
, height
, (interpolate
? "true" : "false"), colorspace
, bitsPerComponent
, rasterDataSize
);
150 if (rasterData
!= data
)
151 fprintf(pdfFile
, " /Filter /FlateDecode\n");
153 fprintf(pdfFile
, ">>\nstream\n" );
154 fwrite( data
, size
, 1, pdfFile
);
155 fprintf(pdfFile
, "\nendstream"
158 // free the data the was allocated in compressImageData
159 if (rasterData
!= data
) free( data
);
164 static long writePageStream(FILE *pdfFile
,
165 unsigned int streamReference
,
170 long objectOffset
= 0;
171 char imageStream
[64];
173 snprintf( imageStream
, sizeof( imageStream
), "q %d 0 0 %d 0 0 cm /Im%u Do Q", width
, height
, pageNumber
);
175 fprintf(pdfFile
, "\n%u 0 obj\n", streamReference
);
176 objectOffset
= ftell(pdfFile
);
177 fprintf(pdfFile
, "<< /Length %zu >>\n"
181 "\nendobj\n", strlen(imageStream
), imageStream
);
186 static long writePageObject(FILE *pdfFile
,
187 unsigned int pageReference
,
188 unsigned int resouceReference
,
189 unsigned int contentReference
,
193 long objectOffset
= 0;
195 fprintf(pdfFile
, "\n%u 0 obj\n", pageReference
);
196 objectOffset
= ftell(pdfFile
);
197 fprintf(pdfFile
, "<< /Type /Page\n"
199 " /Resources %u 0 R\n"
200 " /Contents %u 0 R\n"
201 " /MediaBox [0 0 %d %d]\n"
202 ">>\nendobj\n", resouceReference
, contentReference
, width
, height
);
207 static long writeResourceObject(FILE *pdfFile
,
208 unsigned int rsrcReference
,
209 unsigned int contentReference
,
212 long objectOffset
= 0;
214 fprintf(pdfFile
, "\n%u 0 obj\n", rsrcReference
);
215 objectOffset
= ftell(pdfFile
);
216 fprintf(pdfFile
, "<< /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im%u %u 0 R >> >>\nendobj\n", page
, contentReference
);
221 static long writePagesObject( FILE *pdfFile
, std::vector
<unsigned int> pages
)
223 long objectOffset
= 0;
225 fprintf(pdfFile
, "\n2 0 obj\n");
226 objectOffset
= ftell(pdfFile
);
227 fprintf(pdfFile
, "<< /Type /Pages /Count %lu /Kids [", (unsigned long)pages
.size());
228 for (unsigned int i
: pages
)
230 fprintf( pdfFile
, " %d 0 R", i
);
232 fprintf(pdfFile
, " ] >>\nendobj\n");
237 static long writeCatalogObject( FILE *pdfFile
, unsigned int objectReference
)
239 long objectOffset
= 0;
241 fprintf(pdfFile
, "\n%u 0 obj\n", objectReference
);
242 objectOffset
= ftell(pdfFile
);
243 fprintf(pdfFile
, "<< /Type /Catalog /Pages 2 0 R >>\n");
244 fprintf(pdfFile
, "endobj\n");
249 static void writeTrailerObject(FILE *pdfFile
,
250 unsigned int catalogReference
,
251 unsigned long numObjects
,
254 fprintf( pdfFile
, "trailer\n"
259 "%%%%EOF\n", catalogReference
, numObjects
, startXOffset
);
262 static long writeXRefTable( FILE *pdfFile
, std::vector
<long> offsets
, long startOffset
)
264 long objectOffset
= ftell(pdfFile
);
265 fprintf( pdfFile
, "xref\n"
267 "0000000000 65535 f\n", (unsigned long)(offsets
.size() + 1) );
268 for (long offset
: offsets
)
270 fprintf( pdfFile
, "%010ld 00000 n\n", offset
- startOffset
);
275 static long writeHeader( FILE *pdfFile
)
277 fprintf(pdfFile
, "%%PDF-1.3\n");
279 return ftell(pdfFile
);
283 static int convertCUPSRasterToPDF( int rasterIn
)
285 #define kInitialImageReferenceID 10
288 unsigned int objectReference
= kInitialImageReferenceID
;
289 unsigned int catalogReference
= objectReference
++;
297 size_t largestAllocatedMemory
= 0;
298 unsigned char *rasterData
= NULL
;
300 std::vector
<unsigned int> pageReferences
;
301 std::vector
<long> objectOffsets
;
302 cups_raster_t
*rasterFile
= NULL
;
303 cups_page_header2_t pageHeader
;
305 FILE *pdfFile
= stdout
;
307 rasterFile
= cupsRasterOpen(rasterIn
, CUPS_RASTER_READ
);
308 if (rasterFile
== NULL
)
311 fprintf(stderr
, "ERROR: Error reading raster data.\n");
312 perror("DEBUG: cupsRasterOpen failed to open the file");
316 startOffset
= writeHeader( pdfFile
);
317 while ( !Canceled
&& cupsRasterReadHeader2(rasterFile
, &pageHeader
) )
319 char colorspace
[256];
320 int bitsPerComponent
= 8;
322 fprintf(stderr
, "PAGE: %d %d\n", pages
+1, pageHeader
.NumCopies
);
323 fprintf(stderr
, "DEBUG:%04d] pageHeader.colorSpace=%u, .bitsPerPixel=%u, .duplexMode=%u\n",
324 pages
, pageHeader
.cupsColorSpace
, pageHeader
.cupsBitsPerPixel
, pageHeader
.Duplex
);
325 fprintf(stderr
, "DEBUG: pageHeader.width=%u, .height=%u, .resolution=%u x %u\n",
326 pageHeader
.cupsWidth
, pageHeader
.cupsHeight
, pageHeader
.HWResolution
[0], pageHeader
.HWResolution
[1]);
328 int status
= rasterToPDFColorSpace( pageHeader
.cupsColorSpace
, pageHeader
.cupsBitsPerPixel
, &bitsPerComponent
, colorspace
, sizeof(colorspace
) );
331 fprintf( stderr
, "INFO: Unable to determine a colorspace. skipping this page.\n" );
335 size_t imageSize
= pageHeader
.cupsHeight
* pageHeader
.cupsBytesPerLine
;
336 if (imageSize
> largestAllocatedMemory
)
338 rasterData
= (unsigned char *)(rasterData
== NULL
? malloc(imageSize
) : realloc(rasterData
, imageSize
));
339 largestAllocatedMemory
= imageSize
;
342 if (rasterData
== NULL
)
344 fprintf(stderr
, "ERROR: Unable to allocate memory for page info\n");
349 size_t result
= (size_t) cupsRasterReadPixels(rasterFile
, rasterData
, (unsigned int)imageSize
);
350 if (result
!= imageSize
)
353 fprintf(stderr
, "ERROR: Unable to read print data.\n");
354 fprintf(stderr
, "DEBUG: cupsRasterReadPixels faild on page:%d (%zu of %zu bytes read)\n", pages
+1, result
, imageSize
);
358 width
= 72.0 * pageHeader
.cupsWidth
/ pageHeader
.HWResolution
[1];
359 height
= 72.0 * pageHeader
.cupsHeight
/ pageHeader
.HWResolution
[0];
361 unsigned int pageReference
= objectReference
++;
362 unsigned int rsrcReference
= objectReference
++;
363 unsigned int streamReference
= objectReference
++;
364 unsigned int imageReference
= objectReference
++;
367 offset
= writePageStream(pdfFile
, streamReference
, width
, height
, pages
+1 );
368 objectOffsets
.push_back( offset
);
370 offset
= writePageObject(pdfFile
,
376 objectOffsets
.push_back( offset
);
378 offset
= writeResourceObject(pdfFile
, rsrcReference
, imageReference
, pages
+1 );
379 objectOffsets
.push_back( offset
);
381 offset
= writeImageObject(pdfFile
,
383 pageHeader
.cupsWidth
,
384 pageHeader
.cupsHeight
,
390 objectOffsets
.push_back( offset
);
392 pageReferences
.push_back( pageReference
);
396 offset
= writePagesObject( pdfFile
, pageReferences
);
397 objectOffsets
.push_back( offset
);
399 offset
= writeCatalogObject( pdfFile
, catalogReference
);
400 objectOffsets
.push_back( offset
);
402 offset
= writeXRefTable( pdfFile
, objectOffsets
, startOffset
);
404 writeTrailerObject( pdfFile
, catalogReference
,
405 objectOffsets
.size() + 1, offset
- startOffset
);
407 if ( pdfFile
!= NULL
) fclose( pdfFile
);
408 if ( rasterFile
!= NULL
) cupsRasterClose(rasterFile
);
409 if ( rasterIn
!= -1 ) close( rasterIn
);
410 if ( rasterData
) free( rasterData
);
415 static void sigterm_handler(int sig
)
422 static void installSignalHandler( void )
425 sigset(SIGTERM
, sigterm_handler
);
426 #elif defined(HAVE_SIGACTION)
427 struct sigaction action
; /* Actions for POSIX signals */
428 memset(&action
, 0, sizeof(action
));
430 sigemptyset(&action
.sa_mask
);
431 sigaddset(&action
.sa_mask
, SIGTERM
);
433 action
.sa_handler
= sigterm_handler
;
434 sigaction(SIGTERM
, &action
, NULL
);
436 signal(SIGTERM
, sigterm_handler
);
437 #endif /* HAVE_SIGSET */
441 int main(int argc
, const char * argv
[])
446 * Make sure status messages are not buffered...
448 setbuf(stderr
, NULL
);
451 * Check the command-line...
453 if (argc
< 6 || argc
> 7)
455 fprintf(stderr
, "Usage: %s job-id user title copies options [file]\n",
457 return (CUPS_BACKEND_FAILED
);
461 * Register a signal handler to eject the current page if the
464 installSignalHandler();
466 int fd
= fileno(stdin
);
469 if ((fd
= open(argv
[6], O_RDONLY
)) < 0)
471 perror("ERROR: Unable to open file");
476 err
= convertCUPSRasterToPDF( fd
);