<commit_before># frozen_string_literal: true

require_relative 'spec_helper'

describe 'Asciidoctor::PDF::Converter - Listing' do
  it 'should render empty block if listing block is empty' do
    pdf_theme = {
      code_line_height: 1,
      code_padding: 0,
      code_border_width: 1,
      code_border_radius: 0,
    }
    pdf = to_pdf <<~'EOS', pdf_theme: pdf_theme, analyze: :line
    ----
    ----
    EOS

    lines = pdf.lines
    (expect lines).to have_size 4
    (expect lines[1][:from][:y] - lines[1][:to][:y]).to be <= 1
  end

  it 'should wrap text consistently regardless of whether the characters contain diacritics' do
    pdf = to_pdf <<~'EOS', analyze: true
    :pdf-page-size: A5

    ....
    aàbècìdòeùf gáhéiíjókúlým nâoêpîqôrûs tñuõvãw xäyëzïaöbücÿd
    aabbccddeef gghhiijjkkllm nnooppqqrrs ttuuvvw xxyyzzaabbccd
    ....
    EOS

    text = pdf.text
    (expect text).to have_size 4
    (expect text[1][:string]).to start_with 'x'
    (expect text[3][:string]).to start_with 'x'
  end

  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}
    ----
    EOS

    listing_page_numbers = (pdf.find_text 'listing').map {|it| it[:page_number] }.uniq
    (expect listing_page_numbers).to eql [2]
  end

  it 'should keep anchor together with block when block is moved to next page' do
    pdf = to_pdf <<~EOS
    #{(['paragraph'] * 20).join (?\n * 2)}

    [#listing-1%unbreakable]
    ----
    #{(['listing'] * 20).join ?\n}
    ----
    EOS

    (expect (pdf.page 1).text).not_to include 'listing'
    (expect (pdf.page 2).text).to include 'listing'
    (expect (dest = get_dest pdf, 'listing-1')).not_to be_nil
    (expect dest[:page_number]).to be 2
    (expect dest[:y]).to eql 805.89
  end

  it 'should place anchor directly at top of block' do
    input = <<~'EOS'
    paragraph

    [#listing-1]
    ----
    listing
    ----
    EOS

    lines = (to_pdf input, analyze: :line).lines
    pdf = to_pdf input
    (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]
  end

  it 'should offset anchor from top of block by value of block_anchor_top' do
    input = <<~'EOS'
    paragraph

    [#listing-1]
    ----
    listing
    ----
    EOS

    pdf_theme = { block_anchor_top: -12 }

    lines = (to_pdf input, pdf_theme: pdf_theme, analyze: :line).lines
    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] + -pdf_theme[:block_anchor_top])
  end

  it 'should place anchor at top of block if advanced to next page' do
    input = <<~EOS
    paragraph

    [#listing-1%unbreakable]
    ----
    #{(['filler'] * 25).join %(\n\n)}
    ----
    EOS

    lines = (to_pdf input, analyze: :line).lines
    pdf = to_pdf input
    (expect (dest = get_dest pdf, 'listing-1')).not_to be_nil
    (expect dest[:page_number]).to be 2
    (expect dest[:y]).to eql lines[0][:from][:y]
  end

  it 'should split block if it cannot fit on a whole page' do
    pdf = to_pdf <<~EOS, analyze: true
    #{(['paragraph'] * 20).join (?\n * 2)}

    ----
    #{(['listing'] * 60).join ?\n}
    ----
    EOS

    (expect pdf.pages).to have_size 2
    listing_texts = pdf.find_text 'listing'
    (expect listing_texts[0][:page_number]).to be 1
    (expect listing_texts[-1][:page_number]).to be 2
  end

  it 'should use dashed border to indicate where block is split across a page boundary', visual: true do
    to_file = to_pdf_file <<~EOS, 'listing-page-split.pdf'
    ----
    #{(['listing'] * 60).join ?\n}
    ----

    ----
    #{(['more listing'] * 2).join ?\n}
    ----
    EOS

    (expect to_file).to visually_match 'listing-page-split.pdf'
  end

  it 'should not collapse bottom padding if block ends near bottom of page' do
    pdf_theme = {
      code_padding: 11,
      code_background_color: 'EEEEEE',
      code_border_width: 0,
      code_border_radius: 0,
    }
    pdf = with_content_spacer 10, 695 do |spacer_path|
      to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
      image::#{spacer_path}[]

      ----
      $ gem install asciidoctor-pdf
      $ asciidoctor-pdf doc.adoc
      ----
      EOS
    end

    pages = pdf.pages
    (expect pages).to have_size 1
    gs = pdf.extract_graphic_states pages[0][:raw_content]
    (expect gs[1]).to have_background color: 'EEEEEE', top_left: [48.24, 98.89], bottom_right: [48.24, 48.24]
    last_text_y = pdf.text[-1][:y]
    (expect last_text_y - pdf_theme[:code_padding]).to be > 48.24

    pdf = with_content_spacer 10, 696 do |spacer_path|
      to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
      image::#{spacer_path}[]

      ----
      $ gem install asciidoctor-pdf
      $ asciidoctor-pdf doc.adoc
      ----
      EOS
    end

    pages = pdf.pages
    (expect pages).to have_size 2
    gs = pdf.extract_graphic_states pages[0][:raw_content]
    (expect gs[1]).to have_background color: 'EEEEEE', top_left: [48.24, 97.89], bottom_right: [48.24, 48.24]
    (expect pdf.text[0][:page_number]).to eql 1
    (expect pdf.text[1][:page_number]).to eql 2
    (expect pdf.text[0][:y] - pdf_theme[:code_padding]).to be > 48.24
  end

  it 'should resize font to prevent wrapping if autofit option is set' do
    pdf = to_pdf <<~'EOS', analyze: true
    [%autofit]
    ----
    @themesdir = ::File.expand_path theme.__dir__ || (doc.attr 'pdf-themesdir') || ::Dir.pwd
    ----
    EOS

    (expect pdf.text).to have_size 1
    (expect pdf.text[0][:font_size]).to be < build_pdf_theme.code_font_size
  end

  it 'should not resize font if not necessary' do
    pdf = to_pdf <<~'EOS', analyze: true
    [%autofit]
    ----
    puts 'Hello, World!'
    ----
    EOS

    (expect pdf.text).to have_size 1
    (expect pdf.text[0][:font_size]).to eql 11
  end

  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]
    ----
    play_symbol = (node.document.attr? 'icons', 'font') ? %(<font name="fas">#{(icon_font_data 'fas').unicode 'play'}</font>) : RightPointer<commit_msg>verify that long line in verbatim block wraps and that extent is computed correctly<commit_after># frozen_string_literal: true

require_relative 'spec_helper'

describe 'Asciidoctor::PDF::Converter - Listing' do
  it 'should render empty block if listing block is empty' do
    pdf_theme = {
      code_line_height: 1,
      code_padding: 0,
      code_border_width: 1,
      code_border_radius: 0,
    }
    pdf = to_pdf <<~'EOS', pdf_theme: pdf_theme, analyze: :line
    ----
    ----
    EOS

    lines = pdf.lines
    (expect lines).to have_size 4
    (expect lines[1][:from][:y] - lines[1][:to][:y]).to be <= 1
  end

  it 'should wrap text consistently regardless of whether the characters contain diacritics' do
    pdf = to_pdf <<~'EOS', analyze: true
    :pdf-page-size: A5

    ....
    aàbècìdòeùf gáhéiíjókúlým nâoêpîqôrûs tñuõvãw xäyëzïaöbücÿd
    aabbccddeef gghhiijjkkllm nnooppqqrrs ttuuvvw xxyyzzaabbccd
    ....
    EOS

    text = pdf.text
    (expect text).to have_size 4
    (expect text[1][:string]).to start_with 'x'
    (expect text[3][:string]).to start_with 'x'
  end

  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}
    ----
    EOS

    listing_page_numbers = (pdf.find_text 'listing').map {|it| it[:page_number] }.uniq
    (expect listing_page_numbers).to eql [2]
  end

  it 'should keep anchor together with block when block is moved to next page' do
    pdf = to_pdf <<~EOS
    #{(['paragraph'] * 20).join (?\n * 2)}

    [#listing-1%unbreakable]
    ----
    #{(['listing'] * 20).join ?\n}
    ----
    EOS

    (expect (pdf.page 1).text).not_to include 'listing'
    (expect (pdf.page 2).text).to include 'listing'
    (expect (dest = get_dest pdf, 'listing-1')).not_to be_nil
    (expect dest[:page_number]).to be 2
    (expect dest[:y]).to eql 805.89
  end

  it 'should place anchor directly at top of block' do
    input = <<~'EOS'
    paragraph

    [#listing-1]
    ----
    listing
    ----
    EOS

    lines = (to_pdf input, analyze: :line).lines
    pdf = to_pdf input
    (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]
  end

  it 'should offset anchor from top of block by value of block_anchor_top' do
    input = <<~'EOS'
    paragraph

    [#listing-1]
    ----
    listing
    ----
    EOS

    pdf_theme = { block_anchor_top: -12 }

    lines = (to_pdf input, pdf_theme: pdf_theme, analyze: :line).lines
    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] + -pdf_theme[:block_anchor_top])
  end

  it 'should place anchor at top of block if advanced to next page' do
    input = <<~EOS
    paragraph

    [#listing-1%unbreakable]
    ----
    #{(['filler'] * 25).join %(\n\n)}
    ----
    EOS

    lines = (to_pdf input, analyze: :line).lines
    pdf = to_pdf input
    (expect (dest = get_dest pdf, 'listing-1')).not_to be_nil
    (expect dest[:page_number]).to be 2
    (expect dest[:y]).to eql lines[0][:from][:y]
  end

  it 'should split block if it cannot fit on a whole page' do
    pdf = to_pdf <<~EOS, analyze: true
    #{(['paragraph'] * 20).join (?\n * 2)}

    ----
    #{(['listing'] * 60).join ?\n}
    ----
    EOS

    (expect pdf.pages).to have_size 2
    listing_texts = pdf.find_text 'listing'
    (expect listing_texts[0][:page_number]).to be 1
    (expect listing_texts[-1][:page_number]).to be 2
  end

  it 'should use dashed border to indicate where block is split across a page boundary', visual: true do
    to_file = to_pdf_file <<~EOS, 'listing-page-split.pdf'
    ----
    #{(['listing'] * 60).join ?\n}
    ----

    ----
    #{(['more listing'] * 2).join ?\n}
    ----
    EOS

    (expect to_file).to visually_match 'listing-page-split.pdf'
  end

  it 'should not collapse bottom padding if block ends near bottom of page' do
    pdf_theme = {
      code_padding: 11,
      code_background_color: 'EEEEEE',
      code_border_width: 0,
      code_border_radius: 0,
    }
    pdf = with_content_spacer 10, 695 do |spacer_path|
      to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
      image::#{spacer_path}[]

      ----
      $ gem install asciidoctor-pdf
      $ asciidoctor-pdf doc.adoc
      ----
      EOS
    end

    pages = pdf.pages
    (expect pages).to have_size 1
    gs = pdf.extract_graphic_states pages[0][:raw_content]
    (expect gs[1]).to have_background color: 'EEEEEE', top_left: [48.24, 98.89], bottom_right: [48.24, 48.24]
    last_text_y = pdf.text[-1][:y]
    (expect last_text_y - pdf_theme[:code_padding]).to be > 48.24

    pdf = with_content_spacer 10, 696 do |spacer_path|
      to_pdf <<~EOS, pdf_theme: pdf_theme, analyze: true
      image::#{spacer_path}[]

      ----
      $ gem install asciidoctor-pdf
      $ asciidoctor-pdf doc.adoc
      ----
      EOS
    end

    pages = pdf.pages
    (expect pages).to have_size 2
    gs = pdf.extract_graphic_states pages[0][:raw_content]
    (expect gs[1]).to have_background color: 'EEEEEE', top_left: [48.24, 97.89], bottom_right: [48.24, 48.24]
    (expect pdf.text[0][:page_number]).to eql 1
    (expect pdf.text[1][:page_number]).to eql 2
    (expect pdf.text[0][:y] - pdf_theme[:code_padding]).to be > 48.24
  end

  it 'should break line if wider than content area of block and still compute height correctly' do
    pdf_theme = {
      code_border_radius: 0,
      code_border_color: 'CCCCCC',
      code_border_width: [1, 0],
      code_background_color: 'transparent',
      sidebar_border_radius: 0,
      sidebar_border_width: [1, 0],
      sidebar_border_color: '0000EE',
      sidebar_background_color: 'transparent',
    }
    input = <<~EOS
    ****
    before

    ----
    one
    tw#{'o' * 250}
    three
    ----

    after
    ****
    EOS

    pdf = to_pdf input, pdf_theme: pdf_theme, analyze: true
    (expect pdf.find_text %r/^ooo/).to have_size 3
    lines = (to_pdf input, pdf_theme: pdf_theme, analyze: :line, debug: true).lines.sort_by {|it| -it[:from][:y] }
    (expect lines).to have_size 4
    (expect lines[0][:color]).to eql '0000EE'
    (expect lines[1][:color]).to eql 'CCCCCC'
    (expect lines[2][:color]).to eql 'CCCCCC'
    (expect lines[3][:color]).to eql '0000EE'
    (expect (lines[0][:from][:y] - lines[1][:from][:y]).round 5).to eql ((lines[2][:from][:y] - lines[3][:from][:y]).round 5)
  end

  it 'should resize font to prevent wrapping if autofit option is set' do
    pdf = to_pdf <<~'EOS', analyze: true
    [%autofit]
    ----
    @themesdir = ::File.expand_path theme.__dir__ || (doc.attr 'pdf-themesdir') || ::Dir.pwd
    ----
    EOS

    (expect pdf.text).to have_size 1
    (expect pdf.text[0][:font_size]).to be < build_pdf_theme.code_font_size
  end

  it 'should not resize font if not necessary' do
    pdf = to_pdf <<~'EOS', analyze: true
    [%autofit]
    ----
    puts 'Hello, World!'
    ----
    EOS

    (expect pdf.text).to have_size 1
    (expect pdf.text[0][:font_size]).to eql 11
  end

  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]
    ----
    play_symbol = (node.document.attr? 'icons', 'font') ? %(<font name="fas">#{(icon_font_data 'fas').unicode 'play'}</font>) : RightPointer
{'fraction_non_alphanumeric': 0.15091769429308863, 'fraction_numerical': 0.021723544594207056, 'mean_word_length': 2.7118147951037788, 'pattern_counts': {'":': 0, '<': 69, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5070351', 'n_tokens_mistral': 5355, 'n_tokens_neox': 5016, 'n_words': 1343}