From e619567f35bb74ab4d697bec25656eb8e9c06d6b Mon Sep 17 00:00:00 2001 From: Thomas Leitner Date: Wed, 23 Aug 2023 22:49:47 +0200 Subject: [PATCH] Experimental horizontal filling support --- lib/hexapdf/layout/style.rb | 1 + lib/hexapdf/layout/text_fragment.rb | 16 ++++++++++++++++ lib/hexapdf/layout/text_layouter.rb | 13 +++++++++++++ 3 files changed, 30 insertions(+) diff --git a/lib/hexapdf/layout/style.rb b/lib/hexapdf/layout/style.rb index cdcdfc8c..031b0d60 100644 --- a/lib/hexapdf/layout/style.rb +++ b/lib/hexapdf/layout/style.rb @@ -1293,6 +1293,7 @@ def update(**properties) "{type: value, value: extra_arg} : value))", extra_args: ", extra_arg = nil"}], [:last_line_gap, false, {valid_values: [true, false]}], + [:fill_horizontal, nil], [:background_color, nil], [:background_alpha, 1], [:padding, "Quad.new(0)", {setter: "Quad.new(value)"}], diff --git a/lib/hexapdf/layout/text_fragment.rb b/lib/hexapdf/layout/text_fragment.rb index 79583422..a6506e07 100644 --- a/lib/hexapdf/layout/text_fragment.rb +++ b/lib/hexapdf/layout/text_fragment.rb @@ -283,6 +283,22 @@ def valign :text end + def fill_horizontal(new_width) + factor, rest = new_width.divmod(width) + items = @items * factor + rest = @items.inject(rest) do |available_width, item| + new_width = available_width - style.scaled_item_width(item) + break available_width if new_width < 0 + items << item + new_width + end + # spacing needs to be correctly scaled + spacing = - (rest / (items.size - 1) / style.scaled_font_size) + items = items.each_with_object([]) {|i, result| result << i << spacing } + items.pop + dup_attributes(items) + end + # Clears all cached values. # # This method needs to be called if the fragment's items or attributes are changed! diff --git a/lib/hexapdf/layout/text_layouter.rb b/lib/hexapdf/layout/text_layouter.rb index 76dd6165..453362c8 100644 --- a/lib/hexapdf/layout/text_layouter.rb +++ b/lib/hexapdf/layout/text_layouter.rb @@ -363,6 +363,7 @@ def initialize(items, width_block) @last_breakpoint_index = 0 @last_breakpoint_line_items_index = 0 @break_prohibited_state = false + @fill_horizontal = false @height_calc = Line::HeightCalculator.new @line = DummyLine.new(0, 0) @@ -508,6 +509,7 @@ def add_box_item(item) return false unless @width + item.width <= @available_width @line_items.concat(@glue_items).push(item) @width += item.width + @fill_horizontal ||= item.style.fill_horizontal @glue_items.clear true end @@ -547,6 +549,17 @@ def item_fits_on_line?(item) # Creates a Line object from the current line items. def create_line + if @fill_horizontal + rest_width = @available_width - @width + indices = [] + @line_items.each_with_index do |item, index| + next unless item.style.fill_horizontal + indices << [index, item.style.fill_horizontal] + rest_width += item.width + end + unit_width = rest_width / indices.sum(&:last) + indices.each {|i, count| @line_items[i] = @line_items[i].fill_horizontal(unit_width * count) } + end Line.new(@line_items) end