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' adds a 'title' attr to 'a' if the target has a title.
47 - there is an implementation in convert_link() but it is slow
48 - we might need to collect titles as we chunk
49 - if we do this we'd need to keep iterating, but might be able to replace
51 - handle the 'xref' tag
52 - this needs the title + the type of the target
53 - for the title, see convert_link()
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
]
206 result
['title'] = ''.join(convert_title(ctx
, xml
))
207 if xml
.tag
!= 'title':
208 result
['title_tag'] = xml
.tag
210 result
['title_tag'] = tag
216 result
['subtitle'] = ''.join(convert_title(ctx
, xml
))
217 result
['subtitle_tag'] = xml
.tag
221 def chunk(xml_node
, module
, depth
=0, idx
=0, parent
=None):
224 The first time, we're called with parent=None and in that case we return
225 the new_node as the root of the tree. For each tree-node we generate a
226 filename and process the children.
229 chunk_params
= CHUNK_PARAMS
.get(tag
)
231 title_args
= get_chunk_titles(module
, xml_node
)
232 chunk_name
= gen_chunk_name(xml_node
, chunk_params
)
234 # check idx to handle 'sect1'/'section' special casing and title-only
236 if idx
>= chunk_params
.min_idx
:
237 logging
.info('chunk tag: "%s"[%d]', tag
, idx
)
239 # remove the xml-node from the parent
240 sub_tree
= etree
.ElementTree(deepcopy(xml_node
)).getroot()
241 xml_node
.getparent().remove(xml_node
)
244 parent
= Node(tag
, parent
=parent
, xml
=xml_node
, depth
=depth
,
246 filename
=chunk_name
+ '.html', anchor
=None,
249 parent
= Node(tag
, parent
=parent
, xml
=xml_node
, depth
=depth
,
251 filename
=parent
.filename
, anchor
='#' + chunk_name
,
256 for child
in xml_node
:
257 chunk(child
, module
, depth
, idx
, parent
)
258 if child
.tag
in CHUNK_PARAMS
:
264 def add_id_links(files
, links
):
266 chunk_name
= node
.filename
[:-5]
267 chunk_base
= node
.filename
+ '#'
268 for attr
in ID_XPATH(node
.xml
):
269 if attr
== chunk_name
:
270 links
[attr
] = node
.filename
272 links
[attr
] = chunk_base
+ attr
275 def build_glossary(files
):
277 if node
.xml
.tag
!= 'glossary':
279 for term
in GLOSSENTRY_XPATH(node
.xml
):
280 # TODO: there can be all kind of things in a glossary. This only supports
281 # what we commonly use
282 key
= etree
.tostring(term
.find('glossterm'), method
="text", encoding
=str).strip()
283 value
= etree
.tostring(term
.find('glossdef'), method
="text", encoding
=str).strip()
284 glossary
[key
] = value
285 # logging.debug('glosentry: %s:%s', key, value)
291 def convert_inner(ctx
, xml
, result
):
293 result
.extend(convert_tags
.get(child
.tag
, convert__unknown
)(ctx
, child
))
296 def convert_ignore(ctx
, xml
):
298 convert_inner(ctx
, xml
, result
)
302 def convert_skip(ctx
, xml
):
306 def append_text(text
, result
):
307 if text
and text
.strip():
308 result
.append(text
.replace('<', '<').replace('>', '>'))
314 def convert__unknown(ctx
, xml
):
315 # don't recurse on subchunks
316 if xml
.tag
in CHUNK_PARAMS
:
318 if isinstance(xml
, etree
._Comment
):
319 return ['<!-- ' + xml
.text
+ '-->\n']
322 if xml
.tag
not in missing_tags
:
323 logging
.warning('Add tag converter for "%s"', xml
.tag
)
324 missing_tags
[xml
.tag
] = True
325 result
= ['<!-- ' + xml
.tag
+ '-->\n']
326 convert_inner(ctx
, xml
, result
)
327 result
.append('<!-- /' + xml
.tag
+ '-->\n')
331 def convert_sect(ctx
, xml
, h_tag
, inner_func
=convert_inner
):
332 result
= ['<div class="%s">\n' % xml
.tag
]
333 title
= xml
.find('title')
334 if title
is not None:
335 if 'id' in xml
.attrib
:
336 result
.append('<a name="%s"></a>' % xml
.attrib
['id'])
337 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 text
= ''.join(link_text
)
627 (tid
, href
) = fixxref
.GetXRef(linkend
)
629 module
= ctx
['module']
631 # search for a title under id='tid' in all chunks
632 # NOTE: this will only work for local links
633 # TODO: this works but is super slow
634 # id_xpath = etree.XPath('//*[@id="%s"]' % tid)
635 # for c in ctx['files']:
636 # nodes = id_xpath(c.xml)
638 # title = get_chunk_titles(module, nodes[0])['title']
640 # title_attr = ' title="%s"' % title
641 # logging.debug('Have title node: href=%s%s', tid, title_attr)
644 href
= fixxref
.MakeRelativeXRef(module
, href
)
645 result
= ['<a href="%s"%s>%s</a>' % (href
, title_attr
, text
)]
647 # TODO: filename is for the output and xml.sourceline is on the masterdoc ...
648 fixxref
.ReportBadXRef(ctx
['node'].filename
, 0, linkend
, text
)
651 append_text(xml
.text
, result
)
652 convert_inner(ctx
, xml
, result
)
653 append_text(xml
.tail
, result
)
657 def convert_listitem(ctx
, xml
):
658 result
= ['<li class="listitem">']
659 convert_inner(ctx
, xml
, result
)
660 result
.append('</li>')
661 # is in itemizedlist and there can be no 'text'
665 def convert_literallayout(ctx
, xml
):
666 result
= ['<div class="literallayout"><p><br>\n']
667 append_text(xml
.text
, result
)
668 convert_inner(ctx
, xml
, result
)
669 result
.append('</p></div>')
670 append_text(xml
.tail
, result
)
674 def convert_orderedlist(ctx
, xml
):
675 result
= ['<div class="orderedlist"><ol class="orderedlist" type="1">']
676 convert_inner(ctx
, xml
, result
)
677 result
.append('</ol></div>')
678 append_text(xml
.tail
, result
)
682 def convert_para(ctx
, xml
):
684 if 'id' in xml
.attrib
:
685 result
.append('<a name="%s"></a>' % xml
.attrib
['id'])
686 if 'role' in xml
.attrib
:
687 result
.append('<p class="%s">' % xml
.attrib
['role'])
690 append_text(xml
.text
, result
)
691 convert_inner(ctx
, xml
, result
)
692 result
.append('</p>')
693 append_text(xml
.tail
, result
)
697 def convert_para_like(ctx
, xml
):
699 if 'id' in xml
.attrib
:
700 result
.append('<a name="%s"></a>' % xml
.attrib
['id'])
701 result
.append('<p class="%s">' % xml
.tag
)
702 append_text(xml
.text
, result
)
703 convert_inner(ctx
, xml
, result
)
704 result
.append('</p>')
705 append_text(xml
.tail
, result
)
709 def convert_phrase(ctx
, xml
):
711 if 'role' in xml
.attrib
:
712 result
.append(' class="%s">' % xml
.attrib
['role'])
715 append_text(xml
.text
, result
)
716 convert_inner(ctx
, xml
, result
)
717 result
.append('</span>')
718 append_text(xml
.tail
, result
)
722 def convert_primaryie(ctx
, xml
):
724 convert_inner(ctx
, xml
, result
)
725 result
.append('\n</dt>\n<dd></dd>\n')
729 def convert_pre(ctx
, xml
):
730 result
= ['<pre class="%s">\n' % xml
.tag
]
731 append_text(xml
.text
, result
)
732 convert_inner(ctx
, xml
, result
)
733 result
.append('</pre>')
734 append_text(xml
.tail
, result
)
738 def convert_programlisting(ctx
, xml
):
740 if xml
.attrib
.get('role', '') == 'example':
742 lang
= xml
.attrib
.get('language', 'c').lower()
743 if lang
not in LEXERS
:
744 LEXERS
[lang
] = get_lexer_by_name(lang
)
745 lexer
= LEXERS
.get(lang
, None)
747 highlighted
= highlight(xml
.text
, lexer
, HTML_FORMATTER
)
749 # we do own line-numbering
750 line_count
= highlighted
.count('\n')
751 source_lines
= '\n'.join([str(i
) for i
in range(1, line_count
+ 1)])
752 result
.append("""<table class="listing_frame" border="0" cellpadding="0" cellspacing="0">
755 <td class="listing_lines" align="right"><pre>%s</pre></td>
756 <td class="listing_code"><pre class="programlisting">%s</pre></td>
760 """ % (source_lines
, highlighted
))
762 logging
.warn('No pygments lexer for language="%s"', lang
)
763 result
.append('<pre class="programlisting">')
764 result
.append(xml
.text
)
765 result
.append('</pre>')
767 result
.append('<pre class="programlisting">')
768 append_text(xml
.text
, result
)
769 convert_inner(ctx
, xml
, result
)
770 result
.append('</pre>')
771 append_text(xml
.tail
, result
)
775 def convert_quote(ctx
, xml
):
776 result
= ['<span class="quote">"<span class="quote">']
777 append_text(xml
.text
, result
)
778 convert_inner(ctx
, xml
, result
)
779 result
.append('</span>"</span>')
780 append_text(xml
.tail
, result
)
784 def convert_refsect1(ctx
, xml
):
785 # Add a divider between two consequitive refsect2
786 def convert_inner(ctx
, xml
, result
):
789 if child
.tag
== 'refsect2' and prev
is not None and prev
.tag
== child
.tag
:
790 result
.append('<hr>\n')
791 result
.extend(convert_tags
.get(child
.tag
, convert__unknown
)(ctx
, child
))
793 return convert_sect(ctx
, xml
, 'h2', convert_inner
)
796 def convert_refsect2(ctx
, xml
):
797 return convert_sect(ctx
, xml
, 'h3')
800 def convert_refsect3(ctx
, xml
):
801 return convert_sect(ctx
, xml
, 'h4')
804 def convert_row(ctx
, xml
):
806 convert_inner(ctx
, xml
, result
)
807 result
.append('</tr>\n')
811 def convert_sect1_tag(ctx
, xml
):
812 return convert_sect(ctx
, xml
, 'h2')
815 def convert_sect2(ctx
, xml
):
816 return convert_sect(ctx
, xml
, 'h3')
819 def convert_sect3(ctx
, xml
):
820 return convert_sect(ctx
, xml
, 'h4')
823 def convert_simpara(ctx
, xml
):
825 append_text(xml
.text
, result
)
826 result
.append('</p>')
827 append_text(xml
.tail
, result
)
831 def convert_span(ctx
, xml
):
832 result
= ['<span class="%s">' % xml
.tag
]
833 append_text(xml
.text
, result
)
834 convert_inner(ctx
, xml
, result
)
835 result
.append('</span>')
836 append_text(xml
.tail
, result
)
840 def convert_table(ctx
, xml
):
841 result
= ['<div class="table">']
842 if 'id' in xml
.attrib
:
843 result
.append('<a name="%s"></a>' % xml
.attrib
['id'])
844 title_tag
= xml
.find('title')
845 if title_tag
is not None:
846 result
.append('<p class="title"><b>')
847 # TODO(ensonic): Add a 'Table X. ' prefix, needs a table counter
848 result
.extend(convert_title(ctx
, title_tag
))
849 result
.append('</b></p>')
850 xml
.remove(title_tag
)
851 result
.append('<div class="table-contents"><table class="table" summary="g_object_new" border="1">')
853 convert_inner(ctx
, xml
, result
)
855 result
.append('</table></div></div>')
856 append_text(xml
.tail
, result
)
860 def convert_tbody(ctx
, xml
):
862 ctx
['table.entry'] = 'td'
863 convert_inner(ctx
, xml
, result
)
864 result
.append('</tbody>')
865 # is in tgroup and there can be no 'text'
869 def convert_tgroup(ctx
, xml
):
870 # tgroup does not expand to anything, but the nested colspecs need to
871 # be put into a colgroup
872 cols
= xml
.findall('colspec')
875 result
.append('<colgroup>\n')
877 result
.extend(convert_colspec(ctx
, col
))
879 result
.append('</colgroup>\n')
880 convert_inner(ctx
, xml
, result
)
881 # is in informaltable and there can be no 'text'
885 def convert_thead(ctx
, xml
):
887 ctx
['table.entry'] = 'th'
888 convert_inner(ctx
, xml
, result
)
889 result
.append('</thead>')
890 # is in tgroup and there can be no 'text'
894 def convert_title(ctx
, xml
):
895 # This is always called from some context
897 append_text(xml
.text
, result
)
898 convert_inner(ctx
, xml
, result
)
899 append_text(xml
.tail
, result
)
903 def convert_ulink(ctx
, xml
):
904 result
= ['<a class="%s" href="%s">%s</a>' % (xml
.tag
, xml
.attrib
['url'], xml
.text
)]
906 result
.append(xml
.tail
)
910 def convert_userinput(ctx
, xml
):
911 result
= ['<span class="command"><strong>']
912 append_text(xml
.text
, result
)
913 convert_inner(ctx
, xml
, result
)
914 result
.append('</strong></span>')
915 append_text(xml
.tail
, result
)
919 def convert_variablelist(ctx
, xml
):
920 result
= ["""<div class="variablelist"><table border="0" class="variablelist">
922 <col align="left" valign="top">
926 convert_inner(ctx
, xml
, result
)
927 result
.append("""</tbody>
932 def convert_varlistentry(ctx
, xml
):
935 result
.append('<td><p>')
936 term
= xml
.find('term')
937 result
.extend(convert_span(ctx
, term
))
938 result
.append('</p></td>')
940 result
.append('<td>')
941 listitem
= xml
.find('listitem')
942 convert_inner(ctx
, listitem
, result
)
943 result
.append('</td>')
945 result
.append('<tr>')
949 # TODO(ensonic): turn into class with converters as functions and ctx as self
951 'abstract': convert_abstract
,
952 'acronym': convert_acronym
,
953 'anchor': convert_anchor
,
954 'application': convert_span
,
955 'bookinfo': convert_bookinfo
,
956 'blockquote': convert_blockquote
,
957 'caption': convert_div
,
958 'code': convert_code
,
959 'colspec': convert_colspec
,
960 'constant': convert_code
,
961 'command': convert_command
,
962 'corpauthor': convert_corpauthor
,
963 'emphasis': convert_emphasis
,
964 'entry': convert_entry
,
965 'envar': convert_code
,
966 'footnote': convert_footnote
,
967 'filename': convert_code
,
968 'formalpara': convert_formalpara
,
969 'function': convert_code
,
970 'glossdef': convert_glossdef
,
971 'glossdiv': convert_glossdiv
,
972 'glossentry': convert_glossentry
,
973 'glossterm': convert_glossterm
,
974 'imageobject': convert_imageobject
,
975 'indexdiv': convert_indexdiv
,
976 'indexentry': convert_ignore
,
977 'indexterm': convert_skip
,
978 'informalexample': convert_div
,
979 'informaltable': convert_informaltable
,
980 'inlinegraphic': convert_inlinegraphic
,
981 'inlinemediaobject': convert_span
,
982 'itemizedlist': convert_itemizedlist
,
983 'legalnotice': convert_div
,
984 'link': convert_link
,
985 'listitem': convert_listitem
,
986 'literal': convert_code
,
987 'literallayout': convert_literallayout
,
988 'mediaobject': convert_div
,
990 'option': convert_code
,
991 'orderedlist': convert_orderedlist
,
992 'para': convert_para
,
993 'partintro': convert_div
,
994 'parameter': convert_em_class
,
995 'phrase': convert_phrase
,
996 'primaryie': convert_primaryie
,
997 'programlisting': convert_programlisting
,
998 'quote': convert_quote
,
999 'releaseinfo': convert_para_like
,
1000 'refsect1': convert_refsect1
,
1001 'refsect2': convert_refsect2
,
1002 'refsect3': convert_refsect3
,
1003 'replaceable': convert_em_class
,
1004 'returnvalue': convert_span
,
1006 'screen': convert_pre
,
1007 'sect1': convert_sect1_tag
,
1008 'sect2': convert_sect2
,
1009 'sect3': convert_sect3
,
1010 'simpara': convert_simpara
,
1011 'structfield': convert_em_class
,
1012 'structname': convert_span
,
1013 'synopsis': convert_pre
,
1014 'symbol': convert_span
,
1015 'table': convert_table
,
1016 'tbody': convert_tbody
,
1017 'term': convert_span
,
1018 'tgroup': convert_tgroup
,
1019 'thead': convert_thead
,
1020 'title': convert_skip
,
1021 'type': convert_span
,
1022 'ulink': convert_ulink
,
1023 'userinput': convert_userinput
,
1024 'varname': convert_code
,
1025 'variablelist': convert_variablelist
,
1026 'varlistentry': convert_varlistentry
,
1027 'warning': convert_div
,
1030 # conversion helpers
1032 HTML_HEADER
= """<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
1035 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
1037 %s<link rel="stylesheet" href="style.css" type="text/css">
1039 <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
1043 def generate_head_links(ctx
):
1046 '<link rel="home" href="%s" title="%s">\n' % (n
.filename
, n
.title
)
1050 result
.append('<link rel="up" href="%s" title="%s">\n' % (n
.filename
, n
.title
))
1051 if 'nav_prev' in ctx
:
1053 result
.append('<link rel="prev" href="%s" title="%s">\n' % (n
.filename
, n
.title
))
1054 if 'nav_next' in ctx
:
1056 result
.append('<link rel="next" href="%s" title="%s">\n' % (n
.filename
, n
.title
))
1057 return ''.join(result
)
1060 def generate_nav_links(ctx
):
1063 '<td><a accesskey="h" href="%s"><img src="home.png" width="16" height="16" border="0" alt="Home"></a></td>' % n
.filename
1068 '<td><a accesskey="u" href="%s"><img src="up.png" width="16" height="16" border="0" alt="Up"></a></td>' % n
.filename
)
1070 result
.append('<td><img src="up-insensitive.png" width="16" height="16" border="0"></td>')
1071 if 'nav_prev' in ctx
:
1074 '<td><a accesskey="p" href="%s"><img src="left.png" width="16" height="16" border="0" alt="Prev"></a></td>' % n
.filename
)
1076 result
.append('<td><img src="left-insensitive.png" width="16" height="16" border="0"></td>')
1077 if 'nav_next' in ctx
:
1080 '<td><a accesskey="n" href="%s"><img src="right.png" width="16" height="16" border="0" alt="Next"></a></td>' % n
.filename
)
1082 result
.append('<td><img src="right-insensitive.png" width="16" height="16" border="0"></td>')
1084 return ''.join(result
)
1087 def generate_toc(ctx
, node
):
1089 for c
in node
.children
:
1090 # TODO: urlencode the filename: urllib.parse.quote_plus()
1094 result
.append('<dt><span class="%s"><a href="%s">%s</a></span>\n' % (
1095 c
.title_tag
, link
, c
.title
))
1097 result
.append('<span class="%s"> — %s</span>' % (c
.subtitle_tag
, c
.subtitle
))
1098 result
.append('</dt>\n')
1100 result
.append('<dd><dl>')
1101 result
.extend(generate_toc(ctx
, c
))
1102 result
.append('</dl></dd>')
1106 def generate_basic_nav(ctx
):
1107 return """<table class="navigation" id="top" width="100%%" cellpadding="2" cellspacing="5">
1108 <tr valign="middle">
1109 <td width="100%%" align="left" class="shortcuts"></td>
1113 """ % generate_nav_links(ctx
)
1116 def generate_alpha_nav(ctx
, divs
, prefix
, span_id
):
1119 title
= xml_get_title(ctx
, s
)
1120 ix_nav
.append('<a class="shortcut" href="#%s%s">%s</a>' % (prefix
, title
, title
))
1122 return """<table class="navigation" id="top" width="100%%" cellpadding="2" cellspacing="5">
1123 <tr valign="middle">
1124 <td width="100%%" align="left" class="shortcuts">
1132 """ % (span_id
, '\n<span class="dim">|</span>\n'.join(ix_nav
), generate_nav_links(ctx
))
1135 def generate_refentry_nav(ctx
, refsect1s
, result
):
1136 result
.append("""<table class="navigation" id="top" width="100%" cellpadding="2" cellspacing="5">
1137 <tr valign="middle">
1138 <td width="100%" align="left" class="shortcuts">
1139 <a href="#" class="shortcut">Top</a>""")
1142 # don't list TOC sections (role="xxx_proto")
1143 if s
.attrib
.get('role', '').endswith("_proto"):
1145 # skip section without 'id' attrs
1146 if 'id' not in s
.attrib
:
1149 ref_id
= s
.attrib
['id']
1150 # skip foreign sections
1151 if '.' not in ref_id
:
1154 title
= xml_get_title(ctx
, s
)
1155 span_id
= ref_id
.split('.')[1].replace('-', '_')
1159 Â Â <span class="dim">|</span>Â
1160 <a href="#%s" class="shortcut">%s</a>
1162 """ % (span_id
, ref_id
, title
))
1168 """ % generate_nav_links(ctx
))
1171 def generate_footer(ctx
):
1173 if 'footnotes' in ctx
:
1174 result
.append("""<div class="footnotes">\n
1175 <br><hr style="width:100; text-align:left;margin-left: 0">
1177 for f
in ctx
['footnotes']:
1179 result
.append('</div>\n')
1183 def get_id_path(node
):
1184 """ Generate the 'id'.
1185 We need to walk up the xml-tree and check the positions for each sibling.
1186 When reaching the top of the tree we collect remaining index entries from
1191 parent
= xml
.getparent()
1192 while parent
is not None:
1193 children
= parent
.getchildren()
1194 ix
.insert(0, str(children
.index(xml
) + 1))
1196 parent
= xml
.getparent()
1197 while node
is not None:
1198 ix
.insert(0, str(node
.idx
+ 1))
1206 node_id
= xml
.attrib
.get('id', None)
1210 # TODO: this is moot if nothing links to it, we could also consider to omit
1211 # the <a name="$id"></a> tag.
1212 logging
.info('%d: No "id" attribute on "%s", generating one',
1213 xml
.sourceline
, xml
.tag
)
1214 ix
= get_id_path(node
)
1215 # logging.warning('%s: id indexes: %s', node.filename, str(ix))
1216 return 'id-' + '.'.join(ix
)
1219 def convert_chunk_with_toc(ctx
, div_class
, title_tag
):
1222 HTML_HEADER
% (node
.title
+ ": " + node
.root
.title
, generate_head_links(ctx
)),
1223 generate_basic_nav(ctx
),
1224 '<div class="%s">' % div_class
,
1226 title
= node
.xml
.find('title')
1227 if title
is not None:
1229 <div class="titlepage">
1230 <%s class="title"><a name="%s"></a>%s</%s>
1232 title_tag
, get_id(node
), title
.text
, title_tag
))
1234 toc
= generate_toc(ctx
, node
)
1236 # TODO: not all docbook page types use this extra heading
1237 result
.append("""<p><b>Table of Contents</b></p>
1242 result
.append("""</dl>
1245 convert_inner(ctx
, node
.xml
, result
)
1246 result
.extend(generate_footer(ctx
))
1247 result
.append("""</div>
1256 def convert_book(ctx
):
1259 HTML_HEADER
% (node
.title
, generate_head_links(ctx
)),
1260 """<table class="navigation" id="top" width="100%%" cellpadding="2" cellspacing="0">
1261 <tr><th valign="middle"><p class="title">%s</p></th></tr>
1266 bookinfo
= node
.xml
.findall('bookinfo')[0]
1267 result
.extend(convert_bookinfo(ctx
, bookinfo
))
1268 result
.append("""<div class="toc">
1271 result
.extend(generate_toc(ctx
, node
.root
))
1272 result
.append("""</dl>
1275 result
.extend(generate_footer(ctx
))
1276 result
.append("""</div>
1282 def convert_chapter(ctx
):
1283 return convert_chunk_with_toc(ctx
, 'chapter', 'h2')
1286 def convert_glossary(ctx
):
1288 glossdivs
= node
.xml
.findall('glossdiv')
1291 HTML_HEADER
% (node
.title
+ ": " + node
.root
.title
, generate_head_links(ctx
)),
1292 generate_alpha_nav(ctx
, glossdivs
, 'gls', 'glossary'),
1293 """<div class="glossary">
1294 <div class="titlepage"><h%1d class="title">
1295 <a name="%s"></a>%s</h%1d>
1296 </div>""" % (node
.depth
, get_id(node
), node
.title
, node
.depth
)
1299 result
.extend(convert_glossdiv(ctx
, i
))
1300 result
.extend(generate_footer(ctx
))
1301 result
.append("""</div>
1307 def convert_index(ctx
):
1309 # Get all indexdivs under indexdiv
1310 indexdivs
= node
.xml
.find('indexdiv').findall('indexdiv')
1313 HTML_HEADER
% (node
.title
+ ": " + node
.root
.title
, generate_head_links(ctx
)),
1314 generate_alpha_nav(ctx
, indexdivs
, 'idx', 'index'),
1315 """<div class="index">
1316 <div class="titlepage"><h%1d class="title">
1317 <a name="%s"></a>%s</h%1d>
1318 </div>""" % (node
.depth
, get_id(node
), node
.title
, node
.depth
)
1321 result
.extend(convert_indexdiv(ctx
, i
))
1322 result
.extend(generate_footer(ctx
))
1323 result
.append("""</div>
1329 def convert_part(ctx
):
1330 return convert_chunk_with_toc(ctx
, 'part', 'h1')
1333 def convert_preface(ctx
):
1336 HTML_HEADER
% (node
.title
+ ": " + node
.root
.title
, generate_head_links(ctx
)),
1337 generate_basic_nav(ctx
),
1338 '<div class="preface">'
1340 title
= node
.xml
.find('title')
1341 if title
is not None:
1343 <div class="titlepage">
1344 <h2 class="title"><a name="%s"></a>%s</h2>
1345 </div>""" % (get_id(node
), title
.text
))
1346 convert_inner(ctx
, node
.xml
, result
)
1347 result
.extend(generate_footer(ctx
))
1348 result
.append("""</div>
1354 def convert_reference(ctx
):
1355 return convert_chunk_with_toc(ctx
, 'reference', 'h1')
1358 def convert_refentry(ctx
):
1360 node_id
= get_id(node
)
1361 refsect1s
= node
.xml
.findall('refsect1')
1364 refmeta
= node
.xml
.find('refmeta')
1365 if refmeta
is not None:
1366 refmiscinfo
= refmeta
.find('refmiscinfo')
1367 if refmiscinfo
is not None:
1368 inlinegraphic
= refmiscinfo
.find('inlinegraphic')
1369 if inlinegraphic
is not None:
1370 gallery
= ''.join(convert_inlinegraphic(ctx
, inlinegraphic
))
1373 HTML_HEADER
% (node
.title
+ ": " + node
.root
.title
, generate_head_links(ctx
))
1375 generate_refentry_nav(ctx
, refsect1s
, result
)
1377 <div class="refentry">
1379 <div class="refnamediv">
1380 <table width="100%%"><tr>
1382 <h2><span class="refentrytitle"><a name="%s.top_of_page"></a>%s</span></h2>
1385 <td class="gallery_image" valign="top" align="right">%s</td>
1388 """ % (node_id
, node_id
, node
.title
, node
.title
, node
.subtitle
, gallery
))
1391 result
.extend(convert_refsect1(ctx
, s
))
1392 result
.extend(generate_footer(ctx
))
1393 result
.append("""</div>
1399 def convert_sect1(ctx
):
1400 return convert_chunk_with_toc(ctx
, 'sect1', 'h2')
1403 # TODO(ensonic): turn into class with converters as functions and ctx as self
1405 'book': convert_book
,
1406 'chapter': convert_chapter
,
1407 'glossary': convert_glossary
,
1408 'index': convert_index
,
1409 'part': convert_part
,
1410 'preface': convert_preface
,
1411 'reference': convert_reference
,
1412 'refentry': convert_refentry
,
1413 'sect1': convert_sect1
,
1417 def generate_nav_nodes(files
, node
):
1419 'nav_home': node
.root
,
1421 # nav params: up, prev, next
1423 nav
['nav_up'] = node
.parent
1424 ix
= files
.index(node
)
1426 nav
['nav_prev'] = files
[ix
- 1]
1427 if ix
< len(files
) - 1:
1428 nav
['nav_next'] = files
[ix
+ 1]
1432 def convert(out_dir
, module
, files
, node
):
1433 """Convert the docbook chunks to a html file.
1436 out_dir: already created output dir
1437 files: list of nodes in the tree in pre-order
1438 node: current tree node
1441 logging
.info('Writing: %s', node
.filename
)
1442 with
open(os
.path
.join(out_dir
, node
.filename
), 'wt',
1443 newline
='\n', encoding
='utf-8') as html
:
1449 ctx
.update(generate_nav_nodes(files
, node
))
1451 if node
.name
in convert_chunks
:
1452 for line
in convert_chunks
[node
.name
](ctx
):
1455 logging
.warning('Add converter/template for "%s"', node
.name
)
1458 def create_devhelp2_toc(node
):
1460 for c
in node
.children
:
1462 result
.append('<sub name="%s" link="%s">\n' % (c
.title
, c
.filename
))
1463 result
.extend(create_devhelp2_toc(c
))
1464 result
.append('</sub>\n')
1466 result
.append('<sub name="%s" link="%s"/>\n' % (c
.title
, c
.filename
))
1470 def create_devhelp2_condition_attribs(node
):
1471 if 'condition' in node
.attrib
:
1472 # condition -> since, deprecated, ... (separated with '|')
1473 cond
= node
.attrib
['condition'].replace('"', '"').split('|')
1477 keywords
.append('{}="{}"'.format(*c
.split(':', 1)))
1479 # deprecated can have no description
1480 keywords
.append('{}="{}"'.format(c
, ''))
1481 return ' ' + ' '.join(keywords
)
1486 def create_devhelp2_refsect2_keyword(node
, base_link
):
1487 return' <keyword type="%s" name="%s" link="%s"%s/>\n' % (
1488 node
.attrib
['role'], xml_get_title({}, node
), base_link
+ node
.attrib
['id'],
1489 create_devhelp2_condition_attribs(node
))
1492 def create_devhelp2_refsect3_keyword(node
, base_link
, title
, name
):
1493 return' <keyword type="%s" name="%s" link="%s"%s/>\n' % (
1494 node
.attrib
['role'], title
, base_link
+ name
,
1495 create_devhelp2_condition_attribs(node
))
1498 def create_devhelp2(out_dir
, module
, xml
, files
):
1499 with
open(os
.path
.join(out_dir
, module
+ '.devhelp2'), 'wt',
1500 newline
='\n', encoding
='utf-8') as idx
:
1501 bookinfo_nodes
= xml
.xpath('/book/bookinfo')
1503 if bookinfo_nodes
is not None:
1504 bookinfo
= bookinfo_nodes
[0]
1505 title
= bookinfo
.xpath('./title/text()')[0]
1506 online_url
= bookinfo
.xpath('./releaseinfo/ulink[@role="online-location"]/@url')[0]
1507 # TODO: support author too (see devhelp2.xsl)
1508 # TODO: fixxref uses '--src-lang' to set the language
1510 """<?xml version="1.0" encoding="utf-8" standalone="no"?>
1511 <book xmlns="http://www.devhelp.net/book" title="%s" link="index.html" author="" name="%s" version="2" language="c" online="%s">
1513 """ % (title
, module
, online_url
)
1516 result
.extend(create_devhelp2_toc(files
[0].root
))
1517 result
.append(""" </chapters>
1520 # keywords from all refsect2 and refsect3
1521 refsect2
= etree
.XPath('//refsect2[@role]')
1522 refsect3_enum
= etree
.XPath('refsect3[@role="enum_members"]/informaltable/tgroup/tbody/row[@role="constant"]')
1523 refsect3_enum_details
= etree
.XPath('entry[@role="enum_member_name"]/para')
1524 refsect3_struct
= etree
.XPath('refsect3[@role="struct_members"]/informaltable/tgroup/tbody/row[@role="member"]')
1525 refsect3_struct_details
= etree
.XPath('entry[@role="struct_member_name"]/para/structfield')
1527 base_link
= node
.filename
+ '#'
1528 refsect2_nodes
= refsect2(node
.xml
)
1529 for refsect2_node
in refsect2_nodes
:
1530 result
.append(create_devhelp2_refsect2_keyword(refsect2_node
, base_link
))
1531 refsect3_nodes
= refsect3_enum(refsect2_node
)
1532 for refsect3_node
in refsect3_nodes
:
1533 details_node
= refsect3_enum_details(refsect3_node
)[0]
1534 name
= details_node
.attrib
['id']
1535 result
.append(create_devhelp2_refsect3_keyword(refsect3_node
, base_link
, details_node
.text
, name
))
1536 refsect3_nodes
= refsect3_struct(refsect2_node
)
1537 for refsect3_node
in refsect3_nodes
:
1538 details_node
= refsect3_struct_details(refsect3_node
)[0]
1539 name
= details_node
.attrib
['id']
1540 result
.append(create_devhelp2_refsect3_keyword(refsect3_node
, base_link
, name
, name
))
1542 result
.append(""" </functions>
1549 def get_dirs(uninstalled
):
1551 # this does not work from buiddir!=srcdir
1552 gtkdocdir
= os
.path
.split(sys
.argv
[0])[0]
1553 if not os
.path
.exists(gtkdocdir
+ '/gtk-doc.xsl'):
1554 # try 'srcdir' (set from makefiles) too
1555 if os
.path
.exists(os
.environ
.get("ABS_TOP_SRCDIR", '') + '/gtk-doc.xsl'):
1556 gtkdocdir
= os
.environ
['ABS_TOP_SRCDIR']
1557 styledir
= gtkdocdir
+ '/style'
1559 gtkdocdir
= os
.path
.join(config
.datadir
, 'gtk-doc/data')
1560 styledir
= gtkdocdir
1561 return (gtkdocdir
, styledir
)
1564 def main(module
, index_file
, out_dir
, uninstalled
):
1565 tree
= etree
.parse(index_file
)
1568 (gtkdocdir
, styledir
) = get_dirs(uninstalled
)
1569 # copy navigation images and stylesheets to html directory ...
1570 css_file
= os
.path
.join(styledir
, 'style.css')
1571 for f
in glob(os
.path
.join(styledir
, '*.png')) + [css_file
]:
1572 shutil
.copy(f
, out_dir
)
1573 css_file
= os
.path
.join(out_dir
, 'style.css')
1574 with
open(css_file
, 'at', newline
='\n', encoding
='utf-8') as css
:
1575 css
.write(HTML_FORMATTER
.get_style_defs())
1577 # TODO: migrate options from fixxref
1578 # TODO: do in parallel with loading the xml above.
1579 # TODO: ideally explicity specify the files we need, this will save us the
1580 # globbing and we'll load less files.
1581 fixxref
.LoadIndicies(out_dir
, '/usr/share/gtk-doc/html', [])
1583 # We do multiple passes:
1584 # 1) recursively walk the tree and chunk it into a python tree so that we
1585 # can generate navigation and link tags.
1586 files
= chunk(tree
.getroot(), module
)
1587 files
= [f
for f
in PreOrderIter(files
) if f
.anchor
is None]
1589 # 2) extract tables:
1590 # TODO: can be done in parallel
1591 # - find all 'id' attribs and add them to the link map
1592 add_id_links(files
, fixxref
.Links
)
1593 # - build glossary dict
1594 build_glossary(files
)
1596 # 3) create a xxx.devhelp2 file (could be done in parallel with 4
1597 create_devhelp2(out_dir
, module
, tree
.getroot(), files
)
1599 # 4) iterate the tree and output files
1600 # TODO: can be done in parallel, figure out why this is not faster
1601 # from multiprocessing.pool import Pool
1602 # with Pool(4) as p:
1603 # p.apply_async(convert, args=(out_dir, module, files))
1604 # from multiprocessing.pool import ThreadPool
1605 # with ThreadPool(4) as p:
1606 # p.apply_async(convert, args=(out_dir, module, files))
1608 convert(out_dir
, module
, files
, node
)
1612 logging
.info('options: %s', str(options
.__dict
__))
1613 module
= options
.args
[0]
1614 document
= options
.args
[1]
1616 # TODO: rename to 'html' later on
1617 # - right now in mkhtml, the dir is created by the Makefile and mkhtml
1618 # outputs into the working directory
1619 out_dir
= os
.path
.join(os
.path
.dirname(document
), 'db2html')
1622 except OSError as e
:
1623 if e
.errno
!= errno
.EEXIST
:
1626 sys
.exit(main(module
, document
, out_dir
, options
.uninstalled
))