+2007-11-16 Nikolas Zimmermann <zimmermann@kde.org>
+
+ Reviewed by Eric.
+
+ Update layout test results after the addition of the SVGTextContentElement DOM methods.
+
+ * platform/mac/svg/W3C-SVG-1.1/text-tselect-02-f-expected.checksum:
+ * platform/mac/svg/W3C-SVG-1.1/text-tselect-02-f-expected.png:
+ * platform/mac/svg/W3C-SVG-1.1/text-tselect-02-f-expected.txt:
+ * platform/mac/svg/custom/text-dom-01-f-expected.checksum: Added.
+ * platform/mac/svg/custom/text-dom-01-f-expected.png: Added.
+ * platform/mac/svg/custom/text-dom-01-f-expected.txt: Added.
+ * svg/custom/text-dom-01-f.svg: Added.
+ * svg/text/text-tselect-02-f-expected.checksum:
+ * svg/text/text-tselect-02-f-expected.png:
+ * svg/text/text-tselect-02-f-expected.txt:
+
2007-11-15 Adele Peterson <adele@apple.com>
Reviewed by Oliver.
-54716022e8bb30d6886c54485bcf5a88
\ No newline at end of file
+0cdd7f9f52ec5ee59a1b84ef447fe47e
\ No newline at end of file
RenderSVGInlineText {#text} at (0,-36) size 264x46
chunk 1 text run 1 at (10.00,340.00) startOffset 0 endOffset 16 width 264.00: "$Revision: 1.2 $"
RenderPath {rect} at (0.50,0.50) size 479x359 [stroke={[type=SOLID] [color=#000000]}] [data="M1.00,1.00L479.00,1.00L479.00,359.00L1.00,359.00"]
+selection start: position 3 of child 0 {#text} of child 3 {text} of child 3 {g} of child 35 {g} of child 0 {svg} of document
+selection end: position 12 of child 0 {#text} of child 3 {text} of child 3 {g} of child 35 {g} of child 0 {svg} of document
--- /dev/null
+536a2457354cfd76e533641d0063cfea
\ No newline at end of file
--- /dev/null
+layer at (0,0) size 800x600
+ RenderView at (0,0) size 800x600
+layer at (0,0) size 480x360
+ RenderSVGRoot {svg} at (0.50,0.50) size 479x359
+ RenderSVGContainer {g} at (30,15) size 405x268
+ RenderSVGContainer {g} at (30,15) size 405x268
+ RenderPath {rect} at (125,16) size 8x17 [fill={[type=SOLID] [color=#ADD8E6]}] [data="M125.00,16.00L133.00,16.00L133.00,33.00L125.00,33.00"]
+ RenderPath {rect} at (91.88,20.10) size 14.14x14.14 [fill={[type=SOLID] [color=#ADD8E6]}] [data="M91.88,20.10L106.02,20.10L106.02,34.24L91.88,34.24"]
+ RenderPath {line} at (62,29.50) size 356x1 [stroke={[type=SOLID] [color=#FF0000]}] [fill={[type=SOLID] [color=#000000]}] [data="M62.00,30.00L418.00,30.00"]
+ RenderPath {line} at (132.50,15) size 1x15 [stroke={[type=SOLID] [color=#FF0000]}] [fill={[type=SOLID] [color=#000000]}] [data="M133.00,30.00L133.00,15.00"]
+ RenderPath {line} at (124.50,15) size 1x15 [stroke={[type=SOLID] [color=#FF0000]}] [fill={[type=SOLID] [color=#000000]}] [data="M125.00,30.00L125.00,15.00"]
+ RenderPath {line} at (189,29.50) size 60x1 [stroke={[type=SOLID] [color=#00FF00]}] [fill={[type=SOLID] [color=#000000]}] [data="M189.00,30.00L249.00,30.00"]
+ RenderSVGText {text} at (62,30) size 356x22 contains 1 chunk(s)
+ RenderSVGInlineText {#text} at (0,-14) size 32x17
+ chunk 1 (middle anchor) text run 1 at (62.00,30.00) startOffset 0 endOffset 5 width 32.00: "This "
+ RenderSVGTSpan {tspan} at (0,0) size 20x18
+ RenderSVGInlineText {#text} at (29,-10) size 20x18
+ chunk 1 (middle anchor) text run 2 at (94.00,30.00) startOffset 0 endOffset 2 width 11.00: "is"
+ RenderSVGInlineText {#text} at (43,-14) size 313x17
+ chunk 1 (middle anchor) text run 3 at (105.00,30.00) startOffset 0 endOffset 47 width 313.00: " a test of the interface SVGTextContentElement."
+ RenderSVGText {text} at (30,60) size 227x17 contains 1 chunk(s)
+ RenderSVGInlineText {#text} at (0,-14) size 227x17
+ chunk 1 text run 1 at (30.00,60.00) startOffset 0 endOffset 34 width 227.00: ".getCharNumAtPosition() result: 30"
+ RenderSVGText {text} at (30,80) size 246x17 contains 1 chunk(s)
+ RenderSVGInlineText {#text} at (0,-14) size 246x17
+ chunk 1 text run 1 at (30.00,80.00) startOffset 0 endOffset 36 width 246.00: ".getComputedTextLength() result: 356"
+ RenderSVGText {text} at (30,100) size 296x17 contains 1 chunk(s)
+ RenderSVGInlineText {#text} at (0,-14) size 296x17
+ chunk 1 text run 1 at (30.00,100.00) startOffset 0 endOffset 46 width 296.00: ".getEndPositionOfChar(11) result ('e'): 133,30"
+ RenderSVGText {text} at (30,120) size 292x17 contains 1 chunk(s)
+ RenderSVGInlineText {#text} at (0,-14) size 292x17
+ chunk 1 text run 1 at (30.00,120.00) startOffset 0 endOffset 46 width 292.00: ".getExtentOfChar(11) result ('e'): 125,16,8,17"
+ RenderSVGText {text} at (30,140) size 206x17 contains 1 chunk(s)
+ RenderSVGInlineText {#text} at (0,-14) size 206x17
+ chunk 1 text run 1 at (30.00,140.00) startOffset 0 endOffset 30 width 206.00: ".getNumberOfChars() result: 54"
+ RenderSVGText {text} at (30,160) size 208x17 contains 1 chunk(s)
+ RenderSVGInlineText {#text} at (0,-14) size 208x17
+ chunk 1 text run 1 at (30.00,160.00) startOffset 0 endOffset 32 width 208.00: ".getRotationOfChar(5) result: 45"
+ RenderSVGText {text} at (30,180) size 301x17 contains 1 chunk(s)
+ RenderSVGInlineText {#text} at (0,-14) size 301x17
+ chunk 1 text run 1 at (30.00,180.00) startOffset 0 endOffset 48 width 301.00: ".getStartPositionOfChar(11) result ('e'): 125,30"
+ RenderSVGText {text} at (30,200) size 310x17 contains 1 chunk(s)
+ RenderSVGInlineText {#text} at (0,-14) size 310x17
+ chunk 1 text run 1 at (30.00,200.00) startOffset 0 endOffset 50 width 310.00: ".getSubStringLength(22,9) result ('interface'): 60"
+ RenderSVGText {text} at (30,220) size 405x17 contains 1 chunk(s)
+ RenderSVGInlineText {#text} at (0,-14) size 405x17
+ chunk 1 text run 1 at (30.00,220.00) startOffset 0 endOffset 64 width 405.00: ".selectSubString(18,3) result: the word 'the' should be selected"
+ RenderSVGText {text} at (30,240) size 224x17 contains 1 chunk(s)
+ RenderSVGInlineText {#text} at (0,-14) size 224x17
+ chunk 1 text run 1 at (30.00,240.00) startOffset 0 endOffset 35 width 224.00: ".textLength.baseVal.value result: 0"
+ RenderSVGText {text} at (30,260) size 223x17 contains 1 chunk(s)
+ RenderSVGInlineText {#text} at (0,-14) size 223x17
+ chunk 1 text run 1 at (30.00,260.00) startOffset 0 endOffset 35 width 223.00: ".textLength.animVal.value result: 0"
+ RenderSVGText {text} at (30,280) size 381x17 contains 1 chunk(s)
+ RenderSVGInlineText {#text} at (0,-14) size 381x17
+ chunk 1 text run 1 at (30.00,280.00) startOffset 0 endOffset 59 width 381.00: ".lengthAdjust.baseVal and .lengthAdjust.animVal result: 1,1"
+ RenderSVGText {text} at (10,340) size 264x46 contains 1 chunk(s)
+ RenderSVGInlineText {#text} at (0,-36) size 264x46
+ chunk 1 text run 1 at (10.00,340.00) startOffset 0 endOffset 16 width 264.00: "$Revision: 1.1 $"
+ RenderPath {rect} at (0.50,0.50) size 479x359 [stroke={[type=SOLID] [color=#000000]}] [data="M1.00,1.00L479.00,1.00L479.00,359.00L1.00,359.00"]
+selection start: position 11 of child 2 {#text} of child 7 {text} of child 3 {g} of child 35 {g} of child 0 {svg} of document
+selection end: position 14 of child 2 {#text} of child 7 {text} of child 3 {g} of child 35 {g} of child 0 {svg} of document
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Full//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-flat.dtd">
+<svg version="1.1" baseProfile="full" id="svg-root" width="480" height="360" viewBox="0 0 480 360" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="testSVGTextContentElement()">
+ <!--========================================================================-->
+ <!--= Copyright 2006 World Wide Web Consortium, (Massachusetts =-->
+ <!--= Institute of Technology, European Research Consortium for =-->
+ <!--= Informatics and Mathematics (ERCIM), Keio University). =-->
+ <!--= All Rights Reserved. =-->
+ <!--= See http://www.w3.org/Consortium/Legal/. =-->
+ <!--========================================================================-->
+ <!-- NOTE: CVS will automatically update the -->
+ <!-- "$RCSfile: text-dom-01-f.svg,v $" and "$Revision: 1.1 $" -->
+ <!-- fields in the file. -->
+ <!-- There is no need to update this information. -->
+ <!-- =====================================================================-->
+ <SVGTestCase xmlns="http://www.w3.org/2000/02/svg/testsuite/description/" reviewer="[reviewer]" owner="AN" desc="Tests the interface SVGTextContentElement" status="created" version="$Revision: 1.1 $" testname="$RCSfile: text-dom-01-f.svg,v $">
+ <OperatorScript>
+ <Paragraph>
+ This tests the methods and properties of the SVGTextContentElement interface on the text element with the id 'testText'
+ and the content 'This is a test of the interface SVGTextContentElement'. The word 'is' has two glyphs with different
+ rotation values defined with a <tspan/> element. There are 12 subtests testing the 9 methods and 2 properties.
+ Note that the numeric results of some methods may vary. The additional instructions state where the result may vary
+ and where it should have an exact value.
+ </Paragraph>
+ <Paragraph>
+ The first subtest is testing the method .getCharNumAtPosition(svgPt), where svgPt has an x value of 240 and y value of 25.
+ The result of this subtest must be "30".
+ </Paragraph>
+ <Paragraph>
+ The second subtest is testing the method .getComputedTextLength(). The rounded result may vary in the implementations but should be around 364.
+ A red line below the testText is visually indicating the result of the method .getComputedTextLength() and must look like a red underline
+ with a length that spans the whole text length from 'T' to '.'.
+ </Paragraph>
+ <Paragraph>
+ The third subtest is testing the method .getEndPositionOfChar() at the 11th character ('e').
+ The rounded result may vary in the implementations but should be around 131 for the 'x' value and must be 30 for the 'y' value.
+ Additionally, a red vertical line is indicating the end position of the character 'e'. Its lower 'y' value must be at 30
+ and the 'x' values must match the end position of the 11th character 'e'.
+ </Paragraph>
+ <Paragraph>
+ The fourth subtest is testing the method .getExtentOfChar() at the 11th character ('e').
+ The rounded result may vary in the implementations but should be around '123,16,8,17' for the 'x,y,width,height' values.
+ A lightblue rectangle below the character 'e' must fully enclose the 11th glyph.
+ </Paragraph>
+ <Paragraph>
+ The fifth subtest is testing the method .getNumberOfChars(). The result must be 54.
+ </Paragraph>
+ <Paragraph>
+ The sixth subtest is testing the method .getRotationOfChar() for the fifth character. The result must be 45.
+ Additionally, a lightblue rectangle below the text indicates the extent of the fifth glyph 'i'.
+ It must fully enclose the diagonally rotated fifth glyph 'i'.
+ </Paragraph>
+ <Paragraph>
+ The seventh subtest is testing the method .getStartPositionOfChar() at the 11th character ('e').
+ The rounded result may vary in the implementations but should be around 123 for the 'x' value and must be 30 for the 'y' value.
+ Additionally, a red vertical line is indicating the start position of the character 'e'. Its lower 'y' value must be at 30
+ and the 'x' values must match the end position of the 11th character 'e'.
+ </Paragraph>
+ <Paragraph>
+ The eighth subtest is testing the method .getSubStringLength(), starting at character 22 and including the 9 following characters.
+ The result may vary in the implementations but should be around 58. Additionally, a green (lime) line visually indicates
+ the result of the method. The word 'interface' must be fully underlined with the green line.
+ </Paragraph>
+ <Paragraph>
+ The ninth subtest is testing the method .selectSubString(). After loading the file, the word "the" must be selected.
+ </Paragraph>
+ <Paragraph>
+ The tenth subtest is testing the property .textLength. The rounded result of .textLength.baseVal.value may vary in
+ the implementations but should be around 364.
+ It must match the value calculated in the second subtest (.getComputedTextLength()).
+ </Paragraph>
+ <Paragraph>
+ The eleventh subtest is again testing the property .textLength. The rounded result of .textLength.animVal.value may vary in
+ the implementations but should be around 364.
+ It must match the value calculated in the second subtest (.getComputedTextLength()).
+ </Paragraph>
+ <Paragraph>
+ The twelfth subtest is again testing the property .lengthAdjust. The results of .lengthAdjust.baseVal and
+ .lengthAdjust.animVal must be 1 and 1.
+ </Paragraph>
+ </OperatorScript>
+ </SVGTestCase>
+ <title id="test-title">$RCSfile: text-dom-01-f.svg,v $</title>
+ <!--======================================================================-->
+ <!--Content of Test Case follows... =====================-->
+ <!--======================================================================-->
+ <g id="test-body-content">
+ <script type="text/ecmascript"><![CDATA[
+ function testSVGTextContentElement() {
+ var svgNS = "http://www.w3.org/2000/svg";
+ var tContentEl = document.getElementById("testText");
+ var textGroup = document.getElementById("textGroup");
+ var svgPt = document.documentElement.createSVGPoint();
+ svgPt.x = 240;
+ svgPt.y = 25;
+
+ //1: testing .getCharNumAtPosition()
+ var charNumresult = tContentEl.getCharNumAtPosition(svgPt);
+ document.getElementById("text1").firstChild.data = ".getCharNumAtPosition() result: "+charNumresult;
+
+ //2: testing .getCharNumAtPosition()
+ var compTextLength = tContentEl.getComputedTextLength();
+ document.getElementById("text2").firstChild.data = ".getComputedTextLength() result: "+Math.round(compTextLength);
+ var baseLine = document.createElementNS(svgNS,"line");
+ baseLine.setAttributeNS(null,"stroke","red");
+ baseLine.setAttributeNS(null,"x1",(240 - compTextLength * 0.5));
+ baseLine.setAttributeNS(null,"x2",(240 + compTextLength * 0.5));
+ baseLine.setAttributeNS(null,"y1",30);
+ baseLine.setAttributeNS(null,"y2",30);
+ textGroup.insertBefore(baseLine,tContentEl);
+
+ //3: testing .getEndPositionOfChar(), end of character
+ var endPosChar = tContentEl.getEndPositionOfChar(11);
+ document.getElementById("text3").firstChild.data = ".getEndPositionOfChar(11) result ('e'): "+Math.round(endPosChar.x)+","+Math.round(endPosChar.y);
+ var endPosLine = document.createElementNS(svgNS,"line");
+ endPosLine.setAttributeNS(null,"stroke","red");
+ endPosLine.setAttributeNS(null,"x1",endPosChar.x);
+ endPosLine.setAttributeNS(null,"x2",endPosChar.x);
+ endPosLine.setAttributeNS(null,"y1",endPosChar.y);
+ endPosLine.setAttributeNS(null,"y2",endPosChar.y-15);
+ textGroup.insertBefore(endPosLine,tContentEl);
+
+ //4: testing getExtentOfChar
+ var charExtent = tContentEl.getExtentOfChar(11);
+ document.getElementById("text4").firstChild.data = ".getExtentOfChar(11) result ('e'): "+Math.round(charExtent.x)+","+Math.round(charExtent.y)+","+Math.round(charExtent.width)+","+Math.round(charExtent.height);
+ var extentRect = document.createElementNS(svgNS,"rect");
+ extentRect.setAttributeNS(null,"fill","lightblue");
+ extentRect.setAttributeNS(null,"x",charExtent.x);
+ extentRect.setAttributeNS(null,"y",charExtent.y);
+ extentRect.setAttributeNS(null,"width",charExtent.width);
+ extentRect.setAttributeNS(null,"height",charExtent.height);
+ textGroup.insertBefore(extentRect,baseLine);
+
+ //5: testing getNumberOfChars
+ var numChars = tContentEl.getNumberOfChars();
+ document.getElementById("text5").firstChild.data = ".getNumberOfChars() result: "+numChars;
+
+ //6: testing getRotationOfChar
+ var charRot = tContentEl.getRotationOfChar(5);
+ document.getElementById("text6").firstChild.data = ".getRotationOfChar(5) result: "+charRot;
+ var rotCharExtent = tContentEl.getExtentOfChar(5);
+ var rotExtentRect = document.createElementNS(svgNS,"rect");
+ rotExtentRect.setAttributeNS(null,"fill","lightblue");
+ rotExtentRect.setAttributeNS(null,"x",rotCharExtent.x);
+ rotExtentRect.setAttributeNS(null,"y",rotCharExtent.y);
+ rotExtentRect.setAttributeNS(null,"width",rotCharExtent.width);
+ rotExtentRect.setAttributeNS(null,"height",rotCharExtent.height);
+ textGroup.insertBefore(rotExtentRect,baseLine);
+
+ //7: testing .getStartPositionOfChar(), end of character
+ var startPosChar = tContentEl.getStartPositionOfChar(11);
+ document.getElementById("text7").firstChild.data = ".getStartPositionOfChar(11) result ('e'): "+Math.round(startPosChar.x)+","+Math.round(startPosChar.y);
+ var startPosLine = document.createElementNS(svgNS,"line");
+ startPosLine.setAttributeNS(null,"stroke","red");
+ startPosLine.setAttributeNS(null,"x1",startPosChar.x);
+ startPosLine.setAttributeNS(null,"x2",startPosChar.x);
+ startPosLine.setAttributeNS(null,"y1",startPosChar.y);
+ startPosLine.setAttributeNS(null,"y2",startPosChar.y-15);
+ textGroup.insertBefore(startPosLine,tContentEl);
+
+ //8: testing .getSubStringLength()
+ var startPosInterface = tContentEl.getStartPositionOfChar(22);
+ var subStrLength = tContentEl.getSubStringLength(22,9);
+ document.getElementById("text8").firstChild.data = ".getSubStringLength(22,9) result ('interface'): "+Math.round(subStrLength);
+ var subStrLine = document.createElementNS(svgNS,"line");
+ subStrLine.setAttributeNS(null,"stroke","lime");
+ subStrLine.setAttributeNS(null,"x1",startPosInterface.x);
+ subStrLine.setAttributeNS(null,"x2",(startPosInterface.x+subStrLength));
+ subStrLine.setAttributeNS(null,"y1",startPosInterface.y);
+ subStrLine.setAttributeNS(null,"y2",startPosInterface.y);
+ textGroup.insertBefore(subStrLine,tContentEl);
+
+ //9: testing .selectSubString()
+ tContentEl.selectSubString(18,3);
+
+ //10: testing textLength.baseVal.value
+ var tlbaseval = tContentEl.textLength.baseVal.value;
+ document.getElementById("text10").firstChild.data = ".textLength.baseVal.value result: "+Math.round(tlbaseval);
+
+ //11: testing textLength.baseVal.value
+ var tlanimval = tContentEl.textLength.animVal.value;
+ document.getElementById("text11").firstChild.data = ".textLength.animVal.value result: "+Math.round(tlanimval);
+
+ //12: testing lengthAdjust baseVal and animVal
+ document.getElementById("text12").firstChild.data = ".lengthAdjust.baseVal and .lengthAdjust.animVal result: "+tContentEl.lengthAdjust.baseVal+","+tContentEl.lengthAdjust.animVal;;
+ }
+ ]]></script>
+ <g id="textGroup" font-family="Arial" font-size="15">
+ <text x="240" y="30" text-anchor="middle" id="testText">This <tspan rotate="45,90">is</tspan> a test of the interface SVGTextContentElement.</text>
+ <text id="text1" x="30" y="60">.getCharNumAtPosition() result: </text>
+ <text id="text2" x="30" y="80">.getComputedTextLength() result: </text>
+ <text id="text3" x="30" y="100">.getEndPositionOfChar(11) result ('e'): </text>
+ <text id="text4" x="30" y="120">.getExtentOfChar(11) result ('e'): </text>
+ <text id="text5" x="30" y="140">.getNumberOfChars() result: </text>
+ <text id="text6" x="30" y="160">.getRotationOfChar(5) result: </text>
+ <text id="text7" x="30" y="180">.getStartPositionOfChar(11) result: </text>
+ <text id="text8" x="30" y="200">.getSubStringLength(22,9) result ('interface'): </text>
+ <text id="text9" x="30" y="220">.selectSubString(18,3) result: the word 'the' should be selected</text>
+ <text id="text10" x="30" y="240">.textLength.baseVal.value result:</text>
+ <text id="text11" x="30" y="260">.textLength.animVal.value result:</text>
+ <text id="text12" x="30" y="280">.lengthAdjust.baseVal and .lengthAdjust.animVal result:</text>
+ </g>
+ </g>
+ <text id="revision" x="10" y="340" font-size="40" stroke="none" fill="black">$Revision: 1.1 $</text>
+ <rect id="test-frame" x="1" y="1" width="478" height="358" fill="none" stroke="#000"/>
+</svg>
-8134633414eba4f7ed52a725056b17a3
\ No newline at end of file
+2b667bf1c69194fb6f0ca5894e059a07
\ No newline at end of file
RenderSVGInlineText {#text} at (0,-36) size 264x46
chunk 1 text run 1 at (10.00,340.00) startOffset 0 endOffset 16 width 264.00: "$Revision: 1.2 $"
RenderPath {rect} at (0.83,0.83) size 798.33x598.33 [stroke={[type=SOLID] [color=#000000]}] [data="M1.00,1.00L479.00,1.00L479.00,359.00L1.00,359.00"]
-selection start: position 0 of child 0 {#text} of child 1 {text} of child 3 {g} of child 35 {g} of child 0 {svg} of document
-selection end: position 16 of child 0 {#text} of child 37 {text} of child 0 {svg} of document
+selection start: position 3 of child 0 {#text} of child 3 {text} of child 3 {g} of child 35 {g} of child 0 {svg} of document
+selection end: position 12 of child 0 {#text} of child 3 {text} of child 3 {g} of child 35 {g} of child 0 {svg} of document
+2007-11-16 Nikolas Zimmermann <zimmermann@kde.org>
+
+ Reviewed by Eric.
+
+ Fixes: http://bugs.webkit.org/show_bug.cgi?id=12290
+
+ Implement all SVGTextContentElement DOM methods.
+ This is the last missing SVG text feature. SVG fonts is next.
+
+ Added testcase: svg/custom/text-dom-01-f.svg (tests all new methods)
+
+ * ksvg2/svg/SVGTextContentElement.cpp:
+ (WebCore::cummulatedCharacterRangeLength):
+ (WebCore::SVGInlineTextBoxQueryWalker::):
+ (WebCore::SVGInlineTextBoxQueryWalker::SVGInlineTextBoxQueryWalker):
+ (WebCore::SVGInlineTextBoxQueryWalker::chunkPortionCallback):
+ (WebCore::SVGInlineTextBoxQueryWalker::setQueryInputParameters):
+ (WebCore::SVGInlineTextBoxQueryWalker::longResult):
+ (WebCore::SVGInlineTextBoxQueryWalker::floatResult):
+ (WebCore::SVGInlineTextBoxQueryWalker::pointResult):
+ (WebCore::SVGInlineTextBoxQueryWalker::rectResult):
+ (WebCore::SVGInlineTextBoxQueryWalker::stopProcessing):
+ (WebCore::findInlineTextBoxInTextChunks):
+ (WebCore::rootInlineBoxForTextContentElement):
+ (WebCore::executeTextQuery):
+ (WebCore::SVGTextContentElement::getNumberOfChars):
+ (WebCore::SVGTextContentElement::getComputedTextLength):
+ (WebCore::SVGTextContentElement::getSubStringLength):
+ (WebCore::SVGTextContentElement::getStartPositionOfChar):
+ (WebCore::SVGTextContentElement::getEndPositionOfChar):
+ (WebCore::SVGTextContentElement::getExtentOfChar):
+ (WebCore::SVGTextContentElement::getRotationOfChar):
+ (WebCore::SVGTextContentElement::getCharNumAtPosition):
+ (WebCore::SVGTextContentElement::selectSubString):
+ * ksvg2/svg/SVGTextContentElement.h:
+ * rendering/SVGInlineTextBox.h:
+
2007-11-15 Adele Peterson <adele@apple.com>
Reviewed by Oliver.
#include "CSSPropertyNames.h"
#include "CSSValueKeywords.h"
+#include "ExceptionCode.h"
#include "FloatPoint.h"
#include "FloatRect.h"
+#include "Frame.h"
+#include "Position.h"
+#include "RenderSVGText.h"
+#include "SelectionController.h"
+#include "SVGCharacterLayoutInfo.h"
+#include "SVGRootInlineBox.h"
#include "SVGLength.h"
+#include "SVGInlineTextBox.h"
#include "SVGNames.h"
#include "XMLNames.h"
ANIMATED_PROPERTY_DEFINITIONS(SVGTextContentElement, SVGLength, Length, length, TextLength, textLength, SVGNames::textLengthAttr.localName(), m_textLength)
ANIMATED_PROPERTY_DEFINITIONS(SVGTextContentElement, int, Enumeration, enumeration, LengthAdjust, lengthAdjust, SVGNames::lengthAdjustAttr.localName(), m_lengthAdjust)
+static inline float cummulatedCharacterRangeLength(const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end, SVGInlineTextBox* textBox,
+ int startOffset, long startPosition, long length, bool isVerticalText, long& atCharacter)
+{
+ float textLength = 0.0f;
+ RenderStyle* style = textBox->textObject()->style();
+
+ bool usesFullRange = (startPosition == -1 && length == -1);
+
+ for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
+ if (usesFullRange || (atCharacter >= startPosition && atCharacter <= startPosition + length)) {
+ unsigned int newOffset = textBox->start() + (it - start) + startOffset;
+
+ // Take RTL text into account and pick right glyph width/height.
+ if (textBox->m_reversed)
+ newOffset = textBox->start() + textBox->end() - newOffset;
+
+ if (isVerticalText)
+ textLength += textBox->calculateGlyphHeight(style, newOffset);
+ else
+ textLength += textBox->calculateGlyphWidth(style, newOffset);
+ }
+
+ if (!usesFullRange) {
+ if (atCharacter < startPosition + length)
+ atCharacter++;
+ else if (atCharacter == startPosition + length)
+ break;
+ }
+ }
+
+ return textLength;
+}
+
+// Helper class for querying certain glyph information
+struct SVGInlineTextBoxQueryWalker {
+ typedef enum {
+ NumberOfCharacters,
+ TextLength,
+ SubStringLength,
+ StartPosition,
+ EndPosition,
+ Extent,
+ Rotation,
+ CharacterNumberAtPosition
+ } QueryMode;
+
+ SVGInlineTextBoxQueryWalker(const SVGTextContentElement* reference, QueryMode mode)
+ : m_reference(reference)
+ , m_mode(mode)
+ , m_queryStartPosition(0)
+ , m_queryLength(0)
+ , m_queryPointInput()
+ , m_queryLongResult(0)
+ , m_queryFloatResult(0.0f)
+ , m_queryPointResult()
+ , m_queryRectResult()
+ , m_stopProcessing(true)
+ , m_atCharacter(0)
+ {
+ }
+
+ void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const AffineTransform& chunkCtm,
+ const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end)
+ {
+ RenderStyle* style = textBox->textObject()->style();
+ bool isVerticalText = style->svgStyle()->writingMode() == WM_TBRL || style->svgStyle()->writingMode() == WM_TB;
+
+ switch (m_mode) {
+ case NumberOfCharacters:
+ {
+ m_queryLongResult += (end - start);
+ m_stopProcessing = false;
+ return;
+ }
+ case TextLength:
+ {
+ float textLength = cummulatedCharacterRangeLength(start, end, textBox, startOffset, -1, -1, isVerticalText, m_atCharacter);
+
+ if (isVerticalText)
+ m_queryFloatResult += textLength;
+ else
+ m_queryFloatResult += textLength;
+
+ m_stopProcessing = false;
+ return;
+ }
+ case SubStringLength:
+ {
+ long startPosition = m_queryStartPosition;
+ long length = m_queryLength;
+
+ float textLength = cummulatedCharacterRangeLength(start, end, textBox, startOffset, startPosition, length, isVerticalText, m_atCharacter);
+
+ if (isVerticalText)
+ m_queryFloatResult += textLength;
+ else
+ m_queryFloatResult += textLength;
+
+ if (m_atCharacter == startPosition + length)
+ m_stopProcessing = true;
+ else
+ m_stopProcessing = false;
+
+ return;
+ }
+ case StartPosition:
+ {
+ for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
+ if (m_atCharacter == m_queryStartPosition) {
+ m_queryPointResult = FloatPoint(it->x, it->y);
+ m_stopProcessing = true;
+ return;
+ }
+
+ m_atCharacter++;
+ }
+
+ m_stopProcessing = false;
+ return;
+ }
+ case EndPosition:
+ {
+ for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
+ if (m_atCharacter == m_queryStartPosition) {
+ unsigned int newOffset = textBox->start() + (it - start) + startOffset;
+
+ // Take RTL text into account and pick right glyph width/height.
+ if (textBox->m_reversed)
+ newOffset = textBox->start() + textBox->end() - newOffset;
+
+ if (isVerticalText)
+ m_queryPointResult.move(it->x, it->y + textBox->calculateGlyphHeight(style, newOffset));
+ else
+ m_queryPointResult.move(it->x + textBox->calculateGlyphWidth(style, newOffset), it->y);
+
+ m_stopProcessing = true;
+ return;
+ }
+
+ m_atCharacter++;
+ }
+
+ m_stopProcessing = false;
+ return;
+ }
+ case Extent:
+ {
+ for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
+ if (m_atCharacter == m_queryStartPosition) {
+ unsigned int newOffset = textBox->start() + (it - start) + startOffset;
+ m_queryRectResult = textBox->calculateGlyphBoundaries(style, newOffset, *it);
+ m_stopProcessing = true;
+ return;
+ }
+
+ m_atCharacter++;
+ }
+
+ m_stopProcessing = false;
+ return;
+ }
+ case Rotation:
+ {
+ for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
+ if (m_atCharacter == m_queryStartPosition) {
+ m_queryFloatResult = it->angle;
+ m_stopProcessing = true;
+ return;
+ }
+
+ m_atCharacter++;
+ }
+
+ m_stopProcessing = false;
+ return;
+ }
+ case CharacterNumberAtPosition:
+ {
+ int offset = 0;
+ SVGChar* charAtPos = textBox->closestCharacterToPosition(m_queryPointInput.x(), m_queryPointInput.y(), offset);
+
+ offset += m_atCharacter;
+ if (charAtPos && offset > m_queryLongResult)
+ m_queryLongResult = offset;
+
+ m_atCharacter += end - start;
+ m_stopProcessing = false;
+ return;
+ }
+ default:
+ ASSERT_NOT_REACHED();
+ m_stopProcessing = true;
+ return;
+ }
+ }
+
+ void setQueryInputParameters(long startPosition, long length, FloatPoint referencePoint)
+ {
+ m_queryStartPosition = startPosition;
+ m_queryLength = length;
+ m_queryPointInput = referencePoint;
+ }
+
+ long longResult() const { return m_queryLongResult; }
+ float floatResult() const { return m_queryFloatResult; }
+ FloatPoint pointResult() const { return m_queryPointResult; }
+ FloatRect rectResult() const { return m_queryRectResult; }
+ bool stopProcessing() const { return m_stopProcessing; }
+
+private:
+ const SVGTextContentElement* m_reference;
+ QueryMode m_mode;
+
+ long m_queryStartPosition;
+ long m_queryLength;
+ FloatPoint m_queryPointInput;
+
+ long m_queryLongResult;
+ float m_queryFloatResult;
+ FloatPoint m_queryPointResult;
+ FloatRect m_queryRectResult;
+
+ bool m_stopProcessing;
+ long m_atCharacter;
+};
+
+static Vector<SVGInlineTextBox*> findInlineTextBoxInTextChunks(const SVGTextContentElement* element, const Vector<SVGTextChunk>& chunks)
+{
+ Vector<SVGTextChunk>::const_iterator it = chunks.begin();
+ const Vector<SVGTextChunk>::const_iterator end = chunks.end();
+
+ Vector<SVGInlineTextBox*> boxes;
+
+ for (; it != end; ++it) {
+ Vector<SVGInlineBoxCharacterRange>::const_iterator boxIt = it->boxes.begin();
+ const Vector<SVGInlineBoxCharacterRange>::const_iterator boxEnd = it->boxes.end();
+
+ for (; boxIt != boxEnd; ++boxIt) {
+ SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(boxIt->box);
+
+ Node* textElement = textBox->textObject()->parent()->element();
+ ASSERT(textElement);
+
+ if (textElement == element || textElement->parent() == element)
+ boxes.append(textBox);
+ }
+ }
+
+ return boxes;
+}
+
+static inline SVGRootInlineBox* rootInlineBoxForTextContentElement(const SVGTextContentElement* element)
+{
+ RenderObject* object = element->renderer();
+ ASSERT(object);
+
+ if (!object->isSVGText() || object->isText())
+ return 0;
+
+ RenderSVGText* svgText = static_cast<RenderSVGText*>(object);
+
+ // Find root inline box
+ SVGRootInlineBox* rootBox = static_cast<SVGRootInlineBox*>(svgText->firstRootBox());
+ if (!rootBox) {
+ // Layout is not sync yet!
+ element->document()->updateLayoutIgnorePendingStylesheets();
+ rootBox = static_cast<SVGRootInlineBox*>(svgText->firstRootBox());
+ }
+
+ ASSERT(rootBox);
+ return rootBox;
+}
+
+static inline SVGInlineTextBoxQueryWalker executeTextQuery(const SVGTextContentElement* element, SVGInlineTextBoxQueryWalker::QueryMode mode,
+ long startPosition = 0, long length = 0, FloatPoint referencePoint = FloatPoint())
+{
+ SVGRootInlineBox* rootBox = rootInlineBoxForTextContentElement(element);
+ if (!rootBox)
+ return SVGInlineTextBoxQueryWalker(0, mode);
+
+ // Find all inline text box associated with our renderer
+ Vector<SVGInlineTextBox*> textBoxes = findInlineTextBoxInTextChunks(element, rootBox->svgTextChunks());
+
+ // Walk text chunks to find chunks associated with our inline text box
+ SVGInlineTextBoxQueryWalker walkerCallback(element, mode);
+ walkerCallback.setQueryInputParameters(startPosition, length, referencePoint);
+
+ SVGTextChunkWalker<SVGInlineTextBoxQueryWalker> walker(&walkerCallback, &SVGInlineTextBoxQueryWalker::chunkPortionCallback);
+
+ Vector<SVGInlineTextBox*>::iterator it = textBoxes.begin();
+ Vector<SVGInlineTextBox*>::iterator end = textBoxes.end();
+
+ for (; it != end; ++it) {
+ rootBox->walkTextChunks(&walker, *it);
+
+ if (walkerCallback.stopProcessing())
+ break;
+ }
+
+ return walkerCallback;
+}
+
long SVGTextContentElement::getNumberOfChars() const
{
- return 0;
+ return executeTextQuery(this, SVGInlineTextBoxQueryWalker::NumberOfCharacters).longResult();
}
float SVGTextContentElement::getComputedTextLength() const
{
- return 0.0f;
+ return executeTextQuery(this, SVGInlineTextBoxQueryWalker::TextLength).floatResult();
}
-float SVGTextContentElement::getSubStringLength(unsigned long charnum, unsigned long nchars, ExceptionCode&) const
+float SVGTextContentElement::getSubStringLength(long charnum, unsigned long nchars, ExceptionCode& ec) const
{
- return 0.0f;
+ if (charnum < 0 || charnum > getNumberOfChars()) {
+ ec = INDEX_SIZE_ERR;
+ return 0.0f;
+ }
+
+ return executeTextQuery(this, SVGInlineTextBoxQueryWalker::SubStringLength, charnum, nchars).floatResult();
}
-FloatPoint SVGTextContentElement::getStartPositionOfChar(unsigned long charnum, ExceptionCode&) const
+FloatPoint SVGTextContentElement::getStartPositionOfChar(long charnum, ExceptionCode& ec) const
{
- return FloatPoint();
+ if (charnum < 0 || charnum > getNumberOfChars()) {
+ ec = INDEX_SIZE_ERR;
+ return FloatPoint();
+ }
+
+ return executeTextQuery(this, SVGInlineTextBoxQueryWalker::StartPosition, charnum).pointResult();
}
-FloatPoint SVGTextContentElement::getEndPositionOfChar(unsigned long charnum, ExceptionCode&) const
+FloatPoint SVGTextContentElement::getEndPositionOfChar(long charnum, ExceptionCode& ec) const
{
- return FloatPoint();
+ if (charnum < 0 || charnum > getNumberOfChars()) {
+ ec = INDEX_SIZE_ERR;
+ return FloatPoint();
+ }
+
+ return executeTextQuery(this, SVGInlineTextBoxQueryWalker::EndPosition, charnum).pointResult();
}
-FloatRect SVGTextContentElement::getExtentOfChar(unsigned long charnum, ExceptionCode&) const
+FloatRect SVGTextContentElement::getExtentOfChar(long charnum, ExceptionCode& ec) const
{
- return FloatRect();
+ if (charnum < 0 || charnum > getNumberOfChars()) {
+ ec = INDEX_SIZE_ERR;
+ return FloatRect();
+ }
+
+ return executeTextQuery(this, SVGInlineTextBoxQueryWalker::Extent, charnum).rectResult();
}
-float SVGTextContentElement::getRotationOfChar(unsigned long charnum, ExceptionCode&) const
+float SVGTextContentElement::getRotationOfChar(long charnum, ExceptionCode& ec) const
{
- return 0.0f;
+ if (charnum < 0 || charnum > getNumberOfChars()) {
+ ec = INDEX_SIZE_ERR;
+ return 0.0f;
+ }
+
+ return executeTextQuery(this, SVGInlineTextBoxQueryWalker::Rotation, charnum).floatResult();
}
long SVGTextContentElement::getCharNumAtPosition(const FloatPoint& point) const
{
- return 0;
+ return executeTextQuery(this, SVGInlineTextBoxQueryWalker::CharacterNumberAtPosition, 0.0f, 0.0f, point).longResult();
}
-void SVGTextContentElement::selectSubString(unsigned long charnum, unsigned long nchars, ExceptionCode&) const
+void SVGTextContentElement::selectSubString(long charnum, long nchars, ExceptionCode& ec) const
{
+ long numberOfChars = getNumberOfChars();
+ if (charnum < 0 || nchars < 0 || charnum > numberOfChars) {
+ ec = INDEX_SIZE_ERR;
+ return;
+ }
+
+ if (nchars > numberOfChars - charnum)
+ nchars = numberOfChars - charnum;
+
+ ASSERT(document());
+ ASSERT(document()->frame());
+
+ SelectionController* controller = document()->frame()->selectionController();
+ if (!controller)
+ return;
+
+ // Find selection start
+ VisiblePosition start(const_cast<SVGTextContentElement*>(this), 0, SEL_DEFAULT_AFFINITY);
+ for (long i = 0; i < charnum; ++i)
+ start = start.next();
+
+ // Find selection end
+ VisiblePosition end(start);
+ for (long i = 0; i < nchars; ++i)
+ end = end.next();
+
+ controller->setSelection(Selection(start, end));
}
void SVGTextContentElement::parseMappedAttribute(MappedAttribute* attr)
#ifndef SVGTextContentElement_h
#define SVGTextContentElement_h
-#if ENABLE(SVG)
+#if ENABLE(SVG)
#include "SVGExternalResourcesRequired.h"
#include "SVGLangSpace.h"
#include "SVGStyledElement.h"
// 'SVGTextContentElement' functions
long getNumberOfChars() const;
float getComputedTextLength() const;
- float getSubStringLength(unsigned long charnum, unsigned long nchars, ExceptionCode&) const;
- FloatPoint getStartPositionOfChar(unsigned long charnum, ExceptionCode&) const;
- FloatPoint getEndPositionOfChar(unsigned long charnum, ExceptionCode&) const;
- FloatRect getExtentOfChar(unsigned long charnum, ExceptionCode&) const;
- float getRotationOfChar(unsigned long charnum, ExceptionCode&) const;
+ float getSubStringLength(long charnum, unsigned long nchars, ExceptionCode&) const;
+ FloatPoint getStartPositionOfChar(long charnum, ExceptionCode&) const;
+ FloatPoint getEndPositionOfChar(long charnum, ExceptionCode&) const;
+ FloatRect getExtentOfChar(long charnum, ExceptionCode&) const;
+ float getRotationOfChar(long charnum, ExceptionCode&) const;
long getCharNumAtPosition(const FloatPoint&) const;
- void selectSubString(unsigned long charnum, unsigned long nchars, ExceptionCode&) const;
+ void selectSubString(long charnum, long nchars, ExceptionCode&) const;
virtual void parseMappedAttribute(MappedAttribute*);
float calculateGlyphHeight(RenderStyle*, int offset) const;
FloatRect calculateGlyphBoundaries(RenderStyle*, int offset, const SVGChar&) const;
+ SVGChar* closestCharacterToPosition(int x, int y, int& offset) const;
private:
friend class RenderSVGInlineText;
bool svgCharacterHitsPosition(int x, int y, int& offset) const;
-
- private:
- SVGChar* closestCharacterToPosition(int x, int y, int& offset) const;
};
} // namespace WebCore