|
|
@@ -0,0 +1,277 @@
|
|
|
+########################################################################
|
|
|
+##
|
|
|
+## Win32 Usage: makepanda\makechm.bat
|
|
|
+## Linux Usage: makepanda/makechm.py
|
|
|
+##
|
|
|
+## To use this script, you will need to have hhc.exe on your system.
|
|
|
+## For verbose output, run with -v or --verbose option.
|
|
|
+## To keep the temporary .hhc, .hhk, .hhp, .chw files use -k or --keep.
|
|
|
+##
|
|
|
+## You can also import this file as a python module. You will then have
|
|
|
+## access to three functions: makeCHM, makeManualCHM, makeReferenceCHM.
|
|
|
+## This is how you call them:
|
|
|
+## makeCHM(outputfile, dirname, title)
|
|
|
+## where outputfile is the filename where the .chm file will be written,
|
|
|
+## and dirname is the directory containing the html files to include.
|
|
|
+## Title will be the title of the CHM file.
|
|
|
+## The functions makeManualCHM and makeReferenceCHM work exactly the
|
|
|
+## same, except that they work with a structure resembling that of the
|
|
|
+## Panda3D manual and reference, respectively.
|
|
|
+## Note: outputfile should not contain spaces.
|
|
|
+##
|
|
|
+########################################################################
|
|
|
+
|
|
|
+__all__ = ["makeCHM", "makeManualCHM", "makeReferenceCHM"]
|
|
|
+import os, re
|
|
|
+from sys import exit
|
|
|
+import xml.dom.minidom
|
|
|
+from xml.dom.minidom import Node
|
|
|
+
|
|
|
+VERBOSE = False
|
|
|
+KEEPTEMP = False
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ from sys import argv
|
|
|
+ VERBOSE = ("-v" in argv) or ("-vk" in argv) or ("-kv" in argv) or ("--verbose" in argv)
|
|
|
+ KEEPTEMP = ("-k" in argv) or ("-kv" in argv) or ("-vk" in argv) or ("--keep" in argv)
|
|
|
+
|
|
|
+OPTIONBLOCK = """
|
|
|
+Binary TOC=Yes
|
|
|
+Compatibility=1.1 or later
|
|
|
+Compiled file=%s
|
|
|
+Contents file=%s.hhc
|
|
|
+Default Font=Arial,10,0
|
|
|
+Default topic=%s
|
|
|
+Display compile progress=VERBOSE
|
|
|
+Full-text search=Yes
|
|
|
+Index file=%s.hhk
|
|
|
+Language=0x409 English (United States)
|
|
|
+Title=%s""".replace("VERBOSE", VERBOSE and "Yes" or "No")
|
|
|
+
|
|
|
+HTMLBLOCK = """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
|
|
+<html>
|
|
|
+ <head>
|
|
|
+ <meta name="generator" content="Panda3D - makechm.py">
|
|
|
+ </head>
|
|
|
+ <body>
|
|
|
+ <object type="text/site properties">
|
|
|
+ <param name="Window Styles" value="0x800025">
|
|
|
+ <param name="ImageType" value="Folder">
|
|
|
+ <param name="Font" value="Arial,8,0">
|
|
|
+ </object>
|
|
|
+ <ul>\n"""
|
|
|
+
|
|
|
+REFERENCEITEMS = [
|
|
|
+ ("index.html", "Main Page"),
|
|
|
+ ("methods.html", "Methods"),
|
|
|
+ ("functions.html", "Global Functions"),
|
|
|
+ ("classes.html", "Classes"),
|
|
|
+]
|
|
|
+
|
|
|
+def urldecode(url):
|
|
|
+ regex = re.compile("%([0-9a-hA-H][0-9a-hA-H])", re.M)
|
|
|
+ return regex.sub(lambda x: chr(int(x.group(1), 16)), url)
|
|
|
+
|
|
|
+def ireplace(string, target, replacement):
|
|
|
+ """Case-insensitive replace."""
|
|
|
+ index = string.lower().find(target.lower())
|
|
|
+ if index >= 0:
|
|
|
+ result = string[:index] + replacement + string[index + len(target):]
|
|
|
+ return result
|
|
|
+ else:
|
|
|
+ return string
|
|
|
+
|
|
|
+def parseAnchor(node):
|
|
|
+ """Parses an XML minidom node representing an anchor and returns a tuple
|
|
|
+ containing the href and the content of the link."""
|
|
|
+ assert node.nodeType == Node.ELEMENT_NODE
|
|
|
+ assert node.localName == "a"
|
|
|
+ href = ""
|
|
|
+ title = ""
|
|
|
+ for localName, a in node.attributes.items():
|
|
|
+ if localName.lower() == "href":
|
|
|
+ href = a
|
|
|
+ for e in node.childNodes:
|
|
|
+ if e.nodeType == Node.TEXT_NODE:
|
|
|
+ title += e.data
|
|
|
+ return href, title
|
|
|
+
|
|
|
+def parseManualTree(node):
|
|
|
+ """Parses a tree of the manual Main_Page and returns it through a list containing tuples:
|
|
|
+ [(title, href, [(title, href, [...]), ...]), ...]"""
|
|
|
+ if node.nodeType != Node.ELEMENT_NODE: return []
|
|
|
+ result = []
|
|
|
+ lastadded = None
|
|
|
+ for e in node.childNodes:
|
|
|
+ if e.nodeType == Node.ELEMENT_NODE:
|
|
|
+ if e.localName == "ol":
|
|
|
+ assert lastadded != None
|
|
|
+ for i in xrange(len(result)):
|
|
|
+ if result[i][:2] == lastadded:
|
|
|
+ result[i] = lastadded + (parseManualTree(e),)
|
|
|
+ elif e.localName == "a":
|
|
|
+ href, title = parseAnchor(e)
|
|
|
+ lastadded = title, href
|
|
|
+ result.append((title, href, None))
|
|
|
+ return result
|
|
|
+
|
|
|
+def parseManualTOC(filename):
|
|
|
+ """Reads the manual's Main_Page file and returns a list of all the trees found."""
|
|
|
+ filename = open(filename)
|
|
|
+ text = filename.read()
|
|
|
+ filename.close()
|
|
|
+ text = text.split("<h2>Table of Contents</h2>")[1].split("</td>")[0]
|
|
|
+ text = "<root>" + text.replace("<li>", "") + "</root>"
|
|
|
+ text = re.sub(re.compile("<!--([^>]+)>"), "", text)
|
|
|
+ result = []
|
|
|
+ for e in xml.dom.minidom.parseString(text).childNodes[0].childNodes:
|
|
|
+ if e.nodeType == Node.ELEMENT_NODE:
|
|
|
+ result.append(parseManualTree(e))
|
|
|
+ return result
|
|
|
+
|
|
|
+def treeToHTML(tree, dirname, indent = ""):
|
|
|
+ """Converts a tree into HTML code suitable for .hhc or .hhk files. The tree should be like:
|
|
|
+ [(title, href, [(title, href, [...]), ...]), ...]"""
|
|
|
+ html = ""
|
|
|
+ for title, href, sub in tree:
|
|
|
+ html += indent + "<li><object type=\"text/sitemap\">\n"
|
|
|
+ html += indent + " <param name=\"Name\" value=\"%s\">\n" % title.replace("CXX", "C++").replace("\"", """)
|
|
|
+ html += indent + " <param name=\"Local\" value=\"%s\">\n" % urldecode(os.path.join(dirname, href))
|
|
|
+ html += indent + "</object>\n"
|
|
|
+ if sub != None:
|
|
|
+ html += indent + "<ul>\n"
|
|
|
+ html += treeToHTML(sub, dirname, indent + " ")
|
|
|
+ html += indent + "</ul>\n"
|
|
|
+ return html
|
|
|
+
|
|
|
+def makeCHM(outputfile, dirname, title, special = None):
|
|
|
+ """Creates a CHM file based on a directory of HTML files. See the top of this file for more info."""
|
|
|
+ assert special == None or special in ["manual", "reference"]
|
|
|
+ reference = (special == "reference")
|
|
|
+ manual = (special == "manual")
|
|
|
+ base = ireplace(outputfile, ".chm", "")
|
|
|
+ if os.path.isfile(base + ".chm"): os.remove(base + ".chm")
|
|
|
+ # Create the hhp file
|
|
|
+ hhp = open(base + ".hhp", "w")
|
|
|
+ hhp.write("[OPTIONS]\n")
|
|
|
+ hhp.write(OPTIONBLOCK % (base + ".chm", base, urldecode(os.path.join(dirname, "index.html")), base, title))
|
|
|
+ hhp.write("\n[FILES]\n")
|
|
|
+ # Create the TOC file and Index file
|
|
|
+ hhk = open(base + ".hhk", "w")
|
|
|
+ hhc = open(base + ".hhc", "w")
|
|
|
+ hhk.write(HTMLBLOCK)
|
|
|
+ hhc.write(HTMLBLOCK)
|
|
|
+ # The manual should be treated as a special case.
|
|
|
+ if manual:
|
|
|
+ hhc.write(" <li><object type=\"text/sitemap\">\n")
|
|
|
+ hhc.write(" <param name=\"Name\" value=\"Main Page\">\n")
|
|
|
+ hhc.write(" <param name=\"Local\" value=\"%s\">\n" % urldecode(os.path.join(dirname, "index.html")))
|
|
|
+ hhc.write(" </object>\n")
|
|
|
+ for item in parseManualTOC("manual/index.html"):
|
|
|
+ hhc.write(treeToHTML(item, dirname, " "))
|
|
|
+ for i in os.listdir(dirname):
|
|
|
+ hhp.write(os.path.join(dirname, i) + "\n")
|
|
|
+ if i != "index.html":
|
|
|
+ hhk.write(" <li><object type=\"text/sitemap\">\n")
|
|
|
+ hhk.write(" <param name=\"Name\" value=\"%s\">\n" % ireplace(urldecode(i).replace(".1", "").replace("_", " ").replace("CXX", "C++"), ".html", "").replace("\"", """))
|
|
|
+ hhk.write(" <param name=\"Local\" value=\"%s\">\n" % urldecode(os.path.join(dirname, i)))
|
|
|
+ hhk.write(" </object>\n")
|
|
|
+ else:
|
|
|
+ idt = " "
|
|
|
+ # If we are writing out the reference, write some extra stuff.
|
|
|
+ if reference:
|
|
|
+ idt = " "
|
|
|
+ for i, desc in REFERENCEITEMS:
|
|
|
+ hhk.write(" <li><object type=\"text/sitemap\">\n")
|
|
|
+ hhk.write(" <param name=\"Name\" value=\"%s\">\n" % desc.replace("\"", """))
|
|
|
+ hhk.write(" <param name=\"Local\" value=\"%s\">\n" % urldecode(os.path.join(dirname, i)))
|
|
|
+ hhk.write(" </object>\n")
|
|
|
+ hhc.write(" <li><object type=\"text/sitemap\">\n")
|
|
|
+ hhc.write(" <param name=\"Name\" value=\"%s\">\n" % desc.replace("\"", """))
|
|
|
+ hhc.write(" <param name=\"Local\" value=\"%s\">\n" % urldecode(os.path.join(dirname, i)))
|
|
|
+ hhc.write(" </object>\n")
|
|
|
+ hhc.write(" <ul>\n")
|
|
|
+ # Loop through the directories and write out relevant data.
|
|
|
+ for i in os.listdir(dirname):
|
|
|
+ hhp.write(os.path.join(dirname, i) + "\n")
|
|
|
+ if i != "index.html" and ((not reference) or (not i in ["classes.html", "methods.html", "functions.html"])):
|
|
|
+ hhk.write(" <li><object type=\"text/sitemap\">\n")
|
|
|
+ hhk.write(" <param name=\"Name\" value=\"%s\">\n" % ireplace(urldecode(i).replace(".1", ""), ".html", "").replace("\"", """))
|
|
|
+ hhk.write(" <param name=\"Local\" value=\"%s\">\n" % urldecode(os.path.join(dirname, i)))
|
|
|
+ hhk.write(" </object>\n")
|
|
|
+ hhc.write(idt + "<li><object type=\"text/sitemap\">\n")
|
|
|
+ hhc.write(idt + " <param name=\"Name\" value=\"%s\">\n" % ireplace(urldecode(i).replace(".1", ""), ".html", "").replace("\"", """))
|
|
|
+ hhc.write(idt + " <param name=\"Local\" value=\"%s\">\n" % urldecode(os.path.join(dirname, i)))
|
|
|
+ hhc.write(idt + "</object>\n")
|
|
|
+ # Close the files.
|
|
|
+ if reference: hhc.write(" </ul>\n")
|
|
|
+ hhk.write(" </ul>\n </body>\n</html>")
|
|
|
+ hhc.write(" </ul>\n </body>\n</html>")
|
|
|
+ hhk.close()
|
|
|
+ hhc.close()
|
|
|
+ hhp.close()
|
|
|
+ # Now, execute the command to compile the files.
|
|
|
+ if os.path.isdir("C:\Program Files\HTML Help Workshop"):
|
|
|
+ cmd = "\"C:\Program Files\HTML Help Workshop\hhc.exe\" %s.hhp" % base
|
|
|
+ else:
|
|
|
+ cmd = "hhc \"%s.hhp\"" % base
|
|
|
+ print cmd
|
|
|
+ os.system(cmd)
|
|
|
+ if not KEEPTEMP:
|
|
|
+ if os.path.isfile("%s.hhp" % base): os.remove("%s.hhp" % base)
|
|
|
+ if os.path.isfile("%s.hhc" % base): os.remove("%s.hhc" % base)
|
|
|
+ if os.path.isfile("%s.hhk" % base): os.remove("%s.hhk" % base)
|
|
|
+ if os.path.isfile("%s.chw" % base): os.remove("%s.chw" % base)
|
|
|
+ if not os.path.isfile(base + ".chm"):
|
|
|
+ print "An error has occurred!"
|
|
|
+ if __name__ == "__main__":
|
|
|
+ exit(1)
|
|
|
+ else:
|
|
|
+ return False
|
|
|
+ if __name__ != "__main__":
|
|
|
+ return True
|
|
|
+
|
|
|
+def makeManualCHM(outputfile, dirname, title):
|
|
|
+ """Same as makeCHM, but suitable for converting the Panda3D manual."""
|
|
|
+ return makeCHM(outputfile, dirname, title, special = "manual")
|
|
|
+
|
|
|
+def makeReferenceCHM(outputfile, dirname, title):
|
|
|
+ """Same as makeCHM, but converts a structure resembling that of the Panda3D reference."""
|
|
|
+ return makeCHM(outputfile, dirname, title, special = "reference")
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ # Extract a version number, if we have one.
|
|
|
+ VERSION = None
|
|
|
+ try:
|
|
|
+ f = file("built/include/pandaVersion.h","r")
|
|
|
+ pattern = re.compile('^\s*[#]\s*define\s+PANDA_VERSION_STR\s+["]([0-9.]+)["]')
|
|
|
+ for line in f:
|
|
|
+ match = pattern.match(line,0)
|
|
|
+ if (match):
|
|
|
+ VERSION = match.group(1)
|
|
|
+ break
|
|
|
+ f.close()
|
|
|
+ except:
|
|
|
+ # If not, we don't care at all.
|
|
|
+ pass
|
|
|
+
|
|
|
+ # Now, make CHM's for both the manual and reference, if we have them.
|
|
|
+ if not os.path.isdir("manual"):
|
|
|
+ print "No directory named 'manual' found"
|
|
|
+ else:
|
|
|
+ print "Making CHM file for manual..."
|
|
|
+ if VERSION == None:
|
|
|
+ makeManualCHM("panda3d-manual.chm", "manual", "Panda3D Manual")
|
|
|
+ else:
|
|
|
+ makeManualCHM("panda3d-%s-manual.chm" % VERSION, "manual", "Panda3D %s Manual" % VERSION)
|
|
|
+
|
|
|
+ if not os.path.isdir("reference"):
|
|
|
+ print "No directory named 'reference' found"
|
|
|
+ else:
|
|
|
+ print "Making CHM file for API reference..."
|
|
|
+ if VERSION == None:
|
|
|
+ makeReferenceCHM("panda3d-reference.chm", "reference", "Panda3D Reference")
|
|
|
+ else:
|
|
|
+ makeReferenceCHM("panda3d-%s-reference.chm" % VERSION, "reference", "Panda3D %s Reference" % VERSION)
|
|
|
+
|
|
|
+ print "Done!"
|