diff options
author | Karl Berry <karl@freefriends.org> | 2006-01-09 01:54:09 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2006-01-09 01:54:09 +0000 |
commit | 50b347972956e0bfbe7029305e0f459e5ce3ac0c (patch) | |
tree | d1b824bbc33a30bf7fcf54b866a1cff949d2e0bf /Master/texmf-dist/scripts/context/ruby/graphics | |
parent | 52f01b2f769ac290674a469d46f149985042ee2e (diff) |
trunk/Master/texmf-dist/scripts
git-svn-id: svn://tug.org/texlive/trunk@92 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts/context/ruby/graphics')
3 files changed, 905 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/context/ruby/graphics/gs.rb b/Master/texmf-dist/scripts/context/ruby/graphics/gs.rb new file mode 100644 index 00000000000..807cad10ccd --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/graphics/gs.rb @@ -0,0 +1,641 @@ +# module : graphics/gs +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# ['base/variables','../variables','variables'].each do |r| begin require r ; rescue Exception ; else break ; end ; end +# ['base/system', '../system', 'system' ].each do |r| begin require r ; rescue Exception ; else break ; end ; end + +require 'base/variables' +require 'base/system' + +class GhostScript + + include Variables + + @@pstopdfoptions = [ + 'AntiAliasColorImages', + 'AntiAliasGrayImages', + 'AntiAliasMonoImages', + 'ASCII85EncodePages', + 'AutoFilterColorImages', + 'AutoFilterGrayImages', + 'AutoPositionEPSFiles', + 'AutoRotatePages', + 'Binding', + 'ColorConversionStrategy', + 'ColorImageDepth', + 'ColorImageDownsampleThreshold', + 'ColorImageDownsampleType', + 'ColorImageFilter', + 'ColorImageResolution', + 'CompatibilityLevel', + 'CompressPages', + #'ConvertCMYKImagesToRGB', # buggy + #'ConvertImagesToIndexed', # buggy + 'CreateJobTicket', + 'DetectBlends', + 'DoThumbnails', + 'DownsampleColorImages', + 'DownsampleGrayImages', + 'DownsampleMonoImages', + 'EmbedAllFonts', + 'EncodeColorImages', + 'EncodeGrayImages', + 'EncodeMonoImages', + 'EndPage', + 'FirstPage', + 'GrayImageDepth', + 'GrayImageDownsampleThreshold', + 'GrayImageDownsampleType', + 'GrayImageFilter', + 'GrayImageResolution', + 'MaxSubsetPct', + 'MonoImageDepth', + 'MonoImageDownsampleThreshold', + 'MonoImageDownsampleType', + 'MonoImageFilter', + 'MonoImageResolution', + 'Optimize', + 'ParseDCSComments', + 'ParseDCSCommentsForDocInfo', + 'PreserveCopyPage', + 'PreserveEPSInfo', + 'PreserveHalftoneInfo', + 'PreserveOPIComments', + 'PreserveOverprintSettings', + 'SubsetFonts', + 'UseFlateCompression' + ] + + @@methods = Hash.new + + @@methods['raw'] = '1' + @@methods['bound'] = '2' + @@methods['bounded'] = '2' + @@methods['crop'] = '3' + @@methods['cropped'] = '3' + @@methods['down'] = '4' + @@methods['downsample'] = '4' + @@methods['downsampled'] = '4' + @@methods['simplify'] = '5' + @@methods['simplified'] = '5' + + @@tempfile = 'gstemp' + @@pstempfile = @@tempfile + '.ps' + @@pdftempfile = @@tempfile + '.pdf' + + @@bboxspec = '\s*([\-\d\.]+)' + '\s+([\-\d\.]+)'*3 + + def initialize(logger=nil) + + unless logger then + puts('gs class needs a logger') + exit + end + + @variables = Hash.new + @psoptions = Hash.new + @logger = logger + + setvariable('profile', 'gsprofile.ini') + setvariable('pipe', true) + setvariable('method', 2) + setvariable('force', false) + setvariable('colormodel', 'cmyk') + setvariable('inputfile', '') + setvariable('outputfile', '') + + @@pstopdfoptions.each do |key| + @psoptions[key] = '' + end + + reset + + end + + def reset + @llx = @lly = @ulx = @uly = 0 + @oldbbox = [@llx,@lly,@urx,@ury] + @width = @height = @xoffset = @yoffset = @offset = 0 + @rs = Tool.default_line_separator + end + + def supported?(filename) + psfile?(filename) || pdffile?(filename) + end + + def psfile?(filename) + filename =~ /\.(eps|epsf|ps|ai\d*)$/io + end + + def pdffile?(filename) + filename =~ /\.(pdf)$/io + end + + def setpsoption(key,value) + @psoptions[key] = value unless value.empty? + end + + def setdimensions (llx,lly,urx,ury) + @oldbbox = [llx,lly,urx,ury] + @llx, @lly = llx.to_f-@offset, lly.to_f-@offset + @urx, @ury = urx.to_f+@offset, ury.to_f+@offset + @width, @height = @urx - @llx, @ury - @lly + @xoffset, @yoffset = 0 - @llx, 0 - @lly + end + + def setoffset (offset=0) + @offset = offset.to_f + setdimensions(@llx,@lly,@urx,@ury) if dimensions? + end + + def resetdimensions + setdimensions(0,0,0,0) + end + + def dimensions? + (@width>0) && (@height>0) + end + + def convert + + inpfile = getvariable('inputfile') + + if inpfile.empty? then + report('no inputfile specified') + return false + end + + unless FileTest.file?(inpfile) then + report("unknown input file #{inpfile}") + return false + end + + outfile = getvariable('outputfile') + + if outfile.empty? then + outfile = inpfile + outfile = outfile.sub(/^.*[\\\/]/,'') + end + + outfile = outfile.sub(/\.(pdf|eps|ps|ai)/i, "") + resultfile = outfile + '.pdf' + setvariable('outputfile', resultfile) + + # flags + + saveprofile(getvariable('profile')) + + begin + gsmethod = method(getvariable('method')).to_i + report("conversion method #{gsmethod}") + rescue + gsmethod = 1 + report("fallback conversion method #{gsmethod}") + end + + debug('piping data') if getvariable('pipe') + + ok = false + begin + case gsmethod + when 0, 1 then ok = convertasis(inpfile,resultfile) + when 2 then ok = convertbounded(inpfile,resultfile) + when 3 then ok = convertcropped(inpfile,resultfile) + when 4 then ok = downsample(inpfile,resultfile,'screen') + when 5 then ok = downsample(inpfile,resultfile,'prepress') + else report("invalid conversion method #{gsmethod}") + end + rescue + report("job aborted due to some error #{$!}") + begin + File.delete(resultfile) if test(?e,resultfile) + rescue + report("unable to delete faulty #{resultfile}") + end + ok = false + ensure + deleteprofile(getvariable('profile')) + File.delete(@@pstempfile) if test(?e,@@pstempfile) + File.delete(@@pdftempfile) if test(?e,@@pdftempfile) + end + return ok + end + + # private + + def method (str) + if @@methods.key?(str) then + @@methods[str] + else + str + end + end + + def pdfmethod? (str) + case method(str).to_i + when 4, 5 then return true + end + return false + end + + def pdfprefix (str) + case method(str).to_i + when 4 then return 'lowres-' + when 5 then return 'normal-' + end + return '' + end + + def psmethod? (str) + ! pdfmethod?(str) + end + + def insertprofile (flags) + for key in flags.keys do + replacevariable("flag.#{key}", flags[key]) + end + end + + def deleteprofile (filename) + begin + File.delete(filename) if FileTest.file?(filename) + rescue + end + end + + def saveprofile (filename) + return if filename.empty? || ! (ini = open(filename,"w")) + @@pstopdfoptions.each do |k| + str = @psoptions[k] + # beware, booleans are translated, but so are yes/no which is dangerous + if str.class == String then + if ! str.empty? && (str != 'empty') then + str.sub!(/(.+)\-/io, '') + str = "/" + str unless str =~ /^(true|false|none|[\d\.\-\+]+)$/ + ini.puts("-d#{k}=#{str}\n") + end + end + end + ini.close + debug("gs profile #{filename} saved") + end + + def gsstream # private + if getvariable('pipe') then '-' else @@pstempfile end + end + + def gscolorswitch + case getvariable('colormodel') + when 'cmyk' then '-dProcessColorModel=/DeviceCMYK ' + when 'rgb' then '-dProcessColorModel=/DeviceRGB ' + when 'gray' then '-dProcessColorModel=/DeviceGRAY ' + else + '' + end + end + + def gsdefaults + defaults = '' + begin + defaults << '-dAutoRotatePages=/None ' if @psoptions['AutoRotatePages'].empty? + rescue + defaults << '-dAutoRotatePages=/None ' + end + return defaults + end + + def convertasis (inpfile, outfile) + + report("converting #{inpfile} as-is") + + @rs = Tool.line_separator(inpfile) + debug("platform mac") if @rs == "\r" + + arguments = '' + arguments << "\@gsprofile.ini " + arguments << "-q -sDEVICE=pdfwrite -dNOPAUSE -dNOCACHE -dBATCH " + arguments << "#{gsdefaults} " + arguments << "#{gscolorswitch} " + arguments << "-sOutputFile=#{outfile} #{inpfile} -c quit " + + debug("ghostscript: #{arguments}") + unless ok = System.run('ghostscript',arguments) then + begin + report("removing file #{outfile}") + File.delete(outfile) if FileTest.file?(outfile) + rescue + debug("file #{outfile} may be invalid") + end + end + return ok + + end + + def convertbounded (inpfile, outfile) + + report("converting #{inpfile} bounded") + + begin + return false if FileTest.file?(outfile) && (! File.delete(outfile)) + rescue + return false + end + + arguments = '' + arguments << "\@gsprofile.ini " + arguments << "-q -sDEVICE=pdfwrite -dNOPAUSE -dNOCACHE -dBATCH -dSAFER" + arguments << "#{gscolorswitch} " + arguments << "#{gsdefaults} " + arguments << "-sOutputFile=#{outfile} #{gsstream} -c quit " + + debug("ghostscript: #{arguments}") + debug('opening input file') + + @rs = Tool.line_separator(inpfile) + debug("platform mac") if @rs == "\r" + + return false unless tmp = open(inpfile, 'rb') + + debug('opening pipe/file') + + if getvariable('pipe') then + + return false unless eps = IO.popen(System.command('ghostscript',arguments),'wb') + debug('piping data') + unless pipebounded(tmp,eps) then + debug('something went wrong in the pipe') + File.delete(outfile) if test(?e,outfile) + end + debug('closing pipe') + eps.close_write + + else + + return false unless eps = File.open(@@pstempfile, 'wb') + + debug('copying data') + + if pipebounded(tmp,eps) then + eps.close + debug('processing temp file') + begin + ok = System.run('ghostscript',arguments) + rescue + ok = false + # debug("fatal error: #{$!}") + ensure + end + else + eps.close + ok = false + end + + unless ok then + begin + report('no output file due to error') + File.delete(outfile) if test(?e,outfile) + rescue + # debug("fatal error: #{$!}") + debug('file',outfile,'may be invalid') + end + end + + debug('deleting temp file') + begin + File.delete(@@pstempfile) if test(?e,@@pstempfile) + rescue + end + + end + + tmp.close + return FileTest.file?(outfile) + + end + + # hm, strange, no execute here, todo ! ! ! + + def getdimensions (inpfile) + + # -dEPSFitPage and -dEPSCrop behave weird (don't work) + + arguments = "-sDEVICE=bbox -dSAFER -dNOPAUSE -dBATCH #{inpfile}" + + debug("ghostscript: #{arguments}") + + begin + bbox = System.run('ghostscript',arguments,true,true) + rescue + bbox = '' + end + + resetdimensions + + debug('bbox spec', bbox) + + if bbox =~ /(Exact|HiRes)BoundingBox:#{@@bboxspec}/mois then + debug("high res bbox #{$2} #{$3} #{$4} #{$5}") + setdimensions($2,$3,$4,$5) + elsif bbox =~ /BoundingBox:#{@@bboxspec}/mois + debug("low res bbox #{$1} #{$2} #{$3} #{$4}") + setdimensions($1,$2,$3,$4) + end + + return dimensions? + + end + + def convertcropped (inpfile, outfile) + + report("converting #{inpfile} cropped") + + convertbounded(inpfile, @@pdftempfile) + + return unless test(?e,@@pdftempfile) + + arguments = " --offset=#{@offset} #{@@pdftempfile} #{outfile}" + + unless ok = System.run('cropcrap',arguments) then + report('cropping failed') + begin + File.delete(outfile) + rescue + end + begin + File.move(@@pdftempfile,outfile) + rescue + File.copy(@@pdftempfile,outfile) + File.delete(@@pdftempfile) + end + end + + return ok + + end + + def pipebounded (eps, out) + + epsbbox, skip, buffer = false, false, '' + + while str = eps.gets(rs=@rs) do + if str =~ /^%!PS/oi then + debug("looks like a valid ps file") + break + elsif str =~ /%PDF\-\d+\.\d+/oi then + debug("looks like a pdf file, so let\'s quit") + return false + end + end + + debug('locating boundingbox') + + # why no BeginData check + + eps.rewind + + while str = eps.gets(rs=@rs) do + case str + when /^%%Page:/io then + break + when /^%%(Crop|HiResBounding|ExactBounding)Box:#{@@bboxspec}/moi then + debug('high res boundingbox found') + setdimensions($2,$3,$4,$5) + break + when /^%%BoundingBox:#{@@bboxspec}/moi then + debug('low res boundingbox found') + setdimensions($1,$2,$3,$4) + end + end + + debug('no boundingbox found') if @width == 0 + + eps.rewind + + while str = eps.gets(rs=@rs) do + if str.sub!(/^(.*)%!PS/moi, "%!PS") then + debug("removing pre banner data") + out.puts(str) + break + end + end + + while str = eps.gets(rs=@rs) do + if skip then + skip = false if str =~ /^%+(EndData|EndPhotoshop|BeginProlog).*$/o + out.puts(str) if $1 == "BeginProlog" + elsif str =~ /^%(BeginPhotoshop)\:\s*\d+.*$/o then + skip = true + elsif str =~ /^%%/mos then + if ! epsbbox && str =~ /^%%(Page:|EndProlog)/io then + out.puts(str) if $1 == "EndProlog" + debug('faking papersize') + out.puts("<< /PageSize [#{@width} #{@height}] >> setpagedevice\n") + out.puts("gsave #{@xoffset} #{@yoffset} translate\n") + epsbbox = true + elsif str =~ /^%%BeginBinary\:\s*\d+\s*$/o then + debug('copying binary data') + out.puts(str) + while str = eps.gets(rs=@rs) + if str =~ /^%%EndBinary\s*$/o then + out.puts(str) + else + out.write(str) + end + end + elsif str =~ /^%AI9\_PrivateDataBegin/o then + debug('ignore private ai crap') + break + elsif str =~ /^%%EOF/o then + debug('ignore post eof crap') + break + # elsif str =~ /^%%PageTrailer/o then + # debug('ignoring post page trailer crap') + # break + elsif str =~ /^%%Trailer/o then + debug('ignoring post trailer crap') + break + elsif str =~ /^%%Creator.*Illustrator.*$/io then + debug('getting rid of problematic creator spec') + str = "% Creator: Adobe Illustrator ..." + out.puts(str) + elsif str =~ /^%%AI.*(PaperRect|Margin)/io then + debug('removing AI paper crap') + elsif str =~ /^%%AI.*Version.*$/io then + debug('removing dangerous version info') + elsif str =~ /^(%+AI.*Thumbnail.*)$/o then + debug('skipping AI thumbnail') + skip = true + else + out.puts(str) + end + else + out.puts(str) + end + end + + debug('done, sending EOF') + + out.puts "grestore\n%%EOF\n" + + # ok = $? == 0 + # report('process aborted, broken pipe, fatal error') unless ok + # return ok + + return true + + end + + def downsample (inpfile, outfile, method='screen') + + # gs <= 8.50 + + report("downsampling #{inpfile}") + + doit = true + unless getvariable('force') then + begin + if f = File.open(inpfile) then + f.binmode + while doit && (data = f.gets) do + if data =~ /\/ArtBox\s*\[\s*[\d\.]+\s+[\d\.]+\s+[\d\.]+\s+[\d\.]+\s*\]/io then + doit = false + end + end + f.close + end + rescue + end + end + + if doit then + arguments = '' + arguments << "-dPDFSETTINGS=/#{method} -dEmbedAllFonts=true " + arguments << "#{gscolorswitch} " + arguments << "#{gsdefaults} " + arguments << "-q -sDEVICE=pdfwrite -dNOPAUSE -dNOCACHE -dBATCH -dSAFER " + arguments << "-sOutputFile=#{outfile} #{inpfile} -c quit " + unless ok = System.run('ghostscript',arguments) then + begin + File.delete(outfile) if FileTest.file?(outfile) + report("removing file #{outfile}") + rescue + debug("file #{outfile} may be invalid") + end + end + return ok + else + report("crop problem, straight copying #{inpfile}") + File.copy(inpfile,outfile) + return false + end + + end + +end diff --git a/Master/texmf-dist/scripts/context/ruby/graphics/inkscape.rb b/Master/texmf-dist/scripts/context/ruby/graphics/inkscape.rb new file mode 100644 index 00000000000..4495c3070f4 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/graphics/inkscape.rb @@ -0,0 +1,103 @@ +# module : graphics/inkscape +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# ['base/variables','variables'].each do |r| begin require r ; rescue Exception ; else break ; end ; end +# ['graphics/gs','gs'].each do |r| begin require r ; rescue Exception ; else break ; end ; end + +require 'base/variables' +require 'base/system' +require 'graphics/gs' + +class InkScape + + include Variables + + def initialize(logger=nil) + + unless logger then + puts('inkscape class needs a logger') + exit + end + + @variables = Hash.new + @logger = logger + + reset + + end + + def reset + # nothing yet + end + + def supported?(filename) + filename =~ /.*\.(svg|svgz)/io + end + + def convert(logfile=System.null) + + inpfilename = getvariable('inputfile').dup + outfilename = getvariable('outputfile').dup + outfilename = inpfilename.dup if outfilename.empty? + outfilename.gsub!(/(\.[^\.]*?)$/, ".pdf") + tmpfilename = outfilename.gsub(/(\.[^\.]*?)$/, ".ps") + + if inpfilename.empty? || outfilename.empty? then + report("no filenames given") + return false + end + if inpfilename == outfilename then + report("filenames must differ (#{inpfilename} #{outfilename})") + return false + end + unless FileTest.file?(inpfilename) then + report("unknown file #{inpfilename}") + return false + end + + report("converting #{inpfilename} to #{tmpfilename}") + + # we need to redirect the error info else we get a pop up console + + resultpipe = "--without-gui --print=\">#{tmpfilename}\" 2>#{logfile}" + + arguments = [resultpipe,inpfilename].join(' ').gsub(/\s+/,' ') + + ok = true + begin + debug("inkscape: #{arguments}") + # should work + # ok = System.run('inkscape',arguments) # does not work here + # but 0.40 only works with this: + ok = system("inkscape #{arguments}") + # and 0.41 fails with everything + rescue + report("aborted due to error") + return false + else + return false unless ok + end + + ghostscript = GhostScript.new(@logger) + + ghostscript.setvariable('inputfile',tmpfilename) + ghostscript.setvariable('outputfile',outfilename) + + report("converting #{tmpfilename} to #{outfilename}") + + ghostscript.convert + + begin + File.delete(tmpfilename) + rescue + end + end + +end diff --git a/Master/texmf-dist/scripts/context/ruby/graphics/magick.rb b/Master/texmf-dist/scripts/context/ruby/graphics/magick.rb new file mode 100644 index 00000000000..f59087bdf44 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/graphics/magick.rb @@ -0,0 +1,161 @@ +# module : graphics/inkscape +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# ['base/variables','variables'].each do |r| begin require r ; rescue Exception ; else break ; end ; end + +require 'base/variables' + +class ImageMagick + + include Variables + + def initialize(logger=nil) + + unless logger then + puts('magick class needs a logger') + exit + end + + @variables = Hash.new + @logger = logger + + reset + + end + + def reset + ['compression','depth','colorspace','quality'].each do |key| + setvariable(key) + end + end + + def supported?(filename) # ? pdf + filename =~ /.*\.(png|gif|tif|tiff|jpg|jpeg|eps|ai\d*)/io + end + + def convert(suffix='pdf') + + inpfilename = getvariable('inputfile').dup + outfilename = getvariable('outputfile').dup + outfilename = inpfilename.dup if outfilename.empty? + outfilename.gsub!(/(\.[^\.]*?)$/, ".#{suffix}") + + if inpfilename.empty? || outfilename.empty? then + report("no filenames given") + return false + end + if inpfilename == outfilename then + report("filenames must differ (#{inpfilename} #{outfilename})") + return false + end + unless FileTest.file?(inpfilename) then + report("unknown file #{inpfilename}") + return false + end + + if inpfilename =~ /\.tif+$/io then + tmpfilename = 'temp.png' + arguments = "#{inpfilename} #{tmpfilename}" + begin + debug("imagemagick: #{arguments}") + ok = System.run('imagemagick',arguments) + rescue + report("aborted due to error") + return false + else + return false unless ok + end + inpfilename = tmpfilename + end + + compression = depth = colorspace = quality = '' + + if getvariable('compression') =~ /(zip|jpeg)/o then + compression = " -compress #{$1}" + end + if getvariable('depth') =~ /(8|16)/o then + depth = "-depth #{$1}" + end + if getvariable('colorspace') =~ /(gray|rgb|cmyk)/o then + colorspace = "-colorspace #{$1}" + end + case getvariable('quality') + when 'low' then quality = '-quality 0' + when 'medium' then quality = '-quality 75' + when 'high' then quality = '-quality 100' + end + + report("converting #{inpfilename} to #{outfilename}") + + arguments = [compression,depth,colorspace,quality,inpfilename,outfilename].join(' ').gsub(/\s+/,' ') + + begin + debug("imagemagick: #{arguments}") + ok = System.run('imagemagick',arguments) + rescue + report("aborted due to error") + return false + else + return ok + end + + end + + def autoconvert + + inpfilename = getvariable('inputfile') + outfilename = getvariable('outputfile') + + if inpfilename.empty? || ! FileTest.file?(inpfilename) then + report("missing file #{inpfilename}") + return + end + + outfilename = inpfilename.dup if outfilename.empty? + tmpfilename = 'temp.jpg' + + reset + + megabyte = 1024*1024 + + ok = false + + if FileTest.size(inpfilename)>2*megabyte + setvariable('compression','zip') + ok = convert + else + setvariable('compression','jpeg') + if FileTest.size(inpfilename)>10*megabyte then + setvariable('quality',85) + elsif FileTest.size(inpfilename)>5*megabyte then + setvariable('quality',90) + else + setvariable('quality',95) + end + report("auto quality #{getvariable('quality')}") + setvariable('outputfile', tmpfilename) + ok = convert('jpg') + setvariable('inputfile', tmpfilename) + setvariable('outputfile', outfilename) + ok = convert + begin + File.delete(tmpfilename) + rescue + report("#{tmpfilename} cannot be deleted") + end + end + + reset + + return ok + + end + +end |