diff options
Diffstat (limited to 'include')
45 files changed, 1772 insertions, 241 deletions
diff --git a/include/qpdf/Buffer.hh b/include/qpdf/Buffer.hh index c8b101fa..bb9a3eb9 100644 --- a/include/qpdf/Buffer.hh +++ b/include/qpdf/Buffer.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __BUFFER_HH__ -#define __BUFFER_HH__ +#ifndef BUFFER_HH +#define BUFFER_HH #include <qpdf/DLL.h> #include <cstring> // for size_t @@ -64,4 +64,4 @@ class Buffer unsigned char* buf; }; -#endif // __BUFFER_HH__ +#endif // BUFFER_HH diff --git a/include/qpdf/BufferInputSource.hh b/include/qpdf/BufferInputSource.hh index 954ffe36..3fb0625b 100644 --- a/include/qpdf/BufferInputSource.hh +++ b/include/qpdf/BufferInputSource.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDF_BUFFERINPUTSOURCE_HH__ -#define __QPDF_BUFFERINPUTSOURCE_HH__ +#ifndef QPDF_BUFFERINPUTSOURCE_HH +#define QPDF_BUFFERINPUTSOURCE_HH #include <qpdf/InputSource.hh> #include <qpdf/Buffer.hh> @@ -60,4 +60,4 @@ class BufferInputSource: public InputSource qpdf_offset_t cur_offset; }; -#endif // __QPDF_BUFFERINPUTSOURCE_HH__ +#endif // QPDF_BUFFERINPUTSOURCE_HH diff --git a/include/qpdf/ClosedFileInputSource.hh b/include/qpdf/ClosedFileInputSource.hh index 1e3ff82f..ee8557fc 100644 --- a/include/qpdf/ClosedFileInputSource.hh +++ b/include/qpdf/ClosedFileInputSource.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDF_CLOSEDFILEINPUTSOURCE_HH__ -#define __QPDF_CLOSEDFILEINPUTSOURCE_HH__ +#ifndef QPDF_CLOSEDFILEINPUTSOURCE_HH +#define QPDF_CLOSEDFILEINPUTSOURCE_HH // This is an input source that reads from files, like // FileInputSource, except that it opens and close the file @@ -55,6 +55,13 @@ class ClosedFileInputSource: public InputSource QPDF_DLL virtual void unreadCh(char ch); + // The file stays open between calls to stayOpen(true) and + // stayOpen(false). You can use this to surround multiple + // operations on a single ClosedFileInputSource to reduce the + // overhead of a separate open/close on each call. + QPDF_DLL + void stayOpen(bool); + private: ClosedFileInputSource(ClosedFileInputSource const&); ClosedFileInputSource& operator=(ClosedFileInputSource const&); @@ -76,8 +83,9 @@ class ClosedFileInputSource: public InputSource std::string filename; qpdf_offset_t offset; FileInputSource* fis; + bool stay_open; }; PointerHolder<Members> m; }; -#endif // __QPDF_CLOSEDFILEINPUTSOURCE_HH__ +#endif // QPDF_CLOSEDFILEINPUTSOURCE_HH diff --git a/include/qpdf/Constants.h b/include/qpdf/Constants.h index 0191a411..1428216c 100644 --- a/include/qpdf/Constants.h +++ b/include/qpdf/Constants.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2005-2018 Jay Berkenbilt +/* Copyright (c) 2005-2019 Jay Berkenbilt * * This file is part of qpdf. * @@ -20,8 +20,8 @@ * see the manual for additional information. */ -#ifndef __QPDFCONSTANTS_H__ -#define __QPDFCONSTANTS_H__ +#ifndef QPDFCONSTANTS_H +#define QPDFCONSTANTS_H /* Keep this file 'C' compatible so it can be used from the C and C++ * interfaces. @@ -80,13 +80,67 @@ enum qpdf_r3_print_e qpdf_r3p_low, /* allow only low-resolution printing */ qpdf_r3p_none /* allow no printing */ }; + +/* qpdf_r3_modify_e doesn't allow the full flexibility of the spec. It + * corresponds to options in Acrobat 5's menus. The new interface in + * QPDFWriter offers more granularity and no longer uses this type. + */ enum qpdf_r3_modify_e /* Allowed changes: */ { - qpdf_r3m_all = 0, /* General editing, comments, forms */ - qpdf_r3m_annotate, /* Comments, form field fill-in, and signing */ - qpdf_r3m_form, /* form field fill-in and signing */ - qpdf_r3m_assembly, /* only document assembly */ - qpdf_r3m_none /* no modifications */ + qpdf_r3m_all = 0, /* All editing */ + qpdf_r3m_annotate, /* Comments, fill forms, signing, assembly */ + qpdf_r3m_form, /* Fill forms, signing, assembly */ + qpdf_r3m_assembly, /* Only document assembly */ + qpdf_r3m_none /* No modifications */ +}; + +/* Form field flags from the PDF spec */ + +enum pdf_form_field_flag_e +{ + /* flags that apply to all form fields */ + ff_all_read_only = 1 << 0, + ff_all_required = 1 << 1, + ff_all_no_export = 1 << 2, + + /* flags that apply to fields of type /Btn (button) */ + ff_btn_no_toggle_off = 1 << 14, + ff_btn_radio = 1 << 15, + ff_btn_pushbutton = 1 << 16, + ff_btn_radios_in_unison = 1 << 17, + + /* flags that apply to fields of type /Tx (text) */ + ff_tx_multiline = 1 << 12, + ff_tx_password = 1 << 13, + ff_tx_file_select = 1 << 20, + ff_tx_do_not_spell_check = 1 << 22, + ff_tx_do_not_scroll = 1 << 23, + ff_tx_comb = 1 << 24, + ff_tx_rich_text = 1 << 25, + + /* flags that apply to fields of type /Ch (choice) */ + ff_ch_combo = 1 << 17, + ff_ch_edit = 1 << 18, + ff_ch_sort = 1 << 19, + ff_ch_multi_select = 1 << 21, + ff_ch_do_not_spell_check = 1 << 22, + ff_ch_commit_on_sel_change = 1 << 26 +}; + +/* Annotation flags from the PDF spec */ + +enum pdf_annotation_flag_e +{ + an_invisible = 1 << 0, + an_hidden = 1 << 1, + an_print = 1 << 2, + an_no_zoom = 1 << 3, + an_no_rotate = 1 << 4, + an_no_view = 1 << 5, + an_read_only = 1 << 6, + an_locked = 1 << 7, + an_toggle_no_view = 1 << 8, + an_locked_contents = 1 << 9 }; -#endif /* __QPDFCONSTANTS_H__ */ +#endif /* QPDFCONSTANTS_H */ diff --git a/include/qpdf/DLL.h b/include/qpdf/DLL.h index a5f72f6a..ccd05fce 100644 --- a/include/qpdf/DLL.h +++ b/include/qpdf/DLL.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2005-2018 Jay Berkenbilt +/* Copyright (c) 2005-2019 Jay Berkenbilt * * This file is part of qpdf. * @@ -20,8 +20,8 @@ * see the manual for additional information. */ -#ifndef __QPDF_DLL_HH__ -#define __QPDF_DLL_HH__ +#ifndef QPDF_DLL_HH +#define QPDF_DLL_HH #if defined(_WIN32) && defined(DLL_EXPORT) # define QPDF_DLL __declspec(dllexport) @@ -29,4 +29,4 @@ # define QPDF_DLL #endif -#endif /* __QPDF_DLL_HH__ */ +#endif /* QPDF_DLL_HH */ diff --git a/include/qpdf/FileInputSource.hh b/include/qpdf/FileInputSource.hh index 4bc51262..0845f241 100644 --- a/include/qpdf/FileInputSource.hh +++ b/include/qpdf/FileInputSource.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDF_FILEINPUTSOURCE_HH__ -#define __QPDF_FILEINPUTSOURCE_HH__ +#ifndef QPDF_FILEINPUTSOURCE_HH +#define QPDF_FILEINPUTSOURCE_HH #include <qpdf/InputSource.hh> @@ -61,4 +61,4 @@ class FileInputSource: public InputSource FILE* file; }; -#endif // __QPDF_FILEINPUTSOURCE_HH__ +#endif // QPDF_FILEINPUTSOURCE_HH diff --git a/include/qpdf/InputSource.hh b/include/qpdf/InputSource.hh index 2fd95977..8465ad55 100644 --- a/include/qpdf/InputSource.hh +++ b/include/qpdf/InputSource.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDF_INPUTSOURCE_HH__ -#define __QPDF_INPUTSOURCE_HH__ +#ifndef QPDF_INPUTSOURCE_HH +#define QPDF_INPUTSOURCE_HH #include <qpdf/DLL.h> #include <qpdf/Types.h> @@ -88,4 +88,4 @@ class InputSource qpdf_offset_t last_offset; }; -#endif // __QPDF_INPUTSOURCE_HH__ +#endif // QPDF_INPUTSOURCE_HH diff --git a/include/qpdf/JSON.hh b/include/qpdf/JSON.hh new file mode 100644 index 00000000..50bc78b6 --- /dev/null +++ b/include/qpdf/JSON.hh @@ -0,0 +1,170 @@ +// Copyright (c) 2005-2019 Jay Berkenbilt +// +// This file is part of qpdf. +// +// 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. +// +// Versions of qpdf prior to version 7 were released under the terms +// of version 2.0 of the Artistic License. At your option, you may +// continue to consider qpdf to be licensed under those terms. Please +// see the manual for additional information. + +#ifndef JSON_HH +#define JSON_HH + +// This is a simple JSON serializer, primarily designed for +// serializing QPDF Objects as JSON. JSON objects contain their data +// as smart pointers. One JSON object is added to another, this +// pointer is copied. This means you can create temporary JSON objects +// on the stack, add them to other objects, and let them go out of +// scope safely. It also means that if the json JSON object is added +// in more than one place, all copies share underlying data. + +#include <qpdf/DLL.h> +#include <qpdf/PointerHolder.hh> +#include <string> +#include <map> +#include <vector> +#include <list> + +class JSON +{ + public: + QPDF_DLL + std::string unparse() const; + + // The JSON spec calls dictionaries "objects", but that creates + // too much confusion when referring to instances of the JSON + // class. + QPDF_DLL + static JSON makeDictionary(); + // addDictionaryMember returns the newly added item. + QPDF_DLL + JSON addDictionaryMember(std::string const& key, JSON const&); + QPDF_DLL + static JSON makeArray(); + // addArrayElement returns the newly added item. + QPDF_DLL + JSON addArrayElement(JSON const&); + QPDF_DLL + static JSON makeString(std::string const& utf8); + QPDF_DLL + static JSON makeInt(long long int value); + QPDF_DLL + static JSON makeReal(double value); + QPDF_DLL + static JSON makeNumber(std::string const& encoded); + QPDF_DLL + static JSON makeBool(bool value); + QPDF_DLL + static JSON makeNull(); + + // Check this JSON object against a "schema". This is not a schema + // according to any standard. It's just a template of what the + // JSON is supposed to contain. The checking does the following: + // + // * The schema is a nested structure containing dictionaries, + // single-element arrays, and strings only. + // * Recursively walk the schema + // * If the current value is a dictionary, this object must have + // a dictionary in the same place with the same keys + // * If the current value is an array, this object must have an + // array in the same place. The schema's array must contain a + // single element, which is used as a schema to validate each + // element of this object's corresponding array. + // * Otherwise, the value is ignored. + // + // QPDF's JSON output conforms to certain strict compatibility + // rules as discussed in the manual. The idea is that a JSON + // structure created manually in qpdf.cc doubles as both JSON help + // information and a schema for validating the JSON that qpdf + // generates. Any discrepancies are a bug in qpdf. + QPDF_DLL + bool checkSchema(JSON schema, std::list<std::string>& errors); + + private: + static std::string encode_string(std::string const& utf8); + + struct JSON_value + { + virtual ~JSON_value(); + virtual std::string unparse(size_t depth) const = 0; + }; + struct JSON_dictionary: public JSON_value + { + virtual ~JSON_dictionary(); + virtual std::string unparse(size_t depth) const; + std::map<std::string, PointerHolder<JSON_value> > members; + }; + struct JSON_array: public JSON_value + { + virtual ~JSON_array(); + virtual std::string unparse(size_t depth) const; + std::vector<PointerHolder<JSON_value> > elements; + }; + struct JSON_string: public JSON_value + { + JSON_string(std::string const& utf8); + virtual ~JSON_string(); + virtual std::string unparse(size_t depth) const; + std::string encoded; + }; + struct JSON_number: public JSON_value + { + JSON_number(long long val); + JSON_number(double val); + JSON_number(std::string const& val); + virtual ~JSON_number(); + virtual std::string unparse(size_t depth) const; + std::string encoded; + }; + struct JSON_bool: public JSON_value + { + JSON_bool(bool val); + virtual ~JSON_bool(); + virtual std::string unparse(size_t depth) const; + bool value; + }; + struct JSON_null: public JSON_value + { + virtual ~JSON_null(); + virtual std::string unparse(size_t depth) const; + }; + + JSON(PointerHolder<JSON_value>); + + static bool + checkSchemaInternal(JSON_value* this_v, JSON_value* sch_v, + std::list<std::string>& errors, + std::string prefix); + + class Members + { + friend class JSON; + + public: + QPDF_DLL + ~Members(); + + private: + Members(PointerHolder<JSON_value>); + Members(Members const&); + + PointerHolder<JSON_value> value; + }; + + PointerHolder<Members> m; +}; + + +#endif // JSON_HH diff --git a/include/qpdf/Pipeline.hh b/include/qpdf/Pipeline.hh index 105dd7cb..b7bc5a7e 100644 --- a/include/qpdf/Pipeline.hh +++ b/include/qpdf/Pipeline.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -41,8 +41,8 @@ // are not. It is up to the caller to use a pipeline according to its // own restrictions. -#ifndef __PIPELINE_HH__ -#define __PIPELINE_HH__ +#ifndef PIPELINE_HH +#define PIPELINE_HH #include <qpdf/DLL.h> #include <string> @@ -82,4 +82,4 @@ class Pipeline Pipeline* next; }; -#endif // __PIPELINE_HH__ +#endif // PIPELINE_HH diff --git a/include/qpdf/Pl_Buffer.hh b/include/qpdf/Pl_Buffer.hh index 7b49a73a..5eb143b2 100644 --- a/include/qpdf/Pl_Buffer.hh +++ b/include/qpdf/Pl_Buffer.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __PL_BUFFER_HH__ -#define __PL_BUFFER_HH__ +#ifndef PL_BUFFER_HH +#define PL_BUFFER_HH // This pipeline accumulates the data passed to it into a memory // buffer. Each subsequent use of this buffer appends to the data @@ -62,4 +62,4 @@ class Pl_Buffer: public Pipeline size_t total_size; }; -#endif // __PL_BUFFER_HH__ +#endif // PL_BUFFER_HH diff --git a/include/qpdf/Pl_Concatenate.hh b/include/qpdf/Pl_Concatenate.hh index 32f87a4a..26e5849e 100644 --- a/include/qpdf/Pl_Concatenate.hh +++ b/include/qpdf/Pl_Concatenate.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __PL_CONCATENATE_HH__ -#define __PL_CONCATENATE_HH__ +#ifndef PL_CONCATENATE_HH +#define PL_CONCATENATE_HH // This pipeline will drop all regular finished calls rather than // passing them onto next. To finish downstream streams, call @@ -50,4 +50,4 @@ class Pl_Concatenate: public Pipeline void manualFinish(); }; -#endif // __PL_CONCATENATE_HH__ +#endif // PL_CONCATENATE_HH diff --git a/include/qpdf/Pl_Count.hh b/include/qpdf/Pl_Count.hh index 7b78b326..c3f7b3e1 100644 --- a/include/qpdf/Pl_Count.hh +++ b/include/qpdf/Pl_Count.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __PL_COUNT_HH__ -#define __PL_COUNT_HH__ +#ifndef PL_COUNT_HH +#define PL_COUNT_HH // This pipeline is reusable; i.e., it is safe to call write() after // calling finish(). @@ -52,4 +52,4 @@ class Pl_Count: public Pipeline unsigned char last_char; }; -#endif // __PL_COUNT_HH__ +#endif // PL_COUNT_HH diff --git a/include/qpdf/Pl_DCT.hh b/include/qpdf/Pl_DCT.hh index 984aad96..665a2b01 100644 --- a/include/qpdf/Pl_DCT.hh +++ b/include/qpdf/Pl_DCT.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __PL_DCT_HH__ -#define __PL_DCT_HH__ +#ifndef PL_DCT_HH +#define PL_DCT_HH #include <qpdf/Pipeline.hh> #include <qpdf/Pl_Buffer.hh> @@ -81,4 +81,4 @@ class Pl_DCT: public Pipeline }; -#endif // __PL_DCT_HH__ +#endif // PL_DCT_HH diff --git a/include/qpdf/Pl_Discard.hh b/include/qpdf/Pl_Discard.hh index 3c15650c..129431b2 100644 --- a/include/qpdf/Pl_Discard.hh +++ b/include/qpdf/Pl_Discard.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __PL_DISCARD_HH__ -#define __PL_DISCARD_HH__ +#ifndef PL_DISCARD_HH +#define PL_DISCARD_HH // This pipeline discards its output. It is an end-of-line pipeline // (with no next). @@ -43,4 +43,4 @@ class Pl_Discard: public Pipeline virtual void finish(); }; -#endif // __PL_DISCARD_HH__ +#endif // PL_DISCARD_HH diff --git a/include/qpdf/Pl_Flate.hh b/include/qpdf/Pl_Flate.hh index cbc44bcf..fdd66996 100644 --- a/include/qpdf/Pl_Flate.hh +++ b/include/qpdf/Pl_Flate.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __PL_FLATE_HH__ -#define __PL_FLATE_HH__ +#ifndef PL_FLATE_HH +#define PL_FLATE_HH #include <qpdf/Pipeline.hh> @@ -53,4 +53,4 @@ class Pl_Flate: public Pipeline void* zdata; }; -#endif // __PL_FLATE_HH__ +#endif // PL_FLATE_HH diff --git a/include/qpdf/Pl_QPDFTokenizer.hh b/include/qpdf/Pl_QPDFTokenizer.hh index 65dc7919..a571b079 100644 --- a/include/qpdf/Pl_QPDFTokenizer.hh +++ b/include/qpdf/Pl_QPDFTokenizer.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,14 +19,15 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __PL_QPDFTOKENIZER_HH__ -#define __PL_QPDFTOKENIZER_HH__ +#ifndef PL_QPDFTOKENIZER_HH +#define PL_QPDFTOKENIZER_HH #include <qpdf/Pipeline.hh> #include <qpdf/QPDFTokenizer.hh> #include <qpdf/PointerHolder.hh> #include <qpdf/QPDFObjectHandle.hh> +#include <qpdf/Pl_Buffer.hh> // Tokenize the incoming text using QPDFTokenizer and pass the tokens // in turn to a QPDFObjectHandle::TokenFilter object. All bytes of @@ -44,22 +45,24 @@ class Pl_QPDFTokenizer: public Pipeline // Whatever pipeline is provided as "next" will be set as the // pipeline that the token filter writes to. If next is not // provided, any output written by the filter will be discarded. + QPDF_DLL Pl_QPDFTokenizer(char const* identifier, QPDFObjectHandle::TokenFilter* filter, Pipeline* next = 0); + QPDF_DLL virtual ~Pl_QPDFTokenizer(); + QPDF_DLL virtual void write(unsigned char* buf, size_t len); + QPDF_DLL virtual void finish(); private: - void processChar(char ch); - void checkUnread(); - class Members { friend class Pl_QPDFTokenizer; public: + QPDF_DLL ~Members(); private: @@ -68,11 +71,9 @@ class Pl_QPDFTokenizer: public Pipeline QPDFObjectHandle::TokenFilter* filter; QPDFTokenizer tokenizer; - bool last_char_was_cr; - bool unread_char; - char char_to_unread; + Pl_Buffer buf; }; PointerHolder<Members> m; }; -#endif // __PL_QPDFTOKENIZER_HH__ +#endif // PL_QPDFTOKENIZER_HH diff --git a/include/qpdf/Pl_RunLength.hh b/include/qpdf/Pl_RunLength.hh index be30ed22..b8e696b6 100644 --- a/include/qpdf/Pl_RunLength.hh +++ b/include/qpdf/Pl_RunLength.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __PL_RUNLENGTH_HH__ -#define __PL_RUNLENGTH_HH__ +#ifndef PL_RUNLENGTH_HH +#define PL_RUNLENGTH_HH #include <qpdf/Pipeline.hh> @@ -53,4 +53,4 @@ class Pl_RunLength: public Pipeline unsigned int length; }; -#endif // __PL_RUNLENGTH_HH__ +#endif // PL_RUNLENGTH_HH diff --git a/include/qpdf/Pl_StdioFile.hh b/include/qpdf/Pl_StdioFile.hh index b89ead43..fd228333 100644 --- a/include/qpdf/Pl_StdioFile.hh +++ b/include/qpdf/Pl_StdioFile.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -21,8 +21,8 @@ // End-of-line pipeline that simply writes its data to a stdio FILE* object. -#ifndef __PL_STDIOFILE_HH__ -#define __PL_STDIOFILE_HH__ +#ifndef PL_STDIOFILE_HH +#define PL_STDIOFILE_HH #include <qpdf/Pipeline.hh> @@ -51,4 +51,4 @@ class Pl_StdioFile: public Pipeline FILE* file; }; -#endif // __PL_STDIOFILE_HH__ +#endif // PL_STDIOFILE_HH diff --git a/include/qpdf/PointerHolder.hh b/include/qpdf/PointerHolder.hh index 99c12878..18833773 100644 --- a/include/qpdf/PointerHolder.hh +++ b/include/qpdf/PointerHolder.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __POINTERHOLDER_HH__ -#define __POINTERHOLDER_HH__ +#ifndef POINTERHOLDER_HH +#define POINTERHOLDER_HH // This class is basically boost::shared_pointer but predates that by // several years. @@ -182,4 +182,4 @@ class PointerHolder Data* data; }; -#endif // __POINTERHOLDER_HH__ +#endif // POINTERHOLDER_HH diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index 681d233c..634c9bd1 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDF_HH__ -#define __QPDF_HH__ +#ifndef QPDF_HH +#define QPDF_HH #include <qpdf/DLL.h> #include <qpdf/Types.h> @@ -160,6 +160,39 @@ class QPDF QPDF_DLL void setAttemptRecovery(bool); + // Tell other QPDF objects that streams copied from this QPDF need + // to be fully copied when copyForeignObject is called on them. + // Calling setIgnoreXRefStreams(true) on a QPDF object makes it + // possible for the object and its input source to disappear + // before streams copied from it are written with the destination + // QPDF object. Confused? Ordinarily, if you are going to copy + // objects from a source QPDF object to a destination QPDF object + // using copyForeignObject or addPage, the source object's input + // source must stick around until after the destination PDF is + // written. If you call this method on the source QPDF object, it + // sends a signal to the destination object that it must fully + // copy the stream data when copyForeignObject. It will do this by + // making a copy in RAM. Ordinarily the stream data is copied + // lazily to avoid unnecessary duplication of the stream data. + // Note that the stream data is copied into RAM only once + // regardless of how many objects the stream is copied into. The + // result is that, if you called setImmediateCopyFrom(true) on a + // given QPDF object prior to copying any of its streams, you do + // not need to keep it or its input source around after copying + // its objects to another QPDF. This is true even if the source + // streams use StreamDataProvider. Note that this method is called + // on the QPDF object you are copying FROM, not the one you are + // copying to. The reasoning for this is that there's no reason a + // given QPDF may not get objects copied to it from a variety of + // other objects, some transient and some not. Since what's + // relevant is whether the source QPDF is transient, the method + // must be called on the source QPDF, not the destination one. + // Since this method will make a copy of the stream in RAM, so be + // sure you have enough memory to simultaneously hold all the + // streams you're copying. + QPDF_DLL + void setImmediateCopyFrom(bool); + // Other public methods // Return the list of warnings that have been issued so far and @@ -170,6 +203,16 @@ class QPDF QPDF_DLL std::vector<QPDFExc> getWarnings(); + // Return an application-scoped unique ID for this QPDF object. + // This is not a globally unique ID. It is constructing using a + // timestamp and a random number and is intended to be unique + // among QPDF objects that are created by a single run of an + // application. While it's very likely that these are actually + // globally unique, it is not recommended to use them for + // long-term purposes. + QPDF_DLL + unsigned long long getUniqueId() const; + QPDF_DLL std::string getFilename() const; QPDF_DLL @@ -237,19 +280,39 @@ class QPDF replaceReserved(QPDFObjectHandle reserved, QPDFObjectHandle replacement); - // Copy an object from another QPDF to this one. Please note that - // the QPDF object containing the object being copied must stick - // around because it is still used to retrieve any stream data - // referenced by the copied objects. + // Copy an object from another QPDF to this one. Starting with + // qpdf version 8.3.0, it is no longer necessary to keep the + // original QPDF around after the call to copyForeignObject as + // long as the source of any copied stream data is still + // available. Usually this means you just have to keep the input + // file around, not the QPDF object. The exception to this is if + // you copy a stream that gets its data from a + // QPDFObjectHandle::StreamDataProvider. In this case only, the + // original stream's QPDF object must stick around because the + // QPDF object is itself the source of the original stream data. + // For a more in-depth discussion, please see the TODO file. + // Starting in 8.4.0, you can call setImmediateCopyFrom(true) on + // the SOURCE QPDF object (the one you're copying FROM). If you do + // this prior to copying any of its objects, then neither the + // source QPDF object nor its input source needs to stick around + // at all regardless of the source. The cost is that the stream + // data is copied into RAM at the time copyForeignObject is + // called. See setImmediateCopyFrom for more information. // - // The return value is an indirect reference to the copied object - // in this file. This method is intended to be used to copy - // non-page objects and will not copy page objects. To copy page - // objects, pass the foreign page object directly to addPage (or - // addPageAt). If you copy objects that contain references to - // pages, you should copy the pages first using addPage(At). - // Otherwise references to the pages that have not been copied - // will be replaced with nulls. + // The return value of this method is an indirect reference to the + // copied object in this file. This method is intended to be used + // to copy non-page objects. To copy page objects, pass the + // foreign page object directly to addPage (or addPageAt). If you + // copy objects that contain references to pages, you should copy + // the pages first using addPage(At). Otherwise references to the + // pages that have not been copied will be replaced with nulls. It + // is possible to use copyForeignObject on page objects if you are + // not going to use them as pages. Doing so copies the object + // normally but does not update the page structure. For example, + // it is a valid use case to use copyForeignObject for a page that + // you are going to turn into a form XObject, though you can also + // use QPDFPageObjectHelper::getFormXObjectForPage for that + // purpose. // When copying objects with this method, object structure will be // preserved, so all indirectly referenced indirect objects will @@ -431,9 +494,21 @@ class QPDF QPDF_DLL void showXRefTable(); + // Detect all indirect references to objects that don't exist and + // resolve them by replacing them with null, which is how the PDF + // spec says to interpret such dangling references. This method is + // called automatically if you try to add any new objects, if you + // call getAllObjects, and before a file is written. The qpdf + // object caches whether it has run this to avoid running it + // multiple times. You can pass true to force it to run again if + // you have explicitly added new objects that may have additional + // dangling references. + QPDF_DLL + void fixDanglingReferences(bool force = false); + // Return the approximate number of indirect objects. It is // approximate because not all objects in the file are preserved - // in all cases. + // in all cases, and gaps in object numbering are not preserved. QPDF_DLL size_t getObjectCount(); @@ -458,15 +533,16 @@ class QPDF void optimize(std::map<int, int> const& object_stream_data, bool allow_changes = true); - // Traverse page tree return all /Page objects. For efficiency, - // this method returns a const reference to an internal vector of - // pages. Calls to addPage, addPageAt, and removePage safely - // update this, but directly manipulation of the pages three or - // pushing inheritable objects to the page level may invalidate - // it. See comments for updateAllPagesCache() for additional - // notes. Newer code should use - // QPDFPageDocumentHelper::getAllPages instead. The decision to - // expose this internal cache was arguably incorrect, but it is + // Traverse page tree return all /Page objects. It also detects + // and resolves cases in which the same /Page object is + // duplicated. For efficiency, this method returns a const + // reference to an internal vector of pages. Calls to addPage, + // addPageAt, and removePage safely update this, but directly + // manipulation of the pages three or pushing inheritable objects + // to the page level may invalidate it. See comments for + // updateAllPagesCache() for additional notes. Newer code should + // use QPDFPageDocumentHelper::getAllPages instead. The decision + // to expose this internal cache was arguably incorrect, but it is // being left here for compatibility. It is, however, completely // safe to use this for files that you are not modifying. QPDF_DLL @@ -629,9 +705,59 @@ class QPDF std::set<QPDFObjGen> visiting; }; + class EncryptionParameters + { + friend class QPDF; + public: + EncryptionParameters(); + + private: + bool encrypted; + bool encryption_initialized; + int encryption_V; + int encryption_R; + bool encrypt_metadata; + std::map<std::string, encryption_method_e> crypt_filters; + encryption_method_e cf_stream; + encryption_method_e cf_string; + encryption_method_e cf_file; + std::string provided_password; + std::string user_password; + std::string encryption_key; + std::string cached_object_encryption_key; + int cached_key_objid; + int cached_key_generation; + }; + + class ForeignStreamData + { + friend class QPDF; + public: + ForeignStreamData( + PointerHolder<EncryptionParameters> encp, + PointerHolder<InputSource> file, + int foreign_objid, + int foreign_generation, + qpdf_offset_t offset, + size_t length, + bool is_attachment_stream, + QPDFObjectHandle local_dict); + + private: + PointerHolder<EncryptionParameters> encp; + PointerHolder<InputSource> file; + int foreign_objid; + int foreign_generation; + qpdf_offset_t offset; + size_t length; + bool is_attachment_stream; + QPDFObjectHandle local_dict; + }; + class CopiedStreamDataProvider: public QPDFObjectHandle::StreamDataProvider { public: + CopiedStreamDataProvider(QPDF& destination_qpdf); virtual ~CopiedStreamDataProvider() { } @@ -639,9 +765,14 @@ class QPDF Pipeline* pipeline); void registerForeignStream(QPDFObjGen const& local_og, QPDFObjectHandle foreign_stream); + void registerForeignStream(QPDFObjGen const& local_og, + PointerHolder<ForeignStreamData>); private: + QPDF& destination_qpdf; std::map<QPDFObjGen, QPDFObjectHandle> foreign_streams; + std::map<QPDFObjGen, + PointerHolder<ForeignStreamData> > foreign_stream_data; }; class StringDecrypter: public QPDFObjectHandle::StringDecrypter @@ -714,6 +845,7 @@ class QPDF PointerHolder<QPDFObject> resolve(int objid, int generation); void resolveObjectsInStream(int obj_stream_number); void findAttachmentStreams(); + void stopOnError(std::string const& message); // Calls finish() on the pipeline when done but does not delete it bool pipeStreamData(int objid, int generation, @@ -722,6 +854,21 @@ class QPDF Pipeline* pipeline, bool suppress_warnings, bool will_retry); + bool pipeForeignStreamData( + PointerHolder<ForeignStreamData>, + Pipeline*, + unsigned long encode_flags, + qpdf_stream_decode_level_e decode_level); + static bool pipeStreamData(PointerHolder<QPDF::EncryptionParameters> encp, + PointerHolder<InputSource> file, + QPDF& qpdf_for_warning, + int objid, int generation, + qpdf_offset_t offset, size_t length, + QPDFObjectHandle dict, + bool is_attachment_stream, + Pipeline* pipeline, + bool suppress_warnings, + bool will_retry); // For QPDFWriter: @@ -751,10 +898,9 @@ class QPDF // methods to support page handling void getAllPagesInternal(QPDFObjectHandle cur_pages, - std::vector<QPDFObjectHandle>& result); - void getAllPagesInternal2(QPDFObjectHandle cur_pages, - std::vector<QPDFObjectHandle>& result, - std::set<QPDFObjGen>& visited); + std::vector<QPDFObjectHandle>& result, + std::set<QPDFObjGen>& visited, + std::set<QPDFObjGen>& seen); void insertPage(QPDFObjectHandle newpage, int pos); int findPage(QPDFObjGen const& og); int findPage(QPDFObjectHandle& page); @@ -763,9 +909,12 @@ class QPDF bool check_duplicate); // methods to support encryption -- implemented in QPDF_encryption.cc - encryption_method_e interpretCF(QPDFObjectHandle); + static encryption_method_e interpretCF( + PointerHolder<EncryptionParameters> encp, QPDFObjectHandle); void initializeEncryption(); - std::string getKeyForObject(int objid, int generation, bool use_aes); + static std::string getKeyForObject( + PointerHolder<EncryptionParameters> encp, + int objid, int generation, bool use_aes); void decryptString(std::string&, int objid, int generation); static std::string compute_encryption_key_from_password( std::string const& password, EncryptionData const& data); @@ -774,14 +923,18 @@ class QPDF static std::string recover_encryption_key_with_password( std::string const& password, EncryptionData const& data, bool& perms_valid); - void decryptStream( - Pipeline*& pipeline, int objid, int generation, - QPDFObjectHandle& stream_dict, + static void decryptStream( + PointerHolder<EncryptionParameters> encp, + PointerHolder<InputSource> file, + QPDF& qpdf_for_warning, Pipeline*& pipeline, + int objid, int generation, + QPDFObjectHandle& stream_dict, bool is_attachment_stream, std::vector<PointerHolder<Pipeline> >& heap); - // Methods to support object copying + // Unused copyForeignObject -- remove at next ABI change QPDFObjectHandle copyForeignObject( - QPDFObjectHandle foreign, bool allow_page); + QPDFObjectHandle foreign, bool unused); + // Methods to support object copying void reserveObjects(QPDFObjectHandle foreign, ObjCopier& obj_copier, bool top); QPDFObjectHandle replaceForeignIndirectObjects( @@ -1135,11 +1288,6 @@ class QPDF QPDFObjectHandle, std::map<std::string, std::vector<QPDFObjectHandle> >&, std::vector<QPDFObjectHandle>& all_pages, - bool allow_changes, bool warn_skipped_keys); - void pushInheritedAttributesToPageInternal2( - QPDFObjectHandle, - std::map<std::string, std::vector<QPDFObjectHandle> >&, - std::vector<QPDFObjectHandle>& all_pages, bool allow_changes, bool warn_skipped_keys, std::set<QPDFObjGen>& visited); void updateObjectMaps(ObjUser const& ou, QPDFObjectHandle oh); @@ -1152,36 +1300,24 @@ class QPDF friend class QPDF; public: + QPDF_DLL ~Members(); private: Members(); Members(Members const&); + unsigned long long unique_id; QPDFTokenizer tokenizer; PointerHolder<InputSource> file; std::string last_object_description; bool provided_password_is_hex_key; - bool encrypted; - bool encryption_initialized; bool ignore_xref_streams; bool suppress_warnings; std::ostream* out_stream; std::ostream* err_stream; bool attempt_recovery; - int encryption_V; - int encryption_R; - bool encrypt_metadata; - std::map<std::string, encryption_method_e> crypt_filters; - encryption_method_e cf_stream; - encryption_method_e cf_string; - encryption_method_e cf_file; - std::string provided_password; - std::string user_password; - std::string encryption_key; - std::string cached_object_encryption_key; - int cached_key_objid; - int cached_key_generation; + PointerHolder<EncryptionParameters> encp; std::string pdf_version; std::map<QPDFObjGen, QPDFXRefEntry> xref_table; std::set<int> deleted_objects; @@ -1192,12 +1328,14 @@ class QPDF std::map<QPDFObjGen, int> pageobj_to_pages_pos; bool pushed_inherited_attributes_to_pages; std::vector<QPDFExc> warnings; - std::map<QPDF*, ObjCopier> object_copiers; + std::map<unsigned long long, ObjCopier> object_copiers; PointerHolder<QPDFObjectHandle::StreamDataProvider> copied_streams; // copied_stream_data_provider is owned by copied_streams CopiedStreamDataProvider* copied_stream_data_provider; std::set<QPDFObjGen> attachment_streams; bool reconstructed_xref; + bool fixed_dangling_refs; + bool immediate_copy_from; // Linearization data qpdf_offset_t first_xref_item_offset; // actual value from file @@ -1239,4 +1377,4 @@ class QPDF PointerHolder<Members> m; }; -#endif // __QPDF_HH__ +#endif // QPDF_HH diff --git a/include/qpdf/QPDFAcroFormDocumentHelper.hh b/include/qpdf/QPDFAcroFormDocumentHelper.hh index 519f626b..b0e05f50 100644 --- a/include/qpdf/QPDFAcroFormDocumentHelper.hh +++ b/include/qpdf/QPDFAcroFormDocumentHelper.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDFACROFORMDOCUMENTHELPER_HH__ -#define __QPDFACROFORMDOCUMENTHELPER_HH__ +#ifndef QPDFACROFORMDOCUMENTHELPER_HH +#define QPDFACROFORMDOCUMENTHELPER_HH // This document helper is intended to help with operations on // interactive forms. Here are the key things to know: @@ -85,6 +85,10 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper public: QPDF_DLL QPDFAcroFormDocumentHelper(QPDF&); + QPDF_DLL + virtual ~QPDFAcroFormDocumentHelper() + { + } // This class lazily creates an internal cache of the mapping // among form fields, annotations, and pages. Methods within this @@ -153,6 +157,16 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper QPDF_DLL void setNeedAppearances(bool); + // If /NeedAppearances is false, do nothing. Otherwise generate + // appearance streams for all widget annotations that need them. + // See comments in QPDFFormFieldObjectHelper.hh for + // generateAppearance for limitations. For checkbox and radio + // button fields, this code ensures that appearance state is + // consistent with the field's value and uses any pre-existing + // appearance streams. + QPDF_DLL + void generateAppearancesIfNeeded(); + private: void analyze(); void traverseField(QPDFObjectHandle field, @@ -181,4 +195,4 @@ class QPDFAcroFormDocumentHelper: public QPDFDocumentHelper PointerHolder<Members> m; }; -#endif // __QPDFACROFORMDOCUMENTHELPER_HH__ +#endif // QPDFACROFORMDOCUMENTHELPER_HH diff --git a/include/qpdf/QPDFAnnotationObjectHelper.hh b/include/qpdf/QPDFAnnotationObjectHelper.hh index a5bb5a0d..e2493bba 100644 --- a/include/qpdf/QPDFAnnotationObjectHelper.hh +++ b/include/qpdf/QPDFAnnotationObjectHelper.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,10 +19,11 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDFANNOTATIONOBJECTHELPER_HH__ -#define __QPDFANNOTATIONOBJECTHELPER_HH__ +#ifndef QPDFANNOTATIONOBJECTHELPER_HH +#define QPDFANNOTATIONOBJECTHELPER_HH #include <qpdf/QPDFObjectHelper.hh> +#include <qpdf/Constants.h> #include <qpdf/DLL.h> @@ -31,6 +32,10 @@ class QPDFAnnotationObjectHelper: public QPDFObjectHelper public: QPDF_DLL QPDFAnnotationObjectHelper(QPDFObjectHandle); + QPDF_DLL + virtual ~QPDFAnnotationObjectHelper() + { + } // This class provides helper methods for certain types of // annotations. At its introduction, it only supports Widget @@ -56,6 +61,11 @@ class QPDFAnnotationObjectHelper: public QPDFObjectHelper QPDF_DLL std::string getAppearanceState(); + // Return flags from "/F". The value is a logical or of + // pdf_annotation_flag_e as defined in qpdf/Constants.h. + QPDF_DLL + int getFlags(); + // Return a specific stream. "which" may be one of "/N", "/R", or // "/D" to indicate the normal, rollover, or down appearance // stream. (Any value may be passed to "which"; if an appearance @@ -68,10 +78,29 @@ class QPDFAnnotationObjectHelper: public QPDFObjectHelper QPDFObjectHandle getAppearanceStream(std::string const& which, std::string const& state = ""); + // Generate text suitable for addition to the containing page's + // content stream that draws this annotation's appearance stream + // as a form XObject. The value "name" is the resource name that + // will be used to refer to the form xobject. The value "rotate" + // should be set to the page's /Rotate value or 0 if none. The + // values of required_flags and forbidden_flags are constructed by + // logically "or"ing annotation flags of type + // pdf_annotation_flag_e defined in qpdf/Constants.h. Content will + // be returned only if all required_flags are set and no + // forbidden_flags are set. For example, including an_no_view in + // forbidden_flags could be useful for creating an on-screen view, + // and including an_print to required_flags could be useful if + // preparing to print. + QPDF_DLL + std::string getPageContentForAppearance( + std::string const& name, int rotate, + int required_flags = 0, + int forbidden_flags = an_invisible | an_hidden); + private: class Members { - friend class QPDFPageObjectHelper; + friend class QPDFAnnotationObjectHelper; public: QPDF_DLL @@ -85,4 +114,4 @@ class QPDFAnnotationObjectHelper: public QPDFObjectHelper PointerHolder<Members> m; }; -#endif // __QPDFANNOTATIONOBJECTHELPER_HH__ +#endif // QPDFANNOTATIONOBJECTHELPER_HH diff --git a/include/qpdf/QPDFDocumentHelper.hh b/include/qpdf/QPDFDocumentHelper.hh index 3b180743..60da87dd 100644 --- a/include/qpdf/QPDFDocumentHelper.hh +++ b/include/qpdf/QPDFDocumentHelper.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDFDOCUMENTHELPER_HH__ -#define __QPDFDOCUMENTHELPER_HH__ +#ifndef QPDFDOCUMENTHELPER_HH +#define QPDFDOCUMENTHELPER_HH #include <qpdf/DLL.h> #include <qpdf/QPDF.hh> @@ -63,4 +63,4 @@ class QPDFDocumentHelper QPDF& qpdf; }; -#endif // __QPDFDOCUMENTHELPER_HH__ +#endif // QPDFDOCUMENTHELPER_HH diff --git a/include/qpdf/QPDFExc.hh b/include/qpdf/QPDFExc.hh index 5ef7c803..fe0aa1d5 100644 --- a/include/qpdf/QPDFExc.hh +++ b/include/qpdf/QPDFExc.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDFEXC_HH__ -#define __QPDFEXC_HH__ +#ifndef QPDFEXC_HH +#define QPDFEXC_HH #include <qpdf/DLL.h> #include <qpdf/Types.h> @@ -39,7 +39,9 @@ class QPDFExc: public std::runtime_error qpdf_offset_t offset, std::string const& message); QPDF_DLL - virtual ~QPDFExc() throw (); + virtual ~QPDFExc() throw () + { + } // To get a complete error string, call what(), provided by // std::exception. The accessors below return the original values @@ -75,4 +77,4 @@ class QPDFExc: public std::runtime_error std::string message; }; -#endif // __QPDFEXC_HH__ +#endif // QPDFEXC_HH diff --git a/include/qpdf/QPDFFormFieldObjectHelper.hh b/include/qpdf/QPDFFormFieldObjectHelper.hh index 4654d956..58c5d098 100644 --- a/include/qpdf/QPDFFormFieldObjectHelper.hh +++ b/include/qpdf/QPDFFormFieldObjectHelper.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDFFORMFIELDOBJECTHELPER_HH__ -#define __QPDFFORMFIELDOBJECTHELPER_HH__ +#ifndef QPDFFORMFIELDOBJECTHELPER_HH +#define QPDFFORMFIELDOBJECTHELPER_HH // This object helper helps with form fields for interactive forms. // Please see comments in QPDFAcroFormDocumentHelper.hh for additional @@ -29,6 +29,9 @@ #include <qpdf/QPDFObjectHelper.hh> #include <qpdf/DLL.h> +#include <vector> + +class QPDFAnnotationObjectHelper; class QPDFFormFieldObjectHelper: public QPDFObjectHelper { @@ -37,6 +40,10 @@ class QPDFFormFieldObjectHelper: public QPDFObjectHelper QPDFFormFieldObjectHelper(); QPDF_DLL QPDFFormFieldObjectHelper(QPDFObjectHandle); + QPDF_DLL + virtual ~QPDFFormFieldObjectHelper() + { + } QPDF_DLL bool isNull(); @@ -116,6 +123,38 @@ class QPDFFormFieldObjectHelper: public QPDFObjectHelper QPDF_DLL int getQuadding(); + // Return field flags from /Ff. The value is a logical or of + // pdf_form_field_flag_e as defined in qpdf/Constants.h + QPDF_DLL + int getFlags(); + + // Methods for testing for particular types of form fields + + // Returns true if field is of type /Tx + QPDF_DLL + bool isText(); + // Returns true if field is of type /Btn and flags do not indicate + // some other type of button. + QPDF_DLL + bool isCheckbox(); + // Returns true if field is a checkbox and is checked. + QPDF_DLL + bool isChecked(); + // Returns true if field is of type /Btn and flags indicate that + // it is a radio button + QPDF_DLL + bool isRadioButton(); + // Returns true if field is of type /Btn and flags indicate that + // it is a pushbutton + QPDF_DLL + bool isPushbutton(); + // Returns true if fields if of type /Ch + QPDF_DLL + bool isChoice(); + // Returns choices as UTF-8 strings + QPDF_DLL + std::vector<std::string> getChoices(); + // Set an attribute to the given value QPDF_DLL void setFieldAttribute(std::string const& key, QPDFObjectHandle value); @@ -126,10 +165,13 @@ class QPDFFormFieldObjectHelper: public QPDFObjectHelper void setFieldAttribute(std::string const& key, std::string const& utf8_value); - // Set /V (field value) to the given value. Optionally set - // /NeedAppearances to true. You can explicitly tell this method - // not to set /NeedAppearances if you are going to explicitly - // generate an appearance stream yourself. + // Set /V (field value) to the given value. If need_appearances is + // true and the field type is either /Tx (text) or /Ch (choice), + // set /NeedAppearances to true. You can explicitly tell this + // method not to set /NeedAppearances if you are going to generate + // an appearance stream yourself. Starting with qpdf 8.3.0, this + // method handles fields of type /Btn (checkboxes, radio buttons, + // pushbuttons) specially. QPDF_DLL void setV(QPDFObjectHandle value, bool need_appearances = true); @@ -139,7 +181,24 @@ class QPDFFormFieldObjectHelper: public QPDFObjectHelper QPDF_DLL void setV(std::string const& utf8_value, bool need_appearances = true); + // Update the appearance stream for this field. Note that qpdf's + // ability to generate appearance streams is limited. We only + // generate appearance streams for streams of type text or choice. + // The appearance uses the default parameters provided in the + // file, and it only supports ASCII characters. Quadding is + // currently ignored. While this functionality is limited, it + // should do a decent job on properly constructed PDF files when + // field values are restricted to ASCII characters. + QPDF_DLL + void generateAppearance(QPDFAnnotationObjectHelper&); + private: + void setRadioButtonValue(QPDFObjectHandle name); + void setCheckBoxValue(bool value); + void generateTextAppearance(QPDFAnnotationObjectHelper&); + QPDFObjectHandle getFontFromResource( + QPDFObjectHandle resources, std::string const& font_name); + class Members { friend class QPDFFormFieldObjectHelper; @@ -156,4 +215,4 @@ class QPDFFormFieldObjectHelper: public QPDFObjectHelper PointerHolder<Members> m; }; -#endif // __QPDFFORMFIELDOBJECTHELPER_HH__ +#endif // QPDFFORMFIELDOBJECTHELPER_HH diff --git a/include/qpdf/QPDFNameTreeObjectHelper.hh b/include/qpdf/QPDFNameTreeObjectHelper.hh new file mode 100644 index 00000000..04a15ca8 --- /dev/null +++ b/include/qpdf/QPDFNameTreeObjectHelper.hh @@ -0,0 +1,83 @@ +// Copyright (c) 2005-2019 Jay Berkenbilt +// +// This file is part of qpdf. +// +// 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. +// +// Versions of qpdf prior to version 7 were released under the terms +// of version 2.0 of the Artistic License. At your option, you may +// continue to consider qpdf to be licensed under those terms. Please +// see the manual for additional information. + +#ifndef QPDFNAMETREEOBJECTHELPER_HH +#define QPDFNAMETREEOBJECTHELPER_HH + +#include <qpdf/QPDFObjectHelper.hh> +#include <qpdf/QPDFObjGen.hh> +#include <map> + +#include <qpdf/DLL.h> + +// This is an object helper for name trees. See section 7.9.6 in the +// PDF spec (ISO 32000) for a description of name trees. This +// implementation disregards stated limits and sequencing and simply +// builds a map from string object. If the array of values does not +// contain a string where expected, this implementation silently skips +// forward until it finds a string. When looking up items in the name +// tree, use UTF-8 strings. All names are normalized for lookup +// purposes. + +class QPDFNameTreeObjectHelper: public QPDFObjectHelper +{ + public: + QPDF_DLL + QPDFNameTreeObjectHelper(QPDFObjectHandle); + QPDF_DLL + virtual ~QPDFNameTreeObjectHelper(); + + // Return whether the number tree has an explicit entry for this + // number. + QPDF_DLL + bool hasName(std::string const& utf8); + + // Find an object by name. If found, returns true and initializes + // oh. + QPDF_DLL + bool findObject(std::string const& utf8, QPDFObjectHandle& oh); + + QPDF_DLL + std::map<std::string, QPDFObjectHandle> getAsMap() const; + + private: + class Members + { + friend class QPDFNameTreeObjectHelper; + + public: + QPDF_DLL + ~Members(); + + private: + Members(); + Members(Members const&); + + std::map<std::string, QPDFObjectHandle> entries; + std::set<QPDFObjGen> seen; + }; + + void updateMap(QPDFObjectHandle oh); + + PointerHolder<Members> m; +}; + +#endif // QPDFNAMETREEOBJECTHELPER_HH diff --git a/include/qpdf/QPDFNumberTreeObjectHelper.hh b/include/qpdf/QPDFNumberTreeObjectHelper.hh new file mode 100644 index 00000000..60e5bd9a --- /dev/null +++ b/include/qpdf/QPDFNumberTreeObjectHelper.hh @@ -0,0 +1,114 @@ +// Copyright (c) 2005-2019 Jay Berkenbilt +// +// This file is part of qpdf. +// +// 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. +// +// Versions of qpdf prior to version 7 were released under the terms +// of version 2.0 of the Artistic License. At your option, you may +// continue to consider qpdf to be licensed under those terms. Please +// see the manual for additional information. + +#ifndef QPDFNUMBERTREEOBJECTHELPER_HH +#define QPDFNUMBERTREEOBJECTHELPER_HH + +#include <qpdf/QPDFObjectHelper.hh> +#include <qpdf/QPDFObjGen.hh> +#include <functional> +#include <map> + +#include <qpdf/DLL.h> + +// This is an object helper for number trees. See section 7.9.7 in the +// PDF spec (ISO 32000) for a description of number trees. This +// implementation disregards stated limits and sequencing and simply +// builds a map from numerical index to object. If the array of +// numbers does not contain a numerical value where expected, this +// implementation silently skips forward until it finds a number. + +class QPDFNumberTreeObjectHelper: public QPDFObjectHelper +{ + public: + QPDF_DLL + QPDFNumberTreeObjectHelper(QPDFObjectHandle); + QPDF_DLL + virtual ~QPDFNumberTreeObjectHelper() + { + } + + typedef long long int numtree_number; + + // Return overall minimum and maximum indices + QPDF_DLL + numtree_number getMin(); + QPDF_DLL + numtree_number getMax(); + + // Return whether the number tree has an explicit entry for this + // number. + QPDF_DLL + bool hasIndex(numtree_number idx); + + // Find an object with a specific index. If found, returns true + // and initializes oh. + QPDF_DLL + bool findObject(numtree_number idx, QPDFObjectHandle& oh); + // Find the object at the index or, if not found, the object whose + // index is the highest index less than the requested index. If + // the requested index is less than the minimum, return false. + // Otherwise, return true, initialize oh to the object, and set + // offset to the difference between the requested index and the + // actual index. For example, if a number tree has values for 3 + // and 6 and idx is 5, this method would return true, initialize + // oh to the value with index 3, and set offset to 2 (5 - 3). + QPDF_DLL + bool findObjectAtOrBelow(numtree_number idx, QPDFObjectHandle& oh, + numtree_number& offset); + + typedef std::map<numtree_number, QPDFObjectHandle> idx_map; + QPDF_DLL + idx_map getAsMap() const; + + private: + class Members + { + friend class QPDFNumberTreeObjectHelper; + typedef QPDFNumberTreeObjectHelper::numtree_number numtree_number; + + public: + QPDF_DLL + ~Members(); + + private: + Members(); + Members(Members const&); + + // Use a reverse sorted map so we can use the lower_bound + // method for searching. lower_bound returns smallest entry + // not before the searched entry, meaning that the searched + // entry is the lower bound. There's also an upper_bound + // method, but it does not do what you'd think it should. + // lower_bound implements >=, and upper_bound implements >. + typedef std::map<numtree_number, + QPDFObjectHandle, + std::greater<numtree_number> > idx_map; + idx_map entries; + std::set<QPDFObjGen> seen; + }; + + void updateMap(QPDFObjectHandle oh); + + PointerHolder<Members> m; +}; + +#endif // QPDFNUMBERTREEOBJECTHELPER_HH diff --git a/include/qpdf/QPDFObjGen.hh b/include/qpdf/QPDFObjGen.hh index b7b14af1..81c45d9f 100644 --- a/include/qpdf/QPDFObjGen.hh +++ b/include/qpdf/QPDFObjGen.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDFOBJGEN_HH__ -#define __QPDFOBJGEN_HH__ +#ifndef QPDFOBJGEN_HH +#define QPDFOBJGEN_HH #include <qpdf/DLL.h> @@ -48,4 +48,4 @@ class QPDFObjGen int gen; }; -#endif // __QPDFOBJGEN_HH__ +#endif // QPDFOBJGEN_HH diff --git a/include/qpdf/QPDFObject.hh b/include/qpdf/QPDFObject.hh index da54c027..9878804b 100644 --- a/include/qpdf/QPDFObject.hh +++ b/include/qpdf/QPDFObject.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,11 +19,12 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDFOBJECT_HH__ -#define __QPDFOBJECT_HH__ +#ifndef QPDFOBJECT_HH +#define QPDFOBJECT_HH #include <qpdf/DLL.h> #include <qpdf/PointerHolder.hh> +#include <qpdf/JSON.hh> #include <string> @@ -62,6 +63,7 @@ class QPDFObject virtual ~QPDFObject() {} virtual std::string unparse() = 0; + virtual JSON getJSON() = 0; // Return a unique type code for the object virtual object_type_e getTypeCode() const = 0; @@ -100,6 +102,7 @@ class QPDFObject { friend class QPDFObject; public: + QPDF_DLL ~Members(); private: Members(); @@ -109,4 +112,4 @@ class QPDFObject PointerHolder<Members> m; }; -#endif // __QPDFOBJECT_HH__ +#endif // QPDFOBJECT_HH diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index 6e45b5fd..b181bd79 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDFOBJECTHANDLE_HH__ -#define __QPDFOBJECTHANDLE_HH__ +#ifndef QPDFOBJECTHANDLE_HH +#define QPDFOBJECTHANDLE_HH #include <qpdf/DLL.h> #include <qpdf/Types.h> @@ -198,6 +198,38 @@ class QPDFObjectHandle double ury; }; + // Convenience object for transformation matrices + class Matrix + { + public: + Matrix() : + a(0.0), + b(0.0), + c(0.0), + d(0.0), + e(0.0), + f(0.0) + { + } + Matrix(double a, double b, double c, + double d, double e, double f) : + a(a), + b(b), + c(c), + d(d), + e(e), + f(f) + { + } + + double a; + double b; + double c; + double d; + double e; + double f; + }; + QPDF_DLL QPDFObjectHandle(); QPDF_DLL @@ -255,6 +287,12 @@ class QPDFObjectHandle // Public factory methods + // Wrap an object in an array if it is not already an array. This + // is a helper for cases in which something in a PDF may either be + // a single item or an array of items, which is a common idiom. + QPDF_DLL + QPDFObjectHandle wrapInArray(); + // Construct an object of any type from a string representation of // the object. Throws QPDFExc with an empty filename and an // offset into the string if there is an error. Any indirect @@ -362,6 +400,8 @@ class QPDFObjectHandle QPDF_DLL static QPDFObjectHandle newArray(Rectangle const&); QPDF_DLL + static QPDFObjectHandle newArray(Matrix const&); + QPDF_DLL static QPDFObjectHandle newDictionary(); QPDF_DLL static QPDFObjectHandle newDictionary( @@ -371,6 +411,10 @@ class QPDFObjectHandle // form of newArray. QPDF_DLL static QPDFObjectHandle newFromRectangle(Rectangle const&); + // Create an array from a matrix. Equivalent to the matrix + // form of newArray. + QPDF_DLL + static QPDFObjectHandle newFromMatrix(Matrix const&); // Create a new stream and associate it with the given qpdf // object. A subsequent call must be made to replaceStreamData() @@ -494,6 +538,12 @@ class QPDFObjectHandle // rectangle. Otherwise, return the rectangle [0, 0, 0, 0] QPDF_DLL Rectangle getArrayAsRectangle(); + QPDF_DLL + bool isMatrix(); + // If the array an array of six numeric values, return as a + // matrix. Otherwise, return the matrix [1, 0, 0, 1, 0, 0] + QPDF_DLL + Matrix getArrayAsMatrix(); // Methods for dictionary objects QPDF_DLL @@ -509,6 +559,59 @@ class QPDFObjectHandle QPDF_DLL bool isOrHasName(std::string const&); + // Merge resource dictionaries. Assumes resource dictionaries have + // the property that the collection of keys of all first-level + // dictionary members contains no duplicates. This method does + // nothing if both this object and the other object are not + // dictionaries. Otherwise, it has following behavior, where + // "object" refers to the object whose method is invoked, and + // "other" refers to the argument: + // + // * For each key in "other" whose value is an array: + // * If "object" does not have that entry, shallow copy it. + // * Otherwise, if "object" has an array in the same place, + // append to that array any objects in "other"'s array that + // are not already present. + // * For each key in "other" whose value is a dictionary: + // * If "object" does not have that entry, shallow copy it. + // * Otherwise, for each key in the subdictionary: + // * If key is not present in "object"'s entry, shallow copy it. + // * Otherwise, ignore. Conflicts are not detected. + // + // The primary purpose of this method is to facilitate merging of + // resource dictionaries that are supposed to have the same scope + // as each other. For example, this can be used to merge a form + // XObject's /Resources dictionary with a form field's /DR. + // Conflicts are not detected. If, in the future, there should be + // a need to detect conflicts, this method could detect them and + // return a mapping from old to new names. This mapping could be + // used for filtering the stream. This would be necessary, for + // example, to merge a form XObject's resources with a page's + // resources with the intention of concatenating the content + // streams. + QPDF_DLL + void mergeResources(QPDFObjectHandle other); + + // Get all resource names from a resource dictionary. If this + // object is a dictionary, this method returns a set of all the + // keys in all top-level subdictionaries. For resources + // dictionaries, this is the collection of names that may be + // referenced in the content stream. + QPDF_DLL + std::set<std::string> getResourceNames(); + + // Find a unique name within a resource dictionary starting with a + // given prefix. This method works by appending a number to the + // given prefix. It searches starting with min_suffix and sets + // min_suffix to selected value upon return. This can be used to + // increase efficiency if adding multiple items with the same + // prefix. (Why doesn't it set min_suffix to the next number? + // Well, maybe you aren't going to actually use the name it + // returns.) + QPDF_DLL + std::string getUniqueResourceName(std::string const& prefix, + int& min_suffix); + // Return the QPDF object that owns an indirect object. Returns // null for a direct object. QPDF_DLL @@ -727,6 +830,25 @@ class QPDFObjectHandle QPDF_DLL std::string unparseBinary(); + // Return encoded as JSON. For most object types, there is an + // obvious mapping. The JSON is generated as follows: + // * Names are encoded as strings representing the normalized value of + // getName() + // * Indirect references are encoded as strings containing "obj gen R" + // * Strings are encoded as UTF-8 strings with unrepresentable binary + // characters encoded as \uHHHH + // * Encoding streams just encodes the stream's dictionary; the stream + // data is not represented + // * Object types that are only valid in content streams (inline + // image, operator) as well as "reserved" objects are not + // representable and will be serialized as "null". + // If dereference_indirect is true and this is an indirect object, + // show the actual contents of the object. The effect of + // dereference_indirect applies only to this object. It is not + // recursive. + QPDF_DLL + JSON getJSON(bool dereference_indirect = false); + // Legacy helper methods for commonly performed operations on // pages. Newer code should use QPDFPageObjectHelper instead. The // specification and behavior of these methods are the same as the @@ -750,6 +872,7 @@ class QPDFObjectHandle // do nothing. Objects read normally from the file have // descriptions. See comments on setObjectDescription for // additional details. + QPDF_DLL void warnIfPossible(std::string const& warning, bool throw_if_no_description = false); @@ -927,4 +1050,4 @@ class QPDFObjectHandle PointerHolder<Members> m; }; -#endif // __QPDFOBJECTHANDLE_HH__ +#endif // QPDFOBJECTHANDLE_HH diff --git a/include/qpdf/QPDFObjectHelper.hh b/include/qpdf/QPDFObjectHelper.hh index 8aec8955..c61f4c38 100644 --- a/include/qpdf/QPDFObjectHelper.hh +++ b/include/qpdf/QPDFObjectHelper.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDFOBJECTHELPER_HH__ -#define __QPDFOBJECTHELPER_HH__ +#ifndef QPDFOBJECTHELPER_HH +#define QPDFOBJECTHELPER_HH #include <qpdf/DLL.h> @@ -64,4 +64,4 @@ class QPDFObjectHelper QPDFObjectHandle oh; }; -#endif // __QPDFOBJECTHELPER_HH__ +#endif // QPDFOBJECTHELPER_HH diff --git a/include/qpdf/QPDFOutlineDocumentHelper.hh b/include/qpdf/QPDFOutlineDocumentHelper.hh new file mode 100644 index 00000000..f1920574 --- /dev/null +++ b/include/qpdf/QPDFOutlineDocumentHelper.hh @@ -0,0 +1,108 @@ +// Copyright (c) 2005-2019 Jay Berkenbilt +// +// This file is part of qpdf. +// +// 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. +// +// Versions of qpdf prior to version 7 were released under the terms +// of version 2.0 of the Artistic License. At your option, you may +// continue to consider qpdf to be licensed under those terms. Please +// see the manual for additional information. + +#ifndef QPDFOUTLINEDOCUMENTHELPER_HH +#define QPDFOUTLINEDOCUMENTHELPER_HH + +#include <qpdf/QPDFDocumentHelper.hh> +#include <qpdf/QPDFOutlineObjectHelper.hh> +#include <qpdf/QPDFNameTreeObjectHelper.hh> + +#include <qpdf/QPDF.hh> +#include <map> +#include <list> +#include <set> + +#include <qpdf/DLL.h> + +// This is a document helper for outlines, also known as bookmarks. +// Outlines are discussed in section 12.3.3 of the PDF spec +// (ISO-32000). With the help of QPDFOutlineObjectHelper, the outlines +// tree is traversed, and a bidirectional map is made between pages +// and outlines. See also QPDFOutlineObjectHelper. + +class QPDFOutlineDocumentHelper: public QPDFDocumentHelper +{ + public: + QPDF_DLL + QPDFOutlineDocumentHelper(QPDF&); + QPDF_DLL + virtual ~QPDFOutlineDocumentHelper(); + + QPDF_DLL + bool hasOutlines(); + + QPDF_DLL + std::list<QPDFOutlineObjectHelper> getTopLevelOutlines(); + + // If the name is a name object, look it up in the /Dests key of + // the document catalog. If the name is a string, look it up in + // the name tree pointed to by the /Dests key of the names + // dictionary. + QPDF_DLL + QPDFObjectHandle + resolveNamedDest(QPDFObjectHandle name); + + // Return a list outlines that are known to target the specified + // page + QPDF_DLL + std::list<QPDFOutlineObjectHelper> getOutlinesForPage(QPDFObjGen const&); + + class Accessor + { + friend class QPDFOutlineObjectHelper; + + QPDF_DLL + static bool + checkSeen(QPDFOutlineDocumentHelper& dh, QPDFObjGen const& og) + { + return dh.checkSeen(og); + } + }; + friend class Accessor; + + private: + bool checkSeen(QPDFObjGen const& og); + void initializeByPage(); + + class Members + { + friend class QPDFOutlineDocumentHelper; + + public: + QPDF_DLL + ~Members(); + + private: + Members(); + Members(Members const&); + + std::list<QPDFOutlineObjectHelper> outlines; + std::set<QPDFObjGen> seen; + QPDFObjectHandle dest_dict; + PointerHolder<QPDFNameTreeObjectHelper> names_dest; + std::map<QPDFObjGen, std::list<QPDFOutlineObjectHelper> > by_page; + }; + + PointerHolder<Members> m; +}; + +#endif // QPDFOUTLINEDOCUMENTHELPER_HH diff --git a/include/qpdf/QPDFOutlineObjectHelper.hh b/include/qpdf/QPDFOutlineObjectHelper.hh new file mode 100644 index 00000000..9063b86f --- /dev/null +++ b/include/qpdf/QPDFOutlineObjectHelper.hh @@ -0,0 +1,122 @@ +// Copyright (c) 2005-2019 Jay Berkenbilt +// +// This file is part of qpdf. +// +// 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. +// +// Versions of qpdf prior to version 7 were released under the terms +// of version 2.0 of the Artistic License. At your option, you may +// continue to consider qpdf to be licensed under those terms. Please +// see the manual for additional information. + +#ifndef QPDFOUTLINEOBJECTHELPER_HH +#define QPDFOUTLINEOBJECTHELPER_HH + +#include <qpdf/QPDFObjectHelper.hh> +#include <qpdf/QPDFObjGen.hh> +#include <list> + +class QPDFOutlineDocumentHelper; + +#include <qpdf/DLL.h> + +// This is an object helper for outline items. Outlines, also known as +// bookmarks, are described in section 12.3.3 of the PDF spec +// (ISO-32000). See comments below for details. + +class QPDFOutlineObjectHelper: public QPDFObjectHelper +{ + public: + QPDF_DLL + virtual ~QPDFOutlineObjectHelper() + { + // This must be cleared explicitly to avoid circular references + // that prevent cleanup of pointer holders. + this->m->parent = 0; + } + + // All constructors are private. You can only create one of these + // using QPDFOutlineDocumentHelper. + + // Return parent pointer. Returns a null pointer if this is a + // top-level outline. + QPDF_DLL + PointerHolder<QPDFOutlineObjectHelper> getParent(); + + // Return children as a list. + QPDF_DLL + std::list<QPDFOutlineObjectHelper> getKids(); + + // Return the destination, regardless of whether it is named or + // explicit and whether it is directly provided or in a GoTo + // action. Returns a null object if the destination can't be + // determined. Named destinations can be resolved using the older + // root /Dest dictionary or the current names tree. + QPDF_DLL + QPDFObjectHandle getDest(); + + // Return the page that the outline points to. Returns a null + // object if the destination page can't be determined. + QPDF_DLL + QPDFObjectHandle getDestPage(); + + // Returns the value of /Count as present in the object, or 0 if + // not present. If count is positive, the outline is open. If + // negative, it is closed. Either way, the absolute value is the + // number descendant items that would be visible if this were + // open. + QPDF_DLL + int getCount(); + + // Returns the title as a UTF-8 string. Returns the empty string + // if there is no title. + QPDF_DLL + std::string getTitle(); + + class Accessor + { + friend class QPDFOutlineDocumentHelper; + + static QPDFOutlineObjectHelper + create(QPDFObjectHandle oh, QPDFOutlineDocumentHelper& dh, int depth) + { + return QPDFOutlineObjectHelper(oh, dh, depth); + } + }; + friend class Accessor; + + private: + QPDF_DLL + QPDFOutlineObjectHelper(QPDFObjectHandle, QPDFOutlineDocumentHelper&, int); + + class Members + { + friend class QPDFOutlineObjectHelper; + + public: + QPDF_DLL + ~Members(); + + private: + Members(QPDFOutlineDocumentHelper& dh); + Members(Members const&); + + QPDFOutlineDocumentHelper& dh; + PointerHolder<QPDFOutlineObjectHelper> parent; + std::list<QPDFOutlineObjectHelper> kids; + }; + + PointerHolder<Members> m; +}; + +#endif // QPDFOUTLINEOBJECTHELPER_HH diff --git a/include/qpdf/QPDFPageDocumentHelper.hh b/include/qpdf/QPDFPageDocumentHelper.hh index 514f7277..096a401c 100644 --- a/include/qpdf/QPDFPageDocumentHelper.hh +++ b/include/qpdf/QPDFPageDocumentHelper.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,11 +19,12 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDFPAGEDOCUMENTHELPER_HH__ -#define __QPDFPAGEDOCUMENTHELPER_HH__ +#ifndef QPDFPAGEDOCUMENTHELPER_HH +#define QPDFPAGEDOCUMENTHELPER_HH #include <qpdf/QPDFDocumentHelper.hh> #include <qpdf/QPDFPageObjectHelper.hh> +#include <qpdf/Constants.h> #include <qpdf/DLL.h> @@ -31,11 +32,17 @@ #include <qpdf/QPDF.hh> +class QPDFAcroFormDocumentHelper; + class QPDFPageDocumentHelper: public QPDFDocumentHelper { public: QPDF_DLL QPDFPageDocumentHelper(QPDF&); + QPDF_DLL + virtual ~QPDFPageDocumentHelper() + { + } // Traverse page tree, and return all /Page objects wrapped in // QPDFPageObjectHelper objects. Unlike with @@ -66,7 +73,12 @@ class QPDFPageDocumentHelper: public QPDFDocumentHelper // indirect. If it is an indirect object from another QPDF, this // method will call pushInheritedAttributesToPage on the other // file and then copy the page to this QPDF using the same - // underlying code as copyForeignObject. + // underlying code as copyForeignObject. Note that you can call + // copyForeignObject directly to copy a page from a different + // file, but the resulting object will not be a page in the new + // file. You could do this, for example, to convert a page into a + // form XObject, though for that, you're better off using + // QPDFPageObjectHelper::getFormXObjectForPage. QPDF_DLL void addPage(QPDFPageObjectHelper newpage, bool first); @@ -80,7 +92,30 @@ class QPDFPageDocumentHelper: public QPDFDocumentHelper QPDF_DLL void removePage(QPDFPageObjectHelper page); + // For every annotation, integrate the annotation's appearance + // stream into the containing page's content streams, merge the + // annotation's resources with the page's resources, and remove + // the annotation from the page. Handles widget annotations + // associated with interactive form fields as a special case, + // including removing the /AcroForm key from the document catalog. + // The values passed to required_flags and forbidden_flags are + // passed along to + // QPDFAnnotationObjectHelper::getPageContentForAppearance. See + // comments there in QPDFAnnotationObjectHelper.hh for meanings of + // those flags. + QPDF_DLL + void flattenAnnotations( + int required_flags = 0, + int forbidden_flags = an_invisible | an_hidden); + private: + void flattenAnnotationsForPage( + QPDFPageObjectHelper& page, + QPDFObjectHandle& resources, + QPDFAcroFormDocumentHelper& afdh, + int required_flags, + int forbidden_flags); + class Members { friend class QPDFPageDocumentHelper; @@ -97,4 +132,4 @@ class QPDFPageDocumentHelper: public QPDFDocumentHelper PointerHolder<Members> m; }; -#endif // __QPDFPAGEDOCUMENTHELPER_HH__ +#endif // QPDFPAGEDOCUMENTHELPER_HH diff --git a/include/qpdf/QPDFPageLabelDocumentHelper.hh b/include/qpdf/QPDFPageLabelDocumentHelper.hh new file mode 100644 index 00000000..2ad05934 --- /dev/null +++ b/include/qpdf/QPDFPageLabelDocumentHelper.hh @@ -0,0 +1,104 @@ +// Copyright (c) 2005-2019 Jay Berkenbilt +// +// This file is part of qpdf. +// +// 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. +// +// Versions of qpdf prior to version 7 were released under the terms +// of version 2.0 of the Artistic License. At your option, you may +// continue to consider qpdf to be licensed under those terms. Please +// see the manual for additional information. + +#ifndef QPDFPAGELABELDOCUMENTHELPER_HH +#define QPDFPAGELABELDOCUMENTHELPER_HH + +#include <qpdf/QPDFDocumentHelper.hh> + +#include <qpdf/QPDF.hh> +#include <qpdf/QPDFNumberTreeObjectHelper.hh> +#include <vector> + +#include <qpdf/DLL.h> + +// Page labels are discussed in the PDF spec (ISO-32000) in section +// 12.4.2. +// +// Page labels are implemented as a number tree. Each key is a page +// index, numbered from 0. The values are dictionaries with the +// following keys, all optional: +// +// * /Type: if present, must be /PageLabel +// * /S: one of /D, /R, /r, /A, or /a for decimal, upper-case and +// lower-case Roman numeral, or upper-case and lower-case alphabetic +// * /P: if present, a fixed prefix string that is prepended to each +// page number +// * /St: the starting number, or 1 if not specified + +class QPDFPageLabelDocumentHelper: public QPDFDocumentHelper +{ + public: + QPDF_DLL + QPDFPageLabelDocumentHelper(QPDF&); + QPDF_DLL + virtual ~QPDFPageLabelDocumentHelper() + { + } + + QPDF_DLL + bool hasPageLabels(); + + // Return a page label dictionary representing the page label for + // the given page. The page does not need to appear explicitly in + // the page label dictionary. This method will adjust /St as + // needed to produce a label that is suitable for the page. + QPDF_DLL + QPDFObjectHandle getLabelForPage(long long page_idx); + + // Append to the incoming vector a list of objects suitable for + // inclusion in a /PageLabels dictionary's /Nums field. start_idx + // and end_idx are the indexes to the starting and ending pages + // (inclusive) in the original file, and new_start_idx is the + // index to the first page in the new file. For example, if pages + // 10 through 12 of one file are being copied to a new file as + // pages 6 through 8, you would call getLabelsForPageRange(10, 12, + // 6), which would return as many entries as are required to add + // to the new file's PageLabels. This method fabricates a suitable + // entry even if the original document has no page labels. This + // behavior facilitates using this function to incrementally build + // up a page labels tree when merging files. + QPDF_DLL + void + getLabelsForPageRange(long long start_idx, long long end_idx, + long long new_start_idx, + std::vector<QPDFObjectHandle>& new_labels); + + private: + class Members + { + friend class QPDFPageLabelDocumentHelper; + + public: + QPDF_DLL + ~Members(); + + private: + Members(); + Members(Members const&); + + PointerHolder<QPDFNumberTreeObjectHelper> labels; + }; + + PointerHolder<Members> m; +}; + +#endif // QPDFPAGELABELDOCUMENTHELPER_HH diff --git a/include/qpdf/QPDFPageObjectHelper.hh b/include/qpdf/QPDFPageObjectHelper.hh index 08a7d8ab..d7291723 100644 --- a/include/qpdf/QPDFPageObjectHelper.hh +++ b/include/qpdf/QPDFPageObjectHelper.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDFPAGEOBJECTHELPER_HH__ -#define __QPDFPAGEOBJECTHELPER_HH__ +#ifndef QPDFPAGEOBJECTHELPER_HH +#define QPDFPAGEOBJECTHELPER_HH #include <qpdf/QPDFObjectHelper.hh> #include <qpdf/QPDFAnnotationObjectHelper.hh> @@ -34,17 +34,50 @@ class QPDFPageObjectHelper: public QPDFObjectHelper public: QPDF_DLL QPDFPageObjectHelper(QPDFObjectHandle); + QPDF_DLL + virtual ~QPDFPageObjectHelper() + { + } + + // Return the effective value of this attribute for the page. If + // the requested attribute is not present on the page but is + // inheritable, look up through the page's ancestors in the page + // tree. If copy_if_shared is true, then this method will replace + // the attribute with a shallow copy if it is in indirect or + // inherited and return the copy. You should do this if you are + // going to modify the returned object and want the modifications + // to apply to the current page only. + QPDF_DLL + QPDFObjectHandle + getAttribute(std::string const& name, bool copy_if_shared); + + // Return the TrimBox. If not defined, fall back to CropBox + QPDF_DLL + QPDFObjectHandle + getTrimBox(bool copy_if_shared = false); + + // Return the CropBox. If not defined, fall back to MediaBox + QPDF_DLL + QPDFObjectHandle + getCropBox(bool copy_if_shared = false); + + // Return the MediaBox + QPDF_DLL + QPDFObjectHandle + getMediaBox(bool copy_if_shared = false); // Returns an empty map if there are no images or no resources. - // This function does not presently support inherited resources. - // If this is a significant concern, call - // pushInheritedAttributesToPage() on the QPDF object that owns - // this page. See comment in the source for details. Return value - // is a map from XObject name to the image object, which is always - // a stream. + // Prior to qpdf 8.4.0, this function did not support inherited + // resources, but it does now. Return value is a map from XObject + // name to the image object, which is always a stream. QPDF_DLL std::map<std::string, QPDFObjectHandle> getPageImages(); + // Convert each inline image to an external (normal) image if the + // size is at least the specified number of bytes. + QPDF_DLL + void externalizeInlineImages(size_t min_size = 0); + // Return the annotations in the page's "/Annots" list, if any. If // only_subtype is non-empty, only include annotations of the // given subtype. @@ -136,6 +169,67 @@ class QPDFPageObjectHelper: public QPDFObjectHelper QPDF_DLL void removeUnreferencedResources(); + // Return a new QPDFPageDocumentHelper that is a duplicate of the + // page. The returned object is an indirect object that is ready + // to be inserted into the same or a different QPDF object using + // any of the addPage methods in QPDFPageDocumentHelper or QPDF. + // Without calling one of those methods, the page will not be + // added anywhere. The new page object shares all content streams + // and indirect object resources with the original page, so if you + // are going to modify the contents or other aspects of the page, + // you will need to handling copying of the component parts + // separately. + QPDF_DLL + QPDFPageObjectHelper shallowCopyPage(); + + // Return a transformation matrix whose effect is the same as the + // page's /Rotate and /UserUnit parameters. If invert is true, + // return a matrix whose effect is the opposite. The regular + // matrix is suitable for taking something from this page to put + // elsewhere, and the second one is suitable for putting something + // else onto this page. The page's TrimBox is used as the bounding + // box for purposes of computing the matrix. + QPDF_DLL + QPDFObjectHandle::Matrix getMatrixForTransformations(bool invert = false); + + // Return a form XObject that draws this page. This is useful for + // n-up operations, underlay, overlay, thumbnail generation, or + // any other case in which it is useful to replicate the contents + // of a page in some other context. The dictionaries are shallow + // copies of the original page dictionary, and the contents are + // coalesced from the page's contents. The resulting object handle + // is not referenced anywhere. If handle_transformations is true, + // the resulting form XObject's /Matrix will be set to replicate + // rotation (/Rotate) and scaling (/UserUnit) in the page's + // dictionary. In this way, the page's transformations will be + // preserved when placing this object on another page. + QPDF_DLL + QPDFObjectHandle getFormXObjectForPage(bool handle_transformations = true); + + // Return content stream text that will place the given form + // XObject (fo) using the resource name "name" on this page + // centered within the given rectangle and shrunk to fit if + // necessary. If invert_transformations is true, the effect of any + // rotation (/Rotate) and scaling (/UserUnit) applied to the + // current page will be inverted in the form XObject placement. + // This will cause the form XObject's absolute orientation to be + // preserved. You could overlay one page on another by calling + // getFormXObjectForPage on the original page, + // QPDFObjectHandle::getUniqueResourceName on the destination + // page's Resources dictionary to generate a name for the + // resulting object, and calling placeFormXObject on the + // destination page. Then insert the new fo (or, if it comes from + // a different file, the result of calling copyForeignObject on + // it) into the resources dictionary using name, and append or + // prepend the content to the page's content streams. See the + // overlay/underlay code in qpdf.cc or + // examples/pdf-overlay-page.cc for an example. + QPDF_DLL + std::string placeFormXObject( + QPDFObjectHandle fo, std::string name, + QPDFObjectHandle::Rectangle rect, + bool invert_transformations = true); + private: class Members { @@ -153,4 +247,4 @@ class QPDFPageObjectHelper: public QPDFObjectHelper PointerHolder<Members> m; }; -#endif // __QPDFPAGEOBJECTHELPER_HH__ +#endif // QPDFPAGEOBJECTHELPER_HH diff --git a/include/qpdf/QPDFSystemError.hh b/include/qpdf/QPDFSystemError.hh new file mode 100644 index 00000000..c4e48713 --- /dev/null +++ b/include/qpdf/QPDFSystemError.hh @@ -0,0 +1,58 @@ +// Copyright (c) 2005-2019 Jay Berkenbilt +// +// This file is part of qpdf. +// +// 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. +// +// Versions of qpdf prior to version 7 were released under the terms +// of version 2.0 of the Artistic License. At your option, you may +// continue to consider qpdf to be licensed under those terms. Please +// see the manual for additional information. + +#ifndef QPDFSYSTEMERROR_HH +#define QPDFSYSTEMERROR_HH + +#include <qpdf/DLL.h> +#include <qpdf/Types.h> + +#include <qpdf/Constants.h> +#include <string> +#include <stdexcept> + +class QPDFSystemError: public std::runtime_error +{ + public: + QPDF_DLL + QPDFSystemError(std::string const& description, + int system_errno); + QPDF_DLL + virtual ~QPDFSystemError() throw (); + + // To get a complete error string, call what(), provided by + // std::exception. The accessors below return the original values + // used to create the exception. + + QPDF_DLL + std::string const& getDescription() const; + QPDF_DLL + int getErrno() const; + + private: + static std::string createWhat(std::string const& description, + int system_errno); + + std::string description; + int system_errno; +}; + +#endif // QPDFSYSTEMERROR_HH diff --git a/include/qpdf/QPDFTokenizer.hh b/include/qpdf/QPDFTokenizer.hh index eb9215aa..ed33e13c 100644 --- a/include/qpdf/QPDFTokenizer.hh +++ b/include/qpdf/QPDFTokenizer.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDFTOKENIZER_HH__ -#define __QPDFTOKENIZER_HH__ +#ifndef QPDFTOKENIZER_HH +#define QPDFTOKENIZER_HH #include <qpdf/DLL.h> @@ -174,11 +174,25 @@ class QPDFTokenizer size_t max_len = 0); // Calling this method puts the tokenizer in a state for reading - // inline images. In that state, it will return all data up to and - // including the next EI token. After you call this method, the - // next call to readToken (or the token created next time getToken - // returns true) will either be tt_inline_image or tt_bad. This is - // the only way readToken returns a tt_inline_image token. + // inline images. You should call this method after reading the + // character following the ID operator. In that state, it will + // return all data up to BUT NOT INCLUDING the next EI token. This + // is a difference in behavior from the legacy version. After you + // call this method, the next call to readToken (or the token + // created next time getToken returns true) will either be + // tt_inline_image or tt_bad. This is the only way readToken + // returns a tt_inline_image token. The older version of this + // method that takes does not take a PointerHolder<InputSource> + // will always end the inline image the first time it sees + // something that looks like an EI operator and will include the + // EI operator in the token. It is being maintained for backward + // compatibility only and will likely be removed in the future. + QPDF_DLL + void expectInlineImage(PointerHolder<InputSource> input); + + // Legacy version. New code should not call this. The token + // returned will include the EI keyword. The recipient of the + // token will have to remove it. QPDF_DLL void expectInlineImage(); @@ -190,6 +204,7 @@ class QPDFTokenizer void resolveLiteral(); bool isSpace(char); bool isDelimiter(char); + void findEI(PointerHolder<InputSource> input); enum state_e { st_top, st_in_space, st_in_comment, st_in_string, st_lt, st_gt, @@ -223,14 +238,16 @@ class QPDFTokenizer std::string error_message; bool unread_char; char char_to_unread; + size_t inline_image_bytes; // State for strings int string_depth; bool string_ignoring_newline; char bs_num_register[4]; bool last_char_was_bs; + bool last_char_was_cr; }; PointerHolder<Members> m; }; -#endif // __QPDFTOKENIZER_HH__ +#endif // QPDFTOKENIZER_HH diff --git a/include/qpdf/QPDFWriter.hh b/include/qpdf/QPDFWriter.hh index 1802078a..1aa4e8a8 100644 --- a/include/qpdf/QPDFWriter.hh +++ b/include/qpdf/QPDFWriter.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -23,8 +23,8 @@ // new PDF files. See comments through the header file for additional // details. -#ifndef __QPDFWRITER_HH__ -#define __QPDFWRITER_HH__ +#ifndef QPDFWRITER_HH +#define QPDFWRITER_HH #include <qpdf/DLL.h> #include <qpdf/Types.h> @@ -343,6 +343,49 @@ class QPDFWriter // setting R4 parameters pushes the version to at least 1.5, or if // AES is used, 1.6, and setting R5 or R6 parameters pushes the // version to at least 1.7 with extension level 3. + // + // Note about Unicode passwords: the PDF specification requires + // passwords to be encoded with PDF Doc encoding for R <= 4 and + // UTF-8 for R >= 5. In all cases, these methods take strings of + // bytes as passwords. It is up to the caller to ensure that + // passwords are properly encoded. The qpdf command-line tool + // tries to do this, as discussed in the manual. If you are doing + // this from your own application, QUtil contains many transcoding + // functions that could be useful to you, most notably + // utf8_to_pdf_doc. + QPDF_DLL + void setR3EncryptionParameters( + char const* user_password, char const* owner_password, + bool allow_accessibility, bool allow_extract, + bool allow_assemble, bool allow_annotate_and_form, + bool allow_form_filling, bool allow_modify_other, + qpdf_r3_print_e print); + QPDF_DLL + void setR4EncryptionParameters( + char const* user_password, char const* owner_password, + bool allow_accessibility, bool allow_extract, + bool allow_assemble, bool allow_annotate_and_form, + bool allow_form_filling, bool allow_modify_other, + qpdf_r3_print_e print, bool encrypt_metadata, bool use_aes); + // R5 is deprecated. Do not use it for production use. Writing + // R5 is supported by qpdf primarily to generate test files for + // applications that may need to test R5 support. + QPDF_DLL + void setR5EncryptionParameters( + char const* user_password, char const* owner_password, + bool allow_accessibility, bool allow_extract, + bool allow_assemble, bool allow_annotate_and_form, + bool allow_form_filling, bool allow_modify_other, + qpdf_r3_print_e print, bool encrypt_metadata); + QPDF_DLL + void setR6EncryptionParameters( + char const* user_password, char const* owner_password, + bool allow_accessibility, bool allow_extract, + bool allow_assemble, bool allow_annotate_and_form, + bool allow_form_filling, bool allow_modify_other, + qpdf_r3_print_e print, bool encrypt_metadata_aes); + + // Pre qpdf 8.4.0 API QPDF_DLL void setR2EncryptionParameters( char const* user_password, char const* owner_password, @@ -359,9 +402,6 @@ class QPDFWriter bool allow_accessibility, bool allow_extract, qpdf_r3_print_e print, qpdf_r3_modify_e modify, bool encrypt_metadata, bool use_aes); - // R5 is deprecated. Do not use it for production use. Writing - // R5 is supported by qpdf primarily to generate test files for - // applications that may need to test R5 support. QPDF_DLL void setR5EncryptionParameters( char const* user_password, char const* owner_password, @@ -404,6 +444,18 @@ class QPDFWriter QPDF_DLL void registerProgressReporter(PointerHolder<ProgressReporter>); + // Return the PDF version that will be written into the header. + // Calling this method does all the preparation for writing, so it + // is an error to call any methods that may cause a change to the + // version. Adding new objects to the original file after calling + // this may also cause problems. It is safe to update existing + // objects or stream contents after calling this method, e.g., to + // include the final version number in metadata. + QPDF_DLL + std::string getFinalVersion(); + + // Write the final file. There is no expectation of being able to + // call write() more than once. QPDF_DLL void write(); @@ -447,6 +499,8 @@ class QPDFWriter std::set<int>& bits_to_clear, char const* user_password, char const* owner_password, bool allow_accessibility, bool allow_extract, + bool allow_assemble, bool allow_annotate_and_form, + bool allow_form_filling, bool allow_modify_other, qpdf_r3_print_e print, qpdf_r3_modify_e modify); void disableIncompatibleEncryption(int major, int minor, int extension_level); @@ -473,6 +527,7 @@ class QPDFWriter void writeLinearized(); void enqueuePart(std::vector<QPDFObjectHandle>& part); void writeEncryptionDictionary(); + void doWriteSetup(); void writeHeader(); void writeHintStream(int hint_id); qpdf_offset_t writeXRefTable( @@ -530,6 +585,7 @@ class QPDFWriter friend class QPDFWriter; public: + QPDF_DLL ~Members(); private: @@ -597,6 +653,7 @@ class QPDFWriter bool deterministic_id; Pl_MD5* md5_pipeline; std::string deterministic_id_data; + bool did_write_setup; // For linearization only std::string lin_pass1_filename; @@ -616,4 +673,4 @@ class QPDFWriter PointerHolder<Members> m; }; -#endif // __QPDFWRITER_HH__ +#endif // QPDFWRITER_HH diff --git a/include/qpdf/QPDFXRefEntry.hh b/include/qpdf/QPDFXRefEntry.hh index d85e54da..ee3cf746 100644 --- a/include/qpdf/QPDFXRefEntry.hh +++ b/include/qpdf/QPDFXRefEntry.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QPDFXREFENTRY_HH__ -#define __QPDFXREFENTRY_HH__ +#ifndef QPDFXREFENTRY_HH +#define QPDFXREFENTRY_HH #include <qpdf/DLL.h> #include <qpdf/Types.h> @@ -54,4 +54,4 @@ class QPDFXRefEntry int field2; }; -#endif // __QPDFXREFENTRY_HH__ +#endif // QPDFXREFENTRY_HH diff --git a/include/qpdf/QTC.hh b/include/qpdf/QTC.hh index 1de779db..048f1794 100644 --- a/include/qpdf/QTC.hh +++ b/include/qpdf/QTC.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QTC_HH__ -#define __QTC_HH__ +#ifndef QTC_HH +#define QTC_HH #include <qpdf/DLL.h> @@ -30,4 +30,4 @@ namespace QTC void TC(char const* const scope, char const* const ccase, int n = 0); }; -#endif // __QTC_HH__ +#endif // QTC_HH diff --git a/include/qpdf/QUtil.hh b/include/qpdf/QUtil.hh index a81b0a9e..02dec5ad 100644 --- a/include/qpdf/QUtil.hh +++ b/include/qpdf/QUtil.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,13 +19,14 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __QUTIL_HH__ -#define __QUTIL_HH__ +#ifndef QUTIL_HH +#define QUTIL_HH #include <qpdf/DLL.h> #include <qpdf/Types.h> #include <string> #include <list> +#include <vector> #include <stdexcept> #include <stdio.h> #include <time.h> @@ -61,9 +62,14 @@ namespace QUtil QPDF_DLL unsigned char* unsigned_char_pointer(char const* str); - // Throw std::runtime_error with a string formed by appending to + // Throw QPDFSystemError, which is derived from + // std::runtime_error, with a string formed by appending to // "description: " the standard string corresponding to the - // current value of errno. + // current value of errno. You can retrieve the value of errno by + // calling getErrno() on the QPDFSystemError. Prior to qpdf 8.2.0, + // this method threw system::runtime_error directly, but since + // QPDFSystemError is derived from system::runtime_error, old code + // that specifically catches std::runtime_error will still work. QPDF_DLL void throw_system_error(std::string const& description); @@ -141,11 +147,104 @@ namespace QUtil std::string toUTF8(unsigned long uval); // Return a string containing the byte representation of the - // UTF-16 BE encoding for the unicode value passed in. + // UTF-16 big-endian encoding for the unicode value passed in. // Unrepresentable code points are converted to U+FFFD. QPDF_DLL std::string toUTF16(unsigned long uval); + // Test whether this is a UTF-16 big-endian string. This is + // indicated by first two bytes being 0xFE 0xFF. + QPDF_DLL + bool is_utf16(std::string const&); + + // Convert a UTF-8 encoded string to UTF-16 big-endian. + // Unrepresentable code points are converted to U+FFFD. + QPDF_DLL + std::string utf8_to_utf16(std::string const& utf8); + + // Convert a UTF-8 encoded string to the specified single-byte + // encoding system by replacing all unsupported characters with + // the given unknown_char. + QPDF_DLL + std::string utf8_to_ascii( + std::string const& utf8, char unknown_char = '?'); + QPDF_DLL + std::string utf8_to_win_ansi( + std::string const& utf8, char unknown_char = '?'); + QPDF_DLL + std::string utf8_to_mac_roman( + std::string const& utf8, char unknown_char = '?'); + QPDF_DLL + std::string utf8_to_pdf_doc( + std::string const& utf8, char unknown_char = '?'); + + // These versions return true if the conversion was successful and + // false if any unrepresentable characters were found and had to + // be substituted with the unknown character. + QPDF_DLL + bool utf8_to_ascii( + std::string const& utf8, std::string& ascii, char unknown_char = '?'); + QPDF_DLL + bool utf8_to_win_ansi( + std::string const& utf8, std::string& win, char unknown_char = '?'); + QPDF_DLL + bool utf8_to_mac_roman( + std::string const& utf8, std::string& mac, char unknown_char = '?'); + QPDF_DLL + bool utf8_to_pdf_doc( + std::string const& utf8, std::string& pdfdoc, char unknown_char = '?'); + + // Convert a UTF-16 big-endian encoded string to UTF-8. + // Unrepresentable code points are converted to U+FFFD. + QPDF_DLL + std::string utf16_to_utf8(std::string const& utf16); + + // Convert from the specified single-byte encoding system to + // UTF-8. There is no ascii_to_utf8 because all ASCII strings are + // already valid UTF-8. + QPDF_DLL + std::string win_ansi_to_utf8(std::string const& win); + QPDF_DLL + std::string mac_roman_to_utf8(std::string const& mac); + QPDF_DLL + std::string pdf_doc_to_utf8(std::string const& pdfdoc); + + // Analyze a string for encoding. We can't tell the difference + // between any single-byte encodings, and we can't tell for sure + // whether a string that happens to be valid UTF-8 isn't a + // different encoding, but we can at least tell a few things to + // help us guess. If there are no characters with the high bit + // set, has_8bit_chars is false, and the other values are also + // false, even though ASCII strings are valid UTF-8. is_valid_utf8 + // means that the string is non-trivially valid UTF-8. + QPDF_DLL + void analyze_encoding(std::string const& str, + bool& has_8bit_chars, + bool& is_valid_utf8, + bool& is_utf16); + + // Try to compensate for previously incorrectly encoded strings. + // We want to compensate for the following errors: + // + // * The string was supposed to be UTF-8 but was one of the + // single-byte encodings + // * The string was supposed to be PDF Doc but was either UTF-8 or + // one of the other single-byte encodings + // + // The returned vector always contains the original string first, + // and then it contains what the correct string would be in the + // event that the original string was the result of any of the + // above errors. + // + // This method is useful for attempting to recover a password that + // may have been previously incorrectly encoded. For example, the + // password was supposed to be UTF-8 but the previous application + // used a password encoded in WinAnsi, or if the previous password + // was supposed to be PDFDoc but was actually given as UTF-8 or + // WinAnsi, this method would find the correct password. + QPDF_DLL + std::vector<std::string> possible_repaired_encodings(std::string); + // If secure random number generation is supported on your // platform and qpdf was not compiled with insecure random number // generation, this returns a cryptographically secure random @@ -215,6 +314,11 @@ namespace QUtil QPDF_DLL bool is_number(char const*); + + // This method parses the numeric range syntax used by the qpdf + // command-line tool. May throw std::runtime_error. + QPDF_DLL + std::vector<int> parse_numrange(char const* range, int max); }; -#endif // __QUTIL_HH__ +#endif // QUTIL_HH diff --git a/include/qpdf/RandomDataProvider.hh b/include/qpdf/RandomDataProvider.hh index 59849289..c3a519aa 100644 --- a/include/qpdf/RandomDataProvider.hh +++ b/include/qpdf/RandomDataProvider.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2018 Jay Berkenbilt +// Copyright (c) 2005-2019 Jay Berkenbilt // // This file is part of qpdf. // @@ -19,8 +19,8 @@ // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. -#ifndef __RANDOMDATAPROVIDER_HH__ -#define __RANDOMDATAPROVIDER_HH__ +#ifndef RANDOMDATAPROVIDER_HH +#define RANDOMDATAPROVIDER_HH #include <string.h> // for size_t @@ -42,4 +42,4 @@ class RandomDataProvider RandomDataProvider& operator=(RandomDataProvider const&); }; -#endif // __RANDOMDATAPROVIDER_HH__ +#endif // RANDOMDATAPROVIDER_HH diff --git a/include/qpdf/Types.h b/include/qpdf/Types.h index ae959fed..aa0695f1 100644 --- a/include/qpdf/Types.h +++ b/include/qpdf/Types.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2005-2018 Jay Berkenbilt +/* Copyright (c) 2005-2019 Jay Berkenbilt * * This file is part of qpdf. * @@ -20,8 +20,8 @@ * see the manual for additional information. */ -#ifndef __QPDFTYPES_H__ -#define __QPDFTYPES_H__ +#ifndef QPDFTYPES_H +#define QPDFTYPES_H /* Provide an offset type that should be as big as off_t on just about * any system. If your compiler doesn't support C99 (or at least the @@ -30,4 +30,4 @@ typedef long long int qpdf_offset_t; -#endif /* __QPDFTYPES_H__ */ +#endif /* QPDFTYPES_H */ diff --git a/include/qpdf/qpdf-c.h b/include/qpdf/qpdf-c.h index a6002a92..c0983a53 100644 --- a/include/qpdf/qpdf-c.h +++ b/include/qpdf/qpdf-c.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2005-2018 Jay Berkenbilt +/* Copyright (c) 2005-2019 Jay Berkenbilt * * This file is part of qpdf. * @@ -20,8 +20,8 @@ * see the manual for additional information. */ -#ifndef __QPDF_C_H__ -#define __QPDF_C_H__ +#ifndef QPDF_C_H +#define QPDF_C_H /* * This file defines a basic "C" API for qpdf. It provides access to @@ -390,6 +390,40 @@ extern "C" { QPDF_BOOL allow_extract, QPDF_BOOL allow_annotate); QPDF_DLL + void qpdf_set_r3_encryption_parameters2( + qpdf_data qpdf, char const* user_password, char const* owner_password, + QPDF_BOOL allow_accessibility, QPDF_BOOL allow_extract, + QPDF_BOOL allow_assemble, QPDF_BOOL allow_annotate_and_form, + QPDF_BOOL allow_form_filling, QPDF_BOOL allow_modify_other, + enum qpdf_r3_print_e print); + + QPDF_DLL + void qpdf_set_r4_encryption_parameters2( + qpdf_data qpdf, char const* user_password, char const* owner_password, + QPDF_BOOL allow_accessibility, QPDF_BOOL allow_extract, + QPDF_BOOL allow_assemble, QPDF_BOOL allow_annotate_and_form, + QPDF_BOOL allow_form_filling, QPDF_BOOL allow_modify_other, + enum qpdf_r3_print_e print, + QPDF_BOOL encrypt_metadata, QPDF_BOOL use_aes); + + QPDF_DLL + void qpdf_set_r5_encryption_parameters2( + qpdf_data qpdf, char const* user_password, char const* owner_password, + QPDF_BOOL allow_accessibility, QPDF_BOOL allow_extract, + QPDF_BOOL allow_assemble, QPDF_BOOL allow_annotate_and_form, + QPDF_BOOL allow_form_filling, QPDF_BOOL allow_modify_other, + enum qpdf_r3_print_e print, QPDF_BOOL encrypt_metadata); + + QPDF_DLL + void qpdf_set_r6_encryption_parameters2( + qpdf_data qpdf, char const* user_password, char const* owner_password, + QPDF_BOOL allow_accessibility, QPDF_BOOL allow_extract, + QPDF_BOOL allow_assemble, QPDF_BOOL allow_annotate_and_form, + QPDF_BOOL allow_form_filling, QPDF_BOOL allow_modify_other, + enum qpdf_r3_print_e print, QPDF_BOOL encrypt_metadata); + + /* Pre 8.4.0 encryption API */ + QPDF_DLL void qpdf_set_r3_encryption_parameters( qpdf_data qpdf, char const* user_password, char const* owner_password, QPDF_BOOL allow_accessibility, QPDF_BOOL allow_extract, @@ -442,4 +476,4 @@ extern "C" { #endif -#endif /* __QPDF_C_H__ */ +#endif /* QPDF_C_H */ |