diff options
author | Karl Berry <karl@freefriends.org> | 2009-03-08 01:37:33 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2009-03-08 01:37:33 +0000 |
commit | 527a74fac35059aa094d70a74a74949e35655060 (patch) | |
tree | f72b507060bd21b802a6b47aa453ce236031a239 /Master/texmf-dist/scripts/epspdf | |
parent | ff0e5deb9aa09b585004a4e4c66a0543979e8425 (diff) |
epspdf update
git-svn-id: svn://tug.org/texlive/trunk@12334 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts/epspdf')
-rwxr-xr-x | Master/texmf-dist/scripts/epspdf/epspdf | 2 | ||||
-rw-r--r-- | Master/texmf-dist/scripts/epspdf/epspdf.help | 485 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/epspdf/epspdf.rb | 540 | ||||
-rw-r--r-- | Master/texmf-dist/scripts/epspdf/epspdfboot.rb | 40 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/epspdf/epspdfrc.rb | 415 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/epspdf/epspdftk | 2 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/epspdf/epspdftk.rb | 274 | ||||
-rw-r--r-- | Master/texmf-dist/scripts/epspdf/makegray.pro | 4 |
8 files changed, 1148 insertions, 614 deletions
diff --git a/Master/texmf-dist/scripts/epspdf/epspdf b/Master/texmf-dist/scripts/epspdf/epspdf index 4492608f398..f973474b312 100755 --- a/Master/texmf-dist/scripts/epspdf/epspdf +++ b/Master/texmf-dist/scripts/epspdf/epspdf @@ -1,3 +1,3 @@ #!/bin/sh script=`kpsewhich -format=texmfscripts epspdf.rb` -"$script" $* +"$script" "$@" diff --git a/Master/texmf-dist/scripts/epspdf/epspdf.help b/Master/texmf-dist/scripts/epspdf/epspdf.help index 5bdc475ff8d..f7d37ff3c2c 100644 --- a/Master/texmf-dist/scripts/epspdf/epspdf.help +++ b/Master/texmf-dist/scripts/epspdf/epspdf.help @@ -1,173 +1,366 @@ - Epspdf and epspdftk User Guide +Epspdf and epsdftk +****************** - A multiplatform GUI- and command-line converter for [e]ps and pdf. +This manual is for epspdf, version 0.4.0. - Siep Kroonenberg, - siepo at cybercomm dot nl +Copyright (C) 2006, 2007, 2008, 2009 Siep Kroonenberg. -Overview + Permission is granted to make and distribute verbatim copies of + this entire document without royalty provided the copyright notice + and this notice are preserved. - Epspdftk converts files between eps, pdf and general PostScript. The main - screen lets you open a file, select some options and convert the file. +1 Usage +******* - 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 half of the screen. +1.1 Overview +============ -Viewing +Epspdftk converts files between eps, pdf and general PostScript. The +main screen lets you open a file, select some options and convert the +file. - The View button at the bottom of the screen calls an appropriate external - viewer. For Windows this is determined by the default Open command, for OS - X it is always Preview, and under Linux it is configurable; see below - under [1]The Configuration screen. +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. - The View button is grayed if epspdf thinks that there is no previewer for - the current file; see [2]the epspdf main page for details. -Conversion options - Grayscaling: Simple color figures can be converted to grayscale. - Grayscaling will fail for bitmapped data and for advanced color models. - 'Try harder to grayscale' will handle a few more cases, but may take - longer and still won't handle bitmaps. +1.2 Viewing +=========== - Call for assistance: If a PostScript color expert is reading this: please - have a look at makegray.pro and see whether you can come up with something - that handles more color models. +The View button at the bottom of the screen calls an appropriate +external viewer. - Compute tight boundingbox: This option is only available if a single page - is converted. +Windows: Epspdftk simply tries to use the default Open command. A +second button, labeled View with..., lets the user select a different +program to view the file. - Page selection: The only possibilities are selecting a single page or - selecting all pages. When converting to eps you are restricted to a single - page. +For OS X the viewer is also the default program, but there is no View +with... button. - For general PostScript files, there is no quick way to determine the - number of pages, so the program won't check beforehand whether you picked - an existing page. If you picked a non-existent page, the program will - abort. If you don't like that, convert the entire document to pdf first - - which will be done behind the scenes anyhow. +Under Linux the PostScript- and pdf viewers are configurable; see *note +The configuration screen::. - Specifying options such as grayscaling or page selection may require - several roundtrips between PostScript or eps and pdf. If epspdf cannot use - pdftops then some combinations of options will not be available. +The View button is grayed if epspdf thinks that there is no previewer +for the current file. -Converting +1.3 Conversion options +====================== - The Convert and save... button calls up a file save dialog, with - appropriate default extension. After a successful conversion, the result - becomes the new current file, so you can judge the result by pressing the - View button again. +Grayscaling: Simple color figures can be converted to grayscale. +Bitmapped data won't be converted. If grayscaling doesn't work on +non-bitmapped data, select the "Try harder to grayscale" option. This +may cause an extra pdf-PostScript roundtrip. -The configuration screen +Compute tight boundingbox: This option is only available if a single +page is converted. - Epspdftk also has a configuration screen. The settings here are preserved - between sessions. Pay particular attention to 'Intended use' under PDF - CONVERSION. Choose 'prepress' for pdfs which are intended for commercial - printing. This option will try to embed all fonts, among other things. - More and more often, printshops insist on this. With luck, Ghostscript - fonts and system fonts will be used for fonts which were not originally - embedded. +Page selection: The only possibilities are selecting a single page or +selecting all pages. When converting to eps you are restricted to a +single page. - Under Windows, this screen also lets you tell the program where to find - pdftops.exe. Under Linux, it lets you pick PostScript- and pdf previewers. +For general PostScript files, there is no quick way to determine the +number of pages, so the program won't check beforehand whether you +picked an existing page. If you picked a non-existent page, the program +will abort. If you don't like that, convert the entire document to pdf +first - which will be done behind the scenes anyhow. -Adding standard fonts to an existing pdf +Specifying options such as grayscaling or page selection may require +several roundtrips between PostScript or eps and pdf. - You can add standard fonts such as Times and Helvetica to an existing pdf: - first, convert to ps, with 'Convert all pages' selected. Then, make sure - that in the configuration screen 'Intended use' is set to prepress, then - convert back to pdf. +1.4 Converting +============== - If this option is set to screen, then standard fonts will be removed - instead of added. This option may also affect the resolution of bitmaps. +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. -Eps preview headers +1.5 The configuration screen +============================ - 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. + Epspdftk also has a configuration screen. The settings here are +preserved between sessions. -On-the-fly conversion +1.5.1 Configuring viewers +------------------------- + +Under Linux, the preferred PostScript- and pdf viewers can be configured +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 +will use the program associated with the file type, which can be +configured outside epspdf. Under Windows, you can use the "View +with..." button instead. + +1.5.2 Options for converting to pdf +----------------------------------- + +Double-check the setting "Target use" under "Conversion to pdf". Choose +"prepress" for pdfs which are target for commercial printing. This +option will try to embed all fonts, among other things. Often, +printshops insist on this. With luck, Ghostscript fonts and system +fonts will be used for fonts which were not originally embedded. + +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. + +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. +Characters may be converted to drawn shapes or little bitmaps so this +is not recommended. + +Under Windows, this is also the place to point the program to the +location of pdftops.exe. + +1.5.4 Hires BoundingBox +----------------------- + +Uncheck the option "Use hires boundingbox if possible" if otherwise the +computed boundingbox is slightly too tight, resulting in some clipping. +It has two effects: + * When converting to pdf, it uses the standard rather than the hires + boundingbox for page dimensions + + * When computing a boundingbox, it adds a small safety margin of 1pt + to the standard boundingbox, except where it would make any + boundingbox coordinate negative. + +2 Command-line usage +******************** + +epspdf.rb, the library for epspdftk.rb, does double duty as command-line +version of epspdftk.rb. It shares configuration settings with +epspdftk.rb. + +It is strongly recommended to invoke epspdf.rb via a wrapper script +epspdf or epspdf.bat on the search path, e.g. + #!/bin/sh + PATH_TO_EPSPDF/epspdf.rb "@" +for Unix/Linux/OS X, or + "PATH_TO_RUBY.EXE" "PATH_TO_EPSPDF.RB" %* +for Windows. It is the responsibility of the caller to provide double +quotes. + +The TeX Live package for epspdf already includes such wrapper scripts. + +Basic usage: + + epspdf [OPTIONS] INFILE OUTFILE + +2.1 Option summary +================== + +The summary below is the output of a command `epspdf --help' for help. + $ epspdf --help + Epspdf version 0.4.0 + Copyright (C) 2006, 2008, 2009 Siep Kroonenberg + Epspdf 0.4.0 + Convert between [e]ps and pdf formats + Usage: epspdf.rb [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 + -g, --gray, --grey Convert to grayscale; + success not guaranteed + -G, --GRAY, --GREY Try harder to convert to grayscale + -p, --pagenumber=PAGENUMBER Page to be converted or selected + -b, --bbox, --BoundingBox Compute tight boundingbox + -n, --no-hires Don't use hires boundingbox + -r, --hires Use hires boundingbox + -T, --target=TARGET Target use of pdf; one of + default, printer, prepress, screen, ebook + -N, --pdfversion=PDFVERSION Pdf version to be generated + -V, --version=PDFVERSION Deprecated; use `-N' or `--pdfversion'. + -I Ignore pdftops even if available + (default: use if available) + -U Use pdftops if available + (overrides previous -I setting) + -C, --custom=CUSTOMOPTIONS Custom options for conversion to pdf, + view Use.htm and ps2pdf.htm from + the Ghostscript documentation set + -P, --psoptions=PSOPTIONS Options for pdftops; default -level3, + don't include -eps or page number options; + these will be generated by the program + -i, --info Info: display detected filetype + -s Save (some) settings + -d Debug: don't remove temp files + -v Prints version info + -h, --help Show this message + +3 Notes on PostScript and pdf +***************************** + +3.1 Bitmapped and vector +======================== + +Pictures can be described either in terms of pixels, or more abstractly, +in terms of geometric shapes, fonts and text. + +Bitmapped or pixel-based graphics are appropriate for photographs and +screenshots, but less so for diagrams and spreadsheet-generated +graphics. + +A file in PostScript- or pdf format can contain both types of graphic +data, e.g. a text in vector format agains a photographic background in +bitmapped format. + +Vector graphics can be freely scaled without losing sharpness or +becoming pixellated. If bitmapped graphics are enlarged too much, +individual pixels become apparent. With low-resolution bitmaps this +happens sooner than with high-resolution bitmaps, but high-resolution +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 +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. + +3.2 Font embedding +================== + +When converting to pdf, Ghostscript handles font embedding differently +depending on the "Target use" option. For prepress, it embeds even the +standard fonts, including Times etc. Ghostscript has copies of these +fonts in case the PostScript file doesn't have them. For screen, it +doesn't embed standard fonts. + +You can embed fonts after the fact by converting pdf to eps or ps, and +back to pdf, with target use set to "prepress". However, don't try this +unless you have pdftops. + +If target use is set to "screen", then standard fonts will be removed +instead of added. This option may also cause downsampling of bitmaps. + +3.3 Eps preview headers +======================= + +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. + +3.4 Bounding- and other boxes +============================= + +A PostScript file may have a page size and a boundingbox defined. A pdf +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. + +When epspdf or a similar program converts eps to pdf, it adds code to +move the bottom left corner of the graphic to (0,0) and to set the page +to the size of the graphic. Anything outside the boundingbox should be +cut off. + +Both Ghostscript and pdftops have some parameters for setting or using +these boxes; consult Ps2pdf.htm from the Ghostscript documentation and +"pdftops -help" or "man pdftops". + +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. It may help to change the extension from ".eps" to ".ps" +before converting to pdf. In this case, let epspdf generate a new +boundingbox. + +3.6 Exporting PostScript from Windows programs +============================================== + +Often, the only way to get EPS or PostScript from a Windows program is +by "printing" to a PostScript file. From some programs, you can print a +selection. A suitable driver which comes with Windows is Generic / MS +Publisher Imagesetter. Pay attention to the printer properties: choose +"Outline" for font downloading and avoid the "Optimize for speed" +setting for PostScript Output Option. Try e.g. "Archive" instead. These +options can be found under the Advanced button. + + Other possibilities are the TpX and wmf2eps programs, which both have +options to write clipboard contents to an emf file. Wmf2eps uses a +printer driver in the background. For faithful conversion, pick +wmf2eps; for subsequent editing, choose TpX. Both programs are +available from `http://www.tug.org/ctan.html'. + +4 Troubleshooting +***************** + +4.1 Grayscaling fails +===================== + +See above, under *note Grayscaling: gray. Epspdf is limited by what can +be done by the backend tools, i.e. Ghostscript and pdftops. In +particular, epspdf cannot grayscale bitmapped data. + +4.2 Part of the graphic gets cut off +==================================== + +Things to try: + * If the PostScript file was generated with the Windows PostScript + driver, experiment with the PostScript Output option. Don't choose + Optimize for Speed. + + * Replace the extension with ".ps" and let epspdf generate a + boundingbox. + + * Boundingbox calculations may be slightly inaccurate where text is + involved. If just a sliver is cut off, let epspdf ignore the + high-resolutuion boundingbox. Command-line option: "-n" or + "-no-hires". + +4.3 Fonts look ugly +=================== + +If Ghostscript has to do the conversion from pdf to ps then text will +not remain text, but will be replaced by drawn shapes or bitmaps. Try +to get hold of the xpdf suite, which includes pdftops, especially if +your files contain serious amount of text. + +4.4 Resources for troubleshooting +================================= + +Logfile. Epspdf and epspdftk maintain a log file epspdf.log in your +home directory or, in case of Windows, your profile directory. This +profile directory may be `c:\Documents and Settings\YOUR USER NAME'. +Otherwise, open a DOS box and type `echo %USERPROFILE%', which will +produce this information. + +This logfile lists all Ghostscript- and pdftops program calls plus +error information. "EPCallError" is probably a program error. +'EPSystemError" is more likely caused by a faulty file, but may be a +program error as well. + +Temporary files. The temporary files may give clues as well. They will +be deleted at the end of the session (for the command-line version, +give a -d parameter to keep the temporary files). Check the logfile as +to 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. + +5 Changes in version 0.4 +************************ + +Hi-res boundingboxes are now supported. By default, conversion from eps +to pdf now uses the hires boundingbox as "page" to determine the page +dmensions of the pdf file. Other conversions preserve or generate a +hires boundingbox. + +Under Windows, the new version looks for an installed TeX and will use +its private Ghostscript if it cannot find separately installed +Ghostscript. TeX Live's pdftops, being on the searchpath, will be used +unless epspdf finds another copy first. + +There is now a "-v" (lowercase) option to print the version string. - Daniel Becker wrote a LaTeX package epspdfconversion which converts eps - files on the fly to pdf, like Heiko Oberdiek's epstopdf package, but with - epspdf as backend. It can make use of the epspdf conversion options. Both - packages are available from [3]CTAN and included in TeX Live. - -Exporting PostScript from Windows programs - - Often, the only way to get EPS or PostScript from a Windows program is by - 'printing' to a PostScript file. From some programs, you can print a - selection. A suitable driver which comes with Windows is Generic / MS - Publisher Imagesetter. Pay attention to the printer properties: avoid the - 'Optimize for speed' setting and choose Outline for font downloading. - These options can be found under the Advanced button. - - Other possibilities are the TpX and wmf2eps programs, which both have - options to write clipboard contents to an emf file. Wmf2eps uses a printer - driver in the background. For faithful conversion, pick wmf2eps; for - subsequent editing, choose TpX. Both programs are availbable from [4]CTAN. - -Command-line use - - Epspdf.rb, the library for epspdftk.rb, does double duty as command-line - version of epspdftk.rb. Type epspdf.rb -h for help. It shares - configuration settings with epspdftk. - -Troubleshooting - - Grayscaling fails - - See above, under [5]Grayscaling. Epspdf is limited by what can be done by - the backend tools, i.e. Ghostscript and pdftops. - - Part of the graphic gets cut off - - I have seen this happen with eps files generated by printing landscape - PowerPoint slides to PostScript with the Encapsulated PostScript output - option. Using the Archive output option instead solved the problem. In - general, experiment a bit in generating your PostScript. - - Mac OS X problems with epspdftk - - Even though epspdftk is not an X11 program, you may still need some pieces - of X11 and the X11 SDK, which are optional installs of OS X. - - Under Leopard, the window that is displayed during the conversion stays - blank. I have no idea why. - - Resources for troubleshooting - - Logfile. Epspdf and epspdftk maintain a log file. For Unix and Mac OS X it - is epspdf.log in your home directory. For Windows, it is epspdf.log or - epspdftl.log in your profile directory. The latter filename is used if - epspdf is embedded in TeX Live. This profile directory may be c:\Documents - and Settings\<your user name>. Otherwise, open a DOS box and type 'echo - %USERPROFILE%', which will produce this information. - - This logfile lists all Ghostscript- and pdftops program calls plus error - information. 'EPCallError' is probably a program error. 'EPSystemError' is - more likely caused by a faulty file, but may be a program error as well. - - Temporary files. The temporary files may give clues as well. They will be - deleted at the end of the session (for the command-line version, give a -d - parameter to keep the temporary files). The logfile can tell you which - temporary files have been created. - - [6]Main page... - - -------------------------------------------------------------------------- - - Last revised: July 27, 2008 - -References - - Visible links - 1. http://tex.aanhet.net/epspdf/epspdf_UG.html#configure - 2. http://tex.aanhet.net/epspdf/index.html#preview - 3. http://www.tug.org/ctan.html - 4. http://www.tug.org/ctan.html - 5. http://tex.aanhet.net/epspdf/epspdf_UG.html#gray - 6. http://tex.aanhet.net/epspdf/index.html diff --git a/Master/texmf-dist/scripts/epspdf/epspdf.rb b/Master/texmf-dist/scripts/epspdf/epspdf.rb index 3b11c62278f..09462af8330 100755 --- a/Master/texmf-dist/scripts/epspdf/epspdf.rb +++ b/Master/texmf-dist/scripts/epspdf/epspdf.rb @@ -3,7 +3,7 @@ # epspdf conversion utility, main source ##### -# Copyright (C) 2006, 2008 Siep Kroonenberg +# Copyright (C) 2006, 2008, 2009 Siep Kroonenberg # n dot s dot kroonenberg at rug dot nl # # This program is free software, licensed under the GNU GPL, >=2.0. @@ -14,41 +14,32 @@ # The focus is on converting between eps and pdf. # Code is organized as a set of single-step conversion methods plus an -# any-to-any conversion. +# any-to-any conversion chaining them together. # `Target' is not a parameter; all conversions write to a new temp # file. Conversions can be chained: object.conversion1( params # ).conversion2( params ) ... The final temp file is moved or copied to -# the desired destination by the main program (which can be epspdf +# the desired destination by the main program (which can be epspdf.rb # itself). # The use of exception handling makes it unnecessary to inspect return # values. -# Limitation -# HiRes boundingboxes are ignored. -# Two reasons: -# - they get lost anyhow on conversion to pdf -# - They are not required so when you find one you have to make sure -# that it doesn't refer to an included eps. This would -# require additional logic. - ########################################### # some initialization in case this file is used as main program if __FILE__ == $0 - # epspdfboot.rb: modify loadpath in a way which works both - # with a full Ruby and with a custom subset ruby - - require __FILE__.sub( /epspdf\.rb/, 'epspdfboot.rb' ) + # add directory of this source to loadpath + $:.unshift( File.dirname( __FILE__ ) ) # turn on warnings #$VERBOSE = 1 end # if main program + ########################################### # Error handling @@ -241,24 +232,30 @@ end # def ########################################### -# Boundingboxes +# Boundingboxes; first standard, then hires -BB_PAT = /^\s*%%BoundingBox:\s*(-?\d+)((\s+-?\d+){3})\s*$/ +#changes: +#hires boundingboxes +#numbers may start with a plus-sign. DSC manual is ambiguous, +# PostScript manual allows it, but of course PS != DSC +#left-right and lower-upper need not be in natural order + +BB_PAT = /^\s*%%BoundingBox:\s*([-+]?\d+)((\s+[-+]?\d+){3})\s*$/ BB_END = /^\s*%%BoundingBox:\s*\(\s*atend\s*\)\s*$/ -# for multiline matching: -BB_PAT1 = /%%BoundingBox:\s*(-?\d+)((\s+-?\d+){3})/m -BB_END1 = /%%BoundingBox:\s*\(\s*atend\s*\)/m class Bb - attr_accessor :llx, :lly, :urx, :ury + attr_accessor :llx, :lly, :urx, :ury # strings def initialize( llx, lly, urx, ury ) @llx, @lly, @urx, @ury = llx, lly, urx, ury + # guarantee valid syntax: [@llx, @lly, @urx, @ury].each { |l| l = l.to_i.to_s } + @llx, @urx = @urx, @llx if @llx.to_i > @urx.to_i + @lly, @ury = @ury, @lly if @lly.to_i > @ury.to_i end - def Bb.from_bbcomment( s ) + def Bb.from_comment( s ) return nil unless s =~ BB_PAT llx, lly, urx, ury = s.sub( /^\s*%%BoundingBox:\s*/, '' ).split( /\s+/ ) @@ -325,13 +322,103 @@ class Bb # convert boundingbox to boundingbox comment - def bbcomment + def comment fail EPBBError, prn unless valid "%%BoundingBox: #{@llx} #{@lly} #{@urx} #{@ury}" end end # class Bb +# [-+](\d+(\.\d*)?|\.\d+)([eE]\d+)? PostScript number +HRBB_PAT = /^\s*%%HiResBoundingBox:\s*[-+]?(\d+(\.\d*)?|\.\d+)([eE]\d+)?((\s[-+]?(\d+(\.\d*)?|\.\d+)([eE]\d+)?){3})\s*$/ +HRBB_END = /^\s*%%HiResBoundingBox:\s*\(\s*atend\s*\)\s*$/ + +class HRBb + + attr_accessor :llx, :lly, :urx, :ury + + def initialize( llx, lly, urx, ury ) + @llx, @lly, @urx, @ury = llx, lly, urx, ury + [@llx, @lly, @urx, @ury].each do |l| + if l =~ /\./ + # make floats conform to Ruby syntax: + # decimal dots must be padded with digits on either side + l.sub!( /^\./, '0.' ) + l.sub!( /\.(?!\d)/, '.0' ) # (?!\d): zero-width neg. lookahead + end + l = l.to_f.to_s + end + @llx, @urx = @urx, @llx if @llx.to_f > @urx.to_f + @lly, @ury = @ury, @lly if @lly.to_f > @ury.to_f + end + + def HRBb.from_hrcomment( s ) + return nil unless s =~ HRBB_PAT + llx, lly, urx, ury = + s.sub( /^\s*%%HiResBoundingBox:\s*/, '' ).split( /\s+/ ) + HRBb.new( llx, lly, urx, ury ) + end + + def height + ( @ury.to_f - @lly.to_f ).to_s + end + + def width + ( @urx.to_f - @llx.to_f ).to_s + end + + def valid + @llx.to_f < @urx.to_f and @lly.to_f < @ury.to_f + end + + def non_negative + valid and @llx.to_f >= 0 and @lly.to_f >= 0 + end + + def copy + HRBb.new( @llx, @lly, @urx, @ury ) + end + + def prn + "#{@llx} #{@lly} #{@urx} #{@ury}" + end + + ################## + + # wrapper code for a hires boundingbox; + # moves lower left corner of eps to (0,0) + # and defines a page size identical to the eps width and height. + # The gsave in the wrapper code should be matched by + # a grestore at the end of the PostScript code. + # This grestore can be specified on the Ghostscript command-line. + + def wrapper + fail EPBBError, prn unless valid + fname = mktemp( 'ps' ) + File.open( fname, $W ) do |f| + f.binmode if ARCH == 'w32' + f.write( + "%%BoundingBox: 0 0 #{width.to_f.ceil} #{height.to_f.ceil}\n" + + "%%HiResBoundingBox: 0 0 #{width.to_f} #{height.to_f}\n" + + "<< /PageSize [#{width} #{height}] >>" + + " setpagedevice\n" + + "gsave #{(-(@llx.to_f)).to_s} #{(-(@lly.to_f)).to_s}" + + " translate\n" ) > 0 + end # open + return fname + end # wrapper + + ################## + + # convert hiresboundingbox to hires boundingbox comment + + def hrcomment + fail EPBBError, prn unless valid + "%%HiResBoundingBox: #{@llx} #{@lly} #{@urx} #{@ury}" + end + +end # class HRBb + ########################################### # PsPdf class definition @@ -367,7 +454,7 @@ class PsPdf when /-level\d\b/ $settings.ps_options.sub( /(-level\d)/, '\1sep' ) else - $settings.ps_options + " -level2sep" + $settings.ps_options + " -level3sep" end # case else $settings.ps_options @@ -376,7 +463,7 @@ class PsPdf # instance methods ### - attr_accessor :path, :bb, :type, :npages, :atfront + attr_accessor :path, :bb, :hrbb, :type, :npages, :atfront, :hr_atfront ################## @@ -421,22 +508,33 @@ class PsPdf # Find boundingbox, simple case. # We shall call this method only for eps PsPdf objects which were # converted by pdftops or Ghostscript, so we can be sure that - # the boundingbox is not (atend). The file is not rewritten. + # the boundingbox is not (atend). + # We also assume that the hrbb lies within the bb. + # The file is not rewritten. def find_bb_simple fail EPCallError, buginfo unless @type == 'eps' @bb = nil + @hrbb = nil slurp = '' File.open( @path, $R ) do |fl| slurp = fl.read( [File.size(@path), SAFESIZE].min ) end lines = slurp.split( /\r\n?|\n/ ) + # look for a bb or a hrbb + # if a valid bb is found, check the next line for hrbb + # but look no further; we don't want to mistake a hrbb of + # an included eps for the hrbb of the outer eps. lines.each do |l| if l =~ BB_PAT - @bb = Bb.from_bbcomment( l ) - break + @bb = Bb.from_comment( l ) + elsif l =~ HRBB_PAT + @hrbb = HRBb.from_hrcomment( l ) + elsif @bb + break # stop looking; we expect hrbb next to bb end + break if @bb and @hrbb end # do |l| fail EPBBError, @path unless @bb and @bb.valid @@ -484,12 +582,14 @@ class PsPdf # eps_clean: write source eps to an eps without preview, and # with a boundingbox in the header. + # clean up any potential problems # the eps is always written to a new file. def eps_clean fail EPCallError, buginfo if @type != 'eps' and @type != 'epsPreview' atend = nil + hr_atend = nil slurp = '' offset, ps_len = nil, nil if @type == 'eps' @@ -507,8 +607,8 @@ class PsPdf end # File end # ifthenelse @type - # boundingbox unknown and possibly atend - @bb, @atfront = nil, nil + # [hires] boundingbox unknown and possibly atend + @bb, @atfront, @hrbb, @hr_atfront = nil, nil, nil, nil # limit search for boundingbox comments. # For very large eps files, we don't want to scan the entire file. @@ -522,68 +622,147 @@ class PsPdf # We capture both lines and separators, as an easy way to # keep track of how many bytes have been read. - # It is not necessary to treat lines and separators differently. - - lines = slurp.split( /(\r\n?|\n)/ ) - pre_block = '' - post_block = '' + # we assume that if there is a hires bb then + # bb and hires bb are on consecutive lines. + # Otherwise the logic would get too messy. + + # The epsfile will be reconstituted from: + # a series of lines and line separators; then either + # - a bbox or + # - (bbox or hrbbox), separator, (hrbbox or bbox) + # the big blob + # possibly a trailer with removed bb comments + + pre_lines = slurp.split( /(\r\n?|\n)/ ) bb_comment = '' - lines.each do |l| - pre_block = pre_block + l + # initialize indices of bb comments to less than smallest index + i_bb = -1 + i_hrbb = -1 + i = -1 + i_end = -1 + pre_length = 0 + pre_lines.each do |l| + pre_length += l.length + i += 1 + next if l =~ /(\r\n?|\n)/ if l =~ BB_PAT - @bb = Bb.from_bbcomment( l ) + @bb = Bb.from_comment( l ) @atfront = true - break + i_bb = i elsif l =~ BB_END atend = true - break + i_bb = i + elsif l =~ HRBB_PAT + @hrbb = HRBb.from_hrcomment( l ) + @hr_atfront = true + i_hrbb = i + elsif l =~ HRBB_END + hr_atend = true + i_hrbb = i + elsif @bb or atend + i_end = i + break # stop looking; we expect hrbb next to bb end # =~ BB_PAT end # do |l| - if atend + if atend or hr_atend if ps_len > SAFESIZE File.open( @path, $R ) do |fl| fl.seek( offset + ps_len - SAFESIZE, IO::SEEK_SET ) slurp = fl.read( SAFESIZE ) end - lines = slurp.split( /(\r\n?|\n)/ ) - end - lines.reverse_each do |l| - post_block = l + post_block + end # else use old slurp + post_lines = slurp.split( /(\r\n?|\n)/ ) + # initialize indices of atend bb comments to more than largest index + j = post_lines.length + j_bb = j + j_hrbb = j + j_end = j + post_length = 0 + post_lines.reverse_each do |l| + post_length += l.length + j -= 1 + next if l =~ /(\r\n?|\n)/ if l =~ BB_PAT bb_comment = l - @bb = Bb.from_bbcomment( bb_comment ) + @bb = Bb.from_comment( bb_comment ) + j_bb = j + elsif l =~ HRBB_PAT + bb_comment = l + @hrbb = HRBb.from_hrcomment( bb_comment ) + j_hrbb = j + end + if (@bb or !atend) and (@hrbb or !hr_atend) + j_end = j break - end # if + end end # do + #post_lines.slice([j_bb,j_hrbb].min .. -1).each do |l| + # post_block = post_block + l end # if atend + fail EPBBError, @path unless @bb.valid + # in case of discrepancy, drop @hrbb. + # we accept a `safety margin': a difference >1pt is not a discrepancy. + @hrbb = nil if @hrbb and + (( @bb.llx.to_i > @hrbb.llx.to_f.floor ) or + ( @bb.lly.to_i > @hrbb.lly.to_f.floor ) or + ( @bb.urx.to_i < @hrbb.urx.to_f.ceil ) or + ( @bb.ury.to_i < @hrbb.ury.to_f.ceil )) + retVal = PsPdf.new( 'ext' => 'eps' ) - if @type == 'eps' and @atfront # simple copy - ccp( @path, retVal.path ) - elsif @atfront # copy a slice - sliceFile( @path, retVal.path, ps_len, offset ) - elsif atend # relocate boundingbox - File.open( retVal.path, $W ) do |fl| - fl.write( pre_block.sub( BB_END1, bb_comment )) + + # always rewrite the eps, to get normalized, ruby-compatible bb syntax. + # we modify part of the header, and possibly of the trailer. + + # offset, length of middle part, which can be copied byte for byte + cp_start = offset + pre_length + cp_len = ps_len - pre_length + cp_len -= post_length if ( atend or hr_atend ) + # replace boundingbox comments + if atend + pre_lines[i_bb].sub!( BB_END, @bb.comment ) + post_lines[j_bb].sub!( BB_PAT, '%%' ) + else + pre_lines[i_bb].sub!( BB_PAT, @bb.comment ) + end + if @hrbb + # replace valid hires boundingbox comments + if hr_atend + pre_lines[i_hrbb].sub!( HRBB_END, @hrbb.hrcomment ) + post_lines[j_hrbb].sub!( HRBB_PAT, '%%' ) + else + pre_lines[i_hrbb].sub!( HRBB_PAT, @hrbb.hrcomment ) end - sliceFile( @path, retVal.path, - ps_len - pre_block.length - post_block.length, - offset + pre_block.length, $A ) + elsif i_hrbb # invalid hires bb + # erase invalid hr boundingbox comments + if atend + pre_lines[i_hrbb].sub!( HRBB_END, '%%' ) + post_lines[j_hrbb].sub!( HRBB_PAT, '%%' ) + else + pre_lines[i_hrbb].sub!( HRBB_PAT, '%%' ) + end + end # test for @hrbb + + File.open( retVal.path, $W ) do |fl| + fl.write( pre_lines[ 0 .. i_end ].join ) + end + sliceFile( @path, retVal.path, cp_len, cp_start, $A ) + if ( atend or hr_atend ) File.open( retVal.path, $A ) do |fl| - fl.write( post_block.sub( BB_PAT1, '%' )) + fl.write( post_lines[ j_end .. -1 ].join ) end - end # if atend + end retVal.bb = @bb.copy + retVal.hrbb = @hrbb.copy if @hrbb retVal.atfront = true + retVal.hr_atfront = true return retVal - end # eps_clean ################## # Use the Ghostscript bbox device to give an eps a tight boundingbox. - # Newer versions of Ghostscript can do that by themselves, but - # for the time being we just assume a bbox device. + # Here, we don't test for use_hires_bb. # 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. @@ -597,44 +776,74 @@ class PsPdf fail EPCallError, buginfo \ unless @type == 'eps' and @bb.non_negative - cmd = "#{short_name($settings.gs_prog)} #{PsPdf.gs_options}" + - " -sDEVICE=bbox #{short_name(@path)} 2>&1" + # let ghostscript calculate new boundingbox + cmd = $settings.gs_prog + ' ' + PsPdf.gs_options + + " -sDEVICE=bbox \"" + @path + "\" 2>&1" write_log cmd # if $DEBUG bb_output = `#{cmd}` + # inspect the result fail EPSystemError, cmd unless $? == 0 and bb_output bb_output.split(/\r\n?|\n/).each do |b| if b =~ BB_PAT - bb_temp = Bb.from_bbcomment( b ) + bb_temp = Bb.from_comment( b ) fail EPBBError, bb_output unless bb_temp.valid - bb_temp.expand + bb_temp.expand unless $settings.use_hires_bb @bb = bb_temp.copy - # write a new eps file with a new boundingbox comment - ps_len = File.size( @path ) - slurp = '' - File.open( @path, $R ) do |fl| - slurp = fl.read( [ps_len,SAFESIZE].min ) - end - lines = slurp.split( /(\r\n?|\n)/ ) - pre_block = '' - pre_length = 0 - lines.each do |l| - pre_length = pre_length + l.length - if l =~ BB_PAT - pre_block = pre_block + bb_temp.bbcomment - oldpath = @path - @path = mktemp( 'eps' ) - File.open( @path, $W ) { |fl| fl.write( pre_block ) } - sliceFile( oldpath, @path, \ - ps_len - pre_length, pre_length, $A ) - return self - else - pre_block = pre_block + l - end - end # do |l| - fail EPBBError, bb_output - end # if =~ BB_PAT + elsif b =~ HRBB_PAT + bb_temp = HRBb.from_hrcomment( b ) + @hrbb = bb_temp.valid ? bb_temp.copy : nil + end end # do |b| - fail EPBBError, bb_output + fail EPBBError, bb_output unless @bb + # this won't happen, but we deal with it anyway: + @hrbb = HRBb.new( @bb.llx, @bb.lly, @bb.urx, @bb.ury ) unless @hrbb + + # locate current [hr]boundingbox, which ha[s|ve] to be replaced + # assumptions: both in header, and hrbb no later than + # first line after bb. + slurp = '' + File.open( @path, $R ) do |fl| + slurp = fl.read( [File.size(@path),SAFESIZE].min ) + end + pre_lines = slurp.split( /(\r\n?|\n)/ ) + i_bb = -1 + i_hrbb = -1 + i = -1 + i_end = -1 + pre_length = 0 + pre_lines.each do |l| + pre_length += l.length + i += 1 + next if l =~ /(\r\n?|\n)/ + if l =~ BB_PAT + i_bb = i + elsif l =~ HRBB_PAT + i_hrbb = i + elsif i_bb >= 0 + i_end = i + break # stop looking; we expect hrbb next to bb + end # =~ BB_PAT + end # do |l,i| + fail EPBBError, "No boundingbox found in #{@path}" if i_bb < 0 + + # replace boundingbox[es] by editing initial part pre_block + # and copying the rest byte for byte + if i_hrbb < 0 + # no old hrbb; replace bb with new bb and new hrbb + # pre_lines[i_bb+1] should match /\r\n?|\n/ + pre_lines[i_bb].sub!( BB_PAT, @bb.comment + + pre_lines[i_bb+1] + @hrbb.hrcomment ) + else + pre_lines[i_bb].sub!( BB_PAT, @bb.comment ) + pre_lines[i_hrbb].sub!( HRBB_PAT, @hrbb.hrcomment ) + end + oldpath = @path + @path = mktemp( 'eps' ) + File.open( @path, $W ) { |fl| + fl.write( pre_lines[ 0 .. i_end ].join ) } + sliceFile( oldpath, @path, \ + File.size( oldpath ) - pre_length, pre_length, $A ) + return self end # fix_bb @@ -642,8 +851,6 @@ class PsPdf # Convert eps to pdf. # The eps should already have a boundingbox in the header. - # When fix_bbox, let Ghostscript calculate a tight boundingbox. - # For this, we require non-negative boundingbox coordinates. def eps_to_pdf( params={} ) @@ -651,7 +858,7 @@ class PsPdf fail EPCallError, buginfo( params ) unless @type == 'eps' and @atfront - wrp = @bb.wrapper + wrp = ( $settings.use_hires_bb and @hrbb ) ? @hrbb.wrapper : @bb.wrapper retVal = PsPdf.new( 'ext' => 'pdf' ) cmd = "\"#{$settings.gs_prog}\" #{PsPdf.gs_options}" + @@ -687,7 +894,7 @@ class PsPdf " -paper match -eps -f #{page} -l #{page}" + " \"#{@path}\" \"#{retVal.path}\"" else - cmd = "\"#{$settings.gs_prog}\" -sDEVICE=epswrite" + + cmd = "\"#{$settings.gs_prog}\" -sDEVICE=epswrite -r600" + " #{PsPdf.gs_options}" + " -dFirstPage=#{page}" + " -dLastPage=#{page}" + @@ -696,24 +903,28 @@ class PsPdf write_log cmd # if $DEBUG fail EPSystemError, cmd unless \ system( cmd ) and test( ?s, retVal.path ) + # fix for incorrect DSC header produced by some versions of pdftops: # if necessary, change line `% Produced by ...' into `%%Produced by ...' +# this is usually the second line. # otherwise the DSC header would be terminated before the bbox comment -# this bug has been reported, but not yet acted upon. -# match first 500 chars against `% Produced by' - fhead = '' +# match first chk_ze chars against `% Produced by' + chk_size = [ 1500, File.size( retVal.path ) ].min + slurp = '' File.open( retVal.path, $R ) do |fl| - fhead = fl.read( 500 ) + slurp = fl.read( chk_size ) end # File - if fhead.match( /[\r\n]% Produced by/ ) + pdfpat = /([\r\n])% Produced by/m + if slurp =~ pdfpat newpath = mktemp( 'eps' ) write_log "pdftops header fix #{retVal.path} => #{newpath}" File.open( newpath, $W ) do |fl2| - fl2.write( fhead.sub( /([\r\n])(% Produced by)/, '\1%%Produced by' ) ) + fl2.write( slurp.sub( pdfpat, '\1%% Produced by)' ) ) end - sliceFile( retVal.path, newpath, File.size( retVal.path ) - 500, 500, $A ) + sliceFile( retVal.path, newpath, File.size( retVal.path ) - chk_size, + chk_size, $A ) retVal.path = newpath - end + end # if =~ # end fix for incorrect DSC header produced by some versions of pdftops retVal.atfront = 1 retVal.find_bb_simple @@ -751,12 +962,18 @@ class PsPdf sep = params[ 'sep' ] page = params[ 'page' ] ? params[ 'page' ].to_s : nil - fail EPCallError, buginfo( params ) unless \ - $settings.pdftops_prog and $settings.use_pdftops and @type == 'pdf' + fail EPCallError, buginfo( params ) unless @type == 'pdf' retVal = PsPdf.new( 'ext' => 'ps' ) - cmd = "\"#{$settings.pdftops_prog}\" #{PsPdf.ps_options( sep )}" + - ( page ? " -f #{page} -l #{page}" : '' ) + - " -paper match \"#{@path}\" \"#{retVal.path}\"" + if $settings.pdftops_prog and $settings.use_pdftops + cmd = "\"#{$settings.pdftops_prog}\" #{PsPdf.ps_options( sep )}" + + ( page ? " -f #{page} -l #{page}" : '' ) + + " -paper match \"#{@path}\" \"#{retVal.path}\"" + else + cmd = "\"#{$settings.gs_prog}\" #{PsPdf.gs_options}" + + " -sDEVICE=pswrite -r600" + + ( page ? " -dFirstPage=#{page} -dLastPage#{page}" : '' ) + + " -sOutputFile=\"#{retVal.path}\"" + " \"#{@path}\"" + end write_log cmd # if $DEBUG fail EPSystemError, cmd unless \ system( cmd ) and test( ?s, retVal.path ) @@ -793,14 +1010,11 @@ class PsPdf # an extra roundtrip. # The separable color conversion is an option of pdftops. - gRAY = nil unless \ - ( $settings.pdftops_prog and $settings.use_pdftops ) - pp = self pp = pp.eps_clean if pp.type == 'epsPreview' pp = pp.eps_clean if pp.type == 'eps' and \ - not ( pp.bb and pp.atfront ) + not ( pp.bb and pp.atfront ) # => not yet `cleaned' pp.pdf_pages if pp.type == 'pdf' and not pp.npages case pp.type @@ -931,7 +1145,7 @@ save_settings = false opts = OptionParser.new do |opts| # for help output - opts.banner = "Epspdf 0.3.0" + opts.banner = "Epspdf 0.4.0" opts.separator "Convert between [e]ps and pdf formats" opts.separator "Usage: epspdf.rb [options] infile [outfile]" opts.separator "" @@ -941,30 +1155,68 @@ opts = OptionParser.new do |opts| " if infile is file.pdf" opts.separator "" - opts.on( "-I", - "Ignore pdftops even if available", - "(default: use if available)" ) do |opt| - $settings.ignore_pdftops = '1' + opts.on( "-g", "--gray", "--grey", + "Convert to grayscale;", + "success not guaranteed" ) do |opt| + $options.gray = true end - opts.on( "-U", - "Use pdftops if available", - "(overrides previous -I setting)" ) do |opt| - $settings.ignore_pdftops = '0' + opts.on( "-G", "--GRAY", "--GREY", + "Try harder to convert to grayscale" ) do |opt| + $options.gRAY = true + end + + opts.on( "-p PAGENUMBER", "--pagenumber=PAGENUMBER", + "Page to be converted or selected", Integer ) do |opt| + $options.page = opt + end + + opts.on( "-b", "--bbox", "--BoundingBox", + "Compute tight boundingbox" ) do |opt| + $options.bbox = true end - opts.on( "-T TARGET", "--target=TARGET", PDF_TARGETS, + opts.on( "-n", "--no-hires", + "Don't use hires boundingbox" ) do |opt| + $settings.ignore_hires_bb = '1' + end + + opts.on( "-r", "--hires", + "Use hires boundingbox" ) do |opt| + $settings.ignore_hires_bb = '0' + end + + opts.on( "-T TARGET", "--target=TARGET", + PDF_TARGETS, "Target use of pdf; one of", "#{PDF_TARGETS.join( ', ' )}" ) do |opt| $settings.pdf_target = opt end - opts.on( "-V VERSION", "--version=VERSION", PDF_VERSIONS, - "Pdf version to be generated;", - "one of #{PDF_VERSIONS.join( ', ' )}" ) do |opt| + opts.on( "-N PDFVERSION", "--pdfversion=PDFVERSION", + PDF_VERSIONS, + "Pdf version to be generated" ) do |opt| $settings.pdf_version = opt end + opts.on( "-V PDFVERSION", "--version=PDFVERSION", + PDF_VERSIONS, + "Deprecated; use `-N' or `--pdfversion'." ) do |opt| + $settings.pdf_version = opt + end + + opts.on( "-I", + "Ignore pdftops even if available", + "(default: use if available)" ) do |opt| + $settings.ignore_pdftops = '1' + end + + opts.on( "-U", + "Use pdftops if available", + "(overrides previous -I setting)" ) do |opt| + $settings.ignore_pdftops = '0' + end + opts.on( "-C CUSTOMOPTIONS", "--custom=CUSTOMOPTIONS", "Custom options for conversion to pdf,", "view Use.htm and ps2pdf.htm from", @@ -973,34 +1225,12 @@ opts = OptionParser.new do |opts| end opts.on( "-P PSOPTIONS", "--psoptions=PSOPTIONS", - "Options for pdftops; default -level2,", + "Options for pdftops; default -level3,", "don't include -eps or page number options;", "these will be generated by the program" ) do |opt| $settings.ps_options = opt end - opts.on( "-p PAGENUMBER", "--pagenumber=PAGENUMBER", - "Page to be converted or selected", Integer ) do |opt| - $options.page = opt - end - - opts.on( "-b", "--bbox", "--BoundingBox", - "Compute tight boundingbox" ) do |opt| - $options.bbox = true - end - - opts.on( "-g", "--gray", "--grey", - "Convert to grayscale;", - "success not guaranteed" ) do |opt| - $options.gray = true - end - - opts.on( "-G", "--GRAY", "--GREY", - "Try harder to convert to grayscale;", - "success still not guaranteed" ) do |opt| - $options.gRAY = true - end - opts.on( "-i", "--info", "Info: display detected filetype" ) do |opt| $options.info = true @@ -1017,15 +1247,17 @@ opts = OptionParser.new do |opts| opts.separator "" + opts.on( "-v", "Prints version info" ) do |opt| + puts EPVERSION + exit + end + opts.on_tail("-h", "--help", "Show this message") do + puts "Epspdf version " + EPVERSION + puts "Copyright (C) " + COPYRIGHT + " Siep Kroonenberg" puts opts exit end - - # opts.on_tail("--version", "Show version") do - # puts OptionParser::Version.join('.') - # exit - # end end # opts # parse options destructively diff --git a/Master/texmf-dist/scripts/epspdf/epspdfboot.rb b/Master/texmf-dist/scripts/epspdf/epspdfboot.rb deleted file mode 100644 index bdcebb77b26..00000000000 --- a/Master/texmf-dist/scripts/epspdf/epspdfboot.rb +++ /dev/null @@ -1,40 +0,0 @@ -# Manipulate library path and maybe search path. - -# Problem to be solved: - -# We don't want to require Windows users to install Ruby and Tcl/Tk so -# we offer a package with a built-in Ruby subset. This subset is -# slightly modified from the subset created by rubyscript2exe -# (http://www.erikveen.dds.nl/rubyscript2exe/). - -# The rubyscript2exe directory structure is simplified compared to the -# original Ruby directory structure, so we need to modify the library -# search path and also the general search path. - -# Assumptions: - -# - The script itself is at the root or in a toplevel subdirectory. -# - If an rbconfig.rb is found at the root or in -# a toplevel directory then there is a Ruby subset. -# If that is the Ruby which is used -# then it needs help to find its files. - -dir = File.expand_path( File.dirname( __FILE__ ) ) -parent = File.dirname( dir ) -parent_sl = parent.dup + ( parent =~ /[\\\/]$/ ? '' : '/' ) -dir_sl = dir.dup + ( parent =~ /[\\\/]$/ ? '' : '/' ) -spath = ENV['PATH'].dup - -if test( ?e, parent_sl + 'lib/rbconfig.rb' ) - root_sl = parent_sl -elsif test( ?e, dir_sl + 'lib/rbconfig.rb' ) - root_sl = dir_sl -else - root_sl = nil -end - -if root_sl - $: << "#{root_sl}lib" -end - -$:.unshift( dir ) diff --git a/Master/texmf-dist/scripts/epspdf/epspdfrc.rb b/Master/texmf-dist/scripts/epspdf/epspdfrc.rb index aa64309a822..acdbd9ac5e4 100755 --- a/Master/texmf-dist/scripts/epspdf/epspdfrc.rb +++ b/Master/texmf-dist/scripts/epspdf/epspdfrc.rb @@ -1,20 +1,16 @@ +EPVERSION = '0.4.0' +COPYRIGHT = '2006, 2008, 2009' + # epspdf conversion utility, configuration module ##### -# Copyright (C) 2006, 2008 Siep Kroonenberg +# Copyright (C) 2006, 2008, 2009 Siep Kroonenberg # n dot s dot kroonenberg at rug 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! ##### -# This version has been adapted for TeX Live. The adaptations only -# concern Windows. If TL is set then pdftops and -# ghostscript are set to the version included in TeXLive, settings are -# stored in HKCU\software\epspdftl (rather than epspdf) and the logfile -# is %USERPROFILE%\epspdftl.log, rather than epspdf.log. -# A future version should offer a dynamic way of settingt his option. - # Settings are stored in a settings hash, with some methods added # for less unwieldy notation. # @@ -43,112 +39,145 @@ else 'unix' end -TL = ENV.has_key?('TLROOT') if ARCH == 'w32' + # registry and API calls require 'dl/win32' require 'win32/registry' include Win32 # this lets us abbreviate Win32::Registry to Registry + # lay groundwork for short_name function ShortPName = Win32API.new( 'kernel32', 'GetShortPathName', ['P','P','N'], 'N' ) - TLROOT = ENV['TLROOT'] + # shellopen function + ShellEx = Win32API.new( + 'shell32','ShellExecute', ['L','P','P','P','P','L'], 'L' ) + #FindEx = Win32API.new( + # 'shell32','FindExecutable', ['P','P','P'], 'L' ) + # from winuser.h: + SW_SHOWNORMAL = 1 end -# for Windows, use short names within backquotes -# to avoid quotes within backquotes - +# need short_name function to get around limitations of w2k quoting def short_name( fn ) + #puts "Calculate short name for #{fn}\n" return fn if ARCH != 'w32' - fn.gsub!( /\\/, '/' ) + fn.gsub!( /\//, "\\" ) buffer = ' ' * 260 length = ShortPName.call( fn, buffer, buffer.size ) - return ( length > 0 ) ? buffer.slice(0..length-1) : fn + fn = buffer.slice(0..length-1) if length > 0 + fn.gsub!( /\\/, '/' ) + return fn +end + +def shell_open( fn ) + return nil if ARCH != 'w32' + result = ShellEx.call( 0, 0, fn, 0, 0, SW_SHOWNORMAL ) end +def shell_open_with( fn ) + return nil if ARCH != 'w32' + result = ShellEx.call( 0, 0, "RUNDLL32.EXE", "shell32.dll,OpenAs_RunDLL #{fn}", + 0, SW_SHOWNORMAL ) +end + +# interpret error value for shell_open +# values <=32 indicate error +# constants defined in winerror.h, shellapi.h + +def shell_error_string ( e ) + if e > 32 + returl nil + else + return case e + when 0: "Out of memory or resources" + #define ERROR_FILE_NOT_FOUND 2L + #define SE_ERR_FNF 2 + when 2: "File not found" + #define ERROR_PATH_NOT_FOUND 3L + #define SE_ERR_PNF 3 + when 3: "Path not found" + #define SE_ERR_ACCESSDENIED 5 + when 5: "Access denied" + #define SE_ERR_OOM 8 + when 8: "Not enough memory" + #define ERROR_BAD_FORMAT 11L + when 11: "Invalid exe" + #define SE_ERR_SHARE 26 + when 26: "Sharing violation" + #define SE_ERR_ASSOCINCOMPLETE 27 + when 27: "Invalid file association" + #define SE_ERR_DDETIMEOUT 28 + when 28: "DDE timeout" + #define SE_ERR_DDEFAIL 29 + when 29: "DDE fail" + #define SE_ERR_DDEBUSY 30 + when 30: "DDE busy" + #define SE_ERR_NOASSOC 31 + when 31: "No file association" + #define SE_ERR_DLLNOTFOUND 32 + when 32: "DLL not found" + else "Unspecified error" + end # case + end # else +end # def + # system-dependent location of logfile LOGFILE = case ARCH when 'w32' - TL ? "#{ENV['USERPROFILE']}\\epspdftl.log" : "#{ENV['USERPROFILE']}\\epspdf.log" else - "#{ENV['HOME']}//epspdf.log" + "#{ENV['HOME']}/epspdf.log" end LOGFILE_OLD = LOGFILE + '.old' # system-dependent locations of saved settings -RC_FILE = "#{ENV['HOME']}/.epspdfrc" -REG_K = TL ? 'epspdftl' : 'epspdf' -REG_KEY = TL ? 'SOFTWARE\\epspdftl' : 'SOFTWARE\\epspdf' +RC_FILE = "#{ENV['HOME']}/.epspdfrc" # not used under Windows +REG_K = 'epspdf' +REG_KEY = 'SOFTWARE\\epspdf' # hash of saved settings from rc file or registry $rc_settings = {} -# find open command for extension (w32 only) - -def open_with( ext ) - ext = '' unless ext - answer = nil - if ARCH == 'w32' - begin # open cl = HKCR\ext safely - Registry::HKEY_CLASSES_ROOT.open( - (ext =~ /^\./ ? ext : '.'+ext) ) do |cl| - begin # read ftype safely - ftype = cl.read_s( nil ) - begin # open cmd = HKCR\ftype\shell... safely - Registry::HKEY_CLASSES_ROOT.open( - ftype + '\\shell\\open\\command' ) do |cmd| - begin # read answer safely - answer = cmd.read_s( nil ) - if answer =~ /^"([^"]+)".*/ - answer = $1 - elsif answer =~ /^([^ ]+) .*/ - answer = $1 - end - answer = nil unless answer and test( ?s, answer ) - rescue # answer not read - end # read answer safely - end # HKCR\ftype\shell... do |cmd| - rescue # cmd not opened - end # open cmd = HKCR\ftype\shell... safely - rescue # ftype not read - end # read ftype safely - end # HKCR\ext do |cl| - rescue # cl not opened - end # open cl = HKCR\ext safely - end # w32 - answer -end - # test whether a string is a valid program call # 'which' also works with explicit paths. # 'which' returns an error for non-executable files. +# Linux/Unix OS X: for testing existence of gs, pdftops and ps/pdf viewers +# Mac OS X: for testing existence of gs and pdftops +# Windows: function not usable and not used def is_a_program( s ) case ARCH when 'w32' nil else - ( system "which #{s} >/dev/null" ) ? 1 : nil + ( system "which #{s} >/dev/null 2>&1" ) ? 1 : nil end end # is_a_program -# Parse version info from a prog from the xpdf suite. -# This is used as validity test. - -def xpdf_version( arg ) - retval = nil - arg = short_name( arg ) if ARCH == 'w32' - version_output = `#{arg} -v 2>&1` - if version_output and not version_output.empty? - vo = version_output.split(/\r\n?|\n/) - vo.grep(/version/i) { |a| - a =~ /^\D*(\d+\.\w*)\b/ - retval = $& # matched part; nil if no match - } +# for Windows, use the registry or the following function instead: + +def find_on_path( s ) + # for now, Windows-only; parameter s with or without path + if ARCH != 'w32' + return nil end - retval + ENV['PATH'].gsub( "\\", "/" ).split( /;/ ).each do |d| + d = File.expand_path( d ) # this seems a syntactic operation + s_full = d.sub( /\/$/, '' ) + '/' + s + if test( ?d, d ) and test( ?f, s_full ) + return s_full + end + ENV['PATHEXT'].split( /;/ ).each do |e| + # components of PATHEXT should include leading dot + s_full = d.sub( /\/$/, '' ) + '/' + s + e + if test( ?f, s_full ) + return s_full + end + end # do |e| + end # do |d| + return nil end class Setting @@ -167,30 +196,56 @@ PDF_VERSIONS = [ '1.2', '1.3', '1.4', 'default' ] $settings = { # converters + # ghostscript: under windows it must be told about its directories, + # so we don't want users to mess with it. + # If they really want to, they can manually edit PATH and set GS_LIB 'gs_prog' => Setting.new( nil, 'auto', nil ), - 'gs_version' => Setting.new( nil, 'auto', nil ), + #'gs_version' => Setting.new( nil, 'auto', nil ), 'pdftops_prog' => Setting.new( nil, ARCH=='w32' ? 'config' : 'auto', nil ), - # viewers + # epspdftk: viewers 'pdf_viewers' => Setting.new( nil, ARCH=='unix' ? 'auto' : nil, nil ), 'ps_viewers' => Setting.new( nil, ARCH=='unix' ? 'auto' : nil, nil ), 'pdf_viewer' => Setting.new( nil, ARCH=='unix' ? 'config' : 'auto', nil ), 'ps_viewer' => Setting.new( nil, ARCH=='unix' ? 'config' : 'auto', nil ), + # epspdftk: initial dir + 'defaultDir' => Setting.new( nil, 'config', nil ), # conversion options 'ignore_pdftops' => Setting.new( '0', 'config', 'Ignore pdftops even if available; 1=yes, empty or 0=no(default)' ), 'pdf_target' => Setting.new( 'prepress', 'config', 'Target: screen, ebook, print, prepress (default) or default' ), - 'pdf_version' => Setting.new( '1.4', 'config', - 'Pdf version: e.g 1.4 or 1.2 (fewer features, more compatibility)' + - ' or unspecified' ), + 'pdf_version' => Setting.new( 'default', 'config', + 'Pdf version: e.g 1.4 or 1.2 or default' ), 'pdf_custom' => Setting.new( '', 'config', 'additional options for [e]ps to pdf conversion' ), - 'ps_options' => Setting.new( '-level2', 'config', - 'options for pdftops; default -level2' ), + 'ps_options' => Setting.new( '-level3', 'config', + 'options for pdftops; default -level3' ), + 'ignore_hires_bb' => Setting.new( '0', 'config', + 'Ignore hi-res boundingbox for pdf generation;' + + ' 1=yes, empty or 0=no(default)' ), 'bb_spread' => Setting.new( '1', 'config', - 'margin to be added to computed boundingbox; default 1' ) + 'margin to be added to computed lores boundingbox; default 1' ) } +# Using pdftops or a hires boundingbox should be the norm, not the exception. +# We emphasize this by naming the corresponding options ignore_pdftops and +# ignore_hires_bb, and letting them default to false. + +def reverse_of( x ) + case x + when /t(rue)?|y(es)?|1/i + false + when /f(alse)?|n(o)?|0/i + true + when 1 + false + when 0 + true + else + true + end +end + class << $settings # create shortcut methods $settings.x and $settings.x= @@ -201,47 +256,15 @@ class << $settings eval "def #{k}=(v) ; self[\'#{k}\'].val=v ; end" } - # use_pdftops is a boolean counterpart and inverse - # of the stored string attribute ignore_pdftops + # use_pdftops and use_hires_bb are a boolean counterparts and inverses + # of the stored string attributes ignore_pdftops and ignore_hires_bb - def use_pdftops - case self['ignore_pdftops'].val - when /t(rue)?|y(es)?|1/i - false - when /f(alse)?|n(o)?|0/i - true - when 1 - false - when 0 - true - else - true - end + def use_hires_bb + reverse_of( self['ignore_hires_bb'].val ) end - # we want use_pdftops = nil = default = true but - # we also want use_pdftops = nil <=> use_pdftops = false - # we give the second one priority. - - def use_pdftops=( x ) - self['ignore_pdftops'].val = case x - when /t(rue)?|y(es)?|1/i - '0' - when /f(alse)?|n(o)?|0/i - '1' - when 1 - '0' - when 0 - '1' - when false - '1' - when nil # try to avoid this assignment - '1' - when true - '0' - else - '1' - end + def use_pdftops + reverse_of( self['ignore_pdftops'].val ) end def accept_pdf_viewer( s ) @@ -337,7 +360,7 @@ class << $settings end else File.open( RC_FILE, 'w' ) do |f| - f.write( "# This file will be overwritten by epspdf_tk[.rb]" \ + f.write( "# This file will be overwritten by epspdf[tk][.rb]" \ + $/ ) $settings.each do |key, st| if st.type == 'config' @@ -355,26 +378,40 @@ class << $settings def get_settings # phase 1: autodetect - # w32: gs_prog, gs_version, pdf_viewer, ps_viewer - # gs: search registry settings; the highest version wins. - # viewers: check file type registry entry for valid open commands - # w32/TL: gs_prog, gs_version, pdf_viewer, ps_viewer - # gs: from TL - # viewers: check file type registry entry for valid open commands - # unix: gs_prog, gs_version, pdftops_prog, pdf_viewers, ps_viewers - # osx: gs_prog, gs_version, pdftops_prog + # w32: gs_prog, pdf_viewer, ps_viewer + # gs: search path; search registry settings (user and system); + # search tex installation. The first one wins. + # viewers: check file type registry entry for valid open commands + # unix: gs_prog, pdftops_prog, pdf_viewers, ps_viewers + # osx: gs_prog, pdftops_prog # viewers: use Preview; don't check anything # unix/osx are done together, except that for osx # the viewers code is skipped. + # Windows: test for TeX, because it may come with a hidden + # ghostscript. TeX may also come with pdftops.exe, but pdftops + # would then be on the searchpath and not require special treatment. + + texbindir = ( ARCH == 'w32' ) ? find_on_path( 'tex' ) : nil + texbindir = short_name( File.dirname( texbindir ) ) if texbindir + case ARCH when 'w32' - if !TL then # else use built-in texlive versions - # check registry for latest valid version of Ghostscript - # tentative values; HKLM and HKCU may provide different values - try_gs_version = nil + # ghostscript: + # 1. check searchpath + # 2. check registry for latest valid version of Ghostscript + # 3. check tex installation + # TeX Live- and MikTeX versions need environment variables + # no need to remember: it will be the last thing we try + # searchpath + self.gs_prog = find_on_path( 'gswin32c' ) + + if not self.gs_prog + try_gs_version = 0 try_gs_prog = nil + this_gs_prog = nil + # tentative values; HKLM and HKCU may provide different values [Registry::HKEY_LOCAL_MACHINE, Registry::HKEY_CURRENT_USER].each { |hk| puts hk.to_s if $DEBUG @@ -384,55 +421,75 @@ class << $settings puts key.to_s if $DEBUG if key =~ /ghostscript/i gh = hk.open('SOFTWARE\\' + key) - gh.each_key { |skey, wtime| - if skey =~ /^\d+\.\d+$/ # version number - if skey.to_f > try_gs_version.to_f - this_gs = hk.open('SOFTWARE\\' + key + '\\' + skey) + gh.each_key { |this_gs_version, wtime| + if this_gs_version =~ /^\d+\.\d+/ # version number + if this_gs_version.to_f > try_gs_version.to_f + this_gs = hk.open('SOFTWARE\\' + key + + '\\' + this_gs_version) this_gs_prog = this_gs['GS_DLL'].sub( /([\\\/])([^\\\/]+)$/, '\1gswin32c.exe') if test(?e,this_gs_prog) - try_gs_version = skey + try_gs_version = this_gs_version try_gs_prog = this_gs_prog end # if ?e (exist) - end # skey.to_f > try_gs_version.to_f - end # if skey =~ /^\d+\.\d+$/ + end # this_gs_version.to_f > try_gs_version.to_f + end # if this_gs_version =~ /^\d+\.\d+$/ } # gh.each_key end # if key =~ /ghostscript/i } # sof.each_key } # hk.open |sof| } # [HKCU, HKLM].each - self.gs_version = try_gs_version - self.gs_prog = try_gs_prog - end - - # viewers - self.pdf_viewer = open_with( 'pdf' ) - self.ps_viewer = open_with( 'ps' ) + self.gs_prog = this_gs_prog if this_gs_prog + end # if not self.gs_prog + if texbindir and ! self.gs_prog + # TeX Live >= 2008: + # hidden gs in texbindir/../../tlpkg/tlgs + texroot = File.dirname( File.dirname( File.expand_path( texbindir ))) + gsroot = texroot + '/tlpkg/tlgs' + this_gs_prog = gsroot + '/bin/gswin32c.exe' + if test( ?f, this_gs_prog ) + self.gs_prog = this_gs_prog + ENV['GS_LIB'] = "#{gsroot}/lib;#{gsroot}/fonts;#{gsroot}/Resource" + else # test for MikTeX hidden ghostscript + # http://blog.miktex.org/post/2005/04/ + # Starting-mgsexe-at-the-DOS-Prompt.aspx + # That's because mgs.exe doesn't use the original registry keys + # and environment variables. For example, mgs.exe queries + # MIKTEX_GS_LIB instead of GS_LIB. You can start mgs.exe + # at the DOS-prompt if you set MIKTEX_GS_LIB as follows: + # MIKTEX_GS_LIB=C:\texmf\ghostscript\base;C:\texmf\fonts + this_gs_prog = texbindir + '/mgs.exe' + if test( ?f, this_gs_prog ) and + test( ?d, "#{texroot}/ghostscript/base" ) + self.gs_prog = this_gs_prog + ENV['MIKTEX_GS_LIB'] = + "#{texroot}/ghostscript/base;#{texroot}/fonts" + end # ?f, this_gs_prog + end # try MikTeX hidden Ghostscript + end # texbindir and ! self.gs_prog + fail( "No ghostscript" ) unless self.gs_prog + self.gs_prog = short_name( self.gs_prog ) + + # pdftops: just check the searchpath + self.pdftops_prog = find_on_path( 'pdftops' ) when /unix|osx/ # gs - answer = `gs --version` - if answer and not answer.empty? - self.gs_prog = 'gs' - self.gs_version = answer.chomp - else - self.gs_prog = nil - self.gs_version = nil - end + self.gs_prog = is_a_program( 'gs' ) ? 'gs' : nil # pdftops - self.pdftops_prog = xpdf_version( 'pdftops' ) ? 'pdftops' : nil + self.pdftops_prog = is_a_program( 'pdftops' ) ? 'pdftops' : nil # viewers if ARCH == 'unix' - vw = %w{ gpdf xpdf acroread evince ggv gv kghostview } + vw = %w{ evince gpdf xpdf acroread epdfview gv gsview okular } vw.delete_if { |x| not is_a_program( x ) } vw = nil if vw.empty? self.pdf_viewers = vw self.pdf_viewer = vw ? vw[0] : nil if vw vw2 = vw.dup # copy the elements rather than the array ref - [ 'gpdf', 'xpdf', 'acroread' ].each { |pg| + [ 'gpdf', 'xpdf', 'acroread', 'epdfview' ].each { |pg| vw2.delete( pg ) if vw2.index( pg ) } vw2 = nil if vw2.empty? @@ -440,7 +497,8 @@ class << $settings self.ps_viewer = vw2 ? vw2[ 0 ] : nil end # if vw end # if unix - end # case + + end # case ARCH # built-in defaults already set during initialization of $settings @@ -452,21 +510,10 @@ class << $settings case ARCH when 'w32' - $rc_settings[ 'pdftops_prog' ] = ( - TL ? (TLROOT.gsub( /\//, '\\' ) + '\\bin\\win32\\pdftops.exe') : - "z:\\aps\\staff\\ber\\miktex\\xpdf\\pdftops.exe" ) unless - ( $rc_settings[ 'pdftops_prog' ] and - ! $rc_settings[ 'pdftops_prog' ].empty? ) - ptop = $rc_settings[ 'pdftops_prog' ] - if xpdf_version( ptop ) then - self.pdftops_prog = ptop - end - if TL - self.gs_prog = TLROOT.gsub( /\//, '\\' ) + - '\\tlpkg\\tlgs\\bin\\gswin32c.exe' - self.gs_version = - ( `#{short_name( self.gs_prog )} --version` ).chomp - end + self.pdftops_prog = $rc_settings[ 'pdftops_prog' ] if + $rc_settings[ 'pdftops_prog' ] and + ! $rc_settings[ 'pdftops_prog' ].empty? and + test( ?f, $rc_settings[ 'pdftops_prog' ] ) when 'unix' if v = $rc_settings[ 'pdf_viewer' ] and is_a_program( v ) self.pdf_viewer = v @@ -482,22 +529,44 @@ class << $settings end # if end # case ARCH + if ( $rc_settings[ 'defaultDir' ] and + ( not $rc_settings[ 'defaultDir' ].strip.empty? ) and + test( ?d, $rc_settings[ 'defaultDir' ] ) ) + self.defaultDir = File.expand_path( $rc_settings[ 'defaultDir' ] ) + else + self.defaultDir = Dir.getwd + end + #print "defaultDir set to #{self.defaultDir} by epspdfrc.getsettings" + + # no validity checks for pdf- and ps output options. # reminder: self.s shortcut for self['s'].val [ 'pdf_target', 'pdf_version', 'pdf_custom', 'ps_options' ].each { |p| self[ p ].val = $rc_settings[ p ] if $rc_settings[ p ] } if $rc_settings.has_key?( 'ignore_pdftops' ) - $settings.ignore_pdftops = case $rc_settings[ 'ignore_pdftops' ] + self.ignore_pdftops = case $rc_settings[ 'ignore_pdftops' ] + when /1|yes|true|y|t/i + '1' + when /0|no|false|n|f/i + '0' + end + else + self.ignore_pdftops = '0' + end + + if $rc_settings.has_key?( 'ignore_hires_bb' ) + self.ignore_hires_bb = case $rc_settings[ 'ignore_hires_bb' ] when /1|yes|true|y|t/i '1' when /0|no|false|n|f/i '0' end else - $settings.ignore_pdftops = '0' + self.ignore_hires_bb = '0' end + # bb_spread has an effect only if ignore_hires_bb self.bb_spread = $rc_settings[ 'bb_spread' ] if $rc_settings.has_key?( 'bb_spread' ) and $rc_settings[ 'bb_spread' ] =~ /^[+-]?[\d]+$/ diff --git a/Master/texmf-dist/scripts/epspdf/epspdftk b/Master/texmf-dist/scripts/epspdf/epspdftk index 6d3465a8c4b..95c551cf8c1 100755 --- a/Master/texmf-dist/scripts/epspdf/epspdftk +++ b/Master/texmf-dist/scripts/epspdf/epspdftk @@ -3,5 +3,5 @@ script=`kpsewhich -format=texmfscripts epspdftk.rb` if test "x$1" = "x"; then "$script" $HOME else - "$script" $* + "$script" "$@" fi
\ No newline at end of file diff --git a/Master/texmf-dist/scripts/epspdf/epspdftk.rb b/Master/texmf-dist/scripts/epspdf/epspdftk.rb index 356ec78d620..79dbdd5d325 100755 --- a/Master/texmf-dist/scripts/epspdf/epspdftk.rb +++ b/Master/texmf-dist/scripts/epspdf/epspdftk.rb @@ -3,7 +3,7 @@ # epspdf conversion utility, GUI frontend ##### -# Copyright (C) 2006, 2008 Siep Kroonenberg +# Copyright (C) 2006, 2008, 2009 Siep Kroonenberg # n dot s dot kroonenberg at rug dot nl # # This program is free software, licensed under the GNU GPL, >=2.0. @@ -12,10 +12,8 @@ #$DEBUG=1 -# epspdfboot.rb: modify loadpath in a way which works both -# with a full Ruby and with a custom subset ruby - -require __FILE__.sub( /epspdftk\.rbw?/, 'epspdfboot.rb' ) +# find other pieces of epspdf +$:.unshift( File.dirname( __FILE__ ) ) ########################################## @@ -31,25 +29,21 @@ $VERBOSE = nil if $DEBUG puts ARCH puts Config::CONFIG['arch'] - puts "Ghostscript command #{$settings.gs_prog}" \ - " and version #{$settings.gs_version}" - puts "Pdftops command #{$settings.pdftops_prog}" \ - " and version #{$settings.pdftops_version}" + puts "Ghostscript command #{$settings.gs_prog}" + puts "Pdftops command #{$settings.pdftops_prog}" end +# is there a viewer for the PsPdf object p? + def viewable( p ) return nil unless p && p.path return nil if p.type == 'other' case ARCH - when 'osx' - return true - when 'w32' - return nil if p.type == 'epsPreview' - return open_with( File.extname( p.path ) ) - else - return nil if p.type == 'epsPreview' + when 'unix' return true if p.type =~ /e?ps/ and ( not $settings.ps_viewer.empty? ) return true if p.type == 'pdf' and ( not $settings.pdf_viewer.empty? ) + else + return true end # case ARCH return nil end # viewable @@ -79,6 +73,8 @@ static unsigned char dwnarrow.icn_bits[] = { 0xc0, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 }; STR +BG = 'gray85' + class TkACombobox < TkFrame include TkComposite @@ -320,7 +316,7 @@ if not $settings.gs_prog abort end -if ARCH != 'osx' +if ARCH == 'unix' if not $settings.pdf_viewer Tk.messageBox( :icon=>'warning', :type=>'ok', :message => "No PostScript- or pdf viewer found" ) @@ -332,14 +328,17 @@ end ######################################## -$defaultDir = ENV['PWD'] +# defaultDir already initialized by epspdfrc -$defaultDir = ARGV[0] if ((ARGV.length > 0) and File.directory?(ARGV[0])) +if ((ARGV.length > 0) and (ARGV[0].length > 0) and File.directory?(ARGV[0])) + $settings.defaultDir = ARGV[0] + #print "Default dir set to #{ARGV[0]} by epspdftk" +end $source = PsPdf.new # currently loaded PsPdf object $tl_config = nil # configuration screen -$tl_wait = nil # instead of a wait cursor or progress bar +# $tl_wait = nil # instead of a wait cursor or progress bar # widgets which are configured on the fly, # e.g. by ep_config, get a global name. @@ -347,6 +346,7 @@ $tl_wait = nil # instead of a wait cursor or progress bar # buttons and option widgets $view_button = nil +$view_with_button = nil $page_radio = nil $page_entry = nil $type_radio = nil @@ -366,6 +366,8 @@ def set_widget_states $view_button.configure( :state => ( viewable( $source ) ? 'normal' : 'disabled' ) ) + $view_with_button.configure( :state => + ( viewable( $source ) ? 'normal' : 'disabled' ) ) if ARCH == 'w32' $save_button.configure( :state => ( ( $source.type and $source.type != 'other' ) ? \ 'normal' : 'disabled' ) ) @@ -431,16 +433,20 @@ def ep_config if not $tl_config - $tl_config = TkToplevel.new( $root, :title => 'Configure epspdf' ) + $tl_config = TkToplevel.new( $root, + :title => 'Configure epspdf' ) ### viewers frame if ARCH == 'unix' TkFrame.new( $tl_config ) { |frm| frm.borderwidth 2 - frm.relief 'groove' - TkLabel.new( frm, :text => 'VIEWERS' - ).pack( :side => 'top', :padx => 4, :anchor => 'w' ) + frm.relief 'sunken' + TkLabel.new( frm ) do + text 'Viewers' + font.weight 'bold' + pack( :side => 'top', :padx => 8, :anchor => 'w' ) + end TkFrame.new( frm ) { |fm| TkLabel.new( fm, :text => 'Pdf viewer' ).grid( :in => fm, :row => 0, :column => 0, @@ -461,13 +467,57 @@ def ep_config }.pack( FRM_PACK ) end # if unix + ### pdf frame + + TkFrame.new( $tl_config ){ |frm| + frm.borderwidth 2 + frm.relief 'sunken' + TkLabel.new( frm ) do + text 'Conversion to pdf' + font.weight 'bold' + pack( :side => 'top', :padx => 4, :anchor => 'w' ) + end + + # internal packing + pack_pdf = { + :side => 'left', :padx => 2, :pady => 4, :anchor => 'w' } + + TkLabel.new( frm, :text => 'Target use' + ).pack( :side => 'top', :padx => 2, :pady => 0, :anchor => 'w' ) + $pdf_target_radio = TkRadios.new( frm, + 'values' => PDF_TARGETS, 'packing' => pack_pdf + ).pack( :side => 'top', :anchor => 'w' ) + + # dummy widget for extra vertical space + TkLabel.new( frm, :text => ' ' ).pack + + TkLabel.new( frm, :text => 'Pdf version' + ).pack( :side => 'top', :padx => 2, :pady => 0, :anchor => 'w' ) + + $pdf_version_radio = TkRadios.new( frm, + 'values' => PDF_VERSIONS, 'packing' => pack_pdf + ).pack( :side => 'top', :anchor => 'w' ) + + TkLabel.new( frm, :text => ' ' ).pack + + TkLabel.new( frm, :text => 'Custom Ghostscript/ps2pdf parameters' + ).pack( :side => 'top', :padx => 2, :pady => 0, :anchor => 'w' ) + + $pdf_custom_entry = TkEntry.new( frm, :width => 50 + ).pack( :side => 'top', :padx => 2, :pady => 2, :anchor => 'w' ) + + }.pack( FRM_PACK ) + ### postscript frame TkFrame.new( $tl_config ){ |frm| frm.borderwidth 2 - frm.relief 'groove' - TkLabel.new( frm, :text => 'POSTSCRIPT CONVERSION' - ).pack( :side => 'top', :padx => 4, :anchor => 'w' ) + frm.relief 'sunken' + TkLabel.new( frm ) do + text 'Conversion to EPS and PostScript' + font.weight 'bold' + pack( :side => 'top', :padx => 4, :anchor => 'w' ) + end if ARCH == 'w32' # others: assume pdftops on search path TkLabel.new( frm, :text => "Find pdftops" @@ -484,17 +534,11 @@ def ep_config :parent => fm ) if tryprog and ( not tryprog.empty? ) - if not xpdf_version( tryprog ) - Tk.messageBox( - :icon => 'warning', - :type => 'ok', - :parent => fm, - :message => "pdftops missing or dysfunctional" - ) - else + tryprog.sub!( /([\/\\])[^\/\\]+$/, '\1pdftops.exe' ) + if test( ?f, tryprog ) $pdftops_entry.delete( 0, 'end' ) $pdftops_entry.insert( 'end', tryprog ) - end # ifthenelse xpdf_version + end end # ifthenelse tryprog } # command lambda ).pack( :side => 'left' ) # TkButton @@ -502,52 +546,37 @@ def ep_config end # ARCH == 'w32' $pdftops_check = TkACheck.new( frm, - 'text' => "Use pdftops if available" + 'text' => "Use pdftops if available (recommended)" ).pack( :side => 'top', :pady => 4, :anchor => 'w' ) }.pack( FRM_PACK ) # TkFrame |frm| - ### pdf frame + ### boundingbox frame TkFrame.new( $tl_config ){ |frm| frm.borderwidth 2 - frm.relief 'groove' - TkLabel.new( frm, :text => 'PDF CONVERSION' - ).pack( :side => 'top', :padx => 4, :anchor => 'w' ) - - # internal packing - pack_pdf = { - :side => 'left', :padx => 2, :pady => 4, :anchor => 'w' } - - TkLabel.new( frm, :text => 'Intended use' - ).pack( :side => 'top', :padx => 2, :pady => 0, :anchor => 'w' ) - $pdf_target_radio = TkRadios.new( frm, - 'values' => PDF_TARGETS, 'packing' => pack_pdf - ).pack( :side => 'top', :anchor => 'w' ) - - # dummy widget for extra vertical space - TkLabel.new( frm, :text => ' ' ).pack - - TkLabel.new( frm, :text => 'Pdf version' - ).pack( :side => 'top', :padx => 2, :pady => 0, :anchor => 'w' ) - - $pdf_version_radio = TkRadios.new( frm, - 'values' => PDF_VERSIONS, 'packing' => pack_pdf - ).pack( :side => 'top', :anchor => 'w' ) - - TkLabel.new( frm, :text => ' ' ).pack + frm.relief 'sunken' + TkLabel.new( frm ) do + text 'Hires BoundingBox' + font.weight 'bold' + pack( :side => 'top', :padx => 4, :anchor => 'w' ) + end - TkLabel.new( frm, :text => 'Custom Ghostscript/ps2pdf parameters' - ).pack( :side => 'top', :padx => 2, :pady => 0, :anchor => 'w' ) + TkLabel.new( frm ) do + text "Uncheck to prevent clipping" + pack( :side => 'top', :padx => 4, :anchor => 'w' ) + end - $pdf_custom_entry = TkEntry.new( frm, :width => 50 - ).pack( :side => 'top', :padx => 2, :pady => 2, :anchor => 'w' ) + $hires_check = TkACheck.new( frm, + 'text' => "Use hires boundingbox if possible" + ).pack( :side => 'top', :pady => 4, :anchor => 'w' ) - }.pack( FRM_PACK ) + }.pack( FRM_PACK ) # TkFrame |frm| ### button frame TkFrame.new( $tl_config ) { |frm| + #frm.bg = BG TkButton.new( frm, :text => 'Cancel', @@ -568,7 +597,8 @@ def ep_config when 'w32' $settings.pdftops_prog = $pdftops_entry.get end - $settings.use_pdftops = ( not $pdftops_check.value == '0' ) + $settings.ignore_hires_bb = ( $hires_check.value == '0' ) ? '1' : '0' + $settings.ignore_pdftops = ( $pdftops_check.value == '0' ) ? '1' : '0' $settings.pdf_target = $pdf_target_radio.value $settings.pdf_version = $pdf_version_radio.value $settings.pdf_custom = $pdf_custom_entry.get @@ -595,6 +625,7 @@ def ep_config $pdftops_entry.insert( 'end', $settings.pdftops_prog ) \ if $settings.pdftops_prog and not $settings.pdftops_prog.empty? end # ifthenelse ARCH + $hires_check.value = $settings.use_hires_bb ? '1' : '0' $pdftops_check.value = $settings.use_pdftops ? '1' : '0' $pdf_target_radio.value = $settings.pdf_target $pdf_version_radio.value = $settings.pdf_version @@ -658,6 +689,39 @@ TkFrame.new( $root ) { |frm| :command => lambda { ep_config } ).pack( :side => 'left', :padx => 4, :pady => 4 ) + # There is pdf- and html help, and it would be simple enough to invoke + # either file from here, but for TL this would be in an entirely + # different directory, so instead we display an ascified helpfile + # packaged in the same directory as the code. + +# TkButton.new( frm, +# :text => 'Help', +# :command => lambda { +# docdir = File.dirname( __FILE__ ) +# docdir.sub!( /[\\\/]$/, '' ) +# docdir = docdir + '/doc' +# if ARCH == 'w32' +# result = shell_open( docdir + "/epspdf_UG.html" ) +# Tk.messageBox( +# 'icon' => 'error', +# 'title' => 'ShellExecute error', +# 'message' => shell_error_string( result ) +# ) if result <= 32 +# elsif ARCH == 'osx' +# fork { exec "open \"#{docdir}/epspdf_UG.html\"" } +# elsif $settings.pdf_viewer +# fork { exec "\"#{$settings.pdf_viewer}\" \"#{docdir}/epspdf.pdf\"" } +# else +# Tk.messageBox( +# 'icon' => 'warning', +# 'title' => 'No help viewer', +# 'message' => "Please select a pdf viewer in the configuration screen" + +# "\nand restart this program." +# ) +# end +# } +# ).pack( :side => 'right', :padx => 4, :pady => 4) + TkButton.new( frm, :text => 'Help', :command => lambda { ep_help } @@ -671,7 +735,7 @@ TkFrame.new( $root ) { |frm| TkFrame.new( $root ){ |frm| frm.borderwidth 2 - frm.relief 'groove' + frm.relief 'sunken' ### status info @@ -803,6 +867,13 @@ TkFrame.new( $root ) { |frm| ######################################## +### wait message packed at bottom + +$cnv_label = TkLabel.new( $root, 'text' => '' +).pack( 'side' => 'bottom', 'fill' => 'x', :anchor => 'nw' ) + +######################################## + ### view | open | save | done buttons TkFrame.new( $root ) { |frm| @@ -813,12 +884,14 @@ TkFrame.new( $root ) { |frm| :command => lambda { # start a viewer if ARCH == 'w32' - shell = Win32API.new("shell32", "ShellExecute", - ['l','p','p','p','p','l'], 'l') - shell.Call(0, 'open', $source.path, 0, 0, 1) - # shell.Call(0, 'open', $source.path, 0, 0, SW_SHOWNORMAL) + result = shell_open( $source.path ) + Tk.messageBox( + 'icon' => 'error', + 'title' => 'ShellExecute error', + 'message' => shell_error_string( result ) + ) if result <= 32 elsif ARCH == 'osx' - fork { exec "open -a Preview \"#{$source.path}\"" } + fork { exec "open \"#{$source.path}\"" } elsif viewable( $source ) viewer = $source.type == 'pdf' ? $settings.pdf_viewer : $settings.ps_viewer @@ -827,12 +900,27 @@ TkFrame.new( $root ) { |frm| } ).pack( :side=>'left', :anchor => 'w', :padx=>4, :pady=>4 ) + if ARCH == 'w32' + $view_with_button = TkButton.new( frm, + :text => 'View with...', + :state => 'disabled', + :command => lambda { + result = shell_open_with( $source.path ) + Tk.messageBox( + 'icon' => 'error', + 'title' => 'ShellExecute error', + 'message' => shell_error_string( result ) + ) if result <= 32 + } + ).pack( :side=>'left', :anchor => 'w', :padx=>4, :pady=>4 ) + end # 'w32' + TkFrame.new( frm ) { |fm| TkButton.new( fm, :text => 'Open..', :command => lambda { path = Tk.getOpenFile( - :initialdir => $defaultDir, + :initialdir => $settings.defaultDir, :filetypes => [ ['All files', '*'], ['EPS', ['.eps']], @@ -843,7 +931,7 @@ TkFrame.new( $root ) { |frm| ) if path and ( not path.empty? ) load_file( path ) - $defaultDir = File.expand_path( File.dirname( path ) ) + $settings.defaultDir = File.expand_path( File.dirname( path ) ) end } ).pack( :side=>'left', :padx=>4, :pady=>4 ) @@ -873,29 +961,14 @@ TkFrame.new( $root ) { |frm| :defaultextension => '.' + $options.type ) return unless path && ! path.empty? - $defaultDir = File.expand_path( File.dirname( path ) ) - - # waitbox - if not $tl_wait - $tl_wait = TkToplevel.new( $root, - 'title' => 'Converting...', - 'geometry' => - '+' + ( TkWinfo.rootx( $root ).to_i + 100 ).to_s + - '+' + ( TkWinfo.rooty( $root ).to_i + 100 ).to_s, - 'borderwidth' => 2, - 'relief' => 'solid' - ) - $cnv_label = TkLabel.new( $tl_wait, - 'text' => 'converting...' - ).pack( 'side' => 'top' ) - $tl_wait.overrideredirect( 1 ) - end + $settings.defaultDir = File.expand_path( File.dirname( path ) ) + $cnv_label.configure( - 'text' => "Converting #{$source.path} to \n #{path}..." + 'text' => "Converting #{File.basename($source.path)}" + + " to #{ File.basename(path)}..." ) - $tl_wait.update - $tl_wait.deiconify - $tl_wait.raise + $cnv_label.update + begin result = $source.any_to_any( $options ) rescue EPCallError => exc @@ -930,7 +1003,9 @@ TkFrame.new( $root ) { |frm| 'message' => "#{mess}\nSee #{LOGFILE} for details." ) ensure - $tl_wait.withdraw + # $tl_wait.withdraw + $cnv_label.configure( 'text' => '' ) + $cnv_label.update end if result ccp( result.path, path ) @@ -957,4 +1032,5 @@ set_widget_states $root.raise Tk.mainloop +$settings.write_settings cleantemp diff --git a/Master/texmf-dist/scripts/epspdf/makegray.pro b/Master/texmf-dist/scripts/epspdf/makegray.pro index 67dc5705839..6206e559560 100644 --- a/Master/texmf-dist/scripts/epspdf/makegray.pro +++ b/Master/texmf-dist/scripts/epspdf/makegray.pro @@ -26,4 +26,8 @@ userdict begin 1 exch sub systemdict begin setgray end } def +% hsb: toss hue and saturation; keep brightness for gray setting +/sethsbcolor { + pop pop systemdict begin setgray end +} def end |