docgenerator 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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