unipdf/core/io.go
2019-05-16 20:44:51 +00:00

114 lines
2.5 KiB
Go

/*
* This file is subject to the terms and conditions defined in
* file 'LICENSE.md', which is part of this source code package.
*/
package core
import (
"bufio"
"errors"
"io"
"github.com/unidoc/unipdf/v3/common"
)
// Offset reader encapsulates io.ReadSeeker and offsets it by the specified
// offset, thus skipping the first offset bytes. Reading always occurs after the
// offset.
type offsetReader struct {
reader io.ReadSeeker
offset int64
}
func newOffsetReader(reader io.ReadSeeker, offset int64) (*offsetReader, error) {
r := &offsetReader{
reader: reader,
offset: offset,
}
_, err := r.Seek(0, io.SeekStart)
return r, err
}
func (r *offsetReader) Read(p []byte) (n int, err error) {
return r.reader.Read(p)
}
func (r *offsetReader) Seek(offset int64, whence int) (int64, error) {
if whence == io.SeekStart {
offset += r.offset
}
n, err := r.reader.Seek(offset, whence)
if err != nil {
return n, err
}
if whence == io.SeekCurrent {
n -= r.offset
}
if n < 0 {
return 0, errors.New("core.offsetReader.Seek: negative position")
}
return n, nil
}
// ReadAtLeast reads at least n bytes into slice p.
// Returns the number of bytes read (should always be == n), and an error on failure.
func (parser *PdfParser) ReadAtLeast(p []byte, n int) (int, error) {
remaining := n
start := 0
numRounds := 0
for remaining > 0 {
nRead, err := parser.reader.Read(p[start:])
if err != nil {
common.Log.Debug("ERROR Failed reading (%d;%d) %s", nRead, numRounds, err.Error())
return start, errors.New("failed reading")
}
numRounds++
start += nRead
remaining -= nRead
}
return start, nil
}
// GetFileOffset returns the current file offset, accounting for buffered position.
func (parser *PdfParser) GetFileOffset() int64 {
offset, _ := parser.rs.Seek(0, io.SeekCurrent)
offset -= int64(parser.reader.Buffered())
return offset
}
// SetFileOffset sets the file to an offset position and resets buffer.
func (parser *PdfParser) SetFileOffset(offset int64) {
if offset < 0 {
offset = 0
}
parser.rs.Seek(offset, io.SeekStart)
parser.reader = bufio.NewReader(parser.rs)
}
// ReadBytesAt reads byte content at specific offset and length within the PDF.
func (parser *PdfParser) ReadBytesAt(offset, len int64) ([]byte, error) {
curPos := parser.GetFileOffset()
_, err := parser.rs.Seek(offset, io.SeekStart)
if err != nil {
return nil, err
}
bb := make([]byte, len)
_, err = io.ReadAtLeast(parser.rs, bb, int(len))
if err != nil {
return nil, err
}
// Restore.
parser.SetFileOffset(curPos)
return bb, nil
}