assertions: - id: fallback text: "The contents of the canvas element, if any, are the element's fallback content<^>." - id: canvas.type text: "interface HTMLCanvasElement<^> : HTMLElement {" - id: size.width text: "attribute unsigned long width;<^>" - id: size.height text: "attribute unsigned long height;<^>" - id: size.nonnegativeinteger text: "The rules for parsing non-negative integers *must* be used to obtain their numeric values<^>." - id: size.missing text: "If an attribute is missing<^>, <...> then the default value *must* be used instead." - id: size.error text: "if parsing its value returns an error<^>, then the default value *must* be used instead." - id: size.default text: "The width attribute defaults to 300, and the height attribute defaults to 150<^>." - id: size.css text: "the element can be sized arbitrarily by a style sheet. During rendering, the image is scaled to fit this layout size<^>." - id: initial.colour text: "The canvas *must* initially be fully transparent black<^>." - id: initial.reset text: "Whenever the width and height attributes are set (whether to a new value or to the previous value), the bitmap and any associated contexts *must* be cleared back to their initial state <...><^>." - id: size.reflect text: "The width and height DOM attributes *must* reflect the content attributes of the same name<^>." - id: context.2d text: "If getContext() is called with that exact string for its contextId argument, then the UA *must* return a reference to an object implementing CanvasRenderingContext2D<^>." - id: context.empty text: "When the UA is passed an empty string<^> <...> then it *must* return null." - id: context.unrecognised text: "When the UA is passed <...> a string specifying a context that it does not support<^>, then it *must* return null." - id: context.casesensitive text: "String comparisons *must* be <...> case-sensitive<^>." - id: toDataURL.noarguments text: "The toDataURL() method *must*, when called with no arguments, return a data: URI containing a representation of the image as a PNG file<^>." - id: toDataURL.png text: "support for image/png is *required*<^>." - id: toDataURL.unrecognised text: "If the user agent does not support the requested type, it *must* return the image using the PNG format<^>." - id: toDataURL.lowercase text: "User agents *must* convert the provided type to lower case before establishing if they support that type and before creating the data: URI<^>." - id: toDataURL.arguments text: "Arguments other than the type *must* be ignored, and must not cause the user agent to raise an exception (as would normally occur if a method was called with the wrong number of arguments)<^>." - id: toDataURL.security text: "the toDataURL()<^> and getImageData() methods *should* raise a security exception if the canvas has ever had an image painted on it whose origin is different from that of the script calling the method." - id: getImageData.security text: "the toDataURL() and getImageData()<^> methods *should* raise a security exception if the canvas has ever had an image painted on it whose origin is different from that of the script calling the method." - id: context.2d.unique text: "calling the getContext() method with the 2d argument a second time *must* return the same object<^>." - id: 2d.coordinatespace text: "flat cartesian surface whose origin (0,0) is at the top left corner, with the coordinate space having x values increasing when going right, and y values increasing when going down<^>." - id: context.2d.type text: "interface CanvasRenderingContext2D<^> {" - id: 2d.canvas.attribute text: "readonly<^> attribute HTMLCanvasElement canvas;" - id: 2d.canvas text: "The canvas attribute *must* return the canvas element that the context paints on<^>." - meta: | for s in ['strokeStyle', 'fillStyle', 'globalAlpha', 'lineWidth', 'lineCap', 'lineJoin', 'miterLimit', 'shadowOffsetX', 'shadowOffsetY', 'shadowBlur', 'shadowColor', 'globalCompositeOperation']: assertions.append( { 'id': '2d.state.%s' % s, 'text': 'The current values of the following attributes:<...>%s<^>' % s } ) - id: 2d.state.transformation text: "The current transformation matrix<^>." - id: 2d.state.clip text: "The current clipping path<^>." - id: 2d.state.path text: "The current path<^> <...> are not part of the drawing state." - id: 2d.state.bitmap text: "The <...> current bitmap<^> are not part of the drawing state." - id: 2d.state.save text: "The save() method must push a copy of the current drawing state onto the drawing state stack<^>." - id: 2d.state.restore text: "The restore() method must pop the top entry in the drawing state stack, and reset the drawing state it describes<^>." - id: 2d.state.restore.underflow text: "If there is no saved state, the method must do nothing<^>." - id: 2d.transformation.order text: "For instance, <...> the actual result will be a square<^>." - id: 2d.transformation.scale text: "The scale(x, y) method *must* add the scaling transformation described by the arguments to the transformation matrix<^>." - id: 2d.transformation.scale.multiple text: "The factors are multiples<^>." - id: 2d.transformation.rotate text: "The rotate(angle) method *must* add the rotation transformation described by the argument to the transformation matrix<^>." - id: 2d.transformation.rotate.direction text: "The angle argument represents a clockwise rotation angle<^>" - id: 2d.transformation.rotate.radians text: "The angle argument <...> expressed in radians<^>." - id: 2d.transformation.translate text: "The translate(x, y) method *must* add the translation transformation described by the arguments to the transformation matrix<^>." - id: 2d.transformation.transform text: "The transform(m11, m12, m21, m22, dx, dy) method *must* multiply the current transformation matrix with the matrix described by<^>:" - id: 2d.transformation.transform.multiply text: "The transform(m11, m12, m21, m22, dx, dy) method *must* multiply<^> the current transformation matrix with the matrix described by:" - id: 2d.transformation.setTransform text: "The setTransform(m11, m12, m21, m22, dx, dy) method *must* <...> invoke the transform(m11, m12, m21, m22, dx, dy) method with the same arguments<^>" - id: 2d.transformation.setTransform.identity text: "The setTransform(m11, m12, m21, m22, dx, dy) method *must* reset the current transform to the identity matrix<^>, " - id: 2d.composite.operation text: "All drawing operations are affected by the global compositing attributes, globalAlpha and globalCompositeOperation<^>." - id: 2d.composite.globalAlpha.shape text: "The globalAlpha attribute gives an alpha value that is applied to shapes<^> and images before they are composited onto the canvas." - id: 2d.composite.globalAlpha.image text: "The globalAlpha attribute gives an alpha value that is applied to shapes and images<^> before they are composited onto the canvas." - id: 2d.composite.globalAlpha.range text: "The value must be in the range from 0.0 (fully transparent) to 1.0 (no additional transparency). If an attempt is made to set the attribute to a value outside this range, the attribute *must* retain its previous value<^>." - id: 2d.composite.globalAlpha.default text: "When the context is created, the globalAlpha attribute *must* initially have the value 1.0<^>." - id: 2d.composite.source-atop text: "source-atop<^>" - id: 2d.composite.source-in text: "source-in<^>" - id: 2d.composite.source-out text: "source-out<^>" - id: 2d.composite.source-over text: "source-over<^> (default)" - id: 2d.composite.destination-atop text: "destination-atop<^>" - id: 2d.composite.destination-in text: "destination-in<^>" - id: 2d.composite.destination-out text: "destination-out<^>" - id: 2d.composite.destination-over text: "destination-over<^>" - id: 2d.composite.lighter text: "lighter<^>" - id: 2d.composite.copy text: "copy<^>" - id: 2d.composite.xor text: "xor<^>" - id: 2d.composite.operation.casesensitive text: "These values are all case-sensitive<^> <...> they *must* be used exactly as shown." - id: 2d.composite.operation.exact text: "User agents *must* only recognise values that exactly match the values given above<^>." - id: 2d.composite.operation.unrecognised text: "On setting, if the user agent does not recognise the specified value, it *must* be ignored, leaving the value of globalCompositeOperation unaffected<^>." - id: 2d.composite.operation.default text: "When the context is created, the globalCompositeOperation attribute *must* initially have the value source-over<^>." - id: 2d.colours.parse text: "On setting, strings *must* be parsed as CSS values and the color assigned<^>, <...> [CSS3COLOR]" - id: 2d.colours.invalidstring text: "If the value is a string but is not a valid color<^>, <...> then it *must* be ignored, and the attribute must retain its previous value." - id: 2d.colours.invalidtype text: "If the value is <...> neither a string, a CanvasGradient, nor a CanvasPattern<^>, then it *must* be ignored, and the attribute must retain its previous value." - id: 2d.colours.getcolour.solid text: "if it has alpha equal to 1.0, then the string is a lowercase six-digit hex value<^>" - id: 2d.colours.getcolour.transparent text: "Otherwise, the color value has alpha less than 1.0, and the string is the color value in the CSS rgba() functional-notation format: <...> finally a U+0029 RIGHT PARENTHESIS<^>." - id: 2d.gradient.object text: "if it is not a color but a CanvasGradient<^> or CanvasPattern, then the respective object *must* be returned." - id: 2d.pattern.object text: "if it is not a color but a CanvasGradient or CanvasPattern<^>, then the respective object *must* be returned." - id: 2d.colours.default text: "When the context is created, the strokeStyle and fillStyle attributes *must* initially have the string value #000000<^>." - id: 2d.gradient.interpolate.linear text: "Between each such stop, the colors and the alpha component *must* be linearly interpolated<^> over the RGBA space without premultiplying the alpha value to find the color to use at that offset." - id: 2d.gradient.interpolate.alpha text: "Between each such stop, the colors and the alpha component *must* be linearly interpolated over the RGBA space without premultiplying the alpha value<^> to find the color to use at that offset." - id: 2d.gradient.interpolate.overlap text: "If multiple stops are added at the same offset on a gradient, they *must* be placed in the order added, with the first one closest to the start of the gradient, <...><^>." - id: 2d.gradient.invalidoffset text: "If the offset is less than 0 or greater than 1 then an INDEX_SIZE_ERR exception *must* be raised<^>." - id: 2d.gradient.invalidcolour text: "If the color cannot be parsed as a CSS color, then a SYNTAX_ERR exception *must* be raised<^>." - id: 2d.gradient.update text: "Otherwise, the gradient *must* have a new stop placed, at offset offset relative to the whole gradient, and with the color obtained by parsing color as a CSS value<^>." - id: 2d.gradient.return.linear text: "The createLinearGradient(x0, y0, x1, y1) method <...> *must* return a linear CanvasGradient<^> initialised with that line." - id: 2d.gradient.outside.first text: "Before the first stop, the color *must* be the color of the first stop<^>." - id: 2d.gradient.outside.last text: "After the last stop, the color *must* be the color of the last stop<^>." - id: 2d.gradient.empty text: "When there are no stops, the gradient is transparent black<^>." - id: 2d.gradient.zerosize text: "If x0 = x1 and y0 = y1, then the linear gradient *must* paint nothing<^>." - id: 2d.gradient.return.radial text: "The createRadialGradient(x0, y0, r0, x1, y1, r1) method <...> *must* return a radial CanvasGradient<^> initialised with those two circles." - id: 2d.gradient.radial.negative text: "If either of r0 or r1 are negative, an INDEX_SIZE_ERR exception *must* be raised<^>." - id: 2d.gradient.radial.rendering text: "Radial gradients *must* be rendered by following these steps<^>:" - id: 2d.gradient.radial.equal text: "If x0 = x1 and y0 = y1 and r0 = r1, then the radial gradient *must* paint nothing<^>." - id: 2d.pattern.modify text: "Modifying this image after calling the createPattern() method *must* not affect the pattern<^>." - id: 2d.pattern.missing text: "If the empty string or null is specified, repeat *must* be assumed<^>." - id: 2d.pattern.unrecognised text: "If an unrecognised value is given, then the user agent *must* raise a SYNTAX_ERR exception<^>." - id: 2d.pattern.exact text: "User agents *must* recognise the four values described above exactly (e.g. they must not do case folding)<^>." - id: 2d.pattern.return text: "The method *must* return a CanvasPattern object suitably initialised<^>." - id: 2d.pattern.wrongtype text: "If the image is of the wrong type, the implementation *must* raise a TYPE_MISMATCH_ERR exception<^>. If the image argument" - id: 2d.pattern.incomplete text: "a TYPE_MISMATCH_ERR exception. If the image argument is an HTMLImageElement object whose complete attribute is false, then the implementation *must* raise an INVALID_STATE_ERR exception<^>." - id: 2d.pattern.painting text: "Patterns *must* be painted so that the top left of the first image is anchored at the origin of the coordinate space, and images are then repeated horizontally to the left and right (if the repeat-x string was specified) or vertically up and down (if the repeat-y string was specified) or in all four directions all over the canvas (if the repeat string was specified)<^>." - id: 2d.pattern.unscaled text: "The images are not be scaled by this process; one CSS pixel of the image *must* be painted on one coordinate space unit<^>." - id: 2d.pattern.extent text: "patterns *must* only actually painted where the stroking or filling effect requires that they be drawn, and are affected by the current transformation matrix<^>." - id: 2d.pattern.unsupported text: "If the user agent doesn't support patterns, then createPattern() *must* return null<^>." - id: 2d.lineWidth text: "The lineWidth attribute gives the default width of lines, in coordinate space units<^>." - id: 2d.lineWidth.invalid text: "The lineWidth attribute <...>. On setting, zero and negative values *must* be ignored, leaving the value unchanged<^>." - id: 2d.lineWidth.default text: "the lineWidth attribute *must* initially have the value 1.0<^>." - id: 2d.lineCap.butt text: "The butt value means that the end of each line is a flat edge perpendicular to the direction of the line<^>." - id: 2d.lineCap.round text: "The round value means that a semi-circle with the diameter equal to the width of the line is then added on to the end of the line<^>." - id: 2d.lineCap.square text: "The square value means that at the end of each line is a rectangle with the length of the line width and the width of half the line width, placed flat against the edge perpendicular to the direction of the line<^>." - id: 2d.lineCap.invalid text: "On setting, any other value than the literal strings butt, round, and square *must* be ignored, leaving the value unchanged<^>." - id: 2d.lineCap.default text: "When the context is created, the lineCap attribute *must* initially have the value butt<^>." - id: 2d.lineJoin.invalid text: "On setting, any other value than the literal strings round, bevel and miter *must* be ignored, leaving the value unchanged<^>." - id: 2d.lineJoin.default text: "When the context is created, the lineJoin attribute *must* initially have the value miter<^>." - id: 2d.lineJoin.round text: "The round value means that a filled arc connecting the corners on the outside of the join, with the diameter equal to the line width, and the origin at the point where the inside edges of the lines touch, *must* be rendered at joins<^>." - id: 2d.lineJoin.bevel text: "The bevel value means that a filled triangle connecting those two corners with a straight line, the third point of the triangle being the point where the lines touch on the inside of the join, *must* be rendered at joins<^>." - id: 2d.lineJoin.miter text: "The miter value means that a filled four- or five-sided polygon *must* be placed at the join, <...><^>." - id: 2d.lineJoin.miterLimit text: "If the miter limit would be exceeded, then a fifth line *must* be added to the polygon, <...><^>." - id: 2d.miterLimit.invalid text: "The <...> miterLimit attribute. On setting, zero and negative values *must* be ignored, leaving the value unchanged<^>." - id: 2d.miterLimit.default text: "When the context is created, the miterLimit attribute *must* initially have the value 10.0<^>." - id: 2d.rect.negative text: "Negative values for width and height *must* cause the implementation to raise an INDEX_SIZE_ERR exception<^>." - id: 2d.clearRect text: "The clearRect() method *must* clear the pixels in the specified rectangle that also intersect the current clipping path to a fully transparent black, erasing any previous image<^>." - id: 2d.fillRect text: "The fillRect() method *must* paint the specified rectangular area using the fillStyle<^>." - id: 2d.strokeRect text: "The strokeRect() method *must* draw stroke the specified rectangle's path using the strokeStyle, lineWidth, lineJoin, and (if appropriate) miterLimit attributes<^>." - id: 2d.path.initial text: "Initially, the context's path *must* have zero subpaths<^>." - id: 2d.path.beginPath text: "The beginPath() method *must* empty the list of subpaths so that the context once again has zero subpaths<^>." - id: 2d.path.moveTo text: "The moveTo(x, y) method *must* create a new subpath with the specified point as its first (and only) point<^>." - id: 2d.path.closePath.empty text: "The closePath() method *must* do nothing if the context has no subpaths<^>." - id: 2d.path.closePath.nonempty text: "The closePath() method <...> *must* mark the last subpath as closed, create a new subpath whose first point is the same as the previous subpath's first point, and finally add this new subpath to the path<^>." - id: 2d.path.lineTo.empty text: "The lineTo(x, y) method *must* do nothing if the context has no subpaths<^>." - id: 2d.path.lineTo.nonempty text: "The lineTo(x, y) method <...> *must* connect the last point in the subpath to the given point (x, y) using a straight line, and must then add the given point (x, y) to the subpath<^>." - id: 2d.path.quadratic.empty text: "The quadraticCurveTo(cpx, cpy, x, y) method *must* do nothing if the context has no subpaths<^>." - id: 2d.path.quadratic.nonempty text: "The quadraticCurveTo(cpx, cpy, x, y) method <...> *must* connect the last point in the subpath to the given point (x, y) using a quadratic B<...>zier curve with control point (cpx, cpy), and must then add the given point (x, y) to the subpath<^>." - id: 2d.path.bezier.empty text: "The bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) method *must* do nothing if the context has no subpaths<^>." - id: 2d.path.bezier.nonempty text: "The bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) method <...> *must* connect the last point in the subpath to the given point (x, y) using a cubic B<...>zier curve with control points (cp1x, cp1y) and (cp2x, cp2y). Then, it must add the point (x, y) to the subpath<^>." - id: 2d.path.arcTo.empty text: "The arcTo(x1, y1, x2, y2, radius) method *must* do nothing if the context has no subpaths<^>." - id: 2d.path.arcTo.noarc text: "If the point (x2, y2) is on the line defined by the points (x0, y0) and (x1, y1) then the method *must* do nothing<^>" - id: 2d.path.arcTo.nonempty text: "the method *must* connect the point (x0, y0) to the start tangent point by a straight line, then connect the start tangent point to the end tangent point by The Arc, and finally add the start and end tangent points to the subpath<^>." - id: 2d.path.arcTo.nonpositive text: "Negative or zero values for radius *must* cause the implementation to raise an INDEX_SIZE_ERR exception<^>." previously: [ 4, "The arcTo(x1, y1, x2, y2, radius) method" ] - id: 2d.path.arc.nonempty text: "If the context has any subpaths, then the method *must* add a straight line from the last point in the subpath to the start point of the arc<^>." - id: 2d.path.arc.draw text: "it *must* draw the arc between the start point of the arc and the end point of the arc, and add the start and end points of the arc to the subpath<^>." - id: 2d.path.arc.nonpositive text: "Negative or zero values for radius *must* cause the implementation to raise an INDEX_SIZE_ERR exception<^>." previously: [ 2, "The arc(x, y, radius, startAngle, endAngle, anticlockwise) method" ] - id: 2d.path.rect.subpath text: "The rect(x, y, w, h) method *must* create a new subpath containing just the four points (x, y), (x+w, y), (x+w, y+h), (x, y+h), with those four points connected by straight lines<^>" - id: 2d.path.rect.closed text: "The rect(x, y, w, h) method <...> *must* then mark the subpath as closed<^>." - id: 2d.path.rect.newsubpath text: "The rect(x, y, w, h) method <...> *must* then create a new subpath with the point (x, y) as the only point in the subpath<^>." - id: 2d.path.rect.negative text: "Negative values for w and h *must* cause the implementation to raise an INDEX_SIZE_ERR exception<^>." - id: 2d.path.fill.basic text: "The fill() method *must* fill each subpath of the current path in turn, using fillStyle, and using the non-zero winding number rule<^>." - id: 2d.path.fill.closed text: "Open subpaths *must* be implicitly closed when being filled (without affecting the actual subpaths)<^>." - id: 2d.path.stroke.basic text: "The stroke() method *must* stroke each subpath of the current path in turn, using the strokeStyle, lineWidth, lineJoin, and (if appropriate) miterLimit attributes<^>." - id: 2d.path.unaffected text: "Paths, when filled or stroked, *must* be painted without affecting the current path<^>" - id: 2d.path.subjected text: "Paths, when filled or stroked, <...> *must* be subject to transformations, shadow effects, global alpha, clipping paths, and global composition operators<^>." - id: 2d.path.clip.basic text: "The clip() method *must* create a new clipping path by calculating the intersection of the current clipping path and the area described by the current path, using the non-zero winding number rule<^>." - id: 2d.path.clip.closed text: "Open subpaths *must* be implicitly closed when computing the clipping path, without affecting the actual subpaths<^>." - id: 2d.path.clip.initial text: "When the context is created, the initial clipping path is the rectangle with the top left corner at (0,0) and the width and height of the coordinate space<^>." - id: 2d.path.isPointInPath text: "The isPointInPath(x, y) method *must* return true if the point given by the x and y coordinates passed to the method, when treated as coordinates in the canvas' coordinate space unaffected by the current transformation, is within the area of the canvas that would be filled if the current path was to be filled; and must return false otherwise<^>." - id: 2d.drawImage.defaultdest text: "If not specified, the dw and dh arguments default to the values of sw and sh, interpreted such that one CSS pixel in the image is treated as one unit in the canvas coordinate space<^>." - id: 2d.drawImage.defaultsource text: "If the sx, sy, sw, and sh arguments are omitted, they default to 0, 0, the image's intrinsic width in image pixels, and the image's intrinsic height in image pixels, respectively<^>." - id: 2d.drawImage.type text: "If the image is of the wrong type, the implementation *must* raise a TYPE_MISMATCH_ERR exception<^>. If one of" - id: 2d.drawImage.outsidesource text: "If one of the sy, sw, sw, and sh arguments is outside the size of the image<...> the implementation *must* raise an INDEX_SIZE_ERR exception<^>." - id: 2d.drawImage.negativedest text: "if one of the dw and dh arguments is negative, the implementation *must* raise an INDEX_SIZE_ERR exception<^>." - id: 2d.drawImage.incomplete text: "an INDEX_SIZE_ERR exception. If the image argument is an HTMLImageElement object whose complete attribute is false, then the implementation *must* raise an INVALID_STATE_ERR exception<^>." - id: 2d.drawImage.paint text: "When drawImage() is invoked, the specified region of the image specified by the source rectangle (sx, sy, sw, sh) *must* be painted on the region of the canvas specified by the destination rectangle (dx, dy, dw, dh)<^>, after applying the current transformation matrix<^>." - id: 2d.drawImage.unaffect text: "Images are painted without affecting the current path<^>" - id: 2d.drawImage.subject text: "Images are painted without affecting the current path, and are subject to shadow effects, global alpha, clipping paths, and global composition operators<^>." - id: 2d.imageData.get.basic text: "The getImageData(sx, sy, sw, sh) method *must* return an ImageData object representing the underlying pixel data for the area of the canvas denoted by the rectangle which has its top left corner at the (sx, sy) coordinate, and that has width sw and height sh<^>." - id: 2d.imageData.get.outside text: "Pixels outside the canvas *must* be returned as transparent black<^>." - id: 2d.imageData.get.premul text: "Pixels *must* be returned as non-premultiplied alpha values<^>." - id: 2d.imageData.get.properties text: "ImageData objects *must* be initialised so that their width attribute is set to w, the number of physical device pixels per row in the image data, their height attribute is set to h, the number of rows in the image data, and the data attribute is initialised to an array of h<...>w<...>4 integers<^>." - id: 2d.imageData.get.pixelorder text: "ImageData objects *must* be initialised <...> The pixels must be represented in this array in left-to-right order, row by row, starting at the top left, with each pixel's red, green, blue, and alpha components being given in that order<^>." - id: 2d.imageData.get.range text: "Each component of each device pixel represented in this array *must* be in the range 0..255, representing the 8 bit value for that component<^>." - id: 2d.imageData.get.smallest text: "At least one pixel *must* be returned<^>." - id: 2d.imageData.get.nonpositive text: "If the getImageData(sx, sy, sw, sh) method is called with either the sw or sh arguments set to zero or negative values, the method *must* raise an INDEX_SIZE_ERR exception<^>." - id: 2d.imageData.put.basic text: "The putImageData(imagedata, dx, dy) method *must* take the given ImageData structure, and place it at the specified location (dx, dy) in the canvas coordinate space, mapping each pixel represented by the ImageData structure into one device pixel<^>." - id: 2d.imageData.put.type text: "If the first argument to the method is not an object whose [[Class]] property is ImageData, but all of the following conditions are true, then the method *must* treat the first argument as if it was an ImageData object (and thus not raise the TYPE_MISMATCH_ERR exception)<^>:" - id: 2d.imageData.unchanged text: "the following *must* result in no visible changes to the rendering<^>:" - id: 2d.imageData.transform text: "The current transformation matrix *must* not affect the getImageData() and putImageData() methods<^>." # - id: nonnegative.return # text: "This algorithm will either return zero, a positive integer, or an error<^>." # - id: nonnegative.leading # text: "Leading spaces are ignored<^>." # - id: nonnegative.trailing # text: "Trailing spaces and indeed any trailing garbage characters are ignored<^>."