WebCore:
authormjs@apple.com <mjs@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 27 Mar 2008 02:34:14 +0000 (02:34 +0000)
committermjs@apple.com <mjs@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 27 Mar 2008 02:34:14 +0000 (02:34 +0000)
2008-03-26  Maciej Stachowiak  <mjs@apple.com>

        Reviewed by Hyatt and Adam.

        Coded by me and Darin.

        - SVG kerning support (horizontal kerning only for now since we don't do vertical text layout right yet)

        Acid3 100/100

        * DerivedSources.make:
        * WebCore.vcproj/WebCore.vcproj:
        * WebCore.xcodeproj/project.pbxproj:
        * platform/graphics/Font.cpp:
        (WebCore::Font::floatWidth):
        (WebCore::Font::isSVGFont):
        * platform/graphics/Font.h:
        * rendering/SVGInlineTextBox.cpp:
        (WebCore::SVGInlineTextBox::calculateGlyphWidth):
        (WebCore::SVGInlineTextBox::calculateGlyphBoundaries):
        * rendering/SVGInlineTextBox.h:
        * rendering/SVGRootInlineBox.cpp:
        (WebCore::cummulatedWidthOrHeightOfTextChunk):
        (WebCore::SVGRootInlineBox::buildLayoutInformation):
        (WebCore::SVGRootInlineBox::buildLayoutInformationForTextBox):
        * rendering/SVGRootInlineBox.h:
        (WebCore::LastGlyphInfo::LastGlyphInfo):
        * svg/SVGFont.cpp:
        (WebCore::SVGTextRunWalker::walk):
        (WebCore::Font::svgFont):
        (WebCore::floatWidthOfSubStringUsingSVGFont):
        (WebCore::Font::floatWidthUsingSVGFont):
        (WebCore::Font::drawTextUsingSVGFont):
        (WebCore::Font::selectionRectForTextUsingSVGFont):
        * svg/SVGFontElement.cpp:
        (WebCore::SVGFontElement::invalidateGlyphCache):
        (WebCore::SVGFontElement::ensureGlyphCache):
        (WebCore::parseUnicodeRange):
        (WebCore::parseUnicodeRangeList):
        (WebCore::stringMatchesUnicodeRange):
        (WebCore::matches):
        (WebCore::SVGFontElement::getHorizontalKerningPairForStringsAndGlyphs):
        * svg/SVGFontElement.h:
        * svg/SVGGlyphElement.cpp:
        (WebCore::SVGGlyphElement::insertedIntoDocument):
        (WebCore::SVGGlyphElement::removedFromDocument):
        * svg/SVGHKernElement.cpp: Added.
        (WebCore::SVGHKernElement::SVGHKernElement):
        (WebCore::SVGHKernElement::~SVGHKernElement):
        (WebCore::SVGHKernElement::insertedIntoDocument):
        (WebCore::SVGHKernElement::removedFromDocument):
        (WebCore::SVGHKernElement::buildHorizontalKerningPair):
        * svg/SVGHKernElement.h: Added.
        (WebCore::SVGHorizontalKerningPair::SVGHorizontalKerningPair):
        (WebCore::SVGHKernElement::rendererIsNeeded):
        * svg/SVGHKernElement.idl: Added.
        * svg/SVGTextContentElement.cpp:
        (WebCore::cummulatedCharacterRangeLength):
        (WebCore::SVGInlineTextBoxQueryWalker::chunkPortionCallback):
        * svg/svgtags.in:

LayoutTests:

2008-03-26  Maciej Stachowiak  <mjs@apple.com>

        Reviewed by Hyatt and Adam.

        - new test case and test fixes for SVG text kerning

        * svg/text/kerning.svg: Added.
        * platform/mac/svg/text/kerning-expected.txt: Added.

        * platform/mac/svg/W3C-SVG-1.1/fonts-kern-01-t-expected.txt:
        * platform/mac/svg/W3C-SVG-1.1/masking-mask-01-b-expected.txt:
        * platform/mac/svg/W3C-SVG-1.1/render-groups-01-b-expected.txt:
        * platform/mac/svg/W3C-SVG-1.1/render-groups-03-t-expected.txt:
        * platform/mac/svg/custom/scrolling-embedded-svg-file-image-repaint-problem-expected.txt:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@31342 268f45cc-cd09-0410-ab3c-d52691b4dbfc

27 files changed:
LayoutTests/ChangeLog
LayoutTests/platform/mac/svg/W3C-SVG-1.1/fonts-kern-01-t-expected.txt
LayoutTests/platform/mac/svg/W3C-SVG-1.1/masking-mask-01-b-expected.txt
LayoutTests/platform/mac/svg/W3C-SVG-1.1/render-groups-01-b-expected.txt
LayoutTests/platform/mac/svg/W3C-SVG-1.1/render-groups-03-t-expected.txt
LayoutTests/platform/mac/svg/custom/scrolling-embedded-svg-file-image-repaint-problem-expected.txt
LayoutTests/platform/mac/svg/text/kerning-expected.txt [new file with mode: 0644]
LayoutTests/svg/text/kerning.svg [new file with mode: 0644]
WebCore/ChangeLog
WebCore/DerivedSources.make
WebCore/WebCore.vcproj/WebCore.vcproj
WebCore/WebCore.xcodeproj/project.pbxproj
WebCore/platform/graphics/Font.cpp
WebCore/platform/graphics/Font.h
WebCore/rendering/SVGInlineTextBox.cpp
WebCore/rendering/SVGInlineTextBox.h
WebCore/rendering/SVGRootInlineBox.cpp
WebCore/rendering/SVGRootInlineBox.h
WebCore/svg/SVGFont.cpp
WebCore/svg/SVGFontElement.cpp
WebCore/svg/SVGFontElement.h
WebCore/svg/SVGGlyphElement.cpp
WebCore/svg/SVGHKernElement.cpp [new file with mode: 0644]
WebCore/svg/SVGHKernElement.h [new file with mode: 0644]
WebCore/svg/SVGHKernElement.idl [new file with mode: 0644]
WebCore/svg/SVGTextContentElement.cpp
WebCore/svg/svgtags.in

index 89d7ed25b38ecd07580fb08025b806a1082269af..5bd12866bfa02abb9edafab7ad209ae84bdbd422 100644 (file)
@@ -1,3 +1,18 @@
+2008-03-26  Maciej Stachowiak  <mjs@apple.com>
+
+        Reviewed by Hyatt and Adam.
+        
+        - new test case and test fixes for SVG text kerning
+
+        * svg/text/kerning.svg: Added.
+        * platform/mac/svg/text/kerning-expected.txt: Added.
+
+        * platform/mac/svg/W3C-SVG-1.1/fonts-kern-01-t-expected.txt:
+        * platform/mac/svg/W3C-SVG-1.1/masking-mask-01-b-expected.txt:
+        * platform/mac/svg/W3C-SVG-1.1/render-groups-01-b-expected.txt:
+        * platform/mac/svg/W3C-SVG-1.1/render-groups-03-t-expected.txt:
+        * platform/mac/svg/custom/scrolling-embedded-svg-file-image-repaint-problem-expected.txt:
+
 2008-03-26  Stephanie Lewis  <slewis@apple.com>
 
         Not Reviewed.
index 05fad92958b608dd856839f9adc8a795c43550a4..848244ccafad5d305960ac5a9ced9c5c756d564f 100644 (file)
@@ -1,8 +1,8 @@
 layer at (0,0) size 480x360
   RenderView at (0,0) size 480x360
 layer at (0,0) size 480x360
-  RenderSVGRoot {svg} at (0.50,0.50) size 482.50x359
-    RenderSVGContainer {g} at (9.50,12) size 473.50x263.50
+  RenderSVGRoot {svg} at (0.50,0.50) size 9344.50x359
+    RenderSVGContainer {g} at (9.50,12) size 9335.50x263.50
       RenderSVGHiddenContainer {defs} at (0,0) size 0x0
         RenderSVGContainer {g} at (-2,-12) size 14x14
           RenderPath {line} at (-0.50,-12) size 1x12 [stroke={[type=SOLID] [color=#FF0000]}] [fill={[type=SOLID] [color=#000000]}] [data="M0.00,0.00L0.00,-12.00"]
@@ -15,8 +15,8 @@ layer at (0,0) size 480x360
       RenderSVGText {text} at (206,30) size 68x23 contains 1 chunk(s)
         RenderSVGInlineText {#text} at (0,-18) size 68x23
           chunk 1 (middle anchor) text run 1 at (206.00,30.00) startOffset 0 endOffset 7 width 68.00: "<hkern>"
-      RenderSVGContainer {g} at (9.50,59.50) size 473.50x216 [transform={m=((1.00,0.00)(0.00,1.00)) t=(30.00,60.00)}]
-        RenderSVGContainer {g} at (9.50,59.50) size 221x51
+      RenderSVGContainer {g} at (9.50,59.50) size 9335.50x216 [transform={m=((1.00,0.00)(0.00,1.00)) t=(30.00,60.00)}]
+        RenderSVGContainer {g} at (9.50,59.50) size 2041.50x51
           RenderSVGContainer {g} at (9.50,59.50) size 21x51
             RenderPath {rect} at (9.50,59.50) size 21x51 [stroke={[type=SOLID] [color=#000000]}] [data="M-20.00,0.00L0.00,0.00L0.00,50.00L-20.00,50.00"]
             RenderSVGText {text} at (-21,0) size 41x18 contains 1 chunk(s)
@@ -31,7 +31,7 @@ layer at (0,0) size 480x360
           RenderSVGText {text} at (5,15) size 73x14 contains 1 chunk(s)
             RenderSVGInlineText {#text} at (0,-11) size 73x14
               chunk 1 text run 1 at (5.00,15.00) startOffset 0 endOffset 13 width 73.00: "u1=\"1\" u2=\"2\""
-          RenderSVGContainer {g} at (31,81) size 53x28 [transform={m=((2.00,0.00)(0.00,2.00)) t=(5.00,45.00)}]
+          RenderSVGContainer {g} at (31,81) size 2020x28 [transform={m=((2.00,0.00)(0.00,2.00)) t=(5.00,45.00)}]
             RenderSVGContainer {use} at (31,81) size 28x28
               RenderSVGContainer {g} at (31,81) size 28x28
                 RenderSVGContainer {g} at (31,81) size 28x28
@@ -44,10 +44,10 @@ layer at (0,0) size 480x360
                   RenderPath {line} at (59,81) size 2x24 [stroke={[type=SOLID] [color=#FF0000]}] [fill={[type=SOLID] [color=#8888FF]}] [data="M0.00,0.00L0.00,-12.00"]
                   RenderPath {line} at (60,104) size 24x2 [stroke={[type=SOLID] [color=#FF0000]}] [fill={[type=SOLID] [color=#8888FF]}] [data="M0.00,0.00L12.00,0.00"]
                   RenderPath {rect} at (56,101) size 8x8 [fill={[type=SOLID] [color=#FF0000]}] [data="M-2.00,-2.00L2.00,-2.00L2.00,2.00L-2.00,2.00"]
-            RenderSVGText {text} at (0,0) size 8x10 contains 1 chunk(s)
-              RenderSVGInlineText {#text} at (0,-8) size 8x10
+            RenderSVGText {text} at (0,0) size 1008x10 contains 1 chunk(s)
+              RenderSVGInlineText {#text} at (0,-8) size 1008x10
                 chunk 1 text run 1 at (0.00,0.00) startOffset 0 endOffset 2 width 7.50: "12"
-        RenderSVGContainer {g} at (9.50,114.50) size 221x51 [transform={m=((1.00,0.00)(0.00,1.00)) t=(0.00,55.00)}]
+        RenderSVGContainer {g} at (9.50,114.50) size 4041.50x51 [transform={m=((1.00,0.00)(0.00,1.00)) t=(0.00,55.00)}]
           RenderSVGContainer {g} at (9.50,114.50) size 21x51
             RenderPath {rect} at (9.50,114.50) size 21x51 [stroke={[type=SOLID] [color=#000000]}] [data="M-20.00,0.00L0.00,0.00L0.00,50.00L-20.00,50.00"]
             RenderSVGText {text} at (-20,0) size 40x18 contains 1 chunk(s)
@@ -62,7 +62,7 @@ layer at (0,0) size 480x360
           RenderSVGText {text} at (5,15) size 138x18 contains 1 chunk(s)
             RenderSVGInlineText {#text} at (0,-14) size 138x18
               chunk 1 text run 1 at (5.00,15.00) startOffset 0 endOffset 19 width 138.00: "g1=\"gl_1\" g2=\"gl_2\""
-          RenderSVGContainer {g} at (31,136) size 73x28 [transform={m=((2.00,0.00)(0.00,2.00)) t=(5.00,45.00)}]
+          RenderSVGContainer {g} at (31,136) size 4020x28 [transform={m=((2.00,0.00)(0.00,2.00)) t=(5.00,45.00)}]
             RenderSVGContainer {use} at (31,136) size 28x28
               RenderSVGContainer {g} at (31,136) size 28x28
                 RenderSVGContainer {g} at (31,136) size 28x28
@@ -75,8 +75,8 @@ layer at (0,0) size 480x360
                   RenderPath {line} at (79,136) size 2x24 [stroke={[type=SOLID] [color=#FF0000]}] [fill={[type=SOLID] [color=#8888FF]}] [data="M0.00,0.00L0.00,-12.00"]
                   RenderPath {line} at (80,159) size 24x2 [stroke={[type=SOLID] [color=#FF0000]}] [fill={[type=SOLID] [color=#8888FF]}] [data="M0.00,0.00L12.00,0.00"]
                   RenderPath {rect} at (76,156) size 8x8 [fill={[type=SOLID] [color=#FF0000]}] [data="M-2.00,-2.00L2.00,-2.00L2.00,2.00L-2.00,2.00"]
-            RenderSVGText {text} at (0,0) size 8x10 contains 1 chunk(s)
-              RenderSVGInlineText {#text} at (0,-8) size 8x10
+            RenderSVGText {text} at (0,0) size 2008x10 contains 1 chunk(s)
+              RenderSVGInlineText {#text} at (0,-8) size 2008x10
                 chunk 1 text run 1 at (0.00,0.00) startOffset 0 endOffset 2 width 7.50: "12"
         RenderSVGContainer {g} at (9.50,169.50) size 265.50x51 [transform={m=((1.00,0.00)(0.00,1.00)) t=(0.00,110.00)}]
           RenderSVGContainer {g} at (9.50,169.50) size 21x51
@@ -207,7 +207,7 @@ layer at (0,0) size 480x360
             RenderSVGText {text} at (0,0) size 35x10 contains 1 chunk(s)
               RenderSVGInlineText {#text} at (0,-8) size 35x10
                 chunk 1 text run 1 at (0.00,0.00) startOffset 0 endOffset 4 width 35.00: "1234"
-        RenderSVGContainer {g} at (249.50,114.50) size 233.50x51 [transform={m=((1.00,0.00)(0.00,1.00)) t=(240.00,55.00)}]
+        RenderSVGContainer {g} at (249.50,114.50) size 9095.50x51 [transform={m=((1.00,0.00)(0.00,1.00)) t=(240.00,55.00)}]
           RenderSVGContainer {g} at (249.50,114.50) size 21x51
             RenderPath {rect} at (249.50,114.50) size 21x51 [stroke={[type=SOLID] [color=#000000]}] [data="M-20.00,0.00L0.00,0.00L0.00,50.00L-20.00,50.00"]
             RenderSVGText {text} at (-19,0) size 38x18 contains 1 chunk(s)
@@ -222,7 +222,7 @@ layer at (0,0) size 480x360
           RenderSVGText {text} at (5,15) size 208x18 contains 1 chunk(s)
             RenderSVGInlineText {#text} at (0,-14) size 208x18
               chunk 1 text run 1 at (5.00,15.00) startOffset 0 endOffset 26 width 208.00: "u1=\"U+003?\" u2=\"U+0031-34\""
-          RenderSVGContainer {g} at (271,136) size 168x28 [transform={m=((2.00,0.00)(0.00,2.00)) t=(5.00,45.00)}]
+          RenderSVGContainer {g} at (271,136) size 9074x28 [transform={m=((2.00,0.00)(0.00,2.00)) t=(5.00,45.00)}]
             RenderSVGContainer {use} at (271,136) size 28x28
               RenderSVGContainer {g} at (271,136) size 28x28
                 RenderSVGContainer {g} at (271,136) size 28x28
@@ -247,10 +247,10 @@ layer at (0,0) size 480x360
                   RenderPath {line} at (414,136) size 2x24 [stroke={[type=SOLID] [color=#FF0000]}] [fill={[type=SOLID] [color=#8888FF]}] [data="M0.00,0.00L0.00,-12.00"]
                   RenderPath {line} at (415,159) size 24x2 [stroke={[type=SOLID] [color=#FF0000]}] [fill={[type=SOLID] [color=#8888FF]}] [data="M0.00,0.00L12.00,0.00"]
                   RenderPath {rect} at (411,156) size 8x8 [fill={[type=SOLID] [color=#FF0000]}] [data="M-2.00,-2.00L2.00,-2.00L2.00,2.00L-2.00,2.00"]
-            RenderSVGText {text} at (0,0) size 35x10 contains 1 chunk(s)
-              RenderSVGInlineText {#text} at (0,-8) size 35x10
+            RenderSVGText {text} at (0,0) size 4535x10 contains 1 chunk(s)
+              RenderSVGInlineText {#text} at (0,-8) size 4535x10
                 chunk 1 text run 1 at (0.00,0.00) startOffset 0 endOffset 4 width 35.00: "1234"
-        RenderSVGContainer {g} at (249.50,169.50) size 221x51 [transform={m=((1.00,0.00)(0.00,1.00)) t=(240.00,110.00)}]
+        RenderSVGContainer {g} at (249.50,169.50) size 2061.50x51 [transform={m=((1.00,0.00)(0.00,1.00)) t=(240.00,110.00)}]
           RenderSVGContainer {g} at (249.50,169.50) size 21x51
             RenderPath {rect} at (249.50,169.50) size 21x51 [stroke={[type=SOLID] [color=#000000]}] [data="M-20.00,0.00L0.00,0.00L0.00,50.00L-20.00,50.00"]
             RenderSVGText {text} at (-21,0) size 41x18 contains 1 chunk(s)
@@ -265,7 +265,7 @@ layer at (0,0) size 480x360
           RenderSVGText {text} at (5,15) size 118x18 contains 1 chunk(s)
             RenderSVGInlineText {#text} at (0,-14) size 118x18
               chunk 1 text run 1 at (5.00,15.00) startOffset 0 endOffset 16 width 118.00: "u1=\"1\" g2=\"gl_2\""
-          RenderSVGContainer {g} at (271,191) size 53x28 [transform={m=((2.00,0.00)(0.00,2.00)) t=(5.00,45.00)}]
+          RenderSVGContainer {g} at (271,191) size 2040x28 [transform={m=((2.00,0.00)(0.00,2.00)) t=(5.00,45.00)}]
             RenderSVGContainer {use} at (271,191) size 28x28
               RenderSVGContainer {g} at (271,191) size 28x28
                 RenderSVGContainer {g} at (271,191) size 28x28
@@ -278,8 +278,8 @@ layer at (0,0) size 480x360
                   RenderPath {line} at (299,191) size 2x24 [stroke={[type=SOLID] [color=#FF0000]}] [fill={[type=SOLID] [color=#8888FF]}] [data="M0.00,0.00L0.00,-12.00"]
                   RenderPath {line} at (300,214) size 24x2 [stroke={[type=SOLID] [color=#FF0000]}] [fill={[type=SOLID] [color=#8888FF]}] [data="M0.00,0.00L12.00,0.00"]
                   RenderPath {rect} at (296,211) size 8x8 [fill={[type=SOLID] [color=#FF0000]}] [data="M-2.00,-2.00L2.00,-2.00L2.00,2.00L-2.00,2.00"]
-            RenderSVGText {text} at (0,0) size 18x10 contains 1 chunk(s)
-              RenderSVGInlineText {#text} at (0,-8) size 18x10
+            RenderSVGText {text} at (0,0) size 1018x10 contains 1 chunk(s)
+              RenderSVGInlineText {#text} at (0,-8) size 1018x10
                 chunk 1 text run 1 at (0.00,0.00) startOffset 0 endOffset 2 width 17.50: "12"
       RenderSVGContainer {g} at (320,100) size 0x0 [transform={m=((1.00,0.00)(0.00,1.00)) t=(320.00,100.00)}]
     RenderSVGHiddenContainer {defs} at (0,0) size 0x0
index 1cb717c95e84908edc38ae6e45faf5c7051e7d9f..43dffc592fcace130b80d098d44480728060d9a9 100644 (file)
@@ -40,8 +40,8 @@ layer at (0,0) size 480x360
         RenderPath {rect} at (60,225) size 200x25 [fill={[type=SOLID] [color=#FFFFFF] [opacity=0.20]}] [data="M60.00,225.00L260.00,225.00L260.00,250.00L60.00,250.00"]
         RenderPath {rect} at (60,250) size 200x25 [fill={[type=SOLID] [color=#FFFFFF]}] [data="M60.00,250.00L260.00,250.00L260.00,275.00L60.00,275.00"]
         RenderPath {rect} at (60,275) size 200x25 [fill={[type=SOLID] [color=#FFFFFF] [opacity=0.70]}] [data="M60.00,275.00L260.00,275.00L260.00,300.00L60.00,300.00"]
-      RenderSVGText {text} at (60,280) size 121x110 contains 1 chunk(s)
-        RenderSVGInlineText {#text} at (0,-88) size 121x110
+      RenderSVGText {text} at (60,280) size 161x110 contains 1 chunk(s)
+        RenderSVGInlineText {#text} at (0,-88) size 161x110
           chunk 1 text run 1 at (60.00,280.00) startOffset 0 endOffset 3 width 121.33: "SVG"
       RenderSVGText {text} at (200,225) size 200x14 contains 1 chunk(s)
         RenderSVGInlineText {#text} at (0,-11) size 200x14
index e895d03d9a0d918a5cb9b8aba64640c0804ed56a..6bb7f0cf1c54b0df544f33871ae95e84bcee31e2 100644 (file)
@@ -16,8 +16,8 @@ layer at (0,0) size 480x360
         RenderPath {line} at (436.50,18) size 27x297 [stroke={[type=SOLID] [color=#FFFFFF] [stroke width=15.00]}] [fill={[type=SOLID] [color=#000000]}] [data="M250.00,10.00L250.00,175.00"]
         RenderPath {rect} at (35.10,17.10) size 415.80x280.80 [stroke={[type=SOLID] [color=#000000]}] [data="M20.00,10.00L250.00,10.00L250.00,165.00L20.00,165.00"]
         RenderSVGContainer {g} at (54,-10.80) size 381.60x289.80 [opacity=0.50]
-          RenderSVGText {text} at (30,90) size 132x120 contains 1 chunk(s)
-            RenderSVGInlineText {#text} at (0,-96) size 132x120
+          RenderSVGText {text} at (30,90) size 172x120 contains 1 chunk(s)
+            RenderSVGInlineText {#text} at (0,-96) size 172x120
               chunk 1 text run 1 at (30.00,90.00) startOffset 0 endOffset 3 width 132.36: "SVG"
           RenderPath {rect} at (126,99) size 234x126 [fill={[type=SOLID] [color=#820032]}] [data="M70.00,55.00L200.00,55.00L200.00,125.00L70.00,125.00"]
           RenderImage {image} at (0,0) size 80x80
index fd36bf02b96d0e2a476b263f4c4200380db8b5ac..42c277534e99cd56ae30c641615e5309a70f8006 100644 (file)
@@ -15,8 +15,8 @@ layer at (0,0) size 480x360
         RenderPath {line} at (382.50,18) size 27x297 [stroke={[type=SOLID] [color=#FFFFFF] [stroke width=15.00]}] [fill={[type=SOLID] [color=#000000]}] [data="M220.00,10.00L220.00,175.00"]
         RenderPath {line} at (436.50,18) size 27x297 [stroke={[type=SOLID] [color=#FFFFFF] [stroke width=15.00]}] [fill={[type=SOLID] [color=#000000]}] [data="M250.00,10.00L250.00,175.00"]
         RenderPath {rect} at (35.10,17.10) size 415.80x280.80 [stroke={[type=SOLID] [color=#000000]}] [data="M20.00,10.00L250.00,10.00L250.00,165.00L20.00,165.00"]
-        RenderSVGText {text} at (30,90) size 132x120 contains 1 chunk(s)
-          RenderSVGInlineText {#text} at (0,-96) size 132x120
+        RenderSVGText {text} at (30,90) size 172x120 contains 1 chunk(s)
+          RenderSVGInlineText {#text} at (0,-96) size 172x120
             chunk 1 text run 1 at (30.00,90.00) startOffset 0 endOffset 3 width 132.36: "SVG"
         RenderPath {rect} at (126,99) size 234x126 [fill={[type=SOLID] [color=#820032]}] [data="M70.00,55.00L200.00,55.00L200.00,125.00L70.00,125.00"]
         RenderImage {image} at (0,0) size 80x80
index ffb7200b7fea7e75074a17cf3d3e3f1751ea880f..8810860462375dd1a5d6ecf420598089037a5903 100644 (file)
@@ -73,8 +73,8 @@ layer at (0,0) size 1026x1014
                                 RenderPath {line} at (436.50,18) size 27x297 [stroke={[type=SOLID] [color=#FFFFFF] [stroke width=15.00]}] [fill={[type=SOLID] [color=#000000]}] [data="M250.00,10.00L250.00,175.00"]
                                 RenderPath {rect} at (35.10,17.10) size 415.80x280.80 [stroke={[type=SOLID] [color=#000000]}] [data="M20.00,10.00L250.00,10.00L250.00,165.00L20.00,165.00"]
                                 RenderSVGContainer {g} at (54,-10.80) size 381.60x289.80 [opacity=0.50]
-                                  RenderSVGText {text} at (30,90) size 132x120 contains 1 chunk(s)
-                                    RenderSVGInlineText {#text} at (0,-96) size 132x120
+                                  RenderSVGText {text} at (30,90) size 172x120 contains 1 chunk(s)
+                                    RenderSVGInlineText {#text} at (0,-96) size 172x120
                                       chunk 1 text run 1 at (30.00,90.00) startOffset 0 endOffset 3 width 132.36: "SVG"
                                   RenderPath {rect} at (126,99) size 234x126 [fill={[type=SOLID] [color=#820032]}] [data="M70.00,55.00L200.00,55.00L200.00,125.00L70.00,125.00"]
                                   RenderImage {image} at (0,0) size 80x80
diff --git a/LayoutTests/platform/mac/svg/text/kerning-expected.txt b/LayoutTests/platform/mac/svg/text/kerning-expected.txt
new file mode 100644 (file)
index 0000000..ba03048
--- /dev/null
@@ -0,0 +1,102 @@
+layer at (0,0) size 808x585
+  RenderView at (0,0) size 800x585
+layer at (0,0) size 808x585
+  RenderBlock {html} at (0,0) size 800x585
+    RenderBody {body} at (8,16) size 784x415
+      RenderBlock {p} at (0,0) size 784x36
+        RenderText {#text} at (0,0) size 783x36
+          text run at (0,0) width 783: "The left edges of the black boxes below should line up with the left edges of their containing red or green boxes. In addition,"
+          text run at (0,18) width 227: "all the assertions below should pass."
+      RenderBlock {div} at (0,52) size 800x200
+        RenderSVGRoot {svg} at (8,-2) size 570x240
+          RenderPath {rect} at (8,78) size 70x160 [fill={[type=SOLID] [color=#FF0000]}] [data="M0.00,10.00L70.00,10.00L70.00,170.00L0.00,170.00"]
+          RenderPath {rect} at (78,78) size 100x150 [fill={[type=SOLID] [color=#008000]}] [data="M70.00,10.00L170.00,10.00L170.00,160.00L70.00,160.00"]
+          RenderPath {rect} at (178,78) size 70x140 [fill={[type=SOLID] [color=#FF0000]}] [data="M170.00,10.00L240.00,10.00L240.00,150.00L170.00,150.00"]
+          RenderPath {rect} at (248,78) size 70x130 [fill={[type=SOLID] [color=#008000]}] [data="M240.00,10.00L310.00,10.00L310.00,140.00L240.00,140.00"]
+          RenderPath {rect} at (318,78) size 80x120 [fill={[type=SOLID] [color=#FF0000]}] [data="M310.00,10.00L390.00,10.00L390.00,130.00L310.00,130.00"]
+          RenderPath {rect} at (398,78) size 60x110 [fill={[type=SOLID] [color=#008000]}] [data="M390.00,10.00L450.00,10.00L450.00,120.00L390.00,120.00"]
+          RenderSVGText {text} at (0,10) size 570x100 contains 1 chunk(s)
+            RenderSVGInlineText {#text} at (0,-80) size 570x100
+              chunk 1 text run 1 at (0.00,10.00) startOffset 0 endOffset 10 width 590.00: "GGDGGBBBFB"
+        RenderText {#text} at (0,0) size 0x0
+      RenderBlock {pre} at (0,265) size 784x150
+        RenderInline {span} at (0,0) size 312x15
+          RenderInline {span} at (0,0) size 312x15
+            RenderInline {span} at (0,0) size 32x15
+              RenderText {#text} at (0,0) size 32x15
+                text run at (0,0) width 32: "PASS"
+            RenderText {#text} at (32,0) size 280x15
+              text run at (32,0) width 280: " t.getStartPositionOfChar(0).x is 0"
+          RenderBR {br} at (312,0) size 0x15
+        RenderInline {span} at (0,0) size 312x15
+          RenderInline {span} at (0,0) size 312x15
+            RenderInline {span} at (0,0) size 32x15
+              RenderText {#text} at (0,15) size 32x15
+                text run at (0,15) width 32: "PASS"
+            RenderText {#text} at (32,15) size 280x15
+              text run at (32,15) width 280: " t.getStartPositionOfChar(1).x is 0"
+          RenderBR {br} at (312,15) size 0x15
+        RenderInline {span} at (0,0) size 352x15
+          RenderInline {span} at (0,0) size 352x15
+            RenderInline {span} at (0,0) size 32x15
+              RenderText {#text} at (0,30) size 32x15
+                text run at (0,30) width 32: "PASS"
+            RenderText {#text} at (32,30) size 320x15
+              text run at (32,30) width 320: " t.getStartPositionOfChar(2).x is 0 + 70"
+          RenderBR {br} at (352,30) size 0x15
+        RenderInline {span} at (0,0) size 400x15
+          RenderInline {span} at (0,0) size 400x15
+            RenderInline {span} at (0,0) size 32x15
+              RenderText {#text} at (0,45) size 32x15
+                text run at (0,45) width 32: "PASS"
+            RenderText {#text} at (32,45) size 368x15
+              text run at (32,45) width 368: " t.getStartPositionOfChar(3).x is 0 + 70 + 100"
+          RenderBR {br} at (400,45) size 0x15
+        RenderInline {span} at (0,0) size 400x15
+          RenderInline {span} at (0,0) size 400x15
+            RenderInline {span} at (0,0) size 32x15
+              RenderText {#text} at (0,60) size 32x15
+                text run at (0,60) width 32: "PASS"
+            RenderText {#text} at (32,60) size 368x15
+              text run at (32,60) width 368: " t.getStartPositionOfChar(4).x is 0 + 70 + 100"
+          RenderBR {br} at (400,60) size 0x15
+        RenderInline {span} at (0,0) size 440x15
+          RenderInline {span} at (0,0) size 440x15
+            RenderInline {span} at (0,0) size 32x15
+              RenderText {#text} at (0,75) size 32x15
+                text run at (0,75) width 32: "PASS"
+            RenderText {#text} at (32,75) size 408x15
+              text run at (32,75) width 408: " t.getStartPositionOfChar(5).x is 0 + 70 + 100 + 70"
+          RenderBR {br} at (440,75) size 0x15
+        RenderInline {span} at (0,0) size 440x15
+          RenderInline {span} at (0,0) size 440x15
+            RenderInline {span} at (0,0) size 32x15
+              RenderText {#text} at (0,90) size 32x15
+                text run at (0,90) width 32: "PASS"
+            RenderText {#text} at (32,90) size 408x15
+              text run at (32,90) width 408: " t.getStartPositionOfChar(6).x is 0 + 70 + 100 + 70"
+          RenderBR {br} at (440,90) size 0x15
+        RenderInline {span} at (0,0) size 480x15
+          RenderInline {span} at (0,0) size 480x15
+            RenderInline {span} at (0,0) size 32x15
+              RenderText {#text} at (0,105) size 32x15
+                text run at (0,105) width 32: "PASS"
+            RenderText {#text} at (32,105) size 448x15
+              text run at (32,105) width 448: " t.getStartPositionOfChar(7).x is 0 + 70 + 100 + 70 + 70"
+          RenderBR {br} at (480,105) size 0x15
+        RenderInline {span} at (0,0) size 520x15
+          RenderInline {span} at (0,0) size 520x15
+            RenderInline {span} at (0,0) size 32x15
+              RenderText {#text} at (0,120) size 32x15
+                text run at (0,120) width 32: "PASS"
+            RenderText {#text} at (32,120) size 488x15
+              text run at (32,120) width 488: " t.getStartPositionOfChar(8).x is 0 + 70 + 100 + 70 + 70 + 80"
+          RenderBR {br} at (520,120) size 0x15
+        RenderInline {span} at (0,0) size 568x15
+          RenderInline {span} at (0,0) size 568x15
+            RenderInline {span} at (0,0) size 32x15
+              RenderText {#text} at (0,135) size 32x15
+                text run at (0,135) width 32: "PASS"
+            RenderText {#text} at (32,135) size 536x15
+              text run at (32,135) width 536: " t.getStartPositionOfChar(9).x is 0 + 70 + 100 + 70 + 70 + 80 + 100"
+          RenderBR {br} at (568,135) size 0x15
diff --git a/LayoutTests/svg/text/kerning.svg b/LayoutTests/svg/text/kerning.svg
new file mode 100644 (file)
index 0000000..a0cc2f3
--- /dev/null
@@ -0,0 +1,122 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="width: 100%; height: 100%;">
+<body>
+<p>The left edges of the black boxes below should line up with the
+left edges of their containing red or green boxes. In addition, all
+the assertions below should pass.</p>
+
+<div style="width: 800px; height: 200px;">
+<svg xmlns="http://www.w3.org/2000/svg">
+<font>
+<font-face font-family="xyzzy" units-per-em="100" ascent="100" descent="500">
+</font-face>
+<glyph unicode="BD" d="M0,0 h40 v-80 h-40 z" horiz-adv-x="80">
+</glyph>
+<glyph unicode="GG" d="M0,0 h20 v-60 h-20 z" horiz-adv-x="70">
+</glyph>
+<glyph unicode="BB" d="M0,0 h20 v-70 h-20 z" horiz-adv-x="70">
+</glyph>
+<glyph unicode="B" d="M0,0 h30 v-40 h-30 z" horiz-adv-x="80">
+</glyph>
+<glyph unicode="D" d="M0,0 h30 v-40 h-30 z" horiz-adv-x="100">
+</glyph>
+<glyph unicode="F" d="M0,0 h30 v-40 h-30 z" horiz-adv-x="120">
+</glyph>
+<glyph unicode="G" d="M0,0 h30 v-40 h-30 z" horiz-adv-x="60">
+</glyph>
+<hkern u1="F" u2="B" k="20"/>
+</font>
+<rect x="0" y="10" width="70" height="160" fill="red"/>
+<rect x="70" y="10" width="100" height="150" fill="green"/>
+<rect x="170" y="10" width="70" height="140" fill="red"/>
+<rect x="240" y="10" width="70" height="130" fill="green"/>
+<rect x="310" y="10" width="80" height="120" fill="red"/>
+<rect x="390" y="10" width="60" height="110" fill="green"/>
+<text id="foo" y="10" font-family="xyzzy" font-size="100px" letter-spacing="0px" word-spacing="0px">GGDGGBBBFB</text>
+</svg>
+</div>
+<pre id="console" />
+<script>
+<![CDATA[
+
+function debug(msg)
+{
+    var span = document.createElementNS("http://www.w3.org/1999/xhtml", "span");
+    document.getElementById("console").appendChild(span); // insert it first so XHTML knows the namespace
+    span.innerHTML = msg + '<br />';
+}
+
+function escapeHTML(text)
+{
+    return text.replace(/&/g, "&amp;").replace(/</g, "&lt;");
+}
+
+function testPassed(msg)
+{
+    debug('<span><span class="pass">PASS</span> ' + escapeHTML(msg) + '</span>');
+}
+
+function testFailed(msg)
+{
+    debug('<span><span class="fail">FAIL</span> ' + escapeHTML(msg) + '</span>');
+}
+
+function areArraysEqual(_a, _b)
+{
+    if (_a.length !== _b.length)
+        return false;
+    for (var i = 0; i < _a.length; i++)
+        if (_a[i] !== _b[i])
+            return false;
+    return true;
+}
+
+function isResultCorrect(_actual, _expected)
+{
+    if (_actual === _expected)
+        return true;
+    if (typeof(_expected) == "number" && isNaN(_expected))
+        return typeof(_actual) == "number" && isNaN(_actual);
+    if (Object.prototype.toString.call(_expected) == Object.prototype.toString.call([]))
+        return areArraysEqual(_actual, _expected);
+    return false;
+}
+
+function shouldBe(_a, _b)
+{
+  if (typeof _a != "string" || typeof _b != "string")
+    debug("WARN: shouldBe() expects string arguments");
+  var exception;
+  var _av;
+  try {
+     _av = eval(_a);
+  } catch (e) {
+     exception = e;
+  }
+  var _bv = eval(_b);
+
+  if (exception)
+    testFailed(_a + " should be " + _bv + ". Threw exception " + exception);
+  else if (isResultCorrect(_av, _bv))
+    testPassed(_a + " is " + _b);
+  else if (typeof(_av) == typeof(_bv))
+    testFailed(_a + " should be " + _bv + ". Was " + _av + ".");
+  else
+    testFailed(_a + " should be " + _bv + " (of type " + typeof _bv + "). Was " + _av + " (of type " + typeof _av + ").");
+}
+
+var t = document.getElementById("foo");
+shouldBe("t.getStartPositionOfChar(0).x", '0');
+shouldBe("t.getStartPositionOfChar(1).x", '0');
+shouldBe("t.getStartPositionOfChar(2).x", '0 + 70');
+shouldBe("t.getStartPositionOfChar(3).x", '0 + 70 + 100');
+shouldBe("t.getStartPositionOfChar(4).x", '0 + 70 + 100');
+shouldBe("t.getStartPositionOfChar(5).x", '0 + 70 + 100 + 70');
+shouldBe("t.getStartPositionOfChar(6).x", '0 + 70 + 100 + 70');
+shouldBe("t.getStartPositionOfChar(7).x", '0 + 70 + 100 + 70 + 70');
+shouldBe("t.getStartPositionOfChar(8).x", '0 + 70 + 100 + 70 + 70 + 80');
+shouldBe("t.getStartPositionOfChar(9).x", '0 + 70 + 100 + 70 + 70 + 80 + 100');
+]]>
+</script>
+</body>
+</html>
+
index 59549510767acea65753a45baf437ba87ec05b0a..bb5c366bc25994601a5ef2e798396bb7c25a1f54 100644 (file)
@@ -1,3 +1,64 @@
+2008-03-26  Maciej Stachowiak  <mjs@apple.com>
+
+        Reviewed by Hyatt and Adam.
+
+        Coded by me and Darin.
+        
+        - SVG kerning support (horizontal kerning only for now since we don't do vertical text layout right yet) 
+        
+        Acid3 100/100
+
+        * DerivedSources.make:
+        * WebCore.vcproj/WebCore.vcproj:
+        * WebCore.xcodeproj/project.pbxproj:
+        * platform/graphics/Font.cpp:
+        (WebCore::Font::floatWidth):
+        (WebCore::Font::isSVGFont):
+        * platform/graphics/Font.h:
+        * rendering/SVGInlineTextBox.cpp:
+        (WebCore::SVGInlineTextBox::calculateGlyphWidth):
+        (WebCore::SVGInlineTextBox::calculateGlyphBoundaries):
+        * rendering/SVGInlineTextBox.h:
+        * rendering/SVGRootInlineBox.cpp:
+        (WebCore::cummulatedWidthOrHeightOfTextChunk):
+        (WebCore::SVGRootInlineBox::buildLayoutInformation):
+        (WebCore::SVGRootInlineBox::buildLayoutInformationForTextBox):
+        * rendering/SVGRootInlineBox.h:
+        (WebCore::LastGlyphInfo::LastGlyphInfo):
+        * svg/SVGFont.cpp:
+        (WebCore::SVGTextRunWalker::walk):
+        (WebCore::Font::svgFont):
+        (WebCore::floatWidthOfSubStringUsingSVGFont):
+        (WebCore::Font::floatWidthUsingSVGFont):
+        (WebCore::Font::drawTextUsingSVGFont):
+        (WebCore::Font::selectionRectForTextUsingSVGFont):
+        * svg/SVGFontElement.cpp:
+        (WebCore::SVGFontElement::invalidateGlyphCache):
+        (WebCore::SVGFontElement::ensureGlyphCache):
+        (WebCore::parseUnicodeRange):
+        (WebCore::parseUnicodeRangeList):
+        (WebCore::stringMatchesUnicodeRange):
+        (WebCore::matches):
+        (WebCore::SVGFontElement::getHorizontalKerningPairForStringsAndGlyphs):
+        * svg/SVGFontElement.h:
+        * svg/SVGGlyphElement.cpp:
+        (WebCore::SVGGlyphElement::insertedIntoDocument):
+        (WebCore::SVGGlyphElement::removedFromDocument):
+        * svg/SVGHKernElement.cpp: Added.
+        (WebCore::SVGHKernElement::SVGHKernElement):
+        (WebCore::SVGHKernElement::~SVGHKernElement):
+        (WebCore::SVGHKernElement::insertedIntoDocument):
+        (WebCore::SVGHKernElement::removedFromDocument):
+        (WebCore::SVGHKernElement::buildHorizontalKerningPair):
+        * svg/SVGHKernElement.h: Added.
+        (WebCore::SVGHorizontalKerningPair::SVGHorizontalKerningPair):
+        (WebCore::SVGHKernElement::rendererIsNeeded):
+        * svg/SVGHKernElement.idl: Added.
+        * svg/SVGTextContentElement.cpp:
+        (WebCore::cummulatedCharacterRangeLength):
+        (WebCore::SVGInlineTextBoxQueryWalker::chunkPortionCallback):
+        * svg/svgtags.in:
+
 2008-03-26  Sam Weinig  <sam@webkit.org>
 
         Reviewed by Adam Roben.
index fedb204e32a1de2104f1f48d8d0b18505ea44e7c..e209464a75edc40223905ed6a739da19640e9b6b 100644 (file)
@@ -250,6 +250,7 @@ DOM_CLASSES = \
     SVGGElement \
     SVGGlyphElement \
     SVGGradientElement \
+    SVGHKernElement \
     SVGImageElement \
     SVGLangSpace \
     SVGLength \
index cdfb2371c55edced9cc6fcbdb7d40e099178a1be..3b47a0cd3a05b6a3eae7ba2c72dcf3d4fb12a289 100644 (file)
                                RelativePath="..\svg\SVGAltGlyphElement.h"\r
                                >\r
                        </File>\r
+                       <File\r
+                               RelativePath="..\svg\SVGHKernElement.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\svg\SVGHKernElement.h"\r
+                               >\r
+                       </File>\r
                        <File\r
                                RelativePath="..\svg\SVGAngle.cpp"\r
                                >\r
index cfb0b75116f4c5264fbb218a6a8a0d4f85de2d26..892312ec746616ebdfa46850cfc9f4018a4af34b 100644 (file)
                5DFE8F570D16477C0076E937 /* ScheduledAction.h in Headers */ = {isa = PBXBuildFile; fileRef = BCA378BB0D15F64200B793D6 /* ScheduledAction.h */; };
                650F53DC09D15DDA00C9B0C8 /* CSSGrammar.h in Headers */ = {isa = PBXBuildFile; fileRef = 650F53DB09D15DDA00C9B0C8 /* CSSGrammar.h */; };
                650F53DD09D15DDF00C9B0C8 /* CSSGrammar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6565814409D13043000E61D7 /* CSSGrammar.cpp */; };
+               650FBF2A0D9AF047008FC292 /* SVGHKernElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 650FBF270D9AF046008FC292 /* SVGHKernElement.cpp */; };
+               650FBF2B0D9AF047008FC292 /* SVGHKernElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 650FBF280D9AF047008FC292 /* SVGHKernElement.h */; };
+               650FBF2C0D9AF047008FC292 /* SVGHKernElement.idl in Resources */ = {isa = PBXBuildFile; fileRef = 650FBF290D9AF047008FC292 /* SVGHKernElement.idl */; };
                6515EC910D9723FF0063D49A /* JSSVGAltGlyphElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6515EC8F0D9723FF0063D49A /* JSSVGAltGlyphElement.cpp */; };
                6515EC920D9723FF0063D49A /* JSSVGAltGlyphElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 6515EC900D9723FF0063D49A /* JSSVGAltGlyphElement.h */; };
                6550B69D099DF0270090D781 /* CDATASection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6550B693099DF0270090D781 /* CDATASection.cpp */; };
                550A0BC8085F6039007353D6 /* QualifiedName.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = QualifiedName.h; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
                5DCF836C0D59159800953BC6 /* PluginInfoStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PluginInfoStore.h; sourceTree = "<group>"; };
                650F53DB09D15DDA00C9B0C8 /* CSSGrammar.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CSSGrammar.h; sourceTree = "<group>"; };
+               650FBF270D9AF046008FC292 /* SVGHKernElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SVGHKernElement.cpp; sourceTree = "<group>"; };
+               650FBF280D9AF047008FC292 /* SVGHKernElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVGHKernElement.h; sourceTree = "<group>"; };
+               650FBF290D9AF047008FC292 /* SVGHKernElement.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = SVGHKernElement.idl; sourceTree = "<group>"; };
                6515EC8F0D9723FF0063D49A /* JSSVGAltGlyphElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSSVGAltGlyphElement.cpp; sourceTree = "<group>"; };
                6515EC900D9723FF0063D49A /* JSSVGAltGlyphElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSSVGAltGlyphElement.h; sourceTree = "<group>"; };
                6548E24809E1E04D00AF8020 /* Document.idl */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = Document.idl; sourceTree = "<group>"; };
                                B22277FE0D00BF1F0071B782 /* SVGAnimateTransformElement.cpp */,
                                B22277FF0D00BF1F0071B782 /* SVGAnimateTransformElement.h */,
                                B22278000D00BF1F0071B782 /* SVGAnimateTransformElement.idl */,
+                               650FBF270D9AF046008FC292 /* SVGHKernElement.cpp */,
+                               650FBF280D9AF047008FC292 /* SVGHKernElement.h */,
+                               650FBF290D9AF047008FC292 /* SVGHKernElement.idl */,
                                B22278010D00BF1F0071B782 /* SVGAnimationElement.cpp */,
                                B22278020D00BF1F0071B782 /* SVGAnimationElement.h */,
                                B22278030D00BF1F0071B782 /* SVGAnimationElement.idl */,
                                BCE1C41B0D982980003B02F2 /* Location.h in Headers */,
                                BCE1C43C0D9830D3003B02F2 /* JSLocation.h in Headers */,
                                E415F1840D9A1A830033CE97 /* ElementTimeControl.h in Headers */,
+                               650FBF2B0D9AF047008FC292 /* SVGHKernElement.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                A779791B0D6B9D0C003851B9 /* ImageData.idl in Resources */,
                                BCA83E370D7CDC4E003421A8 /* Clipboard.idl in Resources */,
                                65653F2F0D9727D200CA9723 /* SVGAltGlyphElement.idl in Resources */,
+                               650FBF2C0D9AF047008FC292 /* SVGHKernElement.idl in Resources */,
                                BCE1C4230D9829F2003B02F2 /* Location.idl in Resources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                                6515EC910D9723FF0063D49A /* JSSVGAltGlyphElement.cpp in Sources */,
                                65653F2D0D9727D200CA9723 /* SVGAltGlyphElement.cpp in Sources */,
                                65AA6BB00D974A00000541AE /* DOMSVGAltGlyphElement.mm in Sources */,
+                               650FBF2A0D9AF047008FC292 /* SVGHKernElement.cpp in Sources */,
                                BCE1C41C0D982981003B02F2 /* Location.cpp in Sources */,
                                BCE1C43B0D9830D3003B02F2 /* JSLocation.cpp in Sources */,
                                BCE1C4400D9830F4003B02F2 /* JSLocationCustom.cpp in Sources */,
index 3dc7e370f39275472b3bea398d258651039b11a8..f10cc8a40c911c93530ddb3de1a3789a1e2a4083 100644 (file)
@@ -708,14 +708,15 @@ float Font::floatWidth(const TextRun& run) const
     return floatWidthForComplexText(run);
 }
 
-float Font::floatWidth(const TextRun& run, int extraCharsAvailable, int& charsConsumed) const
+float Font::floatWidth(const TextRun& run, int extraCharsAvailable, int& charsConsumed, String& glyphName) const
 {
 #if ENABLE(SVG_FONTS)
     if (primaryFont()->isSVGFont())
-        return floatWidthUsingSVGFont(run, extraCharsAvailable, charsConsumed);
+        return floatWidthUsingSVGFont(run, extraCharsAvailable, charsConsumed, glyphName);
 #endif
 
     charsConsumed = run.length();
+    glyphName = "";
     if (canUseGlyphCache(run))
         return floatWidthForSimpleText(run, 0);
     return floatWidthForComplexText(run);
@@ -814,6 +815,13 @@ int Font::offsetForPositionForSimpleText(const TextRun& run, int x, bool include
     return offset;
 }
 
+#if ENABLE(SVG_FONTS)
+bool Font::isSVGFont() const
+{ 
+    return primaryFont()->isSVGFont(); 
+}
+#endif
+
 FontSelector* Font::fontSelector() const
 {
     return m_fontList ? m_fontList->fontSelector() : 0;
index 35a9674028315a24ac367e6b4581d9251c94be42..1a676ad5b5c21b2db8137416ab4d2154a664a0a4 100644 (file)
@@ -48,6 +48,7 @@ class GraphicsContext;
 class IntPoint;
 class RenderObject;
 class SimpleFontData;
+class SVGFontElement;
 class SVGPaintServer;
 
 struct GlyphData;
@@ -170,7 +171,7 @@ public:
 
     int width(const TextRun&) const;
     float floatWidth(const TextRun&) const;
-    float floatWidth(const TextRun& run, int extraCharsAvailable, int& charsConsumed) const;
+    float floatWidth(const TextRun& run, int extraCharsAvailable, int& charsConsumed, String& glyphName) const;
 
     int offsetForPosition(const TextRun&, int position, bool includePartialGlyphs) const;
     FloatRect selectionRectForText(const TextRun&, const IntPoint&, int h, int from = 0, int to = -1) const;
@@ -235,7 +236,7 @@ private:
 #if ENABLE(SVG_FONTS)
     void drawTextUsingSVGFont(GraphicsContext*, const TextRun&, const FloatPoint&, int from, int to) const;
     float floatWidthUsingSVGFont(const TextRun&) const;
-    float floatWidthUsingSVGFont(const TextRun&, int extraCharsAvailable, int& charsConsumed) const;
+    float floatWidthUsingSVGFont(const TextRun&, int extraCharsAvailable, int& charsConsumed, String& glyphName) const;
     FloatRect selectionRectForTextUsingSVGFont(const TextRun&, const IntPoint&, int h, int from, int to) const;
     int offsetForPositionForTextUsingSVGFont(const TextRun&, int position, bool includePartialGlyphs) const;
 #endif
@@ -269,6 +270,12 @@ public:
 #endif
     static bool treatAsSpace(UChar c) { return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0; }
     static bool treatAsZeroWidthSpace(UChar c) { return c < 0x20 || (c >= 0x7F && c < 0xA0) || c == 0x200e || c == 0x200f || c >= 0x202a && c <= 0x202e; }
+
+#if ENABLE(SVG_FONTS)
+    bool isSVGFont() const;
+    SVGFontElement* svgFont() const;
+#endif
+
 private:
     FontDescription m_fontDescription;
 #if !PLATFORM(QT)
index 94c57be5e671b1faa0b7f299c6cb7751d298cea8..96fd835c6b427e0933559d321e4acf03fd764e9c 100644 (file)
@@ -74,10 +74,10 @@ SVGRootInlineBox* SVGInlineTextBox::svgRootInlineBox() const
     return static_cast<SVGRootInlineBox*>(parentBox);
 }
 
-float SVGInlineTextBox::calculateGlyphWidth(RenderStyle* style, int offset, int extraCharsAvailable, int& charsConsumed) const
+float SVGInlineTextBox::calculateGlyphWidth(RenderStyle* style, int offset, int extraCharsAvailable, int& charsConsumed, String& glyphName) const
 {
     ASSERT(style);
-    return style->font().floatWidth(svgTextRunForInlineTextBox(textObject()->text()->characters() + offset, 1, style, this, 0), extraCharsAvailable, charsConsumed);
+    return style->font().floatWidth(svgTextRunForInlineTextBox(textObject()->text()->characters() + offset, 1, style, this, 0), extraCharsAvailable, charsConsumed, glyphName);
 }
 
 float SVGInlineTextBox::calculateGlyphHeight(RenderStyle* style, int offset, int extraCharsAvailable) const
@@ -98,10 +98,11 @@ FloatRect SVGInlineTextBox::calculateGlyphBoundaries(RenderStyle* style, int off
 
     // FIXME: account for multi-character glyphs
     int charsConsumed;
+    String glyphName;
     if (!m_reversed)
-        glyphWidth = calculateGlyphWidth(style, offset, 0, charsConsumed);
+        glyphWidth = calculateGlyphWidth(style, offset, 0, charsConsumed, glyphName);
     else
-        glyphWidth = calculateGlyphWidth(style, start() + end() - offset, 0, charsConsumed);
+        glyphWidth = calculateGlyphWidth(style, start() + end() - offset, 0, charsConsumed, glyphName);
 
     float x1 = svgChar.x;
     float x2 = svgChar.x + glyphWidth;
index 6aaacabe8bc3d08af49ef2e45a1b216b269ff1b3..1ddc23a61deab5dd5c572317ac1a4b675a72c39b 100644 (file)
@@ -58,7 +58,7 @@ namespace WebCore {
         SVGRootInlineBox* svgRootInlineBox() const;
 
         // Helper functions shared with SVGRootInlineBox     
-        float calculateGlyphWidth(RenderStyle* style, int offset, int extraCharsAvailable, int& charsConsumed) const;
+        float calculateGlyphWidth(RenderStyle* style, int offset, int extraCharsAvailable, int& charsConsumed, String& glyphName) const;
         float calculateGlyphHeight(RenderStyle*, int offset, int extraCharsAvailable) const;
 
         FloatRect calculateGlyphBoundaries(RenderStyle*, int offset, const SVGChar&) const;
index e706ab26957f37a9c78467fbf3aedfa8556c9e84..e8e445b2654c0e1c874402b4d1493e2b0ed49632 100644 (file)
@@ -33,6 +33,7 @@
 #include "RenderSVGRoot.h"
 #include "SVGInlineFlowBox.h"
 #include "SVGInlineTextBox.h"
+#include "SVGFontElement.h"
 #include "SVGPaintServer.h"
 #include "SVGRenderStyleDefs.h"
 #include "SVGRenderSupport.h"
@@ -712,8 +713,9 @@ static float cummulatedWidthOrHeightOfTextChunk(SVGTextChunk& chunk, bool calcWi
 
                 // FIXME: does this need to change to handle multichar glyphs?
                 int charsConsumed = 1;
+                String glyphName;
                 if (calcWidthOnly) {
-                    float lastGlyphWidth = box->calculateGlyphWidth(style, offset, 0, charsConsumed);
+                    float lastGlyphWidth = box->calculateGlyphWidth(style, offset, 0, charsConsumed, glyphName);
                     length += currentCharacter.x - lastCharacter.x - lastGlyphWidth;
                 } else {
                     float lastGlyphHeight = box->calculateGlyphHeight(style, offset, 0);
@@ -904,9 +906,11 @@ void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacter
         info.addLayoutInformation(positioningElement);
     }
 
+    LastGlyphInfo lastGlyph;
+    
     for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
         if (curr->object()->isText())
-            buildLayoutInformationForTextBox(info, static_cast<InlineTextBox*>(curr));
+            buildLayoutInformationForTextBox(info, static_cast<InlineTextBox*>(curr), lastGlyph);
         else {
             ASSERT(curr->isInlineFlowBox());
             InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
@@ -1096,7 +1100,7 @@ void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>::
     }
 }
 
-void SVGRootInlineBox::buildLayoutInformationForTextBox(SVGCharacterLayoutInfo& info, InlineTextBox* textBox)
+void SVGRootInlineBox::buildLayoutInformationForTextBox(SVGCharacterLayoutInfo& info, InlineTextBox* textBox, LastGlyphInfo& lastGlyph)
 {
     RenderText* text = textBox->textObject();
     ASSERT(text);
@@ -1124,12 +1128,16 @@ void SVGRootInlineBox::buildLayoutInformationForTextBox(SVGCharacterLayoutInfo&
 
         int extraCharsAvailable = length - i - 1;
 
+        String unicodeStr;
+        String glyphName;
         if (textBox->m_reversed) {
-            glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->end() - i, extraCharsAvailable, charsConsumed);
+            glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->end() - i, extraCharsAvailable, charsConsumed, glyphName);
             glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->end() - i, extraCharsAvailable);
+            unicodeStr = String(textBox->textObject()->text()->characters() + textBox->end() - i, charsConsumed);
         } else {
-            glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->start() + i, extraCharsAvailable, charsConsumed);
+            glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->start() + i, extraCharsAvailable, charsConsumed, glyphName);
             glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->start() + i, extraCharsAvailable);
+            unicodeStr = String(textBox->textObject()->text()->characters() + textBox->start() + i, charsConsumed);
         }
 
         bool assignedX = false;
@@ -1297,12 +1305,33 @@ void SVGRootInlineBox::buildLayoutInformationForTextBox(SVGCharacterLayoutInfo&
             }
         }
 
+        SVGFontElement* svgFont;
+        if (style->font().isSVGFont())
+            svgFont = style->font().svgFont();
+
+        double kerning = 0.0;
+        if (lastGlyph.isValid && style->font().isSVGFont()) {
+            SVGHorizontalKerningPair kerningPair;
+            if (svgFont->getHorizontalKerningPairForStringsAndGlyphs(lastGlyph.unicode, lastGlyph.glyphName, unicodeStr, glyphName, kerningPair))
+                kerning = kerningPair.kerning;
+        }
+
+        if (style->font().isSVGFont()) {
+            lastGlyph.unicode = unicodeStr;
+            lastGlyph.glyphName = glyphName;
+            lastGlyph.isValid = true;
+        } else {
+            lastGlyph.isValid = false;
+        }
+        
+        svgChar.x -= (float)kerning;
+
         // Advance to new position
         if (isVerticalText) {
             svgChar.drawnSeperated = true;
             info.cury += glyphAdvance + spacing;
         } else
-            info.curx += glyphAdvance + spacing;
+            info.curx += glyphAdvance + spacing - (float)kerning;
 
         // Advance to next character group
         for (int k = 0; k < charsConsumed; ++k) {
index ef81fb0f94f235ad27d7111640003407b548eeee..800664b84dba067933434c82b93ef084c01863f1 100644 (file)
@@ -35,6 +35,14 @@ class InlineTextBox;
 class RenderSVGRoot;
 class SVGInlineTextBox;
 
+struct LastGlyphInfo {
+    LastGlyphInfo() : isValid(false) { }
+
+    String unicode;
+    String glyphName;
+    bool isValid;
+};
+
 class SVGRootInlineBox : public RootInlineBox {
 public:
     SVGRootInlineBox(RenderObject* obj)
@@ -63,7 +71,7 @@ private:
     void layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>::iterator& it, int& minX, int& maxX, int& minY, int& maxY);
 
     void buildLayoutInformation(InlineFlowBox* start, SVGCharacterLayoutInfo&);
-    void buildLayoutInformationForTextBox(SVGCharacterLayoutInfo&, InlineTextBox*);
+    void buildLayoutInformationForTextBox(SVGCharacterLayoutInfo&, InlineTextBox*, LastGlyphInfo&);
 
     void buildTextChunks(Vector<SVGChar>&, Vector<SVGTextChunk>&, InlineFlowBox* start);
     void buildTextChunks(Vector<SVGChar>&, InlineFlowBox* start, SVGTextChunkLayoutInfo&);
index 403e65add0590e4a06de069a09197669479948fb..9406a4d0d510b28ec73f029da95b7cb289c91519 100644 (file)
@@ -290,7 +290,8 @@ struct SVGTextRunWalker {
                     ASSERT(characterLookupRange > 0);
                     i += identifier.nameLength - 1;
                     m_walkerData.charsConsumed += identifier.nameLength;
-                    
+                    m_walkerData.glyphName = identifier.glyphName;
+
                     foundGlyph = true;
                     SVGGlyphElement::inheritUnspecifiedAttributes(identifier, m_fontData);
                     break;
@@ -336,6 +337,7 @@ struct SVGTextRunWalkerMeasuredLengthData {
     int to;
     int extraCharsAvailable;
     int charsConsumed;
+    String glyphName;
 
     float scale;
     float length;
@@ -362,7 +364,21 @@ void floatWidthMissingGlyphCallback(const TextRun& run, SVGTextRunWalkerMeasured
     data.length += font.floatWidth(run);
 }
 
-static float floatWidthOfSubStringUsingSVGFont(const Font* font, const TextRun& run, int extraCharsAvailable, int from, int to, int& charsConsumed)
+
+SVGFontElement* Font::svgFont() const
+{ 
+    if (!isSVGFont())
+        return 0;
+
+    SVGFontElement* fontElement = 0;
+    SVGFontFaceElement* fontFaceElement = 0;
+    if (svgFontAndFontFaceElementForFontData(primaryFont(), fontFaceElement, fontElement))
+        return fontElement;
+    
+    return 0;
+}
+
+static float floatWidthOfSubStringUsingSVGFont(const Font* font, const TextRun& run, int extraCharsAvailable, int from, int to, int& charsConsumed, String& glyphName)
 {
     int newFrom = to > from ? from : to;
     int newTo = to > from ? to : from;
@@ -402,6 +418,7 @@ static float floatWidthOfSubStringUsingSVGFont(const Font* font, const TextRun&
         SVGTextRunWalker<SVGTextRunWalkerMeasuredLengthData> runWalker(fontData, fontElement, data, floatWidthUsingSVGFontCallback, floatWidthMissingGlyphCallback);
         runWalker.walk(run, isVerticalText, language, 0, run.length());
         charsConsumed = data.charsConsumed;
+        glyphName = data.glyphName;
         return data.length;
     }
 
@@ -411,12 +428,13 @@ static float floatWidthOfSubStringUsingSVGFont(const Font* font, const TextRun&
 float Font::floatWidthUsingSVGFont(const TextRun& run) const
 {
     int charsConsumed;
-    return floatWidthOfSubStringUsingSVGFont(this, run, 0, 0, run.length(), charsConsumed);
+    String glyphName;
+    return floatWidthOfSubStringUsingSVGFont(this, run, 0, 0, run.length(), charsConsumed, glyphName);
 }
 
-float Font::floatWidthUsingSVGFont(const TextRun& run, int extraCharsAvailable, int& charsConsumed) const
+float Font::floatWidthUsingSVGFont(const TextRun& run, int extraCharsAvailable, int& charsConsumed, String& glyphName) const
 {
-    return floatWidthOfSubStringUsingSVGFont(this, run, extraCharsAvailable, 0, run.length(), charsConsumed);
+    return floatWidthOfSubStringUsingSVGFont(this, run, extraCharsAvailable, 0, run.length(), charsConsumed, glyphName);
 }
 
 // Callback & data structures to draw text using SVG Fonts
@@ -425,6 +443,7 @@ struct SVGTextRunWalkerDrawTextData {
     bool isVerticalText;
     int extraCharsAvailable;
     int charsConsumed;
+    String glyphName;
 
     float xStartOffset;
     FloatPoint currentPoint;
@@ -524,8 +543,9 @@ void Font::drawTextUsingSVGFont(GraphicsContext* context, const TextRun& run,
         ASSERT(data.activePaintServer);
 
         int charsConsumed;
+        String glyphName;
         data.isVerticalText = false;
-        data.xStartOffset = floatWidthOfSubStringUsingSVGFont(this, run, 0, run.rtl() ? to : 0, run.rtl() ? run.length() : from, charsConsumed);
+        data.xStartOffset = floatWidthOfSubStringUsingSVGFont(this, run, 0, run.rtl() ? to : 0, run.rtl() ? run.length() : from, charsConsumed, glyphName);
         data.glyphOrigin = FloatPoint();
         data.context = context;
 
@@ -554,8 +574,10 @@ void Font::drawTextUsingSVGFont(GraphicsContext* context, const TextRun& run,
 FloatRect Font::selectionRectForTextUsingSVGFont(const TextRun& run, const IntPoint& point, int height, int from, int to) const
 {
     int charsConsumed;
-    return FloatRect(point.x() + floatWidthOfSubStringUsingSVGFont(this, run, 0, run.rtl() ? to : 0, run.rtl() ? run.length() : from, charsConsumed),
-                     point.y(), floatWidthOfSubStringUsingSVGFont(this, run, 0, from, to, charsConsumed), height);
+    String glyphName;
+
+    return FloatRect(point.x() + floatWidthOfSubStringUsingSVGFont(this, run, 0, run.rtl() ? to : 0, run.rtl() ? run.length() : from, charsConsumed, glyphName),
+                     point.y(), floatWidthOfSubStringUsingSVGFont(this, run, 0, from, to, charsConsumed, glyphName), height);
 }
 
 int Font::offsetForPositionForTextUsingSVGFont(const TextRun&, int position, bool includePartialGlyphs) const
index b76b5897cf60a9dc6770fd41055c9c1381bbfa58..d304c87dd30741c1a2f57e4a2aa091f397de1d14 100644 (file)
@@ -29,6 +29,9 @@
 #include "SVGMissingGlyphElement.h"
 #include "SVGNames.h"
 #include "SVGParserUtilities.h"
+#include <wtf/ASCIICType.h>
+
+using namespace WTF;
 
 namespace WebCore {
 
@@ -44,17 +47,12 @@ SVGFontElement::~SVGFontElement()
 {
 }
 
-void SVGFontElement::addGlyphToCache(SVGGlyphElement* glyphElement)
-{
-    if (m_isGlyphCacheValid)
-        m_glyphMap.clear();
-    m_isGlyphCacheValid = false;
-}
-
-void SVGFontElement::removeGlyphFromCache(SVGGlyphElement* glyphElement)
+void SVGFontElement::invalidateGlyphCache()
 {
-    if (m_isGlyphCacheValid)
+    if (m_isGlyphCacheValid) {
         m_glyphMap.clear();
+        m_kerningPairs.clear();
+    }
     m_isGlyphCacheValid = false;
 }
 
@@ -79,11 +77,159 @@ void SVGFontElement::ensureGlyphCache() const
             String unicode = glyph->getAttribute(unicodeAttr);
             if (unicode.length())
                 m_glyphMap.add(unicode, glyph->buildGlyphIdentifier());
+        } else if (child->hasTagName(hkernTag)) {
+            SVGHKernElement* hkern = static_cast<SVGHKernElement*>(child);
+            SVGHorizontalKerningPair kerningPair = hkern->buildHorizontalKerningPair();
+            m_kerningPairs.append(kerningPair);
         }
     }
         
     m_isGlyphCacheValid = true;
 }
+    
+// Returns the number of characters consumed or 0 if no range was found.
+static unsigned parseUnicodeRange(const UChar* characters, unsigned length, pair<unsigned, unsigned>& range)
+{
+    if (length < 2)
+        return 0;
+    if (characters[0] != 'U')
+        return 0;
+    if (characters[1] != '+')
+        return 0;
+    
+    // Parse the starting hex number (or its prefix).
+    unsigned start = 0;
+    unsigned startLength = 0;
+    for (unsigned i = 2; i < length; ++i) {
+        if (!isASCIIHexDigit(characters[i]))
+            break;
+        if (++startLength > 6)
+            return 0;
+        start = (start << 4) | toASCIIHexValue(characters[i]);
+    }
+    
+    // Handle the case of ranges separated by "-" sign.
+    if (2 + startLength < length && characters[2 + startLength] == '-') {
+        if (!startLength)
+            return 0;
+        
+        // Parse the ending hex number (or its prefix).
+        unsigned end = 0;
+        unsigned endLength = 0;
+        for (unsigned i = 2 + startLength + 1; i < length; ++i) {
+            if (!isASCIIHexDigit(characters[i]))
+                break;
+            if (++endLength > 6)
+                return 0;
+            end = (end << 4) | toASCIIHexValue(characters[i]);
+        }
+        
+        if (!endLength)
+            return 0;
+        
+        range.first = start;
+        range.second = end;
+        return 2 + startLength + 1 + endLength;
+    }
+    
+    // Handle the case of a number with some optional trailing question marks.
+    unsigned end = start;
+    for (unsigned i = 2 + startLength; i < length; ++i) {
+        if (characters[i] != '?')
+            break;
+        if (++startLength > 6)
+            return 0;
+        start <<= 4;
+        end = (end << 4) | 0xF;
+    }
+    
+    if (!startLength)
+        return 0;
+    
+    range.first = start;
+    range.second = end;
+    return 2 + startLength;
+}
+    
+static bool parseUnicodeRangeList(const UChar* characters, unsigned length, Vector<pair<unsigned, unsigned> >& ranges)
+{
+    ranges.clear();
+    if (!length)
+        return true;
+    
+    const UChar* remainingCharacters = characters;
+    unsigned remainingLength = length;
+    
+    while (1) {
+        pair<unsigned, unsigned> range;
+        unsigned charactersConsumed = parseUnicodeRange(remainingCharacters, remainingLength, range);
+        if (charactersConsumed) {
+            ranges.append(range);
+            remainingCharacters += charactersConsumed;
+            remainingLength -= charactersConsumed;
+        } else {
+            if (!remainingLength)
+                return false;
+            UChar character = remainingCharacters[0];
+            if (character == ',')
+                return false;
+            ranges.append(make_pair(character, character));
+            ++remainingCharacters;
+            --remainingLength;
+        }
+        if (!remainingLength)
+            return true;
+        if (remainingCharacters[0] != ',')
+            return false;
+        ++remainingCharacters;
+        --remainingLength;
+    }
+}
+
+static bool stringMatchesUnicodeRange(const String& unicodeString, const String& unicodeRangeSpec)
+{
+    Vector<pair<unsigned, unsigned> > ranges;
+    if (!parseUnicodeRangeList(unicodeRangeSpec.characters(), unicodeRangeSpec.length(), ranges))
+        return false;
+    
+    if (unicodeString.length() != ranges.size())
+        return false;
+    
+    for (size_t i = 0; i < unicodeString.length(); ++i) {
+        UChar c = unicodeString[i];
+        if (c < ranges[i].first || c > ranges[i].second)
+            return false;
+    }
+    
+    return true;
+}
+    
+static bool matches(const String& u1, const String& g1, const String& u2, const String& g2, const SVGHorizontalKerningPair& kerningPair)
+{
+    if (kerningPair.unicode1.length() && !stringMatchesUnicodeRange(u1, kerningPair.unicode1))
+        return false;
+    if (kerningPair.glyphName1.length() && kerningPair.glyphName1 != g1)
+        return false;
+    
+    if (kerningPair.unicode2.length() && !stringMatchesUnicodeRange(u2, kerningPair.unicode2))
+        return false;
+    if (kerningPair.glyphName2.length() && kerningPair.glyphName2 != g2)
+        return false;
+    
+    return true;
+}
+    
+bool SVGFontElement::getHorizontalKerningPairForStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2, SVGHorizontalKerningPair& kerningPair) const
+{
+    for (size_t i = 0; i < m_kerningPairs.size(); ++i) {
+        if (matches(u1, g1, u2, g2, m_kerningPairs[i])) {
+            kerningPair = m_kerningPairs[i];
+            return true;
+        }        
+    }
+    
+    return false;
+}
 
 void SVGFontElement::getGlyphIdentifiersForString(const String& string, Vector<SVGGlyphIdentifier>& glyphs) const
 {
index 8223ada7a5d10eb739093f77ea88af134a55aab8..4d0370b7d0e3a06bf45aff2c3a7f526d3a58cdac 100644 (file)
@@ -25,6 +25,7 @@
 #include "SVGExternalResourcesRequired.h"
 #include "SVGGlyphElement.h"
 #include "SVGGlyphMap.h"
+#include "SVGHKernElement.h"
 #include "SVGStyledElement.h"
 
 namespace WebCore {
@@ -39,16 +40,20 @@ namespace WebCore {
         virtual bool rendererIsNeeded(RenderStyle*) { return false; }    
         virtual const SVGElement* contextElement() const { return this; }
 
-        void addGlyphToCache(SVGGlyphElement*);
-        void removeGlyphFromCache(SVGGlyphElement*);
+        void invalidateGlyphCache();
 
         void getGlyphIdentifiersForString(const String&, Vector<SVGGlyphIdentifier>&) const;
 
+        bool getHorizontalKerningPairForStringsAndGlyphs(const String& u1, const String& g1, const String& u2, const String& g2, SVGHorizontalKerningPair& kerningPair) const;
+
         SVGMissingGlyphElement* firstMissingGlyphElement() const;
 
     private:
         void ensureGlyphCache() const;
 
+        typedef Vector<SVGHorizontalKerningPair> KerningPairVector;
+
+        mutable KerningPairVector m_kerningPairs;
         mutable SVGGlyphMap m_glyphMap;
         mutable bool m_isGlyphCacheValid;
     };
index 99aaccc3e5fbaa011ee6a06867d773e097dc3f04..a1a8008c24ffe56402e33251500c3c1a3abc4948 100644 (file)
@@ -49,7 +49,7 @@ void SVGGlyphElement::insertedIntoDocument()
     Node* fontNode = parentNode();
     if (fontNode && fontNode->hasTagName(fontTag)) {
         if (SVGFontElement* element = static_cast<SVGFontElement*>(fontNode))
-            element->addGlyphToCache(this);
+            element->invalidateGlyphCache();
     }
     SVGStyledElement::insertedIntoDocument();
 }
@@ -59,7 +59,7 @@ void SVGGlyphElement::removedFromDocument()
     Node* fontNode = parentNode();
     if (fontNode && fontNode->hasTagName(fontTag)) {
         if (SVGFontElement* element = static_cast<SVGFontElement*>(fontNode))
-            element->removeGlyphFromCache(this);
+            element->invalidateGlyphCache();
     }
     SVGStyledElement::removedFromDocument();
 }
diff --git a/WebCore/svg/SVGHKernElement.cpp b/WebCore/svg/SVGHKernElement.cpp
new file mode 100644 (file)
index 0000000..f232fdc
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+   Copyright (C) 2007 Eric Seidel <eric@webkit.org>
+   Copyright (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
+   Copyright (C) 2008 Eric Seidel <eric@webkit.org>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.
+*/
+
+#include "config.h"
+
+#if ENABLE(SVG_FONTS)
+#include "SVGHKernElement.h"
+
+#include "SVGFontElement.h"
+#include "SVGFontFaceElement.h"
+#include "SVGFontData.h"
+#include "SVGNames.h"
+#include "SVGParserUtilities.h"
+#include "SimpleFontData.h"
+#include "XMLNames.h"
+
+namespace WebCore {
+
+using namespace SVGNames;
+
+SVGHKernElement::SVGHKernElement(const QualifiedName& tagName, Document* doc)
+    : SVGElement(tagName, doc)
+{
+}
+
+SVGHKernElement::~SVGHKernElement()
+{
+}
+
+void SVGHKernElement::insertedIntoDocument()
+{
+    Node* fontNode = parentNode();
+    if (fontNode && fontNode->hasTagName(fontTag)) {
+        if (SVGFontElement* element = static_cast<SVGFontElement*>(fontNode))
+            element->invalidateGlyphCache();
+    }
+}
+
+void SVGHKernElement::removedFromDocument()
+{
+    Node* fontNode = parentNode();
+    if (fontNode && fontNode->hasTagName(fontTag)) {
+        if (SVGFontElement* element = static_cast<SVGFontElement*>(fontNode))
+            element->invalidateGlyphCache();
+    }
+}
+
+SVGHorizontalKerningPair SVGHKernElement::buildHorizontalKerningPair() const
+{
+    SVGHorizontalKerningPair kerningPair;
+
+    kerningPair.unicode1 = getAttribute(u1Attr);
+    kerningPair.glyphName1 = getAttribute(g1Attr);
+    kerningPair.unicode2 = getAttribute(u2Attr);
+    kerningPair.glyphName2 = getAttribute(g2Attr);
+    kerningPair.kerning = getAttribute(kAttr).string().toDouble();
+
+    return kerningPair;
+}
+
+}
+
+#endif // ENABLE(SVG_FONTS)
diff --git a/WebCore/svg/SVGHKernElement.h b/WebCore/svg/SVGHKernElement.h
new file mode 100644 (file)
index 0000000..6fda779
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+   Copyright (C) 2007 Eric Seidel <eric@webkit.org>
+   Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+   Copyright (C) 2008 Apple, Inc
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.
+ */
+
+#ifndef SVGHKernElement_h
+#define SVGHKernElement_h
+
+#if ENABLE(SVG_FONTS)
+#include "SVGStyledElement.h"
+
+#include <limits>
+#include "Path.h"
+
+namespace WebCore {
+
+    class AtomicString;
+    struct SVGFontData;
+
+    // Describe an SVG <hkern> element
+    struct SVGHorizontalKerningPair {
+        String unicode1;
+        String glyphName1;
+        String unicode2;
+        String glyphName2;
+        double kerning;
+        
+        SVGHorizontalKerningPair()
+            : kerning(0)
+        {
+        }
+    };
+
+    class SVGHKernElement : public SVGElement {
+    public:
+        SVGHKernElement(const QualifiedName&, Document*);
+        virtual ~SVGHKernElement();
+
+        virtual void insertedIntoDocument();
+        virtual void removedFromDocument();
+
+        virtual bool rendererIsNeeded(RenderStyle*) { return false; }
+
+        SVGHorizontalKerningPair buildHorizontalKerningPair() const;
+    };
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG_FONTS)
+#endif
diff --git a/WebCore/svg/SVGHKernElement.idl b/WebCore/svg/SVGHKernElement.idl
new file mode 100644 (file)
index 0000000..1971aef
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+    Copyright (C) 2008 Apple Computer, Inc.  All rights reserved.
+
+    This file is part of the KDE project
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+module svg {
+
+    interface [Conditional=SVG&SVG_FONTS] SVGHKernElement : SVGElement {
+    };
+
+}
index f03fe78f68eefade0ba64996cdc500799aec04f9..16383acb7fd2111c8f8560ae3c1e303924749c31 100644 (file)
@@ -81,10 +81,11 @@ static inline float cummulatedCharacterRangeLength(const Vector<SVGChar>::iterat
 
             // FIXME: does this handle multichar glyphs ok? not sure
             int charsConsumed = 0;
+            String glyphName;
             if (isVerticalText)
                 textLength += textBox->calculateGlyphHeight(style, newOffset, 0);
             else
-                textLength += textBox->calculateGlyphWidth(style, newOffset, 0, charsConsumed);
+                textLength += textBox->calculateGlyphWidth(style, newOffset, 0, charsConsumed, glyphName);
         }
 
         if (!usesFullRange) {
@@ -196,10 +197,11 @@ struct SVGInlineTextBoxQueryWalker {
                         newOffset = textBox->start() + textBox->end() - newOffset;
 
                     int charsConsumed;
+                    String glyphName;
                     if (isVerticalText)
                         m_queryPointResult.move(it->x, it->y + textBox->calculateGlyphHeight(style, newOffset, end - it));
                     else
-                        m_queryPointResult.move(it->x + textBox->calculateGlyphWidth(style, newOffset, end - it, charsConsumed), it->y);
+                        m_queryPointResult.move(it->x + textBox->calculateGlyphWidth(style, newOffset, end - it, charsConsumed, glyphName), it->y);
 
                     m_stopProcessing = true;
                     return;
index ed613c6d7a11f79bb858a103cc327569243835a6..4c8220d3bc39bd5931420b21a2f265e8522bb1c8 100644 (file)
@@ -70,8 +70,8 @@ glyph
 #endif
 #if 0
 glyphRef
-hkern
 #endif
+hkern
 image
 line
 linearGradient