thinreports 0.10.2 → 0.12.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/CONTRIBUTING.md +2 -2
- data/.github/dependabot.yml +11 -0
- data/.github/workflows/test.yml +50 -0
- data/.gitignore +1 -0
- data/CHANGELOG.md +43 -0
- data/Gemfile +2 -1
- data/{MIT-LICENSE → LICENSE.txt} +2 -0
- data/README.md +70 -56
- data/Rakefile +12 -25
- data/gemfiles/prawn-2.2.gemfile +5 -0
- data/gemfiles/prawn-2.3.gemfile +5 -0
- data/gemfiles/prawn-2.4.gemfile +5 -0
- data/lib/thinreports/core/shape/basic/format.rb +5 -0
- data/lib/thinreports/core/shape/image_block.rb +1 -1
- data/lib/thinreports/core/shape/list.rb +1 -1
- data/lib/thinreports/core/shape/page_number.rb +1 -1
- data/lib/thinreports/core/shape/stack_view/format.rb +27 -0
- data/lib/thinreports/core/shape/stack_view/interface.rb +17 -0
- data/lib/thinreports/core/shape/stack_view/internal.rb +22 -0
- data/lib/thinreports/core/shape/stack_view/row_format.rb +39 -0
- data/lib/thinreports/core/shape/stack_view.rb +17 -0
- data/lib/thinreports/core/shape/style/basic.rb +4 -1
- data/lib/thinreports/core/shape/text.rb +1 -1
- data/lib/thinreports/core/shape/text_block/formatter/number.rb +6 -1
- data/lib/thinreports/core/shape/text_block/internal.rb +0 -2
- data/lib/thinreports/core/shape/text_block.rb +1 -1
- data/lib/thinreports/core/shape.rb +2 -0
- data/lib/thinreports/generate.rb +11 -0
- data/lib/thinreports/generator/pdf/document/draw_shape.rb +31 -10
- data/lib/thinreports/generator/pdf/document/draw_template_items.rb +1 -1
- data/lib/thinreports/generator/pdf/document/graphics/image.rb +19 -1
- data/lib/thinreports/generator/pdf/document/graphics/text.rb +10 -5
- data/lib/thinreports/generator/pdf/document/page.rb +12 -0
- data/lib/thinreports/generator/pdf/prawn_ext/width_of.rb +7 -7
- data/lib/thinreports/layout/base.rb +1 -1
- data/lib/thinreports/layout/legacy_schema.rb +1 -0
- data/lib/thinreports/layout/version.rb +1 -1
- data/lib/thinreports/report/internal.rb +2 -0
- data/lib/thinreports/report.rb +2 -2
- data/lib/thinreports/section_report/build.rb +32 -0
- data/lib/thinreports/section_report/builder/item_builder.rb +49 -0
- data/lib/thinreports/section_report/builder/report_builder.rb +82 -0
- data/lib/thinreports/section_report/builder/report_data.rb +13 -0
- data/lib/thinreports/section_report/builder/stack_view_builder.rb +53 -0
- data/lib/thinreports/section_report/builder/stack_view_data.rb +11 -0
- data/lib/thinreports/section_report/generate.rb +26 -0
- data/lib/thinreports/section_report/pdf/render.rb +23 -0
- data/lib/thinreports/section_report/pdf/renderer/draw_item.rb +68 -0
- data/lib/thinreports/section_report/pdf/renderer/group_renderer.rb +57 -0
- data/lib/thinreports/section_report/pdf/renderer/section_height.rb +100 -0
- data/lib/thinreports/section_report/pdf/renderer/section_renderer.rb +39 -0
- data/lib/thinreports/section_report/pdf/renderer/stack_view_renderer.rb +55 -0
- data/lib/thinreports/section_report/pdf/renderer/stack_view_row_renderer.rb +38 -0
- data/lib/thinreports/section_report/schema/loader.rb +28 -0
- data/lib/thinreports/section_report/schema/parser.rb +52 -0
- data/lib/thinreports/section_report/schema/report.rb +30 -0
- data/lib/thinreports/section_report/schema/section.rb +47 -0
- data/lib/thinreports/version.rb +1 -1
- data/lib/thinreports.rb +5 -0
- data/thinreports.gemspec +11 -10
- metadata +48 -150
- data/.travis.yml +0 -16
- data/examples/character_spacing/character_spacing.rb +0 -8
- data/examples/character_spacing/character_spacing.tlf +0 -293
- data/examples/dynamic_image/dynamic_image.rb +0 -31
- data/examples/dynamic_image/dynamic_image.tlf +0 -661
- data/examples/dynamic_image/img200x100.png +0 -0
- data/examples/dynamic_image/img50x50.png +0 -0
- data/examples/dynamic_style/dynamic_style.rb +0 -150
- data/examples/dynamic_style/dynamic_style.tlf +0 -1835
- data/examples/dynamic_style/dynamic_style_in_list.tlf +0 -344
- data/examples/dynamic_style/image.png +0 -0
- data/examples/eudc/eudc.rb +0 -20
- data/examples/eudc/eudc.tlf +0 -180
- data/examples/eudc/eudc.ttf +0 -0
- data/examples/helper.rb +0 -50
- data/examples/hidden_shapes/hidden_shapes.rb +0 -9
- data/examples/hidden_shapes/hidden_shapes.tlf +0 -449
- data/examples/list_events/list_events.rb +0 -32
- data/examples/list_events/list_events.tlf +0 -361
- data/examples/list_manual_generation/list_manual_generation.rb +0 -22
- data/examples/list_manual_generation/list_manual_generation.tlf +0 -132
- data/examples/list_page_number/list_page_number.rb +0 -17
- data/examples/list_page_number/list_page_number.tlf +0 -254
- data/examples/page_number/page_number.rb +0 -33
- data/examples/page_number/page_number.tlf +0 -215
- data/examples/palleted_png/palleted_png.png +0 -0
- data/examples/palleted_png/palleted_png.rb +0 -9
- data/examples/palleted_png/palleted_png.tlf +0 -47
- data/examples/password_setting/password_setting.rb +0 -10
- data/examples/password_setting/password_setting.tlf +0 -45
- data/examples/report_callbacks/report_callbacks.rb +0 -14
- data/examples/report_callbacks/report_callbacks.tlf +0 -147
- data/examples/single_line_tblock/single_line_tblock.rb +0 -13
- data/examples/single_line_tblock/single_line_tblock.tlf +0 -170
- data/examples/tblock_overflow/tblock_overflow.rb +0 -20
- data/examples/tblock_overflow/tblock_overflow.tlf +0 -538
- data/examples/tblock_styles/font_size.tlf +0 -383
- data/examples/tblock_styles/tblock_styles.rb +0 -43
- data/examples/tblock_styles/tblock_styles.tlf +0 -889
- data/examples/text_align/text_align.rb +0 -8
- data/examples/text_align/text_align.tlf +0 -241
- data/examples/typeB_page_size/B4_ISO.tlf +0 -45
- data/examples/typeB_page_size/B4_JIS.tlf +0 -45
- data/examples/typeB_page_size/typeB_page_size.rb +0 -17
- data/examples/word_wrap/word_wrap.rb +0 -26
- data/examples/word_wrap/word_wrap.tlf +0 -297
- data/test/data/font.ttf +0 -0
- data/test/data/image_normal.jpg +0 -0
- data/test/data/image_normal.png +0 -0
- data/test/data/image_normal_jpg_noext +0 -0
- data/test/data/image_normal_png_noext +0 -0
- data/test/data/image_pallete_based.png +0 -0
- data/test/data/legacy_layout/all-items.tlf +0 -1
- data/test/schema_helper.rb +0 -122
- data/test/test_helper.rb +0 -48
- data/test/tmp/.gitkeep +0 -0
- data/test/unit/core/format/test_base.rb +0 -152
- data/test/unit/core/shape/base/test_internal.rb +0 -87
- data/test/unit/core/shape/basic/test_block_format.rb +0 -23
- data/test/unit/core/shape/basic/test_block_interface.rb +0 -29
- data/test/unit/core/shape/basic/test_block_internal.rb +0 -55
- data/test/unit/core/shape/basic/test_format.rb +0 -37
- data/test/unit/core/shape/basic/test_interface.rb +0 -108
- data/test/unit/core/shape/basic/test_internal.rb +0 -55
- data/test/unit/core/shape/image_block/test_interface.rb +0 -24
- data/test/unit/core/shape/image_block/test_internal.rb +0 -31
- data/test/unit/core/shape/list/test_format.rb +0 -111
- data/test/unit/core/shape/list/test_manager.rb +0 -67
- data/test/unit/core/shape/list/test_page.rb +0 -156
- data/test/unit/core/shape/list/test_page_state.rb +0 -31
- data/test/unit/core/shape/list/test_section_format.rb +0 -36
- data/test/unit/core/shape/list/test_section_interface.rb +0 -75
- data/test/unit/core/shape/list/test_section_internal.rb +0 -50
- data/test/unit/core/shape/manager/test_format.rb +0 -38
- data/test/unit/core/shape/manager/test_internal.rb +0 -132
- data/test/unit/core/shape/manager/test_target.rb +0 -140
- data/test/unit/core/shape/page_number/test_format.rb +0 -55
- data/test/unit/core/shape/page_number/test_interface.rb +0 -33
- data/test/unit/core/shape/page_number/test_internal.rb +0 -81
- data/test/unit/core/shape/styles/test_base.rb +0 -191
- data/test/unit/core/shape/styles/test_basic.rb +0 -24
- data/test/unit/core/shape/styles/test_graphic.rb +0 -50
- data/test/unit/core/shape/styles/test_text.rb +0 -97
- data/test/unit/core/shape/text/test_format.rb +0 -44
- data/test/unit/core/shape/text/test_internal.rb +0 -20
- data/test/unit/core/shape/text_block/formatter/test_basic.rb +0 -24
- data/test/unit/core/shape/text_block/formatter/test_datetime.rb +0 -49
- data/test/unit/core/shape/text_block/formatter/test_number.rb +0 -76
- data/test/unit/core/shape/text_block/formatter/test_padding.rb +0 -77
- data/test/unit/core/shape/text_block/test_format.rb +0 -169
- data/test/unit/core/shape/text_block/test_formatter.rb +0 -28
- data/test/unit/core/shape/text_block/test_interface.rb +0 -63
- data/test/unit/core/shape/text_block/test_internal.rb +0 -128
- data/test/unit/core/test_shape.rb +0 -52
- data/test/unit/core/test_utils.rb +0 -68
- data/test/unit/generator/pdf/document/graphics/test_attributes.rb +0 -135
- data/test/unit/generator/pdf/document/graphics/test_basic.rb +0 -46
- data/test/unit/generator/pdf/document/graphics/test_image.rb +0 -46
- data/test/unit/generator/pdf/document/graphics/test_text.rb +0 -171
- data/test/unit/generator/pdf/document/test_font.rb +0 -110
- data/test/unit/generator/pdf/document/test_graphics.rb +0 -42
- data/test/unit/generator/pdf/document/test_page.rb +0 -122
- data/test/unit/generator/pdf/document/test_parse_color.rb +0 -30
- data/test/unit/generator/pdf/prawn_ext/test_calc_image_dimensions.rb +0 -41
- data/test/unit/generator/pdf/prawn_ext/test_width_of.rb +0 -22
- data/test/unit/generator/pdf/test_document.rb +0 -22
- data/test/unit/generator/test_pdf.rb +0 -26
- data/test/unit/layout/test_base.rb +0 -41
- data/test/unit/layout/test_format.rb +0 -100
- data/test/unit/layout/test_legacy_schema.rb +0 -574
- data/test/unit/layout/test_version.rb +0 -26
- data/test/unit/report/test_base.rb +0 -248
- data/test/unit/report/test_internal.rb +0 -206
- data/test/unit/test_config.rb +0 -36
- data/test/unit/test_layout.rb +0 -12
- data/test/unit/test_report.rb +0 -18
@@ -18,6 +18,7 @@ module Thinreports
|
|
18
18
|
when TextBlock::TYPE_NAME then TextBlock
|
19
19
|
when ImageBlock::TYPE_NAME then ImageBlock
|
20
20
|
when List::TYPE_NAME then List
|
21
|
+
when StackView::TYPE_NAME then StackView
|
21
22
|
when Text::TYPE_NAME then Text
|
22
23
|
when PageNumber::TYPE_NAME then PageNumber
|
23
24
|
when *Basic::TYPE_NAMES then Basic
|
@@ -37,4 +38,5 @@ require_relative 'shape/text'
|
|
37
38
|
require_relative 'shape/text_block'
|
38
39
|
require_relative 'shape/image_block'
|
39
40
|
require_relative 'shape/list'
|
41
|
+
require_relative 'shape/stack_view'
|
40
42
|
require_relative 'shape/page_number'
|
@@ -5,20 +5,26 @@ module Thinreports
|
|
5
5
|
class PDF
|
6
6
|
module DrawShape
|
7
7
|
# @param [Thinreports::Core::Shape::TextBlock::Internal] shape
|
8
|
-
|
9
|
-
|
8
|
+
# @param [Numeric] height (nil) It will be used as rendering height if present.
|
9
|
+
# Otherwise, the rendering height is the height of schema.
|
10
|
+
# @param [:truncate, :shrink_to_fit, :expand] overflow (nil) It will be set the overflow attribute if present.
|
11
|
+
def draw_shape_tblock(shape, height: nil, overflow: nil, &block)
|
12
|
+
x, y, w = shape.format.attributes.values_at('x', 'y', 'width')
|
13
|
+
|
14
|
+
h = height || shape.format.attributes['height']
|
10
15
|
|
11
16
|
content = shape.real_value.to_s
|
12
17
|
return if content.empty?
|
13
18
|
|
14
19
|
attrs = build_text_attributes(shape.style.finalized_styles)
|
20
|
+
attrs[:overflow] = overflow if overflow
|
15
21
|
|
16
22
|
unless shape.multiple?
|
17
23
|
content = content.tr("\n", ' ')
|
18
24
|
attrs[:single] = true
|
19
25
|
end
|
20
26
|
|
21
|
-
text_box(content, x, y, w, h, attrs)
|
27
|
+
text_box(content, x, y, w, h, attrs, &block)
|
22
28
|
end
|
23
29
|
|
24
30
|
def draw_shape_pageno(shape, page_no, page_count)
|
@@ -44,6 +50,21 @@ module Thinreports
|
|
44
50
|
style = shape.style.finalized_styles
|
45
51
|
|
46
52
|
image_box(
|
53
|
+
shape.src, x, y, w, h,
|
54
|
+
position_x: image_position_x(style['position-x']),
|
55
|
+
position_y: image_position_y(style['position-y']),
|
56
|
+
offset_x: style['offset-x'],
|
57
|
+
offset_y: style['offset-y']
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
def shape_iblock_dimenions(shape)
|
62
|
+
return nil if blank_value?(shape.src)
|
63
|
+
|
64
|
+
x, y, w, h = shape.format.attributes.values_at('x', 'y', 'width', 'height')
|
65
|
+
style = shape.style.finalized_styles
|
66
|
+
|
67
|
+
image_dimensions(
|
47
68
|
shape.src, x, y, w, h,
|
48
69
|
position_x: image_position_x(style['position-x']),
|
49
70
|
position_y: image_position_y(style['position-y'])
|
@@ -51,10 +72,10 @@ module Thinreports
|
|
51
72
|
end
|
52
73
|
|
53
74
|
# @param [Thinreports::Core::Shape::Text::Internal] shape
|
54
|
-
def draw_shape_text(shape)
|
75
|
+
def draw_shape_text(shape, dheight = 0)
|
55
76
|
x, y, w, h = shape.format.attributes.values_at('x', 'y', 'width', 'height')
|
56
77
|
text(
|
57
|
-
shape.texts.join("\n"), x, y, w, h,
|
78
|
+
shape.texts.join("\n"), x, y, w, h + dheight,
|
58
79
|
build_text_attributes(shape.style.finalized_styles)
|
59
80
|
)
|
60
81
|
end
|
@@ -66,18 +87,18 @@ module Thinreports
|
|
66
87
|
end
|
67
88
|
|
68
89
|
# @param [Thinreports::Core::Shape::Basic::Internal] shape
|
69
|
-
def draw_shape_line(shape)
|
90
|
+
def draw_shape_line(shape, dy1 = 0, dy2 = 0)
|
70
91
|
x1, y1, x2, y2 = shape.format.attributes.values_at('x1', 'y1', 'x2', 'y2')
|
71
|
-
line(x1, y1, x2, y2, build_graphic_attributes(shape.style.finalized_styles))
|
92
|
+
line(x1, y1 + dy1, x2, y2 + dy2, build_graphic_attributes(shape.style.finalized_styles))
|
72
93
|
end
|
73
94
|
|
74
95
|
# @param [Thinreports::Core::Shape::Basic::Internal] shape
|
75
|
-
def draw_shape_rect(shape)
|
96
|
+
def draw_shape_rect(shape, dheight = 0)
|
76
97
|
x, y, w, h = shape.format.attributes.values_at('x', 'y', 'width', 'height')
|
77
98
|
rect_attributes = build_graphic_attributes(shape.style.finalized_styles) do |attrs|
|
78
|
-
attrs[:radius] = shape.format.attributes['
|
99
|
+
attrs[:radius] = shape.format.attributes['border-radius']
|
79
100
|
end
|
80
|
-
rect(x, y, w, h, rect_attributes)
|
101
|
+
rect(x, y, w, h + dheight, rect_attributes)
|
81
102
|
end
|
82
103
|
end
|
83
104
|
end
|
@@ -25,7 +25,7 @@ module Thinreports
|
|
25
25
|
def draw_rect(item_attributes)
|
26
26
|
x, y, w, h = item_attributes.values_at('x', 'y', 'width', 'height')
|
27
27
|
graphic_attributes = build_graphic_attributes(item_attributes['style']) do |attrs|
|
28
|
-
attrs[:radius] = item_attributes['
|
28
|
+
attrs[:radius] = item_attributes['border-radius']
|
29
29
|
end
|
30
30
|
|
31
31
|
rect(x, y, w, h, graphic_attributes)
|
@@ -39,9 +39,16 @@ module Thinreports
|
|
39
39
|
# @param [Hash] options
|
40
40
|
# @option options [:left, :center, :right] :position_x (:left)
|
41
41
|
# @option options [:top, :center, :bottom] :position_y (:top)
|
42
|
+
# @option options [Numeric] :offset_x
|
43
|
+
# @option options [Numeric] :offset_y
|
42
44
|
def image_box(filename_or_io, x, y, w, h, options = {})
|
43
45
|
w, h = s2f(w, h)
|
44
|
-
|
46
|
+
|
47
|
+
computed_position = pos(
|
48
|
+
x + (options[:offset_x] || 0),
|
49
|
+
y + (options[:offset_y] || 0)
|
50
|
+
)
|
51
|
+
pdf.bounding_box(computed_position, width: w, height: h) do
|
45
52
|
pdf.image(
|
46
53
|
filename_or_io,
|
47
54
|
position: options[:position_x] || :left,
|
@@ -51,6 +58,17 @@ module Thinreports
|
|
51
58
|
end
|
52
59
|
end
|
53
60
|
|
61
|
+
def image_dimensions(filename_or_io, x, y, w, h, options = {})
|
62
|
+
w, h = s2f(w, h)
|
63
|
+
# XXX: Calling @private method
|
64
|
+
_pdf_obj, info = pdf.build_image_object(filename_or_io)
|
65
|
+
info.calc_image_dimensions(
|
66
|
+
position: options[:position_x] || :left,
|
67
|
+
vposition: options[:position_y] || :top,
|
68
|
+
auto_fit: [w, h]
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
54
72
|
def clean_temp_images
|
55
73
|
temp_image_registry.each_value do |image_path|
|
56
74
|
File.delete(image_path) if File.exist?(image_path)
|
@@ -22,7 +22,7 @@ module Thinreports
|
|
22
22
|
# @option attrs [Boolean] :single (false)
|
23
23
|
# @option attrs [:trancate, :shrink_to_fit, :expand] :overflow (:trancate)
|
24
24
|
# @option attrs [:none, :break_word] :word_wrap (:none)
|
25
|
-
def text_box(content, x, y, w, h, attrs = {})
|
25
|
+
def text_box(content, x, y, w, h, attrs = {}, &block)
|
26
26
|
w, h = s2f(w, h)
|
27
27
|
|
28
28
|
box_attrs = text_box_attrs(
|
@@ -35,10 +35,15 @@ module Thinreports
|
|
35
35
|
content = text_without_line_wrap(content) if attrs[:word_wrap] == :none
|
36
36
|
|
37
37
|
with_text_styles(attrs) do |built_attrs, font_styles|
|
38
|
-
|
39
|
-
[{ text: content, styles: font_styles }],
|
40
|
-
|
41
|
-
|
38
|
+
if block
|
39
|
+
block.call [{ text: content, styles: font_styles }],
|
40
|
+
built_attrs.merge(box_attrs)
|
41
|
+
else
|
42
|
+
pdf.formatted_text_box(
|
43
|
+
[{ text: content, styles: font_styles }],
|
44
|
+
built_attrs.merge(box_attrs)
|
45
|
+
)
|
46
|
+
end
|
42
47
|
end
|
43
48
|
rescue Prawn::Errors::CannotFit
|
44
49
|
# Nothing to do.
|
@@ -26,6 +26,18 @@ module Thinreports
|
|
26
26
|
stamp(format_id.to_s)
|
27
27
|
end
|
28
28
|
|
29
|
+
def start_new_page_for_section_report(format)
|
30
|
+
@current_page_format = format
|
31
|
+
pdf.start_new_page(new_basic_page_options(current_page_format).merge(
|
32
|
+
top_margin: current_page_format.page_margin[0],
|
33
|
+
bottom_margin: current_page_format.page_margin[2]
|
34
|
+
))
|
35
|
+
end
|
36
|
+
|
37
|
+
def max_content_height
|
38
|
+
pdf.margin_box.height
|
39
|
+
end
|
40
|
+
|
29
41
|
def add_blank_page
|
30
42
|
pdf.start_new_page(pdf.page_count.zero? ? { size: 'A4' } : {})
|
31
43
|
end
|
@@ -4,16 +4,13 @@ module Thinreports
|
|
4
4
|
module Generator
|
5
5
|
module PrawnExt
|
6
6
|
module WidthOf
|
7
|
-
#
|
7
|
+
# This patch fixes the character_spacing effect on text width calculation.
|
8
8
|
#
|
9
|
-
# The original
|
10
|
-
#
|
11
|
-
# Width of Character is 1
|
12
|
-
# Width of Character Space is 1
|
9
|
+
# The original #width_of:
|
13
10
|
#
|
14
11
|
# width_of('abcd') #=> 4 + 4 = 8
|
15
12
|
#
|
16
|
-
#
|
13
|
+
# The #width_of in this patch:
|
17
14
|
#
|
18
15
|
# width_of('abcd') #=> 4 + 3 = 7
|
19
16
|
#
|
@@ -26,4 +23,7 @@ module Thinreports
|
|
26
23
|
end
|
27
24
|
end
|
28
25
|
|
29
|
-
Prawn
|
26
|
+
# Prawn v2.3 and later includes this patch by https://github.com/prawnpdf/prawn/pull/1117.
|
27
|
+
if Prawn::VERSION < '2.3.0'
|
28
|
+
Prawn::Document.prepend Thinreports::Generator::PrawnExt::WidthOf
|
29
|
+
end
|
@@ -104,6 +104,7 @@ module Thinreports
|
|
104
104
|
'width' => attributes['width'].to_f,
|
105
105
|
'height' => attributes['height'].to_f,
|
106
106
|
'display' => display(attributes['x-display']),
|
107
|
+
'border-radius' => attributes['rx'].to_i,
|
107
108
|
'style' => {
|
108
109
|
'border-width' => attributes['stroke-width'].to_f,
|
109
110
|
'border-color' => attributes['stroke'],
|
data/lib/thinreports/report.rb
CHANGED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'schema/loader'
|
4
|
+
require_relative 'builder/report_builder'
|
5
|
+
|
6
|
+
module Thinreports
|
7
|
+
module SectionReport
|
8
|
+
class Build
|
9
|
+
def call(report_params)
|
10
|
+
schema = load_schema(report_params)
|
11
|
+
params = report_params[:params] || {}
|
12
|
+
|
13
|
+
Builder::ReportBuilder.new(schema).build(params)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def load_schema(report_params)
|
19
|
+
loader = Schema::Loader.new
|
20
|
+
|
21
|
+
case
|
22
|
+
when report_params[:layout_file]
|
23
|
+
loader.load_from_file(report_params[:layout_file])
|
24
|
+
when report_params[:layout_data]
|
25
|
+
loader.load_from_data(report_params[:layout_data])
|
26
|
+
else
|
27
|
+
raise Errors::LayoutFileNotFound
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'stack_view_builder'
|
4
|
+
|
5
|
+
module Thinreports
|
6
|
+
module SectionReport
|
7
|
+
module Builder
|
8
|
+
class ItemBuilder
|
9
|
+
Context = Struct.new(:parent_schema)
|
10
|
+
|
11
|
+
def initialize(item_schema, parent_schema)
|
12
|
+
@item = Core::Shape::Interface(nil, item_schema)
|
13
|
+
@parent_schema = parent_schema
|
14
|
+
end
|
15
|
+
|
16
|
+
def build(item_params)
|
17
|
+
params = build_params(item_params)
|
18
|
+
|
19
|
+
item.visible(params[:display]) if params.key?(:display)
|
20
|
+
item.value(params[:value]) if params.key?(:value)
|
21
|
+
item.styles(params[:styles]) if params.key?(:styles)
|
22
|
+
|
23
|
+
if item.internal.format.attributes['type'] == Core::Shape::StackView::TYPE_NAME
|
24
|
+
StackViewBuilder.new(item).update(params)
|
25
|
+
end
|
26
|
+
|
27
|
+
item
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :item, :parent_schema
|
33
|
+
|
34
|
+
def build_params(params)
|
35
|
+
return {} unless params
|
36
|
+
|
37
|
+
case params
|
38
|
+
when Hash
|
39
|
+
params
|
40
|
+
when Proc
|
41
|
+
params.call(Context.new(parent_schema))
|
42
|
+
else
|
43
|
+
{ value: params }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'report_data'
|
4
|
+
require_relative 'item_builder'
|
5
|
+
|
6
|
+
module Thinreports
|
7
|
+
module SectionReport
|
8
|
+
module Builder
|
9
|
+
class ReportBuilder
|
10
|
+
def initialize(schema)
|
11
|
+
@schema = schema
|
12
|
+
end
|
13
|
+
|
14
|
+
def build(params)
|
15
|
+
ReportData::Main.new(
|
16
|
+
schema,
|
17
|
+
build_groups(params[:groups])
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :schema
|
24
|
+
|
25
|
+
def build_groups(groups_params)
|
26
|
+
return [] unless groups_params
|
27
|
+
|
28
|
+
groups_params.map do |group_params|
|
29
|
+
ReportData::Group.new(
|
30
|
+
build_sections(:header, group_params[:headers] || {}),
|
31
|
+
build_detail_sections(group_params[:details] || []),
|
32
|
+
build_sections(:footer, group_params[:footers] || {})
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_sections(section_type, sections_params)
|
38
|
+
sections_schemas =
|
39
|
+
case section_type
|
40
|
+
when :header then schema.headers
|
41
|
+
when :footer then schema.footers
|
42
|
+
end
|
43
|
+
|
44
|
+
sections_schemas.each_with_object([]) do |(section_id, section_schema), sections|
|
45
|
+
section_params = sections_params[section_id.to_sym] || {}
|
46
|
+
next unless section_enabled?(section_schema, section_params)
|
47
|
+
|
48
|
+
items = build_items(section_schema, section_params[:items] || {})
|
49
|
+
sections << ReportData::Section.new(section_schema, items, section_params[:min_height])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def build_detail_sections(details_params)
|
54
|
+
details_params.each_with_object([]) do |detail_params, details|
|
55
|
+
detail_id = detail_params[:id].to_sym
|
56
|
+
detail_schema = schema.details[detail_id]
|
57
|
+
|
58
|
+
next unless detail_schema
|
59
|
+
|
60
|
+
items = build_items(detail_schema, detail_params[:items] || {})
|
61
|
+
details << ReportData::Section.new(detail_schema, items, detail_params[:min_height])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def build_items(section_schema, items_params)
|
66
|
+
section_schema.items.each_with_object([]) do |item_schema, items|
|
67
|
+
item = ItemBuilder.new(item_schema, section_schema).build(items_params[item_schema.id&.to_sym])
|
68
|
+
items << item if item.visible?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def section_enabled?(section_schema, section_params)
|
73
|
+
if section_params.key?(:display)
|
74
|
+
section_params[:display]
|
75
|
+
else
|
76
|
+
section_schema.display?
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Thinreports
|
4
|
+
module SectionReport
|
5
|
+
module Builder
|
6
|
+
module ReportData
|
7
|
+
Main = Struct.new :schema, :groups
|
8
|
+
Group = Struct.new :headers, :details, :footers
|
9
|
+
Section = Struct.new :schema, :items, :min_height
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'stack_view_data'
|
4
|
+
|
5
|
+
module Thinreports
|
6
|
+
module SectionReport
|
7
|
+
module Builder
|
8
|
+
class StackViewBuilder
|
9
|
+
def initialize(item)
|
10
|
+
@item = item
|
11
|
+
end
|
12
|
+
|
13
|
+
def update(params)
|
14
|
+
rows_params = params[:rows] || {}
|
15
|
+
rows_schema = item.internal.format.rows
|
16
|
+
|
17
|
+
rows = []
|
18
|
+
rows_schema.each do |row_schema|
|
19
|
+
row_params = rows_params[row_schema.id.to_sym] || {}
|
20
|
+
next unless row_enabled?(row_schema, row_params)
|
21
|
+
|
22
|
+
items = build_row_items(
|
23
|
+
row_schema,
|
24
|
+
row_params[:items] || {}
|
25
|
+
)
|
26
|
+
|
27
|
+
rows << StackViewData::Row.new(row_schema, items, row_params[:min_height])
|
28
|
+
end
|
29
|
+
item.internal.rows = rows
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :item
|
35
|
+
|
36
|
+
def build_row_items(row_schema, items_params)
|
37
|
+
row_schema.items.each_with_object([]) do |item_schema, items|
|
38
|
+
item = ItemBuilder.new(item_schema, row_schema).build(items_params[item_schema.id&.to_sym])
|
39
|
+
items << item if item.visible?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def row_enabled?(row_schema, row_params)
|
44
|
+
if row_params.key?(:display)
|
45
|
+
row_params[:display]
|
46
|
+
else
|
47
|
+
row_schema.display?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'build'
|
4
|
+
require_relative 'pdf/render'
|
5
|
+
|
6
|
+
module Thinreports
|
7
|
+
module SectionReport
|
8
|
+
class Generate
|
9
|
+
def initialize
|
10
|
+
@pdf = Thinreports::Generator::PDF::Document.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(report_params, filename: nil)
|
14
|
+
report = Build.new.call(report_params)
|
15
|
+
|
16
|
+
PDF::Render.new(pdf).call!(report)
|
17
|
+
|
18
|
+
filename ? pdf.render_file(filename) : pdf.render
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :pdf
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'renderer/group_renderer'
|
4
|
+
|
5
|
+
module Thinreports
|
6
|
+
module SectionReport
|
7
|
+
module PDF
|
8
|
+
class Render
|
9
|
+
def initialize(pdf)
|
10
|
+
@group_renderer = Renderer::GroupRenderer.new(pdf)
|
11
|
+
end
|
12
|
+
|
13
|
+
def call!(report)
|
14
|
+
report.groups.each { |group| group_renderer.render(report, group) }
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :group_renderer
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Thinreports
|
4
|
+
module SectionReport
|
5
|
+
module Renderer
|
6
|
+
module DrawItem
|
7
|
+
def draw_item(item, expanded_height = 0)
|
8
|
+
shape = item.internal
|
9
|
+
|
10
|
+
if shape.type_of?(Core::Shape::TextBlock::TYPE_NAME)
|
11
|
+
computed_height = shape.format.attributes['height']
|
12
|
+
computed_height += expanded_height if shape.format.follow_stretch == 'height'
|
13
|
+
|
14
|
+
if shape.style.finalized_styles['overflow'] == 'expand'
|
15
|
+
# When overflow is "expand", the value of the height argument is ignored and the shape is expanded to
|
16
|
+
# the bottom of the outer bounding box.
|
17
|
+
# That causes a position shift problem if vertical-align is "middle" or "bottom".
|
18
|
+
# To solve it, we overwrite the overflow to "truncate" when drawing.
|
19
|
+
# To emulate the "expand" behavior in the "truncate" mode,
|
20
|
+
# here we pass the greater value of the computed_height and the text height as text block height.
|
21
|
+
pdf.draw_shape_tblock(shape, height: [computed_height, calc_text_block_height(shape)].max, overflow: :truncate)
|
22
|
+
else
|
23
|
+
pdf.draw_shape_tblock(shape, height: computed_height)
|
24
|
+
end
|
25
|
+
elsif shape.type_of?(Core::Shape::ImageBlock::TYPE_NAME)
|
26
|
+
pdf.draw_shape_iblock(shape)
|
27
|
+
elsif shape.type_of?('text')
|
28
|
+
case shape.format.follow_stretch
|
29
|
+
when 'height'
|
30
|
+
pdf.draw_shape_text(shape, expanded_height)
|
31
|
+
else
|
32
|
+
pdf.draw_shape_text(shape)
|
33
|
+
end
|
34
|
+
elsif shape.type_of?('image')
|
35
|
+
pdf.draw_shape_image(shape)
|
36
|
+
elsif shape.type_of?('ellipse')
|
37
|
+
pdf.draw_shape_ellipse(shape)
|
38
|
+
elsif shape.type_of?('rect')
|
39
|
+
case shape.format.follow_stretch
|
40
|
+
when 'height'
|
41
|
+
pdf.draw_shape_rect(shape, expanded_height)
|
42
|
+
else
|
43
|
+
pdf.draw_shape_rect(shape)
|
44
|
+
end
|
45
|
+
elsif shape.type_of?('line')
|
46
|
+
case shape.format.follow_stretch
|
47
|
+
when 'height'
|
48
|
+
y1, y2 = shape.format.attributes.values_at('y1', 'y2')
|
49
|
+
if y1 < y2
|
50
|
+
pdf.draw_shape_line(shape, 0, expanded_height)
|
51
|
+
else
|
52
|
+
pdf.draw_shape_line(shape, expanded_height, 0)
|
53
|
+
end
|
54
|
+
when 'y'
|
55
|
+
pdf.draw_shape_line(shape, expanded_height, expanded_height)
|
56
|
+
else
|
57
|
+
pdf.draw_shape_line(shape)
|
58
|
+
end
|
59
|
+
elsif shape.type_of?(Core::Shape::StackView::TYPE_NAME)
|
60
|
+
stack_view_renderer.render(shape)
|
61
|
+
else
|
62
|
+
raise Thinreports::Errors::UnknownShapeType
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|