Skip to content

Commit

Permalink
Merge branch 'main' into feature/mermaid-support
Browse files Browse the repository at this point in the history
  • Loading branch information
as-op authored Jun 19, 2024
2 parents b4d3609 + 8772c79 commit eac592c
Show file tree
Hide file tree
Showing 25 changed files with 464 additions and 83 deletions.
11 changes: 1 addition & 10 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,13 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
- name: Run rspec
run: bundle exec rspec
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
- name: Run rubocop
run: bundle exec rubocop
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
## [Unreleased]

## [0.0.27] - 2024-06-11

- fix(tables): better image handling in table autosizing
- fix(tables): better image handling in table cells

## [0.0.26] - 2024-02-15

- fix(tasklists): support nested tasklists and tasklist items with or without any text
Expand Down
8 changes: 4 additions & 4 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ gemspec

group :development, :test do
gem 'pdf-inspector', '~> 1.3'
gem 'rake', '~> 13.1'
gem 'rspec', '~> 3.12'
gem 'rubocop', '~> 1.59'
gem 'rake', '~> 13.2'
gem 'rspec', '~> 3.1'
gem 'rubocop', '~> 1.6'
gem 'rubocop-rake', '~> 0.6'
gem 'rubocop-rspec', '~> 2.26'
gem 'rubocop-rspec', '~> 2.3'
end
22 changes: 11 additions & 11 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
PATH
remote: .
specs:
md_to_pdf (0.0.26)
md_to_pdf (0.0.27)
color_conversion (~> 0.1)
front_matter_parser (~> 1.0)
json-schema (~> 4.1)
json-schema (~> 4.3)
markly (~> 0.10)
matrix (~> 0.4)
nokogiri (~> 1.1)
Expand All @@ -25,13 +25,13 @@ GEM
front_matter_parser (1.0.1)
hashery (2.1.2)
json (2.7.1)
json-schema (4.1.1)
json-schema (4.3.0)
addressable (>= 2.8)
language_server-protocol (3.17.0.3)
markly (0.10.0)
matrix (0.4.2)
mini_portile2 (2.8.5)
nokogiri (1.16.2)
mini_portile2 (2.8.7)
nokogiri (1.16.5)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
parallel (1.24.0)
Expand All @@ -52,10 +52,10 @@ GEM
ttfunk (~> 1.7)
prawn-table (0.2.2)
prawn (>= 1.3.0, < 3.0.0)
public_suffix (5.0.4)
public_suffix (5.0.5)
racc (1.7.3)
rainbow (3.1.1)
rake (13.1.0)
rake (13.2.1)
regexp_parser (2.9.0)
rexml (3.2.6)
rspec (3.13.0)
Expand Down Expand Up @@ -110,11 +110,11 @@ PLATFORMS
DEPENDENCIES
md_to_pdf!
pdf-inspector (~> 1.3)
rake (~> 13.1)
rspec (~> 3.12)
rubocop (~> 1.59)
rake (~> 13.2)
rspec (~> 3.1)
rubocop (~> 1.6)
rubocop-rake (~> 0.6)
rubocop-rspec (~> 2.26)
rubocop-rspec (~> 2.3)

BUNDLED WITH
2.3.13
2 changes: 2 additions & 0 deletions lib/md_to_pdf/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
require 'md_to_pdf/ext/prawn-table/table/natural_split_generator'
require 'md_to_pdf/ext/prawn-table/table/cell/cell'
require 'md_to_pdf/ext/prawn-table/table/cell/cell/text'
require 'md_to_pdf/ext/prawn-table/table/cell/cell/image'
require 'md_to_pdf/ext/prawn-table/table/cell/cell/subtable'

module MarkdownToPDF
module Core
Expand Down
4 changes: 2 additions & 2 deletions lib/md_to_pdf/elements/blockquote.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
26 changes: 24 additions & 2 deletions lib/md_to_pdf/elements/html.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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)
Expand Down Expand Up @@ -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'
Expand Down
4 changes: 1 addition & 3 deletions lib/md_to_pdf/elements/image.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 8 additions & 7 deletions lib/md_to_pdf/elements/table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,15 +147,15 @@ 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|
if elem.key?(:image)
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
Expand All @@ -165,34 +165,35 @@ 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, { position: alignment }) do
@pdf.make_table(rows, column_widths: [@pdf.bounds.width / column_count]) do
columns(0).align = alignment unless alignment == nil
end
end

def image_in_table_column(image_file, alignment)
{ image: image_file, fit: [100, 100], position: alignment, vposition: :center }
{ image: image_file,
position: alignment, vposition: :center }
end

def build_table_data(data_rows, column_alignments, opts)
data = []
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
Expand Down
6 changes: 6 additions & 0 deletions lib/md_to_pdf/ext/prawn-table/table/cell/cell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,10 @@ def split_width
column_widths[column] = [column_widths[column], split_width_ignoring_span].max
column_widths.values.inject(0, &:+)
end

def width=(new_width)
@min_width = new_width unless defined?(@min_width)
@max_width = new_width unless defined?(@max_width)
@width = new_width
end
end)
36 changes: 36 additions & 0 deletions lib/md_to_pdf/ext/prawn-table/table/cell/cell/image.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Prawn::Table::Cell::Image.prepend(Module.new do
def initialize(pdf, point, options = {})
@image_options = {}
custom_max_width = options.delete(:custom_max_width)
super

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, @natural_width].min
@height = (@natural_height * (@width / @natural_width))
end

def natural_content_width
@width || @natural_width
end

def natural_content_height
@height || @natural_height
end

def draw_content
fit_width = [@width, @natural_width].min - padding_left - padding_right
fit_height = [@height, @natural_height].min - padding_top - padding_bottom
@pdf.embed_image(@pdf_object, @image_info,
@image_options
.merge(fit: [fit_width, fit_height]))
end
end)
9 changes: 9 additions & 0 deletions lib/md_to_pdf/ext/prawn-table/table/cell/cell/subtable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Prawn::Table::Cell::Subtable.prepend(Module.new do
def width=(new_width)
@width = @min_width = @max_width = new_width
@height = nil
@subtable.cells.width = new_width - padding_left
@subtable.recalculate_positions
@width
end
end)
7 changes: 7 additions & 0 deletions lib/md_to_pdf/ext/prawn-table/table/cell/cell/text.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,11 @@ def styled_width_of_longest_word
options = @text_options.reject { |k| %i[style inline_format].include?(k) }
with_font { @pdf.width_of(longest_word, options) }
end

def width=(new_width)
@width = new_width
# recalculate height
@height = nil
content_height
end
end)
21 changes: 14 additions & 7 deletions lib/md_to_pdf/ext/prawn-table/table/table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,28 @@ 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
@natural_row_heights = nil
position_cells
end

def natural_split_column_widths
Expand Down
8 changes: 8 additions & 0 deletions lib/md_to_pdf/utils/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion lib/md_to_pdf/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module MarkdownToPDF
VERSION = '0.0.26'
VERSION = '0.0.27'
end
2 changes: 1 addition & 1 deletion md_to_pdf.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Gem::Specification.new do |s|

s.add_runtime_dependency "color_conversion", ["~> 0.1"]
s.add_runtime_dependency "front_matter_parser", ["~> 1.0"]
s.add_runtime_dependency "json-schema", ["~> 4.1"]
s.add_runtime_dependency "json-schema", ["~> 4.3"]
s.add_runtime_dependency "markly", ["~> 0.10"]
s.add_runtime_dependency "matrix", ["~> 0.4"]
s.add_runtime_dependency "nokogiri", ["~> 1.1"]
Expand Down
Loading

0 comments on commit eac592c

Please sign in to comment.