hexapdf 0.44.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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +106 -47
  3. data/examples/019-acro_form.rb +5 -0
  4. data/examples/027-composer_optional_content.rb +6 -4
  5. data/examples/030-pdfa.rb +12 -11
  6. data/lib/hexapdf/cli/inspect.rb +5 -0
  7. data/lib/hexapdf/composer.rb +23 -1
  8. data/lib/hexapdf/configuration.rb +8 -0
  9. data/lib/hexapdf/content/canvas.rb +3 -3
  10. data/lib/hexapdf/content/canvas_composer.rb +1 -0
  11. data/lib/hexapdf/digital_signature/cms_handler.rb +31 -3
  12. data/lib/hexapdf/digital_signature/signing/default_handler.rb +9 -1
  13. data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +5 -1
  14. data/lib/hexapdf/document/layout.rb +63 -30
  15. data/lib/hexapdf/document.rb +24 -2
  16. data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
  17. data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
  18. data/lib/hexapdf/importer.rb +15 -5
  19. data/lib/hexapdf/layout/box.rb +48 -36
  20. data/lib/hexapdf/layout/column_box.rb +3 -11
  21. data/lib/hexapdf/layout/container_box.rb +4 -4
  22. data/lib/hexapdf/layout/frame.rb +7 -6
  23. data/lib/hexapdf/layout/inline_box.rb +17 -23
  24. data/lib/hexapdf/layout/list_box.rb +27 -42
  25. data/lib/hexapdf/layout/page_style.rb +23 -16
  26. data/lib/hexapdf/layout/style.rb +5 -5
  27. data/lib/hexapdf/layout/table_box.rb +14 -10
  28. data/lib/hexapdf/layout/text_box.rb +60 -36
  29. data/lib/hexapdf/layout/text_fragment.rb +1 -1
  30. data/lib/hexapdf/layout/text_layouter.rb +7 -8
  31. data/lib/hexapdf/parser.rb +5 -1
  32. data/lib/hexapdf/rectangle.rb +4 -4
  33. data/lib/hexapdf/revisions.rb +1 -1
  34. data/lib/hexapdf/stream.rb +3 -3
  35. data/lib/hexapdf/tokenizer.rb +3 -2
  36. data/lib/hexapdf/type/acro_form/button_field.rb +2 -0
  37. data/lib/hexapdf/type/acro_form/choice_field.rb +2 -0
  38. data/lib/hexapdf/type/acro_form/field.rb +8 -0
  39. data/lib/hexapdf/type/acro_form/form.rb +2 -1
  40. data/lib/hexapdf/type/acro_form/text_field.rb +2 -0
  41. data/lib/hexapdf/type/form.rb +2 -2
  42. data/lib/hexapdf/version.rb +1 -1
  43. data/test/hexapdf/content/test_canvas_composer.rb +13 -8
  44. data/test/hexapdf/digital_signature/common.rb +66 -84
  45. data/test/hexapdf/digital_signature/signing/test_default_handler.rb +7 -0
  46. data/test/hexapdf/digital_signature/signing/test_signed_data_creator.rb +9 -0
  47. data/test/hexapdf/digital_signature/test_cms_handler.rb +41 -1
  48. data/test/hexapdf/digital_signature/test_handler.rb +2 -1
  49. data/test/hexapdf/document/test_layout.rb +44 -5
  50. data/test/hexapdf/layout/test_box.rb +23 -5
  51. data/test/hexapdf/layout/test_frame.rb +21 -2
  52. data/test/hexapdf/layout/test_inline_box.rb +17 -28
  53. data/test/hexapdf/layout/test_list_box.rb +8 -8
  54. data/test/hexapdf/layout/test_page_style.rb +7 -2
  55. data/test/hexapdf/layout/test_table_box.rb +8 -1
  56. data/test/hexapdf/layout/test_text_box.rb +51 -29
  57. data/test/hexapdf/layout/test_text_layouter.rb +0 -3
  58. data/test/hexapdf/test_composer.rb +14 -5
  59. data/test/hexapdf/test_document.rb +27 -0
  60. data/test/hexapdf/test_importer.rb +17 -0
  61. data/test/hexapdf/test_revisions.rb +54 -41
  62. data/test/hexapdf/test_serializer.rb +1 -0
  63. data/test/hexapdf/type/acro_form/test_form.rb +9 -0
  64. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b30b54b90222fbcc5469b23153efc4b15083797f527af6c5b34b3f6e161832d
4
- data.tar.gz: 969c67ab85591e3b7ccad17023bd3bc820ea5effa4c01e53d254915ab1626d71
3
+ metadata.gz: f9c1a35d4ad93b48faa1f728bcddc91778da584c8d673e2f15557bd22050a438
4
+ data.tar.gz: 2ef8ca70891723643080a402f0808882f66f01a816c422477cc03fad34774859
5
5
  SHA512:
6
- metadata.gz: b60934dd6534ccd019953b278fa188b77996634b3e3878332302b9a2acccfd13b493b57432cff2113b7cc7727f028f24301c2d050f9b908c1b43cf66fbdeebfd
7
- data.tar.gz: fdd792109306bc7cf0e30bb2da770e58e81e333843e3c785a9a31873a296b7d027121b467b54a80405332735e99bdaf6b0d167c9eaf08dbc04a26922b890bb51
6
+ metadata.gz: 87c109bd8a6711b4df27a40c689a025d816075d6c68c21a1cc22924b5ae827cd44d98e45bff8f43c85403dd82d6a6e54b66c99bfe135298cba33292e9511a1fc
7
+ data.tar.gz: e0529e3e244be8366e722ea29ab06a8b922f18698526c79a28d5d11f02da5ee103cba71ecbc6882fefe3de27f02d38f8ce8b505fefbb45839069c74aefaed9ae
data/CHANGELOG.md CHANGED
@@ -1,3 +1,62 @@
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
+
41
+ ## 0.45.0 - 2024-06-18
42
+
43
+ ### Added
44
+
45
+ * [HexaPDF::Document::Layout#styles] and [HexaPDF::Composer#styles] for defining
46
+ multiple styles at once
47
+
48
+ ### Changed
49
+
50
+ * [HexaPDF::Layout::Box#fit] to set width/height correctly for boxes with
51
+ position `:flow`
52
+
53
+ ### Fixed
54
+
55
+ * Regression in [HexaPDF::Layout::ListBox] that leads to missing markers
56
+ * [HexaPDF::Content::CanvasComposer#draw_box] to handle truncated boxes
57
+ * [HexaPDF::Layout::TableBox::Cell] to handle too-big content in all cases
58
+
59
+
1
60
  ## 0.44.0 - 2024-06-05
2
61
 
3
62
  ### Added
@@ -356,8 +415,8 @@
356
415
 
357
416
  ### Fixed
358
417
 
359
- * [HexaPDF::Document::Pages#add_labelling_range] to add a correct entry for
360
- 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
361
420
  * [HexaPDF::Type::Page#flatten_annotations] to correctly handle scaled
362
421
  appearances
363
422
  * Using an unknown style name in [HexaPDF:Document::Layout] method by providing
@@ -368,6 +427,8 @@
368
427
  than the item content height
369
428
  * [HexaPDF::Dictionary] setting default values on wrong classes in certain
370
429
  situations
430
+ * [HexaPDF::Importer#import] to correctly import stream objects backed by a
431
+ [HexaPDF::FiberDoubleForString]
371
432
 
372
433
 
373
434
  ## 0.33.0 - 2023-08-02
@@ -420,8 +481,8 @@
420
481
  * [HexaPDF::Content::Canvas#text] to set the leading only when multiple lines
421
482
  are drawn
422
483
  * [HexaPDF::Layout::TextBox#split] to use float comparison
423
- * Validation of standard encryption dictionary to auto-correct invalid /U and
424
- /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
425
486
  * [HexaPDF::Document#wrap] handling of sub-type mapping in case of missing type
426
487
  * [HexaPDF::Type::AcroForm::AppearanceGenerator] to also take a text field
427
488
  widget's width into account when auto-sizing
@@ -742,8 +803,8 @@
742
803
  * Support for the document outline
743
804
  * [HexaPDF::Layout::Style#line_height] for setting a custom line height
744
805
  independent of the font size
745
- * [HexaPDF::Document::Destinations#use_or_create] as unified interface for
746
- using or creating destinations
806
+ * [HexaPDF::Document::Destinations#use_or_create] as unified interface for using
807
+ or creating destinations
747
808
  * [HexaPDF::Document::Destinations::Destination#valid?] and class method for
748
809
  checking whether a destination array is valid
749
810
 
@@ -752,8 +813,8 @@
752
813
  * Calculation of text related [HexaPDF::Layout::Style] values for Type3 fonts
753
814
  * [HexaPDF::Encryption::SecurityHandler#encrypt_string] to either return a
754
815
  dupped or encrypted string
755
- * [HexaPDF::Layout::TextLayouter#fit] to avoid infinite loop when encountering
756
- a non-zero width breakpoint penalty
816
+ * [HexaPDF::Layout::TextLayouter#fit] to avoid infinite loop when encountering a
817
+ non-zero width breakpoint penalty
757
818
  * [HexaPDF::Type::ObjectStream] to parse the initial stream data right after
758
819
  initialization to avoid access errors
759
820
  * [HexaPDF::Revisions::from_io] to merge a completely empty revision with just a
@@ -835,8 +896,7 @@
835
896
  fragment if there would not be enough height left anyway
836
897
  * [HexaPDF::Layout::WidthFromPolygon] to work correctly in case of very small
837
898
  floating point errors
838
- * HexaPDF::Layout::TextFragment#inspect to work in case of interspersed
839
- numbers
899
+ * HexaPDF::Layout::TextFragment#inspect to work in case of interspersed numbers
840
900
  * [HexaPDF::Layout::TextBox#split] to work for position :flow when box is wider
841
901
  than the initial available width
842
902
  * [HexaPDF::Layout::Frame#fit] to create minimally sized mask rectangles
@@ -1118,8 +1178,8 @@
1118
1178
  dictionary are indirect objects
1119
1179
  * [HexaPDF::Content::GraphicObject::EndpointArc] to correctly determine the
1120
1180
  start and end points
1121
- * HexaPDF::Dictionary#perform_validation to correctly handle objects that
1122
- should not be indirect objects
1181
+ * HexaPDF::Dictionary#perform_validation to correctly handle objects that should
1182
+ not be indirect objects
1123
1183
 
1124
1184
 
1125
1185
  ## 0.17.3 - 2021-10-31
@@ -1241,8 +1301,8 @@
1241
1301
 
1242
1302
  ### Fixed
1243
1303
 
1244
- * [HexaPDF::Type::Annotation#appearance] to handle cases where there is
1245
- no valid appearance stream
1304
+ * [HexaPDF::Type::Annotation#appearance] to handle cases where there is no valid
1305
+ appearance stream
1246
1306
 
1247
1307
 
1248
1308
  ## 0.15.3 - 2021-05-01
@@ -1297,8 +1357,8 @@
1297
1357
  empty background color arrays
1298
1358
  * [HexaPDF::Type::AcroForm::Field#delete_widget] to update the wrapper object
1299
1359
  stored in the document in case the widget is embedded
1300
- * Processing of invalid PDF files containing a space,CR,LF combination after
1301
- the 'stream' keyword
1360
+ * Processing of invalid PDF files containing a space,CR,LF combination after the
1361
+ 'stream' keyword
1302
1362
  * Cross-reference stream reconstruction with respect to detection of linearized
1303
1363
  files
1304
1364
  * Detection of existing appearances for AcroForm push button fields when
@@ -1393,8 +1453,8 @@
1393
1453
 
1394
1454
  * [HexaPDF::Utils::ObjectHash#oids] to be public instead of private
1395
1455
  * Cross-reference table parsing to handle invalidly numbered main sections
1396
- * [HexaPDF::Document#cache] and [HexaPDF::Object#cache] to allow updating
1397
- values for existing keys
1456
+ * [HexaPDF::Document#cache] and [HexaPDF::Object#cache] to allow updating values
1457
+ for existing keys
1398
1458
  * Appearance creation methods of AcroForm objects to allow forcing the creation
1399
1459
  of new appearances
1400
1460
  * [HexaPDF::Type::AcroForm::AppearanceGenerator#create_text_appearances] to
@@ -1432,8 +1492,8 @@
1432
1492
  new 'parser.try_xref_reconstruction' option
1433
1493
  * Two new `hexapdf inspect` commands for showing page objects and page content
1434
1494
  streams by page number
1435
- * Flag `--check` to the CLI command `hexapdf info` for checking a file for
1436
- parse and validation errors
1495
+ * Flag `--check` to the CLI command `hexapdf info` for checking a file for parse
1496
+ and validation errors
1437
1497
  * [HexaPDF::Type::AcroForm::Field#embedded_widget?] for checking if a widget is
1438
1498
  embedded in the field object
1439
1499
  * [HexaPDF::Type::AcroForm::Field#delete_widget] for deleting a widget
@@ -1490,8 +1550,8 @@
1490
1550
 
1491
1551
  ### Added
1492
1552
 
1493
- * [HexaPDF::Font::Encoding::Base#code] for retrieving the code for a given
1494
- glyph name
1553
+ * [HexaPDF::Font::Encoding::Base#code] for retrieving the code for a given glyph
1554
+ name
1495
1555
 
1496
1556
  ### Fixed
1497
1557
 
@@ -1507,11 +1567,11 @@
1507
1567
  [HexaPDF::Type::AcroForm::Field]
1508
1568
  * [HexaPDF::Type::AcroForm::TextField] and
1509
1569
  [HexaPDF::Type::AcroForm::VariableTextField] for basic text field support
1510
- * [HexaPDF::Type::AcroForm::ButtonField] for push button, radio button and
1511
- check box support
1570
+ * [HexaPDF::Type::AcroForm::ButtonField] for push button, radio button and check
1571
+ box support
1512
1572
  * [HexaPDF::Type::AcroForm::ChoiceField] for combo box and list box support
1513
- * [HexaPDF::Type::AcroForm::AppearanceGenerator] as central class for
1514
- generating appearance streams for form fields
1573
+ * [HexaPDF::Type::AcroForm::AppearanceGenerator] as central class for generating
1574
+ appearance streams for form fields
1515
1575
  * Various convenience methods for [HexaPDF::Type::AcroForm::Form]
1516
1576
  * Various convenience methods for [HexaPDF::Type::AcroForm::Field]
1517
1577
  * Various convenience methods for [HexaPDF::Type::Annotations::Widget]
@@ -1530,8 +1590,8 @@
1530
1590
  * [HexaPDF::Type::Annotation::Border] class
1531
1591
  * [HexaPDF::Content::ColorSpace::device_color_from_specification] for easily
1532
1592
  getting a device color object
1533
- * [HexaPDF::Content::ColorSpace::prenormalized_device_color] for getting a device
1534
- color object without normalizing values
1593
+ * [HexaPDF::Content::ColorSpace::prenormalized_device_color] for getting a
1594
+ device color object without normalizing values
1535
1595
  * [HexaPDF::Type::Annotation#appearance] for returning the associated appearance
1536
1596
  dictionary
1537
1597
  * [HexaPDF::Type::Annotation#appearance?] for checking whether an appearance for
@@ -1650,8 +1710,8 @@
1650
1710
 
1651
1711
  ### Fixed
1652
1712
 
1653
- * Conversion of [HexaPDF::Rectangle] type when the original is not a plain
1654
- Array but a [HexaPDF::PDFArray]
1713
+ * Conversion of [HexaPDF::Rectangle] type when the original is not a plain Array
1714
+ but a [HexaPDF::PDFArray]
1655
1715
 
1656
1716
 
1657
1717
  ## 0.11.1 - 2019-11-19
@@ -1812,12 +1872,12 @@
1812
1872
 
1813
1873
  ### Added
1814
1874
 
1815
- * [HexaPDF::Layout::Frame] for box positioning and easier text layouting
1816
- inside an arbitrary polygon
1875
+ * [HexaPDF::Layout::Frame] for box positioning and easier text layouting inside
1876
+ an arbitrary polygon
1817
1877
  * [HexaPDF::Layout::TextBox] for displaying text in a rectangular and for
1818
1878
  flowing text inside a frame
1819
- * [HexaPDF::Layout::WidthFromPolygon] for getting a width specification from
1820
- 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
1821
1881
  * [HexaPDF::Type::Image#width] and [HexaPDF::Type::Image#height] convenience
1822
1882
  methods
1823
1883
  * [HexaPDF::Type::FontType3] for Type 3 font support
@@ -1869,12 +1929,12 @@
1869
1929
  character in a text fragment is \r
1870
1930
  * [HexaPDF::Layout::TextLayouter] to work if an optional break point (think
1871
1931
  soft-hyphen) is followed by whitespace
1872
- * [HexaPDF::Font::TrueType::Builder] to correctly order the entries in the
1873
- table directory
1932
+ * [HexaPDF::Font::TrueType::Builder] to correctly order the entries in the table
1933
+ directory
1874
1934
  * [HexaPDF::Font::TrueType::Builder] to pad the table data to achieve the
1875
1935
  correct alignment
1876
- * [HexaPDF::Filter::FlateDecode] by removing the Zlib pools since they were
1877
- not thread safe
1936
+ * [HexaPDF::Filter::FlateDecode] by removing the Zlib pools since they were not
1937
+ thread safe
1878
1938
  * All color space classes to accept the color space definition as argument to
1879
1939
  `::new`
1880
1940
 
@@ -1906,9 +1966,8 @@
1906
1966
  * Cross-reference subsection parsing can handle missing whitespace
1907
1967
  * Renamed HexaPDF::Layout::LineFragment to [HexaPDF::Layout::Line]
1908
1968
  * Renamed HexaPDF::Layout::TextBox to [HexaPDF::Layout::TextLayouter]
1909
- * [HexaPDF::Layout::TextFragment::new] and
1910
- [HexaPDF::Layout::TextLayouter::new] to either take a Style object or
1911
- style options
1969
+ * [HexaPDF::Layout::TextFragment::new] and [HexaPDF::Layout::TextLayouter::new]
1970
+ to either take a Style object or style options
1912
1971
  * [HexaPDF::Layout::TextLayouter#fit] method signature
1913
1972
  * [HexaPDF::Layout::InlineBox] to wrap a generic box
1914
1973
  * HexaPDF::Document::Fonts#load to [HexaPDF::Document::Fonts#add] for
@@ -1970,8 +2029,8 @@
1970
2029
 
1971
2030
  * Handling of invalid glyphs is done using the special
1972
2031
  [HexaPDF::Font::InvalidGlyph] class
1973
- * Configuration option 'font.on_missing_glyph'; returns an invalid glyph
1974
- instead of raising an error
2032
+ * Configuration option 'font.on_missing_glyph'; returns an invalid glyph instead
2033
+ of raising an error
1975
2034
  * Bounding box of TrueType glyphs without contours is set to `[0, 0, 0, 0]`
1976
2035
  * Ligature pairs for AFM fonts are stored like kerning pairs
1977
2036
  * Use TrueType configuration option 'font.true_type.unknown_format' in all
@@ -1988,8 +2047,8 @@
1988
2047
 
1989
2048
  * [HexaPDF::Task::Dereference] to work correctly when encountering invalid
1990
2049
  references
1991
- * [HexaPDF::Tokenizer] and HexaPDF::Content::Tokenizer to parse a solitary
1992
- plus sign
2050
+ * [HexaPDF::Tokenizer] and HexaPDF::Content::Tokenizer to parse a solitary plus
2051
+ sign
1993
2052
  * Usage of Strings instead of Symbols for AFM font kerning and ligature pairs
1994
2053
  * Processing the contents of form XObjects in case they don't have a resources
1995
2054
  dictionary
@@ -2012,8 +2071,8 @@
2012
2071
  * CLI option `--verbose` for more verbose output; also changed the default
2013
2072
  verbosity level to only display warnings and not informational messages
2014
2073
  * CLI option `--quiet` for suppressing additional and diagnostic output
2015
- * CLI option `--strict` for enabling strict parsing and validation; also
2016
- 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
2017
2076
  * CLI optimization option `--optimize-fonts` for optimizing embedded fonts
2018
2077
  * Method `#word_spacing_applicable?` to font types
2019
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
  #
@@ -16,8 +16,10 @@
16
16
  require 'hexapdf'
17
17
 
18
18
  HexaPDF::Composer.create('composer_optional_content.pdf') do |composer|
19
- composer.style(:question, font_size: 16, margin: [0, 0, 16], fill_color: 'hp-blue')
20
- composer.style(:answer, font: 'ZapfDingbats', fill_color: "green")
19
+ composer.styles(
20
+ question: {font_size: 16, margin: [0, 0, 16], fill_color: 'hp-blue'},
21
+ answer: {font: 'ZapfDingbats', fill_color: "green"},
22
+ )
21
23
 
22
24
  all = composer.document.optional_content.ocg('All answers')
23
25
  a1 = composer.document.optional_content.ocg('Answer 1')
@@ -38,7 +40,7 @@ HexaPDF::Composer.create('composer_optional_content.pdf') do |composer|
38
40
  answers.text('Guido van Rossum')
39
41
  answers.multiple do |answer|
40
42
  answer.text('Yukihiro “Matz” Matsumoto', position: :float)
41
- answer.text("\u{a0}\u{a0}4", style: :answer,
43
+ answer.text("\u{a0}\u{a0}", style: :answer,
42
44
  properties: {'optional_content' => a1})
43
45
  end
44
46
  answers.text('Rob Pike')
@@ -54,7 +56,7 @@ HexaPDF::Composer.create('composer_optional_content.pdf') do |composer|
54
56
  answers.text('1992')
55
57
  answers.multiple do |answer|
56
58
  answer.text('1993', position: :float)
57
- answer.text("\u{a0}\u{a0}4", style: :answer,
59
+ answer.text("\u{a0}\u{a0}", style: :answer,
58
60
  properties: {'optional_content' => a2})
59
61
  end
60
62
  end
data/examples/030-pdfa.rb CHANGED
@@ -28,17 +28,18 @@ HexaPDF::Composer.create('pdfa.pdf') do |composer|
28
28
  }
29
29
 
30
30
  # Define all styles
31
- composer.style(:base, font: 'Lato', font_size: 10, line_spacing: 1.3)
32
- composer.style(:top, font_size: 8)
33
- composer.style(:top_box, padding: [100, 0, 0], margin: [0, 0, 10], border: {width: [0, 0, 1]})
34
- composer.style(:header, font: 'Lato bold', font_size: 20, margin: [50, 0, 20])
35
- composer.style(:line_items, border: {width: 1, color: "eee"}, margin: [20, 0])
36
- composer.style(:line_item_cell, font_size: 8)
37
- composer.style(:footer, border: {width: [1, 0, 0], color: "darkgrey"},
38
- padding: [5, 0, 0], valign: :bottom)
39
- composer.style(:footer_heading, font: 'Lato bold',
40
- font_size: 8, padding: [0, 0, 8])
41
- composer.style(:footer_text, font_size: 8, fill_color: "darkgrey")
31
+ composer.styles(
32
+ base: {font: 'Lato', font_size: 10, line_spacing: 1.3},
33
+ top: {font_size: 8},
34
+ top_box: {padding: [100, 0, 0], margin: [0, 0, 10], border: {width: [0, 0, 1]}},
35
+ header: {font: 'Lato bold', font_size: 20, margin: [50, 0, 20]},
36
+ line_items: {border: {width: 1, color: "eee"}, margin: [20, 0]},
37
+ line_item_cell: {font_size: 8},
38
+ footer: {border: {width: [1, 0, 0], color: "darkgrey"}, padding: [5, 0, 0],
39
+ valign: :bottom},
40
+ footer_heading: {font: 'Lato bold', font_size: 8, padding: [0, 0, 8]},
41
+ footer_text: {font_size: 8, fill_color: "darkgrey"},
42
+ )
42
43
 
43
44
  # Top part
44
45
  composer.box(:container, style: :top_box) do |container|
@@ -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
@@ -254,6 +254,28 @@ module HexaPDF
254
254
  @document.layout.style(name, base: base, **properties)
255
255
  end
256
256
 
257
+ # :call-seq:
258
+ # composer.styles -> styles
259
+ # composer.styles(**mapping) -> styles
260
+ #
261
+ # Creates multiple named styles at once if +mapping+ is provided, and returns the style mapping.
262
+ #
263
+ # See HexaPDF::Document::Layout#styles for details; this method is just a thin wrapper around
264
+ # that method.
265
+ #
266
+ # Example:
267
+ #
268
+ # composer.styles(
269
+ # base: {font_size: 12, leading: 1.2},
270
+ # header: {font: 'Helvetica', fill_color: "008"},
271
+ # header1: {base: :header, font_size: 30}
272
+ # )
273
+ #
274
+ # See: HexaPDF::Layout::Style
275
+ def styles(**mapping)
276
+ @document.layout.styles(**mapping)
277
+ end
278
+
257
279
  # :call-seq:
258
280
  # composer.page_style(name) -> page_style
259
281
  # composer.page_style(name, **attributes, &template_block) -> page_style
@@ -428,7 +450,7 @@ module HexaPDF
428
450
  (box = draw_box; break) unless box
429
451
  elsif !@frame.find_next_region
430
452
  unless drawn_on_page
431
- raise HexaPDF::Error, "Box doesn't fit on empty page"
453
+ raise HexaPDF::Error, "Box didn't fit multiple times, even on empty page"
432
454
  end
433
455
  new_page
434
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),
@@ -1127,7 +1127,7 @@ module HexaPDF
1127
1127
  # canvas.rectangle(x, y, width, height, radius: 0) => canvas
1128
1128
  #
1129
1129
  # Appends a rectangle to the current path as a complete subpath (drawn in counterclockwise
1130
- # direction), with the bottom left corner specified by +x+ and +y+ and the given +width+ and
1130
+ # direction), with the bottom-left corner specified by +x+ and +y+ and the given +width+ and
1131
1131
  # +height+. Returns +self+.
1132
1132
  #
1133
1133
  # If +radius+ is greater than 0, the corners are rounded with the given radius.
@@ -1137,7 +1137,7 @@ module HexaPDF
1137
1137
  #
1138
1138
  # If there is no current path when the method is invoked, a new path is automatically begun.
1139
1139
  #
1140
- # The current point is set to the bottom left corner if +radius+ is zero, otherwise it is set
1140
+ # The current point is set to the bottom-left corner if +radius+ is zero, otherwise it is set
1141
1141
  # to (x, y + radius).
1142
1142
  #
1143
1143
  # Examples:
@@ -1720,7 +1720,7 @@ module HexaPDF
1720
1720
  # If the filename or the IO specifies a PDF file, the first page of this file is used to
1721
1721
  # create a form XObject which is then drawn.
1722
1722
  #
1723
- # The +at+ argument has to be an array containing two numbers specifying the bottom left
1723
+ # The +at+ argument has to be an array containing two numbers specifying the bottom-left
1724
1724
  # corner at which to draw the XObject.
1725
1725
  #
1726
1726
  # If +width+ and +height+ are specified, the drawn XObject will have exactly these
@@ -96,6 +96,7 @@ module HexaPDF
96
96
  draw_box, box = @frame.split(result)
97
97
  if draw_box
98
98
  @frame.draw(@canvas, result)
99
+ (box = draw_box; break) unless box
99
100
  elsif !@frame.find_next_region
100
101
  raise HexaPDF::Error, "Frame for canvas composer is full and box doesn't fit anymore"
101
102
  end
@@ -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))