From 4770ea70ba66ccdfa7ebd15b32ec7792d7deec90 Mon Sep 17 00:00:00 2001 From: Andrej Sandorf <77627197+as-op@users.noreply.github.com> Date: Mon, 10 Jun 2024 16:34:30 +0200 Subject: [PATCH] fix(tables): better image handling in table autosizing (#8) * fix(tables): better image handling in table autosizing --- lib/md_to_pdf/elements/blockquote.rb | 4 +- lib/md_to_pdf/elements/html.rb | 26 +- lib/md_to_pdf/elements/image.rb | 4 +- lib/md_to_pdf/elements/table.rb | 12 +- .../ext/prawn-table/table/cell/cell/image.rb | 14 +- lib/md_to_pdf/ext/prawn-table/table/table.rb | 16 +- lib/md_to_pdf/utils/common.rb | 8 + spec/fixtures/image/html.md | 3 + spec/fixtures/image/image.md | 2 +- spec/fixtures/image/in_table.md | 8 +- spec/fixtures/image/large_in_table.md | 8 + spec/markdown_to_pdf/image_spec.rb | 271 ++++++++++++++---- spec/pdf_helpers.rb | 23 +- 13 files changed, 311 insertions(+), 88 deletions(-) create mode 100644 spec/fixtures/image/large_in_table.md diff --git a/lib/md_to_pdf/elements/blockquote.rb b/lib/md_to_pdf/elements/blockquote.rb index 6628163..666f9d2 100644 --- a/lib/md_to_pdf/elements/blockquote.rb +++ b/lib/md_to_pdf/elements/blockquote.rb @@ -31,7 +31,7 @@ def draw_blockquote(node, opts) def data_blockquote_paragraph(node, opts) cell_data = data_node_children(node, opts) - [make_table_cell_or_subtable(cell_data, opts, :left)] + [make_table_cell_or_subtable(cell_data, opts, :left, 1)] end def data_blockquote_list(node, opts) @@ -40,7 +40,7 @@ def data_blockquote_list(node, opts) sub.to_a.each do |paragraph| cell_data = data_node_children(paragraph, opts) unless cell_data.empty? - cell = make_table_cell_or_subtable(cell_data, opts, :left) + cell = make_table_cell_or_subtable(cell_data, opts, :left, 1) result.push(cell) end end diff --git a/lib/md_to_pdf/elements/html.rb b/lib/md_to_pdf/elements/html.rb index d0e02f7..925ce05 100644 --- a/lib/md_to_pdf/elements/html.rb +++ b/lib/md_to_pdf/elements/html.rb @@ -105,7 +105,7 @@ def data_inlinehtml_tag(tag, node, opts) when 'comment' # ignore html comments when 'img' - result.push({ image: sub.attr('src') }) + result.push(data_inline_image_tag(sub, node, opts)) when 'ul', 'ol' result.concat(data_inlinehtml_list_tag(sub, node, opts)) when 'label', 'p', 'li' @@ -126,6 +126,27 @@ def data_inlinehtml_tag(tag, node, opts) result end + def data_image_style_opts(tag, _node, _opts) + result = {} + if tag.attr("style") + image_styles = tag.attr("style").split(';').to_h do |pair| + k, v = pair.split(':', 2) + [k, v] + end + if image_styles['width'] + custom_max_width = parse_pt(image_styles['width']) + result[:custom_max_width] = custom_max_width unless custom_max_width.nil? || custom_max_width <= 0 + end + end + result[:image_classes] = tag.attr('class') + result[:image_caption] = find_img_caption(tag) + result + end + + def data_inline_image_tag(tag, node, opts) + { image: tag.attr('src') }.merge(data_image_style_opts(tag, node, opts)) + end + def data_inlinehtml_list_tag(tag, node, opts) result = [] points, level, _list_style, content_opts = data_html_list(tag, node, opts) @@ -188,7 +209,8 @@ def draw_html_tag(tag, node, opts) tag.children.each do |sub| case sub.name when 'img' - embed_image(sub.attr('src'), node, current_opts.merge({ image_classes: sub.attr('class'), image_caption: find_img_caption(sub) })) + embed_image(sub.attr('src'), node, current_opts + .merge(data_image_style_opts(sub, node, current_opts))) when 'text' @pdf.formatted_text([text_hash(sub.text, current_opts)]) when 'a' diff --git a/lib/md_to_pdf/elements/image.rb b/lib/md_to_pdf/elements/image.rb index 143da46..1b7aabe 100644 --- a/lib/md_to_pdf/elements/image.rb +++ b/lib/md_to_pdf/elements/image.rb @@ -55,11 +55,9 @@ def build_image_settings(style, image_info, opts) end def build_image_opts(style, image_info, image_margin_opts, opts) - max_width = opt_image_max_width(style) options = opts_image(style) width = @pdf.bounds.width - (image_margin_opts[:left_margin] || 0) - (image_margin_opts[:right_margin] || 0) - width = [max_width, width].min unless max_width.nil? - width = [image_info.width, width].min + width = [image_info.width, opt_image_max_width(style), opts[:custom_max_width], width].compact.min options[:scale] = [width / image_info.width.to_f, 1].min options[:position] = :right if (opts[:image_classes] || '') =~ /\bright\b/ options diff --git a/lib/md_to_pdf/elements/table.rb b/lib/md_to_pdf/elements/table.rb index a1e619a..36aa429 100644 --- a/lib/md_to_pdf/elements/table.rb +++ b/lib/md_to_pdf/elements/table.rb @@ -147,7 +147,7 @@ def build_pdf_table(table, cell_style, data, column_alignments) end end - def make_subtable(cell_data, opts, alignment) + def make_subtable(cell_data, opts, alignment, column_count) rows = [] row = [] cell_data.each do |elem| @@ -155,7 +155,7 @@ def make_subtable(cell_data, opts, alignment) rows.push([make_subtable_cell(row, opts)]) unless row.empty? image_file = image_url_to_local_file(elem[:image]) unless image_file.nil? || image_file.empty? - rows.push([image_in_table_column(image_file, alignment).merge({ borders: [] })]) + rows.push([image_in_table_column(image_file, alignment).merge({ borders: [], custom_max_width: elem[:custom_max_width] })]) end row = [] else @@ -165,7 +165,7 @@ def make_subtable(cell_data, opts, alignment) rows.push([make_subtable_cell(row, opts)]) unless row.empty? return make_table_cell([{ text: '' }], opts) if rows.empty? - @pdf.make_table(rows, {}) do + @pdf.make_table(rows, column_widths: [@pdf.bounds.width / column_count]) do columns(0).align = alignment unless alignment == nil end end @@ -180,20 +180,20 @@ def build_table_data(data_rows, column_alignments, opts) data_rows.each do |data_row| cells_row = [] data_row.each_with_index do |cell_data, index| - cells_row.push(make_table_cell_or_subtable(cell_data, opts, column_alignments[index])) + cells_row.push(make_table_cell_or_subtable(cell_data, opts, column_alignments[index], data_row.length)) end data.push(cells_row) end data end - def make_table_cell_or_subtable(cell_data, opts, alignment) + def make_table_cell_or_subtable(cell_data, opts, alignment, column_count) image_data = cell_data.find { |elem| elem.key?(:image) } if image_data image_file = image_url_to_local_file(image_data[:image]) return image_in_table_column(image_file, alignment) if !(image_file.nil? || image_file.empty?) && cell_data.length === 1 - return make_subtable(cell_data, opts, alignment) + return make_subtable(cell_data, opts, alignment, column_count) end make_table_cell(cell_data, opts) end diff --git a/lib/md_to_pdf/ext/prawn-table/table/cell/cell/image.rb b/lib/md_to_pdf/ext/prawn-table/table/cell/cell/image.rb index f26a2ea..5329589 100644 --- a/lib/md_to_pdf/ext/prawn-table/table/cell/cell/image.rb +++ b/lib/md_to_pdf/ext/prawn-table/table/cell/cell/image.rb @@ -1,15 +1,21 @@ Prawn::Table::Cell::Image.prepend(Module.new do def initialize(pdf, point, options = {}) @image_options = {} + custom_max_width = options.delete(:custom_max_width) super - @pdf_object, @image_info = @pdf.build_image_object(@file) - @natural_width, @natural_height = @image_info.calc_image_dimensions(@image_options) + natural_width(custom_max_width) if custom_max_width && custom_max_width > 0 && custom_max_width < @natural_width + natural_width(@pdf.bounds.width) if @natural_width > @pdf.bounds.width + end + + def natural_width(new_width) + @natural_height = @natural_height * (new_width / @natural_width) + @natural_width = new_width end def width=(new_width) - @width = new_width - @height = (@natural_height * (new_width / @natural_width)) + @width = [new_width, @natural_width].min + @height = (@natural_height * (@width / @natural_width)) end def natural_content_width diff --git a/lib/md_to_pdf/ext/prawn-table/table/table.rb b/lib/md_to_pdf/ext/prawn-table/table/table.rb index a950f3e..005ef65 100644 --- a/lib/md_to_pdf/ext/prawn-table/table/table.rb +++ b/lib/md_to_pdf/ext/prawn-table/table/table.rb @@ -49,21 +49,23 @@ def distribute_to_available_space(needed) end def reduce_to_available_space(needed) - cols = natural_split_column_widths.each_with_index - .map { |w, index| { min: w, max: natural_column_widths[index], index: index } } - .sort_by { |e| -e[:max] } - steps = needed / natural_split_column_widths.length + split_widths = natural_split_column_widths + return [[20, split_widths[0] - needed].max] if split_widths.length == 1 - col = cols.first + cols = split_widths.each_with_index + .map { |w, index| { min: w, max: natural_column_widths[index], index: index } } + steps = needed / (split_widths.length * 2) + + col = cols.max_by { |e| e[:max] } while needed > Prawn::FLOAT_PRECISION reduce = [steps, needed].min col[:min] -= reduce col[:max] -= reduce needed -= reduce - col = cols.max_by { |e| e[:max] } + col = cols.max_by { |e| e[:min] } end - cols.sort_by { |e| e[:index] }.map { |e| e[:min] } + cols.map { |e| e[:min] } end def recalculate_positions diff --git a/lib/md_to_pdf/utils/common.rb b/lib/md_to_pdf/utils/common.rb index f8a5caf..86407fe 100644 --- a/lib/md_to_pdf/utils/common.rb +++ b/lib/md_to_pdf/utils/common.rb @@ -58,11 +58,19 @@ def parse_pt(something) ft2pt(value) when 'in' in2pt(value) + when 'px' + csspx2pt(value) else value end end + def csspx2pt(css_px) + # only css pixels are supported, not device pixels + # https://github.com/prawnpdf/prawn/pull/879 + css_px * 0.75 + end + def number_unit(string) value = string.to_f # => -123.45 unit = string[/[a-zA-Z]+/] # => "min" diff --git a/spec/fixtures/image/html.md b/spec/fixtures/image/html.md index 82d3b43..fd23c92 100644 --- a/spec/fixtures/image/html.md +++ b/spec/fixtures/image/html.md @@ -1,3 +1,6 @@ + + + # An image in the middle of a headline diff --git a/spec/fixtures/image/image.md b/spec/fixtures/image/image.md index 8981fff..75db693 100644 --- a/spec/fixtures/image/image.md +++ b/spec/fixtures/image/image.md @@ -1 +1 @@ -Floating text above ![Dummy image](demo.jpg) and below image +Floating text above ![Dummy image](demo.jpg) and below image ![Dummy image](logo.png) diff --git a/spec/fixtures/image/in_table.md b/spec/fixtures/image/in_table.md index 72782d5..08e5c45 100644 --- a/spec/fixtures/image/in_table.md +++ b/spec/fixtures/image/in_table.md @@ -3,7 +3,7 @@

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor

-

+

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor 

@@ -22,10 +22,10 @@

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor

-

+

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor 

-

+

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor

@@ -40,7 +40,7 @@

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, 

-

+

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor 

diff --git a/spec/fixtures/image/large_in_table.md b/spec/fixtures/image/large_in_table.md new file mode 100644 index 0000000..44ee06b --- /dev/null +++ b/spec/fixtures/image/large_in_table.md @@ -0,0 +1,8 @@ +With image style.width: + +

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, 

sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. 

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est L

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est L

Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est L

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est L

+ + +Without image style.width: + +

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, 

sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. 

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est L

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est L

Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est L

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est L

diff --git a/spec/markdown_to_pdf/image_spec.rb b/spec/markdown_to_pdf/image_spec.rb index 4da0ca0..0b5c783 100644 --- a/spec/markdown_to_pdf/image_spec.rb +++ b/spec/markdown_to_pdf/image_spec.rb @@ -10,93 +10,252 @@ expect_pdf([ { x: 36.0, y: 747.384, text: "Floating text above" }, { x: 36.0, y: 306.507, text: "and below image" }]) - expect_images_in_pdf(1) + expect_pdf_images([ + { x: 36.0, y: 315.123, width: 540.0, height: 427.005 }, + { x: 164.0, y: 215.251, width: 284.0, height: 86.0 }]) end it 'creates image with classes' do - generator.parse_file('image/classes.md') + generator.parse_file('image/classes.md', + { + image_classes: { + small: { max_width: 100 }, + left: { margin: 0, align: "left" }, + center: { margin: 0, align: "center" }, + right: { margin: 0, align: "right" } + } + }) expect_pdf([]) - expect_images_in_pdf(3) + expect_pdf_images([ + { x: 36.0, y: 676.925, width: 100.0, height: 79.075 }, + { x: 256.0, y: 597.85, width: 100.0, height: 79.075 }, + { x: 476.0, y: 518.775, width: 100.0, height: 79.075 }]) end it 'creates image by html' do - generator.parse_file('image/html.md') + generator.parse_file('image/html.md', { image: { max_width: 300 } }) expect_pdf([ - { x: 36.0, y: 320.379, text: "An image in the middle of" }, - { x: 36.0, y: 320.379, text: "a headline" }, - { x: 36.0, y: 306.507, text: "•" }, - { x: 43.536, y: 306.507, text: "List 1" }, - { x: 36.0, y: 292.635, text: "•" }, - { x: 43.536, y: 292.635, text: "An image in the middle of" }, - { x: 43.536, y: 326.33809, text: "a list" }, - { x: 36.0, y: 312.46609, text: "•" }, - { x: 43.536, y: 312.46609, text: "List 3" }, + { x: 36.0, y: 450.85275, text: "An image in the middle of" }, + { x: 36.0, y: 199.75575, text: "a headline" }, + { x: 36.0, y: 185.88375, text: "•" }, + { x: 43.536, y: 185.88375, text: "List 1" }, + { x: 36.0, y: 172.01175, text: "•" }, + { x: 43.536, y: 172.01175, text: "An image in the middle of" }, + { x: 43.536, y: 510.159, text: "a list" }, + { x: 36.0, y: 496.287, text: "•" }, + { x: 43.536, y: 496.287, text: "List 3" }, { x: 41.0, y: 739.756, text: "Quote 1 An image in the middle of" }, { x: 41.0, y: 288.879, text: "a quote Quote 2" }]) - expect_images_in_pdf(4) + expect_pdf_images([ + { x: 268.5, y: 696.69375, width: 75.0, height: 59.30625 }, + { x: 156.0, y: 459.46875, width: 300.0, height: 237.225 }, + { x: 156.0, y: 208.37175, width: 300.0, height: 237.225 }, + { x: 159.768, y: 518.775, width: 300.0, height: 237.225 }, + { x: 41.0, y: 309.623, width: 527.35378, height: 417.005 }]) end it 'creates image by footnote' do - generator.parse_file('image/footnote.md') - expect_pdf([{ x: 36.0, y: 320.379, text: "The demo image" }]) - expect_images_in_pdf(1) + generator.parse_file('image/footnote.md', { image: { max_width: 100 } }) + expect_pdf([{ x: 36.0, y: 668.309, text: "The demo image" }]) + expect_pdf_images([{ x: 256.0, y: 676.925, width: 100.0, height: 79.075 }]) end it 'creates image with caption' do - generator.parse_file('image/caption.md', { image: { caption: { align: :center, size: 10 } } }) - expect_pdf([{ x: 264.315, y: 321.815, text: "Image with caption" }]) - expect_images_in_pdf(1) + generator.parse_file('image/caption.md', { image: { max_width: 100, caption: { align: :center, size: 10 } } }) + expect_pdf([{ x: 264.315, y: 669.745, text: "Image with caption" }]) + expect_pdf_images([{ x: 256.0, y: 676.925, width: 100.0, height: 79.075 }]) end it 'creates image with figure and caption' do - generator.parse_file('image/figure.md', { image: { margin_bottom: 100, caption: { align: :center, size: 10 } } }) - expect_pdf([{ x: 273.85, y: 321.815, text: "Dummy image" }]) - expect_images_in_pdf(1) + generator.parse_file('image/figure.md', { image: { max_width: 100, margin_bottom: 100, caption: { align: :center, size: 10 } } }) + expect_pdf([{ x: 273.85, y: 669.745, text: "Dummy image" }]) + expect_pdf_images([{ x: 256.0, y: 676.925, width: 100.0, height: 79.075 }]) end it 'creates image in table' do - generator.parse_file('image/in_table.md', { image: { margin_bottom: 100, caption: { align: :center, size: 10 } } }) + generator.parse_file('image/in_table.md', { headless_table: { auto_width: true } }) expect_pdf([ { x: 41.0, y: 739.756, text: "Lorem ipsum dolor sit amet, consetetur" }, { x: 41.0, y: 725.884, text: "sadipscing elitr, sed diam nonumy eirmod tempor" }, { x: 41.0, y: 712.012, text: "" }, - { x: 41.0, y: 474.6375, text: "Lorem ipsum dolor sit amet, consetetur" }, - { x: 41.0, y: 460.7655, text: "sadipscing elitr, sed diam nonumy eirmod" }, - { x: 41.0, y: 446.8935, text: "tempor " }, + { x: 41.0, y: 606.37944, text: "Lorem ipsum dolor sit amet, consetetur" }, + { x: 41.0, y: 592.50744, text: "sadipscing elitr, sed diam nonumy eirmod" }, + { x: 41.0, y: 578.63544, text: "tempor " }, { x: 306.0, y: 744.756, text: "Lorem ipsum dolor sit amet, consetetur sadipscing" }, { x: 306.0, y: 730.884, text: "elitr, sed diam nonumy eirmod tempor invidunt ut" }, { x: 306.0, y: 717.012, text: "labore et dolore magna aliquyam erat, sed diam" }, { x: 306.0, y: 703.14, text: "voluptua. At vero eos et accusam et justo duo" }, { x: 306.0, y: 689.268, text: "dolores et ea rebum. Stet clita kasd gubergren, no" }, { x: 306.0, y: 675.396, text: "sea takimata sanctus est L" }, - { x: 36.0, y: 744.756, text: "Lorem ipsum dolor sit amet, consetetur sadipscing" }, - { x: 36.0, y: 730.884, text: "elitr, sed diam nonumy" }, - { x: 311.0, y: 739.756, text: "Lorem ipsum dolor sit amet, consetetur" }, - { x: 311.0, y: 725.884, text: "sadipscing elitr, sed diam nonumy eirmod tempor" }, - { x: 311.0, y: 712.012, text: "" }, - { x: 311.0, y: 474.6375, text: "Lorem ipsum dolor sit amet, consetetur" }, - { x: 311.0, y: 460.7655, text: "sadipscing elitr, sed diam nonumy eirmod" }, - { x: 311.0, y: 446.8935, text: "tempor " }, - { x: 311.0, y: 209.519, text: "Lorem ipsum dolor sit amet, consetetur" }, - { x: 311.0, y: 195.647, text: "sadipscing elitr, sed diam nonumy eirmod tempor" }, - { x: 311.0, y: 181.775, text: "" }, - { x: 36.0, y: 744.756, text: "Lorem ipsum dolor sit amet," }, - { x: 36.0, y: 730.884, text: "consetetur sadipscing elitr, sed" }, - { x: 36.0, y: 717.012, text: "diam nonumy" }, - { x: 216.0, y: 744.756, text: "Lorem ipsum dolor sit amet," }, - { x: 216.0, y: 730.884, text: "consetetur sadipscing elitr, sed" }, - { x: 216.0, y: 717.012, text: "diam nonumy" }, - { x: 401.0, y: 739.756, text: "Lorem ipsum dolor sit amet," }, - { x: 401.0, y: 725.884, text: "consetetur sadipscing elitr, sed" }, - { x: 401.0, y: 712.012, text: "diam nonumy eirmod tempor" }, - { x: 401.0, y: 698.14, text: "invidunt ut labore et dolore" }, - { x: 401.0, y: 684.268, text: "magna aliquyam erat, " }, - { x: 401.0, y: 670.396, text: "" }, - { x: 401.0, y: 504.189, text: "Lorem ipsum dolor sit amet," }, - { x: 401.0, y: 490.317, text: "consetetur sadipscing elitr, sed" }, - { x: 401.0, y: 476.445, text: "diam nonumy eirmod tempor " }, - { x: 41.0, y: 452.573, text: "" }, - { x: 41.0, y: 265.17987, text: "" }]) - expect_images_in_pdf(5) + { x: 36.0, y: 559.76344, text: "Lorem ipsum dolor sit amet, consetetur sadipscing" }, + { x: 36.0, y: 545.89144, text: "elitr, sed diam nonumy" }, + { x: 311.0, y: 554.76344, text: "Lorem ipsum dolor sit amet, consetetur" }, + { x: 311.0, y: 540.89144, text: "sadipscing elitr, sed diam nonumy eirmod tempor" }, + { x: 311.0, y: 527.01944, text: "" }, + { x: 311.0, y: 421.38687, text: "Lorem ipsum dolor sit amet, consetetur" }, + { x: 311.0, y: 407.51487, text: "sadipscing elitr, sed diam nonumy eirmod" }, + { x: 311.0, y: 393.64287, text: "tempor " }, + { x: 311.0, y: 288.01031, text: "Lorem ipsum dolor sit amet, consetetur" }, + { x: 311.0, y: 274.13831, text: "sadipscing elitr, sed diam nonumy eirmod tempor" }, + { x: 311.0, y: 260.26631, text: "" }, + { x: 36.0, y: 241.39431, text: "Lorem ipsum dolor sit amet, consetetur" }, + { x: 36.0, y: 227.52231, text: "sadipscing elitr, sed diam nonumy" }, + { x: 257.324, y: 241.39431, text: "Lorem ipsum dolor sit" }, + { x: 257.324, y: 227.52231, text: "amet, consetetur" }, + { x: 257.324, y: 213.65031, text: "sadipscing elitr, sed diam" }, + { x: 257.324, y: 199.77831, text: "nonumy" }, + { x: 401.0, y: 236.39431, text: "Lorem ipsum dolor sit amet," }, + { x: 401.0, y: 222.52231, text: "consetetur sadipscing elitr, sed" }, + { x: 401.0, y: 208.65031, text: "diam nonumy eirmod tempor" }, + { x: 401.0, y: 194.77831, text: "invidunt ut labore et dolore" }, + { x: 401.0, y: 180.90631, text: "magna aliquyam erat, " }, + { x: 401.0, y: 167.03431, text: "" }, + { x: 401.0, y: 88.65527, text: "Lorem ipsum dolor sit amet," }, + { x: 401.0, y: 74.78327, text: "consetetur sadipscing elitr, sed" }, + { x: 401.0, y: 60.91127, text: "diam nonumy eirmod tempor " }, + { x: 41.0, y: 739.756, text: "" }, + { x: 41.0, y: 629.884, text: "" }]) + expect_pdf_images([ + { x: 41.0, y: 627.12344, width: 236.97674, height: 71.76056 }, + { x: 311.0, y: 442.13087, width: 236.97674, height: 71.76056 }, + { x: 311.0, y: 308.75431, width: 236.97674, height: 71.76056 }, + { x: 401.0, y: 109.39927, width: 146.97674, height: 44.50704 }, + { x: 41.0, y: 650.628, width: 250.97674, height: 76.0 }]) + end + + it 'creates large image in table' do + generator.parse_file('image/large_in_table.md', { headless_table: { auto_width: true } }) + expect_pdf([ + { x: 36.0, y: 747.384, text: "With image style.width:" }, + { x: 41.0, y: 725.884, text: "Lorem ipsum dolor sit amet, consetetur" }, + { x: 41.0, y: 712.012, text: "sadipscing elitr, sed diam nonumy eirmod" }, + { x: 41.0, y: 698.14, text: "tempor invidunt ut labore et dolore magna" }, + { x: 41.0, y: 684.268, text: "aliquyam erat, " }, + { x: 41.0, y: 505.01362, text: "sed diam voluptua. At vero eos et accusam et" }, + { x: 41.0, y: 491.14162, text: "justo duo dolores et ea rebum. " }, + { x: 298.648, y: 730.884, text: "Lorem ipsum dolor sit" }, + { x: 298.648, y: 717.012, text: "amet, consetetur" }, + { x: 298.648, y: 703.14, text: "sadipscing elitr, sed diam" }, + { x: 298.648, y: 689.268, text: "nonumy eirmod tempor" }, + { x: 298.648, y: 675.396, text: "invidunt ut labore et" }, + { x: 298.648, y: 661.524, text: "dolore magna aliquyam" }, + { x: 298.648, y: 647.652, text: "erat, sed diam voluptua." }, + { x: 298.648, y: 633.78, text: "At vero eos et accusam et" }, + { x: 298.648, y: 619.908, text: "justo duo dolores et ea" }, + { x: 298.648, y: 606.036, text: "rebum. Stet clita kasd" }, + { x: 298.648, y: 592.164, text: "gubergren, no sea" }, + { x: 298.648, y: 578.292, text: "takimata sanctus est L" }, + { x: 437.324, y: 730.884, text: "Lorem ipsum dolor sit" }, + { x: 437.324, y: 717.012, text: "amet, consetetur" }, + { x: 437.324, y: 703.14, text: "sadipscing elitr, sed diam" }, + { x: 437.324, y: 689.268, text: "nonumy eirmod tempor" }, + { x: 437.324, y: 675.396, text: "invidunt ut labore et" }, + { x: 437.324, y: 661.524, text: "dolore magna aliquyam" }, + { x: 437.324, y: 647.652, text: "erat, sed diam voluptua." }, + { x: 437.324, y: 633.78, text: "At vero eos et accusam et" }, + { x: 437.324, y: 619.908, text: "justo duo dolores et ea" }, + { x: 437.324, y: 606.036, text: "rebum. Stet clita kasd" }, + { x: 437.324, y: 592.164, text: "gubergren, no sea" }, + { x: 437.324, y: 578.292, text: "takimata sanctus est L" }, + { x: 36.0, y: 472.26962, text: "Stet clita kasd gubergren, no sea takimata" }, + { x: 36.0, y: 458.39762, text: "sanctus est Lorem ipsum dolor sit amet. Lorem" }, + { x: 36.0, y: 444.52562, text: "ipsum dolor sit amet, consetetur sadipscing elitr," }, + { x: 36.0, y: 430.65362, text: "sed diam nonumy eirmod tempor invidunt ut" }, + { x: 36.0, y: 416.78162, text: "labore et dolore magna aliquyam erat, sed diam" }, + { x: 36.0, y: 402.90962, text: "voluptua. At vero eos et accusam et justo duo" }, + { x: 36.0, y: 389.03762, text: "dolores et ea rebum. Stet clita kasd gubergren," }, + { x: 36.0, y: 375.16562, text: "no sea takimata sanctus est Lorem ipsum dolor" }, + { x: 36.0, y: 361.29362, text: "sit amet." }, + { x: 298.648, y: 472.26962, text: "Lorem ipsum dolor sit" }, + { x: 298.648, y: 458.39762, text: "amet, consetetur" }, + { x: 298.648, y: 444.52562, text: "sadipscing elitr, sed diam" }, + { x: 298.648, y: 430.65362, text: "nonumy eirmod tempor" }, + { x: 298.648, y: 416.78162, text: "invidunt ut labore et" }, + { x: 298.648, y: 402.90962, text: "dolore magna aliquyam" }, + { x: 298.648, y: 389.03762, text: "erat, sed diam voluptua." }, + { x: 298.648, y: 375.16562, text: "At vero eos et accusam et" }, + { x: 298.648, y: 361.29362, text: "justo duo dolores et ea" }, + { x: 298.648, y: 347.42162, text: "rebum. Stet clita kasd" }, + { x: 298.648, y: 333.54962, text: "gubergren, no sea" }, + { x: 298.648, y: 319.67762, text: "takimata sanctus est L" }, + { x: 437.324, y: 472.26962, text: "Lorem ipsum dolor sit" }, + { x: 437.324, y: 458.39762, text: "amet, consetetur" }, + { x: 437.324, y: 444.52562, text: "sadipscing elitr, sed diam" }, + { x: 437.324, y: 430.65362, text: "nonumy eirmod tempor" }, + { x: 437.324, y: 416.78162, text: "invidunt ut labore et" }, + { x: 437.324, y: 402.90962, text: "dolore magna aliquyam" }, + { x: 437.324, y: 389.03762, text: "erat, sed diam voluptua." }, + { x: 437.324, y: 375.16562, text: "At vero eos et accusam et" }, + { x: 437.324, y: 361.29362, text: "justo duo dolores et ea" }, + { x: 437.324, y: 347.42162, text: "rebum. Stet clita kasd" }, + { x: 437.324, y: 333.54962, text: "gubergren, no sea" }, + { x: 437.324, y: 319.67762, text: "takimata sanctus est L" }, + { x: 36.0, y: 308.43362, text: "Without image style.width:" }, + { x: 41.0, y: 739.756, text: "Lorem ipsum dolor sit amet, consetetur" }, + { x: 41.0, y: 725.884, text: "sadipscing elitr, sed diam nonumy eirmod" }, + { x: 41.0, y: 712.012, text: "tempor invidunt ut labore et dolore magna" }, + { x: 41.0, y: 698.14, text: "aliquyam erat, " }, + { x: 41.0, y: 466.57909, text: "sed diam voluptua. At vero eos et accusam et" }, + { x: 41.0, y: 452.70709, text: "justo duo dolores et ea rebum. " }, + { x: 298.648, y: 744.756, text: "Lorem ipsum dolor sit" }, + { x: 298.648, y: 730.884, text: "amet, consetetur" }, + { x: 298.648, y: 717.012, text: "sadipscing elitr, sed diam" }, + { x: 298.648, y: 703.14, text: "nonumy eirmod tempor" }, + { x: 298.648, y: 689.268, text: "invidunt ut labore et" }, + { x: 298.648, y: 675.396, text: "dolore magna aliquyam" }, + { x: 298.648, y: 661.524, text: "erat, sed diam voluptua." }, + { x: 298.648, y: 647.652, text: "At vero eos et accusam et" }, + { x: 298.648, y: 633.78, text: "justo duo dolores et ea" }, + { x: 298.648, y: 619.908, text: "rebum. Stet clita kasd" }, + { x: 298.648, y: 606.036, text: "gubergren, no sea" }, + { x: 298.648, y: 592.164, text: "takimata sanctus est L" }, + { x: 437.324, y: 744.756, text: "Lorem ipsum dolor sit" }, + { x: 437.324, y: 730.884, text: "amet, consetetur" }, + { x: 437.324, y: 717.012, text: "sadipscing elitr, sed diam" }, + { x: 437.324, y: 703.14, text: "nonumy eirmod tempor" }, + { x: 437.324, y: 689.268, text: "invidunt ut labore et" }, + { x: 437.324, y: 675.396, text: "dolore magna aliquyam" }, + { x: 437.324, y: 661.524, text: "erat, sed diam voluptua." }, + { x: 437.324, y: 647.652, text: "At vero eos et accusam et" }, + { x: 437.324, y: 633.78, text: "justo duo dolores et ea" }, + { x: 437.324, y: 619.908, text: "rebum. Stet clita kasd" }, + { x: 437.324, y: 606.036, text: "gubergren, no sea" }, + { x: 437.324, y: 592.164, text: "takimata sanctus est L" }, + { x: 36.0, y: 433.83509, text: "Stet clita kasd gubergren, no sea takimata" }, + { x: 36.0, y: 419.96309, text: "sanctus est Lorem ipsum dolor sit amet. Lorem" }, + { x: 36.0, y: 406.09109, text: "ipsum dolor sit amet, consetetur sadipscing elitr," }, + { x: 36.0, y: 392.21909, text: "sed diam nonumy eirmod tempor invidunt ut" }, + { x: 36.0, y: 378.34709, text: "labore et dolore magna aliquyam erat, sed diam" }, + { x: 36.0, y: 364.47509, text: "voluptua. At vero eos et accusam et justo duo" }, + { x: 36.0, y: 350.60309, text: "dolores et ea rebum. Stet clita kasd gubergren," }, + { x: 36.0, y: 336.73109, text: "no sea takimata sanctus est Lorem ipsum dolor" }, + { x: 36.0, y: 322.85909, text: "sit amet." }, + { x: 298.648, y: 433.83509, text: "Lorem ipsum dolor sit" }, + { x: 298.648, y: 419.96309, text: "amet, consetetur" }, + { x: 298.648, y: 406.09109, text: "sadipscing elitr, sed diam" }, + { x: 298.648, y: 392.21909, text: "nonumy eirmod tempor" }, + { x: 298.648, y: 378.34709, text: "invidunt ut labore et" }, + { x: 298.648, y: 364.47509, text: "dolore magna aliquyam" }, + { x: 298.648, y: 350.60309, text: "erat, sed diam voluptua." }, + { x: 298.648, y: 336.73109, text: "At vero eos et accusam et" }, + { x: 298.648, y: 322.85909, text: "justo duo dolores et ea" }, + { x: 298.648, y: 308.98709, text: "rebum. Stet clita kasd" }, + { x: 298.648, y: 295.11509, text: "gubergren, no sea" }, + { x: 298.648, y: 281.24309, text: "takimata sanctus est L" }, + { x: 437.324, y: 433.83509, text: "Lorem ipsum dolor sit" }, + { x: 437.324, y: 419.96309, text: "amet, consetetur" }, + { x: 437.324, y: 406.09109, text: "sadipscing elitr, sed diam" }, + { x: 437.324, y: 392.21909, text: "nonumy eirmod tempor" }, + { x: 437.324, y: 378.34709, text: "invidunt ut labore et" }, + { x: 437.324, y: 364.47509, text: "dolore magna aliquyam" }, + { x: 437.324, y: 350.60309, text: "erat, sed diam voluptua." }, + { x: 437.324, y: 336.73109, text: "At vero eos et accusam et" }, + { x: 437.324, y: 322.85909, text: "justo duo dolores et ea" }, + { x: 437.324, y: 308.98709, text: "rebum. Stet clita kasd" }, + { x: 437.324, y: 295.11509, text: "gubergren, no sea" }, + { x: 437.324, y: 281.24309, text: "takimata sanctus est L" }]) + expect_pdf_images([ + { x: 41.0, y: 525.75762, width: 183.85378, height: 145.38238 }, + { x: 41.0, y: 487.32309, width: 250.00178, height: 197.68891 }]) end end diff --git a/spec/pdf_helpers.rb b/spec/pdf_helpers.rb index ece0e38..74dcc59 100644 --- a/spec/pdf_helpers.rb +++ b/spec/pdf_helpers.rb @@ -65,7 +65,7 @@ def page result end - def images + def xobjects PDF::Inspector::XObject.analyze(pdf) end @@ -84,6 +84,23 @@ def out .gsub(':text=>', 'text:')})" end + def images + all_calls = calls + image_calls = calls.each_index.select { |i| all_calls[i][0] == :invoke_xobject } # .find_index { |call| call[0] == :invoke_xobject } + image_calls.map do |call_index| + call = all_calls[call_index - 1] + { x: call[5], y: call[6], width: call[1], height: call[4] } + end + end + + def out_images + puts "expect_pdf_images(#{images.to_s + .gsub('{:x=>', "\n{x:") + .gsub(':width=>', 'width:') + .gsub(':height=>', 'height:') + .gsub(':y=>', 'y:')})" + end + def rectangles rects = [] calls.each_with_index do |call, i| @@ -110,8 +127,8 @@ def expect_pdf(data) expect(page).to eq(data) end - def expect_images_in_pdf(amount) - expect(images.xobject_streams.size).to eq(amount) + def expect_pdf_images(data) + expect(images).to eq(data) end def pdf_raw_color(color)