hexapdf 0.45.0 → 0.46.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -47
  3. data/examples/019-acro_form.rb +5 -0
  4. data/lib/hexapdf/cli/inspect.rb +5 -0
  5. data/lib/hexapdf/composer.rb +1 -1
  6. data/lib/hexapdf/configuration.rb +8 -0
  7. data/lib/hexapdf/digital_signature/cms_handler.rb +31 -3
  8. data/lib/hexapdf/digital_signature/signing/default_handler.rb +9 -1
  9. data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +5 -1
  10. data/lib/hexapdf/document/layout.rb +48 -27
  11. data/lib/hexapdf/document.rb +24 -2
  12. data/lib/hexapdf/importer.rb +15 -5
  13. data/lib/hexapdf/layout/box.rb +25 -28
  14. data/lib/hexapdf/layout/frame.rb +1 -1
  15. data/lib/hexapdf/layout/inline_box.rb +17 -23
  16. data/lib/hexapdf/layout/list_box.rb +24 -29
  17. data/lib/hexapdf/layout/page_style.rb +23 -16
  18. data/lib/hexapdf/layout/style.rb +2 -2
  19. data/lib/hexapdf/layout/text_box.rb +2 -6
  20. data/lib/hexapdf/parser.rb +5 -1
  21. data/lib/hexapdf/revisions.rb +1 -1
  22. data/lib/hexapdf/stream.rb +3 -3
  23. data/lib/hexapdf/tokenizer.rb +3 -2
  24. data/lib/hexapdf/type/acro_form/button_field.rb +2 -0
  25. data/lib/hexapdf/type/acro_form/choice_field.rb +2 -0
  26. data/lib/hexapdf/type/acro_form/field.rb +8 -0
  27. data/lib/hexapdf/type/acro_form/form.rb +2 -1
  28. data/lib/hexapdf/type/acro_form/text_field.rb +2 -0
  29. data/lib/hexapdf/version.rb +1 -1
  30. data/test/hexapdf/digital_signature/common.rb +66 -84
  31. data/test/hexapdf/digital_signature/signing/test_default_handler.rb +7 -0
  32. data/test/hexapdf/digital_signature/signing/test_signed_data_creator.rb +9 -0
  33. data/test/hexapdf/digital_signature/test_cms_handler.rb +41 -1
  34. data/test/hexapdf/digital_signature/test_handler.rb +2 -1
  35. data/test/hexapdf/document/test_layout.rb +28 -5
  36. data/test/hexapdf/layout/test_box.rb +12 -5
  37. data/test/hexapdf/layout/test_frame.rb +12 -2
  38. data/test/hexapdf/layout/test_inline_box.rb +17 -28
  39. data/test/hexapdf/layout/test_list_box.rb +5 -5
  40. data/test/hexapdf/layout/test_page_style.rb +7 -2
  41. data/test/hexapdf/layout/test_text_box.rb +3 -9
  42. data/test/hexapdf/layout/test_text_layouter.rb +0 -3
  43. data/test/hexapdf/test_document.rb +27 -0
  44. data/test/hexapdf/test_importer.rb +17 -0
  45. data/test/hexapdf/test_revisions.rb +54 -41
  46. data/test/hexapdf/type/acro_form/test_form.rb +9 -0
  47. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd31e769d2a906198da2a4194085cb2a884fa3879442b75add168d7f81d67d8b
4
- data.tar.gz: 43c2b4ba4df2f997d470835995f2581aa306c639c63ddc892e648d94605f3b22
3
+ metadata.gz: f9c1a35d4ad93b48faa1f728bcddc91778da584c8d673e2f15557bd22050a438
4
+ data.tar.gz: 2ef8ca70891723643080a402f0808882f66f01a816c422477cc03fad34774859
5
5
  SHA512:
6
- metadata.gz: f17a303dba8104974564a213c72c0f0862c520964a3255b575544489ccb5a17a468d093d3970817903c4eb798e888592410db18447d3264d535f3a01a4a94c82
7
- data.tar.gz: d5727e2f2bfb5ed827ac92eecc8cd9e0ba73044150ba07d03623dccde43dc29db1cddc2fbce24bfd63268522e7965519e188271c2bc9c00162b1a660fc468b74
6
+ metadata.gz: 87c109bd8a6711b4df27a40c689a025d816075d6c68c21a1cc22924b5ae827cd44d98e45bff8f43c85403dd82d6a6e54b66c99bfe135298cba33292e9511a1fc
7
+ data.tar.gz: e0529e3e244be8366e722ea29ab06a8b922f18698526c79a28d5d11f02da5ee103cba71ecbc6882fefe3de27f02d38f8ce8b505fefbb45839069c74aefaed9ae
data/CHANGELOG.md CHANGED
@@ -1,3 +1,43 @@
1
+ ## 0.46.0 - 2024-08-11
2
+
3
+ ### Added
4
+
5
+ * [HexaPDF::DigitalSignature::CMSHandler#embedded_tsa_signature] to return the
6
+ embedded timestamp authority signature if any
7
+ * [HexaPDF::DigitalSignature::Signing::DefaultHandler#signing_time] for setting
8
+ a custom signing time
9
+ * [HexaPDF::Document#duplicate] for making an in-memory copy of a PDF document
10
+ * Configuration option 'font.default' for setting the default font for the
11
+ document layout engine
12
+
13
+ ### Changed
14
+
15
+ * [HexaPDF::Document::Layout::CellArgumentCollector#[]=] to allow stepped ranges
16
+ * [HexaPDF::Document::Layout::ChildrenCollector] to also return the box when
17
+ creating and adding one to the list
18
+ * [HexaPDF::Layout::InlineBox] to allow usage without predefined width
19
+ * [HexaPDF::DigitalSignature::CMSHandler#verify] to recognize non-repudiation
20
+ signatures
21
+ * [HexaPDF::DigitalSignature::CMSHandler#signing_time] to use time from an
22
+ embedded timestamp authority signature if possible
23
+ * [HexaPDF::Layout::Box#fit] to return success for boxes with content
24
+ width/height of zero
25
+ * [HexaPDF::Importer::copy] to optionally allow copying the catalog and page
26
+ tree nodes
27
+
28
+ ### Fixed
29
+
30
+ * Setting of correct x-position in fit result for boxes with flow positioning
31
+ * [HexaPDF::Layout::ListBox#fit] to respect the set height
32
+ * CLI command `hexapdf inspect` to work in case of missing Unicde mappings
33
+ * [HexaPDF::Type::AcroForm::Form#delete_field] to correctly work for fields with
34
+ an embedded widget
35
+ * Parsing of "linearized" PDF files where the first cross-reference section
36
+ isn't actually used
37
+ * [HexaPDF::Layout::PageStyle#create_page] to return new frame objects on each
38
+ invocation
39
+
40
+
1
41
  ## 0.45.0 - 2024-06-18
2
42
 
3
43
  ### Added
@@ -375,8 +415,8 @@
375
415
 
376
416
  ### Fixed
377
417
 
378
- * [HexaPDF::Document::Pages#add_labelling_range] to add a correct entry for
379
- the default range starting at page 1
418
+ * [HexaPDF::Document::Pages#add_labelling_range] to add a correct entry for the
419
+ default range starting at page 1
380
420
  * [HexaPDF::Type::Page#flatten_annotations] to correctly handle scaled
381
421
  appearances
382
422
  * Using an unknown style name in [HexaPDF:Document::Layout] method by providing
@@ -387,6 +427,8 @@
387
427
  than the item content height
388
428
  * [HexaPDF::Dictionary] setting default values on wrong classes in certain
389
429
  situations
430
+ * [HexaPDF::Importer#import] to correctly import stream objects backed by a
431
+ [HexaPDF::FiberDoubleForString]
390
432
 
391
433
 
392
434
  ## 0.33.0 - 2023-08-02
@@ -439,8 +481,8 @@
439
481
  * [HexaPDF::Content::Canvas#text] to set the leading only when multiple lines
440
482
  are drawn
441
483
  * [HexaPDF::Layout::TextBox#split] to use float comparison
442
- * Validation of standard encryption dictionary to auto-correct invalid /U and
443
- /O fields in case they are padded with zeros
484
+ * Validation of standard encryption dictionary to auto-correct invalid /U and /O
485
+ fields in case they are padded with zeros
444
486
  * [HexaPDF::Document#wrap] handling of sub-type mapping in case of missing type
445
487
  * [HexaPDF::Type::AcroForm::AppearanceGenerator] to also take a text field
446
488
  widget's width into account when auto-sizing
@@ -761,8 +803,8 @@
761
803
  * Support for the document outline
762
804
  * [HexaPDF::Layout::Style#line_height] for setting a custom line height
763
805
  independent of the font size
764
- * [HexaPDF::Document::Destinations#use_or_create] as unified interface for
765
- using or creating destinations
806
+ * [HexaPDF::Document::Destinations#use_or_create] as unified interface for using
807
+ or creating destinations
766
808
  * [HexaPDF::Document::Destinations::Destination#valid?] and class method for
767
809
  checking whether a destination array is valid
768
810
 
@@ -771,8 +813,8 @@
771
813
  * Calculation of text related [HexaPDF::Layout::Style] values for Type3 fonts
772
814
  * [HexaPDF::Encryption::SecurityHandler#encrypt_string] to either return a
773
815
  dupped or encrypted string
774
- * [HexaPDF::Layout::TextLayouter#fit] to avoid infinite loop when encountering
775
- a non-zero width breakpoint penalty
816
+ * [HexaPDF::Layout::TextLayouter#fit] to avoid infinite loop when encountering a
817
+ non-zero width breakpoint penalty
776
818
  * [HexaPDF::Type::ObjectStream] to parse the initial stream data right after
777
819
  initialization to avoid access errors
778
820
  * [HexaPDF::Revisions::from_io] to merge a completely empty revision with just a
@@ -854,8 +896,7 @@
854
896
  fragment if there would not be enough height left anyway
855
897
  * [HexaPDF::Layout::WidthFromPolygon] to work correctly in case of very small
856
898
  floating point errors
857
- * HexaPDF::Layout::TextFragment#inspect to work in case of interspersed
858
- numbers
899
+ * HexaPDF::Layout::TextFragment#inspect to work in case of interspersed numbers
859
900
  * [HexaPDF::Layout::TextBox#split] to work for position :flow when box is wider
860
901
  than the initial available width
861
902
  * [HexaPDF::Layout::Frame#fit] to create minimally sized mask rectangles
@@ -1137,8 +1178,8 @@
1137
1178
  dictionary are indirect objects
1138
1179
  * [HexaPDF::Content::GraphicObject::EndpointArc] to correctly determine the
1139
1180
  start and end points
1140
- * HexaPDF::Dictionary#perform_validation to correctly handle objects that
1141
- should not be indirect objects
1181
+ * HexaPDF::Dictionary#perform_validation to correctly handle objects that should
1182
+ not be indirect objects
1142
1183
 
1143
1184
 
1144
1185
  ## 0.17.3 - 2021-10-31
@@ -1260,8 +1301,8 @@
1260
1301
 
1261
1302
  ### Fixed
1262
1303
 
1263
- * [HexaPDF::Type::Annotation#appearance] to handle cases where there is
1264
- no valid appearance stream
1304
+ * [HexaPDF::Type::Annotation#appearance] to handle cases where there is no valid
1305
+ appearance stream
1265
1306
 
1266
1307
 
1267
1308
  ## 0.15.3 - 2021-05-01
@@ -1316,8 +1357,8 @@
1316
1357
  empty background color arrays
1317
1358
  * [HexaPDF::Type::AcroForm::Field#delete_widget] to update the wrapper object
1318
1359
  stored in the document in case the widget is embedded
1319
- * Processing of invalid PDF files containing a space,CR,LF combination after
1320
- the 'stream' keyword
1360
+ * Processing of invalid PDF files containing a space,CR,LF combination after the
1361
+ 'stream' keyword
1321
1362
  * Cross-reference stream reconstruction with respect to detection of linearized
1322
1363
  files
1323
1364
  * Detection of existing appearances for AcroForm push button fields when
@@ -1412,8 +1453,8 @@
1412
1453
 
1413
1454
  * [HexaPDF::Utils::ObjectHash#oids] to be public instead of private
1414
1455
  * Cross-reference table parsing to handle invalidly numbered main sections
1415
- * [HexaPDF::Document#cache] and [HexaPDF::Object#cache] to allow updating
1416
- values for existing keys
1456
+ * [HexaPDF::Document#cache] and [HexaPDF::Object#cache] to allow updating values
1457
+ for existing keys
1417
1458
  * Appearance creation methods of AcroForm objects to allow forcing the creation
1418
1459
  of new appearances
1419
1460
  * [HexaPDF::Type::AcroForm::AppearanceGenerator#create_text_appearances] to
@@ -1451,8 +1492,8 @@
1451
1492
  new 'parser.try_xref_reconstruction' option
1452
1493
  * Two new `hexapdf inspect` commands for showing page objects and page content
1453
1494
  streams by page number
1454
- * Flag `--check` to the CLI command `hexapdf info` for checking a file for
1455
- parse and validation errors
1495
+ * Flag `--check` to the CLI command `hexapdf info` for checking a file for parse
1496
+ and validation errors
1456
1497
  * [HexaPDF::Type::AcroForm::Field#embedded_widget?] for checking if a widget is
1457
1498
  embedded in the field object
1458
1499
  * [HexaPDF::Type::AcroForm::Field#delete_widget] for deleting a widget
@@ -1509,8 +1550,8 @@
1509
1550
 
1510
1551
  ### Added
1511
1552
 
1512
- * [HexaPDF::Font::Encoding::Base#code] for retrieving the code for a given
1513
- glyph name
1553
+ * [HexaPDF::Font::Encoding::Base#code] for retrieving the code for a given glyph
1554
+ name
1514
1555
 
1515
1556
  ### Fixed
1516
1557
 
@@ -1526,11 +1567,11 @@
1526
1567
  [HexaPDF::Type::AcroForm::Field]
1527
1568
  * [HexaPDF::Type::AcroForm::TextField] and
1528
1569
  [HexaPDF::Type::AcroForm::VariableTextField] for basic text field support
1529
- * [HexaPDF::Type::AcroForm::ButtonField] for push button, radio button and
1530
- check box support
1570
+ * [HexaPDF::Type::AcroForm::ButtonField] for push button, radio button and check
1571
+ box support
1531
1572
  * [HexaPDF::Type::AcroForm::ChoiceField] for combo box and list box support
1532
- * [HexaPDF::Type::AcroForm::AppearanceGenerator] as central class for
1533
- generating appearance streams for form fields
1573
+ * [HexaPDF::Type::AcroForm::AppearanceGenerator] as central class for generating
1574
+ appearance streams for form fields
1534
1575
  * Various convenience methods for [HexaPDF::Type::AcroForm::Form]
1535
1576
  * Various convenience methods for [HexaPDF::Type::AcroForm::Field]
1536
1577
  * Various convenience methods for [HexaPDF::Type::Annotations::Widget]
@@ -1549,8 +1590,8 @@
1549
1590
  * [HexaPDF::Type::Annotation::Border] class
1550
1591
  * [HexaPDF::Content::ColorSpace::device_color_from_specification] for easily
1551
1592
  getting a device color object
1552
- * [HexaPDF::Content::ColorSpace::prenormalized_device_color] for getting a device
1553
- color object without normalizing values
1593
+ * [HexaPDF::Content::ColorSpace::prenormalized_device_color] for getting a
1594
+ device color object without normalizing values
1554
1595
  * [HexaPDF::Type::Annotation#appearance] for returning the associated appearance
1555
1596
  dictionary
1556
1597
  * [HexaPDF::Type::Annotation#appearance?] for checking whether an appearance for
@@ -1669,8 +1710,8 @@
1669
1710
 
1670
1711
  ### Fixed
1671
1712
 
1672
- * Conversion of [HexaPDF::Rectangle] type when the original is not a plain
1673
- Array but a [HexaPDF::PDFArray]
1713
+ * Conversion of [HexaPDF::Rectangle] type when the original is not a plain Array
1714
+ but a [HexaPDF::PDFArray]
1674
1715
 
1675
1716
 
1676
1717
  ## 0.11.1 - 2019-11-19
@@ -1831,12 +1872,12 @@
1831
1872
 
1832
1873
  ### Added
1833
1874
 
1834
- * [HexaPDF::Layout::Frame] for box positioning and easier text layouting
1835
- inside an arbitrary polygon
1875
+ * [HexaPDF::Layout::Frame] for box positioning and easier text layouting inside
1876
+ an arbitrary polygon
1836
1877
  * [HexaPDF::Layout::TextBox] for displaying text in a rectangular and for
1837
1878
  flowing text inside a frame
1838
- * [HexaPDF::Layout::WidthFromPolygon] for getting a width specification from
1839
- a polygon for use with the text layouting engine
1879
+ * [HexaPDF::Layout::WidthFromPolygon] for getting a width specification from a
1880
+ polygon for use with the text layouting engine
1840
1881
  * [HexaPDF::Type::Image#width] and [HexaPDF::Type::Image#height] convenience
1841
1882
  methods
1842
1883
  * [HexaPDF::Type::FontType3] for Type 3 font support
@@ -1888,12 +1929,12 @@
1888
1929
  character in a text fragment is \r
1889
1930
  * [HexaPDF::Layout::TextLayouter] to work if an optional break point (think
1890
1931
  soft-hyphen) is followed by whitespace
1891
- * [HexaPDF::Font::TrueType::Builder] to correctly order the entries in the
1892
- table directory
1932
+ * [HexaPDF::Font::TrueType::Builder] to correctly order the entries in the table
1933
+ directory
1893
1934
  * [HexaPDF::Font::TrueType::Builder] to pad the table data to achieve the
1894
1935
  correct alignment
1895
- * [HexaPDF::Filter::FlateDecode] by removing the Zlib pools since they were
1896
- not thread safe
1936
+ * [HexaPDF::Filter::FlateDecode] by removing the Zlib pools since they were not
1937
+ thread safe
1897
1938
  * All color space classes to accept the color space definition as argument to
1898
1939
  `::new`
1899
1940
 
@@ -1925,9 +1966,8 @@
1925
1966
  * Cross-reference subsection parsing can handle missing whitespace
1926
1967
  * Renamed HexaPDF::Layout::LineFragment to [HexaPDF::Layout::Line]
1927
1968
  * Renamed HexaPDF::Layout::TextBox to [HexaPDF::Layout::TextLayouter]
1928
- * [HexaPDF::Layout::TextFragment::new] and
1929
- [HexaPDF::Layout::TextLayouter::new] to either take a Style object or
1930
- style options
1969
+ * [HexaPDF::Layout::TextFragment::new] and [HexaPDF::Layout::TextLayouter::new]
1970
+ to either take a Style object or style options
1931
1971
  * [HexaPDF::Layout::TextLayouter#fit] method signature
1932
1972
  * [HexaPDF::Layout::InlineBox] to wrap a generic box
1933
1973
  * HexaPDF::Document::Fonts#load to [HexaPDF::Document::Fonts#add] for
@@ -1989,8 +2029,8 @@
1989
2029
 
1990
2030
  * Handling of invalid glyphs is done using the special
1991
2031
  [HexaPDF::Font::InvalidGlyph] class
1992
- * Configuration option 'font.on_missing_glyph'; returns an invalid glyph
1993
- instead of raising an error
2032
+ * Configuration option 'font.on_missing_glyph'; returns an invalid glyph instead
2033
+ of raising an error
1994
2034
  * Bounding box of TrueType glyphs without contours is set to `[0, 0, 0, 0]`
1995
2035
  * Ligature pairs for AFM fonts are stored like kerning pairs
1996
2036
  * Use TrueType configuration option 'font.true_type.unknown_format' in all
@@ -2007,8 +2047,8 @@
2007
2047
 
2008
2048
  * [HexaPDF::Task::Dereference] to work correctly when encountering invalid
2009
2049
  references
2010
- * [HexaPDF::Tokenizer] and HexaPDF::Content::Tokenizer to parse a solitary
2011
- plus sign
2050
+ * [HexaPDF::Tokenizer] and HexaPDF::Content::Tokenizer to parse a solitary plus
2051
+ sign
2012
2052
  * Usage of Strings instead of Symbols for AFM font kerning and ligature pairs
2013
2053
  * Processing the contents of form XObjects in case they don't have a resources
2014
2054
  dictionary
@@ -2031,8 +2071,8 @@
2031
2071
  * CLI option `--verbose` for more verbose output; also changed the default
2032
2072
  verbosity level to only display warnings and not informational messages
2033
2073
  * CLI option `--quiet` for suppressing additional and diagnostic output
2034
- * CLI option `--strict` for enabling strict parsing and validation; also
2035
- changed the default from strict to non-strict parsing/validation
2074
+ * CLI option `--strict` for enabling strict parsing and validation; also changed
2075
+ the default from strict to non-strict parsing/validation
2036
2076
  * CLI optimization option `--optimize-fonts` for optimizing embedded fonts
2037
2077
  * Method `#word_spacing_applicable?` to font types
2038
2078
  * Support for marked-content points and sequences in [HexaPDF::Content::Canvas]
@@ -6,6 +6,11 @@
6
6
  # This example show-cases how to create the various form field types and their
7
7
  # possible standard appearances.
8
8
  #
9
+ # The [HexaPDF::Type::AcroForm::Form] and [HexaPDF::Type::AcroForm::Field]
10
+ # classes provide a plethora of convenience methods for working with forms, like
11
+ # for creating fields and their widgets, getting field properties like the full
12
+ # hierarchical field name or for setting the field value.
13
+ #
9
14
  # Usage:
10
15
  # : `ruby acro_form.rb`
11
16
  #
@@ -117,6 +117,11 @@ module HexaPDF
117
117
 
118
118
  def execute(file, *commands) #:nodoc:
119
119
  with_document(file, password: @password) do |doc|
120
+ doc.config['font.on_missing_unicode_mapping'] = lambda do |code, font|
121
+ $stderr.puts("No Unicode mapping for code point #{code} in font #{font[:BaseFont]}, " \
122
+ "using the Unicode replacement character")
123
+ "\u{FFFD}"
124
+ end
120
125
  @doc = doc
121
126
  if commands.empty?
122
127
  begin
@@ -450,7 +450,7 @@ module HexaPDF
450
450
  (box = draw_box; break) unless box
451
451
  elsif !@frame.find_next_region
452
452
  unless drawn_on_page
453
- raise HexaPDF::Error, "Box doesn't fit on empty page"
453
+ raise HexaPDF::Error, "Box didn't fit multiple times, even on empty page"
454
454
  end
455
455
  new_page
456
456
  drawn_on_page = false
@@ -277,6 +277,13 @@ module HexaPDF
277
277
  #
278
278
  # See PDF2.0 s7.4.1, ADB sH.3 3.3
279
279
  #
280
+ # font.default::
281
+ # This font is used by the layout engine when no font is specified but one is needed.
282
+ #
283
+ # This is used, for example, for the font set on styles that don't have a font set.
284
+ #
285
+ # The default value is 'Times'.
286
+ #
280
287
  # font.fallback::
281
288
  # An array of fallback font names to be used when replacing invalid glyphs.
282
289
  #
@@ -518,6 +525,7 @@ module HexaPDF
518
525
  Crypt: 'HexaPDF::Filter::Crypt',
519
526
  Encryption: 'HexaPDF::Filter::Encryption',
520
527
  },
528
+ 'font.default' => 'Times',
521
529
  'font.fallback' => ['ZapfDingbats', 'Symbol'],
522
530
  'font.map' => {},
523
531
  'font.on_invalid_glyph' => method(:font_on_invalid_glyph),
@@ -59,7 +59,11 @@ module HexaPDF
59
59
 
60
60
  # Returns the time of signing.
61
61
  def signing_time
62
- signer_info.signed_time rescue super
62
+ if embedded_tsa_signature
63
+ embedded_tsa_signature.signers.first.signed_time
64
+ else
65
+ signer_info.signed_time rescue super
66
+ end
63
67
  end
64
68
 
65
69
  # Returns the certificate chain.
@@ -78,6 +82,23 @@ module HexaPDF
78
82
  @pkcs7.signers.first
79
83
  end
80
84
 
85
+ # Returns the OpenSSL::PKCS7 object for the embedded TSA signature if there is one or +nil+
86
+ # otherwise.
87
+ def embedded_tsa_signature
88
+ return @embedded_tsa_signature if defined?(@embedded_tsa_signature)
89
+
90
+ @embedded_tsa_signature = nil
91
+ p7 = OpenSSL::ASN1.decode(signature_dict.contents.sub(/\x00*\z/, ''))
92
+ signed_data = p7.value[1].value[0]
93
+ signer_info = signed_data.value[-1].value[0] # first (and only) signer info
94
+ return unless signer_info.value[-1].tag == 1 # check for unsigned attributes
95
+ timestamp_token = signer_info.value[-1].value.find do |unsigned_attr|
96
+ unsigned_attr.value[0].value == "id-smime-aa-timeStampToken"
97
+ end
98
+ return unless timestamp_token
99
+ @embedded_tsa_signature = OpenSSL::PKCS7.new(timestamp_token.value[1].value[0])
100
+ end
101
+
81
102
  # Verifies the signature using the provided OpenSSL::X509::Store object.
82
103
  def verify(store, allow_self_signed: false)
83
104
  result = super
@@ -101,9 +122,16 @@ module HexaPDF
101
122
  return result
102
123
  end
103
124
 
125
+ if embedded_tsa_signature
126
+ result.log(:info, 'Signing time comes from timestamp authority')
127
+ end
128
+
104
129
  key_usage = signer_certificate.extensions.find {|ext| ext.oid == 'keyUsage' }
105
- unless key_usage && key_usage.value.split(', ').include?("Digital Signature")
106
- result.log(:error, "Certificate key usage is missing 'Digital Signature'")
130
+ key_usage = key_usage&.value&.split(', ')
131
+ if key_usage&.include?("Non Repudiation") && !key_usage.include?("Digital Signature")
132
+ result.log(:info, 'Certificate used for non-repudiation')
133
+ elsif !key_usage || !key_usage.include?("Digital Signature")
134
+ result.log(:error, "Certificate key usage is missing 'Digital Signature' or 'Non Repudiation'")
107
135
  end
108
136
 
109
137
  if signature_dict.signature_type == 'ETSI.RFC3161'
@@ -211,6 +211,13 @@ module HexaPDF
211
211
  # The contact information. If used, will be set on the signature dictionary.
212
212
  attr_accessor :contact_info
213
213
 
214
+ # The custom signing time.
215
+ #
216
+ # The signing time is usually the time when signing actually happens. This is also what
217
+ # HexaPDF uses. If it is known that signing happened at a different point in time, that time
218
+ # can be provided using this accessor.
219
+ attr_accessor :signing_time
220
+
214
221
  # The size of the serialized signature that should be reserved.
215
222
  #
216
223
  # If this attribute is not set, an empty string will be signed using #sign to determine the
@@ -277,7 +284,7 @@ module HexaPDF
277
284
  def finalize_objects(_signature_field, signature)
278
285
  signature[:Filter] = :'Adobe.PPKLite'
279
286
  signature[:SubFilter] = (signature_type == :pades ? :'ETSI.CAdES.detached' : :'adbe.pkcs7.detached')
280
- signature[:M] = Time.now
287
+ signature[:M] = self.signing_time ||= Time.now
281
288
  signature[:Reason] = reason if reason
282
289
  signature[:Location] = location if location
283
290
  signature[:ContactInfo] = contact_info if contact_info
@@ -312,6 +319,7 @@ module HexaPDF
312
319
  type: signature_type,
313
320
  certificate: certificate, key: key,
314
321
  digest_algorithm: digest_algorithm,
322
+ signing_time: signing_time,
315
323
  timestamp_handler: timestamp_handler,
316
324
  certificates: certificate_chain, &external_signing).to_der
317
325
  else
@@ -84,6 +84,9 @@ module HexaPDF
84
84
  # Allowed values: sha256, sha384, sha512.
85
85
  attr_accessor :digest_algorithm
86
86
 
87
+ # The signing time to use instead of Time.now.
88
+ attr_accessor :signing_time
89
+
87
90
  # The timestamp handler instance that should be used for timestamping.
88
91
  attr_accessor :timestamp_handler
89
92
 
@@ -119,9 +122,10 @@ module HexaPDF
119
122
 
120
123
  # Creates the set of signed attributes for the signer information structure.
121
124
  def create_signed_attrs(data, signing_time: true)
125
+ signing_time = (self.signing_time || Time.now).utc if signing_time
122
126
  set(
123
127
  attribute('content-type', oid('id-data')),
124
- (attribute('id-signingTime', utc_time(Time.now.utc)) if signing_time),
128
+ (attribute('id-signingTime', utc_time(signing_time)) if signing_time),
125
129
  attribute(
126
130
  'message-digest',
127
131
  binary(OpenSSL::Digest.digest(@digest_algorithm, data))
@@ -77,9 +77,9 @@ module HexaPDF
77
77
  #
78
78
  # One style property, Layout::Style#font, is handled specially:
79
79
  #
80
- # * If no font is set on a style, the font "Times" is automatically set because otherwise there
81
- # would be problems with text drawing operations (font is the only style property that has no
82
- # valid default value).
80
+ # * If no font is set on a style, the default font specified via the configuration option
81
+ # 'font.default' is automatically set because otherwise there would be problems with text
82
+ # drawing operations (font is the only style property that has no valid default value).
83
83
  #
84
84
  # * Standard style objects only allow font wrapper objects to be set via the Layout::Style#font
85
85
  # method. This class makes usage easier by allowing strings or an array [name, options_hash]
@@ -151,7 +151,9 @@ module HexaPDF
151
151
  # :nodoc:
152
152
  def method_missing(name, *args, **kwargs, &block)
153
153
  if @layout.box_creation_method?(name)
154
- @children << @layout.send(name, *args, **kwargs, &block)
154
+ box = @layout.send(name, *args, **kwargs, &block)
155
+ @children << box
156
+ box
155
157
  else
156
158
  super
157
159
  end
@@ -354,6 +356,7 @@ module HexaPDF
354
356
  width: width, height: height, properties: properties,
355
357
  style: box_style)
356
358
  end
359
+ alias text text_box
357
360
 
358
361
  # Creates a HexaPDF::Layout::TextBox like #text_box but allows parts of the text to be
359
362
  # formatted differently.
@@ -456,6 +459,7 @@ module HexaPDF
456
459
  box_class_for_name(:text).new(items: data, width: width, height: height,
457
460
  properties: properties, style: box_style)
458
461
  end
462
+ alias formatted_text formatted_text_box
459
463
 
460
464
  # Creates a HexaPDF::Layout::ImageBox for the given image.
461
465
  #
@@ -477,6 +481,7 @@ module HexaPDF
477
481
  box_class_for_name(:image).new(image: image, width: width, height: height,
478
482
  properties: properties, style: style)
479
483
  end
484
+ alias image image_box
480
485
 
481
486
  # This helper class is used by Layout#table_box to allow specifying the keyword arguments used
482
487
  # when converting cell data to box instances.
@@ -495,8 +500,25 @@ module HexaPDF
495
500
  @number_of_columns = number_of_columns
496
501
  end
497
502
 
498
- # Stores the keyword arguments in +args+ for the given 0-based rows and columns which can
499
- # either be a single number or a range of numbers.
503
+ # Stores the hash +args+ containing styling properties for the cells specified via the given
504
+ # 0-based rows and columns.
505
+ #
506
+ # Rows and columns can either be single numbers, ranges of numbers or stepped ranges (i.e.
507
+ # Enumerator::ArithmeticSequence instances).
508
+ #
509
+ # Examples:
510
+ #
511
+ # # Gray background for all cells
512
+ # args[] = {cell: {background_color: "gray"}}
513
+ #
514
+ # # Cell at (2, 3) gets a bigger font size
515
+ # args[2, 3] = {font_size: 50}
516
+ #
517
+ # # First column of every row has bold font
518
+ # args[0..-1, 0] = {font: 'Helvetica bold'}
519
+ #
520
+ # # Every second row has a blue background
521
+ # args[(0..-1).step(2)] = {cell: {background_color: "blue"}}
500
522
  def []=(rows = 0..-1, cols = 0..-1, args)
501
523
  rows = adjust_range(rows.kind_of?(Integer) ? rows..rows : rows, @number_of_rows)
502
524
  cols = adjust_range(cols.kind_of?(Integer) ? cols..cols : cols, @number_of_columns)
@@ -509,7 +531,7 @@ module HexaPDF
509
531
  # is merged.
510
532
  def retrieve_arguments_for(row, col)
511
533
  @argument_infos.each_with_object({}) do |arg_info, result|
512
- next unless arg_info.rows.cover?(row) && arg_info.cols.cover?(col)
534
+ next unless arg_info.rows.include?(row) && arg_info.cols.include?(col)
513
535
  if arg_info.args[:cell]
514
536
  arg_info.args[:cell] = (result[:cell] || {}).merge(arg_info.args[:cell])
515
537
  end
@@ -522,7 +544,8 @@ module HexaPDF
522
544
  # Adjusts the +range+ so that both the begin and the end of the range are zero or positive
523
545
  # integers smaller than +max+.
524
546
  def adjust_range(range, max)
525
- (range.begin % max)..(range.end % max)
547
+ r = (range.begin % max)..(range.end % max)
548
+ range.kind_of?(Range) ? r : r.step(range.step)
526
549
  end
527
550
 
528
551
  end
@@ -540,7 +563,8 @@ module HexaPDF
540
563
  # Additional arguments for the #text_box invocations can be specified using the optional block
541
564
  # that yields a CellArgumentCollector instance. This allows customization of the text boxes.
542
565
  # By specifying the special key +:cell+ it is also possible to assign style properties to the
543
- # cells themselves.
566
+ # cells themselves, irrespective of the type of content of the cells. See
567
+ # CellArgumentCollector#[]= for details.
544
568
  #
545
569
  # See HexaPDF::Layout::TableBox::new for details on +column_widths+, +header+, +footer+, and
546
570
  # +cell_style+.
@@ -586,6 +610,7 @@ module HexaPDF
586
610
  footer: footer, cell_style: cell_style, width: width,
587
611
  height: height, properties: properties, style: style)
588
612
  end
613
+ alias table table_box
589
614
 
590
615
  LOREM_IPSUM = [ # :nodoc:
591
616
  "Lorem ipsum dolor sit amet, con\u{00AD}sectetur adipis\u{00AD}cing elit, sed " \
@@ -605,22 +630,13 @@ module HexaPDF
605
630
  def lorem_ipsum_box(sentences: 4, count: 1, **text_box_properties)
606
631
  text_box(([LOREM_IPSUM[0, sentences].join(" ")] * count).join("\n\n"), **text_box_properties)
607
632
  end
633
+ alias lorem_ipsum lorem_ipsum_box
608
634
 
609
- BOX_METHOD_NAMES = [:text, :formatted_text, :image, :table, :lorem_ipsum] #:nodoc:
610
-
611
- # Allows creating boxes using more convenient method names:
612
- #
613
- # * #text for #text_box
614
- # * #formatted_text for #formatted_text_box
615
- # * #image for #image_box
616
- # * #lorem_ipsum for #lorem_ipsum_box
617
- # * The name of a pre-defined box class like #column will invoke #box appropriately. Same if
618
- # used with a '_box' suffix.
635
+ # Allows creating boxes using more convenient method names: The name of a pre-defined box
636
+ # class like #column will invoke #box appropriately. Same if used with a '_box' suffix.
619
637
  def method_missing(name, *args, **kwargs, &block)
620
638
  name_without_box = name.to_s.sub(/_box$/, '').intern
621
- if BOX_METHOD_NAMES.include?(name)
622
- send("#{name}_box", *args, **kwargs, &block)
623
- elsif @document.config['layout.boxes.map'].key?(name_without_box)
639
+ if @document.config['layout.boxes.map'].key?(name_without_box)
624
640
  box(name_without_box, *args, **kwargs, &block)
625
641
  else
626
642
  super
@@ -632,6 +648,8 @@ module HexaPDF
632
648
  box_creation_method?(name) || super
633
649
  end
634
650
 
651
+ BOX_METHOD_NAMES = [:text, :formatted_text, :image, :table, :lorem_ipsum] #:nodoc:
652
+
635
653
  # :nodoc:
636
654
  def box_creation_method?(name)
637
655
  name = name.to_s.sub(/_box$/, '').intern
@@ -648,8 +666,8 @@ module HexaPDF
648
666
  end
649
667
  end
650
668
 
651
- # Retrieves the appropriate HexaPDF::Layout::Style object based on the +style+ and +properties+
652
- # arguments.
669
+ # Retrieves the appropriate HexaPDF::Layout::Style object based on the +style+ and
670
+ # +properties+ arguments.
653
671
  #
654
672
  # The +style+ argument specifies the style to retrieve. It can either be a registered style
655
673
  # name (see #style), a hash with style properties or +nil+. In the latter case the registered
@@ -658,15 +676,18 @@ module HexaPDF
658
676
  # If the +properties+ hash is not empty, the retrieved style is duplicated and the properties
659
677
  # hash is applied to it.
660
678
  #
661
- # Finally, a default font (the one from the :base style or otherwise 'Times') is set if
662
- # necessary to ensure that the style object works in all cases.
679
+ # Finally, a default font (the one from the :base style or otherwise the one set using the
680
+ # configuration option 'font.default') is set if necessary to ensure that the style object
681
+ # works in all cases.
663
682
  def retrieve_style(style, properties = nil)
664
683
  if style.kind_of?(Symbol) && !@styles.key?(style)
665
684
  raise HexaPDF::Error, "Style #{style} not defined"
666
685
  end
667
686
  style = HexaPDF::Layout::Style.create(@styles[style] || style || @styles[:base])
668
687
  style = style.dup.update(**properties) unless properties.nil? || properties.empty?
669
- style.font(@styles[:base].font? && @styles[:base].font || 'Times') unless style.font?
688
+ unless style.font?
689
+ style.font(@styles[:base].font? && @styles[:base].font || @document.config['font.default'])
690
+ end
670
691
  unless style.font.respond_to?(:pdf_object)
671
692
  name, options = *style.font
672
693
  style.font(@document.fonts.add(name, **(options || {})))