1tomany/pdf-pack-bundle
Symfony bundle for 1tomany/pdf-pack that makes PDF metadata reading and page extraction easy via autowired actions. Supports Poppler tools (pdfinfo, pdftoppm, pdftotext) to rasterize pages to PNG or extract text with simple request objects.
Installation:
composer require 1tomany/pdf-pack-bundle
Ensure Poppler utilities (pdfinfo, pdftoppm, pdftotext) are installed on your system and available in $PATH.
Basic Configuration:
Create config/packages/onetomany_pdfpack.yaml:
onetomany_pdfpack:
client: "poppler"
First Use Case:
Inject ExtractActionInterface or ReadActionInterface into a service to extract PDF metadata or content:
use OneToMany\PdfPack\Contract\Action\ReadActionInterface;
use OneToMany\PdfPack\Request\ReadRequest;
class PdfMetadataService {
public function __construct(private ReadActionInterface $readAction) {}
public function getMetadata(string $filePath): array {
$request = new ReadRequest($filePath);
$response = $this->readAction->act($request);
return $response->toArray();
}
}
Metadata Extraction:
Use ReadActionInterface to fetch PDF properties (e.g., page count, author, title):
$request = new ReadRequest('/path/to/file.pdf');
$response = $readAction->act($request);
$pageCount = $response->getPageCount();
Page Rasterization:
Convert PDF pages to images (PNG/JPEG) with ExtractActionInterface:
$request = (new ExtractRequest('/path/to/file.pdf'))
->fromPage(1)
->toPage(3)
->asPngOutput()
->atResolution(300);
foreach ($extractAction->act($request) as $page) {
file_put_contents("page_{$page->getPageNumber()}.png", $page->getData());
}
Text Extraction: Extract text from specific pages:
$request = (new ExtractRequest('/path/to/file.pdf'))
->fromPage(2)
->toPage(5)
->asTextOutput();
foreach ($extractAction->act($request) as $page) {
$text = $page->getData();
// Process text (e.g., save to DB, analyze with NLP)
}
File Uploads:
Combine with Laravel’s UploadedFile or Symfony’s UploadedFile to handle dynamic PDF uploads:
public function upload(UploadedFile $file): void {
$tempPath = $file->getRealPath();
$request = new ExtractRequest($tempPath)->asTextOutput();
// ...
}
Queue Jobs: Offload PDF processing to queues (e.g., Laravel Queues or Symfony Messenger) to avoid blocking requests:
use OneToMany\PdfPack\Contract\Action\ExtractActionInterface;
class ProcessPdfJob implements ShouldQueue {
public function handle(ExtractActionInterface $extractAction) {
// Process PDF asynchronously
}
}
Storage Integration:
Save extracted images/text to cloud storage (e.g., S3, GCS) using Laravel’s Storage facade or Symfony’s Filesystem:
Storage::put("pdf/page_{$page->getPageNumber()}.png", $page->getData());
Event-Driven Workflows:
Dispatch events after extraction (e.g., PdfExtractedEvent) to trigger downstream actions:
event(new PdfExtractedEvent($extractedData));
Poppler Dependencies:
pdfinfo, pdftoppm, and pdftotext are installed and accessible.which pdfinfo in your terminal to verify paths. Adjust pdfinfo_binary, pdftoppm_binary, and pdftotext_binary in config if needed.Memory Limits:
atResolution() to reduce DPI or process pages in batches:
$request->atResolution(72); // Lower resolution
File Permissions:
Mock Client in Tests:
mock client in tests may cause flaky tests due to external process calls:
# config/packages/onetomany_pdfpack.yaml
when@test:
onetomany_pdfpack:
client: "mock"
Generator Handling:
ExtractActionInterface::act() returns a Generator. Always iterate fully to avoid resource leaks:
foreach ($extractAction->act($request) as $page) {
// Process page
}
Log Binary Paths: Add logging to verify binary paths:
$this->logger->info('Poppler binary paths', [
'pdfinfo' => $this->popplerClient->getPdfInfoBinary(),
'pdftoppm' => $this->popplerClient->getPdfToPpmBinary(),
]);
Check Return Codes:
Wrap ExtractActionInterface calls in try-catch to handle failures:
try {
$response = $extractAction->act($request);
} catch (\OneToMany\PdfPack\Exception\PdfPackException $e) {
$this->logger->error('PDF extraction failed', ['error' => $e->getMessage()]);
throw new \RuntimeException('Failed to process PDF', 0, $e);
}
Custom Clients:
Implement ClientInterface for alternative backends (e.g., Ghostscript, Imagick):
class GhostscriptClient implements ClientInterface {
public function read(ReadRequest $request): ReadResponse {
// Custom implementation
}
public function extract(ExtractRequest $request): Generator {
// Custom implementation
}
}
Tag the service in services.yaml:
services:
App\PdfPack\Client\GhostscriptClient:
tags:
- { name: onetomany.pdfpack.client, key: ghostscript }
Response Transformers:
Extend ReadResponse or ExtractResponse to add custom methods:
class EnhancedExtractResponse extends \OneToMany\PdfPack\Response\ExtractResponse {
public function getTextByPage(int $pageNumber): string {
return $this->getPage($pageNumber)?->getData() ?? '';
}
}
Request Builders: Create fluent builders for complex requests:
class PdfExtractor {
public static function textFromPages(string $filePath, int $start, int $end): ExtractRequest {
return (new ExtractRequest($filePath))
->fromPage($start)
->toPage($end)
->asTextOutput();
}
}
Batch Processing: Process pages in chunks for large PDFs:
$batchSize = 10;
for ($page = 1; $page <= $totalPages; $page += $batchSize) {
$request = (new ExtractRequest($filePath))
->fromPage($page)
->toPage(min($page + $batchSize - 1, $totalPages));
// Process batch
}
Caching:
Cache metadata or extracted content (e.g., using Symfony’s Cache component or Laravel’s Cache):
$cacheKey = "pdf:{$filePath}:metadata";
$metadata = $this->cache->get($cacheKey, function() use ($filePath) {
return $this->readAction->act(new ReadRequest($filePath))->toArray();
});
Parallel Processing:
Use Symfony’s Parallel component or Laravel’s parallel package to process pages concurrently:
Parallel::run([
fn() => $this->processPage($extractAction, $filePath, 1),
fn() => $this->processPage($extractAction, $filePath, 2),
]);
How can I help you explore Laravel packages today?