page = 0;
$this->n = 2;
$this->buffer = '';
$this->pages = array();
$this->PageInfo = array();
$this->fonts = array();
$this->FontFiles = array();
$this->encodings = array();
$this->cmaps = array();
$this->images = array();
$this->links = array();
$this->InHeader = false;
$this->InFooter = false;
$this->lasth = 0;
$this->FontFamily = '';
$this->FontStyle = '';
$this->FontSizePt = 12;
$this->underline = false;
$this->DrawColor = '0 G';
$this->FillColor = '0 g';
$this->TextColor = '0 g';
$this->ColorFlag = false;
$this->WithAlpha = false;
$this->ws = 0;
$this->CoreFonts = array('courier', 'helvetica', 'times', 'symbol', 'zapfdingbats');
if($unit=='pt') $this->k = 1;
elseif($unit=='mm') $this->k = 72/25.4;
elseif($unit=='cm') $this->k = 72/2.54;
elseif($unit=='in') $this->k = 72;
else $this->Error('Incorrect unit: '.$unit);
$this->StdPageSizes = array(
'a3'=>array(841.89,1190.55),
'a4'=>array(595.28,841.89),
'a5'=>array(420.94,595.28),
'letter'=>array(612,792),
'legal'=>array(612,1008)
);
$size = $this->_getpagesize($size);
$this->DefPageSize = $size;
$this->CurPageSize = $size;
$orientation = strtolower($orientation);
if($orientation=='p' || $orientation=='portrait') {
$this->DefOrientation = 'P';
$this->w = $size[0];
$this->h = $size[1];
} elseif($orientation=='l' || $orientation=='landscape') {
$this->DefOrientation = 'L';
$this->w = $size[1];
$this->h = $size[0];
} else {
$this->Error('Incorrect orientation: '.$orientation);
}
$this->CurOrientation = $this->DefOrientation;
$this->wPt = $this->w*$this->k;
$this->hPt = $this->h*$this->k;
$this->CurRotation = 0;
$margin = 28.35/$this->k;
$this->SetMargins($margin,$margin);
$this->cMargin = $margin/10;
$this->LineWidth = .567/$this->k;
$this->SetAutoPageBreak(true,2*$margin);
$this->SetDisplayMode('default');
$this->SetCompression(true);
$this->metadata = array('Producer'=>'FPDF 1.86');
$this->PDFVersion = '1.3';
}
function SetMargins($left, $top, $right=null) {
$this->lMargin = $left;
$this->tMargin = $top;
if($right===null) $right = $left;
$this->rMargin = $right;
}
function SetAutoPageBreak($auto, $margin=0) {
$this->AutoPageBreak = $auto;
$this->bMargin = $margin;
$this->PageBreakTrigger = $this->h-$margin;
}
function SetDisplayMode($zoom, $layout='default') {
$this->ZoomMode = $zoom;
$this->LayoutMode = $layout;
}
function SetCompression($compress) {
$this->compress = $compress;
}
function AddPage($orientation='', $size='', $rotation=0) {
if($this->state==3) $this->Error('The document is closed');
$family = $this->FontFamily;
$style = $this->FontStyle.($this->underline ? 'U' : '');
$fontsize = $this->FontSizePt;
$lw = $this->LineWidth;
$dc = $this->DrawColor;
$fc = $this->FillColor;
$tc = $this->TextColor;
$cf = $this->ColorFlag;
if($this->page>0) {
$this->InFooter = true;
$this->Footer();
$this->InFooter = false;
$this->_endpage();
}
$this->_beginpage($orientation,$size,$rotation);
$this->_out('2 J');
$this->LineWidth = $lw;
$this->_out(sprintf('%.2F w',$lw*$this->k));
if($family) $this->SetFont($family,$style,$fontsize);
$this->DrawColor = $dc;
if($dc!='0 G') $this->_out($dc);
$this->FillColor = $fc;
if($fc!='0 g') $this->_out($fc);
$this->TextColor = $tc;
$this->ColorFlag = $cf;
$this->InHeader = true;
$this->Header();
$this->InHeader = false;
if($this->LineWidth!=$lw) {
$this->LineWidth = $lw;
$this->_out(sprintf('%.2F w',$lw*$this->k));
}
if($family) $this->SetFont($family,$style,$fontsize);
if($this->DrawColor!=$dc) {
$this->DrawColor = $dc;
$this->_out($dc);
}
if($this->FillColor!=$fc) {
$this->FillColor = $fc;
$this->_out($fc);
}
$this->TextColor = $tc;
$this->ColorFlag = $cf;
}
function Header() {}
function Footer() {}
function PageNo() { return $this->page; }
function Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='') {
if($this->state!=2) return;
if(!isset($this->images[$file])) {
if($type=='') {
$pos = strrpos($file,'.');
if(!$pos) $this->Error('Image file has no extension: '.$file);
$type = substr($file,$pos+1);
}
$type = strtolower($type);
if($type=='jpeg') $type = 'jpg';
$info = $this->_parsejpg($file);
$info['i'] = count($this->images)+1;
$this->images[$file] = $info;
} else {
$info = $this->images[$file];
}
if($w==0 && $h==0) {
$w = -96;
$h = -96;
}
if($w<0) $w = -$info['w']*72/$w/$this->k;
if($h<0) $h = -$info['h']*72/$h/$this->k;
if($w==0) $w = $h*$info['w']/$info['h'];
if($h==0) $h = $w*$info['h']/$info['w'];
if($y===null) {
if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak()) {
$x2 = $this->x;
$this->AddPage($this->CurOrientation,$this->CurPageSize,$this->CurRotation);
$this->x = $x2;
}
$y = $this->y;
$this->y += $h;
}
if($x===null) $x = $this->x;
$this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q',
$w*$this->k,$h*$this->k,$x*$this->k,($this->h-($y+$h))*$this->k,$info['i']));
}
function AcceptPageBreak() {
return $this->AutoPageBreak;
}
function Output($dest='', $name='', $isUTF8=false) {
if($this->state<3) $this->Close();
if(is_bool($dest)) $dest = $dest ? 'D' : 'F';
$dest = strtoupper($dest);
if($dest=='') {
if($name=='') {
$name = 'doc.pdf';
$dest = 'I';
} else {
$dest = 'F';
}
}
if(!$isUTF8) $name = utf8_encode($name);
switch($dest) {
case 'I':
$this->_checkoutput();
if(PHP_SAPI!='cli') {
if(headers_sent()) $this->Error('Some data already output');
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="'.$name.'"');
header('Cache-Control: private, max-age=0, must-revalidate');
header('Pragma: public');
}
echo $this->buffer;
break;
case 'D':
$this->_checkoutput();
if(headers_sent()) $this->Error('Some data already output');
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="'.$name.'"');
header('Cache-Control: private, max-age=0, must-revalidate');
header('Pragma: public');
echo $this->buffer;
break;
case 'F':
$f = fopen($name,'wb');
if(!$f) $this->Error('Unable to create output file: '.$name);
fwrite($f,$this->buffer,strlen($this->buffer));
fclose($f);
break;
case 'S':
return $this->buffer;
default:
$this->Error('Incorrect output destination: '.$dest);
}
return '';
}
protected function _getpagesize($size) {
if(is_string($size)) {
$size = strtolower($size);
if(!isset($this->StdPageSizes[$size]))
$this->Error('Unknown page size: '.$size);
$a = $this->StdPageSizes[$size];
return array($a[0]/$this->k, $a[1]/$this->k);
} else {
if($size[0]>$size[1])
return array($size[1], $size[0]);
else
return $size;
}
}
protected function _beginpage($orientation, $size, $rotation) {
$this->page++;
$this->pages[$this->page] = '';
$this->state = 2;
$this->x = $this->lMargin;
$this->y = $this->tMargin;
$this->FontFamily = '';
if($orientation=='') $orientation = $this->DefOrientation;
else $orientation = strtoupper($orientation[0]);
if($size=='') $size = $this->DefPageSize;
else $size = $this->_getpagesize($size);
if($orientation!=$this->CurOrientation || $size[0]!=$this->CurPageSize[0] || $size[1]!=$this->CurPageSize[1]) {
if($orientation=='P') {
$this->w = $size[0];
$this->h = $size[1];
} else {
$this->w = $size[1];
$this->h = $size[0];
}
$this->wPt = $this->w*$this->k;
$this->hPt = $this->h*$this->k;
$this->PageBreakTrigger = $this->h-$this->bMargin;
$this->CurOrientation = $orientation;
$this->CurPageSize = $size;
}
if($orientation!=$this->DefOrientation || $size[0]!=$this->DefPageSize[0] || $size[1]!=$this->DefPageSize[1])
$this->PageInfo[$this->page]['size'] = array($this->wPt, $this->hPt);
if($rotation!=0) {
if($rotation%90!=0) $this->Error('Incorrect rotation value: '.$rotation);
$this->CurRotation = $rotation;
$this->PageInfo[$this->page]['rotation'] = $rotation;
}
}
protected function _endpage() {
$this->state = 1;
}
protected function _parsejpg($file) {
$a = getimagesize($file);
if(!$a) $this->Error('Missing or incorrect image file: '.$file);
if($a[2]!=2) $this->Error('Not a JPEG file: '.$file);
if(!isset($a['channels']) || $a['channels']==3)
$colspace = 'DeviceRGB';
elseif($a['channels']==4)
$colspace = 'DeviceCMYK';
else
$colspace = 'DeviceGray';
$bpc = isset($a['bits']) ? $a['bits'] : 8;
$data = file_get_contents($file);
return array('w'=>$a[0], 'h'=>$a[1], 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'DCTDecode', 'data'=>$data);
}
protected function _out($s) {
if($this->state==2)
$this->pages[$this->page] .= $s."\n";
elseif($this->state==1)
$this->_put($s);
elseif($this->state==0)
$this->Error('No page has been added yet');
elseif($this->state==3)
$this->Error('The document is closed');
}
protected function _put($s) {
$this->buffer .= $s."\n";
}
protected function _putpages() {
$nb = $this->page;
for($n=1;$n<=$nb;$n++)
$this->PageInfo[$n]['n'] = $this->n+1+2*($n-1);
for($n=1;$n<=$nb;$n++)
$this->_putpage($n);
$this->_put('1 0 obj');
$this->_put('<PageInfo[$n]['n'].' 0 R ';
$this->_put($kids.']');
$this->_put('/Count '.$nb);
if($this->DefOrientation=='P') {
$w = $this->DefPageSize[0];
$h = $this->DefPageSize[1];
} else {
$w = $this->DefPageSize[1];
$h = $this->DefPageSize[0];
}
$this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]',$w*$this->k,$h*$this->k));
$this->_put('>>');
$this->_put('endobj');
}
protected function _putpage($n) {
$this->_newobj();
$this->_put('<_put('/Parent 1 0 R');
if(isset($this->PageInfo[$n]['size']))
$this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->PageInfo[$n]['size'][0],$this->PageInfo[$n]['size'][1]));
if(isset($this->PageInfo[$n]['rotation']))
$this->_put('/Rotate '.$this->PageInfo[$n]['rotation']);
$this->_put('/Resources 2 0 R');
$this->_put('/Contents '.($this->n+1).' 0 R>>');
$this->_put('endobj');
$this->_newobj();
$p = $this->pages[$n];
$this->_put('<>');
$this->_putstream($p);
$this->_put('endobj');
}
protected function _putimages() {
foreach(array_keys($this->images) as $file) {
$this->_putimage($this->images[$file]);
unset($this->images[$file]['data']);
}
}
protected function _putimage(&$info) {
$this->_newobj();
$info['n'] = $this->n;
$this->_put('<_put('/Subtype /Image');
$this->_put('/Width '.$info['w']);
$this->_put('/Height '.$info['h']);
$this->_put('/ColorSpace /'.$info['cs']);
if($info['cs']=='DeviceCMYK')
$this->_put('/Decode [1 0 1 0 1 0 1 0]');
$this->_put('/BitsPerComponent '.$info['bpc']);
if(isset($info['f']))
$this->_put('/Filter /'.$info['f']);
$this->_put('/Length '.strlen($info['data']).'>>');
$this->_putstream($info['data']);
$this->_put('endobj');
}
protected function _newobj() {
$this->n++;
$this->offsets[$this->n] = strlen($this->buffer);
$this->_put($this->n.' 0 obj');
}
protected function _putstream($data) {
$this->_put('stream');
$this->_put($data);
$this->_put('endstream');
}
protected function _checkoutput() {
if(PHP_SAPI!='cli') {
if(headers_sent($file,$line))
$this->Error("Some data has already been output, can't send PDF file (output started at $file:$line)");
}
if(ob_get_length()) {
if(preg_match('/^(\xEF\xBB\xBF)?\s*$/',ob_get_contents())) {
ob_end_clean();
} else {
$this->Error("Some data has already been output, can't send PDF file");
}
}
}
function SetFont($family, $style='', $size=0) {
$family = strtolower($family);
if($family=='') $family = $this->FontFamily;
if($family=='arial') $family = 'helvetica';
$this->FontFamily = $family;
$this->FontStyle = strtoupper($style);
if($size==0) $size = $this->FontSizePt;
$this->FontSizePt = $size;
$this->FontSize = $size/$this->k;
$this->CurrentFont = array('family'=>$family,'style'=>$this->FontStyle,'size'=>$size);
}
function Close() {
if($this->state==3) return;
if($this->page==0) $this->AddPage();
$this->InFooter = true;
$this->Footer();
$this->InFooter = false;
$this->_endpage();
$this->_putimages();
$this->_putpages();
$this->_putresources();
$this->_putinfo();
$this->_putcatalog();
$this->_putheader();
$this->_puttrailer();
$this->state = 3;
}
protected function _putresources() {
$this->_put('2 0 obj');
$this->_put('<<');
$this->_putfonts();
$this->_putimagedict();
$this->_put('>>');
$this->_put('endobj');
}
protected function _putfonts() {
$this->_put('/Font <<');
$this->_put('/F1 <<');
$this->_put('/Type /Font');
$this->_put('/Subtype /Type1');
$this->_put('/BaseFont /Helvetica');
$this->_put('>>');
$this->_put('>>');
}
protected function _putimagedict() {
if(!empty($this->images)) {
$this->_put('/XObject <<');
foreach($this->images as $image)
$this->_put('/I'.$image['i'].' '.$image['n'].' 0 R');
$this->_put('>>');
}
}
protected function _putinfo() {
$info = '<<';
foreach($this->metadata as $key=>$value)
$info .= '/'.$key.' '.$this->_textstring($value);
$info .= '/CreationDate '.$this->_textstring('D:'.@date('YmdHis'));
$info .= '>>';
$this->_put($info);
}
protected function _putcatalog() {
$this->_put('/Type /Catalog');
$this->_put('/Pages 1 0 R');
}
protected function _putheader() {
$this->buffer = '%PDF-'.$this->PDFVersion."\n".$this->buffer;
}
protected function _puttrailer() {
$this->_put('xref');
$this->_put('0 '.($this->n+1));
$this->_put('0000000000 65535 f ');
for($i=1;$i<=$this->n;$i++)
$this->_put(sprintf('%010d 00000 n ',$this->offsets[$i]));
$this->_put('trailer');
$this->_put('<<');
$this->_put('/Size '.($this->n+1));
$this->_put('/Root '.$this->n.' 0 R');
$this->_put('/Info '.($this->n-1).' 0 R');
$this->_put('>>');
$this->_put('startxref');
$this->_put(strlen($this->buffer));
$this->_put('%%EOF');
$this->state = 3;
}
protected function _textstring($s) {
return '('.$this->_escape($s).')';
}
protected function _escape($s) {
$s = str_replace('\\','\\\\',$s);
$s = str_replace('(','\(',$s);
$s = str_replace(')','\)',$s);
$s = str_replace("\r",'\r',$s);
return $s;
}
function Error($msg) {
throw new Exception('FPDF error: '.$msg);
}
}
// ============================================================================
// PDF CONVERTER CLASS
// ============================================================================
class PDFConverter {
private $inputPdf;
private $outputDir;
private $prefix;
private $dpi;
private $quality;
private $jpegFiles = [];
private $errors = [];
public function __construct($inputPdf, $dpi = 300, $quality = 95, $outputDir = null, $prefix = null) {
if (!extension_loaded('imagick')) {
throw new Exception("Imagick extension not installed. Install with: sudo apt-get install php-imagick");
}
if (!file_exists($inputPdf)) {
throw new Exception("PDF file not found: $inputPdf");
}
$this->inputPdf = $inputPdf;
$this->dpi = max(72, min(1200, $dpi));
$this->quality = max(1, min(100, $quality));
$this->outputDir = $outputDir ?? dirname($inputPdf);
if (!is_dir($this->outputDir)) {
mkdir($this->outputDir, 0755, true);
}
$this->prefix = $prefix ?? pathinfo($inputPdf, PATHINFO_FILENAME);
}
public function convertToJpegs() {
try {
$imagick = new Imagick();
$imagick->setResolution($this->dpi, $this->dpi);
$imagick->readImage($this->inputPdf);
$imagick = $imagick->coalesceImages();
$pageNum = 1;
foreach ($imagick as $page) {
$page->setImageFormat('jpeg');
$page->setImageCompression(Imagick::COMPRESSION_JPEG);
$page->setImageCompressionQuality($this->quality);
$page->setImageBackgroundColor('white');
$page = $page->flattenImages();
$filename = sprintf("%s_page_%03d.jpg", $this->prefix, $pageNum);
$filepath = $this->outputDir . DIRECTORY_SEPARATOR . $filename;
$page->writeImage($filepath);
$this->jpegFiles[] = $filepath;
$pageNum++;
}
$imagick->clear();
$imagick->destroy();
return $this->jpegFiles;
} catch (Exception $e) {
throw new Exception("Error converting PDF to JPEGs: " . $e->getMessage());
}
}
public function createPdfFromJpegs($pageSize = 'auto') {
if (empty($this->jpegFiles)) {
throw new Exception("No JPEG files to convert.");
}
$outputPdf = $this->outputDir . DIRECTORY_SEPARATOR . $this->prefix . "_reconstructed.pdf";
try {
$pdf = new FPDF();
foreach ($this->jpegFiles as $index => $jpegFile) {
list($imgWidth, $imgHeight) = getimagesize($jpegFile);
if ($pageSize === 'auto') {
$pageWidth = ($imgWidth / $this->dpi) * 25.4;
$pageHeight = ($imgHeight / $this->dpi) * 25.4;
$orientation = $pageWidth > $pageHeight ? 'L' : 'P';
} elseif ($pageSize === 'letter') {
$pageWidth = 215.9;
$pageHeight = 279.4;
$orientation = 'P';
} elseif ($pageSize === 'A4') {
$pageWidth = 210;
$pageHeight = 297;
$orientation = 'P';
} else {
$pageWidth = 210;
$pageHeight = 297;
$orientation = 'P';
}
$pdf->AddPage($orientation, [$pageWidth, $pageHeight]);
$margin = 5;
$maxWidth = $pageWidth - (2 * $margin);
$maxHeight = $pageHeight - (2 * $margin);
$imgRatio = $imgWidth / $imgHeight;
$pageRatio = $maxWidth / $maxHeight;
if ($imgRatio > $pageRatio) {
$scaledWidth = $maxWidth;
$scaledHeight = $maxWidth / $imgRatio;
} else {
$scaledHeight = $maxHeight;
$scaledWidth = $maxHeight * $imgRatio;
}
$x = ($pageWidth - $scaledWidth) / 2;
$y = ($pageHeight - $scaledHeight) / 2;
$pdf->Image($jpegFile, $x, $y, $scaledWidth, $scaledHeight);
}
$pdf->Output('F', $outputPdf);
return $outputPdf;
} catch (Exception $e) {
throw new Exception("Error creating PDF: " . $e->getMessage());
}
}
public function convert($skipReconstruction = false, $pageSize = 'auto') {
$jpegs = $this->convertToJpegs();
$pdf = null;
if (!$skipReconstruction) {
$pdf = $this->createPdfFromJpegs($pageSize);
}
return ['jpegs' => $jpegs, 'pdf' => $pdf];
}
public function getJpegFiles() {
return $this->jpegFiles;
}
}
// ============================================================================
// COMMAND LINE INTERFACE
// ============================================================================
if (php_sapi_name() === 'cli') {
$options = getopt("", [
"input:",
"dpi::",
"quality::",
"output-dir::",
"prefix::",
"skip-reconstruction",
"pagesize::",
"help"
]);
if (isset($options['help']) || !isset($options['input'])) {
echo <<
convertToJpegs();
echo "✓ Created " . count($jpegs) . " JPEG file(s)\n";
foreach ($jpegs as $jpeg) {
echo " - " . basename($jpeg) . "\n";
}
if (!isset($options['skip-reconstruction'])) {
echo "\nCreating PDF from JPEGs...\n";
$pdf = $converter->createPdfFromJpegs($options['pagesize'] ?? 'auto');
echo "✓ Created PDF: " . basename($pdf) . "\n";
}
echo "\n" . str_repeat("=", 70) . "\n";
echo "✓ Conversion completed successfully!\n";
echo str_repeat("=", 70) . "\n\n";
} catch (Exception $e) {
echo "\n✗ Error: " . $e->getMessage() . "\n\n";
exit(1);
}
exit(0);
}
// ============================================================================
// WEB INTERFACE
// ============================================================================
$message = '';
$messageType = '';
$files = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['pdf_file'])) {
try {
if ($_FILES['pdf_file']['error'] !== UPLOAD_ERR_OK) {
throw new Exception('File upload error');
}
$uploadedFile = $_FILES['pdf_file'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $uploadedFile['tmp_name']);
finfo_close($finfo);
if ($mimeType !== 'application/pdf') {
throw new Exception('Only PDF files are allowed');
}
$dpi = isset($_POST['dpi']) ? (int)$_POST['dpi'] : 300;
$quality = isset($_POST['quality']) ? (int)$_POST['quality'] : 95;
$prefix = isset($_POST['prefix']) && $_POST['prefix'] !== '' ? $_POST['prefix'] : null;
$skipReconstruction = isset($_POST['skip_reconstruction']);
$pageSize = isset($_POST['pagesize']) ? $_POST['pagesize'] : 'auto';
$outputDir = __DIR__ . '/converted';
if (!is_dir($outputDir)) {
mkdir($outputDir, 0755, true);
}
$tempFile = $outputDir . '/' . basename($uploadedFile['name']);
move_uploaded_file($uploadedFile['tmp_name'], $tempFile);
$converter = new PDFConverter($tempFile, $dpi, $quality, $outputDir, $prefix);
$result = $converter->convert($skipReconstruction, $pageSize);
foreach ($result['jpegs'] as $jpeg) {
$files[] = [
'name' => basename($jpeg),
'path' => 'converted/' . basename($jpeg),
'size' => filesize($jpeg),
'type' => 'image'
];
}
if ($result['pdf']) {
$files[] = [
'name' => basename($result['pdf']),
'path' => 'converted/' . basename($result['pdf']),
'size' => filesize($result['pdf']),
'type' => 'pdf'
];
}
unlink($tempFile);
$message = 'Conversion completed successfully!';
$messageType = 'success';
} catch (Exception $e) {
$message = 'Error: ' . $e->getMessage();
$messageType = 'error';
}
}
?>
PDF to JPEG to PDF Converter
📄 PDF to JPEG Converter
All-in-One PHP Tool - No Dependencies Required
How it works: Upload a PDF and convert each page to high-quality JPEG images.
Optionally combine them back into a single PDF.
Command Line Usage:
php --input=document.pdf --help