import string, re import sys from plasTeX.Renderers import Renderer from plasTeX.Base.TeX import Primitives # import generated OpenCV function names # if the file function_names.py does not exist, it # can be generated using the script find_function_names.sh try: from function_names import opencv_function_names except: opencv_function_names = [] pass class XmlRenderer(Renderer): def default(self, node): """ Rendering method for all non-text nodes """ s = [] # Handle characters like \&, \$, \%, etc. if len(node.nodeName) == 1 and node.nodeName not in string.letters: return self.textDefault(node.nodeName) # Start tag s.append('<%s>' % node.nodeName) # See if we have any attributes to render if node.hasAttributes(): s.append('') for key, value in node.attributes.items(): # If the key is 'self', don't render it # these nodes are the same as the child nodes if key == 'self': continue s.append('<%s>%s' % (key, unicode(value), key)) s.append('') # Invoke rendering on child nodes s.append(unicode(node)) # End tag s.append('' % node.nodeName) return u'\n'.join(s) def textDefault(self, node): """ Rendering method for all text nodes """ return node.replace('&','&').replace('<','<').replace('>','>') from plasTeX.Renderers import Renderer as BaseRenderer class reStructuredTextRenderer(BaseRenderer): aliases = { 'superscript': 'active::^', 'subscript': 'active::_', 'dollar': '$', 'percent': '%', 'opencurly': '{', 'closecurly': '}', 'underscore': '_', 'ampersand': '&', 'hashmark': '#', 'space': ' ', 'tilde': 'active::~', 'at': '@', 'backslash': '\\', } def __init__(self, *args, **kwargs): BaseRenderer.__init__(self, *args, **kwargs) # Load dictionary with methods for key in vars(type(self)): if key.startswith('do__'): self[self.aliases[key[4:]]] = getattr(self, key) elif key.startswith('do_'): self[key[3:]] = getattr(self, key) self.indent = 0 self.in_func = False self.in_cvarg = False self.descriptions = 0 self.after_parameters = False self.func_short_desc = '' def do_document(self, node): return unicode(node) def do_par(self, node): if self.indent == -1: pre = "" post = "" else: pre = "\n" + (" " * self.indent) post = "\n" return pre + unicode(node).lstrip(" ") + post def do_chapter(self, node): t = str(node.attributes['title']) section_files = [] for section in node.subsections: try: filename = section.filenameoverride if filename is not None: section_files.append(filename) except: pass toc = ".. toctree::\n :maxdepth: 2\n\n" for file in section_files: if file[-4:] != '.rst': print >>sys.stderr, "WARNING: unexpected file extension:", file else: toc += " %s\n" % file[:-4] toc += "\n\n" return "\n\n%s\n%s\n%s\n\n" % ('*' * len(t), t, '*' * len(t)) + toc + unicode(node) def do_section(self, node): t = str(node.attributes['title']) return "\n\n%s\n%s\n\n" % (t, '=' * len(t)) + unicode(node) def do_subsection(self, node): t = str(node.attributes['title']) return "\n\n%s\n%s\n\n" % (t, '-' * len(t)) + unicode(node) def do_cvdefX(self, node, lang): if self.language != lang: return u"" self.indent = -1 self.in_func = False decl = unicode(node.attributes['a']).rstrip(' ;') # remove trailing ';' decl_list = decl.split(";") r = u"" for d in decl_list: r += u"\n\n.. %s:: %s\n\n" % ({'c' : 'cfunction', 'cpp' : 'cfunction', 'py' : 'function'}[self.language], d.strip()) self.indent = 4 if self.func_short_desc != '': r += self.ind() + self.func_short_desc + '\n\n' self.func_short_desc = '' return r def do_cvdefC(self, node): return self.do_cvdefX(node, 'c') def do_cvcode(self, node): #body = unicode(node.source).replace(u"\n",u"").replace(u"\\newline", u"\n"); #body = body.replace(u"\\par", u"\n").replace(u"\\cvcode{", "").replace(u"\\", u"")[:-1]; body = unicode(node.source).replace(u"\\newline", u"\n").replace("_ ", "_"); body = body.replace(u"\\par", u"\n").replace(u"\\cvcode{", "").replace(u"\n\n",u"\n"); body = body.replace(u",\n", ",\n ").replace(u"\\", u"")[:-1]; lines = body.split(u"\n") self.indent += 4 body = "\n".join([u"%s %s" % (self.ind(), s) for s in lines]) r = (u"\n\n%s::\n\n" % self.ind()) + unicode(body) + u"\n\n" self.indent -= 4 return r def do_cvdefCpp(self, node): lang = 'cpp' if self.language != lang: return u"" self.indent = -1 self.in_func = False decl = unicode(node.source).replace(u"\n",u"").replace(u"\\newline", u"").replace(u"_ ", u"_"); decl = decl.replace(u"\\par", u"").replace(u"\\cvdefCpp{", "").replace(u"\\", u"").rstrip(u" ;}"); decl_list = decl.split(";") r = u"" for d in decl_list: r += u"\n\n.. %s:: %s\n\n" % ({'c' : 'cfunction', 'cpp' : 'cfunction', 'py' : 'function'}[self.language], d.strip()) self.indent = 4 if self.func_short_desc != '': r += self.ind() + self.func_short_desc + '\n\n' self.func_short_desc = '' return r def do_cvdefPy(self, node): return self.do_cvdefX(node, 'py') def do_description(self, node): self.descriptions += 1 desc = unicode(node) self.descriptions -= 1 if self.descriptions == 0: self.after_parameters = True return u"\n\n" + desc + u"\n\n" def do_includegraphics(self, node): filename = '../' + str(node.attributes['file']).strip() if not os.path.isfile(filename): print >>sys.stderr, "WARNING: missing image file", filename return u"" return u"\n\n%s.. image:: %s\n\n" % (self.ind(), filename) def do_xfunc(self, node, a='title'): t = self.get_func_prefix() + unicode(node.attributes[a]).strip() print "====>", t label = u"\n\n.. index:: %s\n\n.. _%s:\n\n" % (t, t) self.in_func = True self.descriptions = 0 self.after_parameters = False self.indent = 0 #return u"" + unicode(node) # Would like to look ahead to reorder things, but cannot see more than 2 ahead if 0: print "NODES:", node.source n = node.nextSibling while (n != None) and (n.nodeName != 'cvfunc'): print " ", n.nodeName, len(n.childNodes) n = n.nextSibling print "-----" return label + u"\n\n%s\n%s\n\n" % (t, '-' * len(t)) + unicode(node) def do_cvfunc(self, node): return self.do_xfunc(node) def do_cvclass(self, node): return self.do_xfunc(node) def get_func_prefix(self): return u"" if self.language == 'c': return u"cv" if self.language == 'cpp': return u"cv\\:\\:" if self.language == 'py': return u"cv\\." return u"" def do_cvFunc(self, node): return self.do_xfunc(node, ['title','alt'][self.language == 'cpp']) def do_cvCPyFunc(self, node): return self.do_xfunc(node) def do_cvCppFunc(self, node): return self.do_xfunc(node) def do_cvstruct(self, node): t = str(node.attributes['title']).strip() self.after_parameters = False self.indent = 4 return u".. ctype:: %s" % t + unicode(node) def do_cvmacro(self, node): t = str(node.attributes['title']).strip() self.after_parameters = False self.indent = 4 return u".. cmacro:: %s" % t + unicode(node) def showTree(self, node, i = 0): n = node while n != None: print "%s[%s]" % (" " * i, n.nodeName) if len(n.childNodes) != 0: self.showTree(n.childNodes[0], i + 4) n = n.nextSibling def do_Huge(self, node): return unicode(node) def do_tabular(self, node): if 0: self.showTree(node) rows = [] for row in node.childNodes: cols = [] for col in row.childNodes: cols.append(unicode(col).strip()) rows.append(cols) maxes = [ 0 ] * len(rows[0]) for r in rows: maxes = [ max(m,len(c)) for m,c in zip(maxes, r) ] sep = "+" + "+".join([ ('-' * (m + 4)) for m in maxes]) + "+" s = "" s += sep + "\n" for r in rows: #s += "|" + "|".join([ ' ' + c.ljust(m + 3) for c,m in zip(r, maxes) ]) + "|" + "\n" #s += sep + "\n" s += self.ind() + "|" + "|".join([ ' ' + c.ljust(m + 3) for c,m in zip(r, maxes) ]) + "|" + "\n" s += self.ind() + sep + "\n" return unicode(s) def do_verbatim(self, node): return u"\n\n::\n\n " + unicode(node.source.replace('\n', '\n ')) + "\n\n" def do_index(self, node): return u"" # No idea why this does not work... JCB return u"\n\n.. index:: (%s)\n\n" % node.attributes['entry'] def do_label(self, node): return u"" def fixup_funcname(self, str): """ add parentheses to a function name if not already present """ str = str.strip() if str[-1] != ')': return str + '()' return str def gen_reference(self, name): """ try to guess whether *name* is a function, struct or macro and if yes, generate the appropriate reference markup """ name = name.strip() if name[0:2] == 'cv': return u":cfunc:`%s`" % self.fixup_funcname(name) elif 'cv'+name in opencv_function_names: if self.language in ['c', 'cpp']: return u":cfunc:`cv%s`" % self.fixup_funcname(name) else: return u":func:`%s`" % self.fixup_funcname(name) elif name[0:2] == 'Cv' or name[0:3] == 'Ipl': return u":ctype:`%s`" % name elif name[0:2] == 'CV': return u":cmacro:`%s`" % name return None def do_xcross(self, refname): # try to guess whether t is a function, struct or macro # and if yes, generate the appropriate reference markup #rst_ref = self.gen_reference(refname) #if rst_ref is not None: # return rst_ref return u":ref:`%s`" % refname def do_cross(self, node): return self.do_xcross(str(node.attributes['name']).strip()) def do_cvCross(self, node): prefix = self.get_func_prefix() if self.language == 'cpp': t = prefix + str(node.attributes['altname']).strip() return u":ref:`%s`" % t else: t = prefix + str(node.attributes['name']).strip() return self.do_xcross(t) def do_cvCPyCross(self, node): t = self.get_func_prefix() + str(node.attributes['name']).strip() return self.do_xcross(t) def do_cvCppCross(self, node): t = self.get_func_prefix() + str(node.attributes['name']).strip() return u":ref:`%s`" % t def ind(self): return u" " * self.indent def do_cvarg(self, node): self.indent += 4 # Nested descriptions occur e.g. when a flag parameter can # be one of several constants. We want to render the inner # description differently than the outer parameter descriptions. if self.in_cvarg or self.after_parameters: defstr = unicode(node.attributes['def']) assert not (u"\xe2" in unicode(defstr)) self.indent -= 4 param_str = u"\n%s * **%s** - %s\n" return param_str % (self.ind(), str(node.attributes['item']).strip(), self.fix_quotes(defstr).strip(" ")) # save that we are in a paramater description self.in_cvarg = True defstr = unicode(node.attributes['def']) assert not (u"\xe2" in unicode(defstr)) self.in_cvarg = False self.indent -= 4 param_str = u"\n%s:param %s: %s" return param_str % (self.ind(), str(node.attributes['item']).strip(), self.fix_quotes(defstr).strip()) #lines = defstr.split('\n') #return u"\n%s%s\n%s\n" % (self.ind(), str(node.attributes['item']).strip(), "\n".join([self.ind()+" "+l for l in lines])) def do_bgroup(self, node): return u"bgroup(%s)" % node.source def do_url(self, node): return unicode(node.attributes['loc']) def do_enumerate(self, node): return unicode(node) def do_itemize(self, node): return unicode(node) def do_item(self, node): #if node.attributes['term'] != None: if node.attributes.get('term',None): self.indent += 4 defstr = unicode(node).strip() assert not (u"\xe2" in unicode(defstr)) self.indent -= 4 return u"\n%s* %s *\n%s %s\n" % (self.ind(), unicode(node.attributes['term']).strip(), self.ind(), defstr) else: return u"\n\n%s* %s" % (self.ind(), unicode(node).strip()) def do_textit(self, node): return "*%s*" % unicode(node.attributes['self']) def do_texttt(self, node): t = unicode(node) # try to guess whether t is a function, struct or macro # and if yes, generate the appropriate reference markup rst_ref = self.gen_reference(t) if rst_ref is not None: return rst_ref return u"``%s``" % t def do__underscore(self, node): return u"_" def default(self, node): print "DEFAULT dropping", node.nodeName return unicode(node) def do_lstlisting(self, node): self.in_func = False lines = node.source.split('\n') self.indent += 2 body = "\n".join([u"%s %s" % (self.ind(), s) for s in lines[1:-1]]) r = (u"\n\n%s::\n\n" % self.ind()) + unicode(body) + u"\n\n" if self.func_short_desc != '': r = self.ind() + self.func_short_desc + '\n\n' + r self.func_short_desc = '' self.indent -= 2 return r def do_math(self, node): return u":math:`%s`" % node.source def do_displaymath(self, node): words = self.fix_quotes(node.source).strip().split() return u"\n\n%s.. math::\n\n%s %s\n\n" % (self.ind(), self.ind(), " ".join(words[1:-1])) def do_maketitle(self, node): return u"" def do_setcounter(self, node): return u"" def do_tableofcontents(self, node): return u"" def do_titleformat(self, node): return u"" def do_subsubsection(self, node): return u"" def do_include(self, node): return u"" def fix_quotes(self, s): s = s.replace(u'\u2013', "'") s = s.replace(u'\u2019', "'") s = s.replace(u'\u2264', "#<2264>") s = s.replace(u'\xd7', "#") return s def do_cvC(self, node): if self.language == 'c': return unicode(node.attributes['a']) return unicode("") def do_cvCpp(self, node): if self.language == 'cpp': return unicode(node.attributes['a']) return unicode("") def do_cvPy(self, node): if self.language == 'py': return unicode(node.attributes['a']) return unicode("") def do_cvCPy(self, node): if self.language == 'c' or self.language == 'py': return unicode(node.attributes['a']) return unicode("") def do_ifthenelse(self, node): # print "IFTHENELSE: [%s],[%s],[%s]" % (node.attributes['test'], str(node.attributes['then']), node.attributes['else']) print "CONDITION", unicode(node.attributes['test']).strip() == u'true' if unicode(node.attributes['test']).strip() == u'true': print "TRUE: [%s]" % str(node.attributes['then']) return unicode(node.attributes['then']) else: return unicode(node.attributes['else']) def do_equal(self, node): first = unicode(node.attributes['first']).strip() second = unicode(node.attributes['second']).strip() if first == second: return u'true' else: return u'false' def textDefault(self, node): if self.in_func: self.func_short_desc += self.fix_quotes(unicode(node)).strip(" ") return u"" s = unicode(node) s = self.fix_quotes(s) return s return node.replace('\\_','_') from plasTeX.TeX import TeX import os import pickle def preprocess_conditionals(fname, suffix, conditionals): print 'conditionals', conditionals f = open("../" + fname + ".tex", 'r') fout = open(fname + suffix + ".tex", 'w') print 'write', fname + suffix + ".tex" ifstack=[True] for l in f.readlines(): ll = l.lstrip() if ll.startswith("\\if"): ifstack.append(conditionals.get(ll.rstrip()[3:], False)) elif ll.startswith("\\else"): ifstack[-1] = not ifstack[-1] elif ll.startswith("\\fi"): ifstack.pop() elif not False in ifstack: fout.write(l) f.close() fout.close() def parse_documentation_source(language): # Instantiate a TeX processor and parse the input text tex = TeX() tex.ownerDocument.config['files']['split-level'] = 0 master_f = open("../online-opencv.tex", "rt") out_master_f = open(("../online-opencv-%s.tex" % language), "wt") flist = [] for l in master_f.readlines(): outl = l if l.startswith("\\newcommand{\\targetlang}{}"): outl = l.replace("}", ("%s}" % language)) elif l.startswith("\\input{"): flist.append(re.findall(r"\{(.+)\}", l)[0]) outl = l.replace("}", ("-%s}" % language)) out_master_f.write(outl) master_f.close() out_master_f.close() index_f = open("index.rst.copy", "rt") index_lines = list(index_f.readlines()) index_f.close() out_index_f = open("index.rst", "wt") header_line = "OpenCV |version| %s Reference" % {"py": "Python", "c": "C", "cpp": "C++"}[language] index_lines = [header_line + "\n", "="*len(header_line) + "\n", "\n"] + index_lines for l in index_lines: out_index_f.write(l) out_index_f.close() for f in flist: preprocess_conditionals(f, '-' + language, {'C':language=='c', 'Python':language=='py', 'Py':language=='py', 'CPy':(language=='py' or language == 'c'), 'Cpp':language=='cpp', 'plastex':True}) if 1: tex.input("\\input{online-opencv-%s.tex}" % language) else: src0 = r''' \documentclass{book} \usepackage{myopencv} \begin{document}''' src1 = r''' \end{document} ''' lines = list(open("../CvReference.tex")) LINES = 80 tex.input(src0 + "".join(lines[:LINES]) + src1) return tex.parse() language = sys.argv[1] document = parse_documentation_source(language) rest = reStructuredTextRenderer() rest.language = language rest.render(document)