diff --git a/ChangeLog b/ChangeLog index 7b40e1c7..7dd3dead 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2022-02-05 Jay Berkenbilt + * Add a global user-defined string literal "_qpdf" as a shorthand + for QPDFObjectHandle::parse, allowing you to create + QPDFObjectHandle objects with + + QPDFObjectHandle oh = "<>"_qpdf; + * Expose QPDF::emptyPDF to the C API as qpdf_empty_pdf() * Add comments letting people know that the version string diff --git a/TODO b/TODO index 9bbaecc7..26d7ebc8 100644 --- a/TODO +++ b/TODO @@ -11,9 +11,6 @@ * QPDFObjectHandle: getValueAsX methods, getKeyIfDict. Plus C API. -* Add user-defined initializer `QPDFObjectHandle operator ""_qpdf` to - be like QPDFObjectHandle::parse: `auto oh = "<< /a (b) >>"_qpdf;` - * See if this has been done or is trivial with C++11 local static initializers: Secure random number generation could be made more efficient by using a local static to ensure a single random device diff --git a/examples/pdf-attach-file.cc b/examples/pdf-attach-file.cc index 0c046000..decc742f 100644 --- a/examples/pdf-attach-file.cc +++ b/examples/pdf-attach-file.cc @@ -36,16 +36,16 @@ static void process(char const* infilename, char const* password, QPDF q; q.processFile(infilename, password); - // Create an indirect object for the built-in Helvetica font. + // Create an indirect object for the built-in Helvetica font. This + // uses the qpdf literal syntax introduced in qpdf 10.6. auto f1 = q.makeIndirectObject( - QPDFObjectHandle::parse( - "<<" - " /Type /Font" - " /Subtype /Type1" - " /Name /F1" - " /BaseFont /Helvetica" - " /Encoding /WinAnsiEncoding" - ">>")); + "<<" + " /Type /Font" + " /Subtype /Type1" + " /Name /F1" + " /BaseFont /Helvetica" + " /Encoding /WinAnsiEncoding" + ">>"_qpdf); // Create a resources dictionary with fonts. This uses the new // parse introduced in qpdf 10.2 that takes a QPDF* and allows @@ -93,7 +93,7 @@ static void process(char const* infilename, char const* password, apdict.replaceKey("/Resources", QPDFObjectHandle::newDictionary()); apdict.replaceKey("/Type", QPDFObjectHandle::newName("/XObject")); apdict.replaceKey("/Subtype", QPDFObjectHandle::newName("/Form")); - apdict.replaceKey("/BBox", QPDFObjectHandle::parse("[ 0 0 20 20 ]")); + apdict.replaceKey("/BBox", "[ 0 0 20 20 ]"_qpdf); auto annot = q.makeIndirectObject( QPDFObjectHandle::parse( &q, diff --git a/examples/pdf-create.cc b/examples/pdf-create.cc index 52f02a0d..8d1e8ee6 100644 --- a/examples/pdf-create.cc +++ b/examples/pdf-create.cc @@ -182,12 +182,11 @@ void add_page(QPDFPageDocumentHelper& dh, QPDFObjectHandle font, size_t width = p->getWidth(); size_t height = p->getHeight(); QPDFObjectHandle image = QPDFObjectHandle::newStream(&pdf); - image.replaceDict(QPDFObjectHandle::parse( - "<<" - " /Type /XObject" - " /Subtype /Image" - " /BitsPerComponent 8" - ">>")); + image.replaceDict("<<" + " /Type /XObject" + " /Subtype /Image" + " /BitsPerComponent 8" + ">>"_qpdf); QPDFObjectHandle image_dict = image.getDict(); image_dict.replaceKey("/ColorSpace", newName(color_space)); image_dict.replaceKey("/Width", newInteger(width)); @@ -199,8 +198,7 @@ void add_page(QPDFPageDocumentHelper& dh, QPDFObjectHandle font, QPDFObjectHandle::newNull()); // Create direct objects as needed by the page dictionary. - QPDFObjectHandle procset = QPDFObjectHandle::parse( - "[/PDF /Text /ImageC]"); + QPDFObjectHandle procset = "[/PDF /Text /ImageC]"_qpdf; QPDFObjectHandle rfont = QPDFObjectHandle::newDictionary(); rfont.replaceKey("/F1", font); @@ -384,14 +382,13 @@ static void create_pdf(char const* filename) // Add an indirect object to contain a font descriptor for the // built-in Helvetica font. QPDFObjectHandle font = pdf.makeIndirectObject( - QPDFObjectHandle::parse( - "<<" - " /Type /Font" - " /Subtype /Type1" - " /Name /F1" - " /BaseFont /Helvetica" - " /Encoding /WinAnsiEncoding" - ">>")); + "<<" + " /Type /Font" + " /Subtype /Type1" + " /Name /F1" + " /BaseFont /Helvetica" + " /Encoding /WinAnsiEncoding" + ">>"_qpdf); std::vector color_spaces; color_spaces.push_back("/DeviceCMYK"); diff --git a/examples/pdf-overlay-page.cc b/examples/pdf-overlay-page.cc index 1e06c40c..7b8888e4 100644 --- a/examples/pdf-overlay-page.cc +++ b/examples/pdf-overlay-page.cc @@ -65,8 +65,7 @@ static void stamp_page(char const* infile, // Append the content to the page's content. Surround the // original content with q...Q to the new content from the // page's original content. - resources.mergeResources( - QPDFObjectHandle::parse("<< /XObject << >> >>")); + resources.mergeResources("<< /XObject << >> >>"_qpdf); resources.getKey("/XObject").replaceKey(name, stamp_fo); ph.addPageContents( QPDFObjectHandle::newStream(&inpdf, "q\n"), true); diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index 855c14c2..03d7cb0d 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -395,7 +395,7 @@ class QPDFObjectHandle // object syntax (obj gen R) will cause a logic_error exception to // be thrown. If object_description is provided, it will appear // in the message of any QPDFExc exception thrown for invalid - // syntax. + // syntax. See also the global `operator ""_qpdf` defined below. QPDF_DLL static QPDFObjectHandle parse(std::string const& object_str, std::string const& object_description = ""); @@ -1450,6 +1450,17 @@ class QPDFObjectHandle bool reserved; }; +#ifndef QPDF_NO_QPDF_STRING +// This is short for QPDFObjectHandle::parse, so you can do + +// auto oh = "<< /Key (value) >>"_qpdf; + +// If this is causing problems in your code, define +// QPDF_NO_QPDF_STRING to prevent the declaration from being here. +QPDF_DLL +QPDFObjectHandle operator ""_qpdf(char const* v, size_t len); +#endif // QPDF_NO_QPDF_STRING + class QPDFObjectHandle::QPDFDictItems { // This class allows C++-style iteration, including range-for diff --git a/libqpdf/QPDFFormFieldObjectHelper.cc b/libqpdf/QPDFFormFieldObjectHelper.cc index d6d8f562..2702ebcf 100644 --- a/libqpdf/QPDFFormFieldObjectHelper.cc +++ b/libqpdf/QPDFFormFieldObjectHelper.cc @@ -981,8 +981,7 @@ QPDFFormFieldObjectHelper::generateTextAppearance( AS.getDict().replaceKey("/Resources", resources); } // Use mergeResources to force /Font to be local - resources.mergeResources( - QPDFObjectHandle::parse("<< /Font << >> >>")); + resources.mergeResources("<< /Font << >> >>"_qpdf); resources.getKey("/Font").replaceKey(font_name, font); } diff --git a/libqpdf/QPDFJob.cc b/libqpdf/QPDFJob.cc index 646692d5..362db7c2 100644 --- a/libqpdf/QPDFJob.cc +++ b/libqpdf/QPDFJob.cc @@ -2281,8 +2281,7 @@ QPDFJob::doUnderOverlayForPage( from_page, cm, dest_afdh, make_afdh(from_page)); if (! new_content.empty()) { - resources.mergeResources( - QPDFObjectHandle::parse("<< /XObject << >> >>")); + resources.mergeResources("<< /XObject << >> >>"_qpdf); auto xobject = resources.getKey("/XObject"); if (xobject.isDictionary()) { diff --git a/libqpdf/QPDFNameTreeObjectHelper.cc b/libqpdf/QPDFNameTreeObjectHelper.cc index d39a8d86..861e01aa 100644 --- a/libqpdf/QPDFNameTreeObjectHelper.cc +++ b/libqpdf/QPDFNameTreeObjectHelper.cc @@ -61,8 +61,7 @@ QPDFNameTreeObjectHelper QPDFNameTreeObjectHelper::newEmpty(QPDF& qpdf, bool auto_repair) { return QPDFNameTreeObjectHelper( - qpdf.makeIndirectObject( - QPDFObjectHandle::parse("<< /Names [] >>")), qpdf, auto_repair); + qpdf.makeIndirectObject("<< /Names [] >>"_qpdf), qpdf, auto_repair); } QPDFNameTreeObjectHelper::iterator::iterator( diff --git a/libqpdf/QPDFNumberTreeObjectHelper.cc b/libqpdf/QPDFNumberTreeObjectHelper.cc index a8dba0ef..0f61874d 100644 --- a/libqpdf/QPDFNumberTreeObjectHelper.cc +++ b/libqpdf/QPDFNumberTreeObjectHelper.cc @@ -58,8 +58,7 @@ QPDFNumberTreeObjectHelper QPDFNumberTreeObjectHelper::newEmpty(QPDF& qpdf, bool auto_repair) { return QPDFNumberTreeObjectHelper( - qpdf.makeIndirectObject( - QPDFObjectHandle::parse("<< /Nums [] >>")), qpdf, auto_repair); + qpdf.makeIndirectObject("<< /Nums [] >>"_qpdf), qpdf, auto_repair); } QPDFNumberTreeObjectHelper::iterator::iterator( diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index d754448f..2e9bf48b 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -3666,3 +3666,10 @@ QPDFObjectHandle::QPDFArrayItems::end() { return iterator(oh, false); } + +QPDFObjectHandle +operator ""_qpdf(char const* v, size_t len) +{ + return QPDFObjectHandle::parse( + std::string(v, len), "QPDFObjectHandle literal"); +} diff --git a/libqpdf/QPDFPageDocumentHelper.cc b/libqpdf/QPDFPageDocumentHelper.cc index ab9875a9..e76c874f 100644 --- a/libqpdf/QPDFPageDocumentHelper.cc +++ b/libqpdf/QPDFPageDocumentHelper.cc @@ -161,8 +161,7 @@ QPDFPageDocumentHelper::flattenAnnotationsForPage( name, rotate, required_flags, forbidden_flags); if (! content.empty()) { - resources.mergeResources( - QPDFObjectHandle::parse("<< /XObject << >> >>")); + resources.mergeResources("<< /XObject << >> >>"_qpdf); resources.getKey("/XObject").replaceKey(name, as); ++next_fx; } diff --git a/libqpdf/QPDFPageObjectHelper.cc b/libqpdf/QPDFPageObjectHelper.cc index 0dd6d894..ab806c7f 100644 --- a/libqpdf/QPDFPageObjectHelper.cc +++ b/libqpdf/QPDFPageObjectHelper.cc @@ -500,8 +500,7 @@ QPDFPageObjectHelper::externalizeInlineImages(size_t min_size, bool shallow) QPDFObjectHandle resources = getAttribute("/Resources", true); // Calling mergeResources also ensures that /XObject becomes // direct and is not shared with other pages. - resources.mergeResources( - QPDFObjectHandle::parse("<< /XObject << >> >>")); + resources.mergeResources("<< /XObject << >> >>"_qpdf); InlineImageTracker iit(this->oh.getOwningQPDF(), min_size, resources); Pl_Buffer b("new page content"); bool filtered = false; diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index 8d28184b..2031313a 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -1255,10 +1255,8 @@ static void test_31(QPDF& pdf, char const* arg2) { // Test object parsing from a string. The input file is not used. - QPDFObjectHandle o1 = - QPDFObjectHandle::parse( - "[/name 16059 3.14159 false\n" - " << /key true /other [ (string1) (string2) ] >> null]"); + auto o1 = "[/name 16059 3.14159 false\n" + " << /key true /other [ (string1) (string2) ] >> null]"_qpdf; std::cout << o1.unparse() << std::endl; QPDFObjectHandle o2 = QPDFObjectHandle::parse(" 12345 \f "); assert(o2.isInteger() && (o2.getIntValue() == 12345));