github.com/pdfcpu/pdfcpu@v0.11.1/pkg/api/page.go (about)

     1  /*
     2  	Copyright 2020 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  	"sort"
    23  
    24  	"github.com/pdfcpu/pdfcpu/pkg/log"
    25  	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu"
    26  	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model"
    27  	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types"
    28  	"github.com/pkg/errors"
    29  )
    30  
    31  // InsertPages inserts a blank page before or after every page selected of rs and writes the result to w.
    32  func InsertPages(rs io.ReadSeeker, w io.Writer, selectedPages []string, before bool, pageConf *pdfcpu.PageConfiguration, conf *model.Configuration) error {
    33  	if rs == nil {
    34  		return errors.New("pdfcpu: InsertPages: missing rs")
    35  	}
    36  
    37  	if conf == nil {
    38  		conf = model.NewDefaultConfiguration()
    39  	}
    40  	conf.Cmd = model.INSERTPAGESAFTER
    41  	if before {
    42  		conf.Cmd = model.INSERTPAGESBEFORE
    43  	}
    44  
    45  	ctx, err := ReadValidateAndOptimize(rs, conf)
    46  	if err != nil {
    47  		return err
    48  	}
    49  
    50  	pages, err := PagesForPageSelection(ctx.PageCount, selectedPages, true, true)
    51  	if err != nil {
    52  		return err
    53  	}
    54  
    55  	var dim *types.Dim
    56  	if pageConf != nil {
    57  		dim = pageConf.PageDim
    58  	}
    59  
    60  	if err = ctx.InsertBlankPages(pages, dim, before); err != nil {
    61  		return err
    62  	}
    63  
    64  	return Write(ctx, w, conf)
    65  }
    66  
    67  // InsertPagesFile inserts a blank page before or after every inFile page selected and writes the result to w.
    68  func InsertPagesFile(inFile, outFile string, selectedPages []string, before bool, pageConf *pdfcpu.PageConfiguration, conf *model.Configuration) (err error) {
    69  	var f1, f2 *os.File
    70  
    71  	if f1, err = os.Open(inFile); err != nil {
    72  		return err
    73  	}
    74  
    75  	tmpFile := inFile + ".tmp"
    76  	if outFile != "" && inFile != outFile {
    77  		tmpFile = outFile
    78  		logWritingTo(outFile)
    79  	} else {
    80  		logWritingTo(inFile)
    81  	}
    82  	if f2, err = os.Create(tmpFile); err != nil {
    83  		f1.Close()
    84  		return err
    85  	}
    86  
    87  	defer func() {
    88  		if err != nil {
    89  			f2.Close()
    90  			f1.Close()
    91  			os.Remove(tmpFile)
    92  			return
    93  		}
    94  		if err = f2.Close(); err != nil {
    95  			return
    96  		}
    97  		if err = f1.Close(); err != nil {
    98  			return
    99  		}
   100  		if outFile == "" || inFile == outFile {
   101  			err = os.Rename(tmpFile, inFile)
   102  		}
   103  	}()
   104  
   105  	return InsertPages(f1, f2, selectedPages, before, pageConf, conf)
   106  }
   107  
   108  // RemovePages removes selected pages from rs and writes the result to w.
   109  func RemovePages(rs io.ReadSeeker, w io.Writer, selectedPages []string, conf *model.Configuration) error {
   110  	if rs == nil {
   111  		return errors.New("pdfcpu: RemovePages: missing rs")
   112  	}
   113  
   114  	if conf == nil {
   115  		conf = model.NewDefaultConfiguration()
   116  	}
   117  	conf.Cmd = model.REMOVEPAGES
   118  
   119  	ctx, err := ReadValidateAndOptimize(rs, conf)
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	pages, err := RemainingPagesForPageRemoval(ctx.PageCount, selectedPages, true)
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	if len(pages) == 0 {
   130  		if log.CLIEnabled() {
   131  			log.CLI.Println("aborted: missing page numbers!")
   132  		}
   133  		return nil
   134  	}
   135  
   136  	var pageNrs []int
   137  	for k, v := range pages {
   138  		if v {
   139  			pageNrs = append(pageNrs, k)
   140  		}
   141  	}
   142  	sort.Ints(pageNrs)
   143  
   144  	ctxDest, err := pdfcpu.ExtractPages(ctx, pageNrs, false)
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	return Write(ctxDest, w, conf)
   150  }
   151  
   152  // RemovePagesFile removes selected inFile pages and writes the result to outFile..
   153  func RemovePagesFile(inFile, outFile string, selectedPages []string, conf *model.Configuration) (err error) {
   154  	var f1, f2 *os.File
   155  
   156  	if f1, err = os.Open(inFile); err != nil {
   157  		return err
   158  	}
   159  
   160  	tmpFile := inFile + ".tmp"
   161  	if outFile != "" && inFile != outFile {
   162  		tmpFile = outFile
   163  		logWritingTo(outFile)
   164  	} else {
   165  		logWritingTo(inFile)
   166  	}
   167  	if f2, err = os.Create(tmpFile); err != nil {
   168  		f1.Close()
   169  		return err
   170  	}
   171  
   172  	defer func() {
   173  		if err != nil {
   174  			f2.Close()
   175  			f1.Close()
   176  			os.Remove(tmpFile)
   177  			return
   178  		}
   179  		if err = f2.Close(); err != nil {
   180  			return
   181  		}
   182  		if err = f1.Close(); err != nil {
   183  			return
   184  		}
   185  		if outFile == "" || inFile == outFile {
   186  			err = os.Rename(tmpFile, inFile)
   187  		}
   188  	}()
   189  
   190  	return RemovePages(f1, f2, selectedPages, conf)
   191  }
   192  
   193  // PageCount returns rs's page count.
   194  func PageCount(rs io.ReadSeeker, conf *model.Configuration) (int, error) {
   195  	if rs == nil {
   196  		return 0, errors.New("pdfcpu: PageCount: missing rs")
   197  	}
   198  
   199  	ctx, err := ReadAndValidate(rs, conf)
   200  	if err != nil {
   201  		return 0, err
   202  	}
   203  
   204  	return ctx.PageCount, nil
   205  }
   206  
   207  // PageCountFile returns inFile's page count.
   208  func PageCountFile(inFile string) (int, error) {
   209  	f, err := os.Open(inFile)
   210  	if err != nil {
   211  		return 0, err
   212  	}
   213  	defer f.Close()
   214  
   215  	return PageCount(f, model.NewDefaultConfiguration())
   216  }
   217  
   218  // PageDims returns a sorted slice of mediaBox dimensions for rs.
   219  func PageDims(rs io.ReadSeeker, conf *model.Configuration) ([]types.Dim, error) {
   220  	if rs == nil {
   221  		return nil, errors.New("pdfcpu: PageDims: missing rs")
   222  	}
   223  
   224  	ctx, err := ReadAndValidate(rs, conf)
   225  	if err != nil {
   226  		return nil, err
   227  	}
   228  
   229  	pd, err := ctx.PageDims()
   230  	if err != nil {
   231  		return nil, err
   232  	}
   233  
   234  	if len(pd) != ctx.PageCount {
   235  		return nil, errors.New("pdfcpu: corrupt page dimensions")
   236  	}
   237  
   238  	return pd, nil
   239  }
   240  
   241  // PageDimsFile returns a sorted slice of mediaBox dimensions for inFile.
   242  func PageDimsFile(inFile string) ([]types.Dim, error) {
   243  	f, err := os.Open(inFile)
   244  	if err != nil {
   245  		return nil, err
   246  	}
   247  	defer f.Close()
   248  
   249  	return PageDims(f, model.NewDefaultConfiguration())
   250  }