Skip to content

Instantly share code, notes, and snippets.

@Krutie
Created October 10, 2024 07:23
Show Gist options
  • Save Krutie/4fdec214a66fb1005a1c7b894b4c6e31 to your computer and use it in GitHub Desktop.
Save Krutie/4fdec214a66fb1005a1c7b894b4c6e31 to your computer and use it in GitHub Desktop.
Ruby extension to create two-column TOC layout for Asciidoctor PDF
module TOCHelpers
# Method to convert integers to Roman numerals
def to_roman(num)
romans = {
1000 => 'M', 900 => 'CM', 500 => 'D', 400 => 'CD',
100 => 'C', 90 => 'XC', 50 => 'L', 40 => 'XL',
10 => 'X', 9 => 'IX', 5 => 'V', 4 => 'IV', 1 => 'I'
}
result = ''
romans.each do |value, letter|
result << letter * (num / value)
num = num % value
end
result
end
def format_part_title(title, id, page, x_pos, y_pos, column_width)
formatted_text_box(
[
{ text: "#{title}", anchor: id }, # Add an anchor to the title
],
at: [x_pos, y_pos],
width: column_width,
height: 20,
size: 14,
color: '000000',
align: :left
)
# Create a clickable link for the page number
link_annotation([bounds.left, y_pos, column_width, y_pos - 20], Dest: page)
move_down 40
end
def format_chapter_title(title, id, page, x_pos, y_pos, column_width)
formatted_text_box(
[
{ text: "#{title} ........", anchor: id }, # Add an anchor to the title
{ text: page.to_s }
],
at: [x_pos, y_pos],
width: column_width,
height: 20,
size: 12,
theme_font: :heading,
color: '888888',
align: :left
)
# Create a clickable link for the page number
link_annotation([bounds.left, y_pos, column_width, y_pos - 20], Dest: page)
move_down 20
# stroke
stroke_horizontal_rule '00DC82', line_width: 2
move_down 5
end
def format_section_title(title, id, page, x_pos, y_pos, column_width)
formatted_text_box(
[
{ text: "#{title} ........", anchor: id }, # Add an anchor to the title
{ text: page.to_s }
],
at: [x_pos, y_pos],
width: column_width,
height: 20,
size: 9,
color: '000000',
align: :left
)
# Create a clickable link for the page number
link_annotation([bounds.left, y_pos, column_width, y_pos - 20], Dest: page)
move_down 8
end
end
class CustomTwoColumnTocConverter < (Asciidoctor::PDF::Converter)
register_for 'pdf'
include TOCHelpers
def convert_document(doc)
# Call the original document conversion method
super
warn "Page count: #{page_count}"
toc_start_page = 3
go_to_page(toc_start_page)
start_new_page
warn "Starting TOC Generation"
# Generate the TOC PDF
total_pages_filled = 1
# Access parts
doc.sections.each_with_index do |part, index|
roman_number = to_roman(index + 1)
format_part_title("#{roman_number} #{part.title}", part.id , part.attr('pdf-page-start'), 0, cursor, bounds.width)
# Access sections
part.sections.each do |chapter|
format_chapter_title("#{chapter.title}", chapter.id, chapter.attr('pdf-page-start'), 0, cursor, bounds.width)
# Access subsections
toc_items = []
chapter.sections.each do |section|
toc_items << {
text: section.title,
id: section.id,
page: "#{section.attr('pdf-page-start')}"
}
end
if toc_items.empty?
ink_prose "No sections available for this chapter", size: 10, align: :center, style: :italic, color: '888888'
next
end
column_width = bounds.width / 2
left_column, right_column = toc_items.each_slice((toc_items.size / 2.0).ceil).to_a
left_column ||= [] # Ensure left_column is not nil, use empty array if nil
right_column ||= [] # Ensure left_column is not nil, use empty array if nil
# Render the sections in two columns
max_items = [left_column.size, right_column.size].max # Get the maximum number of items between the two columns
(0...max_items).each do |i|
y_pos = cursor
# Left Column - Place TOC item if available
if left_column[i]
format_section_title("#{left_column[i][:text]}", left_column[i][:id], left_column[i][:page], 0, y_pos, column_width)
end
# Left Column - Place TOC item if available
if right_column[i]
format_section_title("#{right_column[i][:text]}", right_column[i][:id], right_column[i][:page], column_width, y_pos, column_width)
end
end
# Check if there is enough space for the item, if not, go to the next page
if cursor < 60 # Adjust this value to control when to break the page
start_new_page
total_pages_filled += 1
end
move_down 20
total_pages_filled
end
# Check if there is enough space for the item, if not, go to the next page
if cursor < 60 # Adjust this value to control when to break the page
start_new_page
total_pages_filled += 1
end
move_down 20
total_pages_filled
end
# Check if there is enough space for the item, if not, go to the next page
if cursor < 60 # Adjust this value to control when to break the page
start_new_page
total_pages_filled += 1
end
move_down 20
total_pages_filled
go_to_page(toc_start_page + total_pages_filled)
warn "TOC Generation Completed"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment