hexapdf 0.18.0 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
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