thinreports 0.12.1 → 0.13.1

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 (177) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +3 -3
  3. data/.github/workflows/test.yml +28 -36
  4. data/.gitignore +1 -1
  5. data/CHANGELOG.md +25 -1
  6. data/Gemfile +11 -0
  7. data/README.md +12 -17
  8. data/Rakefile +17 -11
  9. data/lib/thinreports/basic_report/core/errors.rb +62 -0
  10. data/lib/thinreports/basic_report/core/format/base.rb +91 -0
  11. data/lib/thinreports/basic_report/core/shape/base/interface.rb +42 -0
  12. data/lib/thinreports/basic_report/core/shape/base/internal.rb +53 -0
  13. data/lib/thinreports/basic_report/core/shape/basic/block_format.rb +15 -0
  14. data/lib/thinreports/basic_report/core/shape/basic/block_interface.rb +34 -0
  15. data/lib/thinreports/basic_report/core/shape/basic/block_internal.rb +36 -0
  16. data/lib/thinreports/basic_report/core/shape/basic/format.rb +24 -0
  17. data/lib/thinreports/basic_report/core/shape/basic/interface.rb +87 -0
  18. data/lib/thinreports/basic_report/core/shape/basic/internal.rb +28 -0
  19. data/lib/thinreports/{core → basic_report/core}/shape/basic.rb +6 -4
  20. data/lib/thinreports/basic_report/core/shape/image_block/format.rb +14 -0
  21. data/lib/thinreports/basic_report/core/shape/image_block/interface.rb +25 -0
  22. data/lib/thinreports/basic_report/core/shape/image_block/internal.rb +19 -0
  23. data/lib/thinreports/{core → basic_report/core}/shape/image_block.rb +6 -4
  24. data/lib/thinreports/basic_report/core/shape/list/format.rb +92 -0
  25. data/lib/thinreports/basic_report/core/shape/list/manager.rb +231 -0
  26. data/lib/thinreports/basic_report/core/shape/list/page.rb +112 -0
  27. data/lib/thinreports/basic_report/core/shape/list/page_state.rb +50 -0
  28. data/lib/thinreports/basic_report/core/shape/list/section_format.rb +37 -0
  29. data/lib/thinreports/basic_report/core/shape/list/section_interface.rb +51 -0
  30. data/lib/thinreports/basic_report/core/shape/list/section_internal.rb +33 -0
  31. data/lib/thinreports/{core → basic_report/core}/shape/list.rb +6 -4
  32. data/lib/thinreports/basic_report/core/shape/manager/format.rb +33 -0
  33. data/lib/thinreports/basic_report/core/shape/manager/internal.rb +109 -0
  34. data/lib/thinreports/basic_report/core/shape/manager/target.rb +114 -0
  35. data/lib/thinreports/basic_report/core/shape/page_number/format.rb +32 -0
  36. data/lib/thinreports/basic_report/core/shape/page_number/interface.rb +31 -0
  37. data/lib/thinreports/basic_report/core/shape/page_number/internal.rb +57 -0
  38. data/lib/thinreports/{core → basic_report/core}/shape/page_number.rb +6 -4
  39. data/lib/thinreports/basic_report/core/shape/stack_view/format.rb +29 -0
  40. data/lib/thinreports/basic_report/core/shape/stack_view/interface.rb +19 -0
  41. data/lib/thinreports/basic_report/core/shape/stack_view/internal.rb +24 -0
  42. data/lib/thinreports/basic_report/core/shape/stack_view/row_format.rb +41 -0
  43. data/lib/thinreports/{core → basic_report/core}/shape/stack_view.rb +6 -4
  44. data/lib/thinreports/basic_report/core/shape/style/base.rb +149 -0
  45. data/lib/thinreports/basic_report/core/shape/style/basic.rb +24 -0
  46. data/lib/thinreports/basic_report/core/shape/style/graphic.rb +43 -0
  47. data/lib/thinreports/basic_report/core/shape/style/text.rb +122 -0
  48. data/lib/thinreports/basic_report/core/shape/text/format.rb +17 -0
  49. data/lib/thinreports/basic_report/core/shape/text/interface.rb +20 -0
  50. data/lib/thinreports/basic_report/core/shape/text/internal.rb +24 -0
  51. data/lib/thinreports/{core → basic_report/core}/shape/text.rb +6 -4
  52. data/lib/thinreports/basic_report/core/shape/text_block/format.rb +43 -0
  53. data/lib/thinreports/basic_report/core/shape/text_block/formatter/basic.rb +41 -0
  54. data/lib/thinreports/basic_report/core/shape/text_block/formatter/datetime.rb +25 -0
  55. data/lib/thinreports/basic_report/core/shape/text_block/formatter/number.rb +69 -0
  56. data/lib/thinreports/basic_report/core/shape/text_block/formatter/padding.rb +27 -0
  57. data/lib/thinreports/basic_report/core/shape/text_block/formatter.rb +38 -0
  58. data/lib/thinreports/basic_report/core/shape/text_block/interface.rb +37 -0
  59. data/lib/thinreports/basic_report/core/shape/text_block/internal.rb +75 -0
  60. data/lib/thinreports/{core → basic_report/core}/shape/text_block.rb +6 -4
  61. data/lib/thinreports/basic_report/core/shape.rb +44 -0
  62. data/lib/thinreports/basic_report/core/utils.rb +47 -0
  63. data/lib/thinreports/basic_report/generator/pdf/document/draw_shape.rb +108 -0
  64. data/lib/thinreports/basic_report/generator/pdf/document/draw_template_items.rb +71 -0
  65. data/lib/thinreports/basic_report/generator/pdf/document/font.rb +90 -0
  66. data/lib/thinreports/basic_report/generator/pdf/document/graphics/attributes.rb +149 -0
  67. data/lib/thinreports/basic_report/generator/pdf/document/graphics/basic.rb +137 -0
  68. data/lib/thinreports/basic_report/generator/pdf/document/graphics/image.rb +99 -0
  69. data/lib/thinreports/basic_report/generator/pdf/document/graphics/text.rb +180 -0
  70. data/lib/thinreports/basic_report/generator/pdf/document/graphics.rb +42 -0
  71. data/lib/thinreports/basic_report/generator/pdf/document/page.rb +96 -0
  72. data/lib/thinreports/basic_report/generator/pdf/document/parse_color.rb +51 -0
  73. data/lib/thinreports/basic_report/generator/pdf/document.rb +132 -0
  74. data/lib/thinreports/basic_report/generator/pdf/drawer/base.rb +50 -0
  75. data/lib/thinreports/basic_report/generator/pdf/drawer/list.rb +65 -0
  76. data/lib/thinreports/basic_report/generator/pdf/drawer/list_section.rb +51 -0
  77. data/lib/thinreports/basic_report/generator/pdf/drawer/page.rb +127 -0
  78. data/lib/thinreports/basic_report/generator/pdf/prawn_ext/calc_image_dimensions.rb +34 -0
  79. data/lib/thinreports/basic_report/generator/pdf/prawn_ext/width_of.rb +31 -0
  80. data/lib/thinreports/basic_report/generator/pdf.rb +66 -0
  81. data/lib/thinreports/basic_report/layout/base.rb +48 -0
  82. data/lib/thinreports/basic_report/layout/format.rb +67 -0
  83. data/lib/thinreports/basic_report/layout/legacy_schema.rb +383 -0
  84. data/lib/thinreports/basic_report/layout/version.rb +41 -0
  85. data/lib/thinreports/{layout.rb → basic_report/layout.rb} +6 -4
  86. data/lib/thinreports/basic_report/report/base.rb +174 -0
  87. data/lib/thinreports/basic_report/report/internal.rb +107 -0
  88. data/lib/thinreports/basic_report/report/page.rb +97 -0
  89. data/lib/thinreports/basic_report/report.rb +26 -0
  90. data/lib/thinreports/basic_report.rb +15 -0
  91. data/lib/thinreports/config.rb +1 -1
  92. data/lib/thinreports/section_report.rb +12 -0
  93. data/lib/thinreports/version.rb +1 -1
  94. data/lib/thinreports.rb +2 -13
  95. data/thinreports.gemspec +3 -7
  96. metadata +97 -154
  97. data/gemfiles/prawn-2.2.gemfile +0 -5
  98. data/gemfiles/prawn-2.3.gemfile +0 -5
  99. data/gemfiles/prawn-2.4.gemfile +0 -5
  100. data/lib/thinreports/core/errors.rb +0 -60
  101. data/lib/thinreports/core/format/base.rb +0 -89
  102. data/lib/thinreports/core/shape/base/interface.rb +0 -40
  103. data/lib/thinreports/core/shape/base/internal.rb +0 -51
  104. data/lib/thinreports/core/shape/basic/block_format.rb +0 -13
  105. data/lib/thinreports/core/shape/basic/block_interface.rb +0 -32
  106. data/lib/thinreports/core/shape/basic/block_internal.rb +0 -34
  107. data/lib/thinreports/core/shape/basic/format.rb +0 -22
  108. data/lib/thinreports/core/shape/basic/interface.rb +0 -85
  109. data/lib/thinreports/core/shape/basic/internal.rb +0 -26
  110. data/lib/thinreports/core/shape/image_block/format.rb +0 -12
  111. data/lib/thinreports/core/shape/image_block/interface.rb +0 -23
  112. data/lib/thinreports/core/shape/image_block/internal.rb +0 -17
  113. data/lib/thinreports/core/shape/list/format.rb +0 -90
  114. data/lib/thinreports/core/shape/list/manager.rb +0 -229
  115. data/lib/thinreports/core/shape/list/page.rb +0 -110
  116. data/lib/thinreports/core/shape/list/page_state.rb +0 -48
  117. data/lib/thinreports/core/shape/list/section_format.rb +0 -35
  118. data/lib/thinreports/core/shape/list/section_interface.rb +0 -49
  119. data/lib/thinreports/core/shape/list/section_internal.rb +0 -31
  120. data/lib/thinreports/core/shape/manager/format.rb +0 -31
  121. data/lib/thinreports/core/shape/manager/internal.rb +0 -107
  122. data/lib/thinreports/core/shape/manager/target.rb +0 -112
  123. data/lib/thinreports/core/shape/page_number/format.rb +0 -30
  124. data/lib/thinreports/core/shape/page_number/interface.rb +0 -29
  125. data/lib/thinreports/core/shape/page_number/internal.rb +0 -55
  126. data/lib/thinreports/core/shape/stack_view/format.rb +0 -27
  127. data/lib/thinreports/core/shape/stack_view/interface.rb +0 -17
  128. data/lib/thinreports/core/shape/stack_view/internal.rb +0 -22
  129. data/lib/thinreports/core/shape/stack_view/row_format.rb +0 -39
  130. data/lib/thinreports/core/shape/style/base.rb +0 -146
  131. data/lib/thinreports/core/shape/style/basic.rb +0 -22
  132. data/lib/thinreports/core/shape/style/graphic.rb +0 -41
  133. data/lib/thinreports/core/shape/style/text.rb +0 -120
  134. data/lib/thinreports/core/shape/text/format.rb +0 -15
  135. data/lib/thinreports/core/shape/text/interface.rb +0 -18
  136. data/lib/thinreports/core/shape/text/internal.rb +0 -22
  137. data/lib/thinreports/core/shape/text_block/format.rb +0 -41
  138. data/lib/thinreports/core/shape/text_block/formatter/basic.rb +0 -39
  139. data/lib/thinreports/core/shape/text_block/formatter/datetime.rb +0 -23
  140. data/lib/thinreports/core/shape/text_block/formatter/number.rb +0 -67
  141. data/lib/thinreports/core/shape/text_block/formatter/padding.rb +0 -25
  142. data/lib/thinreports/core/shape/text_block/formatter.rb +0 -34
  143. data/lib/thinreports/core/shape/text_block/interface.rb +0 -35
  144. data/lib/thinreports/core/shape/text_block/internal.rb +0 -73
  145. data/lib/thinreports/core/shape.rb +0 -42
  146. data/lib/thinreports/core/utils.rb +0 -45
  147. data/lib/thinreports/generate.rb +0 -11
  148. data/lib/thinreports/generator/pdf/document/draw_shape.rb +0 -106
  149. data/lib/thinreports/generator/pdf/document/draw_template_items.rb +0 -69
  150. data/lib/thinreports/generator/pdf/document/font.rb +0 -88
  151. data/lib/thinreports/generator/pdf/document/graphics/attributes.rb +0 -147
  152. data/lib/thinreports/generator/pdf/document/graphics/basic.rb +0 -135
  153. data/lib/thinreports/generator/pdf/document/graphics/image.rb +0 -98
  154. data/lib/thinreports/generator/pdf/document/graphics/text.rb +0 -178
  155. data/lib/thinreports/generator/pdf/document/graphics.rb +0 -40
  156. data/lib/thinreports/generator/pdf/document/page.rb +0 -94
  157. data/lib/thinreports/generator/pdf/document/parse_color.rb +0 -49
  158. data/lib/thinreports/generator/pdf/document.rb +0 -130
  159. data/lib/thinreports/generator/pdf/drawer/base.rb +0 -48
  160. data/lib/thinreports/generator/pdf/drawer/list.rb +0 -63
  161. data/lib/thinreports/generator/pdf/drawer/list_section.rb +0 -49
  162. data/lib/thinreports/generator/pdf/drawer/page.rb +0 -125
  163. data/lib/thinreports/generator/pdf/prawn_ext/calc_image_dimensions.rb +0 -32
  164. data/lib/thinreports/generator/pdf/prawn_ext/width_of.rb +0 -29
  165. data/lib/thinreports/generator/pdf.rb +0 -63
  166. data/lib/thinreports/layout/base.rb +0 -46
  167. data/lib/thinreports/layout/format.rb +0 -65
  168. data/lib/thinreports/layout/legacy_schema.rb +0 -381
  169. data/lib/thinreports/layout/version.rb +0 -39
  170. data/lib/thinreports/report/base.rb +0 -169
  171. data/lib/thinreports/report/internal.rb +0 -105
  172. data/lib/thinreports/report/page.rb +0 -95
  173. data/lib/thinreports/report.rb +0 -24
  174. /data/lib/thinreports/{core → basic_report/core}/shape/base.rb +0 -0
  175. /data/lib/thinreports/{core → basic_report/core}/shape/manager.rb +0 -0
  176. /data/lib/thinreports/{core → basic_report/core}/shape/style.rb +0 -0
  177. /data/lib/thinreports/{generator → basic_report/generator}/pdf/prawn_ext.rb +0 -0
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thinreports
4
+ module BasicReport
5
+ module Generator
6
+ class PDF
7
+ module Graphics
8
+ STROKE_DASH = {
9
+ dashed: [2, 2],
10
+ dotted: [1, 2]
11
+ }.freeze
12
+
13
+ # @param [Numeric, String] x1
14
+ # @param [Numeric, String] y1
15
+ # @param [Numeric, String] x2
16
+ # @param [Numeric, String] y2
17
+ # @param [Hash] attrs ({})
18
+ # @option attrs [String] :stroke
19
+ # @option attrs [Numeric, String] :stroke_width
20
+ # @option attrs ["solid", "dashed", "dotted"] :stroke_type
21
+ def line(x1, y1, x2, y2, attrs = {})
22
+ with_graphic_styles(attrs) do
23
+ pdf.line(pos(x1, y1), pos(x2, y2))
24
+ end
25
+ end
26
+
27
+ # @param [Numeric, String] x
28
+ # @param [Numeric, String] y
29
+ # @param [Numeric, String] w width
30
+ # @param [Numeric, String] h height
31
+ # @param [Hash] attrs ({})
32
+ # @option attrs [Integer, String] :radius
33
+ # @option attrs [String] :stroke
34
+ # @option attrs [Numeric, String] :stroke_width
35
+ # @option attrs ["solid", "dashed", "dotted"] :stroke_type
36
+ # @option attrs [String] :fill
37
+ def rect(x, y, w, h, attrs = {})
38
+ w, h = s2f(w, h)
39
+ radius = s2f(attrs[:radius])
40
+
41
+ with_graphic_styles(attrs) do
42
+ if radius && !radius.zero?
43
+ pdf.rounded_rectangle(pos(x, y), w, h, radius)
44
+ else
45
+ pdf.rectangle(pos(x, y), w, h)
46
+ end
47
+ end
48
+ end
49
+
50
+ # @param [Numeric, String] x center-x
51
+ # @param [Numeric, String] y center-y
52
+ # @param [Numeric, String] rx
53
+ # @param [Numeric, String] ry
54
+ # @param [Hash] attrs ({})
55
+ # @option attrs [String] :stroke
56
+ # @option attrs [Numeric, String] :stroke_width
57
+ # @option attrs [Array<Integer, String>] :stroke_dash
58
+ # @option attrs ["solid", "dashed", "dotted"] :stroke_type
59
+ # @option attrs [String] :fill
60
+ def ellipse(x, y, rx, ry, attrs = {})
61
+ rx, ry = s2f(rx, ry)
62
+
63
+ with_graphic_styles(attrs) do
64
+ pdf.ellipse(pos(x, y), rx, ry)
65
+ end
66
+ end
67
+
68
+ # @param [Hash] attrs
69
+ def with_graphic_styles(attrs, &block)
70
+ stroke = build_stroke_styles(attrs)
71
+ fill = build_fill_styles(attrs)
72
+
73
+ # Do not draw if no colors given.
74
+ return unless fill || stroke
75
+
76
+ save_graphics_state
77
+
78
+ # Apply stroke-dashed.
79
+ if stroke && stroke[:dash]
80
+ length, space = stroke[:dash]
81
+ pdf.dash(length, space: space)
82
+ end
83
+
84
+ # Draw with fill and stroke.
85
+ if fill && stroke
86
+ pdf.fill_and_stroke do
87
+ line_width(stroke[:width])
88
+ pdf.fill_color(fill[:color])
89
+ pdf.stroke_color(stroke[:color])
90
+ block.call
91
+ end
92
+ # Draw only with fill.
93
+ elsif fill
94
+ pdf.fill do
95
+ pdf.fill_color(fill[:color])
96
+ block.call
97
+ end
98
+ # Draw only with stroke.
99
+ elsif stroke
100
+ pdf.stroke do
101
+ line_width(stroke[:width])
102
+ pdf.stroke_color(stroke[:color])
103
+ block.call
104
+ end
105
+ end
106
+
107
+ restore_graphics_state
108
+ end
109
+
110
+ # @param [Hash] styles
111
+ # @return [Hash, nil]
112
+ def build_stroke_styles(styles)
113
+ color = styles[:stroke]
114
+ width = styles[:stroke_width]
115
+ return nil unless color && color != 'none'
116
+ return nil unless width && width != 0
117
+
118
+ {
119
+ color: parse_color(color),
120
+ width: s2f(width),
121
+ dash: STROKE_DASH[styles[:stroke_type].to_sym]
122
+ }
123
+ end
124
+
125
+ # @param [Hash] styles
126
+ # @return [Hash, nil]
127
+ def build_fill_styles(styles)
128
+ color = styles[:fill]
129
+ return nil unless color && color != 'none'
130
+
131
+ { color: parse_color(color) }
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tempfile'
4
+ require 'base64'
5
+ require 'digest/md5'
6
+
7
+ module Thinreports
8
+ module BasicReport
9
+ module Generator
10
+ class PDF
11
+ module Graphics
12
+ # @param [String, IO] filename_or_io
13
+ # @param [Numeric, Strng] x
14
+ # @param [Numeric, Strng] y
15
+ # @param [Numeric, Strng] w
16
+ # @param [Numeric, Strng] h
17
+ def image(filename_or_io, x, y, w, h)
18
+ w, h = s2f(w, h)
19
+ pdf.image(filename_or_io, at: pos(x, y), width: w, height: h)
20
+ end
21
+
22
+ # @param [String] base64_data
23
+ # @param [Numeric, Strng] x
24
+ # @param [Numeric, Strng] y
25
+ # @param [Numeric, Strng] w
26
+ # @param [Numeric, Strng] h
27
+ def base64image(base64_data, x, y, w, h)
28
+ image_data = Base64.decode64(base64_data)
29
+ image_id = Digest::MD5.hexdigest(base64_data)
30
+ image_path = create_temp_imagefile(image_id, image_data)
31
+
32
+ image(image_path, x, y, w, h)
33
+ end
34
+
35
+ # @param [String, IO] filename_or_io
36
+ # @param [Numeric, Strng] x
37
+ # @param [Numeric, Strng] y
38
+ # @param [Numeric, Strng] w
39
+ # @param [Numeric, Strng] h
40
+ # @param [Hash] options
41
+ # @option options [:left, :center, :right] :position_x (:left)
42
+ # @option options [:top, :center, :bottom] :position_y (:top)
43
+ # @option options [Numeric] :offset_x
44
+ # @option options [Numeric] :offset_y
45
+ def image_box(filename_or_io, x, y, w, h, options = {})
46
+ w, h = s2f(w, h)
47
+
48
+ computed_position = pos(
49
+ x + (options[:offset_x] || 0),
50
+ y + (options[:offset_y] || 0)
51
+ )
52
+ pdf.bounding_box(computed_position, width: w, height: h) do
53
+ pdf.image(
54
+ filename_or_io,
55
+ position: options[:position_x] || :left,
56
+ vposition: options[:position_y] || :top,
57
+ auto_fit: [w, h]
58
+ )
59
+ end
60
+ end
61
+
62
+ def image_dimensions(filename_or_io, x, y, w, h, options = {})
63
+ w, h = s2f(w, h)
64
+ # XXX: Calling @private method
65
+ _pdf_obj, info = pdf.build_image_object(filename_or_io)
66
+ info.calc_image_dimensions(
67
+ position: options[:position_x] || :left,
68
+ vposition: options[:position_y] || :top,
69
+ auto_fit: [w, h]
70
+ )
71
+ end
72
+
73
+ def clean_temp_images
74
+ temp_image_registry.each_value(&:close!)
75
+ temp_image_registry.clear
76
+ end
77
+
78
+ def temp_image_registry
79
+ @temp_image_registry ||= {}
80
+ end
81
+
82
+ # @param [String] image_id
83
+ # @param [String] image_data
84
+ # @return [String] Path to imagefile
85
+ def create_temp_imagefile(image_id, image_data)
86
+ temp_image_registry[image_id] ||= begin
87
+ file = Tempfile.new('temp-image')
88
+ file.binmode
89
+ file.write(image_data)
90
+ file.open
91
+ file
92
+ end
93
+ temp_image_registry[image_id].path
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thinreports
4
+ module BasicReport
5
+ module Generator
6
+ class PDF
7
+ module Graphics
8
+ # @param [String] content
9
+ # @param [Numeric, String] x
10
+ # @param [Numeric, String] y
11
+ # @param [Numeric, String] w
12
+ # @param [Numeric, String] h
13
+ # @param [Hash] attrs ({})
14
+ # @option attrs [String] :font
15
+ # @option attrs [Numeric, String] :size
16
+ # @option attrs [String] :color
17
+ # @option attrs [Array<:bold, :italic, :underline, :strikethrough>]
18
+ # :styles (nil)
19
+ # @option attrs [:left, :center, :right] :align (:left)
20
+ # @option attrs [:top, :center, :bottom] :valign (:top)
21
+ # @option attrs [Numeric, String] :line_height The total height of an text line.
22
+ # @option attrs [Numeric, String] :letter_spacing
23
+ # @option attrs [Boolean] :single (false)
24
+ # @option attrs [:trancate, :shrink_to_fit, :expand] :overflow (:trancate)
25
+ # @option attrs [:none, :break_word] :word_wrap (:none)
26
+ def text_box(content, x, y, w, h, attrs = {}, &block)
27
+ w, h = s2f(w, h)
28
+
29
+ box_attrs = text_box_attrs(
30
+ x, y, w, h,
31
+ single: attrs.delete(:single),
32
+ overflow: attrs[:overflow]
33
+ )
34
+
35
+ # Do not break by word unless :word_wrap is :break_word
36
+ content = text_without_line_wrap(content) if attrs[:word_wrap] == :none
37
+
38
+ with_text_styles(attrs) do |built_attrs, font_styles|
39
+ if block
40
+ block.call [{ text: content, styles: font_styles }],
41
+ built_attrs.merge(box_attrs)
42
+ else
43
+ pdf.formatted_text_box(
44
+ [{ text: content, styles: font_styles }],
45
+ built_attrs.merge(box_attrs)
46
+ )
47
+ end
48
+ end
49
+ rescue Prawn::Errors::CannotFit
50
+ # Nothing to do.
51
+ #
52
+ # When the area is too small compared
53
+ # with the content and the style of the text.
54
+ # (See prawn/core/text/formatted/line_wrap.rb#L185)
55
+ end
56
+
57
+ # @see #text_box
58
+ def text(content, x, y, w, h, attrs = {})
59
+ # Set the :overflow property to :shirink_to_fit.
60
+ text_box(content, x, y, w, h, { overflow: :shirink_to_fit }.merge(attrs))
61
+ end
62
+
63
+ private
64
+
65
+ # @param x (see #text_box)
66
+ # @param y (see #text_box)
67
+ # @param w (see #text_box)
68
+ # @param h (see #text_box)
69
+ # @param [Hash] states
70
+ # @option states [Boolean] :single
71
+ # @option states [Symbold] :overflow
72
+ # @return [Hash]
73
+ def text_box_attrs(x, y, w, h, states = {})
74
+ attrs = {
75
+ at: pos(x, y),
76
+ width: s2f(w)
77
+ }
78
+ if states[:single]
79
+ states[:overflow] != :expand ? attrs.merge(single_line: true) : attrs
80
+ else
81
+ attrs.merge(height: s2f(h))
82
+ end
83
+ end
84
+
85
+ # @param attrs (see #text)
86
+ # @yield [built_attrs, font_styles]
87
+ # @yieldparam [Hash] built_attrs The finalized attributes.
88
+ # @yieldparam [Array] font_styles The finalized styles.
89
+ def with_text_styles(attrs, &block)
90
+ # When no color is given, do not draw.
91
+ return unless attrs.key?(:color) && attrs[:color] != 'none'
92
+
93
+ save_graphics_state
94
+
95
+ fontinfo = {
96
+ name: attrs.delete(:font).to_s,
97
+ color: parse_color(attrs.delete(:color)),
98
+ size: s2f(attrs.delete(:size))
99
+ }
100
+
101
+ # Add the specified value to :leading option.
102
+ line_height = attrs.delete(:line_height)
103
+ if line_height
104
+ attrs[:leading] = text_line_leading(
105
+ s2f(line_height),
106
+ name: fontinfo[:name],
107
+ size: fontinfo[:size]
108
+ )
109
+ end
110
+
111
+ # Set the :character_spacing option.
112
+ spacing = attrs.delete(:letter_spacing)
113
+ attrs[:character_spacing] = s2f(spacing) if spacing
114
+
115
+ # Or... with_font_styles(attrs, fontinfo, &block)
116
+ with_font_styles(attrs, fontinfo) do |modified_attrs, styles|
117
+ block.call(modified_attrs, styles)
118
+ end
119
+
120
+ restore_graphics_state
121
+ end
122
+
123
+ # @param [Numeric] line_height
124
+ # @param [Hash] font
125
+ # @option font [String] :name Name of font.
126
+ # @option font [Numeric] :size Size of font.
127
+ # @return [Numeric]
128
+ def text_line_leading(line_height, font)
129
+ line_height - pdf.font(font[:name], size: font[:size]).height
130
+ end
131
+
132
+ # @param [String] content
133
+ # @return [String]
134
+ def text_without_line_wrap(content)
135
+ content.gsub(/ /, Prawn::Text::NBSP)
136
+ end
137
+
138
+ # @param [Hash] attrs
139
+ # @param [Hash] font
140
+ # @option font [String] :color
141
+ # @option font [Numeric] :size
142
+ # @option font [String] :name
143
+ # @yield [attributes, styles]
144
+ # @yieldparam [Hash] modified_attrs
145
+ # @yieldparam [Array] styles
146
+ def with_font_styles(attrs, font, &block)
147
+ # Building font styles.
148
+ styles = attrs.delete(:styles)
149
+
150
+ if styles
151
+ manual, styles = styles.partition do |style|
152
+ %i[bold italic].include?(style) && !font_has_style?(font[:name], style)
153
+ end
154
+ end
155
+
156
+ # Emulate bold style.
157
+ if manual && manual.include?(:bold)
158
+ pdf.stroke_color(font[:color])
159
+ pdf.line_width(font[:size] * 0.025)
160
+
161
+ # Change rendering mode to :fill_stroke.
162
+ attrs[:mode] = :fill_stroke
163
+ end
164
+
165
+ # Emulate italic style.
166
+ if manual && manual.include?(:italic)
167
+ # FIXME
168
+ # pdf.transformation_matrix(1, 0, 0.26, 1, 0, 0)
169
+ end
170
+
171
+ pdf.font(font[:name], size: font[:size]) do
172
+ pdf.fill_color(font[:color])
173
+ block.call(attrs, styles || [])
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thinreports
4
+ module BasicReport
5
+ module Generator
6
+ class PDF
7
+ module Graphics
8
+ BASE_LINE_WIDTH = 0.9
9
+
10
+ private
11
+
12
+ # Change the default graphic states defined by Prawn.
13
+ def setup_custom_graphic_states
14
+ pdf.line_width(BASE_LINE_WIDTH)
15
+ end
16
+
17
+ # @param [Numeric] width
18
+ def line_width(width)
19
+ pdf.line_width(width * BASE_LINE_WIDTH)
20
+ end
21
+
22
+ # Delegate to Prawn::Document#save_graphic_state
23
+ # @see Prawn::Document#save_graphics_state
24
+ def save_graphics_state
25
+ pdf.save_graphics_state
26
+ end
27
+
28
+ # Delegate to Prawn::Document#restore_graphic_state
29
+ # @see Prawn::Document#restore_graphics_state
30
+ def restore_graphics_state
31
+ pdf.restore_graphics_state
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ require_relative 'graphics/attributes'
40
+ require_relative 'graphics/basic'
41
+ require_relative 'graphics/image'
42
+ require_relative 'graphics/text'
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thinreports
4
+ module BasicReport
5
+ module Generator
6
+ class PDF
7
+ module Page
8
+ JIS_SIZES = {
9
+ 'B4' => [728.5, 1031.8],
10
+ 'B5' => [515.9, 728.5]
11
+ }.freeze
12
+
13
+ # @param [Thinreports::BasicReport::Layout::Format] format
14
+ def start_new_page(format)
15
+ format_id =
16
+ if change_page_format?(format)
17
+ pdf.start_new_page(new_basic_page_options(format))
18
+ @current_page_format = format
19
+
20
+ create_format_stamp(format) unless format_stamp_registry.include?(format.identifier)
21
+ format.identifier
22
+ else
23
+ pdf.start_new_page(new_basic_page_options(current_page_format))
24
+ current_page_format.identifier
25
+ end
26
+
27
+ stamp(format_id.to_s)
28
+ end
29
+
30
+ def start_new_page_for_section_report(format)
31
+ @current_page_format = format
32
+ pdf.start_new_page(new_basic_page_options(current_page_format).merge(
33
+ top_margin: current_page_format.page_margin[0],
34
+ bottom_margin: current_page_format.page_margin[2]
35
+ ))
36
+ end
37
+
38
+ def max_content_height
39
+ pdf.margin_box.height
40
+ end
41
+
42
+ def add_blank_page
43
+ pdf.start_new_page(pdf.page_count.zero? ? { size: 'A4' } : {})
44
+ end
45
+
46
+ private
47
+
48
+ # @return [Thinreports::BasicReport::Layout::Format]
49
+ attr_reader :current_page_format
50
+
51
+ # @param [Thinreports::BasicReport::Layout::Format] new_format
52
+ # @return [Boolean]
53
+ def change_page_format?(new_format)
54
+ !current_page_format ||
55
+ current_page_format.identifier != new_format.identifier
56
+ end
57
+
58
+ # @param [Thinreports::BasicReport::Layout::Format] format
59
+ def create_format_stamp(format)
60
+ create_stamp(format.identifier.to_s) do
61
+ draw_template_items(format.attributes['items'])
62
+ end
63
+ format_stamp_registry << format.identifier
64
+ end
65
+
66
+ # @return [Array]
67
+ def format_stamp_registry
68
+ @format_stamp_registry ||= []
69
+ end
70
+
71
+ # @param [Thinreports::BasicReport::Layout::Format] format
72
+ # @return [Hash]
73
+ def new_basic_page_options(format)
74
+ options = { layout: format.page_orientation.to_sym }
75
+
76
+ options[:size] =
77
+ if format.user_paper_type?
78
+ [format.page_width.to_f, format.page_height.to_f]
79
+ else
80
+ case format.page_paper_type
81
+ # Convert B4(5)_ISO to B4(5)
82
+ when 'B4_ISO', 'B5_ISO'
83
+ format.page_paper_type.delete('_ISO')
84
+ when 'B4', 'B5'
85
+ JIS_SIZES[format.page_paper_type]
86
+ else
87
+ format.page_paper_type
88
+ end
89
+ end
90
+ options
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thinreports
4
+ module BasicReport
5
+ module Generator
6
+ class PDF
7
+ module ParseColor
8
+ # @param [String] color
9
+ # @return [String]
10
+ def parse_color(color)
11
+ color = color.downcase
12
+
13
+ if color =~ /^#?[\da-f]{6}$/
14
+ color.delete('#')
15
+ else
16
+ find_color_from_name(color)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ # Supported only SAFE COLORS.
23
+ SUPPORTED_COLOR_NAMES = {
24
+ 'red' => 'ff0000',
25
+ 'yellow' => 'fff000',
26
+ 'lime' => '00ff00',
27
+ 'aqua' => '00ffff',
28
+ 'blue' => '0000ff',
29
+ 'fuchsia' => 'ff00ff',
30
+ 'maroon' => '800000',
31
+ 'olive' => '808000',
32
+ 'green' => '008800',
33
+ 'teal' => '008080',
34
+ 'navy' => '000080',
35
+ 'purple' => '800080',
36
+ 'black' => '000000',
37
+ 'gray' => '808080',
38
+ 'silver' => 'c0c0c0',
39
+ 'white' => 'ffffff'
40
+ }.freeze
41
+
42
+ def find_color_from_name(name)
43
+ color = SUPPORTED_COLOR_NAMES[name]
44
+ raise Thinreports::BasicReport::Errors::UnsupportedColorName, name unless color
45
+ color
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end