--- /dev/null
+#!/usr/bin/python -u
+
+# THIS IS ALPHA CODE AND IS NOT COMPLETE!
+
+import sys
+import string
+import libxml2
+import libxslt
+import re
+import math
+
+# Some globals
+pixelsPerInch = 96.0
+unitHash = { 'in': pixelsPerInch,
+ 'cm': pixelsPerInch / 2.54,
+ 'mm': pixelsPerInch / 25.4,
+ 'pc': (pixelsPerInch / 72.0) * 12,
+ 'pt': pixelsPerInch / 72.0,
+ 'px': 1 }
+
+# ======================================================================
+
+def adjustColumnWidths(ctx, nodeset):
+ #
+ # Small check to verify the context is correcly accessed
+ #
+ try:
+ pctxt = libxslt.xpathParserContext(_obj=ctx)
+ ctxt = pctxt.context()
+ tctxt = ctxt.transformContext()
+ except:
+ pass
+
+ # Get the nominal table width
+ varString = lookupVariable(tctxt, "nominal.table.width", None);
+ if varString == None:
+ nominalWidth = 6 * pixelsPerInch;
+ else:
+ nominalWidth = convertLength(varString);
+
+ # Get the requested table width
+ tableWidth = lookupVariable(tctxt, "table.width", "100%")
+
+ foStylesheet = (tctxt.variableLookup("stylesheet.result.type", None) == "fo");
+
+ relTotal = 0
+ relParts = []
+
+ absTotal = 0
+ absParts = []
+
+ colgroup = libxml2.xmlNode(_obj = nodeset[0])
+ # If this is an foStylesheet, we've been passed a list of fo:table-columns.
+ # Otherwise we've been passed a colgroup that contains a list of cols.
+ if foStylesheet:
+ colChildren = colgroup
+ else:
+ colChildren = colgroup.children
+
+ col = colChildren
+ while col != None:
+ if foStylesheet:
+ width = col.prop("column-width")
+ else:
+ width = col.prop("width")
+
+ if width == None:
+ width = "1*"
+
+ relPart = 0.0;
+ absPart = 0.0;
+ starPos = string.find(width, "*");
+ if starPos >= 0:
+ relPart, absPart = string.split(width, "*", 2)
+ relPart = float(relPart);
+ relTotal = relTotal + float(relPart)
+ else:
+ absPart = width
+
+ pixels = convertLength(absPart);
+ absTotal = absTotal + pixels;
+
+ relParts.append(relPart);
+ absParts.append(pixels);
+
+ col = col.next
+
+ # Ok, now we have the relative widths and absolute widths in
+ # two parallel arrays.
+ #
+ # - If there are no relative widths, output the absolute widths
+ # - If there are no absolute widths, output the relative widths
+ # - If there are a mixture of relative and absolute widths,
+ # - If the table width is absolute, turn these all into absolute
+ # widths.
+ # - If the table width is relative, turn these all into absolute
+ # widths in the nominalWidth and then turn them back into
+ # percentages.
+
+ widths = [];
+
+ if relTotal == 0:
+ for absPart in absParts:
+ if foStylesheet:
+ inches = absPart / pixelsPerInch
+ widths.append("%4.2fin" % inches)
+ else:
+ widths.append("%d" % absPart)
+ elif absTotal == 0:
+ for relPart in relParts:
+ rel = relPart / relTotal * 100;
+ widths.append(rel);
+ widths = correctRoundingError(widths)
+ else:
+ pixelWidth = nominalWidth;
+ if string.find(tableWidth, "%") < 0:
+ pixelWidth = convertLength(tableWidth);
+
+ if pixelWidth <= absTotal:
+ print "Table is wider than table width"
+ else:
+ pixelWidth = pixelWidth - absTotal;
+
+ absTotal = 0
+ for count in range(len(relParts)):
+ rel = relParts[count] / relTotal * pixelWidth
+ relParts[count] = rel + absParts[count]
+ absTotal = absTotal + rel + absParts[count]
+
+ if string.find(tableWidth, "%") < 0:
+ for count in range(len(relParts)):
+ if foStylesheet:
+ pixels = relParts[count]
+ inches = pixels / pixelsPerInch
+ widths.append("%4.2fin" % inches)
+ else:
+ widths.append(relParts[count])
+ else:
+ for count in range(len(relParts)):
+ rel = relParts[count] / absTotal * 100
+ widths.append(rel)
+ widths = correctRoundingError(widths)
+
+ # Danger, Will Robinson! In-place modification of the result tree!
+ # Side-effect free? We don' need no steenkin' side-effect free!
+ count = 0
+ col = colChildren
+ while col != None:
+ if foStylesheet:
+ col.setProp("column-width", widths[count])
+ else:
+ col.setProp("width", widths[count])
+
+ count = count+1
+ col = col.next
+
+ return nodeset
+
+def convertLength(length):
+ # Given "3.4in" return the width in pixels
+ global pixelsPerInch
+ global unitHash
+
+ m = re.search('([+-]?[\d\.]+)(\S+)', length)
+ if m != None and m.lastindex > 1:
+ unit = pixelsPerInch;
+ if unitHash.has_key(m.group(2)):
+ unit = unitHash[m.group(2)];
+ else:
+ print "Unrecognized length: " + m.group(2)
+
+ pixels = unit * float(m.group(1))
+ else:
+ pixels = 0
+
+ return pixels
+
+def correctRoundingError(floatWidths):
+ # The widths are currently floating point numbers, we have to truncate
+ # them back to integers and then distribute the error so that they sum
+ # to exactly 100%.
+
+ totalWidth = 0
+ widths = [];
+ for width in floatWidths:
+ width = math.floor(width)
+ widths.append(width)
+ totalWidth = totalWidth + math.floor(width)
+
+ totalError = 100 - totalWidth
+ columnError = totalError / len(widths)
+ error = 0
+ for count in range(len(widths)):
+ width = widths[count]
+ error = error + columnError
+ if error >= 1.0:
+ adj = math.floor(error);
+ error = error - adj;
+ widths[count] = "%d%%" % (width + adj)
+ else:
+ widths[count] = "%d%%" % width
+
+ return widths
+
+def lookupVariable(tctxt, varName, default):
+ varString = tctxt.variableLookup(varName, None);
+ if varString == None:
+ return default
+
+ # If it's a list, get the first element
+ if type(varString) == type([]):
+ varString = varString[0]
+
+ # If it's not a string, it must be a node, get its content
+ if type(varString) != type(""):
+ varString = varString.content
+
+ return varString
+
+# ======================================================================
+# Random notes...
+
+#once you have a node which is a libxml2 python xmlNode wrapper all common
+#operations are possible:
+# .children .last .parent .next .prev .doc for navigation
+# .content .type for introspection
+# .prop("attribute_name") to lookup attribute values
+
+# # Now make a nodeset to return
+# # Danger, Will Robinson! This creates a memory leak!
+# newDoc = libxml2.newDoc("1.0");
+# newColGroup = newDoc.newDocNode(None, "colgroup", None);
+# newDoc.addChild(newColGroup);
+# col = colgroup.children
+# while col != None:
+# newCol = newDoc.newDocNode(None, "col", None);
+# newCol.copyPropList(col);
+# newCol.setProp("width", "4")
+# newColGroup.addChild(newCol)
+# col = col.next
--- /dev/null
+#!/usr/bin/python -u
+
+# THIS IS ALPHA CODE AND MAY NOT WORK CORRECTLY!
+
+import sys
+import string
+import libxml2
+import libxslt
+from docbook import adjustColumnWidths
+
+# Check the arguments
+
+usage = "Usage: %s xmlfile.xml xslfile.xsl [outputfile] [param1=val [param2=val]...]" % sys.argv[0]
+
+xmlfile = None
+xslfile = None
+outfile = "-"
+params = {}
+
+try:
+ xmlfile = sys.argv[1]
+ xslfile = sys.argv[2]
+except IndexError:
+ print usage;
+ sys.exit(1)
+
+try:
+ outfile = sys.argv[3]
+ if string.find(outfile, "=") > 0:
+ name, value = string.split(outfile, "=", 2);
+ params[name] = value
+
+ count = 4;
+ while (sys.argv[count]):
+ try:
+ name, value = string.split(sys.argv[count], "=", 2);
+ if params.has_key(name):
+ print "Warning: '%s' re-specified; replacing value" % name
+ params[name] = value
+ except ValueError:
+ print "Invalid parameter specification: '" + sys.argv[count] + "'"
+ print usage
+ sys.exit(1);
+ count = count+1;
+except IndexError:
+ pass
+
+# ======================================================================
+# Memory debug specific
+# libxml2.debugMemory(1)
+
+# Setup environment
+libxml2.lineNumbersDefault(1)
+libxml2.substituteEntitiesDefault(1)
+libxslt.registerExtModuleFunction("adjustColumnWidths",
+ "http://nwalsh.com/xslt/ext/xsltproc/python/Table",
+ adjustColumnWidths)
+
+# Initialize and run
+styledoc = libxml2.parseFile(xslfile)
+style = libxslt.parseStylesheetDoc(styledoc)
+doc = libxml2.parseFile(xmlfile)
+result = style.applyStylesheet(doc, params)
+
+# Save the result
+style.saveResultToFilename(outfile, result, 0)
+
+# Free things up
+style.freeStylesheet()
+doc.freeDoc()
+result.freeDoc()
+
+# Memory debug specific
+#libxslt.cleanup()
+#if libxml2.debugMemory(1) != 0:
+# print "Memory leak %d bytes" % (libxml2.debugMemory(1))
+# libxml2.dumpMemory()