docbook.py 7.1 KB

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