diff options
| author | Dan Allen <dan.j.allen@gmail.com> | 2019-05-30 02:44:19 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-05-30 02:44:19 -0600 |
| commit | f40a9e50afa114093ae7689518c22914d7b48ce3 (patch) | |
| tree | 4d80045daea036ec565ccd9377020aed509af4c6 | |
| parent | 298c514a19a67e7de4793043d0d76b415b8112bb (diff) | |
resolves #191 allow theme to set paragraph indent (PR #192)
* allow text indent to be set for all paragraphs (aka prose)
* allow theme to control margin between paragraphs using prose_margin_inner
| -rw-r--r-- | CHANGELOG.adoc | 2 | ||||
| -rw-r--r-- | docs/theming-guide.adoc | 15 | ||||
| -rw-r--r-- | lib/asciidoctor-pdf/converter.rb | 18 | ||||
| -rw-r--r-- | lib/asciidoctor-pdf/formatted_text/formatter.rb | 7 | ||||
| -rw-r--r-- | lib/asciidoctor-pdf/prawn_ext/extensions.rb | 38 | ||||
| -rw-r--r-- | spec/paragraph_spec.rb | 71 |
6 files changed, 139 insertions, 12 deletions
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index db32ea27..ec5e7c8a 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -8,6 +8,8 @@ For a detailed view of what has changed, refer to the {uri-repo}/commits/master[ == Unreleased * restore compatibility with Asciidoctor back to 1.5.3 and add verification to test matrix (#1038) +* allow theme to set text indent for paragraphs using prose_text_indent (#191) +* allow theme to set spacing between adjacent paragraphs using prose_margin_inner (#191) * show parts in toc when toclevels=0 (#783) * add support for autonumbered callouts in source blocks (#1076) * fix duplication of footnotes in keep together regions (#1047) diff --git a/docs/theming-guide.adoc b/docs/theming-guide.adoc index d649de3c..85ddb814 100644 --- a/docs/theming-guide.adoc +++ b/docs/theming-guide.adoc @@ -1670,8 +1670,23 @@ Typically, all the margin is placed on the bottom. (default: 12) |prose: margin_bottom: $vertical_spacing + +|margin_inner^[1]^ +|<<measurement-units,Measurement>> + +(default: $prose_margin_bottom) +|prose: + margin_inner: 0 + +|text_indent +|<<measurement-units,Measurement>> + +(default: _not set_) +|prose: + text_indent: 18 |=== +. Controls the margin between adjacent paragraphs. +Useful when using indented paragraphs. + [#keys-block] === Block diff --git a/lib/asciidoctor-pdf/converter.rb b/lib/asciidoctor-pdf/converter.rb index f5385511..2fff5f83 100644 --- a/lib/asciidoctor-pdf/converter.rb +++ b/lib/asciidoctor-pdf/converter.rb @@ -534,6 +534,9 @@ class Converter < ::Prawn::Document end theme_font :abstract do prose_opts = { line_height: @theme.abstract_line_height, align: (@theme.abstract_align || @base_align).to_sym } + if (text_indent = @theme.prose_text_indent) + prose_opts[:indent_paragraphs] = text_indent + end # FIXME control more first_line_options using theme if (line1_font_style = @theme.abstract_first_line_font_style) && line1_font_style.to_sym != font_style prose_opts[:first_line_options] = { styles: [font_style, line1_font_style.to_sym] } @@ -569,15 +572,18 @@ class Converter < ::Prawn::Document convert_content_for_block node end - # TODO add prose around image logic (use role to add special logic for headshot) def convert_paragraph node add_dest_for_block node if node.id - prose_opts = {} + prose_opts = { margin_bottom: 0 } lead = (roles = node.roles).include? 'lead' if (align = resolve_alignment_from_role roles) prose_opts[:align] = align end + if (text_indent = @theme.prose_text_indent) + prose_opts[:indent_paragraphs] = text_indent + end + # TODO check if we're within one line of the bottom of the page # and advance to the next page if so (similar to logic for section titles) layout_caption node.title if node.title? @@ -589,6 +595,14 @@ class Converter < ::Prawn::Document else layout_prose node.content, prose_opts end + + if (margin_inner_val = @theme.prose_margin_inner) && + (next_block = (siblings = node.parent.blocks)[(siblings.index node) + 1]) && next_block.context == :paragraph + margin_bottom_val = margin_inner_val + else + margin_bottom_val = @theme.prose_margin_bottom + end + margin_bottom margin_bottom_val end def convert_admonition node diff --git a/lib/asciidoctor-pdf/formatted_text/formatter.rb b/lib/asciidoctor-pdf/formatted_text/formatter.rb index 6a8229db..33b2ab3a 100644 --- a/lib/asciidoctor-pdf/formatted_text/formatter.rb +++ b/lib/asciidoctor-pdf/formatted_text/formatter.rb @@ -27,6 +27,13 @@ class Formatter [text: string] end end + + # The original purpose of this method is to split paragraphs, but our formatter only works on paragraphs that have + # been presplit. Therefore, we just need to wrap the fragments in a single-element array (representing a single + # paragraph) and return them. + def array_paragraphs fragments + [fragments] + end end end end diff --git a/lib/asciidoctor-pdf/prawn_ext/extensions.rb b/lib/asciidoctor-pdf/prawn_ext/extensions.rb index eec01297..fd9f2ac9 100644 --- a/lib/asciidoctor-pdf/prawn_ext/extensions.rb +++ b/lib/asciidoctor-pdf/prawn_ext/extensions.rb @@ -357,11 +357,19 @@ module Extensions end end - # Performs the same work as text except that the first_line_opts - # are applied to the first line of text renderered. It's necessary - # to use low-level APIs in this method so that we only style the - # first line and not the remaining lines (which is the default - # behavior in Prawn). + # NOTE override built-in draw_indented_formatted_line to insert leading before second line + def draw_indented_formatted_line string, opts + result = super + unless @no_text_printed || @all_text_printed + # as of Prawn 1.2.1, we have to handle the line gap after the first line manually + move_down opts[:leading] + end + result + end + + # Performs the same work as Prawn::Text.text except that the first_line_opts are applied to the first line of text + # renderered. It's necessary to use low-level APIs in this method so we only style the first line and not the + # remaining lines (which is the default behavior in Prawn). def text_with_formatted_first_line string, first_line_opts, opts color = opts.delete :color fragments = parse_text string, opts @@ -378,17 +386,27 @@ module Extensions first_line_opts = opts.merge(first_line_opts).merge single_line: true box = ::Prawn::Text::Formatted::Box.new fragments, first_line_opts # NOTE get remaining_fragments before we add color to fragments on first line - remaining_fragments = box.render dry_run: true + if (text_indent = opts.delete :indent_paragraphs) + remaining_fragments = indent text_indent do + box.render dry_run: true + end + else + remaining_fragments = box.render dry_run: true + end # NOTE color must be applied per-fragment if first_line_color fragments.each {|fragment| fragment[:color] ||= first_line_color} end - fill_formatted_text_box fragments, first_line_opts + if text_indent + indent text_indent do + fill_formatted_text_box fragments, first_line_opts + end + else + fill_formatted_text_box fragments, first_line_opts + end unless remaining_fragments.empty? # NOTE color must be applied per-fragment - if color - remaining_fragments.each {|fragment| fragment[:color] ||= color } - end + remaining_fragments.each {|fragment| fragment[:color] ||= color } if color # as of Prawn 1.2.1, we have to handle the line gap after the first line manually move_down opts[:leading] remaining_fragments = fill_formatted_text_box remaining_fragments, opts diff --git a/spec/paragraph_spec.rb b/spec/paragraph_spec.rb index f9a51508..72835dbe 100644 --- a/spec/paragraph_spec.rb +++ b/spec/paragraph_spec.rb @@ -13,4 +13,75 @@ describe 'Asciidoctor::PDF::Converter - Paragraph' do (expect text).not_to include ?\t (expect text).not_to include ?\n end + + it 'should indent first line of paragraph if prose_text_indent key is set in theme' do + pdf = to_pdf <<~'EOS', pdf_theme: (build_pdf_theme prose_text_indent: 18), analyze: true + Unix cat buffer. + I'm sorry Dave, I'm afraid I can't do that. + Race condition bang endif linux L0phtCrack fork gnu int long stdio.h unix memory leak fail try catch void. + + Hack the mainframe segfault for hexadecimal private deadlock echo linux float stack alloc brute force tcp false packet. + All your base are belong to us. + EOS + + (expect pdf.text.size).to eql 4 + (expect pdf.text[0][:x]).to be > pdf.text[1][:x] + (expect pdf.text[2][:x]).to be > pdf.text[3][:x] + end + + it 'should use prose_margin_inner between paragraphs when prose-text_indent key is set in theme' do + pdf = to_pdf <<~'EOS', pdf_theme: (build_pdf_theme prose_text_indent: 18, prose_margin_inner: 0), analyze: true + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + + * list item + EOS + + line_spacing = 1.upto(3).map {|i| (pdf.text[i - 1][:y] - pdf.text[i][:y]).round 2 }.uniq + (expect line_spacing.size).to eql 1 + (expect line_spacing[0]).to eql 15.78 + (expect pdf.text[0][:x]).to be > pdf.text[1][:x] + (expect pdf.text[2][:x]).to be > pdf.text[3][:x] + list_item_text = (pdf.find_text string: 'list item')[0] + (expect (pdf.text[3][:y] - list_item_text[:y]).round 2).to eql 27.78 + end + + it 'should not alter line height of wrapped lines when prose_text_indent is set in theme' do + pdf = to_pdf <<~'EOS', analyze: true + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + EOS + + last_line_y = pdf.text[-1][:y] + + pdf = to_pdf <<~'EOS', pdf_theme: (build_pdf_theme prose_text_indent: 18), analyze: true + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + EOS + + (expect pdf.text[-1][:y]).to eql last_line_y + end + + it 'should indent first line of abstract if prose_text_indent key is set in theme' do + pdf = to_pdf <<~'EOS', pdf_theme: (build_pdf_theme prose_text_indent: 18), analyze: true + = Document Title + + [abstract] + This document is configured to have indented paragraphs. + This option is controlled by the prose_text_indent key in the theme. + + And on it goes. + EOS + + (expect pdf.text[1][:string]).to start_with 'This document' + (expect pdf.text[1][:x]).to be > pdf.text[2][:x] + (expect pdf.text[3][:string]).to eql 'And on it goes.' + end end |
