Files
enkly-ai/pkg/officekit/pdf_test.go
enkly 79ce8dc4c0
Some checks failed
Go CI Pipeline / build_and_test (push) Failing after 22m0s
Init Project
2026-10-30 18:06:55 +01:00

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")
}
}