92 lines
2.8 KiB
Go
92 lines
2.8 KiB
Go
package webui
|
|
|
|
import (
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// pdfHorizontalRules extracts every horizontal line the PDF draws, as (y, width).
|
|
// The content stream draws a line as: "<w> w <x1> <y> m <x2> <y> l S".
|
|
func pdfHorizontalRules(pdf []byte) map[string]int {
|
|
re := regexp.MustCompile(`([\d.]+) w\s*\n?[^\n]*?([\d.]+) ([\d.]+) m\s*\n?[^\n]*?([\d.]+) ([\d.]+) l`)
|
|
out := map[string]int{}
|
|
for _, m := range re.FindAllStringSubmatch(string(pdf), -1) {
|
|
y1, y2 := m[3], m[5]
|
|
if y1 != y2 {
|
|
continue // not horizontal
|
|
}
|
|
out[y1]++
|
|
}
|
|
return out
|
|
}
|
|
|
|
// The bug: the row loop drew a rule under EVERY row including the last, and then the
|
|
// summary block drew its divider at exactly the same y. Two rules at one y read as a
|
|
// double border between the table and its summaries.
|
|
func TestPDFDrawsOneRuleBetweenTableAndSummaries(t *testing.T) {
|
|
s := newCalcTable()
|
|
s.AddSummaryRow(UserSummaryRow{ID: "t", Label: "Total", Fn: CALC_FN_SUM,
|
|
Operands: []string{"Revenue"}, DataType: CALC_TYPE_NUMBER, Precision: CalcPrecision(0)})
|
|
s.Render()
|
|
|
|
rules := pdfHorizontalRules(s.ExportPDFBytes(AutoTablePDFHeader{Title: "R"}))
|
|
if len(rules) == 0 {
|
|
t.Fatal("no horizontal rules found — the extractor is wrong, not the PDF")
|
|
}
|
|
for y, n := range rules {
|
|
if n > 1 {
|
|
t.Errorf("%d rules stacked at y=%s — that is the double border", n, y)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Without summaries the table still gets its closing rule; removing the last row's
|
|
// rule must not leave the table open at the bottom.
|
|
func TestPDFClosesTheTableWithoutSummaries(t *testing.T) {
|
|
s := newCalcTable()
|
|
s.Render()
|
|
pdf := s.ExportPDFBytes(AutoTablePDFHeader{Title: "R"})
|
|
|
|
rules := pdfHorizontalRules(pdf)
|
|
if len(rules) < 2 {
|
|
t.Errorf("expected a header rule and a closing rule, got %d distinct rules", len(rules))
|
|
}
|
|
for y, n := range rules {
|
|
if n > 1 {
|
|
t.Errorf("%d rules stacked at y=%s", n, y)
|
|
}
|
|
}
|
|
|
|
// And the lowest rule sits below the last row of text, i.e. the table is closed.
|
|
lowest := 1e9
|
|
for y := range rules {
|
|
if f, err := strconv.ParseFloat(y, 64); err == nil && f < lowest {
|
|
lowest = f
|
|
}
|
|
}
|
|
if lowest > 700 || !strings.Contains(string(pdf), "%%EOF") {
|
|
t.Errorf("closing rule at y=%v looks wrong", lowest)
|
|
}
|
|
}
|
|
|
|
// The extractor must actually see stacked rules, or the two tests above are vacuous.
|
|
func TestPDFRuleExtractorCatchesADoubledRule(t *testing.T) {
|
|
pdf := NewPDF(PDFOptions{})
|
|
pdf.Line(50, 300, 500, 300, 0.5, PDFColor{})
|
|
pdf.Line(50, 300, 500, 300, 1, PDFColor{}) // deliberately stacked
|
|
pdf.Line(50, 200, 500, 200, 0.5, PDFColor{})
|
|
|
|
rules := pdfHorizontalRules(pdf.Bytes())
|
|
doubled := 0
|
|
for _, n := range rules {
|
|
if n > 1 {
|
|
doubled++
|
|
}
|
|
}
|
|
if doubled != 1 {
|
|
t.Fatalf("extractor found %d stacked positions, want exactly 1 — it cannot see the bug it is meant to catch (%v)", doubled, rules)
|
|
}
|
|
}
|