# 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' 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', 'allpatterrns', 'nompmode', 'nomprun', 'automprun', 'nomapfiles', 'local', 'arrange', 'noarrange', 'forcexml', 'foxet', 'mpyforce', 'forcempy', 'forcetexutil', 'texutil', 'globalfile', 'autopath', 'purge', 'pdfopen', 'simplerun', 'verbose', ] @@stringvars = [ 'modefile', 'result', 'suffix', 'response', 'path', 'filters', 'usemodules', 'environments', 'separation', 'setuppath', 'arguments', 'input', 'output', 'randomseed', 'modes', 'filename', 'modefile', ] @@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) 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() "--ini" end def tcxflag() "--translate-file=natural.tcx" 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'], ['ctx','ctxfilter'],['translate','ctxfilter'], ['output','backend'],['modes','modes'],['version','contextversion'], ['format','texformat'],['interface','texformat'] ] def scantexpreamble(filename) if tex = File.open(filename) then while str = tex.gets.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 end def scantexcontent(filename) if 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 #{result}") ['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(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(jobname,jobsuffix,forcexml=false) if tmp = File.open(File.suffixed(jobname,'run'),'w') then fullname = File.suffixed(jobname,jobsuffix) tmp << "\\starttext\n" if forcexml then if FileTest.file?(fullname) && (xml = File.open(fullname)) then xml.each do |line| if line =~ /<[a-z]+/io then break elsif line =~ /<\?context\-directive\s+(.+?)\s+(.+?)\s+(.+?)\s*\?>/o then category, key, value = $1, $2, $3 case category when 'job' then case key when 'stylefile', 'environment' then tmp << "\\environment $value\n" when 'module' then tmp << "\\usemodule[$value]\n" when 'interface' then contextinterface = value end end end end end xml.close end tmp << "\\processXMLfilegrouped{#{fullname}}\n" else tmp << "\\processfile{#{fullname}}\n" end tmp << "\\stoptext\n" tmp.close jobsuffix = "run" end return jobsuffix 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 makeoptionfile(jobname, jobsuffix, finalrun, fastdisabled, kindofrun) if opt = File.open(File.suffixed(jobname,'top'),'w') then # local handies opt << "\% $JobName.top\n" opt << "\\unprotect\n" opt << "\\setupsystem[\\c!n=#{kindofrun}]\n" opt << "\\def\\MPOSTformatswitch\{#{prognameflag('metafun')} #{formatflag('mpost')}=\}" 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=#{File.suffixed(jobname,str,nil)}]\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('silent') 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=#{File.suffixed(jobname,jobsuffix)}]\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(',').each do |f| opt << "\\useXMLfilter[#{f}]\n" end rescue end begin getvariable('usemodules').split(',').each do |m| opt << "\\usemodule[#{m}]\n" end rescue end begin getvariable('environments').split(',').each do |e| opt << "\\usemodule[#{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' else ENV['SHELL_ESCAPE'] = ENV['SHELL_ESCAPE'] || 't' ENV['OPENOUT_ANY'] = ENV['OPENOUT_ANY'] || 'p' ENV['OPENIN_ANY'] = ENV['OPENIN_ANY'] || 'a' end if (ENV['OPENIN_ANY'] == 'p') || (ENV['OPENOUT_ANY'] == 'p') then 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']) begin filename.each do |fname| logger = Logger.new('TeXUtil') if tu = TeXUtil::Converter.new(logger) and tu.loaded(fname) then tu.saved if tu.processed end end rescue Kpse.runscript('texutil',filename,options) 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(jobname) case validbackend(getvariable('backend')) when 'dvipdfmx' then fixbackendvars('dvipdfm') system("dvipdfmx -d 4 #{File.unsuffixed(jobname)}") when 'xetex' then fixbackendvars('xetex') system("xdv2pdf #{File.suffixed(jobname,'xdv')}") when 'dvips' then fixbackendvars('dvips') mapfiles = '' begin if tuifile = File.suffixed(jobname,'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(jobname)}") when 'pdftex' then # no need for postprocessing else report("no postprocessing needed") end end def processfile takeprecautions jobname = getvariable('filename') suffix = getvariable('suffix') result = getvariable('result') runonce = getvariable('once') finalrun = getvariable('final') || (getvariable('arrange') && ! getvariable('noarrange')) 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('pdfopen') forcexml = jobsuffix === /(xml|fo|fox|rlg|exa|)/io # === returns true|false, =~ returns position dummyfile = false # fuzzy code snippet: (we kunnen kpse: prefix gebruiken) unless FileTest.file?(File.suffixed(jobname,jobsuffix)) then inppath.split(',').each do |ip| break if dummyfile = FileTest.file?(File.join(ip,File.suffixed(jobname,jobsuffix))) end end jobsuffix = makestubfile(jobname,jobsuffix,forcexml) if dummyfile || forcexml if getvariable('globalfile') || FileTest.file?(File.suffixed(jobname,jobsuffix)) then unless dummyfile # we don't need this for xml scantexpreamble(File.suffixed(jobname,jobsuffix)) scantexcontent(File.suffixed(jobname,jobsuffix)) if getvariable('texformats').standard? end result = File.suffixed(jobname,suffix) unless suffix.empty? pushresult(jobname,result) method = validtexmethod(validtexformat(getvariable('texformats'))) report("tex processing method: #{method}") case method when 'context' then if getvariable('simplerun') || runonce then makeoptionfile(jobname,orisuffix,true,true,3) problems = runtex(File.suffixed(jobname,jobsuffix)) unless problems then ok = runtexutil(jobname) if getvariable('texutil') || getvariable('forcetexutil') runbackend(jobname) popresult(jobname,result) end File.silentrename(File.suffixed(jobname,'top'),File.suffixed(jobname,'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(jobname,s)) end if getvariable('automprun') then # check this ['mprun','mpgraph'].each do |s| state.register(File.suffixed(jobname,s,'mp'),'randomseed') # state.register(File.suffixed(jobname,s,'mpo')) end end while ! stoprunning && (texruns < nofruns) && ok do texruns += 1 report("TeX run #{texruns}") if texruns == 1 then makeoptionfile(jobname,orisuffix,false,false,1) else makeoptionfile(jobname,orisuffix,false,false,2) end ok = runtex(File.suffixed(jobname,jobsuffix)) if ok && (nofruns > 1) then unless getvariable('nompmode') then mprundone = runtexmpjob(jobname, "mpgraph") mprundone = runtexmpjob(jobname, "mprun") end ok = runtexutil(jobname) state.update stoprunning = state.stable? end end ok = runtexutil(jobname) if (nofruns == 1) && getvariable('texutil') if ! problems && finalrun && (nofruns > 1) then makeoptionfile(jobname,orisuffix,true,finalrun,4) report("final TeX run #{texruns}") problems = runtex(File.suffixed(jobname,jobsuffix)) end File.silentcopy(File.suffixed(jobname,'top'),File.suffixed(jobname,'tmp')) ['tup','top'].each do |s| # previous tuo file / runtime option file File.silentdelete(File.suffixed(jobname,s)) end unless problems then runbackend(jobname) popresult(jobname,result) end end Kpse.runscript('ctxtools',jobname,'--purge') if getvariable('purge') when 'latex' then problems = runtex(File.suffixed(jobname,jobsuffix)) else problems = runtex(File.suffixed(jobname,jobsuffix)) end begin File.delete(File.suffixed(jobname,jobsuffix)) if dummyfile || forcexml rescue report("unable to delete stub file") end if ! problems && getvariable('pdfopen') then PDFview.open(if resultname.empty? then jobname else resultname end) 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