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 }