addOption('dry-run', null, InputOption::VALUE_NONE, 'Show what would be compressed without actually doing it') ; } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $dryRun = $input->getOption('dry-run'); // Check if qpdf is installed exec('which qpdf', $qpdfPath, $returnCode); if (0 !== $returnCode) { $io->error('qpdf is not installed. Run: sudo apt install qpdf'); return Command::FAILURE; } $documents = $this->documentRepository->findBy(['mimeType' => 'application/pdf']); if (empty($documents)) { $io->info('No PDF documents found.'); return Command::SUCCESS; } $io->title('PDF Compression'); $io->text(sprintf('Found %d PDF documents', count($documents))); $totalSaved = 0; $compressed = 0; foreach ($documents as $document) { $path = $document->getPath(); if ($this->storageService->isBase64DataUri($path)) { $this->compressBase64Document($document, $path, $dryRun, $io, $totalSaved, $compressed); } else { $this->compressFileDocument($document, $path, $dryRun, $io, $totalSaved, $compressed); } } if (!$dryRun && $compressed > 0) { $this->em->flush(); $io->success(sprintf( 'Compressed %d/%d PDFs. Total space saved: %s', $compressed, count($documents), $this->formatBytes($totalSaved) )); } elseif ($dryRun) { $io->info('Dry run completed. No changes made.'); } else { $io->info('No PDFs needed compression.'); } return Command::SUCCESS; } private function compressBase64Document( Document $document, string $path, bool $dryRun, SymfonyStyle $io, int &$totalSaved, int &$compressed, ): void { $base64Data = $path; if (str_contains($base64Data, ',')) { $base64Data = explode(',', $base64Data, 2)[1]; } $pdfContent = base64_decode($base64Data, true); if (false === $pdfContent) { $io->warning(sprintf('Failed to decode document: %s', $document->getName())); return; } $originalSize = strlen($pdfContent); if ($dryRun) { $io->text(sprintf( ' [DRY-RUN] Would compress (base64): %s (%s)', $document->getName(), $this->formatBytes($originalSize) )); return; } $result = $this->pdfCompressor->compressBase64Pdf($path); if (null !== $result) { $document->setPath($result['path']); $document->setSize($result['size']); $totalSaved += $result['saved']; ++$compressed; $io->text(sprintf( ' OK %s: %s → %s (-%s, -%.1f%%)', $document->getName(), $this->formatBytes($result['originalSize']), $this->formatBytes($result['size']), $this->formatBytes($result['saved']), ($result['saved'] / $result['originalSize']) * 100 )); } else { $io->text(sprintf( ' - %s: Already optimal (%s)', $document->getName(), $this->formatBytes($originalSize) )); } } private function compressFileDocument( Document $document, string $path, bool $dryRun, SymfonyStyle $io, int &$totalSaved, int &$compressed, ): void { $absolutePath = $this->storageService->getAbsolutePath($path); if (!file_exists($absolutePath)) { $io->warning(sprintf('File not found: %s (%s)', $document->getName(), $path)); return; } $originalSize = filesize($absolutePath); if (false === $originalSize) { return; } if ($dryRun) { $io->text(sprintf( ' [DRY-RUN] Would compress (file): %s (%s)', $document->getName(), $this->formatBytes($originalSize) )); return; } $result = $this->pdfCompressor->compressFile($absolutePath); if (null !== $result) { $document->setSize($result['size']); $totalSaved += $result['saved']; ++$compressed; $io->text(sprintf( ' OK %s: %s → %s (-%s, -%.1f%%)', $document->getName(), $this->formatBytes($result['originalSize']), $this->formatBytes($result['size']), $this->formatBytes($result['saved']), ($result['saved'] / $result['originalSize']) * 100 )); } else { $io->text(sprintf( ' - %s: Already optimal (%s)', $document->getName(), $this->formatBytes($originalSize) )); } } private function formatBytes(int $bytes): string { $units = ['B', 'KB', 'MB', 'GB']; $i = 0; while ($bytes >= 1024 && $i < count($units) - 1) { $bytes /= 1024; ++$i; } return round($bytes, 2).' '.$units[$i]; } }