Skip to content

Commit

Permalink
add foundation to support multi-column layout; add example to docs (PR
Browse files Browse the repository at this point in the history
  • Loading branch information
mojavelinux authored May 18, 2022
1 parent 667e66a commit 9e843a1
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ For a detailed view of what has changed, refer to the {url-repo}/commits/main[co
Improvements::

* use more stable approach to recreating current bounds in scratch document
* add foundation to support multi-column layout for the body of an article (using an extended converter only)

== 2.0.0.rc.1 (2022-05-17) - @mojavelinux

Expand Down
10 changes: 10 additions & 0 deletions docs/modules/extend/examples/pdf-converter-columns.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class PDFConverterColumns < (Asciidoctor::Converter.for 'pdf')
register_for 'pdf'

def traverse node
return super unless node.context == :document && (columns = theme.base_columns)
column_box [0, cursor], columns: columns, width: bounds.width, reflow_margins: true, spacer: theme.base_column_gap do
super
end
end
end
33 changes: 33 additions & 0 deletions docs/modules/extend/pages/use-cases.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ This converter works when a custom theme defines the `image-indent` key, as foll

[,yaml]
----
extends: default
image:
indent: [0.5in, 0]
----
Expand All @@ -259,10 +260,42 @@ You can configure the gap next to and below the image using the `image-float-gap

[,yaml]
----
extends: default
image:
float-gap: [12, 6]
----

== Multiple columns

Asciidoctor PDF does not yet provide multi-column support, where the body of the article is arranged into multiple columns.
However, the converter does provide the foundation for supporting a multi-column layout.
We can tap into that foundation using an extended converter.

The trick is to intercept the `traverse` method and enclose the call in a column box using the `column_box` method.
The `traverse` method is called to render the body, accepting the document as the sole argument.
Since this method is also called for other blocks, we'll need to filter out those calls by looking for the `:document` context.

.Extended converter that arranges the body into columns
[,ruby]
----
include::example$pdf-converter-columns.rb[]
----

WARNING: You may encounter some quirks when using this extended converter.
It's not yet a perfect solution.
For example, it does not handle the index section correctly.
You may have to play around with the code to get the desired result.

You can configure the number of columns and the gap between the columns in the theme file as follows:

[,yaml]
----
extends: default
base:
columns: 2
column-gap: 12
----

== Access page number from inline macro

Although not an extended converter, this use case uses information from the converter in much the same way.
Expand Down
2 changes: 1 addition & 1 deletion lib/asciidoctor/pdf/converter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4256,7 +4256,7 @@ def theme_fill_and_stroke_block category, extent, opts = {}
b_shift, b_gap_color = (b_width ||= 0.5) * 0.5, @page_bg_color
end
ink_caption node_with_caption, category: category if node_with_caption
extent.from.page += 1 unless extent.from.page == page_number # sanity check
extent.from.page += 1 unless extent.from.page == page_number || ColumnBox === bounds # sanity check
float do
extent.each_page do |first_page, last_page|
advance_page unless first_page
Expand Down
18 changes: 18 additions & 0 deletions lib/asciidoctor/pdf/ext/prawn/extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ module Extensions
include ::Asciidoctor::PDF::Sanitizer
include ::Asciidoctor::PDF::TextTransformer

ColumnBox = ::Prawn::Document::ColumnBox

FontAwesomeIconSets = %w(fab far fas)
IconSets = %w(fab far fas fi pf).to_set
IconSetPrefixes = IconSets.map {|it| it + '-' }
Expand Down Expand Up @@ -543,6 +545,18 @@ def hyphenate_text text, hyphenator

# Cursor

# Override the built-in float method to add support for restoring the current column of a ColumnBox
#
def float
original_page_number = page_number
original_y = y
original_column = bounds.instance_variable_get :@current_column if ColumnBox === bounds
yield
go_to_page original_page_number unless page_number == original_page_number
self.y = original_y
bounds.instance_variable_set :@current_column, original_column if original_column
end

# Short-circuits the call to the built-in move_up operation
# when n is 0.
#
Expand Down Expand Up @@ -1122,6 +1136,10 @@ def dry_run keep_together: nil, pages_advanced: 0, single_page: nil, onto: nil,
scratch_pdf.bounds = bounds.dup.tap do |bounds_copy|
bounds_copy.instance_variable_set :@document, scratch_pdf
bounds_copy.instance_variable_set :@parent, saved_bounds
if ColumnBox === bounds_copy
bounds_copy.instance_variable_set :@width, bounds_copy.bare_column_width
bounds_copy.instance_variable_set :@current_column, (bounds_copy.instance_variable_set :@columns, 1) - 1
end
end
scratch_pdf.move_cursor_to cursor unless (scratch_start_at_top = keep_together || pages_advanced > 0 || at_page_top?)
scratch_start_cursor = scratch_pdf.cursor
Expand Down
59 changes: 59 additions & 0 deletions spec/arrange_block_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1829,6 +1829,65 @@ def content
end
end

describe 'column box' do
it 'should compute extent for block based on correct width' do
pdf_theme[:code_border_radius] = 0
backend = nil
create_class (Asciidoctor::Converter.for 'pdf') do
register_for (backend = %(pdf#{object_id}).to_sym)
def traverse node
return super unless node.context == :document
column_box [0, cursor], columns: 2, width: bounds.width, reflow_margins: true, spacer: 12 do
super
end
end
end
input = <<~'EOS'
....
$ gem install asciidoctor-pdf asciidoctor-mathematical
$ asciidoctor-pdf -r asciidoctor-mathematical -a mathematical-format=svg sample.adoc
....
EOS
lines = (to_pdf input, backend: backend, pdf_theme: pdf_theme, analyze: :line).lines
pdf = to_pdf input, backend: backend, pdf_theme: pdf_theme, analyze: true
last_line_y = lines.select {|it| it[:from][:y] == it[:to][:y] }.map {|it| it[:from][:y] }.min
last_text = pdf.text[-1]
(expect last_text[:y]).to be > last_line_y
end

it 'should fill extent when block is advanced to next column' do
source_file = doc_file 'modules/extend/examples/pdf-converter-columns.rb'
source_lines = (File.readlines source_file).select {|l| l == ?\n || (l.start_with? ' ') }
ext_class = create_class Asciidoctor::Converter.for 'pdf'
backend = %(pdf#{ext_class.object_id})
source_lines[0] = %( register_for '#{backend}'\n)
ext_class.class_eval source_lines.join, source_file

pdf_theme.update \
base_columns: 2,
base_column_gap: 12,
code_border_radius: 0,
code_border_width: 0,
code_background_color: 'EFEFEF'

pdf = with_content_spacer 10, 675 do |spacer_path|
input = <<~EOS
image::#{spacer_path}[]
....
$ gem install asciidoctor-pdf asciidoctor-mathematical
$ asciidoctor-pdf -r asciidoctor-mathematical -a mathematical-format=svg sample.adoc
....
EOS
pdf = to_pdf input, backend: backend, pdf_theme: pdf_theme, analyze: true
pages = pdf.pages
(expect pages).to have_size 1
gs = (pdf.extract_graphic_states pages[0][:raw_content])[1]
(expect gs).to have_background color: 'EFEFEF', top_left: [312.0, 742.0], bottom_right: [562.0, 646.3]
end
end
end

# NOTE: generate reference files using ./scripts/generate-arrange-block-reference-files.sh
describe 'acceptance', visual: true, if: ENV['COVERAGE'] do
it 'at top, fits' do
Expand Down
22 changes: 22 additions & 0 deletions spec/converter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,28 @@
result = converter.parse_text %(foo\n<strong>bar</strong>), inline_format: [normalize: true]
(expect result).to eql [{ text: 'foo ' }, { text: 'bar', styles: [:bold].to_set }]
end

it 'should restore current column after float yields to current block' do
doc = Asciidoctor.load 'text', backend: :pdf
converter = doc.converter
actual_column = nil
last_visited_column = nil
converter.instance_exec do
init_pdf doc
start_new_page
column_box [bounds.left, cursor], width: bounds.width, columns: 2 do
float do
ink_prose 'before'
bounds.move_past_bottom
ink_prose 'after'
last_visited_column = bounds.instance_variable_get :@current_column
end
actual_column = bounds.instance_variable_get :@current_column
end
end
(expect actual_column).to eql 0
(expect last_visited_column).to eql 1
end
end

describe '#next_enclosed_block' do
Expand Down

0 comments on commit 9e843a1

Please sign in to comment.