281 lines
12 KiB
Python
281 lines
12 KiB
Python
########################################################################
|
|
##
|
|
## 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("</div>")[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(dirname + "/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 "PROGRAMFILES" in os.environ and os.path.isdir("%s\\HTML Help Workshop" % os.environ["PROGRAMFILES"]):
|
|
cmd = "\"%s\\HTML Help Workshop\\hhc.exe\" %s.hhp" % (os.environ["PROGRAMFILES"], base)
|
|
elif 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.
|
|
for lang in ["python", "cxx"]:
|
|
if not os.path.isdir("manual-" + lang):
|
|
print "No directory named 'manual-%s' found" % lang
|
|
else:
|
|
print "Making CHM file for manual-%s..." % lang
|
|
if VERSION == None:
|
|
makeManualCHM("manual-%s.chm" % lang, "manual-" + lang, "Panda3D Manual")
|
|
else:
|
|
makeManualCHM("manual-%s-%s.chm" % (VERSION, lang), "manual-" + lang, "Panda3D %s Manual" % VERSION)
|
|
|
|
if not os.path.isdir("reference-" + lang):
|
|
print "No directory named 'reference-%s' found" % lang
|
|
else:
|
|
print "Making CHM file for reference-%s..." % lang
|
|
if VERSION == None:
|
|
makeReferenceCHM("reference-%s.chm" % lang, "reference-" + lang, "Panda3D Reference")
|
|
else:
|
|
makeReferenceCHM("reference-%s-%s.chm" % (VERSION, lang), "reference-" + lang, "Panda3D %s Reference" % VERSION)
|
|
|
|
print "Done!"
|
|
|