Files
query-orchestration/internal/document/clean/pdf.go
T

152 lines
3.2 KiB
Go
Raw Normal View History

2025-02-28 13:11:53 +00:00
package documentclean
import (
"context"
"io"
"github.com/pdfcpu/pdfcpu/pkg/pdfcpu"
"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model"
"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types"
2025-02-28 13:11:53 +00:00
)
type PDF struct {
pdf io.ReadSeeker
config *model.Configuration
maxPages int
maxBytes int64
maxDimension int
minDimension int
2025-02-28 17:36:53 +00:00
minDPI int
2025-02-28 13:11:53 +00:00
}
const (
TEXTRACT_SYNC_LIMIT int64 = 10 * 1024 * 1024
TEXTRACT_ASYNC_LIMIT int64 = 500 * 1024 * 1024
TEXTRACT_PAGE_LIMIT = 3000
TEXTRACT_MAX_DIMENSION = 10000
TEXTRACT_MIN_DIMENSION = 50
2025-02-28 17:36:53 +00:00
TEXTRACT_MIN_DPI = 72
PDF_DEFAULT_DPI = 72
2025-02-28 13:11:53 +00:00
)
func NewPDF(buf io.ReadSeeker) *PDF {
config := &model.Configuration{
ValidationMode: model.ValidationRelaxed,
Reader15: true,
Offline: true,
}
return &PDF{
pdf: buf,
config: config,
maxPages: TEXTRACT_PAGE_LIMIT,
maxBytes: TEXTRACT_SYNC_LIMIT,
maxDimension: TEXTRACT_MAX_DIMENSION,
minDimension: TEXTRACT_MIN_DIMENSION,
2025-02-28 17:36:53 +00:00
minDPI: TEXTRACT_MIN_DPI,
2025-02-28 13:11:53 +00:00
}
}
func (s *PDF) isCorrupt(ctx context.Context) *InvalidDocumentReason {
2025-02-28 13:11:53 +00:00
pdfCtx, err := pdfcpu.ReadWithContext(ctx, s.pdf, s.config)
if err != nil {
reason := InvalidDocumentRead
2025-02-28 13:11:53 +00:00
return &reason
}
if int64(pdfCtx.Read.ReadFileSize()) > s.maxBytes {
reason := InvalidDocumentLargeFile
2025-02-28 13:11:53 +00:00
return &reason
}
docErr := s.isValidPageCount(pdfCtx)
if docErr != nil {
return docErr
}
docErr = s.isValidDimensions(pdfCtx)
if docErr != nil {
return docErr
}
return nil
}
func (s *PDF) isValidPageCount(pdfCtx *model.Context) *InvalidDocumentReason {
err := pdfCtx.EnsurePageCount()
if err != nil {
reason := InvalidDocumentRead
return &reason
}
if pdfCtx.PageCount > s.maxPages {
reason := InvalidDocumentLargePageCount
return &reason
}
return nil
}
func (s *PDF) isValidDimensions(pdfCtx *model.Context) *InvalidDocumentReason {
err := pdfCtx.EnsurePageCount()
if err != nil {
reason := InvalidDocumentRead
return &reason
}
_, err = s.pdf.Seek(0, io.SeekStart)
if err != nil {
reason := InvalidDocumentRead
return &reason
}
pageSet := types.IntSet{}
for pageNum := 1; pageNum <= pdfCtx.PageCount; pageNum++ {
pageSet[pageNum] = true
}
boxes, err := pdfCtx.PageBoundaries(pageSet)
if err != nil {
reason := InvalidDocumentRead
return &reason
}
for _, box := range boxes {
mediaBox := box.MediaBox()
if mediaBox == nil {
continue
}
dims := mediaBox.Dimensions()
if dims.Height > float64(s.maxDimension) ||
dims.Width > float64(s.maxDimension) {
reason := InvalidDocumentLargeDimensions
return &reason
} else if dims.Height < float64(s.minDimension) ||
dims.Width < float64(s.minDimension) {
reason := InvalidDocumentSmallDimensions
return &reason
}
2025-02-28 17:36:53 +00:00
dpiErr := s.isValidDPI(mediaBox, dims)
if dpiErr != nil {
return dpiErr
}
}
return nil
}
func (s *PDF) isValidDPI(mediaBox *types.Rectangle, dims types.Dim) *InvalidDocumentReason {
widthDPI := mediaBox.Width() / dims.ToInches().Width
heightDPI := mediaBox.Height() / dims.ToInches().Height
if widthDPI < float64(s.minDPI) || heightDPI < float64(s.minDPI) {
reason := InvalidDocumentSmallDPI
return &reason
}
2025-02-28 13:11:53 +00:00
return nil
}