91 lines
2.9 KiB
Go
91 lines
2.9 KiB
Go
package officekit
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/zlib"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// buildMinimalPdf wraps one FlateDecode-compressed content stream in
|
|
// just enough surrounding PDF syntax for ReadPdfText's regex-based
|
|
// scanner to find it — a %PDF- header and one `N 0 obj << ... /Filter
|
|
// /FlateDecode >> stream ... endstream endobj` object. Not a complete,
|
|
// schema-valid PDF (no xref table, no page tree), but ReadPdfText
|
|
// never looks at either of those, so this is a faithful enough fixture
|
|
// for exercising it without needing a real binary PDF checked into
|
|
// the repo.
|
|
func buildMinimalPdf(t *testing.T, content string) []byte {
|
|
t.Helper()
|
|
var compressed bytes.Buffer
|
|
zw := zlib.NewWriter(&compressed)
|
|
if _, err := zw.Write([]byte(content)); err != nil {
|
|
t.Fatalf("zlib write: %v", err)
|
|
}
|
|
if err := zw.Close(); err != nil {
|
|
t.Fatalf("zlib close: %v", err)
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
buf.WriteString("%PDF-1.4\n")
|
|
buf.WriteString("4 0 obj\n<< /Length 999 /Filter /FlateDecode >>\nstream\n")
|
|
buf.Write(compressed.Bytes())
|
|
buf.WriteString("\nendstream\nendobj\n%%EOF")
|
|
return buf.Bytes()
|
|
}
|
|
|
|
func TestReadPdfTextBasic(t *testing.T) {
|
|
data := buildMinimalPdf(t, "BT /F1 12 Tf 72 712 Td (Hello World) Tj ET")
|
|
text, err := ReadPdfText(data)
|
|
if err != nil {
|
|
t.Fatalf("ReadPdfText: %v", err)
|
|
}
|
|
if text != "Hello World" {
|
|
t.Errorf("got %q, want %q", text, "Hello World")
|
|
}
|
|
}
|
|
|
|
func TestReadPdfTextLineBreaksAndEscapes(t *testing.T) {
|
|
content := "BT /F1 12 Tf 72 712 Td (Line one \\(escaped\\)) Tj\n72 700 Td (Line two) Tj ET"
|
|
data := buildMinimalPdf(t, content)
|
|
text, err := ReadPdfText(data)
|
|
if err != nil {
|
|
t.Fatalf("ReadPdfText: %v", err)
|
|
}
|
|
if !strings.Contains(text, "Line one (escaped)") {
|
|
t.Errorf("expected unescaped parens in %q", text)
|
|
}
|
|
if !strings.Contains(text, "Line two") {
|
|
t.Errorf("expected second Td'd line's text in %q", text)
|
|
}
|
|
if !strings.Contains(text, "\n") {
|
|
t.Errorf("expected a newline between Td-separated text runs, got %q", text)
|
|
}
|
|
}
|
|
|
|
func TestReadPdfTextTJArrayKerning(t *testing.T) {
|
|
data := buildMinimalPdf(t, "BT /F1 12 Tf 72 712 Td [(Hel)-20(lo)20( world)] TJ ET")
|
|
text, err := ReadPdfText(data)
|
|
if err != nil {
|
|
t.Fatalf("ReadPdfText: %v", err)
|
|
}
|
|
if text != "Hello world" {
|
|
t.Errorf("got %q, want %q (TJ kerning numbers must not become spaces)", text, "Hello world")
|
|
}
|
|
}
|
|
|
|
func TestReadPdfTextRejectsNonPdf(t *testing.T) {
|
|
if _, err := ReadPdfText([]byte("just some ordinary bytes, not a PDF")); err == nil {
|
|
t.Error("expected an error for input with no %PDF- header")
|
|
}
|
|
}
|
|
|
|
func TestReadPdfTextNoShownTextOperators(t *testing.T) {
|
|
// A validly-structured FlateDecode stream (so the inflate step
|
|
// succeeds) with no Tj/TJ/'/" calls at all — e.g. a pure image draw.
|
|
data := buildMinimalPdf(t, "q 100 0 0 100 0 0 cm /Im0 Do Q")
|
|
if _, err := ReadPdfText(data); err == nil {
|
|
t.Error("expected an error when the stream has no text-showing operators")
|
|
}
|
|
}
|