Skip to content

Commit

Permalink
resolves asciidoctor#897 preserve text formatting in display of index…
Browse files Browse the repository at this point in the history
… term
  • Loading branch information
mojavelinux committed Jun 10, 2022
1 parent a9bcb3a commit dc4d08e
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 12 deletions.
40 changes: 29 additions & 11 deletions lib/asciidoctor/pdf/converter.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require_relative 'formatted_string'
require_relative 'formatted_text'
require_relative 'index_catalog'
require_relative 'pdfmark'
Expand Down Expand Up @@ -731,28 +732,42 @@ def convert_index_section node
end

def convert_index_list_item term, pagenum_sequence_style = nil
text = escape_xml term.name
term_fragments = term.name.fragments
unless term.container?
pagenum_fragment = (parse_text %(<a>#{DummyText}</a>), inline_format: true)[0]
if @media == 'screen'
case pagenum_sequence_style
when 'page'
pagenums = term.dests.uniq {|dest| dest[:page] }.map {|dest| %(<a anchor="#{dest[:anchor]}">#{dest[:page]}</a>) }
pagenums = term.dests.uniq {|dest| dest[:page] }.map {|dest| pagenum_fragment.merge anchor: dest[:anchor], text: dest[:page] }
when 'range'
first_anchor_per_page = {}.tap {|accum| term.dests.each {|dest| accum[dest[:page]] ||= dest[:anchor] } }
pagenums = (consolidate_ranges first_anchor_per_page.keys).map do |range|
anchor = first_anchor_per_page[(range.include? '-') ? (range.partition '-')[0] : range]
%(<a anchor="#{anchor}">#{range}</a>)
pagenum_fragment.merge text: range, anchor: anchor
end
else # term
pagenums = term.dests.map {|dest| %(<a anchor="#{dest[:anchor]}">#{dest[:page]}</a>) }
pagenums = term.dests.map {|dest| pagenum_fragment.merge text: dest[:page], anchor: dest[:anchor] }
end
else
pagenums = consolidate_ranges term.dests.map {|dest| dest[:page] }.uniq
pagenums = (consolidate_ranges term.dests.map {|dest| dest[:page] }.uniq).map {|range| { text: range } }
end
pagenums.each do |pagenum|
if (prev_fragment = term_fragments[-1]).size == 1
# NOTE: addresses a very minor kerning issue for text adjacent to the comma
if pagenum.size == 1
term_fragments[-1] = prev_fragment.merge text: %(#{prev_fragment[:text]}, #{pagenum[:text]})
next
else
term_fragments[-1] = prev_fragment.merge text: %(#{prev_fragment[:text]}, )
end
else
term_fragments << ({ text: ', ' })
end
term_fragments << pagenum
end
text = %(#{text}, #{pagenums.join ', '})
end
subterm_indent = @theme.description_list_description_indent
ink_prose text, align: :left, margin: 0, hanging_indent: subterm_indent * 2
typeset_formatted_text term_fragments, (calc_line_metrics @base_line_height), align: :left, color: @font_color, hanging_indent: subterm_indent * 2
indent subterm_indent do
term.subterms.each do |subterm|
convert_index_list_item subterm, pagenum_sequence_style
Expand Down Expand Up @@ -2570,17 +2585,20 @@ def convert_inline_indexterm node
if scratch?
visible ? node.text : ''
else
# NOTE: initialize index in case converter is called before PDF is initialized
@index ||= IndexCatalog.new
unless defined? @index
# NOTE: initialize index and text formatter in case converter is called before PDF is initialized
@index = IndexCatalog.new
@text_formatter = FormattedText::Formatter.new theme: (load_theme node.document)
end
# NOTE: page number (:page key) is added by InlineDestinationMarker
dest = { anchor: (anchor_name = @index.next_anchor_name) }
anchor = %(<a id="#{anchor_name}" type="indexterm"#{visible ? ' visible="true"' : ''}>#{DummyText}</a>)
if visible
visible_term = node.text
@index.store_primary_term (sanitize visible_term), dest
@index.store_primary_term (FormattedString.new parse_text visible_term, inline_format: [normalize: true]), dest
%(#{anchor}#{visible_term})
else
@index.store_term (node.attr 'terms').map {|term| sanitize term }, dest
@index.store_term (node.attr 'terms').map {|term| FormattedString.new parse_text term, inline_format: [normalize: true] }, dest
anchor
end
end
Expand Down
13 changes: 13 additions & 0 deletions lib/asciidoctor/pdf/formatted_string.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

class FormattedString < String
attr_reader :fragments

def initialize fragments
super [].tap {|accum| (@fragments = fragments).each {|it| accum << it[:text] } }.join
end

def eql? other
self == other && @fragments == other.fragments
end
end
48 changes: 47 additions & 1 deletion spec/index_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
((foo
{empty}
bar))(((yin
&#10;
{empty}
{empty}
yang)))
<<<
Expand Down Expand Up @@ -147,6 +148,51 @@
(expect index_lines).to include 'custom behavior, 1'
end

it 'should preserve text formatting in display of index term' do
pdf = to_pdf <<~'EOS', doctype: :book, analyze: true
= Document Title
== Content
Use the ((`return`)) keyword(((_keyword_))) to force a method to return early.
There are cats, and then there are ((*big* cats)).
A ((mouse _gesture_)) is a movement the software recognizes and interprets as a command.
[index]
= Index
EOS

(expect pdf.pages).to have_size 3
return_entry_text = pdf.find_unique_text 'return', page_number: 3
(expect return_entry_text[:font_name]).to eql 'mplus1mn-regular'
keyword_entry_text = pdf.find_unique_text 'keyword', page_number: 3
(expect keyword_entry_text[:font_name]).to eql 'NotoSerif-Italic'
big_text = pdf.find_unique_text 'big', page_number: 3
(expect big_text[:font_name]).to eql 'NotoSerif-Bold'
gesture_text = pdf.find_unique_text 'gesture', page_number: 3
(expect gesture_text[:font_name]).to eql 'NotoSerif-Italic'
end

it 'should not group term with and without formatting' do
pdf = to_pdf <<~'EOS', doctype: :book, analyze: true
The ((`proc`)) keyword in Ruby defines a ((proc)), which is a block of code.
[index]
= Index
EOS

(expect pdf.pages).to have_size 2
proc_text = pdf.find_text %r/^proc/, page_number: 2
(expect proc_text).to have_size 2
(expect proc_text[0][:string]).to eql 'proc'
(expect proc_text[0][:font_name]).to eql 'mplus1mn-regular'
(expect proc_text[1][:font_name]).not_to eql 'mplus1mn-regular'
index_lines = pdf.lines pdf.find_text page_number: 2
(expect index_lines).to eql ['Index', 'P', 'proc, 1', 'proc, 1']
end

it 'should not add index entries inside delimited block to index twice' do
pdf = to_pdf <<~'EOS', doctype: :book, analyze: true
= Document Title
Expand Down

0 comments on commit dc4d08e

Please sign in to comment.