[b1a51ac1] | 1 | # docbook.py: extension module
|
---|
| 2 | # $Id: docbook.py 8353 2009-03-17 16:57:50Z mzjn $
|
---|
| 3 |
|
---|
| 4 | import sys
|
---|
| 5 | import string
|
---|
| 6 | import libxml2
|
---|
| 7 | import libxslt
|
---|
| 8 | import re
|
---|
| 9 | import math
|
---|
| 10 |
|
---|
| 11 | # Some globals
|
---|
| 12 | pixelsPerInch = 96.0
|
---|
| 13 | unitHash = { 'in': pixelsPerInch,
|
---|
| 14 | 'cm': pixelsPerInch / 2.54,
|
---|
| 15 | 'mm': pixelsPerInch / 25.4,
|
---|
| 16 | 'pc': (pixelsPerInch / 72.0) * 12,
|
---|
| 17 | 'pt': pixelsPerInch / 72.0,
|
---|
| 18 | 'px': 1 }
|
---|
| 19 |
|
---|
| 20 | # ======================================================================
|
---|
| 21 |
|
---|
| 22 | def adjustColumnWidths(ctx, nodeset):
|
---|
| 23 | #
|
---|
| 24 | # Small check to verify the context is correcly accessed
|
---|
| 25 | #
|
---|
| 26 | try:
|
---|
| 27 | pctxt = libxslt.xpathParserContext(_obj=ctx)
|
---|
| 28 | ctxt = pctxt.context()
|
---|
| 29 | tctxt = ctxt.transformContext()
|
---|
| 30 | except:
|
---|
| 31 | pass
|
---|
| 32 |
|
---|
| 33 | # Get the nominal table width
|
---|
| 34 | varString = lookupVariable(tctxt, "nominal.table.width", None)
|
---|
| 35 | if varString == None:
|
---|
| 36 | nominalWidth = 6 * pixelsPerInch;
|
---|
| 37 | else:
|
---|
| 38 | nominalWidth = convertLength(varString);
|
---|
| 39 |
|
---|
| 40 | # Get the requested table width
|
---|
| 41 | tableWidth = lookupVariable(tctxt, "table.width", "100%")
|
---|
| 42 |
|
---|
| 43 | foStylesheet = (tctxt.variableLookup("stylesheet.result.type", None) == "fo")
|
---|
| 44 |
|
---|
| 45 | relTotal = 0
|
---|
| 46 | relParts = []
|
---|
| 47 |
|
---|
| 48 | absTotal = 0
|
---|
| 49 | absParts = []
|
---|
| 50 |
|
---|
| 51 | colgroup = libxml2.xmlNode(_obj = nodeset[0])
|
---|
| 52 | # If this is an foStylesheet, we've been passed a list of fo:table-columns.
|
---|
| 53 | # Otherwise we've been passed a colgroup that contains a list of cols.
|
---|
| 54 | if foStylesheet:
|
---|
| 55 | colChildren = colgroup
|
---|
| 56 | else:
|
---|
| 57 | colChildren = colgroup.children
|
---|
| 58 |
|
---|
| 59 | col = colChildren
|
---|
| 60 | while col != None:
|
---|
| 61 | if foStylesheet:
|
---|
| 62 | width = col.prop("column-width")
|
---|
| 63 | else:
|
---|
| 64 | width = col.prop("width")
|
---|
| 65 |
|
---|
| 66 | if width == None:
|
---|
| 67 | width = "1*"
|
---|
| 68 |
|
---|
| 69 | relPart = 0.0
|
---|
| 70 | absPart = 0.0
|
---|
| 71 | starPos = string.find(width, "*")
|
---|
| 72 | if starPos >= 0:
|
---|
| 73 | relPart, absPart = string.split(width, "*", 2)
|
---|
| 74 | relPart = float(relPart)
|
---|
| 75 | relTotal = relTotal + float(relPart)
|
---|
| 76 | else:
|
---|
| 77 | absPart = width
|
---|
| 78 |
|
---|
| 79 | pixels = convertLength(absPart)
|
---|
| 80 | absTotal = absTotal + pixels
|
---|
| 81 |
|
---|
| 82 | relParts.append(relPart)
|
---|
| 83 | absParts.append(pixels)
|
---|
| 84 |
|
---|
| 85 | col = col.next
|
---|
| 86 |
|
---|
| 87 | # Ok, now we have the relative widths and absolute widths in
|
---|
| 88 | # two parallel arrays.
|
---|
| 89 | #
|
---|
| 90 | # - If there are no relative widths, output the absolute widths
|
---|
| 91 | # - If there are no absolute widths, output the relative widths
|
---|
| 92 | # - If there are a mixture of relative and absolute widths,
|
---|
| 93 | # - If the table width is absolute, turn these all into absolute
|
---|
| 94 | # widths.
|
---|
| 95 | # - If the table width is relative, turn these all into absolute
|
---|
| 96 | # widths in the nominalWidth and then turn them back into
|
---|
| 97 | # percentages.
|
---|
| 98 |
|
---|
| 99 | widths = []
|
---|
| 100 |
|
---|
| 101 | if relTotal == 0:
|
---|
| 102 | for absPart in absParts:
|
---|
| 103 | if foStylesheet:
|
---|
| 104 | inches = absPart / pixelsPerInch
|
---|
| 105 | widths.append("%4.2fin" % inches)
|
---|
| 106 | else:
|
---|
| 107 | widths.append("%d" % absPart)
|
---|
| 108 | elif absTotal == 0:
|
---|
| 109 | for relPart in relParts:
|
---|
| 110 | rel = relPart / relTotal * 100
|
---|
| 111 | widths.append(rel)
|
---|
| 112 | widths = correctRoundingError(widths)
|
---|
| 113 | else:
|
---|
| 114 | pixelWidth = nominalWidth
|
---|
| 115 | if string.find(tableWidth, "%") < 0:
|
---|
| 116 | pixelWidth = convertLength(tableWidth)
|
---|
| 117 |
|
---|
| 118 | if pixelWidth <= absTotal:
|
---|
| 119 | print "Table is wider than table width"
|
---|
| 120 | else:
|
---|
| 121 | pixelWidth = pixelWidth - absTotal
|
---|
| 122 |
|
---|
| 123 | absTotal = 0
|
---|
| 124 | for count in range(len(relParts)):
|
---|
| 125 | rel = relParts[count] / relTotal * pixelWidth
|
---|
| 126 | relParts[count] = rel + absParts[count]
|
---|
| 127 | absTotal = absTotal + rel + absParts[count]
|
---|
| 128 |
|
---|
| 129 | if string.find(tableWidth, "%") < 0:
|
---|
| 130 | for count in range(len(relParts)):
|
---|
| 131 | if foStylesheet:
|
---|
| 132 | pixels = relParts[count]
|
---|
| 133 | inches = pixels / pixelsPerInch
|
---|
| 134 | widths.append("%4.2fin" % inches)
|
---|
| 135 | else:
|
---|
| 136 | widths.append(relParts[count])
|
---|
| 137 | else:
|
---|
| 138 | for count in range(len(relParts)):
|
---|
| 139 | rel = relParts[count] / absTotal * 100
|
---|
| 140 | widths.append(rel)
|
---|
| 141 | widths = correctRoundingError(widths)
|
---|
| 142 |
|
---|
| 143 | # Danger, Will Robinson! In-place modification of the result tree!
|
---|
| 144 | # Side-effect free? We don' need no steenkin' side-effect free!
|
---|
| 145 | count = 0
|
---|
| 146 | col = colChildren
|
---|
| 147 | while col != None:
|
---|
| 148 | if foStylesheet:
|
---|
| 149 | col.setProp("column-width", widths[count])
|
---|
| 150 | else:
|
---|
| 151 | col.setProp("width", widths[count])
|
---|
| 152 |
|
---|
| 153 | count = count+1
|
---|
| 154 | col = col.next
|
---|
| 155 |
|
---|
| 156 | return nodeset
|
---|
| 157 |
|
---|
| 158 | def convertLength(length):
|
---|
| 159 | # Given "3.4in" return the width in pixels
|
---|
| 160 | global pixelsPerInch
|
---|
| 161 | global unitHash
|
---|
| 162 |
|
---|
| 163 | m = re.search('([+-]?[\d\.]+)(\S+)', length)
|
---|
| 164 | if m != None and m.lastindex > 1:
|
---|
| 165 | unit = pixelsPerInch
|
---|
| 166 | if unitHash.has_key(m.group(2)):
|
---|
| 167 | unit = unitHash[m.group(2)]
|
---|
| 168 | else:
|
---|
| 169 | print "Unrecognized length: " + m.group(2)
|
---|
| 170 |
|
---|
| 171 | pixels = unit * float(m.group(1))
|
---|
| 172 | else:
|
---|
| 173 | pixels = 0
|
---|
| 174 |
|
---|
| 175 | return pixels
|
---|
| 176 |
|
---|
| 177 | def correctRoundingError(floatWidths):
|
---|
| 178 | # The widths are currently floating point numbers, we have to truncate
|
---|
| 179 | # them back to integers and then distribute the error so that they sum
|
---|
| 180 | # to exactly 100%.
|
---|
| 181 |
|
---|
| 182 | totalWidth = 0
|
---|
| 183 | widths = []
|
---|
| 184 | for width in floatWidths:
|
---|
| 185 | width = math.floor(width)
|
---|
| 186 | widths.append(width)
|
---|
| 187 | totalWidth = totalWidth + math.floor(width)
|
---|
| 188 |
|
---|
| 189 | totalError = 100 - totalWidth
|
---|
| 190 | columnError = totalError / len(widths)
|
---|
| 191 | error = 0
|
---|
| 192 | for count in range(len(widths)):
|
---|
| 193 | width = widths[count]
|
---|
| 194 | error = error + columnError
|
---|
| 195 | if error >= 1.0:
|
---|
| 196 | adj = math.floor(error)
|
---|
| 197 | error = error - adj
|
---|
| 198 | widths[count] = "%d%%" % (width + adj)
|
---|
| 199 | else:
|
---|
| 200 | widths[count] = "%d%%" % width
|
---|
| 201 |
|
---|
| 202 | return widths
|
---|
| 203 |
|
---|
| 204 | def lookupVariable(tctxt, varName, default):
|
---|
| 205 | varString = tctxt.variableLookup(varName, None)
|
---|
| 206 | if varString == None:
|
---|
| 207 | return default
|
---|
| 208 |
|
---|
| 209 | # If it's a list, get the first element
|
---|
| 210 | if type(varString) == type([]):
|
---|
| 211 | varString = varString[0]
|
---|
| 212 |
|
---|
| 213 | # If it's not a string, it must be a node, get its content
|
---|
| 214 | if type(varString) != type(""):
|
---|
| 215 | varString = varString.content
|
---|
| 216 |
|
---|
| 217 | return varString
|
---|
| 218 |
|
---|
| 219 | # ======================================================================
|
---|
| 220 | # Random notes...
|
---|
| 221 |
|
---|
| 222 | #once you have a node which is a libxml2 python xmlNode wrapper all common
|
---|
| 223 | #operations are possible:
|
---|
| 224 | # .children .last .parent .next .prev .doc for navigation
|
---|
| 225 | # .content .type for introspection
|
---|
| 226 | # .prop("attribute_name") to lookup attribute values
|
---|
| 227 |
|
---|
| 228 | # # Now make a nodeset to return
|
---|
| 229 | # # Danger, Will Robinson! This creates a memory leak!
|
---|
| 230 | # newDoc = libxml2.newDoc("1.0")
|
---|
| 231 | # newColGroup = newDoc.newDocNode(None, "colgroup", None)
|
---|
| 232 | # newDoc.addChild(newColGroup)
|
---|
| 233 | # col = colgroup.children
|
---|
| 234 | # while col != None:
|
---|
| 235 | # newCol = newDoc.newDocNode(None, "col", None)
|
---|
| 236 | # newCol.copyPropList(col);
|
---|
| 237 | # newCol.setProp("width", "4")
|
---|
| 238 | # newColGroup.addChild(newCol)
|
---|
| 239 | # col = col.next
|
---|