summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/epspdf
diff options
context:
space:
mode:
authorSiep Kroonenberg <siepo@cybercomm.nl>2019-02-05 22:51:58 +0000
committerSiep Kroonenberg <siepo@cybercomm.nl>2019-02-05 22:51:58 +0000
commit8098db8d720df19c9eb13838e9acceb706cc7296 (patch)
tree25c94e9f5dadd9bbc4fcc30245841946b53b180e /Master/texmf-dist/scripts/epspdf
parent3602727bc992898e5097359493e7ffe838e01c5a (diff)
Compatibility for texlua 1.9; adjustments for newer gs and tcl/tk
git-svn-id: svn://tug.org/texlive/trunk@49951 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts/epspdf')
-rw-r--r--Master/texmf-dist/scripts/epspdf/epspdf.help234
-rwxr-xr-xMaster/texmf-dist/scripts/epspdf/epspdf.tlu1238
-rw-r--r--Master/texmf-dist/scripts/epspdf/epspdf4tk.cmd1
-rwxr-xr-xMaster/texmf-dist/scripts/epspdf/epspdftk.tcl469
4 files changed, 832 insertions, 1110 deletions
diff --git a/Master/texmf-dist/scripts/epspdf/epspdf.help b/Master/texmf-dist/scripts/epspdf/epspdf.help
index 60987a216d0..c200849f0a5 100644
--- a/Master/texmf-dist/scripts/epspdf/epspdf.help
+++ b/Master/texmf-dist/scripts/epspdf/epspdf.help
@@ -19,7 +19,7 @@ Epspdf and epspdftk
3.6 Exporting PostScript or pdf from Windows programs
4 Bitmapped graphics for LaTeX and pdflatex
4.1 Linux
- 4.2 Mac OS X
+ 4.2 Mac OS
4.3 Windows
5 Troubleshooting
5.1 No options for output format visible
@@ -29,17 +29,18 @@ Epspdf and epspdftk
5.5 The page has been converted to a bitmap
5.6 Temporary files are not removed
5.7 Resources for troubleshooting
-6 Change history
- 6.1 Version 0.6
- 6.2 Version 0.5
- 6.3 Version 0.4
+Appendix A Requirements
+Appendix B Change history
+ B.1 Version 6.4
+ B.2 Version 0.6
+ B.3 Version 0.5
+ B.4 Version 0.4
Epspdf and epspdftk
*******************
-This manual is for epspdf, version 0.6.3.
+This manual is for epspdf and epspdftk, version 0.6.4.
-Copyright (C) 2006, 2008, 2009, 2010, 2011, 2013, 2014, 2015, 2016 Siep
-Kroonenberg.
+Copyright (C) 2006-2019 Siep Kroonenberg.
Copying and distribution of this file, with or without
modification, are permitted in any medium without royalty provided
@@ -52,20 +53,20 @@ Kroonenberg.
1.1 Overview
============
-Epspdftk converts files between eps, pdf and general PostScript. The
-main screen lets you open a file, select some options and convert the
-file.
+Epspdftk is a GUI program that converts files between eps, pdf and
+general PostScript in any direction.
-When opening a file, epspdf tries to find out file type and, in the case
-of a pdf file, the number of pages. This information is displayed in
-the box in the upper part of the screen.
+It lets you interactively select a file, set conversion options, and
+save in the same of another format. A configuration screen offers some
+additional conversion options.
1.2 Viewing
===========
-The View button at the bottom of the screen calls an external viewer.
+The View button at the bottom of the screen invokes an external viewer.
-Windows and OS X: Epspdftk simply tries to use the default Open command.
+Windows and Mac OS: Epspdftk simply tries to use the default Open
+command.
Under Linux the PostScript- and pdf viewers are configurable; see *note
The configuration screen::.
@@ -76,19 +77,19 @@ for the current file.
1.3 Conversion options
======================
-Grayscaling: Grayscaling is now done by Ghostscript itself and should
-"just work".
+ * Grayscaling
-Compute tight boundingbox: This option is only available if a single
-page is converted.
+ * Compute tight boundingbox. This option is only available if a
+ single page is converted.
-Page selection: The only possibilities are selecting a single page or
-selecting all pages. Converting to eps implies selecting a single page.
+ * Page selection. The only possibilities are selecting a single page
+ or selecting all pages. Converting to eps implies selecting a
+ single page.
-For general PostScript files, there is no quick way to determine the
-number of pages, so the program may not check beforehand whether you
+Note. For general PostScript files, there is no quick way to determine
+the number of pages, so the program may not check beforehand whether you
picked an existing page. If you don't like that, convert the entire
-document to pdf first - which will be done behind the scenes anyhow.
+document to pdf first - which will be done behind the scenes anyway.
Specifying options such as grayscaling or page selection may require a
multistep conversion.
@@ -96,9 +97,10 @@ multistep conversion.
1.4 Converting
==============
-The Convert and save... button calls up a file save dialog. After a
+The Convert and save... button calls up a file save dialog. After a
successful conversion, the result becomes the new current file, so you
-can judge the result by pressing the View button again.
+can judge the result by pressing the View button again - if epspdftk
+thinks that there is a suitable viewer.
1.5 The configuration screen
============================
@@ -114,7 +116,7 @@ in this screen. Epspdf looks for a number of PostScript- and pdf
viewers, from which you can select one, but you can also enter one
manually.
-For Windows and Mac OS X, there is no such configuration option. Epspdf
+For Windows and Mac OS there is no such configuration option. Epspdf
will use the program associated with the file type, which can be
configured outside epspdf.
@@ -124,37 +126,30 @@ configured outside epspdf.
Double-check the setting "Target use" under "Conversion to pdf".
"prepress" is for pdfs which are going to be printed commercially. The
options prepress, printer and default will try to embed all fonts.
-Often, printshops insist on this. With luck, Ghostscript fonts and
-system fonts will be used for fonts which were not originally embedded.
-Note that "printer" may not work with some versions of Ghostscript, due
-to a bug in the handling of color.
+Often, printshops insist on this.
On the other hand, you may prefer "screen" if file size is a concern.
See also the Ghostscript documentation, in particular Use.htm and
-Ps2pdf.htm.
+VectorDevices.htm.
Specifying anything other than "default" may cause an additional
conversion step and possibly also loss of high-level structure.
As to pdf versions: this is a trade-off between more features for higher
-versions and better compatibility for lower versions - although even
-version 1.4 is far from bleeding-edge. Converting to a lower version
-may lead to rasterization of vector data and (much) larger file sizes.
-For prepress use, the printshop or publisher may require a low version
-number but otherwise leave this at "default".
+versions and better compatibility for lower versions. Versions below
+1.4 do not support transparency. Converting a page or graphic with
+transparencies to version 1.3 will probably result in the whole page or
+graphic getting rasterized. For prepress use, consult your printshop or
+publisher. Otherwise leave the pdf version at "default".
1.5.3 Options for converting to PostScript
------------------------------------------
-For conversion the other way you may opt not to use pdftops even if it
-is available. In this case, the conversion will be done by Ghostscript.
-With older Ghostscript versions, characters may be converted to drawn
-shapes or little bitmaps, so use pdftops if available. With newer
-Ghostscript versions, with an eps2write rather than an epswrite output
-device, Ghostscript does a much better job.
-
-Under an older MikTeX without bundled pdftops, this is also the place to
-point the program to the location of pdftops.exe.
+For conversion to plain or Encapsulated PostScript, pdftops sometimes
+does a better job of preserving fonts than Ghostscript. Under Windows,
+current versions of both MiKTeX and TeX Live include pdftops. Still,
+you may opt not to use pdftops even if it is available, in which case
+Ghostscript will be used instead.
2 Command-line usage
********************
@@ -179,8 +174,8 @@ Basic usage of epspdf itself:
Typing 'epspdf --help' gives you the following summary:
$ epspdf --help
- Epspdf version 0.6.3
- Copyright (c) 2006, 2008, 2009, 2010, 2011, 2013, 2014, 2015, 2016 Siep Kroonenberg
+ Epspdf version 0.6.4
+ Copyright (c) 2006-2019 Siep Kroonenberg
Convert between [e]ps and pdf formats
Usage: epspdf[.tlu] [options] infile [outfile]
@@ -196,7 +191,7 @@ Typing 'epspdf --help' gives you the following summary:
-T, --target TARGET
One of screen, ebook, printer, prepress or default
-N, --pdfversion VERSION
- One of 1.2, 1.3, 1.4 or default
+ One of 1.2, 1.3, 1.4, 1.5, 1.6, 1.7 or default
-U Use pdftops if available
-I Reverses the above
-s, --save Save some settings to configuration file
@@ -231,7 +226,7 @@ bitmaps have (much) larger file sizes, and take longer to process.
So avoid converting vector to bitmap. However, converting from bitmap
to vector is also best avoided, since it is very hard to do well.
-Epspdf normally avoids conversion from vector to bitmap and never
+Epspdf usually avoids conversion from vector to bitmap and never
converts the other way. With the screen- and ebook "Target use" option,
included bitmaps tend to be downsampled, i.e. reduced to a lower
resolution.
@@ -241,9 +236,8 @@ resolution.
When converting to pdf, Ghostscript handles font embedding differently
depending on the "Target use" option. According to the Ghostscript
-documentation, it embeds all fonts without exception for prepress- and
-printer settings. Epspdf enforces this also for default settings. For
-screen- and ebook settings, standard fonts such as Times may be omitted.
+documentation, it embeds all fonts without exception for all targets
+except "screen", for which standard fonts such as Times may be omitted.
3.3 Eps preview headers
=======================
@@ -251,7 +245,7 @@ screen- and ebook settings, standard fonts such as Times may be omitted.
Preview headers are quietly stripped from eps files. These preview
headers are used by e.g. desktop-publishing software to represent eps
files on screen without having to interpret the PostScript code itself.
-Epspdf currently has no option to preserve or add them.
+Epspdf has no option to preserve or add them.
3.4 Bounding- and other boxes
=============================
@@ -261,36 +255,33 @@ file may have a mediabox, a trimbox and various other boxes.
Ghostscript by itself normally converts the PostScript page - which is
anchored at (0,0) - to the pdf mediabox.
-Conversion from eps to pdf usually translates the bottom-left corner to
-the (0,0) origin and sets the page to the size of the graphic. Anything
-outside the boundingbox should be cut off.
-
-I have seen Illustrator-generated pdf files with negative coordinates
-for the lower-left corner. However, this does not seem to cause
-problems.
+Conversion to pdf usually translates the bottom-left corner to the (0,0)
+origin and sets the ...box to the size of the graphic. Anything outside
+the ...box should be cut off.
3.5 Orientation
===============
-There appears to be no reliable way to set orientation for PostScript-
-and pdf files. This may result part of a graphic being cut off after
-conversion or even everything falling outside the page / mediabox /
-boundingbox.
+Ghostscript may not be able to determine the right orientation of a
+PostScript- or pdf file. This may result in part of a graphic being cut
+off after conversion or even everything falling outside the page /
+mediabox / boundingbox.
3.6 Exporting PostScript or pdf from Windows programs
=====================================================
Microsoft Office 2010 can now export to pdf, either the entire document
or a selection. This removes a major headache for Windows users.
+Windows 10 also includes a pdf printer.
-As a last resort, you can "print" to a PostScript file. From some
-programs, you can print a selection. A suitable driver which comes with
-Windows is Generic / MS Publisher Color Printer. Pay attention to the
-printer properties: choose "Outline" for font downloading and avoid the
-"Optimize for speed" setting for PostScript Output Option. In my tests,
-"Encapsulated PostScript" did not look very promising either. Try e.g.
-"Archive" instead. These options can be found under the Advanced
-button.
+As a last resort on earlier Windows versions, you can "print" to a
+PostScript file. From some programs, you can print a selection. A
+suitable driver which comes with Windows is Generic / MS Publisher Color
+Printer. Pay attention to the printer properties: choose "Outline" for
+font downloading and avoid the "Optimize for speed" setting for
+PostScript Output Option. In my tests, "Encapsulated PostScript" did
+not look very promising either. Try e.g. "Archive" instead. These
+options can be found under the Advanced button.
4 Bitmapped graphics for LaTeX and pdflatex
*******************************************
@@ -315,19 +306,19 @@ _The GIMP_
pre-installed on Linux, and is also available for other platforms.
The GIMP can save in eps- and pdf format.
-4.2 Mac OS X
-============
+4.2 Mac OS
+==========
-Mac OS X's built-in Preview application can read most bitmapped formats
+Mac OS's built-in Preview application can read most bitmapped formats
and save them as pdf or PostScript. In fact, in many cases it is an
excellent alternative to epspdf.
4.3 Windows
===========
-I know of no built-in facility for converting bitmaps to eps or pdf.
-However, many image editors can convert to eps or pdf. A couple of
-command-line utilities:
+I know of no built-in facility for converting bitmaps to eps or pdf,
+apart from "printing" to PostScript or pdf. However, many image editors
+can convert to eps or pdf. A couple of command-line utilities:
sam2p image.png image.eps
or
bmeps -c image.png image.eps
@@ -374,11 +365,11 @@ the xpdf suite and of the Poppler utilities.
5.5 The page has been converted to a bitmap
===========================================
-This may happen if the page contains features such as transparency which
-are not supported by intermediate formats.
+The usual cause is that the page contains features such as transparency
+which are not supported by the target format.
-Set pdf target use and target version both to "default" to avoid
-unnecessary conversions: '-T default -N default'
+Otherwise, set pdf target use and target version both to "default" to
+avoid unnecessary conversions: '-T default -N default'
5.6 Temporary files are not removed
===================================
@@ -395,11 +386,10 @@ unnecessary conversions: '-T default -N default'
=================================
Logfile. The GUI has a button for viewing log output. This same output
-is also written to a file 'epspdf.log'. For Linux/Unix/Mac OS X this is
-in a subdirectory .epspdf of your home directory; for Windows it is in a
-subdirectory epspdf of '%APPDATA%'. This APPDATA directory may be
-'c:\Users\YOUR USER NAME\AppData\Roaming' or 'c:\Documents and
-Settings\YOUR USER NAME\Application Data'. Type
+is also written to a file 'epspdf.log'. For Linux/Unix/Mac Mac OS this
+is in a subdirectory .epspdf of your home directory; for Windows it is
+in a subdirectory epspdf of '%APPDATA%'. This APPDATA directory may be
+'c:\Users\YOUR USER NAME\AppData\Roaming'. Type
echo %APPDATA%
in a Command Prompt window to find out which.
@@ -412,14 +402,48 @@ the button "Remove temp files", or for the command-line version, give a
which temporary files have been created.
Ghostscript- and pdftops documentation. For Ghostscript, the most
-important files are Ps2pdf.htm and Use.htm. For pdftops, type 'pdftops
--h'. For Unix, there is also a man page, and for Windows there is a
-file pdftops.txt in the distribution zip.
+important files are VectorDevices.htm and Use.htm. For pdftops, type
+'pdftops -h'. For Unix, there is also a man page, and for Windows there
+is a file pdftops.txt in the distribution zip. TeX Live includes the
+man page in pdf format: pdftops.pdf.
+
+Appendix A Requirements
+***********************
+
+Epspdftk consists of a GUI front end and a command-line back end.
+
+The command-line component, which can be used separately from the front
+end, requires ghostscript and a not too old TeX installation with
+luatex. It may further benefit from the presence of pdftops. MiKTeX
+and TeX Live for Windows meet these requirements out of the box.
+
+The front end requires a Tcl/Tk installation, or at least a tclkit
+runtime. TeX Live for Windows contains such a tclkit since late 2018,
+and epspdftk as a TeX Live package will make use of it. The minimum
+version is 8.5.
+
+For Windows there is an alternate epspdf-setup package on CTAN with an
+embedded Tcl/Tk runtime and a regular Windows installer, which also
+creates a menu shortcut and an uninstaller.
+
+Appendix B Change history
+*************************
+
+B.1 Version 6.4
+===============
+
+Compatibility changes for luatex 1.9 and later.
+
+The location of pdftops is no longer configurable; it is only searched
+for on the searchpath.
+
+Some corner cases should be handled more successfully.
-6 Change history
-****************
+The GUI now requires Tcl/Tk version 8.5 or higher. On Windows,
+epspdf.tlu is now invoked via a batchfile. This should prevent black
+console windows popping up.
-6.1 Version 0.6
+B.2 Version 0.6
===============
The command-line backend component has been rewritten in texlua and
@@ -430,17 +454,15 @@ This also works for bitmaps.
Croppping of pdfs is now accomplished by running luatex on a suitable
wrapper file (same method as Heiko Oberdiek's pdfcrop). Such a
-conversion preserves advanced features which might otherwise get lost
-during a PostScript round-trip.
+conversion preserves advanced features which might otherwise get lost.
-I no longer own a Mac, and therefore no longer try to provide an
-AppleScript wrapper.
+I no longer try to provide an AppleScript wrapper.
The current version has no provisions for custom Ghostscript- or pdftops
parameters. The corresponding command-line options are accepted but
have no effect.
-6.2 Version 0.5
+B.3 Version 0.5
===============
The GUI has been rewritten in Tcl/Tk, removing the dependence on the
@@ -452,10 +474,10 @@ single executable containing 'epspdftk.tcl' and a Tcl/Tk runtime. See
<http://wiki.tcl.tk/52>.
Epspdf now uses its own subdirectory for both the logfile and the
-configuration file. For Linux/Unix/Mac OS X this is '$HOME/.epspdf',
-for Windows it is '%APPDATA%\epspdf'. On all supported platforms,
-settings are stored in the file 'config' in this directory. Under
-Windows, the registry is no longer used for this.
+configuration file. For Linux/Unix/Mac OS this is '$HOME/.epspdf', for
+Windows it is '%APPDATA%\epspdf'. On all supported platforms, settings
+are stored in the file 'config' in this directory. Under Windows, the
+registry is no longer used for this.
A button has been added to view log output.
@@ -466,7 +488,7 @@ versions, temporary files were deleted at the end of the entire epspdftk
session but that has become less practical now that the GUI and epspdf
itself have become two separate programs.
-The "Open with..." option for Windows has been dropped for technical
+The "Open with..." option for Windows has been dropped for technical
reasons.
The "-version" option now prints the version string instead of setting
@@ -474,7 +496,7 @@ the desired pdf version.
The "-info" option now also prints the number of pages for pdf files.
-6.3 Version 0.4
+B.4 Version 0.4
===============
Hi-res boundingboxes are now supported. By default, conversion from eps
diff --git a/Master/texmf-dist/scripts/epspdf/epspdf.tlu b/Master/texmf-dist/scripts/epspdf/epspdf.tlu
index 709a2d9b691..0007ade0742 100755
--- a/Master/texmf-dist/scripts/epspdf/epspdf.tlu
+++ b/Master/texmf-dist/scripts/epspdf/epspdf.tlu
@@ -2,17 +2,22 @@
kpse.set_program_name('texlua')
--- epspdf conversion utility
+--[[
+epspdf conversion utility
--- 0.6.0: first texlua version
--- 0.6.1: allow TeX installation on path with spaces
--- 0.6.2: compatibility fix for luatex 0.81
--- 0.6.3: compatibility fixes for luatex 0.9x
+0.6.0: first texlua version
+0.6.1: allow TeX installation on path with spaces
+0.6.2: compatibility fix for luatex 0.81
+0.6.3: compatibility fixes for luatex 0.9x
+0.6.4: adaptations for newer versions of LuaTeX and ghostscript;
+ some refactoring; better handling of some corner cases
-ep_version = '0.6.3'
-ep_copyright = '2006, 2008, 2009, 2010, 2011, 2013, 2014, 2015, 2016'
+Copyright (C) 2006-2019 Siep Kroonenberg
---[[
+siepo at bitmuis nl
+
+This program is free software, licensed under the GNU GPL, >=2.0.
+This software comes with absolutely NO WARRANTY. Use at your own risk!
Note.
TeX code for cropping pdfs adapted from Heiko Oberdiek's pdfcrop utility
@@ -26,14 +31,15 @@ Program structure
- other general utilities
- infrastructure: logging and temporary files
- reading and writing settings
- - gui function
+ - gui function for communicating with frontend
- boundingboxes
- manipulating [e]ps- and pdf files
- the PsPdf object:
- creator functions
- boundingbox handling
- one-step conversion methods
- - any_to_any method
+- any_to_any function.
+ this function checks options, the one-step converters check for success.
- main initialization section:
- collecting system information
- infrastructure: setting up logging and temp directory
@@ -71,6 +77,9 @@ else
path_sep = ';'
end
+infile = false
+outfile = false
+
bufsize=16000 -- for reading and writing files
-- these `declarations' are not really needed;
@@ -84,8 +93,8 @@ cwd = ''
-- Actual values determined during system-dependent initialization
is_miktex = false
is_tl_w32 = false
--- recent versions of miktex include pdftops
-auto_psp = true -- configuration of pdftops_prog not needed or not allowed
+
+-- Luatex 1.09 and later replace epdf with pdfe
-- some global file- and directory names
gs_prog = false
@@ -111,7 +120,7 @@ gray_options = false
-- logging ------------------------
-- we open and close the logfile anew for each write.
--- failure to open constitutes no error.
+-- failure to open does not constitute an error.
function print_log(s)
local f = io.open(logfile, 'a')
@@ -141,6 +150,9 @@ end
-- When run from the Tcl/Tk gui, this gui will capture error messages.
function errror(mess)
+ if infile and outfile then
+ mess = 'Failure to convert '..infile..' to '..outfile..':\n'..mess
+ end
if logfile then pcall(write_log, mess) end
-- ignore result of pcall: we can do nothing about failure
error(mess, 2)
@@ -151,38 +163,24 @@ function warn(mess)
print(mess)
end
-function dbg(mess)
- if options.debug then
- warn(mess)
- end
-end
-
---[[
-
-function dbg_opt()
- if options.debug then
- local mess = ''
- for _, k in ipairs({'bbox', 'gray', 'page'}) do
- mess = mess.. ' ' .. k .. ': ' .. tostring(options.k)
- end
- dbg(mess)
- end
-end
-
---]]
+-- function dbg(mess)
+-- if options.debug then
+-- warn(mess)
+-- end
+-- end
-- file- and path utilities ----------------
-function ep_shortname(path)
- if os.type=='unix' then
- return path
- else
- -- shortname appears not to work under miktex
- -- so return original path as a fallback
- local sp = lfs.shortname(path)
- return sp or path
- end
-end
+-- function ep_shortname(path)
+-- if os.type=='unix' then
+-- return path
+-- else
+-- -- shortname appears not to work under miktex
+-- -- so return original path as a fallback
+-- local sp = lfs.shortname(path)
+-- return sp or path
+-- end
+-- end
-- prepend or append dir to path if necessary
function maybe_add_path(dir, append)
@@ -288,41 +286,22 @@ end -- absolute_path
-- we return the original string, although we only need a yes or no answer
function find_on_path (prog)
- if os.type ~= 'unix' then
- errror('find_on_path: this is a unix-only function')
- end
- for d in string.gmatch(os.getenv('PATH'), '[^:]+') do
- if lfs.isfile(d..'/'..prog) then
- return prog
+ if os.type == 'unix' then
+ for d in string.gmatch(os.getenv('PATH'), '[^:]+') do
+ if lfs.isfile(d..'/'..prog) then
+ return prog
+ end
+ end
+ else
+ for d in string.gmatch(os.getenv('PATH'), '[^;]+') do
+ if lfs.isfile(d..'\\'..prog) then
+ return prog
+ end
end
end
return false
end -- find_on_path
--- On Windows, we do not count so much on the existing searchpath
--- so is_prog tests whether the file exists and is an exe file.
-
-function is_prog (path)
- -- 1. test for and if necessary add extension
- -- 2. test for existence
- -- 3. returns either false or absolute path
- if os.type ~= 'windows' then
- errror('is_prog: this is a Windows-only function')
- end
- if not path then
- return false
- end
- if not string.lower(string.sub(path,-4,-1))=='.exe' then
- path = path..'.exe'
- end
- path = absolute_path(path)
- if not (path and lfs.isfile(path)) then
- return false
- else
- return path
- end
-end -- is_prog
-
function dir_writable(d)
-- because directory attributes do not tell the whole story,
-- we actually try to create a file in the directory.
@@ -368,10 +347,8 @@ function system_tempdir ()
d = '/tmp'
end
end
- -- if d then dbg('system tempdir: '..d) end
-- cygwin: $TEMP=/tmp, root '/' being root of cygwin installation
if d and not dir_writable(d) then
- dbg('unfortunately, '..d..' not writable')
d = false
end
return d
@@ -424,6 +401,7 @@ function tab_combine (t)
return res
end -- tab_combine
+-- files ----------------------------------------------------
-- Copy a file in chunks, with optional length and offset.
-- Since files may be very large, we copy them piecemeal.
-- An initial chunk of size bufsize should be plenty to include
@@ -434,6 +412,22 @@ function slice_file(source, dest, len, offset, mode)
-- specifying false as value
-- Assume caller ensured parameters of correct type.
-- We do not allow negative offsets.
+
+ local copy2self = false
+ if source==dest then copy2self = true end
+ if os.type=='unix' and
+ lfs.attributes(source,'ino')==lfs.attributes(dest,'ino') then
+ copy2self = true
+ end
+ if copy2self and not len and not offset and mode~='ab' then
+ return -- nothing to do
+ elseif copy2self then
+ errror('slice_file invoked with identical source and destination '..
+ source..' and non-simple copy')
+ end
+ -- in practice any_to_any already checks for this.
+ -- in addition, the main program makes a backup if infile==outfile
+
local sz = lfs.attributes(source, 'size')
if not offset then
offset = 0
@@ -444,8 +438,6 @@ function slice_file(source, dest, len, offset, mode)
len = sz - offset
end
if not mode then mode = 'wb' end
- -- dbg('copying '..len..' bytes of '..source..' to '..dest..' from '..offset
- -- ..' in '..mode..' mode')
local buffer=''
local s=io.open(source, 'rb')
s:seek('set', offset)
@@ -474,6 +466,12 @@ function move_or_copy(source, dest)
warn('Removing old '..dest)
os.remove(dest) -- in case of failure, go ahead anyway
end
+ -- Windows: try first renaming in-place before moving to the right directory
+ if os.type == 'windows' then
+ if os.rename(source, source..'.renamed') then
+ source = source..'.renamed'
+ end
+ end
if not os.rename(source, dest) then
slice_file(source, dest) -- bails out on failure
local ok, err_mess = os.remove(source)
@@ -498,10 +496,8 @@ function mktemp(ext)
for i=0,99 do
froot = string.format('%02d.', i)
fname = froot..ext
- -- dbg('New temp file '..fname..'?')
if ext~='tex' then
if not lfs.isfile(fname) then
- -- dbg(fname..' available')
f = io.open(fname, 'wb')
if not f then
errror('Cannot create temporary file '..fname)
@@ -535,11 +531,9 @@ function mktemp(ext)
end
function waitasec()
- -- stupid windows file locking
- if os.type=='windows' and (tonumber(os.uname().version) or 0)>=6 then
+ -- stupid windows file locking; assume vista or later
+ if os.type=='windows' then
os.execute('timeout /t 1 /nobreak >nul')
- elseif os.type=='windows' then
- os.execute('ping -n 1 localhost >NUL')
-- else do nothing
end
-- error checking pointless
@@ -573,9 +567,9 @@ function cleantemp()
end
end
--- epsdevice -----------------------
+-- gs_epsdevice -----------------------
-function epsdevice()
+function gs_epsdevice()
local gh = io.popen(gs_prog..' -help')
local s = gh:read("*a")
gh:close()
@@ -599,17 +593,12 @@ function write_settings (file)
end
else -- stdout to be captured by epspdftk
f = io.output()
- if os.type=='windows' and not auto_psp then
- f:write('psp_config = yes', eol)
- end
end
for k, v in pairs(settings) do
- if k ~= 'pdftops_prog' or os.type=='windows' then
- if descriptions[k] and file then
- f:write(eol, '# ', descriptions[k], eol)
- end
- f:write(k, ' = ', tostring(v), eol)
+ if descriptions[k] and file then
+ f:write(eol, '# ', descriptions[k], eol)
end
+ f:write(k, ' = ', tostring(v), eol)
end
if file then
f:close()
@@ -634,15 +623,12 @@ function read_settings(file)
f:close()
end
if not contents or contents=='' then
- dbg('No settings read')
return
- -- else
- -- dbg(contents)
end
-- remove initial \r and \n characters
contents = string.gsub(contents, '^[\r\n]*', '');
-- gmatch chops contents into series of non-line-ending characters
- -- possibly followed by line-ending characters.
+ -- possibly followed by line-ending characters i.e. in lines
local k, v, vl, vnum
for l in string.gmatch(contents, '[^\r\n]+[\r\n]*') do
l = string.match(l,'[^\r\n]*')
@@ -660,31 +646,6 @@ function read_settings(file)
if in_list(v, pdf_versions) then
settings[k] = v
end
- --[[
- elseif k == 'ignore_hires_bb' then
- vl = string.lower(string.sub(v,1,1))
- if v == 0 or vl == 'n' or vl == 'f' then
- settings.use_hires_bb = true
- elseif v == 1 or vl == 'y' or vl == 't' then
- settings.use_hires_bb = false
- end
- elseif k == 'use_hires_bb' then
- vl = string.lower(string.sub(v,1,1))
- if v == 0 or vl == 'n' or vl == 'f' then
- settings.use_hires_bb = false
- elseif v == 1 or vl == 'y' or vl == 't' then
- settings.use_hires_bb = true
- end
- elseif k == 'bb_spread' then
- vnum = tonumber(v)
- if vnum and vnum >= 0 then
- settings[k] = math.modf(v) -- truncate to integer
- end
- --]]
- elseif k == 'pdftops_prog' then
- if is_miktex then
- settings.pdftops_prog = is_prog(v)
- end -- else ignore
elseif k == 'ignore_pdftops' then
vl = string.lower(string.sub(v,1,1))
if v == 0 or vl == 'n' or vl == 'f' then
@@ -707,13 +668,27 @@ function read_settings(file)
settings.pdf_viewer = v
elseif k == 'default_dir' then
settings.default_dir = v
- elseif k == 'default_dir' then
- settings.default_dir = v
end -- test for k
end -- not matching ^#
end -- for
end -- read settings
+function version_added()
+ for _, v in pairs(pdf_options) do
+ if string.find(v, '-dCompatibilityLevel') then
+ return true
+ end
+ end
+ return false
+end
+
+function maybe_add_version_parameter()
+ if options.type=='pdf' and not options.bbox and settings.pdf_version and
+ settings.pdf_version~='default' and not version_added() then
+ table.insert(pdf_options, '-dCompatibilityLevel#'..settings.pdf_version)
+ end
+end
+
-- gui: reading and writing settings -----------
function gui(action)
@@ -733,12 +708,19 @@ end
-- boundingboxes ---------------------------------------------------
--- [HR]Bb.coords names now same as those of epdf PDFRectangle
+-- [HR]Bb.coords names same as those of epdf PDFRectangle
+-- but the new pdfe simply uses an array
Bb = {}
Bb.coords = {'x1', 'y1', 'x2', 'y2'}
function Bb:from_rect(r)
+ -- also handle the case that r is a 4-element array:
+ if not r.x1 then r.x1 = r[1] end
+ if not r.y1 then r.y1 = r[2] end
+ if not r.x2 then r.x2 = r[3] end
+ if not r.y2 then r.y2 = r[4] end
+
for _,k in ipairs(self.coords) do
if not r[k] or type(r[k])~='number' then
errror('from_rect called with illegal parameters')
@@ -778,53 +760,6 @@ function Bb:from_comment(s)
return Bb:from_rect(b)
end
---[[ some no-longer-used bbox functions
-
-function Bb:copy ()
- local b = {}
- for _,k in ipairs(self.coords) do b[k] = self[k] end
- setmetatable(b, {__index=self})
-end
-
-function Bb:width()
- return self.x2 - self.x1
-end
-
-function Bb:height()
- return self.y2 - self.y1
-end
-
-function Bb:expand ()
- -- in-place expansion; does not return an object.
- -- any point in preserving non-negativity?
- local i = settings.bb_spread
- if i and i>0 then
- -- if x1~=0 then x1 = x1-1 end
- -- if y1~=0 then y1 = y1-1 end
- self.x1 = self.x1 - 1
- self.y1 = self.y1 - 1
- self.x2 = self.x2 + 1
- self.y2 = self.y2 + 1
- end
-end
-
--- gs handles this now
--- call this via pcall
-function Bb:wrapper()
- local fn = mktemp('ps')
- local f = io.open(fn, 'wb')
- f:write(string.format('%%%%BoundingBox: 0 0 %d %d\n',
- self:width(), self:height())
- .. string.format('<< /PageSize [%d %d] >> setpagedevice\n',
- self:width(), self:height())
- .. 'gsave\n'
- .. string.format('%d %d translate\n', -self.x1, -self.y1))
- f:close()
- return fn
-end
-
---]]
-
function Bb:nonnegative ()
return self.x1>=0 and self.y1>=0
end
@@ -842,6 +777,12 @@ HRBb = {}
setmetatable(HRBb, {__index=Bb})
function HRBb:from_rect(r)
+ -- also handle the case that r is a 4-element array:
+ if not r.x1 then r.x1 = r[1] end
+ if not r.y1 then r.y1 = r[2] end
+ if not r.x2 then r.x2 = r[3] end
+ if not r.y2 then r.y2 = r[4] end
+
for _,k in ipairs(self.coords) do
if not r[k] or type(r[k])~='number' then
errror('from_rect called with illegal parameters')
@@ -865,7 +806,6 @@ HRBb.bb_pat = '^%s*%%%%HiResBoundingBox:'
HRBb.bb_end = '^%s*%%%%HiResBoundingBox:%s*%(%s*atend%s*%)%s*$'
function HRBb:from_comment(s)
- -- dbg('hrbb from '..s)
local p = self.bb_pat..'%s*([-+.%deE]+)'..string.rep('%s+([-+.%deE]+)',3)
local b = {}
b.x1, b.y1, b.x2, b.y2 = string.match(s, p)
@@ -878,6 +818,10 @@ function HRBb:from_comment(s)
return HRBb:from_rect(b)
end
+function HRBb:nonnegative ()
+ return self.x1>=0 and self.y1>=0
+end
+
function HRBb:comment()
return string.format('%%%%HiResBoundingBox: %f %f %f %f',
self.x1, self.y1, self.x2, self.y2)
@@ -885,10 +829,6 @@ end
--[[
-function HRBb:expand ()
- errror('HRBb:expand not available')
-end
-
-- no longer used: gs handles this
-- call this one also via pcall
function HRBb:wrapper()
@@ -910,10 +850,10 @@ end
-- manipulating eps/ps/pdf files -----------------------------------
-function identify(path)
- local f = io.open(path, 'rb')
+function identify()
+ local f = io.open(infile, 'rb')
if not f then
- errror('Failure to open '..path..' for identification')
+ errror('Failure to open '..infile..' for identification')
end
local filestart= f:read(23)
f:close()
@@ -925,7 +865,7 @@ function identify(path)
return 'eps'
elseif string.match(filestart,'^%%!PS%-Adobe%-%d%.%d') then
for _, p in ipairs({'.eps', '.epi', '.epsi', '.epsf'}) do
- if string.sub(string.lower(path), -1-string.len(p),-1) == p then
+ if string.sub(string.lower(infile), -1-string.len(p),-1) == p then
return 'eps'
else
return 'ps'
@@ -940,45 +880,52 @@ function identify(path)
end -- identify
function pdf_props(path)
- local pdfdoc = epdf.open(path)
- if not pdfdoc then
- errror('epdf.open failed on '..path)
+ local pdfdoc, pgs, maver, miver
+ if pdfe then
+ pdfdoc = pdfe.open(path)
+ if pdfdoc then
+ pgs = pdfe.getnofpages(pdfdoc)
+ maver, miver = pdfe.getversion(pdfdoc)
+ end
+ if not (pdfdoc and pgs and maver and miver) then
+ errror('pdfe failed to get information about '..path)
+ end
+ pdfe.close(pdfdoc)
+ else
+ local cat
+ pdfdoc = epdf.open(path)
+ if pdfdoc then
+ cat = pdfdoc:getCatalog()
+ if cat then
+ pgs = cat:getNumPages()
+ end
+ maver = pdfdoc:getPDFMajorVersion()
+ miver = pdfdoc:getPDFMinorVersion()
+ end
+ if not (pdfdoc and pgs and maver and miver) then
+ errror('epdf failed to get information about '..path)
+ end
+ -- epdf.close(pdfdoc)
end
-- if os.type=='windows' then waitasec() end
- local cat = pdfdoc:getCatalog()
- if not cat then
- errror('Cannot open pdf catalog of '..path)
- end
- local pg = cat:getNumPages()
- if not pg then
- errror('Cannot read n. of pages of '..path)
- end
- local maver = pdfdoc:getPDFMajorVersion()
- if not maver then
- errror('Cannot read pdf major version of '..path)
- end
- local miver = pdfdoc:getPDFMinorVersion()
- if not miver then
- errror('Cannot read pdf minor version of '..path)
- end
if maver > 1 then
print(path..' has pdf major version \n'..tostring(maver)..
' which is unsupported;\n'..
'Continuing with fingers crossed...')
end
- return pg, miver, maver
-end
+ return pgs, miver, maver
+end -- pdf_props
-function info (infile)
- local intype = identify(infile)
+function info()
+ local intype = identify()
if not intype then
print(infile..' has an unsupported filetype.')
elseif intype~='pdf' then
print(infile..' has type '..intype..'.')
else
- local pg, miver, maver = pdf_props(infile)
+ local pgs, miver, maver = pdf_props(infile)
print(infile..' has type pdf, version '..tostring(maver)..
- '.'..tostring(miver)..' and has '..tostring(pg)..' pages.')
+ '.'..tostring(miver)..' and has '..tostring(pgs)..' pages.')
end
os.exit()
end
@@ -990,7 +937,6 @@ PsPdf = {}
-- creators
function PsPdf:new(ext)
- -- dbg('PsPdf:new')
local psp = {}
setmetatable(psp, {__index = self})
-- assign temp file
@@ -1013,7 +959,6 @@ function PsPdf:new(ext)
end -- PsPdf:new
function PsPdf:from_path(path)
- -- dbg('PsPdf:from_path')
local psp = {}
setmetatable(psp, {__index = self})
psp.path = path
@@ -1031,23 +976,40 @@ function PsPdf:from_path(path)
end
psp.bb = false
psp.hrbb = false
- -- calculate when needed
+ -- only calculate when needed
return psp
end -- PsPdf:from_path
---[===[ getting boundingbox property from file itself --------------
+-- do we need to downgrade the pdf to a lower version?
+-- consider [e]ps lower than any pdf version
+function PsPdf:to_downgrade()
+ if self.type~='pdf' then
+ return false
+ elseif options.type~='pdf' then
+ return true
+ elseif settings.pdf_version=='default' then
+ return false
+ elseif settings.pdf_version~='default' and
+ self.maver+0.1*self.miver-0.001 > tonumber(settings.pdf_version) then
+ -- -0.001: exact binary representation of pdf_version not guaranteed
+ return true
+ else
+ return false
+ end
+end
+
+--[[ getting boundingbox property from file itself --------------
-find_bb_simple: use only for eps PsPdf objects we generated
+get_bb_simple: use only for eps PsPdf objects we generated
ourselves, so we can assume that the bbox comments are in the header
and the hires bb lies within the lores bb.
Of course the file itself is not rewritten.
---]===]
+--]]
-function PsPdf:find_bb_simple()
- -- dbg('PsPdf:find_bb_simple')
+function PsPdf:get_bb_simple()
if self.type~='eps' then
- errror('find_bb_simple called with non-eps file '..self.path)
+ errror('get_bb_simple called with non-eps file '..self.path)
end
self.bb = false
self.hrbb = false
@@ -1074,9 +1036,7 @@ function PsPdf:find_bb_simple()
return self -- no real need for a return value
end
-function PsPdf:bb_from_gs(pg)
-
- -- dbg('bb_from_gs '..pg)
+function PsPdf:bb_from_gs()
if self.type=='ps' then
errror('bb_from_gs called with ps file '..self.path)
@@ -1086,19 +1046,25 @@ function PsPdf:bb_from_gs(pg)
end
if self.type=='eps' and not self.bb:nonnegative() then
errror('bb_from_gs called on ' .. self.path ..
- ' which has some negative boundingbox coordinates')
+ ' which has some negative boundingbox coordinates')
+ -- any_to_any should guard against such an invocation
end
- -- A pdf can also have negative ...Box coordinates, but apparently
- -- for pdf the bbox returned by gs is relative to the lower-left corner.
- -- Anyhow, with pdf it all works out even with negative coordinates.
-- Since Ghostscript writes the boundingbox comments to stderr,
-- we need a shell to intercept this output:
local bb_file = mktemp('dsc')
- local cmdline = gs_prog .. ' ' .. table.concat(gs_options,' ')
+ -- a somewhat low resolution parameter may help gs
+ -- deal with eps files with large coordinates
+ -- but this may impact the accuracy of the HRBb
+ local cmdline
+ if self.type=='eps' and (self.bb.x2 > 850 or self.bb.y2 > 850) then
+ cmdline = gs_prog .. ' -r300 ' .. table.concat(gs_options,' ')
+ else
+ cmdline = gs_prog .. ' ' .. table.concat(gs_options,' ')
+ end
if self.type=='pdf' then
- if not pg then pg=1 end
+ pg = options.page or 1
cmdline = cmdline .. ' -dFirstPage#' .. tostring(pg) ..
' -dLastPage#' .. tostring(pg)
end
@@ -1145,7 +1111,8 @@ end
function PsPdf:eps_clean()
-- return a PsPdf object referring to a new file
- -- without a preview header and with boundingbox(es) in the header
+ -- without a preview header and with boundingbox(es) in the header.
+ -- return a new file even if no changes were needed.
local function bytes2num (s, i)
-- convert substring s[i..i+3] to a number.
@@ -1158,7 +1125,6 @@ function PsPdf:eps_clean()
-- + string.byte(s,i+1)) + string.byte(s,i))
end
- -- dbg('PsPdf:eps_clean '..self.path)
if self.type~='eps' and self.type~='epsPreview' then
errror('epsclean called with non-eps file ' .. self.path)
end
@@ -1182,14 +1148,12 @@ function PsPdf:eps_clean()
if not offset then
errror('Could not read preview header of ' .. self.path)
end
- -- dbg(tostring(offset)..' '..tostring(ps_length))
end
-- create the PsPdf object which is to be returned
local psp
psp = PsPdf:new('eps')
- -- dbg(psp.path)
-- read an initial and if necessary a final chunk of the file
-- to find boundingbox comments.
@@ -1218,9 +1182,7 @@ function PsPdf:eps_clean()
-- remaining, unscanned length of input buffer slurp
local unscanned = math.min(ps_length,bufsize)
- -- dbg('bytes to be read: '..tostring(unscanned))
slurp = fin:read(unscanned)
- -- dbg('Read from '..self.path..': '..string.len(slurp)..' bytes')
-- unnecessary:
psp.bb = nil
@@ -1237,18 +1199,14 @@ function PsPdf:eps_clean()
atend = true
i_bb = i
elseif string.match(l, Bb.bb_pat) then
- -- dbg(l)
psp.bb = Bb:from_comment(l)
- -- dbg(psp.bb:comment())
-- from_comment errors out on failure; no need to check return value
i_bb = i
elseif string.match(l, HRBb.bb_end) then
hr_atend = true
i_hrbb = i
elseif string.match(l, HRBb.bb_pat) then
- -- dbg(l)
psp.hrbb = HRBb:from_comment(l)
- -- dbg(psp.hrbb:comment())
i_hrbb = i
end -- bbox line
end -- eol/non-eol
@@ -1281,14 +1239,11 @@ function PsPdf:eps_clean()
j_bb, j_hrbb, j_end = false, false, false
while unscanned>0 do
j = j - 1
- -- dbg(j)
if string.find(slurp,'[\n\r]', string.len(slurp)) then
- -- dbg('eol(s)')
slurp,l = string.match(slurp, '^(.-)([\n\r]+)$')
-- '-': non-greedy matching
else
slurp,l = string.match(slurp, '^(.-)([^\n\r]+)$')
- -- dbg(l)
if string.match(l, Bb.bb_pat) then
psp.bb = Bb:from_comment(l)
j_bb = j
@@ -1318,7 +1273,6 @@ function PsPdf:eps_clean()
post_lines[j_bb+1] = ''
end
if hr_atend and j_hrbb then
- -- dbg(psp.hrbb:comment())
-- pre_lines[i_hrbb] = post_lines[j_hrbb]
pre_lines[i_hrbb] = psp.hrbb:comment()
post_lines[j_hrbb] = ''
@@ -1340,19 +1294,14 @@ end -- eps_clean
function PsPdf:eps_crop()
- -- not a proper conversion, although
- -- we use the Ghostscript bbox device for a tight boundingbox.
+ -- conversion is not done by an external program, although
+ -- we invoke Ghostscript with a bbox device for a tight boundingbox.
-- We use both the regular and the hires boundingbox from gs.
-- The eps should already have been cleaned up by eps_clean,
-- and the current boundingbox should not contain negative coordinates,
-- otherwise the bbox output device may give incorrect results.
-- Only the boundingbox in the eps is rewritten.
- -- dbg('PsPdf:eps_crop '..self.path)
- if self.type~='eps' then
- errror('eps_crop called with non-eps file ' .. self.path)
- end
-
-- create the PsPdf object which is to be returned
local psp = PsPdf:new('eps')
@@ -1377,9 +1326,7 @@ function PsPdf:eps_crop()
-- remaining, unscanned length of input buffer slurp
local unscanned = math.min(ps_length,bufsize)
- -- dbg('bytes to be read: '..tostring(unscanned))
slurp = fin:read(unscanned)
- -- dbg('Read from '..self.path..': '..string.len(slurp)..' bytes')
i, i_bb, i_hrbb = 0, false, false
while unscanned>0 do
i = i+1
@@ -1421,17 +1368,21 @@ function PsPdf:eps_crop()
slice_file(self.path, psp.path,
lfs.attributes(self.path,'size') - offset, offset, 'ab')
options.bbox = false
- -- dbg('eps_crop from '..self.path..' to '..psp.path)
return psp
end -- eps_crop
---[===[ real conversions involving a single call of gs, pdftops or texlua
+--[[
+
+most of these conversions involve a single invocation of gs, pdftops or texlua
Each conversion fullfills all options that it can: gray, bbox and
page. gray when converting to pdf, bbox when converting from eps or
from pdf to pdf and page when converting from pdf. It then sets the
fullfilled option(s) to false.
+We make sure to do the tight boundingbox before a file format downgrade:
+rasterization of the page or graphic frustrates boundingbox calculation.
+
We like to preserve fonts as fonts. gs does this when generating
pdf, but may fail for fonts such as cid and large truetype when
generating PostScript. In such cases, pdftops may succeed. However,
@@ -1439,66 +1390,82 @@ it seems that if the page contains an element that does not cleanly
convert, pdftops simply rasterizes the entire page, and that this
choice is made per page.
---]===]
+--]]
--- TODO: multiple pages?
--- (means additional parameter checking)
+-- Converting from pdf to pdf using luatex; no grayscaling here
--- Converting from pdf to pdf using luatex; no grayscaling
+function PsPdf:getpgbox()
+ if options.page and options.page > self.pages then
+ errror('PsPdf:getpgbox called with non-existent page '.. options.page)
+ end
+ local pg = options.page or 1
+ local bb, hrbb, pgbox, pdfdoc, ppage
+ if pdfe then
+ pdfdoc = pdfe.open(self.path)
+ if pdfdoc then
+ ppage = pdfe.getpage(pdfdoc, pg)
+ if not ppage then errror('did not get page') end
+ if ppage then
+ pgbox = pdfe.getbox(ppage, 'MediaBox')
+ if not pgbox then
+ pgbox = pdfe.getbox(ppage, 'CropBox')
+ if not pgbox then
+ pgbox = pdfe.getbox(ppage, 'TrimBox')
+ if not pgbox then
+ errror('No box acquired')
+ end
+ end
+ end
+ end
+ end
+ pdfe.close(pdfdoc)
+ else
+ local cat
+ pdfdoc = epdf.open(self.path)
+ if pdfdoc then
+ cat = pdfdoc:getCatalog()
+ if cat then
+ ppage = cat:getPage(pg)
+ if ppage then
+ pgbox = ppage:getMediaBox()
+ if not pgbox then
+ pgbox = ppage:getCropBox()
+ if not pgbox then
+ pgbox = ppage:getTrimBox()
+ end
+ end
+ end
+ end
+ end
+ end
+ -- normalization and further checks, including for non-nil,
+ -- by HRBb:from_rect, which errors out on failures
+ if pgbox then
+ pgbox = HRBb:from_rect(pgbox)
+ -- this also converts numeric array indices to x1 ... y2 if necessary
+ else
+ errror('Cannot get page box from '..self.path..' page '..pg)
+ end
+ return pgbox
+end -- PsPdf:getpgbox
function PsPdf:pdf_crop()
- -- options to be fulfilled: page, boundingbox
- -- only called directly.
- -- embeds the pdf with crop parameters into a new (lua)tex document
- -- dbg('PsPdf:pdf_crop '..self.path)
- if not (options.bbox or options.page) then
- return self
- end
+ -- options to be fulfilled: page, boundingbox if possible
+ -- embeds the pdf with boundingbox parameters into a new (lua)tex document
if options.page and options.page > self.pages then
errror('PsPdf:pdf_crop called with non-existent page '.. options.page)
end
- local pg = options.page or 1
- local bb, hrbb, pgbox, pgboxname
- do
- local pdoc, pcat, ppage
- -- we already have the pages, miver and maver properties
- -- but need properties of the desired page as well
- pdoc = epdf.open(self.path)
- if not pdoc then
- errror('Epdf: cannot open '..self.path)
- end
- -- dbg('about to get catalog')
- pcat = pdoc:getCatalog()
- if not pcat then
- errror('Cannot open catalog of '..self.path)
- end
- -- dbg('got catalog')
- ppage = pcat:getPage(pg)
- if not ppage then
- errror('Epdf: cannot open page object '..tostring(pg)..' of '..self.path)
- end
- pgboxname = 'trimbox'
- pgbox = ppage:getTrimBox()
- if not pgbox then
- pgboxname = 'cropbox'
- pgbox = ppage:getCropBox()
- end
- if not pgbox then
- pgboxname = 'mediabox'
- pgbox = ppage:getMediaBox()
- end
- -- normalization and further checks, including for non-nil,
- -- by Bb:from_rect, which errors out on failures
- pgbox = HRBb:from_rect(pgbox)
- end
- if options.bbox then
- bb, hrbb = self:bb_from_gs(pg)
+ local bb, hrbb, pgbox
+ pgbox = self:getpgbox()
+ if options.bbox and pgbox:nonnegative() then
+ bb, hrbb = self:bb_from_gs()
+ options.bbox = false
else
hrbb = pgbox
end
- -- luatex on searchpath
+ -- luatex is on searchpath
local luatex_prog = 'luatex'
-- write TeX file which includes cropped pdf page
@@ -1552,12 +1519,10 @@ function PsPdf:pdf_crop()
pieces[6] = [[}%
\pdfrefximage\pdflastximage
}%
- \pdfhorigin=-#2bp\relax
+ \pdfhorigin=#2bp\relax
\pdfvorigin=#3bp\relax
\pagewidth=#4bp\relax
- \advance\pagewidth by -#2bp\relax
\pageheight=#5bp\relax
- \advance\pageheight by -#3bp\relax
\ht0=\pageheight
\shipout\box0\relax
}
@@ -1567,7 +1532,7 @@ function PsPdf:pdf_crop()
\csname @@end\endcsname
\end
]],
- options.page or 1, hrbb.x1, hrbb.y1, hrbb.x2, hrbb.y2)
+ options.page or 1, -hrbb.x1, hrbb.y1, hrbb.x2-hrbb.x1, hrbb.y2-hrbb.y1)
local textemp = mktemp('tex') -- this also took care of pdf:
local pdftemp = string.gsub(textemp, 'tex$', 'pdf')
@@ -1577,18 +1542,20 @@ function PsPdf:pdf_crop()
f:write(table.concat(pieces, ''))
f:close()
local cmd, res, psp
- if os.type=='unix' then
+ -- if os.type=='unix' then
cmd = {luatex_prog, '--safer', '--no-shell-escape', textemp}
log_cmd(cmd)
res = os.spawn(cmd)
- else
- cmd = luatex_prog..' --safer --no-shell-escape '..textemp
- log_cmd({cmd})
- -- os.execute('timeout /t 1 /nobreak >nul')
- res = os.execute(cmd)
- end
- if res and res==0 and lfs.attributes(pdftemp, 'size')>0 then
+ -- else
+ -- cmd = luatex_prog..' --safer --no-shell-escape '..textemp
+ -- log_cmd({cmd})
+ -- -- os.execute('timeout /t 1 /nobreak >nul')
+ -- res = os.execute(cmd)
+ -- end
+ if res and res==0 and lfs.attributes(pdftemp, 'size')>0 then
psp = PsPdf:from_path(pdftemp)
+ options.bbox = false
+ options.page = false
return psp
else
errror('pdf_crop failed on '..self.path)
@@ -1598,33 +1565,26 @@ end
function PsPdf:eps_to_pdf()
-- option to be fulfilled: gray
- -- set target and pdf version if applicable
- -- dbg('PsPdf:eps_to_pdf '..self.path)
+ -- set target and maybe pdf version if applicable
if self.type~='eps' then
errror('PsPdf:eps_to_pdf called for non-eps file '.. self.path)
end
local cmd
- if options.bbox and self.bb:nonnegative() then
- self = self:eps_crop() -- this sets options.bbox to false
- end
+ maybe_add_version_parameter()
cmd = tab_combine({{gs_prog}, gs_options, pdf_options})
- -- dbg(table.concat(cmd,' '))
if options.gray then
cmd = tab_combine({cmd, gray_options})
- -- dbg(table.concat(cmd,' '))
- options.gray = false
end
- table.insert(cmd, '-dEPSCrop') -- always hires bb
- -- dbg(table.concat(cmd,' '))
+ table.insert(cmd, '-dEPSCrop') -- uses existing hires bb
local psp = PsPdf:new('pdf')
table.insert(cmd, '-sOutputFile#'..psp.path)
- -- dbg(table.concat(cmd,' '))
- cmd = tab_combine({cmd, pdf_tail_options, {self.path}})
- -- dbg(table.concat(cmd,' '))
+ cmd = tab_combine({cmd, pdf_tail_options})
+ table.insert(cmd, self.path)
log_cmd(cmd)
local res = os.spawn(cmd)
if res and res==0 and lfs.attributes(psp.path, 'size')>0 then
psp.pages, psp.miver, psp.maver = pdf_props(psp.path)
+ options.gray = false
return psp
else
errror('eps_to_pdf failed on '..self.path)
@@ -1632,13 +1592,14 @@ function PsPdf:eps_to_pdf()
end -- eps_to_pdf
-- Converting from pdf to pdf with grayscaling and/or page selection
+-- or just eliminating negative boundingbox
function PsPdf:pdf_to_pdf()
- -- option to be fulfilled: gray and optionally page.
+ -- option to be fulfilled: gray, optionally page
+ -- side effect: makes bbox non-negative.
-- do not call this just for page selection because
-- pdf_crop can do this in a less invasive manner
- -- dbg('PsPdf:pdf_to_pdf '..self.path)
if self.type~='pdf' then
errror('PsPdf:pdf_to_pdf called for non-pdf file '.. self.path)
end
@@ -1647,28 +1608,24 @@ function PsPdf:pdf_to_pdf()
errror('PsPdf:pdf_to_pdf called with non-existent page '.. options.page)
end
cmd = tab_combine({{gs_prog}, gs_options, pdf_options})
- -- dbg(table.concat(cmd,' '))
if options.gray then
cmd = tab_combine({cmd, gray_options})
- -- dbg(table.concat(cmd,' '))
- options.gray = false
end
if options.page then
table.insert(cmd, '-dFirstPage#'..tostring(options.page))
table.insert(cmd, '-dLastPage#'..tostring(options.page))
- -- dbg(table.concat(cmd,' '))
- options.page = false
end
+ maybe_add_version_parameter()
local psp = PsPdf:new('pdf')
table.insert(cmd, '-sOutputFile#'..psp.path)
cmd = tab_combine({cmd, pdf_tail_options})
- -- dbg(table.concat(cmd,' '))
table.insert(cmd, self.path)
- -- dbg(table.concat(cmd,' '))
log_cmd(cmd)
local res = os.spawn(cmd)
if res and res==0 and lfs.attributes(psp.path, 'size')>0 then
psp.pages, psp.miver, psp.maver = pdf_props(psp.path)
+ options.gray = false
+ options.page = false
return psp
else
errror('pdf_to_pdf failed on '..self.path)
@@ -1677,34 +1634,19 @@ end -- pdf_to_pdf
function PsPdf:pdf_to_eps()
- -- options to be fulfilled: bbox and page
- -- dbg(tostring(settings.pdftops_prog))
- -- dbg('pdf_to_eps '..self.path)
+ -- options to be fulfilled: page
local psp = PsPdf:new('eps')
local cmd, res
- local page = false
- if self.pages > 1 then
- if options.page then
- page = options.page
- if page > self.pages then
- errror('PsPdf:pdf_to_eps called with non-existant page '..
- tostring(page))
- end
- else
- page = 1
- end
- page = tostring(page)
- end -- self.pages > 1
+ -- any_to_any already checked the validity and relevance of options.page
if pdftops then
- if page then
+ if options.page then
cmd = tab_combine({{pdftops}, ps_options,
- {'-f', page, '-l', page,
+ {'-f', options.page, '-l', options.page,
'-eps', self.path, psp.path}})
else
cmd = tab_combine({{pdftops}, ps_options,
{'-eps', self.path, psp.path}})
end
- options.page = false
log_cmd(cmd)
if os.type=='windows' then
-- suppress console output of 'No display font for...' messages,
@@ -1718,6 +1660,7 @@ function PsPdf:pdf_to_eps()
end
if res and res==0 and lfs.attributes(psp.path, 'size')>0 then
psp.pages = 1
+ options.page = false
else
errror('pdf_to_eps failed on '..self.path)
end
@@ -1766,37 +1709,37 @@ function PsPdf:pdf_to_eps()
if not fout then errror('Cannot write new file '.. newfile) end
fout:write(table.concat(pre_lines))
fout:close()
- -- dbg('fixing '..psp.path..' to '..newfile)
slice_file(psp.path, newfile,
lfs.attributes(psp.path,'size') - offset, offset, 'ab')
psp.path = newfile
end -- needs_fixing
else -- use ghostscript
- local epsdev = epsdevice()
+ local epsdev = gs_epsdevice()
if not epsdev then
errror('Conversion to eps not supported by this ghostscript')
end
cmd = tab_combine({{gs_prog}, gs_options,
{'-sDEVICE#'..epsdev, '-dHaveTrueTypes=true', '-dLanguageLevel#3'}})
- -- the restrictions on eps files are apparently
- -- incompatible with grayscaling
+ if options.gray then
+ cmd = tab_combine({cmd, gray_options})
+ end
if options.page then
table.insert(cmd, '-dFirstPage='..options.page)
table.insert(cmd, '-dLastPage='..options.page)
end
table.insert(cmd, '-sOutputFile='..psp.path)
table.insert(cmd, self.path)
- options.page = false
log_cmd(cmd)
res = os.spawn(cmd)
if res and res==0 and lfs.attributes(psp.path, 'size')>0 then
psp.pages = 1
+ options.page = false
+ options.gray = false
else
errror('pdf_to_eps failed on '..self.path)
end
end -- use ghostscript
- psp:find_bb_simple()
- if options.bbox then psp = psp:eps_crop() end
+ psp:get_bb_simple()
return psp
end -- pdf_to_eps
@@ -1804,7 +1747,6 @@ end -- pdf_to_eps
function PsPdf:ps_to_pdf()
-- options to be fulfilled: gray
- -- dbg('PsPdf:ps_to_pdf '..self.path)
if self.type~='ps' then
errror('PsPdf:ps_to_pdf called for non-ps file '.. self.path)
end
@@ -1812,7 +1754,6 @@ function PsPdf:ps_to_pdf()
cmd = tab_combine({{gs_prog}, gs_options, pdf_options})
if options.gray then
cmd = tab_combine({cmd, gray_options})
- options.gray = false
end
local psp = PsPdf:new('pdf')
table.insert(cmd, '-sOutputFile#'..psp.path)
@@ -1822,6 +1763,7 @@ function PsPdf:ps_to_pdf()
local res = os.spawn(cmd)
if res and res==0 and lfs.attributes(psp.path, 'size')>0 then
psp.pages, psp.miver, psp.maver = pdf_props(psp.path)
+ options.gray = false
return psp
else
errror('ps_to_pdf failed on '..self.path)
@@ -1832,24 +1774,14 @@ end -- PsPdf:ps_to_pdf
function PsPdf:pdf_to_ps()
-- options to be fulfilled: page and, if not using pdftops, also gray
- -- dbg('PsPdf:pdf_to_ps '..self.path)
local psp = PsPdf:new('ps')
- local page = false
- if self.pages>1 then
- if options.page and options.page > self.pages then
- errror('PsPdf:pdf_to_ps called with non-existant page '.. options.page)
- elseif options.page then
- page = tostring(options.page)
- psp.pages = 1
- end
- else
- psp.pages = self.pages
- end
+ -- options.page checked by any_to_any
+ psp.pages = self.pages
local cmd, res
if pdftops then
cmd = tab_combine({{pdftops}, ps_options})
- if page then
- cmd = tab_combine({cmd, {'-f', page, '-l', page}})
+ if options.page then
+ cmd = tab_combine({cmd, {'-f', options.page, '-l', options.page}})
end
cmd = tab_combine({cmd, {'-paper', 'match', self.path, psp.path}})
else -- use ghostscript
@@ -1857,17 +1789,14 @@ function PsPdf:pdf_to_ps()
{'-sDEVICE#ps2write', '-dHaveTrueTypes=true', '-dLanguageLevel#3'}})
if options.gray then
cmd = tab_combine({cmd, gray_options})
- -- dbg(table.concat(cmd,' '))
- options.gray = false
end
- if page then
- cmd = tab_combine({cmd, {'-dFirstPage#'..page, '-dLastPage#'..page}})
+ if options.page then
+ cmd = tab_combine({cmd,
+ {'-dFirstPage#'..options.page, '-dLastPage#'..options.page}})
end
table.insert(cmd, '-sOutputFile#'..psp.path)
- -- table.insert(cmd, '-f')
table.insert(cmd, self.path)
end
- options.page = false
log_cmd(cmd)
if os.type=='windows' and pdftops then
-- suppress console output of 'No display font for...' messages,
@@ -1880,79 +1809,123 @@ function PsPdf:pdf_to_ps()
res = os.spawn(cmd)
end
if res and res==0 and lfs.attributes(psp.path, 'size')>0 then
+ options.page = false
+ -- gs will have grayscaled if requested but pdftops will not
+ if not pdftops then options.gray = false end
return psp
else
errror('pdf_to_ps failed on '..self.path)
end
end -- PsPdf:pdf_to_ps
-function PsPdf:any_to_any()
+function PsPdf:ps_to_ps()
+ -- we do not accept a page option since we do not know
+ -- which pages are available.
+ -- if no gray option then there is no point in invoking this function.
+ local psp = PsPdf:new('ps')
+ local cmd = tab_combine({{gs_prog}, gs_options,
+ {'-sDEVICE#ps2write', '-dHaveTrueTypes=true', '-dLanguageLevel#3'}})
+ if options.gray then
+ cmd = tab_combine({cmd, gray_options})
+ end
+ table.insert(cmd, '-sOutputFile#'..psp.path)
+ table.insert(cmd, self.path)
+ log_cmd(cmd)
+ local res = os.spawn(cmd)
+ if res and res==0 and lfs.attributes(psp.path, 'size')>0 then
+ options.gray = false
+ return psp
+ else
+ errror('ps_to_ps failed on '..self.path)
+ end
+end -- PsPdf:ps_to_ps
+
+function any_to_any()
+
+ local psp = PsPdf:from_path(infile)
- -- weed out nonsense options
+ -- sanitize some options
- -- dbg('PsPdf:any_to_any '..self.path)
if options.type=='ps' then
options.bbox = false
- -- dbg('Ignoring bbox option for ps output')
end
- if options.bbox and not options.page then
+
+ if (options.bbox or options.type=='eps') and not options.page then
options.page = 1
- -- dbg('Selecting page 1 for bbox')
end
- if self.pages==1 then
+
+ if psp.type=='pdf' then
+ if options.page then
+ local pgs = pdf_props(infile)
+ if pgs<options.page then
+ errror(infile..' has only '..pgs..' page(s).')
+ elseif pgs==1 then
+ options.page = false
+ end
+ end
+ elseif psp.type=='eps' then
options.page = false
- -- dbg('dropping page selection; source is already a 1-page document')
end
- -- for _,o in ipairs({'page', 'gray', 'bbox'}) do
- -- -- if options[o] then dbg('Do option '..o) end
- -- end
+ -- psp.type=='ps': pages not yet known but maybe not needed
- -- `distiller' settings depend on whether final output is pdf
+ local do_something = false
+ if psp.type=='eps' then
+ do_something = true -- do at least cleanup
+ elseif psp.type~=options.type then
+ do_something = true
+ elseif options.page then
+ do_something = true
+ elseif options.bbox then
+ do_something = true
+ elseif options.gray then
+ do_something = true
+ elseif options.type=='pdf' then
+ if psp:to_downgrade() then
+ do_something = true
+ elseif settings.pdf_target and settings.pdf_target~=default then
+ do_something = true
+ end
+ end
+ if not do_something then
+ if infile~=outfile then
+ slice_file(infile, outfile)
+ end
+ return true
+ end
+
+ -- `distiller' settings
+ -- these depend on whether final output is pdf
-- '.setpdfwrite' is just some optimization option for ghostscript
if options.type=='pdf' then
- if settings.pdf_version~='default' then
- table.insert(pdf_options, '-dCompatibilityLevel#'..settings.pdf_version)
+ if settings.pdf_target and (settings.pdf_target~='default') then
+ table.insert(pdf_options, '-dPDFSETTINGS#/'..settings.pdf_target)
end
- -- below, consider adding <</NeverEmbed [/Times-Roman /TimesBold ...]>>
if settings.pdf_target=='screen' or settings.pdf_target=='ebook' then
- pdf_tailoptions = {'-c', '.setpdfwrite', '-f'}
+ pdf_tail_options = {'-c', '.setpdfwrite', '-f'}
-- -f ensures that the input filename is not added to the -c string
else
- pdf_tailoptions = {
- '-c', '.setpdfwrite <</NeverEmbed [ ] >> setdistillerparams', '-f'}
- end
- else
- pdf_tailoptions = {
+ pdf_tail_options = {
'-c', '.setpdfwrite <</NeverEmbed [ ] >> setdistillerparams', '-f'}
- end
-
- if options.type=='pdf' then
- table.insert(pdf_options, '-dPDFSETTINGS#/'..settings.pdf_target)
- if settings.pdf_version~='default' then
- table.insert(pdf_options, '-dCompatibilityLevel#'..settings.pdf_version)
end
- else
- table.insert(pdf_options, '-dPDFSETTINGS#/default')
end
- --[[
+ --[[ Actual conversions
each single-step conversion takes care of options it can handle
and sets those options to false.
- for boundingboxes, eps_crop is either called explicitly
- or called implicitly by another converter.
- pdf_crop is always called explicitly and always as the last step
+ Cropping a pdf is best be done before converting to postscript or
+ a low (<1.4) pdf version.
- all calls to external programs work on temporary files
+ all invocations of external programs work on temporary files
in the then-current temporary directory, with a simple generated
- filename. So no need to quote names of input- and output filenames.
+ filename. So no need to quote names of input- or output filenames.
--]]
- local psp = self
local newfile
if psp.type=='eps' or psp.type=='epsPreview' then
- -- As a side effect of eps_clean, the modified source file is copied
- -- to the temp subdirectory.
+ -- As a side effect of eps_clean, the modified or unmodified source file
+ -- is copied to our temp subdirectory.
+ -- We always create a new file.
psp = psp:eps_clean()
if options.bbox and psp.bb:nonnegative() then
psp = psp:eps_crop()
@@ -1960,99 +1933,153 @@ function PsPdf:any_to_any()
if options.type=='eps' then
if options.gray or options.bbox then
-- bbox: eps_crop was apparently not applicable: pdf roundtrip
- psp = psp:eps_to_pdf():pdf_to_eps()
+ psp = psp:eps_to_pdf()
+ if options.bbox then
+ psp = psp:pdf_crop()
+ end
+ psp = psp:pdf_to_eps()
end
elseif options.type=='pdf' then
psp = psp:eps_to_pdf()
if options.bbox then
psp = psp:pdf_crop()
end
+ if psp:to_downgrade() then
+ maybe_add_version_parameter()
+ psp = psp:pdf_to_pdf()
+ end
elseif options.type=='ps' then
+ -- often, the eps file is fine as a postscript file.
+ -- however, it may lack a showpage operator, or have a weird
+ -- boundingbox. converting back-and-forth to pdf solves both problems.
+ -- eps_to_pdf will take care of grayscaling
+ -- a tight boundingbox option is not supported for ps output.
psp = psp:eps_to_pdf():pdf_to_ps()
end
- return psp
+ slice_file(psp.path, outfile)
+ return true
elseif psp.type=='ps' then
+
-- preliminary:
-- copy infile to a file in the temp directory, needed for gs -dSAFER
newfile = mktemp(psp.type)
slice_file(psp.path, newfile)
- -- dbg(psp.path..' copied to '..newfile..' in '..lfs.currentdir())
psp.path = newfile
+ if options.type=='ps' and not options.page then
+ psp = psp:ps_to_ps()
+ slice_file(psp.path, outfile)
+ return true
+ end
+
+ -- remaining options require initial conversion to pdf
+ psp = psp:ps_to_pdf()
+ -- AFAIK, all high-level ps constructs are covered by any pdf version
+ -- so this option should not cause unnecessary loss of structure
+ maybe_add_version_parameter()
+
+ if options.page and options.page>1 then
+ -- could not check page option before
+ local pgs = pdf_props(psp.path)
+ if pgs<options.page then
+ errror(infile..' has only '..pgs..' page(s).')
+ elseif pgs==1 then
+ options.page = false
+ end
+ end
+
-- actual conversion
if options.type=='eps' then
- return psp:ps_to_pdf():pdf_to_eps()
+ if options.bbox then
+ psp = psp:pdf_crop()
+ end
+ psp = psp:pdf_to_eps()
elseif options.type=='pdf' then
if options.bbox or options.page then
- return psp:ps_to_pdf():pdf_crop()
- else
- return psp:ps_to_pdf()
+ psp = psp:pdf_crop()
+ -- else no action needed
end
elseif options.type=='ps' then
- if options.gray or options.page then
- return psp:ps_to_pdf():pdf_to_ps()
- else
- return psp -- no conversion necessary
- end
+ psp = psp:pdf_to_ps()
end -- pdf => ps
+ slice_file(psp.path, outfile)
+ return true
elseif psp.type=='pdf' then
+
+ if options.type=='pdf' and settings.pdf_target=='default' and
+ not options.gray and not options.bbox and not options.page and
+ not psp:to_downgrade() then
+ if infile~=outfile then
+ slice_file(infile, outfile)
+ end
+ return true
+ end
+
-- preliminary:
- -- copy infile to a file in the temp directory, for gs -dSAFER
+ -- copy infile to a file in the temp directory, needed for gs -dSAFER
newfile = mktemp(psp.type)
slice_file(psp.path, newfile)
- -- dbg(psp.path..' copied to '..newfile..' in '..lfs.currentdir())
- psp.path = newfile
+ psp = PsPdf:from_path(newfile)
+ local pgbox
-- actual conversion
if options.type=='eps' then
- if options.gray then
- -- one-step grayscaling available for gs/ps but not for gs/eps
- return psp:pdf_to_pdf():pdf_to_eps()
- else
- return psp:pdf_to_eps()
+ if options.bbox then
+ pgbox = psp:getpgbox() -- page n. available from options table
+ if not pgbox:nonnegative() or options.gray then
+ -- fix in extra pdf-to-pdf step
+ psp = psp:pdf_to_pdf()
+ end
+ -- we want to calculate a tight boundingbox before conversion to eps,
+ -- because this conversion may cause rasterization and baffle
+ -- gs' boundingbox calculations
+ psp = psp:pdf_crop()
+ elseif options.gray and pdftops then
+ psp = psp:pdf_to_pdf()
end
+ psp = psp:pdf_to_eps()
elseif options.type=='pdf' then
-- pdf_crop can take care of bbox and page,
- -- but not of gray and not of target use or pdf version
- do
- local need_gs = false
- -- compare actual and required versions,
- -- allowing for rounding differences
- if settings.pdf_version~='default' and
- (psp.maver+0.1*psp.miver)>tonumber(settings.pdf_version)-0.01 then
- need_gs = true
- end
- if settings.pdf_target~='default' then
- need_gs = true
- end
- if options.gray then
- need_gs = true
- end
- local need_crop = false
- if options.bbox then
- need_crop = true
- end
- if (not need_gs) and options.page then
- need_crop = true
- end
- if need_gs then
+ -- but not of gray and not of some pdf options
+ if options.bbox then
+ pgbox = psp:getpgbox()
+ if not pgbox:nonnegative() then
+ -- pdf_to_pdf fixes negative bbox parameters
+ -- and also takes care of page and gray
psp = psp:pdf_to_pdf()
end
- if need_crop or (psp.pages>1 and options.page) then
- psp = psp:pdf_crop()
- end
- return psp
+ psp = psp:pdf_crop()
+ end
+ need_gs = false
+ if psp:to_downgrade() then
+ need_gs = true
+ maybe_add_version_parameter()
+ end
+ if settings.pdf_target~='default' then
+ need_gs = true
+ end
+ if options.gray then
+ need_gs = true
+ end
+ if (not need_gs) and options.page then
+ psp = psp:pdf_crop()
+ -- less invasive that page selection by gs
+ end
+ if need_gs then
+ psp = psp:pdf_to_pdf() -- will handle page selection too
end
elseif options.type=='ps' then
if options.gray and pdftops then
- return psp:pdf_to_pdf():pdf_to_ps()
+ psp = psp:pdf_to_pdf():pdf_to_ps()
else
- return psp:pdf_to_ps()
+ psp = psp:pdf_to_ps()
end
end -- pdf => ps
- end -- psp.type=='ps'|'pdf'
+ slice_file(psp.path, outfile)
+ return true
+ end
end -- any_to_any
-- system-dependent initialization -----------------------------------
@@ -2067,11 +2094,10 @@ if os.type == 'windows' then cwd = string.gsub(cwd, '\\', '/') end
-- prepend (lua)tex directory to searchpath, if not already there
maybe_add_path(os.selfdir, false)
--- Windows: miktex, TL or neither. is_miktex, is_tl_w32, auto_psp
+-- Windows: miktex, TL or neither. is_miktex, is_tl_w32
-- no support yet for separate ghostscript
is_miktex = false
is_tl_w32 = false
-auto_psp = true -- only false for old miktex without included pdftops
if os.type == 'windows' then
if string.find (string.lower(kpse.version()), 'miktex') then
@@ -2096,14 +2122,9 @@ if os.type == 'windows' then
'/release-texlive.txt; TeX installation not supported.', 0)
end -- if isfile
end -- if not miktex
- -- recent miktex and any windows tl include pdftops
- if not lfs.isfile(string.gsub(os.selfdir, '\\', '/') .. '/pdftops.exe') then
- auto_psp = false
- end
end -- if windows
-- without Ghostscript we are dead in the water.
--- TL/w32: add to searchpath
gs_prog = false
do
local rt=''
@@ -2119,11 +2140,11 @@ do
elseif is_tl_w32 then
-- windows/TeX Live
-- grandparent of texlua.exe directory .. ...
- rt = string.gsub(os.selfdir,'[\\/][^\\/]+[\\/][^\\/]+$', '')
- ..'/tlpkg/tlgs'
- maybe_add_path(rt..'/bin', false)
- gs_prog = 'gswin32c.exe'
- --[[ problems with (at least) grayscaling
+ -- rt = string.gsub(os.selfdir,'[\\/][^\\/]+[\\/][^\\/]+$', '')
+ -- ..'/tlpkg/tlgs'
+ -- maybe_add_path(rt..'/bin', false)
+ -- -- gs_prog = 'gswin32c.exe'
+ -- ---[[ problems with (at least) grayscaling
gs_prog = 'rungs.exe'
--]]
else
@@ -2134,11 +2155,10 @@ end -- do
-- directory for configuration and log
epsdir = ''
if os.type == 'windows' then
- epsdir = fw(ep_shortname(os.getenv('APPDATA'))) .. '/epspdf'
+ epsdir = fw(os.getenv('APPDATA')) .. '/epspdf'
else
epsdir = os.getenv('HOME')..'/.epspdf'
end
--- dbg('epsdir: '..epsdir)
rcfile = epsdir .. '/config'
logfile = epsdir .. '/epspdf.log'
@@ -2166,26 +2186,29 @@ end -- if lfs...logfile
write_log('epspdf '..table.concat(arg, ' '))
---[[ some debug output
-
-dbg ('os is ' .. os.type .. ' and ' .. os.name)
-dbg ('texlua in ' .. os.selfdir)
-dbg('Ghostscript: ' .. gs_prog)
---]]
-
--[[
settings, initial values
-The values in the settings array have lowest priority - lower than
-autodetect and command-line options. We go for false rather than
-undefined, because this results in an actual settings entry.
+priority, from low to high:
+
+- built-in defaults
+- settings read from and written to the configuration file
+- command-line options, defined in the opts table
+
+The options- and settings tables are initialized from built-in
+defaults. We go for false rather than undefined, because this
+results in an actual settings- or options entry.
+
+Command-line options are copied to either options or settings.
We ignore illegal settings in the config file.
--]]
pdf_targets = {'screen', 'ebook', 'printer', 'prepress', 'default'}
-pdf_versions = {'1.2', '1.3', '1.4', 'default'}
+pdf_versions = {'1.2', '1.3', '1.4', '1.5', '1.6', '1.7', 'default'}
+-- ghostscript will substitute higher versions with
+-- the highest-supported version
settings = {}
descriptions = {}
@@ -2196,30 +2219,12 @@ descriptions.pdf_target = 'One of ' .. join(pdf_targets, ', ', ' or ')
settings.pdf_version = 'default'
descriptions.pdf_version = 'One of ' .. join(pdf_versions, ', ', ' or ')
---[[
--- is bb_spread still a useful setting?
--- look at gs options wrt boundingbox
--- settings.bb_spread = 1
--- descriptions.bb_spread = 'Safety margin in points for (low-res) boundingbox'
-
-settings.use_hires_bb = false
--- descriptions.use_hires_bb = 'Use high-resolution boundingbox if available'
--- Ignored; hires bb always used
---]]
-
--- because pdftops_prog is sometimes configurable, it is stored in settings.
--- it will not be used for TeX Live and only be read and written on Windows.
-
-settings.pdftops_prog = false
-descriptions.pdftops_prog =
- 'Full path to pdftops.exe (not used if TeX bin dir contains pdftops)'
-
settings.use_pdftops = true
descriptions.use_pdftops = 'Use pdftops if available'
-- epspdf stores ps- and pdf viewer settings on behalf of the gui interface
-- but does not use them itself.
--- They won't be used at all under osx or windows.
+-- They will not be used at all under macos or windows.
settings.ps_viewer = false
descriptions.ps_viewer =
@@ -2227,13 +2232,13 @@ descriptions.ps_viewer =
settings.pdf_viewer = false
descriptions.pdf_viewer =
- 'Epspdftk: viewer for pdf files; not used on Windows or OS X'
+ 'Epspdftk: viewer for pdf files; not used on Windows or Mac OS'
-- default_dir, which is used on all platforms, is only for the gui.
if os.type == 'windows' then
settings.default_dir =
- string.gsub(ep_shortname(os.getenv('USERPROFILE')), '\\', '/')
+ string.gsub(os.getenv('USERPROFILE'), '\\', '/')
else
settings.default_dir = os.getenv('HOME')
end
@@ -2257,6 +2262,7 @@ options.type = false -- implied via output filename on command line
-- We could make these `class attributes' for PsPdf but to what purpose?
-- For Windows shell commands, we need to substitute `#' for `='
-- when invoking Ghostscript. For simplicity, we do this across the board.
+-- -P- : do not look first in current directory
gs_options = {'-q', '-dNOPAUSE', '-dBATCH', '-P-', '-dSAFER'}
@@ -2273,25 +2279,18 @@ pdftops = false
-- gets a value only if we are going to use pdftops
ps_options = {'-level3'}
--- may add custom options later
-- `main program' inside scope-creating block ----------------------
do -- main program
- local infile = false
local in_dir = false -- directory of infile
- local outfile = false
local out_dir = false -- directory of outfile
- -- dbg('\nSettings are:\n')
- for k,v in pairs(settings) do dbg(k .. ' = ' .. tostring(v)) end
-
do -- Handle settings and command-line inside nested scope --------------
read_settings(rcfile)
- -- dbg('Defining cmdline options')
local opts = {}
opts.page = {
@@ -2345,16 +2344,6 @@ do -- main program
help = descriptions.pdf_version
}
- if os.type=='windows' and not auto_psp then
- opts.pdftops_prog = {
- type = 'string', val = nil,
- forms = {'--pdftops'},
- placeholder = 'PATH',
- negforms = nil,
- help = descriptions.pdftops_prog
- }
- end
-
opts.use_pdftops = {
type = 'boolean', val = nil,
forms = {'-U'},
@@ -2407,22 +2396,24 @@ do -- main program
-- a couple of functions only available during command-line parsing
local function show_version ()
- print('Epspdf version '..ep_version..'\nCopyright (c) '
- ..ep_copyright..' Siep Kroonenberg')
+ print('Epspdf version 0.6.4\nCopyright (c) 2006-2019 Siep Kroonenberg')
end
local function help (mess) -- requires opts array
- if mess then print(mess..eol) end
+ if mess then
+ print(mess..eol)
+ end
show_version()
-- below, string.gsub unindents its long-string parameter.
-- string.format removes the second return value of string.gsub.
- print(string.format('%s', string.gsub([[
+ print( -- string.format('%s',
+ string.gsub([[
Convert between [e]ps and pdf formats
Usage: epspdf[.tlu] [options] infile [outfile]
Default for outfile is file.pdf if infile is file.eps or file.ps
Default for outfile is file.eps if infile is file.pdf
- ]], '([\r\n]+) ', '%1')))
+ ]], '([\r\n]+) ', '%1')) --)
-- need to enforce an ordering, otherwise we could have used pairs(opts)
-- omitted below: no-op options
@@ -2431,31 +2422,28 @@ do -- main program
local intent_sp = string.rep(' ', indent_n)
local indent_fmt = '%-' .. tostring(indent_n) .. 's'
for _, o in ipairs({'page', 'gray', 'bbox', 'pdf_target', 'pdf_version',
- 'pdftops_prog', 'use_pdftops', 'save', 'info', 'debug',
- 'version', 'help'}) do
+ 'use_pdftops', 'save', 'info', 'debug', 'version', 'help'}) do
local v = opts[o]
- if v~='pdftops_prog' or not auto_psp then
- if v and v.help then
- local synt = join(v.forms, ', ')
- if v.type ~= 'boolean' then synt = synt .. ' ' .. v.placeholder end
+ if v and v.help then
+ local synt = join(v.forms, ', ')
+ if v.type ~= 'boolean' then synt = synt .. ' ' .. v.placeholder end
+ if string.len(synt)<indent_n then
+ print(string.format(indent_fmt, synt) .. v.help)
+ else
+ print(synt)
+ print(intent_sp .. v.help)
+ end
+ if v.negforms then
+ local neghelp = 'Reverses the above'
+ synt = join(v.negforms, ', ')
if string.len(synt)<indent_n then
- print(string.format(indent_fmt, synt) .. v.help)
+ print(string.format(indent_fmt, synt) .. neghelp)
else
print(synt)
- print(intent_sp .. v.help)
- end
- if v.negforms then
- local neghelp = 'Reverses the above'
- synt = join(v.negforms, ', ')
- if string.len(synt)<indent_n then
- print(string.format(indent_fmt, synt) .. neghelp)
- else
- print(synt)
- print(intent_sp .. neghelp)
- end
+ print(intent_sp .. neghelp)
end
end
- end -- ~=pdftops_prog or not auto_psp
+ end
end -- for
if mess then os.exit(1) else os.exit() end
end -- help
@@ -2471,7 +2459,6 @@ do -- main program
local i=1
while i<=#arg and string.sub(arg[i],1,1)=='-' do
- -- dbg('parse argument '..tostring(i)..': '..arg[i])
local parsed = false
local kk, vv = string.match(arg[i],'([^=]+)=(.*)$')
if kk==nil then
@@ -2480,7 +2467,6 @@ do -- main program
vv = strip_outer_spaces(vv)
end
for p, o in pairs(opts) do
- -- dbg(' try '..p)
if in_list(kk, o.forms) or in_list(kk, o.negforms) then
parsed = true
if o.type == 'boolean' then
@@ -2506,29 +2492,6 @@ do -- main program
i = i + 1
end -- while
- -- some debug output
-
- --[[
- if i<=#arg then
- dbg('non-option arguments:')
- for j=i,#arg do dbg(arg[j]) end
- dbg(eol)
- else
- dbg('no non-option arguments')
- end
-
- for i=1,#arg do dbg(arg[i]) end
-
- dbg(eol..'Options from command-line:')
- for p, o in pairs(opts) do
- if o.val==nil then
- dbg(p..': undefined')
- else
- dbg(p..': '..tostring(o.val))
- end
- end
- --]]
-
-- check and interpret opts.
-- Copy to either settings or to options table.
-- at syntax error, abort via help function.
@@ -2594,34 +2557,14 @@ do -- main program
end
end
- -- pdftops program; pdftops has already been been initialized to false
-
- -- pdftops_prog as command-line option
- if os.type=='windows' and not auto_psp and
- opts.pdftops_prog.val then
- settings.pdftops_prog = is_prog(opts.pdftops_prog.val)
- end
-
- -- pdftops should be on the path. otherwise, make it so if possible.
- if os.type=='windows' and not auto_psp then
- if settings.use_pdftops then
- pdftops = is_prog(settings.pdftops_prog)
- if pdftops then
- -- strip path and modify searchpath, to avoid paths with spaces
- maybe_add_path(string.gsub(pdftops, '[\\/][^\\/]*$', ''), 'append')
- pdftops = string.gsub(settings.pdftops_prog, '^.*[\\/]', '')
- end
- end
- elseif os.type=='windows' then
- if settings.use_pdftops then
- pdftops = 'pdftops.exe'
- end
- else
- if settings.use_pdftops then
+ -- pdftops should be on the path
+ if settings.use_pdftops then
+ if os.type=='windows' then
+ pdftops = find_on_path('pdftops.exe')
+ else
pdftops = find_on_path('pdftops')
end
end
- -- dbg('Option handling; pdftops is '..tostring(pdftops))
-- other options
@@ -2675,58 +2618,25 @@ do -- main program
-- one final quick option
if opts.info.val then
- info(infile)
- end
-
- -- add pdf_version and pdf_target to the options array,
- -- from where it will be set to false when realized
- if settings.pdf_target == 'default' then
- options.pdf_target = false
- else
- options.pdf_target = settings.pdf_target
+ info()
end
- if settings.pdf_version == 'default' then
- options.pdf_version = false
- else
- options.pdf_version = tonumber(settings.pdf_version)
- end
-
- end -- decoding command-line
- --[[
- dbg('After command-line processing\n Settings')
- -- print settings- and options array with dbg
- for k, v in pairs(settings) do
- dbg(k..': '..tostring(v))
- end
- dbg(' Options')
- for k, v in pairs(options) do
- dbg(k..': '..tostring(v))
- end
- --]]
+ end -- do (decoding command-line)
--[[
Once it becomes clear that real work needs to be done,
we shall create a temp directory.
- because of gs -dSAFER restrictions, infile must be in (a
- subdirectory of) the directory of the output file, e.g. in the
- temp directory.
-
- Also because of -dSAFER, we copy infile to the temp directory of
- it is not in the same directory as outfile.
+ because of gs -dSAFER restrictions, infile must be in (a
+ subdirectory of) the directory of the output file, e.g. in the
+ temp directory. So we copy infile to the temp directory.
--]]
- source = io.open(infile)
- if not source then
- error(infile .. ' not readable')
+ if not lfs.isfile(infile) or lfs.attributes(infile, 'size')==0 then
+ errror(infile..' does not exist or is empty.')
end
- source:close()
- -- if options.debug then
- -- warn('in: '..infile..'\nout: '..outfile..'\n\ncwd: '..cwd)
- -- end
infile, in_dir = absolute_path(infile)
outfile, out_dir = absolute_path(outfile)
if not out_dir then
@@ -2746,58 +2656,36 @@ do -- main program
tempdir = os.tmpdir()
if not tempdir then
errror('Cannot create directory for temporary files')
- else
- -- dbg('temp directory '..tempdir)
end
lfs.chdir(tempdir)
+ -- determine filetype from first few bytes of file
intype = identify(infile)
-- remaining cases: want a real conversion
if not intype then
- error(infile..' has an unsupported filetype')
+ errror(infile..' has an unsupported filetype')
end
- --sanity check on output filetype
+ -- valid output filetype?
options.type = string.match(outfile, '.*%.([^%.]+)$')
- if not options.type or (options.type~='ps'
- and options.type~='eps' and options.type~='pdf') then
+ if not options.type or (options.type~='ps' and
+ options.type~='eps' and options.type~='pdf') then
errror('Output file '..outfile..
' should have extension .eps, .ps or .pdf')
end
if outfile==infile then
- local insave = infile .. '.luasave'
- move_or_copy(infile, insave)
- infile = insave
- end
-
- -- had some trouble under msw when removing outfile later so try it now
- if lfs.isfile(outfile) then
- os.remove(outfile)
- -- if removal fails but outfile is overwritable then no real problem
+ slice_file(infile, infile..'.backup')
+ -- copy, not move, since outfile may be infile if there was nothing to do
end
- local fout = io.open(outfile, 'wb')
- if not fout then
- errror('Output file '..outfile..' not writable; aborting')
- else
- fout:close()
+ result = any_to_any(infile, outfile)
+ if not lfs.isfile(outfile) or lfs.attributes(outfile, 'size')==0 then
+ errror('Failed to generate '..outfile)
end
- source = PsPdf:from_path(infile)
- dest = source:any_to_any()
- if not lfs.isfile(dest.path) then
- errror('Failed to generate '..dest.path)
- end
- write_log('Copying or moving '..dest.path..' to '..outfile)
- move_or_copy(dest.path, outfile)
if not options.debug then
cleantemp()
end
- if lfs.isfile(outfile) and lfs.attributes(outfile, 'size')>0 then
- os.exit()
- else
- errror('Conversion failed')
- end
end
diff --git a/Master/texmf-dist/scripts/epspdf/epspdf4tk.cmd b/Master/texmf-dist/scripts/epspdf/epspdf4tk.cmd
new file mode 100644
index 00000000000..c6d6d09ac5e
--- /dev/null
+++ b/Master/texmf-dist/scripts/epspdf/epspdf4tk.cmd
@@ -0,0 +1 @@
+@texlua "%~dp0epspdf.tlu" %*
diff --git a/Master/texmf-dist/scripts/epspdf/epspdftk.tcl b/Master/texmf-dist/scripts/epspdf/epspdftk.tcl
index 7e4a7bc4c3c..26ed17efcfd 100755
--- a/Master/texmf-dist/scripts/epspdf/epspdftk.tcl
+++ b/Master/texmf-dist/scripts/epspdf/epspdftk.tcl
@@ -3,8 +3,8 @@
# epspdf conversion utility, GUI frontend
#####
-# Copyright (C) 2006, 2008, 2009, 2010, 2011, 2013, 2014, 2015, 2016 Siep Kroonenberg
-# n dot s dot kroonenberg at rug dot nl
+# Copyright (C) 2006-2019 Siep Kroonenberg
+# siepo at bitmuis dot nl
#
# This program is free software, licensed under the GNU GPL, >=2.0.
# This software comes with absolutely NO WARRANTY. Use at your own risk!
@@ -12,13 +12,15 @@
package require Tk
+if {[string index $::tcl_patchLevel 2] <5} {
+ tk_messageBox -message "Tcl/Tk version >= 8.5 required;\naborting..."
+ exit
+}
+
# tired of writing out this combined test again and again
set classic_unix [expr {$::tcl_platform(platform) eq "unix" && \
$::tcl_platform(os) ne "Darwin"}]
-# combo boxes and -ignorestderr introduced in Tk8.5
-set ge_85 [expr {[string index $::tcl_patchLevel 2] > 4}]
-
# normally, epspdf.tlu should be in the same directory
# and texlua should be on the searchpath.
# However, the Windows installer version wraps this script in a starpack.
@@ -35,7 +37,7 @@ proc write_log {s} {
}
}
-### calling epspdf.tlu #########################
+### invoking epspdf.tlu #########################
# Get full path of epspdf.tlu. It should be in the same directory as
# either this script or of the starpack containing this script,
@@ -60,6 +62,13 @@ proc set_progs {} {
set eproot [file dirname [file normalize [info nameofexecutable]]]
set ::epspdf_tlu [file join $eproot "epspdf.tlu"]
}
+ if {$::tcl_platform(platform) eq "windows"} {
+ # For actual conversions, epspdf invokes third-party command-line programs.
+ # To prevent the appearing of transient black console windows for such
+ # invocations, epspdf will be invoked via a batchfile.
+ # This is not necessary for getting info about a file.
+ set ::epspdf_cmd [file join $eproot "epspdf4tk.cmd"]
+ }
if {! [file exists $::epspdf_tlu]} {
tk_messageBox -type ok -icon error -message "Epspdf.tlu not found"
exit 1
@@ -95,9 +104,6 @@ proc is_prog {x} {
# create a global empty settings array
array set ::settings [list]
-# windows: pdftops from tex distro?
-set ::psp_config 0
-
# ask epspdf.tlu for currently configured settings.
# this does not include automatically configured or transient settings.
# the availability of viewers is handled here.
@@ -107,10 +113,6 @@ proc getsettings {} {
}
# write_log "settings from epspdf.tlu:\n$set_str\n"
set l [split $set_str "\r\n"]
- if {$::tcl_platform(platform) eq "windows"} {
- set ::psp_config 0
- set settings(pdftops_prog) ""
- }
foreach e $l {
# puts "settings: $e"
# $e is either a string "var = value"
@@ -122,13 +124,8 @@ proc getsettings {} {
set val [string trim [string range $e [expr $i+1] end]]
if {$val eq "true"} {set val 1}
if {$val eq "false"} {set val 0}
- if {$para eq "psp_config"} {
- set ::psp_config 1
- write_log "TL for Windows not detected by epspdf"
- } else {
- set ::settings($para) $val
- # write_log "setting $para is $val"
- }
+ set ::settings($para) $val
+ # write_log "setting $para is $val"
}
}
@@ -151,7 +148,7 @@ proc getsettings {} {
if {$::settings(pdf_viewer) ne "" && [is_prog $::settings(pdf_viewer)]} {
lappend ::pdf_viewers $::settings(pdf_viewer)
}
- foreach v {evince okular xpdf epdfview qpdfview zathura acroread gv} {
+ foreach v {evince okular mupdf qpdfview texworks xpdf zathura} {
if {$v ne $::settings(pdf_viewer) && [is_prog $v]} {
lappend ::pdf_viewers $v
}
@@ -224,154 +221,25 @@ proc viewable {} {
### groundwork for GUI #################################
# padding for buttons and frames
-proc packb {b args} {
- eval [linsert $args 0 pack $b -padx 3 -pady 3]
- $b configure -border 2
-}
-
-proc packf {f args} {
- eval [linsert $args 0 pack $f -padx 3 -pady 3]
-}
-
-# dummy widget for vertical spacing
-set idummy -1
-proc spacing {w} {
- incr ::idummy
- pack [label $w.$::idummy -text " "]
-}
+proc ppack {b args} {pack $b {*}$args -padx 3 -pady 3}
# bold font
-font create boldfont
-# create dummy label widget to get at the default label font properties
-label .dummy
-font configure boldfont -family [font actual [.dummy cget -font] -family]
-font configure boldfont -size [font actual [.dummy cget -font] -size]
-font configure boldfont -weight bold
-destroy .dummy
-
-# bitmaps for mycombo (no combobox in core tk8.4)
-image create bitmap dwnarrow -data {
-#define dwnarrow_width 15
-#define dwnarrow_height 10
-static unsigned char dwnarrow_bits[] = {
- 0x00, 0x00, 0x00, 0x00, 0xfc, 0x1f, 0xf8, 0x0f, 0xf0, 0x07, 0xe0, 0x03,
- 0xc0, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 };
-}
-
-image create bitmap uparrow -data {
-#define uparrow_width 15
-#define uparrow_height 10
-static unsigned char uparrow_bits[] = {
- 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07,
- 0xf8, 0x0f, 0xfc, 0x1f, 0x00, 0x00, 0x00, 0x00 };
-}
-
-# mycombo
-if {$::ge_85} {
- proc update_combo {w vls} {
- upvar $vls vs
- set new [$w get]
- if {$new ni $vs} {
- if {[is_prog $new]} {
- set vs [linsert $vs 0 $new]
- $w configure -values $vs
- } else {
- tk_messageBox -title Error -icon error -message "$vl Not a program"
- }
- }
- }
-} else {
- proc mycombo {w} {
- # entry widget and dropdown button
- frame $w
- frame $w.ef
- entry $w.ef.e -width 30 -borderwidth 1
- pack $w.ef.e -side left
- button $w.ef.b -image dwnarrow -command "toggle_list $w" -borderwidth 1
- pack $w.ef.b -side right
- pack $w.ef
- # 'drop-down' listbox; width should match entry widget above
- toplevel $w.lf -bd 0
- listbox $w.lf.l -yscrollcommand "$w.lf.s set" -height 4 -width 30 \
- -bd 1 -relief raised
- grid $w.lf.l -column 0 -row 0 -sticky news
- scrollbar $w.lf.s -command "$w.lf.l yview" -bd 1
- grid $w.lf.s -column 1 -row 0 -sticky ns
- grid columnconfigure $w.lf 0 -weight 1
- wm overrideredirect $w.lf 1
- wm transient $w.lf
- wm withdraw $w.lf
- # next two bindings:
- # final parameter: unmap/toggle listbox
- bind $w.lf.l <KeyRelease-space> {update_e %W [%W get active] 1}
- bind $w.lf.l <KeyRelease-Tab> {update_e %W [%W get active] 1}
- bind $w.lf.l <1> {update_e %W [%W index @%x,%y] 0}
- bind $w.lf.l <Double-1> {update_e %W [%W index @%x,%y] 1}
- bind $w.ef.e <Return> {update_l %W}
- bind $w.ef.e <Tab> {update_l %W}
- return $w
- }
-
- # toggle state of listbox.
- # this involves calculating the place where it should appear
- # and toggling the arrow image.
- proc toggle_list {w} {
- # $w.ef is the frame with the entry widget
- # $w.lf is the toplevel with the listbox
- # which needs to turn up right below $w.ef
- if {[wm state $w.lf] eq "withdrawn" || [wm state $w.lf] eq "iconified"} {
- set lfx [winfo rootx $w.ef]
- set lfy [expr [winfo rooty $w.ef] + [winfo height $w.ef]]
- wm geometry $w.lf [format "+%d+%d" $lfx $lfy]
- wm deiconify $w.lf
- $w.ef.b configure -image uparrow
- } else {
- wm withdraw $w.lf
- $w.ef.b configure -image dwnarrow
- }
- }
-
- # note: in tcl/tk 8.5, values of (some) widget variables can be accessed
- # directly and explicit use of upvar is unnecessary.
-
- # list => entry; optionally toggle list display
- proc update_e {v i toggle} {
- set w [winfo parent [winfo parent $v]]
- set lv [$w.lf.l cget -listvariable]
- upvar $lv l
- set tv [$w.ef.e cget -textvariable]
- upvar $tv t
- set t [lindex $l $i]
- if {$toggle} {toggle_list $w}
- }
-
- # entry => list
- proc update_l {v} {
- set w [winfo parent [winfo parent $v]]
- set tv [$w.ef.e cget -textvariable]
- upvar $tv t
- set lv [$w.lf.l cget -listvariable]
- upvar $lv l
- set found [lsearch $l $t]
- if { $found < 0} {
- set ok [$v validate]
- if {$ok} {
- lappend l $t
- set l [lsort $l]
- } else {
- tk_messageBox -message "Not a program"
- }
+font create bfont {*}[font configure TkDefaultFont]
+font configure bfont -weight bold
+
+proc update_combo {w vls} {
+ upvar $vls vs
+ set new [$w get]
+ if {$new ni $vs} {
+ if {[is_prog $new]} {
+ set vs [linsert $vs 0 $new]
+ $w configure -values $vs
+ } else {
+ tk_messageBox -title Error -icon error -message "$vl Not a program"
}
- set the_index [lsearch $l $t]
- $w.lf.l see $the_index
- $w.lf.l activate $the_index
- wm withdraw $w.lf
- $w.ef.b configure -image dwnarrow
}
}
-# end mycombo
-
### and now the actual GUI ###################################
wm title . "PostScript- and pdf conversions"
@@ -398,13 +266,14 @@ proc readhelp {} {
toplevel .help_t
wm title .help_t "EpsPdf help"
-packb [button .help_t.done -text "Close" -command {wm withdraw .help_t}] \
- -side bottom -anchor e -padx 50
+pack [ttk::frame .help_t.bg -padding 3] -fill both -expand 1
+ppack [ttk::button .help_t.done -text "Close" -command {wm withdraw .help_t}] \
+ -in .help_t.bg -side bottom -anchor e -padx 50
text .help_t.text -wrap word -width 60 -height 20 -setgrid 1 \
-yscrollcommand ".help_t.scroll set"
-scrollbar .help_t.scroll -command ".help_t.text yview"
-pack .help_t.scroll -side right -fill y
-pack .help_t.text -expand 1 -fill both
+ttk::scrollbar .help_t.scroll -command ".help_t.text yview"
+pack .help_t.scroll -in .help_t.bg -side right -fill y
+pack .help_t.text -in .help_t.bg -expand 1 -fill both
readhelp
.help_t.text configure -state disabled
wm withdraw .help_t
@@ -413,13 +282,14 @@ wm withdraw .help_t
toplevel .log_t
wm title .log_t "Epspdf log"
-packb [button .log_t.b -text "Close" -command {wm withdraw .log_t}] \
- -side bottom -anchor e -padx 50
+pack [ttk::frame .log_t.bg -padding 3] -fill both -expand 1
+ppack [ttk::button .log_t.b -text "Close" -command {wm withdraw .log_t}] \
+ -in .log_t.bg -side bottom -anchor e -padx 50
text .log_t.text -wrap word -relief flat -height 15 -width 60 \
-setgrid 1 -yscrollcommand ".log_t.scroll set"
-scrollbar .log_t.scroll -command ".log_t.text yview"
-pack .log_t.scroll -side right -fill y
-pack .log_t.text -expand 1 -fill both
+ttk::scrollbar .log_t.scroll -command ".log_t.text yview"
+pack .log_t.scroll -in .log_t.bg -side right -fill y
+pack .log_t.text -in .log_t.bg -expand 1 -fill both
.log_t.text configure -state disabled
wm withdraw .log_t
@@ -427,6 +297,7 @@ wm withdraw .log_t
toplevel .config_t
wm title .config_t "Configure epspdf"
+pack [ttk::frame .config_t.bg -padding 3] -fill both -expand 1
# The settings array is edited directly in the configuration screen:
# for most control widgets, we need a global external variable anyway,
@@ -438,120 +309,66 @@ wm title .config_t "Configure epspdf"
# viewers (not on windows or osx)
if {$::classic_unix} {
- packf [frame .config_t.viewf] -ipadx 4 -fill x
- grid [label .config_t.viewf.title -font boldfont -text "Viewers"] \
+ ppack [ttk::frame .config_t.viewf] -in .config_t.bg -ipadx 4 -fill x
+ grid [ttk::label .config_t.viewf.title -font bfont -text "Viewers"] \
-row 0 -column 0 -sticky w
- grid [label .config_t.viewf.lb_pdf -text "Pdf"] \
+ grid [ttk::label .config_t.viewf.lb_pdf -text "Pdf"] \
-row 1 -column 0 -sticky w
- grid [label .config_t.viewf.lb_ps -text "PostScript"] \
+ grid [ttk::label .config_t.viewf.lb_ps -text "PostScript"] \
-row 2 -column 0 -sticky w
- if {$::ge_85} {
- grid [ttk::combobox .config_t.viewf.pdf] -row 1 -column 1 -sticky e
- .config_t.viewf.pdf configure -values $::pdf_viewers
- .config_t.viewf.pdf configure -textvariable ::settings(pdf_viewer)
- bind .config_t.viewf.pdf <Return> {update_combo %W $::pdf_viewers}
- grid [ttk::combobox .config_t.viewf.ps] -row 2 -column 1 -sticky e
- .config_t.viewf.ps configure -values $::ps_viewers
- .config_t.viewf.ps configure -textvariable ::settings(ps_viewer)
- bind .config_t.viewf.ps <Return> {update_combo %W $::ps_viewers}
- } else {
- grid [mycombo .config_t.viewf.pdf] -row 1 -column 1 -sticky e
- .config_t.viewf.pdf.lf.l configure -listvariable ::pdf_viewers
- .config_t.viewf.pdf.ef.e configure -textvariable ::settings(pdf_viewer)
- .config_t.viewf.pdf.ef.e configure -vcmd {is_prog %P} -validate none
- grid [mycombo .config_t.viewf.ps] -row 2 -column 1 -sticky e -pady 4
- .config_t.viewf.ps.lf.l configure -listvariable ::ps_viewers
- .config_t.viewf.ps.ef.e configure -textvariable ::settings(ps_viewer)
- .config_t.viewf.ps.ef.e configure -vcmd {is_prog %P} -validate none
- }
+ grid [ttk::combobox .config_t.viewf.pdf] -row 1 -column 1 -sticky e
+ .config_t.viewf.pdf configure -values $::pdf_viewers
+ .config_t.viewf.pdf configure -textvariable ::settings(pdf_viewer)
+ bind .config_t.viewf.pdf <Return> {update_combo %W $::pdf_viewers}
+ grid [ttk::combobox .config_t.viewf.ps] -row 2 -column 1 -sticky e
+ .config_t.viewf.ps configure -values $::ps_viewers
+ .config_t.viewf.ps configure -textvariable ::settings(ps_viewer)
+ bind .config_t.viewf.ps <Return> {update_combo %W $::ps_viewers}
grid columnconfigure .config_t.viewf 1 -weight 1 -pad 2
-
- spacing .config_t
}
# settings for conversion to pdf
-packf [frame .config_t.pdff] -ipadx 4 -fill x
-pack [label .config_t.pdff.title -font boldfont -text "Conversion to pdf"] \
+ppack [ttk::frame .config_t.pdff] \
+ -in .config_t.bg -ipadx 4 -fill x -pady [list 10 0]
+pack [ttk::label .config_t.pdff.title -font bfont -text "Conversion to pdf"] \
-anchor w
-pack [label .config_t.pdff.l_target -text "Target use"] -anchor w
-pack [frame .config_t.pdff.f_targets] -fill x
+pack [ttk::label .config_t.pdff.l_target -text "Target use"] -anchor w
+pack [ttk::frame .config_t.pdff.f_targets] -fill x
foreach t {default printer prepress screen ebook} {
- pack [radiobutton .config_t.pdff.f_targets.$t \
+ pack [ttk::radiobutton .config_t.pdff.f_targets.$t \
-variable ::settings(pdf_target) \
-text $t -value $t] -side left -padx 2 -pady 4 -anchor w
}
-pack [label .config_t.pdff.l_version -text "Pdf version"] -anchor w
-pack [frame .config_t.pdff.f_version] -fill x
-foreach t {1.2 1.3 1.4 default} {
+pack [ttk::label .config_t.pdff.l_version -text "Pdf version"] -anchor w
+pack [ttk::frame .config_t.pdff.f_version] -fill x
+foreach t {1.2 1.3 1.4 1.5 1.6 1.7 default} {
regsub {\.} $t _ tp ; # replace dot in name: dots are path separators!
- pack [radiobutton .config_t.pdff.f_version.$tp \
+ pack [ttk::radiobutton .config_t.pdff.f_version.$tp \
-variable ::settings(pdf_version) \
-text $t -value $t] -side left -padx 2 -pady 4 -anchor w
}
-#pack [label .config_t.pdff.l_gs \
-# -text "Custom Ghostscript/ps2pdf parameters"] -anchor w
-#pack [entry .config_t.pdff.e_gs -border 1] -fill x -padx 2 -pady 2
-#.config_t.pdff.e_gs configure -textvariable settings(pdf_custom)
-
-spacing .config_t
-
# settings for conversion to EPS and PostScript
-packf [frame .config_t.psf] -ipadx 4 -fill x
-pack [label .config_t.psf.l_ps -text "Conversion to EPS and PostScript" \
- -font boldfont] -anchor w
-if { $::psp_config} {
- if {[string tolower [string range $::settings(pdftops_prog) end-3 end]] ne \
- ".exe"} {set ::settings(pdftops_prog) ""}
- pack [label .config_t.psf.l_pdftops -text "Find pdftops"] -anchor w
- pack [frame .config_t.psf.findf] -anchor w
- pack [entry .config_t.psf.findf.e -width 40] -side left -padx 4
- .config_t.psf.findf.e configure -textvariable ::settings(pdftops_prog)
- packb [button .config_t.psf.findf.b -text "Browse..." \
- -command find_pdftops] -side left
-}
-
-proc find_pdftops {} {
- set try [tk_getOpenFile -title "Find pdftops.exe" \
- -filetypes {{"Programs" {.exe}}} -initialdir "c:/"]
- if {$try ne ""} {
- .config_t.psf.findf.e delete 0 end
- .config_t.psf.findf.e insert 0 $try
- }
-}
+ppack [ttk::frame .config_t.psf] \
+ -in .config_t.bg -pady [list 10 0] -ipadx 4 -fill x
+pack [ttk::label .config_t.psf.l_ps -text "Conversion to EPS and PostScript" \
+ -font bfont] -anchor w
-pack [checkbutton .config_t.psf.c \
- -text "Use pdftops if available (recommended)"] -anchor w
+pack [ttk::checkbutton .config_t.psf.c \
+ -text "Use pdftops if available"] -anchor w
.config_t.psf.c configure -variable ::settings(use_pdftops) \
-onvalue 1 -offvalue 0
-spacing .config_t
-
-## hires boundingbox setting
-#
-#packf [frame .config_t.hiresf] -ipadx 4 -fill x
-#pack [label .config_t.hiresf.title -font boldfont -text "Hires BoundingBox"] \
-# -anchor w
-#pack [label .config_t.hiresf.l -text "Uncheck to prevent clipping"] \
-# -anchor w
-#
-#pack [checkbutton .config_t.hiresf.c \
-# -text "Use hires boundingbox if possible"] -anchor w
-#.config_t.hiresf.c configure \
-# -variable ::settings(ignore_hires_bb) -onvalue 0 -offvalue 1
-#
-#spacing .config_t
-
# buttons for closing the configuration screen
-pack [frame .config_t.buttonsf] -fill x
-packb [button .config_t.buttonsf.done -text "Done" -command putsettings] \
+pack [ttk::frame .config_t.buttonsf] -in .config_t.bg -pady [list 10 0] -fill x
+ppack [ttk::button .config_t.buttonsf.done -text "Done" -command putsettings] \
-side right
-packb [button .config_t.buttonsf.cancel -text "Cancel" \
+ppack [ttk::button .config_t.buttonsf.cancel -text "Cancel" \
-command cancelsettings] -side right
wm transient .config_t
@@ -566,20 +383,12 @@ proc edit_settings {} {
# store new settings
proc putsettings {} {
- if {$::classic_unix && ! $::ge_85} {
- wm withdraw .config_t.viewf.pdf.lf
- wm withdraw .config_t.viewf.ps.lf
- }
wm withdraw .config_t
grab release .config_t
write_settings
}
proc cancelsettings {} {
- if {$::classic_unix && ! $::ge_85} {
- wm withdraw .config_t.viewf.pdf.lf
- wm withdraw .config_t.viewf.ps.lf
- }
wm withdraw .config_t
grab release .config_t
# re-read config file / reg entries
@@ -593,93 +402,91 @@ proc show_w {w} {
raise $w
}
+pack [ttk::frame .bg -padding 3] -fill both -expand 1
+
# buttons to call up configure-, log- and help screens
-pack [frame .topf] -fill x
-packb [button .topf.config_t -text "Configure" \
+pack [ttk::frame .topf] -in .bg -fill x
+ppack [ttk::button .topf.config_t -text "Configure" \
-command edit_settings] -side left
-packb [button .topf.help_t -text "Help" \
+ppack [ttk::button .topf.help_t -text "Help" \
-command {show_w .help_t}] -side right
-packb [button .topf.logb -text "Show log" -command {show_w .log_t}] \
+ppack [ttk::button .topf.logb -text "Show log" -command {show_w .log_t}] \
-side right -anchor w
# file info in grid layout
-packf [frame .infof -relief sunken -border 1] -fill x
-grid [label .infof.dir_label -text "Directory" -anchor w] \
+ppack [ttk::frame .infof -relief sunken -border 1 -padding 3] -in .bg -fill x
+grid [ttk::label .infof.dir_label -text "Directory" -anchor w] \
-row 1 -column 1 -sticky w
-grid [label .infof.dir_value -textvariable ::gfile(dir) -anchor w] \
+grid [ttk::label .infof.dir_value -textvariable ::gfile(dir) -anchor w] \
-row 1 -column 2 -sticky w
-grid [label .infof.name_label -text "File" -anchor w] \
+grid [ttk::label .infof.name_label -text "File" -anchor w] \
-row 2 -column 1 -sticky w
-grid [label .infof.name_value -textvariable ::gfile(name) -anchor w] \
+grid [ttk::label .infof.name_value -textvariable ::gfile(name) -anchor w] \
-row 2 -column 2 -sticky w
-grid [label .infof.type_label -text "Type" -anchor w] \
+grid [ttk::label .infof.type_label -text "Type" -anchor w] \
-row 3 -column 1 -sticky w
-grid [label .infof.type_value -textvariable ::gfile(type) -anchor w] \
+grid [ttk::label .infof.type_value -textvariable ::gfile(type) -anchor w] \
-row 3 -column 2 -sticky w
-grid [label .infof.npages_label -text "Pages" -anchor w] \
+grid [ttk::label .infof.npages_label -text "Pages" -anchor w] \
-row 4 -column 1 -sticky w
-grid [label .infof.npages_value -textvariable ::gfile(npages) -anchor w] \
+grid [ttk::label .infof.npages_value -textvariable ::gfile(npages) -anchor w] \
-row 4 -column 2 -sticky w
grid columnconfigure .infof 1 -weight 1 -pad 2
grid columnconfigure .infof 2 -weight 3 -pad 2
-spacing .
-
# conversion options
-pack [frame .optsf] -fill x
+pack [ttk::frame .optsf] -in .bg -pady [list 10 0] -fill x
# grayscaling
-pack [frame .optsf.gray] -side left -anchor nw
-pack [label .optsf.gray.l -text "Grayscaling"] -anchor w
-pack [radiobutton .optsf.gray.off -text "No color conversion" \
+pack [ttk::frame .optsf.gray] -side left -anchor nw
+pack [ttk::label .optsf.gray.l -text "Grayscaling"] -anchor w
+pack [ttk::radiobutton .optsf.gray.off -text "No color conversion" \
-variable ::options(gray) -value "color"] -anchor w
-pack [radiobutton .optsf.gray.gray -text "Grayscale" \
+pack [ttk::radiobutton .optsf.gray.gray -text "Grayscale" \
-variable ::options(gray) -value "gray"] -anchor w
-#pack [radiobutton .optsf.gray.gRAY -text "Try harder to grayscale" \
-# -variable ::options(gray) -value "gRAY"] -anchor w
# output format
-pack [label .optsf.format] -side right -anchor ne
-pack [label .optsf.format.l -text "Output format"] -anchor w
-pack [radiobutton .optsf.format.pdf -text "pdf" -command set_widget_states \
- -variable ::options(format) -value "pdf"] -anchor w
-pack [radiobutton .optsf.format.eps -text "eps" -command set_widget_states \
- -variable ::options(format) -value "eps"] -anchor w
-pack [radiobutton .optsf.format.ps -text "ps" -command set_widget_states \
- -variable ::options(format) -value "ps"] -anchor w
-
-spacing .
+pack [ttk::label .optsf.format] -side right -anchor ne
+pack [ttk::label .optsf.format.l -text "Output format"] -anchor w
+ttk::radiobutton .optsf.format.pdf -text "pdf" \
+ -command set_widget_states -variable ::options(format) -value "pdf"
+ttk::radiobutton .optsf.format.eps -text "eps" -command set_widget_states \
+ -variable ::options(format) -value "eps"
+ttk::radiobutton .optsf.format.ps -text "ps" -command set_widget_states \
+ -variable ::options(format) -value "ps"
+pack .optsf.format.pdf -anchor w
+pack .optsf.format.eps -anchor w
+pack .optsf.format.ps -anchor w
# boundingbox
-pack [checkbutton .bbox -text "Compute tight boundingbox" \
- -variable ::options(bbox) -command set_widget_states] -anchor w
+pack [ttk::checkbutton .bbox -text "Compute tight boundingbox" \
+ -variable ::options(bbox) -command set_widget_states] \
+ -in .bg -anchor w -pady [list 10 0]
# page selection
-pack [frame .pagesf] -fill x
-pack [radiobutton .pagesf.all -text "Convert all pages" \
+pack [ttk::frame .pagesf] -in .bg -fill x
+pack [ttk::radiobutton .pagesf.all -text "Convert all pages" \
-variable ::options(pages) -value "all" -command set_widget_states] \
-side left
-pack [radiobutton .pagesf.single -text "Page:" \
+pack [ttk::radiobutton .pagesf.single -text "Page:" \
-variable ::options(pages) -value "single" -command set_widget_states] \
- -side left
+ -side left -padx [list 10 0]
pack [entry .pagesf.e -width 6 -textvariable ::options(page)] -side left
#.pagesf.e configure -vcmd {page_valid %W} -validate focusout \
# -invcmd "focusAndFlash %W [.pagesf.e cget -fg] [.pagesf.e cget -bg]"
.pagesf.e configure -vcmd {page_valid %W} -validate focusout \
-invcmd "see_red %W"
-spacing .
-
# temp files
-pack [checkbutton .clean -text "Remove temp files" \
- -variable ::options(clean)] -anchor w
+pack [ttk::checkbutton .clean -text "Remove temp files" \
+ -variable ::options(clean)] -in .bg -anchor w -pady [list 10 0]
proc focusAndFlash {w fg bg {count 9}} {
focus $w
@@ -720,17 +527,18 @@ proc page_valid {w} {
# end conversion options
-pack [label .status -justify left] -side bottom -anchor w -fill x -expand 1
+pack [ttk::label .status -justify left -text "Idle"] \
+ -in .bg -side bottom -anchor w -fill x -expand 1
# main buttons
-pack [frame .bottomf] -side bottom -fill x
-packb [button .bottomf.view -text "View" -command view] -side left
-packb [button .bottomf.open -text "Open" -command openDialog] \
+pack [ttk::frame .bottomf] -in .bg -side bottom -fill x
+ppack [ttk::button .bottomf.view -text "View" -command view] -side left
+ppack [ttk::button .bottomf.open -text "Open" -command openDialog] \
-side left -padx 2
-packb [button .bottomf.convert -text "Convert and save..." \
+ppack [ttk::button .bottomf.convert -text "Convert and save..." \
-command saveDialog] -side left -padx 2
-packb [button .bottomf.done -text "Done" -command exit] -side right
+ppack [ttk::button .bottomf.done -text "Done" -command exit] -side right
proc view {} {
if {! [viewable]} {
@@ -812,9 +620,7 @@ proc saveDialog {} {
# epspdf can read persistent options from configuration.
# only options from the options array need to be converted to parameters.
set args [list]
- if {$::options(gray) eq "gray"} {
- lappend args "-g"
- }
+ if {$::options(gray) eq "gray"} {lappend args "-g"}
if {$::options(bbox)} {lappend args "-b"}
if {! $::options(clean)} {lappend args "-d"}
if {$::options(pages) eq "single"} {
@@ -828,31 +634,36 @@ proc saveDialog {} {
}
update idletasks; # force immediate redisplay main window
- if {$::ge_85} {
- set failed [catch [linsert $args 0 \
- exec -ignorestderr $::texlua $::epspdf_tlu --gui=gui] result]
+ set cmd {} ; # necessary? if- and else blocks do not seem to define scopes
+ if {$::tcl_platform(platform) == "windows"} {
+ set cmd [linsert $args 0 exec -ignorestderr $::epspdf_cmd --gui=gui]
} else {
- set failed [catch [linsert $args 0 \
- exec $::texlua $::epspdf_tlu --gui=gui] result]
+ set cmd [linsert $args 0 exec -ignorestderr \
+ $::texlua $::epspdf_tlu --gui=gui]
}
+ set failed [catch $cmd result]
write_log $result
- if {$failed} {
+ if $failed {
tk_messageBox -icon error -type ok -message "Error; see log window"
} else {
set ::gfile(path) [file normalize $try]
set ::gfile(dir) [file dirname $::gfile(path)]
set ::gfile(type) $::options(format)
set ::gfile(name) [file tail $::gfile(path)]
- # parse result output
- regexp { is (\w+)(?: with (\d+) pages)?[\r\n]*$} \
- [string range $result [string last "File type of" $result] end] \
- mtc ::gfile(type) ::gfile(npages)
- if {$::gfile(type) eq "eps"} {set ::gfile(npages) 1}
+ if {$::gfile(type) eq "pdf"} {
+ if {! [catch {
+ exec $::texlua $::epspdf_tlu --gui=gui -i $::gfile(path)} \
+ result]} {
+ regexp {(\d+) pages\.$} $result mtc ::gfile(npages)
+ }
+ } elseif {$::gfile(type) eq "eps"} {
+ set ::gfile(npages) 1
+ }
set ::settings(default_dir) $::gfile(dir)
putsettings
set ::options(page) 1
}
- .status configure -text ""
+ .status configure -text "Idle"
foreach b {view open convert done} {
.bottomf.$b configure -state normal
}