Fix writeXrefTable to handle PDFs with multiple revisions
- Refactor xref table writing logic to correctly parse existing entries - Add support for variable first object ID in xref table - Implement dynamic calculation of new xref entries - Improve error handling and reporting This change resolves issue #4 with malformed xref tables when signing PDFs that have undergone multiple revisions.
This commit is contained in:

committed by
Paul van Brouwershaven

parent
e6e59033a0
commit
a0da9873d4
@@ -6,7 +6,9 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (context *SignContext) writeXref() error {
|
func (context *SignContext) writeXref() error {
|
||||||
@@ -27,53 +29,64 @@ func (context *SignContext) writeXref() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (context *SignContext) writeXrefTable() error {
|
func (context *SignContext) writeXrefTable() error {
|
||||||
// @todo: maybe we need a prev here too.
|
// Read the existing xref table
|
||||||
xref_size := "xref\n0 " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount, 10) + "\n"
|
context.InputFile.Seek(context.PDFReader.XrefInformation.StartPos, 0)
|
||||||
new_xref_size := "xref\n0 " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount+4, 10) + "\n"
|
xrefContent := make([]byte, context.PDFReader.XrefInformation.Length)
|
||||||
|
_, err := context.InputFile.Read(xrefContent)
|
||||||
if _, err := context.OutputBuffer.Write([]byte(new_xref_size)); err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to read xref table: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the old xref table to the output pdf.
|
// Parse the xref header
|
||||||
if err := writePartFromSourceFileToTargetFile(context.InputFile, context.OutputBuffer, context.PDFReader.XrefInformation.StartPos+int64(len(xref_size)), context.PDFReader.XrefInformation.Length-int64(len(xref_size))); err != nil {
|
xrefLines := strings.Split(string(xrefContent), "\n")
|
||||||
return err
|
xrefHeader := strings.Fields(xrefLines[1])
|
||||||
|
if len(xrefHeader) != 2 {
|
||||||
|
return fmt.Errorf("invalid xref header format")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the new catalog xref line.
|
firstObjectID, err := strconv.Atoi(xrefHeader[0])
|
||||||
visual_signature_object_start_position := strconv.FormatInt(context.Filesize, 10)
|
if err != nil {
|
||||||
visual_signature_xref_line := leftPad(visual_signature_object_start_position, "0", 10-len(visual_signature_object_start_position)) + " 00000 n \n"
|
return fmt.Errorf("invalid first object ID: %w", err)
|
||||||
|
|
||||||
// Write the new catalog xref line.
|
|
||||||
if _, err := context.OutputBuffer.Write([]byte(visual_signature_xref_line)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the new catalog xref line.
|
itemCount, err := strconv.Atoi(xrefHeader[1])
|
||||||
catalog_object_start_position := strconv.FormatInt(context.Filesize+context.VisualSignData.Length, 10)
|
if err != nil {
|
||||||
catalog_xref_line := leftPad(catalog_object_start_position, "0", 10-len(catalog_object_start_position)) + " 00000 n \n"
|
return fmt.Errorf("invalid item count: %w", err)
|
||||||
|
|
||||||
// Write the new catalog xref line.
|
|
||||||
if _, err := context.OutputBuffer.Write([]byte(catalog_xref_line)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the new signature xref line.
|
// Calculate new entries
|
||||||
info_object_start_position := strconv.FormatInt(context.Filesize+context.VisualSignData.Length+context.CatalogData.Length, 10)
|
newEntries := []struct {
|
||||||
info_xref_line := leftPad(info_object_start_position, "0", 10-len(info_object_start_position)) + " 00000 n \n"
|
startPosition int64
|
||||||
|
name string
|
||||||
// Write the new signature xref line.
|
}{
|
||||||
if _, err := context.OutputBuffer.Write([]byte(info_xref_line)); err != nil {
|
{context.Filesize, "visual signature"},
|
||||||
return err
|
{context.Filesize + context.VisualSignData.Length, "catalog"},
|
||||||
|
{context.Filesize + context.VisualSignData.Length + context.CatalogData.Length, "info"},
|
||||||
|
{context.Filesize + context.VisualSignData.Length + context.CatalogData.Length + context.InfoData.Length, "signature"},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the new signature xref line.
|
// Write new xref table
|
||||||
signature_object_start_position := strconv.FormatInt(context.Filesize+context.VisualSignData.Length+context.CatalogData.Length+context.InfoData.Length, 10)
|
newXrefHeader := fmt.Sprintf("xref\n%d %d\n", firstObjectID, itemCount+len(newEntries))
|
||||||
signature_xref_line := leftPad(signature_object_start_position, "0", 10-len(signature_object_start_position)) + " 00000 n \n"
|
if _, err := context.OutputBuffer.Write([]byte(newXrefHeader)); err != nil {
|
||||||
|
return fmt.Errorf("failed to write new xref header: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Write the new signature xref line.
|
// Write existing entries
|
||||||
if _, err := context.OutputBuffer.Write([]byte(signature_xref_line)); err != nil {
|
for i, line := range xrefLines[2:] {
|
||||||
return err
|
if i >= itemCount {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if _, err := context.OutputBuffer.Write([]byte(line + "\n")); err != nil {
|
||||||
|
return fmt.Errorf("failed to write existing xref entry: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write new entries
|
||||||
|
for _, entry := range newEntries {
|
||||||
|
xrefLine := fmt.Sprintf("%010d 00000 n \n", entry.startPosition)
|
||||||
|
if _, err := context.OutputBuffer.Write([]byte(xrefLine)); err != nil {
|
||||||
|
return fmt.Errorf("failed to write new xref entry for %s: %w", entry.name, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
BIN
testfiles/testfile16.pdf
Normal file
BIN
testfiles/testfile16.pdf
Normal file
Binary file not shown.
Reference in New Issue
Block a user