Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,47 @@ func appendReal(dst []byte, f float64) []byte {
return strconv.AppendFloat(dst, f, 'f', -1, 64)
}

// appendString writes a literal string, escaping only what has to be escaped
// and rendering anything unprintable as an octal escape.
// appendString writes a string in whichever of the two forms a PDF allows is
// shorter. A literal string spells an unprintable byte as a four-character
// octal escape, so text in UTF-16 — which is how a PDF says anything that is
// not Latin-1 — comes out four times its length; the hex form costs two
// characters a byte whatever the byte is.
func appendString(dst []byte, s []byte) []byte {
if literalStringCost(s) > 2*len(s)+2 {
return appendHexString(dst, s)
}
return appendLiteralString(dst, s)
}

// literalStringCost is how many characters the literal form would take.
func literalStringCost(s []byte) int {
n := 2
for _, c := range s {
switch {
case c == '(' || c == ')' || c == '\\' || c == '\n' || c == '\r' || c == '\t':
n += 2
case c < 32 || c > 126:
n += 4
default:
n++
}
}
return n
}

// appendHexString writes the <hex> form.
func appendHexString(dst []byte, s []byte) []byte {
const hex = "0123456789ABCDEF"
dst = append(dst, '<')
for _, c := range s {
dst = append(dst, hex[c>>4], hex[c&15])
}
return append(dst, '>')
}

// appendLiteralString writes the (parenthesised) form, escaping only what has
// to be escaped and rendering anything unprintable as an octal escape.
func appendLiteralString(dst []byte, s []byte) []byte {
dst = append(dst, '(')
for _, c := range s {
switch {
Expand Down
6 changes: 5 additions & 1 deletion writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ func TestAppendObject(t *testing.T) {
{String("plain"), "(plain)"},
{String("a(b)c\\"), `(a\(b\)c\\)`},
{String("\n\r\t"), `(\n\r\t)`},
{String{0x00, 0xFF}, `(\000\377)`},
{String{0x00, 0xFF}, "<00FF>"},
// One awkward byte in a long readable string still comes out readable.
{String("a readable line with one \x00 in it"), `(a readable line with one \000 in it)`},
// Text in UTF-16 goes out as hex, which is half the size.
{String{0xFE, 0xFF, 0x00, 0x41}, "<FEFF0041>"},
{Name("Simple"), "/Simple"},
{Name("With Space"), "/With#20Space"},
{Name("h#sh"), "/h#23sh"},
Expand Down
Loading