From c22a1179b7c33da4191f34c2da496d9d6f355546 Mon Sep 17 00:00:00 2001 From: Katsuya Hidaka Date: Sun, 21 May 2023 19:35:10 +0900 Subject: [PATCH] new implementation for section report --- lib/thinreports/section_report/build.rb | 9 ++- .../section_report/builder/item_builder.rb | 34 +++++++---- .../section_report/builder/report_builder.rb | 29 ++++++---- .../section_report/builder/report_data.rb | 13 ----- .../section_report/builder/stack_view_data.rb | 11 ---- ..._builder.rb => stack_view_item_builder.rb} | 34 ++++++----- lib/thinreports/section_report/generate.rb | 15 +++-- .../section_report/pdf/component.rb | 13 +++++ .../section_report/pdf/document.rb | 37 ++++++++++++ lib/thinreports/section_report/pdf/fill.rb | 20 +++++++ lib/thinreports/section_report/pdf/rect.rb | 53 +++++++++++++++++ lib/thinreports/section_report/pdf/render.rb | 23 -------- .../pdf/renderer/group_renderer.rb | 57 ------------------- .../pdf/renderer/section_renderer.rb | 39 ------------- lib/thinreports/section_report/pdf/stroke.rb | 41 +++++++++++++ .../{pdf => }/renderer/draw_item.rb | 0 .../section_report/renderer/group_renderer.rb | 55 ++++++++++++++++++ .../section_report/renderer/rect_renderer.rb | 32 +++++++++++ .../{pdf => }/renderer/section_height.rb | 0 .../renderer/section_renderer.rb | 34 +++++++++++ .../{pdf => }/renderer/stack_view_renderer.rb | 0 .../renderer/stack_view_row_renderer.rb | 0 .../section_report/report/group.rb | 21 +++++++ .../section_report/report/item/base.rb | 36 ++++++++++++ .../section_report/report/item/rect.rb | 25 ++++++++ .../section_report/report/item/stack_view.rb | 27 +++++++++ .../report/item/stack_view_row.rb | 21 +++++++ .../section_report/report/item/text_block.rb | 22 +++++++ .../section_report/report/section.rb | 41 +++++++++++++ .../section_report/report/style/base.rb | 34 +++++++++++ .../section_report/report/style/rect.rb | 24 ++++++++ lib/thinreports/section_report/schema/base.rb | 25 ++++++++ .../section_report/schema/item_container.rb | 41 +++++++++++++ lib/thinreports/section_report/schema/rect.rb | 22 +++++++ .../section_report/schema/section.rb | 35 +++++------- .../section_report/schema/stack_view.rb | 33 +++++++++++ .../section_report/schema/stack_view_row.rb | 23 ++++++++ 37 files changed, 769 insertions(+), 210 deletions(-) delete mode 100644 lib/thinreports/section_report/builder/report_data.rb delete mode 100644 lib/thinreports/section_report/builder/stack_view_data.rb rename lib/thinreports/section_report/builder/{stack_view_builder.rb => stack_view_item_builder.rb} (59%) create mode 100644 lib/thinreports/section_report/pdf/component.rb create mode 100644 lib/thinreports/section_report/pdf/document.rb create mode 100644 lib/thinreports/section_report/pdf/fill.rb create mode 100644 lib/thinreports/section_report/pdf/rect.rb delete mode 100644 lib/thinreports/section_report/pdf/render.rb delete mode 100644 lib/thinreports/section_report/pdf/renderer/group_renderer.rb delete mode 100644 lib/thinreports/section_report/pdf/renderer/section_renderer.rb create mode 100644 lib/thinreports/section_report/pdf/stroke.rb rename lib/thinreports/section_report/{pdf => }/renderer/draw_item.rb (100%) create mode 100644 lib/thinreports/section_report/renderer/group_renderer.rb create mode 100644 lib/thinreports/section_report/renderer/rect_renderer.rb rename lib/thinreports/section_report/{pdf => }/renderer/section_height.rb (100%) create mode 100644 lib/thinreports/section_report/renderer/section_renderer.rb rename lib/thinreports/section_report/{pdf => }/renderer/stack_view_renderer.rb (100%) rename lib/thinreports/section_report/{pdf => }/renderer/stack_view_row_renderer.rb (100%) create mode 100644 lib/thinreports/section_report/report/group.rb create mode 100644 lib/thinreports/section_report/report/item/base.rb create mode 100644 lib/thinreports/section_report/report/item/rect.rb create mode 100644 lib/thinreports/section_report/report/item/stack_view.rb create mode 100644 lib/thinreports/section_report/report/item/stack_view_row.rb create mode 100644 lib/thinreports/section_report/report/item/text_block.rb create mode 100644 lib/thinreports/section_report/report/section.rb create mode 100644 lib/thinreports/section_report/report/style/base.rb create mode 100644 lib/thinreports/section_report/report/style/rect.rb create mode 100644 lib/thinreports/section_report/schema/base.rb create mode 100644 lib/thinreports/section_report/schema/item_container.rb create mode 100644 lib/thinreports/section_report/schema/rect.rb create mode 100644 lib/thinreports/section_report/schema/stack_view.rb create mode 100644 lib/thinreports/section_report/schema/stack_view_row.rb diff --git a/lib/thinreports/section_report/build.rb b/lib/thinreports/section_report/build.rb index e952bde4..c5b282cd 100644 --- a/lib/thinreports/section_report/build.rb +++ b/lib/thinreports/section_report/build.rb @@ -18,11 +18,10 @@ def call(report_params) def load_schema(report_params) loader = Schema::Loader.new - case - when report_params[:layout_file] - loader.load_from_file(report_params[:layout_file]) - when report_params[:layout_data] - loader.load_from_data(report_params[:layout_data]) + if file = report_params[:layout_file] + loader.load_from_file(file) + elsif data = report_params[:layout_data] + loader.load_from_data(data) else raise Errors::LayoutFileNotFound end diff --git a/lib/thinreports/section_report/builder/item_builder.rb b/lib/thinreports/section_report/builder/item_builder.rb index 990dc366..5906e08f 100644 --- a/lib/thinreports/section_report/builder/item_builder.rb +++ b/lib/thinreports/section_report/builder/item_builder.rb @@ -9,27 +9,41 @@ class ItemBuilder Context = Struct.new(:parent_schema) def initialize(item_schema, parent_schema) - @item = Core::Shape::Interface(nil, item_schema) + @item_schema = item_schema @parent_schema = parent_schema end def build(item_params) params = build_params(item_params) - item.visible(params[:display]) if params.key?(:display) - item.value(params[:value]) if params.key?(:value) - item.styles(params[:styles]) if params.key?(:styles) - - if item.internal.format.attributes['type'] == Core::Shape::StackView::TYPE_NAME - StackViewBuilder.new(item).update(params) + build_item(item_schema).tap do |item| + item.visible(params[:display]) if params.key?(:display) + item.value(params[:value]) if params.key?(:value) + item.styles(params[:styles]) if params.key?(:styles) end - - item end private - attr_reader :item, :parent_schema + attr_reader :item_schema, :parent_schema + + def build_item(schema) + item_class = + case schema + when Schema::TextBlock + Item::TextBlock + when Schema::ImageBlock + Item::ImageBlock + when Schema::StackView + Item::StackView + when Schema::Text + Item::Text + when Schema::Basic + Item::Basic + end + + item_class.new(schema) + end def build_params(params) return {} unless params diff --git a/lib/thinreports/section_report/builder/report_builder.rb b/lib/thinreports/section_report/builder/report_builder.rb index e9c64143..229a05cd 100644 --- a/lib/thinreports/section_report/builder/report_builder.rb +++ b/lib/thinreports/section_report/builder/report_builder.rb @@ -1,21 +1,19 @@ # frozen_string_literal: true -require_relative 'report_data' require_relative 'item_builder' module Thinreports module SectionReport module Builder class ReportBuilder + Root = Struct.new(:schema, :groups) + def initialize(schema) @schema = schema end def build(params) - ReportData::Main.new( - schema, - build_groups(params[:groups]) - ) + Root.new(schema, build_groups(params[:groups])) end private @@ -26,7 +24,7 @@ def build_groups(groups_params) return [] unless groups_params groups_params.map do |group_params| - ReportData::Group.new( + Report::Group.new( build_sections(:header, group_params[:headers] || {}), build_detail_sections(group_params[:details] || []), build_sections(:footer, group_params[:footers] || {}) @@ -43,10 +41,11 @@ def build_sections(section_type, sections_params) sections_schemas.each_with_object([]) do |(section_id, section_schema), sections| section_params = sections_params[section_id.to_sym] || {} + next unless section_enabled?(section_schema, section_params) items = build_items(section_schema, section_params[:items] || {}) - sections << ReportData::Section.new(section_schema, items, section_params[:min_height]) + sections << Report::Section.new(section_schema, items, **section_params.slice(:min_height)) end end @@ -58,14 +57,22 @@ def build_detail_sections(details_params) next unless detail_schema items = build_items(detail_schema, detail_params[:items] || {}) - details << ReportData::Section.new(detail_schema, items, detail_params[:min_height]) + details << Report::Section.new(detail_schema, items, **detail_params.slice(:min_height)) end end def build_items(section_schema, items_params) - section_schema.items.each_with_object([]) do |item_schema, items| - item = ItemBuilder.new(item_schema, section_schema).build(items_params[item_schema.id&.to_sym]) - items << item if item.visible? + section_schema.items.each_with_object([]) do |item_schema, m| + builder = + if item_schema.is_a?(Schema::StackView) + StackViewItemBuilder + else + ItemBuilder + end + + item = builder.new(item_schema, section_schema).build(item_params[item_schema.id.to_sym]) + + m << item if item.visible? end end diff --git a/lib/thinreports/section_report/builder/report_data.rb b/lib/thinreports/section_report/builder/report_data.rb deleted file mode 100644 index d106e275..00000000 --- a/lib/thinreports/section_report/builder/report_data.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module Thinreports - module SectionReport - module Builder - module ReportData - Main = Struct.new :schema, :groups - Group = Struct.new :headers, :details, :footers - Section = Struct.new :schema, :items, :min_height - end - end - end -end diff --git a/lib/thinreports/section_report/builder/stack_view_data.rb b/lib/thinreports/section_report/builder/stack_view_data.rb deleted file mode 100644 index bc58bb87..00000000 --- a/lib/thinreports/section_report/builder/stack_view_data.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -module Thinreports - module SectionReport - module Builder - module StackViewData - Row = Struct.new :schema, :items, :min_height - end - end - end -end diff --git a/lib/thinreports/section_report/builder/stack_view_builder.rb b/lib/thinreports/section_report/builder/stack_view_item_builder.rb similarity index 59% rename from lib/thinreports/section_report/builder/stack_view_builder.rb rename to lib/thinreports/section_report/builder/stack_view_item_builder.rb index f7c38049..5e3373bf 100644 --- a/lib/thinreports/section_report/builder/stack_view_builder.rb +++ b/lib/thinreports/section_report/builder/stack_view_item_builder.rb @@ -5,18 +5,29 @@ module Thinreports module SectionReport module Builder - class StackViewBuilder - def initialize(item) - @item = item + class StackViewItemBuilder + def initialize(item_schema, parent_schema) + @item_schema = item_schema + @parent_schema = parent_schema end - def update(params) - rows_params = params[:rows] || {} - rows_schema = item.internal.format.rows + def build(item_params) + rows = build_rows(item_params[:rows] || {}) - rows = [] - rows_schema.each do |row_schema| + item = Report::Item::StackView.new(item_schema, rows) + item.visible(item_params[:display]) if item_params.key?(:display) + + item + end + + private + + attr_reader :item_schema + + def build_rows(rows_params) + item_schema.rows.each_with_object([]) do |row_schema, m| row_params = rows_params[row_schema.id.to_sym] || {} + next unless row_enabled?(row_schema, row_params) items = build_row_items( @@ -24,15 +35,10 @@ def update(params) row_params[:items] || {} ) - rows << StackViewData::Row.new(row_schema, items, row_params[:min_height]) + m << Report::Item::StackViewRow.new(row_schema, items, **row_params.slice(:min_height)) end - item.internal.rows = rows end - private - - attr_reader :item - def build_row_items(row_schema, items_params) row_schema.items.each_with_object([]) do |item_schema, items| item = ItemBuilder.new(item_schema, row_schema).build(items_params[item_schema.id&.to_sym]) diff --git a/lib/thinreports/section_report/generate.rb b/lib/thinreports/section_report/generate.rb index b815ad0b..a2198c9f 100644 --- a/lib/thinreports/section_report/generate.rb +++ b/lib/thinreports/section_report/generate.rb @@ -1,19 +1,16 @@ # frozen_string_literal: true require_relative 'build' -require_relative 'pdf/render' module Thinreports module SectionReport class Generate - def initialize - @pdf = Thinreports::Generator::PDF::Document.new - end - def call(report_params, filename: nil) report = Build.new.call(report_params) - PDF::Render.new(pdf).call!(report) + pdf = SectionReport::Pdf::Document.new(report.schema) + + render(pdf, report) filename ? pdf.render_file(filename) : pdf.render end @@ -21,6 +18,12 @@ def call(report_params, filename: nil) private attr_reader :pdf + + def render(pdf, report) + report.groups.each do |group| + group.render(pdf) + end + end end end end diff --git a/lib/thinreports/section_report/pdf/component.rb b/lib/thinreports/section_report/pdf/component.rb new file mode 100644 index 00000000..da3fb82f --- /dev/null +++ b/lib/thinreports/section_report/pdf/component.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Thinreports + module SectionReport + module Pdf + module Component + def point(x, y) + [x, pdf.bounds.height - y] + end + end + end + end +end diff --git a/lib/thinreports/section_report/pdf/document.rb b/lib/thinreports/section_report/pdf/document.rb new file mode 100644 index 00000000..d8871742 --- /dev/null +++ b/lib/thinreports/section_report/pdf/document.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require_relative 'rect' + +module Thinreports + module SectionReport + module Pdf + class Document + def draw_rect(&block) + Rect.new(pdf).tap(&block).draw + end + + def draw_line + end + + def draw_ellipse + end + + def draw_text + end + + def draw_text_block + end + + def draw_image + end + + def draw_image_block + end + + def section(height, &block) + pdf.bounding_box([0, pdf.cursor], width: pdf.bounds.width, height: height, &block) + end + end + end + end +end diff --git a/lib/thinreports/section_report/pdf/fill.rb b/lib/thinreports/section_report/pdf/fill.rb new file mode 100644 index 00000000..30728e95 --- /dev/null +++ b/lib/thinreports/section_report/pdf/fill.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Thinreports + module SectionReport + module Pdf + module Fill + attr_accessor :fill_color + + def initialize(*) + super + @fill_color = 'none' + end + + def fill? + fill_color && fill_color != 'none' + end + end + end + end +end diff --git a/lib/thinreports/section_report/pdf/rect.rb b/lib/thinreports/section_report/pdf/rect.rb new file mode 100644 index 00000000..f8a93bbd --- /dev/null +++ b/lib/thinreports/section_report/pdf/rect.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require_relative 'component' +require_relative 'stroke' +require_relative 'fill' + +module Thinreports + module SectionReport + module Pdf + class Rect + include Component + include Stroke + include Fill + + attr_reader :pdf + attr_accessor :x, :y, :width, :height + attr_accessor :radius + + def initialize(pdf) + @pdf = pdf + @x, @y = 0, 0 + @width, @height = 0, 0 + @radius = 0 + end + + def draw + pdf.fill_and_stroke do + set_stroke + set_fill + + if radius.positive? + pdf.rounded_rectangle(point(x, y), width, height, radius) + else + pdf.rectangle(point(x, y), width, height) + end + end + end + + def set_stroke + return unless stroke? + + pdf.stroke_color(stroke_color) + pdf.line_width(stroke_width) + pdf.dash(stroke_dash_length, space: stroke_dash_space) if stroke_dash? + end + + def set_fill + pdf.fill_color(fill_color) if fill? + end + end + end + end +end diff --git a/lib/thinreports/section_report/pdf/render.rb b/lib/thinreports/section_report/pdf/render.rb deleted file mode 100644 index ad4cc644..00000000 --- a/lib/thinreports/section_report/pdf/render.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -require_relative 'renderer/group_renderer' - -module Thinreports - module SectionReport - module PDF - class Render - def initialize(pdf) - @group_renderer = Renderer::GroupRenderer.new(pdf) - end - - def call!(report) - report.groups.each { |group| group_renderer.render(report, group) } - end - - private - - attr_reader :group_renderer - end - end - end -end diff --git a/lib/thinreports/section_report/pdf/renderer/group_renderer.rb b/lib/thinreports/section_report/pdf/renderer/group_renderer.rb deleted file mode 100644 index a43d4b05..00000000 --- a/lib/thinreports/section_report/pdf/renderer/group_renderer.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true - -require_relative 'section_renderer' - -module Thinreports - module SectionReport - module Renderer - class GroupRenderer - def initialize(pdf) - @pdf = pdf - @section_renderer = Renderer::SectionRenderer.new(pdf) - end - - def render(report, group) - pdf.start_new_page_for_section_report report.schema - current_page_height = 0 - - max_page_height = pdf.max_content_height - - group.headers.each do |header| - section_renderer.render(header) - current_page_height += section_renderer.section_height(header) - end - - group.details.each do |detail| - if current_page_height + section_renderer.section_height(detail) > max_page_height - pdf.start_new_page_for_section_report report.schema - current_page_height = 0 - - group.headers.each do |header| - if header.schema.every_page? - section_renderer.render(header) - current_page_height += section_renderer.section_height(header) - end - end - end - section_renderer.render(detail) - current_page_height += section_renderer.section_height(detail) - end - - group.footers.each do |footer| - if current_page_height + section_renderer.section_height(footer) > max_page_height - pdf.start_new_page_for_section_report report.schema - current_page_height = 0 - end - section_renderer.render(footer) - current_page_height += section_renderer.section_height(footer) - end - end - - private - - attr_reader :pdf, :section_renderer - end - end - end -end diff --git a/lib/thinreports/section_report/pdf/renderer/section_renderer.rb b/lib/thinreports/section_report/pdf/renderer/section_renderer.rb deleted file mode 100644 index 9ac278dd..00000000 --- a/lib/thinreports/section_report/pdf/renderer/section_renderer.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -require_relative 'stack_view_renderer' -require_relative 'section_height' -require_relative 'draw_item' - -module Thinreports - module SectionReport - module Renderer - class SectionRenderer - include SectionHeight - include DrawItem - - def initialize(pdf) - @pdf = pdf - end - - def render(section) - doc = pdf.pdf - - actual_height = section_height(section) - doc.bounding_box([0, doc.cursor], width: doc.bounds.width, height: actual_height) do - section.items.each do |item| - draw_item(item, (actual_height - section.schema.height)) - end - end - end - - private - - attr_reader :pdf - - def stack_view_renderer - @stack_view_renderer ||= Renderer::StackViewRenderer.new(pdf) - end - end - end - end -end diff --git a/lib/thinreports/section_report/pdf/stroke.rb b/lib/thinreports/section_report/pdf/stroke.rb new file mode 100644 index 00000000..f59b6e3f --- /dev/null +++ b/lib/thinreports/section_report/pdf/stroke.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Thinreports + module SectionReport + module Pdf + module Stroke + STROKE_BASE_WIDTH = 0.9 + + attr_accessor :stroke_color, :stroke_width + :stroke_dash_length, :stroke_dash_space + + def initialize(*) + super + @stroke_color = 'none' + @stroke_width = 0 + @stroke_dash_length = 0 + @stroke_dash_space = 0 + end + + def stroke? = stroke_width.positive? && stroke_color != 'none' + + def stroke_dash? + stroke? && stroke_dash_length.positive? + end + + def stroke_width + super * BASE_WIDTH + end + + def stroke_style=(type) + @stroke_dash_length, @stroke_dash_space = + case type + when 'dashed' then [2, 2] + when 'dotted' then [1, 2] + else [0, 0] + end + end + end + end + end +end diff --git a/lib/thinreports/section_report/pdf/renderer/draw_item.rb b/lib/thinreports/section_report/renderer/draw_item.rb similarity index 100% rename from lib/thinreports/section_report/pdf/renderer/draw_item.rb rename to lib/thinreports/section_report/renderer/draw_item.rb diff --git a/lib/thinreports/section_report/renderer/group_renderer.rb b/lib/thinreports/section_report/renderer/group_renderer.rb new file mode 100644 index 00000000..eaab6c8e --- /dev/null +++ b/lib/thinreports/section_report/renderer/group_renderer.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require_relative 'section_renderer' + +module Thinreports + module SectionReport + module Renderer + class GroupRenderer + def initialize(pdf, group) + @pdf = pdf + @group = group + end + + def render + render_headers + render_details + render_footers + end + + private + + attr_reader :pdf, :group + + def render_headers(headers = group.headers) + headers.each { |header| header.render(pdf) } + end + + def render_details + group.details.each do |detail| + if pdf.page_overflow_with?(detail.height(pdf)) + pdf.start_new_page + render_headers(every_page_headers) + end + + detail.render(pdf) + end + end + + def render_footers + group.footers.each do |footer| + pdf.start_new_page if pdf.page_overflow_with?(footer.height(pdf)) + + footer.render(pdf) + end + end + + def every_page_headers + @every_page_headers ||= group.headers.select { |header| + header.schema.every_page? + } + end + end + end + end +end diff --git a/lib/thinreports/section_report/renderer/rect_renderer.rb b/lib/thinreports/section_report/renderer/rect_renderer.rb new file mode 100644 index 00000000..5b3b2094 --- /dev/null +++ b/lib/thinreports/section_report/renderer/rect_renderer.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Thinreports + module SectionReport + module Renderer + class RectRenderer + def initialize(pdf, rect) + @pdf = pdf + @rect = rect + end + + def render + pdf.draw_rect { |r| + r.x = rect.x + r.y = rect.y + r.width = rect.width + r.height = rect.height + r.stroke_style = rect.style.border_style + r.stroke_color = rect.style.border_color + r.stroke_width = rect.style.border_width + r.fill_color = rect.style.fill_color + r.radius = rect.border_radius + } + end + + private + + attr_reader :pdf, :rect + end + end + end +end diff --git a/lib/thinreports/section_report/pdf/renderer/section_height.rb b/lib/thinreports/section_report/renderer/section_height.rb similarity index 100% rename from lib/thinreports/section_report/pdf/renderer/section_height.rb rename to lib/thinreports/section_report/renderer/section_height.rb diff --git a/lib/thinreports/section_report/renderer/section_renderer.rb b/lib/thinreports/section_report/renderer/section_renderer.rb new file mode 100644 index 00000000..6d223b39 --- /dev/null +++ b/lib/thinreports/section_report/renderer/section_renderer.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Thinreports + module SectionReport + module Renderer + class SectionRenderer + def initialize(pdf, section) + @pdf = pdf + @section = section + end + + def render + actual_height = section.height + + pdf.section(section.height) do + section.items.each do |item| + item.render(pdf) + end + end + + pdf.bounding_box([0, pdf.cursor], width: pdf.bounds.width, height: actual_height) do + section.items.each do |item| + item.render(pdf, expanded_height: actual_height - section.schema.height) + end + end + end + + private + + attr_reader :pdf, :section + end + end + end +end diff --git a/lib/thinreports/section_report/pdf/renderer/stack_view_renderer.rb b/lib/thinreports/section_report/renderer/stack_view_renderer.rb similarity index 100% rename from lib/thinreports/section_report/pdf/renderer/stack_view_renderer.rb rename to lib/thinreports/section_report/renderer/stack_view_renderer.rb diff --git a/lib/thinreports/section_report/pdf/renderer/stack_view_row_renderer.rb b/lib/thinreports/section_report/renderer/stack_view_row_renderer.rb similarity index 100% rename from lib/thinreports/section_report/pdf/renderer/stack_view_row_renderer.rb rename to lib/thinreports/section_report/renderer/stack_view_row_renderer.rb diff --git a/lib/thinreports/section_report/report/group.rb b/lib/thinreports/section_report/report/group.rb new file mode 100644 index 00000000..54f13af5 --- /dev/null +++ b/lib/thinreports/section_report/report/group.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Thinreports + module SectionReport + module Report + class Group + attr_reader :headers, :details, :footers + + def initailize(headers, details, footers) + @headers = headers + @details = details + @footers = footers + end + + def render(pdf) + Renderer::GroupRenderer.new(pdf, self).render + end + end + end + end +end diff --git a/lib/thinreports/section_report/report/item/base.rb b/lib/thinreports/section_report/report/item/base.rb new file mode 100644 index 00000000..724c16f5 --- /dev/null +++ b/lib/thinreports/section_report/report/item/base.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'forwardable' +require 'style' + +module Thinreports + module SectionReport + module Report + module Item + class Base + extend Forwardable + + attr_reader :schema, :state + def_delegator :schema, :id + + def initialize(schema) + @schema = schema + @state = {} + end + + def display? + state.fetch(:display) { schema.display? } + end + + def display=(visibility) + state[:display] = visibility + end + + def render + raise NotImplementedError + end + end + end + end + end +end diff --git a/lib/thinreports/section_report/report/item/rect.rb b/lib/thinreports/section_report/report/item/rect.rb new file mode 100644 index 00000000..b13e80cf --- /dev/null +++ b/lib/thinreports/section_report/report/item/rect.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'style' + +module Thinreports + module SectionReport + module Report + module Item + class Rect < Base + def_delegators :schema, + :x, :y, :width, :height, :border_radius, + :affect_bottom_margin? + + def style + @style ||= Style::Rect.new(schema.style) + end + + def render(pdf, height_to_expand: nil) + Renderer::RectRenderer.new(pdf, self).render + end + end + end + end + end +end diff --git a/lib/thinreports/section_report/report/item/stack_view.rb b/lib/thinreports/section_report/report/item/stack_view.rb new file mode 100644 index 00000000..698a8ca9 --- /dev/null +++ b/lib/thinreports/section_report/report/item/stack_view.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'forwardable' +require 'style' + +module Thinreports + module SectionReport + module Report + module Item + class StackView < Base + attr_reader :rows + + def_delegators :schema, :height + + def initialize(schema, rows) + super(schema) + @rows = rows + end + + def render(pdf) + Renderer::StackViewRenderer.new(pdf, self).render + end + end + end + end + end +end diff --git a/lib/thinreports/section_report/report/item/stack_view_row.rb b/lib/thinreports/section_report/report/item/stack_view_row.rb new file mode 100644 index 00000000..2e9b99db --- /dev/null +++ b/lib/thinreports/section_report/report/item/stack_view_row.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Thinreports + module SectionReport + module Report + module Item + class StackViewRow + def initialize(schema, items, min_height: nil) + @schema = schema + @items = items + @min_height = min_height + end + + def render + Renderer::StackViewRowRenderer.new(self).render + end + end + end + end + end +end diff --git a/lib/thinreports/section_report/report/item/text_block.rb b/lib/thinreports/section_report/report/item/text_block.rb new file mode 100644 index 00000000..71500249 --- /dev/null +++ b/lib/thinreports/section_report/report/item/text_block.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require 'forwardable' +require 'style' + +module Thinreports + module SectionReport + module Report + module Item + class TextBlock < Base + def style + @style ||= Style::TextBlock.new(schema.style) + end + + def render + Renderer::TextBlockRenderer.new(self).render + end + end + end + end + end +end diff --git a/lib/thinreports/section_report/report/section.rb b/lib/thinreports/section_report/report/section.rb new file mode 100644 index 00000000..e1782006 --- /dev/null +++ b/lib/thinreports/section_report/report/section.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Thinreports + module SectionReport + module Report + class Section + Dimentions = Struct.new(:bottom_margin, :content_bottom) + + attr_reader :schema, :items, :min_height + + def initialize(schema, items, min_height: nil) + @schema = schema + @items = items + @min_height = min_height || 0 + end + + def render(pdf) + Renderer::SectionRenderer.new(pdf, self).render + end + + def height + return [min_height, schema.height].max if schema.auto_stretch? || items.empty? + + # item_layouts = section.items.map { |item| item_layout(section, item.internal) }.compact + + max_content_bottom = item_layouts.each_with_object([]) do |l, bottoms| + bottoms << l.top_margin + l.content_height if l.shape.format.affect_bottom_margin? + end.max.to_f + + [section.min_height || 0, max_content_bottom + min_bottom_margin].max + end + + def bottom_margin + end + + def content_bottom + end + end + end + end +end diff --git a/lib/thinreports/section_report/report/style/base.rb b/lib/thinreports/section_report/report/style/base.rb new file mode 100644 index 00000000..4efbff81 --- /dev/null +++ b/lib/thinreports/section_report/report/style/base.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Thinreports + module SectionReport + module Report + module Style + class Base + class << self + def style(method_name, key) + define_method(method_name.to_sym) { style[key] } + define_method(:"#{method_name}=") { |value| style[key] = value } + end + end + + def initialize(schema_style) + @style = schema_style.dup + end + + def [](name) + public_send(name.to_sym) + end + + def []=(name, value) + public_send(:"#{name}=", value) + end + + private + + attr_reader :style + end + end + end + end +end diff --git a/lib/thinreports/section_report/report/style/rect.rb b/lib/thinreports/section_report/report/style/rect.rb new file mode 100644 index 00000000..8c5e0935 --- /dev/null +++ b/lib/thinreports/section_report/report/style/rect.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Thinreports + module SectionReport + module Report + module Style + class Rect < Style::Base + style :border_color, 'border-color' + style :border_width, 'border-width' + style :border_style, 'border-style' + style :fill_color, 'fill-color' + + def border + [border_width, border_color] + end + + def border=(width_and_color) + self.border_width, self.border_color = width_and_color + end + end + end + end + end +end diff --git a/lib/thinreports/section_report/schema/base.rb b/lib/thinreports/section_report/schema/base.rb new file mode 100644 index 00000000..810d70b2 --- /dev/null +++ b/lib/thinreports/section_report/schema/base.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Thinreports + module SectionReport + module Schema + class Base + class << self + def attribute(name, *keys) + define_method(name) { schema.dig(keys.empty? ? name : *keys) } + end + + def attributes(*names) + names.each { |name| attribute(name) } + end + end + + attr_reader :schema + + def initialize(schema) + @schema = schema + end + end + end + end +end diff --git a/lib/thinreports/section_report/schema/item_container.rb b/lib/thinreports/section_report/schema/item_container.rb new file mode 100644 index 00000000..01c0150e --- /dev/null +++ b/lib/thinreports/section_report/schema/item_container.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Thinreports + module SectionReport + module Schema + module ItemContainer + attr_reader :items + + def initialize_items + @items = schema['items'].each_with_object([]) do |schema, m| + item_schema = initialize_item_schema(schema) + + next if item_schema.id.empty? + + m << item_schema + end + end + + def initialize_item_schema(schema) + schema_class = + case schema['type'] + when 'text-block' + Schema::TextBlock + when 'image-block' + Schema::ImageBlock + when 'stack-view' + Schema::StackView + when 'text' + Schema::Text + when 'rect' + Schema::Rect + when 'line', 'ellipse', 'image' + Schema::Basic + end + + schema_class.new(item_schema) + end + end + end + end +end diff --git a/lib/thinreports/section_report/schema/rect.rb b/lib/thinreports/section_report/schema/rect.rb new file mode 100644 index 00000000..c51392f3 --- /dev/null +++ b/lib/thinreports/section_report/schema/rect.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Thinreports + module SectionReport + module Schema + class Rect < Schema::Base + attributes :id, :type, :style + attributes :x, :y, :width, :height + + attribute :display?, 'display' + attribute :follow_stretch, 'follow-stretch' + attribute :affect_bottom_margin?, 'affect-bottom-margin' + attribute :border_radius, 'border-radius' + + def bottom + y + height + end + end + end + end +end + diff --git a/lib/thinreports/section_report/schema/section.rb b/lib/thinreports/section_report/schema/section.rb index 63ab7185..207a472e 100644 --- a/lib/thinreports/section_report/schema/section.rb +++ b/lib/thinreports/section_report/schema/section.rb @@ -4,36 +4,29 @@ module Thinreports module SectionReport module Schema module Section - class Base < Core::Shape::Manager::Format - config_reader :id, :type - config_reader :height - config_checker true, :display - config_checker true, auto_stretch: 'auto-stretch' + class Base < Schema::Base + include ItemContainer - attr_reader :items + attributes :id, :type, :height - def initialize(schema_data, items:) - super(schema_data) - initialize_items(items) - end + attribute :display?, 'display' + attribute :auto_stretch?, 'auto-stretch' - def find_item(id) - @item_with_ids[id.to_sym] - end + def initialize(*) + super - private + initialize_items + end - def initialize_items(items) - @items = items - @item_with_ids = items.each_with_object({}) do |item, item_with_ids| - next if item.id.empty? - item_with_ids[item.id.to_sym] = item - end + def bottom_margin + @bottom_margin ||= items.each_with_object([]) do |item, margins| + margins << height - item.bottom if item.affect_bottom_margin? + end.min end end class Header < Base - config_checker true, every_page: 'every-page' + attribute :every_page?, 'every-page' end class Footer < Base diff --git a/lib/thinreports/section_report/schema/stack_view.rb b/lib/thinreports/section_report/schema/stack_view.rb new file mode 100644 index 00000000..a93ebdc7 --- /dev/null +++ b/lib/thinreports/section_report/schema/stack_view.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Thinreports + module SectionReport + module Schema + class StackView < Schema::Base + attributes :id, :type + + attribute :display?, 'display' + attribute :follow_stretch, 'follow-stretch' + attribute :affect_bottom_margin?, 'affect-bottom-margin' + + def rows + @rows ||= schema['rows'].map { |row| + Schema::StackViewRow.new(row) + } + end + + def style + schema['style'] || {} + end + + def height + @height ||= rows.sum(&:height) + end + + def bottom + y + height + end + end + end + end +end diff --git a/lib/thinreports/section_report/schema/stack_view_row.rb b/lib/thinreports/section_report/schema/stack_view_row.rb new file mode 100644 index 00000000..39cf00f9 --- /dev/null +++ b/lib/thinreports/section_report/schema/stack_view_row.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Thinreports + module SectionReport + module Schema + module StackViewRow < Schema::Base + include ItemContainer + + attributes :id, :height + + attribute :display?, 'display' + attribute :auto_stretch?, 'auto-stretch' + + def initialize(schema) + super + + initialize_items + end + end + end + end +end +