From d3dbbaa81bdbb60fc1c6d327e9e0271a78831e49 Mon Sep 17 00:00:00 2001 From: Jeroen Bobbeldijk Date: Sun, 10 Sep 2017 17:26:01 +0200 Subject: [PATCH] Xref stream progress --- sign/pdftrailer.go | 49 ++++++++++++++------------- sign/pdfxref.go | 82 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 106 insertions(+), 25 deletions(-) diff --git a/sign/pdftrailer.go b/sign/pdftrailer.go index e73b792..c5c5dc4 100644 --- a/sign/pdftrailer.go +++ b/sign/pdftrailer.go @@ -6,39 +6,42 @@ import ( ) func (context *SignContext) writeTrailer() error { - trailer_length := context.PDFReader.XrefInformation.IncludingTrailerEndPos - context.PDFReader.XrefInformation.EndPos - // Read the trailer so we can replace the size. - context.InputFile.Seek(context.PDFReader.XrefInformation.EndPos+1, 0) - trailer_buf := make([]byte, trailer_length) - if _, err := context.InputFile.Read(trailer_buf); err != nil { - return err - } + if context.PDFReader.XrefInformation.Type == "table" { + trailer_length := context.PDFReader.XrefInformation.IncludingTrailerEndPos - context.PDFReader.XrefInformation.EndPos - root_string := "Root " + context.CatalogData.RootString - new_root := "Root " + strconv.FormatInt(int64(context.CatalogData.ObjectId), 10) + " 0 R" + // Read the trailer so we can replace the size. + context.InputFile.Seek(context.PDFReader.XrefInformation.EndPos+1, 0) + trailer_buf := make([]byte, trailer_length) + if _, err := context.InputFile.Read(trailer_buf); err != nil { + return err + } - size_string := "Size " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount, 10) - new_size := "Size " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount+4, 10) + root_string := "Root " + context.CatalogData.RootString + new_root := "Root " + strconv.FormatInt(int64(context.CatalogData.ObjectId), 10) + " 0 R" - info := context.PDFReader.Trailer().Key("Info") - infoPtr := info.GetPtr() + size_string := "Size " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount, 10) + new_size := "Size " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount+4, 10) - info_string := "Info " + strconv.Itoa(int(infoPtr.GetID())) + " " + strconv.Itoa(int(infoPtr.GetGen())) + " R" - new_info := "Info " + strconv.FormatInt(int64(context.InfoData.ObjectId), 10) + " 0 R" + info := context.PDFReader.Trailer().Key("Info") + infoPtr := info.GetPtr() - trailer_string := string(trailer_buf) - trailer_string = strings.Replace(trailer_string, root_string, new_root, -1) - trailer_string = strings.Replace(trailer_string, size_string, new_size, -1) - trailer_string = strings.Replace(trailer_string, info_string, new_info, -1) + info_string := "Info " + strconv.Itoa(int(infoPtr.GetID())) + " " + strconv.Itoa(int(infoPtr.GetGen())) + " R" + new_info := "Info " + strconv.FormatInt(int64(context.InfoData.ObjectId), 10) + " 0 R" - // Write the new trailer. - if _, err := context.OutputFile.Write([]byte(trailer_string)); err != nil { - return err + trailer_string := string(trailer_buf) + trailer_string = strings.Replace(trailer_string, root_string, new_root, -1) + trailer_string = strings.Replace(trailer_string, size_string, new_size, -1) + trailer_string = strings.Replace(trailer_string, info_string, new_info, -1) + + // Write the new trailer. + if _, err := context.OutputFile.Write([]byte(trailer_string)); err != nil { + return err + } } // Write the new xref start position. - if _, err := context.OutputFile.Write([]byte(strconv.FormatInt(context.NewXrefStart, 10) + "\n")); err != nil { + if _, err := context.OutputFile.Write([]byte("\nstartxref\n" + strconv.FormatInt(context.NewXrefStart, 10) + "\n")); err != nil { return err } diff --git a/sign/pdfxref.go b/sign/pdfxref.go index 566e6cb..3df8f38 100644 --- a/sign/pdfxref.go +++ b/sign/pdfxref.go @@ -3,16 +3,21 @@ package sign import ( "errors" "strconv" + "encoding/hex" + "compress/zlib" + "bytes" ) func (context *SignContext) writeXref() error { - // @todo: support stream xref. - if context.PDFReader.XrefInformation.Type == "table" { if err := context.writeXrefTable(); err != nil { return err } + } else if context.PDFReader.XrefInformation.Type == "stream" { + if err := context.writeXrefStream(); err != nil { + return err + } } else { return errors.New("Unkwn xref type: " + context.PDFReader.XrefInformation.Type) } @@ -71,3 +76,76 @@ func (context *SignContext) writeXrefTable() error { return nil } + +func (context *SignContext) writeXrefStream() error { + // @todo: DO NOT FORGET TO ADD SELF REFERENCE!!! + // @todo: fix format (columns) for stream + // @todo: add PNG-Up support. + var streamBytes bytes.Buffer + w := zlib.NewWriter(&streamBytes) + + // Create the new catalog xref line. + visual_signature_object_start_position := strconv.FormatInt(context.Filesize, 10) + visual_signature_xref_line := leftPad(visual_signature_object_start_position, "0", 10-len(visual_signature_object_start_position)) + " 00000 n \n" + + // Write the new catalog xref line. + if _, err := w.Write([]byte(visual_signature_xref_line)); err != nil { + return err + } + + // Create the new catalog xref line. + catalog_object_start_position := strconv.FormatInt(context.Filesize+context.VisualSignData.Length, 10) + catalog_xref_line := leftPad(catalog_object_start_position, "0", 10-len(catalog_object_start_position)) + " 00000 n \n" + + // Write the new catalog xref line. + if _, err := w.Write([]byte(catalog_xref_line)); err != nil { + return err + } + + // Create the new signature xref line. + info_object_start_position := strconv.FormatInt(context.Filesize+context.VisualSignData.Length+context.CatalogData.Length, 10) + info_xref_line := leftPad(info_object_start_position, "0", 10-len(info_object_start_position)) + " 00000 n \n" + + // Write the new signature xref line. + if _, err := w.Write([]byte(info_xref_line)); err != nil { + return err + } + + // Create the new signature xref line. + signature_object_start_position := strconv.FormatInt(context.Filesize+context.VisualSignData.Length+context.CatalogData.Length+context.InfoData.Length, 10) + signature_xref_line := leftPad(signature_object_start_position, "0", 10-len(signature_object_start_position)) + " 00000 n \n" + + // Write the new signature xref line. + if _, err := w.Write([]byte(signature_xref_line)); err != nil { + return err + } + w.Close() + + new_info := "Info " + strconv.FormatInt(int64(context.InfoData.ObjectId), 10) + " 0 R" + new_root := "Root " + strconv.FormatInt(int64(context.CatalogData.ObjectId), 10) + " 0 R" + + id := context.PDFReader.Trailer().Key("ID") + + id0 := hex.EncodeToString([]byte(id.Index(0).RawString())) + id1 := hex.EncodeToString([]byte(id.Index(0).RawString())) + + new_xref := strconv.Itoa(int(context.SignData.ObjectId + 1)) + " 0 obj\n" + new_xref += "<< /Type /XRef /Length " + strconv.Itoa(len(streamBytes.Bytes())) + " /Filter /FlateDecode /DecodeParms << /Columns 5 /Predictor 12 >> /W [ 1 3 1 ] /Prev " + strconv.FormatInt(context.PDFReader.XrefInformation.StartPos, 10) + " /Size " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount+5, 10) + " /Index [ 5 " + strconv.FormatInt(context.PDFReader.XrefInformation.ItemCount, 10) + " ] /" + new_info + " /" + new_root + " /ID [<" + id0 + "><" + id1 + ">] >>\n" + if _, err := context.OutputFile.Write([]byte(new_xref)); err != nil { + return err + } + + if _, err := context.OutputFile.Write([]byte("stream\n")); err != nil { + return err + } + + if _, err := context.OutputFile.Write(streamBytes.Bytes()); err != nil { + return err + } + + if _, err := context.OutputFile.Write([]byte("\nendstream\n")); err != nil { + return err + } + + return nil +}