Skip to content

Commit 7cae81e

Browse files
committed
Fix #819, clean up
1 parent d1433b9 commit 7cae81e

File tree

8 files changed

+177
-89
lines changed

8 files changed

+177
-89
lines changed

pkg/pdfcpu/form/export.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ func exportBtn(
527527
locked bool,
528528
ok *bool) error {
529529

530-
if len(d.ArrayEntry("Kids")) > 0 {
530+
if len(d.ArrayEntry("Kids")) > 1 {
531531

532532
for _, rb := range form.RadioButtonGroups {
533533
if rb.ID == id && rb.Name == name {

pkg/pdfcpu/form/fill.go

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,37 @@ func fillRadioButtonGroup(
541541
return nil
542542
}
543543

544+
func fillCheckBoxKid(ctx *model.Context, kids types.Array, off bool) (*types.Name, error) {
545+
d, err := ctx.DereferenceDict(kids[0])
546+
if err != nil {
547+
return nil, err
548+
}
549+
550+
d1 := d.DictEntry("AP")
551+
if d1 == nil {
552+
return nil, errors.New("pdfcpu: corrupt form field: missing entry AP")
553+
}
554+
555+
d2 := d1.DictEntry("N")
556+
if d2 == nil {
557+
return nil, errors.New("pdfcpu: corrupt AP field: missing entry N")
558+
}
559+
560+
offName, yesName := primitives.CalcCheckBoxASNames(d2)
561+
asName := yesName
562+
if off {
563+
asName = offName
564+
}
565+
566+
if _, found := d.Find("AS"); found {
567+
d["AS"] = asName
568+
}
569+
570+
return &asName, nil
571+
}
572+
544573
func fillCheckBox(
574+
ctx *model.Context,
545575
d types.Dict,
546576
id, name string,
547577
locked bool,
@@ -570,7 +600,7 @@ func fillCheckBox(
570600
vNew := strings.HasPrefix(s, "t")
571601
vOld := false
572602
if o, found := d.Find("V"); found {
573-
vOld = o.(types.Name) == "Yes"
603+
vOld = o.(types.Name) != "Off"
574604
}
575605
if vNew == vOld {
576606
return nil
@@ -580,6 +610,18 @@ func fillCheckBox(
580610
if vNew {
581611
v = types.Name("Yes")
582612
}
613+
614+
kids := d.ArrayEntry("Kids")
615+
if len(kids) == 1 {
616+
asName, err := fillCheckBoxKid(ctx, kids, v == types.Name("Off"))
617+
if err != nil {
618+
return err
619+
}
620+
d["V"] = *asName
621+
*ok = true
622+
return nil
623+
}
624+
583625
d["V"] = v
584626
if _, found := d.Find("AS"); found {
585627
offName, yesName := primitives.CalcCheckBoxASNames(d)
@@ -589,9 +631,9 @@ func fillCheckBox(
589631
asName = offName
590632
}
591633
d["AS"] = asName
634+
d["V"] = asName
592635
}
593636
*ok = true
594-
595637
return nil
596638
}
597639

@@ -609,12 +651,12 @@ func fillBtn(
609651
return nil
610652
}
611653

612-
if len(d.ArrayEntry("Kids")) > 0 {
654+
if len(d.ArrayEntry("Kids")) > 1 {
613655
if err := fillRadioButtonGroup(ctx, d, id, name, locked, format, fillDetails, ok); err != nil {
614656
return err
615657
}
616658
} else {
617-
if err := fillCheckBox(d, id, name, locked, format, fillDetails, ok); err != nil {
659+
if err := fillCheckBox(ctx, d, id, name, locked, format, fillDetails, ok); err != nil {
618660
return err
619661
}
620662
}

pkg/pdfcpu/form/form.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ func collectBtn(xRefTable *model.XRefTable, d types.Dict, f *Field, fm *FieldMet
381381
f.Dv = dv
382382
}
383383

384-
if len(d.ArrayEntry("Kids")) > 0 {
384+
if len(d.ArrayEntry("Kids")) > 1 {
385385
return collectRadioButtonGroup(xRefTable, d, f, fm)
386386
}
387387

pkg/pdfcpu/primitives/dateField.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -485,14 +485,12 @@ func (df *DateField) renderN(xRefTable *model.XRefTable) ([]byte, error) {
485485

486486
// RefreshN updates the normal appearance referred to by indRef according to df.
487487
func (df *DateField) RefreshN(xRefTable *model.XRefTable, indRef *types.IndirectRef) error {
488-
489-
entry, _ := xRefTable.FindTableEntryForIndRef(indRef)
490-
491488
bb, err := df.renderN(xRefTable)
492489
if err != nil {
493490
return err
494491
}
495492

493+
entry, _ := xRefTable.FindTableEntryForIndRef(indRef)
496494
sd, _ := entry.Object.(types.StreamDict)
497495

498496
sd.Content = bb
@@ -936,7 +934,6 @@ func refreshDateFieldAP(ctx *model.Context, d types.Dict, v string, fonts map[st
936934
}
937935

938936
func EnsureDateFieldAP(ctx *model.Context, d types.Dict, v string, fonts map[string]types.IndirectRef) error {
939-
940937
apd := d.DictEntry("AP")
941938
if apd == nil {
942939
return renderDateFieldAP(ctx, d, v, fonts)

pkg/pdfcpu/primitives/textField.go

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,35 @@ func (tf *TextField) renderBackground(w io.Writer, bgCol, boCol *color.SimpleCol
381381
}
382382
}
383383

384+
func (tf *TextField) renderLines(xRefTable *model.XRefTable, boWidth, lh, w, y float64, lines []string, buf io.Writer) {
385+
f := tf.Font
386+
cjk := pdffont.CJK(f.Script, f.Lang)
387+
for i := 0; i < len(lines); i++ {
388+
s := lines[i]
389+
lineBB := model.CalcBoundingBox(s, 0, 0, f.Name, f.Size)
390+
s = model.PrepBytes(xRefTable, s, f.Name, !cjk, f.RTL())
391+
x := 2 * boWidth
392+
if x == 0 {
393+
x = 2
394+
}
395+
switch tf.HorAlign {
396+
case types.AlignCenter:
397+
x = w/2 - lineBB.Width()/2
398+
case types.AlignRight:
399+
x = w - lineBB.Width() - 2
400+
}
401+
fmt.Fprint(buf, "BT ")
402+
if i == 0 {
403+
fmt.Fprintf(buf, "/%s %d Tf %.2f %.2f %.2f RG %.2f %.2f %.2f rg ",
404+
tf.fontID, f.Size,
405+
f.col.R, f.col.G, f.col.B,
406+
f.col.R, f.col.G, f.col.B)
407+
}
408+
fmt.Fprintf(buf, "%.2f %.2f Td (%s) Tj ET ", x, y, s)
409+
y -= lh
410+
}
411+
}
412+
384413
func (tf *TextField) renderN(xRefTable *model.XRefTable) ([]byte, error) {
385414
w, h := tf.BoundingBox.Width(), tf.BoundingBox.Height()
386415
bgCol := tf.BgCol
@@ -417,32 +446,33 @@ func (tf *TextField) renderN(xRefTable *model.XRefTable) ([]byte, error) {
417446
fmt.Fprintf(buf, "q 1 1 %.1f %.1f re W n ", w-2, h-2)
418447
}
419448

420-
cjk := pdffont.CJK(f.Script, f.Lang)
421-
422-
for i := 0; i < len(lines); i++ {
423-
s := lines[i]
424-
lineBB := model.CalcBoundingBox(s, 0, 0, f.Name, f.Size)
425-
s = model.PrepBytes(xRefTable, s, f.Name, !cjk, f.RTL())
426-
x := 2 * boWidth
427-
if x == 0 {
428-
x = 2
429-
}
430-
switch tf.HorAlign {
431-
case types.AlignCenter:
432-
x = w/2 - lineBB.Width()/2
433-
case types.AlignRight:
434-
x = w - lineBB.Width() - 2
435-
}
436-
fmt.Fprint(buf, "BT ")
437-
if i == 0 {
438-
fmt.Fprintf(buf, "/%s %d Tf %.2f %.2f %.2f RG %.2f %.2f %.2f rg ",
439-
tf.fontID, f.Size,
440-
f.col.R, f.col.G, f.col.B,
441-
f.col.R, f.col.G, f.col.B)
442-
}
443-
fmt.Fprintf(buf, "%.2f %.2f Td (%s) Tj ET ", x, y, s)
444-
y -= lh
445-
}
449+
tf.renderLines(xRefTable, boWidth, lh, w, y, lines, buf)
450+
//cjk := pdffont.CJK(f.Script, f.Lang)
451+
452+
// for i := 0; i < len(lines); i++ {
453+
// s := lines[i]
454+
// lineBB := model.CalcBoundingBox(s, 0, 0, f.Name, f.Size)
455+
// s = model.PrepBytes(xRefTable, s, f.Name, !cjk, f.RTL())
456+
// x := 2 * boWidth
457+
// if x == 0 {
458+
// x = 2
459+
// }
460+
// switch tf.HorAlign {
461+
// case types.AlignCenter:
462+
// x = w/2 - lineBB.Width()/2
463+
// case types.AlignRight:
464+
// x = w - lineBB.Width() - 2
465+
// }
466+
// fmt.Fprint(buf, "BT ")
467+
// if i == 0 {
468+
// fmt.Fprintf(buf, "/%s %d Tf %.2f %.2f %.2f RG %.2f %.2f %.2f rg ",
469+
// tf.fontID, f.Size,
470+
// f.col.R, f.col.G, f.col.B,
471+
// f.col.R, f.col.G, f.col.B)
472+
// }
473+
// fmt.Fprintf(buf, "%.2f %.2f Td (%s) Tj ET ", x, y, s)
474+
// y -= lh
475+
// }
446476

447477
if len(lines) > 0 {
448478
fmt.Fprint(buf, "Q ")

pkg/pdfcpu/read.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,19 @@ func showRep() {
13761376
}
13771377
}
13781378

1379+
func processObject(c context.Context, ctx *model.Context, line string, offset *int64) (*bufio.Scanner, error) {
1380+
if err := parseAndLoad(c, ctx, line, offset); err != nil {
1381+
return nil, err
1382+
}
1383+
rd, err := newPositionedReader(ctx.Read.RS, offset)
1384+
if err != nil {
1385+
return nil, err
1386+
}
1387+
s := bufio.NewScanner(rd)
1388+
s.Split(scan.Lines)
1389+
return s, nil
1390+
}
1391+
13791392
// bypassXrefSection is a fix for digesting corrupt xref sections.
13801393
// It populates the xRefTable by reading in all indirect objects line by line
13811394
// and works on the assumption of a single xref section - meaning no incremental updates.
@@ -1445,15 +1458,10 @@ func bypassXrefSection(c context.Context, ctx *model.Context, offExtra int64, wa
14451458
i = strings.Index(line, "obj")
14461459
if i >= 0 {
14471460
if i > 2 && strings.Index(line, "endobj") != i-3 {
1448-
if err := parseAndLoad(c, ctx, line, &offset); err != nil {
1449-
return err
1450-
}
1451-
rd, err = newPositionedReader(ctx.Read.RS, &offset)
1461+
s, err = processObject(c, ctx, line, &offset)
14521462
if err != nil {
14531463
return err
14541464
}
1455-
s = bufio.NewScanner(rd)
1456-
s.Split(scan.Lines)
14571465
continue
14581466
}
14591467
}

pkg/pdfcpu/validate/form.go

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,50 @@ func validateFormEntryDR(xRefTable *model.XRefTable, d types.Dict) error {
528528
return err
529529
}
530530

531+
func validateFormEntries(xRefTable *model.XRefTable, d types.Dict, dictName string, requiresDA bool, sinceVersion model.Version) error {
532+
// NeedAppearances: optional, boolean
533+
_, err := validateBooleanEntry(xRefTable, d, dictName, "NeedAppearances", OPTIONAL, model.V10, nil)
534+
if err != nil {
535+
return err
536+
}
537+
538+
// SigFlags: optional, since 1.3, integer
539+
sinceV := model.V13
540+
if xRefTable.ValidationMode == model.ValidationRelaxed {
541+
sinceV = model.V12
542+
}
543+
sf, err := validateIntegerEntry(xRefTable, d, dictName, "SigFlags", OPTIONAL, sinceV, nil)
544+
if err != nil {
545+
return err
546+
}
547+
if sf != nil {
548+
i := sf.Value()
549+
xRefTable.SignatureExist = i&1 > 0
550+
xRefTable.AppendOnly = i&2 > 0
551+
}
552+
553+
// CO: arra
554+
err = validateFormEntryCO(xRefTable, d, model.V13, requiresDA)
555+
if err != nil {
556+
return err
557+
}
558+
559+
// DR, optional, resource dict
560+
err = validateFormEntryDR(xRefTable, d)
561+
if err != nil {
562+
return err
563+
}
564+
565+
// Q: optional, integer
566+
_, err = validateIntegerEntry(xRefTable, d, dictName, "Q", OPTIONAL, model.V10, validateQ)
567+
if err != nil {
568+
return err
569+
}
570+
571+
// XFA: optional, since 1.5, stream or array
572+
return validateFormXFA(xRefTable, d, sinceVersion)
573+
}
574+
531575
func validateForm(xRefTable *model.XRefTable, rootDict types.Dict, required bool, sinceVersion model.Version) error {
532576

533577
// => 12.7.2 Interactive Form Dictionary
@@ -575,45 +619,5 @@ func validateForm(xRefTable *model.XRefTable, rootDict types.Dict, required bool
575619
return err
576620
}
577621

578-
// NeedAppearances: optional, boolean
579-
_, err = validateBooleanEntry(xRefTable, d, dictName, "NeedAppearances", OPTIONAL, model.V10, nil)
580-
if err != nil {
581-
return err
582-
}
583-
584-
// SigFlags: optional, since 1.3, integer
585-
sinceV := model.V13
586-
if xRefTable.ValidationMode == model.ValidationRelaxed {
587-
sinceV = model.V12
588-
}
589-
sf, err := validateIntegerEntry(xRefTable, d, dictName, "SigFlags", OPTIONAL, sinceV, nil)
590-
if err != nil {
591-
return err
592-
}
593-
if sf != nil {
594-
i := sf.Value()
595-
xRefTable.SignatureExist = i&1 > 0
596-
xRefTable.AppendOnly = i&2 > 0
597-
}
598-
599-
// CO: arra
600-
err = validateFormEntryCO(xRefTable, d, model.V13, requiresDA)
601-
if err != nil {
602-
return err
603-
}
604-
605-
// DR, optional, resource dict
606-
err = validateFormEntryDR(xRefTable, d)
607-
if err != nil {
608-
return err
609-
}
610-
611-
// Q: optional, integer
612-
_, err = validateIntegerEntry(xRefTable, d, dictName, "Q", OPTIONAL, model.V10, validateQ)
613-
if err != nil {
614-
return err
615-
}
616-
617-
// XFA: optional, since 1.5, stream or array
618-
return validateFormXFA(xRefTable, d, sinceVersion)
622+
return validateFormEntries(xRefTable, d, dictName, requiresDA, sinceVersion)
619623
}

pkg/pdfcpu/validate/page.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,17 @@ func pagesDictKids(xRefTable *model.XRefTable, d types.Dict) types.Array {
954954
return kids
955955
}
956956

957+
func validateParent(pageNodeDict types.Dict, objNr int) error {
958+
parentIndRef := pageNodeDict.IndirectRefEntry("Parent")
959+
if parentIndRef == nil {
960+
return errors.New("pdfcpu: validatePagesDict: missing parent node")
961+
}
962+
if parentIndRef.ObjectNumber.Value() != objNr {
963+
return errors.New("pdfcpu: validatePagesDict: corrupt parent node")
964+
}
965+
return nil
966+
}
967+
957968
func processPagesKids(xRefTable *model.XRefTable, kids types.Array, objNr int, hasResources, hasMediaBox bool, curPage *int) (types.Array, error) {
958969
var a types.Array
959970

@@ -987,12 +998,8 @@ func processPagesKids(xRefTable *model.XRefTable, kids types.Array, objNr int, h
987998
return nil, errors.New("pdfcpu: validatePagesDict: corrupt page node")
988999
}
9891000

990-
parentIndRef := pageNodeDict.IndirectRefEntry("Parent")
991-
if parentIndRef == nil {
992-
return nil, errors.New("pdfcpu: validatePagesDict: missing parent node")
993-
}
994-
if parentIndRef.ObjectNumber.Value() != objNr {
995-
return nil, errors.New("pdfcpu: validatePagesDict: corrupt parent node")
1001+
if err := validateParent(pageNodeDict, objNr); err != nil {
1002+
return nil, err
9961003
}
9971004

9981005
dictType, err := dictTypeForPageNodeDict(pageNodeDict)

0 commit comments

Comments
 (0)