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  }