Class: Sakusei::HeadingWrapper

Inherits:
Object
  • Object
show all
Defined in:
lib/sakusei/heading_wrapper.rb

Overview

Wraps each h2/h3 heading and its immediately following content block in a <div style=“page-break-inside: avoid”> so that Chromium/Puppeteer keeps the heading glued to the content that follows it.

Operates on the markdown string after ERB and Vue processing so that Vue component output (already rendered to HTML) is treated as a normal block. Raw HTML in the markdown (html: true) passes through md-to-pdf untouched.

Constant Summary collapse

HEADING_PATTERN =
/\A[ \t]*(##|###) /
FENCE_PATTERN =
/\A`{3,}/

Instance Method Summary collapse

Constructor Details

#initialize(content) ⇒ HeadingWrapper

Returns a new instance of HeadingWrapper.



15
16
17
# File 'lib/sakusei/heading_wrapper.rb', line 15

def initialize(content)
  @content = content
end

Instance Method Details

#wrapObject



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/sakusei/heading_wrapper.rb', line 19

def wrap
  code_blocks = []
  text_lines = []
  in_code = false
  code_buffer = []

  @content.each_line do |line|
    if line.match?(FENCE_PATTERN)
      if in_code
        # End of code block
        code_buffer << line
        code_blocks << code_buffer.join
        text_lines << "#{placeholder_for(code_blocks.length - 1)}\n\n"
        code_buffer = []
        in_code = false
      else
        # Start of code block
        in_code = true
        code_buffer << line
      end
    elsif in_code
      code_buffer << line
    else
      text_lines << line
    end
  end

  # Handle unclosed code block
  if in_code
    code_blocks << code_buffer.join
    text_lines << "#{placeholder_for(code_blocks.length - 1)}\n\n"
  end

  wrapped = wrap_text(text_lines.join)

  # Restore code blocks
  code_blocks.each_with_index do |block, i|
    wrapped = wrapped.sub(placeholder_for(i), block)
  end

  wrapped
end