hexapdf 0.1.0 → 0.2.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.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +56 -0
  3. data/CONTRIBUTERS +1 -1
  4. data/Rakefile +3 -3
  5. data/VERSION +1 -1
  6. data/examples/arc.rb +1 -1
  7. data/examples/graphics.rb +1 -1
  8. data/examples/hello_world.rb +1 -1
  9. data/examples/merging.rb +1 -1
  10. data/examples/show_char_bboxes.rb +1 -1
  11. data/examples/standard_pdf_fonts.rb +1 -1
  12. data/examples/truetype.rb +1 -1
  13. data/lib/hexapdf/cli.rb +14 -7
  14. data/lib/hexapdf/cli/extract.rb +1 -1
  15. data/lib/hexapdf/cli/info.rb +2 -2
  16. data/lib/hexapdf/cli/inspect.rb +4 -4
  17. data/lib/hexapdf/cli/modify.rb +151 -51
  18. data/lib/hexapdf/configuration.rb +1 -1
  19. data/lib/hexapdf/content/canvas.rb +1 -1
  20. data/lib/hexapdf/content/processor.rb +1 -1
  21. data/lib/hexapdf/dictionary.rb +6 -19
  22. data/lib/hexapdf/dictionary_fields.rb +1 -1
  23. data/lib/hexapdf/document.rb +23 -16
  24. data/lib/hexapdf/document/files.rb +130 -0
  25. data/lib/hexapdf/{font_utils.rb → document/fonts.rb} +40 -38
  26. data/lib/hexapdf/document/images.rb +117 -0
  27. data/lib/hexapdf/document/pages.rb +125 -0
  28. data/lib/hexapdf/encryption/aes.rb +1 -1
  29. data/lib/hexapdf/encryption/ruby_aes.rb +10 -10
  30. data/lib/hexapdf/encryption/standard_security_handler.rb +11 -8
  31. data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
  32. data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -6
  33. data/lib/hexapdf/font/cmap/writer.rb +5 -7
  34. data/lib/hexapdf/font/true_type.rb +4 -1
  35. data/lib/hexapdf/font/true_type/font.rb +8 -16
  36. data/lib/hexapdf/font/true_type/table.rb +5 -16
  37. data/lib/hexapdf/font/true_type/table/cmap.rb +2 -7
  38. data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +2 -6
  39. data/lib/hexapdf/font/true_type/table/directory.rb +0 -5
  40. data/lib/hexapdf/font/true_type/table/glyf.rb +3 -11
  41. data/lib/hexapdf/font/true_type/table/head.rb +0 -12
  42. data/lib/hexapdf/font/true_type/table/hhea.rb +0 -7
  43. data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -5
  44. data/lib/hexapdf/font/true_type/table/loca.rb +0 -4
  45. data/lib/hexapdf/font/true_type/table/maxp.rb +0 -8
  46. data/lib/hexapdf/font/true_type/table/name.rb +3 -17
  47. data/lib/hexapdf/font/true_type/table/os2.rb +0 -14
  48. data/lib/hexapdf/font/true_type/table/post.rb +0 -8
  49. data/lib/hexapdf/font/true_type_wrapper.rb +1 -1
  50. data/lib/hexapdf/font/type1.rb +2 -2
  51. data/lib/hexapdf/font/type1/font.rb +2 -1
  52. data/lib/hexapdf/font/type1/font_metrics.rb +10 -1
  53. data/lib/hexapdf/font/type1_wrapper.rb +2 -1
  54. data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
  55. data/lib/hexapdf/font_loader/standard14.rb +1 -1
  56. data/lib/hexapdf/image_loader/jpeg.rb +1 -1
  57. data/lib/hexapdf/image_loader/pdf.rb +1 -1
  58. data/lib/hexapdf/image_loader/png.rb +2 -2
  59. data/lib/hexapdf/object.rb +18 -5
  60. data/lib/hexapdf/rectangle.rb +8 -1
  61. data/lib/hexapdf/revisions.rb +4 -2
  62. data/lib/hexapdf/serializer.rb +3 -3
  63. data/lib/hexapdf/stream.rb +3 -2
  64. data/lib/hexapdf/task/dereference.rb +4 -5
  65. data/lib/hexapdf/task/optimize.rb +6 -3
  66. data/lib/hexapdf/tokenizer.rb +3 -3
  67. data/lib/hexapdf/type/file_specification.rb +2 -2
  68. data/lib/hexapdf/type/form.rb +19 -0
  69. data/lib/hexapdf/type/page.rb +21 -6
  70. data/lib/hexapdf/type/page_tree_node.rb +27 -34
  71. data/lib/hexapdf/utils/bit_stream.rb +1 -1
  72. data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
  73. data/lib/hexapdf/version.rb +1 -1
  74. data/man/man1/hexapdf.1 +259 -187
  75. data/test/hexapdf/content/graphic_object/test_arc.rb +1 -1
  76. data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +1 -1
  77. data/test/hexapdf/content/graphic_object/test_solid_arc.rb +1 -1
  78. data/test/hexapdf/content/test_canvas.rb +1 -1
  79. data/test/hexapdf/document/test_files.rb +71 -0
  80. data/test/hexapdf/{test_font_utils.rb → document/test_fonts.rb} +1 -2
  81. data/test/hexapdf/document/test_images.rb +78 -0
  82. data/test/hexapdf/document/test_pages.rb +114 -0
  83. data/test/hexapdf/encryption/test_standard_security_handler.rb +26 -5
  84. data/test/hexapdf/font/test_true_type_wrapper.rb +1 -1
  85. data/test/hexapdf/font/true_type/common.rb +0 -4
  86. data/test/hexapdf/font/true_type/table/test_cmap.rb +0 -6
  87. data/test/hexapdf/font/true_type/table/test_directory.rb +0 -5
  88. data/test/hexapdf/font/true_type/table/test_glyf.rb +5 -8
  89. data/test/hexapdf/font/true_type/table/test_head.rb +0 -20
  90. data/test/hexapdf/font/true_type/table/test_hhea.rb +0 -7
  91. data/test/hexapdf/font/true_type/table/test_hmtx.rb +2 -7
  92. data/test/hexapdf/font/true_type/table/test_loca.rb +4 -8
  93. data/test/hexapdf/font/true_type/table/test_maxp.rb +0 -7
  94. data/test/hexapdf/font/true_type/table/test_name.rb +0 -19
  95. data/test/hexapdf/font/true_type/table/test_os2.rb +0 -8
  96. data/test/hexapdf/font/true_type/table/test_post.rb +0 -13
  97. data/test/hexapdf/font/true_type/test_font.rb +14 -38
  98. data/test/hexapdf/font/true_type/test_table.rb +0 -9
  99. data/test/hexapdf/font/type1/test_font_metrics.rb +22 -0
  100. data/test/hexapdf/task/test_dereference.rb +5 -1
  101. data/test/hexapdf/task/test_optimize.rb +1 -1
  102. data/test/hexapdf/test_dictionary.rb +4 -0
  103. data/test/hexapdf/test_document.rb +0 -7
  104. data/test/hexapdf/test_importer.rb +4 -4
  105. data/test/hexapdf/test_object.rb +31 -9
  106. data/test/hexapdf/test_rectangle.rb +18 -0
  107. data/test/hexapdf/test_revisions.rb +7 -0
  108. data/test/hexapdf/test_serializer.rb +6 -0
  109. data/test/hexapdf/test_writer.rb +2 -2
  110. data/test/hexapdf/type/test_form.rb +12 -0
  111. data/test/hexapdf/type/test_page.rb +39 -20
  112. data/test/hexapdf/type/test_page_tree_node.rb +28 -21
  113. metadata +21 -9
  114. data/lib/hexapdf/document_utils.rb +0 -209
  115. data/test/hexapdf/test_document_utils.rb +0 -144
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'test_helper'
4
4
  require 'hexapdf/rectangle'
5
+ require 'hexapdf/document'
5
6
 
6
7
  describe HexaPDF::Rectangle do
7
8
  describe "after_data_change" do
@@ -33,4 +34,21 @@ describe HexaPDF::Rectangle do
33
34
  assert_equal(2, rect.width)
34
35
  assert_equal(4, rect.height)
35
36
  end
37
+
38
+ describe "validation" do
39
+ it "ensures that it is a correct PDF rectangle" do
40
+ doc = HexaPDF::Document.new
41
+ rect = HexaPDF::Rectangle.new([0, 1, 2, 3], document: doc)
42
+ assert(rect.validate)
43
+
44
+ rect.value.shift
45
+ refute(rect.validate)
46
+
47
+ rect.value.unshift(:A)
48
+ refute(rect.validate)
49
+
50
+ rect.data.value = :A
51
+ refute(rect.validate)
52
+ end
53
+ end
36
54
  end
@@ -82,6 +82,13 @@ EOF
82
82
  assert_equal(1, @revisions.each.to_a.size)
83
83
  assert_equal([10, 200], @revisions.current.each.to_a.sort.map(&:value))
84
84
  end
85
+
86
+ it "handles objects correctly that are in multiple revisions" do
87
+ @revisions.current.add(@revisions[0].object(1))
88
+ @revisions.merge
89
+ assert_equal(1, @revisions.each.to_a.size)
90
+ assert_equal([10, 200], @revisions.current.each.to_a.sort.map(&:value))
91
+ end
85
92
  end
86
93
 
87
94
  describe "initialize" do
@@ -25,6 +25,12 @@ describe HexaPDF::Serializer do
25
25
  assert_equal(result, @serializer.serialize(object))
26
26
  end
27
27
 
28
+ it "works correctly with unnamed classes and modules" do
29
+ klass = Class.new(String)
30
+ s = klass.new("test")
31
+ assert_serialized("(test)", s)
32
+ end
33
+
28
34
  it "serializes nil" do
29
35
  assert_serialized("null", nil)
30
36
  end
@@ -40,7 +40,7 @@ startxref
40
40
  219
41
41
  %%EOF
42
42
  3 0 obj
43
- <</Producer(HexaPDF version 0.1.0)>>
43
+ <</Producer(HexaPDF version 0.2.0)>>
44
44
  endobj
45
45
  xref
46
46
  3 1
@@ -72,7 +72,7 @@ startxref
72
72
  141
73
73
  %%EOF
74
74
  6 0 obj
75
- <</Producer(HexaPDF version 0.1.0)>>
75
+ <</Producer(HexaPDF version 0.2.0)>>
76
76
  endobj
77
77
  2 0 obj
78
78
  <</Length 10>>stream
@@ -57,4 +57,16 @@ describe HexaPDF::Type::Form do
57
57
  assert_equal([[:set_line_width, [10]]], processor.recorded_ops)
58
58
  end
59
59
  end
60
+
61
+ describe "canvas" do
62
+ it "always returns the same Canvas instance" do
63
+ canvas = @form.canvas
64
+ assert_same(canvas, @form.canvas)
65
+ end
66
+
67
+ it "fails if the form XObject already has data" do
68
+ @form.stream = '10 w'
69
+ assert_raises(HexaPDF::Error) { @form.canvas }
70
+ end
71
+ end
60
72
  end
@@ -49,13 +49,11 @@ describe HexaPDF::Type::Page do
49
49
 
50
50
  @kid[:Rotate] = :kid_rotate
51
51
  assert_equal(:kid_rotate, @page[:Rotate])
52
- @kid.delete(:Rotate)
53
- assert_equal(0, @page[:Rotate])
54
52
  end
55
53
 
56
- it "fails if no parent node is associated" do
57
- page = @doc.add(Type: :Page)
58
- assert_raises(HexaPDF::InvalidPDFObjectError) { page[:Resources] }
54
+ it "returns nil or the default value if no value is set anywhere" do
55
+ assert_nil(@page[:MediaBox])
56
+ assert_equal(0, @page[:Rotate])
59
57
  end
60
58
  end
61
59
 
@@ -71,7 +69,7 @@ describe HexaPDF::Type::Page do
71
69
 
72
70
  describe "box" do
73
71
  before do
74
- @page = @doc.pages.add_page
72
+ @page = @doc.pages.add
75
73
  end
76
74
 
77
75
  it "returns the correct media box" do
@@ -106,13 +104,13 @@ describe HexaPDF::Type::Page do
106
104
 
107
105
  describe "contents" do
108
106
  it "returns the contents of a single content stream" do
109
- page = @doc.pages.add_page
107
+ page = @doc.pages.add
110
108
  page[:Contents] = @doc.wrap({}, stream: 'q 10 w Q')
111
109
  assert_equal('q 10 w Q', page.contents)
112
110
  end
113
111
 
114
112
  it "returns the concatenated contents of multiple content stream" do
115
- page = @doc.pages.add_page
113
+ page = @doc.pages.add
116
114
  page[:Contents] = [@doc.wrap({}, stream: 'q 10'), @doc.wrap({}, stream: 'w Q')]
117
115
  assert_equal('q 10 w Q', page.contents)
118
116
  end
@@ -120,13 +118,13 @@ describe HexaPDF::Type::Page do
120
118
 
121
119
  describe "contents=" do
122
120
  it "creates a content stream if none already exist" do
123
- page = @doc.pages.add_page
121
+ page = @doc.pages.add
124
122
  page.contents = 'test'
125
123
  assert_equal('test', page[:Contents].stream)
126
124
  end
127
125
 
128
126
  it "reuses an existing content stream" do
129
- page = @doc.pages.add_page
127
+ page = @doc.pages.add
130
128
  page[:Contents] = content = @doc.wrap({}, stream: 'q 10 w Q')
131
129
  page.contents = 'test'
132
130
  assert_equal(content, page[:Contents])
@@ -134,7 +132,7 @@ describe HexaPDF::Type::Page do
134
132
  end
135
133
 
136
134
  it "reuses the first content stream and deletes the rest if more than one exist" do
137
- page = @doc.pages.add_page
135
+ page = @doc.pages.add
138
136
  page[:Contents] = [content = @doc.add({}, stream: 'q 10 w Q'), @doc.add({}, stream: 'q Q')]
139
137
  page.contents = 'test'
140
138
  assert_equal(content, page[:Contents])
@@ -144,33 +142,54 @@ describe HexaPDF::Type::Page do
144
142
 
145
143
  describe "resources" do
146
144
  it "creates the resource dictionary if it is not found" do
147
- page = @doc.add(Type: :Page, Parent: @doc.pages)
145
+ page = @doc.add(Type: :Page, Parent: @doc.pages.root)
148
146
  resources = page.resources
149
147
  assert_equal(:XXResources, resources.type)
150
148
  assert_equal({}, resources.value)
151
149
  end
152
150
 
153
151
  it "returns the already used resource dictionary" do
154
- @doc.pages[:Resources] = {Font: {F1: nil}}
155
- page = @doc.pages.add_page(@doc.add(Type: :Page))
152
+ @doc.pages.root[:Resources] = {Font: {F1: nil}}
153
+ page = @doc.pages.add(@doc.add(Type: :Page))
156
154
  resources = page.resources
157
155
  assert_equal(:XXResources, resources.type)
158
- assert_equal(@doc.pages[:Resources], resources)
156
+ assert_equal(@doc.pages.root[:Resources], resources)
159
157
  end
160
158
  end
161
159
 
162
160
  describe "process_contents" do
163
161
  it "parses the contents and processes it" do
164
- page = @doc.pages.add_page
162
+ page = @doc.pages.add
165
163
  page[:Contents] = @doc.wrap({}, stream: 'q 10 w Q')
166
164
  assert_operators(page, [[:save_graphics_state], [:set_line_width, [10]],
167
165
  [:restore_graphics_state]])
168
166
  end
169
167
  end
170
168
 
169
+ describe "index" do
170
+ it "returns the index of the page in the page tree" do
171
+ kid1 = @doc.add(Type: :Pages, Parent: @doc.pages.root, Count: 4)
172
+ @doc.pages.root[:Kids] << kid1
173
+
174
+ kid11 = @doc.add(Type: :Pages, Parent: kid1)
175
+ page1 = kid11.add_page
176
+ kid1[:Kids] << kid11
177
+ page2 = kid1.add_page
178
+ kid12 = @doc.add(Type: :Pages, Parent: kid1)
179
+ page3 = kid12.add_page
180
+ page4 = kid12.add_page
181
+ kid1[:Kids] << kid12
182
+
183
+ assert_equal(0, page1.index)
184
+ assert_equal(1, page2.index)
185
+ assert_equal(2, page3.index)
186
+ assert_equal(3, page4.index)
187
+ end
188
+ end
189
+
171
190
  describe "canvas" do
172
191
  before do
173
- @page = @doc.pages.add_page
192
+ @page = @doc.pages.add
174
193
  end
175
194
 
176
195
  it "works correctly if invoked on an empty page, using type :page in first invocation" do
@@ -234,7 +253,7 @@ describe HexaPDF::Type::Page do
234
253
 
235
254
  describe "to_form_xobject" do
236
255
  it "creates an independent form xobject" do
237
- page = @doc.pages.add_page
256
+ page = @doc.pages.add
238
257
  page.contents = "test"
239
258
  form = page.to_form_xobject
240
259
  refute(form.indirect?)
@@ -242,13 +261,13 @@ describe HexaPDF::Type::Page do
242
261
  end
243
262
 
244
263
  it "works for pages without content" do
245
- page = @doc.pages.add_page
264
+ page = @doc.pages.add
246
265
  form = page.to_form_xobject
247
266
  assert_equal('', form.stream)
248
267
  end
249
268
 
250
269
  it "uses the raw stream data if possible to avoid unnecessary work" do
251
- page = @doc.pages.add_page
270
+ page = @doc.pages.add
252
271
  page.contents = HexaPDF::StreamData.new(StringIO.new("test"))
253
272
  form = page.to_form_xobject
254
273
  assert_same(form.raw_stream, page[:Contents].raw_stream)
@@ -151,40 +151,47 @@ describe HexaPDF::Type::PageTreeNode do
151
151
  define_multilevel_page_tree
152
152
  end
153
153
 
154
- it "does nothing if the page index is not valid" do
155
- assert_nil(@root.delete_page(20))
156
- assert_nil(@root.delete_page(-20))
157
- assert_equal(8, @root.page_count)
158
- end
159
-
160
- it "deletes the correct page" do
154
+ it "deletes the correct page by index" do
161
155
  assert_equal(@pages[2], @root.delete_page(2))
162
156
  assert_equal(2, @kid12.page_count)
163
157
  assert_equal(4, @kid1.page_count)
164
158
  assert_equal(7, @root.page_count)
159
+ assert_nil(@pages[2][:Parent])
165
160
 
166
161
  assert_equal(@pages[5], @root.delete_page(4))
167
162
  assert_equal(6, @root.page_count)
163
+ assert_nil(@pages[5][:Parent])
168
164
  end
169
165
 
170
- it "deletes intermediate page tree nodes if they contain only one child after deletion" do
171
- assert_equal(@pages[0], @root.delete_page(0))
172
- assert_equal(4, @kid1.page_count)
166
+ it "deletes the given page" do
167
+ assert_equal(@pages[2], @root.delete_page(@pages[2]))
168
+ assert_equal(@pages[5], @root.delete_page(@pages[5]))
169
+ end
170
+
171
+ it "allows deleting a page from an intermediary node" do
172
+ assert_equal(@pages[2], @kid1.delete_page(@pages[2]))
173
173
  assert_equal(7, @root.page_count)
174
- assert_nil(@doc.object(@kid11).value)
175
- assert_equal(@pages[1], @kid1[:Kids][0])
176
174
  end
177
175
 
178
- it "deletes intermediate page tree nodes if they don't have any children after deletion" do
179
- node = @doc.add(Type: :Pages, Parent: @root)
180
- page = node.add_page
181
- @root[:Kids] << node
182
- @root[:Count] += 1
176
+ it "does nothing if the page index is not valid" do
177
+ assert_nil(@root.delete_page(20))
178
+ assert_nil(@root.delete_page(-20))
179
+ assert_equal(8, @root.page_count)
180
+ end
183
181
 
184
- assert_equal(page, @root.delete_page(-1))
185
- assert_nil(@doc.object(node).value)
186
- refute_equal(node, @root[:Kids].last)
187
- assert(8, @root.page_count)
182
+ it "does nothing if the page is not in its parent's /Kids array" do
183
+ @kid12[:Kids].shift
184
+ assert_nil(@root.delete_page(@pages[2]))
185
+ assert_equal(8, @root.page_count)
186
+ end
187
+
188
+ it "does nothing if the page is not part of the page tree" do
189
+ pages = @doc.add(Type: :Pages, Count: 1)
190
+ page = @doc.add(Type: :Page, Parent: pages)
191
+ pages[:Kids] << page
192
+
193
+ assert_nil(@root.delete_page(page))
194
+ assert_equal(8, @root.page_count)
188
195
  end
189
196
  end
190
197
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hexapdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Leitner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-26 00:00:00.000000000 Z
11
+ date: 2016-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cmdparse
@@ -31,19 +31,25 @@ dependencies:
31
31
  - !ruby/object:Gem::Version
32
32
  version: 3.0.1
33
33
  - !ruby/object:Gem::Dependency
34
- name: ronn
34
+ name: kramdown
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '0.7'
39
+ version: '1.0'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.13.0
40
43
  type: :development
41
44
  prerelease: false
42
45
  version_requirements: !ruby/object:Gem::Requirement
43
46
  requirements:
44
47
  - - "~>"
45
48
  - !ruby/object:Gem::Version
46
- version: '0.7'
49
+ version: '1.0'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.13.0
47
53
  description: |-
48
54
  HexaPDF is a pure Ruby library with an accompanying application for working with PDF files.
49
55
 
@@ -56,6 +62,7 @@ executables:
56
62
  extensions: []
57
63
  extra_rdoc_files: []
58
64
  files:
65
+ - CHANGELOG.md
59
66
  - CONTRIBUTERS
60
67
  - LICENSE
61
68
  - README.md
@@ -112,7 +119,10 @@ files:
112
119
  - lib/hexapdf/dictionary.rb
113
120
  - lib/hexapdf/dictionary_fields.rb
114
121
  - lib/hexapdf/document.rb
115
- - lib/hexapdf/document_utils.rb
122
+ - lib/hexapdf/document/files.rb
123
+ - lib/hexapdf/document/fonts.rb
124
+ - lib/hexapdf/document/images.rb
125
+ - lib/hexapdf/document/pages.rb
116
126
  - lib/hexapdf/encryption.rb
117
127
  - lib/hexapdf/encryption/aes.rb
118
128
  - lib/hexapdf/encryption/arc4.rb
@@ -173,7 +183,6 @@ files:
173
183
  - lib/hexapdf/font_loader.rb
174
184
  - lib/hexapdf/font_loader/from_configuration.rb
175
185
  - lib/hexapdf/font_loader/standard14.rb
176
- - lib/hexapdf/font_utils.rb
177
186
  - lib/hexapdf/image_loader.rb
178
187
  - lib/hexapdf/image_loader/jpeg.rb
179
188
  - lib/hexapdf/image_loader/pdf.rb
@@ -306,6 +315,10 @@ files:
306
315
  - test/hexapdf/content/test_parser.rb
307
316
  - test/hexapdf/content/test_processor.rb
308
317
  - test/hexapdf/content/test_transformation_matrix.rb
318
+ - test/hexapdf/document/test_files.rb
319
+ - test/hexapdf/document/test_fonts.rb
320
+ - test/hexapdf/document/test_images.rb
321
+ - test/hexapdf/document/test_pages.rb
309
322
  - test/hexapdf/encryption/common.rb
310
323
  - test/hexapdf/encryption/test_aes.rb
311
324
  - test/hexapdf/encryption/test_arc4.rb
@@ -350,6 +363,7 @@ files:
350
363
  - test/hexapdf/font/true_type/test_table.rb
351
364
  - test/hexapdf/font/type1/test_afm_parser.rb
352
365
  - test/hexapdf/font/type1/test_font.rb
366
+ - test/hexapdf/font/type1/test_font_metrics.rb
353
367
  - test/hexapdf/font/type1/test_pfb_parser.rb
354
368
  - test/hexapdf/font_loader/test_from_configuration.rb
355
369
  - test/hexapdf/font_loader/test_standard14.rb
@@ -363,9 +377,7 @@ files:
363
377
  - test/hexapdf/test_dictionary.rb
364
378
  - test/hexapdf/test_dictionary_fields.rb
365
379
  - test/hexapdf/test_document.rb
366
- - test/hexapdf/test_document_utils.rb
367
380
  - test/hexapdf/test_filter.rb
368
- - test/hexapdf/test_font_utils.rb
369
381
  - test/hexapdf/test_importer.rb
370
382
  - test/hexapdf/test_object.rb
371
383
  - test/hexapdf/test_parser.rb
@@ -1,209 +0,0 @@
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) 2016 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/configuration'
35
-
36
- module HexaPDF
37
-
38
- # This class provides utility functions for PDF documents. It is available through the
39
- # HexaPDF::Document#utils method.
40
- #
41
- # Some functions can't be attributed to a single "manager" object. For example, while embedding a
42
- # file can be done within a HexaPDF::Type::Filespecification object, loading an image from a file
43
- # as a PDF object doesn't have such a place. Such functions are available via this class.
44
- class DocumentUtils
45
-
46
- # This module provides methods for managing the images embedded in a PDF file; images
47
- # themselves are represented by the HexaPDF::Type::Image class.
48
- #
49
- # Since an image can be used as a mask for another image, not all image objects found in a PDF
50
- # are really used as images. Such cases are all handled by this class automatically.
51
- module Images
52
-
53
- # :call-seq:
54
- # images.add_image(file) -> image
55
- # images.add_image(io) -> image
56
- #
57
- # Adds the image from the given file or IO to the PDF and returns the image object.
58
- #
59
- # If the image has been added to the PDF before (i.e. if there is an image object with the
60
- # same path name), the already existing image object is returned.
61
- def add_image(file_or_io)
62
- name = if file_or_io.kind_of?(String)
63
- file_or_io
64
- elsif file_or_io.respond_to?(:to_path)
65
- file_or_io.to_path
66
- end
67
- if name
68
- name = File.absolute_path(name)
69
- image = each_image.find {|im| im.source_path == name}
70
- end
71
- unless image
72
- image = image_loader_for(file_or_io).load(@document, file_or_io)
73
- image.source_path = name
74
- end
75
- image
76
- end
77
-
78
- # :call-seq:
79
- # images.each_image {|image| block } -> images
80
- # images.each_image -> Enumerator
81
- #
82
- # Iterates over all images in the PDF.
83
- #
84
- # Note that only real images are yielded which means, for example, that images used as soft
85
- # mask are not.
86
- def each_image(&block)
87
- images = @document.each(current: false).select do |obj|
88
- obj[:Subtype] == :Image && !obj[:ImageMask]
89
- end
90
- masks = images.each_with_object([]) do |image, temp|
91
- temp << image[:Mask] if image[:Mask].kind_of?(Stream)
92
- temp << image[:SMask] if image[:SMask].kind_of?(Stream)
93
- end
94
- (images - masks).each(&block)
95
- end
96
-
97
- private
98
-
99
- # Returns the image loader (see ImageLoader) for the given file or IO stream or raises an
100
- # error if no suitable image loader is found.
101
- def image_loader_for(file_or_io)
102
- GlobalConfiguration['image_loader'].each_index do |index|
103
- loader = GlobalConfiguration.constantize('image_loader', index) do
104
- raise HexaPDF::Error, "Couldn't retrieve image loader from configuration"
105
- end
106
- return loader if loader.handles?(file_or_io)
107
- end
108
-
109
- raise HexaPDF::Error, "Couldn't find suitable image loader"
110
- end
111
-
112
- end
113
-
114
-
115
- # This module provides methods for managing file specification of a PDF file.
116
- #
117
- # Note that for a given PDF file not all file specifications may be found, e.g. when a file
118
- # specification is only a string. Therefore this module can only handle those file
119
- # specifications that are indirect file specification dictionaries with the /Type key set.
120
- module Files
121
-
122
- # :call-seq:
123
- # files.add_file(filename, name: File.basename(filename), description: nil, embed: true) -> file_spec
124
- # files.add_file(io, name:, description: nil) -> file_spec
125
- #
126
- # Adds the file or IO to the PDF and returns the corresponding file specification object.
127
- #
128
- # Options:
129
- #
130
- # name::
131
- # The name that should be used for the file path. This name is also for registering the
132
- # file in the EmbeddedFiles name tree.
133
- #
134
- # description::
135
- # A description of the file.
136
- #
137
- # embed::
138
- # When an IO object is given, it is always embedded and this option is ignored.
139
- #
140
- # When a filename is given and this option is +true+, then the file is embedded. Otherwise
141
- # only a reference to it is stored.
142
- #
143
- # See: HexaPDF::Type::FileSpecification
144
- def add_file(file_or_io, name: nil, description: nil, embed: true)
145
- name ||= File.basename(file_or_io) if file_or_io.kind_of?(String)
146
- if name.nil?
147
- raise ArgumentError, "The name argument is mandatory when given an IO object"
148
- end
149
-
150
- spec = @document.add(Type: :Filespec)
151
- spec.path = name
152
- spec[:Desc] = description if description
153
- spec.embed(file_or_io, name: name, register: true) if embed || !file_or_io.kind_of?(String)
154
- spec
155
- end
156
-
157
- # :call-seq:
158
- # files.each_file(search: false) {|file_spec| block } -> files
159
- # files.each_file(search: false) -> Enumerator
160
- #
161
- # Iterates over indirect file specification dictionaries of the PDF.
162
- #
163
- # By default, only the file specifications in their standard locations, namely in the
164
- # EmbeddedFiles name tree and in the page annotations, are returned. If the +search+ option is
165
- # +true+, then all indirect objects are searched for file specification dictionaries which is
166
- # much slower.
167
- def each_file(search: false)
168
- return to_enum(__method__, search: search) unless block_given?
169
-
170
- if search
171
- @document.each(current: false) do |obj|
172
- yield(obj) if obj.type == :Filespec
173
- end
174
- else
175
- seen = {}
176
- tree = @document.catalog[:Names] && @document.catalog[:Names][:EmbeddedFiles]
177
- tree.each_entry do |_, spec|
178
- seen[spec] = true
179
- yield(spec)
180
- end if tree
181
-
182
- @document.pages.each_page do |page|
183
- next unless page[:Annots]
184
- page[:Annots].each do |annot|
185
- annot = @document.deref(annot)
186
- next unless annot[:Subtype] == :FileAttachment
187
- spec = @document.deref(annot[:FS])
188
- yield(spec) unless seen.key?(spec)
189
- seen[spec] = true
190
- end
191
- end
192
- end
193
-
194
- self
195
- end
196
-
197
- end
198
-
199
- include Images
200
- include Files
201
-
202
- # Creates a new DocumentUtils object for the given PDF document.
203
- def initialize(document)
204
- @document = document
205
- end
206
-
207
- end
208
-
209
- end