summaryrefslogtreecommitdiff
path: root/scripts/context/ruby/rlxtools.rb
blob: 6a5c5ca209802ff8c13bf4f8f60e8be713a55543 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
#!/usr/bin/env ruby

# program   : rlxtools
# copyright : PRAGMA Advanced Document Engineering
# version   : 2004-2005
# author    : Hans Hagen
#
# project   : ConTeXt / eXaMpLe
# concept   : Hans Hagen
# info      : j.hagen@xs4all.nl
# www       : www.pragma-ade.com

banner = ['RlxTools', 'version 1.0.1', '2004/2005', 'PRAGMA ADE/POD']

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

require 'base/switch'
require 'base/logger'
require 'base/system'
require 'base/kpse'

require 'fileutils'
# require 'ftools'
require 'rexml/document'

class Commands

    include CommandBase

    # <?xml version='1.0 standalone='yes'?>
    # <rl:manipulators>
    #    <rl:manipulator name='lowres' suffix='pdf'>
    #         <rl:step>
    #             texmfstart
    #             --verbose
    #             --iftouched=<rl:value name='path'/>/<rl:value name='file'/>,<rl:value name='path'/>/<rl:value name='prefix'/><rl:value name='file'/>
    #             pstopdf
    #             --method=5
    #             --inputpath=<rl:value name='path'/>
    #             --outputpath=<rl:value name='path'/>/<rl:value name='prefix'/>
    #             <rl:value name='file'/>
    #         </rl:step>
    #     </rl:manipulator>
    # </rl:manipulators>
    #
    # <?xml version='1.0' standalone='yes'?>
    # <rl:library>
    #     <rl:usage>
    #         <rl:type>figure</rl:type>
    #         <rl:state>found</rl:state>
    #         <rl:file>cow.pdf</rl:file>
    #         <rl:suffix>pdf</rl:suffix>
    #         <rl:path>.</rl:path>
    #         <rl:conversion>lowres</rl:conversion>
    #         <rl:prefix>lowres/</rl:prefix>
    #         <rl:width>276.03125pt</rl:width>
    #         <rl:height>200.75pt</rl:height>
    #     </rl:usage>
    # </r:library>

    def manipulate

        procname = @commandline.argument('first')  || ''
        filename = @commandline.argument('second') || ''

        procname = Kpse.found(procname)

        if procname.empty? || ! FileTest.file?(procname) then
            report('provide valid manipulator file')
        elsif filename.empty? || ! FileTest.file?(filename) then
            report('provide valid resource log file')
        else
            begin
                data = REXML::Document.new(File.new(filename))
            rescue
                report('provide valid resource log file (xml error)')
                return
            end
            begin
                proc = REXML::Document.new(File.new(procname))
            rescue
                report('provide valid manipulator file (xml error)')
                return
            end
            report("manipulator file: #{procname}")
            report("resourcelog file: #{filename}")
            begin
                nofrecords, nofdone = 0, 0
                REXML::XPath.each(data.root,"/rl:library/rl:usage") do |usage|
                    nofrecords += 1
                    variables = Hash.new
                    usage.elements.each do |e|
                        variables[e.name] = e.text.to_s
                    end
                    report("processing record #{nofrecords} (#{variables['file'] || 'noname'}: #{variables.size} entries)")
                    if conversion = variables['conversion'] then
                        report("testing for conversion #{conversion}")
                        if suffix = variables['suffix'] then
                            suffix.downcase!
                            if ! suffix.empty? && variables['file'] && variables['file'] !~ /\.([a-z]+)$/i then
                                variables['file'] += ".#{suffix}"
                            end
                            if file = variables['file'] then
                                report("conversion #{conversion} for suffix #{suffix} for file #{file}")
                            else
                                report("conversion #{conversion} for suffix #{suffix}")
                            end
                            pattern = "@name='#{conversion}' and @suffix='#{suffix}'"
                            if steps = REXML::XPath.first(proc.root,"/rl:manipulators/rl:manipulator[#{pattern}]") then
                                localsteps = steps.deep_clone
                                ['rl:old','rl:new'].each do |tag|
                                    REXML::XPath.each(localsteps,tag) do |extras|
                                        REXML::XPath.each(extras,"rl:value") do |value|
                                            if name = value.attributes['name'] then
                                                substitute(value,variables[name.to_s] || '')
                                            end
                                        end
                                    end
                                end
                                old = REXML::XPath.first(localsteps,"rl:old")
                                new = REXML::XPath.first(localsteps,"rl:new")
                                if old && new then
                                    old, new = justtext(old.to_s), justtext(new.to_s)
                                    variables['old'], variables['new'] = old, new
                                    begin
                                        [old,new].each do |d|
                                            File.makedirs(File.dirname(d))
                                        end
                                    rescue
                                        report("error during path creation")
                                    end
                                    report("old file #{old}")
                                    report("new file #{new}")
                                    level = if File.needsupdate(old,new) then 2 else 0 end
                                else
                                    level = 1
                                end
                                if level>0 then
                                    REXML::XPath.each(localsteps,"rl:step") do |command|
                                        REXML::XPath.each(command,"rl:old") do |value|
                                            replace(value,old)
                                        end
                                        REXML::XPath.each(command,"rl:new") do |value|
                                            replace(value,new)
                                        end
                                        REXML::XPath.each(command,"rl:value") do |value|
                                            if name = value.attributes['name'] then
                                                substitute(value,variables[name.to_s])
                                            end
                                        end
                                        str = justtext(command.to_s)
                                        # str.gsub!(/(\.\/)+/io, '')
                                        report("command #{str}")
                                        System.run(str) unless @commandline.option('test')
                                        report("synchronizing #{old} and #{new}")
                                        File.syncmtimes(old,new) if level > 1
                                        nofdone += 1
                                    end
                                else
                                    report("no need for a manipulation")
                                end
                            else
                                report("no manipulator found")
                            end
                        else
                            report("no suffix specified")
                        end
                    else
                        report("no conversion needed")
                    end
                end
                if nofdone > 0 then
                    jobname = filename.gsub(/\.(.*?)$/,'') # not 'tuo' here
                    tuoname = jobname + '.tuo'
                    if FileTest.file?(tuoname) && (f = File.open(tuoname,'a')) then
                        f.puts("%\n% number of rlx manipulations: #{nofdone}\n")
                        f.close
                    end
                end
            rescue
                report("error in manipulating files: #{$!}")
            end
            begin
                logname = "#{filename}.log"
                File.delete(logname) if FileTest.file?(logname)
                File.copy(filename,logname)
            rescue
            end
        end

    end

    private

    def justtext(str)
        str = str.to_s
        str.gsub!(/<[^>]*?>/o, '')
        str.gsub!(/\s+/o, ' ')
        str.gsub!(/&lt;/o, '<')
        str.gsub!(/&gt;/o, '>')
        str.gsub!(/&amp;/o, '&')
        str.gsub!(/&quot;/o, '"')
        str.gsub!(/[\/\\]+/o, '/')
        return str.strip
    end

    def substitute(value,str='')
        if str then
            begin
                if value.attributes.key?('method') then
                    str = filtered(str.to_s,value.attributes['method'].to_s)
                end
                if str.empty? && value.attributes.key?('default') then
                    str = value.attributes['default'].to_s
                end
                value.insert_after(value,REXML::Text.new(str.to_s))
            rescue Exception
            end
        end
    end

    def replace(value,str)
        if str then
            begin
                value.insert_after(value,REXML::Text.new(str.to_s))
            rescue Exception
            end
        end
    end

    def filtered(str,method)

        str = str.to_s # to be sure
        case method
            when 'name' then # no path, no suffix
                case str
                    when /^.*[\\\/](.+?)\..*?$/o then $1
                    when /^.*[\\\/](.+?)$/o      then $1
                    when /^(.*)\..*?$/o          then $1
                    else                              str
                end
            when 'path'     then if str =~ /^(.+)([\\\/])(.*?)$/o then $1 else ''  end
            when 'suffix'   then if str =~ /^.*\.(.*?)$/o         then $1 else ''  end
            when 'nosuffix' then if str =~ /^(.*)\..*?$/o         then $1 else str end
            when 'nopath'   then if str =~ /^.*[\\\/](.*?)$/o     then $1 else str end
            else                                                               str
        end
    end

end

class Commands

    include CommandBase

    @@xmlbanner = "<?xml version='1.0' standalone='yes'?>"

    def identify(resultfile='rlxtools.rli')
        if @commandline.option('collect') then
            begin
                File.open(resultfile,'w') do |f|
                    f << "#{@@xmlbanner}\n"
                    f << "<rl:identification>\n"
                    @commandline.arguments.each do |filename|
                        if state = do_identify(filename) then
                            report("#{filename} is identified")
                            f << state
                        else
                            report("unable to identify #{filename}")
                        end
                    end
                    f << "</rl:identification>\n"
                    report("result saved in #{resultfile}")
                end
            rescue
                report("error in writing result")
            end
        else
            @commandline.arguments.each do |filename|
                if state = do_identify(filename) then
                    begin
                        File.open(filename+'.rli','w') do |f|
                            f << "#{@@xmlbanner}\n"
                            f << state
                        end
                    rescue
                        report("error in identifying #{filename}")
                    else
                        report("#{filename} is identified")
                    end
                else
                    report("unable to identify #{filename}")
                end
            end
        end
    end

    private

    def do_identify(filename,centimeters=false)
        begin
            str = nil
            if FileTest.file?(filename) then
                # todo: use pdfinto for pdf files, identify is bugged
                if centimeters then
                    result = `identify -units PixelsPerCentimeter -format \"x=%x,y=%y,w=%w,h=%h,b=%b\" #{filename}`.chomp.split(',')
                else
                    result = `identify -units PixelsPerInch       -format \"x=%x,y=%y,w=%w,h=%h,b=%b\" #{filename}`.chomp.split(',')
                end
                tags = Hash.new
                result.each do |r|
                    if rr = r.split("=") then
                        tags[rr[0]] = rr[1]
                    end
                end
                size   = (tags['b']||0).to_i
                width  = unified(tags['w']||0,tags['x']||'1')
                height = unified(tags['h']||0,tags['y']||'1')
                if size > 0 then
                    str = ''
                    str << "<rl:identify name='#{File.basename(filename)}'>\n"
                    str << "  <rl:size>#{size}</rl:size>\n"
                    str << "  <rl:path>#{File.dirname(filename).sub(/\\/o,'/')}</rl:path>\n"
                    str << "  <rl:width>#{width}</rl:width>\n"
                    str << "  <rl:height>#{height}</rl:height>\n"
                    str << "</rl:identify>\n"
                end
            else
                str = nil
            end
        rescue
            str = nil
        end
        return str
    end

    def unified(dim,res)
        case res
            when /([\d\.]+)\s*PixelsPerInch/io then
                sprintf("%.4fin",dim.to_f/$1.to_f)
            when /([\d\.]+)\s*PixelsPerCentimeter/io then
                sprintf("%.4fcm",dim.to_f/$1.to_f)
            when /([\d\.]+)\s*PixelsPerMillimeter/io then
                sprintf("%.4fmm",dim.to_f/$1.to_f)
            when /([\d\.]+)\s*PixelsPerPoint/io then
                sprintf("%.4fbp",dim.to_f/$1.to_f)
            else
                sprintf("%.4fbp",dim.to_f)
        end
    end

end

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

commandline.registeraction('manipulate', '[--test] manipulatorfile resourselog')
commandline.registeraction('identify'  , '[--collect] filename')

commandline.registeraction('help')
commandline.registeraction('version')

commandline.registerflag('test')
commandline.registerflag('collect')

commandline.expand

Commands.new(commandline,logger,banner).send(commandline.action || 'help')