@@ -23,6 +23,7 @@ import (
2323 "strings"
2424
2525 "github.com/pdfcpu/pdfcpu/pkg/log"
26+ "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/draw"
2627 "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model"
2728 "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types"
2829 "github.com/pkg/errors"
@@ -122,12 +123,16 @@ func findAnnotByObjNr(objNr int, annots types.Array) (int, error) {
122123 return - 1 , nil
123124}
124125
125- func createAnnot (ctx * model.Context , ar model.AnnotationRenderer , pageIndRef * types.IndirectRef ) (* types.IndirectRef , error ) {
126- d , err := ar .RenderDict (ctx .XRefTable , * pageIndRef )
126+ func createAnnot (ctx * model.Context , ar model.AnnotationRenderer , pageIndRef * types.IndirectRef ) (* types.IndirectRef , types. Dict , error ) {
127+ d , err := ar .RenderDict (ctx .XRefTable , pageIndRef )
127128 if err != nil {
128- return nil , err
129+ return nil , nil , err
130+ }
131+ indRef , err := ctx .IndRefForNewObject (d )
132+ if err != nil {
133+ return nil , nil , err
129134 }
130- return ctx . IndRefForNewObject ( d )
135+ return indRef , d , nil
131136}
132137
133138// Annotation returns an annotation renderer.
@@ -147,11 +152,13 @@ func Annotation(xRefTable *model.XRefTable, d types.Dict) (model.AnnotationRende
147152 return nil , err
148153 }
149154
150- bb , err := d .StringEntryBytes ("Contents" )
151- if err != nil {
152- return nil , err
155+ contents := ""
156+ if c , ok := d ["Contents" ]; ok {
157+ contents , err = xRefTable .DereferenceStringOrHexLiteral (c , model .V10 , nil )
158+ if err != nil {
159+ return nil , err
160+ }
153161 }
154- contents := string (bb )
155162
156163 var nm string
157164 s := d .StringEntry ("NM" ) // This is what pdfcpu refers to as the annotation id.
@@ -170,7 +177,8 @@ func Annotation(xRefTable *model.XRefTable, d types.Dict) (model.AnnotationRende
170177 switch * subtype {
171178
172179 case "Text" :
173- ann = model .NewTextAnnotation (* r , contents , nm , "" , f , nil , nil , "" , "" , true , "" )
180+ popupIndRef := d .IndirectRefEntry ("Popup" )
181+ ann = model .NewTextAnnotation (* r , contents , nm , "" , f , nil , "" , popupIndRef , nil , "" , "" , true , "" )
174182
175183 case "Link" :
176184 var uri string
@@ -190,16 +198,17 @@ func Annotation(xRefTable *model.XRefTable, d types.Dict) (model.AnnotationRende
190198 }
191199 }
192200 dest := (* model .Destination )(nil ) // will not collect link dest during validation.
193- ann = model .NewLinkAnnotation (* r , nil , dest , uri , nm , f , 0 , model .BSSolid , nil , false )
201+ ann = model .NewLinkAnnotation (* r , contents , nm , "" , f , nil , dest , uri , nil , false , 0 , model .BSSolid )
194202
195203 case "Popup" :
196204 parentIndRef := d .IndirectRefEntry ("Parent" )
197- ann = model .NewPopupAnnotation (* r , nil , contents , nm , f , nil , parentIndRef )
205+ ann = model .NewPopupAnnotation (* r , contents , nm , "" , f , nil , parentIndRef , false )
198206
199207 // TODO handle remaining annotation types.
200208
201209 default :
202- ann = model .NewAnnotationForRawType (* subtype , * r , contents , nil , nm , f , nil )
210+ ann = model .NewAnnotationForRawType (* subtype , * r , contents , nm , "" , f , nil )
211+
203212 }
204213
205214 return ann , nil
@@ -234,6 +243,46 @@ func AnnotationsForSelectedPages(ctx *model.Context, selectedPages types.IntSet)
234243 return m
235244}
236245
246+ func prepareHeader (horSep * []int , maxLen * AnnotListMaxLengths ) string {
247+ s := " Obj# "
248+ if maxLen .ObjNr > 4 {
249+ s += strings .Repeat (" " , maxLen .ObjNr - 4 )
250+ * horSep = append (* horSep , 10 + maxLen .ObjNr - 4 )
251+ } else {
252+ * horSep = append (* horSep , 10 )
253+ }
254+
255+ s += draw .VBar + " Id "
256+ if maxLen .ID > 2 {
257+ s += strings .Repeat (" " , maxLen .ID - 2 )
258+ * horSep = append (* horSep , 4 + maxLen .ID - 2 )
259+ } else {
260+ * horSep = append (* horSep , 4 )
261+ }
262+
263+ s += draw .VBar + " Rect "
264+ if maxLen .Rect > 4 {
265+ s += strings .Repeat (" " , maxLen .Rect - 4 )
266+ * horSep = append (* horSep , 6 + maxLen .Rect - 4 )
267+ } else {
268+ * horSep = append (* horSep , 6 )
269+ }
270+
271+ s += draw .VBar + " Content"
272+ if maxLen .Content > 7 {
273+ s += strings .Repeat (" " , maxLen .Rect - 7 )
274+ * horSep = append (* horSep , 8 + maxLen .Rect - 7 )
275+ } else {
276+ * horSep = append (* horSep , 8 )
277+ }
278+
279+ return s
280+ }
281+
282+ type AnnotListMaxLengths struct {
283+ ObjNr , ID , Rect , Content int
284+ }
285+
237286// ListAnnotations returns a formatted list of annotations.
238287func ListAnnotations (annots map [int ]model.PgAnnots ) (int , []string , error ) {
239288 var (
@@ -262,37 +311,62 @@ func ListAnnotations(annots map[int]model.PgAnnots) (int, []string, error) {
262311
263312 for _ , annType := range annTypes {
264313 annots := pageAnnots [model.AnnotTypes [annType ]]
265- var (
266- maxLenRect int
267- maxLenContent int
268- )
269- maxLenID := 2
314+
315+ var maxLen AnnotListMaxLengths
316+ maxLen . ID = 2
317+ maxLen . Content = len ( "Content" )
318+
270319 var objNrs []int
271320 for objNr , ann := range annots .Map {
272321 objNrs = append (objNrs , objNr )
273- if len (ann .RectString ()) > maxLenRect {
274- maxLenRect = len (ann .RectString ())
322+ s := strconv .Itoa (objNr )
323+ if len (s ) > maxLen .ObjNr {
324+ maxLen .ObjNr = len (s )
325+ }
326+ if len (ann .RectString ()) > maxLen .Rect {
327+ maxLen .Rect = len (ann .RectString ())
275328 }
276- if len (ann .ID ()) > maxLenID {
277- maxLenID = len (ann .ID ())
329+ if len (ann .ID ()) > maxLen . ID {
330+ maxLen . ID = len (ann .ID ())
278331 }
279- if len (ann .ContentString ()) > maxLenContent {
280- maxLenContent = len (ann .ContentString ())
332+ if len (ann .ContentString ()) > maxLen . Content {
333+ maxLen . Content = len (ann .ContentString ())
281334 }
282335 }
283336 sort .Ints (objNrs )
284337 ss = append (ss , "" )
285338 ss = append (ss , fmt .Sprintf (" %s:" , annType ))
286- s1 := (" obj# " )
287- s2 := fmt .Sprintf ("%%%ds" , maxLenRect )
288- s3 := fmt .Sprintf ("%%%ds" , maxLenID )
289- s4 := fmt .Sprintf ("%%%ds" , maxLenContent )
290- s := fmt .Sprintf (s1 + s2 + " " + s3 + " " + s4 , "rect" , "id" , "content" )
291- ss = append (ss , s )
292- ss = append (ss , " " + strings .Repeat ("=" , len (s )- 4 ))
339+
340+ horSep := []int {}
341+
342+ // Render header.
343+ ss = append (ss , prepareHeader (& horSep , & maxLen ))
344+
345+ // Render separator.
346+ ss = append (ss , draw .HorSepLine (horSep ))
347+
348+ // Render content.
293349 for _ , objNr := range objNrs {
294350 ann := annots .Map [objNr ]
295- ss = append (ss , fmt .Sprintf (" %5d " + s2 + " " + s3 + " " + s4 , objNr , ann .RectString (), ann .ID (), ann .ContentString ()))
351+
352+ s := strconv .Itoa (objNr )
353+ fill1 := strings .Repeat (" " , maxLen .ObjNr - len (s ))
354+ if maxLen .ObjNr < 4 {
355+ fill1 += strings .Repeat (" " , 4 - maxLen .ObjNr )
356+ }
357+
358+ s = ann .ID ()
359+ fill2 := strings .Repeat (" " , maxLen .ID - len (s ))
360+ if maxLen .ID < 2 {
361+ fill2 += strings .Repeat (" " , 2 - maxLen .ID )
362+ }
363+
364+ s = ann .RectString ()
365+ fill3 := strings .Repeat (" " , maxLen .Rect - len (s ))
366+
367+ ss = append (ss , fmt .Sprintf (" %s%d %s %s%s %s %s%s %s %s" ,
368+ fill1 , objNr , draw .VBar , fill2 , ann .ID (), draw .VBar , fill3 , ann .RectString (), draw .VBar , ann .ContentString ()))
369+
296370 j ++
297371 }
298372 }
@@ -308,22 +382,22 @@ func addAnnotationToDirectObj(
308382 pageDict types.Dict ,
309383 pageNr int ,
310384 ar model.AnnotationRenderer ,
311- incr bool ) ( bool , error ) {
385+ incr bool ) error {
312386
313387 i , err := findAnnotByID (ctx , ar .ID (), annots )
314388 if err != nil {
315- return false , err
389+ return err
316390 }
317391 if i >= 0 {
318- return false , errors .Errorf ("page %d: duplicate annotation with id:%s\n " , pageNr , ar .ID ())
392+ return errors .Errorf ("page %d: duplicate annotation with id:%s\n " , pageNr , ar .ID ())
319393 }
320394 pageDict .Update ("Annots" , append (annots , * annotIndRef ))
321395 if incr {
322396 // Mark page dict obj for incremental writing.
323397 ctx .Write .IncrementWithObjNr (pageDictIndRef .ObjectNumber .Value ())
324398 }
325399 ctx .EnsureVersionForWriting ()
326- return true , nil
400+ return nil
327401}
328402
329403// AddAnnotation adds ar to pageDict.
@@ -333,18 +407,18 @@ func AddAnnotation(
333407 pageDict types.Dict ,
334408 pageNr int ,
335409 ar model.AnnotationRenderer ,
336- incr bool ) (bool , error ) {
410+ incr bool ) (* types. IndirectRef , types. Dict , error ) {
337411
338412 // Create xreftable entry for annotation.
339- annotIndRef , err := createAnnot (ctx , ar , pageDictIndRef )
413+ annotIndRef , d , err := createAnnot (ctx , ar , pageDictIndRef )
340414 if err != nil {
341- return false , err
415+ return nil , nil , err
342416 }
343417
344418 // Add annotation to xreftable page annotation cache.
345419 err = addAnnotationToCache (ctx , ar , pageNr , annotIndRef .ObjectNumber .Value ())
346420 if err != nil {
347- return false , err
421+ return nil , nil , err
348422 }
349423
350424 if incr {
@@ -360,33 +434,33 @@ func AddAnnotation(
360434 ctx .Write .IncrementWithObjNr (pageDictIndRef .ObjectNumber .Value ())
361435 }
362436 ctx .EnsureVersionForWriting ()
363- return true , nil
437+ return annotIndRef , d , nil
364438 }
365439
366440 ir , ok := obj .(types.IndirectRef )
367441 if ! ok {
368- return addAnnotationToDirectObj (ctx , obj .(types.Array ), annotIndRef , pageDictIndRef , pageDict , pageNr , ar , incr )
442+ return annotIndRef , d , addAnnotationToDirectObj (ctx , obj .(types.Array ), annotIndRef , pageDictIndRef , pageDict , pageNr , ar , incr )
369443 }
370444
371445 // Annots array is an IndirectReference.
372446
373447 o , err := ctx .Dereference (ir )
374448 if err != nil || o == nil {
375- return false , err
449+ return nil , nil , err
376450 }
377451
378452 annots , _ := o .(types.Array )
379453 i , err := findAnnotByID (ctx , ar .ID (), annots )
380454 if err != nil {
381- return false , err
455+ return nil , nil , err
382456 }
383457 if i >= 0 {
384- return false , errors .Errorf ("page %d: duplicate annotation with id:%s\n " , pageNr , ar .ID ())
458+ return nil , nil , errors .Errorf ("page %d: duplicate annotation with id:%s\n " , pageNr , ar .ID ())
385459 }
386460
387461 entry , ok := ctx .FindTableEntryForIndRef (& ir )
388462 if ! ok {
389- return false , errors .Errorf ("page %d: can't dereference Annots indirect reference(obj#:%d)\n " , pageNr , ir .ObjectNumber )
463+ return nil , nil , errors .Errorf ("page %d: can't dereference Annots indirect reference(obj#:%d)\n " , pageNr , ir .ObjectNumber )
390464 }
391465 entry .Object = append (annots , * annotIndRef )
392466 if incr {
@@ -395,7 +469,21 @@ func AddAnnotation(
395469 }
396470
397471 ctx .EnsureVersionForWriting ()
398- return true , nil
472+ return annotIndRef , d , nil
473+ }
474+
475+ func AddAnnotationToPage (ctx * model.Context , pageNr int , ar model.AnnotationRenderer , incr bool ) (* types.IndirectRef , types.Dict , error ) {
476+ pageDictIndRef , err := ctx .PageDictIndRef (pageNr )
477+ if err != nil {
478+ return nil , nil , err
479+ }
480+
481+ d , err := ctx .DereferenceDict (* pageDictIndRef )
482+ if err != nil {
483+ return nil , nil , err
484+ }
485+
486+ return AddAnnotation (ctx , pageDictIndRef , d , pageNr , ar , incr )
399487}
400488
401489// AddAnnotations adds ar to selected pages.
@@ -424,11 +512,11 @@ func AddAnnotations(ctx *model.Context, selectedPages types.IntSet, ar model.Ann
424512 return false , err
425513 }
426514
427- added , err := AddAnnotation (ctx , pageDictIndRef , d , k , ar , incr )
515+ indRef , _ , err := AddAnnotation (ctx , pageDictIndRef , d , k , ar , incr )
428516 if err != nil {
429517 return false , err
430518 }
431- if added {
519+ if indRef != nil {
432520 ok = true
433521 }
434522 }
@@ -460,11 +548,11 @@ func AddAnnotationsMap(ctx *model.Context, m map[int][]model.AnnotationRenderer,
460548 }
461549
462550 for _ , annot := range annots {
463- added , err := AddAnnotation (ctx , pageDictIndRef , d , i , annot , incr )
551+ indRef , _ , err := AddAnnotation (ctx , pageDictIndRef , d , i , annot , incr )
464552 if err != nil {
465553 return false , err
466554 }
467- if added {
555+ if indRef != nil {
468556 ok = true
469557 }
470558 }
0 commit comments