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 }