github.com/pdfcpu/pdfcpu@v0.11.1/pkg/api/bookmark.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  var (
    29  	ErrNoOutlines = errors.New("pdfcpu: no outlines available")
    30  	ErrOutlines   = errors.New("pdfcpu: existing outlines")
    31  )
    32  
    33  // Bookmarks returns rs's bookmark hierarchy.
    34  func Bookmarks(rs io.ReadSeeker, conf *model.Configuration) ([]pdfcpu.Bookmark, error) {
    35  	if rs == nil {
    36  		return nil, errors.New("pdfcpu: Bookmarks: missing rs")
    37  	}
    38  
    39  	if conf == nil {
    40  		conf = model.NewDefaultConfiguration()
    41  	} else {
    42  		conf.ValidationMode = model.ValidationRelaxed
    43  	}
    44  	conf.Cmd = model.LISTBOOKMARKS
    45  
    46  	ctx, err := ReadValidateAndOptimize(rs, conf)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	return pdfcpu.Bookmarks(ctx)
    51  }
    52  
    53  // ExportBookmarksJSON extracts outline data from rs (originating from source) and writes the result to w.
    54  func ExportBookmarksJSON(rs io.ReadSeeker, w io.Writer, source string, conf *model.Configuration) error {
    55  	if rs == nil {
    56  		return errors.New("pdfcpu: ExportBookmarksJSON: missing rs")
    57  	}
    58  
    59  	if w == nil {
    60  		return errors.New("pdfcpu: ExportBookmarksJSON: missing w")
    61  	}
    62  
    63  	if conf == nil {
    64  		conf = model.NewDefaultConfiguration()
    65  	}
    66  	conf.Cmd = model.EXPORTBOOKMARKS
    67  
    68  	ctx, err := ReadValidateAndOptimize(rs, conf)
    69  	if err != nil {
    70  		return err
    71  	}
    72  
    73  	ok, err := pdfcpu.ExportBookmarksJSON(ctx, source, w)
    74  	if err != nil {
    75  		return err
    76  	}
    77  	if !ok {
    78  		return ErrNoOutlines
    79  	}
    80  
    81  	return nil
    82  }
    83  
    84  // ExportBookmarksFile extracts outline data from inFilePDF and writes the result to outFileJSON.
    85  func ExportBookmarksFile(inFilePDF, outFileJSON string, conf *model.Configuration) (err error) {
    86  	var f1, f2 *os.File
    87  
    88  	if f1, err = os.Open(inFilePDF); err != nil {
    89  		return err
    90  	}
    91  
    92  	if f2, err = os.Create(outFileJSON); err != nil {
    93  		f1.Close()
    94  		return err
    95  	}
    96  	logWritingTo(outFileJSON)
    97  
    98  	defer func() {
    99  		if err != nil {
   100  			f2.Close()
   101  			f1.Close()
   102  			return
   103  		}
   104  		if err = f2.Close(); err != nil {
   105  			return
   106  		}
   107  		if err = f1.Close(); err != nil {
   108  			return
   109  		}
   110  	}()
   111  
   112  	return ExportBookmarksJSON(f1, f2, inFilePDF, conf)
   113  }
   114  
   115  // ImportBookmarks creates/replaces outlines in rs and writes the result to w.
   116  func ImportBookmarks(rs io.ReadSeeker, rd io.Reader, w io.Writer, replace bool, conf *model.Configuration) error {
   117  	if rs == nil {
   118  		return errors.New("pdfcpu: ImportBookmarks: missing rs")
   119  	}
   120  
   121  	if rd == nil {
   122  		return errors.New("pdfcpu: ImportBookmarks: missing rd")
   123  	}
   124  
   125  	if conf == nil {
   126  		conf = model.NewDefaultConfiguration()
   127  	} else {
   128  		conf.ValidationMode = model.ValidationRelaxed
   129  	}
   130  	conf.Cmd = model.IMPORTBOOKMARKS
   131  
   132  	ctx, err := ReadValidateAndOptimize(rs, conf)
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	ok, err := pdfcpu.ImportBookmarks(ctx, rd, replace)
   138  	if err != nil {
   139  		return err
   140  	}
   141  	if !ok {
   142  		return ErrOutlines
   143  	}
   144  
   145  	return WriteContext(ctx, w)
   146  }
   147  
   148  // ImportBookmarks creates/replaces outlines in inFilePDF and writes the result to outFilePDF.
   149  func ImportBookmarksFile(inFilePDF, inFileJSON, outFilePDF string, replace bool, conf *model.Configuration) (err error) {
   150  	var f0, f1, f2 *os.File
   151  
   152  	if f0, err = os.Open(inFilePDF); err != nil {
   153  		return err
   154  	}
   155  
   156  	if f1, err = os.Open(inFileJSON); err != nil {
   157  		return err
   158  	}
   159  
   160  	tmpFile := inFilePDF + ".tmp"
   161  	if outFilePDF != "" && inFilePDF != outFilePDF {
   162  		tmpFile = outFilePDF
   163  	}
   164  	if f2, err = os.Create(tmpFile); err != nil {
   165  		f1.Close()
   166  		return err
   167  	}
   168  
   169  	defer func() {
   170  		if err != nil {
   171  			f2.Close()
   172  			f1.Close()
   173  			os.Remove(tmpFile)
   174  			return
   175  		}
   176  		if err = f2.Close(); err != nil {
   177  			return
   178  		}
   179  		if err = f1.Close(); err != nil {
   180  			return
   181  		}
   182  		if outFilePDF == "" || inFilePDF == outFilePDF {
   183  			err = os.Rename(tmpFile, inFilePDF)
   184  		}
   185  	}()
   186  
   187  	return ImportBookmarks(f0, f1, f2, replace, conf)
   188  }
   189  
   190  // AddBookmarks adds a single bookmark outline layer to the PDF context read from rs and writes the result to w.
   191  func AddBookmarks(rs io.ReadSeeker, w io.Writer, bms []pdfcpu.Bookmark, replace bool, conf *model.Configuration) error {
   192  	if rs == nil {
   193  		return errors.New("pdfcpu: AddBookmarks: missing rs")
   194  	}
   195  
   196  	if conf == nil {
   197  		conf = model.NewDefaultConfiguration()
   198  	} else {
   199  		conf.ValidationMode = model.ValidationRelaxed
   200  	}
   201  	conf.Cmd = model.ADDBOOKMARKS
   202  
   203  	if len(bms) == 0 {
   204  		return errors.New("pdfcpu: AddBookmarks: missing bms")
   205  	}
   206  
   207  	ctx, err := ReadValidateAndOptimize(rs, conf)
   208  	if err != nil {
   209  		return err
   210  	}
   211  
   212  	if err := pdfcpu.AddBookmarks(ctx, bms, replace); err != nil {
   213  		return err
   214  	}
   215  
   216  	return WriteContext(ctx, w)
   217  }
   218  
   219  // AddBookmarksFile adds outlines to the PDF context read from inFile and writes the result to outFile.
   220  func AddBookmarksFile(inFile, outFile string, bms []pdfcpu.Bookmark, replace bool, conf *model.Configuration) (err error) {
   221  	var f1, f2 *os.File
   222  
   223  	if f1, err = os.Open(inFile); err != nil {
   224  		return err
   225  	}
   226  
   227  	tmpFile := inFile + ".tmp"
   228  	if outFile != "" && inFile != outFile {
   229  		tmpFile = outFile
   230  	}
   231  	if f2, err = os.Create(tmpFile); err != nil {
   232  		f1.Close()
   233  		return err
   234  	}
   235  
   236  	defer func() {
   237  		if err != nil {
   238  			f2.Close()
   239  			f1.Close()
   240  			os.Remove(tmpFile)
   241  			return
   242  		}
   243  		if err = f2.Close(); err != nil {
   244  			return
   245  		}
   246  		if err = f1.Close(); err != nil {
   247  			return
   248  		}
   249  		if outFile == "" || inFile == outFile {
   250  			err = os.Rename(tmpFile, inFile)
   251  		}
   252  	}()
   253  
   254  	return AddBookmarks(f1, f2, bms, replace, conf)
   255  }
   256  
   257  // RemoveBookmarks deletes outlines from rs and writes the result to w.
   258  func RemoveBookmarks(rs io.ReadSeeker, w io.Writer, conf *model.Configuration) error {
   259  	if rs == nil {
   260  		return errors.New("pdfcpu: AddBookmarks: missing rs")
   261  	}
   262  
   263  	if conf == nil {
   264  		conf = model.NewDefaultConfiguration()
   265  	} else {
   266  		conf.ValidationMode = model.ValidationRelaxed
   267  	}
   268  	conf.Cmd = model.REMOVEBOOKMARKS
   269  
   270  	ctx, err := ReadValidateAndOptimize(rs, conf)
   271  	if err != nil {
   272  		return err
   273  	}
   274  
   275  	ok, err := pdfcpu.RemoveBookmarks(ctx)
   276  	if err != nil {
   277  		return err
   278  	}
   279  	if !ok {
   280  		return ErrNoOutlines
   281  	}
   282  
   283  	return WriteContext(ctx, w)
   284  }
   285  
   286  // RemoveBookmarksFile deletes outlines from inFile and writes the result to outFile.
   287  func RemoveBookmarksFile(inFile, outFile string, conf *model.Configuration) (err error) {
   288  	var f1, f2 *os.File
   289  
   290  	if f1, err = os.Open(inFile); err != nil {
   291  		return err
   292  	}
   293  
   294  	tmpFile := inFile + ".tmp"
   295  	if outFile != "" && inFile != outFile {
   296  		tmpFile = outFile
   297  	}
   298  	if f2, err = os.Create(tmpFile); err != nil {
   299  		f1.Close()
   300  		return err
   301  	}
   302  
   303  	defer func() {
   304  		if err != nil {
   305  			f2.Close()
   306  			f1.Close()
   307  			os.Remove(tmpFile)
   308  			return
   309  		}
   310  		if err = f2.Close(); err != nil {
   311  			return
   312  		}
   313  		if err = f1.Close(); err != nil {
   314  			return
   315  		}
   316  		if outFile == "" || inFile == outFile {
   317  			err = os.Rename(tmpFile, inFile)
   318  		}
   319  	}()
   320  
   321  	return RemoveBookmarks(f1, f2, conf)
   322  }