hexapdf 0.18.0 → 0.19.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -2
  3. data/lib/hexapdf/cli/command.rb +7 -1
  4. data/lib/hexapdf/content/canvas.rb +2 -2
  5. data/lib/hexapdf/content/graphics_state.rb +144 -21
  6. data/lib/hexapdf/dictionary_fields.rb +1 -1
  7. data/lib/hexapdf/task/optimize.rb +46 -3
  8. data/lib/hexapdf/version.rb +1 -1
  9. data/test/data/cert/create.sh +171 -0
  10. data/test/data/cert/root-ca/certs/84E66B6F4C359E741C0AFA014790DF39.pem +119 -0
  11. data/test/data/cert/root-ca/certs/84E66B6F4C359E741C0AFA014790DF3A.pem +125 -0
  12. data/test/data/cert/root-ca/db/crlnumber +1 -0
  13. data/test/data/cert/root-ca/db/index +2 -0
  14. data/test/data/cert/root-ca/db/index.attr +1 -0
  15. data/test/data/cert/root-ca/db/index.attr.old +1 -0
  16. data/test/data/cert/root-ca/db/index.old +1 -0
  17. data/test/data/cert/root-ca/db/serial +1 -0
  18. data/test/data/cert/root-ca/db/serial.old +1 -0
  19. data/test/data/cert/root-ca/private/root-ca.key +52 -0
  20. data/test/data/cert/root-ca/root-ca.conf +65 -0
  21. data/test/data/cert/root-ca/root-ca.crt +119 -0
  22. data/test/data/cert/root-ca/root-ca.csr +28 -0
  23. data/test/data/cert/signature-1-pkcs7-detached.pdf +182 -0
  24. data/test/data/cert/sub-ca/certs/453FF080E3EDCD6A388D5368DFC320D9.pem +125 -0
  25. data/test/data/cert/sub-ca/db/crlnumber +1 -0
  26. data/test/data/cert/sub-ca/db/index +1 -0
  27. data/test/data/cert/sub-ca/db/index.attr +1 -0
  28. data/test/data/cert/sub-ca/db/index.old +0 -0
  29. data/test/data/cert/sub-ca/db/serial +1 -0
  30. data/test/data/cert/sub-ca/db/serial.old +1 -0
  31. data/test/data/cert/sub-ca/private/signing.key +52 -0
  32. data/test/data/cert/sub-ca/private/sub-ca.key +52 -0
  33. data/test/data/cert/sub-ca/signing.crt +125 -0
  34. data/test/data/cert/sub-ca/signing.csr +28 -0
  35. data/test/data/cert/sub-ca/signing.p12 +0 -0
  36. data/test/data/cert/sub-ca/sub-ca.conf +65 -0
  37. data/test/data/cert/sub-ca/sub-ca.crt +125 -0
  38. data/test/data/cert/sub-ca/sub-ca.csr +28 -0
  39. data/test/hexapdf/task/test_optimize.rb +26 -0
  40. data/test/hexapdf/test_dictionary_fields.rb +1 -0
  41. data/test/hexapdf/test_writer.rb +2 -2
  42. metadata +32 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d95ce1575c017f44b2c0f96e7e5a927b8c4f8c3adf6aa0f3a7dc983c5dfa77a8
4
- data.tar.gz: e49a23655e5ce4f4ded50c5ac0c90d7892c41bd526dbdfe72ad85cca4891098b
3
+ metadata.gz: 55ddbf5c737ab446c5793660957e64aaf6f8872c6bb1c4a76ba39a87193b951f
4
+ data.tar.gz: eb29fbb41cbb20c060c43cc80406bd4b11fa36b0d86a95daa9d1093d060ab89e
5
5
  SHA512:
6
- metadata.gz: 37e3b09a059bb7875c797f50f60642b080f20a081fa2540f74837ac51a8cb9c1aa5b93e6502dd9f242d178f68ea654c0619b2e55d4cb934e36badca4a5057d1c
7
- data.tar.gz: cd9fc830b8b4f5387478d7e6c32260672a9332bb2faf3aa7afcb4d71bf7c39481616f7f33d731229a6099e44dac84bbecd651c3618593f6c7ba872b9e958a3cb
6
+ metadata.gz: 28368cd415cd9adf28050df8812a0cda999b8140fcba4a9f307d7e37f6dc0254d99abf5173a81a1b2b59f9f6a658a46f6998d7834b93ffc19840d578e507ce92
7
+ data.tar.gz: b25e409653a4bd9359f3a39c2920187261a43151942c4120d2bd818886dbf236f77483a42ca7a7fb39ffbcdfc7da8cf9d7a87609a4f4af514cf7f93f495eb5ed
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## 0.19.0 - 2021-11-24
2
+
3
+ ### Added
4
+
5
+ * Page resource pruning to the optimization task
6
+ * An option for page resources pruning to the optimization options of the
7
+ `hexapdf` command
8
+
9
+ ### Fixed
10
+
11
+ * Handling of invalid date strings with a minute time zone offset greater than
12
+ 59
13
+
14
+
1
15
  ## 0.18.0 - 2021-11-04
2
16
 
3
17
  ### Added
@@ -6,7 +20,7 @@
6
20
  device colors in parts other than the canvas
7
21
  * [HexaPDF::Type::AcroForm::VariableTextField::create_appearance_string] for
8
22
  centralized creation of appearance strings
9
- * [HexaPDF::Object.make_direct] for making objects and all parts of them direct
23
+ * [HexaPDF::Object::make_direct] for making objects and all parts of them direct
10
24
  instead of indirect
11
25
 
12
26
  ### Changed
@@ -26,7 +40,7 @@
26
40
  dictionary are indirect objects
27
41
  * [HexaPDF::Content::GraphicObject::EndpointArc] to correctly determine the
28
42
  start and end points
29
- * [HexaPDF::Dictionary#perform_validation] to correctly handle objects that
43
+ * HexaPDF::Dictionary#perform_validation to correctly handle objects that
30
44
  should not be indirect objects
31
45
 
32
46
 
@@ -66,6 +66,7 @@ module HexaPDF
66
66
  @out_options.xref_streams = :preserve
67
67
  @out_options.streams = :preserve
68
68
  @out_options.optimize_fonts = false
69
+ @out_options.prune_page_resources = false
69
70
 
70
71
  @out_options.encryption = :preserve
71
72
  @out_options.enc_user_pwd = @out_options.enc_owner_pwd = nil
@@ -169,6 +170,10 @@ module HexaPDF
169
170
  "time; default: #{@out_options.compress_pages})") do |c|
170
171
  @out_options.compress_pages = c
171
172
  end
173
+ options.on("--[no-]prune-page-resources", "Prunes unused objects from the page resources " \
174
+ "(may take a long time; default: #{@out_options.prune_page_resources})") do |c|
175
+ @out_options.prune_page_resources = c
176
+ end
172
177
  options.on("--[no-]optimize-fonts", "Optimize embedded font files; " \
173
178
  "default: #{@out_options.optimize_fonts})") do |o|
174
179
  @out_options.optimize_fonts = o
@@ -236,7 +241,8 @@ module HexaPDF
236
241
  doc.task(:optimize, compact: @out_options.compact,
237
242
  object_streams: @out_options.object_streams,
238
243
  xref_streams: @out_options.xref_streams,
239
- compress_pages: @out_options.compress_pages)
244
+ compress_pages: @out_options.compress_pages,
245
+ prune_page_resources: @out_options.prune_page_resources)
240
246
  if @out_options.streams != :preserve || @out_options.optimize_fonts
241
247
  doc.each(only_current: false) do |obj|
242
248
  optimize_stream(obj)
@@ -589,7 +589,7 @@ module HexaPDF
589
589
  #
590
590
  # The line cap style specifies how the ends of stroked open paths should look like.
591
591
  #
592
- # The +style+ parameter can be one of:
592
+ # The +style+ parameter can be one of (also see LineCapStyle):
593
593
  #
594
594
  # :butt or 0::
595
595
  # Stroke is squared off at the endpoint of a path.
@@ -641,7 +641,7 @@ module HexaPDF
641
641
  #
642
642
  # The line join style specifies the shape that is used at the corners of stroked paths.
643
643
  #
644
- # The +style+ parameter can be one of:
644
+ # The +style+ parameter can be one of (also see LineJoinStyle):
645
645
  #
646
646
  # :miter or 0::
647
647
  # The outer lines of the two segments continue until the meet at an angle.
@@ -73,7 +73,7 @@ module HexaPDF
73
73
  end
74
74
 
75
75
  # Defines all available line cap styles as constants. Each line cap style is an instance of
76
- # NamedValue. For use with Content::GraphicsState#line_cap_style.
76
+ # NamedValue, see ::normalize. For use with e.g. Content::Canvas#line_cap_style.
77
77
  #
78
78
  # See: PDF1.7 s8.4.3.3
79
79
  module LineCapStyle
@@ -95,18 +95,39 @@ module HexaPDF
95
95
  end
96
96
 
97
97
  # Stroke is squared off at the endpoint of a path.
98
+ #
99
+ # Specify as 0 or :butt.
100
+ #
101
+ # #>pdf-small-hide
102
+ # canvas.line_cap_style(:butt)
103
+ # canvas.line_width(10).line(50, 20, 50, 80).stroke
104
+ # canvas.stroke_color("white").line_width(1).line(50, 20, 50, 80).stroke
98
105
  BUTT_CAP = NamedValue.new(:butt, 0)
99
106
 
100
107
  # A semicircular arc is drawn at the endpoint of a path.
108
+ #
109
+ # Specify as 1 or :round.
110
+ #
111
+ # #>pdf-small-hide
112
+ # canvas.line_cap_style(:round)
113
+ # canvas.line_width(10).line(50, 20, 50, 80).stroke
114
+ # canvas.stroke_color("white").line_width(1).line(50, 20, 50, 80).stroke
101
115
  ROUND_CAP = NamedValue.new(:round, 1)
102
116
 
103
117
  # The stroke continues half the line width beyond the endpoint of a path.
118
+ #
119
+ # Specify as 2 or :projecting_square.
120
+ #
121
+ # #>pdf-small-hide
122
+ # canvas.line_cap_style(:projecting_square)
123
+ # canvas.line_width(10).line(50, 20, 50, 80).stroke
124
+ # canvas.stroke_color("white").line_width(1).line(50, 20, 50, 80).stroke
104
125
  PROJECTING_SQUARE_CAP = NamedValue.new(:projecting_square, 2)
105
126
 
106
127
  end
107
128
 
108
129
  # Defines all available line join styles as constants. Each line join style is an instance of
109
- # NamedValue. For use with Content::GraphicsState#line_join_style.
130
+ # NamedValue, see ::normalize For use with e.g. Content::Canvas#line_join_style.
110
131
  #
111
132
  # See: PDF1.7 s8.4.3.4
112
133
  module LineJoinStyle
@@ -127,20 +148,47 @@ module HexaPDF
127
148
  end
128
149
  end
129
150
 
130
- # The outer lines of the two segments continue until the meet at an angle.
151
+ # The outer lines of the two segments continue until they meet at an angle.
152
+ #
153
+ # Specify as 0 or :miter.
154
+ #
155
+ # #>pdf-small-hide
156
+ # canvas.line_join_style(:miter)
157
+ # canvas.line_width(10).
158
+ # polyline(20, 20, 50, 80, 80, 20).stroke
159
+ # canvas.stroke_color("white").line_width(1).line_join_style(:bevel).
160
+ # polyline(20, 20, 50, 80, 80, 20).stroke
131
161
  MITER_JOIN = NamedValue.new(:miter, 0)
132
162
 
133
163
  # An arc of a circle is drawn around the point where the segments meet.
164
+ #
165
+ # Specify as 1 or :round.
166
+ #
167
+ # #>pdf-small-hide
168
+ # canvas.line_join_style(:round)
169
+ # canvas.line_width(10).
170
+ # polyline(20, 20, 50, 80, 80, 20).stroke
171
+ # canvas.stroke_color("white").line_width(1).line_join_style(:bevel).
172
+ # polyline(20, 20, 50, 80, 80, 20).stroke
134
173
  ROUND_JOIN = NamedValue.new(:round, 1)
135
174
 
136
- # The two segments are finished with butt caps and the space between the ends is filled with
137
- # a triangle.
175
+ # The two segments are finished with butt caps and the space between the ends is filled with a
176
+ # triangle.
177
+ #
178
+ # Specify as 2 or :bevel.
179
+ #
180
+ # #>pdf-small-hide
181
+ # canvas.line_join_style(:bevel)
182
+ # canvas.line_width(10).
183
+ # polyline(20, 20, 50, 80, 80, 20).stroke
184
+ # canvas.stroke_color("white").line_width(1).line_join_style(:bevel).
185
+ # polyline(20, 20, 50, 80, 80, 20).stroke
138
186
  BEVEL_JOIN = NamedValue.new(:bevel, 2)
139
187
 
140
188
  end
141
189
 
142
- # The line dash pattern defines how a line should be dashed. For use with
143
- # Content::GraphicsState#line_dash_pattern.
190
+ # The line dash pattern defines how a line should be dashed. For use with e.g.
191
+ # Content::Canvas#line_dash_pattern.
144
192
  #
145
193
  # A dash pattern consists of two parts: the dash array and the dash phase. The dash array
146
194
  # defines the length of alternating dashes and gaps (important: starting with dashes). And the
@@ -159,6 +207,12 @@ module HexaPDF
159
207
  # See: PDF1.7 s8.4.3.6
160
208
  class LineDashPattern
161
209
 
210
+ # :call-seq:
211
+ # LineDashPattern.normalize(line_dash_pattern) -> line_dash_pattern
212
+ # LineDashPattern.normalize(array, phase = 0) -> LineDashPattern.new(array, phase)
213
+ # LineDashPattern.normalize(number, phase = 0) -> LineDashPattern.new([number], phase)
214
+ # LineDashPattern.normalize(0) -> LineDashPattern.new
215
+ #
162
216
  # Returns the arguments normalized to a valid LineDashPattern instance.
163
217
  #
164
218
  # If +array+ is 0, the default line dash pattern representing a solid line will be used. If it
@@ -206,8 +260,8 @@ module HexaPDF
206
260
 
207
261
  end
208
262
 
209
- # Defines all available rendering intents as constants. For use with
210
- # Content::GraphicsState#rendering_intent.
263
+ # Defines all available rendering intents as constants. For use with e.g.
264
+ # Content::Canvas#rendering_intent.
211
265
  #
212
266
  # See: PDF1.7 s8.6.5.8
213
267
  module RenderingIntent
@@ -241,7 +295,7 @@ module HexaPDF
241
295
  end
242
296
 
243
297
  # Defines all available text rendering modes as constants. Each text rendering mode is an
244
- # instance of NamedValue. For use with Content::GraphicsState#text_rendering_mode.
298
+ # instance of NamedValue. For use with e.g. Content::Canvas#text_rendering_mode.
245
299
  #
246
300
  # See: PDF1.7 s9.3.6
247
301
  module TextRenderingMode
@@ -272,28 +326,97 @@ module HexaPDF
272
326
  end
273
327
  end
274
328
 
275
- # Fill text
329
+ # Fill text.
330
+ #
331
+ # Specify as 0 or :fill.
332
+ #
333
+ # #>pdf-small-hide
334
+ # canvas.font("Helvetica", size: 13)
335
+ # canvas.stroke_color("green").line_width(0.5)
336
+ # canvas.text_rendering_mode(:fill)
337
+ # canvas.text("#{canvas.text_rendering_mode.name}", at: [10, 50])
276
338
  FILL = NamedValue.new(:fill, 0)
277
339
 
278
- # Stroke text
340
+ # Stroke text.
341
+ #
342
+ # Specify as 1 or :stroke.
343
+ #
344
+ # #>pdf-small-hide
345
+ # canvas.font("Helvetica", size: 13)
346
+ # canvas.stroke_color("green").line_width(0.5)
347
+ # canvas.text_rendering_mode(:stroke)
348
+ # canvas.text("#{canvas.text_rendering_mode.name}", at: [10, 50])
279
349
  STROKE = NamedValue.new(:stroke, 1)
280
350
 
281
- # Fill, then stroke text
351
+ # Fill, then stroke text.
352
+ #
353
+ # Specify as 2 or :fill_stroke.
354
+ #
355
+ # #>pdf-small-hide
356
+ # canvas.font("Helvetica", size: 13)
357
+ # canvas.stroke_color("green").line_width(0.5)
358
+ # canvas.text_rendering_mode(:fill_stroke)
359
+ # canvas.text("#{canvas.text_rendering_mode.name}", at: [10, 50])
282
360
  FILL_STROKE = NamedValue.new(:fill_stroke, 2)
283
361
 
284
- # Neither fill nor stroke text (invisible)
362
+ # Neither fill nor stroke text (invisible).
363
+ #
364
+ # Specify as 3 or :invisible.
365
+ #
366
+ # #>pdf-small-hide
367
+ # canvas.font("Helvetica", size: 13)
368
+ # canvas.stroke_color("green").line_width(0.5)
369
+ # canvas.text_rendering_mode(:invisible)
370
+ # canvas.text("#{canvas.text_rendering_mode.name}", at: [10, 50])
371
+ # canvas.stroke_color("red").line_width(20).line(30, 20, 30, 80).stroke
285
372
  INVISIBLE = NamedValue.new(:invisible, 3)
286
373
 
287
- # Fill text and add to path for clipping
374
+ # Fill text and add to path for clipping.
375
+ #
376
+ # Specify as 4 or :fill_clip.
377
+ #
378
+ # #>pdf-small-hide
379
+ # canvas.font("Helvetica", size: 13)
380
+ # canvas.stroke_color("green").line_width(0.5)
381
+ # canvas.text_rendering_mode(:fill_clip)
382
+ # canvas.text("#{canvas.text_rendering_mode.name}", at: [10, 50])
383
+ # canvas.stroke_color("red").line_width(20).line(30, 20, 30, 80).stroke
288
384
  FILL_CLIP = NamedValue.new(:fill_clip, 4)
289
385
 
290
- # Stroke text and add to path for clipping
386
+ # Stroke text and add to path for clipping.
387
+ #
388
+ # Specify as 5 or :stroke_clip.
389
+ #
390
+ # #>pdf-small-hide
391
+ # canvas.font("Helvetica", size: 13)
392
+ # canvas.stroke_color("green").line_width(0.5)
393
+ # canvas.text_rendering_mode(:stroke_clip)
394
+ # canvas.text("#{canvas.text_rendering_mode.name}", at: [10, 50])
395
+ # canvas.stroke_color("red").line_width(20).line(30, 20, 30, 80).stroke
291
396
  STROKE_CLIP = NamedValue.new(:stroke_clip, 5)
292
397
 
293
- # Fill, then stroke text and add to path for clipping
398
+ # Fill, then stroke text and add to path for clipping.
399
+ #
400
+ # Specify as 6 or :fill_stroke_clip.
401
+ #
402
+ # #>pdf-small-hide
403
+ # canvas.font("Helvetica", size: 13)
404
+ # canvas.stroke_color("green").line_width(0.5)
405
+ # canvas.text_rendering_mode(:fill_stroke_clip)
406
+ # canvas.text("#{canvas.text_rendering_mode.name}", at: [10, 50])
407
+ # canvas.stroke_color("red").line_width(20).line(30, 20, 30, 80).stroke
294
408
  FILL_STROKE_CLIP = NamedValue.new(:fill_stroke_clip, 6)
295
409
 
296
- # Add text to path for clipping
410
+ # Add text to path for clipping.
411
+ #
412
+ # Specify as 7 or :clip.
413
+ #
414
+ # #>pdf-small-hide
415
+ # canvas.font("Helvetica", size: 13)
416
+ # canvas.stroke_color("green").line_width(0.5)
417
+ # canvas.text_rendering_mode(:clip)
418
+ # canvas.text("#{canvas.text_rendering_mode.name}", at: [10, 50])
419
+ # canvas.stroke_color("red").line_width(20).line(30, 20, 30, 80).stroke
297
420
  CLIP = NamedValue.new(:clip, 7)
298
421
 
299
422
  end
@@ -415,21 +538,21 @@ module HexaPDF
415
538
 
416
539
  # The scaled character spacing used in glyph displacement calculations.
417
540
  #
418
- # This returns the value T_c multiplied by #scaled_horizontal_scaling.
541
+ # This returns the character spacing multiplied by #scaled_horizontal_scaling.
419
542
  #
420
543
  # See PDF1.7 s9.4.4
421
544
  attr_reader :scaled_character_spacing
422
545
 
423
546
  # The scaled word spacing used in glyph displacement calculations.
424
547
  #
425
- # This returns the value T_w multiplied by #scaled_horizontal_scaling.
548
+ # This returns the word spacing multiplied by #scaled_horizontal_scaling.
426
549
  #
427
550
  # See PDF1.7 s9.4.4
428
551
  attr_reader :scaled_word_spacing
429
552
 
430
553
  # The scaled font size used in glyph displacement calculations.
431
554
  #
432
- # This returns the value T_fs / 1000 multiplied by #scaled_horizontal_scaling.
555
+ # This returns the font size divided by 1000 multiplied by #scaled_horizontal_scaling.
433
556
  #
434
557
  # See PDF1.7 s9.4.4
435
558
  attr_reader :scaled_font_size
@@ -293,7 +293,7 @@ module HexaPDF
293
293
  end
294
294
 
295
295
  # :nodoc:
296
- DATE_RE = /\AD:(\d{4})(\d\d)?(\d\d)?(\d\d)?(\d\d)?(\d\d)?([Z+-])?(?:(\d\d)(?:'|'(\d\d)'?|\z)?)?\z/n
296
+ DATE_RE = /\AD:(\d{4})(\d\d)?(\d\d)?(\d\d)?(\d\d)?(\d\d)?([Z+-])?(?:(\d\d)(?:'|'([0-5]\d)'?|\z)?)?\z/n
297
297
 
298
298
  # Checks if the given object is a string and converts into a Time object if possible.
299
299
  # Otherwise returns +nil+.
@@ -72,8 +72,19 @@ module HexaPDF
72
72
  # Compresses the content streams of all pages if set to +true+. Note that this can take a
73
73
  # *very* long time because each content stream has to be unfiltered, parsed, serialized
74
74
  # and then filtered again.
75
+ #
76
+ # prune_page_resources::
77
+ # Removes all unused XObjects from the resources dictionaries of all pages. It is
78
+ # recommended to also set the +compact+ argument because otherwise the unused XObjects won't
79
+ # be deleted from the document.
80
+ #
81
+ # This is sometimes necessary after importing pages from other PDF files that use a single
82
+ # resources dictionary for all pages.
75
83
  def self.call(doc, compact: false, object_streams: :preserve, xref_streams: :preserve,
76
- compress_pages: false)
84
+ compress_pages: false, prune_page_resources: false)
85
+ used_refs = compress_pages(doc) if compress_pages
86
+ prune_page_resources(doc, used_refs) if prune_page_resources
87
+
77
88
  if compact
78
89
  compact(doc, object_streams, xref_streams)
79
90
  elsif object_streams != :preserve
@@ -83,8 +94,6 @@ module HexaPDF
83
94
  else
84
95
  doc.each(only_current: false, &method(:delete_fields_with_defaults))
85
96
  end
86
-
87
- compress_pages(doc) if compress_pages
88
97
  end
89
98
 
90
99
  # Compacts the document by merging all revisions into one, deleting null and unused entries
@@ -214,12 +223,41 @@ module HexaPDF
214
223
 
215
224
  # Compresses the contents of all pages by parsing and then serializing again. The HexaPDF
216
225
  # serializer is already optimized for small output size so nothing else needs to be done.
226
+ #
227
+ # Returns a hash of the form key=>true where the keys are the used XObjects (for use with
228
+ # #prune_page_resources).
217
229
  def self.compress_pages(doc)
230
+ used_refs = {}
218
231
  doc.pages.each do |page|
219
232
  processor = SerializationProcessor.new
220
233
  HexaPDF::Content::Parser.parse(page.contents, processor)
221
234
  page.contents = processor.result
222
235
  page[:Contents].set_filter(:FlateDecode)
236
+ xobjects = page.resources[:XObject]
237
+ processor.used_references.each {|ref| used_refs[xobjects[ref]] = true }
238
+ end
239
+ used_refs
240
+ end
241
+
242
+ # Deletes all XObject entries from the resources dictionaries of all pages whose names do not
243
+ # match the keys in +used_refs+.
244
+ def self.prune_page_resources(doc, used_refs)
245
+ unless used_refs
246
+ used_refs = {}
247
+ doc.pages.each do |page|
248
+ xobjects = page.resources[:XObject]
249
+ HexaPDF::Content::Parser.parse(page.contents) do |op, operands|
250
+ used_refs[xobjects[operands[0]]] = true if op == :Do
251
+ end
252
+ end
253
+ end
254
+
255
+ doc.pages.each do |page|
256
+ xobjects = page.resources[:XObject]
257
+ xobjects.each do |key, obj|
258
+ next if used_refs[obj]
259
+ xobjects.delete(key)
260
+ end
223
261
  end
224
262
  end
225
263
 
@@ -228,14 +266,19 @@ module HexaPDF
228
266
 
229
267
  attr_reader :result #:nodoc:
230
268
 
269
+ # Contains all found references
270
+ attr_reader :used_references
271
+
231
272
  def initialize #:nodoc:
232
273
  @result = ''.b
233
274
  @serializer = HexaPDF::Serializer.new
275
+ @used_references = []
234
276
  end
235
277
 
236
278
  def process(op, operands) #:nodoc:
237
279
  @result << HexaPDF::Content::Operator::DEFAULT_OPERATORS[op].
238
280
  serialize(@serializer, *operands)
281
+ @used_references << operands[0] if op == :Do
239
282
  end
240
283
 
241
284
  end
@@ -37,6 +37,6 @@
37
37
  module HexaPDF
38
38
 
39
39
  # The version of HexaPDF.
40
- VERSION = '0.18.0'
40
+ VERSION = '0.19.0'
41
41
 
42
42
  end
@@ -0,0 +1,171 @@
1
+ #!/bin/sh
2
+ # See https://www.feistyduck.com/library/openssl-cookbook/online/ch-openssl.html
3
+
4
+
5
+ #####################################3
6
+ # Root CA
7
+ mkdir root-ca
8
+ cd root-ca
9
+ mkdir certs db private
10
+ touch db/index
11
+ openssl rand -hex 16 > db/serial
12
+ echo 1001 > db/crlnumber
13
+
14
+ cat > root-ca.conf <<'CACONF'
15
+ [default]
16
+ name = root-ca
17
+ domain_suffix = hexapdf.gettalong.org
18
+ aia_url = http://$name.$domain_suffix/$name.crt
19
+ crl_url = http://$name.$domain_suffix/$name.crl
20
+ default_ca = ca_default
21
+ name_opt = utf8,esc_ctrl,multiline,lname,align
22
+
23
+ [ca_dn]
24
+ countryName = "AT"
25
+ organizationName = "HexaPDF"
26
+ commonName = "HexaPDF Test Root CA"
27
+
28
+ [ca_default]
29
+ home = ../root-ca
30
+ database = $home/db/index
31
+ serial = $home/db/serial
32
+ crlnumber = $home/db/crlnumber
33
+ certificate = $home/$name.crt
34
+ private_key = $home/private/$name.key
35
+ RANDFILE = $home/private/random
36
+ new_certs_dir = $home/certs
37
+ unique_subject = no
38
+ copy_extensions = none
39
+ default_days = 36500
40
+ default_crl_days = 365
41
+ default_md = sha256
42
+ policy = policy_c_o_match
43
+
44
+ [policy_c_o_match]
45
+ countryName = match
46
+ stateOrProvinceName = optional
47
+ organizationName = match
48
+ organizationalUnitName = optional
49
+ commonName = supplied
50
+ emailAddress = optional
51
+
52
+ [req]
53
+ default_bits = 4096
54
+ encrypt_key = no
55
+ default_md = sha256
56
+ utf8 = yes
57
+ string_mask = utf8only
58
+ prompt = no
59
+ distinguished_name = ca_dn
60
+ req_extensions = ca_ext
61
+
62
+ [ca_ext]
63
+ basicConstraints = critical,CA:true
64
+ keyUsage = critical,keyCertSign,cRLSign
65
+ subjectKeyIdentifier = hash
66
+
67
+ [sub_ca_ext]
68
+ authorityKeyIdentifier = keyid:always
69
+ basicConstraints = critical,CA:true,pathlen:0
70
+ extendedKeyUsage = clientAuth,serverAuth
71
+ keyUsage = critical,keyCertSign,cRLSign
72
+ subjectKeyIdentifier = hash
73
+
74
+ [client_ext]
75
+ authorityKeyIdentifier = keyid:always
76
+ basicConstraints = critical,CA:false
77
+ extendedKeyUsage = clientAuth
78
+ keyUsage = critical,digitalSignature
79
+ subjectKeyIdentifier = hash
80
+ CACONF
81
+
82
+ openssl req -new -config root-ca.conf -out root-ca.csr -keyout private/root-ca.key
83
+ openssl ca -selfsign -config root-ca.conf -batch -in root-ca.csr -out root-ca.crt -extensions ca_ext
84
+
85
+ #####################################3
86
+ # Subordinate CA
87
+ cd ..
88
+ mkdir sub-ca
89
+ cd sub-ca
90
+ mkdir certs db private
91
+ touch db/index
92
+ openssl rand -hex 16 > db/serial
93
+ echo 1001 > db/crlnumber
94
+
95
+ cat > sub-ca.conf <<'CACONF'
96
+ [default]
97
+ name = sub-ca
98
+ domain_suffix = hexapdf.gettalong.org
99
+ aia_url = http://$name.$domain_suffix/$name.crt
100
+ crl_url = http://$name.$domain_suffix/$name.crl
101
+ default_ca = ca_default
102
+ name_opt = utf8,esc_ctrl,multiline,lname,align
103
+
104
+ [ca_dn]
105
+ countryName = "AT"
106
+ organizationName = "HexaPDF"
107
+ commonName = "HexaPDF Test Subordinate CA"
108
+
109
+ [ca_default]
110
+ home = ../sub-ca
111
+ database = $home/db/index
112
+ serial = $home/db/serial
113
+ crlnumber = $home/db/crlnumber
114
+ certificate = $home/$name.crt
115
+ private_key = $home/private/$name.key
116
+ RANDFILE = $home/private/random
117
+ new_certs_dir = $home/certs
118
+ unique_subject = no
119
+ copy_extensions = copy
120
+ default_days = 36500
121
+ default_crl_days = 90
122
+ default_md = sha256
123
+ policy = policy_c_o_match
124
+
125
+ [policy_c_o_match]
126
+ countryName = match
127
+ stateOrProvinceName = optional
128
+ organizationName = match
129
+ organizationalUnitName = optional
130
+ commonName = supplied
131
+ emailAddress = optional
132
+
133
+ [req]
134
+ default_bits = 4096
135
+ encrypt_key = no
136
+ default_md = sha256
137
+ utf8 = yes
138
+ string_mask = utf8only
139
+ prompt = no
140
+ distinguished_name = ca_dn
141
+ req_extensions = ca_ext
142
+
143
+ [ca_ext]
144
+ basicConstraints = critical,CA:true
145
+ keyUsage = critical,keyCertSign,cRLSign
146
+ subjectKeyIdentifier = hash
147
+
148
+ [sub_ca_ext]
149
+ authorityKeyIdentifier = keyid:always
150
+ basicConstraints = critical,CA:true,pathlen:0
151
+ extendedKeyUsage = clientAuth,serverAuth
152
+ keyUsage = critical,keyCertSign,cRLSign
153
+ subjectKeyIdentifier = hash
154
+
155
+ [client_ext]
156
+ authorityKeyIdentifier = keyid:always
157
+ basicConstraints = critical,CA:false
158
+ extendedKeyUsage = clientAuth
159
+ keyUsage = critical,digitalSignature
160
+ subjectKeyIdentifier = hash
161
+ CACONF
162
+
163
+ openssl req -new -config sub-ca.conf -out sub-ca.csr -keyout private/sub-ca.key
164
+ openssl ca -config ../root-ca/root-ca.conf -batch -in sub-ca.csr -out sub-ca.crt -extensions sub_ca_ext
165
+
166
+
167
+ #####################################3
168
+ # Signing certificate
169
+ openssl req -new -config sub-ca.conf -subj "/C=AT/O=HexaPDF/CN=HexaPDF Test Certifcate" -keyout private/signing.key -out signing.csr
170
+ openssl ca -config sub-ca.conf -in signing.csr -batch -out signing.crt -extensions client_ext
171
+ openssl pkcs12 -export -in signing.crt -inkey private/signing.key -password pass: -out signing.p12