Last active
August 18, 2023 13:50
-
-
Save zb3/36ed58f8d9ba8d711f9f555bc613f733 to your computer and use it in GitHub Desktop.
Merge PDF files with custom titles using Ghostscript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// merge_pdf_files(['file1.pdf', 'file2.pdf', 'file3.pdf'], ["title a", "title b", "title c"], "outfile.pdf") | |
function encode_ps_base($str) { // <hex encoded UTF-16BE with BOM> | |
return '<'.bin2hex($str).'>'; | |
} | |
function encode_ps_utf16($str) { // <hex encoded UTF-16BE with BOM> | |
$utf16be = "\xFE\xFF".mb_convert_encoding($str, 'UTF-16BE', 'UTF-8'); | |
return encode_ps_base($utf16be); | |
} | |
function merge_pdf_files($files, $titles, $outfile) { | |
$cmd = 'gs -q -dNOSAFER -sDEVICE=pdfwrite -o '.escapeshellarg($outfile).' - 2>&1'; | |
$pipes = []; | |
$proc = proc_open($cmd, [ | |
0 => ['pipe', 'r'], | |
1 => ['pipe', 'w'], | |
], $pipes); | |
if (!is_resource($proc)) { | |
return false; | |
} | |
$encoded_files = implode(' ', array_map('encode_ps_base', $files)); | |
$encoded_titles = implode(' ', array_map('encode_ps_utf16', $titles)); | |
$script = <<<SCRIPT | |
/files [{$encoded_files}] def | |
/titles [{$encoded_titles}] def | |
/fileCount files length def | |
0 1 fileCount 1 sub { | |
/index exch def | |
files index get /fileName exch def | |
titles index get /title exch def | |
/pagesDone currentpagedevice /PageCount get 1 add def | |
fileName run [ /Page pagesDone /Title title /OUT pdfmark | |
} for | |
SCRIPT; | |
fwrite($pipes[0], $script); | |
fclose($pipes[0]); | |
$err = stream_get_contents($pipes[1]); | |
fclose($pipes[1]); | |
$exit_code = proc_close($proc); | |
if ($exit_code) { | |
trigger_error("Ghostscript error: $err", E_USER_WARNING); | |
} | |
return $exit_code === 0; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
% gs -dNOSAFER -sDEVICE=pdfwrite -o finished.pdf mergepdf.ps | |
% replace these | |
/files [(file1.pdf) (file2.pdf) (file3.pdf)] def | |
% might want to use hex encoding, but then it must be UTF-16BE with BOM | |
/titles [(title1) (title2) (title3)] def | |
/fileCount files length def | |
0 1 fileCount 1 sub { | |
/index exch def | |
files index get /fileName exch def | |
titles index get /title exch def | |
/pagesDone currentpagedevice /PageCount get 1 add def | |
% "[" is a ... mark operator, so it doesn't necessarily start the array | |
% so it's like the "pdfmark" pops it, maybe "]pdfmark" 'd make it more clear | |
fileName run [ /Page pagesDone /Title title /OUT pdfmark | |
} for | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment