hexapdf 0.21.1 → 0.24.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +137 -0
- data/LICENSE +1 -1
- data/Rakefile +1 -1
- data/examples/016-frame_automatic_box_placement.rb +7 -2
- data/examples/017-frame_text_flow.rb +10 -18
- data/examples/020-column_box.rb +20 -37
- data/examples/021-list_box.rb +26 -0
- data/lib/hexapdf/cli/batch.rb +1 -1
- data/lib/hexapdf/cli/command.rb +1 -1
- data/lib/hexapdf/cli/files.rb +1 -1
- data/lib/hexapdf/cli/fonts.rb +1 -1
- data/lib/hexapdf/cli/form.rb +31 -4
- data/lib/hexapdf/cli/image2pdf.rb +1 -1
- data/lib/hexapdf/cli/images.rb +1 -1
- data/lib/hexapdf/cli/info.rb +2 -2
- data/lib/hexapdf/cli/inspect.rb +19 -6
- data/lib/hexapdf/cli/merge.rb +1 -1
- data/lib/hexapdf/cli/modify.rb +24 -4
- 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/cli.rb +1 -1
- data/lib/hexapdf/composer.rb +66 -125
- data/lib/hexapdf/configuration.rb +17 -1
- data/lib/hexapdf/content/canvas.rb +1 -1
- data/lib/hexapdf/content/color_space.rb +1 -1
- data/lib/hexapdf/content/graphic_object/arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object/geom2d.rb +2 -1
- data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object.rb +1 -1
- data/lib/hexapdf/content/graphics_state.rb +1 -1
- data/lib/hexapdf/content/operator.rb +1 -1
- data/lib/hexapdf/content/parser.rb +1 -1
- data/lib/hexapdf/content/processor.rb +1 -1
- data/lib/hexapdf/content/transformation_matrix.rb +1 -1
- data/lib/hexapdf/content.rb +1 -1
- data/lib/hexapdf/data_dir.rb +1 -1
- data/lib/hexapdf/dictionary.rb +1 -1
- data/lib/hexapdf/dictionary_fields.rb +2 -2
- data/lib/hexapdf/document/destinations.rb +396 -0
- data/lib/hexapdf/document/files.rb +1 -1
- data/lib/hexapdf/document/fonts.rb +1 -1
- data/lib/hexapdf/document/images.rb +1 -1
- data/lib/hexapdf/document/layout.rb +397 -0
- data/lib/hexapdf/document/pages.rb +17 -1
- data/lib/hexapdf/document/signatures.rb +5 -4
- data/lib/hexapdf/document.rb +46 -90
- 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 +30 -21
- 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 +1 -1
- data/lib/hexapdf/encryption/standard_security_handler.rb +1 -1
- data/lib/hexapdf/encryption.rb +1 -1
- data/lib/hexapdf/error.rb +1 -1
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
- data/lib/hexapdf/filter/crypt.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/pass_through.rb +1 -1
- data/lib/hexapdf/filter/predictor.rb +1 -1
- data/lib/hexapdf/filter/run_length_decode.rb +1 -1
- data/lib/hexapdf/filter.rb +1 -1
- data/lib/hexapdf/font/cmap/parser.rb +1 -1
- data/lib/hexapdf/font/cmap/writer.rb +1 -1
- data/lib/hexapdf/font/cmap.rb +1 -1
- data/lib/hexapdf/font/encoding/base.rb +1 -1
- data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/glyph_list.rb +2 -2
- 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/encoding.rb +1 -1
- data/lib/hexapdf/font/invalid_glyph.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/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 +1 -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 +1 -1
- data/lib/hexapdf/font/true_type/table/post.rb +1 -1
- data/lib/hexapdf/font/true_type/table.rb +1 -1
- data/lib/hexapdf/font/true_type.rb +1 -1
- data/lib/hexapdf/font/true_type_wrapper.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.rb +1 -1
- data/lib/hexapdf/font/type1_wrapper.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/font_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 +1 -1
- data/lib/hexapdf/image_loader.rb +1 -1
- data/lib/hexapdf/importer.rb +1 -1
- data/lib/hexapdf/layout/box.rb +121 -22
- data/lib/hexapdf/layout/box_fitter.rb +136 -0
- data/lib/hexapdf/layout/column_box.rb +168 -89
- data/lib/hexapdf/layout/frame.rb +155 -140
- data/lib/hexapdf/layout/image_box.rb +19 -4
- data/lib/hexapdf/layout/inline_box.rb +1 -1
- data/lib/hexapdf/layout/line.rb +1 -1
- data/lib/hexapdf/layout/list_box.rb +355 -0
- data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
- data/lib/hexapdf/layout/style.rb +285 -8
- data/lib/hexapdf/layout/text_box.rb +30 -11
- data/lib/hexapdf/layout/text_fragment.rb +3 -2
- data/lib/hexapdf/layout/text_layouter.rb +23 -3
- data/lib/hexapdf/layout/text_shaper.rb +1 -1
- data/lib/hexapdf/layout/width_from_polygon.rb +12 -7
- data/lib/hexapdf/layout.rb +4 -1
- data/lib/hexapdf/name_tree_node.rb +1 -1
- data/lib/hexapdf/number_tree_node.rb +1 -1
- data/lib/hexapdf/object.rb +1 -1
- data/lib/hexapdf/parser.rb +1 -8
- data/lib/hexapdf/pdf_array.rb +1 -1
- data/lib/hexapdf/rectangle.rb +1 -1
- data/lib/hexapdf/reference.rb +1 -1
- data/lib/hexapdf/revision.rb +9 -2
- data/lib/hexapdf/revisions.rb +152 -51
- data/lib/hexapdf/serializer.rb +1 -1
- data/lib/hexapdf/stream.rb +1 -1
- data/lib/hexapdf/task/dereference.rb +1 -1
- data/lib/hexapdf/task/optimize.rb +22 -12
- data/lib/hexapdf/task.rb +1 -1
- data/lib/hexapdf/tokenizer.rb +1 -1
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +1 -1
- data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/field.rb +1 -1
- data/lib/hexapdf/type/acro_form/form.rb +12 -6
- data/lib/hexapdf/type/acro_form/signature_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/text_field.rb +9 -1
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
- data/lib/hexapdf/type/acro_form.rb +1 -1
- data/lib/hexapdf/type/action.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 +1 -1
- data/lib/hexapdf/type/actions.rb +1 -1
- data/lib/hexapdf/type/annotation.rb +1 -1
- data/lib/hexapdf/type/annotations/link.rb +1 -1
- 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 +1 -1
- data/lib/hexapdf/type/annotations.rb +1 -1
- data/lib/hexapdf/type/catalog.rb +10 -2
- 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 +1 -1
- data/lib/hexapdf/type/font.rb +1 -1
- data/lib/hexapdf/type/font_descriptor.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +1 -1
- data/lib/hexapdf/type/font_true_type.rb +1 -1
- data/lib/hexapdf/type/font_type0.rb +1 -1
- data/lib/hexapdf/type/font_type1.rb +1 -1
- data/lib/hexapdf/type/font_type3.rb +1 -1
- data/lib/hexapdf/type/form.rb +1 -1
- 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 +48 -4
- data/lib/hexapdf/type/info.rb +1 -1
- data/lib/hexapdf/type/names.rb +14 -1
- data/lib/hexapdf/type/object_stream.rb +1 -1
- data/lib/hexapdf/type/page.rb +1 -1
- data/lib/hexapdf/type/page_tree_node.rb +19 -2
- data/lib/hexapdf/type/resources.rb +1 -1
- data/lib/hexapdf/type/signature/adbe_pkcs7_detached.rb +1 -1
- data/lib/hexapdf/type/signature/adbe_x509_rsa_sha1.rb +1 -1
- data/lib/hexapdf/type/signature/handler.rb +1 -1
- data/lib/hexapdf/type/signature/verification_result.rb +1 -1
- data/lib/hexapdf/type/signature.rb +1 -1
- data/lib/hexapdf/type/trailer.rb +2 -2
- data/lib/hexapdf/type/viewer_preferences.rb +1 -1
- data/lib/hexapdf/type/xref_stream.rb +3 -2
- data/lib/hexapdf/type.rb +1 -1
- data/lib/hexapdf/utils/bit_field.rb +1 -1
- 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 +4 -2
- data/lib/hexapdf/version.rb +2 -2
- data/lib/hexapdf/writer.rb +23 -8
- data/lib/hexapdf/xref_section.rb +1 -1
- data/lib/hexapdf.rb +1 -1
- data/test/hexapdf/content/graphic_object/test_geom2d.rb +1 -1
- data/test/hexapdf/document/test_destinations.rb +338 -0
- data/test/hexapdf/document/test_images.rb +1 -1
- data/test/hexapdf/document/test_layout.rb +264 -0
- data/test/hexapdf/document/test_pages.rb +9 -0
- data/test/hexapdf/document/test_signatures.rb +10 -3
- data/test/hexapdf/encryption/test_security_handler.rb +3 -3
- data/test/hexapdf/font/encoding/test_glyph_list.rb +4 -0
- data/test/hexapdf/layout/test_box.rb +53 -3
- data/test/hexapdf/layout/test_box_fitter.rb +62 -0
- data/test/hexapdf/layout/test_column_box.rb +159 -0
- data/test/hexapdf/layout/test_frame.rb +114 -39
- data/test/hexapdf/layout/test_image_box.rb +1 -1
- data/test/hexapdf/layout/test_list_box.rb +249 -0
- data/test/hexapdf/layout/test_text_box.rb +33 -2
- data/test/hexapdf/layout/test_text_fragment.rb +1 -1
- data/test/hexapdf/layout/test_text_layouter.rb +49 -17
- data/test/hexapdf/layout/test_width_from_polygon.rb +13 -0
- data/test/hexapdf/task/test_optimize.rb +17 -4
- data/test/hexapdf/test_composer.rb +35 -1
- data/test/hexapdf/test_dictionary_fields.rb +10 -10
- data/test/hexapdf/test_document.rb +33 -136
- data/test/hexapdf/test_filter.rb +1 -1
- data/test/hexapdf/test_parser.rb +1 -3
- data/test/hexapdf/test_revision.rb +14 -0
- data/test/hexapdf/test_revisions.rb +137 -29
- data/test/hexapdf/test_serializer.rb +1 -5
- data/test/hexapdf/test_writer.rb +99 -15
- data/test/hexapdf/type/acro_form/test_form.rb +2 -1
- data/test/hexapdf/type/acro_form/test_text_field.rb +17 -0
- data/test/hexapdf/type/test_catalog.rb +8 -0
- data/test/hexapdf/type/test_image.rb +45 -9
- data/test/hexapdf/type/test_names.rb +20 -0
- data/test/hexapdf/type/test_page_tree_node.rb +21 -1
- data/test/hexapdf/type/test_trailer.rb +3 -3
- data/test/hexapdf/type/test_xref_stream.rb +2 -1
- data/test/hexapdf/utils/test_sorted_tree_node.rb +11 -1
- data/test/test_helper.rb +5 -1
- metadata +29 -3
@@ -0,0 +1,355 @@
|
|
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-2022 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/layout/box'
|
38
|
+
require 'hexapdf/layout/box_fitter'
|
39
|
+
require 'hexapdf/layout/text_box'
|
40
|
+
require 'hexapdf/layout/text_fragment'
|
41
|
+
|
42
|
+
module HexaPDF
|
43
|
+
module Layout
|
44
|
+
|
45
|
+
# A ListBox arranges its children as unordered or ordered list items.
|
46
|
+
#
|
47
|
+
# The indentation of the contents from the left (#content_indentation) as well as the type of
|
48
|
+
# item (#item_type) can be specified. Additionally, it is possible to define the start number
|
49
|
+
# for ordered lists (#start_number) and the amount of spacing between items (#item_spacing).
|
50
|
+
#
|
51
|
+
# If the list box has padding and/or borders specified, they are handled like with any other
|
52
|
+
# box. This means they are around all items and their contents and are not used separately for
|
53
|
+
# each item.
|
54
|
+
#
|
55
|
+
# The following style properties are used (additionally to those used by the parent class):
|
56
|
+
#
|
57
|
+
# Style#position::
|
58
|
+
# If this is set to :flow, the frames created for the list items will take the shape of the
|
59
|
+
# frame into account. This also means that the +available_width+ and +available_height+
|
60
|
+
# arguments are ignored.
|
61
|
+
class ListBox < Box
|
62
|
+
|
63
|
+
# The child boxes of this ListBox. They need to be finalized before #fit is called.
|
64
|
+
attr_reader :children
|
65
|
+
|
66
|
+
# The type of list item marker to be rendered before the list item contents.
|
67
|
+
#
|
68
|
+
# The following values are supported (and :disc is the default):
|
69
|
+
#
|
70
|
+
# :disc::
|
71
|
+
#
|
72
|
+
# Draws a filled disc for the items of the unordered list.
|
73
|
+
#
|
74
|
+
# #>pdf-composer100
|
75
|
+
# composer.box(:list, item_type: :disc) do |list|
|
76
|
+
# list.lorem_ipsum_box(sentences: 1)
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# :circle::
|
80
|
+
#
|
81
|
+
# Draws an unfilled circle for the items of the unordered list.
|
82
|
+
#
|
83
|
+
# #>pdf-composer100
|
84
|
+
# composer.box(:list, item_type: :circle) do |list|
|
85
|
+
# list.lorem_ipsum_box(sentences: 1)
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# :square::
|
89
|
+
#
|
90
|
+
# Draws a filled square for the items of the unordered list.
|
91
|
+
#
|
92
|
+
# #>pdf-composer100
|
93
|
+
# composer.box(:list, item_type: :square) do |list|
|
94
|
+
# list.lorem_ipsum_box(sentences: 1)
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# :decimal::
|
98
|
+
#
|
99
|
+
# Draws the numbers in decimal form, starting from #start_number) for the items of
|
100
|
+
# the ordered list.
|
101
|
+
#
|
102
|
+
# #>pdf-composer100
|
103
|
+
# composer.box(:list, item_type: :decimal) do |list|
|
104
|
+
# 5.times { list.lorem_ipsum_box(sentences: 1) }
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# custom marker::
|
108
|
+
#
|
109
|
+
# Additionally, it is possible to specify an object as value that responds to
|
110
|
+
# #call(document, box, index) where +document+ is the HexaPDF::Document, +box+ is the list
|
111
|
+
# box, and +index+ is the current item index, starting at 0. The return value needs to be a
|
112
|
+
# Box object which is then fit into the content indentation area and drawn.
|
113
|
+
#
|
114
|
+
# #>pdf-composer100
|
115
|
+
# image = lambda do |document, box, index|
|
116
|
+
# document.layout.image_box(machu_picchu, height: box.style.font_size)
|
117
|
+
# end
|
118
|
+
# composer.box(:list, item_type: image) do |list|
|
119
|
+
# 2.times { list.lorem_ipsum_box(sentences: 1) }
|
120
|
+
# end
|
121
|
+
attr_reader :item_type
|
122
|
+
|
123
|
+
# The start number when using an #item_type that represents an ordered list.
|
124
|
+
#
|
125
|
+
# The default value for this is 1.
|
126
|
+
#
|
127
|
+
# Example:
|
128
|
+
#
|
129
|
+
# #>pdf-composer100
|
130
|
+
# composer.box(:list, item_type: :decimal, start_number: 3) do |list|
|
131
|
+
# 2.times { list.lorem_ipsum_box(sentences: 1) }
|
132
|
+
# end
|
133
|
+
attr_reader :start_number
|
134
|
+
|
135
|
+
# The indentation of the list content in PDF points. The item marker will be inside this
|
136
|
+
# indentation.
|
137
|
+
#
|
138
|
+
# The default value is two times the font size.
|
139
|
+
#
|
140
|
+
# Example:
|
141
|
+
#
|
142
|
+
# #>pdf-composer100
|
143
|
+
# composer.box(:list) {|list| list.lorem_ipsum_box(sentences: 1) }
|
144
|
+
# composer.box(:list, content_indentation: 50) do |list|
|
145
|
+
# list.lorem_ipsum_box(sentences: 1)
|
146
|
+
# end
|
147
|
+
attr_reader :content_indentation
|
148
|
+
|
149
|
+
# The spacing between two consecutive list items.
|
150
|
+
#
|
151
|
+
# The default value is zero.
|
152
|
+
#
|
153
|
+
# Example:
|
154
|
+
#
|
155
|
+
# #>pdf-composer
|
156
|
+
# composer.box(:list, item_spacing: 10) do |list|
|
157
|
+
# 3.times { list.lorem_ipsum_box(sentences: 1) }
|
158
|
+
# end
|
159
|
+
attr_reader :item_spacing
|
160
|
+
|
161
|
+
# Creates a new ListBox object for the given child boxes in +children+.
|
162
|
+
def initialize(children: [], item_type: :disc, content_indentation: nil, start_number: 1,
|
163
|
+
item_spacing: 0, **kwargs)
|
164
|
+
super(**kwargs)
|
165
|
+
@children = children
|
166
|
+
@item_type = item_type
|
167
|
+
@content_indentation = content_indentation || 2 * style.font_size
|
168
|
+
@start_number = start_number
|
169
|
+
@item_spacing = item_spacing
|
170
|
+
|
171
|
+
@results = nil
|
172
|
+
@results_item_marker_x = nil
|
173
|
+
end
|
174
|
+
|
175
|
+
# Returns +true+ as the 'position' style property value :flow is supported.
|
176
|
+
def supports_position_flow?
|
177
|
+
true
|
178
|
+
end
|
179
|
+
|
180
|
+
# Fits the list box into the available space.
|
181
|
+
def fit(available_width, available_height, frame)
|
182
|
+
@width = if @initial_width > 0
|
183
|
+
@initial_width
|
184
|
+
else
|
185
|
+
(style.position == :flow ? frame.width : available_width)
|
186
|
+
end
|
187
|
+
height = if @initial_height > 0
|
188
|
+
@initial_height - reserved_height
|
189
|
+
else
|
190
|
+
(style.position == :flow ? frame.y - frame.bottom : available_height) - reserved_height
|
191
|
+
end
|
192
|
+
|
193
|
+
width = @width - reserved_width
|
194
|
+
left = (style.position == :flow ? frame.left : frame.x) + reserved_width_left
|
195
|
+
top = frame.y - reserved_height_top
|
196
|
+
|
197
|
+
# The left side of the frame of an item is always indented, regardless of style.position
|
198
|
+
item_frame_left = left + @content_indentation
|
199
|
+
item_frame_width = width - @content_indentation
|
200
|
+
|
201
|
+
# We can remove the content indentation for a rectangle by just modifying left and width
|
202
|
+
unless style.position == :flow
|
203
|
+
left = item_frame_left
|
204
|
+
width = item_frame_width
|
205
|
+
end
|
206
|
+
|
207
|
+
@results = []
|
208
|
+
@results_item_marker_x = []
|
209
|
+
|
210
|
+
@children.each_with_index do |child, index|
|
211
|
+
shape = Geom2D::Polygon([left, top - height],
|
212
|
+
[left + width, top - height],
|
213
|
+
[left + width, top],
|
214
|
+
[left, top])
|
215
|
+
if style.position == :flow
|
216
|
+
shape = Geom2D::Algorithms::PolygonOperation.run(frame.shape, shape, :intersection)
|
217
|
+
remove_indent_from_frame_shape(shape) unless shape.polygons.empty?
|
218
|
+
end
|
219
|
+
|
220
|
+
#p [:list, left, width, shape]
|
221
|
+
|
222
|
+
item_frame = Frame.new(item_frame_left, top - height, item_frame_width, height, shape: shape)
|
223
|
+
|
224
|
+
#p [index, item_frame.x, @results_item_marker_x]
|
225
|
+
@results_item_marker_x << item_frame.x - content_indentation
|
226
|
+
|
227
|
+
box_fitter = BoxFitter.new([item_frame])
|
228
|
+
Array(child).each {|box| box_fitter.fit(box) }
|
229
|
+
@results << box_fitter
|
230
|
+
|
231
|
+
top -= box_fitter.content_heights[0] + item_spacing
|
232
|
+
height -= box_fitter.content_heights[0] + item_spacing
|
233
|
+
|
234
|
+
break if !box_fitter.fit_successful? || height <= 0
|
235
|
+
end
|
236
|
+
|
237
|
+
@height = @results.sum {|box_fitter| box_fitter.content_heights[0] } +
|
238
|
+
(@results.count - 1) * item_spacing +
|
239
|
+
reserved_height
|
240
|
+
|
241
|
+
@fit_successful = @results.all?(&:fit_successful?) && @results.size == @children.size
|
242
|
+
end
|
243
|
+
|
244
|
+
private
|
245
|
+
|
246
|
+
# Removes the +content_indentation+ from the left side of the given shape (a Geom2D::PolygonSet).
|
247
|
+
def remove_indent_from_frame_shape(shape)
|
248
|
+
polygon_index = 0
|
249
|
+
data = []
|
250
|
+
|
251
|
+
# Determine the lower-left-most and upper-left-most vertices and their indices, together
|
252
|
+
# with the polygon index that holds them and the direction wrt to the indices from
|
253
|
+
# upper-left-most to lower-left-most.
|
254
|
+
shape.polygons.each_with_index do |polygon, pindex|
|
255
|
+
lower_vertex = upper_vertex = polygon[0]
|
256
|
+
lower_index = upper_index = 0
|
257
|
+
1.upto(polygon.nr_of_vertices - 1) do |i|
|
258
|
+
v = polygon[i]
|
259
|
+
if v.y < lower_vertex.y || (v.y == lower_vertex.y && v.x <= lower_vertex.x)
|
260
|
+
lower_vertex = v
|
261
|
+
lower_index = i
|
262
|
+
elsif v.y > upper_vertex.y || (v.y == upper_vertex.y && v.x <= upper_vertex.x)
|
263
|
+
upper_vertex = v
|
264
|
+
upper_index = i
|
265
|
+
end
|
266
|
+
end
|
267
|
+
direction = upper_vertex.x == polygon[(upper_index + 1) % polygon.nr_of_vertices].x ? 1 : -1
|
268
|
+
if data.empty? || data[0].x > lower_vertex.x
|
269
|
+
polygon_index = pindex
|
270
|
+
data = [lower_vertex, lower_index, upper_vertex, upper_index, direction]
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# Now we have all the data to remove the indentation on the left side of the polygon. This
|
275
|
+
# is done by shifting all vertices between and including the lower-left-most and
|
276
|
+
# upper-left-most vertices to the right.
|
277
|
+
vertices = shape.polygons[polygon_index].to_a
|
278
|
+
point = data[2]
|
279
|
+
index = data[3]
|
280
|
+
while point != data[0]
|
281
|
+
vertices[index] = Geom2D::Point(point.x + content_indentation, point.y)
|
282
|
+
index = (index + data[4]) % vertices.size
|
283
|
+
point = vertices[index]
|
284
|
+
end
|
285
|
+
vertices[data[1]] = Geom2D::Point(data[0].x + content_indentation, data[0].y)
|
286
|
+
|
287
|
+
shape.polygons[polygon_index] = Geom2D::Polygon(*vertices)
|
288
|
+
end
|
289
|
+
|
290
|
+
# Splits the content of the list box. This method is called from Box#split.
|
291
|
+
def split_content(_available_width, _available_height, _frame)
|
292
|
+
remaining_boxes = @results[-1].remaining_boxes
|
293
|
+
first_is_split_box = remaining_boxes.first&.split_box?
|
294
|
+
children = (remaining_boxes.empty? ? [] : [remaining_boxes]) + @children[@results.size..-1]
|
295
|
+
|
296
|
+
box = create_split_box(split_box_value: first_is_split_box ? :hide_first_marker : :show_first_marker)
|
297
|
+
box.instance_variable_set(:@children, children)
|
298
|
+
box.instance_variable_set(:@start_number,
|
299
|
+
@start_number + @results.size + (first_is_split_box ? -1 : 0))
|
300
|
+
box.instance_variable_set(:@results, [])
|
301
|
+
box.instance_variable_set(:@results_item_marker_x, [])
|
302
|
+
|
303
|
+
[self, box]
|
304
|
+
end
|
305
|
+
|
306
|
+
# Creates a box for the item marker at the given item index, using #item_style to decide on
|
307
|
+
# its contents.
|
308
|
+
def item_marker_box(document, index)
|
309
|
+
return @item_type.call(document, self, index) if @item_type.kind_of?(Proc)
|
310
|
+
return @item_marker_box if defined?(@item_marker_box)
|
311
|
+
|
312
|
+
fragment = case @item_type
|
313
|
+
when :disc
|
314
|
+
TextFragment.create("•", font: document.fonts.add("Times"),
|
315
|
+
font_size: style.font_size)
|
316
|
+
when :circle
|
317
|
+
TextFragment.create("❍", font: document.fonts.add("ZapfDingbats"),
|
318
|
+
font_size: style.font_size / 2.0,
|
319
|
+
text_rise: -style.font_size / 1.8)
|
320
|
+
when :square
|
321
|
+
TextFragment.create("■", font: document.fonts.add("ZapfDingbats"),
|
322
|
+
font_size: style.font_size / 2.0,
|
323
|
+
text_rise: -style.font_size / 1.8)
|
324
|
+
when :decimal
|
325
|
+
text = (@start_number + index).to_s << "."
|
326
|
+
decimal_style = {
|
327
|
+
font: (style.font? ? style.font : document.fonts.add("Times")),
|
328
|
+
font_size: style.font_size || 10,
|
329
|
+
}
|
330
|
+
TextFragment.create(text, decimal_style)
|
331
|
+
else
|
332
|
+
raise HexaPDF::Error, "Unknown list item type #{@item_type.inspect}"
|
333
|
+
end
|
334
|
+
box = TextBox.new(items: [fragment], style: {align: :right, padding: [0, 5, 0, 0]})
|
335
|
+
@item_marker_box = box unless @item_type == :decimal
|
336
|
+
box
|
337
|
+
end
|
338
|
+
|
339
|
+
# Draws the list items onto the canvas at position [x, y].
|
340
|
+
def draw_content(canvas, _x, _y)
|
341
|
+
@results.each_with_index do |box_fitter, index|
|
342
|
+
if index != 0 || !split_box? || @split_box == :show_first_marker
|
343
|
+
box = item_marker_box(canvas.context.document, index)
|
344
|
+
box.fit(content_indentation, box_fitter.content_heights[0], nil)
|
345
|
+
box.draw(canvas, @results_item_marker_x[index],
|
346
|
+
box_fitter.frames[0].bottom + box_fitter.frames[0].height - box.height)
|
347
|
+
end
|
348
|
+
box_fitter.fit_results.each {|result| result.draw(canvas) }
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
end
|
353
|
+
|
354
|
+
end
|
355
|
+
end
|
@@ -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-2022 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
|