hexapdf 0.44.0 → 0.46.0

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