hexapdf 0.9.3 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/CONTRIBUTERS +2 -1
  4. data/VERSION +1 -1
  5. data/lib/hexapdf/cli/command.rb +9 -5
  6. data/lib/hexapdf/cli/images.rb +68 -13
  7. data/lib/hexapdf/cli/inspect.rb +201 -71
  8. data/lib/hexapdf/content/canvas.rb +1 -1
  9. data/lib/hexapdf/dictionary.rb +15 -1
  10. data/lib/hexapdf/dictionary_fields.rb +5 -4
  11. data/lib/hexapdf/document.rb +15 -6
  12. data/lib/hexapdf/encryption/security_handler.rb +3 -2
  13. data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +165 -165
  14. data/lib/hexapdf/font/true_type_wrapper.rb +2 -2
  15. data/lib/hexapdf/font/type1/afm_parser.rb +1 -1
  16. data/lib/hexapdf/image_loader/jpeg.rb +13 -11
  17. data/lib/hexapdf/reference.rb +5 -0
  18. data/lib/hexapdf/revision.rb +14 -0
  19. data/lib/hexapdf/serializer.rb +6 -6
  20. data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
  21. data/lib/hexapdf/type/image.rb +3 -1
  22. data/lib/hexapdf/version.rb +1 -1
  23. data/lib/hexapdf/xref_section.rb +8 -0
  24. data/man/man1/hexapdf.1 +88 -20
  25. data/test/data/images/truecolour-alpha-8bit.png +0 -0
  26. data/test/data/images/ycck.jpg +0 -0
  27. data/test/hexapdf/content/test_canvas.rb +2 -2
  28. data/test/hexapdf/document/test_images.rb +7 -5
  29. data/test/hexapdf/encryption/test_security_handler.rb +13 -0
  30. data/test/hexapdf/image_loader/test_jpeg.rb +10 -0
  31. data/test/hexapdf/image_loader/test_png.rb +3 -2
  32. data/test/hexapdf/test_dictionary.rb +9 -0
  33. data/test/hexapdf/test_dictionary_fields.rb +15 -0
  34. data/test/hexapdf/test_document.rb +5 -2
  35. data/test/hexapdf/test_reference.rb +4 -0
  36. data/test/hexapdf/test_revision.rb +12 -0
  37. data/test/hexapdf/test_writer.rb +5 -5
  38. data/test/hexapdf/test_xref_section.rb +11 -0
  39. data/test/hexapdf/type/test_form.rb +1 -1
  40. data/test/hexapdf/type/test_image.rb +28 -19
  41. metadata +3 -2
@@ -237,9 +237,9 @@ module HexaPDF
237
237
  end
238
238
 
239
239
  fd.flag(:fixed_pitch) if @wrapped_font[:post].is_fixed_pitch? ||
240
- @wrapped_font[:hhea].num_of_long_hor_metrics == 1
240
+ @wrapped_font[:hhea].num_of_long_hor_metrics == 1
241
241
  fd.flag(:italic) if @wrapped_font[:'OS/2'].selection_include?(:italic) ||
242
- @wrapped_font[:'OS/2'].selection_include?(:oblique)
242
+ @wrapped_font[:'OS/2'].selection_include?(:oblique)
243
243
  fd.flag(:symbolic)
244
244
 
245
245
  cid_font = @document.add(Type: :Font, Subtype: :CIDFontType2,
@@ -128,7 +128,7 @@ module HexaPDF
128
128
  when :CharacterSet then @metrics.character_set = parse_string
129
129
  when :EncodingScheme then @metrics.encoding_scheme = parse_string
130
130
  when :Weight then @metrics.weight = parse_string
131
- when :FontBBox then
131
+ when :FontBBox
132
132
  @metrics.bounding_box = [parse_number, parse_number, parse_number, parse_number]
133
133
  when :CapHeight then @metrics.cap_height = parse_number
134
134
  when :XHeight then @metrics.x_height = parse_number
@@ -41,7 +41,7 @@ module HexaPDF
41
41
 
42
42
  # This module is used for loading images in the JPEG format from files or IO streams.
43
43
  #
44
- # See: PDF1.7 s7.4.8, ITU T.81 Annex B
44
+ # See: PDF1.7 s7.4.8, ITU T.81 Annex B, ITU T.872
45
45
  module JPEG
46
46
 
47
47
  # The magic marker that tells us if the file/IO contains an image in JPEG format.
@@ -56,13 +56,13 @@ module HexaPDF
56
56
  0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF
57
57
  ].freeze
58
58
 
59
- # Adobe uses the marker 0xEE (APPE) for its purposes. We need to use it for determinig
60
- # whether to invert the colors for CMYK/YCCK images or not (Adobe does this...).
61
- #
62
- # The marker also let's us distinguish between YCCK and CMYK images. However, we don't
63
- # actually need this information (and we don't need to set the /ColorTransform value)
64
- # because if the image has this information it is automically used.
65
- ADOBE_MARKER = 0xEE
59
+ # Adobe uses the marker 0xEE (APPE or APP14) for its purposes. We need to use it for
60
+ # determinig whether we have a CMYK or YCCK image.
61
+ APP14_MARKER = 0xEE
62
+
63
+ # Value of the 12th byte in an APP14 marker specifying that the image uses CMYK color
64
+ # encoding, with all four colors complemented.
65
+ APP14_TRANSFORM_CMYK = 0
66
66
 
67
67
  # End-of-image marker
68
68
  EOI_MARKER = 0xD9
@@ -121,9 +121,11 @@ module HexaPDF
121
121
  # but those shouldn't appear here)
122
122
  length = io.read(2).unpack1('n')
123
123
 
124
- if code1 == ADOBE_MARKER # Adobe apps invert the colors when using CMYK color space
125
- invert_colors = true
126
- io.seek(length - 2, IO::SEEK_CUR)
124
+ # According to T.872 6.1 and 6.5.3, if this marker is present, we need to use it for
125
+ # correctly determining whether complemented CMYK or YCCK is used
126
+ if code1 == APP14_MARKER
127
+ io.seek(length - 3, IO::SEEK_CUR)
128
+ invert_colors = true if io.getbyte == APP14_TRANSFORM_CMYK
127
129
  next
128
130
  elsif !SOF_MARKERS.include?(code1)
129
131
  io.seek(length - 2, IO::SEEK_CUR)
@@ -90,6 +90,11 @@ module HexaPDF
90
90
  oid.hash ^ gen.hash
91
91
  end
92
92
 
93
+ # Returns the object identifier as "oid,gen".
94
+ def to_s
95
+ "#{oid} #{gen} R"
96
+ end
97
+
93
98
  def inspect #:nodoc:
94
99
  "#<#{self.class.name} [#{oid}, #{gen}]>"
95
100
  end
@@ -89,6 +89,20 @@ module HexaPDF
89
89
  ((a = @xref_section.max_oid) < (b = @objects.max_oid) ? b : a) + 1
90
90
  end
91
91
 
92
+ # :call-seq:
93
+ # revision.xref(ref) -> xref_entry or nil
94
+ # revision.xref(oid) -> xref_entry or nil
95
+ #
96
+ # Returns an XRefSection::Entry structure for the given reference or object number if it is
97
+ # available, or +nil+ otherwise.
98
+ def xref(ref)
99
+ if ref.respond_to?(:oid)
100
+ @xref_section[ref.oid, ref.gen]
101
+ else
102
+ @xref_section[ref, nil]
103
+ end
104
+ end
105
+
92
106
  # :call-seq:
93
107
  # revision.object(ref) -> obj or nil
94
108
  # revision.object(oid) -> obj or nil
@@ -186,7 +186,7 @@ module HexaPDF
186
186
  NAME_CACHE[obj] ||=
187
187
  begin
188
188
  str = obj.to_s.force_encoding(Encoding::BINARY)
189
- str.gsub!(NAME_REGEXP) {|m| NAME_SUBSTS[m] }
189
+ str.gsub!(NAME_REGEXP, NAME_SUBSTS)
190
190
  "/#{str}"
191
191
  end
192
192
  end
@@ -203,7 +203,7 @@ module HexaPDF
203
203
  while index < obj.size
204
204
  tmp = __serialize(obj[index])
205
205
  str << " " unless BYTE_IS_DELIMITER[tmp.getbyte(0)] ||
206
- BYTE_IS_DELIMITER[str.getbyte(-1)]
206
+ BYTE_IS_DELIMITER[str.getbyte(-1)]
207
207
  str << tmp
208
208
  index += 1
209
209
  end
@@ -217,10 +217,10 @@ module HexaPDF
217
217
  str = +"<<"
218
218
  obj.each do |k, v|
219
219
  next if v.nil? || (v.respond_to?(:null?) && v.null?)
220
- str << __serialize(k)
220
+ str << serialize_symbol(k)
221
221
  tmp = __serialize(v)
222
222
  str << " " unless BYTE_IS_DELIMITER[tmp.getbyte(0)] ||
223
- BYTE_IS_DELIMITER[str.getbyte(-1)]
223
+ BYTE_IS_DELIMITER[str.getbyte(-1)]
224
224
  str << tmp
225
225
  end
226
226
  str << ">>"
@@ -311,7 +311,7 @@ module HexaPDF
311
311
 
312
312
  if @io && fiber.respond_to?(:length) && fiber.length >= 0
313
313
  obj.value[:Length] = fiber.length
314
- @io << __serialize(obj.value)
314
+ @io << serialize_hash(obj.value)
315
315
  @io << "stream\n"
316
316
  while fiber.alive? && (data = fiber.resume)
317
317
  @io << data.freeze
@@ -323,7 +323,7 @@ module HexaPDF
323
323
  data = Filter.string_from_source(fiber)
324
324
  obj.value[:Length] = data.size
325
325
 
326
- str = __serialize(obj.value)
326
+ str = serialize_hash(obj.value)
327
327
  str << "stream\n"
328
328
  str << data
329
329
  str << "\nendstream"
@@ -47,7 +47,7 @@ module HexaPDF
47
47
  class MarkupAnnotation < Annotation
48
48
 
49
49
  define_field :T, type: String, version: '1.1'
50
- define_field :Popup, type: :Annotation, version: '1.3'
50
+ define_field :Popup, type: :Annot, version: '1.3'
51
51
  define_field :CA, type: Numeric, default: 1.0, version: '1.4'
52
52
  define_field :RC, type: [Stream, String], version: '1.5'
53
53
  define_field :CreationDate, type: PDFDate, version: '1.5'
@@ -253,8 +253,10 @@ module HexaPDF
253
253
  if filter == :FlateDecode && self[:DecodeParms] && self[:DecodeParms][:Predictor].to_i >= 10
254
254
  data = stream_source
255
255
  else
256
+ colors = (color_type == ImageLoader::PNG::INDEXED ? 1 : info.components)
256
257
  flate_decode = config.constantize('filter.map', :FlateDecode)
257
- data = flate_decode.encoder(stream_decoder, Predictor: 15, Colors: 1, Columns: info.width,
258
+ data = flate_decode.encoder(stream_decoder, Predictor: 15,
259
+ Colors: colors, Columns: info.width,
258
260
  BitsPerComponent: info.bits_per_component)
259
261
  end
260
262
  io << png_chunk('IDAT', Filter.string_from_source(data))
@@ -37,6 +37,6 @@
37
37
  module HexaPDF
38
38
 
39
39
  # The version of HexaPDF.
40
- VERSION = '0.9.3'
40
+ VERSION = '0.10.0'
41
41
 
42
42
  end
@@ -82,6 +82,14 @@ module HexaPDF
82
82
  def compressed?
83
83
  type == :compressed
84
84
  end
85
+
86
+ def to_s
87
+ case type
88
+ when :free then "xref #{oid},#{gen} type=free"
89
+ when :in_use then "xref #{oid},#{gen} type=normal pos=#{pos}"
90
+ when :compressed then "xref #{oid},#{gen} type=compressed objstm=#{objstm},0 index=#{pos}"
91
+ end
92
+ end
85
93
  end
86
94
 
87
95
  # Creates an in-use cross-reference entry. See Entry for details on the arguments.
@@ -189,7 +189,9 @@ This command prints the application help if no arguments are given\. If one or m
189
189
  .SS "images"
190
190
  Synopsis: \fBimages\fP [\fBOPTIONS\fP] \fIPDF\fP
191
191
  .P
192
- This command extracts images from the \fIPDF\fP\&\. If the \fB\-\-extract\fP option is not specified, the images are listed with their indices and additional information, sorted by page number\. The \fB\-\-extract\fP option can then be used to extract one or more images, saving them to files called \fBPREFIX\-N\.EXT\fP where the prefix can be set via \fB\-\-prefix\fP, \fIN\fP is the image index and \fIEXT\fP is either png, jpg or jpx\.
192
+ This command extracts images from the \fIPDF\fP\&\. If the \fB\-\-extract\fP option is not specified, the images are listed with their indices and additional information, sorted by page number\. Note that if an image is used multiple times on a page, only the first occurence of it will be included\.
193
+ .P
194
+ The \fB\-\-extract\fP option can then be used to extract one or more images, saving them to files called \fBPREFIX\-N\.EXT\fP where the prefix can be set via \fB\-\-prefix\fP, \fIN\fP is the image index and \fIEXT\fP is either png, jpg or jpx\.
193
195
  .TP
194
196
  \fB\-e\fP [\fIA,B,C,\.\.\.\fP], \fB\-\-extract\fP [\fIA,B,C,\.\.\.\fP]
195
197
  The indices of the images that should be extracted\. Use \fI0\fP or no value to extract all images\.
@@ -230,6 +232,15 @@ The number of color components\.
230
232
  \fBbpc\fP
231
233
  The number of bits per color component\.
232
234
  .TP
235
+ \fBx\-ppi\fP
236
+ The pixels per inch (PPI) of the x\-direction of the image, as found on the page\.
237
+ .TP
238
+ \fBy\-ppi\fP
239
+ The pixels per inch (PPI) of the y\-direction of the image, as found on the page\.
240
+ .TP
241
+ \fBsize\fP
242
+ The file size of the image as stored in the PDF\.
243
+ .TP
233
244
  \fBtype\fP
234
245
  The image type\. Either jpg (JPEG), jp2 (JPEG2000), ccitt (CCITT Group 3 or 4 Fax), jbig2 (JBIG2) or png (PNG)\.
235
246
  .TP
@@ -244,32 +255,87 @@ This command reads the \fIFILE\fP and shows general information about it, like a
244
255
  \fB\-p\fP \fIPASSWORD\fP, \fB\-\-password\fP \fIPASSWORD\fP
245
256
  The password to decrypt the PDF \fIFILE\fP\&\. Use \fB\-\fP for \fIPASSWORD\fP for reading it from standard input\.
246
257
  .SS "inspect"
247
- Synopsis: \fBinspect\fP [\fBOPTIONS\fP] \fIFILE\fP
258
+ Synopsis: \fBinspect\fP [\fBOPTIONS\fP] \fIFILE\fP \fI[[CMD [ARGS]]\.\.\.]\fP
248
259
  .P
249
260
  This command is useful when one needs to inspect the internal object structure or a stream of a PDF file\.
250
261
  .P
251
- If no option is given, the PDF trailer is shown\. Otherwise the various, mutually exclusive display options define what is shown\. If multiple such options are specified only the last one is respected\. Note that PDF objects are always shown in the native PDF syntax\.
262
+ If no arguments are given, the interactive mode is started\. This interactive mode allows you to execute inspection commands without re\-parsing the PDF file, leading to better performance for big PDF files\.
263
+ .P
264
+ Otherwise the arguments are interpreted as interactive mode commands and executed\. It is possible to specify more than one command in this way by separating them with semicolons, or whitespace in case the number of command arguments is fixed\.
252
265
  .TP
253
- \fB\-\-catalog\fP
254
- Show the PDF catalog dictionary\.
266
+ \fB\-p\fP \fIPASSWORD\fP, \fB\-\-password\fP \fIPASSWORD\fP
267
+ The password to decrypt the PDF \fIFILE\fP\&\. Use \fB\-\fP for \fIPASSWORD\fP for reading it from standard input\.
268
+ .P
269
+ If an interactive mode command or argument is \fBOID[,GEN]\fP, object and generation numbers are expected\. The generation number defaults to 0 if not given\. PDF objects are always shown in the native PDF syntax\.
270
+ .P
271
+ The available commands are:
255
272
  .TP
256
- \fB\-c\fP, \fB\-\-page\-count\fP
257
- Print the number of pages\.
273
+ \fBOID[,GEN] | o[bject] OID[,GEN]\fP
274
+ Print the given indirect object\.
258
275
  .TP
259
- \fB\-\-pages\fP [\fIPAGES\fP]
260
- Show the pages with their object and generation numbers and their associated content streams\. If a range is specified, only those pages are listed\. See the \fBPAGES SPECIFICATION\fP below for details on the allowed format of \fIPAGES\fP\&\.
276
+ \fBr[ecursive] OID[,GEN]\fP
277
+ Print the given indirect object recursively\. This means that all references found in the object are resolved and the resulting objects themselves recursively printed\.
278
+ .RS
279
+ .P
280
+ To make it easier to compare such structures between PDF files, the entries of dictionaries are printed in sorted order and the original references are replaced by custom ones\. Once an indirect object is first encountered, it is preceeded by either \fB{obj INDEX}\fP or \fB{obj page PAGEINDEX}\fP where \fBINDEX\fP is an increasing number and \fBPAGEINDEX\fP is the index of the page\. Later references are replaced by \fB{ref INDEX}\fP and \fB{ref page PAGEINDEX}\fP respectively\.
281
+ .P
282
+ Here is a simplified example output:
283
+ .sp
284
+ .RS 4
285
+ .EX
286
+ <<
287
+ /Info {obj 1} <<
288
+ /Producer (HexaPDF version 0\.9\.3)
289
+ >>
290
+ /Root {obj 2} <<
291
+ /Pages {obj 3} <<
292
+ /Count 1
293
+ /Kids [{obj page 1} <<
294
+ /MediaBox [0 0 595 842 ]
295
+ /Parent {ref 3}
296
+ /Type /Page
297
+ >> ]
298
+ /Type /Pages
299
+ >>
300
+ /Type /Catalog
301
+ >>
302
+ /Size 4
303
+ >>
304
+ .EE
305
+ .RE
306
+ .P
307
+ On line 2 the indirect object for the key \fB/Info\fP is shown, preceeded by the custom reference\. On line 8 is an example for a page object with the special reference key\. And on line 10 there is a back reference to the object with index 3 which is started on line 6\.
308
+ .RE
261
309
  .TP
262
- \fB\-o\fP \fIOID\fP[,\fIGEN\fP], \fB\-\-object\fP \fIOID\fP[,\fIGEN\fP]
263
- Show the object with the given object and generation numbers\. The generation number defaults to 0 if not given\.
310
+ \fBs[tream] OID[,GEN]\fP
311
+ Print the filtered stream, i\.e\. the stream with all filters applied\. This is useful, for example, to view the contents of content streams\.
264
312
  .TP
265
- \fB\-s\fP \fIOID\fP[,\fIGEN\fP], \fB\-\-stream\fP \fIOID\fP[,\fIGEN\fP]
266
- Show the filtered stream data (add \fB\-\-raw\fP to get the raw stream data) of the object with the given object and generation numbers\. The generation number defaults to 0 if not given\.
313
+ \fBraw[\-stream] OID[,GEN]\fP
314
+ Print the raw stream, i\.e\. the stream as it appears in the file\. This is useful, for example, to extract streams into files\.
267
315
  .TP
268
- \fB\-\-raw\fP
269
- Modifies \fB\-\-stream\fP to show the raw stream data instead of the filtered one\.
316
+ \fBx[ref] OID[,GEN]\fP
317
+ Print the cross\-reference entry for the given indirect object\.
270
318
  .TP
271
- \fB\-p\fP \fIPASSWORD\fP, \fB\-\-password\fP \fIPASSWORD\fP
272
- The password to decrypt the PDF \fIFILE\fP\&\. Use \fB\-\fP for \fIPASSWORD\fP for reading it from standard input\.
319
+ \fBc[atalog]\fP
320
+ Print the catalog dictionary\.
321
+ .TP
322
+ \fBt[railer]\fP
323
+ Print the trailer dictionary\.
324
+ .TP
325
+ \fBp[ages] [RANGE]\fP
326
+ Print the pages with their object and generation numbers and their associated content streams\. If a range is specified, only those pages are listed\. See the \fBPAGES SPECIFICATION\fP below for details on the allowed format of \fIPAGES\fP\&\.
327
+ .TP
328
+ \fBpc | page\-count\fP
329
+ Print the number of pages\.
330
+ .TP
331
+ \fBsearch REGEXP\fP
332
+ Print all objects matching the pattern\. Each object is preceeded by \fBobj OID GEN\fP and followed by \fBendobj\fP to make it easier to further explore the data\.
333
+ .TP
334
+ \fBh[elp]\fP
335
+ Print the available commands with a short description\.
336
+ .TP
337
+ \fBq[uit]Quit\fP
338
+ Quit the interactive mode\.
273
339
  .SS "merge"
274
340
  Synopsis: \fBmerge\fP [\fBOPTIONS\fP] { \fIINPUT\fP | \fB\-\-empty\fP } [\fIINPUT\fP]\.\.\. \fIOUTPUT\fP
275
341
  .P
@@ -426,11 +492,13 @@ Image info and extraction: The first command lists the images of the \fBinput\.p
426
492
  .P
427
493
  File information: Show general information about the PDF file, like PDF version, number of pages, creator, creation date and encryption related information\.
428
494
  .SS "inspect"
429
- \fBhexapdf inspect input\.pdf\fP
430
- .br
431
495
  \fBhexapdf inspect input\.pdf \-o 3\fP
432
496
  .P
433
- Inspect a PDF: These commands can be used to inspect the internal object structure of a PDF file\. The first command shows the PDF trailer object\. The second one shows the object with the object number 3\.
497
+ Show the object with the object number 3 of the given PDF file\.
498
+ .P
499
+ \fBhexapdf inspect input\.pdf\fP
500
+ .P
501
+ Start the interactive inspection mode\.
434
502
  .SS "batch"
435
503
  \fBhexapdf batch \'info {}\' input1\.pdf input2\.pdf input3\.pdf\fP
436
504
  .P
Binary file
@@ -777,9 +777,9 @@ describe HexaPDF::Content::Canvas do
777
777
 
778
778
  describe "xobject" do
779
779
  before do
780
- @image = @doc.add(Subtype: :Image, Width: 10, Height: 5)
780
+ @image = @doc.add(Type: :XObject, Subtype: :Image, Width: 10, Height: 5)
781
781
  @image.source_path = File.join(TEST_DATA_DIR, 'images', 'gray.jpg')
782
- @form = @doc.add(Subtype: :Form, BBox: [100, 50, 200, 100])
782
+ @form = @doc.add(Type: :XObject, Subtype: :Form, BBox: [100, 50, 200, 100])
783
783
  end
784
784
 
785
785
  it "can use any xobject specified via a filename" do
@@ -16,7 +16,7 @@ describe HexaPDF::Document::Images do
16
16
  @loader.define_singleton_method(:handles?) {|*| true }
17
17
  @loader.define_singleton_method(:load) do |doc, s|
18
18
  s = HexaPDF::StreamData.new(s) if s.kind_of?(IO)
19
- doc.add({Subtype: :Image}, stream: s)
19
+ doc.add({Type: :XObject, Subtype: :Image}, stream: s)
20
20
  end
21
21
  @doc.config['image_loader'].unshift(@loader)
22
22
  end
@@ -60,10 +60,12 @@ describe HexaPDF::Document::Images do
60
60
  it "iterates over all non-mask images" do
61
61
  @doc.add(5)
62
62
  images = []
63
- images << @doc.add(Subtype: :Image)
64
- images << @doc.add(Subtype: :Image, Mask: [5, 6])
65
- images << @doc.add(Subtype: :Image, Mask: @doc.add(Subtype: :Image))
66
- images << @doc.add(Subtype: :Image, SMask: @doc.add(Subtype: :Image))
63
+ images << @doc.add(Type: :XObject, Subtype: :Image)
64
+ images << @doc.add(Type: :XObject, Subtype: :Image, Mask: [5, 6])
65
+ images << @doc.add(Type: :XObject, Subtype: :Image,
66
+ Mask: @doc.add(Type: :XObject, Subtype: :Image))
67
+ images << @doc.add(Type: :XObject, Subtype: :Image,
68
+ SMask: @doc.add(Type: :XObject, Subtype: :Image))
67
69
  assert_equal(images.sort, @doc.images.to_a.sort)
68
70
  end
69
71
  end
@@ -298,6 +298,12 @@ describe HexaPDF::Encryption::SecurityHandler do
298
298
  assert_equal(@encrypted, @handler.decrypt(@obj)[:Key])
299
299
  end
300
300
 
301
+ it "doesn't decrypt the /Contents of a signature dictionary" do
302
+ @obj[:Type] = :Sig
303
+ @obj[:Contents] = "test"
304
+ assert_equal("test", @handler.decrypt(@obj)[:Contents])
305
+ end
306
+
301
307
  it "fails if V < 5 and the object number changes" do
302
308
  @obj.oid = 55
303
309
  @handler.decrypt(@obj)
@@ -332,6 +338,13 @@ describe HexaPDF::Encryption::SecurityHandler do
332
338
  @stream[:Type] = :XRef
333
339
  assert_equal('string', @handler.encrypt_stream(@stream).resume)
334
340
  end
341
+
342
+ it "doesn't encrypt the /Contents key of signature dictionaries" do
343
+ @obj[:Type] = :Sig
344
+ @obj[:Contents] = "test"
345
+ refute_equal('test', @handler.encrypt_string("test", @obj))
346
+ assert_equal('test', @handler.encrypt_string(@obj[:Contents], @obj))
347
+ end
335
348
  end
336
349
 
337
350
  it "works correctly with different decryption and encryption handlers" do
@@ -66,6 +66,16 @@ describe HexaPDF::ImageLoader::JPEG do
66
66
  assert_equal(File.binread(jpeg), image.stream)
67
67
  end
68
68
 
69
+ it "works for a YCCK jpeg" do
70
+ jpeg = @images.grep(/ycck\.jpg/).first
71
+ image = @loader.load(@doc, jpeg)
72
+ assert_equal(5, image[:Width])
73
+ assert_equal(5, image[:Height])
74
+ assert_equal(:DeviceCMYK, image[:ColorSpace])
75
+ refute(image.key?(:Decode))
76
+ assert_equal(File.binread(jpeg), image.stream)
77
+ end
78
+
69
79
  it "fails if the JPEG is corrupt" do
70
80
  exp = assert_raises(HexaPDF::Error) do
71
81
  @loader.load(@doc, StringIO.new("some non JPEG data"))
@@ -206,8 +206,9 @@ describe HexaPDF::ImageLoader::PNG do
206
206
  end
207
207
 
208
208
  it "works for a true color 8-bit png with alpha" do
209
- png = @images.grep(/truecolour-alpha-8bit\.png/).first
210
- image = @loader.load(@doc, png)
209
+ png_data = File.binread(@images.grep(/truecolour-alpha-8bit\.png/).first)
210
+ png_data[33, 0] = [0, "tRNS", 0].pack('NA4N') # add invalid tRNS chunk
211
+ image = @loader.load(@doc, StringIO.new(png_data))
211
212
  data = [[12, 92, 146, 80, 136, 175, 167, 193, 213, 97, 175, 101, 38, 113, 50],
212
213
  [12, 92, 146, 81, 137, 176, 168, 194, 214, 97, 175, 101, 38, 113, 49],
213
214
  [12, 92, 146, 81, 137, 176, 169, 195, 214, 96, 175, 101, 37, 113, 49],
@@ -230,6 +230,15 @@ describe HexaPDF::Dictionary do
230
230
 
231
231
  @obj.value[:Inherited] = Class.new(Array).new([5])
232
232
  assert(@obj.validate(auto_correct: false))
233
+
234
+ @test_class.define_field(:StringField, type: String)
235
+ @test_class.define_field(:NameField, type: Symbol)
236
+ @obj.value[:StringField] = :symbol
237
+ refute(@obj.validate(auto_correct: false))
238
+ assert(@obj.validate(auto_correct: true))
239
+ @obj.value[:NameField] = "string"
240
+ assert(@obj.validate(auto_correct: true))
241
+ assert(@obj.validate(auto_correct: true))
233
242
  end
234
243
 
235
244
  it "checks whether a field needs to be indirect w/wo auto_correct" do