hexapdf 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +68 -0
- data/CONTRIBUTERS +1 -1
- data/README.md +35 -4
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/data/hexapdf/cmap/83pv-RKSJ-H +314 -0
- data/data/hexapdf/cmap/90ms-RKSJ-H +259 -0
- data/data/hexapdf/cmap/90ms-RKSJ-V +156 -0
- data/data/hexapdf/cmap/90msp-RKSJ-H +257 -0
- data/data/hexapdf/cmap/90msp-RKSJ-V +155 -0
- data/data/hexapdf/cmap/90pv-RKSJ-H +355 -0
- data/data/hexapdf/cmap/Add-RKSJ-H +738 -0
- data/data/hexapdf/cmap/Add-RKSJ-V +135 -0
- data/data/hexapdf/cmap/Adobe-CNS1-UCS2 +18209 -0
- data/data/hexapdf/cmap/Adobe-GB1-UCS2 +14267 -0
- data/data/hexapdf/cmap/Adobe-Japan1-UCS2 +19159 -0
- data/data/hexapdf/cmap/Adobe-Korea1-UCS2 +9267 -0
- data/data/hexapdf/cmap/B5pc-H +337 -0
- data/data/hexapdf/cmap/B5pc-V +90 -0
- data/data/hexapdf/cmap/CNS-EUC-H +490 -0
- data/data/hexapdf/cmap/CNS-EUC-V +538 -0
- data/data/hexapdf/cmap/ETen-B5-H +343 -0
- data/data/hexapdf/cmap/ETen-B5-V +91 -0
- data/data/hexapdf/cmap/ETenms-B5-H +79 -0
- data/data/hexapdf/cmap/ETenms-B5-V +99 -0
- data/data/hexapdf/cmap/EUC-H +207 -0
- data/data/hexapdf/cmap/EUC-V +105 -0
- data/data/hexapdf/cmap/Ext-RKSJ-H +768 -0
- data/data/hexapdf/cmap/Ext-RKSJ-V +117 -0
- data/data/hexapdf/cmap/GB-EUC-H +173 -0
- data/data/hexapdf/cmap/GB-EUC-V +98 -0
- data/data/hexapdf/cmap/GBK-EUC-H +4273 -0
- data/data/hexapdf/cmap/GBK-EUC-V +97 -0
- data/data/hexapdf/cmap/GBK2K-H +5325 -0
- data/data/hexapdf/cmap/GBK2K-V +118 -0
- data/data/hexapdf/cmap/GBKp-EUC-H +4272 -0
- data/data/hexapdf/cmap/GBKp-EUC-V +97 -0
- data/data/hexapdf/cmap/GBpc-EUC-H +175 -0
- data/data/hexapdf/cmap/GBpc-EUC-V +98 -0
- data/data/hexapdf/cmap/H +200 -0
- data/data/hexapdf/cmap/HKscs-B5-H +1331 -0
- data/data/hexapdf/cmap/HKscs-B5-V +90 -0
- data/data/hexapdf/cmap/Identity-H +339 -0
- data/data/hexapdf/cmap/Identity-V +73 -0
- data/data/hexapdf/cmap/KSC-EUC-H +562 -0
- data/data/hexapdf/cmap/KSC-EUC-V +94 -0
- data/data/hexapdf/cmap/KSCms-UHC-H +776 -0
- data/data/hexapdf/cmap/KSCms-UHC-HW-H +775 -0
- data/data/hexapdf/cmap/KSCms-UHC-HW-V +93 -0
- data/data/hexapdf/cmap/KSCms-UHC-V +94 -0
- data/data/hexapdf/cmap/KSCpc-EUC-H +608 -0
- data/data/hexapdf/cmap/LICENSE.txt +26 -0
- data/data/hexapdf/cmap/README.txt +9 -0
- data/data/hexapdf/cmap/UniCNS-UCS2-H +16992 -0
- data/data/hexapdf/cmap/UniCNS-UCS2-V +90 -0
- data/data/hexapdf/cmap/UniCNS-UTF16-H +19117 -0
- data/data/hexapdf/cmap/UniCNS-UTF16-V +94 -0
- data/data/hexapdf/cmap/UniGB-UCS2-H +14321 -0
- data/data/hexapdf/cmap/UniGB-UCS2-V +101 -0
- data/data/hexapdf/cmap/UniGB-UTF16-H +14381 -0
- data/data/hexapdf/cmap/UniGB-UTF16-V +104 -0
- data/data/hexapdf/cmap/UniJIS-UCS2-H +8870 -0
- data/data/hexapdf/cmap/UniJIS-UCS2-HW-H +81 -0
- data/data/hexapdf/cmap/UniJIS-UCS2-HW-V +279 -0
- data/data/hexapdf/cmap/UniJIS-UCS2-V +275 -0
- data/data/hexapdf/cmap/UniJIS-UTF16-H +14450 -0
- data/data/hexapdf/cmap/UniJIS-UTF16-V +299 -0
- data/data/hexapdf/cmap/UniKS-UCS2-H +8725 -0
- data/data/hexapdf/cmap/UniKS-UCS2-V +95 -0
- data/data/hexapdf/cmap/UniKS-UTF16-H +8895 -0
- data/data/hexapdf/cmap/UniKS-UTF16-V +99 -0
- data/data/hexapdf/cmap/V +105 -0
- data/examples/arc.rb +3 -3
- data/examples/merging.rb +4 -1
- data/examples/optimizing.rb +3 -0
- data/examples/show_char_bboxes.rb +2 -2
- data/examples/truetype.rb +2 -2
- data/lib/hexapdf/cli.rb +40 -1
- data/lib/hexapdf/cli/batch.rb +72 -0
- data/lib/hexapdf/cli/command.rb +112 -15
- data/lib/hexapdf/cli/files.rb +2 -2
- data/lib/hexapdf/cli/images.rb +14 -6
- data/lib/hexapdf/cli/info.rb +6 -8
- data/lib/hexapdf/cli/inspect.rb +5 -8
- data/lib/hexapdf/cli/merge.rb +13 -20
- data/lib/hexapdf/cli/modify.rb +4 -7
- data/lib/hexapdf/cli/optimize.rb +2 -5
- data/lib/hexapdf/configuration.rb +32 -3
- data/lib/hexapdf/content/canvas.rb +130 -37
- data/lib/hexapdf/content/parser.rb +40 -6
- data/lib/hexapdf/content/processor.rb +4 -4
- data/lib/hexapdf/document.rb +40 -10
- data/lib/hexapdf/document/fonts.rb +1 -0
- data/lib/hexapdf/encryption/security_handler.rb +8 -12
- data/lib/hexapdf/filter/flate_decode.rb +25 -2
- data/lib/hexapdf/font/cmap.rb +124 -8
- data/lib/hexapdf/font/cmap/parser.rb +65 -15
- data/lib/hexapdf/font/encoding/base.rb +2 -2
- data/lib/hexapdf/font/encoding/glyph_list.rb +2 -4
- data/lib/hexapdf/font/true_type.rb +1 -0
- data/lib/hexapdf/font/true_type/builder.rb +75 -0
- data/lib/hexapdf/font/true_type/optimizer.rb +65 -0
- data/lib/hexapdf/font/true_type/subsetter.rb +9 -22
- data/lib/hexapdf/font/true_type_wrapper.rb +9 -21
- data/lib/hexapdf/font_loader.rb +1 -1
- data/lib/hexapdf/importer.rb +1 -1
- data/lib/hexapdf/serializer.rb +5 -3
- data/lib/hexapdf/type.rb +2 -0
- data/lib/hexapdf/type/cid_font.rb +120 -0
- data/lib/hexapdf/type/font.rb +32 -12
- data/lib/hexapdf/type/font_simple.rb +34 -42
- data/lib/hexapdf/type/font_type0.rb +148 -0
- data/lib/hexapdf/type/form.rb +4 -4
- data/lib/hexapdf/type/page.rb +12 -11
- data/lib/hexapdf/type/resources.rb +14 -0
- data/lib/hexapdf/utils/graphics_helpers.rb +77 -0
- data/lib/hexapdf/version.rb +1 -1
- data/man/man1/hexapdf.1 +43 -1
- data/test/hexapdf/content/test_canvas.rb +76 -0
- data/test/hexapdf/content/test_parser.rb +20 -1
- data/test/hexapdf/content/test_processor.rb +11 -7
- data/test/hexapdf/document/test_fonts.rb +3 -1
- data/test/hexapdf/font/cmap/test_parser.rb +42 -7
- data/test/hexapdf/font/encoding/test_base.rb +1 -1
- data/test/hexapdf/font/encoding/test_glyph_list.rb +3 -3
- data/test/hexapdf/font/test_cmap.rb +104 -0
- data/test/hexapdf/font/test_true_type_wrapper.rb +63 -46
- data/test/hexapdf/font/true_type/test_builder.rb +37 -0
- data/test/hexapdf/font/true_type/test_optimizer.rb +27 -0
- data/test/hexapdf/font/true_type/test_subsetter.rb +6 -13
- data/test/hexapdf/test_configuration.rb +12 -7
- data/test/hexapdf/test_document.rb +24 -0
- data/test/hexapdf/test_importer.rb +9 -1
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/test_cid_font.rb +61 -0
- data/test/hexapdf/type/test_font.rb +31 -4
- data/test/hexapdf/type/test_font_simple.rb +6 -21
- data/test/hexapdf/type/test_font_type0.rb +114 -0
- data/test/hexapdf/type/test_resources.rb +17 -1
- data/test/hexapdf/utils/test_graphics_helpers.rb +29 -0
- metadata +82 -3
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
%!PS-Adobe-3.0 Resource-CMap
|
|
2
|
+
%%DocumentNeededResources: ProcSet (CIDInit)
|
|
3
|
+
%%DocumentNeededResources: CMap (UniKS-UTF16-H)
|
|
4
|
+
%%IncludeResource: ProcSet (CIDInit)
|
|
5
|
+
%%IncludeResource: CMap (UniKS-UTF16-H)
|
|
6
|
+
%%BeginResource: CMap (UniKS-UTF16-V)
|
|
7
|
+
%%Title: (UniKS-UTF16-V Adobe Korea1 1)
|
|
8
|
+
%%Version: 1.004
|
|
9
|
+
%%Copyright: -----------------------------------------------------------
|
|
10
|
+
%%Copyright: Copyright 1990-2015 Adobe Systems Incorporated.
|
|
11
|
+
%%Copyright: All rights reserved.
|
|
12
|
+
%%Copyright:
|
|
13
|
+
%%Copyright: Redistribution and use in source and binary forms, with or
|
|
14
|
+
%%Copyright: without modification, are permitted provided that the
|
|
15
|
+
%%Copyright: following conditions are met:
|
|
16
|
+
%%Copyright:
|
|
17
|
+
%%Copyright: Redistributions of source code must retain the above
|
|
18
|
+
%%Copyright: copyright notice, this list of conditions and the following
|
|
19
|
+
%%Copyright: disclaimer.
|
|
20
|
+
%%Copyright:
|
|
21
|
+
%%Copyright: Redistributions in binary form must reproduce the above
|
|
22
|
+
%%Copyright: copyright notice, this list of conditions and the following
|
|
23
|
+
%%Copyright: disclaimer in the documentation and/or other materials
|
|
24
|
+
%%Copyright: provided with the distribution.
|
|
25
|
+
%%Copyright:
|
|
26
|
+
%%Copyright: Neither the name of Adobe Systems Incorporated nor the names
|
|
27
|
+
%%Copyright: of its contributors may be used to endorse or promote
|
|
28
|
+
%%Copyright: products derived from this software without specific prior
|
|
29
|
+
%%Copyright: written permission.
|
|
30
|
+
%%Copyright:
|
|
31
|
+
%%Copyright: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
32
|
+
%%Copyright: CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
33
|
+
%%Copyright: INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
34
|
+
%%Copyright: MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
35
|
+
%%Copyright: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
36
|
+
%%Copyright: CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
37
|
+
%%Copyright: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
38
|
+
%%Copyright: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
39
|
+
%%Copyright: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
40
|
+
%%Copyright: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
41
|
+
%%Copyright: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
42
|
+
%%Copyright: OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
43
|
+
%%Copyright: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
44
|
+
%%Copyright: -----------------------------------------------------------
|
|
45
|
+
%%EndComments
|
|
46
|
+
|
|
47
|
+
/CIDInit /ProcSet findresource begin
|
|
48
|
+
|
|
49
|
+
12 dict begin
|
|
50
|
+
|
|
51
|
+
begincmap
|
|
52
|
+
|
|
53
|
+
/UniKS-UTF16-H usecmap
|
|
54
|
+
|
|
55
|
+
/CIDSystemInfo 3 dict dup begin
|
|
56
|
+
/Registry (Adobe) def
|
|
57
|
+
/Ordering (Korea1) def
|
|
58
|
+
/Supplement 1 def
|
|
59
|
+
end def
|
|
60
|
+
|
|
61
|
+
/CMapName /UniKS-UTF16-V def
|
|
62
|
+
/CMapVersion 1.004 def
|
|
63
|
+
/CMapType 1 def
|
|
64
|
+
|
|
65
|
+
/XUID [1 10 25545] def
|
|
66
|
+
|
|
67
|
+
/WMode 1 def
|
|
68
|
+
|
|
69
|
+
11 begincidchar
|
|
70
|
+
<2016> 8061
|
|
71
|
+
<2025> 8058
|
|
72
|
+
<3013> 8075
|
|
73
|
+
<ff01> 8076
|
|
74
|
+
<ff0c> 8079
|
|
75
|
+
<ff0e> 8080
|
|
76
|
+
<ff3b> 8087
|
|
77
|
+
<ff3d> 8088
|
|
78
|
+
<ff3f> 8089
|
|
79
|
+
<ff5e> 8062
|
|
80
|
+
<ffe3> 8093
|
|
81
|
+
endcidchar
|
|
82
|
+
|
|
83
|
+
7 begincidrange
|
|
84
|
+
<2013> <2014> 8059
|
|
85
|
+
<3001> <3002> 8056
|
|
86
|
+
<3008> <3011> 8065
|
|
87
|
+
<3014> <3015> 8063
|
|
88
|
+
<ff08> <ff09> 8077
|
|
89
|
+
<ff1a> <ff1f> 8081
|
|
90
|
+
<ff5b> <ff5d> 8090
|
|
91
|
+
endcidrange
|
|
92
|
+
|
|
93
|
+
endcmap
|
|
94
|
+
CMapName currentdict /CMap defineresource pop
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
%%EndResource
|
|
99
|
+
%%EOF
|
data/data/hexapdf/cmap/V
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
%!PS-Adobe-3.0 Resource-CMap
|
|
2
|
+
%%DocumentNeededResources: ProcSet (CIDInit)
|
|
3
|
+
%%DocumentNeededResources: CMap (H)
|
|
4
|
+
%%IncludeResource: ProcSet (CIDInit)
|
|
5
|
+
%%IncludeResource: CMap (H)
|
|
6
|
+
%%BeginResource: CMap (V)
|
|
7
|
+
%%Title: (V Adobe Japan1 1)
|
|
8
|
+
%%Version: 12.004
|
|
9
|
+
%%Copyright: -----------------------------------------------------------
|
|
10
|
+
%%Copyright: Copyright 1990-2015 Adobe Systems Incorporated.
|
|
11
|
+
%%Copyright: All rights reserved.
|
|
12
|
+
%%Copyright:
|
|
13
|
+
%%Copyright: Redistribution and use in source and binary forms, with or
|
|
14
|
+
%%Copyright: without modification, are permitted provided that the
|
|
15
|
+
%%Copyright: following conditions are met:
|
|
16
|
+
%%Copyright:
|
|
17
|
+
%%Copyright: Redistributions of source code must retain the above
|
|
18
|
+
%%Copyright: copyright notice, this list of conditions and the following
|
|
19
|
+
%%Copyright: disclaimer.
|
|
20
|
+
%%Copyright:
|
|
21
|
+
%%Copyright: Redistributions in binary form must reproduce the above
|
|
22
|
+
%%Copyright: copyright notice, this list of conditions and the following
|
|
23
|
+
%%Copyright: disclaimer in the documentation and/or other materials
|
|
24
|
+
%%Copyright: provided with the distribution.
|
|
25
|
+
%%Copyright:
|
|
26
|
+
%%Copyright: Neither the name of Adobe Systems Incorporated nor the names
|
|
27
|
+
%%Copyright: of its contributors may be used to endorse or promote
|
|
28
|
+
%%Copyright: products derived from this software without specific prior
|
|
29
|
+
%%Copyright: written permission.
|
|
30
|
+
%%Copyright:
|
|
31
|
+
%%Copyright: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
32
|
+
%%Copyright: CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
33
|
+
%%Copyright: INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
34
|
+
%%Copyright: MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
35
|
+
%%Copyright: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
36
|
+
%%Copyright: CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
37
|
+
%%Copyright: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
38
|
+
%%Copyright: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
39
|
+
%%Copyright: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
40
|
+
%%Copyright: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
41
|
+
%%Copyright: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
42
|
+
%%Copyright: OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
43
|
+
%%Copyright: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
44
|
+
%%Copyright: -----------------------------------------------------------
|
|
45
|
+
%%EndComments
|
|
46
|
+
|
|
47
|
+
/CIDInit /ProcSet findresource begin
|
|
48
|
+
|
|
49
|
+
12 dict begin
|
|
50
|
+
|
|
51
|
+
begincmap
|
|
52
|
+
|
|
53
|
+
/H usecmap
|
|
54
|
+
|
|
55
|
+
/CIDSystemInfo 3 dict dup begin
|
|
56
|
+
/Registry (Adobe) def
|
|
57
|
+
/Ordering (Japan1) def
|
|
58
|
+
/Supplement 1 def
|
|
59
|
+
end def
|
|
60
|
+
|
|
61
|
+
/CMapName /V def
|
|
62
|
+
/CMapVersion 12.004 def
|
|
63
|
+
/CMapType 1 def
|
|
64
|
+
|
|
65
|
+
/UIDOffset 850 def
|
|
66
|
+
/XUID [1 10 25340] def
|
|
67
|
+
|
|
68
|
+
/WMode 1 def
|
|
69
|
+
|
|
70
|
+
27 begincidrange
|
|
71
|
+
<2122> <2123> 7887
|
|
72
|
+
<2131> <2132> 7889
|
|
73
|
+
<213c> <213e> 7891
|
|
74
|
+
<2141> <2145> 7894
|
|
75
|
+
<214a> <215b> 7899
|
|
76
|
+
<2161> <2161> 7917
|
|
77
|
+
<2421> <2421> 7918
|
|
78
|
+
<2423> <2423> 7919
|
|
79
|
+
<2425> <2425> 7920
|
|
80
|
+
<2427> <2427> 7921
|
|
81
|
+
<2429> <2429> 7922
|
|
82
|
+
<2443> <2443> 7923
|
|
83
|
+
<2463> <2463> 7924
|
|
84
|
+
<2465> <2465> 7925
|
|
85
|
+
<2467> <2467> 7926
|
|
86
|
+
<246e> <246e> 7927
|
|
87
|
+
<2521> <2521> 7928
|
|
88
|
+
<2523> <2523> 7929
|
|
89
|
+
<2525> <2525> 7930
|
|
90
|
+
<2527> <2527> 7931
|
|
91
|
+
<2529> <2529> 7932
|
|
92
|
+
<2543> <2543> 7933
|
|
93
|
+
<2563> <2563> 7934
|
|
94
|
+
<2565> <2565> 7935
|
|
95
|
+
<2567> <2567> 7936
|
|
96
|
+
<256e> <256e> 7937
|
|
97
|
+
<2575> <2576> 7938
|
|
98
|
+
endcidrange
|
|
99
|
+
endcmap
|
|
100
|
+
CMapName currentdict /CMap defineresource pop
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
%%EndResource
|
|
105
|
+
%%EOF
|
data/examples/arc.rb
CHANGED
|
@@ -17,7 +17,7 @@ radius = 75
|
|
|
17
17
|
|
|
18
18
|
# Left pie chart
|
|
19
19
|
center = [page.box.width * 0.25, page.box.height * 0.85]
|
|
20
|
-
pie = canvas.graphic_object(:solid_arc, cx: center[0], cy: center[1],
|
|
20
|
+
pie = canvas.graphic_object(:solid_arc, cx: center[0], cy: center[1],
|
|
21
21
|
outer_a: radius, outer_b: radius)
|
|
22
22
|
canvas.fill_color('ddddff')
|
|
23
23
|
canvas.draw(pie, start_angle: 30, end_angle: 110).fill
|
|
@@ -26,7 +26,7 @@ canvas.draw(pie, start_angle: 110, end_angle: 130).fill
|
|
|
26
26
|
canvas.fill_color('ddffdd')
|
|
27
27
|
canvas.draw(pie, start_angle: 130, end_angle: 30).fill
|
|
28
28
|
|
|
29
|
-
arc = canvas.graphic_object(:arc, cx: center[0], cy: center[1],
|
|
29
|
+
arc = canvas.graphic_object(:arc, cx: center[0], cy: center[1],
|
|
30
30
|
a: radius, b: radius)
|
|
31
31
|
canvas.stroke_color('0000ff')
|
|
32
32
|
canvas.draw(arc, start_angle: 30, end_angle: 110).stroke
|
|
@@ -38,7 +38,7 @@ canvas.draw(arc, start_angle: 130, end_angle: 30).stroke
|
|
|
38
38
|
# Right pie chart
|
|
39
39
|
center = [page.box.width * 0.75, page.box.height * 0.85]
|
|
40
40
|
canvas.stroke_color('777777')
|
|
41
|
-
pie = canvas.graphic_object(:solid_arc, cx: center[0], cy: center[1],
|
|
41
|
+
pie = canvas.graphic_object(:solid_arc, cx: center[0], cy: center[1],
|
|
42
42
|
outer_a: radius, outer_b: radius)
|
|
43
43
|
canvas.fill_color('ddddff')
|
|
44
44
|
canvas.draw(pie, start_angle: 30, end_angle: 110).fill_stroke
|
data/examples/merging.rb
CHANGED
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
# Sometimes other things like attached files or a document outline
|
|
11
11
|
# should also be preserved.
|
|
12
12
|
#
|
|
13
|
+
# The hexapdf binary provides a command for merging files which does
|
|
14
|
+
# the merging in a more sophisticated way.
|
|
15
|
+
#
|
|
13
16
|
# Usage:
|
|
14
17
|
# : `ruby merging.rb INPUT1.PDF INPUT2.PDF ...`
|
|
15
18
|
#
|
|
@@ -21,4 +24,4 @@ ARGV.each do |file|
|
|
|
21
24
|
pdf = HexaPDF::Document.open(file)
|
|
22
25
|
pdf.pages.each {|page| target.pages << target.import(page)}
|
|
23
26
|
end
|
|
24
|
-
target.write("merging.pdf", optimize: true)
|
|
27
|
+
target.write("2.merging.pdf", optimize: true)
|
data/examples/optimizing.rb
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
# over which aspects should be optimized. See [HexaPDF::Task::Optimize]
|
|
8
8
|
# for detailed information.
|
|
9
9
|
#
|
|
10
|
+
# The hexapdf binary provides an optimization command which does some
|
|
11
|
+
# additional operations like optimizing the page tree.
|
|
12
|
+
#
|
|
10
13
|
# Usage:
|
|
11
14
|
# : `ruby optimizing.rb INPUT.PDF`
|
|
12
15
|
#
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
#
|
|
3
3
|
# This examples shows how to process the contents of a page. It finds all
|
|
4
4
|
# characters on a page and surrounds them with their bounding box. Additionally,
|
|
5
|
-
# all consecutive text runs are also surrounded by box.
|
|
5
|
+
# all consecutive text runs are also surrounded by a box.
|
|
6
6
|
#
|
|
7
7
|
# The code provides two ways of generating the boxes. The commented part of
|
|
8
8
|
# `ShowTextProcessor#show_text` uses a polyline since some characters may be
|
|
9
9
|
# transforemd (rotated or skewed). The un-commented part uses rectangles which
|
|
10
|
-
# is faster and correct
|
|
10
|
+
# is faster and correct for most but not all cases.
|
|
11
11
|
#
|
|
12
12
|
# Usage:
|
|
13
13
|
# : `ruby show_char_boxes.rb INPUT.PDF`
|
data/examples/truetype.rb
CHANGED
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
require 'hexapdf'
|
|
18
18
|
|
|
19
19
|
doc = HexaPDF::Document.new
|
|
20
|
-
doc.config['font.on_missing_glyph'] = ->(
|
|
20
|
+
doc.config['font.on_missing_glyph'] = ->(_, f) { f.missing_glyph_id }
|
|
21
21
|
doc.config['font.map'] = {
|
|
22
|
-
'myfont' => {none: ARGV.shift || File.join(__dir__, '../test/data/fonts/Ubuntu-Title.ttf')}
|
|
22
|
+
'myfont' => {none: ARGV.shift || File.join(__dir__, '../test/data/fonts/Ubuntu-Title.ttf')},
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
wrapper = doc.fonts.load('myfont')
|
data/lib/hexapdf/cli.rb
CHANGED
|
@@ -39,6 +39,7 @@ require 'hexapdf/cli/modify'
|
|
|
39
39
|
require 'hexapdf/cli/merge'
|
|
40
40
|
require 'hexapdf/cli/optimize'
|
|
41
41
|
require 'hexapdf/cli/images'
|
|
42
|
+
require 'hexapdf/cli/batch'
|
|
42
43
|
require 'hexapdf/version'
|
|
43
44
|
require 'hexapdf/document'
|
|
44
45
|
|
|
@@ -59,14 +60,28 @@ module HexaPDF
|
|
|
59
60
|
# The CmdParse::CommandParser class that is used for running the CLI application.
|
|
60
61
|
class Application < CmdParse::CommandParser
|
|
61
62
|
|
|
63
|
+
# Verbosity level for no output
|
|
64
|
+
VERBOSITY_QUIET = 0
|
|
65
|
+
|
|
66
|
+
# Verbosity level for warning output
|
|
67
|
+
VERBOSITY_WARNING = 1
|
|
68
|
+
|
|
69
|
+
# Verbosity level for informational output
|
|
70
|
+
VERBOSITY_INFO = 2
|
|
71
|
+
|
|
62
72
|
# Specifies whether an operation should be forced. For example, if an existing file should be
|
|
63
73
|
# overwritten.
|
|
64
74
|
attr_reader :force
|
|
65
75
|
|
|
76
|
+
# Specifies whether strict parsing and validation should be used.
|
|
77
|
+
attr_reader :strict
|
|
78
|
+
|
|
66
79
|
def initialize #:nodoc:
|
|
67
80
|
super(handle_exceptions: :no_help)
|
|
68
81
|
main_command.options.program_name = "hexapdf"
|
|
69
82
|
main_command.options.version = HexaPDF::VERSION
|
|
83
|
+
main_command.extend(Command::Extensions)
|
|
84
|
+
main_command.define_singleton_method(:usage_commands) { "COMMAND" }
|
|
70
85
|
add_command(HexaPDF::CLI::Info.new)
|
|
71
86
|
add_command(HexaPDF::CLI::Files.new)
|
|
72
87
|
add_command(HexaPDF::CLI::Images.new)
|
|
@@ -74,13 +89,37 @@ module HexaPDF
|
|
|
74
89
|
add_command(HexaPDF::CLI::Modify.new)
|
|
75
90
|
add_command(HexaPDF::CLI::Optimize.new)
|
|
76
91
|
add_command(HexaPDF::CLI::Merge.new)
|
|
92
|
+
add_command(HexaPDF::CLI::Batch.new)
|
|
77
93
|
add_command(CmdParse::HelpCommand.new)
|
|
78
|
-
|
|
94
|
+
version_command = CmdParse::VersionCommand.new(add_switches: false)
|
|
95
|
+
add_command(version_command)
|
|
96
|
+
main_options.on_tail("--version", "Show hexapdf version") { version_command.execute }
|
|
79
97
|
|
|
80
98
|
@force = false
|
|
99
|
+
@verbosity = VERBOSITY_WARNING
|
|
100
|
+
@strict = false
|
|
81
101
|
global_options.on("--[no-]force", "Force overwriting existing files. Default: false") do |f|
|
|
82
102
|
@force = f
|
|
83
103
|
end
|
|
104
|
+
global_options.on("--strict", "Enable strict parsing and validation") do |s|
|
|
105
|
+
@strict = s
|
|
106
|
+
end
|
|
107
|
+
global_options.on("--verbose", "-v", "Verbose output") do
|
|
108
|
+
@verbosity += 1
|
|
109
|
+
end
|
|
110
|
+
global_options.on("--quiet", "-q", "Suppress any output") do
|
|
111
|
+
@verbosity = VERBOSITY_QUIET
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Returns +true+ if the verbosity level warning is enabled.
|
|
116
|
+
def verbosity_warning?
|
|
117
|
+
@verbosity >= VERBOSITY_WARNING
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Returns +true+ if the verbosity level info is enabled.
|
|
121
|
+
def verbosity_info?
|
|
122
|
+
@verbosity >= VERBOSITY_INFO
|
|
84
123
|
end
|
|
85
124
|
|
|
86
125
|
def parse(argv = ARGV) #:nodoc:
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
#--
|
|
4
|
+
# This file is part of HexaPDF.
|
|
5
|
+
#
|
|
6
|
+
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
+
# Copyright (C) 2014-2017 Thomas Leitner
|
|
8
|
+
#
|
|
9
|
+
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
|
+
# under the terms of the GNU Affero General Public License version 3 as
|
|
11
|
+
# published by the Free Software Foundation with the addition of the
|
|
12
|
+
# following permission added to Section 15 as permitted in Section 7(a):
|
|
13
|
+
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
|
|
14
|
+
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
|
|
15
|
+
# INFRINGEMENT OF THIRD PARTY RIGHTS.
|
|
16
|
+
#
|
|
17
|
+
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
|
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
20
|
+
# License for more details.
|
|
21
|
+
#
|
|
22
|
+
# You should have received a copy of the GNU Affero General Public License
|
|
23
|
+
# along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
|
|
24
|
+
#
|
|
25
|
+
# The interactive user interfaces in modified source and object code
|
|
26
|
+
# versions of HexaPDF must display Appropriate Legal Notices, as required
|
|
27
|
+
# under Section 5 of the GNU Affero General Public License version 3.
|
|
28
|
+
#
|
|
29
|
+
# In accordance with Section 7(b) of the GNU Affero General Public
|
|
30
|
+
# License, a covered work must retain the producer line in every PDF that
|
|
31
|
+
# is created or manipulated using HexaPDF.
|
|
32
|
+
#++
|
|
33
|
+
|
|
34
|
+
require 'hexapdf/cli/command'
|
|
35
|
+
require 'shellwords'
|
|
36
|
+
|
|
37
|
+
module HexaPDF
|
|
38
|
+
module CLI
|
|
39
|
+
|
|
40
|
+
# Execute the same command for multiple input files.
|
|
41
|
+
class Batch < Command
|
|
42
|
+
|
|
43
|
+
def initialize #:nodoc:
|
|
44
|
+
super('batch', takes_commands: false)
|
|
45
|
+
short_desc("Execute a single command on multiple files")
|
|
46
|
+
long_desc(<<-EOF.gsub!(/^ */, ''))
|
|
47
|
+
This command allows executing a single command for multiple input files, thereby reducing
|
|
48
|
+
the overall execution time.
|
|
49
|
+
|
|
50
|
+
The first argument is used as a hexapdf command line (but without the binary name) and
|
|
51
|
+
the rest as input files. The specified command will be executed for each input file, with
|
|
52
|
+
{} being replaced by the file name.
|
|
53
|
+
EOF
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def execute(command, *files) #:nodoc:
|
|
57
|
+
args = Shellwords.split(command)
|
|
58
|
+
files.each do |file|
|
|
59
|
+
begin
|
|
60
|
+
HexaPDF::CLI::Application.new.parse(args.map {|a| a.gsub(/{}/, file)})
|
|
61
|
+
rescue
|
|
62
|
+
if command_parser.verbosity_warning?
|
|
63
|
+
$stderr.puts "Error processing '#{file}': #{$!.message}"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
end
|
data/lib/hexapdf/cli/command.rb
CHANGED
|
@@ -35,6 +35,7 @@ require 'io/console'
|
|
|
35
35
|
require 'ostruct'
|
|
36
36
|
require 'cmdparse'
|
|
37
37
|
require 'hexapdf/document'
|
|
38
|
+
require 'hexapdf/font/true_type'
|
|
38
39
|
|
|
39
40
|
module HexaPDF
|
|
40
41
|
module CLI
|
|
@@ -43,6 +44,16 @@ module HexaPDF
|
|
|
43
44
|
# commands.
|
|
44
45
|
class Command < CmdParse::Command
|
|
45
46
|
|
|
47
|
+
module Extensions #:nodoc:
|
|
48
|
+
def help_banner #:nodoc:
|
|
49
|
+
"hexapdf #{HexaPDF::VERSION} - Versatile PDF Manipulation Tool\n" \
|
|
50
|
+
"Copyright (c) 2014-2017 Thomas Leitner; licensed under the AGPLv3\n\n" <<
|
|
51
|
+
format(usage, indent: 7) << "\n\n"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
include Extensions
|
|
56
|
+
|
|
46
57
|
def initialize(*args, &block) #:nodoc:
|
|
47
58
|
super
|
|
48
59
|
@out_options = OpenStruct.new
|
|
@@ -51,6 +62,7 @@ module HexaPDF
|
|
|
51
62
|
@out_options.object_streams = :preserve
|
|
52
63
|
@out_options.xref_streams = :preserve
|
|
53
64
|
@out_options.streams = :preserve
|
|
65
|
+
@out_options.optimize_fonts = false
|
|
54
66
|
|
|
55
67
|
@out_options.encryption = :preserve
|
|
56
68
|
@out_options.enc_user_pwd = @out_options.enc_owner_pwd = nil
|
|
@@ -62,6 +74,57 @@ module HexaPDF
|
|
|
62
74
|
|
|
63
75
|
protected
|
|
64
76
|
|
|
77
|
+
# Creates a HexaPDF::Document instance for the PDF file and yields it.
|
|
78
|
+
#
|
|
79
|
+
# If +out_file+ is given, the document is written to it after yielding.
|
|
80
|
+
def with_document(file, password: nil, out_file: nil) #:yield: document
|
|
81
|
+
if file == out_file
|
|
82
|
+
doc = HexaPDF::Document.open(file, pdf_options(password))
|
|
83
|
+
else
|
|
84
|
+
file_io = File.open(file, 'rb')
|
|
85
|
+
doc = HexaPDF::Document.new(io: file_io, **pdf_options(password))
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
yield(doc)
|
|
89
|
+
|
|
90
|
+
write_document(doc, out_file)
|
|
91
|
+
ensure
|
|
92
|
+
file_io&.close
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Returns a hash with HexaPDF::Document options based on the given password and the option
|
|
96
|
+
# switches.
|
|
97
|
+
def pdf_options(password)
|
|
98
|
+
hash = {decryption_opts: {password: password}, config: {}}
|
|
99
|
+
hash[:config]['parser.on_correctable_error'] =
|
|
100
|
+
if command_parser.strict
|
|
101
|
+
proc { true }
|
|
102
|
+
else
|
|
103
|
+
proc do |_, msg, pos|
|
|
104
|
+
if command_parser.verbosity_info?
|
|
105
|
+
msg = MalformedPDFError.new(msg, pos: pos).message
|
|
106
|
+
$stderr.puts "Corrected parsing problem: #{msg}"
|
|
107
|
+
end
|
|
108
|
+
false
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
hash
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Writes the document to the given file or does nothing if +out_file+ is +nil+.
|
|
115
|
+
def write_document(doc, out_file)
|
|
116
|
+
if out_file
|
|
117
|
+
doc.validate(auto_correct: true) do |msg, correctable|
|
|
118
|
+
if command_parser.strict && !correctable
|
|
119
|
+
raise "Validation error: #{msg}"
|
|
120
|
+
elsif command_parser.verbosity_info?
|
|
121
|
+
$stderr.puts "#{correctable ? 'Corrected' : 'Ignored'} validation problem: #{msg}"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
doc.write(out_file, validate: false)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
65
128
|
# Checks whether the given output file exists and raises an error if it does and
|
|
66
129
|
# HexaPDF::CLI#force is not set.
|
|
67
130
|
def maybe_raise_on_existing_file(filename)
|
|
@@ -98,6 +161,10 @@ module HexaPDF
|
|
|
98
161
|
"time; default: #{@out_options.compress_pages})") do |c|
|
|
99
162
|
@out_options.compress_pages = c
|
|
100
163
|
end
|
|
164
|
+
options.on("--[no-]optimize-fonts", "Optimize embedded font files; " \
|
|
165
|
+
"default: #{@out_options.optimize_fonts})") do |o|
|
|
166
|
+
@out_options.optimize_fonts = o
|
|
167
|
+
end
|
|
101
168
|
end
|
|
102
169
|
|
|
103
170
|
# Defines the encryption options.
|
|
@@ -160,23 +227,37 @@ module HexaPDF
|
|
|
160
227
|
object_streams: @out_options.object_streams,
|
|
161
228
|
xref_streams: @out_options.xref_streams,
|
|
162
229
|
compress_pages: @out_options.compress_pages)
|
|
163
|
-
|
|
230
|
+
if @out_options.streams != :preserve || @out_options.optimize_fonts
|
|
231
|
+
doc.each(current: false) do |obj|
|
|
232
|
+
optimize_stream(obj)
|
|
233
|
+
optimize_font(obj)
|
|
234
|
+
end
|
|
235
|
+
end
|
|
164
236
|
end
|
|
165
237
|
|
|
166
238
|
IGNORED_FILTERS = { #:nodoc:
|
|
167
239
|
CCITTFaxDecode: true, JBIG2Decode: true, DCTDecode: true, JPXDecode: true, Crypt: true
|
|
168
240
|
}.freeze
|
|
169
241
|
|
|
170
|
-
# Applies the chosen stream mode to
|
|
171
|
-
def
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
242
|
+
# Applies the chosen stream mode to the given object.
|
|
243
|
+
def optimize_stream(obj)
|
|
244
|
+
return if @out_options.streams == :preserve || !obj.respond_to?(:set_filter) ||
|
|
245
|
+
Array(obj[:Filter]).any? {|f| IGNORED_FILTERS[f]}
|
|
246
|
+
|
|
247
|
+
obj.set_filter(@out_options.streams == :compress ? :FlateDecode : nil)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Optimize the object if it is a font object.
|
|
251
|
+
def optimize_font(obj)
|
|
252
|
+
return unless @out_options.optimize_fonts && obj.kind_of?(HexaPDF::Type::Font) &&
|
|
253
|
+
(obj[:Subtype] == :TrueType ||
|
|
254
|
+
(obj[:Subtype] == :Type0 && obj.descendant_font[:Subtype] == :CIDFontType2)) &&
|
|
255
|
+
obj.embedded?
|
|
256
|
+
|
|
257
|
+
font = HexaPDF::Font::TrueType::Font.new(StringIO.new(obj.font_file.stream))
|
|
258
|
+
data = HexaPDF::Font::TrueType::Optimizer.build_for_pdf(font)
|
|
259
|
+
obj.font_file.stream = data
|
|
260
|
+
obj.font_file[:Length1] = data.size
|
|
180
261
|
end
|
|
181
262
|
|
|
182
263
|
# Applies the encryption related options to the given HexaPDF::Document instance.
|
|
@@ -199,7 +280,9 @@ module HexaPDF
|
|
|
199
280
|
ROTATE_MAP = {'l' => -90, 'r' => 90, 'd' => 180, 'n' => :none}.freeze #:nodoc:
|
|
200
281
|
|
|
201
282
|
# Parses the pages specification string and returns an array of tuples containing a page
|
|
202
|
-
# number and a rotation value (either -90, 90, 180 or
|
|
283
|
+
# number and a rotation value (either -90, 90, 180, :none or +nil+ where an integer means
|
|
284
|
+
# adding a rotation by that number of degrees, :none means removing any set rotation value and
|
|
285
|
+
# +nil+ means preserving the set rotation value).
|
|
203
286
|
#
|
|
204
287
|
# The parameter +count+ needs to be the total number of pages in the document.
|
|
205
288
|
#
|
|
@@ -208,10 +291,12 @@ module HexaPDF
|
|
|
208
291
|
range.split(',').each_with_object([]) do |str, arr|
|
|
209
292
|
case str
|
|
210
293
|
when /\A#{PAGE_NUMBER_SPEC}(l|r|d|n)?\z/o
|
|
211
|
-
|
|
294
|
+
page_num = ($1 == 'e' ? count : str.to_i)
|
|
295
|
+
next if page_num > count
|
|
296
|
+
arr << [page_num - 1, ROTATE_MAP[$2]]
|
|
212
297
|
when /\A#{PAGE_NUMBER_SPEC}-#{PAGE_NUMBER_SPEC}(?:\/([1-9]\d*))?(l|r|d|n)?\z/
|
|
213
|
-
start_nr = ($1 == 'e' ? count : $1.to_i) - 1
|
|
214
|
-
end_nr = ($2 == 'e' ? count : $2.to_i) - 1
|
|
298
|
+
start_nr = ($1 == 'e' ? count : [$1.to_i, count].min) - 1
|
|
299
|
+
end_nr = ($2 == 'e' ? count : [$2.to_i, count].min) - 1
|
|
215
300
|
step = ($3 ? $3.to_i : 1) * (start_nr > end_nr ? -1 : 1)
|
|
216
301
|
rotation = ROTATE_MAP[$4]
|
|
217
302
|
start_nr.step(to: end_nr, by: step) {|n| arr << [n, rotation]}
|
|
@@ -235,6 +320,18 @@ module HexaPDF
|
|
|
235
320
|
end
|
|
236
321
|
end
|
|
237
322
|
|
|
323
|
+
# Removes unused pages and page tree nodes from the document.
|
|
324
|
+
def remove_unused_pages(doc)
|
|
325
|
+
retained = doc.pages.each_with_object({}) {|page, h| h[page.data] = true}
|
|
326
|
+
retained[doc.pages.root.data] = true
|
|
327
|
+
doc.each(current: false) do |obj|
|
|
328
|
+
next unless obj.kind_of?(HexaPDF::Dictionary)
|
|
329
|
+
if (obj.type == :Pages || obj.type == :Page) && !retained.key?(obj.data)
|
|
330
|
+
doc.delete(obj)
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
238
335
|
private
|
|
239
336
|
|
|
240
337
|
# Displays the given prompt, reads from the console without echo and returns the read string.
|