docgenerator 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,374 @@
1
+ #
2
+ #Container for a document.
3
+ #
4
+ #Each document contains a header and a body. Both are special Element classes.
5
+ #
6
+ class Document
7
+ #Create a new document.
8
+ #There are different document templates supported.
9
+ #The templates refer to the corresponding TeX-classes.
10
+ #- article
11
+ #- report
12
+ #- book
13
+ def initialize( template = nil )
14
+ #Set template defaults
15
+ @template = {
16
+ :html => DocumentTemplate[:html],
17
+ :latex => DocumentTemplate[:article],
18
+ :text => DocumentTemplate[:text],
19
+ :wiki => DocumentTemplate[:wiki],
20
+ }
21
+ self.set_template( template ) if template
22
+ @body = element( :body )
23
+ @head = element( :head )
24
+ #~ @body.part_of << self
25
+ #~ @head.part_of << self
26
+ @body.part_of_doc << self
27
+ @head.part_of_doc << self
28
+ @language = 'ngerman'
29
+ @options = []
30
+ @meta = {} #some meta-Tags for HTML
31
+ #Flag to avoid double definition of docinfo()
32
+ @docinfo_called = false
33
+ @creator = nil
34
+ end
35
+ #
36
+ #Set Template to Generate a target.
37
+ #
38
+ #Compatibility for old version:
39
+ #-If template is an array, each entry is used as a key for DocumentTemplate.
40
+ #
41
+ def set_template( template, target = :latex )
42
+ template = [ template ] if template.kind_of?( Symbol )
43
+ #Compatibility for older version: Template can be given direct
44
+ if template.kind_of?(String )
45
+ #~ puts "Set Template with string"
46
+ @template[target] = template
47
+ return
48
+ elsif template.kind_of?(Array )
49
+ template.each{|templ|
50
+ if SUPPORTED_LaTeX_TEMPLATES.include?( templ )
51
+ target = :latex
52
+ elsif SUPPORTED_HTML_TEMPLATES.include?( templ )
53
+ target = :html
54
+ elsif /text/ =~ templ.to_s or /txt/ =~ templ.to_s
55
+ target = :txt
56
+ else
57
+ puts "Unknown template conversion for #{templ}"
58
+ target = :latex
59
+ end
60
+ @template[target] = DocumentTemplate[templ]
61
+ if ! @template[target]
62
+ puts "Unknown template #{templ}, valid:\n\t#{DocumentTemplate.keys.join("\n\t")}" #fixme raise
63
+ elsif ! @template[target].kind_of?(String )
64
+ puts "Template no string #{@template.inspect}"
65
+ @template[target] = @template[target].to_s
66
+ end
67
+ }
68
+ else
69
+ raise "Should set template, but don't know how #{template.inspect}"
70
+ end
71
+ end
72
+ #body and head are lists, containing elements.
73
+ attr_reader :body, :head
74
+ attr_reader :template
75
+ #Document title
76
+ attr_accessor :title
77
+ #Set document description
78
+ attr_writer :author, :date, :keywords, :description, :creator
79
+ attr_writer :language
80
+ #Flag, if tex should be started immediate after document creation.
81
+ attr_writer :runtex
82
+ #Add more keywords
83
+ def keyword_add( keyword )
84
+ @keywords << ", #{keyword}"
85
+ end
86
+
87
+ #Constant to detect the generated header
88
+ PREFIX_ENDFLAG = 'Generation-Info-End'
89
+ #
90
+ #Define, if there should be a message in case of:
91
+ #-:change Document changed
92
+ #-:nochange Document existed, but is unchanged
93
+ #Give an array with the wanted values.
94
+ def Document.givemessage=( value = [:change, :nochange] )
95
+ @@givemessage = value
96
+ end
97
+ @@givemessage = [:change, :nochange]
98
+ def Document.givemessage(); @@givemessage; end
99
+ #Add a meta-tag-information for the HTML-Output.
100
+ def meta( key, content )
101
+ key = key.to_s
102
+ if @meta[key]
103
+ puts "Double definition meta-tag #{key} (old: #{@meta[key]}, new: #{content})"
104
+ end
105
+ @meta[key] = content
106
+ end
107
+ #Prepare the docinfo.
108
+ #May be called only once.
109
+ #
110
+ #If the title should change (e.g. when you save the document twice in different versions)
111
+ #you can do it via the parameter.
112
+ def docinfo()
113
+ #If this method is called twice (e.g. with save to tex and html),
114
+ #then the title is doubled in the second output.
115
+ #(Elements are added to @head)
116
+ return nil if @docinfo_called
117
+ @docinfo_called = true
118
+
119
+ #Build docinfo.
120
+ result = []
121
+ result << element( :title, {:short => @meta["shorttitle"] }, @title).CR if @title
122
+ result << element( :author, {}, @author).cr if @author
123
+ result << element( :date, {}, @date).cr if @date
124
+ result << element( :keywords, {}, @keywords).cr if @keywords
125
+ result << element( :metadescription, {}, @description).cr if @description
126
+ result << element( :creator, {}, @creator).cr if @creator
127
+ @meta.each{ |key, content|
128
+ result << element( :meta, { :name => key, :content => content } ).cr
129
+ }
130
+ return result
131
+ end
132
+ def add_option( option)
133
+ @options << option
134
+ end
135
+ #Prepare a table of content.
136
+ #More or less a test.
137
+ #Use it for element :tableofcontents?
138
+ def toc()
139
+ toc = []
140
+ toccnt = 0
141
+ #~ toc = element(:ul).cR
142
+ toc_ids = [ :chapter, :section,:subsection, :subsubsection, :paragraph, :subparagraph,:minisec]
143
+ @body.content.flatten.each{|el|
144
+ depth = toc_ids.index((el.ids & toc_ids)[0])
145
+ #~ puts "#{depth.inspect}\t#{el.content}"
146
+ if el[:id].content.empty?
147
+ el[:id] << "toc#{toccnt += 1}"
148
+ end
149
+ if depth
150
+ #~ toc << element(:li,{},[ el.content])
151
+ toc << element(:a, {:href => "#{el[:id]}" }, [ '*' * depth, el.content ])
152
+ toc << element(:br).cr
153
+ end
154
+ }
155
+ return toc
156
+ end
157
+ #Save the file.
158
+ #The type of the document is determined by the file extensison.
159
+ #Supported document types are:
160
+ #- tex
161
+ #- html
162
+ #Depending on a template, different results are created.
163
+ #
164
+ #The is a comparison between an already existing file and the new one.
165
+ #To write a new version, the document must change and overwrite must be true.
166
+ #
167
+ #It is possible to give pairs of RegExp (pattern) and replacements to the result.
168
+ def save( filename, overwrite=false, replacements = {} )
169
+
170
+ if ! @template
171
+ puts "No template available to create #{filename}"
172
+ return false
173
+ end
174
+
175
+ old = nil
176
+ prefix = [ nil,
177
+ "Build by\t#{__FILE__}",
178
+ "Dir:\t\t#{Dir.pwd}",
179
+ "Creator:\t#{$0}",
180
+ "Target:\t\t#{filename}",
181
+ "#{Time.now.strftime('%Y/%m/%d %H:%M:%S')}",
182
+ nil,
183
+ "#{PREFIX_ENDFLAG}"
184
+ ].join("\n\t")
185
+ if File.exist?( filename )
186
+ f = File.new( filename )
187
+ old = f.readlines().to_s
188
+ f.close
189
+ end
190
+ #Determine the target document type, depending on extension.
191
+ extension = File.basename( filename ).split( /\./).last
192
+ case extension
193
+ when /tex/i
194
+ @target = :latex
195
+ when /htm[l]?/i
196
+ @target = :html
197
+ when /txt/
198
+ @target = :text
199
+ when /wiki/
200
+ @target = :wiki
201
+ else
202
+ raise "Unknown Extension #{extension}"
203
+ end
204
+
205
+ @@target = @target if ! UNDER_CONSTRUCTION
206
+ new = to_element( @target, @template[@target], replacements )
207
+
208
+ case @target
209
+ when :latex
210
+ prefix.gsub!( /^/, '%' )
211
+ old.sub!(/\A.*#{PREFIX_ENDFLAG}/m, '<<prefix>>' ) if old
212
+ when :html
213
+ #Delete prefix (with generation time) for later compare.
214
+ old.sub!(/\A.*#{PREFIX_ENDFLAG}/m, "<!--\n<<prefix>>" ) if old
215
+ when :text
216
+ when :wiki
217
+ #~ new.squeeze!("\n")
218
+ else
219
+ raise "Unknown target #{@target}"
220
+ end
221
+ #Make it a bit more compact.
222
+ #TeX requires at least 2 \n for paragraph changes
223
+ new.gsub!(/\n+\n\n/, "\n\n")
224
+
225
+ if ! new.kind_of?( String )
226
+ puts "New is wrong type: #{new.inspect}"
227
+ end
228
+ @target = nil
229
+
230
+ if new != old
231
+ new.sub!( '<<prefix>>', prefix)
232
+ if ( File.exist?( filename ) and !overwrite )
233
+ puts "Datei #{filename} exist already \nContinue [yn]?"
234
+ answer = $stdin.gets() if $stdin.tty? #nur bei call aus DOS-Box
235
+ if ! ( answer =~ /[YyjJ].*/ )
236
+ puts "Bye"
237
+ return false
238
+ end
239
+ end
240
+ f = File.new( filename, 'w' )
241
+ f << new
242
+ f.close
243
+ puts "Save changed\t#{filename}" if @@givemessage.include?(:change)
244
+ #Save copy of old version (attention, *.bak makes no control on tex or html)
245
+ #~ f = File.new( filename.sub( extension, 'bak'), 'w' )
246
+ #~ f << old
247
+ #~ f.close
248
+ Document.runtex( filename, @runtex ) if @runtex
249
+ return true
250
+ elsif old
251
+ puts "Unchanged\t#{filename}" if @@givemessage.include?(:nochange)
252
+ return false
253
+ end
254
+ end
255
+ #Build the content for the target format.
256
+ #Supported tagret formats are:
257
+ #- :latex
258
+ #- :html
259
+ #- :text
260
+ #If the standard templates are used, there is a <<prefix>> left
261
+ #(used from Document#save to include some additional information).
262
+ #
263
+ #If the method is called directly to prpare document snipplets, you can use:
264
+ # puts Document#to_element( :latex, '<<body>>')
265
+ #
266
+ #It is possible to give pairs of RegExp (pattern) and a replacement to the result.
267
+ def to_element( target, template=@template[target], replacements = {} )
268
+
269
+ @@target = target if ! UNDER_CONSTRUCTION
270
+ if ! template
271
+ puts "No template available to create #{target.inspect} #{self.class}"
272
+ return ''
273
+ end
274
+
275
+ new = template.dup
276
+ case target
277
+ when :latex
278
+ add_option( @language )
279
+ when :html
280
+ when :text
281
+ when :wiki
282
+ else
283
+ raise "Unknown Extension #{extension}"
284
+ end
285
+ if ! new.kind_of?( String )
286
+ puts "New is wrong type: #{new.inspect}"
287
+ end
288
+
289
+ @head << self.docinfo()
290
+ new.sub!( '<<head>>', @head.to_s)
291
+ new.sub!( '<<body>>', @body.to_s)
292
+ new.sub!( '<<classoptions>>', @options.join(','))
293
+
294
+ replacements.each{|pattern, replace |
295
+ new.gsub!( pattern, replace ) if replace
296
+ }
297
+ return new
298
+ end #Document#to_element
299
+ #Return the actual target type.
300
+ #Needed by Element#to_s to decide which method is used to prepare the output.
301
+ #
302
+ #The usage of this technic makes similar processing unposibble.
303
+ #Set in Document#save.
304
+ attr_reader :target
305
+ @@target = nil
306
+ #Set a major output routine.
307
+ def Document.target=( target )
308
+ @@target = target
309
+ end
310
+ def Document.target( element )
311
+ return @@target if @@target #Use global value for multiple outputs
312
+ case element.part_of_doc.size
313
+ when 1
314
+ return element.part_of_doc.first.target
315
+ when 0
316
+ puts "Document.target: No related document. Don't know which target to take #{element.inspect} line #{__LINE__}"
317
+ element.ancestors.flatten.each_with_index{|a,i|
318
+ puts "\tAncestor #{i}\t#{a.inspect}"
319
+ puts "\t\t\t#{a.ancestors.inspect}"
320
+ }
321
+ #~ puts caller()
322
+ return :text
323
+ else
324
+ puts "Multiple documents. Don't know which target to take #{element.inspect}"
325
+ element.part_of_doc.each{|d| puts "\t#{d.inspect}" }
326
+ return :text
327
+ end
328
+ end
329
+ #Call TeX and translate the file.
330
+ #Experimental
331
+ #Fixme: Chain
332
+ def Document.runtex( filename, format = :pdflatex )
333
+ begin
334
+ #~ require 'rtex'
335
+ require 'c:/usr/script/rtex/rtex'
336
+ rescue LoadError
337
+ puts "Sorry, didn't find the experimental tex translation tool to translate #{filename}"
338
+ return false
339
+ end
340
+ #~ puts "Unknown texfile #{filename}" if ! filename
341
+ tex = Chain.new()
342
+ case format
343
+ when :latex
344
+ tex.latex()
345
+ end
346
+ #~ Logger.level=3
347
+ tex.file = filename
348
+ begin
349
+ tex.execute()
350
+ rescue SystemExit
351
+ puts "rescued a SystemExit exception"
352
+ return false
353
+ end
354
+ end
355
+ #Make some basic replacemnts for TeX.
356
+ #There is no sense to use it with HTML.
357
+ #
358
+ #Better solution: Puts String into \path, \verb or similar.
359
+ def Document.texify( input )
360
+ out = input.strip
361
+ #~ out.gsub!( /&/, '\\\&') #geht schief. erzeugt <<body>>...
362
+ #~ out.gsub!( /\\/, '\\\\')
363
+ out.gsub!( /%/, '\%')
364
+ out.gsub!( /\$/, '\$')
365
+ out.gsub!( /&/, '\\\\&')
366
+ out.gsub!( /_/, '\_')
367
+ out.gsub!( /�/, '\euro ')
368
+ return out
369
+ end
370
+ def inspect()
371
+ return "#<Document '#{@title}'>"
372
+ #~ return "#<Document '#{@title} #{@body.inspect}>'"
373
+ end
374
+ end #Document