Class: Docgenerator::Document

Inherits:
Object
  • Object
show all
Includes:
Docgenerator, Rake::DSL
Defined in:
lib/docgenerator/document.rb

Overview

Container for a document.

Each document contains a header and a body. Both are special Element classes.

Constant Summary collapse

VALID_META_TAGS =

Valid meta tags, used for HTML

This meta-tags are used by sitegenerator

%w{
  keywords
  description
  id
  altlang
  toc
  pdf
  odp
  amazon
  forum
  feed
}
PREFIX_ENDFLAG =

Constant to detect the generated header

'Generation-Info-End'
@@givemessage =
[:change, :nochange]

Constants included from Docgenerator

ATTR_LANG, CSS_BORDER, CSS_COLORS, CSS_WIDTH, DOCGENERATOR_DEFAULT_LOGGER, DOCGENERATOR_LOGGER, ENDTAG, HTML_ATTR_ALIGN, HTML_ATTR_ALL, HTML_ATTR_CORE, HTML_ATTR_EVENTS, HTML_ATTR_I18N, HTML_ATTR_VALIGN, VERSION

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Docgenerator

#set_option_defaults, set_option_defaults, trace_off, trace_on, trace_on?

Constructor Details

#initialize(settings = {}) ⇒ Document

Create a new document. There are different document templates supported. The templates refer to the corresponding TeX-classes.

  • article

  • report

  • book



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
# File 'lib/docgenerator/document.rb', line 63

def initialize( settings = {} )
  #Set template defaults
  @template  = {
    :html    => DocumentTemplate[:html],
    :latex  => DocumentTemplate[:article_utf8],
    :text    => DocumentTemplate[:text],
    :wiki    => DocumentTemplate[:wiki],
  }
  @language  = 'ngerman' #default
  @date  = nil

  ##>>>>temporary check, old interface
  #~ puts settings.inspect
  if ! settings.is_a?(Hash)
    #Make an exception to correct data
    raise "Old interface for Document#new, use :template => "
    settings = { :template => settings }
  end
  ##<<<<temporary check, old interface
  
  @log = settings[:log]
  if ! @log
    @log = Log4r::Logger.new( settings[:logname] || 'Doc', Log4r::WARN )
    @log.outputters = Log4r::StdoutOutputter.new('log_xxx')
  end
  @log.extend(Logger_INFO) unless @log.respond_to?(:INFO)
  
  @meta  = {}  #some meta-Tags for HTML
  @body  = element( :body, { :log => @log } )
  @head  = element( :head, { :log => @log } )
  #~ @body.part_of << self
  #~ @head.part_of << self
  @options  = []
  #Flag to avoid double definition of docinfo()
  @docinfo_called = false
  @creator  = nil

  settings.each{|key, value|
    case key
      when :title, :shorttitle, :subtitle, :author, :date, :location, :keywords, :description, :creator, :language
        self.send("#{key}=".to_sym, value)
      #~ when :odp, :pdf, :amazon  #VALID_META_TAGS
      when *VALID_META_TAGS.map{|mkey|mkey.to_sym}
        self.meta( key, value )
      when :template
         [value].flatten.each{|template|
              case template
                when DocumentTemplate
                  @template[template.target] = template
                when Symbol
                  templ = DocumentTemplate[template]
                  if ! templ.is_a?( DocumentTemplate )
                    @log.fatal("Unknown template #{template}, valid:\n\t#{DocumentTemplate.keys.join("\n\t")}") if @log.fatal?
                  else
                    @template[templ.target] = templ
                  end
              end 
        } #templates
      when :css
        if ! File.exist?(value)
          @log.warn("CSS-Reference not found (#{value})") if @log.warn?
        end
        #~ @head << element(:htmlonly,{}, <<css
  #~ <meta http-equiv="Content-Style-Type" content="text/css">
  #~ css
  #~ <link rel="stylesheet" type="text/css" href="#{value}">
                  #~ ).cr
        @head << element( :link, { 
                    :href    => value,
                    :rel    => 'stylesheet',
                    :type  => "text/css" } ).cr. restrict_to(:html)
      when :maketitle
        #:maketitle is defined, but we have also to check, if it is true
        @body << element(:maketitle ).cr if settings[key]
      when :latex_options
        add_option(value)
      when :log, :logname
      else
        @log.warn("Document.new: Unknown setting #{key.inspect} (=#{value.inspect})") if @log.warn?
    end
  } #settings

  if @template[:html].encoding.name == 'UTF-8'
    @log.debug("Document.new: set UTF-8 for html") if @log.debug?
    @head << element(:htmlonly,{},'<meta http-equiv="Content-Type" content="text/html; charset=utf-8">').Cr
  end

end

Instance Attribute Details

#author=(value) ⇒ Object (writeonly)

Set document description



161
162
163
# File 'lib/docgenerator/document.rb', line 161

def author=(value)
  @author = value
end

#bodyObject (readonly)

body and head are lists, containing elements.



152
153
154
# File 'lib/docgenerator/document.rb', line 152

def body
  @body
end

#creator=(value) ⇒ Object (writeonly)

Set document description



161
162
163
# File 'lib/docgenerator/document.rb', line 161

def creator=(value)
  @creator = value
end

#date=(value) ⇒ Object (writeonly)

Set document description



161
162
163
# File 'lib/docgenerator/document.rb', line 161

def date=(value)
  @date = value
end

#description=(value) ⇒ Object (writeonly)

Set document description



161
162
163
# File 'lib/docgenerator/document.rb', line 161

def description=(value)
  @description = value
end

#headObject (readonly)

body and head are lists, containing elements.



152
153
154
# File 'lib/docgenerator/document.rb', line 152

def head
  @head
end

#keywords=(value) ⇒ Object (writeonly)

Set document description



161
162
163
# File 'lib/docgenerator/document.rb', line 161

def keywords=(value)
  @keywords = value
end

#language=(value) ⇒ Object (writeonly)

Sets the attribute language

Parameters:

  • value

    the value to set the attribute language to.



162
163
164
# File 'lib/docgenerator/document.rb', line 162

def language=(value)
  @language = value
end

#logObject (readonly)

Logger for the document



155
156
157
# File 'lib/docgenerator/document.rb', line 155

def log
  @log
end

#runtex=(value) ⇒ Object (writeonly)

Flag, if tex should be started immediate after document creation.



164
165
166
# File 'lib/docgenerator/document.rb', line 164

def runtex=(value)
  @runtex = value
end

#shorttitleObject

Document title



157
158
159
# File 'lib/docgenerator/document.rb', line 157

def shorttitle
  @shorttitle
end

#subtitleObject

For usage in ocument title



159
160
161
# File 'lib/docgenerator/document.rb', line 159

def subtitle
  @subtitle
end

#templateObject (readonly)

Returns the value of attribute template.



153
154
155
# File 'lib/docgenerator/document.rb', line 153

def template
  @template
end

#titleObject

Document title



157
158
159
# File 'lib/docgenerator/document.rb', line 157

def title
  @title
end

Class Method Details

.givemessageObject



181
# File 'lib/docgenerator/document.rb', line 181

def Document.givemessage();  @@givemessage;  end

.givemessage=(value = [:change, :nochange]) ⇒ Object

Define, if there should be a message in case of: -:change Document changed -:nochange Document existed, but is unchanged Please provide an array with the wanted values.



177
178
179
# File 'lib/docgenerator/document.rb', line 177

def Document.givemessage=( value = [:change, :nochange] )
  @@givemessage = value
end

.runtex(filename, options = [:touch, :statistic, :clean]) ⇒ Object

Call rake4latex to translate the file.



598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
# File 'lib/docgenerator/document.rb', line 598

def runtex( filename, options = [:touch, :statistic, :clean])
  if ! defined? Rake4LaTeX          
    puts <<warning
Rake4LaTeX not loaded!
Please use one of the following commands:
  require 'rake4latex'
  require 'rake4latex_latex'
  require 'rake4latex_pdflatex'
  require 'rake4latex_xelatex'
  ...
  Document.save(
:runtex => [:statistic, :clean]
#or with more options:
#:runtex => [:touch, :texerrors_allowed, :statistic, :clean]
    )
warning
    begin
      #interprete old interface before 1.2.0
      gem 'rake4latex', '>= 0.1.3'
      case options
        when :latex; require 'rake4latex_latex'
        when :pdflatex; require 'rake4latex_pdflatex'
        when :xelatex; require 'rake4latex_xelatex'
        else;  require 'rake4latex'
      end #format
      options = [:touch, :statistic, :clean]
    rescue LoadError
      puts "Sorry, I didn't find the rake4latex to translate #{filename}"
      return false
    end unless defined? Rake4LaTeX
  end # ! defined? Rake4LaTeX
  
  case options
    when Array  #ok
    when true; options = [:touch, :statistic, :clean]
    else
      options = [:touch, :statistic, :clean]
      puts "Please change runtex-options to: #{options.inspect}"
  end #case options
    
  extend Rake::DSL if defined? Rake::DSL  #needed for task generation
  #Taskname must be uniq for this file.
  runtex = "Define TeX-task #{filename.ext('pdf')}"
  rake4latex = Rake4LaTeX::Basefile.set(filename)#needed for clean...
  rake4latex [:texerrors_allowed] = true  if options.delete(:texerrors_allowed)
  rake4latex[:loglevel] = Log4r::WARN

  if options.delete(:touch)
    task :touch => filename.ext('tex')
    task runtex => :touch   
  end   #if options.delete(:touch)
  task runtex => filename.ext('pdf')
  task runtex => options
  
  Rake.application[runtex].invoke
  options.each{|option| Rake.application[option].reenable }
end

.texify(input) ⇒ Object

Make some basic replacements for TeX. There is no sense to use it with HTML.

Better solution:

  • use elements from Docgenerator::Characters

  • Use unicode in TeX-Document.

  • Put string into \path, \verb or similar.

Usage:

Docgenerator::Document.texify('text_with_underscore')


668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
# File 'lib/docgenerator/document.rb', line 668

def texify( input )
  out = input.strip
  
  #~ out.gsub!( /&/, '\\\&')  #geht schief. erzeugt <<body>>...
  #~ out.gsub!( /\\/, '\\\\')
  out.gsub!( /%/, '\%')
  out.gsub!( /\$/, '\$')
  out.gsub!( /&/, '\\\\&')
  out.gsub!( /_/, '\_')
  out.gsub!( /#/, '\#')
  #~ out.gsub!( /\^/, '\(\wedge\)')
  out.gsub!( /\^/, '\textasciicircum{}')
  #~ out.gsub!( /€/, '\euro ')  #regexp is in utf-8 -- may make problems.
  return out
end

Instance Method Details

#add_option(*options) ⇒ Object

Add an class option for LaTeX



225
226
227
# File 'lib/docgenerator/document.rb', line 225

def add_option( *options)
  options.each{|option|    @options << option }
end

#docinfoObject

Prepare the docinfo. May be called only once.

If the title should change (e.g. when you save the document twice in different versions) you can do it via the parameter.



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/docgenerator/document.rb', line 203

def docinfo()
  #If this method is called twice (e.g. with save to tex and html),
  #then the title is doubled in the second output.
  #(Elements are added to @head)
  return nil if @docinfo_called
  @docinfo_called = true
  
  #Build docinfo.
  result = []
  result << element( :title, {:short => shorttitle }, @title).CR if @title
  result << element( :subtitle, {}, @subtitle).CR if @subtitle
  result << element( :author, {}, @author).cr if @author
  result << element( :date, {}, @date).cr if @date
  result << element( :keywords, {}, @keywords).cr if @keywords
  result << element( :metadescription, {}, @description).cr if @description
  result << element( :creator, {}, @creator).cr if @creator
  @meta.each{ |key, content|
    result << element( :meta, { :name => key, :content => content } ).cr
  }
  return result
end

#inspectObject

Document#to_doc



587
588
589
# File 'lib/docgenerator/document.rb', line 587

def inspect()
  return "#<Document '#{self.title}'>"
end

#keyword_add(keyword) ⇒ Object

Add more keywords



166
167
168
# File 'lib/docgenerator/document.rb', line 166

def keyword_add( keyword )
  @keywords << ", #{keyword}"
end

#meta(key, content) ⇒ Object

Add a meta-tag-information for the HTML-Output.



185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/docgenerator/document.rb', line 185

def meta( key, content )
  key  = key.to_s
  
  if ! VALID_META_TAGS.include?(key)
    @log.warn("Unknown meta-tag #{key} (#{content})") if @log.warn?
    @log.info("\tAllowed meta-tags: #{VALID_META_TAGS.join(', ')}") if @log.info?
  end
  if @meta[key]
    @log.warn("Double definition meta-tag #{key} (old: #{@meta[key]}, new: #{content})") if @log.warn? and @meta[key] != content
  end
  
  @meta[key] = content
end

#save(filename, options = {}) ⇒ Object

Save the file. The type of the document is determined by the file extensison.

Supported document types are:

  • tex (*.tex)

  • context (*.mkiv)

  • html (*.htm[l])

  • (wiki wikimedia/creole)

  • (text)

Depending on a template, different results are created.

There is a comparison between an already existing file and the new one. To write a new version, the document must change and overwrite must be true.

Valid options:

  • :replacements

    It is possible to give pairs of RegExp (pattern) and replacements to the result.
    
  • overwrite [true]

    allows to overwrite
    
  • target

    In most cases this is defined by the extension.
    This attribute allows to set :context for tex-files.
    
  • :additional_options

    List of options.
    If you want to give parameters to specific 'Element'-objects you can define the keys here.
    If you don't make it, you get warnings about undefined options.
    


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
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
# File 'lib/docgenerator/document.rb', line 341

def save( filename, options = {} )
  #check old interface before 1.1.0
  case options
    when true, false
      @log.warn( "Old interface for Document.save, use :overwrite => true/false" ) if @log.error?
      options = { :overwrite => options }
    end    
    
  options = {
    :overwrite      => true, 
    :replacements => {} ,
    :additional_options => [],
    :target   => nil,   #Will be set by extension
    :runtex   => @runtex,
  }.update(options)
  
  #Check valid options
  options.each{|key,value|
    case key
      when :overwrite, :replacements, :target
      when :additional_options
      else
        if options[:additional_options].include?(key)
          @log.warn( "unknown option #{key.inspect} for Document.save" ) if @log.warn?
        else
          @log.debug( "Save-Specific option #{key.inspect} in Document.save" ) if @log.debug?
        end
    end
  }
  
  if ! @template
    @log.error( "No template available to create #{filename}" ) if @log.error?
    return false
  end
  
  #Determine the target document type, depending on extension.
  extension = File.basename( filename ).split( /\./).last
  case extension
  when /tex/i
    target  = :latex
    #Take context if requested.
    target  = :context if options[:target] == :context
  when /mk(ii|iv)/i #Mark II and IV
    target  = :context
  when /htm[l]?/i
    target  = :html
  when /txt/
    target  = :text
  when /wiki/
    target = :wiki
    target  = :creole if options[:target] == :creole
  when /creole/
    target  = :creole
  else
    @log.fatal( "Unknown Extension #{extension} for #{File.basename( filename )}" ) if @log.fatal?
    raise "Unknown Extension #{extension}"
  end
  
  #Check if the wanted target makes sense
  case options[:target]
    when nil #nothing predefined -> no check necessary
    when target #Same as determined by extension -> no check necessary
    else  #Requested target is another one.
      @log.warn( "Target #{options[:target]} doesn't match extension. Recommended: #{target}" ) if @log.warn?
      target = options[:target]
  end
  
  #Create new version.
  new = to_doc( target, {
                  :template=> @template[target],
                  :filename => filename,
                  :replacements => options[:replacements],
                  :log  => @log
                  })

  #Define a file prefix.
  prefix = [ nil,
    ( target == :latex ? "!TEX encoding= %s" : 'encoding: %s' ) % new.encoding,
    #~ "!TEX TS-program = lualatex",
      "Build by\t#{__FILE__}",
      "Dir:\t\t#{Dir.pwd}",
      "Creator:\tDocgenerator #{Docgenerator::VERSION} - http://rubygems.org/gems/docgenerator",
      "Target:\t\t#{filename}",
      "#{Time.now.strftime('%Y/%m/%d %H:%M:%S')}",
      nil,
      "#{PREFIX_ENDFLAG}"
    ].join("\n\t")

  #Get previous content if available
  old = nil
  if File.exist?( filename )
      #read the file. We expect to have the same encoding.
      File.open(filename, 'r', :external_encoding => new.encoding){|f| old = f.read }
      old = File.read(filename) unless old.valid_encoding?  #
  end
  prefix.encode!(new.encoding)  #for later sub...
  case target
  when :latex, :context
    prefix.gsub!( /^/, '%' )
    old.sub!(/\A.*#{PREFIX_ENDFLAG}/m, '<<prefix>>' ) if old
  when :html
    #Delete prefix (with generation time) for later compare.
    old.sub!(/\A.*#{PREFIX_ENDFLAG}/m, "<!--\n<<prefix>>" ) if old
  when :text
  when :wiki
  when :creole
    #~ new.squeeze!("\n")
  else
    @log.fatal( "Unknown target #{target} for #{File.basename( filename )}" ) if @log.fatal?
    raise "Unknown target #{target}"
  end
  #Make it a bit more compact.
  #TeX requires at least 2 \n for paragraph changes
  new.gsub!(/\n+\n\n/, "\n\n")
  
  if ! new.kind_of?( String )
    @log.error( "New is wrong type: #{new.inspect}" ) if @log.error?
  end
  
  if new != old
    new.sub!( '<<prefix>>',  prefix)
    if ( File.exist?( filename ) and ! options[:overwrite] )
      @log.warn("Datei #{filename} exist already.") if @log.warn?
      if $stdin.tty? #only when called in a shell
        puts "Datei #{filename} exist already \nContinue [yn]?"
        answer = $stdin.gets()  
        if ! ( answer =~ /[YyjJ].*/ )
          puts "Bye"
          return false
        end
      end
      @log.info( "Overwrite #{filename} after confirmation" ) if @log.info?
    end  
    File.open( filename, 'w', :external_encoding => new.encoding ){|f|
      f << new
    }
    @log.INFO( "Save changed\t#{filename}") if @log.INFO?
    #Save copy of old version (attention, *.bak makes no control on tex or html)
    #~ f = File.new( filename.sub( extension, 'bak'), 'w' )
    #~ f << old
    #~ f.close
    Document.runtex( filename, options[:runtex] ) if options[:runtex] and [:latex, :context].include?(target)
    return true
  elsif old
    @log.INFO("Unchanged\t#{filename}") if @log.INFO?
    return false
  end    
end

#to_doc(target, options = {}) ⇒ Object

Build the content of a document for the target format.

Supported target formats are:

  • :latex

  • :html

  • :text

  • :wiki

  • :creole

Options are passed in a Hash:

  • :log [required]: a logger

  • :template [required]: a Docgenerator::DocumentTemplate-instance

  • :filename [recommended]: used in logging informations

  • :replacements [optional]: Hash with regexp/result pairs to modify the output.

If the standard templates are used, there is a <<prefix>> left (used from Document#save to include some additional information).

You may use a String as dummy template.

If the method is called directly to prepare document snipplets, you can use:

doc = Document.new()
# ... fill doc
puts doc.to_doc( :latex, 
                  :log  => Log4r::Logger.new('testlog'),
                  :template=> '<<body>>',
                  #~ :template=> doc.template[:latex],
                  :filename => 'dummy',
                  #~ :replacements => { /xxx/ => 'cc' }
                )


522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
# File 'lib/docgenerator/document.rb', line 522

def to_doc( target, options = {} )
  #~ o = set_option_defaults(options)
  options[:template] ||= @template[target]
  options[:log] ||= @log
  
  options[:log].info( "Build document for #{target.inspect}" ) if options[:log].info?

  #check target
  case target
  when :latex
    add_option( @language )
  when :context
    options[:log].warn( "Support for #{target} under development" ) if options[:log].warn?    
  when :html
  when :text
  when :wiki
    options[:log].warn( "Support for #{target} stopped" ) if options[:log].warn?
  when :creole
    options[:log].warn( "Support for #{target} under development" ) if options[:log].warn?
  else
    options[:log].fatal( "Unknown target #{target} for #{options[:filename]}" ) if options[:log].fatal?
    return ''
  end
  
  #set template to variable new
  case options[:template]
    when DocumentTemplate
      new = options[:template].template
    when String
      new = options[:template]
    else
      options[:log].error( "No template available to create #{target.inspect} #{self.class}" ) if options[:log].error?
      return ''
  end
  if ! new.kind_of?( String )
    options[:log].fatal( "New is wrong type: #{new.inspect}") if options[:log].fatal?
    return ''
  end
  encoding = new.encoding

  #Set replacements
  replacements = options[:replacements] || {}

  options[:document] = self
  
  @head  << self.docinfo()
  options[:log].debug( "Build header data") if options[:log].debug?
  new.sub!( '<<head>>',  @head.to_doc(target, options ).encode(encoding))
  
  options[:log].debug( "Build body") if options[:log].debug?
  #The block version is needed.
  #Else there is a problem with \&  
  #Example: puts 'xx<<body>>xx'.sub('<<body>>', 'a\&b')
  new.sub!( '<<body>>'){ @body.to_doc(target, options).encode(encoding) }
  
  new.sub!( '<<classoptions>>',  @options.uniq.join(',').encode(encoding))
  
  #manipulate result
  replacements.each{|pattern, replace |
    options[:log].info("Replace text #{pattern.source} with #{replace}" ) if options[:log].info?
    new.gsub!( pattern, replace ) if replace
  }
  return new
end

#toc(options = {}) ⇒ Object

Prepare a table of content.

There is no sense to use it with LaTeX, better you use \tableofcontents

Options:

  • id: Id of div, needed for CSS-formatting

  • tocfilename: Added as prefix to link. can be used to make toc for in another file (cross-referencing)

  • level: Depth of toc

The document should be filled before you add the toc. You an use Element#insertbefore:

doc.body.insertbefore(start, doc.toc)


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
# File 'lib/docgenerator/document.rb', line 240

def toc( options = {} )
  opt = {
    :id => 'toc',
    :tocfilename => nil,  #Can be used to build cross references
    :level  => 3,
    :log  => @log
  }.update(options)    

  opt[:level] = 3 unless opt[:level]
  opt[:log].info("Prepare Table of contents, options: #{options.keys.inspect}") if opt[:log].info?
  opt[:log].debug("Options: #{[:id, :level, :tocfilename].map{|k| "#{k}: #{opt[k].inspect}"}.join(',')}") if opt[:log].debug?
  
  toc = element(:div, { :id => opt[:id] }).cR    
  opt[:pre].is_a?(String) ? toc << element(:p, {}, opt[:pre] ).cr : toc << opt[:pre]
  
  toc_uls = [ toc ]

  #fixme
  #It would be nicer to get something like 1-1-1 instead of a counter.
  toccnt  = 0
  toc_ids = [ :chapter, :section,:subsection, :subsubsection, :paragraph, :subparagraph,:minisec]
  @body.content.flatten.each{|el|
    if ! el.respond_to?(:element_ids)
      #e.g. Wikitext. Has a to_doc, but doesn't react on flatten.
      opt[:log].error("Toc: Element without ids #{el.class}") if opt[:log].error?
      next
    end  
    #Get section depth. Determine it via the position inside toc_ids.
    depth = toc_ids.index((el.element_ids & toc_ids)[0])
    next if ! depth #Element is no header
    
    opt[:log].debug("Toc: Found entry on level #{depth}: <#{el.content}>") if opt[:log].debug?
    #Create an id if not already defined
    if el[:id].content.empty?
      el[:id] << "toc#{toccnt += 1}"
    end
    if depth > opt[:level]
      opt[:log].debug("Toc: Reject by level #{el.content}") if opt[:log].debug?
      next 
    end

    #Check missing levels
    (depth - toc_uls.size).times{|i|
      opt[:log].warn("Toc: Jump over section level #{depth-1+i} (#{opt[:tocfilename]})") if opt[:log].warn?
      toc_uls << element(:ul, {}, element(:li ).cr ).cR
      if toc_uls.size == 2 #First entry
        toc_uls.first << toc_uls.last
      else
        toc_uls[-2].content.last << toc_uls.last
      end
    }
    
    #Build new toc-list and deletet bigger ones
    if ! toc_uls[depth]
      opt[:log].debug("Toc: Start new level #{depth}") if opt[:log].debug?
      toc_uls << element(:ul).cR
      if depth == 1
        toc << toc_uls[depth]
      else
        toc_uls[depth -1 ].content.last << toc_uls[depth]
      end
    else  #delete levels under actual level
      if toc_uls.slice!(depth+1..toc_uls.size+1)
        opt[:log].debug("Toc: Stop level > #{depth}") if opt[:log].debug?
      end
    end
    toc_uls[depth] << element(:li, {}, element(:a, {:href => "#{opt[:tocfilename]}##{el[:id]}" }, el.content ) ).cr
  }
  opt[:post].is_a?(String) ? toc << element(:p, {}, opt[:post] ).cr : toc << opt[:post]
  
  return toc
end