summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Allen <dan.j.allen@gmail.com>2019-05-30 02:44:19 -0600
committerGitHub <noreply@github.com>2019-05-30 02:44:19 -0600
commitf40a9e50afa114093ae7689518c22914d7b48ce3 (patch)
tree4d80045daea036ec565ccd9377020aed509af4c6
parent298c514a19a67e7de4793043d0d76b415b8112bb (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.adoc2
-rw-r--r--docs/theming-guide.adoc15
-rw-r--r--lib/asciidoctor-pdf/converter.rb18
-rw-r--r--lib/asciidoctor-pdf/formatted_text/formatter.rb7
-rw-r--r--lib/asciidoctor-pdf/prawn_ext/extensions.rb38
-rw-r--r--spec/paragraph_spec.rb71
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