diff options
Diffstat (limited to 'Master/texmf-dist/scripts/epspdf/epspdfrc.rb')
-rwxr-xr-x | Master/texmf-dist/scripts/epspdf/epspdfrc.rb | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/epspdf/epspdfrc.rb b/Master/texmf-dist/scripts/epspdf/epspdfrc.rb new file mode 100755 index 00000000000..f75d65b5339 --- /dev/null +++ b/Master/texmf-dist/scripts/epspdf/epspdfrc.rb @@ -0,0 +1,507 @@ +# epspdf conversion utility, configuration module + +##### +# Copyright (C) 2006, 2008 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. +# +# In phase 1, some programs (converters, viewers) are autodetected. +# In phase 2, user preferences are read from the epspdf configuration. +# +# For unix/osx, configuration info is stored in $HOME/.epspdfrc. +# For w32, configuration info is stored in HKCU\software\epspdf. +# +# The UIs for settings are not here; they are: +# 1. the epspdf.rb command-line if epspdf.rb is run as +# a stand-alone program, and +# 2. a configuration screen in epspdftk.rb. + +require 'rbconfig' +include Config + +# for notational convenience, we keep ARCH separate + +ARCH = case CONFIG['arch'] +when /win32|mingw/ + 'w32' +when /darwin/ + 'osx' +else + 'unix' +end + +TL = ENV.has_key?('TLROOT') +if ARCH == 'w32' + require 'dl/win32' + require 'win32/registry' + include Win32 # this lets us abbreviate Win32::Registry to Registry + ShortPName = Win32API.new( + 'kernel32', 'GetShortPathName', ['P','P','N'], 'N' ) + TLROOT = ENV['TLROOT'] +end + +# for Windows, use short names within backquotes +# to avoid quotes within backquotes + +def short_name( fn ) + return fn if ARCH != 'w32' + fn.gsub!( /\\/, '/' ) + buffer = ' ' * 260 + length = ShortPName.call( fn, buffer, buffer.size ) + return ( length > 0 ) ? buffer.slice(0..length-1) : fn +end + +# system-dependent location of logfile + +LOGFILE = case ARCH +when 'w32' + TL ? "#{ENV['USERPROFILE']}\\epspdftl.log" : + "#{ENV['USERPROFILE']}\\epspdf.log" +else + "#{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' + +# 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. + +def is_a_program( s ) + case ARCH + when 'w32' + nil + else + ( system "which #{s} >/dev/null" ) ? 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 + } + end + retval +end + +class Setting + attr_reader :val, :type, :comment + attr_writer :val + + def initialize( v, t, c ) + @val = v + @type = t + @comment = c + end +end + +PDF_TARGETS = [ 'default', 'printer', 'prepress', 'screen', 'ebook' ] +PDF_VERSIONS = [ '1.2', '1.3', '1.4', 'default' ] + +$settings = { + # converters + 'gs_prog' => Setting.new( nil, 'auto', nil ), + 'gs_version' => Setting.new( nil, 'auto', nil ), + 'pdftops_prog' => Setting.new( nil, ARCH=='w32' ? 'config' : 'auto', nil ), + # 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 ), + # 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_custom' => Setting.new( '', 'config', + 'additional options for [e]ps to pdf conversion' ), + 'ps_options' => Setting.new( '-level2', 'config', + 'options for pdftops; default -level2' ), + 'bb_spread' => Setting.new( '1', 'config', + 'margin to be added to computed boundingbox; default 1' ) +} + +class << $settings + + # create shortcut methods $settings.x and $settings.x= + # for reading and writing hash elements. + + $settings.each_key { |k| + eval "def #{k} ; self[\'#{k}\'].val ; end" + eval "def #{k}=(v) ; self[\'#{k}\'].val=v ; end" + } + + # use_pdftops is a boolean counterpart and inverse + # of the stored string attribute ignore_pdftops + + 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 + 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 + end + + def accept_pdf_viewer( s ) + if ARCH == 'unix' + if is_a_program( s ) + self.pdf_viewer = s + self.pdf_viewers.unshift( s ) unless self.pdf_viewers.index( s ) + return 1 + else + return nil + end + else + return nil + end + end # accept_pdf_viewer + + def accept_ps_viewer( s ) + if ARCH == 'unix' + if is_a_program( s ) + self.ps_viewer = s + self.pdf_viewer = s unless \ + self.pdf_viewer and not self.pdf_viewer.empty? + self.ps_viewers.unshift( s ) unless self.ps_viewers.index( s ) + self.pdf_viewers.push( s ) unless self.pdf_viewers.index( s ) + return 1 + else + return nil + end + else + return nil + end + end # accept_ps_viewer + + # previously-configured settings into hash $rc_settings + # here no validity testing. + # we accept empty settings. + + def read_settings + if ARCH == 'w32' + [Registry::HKEY_LOCAL_MACHINE, + Registry::HKEY_CURRENT_USER].each { |hk| + begin # open REG_KEY safely + hk.open( REG_KEY ) do |ep| + ep.each_value do |key, type, val| + $rc_settings[ key ] = val \ + if $settings[ key ] and $settings[ key ].type == 'config' + end # do |key, type, val| + end # do |ep| + rescue + end # open REG_KEY safely + } # each |hk| + else + if test( ?s, RC_FILE ) + lines = File.read( RC_FILE ).split( /\r\n?|\n/ ) + lines.each do |l| + l = l.sub( /#.*$/, '' ) # remove trailing comments + if l =~ /^\s*(\S+)\s*=\s*(\S(.*\S)?)\s*$/ + key, val = $1, $2 + $rc_settings[ key ] = val \ + if $settings[ key ] and $settings[ key ].type == 'config' + elsif l =~ /^\s*(\S+)\s*=\s*$/ + key, val = $1, '' + $rc_settings[ key ] = '' \ + if $settings[ key ] and $settings[ key ].type == 'config' + end # if l =~ + end # do |l| + end # if ?s + end # if ARCH + end # def + + def write_settings + if ARCH == 'w32' + begin + Registry::HKEY_CURRENT_USER.create( + 'SOFTWARE', desired = Registry::KEY_ALL_ACCESS) do |reg| + reg.create( REG_K, desired = Registry::KEY_ALL_ACCESS ) do |rk| + $settings.each do |k, s| + key = String.new( k ) + st = Setting.new( s.val, s.type, s.comment ) + # this copying looks kind of silly, + # but prevents a 'modifying frozen object' error. + if st.type == 'config' + vl = st.val ? st.val : '' + rk.write_s( key, vl ) + end + end # do each + end # do |rk| + end # do |reg| + rescue => er # no real error handling for now + puts $! + puts er.backtrace.join( $/ ) + puts 'Settings will be lost at next run' + end + else + File.open( RC_FILE, 'w' ) do |f| + f.write( "# This file will be overwritten by epspdf_tk[.rb]" \ + + $/ ) + $settings.each do |key, st| + if st.type == 'config' + f.write( $/ ) + f.write( '# ' + st.comment + $/ ) if st.comment + # STDERR.puts "write setting for " + key + # STDERR.flush + f.write( key + " = " + ( st.val ? st.val : '' ) + $/ ) + end + end # do |key, st| + end # do |f| + end # if + end # def + + 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 + # viewers: use Preview; don't check anything + # unix/osx are done together, except that for osx + # the viewers code is skipped. + + 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 + try_gs_prog = nil + [Registry::HKEY_LOCAL_MACHINE, + Registry::HKEY_CURRENT_USER].each { |hk| + puts hk.to_s if $DEBUG + hk.open('SOFTWARE') { |sof| + puts sof.to_s if $DEBUG + sof.each_key { |key, wtime| + 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) + this_gs_prog = this_gs['GS_DLL'].sub( + /([\\\/])([^\\\/]+)$/, '\1gswin32c.exe') + if test(?e,this_gs_prog) + try_gs_version = skey + try_gs_prog = this_gs_prog + end # if ?e (exist) + end # skey.to_f > try_gs_version.to_f + end # if skey =~ /^\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' ) + + 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 + + # pdftops + self.pdftops_prog = xpdf_version( 'pdftops' ) ? 'pdftops' : nil + + # viewers + vw = %w{ gpdf xpdf acroread evince ggv gv kghostview } + 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| + vw2.delete( pg ) if vw2.index( pg ) + } + vw2 = nil if vw2.empty? + self.ps_viewers = vw2 + self.ps_viewer = vw2 ? vw2[ 0 ] : nil + end # if vw + end # case + + # built-in defaults already set during initialization of $settings + + # phase 2: pre-existing configuration + # w32: pdftops_prog + # unix (not osx): pdf_viewers + + read_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 + when 'unix' + if v = $rc_settings[ 'pdf_viewer' ] and is_a_program( v ) + self.pdf_viewer = v + self.pdf_viewers.unshift( v ) unless self.pdf_viewers.index( v ) + end # if + if v = $rc_settings[ 'ps_viewer' ] and is_a_program( v ) + self.ps_viewer = v + self.ps_viewers.unshift( v ) unless self.ps_viewers.index( v ) + end # if + if self.ps_viewers and not self.pdf_viewers + self.pdf_viewers = self.ps_viewers.dup + self.pdf_viewer = self.ps_viewer + end # if + end # case ARCH + + # 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' ] + when /1|yes|true|y|t/i + '1' + when /0|no|false|n|f/i + '0' + end + else + $settings.ignore_pdftops = '0' + end + + self.bb_spread = $rc_settings[ 'bb_spread' ] if + $rc_settings.has_key?( 'bb_spread' ) and + $rc_settings[ 'bb_spread' ] =~ /^[+-]?[\d]+$/ + + end # get_settings + +end # class << $settings + +$settings.get_settings |