Document extractor service (#15665)
* Document extractor service * Fixing vendor modules * Addressing PR Review comments * Some small simplifications * Fixing a linter complain * simplifying a bit the code using package variables Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
04ef5c682e
Коммит
8d5be2d657
27
vendor/github.com/ledongthuc/pdf/LICENSE
сгенерированный
поставляемый
Обычный файл
27
vendor/github.com/ledongthuc/pdf/LICENSE
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
136
vendor/github.com/ledongthuc/pdf/README.md
сгенерированный
поставляемый
Обычный файл
136
vendor/github.com/ledongthuc/pdf/README.md
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,136 @@
|
||||
# PDF Reader
|
||||
|
||||
A simple Go library which enables reading PDF files. Forked from https://github.com/rsc/pdf
|
||||
|
||||
Features
|
||||
- Get plain text content (without format)
|
||||
- Get Content (including all font and formatting information)
|
||||
|
||||
## Install:
|
||||
|
||||
`go get -u github.com/ledongthuc/pdf`
|
||||
|
||||
|
||||
## Read plain text
|
||||
|
||||
```golang
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/ledongthuc/pdf"
|
||||
)
|
||||
|
||||
func main() {
|
||||
pdf.DebugOn = true
|
||||
content, err := readPdf("test.pdf") // Read local pdf file
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(content)
|
||||
return
|
||||
}
|
||||
|
||||
func readPdf(path string) (string, error) {
|
||||
f, r, err := pdf.Open(path)
|
||||
// remember close file
|
||||
defer f.Close()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
b, err := r.GetPlainText()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf.ReadFrom(b)
|
||||
return buf.String(), nil
|
||||
}
|
||||
```
|
||||
|
||||
## Read all text with styles from PDF
|
||||
|
||||
```golang
|
||||
func readPdf2(path string) (string, error) {
|
||||
f, r, err := pdf.Open(path)
|
||||
// remember close file
|
||||
defer f.Close()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
totalPage := r.NumPage()
|
||||
|
||||
for pageIndex := 1; pageIndex <= totalPage; pageIndex++ {
|
||||
p := r.Page(pageIndex)
|
||||
if p.V.IsNull() {
|
||||
continue
|
||||
}
|
||||
var lastTextStyle pdf.Text
|
||||
texts := p.Content().Text
|
||||
for _, text := range texts {
|
||||
if isSameSentence(text, lastTextStyle) {
|
||||
lastTextStyle.S = lastTextStyle.S + text.S
|
||||
} else {
|
||||
fmt.Printf("Font: %s, Font-size: %f, x: %f, y: %f, content: %s \n", lastTextStyle.Font, lastTextStyle.FontSize, lastTextStyle.X, lastTextStyle.Y, lastTextStyle.S)
|
||||
lastTextStyle = text
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Read text grouped by rows
|
||||
|
||||
```golang
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/dcu/pdf"
|
||||
)
|
||||
|
||||
func main() {
|
||||
content, err := readPdf(os.Args[1]) // Read local pdf file
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(content)
|
||||
return
|
||||
}
|
||||
|
||||
func readPdf(path string) (string, error) {
|
||||
f, r, err := pdf.Open(path)
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
totalPage := r.NumPage()
|
||||
|
||||
for pageIndex := 1; pageIndex <= totalPage; pageIndex++ {
|
||||
p := r.Page(pageIndex)
|
||||
if p.V.IsNull() {
|
||||
continue
|
||||
}
|
||||
|
||||
rows, _ := p.GetTextByRow()
|
||||
for _, row := range rows {
|
||||
println(">>>> row: ", row.Position)
|
||||
for _, word := range row.Content {
|
||||
fmt.Println(word.S)
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
```
|
||||
|
||||
## Demo
|
||||

|
||||
53
vendor/github.com/ledongthuc/pdf/ascii85.go
сгенерированный
поставляемый
Обычный файл
53
vendor/github.com/ledongthuc/pdf/ascii85.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,53 @@
|
||||
// file with help function for ascii85 decoder
|
||||
// later if new decoders is going to add it reasonable to rename file and add them here
|
||||
// also create interfaces to switch between them (like in unidoc)
|
||||
|
||||
package pdf
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type alphaReader struct {
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
func newAlphaReader(reader io.Reader) *alphaReader {
|
||||
return &alphaReader{reader: reader}
|
||||
}
|
||||
|
||||
func checkASCII85(r byte) byte {
|
||||
if r >= '!' && r <= 'u' { // 33 <= ascii85 <=117
|
||||
return r
|
||||
}
|
||||
if r == '~' {
|
||||
return 1 // for marking possible end of data
|
||||
}
|
||||
return 0 // if non-ascii85
|
||||
}
|
||||
|
||||
func (a *alphaReader) Read(p []byte) (int, error) {
|
||||
n, err := a.reader.Read(p)
|
||||
if err == io.EOF {
|
||||
}
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
buf := make([]byte, n)
|
||||
tilda := false
|
||||
for i := 0; i < n; i++ {
|
||||
char := checkASCII85(p[i])
|
||||
if char == '>' && tilda { // end of data
|
||||
break
|
||||
}
|
||||
if char > 1 {
|
||||
buf[i] = char
|
||||
}
|
||||
if char == 1 {
|
||||
tilda = true // possible end of data
|
||||
}
|
||||
}
|
||||
|
||||
copy(p, buf)
|
||||
return n, nil
|
||||
}
|
||||
529
vendor/github.com/ledongthuc/pdf/lex.go
сгенерированный
поставляемый
Обычный файл
529
vendor/github.com/ledongthuc/pdf/lex.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,529 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Reading of PDF tokens and objects from a raw byte stream.
|
||||
|
||||
package pdf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A token is a PDF token in the input stream, one of the following Go types:
|
||||
//
|
||||
// bool, a PDF boolean
|
||||
// int64, a PDF integer
|
||||
// float64, a PDF real
|
||||
// string, a PDF string literal
|
||||
// keyword, a PDF keyword
|
||||
// name, a PDF name without the leading slash
|
||||
//
|
||||
type token interface{}
|
||||
|
||||
// A name is a PDF name, without the leading slash.
|
||||
type name string
|
||||
|
||||
// A keyword is a PDF keyword.
|
||||
// Delimiter tokens used in higher-level syntax,
|
||||
// such as "<<", ">>", "[", "]", "{", "}", are also treated as keywords.
|
||||
type keyword string
|
||||
|
||||
// A buffer holds buffered input bytes from the PDF file.
|
||||
type buffer struct {
|
||||
r io.Reader // source of data
|
||||
buf []byte // buffered data
|
||||
pos int // read index in buf
|
||||
offset int64 // offset at end of buf; aka offset of next read
|
||||
tmp []byte // scratch space for accumulating token
|
||||
unread []token // queue of read but then unread tokens
|
||||
allowEOF bool
|
||||
allowObjptr bool
|
||||
allowStream bool
|
||||
eof bool
|
||||
key []byte
|
||||
useAES bool
|
||||
objptr objptr
|
||||
}
|
||||
|
||||
// newBuffer returns a new buffer reading from r at the given offset.
|
||||
func newBuffer(r io.Reader, offset int64) *buffer {
|
||||
return &buffer{
|
||||
r: r,
|
||||
offset: offset,
|
||||
buf: make([]byte, 0, 4096),
|
||||
allowObjptr: true,
|
||||
allowStream: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *buffer) seek(offset int64) {
|
||||
b.offset = offset
|
||||
b.buf = b.buf[:0]
|
||||
b.pos = 0
|
||||
b.unread = b.unread[:0]
|
||||
}
|
||||
|
||||
func (b *buffer) readByte() byte {
|
||||
if b.pos >= len(b.buf) {
|
||||
b.reload()
|
||||
if b.pos >= len(b.buf) {
|
||||
return '\n'
|
||||
}
|
||||
}
|
||||
c := b.buf[b.pos]
|
||||
b.pos++
|
||||
return c
|
||||
}
|
||||
|
||||
func (b *buffer) errorf(format string, args ...interface{}) {
|
||||
panic(fmt.Errorf(format, args...))
|
||||
}
|
||||
|
||||
func (b *buffer) reload() bool {
|
||||
n := cap(b.buf) - int(b.offset%int64(cap(b.buf)))
|
||||
n, err := b.r.Read(b.buf[:n])
|
||||
if n == 0 && err != nil {
|
||||
b.buf = b.buf[:0]
|
||||
b.pos = 0
|
||||
if b.allowEOF && err == io.EOF {
|
||||
b.eof = true
|
||||
return false
|
||||
}
|
||||
b.errorf("malformed PDF: reading at offset %d: %v", b.offset, err)
|
||||
return false
|
||||
}
|
||||
b.offset += int64(n)
|
||||
b.buf = b.buf[:n]
|
||||
b.pos = 0
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *buffer) seekForward(offset int64) {
|
||||
for b.offset < offset {
|
||||
if !b.reload() {
|
||||
return
|
||||
}
|
||||
}
|
||||
b.pos = len(b.buf) - int(b.offset-offset)
|
||||
}
|
||||
|
||||
func (b *buffer) readOffset() int64 {
|
||||
return b.offset - int64(len(b.buf)) + int64(b.pos)
|
||||
}
|
||||
|
||||
func (b *buffer) unreadByte() {
|
||||
if b.pos > 0 {
|
||||
b.pos--
|
||||
}
|
||||
}
|
||||
|
||||
func (b *buffer) unreadToken(t token) {
|
||||
b.unread = append(b.unread, t)
|
||||
}
|
||||
|
||||
func (b *buffer) readToken() token {
|
||||
if n := len(b.unread); n > 0 {
|
||||
t := b.unread[n-1]
|
||||
b.unread = b.unread[:n-1]
|
||||
return t
|
||||
}
|
||||
|
||||
// Find first non-space, non-comment byte.
|
||||
c := b.readByte()
|
||||
for {
|
||||
if isSpace(c) {
|
||||
if b.eof {
|
||||
return io.EOF
|
||||
}
|
||||
c = b.readByte()
|
||||
} else if c == '%' {
|
||||
for c != '\r' && c != '\n' {
|
||||
c = b.readByte()
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch c {
|
||||
case '<':
|
||||
if b.readByte() == '<' {
|
||||
return keyword("<<")
|
||||
}
|
||||
b.unreadByte()
|
||||
return b.readHexString()
|
||||
|
||||
case '(':
|
||||
return b.readLiteralString()
|
||||
|
||||
case '[', ']', '{', '}':
|
||||
return keyword(string(c))
|
||||
|
||||
case '/':
|
||||
return b.readName()
|
||||
|
||||
case '>':
|
||||
if b.readByte() == '>' {
|
||||
return keyword(">>")
|
||||
}
|
||||
b.unreadByte()
|
||||
fallthrough
|
||||
|
||||
default:
|
||||
if isDelim(c) {
|
||||
b.errorf("unexpected delimiter %#q", rune(c))
|
||||
return nil
|
||||
}
|
||||
b.unreadByte()
|
||||
return b.readKeyword()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *buffer) readHexString() token {
|
||||
tmp := b.tmp[:0]
|
||||
for {
|
||||
Loop:
|
||||
c := b.readByte()
|
||||
if c == '>' {
|
||||
break
|
||||
}
|
||||
if isSpace(c) {
|
||||
goto Loop
|
||||
}
|
||||
Loop2:
|
||||
c2 := b.readByte()
|
||||
if isSpace(c2) {
|
||||
goto Loop2
|
||||
}
|
||||
x := unhex(c)<<4 | unhex(c2)
|
||||
if x < 0 {
|
||||
b.errorf("malformed hex string %c %c %s", c, c2, b.buf[b.pos:])
|
||||
break
|
||||
}
|
||||
tmp = append(tmp, byte(x))
|
||||
}
|
||||
b.tmp = tmp
|
||||
return string(tmp)
|
||||
}
|
||||
|
||||
func unhex(b byte) int {
|
||||
switch {
|
||||
case '0' <= b && b <= '9':
|
||||
return int(b) - '0'
|
||||
case 'a' <= b && b <= 'f':
|
||||
return int(b) - 'a' + 10
|
||||
case 'A' <= b && b <= 'F':
|
||||
return int(b) - 'A' + 10
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (b *buffer) readLiteralString() token {
|
||||
tmp := b.tmp[:0]
|
||||
depth := 1
|
||||
Loop:
|
||||
for !b.eof {
|
||||
c := b.readByte()
|
||||
switch c {
|
||||
default:
|
||||
tmp = append(tmp, c)
|
||||
case '(':
|
||||
depth++
|
||||
tmp = append(tmp, c)
|
||||
case ')':
|
||||
if depth--; depth == 0 {
|
||||
break Loop
|
||||
}
|
||||
tmp = append(tmp, c)
|
||||
case '\\':
|
||||
switch c = b.readByte(); c {
|
||||
default:
|
||||
b.errorf("invalid escape sequence \\%c", c)
|
||||
tmp = append(tmp, '\\', c)
|
||||
case 'n':
|
||||
tmp = append(tmp, '\n')
|
||||
case 'r':
|
||||
tmp = append(tmp, '\r')
|
||||
case 'b':
|
||||
tmp = append(tmp, '\b')
|
||||
case 't':
|
||||
tmp = append(tmp, '\t')
|
||||
case 'f':
|
||||
tmp = append(tmp, '\f')
|
||||
case '(', ')', '\\':
|
||||
tmp = append(tmp, c)
|
||||
case '\r':
|
||||
if b.readByte() != '\n' {
|
||||
b.unreadByte()
|
||||
}
|
||||
fallthrough
|
||||
case '\n':
|
||||
// no append
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7':
|
||||
x := int(c - '0')
|
||||
for i := 0; i < 2; i++ {
|
||||
c = b.readByte()
|
||||
if c < '0' || c > '7' {
|
||||
b.unreadByte()
|
||||
break
|
||||
}
|
||||
x = x*8 + int(c-'0')
|
||||
}
|
||||
if x > 255 {
|
||||
b.errorf("invalid octal escape \\%03o", x)
|
||||
}
|
||||
tmp = append(tmp, byte(x))
|
||||
}
|
||||
}
|
||||
}
|
||||
b.tmp = tmp
|
||||
return string(tmp)
|
||||
}
|
||||
|
||||
func (b *buffer) readName() token {
|
||||
tmp := b.tmp[:0]
|
||||
for {
|
||||
c := b.readByte()
|
||||
if isDelim(c) || isSpace(c) {
|
||||
b.unreadByte()
|
||||
break
|
||||
}
|
||||
if c == '#' {
|
||||
x := unhex(b.readByte())<<4 | unhex(b.readByte())
|
||||
if x < 0 {
|
||||
b.errorf("malformed name")
|
||||
}
|
||||
tmp = append(tmp, byte(x))
|
||||
continue
|
||||
}
|
||||
tmp = append(tmp, c)
|
||||
}
|
||||
b.tmp = tmp
|
||||
return name(string(tmp))
|
||||
}
|
||||
|
||||
func (b *buffer) readKeyword() token {
|
||||
tmp := b.tmp[:0]
|
||||
for {
|
||||
c := b.readByte()
|
||||
if isDelim(c) || isSpace(c) {
|
||||
b.unreadByte()
|
||||
break
|
||||
}
|
||||
tmp = append(tmp, c)
|
||||
}
|
||||
b.tmp = tmp
|
||||
s := string(tmp)
|
||||
switch {
|
||||
case s == "true":
|
||||
return true
|
||||
case s == "false":
|
||||
return false
|
||||
case isInteger(s):
|
||||
x, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
b.errorf("invalid integer %s", s)
|
||||
}
|
||||
return x
|
||||
case isReal(s):
|
||||
x, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
b.errorf("invalid real %s", s)
|
||||
}
|
||||
return x
|
||||
}
|
||||
return keyword(string(tmp))
|
||||
}
|
||||
|
||||
func isInteger(s string) bool {
|
||||
if len(s) > 0 && (s[0] == '+' || s[0] == '-') {
|
||||
s = s[1:]
|
||||
}
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, c := range s {
|
||||
if c < '0' || '9' < c {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isReal(s string) bool {
|
||||
if len(s) > 0 && (s[0] == '+' || s[0] == '-') {
|
||||
s = s[1:]
|
||||
}
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
ndot := 0
|
||||
for _, c := range s {
|
||||
if c == '.' {
|
||||
ndot++
|
||||
continue
|
||||
}
|
||||
if c < '0' || '9' < c {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return ndot == 1
|
||||
}
|
||||
|
||||
// An object is a PDF syntax object, one of the following Go types:
|
||||
//
|
||||
// bool, a PDF boolean
|
||||
// int64, a PDF integer
|
||||
// float64, a PDF real
|
||||
// string, a PDF string literal
|
||||
// name, a PDF name without the leading slash
|
||||
// dict, a PDF dictionary
|
||||
// array, a PDF array
|
||||
// stream, a PDF stream
|
||||
// objptr, a PDF object reference
|
||||
// objdef, a PDF object definition
|
||||
//
|
||||
// An object may also be nil, to represent the PDF null.
|
||||
type object interface{}
|
||||
|
||||
type dict map[name]object
|
||||
|
||||
type array []object
|
||||
|
||||
type stream struct {
|
||||
hdr dict
|
||||
ptr objptr
|
||||
offset int64
|
||||
}
|
||||
|
||||
type objptr struct {
|
||||
id uint32
|
||||
gen uint16
|
||||
}
|
||||
|
||||
type objdef struct {
|
||||
ptr objptr
|
||||
obj object
|
||||
}
|
||||
|
||||
func (b *buffer) readObject() object {
|
||||
tok := b.readToken()
|
||||
if kw, ok := tok.(keyword); ok {
|
||||
switch kw {
|
||||
case "null":
|
||||
return nil
|
||||
case "<<":
|
||||
return b.readDict()
|
||||
case "[":
|
||||
return b.readArray()
|
||||
}
|
||||
b.errorf("unexpected keyword %q parsing object", kw)
|
||||
return nil
|
||||
}
|
||||
|
||||
if str, ok := tok.(string); ok && b.key != nil && b.objptr.id != 0 {
|
||||
tok = decryptString(b.key, b.useAES, b.objptr, str)
|
||||
}
|
||||
|
||||
if !b.allowObjptr {
|
||||
return tok
|
||||
}
|
||||
|
||||
if t1, ok := tok.(int64); ok && int64(uint32(t1)) == t1 {
|
||||
tok2 := b.readToken()
|
||||
if t2, ok := tok2.(int64); ok && int64(uint16(t2)) == t2 {
|
||||
tok3 := b.readToken()
|
||||
switch tok3 {
|
||||
case keyword("R"):
|
||||
return objptr{uint32(t1), uint16(t2)}
|
||||
case keyword("obj"):
|
||||
old := b.objptr
|
||||
b.objptr = objptr{uint32(t1), uint16(t2)}
|
||||
obj := b.readObject()
|
||||
if _, ok := obj.(stream); !ok {
|
||||
tok4 := b.readToken()
|
||||
if tok4 != keyword("endobj") {
|
||||
b.errorf("missing endobj after indirect object definition")
|
||||
b.unreadToken(tok4)
|
||||
}
|
||||
}
|
||||
b.objptr = old
|
||||
return objdef{objptr{uint32(t1), uint16(t2)}, obj}
|
||||
}
|
||||
b.unreadToken(tok3)
|
||||
}
|
||||
b.unreadToken(tok2)
|
||||
}
|
||||
return tok
|
||||
}
|
||||
|
||||
func (b *buffer) readArray() object {
|
||||
var x array
|
||||
for {
|
||||
tok := b.readToken()
|
||||
if tok == nil || tok == keyword("]") {
|
||||
break
|
||||
}
|
||||
b.unreadToken(tok)
|
||||
x = append(x, b.readObject())
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func (b *buffer) readDict() object {
|
||||
x := make(dict)
|
||||
for {
|
||||
tok := b.readToken()
|
||||
if tok == nil || tok == keyword(">>") {
|
||||
break
|
||||
}
|
||||
n, ok := tok.(name)
|
||||
if !ok {
|
||||
b.errorf("unexpected non-name key %T(%v) parsing dictionary", tok, tok)
|
||||
continue
|
||||
}
|
||||
x[n] = b.readObject()
|
||||
}
|
||||
|
||||
if !b.allowStream {
|
||||
return x
|
||||
}
|
||||
|
||||
tok := b.readToken()
|
||||
if tok != keyword("stream") {
|
||||
b.unreadToken(tok)
|
||||
return x
|
||||
}
|
||||
|
||||
switch b.readByte() {
|
||||
case '\r':
|
||||
if b.readByte() != '\n' {
|
||||
b.unreadByte()
|
||||
}
|
||||
case '\n':
|
||||
// ok
|
||||
default:
|
||||
b.errorf("stream keyword not followed by newline")
|
||||
}
|
||||
|
||||
return stream{x, b.objptr, b.readOffset()}
|
||||
}
|
||||
|
||||
func isSpace(b byte) bool {
|
||||
switch b {
|
||||
case '\x00', '\t', '\n', '\f', '\r', ' ':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isDelim(b byte) bool {
|
||||
switch b {
|
||||
case '<', '>', '(', ')', '[', ']', '{', '}', '/', '%':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
4286
vendor/github.com/ledongthuc/pdf/name.go
сгенерированный
поставляемый
Обычный файл
4286
vendor/github.com/ledongthuc/pdf/name.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
1050
vendor/github.com/ledongthuc/pdf/page.go
сгенерированный
поставляемый
Обычный файл
1050
vendor/github.com/ledongthuc/pdf/page.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
138
vendor/github.com/ledongthuc/pdf/ps.go
сгенерированный
поставляемый
Обычный файл
138
vendor/github.com/ledongthuc/pdf/ps.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,138 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pdf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// A Stack represents a stack of values.
|
||||
type Stack struct {
|
||||
stack []Value
|
||||
}
|
||||
|
||||
func (stk *Stack) Len() int {
|
||||
return len(stk.stack)
|
||||
}
|
||||
|
||||
func (stk *Stack) Push(v Value) {
|
||||
stk.stack = append(stk.stack, v)
|
||||
}
|
||||
|
||||
func (stk *Stack) Pop() Value {
|
||||
n := len(stk.stack)
|
||||
if n == 0 {
|
||||
return Value{}
|
||||
}
|
||||
v := stk.stack[n-1]
|
||||
stk.stack[n-1] = Value{}
|
||||
stk.stack = stk.stack[:n-1]
|
||||
return v
|
||||
}
|
||||
|
||||
func newDict() Value {
|
||||
return Value{nil, objptr{}, make(dict)}
|
||||
}
|
||||
|
||||
// Interpret interprets the content in a stream as a basic PostScript program,
|
||||
// pushing values onto a stack and then calling the do function to execute
|
||||
// operators. The do function may push or pop values from the stack as needed
|
||||
// to implement op.
|
||||
//
|
||||
// Interpret handles the operators "dict", "currentdict", "begin", "end", "def", and "pop" itself.
|
||||
//
|
||||
// Interpret is not a full-blown PostScript interpreter. Its job is to handle the
|
||||
// very limited PostScript found in certain supporting file formats embedded
|
||||
// in PDF files, such as cmap files that describe the mapping from font code
|
||||
// points to Unicode code points.
|
||||
//
|
||||
// There is no support for executable blocks, among other limitations.
|
||||
//
|
||||
func Interpret(strm Value, do func(stk *Stack, op string)) {
|
||||
rd := strm.Reader()
|
||||
b := newBuffer(rd, 0)
|
||||
b.allowEOF = true
|
||||
b.allowObjptr = false
|
||||
b.allowStream = false
|
||||
var stk Stack
|
||||
var dicts []dict
|
||||
Reading:
|
||||
for {
|
||||
tok := b.readToken()
|
||||
if tok == io.EOF {
|
||||
break
|
||||
}
|
||||
if kw, ok := tok.(keyword); ok {
|
||||
switch kw {
|
||||
case "null", "[", "]", "<<", ">>":
|
||||
break
|
||||
default:
|
||||
for i := len(dicts) - 1; i >= 0; i-- {
|
||||
if v, ok := dicts[i][name(kw)]; ok {
|
||||
stk.Push(Value{nil, objptr{}, v})
|
||||
continue Reading
|
||||
}
|
||||
}
|
||||
do(&stk, string(kw))
|
||||
continue
|
||||
case "dict":
|
||||
stk.Pop()
|
||||
stk.Push(Value{nil, objptr{}, make(dict)})
|
||||
continue
|
||||
case "currentdict":
|
||||
if len(dicts) == 0 {
|
||||
panic("no current dictionary")
|
||||
}
|
||||
stk.Push(Value{nil, objptr{}, dicts[len(dicts)-1]})
|
||||
continue
|
||||
case "begin":
|
||||
d := stk.Pop()
|
||||
if d.Kind() != Dict {
|
||||
panic("cannot begin non-dict")
|
||||
}
|
||||
dicts = append(dicts, d.data.(dict))
|
||||
continue
|
||||
case "end":
|
||||
if len(dicts) <= 0 {
|
||||
panic("mismatched begin/end")
|
||||
}
|
||||
dicts = dicts[:len(dicts)-1]
|
||||
continue
|
||||
case "def":
|
||||
if len(dicts) <= 0 {
|
||||
panic("def without open dict")
|
||||
}
|
||||
val := stk.Pop()
|
||||
key, ok := stk.Pop().data.(name)
|
||||
if !ok {
|
||||
panic("def of non-name")
|
||||
}
|
||||
dicts[len(dicts)-1][key] = val.data
|
||||
continue
|
||||
case "pop":
|
||||
stk.Pop()
|
||||
continue
|
||||
}
|
||||
}
|
||||
b.unreadToken(tok)
|
||||
obj := b.readObject()
|
||||
stk.Push(Value{nil, objptr{}, obj})
|
||||
}
|
||||
}
|
||||
|
||||
type seqReader struct {
|
||||
rd io.Reader
|
||||
offset int64
|
||||
}
|
||||
|
||||
func (r *seqReader) ReadAt(buf []byte, offset int64) (int, error) {
|
||||
if offset != r.offset {
|
||||
return 0, fmt.Errorf("non-sequential read of stream")
|
||||
}
|
||||
n, err := io.ReadFull(r.rd, buf)
|
||||
r.offset += int64(n)
|
||||
return n, err
|
||||
}
|
||||
1112
vendor/github.com/ledongthuc/pdf/read.go
сгенерированный
поставляемый
Обычный файл
1112
vendor/github.com/ledongthuc/pdf/read.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
158
vendor/github.com/ledongthuc/pdf/text.go
сгенерированный
поставляемый
Обычный файл
158
vendor/github.com/ledongthuc/pdf/text.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,158 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pdf
|
||||
|
||||
import (
|
||||
"unicode"
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
const noRune = unicode.ReplacementChar
|
||||
|
||||
func isPDFDocEncoded(s string) bool {
|
||||
if isUTF16(s) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(s); i++ {
|
||||
if pdfDocEncoding[s[i]] == noRune {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func pdfDocDecode(s string) string {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] >= 0x80 || pdfDocEncoding[s[i]] != rune(s[i]) {
|
||||
goto Decode
|
||||
}
|
||||
}
|
||||
return s
|
||||
|
||||
Decode:
|
||||
r := make([]rune, len(s))
|
||||
for i := 0; i < len(s); i++ {
|
||||
r[i] = pdfDocEncoding[s[i]]
|
||||
}
|
||||
return string(r)
|
||||
}
|
||||
|
||||
func isUTF16(s string) bool {
|
||||
return len(s) >= 2 && s[0] == 0xfe && s[1] == 0xff && len(s)%2 == 0
|
||||
}
|
||||
|
||||
func utf16Decode(s string) string {
|
||||
var u []uint16
|
||||
for i := 0; i < len(s); i += 2 {
|
||||
u = append(u, uint16(s[i])<<8|uint16(s[i+1]))
|
||||
}
|
||||
return string(utf16.Decode(u))
|
||||
}
|
||||
|
||||
// See PDF 32000-1:2008, Table D.2
|
||||
var pdfDocEncoding = [256]rune{
|
||||
noRune, noRune, noRune, noRune, noRune, noRune, noRune, noRune,
|
||||
noRune, 0x0009, 0x000a, noRune, noRune, 0x000d, noRune, noRune,
|
||||
noRune, noRune, noRune, noRune, noRune, noRune, noRune, noRune,
|
||||
0x02d8, 0x02c7, 0x02c6, 0x02d9, 0x02dd, 0x02db, 0x02da, 0x02dc,
|
||||
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
||||
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
||||
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
|
||||
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
||||
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
|
||||
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
|
||||
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
|
||||
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, noRune,
|
||||
0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x0192, 0x2044,
|
||||
0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018,
|
||||
0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x0141, 0x0152, 0x0160,
|
||||
0x0178, 0x017d, 0x0131, 0x0142, 0x0153, 0x0161, 0x017e, noRune,
|
||||
0x20ac, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
|
||||
0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, noRune, 0x00ae, 0x00af,
|
||||
0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
|
||||
0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
|
||||
0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
|
||||
0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
|
||||
0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
|
||||
0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
|
||||
0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
|
||||
0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
|
||||
0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
|
||||
0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff,
|
||||
}
|
||||
|
||||
var winAnsiEncoding = [256]rune{
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
|
||||
0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
|
||||
0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
|
||||
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
||||
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
||||
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
|
||||
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
||||
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
|
||||
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
|
||||
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
|
||||
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
|
||||
0x20ac, noRune, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
|
||||
0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, noRune, 0x017d, noRune,
|
||||
noRune, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
|
||||
0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, noRune, 0x017e, 0x0178,
|
||||
0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
|
||||
0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
|
||||
0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
|
||||
0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
|
||||
0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
|
||||
0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
|
||||
0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
|
||||
0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
|
||||
0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
|
||||
0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
|
||||
0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
|
||||
0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff,
|
||||
}
|
||||
|
||||
var macRomanEncoding = [256]rune{
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
|
||||
0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
|
||||
0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
|
||||
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
||||
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
||||
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
|
||||
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
||||
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
|
||||
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
|
||||
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
|
||||
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
|
||||
0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1,
|
||||
0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8,
|
||||
0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3,
|
||||
0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc,
|
||||
0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df,
|
||||
0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8,
|
||||
0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211,
|
||||
0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x00e6, 0x00f8,
|
||||
0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab,
|
||||
0x00bb, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153,
|
||||
0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca,
|
||||
0x00ff, 0x0178, 0x2044, 0x20ac, 0x2039, 0x203a, 0xfb01, 0xfb02,
|
||||
0x2021, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1,
|
||||
0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4,
|
||||
0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc,
|
||||
0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7,
|
||||
}
|
||||
Ссылка в новой задаче
Block a user