# encoding: ASCII-8BIT
require_relative "spec_helper"

# See PDF Reference, Sixth Edition (1.7) pp51-60 for details
describe "PDF Object Serialization" do

  it "should convert Ruby's nil to PDF null" do
    PDF::Core::PdfObject(nil).should == "null"
  end

  it "should convert Ruby booleans to PDF booleans" do
    PDF::Core::PdfObject(true).should  == "true"
    PDF::Core::PdfObject(false).should == "false"
  end

  it "should convert a Ruby number to PDF number" do
    PDF::Core::PdfObject(42).should == "42"

    # numbers are rounded to four decimal places
    PDF::Core::PdfObject(1.214112421).should == "1.2141"
  end

  it "should convert a Ruby time object to a PDF timestamp" do
    t = Time.now
    PDF::Core::PdfObject(t).should == t.strftime("(D:%Y%m%d%H%M%S%z").chop.chop + "'00')"
  end

  it "should convert a Ruby string to PDF string when inside a content stream" do
    s = "I can has a string"
    PDF::Inspector.parse(PDF::Core::PdfObject(s, true)).should == s
  end

  it "should convert a Ruby string to a UTF-16 PDF string when outside a content stream" do
    s = "I can has a string"
    s_utf16 = "\xFE\xFF" + s.unpack("U*").pack("n*")
    PDF::Inspector.parse(PDF::Core::PdfObject(s, false)).should == s_utf16
  end

  it "should convert a Ruby string with characters outside the BMP to its " +
     "UTF-16 representation with a BOM" do
    # U+10192 ROMAN SEMUNCIA SIGN
    semuncia = [65938].pack("U")
    PDF::Core::PdfObject(semuncia, false).upcase.should == "<FEFFD800DD92>"
  end

  it "should pass through bytes regardless of content stream status for ByteString" do
    PDF::Core::PdfObject(PDF::Core::ByteString.new("\xDE\xAD\xBE\xEF")).upcase.
      should == "<DEADBEEF>"
  end

  it "should escape parens when converting from Ruby string to PDF" do
    s =  'I )(can has a string'
    PDF::Inspector.parse(PDF::Core::PdfObject(s, true)).should == s
  end

  it "should handle ruby escaped parens when converting to PDF string" do
    s = 'I can \\)( has string'
    PDF::Inspector.parse(PDF::Core::PdfObject(s, true)).should == s
  end

  it "should escape various strings correctly when converting a LiteralString" do
    ls = PDF::Core::LiteralString.new("abc")
    PDF::Core::PdfObject(ls).should == "(abc)"

    ls = PDF::Core::LiteralString.new("abc\x0Ade") # should escape \n
    PDF::Core::PdfObject(ls).should == "(abc\x5C\x0Ade)"

    ls = PDF::Core::LiteralString.new("abc\x0Dde") # should escape \r
    PDF::Core::PdfObject(ls).should == "(abc\x5C\x0Dde)"

    ls = PDF::Core::LiteralString.new("abc\x09de") # should escape \t
    PDF::Core::PdfObject(ls).should == "(abc\x5C\x09de)"

    ls = PDF::Core::LiteralString.new("abc\x08de") # should escape \b
    PDF::Core::PdfObject(ls).should == "(abc\x5C\x08de)"

    ls = PDF::Core::LiteralString.new("abc\x0Cde") # should escape \f
    PDF::Core::PdfObject(ls).should == "(abc\x5C\x0Cde)"

    ls = PDF::Core::LiteralString.new("abc(de") # should escape \(
    PDF::Core::PdfObject(ls).should == "(abc\x5C(de)"

    ls = PDF::Core::LiteralString.new("abc)de") # should escape \)
    PDF::Core::PdfObject(ls).should == "(abc\x5C)de)"

    ls = PDF::Core::LiteralString.new("abc\x5Cde") # should escape \\
    PDF::Core::PdfObject(ls).should == "(abc\x5C\x5Cde)"
    PDF::Core::PdfObject(ls).size.should == 9
  end

  it "should escape strings correctly when converting a LiteralString that is not utf-8" do
    data = "\x43\xaf\xc9\x7f\xef\xf\xe6\xa8\xcb\x5c\xaf\xd0"
    ls = PDF::Core::LiteralString.new(data)
    PDF::Core::PdfObject(ls).should == "(\x43\xaf\xc9\x7f\xef\xf\xe6\xa8\xcb\x5c\x5c\xaf\xd0)"
  end

  it "should convert a Ruby symbol to PDF name" do
    PDF::Core::PdfObject(:my_symbol).should == "/my_symbol"
    PDF::Core::PdfObject(:"A;Name_With-Various***Characters?").should ==
     "/A;Name_With-Various***Characters?"
  end

  it "should convert a whitespace or delimiter containing Ruby symbol to a PDF name" do
    PDF::Core::PdfObject(:"my symbol").should == "/my#20symbol"
    PDF::Core::PdfObject(:"my#symbol").should == "/my#23symbol"
    PDF::Core::PdfObject(:"my/symbol").should == "/my#2Fsymbol"
    PDF::Core::PdfObject(:"my(symbol").should == "/my#28symbol"
    PDF::Core::PdfObject(:"my)symbol").should == "/my#29symbol"
    PDF::Core::PdfObject(:"my<symbol").should == "/my#3Csymbol"
    PDF::Core::PdfObject(:"my>symbol").should == "/my#3Esymbol"
  end

  it "should convert a Ruby array to PDF Array when inside a content stream" do
    PDF::Core::PdfObject([1,2,3]).should == "[1 2 3]"
    PDF::Inspector.parse(PDF::Core::PdfObject([[1,2],:foo,"Bar"], true)).should ==
      [[1,2],:foo, "Bar"]
  end

  it "should convert a Ruby array to PDF Array when outside a content stream" do
    bar = "\xFE\xFF" + "Bar".unpack("U*").pack("n*")
    PDF::Core::PdfObject([1,2,3]).should == "[1 2 3]"
    PDF::Inspector.parse(PDF::Core::PdfObject([[1,2],:foo,"Bar"], false)).should ==
      [[1,2],:foo, bar]
  end

  it "should convert a Ruby hash to a PDF Dictionary when inside a content stream" do
    dict = PDF::Core::PdfObject( {:foo  => :bar,
                              "baz" => [1,2,3],
                              :bang => {:a => "what", :b => [:you, :say] }}, true )

    res = PDF::Inspector.parse(dict)

    res[:foo].should == :bar
    res[:baz].should == [1,2,3]
    res[:bang].should == { :a => "what", :b => [:you, :say] }

  end

  it "should convert a Ruby hash to a PDF Dictionary when outside a content stream" do
    what = "\xFE\xFF" + "what".unpack("U*").pack("n*")
    dict = PDF::Core::PdfObject( {:foo  => :bar,
                              "baz" => [1,2,3],
                              :bang => {:a => "what", :b => [:you, :say] }}, false )

    res = PDF::Inspector.parse(dict)

    res[:foo].should == :bar
    res[:baz].should == [1,2,3]
    res[:bang].should == { :a => what, :b => [:you, :say] }

  end

  it "should not allow keys other than strings or symbols for PDF dicts" do
    lambda { PDF::Core::PdfObject(:foo => :bar, :baz => :bang, 1 => 4) }.
      should raise_error(PDF::Core::Errors::FailedObjectConversion)
  end

  it "should convert a Prawn::Reference to a PDF indirect object reference" do
    ref = PDF::Core::Reference(1,true)
    PDF::Core::PdfObject(ref).should == ref.to_s
  end

  it "should convert a NameTree::Node to a PDF hash" do
    # FIXME: Soft dependench on Prawn::Document exists in Node
    node = PDF::Core::NameTree::Node.new(nil, 10)
    node.add "hello", 1.0
    node.add "world", 2.0
    data = PDF::Core::PdfObject(node)
    res = PDF::Inspector.parse(data)
    res.should == {:Names => ["hello", 1.0, "world", 2.0]}
  end
end