thinreports 0.11.0 → 0.12.0
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/workflows/test.yml +17 -5
- data/CHANGELOG.md +12 -1
- data/Dockerfile +1 -1
- data/Gemfile +0 -4
- data/README.md +4 -2
- 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.rb +5 -0
- data/lib/thinreports/core/shape.rb +2 -0
- data/lib/thinreports/core/shape/basic/format.rb +5 -0
- data/lib/thinreports/core/shape/stack_view.rb +17 -0
- 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/style/basic.rb +4 -1
- data/lib/thinreports/generate.rb +11 -0
- data/lib/thinreports/generator/pdf/document/draw_shape.rb +30 -9
- 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 -13
- 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 +55 -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/headers_renderer.rb +24 -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/thinreports.gemspec +8 -2
- metadata +103 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9713f8331c7d6c97d17f0b30ba84124148f48ed40d3fde29f884503146415ed0
|
4
|
+
data.tar.gz: 0617cb86495f89090b6f751334aed7fd69c42b79357683992ac65d3ee0dc72e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 37bd09438b27f1147c9803b0236bac461c0d5c08844bff920e7a183bd3f09912f157bf66c6ad246ece7a8c8041fd559a18495c56a22550459e15e357b60355c9
|
7
|
+
data.tar.gz: 43fd24534374c1c63162cece99c12622c5bdab0fce12c9f8c7b9e82d985ec50fc0b1fa388f1b67658ae3892e6b9d97350fada88ef6c48021c4dedecff128e910
|
data/.github/CONTRIBUTING.md
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
|
2
|
-
If you have a support question about how to use thinreports, please [ask on
|
1
|
+
We only accept bug reports and pull requests in GitHub.
|
2
|
+
If you have a support question about how to use thinreports, please [ask on GitHub Discussions](https://github.com/thinreports/thinreports/discussions).
|
data/.github/workflows/test.yml
CHANGED
@@ -1,19 +1,28 @@
|
|
1
1
|
name: Test
|
2
2
|
|
3
|
-
on: push
|
3
|
+
on: [push, pull_request]
|
4
4
|
|
5
5
|
jobs:
|
6
6
|
test:
|
7
|
-
name: Test against ${{ matrix.ruby }}
|
7
|
+
name: Test against ruby ${{ matrix.ruby }} and prawn ${{ matrix.prawn }}
|
8
8
|
runs-on: ubuntu-latest
|
9
9
|
strategy:
|
10
10
|
matrix:
|
11
11
|
ruby:
|
12
|
-
- 2.4
|
13
12
|
- 2.5
|
14
13
|
- 2.6
|
15
14
|
- 2.7
|
15
|
+
- 3.0
|
16
16
|
- jruby
|
17
|
+
prawn:
|
18
|
+
- 2.2
|
19
|
+
- 2.3
|
20
|
+
- 2.4
|
21
|
+
exclude:
|
22
|
+
- ruby: 3.0
|
23
|
+
prawn: 2.2
|
24
|
+
- ruby: 3.0
|
25
|
+
prawn: 2.3
|
17
26
|
|
18
27
|
steps:
|
19
28
|
- uses: actions/checkout@v1
|
@@ -31,9 +40,12 @@ jobs:
|
|
31
40
|
with:
|
32
41
|
ruby-version: ${{ matrix.ruby }}
|
33
42
|
|
34
|
-
- name:
|
43
|
+
- name: Install dependencies
|
35
44
|
run: |
|
36
45
|
gem install bundler
|
37
|
-
bundle install --jobs 4 --retry 3
|
46
|
+
bundle install --gemfile gemfiles/prawn-${{ matrix.prawn }}.gemfile --jobs 4 --retry 3
|
47
|
+
|
48
|
+
- name: Run tests
|
49
|
+
run: |
|
38
50
|
bundle exec rake test:units
|
39
51
|
bundle exec rake test:features
|
data/CHANGELOG.md
CHANGED
data/Dockerfile
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -15,11 +15,13 @@
|
|
15
15
|
* [Quick Start Guide](http://www.thinreports.org/documentation/getting-started/quickstart.html)
|
16
16
|
* [Examples](https://github.com/thinreports/thinreports-examples)
|
17
17
|
* [Changelog](https://github.com/thinreports/thinreports-generator/blob/master/CHANGELOG.md)
|
18
|
-
* [Discussion Group](https://
|
18
|
+
* [Discussion Group (GitHub Discussions)](https://github.com/thinreports/thinreports/discussions)
|
19
|
+
* [Discussion Group (Google Groups)](https://groups.google.com/forum/#!forum/thinreports)
|
19
20
|
|
20
21
|
## Supported versions
|
21
22
|
|
22
|
-
* Ruby 2.
|
23
|
+
* Ruby 2.5, 2.6, 2.7, 3.0
|
24
|
+
* Prawn 2.2, 2.3, 2.4
|
23
25
|
* JRuby 9.2
|
24
26
|
|
25
27
|
## Quick Reference
|
data/lib/thinreports.rb
CHANGED
@@ -6,6 +6,10 @@ module Thinreports
|
|
6
6
|
def self.root
|
7
7
|
@root ||= Pathname.new(__FILE__).join('..', '..')
|
8
8
|
end
|
9
|
+
|
10
|
+
def self.generate(report_params, filename: nil)
|
11
|
+
Generate.new.call(report_params, filename: filename)
|
12
|
+
end
|
9
13
|
end
|
10
14
|
|
11
15
|
require_relative 'thinreports/version'
|
@@ -18,3 +22,4 @@ require_relative 'thinreports/core/utils'
|
|
18
22
|
require_relative 'thinreports/report'
|
19
23
|
require_relative 'thinreports/layout'
|
20
24
|
require_relative 'thinreports/generator/pdf'
|
25
|
+
require_relative 'thinreports/generate'
|
@@ -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'
|
@@ -10,6 +10,11 @@ module Thinreports
|
|
10
10
|
config_reader :type, :id
|
11
11
|
config_reader :style
|
12
12
|
config_checker true, :display
|
13
|
+
config_reader follow_stretch: %w[follow-stretch]
|
14
|
+
|
15
|
+
def affect_bottom_margin?
|
16
|
+
attributes.fetch('affect-bottom-margin', true)
|
17
|
+
end
|
13
18
|
end
|
14
19
|
end
|
15
20
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Thinreports
|
4
|
+
module Core
|
5
|
+
module Shape
|
6
|
+
module StackView
|
7
|
+
TYPE_NAME = 'stack-view'.freeze
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
require_relative 'stack_view/format'
|
14
|
+
require_relative 'stack_view/interface'
|
15
|
+
require_relative 'stack_view/internal'
|
16
|
+
|
17
|
+
require_relative 'stack_view/row_format'
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Thinreports
|
4
|
+
module Core
|
5
|
+
module Shape
|
6
|
+
module StackView
|
7
|
+
class Format < Basic::Format
|
8
|
+
attr_reader :rows
|
9
|
+
|
10
|
+
def initialize(*)
|
11
|
+
super
|
12
|
+
initialize_rows
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def initialize_rows
|
18
|
+
@rows = []
|
19
|
+
attributes['rows'].each do |row|
|
20
|
+
@rows << StackView::RowFormat.new(row)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Thinreports
|
4
|
+
module Core
|
5
|
+
module Shape
|
6
|
+
module StackView
|
7
|
+
class Interface < Basic::Interface
|
8
|
+
private
|
9
|
+
|
10
|
+
def init_internal(parent, format)
|
11
|
+
StackView::Internal.new(parent, format)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Thinreports
|
4
|
+
module Core
|
5
|
+
module Shape
|
6
|
+
module StackView
|
7
|
+
class Internal < Basic::Internal
|
8
|
+
def initialize(parent, format)
|
9
|
+
super
|
10
|
+
@rows = []
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_accessor :rows
|
14
|
+
|
15
|
+
def type_of?(type_name)
|
16
|
+
type_name == StackView::TYPE_NAME
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Thinreports
|
4
|
+
module Core
|
5
|
+
module Shape
|
6
|
+
module StackView
|
7
|
+
class RowFormat < Core::Format::Base
|
8
|
+
config_reader :id
|
9
|
+
config_reader :height
|
10
|
+
config_checker true, :display
|
11
|
+
config_checker true, auto_stretch: 'auto-stretch'
|
12
|
+
|
13
|
+
attr_reader :items
|
14
|
+
|
15
|
+
def initialize(*)
|
16
|
+
super
|
17
|
+
@items = []
|
18
|
+
@item_with_ids = {}
|
19
|
+
initialize_items(attributes['items'])
|
20
|
+
end
|
21
|
+
|
22
|
+
def find_item(id)
|
23
|
+
@item_with_ids[id.to_sym]
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def initialize_items(item_schemas)
|
29
|
+
item_schemas.each do |item_schema|
|
30
|
+
item = Core::Shape::Format(item_schema['type']).new(item_schema)
|
31
|
+
@items << item
|
32
|
+
@item_with_ids[item.id.to_sym] = item unless item.id.empty?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -5,9 +5,12 @@ module Thinreports
|
|
5
5
|
module Shape
|
6
6
|
module Style
|
7
7
|
class Basic < Style::Base
|
8
|
-
style_accessible :visible
|
8
|
+
style_accessible :visible, :offset_x, :offset_y
|
9
9
|
attr_accessor :visible
|
10
10
|
|
11
|
+
style_accessor :offset_x, 'offset-x'
|
12
|
+
style_accessor :offset_y, 'offset-y'
|
13
|
+
|
11
14
|
def initialize(*args)
|
12
15
|
super
|
13
16
|
@visible = @format.display?
|
@@ -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
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
|
@@ -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)
|