Commit b8f20fe34c1e91e312e904cf828cb164dac2326e

Authored by Jay Berkenbilt
Committed by GitHub
2 parents c630c9e4 607345d0

Merge pull request #998 from m-holger/todo

Add content table to TODO file
.git-blame-ignore-revs
... ... @@ -15,3 +15,4 @@ d740c6ccced02147f84a39d5e5f0984d12bac6cb
15 15 # Reflow comments and strings to 100 columns
16 16 698a70e6a84cf7c0db667e9d9e021b4c34c85a3e
17 17 3c5700c255f4603b5df9c6d183d13dd71a083cc3
  18 +9ae7bdea966102f9621b22192747a891078e7470
... ...
TODO deleted
1   -Always
2   -======
3   -
4   -* Evaluate issues tagged with `next` and `bug`. Remember to check
5   - discussions and pull requests in addition to regular issues.
6   -* When close to release, make sure external-libs is building and
7   - follow instructions in ../external-libs/README
8   -
9   -Next
10   -====
11   -
12   -* Fix #874 -- make args in --encrypt to match the json and make
13   - positional fill in the gaps
14   -* Maybe fix #553 -- use file times for attachments
15   -* std::string_view transition -- work being done by m-holger
16   -* Break ground on "Document-level work" -- TODO-pages.md lives on a
17   - separate branch.
18   -* Standard for CLI and Job JSON support for JSON-based command-line
19   - arguments. Come up with a standard way of supporting command-line
20   - arguments that take JSON specifications of things so that
21   - * there is a predictable way to indicate whether an argument is a
22   - file or a JSON blob
23   - * with QPDFJob JSON, make sure it is possible to directly include
24   - the JSON rather than having to stringify a JSON blob
25   - * One option might be to prepend file:// to a filename or otherwise
26   - to take a JSON blob. We could have that as a particular type of
27   - argument that would behave properly for both job JSON and CLI.
28   -
29   -
30   -Possible future JSON enhancements
31   -=================================
32   -
33   -* Consider not including unreferenced objects and trimming the trailer
34   - in the same way that QPDFWriter does (except don't remove `/ID`).
35   - This means excluding the linearization dictionary and hint stream,
36   - the encryption dictionary, all keys from trailer that are removed by
37   - QPDFWriter::getTrimmedTrailer except `/ID`, any object streams, and
38   - the xref stream as long as all those objects are unreferenced. (They
39   - always should be, but there could be some bizarre case of someone
40   - creating a PDF file that has an indirect reference to one of those,
41   - in which case we need to preserve it.) If this is done, make
42   - `--preserve-unreferenced` preserve unreference objects and also
43   - those extra keys. Search for "linear" and "trailer" in json.rst to
44   - update the various places in the documentation that discuss this.
45   - Also update the help for --json and --preserve-unreferenced.
46   -
47   -* Add to JSON output the information available from a few additional
48   - informational options:
49   -
50   - * --check: add but maybe not by default?
51   -
52   - * --show-linearization: add but maybe not by default? Also figure
53   - out whether warnings reported for some of the PDF specs (1.7) are
54   - qpdf problems. This may not be worth adding in the first
55   - increment.
56   -
57   - * --show-xref: add
58   -
59   -* Consider having --check, --show-encryption, etc., just select the
60   - right keys when in json mode. I don't think I want check on by
61   - default, so that might be different.
62   -
63   -* Consider having warnings be included in the json in a "warnings" key
64   - in json mode.
65   -
66   -QPDFJob
67   -=======
68   -
69   -Here are some ideas for QPDFJob that didn't make it into 10.6. Not all
70   -of these are necessarily good -- just things to consider.
71   -
72   -* How do we chain jobs? The idea would be that the input and/or output
73   - of a QPDFJob could be a QPDF object rather than a file. For input,
74   - it's pretty easy. For output, none of the output-specific options
75   - (encrypt, compress-streams, objects-streams, etc.) would have any
76   - affect, so we would have to treat this like inspect for error
77   - checking. The QPDF object in the state where it's ready to be sent
78   - off to QPDFWriter would be used as the input to the next QPDFJob.
79   - For the job json, I think we can have the output be an identifier
80   - that can be used as the input for another QPDFJob. For a json file,
81   - we could the top level detect if it's an array with the convention
82   - that exactly one has an output, or we could have a subkey with other
83   - job definitions or something. Ideally, any input
84   - (copy-attachments-from, pages, etc.) could use a QPDF object. It
85   - wouldn't surprise me if this exposes bugs in qpdf around foreign
86   - streams as this has been a relatively fragile area before.
87   -
88   -Documentation
89   -=============
90   -
91   -* Do a full pass through the documentation.
92   -
93   - * Make sure `qpdf` is consistent. Use QPDF when just referring to
94   - the package.
95   - * Make sure markup is consistent
96   - * Autogenerate where possible
97   - * Consider which parts might be good candidates for moving to the
98   - wiki.
99   -
100   -* Commit 'Manual - enable line wrapping in table cells' from
101   - Mon Jan 17 12:22:35 2022 +0000 enables table cell wrapping. See if
102   - this can be incorporated directly into sphinx_rtd_theme and the
103   - workaround can be removed.
104   -
105   -* When possible, update the debian package to include docs again. See
106   - https://bugs.debian.org/1004159 for details.
107   -
108   -Document-level work
109   -===================
110   -
111   -* Ideas here may by superseded by #593.
112   -
113   -* QPDFPageCopier -- object for moving pages around within files or
114   - between files and performing various transformations. Reread/rewrite
115   - _page-selection in the manual if needed.
116   -
117   - * Handle all the stuff of pages and split-pages
118   - * Do n-up, booklet, collation
119   - * Look through cli and see what else...flatten-*?
120   - * See comments in QPDFPageDocumentHelper.hh for addPage -- search
121   - for "a future version".
122   - * Make it efficient for bulk operations
123   - * Make certain doc-level features selectable
124   - * qpdf.cc should do all its page operations, including
125   - overlay/underlay, splitting, and merging, using this
126   - * There should also be example code
127   -
128   -* After doc-level checks are in, call --check on the output files in
129   - the "Copy Annotations" tests.
130   -
131   -* Document-level checks. For example, for forms, make sure all form
132   - fields point to an annotation on exactly one page as well as that
133   - all widget annotations are associated with a form field. Hook this
134   - into QPDFPageCopier as well as the doc helpers. Make sure it is
135   - called from --check.
136   -
137   -* See also issues tagged with "pages". Include closed issues.
138   -
139   -* Add flags to CLI to select which document-level options to
140   - preserve or not preserve. We will probably need a pair of mutually
141   - exclusive, repeatable options with a way to specify all, none, only
142   - {x,y}, or all but {x,y}.
143   -
144   -* If a page contains a reference a file attachment annotation, when
145   - that page is copied, if the file attachment appears in the top-level
146   - EmbeddedFiles tree, that entry should be preserved in the
147   - destination file. Otherwise, we probably will require the use of
148   - --copy-attachments-from to preserve these. What will the strategy be
149   - for deduplicating in the automatic case?
150   -
151   -Text Appearance Streams
152   -=======================
153   -
154   -This is a list of known issues with text appearance streams and things
155   -we might do about it.
156   -
157   -* For variable text, the spec says to pull any resources from /DR that
158   - are referenced in /DA but if the resource dictionary already has
159   - that resource, just use the one that's there. The current code looks
160   - only for /Tf and adds it if needed. We might want to instead merge
161   - /DR with resources and then remove anything that's unreferenced. We
162   - have all the code required for that in ResourceFinder except
163   - TfFinder also gets the font size, which ResourceFinder doesn't do.
164   -
165   -* There are things we are missing because we don't look at font
166   - metrics. The code from TextBuilder (work) has almost everything in
167   - it that is required. Once we have knowledge of character widths, we
168   - can support quadding and multiline text fields (/Ff 4096), and we
169   - can potentially squeeze text to fit into a field. For multiline,
170   - first squeeze vertically down to the font height, then squeeze
171   - horizontally with Tz. For single line, squeeze horizontally with Tz.
172   - If we use Tz, issue a warning.
173   -
174   -* When mapping characters to widths, we will need to care about
175   - character encoding. For built-in fonts, we can create a map from
176   - Unicode code point to width and then go from the font's encoding to
177   - unicode to the width. See misc/character-encoding/ (not on github)
178   - and font metric information for the 14 standard fonts in my local
179   - pdf-spec directory.
180   -
181   -* Once we know about character widths, we can correctly support
182   - auto-sized variable text fields (0 Tf). If this is fixed, search for
183   - "auto-sized" in cli.rst.
184   -
185   -Fuzz Errors
186   -===========
187   -
188   -* https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=<N>
189   -
190   -* Ignoring these:
191   - * Out of memory in dct: 35001, 32516
192   -
193   -External Libraries
194   -==================
195   -
196   -Current state (10.0.2):
197   -
198   -* qpdf/external-libs repository builds external-libs on a schedule.
199   - It detects and downloads the latest versions of zlib, jpeg, and
200   - openssl and creates source and binary distribution zip files in an
201   - artifact called "distribution".
202   -
203   -* Releases in qpdf/external-libs are made manually. They contain
204   - qpdf-external-libs-{bin,src}.zip.
205   -
206   -* The qpdf build finds the latest non-prerelease release and downloads
207   - the qpdf-external-libs-*.zip files from the releases in the setup
208   - stage.
209   -
210   -* To upgrade to a new version of external-libs, create a new release
211   - of qpdf/external-libs (see README-maintainer in external-libs) from
212   - the distribution artifact of the most recent successful build after
213   - ensuring that it works.
214   -
215   -Desired state:
216   -
217   -* The qpdf/external-libs repository should create release candidates.
218   - Ideally, every scheduled run would make its zip files available. A
219   - personal access token with actions:read scope for the
220   - qpdf/external-libs repository is required to download the artifact
221   - from an action run, and qpdf/qpdf's secrets.GITHUB_TOKEN doesn't
222   - have this access. We could create a service account for this
223   - purpose. As an alternative, we could have a draft release in
224   - qpdf/external-libs that the qpdf/external-libs build could update
225   - with each candidate. It may also be possible to solve this by
226   - developing a simple GitHub app.
227   -
228   -* Scheduled runs of the qpdf build in the qpdf/qpdf repository (not a
229   - fork or pull request) could download external-libs from the release
230   - candidate area instead of the latest stable release. Pushes to the
231   - build branch should still use the latest release so it always
232   - matches the main branch.
233   -
234   -* Periodically, we would create a release of external-libs from the
235   - release candidate zip files. This could be done safely because we
236   - know the latest qpdf works with it. This could be done at least
237   - before every release of qpdf, but potentially it could be done at
238   - other times, such as when a new dependency version is available or
239   - after some period of time.
240   -
241   -Other notes:
242   -
243   -* The external-libs branch in qpdf/qpdf was never documented. We might
244   - be able to get away with deleting it.
245   -
246   -* See README-maintainer in qpdf/external-libs for information on
247   - creating a release. This could be at least partially scripted in a
248   - way that works for the qpdf/qpdf repository as well since they are
249   - very similar.
250   -
251   -ABI Changes
252   -===========
253   -
254   -This is a list of changes to make next time there is an ABI change.
255   -Comments appear in the code prefixed by "ABI".
256   -
257   -Always:
258   -* Search for ABI in source and header files
259   -* Search for "[[deprecated" to find deprecated APIs that can be removed
260   -* Search for issues, pull requests, and discussions with the "abi" label
261   -* Check discussion "qpdf X planning" where X is the next major
262   - version. This should be tagged `abi`
263   -
264   -For qpdf 12, see https://github.com/qpdf/qpdf/discussions/785
265   -
266   -C++ Version Changes
267   -===================
268   -
269   -Use
270   -// C++NN: ...
271   -to mark places in the code that should be updated when we require at
272   -least that version of C++.
273   -
274   -Page splitting/merging
275   -======================
276   -
277   - * Update page splitting and merging to handle document-level
278   - constructs with page impact such as interactive forms and article
279   - threading. Check keys in the document catalog for others, such as
280   - outlines, page labels, thumbnails, and zones. For threads,
281   - Subramanyam provided a test file; see ../misc/article-threads.pdf.
282   - Email Q-Count: 431864 from 2009-11-03.
283   -
284   - * bookmarks (outlines) 12.3.3
285   - * support bookmarks when merging
286   - * prune bookmarks that don't point to a surviving page when merging
287   - or splitting
288   - * make sure conflicting named destinations work possibly test by
289   - including the same file by two paths in a merge
290   - * see also comments in issue 343
291   -
292   - Note: original implementation of bookmark preservation for split
293   - pages caused a very high performance hit. The problem was
294   - introduced in 313ba081265f69ac9a0324f9fe87087c72918191 and reverted
295   - in the commit that adds this paragraph. The revert includes marking
296   - a few tests cases as $td->EXPECT_FAILURE. When properly coded, the
297   - test cases will need to be adjusted to only include the parts of
298   - the outlines that are actually copied. The tests in question are
299   - "split page with outlines". When implementing properly, ensure that
300   - the performance is not adversely affected by timing split-pages on
301   - a large file with complex outlines such as the PDF specification.
302   -
303   - When pruning outlines, keep all outlines in the hierarchy that are
304   - above an outline for a page we care about. If one of the ancestor
305   - outlines points to a non-existent page, clear its dest. If an
306   - outline does not have any children that point to pages in the
307   - document, just omit it.
308   -
309   - Possible strategy:
310   - * resolve all named destinations to explicit destinations
311   - * concatenate top-level outlines
312   - * prune outlines whose dests don't point to a valid page
313   - * recompute all /Count fields
314   -
315   - Test files
316   - * page-labels-and-outlines.pdf: old file with both page labels and
317   - outlines. All destinations are explicit destinations. Each page
318   - has Potato and a number. All titles are feline names.
319   - * outlines-with-actions.pdf: mixture of explicit destinations,
320   - named destinations, goto actions with explicit destinations, and
321   - goto actions with named destinations; uses /Dests key in names
322   - dictionary. Each page has Salad and a number. All titles are
323   - silly words. One destination is an indirect object.
324   - * outlines-with-old-root-dests.pdf: like outlines-with-actions
325   - except it uses the PDF-1.1 /Dests dictionary for named
326   - destinations, and each page has Soup and a number. Also pages are
327   - numbered with upper-case Roman numerals starting with 0. All
328   - titles are silly words preceded by a bullet.
329   -
330   - If outline handling is significantly improved, see
331   - ../misc/bad-outlines/bad-outlines.pdf and email:
332   - https://mail.google.com/mail/u/0/#search/rfc822msgid%3A02aa01d3d013%249f766990%24de633cb0%24%40mono.hr)
333   -
334   - * Form fields: should be similar to outlines.
335   -
336   -Analytics
337   -=========
338   -
339   -Consider features that make it easier to detect certain patterns in
340   -PDF files. The information below could be computed using an external
341   -program that reads the existing json, but if it's useful enough, we
342   -could add it directly to the json output.
343   -
344   - * Add to "pages" in the json:
345   - * "inheritsresources": bool; whether there are any inherited
346   - attributes from ancestor page tree nodes
347   - * "sharedresources": a list of indirect objects that are
348   - "/Resources" dictionaries or "XObject" resource dictionary subkeys
349   - of either the page itself or of any form XObject referenced by the
350   - page.
351   -
352   - * Add to "objectinfo" in json: "directpagerefcount": the number of
353   - pages that directly reference this object (i.e., you can find an
354   - indirect reference to the object in the page dictionary without
355   - traversing over any indirect objects)
356   -
357   -General
358   -=======
359   -
360   -NOTE: Some items in this list refer to files in my personal home
361   -directory or that are otherwise not publicly accessible. This includes
362   -things sent to me by email that are specifically not public. Even so,
363   -I find it useful to make reference to them in this list.
364   -
365   -* Consider enabling code scanning on GitHub.
366   -
367   -* Add an option --ignore-encryption to ignore encryption information
368   - and treat encrypted files as if they weren't encrypted. This should
369   - make it possible to solve #598 (--show-encryption without a
370   - password). We'll need to make sure we don't try to filter any
371   - streams in this mode. Ideally we should be able to combine this with
372   - --json so we can look at the raw encrypted strings and streams if we
373   - want to, though be sure to document that the resulting JSON won't be
374   - convertible back to a valid PDF. Since providing the password may
375   - reveal additional details, --show-encryption could potentially retry
376   - with this option if the first time doesn't work. Then, with the file
377   - open, we can read the encryption dictionary normally. If this is
378   - done, search for "raw, encrypted" in json.rst.
379   -
380   -* In libtests, separate executables that need the object library
381   - from those that strictly use public API. Move as many of the test
382   - drivers from the qpdf directory into the latter category as long
383   - as doing so isn't too troublesome from a coverage standpoint.
384   -
385   -* Consider generating a non-flat pages tree before creating output to
386   - better handle files with lots of pages. If there are more than 256
387   - pages, add a second layer with the second layer nodes having no more
388   - than 256 nodes and being as evenly sizes as possible. Don't worry
389   - about the case of more than 65,536 pages. If the top node has more
390   - than 256 children, we'll live with it. This is only safe if all
391   - intermediate page nodes have only /Kids, /Parent, /Type, and /Count.
392   -
393   -* Look at https://bestpractices.coreinfrastructure.org/en
394   -
395   -* Consider adding fuzzer code for JSON
396   -
397   -* Rework tests so that nothing is written into the source directory.
398   - Ideally then the entire build could be done with a read-only
399   - source tree.
400   -
401   -* Large file tests fail with linux32 before and after cmake. This was
402   - first noticed after 10.6.3. I don't think it's worth fixing.
403   -
404   -* Consider updating the fuzzer with code that exercises
405   - copyAnnotations, file attachments, and name and number trees. Check
406   - fuzzer coverage.
407   -
408   -* Add code for creation of a file attachment annotation. It should
409   - also be possible to create a widget annotation and a form field.
410   - Update the pdf-attach-file.cc example with new APIs when ready.
411   -
412   -* Flattening of form XObjects seems like something that would be
413   - useful in the library. We are seeing more cases of completely valid
414   - PDF files with form XObjects that cause problems in other software.
415   - Flattening of form XObjects could be a useful way to work around
416   - those issues or to prepare files for additional processing, making
417   - it possible for users of the qpdf library to not be concerned about
418   - form XObjects. This could be done recursively; i.e., we could have a
419   - method to embed a form XObject into whatever contains it, whether
420   - that is a form XObject or a page. This would require more
421   - significant interpretation of the content stream. We would need a
422   - test file in which the placement of the form XObject has to be in
423   - the right place, e.g., the form XObject partially obscures earlier
424   - code and is partially obscured by later code. Keys in the resource
425   - dictionary may need to be changed -- create test cases with lots of
426   - duplicated/overlapping keys.
427   -
428   -* Part of closed_file_input_source.cc is disabled on Windows because
429   - of odd failures. It might be worth investigating so we can fully
430   - exercise this in the test suite. That said, ClosedFileInputSource
431   - is exercised elsewhere in qpdf's test suite, so this is not that
432   - pressing.
433   -
434   -* If possible, consider adding CCITT3, CCITT4, or any other easy
435   - filters. For some reference code that we probably can't use but may
436   - be handy anyway, see
437   - http://partners.adobe.com/public/developer/ps/sdk/index_archive.html
438   -
439   -* If possible, support the following types of broken files:
440   -
441   - - Files that have no whitespace token after "endobj" such that
442   - endobj collides with the start of the next object
443   -
444   - - See ../misc/broken-files
445   -
446   - - See ../misc/bad-files-issue-476. This directory contains a
447   - snapshot of the google doc and linked PDF files from issue #476.
448   - Please see the issue for details.
449   -
450   -* Additional form features
451   - * set value from CLI? Specify title, and provide way to
452   - disambiguate, probably by giving objgen of field
453   -
454   -* Pl_TIFFPredictor is pretty slow.
455   -
456   -* Support for handling file names with Unicode characters in Windows
457   - is incomplete. qpdf seems to support them okay from a functionality
458   - standpoint, and the right thing happens if you pass in UTF-8
459   - encoded filenames to QPDF library routines in Windows (they are
460   - converted internally to wchar_t*), but file names are encoded in
461   - UTF-8 on output, which doesn't produce nice error messages or
462   - output on Windows in some cases.
463   -
464   -* If we ever wanted to do anything more with character encoding, see
465   - ../misc/character-encoding/, which includes machine-readable dump
466   - of table D.2 in the ISO-32000 PDF spec. This shows the mapping
467   - between Unicode, StandardEncoding, WinAnsiEncoding,
468   - MacRomanEncoding, and PDFDocEncoding.
469   -
470   -* Some test cases on bad files fail because qpdf is unable to find
471   - the root dictionary when it fails to read the trailer. Recovery
472   - could find the root dictionary and even the info dictionary in
473   - other ways. In particular, issue-202.pdf can be opened by evince,
474   - and there's no real reason that qpdf couldn't be made to be able to
475   - recover that file as well.
476   -
477   -* Audit every place where qpdf allocates memory to see whether there
478   - are cases where malicious inputs could cause qpdf to attempt to
479   - grab very large amounts of memory. Certainly there are cases like
480   - this, such as if a very highly compressed, very large image stream
481   - is requested in a buffer. Hopefully normal input to output
482   - filtering doesn't ever try to do this. QPDFWriter should be checked
483   - carefully too. See also bugs/private/from-email-663916/
484   -
485   -* Interactive form modification:
486   - https://github.com/qpdf/qpdf/issues/213 contains a good discussion
487   - of some ideas for adding methods to modify annotations and form
488   - fields if we want to make it easier to support modifications to
489   - interactive forms. Some of the ideas have been implemented, and
490   - some of the probably never will be implemented, but it's worth a
491   - read if there is an intention to work on this. In the issue, search
492   - for "Regarding write functionality", and read that comment and the
493   - responses to it.
494   -
495   -* Look at ~/Q/pdf-collection/forms-from-appian/
496   -
497   -* When decrypting files with /R=6, hash_V5 is called more than once
498   - with the same inputs. Caching the results or refactoring to reduce
499   - the number of identical calls could improve performance for
500   - workloads that involve processing large numbers of small files.
501   -
502   -* Consider adding a method to balance the pages tree. It would call
503   - pushInheritedAttributesToPage, construct a pages tree from scratch,
504   - and replace the /Pages key of the root dictionary with the new
505   - tree.
506   -
507   -* Study what's required to support savable forms that can be saved by
508   - Adobe Reader. Does this require actually signing the document with
509   - an Adobe private key? Search for "Digital signatures" in the PDF
510   - spec, and look at ~/Q/pdf-collection/form-with-full-save.pdf, which
511   - came from Adobe's example site. See also
512   - ../misc/digital-sign-from-trueroad/ and
513   - ../misc/digital-signatures/digitally-signed-pdf-xfa.pdf. If digital
514   - signatures are implemented, update the docs on crypto providers,
515   - which mention that this may happen in the future.
516   -
517   -* Qpdf does not honor /EFF when adding new file attachments. When it
518   - encrypts, it never generates streams with explicit crypt filters.
519   - Prior to 10.2, there was an incorrect attempt to treat /EFF as a
520   - default value for decrypting file attachment streams, but it is not
521   - supposed to mean that. Instead, it is intended for conforming
522   - writers to obey this when adding new attachments. Qpdf is not a
523   - conforming writer in that respect.
524   -
525   -* The whole xref handling code in the QPDF object allows the same
526   - object with more than one generation to coexist, but a lot of logic
527   - assumes this isn't the case. Anything that creates mappings only
528   - with the object number and not the generation is this way,
529   - including most of the interaction between QPDFWriter and QPDF. If
530   - we wanted to allow the same object with more than one generation to
531   - coexist, which I'm not sure is allowed, we could fix this by
532   - changing xref_table. Alternatively, we could detect and disallow
533   - that case. In fact, it appears that Adobe reader and other PDF
534   - viewing software silently ignores objects of this type, so this is
535   - probably not a big deal.
536   -
537   -* From a suggestion in bug 3152169, consider having an option to
538   - re-encode inline images with an ASCII encoding.
539   -
540   -* From github issue 2, provide more in-depth output for examining
541   - hint stream contents. Consider adding on option to provide a
542   - human-readable dump of linearization hint tables. This should
543   - include improving the 'overflow reading bit stream' message as
544   - reported in issue #2. There are multiple calls to stopOnError in
545   - the linearization checking code. Ideally, these should not
546   - terminate checking. It would require re-acquiring an understanding
547   - of all that code to make the checks more robust. In particular,
548   - it's hard to look at the code and quickly determine what is a true
549   - logic error and what could happen because of malformed user input.
550   - See also ../misc/linearization-errors.
551   -
552   -* If I ever decide to make appearance stream-generation aware of
553   - fonts or font metrics, see email from Tobias with Message-ID
554   - <5C3C9C6C.8000102@thax.hardliners.org> dated 2019-01-14.
555   -
556   -* Look at places in the code where object traversal is being done and,
557   - where possible, try to avoid it entirely or at least avoid ever
558   - traversing the same objects multiple times.
559   -
560   -----------------------------------------------------------------------
561   -
562   -HISTORICAL NOTES
563   -
564   -Performance
565   -===========
566   -
567   -As described in https://github.com/qpdf/qpdf/issues/401, there was
568   -great performance degradation between qpdf 7.1.1 and 9.1.1. Doing a
569   -bisect between dac65a21fb4fa5f871e31c314280b75adde89a6c and
570   -release-qpdf-7.1.1, I found several commits that damaged performance.
571   -I fixed some of them to improve performance by about 70% (as measured
572   -by saying that old times were 170% of new times). The remaining
573   -commits that broke performance either can't be correct because they
574   -would re-introduce an old bug or aren't worth correcting because of
575   -the high value they offer relative to a relatively low penalty. For
576   -historical reference, here are the commits. The numbers are the time
577   -in seconds on the machine I happened to be using of splitting the
578   -first 100 pages of PDF32000_2008.pdf 20 times and taking an average
579   -duration.
580   -
581   -Commits that broke performance:
582   -
583   -* d0e99f195a987c483bbb6c5449cf39bee34e08a1 -- object description and
584   - context: 0.39 -> 0.45
585   -* a01359189b32c60c2d55b039f7aefd6c3ce0ebde (minus 313ba08) -- fix
586   - dangling references: 0.55 -> 0.6
587   -* e5f504b6c5dc34337cc0b316b4a7b1fca7e614b1 -- sparse array: 0.6 -> 0.62
588   -
589   -Other intermediate steps that were previously fixed:
590   -
591   -* 313ba081265f69ac9a0324f9fe87087c72918191 -- copy outlines into
592   - split: 0.55 -> 4.0
593   -* a01359189b32c60c2d55b039f7aefd6c3ce0ebde -- fix dangling references:
594   - 4.0 -> 9.0
595   -
596   -This commit fixed the awful problem introduced in 313ba081:
597   -
598   -* a5a016cdd26a8e5c99e5f019bc30d1bdf6c050a2 -- revert outline
599   - preservation: 9.0 -> 0.6
600   -
601   -Note that the fix dangling references commit had a much worse impact
602   -prior to removing the outline preservation, so I also measured its
603   -impact in isolation.
604   -
605   -A few important lessons (in README-maintainer)
606   -
607   -* Indirection through PointerHolder<Members> is expensive, and should
608   - not be used for things that are created and destroyed frequently
609   - such as QPDFObjectHandle and QPDFObject.
610   -* Traversal of objects is expensive and should be avoided where
611   - possible.
612   -
613   -Also, it turns out that PointerHolder is more performant than
614   -std::shared_ptr. (This was true at the time but subsequent
615   -implementations of std::shared_ptr became much more efficient.)
616   -
617   -QPDFPagesTree
618   -=============
619   -
620   -On a few occasions, I have considered implementing a QPDFPagesTree
621   -object that would allow the document's original page tree structure to
622   -be preserved. See comments at the top QPDF_pages.cc for why this was
623   -abandoned.
624   -
625   -Partial work is in refs/attic/QPDFPagesTree. QPDFPageTree is mostly
626   -implemented and mostly tested. There are not enough cases of different
627   -kinds of operations (pclm, linearize, json, etc.) with non-flat pages
628   -trees. Insertion is not implemented. Insertion is potentially complex
629   -because of the issue of inherited objects. We will have to call
630   -pushInheritedAttributesToPage before adding any pages to the pages
631   -tree. The test suite is failing on that branch.
632   -
633   -Some parts of page tree repair are silent (no warnings). All page tree
634   -repair should warn. The reason is that page tree repair will change
635   -object numbers, and knowing that is important when working with JSON
636   -output.
637   -
638   -If we were to do this, we would still need keep a pages cache for
639   -efficient insertion. There's no reason we can't keep a vector of page
640   -objects up to date and just do a traversal the first time we do
641   -getAllPages just like we do now. The difference is that we would not
642   -flatten the pages tree. It would be useful to go through QPDF_pages
643   -and reimplement everything without calling flattenPagesTree. Then we
644   -can remove flattenPagesTree, which is private. That said, with the
645   -addition of creating non-flat pages trees, there is really no reason
646   -not to flatten the pages tree for internal use.
647   -
648   -In its current state, QPDFPagesTree does not proactively fix /Type or
649   -correct page objects that are used multiple times. You have to
650   -traverse the pages tree to trigger this operation. It would be nice if
651   -we would do that somewhere but not do it more often than necessary so
652   -isPagesObject and isPageObject are reliable and can be made more
653   -reliable. Maybe add a validate or repair function? It should also make
654   -sure /Count and /Parent are correct.
655   -
656   -Rejected Ideas
657   -==============
658   -
659   -* Investigate whether there is a way to automate the memory checker
660   - tests for Windows.
661   -
662   -* Provide support in QPDFWriter for writing incremental updates.
663   - Provide support in qpdf for preserving incremental updates. The
664   - goal should be that QDF mode should be fully functional for files
665   - with incremental updates including fix_qdf.
666   -
667   - Note that there's nothing that says an indirect object in one
668   - update can't refer to an object that doesn't appear until a later
669   - update. This means that QPDF has to treat indirect null objects
670   - differently from how it does now. QPDF drops indirect null objects
671   - that appear as members of arrays or dictionaries. For arrays, it's
672   - handled in QPDFWriter where we make indirect nulls direct. This is
673   - in a single if block, and nothing else in the code cares about it.
674   - We could just remove that if block and not break anything except a
675   - few test cases that exercise the current behavior. For
676   - dictionaries, it's more complicated. In this case,
677   - QPDF_Dictionary::getKeys() ignores all keys with null values, and
678   - hasKey() returns false for keys that have null values. We would
679   - probably want to make QPDF_Dictionary able to handle the special
680   - case of keys that are indirect nulls and basically never have it
681   - drop any keys that are indirect objects.
682   -
683   - If we make a change to have qpdf preserve indirect references to
684   - null objects, we have to note this in ChangeLog and in the release
685   - notes since this will change output files. We did this before when
686   - we stopped flattening scalar references, so this is probably not a
687   - big deal. We also have to make sure that the testing for this
688   - handles non-trivial cases of the targets of indirect nulls being
689   - replaced by real objects in an update. I'm not sure how this plays
690   - with linearization, if at all. For cases where incremental updates
691   - are not being preserved as incremental updates and where the data
692   - is being folded in (as is always the case with qpdf now), none of
693   - this should make any difference in the actual semantics of the
694   - files.
695   -
696   -* The second xref stream for linearized files has to be padded only
697   - because we need file_size as computed in pass 1 to be accurate. If
698   - we were not allowing writing to a pipe, we could seek back to the
699   - beginning and fill in the value of /L in the linearization
700   - dictionary as an optimization to alleviate the need for this
701   - padding. Doing so would require us to pad the /L value
702   - individually and also to save the file descriptor and determine
703   - whether it's seekable. This is probably not worth bothering with.
704   -
705   -* Based on an idea suggested by user "Atom Smasher", consider
706   - providing some mechanism to recover earlier versions of a file
707   - embedded prior to appended sections.
708   -
709   -* Consider creating a sanitizer to make it easier for people to send
710   - broken files. Now that we have json mode, this is probably no
711   - longer worth doing. Here is the previous idea, possibly implemented
712   - by making it possible to run the lexer (tokenizer) over a whole
713   - file. Make it possible to replace all strings in a file lexically
714   - even on badly broken files. Ideally this should work files that are
715   - lacking xref, have broken links, duplicated dictionary keys, syntax
716   - errors, etc., and ideally it should work with encrypted files if
717   - possible. This should go through the streams and strings and
718   - replace them with fixed or random characters, preferably, but not
719   - necessarily, in a manner that works with fonts. One possibility
720   - would be to detect whether a string contains characters with normal
721   - encoding, and if so, use 0x41. If the string uses character maps,
722   - use 0x01. The output should otherwise be unrelated to the input.
723   - This could be built after the filtering and tokenizer rewrite and
724   - should be done in a manner that takes advantage of the other
725   - lexical features. This sanitizer should also clear metadata and
726   - replace images. If I ever do this, the file from issue #494 would
727   - be a great one to look at.
728   -
729   -* Here are some notes about having stream data providers modify
730   - stream dictionaries. I had wanted to add this functionality to make
731   - it more efficient to create stream data providers that may
732   - dynamically decide what kind of filters to use and that may end up
733   - modifying the dictionary conditionally depending on the original
734   - stream data. Ultimately I decided not to implement this feature.
735   - This paragraph describes why.
736   -
737   - * When writing, the way objects are placed into the queue for
738   - writing strongly precludes creation of any new indirect objects,
739   - or even changing which indirect objects are referenced from which
740   - other objects, because we sometimes write as we are traversing
741   - and enqueuing objects. For non-linearized files, there is a risk
742   - that an indirect object that used to be referenced would no
743   - longer be referenced, and whether it was already written to the
744   - output file would be based on an accident of where it was
745   - encountered when traversing the object structure. For linearized
746   - files, the situation is considerably worse. We decide which
747   - section of the file to write an object to based on a mapping of
748   - which objects are used by which other objects. Changing this
749   - mapping could cause an object to appear in the wrong section, to
750   - be written even though it is unreferenced, or to be entirely
751   - omitted since, during linearization, we don't enqueue new objects
752   - as we traverse for writing.
753   -
754   - * There are several places in QPDFWriter that query a stream's
755   - dictionary in order to prepare for writing or to make decisions
756   - about certain aspects of the writing process. If the stream data
757   - provider has the chance to modify the dictionary, every piece of
758   - code that gets stream data would have to be aware of this. This
759   - would potentially include end user code. For example, any code
760   - that called getDict() on a stream before installing a stream data
761   - provider and expected that dictionary to be valid would
762   - potentially be broken. As implemented right now, you must perform
763   - any modifications on the dictionary in advance and provided
764   - /Filter and /DecodeParms at the time you installed the stream
765   - data provider. This means that some computations would have to be
766   - done more than once, but for linearized files, stream data
767   - providers are already called more than once. If the work done by
768   - a stream data provider is especially expensive, it can implement
769   - its own cache.
770   -
771   - The example examples/pdf-custom-filter.cc demonstrates the use of
772   - custom stream filters. This includes a custom pipeline, a custom
773   - stream filter, as well as modification of a stream's dictionary to
774   - include creation of a new stream that is referenced from
775   - /DecodeParms.
776   -
777   -* Removal of raw QPDF* from the API. Discussions in #747 and #754.
778   - This is a summary of the arguments I put forth in #754. The idea was
779   - to make QPDF::QPDF() private and require all QPDF objects to be
780   - shared pointers created with QPDF::create(). This would enable us to
781   - have QPDFObjectHandle::getOwningQPDF() return a std::weak_ptr<QPDF>.
782   - Prior to #726 (QPDFObject/QPDFValue split, released in qpdf 11.0.0),
783   - getOwningQPDF() could return an invalid pointer if the owning QPDF
784   - disappeared, but this is no longer the case, which removes the main
785   - motivation. QPDF 11 added QPDF::create() anyway though.
786   -
787   - Removing raw QPDF* would look something like this. Note that you
788   - can't use std::make_shared<T> unless T has a public constructor.
789   -
790   - QPDF_POINTER_TRANSITION = 0 -- no warnings around calling the QPDF constructor
791   - QPDF_POINTER_TRANSITION = 1 -- calls to QPDF() are deprecated, but QPDF is still available so code can be backward compatible and use std::make_shared<QPDF>
792   - QPDF_POINTER_TRANSITION = 2 -- the QPDF constructor is private; all calls to std::make_shared<QPDF> have to be replaced with QPDF::create
793   -
794   - If we were to do this, we'd have to look at each use of QPDF* in the
795   - interface and decide whether to use a std::shared_ptr or a
796   - std::weak_ptr. The answer would almost always be to use a
797   - std::weak_ptr, which means we'd have to take the extra step of
798   - calling lock(), and it means there would be lots of code changes
799   - cause people would have to pass weak pointers instead of raw
800   - pointers around, and those have to be constructed and locked.
801   - Passing std::shared_ptr around leaves the possibility of creating
802   - circular references. It seems to be too much trouble in the library
803   - and too much toil for library users to be worth the small benefit of
804   - not having to call resetObjGen in QPDF's destructor.
805   -
806   -* Fix Multiple Direct Object Parent Issue
807   -
808   - This idea was rejected because it would be complicated to implement
809   - and would likely have a high performance cost to fix what is not
810   - really that big of a problem in practice.
811   -
812   - It is possible for a QPDFObjectHandle for a direct object to be
813   - contained inside of multiple QPDFObjectHandle objects or even
814   - replicated across multiple QPDF objects. This creates a potentially
815   - confusing and unintentional aliasing of direct objects. There are
816   - known cases in the qpdf library where this happens including page
817   - splitting and merging (particularly with page labels, and possibly
818   - with other cases), and also with unsafeShallowCopy. Disallowing this
819   - would incur a significant performance penalty and is probably not
820   - worth doing. If we were to do it, here are some ideas.
821   -
822   - * Add std::weak_ptr<QPDFObject> parent to QPDFObject. When adding a
823   - direct object to an array or dictionary, set its parent. When
824   - removing it, clear the parent pointer. The parent pointer would
825   - always be null for indirect objects, so the parent pointer, which
826   - would reside in QPDFObject, would have to be managed by
827   - QPDFObjectHandle. This is because QPDFObject can't tell the
828   - difference between a resolved indirect object and a direct object.
829   -
830   - * Phase 1: When a direct object that already has a parent is added
831   - to a dictionary or array, issue a warning. There would need to be
832   - unsafe add methods used by unsafeShallowCopy. These would add but
833   - not modify the parent pointer.
834   -
835   - * Phase 2: In the next major release, make the multiple parent case
836   - an error. Require people to create a copy. The unsafe operations
837   - would still have to be permitted.
838   -
839   - This approach would allow an object to be moved from one object to
840   - another by removing it, which returns the now orphaned object, and
841   - then inserting it somewhere else. It also doesn't break the pattern
842   - of adding a direct object to something and subsequently mutating it.
843   - It just prevents the same object from being added to more than one
844   - thing.
TODO.md 0 โ†’ 100644
  1 +Contents
  2 +========
  3 +
  4 +- [Always](#always)
  5 +- [Next](#always)
  6 +- [Possible future JSON enhancements](#possible-future-json-enhancements)
  7 +- [QPDFJob](#qpdfjob)
  8 +- [Documentation](#documentation)
  9 +- [Document-level work](#document-level-work)
  10 +- [Text Appearance Streams](#text-appearance-streams)
  11 +- [Fuzz Errors](#fuzz-errors)
  12 +- [External Libraries](#external-libraries)
  13 +- [ABI Changes](#abi-changes)
  14 +- [C++ Version Changes](#c-version-changes)
  15 +- [Page splitting/merging](#page-splittingmerging)
  16 +- [Analytics](#analytics)
  17 +- [General](#general)
  18 +
  19 +- [HISTORICAL NOTES](#historical-notes)
  20 +
  21 +Always
  22 +======
  23 +
  24 +* Evaluate issues tagged with `next` and `bug`. Remember to check discussions and pull requests in
  25 + addition to regular issues.
  26 +* When close to release, make sure external-libs is building and follow instructions in
  27 + ../external-libs/README
  28 +
  29 +Next
  30 +====
  31 +
  32 +* Fix #874 -- make args in --encrypt to match the json and make positional fill in the gaps
  33 +* Maybe fix #553 -- use file times for attachments
  34 +* std::string_view transition -- work being done by m-holger
  35 +* Break ground on "Document-level work" -- TODO-pages.md lives on a separate branch.
  36 +* Standard for CLI and Job JSON support for JSON-based command-line arguments. Come up with a
  37 + standard way of supporting command-line arguments that take JSON specifications of things so that
  38 + * there is a predictable way to indicate whether an argument is a file or a JSON blob
  39 + * with QPDFJob JSON, make sure it is possible to directly include the JSON rather than having to
  40 + stringify a JSON blob
  41 + * One option might be to prepend file:// to a filename or otherwise to take a JSON blob. We could
  42 + have that as a particular type of argument that would behave properly for both job JSON and CLI.
  43 +
  44 +Possible future JSON enhancements
  45 +=================================
  46 +
  47 +* Consider not including unreferenced objects and trimming the trailer in the same way that
  48 + QPDFWriter does (except don't remove `/ID`). This means excluding the linearization dictionary and
  49 + hint stream, the encryption dictionary, all keys from trailer that are removed by
  50 + QPDFWriter::getTrimmedTrailer except `/ID`, any object streams, and the xref stream as long as all
  51 + those objects are unreferenced. (They always should be, but there could be some bizarre case of
  52 + someone creating a PDF file that has an indirect reference to one of those, in which case we need
  53 + to preserve it.) If this is done, make `--preserve-unreferenced` preserve unreference objects and
  54 + also those extra keys. Search for "linear" and "trailer" in json.rst to update the various places
  55 + in the documentation that discuss this. Also update the help for --json and
  56 + --preserve-unreferenced.
  57 +
  58 +* Add to JSON output the information available from a few additional informational options:
  59 +
  60 + * --check: add but maybe not by default?
  61 +
  62 + * --show-linearization: add but maybe not by default? Also figure out whether warnings reported
  63 + for some of the PDF specs (1.7) are qpdf problems. This may not be worth adding in the first
  64 + increment.
  65 +
  66 + * --show-xref: add
  67 +
  68 +* Consider having --check, --show-encryption, etc., just select the right keys when in json mode. I
  69 + don't think I want check on by default, so that might be different.
  70 +
  71 +* Consider having warnings be included in the json in a "warnings" key in json mode.
  72 +
  73 +QPDFJob
  74 +=======
  75 +
  76 +Here are some ideas for QPDFJob that didn't make it into 10.6. Not all of these are necessarily
  77 +good -- just things to consider.
  78 +
  79 +* How do we chain jobs? The idea would be that the input and/or output of a QPDFJob could be a QPDF
  80 + object rather than a file. For input, it's pretty easy. For output, none of the output-specific
  81 + options (encrypt, compress-streams, objects-streams, etc.) would have any affect, so we would have
  82 + to treat this like inspect for error checking. The QPDF object in the state where it's ready to be
  83 + sent off to QPDFWriter would be used as the input to the next QPDFJob. For the job json, I think
  84 + we can have the output be an identifier that can be used as the input for another QPDFJob. For a
  85 + json file, we could the top level detect if it's an array with the convention that exactly one has
  86 + an output, or we could have a subkey with other job definitions or something. Ideally, any input
  87 + (copy-attachments-from, pages, etc.) could use a QPDF object. It wouldn't surprise me if this
  88 + exposes bugs in qpdf around foreign streams as this has been a relatively fragile area before.
  89 +
  90 +Documentation
  91 +=============
  92 +
  93 +* Do a full pass through the documentation.
  94 +
  95 + * Make sure `qpdf` is consistent. Use QPDF when just referring to the package.
  96 + * Make sure markup is consistent
  97 + * Autogenerate where possible
  98 + * Consider which parts might be good candidates for moving to the wiki.
  99 +
  100 +* Commit 'Manual - enable line wrapping in table cells' from Mon Jan 17 12:22:35 2022 +0000 enables
  101 + table cell wrapping. See if this can be incorporated directly into sphinx_rtd_theme and the
  102 + workaround can be removed.
  103 +
  104 +* When possible, update the debian package to include docs again. See
  105 + https://bugs.debian.org/1004159 for details.
  106 +
  107 +Document-level work
  108 +===================
  109 +
  110 +* Ideas here may by superseded by #593.
  111 +
  112 +* QPDFPageCopier -- object for moving pages around within files or between files and performing
  113 + various transformations. Reread/rewrite
  114 + _page-selection in the manual if needed.
  115 +
  116 + * Handle all the stuff of pages and split-pages
  117 + * Do n-up, booklet, collation
  118 + * Look through cli and see what else...flatten-*?
  119 + * See comments in QPDFPageDocumentHelper.hh for addPage -- search for "a future version".
  120 + * Make it efficient for bulk operations
  121 + * Make certain doc-level features selectable
  122 + * qpdf.cc should do all its page operations, including overlay/underlay, splitting, and merging,
  123 + using this
  124 + * There should also be example code
  125 +
  126 +* After doc-level checks are in, call --check on the output files in the "Copy Annotations" tests.
  127 +
  128 +* Document-level checks. For example, for forms, make sure all form fields point to an annotation on
  129 + exactly one page as well as that all widget annotations are associated with a form field. Hook
  130 + this into QPDFPageCopier as well as the doc helpers. Make sure it is called from --check.
  131 +
  132 +* See also issues tagged with "pages". Include closed issues.
  133 +
  134 +* Add flags to CLI to select which document-level options to preserve or not preserve. We will
  135 + probably need a pair of mutually exclusive, repeatable options with a way to specify all, none,
  136 + only {x,y}, or all but {x,y}.
  137 +
  138 +* If a page contains a reference a file attachment annotation, when that page is copied, if the file
  139 + attachment appears in the top-level EmbeddedFiles tree, that entry should be preserved in the
  140 + destination file. Otherwise, we probably will require the use of --copy-attachments-from to
  141 + preserve these. What will the strategy be for deduplicating in the automatic case?
  142 +
  143 +Text Appearance Streams
  144 +=======================
  145 +
  146 +This is a list of known issues with text appearance streams and things we might do about it.
  147 +
  148 +* For variable text, the spec says to pull any resources from /DR that are referenced in /DA but if
  149 + the resource dictionary already has that resource, just use the one that's there. The current code
  150 + looks only for /Tf and adds it if needed. We might want to instead merge /DR with resources and
  151 + then remove anything that's unreferenced. We have all the code required for that in ResourceFinder
  152 + except TfFinder also gets the font size, which ResourceFinder doesn't do.
  153 +
  154 +* There are things we are missing because we don't look at font metrics. The code from TextBuilder
  155 + (work) has almost everything in it that is required. Once we have knowledge of character widths,
  156 + we can support quadding and multiline text fields (/Ff 4096), and we can potentially squeeze text
  157 + to fit into a field. For multiline, first squeeze vertically down to the font height, then squeeze
  158 + horizontally with Tz. For single line, squeeze horizontally with Tz. If we use Tz, issue a
  159 + warning.
  160 +
  161 +* When mapping characters to widths, we will need to care about character encoding. For built-in
  162 + fonts, we can create a map from Unicode code point to width and then go from the font's encoding
  163 + to unicode to the width. See misc/character-encoding/ (not on github)
  164 + and font metric information for the 14 standard fonts in my local pdf-spec directory.
  165 +
  166 +* Once we know about character widths, we can correctly support auto-sized variable text fields
  167 + (0 Tf). If this is fixed, search for "auto-sized" in cli.rst.
  168 +
  169 +Fuzz Errors
  170 +===========
  171 +
  172 +* https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=<N>
  173 +
  174 +* Ignoring these:
  175 + * Out of memory in dct: 35001, 32516
  176 +
  177 +External Libraries
  178 +==================
  179 +
  180 +Current state (10.0.2):
  181 +
  182 +* qpdf/external-libs repository builds external-libs on a schedule. It detects and downloads the
  183 + latest versions of zlib, jpeg, and openssl and creates source and binary distribution zip files in
  184 + an artifact called "distribution".
  185 +
  186 +* Releases in qpdf/external-libs are made manually. They contain qpdf-external-libs-{bin,src}.zip.
  187 +
  188 +* The qpdf build finds the latest non-prerelease release and downloads the qpdf-external-libs-*.zip
  189 + files from the releases in the setup stage.
  190 +
  191 +* To upgrade to a new version of external-libs, create a new release of qpdf/external-libs (see
  192 + README-maintainer in external-libs) from the distribution artifact of the most recent successful
  193 + build after ensuring that it works.
  194 +
  195 +Desired state:
  196 +
  197 +* The qpdf/external-libs repository should create release candidates. Ideally, every scheduled run
  198 + would make its zip files available. A personal access token with actions:read scope for the
  199 + qpdf/external-libs repository is required to download the artifact from an action run, and
  200 + qpdf/qpdf's secrets.GITHUB_TOKEN doesn't have this access. We could create a service account for
  201 + this purpose. As an alternative, we could have a draft release in qpdf/external-libs that the
  202 + qpdf/external-libs build could update with each candidate. It may also be possible to solve this
  203 + by developing a simple GitHub app.
  204 +
  205 +* Scheduled runs of the qpdf build in the qpdf/qpdf repository (not a fork or pull request) could
  206 + download external-libs from the release candidate area instead of the latest stable release.
  207 + Pushes to the build branch should still use the latest release so it always matches the main
  208 + branch.
  209 +
  210 +* Periodically, we would create a release of external-libs from the release candidate zip files.
  211 + This could be done safely because we know the latest qpdf works with it. This could be done at
  212 + least before every release of qpdf, but potentially it could be done at other times, such as when
  213 + a new dependency version is available or after some period of time.
  214 +
  215 +Other notes:
  216 +
  217 +* The external-libs branch in qpdf/qpdf was never documented. We might be able to get away with
  218 + deleting it.
  219 +
  220 +* See README-maintainer in qpdf/external-libs for information on creating a release. This could be
  221 + at least partially scripted in a way that works for the qpdf/qpdf repository as well since they
  222 + are very similar.
  223 +
  224 +ABI Changes
  225 +===========
  226 +
  227 +This is a list of changes to make next time there is an ABI change. Comments appear in the code
  228 +prefixed by "ABI".
  229 +
  230 +Always:
  231 +* Search for ABI in source and header files
  232 +* Search for "[[deprecated" to find deprecated APIs that can be removed
  233 +* Search for issues, pull requests, and discussions with the "abi" label
  234 +* Check discussion "qpdf X planning" where X is the next major version. This should be tagged `abi`
  235 +
  236 +For qpdf 12, see https://github.com/qpdf/qpdf/discussions/785
  237 +
  238 +C++ Version Changes
  239 +===================
  240 +
  241 +Use
  242 +```
  243 +// C++NN: ...
  244 +```
  245 +to mark places in the code that should be updated when we require at least that version of C++.
  246 +
  247 +Page splitting/merging
  248 +======================
  249 +
  250 +* Update page splitting and merging to handle document-level constructs with page impact such as
  251 + interactive forms and article threading. Check keys in the document catalog for others, such as
  252 + outlines, page labels, thumbnails, and zones. For threads, Subramanyam provided a test file; see
  253 + ../misc/article-threads.pdf. Email Q-Count: 431864 from 2009-11-03.
  254 +
  255 +* bookmarks (outlines) 12.3.3
  256 + * support bookmarks when merging
  257 + * prune bookmarks that don't point to a surviving page when merging or splitting
  258 + * make sure conflicting named destinations work possibly test by including the same file by two
  259 + paths in a merge
  260 + * see also comments in issue 343
  261 +
  262 + Note: original implementation of bookmark preservation for split pages caused a very high
  263 + performance hit. The problem was introduced in 313ba081265f69ac9a0324f9fe87087c72918191 and
  264 + reverted in the commit that adds this paragraph. The revert includes marking a few tests cases as
  265 + $td->EXPECT_FAILURE. When properly coded, the test cases will need to be adjusted to only include
  266 + the parts of the outlines that are actually copied. The tests in question are
  267 + "split page with outlines". When implementing properly, ensure that the performance is not
  268 + adversely affected by timing split-pages on a large file with complex outlines such as the PDF
  269 + specification.
  270 +
  271 + When pruning outlines, keep all outlines in the hierarchy that are above an outline for a page we
  272 + care about. If one of the ancestor outlines points to a non-existent page, clear its dest. If an
  273 + outline does not have any children that point to pages in the document, just omit it.
  274 +
  275 + Possible strategy:
  276 + * resolve all named destinations to explicit destinations
  277 + * concatenate top-level outlines
  278 + * prune outlines whose dests don't point to a valid page
  279 + * recompute all /Count fields
  280 +
  281 + Test files
  282 + * page-labels-and-outlines.pdf: old file with both page labels and outlines. All destinations are
  283 + explicit destinations. Each page has Potato and a number. All titles are feline names.
  284 + * outlines-with-actions.pdf: mixture of explicit destinations, named destinations, goto actions
  285 + with explicit destinations, and goto actions with named destinations; uses /Dests key in names
  286 + dictionary. Each page has Salad and a number. All titles are silly words. One destination is an
  287 + indirect object.
  288 + * outlines-with-old-root-dests.pdf: like outlines-with-actions except it uses the PDF-1.1 /Dests
  289 + dictionary for named destinations, and each page has Soup and a number. Also pages are numbered
  290 + with upper-case Roman numerals starting with 0. All titles are silly words preceded by a bullet.
  291 +
  292 + If outline handling is significantly improved, see ../misc/bad-outlines/bad-outlines.pdf and
  293 + email:
  294 + https://mail.google.com/mail/u/0/#search/rfc822msgid%3A02aa01d3d013%249f766990%24de633cb0%24%40mono.hr)
  295 +
  296 +* Form fields: should be similar to outlines.
  297 +
  298 +Analytics
  299 +=========
  300 +
  301 +Consider features that make it easier to detect certain patterns in PDF files. The information below
  302 +could be computed using an external program that reads the existing json, but if it's useful enough,
  303 +we could add it directly to the json output.
  304 +
  305 +* Add to "pages" in the json:
  306 + * "inheritsresources": bool; whether there are any inherited attributes from ancestor page tree
  307 + nodes
  308 + * "sharedresources": a list of indirect objects that are
  309 + "/Resources" dictionaries or "XObject" resource dictionary subkeys of either the page itself or
  310 + of any form XObject referenced by the page.
  311 +
  312 +* Add to "objectinfo" in json: "directpagerefcount": the number of pages that directly reference
  313 + this object (i.e., you can find an indirect reference to the object in the page dictionary without
  314 + traversing over any indirect objects)
  315 +
  316 +General
  317 +=======
  318 +
  319 +NOTE: Some items in this list refer to files in my personal home directory or that are otherwise not
  320 +publicly accessible. This includes things sent to me by email that are specifically not public. Even
  321 +so, I find it useful to make reference to them in this list.
  322 +
  323 +* Consider enabling code scanning on GitHub.
  324 +
  325 +* Add an option --ignore-encryption to ignore encryption information and treat encrypted files as if
  326 + they weren't encrypted. This should make it possible to solve #598 (--show-encryption without a
  327 + password). We'll need to make sure we don't try to filter any streams in this mode. Ideally we
  328 + should be able to combine this with --json so we can look at the raw encrypted strings and streams
  329 + if we want to, though be sure to document that the resulting JSON won't be convertible back to a
  330 + valid PDF. Since providing the password may reveal additional details, --show-encryption could
  331 + potentially retry with this option if the first time doesn't work. Then, with the file open, we
  332 + can read the encryption dictionary normally. If this is done, search for "raw, encrypted" in
  333 + json.rst.
  334 +
  335 +* In libtests, separate executables that need the object library from those that strictly use public
  336 + API. Move as many of the test drivers from the qpdf directory into the latter category as long as
  337 + doing so isn't too troublesome from a coverage standpoint.
  338 +
  339 +* Consider generating a non-flat pages tree before creating output to better handle files with lots
  340 + of pages. If there are more than 256 pages, add a second layer with the second layer nodes having
  341 + no more than 256 nodes and being as evenly sizes as possible. Don't worry about the case of more
  342 + than 65,536 pages. If the top node has more than 256 children, we'll live with it. This is only
  343 + safe if all intermediate page nodes have only /Kids, /Parent, /Type, and /Count.
  344 +
  345 +* Look at https://bestpractices.coreinfrastructure.org/en
  346 +
  347 +* Consider adding fuzzer code for JSON
  348 +
  349 +* Rework tests so that nothing is written into the source directory. Ideally then the entire build
  350 + could be done with a read-only source tree.
  351 +
  352 +* Large file tests fail with linux32 before and after cmake. This was first noticed after 10.6.3. I
  353 + don't think it's worth fixing.
  354 +
  355 +* Consider updating the fuzzer with code that exercises copyAnnotations, file attachments, and name
  356 + and number trees. Check fuzzer coverage.
  357 +
  358 +* Add code for creation of a file attachment annotation. It should also be possible to create a
  359 + widget annotation and a form field. Update the pdf-attach-file.cc example with new APIs when
  360 + ready.
  361 +
  362 +* Flattening of form XObjects seems like something that would be useful in the library. We are
  363 + seeing more cases of completely valid PDF files with form XObjects that cause problems in other
  364 + software. Flattening of form XObjects could be a useful way to work around those issues or to
  365 + prepare files for additional processing, making it possible for users of the qpdf library to not
  366 + be concerned about form XObjects. This could be done recursively; i.e., we could have a method to
  367 + embed a form XObject into whatever contains it, whether that is a form XObject or a page. This
  368 + would require more significant interpretation of the content stream. We would need a test file in
  369 + which the placement of the form XObject has to be in the right place, e.g., the form XObject
  370 + partially obscures earlier code and is partially obscured by later code. Keys in the resource
  371 + dictionary may need to be changed -- create test cases with lots of duplicated/overlapping keys.
  372 +
  373 +* Part of closed_file_input_source.cc is disabled on Windows because of odd failures. It might be
  374 + worth investigating so we can fully exercise this in the test suite. That said,
  375 + ClosedFileInputSource is exercised elsewhere in qpdf's test suite, so this is not that pressing.
  376 +
  377 +* If possible, consider adding CCITT3, CCITT4, or any other easy filters. For some reference code
  378 + that we probably can't use but may be handy anyway, see
  379 + http://partners.adobe.com/public/developer/ps/sdk/index_archive.html
  380 +
  381 +* If possible, support the following types of broken files:
  382 +
  383 + - Files that have no whitespace token after "endobj" such that endobj collides with the start of
  384 + the next object
  385 +
  386 + - See ../misc/broken-files
  387 +
  388 + - See ../misc/bad-files-issue-476. This directory contains a snapshot of the google doc and linked
  389 + PDF files from issue #476. Please see the issue for details.
  390 +
  391 +* Additional form features
  392 + * set value from CLI? Specify title, and provide way to disambiguate, probably by giving objgen of
  393 + field
  394 +
  395 +* Pl_TIFFPredictor is pretty slow.
  396 +
  397 +* Support for handling file names with Unicode characters in Windows is incomplete. qpdf seems to
  398 + support them okay from a functionality standpoint, and the right thing happens if you pass in
  399 + UTF-8 encoded filenames to QPDF library routines in Windows (they are converted internally to
  400 + wchar_t*), but file names are encoded in UTF-8 on output, which doesn't produce nice error
  401 + messages or output on Windows in some cases.
  402 +
  403 +* If we ever wanted to do anything more with character encoding, see ../misc/character-encoding/,
  404 + which includes machine-readable dump of table D.2 in the ISO-32000 PDF spec. This shows the
  405 + mapping between Unicode, StandardEncoding, WinAnsiEncoding, MacRomanEncoding, and PDFDocEncoding.
  406 +
  407 +* Some test cases on bad files fail because qpdf is unable to find the root dictionary when it fails
  408 + to read the trailer. Recovery could find the root dictionary and even the info dictionary in other
  409 + ways. In particular, issue-202.pdf can be opened by evince, and there's no real reason that qpdf
  410 + couldn't be made to be able to recover that file as well.
  411 +
  412 +* Audit every place where qpdf allocates memory to see whether there are cases where malicious
  413 + inputs could cause qpdf to attempt to grab very large amounts of memory. Certainly there are cases
  414 + like this, such as if a very highly compressed, very large image stream is requested in a buffer.
  415 + Hopefully normal input to output filtering doesn't ever try to do this. QPDFWriter should be
  416 + checked carefully too. See also bugs/private/from-email-663916/
  417 +
  418 +* Interactive form modification:
  419 + https://github.com/qpdf/qpdf/issues/213 contains a good discussion of some ideas for adding
  420 + methods to modify annotations and form fields if we want to make it easier to support
  421 + modifications to interactive forms. Some of the ideas have been implemented, and some of the
  422 + probably never will be implemented, but it's worth a read if there is an intention to work on
  423 + this. In the issue, search for "Regarding write functionality", and read that comment and the
  424 + responses to it.
  425 +
  426 +* Look at ~/Q/pdf-collection/forms-from-appian/
  427 +
  428 +* When decrypting files with /R=6, hash_V5 is called more than once with the same inputs. Caching
  429 + the results or refactoring to reduce the number of identical calls could improve performance for
  430 + workloads that involve processing large numbers of small files.
  431 +
  432 +* Consider adding a method to balance the pages tree. It would call pushInheritedAttributesToPage,
  433 + construct a pages tree from scratch, and replace the /Pages key of the root dictionary with the
  434 + new tree.
  435 +
  436 +* Study what's required to support savable forms that can be saved by Adobe Reader. Does this
  437 + require actually signing the document with an Adobe private key? Search for "Digital signatures"
  438 + in the PDF spec, and look at ~/Q/pdf-collection/form-with-full-save.pdf, which came from Adobe's
  439 + example site. See also ../misc/digital-sign-from-trueroad/ and
  440 + ../misc/digital-signatures/digitally-signed-pdf-xfa.pdf. If digital signatures are implemented,
  441 + update the docs on crypto providers, which mention that this may happen in the future.
  442 +
  443 +* Qpdf does not honor /EFF when adding new file attachments. When it encrypts, it never generates
  444 + streams with explicit crypt filters. Prior to 10.2, there was an incorrect attempt to treat /EFF
  445 + as a default value for decrypting file attachment streams, but it is not supposed to mean that.
  446 + Instead, it is intended for conforming writers to obey this when adding new attachments. Qpdf is
  447 + not a conforming writer in that respect.
  448 +
  449 +* The whole xref handling code in the QPDF object allows the same object with more than one
  450 + generation to coexist, but a lot of logic assumes this isn't the case. Anything that creates
  451 + mappings only with the object number and not the generation is this way, including most of the
  452 + interaction between QPDFWriter and QPDF. If we wanted to allow the same object with more than one
  453 + generation to coexist, which I'm not sure is allowed, we could fix this by changing xref_table.
  454 + Alternatively, we could detect and disallow that case. In fact, it appears that Adobe reader and
  455 + other PDF viewing software silently ignores objects of this type, so this is probably not a big
  456 + deal.
  457 +
  458 +* From a suggestion in bug 3152169, consider having an option to re-encode inline images with an
  459 + ASCII encoding.
  460 +
  461 +* From github issue 2, provide more in-depth output for examining hint stream contents. Consider
  462 + adding on option to provide a human-readable dump of linearization hint tables. This should
  463 + include improving the 'overflow reading bit stream' message as reported in issue #2. There are
  464 + multiple calls to stopOnError in the linearization checking code. Ideally, these should not
  465 + terminate checking. It would require re-acquiring an understanding of all that code to make the
  466 + checks more robust. In particular, it's hard to look at the code and quickly determine what is a
  467 + true logic error and what could happen because of malformed user input. See also
  468 + ../misc/linearization-errors.
  469 +
  470 +* If I ever decide to make appearance stream-generation aware of fonts or font metrics, see email
  471 + from Tobias with Message-ID
  472 + <5C3C9C6C.8000102@thax.hardliners.org> dated 2019-01-14.
  473 +
  474 +* Look at places in the code where object traversal is being done and, where possible, try to avoid
  475 + it entirely or at least avoid ever traversing the same objects multiple times.
  476 +
  477 +----------------------------------------------------------------------
  478 +
  479 +### HISTORICAL NOTES
  480 +
  481 +* [Performance](#performance)
  482 +* [QPDFPagesTree](#qpdfpagestree)
  483 +* [Rejected Ideas](#rejected-ideas)
  484 +
  485 +Performance
  486 +===========
  487 +
  488 +As described in https://github.com/qpdf/qpdf/issues/401, there was great performance degradation
  489 +between qpdf 7.1.1 and 9.1.1. Doing a bisect between dac65a21fb4fa5f871e31c314280b75adde89a6c and
  490 +release-qpdf-7.1.1, I found several commits that damaged performance. I fixed some of them to
  491 +improve performance by about 70% (as measured by saying that old times were 170% of new times). The
  492 +remaining commits that broke performance either can't be correct because they would re-introduce an
  493 +old bug or aren't worth correcting because of the high value they offer relative to a relatively low
  494 +penalty. For historical reference, here are the commits. The numbers are the time in seconds on the
  495 +machine I happened to be using of splitting the first 100 pages of PDF32000_2008.pdf 20 times and
  496 +taking an average duration.
  497 +
  498 +Commits that broke performance:
  499 +
  500 +* d0e99f195a987c483bbb6c5449cf39bee34e08a1 -- object description and context: 0.39 -> 0.45
  501 +* a01359189b32c60c2d55b039f7aefd6c3ce0ebde (minus 313ba08) -- fix dangling references: 0.55 -> 0.6
  502 +* e5f504b6c5dc34337cc0b316b4a7b1fca7e614b1 -- sparse array: 0.6 -> 0.62
  503 +
  504 +Other intermediate steps that were previously fixed:
  505 +
  506 +* 313ba081265f69ac9a0324f9fe87087c72918191 -- copy outlines into split: 0.55 -> 4.0
  507 +* a01359189b32c60c2d55b039f7aefd6c3ce0ebde -- fix dangling references:
  508 + 4.0 -> 9.0
  509 +
  510 +This commit fixed the awful problem introduced in 313ba081:
  511 +
  512 +* a5a016cdd26a8e5c99e5f019bc30d1bdf6c050a2 -- revert outline preservation: 9.0 -> 0.6
  513 +
  514 +Note that the fix dangling references commit had a much worse impact prior to removing the outline
  515 +preservation, so I also measured its impact in isolation.
  516 +
  517 +A few important lessons (in README-maintainer)
  518 +
  519 +* Indirection through PointerHolder<Members> is expensive, and should not be used for things that
  520 + are created and destroyed frequently such as QPDFObjectHandle and QPDFObject.
  521 +* Traversal of objects is expensive and should be avoided where possible.
  522 +
  523 +Also, it turns out that PointerHolder is more performant than std::shared_ptr. (This was true at the
  524 +time but subsequent implementations of std::shared_ptr became much more efficient.)
  525 +
  526 +QPDFPagesTree
  527 +=============
  528 +
  529 +On a few occasions, I have considered implementing a QPDFPagesTree object that would allow the
  530 +document's original page tree structure to be preserved. See comments at the top QPDF_pages.cc for
  531 +why this was abandoned.
  532 +
  533 +Partial work is in refs/attic/QPDFPagesTree. QPDFPageTree is mostly implemented and mostly tested.
  534 +There are not enough cases of different kinds of operations (pclm, linearize, json, etc.) with
  535 +non-flat pages trees. Insertion is not implemented. Insertion is potentially complex because of the
  536 +issue of inherited objects. We will have to call pushInheritedAttributesToPage before adding any
  537 +pages to the pages tree. The test suite is failing on that branch.
  538 +
  539 +Some parts of page tree repair are silent (no warnings). All page tree repair should warn. The
  540 +reason is that page tree repair will change object numbers, and knowing that is important when
  541 +working with JSON output.
  542 +
  543 +If we were to do this, we would still need keep a pages cache for efficient insertion. There's no
  544 +reason we can't keep a vector of page objects up to date and just do a traversal the first time we
  545 +do getAllPages just like we do now. The difference is that we would not flatten the pages tree. It
  546 +would be useful to go through QPDF_pages and reimplement everything without calling
  547 +flattenPagesTree. Then we can remove flattenPagesTree, which is private. That said, with the
  548 +addition of creating non-flat pages trees, there is really no reason not to flatten the pages tree
  549 +for internal use.
  550 +
  551 +In its current state, QPDFPagesTree does not proactively fix /Type or correct page objects that are
  552 +used multiple times. You have to traverse the pages tree to trigger this operation. It would be nice
  553 +if we would do that somewhere but not do it more often than necessary so isPagesObject and
  554 +isPageObject are reliable and can be made more reliable. Maybe add a validate or repair function? It
  555 +should also make sure /Count and /Parent are correct.
  556 +
  557 +Rejected Ideas
  558 +==============
  559 +
  560 +* Investigate whether there is a way to automate the memory checker tests for Windows.
  561 +
  562 +* Provide support in QPDFWriter for writing incremental updates. Provide support in qpdf for
  563 + preserving incremental updates. The goal should be that QDF mode should be fully functional for
  564 + files with incremental updates including fix_qdf.
  565 +
  566 + Note that there's nothing that says an indirect object in one update can't refer to an object that
  567 + doesn't appear until a later update. This means that QPDF has to treat indirect null objects
  568 + differently from how it does now. QPDF drops indirect null objects that appear as members of
  569 + arrays or dictionaries. For arrays, it's handled in QPDFWriter where we make indirect nulls
  570 + direct. This is in a single if block, and nothing else in the code cares about it. We could just
  571 + remove that if block and not break anything except a few test cases that exercise the current
  572 + behavior. For dictionaries, it's more complicated. In this case, QPDF_Dictionary::getKeys()
  573 + ignores all keys with null values, and hasKey() returns false for keys that have null values. We
  574 + would probably want to make QPDF_Dictionary able to handle the special case of keys that are
  575 + indirect nulls and basically never have it drop any keys that are indirect objects.
  576 +
  577 + If we make a change to have qpdf preserve indirect references to null objects, we have to note
  578 + this in ChangeLog and in the release notes since this will change output files. We did this before
  579 + when we stopped flattening scalar references, so this is probably not a big deal. We also have to
  580 + make sure that the testing for this handles non-trivial cases of the targets of indirect nulls
  581 + being replaced by real objects in an update. I'm not sure how this plays with linearization, if at
  582 + all. For cases where incremental updates are not being preserved as incremental updates and where
  583 + the data is being folded in (as is always the case with qpdf now), none of this should make any
  584 + difference in the actual semantics of the files.
  585 +
  586 +* The second xref stream for linearized files has to be padded only because we need file_size as
  587 + computed in pass 1 to be accurate. If we were not allowing writing to a pipe, we could seek back
  588 + to the beginning and fill in the value of /L in the linearization dictionary as an optimization to
  589 + alleviate the need for this padding. Doing so would require us to pad the /L value individually
  590 + and also to save the file descriptor and determine whether it's seekable. This is probably not
  591 + worth bothering with.
  592 +
  593 +* Based on an idea suggested by user "Atom Smasher", consider providing some mechanism to recover
  594 + earlier versions of a file embedded prior to appended sections.
  595 +
  596 +* Consider creating a sanitizer to make it easier for people to send broken files. Now that we have
  597 + json mode, this is probably no longer worth doing. Here is the previous idea, possibly implemented
  598 + by making it possible to run the lexer (tokenizer) over a whole file. Make it possible to replace
  599 + all strings in a file lexically even on badly broken files. Ideally this should work files that
  600 + are lacking xref, have broken links, duplicated dictionary keys, syntax errors, etc., and ideally
  601 + it should work with encrypted files if possible. This should go through the streams and strings
  602 + and replace them with fixed or random characters, preferably, but not necessarily, in a manner
  603 + that works with fonts. One possibility would be to detect whether a string contains characters
  604 + with normal encoding, and if so, use 0x41. If the string uses character maps, use 0x01. The output
  605 + should otherwise be unrelated to the input. This could be built after the filtering and tokenizer
  606 + rewrite and should be done in a manner that takes advantage of the other lexical features. This
  607 + sanitizer should also clear metadata and replace images. If I ever do this, the file from issue
  608 + #494 would be a great one to look at.
  609 +
  610 +* Here are some notes about having stream data providers modify stream dictionaries. I had wanted to
  611 + add this functionality to make it more efficient to create stream data providers that may
  612 + dynamically decide what kind of filters to use and that may end up modifying the dictionary
  613 + conditionally depending on the original stream data. Ultimately I decided not to implement this
  614 + feature. This paragraph describes why.
  615 +
  616 + * When writing, the way objects are placed into the queue for writing strongly precludes creation
  617 + of any new indirect objects, or even changing which indirect objects are referenced from which
  618 + other objects, because we sometimes write as we are traversing and enqueuing objects. For
  619 + non-linearized files, there is a risk that an indirect object that used to be referenced would
  620 + no longer be referenced, and whether it was already written to the output file would be based on
  621 + an accident of where it was encountered when traversing the object structure. For linearized
  622 + files, the situation is considerably worse. We decide which section of the file to write an
  623 + object to based on a mapping of which objects are used by which other objects. Changing this
  624 + mapping could cause an object to appear in the wrong section, to be written even though it is
  625 + unreferenced, or to be entirely omitted since, during linearization, we don't enqueue new
  626 + objects as we traverse for writing.
  627 +
  628 + * There are several places in QPDFWriter that query a stream's dictionary in order to prepare for
  629 + writing or to make decisions about certain aspects of the writing process. If the stream data
  630 + provider has the chance to modify the dictionary, every piece of code that gets stream data
  631 + would have to be aware of this. This would potentially include end user code. For example, any
  632 + code that called getDict() on a stream before installing a stream data provider and expected
  633 + that dictionary to be valid would potentially be broken. As implemented right now, you must
  634 + perform any modifications on the dictionary in advance and provided /Filter and /DecodeParms at
  635 + the time you installed the stream data provider. This means that some computations would have to
  636 + be done more than once, but for linearized files, stream data providers are already called more
  637 + than once. If the work done by a stream data provider is especially expensive, it can implement
  638 + its own cache.
  639 +
  640 + The example examples/pdf-custom-filter.cc demonstrates the use of custom stream filters. This
  641 + includes a custom pipeline, a custom stream filter, as well as modification of a stream's
  642 + dictionary to include creation of a new stream that is referenced from /DecodeParms.
  643 +
  644 +* Removal of raw QPDF* from the API. Discussions in #747 and #754. This is a summary of the
  645 + arguments I put forth in #754. The idea was to make QPDF::QPDF() private and require all QPDF
  646 + objects to be shared pointers created with QPDF::create(). This would enable us to have
  647 + QPDFObjectHandle::getOwningQPDF() return a std::weak_ptr<QPDF>. Prior to #726 (
  648 + QPDFObject/QPDFValue split, released in qpdf 11.0.0), getOwningQPDF() could return an invalid
  649 + pointer if the owning QPDF disappeared, but this is no longer the case, which removes the main
  650 + motivation. QPDF 11 added QPDF::create() anyway though.
  651 +
  652 + Removing raw QPDF* would look something like this. Note that you can't use std::make_shared<T>
  653 + unless T has a public constructor.
  654 +
  655 + QPDF_POINTER_TRANSITION = 0 -- no warnings around calling the QPDF constructor
  656 + QPDF_POINTER_TRANSITION = 1 -- calls to QPDF() are deprecated, but QPDF is still available so code
  657 + can be backward compatible and use std::make_shared<QPDF>
  658 + QPDF_POINTER_TRANSITION = 2 -- the QPDF constructor is private; all calls to
  659 + std::make_shared<QPDF> have to be replaced with QPDF::create
  660 +
  661 + If we were to do this, we'd have to look at each use of QPDF* in the interface and decide whether
  662 + to use a std::shared_ptr or a std::weak_ptr. The answer would almost always be to use a std::
  663 + weak_ptr, which means we'd have to take the extra step of calling lock(), and it means there would
  664 + be lots of code changes cause people would have to pass weak pointers instead of raw pointers
  665 + around, and those have to be constructed and locked. Passing std::shared_ptr around leaves the
  666 + possibility of creating circular references. It seems to be too much trouble in the library and
  667 + too much toil for library users to be worth the small benefit of not having to call resetObjGen in
  668 + QPDF's destructor.
  669 +
  670 +* Fix Multiple Direct Object Parent Issue
  671 +
  672 + This idea was rejected because it would be complicated to implement and would likely have a high
  673 + performance cost to fix what is not really that big of a problem in practice.
  674 +
  675 + It is possible for a QPDFObjectHandle for a direct object to be contained inside of multiple
  676 + QPDFObjectHandle objects or even replicated across multiple QPDF objects. This creates a
  677 + potentially confusing and unintentional aliasing of direct objects. There are known cases in the
  678 + qpdf library where this happens including page splitting and merging (particularly with page
  679 + labels, and possibly with other cases), and also with unsafeShallowCopy. Disallowing this would
  680 + incur a significant performance penalty and is probably not worth doing. If we were to do it, here
  681 + are some ideas.
  682 +
  683 + * Add std::weak_ptr<QPDFObject> parent to QPDFObject. When adding a direct object to an array or
  684 + dictionary, set its parent. When removing it, clear the parent pointer. The parent pointer would
  685 + always be null for indirect objects, so the parent pointer, which would reside in QPDFObject,
  686 + would have to be managed by QPDFObjectHandle. This is because QPDFObject can't tell the
  687 + difference between a resolved indirect object and a direct object.
  688 +
  689 + * Phase 1: When a direct object that already has a parent is added to a dictionary or array, issue
  690 + a warning. There would need to be unsafe add methods used by unsafeShallowCopy. These would add
  691 + but not modify the parent pointer.
  692 +
  693 + * Phase 2: In the next major release, make the multiple parent case an error. Require people to
  694 + create a copy. The unsafe operations would still have to be permitted.
  695 +
  696 + This approach would allow an object to be moved from one object to another by removing it, which
  697 + returns the now orphaned object, and then inserting it somewhere else. It also doesn't break the
  698 + pattern of adding a direct object to something and subsequently mutating it. It just prevents the
  699 + same object from being added to more than one thing.
... ...
appimage/build-appimage
... ... @@ -137,7 +137,7 @@ for i in appdir/usr/share/doc/qpdf; do
137 137 cp $top/LICENSE.txt $i
138 138 cp $top/Artistic-2.0 $i/Artistic-LICENSE.txt
139 139 cp $top/ChangeLog $i/README-ChangeLog
140   - cp $top/TODO $i/README-todo
  140 + cp $top/TODO.md $i/README-todo.md
141 141 done
142 142  
143 143 # The following lines are experimental (for debugging; and to test
... ...