# encoding: utf-8

require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")

describe "Prawn::Text::NBSP" do
  it "should be defined" do
    expect(Prawn::Text::NBSP).to eq(" ")
  end
end

describe "#height_of" do
  before(:each) { create_pdf }

  it "should return the height that would be required to print a" \
    "particular string of text" do
    original_y = @pdf.y
    @pdf.text("Foo")
    new_y = @pdf.y
    expect(@pdf.height_of("Foo")).to be_within(0.0001).of(original_y - new_y)
  end

  it "should omit the gap below the last descender if :final_gap => false " \
    "is given" do
    original_y = @pdf.y
    @pdf.text("Foo", :final_gap => false)
    new_y = @pdf.y
    expect(@pdf.height_of("Foo", :final_gap => false)).to be_within(0.0001).of(original_y - new_y)
  end

  it "should raise_error CannotFit if a too-small width is given" do
    expect do
      @pdf.height_of("text", :width => 1)
    end.to raise_error(Prawn::Errors::CannotFit)
  end

  it "should raise_error NotImplementedError if :indent_paragraphs option is provided" do
    expect {
      @pdf.height_of("hai", :width => 300,
                            :indent_paragraphs => 60)
    }.to raise_error(NotImplementedError)
  end

  it "should_not raise_error Prawn::Errors::UnknownOption if :final_gap option is provided" do
    @pdf.height_of("hai", :width => 300, :final_gap => true)
  end
end

describe "#text" do
  before(:each) { create_pdf }

  it "should not fail when @output is nil when PDF::Core::Text::LineWrap#finalize_line is called" do
    # need a document with margins for these particulars to produce the
    # condition that was throwing the error
    pdf = Prawn::Document.new
    pdf.text "transparency " * 150, :size => 18
  end

  it "should allow drawing empty strings to the page" do
    @pdf.text " "
    text = PDF::Inspector::Text.analyze(@pdf.render)
    # If anything is rendered to the page, it should be whitespace.
    text.strings.each { |str| expect(str).to match(/\A\s*\z/) }
  end

  it "should ignore call when string is nil" do
    expect(@pdf.text(nil)).to eq false
  end

  it "should correctly render empty paragraphs" do
    @pdf.text "text\n\ntext"
    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(@pdf.page_count).to eq(1)
    expect(text.strings.reject(&:empty?)).to eq(["text", "text"])
  end

  it "should correctly render empty paragraphs with :indent_paragraphs" do
    @pdf.text "text\n\ntext", :indent_paragraphs => 5
    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(@pdf.page_count).to eq(1)
    expect(text.strings.reject(&:empty?)).to eq(["text", "text"])
  end

  it "should correctly render strings ending with empty paragraphs and " \
     ":inline_format and :indent_paragraphs" do
    @pdf.text "text\n\n", :inline_format => true, :indent_paragraphs => 5
    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(@pdf.page_count).to eq(1)
    expect(text.strings).to eq(["text"])
  end

  it "should default to use kerning information" do
    @pdf.text "hello world"
    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(text.kerned[0]).to eq true
  end

  it "should be able to disable kerning with an option" do
    @pdf.text "hello world", :kerning => false
    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(text.kerned[0]).to eq false
  end

  it "should be able to disable kerning document-wide" do
    @pdf.default_kerning(false)
    @pdf.default_kerning = false
    @pdf.text "hello world"
    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(text.kerned[0]).to eq false
  end

  it "option should be able to override document-wide kerning disabling" do
    @pdf.default_kerning = false
    @pdf.text "hello world", :kerning => true
    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(text.kerned[0]).to eq true
  end

  it "should raise_error ArgumentError if :at option included" do
    expect { @pdf.text("hai", :at => [0, 0]) }.to raise_error(ArgumentError)
  end

  it "should advance down the document based on font_height" do
    position = @pdf.y
    @pdf.text "Foo"

    expect(@pdf.y).to be_within(0.0001).of(position - @pdf.font.height)

    position = @pdf.y
    @pdf.text "Foo\nBar\nBaz"
    expect(@pdf.y).to be_within(0.0001).of(position - 3 * @pdf.font.height)
  end

  it "should advance down the document based on font_height with size option" do
    position = @pdf.y
    @pdf.text "Foo", :size => 15

    @pdf.font_size = 15
    expect(@pdf.y).to be_within(0.0001).of(position - @pdf.font.height)

    position = @pdf.y
    @pdf.text "Foo\nBar\nBaz"
    expect(@pdf.y).to be_within(0.0001).of(position - 3 * @pdf.font.height)
  end

  it "should advance down the document based on font_height with leading option" do
    position = @pdf.y
    leading = 2
    @pdf.text "Foo", :leading => leading

    expect(@pdf.y).to be_within(0.0001).of(position - @pdf.font.height - leading)

    position = @pdf.y
    @pdf.text "Foo\nBar\nBaz"
    expect(@pdf.y).to be_within(0.0001).of(position - 3 * @pdf.font.height)
  end

  it "should advance only to the bottom of the final descender if final_gap is false" do
    position = @pdf.y
    @pdf.text "Foo", :final_gap => false

    expect(@pdf.y).to be_within(0.0001).of(position - @pdf.font.ascender - @pdf.font.descender)

    position = @pdf.y
    @pdf.text "Foo\nBar\nBaz", :final_gap => false
    expect(@pdf.y).to be_within(0.0001).of(position - 2 * @pdf.font.height - @pdf.font.ascender - @pdf.font.descender)
  end

  it "should be able to print text starting at the last line of a page" do
    @pdf.move_cursor_to(@pdf.font.height)
    @pdf.text("hello world")
    pages = PDF::Inspector::Page.analyze(@pdf.render).pages
    expect(pages.size).to eq(1)
  end

  it "should default to 12 point helvetica" do
    @pdf.text "Blah"
    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(text.font_settings[0][:name]).to eq(:Helvetica)
    expect(text.font_settings[0][:size]).to eq(12)
    expect(text.strings.first).to eq("Blah")
  end

  it "should allow setting font size" do
    @pdf.text "Blah", :size => 16
    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(text.font_settings[0][:size]).to eq(16)
  end

  it "should allow setting a default font size" do
    @pdf.font_size = 16
    @pdf.text "Blah"
    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(text.font_settings[0][:size]).to eq(16)
  end

  it "should allow overriding default font for a single instance" do
    @pdf.font_size = 16

    @pdf.text "Blah", :size => 11
    @pdf.text "Blaz"
    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(text.font_settings[0][:size]).to eq(11)
    expect(text.font_settings[1][:size]).to eq(16)
  end

  it "should allow setting a font size transaction with a block" do
    @pdf.font_size 16 do
      @pdf.text 'Blah'
    end

    @pdf.text 'blah'

    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(text.font_settings[0][:size]).to eq(16)
    expect(text.font_settings[1][:size]).to eq(12)
  end

  it "should allow manual setting the font size when in a font size block" do
    @pdf.font_size(16) do
      @pdf.text 'Foo'
      @pdf.text 'Blah', :size => 11
      @pdf.text 'Blaz'
    end
    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(text.font_settings[0][:size]).to eq(16)
    expect(text.font_settings[1][:size]).to eq(11)
    expect(text.font_settings[2][:size]).to eq(16)
  end

  it "should allow registering of built-in font_settings on the fly" do
    @pdf.font "Times-Roman"
    @pdf.text "Blah"
    @pdf.font "Courier"
    @pdf.text "Blaz"
    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(text.font_settings[0][:name]).to eq(:"Times-Roman")
    expect(text.font_settings[1][:name]).to eq(:Courier)
  end

  it "should utilise the same default font across multiple pages" do
    @pdf.text "Blah"
    @pdf.start_new_page
    @pdf.text "Blaz"
    text = PDF::Inspector::Text.analyze(@pdf.render)

    expect(text.font_settings.size).to eq(2)
    expect(text.font_settings[0][:name]).to eq(:Helvetica)
    expect(text.font_settings[1][:name]).to eq(:Helvetica)
  end

  it "should raise_error an exception when an unknown font is used" do
    expect { @pdf.font "Pao bu" }.to raise_error(Prawn::Errors::UnknownFont)
  end

  it "should_not raise_error an exception when providing Pathname instance as font" do
    @pdf.font Pathname.new("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
  end

  it "should correctly render a utf-8 string when using a built-in font" do
    str = "©" # copyright symbol
    @pdf.text str

    # grab the text from the rendered PDF and ensure it matches
    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(text.strings.first).to eq(str)
  end

  it "should correctly render a utf-8 string when using a TTF font" do
    str = "©" # copyright symbol
    @pdf.font "#{Prawn::DATADIR}/fonts/DejaVuSans.ttf"
    @pdf.text str

    # grab the text from the rendered PDF and ensure it matches
    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(text.strings.first).to eq(str)
  end

  it "subsets mixed low-ASCII and non-ASCII characters when they can be subsetted together" do
    str = "It’s super effective!"
    @pdf.font "#{Prawn::DATADIR}/fonts/DejaVuSans.ttf"
    @pdf.text str

    text = PDF::Inspector::Text.analyze(@pdf.render)
    expect(text.strings.first).to eq(str)
  end

  it "should correctly render a string with higher bit characters across a page break when using a built-in font" do
    str = "©"
    @pdf.move_cursor_to(@pdf.font.height)
    @pdf.text(str + "\n" + str)

    pages = PDF::Inspector::Page.analyze(@pdf.render).pages
    expect(pages.size).to eq(2)
    expect(pages[0][:strings]).to eq([str])
    expect(pages[1][:strings]).to eq([str])
  end

  it "should correctly render a string with higher bit characters across" \
    " a page break when using a built-in font and :indent_paragraphs option" do
    str = "©"
    @pdf.move_cursor_to(@pdf.font.height)
    @pdf.text(str + "\n" + str, :indent_paragraphs => 20)

    pages = PDF::Inspector::Page.analyze(@pdf.render).pages
    expect(pages.size).to eq(2)
    expect(pages[0][:strings]).to eq([str])
    expect(pages[1][:strings]).to eq([str])
  end

  it "should raise_error an exception when a utf-8 incompatible string is rendered" do
    str = "Blah \xDD"
    expect { @pdf.text str }.to raise_error(
      Prawn::Errors::IncompatibleStringEncoding)
  end

  it "should_not raise_error an exception when a shift-jis string is rendered" do
    datafile = "#{Prawn::DATADIR}/shift_jis_text.txt"
    sjis_str = File.open(datafile, "r:shift_jis") { |f| f.gets }
    @pdf.font("#{Prawn::DATADIR}/fonts/gkai00mp.ttf")

    # Expect that the call to text will not raise an encoding error
    @pdf.text(sjis_str)
  end

  it "should call move_past_bottom when printing more text than can fit" \
     " between the current document.y and bounds.bottom" do
    @pdf.y = @pdf.font.height
    @pdf.text "Hello"
    @pdf.text "World"
    pages = PDF::Inspector::Page.analyze(@pdf.render).pages
    expect(pages.size).to eq(2)
    expect(pages[0][:strings]).to eq(["Hello"])
    expect(pages[1][:strings]).to eq(["World"])
  end

  describe "with :indent_paragraphs option" do
    it "should indent the paragraphs" do
      hello = "hello " * 50
      hello2 = "hello " * 50
      @pdf.text(hello + "\n" + hello2, :indent_paragraphs => 60)
      text = PDF::Inspector::Text.analyze(@pdf.render)
      expect(text.strings[0]).to eq(("hello " * 19).strip)
      expect(text.strings[1]).to eq(("hello " * 21).strip)
      expect(text.strings[3]).to eq(("hello " * 19).strip)
      expect(text.strings[4]).to eq(("hello " * 21).strip)
    end

    it "should indent from right side when using :rtl direction" do
      para1 = "The rain in spain falls mainly on the plains " * 3
      para2 = "The rain in spain falls mainly on the plains " * 3

      @pdf.text(para1 + "\n" + para2, :indent_paragraphs => 60, :direction => :rtl)

      text = PDF::Inspector::Text.analyze(@pdf.render)

      lines = text.strings
      x_positions = text.positions.map { |e| e[0] }

      # NOTE: The code below reflects Prawn's current kerning behavior for RTL
      # text, which isn't necessarily correct. If we change that behavior,
      # this test will need to be updated.

      expect(x_positions[0]).to(
        be_within(0.001).of(@pdf.bounds.absolute_right - 60 -
                            @pdf.width_of(lines[0].reverse, :kerning => true)))

      expect(x_positions[1]).to(
        be_within(0.001).of(@pdf.bounds.absolute_right -
                            @pdf.width_of(lines[1].reverse, :kerning => true)))

      expect(x_positions[2]).to(
        be_within(0.001).of(@pdf.bounds.absolute_right - 60 -
                            @pdf.width_of(lines[2].reverse, :kerning => true)))

      expect(x_positions[3]).to(
        be_within(0.001).of(@pdf.bounds.absolute_right -
                            @pdf.width_of(lines[3].reverse, :kerning => true)))
    end

    it "should indent from right side when document has :rtl direction" do
      para1 = "The rain in spain falls mainly on the plains " * 3
      para2 = "The rain in spain falls mainly on the plains " * 3

      @pdf.text_direction = :rtl
      @pdf.text(para1 + "\n" + para2, :indent_paragraphs => 60)

      text = PDF::Inspector::Text.analyze(@pdf.render)

      lines = text.strings
      x_positions = text.positions.map { |e| e[0] }

      # NOTE: The code below reflects Prawn's current kerning behavior for RTL
      # text, which isn't necessarily correct. If we change that behavior,
      # this test will need to be updated.

      expect(x_positions[0]).to(
        be_within(0.001).of(@pdf.bounds.absolute_right - 60 -
                            @pdf.width_of(lines[0].reverse, :kerning => true)))

      expect(x_positions[1]).to(
        be_within(0.001).of(@pdf.bounds.absolute_right -
                            @pdf.width_of(lines[1].reverse, :kerning => true)))

      expect(x_positions[2]).to(
        be_within(0.001).of(@pdf.bounds.absolute_right - 60 -
                            @pdf.width_of(lines[2].reverse, :kerning => true)))

      expect(x_positions[3]).to(
        be_within(0.001).of(@pdf.bounds.absolute_right -
                            @pdf.width_of(lines[3].reverse, :kerning => true)))
    end

    it "should indent from left side when using :ltr direction" do
      para1 = "The rain in spain falls mainly on the plains " * 3
      para2 = "The rain in spain falls mainly on the plains " * 3

      @pdf.text(para1 + "\n" + para2, :indent_paragraphs => 60, :direction => :ltr)

      text = PDF::Inspector::Text.analyze(@pdf.render)

      x_positions = text.positions.map { |e| e[0] }

      expect(x_positions[0]).to eq(60)
      expect(x_positions[1]).to eq(0)

      expect(x_positions[2]).to eq(60)
      expect(x_positions[3]).to eq(0)
    end

    it "should indent from left side when document has :ltr direction" do
      para1 = "The rain in spain falls mainly on the plains " * 3
      para2 = "The rain in spain falls mainly on the plains " * 3

      @pdf.text_direction = :ltr
      @pdf.text(para1 + "\n" + para2, :indent_paragraphs => 60)

      text = PDF::Inspector::Text.analyze(@pdf.render)

      x_positions = text.positions.map { |e| e[0] }

      expect(x_positions[0]).to eq(60)
      expect(x_positions[1]).to eq(0)

      expect(x_positions[2]).to eq(60)
      expect(x_positions[3]).to eq(0)
    end

    describe "when wrap to new page, and first line of new page" \
             " is not the start of a new paragraph, that line should" \
             " not be indented" do
      it "should indent the paragraphs" do
        hello = "hello " * 50
        hello2 = "hello " * 50
        @pdf.move_cursor_to(@pdf.font.height)
        @pdf.text(hello + "\n" + hello2, :indent_paragraphs => 60)
        text = PDF::Inspector::Text.analyze(@pdf.render)
        expect(text.strings[0]).to eq(("hello " * 19).strip)
        expect(text.strings[1]).to eq(("hello " * 21).strip)
        expect(text.strings[3]).to eq(("hello " * 19).strip)
        expect(text.strings[4]).to eq(("hello " * 21).strip)
      end
    end
    describe "when wrap to new page, and first line of new page" \
             " is the start of a new paragraph, that line should" \
             " be indented" do
      it "should indent the paragraphs" do
        hello = "hello " * 50
        hello2 = "hello " * 50
        @pdf.move_cursor_to(@pdf.font.height * 3)
        @pdf.text(hello + "\n" + hello2, :indent_paragraphs => 60)
        text = PDF::Inspector::Text.analyze(@pdf.render)
        expect(text.strings[0]).to eq(("hello " * 19).strip)
        expect(text.strings[1]).to eq(("hello " * 21).strip)
        expect(text.strings[3]).to eq(("hello " * 19).strip)
        expect(text.strings[4]).to eq(("hello " * 21).strip)
      end
    end
  end

  describe "kerning" do
    it "should respect text kerning setting (document default)" do
      create_pdf
      expect(@pdf.font).to receive(:compute_width_of).with("VAT", hash_including(kerning: true)).and_return(10)
      @pdf.text "VAT"
    end

    it "should respect text kerning setting (kerning=true)" do
      create_pdf
      expect(@pdf.font).to receive(:compute_width_of).with("VAT", hash_including(kerning: true)).at_least(:once).and_return(10)
      @pdf.text "VAT", :kerning => true
    end

    it "should respect text kerning setting (kerning=false)" do
      create_pdf
      expect(@pdf.font).to receive(:compute_width_of).with("VAT", hash_including(kerning: false)).at_least(:once).and_return(10)
      @pdf.text "VAT", :kerning => false
    end
  end

  describe "#shrink_to_fit with special utf-8 text" do
    it "Should not throw an exception", :unresolved, :issue => 603 do
      pages = 0
      doc = Prawn::Document.new(page_size: 'A4', margin: [2, 2, 2, 2]) do |pdf|
        add_unicode_fonts(pdf)
        pdf.bounding_box([1, 1], :width => 90, :height => 50) do
          broken_text = " Sample Text\nSAMPLE SAMPLE SAMPLEoddělení ZMĚN\nSAMPLE"
          pdf.text broken_text, :overflow => :shrink_to_fit
        end
      end
    end
  end

  def add_unicode_fonts(pdf)
    dejavu = "#{::Prawn::BASEDIR}/data/fonts/DejaVuSans.ttf"
    pdf.font_families.update(
      "dejavu" => {
        :normal      => dejavu,
        :italic      => dejavu,
        :bold        => dejavu,
        :bold_italic => dejavu
      }
    )
    pdf.fallback_fonts = ["dejavu"]
  end

  describe "fallback_fonts" do
    it "should preserve font style" do
      create_pdf

      @pdf.fallback_fonts ["Helvetica"]
      @pdf.font "Times-Roman", :style => :italic do
        @pdf.text "hello"
      end

      text = PDF::Inspector::Text.analyze(@pdf.render)
      fonts_used = text.font_settings.map { |e| e[:name] }

      expect(fonts_used.length).to eq(1)
      expect(fonts_used[0]).to eq(:"Times-Italic")
      expect(text.strings[0]).to eq("hello")
    end
  end
end
