diff options
author | Reinhard Kotucha <reinhard.kotucha@web.de> | 2015-05-05 21:26:28 +0000 |
---|---|---|
committer | Reinhard Kotucha <reinhard.kotucha@web.de> | 2015-05-05 21:26:28 +0000 |
commit | c51296e843e35a3d133557000409674baae01b54 (patch) | |
tree | 571fdfb2f2660b3c7d6bd5d522a1268a776f8ab4 | |
parent | fe576e3d01b31dbc35e1bc4008052c06c47d3cf3 (diff) |
texlua scripts revised
pdfopt.tlu is using -dFastWebView now instead of pdfopt.ps
git-svn-id: svn://tug.org/texlive/trunk@37204 c570f23f-e606-0410-a88d-b1316a301751
-rwxr-xr-x | Master/texmf-dist/scripts/tlgs/gswin32/eps2eps.tlu | 116 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/tlgs/gswin32/pdf2dsc.tlu | 32 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/tlgs/gswin32/pdfopt.tlu | 147 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/tlgs/gswin32/ps2ascii.tlu | 140 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf.tlu | 163 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf12.tlu | 163 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf13.tlu | 163 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf14.tlu | 163 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/tlgs/gswin32/ps2ps.tlu | 117 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/tlgs/gswin32/ps2ps2.tlu | 79 | ||||
-rw-r--r-- | Master/tlpkg/tlgs/lib/pdfopt.ps | 1272 |
11 files changed, 860 insertions, 1695 deletions
diff --git a/Master/texmf-dist/scripts/tlgs/gswin32/eps2eps.tlu b/Master/texmf-dist/scripts/tlgs/gswin32/eps2eps.tlu index 715e7c1ff25..43701447a0d 100755 --- a/Master/texmf-dist/scripts/tlgs/gswin32/eps2eps.tlu +++ b/Master/texmf-dist/scripts/tlgs/gswin32/eps2eps.tlu @@ -2,12 +2,22 @@ --*-Lua-*- -- $Id$ --- Copyright (C) 2008 Reinhard Kotucha. +-- Copyright (C) 2008-2015 Reinhard Kotucha. -- You may freely use, modify and/or distribute this file. -- Replacement for eps2eps.bat. -- 'Distill' Encapsulated PostScript. + +local function usage (progname) + local message= + 'Usage: '..progname..' [options] <inputfile> <outputfile>\n'.. + ' <inputfile> can be either a PS, EPS, PDF file, or stdin.\n'.. + ' A single hyphen (-) denotes stdin.\n' + return message +end + + function fixwin(args_unix) if os.type == 'windows' then local args_win={} -- new table @@ -21,41 +31,103 @@ function fixwin(args_unix) end end -if os.type == 'windows' then - gs='gswin32c' -else - gs='gs' + +local function push (t, ...) + local args={...} + for _,v in ipairs(args) do + if type(v) == 'table' then + for _,x in ipairs(v) do + t[#t+1]=x + end + else + t[#t+1]=v + end + end end -command={gs, '-q', '-sDEVICE=eps2write'} -if os.type=='unix' then - command[#command+1]='-sstdout=%stderr' +local function filename (file) + -- strip path + if string.find(file, '[/\\]') then + return string.match(file, '.*[/\\](.*)$') + else + return file + end end -for _,v in ipairs{'-dNOPAUSE', '-sOutputFile='..arg[#arg], - '-dBATCH', '-dSAFER', '-dDEVICEWIDTH=250000', - '-dDEVICEHEIGHT=250000'} do - command[#command+1]=v +local function basename (file) + -- strip extension + return string.match(filename(file), '(.*)%..*') end -files=0 +local function extension (file) + -- return extension if available, false otherwise. + local fn=filename(file) + if fn:find('%.') then + return string.match(fn, '.*%.(.*)') + else + return false + end +end + + +local function parse_cmdline () + local files={} + local options={} + + local progname + local basename=filename(arg[0]) + if basename:find('%.') then + progname=basename:match('(.*)%..*') + else + progname=basename + end -for i=1, #arg do - if not string.find(arg[i], '^%-') then - files=files+1 + for i=1, #arg do + if string.find(arg[i], '^%-.+') then + push(options, arg[i]) + else + push(files, arg[i]) + end end - command[#command+1]=arg[i] + files.input =files[1] + files.output=files[2] + + return progname, options, files end -if files < 2 then - io.stderr:write('Usage: eps2eps [options] input.eps output.eps\n') - os.exit(1) + +local function check_files (progname, files) + if #files < 2 then + io.stderr:write(usage(progname)) + os.exit(1) + end end -command[#command]=nil -- already processed at -sOutputFile +-- main -- + +-- parse command line +local progname, options, file=parse_cmdline() + +check_files (progname, file) + +-- setup command + +local command={} +if os.type == 'unix' then command={'gs'} else command={'gswin32c'} end + +push(command, '-q', '-sDEVICE=eps2write') + +if os.type=='unix' then push (command, '-sstdout=%stderr') end + +push(command, '-sOutputFile='..file.output, '-dNOPAUSE', + '-dBATCH', '-P-', '-dSAFER', '-dDEVICEWIDTH=250000', + '-dDEVICEHEIGHT=250000') + +push(command, options) +push(command, file.input) command=fixwin(command) @@ -63,7 +135,7 @@ command=fixwin(command) for i=0, #command do print (command[i]) end -os.exit(ret) +os.exit(0) --]] ret=os.spawn(command) diff --git a/Master/texmf-dist/scripts/tlgs/gswin32/pdf2dsc.tlu b/Master/texmf-dist/scripts/tlgs/gswin32/pdf2dsc.tlu index 25921db23ea..5a6cd479a7e 100755 --- a/Master/texmf-dist/scripts/tlgs/gswin32/pdf2dsc.tlu +++ b/Master/texmf-dist/scripts/tlgs/gswin32/pdf2dsc.tlu @@ -21,13 +21,22 @@ function fixwin (args_unix) end end -if os.type == 'windows' then - gs='gswin32c' -else - gs='gs' + +local function push (t, ...) + local args={...} + for _,v in ipairs(args) do + if type(v) == 'table' then + for _,x in ipairs(v) do + t[#t+1]=x + end + else + t[#t+1]=v + end + end end -if #arg == 0 or #arg > 2 then + +if #arg ~= 2 then io.stderr:write('Usage: pdf2dsc input.pdf [output.dsc]\n') os.exit(1) end @@ -39,9 +48,14 @@ else dscfile=string.gsub(pdffile, '%.%a+$', '.dsc') end -command={gs, '-q', '-dNODISPLAY', '-dSAFER', '-dDELAYSAFER', - '-sPDFname='..pdffile, '-sDSCname='..dscfile, 'pdf2dsc.ps', - '-c', 'quit'} +-- setup command + +local command={} +if os.type == 'unix' then command={'gs'} else command={'gswin32c'} end + +push(command, '-q', '-dNODISPLAY', '-dSAFER', '-dDELAYSAFER', + '-sPDFname='..pdffile, '-sDSCname='..dscfile, 'pdf2dsc.ps', + '-c', 'quit') command=fixwin(command) @@ -49,7 +63,7 @@ command=fixwin(command) for i=0, #command do print (command[i]) end -os.exit(ret) +os.exit(0) --]] ret=os.spawn(command) diff --git a/Master/texmf-dist/scripts/tlgs/gswin32/pdfopt.tlu b/Master/texmf-dist/scripts/tlgs/gswin32/pdfopt.tlu index d267d55f057..d694047cfff 100755 --- a/Master/texmf-dist/scripts/tlgs/gswin32/pdfopt.tlu +++ b/Master/texmf-dist/scripts/tlgs/gswin32/pdfopt.tlu @@ -2,13 +2,24 @@ --*-Lua-*- -- $Id$ --- Copyright (C) 2008 Reinhard Kotucha. +-- Copyright (C) 2008-2015 Reinhard Kotucha. -- You may freely use, modify and/or distribute this file. --- Replacement for pdfopt.bat. --- Linearize PDF +-- Generic wrapper for ps2pdf{,12,13,14} and pdfopt. -function fixwin (args_unix) + +local function usage (progname) + local message= + 'Usage: '..progname..' [options] <inputfile> <outputfile>\n\n'.. + ' <inputfile> can be either a PS, EPS, or PDF file.\n'.. + ' A single hyphen (-) denotes stdin.\n\n'.. + ' <outputfile> is required if <inputfile> is a PDF file\n'.. + ' or input is read from stdin.\n' + return message +end + + +local function fixwin (args_unix) if os.type == 'windows' then local args_win={} -- new table args_win[0]=args_unix[1] @@ -21,43 +32,133 @@ function fixwin (args_unix) end end -if os.type == 'windows' then - gs='gswin32c' -else - gs='gs' + +local function push (t, ...) + local args={...} + for _,v in ipairs(args) do + if type(v) == 'table' then + for _,x in ipairs(v) do + t[#t+1]=x + end + else + t[#t+1]=v + end + end +end + + +local function filename (file) + -- strip path + if string.find(file, '[/\\]') then + return string.match(file, '.*[/\\](.*)$') + else + return file + end +end + + +local function basename (file) + -- strip extension + return string.match(filename(file), '(.*)%..*') +end + + +local function extension (file) + -- return extension if available, false otherwise. + local fn=filename(file) + if fn:find('%.') then + return string.match(fn, '.*%.(.*)') + end end -command={gs, '-q', '-dNODISPLAY', '-dSAFER', '-dDELAYSAFER'} -files={} +local function parse_cmdline () + local files={} + local options={} -for i=1, #arg do - if string.find(arg[i], '^%-') then - command[#command+1]=arg[i] + local progname + local basename=filename(arg[0]) + if basename:find('%.') then + progname=basename:match('(.*)%..*') else - files[#files+1]=arg[i] + progname=basename end + + for i=1, #arg do + if string.find(arg[i], '^%-.+') then + push(options, arg[i]) + else + push(files, arg[i]) + end + end + files.input =files[1] + files.output=files[2] + return progname, options, files end -command[#command+1]='--' -command[#command+1]='pdfopt.ps' -if #files ~= 2 then - io.stderr:write('Usage: pdfopt [options] input.pdf output.pdf\n') - os.exit(1) -else - for i=1, #files do - command[#command+1]=files[i] +local function check_files (progname, files, default_output_ext) + if #files < 1 then + io.stderr:write(usage(progname)) + os.exit(1) + end + + if #files < 2 then + if extension(files.input) then + local ext=string.lower(extension(files.input)) + if ext:match(default_output_ext..'$') then + io.stderr:write('!Error: no output file specified.\n\n') + io.stderr:write(usage(progname)) + os.exit(1) + else + files.output=basename(files.input)..'.'..default_output_ext + end + else -- the input file has no extension + files.output=files.input..'.'..default_output_ext + end end end +-- main -- + +-- parse command line +local progname, options, file=parse_cmdline() + +check_files(progname, file, 'pdf') + +-- determine PDF level +local PDF_level='1.4' -- default, may be changed in the future +if progname == 'ps2pdf12' then PDF_level='1.2' +elseif progname == 'ps2pdf13' then PDF_level='1.3' +elseif progname == 'ps2pdf14' then PDF_level='1.4' +end + +-- setup command + +local command={} +if os.type == 'unix' then command={'gs'} else command={'gswin32c'} end + +push(command, '-P-', '-dSAFER', options, '-dCompatibilityLevel='..PDF_level) + +if progname == 'pdfopt' then push(command, '-dFastWebView=true') end + +push(command, '-q', '-P-', '-dNOPAUSE', '-dBATCH', '-sDEVICE=pdfwrite') + +if os.type == 'unix' then push(command, '-sstdout=%stderr') end + +push(command, '-sOutputFile='..file.output, '-P-', '-dSAFER') + +-- We have to include the options twice because -I only takes effect +-- if it appears before other options [see ps2pdfwr]. +push(command, options, '-c', '.setpdfwrite', '-f', file.input) + command=fixwin(command) --[[ prepend an additional hyphen to activate this code for i=0, #command do print (command[i]) end -os.exit(ret) +os.exit(0) --]] ret=os.spawn(command) diff --git a/Master/texmf-dist/scripts/tlgs/gswin32/ps2ascii.tlu b/Master/texmf-dist/scripts/tlgs/gswin32/ps2ascii.tlu index 2bc31ef26d9..d23c0458ea8 100755 --- a/Master/texmf-dist/scripts/tlgs/gswin32/ps2ascii.tlu +++ b/Master/texmf-dist/scripts/tlgs/gswin32/ps2ascii.tlu @@ -2,26 +2,55 @@ --*-Lua-*- -- $Id$ --- Copyright (C) 2008 Reinhard Kotucha. +-- Copyright (C) 2008-2015 Reinhard Kotucha. -- You may freely use, modify and/or distribute this file. -- Replacement for ps2ascii.bat. --- Convert PostScript to ASCII - -function fixwin (args_unix) - if os.type == 'windows' then - local args_win={} -- new table - args_win[0]=args_unix[1] - for i=1, #args_unix do - args_win[i]='"'..args_unix[i]..'"' + +-- Extract ASCII text from a PostScript file. Usage: +-- +-- ps2ascii [infile.ps [outfile.txt]] +-- +-- If outfile is omitted, output goes to stdout. +-- If both infile and outfile are omitted, ps2ascii acts as a filter, +-- reading from stdin and writing on stdout. + + +-- We have to pass the command as a string to os.execute() because we +-- need a shell for i/o-redirection. But we create a table first and +-- convert it to a string, just to make sure we don't miss any spaces. + +local function push (t, ...) + local args={...} + for _,v in ipairs(args) do + if type(v) == 'table' then + for _,x in ipairs(v) do + t[#t+1]=x + end + else + t[#t+1]=v end - return args_win + end +end + + +local function filename (file) + -- strip path + if string.find(file, '[/\\]') then + return string.match(file, '.*[/\\](.*)$') else - return args_unix + return file end end -function remove_tmpfiles (tmpfiles) + +local function remove_tmpfiles (tmpfiles) + -- The shell script contains + -- + -- trap "rm -f _temp_.err _temp_.out" 0 1 2 15 + -- + -- texlua doesn't support signals (yet). So we remove temporary files + -- if possible. for i=1, #tmpfiles do if lfs.isfile(tmpfiles[i]) then os.remove(tmpfiles[i]) @@ -29,46 +58,71 @@ function remove_tmpfiles (tmpfiles) end end -if os.type == 'windows' then - gs='gswin32c' -else - gs='gs' -end -command={gs, '-q', '-dNODISPLAY', '-dBATCH', '-dSAFER', '-dDELAYBIND', - '-dWRITESYSTEMDICT', '-dSIMPLE', '-c', 'save', - '-f', 'ps2ascii.ps'} +local function parse_cmdline () + local files={} + local options={} -if #arg < 2 then - if #arg == 0 then - command[#command+1]='-' - elseif #arg == 1 then - command[#command+1]=arg[1] + local progname + local basename=filename(arg[0]) + if basename:find('%.') then + progname=basename:match('(.*)%..*') + else + progname=basename end - command=fixwin(command) -elseif #arg == 2 then - -- We need a shell for I/O redirection. - command=gs..' -q -dNODISPLAY -dBATCH -dSAFER -dDELAYBIND '.. - '-dWRITESYSTEMDICT -dSIMPLE -c save '.. - '-f ps2ascii.ps "'..arg[1]..'" > "'..arg[2]..'"' -end ---[[ prepend an additional hyphen to activate this code -if type(command) == 'string' then - print(command) -else - for i=0, #command do - print (command[i]) + for i=1, #arg do + if string.find(arg[i], '^%-.+') then + push(options, arg[i]) + else + push(files, arg[i]) + end end + files.input =files[1] + files.output=files[2] + return progname, options, files end -os.exit(ret) ---]] -if type(command) == 'string' then - ret=os.execute(command) + +-- main -- + +local progname, options, files=parse_cmdline () + +-- setup command + +local command={gs} +if os.type == 'unix' then command={'gs'} else command={'gswin32c'} end + +push(command, {'-q', '-dNODISPLAY', '-P-', '-dSAFER', '-dDELAYBIND', + '-dWRITESYSTEMDICT', '-dSIMPLE'}) + +push(command, '-c', 'save', '-f', 'ps2ascii.ps') + +if #files == 0 then + push(command, '-') else - ret=os.spawn(command) + push(command, '"'..files.input..'"') end +push(command, '-c', 'quit') + +if #files > 1 then + push(command, '>', '"'..files.output..'"') +end + +local cmd_string=table.concat(command, ' ') + +--[[ prepend an additional hyphen to activate this code +print(cmd_string) +os.exit(0) +--]] + +ret=os.execute(cmd_string) + +-- The last character of the ASCII file is a form feed (^L). +-- Uncomment the following line if this confuses your terminal. +-- +-- if #files < 2 then io.stdout:write('\r') end + remove_tmpfiles{'_temp_.err', '_temp_.out'} os.exit(ret) diff --git a/Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf.tlu b/Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf.tlu index 7cb82173cfa..d694047cfff 100755 --- a/Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf.tlu +++ b/Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf.tlu @@ -2,13 +2,24 @@ --*-Lua-*- -- $Id$ --- Copyright (C) 2008 Reinhard Kotucha. +-- Copyright (C) 2008-2015 Reinhard Kotucha. -- You may freely use, modify and/or distribute this file. --- Replacement for ps2pdf*.bat. --- Convert PS to PDF +-- Generic wrapper for ps2pdf{,12,13,14} and pdfopt. -function fixwin (args_unix) + +local function usage (progname) + local message= + 'Usage: '..progname..' [options] <inputfile> <outputfile>\n\n'.. + ' <inputfile> can be either a PS, EPS, or PDF file.\n'.. + ' A single hyphen (-) denotes stdin.\n\n'.. + ' <outputfile> is required if <inputfile> is a PDF file\n'.. + ' or input is read from stdin.\n' + return message +end + + +local function fixwin (args_unix) if os.type == 'windows' then local args_win={} -- new table args_win[0]=args_unix[1] @@ -21,7 +32,23 @@ function fixwin (args_unix) end end -function filename (file) + +local function push (t, ...) + local args={...} + for _,v in ipairs(args) do + if type(v) == 'table' then + for _,x in ipairs(v) do + t[#t+1]=x + end + else + t[#t+1]=v + end + end +end + + +local function filename (file) + -- strip path if string.find(file, '[/\\]') then return string.match(file, '.*[/\\](.*)$') else @@ -29,75 +56,101 @@ function filename (file) end end -function basename (file) - return string.match(filename(file), '(.*)[%.].*') -end -if os.type == 'windows' then - gs='gswin32c' -else - gs='gs' +local function basename (file) + -- strip extension + return string.match(filename(file), '(.*)%..*') end -command={gs} -files={} -options={} - -PDF_level='1.4' -if string.find(filename(arg[0]), 'ps2pdf12') then - PDF_level='1.2' -elseif string.find(filename(arg[0]), 'ps2pdf13') then - PDF_level='1.3' -elseif string.find(filename(arg[0]), 'ps2pdf14') then - PDF_level='1.4' + +local function extension (file) + -- return extension if available, false otherwise. + local fn=filename(file) + if fn:find('%.') then + return string.match(fn, '.*%.(.*)') + end end -options[#options+1]='-dCompatibilityLevel='..PDF_level -for i=1, #arg do - if arg[i] == '-' then - files[#files+1]=arg[i] - elseif string.find(arg[i], '^%-') then - options[#options+1]=arg[i] +local function parse_cmdline () + local files={} + local options={} + + local progname + local basename=filename(arg[0]) + if basename:find('%.') then + progname=basename:match('(.*)%..*') else - files[#files+1]=arg[i] + progname=basename end -end -if #files < 1 or #files > 2 then - io.stderr:write('Usage: ps2pdf [options] (input.[e]ps|-) [output.pdf]\n') - os.exit(1) + for i=1, #arg do + if string.find(arg[i], '^%-.+') then + push(options, arg[i]) + else + push(files, arg[i]) + end + end + files.input =files[1] + files.output=files[2] + return progname, options, files end -if #files == 1 then - if files[1] == '-' then - outfile='-' - elseif string.find(filename(files[1]), '%.[eE]?[pP][sS]$') then - outfile=basename(files[1])..'.pdf' - else - outfile=filename(files[1])..'.pdf' + +local function check_files (progname, files, default_output_ext) + if #files < 1 then + io.stderr:write(usage(progname)) + os.exit(1) end -else - outfile=files[2] -end -for i=1, #options do - command[#command+1]=options[i] + if #files < 2 then + if extension(files.input) then + local ext=string.lower(extension(files.input)) + if ext:match(default_output_ext..'$') then + io.stderr:write('!Error: no output file specified.\n\n') + io.stderr:write(usage(progname)) + os.exit(1) + else + files.output=basename(files.input)..'.'..default_output_ext + end + else -- the input file has no extension + files.output=files.input..'.'..default_output_ext + end + end end -other_opts={'-q', '-dSAFER', '-dNOPAUSE', '-dBATCH', '-sDEVICE=pdfwrite'} +-- main -- + +-- parse command line +local progname, options, file=parse_cmdline() + +check_files(progname, file, 'pdf') -for i=1, #other_opts do - command[#command+1]=other_opts[i] +-- determine PDF level +local PDF_level='1.4' -- default, may be changed in the future +if progname == 'ps2pdf12' then PDF_level='1.2' +elseif progname == 'ps2pdf13' then PDF_level='1.3' +elseif progname == 'ps2pdf14' then PDF_level='1.4' end -command[#command+1]='-sOutputFile='..outfile +-- setup command -other_opts={'-c', '.setpdfwrite', '-f', files[1]} +local command={} +if os.type == 'unix' then command={'gs'} else command={'gswin32c'} end -for i=1, #other_opts do - command[#command+1]=other_opts[i] -end +push(command, '-P-', '-dSAFER', options, '-dCompatibilityLevel='..PDF_level) + +if progname == 'pdfopt' then push(command, '-dFastWebView=true') end + +push(command, '-q', '-P-', '-dNOPAUSE', '-dBATCH', '-sDEVICE=pdfwrite') + +if os.type == 'unix' then push(command, '-sstdout=%stderr') end + +push(command, '-sOutputFile='..file.output, '-P-', '-dSAFER') + +-- We have to include the options twice because -I only takes effect +-- if it appears before other options [see ps2pdfwr]. +push(command, options, '-c', '.setpdfwrite', '-f', file.input) command=fixwin(command) @@ -105,7 +158,7 @@ command=fixwin(command) for i=0, #command do print (command[i]) end -os.exit(ret) +os.exit(0) --]] ret=os.spawn(command) diff --git a/Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf12.tlu b/Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf12.tlu index a35e3b0912a..d694047cfff 100755 --- a/Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf12.tlu +++ b/Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf12.tlu @@ -2,13 +2,24 @@ --*-Lua-*- -- $Id$ --- Copyright (C) 2008 Reinhard Kotucha. +-- Copyright (C) 2008-2015 Reinhard Kotucha. -- You may freely use, modify and/or distribute this file. --- Replacement for ps2pdf*.bat. --- Convert PS to PDF +-- Generic wrapper for ps2pdf{,12,13,14} and pdfopt. -function fixwin (args_unix) + +local function usage (progname) + local message= + 'Usage: '..progname..' [options] <inputfile> <outputfile>\n\n'.. + ' <inputfile> can be either a PS, EPS, or PDF file.\n'.. + ' A single hyphen (-) denotes stdin.\n\n'.. + ' <outputfile> is required if <inputfile> is a PDF file\n'.. + ' or input is read from stdin.\n' + return message +end + + +local function fixwin (args_unix) if os.type == 'windows' then local args_win={} -- new table args_win[0]=args_unix[1] @@ -21,7 +32,23 @@ function fixwin (args_unix) end end -function filename (file) + +local function push (t, ...) + local args={...} + for _,v in ipairs(args) do + if type(v) == 'table' then + for _,x in ipairs(v) do + t[#t+1]=x + end + else + t[#t+1]=v + end + end +end + + +local function filename (file) + -- strip path if string.find(file, '[/\\]') then return string.match(file, '.*[/\\](.*)$') else @@ -29,75 +56,101 @@ function filename (file) end end -function basename (file) - return string.match(filename(file), '(.*)[%.].*') -end -if os.type == 'windows' then - gs='gswin32c' -else - gs='gs' +local function basename (file) + -- strip extension + return string.match(filename(file), '(.*)%..*') end -command={gs} -files={} -options={} - -PDF_level='1.4' -if string.find(filename(arg[0]), 'ps2pdf12') then - PDF_level='1.2' -elseif string.find(filename(arg[0]), 'ps2pdf13') then - PDF_level='1.3' -elseif string.find(filename(arg[0]), 'ps2pdf14') then - PDF_level='1.4' + +local function extension (file) + -- return extension if available, false otherwise. + local fn=filename(file) + if fn:find('%.') then + return string.match(fn, '.*%.(.*)') + end end -options[#options+1]='-dCompatibilityLevel='..PDF_level -for i=1, #arg do - if arg[i] == '-' then - files[#files+1]=arg[i] - elseif string.find(arg[i], '^%-') then - options[#options+1]=arg[i] +local function parse_cmdline () + local files={} + local options={} + + local progname + local basename=filename(arg[0]) + if basename:find('%.') then + progname=basename:match('(.*)%..*') else - files[#files+1]=arg[i] + progname=basename end -end -if #files < 1 or #files > 2 then - io.stderr:write('Usage: ps2pdf [options] (input.[e]ps|-) [output.pdf]\n') - os.exit(1) + for i=1, #arg do + if string.find(arg[i], '^%-.+') then + push(options, arg[i]) + else + push(files, arg[i]) + end + end + files.input =files[1] + files.output=files[2] + return progname, options, files end -if #files == 1 then - if files[1] == '-' then - outfile='-' - elseif string.find(filename(files[1]), '%.[eE]?[pP][sS]$') then - outfile=basename(files[1])..'.pdf' - else - outfile=filename(files[1])..'.pdf' + +local function check_files (progname, files, default_output_ext) + if #files < 1 then + io.stderr:write(usage(progname)) + os.exit(1) end -else - outfile=files[2] -end -for i=1, #options do - command[#command+1]=options[i] + if #files < 2 then + if extension(files.input) then + local ext=string.lower(extension(files.input)) + if ext:match(default_output_ext..'$') then + io.stderr:write('!Error: no output file specified.\n\n') + io.stderr:write(usage(progname)) + os.exit(1) + else + files.output=basename(files.input)..'.'..default_output_ext + end + else -- the input file has no extension + files.output=files.input..'.'..default_output_ext + end + end end -other_opts={'-q', '-dSAFER', '-dNOPAUSE', '-dBATCH', '-sDEVICE=pdfwrite'} +-- main -- + +-- parse command line +local progname, options, file=parse_cmdline() + +check_files(progname, file, 'pdf') -for i=1, #other_opts do - command[#command+1]=other_opts[i] +-- determine PDF level +local PDF_level='1.4' -- default, may be changed in the future +if progname == 'ps2pdf12' then PDF_level='1.2' +elseif progname == 'ps2pdf13' then PDF_level='1.3' +elseif progname == 'ps2pdf14' then PDF_level='1.4' end -command[#command+1]='-sOutputFile='..outfile +-- setup command -other_opts={'-c', '.setpdfwrite', '-f', files[1]} +local command={} +if os.type == 'unix' then command={'gs'} else command={'gswin32c'} end -for i=1, #other_opts do - command[#command+1]=other_opts[i] -end +push(command, '-P-', '-dSAFER', options, '-dCompatibilityLevel='..PDF_level) + +if progname == 'pdfopt' then push(command, '-dFastWebView=true') end + +push(command, '-q', '-P-', '-dNOPAUSE', '-dBATCH', '-sDEVICE=pdfwrite') + +if os.type == 'unix' then push(command, '-sstdout=%stderr') end + +push(command, '-sOutputFile='..file.output, '-P-', '-dSAFER') + +-- We have to include the options twice because -I only takes effect +-- if it appears before other options [see ps2pdfwr]. +push(command, options, '-c', '.setpdfwrite', '-f', file.input) command=fixwin(command) @@ -105,7 +158,7 @@ command=fixwin(command) for i=0, #command do print (command[i]) end -os.exit(ret) +os.exit(0) --]] ret=os.spawn(command) diff --git a/Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf13.tlu b/Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf13.tlu index a35e3b0912a..d694047cfff 100755 --- a/Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf13.tlu +++ b/Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf13.tlu @@ -2,13 +2,24 @@ --*-Lua-*- -- $Id$ --- Copyright (C) 2008 Reinhard Kotucha. +-- Copyright (C) 2008-2015 Reinhard Kotucha. -- You may freely use, modify and/or distribute this file. --- Replacement for ps2pdf*.bat. --- Convert PS to PDF +-- Generic wrapper for ps2pdf{,12,13,14} and pdfopt. -function fixwin (args_unix) + +local function usage (progname) + local message= + 'Usage: '..progname..' [options] <inputfile> <outputfile>\n\n'.. + ' <inputfile> can be either a PS, EPS, or PDF file.\n'.. + ' A single hyphen (-) denotes stdin.\n\n'.. + ' <outputfile> is required if <inputfile> is a PDF file\n'.. + ' or input is read from stdin.\n' + return message +end + + +local function fixwin (args_unix) if os.type == 'windows' then local args_win={} -- new table args_win[0]=args_unix[1] @@ -21,7 +32,23 @@ function fixwin (args_unix) end end -function filename (file) + +local function push (t, ...) + local args={...} + for _,v in ipairs(args) do + if type(v) == 'table' then + for _,x in ipairs(v) do + t[#t+1]=x + end + else + t[#t+1]=v + end + end +end + + +local function filename (file) + -- strip path if string.find(file, '[/\\]') then return string.match(file, '.*[/\\](.*)$') else @@ -29,75 +56,101 @@ function filename (file) end end -function basename (file) - return string.match(filename(file), '(.*)[%.].*') -end -if os.type == 'windows' then - gs='gswin32c' -else - gs='gs' +local function basename (file) + -- strip extension + return string.match(filename(file), '(.*)%..*') end -command={gs} -files={} -options={} - -PDF_level='1.4' -if string.find(filename(arg[0]), 'ps2pdf12') then - PDF_level='1.2' -elseif string.find(filename(arg[0]), 'ps2pdf13') then - PDF_level='1.3' -elseif string.find(filename(arg[0]), 'ps2pdf14') then - PDF_level='1.4' + +local function extension (file) + -- return extension if available, false otherwise. + local fn=filename(file) + if fn:find('%.') then + return string.match(fn, '.*%.(.*)') + end end -options[#options+1]='-dCompatibilityLevel='..PDF_level -for i=1, #arg do - if arg[i] == '-' then - files[#files+1]=arg[i] - elseif string.find(arg[i], '^%-') then - options[#options+1]=arg[i] +local function parse_cmdline () + local files={} + local options={} + + local progname + local basename=filename(arg[0]) + if basename:find('%.') then + progname=basename:match('(.*)%..*') else - files[#files+1]=arg[i] + progname=basename end -end -if #files < 1 or #files > 2 then - io.stderr:write('Usage: ps2pdf [options] (input.[e]ps|-) [output.pdf]\n') - os.exit(1) + for i=1, #arg do + if string.find(arg[i], '^%-.+') then + push(options, arg[i]) + else + push(files, arg[i]) + end + end + files.input =files[1] + files.output=files[2] + return progname, options, files end -if #files == 1 then - if files[1] == '-' then - outfile='-' - elseif string.find(filename(files[1]), '%.[eE]?[pP][sS]$') then - outfile=basename(files[1])..'.pdf' - else - outfile=filename(files[1])..'.pdf' + +local function check_files (progname, files, default_output_ext) + if #files < 1 then + io.stderr:write(usage(progname)) + os.exit(1) end -else - outfile=files[2] -end -for i=1, #options do - command[#command+1]=options[i] + if #files < 2 then + if extension(files.input) then + local ext=string.lower(extension(files.input)) + if ext:match(default_output_ext..'$') then + io.stderr:write('!Error: no output file specified.\n\n') + io.stderr:write(usage(progname)) + os.exit(1) + else + files.output=basename(files.input)..'.'..default_output_ext + end + else -- the input file has no extension + files.output=files.input..'.'..default_output_ext + end + end end -other_opts={'-q', '-dSAFER', '-dNOPAUSE', '-dBATCH', '-sDEVICE=pdfwrite'} +-- main -- + +-- parse command line +local progname, options, file=parse_cmdline() + +check_files(progname, file, 'pdf') -for i=1, #other_opts do - command[#command+1]=other_opts[i] +-- determine PDF level +local PDF_level='1.4' -- default, may be changed in the future +if progname == 'ps2pdf12' then PDF_level='1.2' +elseif progname == 'ps2pdf13' then PDF_level='1.3' +elseif progname == 'ps2pdf14' then PDF_level='1.4' end -command[#command+1]='-sOutputFile='..outfile +-- setup command -other_opts={'-c', '.setpdfwrite', '-f', files[1]} +local command={} +if os.type == 'unix' then command={'gs'} else command={'gswin32c'} end -for i=1, #other_opts do - command[#command+1]=other_opts[i] -end +push(command, '-P-', '-dSAFER', options, '-dCompatibilityLevel='..PDF_level) + +if progname == 'pdfopt' then push(command, '-dFastWebView=true') end + +push(command, '-q', '-P-', '-dNOPAUSE', '-dBATCH', '-sDEVICE=pdfwrite') + +if os.type == 'unix' then push(command, '-sstdout=%stderr') end + +push(command, '-sOutputFile='..file.output, '-P-', '-dSAFER') + +-- We have to include the options twice because -I only takes effect +-- if it appears before other options [see ps2pdfwr]. +push(command, options, '-c', '.setpdfwrite', '-f', file.input) command=fixwin(command) @@ -105,7 +158,7 @@ command=fixwin(command) for i=0, #command do print (command[i]) end -os.exit(ret) +os.exit(0) --]] ret=os.spawn(command) diff --git a/Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf14.tlu b/Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf14.tlu index a35e3b0912a..d694047cfff 100755 --- a/Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf14.tlu +++ b/Master/texmf-dist/scripts/tlgs/gswin32/ps2pdf14.tlu @@ -2,13 +2,24 @@ --*-Lua-*- -- $Id$ --- Copyright (C) 2008 Reinhard Kotucha. +-- Copyright (C) 2008-2015 Reinhard Kotucha. -- You may freely use, modify and/or distribute this file. --- Replacement for ps2pdf*.bat. --- Convert PS to PDF +-- Generic wrapper for ps2pdf{,12,13,14} and pdfopt. -function fixwin (args_unix) + +local function usage (progname) + local message= + 'Usage: '..progname..' [options] <inputfile> <outputfile>\n\n'.. + ' <inputfile> can be either a PS, EPS, or PDF file.\n'.. + ' A single hyphen (-) denotes stdin.\n\n'.. + ' <outputfile> is required if <inputfile> is a PDF file\n'.. + ' or input is read from stdin.\n' + return message +end + + +local function fixwin (args_unix) if os.type == 'windows' then local args_win={} -- new table args_win[0]=args_unix[1] @@ -21,7 +32,23 @@ function fixwin (args_unix) end end -function filename (file) + +local function push (t, ...) + local args={...} + for _,v in ipairs(args) do + if type(v) == 'table' then + for _,x in ipairs(v) do + t[#t+1]=x + end + else + t[#t+1]=v + end + end +end + + +local function filename (file) + -- strip path if string.find(file, '[/\\]') then return string.match(file, '.*[/\\](.*)$') else @@ -29,75 +56,101 @@ function filename (file) end end -function basename (file) - return string.match(filename(file), '(.*)[%.].*') -end -if os.type == 'windows' then - gs='gswin32c' -else - gs='gs' +local function basename (file) + -- strip extension + return string.match(filename(file), '(.*)%..*') end -command={gs} -files={} -options={} - -PDF_level='1.4' -if string.find(filename(arg[0]), 'ps2pdf12') then - PDF_level='1.2' -elseif string.find(filename(arg[0]), 'ps2pdf13') then - PDF_level='1.3' -elseif string.find(filename(arg[0]), 'ps2pdf14') then - PDF_level='1.4' + +local function extension (file) + -- return extension if available, false otherwise. + local fn=filename(file) + if fn:find('%.') then + return string.match(fn, '.*%.(.*)') + end end -options[#options+1]='-dCompatibilityLevel='..PDF_level -for i=1, #arg do - if arg[i] == '-' then - files[#files+1]=arg[i] - elseif string.find(arg[i], '^%-') then - options[#options+1]=arg[i] +local function parse_cmdline () + local files={} + local options={} + + local progname + local basename=filename(arg[0]) + if basename:find('%.') then + progname=basename:match('(.*)%..*') else - files[#files+1]=arg[i] + progname=basename end -end -if #files < 1 or #files > 2 then - io.stderr:write('Usage: ps2pdf [options] (input.[e]ps|-) [output.pdf]\n') - os.exit(1) + for i=1, #arg do + if string.find(arg[i], '^%-.+') then + push(options, arg[i]) + else + push(files, arg[i]) + end + end + files.input =files[1] + files.output=files[2] + return progname, options, files end -if #files == 1 then - if files[1] == '-' then - outfile='-' - elseif string.find(filename(files[1]), '%.[eE]?[pP][sS]$') then - outfile=basename(files[1])..'.pdf' - else - outfile=filename(files[1])..'.pdf' + +local function check_files (progname, files, default_output_ext) + if #files < 1 then + io.stderr:write(usage(progname)) + os.exit(1) end -else - outfile=files[2] -end -for i=1, #options do - command[#command+1]=options[i] + if #files < 2 then + if extension(files.input) then + local ext=string.lower(extension(files.input)) + if ext:match(default_output_ext..'$') then + io.stderr:write('!Error: no output file specified.\n\n') + io.stderr:write(usage(progname)) + os.exit(1) + else + files.output=basename(files.input)..'.'..default_output_ext + end + else -- the input file has no extension + files.output=files.input..'.'..default_output_ext + end + end end -other_opts={'-q', '-dSAFER', '-dNOPAUSE', '-dBATCH', '-sDEVICE=pdfwrite'} +-- main -- + +-- parse command line +local progname, options, file=parse_cmdline() + +check_files(progname, file, 'pdf') -for i=1, #other_opts do - command[#command+1]=other_opts[i] +-- determine PDF level +local PDF_level='1.4' -- default, may be changed in the future +if progname == 'ps2pdf12' then PDF_level='1.2' +elseif progname == 'ps2pdf13' then PDF_level='1.3' +elseif progname == 'ps2pdf14' then PDF_level='1.4' end -command[#command+1]='-sOutputFile='..outfile +-- setup command -other_opts={'-c', '.setpdfwrite', '-f', files[1]} +local command={} +if os.type == 'unix' then command={'gs'} else command={'gswin32c'} end -for i=1, #other_opts do - command[#command+1]=other_opts[i] -end +push(command, '-P-', '-dSAFER', options, '-dCompatibilityLevel='..PDF_level) + +if progname == 'pdfopt' then push(command, '-dFastWebView=true') end + +push(command, '-q', '-P-', '-dNOPAUSE', '-dBATCH', '-sDEVICE=pdfwrite') + +if os.type == 'unix' then push(command, '-sstdout=%stderr') end + +push(command, '-sOutputFile='..file.output, '-P-', '-dSAFER') + +-- We have to include the options twice because -I only takes effect +-- if it appears before other options [see ps2pdfwr]. +push(command, options, '-c', '.setpdfwrite', '-f', file.input) command=fixwin(command) @@ -105,7 +158,7 @@ command=fixwin(command) for i=0, #command do print (command[i]) end -os.exit(ret) +os.exit(0) --]] ret=os.spawn(command) diff --git a/Master/texmf-dist/scripts/tlgs/gswin32/ps2ps.tlu b/Master/texmf-dist/scripts/tlgs/gswin32/ps2ps.tlu index 2d414700ebb..063fc76d0cc 100755 --- a/Master/texmf-dist/scripts/tlgs/gswin32/ps2ps.tlu +++ b/Master/texmf-dist/scripts/tlgs/gswin32/ps2ps.tlu @@ -5,8 +5,21 @@ -- Copyright (C) 2010 Reinhard Kotucha. -- You may freely use, modify and/or distribute this file. --- Replacement for ps2ps.bat. --- 'Distill' PostScript. +-- Replacement for ps2ps2.bat. +-- Converting PostScript 3 or PDF into PostScript 2 with the +-- Ghostscript 'ps2write' device. +-- This generates a PDF-style stream with an attached +-- PostScript program to interpret it. + + +local function usage (progname) + local message= + 'Usage: '..progname..' [options] <inputfile> <outputfile>\n\n'.. + ' <inputfile> can be either a PS, EPS, PDF file, or stdin.\n'.. + ' A single hyphen (-) denotes stdin.\n' + return message +end + function fixwin(args_unix) if os.type == 'windows' then @@ -21,47 +34,97 @@ function fixwin(args_unix) end end -if os.type == 'windows' then - gs='gswin32c' -else - gs='gs' -end +local function push (t, ...) + local args={...} + for _,v in ipairs(args) do + if type(v) == 'table' then + for _,x in ipairs(v) do + t[#t+1]=x + end + else + t[#t+1]=v + end + end +end -files={} -options={'-dNOPAUSE', '-dBATCH', '-dSAFER'} -for i=1, #arg do - if string.find(arg[i], '^%-$') then - files[#files+1]=arg[i] - elseif string.find(arg[i], '^%-') then - options[#options+1]=arg[i] +local function filename (file) + -- strip path + if string.find(file, '[/\\]') then + return string.match(file, '.*[/\\](.*)$') else - files[#files+1]=arg[i] + return file end end -if #files ~= 2 then - io.stderr:write('Usage: ps2ps [options] input.ps output.ps\n') - io.stderr:write(' e.g. ps2ps -sPAPERSIZE=a4 input.ps output.ps\n') - os.exit(1) +local function basename (file) + -- strip extension + return string.match(filename(file), '(.*)%..*') end -command={gs, '-q', '-sDEVICE=pswrite'} +local function extension (file) + -- return extension if available, false otherwise. + local fn=filename(file) + if fn:find('%.') then + return string.match(fn, '.*%.(.*)') + end +end + + +local function parse_cmdline () + local files={} + local options={} + + local progname + local basename=filename(arg[0]) + if basename:find('%.') then + progname=basename:match('(.*)%..*') + else + progname=basename + end -if os.type=='unix' then - command[#command+1]='-sstdout=%stderr' + for i=1, #arg do + if string.find(arg[i], '^%-.+') then + push(options, arg[i]) + else + push(files, arg[i]) + end + end + files.input =files[1] + files.output=files[2] + + return progname, options, files end -command[#command+1]='-sOutputFile='..files[2] -for i=1, #options do - command[#command+1]=options[i] +local function check_files (progname, files) + if #files < 2 then + io.stderr:write(usage(progname)) + os.exit(1) + end end -command[#command+1]=files[1] +-- main -- + +local progname, options, file=parse_cmdline() + +check_files (progname, file) + +-- setup command + +local command={} +if os.type == 'unix' then command={'gs'} else command={'gswin32c'} end + +push(command, '-q', '-sDEVICE=ps2write') + +if os.type=='unix' then push(command, '-sstdout=%stderr') end + +push(command, '-sOutputFile='..file.output) +push(command, '-dNOPAUSE', '-dBATCH', '-P-', '-dSAFER') +push(command, options, file.input) command=fixwin(command) @@ -69,7 +132,7 @@ command=fixwin(command) for i=0, #command do print (command[i]) end -os.exit(ret) +os.exit(0) --]] ret=os.spawn(command) diff --git a/Master/texmf-dist/scripts/tlgs/gswin32/ps2ps2.tlu b/Master/texmf-dist/scripts/tlgs/gswin32/ps2ps2.tlu deleted file mode 100755 index 5fa86abf0d2..00000000000 --- a/Master/texmf-dist/scripts/tlgs/gswin32/ps2ps2.tlu +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env texlua ---*-Lua-*- --- $Id$ - --- Copyright (C) 2010 Reinhard Kotucha. --- You may freely use, modify and/or distribute this file. - --- Replacement for ps2ps2.bat. --- Converting PostScript 3 or PDF into PostScript 2 with the --- Ghostscript 'ps2write' device. --- This generates a PDF-style stream with an attached --- PostScript program to interpret it. - -function fixwin(args_unix) - if os.type == 'windows' then - local args_win={} -- new table - args_win[0]=args_unix[1] - for i=1, #args_unix do - args_win[i]='"'..args_unix[i]..'"' - end - return args_win - else - return args_unix - end -end - -if os.type == 'windows' then - gs='gswin32c' -else - gs='gs' -end - - -files={} -options={'-dNOPAUSE', '-dBATCH', '-dSAFER'} - -for i=1, #arg do - if string.find(arg[i], '^%-$') then - files[#files+1]=arg[i] - elseif string.find(arg[i], '^%-') then - options[#options+1]=arg[i] - else - files[#files+1]=arg[i] - end -end - - -if #files ~= 2 then - io.stderr:write('Usage: ps2ps2 [options] input.ps output.ps\n') - io.stderr:write(' e.g. ps2ps2 -sPAPERSIZE=a4 input.ps output.ps\n') - os.exit(1) -end - - -command={gs, '-q', '-sDEVICE=ps2write'} - -if os.type=='unix' then - command[#command+1]='-sstdout=%stderr' -end - -command[#command+1]='-sOutputFile='..files[2] - -for i=1, #options do - command[#command+1]=options[i] -end - -command[#command+1]=files[1] - -command=fixwin(command) - ---[[ prepend an additional hyphen to activate this code -for i=0, #command do - print (command[i]) -end -os.exit(ret) ---]] - -ret=os.spawn(command) -os.exit(ret) diff --git a/Master/tlpkg/tlgs/lib/pdfopt.ps b/Master/tlpkg/tlgs/lib/pdfopt.ps deleted file mode 100644 index 88a8943f278..00000000000 --- a/Master/tlpkg/tlgs/lib/pdfopt.ps +++ /dev/null @@ -1,1272 +0,0 @@ -% Copyright (C) 2001-2012 Artifex Software, Inc. -% All Rights Reserved. -% -% This software is provided AS-IS with no warranty, either express or -% implied. -% -% This software is distributed under license and may not be copied, -% modified or distributed except as expressly authorized under the terms -% of the license contained in the file LICENSE in this distribution. -% -% Refer to licensing information at http://www.artifex.com or contact -% Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael, -% CA 94903, U.S.A., +1(415)492-9861, for further information. -% - -% PDF linearizer ("optimizer"). - -.currentglobal true .setglobal -/pdfoptdict 200 dict def -pdfoptdict begin - -% This linearizer is designed for simplicity, not for performance. -% See the main program (the last procedure in the file) for comments -% describing the main processing sequence. - -% ---------------- Utilities ---------------- % - -% ------ Data structures ------ % - -% Distinguish dictionaries, arrays, and everything else. -/ifdaelse { % <obj> <dictproc> <arrayproc> <otherproc> ifdaelse - - 3 index type dup /dicttype eq { - pop pop pop - } { - dup /arraytype ne exch /packedarraytype ne and { - exch - } if pop exch pop - } ifelse exec -} bind def - -% Implement dynamically growable arrays using a dictionary. -/darray { % <size> darray <darray> - dict -} bind def -/dadd { % <darray> <value> dadd - - 1 index length exch put -} bind def -/daforall { % <darray> <proc> daforall - - /exch cvx /get cvx 3 -1 roll /exec cvx 5 packedarray cvx - 0 1 2 index 0 get length 1 sub 4 -1 roll for -} bind def -/dacontents { % <darray> dacontents <array> - [ exch { } daforall ] -} bind def -/dacontstring { % <darray> dacontstring <string> - 0 1 index { exch pop length add } forall string - dup /NullEncode filter - % Stack: darray str filter - 3 -1 roll { 1 index exch writestring } daforall - closefile -} bind def - -% Force an object, mapping it if it is a reference. -/omforcenew { % <obj> omforce <obj'> <notseen> - dup oforce 2 copy eq { pop true } { exch 0 get omapnew exch pop } ifelse -} bind def -/omforce { % <obj> omforce <obj'> - omforcenew pop -} bind def -/omget { % <dict|array> <key> omget <obj> - get omforce -} bind def -% Visit an entire tree. -/omvisit { % <obj> omvisit - - omforcenew { - { { omvisit omvisit } forall } - { { omvisit } forall } - { pop } - ifdaelse - } { - pop - } ifelse -} bind def -% Visit a tree, stopping at references to Page objects. -% (This is only needed for the OpenAction in the Catalog.) -/omvisitnopage { % <obj> omvisitnopage - - dup oforce dup type /dicttype eq { - /Type .knownget { /Page eq } { false } ifelse - } { - pop false - } ifelse { - pop % Page reference - } { - omforcenew { - { { omvisitnopage omvisitnopage } forall } - { { omvisitnopage } forall } - { pop } - ifdaelse - } { - pop - } ifelse - } ifelse -} bind def - -% Collect the list of currently mapped object numbers, in order. -/omapped { % - omapped <obj#s> - RMap ld_length larray exch lgrowto - RMap { - 2 index 3 1 roll 1 sub exch lput - } ld_forall -} bind def - -% Collect the list of object numbers passed to omap by a procedure. -/visited { % <proc> visited <obj#s> - false currentomap 2 .execn - omapped exch setomap -} bind def - -% ------ Output ------ % - -% Provide a framework for closure-based streams. -.currentglobal false .setglobal -userdict /clostreams 20 dict put % stream -> [data endproc] -.setglobal -% Create a closure-based stream. -/clostream { % <data> <proc> <endproc> clostream <stream> - 2 index 3 -1 roll /exec load 3 packedarray cvx - /NullEncode filter - % Stack: data endproc stream - clostreams 1 index 5 -2 roll 2 array astore put -} bind def -% Close a closure-based stream. -/closend { % <stream> closend <result> - dup closefile clostreams exch - 2 copy get 3 1 roll undef aload pop exec -} bind def - -% Implement in-memory output streams. -/msproc { % <data> <more> <accum> msproc <scratch> - 3 -1 roll dadd { 100 string } { () } ifelse -} bind def -/mstream { % - mstream <mstream> - 10 darray {msproc} {dacontstring} clostream -} bind def -/mcontents { % <mstream> mcontents <string> - closend -} bind def - -% Implement a stream that only keeps track of its position. -% (All streams should do this, but the PLRM doesn't require it.) -/posbuf 100 string def -/posproc { % <data> <more> <accum> posproc <scratch> - 0 2 copy get 5 -1 roll length add put - pop //posbuf -} bind def -/postream { % - postream <postream> - [0] {posproc} {0 get} clostream -} bind def -/poslength { % <postream> poslength <pos> - closend -} bind def - -% Implement streams with variable-bit-width data. -% Note that these are dictionary objects, not stream objects. -/bitstream { % <stream> bitstream <bstream> - 4 dict begin /S exch def /N 8 def /B 0 def - currentdict end -} bind def -/bitwrite { % <bstream> <value> <width> bitwrite - - PDFOPTDEBUG { ( ) print 1 index =only (:) print dup = } if - 3 -1 roll begin - N exch sub dup 0 ge { - /N exch def N bitshift B add - } { - 2 copy bitshift B add S exch write - % Stack: value -left - { 8 add dup 0 ge { exit } if - 2 copy bitshift 255 and S exch write - } loop - /N 1 index def bitshift 255 and - } ifelse /B exch def - end -} bind def -/bitflush { % <bstream> bitflush - - begin N 8 ne { S B write /B 0 def /N 8 def } if end -} bind def - -/bwn { % <value> <width> bwn - - 2 copy % v w v w - 2 exch exp ge { % v w v>=2**w - /bwn cvx /rangecheck signalerror - } if - bits 3 1 roll bitwrite -} def - -% Capture OFile output on the temporary file, in memory, or just as a length. -/totemp { % <proc> totemp <start> <end> - TFile fileposition OFile - /OFile TFile def 3 .execn - /OFile exch def - TFile fileposition -} bind def -/tomemory { % <proc> tomemory <string> - OFile /OFile mstream def 2 .execn - OFile mcontents exch /OFile exch def -} bind def -/tolength { % <proc> tolength <string> - OFile /OFile postream def 2 .execn - OFile poslength exch /OFile exch def -} bind def - -% Copy a range of bytes from TFile to OFile. -/copyrange { % <start> <end> copybytes - - TFile 2 index setfileposition - exch sub 1024 string exch { - % Stack: buf left - 2 copy 1 index length .min 0 exch getinterval - TFile exch readstring pop OFile exch writestring - 1 index length sub dup 0 le { exit } if - } loop pop pop -} bind def - -% Pad with blanks to a specified position. -/padto { % <pos> padto - - OFile fileposition sub - dup 0 lt { - (ERROR: file position incorrect by ) print = - /padto cvx /rangecheck signalerror - } { - { ( ) ows } repeat - } ifelse -} bind def - -% ---------------- Read objects into memory ---------------- % - -/touch { % <object> touch - - { - { touch touch } forall - } { - dup xcheck { - % Executable array, must be an indirect object. - dup 0 get resolved? { pop pop } { oforce touch } ifelse - } { - { touch } forall - } ifelse - } { - pop - } ifdaelse -} bind def - -% ---------------- Replace references with referents ---------------- % - -/replaceable? { % <value> replaceable? <bool> - dup type /integertype eq exch xcheck not and -} bind def -/replacement { % <obj|ref> replacement <obj'> - dup oforce dup replaceable? { exch } if pop -} bind def - -/replacerefs { % <object> replacerefs <object> - { - dup { - 2 index 2 index undef - exch replacement exch replacement - 2 index 3 1 roll put - } forall - } { - 0 1 2 index length 1 sub { - 1 index exch 2 copy get replacement put - } for - } { - } ifdaelse -} bind def - -/replaceReferences { % - replaceReferences - - Objects { replacerefs pop } lforall - % Delete replaced objects. - 0 1 Objects llength 1 sub { - Objects 1 index lget replaceable? { - PDFOPTDEBUG { (Deleting ) print dup = } if - Generations 1 index 0 lput - } if pop - } for -} bind def - -% ---------------- Create new objects ---------------- % - -/createObjects { % [<obj>...] createObjects <firstobj#> - Objects llength dup - dup 3 index length add growPDFobjects - % Stack: objects objn objn - 3 1 roll exch { - Objects 2 index 3 -1 roll lput - Generations 1 index 1 lput - 1 add - } forall pop -} bind def - -% ---------------- Propagate attributes ---------------- % - -/nopropattrs << - % Never propagate these. - /Type dup /Kids dup /Count dup /Parent dup - % Handle Resources specially. - /Resources dup ->> def - -% Merge Resources. -/mergeres { % <fromdict> <todict> mergeres - - % Values in todict take priority over fromdict. - 1 index /Resources .knownget { - 1 index /Resources .knownget { - % Stack: fromdict todict fromres tores - exch oforce exch oforce - % todict's Resources may be shared, so make a copy. - dup length dict .copydict - exch { - % Stack: fromdict todict tores' fromkey fromvalue - 2 index 2 index knownoget { - % Stack: fromdict todict tores' fromkey fromvalue tovalue - exch oforce exch - % ProcSet is an array, other types are dictionaries. - dup type /dicttype eq { - % Dictionary, not ProcSet. - exch dup length 2 index length add dict .copydict .copydict - } { - % Array or packed array, ProcSet. - % Use dictionaries to do the merge. - dup length 2 index length add dict begin - exch { dup def } forall { dup def } forall - mark currentdict end { pop } forall .packtomark - } ifelse - } if - 2 index 3 1 roll put - } forall - } if /Resources exch put pop - } { - pop pop - } ifelse -} bind def - -% Merge attributes other than Resources. -/mergeattrs { % <fromdict> <todict> mergeattrs <fromdict> <todict> - % Values in todict take priority over fromdict. - 1 index { - % Stack: fromdict todict fromkey fromvalue - //nopropattrs 2 index known { - pop pop - } { - 2 index 2 index known { pop pop } { 2 index 3 1 roll put } ifelse - } ifelse - } forall -} bind def - -% Propagate attributes to a subtree. -/proppage { % <attrs> <subtree> proppage - - % We should be able to tell when we reach a leaf - % by finding a Type unequal to /Pages. Unfortunately, - % some files distributed by Adobe lack the Type key - % in some of the Pages nodes! Instead, we check for Kids. - dup /Kids knownoget { - % Accumulate inherited values. - 3 1 roll - % Stack: kids attrs pagesnode - dup length dict .copydict mergeattrs - dup 3 1 roll mergeres - exch { oforce 1 index exch proppage } forall pop - } { - % Merge inherited values into the leaf. - mergeattrs mergeres - } ifelse -} bind def - -% Propagate attributes to all pages. -/propagateAttributes { % - propagateAttributes - - 0 dict Trailer /Root oget /Pages oget proppage -} bind def - -% ---------------- Identify document-level objects ---------------- % - -/identifyDocumentObjects { % - identifyDocumentObjects <obj#s> - { - Trailer /Root omget - dup /PageMode .knownget { omvisit } if - % Don't allow omvisit to trace references to Page objects. - dup /OpenAction .knownget { omvisitnopage } if - Trailer /Encrypt .knownget { omvisit } if - dup /Threads .knownget { - omforce dup //null ne { { omvisitnopage } forall } { pop } ifelse - } if - dup /AcroForm .knownget { omvisitnopage } if - pop - } visited -} bind def - -% ---------------- Identify the objects of each page ---------------- % - -/identifyfont { % <fontref> identifyfont - - omforce { - exch /FontDescriptor eq { - omforce dup /Flags .knownget { 32 and 0 ne } { false } ifelse - exch { - exch dup dup /FontFile eq exch /FontFile2 eq or - exch /FontFile3 eq or 2 index and { - fontfiles exch dadd - } { - omvisit - } ifelse - } forall pop - } { - omvisit - } ifelse - } forall -} bind def - -% Collect all the objects referenced from a page. The first object number -% (which may not be the smallest one) is that of the page object itself. -/identifyPageObjects { % <extra> <page#> identifyPageObjects <obj#s> - PDFOPTDEBUG { - (%Objects for page: ) print dup = - } if - pdffindpageref - dup 0 get 3 1 roll - 4 dict begin - /filter_params 10 darray def - /images 10 darray def - /fontfiles 10 darray def - { - omforce - % Stack: pageobj# extra page - % Visit any extra objects if applicable. - exch omvisitnopage - % Visit Annots, if any. - % We don't try to defer the drawing information. - dup /Annots .knownget { omvisitnopage } if - % Visit beads. - dup /B .knownget { omvisitnopage } if - % Visit resources dictionaries. - dup /Resources .knownget { - omforce dup { - % Visit the first-level Resource dictionaries. - omforce pop pop - } forall { - % Visit the resources themselves. - % Skip Image XObjects, and FontFile streams if the - % FontDescriptor Flags have bit 6 set. - % We don't try to visit the resources in the order in which - % the Contents stream(s) reference(s) them. - exch dup /XObject eq { - pop oforce { - dup oforce /Subtype get /Image eq { - dup oforce /DecodeParms .knownget { - oforce { - exch /JBIG2Globals eq { - filter_params exch dadd - } { - pop - } ifelse - } forall - } if - images exch dadd - } { - omvisit - } ifelse pop - } forall - } { - /Font eq { - oforce { identifyfont pop } forall - } { - oforce omvisit - } ifelse - } ifelse - } forall - } if - % Visit the Contents stream(s). - dup /Contents .knownget { omvisit } if - % Visit Image XObjects. We don't try to visit them in - % reference order. - filter_params { omvisit } daforall - images { omvisit } daforall - % Visit FontFile streams. We don't try to visit them in - % reference order. - fontfiles { omvisit } daforall - pop - } visited end - % Stack: pageobj# obj#s_larray - [ 3 1 roll { - 2 copy eq { pop } { exch } ifelse - } lforall counttomark 1 roll ] - PDFOPTDEBUG { - (%Objects = ) print dup === flush - } if -} bind def - -% Identify the objects of the first page. -/identifyFirstPageObjects { % - identifyFirstPageObjects <obj#s> - Trailer /Root oget null - 1 index /PageMode knownoget { - /UseOutlines eq { - 1 index /Outlines knownoget { exch pop } if - } if - } if exch pop - 1 identifyPageObjects -} bind def - -% Identify the non-shared objects of the other pages, and the shared objects. -% Note that the page objects themselves may appear to be shared, because of -% references from Dest entries in annotations, but they must be treated as -% non-shared. Note also that some objects referenced on the first page may -% also be referenced from other pages. -/identifyOtherPageObjects { % - identifyOtherPageObjects [<pageobj#s> ...] - % <sharedobj#s> - 4 dict begin - /marks lstring Objects llength lgrowto def - % Collect objects of other pages and identify sharing. - [ 2 1 pdfpagecount { null exch identifyPageObjects } for ] - dup { - { marks exch 2 copy lget 1 add 254 .min lput } forall - } forall - % Mark document-level and first page objects. - CatalogNs { marks exch 255 lput } lforall - FirstPageNs { marks exch 255 lput } forall - % Mark the page objects themselves as non-shared. - dup { - 0 get marks exch 1 lput - } forall - % Collect the non-shared objects of each page. - dup - [ exch { - [ exch { - marks 1 index lget 1 ne { pop } if - } forall ] - } forall ] - % Collect the shared objects of each page. - exch - [ exch { - [ exch { - marks 1 index lget dup 1 le exch 255 eq or { pop } if - } forall ] - } forall ] - - % Collect the shared objects. - [ 1 1 marks llength 1 sub { - marks 1 index lget dup 1 le exch 255 eq or { pop } if - } for ] - - end -} bind def - -% Identify objects not associated with any page. -/identifyNonPageObjects { % - identifyNonPageObjects <obj#s> - 4 dict begin - /marks lstring Objects llength lgrowto def - - LPDictN marks exch 1 lput - PHSN marks exch 1 lput - CatalogNs { marks exch 1 lput } lforall - FirstPageNs { marks exch 1 lput } forall - SharedNs { marks exch 1 lput } forall - OtherPageNs { { marks exch 1 lput } forall } forall - - %****** PUT THESE IN A REASONABLE ORDER ****** - /npobj larray - 0 - 1 1 Objects llength 1 sub { - marks 1 index lget 0 eq { - Generations exch lget 0 ne { 1 add } if - } { - pop - } ifelse - } for - lgrowto def - - 0 - 1 1 Objects llength 1 sub { - marks 1 index lget 0 eq { - % i - Generations 1 index lget 0 ne { - % i - npobj 2 index % i nobj 0 - 3 -1 roll % nobj 0 i - lput 1 add - } { - pop - } ifelse - } { - pop - } ifelse - } for - pop - - npobj - end -} bind def - -% ---------------- Assign object numbers ---------------- % - -% Assign object numbers to all objects that will be copied. -% Return the first (translated) object number in the First Page xref table. -/assignObjectNumbers { % - assignObjectNumbers - - OtherPageNs { { omap pop } forall } forall - SharedNs { omap pop } forall - NonPageNs { omap pop } lforall - % Assign object numbers for the First Page xref table last. - LPDictN omap % don't pop, this is the return value - CatalogNs { omap pop } lforall - FirstPageNs { omap pop } forall - PHSN omap pop -} bind def - -% ---------------- Create the LPDict ---------------- % - -% Create the contents of the LPDict. -/createLPDict { % <phsstart> <phsend> <firstpageend> - % <xref0start> <filelength> createLPDict - - LPDict - dup /Linearized 1 put - dup /L 4 -1 roll put % filelength - dup /T 4 -1 roll put % xref0start - dup /E 4 -1 roll put % firstpageend - dup /H 5 -2 roll 1 index sub 2 array astore put % phsstart, end-start - dup /O 1 pdffindpageref 0 get omap put - /N pdfpagecount put -} bind def - -% ---------------- Adjust object positions ---------------- % - -/adjustObjectPositions { % <boundary> <deltabelow> <deltaabove> - % adjustObjectPositions - - % Objects fall into 4 categories: LPDict, PHS, Catalog, and others. - % We handle the first two as special cases. - XRef { - % Stack: bdy below above key loc - dup 5 index ge { 2 } { 3 } ifelse index add - XRef 3 1 roll ld_put - } ld_forall pop pop pop - XRef LPDictN omap HeaderLength ld_put - XRef PHSN omap PHSStart ld_put -} bind def - -% ---------------- Write the output file ---------------- % - -% Write objects identified by object number. -/writeobjn { % <obj#> writeobjn - - Generations 1 index lget pdfwriteobj -} bind def -/writeobjns { % <obj#s> writeobjns - - { writeobjn } forall -} bind def -/lwriteobjns { % <obj#s> writeobjns - - { writeobjn } lforall -} bind def - -% Write a part of the output file. -/writePart { % <proc> <label> writePart - - PDFOPTDEBUG { - dup print ( count=) print count =only ( start=) print - OFile { .fileposition } stopped { pop (???) } if = - 2 .execn - print ( end=) print - OFile { .fileposition } stopped { pop (???) } if = - } { - pop exec - } ifelse -} bind def - -% Write the header. -/writePart1 { % - writePart1 - - { - pdfwriteheader - } (part1) writePart -} bind def - -% Write the linearization parameters dictionary. -/writePart2 { % - writePart2 - - { - LPDictN writeobjn - } (part2) writePart -} bind def - -% Write the First Page xref table and trailer. -% Free variables: FirstPageXN. -/writePart3 { % <xrefstart> writePart3 - - { - FirstPageXN NObjects 1 add 1 index sub pdfwritexref - Trailer dup length 1 add dict copy Trailer xcheck { cvx } if - dup /Size NObjects 1 add put - dup /Prev 4 -1 roll put - pdfwritetrailer - 0 pdfwritestartxref - } (part3) writePart -} bind def - -% Write the Catalog and other required document-level objects. -% Free variables: CatalogNs. -/writePart4 { % - writePart4 - - { - CatalogNs lwriteobjns - } (part4) writePart -} bind def - -% Write the Primary Hint Stream. -/writePart5 { % - writePart5 - - { - PHSN writeobjn - } (part5) writePart -} bind def - -% Write the First Page's objects. -% Free variables: FirstPageNs. -/writePart6 { % - writePart6 - - { - FirstPageNs writeobjns - } (part6) writePart -} bind def - -% Write the objects of other pages (Page + non-shared objects). -% Free variables: OtherPageNs. -/writePart7 { % - writePart7 <lengths> - { - [ OtherPageNs { - OFile fileposition exch - writeobjns OFile fileposition exch sub - } forall ] - } (part7) writePart -} bind def - -% Write the shared objects of other pages. -% Free variables: SharedNs. -/writePart8 { % - writePart8 - - { - SharedNs writeobjns - } (part8) writePart -} bind def - -% Write the other objects not associated with pages. -% Free variables: NonPageNs. -/writePart9 { % - writePart9 - - { - NonPageNs { writeobjn } lforall - } (part9) writePart -} bind def - -% Write the main xref table and trailer. -% Free variables: FirstPageXN. -/writePart11xref { % writePart11 - - { - 0 FirstPageXN pdfwritexref - } (part11xref) writePart -} bind def -/writePart11rest { % <part3start> writePart11rest - - { - << /Size FirstPageXN >> pdfwritetrailer - pdfwritestartxref - } (part11rest) writePart -} bind def - -% ---------------- Write hint tables ---------------- % - -/bitsneeded { % <maxvalue> bitsneeded <#bits> - 0 exch { dup 0 eq { pop exit } if exch 1 add exch 2 idiv } loop -} bind def - -% Find the start and end of objects in the output. -/omstart { % <obj#> omstart <pos> - PDFOPTDEBUG { (start\() print dup =only } if - omap - PDFOPTDEBUG { (=>) print dup =only } if - XRef exch ld_get - PDFOPTDEBUG { (\) = ) print dup = } if -} bind def -/omend { % <obj#> omend <pos> - % The end of an object is the start of the next object. - % The caller must be sure that this object is not the last one - % in part 9. - PDFOPTDEBUG { (end\() print dup =only } if - omap - PDFOPTDEBUG { (=>) print dup =only } if - 1 add - % Check that the requested object wasn't the last one in part 6: - % the next object in the output file is the first in part 7. - PHSN omap 1 index eq { pop 1 } if - XRef exch ld_get - PDFOPTDEBUG { (\) = ) print dup = } if -} bind def -/omlength { % <obj#> omlength <length> - dup omend exch omstart sub -} bind def - -% Find the Contents of a page. -/contentsobjects { % <pagedict> contentsobjects <firstobj#> <lastobj#> true - % <pagedict> contentsobjects false - /Contents .knownget { - dup oforce % ref [] - dup type /dicttype eq { - pop 0 get dup true % ref ref - } { - exch pop % [] - dup length 0 ne { - dup 0 get 0 get % [] 1st - exch dup % 1st [] [] - length 1 sub get 0 get % 1st last - true - } { - pop false - } ifelse - } ifelse - } { - false - } ifelse -} bind def - -/contentsstart { % <pagedict> contentsstart <pos> true - % <pagedict> contentsstart false - contentsobjects { pop omstart true } { false } ifelse -} bind def - -/contentslength { % <pagedict> contentslength <length> - contentsobjects { omend exch omstart sub } { 0 } ifelse -} bind def - -/writePageOffsetHints { - PDFOPTDEBUG { /writePageOffsetHints == } if - 20 dict begin - /bits OFile bitstream def - - % Calculate least length of a page. - FirstPageLength OtherPageLengths { .min } forall - /minpl exch def - - % Calculate least contents length. - FirstPageNs 0 get Objects exch lget contentslength - OtherPageNs { 0 get Objects exch lget contentslength .min } forall - /mincl exch def - - % The Adobe documentation says that all versions of Acrobat - % require item 8 (mincl) to be zero. Patch this here. - /mincl 0 def - - % Calculate bits needed to represent greatest page length. - FirstPageLength OtherPageLengths { .max } forall - minpl sub bitsneeded /maxplbits exch def - % Calculate bits needed to represent the greatest Contents length. - FirstPageNs 0 get Objects exch lget contentslength - OtherPageNs { 0 get Objects exch lget contentslength .max } forall - mincl sub bitsneeded /maxclbits exch def - - % Per Adobe documentation, Acrobat requires that item 5 (maxplbits) - % be equal to item 9 (maxclbits). Set both to the max of the two. - maxplbits maxclbits .max /maxplbits 1 index def /maxclbits exch def - - % Mapping from object number to shared object reference - /shared_id_dict FirstPageNs length SharedNs length add dict begin - 0 FirstPageNs { 1 index def 1 add } forall - SharedNs { 1 index def 1 add } forall - pop - currentdict end def - - % Table F.3 Page offset hint table, header section - - % 1: Least number of objects in a page: - FirstPageNs length OtherPageNs { length .min } forall - /minnop 1 index def 32 bwn - % 2: Location of first page's Page object: - FirstPageNs 0 get omap XRef exch ld_get 32 bwn - % 3: Bits needed to represent greatest # of objects in a page: - FirstPageNs length OtherPageNs { length .max } forall - minnop sub bitsneeded /maxnopbits 1 index def 16 bwn - % 4: Least length of a page: - minpl 32 bwn - % 5: Bits needed to represent the greatest page length: - maxplbits 16 bwn - % 6: Least start of Contents offset: - 0 % (Acrobat requires that this be 0.) - /minsco 1 index def 32 bwn - % 7: Bits needed to represent the greatest start of Contents - % offset: - 0 % (Acrobat ignores this.) - /maxscobits 1 index def 16 bwn - % 8: Least contents length: - mincl 32 bwn - % 9: Bits needed to represent the greatest Contents length: - maxclbits 16 bwn - % 10: Bits needed to represent the greatest number of Shared - % Object references: - FirstPageNs length SharedPageNs { length .max } forall bitsneeded - /maxsorbits 1 index def 16 bwn - % 11: Bits needed to identify a Shared Object: - FirstPageNs length SharedNs length add bitsneeded - /sobits 1 index def 16 bwn - % 12: Bits needed to represent numerator of fraction: - 2 - /numfbits 1 index def 16 bwn - % 13: Denominator of fraction: - 1 - /denf 1 index def 16 bwn - - % Table F.4 Page offset hint table, per-page entry - - % 1: Number of objects in pages: - FirstPageNs length minnop sub maxnopbits bwn - OtherPageNs { - length minnop sub maxnopbits bwn - } forall - bits bitflush - - % 2: Total length of pages in bytes; - FirstPageLength minpl sub maxplbits bwn - OtherPageLengths { - minpl sub maxplbits bwn - } forall - bits bitflush - - % 3: Number of shared objects referenced from page: - FirstPageNs length maxsorbits bwn - SharedPageNs { length maxsorbits bwn } forall - bits bitflush - % 4: A shared object identifier: - FirstPageNs { shared_id_dict exch get sobits bwn } forall - SharedPageNs { - { shared_id_dict exch get sobits bwn - } forall - } forall - bits bitflush - - % 5: Numerator of fractional position for each shared object: - FirstPageNs { pop 0 numfbits bwn } forall - SharedPageNs { - { pop 0 numfbits bwn - } forall - } forall - bits bitflush - - % 6: Contents offsets: - % Following Implementation Note 133 section 6 is empty. - maxscobits 0 gt { - [FirstPageNs OtherPageNs aload pop] { - 0 get Objects exch lget contentsstart { minsco sub } { 0 } ifelse - maxscobits bwn - } forall - bits bitflush - } if - - % 7: Contents lengths: - [FirstPageNs OtherPageNs aload pop] { - 0 get Objects exch lget contentslength mincl sub maxclbits bwn - } forall - bits bitflush - - end - -} bind def - -/writeSharedObjectHints { - PDFOPTDEBUG { /writeSharedObjectHints == } if - 20 dict begin - /bits OFile bitstream def - /obj_count SharedNs length FirstPageNs length add def - - % Table F.5 Shared object hint table, header section - - % 1: Object number of first object in Shared Objects section - 0 32 bwn - % 2: Location of first object in Shared Objects section: - % If there are no shared objects, - % Acrobat sets this to the location of linearization - % parameters object (the very first object). - { pdfwriteheader } tomemory length 32 bwn - % 3: Number of Shared Object entries for first page: - FirstPageNs length 32 bwn - % 4: Number of Shared Object entries for Shared Objects - % section - obj_count 32 bwn - % 5: Bits needed to represent the greatest number of objects - % in a shared object group (always 0, because all groups - % have only 1 object): - 0 16 bwn - % 6: Least length of a Shared Object Group in bytes: - 16#7fffffff FirstPageNs { omlength .min } forall - SharedNs { omlength .min } forall - /minsol 1 index def 32 bwn - % 7: Bits needed to represent the greatest length of a - % Shared Object Group: - 0 FirstPageNs { omlength .max } forall - SharedNs { omlength .max } forall - minsol sub bitsneeded - /maxsolbits 1 index def 16 bwn - - % Table F.6 Shared object hint table, shared object group entry - - % 1: Lengths of shared object groups: - FirstPageNs { omlength minsol sub maxsolbits bwn } forall - SharedNs { omlength minsol sub maxsolbits bwn } forall - bits bitflush - % 2: MD5 flag: - obj_count { 0 1 bwn } repeat - bits bitflush - % 3: No MD5 shared object signatures. - - % 4: No number_number_of_objects_in_the_group - 1 - end -} bind def - -% ---------------- Main program ---------------- % - -/pdfOptimize { % <infile> <outfile> pdfOptimize - - realtime 3 1 roll - exch pdfdict begin pdfopenfile dup begin - 40 dict begin - /IDict exch def - /OFile exch def - % Disable check for the max object number because pdfopt adds new objects. - /NumObjects 16#7fffffff store - /starttime exch def - /ToWrite 100 dict def - /now { - QUIET { pop } { print (, t = ) print realtime starttime sub = flush } ifelse - } def - omapinit - - % Create and open a temporary file. - % Because of .setsafe, we have to open it as read-write, rather than - % opening for writing, then closing it and reopening it for reading. - - null (w+) .tempfile /TFile exch def /TFileName exch def - .setsafe - - % Read all objects into memory. - - Trailer touch - (Read objects) now - - % Encrypted files are not yet supported. - Trailer /Encrypt known { - (ERROR: Encrypted files are not yet supported.) = flush - /pdfOptimize cvx /limitcheck signalerror - } if - - % Replace indirect references to numbers. This is needed - % for the Length of streams, and doesn't hurt anything else. - - replaceReferences - (Replaced references) now - - % Create the two new objects: the linearization parameter - % dictionary, and the Primary Hint Stream. - - /LPDict 10 dict def - /PHS 10 dict cvx def % executable = stream - [LPDict PHS] createObjects - /LPDictN 1 index def 1 add - /PHSN exch def - PDFOPTDEBUG { << /LPDictN LPDictN /PHSN PHSN >> === } if - - % Count the number of objects in the output. - - 0 0 1 Objects llength 1 sub { - Generations exch lget 0 ne { 1 add } if - } for - /NObjects exch def - QUIET not { NObjects =only ( objects total) = flush } if - - % Propagate inherited attributes down the page tree. - - propagateAttributes - (Propagated attributes) now - - % Identify the document-level objects (part 4). - - identifyDocumentObjects /CatalogNs exch def - QUIET not { CatalogNs === flush } if - (Identified Catalog) now - - % Identify the first page's objects (part 6), - % including the Outlines tree if appropriate. - - pdfopencache - /FirstPageNs identifyFirstPageObjects def - QUIET not { FirstPageNs === flush } if - (Identified first page) now - - % Identify shared vs. non-shared objects for remaining pages - % (parts 7 and 8). - - identifyOtherPageObjects - /SharedNs exch def - /SharedPageNs exch def - /OtherPageNs exch def - QUIET not { OtherPageNs === flush SharedNs === flush } if - (Identified other pages) now - - % Identify objects not associated with any page (part 9). - - /NonPageNs identifyNonPageObjects def - QUIET not { NonPageNs { === } forall flush } if - (Identified non-pages) now - - % Assign final object numbers to all the objects. - % (The omap is currently empty.) - - /FirstPageXN assignObjectNumbers def - (Assigned objects #s) now - - % Write the document-level objects (part 4). - - { writePart4 } totemp - /CatalogTempEnd exch def /CatalogTempStart exch def - (Wrote Catalog) now - - % Write the first page's objects (part 6). - - { writePart6 } totemp - /FirstPageTempEnd exch def /FirstPageTempStart exch def - (Wrote first page) now - - % Write the non-shared objects for other pages (part 7). - - { writePart7 /OtherPageLengths exch def } totemp - /OtherPageTempEnd exch def /OtherPageTempStart exch def - (Wrote other pages) now - - % Write the shared objects for other pages (part 8). - - { writePart8 } totemp - /SharedTempEnd exch def /SharedTempStart exch def - (Wrote shared objects) now - - % Write the objects not associated with pages (part 9). - - { writePart9 } totemp - /NonPageTempEnd exch def /NonPageTempStart exch def - - % Compute conservative lengths of parts 2,3,5,11 of the output. - % It's OK for these to be too large, but not too small. - - % Make dummy XRef entres for LPDict and PHS. - XRef LPDictN omap 0 ld_put - XRef PHSN omap 0 ld_put - - /HeaderLength { % this is exact - writePart1 % part 1 - } tolength def - /CatalogLength % this is exact - CatalogTempEnd CatalogTempStart sub def % part 4 - /FirstPageLength % this is exact - FirstPageTempEnd FirstPageTempStart sub def % part 6 - /OtherObjectsLength % this is exact - NonPageTempEnd OtherPageTempStart sub def % parts 7,8,9 - /ObjectsLength % this is exact - CatalogLength FirstPageLength add OtherObjectsLength add def - /XrefLength { % part 11 - % The LPDict must end within the first 1024 bytes, - % so the start of the FirstPage xref table can't exceed 1024. - writePart11xref 1024 writePart11rest - } tolength def - /NominalFileLength % Make a generous allowance for parts 2,3,5. - HeaderLength ObjectsLength 3 mul add 10000 add 99999 .max def - /FirstPageXrefLength { % part 3 - NominalFileLength writePart3 - } tolength def - /LPDictLength { % part 2 - NominalFileLength dup 2 mul 2 copy add 1 index dup createLPDict writePart2 - } tolength def - - % Compute a few additional values from the above. - - /XrefBeginLength { - (xref\n0 ) ows - OFile FirstPageXN write= - } tolength def - HeaderLength LPDictLength add - /FirstPageXrefStart 1 index def - FirstPageXrefLength add - /CatalogStart 1 index def - CatalogLength add % phsstart - /PHSStart exch def - - % Adjust the object positions ignoring PHS. - % (Writing the PHS needs these.) - - 0 0 CatalogStart CatalogTempStart sub adjustObjectPositions - % Make a temporary XRef entry for the PHS, for the benefit of omend. - XRef PHSN omap CatalogStart ld_put - (Adjusted positions) now - - % Construct the hint tables (part 5). - - { writePageOffsetHints } totemp - pop /PHSTempStart exch def - { writeSharedObjectHints } totemp - exch PHSTempStart sub PHS /S 3 -1 roll put - PHSTempStart sub /PHSTempLength exch def - (Wrote hints) now - - % Prepare to read TFile. -% TFile closefile -% /TFile TFileName (r) file def - - PHS - dup /File TFile put - dup /FilePosition PHSTempStart put - dup /Length PHSTempLength put - pop - /PHSLength { writePart5 } tolength def - - % Construct the linearization parameter dictionary (part 2). - - PHSStart - dup PHSLength add % phsend - /FirstPageStart 1 index def - dup FirstPageLength add % firstpageend - dup OtherObjectsLength add - /XrefStart 1 index def - XrefBeginLength add % xref0start - dup XrefBeginLength sub XrefLength add % fileend - % Because of a bug, Acrobat Reader doesn't recognize any file - % shorter than 1K as linearized. Patch this here. - % Acrobat 9 insists on 4KB - 4096 .max - /FileLength 1 index def - createLPDict - - % Adjust the object positions again, taking the PHS into account. - - PHSStart 0 PHSLength adjustObjectPositions - (Readjusted positions) now - - % Finally, write the output file. - - writePart1 - writePart2 - FirstPageXrefStart padto - XrefStart writePart3 - CatalogStart padto - CatalogTempStart CatalogTempEnd copyrange % part 4 - writePart5 - FirstPageStart padto - FirstPageTempStart NonPageTempEnd copyrange % parts 6,7,8,9 - % No Overflow Hint Stream (part 10). - XrefStart padto - writePart11xref - { FirstPageXrefStart writePart11rest } tomemory - FileLength 1 index length sub padto ows - (Wrote output file) now - - % Wrap up. - - TFile closefile TFileName deletefile - end % temporary dict - end % IDict -} bind def - -end % pdfoptdict -.setglobal - -% Check for command line arguments. -[ shellarguments { - ] dup length 2 eq { - % Load the pdfwrite utilities if necessary. - /omapinit where { pop } { (pdfwrite.ps) runlibfile } ifelse - save exch - aload pop exch (r) file exch (w) file - 3000000 setvmthreshold - 0 setobjectformat - pdfoptdict begin pdfOptimize end - restore - } { - (Usage: gs -dNODISPLAY -- pdfopt.ps input.pdf output.pdf) = flush quit - } ifelse -} { - pop -} ifelse |