thinreports 0.9.1 → 0.10.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/.rubocop.yml +41 -0
- data/.travis.yml +4 -6
- data/CHANGELOG.md +21 -2
- data/Gemfile +2 -4
- data/README.md +4 -5
- data/Rakefile +7 -8
- data/examples/character_spacing/character_spacing.rb +1 -1
- data/examples/dynamic_image/dynamic_image.rb +3 -3
- data/examples/dynamic_style/dynamic_style.rb +1 -1
- data/examples/eudc/eudc.rb +1 -1
- data/examples/helper.rb +2 -2
- data/examples/hidden_shapes/hidden_shapes.rb +1 -1
- data/examples/list_events/list_events.rb +1 -1
- data/examples/list_manual_generation/list_manual_generation.rb +1 -1
- data/examples/list_page_number/list_page_number.rb +1 -1
- data/examples/page_number/page_number.rb +1 -1
- data/examples/palleted_png/palleted_png.rb +1 -3
- data/examples/password_setting/password_setting.rb +1 -1
- data/examples/report_callbacks/report_callbacks.rb +1 -1
- data/examples/single_line_tblock/single_line_tblock.rb +1 -1
- data/examples/tblock_overflow/tblock_overflow.rb +1 -1
- data/examples/tblock_styles/tblock_styles.rb +1 -1
- data/examples/text_align/text_align.rb +1 -1
- data/examples/typeB_page_size/typeB_page_size.rb +1 -1
- data/examples/word_wrap/word_wrap.rb +1 -1
- data/lib/thinreports.rb +16 -15
- data/lib/thinreports/config.rb +2 -24
- data/lib/thinreports/core/errors.rb +6 -17
- data/lib/thinreports/core/format/base.rb +62 -61
- data/lib/thinreports/core/shape.rb +13 -14
- data/lib/thinreports/core/shape/base.rb +3 -12
- data/lib/thinreports/core/shape/base/interface.rb +35 -32
- data/lib/thinreports/core/shape/base/internal.rb +45 -43
- data/lib/thinreports/core/shape/basic.rb +12 -12
- data/lib/thinreports/core/shape/basic/block_format.rb +8 -6
- data/lib/thinreports/core/shape/basic/block_interface.rb +24 -22
- data/lib/thinreports/core/shape/basic/block_internal.rb +28 -26
- data/lib/thinreports/core/shape/basic/format.rb +11 -9
- data/lib/thinreports/core/shape/basic/interface.rb +79 -77
- data/lib/thinreports/core/shape/basic/internal.rb +17 -14
- data/lib/thinreports/core/shape/image_block.rb +9 -9
- data/lib/thinreports/core/shape/image_block/format.rb +7 -5
- data/lib/thinreports/core/shape/image_block/interface.rb +15 -13
- data/lib/thinreports/core/shape/image_block/internal.rb +10 -8
- data/lib/thinreports/core/shape/list.rb +13 -13
- data/lib/thinreports/core/shape/list/format.rb +83 -77
- data/lib/thinreports/core/shape/list/manager.rb +182 -176
- data/lib/thinreports/core/shape/list/page.rb +94 -93
- data/lib/thinreports/core/shape/list/page_state.rb +42 -40
- data/lib/thinreports/core/shape/list/section_format.rb +28 -26
- data/lib/thinreports/core/shape/list/section_interface.rb +42 -40
- data/lib/thinreports/core/shape/list/section_internal.rb +21 -19
- data/lib/thinreports/core/shape/manager.rb +4 -13
- data/lib/thinreports/core/shape/manager/format.rb +21 -19
- data/lib/thinreports/core/shape/manager/internal.rb +88 -84
- data/lib/thinreports/core/shape/manager/target.rb +93 -95
- data/lib/thinreports/core/shape/page_number.rb +9 -9
- data/lib/thinreports/core/shape/page_number/format.rb +20 -18
- data/lib/thinreports/core/shape/page_number/interface.rb +20 -18
- data/lib/thinreports/core/shape/page_number/internal.rb +47 -44
- data/lib/thinreports/core/shape/style.rb +5 -14
- data/lib/thinreports/core/shape/style/base.rb +121 -120
- data/lib/thinreports/core/shape/style/basic.rb +12 -10
- data/lib/thinreports/core/shape/style/graphic.rb +35 -33
- data/lib/thinreports/core/shape/style/text.rb +113 -107
- data/lib/thinreports/core/shape/text.rb +9 -9
- data/lib/thinreports/core/shape/text/format.rb +10 -9
- data/lib/thinreports/core/shape/text/interface.rb +13 -11
- data/lib/thinreports/core/shape/text/internal.rb +14 -12
- data/lib/thinreports/core/shape/text_block.rb +10 -10
- data/lib/thinreports/core/shape/text_block/format.rb +35 -33
- data/lib/thinreports/core/shape/text_block/formatter.rb +26 -24
- data/lib/thinreports/core/shape/text_block/formatter/basic.rb +25 -25
- data/lib/thinreports/core/shape/text_block/formatter/datetime.rb +15 -12
- data/lib/thinreports/core/shape/text_block/formatter/number.rb +45 -39
- data/lib/thinreports/core/shape/text_block/formatter/padding.rb +17 -14
- data/lib/thinreports/core/shape/text_block/interface.rb +25 -23
- data/lib/thinreports/core/shape/text_block/internal.rb +68 -68
- data/lib/thinreports/core/utils.rb +4 -3
- data/lib/thinreports/generator/pdf.rb +24 -25
- data/lib/thinreports/generator/pdf/document.rb +109 -110
- data/lib/thinreports/generator/pdf/document/draw_shape.rb +58 -51
- data/lib/thinreports/generator/pdf/document/draw_template_items.rb +51 -47
- data/lib/thinreports/generator/pdf/document/font.rb +62 -59
- data/lib/thinreports/generator/pdf/document/graphics.rb +32 -33
- data/lib/thinreports/generator/pdf/document/graphics/attributes.rb +122 -121
- data/lib/thinreports/generator/pdf/document/graphics/basic.rb +113 -114
- data/lib/thinreports/generator/pdf/document/graphics/image.rb +58 -150
- data/lib/thinreports/generator/pdf/document/graphics/text.rb +152 -137
- data/lib/thinreports/generator/pdf/document/page.rb +63 -64
- data/lib/thinreports/generator/pdf/document/parse_color.rb +37 -37
- data/lib/thinreports/generator/pdf/drawer/base.rb +41 -41
- data/lib/thinreports/generator/pdf/drawer/list.rb +48 -45
- data/lib/thinreports/generator/pdf/drawer/list_section.rb +42 -40
- data/lib/thinreports/generator/pdf/drawer/page.rb +101 -96
- data/lib/thinreports/generator/pdf/prawn_ext.rb +4 -30
- data/lib/thinreports/generator/pdf/prawn_ext/calc_image_dimensions.rb +34 -0
- data/lib/thinreports/generator/pdf/prawn_ext/width_of.rb +29 -0
- data/lib/thinreports/layout.rb +5 -7
- data/lib/thinreports/layout/base.rb +5 -7
- data/lib/thinreports/layout/format.rb +18 -13
- data/lib/thinreports/layout/legacy_schema.rb +18 -10
- data/lib/thinreports/layout/version.rb +22 -25
- data/lib/thinreports/report.rb +4 -6
- data/lib/thinreports/report/base.rb +24 -51
- data/lib/thinreports/report/internal.rb +9 -11
- data/lib/thinreports/report/page.rb +5 -6
- data/lib/thinreports/version.rb +2 -2
- data/test/schema_helper.rb +4 -3
- data/test/test_helper.rb +9 -3
- data/test/unit/core/format/test_base.rb +1 -1
- data/test/unit/core/shape/base/test_internal.rb +1 -1
- data/test/unit/core/shape/basic/test_block_format.rb +1 -1
- data/test/unit/core/shape/basic/test_block_interface.rb +1 -1
- data/test/unit/core/shape/basic/test_block_internal.rb +1 -1
- data/test/unit/core/shape/basic/test_format.rb +1 -1
- data/test/unit/core/shape/basic/test_interface.rb +1 -1
- data/test/unit/core/shape/basic/test_internal.rb +1 -1
- data/test/unit/core/shape/image_block/test_interface.rb +1 -1
- data/test/unit/core/shape/image_block/test_internal.rb +1 -1
- data/test/unit/core/shape/list/test_format.rb +1 -1
- data/test/unit/core/shape/list/test_manager.rb +4 -4
- data/test/unit/core/shape/list/test_page.rb +1 -1
- data/test/unit/core/shape/list/test_page_state.rb +1 -1
- data/test/unit/core/shape/list/test_section_format.rb +1 -1
- data/test/unit/core/shape/list/test_section_interface.rb +2 -2
- data/test/unit/core/shape/list/test_section_internal.rb +1 -1
- data/test/unit/core/shape/manager/test_format.rb +2 -2
- data/test/unit/core/shape/manager/test_internal.rb +3 -3
- data/test/unit/core/shape/manager/test_target.rb +1 -1
- data/test/unit/core/shape/page_number/test_format.rb +1 -1
- data/test/unit/core/shape/page_number/test_interface.rb +1 -1
- data/test/unit/core/shape/page_number/test_internal.rb +1 -1
- data/test/unit/core/shape/styles/test_base.rb +1 -1
- data/test/unit/core/shape/styles/test_basic.rb +1 -1
- data/test/unit/core/shape/styles/test_graphic.rb +1 -1
- data/test/unit/core/shape/styles/test_text.rb +1 -1
- data/test/unit/core/shape/text/test_format.rb +1 -1
- data/test/unit/core/shape/text/test_internal.rb +1 -1
- data/test/unit/core/shape/text_block/formatter/test_basic.rb +1 -1
- data/test/unit/core/shape/text_block/formatter/test_datetime.rb +1 -1
- data/test/unit/core/shape/text_block/formatter/test_number.rb +1 -1
- data/test/unit/core/shape/text_block/formatter/test_padding.rb +2 -2
- data/test/unit/core/shape/text_block/test_format.rb +1 -1
- data/test/unit/core/shape/text_block/test_formatter.rb +1 -1
- data/test/unit/core/shape/text_block/test_interface.rb +1 -1
- data/test/unit/core/shape/text_block/test_internal.rb +3 -3
- data/test/unit/core/test_shape.rb +1 -1
- data/test/unit/core/test_utils.rb +2 -2
- data/test/unit/generator/pdf/document/graphics/test_attributes.rb +1 -1
- data/test/unit/generator/pdf/document/graphics/test_basic.rb +2 -1
- data/test/unit/generator/pdf/document/graphics/test_image.rb +27 -131
- data/test/unit/generator/pdf/document/graphics/test_text.rb +1 -1
- data/test/unit/generator/pdf/document/test_font.rb +2 -2
- data/test/unit/generator/pdf/document/test_graphics.rb +1 -1
- data/test/unit/generator/pdf/document/test_page.rb +7 -7
- data/test/unit/generator/pdf/document/test_parse_color.rb +1 -1
- data/test/unit/generator/pdf/prawn_ext/test_calc_image_dimensions.rb +41 -0
- data/test/unit/generator/pdf/prawn_ext/test_width_of.rb +22 -0
- data/test/unit/generator/pdf/test_document.rb +8 -6
- data/test/unit/generator/test_pdf.rb +12 -10
- data/test/unit/layout/test_base.rb +1 -1
- data/test/unit/layout/test_format.rb +2 -2
- data/test/unit/layout/test_legacy_schema.rb +4 -4
- data/test/unit/layout/test_version.rb +11 -80
- data/test/unit/report/test_base.rb +45 -59
- data/test/unit/report/test_internal.rb +1 -1
- data/test/unit/test_config.rb +1 -16
- data/test/unit/test_layout.rb +1 -1
- data/test/unit/test_report.rb +1 -1
- data/thinreports.gemspec +11 -14
- metadata +42 -103
- data/lib/thinreports/generator.rb +0 -29
- data/lib/thinreports/generator/base.rb +0 -40
- data/lib/thinreports/generator/configuration.rb +0 -31
- data/lib/thinreports/report/events.rb +0 -32
- data/test/unit/generator/test_base.rb +0 -40
- data/test/unit/generator/test_configuration.rb +0 -21
|
@@ -1,172 +1,80 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'tempfile'
|
|
4
4
|
require 'base64'
|
|
5
5
|
require 'digest/md5'
|
|
6
|
-
require 'chunky_png'
|
|
7
6
|
|
|
8
7
|
module Thinreports
|
|
9
8
|
module Generator
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# @param [String] image_type Mime-type of image
|
|
23
|
-
# @param [String] base64
|
|
24
|
-
# @param [Numeric, Strng] x
|
|
25
|
-
# @param [Numeric, Strng] y
|
|
26
|
-
# @param [Numeric, Strng] w
|
|
27
|
-
# @param [Numeric, Strng] h
|
|
28
|
-
def base64image(image_type, base64_data, x, y, w, h)
|
|
29
|
-
image_path = normalize_image_from_base64(image_type, base64_data)
|
|
30
|
-
image(image_path, x, y, w, h)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# @param [String, IO] file
|
|
34
|
-
# @param [Numeric, Strng] x
|
|
35
|
-
# @param [Numeric, Strng] y
|
|
36
|
-
# @param [Numeric, Strng] w
|
|
37
|
-
# @param [Numeric, Strng] h
|
|
38
|
-
# @param [Hash] options
|
|
39
|
-
# @option options [:left, :center, :right] :position_x (:left)
|
|
40
|
-
# @option options [:top, :center, :bottom] :position_y (:top)
|
|
41
|
-
def image_box(file, x, y, w, h, options = {})
|
|
42
|
-
image_data_or_path = normalize_image_from_file(file)
|
|
43
|
-
|
|
44
|
-
w, h = s2f(w, h)
|
|
45
|
-
pdf.bounding_box(pos(x, y), width: w, height: h) do
|
|
46
|
-
pdf.image(
|
|
47
|
-
image_data_or_path,
|
|
48
|
-
position: options[:position_x] || :left,
|
|
49
|
-
vposition: options[:position_y] || :top,
|
|
50
|
-
auto_fit: [w, h]
|
|
51
|
-
)
|
|
9
|
+
class PDF
|
|
10
|
+
module Graphics
|
|
11
|
+
# @param [String, IO] filename_or_io
|
|
12
|
+
# @param [Numeric, Strng] x
|
|
13
|
+
# @param [Numeric, Strng] y
|
|
14
|
+
# @param [Numeric, Strng] w
|
|
15
|
+
# @param [Numeric, Strng] h
|
|
16
|
+
def image(filename_or_io, x, y, w, h)
|
|
17
|
+
w, h = s2f(w, h)
|
|
18
|
+
pdf.image(filename_or_io, at: pos(x, y), width: w, height: h)
|
|
52
19
|
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def normalize_image_from_base64(image_type, base64_string)
|
|
56
|
-
raw_image_data = Base64.decode64(base64_string)
|
|
57
|
-
|
|
58
|
-
image_data =
|
|
59
|
-
if png_conversion_enabled? && image_type == 'image/png'
|
|
60
|
-
png_normalizer = PNGNormalizer.load_blob(raw_image_data)
|
|
61
|
-
|
|
62
|
-
if png_normalizer.need_normalize?
|
|
63
|
-
png_normalizer.normalize
|
|
64
|
-
else
|
|
65
|
-
raw_image_data
|
|
66
|
-
end
|
|
67
|
-
else
|
|
68
|
-
raw_image_data
|
|
69
|
-
end
|
|
70
20
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return filename unless extname.downcase == '.png' || extname.empty?
|
|
83
|
-
|
|
84
|
-
return temp_image_registry[filename].path if temp_image_registry.key?(filename)
|
|
85
|
-
|
|
86
|
-
png_normalizer = PNGNormalizer.load_file(filename)
|
|
87
|
-
|
|
88
|
-
# Returns the original filename when the image is not PNG.
|
|
89
|
-
return filename if png_normalizer.nil?
|
|
90
|
-
return filename unless png_normalizer.need_normalize?
|
|
91
|
-
|
|
92
|
-
image_id = filename
|
|
93
|
-
image_data = png_normalizer.normalize
|
|
94
|
-
|
|
95
|
-
create_temp_imagefile(image_id, image_data)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def clean_temp_images
|
|
99
|
-
temp_image_registry.each_value do |image_path|
|
|
100
|
-
File.delete(image_path) if File.exist?(image_path)
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def temp_image_registry
|
|
105
|
-
@temp_image_registry ||= {}
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# @param [String] image_id
|
|
109
|
-
# @param [String] image_data
|
|
110
|
-
# @return [String] Path to imagefile
|
|
111
|
-
def create_temp_imagefile(image_id, image_data)
|
|
112
|
-
temp_image_registry[image_id] ||= begin
|
|
113
|
-
file = Tempfile.new('temp-image')
|
|
114
|
-
file.binmode
|
|
115
|
-
file.write(image_data)
|
|
116
|
-
file.open
|
|
117
|
-
file
|
|
118
|
-
end
|
|
119
|
-
temp_image_registry[image_id].path
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def png_conversion_enabled?
|
|
123
|
-
Thinreports.config.convert_palleted_transparency_png
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
class PNGNormalizer
|
|
127
|
-
def self.load_file(filename)
|
|
128
|
-
image = ChunkyPNG::Image.from_file(filename)
|
|
129
|
-
datastream = ChunkyPNG::Datastream.from_file(filename)
|
|
130
|
-
self.new image, datastream
|
|
131
|
-
rescue ChunkyPNG::SignatureMismatch
|
|
132
|
-
# Returns nil if image is not PNG.
|
|
133
|
-
nil
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def self.load_blob(data)
|
|
137
|
-
image = ChunkyPNG::Image.from_blob(data)
|
|
138
|
-
datastream = ChunkyPNG::Datastream.from_blob(data)
|
|
139
|
-
self.new image, datastream
|
|
140
|
-
rescue ChunkyPNG::SignatureMismatch
|
|
141
|
-
# Returns nil if image is not PNG.
|
|
142
|
-
nil
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def initialize(image, datastream)
|
|
146
|
-
@image = image
|
|
147
|
-
@datastream = datastream
|
|
21
|
+
# @param [String] base64_data
|
|
22
|
+
# @param [Numeric, Strng] x
|
|
23
|
+
# @param [Numeric, Strng] y
|
|
24
|
+
# @param [Numeric, Strng] w
|
|
25
|
+
# @param [Numeric, Strng] h
|
|
26
|
+
def base64image(base64_data, x, y, w, h)
|
|
27
|
+
image_data = Base64.decode64(base64_data)
|
|
28
|
+
image_id = Digest::MD5.hexdigest(base64_data)
|
|
29
|
+
image_path = create_temp_imagefile(image_id, image_data)
|
|
30
|
+
|
|
31
|
+
image(image_path, x, y, w, h)
|
|
148
32
|
end
|
|
149
33
|
|
|
150
|
-
|
|
151
|
-
|
|
34
|
+
# @param [String, IO] filename_or_io
|
|
35
|
+
# @param [Numeric, Strng] x
|
|
36
|
+
# @param [Numeric, Strng] y
|
|
37
|
+
# @param [Numeric, Strng] w
|
|
38
|
+
# @param [Numeric, Strng] h
|
|
39
|
+
# @param [Hash] options
|
|
40
|
+
# @option options [:left, :center, :right] :position_x (:left)
|
|
41
|
+
# @option options [:top, :center, :bottom] :position_y (:top)
|
|
42
|
+
def image_box(filename_or_io, x, y, w, h, options = {})
|
|
43
|
+
w, h = s2f(w, h)
|
|
44
|
+
pdf.bounding_box(pos(x, y), width: w, height: h) do
|
|
45
|
+
pdf.image(
|
|
46
|
+
filename_or_io,
|
|
47
|
+
position: options[:position_x] || :left,
|
|
48
|
+
vposition: options[:position_y] || :top,
|
|
49
|
+
auto_fit: [w, h]
|
|
50
|
+
)
|
|
51
|
+
end
|
|
152
52
|
end
|
|
153
53
|
|
|
154
|
-
def
|
|
155
|
-
|
|
54
|
+
def clean_temp_images
|
|
55
|
+
temp_image_registry.each_value do |image_path|
|
|
56
|
+
File.delete(image_path) if File.exist?(image_path)
|
|
57
|
+
end
|
|
156
58
|
end
|
|
157
59
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
def pallete_based?
|
|
161
|
-
color_mode, _depth = @image.palette.best_color_settings
|
|
162
|
-
color_mode == ChunkyPNG::COLOR_INDEXED
|
|
60
|
+
def temp_image_registry
|
|
61
|
+
@temp_image_registry ||= {}
|
|
163
62
|
end
|
|
164
63
|
|
|
165
|
-
|
|
166
|
-
|
|
64
|
+
# @param [String] image_id
|
|
65
|
+
# @param [String] image_data
|
|
66
|
+
# @return [String] Path to imagefile
|
|
67
|
+
def create_temp_imagefile(image_id, image_data)
|
|
68
|
+
temp_image_registry[image_id] ||= begin
|
|
69
|
+
file = Tempfile.new('temp-image')
|
|
70
|
+
file.binmode
|
|
71
|
+
file.write(image_data)
|
|
72
|
+
file.open
|
|
73
|
+
file
|
|
74
|
+
end
|
|
75
|
+
temp_image_registry[image_id].path
|
|
167
76
|
end
|
|
168
77
|
end
|
|
169
78
|
end
|
|
170
|
-
|
|
171
79
|
end
|
|
172
80
|
end
|
|
@@ -1,161 +1,176 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Thinreports
|
|
4
4
|
module Generator
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
5
|
+
class PDF
|
|
6
|
+
module Graphics
|
|
7
|
+
# @param [String] content
|
|
8
|
+
# @param [Numeric, String] x
|
|
9
|
+
# @param [Numeric, String] y
|
|
10
|
+
# @param [Numeric, String] w
|
|
11
|
+
# @param [Numeric, String] h
|
|
12
|
+
# @param [Hash] attrs ({})
|
|
13
|
+
# @option attrs [String] :font
|
|
14
|
+
# @option attrs [Numeric, String] :size
|
|
15
|
+
# @option attrs [String] :color
|
|
16
|
+
# @option attrs [Array<:bold, :italic, :underline, :strikethrough>]
|
|
17
|
+
# :styles (nil)
|
|
18
|
+
# @option attrs [:left, :center, :right] :align (:left)
|
|
19
|
+
# @option attrs [:top, :center, :bottom] :valign (:top)
|
|
20
|
+
# @option attrs [Numeric, String] :line_height The total height of an text line.
|
|
21
|
+
# @option attrs [Numeric, String] :letter_spacing
|
|
22
|
+
# @option attrs [Boolean] :single (false)
|
|
23
|
+
# @option attrs [:trancate, :shrink_to_fit, :expand] :overflow (:trancate)
|
|
24
|
+
# @option attrs [:none, :break_word] :word_wrap (:none)
|
|
25
|
+
def text_box(content, x, y, w, h, attrs = {})
|
|
26
|
+
w, h = s2f(w, h)
|
|
27
|
+
|
|
28
|
+
box_attrs = text_box_attrs(
|
|
29
|
+
x, y, w, h,
|
|
30
|
+
single: attrs.delete(:single),
|
|
31
|
+
overflow: attrs[:overflow]
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Do not break by word unless :word_wrap is :break_word
|
|
35
|
+
content = text_without_line_wrap(content) if attrs[:word_wrap] == :none
|
|
36
|
+
|
|
37
|
+
with_text_styles(attrs) do |built_attrs, font_styles|
|
|
38
|
+
pdf.formatted_text_box(
|
|
39
|
+
[{ text: content, styles: font_styles }],
|
|
40
|
+
built_attrs.merge(box_attrs)
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
# rubocop:disable Lint/HandleExceptions
|
|
44
|
+
rescue Prawn::Errors::CannotFit
|
|
45
|
+
# Nothing to do.
|
|
46
|
+
#
|
|
47
|
+
# When the area is too small compared
|
|
48
|
+
# with the content and the style of the text.
|
|
49
|
+
# (See prawn/core/text/formatted/line_wrap.rb#L185)
|
|
35
50
|
end
|
|
36
|
-
rescue Prawn::Errors::CannotFit
|
|
37
|
-
# Nothing to do.
|
|
38
|
-
#
|
|
39
|
-
# When the area is too small compared
|
|
40
|
-
# with the content and the style of the text.
|
|
41
|
-
# (See prawn/core/text/formatted/line_wrap.rb#L185)
|
|
42
|
-
end
|
|
43
51
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
private
|
|
51
|
-
|
|
52
|
-
# @param x (see #text_box)
|
|
53
|
-
# @param y (see #text_box)
|
|
54
|
-
# @param w (see #text_box)
|
|
55
|
-
# @param h (see #text_box)
|
|
56
|
-
# @param [Hash] states
|
|
57
|
-
# @option states [Boolean] :single
|
|
58
|
-
# @option states [Symbold] :overflow
|
|
59
|
-
# @return [Hash]
|
|
60
|
-
def text_box_attrs(x, y, w, h, states = {})
|
|
61
|
-
attrs = {at: pos(x, y),
|
|
62
|
-
width: s2f(w)}
|
|
63
|
-
if states[:single]
|
|
64
|
-
states[:overflow] != :expand ? attrs.merge(single_line: true) : attrs
|
|
65
|
-
else
|
|
66
|
-
attrs.merge(height: s2f(h))
|
|
52
|
+
# @see #text_box
|
|
53
|
+
def text(content, x, y, w, h, attrs = {})
|
|
54
|
+
# Set the :overflow property to :shirink_to_fit.
|
|
55
|
+
text_box(content, x, y, w, h, { overflow: :shirink_to_fit }.merge(attrs))
|
|
67
56
|
end
|
|
68
|
-
end
|
|
69
57
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
#
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
# @param x (see #text_box)
|
|
61
|
+
# @param y (see #text_box)
|
|
62
|
+
# @param w (see #text_box)
|
|
63
|
+
# @param h (see #text_box)
|
|
64
|
+
# @param [Hash] states
|
|
65
|
+
# @option states [Boolean] :single
|
|
66
|
+
# @option states [Symbold] :overflow
|
|
67
|
+
# @return [Hash]
|
|
68
|
+
def text_box_attrs(x, y, w, h, states = {})
|
|
69
|
+
attrs = {
|
|
70
|
+
at: pos(x, y),
|
|
71
|
+
width: s2f(w)
|
|
72
|
+
}
|
|
73
|
+
if states[:single]
|
|
74
|
+
states[:overflow] != :expand ? attrs.merge(single_line: true) : attrs
|
|
75
|
+
else
|
|
76
|
+
attrs.merge(height: s2f(h))
|
|
77
|
+
end
|
|
90
78
|
end
|
|
91
79
|
|
|
92
|
-
#
|
|
93
|
-
|
|
94
|
-
|
|
80
|
+
# @param attrs (see #text)
|
|
81
|
+
# @yield [built_attrs, font_styles]
|
|
82
|
+
# @yieldparam [Hash] built_attrs The finalized attributes.
|
|
83
|
+
# @yieldparam [Array] font_styles The finalized styles.
|
|
84
|
+
# rubocop:disable Metrics/AbcSize
|
|
85
|
+
def with_text_styles(attrs, &block)
|
|
86
|
+
# When no color is given, do not draw.
|
|
87
|
+
return unless attrs.key?(:color) && attrs[:color] != 'none'
|
|
88
|
+
|
|
89
|
+
save_graphics_state
|
|
90
|
+
|
|
91
|
+
fontinfo = {
|
|
92
|
+
name: attrs.delete(:font).to_s,
|
|
93
|
+
color: parse_color(attrs.delete(:color)),
|
|
94
|
+
size: s2f(attrs.delete(:size))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# Add the specified value to :leading option.
|
|
98
|
+
line_height = attrs.delete(:line_height)
|
|
99
|
+
if line_height
|
|
100
|
+
attrs[:leading] = text_line_leading(
|
|
101
|
+
s2f(line_height),
|
|
102
|
+
name: fontinfo[:name],
|
|
103
|
+
size: fontinfo[:size]
|
|
104
|
+
)
|
|
105
|
+
end
|
|
95
106
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
end
|
|
107
|
+
# Set the :character_spacing option.
|
|
108
|
+
spacing = attrs.delete(:letter_spacing)
|
|
109
|
+
attrs[:character_spacing] = s2f(spacing) if spacing
|
|
100
110
|
|
|
101
|
-
|
|
102
|
-
|
|
111
|
+
# Or... with_font_styles(attrs, fontinfo, &block)
|
|
112
|
+
with_font_styles(attrs, fontinfo) do |modified_attrs, styles|
|
|
113
|
+
block.call(modified_attrs, styles)
|
|
114
|
+
end
|
|
103
115
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
# @option font [String] :name Name of font.
|
|
107
|
-
# @option font [Numeric] :size Size of font.
|
|
108
|
-
# @return [Numeric]
|
|
109
|
-
def text_line_leading(line_height, font)
|
|
110
|
-
line_height - pdf.font(font[:name], size: font[:size]).height
|
|
111
|
-
end
|
|
116
|
+
restore_graphics_state
|
|
117
|
+
end
|
|
112
118
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
# @param [Numeric] line_height
|
|
120
|
+
# @param [Hash] font
|
|
121
|
+
# @option font [String] :name Name of font.
|
|
122
|
+
# @option font [Numeric] :size Size of font.
|
|
123
|
+
# @return [Numeric]
|
|
124
|
+
def text_line_leading(line_height, font)
|
|
125
|
+
line_height - pdf.font(font[:name], size: font[:size]).height
|
|
126
|
+
end
|
|
118
127
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
# @option font [String] :name
|
|
124
|
-
# @yield [attributes, styles]
|
|
125
|
-
# @yieldparam [Hash] modified_attrs
|
|
126
|
-
# @yieldparam [Array] styles
|
|
127
|
-
def with_font_styles(attrs, font, &block)
|
|
128
|
-
# Building font styles.
|
|
129
|
-
styles = attrs.delete(:styles)
|
|
130
|
-
|
|
131
|
-
if styles
|
|
132
|
-
manual, styles = styles.partition do |style|
|
|
133
|
-
[:bold, :italic].include?(style) && !font_has_style?(font[:name], style)
|
|
134
|
-
end
|
|
128
|
+
# @param [String] content
|
|
129
|
+
# @return [String]
|
|
130
|
+
def text_without_line_wrap(content)
|
|
131
|
+
content.gsub(/ /, Prawn::Text::NBSP)
|
|
135
132
|
end
|
|
136
133
|
|
|
137
|
-
#
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
134
|
+
# @param [Hash] attrs
|
|
135
|
+
# @param [Hash] font
|
|
136
|
+
# @option font [String] :color
|
|
137
|
+
# @option font [Numeric] :size
|
|
138
|
+
# @option font [String] :name
|
|
139
|
+
# @yield [attributes, styles]
|
|
140
|
+
# @yieldparam [Hash] modified_attrs
|
|
141
|
+
# @yieldparam [Array] styles
|
|
142
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
143
|
+
def with_font_styles(attrs, font, &block)
|
|
144
|
+
# Building font styles.
|
|
145
|
+
styles = attrs.delete(:styles)
|
|
146
|
+
|
|
147
|
+
if styles
|
|
148
|
+
manual, styles = styles.partition do |style|
|
|
149
|
+
%i[bold italic].include?(style) && !font_has_style?(font[:name], style)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
141
152
|
|
|
142
|
-
#
|
|
143
|
-
|
|
144
|
-
|
|
153
|
+
# Emulate bold style.
|
|
154
|
+
if manual && manual.include?(:bold)
|
|
155
|
+
pdf.stroke_color(font[:color])
|
|
156
|
+
pdf.line_width(font[:size] * 0.025)
|
|
145
157
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
158
|
+
# Change rendering mode to :fill_stroke.
|
|
159
|
+
attrs[:mode] = :fill_stroke
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Emulate italic style.
|
|
163
|
+
if manual && manual.include?(:italic)
|
|
164
|
+
# FIXME
|
|
165
|
+
# pdf.transformation_matrix(1, 0, 0.26, 1, 0, 0)
|
|
166
|
+
end
|
|
151
167
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
168
|
+
pdf.font(font[:name], size: font[:size]) do
|
|
169
|
+
pdf.fill_color(font[:color])
|
|
170
|
+
block.call(attrs, styles || [])
|
|
171
|
+
end
|
|
155
172
|
end
|
|
156
173
|
end
|
|
157
|
-
|
|
158
174
|
end
|
|
159
|
-
|
|
160
175
|
end
|
|
161
176
|
end
|