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

     1  /*
     2  	Copyright 2023 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  	"bytes"
    21  	"encoding/json"
    22  	"io"
    23  	"os"
    24  	"time"
    25  
    26  	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu"
    27  	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model"
    28  	"github.com/pkg/errors"
    29  )
    30  
    31  var ErrNoOp = errors.New("pdfcpu: no operation")
    32  
    33  // ViewerPreferences returns rs's viewer preferences.
    34  func ViewerPreferences(rs io.ReadSeeker, conf *model.Configuration) (*model.ViewerPreferences, *model.Version, error) {
    35  	if rs == nil {
    36  		return nil, nil, errors.New("pdfcpu: ViewerPreferences: missing rs")
    37  	}
    38  
    39  	if conf == nil {
    40  		conf = model.NewDefaultConfiguration()
    41  	} else {
    42  		conf.ValidationMode = model.ValidationRelaxed
    43  	}
    44  	conf.Cmd = model.LISTVIEWERPREFERENCES
    45  
    46  	ctx, err := ReadAndValidate(rs, conf)
    47  	if err != nil {
    48  		return nil, nil, err
    49  	}
    50  
    51  	v := ctx.XRefTable.Version()
    52  
    53  	return ctx.ViewerPref, &v, nil
    54  }
    55  
    56  // ViewerPreferences returns inFile's viewer preferences.
    57  func ViewerPreferencesFile(inFile string, all bool, conf *model.Configuration) (*model.ViewerPreferences, error) {
    58  	f, err := os.Open(inFile)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	defer f.Close()
    63  
    64  	vp, version, err := ViewerPreferences(f, conf)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	if !all {
    70  		return vp, nil
    71  	}
    72  
    73  	return model.ViewerPreferencesWithDefaults(vp, *version)
    74  }
    75  
    76  // ListViewerPreferences returns rs's viewer preferences.
    77  func ListViewerPreferences(rs io.ReadSeeker, all bool, conf *model.Configuration) ([]string, error) {
    78  	if rs == nil {
    79  		return nil, errors.New("pdfcpu: ListViewerPreferences: missing rs")
    80  	}
    81  
    82  	if conf == nil {
    83  		conf = model.NewDefaultConfiguration()
    84  	} else {
    85  		conf.ValidationMode = model.ValidationRelaxed
    86  	}
    87  	conf.Cmd = model.LISTVIEWERPREFERENCES
    88  
    89  	ctx, err := ReadAndValidate(rs, conf)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	if !all {
    95  		if ctx.ViewerPref != nil {
    96  			return ctx.ViewerPref.List(), nil
    97  		}
    98  		return []string{"No viewer preferences available."}, nil
    99  	}
   100  
   101  	vp1, err := model.ViewerPreferencesWithDefaults(ctx.ViewerPref, ctx.XRefTable.Version())
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	return vp1.List(), nil
   107  }
   108  
   109  // ListViewerPreferencesFile lists inFile's viewer preferences in JSON.
   110  func ListViewerPreferencesFileJSON(inFile string, all bool, conf *model.Configuration) ([]string, error) {
   111  	f, err := os.Open(inFile)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	defer f.Close()
   116  
   117  	vp, version, err := ViewerPreferences(f, conf)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	if !all {
   123  		if vp == nil {
   124  			return []string{"No viewer preferences available."}, nil
   125  		}
   126  	} else {
   127  		vp, err = model.ViewerPreferencesWithDefaults(vp, *version)
   128  		if err != nil {
   129  			return nil, err
   130  		}
   131  	}
   132  
   133  	s := struct {
   134  		Header     pdfcpu.Header            `json:"header"`
   135  		ViewerPref *model.ViewerPreferences `json:"viewerPreferences"`
   136  	}{
   137  		Header:     pdfcpu.Header{Version: "pdfcpu " + model.VersionStr, Creation: time.Now().Format("2006-01-02 15:04:05 MST")},
   138  		ViewerPref: vp,
   139  	}
   140  
   141  	bb, err := json.MarshalIndent(s, "", "\t")
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	return []string{string(bb)}, nil
   147  }
   148  
   149  // ListViewerPreferencesFile lists inFile's viewer preferences.
   150  func ListViewerPreferencesFile(inFile string, all, json bool, conf *model.Configuration) ([]string, error) {
   151  	if json {
   152  		return ListViewerPreferencesFileJSON(inFile, all, conf)
   153  	}
   154  
   155  	f, err := os.Open(inFile)
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  	defer f.Close()
   160  
   161  	return ListViewerPreferences(f, all, conf)
   162  }
   163  
   164  // SetViewerPreferences sets rs's viewer preferences and writes the result to w.
   165  func SetViewerPreferences(rs io.ReadSeeker, w io.Writer, vp model.ViewerPreferences, conf *model.Configuration) error {
   166  	if rs == nil {
   167  		return errors.New("pdfcpu: SetViewerPreferences: missing rs")
   168  	}
   169  
   170  	if w == nil {
   171  		return errors.New("pdfcpu: SetViewerPreferences: missing w")
   172  	}
   173  
   174  	if conf == nil {
   175  		conf = model.NewDefaultConfiguration()
   176  	} else {
   177  		conf.ValidationMode = model.ValidationRelaxed
   178  	}
   179  	conf.Cmd = model.SETVIEWERPREFERENCES
   180  
   181  	ctx, err := ReadAndValidate(rs, conf)
   182  	if err != nil {
   183  		return err
   184  	}
   185  
   186  	version := ctx.XRefTable.Version()
   187  
   188  	if err := vp.Validate(version); err != nil {
   189  		return err
   190  	}
   191  
   192  	if ctx.ViewerPref == nil {
   193  		ctx.ViewerPref = &vp
   194  	} else {
   195  		ctx.ViewerPref.Populate(&vp)
   196  	}
   197  
   198  	ctx.XRefTable.BindViewerPreferences()
   199  
   200  	return Write(ctx, w, conf)
   201  }
   202  
   203  // SetViewerPreferencesFromJSONBytes sets rs's viewer preferences corresponding to jsonBytes and writes the result to w.
   204  func SetViewerPreferencesFromJSONBytes(rs io.ReadSeeker, w io.Writer, jsonBytes []byte, conf *model.Configuration) error {
   205  	if rs == nil {
   206  		return errors.New("pdfcpu: SetViewerPreferencesFromJSONBytes: missing rs")
   207  	}
   208  
   209  	if w == nil {
   210  		return errors.New("pdfcpu: SetViewerPreferencesFromJSONBytes: missing w")
   211  	}
   212  
   213  	if !json.Valid(jsonBytes) {
   214  		return ErrInvalidJSON
   215  	}
   216  
   217  	vp := model.ViewerPreferences{}
   218  
   219  	if err := json.Unmarshal(jsonBytes, &vp); err != nil {
   220  		return err
   221  	}
   222  
   223  	return SetViewerPreferences(rs, w, vp, conf)
   224  }
   225  
   226  // SetViewerPreferencesFromJSONReader sets rs's viewer preferences corresponding to rd and writes the result to w.
   227  func SetViewerPreferencesFromJSONReader(rs io.ReadSeeker, w io.Writer, rd io.Reader, conf *model.Configuration) error {
   228  	if rs == nil {
   229  		return errors.New("pdfcpu: SetViewerPreferencesFromJSONReader: missing rs")
   230  	}
   231  
   232  	if w == nil {
   233  		return errors.New("pdfcpu: SetViewerPreferencesFromJSONReader: missing w")
   234  	}
   235  
   236  	if rd == nil {
   237  		return errors.New("pdfcpu: SetViewerPreferencesFromJSONReader: missing rd")
   238  	}
   239  
   240  	var buf bytes.Buffer
   241  	if _, err := io.Copy(&buf, rd); err != nil {
   242  		return err
   243  	}
   244  
   245  	return SetViewerPreferencesFromJSONBytes(rs, w, buf.Bytes(), conf)
   246  }
   247  
   248  // SetViewerPreferencesFile sets inFile's viewer preferences and writes the result to outFile.
   249  func SetViewerPreferencesFile(inFile, outFile string, vp model.ViewerPreferences, conf *model.Configuration) (err error) {
   250  	var f1, f2 *os.File
   251  
   252  	if f1, err = os.Open(inFile); err != nil {
   253  		return err
   254  	}
   255  
   256  	tmpFile := inFile + ".tmp"
   257  	if outFile != "" && inFile != outFile {
   258  		tmpFile = outFile
   259  	}
   260  	if f2, err = os.Create(tmpFile); err != nil {
   261  		f1.Close()
   262  		return err
   263  	}
   264  
   265  	defer func() {
   266  		if err != nil {
   267  			f2.Close()
   268  			f1.Close()
   269  			os.Remove(tmpFile)
   270  			return
   271  		}
   272  		if err = f2.Close(); err != nil {
   273  			return
   274  		}
   275  		if err = f1.Close(); err != nil {
   276  			return
   277  		}
   278  		if outFile == "" || inFile == outFile {
   279  			err = os.Rename(tmpFile, inFile)
   280  		}
   281  	}()
   282  
   283  	return SetViewerPreferences(f1, f2, vp, conf)
   284  }
   285  
   286  // SetViewerPreferencesFileFromJSONBytes sets inFile's viewer preferences corresponding to jsonBytes and writes the result to outFile.
   287  func SetViewerPreferencesFileFromJSONBytes(inFile, outFile string, jsonBytes []byte, 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 SetViewerPreferencesFromJSONBytes(f1, f2, jsonBytes, conf)
   322  }
   323  
   324  // SetViewerPreferencesFileFromJSONFile sets inFile's viewer preferences corresponding to inFileJSON and writes the result to outFile.
   325  func SetViewerPreferencesFileFromJSONFile(inFilePDF, outFilePDF, inFileJSON string, conf *model.Configuration) error {
   326  	if inFileJSON == "" {
   327  		return errors.New("pdfcpu: SetViewerPreferencesFileFromJSONFile: missing inFileJSON")
   328  	}
   329  
   330  	bb, err := os.ReadFile(inFileJSON)
   331  	if err != nil {
   332  		return err
   333  	}
   334  
   335  	return SetViewerPreferencesFileFromJSONBytes(inFilePDF, outFilePDF, bb, conf)
   336  }
   337  
   338  // ResetViewerPreferences resets rs's viewer preferences and writes the result to w.
   339  func ResetViewerPreferences(rs io.ReadSeeker, w io.Writer, conf *model.Configuration) error {
   340  	if rs == nil {
   341  		return errors.New("pdfcpu: ResetViewerPreferences: missing rs")
   342  	}
   343  
   344  	if w == nil {
   345  		return errors.New("pdfcpu: ResetViewerPreferences: missing w")
   346  	}
   347  
   348  	if conf == nil {
   349  		conf = model.NewDefaultConfiguration()
   350  	} else {
   351  		conf.ValidationMode = model.ValidationRelaxed
   352  	}
   353  	conf.Cmd = model.RESETVIEWERPREFERENCES
   354  
   355  	ctx, err := ReadAndValidate(rs, conf)
   356  	if err != nil {
   357  		return err
   358  	}
   359  
   360  	if ctx.ViewerPref == nil {
   361  		return ErrNoOp
   362  	}
   363  
   364  	delete(ctx.RootDict, "ViewerPreferences")
   365  
   366  	return Write(ctx, w, conf)
   367  }
   368  
   369  // ResetViewerPreferencesFile resets inFile's viewer preferences and writes the result to outFile.
   370  func ResetViewerPreferencesFile(inFile, outFile string, conf *model.Configuration) (err error) {
   371  	var f1, f2 *os.File
   372  
   373  	if f1, err = os.Open(inFile); err != nil {
   374  		return err
   375  	}
   376  
   377  	tmpFile := inFile + ".tmp"
   378  	if outFile != "" && inFile != outFile {
   379  		tmpFile = outFile
   380  	}
   381  	if f2, err = os.Create(tmpFile); err != nil {
   382  		f1.Close()
   383  		return err
   384  	}
   385  
   386  	defer func() {
   387  		if err != nil {
   388  			f2.Close()
   389  			f1.Close()
   390  			os.Remove(tmpFile)
   391  			if err == ErrNoOp {
   392  				err = nil
   393  			}
   394  			return
   395  		}
   396  		if err = f2.Close(); err != nil {
   397  			return
   398  		}
   399  		if err = f1.Close(); err != nil {
   400  			return
   401  		}
   402  		if outFile == "" || inFile == outFile {
   403  			err = os.Rename(tmpFile, inFile)
   404  		}
   405  	}()
   406  
   407  	return ResetViewerPreferences(f1, f2, conf)
   408  }