summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/epspdf/epspdf.rb
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/scripts/epspdf/epspdf.rb')
-rwxr-xr-xMaster/texmf-dist/scripts/epspdf/epspdf.rb1479
1 files changed, 0 insertions, 1479 deletions
diff --git a/Master/texmf-dist/scripts/epspdf/epspdf.rb b/Master/texmf-dist/scripts/epspdf/epspdf.rb
deleted file mode 100755
index 47a0c1f65b9..00000000000
--- a/Master/texmf-dist/scripts/epspdf/epspdf.rb
+++ /dev/null
@@ -1,1479 +0,0 @@
-#!/usr/bin/env ruby
-
-# epspdf conversion utility, main source
-
-#####
-# Copyright (C) 2006, 2008, 2009, 2010, 2011 Siep Kroonenberg
-# n dot s dot kroonenberg at rug dot nl
-#
-# This program is free software, licensed under the GNU GPL, >=2.0.
-# This software comes with absolutely NO WARRANTY. Use at your own risk!
-#####
-
-# Operations on a PostScript- or pdf file.
-# The focus is on converting between eps and pdf.
-
-# Code is organized as a set of single-step conversion methods plus an
-# any-to-any conversion chaining them together.
-
-# `Target' is not a parameter; all conversions write to a new temp
-# file. Conversions can be chained: object.conversion1( params
-# ).conversion2( params ) ... The final temp file is moved or copied to
-# the desired destination by the main program (which can be epspdf.rb
-# itself).
-
-# The use of exception handling makes it unnecessary to inspect return
-# values.
-
-###########################################
-
-# some initialization
-
-# add directory of this source to loadpath
-# WARNING
-# readlink apparently only works right from the directory of the symlink
-
-$SCRIPTDIR = File.dirname( File.expand_path( __FILE__ ))
-if RUBY_PLATFORM !~ /win32|mingw/ and File.symlink?( __FILE__ )
- savedir = Dir.pwd
- Dir.chdir( $SCRIPTDIR )
- # puts File.readlink( __FILE__ )
- $SCRIPTDIR = File.dirname( File.expand_path( File.readlink( __FILE__ )))
- Dir.chdir( savedir )
-end
-$:.unshift( $SCRIPTDIR )
-# puts $:
-
-# turn on warnings
-#$VERBOSE = 1
-
-$from_gui = nil
-
-
-###########################################
-
-# Error handling
-
-# internal error: method should not have been called
-# under the current conditions
-class EPCallError < StandardError; end
-
-# Can't get a valid boundingbox for an eps file
-class EPBBError < StandardError; end
-
-# copying failed
-class EPCopyError < StandardError; end
-
-# system call failed
-class EPSystemError < StandardError; end
-
-###########################################
-
-# PostScript header file for grayscaling
-
-$GRAYHEAD = File.join( $SCRIPTDIR, "makegray.pro" )
-
-###########################################
-
-# handle auto-detected and saved settings
-
-#require 'epspdfrc'
-require 'epspdfrc'
-
-###########################################
-
-# Transient options. These will never be saved.
-# We set default values here.
-
-$options = {
- 'type' => nil,
- 'page' => nil,
- 'bbox' => false,
- 'gray' => false,
- 'gRAY' => false,
- 'info' => false
-}
-
-class << $options
-
- # create shortcut methods $options.x and $options.x=
- # for reading and writing hash elements.
-
- $options.each_key { |k|
- eval "def #{k} ; self[\'#{k}\'] ; end"
- eval "def #{k}=(v) ; self[\'#{k}\']=v ; end"
- }
-
-end # class
-
-###########################################
-
-def hash_prn( h )
- if h.empty?
- s = "No parameters" + $/
- else
- s = "Parameters were:" + $/
- h.each_key { |k| s = s + ' ' + k.to_s + ' => ' + h[ k ].to_s + $/ }
- end
- s
-end
-
-###########################################
-
-require 'fileutils'
-#include FileUtils::Verbose
-include FileUtils
-
-###########################################
-
-# Mode strings for file i/o
-
-if ARCH == 'w32'
- $W = 'wb'
- $A = 'ab'
- $R = 'rb'
-else
- $W = 'w'
- $A = 'a'
- $R = 'r'
-end
-
-###########################################
-
-# copy a slice of a file; is there no such standard function?
-# some eps files are very large; don't slurp the file at one go.
-# Mode: can be set to append 'a' rather than write 'w'.
-
-def sliceFile( source, dest, len, offs, mode=$W )
- buffer = ''
- File.open( source ) do |s|
- s.binmode if ARCH == 'w32'
- s.seek( offs, IO::SEEK_SET )
- if s.pos == offs
- begin
- File.open( dest, mode ) do |d|
- d.binmode if ARCH == 'w32'
- tocopy = len
- while tocopy>0 and s.read( [ tocopy, 16384 ].min, buffer )
- tocopy = tocopy - d.write( buffer )
- end # while
- end # do
- rescue
- fail EPCopyError, "Failure to copy to #{dest}"
- end
- end # if s.seek
- end # |s| (automatic closing)
- # return value true if anything has been copied
- File.size?( dest ) # nil if zero-length
-end # def
-
-# write our own file copy, to bypass bug in FileUtils.cp
-
-def ccp( source, dest )
- sliceFile( source, dest, File.size( source ), 0, $W )
-end
-
-###########################################
-
-# logging
-
-def write_log( s )
- if test( ?e, LOGFILE ) and File.size( LOGFILE ) > 100000
- rm( LOGFILE_OLD ) if test( ?e, LOGFILE_OLD )
- ccp( LOGFILE, LOGFILE_OLD )
- File.truncate( LOGFILE, 0 )
- end
- File.open( LOGFILE, 'a' ) { |f|
- f.print( "#{$$} #{Time.now.strftime('%Y/%m/%d %H:%M:%S')} #{s}\n" )
- }
- puts( s ) if $from_gui
-
-end
-
-###########################################
-
-require 'tmpdir'
-
-#$DEBUG=1
-
-# save filenames for cleanup at end
-$tempfiles = []
-
-def mktemp( ext )
- isdone = nil
- (0..99).each do |i|
- fname = Dir.tmpdir + File::SEPARATOR + \
- sprintf( '%d_%02d.%s', $$, i, ext ) # $$ is process id
- next if test( ?e, fname )
- File.open( fname, 'w' ) do |f|; end # creates empty file
- isdone = 1
- $tempfiles.unshift( fname )
- return fname
- end # each do |i|
- fail StandardError, "Cannot create temp file" unless isdone
-end # def
-
-def cleantemp
- write_log( "Cleaning tempfiles" + $/ + $tempfiles.join( $/ ) )
- $tempfiles.each{ |tf|; rm( tf ) }
-end
-
-###########################################
-
-# identifying file type
-
-# EPS with preview starts with 0xC5D0D3C6
-# Encapsulated PostScript starts with %!PS-Adobe-n.n EPSF-n.n
-# but %!PS-Adobe-n.n will also be identified as Encapsulated PostScript
-# if the filename extension suggests it.
-# PostScript starts with %!
-# PDF starts with %PDF-version
-
-def identify( path )
- filestart = nil
- File.open( path, $R ) do |f|
- filestart = f.read( 23 )
- end # this syntax automatically closes the file at end of block
- case filestart
- when /^\xc5\xd0\xd3\xc6/
- 'epsPreview'
- when /^%!PS-Adobe-\d\.\d EPSF-\d\.\d/
- 'eps'
- when /^%!PS-Adobe-\d\.\d/
- ( path =~ /\.ep(i|s|si|sf)$/i ) ? 'eps' : 'ps'
- when /^%!/
- 'ps'
- when /^%PDF/
- 'pdf'
- else
- 'other'
- end # case
-end # def
-
-###########################################
-
-# Boundingboxes; first standard, then hires
-
-#changes:
-#hires boundingboxes
-#numbers may start with a plus-sign. DSC manual is ambiguous,
-# PostScript manual allows it, but of course PS != DSC
-#left-right and lower-upper need not be in natural order
-
-BB_PAT = /^\s*%%BoundingBox:\s*([-+]?\d+)((\s+[-+]?\d+){3})\s*$/
-BB_END = /^\s*%%BoundingBox:\s*\(\s*atend\s*\)\s*$/
-
-class Bb
-
- attr_accessor :llx, :lly, :urx, :ury # strings
-
- def initialize( llx, lly, urx, ury )
- @llx, @lly, @urx, @ury = llx, lly, urx, ury
- # guarantee valid syntax:
- [@llx, @lly, @urx, @ury].each { |l| l = l.to_i.to_s }
- @llx, @urx = @urx, @llx if @llx.to_i > @urx.to_i
- @lly, @ury = @ury, @lly if @lly.to_i > @ury.to_i
- end
-
- def Bb.from_comment( s )
- return nil unless s =~ BB_PAT
- llx, lly, urx, ury =
- s.sub( /^\s*%%BoundingBox:\s*/, '' ).split( /\s+/ )
- Bb.new( llx, lly, urx, ury )
- end
-
- def height
- ( @ury.to_i - @lly.to_i ).to_s
- end
-
- def width
- ( @urx.to_i - @llx.to_i ).to_s
- end
-
- def valid
- @llx.to_i < @urx.to_i and @lly.to_i < @ury.to_i
- end
-
- def non_negative
- valid and @llx.to_i >= 0 and @lly.to_i >= 0
- end
-
- def copy
- Bb.new( @llx, @lly, @urx, @ury )
- end
-
- def prn
- "#{@llx} #{@lly} #{@urx} #{@ury}"
- end
-
- def expand
- i = ( $settings.bb_spread ).to_i
- return if i <= 0
- @llx = ( [ 0, @llx.to_i - i ].max ).to_s
- @lly = ( [ 0, @lly.to_i - i ].max ).to_s
- @urx = ( @urx.to_i + i ).to_s
- @ury = ( @ury.to_i + i ).to_s
- end
-
- ##################
-
- # wrapper code for a boundingbox;
- # moves lower left corner of eps to (0,0)
- # and defines a page size identical to the eps width and height.
- # The gsave in the wrapper code should be matched by
- # a grestore at the end of the PostScript code.
- # This grestore can be specified on the Ghostscript command-line.
-
- def wrapper
- fail EPBBError, prn unless valid
- fname = mktemp( 'ps' )
- File.open( fname, $W ) do |f|
- f.binmode if ARCH == 'w32'
- f.write( "%%BoundingBox: 0 0 #{width} #{height}\n" +
- "<< /PageSize [#{width} #{height}] >>" +
- " setpagedevice\n" +
- "gsave #{(-(@llx.to_i)).to_s} #{(-(@lly.to_i)).to_s}" +
- " translate\n" ) > 0
- end # open
- return fname
- end # wrapper
-
- ##################
-
- # convert boundingbox to boundingbox comment
-
- def comment
- fail EPBBError, prn unless valid
- "%%BoundingBox: #{@llx} #{@lly} #{@urx} #{@ury}"
- end
-
-end # class Bb
-
-# [-+](\d+(\.\d*)?|\.\d+)([eE]\d+)? PostScript number
-HRBB_PAT = /^\s*%%HiResBoundingBox:\s*[-+]?(\d+(\.\d*)?|\.\d+)([eE]\d+)?((\s[-+]?(\d+(\.\d*)?|\.\d+)([eE]\d+)?){3})\s*$/
-HRBB_END = /^\s*%%HiResBoundingBox:\s*\(\s*atend\s*\)\s*$/
-
-class HRBb
-
- attr_accessor :llx, :lly, :urx, :ury
-
- def initialize( llx, lly, urx, ury )
- @llx, @lly, @urx, @ury = llx, lly, urx, ury
- [@llx, @lly, @urx, @ury].each do |l|
- if l =~ /\./
- # make floats conform to Ruby syntax:
- # decimal dots must be padded with digits on either side
- l.sub!( /^\./, '0.' )
- l.sub!( /\.(?!\d)/, '.0' ) # (?!\d): zero-width neg. lookahead
- end
- l = l.to_f.to_s
- end
- @llx, @urx = @urx, @llx if @llx.to_f > @urx.to_f
- @lly, @ury = @ury, @lly if @lly.to_f > @ury.to_f
- end
-
- def HRBb.from_hrcomment( s )
- return nil unless s =~ HRBB_PAT
- llx, lly, urx, ury =
- s.sub( /^\s*%%HiResBoundingBox:\s*/, '' ).split( /\s+/ )
- HRBb.new( llx, lly, urx, ury )
- end
-
- def height
- ( @ury.to_f - @lly.to_f ).to_s
- end
-
- def width
- ( @urx.to_f - @llx.to_f ).to_s
- end
-
- def valid
- @llx.to_f < @urx.to_f and @lly.to_f < @ury.to_f
- end
-
- def non_negative
- valid and @llx.to_f >= 0 and @lly.to_f >= 0
- end
-
- def copy
- HRBb.new( @llx, @lly, @urx, @ury )
- end
-
- def prn
- "#{@llx} #{@lly} #{@urx} #{@ury}"
- end
-
- ##################
-
- # wrapper code for a hires boundingbox;
- # moves lower left corner of eps to (0,0)
- # and defines a page size identical to the eps width and height.
- # The gsave in the wrapper code should be matched by
- # a grestore at the end of the PostScript code.
- # This grestore can be specified on the Ghostscript command-line.
-
- def wrapper
- fail EPBBError, prn unless valid
- fname = mktemp( 'ps' )
- File.open( fname, $W ) do |f|
- f.binmode if ARCH == 'w32'
- f.write(
- "%%BoundingBox: 0 0 #{width.to_f.ceil} #{height.to_f.ceil}\n" +
- "%%HiResBoundingBox: 0 0 #{width.to_f} #{height.to_f}\n" +
- "<< /PageSize [#{width} #{height}] >>" +
- " setpagedevice\n" +
- "gsave #{(-(@llx.to_f)).to_s} #{(-(@lly.to_f)).to_s}" +
- " translate\n" ) > 0
- end # open
- return fname
- end # wrapper
-
- ##################
-
- # convert hiresboundingbox to hires boundingbox comment
-
- def hrcomment
- fail EPBBError, prn unless valid
- "%%HiResBoundingBox: #{@llx} #{@lly} #{@urx} #{@ury}"
- end
-
-end # class HRBb
-
-###########################################
-
-# PsPdf class definition
-
-class PsPdf
-
- protected
-
- SAFESIZE = 16000
-
- public
-
- # class methods ###
-
- def PsPdf.pdf_options
- "-dPDFSETTINGS=/#{$settings.pdf_target}" + \
- ( $settings.pdf_version == 'default' ? '' : \
- " -dCompatibilityLevel=#{$settings.pdf_version}" ) + \
- ($settings.pdf_custom ? ' ' + $settings.pdf_custom : '')
- end
-
- def PsPdf.gs_options
- "-dNOPAUSE -dBATCH -q -dSAFER"
- end
-
- def PsPdf.ps_options( sep )
- # the sep(arable_color) option forces a cmyk color model,
- # which should improve chances of grayscaling later on.
- if sep
- case $settings.ps_options
- when /-level\dsep\b/
- $settings.ps_options
- when /-level\d\b/
- $settings.ps_options.sub( /(-level\d)/, '\1sep' )
- else
- $settings.ps_options + " -level3sep"
- end # case
- else
- $settings.ps_options
- end # ifthenelse sep
- end
-
- # instance methods ###
-
- attr_accessor :path, :bb, :hrbb, :type, :npages, :atfront, :hr_atfront
-
- ##################
-
- def initialize( params={} )
-
- ext = params[ 'ext' ]
- file = params[ 'file' ]
-
- if not ext and not file
- @path = nil
- @type = nil
- elsif not file
- @path = mktemp( ext )
- @type = case ext.downcase
- when 'pdf'
- 'pdf'
- when 'eps'
- 'eps'
- when 'ps'
- 'ps'
- else
- 'other'
- end
- else
- @path = file
- @type = identify( file )
- @npages = pdf_pages if @type == 'pdf' # should we do this?
- @npages = 1 if @type =~ /^eps/
- end
- end # initialize
-
- def file_info
- if @npages and @type !~ /^eps/
- return "File type of #{@path} is #{@type} with #{@npages} pages"
- else
- return "File type of #{@path} is #{@type}"
- end
- end
-
- ##################
-
- # debug string for EPCallError
-
- def buginfo( params = nil )
- b = "Source was: " + @path + $/
- b = b + param_hash_prn( params ) if params
- b
- end
-
- ##################
-
- # Find boundingbox, simple case.
- # We shall call this method only for eps PsPdf objects which were
- # converted by pdftops or Ghostscript, so we can be sure that
- # the boundingbox is not (atend).
- # We also assume that the hrbb lies within the bb.
- # The file is not rewritten.
-
- def find_bb_simple
-
- fail EPCallError, buginfo unless @type == 'eps'
- @bb = nil
- @hrbb = nil
- slurp = ''
- File.open( @path, $R ) do |fl|
- slurp = fl.read( [File.size(@path), SAFESIZE].min )
- end
- lines = slurp.split( /\r\n?|\n/ )
- # look for a bb or a hrbb
- # if a valid bb is found, check the next line for hrbb
- # but look no further; we don't want to mistake a hrbb of
- # an included eps for the hrbb of the outer eps.
- lines.each do |l|
- if l =~ BB_PAT
- @bb = Bb.from_comment( l )
- elsif l =~ HRBB_PAT
- @hrbb = HRBb.from_hrcomment( l )
- elsif @bb
- break # stop looking; we expect hrbb next to bb
- end
- break if @bb and @hrbb
- end # do |l|
- fail EPBBError, @path unless @bb and @bb.valid
-
- end # def find_bb_simple
-
- ##################
-
- def pdf_pages
-
- fail EPCallError, buginfo unless @type == 'pdf'
-
- @npages = nil
-
- # get n. of pages; the Ghostscript pdf2dsc.ps script will
- # create a list of pages. It seems to ignore logical p. numbers.
-
- dsc = mktemp( "dsc" )
- cmd = "\"#{$settings.gs_prog}\" -dNODISPLAY -q" +
- " -sPDFname=\"#{@path}\" -sDSCname=\"#{dsc}\" pdf2dsc.ps"
- write_log cmd # if $DEBUG
- fail EPSystemError, cmd unless system( cmd ) and test( ?s, dsc )
- lines = []
- File.open( dsc, $R ) do |f|
- lines = f.read( SAFESIZE ).split( /\r\n?|\n/ )
- end
- lines.each do |l|
- if l =~ /^%%Pages:\s+(\d+)\s*$/
- @npages = $1.to_i
- break
- end # if =~
- end # do |l|
-
- return @npages
-
- end # pdf_pages
-
- #############################################################
-
- # direct conversions. These methods return a PsPdf object,
- # and raise an exception in case of failure.
- # Direct conversions convert at most once between PostScript and pdf.
- # They always write to a temporary file.
-
- ##################
-
- # eps_clean: write source eps to an eps without preview, and
- # with a boundingbox in the header.
- # clean up any potential problems
- # the eps is always written to a new file.
-
- def eps_clean
-
- fail EPCallError, buginfo if @type != 'eps' and @type != 'epsPreview'
- atend = nil
- hr_atend = nil
- slurp = ''
- offset, ps_len = nil, nil
- if @type == 'eps'
- offset = 0
- ps_len = File.size( @path )
- else
- # read ToC; see Adobe EPS spec
- File.open( @path, $R ) do |fl|
- # bug workaround for unpack
- if "\001\000\000\000".unpack( 'V' )[0] == 1
- dummy, offset, ps_len = fl.read( 12 ).unpack( 'VVV' )
- else
- dummy, offset, ps_len = fl.read( 12 ).unpack( 'NNN' )
- end
- end # File
- end # ifthenelse @type
-
- # [hires] boundingbox unknown and possibly atend
- @bb, @atfront, @hrbb, @hr_atfront = nil, nil, nil, nil
-
- # limit search for boundingbox comments.
- # For very large eps files, we don't want to scan the entire file.
- # A boundingbox comment should be in the header or trailer,
- # so scanning the first and last few KB should be plenty.
-
- File.open( @path, $R ) do |fl|
- fl.seek( offset, IO::SEEK_SET )
- slurp = fl.read( [ps_len,SAFESIZE].min )
- end
-
- # We capture both lines and separators, as an easy way to
- # keep track of how many bytes have been read.
- # we assume that if there is a hires bb then
- # bb and hires bb are on consecutive lines.
- # Otherwise the logic would get too messy.
-
- # The epsfile will be reconstituted from:
- # a series of lines and line separators; then either
- # - a bbox or
- # - (bbox or hrbbox), separator, (hrbbox or bbox)
- # the big blob
- # possibly a trailer with removed bb comments
-
- pre_lines = slurp.split( /(\r\n?|\n)/ )
- bb_comment = ''
- # initialize indices of bb comments to less than smallest index
- i_bb = -1
- i_hrbb = -1
- i = -1
- i_end = -1
- pre_length = 0
- pre_lines.each do |l|
- pre_length += l.length
- i += 1
- next if l =~ /(\r\n?|\n)/
- if l =~ BB_PAT
- @bb = Bb.from_comment( l )
- @atfront = true
- i_bb = i
- elsif l =~ BB_END
- atend = true
- i_bb = i
- elsif l =~ HRBB_PAT
- @hrbb = HRBb.from_hrcomment( l )
- @hr_atfront = true
- i_hrbb = i
- elsif l =~ HRBB_END
- hr_atend = true
- i_hrbb = i
- elsif @bb or atend
- i_end = i
- break # stop looking; we expect hrbb next to bb
- end # =~ BB_PAT
- end # do |l|
- if atend or hr_atend
- if ps_len > SAFESIZE
- File.open( @path, $R ) do |fl|
- fl.seek( offset + ps_len - SAFESIZE, IO::SEEK_SET )
- slurp = fl.read( SAFESIZE )
- end
- end # else use old slurp
- post_lines = slurp.split( /(\r\n?|\n)/ )
- # initialize indices of atend bb comments to more than largest index
- j = post_lines.length
- j_bb = j
- j_hrbb = j
- j_end = j
- post_length = 0
- post_lines.reverse_each do |l|
- post_length += l.length
- j -= 1
- next if l =~ /(\r\n?|\n)/
- if l =~ BB_PAT
- bb_comment = l
- @bb = Bb.from_comment( bb_comment )
- j_bb = j
- elsif l =~ HRBB_PAT
- bb_comment = l
- @hrbb = HRBb.from_hrcomment( bb_comment )
- j_hrbb = j
- end
- if (@bb or !atend) and (@hrbb or !hr_atend)
- j_end = j
- break
- end
- end # do
- #post_lines.slice([j_bb,j_hrbb].min .. -1).each do |l|
- # post_block = post_block + l
- end # if atend
-
- fail EPBBError, @path unless @bb.valid
- # in case of discrepancy, drop @hrbb.
- # we accept a `safety margin': a difference >1pt is not a discrepancy.
- @hrbb = nil if @hrbb and
- (( @bb.llx.to_i > @hrbb.llx.to_f.floor ) or
- ( @bb.lly.to_i > @hrbb.lly.to_f.floor ) or
- ( @bb.urx.to_i < @hrbb.urx.to_f.ceil ) or
- ( @bb.ury.to_i < @hrbb.ury.to_f.ceil ))
-
- retVal = PsPdf.new( 'ext' => 'eps' )
-
- # always rewrite the eps, to get normalized, ruby-compatible bb syntax.
- # we modify part of the header, and possibly of the trailer.
-
- # offset, length of middle part, which can be copied byte for byte
- cp_start = offset + pre_length
- cp_len = ps_len - pre_length
- cp_len -= post_length if ( atend or hr_atend )
- # replace boundingbox comments
- if atend
- pre_lines[i_bb].sub!( BB_END, @bb.comment )
- post_lines[j_bb].sub!( BB_PAT, '%%' )
- else
- pre_lines[i_bb].sub!( BB_PAT, @bb.comment )
- end
- if @hrbb
- # replace valid hires boundingbox comments
- if hr_atend
- pre_lines[i_hrbb].sub!( HRBB_END, @hrbb.hrcomment )
- post_lines[j_hrbb].sub!( HRBB_PAT, '%%' )
- else
- pre_lines[i_hrbb].sub!( HRBB_PAT, @hrbb.hrcomment )
- end
- elsif i_hrbb >= 0 # invalid hires bb
- # erase invalid hr boundingbox comments
- if atend
- pre_lines[i_hrbb].sub!( HRBB_END, '%%' )
- post_lines[j_hrbb].sub!( HRBB_PAT, '%%' )
- else
- pre_lines[i_hrbb].sub!( HRBB_PAT, '%%' )
- end
- end # test for @hrbb
-
- File.open( retVal.path, $W ) do |fl|
- fl.write( pre_lines[ 0 .. i_end ].join )
- end
- sliceFile( @path, retVal.path, cp_len, cp_start, $A )
- if ( atend or hr_atend )
- File.open( retVal.path, $A ) do |fl|
- fl.write( post_lines[ j_end .. -1 ].join )
- end
- end
- retVal.bb = @bb.copy
- retVal.hrbb = @hrbb.copy if @hrbb
- retVal.atfront = true
- retVal.hr_atfront = true
- retVal.npages = 1
- return retVal
- end # eps_clean
-
- ##################
-
- # Use the Ghostscript bbox device to give an eps a tight boundingbox.
- # Here, we don't test for use_hires_bb.
- # The eps should already have been cleaned up by eps_clean
- # and the current boundingbox should not contain negative coordinates,
- # otherwise the bbox output device may give incorrect results.
- # Maybe we should test whether gs' bbox device can handle
- # nonnegative coordinates.
- # The boundingbox in the eps is rewritten, but the eps is
- # not otherwise converted.
- # We don't create a new PsPdf object.
-
- def fix_bb
-
- fail EPCallError, buginfo \
- unless @type == 'eps' and @bb.non_negative
- # let ghostscript calculate new boundingbox
- cmd = $settings.gs_prog + ' ' + PsPdf.gs_options +
- " -sDEVICE=bbox \"" + @path + "\" 2>&1"
- write_log cmd # if $DEBUG
- bb_output = `#{cmd}`
- # inspect the result
- fail EPSystemError, cmd unless $? == 0 and bb_output
- bb_output.split(/\r\n?|\n/).each do |b|
- if b =~ BB_PAT
- bb_temp = Bb.from_comment( b )
- fail EPBBError, bb_output unless bb_temp.valid
- bb_temp.expand unless $settings.use_hires_bb
- @bb = bb_temp.copy
- elsif b =~ HRBB_PAT
- bb_temp = HRBb.from_hrcomment( b )
- @hrbb = bb_temp.valid ? bb_temp.copy : nil
- end
- end # do |b|
- fail EPBBError, bb_output unless @bb
- # this won't happen, but we deal with it anyway:
- @hrbb = HRBb.new( @bb.llx, @bb.lly, @bb.urx, @bb.ury ) unless @hrbb
-
- # locate current [hr]boundingbox, which ha[s|ve] to be replaced
- # assumptions: both in header, and hrbb no later than
- # first line after bb.
- slurp = ''
- File.open( @path, $R ) do |fl|
- slurp = fl.read( [File.size(@path),SAFESIZE].min )
- end
- pre_lines = slurp.split( /(\r\n?|\n)/ )
- i_bb = -1
- i_hrbb = -1
- i = -1
- i_end = -1
- pre_length = 0
- pre_lines.each do |l|
- pre_length += l.length
- i += 1
- next if l =~ /(\r\n?|\n)/
- if l =~ BB_PAT
- i_bb = i
- elsif l =~ HRBB_PAT
- i_hrbb = i
- elsif i_bb >= 0
- i_end = i
- break # stop looking; we expect hrbb next to bb
- end # =~ BB_PAT
- end # do |l,i|
- fail EPBBError, "No boundingbox found in #{@path}" if i_bb < 0
-
- # replace boundingbox[es] by editing initial part pre_block
- # and copying the rest byte for byte
- if i_hrbb < 0
- # no old hrbb; replace bb with new bb and new hrbb
- # pre_lines[i_bb+1] should match /\r\n?|\n/
- pre_lines[i_bb].sub!( BB_PAT, @bb.comment +
- pre_lines[i_bb+1] + @hrbb.hrcomment )
- else
- pre_lines[i_bb].sub!( BB_PAT, @bb.comment )
- pre_lines[i_hrbb].sub!( HRBB_PAT, @hrbb.hrcomment )
- end
- oldpath = @path
- @path = mktemp( 'eps' )
- File.open( @path, $W ) { |fl|
- fl.write( pre_lines[ 0 .. i_end ].join ) }
- sliceFile( oldpath, @path, \
- File.size( oldpath ) - pre_length, pre_length, $A )
- return self
-
- end # fix_bb
-
- ##################
-
- # Convert eps to pdf.
- # The eps should already have a boundingbox in the header.
-
- def eps_to_pdf( params={} )
-
- gray = params[ 'gray' ]
-
- fail EPCallError, buginfo( params ) unless @type == 'eps' and @atfront
-
- wrp = ( $settings.use_hires_bb and @hrbb ) ? @hrbb.wrapper : @bb.wrapper
-
- retVal = PsPdf.new( 'ext' => 'pdf' )
- cmd = "\"#{$settings.gs_prog}\" #{PsPdf.gs_options}" +
- " -sDEVICE=pdfwrite #{PsPdf.pdf_options}" +
- " -sOutputFile=\"#{retVal.path}\"" +
- ( gray ? (' "' + $GRAYHEAD + '"') : "" ) +
- " \"#{wrp}\" \"#{@path}\" -c grestore"
- write_log cmd # if $DEBUG
- fail EPSystemError, cmd \
- unless system( cmd ) and test( ?s, retVal.path )
- retVal.npages = 1
- return retVal
-
- end # eps_to_pdf
-
- ##################
-
- # Convert source pdf to eps.
- # The option sep_color is ignored if pdftops is not available.
-
- def pdf_to_eps( params={} )
-
- page = params[ 'page' ] ? params[ 'page' ].to_i : 1
- sep = params[ 'sep' ]
-
- fail EPCallError, buginfo( params ) unless @type == 'pdf'
- fail EPCallError, buginfo( params ) \
- unless page > 0 and page <= @npages
- retVal = PsPdf.new( 'ext' => 'eps' )
- if $settings.pdftops_prog and $settings.use_pdftops
- cmd = "\"#{$settings.pdftops_prog}\"" +
- " #{PsPdf.ps_options( sep )}" +
- " -paper match -eps -f #{page} -l #{page}" +
- " \"#{@path}\" \"#{retVal.path}\""
- else
- cmd = "\"#{$settings.gs_prog}\" -sDEVICE=epswrite -r600" +
- " #{PsPdf.gs_options}" +
- " -dFirstPage=#{page}" +
- " -dLastPage=#{page}" +
- " -sOutputFile=\"#{retVal.path}\" \"#{@path}\""
- end
- write_log cmd # if $DEBUG
- fail EPSystemError, cmd unless \
- system( cmd ) and test( ?s, retVal.path )
-
-# fix for incorrect DSC header produced by some versions of pdftops:
-# if necessary, change line `% Produced by ...' into `%%Produced by ...'
-# this is usually the second line.
-# otherwise the DSC header would be terminated before the bbox comment
-# match first chk_ze chars against `% Produced by'
- chk_size = [ 1500, File.size( retVal.path ) ].min
- slurp = ''
- File.open( retVal.path, $R ) do |fl|
- slurp = fl.read( chk_size )
- end # File
- pdfpat = /([\r\n])% Produced by/m
- if slurp =~ pdfpat
- newpath = mktemp( 'eps' )
- write_log "pdftops header fix #{retVal.path} => #{newpath}"
- File.open( newpath, $W ) do |fl2|
- fl2.write( slurp.sub( pdfpat, '\1%% Produced by)' ) )
- end
- sliceFile( retVal.path, newpath, File.size( retVal.path ) - chk_size,
- chk_size, $A )
- retVal.path = newpath
- end # if =~
-# end fix for incorrect DSC header produced by some versions of pdftops
- retVal.atfront = 1
- retVal.find_bb_simple
- retVal.npages = 1
- return retVal
-
- end # pdf_to_eps
-
- ##################
-
- def ps_to_pdf( params={} )
-
- gray = params[ 'gray' ]
-
- fail EPCallError, buginfo( params ) \
- unless @type == 'ps'
-
- retVal = PsPdf.new( 'ext' => 'pdf' )
- cmd = "\"#{$settings.gs_prog}\" #{PsPdf.gs_options}" +
- " -sDEVICE=pdfwrite #{PsPdf.pdf_options}" +
- " -sOutputFile=\"#{retVal.path}\"" +
- ( gray ? (' "' + $GRAYHEAD + '"') : "" ) + " \"#{@path}\""
- write_log cmd # if $DEBUG
- fail EPSystemError, cmd \
- unless system( cmd ) and test( ?s, retVal.path )
-
- retVal.pdf_pages
- return retVal
-
- end # def ps_to_pdf
-
- ##################
-
- def pdf_to_ps( params={} )
-
- sep = params[ 'sep' ]
- page = params[ 'page' ] ? params[ 'page' ].to_s : nil
-
- fail EPCallError, buginfo( params ) unless @type == 'pdf'
- retVal = PsPdf.new( 'ext' => 'ps' )
- if $settings.pdftops_prog and $settings.use_pdftops
- cmd = "\"#{$settings.pdftops_prog}\" #{PsPdf.ps_options( sep )}" +
- ( page ? " -f #{page} -l #{page}" : '' ) +
- " -paper match \"#{@path}\" \"#{retVal.path}\""
- else
- cmd = "\"#{$settings.gs_prog}\" #{PsPdf.gs_options}" +
- " -sDEVICE=pswrite -r600" +
- ( page ? " -dFirstPage=#{page} -dLastPage#{page}" : '' ) +
- " -sOutputFile=\"#{retVal.path}\"" + " \"#{@path}\""
- end
- write_log cmd # if $DEBUG
- fail EPSystemError, cmd unless \
- system( cmd ) and test( ?s, retVal.path )
- retVal.npages = @npages
- return retVal
-
- end # pdf_to_ps
-
- ##################
-
- # all possible conversions, as concatenations of direct conversions.
-
- def any_to_any( params={} )
-
- type = params[ 'type' ]
- page = params[ 'page' ]
- bbox = params[ 'bbox' ]
- gray = params[ 'gray' ]
- gRAY = params[ 'gRAY' ] # try harder to grayscale
- gray = 1 if gRAY
- bbox = nil if type == 'ps'
-
- fail EPCallError, buginfo( params ) \
- unless ( type=='eps' or type=='pdf' or type=='ps' )
- fail EPCallError, buginfo( params ) \
- if @type=='other'
- fail EPCallError, buginfo( params ) \
- if type=='ps' and bbox
- fail EPCallError, buginfo( params ) \
- if @type=='eps' and type=='ps'
-
- # gRAY tries harder to grayscale, by converting color first to cmyk
- # even if it requires an additional eps - pdf - eps roundtrip.
- # Normally, conversion to cmyk is done only if it doesn't take
- # an extra roundtrip.
- # The separable color conversion is an option of pdftops.
-
- pp = self
-
- pp = pp.eps_clean if pp.type == 'epsPreview'
- pp = pp.eps_clean if pp.type == 'eps' and \
- not ( pp.bb and pp.atfront ) # => not yet `cleaned'
- #pp.pdf_pages if pp.type == 'pdf' and not pp.npages
- # now also done in initialize
-
- case pp.type
- when 'eps'
-
- # in some cases extra eps => pdf => eps roundtrip:
- # roundtrip guarantees bb.non_negative, necessary for fix_bb.
- # pdf_to_ps( sep ) improves chances of grayscaling.
-
- case type
- when 'eps'
- pp = pp.eps_to_pdf.pdf_to_eps( 'sep' => gray ) \
- if gRAY or ( bbox and not pp.bb.non_negative )
- pp = pp.eps_to_pdf( 'gray' => gray ).pdf_to_eps if gray
- pp = pp.fix_bb if bbox
- return pp
-
- when 'pdf'
- pp = pp.eps_to_pdf.pdf_to_eps( 'sep' => gray ) \
- if gRAY or ( bbox and not pp.bb.non_negative )
- pp = pp.fix_bb if bbox
- return pp.eps_to_pdf( 'gray' => gray )
-
- when 'ps'
- if gRAY
- pp = pp.eps_to_pdf.pdf_to_ps( 'sep' => gray )
- pp = pp.ps_to_pdf( 'gray' => gray ).pdf_to_ps
- else
- pp = pp.eps_to_pdf( 'gray' => gray ).pdf_to_ps
- end
- return pp
-
- end # case type
-
- when 'pdf'
-
- case type
- when 'eps'
- if not gray
- pp = pp.pdf_to_eps( 'page' => page )
- pp = pp.fix_bb if bbox
- return pp
- else
- pp = pp.pdf_to_eps( 'page' => page, 'sep' => 1 )
- pp = pp.fix_bb if bbox
- pp = pp.eps_to_pdf( 'gray' => 1 )
- return pp.pdf_to_eps
- end
-
- when 'pdf'
- return pp unless ( gray or bbox or page )
- if bbox or not $settings.pdftops_prog
- pp = pp.pdf_to_eps( 'page' => page, 'sep' => gray )
- pp = pp.fix_bb if bbox
- return pp.eps_to_pdf( 'gray' => gray )
- else
- pp = pp.pdf_to_ps( 'page' => page, 'sep' => gray )
- return pp.ps_to_pdf( 'gray' => gray )
- end
-
- when 'ps'
- if gray
- pp = pp.pdf_to_ps( 'sep' => 1 )
- pp = pp.ps_to_pdf( 'gray' => 1 )
- end
- return pp.pdf_to_ps
-
- end # case type
-
- when 'ps'
-
- case type
- when 'eps'
- if gRAY
- pp = pp.ps_to_pdf.pdf_to_eps( 'page' => page, 'sep' => 1 )
- pp = pp.eps_to_pdf( 'gray' => 1 )
- pp = pp.pdf_to_eps
- else
- pp = pp.ps_to_pdf( 'gray' => gray )
- pp = pp.pdf_to_eps( 'page' => page )
- end
- return pp.fix_bb
-
- when 'pdf'
- if bbox
- pp = pp.ps_to_pdf.pdf_to_eps( 'sep' => gray, 'page' => page )
- pp = pp.fix_bb
- return pp = pp.eps_to_pdf( 'gray' => gray )
- elsif page
- pp = pp.ps_to_pdf.pdf_to_ps( 'sep' => gray, 'page' => page )
- return pp = pp.ps_to_pdf( 'gray' => gray )
- else
- pp = pp.ps_to_pdf.pdf_to_ps( 'sep' => 1 ) if gRAY
- return pp.ps_to_pdf( 'gray' => gray )
- end
-
- when 'ps'
- return pp unless page or gray
- if gRAY
- pp = pp.ps_to_pdf.pdf_to_ps( 'page' => page, 'sep' => 1 )
- return pp.ps_to_pdf( 'gray' => 1 ).pdf_to_ps
- else
- pp = pp.ps_to_pdf( 'gray' => gray )
- return pp = pp.pdf_to_ps( 'page' => page )
- end
-
- end # case type
-
- end # case pp.type
-
- #raise EPCallError
- fail "Unsupported conversion"
- # this shouldn't happen anymore
-
- end # any_to_any
-
-end # class PsPdf
-
-#################################
-# main program
-
-require 'optparse'
-
-def gui( action )
- case action
- when 'config_w' then
- puts "pdftops_prog=#{$settings.pdftops_prog}" if ARCH=='w32'
- puts "pdf_viewer=#{$settings.pdf_viewer}" if ARCH=='unix'
- puts "ps_viewer=#{$settings.ps_viewer}" if ARCH=='unix'
- puts "defaultDir=#{$settings.defaultDir}"
- puts "ignore_pdftops=#{$settings.ignore_pdftops}"
- puts "pdf_target=#{$settings.pdf_target}"
- puts "pdf_version=#{$settings.pdf_version}"
- puts "pdf_custom=#{$settings.pdf_custom}"
- puts "ps_options=#{$settings.ps_options}"
- puts "ignore_hires_bb=#{$settings.ignore_hires_bb}"
- puts "bb_spread=#{$settings.bb_spread}"
- exit
- when 'config_r' then
- while line = gets
- (varname, val) = line.split('=', 2)
- varname.strip! if varname
- val.strip! if val
- if $settings.has_key?( varname )
- val = nil if val == ''
- $settings[varname].val = val
- # puts( "\"#{val}\" assigned to #{varname}" )
- end
- end # while
- $settings.write_settings
- exit
- when nil then
- $from_gui = true
- else
- abort( "Action should be omitted or 'config_w' or 'config_r'" )
- end # case
-end
-
-# create a pause to examine temp files
-def abortt( msg )
- if $DEBUG
- $stderr.puts msg + "\nPress <enter> to finish"
- $stdin.gets
- end
- fail
-end
-
-save_settings = false
-
-opts = OptionParser.new do |opts|
- # for help output
- opts.banner = "Convert between [e]ps and pdf formats"
- opts.separator "Usage: epspdf.rb [options] infile [outfile]"
- opts.separator ""
- opts.separator "Default for outfile is file.pdf" +
- " if infile is file.eps or file.ps"
- opts.separator "Default for outfile is file.eps" +
- " if infile is file.pdf"
- opts.separator ""
-
- opts.on( "-g", "--gray", "--grey",
- "Convert to grayscale;",
- "success not guaranteed" ) do |opt|
- $options.gray = true
- end
-
- opts.on( "-G", "--GRAY", "--GREY",
- "Try harder to convert to grayscale" ) do |opt|
- $options.gRAY = true
- end
-
- opts.on( "-p PAGENUMBER", "--pagenumber=PAGENUMBER",
- "Page to be converted or selected", Integer ) do |opt|
- $options.page = opt
- end
-
- opts.on( "-b", "--bbox", "--BoundingBox",
- "Compute tight boundingbox" ) do |opt|
- $options.bbox = true
- end
-
- opts.on( "-n", "--no-hires",
- "Don't use hires boundingbox" ) do |opt|
- $settings.ignore_hires_bb = '1'
- end
-
- opts.on( "-r", "--hires",
- "Use hires boundingbox" ) do |opt|
- $settings.ignore_hires_bb = '0'
- end
-
- opts.on( "-T TARGET", "--target=TARGET",
- PDF_TARGETS,
- "Target use of pdf; one of",
- "#{PDF_TARGETS.join( ', ' )}" ) do |opt|
- $settings.pdf_target = opt
- end
-
- opts.on( "-N PDFVERSION", "--pdfversion=PDFVERSION",
- PDF_VERSIONS,
- "Pdf version to be generated" ) do |opt|
- $settings.pdf_version = opt
- end
-
- opts.on( "-V PDFVERSION", "--version=PDFVERSION",
- PDF_VERSIONS,
- "Deprecated; use `-N' or `--pdfversion'." ) do |opt|
- if opt == ""
- puts EPVERSION
- exit
- end
- $settings.pdf_version = opt
- end
-
- opts.on( "-I",
- "Ignore pdftops even if available",
- "(default: use if available)" ) do |opt|
- $settings.ignore_pdftops = '1'
- end
-
- opts.on( "-U",
- "Use pdftops if available",
- "(overrides previous -I setting)" ) do |opt|
- $settings.ignore_pdftops = '0'
- end
-
- opts.on( "-C CUSTOMOPTIONS", "--custom=CUSTOMOPTIONS",
- "Custom options for conversion to pdf,",
- "view Use.htm and ps2pdf.htm from",
- "the Ghostscript documentation set" ) do |opt|
- $settings.pdf_custom = opt
- end
-
- opts.on( "-P PSOPTIONS", "--psoptions=PSOPTIONS",
- "Options for pdftops; default -level3,",
- "don't include -eps or page number options;",
- "these will be generated by the program" ) do |opt|
- $settings.ps_options = opt
- end
-
- opts.on( "-i", "--info",
- "Info: display detected filetype" ) do |opt|
- $options.info = true
- end
-
- opts.on( "-s",
- "Save (some) settings" ) do |opt|
- save_settings = true
- end
-
- opts.on( "-d", "Debug: don't remove temp files" ) do |opt|
- $DEBUG = 1
- end
-
- opts.on( "--gui[=ACTION]", "Do not use; reserved for GUI" ) do |opt|
- gui( opt )
- end
-
- opts.separator ""
-
- opts.on( "-v", "Prints version info" ) do |opt|
- puts EPVERSION
- exit
- end
-
- opts.on_tail("-h", "--help", "Show this message") do
- puts "Epspdf version " + EPVERSION
- puts "Copyright (C) " + COPYRIGHT + " Siep Kroonenberg"
- puts opts
- exit
- end
-end # opts
-
-# hack alert! we support `--version' for version info although
-# --version is still interpreted as desired pdf output version
-
-if ARGV.length == 1 and ARGV[0] == '--version'
- puts EPVERSION
- exit
-end
-
-# save original command-line for later reporting
-cmdline = "#{$0} #{ARGV.join( sep=' ' )}"
-
-# parse options destructively
-begin
- opts.parse!( ARGV )
-rescue OptionParser::ParseError => e
- STDERR.puts e.message, "\n", opts
- exit( -1 )
-end
-
-# log cmdline AFTER we found out whether we run from gui
-write_log( cmdline )
-
-$options.page = 1 if $options.bbox and not $options.page
-
-$settings.write_settings if save_settings
-
-if ARGV.length < 1
- if not save_settings # help output
- puts opts
- abort
- else
- exit
- end
-elsif $options.info
- p = PsPdf.new( 'file' => ARGV[0] )
- puts( p.file_info )
- exit
-elsif ARGV.length > 1 and
- File.expand_path( ARGV[0] ) == File.expand_path( ARGV[1] )
- abort " Input and output files should be different."
-else
- infile = ARGV[0]
- abort( infile + " not found or empty" ) unless test( ?s, infile )
-end
-
-# done with options
-
-########################################
-
-source = PsPdf.new( 'file' => infile )
-
-# We aren't finicky about the extension of the input file,
-# but still want to check whether pdf is pdf.
-
-case source.type
-when 'eps', 'epsPreview', 'ps'
- abort "Wrong extension; input is not in pdf format" \
- if infile =~ /\.pdf$/i
-when 'pdf'
- abort "Wrong extension; input is in pdf format" \
- if infile !~ /\.pdf$/i
-else
- abort "Invalid input file type; not [e]ps or pdf" \
- if source.type == 'other'
-end # case source.type
-
-# find or construct output file name
-if ARGV.length > 1
- outfile = ARGV[1]
-else
- case source.type
- when 'eps', 'epsPreview', 'ps'
- outfile = infile.sub( /\.[^.]+$/, '.pdf' )
- when 'pdf'
- outfile = infile.sub( /\.[^.]+$/, '.eps' )
- end # case
-end # ifthenelse ARGV.length
-outfile = File.expand_path( outfile )
-
-$options.type = case outfile
-when /\.pdf$/i
- "pdf"
-when /\.ps$/i
- "ps"
-when /\.eps$/i
- "eps"
-else
- nil
-end # case outfile
-
-abort "Unknown or unsupported output file extension" \
- unless $options.type
-#abort "Output format not supported without xpdf utilities" \
-# if outfile == 'ps' and not $settings.pstopdf_prog
-
-pp = PsPdf.new( 'file' => infile )
-
-begin # inner rescue block
- pp = pp.any_to_any( $options )
- write_log( pp.file_info ) if $from_gui
- ccp( pp.path, outfile )
-rescue EPCallError => exc
- mess =
- "Wrong method call or conversion not supported or wrong page number" +
- $/ + exc.message + $/ + exc.backtrace.join( $/ )
- write_log( mess )
- puts mess
- exit 1
-rescue EPBBError => exc
- mess = "Boundingbox problem" + $/ +
- exc.message + $/ + exc.backtrace.join( $/ )
- write_log( mess )
- puts mess
- exit 1
-rescue EPCopyError => exc
- mess = "Copying problem" + $/ +
- exc.message + $/ + exc.backtrace.join( $/ )
- write_log( mess )
- puts mess
- exit 1
-rescue EPSystemError => exc
- mess = "Problem with system call" + $/ +
- exc.message + $/ + exc.backtrace.join( $/ )
- write_log( mess )
- puts mess
- exit 1
-rescue StandardError => exc
- mess = exc.message + $/ + exc.backtrace.join( $/ )
- write_log( mess )
- puts mess
- exit 1
-end # rescue block
-
-cleantemp unless $DEBUG == 1
-__END__