2 # -*- python; coding: utf-8 -*-
4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 2018 Stefan Sauer
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 """Generate html from docbook
24 The tool loads the main xml document (<module>-docs.xml) and chunks it
25 like the xsl-stylesheets would do. For that it resolves all the xml-includes.
26 Each chunk is converted to html using python functions.
28 In contrast to our previous approach of running gtkdoc-mkhtml + gtkdoc-fixxref,
29 this tools will replace both without relying on external tools such as xsltproc
32 Please note, that we're not aiming for complete docbook-xml support. All tags
33 used in the generated xml are of course handled. More tags used in handwritten
34 xml can be easilly supported, but for some combinations of tags we prefer
39 - inside 'footnote' one can have many tags, we only handle 'para'/'simpara'
40 - inside 'inlinemediaobject'/'mediaobject' a 'textobject' becomes the 'alt'
41 attr on the <img> tag of the 'imageobject'
42 - handle 'label' attributes on part/chapter/section-types
43 - the titles will have a generated prefix, such as 'Part I:'
45 - in the toc it would only be the label: 'I.'
46 - 'link' seems to add a 'title' attr to 'a' if the target has a title.
47 - we're using fixxref.MakeXRef to generate the 'a' tag, we could pass the
48 title with a default value of "" there to inject it
49 - we might need to split this a bit to first run the 'id' transform logic
50 and then do the linking.
51 - initially we could generate this as needed (we need to run the xpath on
52 each of the chunks though
53 - handle the 'xref' tag, this also need the title + the type of the target
54 - check each docbook tag if it can contain #PCDATA, if not don't check for
56 - consider some perf-warnings flag
57 - see 'No "id" attribute on'
60 - minify html: https://pypi.python.org/pypi/htmlmin/
63 sudo pip3 install anytree lxml pygments
67 ../../../gtkdoc-mkhtml2 tester tester-docs.xml
68 xdg-open db2html/index.html
73 rm html-build.stamp; time make html-build.stamp
83 from anytree
import Node
, PreOrderIter
84 from copy
import deepcopy
86 from lxml
import etree
87 from pygments
import highlight
88 from pygments
.lexers
import CLexer
89 from pygments
.formatters
import HtmlFormatter
91 from . import config
, fixxref
94 # lazily constructed lexer cache
98 HTML_FORMATTER
= HtmlFormatter(nowrap
=True)
101 class ChunkParams(object):
102 def __init__(self
, prefix
, parent
=None, min_idx
=0):
105 self
.min_idx
= min_idx
109 DONT_CHUNK
= float('inf')
110 # docbook-xsl defines the chunk tags here.
111 # http://www.sagehill.net/docbookxsl/Chunking.html#GeneratedFilenames
112 # https://github.com/oreillymedia/HTMLBook/blob/master/htmlbook-xsl/chunk.xsl#L33
113 # If not defined, we can just create an example without an 'id' attr and see
116 # For toc levels see http://www.sagehill.net/docbookxsl/TOCcontrol.html
117 # TODO: this list has also a flag that controls wheter we add the
118 # 'Table of Contents' heading in convert_chunk_with_toc()
120 'appendix': ChunkParams('app', 'book'),
121 'book': ChunkParams('bk'),
122 'chapter': ChunkParams('ch', 'book'),
123 'glossary': ChunkParams('go', 'book'),
124 'index': ChunkParams('ix', 'book'),
125 'part': ChunkParams('pt', 'book'),
126 'preface': ChunkParams('pr', 'book'),
127 'refentry': ChunkParams('re', 'book'),
128 'reference': ChunkParams('rn', 'book'),
129 'sect1': ChunkParams('s', 'chapter', 1),
130 'section': ChunkParams('s', 'chapter', 1),
131 'sect2': ChunkParams('s', 'sect1', DONT_CHUNK
),
132 'sect3': ChunkParams('s', 'sect2', DONT_CHUNK
),
133 'sect4': ChunkParams('s', 'sect3', DONT_CHUNK
),
134 'sect5': ChunkParams('s', 'sect4', DONT_CHUNK
),
136 # TAGS we don't support:
137 # 'article', 'bibliography', 'colophon', 'set', 'setindex'
140 '_': (etree
.XPath('./title'), None),
141 'book': (etree
.XPath('./bookinfo/title'), None),
143 etree
.XPath('./refmeta/refentrytitle'),
144 etree
.XPath('./refnamediv/refpurpose')
148 ID_XPATH
= etree
.XPath('//@id')
150 GLOSSENTRY_XPATH
= etree
.XPath('//glossentry')
156 def gen_chunk_name(node
, chunk_params
):
157 """Generate a chunk file name
159 This is either based on the id or on the position in the doc. In the latter
160 case it uses a prefix from CHUNK_PARAMS and a sequence number for each chunk
163 if 'id' in node
.attrib
:
164 return node
.attrib
['id']
166 name
= ('%s%02d' % (chunk_params
.prefix
, chunk_params
.idx
))
167 chunk_params
.idx
+= 1
169 # handle parents to make names of nested tags like in docbook
170 # - we only need to prepend the parent if there are > 1 of them in the
171 # xml. None, the parents we have are not sufficient, e.g. 'index' can
172 # be in 'book' or 'part' or ... Maybe we can track the chunk_parents
173 # when we chunk explicitly and on each level maintain the 'idx'
174 # while chunk_params.parent:
175 # parent = chunk_params.parent
176 # if parent not in CHUNK_PARAMS:
178 # chunk_params = CHUNK_PARAMS[parent]
179 # name = ('%s%02d' % (chunk_params.prefix, chunk_params.idx)) + name
181 logging
.info('Gen chunk name: "%s"', name
)
185 def get_chunk_titles(module
, node
):
187 if tag
not in TITLE_XPATHS
:
189 (title
, subtitle
) = TITLE_XPATHS
['_']
191 (title
, subtitle
) = TITLE_XPATHS
[tag
]
205 result
['title'] = ''.join(convert_title(ctx
, xml
))
206 if xml
.tag
!= 'title':
207 result
['title_tag'] = xml
.tag
209 result
['title_tag'] = tag
215 result
['subtitle'] = ''.join(convert_title(ctx
, xml
))
216 result
['subtitle_tag'] = xml
.tag
220 def chunk(xml_node
, module
, depth
=0, idx
=0, parent
=None):
223 The first time, we're called with parent=None and in that case we return
224 the new_node as the root of the tree. For each tree-node we generate a
225 filename and process the children.
228 chunk_params
= CHUNK_PARAMS
.get(tag
)
230 title_args
= get_chunk_titles(module
, xml_node
)
231 chunk_name
= gen_chunk_name(xml_node
, chunk_params
)
233 # check idx to handle 'sect1'/'section' special casing and title-only
235 if idx
>= chunk_params
.min_idx
:
236 logging
.info('chunk tag: "%s"[%d]', tag
, idx
)
238 # remove the xml-node from the parent
239 sub_tree
= etree
.ElementTree(deepcopy(xml_node
)).getroot()
240 xml_node
.getparent().remove(xml_node
)
243 parent
= Node(tag
, parent
=parent
, xml
=xml_node
, depth
=depth
,
245 filename
=chunk_name
+ '.html', anchor
=None,
248 parent
= Node(tag
, parent
=parent
, xml
=xml_node
, depth
=depth
,
250 filename
=parent
.filename
, anchor
='#' + chunk_name
,
255 for child
in xml_node
:
256 chunk(child
, module
, depth
, idx
, parent
)
257 if child
.tag
in CHUNK_PARAMS
:
263 def add_id_links(files
, links
):
265 chunk_name
= node
.filename
[:-5]
266 chunk_base
= node
.filename
+ '#'
267 for attr
in ID_XPATH(node
.xml
):
268 if attr
== chunk_name
:
269 links
[attr
] = node
.filename
271 links
[attr
] = chunk_base
+ attr
274 def build_glossary(files
):
276 if node
.xml
.tag
!= 'glossary':
278 for term
in GLOSSENTRY_XPATH(node
.xml
):
279 # TODO: there can be all kind of things in a glossary. This only supports
280 # what we commonly use
281 key
= etree
.tostring(term
.find('glossterm'), method
="text", encoding
=str).strip()
282 value
= etree
.tostring(term
.find('glossdef'), method
="text", encoding
=str).strip()
283 glossary
[key
] = value
284 # logging.debug('glosentry: %s:%s', key, value)
290 def convert_inner(ctx
, xml
, result
):
292 result
.extend(convert_tags
.get(child
.tag
, convert__unknown
)(ctx
, child
))
295 def convert_ignore(ctx
, xml
):
297 convert_inner(ctx
, xml
, result
)
301 def convert_skip(ctx
, xml
):
305 def append_text(text
, result
):
306 if text
and text
.strip():
307 result
.append(text
.replace('<', '<').replace('>', '>'))
313 def convert__unknown(ctx
, xml
):
314 # don't recurse on subchunks
315 if xml
.tag
in CHUNK_PARAMS
:
317 if isinstance(xml
, etree
._Comment
):
318 return ['<!-- ' + xml
.text
+ '-->\n']
321 if xml
.tag
not in missing_tags
:
322 logging
.warning('Add tag converter for "%s"', xml
.tag
)
323 missing_tags
[xml
.tag
] = True
324 result
= ['<!-- ' + xml
.tag
+ '-->\n']
325 convert_inner(ctx
, xml
, result
)
326 result
.append('<!-- /' + xml
.tag
+ '-->\n')
330 def convert_sect(ctx
, xml
, h_tag
, inner_func
=convert_inner
):
331 result
= ['<div class="%s">\n' % xml
.tag
]
332 title
= xml
.find('title')
333 if title
is not None:
334 if 'id' in xml
.attrib
:
335 result
.append('<a name="%s"></a>' % xml
.attrib
['id'])
336 result
.append('<%s>%s</%s>' % (h_tag
, title
.text
, h_tag
))
338 append_text(xml
.text
, result
)
339 inner_func(ctx
, xml
, result
)
340 result
.append('</div>')
341 append_text(xml
.tail
, result
)
345 def xml_get_title(ctx
, xml
):
346 title_tag
= xml
.find('title')
347 if title_tag
is not None:
348 return ''.join(convert_title(ctx
, title_tag
))
350 # TODO(ensonic): any way to get the file (inlcudes) too?
351 logging
.warning('%s: Expected title tag under "%s %s"', xml
.sourceline
, xml
.tag
, str(xml
.attrib
))
358 def convert_abstract(ctx
, xml
):
359 result
= ["""<div class="abstract">
360 <p class="title"><b>Abstract</b></p>"""]
361 append_text(xml
.text
, result
)
362 convert_inner(ctx
, xml
, result
)
363 result
.append('</div>')
364 append_text(xml
.tail
, result
)
368 def convert_acronym(ctx
, xml
):
370 title
= glossary
.get(key
, '')
371 # TODO: print a sensible warning if missing
372 result
= ['<acronym title="%s"><span class="acronym">%s</span></acronym>' % (title
, key
)]
374 result
.append(xml
.tail
)
378 def convert_anchor(ctx
, xml
):
379 return ['<a name="%s"></a>' % xml
.attrib
['id']]
382 def convert_bookinfo(ctx
, xml
):
383 result
= ['<div class="titlepage">']
384 convert_inner(ctx
, xml
, result
)
385 result
.append("""<hr>
388 result
.append(xml
.tail
)
392 def convert_blockquote(ctx
, xml
):
393 result
= ['<div class="blockquote">\n<blockquote class="blockquote">']
394 append_text(xml
.text
, result
)
395 convert_inner(ctx
, xml
, result
)
396 result
.append('</blockquote>\n</div>')
397 append_text(xml
.tail
, result
)
401 def convert_code(ctx
, xml
):
402 result
= ['<code class="%s">' % xml
.tag
]
403 append_text(xml
.text
, result
)
404 convert_inner(ctx
, xml
, result
)
405 result
.append('</code>')
406 append_text(xml
.tail
, result
)
410 def convert_colspec(ctx
, xml
):
414 result
.append(' class="%s"' % a
['colname'])
416 result
.append(' width="%s"' % a
['colwidth'])
418 # is in tgroup and there can be no 'text'
422 def convert_command(ctx
, xml
):
423 result
= ['<strong class="userinput"><code>']
424 append_text(xml
.text
, result
)
425 convert_inner(ctx
, xml
, result
)
426 result
.append('</code></strong>')
427 append_text(xml
.tail
, result
)
431 def convert_corpauthor(ctx
, xml
):
432 result
= ['<div><h3 class="corpauthor">\n']
433 append_text(xml
.text
, result
)
434 convert_inner(ctx
, xml
, result
)
435 result
.append('</h3></div>\n')
436 append_text(xml
.tail
, result
)
440 def convert_div(ctx
, xml
):
441 result
= ['<div class="%s">\n' % xml
.tag
]
442 append_text(xml
.text
, result
)
443 convert_inner(ctx
, xml
, result
)
444 result
.append('</div>')
445 append_text(xml
.tail
, result
)
449 def convert_emphasis(ctx
, xml
):
450 result
= ['<span class="emphasis"><em>']
451 append_text(xml
.text
, result
)
452 convert_inner(ctx
, xml
, result
)
453 result
.append('</em></span>')
454 append_text(xml
.tail
, result
)
458 def convert_em_class(ctx
, xml
):
459 result
= ['<em class="%s"><code>' % xml
.tag
]
460 append_text(xml
.text
, result
)
461 convert_inner(ctx
, xml
, result
)
462 result
.append('</code></em>')
463 append_text(xml
.tail
, result
)
467 def convert_entry(ctx
, xml
):
468 entry_type
= ctx
['table.entry']
469 result
= ['<' + entry_type
]
470 if 'role' in xml
.attrib
:
471 result
.append(' class="%s"' % xml
.attrib
['role'])
472 if 'morerows' in xml
.attrib
:
473 result
.append(' rowspan="%s"' % (1 + int(xml
.attrib
['morerows'])))
475 append_text(xml
.text
, result
)
476 convert_inner(ctx
, xml
, result
)
477 result
.append('</' + entry_type
+ '>')
478 append_text(xml
.tail
, result
)
482 def convert_footnote(ctx
, xml
):
483 footnotes
= ctx
.get('footnotes', [])
484 # footnotes idx is not per page, but per doc
489 # need a pair of ids for each footnote (docbook generates different ids)
490 this_id
= 'footnote-%d' % idx
491 that_id
= 'ftn.' + this_id
493 inner
= ['<div id="%s" class="footnote">' % that_id
]
494 inner
.append('<p><a href="#%s" class="para"><sup class="para">[%d] </sup></a>' % (
496 # TODO(ensonic): this can contain all kind of tags, if we convert them we'll
497 # get double nested paras :/.
498 # convert_inner(ctx, xml, inner)
499 para
= xml
.find('para')
501 para
= xml
.find('simpara')
503 inner
.append(para
.text
)
505 logging
.warning('%s: Unhandled footnote content: %s', xml
.sourceline
,
506 etree
.tostring(xml
, method
="text", encoding
=str).strip())
507 inner
.append('</p></div>')
508 footnotes
.append(inner
)
509 ctx
['footnotes'] = footnotes
510 return ['<a href="#%s" class="footnote" name="%s"><sup class="footnote">[%s]</sup></a>' % (
511 that_id
, this_id
, idx
)]
514 def convert_formalpara(ctx
, xml
):
516 title_tag
= xml
.find('title')
517 result
= ['<p><b>%s</b>' % title_tag
.text
]
518 para_tag
= xml
.find('para')
519 append_text(para_tag
.text
, result
)
520 convert_inner(ctx
, para_tag
, result
)
521 append_text(para_tag
.tail
, result
)
522 result
.append('</p>')
523 append_text(xml
.tail
, result
)
527 def convert_glossdef(ctx
, xml
):
528 result
= ['<dd class="glossdef">']
529 convert_inner(ctx
, xml
, result
)
530 result
.append('</dd>\n')
534 def convert_glossdiv(ctx
, xml
):
535 title_tag
= xml
.find('title')
536 title
= title_tag
.text
537 xml
.remove(title_tag
)
539 '<a name="gls%s"></a><h3 class="title">%s</h3>' % (title
, title
)
541 convert_inner(ctx
, xml
, result
)
545 def convert_glossentry(ctx
, xml
):
547 convert_inner(ctx
, xml
, result
)
551 def convert_glossterm(ctx
, xml
):
554 anchor
= xml
.find('anchor')
555 if anchor
is not None:
556 glossid
= anchor
.attrib
.get('id', '')
557 text
+= anchor
.tail
or ''
558 text
+= xml
.text
or ''
560 glossid
= 'glossterm-' + text
562 '<dt><span class="glossterm"><a name="%s"></a>%s</span></dt>' % (
567 def convert_imageobject(ctx
, xml
):
568 imagedata
= xml
.find('imagedata')
569 if imagedata
is not None:
570 # TODO(ensonic): warn on missing fileref attr?
571 return ['<img src="%s">' % imagedata
.attrib
.get('fileref', '')]
576 def convert_indexdiv(ctx
, xml
):
577 title_tag
= xml
.find('title')
578 title
= title_tag
.text
579 xml
.remove(title_tag
)
581 '<a name="idx%s"></a><h3 class="title">%s</h3>' % (title
, title
)
583 convert_inner(ctx
, xml
, result
)
587 def convert_informaltable(ctx
, xml
):
588 result
= ['<div class="informaltable"><table class="informaltable"']
590 if 'pgwide' in a
and a
['pgwide'] == '1':
591 result
.append(' width="100%"')
592 if 'frame' in a
and a
['frame'] == 'none':
593 result
.append(' border="0"')
595 convert_inner(ctx
, xml
, result
)
596 result
.append('</table></div>')
598 result
.append(xml
.tail
)
602 def convert_inlinegraphic(ctx
, xml
):
603 # TODO(ensonic): warn on missing fileref attr?
604 return ['<img src="%s">' % xml
.attrib
.get('fileref', '')]
607 def convert_itemizedlist(ctx
, xml
):
608 result
= ['<div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; ">']
609 convert_inner(ctx
, xml
, result
)
610 result
.append('</ul></div>')
612 result
.append(xml
.tail
)
616 def convert_link(ctx
, xml
):
617 linkend
= xml
.attrib
['linkend']
618 if linkend
in fixxref
.NoLinks
:
623 append_text(xml
.text
, link_text
)
624 convert_inner(ctx
, xml
, link_text
)
625 # TODO: fixxref does some weird checks in xml.text
626 result
= [fixxref
.MakeXRef(ctx
['module'], '', 0, linkend
, ''.join(link_text
))]
628 append_text(xml
.text
, result
)
629 convert_inner(ctx
, xml
, result
)
630 append_text(xml
.tail
, result
)
634 def convert_listitem(ctx
, xml
):
635 result
= ['<li class="listitem">']
636 convert_inner(ctx
, xml
, result
)
637 result
.append('</li>')
638 # is in itemizedlist and there can be no 'text'
642 def convert_literallayout(ctx
, xml
):
643 result
= ['<div class="literallayout"><p><br>\n']
644 append_text(xml
.text
, result
)
645 convert_inner(ctx
, xml
, result
)
646 result
.append('</p></div>')
647 append_text(xml
.tail
, result
)
651 def convert_orderedlist(ctx
, xml
):
652 result
= ['<div class="orderedlist"><ol class="orderedlist" type="1">']
653 convert_inner(ctx
, xml
, result
)
654 result
.append('</ol></div>')
655 append_text(xml
.tail
, result
)
659 def convert_para(ctx
, xml
):
661 if 'id' in xml
.attrib
:
662 result
.append('<a name="%s"></a>' % xml
.attrib
['id'])
663 if 'role' in xml
.attrib
:
664 result
.append('<p class="%s">' % xml
.attrib
['role'])
667 append_text(xml
.text
, result
)
668 convert_inner(ctx
, xml
, result
)
669 result
.append('</p>')
670 append_text(xml
.tail
, result
)
674 def convert_para_like(ctx
, xml
):
676 if 'id' in xml
.attrib
:
677 result
.append('<a name="%s"></a>' % xml
.attrib
['id'])
678 result
.append('<p class="%s">' % xml
.tag
)
679 append_text(xml
.text
, result
)
680 convert_inner(ctx
, xml
, result
)
681 result
.append('</p>')
682 append_text(xml
.tail
, result
)
686 def convert_phrase(ctx
, xml
):
688 if 'role' in xml
.attrib
:
689 result
.append(' class="%s">' % xml
.attrib
['role'])
692 append_text(xml
.text
, result
)
693 convert_inner(ctx
, xml
, result
)
694 result
.append('</span>')
695 append_text(xml
.tail
, result
)
699 def convert_primaryie(ctx
, xml
):
701 convert_inner(ctx
, xml
, result
)
702 result
.append('\n</dt>\n<dd></dd>\n')
706 def convert_pre(ctx
, xml
):
707 result
= ['<pre class="%s">\n' % xml
.tag
]
708 append_text(xml
.text
, result
)
709 convert_inner(ctx
, xml
, result
)
710 result
.append('</pre>')
711 append_text(xml
.tail
, result
)
715 def convert_programlisting(ctx
, xml
):
717 if xml
.attrib
.get('role', '') == 'example':
719 lang
= xml
.attrib
.get('language', 'c').lower()
720 if lang
not in LEXERS
:
721 LEXERS
[lang
] = get_lexer_by_name(lang
)
722 lexer
= LEXERS
.get(lang
, None)
724 highlighted
= highlight(xml
.text
, lexer
, HTML_FORMATTER
)
726 # we do own line-numbering
727 line_count
= highlighted
.count('\n')
728 source_lines
= '\n'.join([str(i
) for i
in range(1, line_count
+ 1)])
729 result
.append("""<table class="listing_frame" border="0" cellpadding="0" cellspacing="0">
732 <td class="listing_lines" align="right"><pre>%s</pre></td>
733 <td class="listing_code"><pre class="programlisting">%s</pre></td>
737 """ % (source_lines
, highlighted
))
739 logging
.warn('No pygments lexer for language="%s"', lang
)
740 result
.append('<pre class="programlisting">')
741 result
.append(xml
.text
)
742 result
.append('</pre>')
744 result
.append('<pre class="programlisting">')
745 append_text(xml
.text
, result
)
746 convert_inner(ctx
, xml
, result
)
747 result
.append('</pre>')
748 append_text(xml
.tail
, result
)
752 def convert_quote(ctx
, xml
):
753 result
= ['<span class="quote">"<span class="quote">']
754 append_text(xml
.text
, result
)
755 convert_inner(ctx
, xml
, result
)
756 result
.append('</span>"</span>')
757 append_text(xml
.tail
, result
)
761 def convert_refsect1(ctx
, xml
):
762 # Add a divider between two consequitive refsect2
763 def convert_inner(ctx
, xml
, result
):
766 if child
.tag
== 'refsect2' and prev
is not None and prev
.tag
== child
.tag
:
767 result
.append('<hr>\n')
768 result
.extend(convert_tags
.get(child
.tag
, convert__unknown
)(ctx
, child
))
770 return convert_sect(ctx
, xml
, 'h2', convert_inner
)
773 def convert_refsect2(ctx
, xml
):
774 return convert_sect(ctx
, xml
, 'h3')
777 def convert_refsect3(ctx
, xml
):
778 return convert_sect(ctx
, xml
, 'h4')
781 def convert_row(ctx
, xml
):
783 convert_inner(ctx
, xml
, result
)
784 result
.append('</tr>\n')
788 def convert_sect1_tag(ctx
, xml
):
789 return convert_sect(ctx
, xml
, 'h2')
792 def convert_sect2(ctx
, xml
):
793 return convert_sect(ctx
, xml
, 'h3')
796 def convert_sect3(ctx
, xml
):
797 return convert_sect(ctx
, xml
, 'h4')
800 def convert_simpara(ctx
, xml
):
802 append_text(xml
.text
, result
)
803 result
.append('</p>')
804 append_text(xml
.tail
, result
)
808 def convert_span(ctx
, xml
):
809 result
= ['<span class="%s">' % xml
.tag
]
810 append_text(xml
.text
, result
)
811 convert_inner(ctx
, xml
, result
)
812 result
.append('</span>')
813 append_text(xml
.tail
, result
)
817 def convert_table(ctx
, xml
):
818 result
= ['<div class="table">']
819 if 'id' in xml
.attrib
:
820 result
.append('<a name="%s"></a>' % xml
.attrib
['id'])
821 title_tag
= xml
.find('title')
822 if title_tag
is not None:
823 result
.append('<p class="title"><b>')
824 # TODO(ensonic): Add a 'Table X. ' prefix, needs a table counter
825 result
.extend(convert_title(ctx
, title_tag
))
826 result
.append('</b></p>')
827 xml
.remove(title_tag
)
828 result
.append('<div class="table-contents"><table class="table" summary="g_object_new" border="1">')
830 convert_inner(ctx
, xml
, result
)
832 result
.append('</table></div></div>')
833 append_text(xml
.tail
, result
)
837 def convert_tbody(ctx
, xml
):
839 ctx
['table.entry'] = 'td'
840 convert_inner(ctx
, xml
, result
)
841 result
.append('</tbody>')
842 # is in tgroup and there can be no 'text'
846 def convert_tgroup(ctx
, xml
):
847 # tgroup does not expand to anything, but the nested colspecs need to
848 # be put into a colgroup
849 cols
= xml
.findall('colspec')
852 result
.append('<colgroup>\n')
854 result
.extend(convert_colspec(ctx
, col
))
856 result
.append('</colgroup>\n')
857 convert_inner(ctx
, xml
, result
)
858 # is in informaltable and there can be no 'text'
862 def convert_thead(ctx
, xml
):
864 ctx
['table.entry'] = 'th'
865 convert_inner(ctx
, xml
, result
)
866 result
.append('</thead>')
867 # is in tgroup and there can be no 'text'
871 def convert_title(ctx
, xml
):
872 # This is always called from some context
874 append_text(xml
.text
, result
)
875 convert_inner(ctx
, xml
, result
)
876 append_text(xml
.tail
, result
)
880 def convert_ulink(ctx
, xml
):
881 result
= ['<a class="%s" href="%s">%s</a>' % (xml
.tag
, xml
.attrib
['url'], xml
.text
)]
883 result
.append(xml
.tail
)
887 def convert_userinput(ctx
, xml
):
888 result
= ['<span class="command"><strong>']
889 append_text(xml
.text
, result
)
890 convert_inner(ctx
, xml
, result
)
891 result
.append('</strong></span>')
892 append_text(xml
.tail
, result
)
896 def convert_variablelist(ctx
, xml
):
897 result
= ["""<div class="variablelist"><table border="0" class="variablelist">
899 <col align="left" valign="top">
903 convert_inner(ctx
, xml
, result
)
904 result
.append("""</tbody>
909 def convert_varlistentry(ctx
, xml
):
912 result
.append('<td><p>')
913 term
= xml
.find('term')
914 result
.extend(convert_span(ctx
, term
))
915 result
.append('</p></td>')
917 result
.append('<td>')
918 listitem
= xml
.find('listitem')
919 convert_inner(ctx
, listitem
, result
)
920 result
.append('</td>')
922 result
.append('<tr>')
926 # TODO(ensonic): turn into class with converters as functions and ctx as self
928 'abstract': convert_abstract
,
929 'acronym': convert_acronym
,
930 'anchor': convert_anchor
,
931 'application': convert_span
,
932 'bookinfo': convert_bookinfo
,
933 'blockquote': convert_blockquote
,
934 'caption': convert_div
,
935 'code': convert_code
,
936 'colspec': convert_colspec
,
937 'constant': convert_code
,
938 'command': convert_command
,
939 'corpauthor': convert_corpauthor
,
940 'emphasis': convert_emphasis
,
941 'entry': convert_entry
,
942 'envar': convert_code
,
943 'footnote': convert_footnote
,
944 'filename': convert_code
,
945 'formalpara': convert_formalpara
,
946 'function': convert_code
,
947 'glossdef': convert_glossdef
,
948 'glossdiv': convert_glossdiv
,
949 'glossentry': convert_glossentry
,
950 'glossterm': convert_glossterm
,
951 'imageobject': convert_imageobject
,
952 'indexdiv': convert_indexdiv
,
953 'indexentry': convert_ignore
,
954 'indexterm': convert_skip
,
955 'informalexample': convert_div
,
956 'informaltable': convert_informaltable
,
957 'inlinegraphic': convert_inlinegraphic
,
958 'inlinemediaobject': convert_span
,
959 'itemizedlist': convert_itemizedlist
,
960 'legalnotice': convert_div
,
961 'link': convert_link
,
962 'listitem': convert_listitem
,
963 'literal': convert_code
,
964 'literallayout': convert_literallayout
,
965 'mediaobject': convert_div
,
967 'option': convert_code
,
968 'orderedlist': convert_orderedlist
,
969 'para': convert_para
,
970 'partintro': convert_div
,
971 'parameter': convert_em_class
,
972 'phrase': convert_phrase
,
973 'primaryie': convert_primaryie
,
974 'programlisting': convert_programlisting
,
975 'quote': convert_quote
,
976 'releaseinfo': convert_para_like
,
977 'refsect1': convert_refsect1
,
978 'refsect2': convert_refsect2
,
979 'refsect3': convert_refsect3
,
980 'replaceable': convert_em_class
,
981 'returnvalue': convert_span
,
983 'screen': convert_pre
,
984 'sect1': convert_sect1_tag
,
985 'sect2': convert_sect2
,
986 'sect3': convert_sect3
,
987 'simpara': convert_simpara
,
988 'structfield': convert_em_class
,
989 'structname': convert_span
,
990 'synopsis': convert_pre
,
991 'symbol': convert_span
,
992 'table': convert_table
,
993 'tbody': convert_tbody
,
994 'term': convert_span
,
995 'tgroup': convert_tgroup
,
996 'thead': convert_thead
,
997 'type': convert_span
,
998 'ulink': convert_ulink
,
999 'userinput': convert_userinput
,
1000 'varname': convert_code
,
1001 'variablelist': convert_variablelist
,
1002 'varlistentry': convert_varlistentry
,
1003 'warning': convert_div
,
1006 # conversion helpers
1008 HTML_HEADER
= """<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
1011 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
1013 %s<link rel="stylesheet" href="style.css" type="text/css">
1015 <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
1019 def generate_head_links(ctx
):
1022 '<link rel="home" href="%s" title="%s">\n' % (n
.filename
, n
.title
)
1026 result
.append('<link rel="up" href="%s" title="%s">\n' % (n
.filename
, n
.title
))
1027 if 'nav_prev' in ctx
:
1029 result
.append('<link rel="prev" href="%s" title="%s">\n' % (n
.filename
, n
.title
))
1030 if 'nav_next' in ctx
:
1032 result
.append('<link rel="next" href="%s" title="%s">\n' % (n
.filename
, n
.title
))
1033 return ''.join(result
)
1036 def generate_nav_links(ctx
):
1039 '<td><a accesskey="h" href="%s"><img src="home.png" width="16" height="16" border="0" alt="Home"></a></td>' % n
.filename
1044 '<td><a accesskey="u" href="%s"><img src="up.png" width="16" height="16" border="0" alt="Up"></a></td>' % n
.filename
)
1046 result
.append('<td><img src="up-insensitive.png" width="16" height="16" border="0"></td>')
1047 if 'nav_prev' in ctx
:
1050 '<td><a accesskey="p" href="%s"><img src="left.png" width="16" height="16" border="0" alt="Prev"></a></td>' % n
.filename
)
1052 result
.append('<td><img src="left-insensitive.png" width="16" height="16" border="0"></td>')
1053 if 'nav_next' in ctx
:
1056 '<td><a accesskey="n" href="%s"><img src="right.png" width="16" height="16" border="0" alt="Next"></a></td>' % n
.filename
)
1058 result
.append('<td><img src="right-insensitive.png" width="16" height="16" border="0"></td>')
1060 return ''.join(result
)
1063 def generate_toc(ctx
, node
):
1065 for c
in node
.children
:
1066 # TODO: urlencode the filename: urllib.parse.quote_plus()
1070 result
.append('<dt><span class="%s"><a href="%s">%s</a></span>\n' % (
1071 c
.title_tag
, link
, c
.title
))
1073 result
.append('<span class="%s"> — %s</span>' % (c
.subtitle_tag
, c
.subtitle
))
1074 result
.append('</dt>\n')
1076 result
.append('<dd><dl>')
1077 result
.extend(generate_toc(ctx
, c
))
1078 result
.append('</dl></dd>')
1082 def generate_basic_nav(ctx
):
1083 return """<table class="navigation" id="top" width="100%%" cellpadding="2" cellspacing="5">
1084 <tr valign="middle">
1085 <td width="100%%" align="left" class="shortcuts"></td>
1089 """ % generate_nav_links(ctx
)
1092 def generate_alpha_nav(ctx
, divs
, prefix
, span_id
):
1095 title
= xml_get_title(ctx
, s
)
1096 ix_nav
.append('<a class="shortcut" href="#%s%s">%s</a>' % (prefix
, title
, title
))
1098 return """<table class="navigation" id="top" width="100%%" cellpadding="2" cellspacing="5">
1099 <tr valign="middle">
1100 <td width="100%%" align="left" class="shortcuts">
1108 """ % (span_id
, '\n<span class="dim">|</span>\n'.join(ix_nav
), generate_nav_links(ctx
))
1111 def generate_refentry_nav(ctx
, refsect1s
, result
):
1112 result
.append("""<table class="navigation" id="top" width="100%" cellpadding="2" cellspacing="5">
1113 <tr valign="middle">
1114 <td width="100%" align="left" class="shortcuts">
1115 <a href="#" class="shortcut">Top</a>""")
1118 # don't list TOC sections (role="xxx_proto")
1119 if s
.attrib
.get('role', '').endswith("_proto"):
1121 # skip section without 'id' attrs
1122 if 'id' not in s
.attrib
:
1125 ref_id
= s
.attrib
['id']
1126 # skip foreign sections
1127 if '.' not in ref_id
:
1130 title
= xml_get_title(ctx
, s
)
1131 span_id
= ref_id
.split('.')[1].replace('-', '_')
1135 Â Â <span class="dim">|</span>Â
1136 <a href="#%s" class="shortcut">%s</a>
1138 """ % (span_id
, ref_id
, title
))
1144 """ % generate_nav_links(ctx
))
1147 def generate_footer(ctx
):
1149 if 'footnotes' in ctx
:
1150 result
.append("""<div class="footnotes">\n
1151 <br><hr style="width:100; text-align:left;margin-left: 0">
1153 for f
in ctx
['footnotes']:
1155 result
.append('</div>\n')
1159 def get_id_path(node
):
1160 """ Generate the 'id'.
1161 We need to walk up the xml-tree and check the positions for each sibling.
1162 When reaching the top of the tree we collect remaining index entries from
1167 parent
= xml
.getparent()
1168 while parent
is not None:
1169 children
= parent
.getchildren()
1170 ix
.insert(0, str(children
.index(xml
) + 1))
1172 parent
= xml
.getparent()
1173 while node
is not None:
1174 ix
.insert(0, str(node
.idx
+ 1))
1182 node_id
= xml
.attrib
.get('id', None)
1186 # TODO: this is moot if nothing links to it, we could also consider to omit
1187 # the <a name="$id"></a> tag.
1188 logging
.info('%d: No "id" attribute on "%s", generating one',
1189 xml
.sourceline
, xml
.tag
)
1190 ix
= get_id_path(node
)
1191 # logging.warning('%s: id indexes: %s', node.filename, str(ix))
1192 return 'id-' + '.'.join(ix
)
1195 def convert_chunk_with_toc(ctx
, div_class
, title_tag
):
1198 HTML_HEADER
% (node
.title
+ ": " + node
.root
.title
, generate_head_links(ctx
)),
1199 generate_basic_nav(ctx
),
1200 '<div class="%s">' % div_class
,
1202 title
= node
.xml
.find('title')
1203 if title
is not None:
1205 <div class="titlepage">
1206 <%s class="title"><a name="%s"></a>%s</%s>
1208 title_tag
, get_id(node
), title
.text
, title_tag
))
1209 node
.xml
.remove(title
)
1211 toc
= generate_toc(ctx
, node
)
1213 # TODO: not all docbook page types use this extra heading
1214 result
.append("""<p><b>Table of Contents</b></p>
1219 result
.append("""</dl>
1222 convert_inner(ctx
, node
.xml
, result
)
1223 result
.extend(generate_footer(ctx
))
1224 result
.append("""</div>
1233 def convert_book(ctx
):
1236 HTML_HEADER
% (node
.title
, generate_head_links(ctx
)),
1237 """<table class="navigation" id="top" width="100%%" cellpadding="2" cellspacing="0">
1238 <tr><th valign="middle"><p class="title">%s</p></th></tr>
1243 bookinfo
= node
.xml
.findall('bookinfo')[0]
1244 # we already used the title
1245 title
= bookinfo
.find('title')
1246 if title
is not None:
1247 bookinfo
.remove(title
)
1248 result
.extend(convert_bookinfo(ctx
, bookinfo
))
1249 result
.append("""<div class="toc">
1252 result
.extend(generate_toc(ctx
, node
.root
))
1253 result
.append("""</dl>
1256 result
.extend(generate_footer(ctx
))
1257 result
.append("""</div>
1263 def convert_chapter(ctx
):
1264 return convert_chunk_with_toc(ctx
, 'chapter', 'h2')
1267 def convert_glossary(ctx
):
1269 glossdivs
= node
.xml
.findall('glossdiv')
1272 HTML_HEADER
% (node
.title
+ ": " + node
.root
.title
, generate_head_links(ctx
)),
1273 generate_alpha_nav(ctx
, glossdivs
, 'gls', 'glossary'),
1274 """<div class="glossary">
1275 <div class="titlepage"><h%1d class="title">
1276 <a name="%s"></a>%s</h%1d>
1277 </div>""" % (node
.depth
, get_id(node
), node
.title
, node
.depth
)
1280 result
.extend(convert_glossdiv(ctx
, i
))
1281 result
.extend(generate_footer(ctx
))
1282 result
.append("""</div>
1288 def convert_index(ctx
):
1290 # Get all indexdivs under indexdiv
1291 indexdivs
= node
.xml
.find('indexdiv').findall('indexdiv')
1294 HTML_HEADER
% (node
.title
+ ": " + node
.root
.title
, generate_head_links(ctx
)),
1295 generate_alpha_nav(ctx
, indexdivs
, 'idx', 'index'),
1296 """<div class="index">
1297 <div class="titlepage"><h%1d class="title">
1298 <a name="%s"></a>%s</h%1d>
1299 </div>""" % (node
.depth
, get_id(node
), node
.title
, node
.depth
)
1302 result
.extend(convert_indexdiv(ctx
, i
))
1303 result
.extend(generate_footer(ctx
))
1304 result
.append("""</div>
1310 def convert_part(ctx
):
1311 return convert_chunk_with_toc(ctx
, 'part', 'h1')
1314 def convert_preface(ctx
):
1317 HTML_HEADER
% (node
.title
+ ": " + node
.root
.title
, generate_head_links(ctx
)),
1318 generate_basic_nav(ctx
),
1319 '<div class="preface">'
1321 title
= node
.xml
.find('title')
1322 if title
is not None:
1324 <div class="titlepage">
1325 <h2 class="title"><a name="%s"></a>%s</h2>
1326 </div>""" % (get_id(node
), title
.text
))
1327 node
.xml
.remove(title
)
1328 convert_inner(ctx
, node
.xml
, result
)
1329 result
.extend(generate_footer(ctx
))
1330 result
.append("""</div>
1336 def convert_reference(ctx
):
1337 return convert_chunk_with_toc(ctx
, 'reference', 'h1')
1340 def convert_refentry(ctx
):
1342 node_id
= get_id(node
)
1343 refsect1s
= node
.xml
.findall('refsect1')
1346 refmeta
= node
.xml
.find('refmeta')
1347 if refmeta
is not None:
1348 refmiscinfo
= refmeta
.find('refmiscinfo')
1349 if refmiscinfo
is not None:
1350 inlinegraphic
= refmiscinfo
.find('inlinegraphic')
1351 if inlinegraphic
is not None:
1352 gallery
= ''.join(convert_inlinegraphic(ctx
, inlinegraphic
))
1355 HTML_HEADER
% (node
.title
+ ": " + node
.root
.title
, generate_head_links(ctx
))
1357 generate_refentry_nav(ctx
, refsect1s
, result
)
1359 <div class="refentry">
1361 <div class="refnamediv">
1362 <table width="100%%"><tr>
1364 <h2><span class="refentrytitle"><a name="%s.top_of_page"></a>%s</span></h2>
1367 <td class="gallery_image" valign="top" align="right">%s</td>
1370 """ % (node_id
, node_id
, node
.title
, node
.title
, node
.subtitle
, gallery
))
1373 result
.extend(convert_refsect1(ctx
, s
))
1374 result
.extend(generate_footer(ctx
))
1375 result
.append("""</div>
1381 def convert_sect1(ctx
):
1382 return convert_chunk_with_toc(ctx
, 'sect1', 'h2')
1385 # TODO(ensonic): turn into class with converters as functions and ctx as self
1387 'book': convert_book
,
1388 'chapter': convert_chapter
,
1389 'glossary': convert_glossary
,
1390 'index': convert_index
,
1391 'part': convert_part
,
1392 'preface': convert_preface
,
1393 'reference': convert_reference
,
1394 'refentry': convert_refentry
,
1395 'sect1': convert_sect1
,
1399 def generate_nav_nodes(files
, node
):
1401 'nav_home': node
.root
,
1403 # nav params: up, prev, next
1405 nav
['nav_up'] = node
.parent
1406 ix
= files
.index(node
)
1408 nav
['nav_prev'] = files
[ix
- 1]
1409 if ix
< len(files
) - 1:
1410 nav
['nav_next'] = files
[ix
+ 1]
1414 def convert(out_dir
, module
, files
, node
):
1415 """Convert the docbook chunks to a html file.
1418 out_dir: already created output dir
1419 files: list of nodes in the tree in pre-order
1420 node: current tree node
1423 logging
.info('Writing: %s', node
.filename
)
1424 with
open(os
.path
.join(out_dir
, node
.filename
), 'wt',
1425 newline
='\n', encoding
='utf-8') as html
:
1431 ctx
.update(generate_nav_nodes(files
, node
))
1433 if node
.name
in convert_chunks
:
1434 for line
in convert_chunks
[node
.name
](ctx
):
1437 logging
.warning('Add converter/template for "%s"', node
.name
)
1440 def create_devhelp2_toc(node
):
1442 for c
in node
.children
:
1444 result
.append('<sub name="%s" link="%s">\n' % (c
.title
, c
.filename
))
1445 result
.extend(create_devhelp2_toc(c
))
1446 result
.append('</sub>\n')
1448 result
.append('<sub name="%s" link="%s"/>\n' % (c
.title
, c
.filename
))
1452 def create_devhelp2_condition_attribs(node
):
1453 if 'condition' in node
.attrib
:
1454 # condition -> since, deprecated, ... (separated with '|')
1455 cond
= node
.attrib
['condition'].replace('"', '"').split('|')
1459 keywords
.append('{}="{}"'.format(*c
.split(':', 1)))
1461 # deprecated can have no description
1462 keywords
.append('{}="{}"'.format(c
, ''))
1463 return ' ' + ' '.join(keywords
)
1468 def create_devhelp2_refsect2_keyword(node
, base_link
):
1469 return' <keyword type="%s" name="%s" link="%s"%s/>\n' % (
1470 node
.attrib
['role'], xml_get_title({}, node
), base_link
+ node
.attrib
['id'],
1471 create_devhelp2_condition_attribs(node
))
1474 def create_devhelp2_refsect3_keyword(node
, base_link
, title
, name
):
1475 return' <keyword type="%s" name="%s" link="%s"%s/>\n' % (
1476 node
.attrib
['role'], title
, base_link
+ name
,
1477 create_devhelp2_condition_attribs(node
))
1480 def create_devhelp2(out_dir
, module
, xml
, files
):
1481 with
open(os
.path
.join(out_dir
, module
+ '.devhelp2'), 'wt',
1482 newline
='\n', encoding
='utf-8') as idx
:
1483 bookinfo_nodes
= xml
.xpath('/book/bookinfo')
1485 if bookinfo_nodes
is not None:
1486 bookinfo
= bookinfo_nodes
[0]
1487 title
= bookinfo
.xpath('./title/text()')[0]
1488 online_url
= bookinfo
.xpath('./releaseinfo/ulink[@role="online-location"]/@url')[0]
1489 # TODO: support author too (see devhelp2.xsl)
1490 # TODO: fixxref uses '--src-lang' to set the language
1492 """<?xml version="1.0" encoding="utf-8" standalone="no"?>
1493 <book xmlns="http://www.devhelp.net/book" title="%s" link="index.html" author="" name="%s" version="2" language="c" online="%s">
1495 """ % (title
, module
, online_url
)
1498 result
.extend(create_devhelp2_toc(files
[0].root
))
1499 result
.append(""" </chapters>
1502 # keywords from all refsect2 and refsect3
1503 refsect2
= etree
.XPath('//refsect2[@role]')
1504 refsect3_enum
= etree
.XPath('refsect3[@role="enum_members"]/informaltable/tgroup/tbody/row[@role="constant"]')
1505 refsect3_enum_details
= etree
.XPath('entry[@role="enum_member_name"]/para')
1506 refsect3_struct
= etree
.XPath('refsect3[@role="struct_members"]/informaltable/tgroup/tbody/row[@role="member"]')
1507 refsect3_struct_details
= etree
.XPath('entry[@role="struct_member_name"]/para/structfield')
1509 base_link
= node
.filename
+ '#'
1510 refsect2_nodes
= refsect2(node
.xml
)
1511 for refsect2_node
in refsect2_nodes
:
1512 result
.append(create_devhelp2_refsect2_keyword(refsect2_node
, base_link
))
1513 refsect3_nodes
= refsect3_enum(refsect2_node
)
1514 for refsect3_node
in refsect3_nodes
:
1515 details_node
= refsect3_enum_details(refsect3_node
)[0]
1516 name
= details_node
.attrib
['id']
1517 result
.append(create_devhelp2_refsect3_keyword(refsect3_node
, base_link
, details_node
.text
, name
))
1518 refsect3_nodes
= refsect3_struct(refsect2_node
)
1519 for refsect3_node
in refsect3_nodes
:
1520 details_node
= refsect3_struct_details(refsect3_node
)[0]
1521 name
= details_node
.attrib
['id']
1522 result
.append(create_devhelp2_refsect3_keyword(refsect3_node
, base_link
, name
, name
))
1524 result
.append(""" </functions>
1531 def get_dirs(uninstalled
):
1533 # this does not work from buiddir!=srcdir
1534 gtkdocdir
= os
.path
.split(sys
.argv
[0])[0]
1535 if not os
.path
.exists(gtkdocdir
+ '/gtk-doc.xsl'):
1536 # try 'srcdir' (set from makefiles) too
1537 if os
.path
.exists(os
.environ
.get("ABS_TOP_SRCDIR", '') + '/gtk-doc.xsl'):
1538 gtkdocdir
= os
.environ
['ABS_TOP_SRCDIR']
1539 styledir
= gtkdocdir
+ '/style'
1541 gtkdocdir
= os
.path
.join(config
.datadir
, 'gtk-doc/data')
1542 styledir
= gtkdocdir
1543 return (gtkdocdir
, styledir
)
1546 def main(module
, index_file
, out_dir
, uninstalled
):
1547 tree
= etree
.parse(index_file
)
1550 (gtkdocdir
, styledir
) = get_dirs(uninstalled
)
1551 # copy navigation images and stylesheets to html directory ...
1552 css_file
= os
.path
.join(styledir
, 'style.css')
1553 for f
in glob(os
.path
.join(styledir
, '*.png')) + [css_file
]:
1554 shutil
.copy(f
, out_dir
)
1555 css_file
= os
.path
.join(out_dir
, 'style.css')
1556 with
open(css_file
, 'at', newline
='\n', encoding
='utf-8') as css
:
1557 css
.write(HTML_FORMATTER
.get_style_defs())
1559 # TODO: migrate options from fixxref
1560 # TODO: do in parallel with loading the xml above.
1561 # TODO: ideally explicity specify the files we need, this will save us the
1562 # globbing and we'll load less files.
1563 fixxref
.LoadIndicies(out_dir
, '/usr/share/gtk-doc/html', [])
1565 # We do multiple passes:
1566 # 1) recursively walk the tree and chunk it into a python tree so that we
1567 # can generate navigation and link tags.
1568 files
= chunk(tree
.getroot(), module
)
1569 files
= [f
for f
in PreOrderIter(files
) if f
.anchor
is None]
1571 # 2) extract tables:
1572 # TODO: can be done in parallel
1573 # - find all 'id' attribs and add them to the link map
1574 add_id_links(files
, fixxref
.Links
)
1575 # - build glossary dict
1576 build_glossary(files
)
1578 # 3) create a xxx.devhelp2 file, do this before 4), since we modify the tree
1579 create_devhelp2(out_dir
, module
, tree
.getroot(), files
)
1581 # 4) iterate the tree and output files
1582 # TODO: can be done in parallel, figure out why this is not faster
1583 # from multiprocessing.pool import Pool
1584 # with Pool(4) as p:
1585 # p.apply_async(convert, args=(out_dir, module, files))
1586 # from multiprocessing.pool import ThreadPool
1587 # with ThreadPool(4) as p:
1588 # p.apply_async(convert, args=(out_dir, module, files))
1590 convert(out_dir
, module
, files
, node
)
1594 logging
.info('options: %s', str(options
.__dict
__))
1595 module
= options
.args
[0]
1596 document
= options
.args
[1]
1598 # TODO: rename to 'html' later on
1599 # - right now in mkhtml, the dir is created by the Makefile and mkhtml
1600 # outputs into the working directory
1601 out_dir
= os
.path
.join(os
.path
.dirname(document
), 'db2html')
1604 except OSError as e
:
1605 if e
.errno
!= errno
.EEXIST
:
1608 sys
.exit(main(module
, document
, out_dir
, options
.uninstalled
))