Commit b19210fa7dbab7efa7a3cd65653be4ff2f6e08e9

Authored by Sahil Arora
Committed by Jay Berkenbilt
1 parent 3b170ab0

QPDFWriter: Add setPCLm() and writePCLm() methods

* Add support for PCLm using setPCLm() and writePCLm() methods in
  QPDFWriter.hh and QPDFWriter.cc
* Add a function writePCLmHeader() for PCLm header in QPDFWriter
include/qpdf/QPDFWriter.hh
@@ -353,6 +353,11 @@ class QPDFWriter @@ -353,6 +353,11 @@ class QPDFWriter
353 QPDF_DLL 353 QPDF_DLL
354 void setLinearization(bool); 354 void setLinearization(bool);
355 355
  356 + // Create PCLm output. Enables writing unreferenced objects,
  357 + // set PCLm header and writes pages before file catalog and page tree.
  358 + QPDF_DLL
  359 + void setPCLm(bool);
  360 +
356 QPDF_DLL 361 QPDF_DLL
357 void write(); 362 void write();
358 363
@@ -417,9 +422,11 @@ class QPDFWriter @@ -417,9 +422,11 @@ class QPDFWriter
417 void prepareFileForWrite(); 422 void prepareFileForWrite();
418 void writeStandard(); 423 void writeStandard();
419 void writeLinearized(); 424 void writeLinearized();
  425 + void writePCLm();
420 void enqueuePart(std::vector<QPDFObjectHandle>& part); 426 void enqueuePart(std::vector<QPDFObjectHandle>& part);
421 void writeEncryptionDictionary(); 427 void writeEncryptionDictionary();
422 void writeHeader(); 428 void writeHeader();
  429 + void writePCLmHeader();
423 void writeHintStream(int hint_id); 430 void writeHintStream(int hint_id);
424 qpdf_offset_t writeXRefTable( 431 qpdf_offset_t writeXRefTable(
425 trailer_e which, int first, int last, int size); 432 trailer_e which, int first, int last, int size);
@@ -492,6 +499,7 @@ class QPDFWriter @@ -492,6 +499,7 @@ class QPDFWriter
492 bool encrypted; 499 bool encrypted;
493 bool preserve_encryption; 500 bool preserve_encryption;
494 bool linearized; 501 bool linearized;
  502 + bool pclm;
495 qpdf_object_stream_e object_stream_mode; 503 qpdf_object_stream_e object_stream_mode;
496 std::string encryption_key; 504 std::string encryption_key;
497 bool encrypt_metadata; 505 bool encrypt_metadata;
libqpdf/QPDFWriter.cc
@@ -67,6 +67,7 @@ QPDFWriter::init() @@ -67,6 +67,7 @@ QPDFWriter::init()
67 encrypted = false; 67 encrypted = false;
68 preserve_encryption = true; 68 preserve_encryption = true;
69 linearized = false; 69 linearized = false;
  70 + pclm = false;
70 object_stream_mode = qpdf_o_preserve; 71 object_stream_mode = qpdf_o_preserve;
71 encrypt_metadata = true; 72 encrypt_metadata = true;
72 encrypt_use_aes = false; 73 encrypt_use_aes = false;
@@ -347,6 +348,20 @@ void @@ -347,6 +348,20 @@ void
347 QPDFWriter::setLinearization(bool val) 348 QPDFWriter::setLinearization(bool val)
348 { 349 {
349 this->linearized = val; 350 this->linearized = val;
  351 + if (val)
  352 + {
  353 + this->pclm = false;
  354 + }
  355 +}
  356 +
  357 +void
  358 +QPDFWriter::setPCLm(bool val)
  359 +{
  360 + this->pclm = val;
  361 + if (val)
  362 + {
  363 + this->linearized = false;
  364 + }
350 } 365 }
351 366
352 void 367 void
@@ -2290,6 +2305,12 @@ QPDFWriter::write() @@ -2290,6 +2305,12 @@ QPDFWriter::write()
2290 this->qdf_mode = false; 2305 this->qdf_mode = false;
2291 } 2306 }
2292 2307
  2308 + if (this->pclm)
  2309 + {
  2310 + setStreamDataMode(qpdf_s_preserve);
  2311 + this->encrypted = false;
  2312 + }
  2313 +
2293 if (this->qdf_mode) 2314 if (this->qdf_mode)
2294 { 2315 {
2295 if (! this->normalize_content_set) 2316 if (! this->normalize_content_set)
@@ -2428,6 +2449,10 @@ QPDFWriter::write() @@ -2428,6 +2449,10 @@ QPDFWriter::write()
2428 { 2449 {
2429 writeLinearized(); 2450 writeLinearized();
2430 } 2451 }
  2452 + else if (this->pclm)
  2453 + {
  2454 + writePCLm();
  2455 + }
2431 else 2456 else
2432 { 2457 {
2433 writeStandard(); 2458 writeStandard();
@@ -2502,6 +2527,26 @@ QPDFWriter::writeHeader() @@ -2502,6 +2527,26 @@ QPDFWriter::writeHeader()
2502 } 2527 }
2503 2528
2504 void 2529 void
  2530 +QPDFWriter::writePCLmHeader()
  2531 +{
  2532 + setMinimumPDFVersion(pdf.getPDFVersion(), pdf.getExtensionLevel());
  2533 + this->final_pdf_version = this->min_pdf_version;
  2534 + this->final_extension_level = this->min_extension_level;
  2535 + if (! this->forced_pdf_version.empty())
  2536 + {
  2537 + QTC::TC("qpdf", "QPDFWriter using forced PDF version");
  2538 + this->final_pdf_version = this->forced_pdf_version;
  2539 + this->final_extension_level = this->forced_extension_level;
  2540 + }
  2541 +
  2542 + writeString("%PDF-");
  2543 + writeString(this->final_pdf_version);
  2544 + // PCLm version
  2545 + writeString("\n%PCLm 1.0\n");
  2546 + writeStringQDF("%QDF-1.0\n\n");
  2547 +}
  2548 +
  2549 +void
2505 QPDFWriter::writeHintStream(int hint_id) 2550 QPDFWriter::writeHintStream(int hint_id)
2506 { 2551 {
2507 PointerHolder<Buffer> hint_buffer; 2552 PointerHolder<Buffer> hint_buffer;
@@ -3205,3 +3250,84 @@ QPDFWriter::writeStandard() @@ -3205,3 +3250,84 @@ QPDFWriter::writeStandard()
3205 assert(this->md5_pipeline == 0); 3250 assert(this->md5_pipeline == 0);
3206 } 3251 }
3207 } 3252 }
  3253 +
  3254 +void
  3255 +QPDFWriter::writePCLm()
  3256 +{
  3257 + if (this->deterministic_id)
  3258 + {
  3259 + pushMD5Pipeline();
  3260 + }
  3261 +
  3262 + // Start writing
  3263 +
  3264 + writePCLmHeader();
  3265 + writeString(this->extra_header_text);
  3266 +
  3267 + // Image transform stream content for page strip images.
  3268 + // Each of this new stream has to come after every page image
  3269 + // strip written in the pclm file.
  3270 + std::string image_transform_content = "q /image Do Q\n";
  3271 +
  3272 + // enqueue all pages first
  3273 + std::vector<QPDFObjectHandle> all = this->pdf.getAllPages();
  3274 + for (std::vector<QPDFObjectHandle>::iterator iter = all.begin();
  3275 + iter != all.end(); ++iter)
  3276 + {
  3277 + // enqueue page
  3278 + enqueueObject(*iter);
  3279 +
  3280 + // enqueue page contents stream
  3281 + enqueueObject((*iter).getKey("/Contents"));
  3282 +
  3283 + // enqueue all the strips for each page
  3284 + QPDFObjectHandle strips =
  3285 + (*iter).getKey("/Resources").getKey("/XObject");
  3286 + std::set<std::string> keys = strips.getKeys();
  3287 + for (std::set<std::string>::iterator image = keys.begin();
  3288 + image != keys.end(); ++image)
  3289 + {
  3290 + enqueueObject(strips.getKey(*image));
  3291 + enqueueObject(QPDFObjectHandle::newStream(
  3292 + &pdf, image_transform_content));
  3293 + }
  3294 + }
  3295 +
  3296 + // Put root in queue.
  3297 + QPDFObjectHandle trailer = getTrimmedTrailer();
  3298 + enqueueObject(trailer.getKey("/Root"));
  3299 +
  3300 + // Now start walking queue, output each object
  3301 + while (this->object_queue.size())
  3302 + {
  3303 + QPDFObjectHandle cur_object = this->object_queue.front();
  3304 + this->object_queue.pop_front();
  3305 + writeObject(cur_object);
  3306 + }
  3307 +
  3308 + // Now write out xref. next_objid is now the number of objects.
  3309 + qpdf_offset_t xref_offset = this->pipeline->getCount();
  3310 + if (this->object_stream_to_objects.empty())
  3311 + {
  3312 + // Write regular cross-reference table
  3313 + writeXRefTable(t_normal, 0, this->next_objid - 1, this->next_objid);
  3314 + }
  3315 + else
  3316 + {
  3317 + // Write cross-reference stream.
  3318 + int xref_id = this->next_objid++;
  3319 + writeXRefStream(xref_id, xref_id, xref_offset, t_normal,
  3320 + 0, this->next_objid - 1, this->next_objid);
  3321 + }
  3322 + writeString("startxref\n");
  3323 + writeString(QUtil::int_to_string(xref_offset));
  3324 + writeString("\n%%EOF\n");
  3325 +
  3326 + if (this->deterministic_id)
  3327 + {
  3328 + QTC::TC("qpdf", "QPDFWriter standard deterministic ID",
  3329 + this->object_stream_to_objects.empty() ? 0 : 1);
  3330 + popPipelineStack();
  3331 + assert(this->md5_pipeline == 0);
  3332 + }
  3333 +}