diff options
Diffstat (limited to 'Master/texmf-dist/scripts/context/ruby/base/tex.rb')
-rw-r--r-- | Master/texmf-dist/scripts/context/ruby/base/tex.rb | 1495 |
1 files changed, 1495 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/context/ruby/base/tex.rb b/Master/texmf-dist/scripts/context/ruby/base/tex.rb new file mode 100644 index 00000000000..2cc9d25422e --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/base/tex.rb @@ -0,0 +1,1495 @@ +# module : base/tex +# copyright : PRAGMA Advanced Document Engineering +# version : 2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# todo: write systemcall for mpost to file so that it can be run +# faster + +# report ? + +require 'base/variables' +require 'base/kpse' +require 'base/system' +require 'base/state' +require 'base/pdf' +require 'base/file' +require 'base/ctx' + +class String + + def standard? + begin + self == 'standard' + rescue + false + end + end + +end + +class Array + + def standard? + begin + self.include?('standard') + rescue + false + end + end + +end + +class TEX + + # The make-part of this class was made on a rainy day while listening + # to "10.000 clowns on a rainy day" by Jan Akkerman. Unfortunately the + # make method is not as swinging as this live cd. + + include Variables + + @@texengines = Hash.new + @@mpsengines = Hash.new + @@backends = Hash.new + @@runoptions = Hash.new + @@texformats = Hash.new + @@mpsformats = Hash.new + @@prognames = Hash.new + @@texmakestr = Hash.new + @@texprocstr = Hash.new + @@mpsmakestr = Hash.new + @@mpsprocstr = Hash.new + + @@texmethods = Hash.new + @@mpsmethods = Hash.new + + ['tex','pdftex','pdfetex','standard'] .each do |e| @@texengines[e] = 'pdfetex' end + ['aleph','omega'] .each do |e| @@texengines[e] = 'aleph' end + ['xetex'] .each do |e| @@texengines[e] = 'xetex' end + + ['metapost','mpost','standard'] .each do |e| @@mpsengines[e] = 'mpost' end + + ['pdfetex','pdftex','pdf','pdftex','standard'] .each do |b| @@backends[b] = 'pdftex' end + ['dvipdfmx','dvipdfm','dpx','dpm'] .each do |b| @@backends[b] = 'dvipdfmx' end + ['xetex','xtx'] .each do |b| @@backends[b] = 'xetex' end + ['dvips','ps'] .each do |b| @@backends[b] = 'dvips' end + ['dvipsone'] .each do |b| @@backends[b] = 'dvipsone' end + ['acrobat','adobe','distiller'] .each do |b| @@backends[b] = 'acrobat' end + + # todo norwegian (no) + + ['plain'] .each do |f| @@texformats[f] = 'plain' end + ['cont-en','en','english','context','standard'].each do |f| @@texformats[f] = 'cont-en' end + ['cont-nl','nl','dutch'] .each do |f| @@texformats[f] = 'cont-nl' end + ['cont-de','de','german'] .each do |f| @@texformats[f] = 'cont-de' end + ['cont-it','it','italian'] .each do |f| @@texformats[f] = 'cont-it' end + ['cont-cz','cz','czech'] .each do |f| @@texformats[f] = 'cont-cz' end + ['cont-ro','ro','romanian'] .each do |f| @@texformats[f] = 'cont-ro' end + ['cont-uk','uk','brittish'] .each do |f| @@texformats[f] = 'cont-uk' end + ['mptopdf'] .each do |f| @@texformats[f] = 'mptopdf' end + + ['latex'] .each do |f| @@texformats[f] = 'latex.ltx' end + + ['plain','mpost'] .each do |f| @@mpsformats[f] = 'plain' end + ['metafun','context','standard'] .each do |f| @@mpsformats[f] = 'metafun' end + + ['pdfetex','aleph','omega'] .each do |p| @@prognames[p] = 'context' end + ['mpost'] .each do |p| @@prognames[p] = 'metafun' end + + ['plain','default','standard','mptopdf'] .each do |f| @@texmethods[f] = 'plain' end + ['cont-en','cont-nl','cont-de','cont-it', + 'cont-cz','cont-ro','cont-uk'] .each do |f| @@texmethods[f] = 'context' end + ['latex'] .each do |f| @@texmethods[f] = 'latex' end + + ['plain','default','standard'] .each do |f| @@mpsmethods[f] = 'plain' end + ['metafun'] .each do |f| @@mpsmethods[f] = 'metafun' end + + @@texmakestr['plain'] = "\\dump" + @@mpsmakestr['plain'] = "\\dump" + + ['cont-en','cont-nl','cont-de','cont-it', + 'cont-cz','cont-ro','cont-uk'] .each do |f| @@texprocstr[f] = "\\emergencyend" end + + @@runoptions['xetex'] = ['--no-pdf'] + + @@booleanvars = [ + 'batchmode', 'nonstopmode', 'fast', 'fastdisabled', 'silentmode', 'final', + 'paranoid', 'notparanoid', 'nobanner', 'once', 'allpatterns', + 'nompmode', 'nomprun', 'automprun', + 'nomapfiles', 'local', + 'arrange', 'noarrange', + 'forcexml', 'foxet', + 'mpyforce', 'forcempy', + 'forcetexutil', 'texutil', + 'globalfile', 'autopath', + 'purge', 'purgeall', 'autopdf', 'simplerun', 'verbose', + 'nooptionfile' + ] + @@stringvars = [ + 'modefile', 'result', 'suffix', 'response', 'path', + 'filters', 'usemodules', 'environments', 'separation', 'setuppath', + 'arguments', 'input', 'output', 'randomseed', 'modes', 'filename', + 'modefile', 'ctxfile' + ] + @@standardvars = [ + 'mainlanguage', 'bodyfont', 'language' + ] + @@knownvars = [ + 'engine', 'distribution', 'texformats', 'mpsformats', 'progname', 'interface', + 'runs', 'backend' + ] + + @@extrabooleanvars = [] + @@extrastringvars = [] + + def booleanvars + [@@booleanvars,@@extrabooleanvars].flatten + end + def stringvars + [@@stringvars,@@extrastringvars].flatten + end + def standardvars + @@standardvars + end + def knownvars + @@knownvars + end + + def setextrastringvars(vars) + @@extrastringvars << vars + end + def setextrabooleanvars(vars) + @@extrabooleanvars << vars + end + + @@temprunfile = 'texexec' + @@temptexfile = 'texexec.tex' + + def initialize(logger=nil) + if @logger = logger then + def report(str='') + @logger.report(str) + end + else + def report(str='') + puts(str) + end + end + @cleanups = Array.new + @variables = Hash.new + @startuptime = Time.now + # options + booleanvars.each do |k| + setvariable(k,false) + end + stringvars.each do |k| + setvariable(k,'') + end + standardvars.each do |k| + setvariable(k,'standard') + end + setvariable('distribution', Kpse.distribution) + setvariable('texformats', defaulttexformats) + setvariable('mpsformats', defaultmpsformats) + setvariable('progname', 'context') + setvariable('interface', 'standard') + setvariable('engine', 'standard') # replaced by tex/mpsengine + setvariable('backend', 'pdftex') + setvariable('runs', '8') + setvariable('randomseed', rand(1440).to_s) + # files + setvariable('files', []) + # defaults + setvariable('texengine', 'standard') + setvariable('mpsengine', 'standard') + setvariable('backend', 'standard') + end + + def runtime + Time.now - @startuptime + end + + def reportruntime + report("runtime: #{runtime}") + end + + def inspect(name=nil) + if ! name || name.empty? then + name = [booleanvars,stringvars,standardvars,knownvars] + end + [name].flatten.each do |n| + if str = getvariable(n) then + unless (str.class == String) && str.empty? then + report("option '#{n}' is set to '#{str}'") + end + end + end + end + + def tempfilename(suffix='') + @@temprunfile + if suffix.empty? then '' else ".#{suffix}" end + end + + def cleanup + @cleanups.each do |name| + begin + File.delete(name) if FileTest.file?(name) + rescue + report("unable to delete #{name}") + end + end + end + + def cleanuptemprunfiles + begin + Dir.glob("#{@@temprunfile}*").each do |name| + if File.file?(name) && (File.splitname(name)[1] !~ /(pdf|dvi)/o) then + begin File.delete(name) ; rescue ; end + end + end + rescue + end + end + + def backends() @@backends.keys.sort end + + def texengines() @@texengines.keys.sort end + def mpsengines() @@mpsengines.keys.sort end + def texformats() @@texformats.keys.sort end + def mpsformats() @@mpsformats.keys.sort end + + def defaulttexformats() ['en','nl','mptopdf'] end + def defaultmpsformats() ['metafun'] end + + def texmakeextras(format) @@texmakestr[format] || '' end + def mpsmakeextras(format) @@mpsmakestr[format] || '' end + def texprocextras(format) @@texprocstr[format] || '' end + def mpsprocextras(format) @@mpsprocstr[format] || '' end + + def texmethod(format) @@texmethods[str] || @@texmethods['standard'] end + def mpsmethod(format) @@mpsmethods[str] || @@mpsmethods['standard'] end + + def runoptions(engine) + if @@runoptions.key?(engine) then @@runoptions[engine].join(' ') else '' end + end + + # private + + def cleanuplater(name) + begin + @cleanups.push(File.expand_path(name)) + rescue + @cleanups.push(name) + end + end + + def openedfile(name) + begin + f = File.open(name,'w') + rescue + report("file '#{File.expand_path(name)}' cannot be opened for writing") + return nil + else + cleanuplater(name) if f + return f + end + end + + def prefixed(format,engine) + case engine + when /etex|eetex|pdfetex|pdfeetex|pdfxtex|xpdfetex|eomega|aleph|xetex/io then + "*#{format}" + else + format + end + end + + def quoted(str) + if str =~ /^[^\"].* / then "\"#{str}\"" else str end + end + + def getarrayvariable(str='') + str = getvariable(str) + if str.class == String then str.split(',') else str.flatten end + end + + def validtexformat(str) validsomething(str,@@texformats) end + def validmpsformat(str) validsomething(str,@@mpsformats) end + def validtexengine(str) validsomething(str,@@texengines) end + def validmpsengine(str) validsomething(str,@@mpsengines) end + + def validtexmethod(str) [validsomething(str,@@texmethods)].flatten.first end + def validmpsmethod(str) [validsomething(str,@@mpsmethods)].flatten.first end + + def validsomething(str,something) + if str then + list = [str].flatten.collect do |s| + something[s] + end .compact.uniq + if list.length>0 then + if str.class == String then list.first else list end + else + false + end + else + false + end + end + + def validbackend(str) + if str && @@backends.key?(str) then + @@backends[str] + else + @@backends['standard'] + end + end + + def validprogname(str,engine='standard') + if str && @@prognames.key?(str) then + @@prognames[str] + elsif (engine != 'standard') && @@prognames.key?(engine) then + @@prognames[engine] + else + str + end + end + + # we no longer support the & syntax + + def formatflag(engine=nil,format=nil) + case getvariable('distribution') + when 'standard' then prefix = "--fmt" + when /web2c/io then prefix = web2cformatflag(engine) + when /miktex/io then prefix = "--undump" + else return "" + end + if format then + # if engine then + # "#{prefix}=#{engine}/#{format}" + # else + "#{prefix}=#{format}" + # end + else + prefix + end + end + + def web2cformatflag(engine=nil) + # funny that we've standardized on the fmt suffix (at the cost of + # upward compatibility problems) but stuck to the bas/mem/fmt flags + if engine then + case validmpsengine(engine) + when /mpost/ then "--mem" + when /mfont/ then "--bas" + else "--fmt" + end + else + "--fmt" + end + end + + def prognameflag(progname=nil) + case getvariable('distribution') + when 'standard' then prefix = "--progname" + when /web2c/io then prefix = "--progname" + when /miktex/io then prefix = "--alias" + else return "" + end + if progname then + if progname = validprogname(progname) then + "#{prefix}=#{progname}" + else + "" + end + else + prefix + end + end + + def iniflag() # should go to kpse and kpse should become texenv + if Kpse.miktex? then + "-initialize" + else + "--ini" + end + end + def tcxflag(file="natural.tcx") + if Kpse.miktex? then + "-tcx=#{file}" + else + "--translate-file=#{file}" + end + end + + def filestate(file) + File.mtime(file).strftime("%d/%m/%Y %H:%M:%S") + end + + # will go to context/process context/listing etc + + def contextversion # ook elders gebruiken + filename = Kpse.found('context.tex') + version = 'unknown' + begin + if FileTest.file?(filename) && IO.read(filename).match(/\\contextversion\{(\d+\.\d+\.\d+)\}/) then + version = $1 + end + rescue + end + return version + end + + def makeformats + if getvariable('fast') then + report('using existing database') + else + report('updating file database') + Kpse.update + end + # goody + if getvariable('texformats') == 'standard' then + setvariable('texformats',[getvariable('interface')]) unless getvariable('interface').empty? + end + # prepare + texformats = validtexformat(getarrayvariable('texformats')) + mpsformats = validmpsformat(getarrayvariable('mpsformats')) + texengine = validtexengine(getvariable('texengine')) + mpsengine = validmpsengine(getvariable('mpsengine')) + # save current path + savedpath = Dir.getwd + # generate tex formats + if texformats && texengine && (progname = validprogname(getvariable('progname'),texengine)) then + report("using tex engine #{texengine}") + texformatpath = if getvariable('local') then '.' else Kpse.formatpath(texengine,true) end + # can be empty, to do + report("using tex format path #{texformatpath}") + begin + Dir.chdir(texformatpath) + rescue + end + if texformats.length > 0 then + makeuserfile + makeresponsefile + end + texformats.each do |texformat| + report("generating tex format #{texformat}") + command = [quoted(texengine),prognameflag(progname),iniflag,tcxflag,prefixed(texformat,texengine),texmakeextras(texformat)].join(' ') + report(command) if getvariable('verbose') + system(command) + end + else + texformatpath = '' + end + # generate mps formats + if mpsformats && mpsengine && (progname = validprogname(getvariable('progname'),mpsengine)) then + report("using mp engine #{mpsengine}") + mpsformatpath = if getvariable('local') then '.' else Kpse.formatpath(mpsengine,false) end + report("using mps format path #{mpsformatpath}") + begin + Dir.chdir(mpsformatpath) + rescue + end + mpsformats.each do |mpsformat| + report("generating mps format #{mpsformat}") + command = [quoted(mpsengine),prognameflag(progname),iniflag,tcxflag,mpsformat,mpsmakeextras(mpsformat)].join(' ') + report(command) if getvariable('verbose') + system(command) + end + else + mpsformatpath = '' + end + # check for problems + report("tex engine path: #{texformatpath}") unless texformatpath.empty? + report("mps engine path: #{mpsformatpath}") unless mpsformatpath.empty? + [['fmt','tex'],['mem','mps']].each do |f| + [[texformatpath,'global'],[mpsformatpath,'global'],[savedpath,'current']].each do |p| + begin + Dir.chdir(p[0]) + rescue + else + Dir.glob("*.#{f[0]}").each do |file| + report("#{f[1]}format: #{filestate(file)} > #{File.expand_path(file)}") + end + end + end + end + # to be sure, go back to current path + begin + Dir.chdir(savedpath) + rescue + end + # finalize + cleanup + reportruntime + end + + def checkcontext + + # todo : report texmf.cnf en problems + + # basics + report("current distribution: #{Kpse.distribution}") + report("context source date: #{contextversion}") + formatpaths = Kpse.formatpaths + globpattern = "**/{#{formatpaths.join(',')}}/*/*.{fmt,efmt,ofmt,xfmt,mem}" + report("format path: #{formatpaths.join(' ')}") + # utilities + report('start of analysis') + results = Array.new + ['texexec','texutil','ctxtools'].each do |program| + result = `texmfstart #{program} --help` + result.sub!(/.*?(#{program}[^\n]+)\n.*/mi) do $1 end + results.push("#{result}") + end + # formats + cleanuptemprunfiles + if formats = Dir.glob(globpattern) then + formats.sort.each do |name| + cleanuptemprunfiles + if f = open(tempfilename('tex'),'w') then + # kind of aleph-run-out-of-par safe + f << "\\starttext\n" + f << " \\relax test \\relax\n" + f << "\\stoptext\n" + f << "\\endinput\n" + f.close + if FileTest.file?(tempfilename('tex')) then + format = File.basename(name) + engine = if name =~ /(pdfetex|aleph|xetex)[\/\\]#{format}/ then $1 else '' end + if engine.empty? then + engineflag = "" + else + engineflag = "--engine=#{$1}" + end + case format + when /cont\-([a-z]+)/ then + interface = $1.sub(/cont\-/,'') + results.push('') + results.push("testing interface #{interface}") + flags = ['--process','--batch','--once',"--interface=#{interface}",engineflag] + # result = Kpse.pipescript('newtexexec',tempfilename,flags) + result = runtexexec([tempfilename], flags, 1) + if FileTest.file?("#{@@temprunfile}.log") then + logdata = IO.read("#{@@temprunfile}.log") + if logdata =~ /^\s*This is (.*?)[\s\,]+(.*?)$/mois then + if validtexengine($1.downcase) then + results.push("#{$1} #{$2.gsub(/\(format.*$/,'')}".strip) + end + end + if logdata =~ /^\s*(ConTeXt)\s+(.*int:\s+[a-z]+.*?)\s*$/mois then + results.push("#{$1} #{$2}".gsub(/\s+/,' ').strip) + end + else + results.push("format #{format} does not work") + end + when /metafun/ then + # todo + when /mptopdf/ then + # todo + end + else + results.push("error in creating #{tempfilename('tex')}") + end + end + cleanuptemprunfiles + end + end + report('end of analysis') + report + results.each do |line| + report(line) + end + cleanuptemprunfiles + + end + + private + + def makeuserfile + language = getvariable('language') + mainlanguage = getvariable('mainlanguage') + bodyfont = getvariable('bodyfont') + if f = openedfile("cont-fmt.tex") then + f << "\\unprotect" + case language + when 'all' then + f << "\\preloadallpatterns\n" + when '' then + f << "% no language presets\n" + when 'standard' + f << "% using defaults\n" + else + languages = language.split(',') + languages.each do |l| + f << "\\installlanguage[\\s!#{l}][\\c!state=\\v!start]\n" + end + mainlanguage = languages.first + end + unless mainlanguage == 'standard' then + f << "\\setupcurrentlanguage[\\s!#{mainlanguage}]\n"; + end + unless bodyfont == 'standard' then + # ~ will become obsolete when lmr is used + f << "\\definetypescriptsynonym[cmr][#{bodyfont}]" + # ~ is already obsolete for some years now + f << "\\definefilesynonym[font-cmr][font-#{bodyfont}]\n" + end + f << "\\protect\n" + f << "\\endinput\n" + f.close + end + end + + def makeresponsefile + interface = getvariable('interface') + if f = openedfile("mult-def.tex") then + case interface + when 'standard' then + f << "% using default response interface" + else + f << "\\def\\currentresponses\{#{interface}\}\n" + end + f << "\\endinput\n" + f.close + end + end + + private # will become baee/context + + @@preamblekeys = [ + ['tex','texengine'], + ['program','texengine'], + ['translate','tcxfilter'], + ['tcx','tcxfilter'], + ['output','backend'], + ['mode','mode'], + ['ctx','ctxfile'], + ['version','contextversion'], + ['format','texformat'], + ['interface','texformat'] + ] + + def scantexpreamble(filename) + begin + if FileTest.file?(filename) and tex = File.open(filename) then + while str = tex.gets and str.chomp! do + if str =~ /^\%\s*(.*)/o then + vars = Hash.new + $1.split(/\s+/o).each do |s| + k, v = s.split('=') + vars[k] = v + end + @@preamblekeys.each do |v| + setvariable(v[1],vars[v[0]]) if vars.key?(v[0]) + end + else + break + end + end + tex.close + end + rescue + # well, let's not worry too much + end + end + + def scantexcontent(filename) + if FileTest.file?(filename) and tex = File.open(filename) then + while str = tex.gets do + case str.chomp + when /^\%/o then + # next + when /\\(starttekst|stoptekst|startonderdeel|startdocument|startoverzicht)/o then + setvariable('texformat','nl') ; break + when /\\(stelle|verwende|umgebung|benutze)/o then + setvariable('texformat','de') ; break + when /\\(stel|gebruik|omgeving)/o then + setvariable('texformat','nl') ; break + when /\\(use|setup|environment)/o then + setvariable('texformat','en') ; break + when /\\(usa|imposta|ambiente)/o then + setvariable('texformat','it') ; break + when /(height|width|style)=/o then + setvariable('texformat','en') ; break + when /(hoehe|breite|schrift)=/o then + setvariable('texformat','de') ; break + when /(hoogte|breedte|letter)=/o then + setvariable('texformat','nl') ; break + when /(altezza|ampiezza|stile)=/o then + setvariable('texformat','it') ; break + when /externfiguur/o then + setvariable('texformat','nl') ; break + when /externalfigure/o then + setvariable('texformat','en') ; break + when /externeabbildung/o then + setvariable('texformat','de') ; break + when /figuraesterna/o then + setvariable('texformat','it') ; break + end + end + tex.close + end + + end + + private # will become base/context + + def pushresult(filename,resultname) + fname = File.unsuffixed(filename) + rname = File.unsuffixed(resultname) + if ! rname.empty? && (rname != fname) then + report("outputfile #{rname}") + ['tuo','log','dvi','pdf'].each do |s| + File.silentrename(File.suffixed(fname,s),File.suffixed('texexec',s)) + end + ['tuo'].each do |s| + File.silentrename(File.suffixed(rname,s),File.suffixed(fname,s)) if FileTest.file?(File.suffixed(rname,s)) + end + end + end + + def popresult(filename,resultname) + fname = File.unsuffixed(filename) + rname = File.unsuffixed(resultname) + if ! rname.empty? && (rname != fname) then + report("renaming #{fname} to #{rname}") + ['tuo','log','dvi','pdf'].each do |s| + File.silentrename(File.suffixed(fname,s),File.suffixed(rname,s)) + end + report("restoring #{fname}") + unless $fname == 'texexec' then + ['tuo','log','dvi','pdf'].each do |s| + File.silentrename(File.suffixed('texexec',s),File.suffixed(fname,s)) + end + end + end + end + + def makestubfile(rawname,forcexml=false) + if tmp = File.open(File.suffixed(rawname,'run'),'w') then + tmp << "\\starttext\n" + if forcexml then + if FileTest.file?(rawname) && (xml = File.open(rawname)) then + xml.each do |line| + case line + when /<\?context\-directive\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*?)\s*\?>/o then + category, key, value, rest = $1, $2, $3, $4 + case category + when 'job' then + case key + when 'control' then + setvariable(value,if rest.empty? then true else rest end) + when 'mode', 'modes' then + tmp << "\\enablemode[#{value}]\n" + when 'stylefile', 'environment' then + tmp << "\\environment #{value}\n" + when 'module' then + tmp << "\\usemodule[#{value}]\n" + when 'interface' then + contextinterface = value + end + end + when /<[a-z]+/io then # beware of order, first pi test + break + end + end + xml.close + end + tmp << "\\processXMLfilegrouped{#{rawname}}\n" + else + tmp << "\\processfile{#{rawname}}\n" + end + tmp << "\\stoptext\n" + tmp.close + return "run" + else + return File.splitname(rawname)[1] + end + end + +end + +class TEX + + def processtex # much to do: mp, xml, runs etc + setvariable('texformats',[getvariable('interface')]) unless getvariable('interface').empty? + getarrayvariable('files').each do |filename| + setvariable('filename',filename) + report("processing document '#{filename}'") + processfile + end + reportruntime + end + + def processmptex + getarrayvariable('files').each do |filename| + setvariable('filename',filename) + report("processing graphic '#{filename}'") + runtexmp(filename) + end + reportruntime + end + + def processmpxtex + getarrayvariable('files').each do |filename| + setvariable('filename',filename) + report("processing text of graphic '#{filename}'") + processmpx(filename,true) + end + reportruntime + end + + def deleteoptionfile(rawname) + begin + File.delete(File.suffixed(rawname,'top')) + rescue + end + end + + def makeoptionfile(rawname, jobname, jobsuffix, finalrun, fastdisabled, kindofrun) + # jobsuffix = orisuffix + if topname = File.suffixed(rawname,'top') and opt = File.open(topname,'w') then + # local handies + opt << "\% #{topname}\n" + opt << "\\unprotect\n" + opt << "\\setupsystem[\\c!n=#{kindofrun}]\n" + opt << "\\def\\MPOSTformatswitch\{#{prognameflag('metafun')} #{formatflag('mpost')}=\}\n" + if getvariable('batchmode') then + opt << "\\batchmode\n" + end + if getvariable('nonstopmode') then + opt << "\\nonstopmode\n" + end + if getvariable('paranoid') then + opt << "\\def\\maxreadlevel{1}\n" + end + if (str = File.unixfied(getvariable('modefile'))) && ! str.empty? then + opt << "\\readlocfile{#{str}}{}{}\n" + end + if (str = File.unixfied(getvariable('result'))) && ! str.empty? then + opt << "\\setupsystem[file=#{str}]\n" + elsif (str = getvariable('suffix')) && ! str.empty? then + opt << "\\setupsystem[file=#{jobname}.#{str}]\n" + end + if (str = File.unixfied(getvariable('path'))) && ! str.empty? then + opt << "\\usepath[#{str}]\n" unless str.empty? + end + if (str = getvariable('mainlanguage').downcase) && ! str.empty? && ! str.standard? then + opt << "\\setuplanguage[#{str}]\n" + end + if str = validbackend(getvariable('backend')) then + opt << "\\setupoutput[#{str}]\n" + end + if getvariable('color') then + opt << "\\setupcolors[\\c!state=\\v!start]\n" + end + if getvariable('nompmode') || getvariable('nomprun') || getvariable('automprun') then + opt << "\\runMPgraphicsfalse\n" + end + if getvariable('fast') && ! getvariable('fastdisabled') then + opt << "\\fastmode\n" + end + if getvariable('silentmode') then + opt << "\\silentmode\n" + end + if (str = getvariable('separation')) && ! str.empty? then + opt << "\\setupcolors[\\c!split=#{str}]\n" + end + if (str = getvariable('setuppath')) && ! str.empty? then + opt << "\\setupsystem[\\c!directory=\{#{str}\}]\n" + end + if (str = getvariable('paperformat')) && ! str.empty? && ! str.standard? then + if str =~ /^([a-z]+\d+)([a-z]+\d+)$/io then # A5A4 A4A3 A2A1 ... + opt << "\\setuppapersize[#{$1.upcase}][#{$2.upcase}]\n" + else # ...*... + pf = str.upcase.split(/[x\*]/o) + pf << pf[0] if pd.size == 1 + opt << "\\setuppapersize[#{pf[0]}][#{pf[1]}]\n" + end + end + if (str = getvariable('background')) && ! str.empty? then + opt << "\\defineoverlay[whatever][{\\externalfigure[#{str}][\\c!factor=\\v!max]}]\n" + opt << "\\setupbackgrounds[\\v!page][\\c!background=whatever]\n" + end + if getvariable('centerpage') then + opt << "\\setuplayout[\\c!location=\\v!middle,\\c!marking=\\v!on]\n" + end + if getvariable('nomapfiles') then + opt << "\\disablemapfiles\n" + end + if getvariable('noarrange') then + opt << "\\setuparranging[\\v!disable]\n" + elsif getvariable('arrange') then + arrangement = Array.new + if finalrun then + arrangement << "\\v!doublesided" unless getvariable('noduplex') + case printformat + when '' then arrangement << "\\v!normal" + when /.*up/oi then arrangement << "\\v!rotated" + when /.*down/oi then arrangement << ["2DOWN","\\v!rotated"] + when /.*side/oi then arrangement << ["2SIDE","\\v!rotated"] + end + else + arrangement << "\\v!disable" + end + opt << "\\setuparranging[#{arrangement.flatten.join(',')}]\n" if arrangement.size > 0 + end + if (str = getvariable('modes')) && ! str.empty? then + opt << "\\enablemode[#{modes}]\n" + end + if (str = getvariable('arguments')) && ! str.empty? then + opt << "\\setupenv[#{str}]\n" + end + if (str = getvariable('randomseed')) && ! str.empty? then + opt << "\\setupsystem[\\c!random=#{str}]\n" + end + if (str = getvariable('input')) && ! str.empty? then + opt << "\\setupsystem[inputfile=#{str}]\n" + else + opt << "\\setupsystem[inputfile=#{rawname}]\n" + end + if (str = getvariable('pages')) && ! str.empty? then + if str.downcase == 'odd' then + opt << "\\chardef\\whichpagetoshipout=1\n" + elsif str.downcase == 'even' then + opt << "\\chardef\\whichpagetoshipout=2\n" + else + pagelist = Array.new + str.split(/\,/).each do |page| + pagerange = page.split(/(\:|\.\.)/o ) + if pagerange.size > 1 then + pagerange.first.to_i.upto(pagerange.last.to_i) do |p| + pagelist << p.to_s + end + else + pagelist << page + end + end + opt << "\\def\\pagestoshipout\{pagelist.join(',')\}\n"; + end + end + opt << "\\protect\n"; + begin getvariable('filters' ).split(',').uniq.each do |f| opt << "\\useXMLfilter[#{f}]\n" end ; rescue ; end + begin getvariable('usemodules' ).split(',').uniq.each do |m| opt << "\\usemodule[#{m}]\n" end ; rescue ; end + begin getvariable('environments').split(',').uniq.each do |e| opt << "\\environment #{e}\n" end ; rescue ; end + # this will become: + # begin getvariable('environments').split(',').uniq.each do |e| opt << "\\useenvironment[#{e}]\n" end ; rescue ; end + opt << "\\endinput\n" + opt.close + end + end + + def takeprecautions + ENV['MPXCOMAND'] = '0' # else loop + if getvariable('paranoid') then + ENV['SHELL_ESCAPE'] = ENV['SHELL_ESCAPE'] || 'f' + ENV['OPENOUT_ANY'] = ENV['OPENOUT_ANY'] || 'p' + ENV['OPENIN_ANY'] = ENV['OPENIN_ANY'] || 'p' + elsif getvariable('notparanoid') then + ENV['SHELL_ESCAPE'] = ENV['SHELL_ESCAPE'] || 't' + ENV['OPENOUT_ANY'] = ENV['OPENOUT_ANY'] || 'a' + ENV['OPENIN_ANY'] = ENV['OPENIN_ANY'] || 'a' + end + if ENV['OPENIN_ANY'] && (ENV['OPENIN_ANY'] == 'p') then # first test redundant + setvariable('paranoid', true) + end + if ENV.key?('SHELL_ESCAPE') && (ENV['SHELL_ESCAPE'] == 'f') then + setvariable('automprun',true) + end + ['TXRESOURCES','MPRESOURCES','MFRESOURCES'].each do |res| + [getvariable('runpath'),getvariable('path')].each do |pat| + unless pat.empty? then + if ENV.key?(res) then + ENV[res] = if ENV[res].empty? then pat else pat + ":" + ENV[res] end + else + ENV[res] = pat + end + end + end + end + end + + def runtex(filename) + texengine = validtexengine(getvariable('texengine')) + texformat = validtexformat(getarrayvariable('texformats').first) + progname = validprogname(getvariable('progname')) + report("tex engine: #{texengine}") + report("tex format: #{texformat}") + report("progname: #{progname}") + if texengine && texformat && progname then + command = [quoted(texengine),prognameflag(progname),formatflag(texengine,texformat),runoptions(texengine),filename,texprocextras(texformat)].join(' ') + report(command) if getvariable('verbose') + system(command) + else + false + end + end + + def runmp(filename) + mpsengine = validmpsengine(getvariable('mpsengine')) + mpsformat = validmpsformat(getarrayvariable('mpsformats').first) + progname = validprogname(getvariable('progname')) + if mpsengine && mpsformat && progname then + command = [quoted(mpsengine),prognameflag(progname),formatflag(mpsengine,mpsformat),runoptions(mpsengine),filename,mpsprocextras(mpsformat)].join(' ') + report(command) if getvariable('verbose') + system(command) + else + false + end + end + + def runtexmp(filename,filetype='') + mpfile = File.suffixed(filename,filetype,'mp') + if File.atleast?(mpfile,25) then + # first run needed + File.silentdelete(File.suffixed(mpfile,'mpt')) + doruntexmp(mpfile,false) + mpgraphics = checkmpgraphics(mpfile) + mplabels = checkmplabels(mpfile) + if mpgraphics || mplabels then + # second run needed + doruntexmp(mpfile,mplabels) + end + end + end + + def runtexmpjob(filename,filetype='') + mpfile = File.suffixed(filename,filetype,'mp') + if File.atleast?(mpfile,25) && (data = File.silentread(mpfile)) then + textranslation = if data =~ /^\%\s+translate.*?\=([\w\d\-]+)/io then $1 else '' end + mpjobname = if data =~ /collected graphics of job \"(.+?)\"/io then $1 else '' end + if ! mpjobname.empty? and File.unsuffixed(filename) =~ /#{mpjobname}/ then # don't optimize + options = Array.new + options.push("--mptex") + options.push("--nomp") + options.push("--mpyforce") if getvariable('forcempy') || getvariable('mpyforce') + options.push("--translate=#{textranslation}") unless textranslation.empty? + options.push("--batch") if getvariable('batchmode') + options.push("--nonstop") if getvariable('nonstopmode') + options.push("--output=ps") + return runtexexec(mpfile,options,2) + end + end + return false + end + + def runtexutil(filename=[], options=['--ref','--ij','--high'], old=false) + filename.each do |fname| + if old then + Kpse.runscript('texutil',fname,options) + else + begin + logger = Logger.new('TeXUtil') + if tu = TeXUtil::Converter.new(logger) and tu.loaded(fname) then + tu.saved if tu.processed + end + rescue + Kpse.runscript('texutil',fname,options) + end + end + end + end + + # 1=tex 2=mptex 3=mpxtex + + def runtexexec(filename=[], options=[], mode=nil) + begin + if mode and job = TEX.new(@logger) then + options.each do |option| + if option=~ /^\-*(.*?)\=(.*)$/o then + job.setvariable($1,$2) + else + job.setvariable(option,true) + end + end + job.setvariable("files",filename) + case mode + when 1 then job.processtex + when 2 then job.processmptex + when 3 then job.processmpxtex + end + job.inspect && Kpse.inspect if getvariable('verbose') + return true + else + Kpse.runscript('texexec',filename,options) + end + rescue + Kpse.runscript('texexec',filename,options) + end + end + + def fixbackendvars(backend) + ENV['backend'] = backend ; + ENV['progname'] = backend unless validtexengine(backend) + ENV['TEXFONTMAPS'] = ".;\$TEXMF/fonts/map/{#{backend},pdftex,dvips,}//" + end + + def runbackend(rawname) + case validbackend(getvariable('backend')) + when 'dvipdfmx' then + fixbackendvars('dvipdfm') + system("dvipdfmx -d 4 #{File.unsuffixed(rawname)}") + when 'xetex' then + fixbackendvars('xetex') + system("xdv2pdf #{File.suffixed(jrawname,'xdv')}") + when 'dvips' then + fixbackendvars('dvips') + mapfiles = '' + begin + if tuifile = File.suffixed(rawname,'tui') and FileTest.file?(tuifile) then + IO.read(tuifile).scan(/^c \\usedmapfile\{.\}\{(.*?)\}\s*$/o) do + mapfiles += "-u +#{$1} " ; + end + end + rescue + mapfiles = '' + end + system("dvips #{mapfiles} #{File.unsuffixed(rawname)}") + when 'pdftex' then + # no need for postprocessing + else + report("no postprocessing needed") + end + end + + def processfile + + takeprecautions + + rawname = getvariable('filename') + + jobname = getvariable('filename') + suffix = getvariable('suffix') + result = getvariable('result') + + runonce = getvariable('once') + finalrun = getvariable('final') || (getvariable('arrange') && ! getvariable('noarrange')) + globalfile = getvariable('globalfile') + + if getvariable('autopath') then + jobname = File.basename(jobname) + inppath = File.dirname(jobname) + else + inppath = '' + end + + jobname, jobsuffix = File.splitname(jobname,'tex') + + jobname = File.unixfied(jobname) + inppath = File.unixfied(inppath) + result = File.unixfied(result) + + orisuffix = jobsuffix # still needed ? + + setvariable('nomprun',true) if orisuffix == 'mpx' # else cylic run + + PDFview.closeall if getvariable('autopdf') + + forcexml = jobsuffix.match(/^(xml|fo|fox|rlg|exa)$/io) # nil or match + + dummyfile = false + + # fuzzy code snippet: (we kunnen kpse: prefix gebruiken) + + unless FileTest.file?(File.suffixed(jobname,jobsuffix)) then + if FileTest.file?(rawname + '.tex') then + jobname = rawname.dup + jobsuffix = 'tex' + end + end + + # we can have funny names, like 2005.10.10 (given without suffix) + + rawname = jobname + '.' + jobsuffix + + unless FileTest.file?(rawname) then + inppath.split(',').each do |ip| + break if dummyfile = FileTest.file?(File.join(ip,rawname)) + end + end + + # preprocess files + + ctx = CtxRunner.new(rawname,@logger) + if getvariable('ctxfile').empty? then + ctx.manipulate(File.suffixed(rawname,'ctx'),'jobname.ctx') + else + ctx.manipulate(File.suffixed(getvariable('ctxfile'),'ctx')) + end + ctx.savelog(File.suffixed(rawname,'ctl')) + + envs = ctx.environments + mods = ctx.modules + + # merge environment and module specs + + envs << getvariable('environments') unless getvariable('environments').empty? + mods << getvariable('modules') unless getvariable('modules') .empty? + + envs = envs.uniq.join(',') + mods = mods.uniq.join(',') + + report("using environments #{envs}") if envs.length > 0 + report("using modules #{mods}") if mods.length > 0 + + setvariable('environments', envs) + setvariable('modules', mods) + + # end of preprocessing and merging + + jobsuffix = makestubfile(rawname,forcexml) if dummyfile || forcexml + + if globalfile || FileTest.file?(rawname) then + + if not dummyfile and not globalfile then + scantexpreamble(rawname) + scantexcontent(rawname) if getvariable('texformats').standard? + end + + result = File.suffixed(rawname,suffix) unless suffix.empty? + + pushresult(rawname,result) + + method = validtexmethod(validtexformat(getvariable('texformats'))) + + report("tex processing method: #{method}") + + case method + + when 'context' then + if getvariable('simplerun') || runonce then + makeoptionfile(rawname,jobname,orisuffix,true,true,3) unless getvariable('nooptionfile') + ok = runtex(rawname) + if ok then + ok = runtexutil(rawname) if getvariable('texutil') || getvariable('forcetexutil') + runbackend(rawname) + popresult(rawname,result) + end + File.silentdelete(File.suffixed(rawname,'tmp')) + File.silentrename(File.suffixed(rawname,'top'),File.suffixed(rawname,'tmp')) + else + mprundone, ok, stoprunning = false, true, false + texruns, nofruns = 0, getvariable('runs').to_i + state = FileState.new + ['tub','tuo'].each do |s| + state.register(File.suffixed(rawname,s)) + end + if getvariable('automprun') then # check this + ['mprun','mpgraph'].each do |s| + state.register(File.suffixed(rawname,s,'mp'),'randomseed') + end + end + while ! stoprunning && (texruns < nofruns) && ok do + texruns += 1 + report("TeX run #{texruns}") + if texruns == 1 then + makeoptionfile(rawname,jobname,orisuffix,false,false,1) unless getvariable('nooptionfile') + else + makeoptionfile(rawname,jobname,orisuffix,false,false,2) unless getvariable('nooptionfile') + end + ok = runtex(File.suffixed(rawname,jobsuffix)) + if ok && (nofruns > 1) then + unless getvariable('nompmode') then + mprundone = runtexmpjob(rawname, "mpgraph") + mprundone = runtexmpjob(rawname, "mprun") + end + ok = runtexutil(rawname) + state.update + stoprunning = state.stable? + end + end + ok = runtexutil(rawname) if (nofruns == 1) && getvariable('texutil') + if ok && finalrun && (nofruns > 1) then + makeoptionfile(rawname,jobname,orisuffix,true,finalrun,4) unless getvariable('nooptionfile') + report("final TeX run #{texruns}") + ok = runtex(File.suffixed(rawname,jobsuffix)) + end + ['tmp','top'].each do |s| # previous tuo file / runtime option file + File.silentdelete(File.suffixed(rawname,s)) + end + File.silentrename(File.suffixed(rawname,'top'),File.suffixed(rawname,'tmp')) + if ok then + runbackend(rawname) + popresult(rawname,result) + end + end + + Kpse.runscript('ctxtools',rawname,'--purge') if getvariable('purge') + Kpse.runscript('ctxtools',rawname,'--purgeall') if getvariable('purgeall') + + when 'latex' then + + ok = runtex(rawname) + + else + + ok = runtex(rawname) + + end + + if (dummyfile or forcexml) and FileTest.file?(rawname) then + begin + File.delete(File.suffixed(rawname,'run')) + rescue + report("unable to delete stub file") + end + end + + if ok and getvariable('autopdf') then + PDFview.open(File.suffixed(if result.empty? then rawname else result end,'pdf')) + end + + end + + end + + # mp specific + + def doruntexmp(mpname,mergebe=true,context=true) + texfound = false + mpbetex = Hash.new + mpfile = File.suffixed(mpname,'mp') + mpcopy = File.suffixed(mpname,'copy','mp') + setvariable('mp.file',mpfile) + setvariable('mp.line','') + setvariable('mp.error','') + if mpdata = File.silentread(mpfile) then + mpdata.gsub!(/^\#.*\n/o,'') + File.silentrename(mpfile,mpcopy) + texfound = mergebe || mpdata =~ /btex .*? etex/o + if mp = File.silentopen(mpfile,'w') then + mpdata.gsub!(/(btex.*?)\;(.*?etex)/o) do "#{$1}@@@#{$2}" end + mpdata.gsub!(/(\".*?)\;(.*?\")/o) do "#{$1}@@@#{$2}" end + mpdata.gsub!(/\;/o, "\;\n") + mpdata.gsub!(/\n+/o, "\n") + mpdata.gsub!(/(btex.*?)@@@(.*?etex)/o) do "#{$1}\;#{$2}" end + mpdata.gsub!(/(\".*?)@@@(.*?\")/o) do "#{$1};#{$2}" end + if mergebe then + mpdata.gsub!(/beginfig\s*\((\d+)\)\s*\;(.*?)endfig\s*\;/o) do + n, str = $1, $2 + if str =~ /(.*?)(verbatimtex.*?etex)\s*\;(.*)/o then + "beginfig(#{n})\;\n$1$2\;\n#{mpbetex(n)}\n$3\;endfig\;\n" + else + "beginfig(#{n})\;\n#{mpbetex(n)}\n#{str}\;endfig\;\n" + end + end + end + unless mpdata =~ /beginfig\s*\(\s*0\s*\)/o then + mp << mpbetex[0] if mpbetex.key?(0) + end + mp << mpdata # ?? + mp << "\n" + mp << "end" + mp << "\n" + mp.close + end + processmpx(mpname) if texfound + if getvariable('batchmode') then + options = ' --interaction=batch' + elsif getvariable('nonstopmode') then + options = ' --interaction=nonstop' + else + options = '' + end + # todo plain|mpost|metafun + ok = runmp(mpname) + if f = File.silentopen(File.suffixed(mpfile,'log')) then + while str = f.gets do + if str =~ /^l\.(\d+)\s(.*?)\n/o then + setvariable('mp.line',$1) + setvariable('mp.error',$2) + break + end + end + f.close + end + File.silentrename(mpfile,"mptrace.tmp") + File.silentrename(mpcopy, mpfile) + end + end + + def processmpx(mpname,context=true) + mpname = File.suffixed(mpname,'mp') + if File.atleast?(mpname,10) && (data = File.silentread(mpname)) then + begin + if data =~ /(btex|etex|verbatimtex)/o then + mptex = File.suffixed(mpname,'temp','tex') + mpdvi = File.suffixed(mpname,'temp','dvi') + mplog = File.suffixed(mpname,'temp','log') + mpmpx = File.suffixed(mpname,'temp','mpx') + ok = system("mpto #{mpname} > #{mptex}") + if ok && File.appended(mptex, "\\end\n") then + if context then + ok = RunConTeXtFile(mptex) + else + ok = RunSomeTeXFile(mptex) + end + ok = ok && FileTest.file?(mpdvi) && system("dvitomp #{mpdvi} #{mpmpx}") + [mptex,mpdvi,mplog].each do |mpfil| + File.silentdelete(mpfil) + end + end + end + rescue + # error in processing mpx file + end + end + end + + def checkmpgraphics(mpname) + mpoptions = '' + if getvariable('makempy') then + mpoptions += " --makempy " + end + if getvariable('mpyforce') || getvariable('forcempy') then + mpoptions += " --force " + else + mponame = File.suffixed(mpname,'mpo') + mpyname = File.suffixed(mpname,'mpy') + return false unless File.atleast?(mponame,32) + mpochecksum = State.new.checksum(mponame) + return false if mpochecksum.empty? + # where does the checksum get into the file? + # maybe let texexec do it? + # solution: add one if not present or update when different + if f = File.open(mpyname) then + str = f.gets.chomp + f.close + if str =~ /^\%\s*mpochecksum\s*\:\s*(\d+)/o then + return false if mpochecksum == $1 + end + end + end + return Kpse.runscript('makempy',mpname) + end + + def checkmplabels(mpname) + mpname = File.suffixed(mpname,'mpt') + if File.atleast?(mpname,10) && (mp = File.open(mpname)) then + labels = Hash.new + while str = mp.gets do + if str =~ /%\s*setup\s*:\s*(.*)/o then + t = $1 + else + t = '' + end + if str =~ /%\s*figure\s*(\d+)\s*:\s*(.*)/o then + unless t.empty? then + labels[$1] += "#{t}\n" + t = '' + end + labels[$1] += "$2\n" + end + end + mp.close + return labels if labels.size>0 + end + return nil + end + +end |