github.com/pdfcpu/pdfcpu@v0.11.1/pkg/api/annotation.go (about) 1 /* 2 Copyright 2021 The pdfcpu Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package api 18 19 import ( 20 "io" 21 "os" 22 23 "github.com/pdfcpu/pdfcpu/pkg/pdfcpu" 24 "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model" 25 "github.com/pkg/errors" 26 ) 27 28 // Annotations returns page annotations of rs for selected pages. 29 func Annotations(rs io.ReadSeeker, selectedPages []string, conf *model.Configuration) (map[int]model.PgAnnots, error) { 30 if rs == nil { 31 return nil, errors.New("pdfcpu: Annotations: missing rs") 32 } 33 34 if conf == nil { 35 conf = model.NewDefaultConfiguration() 36 } 37 conf.Cmd = model.LISTANNOTATIONS 38 39 ctx, err := ReadValidateAndOptimize(rs, conf) 40 if err != nil { 41 return nil, err 42 } 43 44 pages, err := PagesForPageSelection(ctx.PageCount, selectedPages, true, true) 45 if err != nil { 46 return nil, err 47 } 48 49 return pdfcpu.AnnotationsForSelectedPages(ctx, pages), nil 50 } 51 52 // AddAnnotations adds annotations for selected pages in rs and writes the result to w. 53 func AddAnnotations(rs io.ReadSeeker, w io.Writer, selectedPages []string, ann model.AnnotationRenderer, conf *model.Configuration) error { 54 if rs == nil { 55 return errors.New("pdfcpu: AddAnnotations: missing rs") 56 } 57 58 if conf == nil { 59 conf = model.NewDefaultConfiguration() 60 } 61 conf.Cmd = model.ADDANNOTATIONS 62 63 ctx, err := ReadValidateAndOptimize(rs, conf) 64 if err != nil { 65 return err 66 } 67 68 pages, err := PagesForPageSelection(ctx.PageCount, selectedPages, true, true) 69 if err != nil { 70 return err 71 } 72 73 ok, err := pdfcpu.AddAnnotations(ctx, pages, ann, false) 74 if err != nil { 75 return err 76 } 77 if !ok { 78 return errors.New("pdfcpu: AddAnnotations: No annotations added") 79 } 80 81 return Write(ctx, w, conf) 82 } 83 84 // AddAnnotationsAsIncrement adds annotations for selected pages in rws and writes out a PDF increment. 85 func AddAnnotationsAsIncrement(rws io.ReadWriteSeeker, selectedPages []string, ar model.AnnotationRenderer, conf *model.Configuration) error { 86 if rws == nil { 87 return errors.New("pdfcpu: AddAnnotationsAsIncrement: missing rws") 88 } 89 90 if conf == nil { 91 conf = model.NewDefaultConfiguration() 92 } 93 conf.Cmd = model.ADDANNOTATIONS 94 95 ctx, err := ReadAndValidate(rws, conf) 96 if err != nil { 97 return err 98 } 99 100 if *ctx.HeaderVersion < model.V14 { 101 return errors.New("Incremental writing not supported for PDF version < V1.4 (Hint: Use pdfcpu optimize then try again)") 102 } 103 104 pages, err := PagesForPageSelection(ctx.PageCount, selectedPages, true, true) 105 if err != nil { 106 return err 107 } 108 109 ok, err := pdfcpu.AddAnnotations(ctx, pages, ar, true) 110 if err != nil { 111 return err 112 } 113 if !ok { 114 return errors.New("pdfcpu: AddAnnotationsAsIncrement: No annotations added") 115 } 116 117 return WriteIncr(ctx, rws, conf) 118 } 119 120 // AddAnnotationsFile adds annotations for selected pages to a PDF context read from inFile and writes the result to outFile. 121 func AddAnnotationsFile(inFile, outFile string, selectedPages []string, ar model.AnnotationRenderer, conf *model.Configuration, incr bool) (err error) { 122 tmpFile := inFile + ".tmp" 123 if outFile != "" && inFile != outFile { 124 tmpFile = outFile 125 logWritingTo(outFile) 126 } else { 127 logWritingTo(inFile) 128 if incr { 129 f, err := os.OpenFile(inFile, os.O_RDWR, 0644) 130 if err != nil { 131 return err 132 } 133 defer f.Close() 134 return AddAnnotationsAsIncrement(f, selectedPages, ar, conf) 135 } 136 } 137 138 var f1, f2 *os.File 139 140 if f1, err = os.Open(inFile); err != nil { 141 return err 142 } 143 144 if f2, err = os.Create(tmpFile); err != nil { 145 f1.Close() 146 return err 147 } 148 149 defer func() { 150 if err != nil { 151 f2.Close() 152 f1.Close() 153 os.Remove(tmpFile) 154 return 155 } 156 if err = f2.Close(); err != nil { 157 return 158 } 159 if err = f1.Close(); err != nil { 160 return 161 } 162 if outFile == "" || inFile == outFile { 163 err = os.Rename(tmpFile, inFile) 164 } 165 }() 166 167 return AddAnnotations(f1, f2, selectedPages, ar, conf) 168 } 169 170 // AddAnnotationsMap adds annotations in m to corresponding pages of rs and writes the result to w. 171 func AddAnnotationsMap(rs io.ReadSeeker, w io.Writer, m map[int][]model.AnnotationRenderer, conf *model.Configuration) error { 172 if rs == nil { 173 return errors.New("pdfcpu: AddAnnotationsMap: missing rs") 174 } 175 176 if conf == nil { 177 conf = model.NewDefaultConfiguration() 178 } 179 conf.Cmd = model.ADDANNOTATIONS 180 181 ctx, err := ReadValidateAndOptimize(rs, conf) 182 if err != nil { 183 return err 184 } 185 186 ok, err := pdfcpu.AddAnnotationsMap(ctx, m, false) 187 if err != nil { 188 return err 189 } 190 if !ok { 191 return errors.New("pdfcpu: AddAnnotationsMap: No annotations added") 192 } 193 194 return Write(ctx, w, conf) 195 } 196 197 // AddAnnotationsMapAsIncrement adds annotations in m to corresponding pages of rws and writes out a PDF increment. 198 func AddAnnotationsMapAsIncrement(rws io.ReadWriteSeeker, m map[int][]model.AnnotationRenderer, conf *model.Configuration) error { 199 if rws == nil { 200 return errors.New("pdfcpu: AddAnnotationsMapAsIncrement: missing rws") 201 } 202 203 if conf == nil { 204 conf = model.NewDefaultConfiguration() 205 } 206 conf.Cmd = model.ADDANNOTATIONS 207 208 ctx, err := ReadAndValidate(rws, conf) 209 if err != nil { 210 return err 211 } 212 213 if *ctx.HeaderVersion < model.V14 { 214 return errors.New("Increment writing not supported for PDF version < V1.4 (Hint: Use pdfcpu optimize then try again)") 215 } 216 217 ok, err := pdfcpu.AddAnnotationsMap(ctx, m, true) 218 if err != nil { 219 return err 220 } 221 if !ok { 222 return errors.New("pdfcpu: AddAnnotationsMapAsIncrement: No annotations added") 223 } 224 225 return WriteIncr(ctx, rws, conf) 226 } 227 228 // AddAnnotationsMapFile adds annotations in m to corresponding pages of inFile and writes the result to outFile. 229 func AddAnnotationsMapFile(inFile, outFile string, m map[int][]model.AnnotationRenderer, conf *model.Configuration, incr bool) (err error) { 230 tmpFile := inFile + ".tmp" 231 232 if outFile != "" && inFile != outFile { 233 tmpFile = outFile 234 logWritingTo(outFile) 235 } else { 236 logWritingTo(inFile) 237 if incr { 238 f, err := os.OpenFile(inFile, os.O_RDWR, 0644) 239 if err != nil { 240 return err 241 } 242 defer f.Close() 243 return AddAnnotationsMapAsIncrement(f, m, conf) 244 } 245 } 246 247 var f1, f2 *os.File 248 249 if f1, err = os.Open(inFile); err != nil { 250 return err 251 } 252 253 if f2, err = os.Create(tmpFile); err != nil { 254 f1.Close() 255 return err 256 } 257 258 defer func() { 259 if err != nil { 260 f2.Close() 261 f1.Close() 262 os.Remove(tmpFile) 263 return 264 } 265 if err = f2.Close(); err != nil { 266 return 267 } 268 if err = f1.Close(); err != nil { 269 return 270 } 271 if outFile == "" || inFile == outFile { 272 err = os.Rename(tmpFile, inFile) 273 } 274 }() 275 276 return AddAnnotationsMap(f1, f2, m, conf) 277 } 278 279 // RemoveAnnotations removes annotations for selected pages by id and object number 280 // from a PDF context read from rs and writes the result to w. 281 func RemoveAnnotations(rs io.ReadSeeker, w io.Writer, selectedPages, idsAndTypes []string, objNrs []int, conf *model.Configuration) error { 282 if rs == nil { 283 return errors.New("pdfcpu: RemoveAnnotations: missing rs") 284 } 285 286 if conf == nil { 287 conf = model.NewDefaultConfiguration() 288 } 289 conf.Cmd = model.REMOVEANNOTATIONS 290 291 ctx, err := ReadValidateAndOptimize(rs, conf) 292 if err != nil { 293 return err 294 } 295 296 pages, err := PagesForPageSelection(ctx.PageCount, selectedPages, true, true) 297 if err != nil { 298 return err 299 } 300 301 ok, err := pdfcpu.RemoveAnnotations(ctx, pages, idsAndTypes, objNrs, false) 302 if err != nil { 303 return err 304 } 305 if !ok { 306 return errors.New("pdfcpu: RemoveAnnotations: No annotation removed") 307 } 308 309 return Write(ctx, w, conf) 310 } 311 312 // RemoveAnnotationsAsIncrement removes annotations for selected pages by ids and object number 313 // from a PDF context read from rs and writes out a PDF increment. 314 func RemoveAnnotationsAsIncrement(rws io.ReadWriteSeeker, selectedPages, idsAndTypes []string, objNrs []int, conf *model.Configuration) error { 315 if rws == nil { 316 return errors.New("pdfcpu: RemoveAnnotationsAsIncrement: missing rws") 317 } 318 319 if conf == nil { 320 conf = model.NewDefaultConfiguration() 321 } 322 conf.Cmd = model.REMOVEANNOTATIONS 323 324 ctx, err := ReadAndValidate(rws, conf) 325 if err != nil { 326 return err 327 } 328 329 if *ctx.HeaderVersion < model.V14 { 330 return errors.New("pdfcpu: Incremental writing unsupported for PDF version < V1.4 (Hint: Use pdfcpu optimize then try again)") 331 } 332 333 pages, err := PagesForPageSelection(ctx.PageCount, selectedPages, true, true) 334 if err != nil { 335 return err 336 } 337 338 ok, err := pdfcpu.RemoveAnnotations(ctx, pages, idsAndTypes, objNrs, true) 339 if err != nil { 340 return err 341 } 342 if !ok { 343 return errors.New("pdfcpu: RemoveAnnotationsAsIncrement: No annotation removed") 344 } 345 346 return WriteIncr(ctx, rws, conf) 347 } 348 349 // RemoveAnnotationsFile removes annotations for selected pages by id and object number 350 // from a PDF context read from inFile and writes the result to outFile. 351 func RemoveAnnotationsFile(inFile, outFile string, selectedPages, idsAndTypes []string, objNrs []int, conf *model.Configuration, incr bool) (err error) { 352 var f1, f2 *os.File 353 354 tmpFile := inFile + ".tmp" 355 if outFile != "" && inFile != outFile { 356 tmpFile = outFile 357 logWritingTo(outFile) 358 } else { 359 logWritingTo(inFile) 360 if incr { 361 if f1, err = os.OpenFile(inFile, os.O_RDWR, 0644); err != nil { 362 return err 363 } 364 defer func() { 365 cerr := f1.Close() 366 if err == nil { 367 err = cerr 368 } 369 }() 370 return RemoveAnnotationsAsIncrement(f1, selectedPages, idsAndTypes, objNrs, conf) 371 } 372 } 373 374 if f1, err = os.Open(inFile); err != nil { 375 return err 376 } 377 378 if f2, err = os.Create(tmpFile); err != nil { 379 f1.Close() 380 return err 381 } 382 383 defer func() { 384 if err != nil { 385 f2.Close() 386 f1.Close() 387 os.Remove(tmpFile) 388 return 389 } 390 if err = f2.Close(); err != nil { 391 return 392 } 393 if err = f1.Close(); err != nil { 394 return 395 } 396 if outFile == "" || inFile == outFile { 397 err = os.Rename(tmpFile, inFile) 398 } 399 }() 400 401 return RemoveAnnotations(f1, f2, selectedPages, idsAndTypes, objNrs, conf) 402 }