pstopdf.rb / last modification: 2020-01-30 14:16
#!/usr/bin/env ruby

# program   : pstopdf
# 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

banner = ['PsToPdf', 'version 2.0.1', '2002-2006', 'PRAGMA ADE/POD']

$: << File.expand_path(File.dirname($0)) ; $: << File.join($:.last,&#39;lib') ; $:.uniq!

# todo: paden/prefix in magick and inkscape
# todo: clean up method handling (pass strings, no numbers)
# --method=crop|bounded|raw|...
# --resolution=low|normal|medium|high|printer|print|screen|ebook|default
# + downward compatible flag handling

require &#39;base/switch'
require &#39;base/tool'
require &#39;base/logger'

require &#39;graphics/gs'
require &#39;graphics/magick'
require &#39;graphics/inkscape'

require &#39;rexml/document'

exit if defined?(REQUIRE2LIB)

class Commands

    include CommandBase

    # nowadays we would force a directive, but
    # for old times sake we handle default usage

    def main
        filename = @commandline.argument(&#39;first')
        pattern  = @commandline.option(&#39;pattern')
        if filename.empty? && ! pattern.empty? then
            pattern = "**/#{pattern}" if @commandline.option('recurse')
            globfiles(pattern)
        end
        filename = @commandline.argument(&#39;first')
        if filename.empty? then
            help
        elsif filename =~ /\.exa$/ then
            request
        else
            convert
        end
    end

    # actions

    def convert

        ghostscript = GhostScript.new(logger)
        magick      = ImageMagick.new(logger)
        inkscape    = InkScape.new(logger)

        outpath = @commandline.option(&#39;outputpath')
        unless outpath.empty? then
            begin
                File.expand_path(outpath)
                outpath = File.makedirs(outpath) unless FileTest.directory?(outpath)
            rescue
                # sorry
            end
        end

        @commandline.arguments.each do |filename|

            filename = Tool.cleanfilename(filename,@commandline) # brrrr
            inppath = @commandline.option(&#39;inputpath')
            if inppath.empty? then
               inppath = &#39;.'
               fullname = filename # avoid duplicate './'
            else
               fullname = File.join(inppath,filename)
            end
            if FileTest.file?(fullname) then
                handle_whatever(ghostscript,inkscape,magick,filename)
            else
                report("file #{fullname} does not exist")
            end

        end

    end

    def request

        # <exa:request>
        #   <exa:application>
        #     <exa:command>pstopdf</exa:command>
        #     <exa:filename>E:/tmp/demo.ps</exa:filename>
        #   </exa:application>
        #   <exa:data>
        #     <exa:variable label='gs:DoThumbnails'>false</exa:variable>
        #     <exa:variable label='gs:ColorImageDepth'>-1</exa:variable>
        #   </exa:data>
        # </exa:request>

        ghostscript = GhostScript.new(logger)
        magick      = ImageMagick.new(logger)
        inkscape    = InkScape.new(logger)

        dataname = @commandline.argument(&#39;first')  || ''
        filename = @commandline.argument(&#39;second') || ''

        if dataname.empty? || ! FileTest.file?(dataname) then
            report(&#39;provide valid exa file')
            return
        else
            begin
                request = REXML::Document.new(File.new(dataname))
            rescue
                report(&#39;provide valid exa file (xml error)')
                return
            end
        end
        if filename.empty? then
            begin
                if filename = REXML::XPath.first(request.root,"exa:request/exa:application/exa:filename/text()") then
                    filename = filename.to_s
                else
                    report(&#39;no filename found in exa file')
                    return
                end
            rescue
                filename = &#39;'
            end
        end
        if filename.empty? then
            report(&#39;provide valid filename')
            return
        elsif ! FileTest.file?(filename) then
            report("invalid filename #{filename}")
            return
        end

        [ghostscript,inkscape,magick].each do |i|
            i.setvariable(&#39;inputfile',filename)
        end

        # set ghostscript variables
        REXML::XPath.each(request.root,"/exa:request/exa:data/exa:variable") do |v|
            begin
                if (key = v.attributes[&#39;label']) and (value = v.text.to_s) then
                    case key
                        when /gs[\:\.](var[\:\.])*(offset)/io then ghostscript.setoffset(value)
                        when /gs[\:\.](var[\:\.])*(method)/io then ghostscript.setvariable(&#39;method',value)
                        when /gs[\:\.](var[\:\.])*(.*)/io     then ghostscript.setpsoption($2,value)
                    end
                end
            rescue
            end
        end

        # no inkscape and magick variables (yet)

        handle_whatever(ghostscript,inkscape,magick,filename)

    end

    def watch

        ghostscript = GhostScript.new(logger)
        magick      = ImageMagick.new(logger)
        inkscape    = InkScape.new(logger)

        pathname = commandline.option(&#39;watch')

        unless pathname and not pathname.empty? then
            report(&#39;empty watchpath is not supported')
            exit
        end

        if pathname == &#39;.' then
            report("watchpath #{pathname} is not supported")
            exit
        end

        if FileTest.directory?(pathname) then
            if Dir.chdir(pathname) then
                report("watching path #{pathname}")
            else
                report("unable to change to path #{pathname}")
                exit
            end
        else
            report("invalid path #{pathname}")
            exit
        end

        waiting = false

        loop do

            if waiting then
                report("waiting #{getvariable('delay')}")
                waiting = false
                sleep(getvariable(&#39;delay').to_i)
            end

            files = Dir.glob("**/*.*")

            if files and files.length > 0 then

                files.each do |fullname|

                    next unless fullname

                    if FileTest.directory?(fullname) then
                        debug(&#39;skipping path', fullname)
                        next
                    end

                    unless magick.supported(fullname) then
                        debug(&#39;not supported', fullname)
                        next
                    end

                    if (! FileTest.file?(fullname)) || (FileTest.size(fullname) < 100) then
                        debug("skipping small crap file #{fullname}")
                        next
                    end

                    debug("handling file #{fullname}")

                    begin
                        next unless File.rename(fullname,fullname) # access trick
                    rescue
                        next                                       # being written
                    end

                    fullname = Tool.cleanfilename(fullname,@commandline)

                    fullname.gsub!(/\\/io, &#39;/')

                    filename = File.basename(fullname)
                    filepath = File.dirname(fullname)

                    next if filename =~ /gstemp.*/io

                    if filepath !~ /(result|done|raw|crop|bound|bitmap)/io then
                        begin
                            File.makedirs(filepath+&#39;/raw')
                            File.makedirs(filepath+&#39;/bound')
                            File.makedirs(filepath+&#39;/crop')
                            File.makedirs(filepath+&#39;/bitmap')
                            debug("creating prefered input paths on #{filepath}")
                        rescue
                            debug("creating input paths on #{filepath} failed")
                        end
                    end

                    if filepath =~ /^(.*\/|)(done|result)$/io then
                        debug("skipping file #{fullname}")
                    else
                        report("start processing file #{fullname}")
                        if filepath =~ /^(.*\/*)(raw|crop|bound)$/io then
                            donepath = $1 + &#39;done'
                            resultpath = $1 + &#39;result'
                            case $2
                                when &#39;raw'   then method = 1
                                when &#39;bound' then method = 2
                                when &#39;crop'  then method = 3
                                else              method = 2
                            end
                            report("forcing method #{method}")
                        else
                            method = 2
                            donepath = filepath + &#39;/done'
                            resultpath = filepath + &#39;/result'
                            report("default method #{method}")
                        end

                        begin
                            File.makedirs(donepath)
                            File.makedirs(resultpath)
                        rescue
                            report(&#39;result path creation fails')
                        end

                        if FileTest.directory?(donepath) && FileTest.directory?(resultpath) then

                            resultname = resultpath + &#39;/' + filename.sub(/\.[^\.]*$/,'') + '.pdf'

                            @commandline.setoption(&#39;inputpath',  filepath)
                            @commandline.setoption(&#39;outputpath', resultpath)
                            @commandline.setoption(&#39;method',     method)

                            if ghostscript.psfile?(fullname) then
                                handle_ghostscript(ghostscript,filename)
                            else
                                handle_magick(magick,filename)
                            end

                            sleep(1) # calm down

                            if FileTest.file?(fullname) then
                                begin
                                    File.copy(fullname,donepath + &#39;/' + filename)
                                    File.delete(fullname)
                                rescue
                                    report(&#39;cleanup fails')
                                end
                            end

                        end

                    end

                end

            end

            waiting = true
        end

    end

    private

    def handle_whatever(ghostscript,inkscape,magick,filename)
        if ghostscript.psfile?(filename) then
            # report("processing ps file #{filename}")
            ghostscript.setvariable(&#39;pipe',false) if @commandline.option('nopipe')
            # ghostscript.setvariable('pipe',not @commandline.option('nopipe'))
            ghostscript.setvariable(&#39;colormodel',@commandline.option('colormodel'))
            ghostscript.setvariable(&#39;offset',@commandline.option('offset'))
            handle_ghostscript(ghostscript,filename)
        elsif ghostscript.pdffile?(filename) && ghostscript.pdfmethod?(@commandline.option(&#39;method')) then
            # report("processing pdf file #{filename}")
            handle_ghostscript(ghostscript,filename)
        elsif inkscape.supported?(filename) then
            # report("processing non ps/pdf file #{filename}")
            handle_inkscape(inkscape,filename)
        elsif magick.supported?(filename) then
            # report("processing non ps/pdf file #{filename}")
            handle_magick(magick,filename)
        else
            report("option not supported for #{filename}")
        end
    end

    def handle_magick(magick,filename)

        report("converting non-ps file #{filename} into pdf")

        inppath = @commandline.option(&#39;inputpath')
        outpath = @commandline.option(&#39;outputpath')

        inppath = inppath + &#39;/' if not inppath.empty?
        outpath = outpath + &#39;/' if not outpath.empty?

        prefix = @commandline.option(&#39;prefix')
        suffix = @commandline.option(&#39;suffix')

        inpfilename = "#{inppath}#{filename}"
        outfilename = "#{outpath}#{prefix}#{filename.sub(/\.([^\.]*?)$/, '')}#{suffix}.pdf"

        magick.setvariable(&#39;inputfile' , inpfilename)
        magick.setvariable(&#39;outputfile', outfilename)

        magick.autoconvert

    end

    def handle_inkscape(inkscape,filename)

        report("converting svg(z) file #{filename} into pdf")

        inppath = @commandline.option(&#39;inputpath')
        outpath = @commandline.option(&#39;outputpath')

        inppath = inppath + &#39;/' if not inppath.empty?
        outpath = outpath + &#39;/' if not outpath.empty?

        prefix = @commandline.option(&#39;prefix')
        suffix = @commandline.option(&#39;suffix')

        inpfilename = "#{inppath}#{filename}"
        outfilename = "#{outpath}#{prefix}#{filename.sub(/\.([^\.]*?)$/, '')}#{suffix}.pdf"

        inkscape.setvariable(&#39;inputfile' , inpfilename)
        inkscape.setvariable(&#39;outputfile', outfilename)

        if @commandline.option(&#39;verbose') || @commandline.option('debug') then
            logname = filename.gsub(/\.[^\.]*?$/, &#39;.log')
            report("log info saved in #{logname}")
            inkscape.convert(logname) # logname ook doorgeven
        else
            inkscape.convert
        end

    end

    def handle_ghostscript(ghostscript,filename)

        ghostscript.reset

        method = ghostscript.method(@commandline.option(&#39;method'))
        force = ghostscript.method(@commandline.option(&#39;force'))

        ghostscript.setvariable(&#39;method', method)
        ghostscript.setvariable(&#39;force', force)

        # report("conversion method #{method}")

        inppath = @commandline.option(&#39;inputpath')
        outpath = @commandline.option(&#39;outputpath')

        inppath = inppath + &#39;/' if not inppath.empty?
        outpath = outpath + &#39;/' if not outpath.empty?

        prefix = @commandline.option(&#39;prefix')
        suffix = @commandline.option(&#39;suffix')

        ok = false

        if ghostscript.pdfmethod?(method) then

            report("converting pdf file #{filename} into pdf")

            if prefix.empty? && suffix.empty? && inppath.empty? && outpath.empty? then
                prefix = ghostscript.pdfprefix(method)
            end

            if ghostscript.pdffile?(filename) then

                filename = filename.sub(/\.pdf$/, &#39;')

                inpfilename = "#{inppath}#{filename}.pdf"
                outfilename = "#{outpath}#{prefix}#{filename}#{suffix}.pdf"

                ghostscript.setvariable(&#39;inputfile' ,inpfilename)
                ghostscript.setvariable(&#39;outputfile',outfilename)

                if FileTest.file?(inpfilename) then
                    ok = ghostscript.convert
                else
                    report("no file found #{filename}")
                end

            else
                report("no pdf file #{filename}")
            end

        elsif ghostscript.psfile?(filename) then

            if filename =~ /(.*)\.([^\.]*?)$/io then
                filename, filesuffix = $1, $2
            else
                filesuffix = &#39;eps'
            end

            report("converting #{filesuffix} (ps) into pdf")

            inpfilename = "#{inppath}#{filename}.#{filesuffix}"
            outfilename = "#{outpath}#{prefix}#{filename}#{suffix}.pdf"

            ghostscript.setvariable(&#39;inputfile' , inpfilename)
            ghostscript.setvariable(&#39;outputfile', outfilename)

            if FileTest.file?(inpfilename) then
                ok = ghostscript.convert
                if ! ok && FileTest.file?(outfilename) then
                    begin
                        File.delete(outfilename)
                    rescue
                    end
                end
            else
                report("no file with name #{filename} found")
            end

        else
            report(&#39;file must be of type eps/ps/ai/pdf')
        end

        return ok

    end

end

# ook pdf -> pdf onder optie 0, andere kleurruimte

logger      = Logger.new(banner.shift)
commandline = CommandLine.new

commandline.registerflag(&#39;debug')
commandline.registerflag(&#39;verbose')
commandline.registerflag(&#39;nopipe')

commandline.registervalue(&#39;method',2)
commandline.registervalue(&#39;offset',0)

commandline.registervalue(&#39;prefix')
commandline.registervalue(&#39;suffix')

commandline.registervalue(&#39;inputpath')
commandline.registervalue(&#39;outputpath')

commandline.registerflag(&#39;watch')
commandline.registerflag(&#39;force')
commandline.registerflag(&#39;recurse')

commandline.registervalue(&#39;delay',2)

commandline.registervalue(&#39;colormodel','cmyk')
commandline.registervalue(&#39;pattern','')

commandline.registeraction(&#39;help')
commandline.registeraction(&#39;version')

commandline.registeraction(&#39;convert', 'convert ps into pdf')
commandline.registeraction(&#39;request', 'handles exa request file')
commandline.registeraction(&#39;watch',   'watch folders for conversions (untested)')

commandline.expand

logger.verbose if (commandline.option(&#39;verbose') || commandline.option('debug'))

Commands.new(commandline,logger,banner).send(commandline.action || &#39;main')