def evaluate(self, context): '''Returns a number''' lrt = self._left.evaluate(context) lrt = Conversions.NumberValue(lrt) rrt = self._right.evaluate(context) rrt = Conversions.NumberValue(rrt) res = 0 # multiply if self._op == 0: res = lrt * rrt # divide elif self._op == 1: try: res = lrt / rrt except ZeroDivisionError: if lrt < 0: res = -number.inf elif lrt == 0: res = number.nan else: res = number.inf # modulo elif self._op == 2: try: res = lrt % rrt except ZeroDivisionError: res = number.nan return res
def evaluate(self, context): """Returns a boolean""" left = self._left.evaluate(context) right = self._right.evaluate(context) if isinstance(left, Types.NodesetType) or \ isinstance(right, Types.NodesetType): return _nodeset_compare(self._cmp, left, right) # From XPath 1.0 Section 3.4: # order for equality expressions when neither is a node-set # 1. either is boolean, both are converted as if by boolean() # 2. either is number, both are converted as if by number() # otherwise, both are converted as if by string() if isinstance(left, Types.BooleanType): right = Conversions.BooleanValue(right) elif isinstance(right, Types.BooleanType): left = Conversions.BooleanValue(left) elif isinstance(left, Types.NumberType): right = Conversions.NumberValue(right) elif isinstance(right, Types.NumberType): left = Conversions.NumberValue(left) else: left = Conversions.StringValue(left) right = Conversions.StringValue(right) return self._cmp(left, right) and boolean.true or boolean.false
def pipp_thumbnail(context, src, width, height): ctx = context.processor.extensionParams[(NAMESPACE, 'context')] image_name = ctx.abs_in_path(Conversions.StringValue(src)) ctx.add_depends(image_name[len(ctx.in_root):]) thumb_name = re.sub('(\.\w+)$', '_thumb\g<1>', Conversions.StringValue(src)) if width: width = int(Conversions.NumberValue(width)) if height: height = int(Conversions.NumberValue(height)) img = Image.open(image_name) w, h = img.size if height and not width: width = int(w * height / h) if width and not height: height = int(h * width / w) img = img.resize((width, height)) img.save(ctx.abs_out_path(ctx.abs_in_path(thumb_name))) #-- # Add image to cache using fake inroot name, so width/height functions work #-- images[ctx.abs_in_path(thumb_name)] = img return thumb_name
def ATan2(context, y, x): """ The math:atan2 function returns the angle ( in radians ) from the X axis to a point (y,x). """ x = Conversions.NumberValue(x) y = Conversions.NumberValue(y) try: return math.atan2(y, x) except ValueError: return number.nan
def evaluate(self, context): """Returns a boolean""" left = self._left.evaluate(context) right = self._right.evaluate(context) if isinstance(left, Types.NodesetType) or \ isinstance(right, Types.NodesetType): return _nodeset_compare(self._cmp, left, right, relational=True) left = Conversions.NumberValue(left) right = Conversions.NumberValue(right) return self._cmp(left, right) and boolean.true or boolean.false
def evaluate(self, context): '''Returns a number''' if self._leftLit: lrt = self._left else: lrt = self._left.evaluate(context) lrt = Conversions.NumberValue(lrt) if self._rightLit: rrt = self._right else: rrt = self._right.evaluate(context) rrt = Conversions.NumberValue(rrt) return lrt + (rrt * self._sign)
def __init__(self, sign, left, right): self._sign = sign self._leftLit = 0 self._rightLit = 0 if isinstance(left, ParsedLiteralExpr): self._leftLit = 1 self._left = Conversions.NumberValue(left.evaluate(None)) else: self._left = left if isinstance(right, ParsedLiteralExpr): self._rightLit = 1 self._right = Conversions.NumberValue(right.evaluate(None)) else: self._right = right return
def FormatNumber(context, number, formatString, decimalFormatName=None): """ Implementation of format-number(). Converts its first argument to a string using the format pattern string specified by the second argument and the decimal-format named by the third argument (see the xsl:decimal-format element), or the default decimal-format, if there is no third argument. The format pattern string is in the syntax specified by the JDK 1.1 DecimalFormat class. The decimal-format name must be a QName. It is an error if the stylesheet does not contain a declaration of the decimal-format with the specified expanded-name. """ num = Conversions.NumberValue(number) format_string = Conversions.StringValue(formatString) if decimalFormatName is not None: format_name = context.expandQName(decimalFormatName) else: format_name = None try: decimal_format = context.stylesheet.decimalFormats[format_name] except KeyError: raise XsltRuntimeException(Error.UNDEFINED_DECIMAL_FORMAT, decimalFormatName) return routines.FormatNumber(num, format_string, decimal_format)
def Substring(context, st, start, length=None): """Function: <string> substring(<string>, <number>, <number>?)""" if not isinstance(st, XPathStringType): st = Conversions.StringValue(st) if not isinstance(start, NumberType): start = Conversions.NumberValue(start) # start == NaN: spec doesn't say; assume no substring to return # start == +Inf or -Inf: no substring to return if number.isnan(start) or number.isinf(start): return u'' # start is finite, safe for int() and round(). start = int(round(start)) # convert to 0-based index for python string slice if start < 1: startidx = 0 else: startidx = start - 1 # length undefined: return chars startidx to end if length is None: return st[startidx:] elif not isinstance(length, NumberType): length = Conversions.NumberValue(length) # length == NaN: spec doesn't say; assume no substring to return if number.isnan(length): return u'' # length == +Inf: return chars startidx to end # length == -Inf: no substring to return elif number.isinf(length): if length > 0: return st[startidx:] else: return u'' # length is finite, safe for int() and round(). length = int(round(length)) # return value must end before position (start+length) # which is (start+length-1) in 0-based index endidx = start + length - 1 if endidx > startidx: return st[startidx:endidx] else: return u''
def instantiate(self, context, processor): # get value(s) to format if self._value: value = Conversions.NumberValue(self._value.evaluate(context)) if not number.finite(value) or value < 0.5: # This is an error. However, recovery is to just write # the number as if the string() function was used. processor.writers[-1].text(Conversions.StringValue(value)) return else: values = [int(round(value))] else: node = context.node if self._level == SINGLE: value = self._single_value(context, node, self._count, self._from) if value == 0: values = [] else: values = [value] elif self._level == MULTIPLE: values = self._multiple_values(context, node) elif self._level == ANY: value = self._any_value(context, node) if value == 0: values = [] else: values = [value] else: # 'single' without count or from attributes value = 1 prev = node.previousSibling type = node.nodeType expanded = (node.namespaceURI, node.localName) while prev: if prev.nodeType == type and \ (prev.namespaceURI, prev.localName) == expanded: value += 1 prev = prev.previousSibling values = [value] # format the value(s) grouping_size = int(self._grouping_size.evaluate(context)) if grouping_size: grouping_separator = self._grouping_separator.evaluate(context) else: grouping_separator = None formatter = self._formatter if not formatter: format = self._format and self._format.evaluate( context) or DEFAULT_FORMAT lang = self._lang and self._lang.evaluate(context) or DEFAULT_LANG letter_value = self._letter_value.evaluate(context) or '' formatter = self.createFormatter(format, lang, letter_value) numstr = formatter.format(values, grouping_size, grouping_separator) processor.writers[-1].text(numstr) return
def ATan(context, num): """ The math:atan function returns the arctangent value of a number. """ try: return math.atan(Conversions.NumberValue(num)) except ValueError: return number.nan
def ASin(context, num): """ The math:asin function returns the arcsine value of a number. """ try: return math.asin(Conversions.NumberValue(num)) except ValueError: return number.nan
def Round(context, object_): """Function: <number> round(<number>)""" num = Conversions.NumberValue(object_) if number.isnan(num) or number.isinf(num): return num elif num < 0 and num % 1.0 == 0.5: return round(num, 0) + 1 else: return round(num, 0)
def Constant(context, name, precision): """ The math:constant function returns the specified constant to a set precision. """ name = Conversions.StringValue(name) if not CONSTANTS.has_key(name): return number.nan precision = Conversions.NumberValue(precision) return float('%0.*f' % (int(precision), CONSTANTS[name]))
def Floor(context, object_): """Function: <number> floor(<number>)""" num = Conversions.NumberValue(object_) if number.isnan(num) or number.isinf(num): return num elif int(num) == num: return num elif num < 0: return float(int(num) - 1) else: return float(int(num))
def Ceiling(context, object_): """Function: <number> ceiling(<number>)""" num = Conversions.NumberValue(object_) if number.isnan(num) or number.isinf(num): return num elif int(num) == num: return num elif num > 0: return float(int(num) + 1) else: return float(int(num))
def Java_Color_GetHSBColor(context, hue, saturation, brightness): hue = Conversions.NumberValue(hue) saturation = Conversions.NumberValue(saturation) brightness = Conversions.NumberValue(brightness) if saturation == 0: r = g = b = int(brightness * 255) else: r = g = b = 0 h = (hue - int(hue)) * 6.0 f = h - int(h) p = brightness * (1.0 - saturation) q = brightness * (1.0 - saturation * f) t = brightness * (1.0 - (saturation * (1.0 - f))) h = int(h) if h == 0: r = int(brightness * 255) g = int(t * 255) b = int(p * 255) elif h == 1: r = int(q * 255) g = int(brightness * 255) b = int(p * 255) elif h == 2: r = int(p * 255) g = int(brightness * 255) b = int(t * 255) elif h == 3: r = int(p * 255) g = int(q * 255) b = int(brightness * 255) elif h == 4: r = int(t * 255) g = int(p * 255) b = int(brightness * 255) elif h == 5: r = int(brightness * 255) g = int(p * 255) b = int(q * 255) return 0xff000000L | (r << 16) | (g << 8) | (b << 0)
def Wrap(context, text, width): """ f:wrap() returns a string with the text reflowed so that each line fits within the given width. Existing linefeeds are preserved, but spaces are considered inter-word separators that can be collapsed. To reflow without preserving existing linefeeds, strip them first, e.g. with translate(text, ' ', ''). http://lists.fourthought.com/pipermail/4suite-dev/2002-December/000878.html """ s = Conversions.StringValue(text) width = Conversions.NumberValue(width) return LineWrap(s, width)
def _xu_append(self, context, element, preserveSpace): select = element.getAttributeNS(EMPTY_NAMESPACE, u'select') if not select: raise XUpdateException(XUpdateException.NO_SELECT) child = element.getAttributeNS(EMPTY_NAMESPACE, u'child') or u'last()' oldNss = context.processorNss context.processorNss = Domlette.GetAllNs(element) nodeset = self.evaluateExpression(context, select) if not isinstance(nodeset, Types.NodesetType) or not nodeset: # Error if not a node-set or empty node-set raise XUpdateException(XUpdateException.INVALID_SELECT, expr=select) for refnode in nodeset: self.pushDomResult(refnode.ownerDocument) # A wrapper element is used in case attributes are being added wrapper_localName = 'wrapper' wrapper_namespace = EMPTY_NAMESPACE try: self.writers[-1].startElement(wrapper_localName, wrapper_namespace) for node in element.childNodes: self.visit(context, node, preserveSpace) finally: self.writers[-1].endElement(wrapper_localName, wrapper_namespace) result = self.popResult() size = len(refnode.childNodes) con = Context.Context(refnode, 1, size, processorNss={'xupdate': XUPDATE_NS}) # Python lists is 0-indexed counting, node-sets 1-indexed position = self.evaluateExpression(con, child) position = int(Conversions.NumberValue(position)) wrapper = result.childNodes[0] if wrapper.attributes and hasattr(refnode, 'setAttributeNodeNS'): for attr in wrapper.attributes.values(): refnode.setAttributeNodeNS(attr) # we operate on a shallow copy of the child nodes here to avoid # modifying the membership of the sequence we're interating over. for node in tuple(wrapper.childNodes): if position >= size: refnode.appendChild(node) else: refnode.insertBefore(node, refnode.childNodes[position]) context.processorNss = oldNss return
def pipp_gtitle(context, font, height, texture, bgcolor, text): ctx = context.processor.extensionParams[(NAMESPACE, 'context')] ctx.add_depends(Conversions.StringValue(font)) ctx.add_depends(Conversions.StringValue(texture)) #-- # Convert the XSLT parameters into regular python types #-- font = ctx.abs_in_path(Conversions.StringValue(font)) height = int(Conversions.NumberValue(height)) texture = ctx.abs_in_path(Conversions.StringValue(texture)) bgcolor = int(Conversions.StringValue(bgcolor)[1:], 16) text = Conversions.StringValue(text) file_name = re.sub('[^a-zA-Z0-9]', '_', text) + '.png' pseudo_in_name = ctx.abs_in_path(file_name) # Avoid unwanted cropping text = ' ' + text + ' ' #-- # Create the text mask #-- im_font = ImageFont.truetype(font, height) text_mask = Image.new('RGBA', im_font.getsize(text), 0) text_mask_draw = ImageDraw.Draw(text_mask) text_mask_draw.text((0, 0), text, font=im_font, fill=(0, 0, 0, 0xFF)) text_mask = text_mask.crop(text_mask.getbbox()) #-- # Create the background pattern #-- texture = Image.open(texture) background = Image.new('RGBA', text_mask.size) for x in range(0, 1 + text_mask.size[0] / texture.size[0]): for y in range(0, 1 + text_mask.size[1] / texture.size[1]): background.paste(texture, (x * texture.size[0], y * texture.size[1])) #-- # Create the final image #-- out = Image.new('RGBA', text_mask.size, bgcolor) out.paste(background, (0, 0), text_mask) out = out.convert('P') out.save(ctx.abs_out_path(pseudo_in_name), transparency=0) #-- # Add image to cache using fake inroot name, so width/height functions work #-- images[pseudo_in_name] = out return file_name
def Sqrt(context, num): """ The math:sqrt function returns the square root of a number. """ # The platform C library determines what math.sqrt() returns. # On some platforms, especially prior to Python 2.4, # nan may be returned for a negative or nan argument. # On other platforms, and especially since Python 2.4, # a ValueError is raised. # # EXSLT requires that we return zero for negative arg. # The result for a nan arg is undefined, but we'll return nan. n = Conversions.NumberValue(num) if number.isnan(n): return number.nan if n < 0.0: return 0.0 try: return math.sqrt(Conversions.NumberValue(num)) except ValueError: return 0.0
def PytimeToExslt(context, t=None): """ Takes a Python time value as a number and returns a date/time as if from EXSLT date-time() t - a time stamp number, as from Python's time.time() if omitted, use the current time """ from Ft.Lib import Time as FtTime if t is not None: t = Conversions.NumberValue(t) return unicode(str(FtTime.FromPythonTime(t)), errors='replace') else: return unicode(str(FtTime.FromPythonTime()), errors='replace')
def Padding(context, length, chars=None): """ The str:padding function creates a padding string of a certain length. The second argument gives a string to be used to create the padding. This string is repeated as many times as is necessary to create a string of the length specified by the first argument; if the string is more than a character long, it may have to be truncated to produce the required length. If no second argument is specified, it defaults to a space (' '). """ length = int(Conversions.NumberValue(length)) chars = chars and Conversions.StringValue(chars) or u' ' return (chars * length)[:length]
def Range(context, lo, hi): """ Returns a node-set consisting of text nodes encapsulating integers in the numeric range bounded by the given low and high values. """ # contributed by Lars Marius Garshol; # originally using namespace URI 'http://garshol.priv.no/symbolic/' doc = context.node.rootNode # sanity check for n in (lo, hi): if number.isinf(n) or number.isnan(n): raise ValueError("Arguments to ft:range must be neither infinite nor NaN.") #xrange wants int, not float lo = int(round(Conversions.NumberValue(lo))) hi = int(round(Conversions.NumberValue(hi))) nodeset = [] for num in xrange(lo, hi): nodeset.append(doc.createTextNode(str(num))) return nodeset
def Random(context, max=None, forceInt=0): """ Returns a random number between 0 (inclusive) and max (exclusive). max defaults to 1. The first optional argument is a different value for max, and the second argument is a flag that, if set, causes the random number to be rounded to an integer. See also: EXSLT's math:random() """ if max: max = Conversions.NumberValue(max) else: max = 1.0 rt = DEFAULT_RNG.randrange(0, max) if forceInt: rt = round(rt) return rt
def Indent(context, text, levels, indentstring=None): """ f:indent() returns a string with each line of the text indented the given number of levels. For each level, the indent string, normally 2 spaces by default, is prepended to each line. """ text = Conversions.StringValue(text) levels = int(Conversions.NumberValue(levels)) if indentstring is None: indentstring = u' ' else: indentstring = Conversions.StringValue(indentstring) if indentstring and levels > 0: indent = indentstring * levels return indent + ('\n' + indent).join(text.split('\n')) else: return text
def Sum(context, nodeset, string): """ The dyn:sum function calculates the sum for the nodes passed as the first argument, where the value of each node is calculated dynamically using an XPath expression passed as a string as the second argument. http://www.exslt.org/dyn/functions/sum/index.html """ if type(nodeset) != type([]): raise XsltRuntimeException(Error.WRONG_ARGUMENT_TYPE, context.currentInstruction) string = Conversions.StringValue(string) try: expr = parser.new().parse(string) except SyntaxError: tb = handle_traceback() msg = 'Syntax error in XPath "%s", masked by empty node set return:\n%s' % ( string, tb.getvalue()) context.processor.warning(msg) return [] return sum( [Conversions.NumberValue(n) for n in MapImpl(context, nodeset, expr)])
def Duration(context, seconds=None): """ The date:duration function returns a duration string representing the number of seconds specified by the argument string. If no argument is given, then the result of calling date:seconds without any arguments is used as a default argument. Implements version 1. """ if seconds is None: # The epoch for EXSLT is 1970-01-01T00:00:00Z # FIXME: we could code around this, but most (all?) platforms we # support have a time() epoch of 1970-01-01, so why bother. if time.mktime((1970, 1, 1, 0, 0, 0, 0, 0, 0)) != time.timezone: warnings.warn("platform epoch != 1970-01-01", RuntimeWarning) # Don't use fractional seconds to keep with constructed dateTimes seconds = int(time.time()) else: seconds = Conversions.NumberValue(seconds) if not number.finite(seconds): # +/-Inf or NaN return u'' duration = _Duration(negative=(seconds < 0), seconds=abs(seconds)) return unicode(duration)
def Java_Integer_ToHexString(context, number): return '%X' % Conversions.NumberValue(number)
def Java_Color_GetBlue(context, color): color = Conversions.NumberValue(color) return long(color) & 0xff