thinreports 0.10.2 → 0.12.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.
- 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
|