hexapdf 0.11.9 → 0.13.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +157 -0
- data/LICENSE +1 -1
- data/examples/001-hello_world.rb +1 -1
- data/examples/002-graphics.rb +1 -1
- data/examples/003-arcs.rb +1 -1
- data/examples/004-optimizing.rb +1 -1
- data/examples/005-merging.rb +1 -1
- data/examples/006-standard_pdf_fonts.rb +1 -1
- data/examples/007-truetype.rb +1 -1
- data/examples/008-show_char_bboxes.rb +1 -1
- data/examples/009-text_layouter_alignment.rb +1 -1
- data/examples/010-text_layouter_inline_boxes.rb +1 -1
- data/examples/011-text_layouter_line_wrapping.rb +1 -1
- data/examples/012-text_layouter_styling.rb +1 -1
- data/examples/013-text_layouter_shapes.rb +1 -1
- data/examples/014-text_in_polygon.rb +1 -1
- data/examples/015-boxes.rb +1 -1
- data/examples/016-frame_automatic_box_placement.rb +1 -1
- data/examples/017-frame_text_flow.rb +1 -1
- data/examples/018-composer.rb +1 -1
- data/examples/019-acro_form.rb +51 -0
- data/lib/hexapdf.rb +1 -1
- data/lib/hexapdf/cli.rb +3 -1
- data/lib/hexapdf/cli/batch.rb +1 -1
- data/lib/hexapdf/cli/command.rb +22 -11
- data/lib/hexapdf/cli/files.rb +1 -1
- data/lib/hexapdf/cli/form.rb +240 -0
- data/lib/hexapdf/cli/image2pdf.rb +3 -2
- data/lib/hexapdf/cli/images.rb +1 -1
- data/lib/hexapdf/cli/info.rb +52 -3
- data/lib/hexapdf/cli/inspect.rb +31 -9
- data/lib/hexapdf/cli/merge.rb +2 -2
- data/lib/hexapdf/cli/modify.rb +1 -1
- data/lib/hexapdf/cli/optimize.rb +1 -1
- data/lib/hexapdf/cli/split.rb +1 -1
- data/lib/hexapdf/cli/watermark.rb +1 -1
- data/lib/hexapdf/composer.rb +2 -2
- data/lib/hexapdf/configuration.rb +81 -11
- data/lib/hexapdf/content.rb +3 -1
- data/lib/hexapdf/content/canvas.rb +5 -18
- data/lib/hexapdf/content/color_space.rb +111 -32
- data/lib/hexapdf/content/graphic_object.rb +1 -1
- data/lib/hexapdf/content/graphic_object/arc.rb +4 -4
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object/geom2d.rb +1 -1
- data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
- data/lib/hexapdf/content/graphics_state.rb +1 -1
- data/lib/hexapdf/content/operator.rb +9 -9
- data/lib/hexapdf/content/parser.rb +18 -5
- data/lib/hexapdf/content/processor.rb +1 -1
- data/lib/hexapdf/content/transformation_matrix.rb +1 -1
- data/lib/hexapdf/data_dir.rb +1 -1
- data/lib/hexapdf/dictionary.rb +5 -5
- data/lib/hexapdf/dictionary_fields.rb +2 -10
- data/lib/hexapdf/document.rb +45 -17
- data/lib/hexapdf/document/files.rb +1 -2
- data/lib/hexapdf/document/fonts.rb +1 -1
- data/lib/hexapdf/document/images.rb +1 -1
- data/lib/hexapdf/document/pages.rb +3 -14
- data/lib/hexapdf/encryption.rb +1 -1
- data/lib/hexapdf/encryption/aes.rb +1 -1
- data/lib/hexapdf/encryption/arc4.rb +1 -1
- data/lib/hexapdf/encryption/fast_aes.rb +1 -1
- data/lib/hexapdf/encryption/fast_arc4.rb +2 -2
- data/lib/hexapdf/encryption/identity.rb +1 -1
- data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
- data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +2 -1
- data/lib/hexapdf/encryption/standard_security_handler.rb +2 -1
- data/lib/hexapdf/error.rb +1 -1
- data/lib/hexapdf/filter.rb +3 -3
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
- data/lib/hexapdf/filter/encryption.rb +1 -1
- data/lib/hexapdf/filter/flate_decode.rb +1 -1
- data/lib/hexapdf/filter/lzw_decode.rb +1 -1
- data/lib/hexapdf/filter/{jpx_decode.rb → pass_through.rb} +5 -5
- data/lib/hexapdf/filter/predictor.rb +1 -1
- data/lib/hexapdf/filter/run_length_decode.rb +1 -1
- data/lib/hexapdf/font/cmap.rb +2 -5
- data/lib/hexapdf/font/cmap/parser.rb +1 -1
- data/lib/hexapdf/font/cmap/writer.rb +1 -1
- data/lib/hexapdf/font/encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/base.rb +9 -1
- data/lib/hexapdf/font/encoding/difference_encoding.rb +7 -1
- data/lib/hexapdf/font/encoding/glyph_list.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
- data/lib/hexapdf/font/invalid_glyph.rb +1 -1
- data/lib/hexapdf/font/true_type.rb +1 -1
- data/lib/hexapdf/font/true_type/builder.rb +1 -1
- data/lib/hexapdf/font/true_type/font.rb +1 -1
- data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
- data/lib/hexapdf/font/true_type/subsetter.rb +1 -1
- data/lib/hexapdf/font/true_type/table.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -1
- data/lib/hexapdf/font/true_type/table/directory.rb +1 -1
- data/lib/hexapdf/font/true_type/table/glyf.rb +1 -1
- data/lib/hexapdf/font/true_type/table/head.rb +2 -1
- data/lib/hexapdf/font/true_type/table/hhea.rb +1 -1
- data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -1
- data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
- data/lib/hexapdf/font/true_type/table/loca.rb +1 -1
- data/lib/hexapdf/font/true_type/table/maxp.rb +1 -1
- data/lib/hexapdf/font/true_type/table/name.rb +1 -1
- data/lib/hexapdf/font/true_type/table/os2.rb +3 -1
- data/lib/hexapdf/font/true_type/table/post.rb +1 -1
- data/lib/hexapdf/font/true_type_wrapper.rb +54 -51
- data/lib/hexapdf/font/type1.rb +1 -1
- data/lib/hexapdf/font/type1/afm_parser.rb +1 -1
- data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/font.rb +1 -1
- data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/pfb_parser.rb +1 -1
- data/lib/hexapdf/font/type1_wrapper.rb +68 -52
- data/lib/hexapdf/font_loader.rb +1 -1
- data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
- data/lib/hexapdf/font_loader/from_file.rb +1 -1
- data/lib/hexapdf/font_loader/standard14.rb +1 -1
- data/lib/hexapdf/image_loader.rb +1 -1
- data/lib/hexapdf/image_loader/jpeg.rb +1 -1
- data/lib/hexapdf/image_loader/pdf.rb +1 -1
- data/lib/hexapdf/image_loader/png.rb +4 -3
- data/lib/hexapdf/importer.rb +2 -4
- data/lib/hexapdf/layout.rb +1 -1
- data/lib/hexapdf/layout/box.rb +1 -1
- data/lib/hexapdf/layout/frame.rb +1 -1
- data/lib/hexapdf/layout/image_box.rb +1 -1
- data/lib/hexapdf/layout/inline_box.rb +1 -1
- data/lib/hexapdf/layout/line.rb +2 -2
- data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
- data/lib/hexapdf/layout/style.rb +24 -24
- data/lib/hexapdf/layout/text_box.rb +1 -1
- data/lib/hexapdf/layout/text_fragment.rb +1 -1
- data/lib/hexapdf/layout/text_layouter.rb +1 -1
- data/lib/hexapdf/layout/text_shaper.rb +4 -3
- data/lib/hexapdf/layout/width_from_polygon.rb +1 -1
- data/lib/hexapdf/name_tree_node.rb +1 -1
- data/lib/hexapdf/number_tree_node.rb +1 -1
- data/lib/hexapdf/object.rb +32 -27
- data/lib/hexapdf/parser.rb +69 -6
- data/lib/hexapdf/pdf_array.rb +10 -3
- data/lib/hexapdf/rectangle.rb +31 -1
- data/lib/hexapdf/reference.rb +1 -1
- data/lib/hexapdf/revision.rb +2 -1
- data/lib/hexapdf/revisions.rb +30 -22
- data/lib/hexapdf/serializer.rb +2 -2
- data/lib/hexapdf/stream.rb +1 -1
- data/lib/hexapdf/task.rb +1 -1
- data/lib/hexapdf/task/dereference.rb +1 -1
- data/lib/hexapdf/task/optimize.rb +7 -5
- data/lib/hexapdf/tokenizer.rb +5 -4
- data/lib/hexapdf/type.rb +1 -1
- data/lib/hexapdf/type/acro_form.rb +7 -1
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +405 -0
- data/lib/hexapdf/type/acro_form/button_field.rb +305 -0
- data/lib/hexapdf/type/acro_form/choice_field.rb +220 -0
- data/lib/hexapdf/type/acro_form/field.rb +250 -17
- data/lib/hexapdf/type/acro_form/form.rb +159 -7
- data/lib/hexapdf/type/acro_form/text_field.rb +187 -0
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +122 -0
- data/lib/hexapdf/type/action.rb +1 -1
- data/lib/hexapdf/type/actions.rb +1 -1
- data/lib/hexapdf/type/actions/go_to.rb +1 -1
- data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
- data/lib/hexapdf/type/actions/launch.rb +1 -1
- data/lib/hexapdf/type/actions/uri.rb +4 -3
- data/lib/hexapdf/type/annotation.rb +73 -3
- data/lib/hexapdf/type/annotations.rb +1 -1
- data/lib/hexapdf/type/annotations/link.rb +2 -2
- data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
- data/lib/hexapdf/type/annotations/text.rb +1 -1
- data/lib/hexapdf/type/annotations/widget.rb +238 -2
- data/lib/hexapdf/type/catalog.rb +23 -3
- data/lib/hexapdf/type/cid_font.rb +1 -1
- data/lib/hexapdf/type/embedded_file.rb +1 -1
- data/lib/hexapdf/type/file_specification.rb +2 -2
- data/lib/hexapdf/type/font.rb +18 -1
- data/lib/hexapdf/type/font_descriptor.rb +2 -2
- data/lib/hexapdf/type/font_simple.rb +4 -2
- data/lib/hexapdf/type/font_true_type.rb +7 -3
- data/lib/hexapdf/type/font_type0.rb +2 -2
- data/lib/hexapdf/type/font_type1.rb +16 -1
- data/lib/hexapdf/type/font_type3.rb +1 -1
- data/lib/hexapdf/type/form.rb +12 -2
- data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
- data/lib/hexapdf/type/icon_fit.rb +1 -1
- data/lib/hexapdf/type/image.rb +5 -3
- data/lib/hexapdf/type/info.rb +1 -1
- data/lib/hexapdf/type/names.rb +1 -1
- data/lib/hexapdf/type/object_stream.rb +1 -1
- data/lib/hexapdf/type/page.rb +36 -12
- data/lib/hexapdf/type/page_tree_node.rb +37 -16
- data/lib/hexapdf/type/resources.rb +17 -3
- data/lib/hexapdf/type/trailer.rb +4 -6
- data/lib/hexapdf/type/viewer_preferences.rb +1 -1
- data/lib/hexapdf/type/xref_stream.rb +1 -1
- data/lib/hexapdf/utils/bit_field.rb +38 -24
- data/lib/hexapdf/utils/bit_stream.rb +1 -1
- data/lib/hexapdf/utils/graphics_helpers.rb +1 -1
- data/lib/hexapdf/utils/lru_cache.rb +1 -1
- data/lib/hexapdf/utils/math_helpers.rb +1 -1
- data/lib/hexapdf/utils/object_hash.rb +1 -1
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
- data/lib/hexapdf/utils/sorted_tree_node.rb +19 -16
- data/lib/hexapdf/version.rb +2 -2
- data/lib/hexapdf/writer.rb +1 -1
- data/lib/hexapdf/xref_section.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +6 -1
- data/test/hexapdf/content/common.rb +2 -2
- data/test/hexapdf/content/graphic_object/test_arc.rb +4 -4
- data/test/hexapdf/content/test_canvas.rb +3 -3
- data/test/hexapdf/content/test_color_space.rb +71 -8
- data/test/hexapdf/content/test_operator.rb +22 -22
- data/test/hexapdf/content/test_parser.rb +14 -0
- data/test/hexapdf/document/test_fonts.rb +1 -1
- data/test/hexapdf/document/test_pages.rb +6 -6
- data/test/hexapdf/encryption/test_aes.rb +4 -4
- data/test/hexapdf/encryption/test_standard_security_handler.rb +11 -11
- data/test/hexapdf/filter/test_ascii85_decode.rb +1 -1
- data/test/hexapdf/filter/test_ascii_hex_decode.rb +1 -1
- data/test/hexapdf/font/encoding/test_base.rb +10 -0
- data/test/hexapdf/font/encoding/test_difference_encoding.rb +8 -0
- data/test/hexapdf/font/test_true_type_wrapper.rb +10 -7
- data/test/hexapdf/font/test_type1_wrapper.rb +33 -8
- data/test/hexapdf/layout/test_style.rb +1 -1
- data/test/hexapdf/layout/test_text_layouter.rb +3 -4
- data/test/hexapdf/test_configuration.rb +2 -2
- data/test/hexapdf/test_dictionary.rb +3 -1
- data/test/hexapdf/test_dictionary_fields.rb +2 -2
- data/test/hexapdf/test_document.rb +16 -4
- data/test/hexapdf/test_object.rb +44 -26
- data/test/hexapdf/test_parser.rb +125 -55
- data/test/hexapdf/test_pdf_array.rb +7 -0
- data/test/hexapdf/test_rectangle.rb +14 -0
- data/test/hexapdf/test_revision.rb +3 -0
- data/test/hexapdf/test_revisions.rb +35 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +521 -0
- data/test/hexapdf/type/acro_form/test_button_field.rb +281 -0
- data/test/hexapdf/type/acro_form/test_choice_field.rb +137 -0
- data/test/hexapdf/type/acro_form/test_field.rb +163 -6
- data/test/hexapdf/type/acro_form/test_form.rb +189 -22
- data/test/hexapdf/type/acro_form/test_text_field.rb +121 -0
- data/test/hexapdf/type/acro_form/test_variable_text_field.rb +77 -0
- data/test/hexapdf/type/annotations/test_text.rb +1 -1
- data/test/hexapdf/type/annotations/test_widget.rb +199 -0
- data/test/hexapdf/type/test_annotation.rb +45 -0
- data/test/hexapdf/type/test_catalog.rb +18 -0
- data/test/hexapdf/type/test_font.rb +5 -0
- data/test/hexapdf/type/test_font_simple.rb +2 -1
- data/test/hexapdf/type/test_font_true_type.rb +6 -0
- data/test/hexapdf/type/test_font_type1.rb +8 -0
- data/test/hexapdf/type/test_form.rb +19 -1
- data/test/hexapdf/type/test_image.rb +7 -0
- data/test/hexapdf/type/test_page.rb +45 -7
- data/test/hexapdf/type/test_page_tree_node.rb +62 -12
- data/test/hexapdf/type/test_resources.rb +20 -0
- data/test/hexapdf/type/test_trailer.rb +4 -0
- data/test/hexapdf/utils/test_bit_field.rb +15 -1
- data/test/hexapdf/utils/test_sorted_tree_node.rb +10 -9
- data/test/test_helper.rb +1 -1
- metadata +34 -21
- data/lib/hexapdf/filter/dct_decode.rb +0 -60
data/lib/hexapdf/revisions.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2020 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -67,30 +67,38 @@ module HexaPDF
|
|
|
67
67
|
object_loader = lambda {|xref_entry| parser.load_object(xref_entry) }
|
|
68
68
|
|
|
69
69
|
revisions = []
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
70
|
+
begin
|
|
71
|
+
xref_section, trailer = parser.load_revision(parser.startxref_offset)
|
|
72
|
+
revisions << Revision.new(document.wrap(trailer, type: :XXTrailer),
|
|
73
|
+
xref_section: xref_section, loader: object_loader)
|
|
74
|
+
seen_xref_offsets = {parser.startxref_offset => true}
|
|
75
|
+
|
|
76
|
+
while (prev = revisions[0].trailer.value[:Prev]) &&
|
|
77
|
+
!seen_xref_offsets.key?(prev)
|
|
78
|
+
# PDF1.7 s7.5.5 states that :Prev needs to be indirect, Adobe's reference 3.4.4 says it
|
|
79
|
+
# should be direct. Adobe's POV is followed here. Same with :XRefStm.
|
|
80
|
+
xref_section, trailer = parser.load_revision(prev)
|
|
81
|
+
seen_xref_offsets[prev] = true
|
|
82
|
+
|
|
83
|
+
stm = revisions[0].trailer.value[:XRefStm]
|
|
84
|
+
if stm && !seen_xref_offsets.key?(stm)
|
|
85
|
+
stm_xref_section, = parser.load_revision(stm)
|
|
86
|
+
xref_section.merge!(stm_xref_section)
|
|
87
|
+
seen_xref_offsets[stm] = true
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
revisions.unshift(Revision.new(document.wrap(trailer, type: :XXTrailer),
|
|
91
|
+
xref_section: xref_section, loader: object_loader))
|
|
87
92
|
end
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
rescue HexaPDF::MalformedPDFError
|
|
94
|
+
reconstructed_revision = parser.reconstructed_revision
|
|
95
|
+
unless revisions.empty?
|
|
96
|
+
reconstructed_revision.trailer.data.value = revisions.last.trailer.data.value
|
|
97
|
+
end
|
|
98
|
+
revisions << reconstructed_revision
|
|
91
99
|
end
|
|
92
100
|
|
|
93
|
-
document.version = parser.file_header_version
|
|
101
|
+
document.version = parser.file_header_version rescue '1.0'
|
|
94
102
|
new(document, initial_revisions: revisions, parser: parser)
|
|
95
103
|
end
|
|
96
104
|
|
data/lib/hexapdf/serializer.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2020 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -243,7 +243,7 @@ module HexaPDF
|
|
|
243
243
|
else
|
|
244
244
|
obj.dup
|
|
245
245
|
end
|
|
246
|
-
obj.gsub!(/[
|
|
246
|
+
obj.gsub!(/[()\\\r]/n, STRING_ESCAPE_MAP)
|
|
247
247
|
"(#{obj})"
|
|
248
248
|
end
|
|
249
249
|
|
data/lib/hexapdf/stream.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2020 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
data/lib/hexapdf/task.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2020 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2020 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2020 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -129,9 +129,10 @@ module HexaPDF
|
|
|
129
129
|
xref_stream = false
|
|
130
130
|
objects_to_delete = []
|
|
131
131
|
rev.each do |obj|
|
|
132
|
-
|
|
132
|
+
case obj.type
|
|
133
|
+
when :ObjStm
|
|
133
134
|
objects_to_delete << obj
|
|
134
|
-
|
|
135
|
+
when :XRef
|
|
135
136
|
xref_stream = true
|
|
136
137
|
objects_to_delete << obj if xref_streams == :delete
|
|
137
138
|
else
|
|
@@ -150,9 +151,10 @@ module HexaPDF
|
|
|
150
151
|
objstms = [doc.wrap({Type: :ObjStm})]
|
|
151
152
|
old_objstms = []
|
|
152
153
|
rev.each do |obj|
|
|
153
|
-
|
|
154
|
+
case obj.type
|
|
155
|
+
when :XRef
|
|
154
156
|
xref_stream = true
|
|
155
|
-
|
|
157
|
+
when :ObjStm
|
|
156
158
|
old_objstms << obj
|
|
157
159
|
end
|
|
158
160
|
delete_fields_with_defaults(obj)
|
data/lib/hexapdf/tokenizer.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2020 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -249,17 +249,18 @@ module HexaPDF
|
|
|
249
249
|
#
|
|
250
250
|
# See: PDF1.7 s7.3.3
|
|
251
251
|
def parse_number
|
|
252
|
-
|
|
252
|
+
val = scan_until(WHITESPACE_OR_DELIMITER_RE) || @ss.scan(/.*/)
|
|
253
|
+
if val.match?(/\A[+-]?\d++(?!\.)\z/)
|
|
253
254
|
tmp = val.to_i
|
|
254
255
|
# Handle object references, see PDF1.7 s7.3.10
|
|
255
256
|
prepare_string_scanner(10)
|
|
256
257
|
tmp = Reference.new(tmp, @ss[1].to_i) if @ss.scan(REFERENCE_RE)
|
|
257
258
|
tmp
|
|
258
|
-
elsif
|
|
259
|
+
elsif val.match?(/\A[+-]?(?:\d+\.\d*|\.\d+)\z/)
|
|
259
260
|
val << '0' if val.getbyte(-1) == 46 # dot '.'
|
|
260
261
|
Float(val)
|
|
261
262
|
else
|
|
262
|
-
|
|
263
|
+
TOKEN_CACHE[val] # val is keyword
|
|
263
264
|
end
|
|
264
265
|
end
|
|
265
266
|
|
data/lib/hexapdf/type.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2020 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2020 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -44,6 +44,12 @@ module HexaPDF
|
|
|
44
44
|
|
|
45
45
|
autoload(:Form, 'hexapdf/type/acro_form/form')
|
|
46
46
|
autoload(:Field, 'hexapdf/type/acro_form/field')
|
|
47
|
+
autoload(:VariableTextField, 'hexapdf/type/acro_form/variable_text_field')
|
|
48
|
+
autoload(:TextField, 'hexapdf/type/acro_form/text_field')
|
|
49
|
+
autoload(:ButtonField, 'hexapdf/type/acro_form/button_field')
|
|
50
|
+
autoload(:ChoiceField, 'hexapdf/type/acro_form/choice_field')
|
|
51
|
+
|
|
52
|
+
autoload(:AppearanceGenerator, 'hexapdf/type/acro_form/appearance_generator')
|
|
47
53
|
|
|
48
54
|
end
|
|
49
55
|
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
# -*- encoding: utf-8; frozen_string_literal: true -*-
|
|
2
|
+
#
|
|
3
|
+
#--
|
|
4
|
+
# This file is part of HexaPDF.
|
|
5
|
+
#
|
|
6
|
+
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
+
# Copyright (C) 2014-2020 Thomas Leitner
|
|
8
|
+
#
|
|
9
|
+
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
|
+
# under the terms of the GNU Affero General Public License version 3 as
|
|
11
|
+
# published by the Free Software Foundation with the addition of the
|
|
12
|
+
# following permission added to Section 15 as permitted in Section 7(a):
|
|
13
|
+
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
|
|
14
|
+
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
|
|
15
|
+
# INFRINGEMENT OF THIRD PARTY RIGHTS.
|
|
16
|
+
#
|
|
17
|
+
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
|
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
20
|
+
# License for more details.
|
|
21
|
+
#
|
|
22
|
+
# You should have received a copy of the GNU Affero General Public License
|
|
23
|
+
# along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
|
|
24
|
+
#
|
|
25
|
+
# The interactive user interfaces in modified source and object code
|
|
26
|
+
# versions of HexaPDF must display Appropriate Legal Notices, as required
|
|
27
|
+
# under Section 5 of the GNU Affero General Public License version 3.
|
|
28
|
+
#
|
|
29
|
+
# In accordance with Section 7(b) of the GNU Affero General Public
|
|
30
|
+
# License, a covered work must retain the producer line in every PDF that
|
|
31
|
+
# is created or manipulated using HexaPDF.
|
|
32
|
+
#
|
|
33
|
+
# If the GNU Affero General Public License doesn't fit your need,
|
|
34
|
+
# commercial licenses are available at <https://gettalong.at/hexapdf/>.
|
|
35
|
+
#++
|
|
36
|
+
|
|
37
|
+
require 'hexapdf/error'
|
|
38
|
+
require 'hexapdf/layout/style'
|
|
39
|
+
require 'hexapdf/layout/text_fragment'
|
|
40
|
+
|
|
41
|
+
module HexaPDF
|
|
42
|
+
module Type
|
|
43
|
+
module AcroForm
|
|
44
|
+
|
|
45
|
+
# The AppearanceGenerator class provides methods for generating and updating the appearance
|
|
46
|
+
# streams of form fields.
|
|
47
|
+
#
|
|
48
|
+
# The only method needed is #create_appearances since this method determines to what field the
|
|
49
|
+
# widget belongs and therefore which appearance should be generated.
|
|
50
|
+
#
|
|
51
|
+
# The visual appearance of a field is constructed using information from the field itself as
|
|
52
|
+
# well as information from the widget. See the documentation for the individual methods which
|
|
53
|
+
# information is used in which way.
|
|
54
|
+
#
|
|
55
|
+
# By default, any existing appearances are overwritten and the +:print+ flag is set on the
|
|
56
|
+
# widget so that the field appearance will appear on print-outs.
|
|
57
|
+
#
|
|
58
|
+
# The visual appearances are chosen to be similar to those used by Adobe Acrobat and others.
|
|
59
|
+
# By subclassing and overriding the necessary methods it is possible to define custom
|
|
60
|
+
# appearances.
|
|
61
|
+
#
|
|
62
|
+
# See: PDF1.7 s12.5.5, s12.7
|
|
63
|
+
class AppearanceGenerator
|
|
64
|
+
|
|
65
|
+
# Creates a new instance for the given +widget+.
|
|
66
|
+
def initialize(widget)
|
|
67
|
+
@widget = widget
|
|
68
|
+
@field = widget.form_field
|
|
69
|
+
@document = widget.document
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Creates the appropriate appearances for the widget.
|
|
73
|
+
def create_appearances
|
|
74
|
+
case @field.field_type
|
|
75
|
+
when :Btn
|
|
76
|
+
if @field.check_box?
|
|
77
|
+
create_check_box_appearances
|
|
78
|
+
elsif @field.radio_button?
|
|
79
|
+
create_radio_button_appearances
|
|
80
|
+
else
|
|
81
|
+
raise HexaPDF::Error, "Unsupported button field type"
|
|
82
|
+
end
|
|
83
|
+
when :Tx
|
|
84
|
+
create_text_appearances
|
|
85
|
+
when :Ch
|
|
86
|
+
if @field.combo_box?
|
|
87
|
+
create_text_appearances
|
|
88
|
+
else
|
|
89
|
+
raise HexaPDF::Error, "List box not supported yet"
|
|
90
|
+
end
|
|
91
|
+
else
|
|
92
|
+
raise HexaPDF::Error, "Unsupported field type #{@field.field_type}"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Creates the appropriate appearances for check boxes.
|
|
97
|
+
#
|
|
98
|
+
# For unchecked boxes an empty rectangle is drawn. When checked, a symbol from the
|
|
99
|
+
# ZapfDingbats font is placed inside the rectangle. How this is exactly done depends on the
|
|
100
|
+
# following values:
|
|
101
|
+
#
|
|
102
|
+
# * The widget's rectangle /Rect must be defined. If the height and/or width of the
|
|
103
|
+
# rectangle are zero, they are based on the configuration option
|
|
104
|
+
# +acro_form.default_font_size+ and widget's border width. In such a case the rectangle is
|
|
105
|
+
# appropriately updated.
|
|
106
|
+
#
|
|
107
|
+
# * The line width, style and color of the rectangle are taken from the widget's border
|
|
108
|
+
# style. See HexaPDF::Type::Annotations::Widget#border_style.
|
|
109
|
+
#
|
|
110
|
+
# * The background color is determined by the widget's background color. See
|
|
111
|
+
# HexaPDF::Type::Annotations::Widget#background_color.
|
|
112
|
+
#
|
|
113
|
+
# * The symbol (marker) as well as its size and color are determined by the marker style of
|
|
114
|
+
# the widget. See HexaPDF::Type::Annotations::Widget#marker_style for details.
|
|
115
|
+
#
|
|
116
|
+
# Examples:
|
|
117
|
+
#
|
|
118
|
+
# widget.border_style(color: 0)
|
|
119
|
+
# widget.background_color(1)
|
|
120
|
+
# widget.marker_style(style: :check, size: 0, color: 0)
|
|
121
|
+
# # => default appearance
|
|
122
|
+
#
|
|
123
|
+
# widget.border_style(color: :transparent, width: 2)
|
|
124
|
+
# widget.background_color(0.7)
|
|
125
|
+
# widget.marker_style(style: :cross)
|
|
126
|
+
# # => no visible rectangle, gray background, cross mark when checked
|
|
127
|
+
def create_check_box_appearances
|
|
128
|
+
unless @widget.appearance&.normal_appearance&.value&.size == 2
|
|
129
|
+
raise HexaPDF::Error, "Widget of check box doesn't define name for on state"
|
|
130
|
+
end
|
|
131
|
+
border_style = @widget.border_style
|
|
132
|
+
border_width = border_style.width
|
|
133
|
+
|
|
134
|
+
rect = update_widget(@field[:V], border_width)
|
|
135
|
+
|
|
136
|
+
off_form = @widget.appearance.normal_appearance[:Off] =
|
|
137
|
+
@document.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, rect.width, rect.height]})
|
|
138
|
+
apply_background_and_border(border_style, off_form.canvas)
|
|
139
|
+
|
|
140
|
+
on_form = @widget.appearance.normal_appearance[@field.check_box_on_name] =
|
|
141
|
+
@document.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, rect.width, rect.height]})
|
|
142
|
+
canvas = on_form.canvas
|
|
143
|
+
apply_background_and_border(border_style, canvas)
|
|
144
|
+
canvas.save_graphics_state do
|
|
145
|
+
draw_marker(canvas, rect, border_width, @widget.marker_style)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Creates the appropriate appearances for radio buttons.
|
|
150
|
+
#
|
|
151
|
+
# For unselected radio buttons an empty circle (if the marker is :circle) or rectangle is
|
|
152
|
+
# drawn inside the widget annotation's rectangle. When selected, a symbol from the
|
|
153
|
+
# ZapfDingbats font is placed inside. How this is exactly done depends on the following
|
|
154
|
+
# values:
|
|
155
|
+
#
|
|
156
|
+
# * The widget's rectangle /Rect must be defined. If the height and/or width of the
|
|
157
|
+
# rectangle are zero, they are based on the configuration option
|
|
158
|
+
# +acro_form.default_font_size+ and the widget's border width. In such a case the
|
|
159
|
+
# rectangle is appropriately updated.
|
|
160
|
+
#
|
|
161
|
+
# * The line width, style and color of the circle/rectangle are taken from the widget's
|
|
162
|
+
# border style. See HexaPDF::Type::Annotations::Widget#border_style.
|
|
163
|
+
#
|
|
164
|
+
# * The background color is determined by the widget's background color. See
|
|
165
|
+
# HexaPDF::Type::Annotations::Widget#background_color.
|
|
166
|
+
#
|
|
167
|
+
# * The symbol (marker) as well as its size and color are determined by the marker style of
|
|
168
|
+
# the widget. See HexaPDF::Type::Annotations::Widget#marker_style for details.
|
|
169
|
+
#
|
|
170
|
+
# Examples:
|
|
171
|
+
#
|
|
172
|
+
# widget.border_style(color: 0)
|
|
173
|
+
# widget.background_color(1)
|
|
174
|
+
# widget.marker_style(style: :circle, size: 0, color: 0)
|
|
175
|
+
# # => default appearance
|
|
176
|
+
def create_radio_button_appearances
|
|
177
|
+
unless @widget.appearance&.normal_appearance&.value&.size == 2
|
|
178
|
+
raise HexaPDF::Error, "Widget of radio button doesn't define unique name for on state"
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
on_name = (@widget.appearance.normal_appearance.value.keys - [:Off]).first
|
|
182
|
+
border_style = @widget.border_style
|
|
183
|
+
marker_style = @widget.marker_style
|
|
184
|
+
|
|
185
|
+
rect = update_widget(@field[:V] == on_name ? on_name : :Off, border_style.width)
|
|
186
|
+
|
|
187
|
+
off_form = @widget.appearance.normal_appearance[:Off] =
|
|
188
|
+
@document.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, rect.width, rect.height]})
|
|
189
|
+
apply_background_and_border(border_style, off_form.canvas,
|
|
190
|
+
circular: marker_style.style == :circle)
|
|
191
|
+
|
|
192
|
+
on_form = @widget.appearance.normal_appearance[on_name] =
|
|
193
|
+
@document.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, rect.width, rect.height]})
|
|
194
|
+
canvas = on_form.canvas
|
|
195
|
+
apply_background_and_border(border_style, canvas,
|
|
196
|
+
circular: marker_style.style == :circle)
|
|
197
|
+
canvas.save_graphics_state do
|
|
198
|
+
draw_marker(canvas, rect, border_style.width, @widget.marker_style)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Creates the appropriate appearances for text fields.
|
|
203
|
+
#
|
|
204
|
+
# The following describes how the appearance is built:
|
|
205
|
+
#
|
|
206
|
+
# * The font, font size and font color are taken from the associated field's default
|
|
207
|
+
# appearance string. See VariableTextField.
|
|
208
|
+
#
|
|
209
|
+
# If the font is not usable by HexaPDF (which may be due to a variety of reasons, e.g. no
|
|
210
|
+
# associated information in the form's default resources), the font specified by the
|
|
211
|
+
# configuration option +acro_form.fallback_font+ will be used.
|
|
212
|
+
#
|
|
213
|
+
# * The widget's rectangle /Rect must be defined. If the height is zero, it is auto-sized
|
|
214
|
+
# based on the font size. If additionally the font size is zero, a font size of
|
|
215
|
+
# +acro_form.default_font_size+ is used. If the width is zero, the
|
|
216
|
+
# +acro_form.text_field.default_width+ value is used. In such cases the rectangle is
|
|
217
|
+
# appropriately updated.
|
|
218
|
+
#
|
|
219
|
+
# * The line width, style and color of the rectangle are taken from the widget's border
|
|
220
|
+
# style. See HexaPDF::Type::Annotations::Widget#border_style.
|
|
221
|
+
#
|
|
222
|
+
# * The background color is determined by the widget's background color. See
|
|
223
|
+
# HexaPDF::Type::Annotations::Widget#background_color.
|
|
224
|
+
#
|
|
225
|
+
# Note: Multiline, comb and rich text fields are currently not supported!
|
|
226
|
+
def create_text_appearances
|
|
227
|
+
font_name, font_size = @field.parse_default_appearance_string
|
|
228
|
+
default_resources = @document.acro_form.default_resources
|
|
229
|
+
font = default_resources.font(font_name).font_wrapper rescue nil
|
|
230
|
+
unless font
|
|
231
|
+
fallback_font_name, fallback_font_options = @document.config['acro_form.fallback_font']
|
|
232
|
+
if fallback_font_name
|
|
233
|
+
font = @document.fonts.add(fallback_font_name, **(fallback_font_options || {}))
|
|
234
|
+
else
|
|
235
|
+
raise(HexaPDF::Error, "Font #{font_name} of the AcroForm's default resources not usable")
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
style = HexaPDF::Layout::Style.new(font: font)
|
|
239
|
+
border_style = @widget.border_style
|
|
240
|
+
padding = [1, border_style.width].max
|
|
241
|
+
|
|
242
|
+
@widget[:AS] = :N
|
|
243
|
+
@widget.flag(:print)
|
|
244
|
+
rect = @widget[:Rect]
|
|
245
|
+
rect.width = @document.config['acro_form.text_field.default_width'] if rect.width == 0
|
|
246
|
+
if rect.height == 0
|
|
247
|
+
style.font_size = \
|
|
248
|
+
(font_size == 0 ? @document.config['acro_form.default_font_size'] : font_size)
|
|
249
|
+
rect.height = style.scaled_y_max - style.scaled_y_min + 2 * padding
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
form = (@widget[:AP] ||= {})[:N] = @document.add({Type: :XObject, Subtype: :Form,
|
|
253
|
+
BBox: [0, 0, rect.width, rect.height]})
|
|
254
|
+
form[:Resources] = HexaPDF::Object.deep_copy(default_resources)
|
|
255
|
+
|
|
256
|
+
canvas = form.canvas
|
|
257
|
+
apply_background_and_border(border_style, canvas)
|
|
258
|
+
style.font_size = calculate_font_size(font, font_size, rect, border_style)
|
|
259
|
+
|
|
260
|
+
canvas.marked_content_sequence(:Tx) do
|
|
261
|
+
if (value = @field.field_value)
|
|
262
|
+
canvas.save_graphics_state do
|
|
263
|
+
canvas.rectangle(padding, padding, rect.width - 2 * padding,
|
|
264
|
+
rect.height - 2 * padding).clip_path.end_path
|
|
265
|
+
fragment = HexaPDF::Layout::TextFragment.create(value, style)
|
|
266
|
+
# Adobe seems to be left/right-aligning based on twice the border width and
|
|
267
|
+
# vertically centering based on the cap height, if enough space is available
|
|
268
|
+
x = case @field.text_alignment
|
|
269
|
+
when :left then 2 * padding
|
|
270
|
+
when :right then [rect.width - 2 * padding - fragment.width, 2 * padding].max
|
|
271
|
+
when :center then [(rect.width - fragment.width) / 2.0, 2 * padding].max
|
|
272
|
+
end
|
|
273
|
+
cap_height = font.wrapped_font.cap_height * font.scaling_factor / 1000.0 *
|
|
274
|
+
style.font_size
|
|
275
|
+
y = padding + (rect.height - 2 * padding - cap_height) / 2.0
|
|
276
|
+
y = padding - style.scaled_font_descender if y < 0
|
|
277
|
+
fragment.draw(canvas, x, y)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
alias create_combo_box_appearances create_text_appearances
|
|
284
|
+
|
|
285
|
+
private
|
|
286
|
+
|
|
287
|
+
# Updates the widget and returns its (possibly modified) rectangle.
|
|
288
|
+
#
|
|
289
|
+
# The following changes are made:
|
|
290
|
+
#
|
|
291
|
+
# * Sets the appearance state to +appearance_state+.
|
|
292
|
+
# * Sets the :print flag.
|
|
293
|
+
# * Adjusts the rectangle based on the default font size and the given border width if its
|
|
294
|
+
# width and/or height are zero.
|
|
295
|
+
def update_widget(appearance_state, border_width)
|
|
296
|
+
@widget[:AS] = appearance_state
|
|
297
|
+
@widget.flag(:print)
|
|
298
|
+
|
|
299
|
+
default_font_size = @document.config['acro_form.default_font_size']
|
|
300
|
+
rect = @widget[:Rect]
|
|
301
|
+
rect.width = default_font_size + 2 * border_width if rect.width == 0
|
|
302
|
+
rect.height = default_font_size + 2 * border_width if rect.height == 0
|
|
303
|
+
rect
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Applies the background and border style of the widget annotation to the appearances.
|
|
307
|
+
#
|
|
308
|
+
# If +circular+ is +true+, then the border is drawn as inscribed circle instead of as
|
|
309
|
+
# rectangle.
|
|
310
|
+
def apply_background_and_border(border_style, canvas, circular: false)
|
|
311
|
+
rect = @widget[:Rect]
|
|
312
|
+
background_color = @widget.background_color
|
|
313
|
+
|
|
314
|
+
if (border_style.width > 0 && border_style.color) || background_color
|
|
315
|
+
canvas.save_graphics_state
|
|
316
|
+
if background_color
|
|
317
|
+
canvas.fill_color(background_color)
|
|
318
|
+
if circular
|
|
319
|
+
canvas.circle(rect.width / 2.0, rect.height / 2.0,
|
|
320
|
+
[rect.width / 2.0, rect.height / 2.0].min)
|
|
321
|
+
else
|
|
322
|
+
canvas.rectangle(0, 0, rect.width, rect.height)
|
|
323
|
+
end
|
|
324
|
+
canvas.fill
|
|
325
|
+
end
|
|
326
|
+
if border_style.color
|
|
327
|
+
offset = [0.5, border_style.width / 2.0].max
|
|
328
|
+
width, height = rect.width - 2 * offset, rect.height - 2 * offset
|
|
329
|
+
canvas.stroke_color(border_style.color).line_width(border_style.width)
|
|
330
|
+
if border_style.style == :underlined # TODO: :beveleded, :inset
|
|
331
|
+
if circular
|
|
332
|
+
canvas.arc(rect.width / 2.0, rect.height / 2.0,
|
|
333
|
+
a: [width / 2.0, height / 2.0].min,
|
|
334
|
+
start_angle: 180, end_angle: 0)
|
|
335
|
+
else
|
|
336
|
+
canvas.line(offset, offset, offset + width, offset)
|
|
337
|
+
end
|
|
338
|
+
else
|
|
339
|
+
canvas.line_dash_pattern(border_style.style) if border_style.style.kind_of?(Array)
|
|
340
|
+
if circular
|
|
341
|
+
canvas.circle(rect.width / 2.0, rect.height / 2.0, [width / 2.0, height / 2.0].min)
|
|
342
|
+
else
|
|
343
|
+
canvas.rectangle(offset, offset, width, height)
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
canvas.stroke
|
|
347
|
+
end
|
|
348
|
+
canvas.restore_graphics_state
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# Draws the marker defined by the marker style inside the widget's rectangle.
|
|
353
|
+
#
|
|
354
|
+
# This method can only used for check boxes and radio buttons!
|
|
355
|
+
def draw_marker(canvas, rect, border_width, marker_style)
|
|
356
|
+
if @field.radio_button? && marker_style.style == :circle
|
|
357
|
+
# Acrobat handles this specially
|
|
358
|
+
canvas.
|
|
359
|
+
fill_color(marker_style.color).
|
|
360
|
+
circle(rect.width / 2.0, rect.height / 2.0,
|
|
361
|
+
([rect.width / 2.0, rect.height / 2.0].min - border_width) / 2).
|
|
362
|
+
fill
|
|
363
|
+
elsif marker_style.style == :cross # Acrobat just places a cross inside
|
|
364
|
+
canvas.
|
|
365
|
+
stroke_color(marker_style.color).
|
|
366
|
+
line(border_width, border_width, rect.width - border_width,
|
|
367
|
+
rect.height - border_width).
|
|
368
|
+
line(border_width, rect.height - border_width, rect.width - border_width,
|
|
369
|
+
border_width).
|
|
370
|
+
stroke
|
|
371
|
+
else
|
|
372
|
+
font = @document.fonts.add('ZapfDingbats')
|
|
373
|
+
mark = font.decode_utf8(@widget[:MK]&.[](:CA) || '4').first
|
|
374
|
+
square_width = [rect.width, rect.height].min - 2 * border_width
|
|
375
|
+
font_size = (marker_style.size == 0 ? square_width : marker_style.size)
|
|
376
|
+
mark_width = mark.width * font.scaling_factor * font_size / 1000.0
|
|
377
|
+
mark_height = (mark.y_max - mark.y_min) * font.scaling_factor * font_size / 1000.0
|
|
378
|
+
x_offset = (rect.width - square_width) / 2.0 + (square_width - mark_width) / 2.0
|
|
379
|
+
y_offset = (rect.height - square_width) / 2.0 + (square_width - mark_height) / 2.0 -
|
|
380
|
+
(mark.y_min * font.scaling_factor * font_size / 1000.0)
|
|
381
|
+
|
|
382
|
+
canvas.font(font, size: font_size)
|
|
383
|
+
canvas.fill_color(marker_style.color)
|
|
384
|
+
canvas.move_text_cursor(offset: [x_offset, y_offset]).show_glyphs_only([mark])
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# Calculates the font size for text fields based on the font and font size of the default
|
|
389
|
+
# appearance string, the annotation rectangle and the border style.
|
|
390
|
+
def calculate_font_size(font, font_size, rect, border_style)
|
|
391
|
+
if font_size == 0
|
|
392
|
+
unit_font_size = (font.wrapped_font.bounding_box[3] - font.wrapped_font.bounding_box[1]) *
|
|
393
|
+
font.scaling_factor / 1000.0
|
|
394
|
+
# The constant factor was found empirically by checking what Adobe Reader etc. do
|
|
395
|
+
(rect.height - 2 * border_style.width) / unit_font_size * 0.83
|
|
396
|
+
else
|
|
397
|
+
font_size
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
end
|