summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorDan Allen <dan.j.allen@gmail.com>2022-02-25 17:41:09 -0700
committerDan Allen <dan.j.allen@gmail.com>2022-04-05 13:43:18 -0600
commitd356798f25b0f93e4442e0879f8c511d6023bbe0 (patch)
treed18351b6331ed8d1fd595fde9a8ad2b726f460fd /spec
parenta87db305bb962323f9957b4dd0169bd93baa7be2 (diff)
resolves #2003 compute extent of content in scratch document to arrange content block in primary document
* introduce arrange_block helper to handle positioning content block in primary document * reimplement new dry_run to compute the extent and start position of the content block instead of just the height * stop rendering at page boundary in scratch document when computing start position of content block to avoid unnecessary work * ensure that caption stays with start of content block when arranging content block * prevent arrange_block from starting content block on page when no content from block fits on that page * avoid converting content that overruns page in AsciiDoc table cell in scratch document * encapsulate push/pop scratch in arrange_block * add tests for the arrange_block helper * prevent nested breakable blocks from flowing beyond first page when performing on single page only * switch dry_run method to accept keyword arguments * avoid arrange_block when converting open block when possible * include negative tests for image taller than page at top of block * verify borders and backgrounds are not drawn in scratch document * add test to verify behavior of stop_if_first_page_empty * include sanity check before drawing background and border over extent * add rdoc to dry_run and arrange_block help methods * reuse computed extent as TOC extent * remove unused code * raise error if table caption does not fit on single page * add test that footnotes are not pushed down if height exceeds height of page
Diffstat (limited to 'spec')
-rw-r--r--spec/admonition_spec.rb12
-rw-r--r--spec/arrange_block_spec.rb1759
-rw-r--r--spec/example_spec.rb24
-rw-r--r--spec/fixtures/tall-spacer.pngbin0 -> 341 bytes
-rw-r--r--spec/footnote_spec.rb40
-rw-r--r--spec/image_spec.rb12
-rw-r--r--spec/listing_spec.rb19
-rw-r--r--spec/open_spec.rb65
-rw-r--r--spec/quote_spec.rb59
-rw-r--r--spec/reference/admonition-page-split.pdf463
-rw-r--r--spec/reference/example-with-nested-block-page-split.pdf840
-rw-r--r--spec/reference/image-svg-with-caption-scale-to-fit-page.pdfbin12348 -> 12351 bytes
-rw-r--r--spec/sidebar_spec.rb1
-rw-r--r--spec/source_spec.rb4
-rw-r--r--spec/spec_helper.rb28
-rw-r--r--spec/toc_spec.rb3
16 files changed, 3051 insertions, 278 deletions
diff --git a/spec/admonition_spec.rb b/spec/admonition_spec.rb
index 562f4454..159876de 100644
--- a/spec/admonition_spec.rb
+++ b/spec/admonition_spec.rb
@@ -3,11 +3,11 @@
require_relative 'spec_helper'
describe 'Asciidoctor::PDF::Converter - Admonition' do
- it 'should advance block to next page to avoid splitting it if it will fit on page' do
+ it 'should advance unbreakable block shorter than page to next page to avoid splitting it' do
pdf = to_pdf <<~EOS, analyze: true
#{(['paragraph'] * 20).join %(\n\n)}
- [NOTE]
+ [NOTE%unbreakable]
====
#{(['admonition'] * 20).join %(\n\n)}
====
@@ -17,7 +17,7 @@ describe 'Asciidoctor::PDF::Converter - Admonition' do
(expect admon_page_numbers).to eql [2]
end
- it 'should place anchor above top margin of block' do
+ it 'should place anchor below top margin of block' do
input = <<~EOS
paragraph
@@ -31,14 +31,14 @@ describe 'Asciidoctor::PDF::Converter - Admonition' do
lines = (to_pdf input, pdf_theme: pdf_theme, analyze: :line).lines
pdf = to_pdf input, pdf_theme: pdf_theme
(expect (dest = get_dest pdf, 'admon-1')).not_to be_nil
- (expect dest[:y]).to eql lines[0][:from][:y] + 10
+ (expect dest[:y]).to eql lines[0][:from][:y]
end
it 'should keep anchor with block if block is advanced to next page' do
input = <<~EOS
paragraph
- [NOTE#admon-1]
+ [NOTE#admon-1%unbreakable]
====
#{(['filler'] * 27).join %(\n\n)}
====
@@ -82,7 +82,7 @@ describe 'Asciidoctor::PDF::Converter - Admonition' do
(expect lines[1][:to][:y]).to be > 36.0
end
- it 'should draw border and background on all pages if block is split across pages', visual: true do
+ it 'should draw border and background on all pages if block is split across pages', visual: true, breakable: true do
pdf_theme = {
admonition_background_color: 'F5A9A9',
admonition_border_width: 0.5,
diff --git a/spec/arrange_block_spec.rb b/spec/arrange_block_spec.rb
new file mode 100644
index 00000000..9a7bb857
--- /dev/null
+++ b/spec/arrange_block_spec.rb
@@ -0,0 +1,1759 @@
+# frozen_string_literal: true
+
+require_relative 'spec_helper'
+
+describe 'Asciidoctor::PDF::Converter#arrange_block' do
+ let :pdf_theme do
+ {
+ page_margin: 50,
+ page_size: 'Letter',
+ example_background_color: 'ffffcc',
+ example_border_radius: 0,
+ example_border_width: 0,
+ sidebar_border_radius: 0,
+ sidebar_border_width: 0,
+ }
+ end
+
+ it 'should draw background across extent of empty block' do
+ pdf = to_pdf <<~'EOS', pdf_theme: pdf_theme, analyze: true
+ before block
+
+ ====
+ ====
+
+ after block
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 1
+ (expect (pdf.find_unique_text 'before block')[:page_number]).to be 1
+ (expect (pdf.find_unique_text 'after block')[:page_number]).to be 1
+ gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect gs).to have_background color: 'FFFFCC', top_left: [50.0, 714.22], bottom_right: [562.0, 702.22]
+ end
+
+ it 'should not draw backgrounds and borders in scratch document' do
+ pdf_theme[:sidebar_border_color] = '222222'
+ pdf_theme[:sidebar_border_width] = 0.5
+ input = <<~'EOS'
+ before
+
+ [%unbreakable]
+ --
+ ====
+ example
+
+ ****
+ sidebar
+ ****
+
+ example
+ ====
+ --
+ EOS
+ scratch_pdf = nil
+ extensions = proc do
+ postprocessor do
+ process do |doc, output|
+ scratch_pdf = doc.converter.scratch
+ output
+ end
+ end
+ end
+ to_pdf input, pdf_theme: pdf_theme, extensions: extensions
+ scratch_pdf_output = scratch_pdf.render
+ scratch_pdf = (EnhancedPDFTextInspector.analyze scratch_pdf_output)
+ (expect (scratch_pdf.extract_graphic_states scratch_pdf.pages[0][:raw_content])).to be_empty
+ scratch_pdf_lines = (LineInspector.analyze scratch_pdf_output).lines
+ (expect scratch_pdf_lines).to be_empty
+ end
+
+ it 'should invoke on_page_create if set on scratch document' do
+ input = <<~'EOS'
+ scratch_background_color:CCCCCC[]
+
+ image::tall.svg[pdfwidth=70mm]
+
+ ====
+ content
+
+ of
+
+ block
+ ====
+ EOS
+ scratch_pdf = nil
+ extensions = proc do
+ inline_macro :scratch_background_color do
+ process do |parent, target|
+ (scratch_pdf = parent.document.converter.scratch).on_page_create do
+ scratch_pdf.fill_absolute_bounds target
+ end
+ create_inline parent, :quoted, 'before'
+ end
+ end
+ end
+ to_pdf input, pdf_theme: pdf_theme, extensions: extensions
+ scratch_pdf_output = scratch_pdf.render
+ scratch_pdf = (EnhancedPDFTextInspector.analyze scratch_pdf_output)
+ (expect scratch_pdf.pages[0][:raw_content]).to include %(/DeviceRGB cs\n0.8 0.8 0.8 scn\n0.0 0.0 612.0 792.0 re)
+ end
+
+ describe 'unbreakable block' do
+ # NOTE: only add tests that verify at top ignores unbreakable option; otherwise, put test in breakable at top
+ describe 'at top' do
+ it 'should keep block on current page if it fits' do
+ pdf = to_pdf <<~'EOS', pdf_theme: pdf_theme, analyze: true
+ [%unbreakable]
+ ====
+ This block fits in the remaining space on the page.
+
+ Therefore, it will not be split or moved to the following page.
+ ====
+
+ after block
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 1
+ (expect (pdf.find_unique_text %r/^This block fits /)[:page_number]).to be 1
+ (expect (pdf.find_unique_text 'after block')[:page_number]).to be 1
+ gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 674.44]
+ end
+
+ it 'should split block taller than page across pages, starting from page top' do
+ block_content = ['block content'] * 35 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ [%unbreakable]
+ ====
+ #{block_content}
+ ====
+
+ after block
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_unique_text 'after block')[:page_number]).to be 2
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[-1][:page_number]).to be 2
+ p1_gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect p1_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 50.0]
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 437.17]
+ end
+
+ it 'should split block with nested block taller than page across pages, starting from page top' do
+ pdf_theme[:example_border_width] = 0.5
+ pdf_theme[:example_border_color] = '0000ff'
+ pdf_theme[:example_background_color] = 'ffffff'
+ block_content = ['nested block content'] * 35 * %(\n\n)
+ input = <<~EOS
+ [%unbreakable]
+ ====
+
+ block content
+
+ [%unbreakable]
+ ======
+ #{block_content}
+ ======
+
+ block content
+ ====
+
+ after block
+ EOS
+
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ lines = (to_pdf input, pdf_theme: pdf_theme, analyze: :line).lines
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_unique_text 'after block')[:page_number]).to be 2
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[-1][:page_number]).to be 2
+ p1_border_cut_lines = lines
+ .select {|it| it[:page_number] == 1 && it[:color] == 'FFFFFF' && it[:style] == :dashed }
+ .sort_by {|it| it[:from][:x] }
+ (expect p1_border_cut_lines).to have_size 2
+ (expect p1_border_cut_lines[0][:from][:x]).to eql 50.5
+ (expect p1_border_cut_lines[1][:from][:x]).to eql 62.5
+ (expect p1_border_cut_lines[0][:from][:y]).to eql 50.0
+ (expect p1_border_cut_lines[0][:from][:y]).to eql p1_border_cut_lines[1][:from][:y]
+ p2_border_cut_lines = lines
+ .select {|it| it[:page_number] == 2 && it[:color] == 'FFFFFF' && it[:style] == :dashed }
+ .sort_by {|it| it[:from][:x] }
+ (expect p2_border_cut_lines).to have_size 2
+ (expect p2_border_cut_lines[0][:from][:x]).to eql 50.5
+ (expect p2_border_cut_lines[1][:from][:x]).to eql 62.5
+ (expect p2_border_cut_lines[0][:from][:y]).to eql 742.0
+ (expect p2_border_cut_lines[0][:from][:y]).to eql p2_border_cut_lines[1][:from][:y]
+ end
+
+ it 'should split block taller than several pages across pages, starting from page top' do
+ block_content = ['block content'] * 50 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ [%unbreakable]
+ ====
+ #{block_content}
+ ====
+
+ after block
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 3
+ (expect (pdf.find_unique_text 'after block')[:page_number]).to be 3
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[-1][:page_number]).to be 3
+ p1_gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect p1_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 50.0]
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 50.0]
+ p3_gs = (pdf.extract_graphic_states pages[2][:raw_content])[0]
+ (expect p3_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 714.22]
+ end
+ end
+
+ describe 'below top' do
+ it 'should keep block on current page if it fits' do
+ pdf = to_pdf <<~'EOS', pdf_theme: pdf_theme, analyze: true
+ before block
+
+ [%unbreakable]
+ ====
+ This block fits in the remaining space on the page.
+
+ Therefore, it will not be split or moved to the following page.
+ ====
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 1
+ (expect (pdf.find_unique_text 'before block')[:page_number]).to be 1
+ (expect (pdf.find_unique_text %r/^This block fits /)[:page_number]).to be 1
+ gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect gs).to have_background color: 'FFFFCC', top_left: [50.0, 714.22], bottom_right: [562.0, 646.66]
+ end
+
+ it 'should advance block shorter than page to next page to avoid breaking' do
+ before_block_content = ['before block'] * 15 * %(\n\n)
+ block_content = ['block content'] * 15 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ #{before_block_content}
+
+ [%unbreakable]
+ ====
+ #{block_content}
+ ====
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 2
+ (expect (pdf.extract_graphic_states pages[0][:raw_content])).to be_empty
+ gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 313.3]
+ end
+
+ it 'should advance block shorter than page and with caption to next page to avoid breaking' do
+ before_block_content = ['before block'] * 15 * %(\n\n)
+ block_content = ['block content'] * 15 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ #{before_block_content}
+
+ .block title
+ [%unbreakable]
+ ====
+ #{block_content}
+ ====
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 2
+ (expect (pdf.find_unique_text 'Example 1. block title')[:page_number]).to be 2
+ (expect (pdf.find_unique_text 'Example 1. block title')[:y]).to be > 723.009
+ (expect (pdf.extract_graphic_states pages[0][:raw_content])).to be_empty
+ gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect gs).to have_background color: 'FFFFCC', top_left: [50.0, 723.009], bottom_right: [562.0, 294.309]
+ end
+
+ it 'should advance block shorter than page and with multiline caption to next page to avoid breaking' do
+ before_block_content = ['before block'] * 15 * %(\n\n)
+ block_content = ['block content'] * 15 * %(\n\n)
+ block_title = ['block title'] * 20 * ' '
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, attributes: { 'example-caption' => nil }, analyze: true
+ #{before_block_content}
+
+ .#{block_title}
+ [%unbreakable]
+ ====
+ #{block_content}
+ ====
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 2
+ block_title_texts = pdf.find_text %r/block title /
+ (expect block_title_texts).to have_size 2
+ (expect block_title_texts[0][:page_number]).to be 2
+ (expect block_title_texts[0][:y]).to be > 723.009
+ (expect block_title_texts[1][:y]).to be > 708.018
+ (expect (pdf.extract_graphic_states pages[0][:raw_content])).to be_empty
+ gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect gs).to have_background color: 'FFFFCC', top_left: [50.0, 708.018], bottom_right: [562.0, 279.318]
+ end
+
+ it 'should advance nested unbreakable block shorter than page to next page to avoid breaking', breakable: true do
+ before_block_content = ['before block'] * 20 * %(\n\n)
+ nested_block_content = ['nested block content'] * 5 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ #{before_block_content}
+
+ ====
+ before nested block
+
+ [%unbreakable]
+ ****
+ #{nested_block_content}
+ ****
+ ====
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 1
+ (expect (pdf.find_unique_text 'before nested block')[:page_number]).to be 1
+ (expect (pdf.find_text 'nested block content')[0][:page_number]).to be 2
+ p1_gs = (pdf.extract_graphic_states pages[0][:raw_content])
+ (expect p1_gs).to have_size 2
+ (expect p1_gs[0]).to have_background color: 'FFFFCC', top_left: [50.0, 186.4], bottom_right: [562.0, 50.0]
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])
+ (expect p2_gs).to have_size 3
+ (expect p2_gs[0]).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 579.1]
+ (expect p2_gs[2]).to have_background color: 'EEEEEE', top_left: [62.0, 742.0], bottom_right: [550.0, 591.1]
+ end
+
+ it 'should advance block with only nested unbreakable block shorter than page to next page to avoid breaking', breakable: true do
+ before_block_content = ['before block'] * 20 * %(\n\n)
+ nested_block_content = ['nested block content'] * 5 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ #{before_block_content}
+
+ ====
+ [%unbreakable]
+ ****
+ #{nested_block_content}
+ ****
+ ====
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 1
+ (expect (pdf.find_text 'nested block content')[0][:page_number]).to be 2
+ (expect (pdf.extract_graphic_states pages[0][:raw_content])).to be_empty
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])
+ (expect p2_gs).to have_size 2
+ (expect p2_gs[0]).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 567.1]
+ (expect p2_gs[1]).to have_background color: 'EEEEEE', top_left: [62.0, 730.0], bottom_right: [550.0, 579.1]
+ end
+
+ it 'should split block taller than page across pages, starting from current position' do
+ block_content = ['block content'] * 35 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ before block
+
+ [%unbreakable]
+ ====
+ #{block_content}
+ ====
+
+ after block
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_unique_text 'before block')[:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[-1][:page_number]).to be 2
+ (expect (pdf.find_unique_text 'after block')[:page_number]).to be 2
+ p1_gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect p1_gs).to have_background color: 'FFFFCC', top_left: [50.0, 714.22], bottom_right: [562.0, 50.0]
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 409.39]
+ end
+
+ it 'should restart dry run on new page if first page is empty', breakable: true do
+ calls = []
+ extensions = proc do
+ block :spy do
+ on_context :paragraph
+ process do |parent, reader, attrs|
+ block = create_paragraph parent, reader.lines, attrs
+ block.instance_variable_set :@_calls, calls
+ block.extend (Module.new do
+ def content
+ @_calls << (caller.join ?\n) if document.converter.scratch? # rubocop:disable RSpec/InstanceVariable
+ super
+ end
+ end)
+ end
+ end
+ end
+ pdf = with_content_spacer 10, 650 do |spacer_path|
+ to_pdf <<~EOS, pdf_theme: pdf_theme, extensions: extensions, analyze: true
+ image::#{spacer_path}[]
+
+ ****
+ [discrete]
+ == does not fit
+
+ [spy]
+ paragraph
+ ****
+ EOS
+ end
+
+ (expect pdf.pages).to have_size 2
+ (expect (pdf.find_unique_text 'does not fit')[:page_number]).to be 2
+ (expect (pdf.find_unique_text 'paragraph')[:page_number]).to be 2
+ (expect calls).to have_size 1
+ (expect (calls.join ?\n).scan '`dry_run\'').to have_size 2
+ end
+
+ it 'should restart dry run at current position once content exceeds height of first page', breakable: true do
+ block_content = ['block content'] * 35 * %(\n\n)
+ calls = []
+ extensions = proc do
+ block :spy do
+ on_context :sidebar
+ process do |parent, reader, attrs|
+ block = create_block parent, :sidebar, reader.lines, attrs, content_model: :compound
+ block.instance_variable_set :@_calls, calls
+ block.extend (Module.new do
+ def content
+ @_calls << (caller.join ?\n) if document.converter.scratch? # rubocop:disable RSpec/InstanceVariable
+ super
+ end
+ end)
+ end
+ end
+ end
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, extensions: extensions, analyze: true
+ before block
+
+ [%unbreakable]
+ ====
+ #{block_content}
+
+ [spy]
+ ****
+ nested block content
+ ****
+ ====
+
+ after block
+ EOS
+
+ (expect pdf.pages).to have_size 2
+ # 1st call: to compute extent of sidebar for example block in scratch document
+ # 2nd call: to render sidebar in example block in scratch document
+ # 3nd call: to compute extent of sidebar in primary document
+ # 4th call: (not included) to render sidebar in primary document
+ (expect calls).to have_size 3
+ (expect (calls.join ?\n)).not_to include '`perform_on_single_page\''
+ (expect (pdf.find_unique_text 'nested block content')[:page_number]).to be 2
+ end
+
+ it 'should not restart dry run at top of page once content exceeds height of first page', breakable: true do
+ block_content = ['block content'] * 35 * %(\n\n)
+ calls = []
+ extensions = proc do
+ block :spy do
+ on_context :sidebar
+ process do |parent, reader, attrs|
+ block = create_block parent, :sidebar, reader.lines, attrs, content_model: :compound
+ block.instance_variable_set :@_calls, calls
+ block.extend (Module.new do
+ def content
+ @_calls << (caller.join ?\n) if document.converter.scratch? # rubocop:disable RSpec/InstanceVariable
+ super
+ end
+ end)
+ end
+ end
+ end
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, extensions: extensions, analyze: true
+ [%unbreakable]
+ ====
+ #{block_content}
+
+ [spy]
+ ****
+ nested block content
+ ****
+ ====
+
+ after block
+ EOS
+
+ (expect pdf.pages).to have_size 2
+ # 1st call: to compute extent of sidebar for example block in scratch document
+ # 2nd call: to render sidebar in example block in scratch document
+ # 3nd call: to compute extent of sidebar in primary document
+ # 4th call: (not included) to render sidebar in primary document
+ (expect calls).to have_size 3
+ (expect (calls.join ?\n)).not_to include '`perform_on_single_page\''
+ (expect (pdf.find_unique_text 'nested block content')[:page_number]).to be 2
+ end
+
+ it 'should restart dry run at current position if unbreakable block exceeds height of first page inside nested block', breakable: true do
+ block_content = ['block content'] * 35 * %(\n\n)
+ calls = []
+ extensions = proc do
+ block :spy do
+ on_context :sidebar
+ process do |parent, reader, attrs|
+ block = create_block parent, :sidebar, reader.lines, attrs, content_model: :compound
+ block.instance_variable_set :@_calls, calls
+ block.extend (Module.new do
+ def content
+ @_calls << (caller.join ?\n) if document.converter.scratch? # rubocop:disable RSpec/InstanceVariable
+ super
+ end
+ end)
+ end
+ end
+ end
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, extensions: extensions, analyze: true
+ before block
+
+ [%unbreakable]
+ ====
+ before nested block
+
+ [%unbreakable]
+ ======
+ #{block_content}
+
+ [spy]
+ ****
+ deeply nested block content
+ ****
+ ======
+ ====
+
+ after block
+ EOS
+
+ (expect pdf.pages).to have_size 2
+ (expect calls).to have_size 7
+ (expect (calls.join ?\n)).not_to include '`perform_on_single_page\''
+ (expect (pdf.find_unique_text 'deeply nested block content')[:page_number]).to be 2
+ end
+
+ it 'should restart dry run at current position if breakable content exceeds height of first page inside nested block', breakable: true do
+ block_content = ['block content'] * 30 * %(\n\n)
+ calls = []
+ extensions = proc do
+ block :spy do
+ on_context :sidebar
+ process do |parent, reader, attrs|
+ block = create_block parent, :sidebar, reader.lines, attrs, content_model: :compound
+ block.instance_variable_set :@_calls, calls
+ block.extend (Module.new do
+ def content
+ @_calls << (caller.join ?\n) if document.converter.scratch? # rubocop:disable RSpec/InstanceVariable
+ super
+ end
+ end)
+ end
+ end
+ end
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, extensions: extensions, analyze: true
+ before block
+
+ [%unbreakable]
+ ====
+ ======
+ #{block_content}
+
+ [spy]
+ ****
+ deeply nested block content
+ ****
+ ======
+ ====
+
+ after block
+ EOS
+
+ (expect pdf.pages).to have_size 2
+ (expect calls).to have_size 7
+ (expect (calls.join ?\n)).not_to include '`perform_on_single_page\''
+ (expect (pdf.find_unique_text 'deeply nested block content')[:page_number]).to be 2
+ end
+
+ it 'should advance block taller than page to next page if only caption fits on current page' do
+ before_block_content = ['before block'] * 22 * %(\n\n)
+ block_content = ['block content'] * 25 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ ****
+ filler
+ ****
+
+ #{before_block_content}
+
+ .block title
+ [%unbreakable]
+ ====
+ #{block_content}
+ ====
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 3
+ (expect (pdf.find_text 'before block')[0][:page_number]).to be 1
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 2
+ (expect (pdf.find_text 'block content')[-1][:page_number]).to be 3
+ (expect (pdf.extract_graphic_states pages[0][:raw_content])).to have_size 1
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'FFFFCC', top_left: [50.0, 723.009], bottom_right: [562.0, 50.0]
+ p3_gs = (pdf.extract_graphic_states pages[2][:raw_content])[0]
+ (expect p3_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 714.22]
+ end
+
+ it 'should advance block taller than page to next page if no content fits on current page' do
+ before_block_content = ['before block'] * 22 * %(\n\n)
+ block_content = ['block content'] * 25 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ ====
+ filler
+ ====
+
+ #{before_block_content}
+
+ .block title
+ [%unbreakable]
+ ****
+ #{block_content}
+ ****
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 3
+ (expect (pdf.find_text 'before block')[0][:page_number]).to be 1
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 2
+ (expect (pdf.find_text 'block content')[-1][:page_number]).to be 3
+ (expect (pdf.extract_graphic_states pages[0][:raw_content])).to have_size 1
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'EEEEEE', top_left: [50.0, 742.0], bottom_right: [562.0, 50.0]
+ p3_gs = (pdf.extract_graphic_states pages[2][:raw_content])[0]
+ (expect p3_gs).to have_background color: 'EEEEEE', top_left: [50.0, 742.0], bottom_right: [562.0, 687.19]
+ end
+
+ it 'should preserve indentation across pages in scratch document' do
+ x = Set.new
+ extensions = proc do
+ block :spy do
+ on_context :paragraph
+ process do |parent, reader, attrs|
+ para = create_paragraph parent, reader.lines, attrs
+ para.instance_variable_set :@_x, x
+ para.extend (Module.new do
+ def content
+ @_x << @document.converter.bounds.absolute_left # rubocop:disable RSpec/InstanceVariable
+ super
+ end
+ end)
+ end
+ end
+ end
+ pdf_theme.delete :page_margin
+ pdf_theme.delete :page_size
+ pdf_theme[:example_border_width] = 0.5
+ pdf_theme[:example_border_color] = '0000ff'
+ pdf_theme[:example_background_color] = 'ffffff'
+ input = <<~EOS
+ before block
+
+ [%unbreakable]
+ ====
+ example1
+
+ [%unbreakable]
+ ======
+ #{['example2'] * 25 * %(\n\n)}
+
+ [%unbreakable]
+ ========
+ [spy]
+ #{['example3'] * 405 * ' '}
+ ========
+ ======
+ ====
+ EOS
+ pdf = to_pdf input, pdf_theme: pdf_theme, extensions: extensions, analyze: true
+ p3_lines = (to_pdf input, pdf_theme: pdf_theme, analyze: :line).lines.select {|it| it[:page_number] == 3 }
+ (expect pdf.pages).to have_size 3
+ (expect x).to have_size 1
+ last_text_y = (pdf.find_text %r/example3/)[-1][:y]
+ last_lines_y = p3_lines
+ .select {|it| it[:color] == '0000FF' && it[:from][:y] < 805 && it[:from][:y] == it[:to][:y] }
+ .map {|it| it[:from][:y] }
+ (expect last_lines_y).to have_size 3
+ (expect last_lines_y[2] - last_lines_y[1]).to eql 12.0
+ (expect last_lines_y[1] - last_lines_y[0]).to eql 12.0
+ (expect last_lines_y[0]).to be < (last_text_y - 1)
+ end
+ end
+ end
+
+ describe 'breakable block', breakable: true do
+ describe 'at top' do
+ it 'should keep block on current page if it fits' do
+ pdf = to_pdf <<~'EOS', pdf_theme: pdf_theme, analyze: true
+ ====
+ This block fits in the remaining space on the page.
+
+ Therefore, it will not be split or moved to the following page.
+ ====
+
+ after block
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 1
+ (expect (pdf.find_unique_text %r/^This block fits /)[:page_number]).to be 1
+ (expect (pdf.find_unique_text 'after block')[:page_number]).to be 1
+ gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 674.44]
+ end
+
+ it 'should split block taller than page across pages, starting from page top' do
+ block_content = ['block content'] * 35 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ ====
+ #{block_content}
+ ====
+
+ after block
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_unique_text 'after block')[:page_number]).to be 2
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[-1][:page_number]).to be 2
+ p1_gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect p1_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 50.0]
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 437.17]
+ end
+
+ it 'should split block with nested block taller than page across pages, starting from page top' do
+ pdf_theme[:example_border_width] = 0.5
+ pdf_theme[:example_border_color] = '0000ff'
+ pdf_theme[:example_background_color] = 'ffffff'
+ block_content = ['nested block content'] * 35 * %(\n\n)
+ input = <<~EOS
+ ====
+
+ block content
+
+ ======
+ #{block_content}
+ ======
+
+ block content
+ ====
+
+ after block
+ EOS
+
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ lines = (to_pdf input, pdf_theme: pdf_theme, analyze: :line).lines
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_unique_text 'after block')[:page_number]).to be 2
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[-1][:page_number]).to be 2
+ p1_border_cut_lines = lines
+ .select {|it| it[:page_number] == 1 && it[:color] == 'FFFFFF' && it[:style] == :dashed }
+ .sort_by {|it| it[:from][:x] }
+ (expect p1_border_cut_lines).to have_size 2
+ (expect p1_border_cut_lines[0][:from][:x]).to eql 50.5
+ (expect p1_border_cut_lines[1][:from][:x]).to eql 62.5
+ (expect p1_border_cut_lines[0][:from][:y]).to eql 50.0
+ (expect p1_border_cut_lines[0][:from][:y]).to eql p1_border_cut_lines[1][:from][:y]
+ p2_border_cut_lines = lines
+ .select {|it| it[:page_number] == 2 && it[:color] == 'FFFFFF' && it[:style] == :dashed }
+ .sort_by {|it| it[:from][:x] }
+ (expect p2_border_cut_lines).to have_size 2
+ (expect p2_border_cut_lines[0][:from][:x]).to eql 50.5
+ (expect p2_border_cut_lines[1][:from][:x]).to eql 62.5
+ (expect p2_border_cut_lines[0][:from][:y]).to eql 742.0
+ (expect p2_border_cut_lines[0][:from][:y]).to eql p2_border_cut_lines[1][:from][:y]
+ end
+
+ it 'should split block taller than several pages, starting from page top' do
+ block_content = ['block content'] * 50 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ ====
+ #{block_content}
+ ====
+
+ after block
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 3
+ (expect (pdf.find_unique_text 'after block')[:page_number]).to be 3
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[-1][:page_number]).to be 3
+ p1_gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect p1_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 50.0]
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 50.0]
+ p3_gs = (pdf.extract_graphic_states pages[2][:raw_content])[0]
+ (expect p3_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 714.22]
+ end
+
+ it 'should split block across pages that contains image that does not fit in remaining space on current page' do
+ block_content = ['block content'] * 10 * %(\n\n)
+ input = <<~EOS
+ ====
+ #{block_content}
+
+ image::tux.png[pdfwidth=100%]
+ ====
+ EOS
+
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ images = (to_pdf input, pdf_theme: pdf_theme, analyze: :image).images
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[-1][:page_number]).to be 1
+ p1_gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect p1_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 50.0]
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 155.88235]
+ (expect images).to have_size 1
+ image = images[0]
+ (expect image[:page_number]).to be 2
+ (expect image[:y]).to eql 742.0
+ end
+
+ # NOTE: this scenario renders an example block that starts with an empty page
+ it 'should split block across pages that contains image taller than page at start of block', negative: true do
+ input = <<~'EOS'
+ ====
+ image::tall-spacer.png[]
+ ====
+ EOS
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ images = (to_pdf input, pdf_theme: pdf_theme, analyze: :image).images
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ page_width, page_height = (get_page_size pdf, 1).map(&:to_f)
+ page_margin = pdf_theme[:page_margin].to_f
+ (expect images).to have_size 1
+ image = images[0]
+ (expect image[:page_number]).to be 2
+ (expect image[:y]).to eql (page_height - page_margin)
+ (expect image[:height]).to eql (page_height - page_margin * 2)
+ p1_gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect p1_gs).to have_background color: 'FFFFCC', top_left: [page_margin, page_height - page_margin], bottom_right: [page_width - page_margin, page_margin]
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'FFFFCC', top_left: [page_margin, page_height - page_margin], bottom_right: [page_width - page_margin, page_margin]
+ end
+
+ it 'should split block across pages that contains image taller than page that follows text' do
+ input = <<~'EOS'
+ ====
+ before image
+
+ image::tall-spacer.png[]
+ ====
+ EOS
+
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ images = (to_pdf input, pdf_theme: pdf_theme, analyze: :image).images
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_unique_text 'before image')[:page_number]).to be 1
+ p1_gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect p1_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 50.0]
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 50.0]
+ (expect images).to have_size 1
+ image = images[0]
+ (expect image[:page_number]).to be 2
+ (expect image[:y]).to eql 742.0
+ end
+ end
+
+ describe 'below top' do
+ it 'should keep block on current page if it fits' do
+ pdf = to_pdf <<~'EOS', pdf_theme: pdf_theme, analyze: true
+ before block
+
+ ====
+ This block fits in the remaining space on the page.
+
+ Therefore, it will not be split or moved to the following page.
+ ====
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 1
+ (expect (pdf.find_unique_text 'before block')[:page_number]).to be 1
+ (expect (pdf.find_unique_text %r/^This block fits /)[:page_number]).to be 1
+ gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect gs).to have_background color: 'FFFFCC', top_left: [50.0, 714.22], bottom_right: [562.0, 646.66]
+ end
+
+ it 'should advance block shorter than page to next page if only caption fits on current page' do
+ before_block_content = ['before block'] * 24 * %(\n\n)
+ block_content = ['block content'] * 15 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ #{before_block_content}
+
+ .block title
+ ====
+ #{block_content}
+ ====
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 1
+ block_title = pdf.find_unique_text 'Example 1. block title'
+ (expect block_title[:page_number]).to be 2
+ (expect block_title[:y]).to be > 723.009
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 2
+ (expect (pdf.extract_graphic_states pages[0][:raw_content])).to be_empty
+ gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect gs).to have_background color: 'FFFFCC', top_left: [50.0, 723.009], bottom_right: [562.0, 294.309]
+ end
+
+ it 'should advance block shorter than page to next page if caption spills over page boundary' do
+ block_content = ['block content'] * 15 * %(\n\n)
+ block_title = ['block title'] * 15 * ' '
+ pdf = with_content_spacer 10, 635 do |spacer_path|
+ input = <<~EOS
+ image::#{spacer_path}[]
+
+ before block
+
+ .#{block_title}
+ ====
+ #{block_content}
+ ====
+ EOS
+ to_pdf input, pdf_theme: pdf_theme, analyze: true
+ end
+
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_unique_text 'before block')[:page_number]).to be 1
+ block_title = pdf.find_unique_text %r/^Example 1. block title/
+ (expect block_title[:page_number]).to be 2
+ (expect block_title[:y]).to be > 708.318
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 2
+ gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect gs).to have_background color: 'FFFFCC', top_left: [50.0, 708.018], bottom_right: [562.0, 279.318]
+ end
+
+ it 'should advance block shorter than page to next page if caption fits but advances page' do
+ block_content = ['block content'] * 15 * %(\n\n)
+ pdf = with_content_spacer 10, 635 do |spacer_path|
+ input = <<~EOS
+ image::#{spacer_path}[]
+
+ before block
+
+ .block title
+ ====
+ #{block_content}
+ ====
+ EOS
+ to_pdf input, pdf_theme: pdf_theme, analyze: true
+ end
+
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_unique_text 'before block')[:page_number]).to be 1
+ block_title = pdf.find_unique_text 'Example 1. block title'
+ (expect block_title[:page_number]).to be 2
+ (expect block_title[:y]).to be > 723.009
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 2
+ gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect gs).to have_background color: 'FFFFCC', top_left: [50.0, 723.009], bottom_right: [562.0, 294.309]
+ end
+
+ it 'should advance block shorter than page to next page if no content fits on current page' do
+ before_block_content = ['before block'] * 24 * %(\n\n)
+ block_content = ['block content'] * 15 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ #{before_block_content}
+
+ .block title
+ ****
+ #{block_content}
+ ****
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 1
+ block_title = pdf.find_unique_text 'block title'
+ (expect block_title[:page_number]).to be 2
+ (expect block_title[:y]).to be < 742.0
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 2
+ (expect (pdf.extract_graphic_states pages[0][:raw_content])).to be_empty
+ gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect gs).to have_background color: 'EEEEEE', top_left: [50.0, 742.0], bottom_right: [562.0, 284.82]
+ end
+
+ it 'should advance block taller than page to next page if only caption fits on current page' do
+ before_block_content = ['before block'] * 24 * %(\n\n)
+ block_content = ['block content'] * 30 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ #{before_block_content}
+
+ .block title
+ ====
+ #{block_content}
+ ====
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 3
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 1
+ block_title = pdf.find_unique_text 'Example 1. block title'
+ (expect block_title[:page_number]).to be 2
+ (expect block_title[:y]).to be > 723.009
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 2
+ (expect (pdf.find_text 'block content')[-1][:page_number]).to be 3
+ (expect (pdf.extract_graphic_states pages[0][:raw_content])).to be_empty
+ gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect gs).to have_background color: 'FFFFCC', top_left: [50.0, 723.009], bottom_right: [562.0, 50.0]
+ end
+
+ it 'should advance block taller than page to next page if no content fits on current page' do
+ before_block_content = ['before block'] * 24 * %(\n\n)
+ block_content = ['block content'] * 30 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ #{before_block_content}
+
+ .block title
+ ****
+ #{block_content}
+ ****
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 3
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 1
+ (expect (pdf.find_unique_text 'block title')[:page_number]).to be 2
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 2
+ (expect (pdf.find_text 'block content')[-1][:page_number]).to be 3
+ (expect (pdf.extract_graphic_states pages[0][:raw_content])).to be_empty
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'EEEEEE', top_left: [50.0, 742.0], bottom_right: [562.0, 50.0]
+ p3_gs = (pdf.extract_graphic_states pages[2][:raw_content])[0]
+ (expect p3_gs).to have_background color: 'EEEEEE', top_left: [50.0, 742.0], bottom_right: [562.0, 548.29]
+ end
+
+ it 'should split block shorter than page across pages, starting from current position if it does not fit on current page' do
+ before_block_content = ['before block'] * 15 * %(\n\n)
+ block_content = ['block content'] * 15 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ #{before_block_content}
+
+ ====
+ #{block_content}
+ ====
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[-1][:page_number]).to be 2
+ p1_gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect p1_gs).to have_background color: 'FFFFCC', top_left: [50.0, 325.3], bottom_right: [562.0, 50.0]
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 576.07]
+ end
+
+ it 'should split block taller than page across pages, starting from current position' do
+ before_block_content = ['before block'] * 15 * %(\n\n)
+ block_content = ['block content'] * 35 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ #{before_block_content}
+
+ ====
+ #{block_content}
+ ====
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 3
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[-1][:page_number]).to be 3
+ p1_gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect p1_gs).to have_background color: 'FFFFCC', top_left: [50.0, 325.3], bottom_right: [562.0, 50.0]
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 50.0]
+ p3_gs = (pdf.extract_graphic_states pages[2][:raw_content])[0]
+ (expect p3_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 714.22]
+ end
+
+ it 'should split block across pages that contains image that does not fit in remaining space on current page' do
+ before_block_content = ['before block'] * 5 * %(\n\n)
+ block_content = ['block content'] * 5 * %(\n\n)
+ input = <<~EOS
+ #{before_block_content}
+
+ ====
+ #{block_content}
+
+ image::tux.png[pdfwidth=100%]
+ ====
+ EOS
+
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ images = (to_pdf input, pdf_theme: pdf_theme, analyze: :image).images
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 1
+ (expect (pdf.find_text 'block content')[-1][:page_number]).to be 1
+ p1_gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect p1_gs).to have_background color: 'FFFFCC', top_left: [50.0, 603.1], bottom_right: [562.0, 50.0]
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 155.88235]
+ (expect images).to have_size 1
+ image = images[0]
+ (expect image[:page_number]).to be 2
+ (expect image[:y]).to eql 742.0
+ end
+
+ it 'should advance block that starts with image that does not fit in remaining space on current page to next page' do
+ before_block_content = ['before block'] * 10 * %(\n\n)
+ input = <<~EOS
+ #{before_block_content}
+
+ ====
+ image::tux.png[pdfwidth=100%]
+
+ after image
+ ====
+ EOS
+
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ images = (to_pdf input, pdf_theme: pdf_theme, analyze: :image).images
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 1
+ (expect (pdf.find_unique_text 'after image')[:page_number]).to be 2
+ (expect (pdf.extract_graphic_states pages[0][:raw_content])).to be_empty
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 116.10235]
+ (expect images).to have_size 1
+ image = images[0]
+ (expect image[:page_number]).to be 2
+ (expect image[:y]).to eql 730.0
+ end
+
+ it 'should advance block with caption that starts with image that does not fit in remaining space on current page to next page' do
+ before_block_content = ['before block'] * 10 * %(\n\n)
+ input = <<~EOS
+ #{before_block_content}
+
+ .block title
+ ====
+ image::tux.png[pdfwidth=100%]
+
+ after image
+ ====
+ EOS
+
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ images = (to_pdf input, pdf_theme: pdf_theme, analyze: :image).images
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 1
+ (expect (pdf.find_unique_text 'Example 1. block title')[:page_number]).to be 2
+ (expect (pdf.find_unique_text 'after image')[:page_number]).to be 2
+ (expect (pdf.extract_graphic_states pages[0][:raw_content])).to be_empty
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'FFFFCC', top_left: [50.0, 723.009], bottom_right: [562.0, 97.11135]
+ (expect images).to have_size 1
+ image = images[0]
+ (expect image[:page_number]).to be 2
+ (expect image[:y]).to eql 711.009
+ end
+
+ # NOTE: this scenario renders an example block that starts with an empty page
+ it 'should split block across pages that contains image taller than page at start of block', negative: true do
+ input = <<~'EOS'
+ before block
+
+ ====
+ image::tall-spacer.png[]
+ ====
+ EOS
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ images = (to_pdf input, pdf_theme: pdf_theme, analyze: :image).images
+ pages = pdf.pages
+ (expect pages).to have_size 3
+ page_width, page_height = (get_page_size pdf, 1).map(&:to_f)
+ page_margin = pdf_theme[:page_margin].to_f
+ (expect images).to have_size 1
+ image = images[0]
+ (expect image[:page_number]).to be 3
+ (expect image[:y]).to eql (page_height - page_margin)
+ (expect image[:height]).to eql (page_height - page_margin * 2)
+ (expect (pdf.extract_graphic_states pages[0][:raw_content])).to be_empty
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'FFFFCC', top_left: [page_margin, page_height - page_margin], bottom_right: [page_width - page_margin, page_margin]
+ p3_gs = (pdf.extract_graphic_states pages[2][:raw_content])[0]
+ (expect p3_gs).to have_background color: 'FFFFCC', top_left: [page_margin, page_height - page_margin], bottom_right: [page_width - page_margin, page_margin]
+ end
+
+ # FIXME: this fails when block is unbreakable
+ it 'should account for top margin of discrete heading inside block with no top padding' do
+ pdf_theme[:sidebar_padding] = [0, 10, 10, 10]
+ pdf_theme[:sidebar_border_color] = '0000ff'
+ pdf_theme[:sidebar_border_width] = 0.5
+ pdf_theme[:heading_margin_top] = 50
+ input = <<~'EOS'
+ before
+
+ ****
+ [discrete]
+ == Discrete Heading
+
+ content
+ ****
+ EOS
+
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ lines = (to_pdf input, pdf_theme: pdf_theme, analyze: :line).lines
+
+ pages = pdf.pages
+ (expect pages).to have_size 1
+ bottom_line_y = lines.select {|it| it[:color] == '0000FF' }.map {|it| it[:to][:y] }.min
+ bottom_content_y = (pdf.find_unique_text 'content')[:y]
+ (expect bottom_line_y).to be < bottom_content_y
+ end
+
+ it 'should not go haywire if caption does not fit and converter does not tare content' do
+ block_title = (['long caption that wraps'] * 15).join ' '
+ extensions = proc do
+ tree_processor do
+ process do |doc|
+ doc.converter.extend TareFirstPageContentStreamNoop
+ nil
+ end
+ end
+ end
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, extensions: extensions, analyze: true
+ image::tall.svg[pdfwidth=78mm]
+
+ .#{block_title}
+ ====
+ content
+
+ of
+
+ example
+ ====
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ block_title = pdf.find_unique_text %r/^Example 1\. /
+ (expect block_title[:page_number]).to be 1
+ (expect (pdf.find_unique_text 'content')[:page_number]).to be 2
+ end
+ end
+ end
+
+ describe 'multiple' do
+ it 'should arrange block after another block has been arranged' do
+ before_block_content = ['before block'] * 35 * %(\n\n)
+ block_content = ['block content'] * 15 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
+ [%unbreakable]
+ ====
+ #{before_block_content}
+ ====
+
+ between
+
+ [%unbreakable]
+ ====
+ #{block_content}
+ ====
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 3
+ (expect (pdf.find_text 'before block')[0][:page_number]).to be 1
+ (expect (pdf.find_text 'before block')[-1][:page_number]).to be 2
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 3
+ (expect (pdf.find_text 'block content')[-1][:page_number]).to be 3
+ p1_gs = (pdf.extract_graphic_states pages[0][:raw_content])[0]
+ (expect p1_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 50.0]
+ p2_gs = (pdf.extract_graphic_states pages[1][:raw_content])[0]
+ (expect p2_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 437.17]
+ p3_gs = (pdf.extract_graphic_states pages[2][:raw_content])[0]
+ (expect p3_gs).to have_background color: 'FFFFCC', top_left: [50.0, 742.0], bottom_right: [562.0, 313.3]
+ end
+ end
+
+ describe 'table cell', breakable: true do
+ describe 'at top' do
+ it 'should keep block on current page if it fits' do
+ pdf_theme[:example_border_width] = 0.5
+ pdf_theme[:example_border_color] = '0000ff'
+ pdf_theme[:example_background_color] = 'ffffff'
+ pdf_theme[:table_cell_padding] = 5
+ block_content = ['block content'] * 3 * %(\n\n)
+ input = <<~EOS
+ |===
+ a|
+ before block
+
+ ====
+ #{block_content}
+ ====
+
+ after block
+ |===
+ EOS
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ lines = (to_pdf input, pdf_theme: pdf_theme, analyze: :line).lines
+ (expect pdf.pages).to have_size 1
+ block_edges = lines.select {|it| it[:color] == '0000FF' }.each_with_object({ x: [], y: [] }) do |line, accum|
+ accum[:x] = (accum[:x] << line[:from][:x] << line[:to][:x]).sort.uniq
+ accum[:y] = (accum[:y] << line[:from][:y] << line[:to][:y]).sort.uniq.reverse
+ end
+ block_edges_expected = { x: [55.0, 557.0], y: [709.22, 613.88] }
+ (expect block_edges).to eql block_edges_expected
+ (expect (pdf.find_unique_text 'after block')[:y]).to be < block_edges_expected[:y][1]
+ end
+
+ it 'should draw border around block extent when table cell has large padding' do
+ pdf_theme[:example_border_width] = 0.5
+ pdf_theme[:example_border_color] = '0000ff'
+ pdf_theme[:example_background_color] = 'ffffff'
+ pdf_theme[:table_cell_padding] = [30, 20]
+ block_content = ['block content'] * 3 * %(\n\n)
+ input = <<~EOS
+ |===
+ a|
+ before block
+
+ ====
+ #{block_content}
+
+ ---
+ ====
+
+ after block
+ |===
+ EOS
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ lines = (to_pdf input, pdf_theme: pdf_theme, analyze: :line).lines
+ (expect pdf.pages).to have_size 1
+ block_edges = lines.select {|it| it[:color] == '0000FF' }.each_with_object({ x: [], y: [] }) do |line, accum|
+ accum[:x] = (accum[:x] << line[:from][:x] << line[:to][:x]).sort.uniq
+ accum[:y] = (accum[:y] << line[:from][:y] << line[:to][:y]).sort.uniq.reverse
+ end
+ block_edges_expected = { x: [70.0, 542.0], y: [684.22, 564.88] }
+ thematic_break = lines.find {|it| it[:color] == 'EEEEEE' }
+ (expect thematic_break[:to][:y]).to be > block_edges[:y][1]
+ (expect (pdf.find_unique_text 'after block')[:y]).to be < block_edges_expected[:y][1]
+ (expect block_edges).to eql block_edges_expected
+ end
+
+ it 'should truncate block taller than page within table cell' do
+ pdf_theme[:example_border_width] = 0.5
+ pdf_theme[:example_border_color] = '0000ff'
+ pdf_theme[:example_background_color] = 'ffffff'
+ pdf_theme[:page_margin] = 36
+ pdf_theme[:table_cell_padding] = 5
+ block_content = ['block content'] * 25 * %(\n\n)
+ input = <<~EOS
+ |===
+ a|
+ table cell
+
+ ====
+ #{block_content}
+ ====
+
+ table cell
+ |===
+ EOS
+ (expect do
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ lines = (to_pdf input, pdf_theme: pdf_theme, analyze: :line).lines
+ (expect pdf.pages).to have_size 1
+ fragment_line = lines.find {|it| it[:color] == 'FFFFFF' && it[:to][:y] == 41.0 }
+ (expect fragment_line).not_to be_nil
+ (expect fragment_line[:style]).to eql :dashed
+ block_edges = lines.select {|it| it[:color] == '0000FF' }.each_with_object({ x: [], y: [] }) do |line, accum|
+ accum[:x] = (accum[:x] << line[:from][:x] << line[:to][:x]).sort.uniq
+ accum[:y] = (accum[:y] << line[:from][:y] << line[:to][:y]).sort.uniq.reverse
+ end
+ block_edges_expected = { x: [41.0, 571.0], y: [723.22, 41.0] }
+ (expect block_edges).to eql block_edges_expected
+ (expect (pdf.find_text 'block content').size).to be < 25
+ end).to log_message severity: :ERROR, message: '~the table cell on page 1 has been truncated'
+ end
+
+ it 'should not convert content in table cell that overruns first page when computing height of table cell' do
+ table_cell_content = ['table cell'] * 30 * %(\n\n)
+ calls = []
+ extensions = proc do
+ block :spy do
+ on_context :paragraph
+ process do |parent, reader, attrs|
+ block = create_paragraph parent, reader.lines, attrs
+ block.instance_variable_set :@_calls, calls
+ block.extend (Module.new do
+ def content
+ @_calls << (caller.join ?\n) if document.converter.scratch? # rubocop:disable RSpec/InstanceVariable
+ super
+ end
+ end)
+ end
+ end
+ end
+ input = <<~EOS
+ before table
+
+ |===
+ a|
+ #{table_cell_content}
+
+ [spy]
+ beyond of first page
+ |===
+ EOS
+ (expect do
+ pdf = to_pdf input, pdf_theme: pdf_theme, extensions: extensions, analyze: true
+ (expect pdf.pages).to have_size 2
+ (expect pdf.find_text 'beyond first page').to be_empty
+ (expect (pdf.find_text 'table cell')[0][:page_number]).to be 2
+ (expect (pdf.find_text 'table cell')[-1][:page_number]).to be 2
+ (expect calls).to be_empty
+ end).to log_message severity: :ERROR, message: '~the table cell on page 2 has been truncated'
+ end
+
+ it 'should not convert content in table cell that overruns first page when computing height of table cell in scratch document' do
+ pdf_theme[:example_border_width] = 0.5
+ pdf_theme[:example_border_color] = '0000ff'
+ pdf_theme[:example_background_color] = 'ffffff'
+ table_cell_content = ['table cell'] * 30 * %(\n\n)
+ calls = []
+ extensions = proc do
+ block :spy do
+ on_context :paragraph
+ process do |parent, reader, attrs|
+ block = create_paragraph parent, reader.lines, attrs
+ block.instance_variable_set :@_calls, calls
+ block.extend (Module.new do
+ def content
+ @_calls << (caller.join ?\n) if document.converter.scratch? # rubocop:disable RSpec/InstanceVariable
+ super
+ end
+ end)
+ end
+ end
+ end
+ input = <<~EOS
+ ====
+ before table
+ |===
+ a|
+ #{table_cell_content}
+
+ [spy]
+ beyond of first page
+ |===
+ ====
+ EOS
+ (expect do
+ pdf = to_pdf input, pdf_theme: pdf_theme, extensions: extensions, analyze: true
+ lines = (to_pdf input, pdf_theme: pdf_theme, extensions: extensions, analyze: :line).lines
+ (expect pdf.pages).to have_size 2
+ (expect pdf.find_text 'beyond first page').to be_empty
+ (expect (pdf.find_text 'table cell')[0][:page_number]).to be 2
+ (expect (pdf.find_text 'table cell')[-1][:page_number]).to be 2
+ (expect calls).to be_empty
+ p2_bottom_border_lines = lines.select do |it|
+ it[:page_number] == 2 && it[:color] != '000000' && it[:from][:y] == 50.0 && it[:to][:y] == 50.0
+ end
+ (expect p2_bottom_border_lines).to have_size 2
+ block_bottom_border_range = [p2_bottom_border_lines[0][:from][:x], p2_bottom_border_lines[0][:to][:x]].sort
+ (expect block_bottom_border_range).to eql [50.0, 562.0]
+ table_bottom_border_range = [p2_bottom_border_lines[1][:from][:x], p2_bottom_border_lines[1][:to][:x]].sort
+ (expect table_bottom_border_range).to eql [62.0, 550.0]
+ end).to log_message severity: :ERROR, message: '~the table cell on page 2 has been truncated'
+ end
+
+ it 'should scale font when computing height of block' do
+ pdf_theme[:example_border_width] = 0.5
+ pdf_theme[:example_border_color] = '0000ff'
+ pdf_theme[:example_background_color] = 'ffffff'
+ pdf_theme[:example_padding] = [10, 10, 0, 10]
+ pdf_theme[:prose_margin_bottom] = 10
+ pdf_theme[:block_margin_bottom] = 10
+ pdf_theme[:table_font_size] = 5.25
+ block_content = ['block content'] * 10 * %(\n\n)
+ input = <<~EOS
+ |===
+ a|
+ ====
+ #{block_content}
+ ====
+
+ table cell
+ |===
+ EOS
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ lines = (to_pdf input, pdf_theme: pdf_theme, analyze: :line).lines
+ border_bottom_y = lines
+ .select {|it| it[:color] == '0000FF' }
+ .reduce(Float::INFINITY) {|min, it| [min, it[:to][:y], it[:from][:y]].min }
+ last_content = (pdf.find_text 'block content')[-1]
+ last_content_bottom_y = last_content[:y]
+ (expect border_bottom_y).to be < last_content_bottom_y
+ padding_below = last_content_bottom_y - border_bottom_y
+ (expect padding_below).to ((be_within 2).of 10)
+ (expect (pdf.find_text 'block content')[0][:font_size]).to eql 5.25
+ (expect (pdf.find_text 'table cell')[0][:font_size]).to eql 5.25
+ end
+ end
+
+ describe 'below top' do
+ it 'should advance table cell that contains block shorter than page but does not fit on current page' do
+ pdf_theme[:example_border_width] = 0.5
+ pdf_theme[:example_border_color] = '0000ff'
+ pdf_theme[:example_background_color] = 'ffffff'
+ pdf_theme[:page_margin] = 36
+ pdf_theme[:table_cell_padding] = 5
+ before_table_content = ['before table'] * 15 * %(\n\n)
+ block_content = ['block content'] * 15 * %(\n\n)
+ input = <<~EOS
+ #{before_table_content}
+
+ |===
+ a|
+ ====
+ #{block_content}
+
+ block content end
+ ====
+ |===
+
+ after table
+ EOS
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ lines = (to_pdf input, pdf_theme: pdf_theme, analyze: :line).lines
+ (expect pdf.pages).to have_size 2
+ (expect (pdf.find_text 'before table')[-1][:page_number]).to be 1
+ (expect (pdf.find_unique_text 'after table')[:page_number]).to be 2
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 2
+ (expect (pdf.find_unique_text 'block content end')[:page_number]).to be 2
+ table_edges_expected = { x: [36.0, 576.0], y: [756.0, 278.0] }
+ block_edges_expected = { x: [41.0, 571.0], y: [751.0, 294.52] }
+ table_border_lines = lines.select {|it| it[:color] == 'DDDDDD' }
+ (expect table_border_lines.map {|it| it[:page_number] }.uniq).to eql [2]
+ table_edges = table_border_lines.each_with_object({ x: [], y: [] }) do |line, accum|
+ accum[:x] = (accum[:x] << line[:from][:x] << line[:to][:x]).sort.uniq
+ from_y = (line[:from][:y].ceil - (line[:from][:y].ceil % 2)).floor.to_f
+ to_y = (line[:to][:y].ceil - (line[:to][:y].ceil % 2)).floor.to_f
+ accum[:y] = (accum[:y] << from_y << to_y).sort.uniq.reverse
+ end
+ (expect table_edges).to eql table_edges_expected
+ block_border_lines = lines.select {|it| it[:color] == '0000FF' }
+ (expect block_border_lines.map {|it| it[:page_number] }.uniq).to eql [2]
+ block_edges = block_border_lines.each_with_object({ x: [], y: [] }) do |line, accum|
+ accum[:x] = (accum[:x] << line[:from][:x] << line[:to][:x]).sort.uniq
+ accum[:y] = (accum[:y] << line[:from][:y] << line[:to][:y]).sort.uniq.reverse
+ end
+ (expect block_edges).to eql block_edges_expected
+ end
+
+ it 'should advance table cell that contains unbreakable block that does not fit on current page' do
+ pdf_theme[:example_border_width] = 0.5
+ pdf_theme[:example_border_color] = '0000ff'
+ pdf_theme[:example_background_color] = 'ffffff'
+ pdf_theme[:page_margin] = 36
+ pdf_theme[:table_cell_padding] = 5
+ before_table_content = ['before table'] * 15 * %(\n\n)
+ block_content = ['block content'] * 15 * %(\n\n)
+ input = <<~EOS
+ #{before_table_content}
+
+ |===
+ a|
+ before block
+
+ [%unbreakable]
+ ====
+ #{block_content}
+
+ block content end
+ ====
+ |===
+
+ after table
+ EOS
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ lines = (to_pdf input, pdf_theme: pdf_theme, analyze: :line).lines
+ (expect pdf.pages).to have_size 2
+ (expect (pdf.find_text 'before table')[-1][:page_number]).to be 1
+ (expect (pdf.find_unique_text 'after table')[:page_number]).to be 2
+ (expect (pdf.find_unique_text 'before block')[:page_number]).to be 2
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 2
+ (expect (pdf.find_unique_text 'block content end')[:page_number]).to be 2
+ table_edges_expected = { x: [36.0, 576.0], y: [756.0, 250.0] }
+ block_edges_expected = { x: [41.0, 571.0], y: [723.22, 266.74] }
+ table_border_lines = lines.select {|it| it[:color] == 'DDDDDD' }
+ (expect table_border_lines.map {|it| it[:page_number] }.uniq).to eql [2]
+ table_edges = table_border_lines.each_with_object({ x: [], y: [] }) do |line, accum|
+ accum[:x] = (accum[:x] << line[:from][:x] << line[:to][:x]).sort.uniq
+ from_y = (line[:from][:y].ceil - (line[:from][:y].ceil % 2)).floor.to_f
+ to_y = (line[:to][:y].ceil - (line[:to][:y].ceil % 2)).floor.to_f
+ accum[:y] = (accum[:y] << from_y << to_y).sort.uniq.reverse
+ end
+ (expect table_edges).to eql table_edges_expected
+ block_border_lines = lines.select {|it| it[:color] == '0000FF' }
+ (expect block_border_lines.map {|it| it[:page_number] }.uniq).to eql [2]
+ block_edges = block_border_lines.each_with_object({ x: [], y: [] }) do |line, accum|
+ accum[:x] = (accum[:x] << line[:from][:x] << line[:to][:x]).sort.uniq
+ accum[:y] = (accum[:y] << line[:from][:y] << line[:to][:y]).sort.uniq.reverse
+ end
+ (expect block_edges).to eql block_edges_expected
+ end
+
+ it 'should advance table cell and truncate child block taller than page' do
+ pdf_theme[:example_border_width] = 0.5
+ pdf_theme[:example_border_color] = '0000ff'
+ pdf_theme[:example_background_color] = 'ffffff'
+ pdf_theme[:page_margin] = 36
+ pdf_theme[:table_cell_padding] = 5
+ before_table_content = ['before table'] * 15 * %(\n\n)
+ block_content = ['block content'] * 25 * %(\n\n)
+ input = <<~EOS
+ #{before_table_content}
+
+ |===
+ a|
+ ====
+ #{block_content}
+
+ block content end
+ ====
+ |===
+
+ after table
+ EOS
+ (expect do
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ lines = (to_pdf input, pdf_theme: pdf_theme, analyze: :line).lines
+ (expect pdf.pages).to have_size 3
+ (expect (pdf.find_text 'before table')[-1][:page_number]).to be 1
+ (expect (pdf.find_unique_text 'after table')[:page_number]).to be 3
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 2
+ (expect (pdf.find_unique_text 'block content end')).to be_nil
+ table_edges_expected = { x: [36.0, 576.0], y: [756.0, 36.0] }
+ block_edges_expected = { x: [41.0, 571.0], y: [751.0, 41.0] }
+ table_border_lines = lines.select {|it| it[:color] == 'DDDDDD' }
+ (expect table_border_lines.map {|it| it[:page_number] }.uniq).to eql [2]
+ table_edges = table_border_lines.each_with_object({ x: [], y: [] }) do |line, accum|
+ accum[:x] = (accum[:x] << line[:from][:x] << line[:to][:x]).sort.uniq
+ from_y = (line[:from][:y].ceil - (line[:from][:y].ceil % 2)).floor.to_f
+ to_y = (line[:to][:y].ceil - (line[:to][:y].ceil % 2)).floor.to_f
+ accum[:y] = (accum[:y] << from_y << to_y).sort.uniq.reverse
+ end
+ (expect table_edges).to eql table_edges_expected
+ block_border_lines = lines.select {|it| it[:color] == '0000FF' }
+ (expect block_border_lines.map {|it| it[:page_number] }.uniq).to eql [2]
+ block_edges = block_border_lines.each_with_object({ x: [], y: [] }) do |line, accum|
+ accum[:x] = (accum[:x] << line[:from][:x] << line[:to][:x]).sort.uniq
+ accum[:y] = (accum[:y] << line[:from][:y] << line[:to][:y]).sort.uniq.reverse
+ end
+ (expect block_edges).to eql block_edges_expected
+ fragment_line = lines.find {|it| it[:color] == 'FFFFFF' && it[:to][:y] == 41.0 }
+ (expect fragment_line).not_to be_nil
+ (expect fragment_line[:style]).to eql :dashed
+ end).to log_message severity: :ERROR, message: '~the table cell on page 2 has been truncated'
+ end
+ end
+ end
+
+ describe 'anchor' do
+ it 'should keep anchor with unbreakable block that is advanced to new page' do
+ before_block_content = ['before block'] * 15 * %(\n\n)
+ block_content = ['block content'] * 15 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme
+ #{before_block_content}
+
+ [#block-id%unbreakable]
+ ====
+ #{block_content}
+ ====
+ EOS
+
+ pages = pdf.pages
+ (expect (pages[0].text.split %r/\n+/).uniq.compact).to eql ['before block']
+ (expect (pages[1].text.split %r/\n+/).uniq.compact).to eql ['block content']
+ (expect pages).to have_size 2
+ dest = get_dest pdf, 'block-id'
+ (expect dest[:page_number]).to be 2
+ (expect dest[:y].to_f).to eql 742.0
+ end
+
+ it 'should keep anchor with breakable block that is advanced to next page' do
+ before_block_content = ['before block'] * 24 * %(\n\n)
+ block_content = ['block content'] * 15 * %(\n\n)
+ pdf = to_pdf <<~EOS, pdf_theme: pdf_theme
+ #{before_block_content}
+
+ .block title
+ [#block-id]
+ ====
+ #{block_content}
+ ====
+ EOS
+
+ pages = pdf.pages
+ (expect pages).to have_size 2
+ dest = get_dest pdf, 'block-id'
+ (expect dest[:page_number]).to be 2
+ (expect dest[:y].to_f).to eql 742.0
+ end
+ end
+end
diff --git a/spec/example_spec.rb b/spec/example_spec.rb
index 897edc8b..41357fa4 100644
--- a/spec/example_spec.rb
+++ b/spec/example_spec.rb
@@ -7,6 +7,7 @@ describe 'Asciidoctor::PDF::Converter - Example' do
pdf = to_pdf <<~EOS, analyze: true
#{(['filler'] * 15).join %(\n\n)}
+ [%unbreakable]
====
#{(['content'] * 15).join %(\n\n)}
====
@@ -45,6 +46,7 @@ describe 'Asciidoctor::PDF::Converter - Example' do
#{(['filler'] * 15).join %(\n\n)}
.Title
+ [%unbreakable]
====
#{(['content'] * 15).join %(\n\n)}
====
@@ -59,6 +61,7 @@ describe 'Asciidoctor::PDF::Converter - Example' do
it 'should split block if it cannot fit on one page' do
pdf = to_pdf <<~EOS, analyze: true
.Title
+ [%unbreakable]
====
#{(['content'] * 30).join %(\n\n)}
====
@@ -74,6 +77,7 @@ describe 'Asciidoctor::PDF::Converter - Example' do
it 'should split border when block is split across pages', visual: true do
to_file = to_pdf_file <<~EOS, 'example-page-split.pdf'
.Title
+ [%unbreakable]
====
#{(['content'] * 30).join %(\n\n)}
====
@@ -82,6 +86,26 @@ describe 'Asciidoctor::PDF::Converter - Example' do
(expect to_file).to visually_match 'example-page-split.pdf'
end
+ it 'should draw border around whole block when block contains nested unbreakable block', visual: true do
+ to_file = to_pdf_file <<~EOS, 'example-with-nested-block-page-split.pdf'
+ .Title
+ ====
+ #{(['content'] * 25).join %(\n\n)}
+
+ [NOTE%unbreakable]
+ ======
+ This block does not fit on a single page.
+
+ Therefore, it is split across multiple pages.
+ ======
+
+ #{(['content'] * 5).join %(\n\n)}
+ ====
+ EOS
+
+ (expect to_file).to visually_match 'example-with-nested-block-page-split.pdf'
+ end
+
it 'should not add signifier and numeral to caption if example-caption attribute is unset' do
pdf = to_pdf <<~'EOS', analyze: true
:!example-caption:
diff --git a/spec/fixtures/tall-spacer.png b/spec/fixtures/tall-spacer.png
new file mode 100644
index 00000000..7a533174
--- /dev/null
+++ b/spec/fixtures/tall-spacer.png
Binary files differ
diff --git a/spec/footnote_spec.rb b/spec/footnote_spec.rb
index f9c9ae73..6cff3952 100644
--- a/spec/footnote_spec.rb
+++ b/spec/footnote_spec.rb
@@ -144,6 +144,40 @@ describe 'Asciidoctor::PDF::Converter - Footnote' do
(expect text[-1][:y]).to be < 60
end
+ it 'should place footnotes at end of page if start on following page' do
+ pdf = with_content_spacer 10, 700 do |spacer_path|
+ to_pdf <<~EOS, pdf_theme: { page_margin: 50 }, analyze: true
+ image::#{spacer_path}[]
+
+ About this thing.footnote:[More about this thing.]
+ About that thing.footnote:[More about that thing.]
+ And so on.
+ EOS
+ end
+
+ (expect pdf.pages).to have_size 2
+ last_text = pdf.find_unique_text %r/And so on/
+ (expect last_text[:page_number]).to be 1
+ first_footnote = pdf.find_unique_text %r/More about this thing/
+ (expect first_footnote[:page_number]).to be 2
+ last_page_texts = pdf.find_text page_number: 2
+ footnotes_height = (last_page_texts[0][:y] + last_page_texts[0][:font_size]) - last_page_texts[-1][:y]
+ (expect first_footnote[:y]).to be < (footnotes_height + 50)
+ end
+
+ it 'should not move footnotes down if height exceeds height of page' do
+ footnotes = ['footnote:[Lots more about this thing.]'] * 50
+ pdf = to_pdf <<~EOS, analyze: true
+ About this thing.#{footnotes}
+ EOS
+
+ (expect pdf.pages).to have_size 2
+ main_text = (pdf.find_text %r/About this thing\./)[0]
+ first_footnote_text = (pdf.find_text %r/Lots more/)[0]
+ delta = main_text[:y] - first_footnote_text[:y]
+ (expect delta).to be < 60
+ end
+
it 'should allow footnote to be externalized so it can be used multiple times' do
pdf = to_pdf <<~'EOS', analyze: true
:fn-disclaimer: footnote:disclaimer[Opinions are my own.]
@@ -346,9 +380,13 @@ describe 'Asciidoctor::PDF::Converter - Footnote' do
(expect f1).to eql '[1] Only available if you have an active subscription.'
end
- it 'should not duplicate footnotes that are included in keep together content' do
+ it 'should not duplicate footnotes that are included in unbreakable blocks' do
pdf = to_pdf <<~'EOS', analyze: true
+ Here we go.
+
+ [%unbreakable]
****
+ [%unbreakable]
____
Make it rain.footnote:[money]
____
diff --git a/spec/image_spec.rb b/spec/image_spec.rb
index 65f3db37..bb37d117 100644
--- a/spec/image_spec.rb
+++ b/spec/image_spec.rb
@@ -1826,6 +1826,18 @@ describe 'Asciidoctor::PDF::Converter - Image' do
(expect pdf.pages[1].text).to eql 'Figure 1. Image caption'
end
+ it 'should raise error if caption does not fit on a single page' do
+ (expect do
+ caption = (['excessive caption'] * 300).join ' '
+ to_pdf <<~EOS
+ before image
+
+ .#{caption}
+ image::tall-diagram.png[Tall diagram]
+ EOS
+ end).to raise_exception Prawn::Errors::CannotFit
+ end
+
it 'should scale down SVG at top of page to fit image and caption if dimensions exceed page size', visual: true do
to_file = to_pdf_file <<~EOS, 'image-svg-with-caption-scale-to-fit-page.pdf'
:pdf-page-size: Letter
diff --git a/spec/listing_spec.rb b/spec/listing_spec.rb
index 00ec11d8..265bcbcc 100644
--- a/spec/listing_spec.rb
+++ b/spec/listing_spec.rb
@@ -20,10 +20,11 @@ describe 'Asciidoctor::PDF::Converter - Listing' do
(expect lines[1][:from][:y] - lines[1][:to][:y]).to be <= 1
end
- it 'should move block to next page if it will fit to avoid splitting it' do
+ it 'should move unbreakable block shorter than page to next page to avoid splitting it' do
pdf = to_pdf <<~EOS, analyze: true
#{(['paragraph'] * 20).join (?\n * 2)}
+ [%unbreakable]
----
#{(['listing'] * 20).join ?\n}
----
@@ -37,7 +38,7 @@ describe 'Asciidoctor::PDF::Converter - Listing' do
pdf = to_pdf <<~EOS
#{(['paragraph'] * 20).join (?\n * 2)}
- [#listing-1]
+ [#listing-1%unbreakable]
----
#{(['listing'] * 20).join ?\n}
----
@@ -50,7 +51,7 @@ describe 'Asciidoctor::PDF::Converter - Listing' do
(expect dest[:y]).to eql 805.89
end
- it 'should place anchor above top margin of block' do
+ it 'should place anchor below top margin of block' do
input = <<~'EOS'
paragraph
@@ -66,14 +67,14 @@ describe 'Asciidoctor::PDF::Converter - Listing' do
pdf = to_pdf input, pdf_theme: pdf_theme
(expect (dest = get_dest pdf, 'listing-1')).not_to be_nil
(expect dest[:page_number]).to be 1
- (expect dest[:y]).to eql lines[0][:from][:y] + 10
+ (expect dest[:y]).to eql lines[0][:from][:y]
end
it 'should place anchor at top of block if advanced to next page' do
input = <<~EOS
paragraph
- [#listing-1]
+ [#listing-1%unbreakable]
----
#{(['filler'] * 25).join %(\n\n)}
----
@@ -117,7 +118,7 @@ describe 'Asciidoctor::PDF::Converter - Listing' do
(expect to_file).to visually_match 'listing-page-split.pdf'
end
- it 'should resize font size to prevent wrapping if autofit option is set' do
+ it 'should resize font to prevent wrapping if autofit option is set' do
pdf = to_pdf <<~'EOS', analyze: true
[%autofit]
----
@@ -129,7 +130,7 @@ describe 'Asciidoctor::PDF::Converter - Listing' do
(expect pdf.text[0][:font_size]).to be < build_pdf_theme.code_font_size
end
- it 'should not resize font size if not necessary' do
+ it 'should not resize font if not necessary' do
pdf = to_pdf <<~'EOS', analyze: true
[%autofit]
----
@@ -141,7 +142,7 @@ describe 'Asciidoctor::PDF::Converter - Listing' do
(expect pdf.text[0][:font_size]).to eql 11
end
- it 'should not resize font size more than base minimum font size' do
+ it 'should not resize font more than base minimum font size' do
pdf = to_pdf <<~'EOS', pdf_theme: { base_font_size_min: 8 }, analyze: true
[%autofit]
----
@@ -153,7 +154,7 @@ describe 'Asciidoctor::PDF::Converter - Listing' do
(expect pdf.text[0][:font_size]).to be 8
end
- it 'should not resize font size more than code minimum font size' do
+ it 'should not resize font more than code minimum font size' do
pdf = to_pdf <<~'EOS', pdf_theme: { base_font_size_min: 0, code_font_size_min: 8 }, analyze: true
[%autofit]
----
diff --git a/spec/open_spec.rb b/spec/open_spec.rb
index d24b5e24..2f790751 100644
--- a/spec/open_spec.rb
+++ b/spec/open_spec.rb
@@ -3,6 +3,23 @@
require_relative 'spec_helper'
describe 'Asciidoctor::PDF::Converter - Open' do
+ it 'should be breakable by default', breakable: true do
+ with_content_spacer 10, 720 do |spacer_path|
+ pdf = to_pdf <<~EOS, analyze: true
+ image::#{spacer_path}[]
+
+ --
+ first page
+
+ second page
+ --
+ EOS
+ (expect pdf.pages).to have_size 2
+ (expect (pdf.find_unique_text 'first page')[:page_number]).to be 1
+ (expect (pdf.find_unique_text 'second page')[:page_number]).to be 2
+ end
+ end
+
it 'should keep block together when it has the unbreakable option', visual: true do
to_file = to_pdf_file <<~EOS, 'open-unbreakable-option-fit.pdf'
Make it rain.footnote:[money]
@@ -86,4 +103,52 @@ describe 'Asciidoctor::PDF::Converter - Open' do
(expect (pdf.find_unique_text 'content')[:page_number]).to be 2
(expect (pdf.find_unique_text 'Title')[:page_number]).to be 2
end
+
+ it 'should not dry run block unless necessary' do
+ calls = []
+ extensions = proc do
+ block :spy do
+ on_context :paragraph
+ process do |parent, reader, attrs|
+ block = create_paragraph parent, reader.lines, attrs
+ block.instance_variable_set :@_calls, calls
+ block.extend (Module.new do
+ def content
+ @_calls << (caller.join ?\n) if document.converter.scratch? # rubocop:disable RSpec/InstanceVariable
+ super
+ end
+ end)
+ end
+ end
+ end
+
+ {
+ '' => false,
+ %(.title) => false,
+ %([#idname]) => false,
+ %([%unbreakable]) => false,
+ %(before\n) => false,
+ %(before\n\n.title) => true,
+ %(before\n\n[#idname]) => true,
+ %(before\n\n[%unbreakable]) => true,
+ }.each do |before_block, dry_run|
+ input = <<~EOS.lstrip
+ #{before_block}
+ --
+ #{['block content'] * 4 * %(\n\n)}
+
+ [spy]
+ block content
+ --
+ EOS
+ pdf = to_pdf input, extensions: extensions, analyze: true
+ (expect pdf.pages).to have_size 1
+ (expect (pdf.find_text 'block content')[0][:page_number]).to be 1
+ if dry_run
+ (expect calls).not_to be_empty
+ else
+ (expect calls).to be_empty
+ end
+ end
+ end
end
diff --git a/spec/quote_spec.rb b/spec/quote_spec.rb
index e1bbaeef..4aeb7b04 100644
--- a/spec/quote_spec.rb
+++ b/spec/quote_spec.rb
@@ -196,21 +196,56 @@ describe 'Asciidoctor::PDF::Converter - Quote' do
end
it 'should advance to next page if block is split and caption does not fit' do
- quote = %(Power concedes nothing without a demand. +\nIt never did and it never will.)
+ quote = ['Power concedes nothing without a demand.', 'It never did and it never will.'].join %( +\n)
+ with_content_spacer 10, 705 do |spacer_path|
+ input = <<~EOS
+ before
- pdf = to_pdf <<~EOS, pdf_theme: { thematic_break_margin_top: 700 }, analyze: true
- before
+ image::#{spacer_path}[]
- '''
+ .Sage advice by Frederick Douglass
+ ____
+ #{([quote] * 18).join %(\n\n)}
+ ____
+ EOS
- .Sage advice by Frederick Douglass
- ____
- #{([quote] * 18).join %(\n\n)}
- ____
- EOS
+ pdf = to_pdf input, analyze: true
+ advice_text = pdf.find_unique_text 'Sage advice by Frederick Douglass'
+ (expect advice_text[:page_number]).to be 2
+ (expect advice_text[:y] + advice_text[:font_size]).to ((be_within 1).of 805)
+ end
+ end
+
+ it 'should keep caption with block and draw border across extent if only caption fits on current page' do
+ block_content = ['text of quote'] * 15 * %(\n\n)
+ pdf_theme = { prose_margin_bottom: 12, blockquote_padding: [0, 0, -12, 15] }
+ with_content_spacer 10, 690 do |spacer_path|
+ input = <<~EOS
+ before
+
+ image::#{spacer_path}[]
+
+ .Sage advice
+ ____
+ #{block_content}
+ ____
+ EOS
- advice_text = pdf.find_unique_text 'Sage advice by Frederick Douglass'
- (expect advice_text[:page_number]).to be 2
- (expect advice_text[:y]).to be > 795
+ pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
+ lines = (to_pdf input, pdf_theme: pdf_theme, analyze: :line).lines
+ (expect pdf.pages).to have_size 2
+ advice_text = pdf.find_unique_text 'Sage advice'
+ (expect advice_text[:page_number]).to be 2
+ (expect advice_text[:y] + advice_text[:font_size]).to ((be_within 1).of 805)
+ quote_text = (pdf.find_text 'text of quote')
+ # NOTE: y location of text does not include descender
+ quote_text_start_y = quote_text[0][:y] + quote_text[0][:font_size] + 1.5
+ quote_text_end_y = quote_text[-1][:y] - 4.5
+ border_left_line = lines.find {|it| it[:color] == 'EEEEEE' }
+ (expect border_left_line[:page_number]).to be 2
+ border_left_line_start_y, border_left_line_end_y = [border_left_line[:from][:y], border_left_line[:to][:y]].sort.reverse
+ (expect border_left_line_start_y).to (be_within 0.5).of (quote_text_start_y)
+ (expect border_left_line_end_y).to (be_within 0.5).of (quote_text_end_y)
+ end
end
end
diff --git a/spec/reference/admonition-page-split.pdf b/spec/reference/admonition-page-split.pdf
index c74e6af3..a2b20b9b 100644
--- a/spec/reference/admonition-page-split.pdf
+++ b/spec/reference/admonition-page-split.pdf
@@ -2,28 +2,28 @@
%
1 0 obj
<< /Title (Untitled)
-/Creator (Asciidoctor PDF 1.5.0.rc.3.dev, based on Prawn 2.2.2)
-/Producer (Asciidoctor PDF 1.5.0.rc.3.dev, based on Prawn 2.2.2)
-/ModDate (D:20200119105628+00'00')
-/CreationDate (D:20200119105628+00'00')
+/Creator (Asciidoctor PDF 2.0.0.dev, based on Prawn 2.4.0)
+/Producer (Asciidoctor PDF 2.0.0.dev, based on Prawn 2.4.0)
+/ModDate (D:20220310040839-07'00')
+/CreationDate (D:20220310040839-07'00')
>>
endobj
2 0 obj
<< /Type /Catalog
/Pages 3 0 R
-/Names 9 0 R
-/Outlines 17 0 R
-/PageLabels 19 0 R
+/Outlines 12 0 R
+/PageLabels 14 0 R
/PageMode /UseOutlines
/OpenAction [7 0 R /FitH 841.89]
/ViewerPreferences << /DisplayDocTitle true
>>
+/Names 16 0 R
>>
endobj
3 0 obj
<< /Type /Pages
-/Count 3
-/Kids [7 0 R 13 0 R 15 0 R]
+/Count 2
+/Kids [7 0 R 10 0 R]
>>
endobj
4 0 obj
@@ -48,7 +48,7 @@ endobj
>>
endobj
6 0 obj
-<< /Length 153
+<< /Length 4506
>>
stream
q
@@ -65,86 +65,36 @@ ET
0.0 0.0 0.0 SCN
0.0 0.0 0.0 scn
-Q
-
-endstream
-endobj
-7 0 obj
-<< /Type /Page
-/Parent 3 0 R
-/MediaBox [0 0 595.28 841.89]
-/CropBox [0 0 595.28 841.89]
-/BleedBox [0 0 595.28 841.89]
-/TrimBox [0 0 595.28 841.89]
-/ArtBox [0 0 595.28 841.89]
-/Contents 6 0 R
-/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
-/Font << /F1.0 11 0 R
->>
->>
->>
-endobj
-8 0 obj
-[7 0 R /XYZ 0 841.89 null]
-endobj
-9 0 obj
-<< /Type /Names
-/Dests 10 0 R
->>
-endobj
-10 0 obj
-<< /Names [(__anchor-top) 8 0 R]
->>
-endobj
-11 0 obj
-<< /Type /Font
-/BaseFont /9be14d+NotoSerif
-/Subtype /TrueType
-/FontDescriptor 21 0 R
-/FirstChar 32
-/LastChar 255
-/Widths 23 0 R
-/ToUnicode 22 0 R
->>
-endobj
-12 0 obj
-<< /Length 4471
->>
-stream
q
-q
-/DeviceRGB cs
-0.9608 0.6627 0.6627 scn
-48.24 805.89 m
-547.04 805.89 l
-547.04 805.89 547.04 805.89 547.04 805.89 c
+0.96078 0.66275 0.66275 scn
+48.24 778.11 m
+547.04 778.11 l
+547.04 778.11 547.04 778.11 547.04 778.11 c
547.04 48.24 l
547.04 48.24 547.04 48.24 547.04 48.24 c
48.24 48.24 l
48.24 48.24 48.24 48.24 48.24 48.24 c
-48.24 805.89 l
-48.24 805.89 48.24 805.89 48.24 805.89 c
+48.24 778.11 l
+48.24 778.11 48.24 778.11 48.24 778.11 c
h
f
-/DeviceRGB CS
0.2 0.2 0.2 SCN
0.5 w
-48.24 805.89 m
-547.04 805.89 l
-547.04 805.89 547.04 805.89 547.04 805.89 c
+48.24 778.11 m
+547.04 778.11 l
+547.04 778.11 547.04 778.11 547.04 778.11 c
547.04 48.24 l
547.04 48.24 547.04 48.24 547.04 48.24 c
48.24 48.24 l
48.24 48.24 48.24 48.24 48.24 48.24 c
-48.24 805.89 l
-48.24 805.89 48.24 805.89 48.24 805.89 c
+48.24 778.11 l
+48.24 778.11 48.24 778.11 48.24 778.11 c
h
S
Q
q
+0.96078 0.66275 0.66275 SCN
0.6 w
-/DeviceRGB CS
-0.9608 0.6627 0.6627 SCN
[2.4 2.4] 0.0 d
48.74 48.24 m
546.54 48.24 l
@@ -152,32 +102,22 @@ S
Q
q
0.5 w
-/DeviceRGB CS
-0.9333 0.9333 0.9333 SCN
-102.2593 805.89 m
-102.2593 48.24 l
+0.93333 0.93333 0.93333 SCN
+102.25929 778.11 m
+102.25929 48.24 l
S
Q
-/DeviceRGB cs
0.2 0.2 0.2 scn
-/DeviceRGB CS
0.2 0.2 0.2 SCN
-
-BT
-60.24 422.991 Td
-/F2.0 10.5 Tf
-[<4e4f> 20.0195 <5445>] TJ
-ET
-
0.0 0.0 0.0 SCN
0.0 0.0 0.0 scn
0.2 0.2 0.2 scn
0.2 0.2 0.2 SCN
BT
-114.2593 789.926 Td
-/F1.0 10.5 Tf
-<61646d6f6e6974696f6e> Tj
+60.24 409.101 Td
+/F2.0 10.5 Tf
+[<4e4f> 20.01953 <5445>] TJ
ET
0.0 0.0 0.0 SCN
@@ -186,7 +126,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 762.146 Td
+114.25929 762.146 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -197,7 +137,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 734.366 Td
+114.25929 734.366 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -208,7 +148,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 706.586 Td
+114.25929 706.586 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -219,7 +159,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 678.806 Td
+114.25929 678.806 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -230,7 +170,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 651.026 Td
+114.25929 651.026 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -241,7 +181,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 623.246 Td
+114.25929 623.246 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -252,7 +192,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 595.466 Td
+114.25929 595.466 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -263,7 +203,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 567.686 Td
+114.25929 567.686 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -274,7 +214,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 539.906 Td
+114.25929 539.906 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -285,7 +225,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 512.126 Td
+114.25929 512.126 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -296,7 +236,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 484.346 Td
+114.25929 484.346 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -307,7 +247,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 456.566 Td
+114.25929 456.566 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -318,7 +258,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 428.786 Td
+114.25929 428.786 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -329,7 +269,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 401.006 Td
+114.25929 401.006 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -340,7 +280,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 373.226 Td
+114.25929 373.226 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -351,7 +291,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 345.446 Td
+114.25929 345.446 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -362,7 +302,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 317.666 Td
+114.25929 317.666 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -373,7 +313,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 289.886 Td
+114.25929 289.886 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -384,7 +324,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 262.106 Td
+114.25929 262.106 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -395,7 +335,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 234.326 Td
+114.25929 234.326 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -406,7 +346,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 206.546 Td
+114.25929 206.546 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -417,7 +357,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 178.766 Td
+114.25929 178.766 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -428,7 +368,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 150.986 Td
+114.25929 150.986 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -439,7 +379,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 123.206 Td
+114.25929 123.206 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -450,7 +390,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 95.426 Td
+114.25929 95.426 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -461,7 +401,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 67.646 Td
+114.25929 67.646 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -472,7 +412,7 @@ Q
endstream
endobj
-13 0 obj
+7 0 obj
<< /Type /Page
/Parent 3 0 R
/MediaBox [0 0 595.28 841.89]
@@ -480,29 +420,40 @@ endobj
/BleedBox [0 0 595.28 841.89]
/TrimBox [0 0 595.28 841.89]
/ArtBox [0 0 595.28 841.89]
-/Contents 12 0 R
+/Contents 6 0 R
/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
-/Font << /F2.0 16 0 R
-/F1.0 11 0 R
+/Font << /F1.0 8 0 R
+/F2.0 11 0 R
>>
>>
>>
endobj
-14 0 obj
-<< /Length 2616
+8 0 obj
+<< /Type /Font
+/BaseFont /9be14d+NotoSerif
+/Subtype /TrueType
+/FontDescriptor 19 0 R
+/FirstChar 32
+/LastChar 255
+/Widths 21 0 R
+/ToUnicode 20 0 R
+>>
+endobj
+9 0 obj
+<< /Length 2790
>>
stream
q
q
/DeviceRGB cs
-0.9608 0.6627 0.6627 scn
+0.96078 0.66275 0.66275 scn
48.24 805.89 m
547.04 805.89 l
547.04 805.89 547.04 805.89 547.04 805.89 c
-547.04 453.5 l
-547.04 453.5 547.04 453.5 547.04 453.5 c
-48.24 453.5 l
-48.24 453.5 48.24 453.5 48.24 453.5 c
+547.04 425.72 l
+547.04 425.72 547.04 425.72 547.04 425.72 c
+48.24 425.72 l
+48.24 425.72 48.24 425.72 48.24 425.72 c
48.24 805.89 l
48.24 805.89 48.24 805.89 48.24 805.89 c
h
@@ -513,19 +464,19 @@ f
48.24 805.89 m
547.04 805.89 l
547.04 805.89 547.04 805.89 547.04 805.89 c
-547.04 453.5 l
-547.04 453.5 547.04 453.5 547.04 453.5 c
-48.24 453.5 l
-48.24 453.5 48.24 453.5 48.24 453.5 c
+547.04 425.72 l
+547.04 425.72 547.04 425.72 547.04 425.72 c
+48.24 425.72 l
+48.24 425.72 48.24 425.72 48.24 425.72 c
48.24 805.89 l
48.24 805.89 48.24 805.89 48.24 805.89 c
h
S
Q
q
-0.6 w
/DeviceRGB CS
-0.9608 0.6627 0.6627 SCN
+0.96078 0.66275 0.66275 SCN
+0.6 w
[2.4 2.4] 0.0 d
48.74 805.89 m
546.54 805.89 l
@@ -534,9 +485,9 @@ Q
q
0.5 w
/DeviceRGB CS
-0.9333 0.9333 0.9333 SCN
-102.2593 805.89 m
-102.2593 453.5 l
+0.93333 0.93333 0.93333 SCN
+102.25929 805.89 m
+102.25929 425.72 l
S
Q
/DeviceRGB cs
@@ -545,7 +496,7 @@ Q
0.2 0.2 0.2 SCN
BT
-114.2593 794.676 Td
+114.25929 794.676 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -556,7 +507,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 766.896 Td
+114.25929 766.896 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -567,7 +518,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 739.116 Td
+114.25929 739.116 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -578,7 +529,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 711.336 Td
+114.25929 711.336 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -589,7 +540,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 683.556 Td
+114.25929 683.556 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -600,7 +551,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 655.776 Td
+114.25929 655.776 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -611,7 +562,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 627.996 Td
+114.25929 627.996 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -622,7 +573,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 600.216 Td
+114.25929 600.216 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -633,7 +584,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 572.436 Td
+114.25929 572.436 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -644,7 +595,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 544.656 Td
+114.25929 544.656 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -655,7 +606,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 516.876 Td
+114.25929 516.876 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -666,7 +617,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 489.096 Td
+114.25929 489.096 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -677,7 +628,7 @@ ET
0.2 0.2 0.2 SCN
BT
-114.2593 461.316 Td
+114.25929 461.316 Td
/F1.0 10.5 Tf
<61646d6f6e6974696f6e> Tj
ET
@@ -688,7 +639,18 @@ ET
0.2 0.2 0.2 SCN
BT
-48.24 429.536 Td
+114.25929 433.536 Td
+/F1.0 10.5 Tf
+<61646d6f6e6974696f6e> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+48.24 401.756 Td
/F1.0 10.5 Tf
<6166746572> Tj
ET
@@ -699,7 +661,7 @@ Q
endstream
endobj
-15 0 obj
+10 0 obj
<< /Type /Page
/Parent 3 0 R
/MediaBox [0 0 595.28 841.89]
@@ -707,180 +669,187 @@ endobj
/BleedBox [0 0 595.28 841.89]
/TrimBox [0 0 595.28 841.89]
/ArtBox [0 0 595.28 841.89]
-/Contents 14 0 R
+/Contents 9 0 R
/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
-/Font << /F1.0 11 0 R
+/Font << /F1.0 8 0 R
>>
>>
>>
endobj
-16 0 obj
+11 0 obj
<< /Type /Font
/BaseFont /aa6569+NotoSerif-Bold
/Subtype /TrueType
-/FontDescriptor 25 0 R
+/FontDescriptor 23 0 R
/FirstChar 32
/LastChar 255
-/Widths 27 0 R
-/ToUnicode 26 0 R
+/Widths 25 0 R
+/ToUnicode 24 0 R
>>
endobj
-17 0 obj
+12 0 obj
<< /Type /Outlines
/Count 1
-/First 18 0 R
-/Last 18 0 R
+/First 13 0 R
+/Last 13 0 R
>>
endobj
-18 0 obj
+13 0 obj
<< /Title <feff0055006e007400690074006c00650064>
-/Parent 17 0 R
+/Parent 12 0 R
/Count 0
/Dest [7 0 R /XYZ 0 841.89 null]
>>
endobj
-19 0 obj
+14 0 obj
<< /Nums [0 << /P (1)
>> 1 << /P (2)
->> 2 << /P (3)
>>]
>>
endobj
-20 0 obj
-<< /Length1 8148
-/Length 4891
+15 0 obj
+[7 0 R /XYZ 0 841.89 null]
+endobj
+16 0 obj
+<< /Type /Names
+/Dests 17 0 R
+>>
+endobj
+17 0 obj
+<< /Names [(__anchor-top) 15 0 R]
+>>
+endobj
+18 0 obj
+<< /Length1 8180
+/Length 4877
/Filter [/FlateDecode]
>>
stream
-xY p[Օ>='ĉ/Ȗ/vlG$ĉd9R&-VbK$ NB5),00,jh ˶NZNiYS0Ҟ{8!ttsνJ
-ok|3ۣQr@
-e:8(oEE% ~c臯:ޛ\@= @c4~ca
-
-J]T!REhoM-r?clWoeo39%hm9hs̺k~Jߑm{v<QI ᚖ=֢ǖ?0,=[*Z͝M_!2;)j`|sƥBҚ<$d& Vkw¦*暎>۷h^j6,mz2'в|hSr1qlqm6P7tn{z4RST1|@:κaOaȱggg=݈#79osٗ|v
- i#$WTٌd^"݆]i鉴ed.Y9xiO}mO\} vT}~08}/~+^A%T5!qկgTSTB
-1 B],Nq,xG_?޵<*T6Ӽ>gcՁ_0t{{-VYSݺ&{4P*\ P,oKg /o`wwod0l/XVX_Qkk.1%/KGGyDO:-,[r,Kcsei1fudbV U
-
-IՐM\ ŤC5'^RRt \䢂`;yO3@Q &*x&6pH](Y.aW"*x6
-|MO<6i<#ѹXhr*Awݑt-cz8ƃ(A B0 Sv-C$w^8Xց4>W{Źgm2
-.?ƻM,Cl_$IE2t}h!-!46XR<[ǙcP$Lk-508l,?ǂ33p8Dchdb׌@~M1"_r7`<4#A-z;xX(لGPÌßөP8i]5HMQ E6)"N3Lo#pDXhzmY&{-d0aE"+Ͽh-
-\ASVkUueb|<1EMԯۏ#6芄HYLƃ8zr6hb*H]{@4N+M+TYYk6,ƈ?LK;\UJ;;\*wg=Bw w:]t`#(uю~/wT`hvHf!
-[m
-<fLMg Y%~i~^+؅u
-\fk#^;pƲ^|W_/oat[>la:vY_q11(fh/+EEnIg[2פo?_g3\H\_@ ۑ ^'fN8'Due_BDM["I@ޛ8΅<_zlbIێ~Y:C~τnDsXG)ARaR[ Ge, c%.DH̓)"'SD"e2]&ɩeJ hc {ꡔ%!(倛dNq(+I&b\3N}s9C\EL !hPWvTyi~x ˧xC, D"Ȱx_["و|ݟAB\W~4]!/v]&jeRh.սU~_)x;~q TFvӏǍ >JFxģ"<$ȩGTYMyyy;OOl J<9¿,\\P[xjµԂ}d?ߘ"鎈H{c~ʨU$YJn;3-t@a#x;n[UH?W<)^E⮚Qਸ=V|8"=ݙnuuwQ];unߺT\awkbNp TSEDMC#.oIe\bm耬y@EB{,tnw<oO`*߲Xx<0ďGP6C|㐮/qr8~ ZX5B1f37샠9Aqs1؜^{5<48пooߞޞ.aֺygScCmR]lTj2sseefJD/V9.}\fGxnԑ5m9q-i[$z
-U)Q.CuH^*x]U΅u(L؃:T&>ꔻNL;}i*XjmRtlk'"ls,
-[dž2? y䭮s$o;7)k첖!FNŪ,agH!,蜟_3˕CbyPN̬ w}H"wDh
-' y^zj:.c;= 3pUNP$ۼh+y^Hɂ}xv* ̜ycHue: .HX2z GYZwRW\IAY{Viv=הlCг$ᮁf8^̼J=RPJST zܘ{xDg(qAZ,tyE`Δ̆Ε*ܻLuRk{e`nۙg;ˏi93f~fc’KyiڸeHHgun|y`hFpx.D:[*qT`(@, Wm
-/K1/
+xYT[}$!~ 000H`~ X lǖ@P , '$BLj䌺ɒ$k'yd;)Iwl9mֺ9g]n'm9'KΠ}ڝ'}_{?=
+y}A~\2a俅|ld2~!_:;*!y{~P@fi4O
+ o,d9X$wO [xƏuU~H^a̠R_X{c| y6c{|rI.| W8SY Ԁi-"$e=" dkV#[<CP/؁B"AARU9}e)))ϡϳ>8+| -`Cjh<gw)U<T$9Hn]ǭaއ F a>3 -谻|^Ϙ{tdxh葁}=.~`[끖:foUel*),0fge3tZ(qI=~TMWx)&_(jP?7[-h9u=ei߰$5%Q;N#^/8%U>QNk*9ٌ3dI.̒D+Y!gJV6H){
+I8!ue*b+TG.l+y]*:]ҕί`o JI"pZZzX)(ՒS%HNba^F7$Q.}*U>F`zz$ڳ_
+&'$jVrr.0 {qjGJybϐ65؞e "TЙ
+Mp?.
+E %.7
+z]HMH.É]-q$T _Vg~
+3 "ɋ䧂WxYX.l
+8ƮXC\`lBxOo#f֜<8+n)t<q{yPT^)4/
+
+˄\%RUhjl>Dtʽ3`u&?tSr/im;aw{Wrj齇zYumMmcO}`9P7XwW;2t@֮Y=S ] oTI&
+8G ד3]{v(=wc\ ptTl{~s"8}̏~+^žU\aF "jƠ&2%aؽ%=]~4\ڸP}4tp붼5?|uc$&RyNEldoCr}/]5cŕ6^ b)2l_S e;k;*rMU5ecmg8EԲ;tZZZA])caV5
+UlƯeNs
+& ˴QYN"}G:ȫȢnU[<%zY_&UBŗc97i
+ݜgq 1C2 {A.BtJ :f|ˈ*x'9c,bD_b"[js&c0Q g#LW$/vM=s|nxcw-lcW1vkw!YeefbF=S=6G.rb%{,0!R
endstream
endobj
-21 0 obj
+19 0 obj
<< /Type /FontDescriptor
/FontName /9be14d+NotoSerif
-/FontFile2 20 0 R
+/FontFile2 18 0 R
/FontBBox [-212 -250 1246 1047]
/Flags 6
/StemV 0
-/ItalicAngle 0.0
+/ItalicAngle 0
/Ascent 1068
/Descent -292
/CapHeight 1462
/XHeight 1098
>>
endobj
-22 0 obj
-<< /Length 1286
+20 0 obj
+<< /Length 1278
/Filter [/FlateDecode]
>>
stream
-xenFὮBtHs&@nu{stԒ + }i /y_!ut~]snVyg쾝+|<.÷}v)[eO/_q9_ɯ}ഏOmt_LLv͵LJ1w9)e6=nPҪtN*eP)DT.ʕrU)BYTVʪQ6[ky ^#k5x5F^ky ^#k5x5F^kZy-^+kxZV^kZy-^+kxZV^:y^'uxu:N^:y^'uxu:N^zy=^/xz^^zy=^/xz^ހ7y o xo7 Aހ7y o xo7 Aވ7Fy#(oĻnuW.I$$*I$ITH$$QI"IDD%$JI$$*I$ITH$$III$$%I$I|›Mx o7M&Iބ7ɛ&y$o›Mx o7M.xyUo ުMMol x;
-JÂ31x;
-JJ특xxx+!ÊwBxbx+ށr;2kΜJYeY7+|x oS7+[ƛețךyޢoV浖 -㭌"RW*4XqC^J[(^1»y]k}YM-x Vz[YEVY_}/7*Y%eӫq+:.7JE/3Y(Y*AW RVJS:(u@cD]a*f)9J)o,#\Z>MU\jPS {HSMj{fkyGm[z*Esa>&ӫj%u; 2^W[®v[2쯲u[P:V̡Յ> MBi2 .Ħԇ!dk`=o qWޕwdJF(L164U)x0E~Z?
+xenFὮBtHs&@nu{stԒ + }ik/y_!}t~]snVyg쾝+|<.÷}v)[eO/_q9_ɯ}ഏOmt_LLv͵LJ1w9)e6=n?[i(JKiU:JSz2QeL*EJ̔Ye,*+eU(NU-уk5x5F^ky ^#k5x5F^ky ^#kxZV^kZy-^+kxZV^kZy-^+uxu:N^:y^'uxu:N^:y^'xz^^zy=^/xz^^zy=^/o xo7 Aހ7y o xo7 Aހ7y oxo7jo$JI$$*I$ITH$$QI"IDD%$JI$$*I$ITD$$III$$u>M&Iބ7ɛ&y$o›Mx o7M&Iބ7ɛ&yUoa[5joSor4ۂs
+JJ특xxx+!ÊwBxbx+ށr;2kΜJYeY7+|x oS7+[ƛețךyޢoV浖 -㭌"RW*4XqC^J[(^1»y]k}YM-x e#e_y+h܊7k:/Z5dUluz5n[eB&Si|y(%q& %+S%*ABijPU6\h,(+L,4G5<cD [˧*t [ jjaijIxʪwgFwضR>sh>:OVRP*#i|e0U,*oU/*[U,Nmlx:&\}M)L2\%CHF7էʻBL)jƻJ1:%H߯:=S۳zrmkO媳7
endstream
endobj
-23 0 obj
-[500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 562 613 500 613 535 369 500 500 319 500 500 500 944 645 577 500 500 471 500 352 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500]
+21 0 obj
+[259 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 562 613 500 613 535 369 500 500 319 500 500 500 944 645 577 500 500 471 500 352 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500]
endobj
-24 0 obj
-<< /Length1 6964
-/Length 3943
+22 0 obj
+<< /Length1 7004
+/Length 3932
/Filter [/FlateDecode]
>>
stream
-xY}pSWv?=Im@2A_acֲL mɶ0$#A&H-m$l3xfC^3ɴtvI<NgNJ:P2Lm;lę&,ܫgNwIs~s=ܫ
-P]H
- Ƃ:Uxb" ;vx|tLذ6rJNCVh8$`QCSX(tl1sA T+"c8,JqNQF]DZ'S@Ici 1= 4Fi}rɧQ2ƵLyDGoU++Uƒr2&)`NP  6n[r L-I M'!9L>G# 4bd$<X;j~-b\o9QSʒvA.QG0H,Cth.) 啦QuQ#b{
- 0*~p:g8q:kGmrچm2Өb^.68?VT0V FUِQ֦Quc\}*=ĿDwtǍ` yXX#e,2,RXHNtXnAڛcyy }X;tueH* c(JcM]jv-C=M{h{oq A[{P9$<R4hb,
--_+wJÑ0 FǃFRl#xVb!49??.'c'I:mVɰz:x4Ƅ$/1k8!~YUA{yc4\j7^B|L;hJuy_׮`7j徺@zőx!`߃ |m/"/^D|~na2L^壓$?)c#(fwJ1Fe9I5f,A~-0c( γ 8_[''>>/o{)5$)k*2f'GmW\) yZc8ݏ<AiL9jhkwR(_|'k!Gʰ&+RzD.bO뉀À [IlaWJcd ѰpnA@Z l6@Qbu%kp,ƱBnMVHp
- w Ba $ρ+ah,9?qw#
-9ԸYo?N)=|7C+ΞNO lw+!$쌘D¸yT*]$^
- p&=-=>l<˄RL.,G'=^v
- ;Bԩtg).j #YR'%fH~ ιoUg0ouOA;WڕFpXea(P&$1k33yY)WIe ^;p*Lk&1J49ސ铏#A?Fv`x3vdlnrHF)3fM&CWI8W0٧nOj:h8~[$&sQ˦lg02
-mzz"̵řr/\YZ0n_FԸBc|:La=}B2*0r"TbXpգ Rؒ3@mzWB5LSrp%* >zp#v0EObJԶO3%2]J92ufXmL~?Y}@c.jF~S\ю5XB#JUS9B}&b
-(1kf"{|n SqP#Ly5XFO}Iځ֌RPfpu[34lN١1~R-+Em:Ƣ.o? jWY5P X+YTO%3GͫgH0L`ES7=\Kl׊i4nw)T`s}}f8&Auf$W(#}&E z&ݞ ^|m39?c'܇})m6CwCi
+xY}pSWv?'YX2`pbblK6d$ֳ$CH7l#.G&mg3@gt:i閲Nufɤ[=؎香|=w>
+rGhjQ]_r\vZü ,xJ,&?\Jį!z3`d7iGڗdƇdjS3 lqΫ|!W[l@Z|G,4GZd"NĀt`Nf36BF?*[}W|Lr}QRfEɒOj8=iUtd_MV
+٤~^ahSR;ӂV p03g8Auz$!;|߉P9:ujWM.j#ak5rLG Kr\-ۖrn%*|4+ag3SGZqyCXhܛM+vv=WS9B=fE^٣x٦lnyt8CG<jAg̝dqr#{*np{z
+ы&hr[3jŀ̈́(mC*`QPSb©mEozƢ
+לjSmb0t-D >5 XYVGVd+%Gk,Y&0xaT;j*'W۵Jܽ(iL30"lĘ!}k p@Zvz+xW+^asj_۔L3#L=/ڂ2+@P0O~6q.83,bg,fZĤ,fJ!1>4-o(=eh#*cSDyҦm oaxK3<w)#uM:|gZH)p {@Ye \`=#vell!3Ĕ! n̼h0/ܝ'F>FtZ {B(+7)1<s8U.N?gs@%6FQ2}t{h>Ş- &آ-l$+Gp,mscy0p8}<woӹ+sy:>n'5އM l"/!;LՋWի^՟<}wZ41]]xƧ*V[Go#v[o7cozS̅I|m֤$&g'3mܚ$tMZ&.L\Mܚ'{.|NXwn9Tq2GT1k[AqLjmDFHrM ` >ֻu
+wYCݦbIܗwBq(4\Yp'!\Κ sjqv航?Q!ox_;6Q)q$Lʠ͛L&S5"H&!H%! gRcRcc/&ɋL)ɔPT_HRYl/rݩ+χ(^Et F
+EWE+hsu P[NKBlH$wZsG b)'F9}m<eŰKrՖja[vIeu`nZ?S%D=?]6` 1w\-ћd{ҟ\r.}2yI;b`d D.ykb\]68opC&;[t(Rw~xRg49<]h)c3Z}yei$J
+iF8:(FyF6 >ѹƛV{Wa_&ƧJ^GzC ap#!R.r/
+i4M4h4~4ZE%xZ iht|O  ۤ΃
+Yk^'B؝XT" ;wX,8谕&J%$N(ko,N%iEcSqԏh
+ibD2ҝ~ƦIꧩ?DhldyX_shW`Oi(Ma)*PJ{O<i ɬñH}PIգr~8s|1WCESI'McрBi_\ffjҵT"<4ҪV'vVѶVgvu]p@k}EziOw*a 'A % Ga*<9V:ˮ$W#a:A\E\IDI_4도S烱J"u*6c ˚RN㩤51)H@P;`':QC|㹥Њ(KVI)8*E6
+NaڐE8FF? #DZ蒏Y:ȱ$l&kYy~l)cHKŸseEBΏvS<rRa]s`+B|Y1#n9֑U?Dl caywհ,wpl
+L+Kuq(ElcHxjB\׉=c5jYkYDc!*ŗ}
+'i [yNa\ЅQqd|+{CC-9u ^{Ptof'13 <_GQ]NJ1G5Ira~&1~&#<Ij h𵰓X n]:]1i1eOk+TuyHbĬqFqdk e ,OGfuom+
endstream
endobj
-25 0 obj
+23 0 obj
<< /Type /FontDescriptor
/FontName /aa6569+NotoSerif-Bold
-/FontFile2 24 0 R
+/FontFile2 22 0 R
/FontBBox [-212 -250 1306 1058]
/Flags 6
/StemV 0
-/ItalicAngle 0.0
+/ItalicAngle 0
/Ascent 1068
/Descent -292
/CapHeight 1462
/XHeight 1098
>>
endobj
-26 0 obj
-<< /Length 1286
+24 0 obj
+<< /Length 1278
/Filter [/FlateDecode]
>>
stream
-xenFὮBtHs&@nu{stԒ + }i /y_!ut~]snVyg쾝+|<.÷}v)[eO/_q9_ɯ}ഏOmt_LLv͵LJ1w9)e6=nPҪtN*eP)DT.ʕrU)BYTVʪQ6[ky ^#k5x5F^ky ^#k5x5F^kZy-^+kxZV^kZy-^+kxZV^:y^'uxu:N^:y^'uxu:N^zy=^/xz^^zy=^/xz^ހ7y o xo7 Aހ7y o xo7 Aވ7Fy#(oĻnuW.I$$*I$ITH$$QI"IDD%$JI$$*I$ITH$$III$$%I$I|›Mx o7M&Iބ7ɛ&y$o›Mx o7M.xyUo ުMMol x;
-JÂ31x;
-JJ특xxx+!ÊwBxbx+ށr;2kΜJYeY7+|x oS7+[ƛețךyޢoV浖 -㭌"RW*4XqC^J[(^1»y]k}YM-x Vz[YEVY_}/7*Y%eӫq+:.7JE/3Y(Y*AW RVJS:(u@cD]a*f)9J)o,#\Z>MU\jPS {HSMj{fkyGm[z*Esa>&ӫj%u; 2^W[®v[2쯲u[P:V̡Յ> MBi2 .Ħԇ!dk`=o qWޕwdJF(L164U)x0E~Z?
+xenFὮBtHs&@nu{stԒ + }ik/y_!}t~]snVyg쾝+|<.÷}v)[eO/_q9_ɯ}ഏOmt_LLv͵LJ1w9)e6=n?[i(JKiU:JSz2QeL*EJ̔Ye,*+eU(NU-уk5x5F^ky ^#k5x5F^ky ^#kxZV^kZy-^+kxZV^kZy-^+uxu:N^:y^'uxu:N^:y^'xz^^zy=^/xz^^zy=^/o xo7 Aހ7y o xo7 Aހ7y oxo7jo$JI$$*I$ITH$$QI"IDD%$JI$$*I$ITD$$III$$u>M&Iބ7ɛ&y$o›Mx o7M&Iބ7ɛ&yUoa[5joSor4ۂs
+JJ특xxx+!ÊwBxbx+ށr;2kΜJYeY7+|x oS7+[ƛețךyޢoV浖 -㭌"RW*4XqC^J[(^1»y]k}YM-x e#e_y+h܊7k:/Z5dUluz5n[eB&Si|y(%q& %+S%*ABijPU6\h,(+L,4G5<cD [˧*t [ jjaijIxʪwgFwضR>sh>:OVRP*#i|e0U,*oU/*[U,Nmlx:&\}M)L2\%CHF7էʻBL)jƻJ1:%H߯:=S۳zrmkO媳7
endstream
endobj
-27 0 obj
-[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 652 600 600 600 600 600 600 600 600 788 787 600 600 600 600 652 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600]
+25 0 obj
+[259 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 652 600 600 600 600 600 600 600 600 788 787 600 600 600 600 652 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600]
endobj
xref
-0 28
+0 26
0000000000 65535 f
0000000015 00000 n
-0000000258 00000 n
-0000000459 00000 n
-0000000530 00000 n
-0000000581 00000 n
-0000000853 00000 n
-0000001057 00000 n
-0000001354 00000 n
-0000001396 00000 n
-0000001444 00000 n
-0000001496 00000 n
-0000001661 00000 n
-0000006185 00000 n
-0000006497 00000 n
-0000009166 00000 n
-0000009465 00000 n
-0000009635 00000 n
-0000009709 00000 n
-0000009834 00000 n
-0000009909 00000 n
-0000014890 00000 n
-0000015104 00000 n
-0000016466 00000 n
-0000017380 00000 n
-0000021413 00000 n
-0000021632 00000 n
-0000022994 00000 n
+0000000248 00000 n
+0000000450 00000 n
+0000000514 00000 n
+0000000565 00000 n
+0000000837 00000 n
+0000005395 00000 n
+0000005704 00000 n
+0000005868 00000 n
+0000008710 00000 n
+0000009007 00000 n
+0000009177 00000 n
+0000009251 00000 n
+0000009376 00000 n
+0000009436 00000 n
+0000009479 00000 n
+0000009528 00000 n
+0000009581 00000 n
+0000014548 00000 n
+0000014760 00000 n
+0000016114 00000 n
+0000017028 00000 n
+0000021050 00000 n
+0000021267 00000 n
+0000022621 00000 n
trailer
-<< /Size 28
+<< /Size 26
/Root 2 0 R
/Info 1 0 R
>>
startxref
-23908
+23535
%%EOF
diff --git a/spec/reference/example-with-nested-block-page-split.pdf b/spec/reference/example-with-nested-block-page-split.pdf
new file mode 100644
index 00000000..f99098bc
--- /dev/null
+++ b/spec/reference/example-with-nested-block-page-split.pdf
@@ -0,0 +1,840 @@
+%PDF-1.4
+%
+1 0 obj
+<< /Title (Untitled)
+/Creator (Asciidoctor PDF 2.0.0.dev, based on Prawn 2.4.0)
+/Producer (Asciidoctor PDF 2.0.0.dev, based on Prawn 2.4.0)
+/ModDate (D:20220228190730-07'00')
+/CreationDate (D:20220228190730-07'00')
+>>
+endobj
+2 0 obj
+<< /Type /Catalog
+/Pages 3 0 R
+/Outlines 13 0 R
+/PageLabels 15 0 R
+/PageMode /UseOutlines
+/OpenAction [7 0 R /FitH 841.89]
+/ViewerPreferences << /DisplayDocTitle true
+>>
+/Names 17 0 R
+>>
+endobj
+3 0 obj
+<< /Type /Pages
+/Count 2
+/Kids [7 0 R 10 0 R]
+>>
+endobj
+4 0 obj
+<< /Length 2
+>>
+stream
+q
+
+endstream
+endobj
+5 0 obj
+<< /Type /Page
+/Parent 3 0 R
+/MediaBox [0 0 595.28 841.89]
+/CropBox [0 0 595.28 841.89]
+/BleedBox [0 0 595.28 841.89]
+/TrimBox [0 0 595.28 841.89]
+/ArtBox [0 0 595.28 841.89]
+/Contents 4 0 R
+/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
+>>
+>>
+endobj
+6 0 obj
+<< /Length 3966
+>>
+stream
+q
+
+BT
+48.24 794.5242 Td
+ET
+
+/DeviceRGB cs
+0.2 0.2 0.2 scn
+/DeviceRGB CS
+0.2 0.2 0.2 SCN
+
+BT
+48.24 794.5242 Td
+/F2.0 9.975 Tf
+<4578616d706c6520312e205469746c65> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+q
+1.0 1.0 1.0 scn
+52.24 786.899 m
+543.04 786.899 l
+545.24914 786.899 547.04 785.10814 547.04 782.899 c
+547.04 52.24 l
+547.04 50.03086 545.24914 48.24 543.04 48.24 c
+52.24 48.24 l
+50.03086 48.24 48.24 50.03086 48.24 52.24 c
+48.24 782.899 l
+48.24 785.10814 50.03086 786.899 52.24 786.899 c
+h
+f
+0.93333 0.93333 0.93333 SCN
+0.75 w
+52.24 786.899 m
+543.04 786.899 l
+545.24914 786.899 547.04 785.10814 547.04 782.899 c
+547.04 52.24 l
+547.04 50.03086 545.24914 48.24 543.04 48.24 c
+52.24 48.24 l
+50.03086 48.24 48.24 50.03086 48.24 52.24 c
+48.24 782.899 l
+48.24 785.10814 50.03086 786.899 52.24 786.899 c
+h
+S
+Q
+q
+1.0 1.0 1.0 SCN
+0.8999999999999999 w
+[3.6 3.6] 0.0 d
+52.99 48.24 m
+542.29 48.24 l
+S
+Q
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 762.935 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 735.155 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 707.375 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 679.595 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 651.815 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 624.035 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 596.255 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 568.475 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 540.695 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 512.915 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 485.135 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 457.355 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 429.575 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 401.795 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 374.015 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 346.235 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 318.455 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 290.675 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 262.895 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 235.115 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 207.335 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 179.555 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 151.775 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 123.995 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 96.215 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+Q
+
+endstream
+endobj
+7 0 obj
+<< /Type /Page
+/Parent 3 0 R
+/MediaBox [0 0 595.28 841.89]
+/CropBox [0 0 595.28 841.89]
+/BleedBox [0 0 595.28 841.89]
+/TrimBox [0 0 595.28 841.89]
+/ArtBox [0 0 595.28 841.89]
+/Contents 6 0 R
+/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
+/Font << /F2.0 8 0 R
+/F1.0 11 0 R
+>>
+>>
+>>
+endobj
+8 0 obj
+<< /Type /Font
+/BaseFont /3c7f81+NotoSerif-Italic
+/Subtype /TrueType
+/FontDescriptor 20 0 R
+/FirstChar 32
+/LastChar 255
+/Widths 22 0 R
+/ToUnicode 21 0 R
+>>
+endobj
+9 0 obj
+<< /Length 2069
+>>
+stream
+q
+q
+/DeviceRGB cs
+1.0 1.0 1.0 scn
+52.24 805.89 m
+543.04 805.89 l
+545.24914 805.89 547.04 804.09914 547.04 801.89 c
+547.04 607.43 l
+547.04 605.22086 545.24914 603.43 543.04 603.43 c
+52.24 603.43 l
+50.03086 603.43 48.24 605.22086 48.24 607.43 c
+48.24 801.89 l
+48.24 804.09914 50.03086 805.89 52.24 805.89 c
+h
+f
+/DeviceRGB CS
+0.93333 0.93333 0.93333 SCN
+0.75 w
+52.24 805.89 m
+543.04 805.89 l
+545.24914 805.89 547.04 804.09914 547.04 801.89 c
+547.04 607.43 l
+547.04 605.22086 545.24914 603.43 543.04 603.43 c
+52.24 603.43 l
+50.03086 603.43 48.24 605.22086 48.24 607.43 c
+48.24 801.89 l
+48.24 804.09914 50.03086 805.89 52.24 805.89 c
+h
+S
+Q
+q
+/DeviceRGB CS
+1.0 1.0 1.0 SCN
+0.8999999999999999 w
+[3.6 3.6] 0.0 d
+52.99 805.89 m
+542.29 805.89 l
+S
+Q
+q
+0.5 w
+/DeviceRGB CS
+0.93333 0.93333 0.93333 SCN
+114.25929 805.89 m
+114.25929 754.33 l
+S
+Q
+/DeviceRGB cs
+0.2 0.2 0.2 scn
+/DeviceRGB CS
+0.2 0.2 0.2 SCN
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+72.24 776.036 Td
+/F3.0 10.5 Tf
+[<4e4f> 20.01953 <5445>] TJ
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+126.25929 789.926 Td
+/F1.0 10.5 Tf
+<5468697320626c6f636b20646f6573206e6f7420666974206f6e20612073696e676c6520706167652e> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+126.25929 762.146 Td
+/F1.0 10.5 Tf
+<5468657265666f72652c2069742069732073706c6974206163726f7373206d756c7469706c652070616765732e> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 730.366 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 702.586 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 674.806 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 647.026 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+0.2 0.2 0.2 scn
+0.2 0.2 0.2 SCN
+
+BT
+60.24 619.246 Td
+/F1.0 10.5 Tf
+<636f6e74656e74> Tj
+ET
+
+0.0 0.0 0.0 SCN
+0.0 0.0 0.0 scn
+Q
+
+endstream
+endobj
+10 0 obj
+<< /Type /Page
+/Parent 3 0 R
+/MediaBox [0 0 595.28 841.89]
+/CropBox [0 0 595.28 841.89]
+/BleedBox [0 0 595.28 841.89]
+/TrimBox [0 0 595.28 841.89]
+/ArtBox [0 0 595.28 841.89]
+/Contents 9 0 R
+/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
+/Font << /F3.0 12 0 R
+/F1.0 11 0 R
+>>
+>>
+>>
+endobj
+11 0 obj
+<< /Type /Font
+/BaseFont /c8e9fb+NotoSerif
+/Subtype /TrueType
+/FontDescriptor 24 0 R
+/FirstChar 32
+/LastChar 255
+/Widths 26 0 R
+/ToUnicode 25 0 R
+>>
+endobj
+12 0 obj
+<< /Type /Font
+/BaseFont /aa6569+NotoSerif-Bold
+/Subtype /TrueType
+/FontDescriptor 28 0 R
+/FirstChar 32
+/LastChar 255
+/Widths 30 0 R
+/ToUnicode 29 0 R
+>>
+endobj
+13 0 obj
+<< /Type /Outlines
+/Count 1
+/First 14 0 R
+/Last 14 0 R
+>>
+endobj
+14 0 obj
+<< /Title <feff0055006e007400690074006c00650064>
+/Parent 13 0 R
+/Count 0
+/Dest [7 0 R /XYZ 0 841.89 null]
+>>
+endobj
+15 0 obj
+<< /Nums [0 << /P (1)
+>> 1 << /P (2)
+>>]
+>>
+endobj
+16 0 obj
+[7 0 R /XYZ 0 841.89 null]
+endobj
+17 0 obj
+<< /Type /Names
+/Dests 18 0 R
+>>
+endobj
+18 0 obj
+<< /Names [(__anchor-top) 16 0 R]
+>>
+endobj
+19 0 obj
+<< /Length1 7640
+/Length 4622
+/Filter [/FlateDecode]
+>>
+stream
+x8 pS՚߹7-M۴*?P~*MۤmZ_V6ilؤ@UUEOuUq-ZYq٧;.vG]Pwt=Odsr[ZqsI
+;:Qžd C*<UUV ރӂwZ5TSg%^}x?->Ӊ+8ڞ ώpcij~Ea,sRR$]Ÿvt.G}V[KsPotK լ^T^VrEqrXgg/\j*Q PjT.v˪b 9L4_Fn.FKQrIsR<+IjJU{N.‡,R//qx UYшԚPUnY7 Qjd $“d&au$ṋ,Y=^ee69]p4rQr4Bt)=KҼ׳%ԍXlY",;#3ɥ*0mݳ~ڮ$H/׀H.ΧxH50 5I)yKɴX؊NjM%^?/7n?٠$&gwꒅ& uq]1wF؀r`MF)3#"Ot8`6ʂqޜ8gb3mvWLVټk|#OzZ!oR,+7rYQټ*,5WW
+S9Mr3z 0;VVyh-%dGur 5<nlQ'lf².ɆF_NlZgj--u*&h+P&ۈsyww ues/6WrzB
+ܣ,4R[WkH̩ט\I3d]|HMH 8"z,8PK]$f1 y,U2cMPؒo5&RTq:VԖXO hX-ؚ.'J~*;],7V^eJzase#gVL$nqfϢ-װm3lIm3.)#y]f>l?KMĸ~Ml/ٶI6oLj4>A|eAii(+ŇYäD4}z|:.;y*0*#22YFDO&8W "i)!I43 J?wc^!+_"iVG4IM*R1z]at- KJye_
+"Y $dι8l
+4Ji6 .l*|j{+HVpyW"ߑ]P;E &'ituYyeA[IKsj,"9G&p+3x@qfp<q>C8q9C<%pX\&S{II۔6/:m2_.'kwk{;tv^&{03`\hD"~HI" %Q(p0&D
+p
+Zq#`WB5FOnMBNp^2z|'r4gf "oԂ`4"NFmKs9XUKV/AfA[n$?7c%RJ'ragTDk&ۘih50>_w~gPlGT
+,,Z[YcМg۴ϦHʵ),1l$ZcMeښ5R:
+Vh0kZ2JtwlmW^*v:m• ޵&aU0V#@T3fKHQ7MdZ#_iֹdtiǏ/ c,~qS
+gHj<B!f)ݡ"\){lPgKw:Uw7Lˏy ۏ`HgٽXsW vXEFj=+T觲fT],
+ Ϫ..|d.kWȃG0 | Iu3d j:|鯮zx.W W|.sy3V}e:|H(ee>~9׿=݇?9CwFiC}K\5pP5ZXX\]ś'w#"ϲH@ewh;no?rNdi{}FsG/>#sq,r%f뎲>>ac17՗թAQʪ# Vy~eKC2<w3H'ު2->{|㥴,rg /%*Pf\X^Nt]wJY/y81|8WvM=S>#pJܠj¾..``\\Mɬ}Y}DbmM꣟WcMهM _\{JNu3LN_g{Kw+l/!w&|KAQr6ȮVjd[7}*۷Wۻ؏leM
+~ڵ!OGu/
+
+ڮZ^Y_z>l"u;~sC_b/8[[R? f "X@XJ
+2a@ՐQ`-&X;q
+
+/vHp,#31,$md
+ǰ]8 ^H+-jVAGՐ'Q`-X;:YFS`*p*lS(ؤzNӠV/S/StҞh GC(X6BC>j aXFwh}A[3
+a C^INh$
+5k*j׭)a2\lN B=4:F<<hECAX<QQ _
+{Q.G &ݻˇЈi5p4=s<&z?
+F=40_0>ǂ^(}nL;¾`RxsR^-̚rzcc@$:l(JzNmBPoKV[OKnoY6v[l=vĚh}{/m[J/Rߞ/Q X>ߏJCL"a@`00@=1f",_Ћ(LJB|A֮(CKu!20G#pyhtѴ|Ɠ(}MV)4#7a!f >G0ݳZp]8zQxSsz
+Ϭ*k|RQh^O"\gA&z`d/<^˙Dy\Ngt qƔ5?!Ά363q9 _=7"22fD[V夥SmaY
+endstream
+endobj
+20 0 obj
+<< /Type /FontDescriptor
+/FontName /3c7f81+NotoSerif-Italic
+/FontFile2 19 0 R
+/FontBBox [-254 -250 1238 1047]
+/Flags 70
+/StemV 0
+/ItalicAngle -12
+/Ascent 1068
+/Descent -292
+/CapHeight 1462
+/XHeight 1098
+>>
+endobj
+21 0 obj
+<< /Length 1278
+/Filter [/FlateDecode]
+>>
+stream
+xenFὮBtHs&@nu{stԒ + }ik/y_!}t~]snVyg쾝+|<.÷}v)[eO/_q9_ɯ}ഏOmt_LLv͵LJ1w9)e6=n?[i(JKiU:JSz2QeL*EJ̔Ye,*+eU(NU-уk5x5F^ky ^#k5x5F^ky ^#kxZV^kZy-^+kxZV^kZy-^+uxu:N^:y^'uxu:N^:y^'xz^^zy=^/xz^^zy=^/o xo7 Aހ7y o xo7 Aހ7y oxo7jo$JI$$*I$ITH$$QI"IDD%$JI$$*I$ITD$$III$$u>M&Iބ7ɛ&y$o›Mx o7M&Iބ7ɛ&yUoa[5joSor4ۂs
+JJ특xxx+!ÊwBxbx+ށr;2kΜJYeY7+|x oS7+[ƛețךyޢoV浖 -㭌"RW*4XqC^J[(^1»y]k}YM-x e#e_y+h܊7k:/Z5dUluz5n[eB&Si|y(%q& %+S%*ABijPU6\h,(+L,4G5<cD [˧*t [ jjaijIxʪwgFwضR>sh>:OVRP*#i|e0U,*oU/*[U,Nmlx:&\}M)L2\%CHF7էʻBL)jƻJ1:%H߯:=S۳zrmkO媳7
+endstream
+endobj
+22 0 obj
+[259 600 600 600 600 600 600 600 600 600 600 600 600 600 250 600 600 559 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 623 600 600 600 600 600 600 600 600 600 600 600 600 600 600 612 600 600 600 600 600 600 600 600 600 600 600 600 579 600 600 600 493 600 600 600 304 600 600 304 895 600 600 577 600 600 600 368 600 600 600 545 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600]
+endobj
+23 0 obj
+<< /Length1 9904
+/Length 6006
+/Filter [/FlateDecode]
+>>
+stream
+xZ p[Uz>+mɱl+-_OY߶lK~Ďߒ8-Vb[$,$@7 a20;-M- L[ CagʏU9?M0B("LU3F u,z vR9#=sP k -ڹb[J(~ :2g)Їy2u P{ l
+u\چ)ښF"+8mVN2T邕6.jRw $'{Wn(Ѵ[Izk[[SЋeU,;ojO6QOnӿ3$eEJ_t/x$H9"d7-k5+Ե FN赱=KTq4~1sSdyHOk2\qjF
+6: tBP脵}|EuyEY1vwL!]֔I]Xg0C[sr^ xCث~)Z@X<Rlf!D~+ hJ܎'qs.R7Q%!&M6+\Hz5QF/kx_V]Ŷ5wV,pk*gyF'Z\`%8}.fz'2]1䰳Ar:@qEkNM X; E%n (ńDx*%i;ŵ [tjt.]1T0KCDx[0CEw5Aw
+>%eIF:tgb^\l5}f~-Q[# DDҐ;i9C3fmb!B_k˜jnsk㝆
+:|Ⱥ5vkƝ3rw@%*e X&k^ J[9*FTa4dʸY<%<` {_sHl@` mжy,:ۉ=&ydƆSkJ
+gaBvdR >Rb-d8V!K]gr G7ÛXī{R;p
+(1V
+Ó',j&3^AC@.ˢ$ cfiyjG pˠv('PbK0"u.{^x?XT-ߣx,
+}v́
+ {e-Ynv!E%lxqI.(WgWg3®Ve=⒂iW\T\Qt= ex߿>>o$l90|DwEcw~s{];:Ū1w^ ,XB:]p8 G$h,~dyy*SEL,"9
+^Od0PWODaU( —@Z({s-;T|/]eT? 'ƉЏ4mDs+)m_Bw7<X u~m4mz+B4z }ݖyAnG{q3ZδE[1ttM ך9~o] /Ú&K?-
+Z?
+i* 4|[q4Z$IūAY[WhC[%-tMOLH%WE@-dQ1r<KcϘ[@<
+Y (-bw2e[Ӳ_>S*5wCZ2jfQGdYP<U"&؇ X_(efٚMmً_>}5>(|ҙ6\[cjkꪫ >w`̤o*0P#Pqj~L'[T,U^YAs[3[[V&B5m 16B#sF%4&!+K0jLZ>=--#u܏geq
+2uBcC[i6SaQ󺡵$K9ݚ ߘ
+zwBcl-tCNg2WMtN&t6*,O CDHgL
+JG|CiJՠ!(
+\dĜwchk^^)λgr͝; AC6K8Rn6 w.<8D1 '.[E/r<
+^" Y"YweVq W4
+;V<,Wp*vtJt:<!6ql CkIK):صdQd{mM1jrܬr`0T67Qu@,4i>\){R^e鮰{z[+ҪVqqu}ㅚGڋoUd{g\AvI~F:+-5Muc'92ȉN3uk9l)MJҖZ]Gծ5)4e.;#j`_y*+XmmoIMT{/ϽDݞ-#l!y;'&Q:,7jЖךyͣ07]EaC}^=W2UCiϼ9H6p2/l>| 3\G7}n%q9 0kSSPiqD$0 M.6!k6ƒeAJ2/ZM
+w|ze0s+=w3G!+DՏ&͵X]PS*XLb@6>n.-*no Jp~3qȑΏx}NҸ7shMr\uPkfFA<x(ONhe.guA+34O5SM/TR[}n$>y6yd~M2392 ч>ڴ+{|QMAn<XI\e,gr@<AdmCuk;ieJg&{г--φBτϐ4x 9c^v9Z6_w c^悀oݾZ$BsRųY;7 ֘Yv ?uC}>]f%Aj) WezNfN,9)uN@KU孇,coPHwo#`_WeLz%m$۝$movZs+O nq5%}5FmN{'ŇzGj [G"]PiG2M\ bmk.h9Ph6vN[o)K.3ߓuXpʒ.Dp׆뫳ٞiu`HP+pj33Q~X$<d
+˛io|V^ֵ{1F$
+U#pb1
+dUY=ZU=.j-Sme8u?en6> \fQY,́ -CmH
+9F'·_k/Kt22iFp?ro'#5߆1#sU*%EMV9b-CjvALDO$: Jt2r%:q'%:p/Ht]NG5 wt+!|2WV=܂[1 (i
+󣾰/t5"~ώ<C@d%]=s9P_(,FsLqvZOwaGBo:fwAny/EUI+7e|$l2nF8X4"&Pv5/JݩB8R Fn|d R\^B|dǏ CARLy P RioGBe %c+;;*!8?1:18gFAkx p|ǠV>?}7CpxbpOW~6I8gܲgfa2ߒx" HV!~,AX#_j<ᙐ? c 4g@](h< U!3LU@@k
+Ȗ˒OQv=c ݹԷ砝 ??S[}H2P=}z }0A8 N6-i^
+endstream
+endobj
+24 0 obj
+<< /Type /FontDescriptor
+/FontName /c8e9fb+NotoSerif
+/FontFile2 23 0 R
+/FontBBox [-212 -250 1246 1047]
+/Flags 6
+/StemV 0
+/ItalicAngle 0
+/Ascent 1068
+/Descent -292
+/CapHeight 1462
+/XHeight 1098
+>>
+endobj
+25 0 obj
+<< /Length 1278
+/Filter [/FlateDecode]
+>>
+stream
+xenFὮBtHs&@nu{stԒ + }ik/y_!}t~]snVyg쾝+|<.÷}v)[eO/_q9_ɯ}ഏOmt_LLv͵LJ1w9)e6=n?[i(JKiU:JSz2QeL*EJ̔Ye,*+eU(NU-уk5x5F^ky ^#k5x5F^ky ^#kxZV^kZy-^+kxZV^kZy-^+uxu:N^:y^'uxu:N^:y^'xz^^zy=^/xz^^zy=^/o xo7 Aހ7y o xo7 Aހ7y oxo7jo$JI$$*I$ITH$$QI"IDD%$JI$$*I$ITD$$III$$u>M&Iބ7ɛ&y$o›Mx o7M&Iބ7ɛ&yUoa[5joSor4ۂs
+JJ특xxx+!ÊwBxbx+ށr;2kΜJYeY7+|x oS7+[ƛețךyޢoV浖 -㭌"RW*4XqC^J[(^1»y]k}YM-x e#e_y+h܊7k:/Z5dUluz5n[eB&Si|y(%q& %+S%*ABijPU6\h,(+L,4G5<cD [˧*t [ jjaijIxʪwgFwضR>sh>:OVRP*#i|e0U,*oU/*[U,Nmlx:&\}M)L2\%CHF7էʻBL)jƻJ1:%H߯:=S۳zrmkO媳7
+endstream
+endobj
+26 0 obj
+[259 500 500 500 500 500 500 500 500 500 500 500 250 500 250 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 612 500 500 500 500 500 500 500 500 500 500 500 500 562 613 492 613 535 369 538 634 319 500 584 310 944 645 577 613 500 471 451 352 634 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500]
+endobj
+27 0 obj
+<< /Length1 7004
+/Length 3932
+/Filter [/FlateDecode]
+>>
+stream
+xY}pSWv?'YX2`pbblK6d$ֳ$CH7l#.G&mg3@gt:i閲Nufɤ[=؎香|=w>
+rGhjQ]_r\vZü ,xJ,&?\Jį!z3`d7iGڗdƇdjS3 lqΫ|!W[l@Z|G,4GZd"NĀt`Nf36BF?*[}W|Lr}QRfEɒOj8=iUtd_MV
+٤~^ahSR;ӂV p03g8Auz$!;|߉P9:ujWM.j#ak5rLG Kr\-ۖrn%*|4+ag3SGZqyCXhܛM+vv=WS9B=fE^٣x٦lnyt8CG<jAg̝dqr#{*np{z
+ы&hr[3jŀ̈́(mC*`QPSb©mEozƢ
+לjSmb0t-D >5 XYVGVd+%Gk,Y&0xaT;j*'W۵Jܽ(iL30"lĘ!}k p@Zvz+xW+^asj_۔L3#L=/ڂ2+@P0O~6q.83,bg,fZĤ,fJ!1>4-o(=eh#*cSDyҦm oaxK3<w)#uM:|gZH)p {@Ye \`=#vell!3Ĕ! n̼h0/ܝ'F>FtZ {B(+7)1<s8U.N?gs@%6FQ2}t{h>Ş- &آ-l$+Gp,mscy0p8}<woӹ+sy:>n'5އM l"/!;LՋWի^՟<}wZ41]]xƧ*V[Go#v[o7cozS̅I|m֤$&g'3mܚ$tMZ&.L\Mܚ'{.|NXwn9Tq2GT1k[AqLjmDFHrM ` >ֻu
+wYCݦbIܗwBq(4\Yp'!\Κ sjqv航?Q!ox_;6Q)q$Lʠ͛L&S5"H&!H%! gRcRcc/&ɋL)ɔPT_HRYl/rݩ+χ(^Et F
+EWE+hsu P[NKBlH$wZsG b)'F9}m<eŰKrՖja[vIeu`nZ?S%D=?]6` 1w\-ћd{ҟ\r.}2yI;b`d D.ykb\]68opC&;[t(Rw~xRg49<]h)c3Z}yei$J
+iF8:(FyF6 >ѹƛV{Wa_&ƧJ^GzC ap#!R.r/
+i4M4h4~4ZE%xZ iht|O  ۤ΃
+Yk^'B؝XT" ;wX,8谕&J%$N(ko,N%iEcSqԏh
+ibD2ҝ~ƦIꧩ?DhldyX_shW`Oi(Ma)*PJ{O<i ɬñH}PIգr~8s|1WCESI'McрBi_\ffjҵT"<4ҪV'vVѶVgvu]p@k}EziOw*a 'A % Ga*<9V:ˮ$W#a:A\E\IDI_4도S烱J"u*6c ˚RN㩤51)H@P;`':QC|㹥Њ(KVI)8*E6
+NaڐE8FF? #DZ蒏Y:ȱ$l&kYy~l)cHKŸseEBΏvS<rRa]s`+B|Y1#n9֑U?Dl caywհ,wpl
+L+Kuq(ElcHxjB\׉=c5jYkYDc!*ŗ}
+'i [yNa\ЅQqd|+{CC-9u ^{Ptof'13 <_GQ]NJ1G5Ira~&1~&#<Ij h𵰓X n]:]1i1eOk+TuyHbĬqFqdk e ,OGfuom+
+endstream
+endobj
+28 0 obj
+<< /Type /FontDescriptor
+/FontName /aa6569+NotoSerif-Bold
+/FontFile2 27 0 R
+/FontBBox [-212 -250 1306 1058]
+/Flags 6
+/StemV 0
+/ItalicAngle 0
+/Ascent 1068
+/Descent -292
+/CapHeight 1462
+/XHeight 1098
+>>
+endobj
+29 0 obj
+<< /Length 1278
+/Filter [/FlateDecode]
+>>
+stream
+xenFὮBtHs&@nu{stԒ + }ik/y_!}t~]snVyg쾝+|<.÷}v)[eO/_q9_ɯ}ഏOmt_LLv͵LJ1w9)e6=n?[i(JKiU:JSz2QeL*EJ̔Ye,*+eU(NU-уk5x5F^ky ^#k5x5F^ky ^#kxZV^kZy-^+kxZV^kZy-^+uxu:N^:y^'uxu:N^:y^'xz^^zy=^/xz^^zy=^/o xo7 Aހ7y o xo7 Aހ7y oxo7jo$JI$$*I$ITH$$QI"IDD%$JI$$*I$ITD$$III$$u>M&Iބ7ɛ&y$o›Mx o7M&Iބ7ɛ&yUoa[5joSor4ۂs
+JJ특xxx+!ÊwBxbx+ށr;2kΜJYeY7+|x oS7+[ƛețךyޢoV浖 -㭌"RW*4XqC^J[(^1»y]k}YM-x e#e_y+h܊7k:/Z5dUluz5n[eB&Si|y(%q& %+S%*ABijPU6\h,(+L,4G5<cD [˧*t [ jjaijIxʪwgFwضR>sh>:OVRP*#i|e0U,*oU/*[U,Nmlx:&\}M)L2\%CHF7էʻBL)jƻJ1:%H߯:=S۳zrmkO媳7
+endstream
+endobj
+30 0 obj
+[259 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 652 600 600 600 600 600 600 600 600 788 787 600 600 600 600 652 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600]
+endobj
+xref
+0 31
+0000000000 65535 f
+0000000015 00000 n
+0000000248 00000 n
+0000000450 00000 n
+0000000514 00000 n
+0000000565 00000 n
+0000000837 00000 n
+0000004855 00000 n
+0000005164 00000 n
+0000005335 00000 n
+0000007456 00000 n
+0000007767 00000 n
+0000007932 00000 n
+0000008102 00000 n
+0000008176 00000 n
+0000008301 00000 n
+0000008361 00000 n
+0000008404 00000 n
+0000008453 00000 n
+0000008506 00000 n
+0000013218 00000 n
+0000013440 00000 n
+0000014794 00000 n
+0000015708 00000 n
+0000021804 00000 n
+0000022016 00000 n
+0000023370 00000 n
+0000024284 00000 n
+0000028306 00000 n
+0000028523 00000 n
+0000029877 00000 n
+trailer
+<< /Size 31
+/Root 2 0 R
+/Info 1 0 R
+>>
+startxref
+30791
+%%EOF
diff --git a/spec/reference/image-svg-with-caption-scale-to-fit-page.pdf b/spec/reference/image-svg-with-caption-scale-to-fit-page.pdf
index 43e6b571..10e975e6 100644
--- a/spec/reference/image-svg-with-caption-scale-to-fit-page.pdf
+++ b/spec/reference/image-svg-with-caption-scale-to-fit-page.pdf
Binary files differ
diff --git a/spec/sidebar_spec.rb b/spec/sidebar_spec.rb
index c865161c..9d9985fa 100644
--- a/spec/sidebar_spec.rb
+++ b/spec/sidebar_spec.rb
@@ -99,6 +99,7 @@ describe 'Asciidoctor::PDF::Converter - Sidebar' do
#{(['filler'] * 15).join %(\n\n)}
.Sidebar
+ [%unbreakable]
****
#{(['content'] * 15).join %(\n\n)}
****
diff --git a/spec/source_spec.rb b/spec/source_spec.rb
index 3f4d8f06..307bed53 100644
--- a/spec/source_spec.rb
+++ b/spec/source_spec.rb
@@ -856,6 +856,8 @@ describe 'Asciidoctor::PDF::Converter - Source' do
main_pdf = to_pdf <<~'EOS', (opts.merge analyze: true)
:source-highlighter: rouge
+ filler
+
[%unbreakable]
--
[source,ruby]
@@ -865,7 +867,7 @@ describe 'Asciidoctor::PDF::Converter - Source' do
--
EOS
- main_pdf_text = main_pdf.text
+ main_pdf_text = main_pdf.text.reject {|it| it[:string] == 'filler' }
(expect main_pdf_text[0][:string]).to eql 'puts'
(expect main_pdf_text[0][:font_color]).not_to eql '333333'
scratch_pdf_output = scratch_pdf.render
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 8cb90cc8..656cfc49 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -129,7 +129,7 @@ class EnhancedPDFTextInspector < PDF::Inspector
end
def page= page
- @pages << { size: (page.attributes[:MediaBox].slice 2, 2), text: [] }
+ @pages << { size: (page.attributes[:MediaBox].slice 2, 2), text: [], raw_content: page.raw_content }
@page_number = page.number
@state = ::PDF::Reader::PageState.new page
page.fonts.each do |label, stream|
@@ -139,6 +139,11 @@ class EnhancedPDFTextInspector < PDF::Inspector
end
end
+ def extract_graphic_states content
+ content = (content.delete_prefix %(q\n)).delete_suffix %(\nQ)
+ (content.scan %r/^q\n(.*?)\nQ$/m).map {|it| it[0].split ?\n }
+ end
+
# Tf
def set_text_font_and_size *params
@state.set_text_font_and_size(*params)
@@ -289,7 +294,15 @@ class LineInspector < PDF::Inspector
end
end
+module TareFirstPageContentStreamNoop
+ def tare_first_page_content_stream
+ yield
+ end
+end
+
RSpec.configure do |config|
+ config.filter_run_excluding breakable: true unless ENV['CI']
+
config.before :suite do
FileUtils.rm_r output_dir, force: true, secure: true
FileUtils.mkdir output_dir
@@ -653,6 +666,19 @@ RSpec::Matchers.define :have_size do |expected|
failure_message {|actual| %(expected #{actual} to have size #{expected}, but was #{actual.size}) }
end
+RSpec::Matchers.define :have_background do |expected|
+ match do |actual|
+ color = ((expected[:color].scan %r/../).map {|it| ((it.to_i 16) / 255.0).round 5 }.join ' ') + ' scn'
+ # FIXME: shave off lines before this line
+ (expect actual).to include color
+ x1, y1 = expected[:top_left]
+ x2, y2 = expected[:bottom_right]
+ (expect actual).to include %(#{x2} #{y1} #{x2} #{y1} #{x2} #{y1} c)
+ (expect actual).to include %(#{x1} #{y2} #{x1} #{y2} #{x1} #{y2} c)
+ end
+ failure_message {|actual| %(expected #{actual} to have background #{expected}, but was \n#{actual.join ?\n}) }
+end
+
RSpec::Matchers.define :annotate do |text|
match do |subject|
left, bottom, right, top = subject[:Rect]
diff --git a/spec/toc_spec.rb b/spec/toc_spec.rb
index f83ecc1a..ec15c3cc 100644
--- a/spec/toc_spec.rb
+++ b/spec/toc_spec.rb
@@ -510,13 +510,14 @@ describe 'Asciidoctor::PDF::Converter - TOC' do
footer_verso_right_content: 'Page {page-number}',
}
- sections = (1..37).map {|num| %(\n\n== Section #{num}) }.join
+ sections = (1..37).map {|num| %(== Section #{num}) }.join %(\n\n)
pdf = to_pdf <<~EOS, pdf_theme: pdf_theme, enable_footer: true, analyze: true
= Document Title
:doctype: book
:toc: macro
== First Chapter
+
#{sections}
[%noheader%nofooter]