Class: Carve::Hexapdf::Renderer
- Inherits:
-
Object
- Object
- Carve::Hexapdf::Renderer
- Defined in:
- lib/carve/hexapdf/renderer.rb
Overview
Walks a Carve AST (as produced by Carve.parse) and draws it onto a
HexaPDF::Composer, producing a laid-out PDF document.
Block nodes become HexaPDF boxes (text, list, table, container, image); inline nodes become the multi-part styled "runs" that HexaPDF::Composer#formatted_text consumes. Emphasis maps to font variants and text decorations, inline code to a monospace font, links to a colored run with a URI overlay.
Math and diagram fences are rendered through optional renderers:
callables (which return image bytes); without a matching renderer they
degrade to their monospace source. The renderer never raises on an
unknown or unsupported node - it degrades to text/children so a document
always renders.
Constant Summary collapse
- BLOCK_GAP =
8- RENDERER_PROPS =
Style properties this renderer consumes itself. They are not HexaPDF style properties, and the style chain hands each of them down to its nested keys:
table.captioninheritstable's :cell_padding,figure.group.captioninheritsfigure.group's column properties. Splatting one into a text box raises NoMethodError, which loses the whole document, so a style destined for a text box drops them (+text_style+). :box is dropped bystyle_foralready. %i[ cell_padding column_gap min_column_width title_margin definition_indent item_spacing content_indentation ].freeze
- DIAGRAM_LANGS =
Code-fence languages that map to a diagram renderer key.
{ "mermaid" => :mermaid, "dot" => :graphviz, "graphviz" => :graphviz, "chart" => :chart, "vega" => :chart, }.freeze
Instance Method Summary collapse
-
#block(node, target) ⇒ Object
---- block dispatch ------------------------------------------------.
-
#initialize(composer, base_font: nil, code_font: nil, link_color: nil, highlight_color: nil, styles: nil, renderers: nil) ⇒ Renderer
constructor
A new instance of Renderer.
- #render_document(doc) ⇒ Object
Constructor Details
#initialize(composer, base_font: nil, code_font: nil, link_color: nil, highlight_color: nil, styles: nil, renderers: nil) ⇒ Renderer
Returns a new instance of Renderer.
56 57 58 59 60 61 62 63 64 65 |
# File 'lib/carve/hexapdf/renderer.rb', line 56 def initialize(composer, base_font: nil, code_font: nil, link_color: nil, highlight_color: nil, styles: nil, renderers: nil) @c = composer @layout = composer.document.layout @styles = StyleMap.new(style_sugar(base_font: base_font, code_font: code_font, link_color: link_color, highlight_color: highlight_color, styles: styles)) @renderers = renderers || {} end |
Instance Method Details
#block(node, target) ⇒ Object
---- block dispatch ------------------------------------------------
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/carve/hexapdf/renderer.rb', line 79 def block(node, target) case node[:type] # A definition is RELOCATED, not rendered in place - it belongs in the # endnote section, which is what the HTML renderer does with it too. # Handled HERE rather than by filtering the root's children, because a # definition written inside a container stays inside it (the engines # differ on hoisting), and a root-level filter would render that one on # the page and again in the endnotes. when "footnote" then nil when "heading" then heading(node, target) when "paragraph" then paragraph(node, target) when "code_block" then code_block(node, target) when "list" then list(node, target) when "block_quote" then block_quote(node, target) when "table" then table(node, target) when "thematic_break" then thematic_break(target) when "div" then container_of(node[:children], target) when "admonition" then admonition(node, target) when "definition_list" then definition_list(node, target) when "figure" then figure(node, target) when "figure_group" then figure_group(node, target) when "block_image", "image" then image_block(node, target) when "block_extension" then container_of(node[:children], target) when "raw_block", "comment", "abbreviation_def" # No meaningful PDF form - drop. else if inline_children?(node[:children]) emit_paragraph(node[:children], target) elsif node[:children] Array(node[:children]).each { |ch| block(ch, target) } end end end |
#render_document(doc) ⇒ Object
67 68 69 70 71 72 73 74 75 |
# File 'lib/carve/hexapdf/renderer.rb', line 67 def render_document(doc) # Keys arrive as Symbols (symbolized JSON) while node :id is a String. @footnote_defs = collect_footnote_defs(doc) @footnotes = [] @footnote_numbers = {} Array(doc[:children]).each { |node| block(node, @c) } render_footnotes(@c) @c end |