package extractors import ( "context" "fmt" "io" "os" "os/exec" ) // PDFExtractor extracts text content from PDF files using pdftotext type PDFExtractor struct { pdftotextPath string available bool initError error } // NewPDFExtractor creates a new PDF extractor func NewPDFExtractor() *PDFExtractor { return &PDFExtractor{} } // Key returns the unique identifier for this extractor func (e *PDFExtractor) Key() string { return "pdf" } // Name returns the human-readable name for this extractor func (e *PDFExtractor) Name() string { return "PDF Text Extractor" } // SupportedMimeTypes returns the MIME types this extractor supports func (e *PDFExtractor) SupportedMimeTypes() []string { return []string{"application/pdf"} } // Init initializes the extractor by checking if pdftotext is available func (e *PDFExtractor) Init() error { // Check if pdftotext is available in PATH path, err := exec.LookPath("pdftotext") if err != nil { e.available = false e.initError = fmt.Errorf("pdftotext binary not found in PATH: %w", err) return e.initError } e.pdftotextPath = path e.available = true e.initError = nil return nil } // Extract extracts text content from a PDF file func (e *PDFExtractor) Extract(ctx context.Context, filePath string) (string, error) { if !e.available { return "", fmt.Errorf("PDF extractor is not available: %w", e.initError) } // Open the PDF file file, err := os.Open(filePath) if err != nil { return "", fmt.Errorf("failed to open file: %w", err) } defer func() { _ = file.Close() }() // Create command to run pdftotext // Use "-" to read from stdin and output to stdout cmd := exec.CommandContext(ctx, e.pdftotextPath, "-", "-") cmd.Stdin = file // Capture stdout stdout, err := cmd.StdoutPipe() if err != nil { return "", fmt.Errorf("failed to create stdout pipe: %w", err) } // Start the command if err := cmd.Start(); err != nil { return "", fmt.Errorf("failed to start pdftotext: %w", err) } // Read all output output, err := io.ReadAll(stdout) if err != nil { _ = cmd.Wait() return "", fmt.Errorf("failed to read output: %w", err) } // Wait for command to complete if err := cmd.Wait(); err != nil { return "", fmt.Errorf("pdftotext failed: %w", err) } return string(output), nil } // GetInitError returns the initialization error if any func (e *PDFExtractor) GetInitError() error { return e.initError } // IsAvailable returns whether the extractor is available func (e *PDFExtractor) IsAvailable() bool { return e.available }