Created
October 10, 2024 07:23
-
-
Save Krutie/4fdec214a66fb1005a1c7b894b4c6e31 to your computer and use it in GitHub Desktop.
Ruby extension to create two-column TOC layout for Asciidoctor PDF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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