New upstream version 3.020
gregor herrmann
3 years ago
0 | 0 | See also INFO/Changes-ver_2 for changes released for PDF::API2, and |
1 | 1 | incorporated into PDF::Builder. |
2 | 2 | See also INFO/Changes_2019 for earlier version 3 release logs. |
3 | ||
4 | 3.020 2020-11-29 | |
5 | ||
6 | lib/PDF/Builder/Content-Outline-NamedDestination.pm, INFO/DEPRECATED | |
7 | Rename previously deprecated names "spline" (to "qbspline") and "pdfile" | |
8 | (to "pdf_file"). | |
9 | ||
10 | examples/042_links | |
11 | Add some examples showing non-default positioning (fit) on | |
12 | target PDF's page. | |
13 | ||
14 | lib/PDF/Builder/Basic/PDF/File.pm | |
15 | Extend cross-reference stream handling (read-in PDFs) to cover field | |
16 | widths of 5, 6, or 7 bytes (40-, 48-, or 56-bit integers). Special case | |
17 | of 8 byte (64-bit integer) where top 32 bits are all 0, is treated as | |
18 | 32-bit integer to avoid requiring a 64-bit Perl build just for unpacking | |
19 | this field. | |
20 | ||
21 | lib/PDF/Builder.pm, lib/PDF/Builder/Docs.pm | |
22 | Per [RT 130722, RT 133131, et al.] get any /Version override (in /Catalog) | |
23 | and update the read-in PDF version (beyond 1.4, even if the PDF header | |
24 | still claims 1.4). This minimizes extraneous warnings about having to | |
25 | bump up the PDF version from what is claimed in the header. | |
26 | ||
27 | Cross-check parent/kids links, report orphaned objects, report objects | |
28 | referenced but not defined, report missing Info and Root objects, etc. | |
29 | The input PDF file or string is not changed, but at least it can give | |
30 | you an idea of why an error occurs, or the resulting PDF does not work | |
31 | correctly. | |
32 | ||
33 | lib/PDF/Builder/Basic/PDF/File.pm, INFO/RoadMap | |
34 | Per [RT 133131, Vadim Repin's fix], change Quad int unpack code from Q | |
35 | to Q> to indicate "Big-Endian" byte order (MSB at left) for incoming data | |
36 | field in a cross-reference stream. Apparently, with just Q, Perl will | |
37 | assume the doubleword int is native format (for instance, Little-Endian | |
38 | on an Intel chip). | |
39 | Note: This is a PDF 1.5+ feature (cross-reference streams) and has | |
40 | no business being in a PDF 1.4 file (the original bug report). | |
41 | Also add warning if imported file declared to be PDF 1.4 (or lower), as | |
42 | cross-reference streams are a PDF 1.5 feature. Earlier fix had added | |
43 | cross-reference stream support, but it really doesn't belong in PDF 1.4. | |
44 | ||
45 | lib/PDF/Builder/Outline.pm, lib/PDF/Builder/Basic/PDF/Objind.pm | |
46 | Per [RT 131657, Vadim Repin's fix] add weaken statements to eliminate | |
47 | "Deep Recursion" error messages. | |
48 | ||
49 | lib/PDF/Builder/Matrix.pm | |
50 | Per latest changes to PDF::API2, some code cleanup in Matrix.pm (no | |
51 | functional change except for adding some diagnostic carps). | |
52 | ||
53 | lib/PDF/Builder.pm | |
54 | Per [PR 139, by ppisar], replace some hard-coded checks for | |
55 | Graphics::TIFF and Image::PNG::Libpng with calls to LA_GT() and LA_IPL(). | |
56 | ||
57 | docs/buildDoc.pl, lib/PDF/Builder/Basic/PDF.pm, MANIFEST | |
58 | Add NAVIGATION LINKS section to go Up to parent(s), sideways to | |
59 | Siblings, and Down to children. This enables you to get from any place | |
60 | in the docs to any place else without (usually) having to go all the way | |
61 | back up to index.html. PDF.pm added just for a place to put links (no | |
62 | code). | |
63 | ||
64 | lib/PDF/Builder.pm, INFO/RoadMap minor updates | |
65 | ||
66 | lib/PDF/Builder.pm, README, INFO/LICENSE | |
67 | Make license-related text more consistent, and attempt to clarify the | |
68 | relationship between the LGPL 2.1 master license and the various other | |
69 | licenses that some files are under, as well as the ability to | |
70 | redistribute under a higher version of LGPL. ref [CTS 35] | |
71 | ||
72 | Makefile.PL, t/author-critic.t, tools/1_pc.pl | |
73 | Update to fix some perlcritic warnings, enable author-critic (not useful, | |
74 | as tools/1_pc.pl does a better job of it). Suggested by 'carygravel', | |
75 | but [PR 135] rejected, as 1_pc is a superset of author-critic function. | |
76 | Can't change .perlcriticrc SEVERITY from 5 to 4, as the GitHub Actions | |
77 | CI testing appears to be using it, leading to a lot of errors. | |
78 | ||
79 | lib/PDF/Builder/Resource/XObject/Image/TIFF/File_GT.pm, t/tiff.t, | |
80 | lib/PDF/Builder/Resource/XObject/Image/TIFF_GT.pm, Makefile.PL, | |
81 | lib/PDF/Builder.pm, lib/PDF/Builder/Resource/XObject/Image/TIFF.pm, | |
82 | Fix problem with reading colormap from TIFF [GH 133], by 'carygravel'. | |
83 | Note that this requires upgrade of Graphics::TIFF from 6 to 7. | |
84 | ||
85 | t/tiff.t upgrade so tests 10 and 11 are no longer always skipped. This | |
86 | works only on a Linux system (definitely not on Windows), and makes use | |
87 | of ImageMagick and Linux utilities 'convert' and 'tiffcp'. Graphics::TIFF | |
88 | is needed for test 10, and test 9 (alpha layer) is still skipped. Thanks | |
89 | to GitHub user 'carygravel' for the upgrade. | |
90 | ||
91 | INFO/DEPRECATED add a couple long-deprecated items ("PDFStr" and | |
92 | "PDFUtf") that were missing from this file. | |
93 | ||
94 | INFO/RoadMap, lib/PDF/Builder/Docs.pm add discussion on why images | |
95 | (especially JPEG) are sometimes rotated and/or flipped, and what can be | |
96 | done about it. | |
97 | ||
98 | t/fonts-synfont.t, t/fonts-ttf.t fix a couple paths to DejaVu fonts on | |
99 | certain Linux boxes. From 'gregoa' at Debian Perl Group. | |
100 | ||
101 | lib/PDF/Builder/Resource/XObject/Image/PNM.pm | |
102 | Bi-level (1 bit per pixel) PNM/PBM image was not being handled | |
103 | correctly if the row length (width) was not a multiple of 8. Not enough | |
104 | data was copied over to the PDF. Continuation of [RT 132844]. | |
105 | Almost complete rewrite of PNM.pm to properly support all combinations | |
106 | of comments in the header, and support "plain" (ASCII) image files | |
107 | (P1..P3) and 16 bit samples for grayscale and RGB. Not yet supporting | |
108 | multiple images per file, as I don't have any firm definition! | |
109 | ||
110 | lib/PDF/Builder/Resource/XObject/Image/PNG_IPL.pm | |
111 | Make use of the new 'split_alpha' call in new release 0.47 of | |
112 | Image::PNG::Libpng (this version specified as minimum, for optional | |
113 | library installation). This brings the speed for RGB and Gray scale | |
114 | with Alpha channels up to reasonable levels, as the 'vec' call is no | |
115 | longer called (4 or 8 times per pixel). Note that in contrast to | |
116 | PDF::API2, both 8 and 16bps samples are supported, and Gray scale is | |
117 | supported, at high speed; not just 8bps RGBA. | |
118 | ||
119 | lib/PDF/Builder/Docs.pm, Makefile.PL, README, t/00-all-usable.t | |
120 | PDF::Builder will (again) attempt to install or upgrade OPTIONAL | |
121 | libraries (see README). Includes a warning in Makefile.PL not to be | |
122 | unduly alarmed if an optional install should fail. The user is given | |
123 | a choice of selecting none/all/specific optional libraries when | |
124 | running Makefile.PL (default "all"). | |
125 | ||
126 | README add instructions for installing on pre-5.020 Perls, which may be | |
127 | adaptable to other OS's and Perls. Note that there's no guarantee that | |
128 | PDF::Builder will actually RUN on older Perls! (But you're welcome to | |
129 | try.) | |
130 | ||
131 | lib/PDF/Builder/Docs-Content.pm, lib/PDF/Builder/Content/Text.pm | |
132 | Fix spelling errors/typos, per report by 'carygravel'. | |
133 | ||
134 | lib/PDF/Builder/Resource/PaperSizes.pm | |
135 | Fix a couple missized entries (B7 and jr-Legal), added a bunch more | |
136 | sizes (mostly archaic American and British sizes): dbill, statement | |
137 | (= student), half-letter (= student), old-paper, b-plus | |
138 | (B+/Super-B/A3+/super-A3), arch-a, arch-b, arch-c, arch-d, arch-e, | |
139 | arch-e1, pott, post, large-post, crown, large-crown, demy, small-demy, | |
140 | medium, royal, small-royal, super-royal, imperial, a7, a8, a9, a10. | |
141 | ||
142 | Note that some "standard" sizes, such as broadsheet and foolscap, seem | |
143 | to have a different size specification depending on where you look. You | |
144 | should check that the size used matches your actual paper supply, and to | |
145 | either update PaperSizes.pm to match, or specify the MediaBox in points | |
146 | instead of by name. This is probably a good idea with any unusual, | |
147 | large, archaic, or special-order paper... you want to check what its | |
148 | actual size is before going through a lot of expensive failed printing! | |
149 | ||
150 | examples/HarfBuzz.pl, examples/resources/HarfBuzz_example.pdf | |
151 | In the updated 3.019 example, note that the entire AdobeMingStd-Light.otf | |
152 | font (ttfont call) appears to be embedded, rather than just the subset. | |
153 | This resulted in 3.019 being about 8MB larger than 3.018's version! It may | |
154 | be related to bug [RT 130041]. Use caution when embedding CJK fonts, even | |
155 | when not using the cjkfont call, as at least some apparently don't subset. | |
156 | I replaced the font with AdobeGothicStd-Light.otf, which appears to embed | |
157 | just a subset of the font, greatly reducing the size of the PDF file. | |
3 | 158 | |
4 | 159 | 3.019 2020-07-27 |
5 | 160 | |
26 | 181 | |
27 | 182 | examples/042_links, examples/README, examples/examples_output, |
28 | 183 | tools/3_examples.pl, lib/PDF/Builder/Annotation.pm, MANIFEST |
29 | Improved documentation of annotation-based links: PDF links and targets. | |
184 | Per [CTS 29], improved documentation of annotation-based links: PDF links | |
185 | and targets. | |
30 | 186 | |
31 | 187 | lib/PDF/Builder/Content.pm, lib/PDF/Builder/Resource/XObject/Image/PNM.pm |
32 | 188 | image() and form_image() calls default x,y to 0,0, so can call without |
7 | 7 | user in an emergency), but eventually they WILL be removed. |
8 | 8 | |
9 | 9 | In order of scheduled removal date: |
10 | ||
11 | spline method in Content.pm | |
12 | This functionality was renamed to qbspline() [quadratic Bezier spline]. | |
13 | Simply rename any call to "spline" to "qbspline". spline is scheduled to | |
14 | be removed on or after June, 2020. | |
15 | ||
16 | pdfile method in Outline.pm, NamedDestination.pm | |
17 | This functionality was renamed to pdf_file(). Simply rename any call to | |
18 | "pdfile" to "pdf_file". pdfile is scheduled to be removed on or after | |
19 | October, 2020. | |
20 | 10 | |
21 | 11 | -slant option in Synfont.pm |
22 | 12 | This option was renamed to -condense, as it is the factor to condense |
37 | 27 | global and current page bounding boxes (media, crop, bleed, trim, art). |
38 | 28 | The get routines are now obsolete, and may be removed on or after |
39 | 29 | August, 2021. |
30 | ||
31 | PDFStr() method in Basic/PDF/Utils.pm | |
32 | Use PDFString() instead. May be removed on or after October, 2022. | |
33 | ||
34 | PDFUtf() method in Basic/PDF/Utils.pm | |
35 | Use PDFString() instead. May be removed on or after October, 2022. | |
40 | 36 | |
41 | 37 | *** If I have missed any deprecated interfaces, please let me know! *** |
42 | 38 | |
133 | 129 | "pdfile" to "pdf_file". pdfile is scheduled to be removed on or after |
134 | 130 | November, 2019. [Removed November, 2019] |
135 | 131 | |
132 | spline method in Content.pm | |
133 | This functionality was renamed to qbspline() [quadratic Bezier spline]. | |
134 | Simply rename any call to "spline" to "qbspline". spline is scheduled to | |
135 | be removed on or after June, 2020. [Removed November, 2020] | |
136 | ||
137 | pdfile method in Outline.pm, NamedDestination.pm | |
138 | This functionality was renamed to pdf_file(). Simply rename any call to | |
139 | "pdfile" to "pdf_file". pdfile is scheduled to be removed on or after | |
140 | October, 2020. [Removed November, 2020] | |
141 |
0 | This software is Copyright (c) 2017 by Phil M. Perry. | |
0 | This software is Copyright (c) 2017-2020 by Phil M. Perry. | |
1 | Previous copyrights are held by Steve Simms, Alfred Reibenschuh, et al. | |
2 | ||
3 | Note that some files within this software are under DIFFERENT licenses (than | |
4 | LGPL 2.1). These include (but are not necessarily limited to) | |
5 | PDF::Builder::Matrix (same license as Perl itself), and | |
6 | PDF::Builder::Basic::PDF files (except for the Filters and Literal.pm), which | |
7 | are under the Perl Artistic License. If not otherwise stated in such a file, | |
8 | the overall license is LGPL 2.1, and you may redistribute and/or modify this | |
9 | software under the terms of the indicated license. For LGPL, you are permitted | |
10 | to redistribute and/or modify the software under a version higher than 2.1, | |
11 | at your option). | |
12 | ||
13 | This library is distributed in the hope that it will be useful, but WITHOUT ANY | |
14 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A | |
15 | PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. | |
1 | 16 | |
2 | 17 | This is free software, licensed under: |
3 | 18 | |
4 | 19 | The GNU Lesser General Public License, Version 2.1, February 1999 |
20 | ||
21 | ============================================================================= | |
22 | == Original LGPL 2.1 license text == | |
23 | ============================================================================= | |
5 | 24 | |
6 | 25 | The GNU Lesser General Public License (LGPL) |
7 | 26 | Version 2.1, February 1999 |
0 | Road Map for Future Development of PDF::Builder 11 September 2019 | |
0 | Road Map for Future Development of PDF::Builder 22 November 2020 | |
1 | 1 | |
2 | 2 | In order to encourage others to contribute code and/or algorithms to the |
3 | 3 | effort, I am publishing this road map of where I would like the product to go. |
75 | 75 | |
76 | 76 | E. JPEG2000 image file support (CTS 12): I don't know if this is worth it, as |
77 | 77 | there seems to be very little use of this, but if someone is interested, |
78 | have at it... | |
78 | have at it... any other newish image formats that PDF can support? | |
79 | 79 | |
80 | 80 | F. Fix Bar Code generation (CTS 1): there seems to be something quite wrong |
81 | 81 | with the current bar code generation, so it's possible that no one is using |
129 | 129 | traditional way, e.g., to the next n8-th column) is useful only for |
130 | 130 | monospaced fonts, and no changes in font size in the line. Thus, tab stops |
131 | 131 | would be more useful when defined by some absolute dimension (e.g., inches |
132 | or ems) of column position. Second, tabbing is usually done to get text | |
132 | or mms) of column position. Second, tabbing is usually done to get text | |
133 | 133 | columns (sub columns), which involves a lot of manual setup and twiddling of |
134 | 134 | text. Consider using a TABLE within the column or page to get text organized |
135 | 135 | into the desired format (see "tbl" addition in section II). |
141 | 141 | |
142 | 142 | L. Determine what it is about "CJK" fonts (.ttf and .otf) that makes them |
143 | 143 | incompatible with synfont [RT 130040] and embedding [RT 130041], and fix if |
144 | possible. Are separate CJK fonts even necessary these days? | |
144 | possible. Are separate CJK fonts even necessary these days? Also note that | |
145 | many CJK fonts refuse to "subset" when embedded (the entire font gets | |
146 | embedded, even if you only use a handful of glyphs!). | |
145 | 147 | |
146 | 148 | M. Add decorative rectangular box effects around sections of text. With or |
147 | 149 | without border (allow rounded corners) and background color, drop shadows |
148 | 150 | (3D effect), etc. The box is drawn at given dimensions and location, and |
149 | 151 | the text written over it in the usual manner. Content clipping might also |
150 | be supported. | |
151 | ||
152 | I. Extend HarfBuzz::Shaper use (see A.) to flow paragraphs and sections (fill) | |
152 | be supported. See PDF::Table for drawing rules (and borders) for ideas, as | |
153 | well as block background colors (gfx_bg object before text object). | |
154 | ||
155 | N. Extend HarfBuzz::Shaper use (see A.) to flow paragraphs and sections (fill) | |
153 | 156 | to match capability of existing text-fill calls. Architect so as to extend |
154 | 157 | easily to full paragraph shaping and "pouring" text into arbitrary columns, |
155 | 158 | with balancing. Justification to avoid ragged-left or -right needs to be |
156 | 159 | handled carefully for connected glyphs (e.g., Arabic, Indic, cursive Latin). |
160 | ||
161 | Treat HarfBuzz::Shaper handled-fonts just like any other font when it comes | |
162 | to various text-handling routines (including length, justifying and aligning | |
163 | text, filling lines, paragraph, section, textlabel, etc.). | |
164 | ||
165 | O. ## DONE ## release 3.020 | |
166 | Support for PNM and related graphics images. See RT 132844. | |
167 | ||
168 | P. Look into using TeX::Hyphen or Text::Hyphen to split words properly. The | |
169 | latter supports 3 languages besides English. Both use TeX (Knuth-Liang) | |
170 | algorithms. Eventually need means to override built-in hyphenation (e.g., | |
171 | force 're-cord' or 'rec-ord', per context), similar to ligature control. | |
172 | One way would be to insert soft-hypen ­, this might require all | |
173 | syllable breaks to be so marked, since TH might not see it any more. | |
174 | Use any place where a string is flowed into multiple lines, and eventually | |
175 | for complete paragraph shaping. Retain camelCase and punctuation/numeric | |
176 | splitting as fallback for non-words. Consider own built-in version of either | |
177 | TH, using CTAN directly (would take rewrite of the Perl code?), if can't | |
178 | get owner to upgrade it. | |
179 | ||
180 | Q. ## DONE ## release 3.020 | |
181 | Look at Basic/PDF/File.pm 'Q>' unpack (ref RT 133131). Supposedly will not | |
182 | run on a 32 bit platform -- will it work on such platform to check if high | |
183 | 33 bits are all 0, and convert the low 32 bits to a fullword integer? | |
157 | 184 | |
158 | 185 | ============================================================================= |
159 | 186 | II. Items to add to a separate area (new module or sub-module) |
168 | 195 | paragraph shaping algorithms to flow text into a space in a visually |
169 | 196 | pleasing manner, while obeying widows and orphans constraints (as well as |
170 | 197 | not orphaning headings). Pango may help here with line and word splitting. |
198 | ** Look into item P in section I ** | |
199 | Text::KnuthPlass might be usable for general paragraph setup (allows non- | |
200 | rectangular paragaph), uses Text::Hyphen internally, don't know yet if it | |
201 | does the full Knuth-Plass "rivers of white" and "too many hyphens in a row" | |
202 | bit. Interaction with Text::Layout stuff (font, size, changes)? | |
203 | ||
204 | I've gotten Text::Hyphen and Text::KnuthPlass working, and tests suggest | |
205 | that they (mostly) work great. Unfortunately, KP doesn't build on all | |
206 | systems (a simple fix), so I may have to forcibly take it over if the owner | |
207 | doesn't respond reasonably soon, or just take it into the product. Hyphen | |
208 | could use some improvements for multilanguage support, also waiting for a | |
209 | response. Some sort of "virtual printing (see 'B') would be necessary, as | |
210 | dealing with widows and orphans would be much easier that way. | |
171 | 211 | |
172 | 212 | B. Virtual pages: this would be related to item (A) (paragraph shaping), where |
173 | 213 | PDF code would not be immediately written to an output page, but would be |
179 | 219 | virtuality (virtual line output) could be useful for resetting a baseline |
180 | 220 | to accommodate a change in font size -- this might involve tagging a word or |
181 | 221 | block of words of the same height. |
222 | ||
223 | PDF::Table product (or table functionality within Builder) could make use | |
224 | of virtual printing to "print" to a mini-page within a cell (fixed width, | |
225 | min/max height). If it doesn't fit on the page, decide where to split row | |
226 | to avoid widows and orphans. Also knowing cell height and row height, can | |
227 | vertically align a cell's contents. | |
182 | 228 | |
183 | 229 | C. General text flowing capability, to fill irregularly shaped columns (such |
184 | 230 | as with intruding inserts or margin notes) in a balanced manner, including |
234 | 280 | |
235 | 281 | Page background color or pattern should extend to the full size of the page |
236 | 282 | and not end when content ends part way down the page. Remind users that |
237 | most printers will not print all the way to the edge. | |
283 | most printers will not print all the way to the edge. See Boxes.pl example. | |
284 | ||
285 | H. Incorporate PDF::Table into PDF::Builder::Table. Simplify it somewhat (e.g., | |
286 | instead of separate line-width and color settings, use a list: w (width in | |
287 | points with default color and solid line), or [w, color, optional-dash- | |
288 | pattern]. Use it for borders and rules, and possibly frames. A "frame" would | |
289 | be the enclosure for the table, and would be either a line spec or a width | |
290 | and pattern (3D raised, 3D sunken, sunken table, raised table, floating | |
291 | table with shadow, etc.). A "rule" would be horizontal and vertical | |
292 | divider lines, and a "border" would be cell dividers ([w, color, margin-to- | |
293 | cell edge, optional-dash-pattern]). Other simplifications and consolidations | |
294 | of settings as justified (do not have to maintain absolute compatibility | |
295 | with existing PDF::Table). Tables continued to the next page would not get | |
296 | a full frame at the bottom/top, but a heavy dashed line (if breaking in | |
297 | middle of a cell) or heavy solid line (if breaking at a row boundary). It | |
298 | might be good not to automatically create a next page and start outputting | |
299 | the rest of the table, but hold the contents and alert the programmer that | |
300 | at least one more call is needed to finish. | |
301 | ||
302 | Currently, PDF::Table basically equalizes column widths as much as it can, | |
303 | but consider a starting point of relative and/or absolute column widths, | |
304 | like many other table implementations. Some columns absolute widths, and | |
305 | remaining space divided up even among *'s (with a column N*), subject to | |
306 | minimum and maximum widths. | |
307 | ||
308 | Within a cell, ideally it would be treated as a mini-page, with all the | |
309 | normal PDF construction capabilities including paragraph shaping, flow into | |
310 | column(s), images, etc. This would be better than the "text_block" used in | |
311 | PDF::Table (more uniform coding and treatment), although some ideas from | |
312 | text_block might find a home. However, such complex treatment (a table | |
313 | could embed a table) requires virtual pages to permit a lot of rearrangement. | |
314 | ||
315 | I. Consider Optional Content Groups (Layers), per 32000-2008 section 8.11. | |
316 | This permits drawings to be shown by layer, or a watermark/copyright layer | |
317 | to show only on printing. | |
318 | ||
319 | J. ## DONE ## release 3.020 (documentation improved) | |
320 | Per wkHTMLtoPDF issue 4846, at least some phone cameras output "portrait" | |
321 | mode photos in landscape mode (rotated), with an "orientation" tag. JPEG | |
322 | (at least) image handling may need a rotation flag in the call, and/or | |
323 | pay attention to the Exif orientation flag. Confirmed that | |
324 | Builder JPEG support does NOT respect the orientation flag. | |
325 | https://www.google.com/search?client=firefox-b-1-d&q=jpeg+orientation+metadata | |
326 | Unfortunately, there are a number of ways to specify the orientation flag | |
327 | including XML '<exif:Orientation>Top-left</exif::Orientation>' and buried | |
328 | somewhere in the Exif or JFIF header of the file. It might be best to ask | |
329 | the image what its orientation is, and leave translation and rotation of | |
330 | the placed image to user code, rather than trying to flip the contents of | |
331 | the image file directly. See writeup in Docs.pm. | |
332 | ||
333 | K. A means to update PDF content already written to the $pdf data structure, | |
334 | but not yet written to file. This could involve moving a text write (one | |
335 | or more translates) to somewhere else on the page, or even moving from one | |
336 | page to another. More general editing of material already "written" out, | |
337 | such as changing a font size or a color -- would need a way to find the | |
338 | desired content to be modified. Ability to erase content or move it from | |
339 | page to page (e.g., started a table and then found it wouldn't fit on one | |
340 | page -- move it to the top of the next page to allow display on a single | |
341 | page). If virtual pages, design with all this in mind, so easy to update | |
342 | content before being "written" out. If nothing is written to the PDF file | |
343 | until the end (save or saveas), this might even supersede virtual pages. | |
344 | ||
345 | L. (See B and H items above) Consider a $page->subpage(x, y, w, h, opts) to | |
346 | subclass a page into a restricted area (not necessarily rectangular, or | |
347 | bounded in height). Things like columns and table cells could then be | |
348 | treated as normal pages (inherit text and graphics contexts?) and anything | |
349 | could be put into them. May want to wait until have virtual write | |
350 | capability rather than writing to file right away, for fitting table cells | |
351 | into a page, and balancing columns. Once in, build-in PDF::Table capability | |
352 | as PDF::Builder::Table, and can have arbitrary content in a table cell. | |
238 | 353 | |
239 | 354 | ============================================================================= |
73 | 73 | examples/Windows/027_winfont |
74 | 74 | examples/Windows/Win32.pm |
75 | 75 | lib/PDF/Builder.pm |
76 | lib/PDF/Builder/Basic/PDF.pm | |
76 | 77 | lib/PDF/Builder/Basic/PDF/Filter/ASCII85Decode.pm |
77 | 78 | lib/PDF/Builder/Basic/PDF/Filter/ASCIIHexDecode.pm |
78 | 79 | lib/PDF/Builder/Basic/PDF/Filter/FlateDecode.pm |
3 | 3 | "Phil Perry <phil4597@catskilltech.com>" |
4 | 4 | ], |
5 | 5 | "dynamic_config" : 1, |
6 | "generated_by" : "ExtUtils::MakeMaker version 7.46, CPAN::Meta::Converter version 2.150010", | |
6 | "generated_by" : "ExtUtils::MakeMaker version 7.56, CPAN::Meta::Converter version 2.150010", | |
7 | 7 | "license" : [ |
8 | 8 | "open_source" |
9 | 9 | ], |
56 | 56 | "web" : "https://github.com/PhilterPaper/Perl-PDF-Builder" |
57 | 57 | } |
58 | 58 | }, |
59 | "version" : "3.019", | |
59 | "version" : "3.020", | |
60 | 60 | "x_serialization_backend" : "JSON::PP version 4.05" |
61 | 61 | } |
8 | 8 | configure_requires: |
9 | 9 | ExtUtils::MakeMaker: '6.5503' |
10 | 10 | dynamic_config: 1 |
11 | generated_by: 'ExtUtils::MakeMaker version 7.46, CPAN::Meta::Converter version 2.150010' | |
11 | generated_by: 'ExtUtils::MakeMaker version 7.56, CPAN::Meta::Converter version 2.150010' | |
12 | 12 | license: open_source |
13 | 13 | meta-spec: |
14 | 14 | url: http://module-build.sourceforge.net/META-spec-v1.4.html |
26 | 26 | bugtracker: https://github.com/PhilterPaper/Perl-PDF-Builder/issues |
27 | 27 | homepage: https://www.catskilltech.com |
28 | 28 | repository: git://github.com/PhilterPaper/Perl-PDF-Builder.git |
29 | version: '3.019' | |
29 | version: '3.020' | |
30 | 30 | x_serialization_backend: 'CPAN::Meta::YAML version 0.018' |
7 | 7 | my $PERL_version = '5.020000'; # can't use in "use" statement above! eval? |
8 | 8 | # could read from .perl-version file otherwise |
9 | 9 | my $MakeMaker_version = '6.5503'; |
10 | my $version = '3.019'; # PDFbuild.pl updates from 'version' file | |
10 | my $version = '3.020'; # PDFbuild.pl updates from 'version' file | |
11 | ||
12 | # optional libraries... prompt user whether or not to install | |
13 | # nice to have but not vital for many users | |
14 | # if one fails to install, it might alarm the user, but installation of | |
15 | # PDF::Builder can still proceed | |
16 | # user can remove any optional installed library if they don't need it | |
17 | # and want to reclaim the space | |
18 | # update t/00-all-usable.t to exclude modules using these libraries! | |
19 | my @choice_list = ( # use array to guarantee order | |
20 | # 'a' and 'n' are reserved, do not use as key [0] | |
21 | # [1] is actual library name [2] is minimum version [3] description | |
22 | ["t", "Graphics::TIFF", 7, "TIFF image support"], | |
23 | # improved TIFF image processing | |
24 | ["p", "Image::PNG::Libpng", 0.47, "PNG image support"], | |
25 | # advanced/fast PNG image processing | |
26 | ["h", "HarfBuzz::Shaper", 0.23, "complex script support"], | |
27 | # text shaping for Latin script ligatures and kerning, and for | |
28 | # many complex scripts both LTR and RTL directions. | |
29 | ); | |
11 | 30 | |
12 | 31 | # EVERY RELEASE: check https://www.cpan.org/src/ "First release in each branch |
13 | 32 | # of Perl" (NOT "Latest releases in each branch of Perl"!) and subtract |
24 | 43 | # for future consideration |
25 | 44 | #my $master = 'lib/PDF/Builder.pm'; |
26 | 45 | #my $version = MM->parse_version($master); |
46 | ||
47 | # prompt routine lifted from IO::Socket::SSL, and modified to allow | |
48 | # arbitrary choices (not just y and n). NOT checked: that default is | |
49 | # among choices, and default is lowercase! | |
50 | # for the original Y/n, choices 'yn' max 1 default 'y' or 'n' | |
51 | local $| = 1; | |
52 | ||
53 | my $make_choice = sub { | |
54 | my ($msg, $choices, $max, $default) = @_; | |
55 | return $default if defined $default && $ENV{PERL_MM_USE_DEFAULT}; | |
56 | # Taken from ExtUtils::MakeMaker 6.16 (Michael Schwern) so that | |
57 | # the prompt() function can be emulated for older versions of ExtUtils::MakeMaker. | |
58 | while ( -t STDIN && (-t STDOUT || !(-f STDOUT || -c STDOUT))) { | |
59 | print "$msg "; | |
60 | my $choice = <>; | |
61 | $choice =~ s/\s//g; # strip off whitespace | |
62 | if (length($choice) > $max) { next; } # too many entries? | |
63 | $choice ||= $default; | |
64 | next if $choice !~ m/^([$choices]+)$/i; | |
65 | return lc($1); | |
66 | } | |
67 | ||
68 | return $default; | |
69 | }; | |
27 | 70 | |
28 | 71 | my %WriteMakefileArgs = |
29 | 72 | ( |
42 | 85 | "ExtUtils::MakeMaker" => $MakeMaker_version, |
43 | 86 | }, |
44 | 87 | |
45 | # BUILD_REQUIRES => { | |
88 | # BUILD_REQUIRES => { # mandatory prereqs listed here | |
46 | 89 | # }, |
47 | 90 | |
48 | 91 | TEST_REQUIRES => { |
89 | 132 | |
90 | 133 | }, |
91 | 134 | |
92 | # optional libraries -- nice to have but not vital for most users | |
93 | # if one fails to install, it might alarm the user, but installation of | |
94 | # PDF::Builder can still proceed | |
95 | # user can remove any optional installed library if they don't need it | |
96 | # and want to reclaim the space | |
97 | # update t/00-all-usable.t to exclude modules using these libraries! | |
98 | ## recommends => { | |
99 | ## 'Graphics::TIFF' => 0, # TIFF image processing | |
100 | ## 'Image::PNG::Libpng' => 0, # advanced/fast PNG image processing | |
101 | ## 'HarfBuzz::Shaper' => 1.7.7, # text shaping for Latin script ligatures | |
102 | ## # and kerning, and for many complex | |
103 | ## # scripts both LTR and RTL directions. | |
104 | ## }, | |
135 | # recommends (optional prereqs) goes here | |
105 | 136 | |
106 | 137 | }, |
107 | 138 | |
117 | 148 | # $WriteMakefileArgs{PREREQ_PM}{'MacPerl'} = '0'; |
118 | 149 | #} |
119 | 150 | |
151 | my ($prompt, $i, $c, $all_list); | |
152 | my $list_len = scalar(@choice_list); | |
153 | # build multi-line prompt and output it | |
154 | if (scalar @choice_list > 0) { # in case all disabled! | |
155 | $prompt = "Attempt to install or update optional prereq libraries?\n". | |
156 | " a (default) = install all choices listed below\n". | |
157 | " n = do NOT install any of them\n". | |
158 | " or enter 1 to $list_len of the following choices:\n"; | |
159 | $all_list = ''; | |
160 | for ($i = 0; $i < $list_len; $i++) { | |
161 | $all_list .= $choice_list[$i][0]; | |
162 | $prompt .= " $choice_list[$i][0] = install/update $choice_list[$i][1] ($choice_list[$i][3] support)"; | |
163 | if ($i < $list_len-1) { $prompt .= "\n"; } | |
164 | } | |
165 | $prompt .= " [A|n|{".join('|', split //,$all_list)."}]"; | |
166 | ||
167 | $c = $make_choice->( $prompt, 'an'.$all_list, $list_len, 'a' ); | |
168 | } else { | |
169 | $c = 'n'; # default if nothing available | |
170 | } | |
171 | # $c should have one or more of a,n (contradictory!) t,p,h | |
172 | ||
173 | if ($c !~ m/n/) { # remind user that librarie(s) to be installed/updated | |
174 | print <<END; | |
175 | =========================================================================== | |
176 | The installation process will attempt to install or update several OPTIONAL | |
177 | prerequisite packages. If one or more should fail to install, Don't Panic | |
178 | -- you will still be able to run PDF::Builder, but may not be able to use | |
179 | some advanced features. See the README file for more information. | |
180 | =========================================================================== | |
181 | END | |
182 | if ($c =~ m/a/) { $c = $all_list; } | |
183 | ||
184 | # $c is one or more choices (no 'a' or 'n') | |
185 | # build 'recommends' entry into %WriteMakefileArgs | |
186 | for ($i = 0; $i < $list_len; $i++) { | |
187 | if (index($c, $choice_list[$i][0]) < 0) { next; } | |
188 | $WriteMakefileArgs{'META_MERGE'}{'recommends'}{ | |
189 | "$choice_list[$i][1]"} = $choice_list[$i][2]; | |
190 | } | |
191 | } | |
192 | ||
120 | 193 | WriteMakefile(%WriteMakefileArgs); |
12 | 12 | Alternatively, you can obtain the full source files from |
13 | 13 | https://github.com/PhilterPaper/Perl-PDF-Builder, where the ticket list (bugs, |
14 | 14 | enhancement requests, etc.) is also kept. Unlike the installable CPAN version, |
15 | this will have to be manually installed (copy files). | |
15 | this will have to be manually installed (copy files; there are no XS compiles | |
16 | at this time). | |
16 | 17 | |
17 | 18 | Note that there are several "optional" libraries (Perl modules) used to extend |
18 | 19 | and improve PDF::Builder. The installation process will not attempt to install |
19 | them if not present -- you need to do this manually (if you want to use them). | |
20 | Read about the list of optional libraries in PDF::Builder::Docs, and decide | |
21 | whether or not you want to manually install any of them. | |
20 | them if not present -- you need to do this manually (if you want to use them), | |
21 | or at least, give the go-ahead during the installation process to try | |
22 | installing them. Read about the list of optional libraries in | |
23 | PDF::Builder::Docs, and decide whether or not you want to install any of them. | |
22 | 24 | |
23 | 25 | REQUIREMENTS ====================================================== |
24 | 26 | |
29 | 31 | not supported. The intent is to not waste time and effort trying to fix bugs |
30 | 32 | which are an artifact of old Perl releases. |
31 | 33 | |
34 | If you MUST install on an older (pre 5.20) Perl, you can try the following | |
35 | for Strawberry Perl (Windows). NO PROMISES! Something similar MAY work for | |
36 | other OS's and Perl installations: | |
37 | Unpack installation file (.tar.gz, via a utility such as 7-Zip) into a | |
38 | directory, and cd to that directory | |
39 | Edit .perl-version and change 5.20.0 to 5.16.0 or whatever level desired | |
40 | Edit Makefile.PL and change use 5.020000; to use 5.016000; | |
41 | change $PERL_version from '5.020000' to '5.016000' | |
42 | cpan . | |
43 | ||
44 | Note that some Perl installers MAY have a means to override or suppress the | |
45 | Perl version check. That may be easier to use. Or, you may have to repack | |
46 | the edited directory back into a .tar.gz installable. YMMV. | |
47 | ||
48 | If all goes well, PDF::Builder will be installed on your system. Whether or | |
49 | not it will RUN is another matter. Please do NOT open a bug report (ticket) | |
50 | unless you're absolutely sure that the problem is not a result of using an | |
51 | old Perl release, e.g., PDF::Builder is using a feature introduced in Perl | |
52 | 5.008 and you're trying to run Perl 5.002! | |
53 | ||
32 | 54 | Libraries used, available from CPAN -- |
33 | 55 | |
34 | 56 | REQUIRED, should be automatically installed... |
37 | 59 | Test::Exception (needed only for installation tests) |
38 | 60 | Test::Memory::Cycle (needed only for installation tests) |
39 | 61 | |
40 | OPTIONAL, install manually if wanted... | |
41 | Graphics::TIFF (recommended if using TIFF image functions) | |
42 | Image::PNG::Libpng (recommended for enhanced PNG image function processing) | |
43 | HarfBuzz::Shaper (recommended for Latin script ligatures and kerning, as | |
62 | OPTIONAL, install manually if wanted AND they failed to install | |
63 | automatically when requested... | |
64 | Graphics::TIFF (recommended if using TIFF image functions) | |
65 | Image::PNG::Libpng (recommended for enhanced PNG image function processing) | |
66 | HarfBuzz::Shaper (recommended for Latin script ligatures and kerning, as | |
44 | 67 | well as for any complex script such as Arabic, Indic |
45 | 68 | scripts, or Khmer) |
46 | 69 | |
60 | 83 | |
61 | 84 | The GNU Lesser General Public License, Version 2.1, February 1999 |
62 | 85 | |
86 | EXCEPT for some files which are explicitly under other licenses. You are | |
87 | permitted (at your option) to redistribute and/or modify this software (those | |
88 | portions under LGPL) at an LGPL version greater than 2.1. See INFO/LICENSE for | |
89 | more information on the licenses and warranty statement. | |
90 | ||
63 | 91 | See INFO/RoadMap file for the PDF::Builder road map. |
64 | 92 | See CONTRIBUTING file for how to contribute to the project. |
65 | 93 | See INFO/SUPPORT file for information on reporting bugs, etc. via GitHub Issues |
68 | 96 | See INFO/KNOWN_INCOMP file for known incompatibilities with PDF::API2. |
69 | 97 | See INFO/Changes* files for older change logs. |
70 | 98 | See INFO/PATENTS file for information on patents. |
71 | See INFO/LICENSE file for the license. | |
72 | 99 | See INFO/CONVERSION file for how to convert from PDF::API2 to PDF::Builder |
73 | 100 | |
74 | 101 | INFO/old/ also has some build and test tool files that are not currently used. |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '2.029'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | use PDF::Builder; |
3 | 3 | |
4 | 4 | use PDF::Builder::Basic::PDF::File; |
5 | 5 | |
6 | our $VERSION = '3.019'; # VERSION | |
6 | our $VERSION = '3.020'; # VERSION | |
7 | 7 | my $LAST_UPDATE = '3.010'; # manually update whenever code is changed |
8 | 8 | |
9 | 9 | my $file = shift(@ARGV); |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | use PDF::Builder::Basic::PDF::File; |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | use PDF::Builder::Basic::PDF::File; |
71 | 71 | use strict; |
72 | 72 | use warnings; |
73 | 73 | |
74 | our $VERSION = '3.019'; # VERSION | |
74 | our $VERSION = '3.020'; # VERSION | |
75 | 75 | my $LAST_UPDATE = '3.002'; # manually update whenever code is changed |
76 | 76 | |
77 | 77 | use PDF::Builder; |
1 | 1 | # buildDoc.pl builds documentation tree from Perl .pod and .pm files (POD) |
2 | 2 | # in case of duplicate names, .pod is used in preference to .pm |
3 | 3 | # |
4 | # (c) copyright 2018 Catskill Technology Services, LLC | |
5 | # licensed under license used in PDF::Builder package | |
4 | # (c) copyright 2018-2020 Catskill Technology Services, LLC | |
5 | # licensed under license used in PDF::Builder package (LGPL 2.1+) | |
6 | 6 | # |
7 | 7 | # there is partial code to implement --all to build all PODs, or update an |
8 | 8 | # existing documentation tree with specific name(s), but the whole process |
15 | 15 | use warnings; |
16 | 16 | use Getopt::Long; |
17 | 17 | |
18 | our $VERSION = '3.019'; # VERSION | |
19 | my $LAST_UPDATE = '3.011'; # manually update whenever code is changed | |
18 | our $VERSION = '3.020'; # VERSION | |
19 | my $LAST_UPDATE = '3.020'; # manually update whenever code is changed | |
20 | 20 | |
21 | 21 | # ============= |
22 | 22 | # CONFIGURATION these may be overridden by command-line flags. If reading from |
72 | 72 | # be an .html file) |
73 | 73 | # accessible => accessible (from root) flag 0=no 1=yes |
74 | 74 | # abstract => text from NAME POD entry |
75 | # parents => [] build list of parent(s) chain | |
76 | # siblings => [] build list of any siblings | |
77 | # children => [] build list of any children | |
75 | 78 | |
76 | 79 | # command line flags and files |
77 | 80 | if (scalar(@ARGV) == 0) { help(); exit(1); } |
118 | 121 | if ($all) { |
119 | 122 | # any stray stuff? ARGV should be empty by now |
120 | 123 | foreach (@ARGV) { |
121 | print "$_ WARNING extra command line content ignored\n"; | |
124 | print "WARNING extra command line content '$_' ignored\n"; | |
122 | 125 | } |
123 | 126 | |
124 | 127 | # get complete list of filepaths in @file_list and initialize flags |
181 | 184 | pmname=>toPM($_), # PM format name |
182 | 185 | status=>-1, # status -1 ready to read |
183 | 186 | accessible=>1, # root accessible yes |
184 | abstract=>'' }; # no abstract yet | |
187 | abstract=>'', # no abstract yet | |
188 | parents=>[], # no parents yet | |
189 | siblings=>[], # no siblings yet | |
190 | children=>[], # no children yet | |
191 | depth=>0, # depth of directory | |
192 | }; | |
185 | 193 | } |
186 | 194 | |
187 | 195 | if ($rootname eq '') { |
228 | 236 | # need to swap records i and i+1 |
229 | 237 | # copying one hash to another can be tricky business, so |
230 | 238 | # we'll do it the hard way |
239 | # parents, siblings, children arrays s/b empty | |
231 | 240 | my %temp; |
232 | 241 | $temp{'pmname'} = $file_list[$i]{'pmname'}; |
233 | 242 | $temp{'fpname'} = $file_list[$i]{'fpname'}; |
234 | 243 | $temp{'status'} = $file_list[$i]{'status'}; |
235 | 244 | $temp{'accessible'} = $file_list[$i]{'accessible'}; |
236 | 245 | $temp{'abstract'} = $file_list[$i]{'abstract'}; |
246 | $temp{'parents'} = $file_list[$i]{'parents'}; | |
247 | $temp{'siblings'} = $file_list[$i]{'siblings'}; | |
248 | $temp{'children'} = $file_list[$i]{'children'}; | |
237 | 249 | $file_list[$i]{'pmname'} = $file_list[$i+1]{'pmname'}; |
238 | 250 | $file_list[$i]{'fpname'} = $file_list[$i+1]{'fpname'}; |
239 | 251 | $file_list[$i]{'status'} = $file_list[$i+1]{'status'}; |
240 | 252 | $file_list[$i]{'accessible'} = $file_list[$i+1]{'accessible'}; |
241 | 253 | $file_list[$i]{'abstract'} = $file_list[$i+1]{'abstract'}; |
254 | $file_list[$i]{'parents'} = $file_list[$i+1]{'parents'}; | |
255 | $file_list[$i]{'siblings'} = $file_list[$i+1]{'siblings'}; | |
256 | $file_list[$i]{'children'} = $file_list[$i+1]{'children'}; | |
242 | 257 | $file_list[$i+1]{'pmname'} = $temp{'pmname'}; |
243 | 258 | $file_list[$i+1]{'fpname'} = $temp{'fpname'}; |
244 | 259 | $file_list[$i+1]{'status'} = $temp{'status'}; |
245 | 260 | $file_list[$i+1]{'accessible'} = $temp{'accessible'}; |
246 | 261 | $file_list[$i+1]{'abstract'} = $temp{'abstract'}; |
262 | $file_list[$i+1]{'parents'} = $temp{'parents'}; | |
263 | $file_list[$i+1]{'siblings'} = $temp{'siblings'}; | |
264 | $file_list[$i+1]{'children'} = $temp{'children'}; | |
247 | 265 | $swap = 1; |
248 | 266 | } |
249 | 267 | } |
431 | 449 | |
432 | 450 | # write $htmlfile back out to its .html file ($target) |
433 | 451 | spew($htmlfile, $target); |
452 | $file_list[$i]{'htmlname'} = $target; | |
434 | 453 | |
435 | 454 | } # processed a .pod or .pm file into .html (was status -1) |
436 | 455 | } # for loop through all entries, looking for status -1 |
484 | 503 | next; |
485 | 504 | } |
486 | 505 | # put 'X' at left margin if not accessible from root, else space |
487 | if ($file_list[$i]{'accessible'} == 1) { | |
506 | ## with NAVIGATION LINKS, all should be accessible from within | |
507 | #if ($file_list[$i]{'accessible'} == 1) { | |
488 | 508 | print $fh "<span class=\"fixedwidth\"> </span>"; |
489 | } else { | |
490 | print $fh "<span class=\"fixedwidth\">X</span>"; | |
491 | } | |
509 | #} else { | |
510 | # print $fh "<span class=\"fixedwidth\">X</span>"; | |
511 | #} | |
492 | 512 | |
493 | 513 | my $fname = $file_list[$i]{'fpname'}; |
494 | 514 | $fname =~ s#$libtop/$leading/##; |
529 | 549 | # cleanup |
530 | 550 | unlink "pod2htmd.tmp"; |
531 | 551 | unlink "pod2html.stderr"; |
552 | ||
553 | # now that the individual HTML files and the master index are done, | |
554 | # 1) generate pmnameA array for each entry in @file_list | |
555 | # 2) generate children, siblings, and parents lists | |
556 | # 3) go through all HTML files (ex master index) and update with NAVIGATION | |
557 | if (scalar(@file_list) <= 1) { | |
558 | print "Only 0 or 1 file_list entries. Do not create NAVIGATION LINKS.\n"; | |
559 | exit(0); | |
560 | } | |
561 | make_pmnameA(); | |
562 | my ($j, $ref); | |
563 | ||
564 | process(0, scalar(@file_list)); | |
565 | ||
566 | # entire file_list should have been recursively processed | |
567 | # we have filled in the 'children' of each node. now give each node its | |
568 | # parent, and then siblings | |
569 | do_parents(); | |
570 | # remove duplicate children (grandchildren) AFTER parents set, so that an | |
571 | # element may have chain of parents all the way up to the root | |
572 | remove_grandchildren(); | |
573 | do_siblings(); | |
574 | ||
575 | # update existing HTML files | |
576 | update_HTML(); | |
577 | exit(0); | |
578 | ||
579 | # ================================== | |
580 | sub update_HTML{ | |
581 | my ($i, $fname, $string, @count, $ref, $pos, $newstring, @list); | |
582 | ||
583 | for ($i=0; $i<scalar(@file_list); $i++) { | |
584 | # read POD from file $file_list[$i]{'fpname'} | |
585 | # wrote HTML to $file_list[$i]{'htmlname'} | |
586 | $fname = $file_list[$i]{'htmlname'}; | |
587 | print "Updating NAVIGATION LINKS in $fname\n"; | |
588 | ||
589 | # print "file_list[$i]: $file_list[$i]{'pmname'} - $file_list[$i]{'abstract'}\n"; | |
590 | # read in HTML into $string, modify | |
591 | $string = slurp($fname); | |
592 | if (length $string == 0) { | |
593 | print "ERROR: unable to read in file $fname for Navigation Links update!\n"; | |
594 | next; | |
595 | } | |
596 | @count = (0, 0, 0); # no parents, siblings, or children | |
597 | # count of parents | |
598 | $ref = $file_list[$i]{'parents'}; | |
599 | if (defined $ref) { $count[0] = scalar(@$ref); } | |
600 | $count[0]++; # always the master index | |
601 | # count of siblings | |
602 | $ref = $file_list[$i]{'siblings'}; | |
603 | if (defined $ref) { $count[1] = scalar(@$ref); } | |
604 | # count of children | |
605 | $ref = $file_list[$i]{'children'}; | |
606 | if (defined $ref) { $count[2] = scalar(@$ref); } | |
607 | ||
608 | # update link directory at top of file | |
609 | # guaranteed there's at least one parent entry | |
610 | $pos = index $string, '<ul id="index">'; | |
611 | if ($pos < 0) { | |
612 | print "ERROR: can't find link index in file $fname.\n"; | |
613 | next; | |
614 | } | |
615 | $pos = index $string, "\n</ul>", $pos; | |
616 | if ($pos < 0) { | |
617 | print "ERROR: can't find end of link index in file $fname.\n"; | |
618 | next; | |
619 | } | |
620 | # split point is $pos+1 (just before </ul>) | |
621 | ||
622 | $newstring = " <li><a href=\"#NAVIGATION-LINKS\">NAVIGATION LINKS</a>\n" . | |
623 | " <ul>\n" . | |
624 | " <li><a href=\"#Up-Parents\">Up (Parents)</a></li>\n"; | |
625 | if ($count[1]) { | |
626 | $newstring .= | |
627 | " <li><a href=\"#Siblings\">Siblings</a></li>\n"; | |
628 | } | |
629 | if ($count[2]) { | |
630 | $newstring .= | |
631 | " <li><a href=\"#Down-Children\">Down (Children)</a></li>\n"; | |
632 | } | |
633 | $newstring .= " </ul>\n" . | |
634 | " </li>\n"; | |
635 | $string = substr($string, 0, $pos+1) . $newstring . substr($string, $pos+1); | |
636 | ||
637 | # just before </body> will be location of new section | |
638 | $pos = index $string, "\n</body>\n"; | |
639 | ||
640 | $newstring = "<h1 id=\"NAVIGATION-LINKS\">NAVIGATION LINKS</h1>\n"; | |
641 | ||
642 | # if parents (should be), output that section | |
643 | $newstring .= "\n<h2 id=\"Up-Parents\">Up (Parents)</h2>\n"; | |
644 | # add ever-present master index entry at same level as [0] entry | |
645 | # note: omitting id= because it's not used anywhere | |
646 | $newstring .= "\n<dl>\n\n<dt>" . | |
647 | "<a href=\"".go_up($file_list[$i]{'depth'}-1) . | |
648 | "index.html\">Master Index</a> </dt>\n<dd>\n\n</dd>\n"; | |
649 | ||
650 | $ref = $file_list[$i]{'parents'}; | |
651 | if (defined $ref && scalar(@$ref)) { | |
652 | # @$ref is an ordered array of @file_list indice(s) | |
653 | # pointing back to any parents | |
654 | @list = @$ref; | |
655 | foreach (@list) { | |
656 | $newstring .= "<a href=\"".go_up($file_list[$i]{'depth'}) . | |
657 | "$file_list[$_]{'htmlname'}\">$file_list[$_]{'pmname'}</a> -- $file_list[$_]{'abstract'}</dt>\n<dd>\n\n</dd>\n"; | |
658 | } | |
659 | } | |
660 | $newstring .= "</dl>\n"; | |
661 | ||
662 | # if any siblings, output that section | |
663 | if ($count[1]) { | |
664 | $ref = $file_list[$i]{'siblings'}; | |
665 | if (defined $ref && scalar(@$ref)) { | |
666 | $newstring .= "\n<h2 id=\"Siblings\">Siblings</h2>\n"; | |
667 | # @$ref is an ordered array of @file_list indice(s) | |
668 | # pointing back to any siblings | |
669 | @list = @$ref; | |
670 | foreach (@list) { | |
671 | $newstring .= "<a href=\"".go_up($file_list[$i]{'depth'}) . | |
672 | "$file_list[$_]{'htmlname'}\">$file_list[$_]{'pmname'}</a> -- $file_list[$_]{'abstract'}</dt>\n<dd>\n\n</dd>\n"; | |
673 | } | |
674 | $newstring .= "</dl>\n"; | |
675 | } | |
676 | } | |
677 | ||
678 | # if any children, output that section | |
679 | if ($count[2]) { | |
680 | $ref = $file_list[$i]{'children'}; | |
681 | if (defined $ref && scalar(@$ref)) { | |
682 | $newstring .= "\n<h2 id=\"Down-Children\">Down (Children)</h2>\n"; | |
683 | # @$ref is an ordered array of @file_list indice(s) | |
684 | # pointing back to any children | |
685 | @list = @$ref; | |
686 | foreach (@list) { | |
687 | $newstring .= "<a href=\"".go_up($file_list[$i]{'depth'}) . | |
688 | "$file_list[$_]{'htmlname'}\">$file_list[$_]{'pmname'}</a> -- $file_list[$_]{'abstract'}</dt>\n<dd>\n\n</dd>\n"; | |
689 | } | |
690 | $newstring .= "</dl>\n"; | |
691 | } | |
692 | } | |
693 | ||
694 | # write file back out | |
695 | $string = substr($string, 0, $pos+1) . $newstring . substr($string, $pos+1); | |
696 | spew($string, $fname); | |
697 | } | |
698 | return; | |
699 | } | |
700 | ||
701 | sub go_up { | |
702 | my $depth = shift; | |
703 | if ($depth == 1) { return ''; } | |
704 | return '../' x ($depth-1); | |
705 | } | |
706 | ||
707 | # ================================== | |
708 | # recursively process this range of file_list rows, filling in children array | |
709 | # handle a subset (initially the whole thing) of file_list | |
710 | sub process { | |
711 | my ($start, $len) = @_; | |
712 | # starting element number in file_list, and count of rows | |
713 | ||
714 | if ($len <= 1) { return; } # single element won't have any children | |
715 | ||
716 | my ($i, $j, $dir); | |
717 | my ($start2, $len2, $ref, $refc); | |
718 | ||
719 | # if $start row's pmnameA is now empty, that means that $start is the | |
720 | # parent of everything else below it | |
721 | $ref = $file_list[$start]{'pmnameA'}; | |
722 | if (scalar(@$ref) == 0) { | |
723 | # element $start is parent to all (1..$len-1) other elements | |
724 | # we want only DIRECT children (leaves), not nodes (grandchildren), | |
725 | # so later we will remove grandchildren (duplicates) | |
726 | $refc = $file_list[$start]{'children'}; | |
727 | for ($j=1; $j<$len; $j++) { | |
728 | push @$refc, $start+$j; | |
729 | } | |
730 | # remove first element (empty pmnameA) from further consideration | |
731 | process($start+1, $len-1); | |
732 | return; | |
733 | } | |
734 | ||
735 | # strip first element from each pmnameA if ALL are duplicates | |
736 | LOOP: while (1) { | |
737 | $ref = $file_list[$start]{'pmnameA'}; | |
738 | last if !scalar(@$ref[0]); # first element is empty? | |
739 | $dir = @$ref[0]; | |
740 | for ($i=1; $i<$len; $i++) { | |
741 | last LOOP if @{ $file_list[$start+$i]{'pmnameA'} }[0] ne $dir; | |
742 | } | |
743 | # if we got to here, entire section's [0] element is the same | |
744 | # so remove it | |
745 | for ($i=0; $i<$len; $i++) { | |
746 | shift @{ $file_list[$start+$i]{'pmnameA'} }; | |
747 | } | |
748 | ||
749 | process($start, $len); | |
750 | return; | |
751 | } | |
752 | ||
753 | # now break up remainder into two subranges (first pmnameA element | |
754 | # is NOT the same in all rows) | |
755 | $len2 = 0; | |
756 | for ($i=$start; $i<$start+$len; $i++) { | |
757 | $ref = $file_list[$i]{'pmnameA'}; | |
758 | if ($len2 == 0) { | |
759 | # should NOT see an empty pmnameA for element start! | |
760 | $dir = @$ref[0]; | |
761 | $len2++; | |
762 | next; | |
763 | } | |
764 | ||
765 | if (@$ref[0] eq $dir) { | |
766 | # still a match | |
767 | $len2++; | |
768 | next; | |
769 | ||
770 | } else { | |
771 | # no longer a match. len2 always at least a run of 1 | |
772 | process($start, $len2); | |
773 | # $len2 .. end is remainder, process it | |
774 | process($start+$len2, $len-$len2); | |
775 | ||
776 | return; | |
777 | } | |
778 | } # for loop through file_list section, first level of chunking | |
779 | ||
780 | return; # probably never hit this | |
781 | } | |
782 | ||
783 | # ================================== | |
784 | # remove an element's grandchildren from its children list | |
785 | sub remove_grandchildren { | |
786 | my ($start, $ref, $refs, $ele, $i, $j); | |
787 | ||
788 | for ($start=1; $start<scalar(@file_list); $start++) { | |
789 | # if this element has children, check 0..start-1 for duplicate | |
790 | # children (who are actually grandchildren) and remove them from there | |
791 | $refs = $file_list[$start]{'children'}; | |
792 | if (!defined $refs) { next; } | |
793 | if (!scalar(@$refs)) { next; } # no children for this element | |
794 | ||
795 | for ($ele=0; $ele<$start; $ele++) { | |
796 | $ref = $file_list[$ele]{'children'}; | |
797 | if (!defined $ref) { next; } | |
798 | if (!scalar(@$ref)) { next; } # no children for this element | |
799 | ||
800 | # remove duplicates (grandchildren) from $ele's children | |
801 | for ($i=0; $i<scalar(@$refs); $i++) { | |
802 | for ($j=0; $j<scalar(@$ref); $j++) { | |
803 | if (@$ref[$j] == @$refs[$i]) { | |
804 | # $j is a grandchild, so remove it | |
805 | splice(@$ref, $j, 1); | |
806 | } | |
807 | } | |
808 | } | |
809 | } | |
810 | } | |
811 | ||
812 | return; | |
813 | } | |
814 | ||
815 | # ================================== | |
816 | # follow children links to set their parents | |
817 | sub do_parents { | |
818 | my ($i, @children, $child); | |
819 | for ($i=0; $i<scalar(@file_list); $i++) { | |
820 | if (!defined $file_list[$i]{'children'}) { next; } | |
821 | @children = @{ $file_list[$i]{'children'} }; | |
822 | if (!scalar(@children)) { next; } # no children? | |
823 | ||
824 | while (scalar(@children)) { | |
825 | $child = shift @children; | |
826 | push @{ $file_list[$child]{'parents'} }, $i; | |
827 | } | |
828 | } | |
829 | ||
830 | return; | |
831 | } | |
832 | ||
833 | # ================================== | |
834 | # find siblings from children of a given node | |
835 | sub do_siblings { | |
836 | my ($i, $j, $k, $refs, @children, $child); | |
837 | for ($i=0; $i<scalar(@file_list); $i++) { | |
838 | if (!defined $file_list[$i]{'children'}) { next; } | |
839 | @children = @{ $file_list[$i]{'children'} }; | |
840 | ||
841 | # no children, or a lonely only child? it has no siblings | |
842 | if (scalar(@children) <= 1) { next; } | |
843 | ||
844 | for ($j=0; $j<scalar(@children); $j++) { | |
845 | $child = $children[$j]; | |
846 | $refs = $file_list[$child]{'siblings'}; | |
847 | # this child's siblings is the entire children list except itself | |
848 | for ($k=0; $k<scalar(@children); $k++) { | |
849 | push @$refs, $children[$k] if $children[$k] != $child; | |
850 | } | |
851 | } | |
852 | } | |
853 | ||
854 | return; | |
855 | } | |
856 | ||
857 | # ================================== | |
858 | # split up pmname into pmnameA, and while here, set depth | |
859 | sub make_pmnameA { | |
860 | my (@tempA, $i, $j); | |
861 | for ($i=0; $i<scalar(@file_list); $i++) { | |
862 | @tempA = split /::/, $file_list[$i]{'pmname'}; | |
863 | $file_list[$i]{'depth'} = scalar(@tempA); | |
864 | $file_list[$i]{'pmnameA'} = []; | |
865 | for ($j=0; $j<scalar(@tempA); $j++) { | |
866 | @{$file_list[$i]{'pmnameA'}}[$j] = $tempA[$j]; | |
867 | } | |
868 | } | |
869 | ||
870 | return; | |
871 | } | |
532 | 872 | |
533 | 873 | # ================================== |
534 | 874 | # function to spew a one-string file out to the file |
598 | 938 | } |
599 | 939 | next; |
600 | 940 | } |
601 | push @list, { fpname=>"$dirname/$direntry", pmname=>toPM("$dirname/$direntry"), status=>-2, accessible=>1, abstract=>'' }; | |
941 | push @list, { fpname=>"$dirname/$direntry", pmname=>toPM("$dirname/$direntry"), status=>-2, accessible=>1, abstract=>'', parents=>[], siblings=>[], children=>[], depth=>0 }; | |
602 | 942 | } else { |
603 | 943 | # it should be a directory. recursively process it |
604 | 944 | if (!-d "$dirname/$direntry") { print "$dirname/$direntry WARNING is not a directory or file, ignored\n"; next; } |
110 | 110 | $text->fillcolor('black'); |
111 | 111 | $text->text(" to go to Page 1."); |
112 | 112 | |
113 | # same, except position and zoom | |
114 | $x = 100; | |
115 | $y = 400; | |
116 | ||
117 | $text->translate($x, $y); | |
118 | $text->text("Click "); | |
119 | $x += $text->advancewidth("Click "); | |
120 | ||
121 | # x,y should be at LL corner of "here" (on baseline) | |
122 | $text->fillcolor('blue'); | |
123 | $text->text("here"); | |
124 | $target_width = $text->advancewidth("here"); | |
125 | ||
126 | $annotation = $page->annotation(); | |
127 | $tgt_page = $pdf->openpage(1); # target page 1 | |
128 | $annotation->link($tgt_page, | |
129 | -rect => [$x-1, $y-5, $x+1+$target_width, $y-5+1+$font_size], | |
130 | # clickable area | |
131 | -border => [0, 0, $border], # show border | |
132 | -color => [$b_red, $b_green, $b_blue], # border color | |
133 | -xyz => [ 200,550, 1.5 ], # new position, zoom factor 150% | |
134 | ); | |
135 | ||
136 | # restore color and do rest of line | |
137 | $text->fillcolor('black'); | |
138 | $text->text(" to go to Page 1 positioned/zoomed."); | |
139 | ||
140 | ||
113 | 141 | # ---------- go to a page in ANOTHER document |
114 | 142 | $page = $pdf->page(); # page 3 |
115 | 143 | $text = $page->text(); |
144 | 172 | # restore color and do rest of line |
145 | 173 | $text->fillcolor('black'); |
146 | 174 | $text->text(" to go to Page 1 of another PDF document."); |
175 | ||
176 | # same, except position and zoom | |
177 | $x = 100; | |
178 | $y = 400; | |
179 | ||
180 | $text->translate($x, $y); | |
181 | $text->text("Click "); | |
182 | $x += $text->advancewidth("Click "); | |
183 | ||
184 | # x,y should be at LL corner of "here" (on baseline) | |
185 | $text->fillcolor('blue'); | |
186 | $text->text("here"); | |
187 | $target_width = $text->advancewidth("here"); | |
188 | ||
189 | $annotation = $page->annotation(); | |
190 | $annotation->pdf_file("resources/HarfBuzz_example.pdf", | |
191 | 1, # page number | |
192 | -rect => [$x-1, $y-5, $x+1+$target_width, $y-5+1+$font_size], | |
193 | # clickable area | |
194 | -border => [0, 0, $border], # show border | |
195 | -color => [$b_red, $b_green, $b_blue], # border color | |
196 | -fitr => [100,350, 300,550], # new viewport | |
197 | ); | |
198 | ||
199 | # restore color and do rest of line | |
200 | $text->fillcolor('black'); | |
201 | $text->text(" to go to Page 1 of another PDF document, windowed."); | |
147 | 202 | |
148 | 203 | # ---------- launch (default OS action) another file |
149 | 204 | $page = $pdf->page(); # page 4 |
8 | 8 | use warnings; |
9 | 9 | use strict; |
10 | 10 | |
11 | our $VERSION = '3.019'; # VERSION | |
11 | our $VERSION = '3.020'; # VERSION | |
12 | 12 | my $LAST_UPDATE = '3.013'; # manually update whenever code is changed |
13 | 13 | |
14 | 14 | use Math::Trig; |
20 | 20 | my $bleedbox_adj = 36/pt; # in from crop box on top and right for printer inst. |
21 | 21 | my $cropbox_adj = 0.25/in; # in from media edge |
22 | 22 | |
23 | our $VERSION = '3.019'; # VERSION | |
23 | our $VERSION = '3.020'; # VERSION | |
24 | 24 | my $LAST_UPDATE = '3.017'; # manually update whenever code is changed |
25 | 25 | |
26 | 26 | my $PDFname = $0; |
32 | 32 | use warnings; |
33 | 33 | use strict; |
34 | 34 | |
35 | our $VERSION = '3.019'; # VERSION | |
35 | our $VERSION = '3.020'; # VERSION | |
36 | 36 | my $LAST_UPDATE = '3.010'; # manually update whenever code is changed |
37 | 37 | |
38 | 38 | use Math::Trig; |
5 | 5 | use warnings; |
6 | 6 | use strict; |
7 | 7 | |
8 | our $VERSION = '3.019'; # VERSION | |
8 | our $VERSION = '3.020'; # VERSION | |
9 | 9 | my $LAST_UPDATE = '3.013'; # manually update whenever code is changed |
10 | 10 | |
11 | 11 | use Math::Trig; |
5 | 5 | use warnings; |
6 | 6 | use strict; |
7 | 7 | |
8 | our $VERSION = '3.019'; # VERSION | |
8 | our $VERSION = '3.020'; # VERSION | |
9 | 9 | my $LAST_UPDATE = '3.010'; # manually update whenever code is changed |
10 | 10 | |
11 | 11 | use Math::Trig; |
0 | 0 | #!/usr/bin/perl |
1 | 1 | ########################## |
2 | # CAUTION: cannot merge until new HarfBuzz::Shaper released | |
3 | # ditto for Content.pm changes | |
2 | # NOTE: appears to bring in the entire Ming font (Chinese), rather than a | |
3 | # subset. sorry about that! ref RT 130041 | |
4 | 4 | # Note to maintainer: don't forget to refresh HarfBuzz_example.pdf |
5 | 5 | ########################## |
6 | 6 | # demonstrate some usage of HarfBuzz::Shaper and related text calls |
10 | 10 | use strict; |
11 | 11 | use warnings; |
12 | 12 | |
13 | our $VERSION = '3.019'; # VERSION | |
13 | our $VERSION = '3.020'; # VERSION | |
14 | 14 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
15 | 15 | |
16 | 16 | my $PDFname = $0; |
59 | 59 | # to be. |
60 | 60 | |
61 | 61 | my $pdf = PDF::Builder->new(-compress => 'none'); |
62 | #my $pdf = PDF::Builder->new(); | |
63 | ||
62 | 64 | $pdf->mediabox('universal'); # narrower and shorter of US letter and A4, so |
63 | 65 | # it should be printable on either paper |
64 | 66 | my $labelFont = $pdf->corefont('Helvetica'); |
385 | 387 | # some random Chinese characters. most interested in what direction is |
386 | 388 | # the default, and what is settable |
387 | 389 | 'TTBChinese' => { 'title' => 'TTBChinese', |
388 | 'fontFile' => '/Program Files (x86)/Adobe/Acrobat Reader DC/Resource/CIDFont/AdobeMingStd-Light.otf', | |
390 | # 'fontFile' => '/Program Files (x86)/Adobe/Acrobat Reader DC/Resource/CIDFont/AdobeMingStd-Light.otf', | |
391 | 'fontFile' => '/Program Files (x86)/Adobe/Acrobat Reader DC/Resource/CIDFont/AdobeGothicStd-Light.otf', | |
389 | 392 | 'dir' => 'T', |
390 | 393 | 'script' => 'Chin', |
391 | 'text' => ["\x{5A40}\x{5A41}\x{5A42}\x{5A43}", " PDF::Builder ", "\x{5A44}\x{5A45}"] }, | |
394 | # 'text' => ["\x{5A40}\x{5A41}\x{5A42}\x{5A43}", " PDF::Builder ", "\x{5A44}\x{5A45}"] }, | |
395 | 'text' => ["\x{58D8}\x{5A41}\x{5C62}\x{6A13}", " PDF::Builder ", "\x{6DDA}\x{6F0F}"] }, | |
392 | 396 | |
393 | 397 | # Languages which are normally RTL don't seem to behave with TTB. |
394 | 398 | # I would expect them to be reversed, but they aren't. Maybe the direction |
6 | 6 | use warnings; |
7 | 7 | use strict; |
8 | 8 | |
9 | our $VERSION = '3.019'; # VERSION | |
9 | our $VERSION = '3.020'; # VERSION | |
10 | 10 | my $LAST_UPDATE = '3.017'; # manually update whenever code is changed |
11 | 11 | |
12 | 12 | use PDF::Builder; |
2 | 2 | use strict; |
3 | 3 | no warnings qw[ deprecated recursion uninitialized ]; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.010'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | use Win32::TieRegistry qw( :KEY_ ); # creates $Registry, et al. |
Binary diff not shown
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Basic::PDF::Utils; |
16 | 16 | use strict; |
17 | 17 | use warnings; |
18 | 18 | |
19 | our $VERSION = '3.019'; # VERSION | |
19 | our $VERSION = '3.020'; # VERSION | |
20 | 20 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
21 | 21 | |
22 | 22 | =head1 NAME |
16 | 16 | use strict; |
17 | 17 | use warnings; |
18 | 18 | |
19 | our $VERSION = '3.019'; # VERSION | |
19 | our $VERSION = '3.020'; # VERSION | |
20 | 20 | my $LAST_UPDATE = '3.010'; # manually update whenever code is changed |
21 | 21 | |
22 | 22 | =head1 NAME |
16 | 16 | use strict; |
17 | 17 | no warnings qw[ deprecated recursion uninitialized ]; |
18 | 18 | |
19 | our $VERSION = '3.019'; # VERSION | |
19 | our $VERSION = '3.020'; # VERSION | |
20 | 20 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
21 | 21 | |
22 | 22 | our $mincache = 16 * 1024 * 1024; |
16 | 16 | use strict; |
17 | 17 | no warnings qw[ deprecated recursion uninitialized ]; |
18 | 18 | |
19 | our $VERSION = '3.019'; # VERSION | |
20 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed | |
19 | our $VERSION = '3.020'; # VERSION | |
20 | my $LAST_UPDATE = '3.020'; # manually update whenever code is changed | |
21 | 21 | |
22 | 22 | =head1 NAME |
23 | 23 | |
1167 | 1167 | sub _unpack_xref_stream { |
1168 | 1168 | my ($self, $width, $data) = @_; |
1169 | 1169 | |
1170 | return unpack('C', $data) if $width == 1; | |
1171 | return unpack('n', $data) if $width == 2; | |
1172 | return unpack('N', "\x00$data") if $width == 3; | |
1173 | return unpack('N', $data) if $width == 4; | |
1174 | return unpack('Q', $data) if $width == 8; # PDF 1.5+? | |
1175 | ||
1176 | die "Invalid column width: $width"; | |
1170 | # handle some oddball cases | |
1171 | if ($width == 3) { | |
1172 | $data = "\x00$data"; | |
1173 | $width = 4; | |
1174 | } elsif ($width == 5) { | |
1175 | $data = "\x00\x00\x00$data"; | |
1176 | $width = 8; | |
1177 | } elsif ($width == 6) { | |
1178 | $data = "\x00\x00$data"; | |
1179 | $width = 8; | |
1180 | } elsif ($width == 7) { | |
1181 | $data = "\x00$data"; | |
1182 | $width = 8; | |
1183 | } | |
1184 | # in all cases, "Network" (Big-Endian) byte order assumed | |
1185 | return unpack('C', $data) if $width == 1; | |
1186 | return unpack('n', $data) if $width == 2; | |
1187 | return unpack('N', $data) if $width == 4; | |
1188 | if ($width == 8) { | |
1189 | # Some ways other packages handle this, without Perl-64, according | |
1190 | # to Vadim Repin. Possibly they end up converting the value to | |
1191 | # "double" behind the scenes if on a 32-bit platform? | |
1192 | # PDF::Tiny return hex unpack('H16', $data); | |
1193 | # CAM::PDF my @b = unpack('C*', $data); | |
1194 | # my $i=0; ($i <<= 8) += shift @b while @b; return $i; | |
1195 | ||
1196 | if (substr($data, 0, 4) eq "\x00\x00\x00\x00") { | |
1197 | # can treat as 32 bit unsigned int | |
1198 | return unpack('N', substr($data, 4, 4)); | |
1199 | } else { | |
1200 | # requires 64-bit platform (chip and Perl), else fatal error | |
1201 | # it may blow up and produce a smoking crater if 32-bit Perl! | |
1202 | # also note that Q needs Big-Endian flag (>) specified, else | |
1203 | # it will use the native chip order (Big- or Little- Endian) | |
1204 | return unpack('Q>', $data); | |
1205 | } | |
1206 | } | |
1207 | ||
1208 | die "Unsupported field width: $width. 1-8 supported."; | |
1177 | 1209 | } |
1178 | 1210 | |
1179 | 1211 | sub readxrtr { |
1384 | 1416 | } elsif ($buf =~ m/^(\d+)\s+(\d+)\s+obj/i) { |
1385 | 1417 | my ($xref_obj, $xref_gen) = ($1, $2); |
1386 | 1418 | |
1419 | PDF::Builder->verCheckOutput(1.5, "importing cross-reference stream"); | |
1387 | 1420 | # XRef streams |
1388 | 1421 | ($tdict, $buf) = $self->readval($buf); |
1389 | 1422 |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.010'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | =head1 NAME |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '2.029'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | =head1 NAME |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use POSIX qw(ceil floor); |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '2.029'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | =head1 NAME |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '2.029'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | =head1 NAME |
14 | 14 | use strict; |
15 | 15 | use warnings; |
16 | 16 | |
17 | our $VERSION = '3.019'; # VERSION | |
17 | our $VERSION = '3.020'; # VERSION | |
18 | 18 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
19 | 19 | |
20 | 20 | use PDF::Builder::Basic::PDF::Filter::ASCII85Decode; |
4 | 4 | |
5 | 5 | use strict; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Basic::PDF::Filter; |
16 | 16 | use strict; |
17 | 17 | use warnings; |
18 | 18 | |
19 | our $VERSION = '3.019'; # VERSION | |
19 | our $VERSION = '3.020'; # VERSION | |
20 | 20 | my $LAST_UPDATE = '3.011'; # manually update whenever code is changed |
21 | 21 | |
22 | 22 | =head1 NAME |
16 | 16 | use strict; |
17 | 17 | use warnings; |
18 | 18 | |
19 | our $VERSION = '3.019'; # VERSION | |
19 | our $VERSION = '3.020'; # VERSION | |
20 | 20 | my $LAST_UPDATE = '3.010'; # manually update whenever code is changed |
21 | 21 | |
22 | 22 | =head1 NAME |
16 | 16 | use strict; |
17 | 17 | use warnings; |
18 | 18 | |
19 | our $VERSION = '3.019'; # VERSION | |
19 | our $VERSION = '3.020'; # VERSION | |
20 | 20 | my $LAST_UPDATE = '2.029'; # manually update whenever code is changed |
21 | 21 | |
22 | 22 | =head1 NAME |
13 | 13 | |
14 | 14 | use strict; |
15 | 15 | use warnings; |
16 | ||
17 | our $VERSION = '3.019'; # VERSION | |
18 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed | |
16 | use Scalar::Util 'isweak'; | |
17 | ||
18 | our $VERSION = '3.020'; # VERSION | |
19 | my $LAST_UPDATE = '3.020'; # manually update whenever code is changed | |
19 | 20 | |
20 | 21 | =head1 NAME |
21 | 22 | |
113 | 114 | sub release { |
114 | 115 | my ($self) = @_; |
115 | 116 | |
116 | my @tofree = values %$self; | |
117 | my @tofree = grep { !isweak $_ } values %$self; | |
117 | 118 | %$self = (); |
118 | 119 | |
119 | 120 | while (my $item = shift @tofree) { |
16 | 16 | use strict; |
17 | 17 | use warnings; |
18 | 18 | |
19 | our $VERSION = '3.019'; # VERSION | |
19 | our $VERSION = '3.020'; # VERSION | |
20 | 20 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
21 | 21 | |
22 | 22 | use PDF::Builder::Basic::PDF::Dict; |
16 | 16 | |
17 | 17 | use base 'PDF::Builder::Basic::PDF::Dict'; |
18 | 18 | |
19 | our $VERSION = '3.019'; # VERSION | |
19 | our $VERSION = '3.020'; # VERSION | |
20 | 20 | my $LAST_UPDATE = '3.017'; # manually update whenever code is changed |
21 | 21 | |
22 | 22 | use PDF::Builder::Basic::PDF::Array; |
16 | 16 | use strict; |
17 | 17 | use warnings; |
18 | 18 | |
19 | our $VERSION = '3.019'; # VERSION | |
19 | our $VERSION = '3.020'; # VERSION | |
20 | 20 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
21 | 21 | |
22 | 22 | =head1 NAME |
14 | 14 | use strict; |
15 | 15 | use warnings; |
16 | 16 | |
17 | our $VERSION = '3.019'; # VERSION | |
17 | our $VERSION = '3.020'; # VERSION | |
18 | 18 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
19 | 19 | |
20 | 20 | =head1 NAME |
0 | package PDF::Builder::Basic::PDF; | |
1 | ||
2 | use strict; | |
3 | use warnings; | |
4 | ||
5 | our $VERSION = '3.020'; # VERSION | |
6 | my $LAST_UPDATE = '3.020'; # manually update whenever code is changed | |
7 | ||
8 | =head1 NAME | |
9 | ||
10 | PDF::Builder::Basic::PDF - Various utilities and support routines | |
11 | ||
12 | =cut | |
13 | ||
14 | 1; |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | =head1 NAME |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | =head1 NAME |
743 | 743 | B<Note:> if you need to change any text treatment I<within> a paragraph |
744 | 744 | (B<bold> or I<italicized> text, for instance), this can not handle it. Only |
745 | 745 | plain text (all the same font, size, etc.) can be typeset with C<paragraph()>. |
746 | Also, there is currently very limited line splitting (hypenation) to better | |
746 | Also, there is currently very limited line splitting (hyphenation) to better | |
747 | 747 | fit to a given width, and nothing is done for "widows and orphans". |
748 | 748 | |
749 | 749 | =back |
4 | 4 | use strict; |
5 | 5 | no warnings qw( deprecated recursion uninitialized ); |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
8 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | my $LAST_UPDATE = '3.020'; # manually update whenever code is changed | |
9 | 9 | |
10 | 10 | use Carp; |
11 | 11 | use Compress::Zlib qw(); |
1270 | 1270 | for excess data to a routine). There is no check for duplicate points or other |
1271 | 1271 | degeneracies. |
1272 | 1272 | |
1273 | The former name of B<spline> has been deprecated and will be removed. | |
1274 | ||
1275 | =cut | |
1276 | ||
1277 | sub spline { | |
1278 | warn "Use qbspline instead of spline"; | |
1279 | return qbspline(@_); | |
1280 | } | |
1273 | =cut | |
1274 | ||
1281 | 1275 | sub qbspline { |
1282 | 1276 | my ($self) = shift; |
1283 | 1277 | |
3522 | 3516 | Takes an array of hashes produced by HarfBuzz::Shaper and outputs them to the |
3523 | 3517 | PDF output file. HarfBuzz outputs glyph CIDs and positioning information. |
3524 | 3518 | It may rearrange and swap characters (glyphs), and the result may bear no |
3525 | resemblence to the original Unicode point list. You should see | |
3519 | resemblance to the original Unicode point list. You should see | |
3526 | 3520 | examples/HarfBuzz.pl, which shows a number of examples with Latin and non-Latin |
3527 | 3521 | text, as well as vertical writing. |
3528 | 3522 | examples/resources/HarfBuzz_example.pdf is available in case you want to see |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
6 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | my $LAST_UPDATE = '3.020'; # manually update whenever code is changed | |
7 | 7 | |
8 | 8 | # originally part of Builder.pm, it was split out due to its length |
9 | 9 | |
75 | 75 | ability to read interlaced PNG files. See resolved bug report RT 124349, as well |
76 | 76 | as C<image_png>, for more information. |
77 | 77 | |
78 | Note that the installation process will B<not> attempt to install these | |
79 | libraries automatically. If you wish to use them, you will have to manually | |
80 | initiate the installation of such modules (e.g., with "cpan install"). | |
78 | B<*> HarfBuzz::Shaper -- This library enables PDF::Builder to handle complex | |
79 | scripts (Arabic, Devanagari, etc.) as well as non-LTR writing systems. It is | |
80 | also useful for Latin and other simple scripts, for ligatures and improved | |
81 | kerning. HarfBuzz::Shaper is based on a set of HarfBuzz libraries, which it | |
82 | will attempt to build if they are not found. See C<textHS> for more | |
83 | information. | |
84 | ||
85 | Note that the installation process B<will> attempt to install these | |
86 | libraries automatically. If you don't wish to use one or more of them, you are | |
87 | free to uninstall the optional librarie(s). If one or more failed to install, | |
88 | no need to panic -- you simply won't be able to use some advanced features, | |
89 | unless you are able to manually install the modules (e.g., with "cpan install"). | |
81 | 90 | |
82 | 91 | =head2 Strings (Character Text) |
83 | 92 | |
506 | 515 | Perry's intent is to keep all internal methods as upwardly compatible with |
507 | 516 | PDF::API2 as possible, although it is likely that there will be some drift |
508 | 517 | (incompatibilities) over time. At least initially, any program written based on |
509 | PDF::API2 should be convertable to PDF::Builder simply by changing "API2" | |
518 | PDF::API2 should be convertible to PDF::Builder simply by changing "API2" | |
510 | 519 | anywhere it occurs to "Builder". See the INFO/KNOWN_INCOMP known |
511 | 520 | incompatibilities file for further information. |
512 | 521 | |
520 | 529 | which make the object unusable for further operations. You will likely receive |
521 | 530 | an error message about B<can't call method new_obj on an undefined value> if |
522 | 531 | you try to keep using a PDF object. |
532 | ||
533 | =head2 IntegrityCheck | |
534 | ||
535 | The PDF::Builder methods that open an existing PDF file, pass it by the | |
536 | integrity checker method, C<$self-E<gt>IntegrityCheck(level, content)>. This method | |
537 | servers two purposes: 1) to find any C</Version> settings that override the | |
538 | PDF version found in the PDF heading, and 2) perform some basic validations on | |
539 | the contents of the PDF. | |
540 | ||
541 | The C<level> parameter accepts the following values: | |
542 | ||
543 | =over | |
544 | ||
545 | =item 0 = Do not output any diagnostic messages; just return any version override. | |
546 | ||
547 | =item 1 = Output error-level (serious) diagnostic messages, as well as returning any version override. | |
548 | ||
549 | Errors include, in no place was the /Root object specified, or if it was, the indicated object was not found. An object claims another object as its child (/Kids list), but another object has already claimed that child. An object claims a child, but that child does not list a Parent, or the child lists a different Parent. | |
550 | ||
551 | =item 2 = Output error- (serious) and warning- (less serious) level diagnostic messages, as well as returning any version override. B<This is the default.> | |
552 | ||
553 | =item 3 = Output error- (serious), warning- (less serious), and note- (informational) level diagnostic messages, as well as returning any version override. | |
554 | ||
555 | Notes include, in no place was the (optional) /Info object specified, or if it was, the indicated object was not found. An object was referenced, but no entry for it was found among the objects. (This may be OK if the object is not defined, or is on the free list, as the reference will then be ignored.) An object is defined, but it appears that no other object is referencing it. | |
556 | ||
557 | =item 4 = Output error-, warning-, and note-level diagnostic messages, as well as returning any version override. Also dump the diagnostic data structure. | |
558 | ||
559 | =item 5 = Output error-, warning-, and note-level diagnostic messages, as well as returning any version override. Also dump the diagnostic data structure and the C<$self> data structure (generally useful only if you have already read in the PDF file). | |
560 | ||
561 | =back | |
562 | ||
563 | The version is a string (e.g., '1.5') if found, otherwise C<undef> (undefined value) is returned. | |
564 | ||
565 | For controlling the "automatic" call to IntegrityCheck (via opens), the level | |
566 | may be given with the option (flag) C<-diaglevel =E<gt> I<n>>, where C<n> is between 0 and 5. | |
523 | 567 | |
524 | 568 | =head2 Preferences - set user display preferences |
525 | 569 | |
1616 | 1660 | formats. In addition, see C<examples/Content.pl> for an example of placing an |
1617 | 1661 | image on a page, as well as using in a "Form". |
1618 | 1662 | |
1663 | =head3 Why is my image flipped or rotated? | |
1664 | ||
1665 | Something not uncommonly seen when using JPEG photos in a PDF is that the | |
1666 | images will be rotated and/or mirrored (flipped). This may happen when using | |
1667 | TIFF images too. What happens is that the camera stores an image just as it | |
1668 | comes off the CCD sensor, regardless of the camera orientation, and does not | |
1669 | rotate it to the correct orientation! It I<does> store a separate | |
1670 | "orientation" flag to suggest how the image might be corrected, but not all | |
1671 | image processing obeys this flag (PDF::Builder does B<not>.). For example, if | |
1672 | you take a "portrait" (tall) photo of a tree (with the phone held vertically), | |
1673 | and then use it in a PDF, the tree may appear to have been cut down! (appears | |
1674 | in landscape mode) | |
1675 | ||
1676 | I have found some code that should allow the C<image_jpeg> or C<image> routine | |
1677 | to auto-rotate to (supposedly) the correct orientation, by looking for the Exif | |
1678 | metadata "Orientation" tag in the file. However, three problems arise: | |
1679 | B<1)> if a photo has been edited, and rotated or flipped in the process, there is no guarantee that the Orientation tag has been corrected. | |
1680 | B<2)> more than one Orientation tag may exist (e.g., in the binary APP1/Exif header, I<and> in XML data), and they may not agree with each other -- which should be used? | |
1681 | B<3)> the code would need to uncompress the raster data, swap and/or transpose rows and/or columns, and recompress the raster data for inclusion into the PDF. This is costly and error-prone. | |
1682 | In any case, the user would need to be able to override any auto-rotate function. | |
1683 | ||
1684 | For the time being, PDF::Builder will simply leave it up to the user of the | |
1685 | library to take care of rotating and/or flipping an image which displays | |
1686 | incorrectly. It is possible that we will consider adding some sort of query or warning that the image appears to I<not> be "normally" oriented (Orientation value 1 or "Top-left"), according to the Orientation flag. You can consider either (re-)saving the photo in an editor such as PhotoShop or GIMP, or using PDF::Builder code similar to the following (for images rotated 180 degrees): | |
1687 | ||
1688 | $pW = 612; $pH = 792; # page dimensions (US Letter) | |
1689 | my $img = $pdf->image_jpeg("AliceLake.jpeg"); | |
1690 | # raw size WxH 4032x3024, scaled down to 504x378 | |
1691 | $sW = 4032/8; $sH = 3024/8; | |
1692 | # intent is to center on US Letter sized page (LL at 54,207) | |
1693 | # Orientation flag on this image is 3 (rotated 180 degrees). | |
1694 | # if naively displayed (just $gfx->image call), it will be upside down | |
1695 | ||
1696 | $gfx->save(); | |
1697 | ||
1698 | ## method 0: simple display, is rotated 180 degrees! | |
1699 | #$gfx->image($img, ($pW-$sW)/2,($pH-$sH)/2, $sW,$sH); | |
1700 | ||
1701 | ## method 1: translate, then rotate | |
1702 | #$gfx->translate($pW,$pH); # to new origin (media UR corner) | |
1703 | #$gfx->rotate(180); # rotate around new origin | |
1704 | #$gfx->image($img, ($pW-$sW)/2,($pH-$sH)/2, $sW,$sH); | |
1705 | # image's UR corner, not LL | |
1706 | ||
1707 | # method 2: rotate, then translate | |
1708 | $gfx->rotate(180); # rotate around current origin | |
1709 | $gfx->translate(-$sW,-$sH); # translate in rotated coordinates | |
1710 | $gfx->image($img, -($pW-$sW)/2,-($pH-$sH)/2, $sW,$sH); | |
1711 | # image's UR corner, not LL | |
1712 | ||
1713 | ## method 3: flip (mirror) twice | |
1714 | #$scale = 1; # not rescaling here | |
1715 | #$size_page = $pH/$scale; | |
1716 | #$invScale = 1.0/$scale; | |
1717 | #$gfx->add("-$invScale 0 0 -$invScale 0 $size_page cm"); | |
1718 | #$gfx->image($img, -($pW-$sW)/2-$sW,($pH-$sH)/2, $sW,$sH); | |
1719 | ||
1720 | $gfx->restore(); | |
1721 | ||
1722 | If your image is also mirrored (flipped about an axis), simple rotation will | |
1723 | not suffice. You could do something with a reversal of the coordinate system, as in "method 3" above (see L<PDF::Builder::Content/Advanced Methods>). To mirror only left/right, the second C<$invScale> would be positive; to mirror only top/bottom, the first would be positive. If all else fails, you could save a mirrored copy in a photo editor. | |
1724 | 90 or 270 degree rotations will require a C<rotate> call, possibly with "cm" usage to reverse mirroring. | |
1725 | Incidentally, do not confuse this issue with the coordinate flipping performed | |
1726 | by some Chrome browsers when printing a page to PDF. | |
1727 | ||
1728 | Note that TIFF images may have the same rotation/mirroring problems as JPEG, | |
1729 | which is not surprising, as the Exif format was lifted from TIFF for use in | |
1730 | JPEG. The cure will be similar to JPEG's. | |
1731 | ||
1619 | 1732 | =head3 TIFF Images |
1620 | 1733 | |
1621 | 1734 | Note that the Graphics::TIFF support library does B<not> currently permit a |
1712 | 1825 | |
1713 | 1826 | =back |
1714 | 1827 | |
1715 | =head2 Using Shaper | |
1828 | =head2 USING SHAPER (HarfBuzz::Shaper library) | |
1716 | 1829 | |
1717 | 1830 | # if HarfBuzz::Shaper is not installed, either bail out, or try to |
1718 | 1831 | # use regular TTF calls instead |
2 | 2 | use strict; |
3 | 3 | no warnings qw[ deprecated recursion uninitialized ]; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | BEGIN { |
2 | 2 | # PDF::Builder::Matrix |
3 | 3 | # Original Copyright 1995-96 Ulrich Pfeifer. |
4 | 4 | # modified by Alfred Reibenschuh <areibens@cpan.org> for PDF::API2 |
5 | # | |
6 | # This library is free software; you can redistribute it | |
7 | # and/or modify it under the same terms as Perl itself. | |
5 | # rewritten by Steve Simms <steve@deefs.net> and licensed under the same | |
6 | # terms as the rest of PDF::API2 | |
8 | 7 | # |
9 | 8 | #======================================================================= |
10 | 9 | package PDF::Builder::Matrix; |
11 | 10 | |
12 | 11 | use strict; |
13 | 12 | use warnings; |
13 | use Carp; | |
14 | 14 | |
15 | our $VERSION = '3.019'; # VERSION | |
16 | my $LAST_UPDATE = '3.011'; # manually update whenever code is changed | |
15 | our $VERSION = '3.020'; # VERSION | |
16 | my $LAST_UPDATE = '3.020'; # manually update whenever code is changed | |
17 | 17 | |
18 | 18 | =head1 NAME |
19 | 19 | |
22 | 22 | =cut |
23 | 23 | |
24 | 24 | sub new { |
25 | my $type = shift; | |
25 | my $type = shift(); | |
26 | ||
26 | 27 | my $self = []; |
27 | my $len = scalar(@{$_[0]}); | |
28 | for (@_) { | |
29 | return if scalar(@{$_}) != $len; | |
30 | push(@{$self}, [@{$_}]); | |
28 | my $col_count = scalar(@{$_[0]}); | |
29 | foreach my $row (@_) { | |
30 | unless (scalar(@$row) == $col_count) { | |
31 | carp 'Inconsistent column count in matrix'; | |
32 | return; | |
33 | } | |
34 | push(@{$self}, [@$row]); | |
31 | 35 | } |
32 | bless $self, $type; | |
33 | return $self; | |
36 | ||
37 | return bless($self, $type); | |
34 | 38 | } |
35 | 39 | |
36 | 40 | # internal routine |
37 | 41 | sub transpose { |
38 | my $self = shift; | |
42 | my $self = shift(); | |
43 | ||
39 | 44 | my @result; |
40 | 45 | my $m; |
41 | 46 | |
42 | 47 | for my $col (@{$self->[0]}) { |
43 | 48 | push @result, []; |
44 | 49 | } |
45 | for my $row (@{$self}) { | |
50 | for my $row (@$self) { | |
46 | 51 | $m = 0; |
47 | for my $col (@{$row}) { | |
48 | push(@{$result[$m++]}, $col); | |
52 | for my $col (@$row) { | |
53 | push @{$result[$m++]}, $col; | |
49 | 54 | } |
50 | 55 | } |
56 | ||
51 | 57 | return PDF::Builder::Matrix->new(@result); |
52 | 58 | } |
53 | 59 | |
54 | 60 | # internal routine |
55 | sub vekpro { | |
61 | sub vector_product { | |
56 | 62 | my ($a, $b) = @_; |
57 | 63 | my $result = 0; |
58 | 64 | |
59 | 65 | for my $i (0 .. $#{$a}) { |
60 | 66 | $result += $a->[$i] * $b->[$i]; |
61 | 67 | } |
68 | ||
62 | 69 | return $result; |
63 | 70 | } |
64 | 71 | |
65 | 72 | # used by Content.pm |
66 | 73 | sub multiply { |
67 | my $self = shift; | |
74 | my $self = shift(); | |
68 | 75 | my $other = shift->transpose(); |
76 | ||
69 | 77 | my @result; |
70 | my $m; | |
71 | 78 | |
72 | return if $#{$self->[0]} != $#{$other->[0]}; | |
73 | for my $row (@{$self}) { | |
74 | my $rescol = []; | |
75 | for my $col (@{$other}) { | |
76 | push(@{$rescol}, vekpro($row,$col)); | |
79 | unless ($#{$self->[0]} == $#{$other->[0]}) { | |
80 | carp 'Mismatched dimensions in matrix multiplication'; | |
81 | return; | |
82 | } | |
83 | for my $row (@$self) { | |
84 | my $result_col = []; | |
85 | for my $col (@$other) { | |
86 | push @$result_col, vector_product($row,$col); | |
77 | 87 | } |
78 | push(@result, $rescol); | |
88 | push @result, $result_col; | |
79 | 89 | } |
90 | ||
80 | 91 | return PDF::Builder::Matrix->new(@result); |
81 | 92 | } |
82 | 93 |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
8 | my $LAST_UPDATE = '3.017'; # manually update whenever code is changed | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | my $LAST_UPDATE = '3.020'; # manually update whenever code is changed | |
9 | 9 | |
10 | 10 | # TBD: do -rect and -border apply to Named Destinations (link, url, file)? |
11 | 11 | # There is nothing to implement these options. Perhaps the code was copied |
114 | 114 | Defines the destination as a PDF-file with filepath C<$pdffile>, on page |
115 | 115 | C<$pagenum>, and options %opts (same as dest()). |
116 | 116 | |
117 | The old name, I<pdfile>, is still available but is B<deprecated> and will be | |
118 | removed at some time in the future. | |
119 | ||
120 | =cut | |
121 | ||
122 | # to be removed no earlier than October, 2020 | |
123 | sub pdfile { | |
124 | my ($self, $url, $pnum, %opts) = @_; | |
125 | warn "use pdf_file() method instead of pdfile()"; | |
126 | return $self->pdf_file($url, $pnum, %opts); | |
127 | } | |
117 | =cut | |
128 | 118 | |
129 | 119 | sub pdf_file { |
130 | 120 | my ($self, $url, $pnum, %opts) = @_; |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
8 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | my $LAST_UPDATE = '3.020'; # manually update whenever code is changed | |
9 | 9 | |
10 | 10 | use Carp qw(croak); |
11 | 11 | use PDF::Builder::Basic::PDF::Utils; |
33 | 33 | $self->{'Prev'} = $prev if defined $prev; |
34 | 34 | $self->{' api'} = $api; |
35 | 35 | weaken $self->{' api'}; |
36 | weaken $self->{'Parent'} if defined $parent; | |
37 | weaken $self->{'Prev'} if defined $prev; | |
36 | 38 | |
37 | 39 | return $self; |
38 | 40 | } |
41 | 43 | sub parent { |
42 | 44 | my $self = shift(); |
43 | 45 | $self->{'Parent'} = shift() if defined $_[0]; |
46 | weaken $self->{'Parent'}; | |
44 | 47 | return $self->{'Parent'}; |
45 | 48 | } |
46 | 49 | |
48 | 51 | sub prev { |
49 | 52 | my $self = shift(); |
50 | 53 | $self->{'Prev'} = shift() if defined $_[0]; |
54 | weaken $self->{'Prev'}; | |
51 | 55 | return $self->{'Prev'}; |
52 | 56 | } |
53 | 57 | |
55 | 59 | sub next { |
56 | 60 | my $self = shift(); |
57 | 61 | $self->{'Next'} = shift() if defined $_[0]; |
62 | weaken $self->{'Next'}; | |
58 | 63 | return $self->{'Next'}; |
59 | 64 | } |
60 | 65 | |
64 | 69 | |
65 | 70 | $self->{'First'} = $self->{' children'}->[0] |
66 | 71 | if defined $self->{' children'} and defined $self->{' children'}->[0]; |
72 | weaken $self->{'First'}; | |
67 | 73 | return $self->{'First'}; |
68 | 74 | } |
69 | 75 | |
73 | 79 | |
74 | 80 | $self->{'Last'} = $self->{' children'}->[-1] |
75 | 81 | if defined $self->{' children'} and defined $self->{' children'}->[-1]; |
82 | weaken $self->{'Last'}; | |
76 | 83 | return $self->{'Last'}; |
77 | 84 | } |
78 | 85 | |
290 | 297 | C<$pdffile>, on page C<$pagenum> (default 0), and position C<%position> |
291 | 298 | (same as dest()). |
292 | 299 | |
293 | The old name, I<pdfile>, is still available but is B<deprecated> and will be | |
294 | removed at some time in the future. | |
295 | ||
296 | =cut | |
297 | ||
298 | # to be removed no earlier than October, 2020 | |
299 | sub pdfile { | |
300 | my ($self, $file, $page_number, %position) = @_; | |
301 | warn "use pdf_file() method instead of pdfile()"; | |
302 | return $self->pdf_file($file, $page_number, %position); | |
303 | } | |
300 | =cut | |
304 | 301 | |
305 | 302 | sub pdf_file { |
306 | 303 | my ($self, $file, $page_number, %position) = @_; |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '2.029'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Basic::PDF::Utils; |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.017'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use POSIX qw(floor); |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use Compress::Zlib; |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Util; |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use Carp; |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Basic::PDF::Utils; |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use Encode qw(:all); |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.010'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Basic::PDF::Utils; |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.004'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Basic::PDF::Utils; |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.004'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Basic::PDF::Utils; |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.004'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | =head1 NAME |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.004'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Basic::PDF::Utils; |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.004'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Basic::PDF::Utils; |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Basic::PDF::Utils; |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '2.029'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.017'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Basic::PDF::Utils; |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.017'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Util; |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.013'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.018'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '2.029'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '2.029'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.017'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use File::Basename; |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.017'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use Encode qw(:all); |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.017'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use Math::Trig; # CAUTION: deg2rad(0) = deg2rad(360) = 0! |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.006'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use Encode qw(:all); |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.013'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
6 | my $LAST_UPDATE = '3.017'; # manually update whenever code is changed | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | my $LAST_UPDATE = '3.020'; # manually update whenever code is changed | |
7 | 7 | |
8 | 8 | =head1 NAME |
9 | 9 | |
10 | 10 | PDF::Builder::Resource::PaperSizes - list of standard paper sizes and their dimensions |
11 | 11 | |
12 | =head2 Information and Usage | |
13 | ||
14 | This is a list of standard page (media) sizes by I<name> (e.g., 'A4' or | |
15 | 'Legal'), given by width and height in Big Points (72 per inch). See the code | |
16 | in PaperSizes.pm for the actual entries. You do B<not> have to use these names; | |
17 | they are merely provided as convenient shortcuts. You can always specify the | |
18 | desired dimensions (in points) yourself. | |
19 | ||
20 | The PDF specification (and PDF readers) default to US Letter size (portrait | |
21 | orientation, 8.5 inches wide by 11 inches high). If you want to use anything | |
22 | else, you will have to make a C<mediabox()> call to specify the media (paper) | |
23 | size. For named sizes, capitalization doesn't matter (all entries are folded | |
24 | to lower case, so 'A4' and 'a4' work the same). | |
25 | ||
26 | Different sources give somewhat different paper dimensions, especially for | |
27 | archaic or unusual sizes, so take care and measure your actual paper before | |
28 | printing, so you can avoid wasting paper and time printing to the wrong | |
29 | mediabox! Also keep in mind that many printers cannot print all the way to the | |
30 | edge (don't want to get ink or toner on the paper rollers), so set your margins | |
31 | accordingly. | |
32 | ||
12 | 33 | =cut |
13 | 34 | |
35 | # see sites such as https://www.papersizes.org/ for all the paper size | |
36 | # information you would ever want to know | |
37 | # http://tug.ctan.org/macros/latex/contrib/memoir/memman.pdf pg 39 | |
38 | ||
14 | 39 | sub get_paper_sizes { |
15 | # dimensions are in Big Points | |
40 | ||
41 | # dimensions are Width and Height in Big Points. divide by 72 to get | |
42 | # inches, or divide by 2.83 (72/25.4) to get mm. use page and coordinate | |
43 | # rotations to rotate into landscape mode and vice-versa. | |
44 | ||
16 | 45 | return ( |
17 | 46 | # Metric sizes |
18 | 47 | # non-standard names 4a, 2a, 4b, 2b have been removed |
26 | 55 | 'a4' => [ 595, 842 ], |
27 | 56 | 'a5' => [ 421, 595 ], |
28 | 57 | 'a6' => [ 297, 421 ], |
58 | 'a7' => [ 210, 297 ], | |
59 | 'a8' => [ 147, 210 ], | |
60 | 'a9' => [ 105, 147 ], | |
61 | 'a10' => [ 74, 105 ], | |
29 | 62 | '4b0' => [ 5656, 8000 ], |
30 | 63 | '2b0' => [ 4000, 5656 ], |
31 | 64 | 'b0' => [ 2828, 4000 ], |
35 | 68 | 'b4' => [ 707, 1000 ], |
36 | 69 | 'b5' => [ 500, 707 ], |
37 | 70 | 'b6' => [ 353, 500 ], |
38 | 'b7' => [ 250, 500 ], | |
71 | 'b7' => [ 250, 353 ], | |
39 | 72 | 'b8' => [ 176, 250 ], |
40 | 73 | 'b9' => [ 125, 176 ], |
41 | 74 | 'b10' => [ 88, 125 ], |
42 | 'c0' => [ 2600, 3677 ], | |
75 | 'c0' => [ 2600, 3677 ], # C series envelopes | |
43 | 76 | 'c1' => [ 1837, 2600 ], |
44 | 77 | 'c2' => [ 1298, 1837 ], |
45 | 78 | 'c3' => [ 918, 1298 ], |
70 | 103 | 'p6' => [ 303, 397 ], |
71 | 104 | |
72 | 105 | # mixed |
73 | 'universal' => [ 595, 792 ], # smaller of A4 and Letter | |
106 | 'universal' => [ 595, 792 ], # smaller of A4 and US Letter, | |
107 | # will print on either paper size | |
74 | 108 | |
75 | 109 | # US sizes |
76 | 'broadsheet' => [ 1296, 1584 ], | |
110 | 'broadsheet' => [ 1296, 1584 ], # varies, sometimes 1224 x 1584 | |
77 | 111 | 'executive' => [ 522, 756 ], |
78 | 'foolscap' => [ 576, 936 ], | |
112 | 'foolscap' => [ 576, 936 ], # also listed as 360x486 | |
79 | 113 | 'gov-legal' => [ 612, 936 ], |
80 | 114 | 'gov-letter' => [ 576, 756 ], |
81 | 'jr-legal' => [ 576, 360 ], | |
115 | 'jr-legal' => [ 360, 576 ], | |
82 | 116 | 'ledger' => [ 1224, 792 ], # = tabloid in landscape orientation |
83 | 117 | 'legal' => [ 612, 1008 ], |
84 | 118 | 'letter' => [ 612, 792 ], |
87 | 121 | 'student' => [ 396, 612 ], |
88 | 122 | 'tabloid' => [ 792, 1224 ], |
89 | 123 | '36x36' => [ 2592, 2592 ], |
124 | 'dbill' => [ 216, 504 ], | |
125 | 'statement' => [ 396, 612 ], # = student | |
126 | 'old-paper' => [ 648, 864 ], | |
127 | 'half-letter' => [ 396, 612 ], # = student | |
90 | 128 | 'env-10' => [ 297, 684 ], |
91 | 129 | 'env-monarch' => [ 279, 540 ], |
92 | 130 | 'a' => [ 612, 791 ], # ANSI technical drawing paper |
95 | 133 | 'd' => [ 1585, 2449 ], |
96 | 134 | 'e' => [ 2449, 3169 ], |
97 | 135 | 'f' => [ 2016, 2880 ], |
136 | 'b-plus' => [ 936, 1368 ], # aka super-B, A3+, super-A3 | |
137 | 'arch-a' => [ 648, 864 ], | |
138 | 'arch-b' => [ 864, 1296 ], | |
139 | 'arch-c' => [ 1296, 1728 ], | |
140 | 'arch-d' => [ 1728, 2592 ], | |
141 | 'arch-e' => [ 2592, 3456 ], | |
142 | 'arch-e1' => [ 2160, 3024 ], | |
143 | 'pott' => [ 288, 450 ], # British sizes | |
144 | 'post' => [ 360, 576 ], | |
145 | 'large-post' => [ 378, 594 ], | |
146 | 'crown' => [ 360, 540 ], | |
147 | 'large-crown' => [ 378, 576 ], | |
148 | 'demy' => [ 409, 630 ], | |
149 | 'small-demy' => [ 409, 612 ], | |
150 | 'medium' => [ 414, 648 ], | |
151 | 'royal' => [ 450, 720 ], | |
152 | 'small-royal' => [ 445, 666 ], | |
153 | 'super-royal' => [ 486, 738 ], | |
154 | 'imperial' => [ 540, 792 ], | |
98 | 155 | ); |
99 | 156 | } |
100 | 157 |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '2.031'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | =head1 NAME |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '2.029'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | use Carp; |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '2.029'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | =head1 NAME |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.004'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | =head1 NAME |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.017'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | =head1 NAME |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.004'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | =head1 NAME |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.004'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | =head1 NAME |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.010'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Util; |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Basic::PDF::Dict; |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '2.031'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Basic::PDF::Utils; |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.004'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Util; |
4 | 4 | use strict; |
5 | 5 | no warnings qw[ deprecated recursion uninitialized ]; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.013'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use IO::File; |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.017'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use IO::File; |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use Compress::Zlib; |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
8 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | my $LAST_UPDATE = '3.020'; # manually update whenever code is changed | |
9 | 9 | |
10 | 10 | use Compress::Zlib; |
11 | 11 | use POSIX qw(ceil floor); |
247 | 247 | |
248 | 248 | # transfer over the unpacked (uncompressed, unfiltered) data rows (IDAT) |
249 | 249 | # to self->{' stream'}. stream is already initialized to empty |
250 | my $rows = $png->get_rows(); | |
251 | for (my $row = 0; $row < @{$rows}; $row++) { | |
252 | $self->{' stream'} .= $rows->[$row]; | |
250 | # note that this loop is eliminated for RGBA and GA, as they | |
251 | # are read directly from $png via split_alpha | |
252 | if ($cs != PNG_COLOR_TYPE_GRAY_ALPHA && | |
253 | $cs != PNG_COLOR_TYPE_RGB_ALPHA) { | |
254 | my $rows = $png->get_rows(); | |
255 | for (my $row = 0; $row < @{$rows}; $row++) { | |
256 | $self->{' stream'} .= $rows->[$row]; | |
257 | } | |
253 | 258 | } |
254 | 259 | |
255 | 260 | $self->width($w); |
448 | 453 | # (1 or 2 bytes) to self->stream, and the second half (1 or |
449 | 454 | # 2 bytes) into dict->stream as the Alpha SMask. delete |
450 | 455 | # leftover self->stream. |
451 | my $clearstream = $self->{' stream'}; # s/b uncompressed, unfiltered | |
452 | 456 | delete $self->{' nofilt'}; |
453 | #delete $self->{' stream'}; # will reduce size 50% when Alpha removed | |
454 | 457 | $dict->{' stream'} = ''; |
455 | 458 | $self->{' stream'} = ''; |
456 | # TBD: the following pixel-by-pixel manipulation is SLOW as | |
457 | # molasses, but I haven't found anything faster. pack(unpack(..)) | |
458 | # is about 3x slower, and self->stream .= doesn't work (corrupts). | |
459 | # have requested that it be built into libpng.a. | |
460 | foreach my $n (0 .. $h*$w-1) { | |
461 | # pull out Alpha from pixel into separate Mask area | |
462 | vec($dict->{' stream'}, $n, $bpc) = vec($clearstream, $n*2+1, $bpc); | |
463 | # consolidate remaining 1 sample into self->stream | |
464 | vec($self->{' stream'}, $n, $bpc) = vec($clearstream, $n*2, $bpc); | |
465 | } | |
466 | } | |
459 | # high-speed splitting out of alpha channel | |
460 | my $split = split_alpha($png); | |
461 | $self->{' stream'} = $split->{'data'}; | |
462 | $dict->{' stream'} = $split->{'alpha'}; | |
463 | } | |
467 | 464 | # compress all but short streams |
468 | 465 | if (length($self->{' stream'}) > 32) { |
469 | 466 | $self->{' stream'} = Compress::Zlib::compress($self->{' stream'}); |
522 | 519 | # (1 or 2 bytes) to dict->stream as the Alpha SMask, and the |
523 | 520 | # first 3/4 (3 * 1 or 2 bytes) into self->stream as the image. |
524 | 521 | # delete leftover self->stream. |
525 | my $clearstream = $self->{' stream'}; # s/b uncompressed, unfiltered | |
526 | 522 | delete $self->{' nofilt'}; |
527 | #delete $self->{' stream'}; # will reduce size 25% when Alpha removed | |
528 | 523 | $dict->{' stream'} = ''; |
529 | 524 | $self->{' stream'} = ''; |
530 | # TBD: the following pixel-by-pixel manipulation is SLOW as | |
531 | # molasses, but I haven't found anything faster. pack(unpack(..)) | |
532 | # is about 3x slower, and self->stream .= doesn't work (corrupts). | |
533 | # have requested that it be built into libpng.a. | |
534 | foreach my $n (0 .. $h*$w-1) { | |
535 | # pull out Alpha from pixel into separate Mask area | |
536 | vec($dict->{' stream'}, $n, $bpc) = vec($clearstream, $n*4+3, $bpc); | |
537 | # close up remaining 3 samples into self->stream | |
538 | vec($self->{' stream'}, $n*3, $bpc) = vec($clearstream, $n*4, $bpc); | |
539 | vec($self->{' stream'}, $n*3+1, $bpc) = vec($clearstream, $n*4+1, $bpc); | |
540 | vec($self->{' stream'}, $n*3+2, $bpc) = vec($clearstream, $n*4+2, $bpc); | |
541 | } | |
525 | # high-speed splitting out of alpha channel | |
526 | my $split = split_alpha($png); | |
527 | $self->{' stream'} = $split->{'data'}; | |
528 | $dict->{' stream'} = $split->{'alpha'}; | |
542 | 529 | } |
543 | 530 | # compress all but short streams |
544 | 531 | if (length($self->{' stream'}) > 32) { |
0 | 0 | package PDF::Builder::Resource::XObject::Image::PNM; |
1 | 1 | |
2 | # For spec details, see man pages pam(5), pbm(5), pgm(5), pnm(5), | |
3 | # ppm(5), which were pasted into the __END__ of this file in an | |
4 | # earlier revision. | |
2 | # For spec details, see man pages pam(5), pbm(5), pgm(5), pnm(5), ppm(5) | |
5 | 3 | |
6 | 4 | use base 'PDF::Builder::Resource::XObject::Image'; |
7 | 5 | |
8 | 6 | use strict; |
9 | 7 | no warnings qw[ deprecated recursion uninitialized ]; |
10 | 8 | |
11 | our $VERSION = '3.019'; # VERSION | |
12 | my $LAST_UPDATE = '3.019'; # manually update whenever code is changed | |
9 | our $VERSION = '3.020'; # VERSION | |
10 | my $LAST_UPDATE = '3.020'; # manually update whenever code is changed | |
13 | 11 | |
14 | 12 | use IO::File; |
15 | 13 | use PDF::Builder::Util; |
20 | 18 | |
21 | 19 | PDF::Builder::Resource::XObject::Image::PNM - support routines for PNM (Portable aNy Map) image library. Inherits from L<PDF::Builder::Resource::XObject::Image> |
22 | 20 | |
21 | =head2 METHODS | |
22 | ||
23 | =over | |
24 | ||
25 | =new($pdf, $file, $name) | |
26 | ||
27 | Returns an image in the PDF. PNM types 1 (ASCII/plain bi-level/PBM), | |
28 | 2 (ASCII/plain grayscale/PGM), 3 (ASCII/plain RGB/PPM), | |
29 | 4 (binary/raw bi-level/PBM), 5 (binary/raw grayscale/PGM), and | |
30 | 6 (binary/raw RGB/PPM) are supported. | |
31 | ||
32 | For bi-level, only values 0/1 (white/black) are supported. For grayscale, the | |
33 | maximum sample (full white) may be anything from 1 to 65535, with 0 being full | |
34 | black. If the maximum sample value is 255 or smaller, one byte of raw binary | |
35 | data per pixel, otherwise two bytes. For RGB, each sample (full-on of that | |
36 | color) may be anything from 1 to 65535 (the same maximum for all three colors), | |
37 | with 0 being full black. If the maximum sample value is 255 or smaller, three | |
38 | bytes of raw binary data per pixel, otherwise six bytes. | |
39 | ||
40 | =back | |
41 | ||
23 | 42 | =cut |
24 | 43 | |
44 | # ------------------------------------------------------------------- | |
25 | 45 | sub new { |
26 | 46 | my ($class, $pdf, $file, $name) = @_; |
27 | 47 | |
40 | 60 | return $self; |
41 | 61 | } |
42 | 62 | |
63 | # ------------------------------------------------------------------- | |
43 | 64 | # READPPMHEADER |
44 | 65 | # taken from Image::PBMLib |
45 | 66 | # Copyright by Benjamin Elijah Griffin (28 Feb 2003) |
67 | # extensively modified by Phil M Perry, copyright 2020 | |
46 | 68 | # |
47 | 69 | sub readppmheader { |
48 | my $gr = shift; # input file glob ref | |
49 | my $in = ''; | |
50 | my $no_comments; | |
51 | my %info; | |
52 | my $rc; | |
53 | $info{'error'} = undef; | |
54 | ||
55 | $rc = read($gr, $in, 3); | |
56 | ||
57 | if (!defined($rc) || $rc != 3) { | |
58 | $info{'error'} = 'Read error or EOF'; | |
59 | return \%info; | |
60 | } | |
61 | ||
62 | if ($in =~ /^P([1-6])\s/) { | |
63 | $info{'type'} = $1; | |
64 | if ($info{'type'} > 3) { | |
65 | $info{'raw'} = 1; | |
70 | my ($gr, $buffer) = @_; # already-opened input file's filehandle | |
71 | my %info; | |
72 | $info{'error'} = undef; | |
73 | my ($width, $height, $max, $comment, $content); | |
74 | ||
75 | # extension: allow whitespace BEFORE the magic number (usually none) | |
76 | # read Px magic number | |
77 | ($buffer, $comment) = eat_whitespace($gr, $buffer, 0); | |
78 | ($buffer, $content) = read_content($gr, $buffer); | |
79 | ||
80 | if (length($content) != 2) { | |
81 | $info{'error'} = 'Read error or EOF'; | |
82 | return (\%info, $buffer); | |
83 | } | |
84 | ||
85 | if ($content =~ /^P([1-6])/) { | |
86 | $info{'type'} = $1; | |
87 | if ($info{'type'} > 3) { | |
88 | $info{'raw'} = 1; # P4-6 is raw (binary) | |
89 | } else { | |
90 | $info{'raw'} = 0; # P1-3 is plain (ASCII) | |
91 | } | |
66 | 92 | } else { |
67 | $info{'raw'} = 0; | |
93 | $info{'error'} = 'Unrecognized magic number, not 1..6'; | |
94 | return (\%info, $buffer); | |
68 | 95 | } |
69 | 96 | |
70 | 97 | if ($info{'type'} == 1 or $info{'type'} == 4) { |
71 | $info{'max'} = 1; | |
72 | $info{'bgp'} = 'b'; | |
98 | $max = 1; | |
99 | $info{'bgp'} = 'b'; | |
73 | 100 | } elsif ($info{'type'} == 2 or $info{'type'} == 5) { |
74 | $info{'bgp'} = 'g'; | |
101 | # need to read and validate 'max' | |
102 | $info{'bgp'} = 'g'; | |
103 | } else { # 3 or 6 | |
104 | # need to read and validate 'max' | |
105 | $info{'bgp'} = 'p'; | |
106 | } | |
107 | ||
108 | # expect width as unsigned integer > 0 | |
109 | ($buffer, $comment) = eat_whitespace($gr, $buffer, 0); | |
110 | ($buffer, $content) = read_content($gr, $buffer); | |
111 | if (length($content) == 0) { | |
112 | $info{'error'} = 'Read error or EOF on width'; | |
113 | return (\%info, $buffer); | |
114 | } | |
115 | if ($content =~ m/(^\d+)$/) { | |
116 | $width = $1; | |
75 | 117 | } else { |
76 | $info{'bgp'} = 'p'; | |
77 | } | |
78 | ||
118 | $info{'error'} = 'Invalid width value '.$1; | |
119 | return (\%info, $buffer); | |
120 | } | |
121 | if ($width < 1) { | |
122 | $info{'error'} = 'Invalid width value '.$width; | |
123 | return (\%info, $buffer); | |
124 | } | |
125 | ||
126 | # expect height as unsigned integer > 0 | |
127 | ($buffer, $comment) = eat_whitespace($gr, $buffer, 0); | |
128 | ($buffer, $content) = read_content($gr, $buffer); | |
129 | if (length($content) == 0) { | |
130 | $info{'error'} = 'Read error or EOF on height'; | |
131 | return (\%info, $buffer); | |
132 | } | |
133 | if ($content =~ m/(^\d+)$/) { | |
134 | $height = $1; | |
135 | } else { | |
136 | $info{'error'} = 'Invalid height value '.$1; | |
137 | return (\%info, $buffer); | |
138 | } | |
139 | if ($height < 1) { | |
140 | $info{'error'} = 'Invalid height value '.$height; | |
141 | return (\%info, $buffer); | |
142 | } | |
143 | ||
144 | # expect max sample value as unsigned integer > 0 & < 65536 | |
145 | # IF grayscale or pixmap (RGB). already set to 1 for bi-level | |
146 | if ($info{'bgp'} =~ m/^[gp]$/) { | |
147 | ($buffer, $comment) = eat_whitespace($gr, $buffer, 0); | |
148 | ($buffer, $content) = read_content($gr, $buffer); | |
149 | if (length($content) == 0) { | |
150 | $info{'error'} = 'Read error or EOF on max'; | |
151 | return (\%info, $buffer); | |
152 | } | |
153 | if ($content =~ m/(^\d+)$/) { | |
154 | $max = $1; | |
155 | } else { | |
156 | $info{'error'} = 'Invalid max value '.$1; | |
157 | return (\%info, $buffer); | |
158 | } | |
159 | if ($max < 1 || $max > 65535) { | |
160 | $info{'error'} = 'Invalid max value '.$max; | |
161 | return (\%info, $buffer); | |
162 | } | |
163 | } | |
164 | ||
165 | $info{'width'} = $width; | |
166 | $info{'height'} = $height; | |
167 | $info{'max'} = $max; | |
168 | ||
169 | # for binary (raw) files, a single whitespace character should be seen. | |
170 | # for ASCII (plain) files, extend to allow arbitrary whitespace | |
171 | if ($info{'raw'}) { | |
172 | # The buffer should have a single ws char in it already, left over from | |
173 | # the previous content read. We don't want to read anything beyond that | |
174 | # in case a byte value happens to be a valid whitespace character! If | |
175 | # the file format is botched and there is additional whitespace, it | |
176 | # will unfortunately be read as binary data. | |
177 | if ($buffer =~ m/^\s/) { | |
178 | $buffer = substr($buffer, 1); # discard first character | |
179 | } else { | |
180 | $info{'error'} = 'Expected single whitespace before raster data'; | |
181 | return (\%info, $buffer); | |
182 | } | |
183 | } else { | |
184 | # As an extension, for plain (ASCII) format we allow arbitrary | |
185 | # whitespace (including comments) after the max value and before the | |
186 | # raster data, not just one whitespace. | |
187 | ($buffer, $comment) = eat_whitespace($gr, $buffer, 0); | |
188 | } | |
189 | ||
190 | return (\%info, $buffer); | |
191 | } # end of readppmheader() | |
192 | ||
193 | # ------------------------------------------------------------------- | |
194 | # eat and discard whitespace stream, but return any comment(s) found | |
195 | # within the header, cannot have an EOF during whitespace read | |
196 | sub eat_whitespace { | |
197 | my ($gr, $buffer, $qflag) = @_; | |
198 | # qflag = 0 if OK to read more from file (don't expect an EOF) | |
199 | # = 1 eating ws at end of image, might hit EOF here | |
200 | ||
201 | my ($count, $buf, @comment); | |
202 | # first see if enough material is already in the buffer. if not, read some | |
203 | my $in_comment = 0; # not currently processing a comment, just ws. | |
79 | 204 | while (1) { |
80 | $rc = read($gr, $in, 1, length($in)); | |
81 | if (!defined($rc) || $rc != 1) { | |
82 | $info{'error'} = 'Read error or EOF'; | |
83 | return \%info; | |
84 | } | |
85 | ||
86 | $no_comments = $in; | |
87 | $info{'comments'} = ''; | |
88 | while ($no_comments =~ /#.*\n/) { | |
89 | $no_comments =~ s/#(.*\n)/ /; | |
90 | $info{'comments'} .= $1; | |
91 | } | |
92 | ||
93 | if ($info{'bgp'} eq 'b') { | |
94 | if ($no_comments =~ /^P\d\s+(\d+)\s+(\d+)\s/) { | |
95 | $info{'width'} = $1; | |
96 | $info{'height'} = $2; | |
97 | last; | |
98 | } | |
99 | } else { | |
100 | if ($no_comments =~ /^P\d\s+(\d+)\s+(\d+)\s+(\d+)\s/) { | |
101 | $info{'width'} = $1; | |
102 | $info{'height'} = $2; | |
103 | $info{'max'} = $3; | |
104 | last; | |
105 | } | |
106 | } | |
107 | } # while reading header | |
108 | ||
109 | $info{'fullheader'} = $in; | |
110 | ||
111 | } else { | |
112 | $info{'error'} = 'Wrong magic number'; | |
113 | } | |
114 | ||
115 | return \%info; | |
116 | } | |
117 | ||
205 | # is buffer empty? if so, read some content | |
206 | if (length($buffer) == 0) { | |
207 | $count = read($gr, $buffer, 50); # chunk of up to 50 bytes (could be 0) | |
208 | if ($count == 0 && (!$qflag || $in_comment)) { | |
209 | # EOF or read error, is bad thing here | |
210 | print STDERR "EOF or read error reading whitespace.\n"; | |
211 | return ($buffer, ''); | |
212 | } | |
213 | } | |
214 | # if buffer is still empty (qflag == 1), will exit cleanly | |
215 | ||
216 | if (!$in_comment) { $buffer =~ s/^\s+//; } | |
217 | # a bunch of whitespace may have been discarded. if buffer now starts | |
218 | # with a #, it is a comment to be read to EOL. otherwise we're done. | |
219 | if (length($buffer) > 0) { | |
220 | # buffer still has stuff in it (starts with non-ws) | |
221 | if ($buffer =~ m/^#/) { | |
222 | $in_comment = 1; | |
223 | # at start of comment. discard up through \n | |
224 | # (\n might not yet be in buffer!) | |
225 | # special case: #\n | |
226 | if ($buffer =~ s/^#\n//) { | |
227 | # special empty case | |
228 | $in_comment = 0; | |
229 | } elsif ($buffer =~ s/^#\s*([^\n]*)\n//) { | |
230 | push @comment, $1; # has been removed from buffer | |
231 | $in_comment = 0; | |
232 | } else { | |
233 | # haven't gotten to end of comment (\n) yet | |
234 | $count = read($gr, $buf, 50); | |
235 | if ($count == 0) { | |
236 | # EOF or read error, is bad thing here | |
237 | print STDERR "EOF or read error reading whitespace in pixel data\n"; | |
238 | return ($buffer, ''); | |
239 | } | |
240 | $buffer .= $buf; | |
241 | next; | |
242 | } | |
243 | } else { | |
244 | # non-whitespace, not #. content to be left in buffer | |
245 | $in_comment = 0; | |
246 | last; | |
247 | } | |
248 | } else { | |
249 | # empty buffer, need to read some more | |
250 | if ($qflag && !$in_comment) { last; } | |
251 | next; | |
252 | } | |
253 | } # while(1) until run out of whitespace | |
254 | ||
255 | my $comments = ''; | |
256 | if (scalar(@comment) > 0) { $comments = join("\n", @comment); } | |
257 | return ($buffer, $comments); | |
258 | } # end of eat_whitespace() | |
259 | ||
260 | # ------------------------------------------------------------------- | |
261 | # eat a non-whitespace stream, returning the content up until whitespace | |
262 | # should not see an EOF during this (at least one ws after this stream) | |
263 | sub read_content { | |
264 | my ($gr, $buffer) = @_; | |
265 | ||
266 | my ($count, $content); | |
267 | $content = ''; | |
268 | # first see if enough material is already in the buffer. if not, read some | |
269 | while (1) { | |
270 | # is buffer empty? if so, read some content | |
271 | if (length($buffer) == 0) { | |
272 | $count = read($gr, $buffer, 50); # chunk of up to 50 bytes (could be 0) | |
273 | if ($count == 0) { | |
274 | # EOF or read error, is bad thing here | |
275 | print STDERR "EOF or read error reading content in pixel data\n"; | |
276 | return ($buffer, ''); | |
277 | } | |
278 | } | |
279 | ||
280 | # should always be non-ws content here | |
281 | $buffer =~ s/^([^\s]+)//; | |
282 | $content .= $1; # has been removed from buffer (now possibly empty) | |
283 | # if buffer now empty (didn't see ws char), need to read more | |
284 | if (length($buffer) == 0) { next; } | |
285 | last; # non-empty buffer means it starts with a ws char | |
286 | ||
287 | # this function is used for header fields and non-raw pixel data, so | |
288 | # we don't expect to have an EOF immediately after a data item (must | |
289 | # be a \n after it at the last data item). | |
290 | ||
291 | } # while(1) until run out of non-whitespace | |
292 | ||
293 | return ($buffer, $content); | |
294 | } # end of read_content() | |
295 | ||
296 | # ------------------------------------------------------------------- | |
118 | 297 | sub read_pnm { |
119 | 298 | my $self = shift; |
120 | 299 | my $pdf = shift; |
121 | 300 | my $file = shift; |
122 | 301 | |
123 | my ($buf, $t, $s, $line); | |
302 | my ($rc, $buf, $buf2, $s, $pix, $max); | |
303 | # $s is a scale factor for sample not full 8 or 16 bits. | |
304 | # it should scale the input to 0..255 or 0..65535, so final value | |
305 | # will be a full 8 or 16 bits per channel (bpc) | |
124 | 306 | my ($w,$h, $bpc, $cs, $img, @img) = (0,0, '', '', ''); |
307 | my ($info, $buffer, $content, $comment, $sample, $gr); | |
125 | 308 | my $inf; |
126 | 309 | if (ref($file)) { |
127 | 310 | $inf = $file; |
130 | 313 | } |
131 | 314 | binmode($inf,':raw'); |
132 | 315 | $inf->seek(0, 0); |
133 | my $info = readppmheader($inf); | |
134 | if ($info->{'type'} == 4) { | |
135 | $bpc = 1; | |
136 | read($inf, $self->{' stream'}, ($info->{'width'}*$info->{'height'}/8)); | |
316 | $buffer = ''; # initialize | |
317 | ($info, $buffer) = readppmheader($inf, $buffer); | |
318 | # info (hashref) fields: | |
319 | # error undef or an error description | |
320 | # type magic number 1-6 | |
321 | # raw 0 if plain/ASCII, 1 if raw/binary | |
322 | # bgp b=bi-level (1,4) g=grayscale (2,5), p=pixmap/RGB (3,6) | |
323 | # width width (row length/horizontal) in pixels | |
324 | # height height (row count/vertical) in pixels | |
325 | # max sample max value 1 for bi-level, 1-65535 for grayscale/RGB | |
326 | # comments comment line(s), if any (else '') | |
327 | if (defined $info->{'error'}) { | |
328 | print STDERR "Error reported during PNM file header read:\n".($info->{'error'}).".\n"; | |
329 | return $self; | |
330 | } | |
331 | ||
332 | $w = $info->{'width'}; | |
333 | $h = $info->{'height'}; | |
334 | $max = $info->{'max'}; | |
335 | ||
336 | my $bytes_per_sample = 1; | |
337 | if ($max > 255) { $bytes_per_sample = 2; } | |
338 | ||
339 | # ------------------------------ | |
340 | if ($info->{'type'} == 1) { | |
341 | # plain (ASCII) PBM bi-level, each pixel 0..1, ws between is optional | |
342 | ||
343 | $bpc = 1; # one bit per channel/sample/pixel | |
344 | # pack 8 pixels (possibly with don't-care at end of row) to a byte | |
345 | my ($row, $col, $bits); # need to handle rows separately for d/c bits | |
346 | my $qflag; | |
347 | $content = ''; | |
348 | for ($row = 0; $row < $h; $row++) { | |
349 | $bits = ''; | |
350 | for ($col = 0; $col < $w; $col++) { | |
351 | # could be a single 0 or 1, or a whole bunch lumped together | |
352 | # in one or more groups | |
353 | # buffer has 0 or more entries. handle just one in this loop, | |
354 | # reading in new buffer if necessary | |
355 | if (length($content) == 0) { | |
356 | $qflag = 0; | |
357 | if ($row == $h-1 && $col == $w-1) { $qflag = 1; } | |
358 | ($buffer, $comment) = eat_whitespace($inf, $buffer, $qflag); | |
359 | ($buffer, $content) = read_content($inf, $buffer); | |
360 | if (length($content) == 0) { | |
361 | print STDERR "Unexpected EOF or read error reading pixel data.\n"; | |
362 | return $self; | |
363 | } | |
364 | } | |
365 | $sample = substr($content, 0, 1); | |
366 | $content = substr($content, 1); | |
367 | if ($sample ne '0' && $sample ne '1') { | |
368 | print STDERR "Invalid bit value '$sample' in pixel data.\n"; | |
369 | return $self; | |
370 | } | |
371 | $bits .= $sample; | |
372 | if (length($bits) == 8) { | |
373 | $self->{' stream'} .= pack('B8', $bits); | |
374 | $bits = ''; | |
375 | } | |
376 | ||
377 | } # end of cols in row. partial $bits to finish? | |
378 | if ($bits ne '') { | |
379 | while (length($bits) < 8) { | |
380 | $bits .= '0'; # don't care, but must be 0 or 1 | |
381 | } | |
382 | $self->{' stream'} .= pack('B8', $bits); | |
383 | } | |
384 | } # end of rows | |
385 | ||
386 | $cs = 'DeviceGray'; # at 1 bit per pixel | |
387 | $self->{'Decode'} = PDFArray(PDFNum(1), PDFNum(0)); | |
388 | ||
389 | # ------------------------------ | |
390 | } elsif ($info->{'type'} == 2) { | |
391 | # plain (ASCII) PGM grayscale, each pixel 0..max (1 or 2 bytes) | |
392 | ||
393 | # get scale factor $s to fully fill 8 or 16 bit sample (channel) | |
394 | if ($max == 255 || $max == 65535) { | |
395 | $s = 0; # flag: no scaling | |
396 | } elsif ($max > 255) { | |
397 | $s = 65535/$max; | |
398 | } else { | |
399 | $s = 255/$max; | |
400 | } | |
401 | $bpc = 8 * $bytes_per_sample; | |
402 | my $format = 'C'; | |
403 | if ($bytes_per_sample == 2) { $format = 'S>'; } | |
404 | my $sample; | |
405 | ||
406 | for ($pix=($w*$h); $pix>0; $pix--) { | |
407 | ($buffer, $content) = read_content($inf, $buffer); | |
408 | if (length($content) == 0) { | |
409 | print STDERR "Unexpected EOF or read error reading pixel data.\n"; | |
410 | return $self; | |
411 | } | |
412 | ($buffer, $comment) = eat_whitespace($inf, $buffer, $pix==1); | |
413 | ||
414 | if ($content =~ m/^\d+$/) { | |
415 | if ($content > $max) { | |
416 | print STDERR "Pixel data entry '$content' higher than $max. Value changed to $max.\n"; | |
417 | $content = $max; | |
418 | } | |
419 | } else { | |
420 | print STDERR "Invalid pixel data entry '$content'.\n"; | |
421 | return $self; | |
422 | } | |
423 | $sample = $content; | |
424 | ||
425 | if ($s > 0) { | |
426 | # scaling needed | |
427 | $sample = int($sample*$s + 0.5); # must not exceed 255/65535 | |
428 | } | |
429 | $self->{' stream'} .= pack($format, $sample); | |
430 | } # loop through all pixels | |
137 | 431 | $cs = 'DeviceGray'; |
432 | ||
433 | # ------------------------------ | |
434 | } elsif ($info->{'type'} == 3) { | |
435 | # plain (ASCII) PPM rgb, each pixel 0..max for R, G, B (1 or 2 bytes) | |
436 | ||
437 | # get scale factor $s to fully fill 8 or 16 bit sample (channel) | |
438 | if ($max == 255 || $max == 65535) { | |
439 | $s = 0; # flag: no scaling | |
440 | } elsif ($max > 255) { | |
441 | $s = 65535/$max; | |
442 | } else { | |
443 | $s = 255/$max; | |
444 | } | |
445 | $bpc = 8 * $bytes_per_sample; | |
446 | my $format = 'C'; | |
447 | if ($bytes_per_sample == 2) { $format = 'S>'; } | |
448 | my ($sample, $rgb); | |
449 | ||
450 | for ($pix=($w*$h); $pix>0; $pix--) { | |
451 | for ($rgb = 0; $rgb < 3; $rgb++) { # R, G, and B values | |
452 | ($buffer, $comment) = eat_whitespace($inf, $buffer, $pix==1); | |
453 | ($buffer, $content) = read_content($inf, $buffer); | |
454 | if (length($content) == 0) { | |
455 | print STDERR "Unexpected EOF or read error reading pixel data.\n"; | |
456 | return $self; | |
457 | } | |
458 | ||
459 | if ($content =~ m/^\d+$/) { | |
460 | if ($content > $max) { | |
461 | # remember, $pix counts DOWN from w x h | |
462 | print STDERR "Pixel $pix data entry '$content' higher than $max. Value changed to $max.\n"; | |
463 | $content = $max; | |
464 | } | |
465 | } else { | |
466 | print STDERR "Invalid pixel data entry '$content'.\n"; | |
467 | return $self; | |
468 | } | |
469 | $sample = $content; | |
470 | ||
471 | if ($s > 0) { | |
472 | # scaling needed | |
473 | $sample = int($sample*$s + 0.5); # must not exceed 255/65535 | |
474 | } | |
475 | $self->{' stream'} .= pack($format, $sample); | |
476 | } # R G B loop | |
477 | } # loop through all pixels | |
478 | $cs = 'DeviceRGB'; | |
479 | ||
480 | # ------------------------------ | |
481 | } elsif ($info->{'type'} == 4) { | |
482 | # raw (binary) PBM bi-level, each pixel 0..1, row packed 8 pixel/byte | |
483 | $bpc = 1; # one bit per channel/sample/pixel | |
484 | # round up for don't care bits at end of row | |
485 | my $bytes = int(($w+7)/8) * $h; | |
486 | $bytes -= length($buffer); # some already read from file! | |
487 | $rc = read($inf, $buf2, $bytes); | |
488 | if ($rc != $bytes) { | |
489 | print STDERR "Unexpected EOF or read error while reading PNM binary pixel data.\n"; | |
490 | return $self; | |
491 | } | |
492 | $self->{' stream'} = $buffer.$buf2; | |
493 | $cs = 'DeviceGray'; # at 1 bit per pixel | |
138 | 494 | $self->{'Decode'} = PDFArray(PDFNum(1), PDFNum(0)); |
495 | ||
496 | # ------------------------------ | |
139 | 497 | } elsif ($info->{'type'} == 5) { |
140 | $buf .= <$inf>; | |
141 | if ($info->{'max'} == 255) { | |
142 | $s = 0; | |
143 | } else { | |
144 | $s = 255/$info->{'max'}; | |
145 | } | |
146 | $bpc = 8; | |
147 | if ($s > 0) { | |
148 | for ($line=($info->{'width'}*$info->{'height'}); $line>0; $line--) { | |
149 | read($inf, $buf, 1); | |
150 | $self->{' stream'} .= pack('C', (unpack('C', $buf)*$s)); | |
498 | # raw (binary) PGM grayscale, each pixel 0..max (1 or 2 bytes) | |
499 | ||
500 | # get scale factor $s to fully fill 8 or 16 bit sample (channel) | |
501 | if ($max == 255 || $max == 65535) { | |
502 | $s = 0; # flag: no scaling | |
503 | } elsif ($max > 255) { | |
504 | $s = 65535/$max; | |
505 | } else { | |
506 | $s = 255/$max; | |
507 | } | |
508 | $bpc = 8 * $bytes_per_sample; | |
509 | my $format = 'C'; | |
510 | if ($bytes_per_sample == 2) { $format = 'S>'; } | |
511 | my ($buf, $sample); | |
512 | ||
513 | my $bytes = $w * $h * $bytes_per_sample; | |
514 | $bytes -= length($buffer); # some already read from file! | |
515 | $rc = read($inf, $buf, $bytes); | |
516 | if ($rc != $bytes) { | |
517 | print STDERR "Unexpected EOF or read error reading pixel data.\n"; | |
518 | return $self; | |
519 | } | |
520 | $buf = $buffer . $buf; | |
521 | if ($s > 0) { | |
522 | # scaling needed | |
523 | for ($pix=($w*$h); $pix>0; $pix--) { | |
524 | $buf2 = substr($buf, 0, $bytes_per_sample); | |
525 | $buf = substr($buf, $bytes_per_sample); | |
526 | $sample = unpack($format, $buf2); | |
527 | $sample = int($sample*$s + 0.5); # must not exceed 255/65535 | |
528 | $self->{' stream'} .= pack($format, $sample); | |
151 | 529 | } |
152 | 530 | } else { |
153 | read($inf, $self->{' stream'}, $info->{'width'}*$info->{'height'}); | |
154 | # part of file was already read into $buf but not otherwise used | |
155 | $self->{' stream'} = $buf . $self->{' stream'}; | |
531 | # no scaling needed | |
532 | $self->{' stream'} = $buf; | |
156 | 533 | } |
157 | 534 | $cs = 'DeviceGray'; |
535 | ||
536 | # ------------------------------ | |
158 | 537 | } elsif ($info->{'type'} == 6) { |
159 | if ($info->{'max'} == 255) { | |
160 | $s = 0; | |
161 | } else { | |
162 | $s = 255/$info->{'max'}; | |
163 | } | |
164 | $bpc = 8; | |
165 | if ($s > 0) { | |
166 | for ($line=($info->{'width'}*$info->{'height'}); $line>0; $line--) { | |
167 | read($inf, $buf, 1); | |
168 | $self->{' stream'} .= pack('C', (unpack('C', $buf)*$s)); | |
169 | read($inf, $buf, 1); | |
170 | $self->{' stream'} .= pack('C', (unpack('C', $buf)*$s)); | |
171 | read($inf, $buf, 1); | |
172 | $self->{' stream'} .= pack('C', (unpack('C', $buf)*$s)); | |
538 | # raw (binary) PPM rgb, each pixel 0..max for R, G, B (3 or 6 bytes) | |
539 | ||
540 | # get scale factor $s to fully fill 8 or 16 bit sample (channel) | |
541 | if ($max == 255 || $max == 65535) { | |
542 | $s = 0; # flag: no scaling | |
543 | } elsif ($max > 255) { | |
544 | $s = 65535/$max; | |
545 | } else { | |
546 | $s = 255/$max; | |
547 | } | |
548 | $bpc = 8 * $bytes_per_sample; | |
549 | my $format = 'C'; | |
550 | if ($bytes_per_sample == 2) { $format = 'S>'; } | |
551 | my ($buf, $sample); | |
552 | ||
553 | my $bytes = $w * $h * $bytes_per_sample * 3; | |
554 | $bytes -= length($buffer); # some already read from file! | |
555 | $rc = read($inf, $buf, $bytes); | |
556 | if ($rc != $bytes) { | |
557 | print STDERR "Unexpected EOF or read error reading pixel data.\n"; | |
558 | return $self; | |
559 | } | |
560 | $buf = $buffer . $buf; | |
561 | if ($s > 0) { | |
562 | # scaling needed | |
563 | for ($pix=($w*$h); $pix>0; $pix--) { | |
564 | # Red | |
565 | $buf2 = substr($buf, 0, $bytes_per_sample); | |
566 | $sample = unpack($format, $buf2); | |
567 | $sample = int($sample*$s + 0.5); # must not exceed 255/65535 | |
568 | $self->{' stream'} .= pack($format, $sample); | |
569 | # Green | |
570 | $buf2 = substr($buf, $bytes_per_sample, $bytes_per_sample); | |
571 | $sample = unpack($format, $buf2); | |
572 | $sample = int($sample*$s + 0.5); # must not exceed 255/65535 | |
573 | $self->{' stream'} .= pack($format, $sample); | |
574 | # Blue | |
575 | $buf2 = substr($buf, 2*$bytes_per_sample, $bytes_per_sample); | |
576 | $sample = unpack($format, $buf2); | |
577 | $sample = int($sample*$s + 0.5); # must not exceed 255/65535 | |
578 | $self->{' stream'} .= pack($format, $sample); | |
579 | ||
580 | $buf = substr($buf, $bytes_per_sample*3); | |
173 | 581 | } |
174 | 582 | } else { |
175 | read($inf, $self->{' stream'}, $info->{'width'}*$info->{'height'}*3); | |
583 | # no scaling needed | |
584 | $self->{' stream'} = $buf; | |
176 | 585 | } |
177 | 586 | $cs = 'DeviceRGB'; |
178 | 587 | } |
179 | 588 | close($inf); |
180 | 589 | |
181 | $self->width($info->{'width'}); | |
182 | $self->height($info->{'height'}); | |
590 | $self->width($w); | |
591 | $self->height($h); | |
183 | 592 | |
184 | 593 | $self->bits_per_component($bpc); |
185 | 594 | |
188 | 597 | $self->colorspace($cs); |
189 | 598 | |
190 | 599 | return $self; |
191 | } | |
600 | } # end of read_pnm() | |
192 | 601 | |
193 | 602 | 1; |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.001'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | use IO::File; |
2 | 2 | use strict; |
3 | 3 | use warnings; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
6 | my $LAST_UPDATE = '3.010'; # manually update whenever code is changed | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | my $LAST_UPDATE = '3.020'; # manually update whenever code is changed | |
7 | 7 | |
8 | 8 | use IO::File; |
9 | use Graphics::TIFF ':all'; # already confirmed to be installed | |
9 | use Graphics::TIFF 7 ':all'; # already confirmed to be installed | |
10 | 10 | |
11 | 11 | =head1 NAME |
12 | 12 | |
149 | 149 | # TIFFTAG_CELLWIDTH dithering or halftoning matrix cell width |
150 | 150 | # TIFFTAG_CELLLENGTH dithering or halftoning matrix cell height |
151 | 151 | # palette RGB table definition |
152 | $self->{'colorMapOffset'} = $self->{'object'}->GetField(TIFFTAG_COLORMAP); | |
153 | $self->{'colorMapSamples'} = $#{$self->{'colorMapOffset'}}+1; | |
154 | $self->{'colorMapLength'} = $self->{'colorMapSamples'}*2; # shorts! | |
152 | $self->{'colorMap'} = [ $self->{'object'}->GetField(TIFFTAG_COLORMAP) ]; | |
155 | 153 | # TIFFTAG_GRAYRESPONSEUNIT describe integer->float mapping |
156 | 154 | # TIFFTAG_GRAYRESPONSECURVE optical density of Gray curve at each point |
157 | 155 |
6 | 6 | |
7 | 7 | no warnings 'uninitialized'; |
8 | 8 | |
9 | our $VERSION = '3.019'; # VERSION | |
10 | my $LAST_UPDATE = '3.011'; # manually update whenever code is changed | |
9 | our $VERSION = '3.020'; # VERSION | |
10 | my $LAST_UPDATE = '3.020'; # manually update whenever code is changed | |
11 | 11 | |
12 | 12 | use Compress::Zlib; |
13 | 13 | |
251 | 251 | if ($tif->{'colorSpace'} eq 'Indexed') { |
252 | 252 | my $dict = PDFDict(); |
253 | 253 | $pdf->new_obj($dict); |
254 | $self->colorspace(PDFArray(PDFName($tif->{'colorSpace'}), PDFName('DeviceRGB'), PDFNum(255), $dict)); | |
254 | $self->colorspace(PDFArray(PDFName($tif->{'colorSpace'}), PDFName('DeviceRGB'), PDFNum(2**$tif->{'bitsPerSample'}-1), $dict)); | |
255 | 255 | $dict->{'Filter'} = PDFArray(PDFName('FlateDecode')); |
256 | 256 | $tif->{'fh'}->seek($tif->{'colorMapOffset'}, 0); |
257 | 257 | my $colormap; |
6 | 6 | |
7 | 7 | no warnings 'uninitialized'; |
8 | 8 | |
9 | our $VERSION = '3.019'; # VERSION | |
10 | my $LAST_UPDATE = '3.011'; # manually update whenever code is changed | |
9 | our $VERSION = '3.020'; # VERSION | |
10 | my $LAST_UPDATE = '3.020'; # manually update whenever code is changed | |
11 | 11 | |
12 | 12 | use Compress::Zlib; |
13 | 13 | |
15 | 15 | use PDF::Builder::Resource::XObject::Image::TIFF::File_GT; |
16 | 16 | use PDF::Builder::Util; |
17 | 17 | use Scalar::Util qw(weaken); |
18 | use Graphics::TIFF ':all'; # have already confirmed that this exists | |
18 | use Graphics::TIFF 7 ':all'; # have already confirmed that this exists | |
19 | 19 | |
20 | 20 | =head1 NAME |
21 | 21 | |
185 | 185 | if ($tif->{'colorSpace'} eq 'Indexed') { |
186 | 186 | my $dict = PDFDict(); |
187 | 187 | $pdf->new_obj($dict); |
188 | $self->colorspace(PDFArray(PDFName($tif->{'colorSpace'}), PDFName('DeviceRGB'), PDFNum(255), $dict)); | |
188 | $self->colorspace(PDFArray(PDFName($tif->{'colorSpace'}), PDFName('DeviceRGB'), PDFNum(2**$tif->{'bitsPerSample'}-1), $dict)); | |
189 | 189 | $dict->{'Filter'} = PDFArray(PDFName('FlateDecode')); |
190 | $tif->{'fh'}->seek($tif->{'colorMapOffset'}, 0); | |
191 | my $colormap; | |
192 | my $straight; | |
193 | $tif->{'fh'}->read($colormap, $tif->{'colorMapLength'}); | |
190 | my ($red, $green, $blue) = @{$tif->{'colorMap'}}; | |
194 | 191 | $dict->{' stream'} = ''; |
195 | $straight .= pack('C', ($_/256)) for unpack($tif->{'short'} . '*', $colormap); | |
196 | foreach my $c (0 .. (($tif->{'colorMapSamples'}/3)-1)) { | |
197 | $dict->{' stream'} .= substr($straight, $c, 1); | |
198 | $dict->{' stream'} .= substr($straight, $c + ($tif->{'colorMapSamples'}/3), 1); | |
199 | $dict->{' stream'} .= substr($straight, $c + ($tif->{'colorMapSamples'}/3)*2, 1); | |
192 | for my $i (0 .. $#{$red}) { | |
193 | $dict->{' stream'} .= pack('C', ($red->[$i]/256)); | |
194 | $dict->{' stream'} .= pack('C', ($green->[$i]/256)); | |
195 | $dict->{' stream'} .= pack('C', ($blue->[$i]/256)); | |
200 | 196 | } |
201 | 197 | } else { |
202 | 198 | $self->colorspace($tif->{'colorSpace'}); |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.017'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Basic::PDF::Utils; |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '2.031'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Basic::PDF::Utils; |
4 | 4 | use strict; |
5 | 5 | use warnings; |
6 | 6 | |
7 | our $VERSION = '3.019'; # VERSION | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | 8 | my $LAST_UPDATE = '3.017'; # manually update whenever code is changed |
9 | 9 | |
10 | 10 | use PDF::Builder::Util qw(pdfkey); |
2 | 2 | use strict; |
3 | 3 | no warnings qw[ deprecated recursion uninitialized ]; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.010'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | =head1 NAME |
2 | 2 | use strict; |
3 | 3 | no warnings qw[ recursion uninitialized ]; |
4 | 4 | |
5 | our $VERSION = '3.019'; # VERSION | |
5 | our $VERSION = '3.020'; # VERSION | |
6 | 6 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
7 | 7 | |
8 | 8 | # note: $a and $b are "Magic variables" according to perlcritic, and so it |
4 | 4 | |
5 | 5 | # $VERSION defined here so developers can run PDF::Builder from git. |
6 | 6 | # it should be automatically updated as part of the CPAN build. |
7 | our $VERSION = '3.019'; # VERSION | |
8 | my $LAST_UPDATE = '3.017'; # manually update whenever code is changed | |
7 | our $VERSION = '3.020'; # VERSION | |
8 | my $LAST_UPDATE = '3.020'; # manually update whenever code is changed | |
9 | 9 | |
10 | 10 | use Carp; |
11 | 11 | use Encode qw(:all); |
170 | 170 | The GNU Lesser General Public License (LGPL) Version 2.1, February 1999 |
171 | 171 | |
172 | 172 | (The master copy of this license lives on the GNU website.) |
173 | ||
174 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 | |
175 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
176 | ||
177 | I<Please see the> INFO/LICENSE I<file in the distribution root for full | |
178 | details.> | |
173 | (A copy is provided in the INFO/LICENSE file for your convenience.) | |
174 | ||
175 | This section of Builder.pm is intended only as a very brief summary | |
176 | of the license; please consider INFO/LICENSE to be the controlling version, | |
177 | if there is any conflict or ambiguity between the two. | |
179 | 178 | |
180 | 179 | This program is free software; you can redistribute it and/or modify it under |
181 | 180 | the terms of the GNU Lesser General Public License, as published by the Free |
182 | 181 | Software Foundation, either version 2.1 of the License, or (at your option) any |
183 | later version. | |
182 | later version of this license. | |
183 | ||
184 | NOTE: there are several files in this distribution which were incorporated from | |
185 | outside sources and carry different licenses. If a file states that it is under | |
186 | a license different than LGPL 2.1, that license and its terms will apply to | |
187 | that file, and not LGPL 2.1. | |
184 | 188 | |
185 | 189 | This library is distributed in the hope that it will be useful, but WITHOUT ANY |
186 | 190 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
225 | 229 | '-outver' PDF level has to be bumped up due to either a higher PDF level file |
226 | 230 | being read in, or a higher level feature was requested. A value of 0 |
227 | 231 | suppresses the warning message. |
232 | ||
233 | =item -diaglevel | |
234 | ||
235 | The '-diaglevel' option can be | |
236 | given to specify the level of diagnostics given by IntegrityCheck(). The | |
237 | default is level 2 (errors and warnings). | |
238 | See L<PDF::Builder::Docs/IntegrityCheck> for more information. | |
228 | 239 | |
229 | 240 | =back |
230 | 241 | |
280 | 291 | # code should also allow integers 0 (= 'none') and >0 (= 'flate') |
281 | 292 | # for compatibility with old usage where forcecompress is directly set. |
282 | 293 | } |
294 | if (exists $options{'-diaglevel'}) { | |
295 | my $diaglevel = $options{'-diaglevel'}; | |
296 | if ($diaglevel < 0 || $diaglevel > 5) { | |
297 | print "-diaglevel must be in range 0-5. using 2\n"; | |
298 | $diaglevel = 2; | |
299 | } | |
300 | $self->{'diaglevel'} = $diaglevel; | |
301 | } else { | |
302 | $self->{'diaglevel'} = 2; # default: errors and warnings | |
303 | } | |
304 | ||
283 | 305 | $self->preferences(%options); |
284 | 306 | if (defined $options{'-outver'}) { |
285 | 307 | if ($options{'-outver'} >= 1.4) { |
448 | 470 | |
449 | 471 | =cut |
450 | 472 | |
451 | # Note: openScalar() renamed to open_scalar() | |
452 | ||
453 | 473 | sub open_scalar { |
454 | 474 | my ($class, $content, %options) = @_; |
455 | 475 | |
460 | 480 | } |
461 | 481 | |
462 | 482 | $self->{'content_ref'} = \$content; |
483 | my $diaglevel = 2; | |
484 | if (defined $self->{'diaglevel'}) { $diaglevel = $self->{'diaglevel'}; } | |
485 | if ($diaglevel < 0 || $diaglevel > 5) { $diaglevel = 2; } | |
486 | my $newVer = $self->IntegrityCheck($diaglevel, $content); | |
487 | # if Version override defined in PDF, need to overwrite the %PDF-x.y | |
488 | # statement with the new (if higher) value. it's too late to wait until | |
489 | # after File->open, as it's already complained about some >1.4 features. | |
490 | if (defined $newVer) { | |
491 | my ($verStr, $currentVer, $pos); | |
492 | $pos = index $content, "%PDF-"; | |
493 | if ($pos < 0) { die "no PDF version found in PDF input!\n"; } | |
494 | # assume major and minor PDF version numbers max 2 digits each for now | |
495 | # (are 1 or 2 and 0-7 at this writing) | |
496 | $verStr = substr($content, $pos, 10); | |
497 | if ($verStr =~ m#^%PDF-(\d+)\.(\d+)#) { | |
498 | $currentVer = "$1.$2"; | |
499 | } else { | |
500 | die "unable to get PDF input's version number.\n"; | |
501 | } | |
502 | if ($newVer > $currentVer) { | |
503 | if (length($newVer) > length($currentVer)) { | |
504 | print STDERR "Unable to update 'content' version because override '$newVer' is longer than header version '$currentVer'.\nYou may receive warnings about features that bump up the PDF level.\n"; | |
505 | } else { | |
506 | if (length($newVer) < length($currentVer)) { | |
507 | # unlikely, but cover all the bases | |
508 | $newVer = substr($newVer, 0, length($currentVer)); | |
509 | } | |
510 | substr($content, $pos+5, length($newVer)) = $newVer; | |
511 | $outVer = $newVer; | |
512 | } | |
513 | } | |
514 | } | |
515 | ||
463 | 516 | my $fh; |
464 | 517 | CORE::open($fh, '+<', \$content) or die "Can't begin scalar IO"; |
465 | 518 | |
488 | 541 | $self->{'forcecompress'} = 'flate'; |
489 | 542 | # code should also allow integers 0 (= 'none') and >0 (= 'flate') |
490 | 543 | # for compatibility with old usage where forcecompress is directly set. |
544 | } | |
545 | if (exists $options{'-diaglevel'}) { | |
546 | $self->{'diaglevel'} = $options{'-diaglevel'}; | |
547 | if ($self->{'diaglevel'} < 0 || $self->{'diaglevel'} > 5) { | |
548 | $self->{'diaglevel'} = 2; | |
549 | } | |
550 | } else { | |
551 | $self->{'diaglevel'} = 2; | |
491 | 552 | } |
492 | 553 | $self->{'fonts'} = {}; |
493 | 554 | $self->{'infoMeta'} = [qw(Author CreationDate ModDate Creator Producer Title Subject Keywords)]; |
1451 | 1512 | =item $page = $pdf->import_page($source_pdf, $source_page_number) |
1452 | 1513 | |
1453 | 1514 | =item $page = $pdf->import_page($source_pdf, $source_page_number, $target_page_number) |
1515 | ||
1516 | =item $page = $pdf->import_page($source_pdf, $source_page_number, $target_page_object) | |
1454 | 1517 | |
1455 | 1518 | Imports a page from $source_pdf and adds it to the specified position |
1456 | 1519 | in $pdf. |
2117 | 2180 | my ($self, $file, %opts) = @_; |
2118 | 2181 | |
2119 | 2182 | my ($rc, $obj); |
2120 | $rc = eval { | |
2121 | require Graphics::TIFF; | |
2122 | 1; | |
2123 | }; | |
2124 | if (!defined $rc) { $rc = 0; } # else is 1 | |
2183 | $rc = $self->LA_GT(); | |
2125 | 2184 | if ($rc) { |
2126 | 2185 | # Graphics::TIFF available |
2127 | 2186 | if (defined $opts{'-nouseGT'} && $opts{'-nouseGT'} == 1) { |
2140 | 2199 | $self->{'pdf'}->out_obj($self->{'pages'}); |
2141 | 2200 | |
2142 | 2201 | if ($rc == 0 && $MSG_COUNT[0]++ == 0) { |
2143 | # TBD give warning message once, unless silenced (-silent) or | |
2202 | # give warning message once, unless silenced (-silent) or | |
2144 | 2203 | # deliberately not using Graphics::TIFF (rc == -1) |
2145 | 2204 | if (!defined $opts{'-silent'} || $opts{'-silent'} == 0) { |
2146 | 2205 | print STDERR "Your system does not have Graphics::TIFF installed, so some\nTIFF functions may not run correctly.\n"; |
2229 | 2288 | my ($self, $file, %opts) = @_; |
2230 | 2289 | |
2231 | 2290 | my ($rc, $obj); |
2232 | $rc = eval { | |
2233 | require Image::PNG::Libpng; | |
2234 | 1; | |
2235 | }; | |
2236 | if (!defined $rc) { $rc = 0; } # else is 1 | |
2291 | $rc = $self->LA_IPL(); | |
2237 | 2292 | if ($rc) { |
2238 | 2293 | # Image::PNG::Libpng available |
2239 | 2294 | if (defined $opts{'-nouseIPL'} && $opts{'-nouseIPL'} == 1) { |
2252 | 2307 | $self->{'pdf'}->out_obj($self->{'pages'}); |
2253 | 2308 | |
2254 | 2309 | if ($rc == 0 && $MSG_COUNT[1]++ == 0) { |
2255 | # TBD give warning message once, unless silenced (-silent) or | |
2310 | # give warning message once, unless silenced (-silent) or | |
2256 | 2311 | # deliberately not using Image::PNG::Libpng (rc == -1) |
2257 | 2312 | if (!defined $opts{'-silent'} || $opts{'-silent'} == 0) { |
2258 | 2313 | print STDERR "Your system does not have Image::PNG::Libpng installed, so some\nPNG functions may not run correctly.\n"; |
2688 | 2743 | return $obj; |
2689 | 2744 | } # end of named_destination() |
2690 | 2745 | |
2746 | # ================================================== | |
2747 | # input: level of checking, PDF as a string | |
2748 | # level: 0 just return with any version override | |
2749 | # 1 return version override, and errors | |
2750 | # 2 return version override, and errors and warnings | |
2751 | # 3 return version override, plus errors, warnings, notes | |
2752 | # 4 like (3), plus dump analysis data | |
2753 | # 5 like (4), plus dump $self (PDF) contents | |
2754 | # returns any /Version value found in Catalog, last one if multiple ones found, | |
2755 | # else undefined | |
2756 | ||
2757 | sub IntegrityCheck { | |
2758 | my ($self, $level, $string) = @_; | |
2759 | ||
2760 | my $level_nodiag = 0; | |
2761 | my $level_error = 1; | |
2762 | my $level_warning = 2; | |
2763 | my $level_note = 3; | |
2764 | my $level_dump = 4; | |
2765 | my $level_dumpself = 5; | |
2766 | ||
2767 | my $IC = "PDF Integrity Check:"; | |
2768 | ||
2769 | #print "$IC level $level\n" if $level >= $level_error; | |
2770 | my $Version = undef; | |
2771 | my ($Info, $Root, $str, $pos, $Parent, @Kids, @others); | |
2772 | ||
2773 | my $idx_defined = 0; # has this object been explicitly defined? | |
2774 | my $idx_refcount = 1; # count of all pointing to this obj except as Kid | |
2775 | my $idx_par_clmd = 2; # other object claiming this object as Kid | |
2776 | my $idx_parent = 3; # this object's /Parent entry | |
2777 | my $idx_kid_cnt = 4; # size of kid_list | |
2778 | my $idx_kid_list = 5; # this object's /Kids list | |
2779 | # intialize each element to [ 0 0 -1 -1 -1 [] ] | |
2780 | ||
2781 | return $Version if !length($string); # nothing to examine? | |
2782 | # even if $level 0, still want to get any higher /Version | |
2783 | # build analysis data and issue errors/warnings at appropriate $level | |
2784 | my @major = split /%%EOF/, $string; # typically [0] entire PDF [1] empty | |
2785 | my %objList; | |
2786 | my $update = -1; | |
2787 | foreach (@major) { | |
2788 | # update section number 0, 1, 2... with %%EOF in-between | |
2789 | $update++; | |
2790 | next if !length($_); | |
2791 | ||
2792 | # split on "endobj" | |
2793 | my @rawObjects = split /endobj/, $_; | |
2794 | # each element contains an object plus leading stuff, not incl endobj | |
2795 | ||
2796 | foreach my $rawObject (@rawObjects) { | |
2797 | next if !length($rawObject); | |
2798 | ||
2799 | # remove bulky and unwanted stream...endstream | |
2800 | if ($rawObject =~ m/^(.*)stream\s.*\sendstream(.*)$/s) { | |
2801 | $rawObject = $1.$2; | |
2802 | } | |
2803 | ||
2804 | # trim off anything before obj clause. endobj already gone. | |
2805 | if ($rawObject =~ m/^(.*?)\s?(\d+) (\d+) obj\s(.*)$/s || | |
2806 | $rawObject =~ m/^(.*?)\s?(\d+) (\d+) obj(.*)$/s) { | |
2807 | $rawObject = $4; | |
2808 | ||
2809 | # found an obj, full string is $rawObject. parse into | |
2810 | # selected fields, build $objList{key} entry. | |
2811 | my $objKey = "$2.$3"; # e.g., 4 0 obj -> 4.0 | |
2812 | # if this is a replacement object in an update, clear Parent | |
2813 | # and Kids | |
2814 | if (defined $objList{$objKey} && $update > 0) { | |
2815 | $objList{$objKey}->[$idx_parent] = -1; | |
2816 | $objList{$objKey}->[$idx_kid_cnt] = -1; | |
2817 | $objList{$objKey}->[$idx_kid_list] = []; | |
2818 | } | |
2819 | # might have already created this object element as target | |
2820 | # from another object | |
2821 | if (!defined $objList{$objKey}) { | |
2822 | $objList{$objKey} = [0, 0, -1, -1, -1, []]; | |
2823 | } | |
2824 | # mark object as defined | |
2825 | $objList{$objKey}->[$idx_defined] = 1; | |
2826 | ||
2827 | # found an object | |
2828 | # looking for /Parent x y R | |
2829 | # /Kids [ x y R ] | |
2830 | # /Type = /Catalog -> /Version /x.y | |
2831 | # for now, ignoring any /BaseVersion | |
2832 | # all other x y R | |
2833 | # remove from $rawObject as we find a match | |
2834 | ||
2835 | # /Parent x y R -> $Parent | |
2836 | if ($rawObject =~ m#/Parent(\s+)(\d+)(\s+)(\d+)(\s+)R#) { | |
2837 | $Parent = "$2.$4"; | |
2838 | $str = "/Parent$1$2$3$4$5R"; | |
2839 | $pos = index $rawObject, $str; | |
2840 | substr($rawObject, $pos, length($str)) = ''; | |
2841 | # TBD realistically, do we need to check for >1 /Parent ? | |
2842 | #if ($objList{$objKey}->[$idx_parent] == -1) { | |
2843 | # first /Parent (should not be more) | |
2844 | $objList{$objKey}->[$idx_parent] = $Parent; | |
2845 | #} else { | |
2846 | # print STDERR "$IC Additional Parent ($Parent) in object $objKey, already list $objList{$objKey}->[$idx_parent] as Parent.\n" if $level >= $level_error; | |
2847 | #} | |
2848 | } | |
2849 | ||
2850 | # /Kids [ x y R ] -> @Kids | |
2851 | # should we check for multiple Kids arrays in one object (error)? | |
2852 | if ($rawObject =~ m#/Kids(\s+)\[(.*)\]#) { | |
2853 | $str = "/Kids$1\[$2\]"; | |
2854 | $pos = index $rawObject, $str; | |
2855 | substr($rawObject, $pos, length($str)) = ''; | |
2856 | ||
2857 | my $str2 = " $2"; # guarantee a leading \s | |
2858 | @Kids = (); | |
2859 | while (1) { | |
2860 | if ($str2 =~ m#(\s+)(\d+)(\s+)(\d+)(\s+)R#) { | |
2861 | $str = "$1$2$3$4$5R"; | |
2862 | push @Kids, "$2.$4"; | |
2863 | $pos = index $str2, $str; | |
2864 | substr($str2, $pos, length($str)) = ''; | |
2865 | } else { | |
2866 | last; | |
2867 | } | |
2868 | } | |
2869 | # TBD: realistically, any need to check for >1 /Kids? | |
2870 | #if (!scalar(@{$objList{$objKey}->[$idx_kid_list]})) { | |
2871 | # first /Kids (should not be more) | |
2872 | @{$objList{$objKey}->[$idx_kid_list]} = @Kids; | |
2873 | $objList{$objKey}->[$idx_kid_cnt] = scalar(@Kids); | |
2874 | #} else { | |
2875 | # print STDERR "$IC Multiple Kids lists in object $objKey, already list @{$objList{$objKey}->[$idx_kid_list]} as Kids.\n" if $level >= $level_error; | |
2876 | #} | |
2877 | } | |
2878 | ||
2879 | # /Type /Catalog -> /Version /x.y -> $Version | |
2880 | # both x and y are normally single digits, but allow room | |
2881 | # just global $Version, assuming that each one physically | |
2882 | # later overrides any earlier ones | |
2883 | if ($rawObject =~ m#/Type(\s+)/Catalog#) { | |
2884 | my $sp1 = $1; | |
2885 | if ($rawObject =~ m#/Version /(\d+)\.(\d+)#) { | |
2886 | $Version = "$1.$2"; | |
2887 | $str = "/Version$sp1/$Version"; | |
2888 | $pos = index $rawObject, $str; | |
2889 | substr($rawObject, $pos, length($str)) = ''; | |
2890 | } | |
2891 | } | |
2892 | ||
2893 | # if using cross-reference stream, will find /Root x y R | |
2894 | # and /Info x y R entries in an object of /Type /Xref | |
2895 | # it looks like last ones will win | |
2896 | if ($rawObject =~ m#/Type(\s+)/XRef# || | |
2897 | $rawObject =~ m#/Type/XRef#) { | |
2898 | if ($rawObject =~ m#/Root(\s+)(\d+)(\s+)(\d+)(\s+)R#) { | |
2899 | $Root = "$2.$4"; | |
2900 | $str = "/Root$1$2$3$4$5R"; | |
2901 | $pos = index $rawObject, $str; | |
2902 | substr($rawObject, $pos, length($str)) = ''; | |
2903 | } | |
2904 | if ($rawObject =~ m#/Info(\s+)(\d+)(\s+)(\d+)(\s+)R#) { | |
2905 | $Info = "$2.$4"; | |
2906 | $str = "/Info$1$2$3$4$5R"; | |
2907 | $pos = index $rawObject, $str; | |
2908 | substr($rawObject, $pos, length($str)) = ''; | |
2909 | } | |
2910 | } | |
2911 | ||
2912 | # all other x y R -> @others | |
2913 | @others = (); | |
2914 | while (1) { | |
2915 | if ($rawObject =~ m#(\d+)(\s+)(\d+)(\s+)R#) { | |
2916 | $str = "$1$2$3$4R"; | |
2917 | push @others, "$1.$3"; | |
2918 | $pos = index $rawObject, $str; | |
2919 | substr($rawObject, $pos, length($str)) = ''; | |
2920 | } else { | |
2921 | last; | |
2922 | } | |
2923 | } | |
2924 | # go through all other refs and create element if necessary, | |
2925 | # then increment its refcnt array element | |
2926 | foreach (@others) { | |
2927 | if (!defined $objList{$_}) { | |
2928 | $objList{$_} = [0, 0, -1, -1, -1, []]; | |
2929 | } | |
2930 | $objList{$_}->[$idx_refcount]++; | |
2931 | } | |
2932 | foreach (@Kids) { | |
2933 | if (!defined $objList{$_}) { | |
2934 | $objList{$_} = [0, 0, -1, -1, -1, []]; | |
2935 | } | |
2936 | $objList{$_}->[$idx_refcount]++; | |
2937 | } | |
2938 | ||
2939 | } else { | |
2940 | # not an object, but could be other stuff of interest | |
2941 | # looking for trailer -> /Root x y R & /Info x y R | |
2942 | if ($rawObject =~ m/trailer/) { | |
2943 | if ($rawObject =~ m#trailer(.*)/Info(\s+)(\d+)(\s+)(\d+)(\s+)R#s) { | |
2944 | $Info = "$3.$5"; | |
2945 | } | |
2946 | if ($rawObject =~ m#trailer(.*)/Root(\s+)(\d+)(\s+)(\d+)(\s+)R#s) { | |
2947 | $Root = "$3.$5"; | |
2948 | } | |
2949 | } | |
2950 | } | |
2951 | } | |
2952 | } | |
2953 | ||
2954 | # increment Root and Info objects reference counts | |
2955 | # they probably SHOULD already be defined (issue warning if not) | |
2956 | if (!defined $Root) { | |
2957 | print STDERR "$IC No Root object defined!\n" if $level >= $level_error; | |
2958 | } else { | |
2959 | if (!defined $objList{$Root}) { | |
2960 | $objList{$Root} = [1, 0, -1, -1, -1, []]; | |
2961 | print STDERR "$IC Root object $Root not found!\n" if $level >= $level_error; | |
2962 | } | |
2963 | $objList{$Root}->[$idx_refcount]++; | |
2964 | } | |
2965 | ||
2966 | # Info is optional | |
2967 | if (!defined $Info) { | |
2968 | print STDERR "$IC No Info object defined!\n" if $level >= $level_note; | |
2969 | } else { | |
2970 | if (!defined $objList{$Info}) { | |
2971 | $objList{$Info} = [1, 0, -1, -1, -1, []]; | |
2972 | print STDERR "$IC Info object $Info not found!\n" if $level >= $level_note; | |
2973 | # possibly in a deleted object (on free list) | |
2974 | } | |
2975 | $objList{$Info}->[$idx_refcount]++; | |
2976 | } | |
2977 | ||
2978 | # revisit each element in objList | |
2979 | # visit each Kid, their $idx_par_clmd should be -1 (set to this object) | |
2980 | # (if not -1, is on multiple Kids lists) | |
2981 | # their $idx_parent should be this object | |
2982 | # they should have a Parent declared | |
2983 | # any element with ref count of 0 and no Parent give warning unreachable | |
2984 | # TBD: anything else to add to things to check? | |
2985 | foreach my $thisObj (sort keys %objList) { | |
2986 | ||
2987 | # was an object actually defined for this entry? | |
2988 | # missing Info and Root messages already given, so flag is 1 ("defined") | |
2989 | if ($objList{$thisObj}->[$idx_defined] == 0) { | |
2990 | print STDERR "$IC object $thisObj referenced, but no entry found.\n" if $level >= $level_note; | |
2991 | # it's apparently OK if the missing object is on the free list -- | |
2992 | # it will just be ignored | |
2993 | } | |
2994 | ||
2995 | # check any Kids | |
2996 | if ($objList{$thisObj}[$idx_kid_cnt] > 0) { | |
2997 | # this object has children (/Kids), so explore them one level deep | |
2998 | for (my $kidObj=0; $kidObj<$objList{$thisObj}[$idx_kid_cnt]; $kidObj++) { | |
2999 | my $child = $objList{$thisObj}[$idx_kid_list]->[$kidObj]; | |
3000 | # child's claimed parent should be -1, set to thisObj | |
3001 | if ($objList{$child}[$idx_par_clmd] == -1) { | |
3002 | # no one has claimed to be parent, so set to thisObj | |
3003 | $objList{$child}[$idx_par_clmd] = $thisObj; | |
3004 | } else { | |
3005 | # someone else has already claimed to be parent | |
3006 | print STDERR "$IC object $thisObj wants to claim object $child as its child, but $objList{$child}[$idx_par_clmd] already has!\nPossibly $child is on more than one /Kids list?\n" if $level >= $level_error; | |
3007 | } | |
3008 | # if no object defined for child, already flagged as missing | |
3009 | if ($objList{$child}[$idx_defined] == 1) { | |
3010 | # child should list thisObj as its Parent | |
3011 | if ($objList{$child}[$idx_parent] == -1) { | |
3012 | print STDERR "$IC object $thisObj claims $child as a child (/Kids), but $child claims no Parent!\n" if $level >= $level_error; | |
3013 | $objList{$child}[$idx_parent] = $thisObj; | |
3014 | } elsif ($objList{$child}[$idx_parent] != $thisObj) { | |
3015 | print STDERR "$IC object $thisObj claims $child as a child (/Kids), but $child claims $objList{$child}[$idx_parent] as its parent!\n" if $level >= $level_error; | |
3016 | } | |
3017 | } | |
3018 | } | |
3019 | } | |
3020 | ||
3021 | if ($objList{$thisObj}[$idx_parent] == -1 && | |
3022 | $objList{$thisObj}[$idx_refcount] == 0) { | |
3023 | print STDERR "$IC Warning: object $thisObj appears to be unreachable.\n" if $level >= $level_note; | |
3024 | } | |
3025 | } | |
3026 | ||
3027 | if ($level >= $level_dump) { | |
3028 | # dump analysis data | |
3029 | use Data::Dumper; | |
3030 | my $d = Data::Dumper->new([\%objList]); | |
3031 | print "========= dump of $IC analysis data ===========\n"; | |
3032 | print $d->Dump(); | |
3033 | } | |
3034 | ||
3035 | # if have entire processed PDF in $self | |
3036 | if ($level >= $level_dumpself) { | |
3037 | # dump whole data | |
3038 | use Data::Dumper; | |
3039 | my $d = Data::Dumper->new([$self]); | |
3040 | print "========= dump of $IC PDF (self) data ===========\n"; | |
3041 | print $d->Dump(); | |
3042 | } | |
3043 | ||
3044 | return $Version; | |
3045 | } | |
3046 | ||
2691 | 3047 | 1; |
2692 | 3048 | |
2693 | 3049 | __END__ |
62 | 62 | next; |
63 | 63 | } |
64 | 64 | } |
65 | # HarfBuzz::Shaper is built into Content.pm, doesn't have its own module | |
65 | 66 | use_ok($file); |
66 | 67 | } |
67 | 68 |
1 | 1 | use strict; |
2 | 2 | use warnings; |
3 | 3 | |
4 | ||
4 | # Windows: SET AUTHOR_TESTING=1 | |
5 | # this test is a subset of tools/1_pc.pl | |
5 | 6 | BEGIN { |
6 | 7 | unless ($ENV{'AUTHOR_TESTING'}) { |
7 | 8 | print qq{1..0 # SKIP these tests are for testing by the author\n}; |
9 | 10 | } |
10 | 11 | } |
11 | 12 | |
12 | use Test::Perl::Critic (-profile => "perlcritic.rc") x!! -e "perlcritic.rc"; | |
13 | use Test::Perl::Critic (-profile => "..\.perlcriticrc") x!! -e "..\.perlcriticrc"; | |
13 | 14 | all_critic_ok(); |
8 | 8 | |
9 | 9 | my @possible_locations = ( |
10 | 10 | '/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf', |
11 | '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', | |
11 | 12 | '/var/lib/defoma/gs.d/dirs/fonts/DejaVuSans.ttf', |
12 | 13 | 'C:/Windows/fonts/DejaVuSans.ttf', |
13 | 14 | ); |
8 | 8 | |
9 | 9 | my @possible_locations = ( |
10 | 10 | '/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf', |
11 | '/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf', | |
11 | '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', | |
12 | 12 | 'C:/Windows/fonts/DejaVuSans.ttf', |
13 | 13 | ); |
14 | 14 |
0 | 0 | #!/usr/bin/perl |
1 | 1 | use warnings; |
2 | 2 | use strict; |
3 | ||
4 | use Test::More tests => 11; | |
3 | use English qw' -no_match_vars '; | |
4 | use IPC::Cmd qw(can_run); | |
5 | use Test::More tests => 12; | |
5 | 6 | |
6 | 7 | use PDF::Builder; |
7 | 8 | |
10 | 11 | # usable, otherwise they will display just TIFF. you can use this information |
11 | 12 | # if you are not sure about the status of Graphics::TIFF. |
12 | 13 | |
13 | my $pdf = PDF::Builder->new('-compress' => 'none'); | |
14 | my $pdf = PDF::Builder->new('-compress' => 'none'); # common $pdf all tests | |
15 | my $has_GT = 0; # global flag for all tests that need to know if Graphics::TIFF | |
14 | 16 | |
15 | 17 | # -silent shuts off one-time warning for rest of run |
16 | 18 | my $tiff = $pdf->image_tiff('t/resources/1x1.tif', -silent => 1); |
17 | 19 | if ($tiff->usesLib() == 1) { |
20 | $has_GT = 1; | |
18 | 21 | isa_ok($tiff, 'PDF::Builder::Resource::XObject::Image::TIFF_GT', |
19 | 22 | q{$pdf->image_tiff(filename)}); |
20 | 23 | } else { |
69 | 72 | ok($@, q{Fail fast if the requested file doesn't exist}); |
70 | 73 | |
71 | 74 | ############################################################## |
72 | # tiff2pdf and convert not available on all systems. 1 test skipped | |
73 | ||
75 | # common data for remaining tests | |
74 | 76 | my $width = 568; |
75 | 77 | my $height = 1000; |
76 | 78 | $tiff = 'test.tif'; |
77 | 79 | my $pdfout = 'test.pdf'; |
78 | 80 | |
79 | SKIP: { | |
80 | skip "tiff2pdf doesn't deal with the alpha layer properly either, in this case", 1; | |
81 | # WARNING: do not attempt to run the following 3 tests on a | |
82 | # Windows system. Windows has a 'convert' utility to convert | |
83 | # a filesystem from FAT32 to NTFS, and you don't want to | |
84 | # accidentally run that one! The test ($OSNAME) is for 'linux' | |
85 | # but testing for 'not windows' might do just as well. Even | |
86 | # better might be to see if 'convert' and 'tiffcp' are the | |
87 | # right utilties, if found. Otherwise, the OS is irrelevant. | |
88 | # | |
89 | # NOTE: following 3 tests use Linux/TIFF utilities convert | |
90 | # and tiffcp. They may require software installation on | |
91 | # your Linux system, and will be skipped if the necessary | |
92 | # software is not found. | |
93 | ||
94 | ############################################################## | |
95 | # convert not available on all systems. PDF::Builder itself | |
96 | # doesn't seem to work well with this, so skip for time being. | |
97 | ||
98 | SKIP: { | |
99 | skip "Further work is needed on PDF::Builder and the test process to handle the alpha layer properly.", 1; | |
100 | # skip "Non-Linux system, or no 'convert' utility", !( | |
101 | # $OSNAME eq 'linux' | |
102 | # and can_run('convert') | |
103 | # ); | |
104 | # ---------- | |
81 | 105 | system(sprintf"convert -depth 1 -gravity center -pointsize 78 -size %dx%d caption:'Lorem ipsum etc etc' %s", $width, $height, $tiff); |
106 | # ---------- | |
82 | 107 | $pdf = PDF::Builder->new(-file => $pdfout); |
83 | 108 | my $page = $pdf->page(); |
84 | 109 | $page->mediabox($width, $height); |
88 | 113 | $pdf->save(); |
89 | 114 | $pdf->end(); |
90 | 115 | |
116 | # ---------- | |
91 | 117 | my $example = `convert $pdfout -depth 1 -resize 1x1 txt:-`; |
92 | 118 | my $expected = `convert $tiff -depth 1 -resize 1x1 txt:-`; |
119 | # ---------- | |
93 | 120 | |
94 | 121 | is($example, $expected, 'alpha'); |
95 | 122 | } |
96 | 123 | |
97 | 124 | ############################################################## |
98 | # tiffcp and convert not available on all systems. 1 test skipped | |
99 | ||
100 | SKIP: { | |
101 | skip "Files created with tiffcp -c g3 previously produced the message 'Chunked CCITT G4 TIFF not supported'", 1; | |
125 | # tiffcp, convert and Graphics::TIFF not available on all systems. | |
126 | # Graphics::TIFF needed or you get message "Chunked CCITT G4 TIFF not supported" | |
127 | # from PDF::Builder's TIFF processing library. | |
128 | ||
129 | SKIP: { | |
130 | skip "Non-Linux system, or no 'convert' or no 'tiffcp'", 1 unless | |
131 | $has_GT and $OSNAME eq 'linux' | |
132 | and can_run('convert') | |
133 | and can_run('tiffcp'); | |
134 | # ---------- | |
102 | 135 | system(sprintf "convert -depth 1 -gravity center -pointsize 78 -size %dx%d caption:'Lorem ipsum etc etc' -background white -alpha off %s", $width, $height, $tiff); |
103 | 136 | system("tiffcp -c g3 $tiff tmp.tif && mv tmp.tif $tiff"); |
137 | # ---------- | |
104 | 138 | $pdf = PDF::Builder->new(-file => $pdfout); |
105 | 139 | my $page = $pdf->page(); |
106 | 140 | $page->mediabox($width, $height); |
110 | 144 | $pdf->save(); |
111 | 145 | $pdf->end(); |
112 | 146 | |
113 | my $example = `convert $pdfout -depth 1 -resize 1x1 txt:-`; | |
147 | # ---------- | |
148 | my $example = `convert $pdfout -depth 1 -colorspace gray -alpha off -resize 1x1 txt:-`; | |
114 | 149 | my $expected = `convert $tiff -depth 1 -resize 1x1 txt:-`; |
150 | # ---------- | |
115 | 151 | |
116 | 152 | is($example, $expected, 'G3 (not converted to flate)'); |
117 | 153 | } |
118 | 154 | |
119 | 155 | ############################################################## |
120 | # tiffcp and convert not available on all systems. 1 test skipped | |
121 | ||
122 | SKIP: { | |
123 | skip "convert and tiffcp utilities not available on all systems", 1; | |
156 | # tiffcp and convert not available on all systems. | |
157 | # Graphics::TIFF not needed for this test | |
158 | ||
159 | SKIP: { | |
160 | skip "Non-Linux system, or no 'convert' or no 'tiffcp'", 1 unless | |
161 | $OSNAME eq 'linux' | |
162 | and can_run('convert') | |
163 | and can_run('tiffcp'); | |
164 | # ---------- | |
124 | 165 | system(sprintf"convert -depth 1 -gravity center -pointsize 78 -size %dx%d caption:'Lorem ipsum etc etc' -background white -alpha off %s", $width, $height, $tiff); |
125 | 166 | system("tiffcp -c lzw $tiff tmp.tif && mv tmp.tif $tiff"); |
167 | # ---------- | |
126 | 168 | $pdf = PDF::Builder->new(-file => $pdfout); |
127 | 169 | my $page = $pdf->page; |
128 | 170 | $page->mediabox( $width, $height ); |
132 | 174 | $pdf->save(); |
133 | 175 | $pdf->end(); |
134 | 176 | |
177 | # ---------- | |
135 | 178 | my $example = `convert $pdfout -depth 1 -colorspace gray -alpha off -resize 1x1 txt:-`; |
136 | 179 | my $expected = `convert $tiff -depth 1 -resize 1x1 txt:-`; |
180 | # ---------- | |
137 | 181 | |
138 | 182 | is($example, $expected, 'lzw (converted to flate)'); |
139 | 183 | } |
140 | 184 | |
141 | 185 | ############################################################## |
186 | # convert not available on all systems. | |
187 | # Graphics::TIFF needed for this test | |
188 | ||
189 | SKIP: { | |
190 | skip "Non-Linux system, or no 'convert'", 1 unless | |
191 | $has_GT and $OSNAME eq 'linux' | |
192 | and can_run('convert'); | |
193 | # .png file is temporary file (output, input, erased) | |
194 | system("convert rose: -type palette -depth 2 colormap.png && convert colormap.png $tiff && rm colormap.png"); | |
195 | $pdf = PDF::Builder->new(-file => $pdfout); | |
196 | my $page = $pdf->page; | |
197 | $page->mediabox( $width, $height ); | |
198 | $gfx = $page->gfx(); | |
199 | my $img = $pdf->image_tiff($tiff); | |
200 | $gfx->image( $img, 0, 0, $width, $height ); | |
201 | $pdf->save(); | |
202 | $pdf->end(); | |
203 | pass 'successfully read TIFF with colormap'; | |
204 | } | |
205 | ||
206 | ############################################################## | |
142 | 207 | # cleanup. all tests involving these files skipped? |
143 | 208 | |
144 | 209 | unlink $pdfout, $tiff; |
6 | 6 | use strict; |
7 | 7 | use warnings; |
8 | 8 | |
9 | our $VERSION = '3.019'; # VERSION | |
9 | our $VERSION = '3.020'; # VERSION | |
10 | 10 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
11 | 11 | |
12 | 12 | # command line: |
27 | 27 | # output <source name> OK is always ignored |
28 | 28 | my @ignore_list = ( |
29 | 29 | # should not ignore any level 5 warnings |
30 | "Use IO::Interactive::is_interactive", | |
31 | # not a core module! | |
30 | 32 | # common level 4 warnings to ignore |
31 | 33 | "Code before warnings", # due to use of "no warnings" pragma |
32 | 34 | "Warnings disabled at", # due to use of "no warnings" pragma |
6 | 6 | use strict; |
7 | 7 | use warnings; |
8 | 8 | |
9 | our $VERSION = '3.019'; # VERSION | |
9 | our $VERSION = '3.020'; # VERSION | |
10 | 10 | my $LAST_UPDATE = '3.016'; # manually update whenever code is changed |
11 | 11 | |
12 | 12 | # command line flags, mutually exclusive: |