thinreports 0.8.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/CONTRIBUTING.md +2 -0
- data/.travis.yml +3 -3
- data/CHANGELOG.md +98 -1
- data/README.md +0 -9
- data/Rakefile +1 -2
- data/examples/character_spacing/character_spacing.tlf +293 -1
- data/examples/dynamic_image/dynamic_image.tlf +661 -1
- data/examples/dynamic_style/dynamic_style.rb +4 -4
- data/examples/dynamic_style/dynamic_style.tlf +1835 -1
- data/examples/dynamic_style/dynamic_style_in_list.tlf +344 -1
- data/examples/eudc/eudc.tlf +180 -1
- data/examples/hidden_shapes/hidden_shapes.tlf +449 -1
- data/examples/list_events/list_events.rb +1 -28
- data/examples/list_events/list_events.tlf +361 -0
- data/examples/list_manual_generation/list_manual_generation.tlf +132 -1
- data/examples/list_page_number/list_page_number.tlf +254 -1
- data/examples/page_number/page_number.tlf +215 -1
- data/examples/palleted_png/palleted_png.rb +2 -0
- data/examples/palleted_png/palleted_png.tlf +47 -1
- data/examples/password_setting/password_setting.tlf +45 -1
- data/examples/report_callbacks/report_callbacks.rb +1 -18
- data/examples/report_callbacks/report_callbacks.tlf +147 -1
- data/examples/single_line_tblock/single_line_tblock.tlf +170 -1
- data/examples/tblock_overflow/tblock_overflow.tlf +538 -1
- data/examples/tblock_styles/font_size.tlf +383 -1
- data/examples/tblock_styles/tblock_styles.tlf +889 -1
- data/examples/text_align/text_align.tlf +241 -1
- data/examples/typeB_page_size/B4_ISO.tlf +45 -1
- data/examples/typeB_page_size/B4_JIS.tlf +45 -1
- data/examples/word_wrap/word_wrap.tlf +297 -1
- data/lib/thinreports.rb +0 -1
- data/lib/thinreports/config.rb +3 -0
- data/lib/thinreports/core/errors.rb +0 -3
- data/lib/thinreports/core/format/base.rb +4 -4
- data/lib/thinreports/core/shape.rb +1 -9
- data/lib/thinreports/core/shape/basic.rb +3 -3
- data/lib/thinreports/core/shape/basic/block_format.rb +1 -1
- data/lib/thinreports/core/shape/basic/format.rb +2 -14
- data/lib/thinreports/core/shape/basic/internal.rb +2 -2
- data/lib/thinreports/core/shape/image_block.rb +3 -3
- data/lib/thinreports/core/shape/image_block/format.rb +0 -2
- data/lib/thinreports/core/shape/image_block/internal.rb +1 -1
- data/lib/thinreports/core/shape/list.rb +3 -7
- data/lib/thinreports/core/shape/list/format.rb +37 -24
- data/lib/thinreports/core/shape/list/manager.rb +1 -36
- data/lib/thinreports/core/shape/list/page.rb +0 -15
- data/lib/thinreports/core/shape/list/page_state.rb +1 -1
- data/lib/thinreports/core/shape/list/section_format.rb +14 -12
- data/lib/thinreports/core/shape/list/section_internal.rb +4 -7
- data/lib/thinreports/core/shape/manager/format.rb +0 -2
- data/lib/thinreports/core/shape/manager/internal.rb +1 -1
- data/lib/thinreports/core/shape/page_number.rb +1 -1
- data/lib/thinreports/core/shape/page_number/format.rb +9 -6
- data/lib/thinreports/core/shape/page_number/interface.rb +1 -1
- data/lib/thinreports/core/shape/page_number/internal.rb +2 -6
- data/lib/thinreports/core/shape/style/base.rb +11 -13
- data/lib/thinreports/core/shape/style/basic.rb +4 -4
- data/lib/thinreports/core/shape/style/graphic.rb +4 -10
- data/lib/thinreports/core/shape/style/text.rb +36 -69
- data/lib/thinreports/core/shape/text.rb +3 -3
- data/lib/thinreports/core/shape/text/format.rb +3 -13
- data/lib/thinreports/core/shape/text/internal.rb +2 -2
- data/lib/thinreports/core/shape/text_block.rb +3 -3
- data/lib/thinreports/core/shape/text_block/format.rb +9 -6
- data/lib/thinreports/core/shape/text_block/interface.rb +7 -7
- data/lib/thinreports/core/shape/text_block/internal.rb +1 -1
- data/lib/thinreports/generator/pdf.rb +0 -1
- data/lib/thinreports/generator/pdf/document.rb +2 -2
- data/lib/thinreports/generator/pdf/document/draw_shape.rb +25 -51
- data/lib/thinreports/generator/pdf/document/draw_template_items.rb +66 -0
- data/lib/thinreports/generator/pdf/document/font.rb +3 -8
- data/lib/thinreports/generator/pdf/document/graphics/attributes.rb +97 -77
- data/lib/thinreports/generator/pdf/document/graphics/basic.rb +18 -13
- data/lib/thinreports/generator/pdf/document/graphics/image.rb +17 -12
- data/lib/thinreports/generator/pdf/document/page.rb +1 -1
- data/lib/thinreports/generator/pdf/drawer/list.rb +5 -1
- data/lib/thinreports/generator/pdf/drawer/list_section.rb +1 -4
- data/lib/thinreports/generator/pdf/drawer/page.rb +9 -9
- data/lib/thinreports/layout.rb +3 -3
- data/lib/thinreports/layout/base.rb +0 -16
- data/lib/thinreports/layout/format.rb +38 -57
- data/lib/thinreports/layout/legacy_schema.rb +354 -0
- data/lib/thinreports/layout/version.rb +1 -1
- data/lib/thinreports/report.rb +0 -1
- data/lib/thinreports/report/base.rb +3 -17
- data/lib/thinreports/report/internal.rb +13 -55
- data/lib/thinreports/report/page.rb +1 -1
- data/lib/thinreports/version.rb +1 -1
- data/test/data/legacy_layout/all-items.tlf +1 -0
- data/test/schema_helper.rb +121 -0
- data/test/test_helper.rb +13 -50
- data/test/unit/core/format/test_base.rb +35 -31
- data/test/unit/core/shape/base/test_internal.rb +1 -1
- data/test/unit/core/shape/basic/test_block_format.rb +23 -0
- data/test/unit/core/shape/basic/{test_basic_interface.rb → test_block_interface.rb} +1 -1
- data/test/unit/core/shape/basic/{test_basic_internal.rb → test_block_internal.rb} +4 -7
- data/test/unit/core/shape/basic/test_format.rb +25 -47
- data/test/unit/core/shape/basic/test_interface.rb +5 -5
- data/test/unit/core/shape/basic/test_internal.rb +6 -11
- data/test/unit/core/shape/image_block/test_interface.rb +5 -5
- data/test/unit/core/shape/image_block/test_internal.rb +2 -2
- data/test/unit/core/shape/list/test_format.rb +86 -44
- data/test/unit/core/shape/list/test_manager.rb +10 -65
- data/test/unit/core/shape/list/test_page.rb +10 -61
- data/test/unit/core/shape/list/test_page_state.rb +1 -1
- data/test/unit/core/shape/list/test_section_format.rb +19 -28
- data/test/unit/core/shape/list/test_section_interface.rb +14 -6
- data/test/unit/core/shape/list/test_section_internal.rb +17 -23
- data/test/unit/core/shape/manager/test_format.rb +1 -1
- data/test/unit/core/shape/manager/test_internal.rb +59 -98
- data/test/unit/core/shape/manager/test_target.rb +21 -30
- data/test/unit/core/shape/page_number/test_format.rb +34 -52
- data/test/unit/core/shape/page_number/test_interface.rb +1 -1
- data/test/unit/core/shape/page_number/test_internal.rb +2 -2
- data/test/unit/core/shape/styles/test_base.rb +16 -41
- data/test/unit/core/shape/styles/test_basic.rb +7 -7
- data/test/unit/core/shape/styles/test_graphic.rb +27 -32
- data/test/unit/core/shape/styles/test_text.rb +68 -311
- data/test/unit/core/shape/text/test_format.rb +30 -59
- data/test/unit/core/shape/text/test_internal.rb +5 -5
- data/test/unit/core/shape/text_block/formatter/test_datetime.rb +13 -13
- data/test/unit/core/shape/text_block/test_format.rb +145 -146
- data/test/unit/core/shape/text_block/test_interface.rb +9 -9
- data/test/unit/core/shape/text_block/test_internal.rb +9 -9
- data/test/unit/core/test_shape.rb +19 -31
- data/test/unit/generator/pdf/document/graphics/test_attributes.rb +112 -122
- data/test/unit/generator/pdf/document/graphics/test_basic.rb +45 -0
- data/test/unit/generator/pdf/document/graphics/test_image.rb +5 -5
- data/test/unit/generator/pdf/document/test_font.rb +4 -10
- data/test/unit/generator/pdf/document/test_page.rb +13 -11
- data/test/unit/generator/pdf/document/test_parse_color.rb +5 -5
- data/test/unit/generator/test_base.rb +5 -5
- data/test/unit/generator/test_configuration.rb +4 -8
- data/test/unit/generator/test_pdf.rb +3 -2
- data/test/unit/layout/test_base.rb +16 -22
- data/test/unit/layout/test_format.rb +66 -86
- data/test/unit/layout/test_legacy_schema.rb +587 -0
- data/test/unit/layout/test_version.rb +20 -20
- data/test/unit/report/test_base.rb +32 -45
- data/test/unit/report/test_internal.rb +43 -99
- data/test/unit/test_config.rb +4 -2
- data/test/unit/test_layout.rb +1 -1
- metadata +20 -48
- data/examples/list_events/list_events_0_7_7.tlf +0 -1
- data/examples/list_events/list_events_0_8.tlf +0 -1
- data/lib/thinreports/core/events.rb +0 -69
- data/lib/thinreports/core/format/builder.rb +0 -62
- data/lib/thinreports/core/shape/list/configuration.rb +0 -54
- data/lib/thinreports/core/shape/list/events.rb +0 -47
- data/lib/thinreports/core/shape/list/store.rb +0 -33
- data/lib/thinreports/generator/pdf/configuration.rb +0 -33
- data/lib/thinreports/generator/pdf/document/parse_svg.rb +0 -104
- data/lib/thinreports/layout/configuration.rb +0 -29
- data/test/data/layout_block.tlf +0 -1
- data/test/data/layout_list.tlf +0 -1
- data/test/data/layout_list_noheader.tlf +0 -1
- data/test/data/layout_text1.tlf +0 -1
- data/test/data/layout_text2.tlf +0 -1
- data/test/unit/core/format/test_builder.rb +0 -109
- data/test/unit/core/shape/basic/test_basic_format.rb +0 -30
- data/test/unit/core/shape/image_block/test_format.rb +0 -58
- data/test/unit/core/shape/list/test_configuration.rb +0 -69
- data/test/unit/core/shape/list/test_events.rb +0 -36
- data/test/unit/core/shape/list/test_store.rb +0 -41
- data/test/unit/core/test_events.rb +0 -93
- data/test/unit/generator/pdf/document/test_draw_shape.rb +0 -44
- data/test/unit/generator/pdf/test_configuration.rb +0 -31
- data/test/unit/layout/test_configuration.rb +0 -21
- data/test/unit/report/test_events.rb +0 -22
|
@@ -4,6 +4,11 @@ module Thinreports
|
|
|
4
4
|
module Generator
|
|
5
5
|
|
|
6
6
|
module PDF::Graphics
|
|
7
|
+
STROKE_DASH = {
|
|
8
|
+
dashed: [2, 2],
|
|
9
|
+
dotted: [1, 2]
|
|
10
|
+
}
|
|
11
|
+
|
|
7
12
|
# @param [Numeric, String] x1
|
|
8
13
|
# @param [Numeric, String] y1
|
|
9
14
|
# @param [Numeric, String] x2
|
|
@@ -11,7 +16,7 @@ module Thinreports
|
|
|
11
16
|
# @param [Hash] attrs ({})
|
|
12
17
|
# @option attrs [String] :stroke
|
|
13
18
|
# @option attrs [Numeric, String] :stroke_width
|
|
14
|
-
# @option attrs [
|
|
19
|
+
# @option attrs ["solid", "dashed", "dotted"] :stroke_type
|
|
15
20
|
def line(x1, y1, x2, y2, attrs = {})
|
|
16
21
|
with_graphic_styles(attrs) do
|
|
17
22
|
pdf.line(pos(x1, y1), pos(x2, y2))
|
|
@@ -26,7 +31,7 @@ module Thinreports
|
|
|
26
31
|
# @option attrs [Integer, String] :radius
|
|
27
32
|
# @option attrs [String] :stroke
|
|
28
33
|
# @option attrs [Numeric, String] :stroke_width
|
|
29
|
-
# @option attrs [
|
|
34
|
+
# @option attrs ["solid", "dashed", "dotted"] :stroke_type
|
|
30
35
|
# @option attrs [String] :fill
|
|
31
36
|
def rect(x, y, w, h, attrs = {})
|
|
32
37
|
w, h = s2f(w, h)
|
|
@@ -49,6 +54,7 @@ module Thinreports
|
|
|
49
54
|
# @option attrs [String] :stroke
|
|
50
55
|
# @option attrs [Numeric, String] :stroke_width
|
|
51
56
|
# @option attrs [Array<Integer, String>] :stroke_dash
|
|
57
|
+
# @option attrs ["solid", "dashed", "dotted"] :stroke_type
|
|
52
58
|
# @option attrs [String] :fill
|
|
53
59
|
def ellipse(x, y, rx, ry, attrs = {})
|
|
54
60
|
rx, ry = s2f(rx, ry)
|
|
@@ -58,8 +64,6 @@ module Thinreports
|
|
|
58
64
|
end
|
|
59
65
|
end
|
|
60
66
|
|
|
61
|
-
private
|
|
62
|
-
|
|
63
67
|
# @param [Hash] attrs
|
|
64
68
|
def with_graphic_styles(attrs, &block)
|
|
65
69
|
stroke = build_stroke_styles(attrs)
|
|
@@ -108,22 +112,23 @@ module Thinreports
|
|
|
108
112
|
def build_stroke_styles(styles)
|
|
109
113
|
color = styles[:stroke]
|
|
110
114
|
width = styles[:stroke_width]
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
+
}
|
|
117
123
|
end
|
|
118
124
|
|
|
119
125
|
# @param [Hash] styles
|
|
120
126
|
# @return [Hash, nil]
|
|
121
127
|
def build_fill_styles(styles)
|
|
122
128
|
color = styles[:fill]
|
|
129
|
+
return nil unless color && color != 'none'
|
|
123
130
|
|
|
124
|
-
|
|
125
|
-
{color: parse_color(color)}
|
|
126
|
-
end
|
|
131
|
+
{ color: parse_color(color) }
|
|
127
132
|
end
|
|
128
133
|
|
|
129
134
|
end
|
|
@@ -30,7 +30,7 @@ module Thinreports
|
|
|
30
30
|
image(image_path, x, y, w, h)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
# @param
|
|
33
|
+
# @param [String, IO] file
|
|
34
34
|
# @param [Numeric, Strng] x
|
|
35
35
|
# @param [Numeric, Strng] y
|
|
36
36
|
# @param [Numeric, Strng] w
|
|
@@ -39,13 +39,16 @@ module Thinreports
|
|
|
39
39
|
# @option options [:left, :center, :right] :position_x (:left)
|
|
40
40
|
# @option options [:top, :center, :bottom] :position_y (:top)
|
|
41
41
|
def image_box(file, x, y, w, h, options = {})
|
|
42
|
-
|
|
42
|
+
image_data_or_path = normalize_image_from_file(file)
|
|
43
43
|
|
|
44
44
|
w, h = s2f(w, h)
|
|
45
45
|
pdf.bounding_box(pos(x, y), width: w, height: h) do
|
|
46
|
-
pdf.image(
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
pdf.image(
|
|
47
|
+
image_data_or_path,
|
|
48
|
+
position: options[:position_x] || :left,
|
|
49
|
+
vposition: options[:position_y] || :top,
|
|
50
|
+
auto_fit: [w, h]
|
|
51
|
+
)
|
|
49
52
|
end
|
|
50
53
|
end
|
|
51
54
|
|
|
@@ -69,13 +72,16 @@ module Thinreports
|
|
|
69
72
|
create_temp_imagefile(image_id, image_data)
|
|
70
73
|
end
|
|
71
74
|
|
|
72
|
-
def normalize_image_from_file(
|
|
75
|
+
def normalize_image_from_file(file)
|
|
76
|
+
return file unless file.is_a?(String)
|
|
77
|
+
|
|
78
|
+
filename = file
|
|
73
79
|
extname = File.extname(filename)
|
|
74
80
|
|
|
75
81
|
return filename unless png_conversion_enabled?
|
|
76
82
|
return filename unless extname.downcase == '.png' || extname.empty?
|
|
77
83
|
|
|
78
|
-
return temp_image_registry[filename] if temp_image_registry.key?(filename)
|
|
84
|
+
return temp_image_registry[filename].path if temp_image_registry.key?(filename)
|
|
79
85
|
|
|
80
86
|
png_normalizer = PNGNormalizer.load_file(filename)
|
|
81
87
|
|
|
@@ -101,17 +107,16 @@ module Thinreports
|
|
|
101
107
|
|
|
102
108
|
# @param [String] image_id
|
|
103
109
|
# @param [String] image_data
|
|
104
|
-
# @return [String] imagefile
|
|
110
|
+
# @return [String] Path to imagefile
|
|
105
111
|
def create_temp_imagefile(image_id, image_data)
|
|
106
112
|
temp_image_registry[image_id] ||= begin
|
|
107
113
|
file = Tempfile.new('temp-image')
|
|
108
114
|
file.binmode
|
|
109
115
|
file.write(image_data)
|
|
110
|
-
file.
|
|
111
|
-
|
|
112
|
-
file.close
|
|
116
|
+
file.open
|
|
117
|
+
file
|
|
113
118
|
end
|
|
114
|
-
temp_image_registry[image_id]
|
|
119
|
+
temp_image_registry[image_id].path
|
|
115
120
|
end
|
|
116
121
|
|
|
117
122
|
def png_conversion_enabled?
|
|
@@ -48,7 +48,7 @@ module Thinreports
|
|
|
48
48
|
# @param [Thinreports::Layout::Format] format
|
|
49
49
|
def create_format_stamp(format)
|
|
50
50
|
create_stamp(format.identifier.to_s) do
|
|
51
|
-
|
|
51
|
+
draw_template_items(format.attributes['items'])
|
|
52
52
|
end
|
|
53
53
|
format_stamp_registry << format.identifier
|
|
54
54
|
end
|
|
@@ -43,7 +43,11 @@ module Thinreports
|
|
|
43
43
|
# @param [Thinreports::Core::Shape::List::SectionInterface] section
|
|
44
44
|
def draw_section(section)
|
|
45
45
|
internal = section.internal
|
|
46
|
-
|
|
46
|
+
|
|
47
|
+
base_top = @format.section_base_position_top(internal.section_name)
|
|
48
|
+
position = [internal.relative_left, base_top + internal.relative_top]
|
|
49
|
+
|
|
50
|
+
drawer(internal).draw(section, position)
|
|
47
51
|
end
|
|
48
52
|
|
|
49
53
|
# @param [Thinreports::Core::Shape::List::SectionInternal] section
|
|
@@ -26,10 +26,7 @@ module Thinreports
|
|
|
26
26
|
id = @format.identifier.to_s
|
|
27
27
|
|
|
28
28
|
unless @stamp_created
|
|
29
|
-
@pdf.create_stamp(id)
|
|
30
|
-
@pdf.parse_svg('<svg xmlns:xlink="http://www.w3.org/1999/xlink">' +
|
|
31
|
-
"#{@format.layout}</svg>", '/svg')
|
|
32
|
-
end
|
|
29
|
+
@pdf.create_stamp(id) { @pdf.draw_template_items(@format.attributes['items']) }
|
|
33
30
|
@stamp_created = true
|
|
34
31
|
end
|
|
35
32
|
pdf_stamp(id)
|
|
@@ -20,7 +20,7 @@ module Thinreports
|
|
|
20
20
|
|
|
21
21
|
shape = shape.internal
|
|
22
22
|
|
|
23
|
-
if shape.type_of?(
|
|
23
|
+
if shape.type_of?(Core::Shape::PageNumber::TYPE_NAME)
|
|
24
24
|
# Do not draw pageno if is not for Report
|
|
25
25
|
draw_pageno_shape(shape, page) if page.count? && shape.for_report?
|
|
26
26
|
else
|
|
@@ -33,11 +33,11 @@ module Thinreports
|
|
|
33
33
|
|
|
34
34
|
def draw_shape(shape)
|
|
35
35
|
case
|
|
36
|
-
when shape.type_of?(
|
|
36
|
+
when shape.type_of?(Core::Shape::TextBlock::TYPE_NAME)
|
|
37
37
|
draw_tblock_shape(shape)
|
|
38
|
-
when shape.type_of?(
|
|
38
|
+
when shape.type_of?(Core::Shape::List::TYPE_NAME)
|
|
39
39
|
draw_list_shape(shape)
|
|
40
|
-
when shape.type_of?(
|
|
40
|
+
when shape.type_of?(Core::Shape::ImageBlock::TYPE_NAME)
|
|
41
41
|
draw_iblock_shape(shape)
|
|
42
42
|
else
|
|
43
43
|
id = shape.identifier
|
|
@@ -72,11 +72,11 @@ module Thinreports
|
|
|
72
72
|
# @param [Thinreports::Core::Shape::Base::Internal] shape
|
|
73
73
|
def create_basic_shape_stamp(shape)
|
|
74
74
|
case
|
|
75
|
-
when shape.type_of?(
|
|
76
|
-
when shape.type_of?(
|
|
77
|
-
when shape.type_of?(
|
|
78
|
-
when shape.type_of?(
|
|
79
|
-
when shape.type_of?(
|
|
75
|
+
when shape.type_of?('text') then create_text_stamp(shape)
|
|
76
|
+
when shape.type_of?('image') then create_image_stamp(shape)
|
|
77
|
+
when shape.type_of?('ellipse') then create_ellipse_stamp(shape)
|
|
78
|
+
when shape.type_of?('rect') then create_rect_stamp(shape)
|
|
79
|
+
when shape.type_of?('line') then create_line_stamp(shape)
|
|
80
80
|
end
|
|
81
81
|
end
|
|
82
82
|
|
data/lib/thinreports/layout.rb
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
|
|
3
3
|
module Thinreports
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
module Layout
|
|
6
6
|
# @see Thinreports::Layout::Base#initialize
|
|
7
7
|
def self.new(filename, options = {})
|
|
8
8
|
Base.new(filename, options)
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
require 'thinreports/layout/version'
|
|
15
15
|
require 'thinreports/layout/base'
|
|
16
16
|
require 'thinreports/layout/format'
|
|
17
|
-
require 'thinreports/layout/
|
|
17
|
+
require 'thinreports/layout/legacy_schema'
|
|
@@ -44,22 +44,6 @@ module Thinreports
|
|
|
44
44
|
def default?
|
|
45
45
|
@id.nil?
|
|
46
46
|
end
|
|
47
|
-
|
|
48
|
-
# @yield [config]
|
|
49
|
-
# @yieldparam [List::Configuration] config
|
|
50
|
-
# @return [List::Configuration]
|
|
51
|
-
def config(&block)
|
|
52
|
-
@config ||= Layout::Configuration.new(self)
|
|
53
|
-
call_block_in(@config, &block)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# @param [Thinreports::Report::Base] parent
|
|
57
|
-
# @param [Hash] options ({})
|
|
58
|
-
# @option option [Boolean] :count (true)
|
|
59
|
-
# @return [Page]
|
|
60
|
-
def new_page(parent, options = {})
|
|
61
|
-
Report::Page.new(parent, self, options)
|
|
62
|
-
end
|
|
63
47
|
end
|
|
64
48
|
|
|
65
49
|
end
|
|
@@ -1,80 +1,61 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
|
-
|
|
3
|
-
require 'digest/sha1'
|
|
2
|
+
require 'json'
|
|
4
3
|
|
|
5
4
|
module Thinreports
|
|
6
5
|
module Layout
|
|
7
|
-
|
|
8
6
|
class Format < Core::Shape::Manager::Format
|
|
9
7
|
config_reader last_version: %w( version )
|
|
10
|
-
config_reader report_title: %w(
|
|
11
|
-
config_reader page_paper_type: %w(
|
|
12
|
-
page_width: %w(
|
|
13
|
-
page_height: %w(
|
|
14
|
-
page_orientation: %w(
|
|
15
|
-
|
|
16
|
-
config_checker 'user', user_paper_type: %w( config page paper-type )
|
|
8
|
+
config_reader report_title: %w( title )
|
|
9
|
+
config_reader page_paper_type: %w( report paper-type ),
|
|
10
|
+
page_width: %w( report width ),
|
|
11
|
+
page_height: %w( report height ),
|
|
12
|
+
page_orientation: %w( report orientation )
|
|
17
13
|
|
|
18
14
|
class << self
|
|
15
|
+
def build(filename)
|
|
16
|
+
schema = JSON.parse(read_file(filename))
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def build_internal(filename, options = {})
|
|
26
|
-
build_once(filename, options[:force]) do |content, id|
|
|
27
|
-
raw_format = parse_json(content)
|
|
28
|
-
|
|
29
|
-
# Check the compatibility of specified layout file.
|
|
30
|
-
unless Thinreports::Layout::Version.compatible?(raw_format['version'])
|
|
31
|
-
info = [filename, raw_format['version'],
|
|
32
|
-
Thinreports::Layout::Version.inspect_required_rules]
|
|
33
|
-
raise Thinreports::Errors::IncompatibleLayoutFormat.new(*info)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
compact_format!(raw_format)
|
|
18
|
+
unless Layout::Version.compatible?(schema['version'])
|
|
19
|
+
raise Errors::IncompatibleLayoutFormat.new(
|
|
20
|
+
filename, schema['version'], Thinreports::Layout::Version.inspect_required_rules
|
|
21
|
+
)
|
|
22
|
+
end
|
|
37
23
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
end
|
|
43
|
-
clean(f.layout)
|
|
44
|
-
end
|
|
24
|
+
if schema['version'] < '0.9.0'
|
|
25
|
+
warn '[DEPRECATION] Support for the layout file with old format that generated with Editor 0.8 or lower will be dropped in Thinreports 1.1.' \
|
|
26
|
+
' Please convert to new layout format using Thinreports Editor 0.9 or 1.0.'
|
|
27
|
+
schema = Layout::LegacySchema.new(schema).upgrade
|
|
45
28
|
end
|
|
29
|
+
|
|
30
|
+
new schema
|
|
46
31
|
end
|
|
47
32
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
%w( finger-print state version ).each {|attr| raw_format.delete(attr) }
|
|
33
|
+
def read_file(filename)
|
|
34
|
+
File.open(filename, 'r:UTF-8') {|f| f.read }
|
|
51
35
|
end
|
|
36
|
+
end
|
|
52
37
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
38
|
+
def initialize(*)
|
|
39
|
+
super
|
|
40
|
+
initialize_items(attributes['items'])
|
|
41
|
+
end
|
|
57
42
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
id = Digest::SHA1.hexdigest(content).to_sym
|
|
62
|
-
built_format_registry[id] ||= block.call(content, id)
|
|
63
|
-
end
|
|
64
|
-
end
|
|
43
|
+
def user_paper_type?
|
|
44
|
+
page_paper_type == 'user'
|
|
45
|
+
end
|
|
65
46
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def initialize_items(item_schemas)
|
|
50
|
+
item_schemas.each do |item_schema|
|
|
51
|
+
id, type = item_schema.values_at 'id', 'type'
|
|
71
52
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
53
|
+
next if id.empty? && type != 'page-number'
|
|
54
|
+
|
|
55
|
+
item = Core::Shape::Format(type).new(item_schema)
|
|
56
|
+
self.shapes[item.id.to_sym] = item
|
|
75
57
|
end
|
|
76
58
|
end
|
|
77
59
|
end
|
|
78
|
-
|
|
79
60
|
end
|
|
80
61
|
end
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'rexml/document'
|
|
5
|
+
|
|
6
|
+
module Thinreports
|
|
7
|
+
module Layout
|
|
8
|
+
class LegacySchema
|
|
9
|
+
def initialize(legacy_schema)
|
|
10
|
+
@legacy_schema = legacy_schema
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def upgrade
|
|
14
|
+
config = legacy_schema['config']
|
|
15
|
+
page_config = config['page']
|
|
16
|
+
|
|
17
|
+
{
|
|
18
|
+
'version' => legacy_schema['version'],
|
|
19
|
+
'title' => legacy_schema['config']['title'],
|
|
20
|
+
'report' => {
|
|
21
|
+
'paper-type' => page_config['paper-type'],
|
|
22
|
+
'width' => page_config['width'].to_f,
|
|
23
|
+
'height' => page_config['height'].to_f,
|
|
24
|
+
'orientation' => page_config['orientation'],
|
|
25
|
+
'margin' => page_config.values_at('margin-top', 'margin-right', 'margin-bottom', 'margin-left').map(&:to_f)
|
|
26
|
+
},
|
|
27
|
+
'items' => build_item_schemas_from_svg(legacy_schema['svg'].dup, '/svg/g', 1)
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
attr_reader :legacy_schema
|
|
32
|
+
|
|
33
|
+
def build_item_schemas_from_svg(svg, root_xpath, level)
|
|
34
|
+
item_schemas = extract_item_schemas(svg, level)
|
|
35
|
+
|
|
36
|
+
svg_doc = REXML::Document.new(normalize_svg(svg, level))
|
|
37
|
+
items = []
|
|
38
|
+
|
|
39
|
+
svg_doc.elements[root_xpath].each do |item_element|
|
|
40
|
+
item_schema = item_schemas[item_element.attributes['x-id']] || {}
|
|
41
|
+
item_attributes = item_element.attributes
|
|
42
|
+
|
|
43
|
+
items <<
|
|
44
|
+
case item_element.attributes['class']
|
|
45
|
+
when 's-text' then text_item_schema(item_attributes, extract_texts_from(item_element))
|
|
46
|
+
when 's-image' then image_item_schema(item_attributes)
|
|
47
|
+
when 's-rect' then rect_item_schema(item_attributes)
|
|
48
|
+
when 's-ellipse' then ellipse_item_schema(item_attributes)
|
|
49
|
+
when 's-line' then line_item_schema(item_attributes)
|
|
50
|
+
when 's-tblock' then text_block_item_schema(item_attributes)
|
|
51
|
+
when 's-iblock' then image_block_item_schema(item_attributes)
|
|
52
|
+
when 's-pageno' then page_number_item_schema(item_attributes)
|
|
53
|
+
when 's-list' then list_item_schema(item_schema)
|
|
54
|
+
else raise 'Unknown item type'
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
items
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def text_item_schema(attributes, texts)
|
|
62
|
+
{
|
|
63
|
+
'id' => attributes['x-id'],
|
|
64
|
+
'type' => Core::Shape::Text::TYPE_NAME,
|
|
65
|
+
'x' => attributes['x-left'].to_f,
|
|
66
|
+
'y' => attributes['x-top'].to_f,
|
|
67
|
+
'width' => attributes['x-width'].to_f,
|
|
68
|
+
'height' => attributes['x-height'].to_f,
|
|
69
|
+
'display' => display(attributes['x-display']),
|
|
70
|
+
'texts' => texts,
|
|
71
|
+
'style' => {
|
|
72
|
+
'font-family' => [ attributes['font-family'] ],
|
|
73
|
+
'font-size' => attributes['font-size'].to_f,
|
|
74
|
+
'color' => attributes['fill'],
|
|
75
|
+
'font-style' => font_style(attributes),
|
|
76
|
+
'text-align' => text_align(attributes['text-anchor']),
|
|
77
|
+
'vertical-align' => vertical_align(attributes['x-valign']),
|
|
78
|
+
'line-height' => line_height(attributes['x-line-height']),
|
|
79
|
+
'letter-spacing' => letter_spacing(attributes['kerning'])
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def rect_item_schema(attributes)
|
|
85
|
+
{
|
|
86
|
+
'id' => attributes['x-id'],
|
|
87
|
+
'type' => 'rect',
|
|
88
|
+
'x' => attributes['x'].to_f,
|
|
89
|
+
'y' => attributes['y'].to_f,
|
|
90
|
+
'width' => attributes['width'].to_f,
|
|
91
|
+
'height' => attributes['height'].to_f,
|
|
92
|
+
'display' => display(attributes['x-display']),
|
|
93
|
+
'style' => {
|
|
94
|
+
'border-width' => attributes['stroke-width'].to_f,
|
|
95
|
+
'border-color' => attributes['stroke'],
|
|
96
|
+
'border-style' => attributes['x-stroke-type'],
|
|
97
|
+
'fill-color' => attributes['fill']
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def line_item_schema(attributes)
|
|
103
|
+
{
|
|
104
|
+
'id' => attributes['x-id'],
|
|
105
|
+
'type' => 'line',
|
|
106
|
+
'x1' => attributes['x1'].to_f,
|
|
107
|
+
'y1' => attributes['y1'].to_f,
|
|
108
|
+
'x2' => attributes['x2'].to_f,
|
|
109
|
+
'y2' => attributes['y2'].to_f,
|
|
110
|
+
'display' => display(attributes['x-display']),
|
|
111
|
+
'style' => {
|
|
112
|
+
'border-width' => attributes['stroke-width'].to_f,
|
|
113
|
+
'border-color' => attributes['stroke'],
|
|
114
|
+
'border-style' => attributes['x-stroke-type']
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def ellipse_item_schema(attributes)
|
|
120
|
+
{
|
|
121
|
+
'id' => attributes['x-id'],
|
|
122
|
+
'type' => 'ellipse',
|
|
123
|
+
'cx' => attributes['cx'].to_f,
|
|
124
|
+
'cy' => attributes['cy'].to_f,
|
|
125
|
+
'rx' => attributes['rx'].to_f,
|
|
126
|
+
'ry' => attributes['ry'].to_f,
|
|
127
|
+
'display' => display(attributes['x-display']),
|
|
128
|
+
'style' => {
|
|
129
|
+
'border-width' => attributes['stroke-width'].to_f,
|
|
130
|
+
'border-color' => attributes['stroke'],
|
|
131
|
+
'border-style' => attributes['x-stroke-type'],
|
|
132
|
+
'fill-color' => attributes['fill']
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def image_item_schema(attributes)
|
|
138
|
+
_, image_type, image_data = attributes['xlink:href'].match(%r|^data:(image/[a-z]+?);base64,(.+)|).to_a
|
|
139
|
+
|
|
140
|
+
{
|
|
141
|
+
'id' => attributes['x-id'],
|
|
142
|
+
'type' => 'image',
|
|
143
|
+
'x' => attributes['x'].to_f,
|
|
144
|
+
'y' => attributes['y'].to_f,
|
|
145
|
+
'width' => attributes['width'].to_f,
|
|
146
|
+
'height' => attributes['height'].to_f,
|
|
147
|
+
'display' => display(attributes['x-display']),
|
|
148
|
+
'data' => {
|
|
149
|
+
'mime-type' => image_type,
|
|
150
|
+
'base64' => image_data
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def page_number_item_schema(attributes)
|
|
156
|
+
{
|
|
157
|
+
'id' => attributes['x-id'],
|
|
158
|
+
'type' => Core::Shape::PageNumber::TYPE_NAME,
|
|
159
|
+
'x' => attributes['x-left'].to_f,
|
|
160
|
+
'y' => attributes['x-top'].to_f,
|
|
161
|
+
'width' => attributes['x-width'].to_f,
|
|
162
|
+
'height' => attributes['x-height'].to_f,
|
|
163
|
+
'format' => attributes['x-format'],
|
|
164
|
+
'target' => attributes['x-target'],
|
|
165
|
+
'display' => display(attributes['x-display']),
|
|
166
|
+
'style' => {
|
|
167
|
+
'font-family' => [ attributes['font-family'] ],
|
|
168
|
+
'font-size' => attributes['font-size'].to_f,
|
|
169
|
+
'color' => attributes['fill'],
|
|
170
|
+
'font-style' => font_style(attributes),
|
|
171
|
+
'text-align' => text_align(attributes['text-anchor']),
|
|
172
|
+
'overflow' => attributes['x-overflow']
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def image_block_item_schema(attributes)
|
|
178
|
+
{
|
|
179
|
+
'id' => attributes['x-id'],
|
|
180
|
+
'type' => Core::Shape::ImageBlock::TYPE_NAME,
|
|
181
|
+
'x' => attributes['x-left'].to_f,
|
|
182
|
+
'y' => attributes['x-top'].to_f,
|
|
183
|
+
'width' => attributes['x-width'].to_f,
|
|
184
|
+
'height' => attributes['x-height'].to_f,
|
|
185
|
+
'display' => display(attributes['x-display']),
|
|
186
|
+
'style' => {
|
|
187
|
+
'position-x' => attributes['x-position-x'],
|
|
188
|
+
'position-y' => image_position_y(attributes['x-position-y'])
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def text_block_item_schema(attributes)
|
|
194
|
+
text_format = {
|
|
195
|
+
'base' => attributes['x-format-base'],
|
|
196
|
+
'type' => attributes['x-format-type']
|
|
197
|
+
}
|
|
198
|
+
case text_format['type']
|
|
199
|
+
when 'datetime'
|
|
200
|
+
text_format['datetime'] = {
|
|
201
|
+
'format' => attributes['x-format-datetime-format']
|
|
202
|
+
}
|
|
203
|
+
when 'number'
|
|
204
|
+
text_format['number'] = {
|
|
205
|
+
'delimiter' => attributes['x-format-number-delimiter'],
|
|
206
|
+
'precision' => attributes['x-format-number-precision'].to_i
|
|
207
|
+
}
|
|
208
|
+
when 'padding'
|
|
209
|
+
text_format['padding'] = {
|
|
210
|
+
'length' => attributes['x-format-padding-length'].to_i,
|
|
211
|
+
'char' => attributes['x-format-padding-char'],
|
|
212
|
+
'direction' => attributes['x-format-padding-direction']
|
|
213
|
+
}
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
{
|
|
217
|
+
'id' => attributes['x-id'],
|
|
218
|
+
'type' => Core::Shape::TextBlock::TYPE_NAME,
|
|
219
|
+
'x' => attributes['x-left'].to_f,
|
|
220
|
+
'y' => attributes['x-top'].to_f,
|
|
221
|
+
'width' => attributes['x-width'].to_f,
|
|
222
|
+
'height' => attributes['x-height'].to_f,
|
|
223
|
+
'display' => display(attributes['x-display']),
|
|
224
|
+
'value' => attributes['x-value'],
|
|
225
|
+
'multiple-line' => attributes['x-multiple'] == 'true',
|
|
226
|
+
'format' => text_format,
|
|
227
|
+
'style' => {
|
|
228
|
+
'font-family' => [ attributes['font-family'] ],
|
|
229
|
+
'font-size' => attributes['font-size'].to_f,
|
|
230
|
+
'color' => attributes['fill'],
|
|
231
|
+
'font-style' => font_style(attributes),
|
|
232
|
+
'text-align' => text_align(attributes['text-anchor']),
|
|
233
|
+
'vertical-align' => vertical_align(attributes['x-valign']),
|
|
234
|
+
'line-height' => line_height(attributes['x-line-height']),
|
|
235
|
+
'letter-spacing' => letter_spacing(attributes['kerning']),
|
|
236
|
+
'overflow' => attributes['x-overflow'],
|
|
237
|
+
'word-wrap' => attributes['x-word-wrap']
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def list_item_schema(legacy_schema)
|
|
243
|
+
{
|
|
244
|
+
'id' => legacy_schema['id'],
|
|
245
|
+
'type' => Core::Shape::List::TYPE_NAME,
|
|
246
|
+
'content-height' => legacy_schema['content-height'].to_f,
|
|
247
|
+
'auto-page-break' => legacy_schema['page-break'] == 'true',
|
|
248
|
+
'display' => display(legacy_schema['display']),
|
|
249
|
+
'header' => list_section_schema('header', legacy_schema),
|
|
250
|
+
'detail' => list_section_schema('detail', legacy_schema),
|
|
251
|
+
'page-footer' => list_section_schema('page-footer', legacy_schema),
|
|
252
|
+
'footer' => list_section_schema('footer', legacy_schema)
|
|
253
|
+
}
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def list_section_schema(section_name, legacy_list_schema)
|
|
257
|
+
legacy_section_schema = legacy_list_schema[section_name]
|
|
258
|
+
|
|
259
|
+
section_svg = %(<svg xmlns:xlink="http://www.w3.org/1999/xlink">#{legacy_section_schema['svg']['content']}</svg>)
|
|
260
|
+
|
|
261
|
+
section_schema = {
|
|
262
|
+
'height' => legacy_section_schema['height'].to_f,
|
|
263
|
+
'translate' => {
|
|
264
|
+
'x' => legacy_section_schema['translate']['x'].to_f,
|
|
265
|
+
'y' => legacy_section_schema['translate']['y'].to_f
|
|
266
|
+
},
|
|
267
|
+
'items' => build_item_schemas_from_svg(section_svg, '/svg', 2)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
unless section_name == 'detail'
|
|
271
|
+
section_schema['enabled'] = legacy_list_schema["#{section_name}-enabled"] == 'true'
|
|
272
|
+
end
|
|
273
|
+
section_schema
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def extract_texts_from(text_item_element)
|
|
277
|
+
[].tap do |texts|
|
|
278
|
+
text_item_element.each_element('text') { |e| texts << e.text }
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def image_position_y(legacy_position_y)
|
|
283
|
+
case legacy_position_y
|
|
284
|
+
when 'top' then 'top'
|
|
285
|
+
when 'center' then 'middle'
|
|
286
|
+
when 'bottom' then 'bottom'
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def display(legacy_display)
|
|
291
|
+
legacy_display == 'true'
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def font_style(attributes)
|
|
295
|
+
style = []
|
|
296
|
+
style << 'bold' if attributes['font-weight'] == 'bold'
|
|
297
|
+
style << 'italic' if attributes['font-style'] == 'italic'
|
|
298
|
+
style << 'underline' if attributes['text-decoration'].include?('underline')
|
|
299
|
+
style << 'linethrough' if attributes['text-decoration'].include?('line-through')
|
|
300
|
+
style
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def text_align(legacy_text_align)
|
|
304
|
+
case legacy_text_align
|
|
305
|
+
when 'start' then 'left'
|
|
306
|
+
when 'middle' then 'center'
|
|
307
|
+
when 'end' then 'right'
|
|
308
|
+
else 'left'
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def vertical_align(legacy_vertical_align)
|
|
313
|
+
return nil unless legacy_vertical_align
|
|
314
|
+
|
|
315
|
+
case legacy_vertical_align
|
|
316
|
+
when 'top' then 'top'
|
|
317
|
+
when 'center' then 'middle'
|
|
318
|
+
when 'bottom' then 'bottom'
|
|
319
|
+
else 'top'
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def line_height(legacy_line_height)
|
|
324
|
+
legacy_line_height == '' ? '' : legacy_line_height.to_f
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def letter_spacing(legacy_letter_spacing)
|
|
328
|
+
case legacy_letter_spacing
|
|
329
|
+
when 'auto', '' then ''
|
|
330
|
+
else legacy_letter_spacing.to_f
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def extract_item_schemas(svg, level)
|
|
335
|
+
items = {}
|
|
336
|
+
svg.scan(/<!--#{level_symbol(level)}SHAPE(.*?)SHAPE#{level_symbol(level)}-->/) do |(item_schema_json)|
|
|
337
|
+
item_schema = JSON.parse(item_schema_json)
|
|
338
|
+
items[item_schema['id']] = item_schema
|
|
339
|
+
end
|
|
340
|
+
items
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def normalize_svg(svg, level)
|
|
344
|
+
svg.gsub!(/<!--#{level_symbol(level)}SHAPE.*?SHAPE#{level_symbol(level)}-->/, '')
|
|
345
|
+
svg.gsub!(/<!--#{level_symbol(level)}LAYOUT(.*?)LAYOUT#{level_symbol(level)}-->/) { $1 }
|
|
346
|
+
svg
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def level_symbol(level)
|
|
350
|
+
'-' * (level - 1)
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
end
|