thinreports 0.9.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/test.yml +39 -0
  3. data/.gitignore +1 -0
  4. data/CHANGELOG.md +59 -2
  5. data/Dockerfile +12 -0
  6. data/Gemfile +7 -4
  7. data/README.md +44 -22
  8. data/Rakefile +14 -31
  9. data/lib/thinreports.rb +16 -15
  10. data/lib/thinreports/config.rb +2 -24
  11. data/lib/thinreports/core/errors.rb +6 -17
  12. data/lib/thinreports/core/format/base.rb +62 -61
  13. data/lib/thinreports/core/shape.rb +10 -12
  14. data/lib/thinreports/core/shape/base.rb +3 -12
  15. data/lib/thinreports/core/shape/base/interface.rb +34 -32
  16. data/lib/thinreports/core/shape/base/internal.rb +45 -43
  17. data/lib/thinreports/core/shape/basic.rb +12 -12
  18. data/lib/thinreports/core/shape/basic/block_format.rb +8 -6
  19. data/lib/thinreports/core/shape/basic/block_interface.rb +24 -22
  20. data/lib/thinreports/core/shape/basic/block_internal.rb +28 -26
  21. data/lib/thinreports/core/shape/basic/format.rb +11 -9
  22. data/lib/thinreports/core/shape/basic/interface.rb +79 -77
  23. data/lib/thinreports/core/shape/basic/internal.rb +17 -14
  24. data/lib/thinreports/core/shape/image_block.rb +9 -9
  25. data/lib/thinreports/core/shape/image_block/format.rb +7 -5
  26. data/lib/thinreports/core/shape/image_block/interface.rb +15 -13
  27. data/lib/thinreports/core/shape/image_block/internal.rb +10 -8
  28. data/lib/thinreports/core/shape/list.rb +13 -13
  29. data/lib/thinreports/core/shape/list/format.rb +83 -77
  30. data/lib/thinreports/core/shape/list/manager.rb +179 -176
  31. data/lib/thinreports/core/shape/list/page.rb +91 -93
  32. data/lib/thinreports/core/shape/list/page_state.rb +42 -40
  33. data/lib/thinreports/core/shape/list/section_format.rb +28 -26
  34. data/lib/thinreports/core/shape/list/section_interface.rb +42 -40
  35. data/lib/thinreports/core/shape/list/section_internal.rb +21 -19
  36. data/lib/thinreports/core/shape/manager.rb +4 -13
  37. data/lib/thinreports/core/shape/manager/format.rb +21 -19
  38. data/lib/thinreports/core/shape/manager/internal.rb +87 -84
  39. data/lib/thinreports/core/shape/manager/target.rb +93 -95
  40. data/lib/thinreports/core/shape/page_number.rb +9 -9
  41. data/lib/thinreports/core/shape/page_number/format.rb +20 -18
  42. data/lib/thinreports/core/shape/page_number/interface.rb +20 -18
  43. data/lib/thinreports/core/shape/page_number/internal.rb +46 -44
  44. data/lib/thinreports/core/shape/style.rb +5 -14
  45. data/lib/thinreports/core/shape/style/base.rb +121 -120
  46. data/lib/thinreports/core/shape/style/basic.rb +12 -10
  47. data/lib/thinreports/core/shape/style/graphic.rb +35 -33
  48. data/lib/thinreports/core/shape/style/text.rb +113 -107
  49. data/lib/thinreports/core/shape/text.rb +9 -9
  50. data/lib/thinreports/core/shape/text/format.rb +10 -9
  51. data/lib/thinreports/core/shape/text/interface.rb +13 -11
  52. data/lib/thinreports/core/shape/text/internal.rb +14 -12
  53. data/lib/thinreports/core/shape/text_block.rb +10 -10
  54. data/lib/thinreports/core/shape/text_block/format.rb +35 -33
  55. data/lib/thinreports/core/shape/text_block/formatter.rb +26 -24
  56. data/lib/thinreports/core/shape/text_block/formatter/basic.rb +25 -25
  57. data/lib/thinreports/core/shape/text_block/formatter/datetime.rb +15 -12
  58. data/lib/thinreports/core/shape/text_block/formatter/number.rb +52 -34
  59. data/lib/thinreports/core/shape/text_block/formatter/padding.rb +17 -14
  60. data/lib/thinreports/core/shape/text_block/interface.rb +25 -23
  61. data/lib/thinreports/core/shape/text_block/internal.rb +66 -68
  62. data/lib/thinreports/core/utils.rb +7 -3
  63. data/lib/thinreports/generator/pdf.rb +24 -25
  64. data/lib/thinreports/generator/pdf/document.rb +109 -110
  65. data/lib/thinreports/generator/pdf/document/draw_shape.rb +57 -51
  66. data/lib/thinreports/generator/pdf/document/draw_template_items.rb +50 -47
  67. data/lib/thinreports/generator/pdf/document/font.rb +67 -58
  68. data/lib/thinreports/generator/pdf/document/graphics.rb +32 -33
  69. data/lib/thinreports/generator/pdf/document/graphics/attributes.rb +121 -121
  70. data/lib/thinreports/generator/pdf/document/graphics/basic.rb +112 -114
  71. data/lib/thinreports/generator/pdf/document/graphics/image.rb +58 -150
  72. data/lib/thinreports/generator/pdf/document/graphics/text.rb +149 -137
  73. data/lib/thinreports/generator/pdf/document/page.rb +60 -64
  74. data/lib/thinreports/generator/pdf/document/parse_color.rb +37 -37
  75. data/lib/thinreports/generator/pdf/drawer/base.rb +41 -41
  76. data/lib/thinreports/generator/pdf/drawer/list.rb +56 -54
  77. data/lib/thinreports/generator/pdf/drawer/list_section.rb +42 -40
  78. data/lib/thinreports/generator/pdf/drawer/page.rb +101 -96
  79. data/lib/thinreports/generator/pdf/prawn_ext.rb +4 -30
  80. data/lib/thinreports/generator/pdf/prawn_ext/calc_image_dimensions.rb +32 -0
  81. data/lib/thinreports/generator/pdf/prawn_ext/width_of.rb +35 -0
  82. data/lib/thinreports/layout.rb +5 -7
  83. data/lib/thinreports/layout/base.rb +5 -9
  84. data/lib/thinreports/layout/format.rb +17 -13
  85. data/lib/thinreports/layout/legacy_schema.rb +17 -13
  86. data/lib/thinreports/layout/version.rb +22 -25
  87. data/lib/thinreports/report.rb +4 -6
  88. data/lib/thinreports/report/base.rb +24 -51
  89. data/lib/thinreports/report/internal.rb +13 -15
  90. data/lib/thinreports/report/page.rb +5 -11
  91. data/lib/thinreports/version.rb +2 -2
  92. data/thinreports.gemspec +10 -19
  93. metadata +13 -288
  94. data/.travis.yml +0 -19
  95. data/examples/character_spacing/character_spacing.rb +0 -8
  96. data/examples/character_spacing/character_spacing.tlf +0 -293
  97. data/examples/dynamic_image/dynamic_image.rb +0 -31
  98. data/examples/dynamic_image/dynamic_image.tlf +0 -661
  99. data/examples/dynamic_image/img200x100.png +0 -0
  100. data/examples/dynamic_image/img50x50.png +0 -0
  101. data/examples/dynamic_style/dynamic_style.rb +0 -150
  102. data/examples/dynamic_style/dynamic_style.tlf +0 -1835
  103. data/examples/dynamic_style/dynamic_style_in_list.tlf +0 -344
  104. data/examples/dynamic_style/image.png +0 -0
  105. data/examples/eudc/eudc.rb +0 -20
  106. data/examples/eudc/eudc.tlf +0 -180
  107. data/examples/eudc/eudc.ttf +0 -0
  108. data/examples/helper.rb +0 -50
  109. data/examples/hidden_shapes/hidden_shapes.rb +0 -9
  110. data/examples/hidden_shapes/hidden_shapes.tlf +0 -449
  111. data/examples/list_events/list_events.rb +0 -32
  112. data/examples/list_events/list_events.tlf +0 -361
  113. data/examples/list_manual_generation/list_manual_generation.rb +0 -22
  114. data/examples/list_manual_generation/list_manual_generation.tlf +0 -132
  115. data/examples/list_page_number/list_page_number.rb +0 -17
  116. data/examples/list_page_number/list_page_number.tlf +0 -254
  117. data/examples/page_number/page_number.rb +0 -33
  118. data/examples/page_number/page_number.tlf +0 -215
  119. data/examples/palleted_png/palleted_png.png +0 -0
  120. data/examples/palleted_png/palleted_png.rb +0 -11
  121. data/examples/palleted_png/palleted_png.tlf +0 -47
  122. data/examples/password_setting/password_setting.rb +0 -10
  123. data/examples/password_setting/password_setting.tlf +0 -45
  124. data/examples/report_callbacks/report_callbacks.rb +0 -14
  125. data/examples/report_callbacks/report_callbacks.tlf +0 -147
  126. data/examples/single_line_tblock/single_line_tblock.rb +0 -13
  127. data/examples/single_line_tblock/single_line_tblock.tlf +0 -170
  128. data/examples/tblock_overflow/tblock_overflow.rb +0 -20
  129. data/examples/tblock_overflow/tblock_overflow.tlf +0 -538
  130. data/examples/tblock_styles/font_size.tlf +0 -383
  131. data/examples/tblock_styles/tblock_styles.rb +0 -43
  132. data/examples/tblock_styles/tblock_styles.tlf +0 -889
  133. data/examples/text_align/text_align.rb +0 -8
  134. data/examples/text_align/text_align.tlf +0 -241
  135. data/examples/typeB_page_size/B4_ISO.tlf +0 -45
  136. data/examples/typeB_page_size/B4_JIS.tlf +0 -45
  137. data/examples/typeB_page_size/typeB_page_size.rb +0 -17
  138. data/examples/word_wrap/word_wrap.rb +0 -26
  139. data/examples/word_wrap/word_wrap.tlf +0 -297
  140. data/lib/thinreports/generator.rb +0 -29
  141. data/lib/thinreports/generator/base.rb +0 -40
  142. data/lib/thinreports/generator/configuration.rb +0 -31
  143. data/lib/thinreports/report/events.rb +0 -32
  144. data/test/data/font.ttf +0 -0
  145. data/test/data/image_normal.jpg +0 -0
  146. data/test/data/image_normal.png +0 -0
  147. data/test/data/image_normal_jpg_noext +0 -0
  148. data/test/data/image_normal_png_noext +0 -0
  149. data/test/data/image_pallete_based.png +0 -0
  150. data/test/data/legacy_layout/all-items.tlf +0 -1
  151. data/test/schema_helper.rb +0 -121
  152. data/test/test_helper.rb +0 -42
  153. data/test/tmp/.gitkeep +0 -0
  154. data/test/unit/core/format/test_base.rb +0 -152
  155. data/test/unit/core/shape/base/test_internal.rb +0 -87
  156. data/test/unit/core/shape/basic/test_block_format.rb +0 -23
  157. data/test/unit/core/shape/basic/test_block_interface.rb +0 -29
  158. data/test/unit/core/shape/basic/test_block_internal.rb +0 -55
  159. data/test/unit/core/shape/basic/test_format.rb +0 -37
  160. data/test/unit/core/shape/basic/test_interface.rb +0 -108
  161. data/test/unit/core/shape/basic/test_internal.rb +0 -55
  162. data/test/unit/core/shape/image_block/test_interface.rb +0 -24
  163. data/test/unit/core/shape/image_block/test_internal.rb +0 -31
  164. data/test/unit/core/shape/list/test_format.rb +0 -111
  165. data/test/unit/core/shape/list/test_manager.rb +0 -67
  166. data/test/unit/core/shape/list/test_page.rb +0 -82
  167. data/test/unit/core/shape/list/test_page_state.rb +0 -31
  168. data/test/unit/core/shape/list/test_section_format.rb +0 -36
  169. data/test/unit/core/shape/list/test_section_interface.rb +0 -75
  170. data/test/unit/core/shape/list/test_section_internal.rb +0 -50
  171. data/test/unit/core/shape/manager/test_format.rb +0 -38
  172. data/test/unit/core/shape/manager/test_internal.rb +0 -132
  173. data/test/unit/core/shape/manager/test_target.rb +0 -140
  174. data/test/unit/core/shape/page_number/test_format.rb +0 -55
  175. data/test/unit/core/shape/page_number/test_interface.rb +0 -33
  176. data/test/unit/core/shape/page_number/test_internal.rb +0 -81
  177. data/test/unit/core/shape/styles/test_base.rb +0 -191
  178. data/test/unit/core/shape/styles/test_basic.rb +0 -24
  179. data/test/unit/core/shape/styles/test_graphic.rb +0 -50
  180. data/test/unit/core/shape/styles/test_text.rb +0 -97
  181. data/test/unit/core/shape/text/test_format.rb +0 -44
  182. data/test/unit/core/shape/text/test_internal.rb +0 -20
  183. data/test/unit/core/shape/text_block/formatter/test_basic.rb +0 -24
  184. data/test/unit/core/shape/text_block/formatter/test_datetime.rb +0 -49
  185. data/test/unit/core/shape/text_block/formatter/test_number.rb +0 -76
  186. data/test/unit/core/shape/text_block/formatter/test_padding.rb +0 -77
  187. data/test/unit/core/shape/text_block/test_format.rb +0 -169
  188. data/test/unit/core/shape/text_block/test_formatter.rb +0 -28
  189. data/test/unit/core/shape/text_block/test_interface.rb +0 -63
  190. data/test/unit/core/shape/text_block/test_internal.rb +0 -128
  191. data/test/unit/core/test_shape.rb +0 -52
  192. data/test/unit/core/test_utils.rb +0 -68
  193. data/test/unit/generator/pdf/document/graphics/test_attributes.rb +0 -135
  194. data/test/unit/generator/pdf/document/graphics/test_basic.rb +0 -45
  195. data/test/unit/generator/pdf/document/graphics/test_image.rb +0 -150
  196. data/test/unit/generator/pdf/document/graphics/test_text.rb +0 -171
  197. data/test/unit/generator/pdf/document/test_font.rb +0 -84
  198. data/test/unit/generator/pdf/document/test_graphics.rb +0 -42
  199. data/test/unit/generator/pdf/document/test_page.rb +0 -122
  200. data/test/unit/generator/pdf/document/test_parse_color.rb +0 -30
  201. data/test/unit/generator/pdf/test_document.rb +0 -20
  202. data/test/unit/generator/test_base.rb +0 -40
  203. data/test/unit/generator/test_configuration.rb +0 -21
  204. data/test/unit/generator/test_pdf.rb +0 -24
  205. data/test/unit/layout/test_base.rb +0 -41
  206. data/test/unit/layout/test_format.rb +0 -100
  207. data/test/unit/layout/test_legacy_schema.rb +0 -574
  208. data/test/unit/layout/test_version.rb +0 -95
  209. data/test/unit/report/test_base.rb +0 -262
  210. data/test/unit/report/test_internal.rb +0 -206
  211. data/test/unit/test_config.rb +0 -51
  212. data/test/unit/test_layout.rb +0 -12
  213. data/test/unit/test_report.rb +0 -18
@@ -1,172 +1,80 @@
1
- # coding: utf-8
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
- module PDF::Graphics
12
- # @param [String] filename
13
- # @param [Numeric, Strng] x
14
- # @param [Numeric, Strng] y
15
- # @param [Numeric, Strng] w
16
- # @param [Numeric, Strng] h
17
- def image(filename, x, y, w, h)
18
- w, h = s2f(w, h)
19
- pdf.image(filename, at: pos(x, y), width: w, height: h)
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
- image_id = Digest::MD5.hexdigest(base64_string)
72
- create_temp_imagefile(image_id, image_data)
73
- end
74
-
75
- def normalize_image_from_file(file)
76
- return file unless file.is_a?(String)
77
-
78
- filename = file
79
- extname = File.extname(filename)
80
-
81
- return filename unless png_conversion_enabled?
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
- def need_normalize?
151
- pallete_based? && transparency_chunk?
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 normalize
155
- @image.to_blob(color_mode: ChunkyPNG::COLOR_TRUECOLOR_ALPHA)
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
- private
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
- def transparency_chunk?
166
- !@datastream.transparency_chunk.nil?
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,173 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Thinreports
4
4
  module Generator
5
-
6
- module PDF::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
- box_attrs = text_box_attrs(x, y, w, h, single: attrs.delete(:single),
28
- overflow: attrs[:overflow])
29
- # Do not break by word unless :word_wrap is :break_word
30
- content = text_without_line_wrap(content) if attrs[:word_wrap] == :none
31
-
32
- with_text_styles(attrs) do |built_attrs, font_styles|
33
- pdf.formatted_text_box([{ text: content, styles: font_styles }],
34
- built_attrs.merge(box_attrs))
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
+ rescue Prawn::Errors::CannotFit
44
+ # Nothing to do.
45
+ #
46
+ # When the area is too small compared
47
+ # with the content and the style of the text.
48
+ # (See prawn/core/text/formatted/line_wrap.rb#L185)
35
49
  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
50
 
44
- # @see #text_box
45
- def text(content, x, y, w, h, attrs = {})
46
- # Set the :overflow property to :shirink_to_fit.
47
- text_box(content, x, y, w, h, { overflow: :shirink_to_fit }.merge(attrs))
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))
51
+ # @see #text_box
52
+ def text(content, x, y, w, h, attrs = {})
53
+ # Set the :overflow property to :shirink_to_fit.
54
+ text_box(content, x, y, w, h, { overflow: :shirink_to_fit }.merge(attrs))
67
55
  end
68
- end
69
56
 
70
- # @param attrs (see #text)
71
- # @yield [built_attrs, font_styles]
72
- # @yieldparam [Hash] built_attrs The finalized attributes.
73
- # @yieldparam [Array] font_styles The finalized styles.
74
- def with_text_styles(attrs, &block)
75
- # When no color is given, do not draw.
76
- return unless attrs.key?(:color) && attrs[:color] != 'none'
77
-
78
- save_graphics_state
79
-
80
- fontinfo = {name: attrs.delete(:font).to_s,
81
- color: parse_color(attrs.delete(:color)),
82
- size: s2f(attrs.delete(:size))}
83
-
84
- # Add the specified value to :leading option.
85
- line_height = attrs.delete(:line_height)
86
- if line_height
87
- attrs[:leading] = text_line_leading(s2f(line_height),
88
- name: fontinfo[:name],
89
- size: fontinfo[:size])
57
+ private
58
+
59
+ # @param x (see #text_box)
60
+ # @param y (see #text_box)
61
+ # @param w (see #text_box)
62
+ # @param h (see #text_box)
63
+ # @param [Hash] states
64
+ # @option states [Boolean] :single
65
+ # @option states [Symbold] :overflow
66
+ # @return [Hash]
67
+ def text_box_attrs(x, y, w, h, states = {})
68
+ attrs = {
69
+ at: pos(x, y),
70
+ width: s2f(w)
71
+ }
72
+ if states[:single]
73
+ states[:overflow] != :expand ? attrs.merge(single_line: true) : attrs
74
+ else
75
+ attrs.merge(height: s2f(h))
76
+ end
90
77
  end
91
78
 
92
- # Set the :character_spacing option.
93
- spacing = attrs.delete(:letter_spacing)
94
- attrs[:character_spacing] = s2f(spacing) if spacing
79
+ # @param attrs (see #text)
80
+ # @yield [built_attrs, font_styles]
81
+ # @yieldparam [Hash] built_attrs The finalized attributes.
82
+ # @yieldparam [Array] font_styles The finalized styles.
83
+ def with_text_styles(attrs, &block)
84
+ # When no color is given, do not draw.
85
+ return unless attrs.key?(:color) && attrs[:color] != 'none'
86
+
87
+ save_graphics_state
88
+
89
+ fontinfo = {
90
+ name: attrs.delete(:font).to_s,
91
+ color: parse_color(attrs.delete(:color)),
92
+ size: s2f(attrs.delete(:size))
93
+ }
94
+
95
+ # Add the specified value to :leading option.
96
+ line_height = attrs.delete(:line_height)
97
+ if line_height
98
+ attrs[:leading] = text_line_leading(
99
+ s2f(line_height),
100
+ name: fontinfo[:name],
101
+ size: fontinfo[:size]
102
+ )
103
+ end
95
104
 
96
- # Or... with_font_styles(attrs, fontinfo, &block)
97
- with_font_styles(attrs, fontinfo) do |modified_attrs, styles|
98
- block.call(modified_attrs, styles)
99
- end
105
+ # Set the :character_spacing option.
106
+ spacing = attrs.delete(:letter_spacing)
107
+ attrs[:character_spacing] = s2f(spacing) if spacing
100
108
 
101
- restore_graphics_state
102
- end
109
+ # Or... with_font_styles(attrs, fontinfo, &block)
110
+ with_font_styles(attrs, fontinfo) do |modified_attrs, styles|
111
+ block.call(modified_attrs, styles)
112
+ end
103
113
 
104
- # @param [Numeric] line_height
105
- # @param [Hash] font
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
114
+ restore_graphics_state
115
+ end
112
116
 
113
- # @param [String] content
114
- # @return [String]
115
- def text_without_line_wrap(content)
116
- content.gsub(/ /, Prawn::Text::NBSP)
117
- end
117
+ # @param [Numeric] line_height
118
+ # @param [Hash] font
119
+ # @option font [String] :name Name of font.
120
+ # @option font [Numeric] :size Size of font.
121
+ # @return [Numeric]
122
+ def text_line_leading(line_height, font)
123
+ line_height - pdf.font(font[:name], size: font[:size]).height
124
+ end
118
125
 
119
- # @param [Hash] attrs
120
- # @param [Hash] font
121
- # @option font [String] :color
122
- # @option font [Numeric] :size
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
126
+ # @param [String] content
127
+ # @return [String]
128
+ def text_without_line_wrap(content)
129
+ content.gsub(/ /, Prawn::Text::NBSP)
135
130
  end
136
131
 
137
- # Emulate bold style.
138
- if manual && manual.include?(:bold)
139
- pdf.stroke_color(font[:color])
140
- pdf.line_width(font[:size] * 0.025)
132
+ # @param [Hash] attrs
133
+ # @param [Hash] font
134
+ # @option font [String] :color
135
+ # @option font [Numeric] :size
136
+ # @option font [String] :name
137
+ # @yield [attributes, styles]
138
+ # @yieldparam [Hash] modified_attrs
139
+ # @yieldparam [Array] styles
140
+ def with_font_styles(attrs, font, &block)
141
+ # Building font styles.
142
+ styles = attrs.delete(:styles)
143
+
144
+ if styles
145
+ manual, styles = styles.partition do |style|
146
+ %i[bold italic].include?(style) && !font_has_style?(font[:name], style)
147
+ end
148
+ end
141
149
 
142
- # Change rendering mode to :fill_stroke.
143
- attrs[:mode] = :fill_stroke
144
- end
150
+ # Emulate bold style.
151
+ if manual && manual.include?(:bold)
152
+ pdf.stroke_color(font[:color])
153
+ pdf.line_width(font[:size] * 0.025)
145
154
 
146
- # Emulate italic style.
147
- if manual && manual.include?(:italic)
148
- # FIXME
149
- # pdf.transformation_matrix(1, 0, 0.26, 1, 0, 0)
150
- end
155
+ # Change rendering mode to :fill_stroke.
156
+ attrs[:mode] = :fill_stroke
157
+ end
158
+
159
+ # Emulate italic style.
160
+ if manual && manual.include?(:italic)
161
+ # FIXME
162
+ # pdf.transformation_matrix(1, 0, 0.26, 1, 0, 0)
163
+ end
151
164
 
152
- pdf.font(font[:name], size: font[:size]) do
153
- pdf.fill_color(font[:color])
154
- block.call(attrs, styles || [])
165
+ pdf.font(font[:name], size: font[:size]) do
166
+ pdf.fill_color(font[:color])
167
+ block.call(attrs, styles || [])
168
+ end
155
169
  end
156
170
  end
157
-
158
171
  end
159
-
160
172
  end
161
173
  end