Closed Bug 282579 Opened 20 years ago Closed 19 years ago

Implement <svg:textPath>

Categories

(Core :: SVG, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED

People

(Reporter: ortylp, Assigned: tor)

Details

(Keywords: fixed1.8.1)

Attachments

(12 files, 5 obsolete files)

494 bytes, image/svg+xml
Details
5.30 KB, image/svg+xml
Details
148.20 KB, patch
alex
: review+
Details | Diff | Splinter Review
147.12 KB, patch
Details | Diff | Splinter Review
147.13 KB, patch
Details | Diff | Splinter Review
8.04 KB, patch
Details | Diff | Splinter Review
1.33 KB, patch
jwatt
: review+
Details | Diff | Splinter Review
2.19 KB, image/svg+xml
Details
1.01 KB, patch
roc
: review+
Details | Diff | Splinter Review
5.29 KB, image/svg+xml
Details
4.91 KB, patch
alex
: review+
Details | Diff | Splinter Review
1.17 KB, text/xml
Details
User-Agent:       Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5) Gecko/20050210 Firefox/1.0 (Debian package 1.0+dfsg.1-6)
Build Identifier: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5) Gecko/20050105 Debian/1.7.5-1

The text on the path in the example below is not shown, the arc should be part
of a circle, it is not.

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20001102//EN"
"http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd">
<svg viewBox="0 0 200 100" zoomAndPan="disable" 
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <path id="p" d="M15 100a85 85 0 0 1 170 0" stroke="blue" fill="none"/>
  <text fill="black" text-anchor="middle">
  <textPath xlink:href="#p" startOffset="50%">centered text on a path</textPath>
  </text>
  <text x="100" y="100" text-anchor="middle">centered text in the middle</text>
</svg>


Reproducible: Always

Steps to Reproduce:
1. save the above example to .svg file
2. open in mozilla 
3.

Actual Results:  
the text on the path is not displayed

Expected Results:  
the text should be displayed centered on the path
(Adobe SVG plugin renders it accoding to the specs)
Version: unspecified → 1.7 Branch
Assignee: general → general
Component: General → SVG
Product: Mozilla Application Suite → Core
QA Contact: general → ian
Attached image testcase
text on a path (textPath) is not displayed - ALWAYS!!!

browser:

Deer Park 1.0+
Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.8b2) Gecko/20050531
Firefox/1.0+

test systems:
- standard Linux Fedora Core 2
- Windows 2000
Assignee: general → tor
Status: UNCONFIRMED → NEW
Ever confirmed: true
OS: Linux → All
Hardware: PC → All
Summary: SVG: text on a path (textPath) is not displayed when centered (text/@text-anchor="middle") → Implement <svg:textPath>
Version: 1.7 Branch → Trunk
Attached patch work in progress (obsolete) — Splinter Review
Snapshot of work.  Cairo only at this point.  Bounding box and hit detection
among the items not currently implemented.
Attached patch snapshot (obsolete) — Splinter Review
cairo work: stroke/clip cases, hit testing.

gdiplus work: path flattening, stubbed out metrics to compile.
Attachment #185603 - Attachment is obsolete: true
Attached patch getBBox work, some polish (obsolete) — Splinter Review
Attachment #185813 - Attachment is obsolete: true
Attachment #186161 - Flags: review?(alex)
Setting blocking-avairy1.1 - it's a useful and unique feature of SVG, and as we
have it patch form we should try to get in for 1.1.  While this is a new
feature, it's a new feature of a new feature (SVG), so the added risk is small.
Flags: blocking-aviary1.1+
Small change to nsSVGPathFrame::GetFlattenedPath per afri's comments:

NS_IMETHODIMP
nsSVGPathFrame::GetFlattenedPath(nsSVGPathData **data,
                                 nsIFrame *parent)
{
  nsIFrame *oldParent = mParent;
  nsCOMPtr<nsISVGRendererRegion> dirty_region;

  if (parent)
    mParent = parent;
  else
    SetMatrixPropagation(PR_FALSE);

  GetGeometry()->Update(nsISVGGeometrySource::UPDATEMASK_CANVAS_TM,
                        getter_AddRefs(dirty_region));

  GetGeometry()->Flatten(data);

  if (parent)
    mParent = oldParent;
  else
    SetMatrixPropagation(PR_TRUE);

  GetGeometry()->Update(nsISVGGeometrySource::UPDATEMASK_CANVAS_TM,
                        getter_AddRefs(dirty_region));

  return NS_OK;
}
Comment on attachment 186161 [details] [diff] [review]
getBBox work, some polish

>Index: dom/public/idl/svg/nsIDOMSVGTextPathElement.idl
>===================================================================
>[...]
>+[scriptable, uuid(5c29a76c-3489-48fe-b9ea-ea0f5b196dff)]
>+interface nsIDOMSVGTextPathElement
>+  : nsIDOMSVGTextContentElement
>+/*
>+        The SVG DOM makes use of multiple interface inheritance.
>+        Since XPCOM only supports single interface inheritance,
>+        the best thing that we can do is to promise that whenever
>+        an object implements _this_ interface it will also
>+        implement the following interfaces. (We then have to QI to
>+        hop between them.)
>+        
>+    nsIDOMSVGURIReference,
>+*/
>+{
>+  readonly attribute nsIDOMSVGAnimatedLength startOffset;
>+};

What about 
 readonly attribute SVGAnimatedEnumeration method; &
 readonly attribute SVGAnimatedEnumeration spacing;
?

>Index: layout/svg/base/src/nsSVGGlyphFrame.cpp
>===================================================================
>[...]
>+static void
>+FindPoint(nsSVGPathData *data,
>+          float aX, float aY, float aAdvance,
>+          nsSVGCharacterPosition *aCP)
>+{
>[...]
>+/* readonly attribute nsSVGCharacterPostion characterPosition; */
>+NS_IMETHODIMP
>+nsSVGGlyphFrame::GetCharacterPosition(nsSVGCharacterPosition **aCharacterPosition)
>+{

How about maintaining segment state between invocations of FindPoint,
so that you don't have to recount from the first segment every time?
I'm thinking complex paths with lots of segments here, e.g. text
around a circle.

Also, I'm not quite sure about the check for textpathframe parents. In
GetCharacterPosition() you walk up the tree (which presumably you need
to do to support things like <textPath><tspan>...</tspan></textPath>),
but in IsAbsolutelyPositioned() you just check mParent. Is that
intended? Maybe you could tag glyph frames that are textPath children
at frame construction time instead of walking up the tree every time?

>Index: layout/svg/base/src/nsSVGPathFrame.cpp
>===================================================================
>+NS_IMETHODIMP
>+nsSVGPathFrame::GetFlattenedPath(nsSVGPathData **data,
>+                                 nsIFrame *parent)
>+{
>+  nsIFrame *oldParent = mParent;
>+  nsCOMPtr<nsISVGRendererRegion> dirty_region;
>+
>+  if (parent) {
>+    mParent = parent;
>+    GetGeometry()->Update(nsISVGGeometrySource::UPDATEMASK_CANVAS_TM,
>+                          getter_AddRefs(dirty_region));
>+  }
>+
>+  GetGeometry()->Flatten(data);
>+
>+  if (parent) {
>+    mParent = oldParent;
>+    GetGeometry()->Update(nsISVGGeometrySource::UPDATEMASK_CANVAS_TM,
>+                          getter_AddRefs(dirty_region));
>+  }
>+
>+  return NS_OK;
>+}

This is sooo nasty for GDI+ and Libart, but what the heck. Cairo's the future
:-)

>Index: layout/svg/base/src/nsSVGTextFrame.cpp
>===================================================================
>[...]
> static void
>+GetSingleValue(nsISVGGlyphFragmentLeaf *fragment,
>+               nsIDOMSVGLengthList *list, float *val)
>[...]
>+    /* check for % sizing of textpath */
>[...]
>+      if (textPath) {
>+        nsSVGPathData *data;
>+        textPath->GetFlattenedPath(&data, nsnull);
>+
>+        if (!data)
>+          return;
>+
>+        float percent;
>+        length->GetValueInSpecifiedUnits(&percent);
>+
>+        *val = data->Length()*percent/100.0f;
>+        delete data;

Hmm, ok we don't do multiple x/y's yet, but I'm beginning to wonder
whether it would be worthwhile caching the nsSVGPathData on textPaths
rather than generating it everytime. I don't know how common
individually placed percentage-based glyphs are, but I'm thinking
SVG export filters from drawing programs here. E.g. at some point
ooffice drawing app's PS export filter placed each glyph
individually...  What do you reckon?

>Index: layout/svg/renderer/public/nsISVGGlyphMetricsSource.idl
>===================================================================
>[...]
>   /**
>+   * @name Character positioning information
>+   * @{
>+   */
>+  [noscript] void GetCharacterPosition(out nsSVGCharacterPosition aCP);
    ^^^^^^^^^^
Or you could just make the whole interface unscriptable. I think some of them
already are.


>Index: layout/svg/renderer/public/nsISVGRendererGlyphGeometry.idl
>===================================================================
>[...]
>+
>+  /**
>+   * Transformed ounding box (does not include stroke width)
>+   */

*b*ounding box

>Index: layout/svg/renderer/src/gdiplus/nsSVGGDIPlusGlyphGeometry.cpp
>===================================================================
>RCS file: /cvsroot/mozilla/layout/svg/renderer/src/gdiplus/nsSVGGDIPlusGlyphGeometry.cpp,v
>retrieving revision 1.10
>diff -u -8 -p -r1.10 nsSVGGDIPlusGlyphGeometry.cpp
>--- layout/svg/renderer/src/gdiplus/nsSVGGDIPlusGlyphGeometry.cpp	25 Apr 2005 23:51:32 -0000	1.10
>+++ layout/svg/renderer/src/gdiplus/nsSVGGDIPlusGlyphGeometry.cpp	13 Jun 2005 23:08:36 -0000
>@@ -54,16 +54,19 @@ using namespace Gdiplus;
> #include "nsISVGRendererRegion.h"
> #include "nsISVGGlyphGeometrySource.h"
> #include "nsPromiseFlatString.h"
> #include "nsSVGGDIPlusGlyphMetrics.h"
> #include "nsISVGGDIPlusGlyphMetrics.h"
> #include "nsPresContext.h"
> #include "nsMemory.h"
> #include "nsSVGGDIPlusGradient.h"
>+#include "nsSVGTypeCIDs.h"
>+#include "nsIComponentManager.h"
>+#include "nsIDOMSVGRect.h"
> 
> /**
>  * \addtogroup gdiplus_renderer GDI+ Rendering Engine
>  * @{
>  */
> //////////////////////////////////////////////////////////////////////
> /**
>  * Helper class used by nsSVGGDIPlusGlyphGeometry
>@@ -831,8 +834,67 @@ nsSVGGDIPlusGlyphGeometry::UpdateRegions
>   nsCOMPtr<nsPresContext> presContext;
>   mSource->GetPresContext(getter_AddRefs(presContext));
> 
>   NS_NewSVGGDIPlusClonedRegion(getter_AddRefs(mCoveredRegion),
>                                mHitTestRegion,
>                                presContext);
> }
> 
>+NS_IMETHODIMP
>+nsSVGGDIPlusGlyphGeometry::GetBoundingBox(nsIDOMSVGRect * *aBoundingBox)
>+{
>[...]
>+  //Region region(*(metrics->GetBoundingRect()));
>+  const RectF *bounds = metrics->GetBoundingRect();
>[...]
>+  Matrix m;
>+  GetGlobalTransform(&m);
>+  m.TransformPoints(pts, 4);
>+  REAL xmin, ymin, xmax, ymax;

This will give you an inflated bounding box.
matrix->GetBoundingRect() measures in transformed space and then
converts to untransformed space. Maybe you could add a parameter to
nsSVGGDIPlusGlyphMetrics::Get(Sub)BoundingRect() that, when set, skips
restoring of the GraphicsState before obtaining the bounds (~ line
424).
Attachment #186161 - Flags: review?(alex) → review-
(In reply to comment #9)
> (From update of attachment 186161 [details] [diff] [review] [edit])
> >Index: dom/public/idl/svg/nsIDOMSVGTextPathElement.idl
> >===================================================================
> >[...]
> >+[scriptable, uuid(5c29a76c-3489-48fe-b9ea-ea0f5b196dff)]
> >+interface nsIDOMSVGTextPathElement
> >+  : nsIDOMSVGTextContentElement
> >+{
> >+  readonly attribute nsIDOMSVGAnimatedLength startOffset;
> >+};
> 
> What about 
>  readonly attribute SVGAnimatedEnumeration method; &
>  readonly attribute SVGAnimatedEnumeration spacing;
> ?

Since we only do align/exact, I thought adding the attributes would be giving
false hope to people.  I can add these and ignore them if you like.

> >Index: layout/svg/base/src/nsSVGGlyphFrame.cpp
> >===================================================================
> >[...]
> >+static void
> >+FindPoint(nsSVGPathData *data,
> >+          float aX, float aY, float aAdvance,
> >+          nsSVGCharacterPosition *aCP)
> >+{
> >[...]
> >+/* readonly attribute nsSVGCharacterPostion characterPosition; */
> >+NS_IMETHODIMP
> >+nsSVGGlyphFrame::GetCharacterPosition(nsSVGCharacterPosition
**aCharacterPosition)
> >+{
> 
> How about maintaining segment state between invocations of FindPoint,
> so that you don't have to recount from the first segment every time?
> I'm thinking complex paths with lots of segments here, e.g. text
> around a circle.
> 
> Also, I'm not quite sure about the check for textpathframe parents. In
> GetCharacterPosition() you walk up the tree (which presumably you need
> to do to support things like <textPath><tspan>...</tspan></textPath>),
> but in IsAbsolutelyPositioned() you just check mParent. Is that
> intended? Maybe you could tag glyph frames that are textPath children
> at frame construction time instead of walking up the tree every time?

As you point out here and later on in your comments, there are opportunities to
speed thing sup by caching various bits of data.  My thought was to go for a
simple implementation and do the other bits when/if this code starts being a
bottleneck according to profiles.

> >Index: layout/svg/base/src/nsSVGPathFrame.cpp
> >===================================================================
> >+NS_IMETHODIMP
> >+nsSVGPathFrame::GetFlattenedPath(nsSVGPathData **data,
> >+                                 nsIFrame *parent)
> >+{
> >+  nsIFrame *oldParent = mParent;
> >+  nsCOMPtr<nsISVGRendererRegion> dirty_region;
> >+
> >+  if (parent) {
> >+    mParent = parent;
> >+    GetGeometry()->Update(nsISVGGeometrySource::UPDATEMASK_CANVAS_TM,
> >+                          getter_AddRefs(dirty_region));
> >+  }
> >+
> >+  GetGeometry()->Flatten(data);
> >+
> >+  if (parent) {
> >+    mParent = oldParent;
> >+    GetGeometry()->Update(nsISVGGeometrySource::UPDATEMASK_CANVAS_TM,
> >+                          getter_AddRefs(dirty_region));
> >+  }
> >+
> >+  return NS_OK;
> >+}
> 
> This is sooo nasty for GDI+ and Libart, but what the heck. Cairo's the future
> :-)

Theoretically, yes
(In reply to comment #10)
> > What about 
> >  readonly attribute SVGAnimatedEnumeration method; &
> >  readonly attribute SVGAnimatedEnumeration spacing;
> > ?
> 
> Since we only do align/exact, I thought adding the attributes would be giving
> false hope to people.  I can add these and ignore them if you like.

I think they should go in, even if they are just implemented by stubs. That's
what we've done for all other SVG interfaces so far, and, who knows, maybe
someone will step forward and implement them. Yeah, right ;-)

> > Also, I'm not quite sure about the check for textpathframe parents. In
> > GetCharacterPosition() you walk up the tree (which presumably you need
> > to do to support things like <textPath><tspan>...</tspan></textPath>),
> > but in IsAbsolutelyPositioned() you just check mParent. Is that
> > intended? Maybe you could tag glyph frames that are textPath children
> > at frame construction time instead of walking up the tree every time?
> 
> As you point out here and later on in your comments, there are opportunities to
> speed thing sup by caching various bits of data.  My thought was to go for a
> simple implementation and do the other bits when/if this code starts being a
> bottleneck according to profiles.

Fair enough, but in that case, don't you need to walk the parent chain in
IsAbsolutelyPositioned() as well?
Attachment #186161 - Attachment is obsolete: true
Attached patch implement for GDI+ (obsolete) — Splinter Review
Attachment #190867 - Attachment is obsolete: true
Attachment #190965 - Flags: review?(alex)
Flags: blocking-aviary1.5+ → blocking1.8b4?
not holding on this, however, if it comes up, pending reviews including security
and its safe and timely we'll take it for 1.8b4
Flags: blocking1.8b4? → blocking1.8b4-
Attached patch update to trunkSplinter Review
Attachment #190965 - Attachment is obsolete: true
Attachment #192523 - Flags: review?(alex)
Attachment #190965 - Flags: review?(alex)
Comment on attachment 192523 [details] [diff] [review]
update to trunk

r=afri

Looks good. Sorry for the delay in reviewing.

In your checkin comment could you please state that the checkin also removes
the gdi+ text highlighting code?
This is just to make it easier to locate the highlighting code in future by
looking at the revision history.
Attachment #192523 - Flags: review?(alex) → review+
Attachment #192523 - Flags: approval1.8b4?
Update patch for the trunk - fixes some conflicts with changes that happened
post 1.8-branch.  This won't work with the cairo that's on the trunk, as
something broke in the cairo font code between 0.5.0 (branch version) and
1.0.0.	It does work with keithp's cairo scaled glyph patch.
Landed on the trunk.  Turns out cairo-0.9.0 is ok once I did the minor update to
not use a null surface (patch we used to apply to cairo).

Leaving open for branch decision.
Attached patch stubs for libartSplinter Review
Stubs for libart, since a number of ports tinderboxes still build this
depreciated backend.  Checked into trunk.
The example on http://www.zvon.org/xxl/svgReference/Output/exd0e20220.html
doesn't work as shown on the example image. There is a space after the comma,
and the text is wider than the SVG. 
Not sure exactly what you're refering to, as that example looks ok on both linux
and win32 here.  On my win32 setup it appears we select a slightly larger font
than whatever produced the reference image, so the last two characters are off
the end of the path and are omitted per spec.
http://www.zvon.org/xxl/svgReference/Output/exd0e20220.html works for me too 
in 20050826 build.  Looks identical to IE+ASV from what I can tell.

All samples at http://www.svgbasics.com/text2.html work for me too.

Doh!  Ignore my statement about 
http://www.zvon.org/xxl/svgReference/Output/exd0e20220.html, I was looking at 
the raster! :)

Yes, there is an error, it seems that font may be larger, but the last word 
shows here as "agi".  If it was simply chopping off the last two characters 
then it should be "aga".  Something funny going on there.
probably 'a' is too wide that we skip, but then 'i' looks like it would fit so 
it is printed?
Attachment #193956 - Flags: review?(jonathan.watt)
Attached image testcase
this file shows two bugs, 
1) somtimes letters get cliped,
2) text when animated, leaves a visible trail.
besides that, it works great, tor++ !

note: you will need a fast computer for this testcase
Attachment #193962 - Flags: review?(jonathan.watt)
Attachment #193962 - Flags: review?(jonathan.watt) → review?(roc)
Comment on attachment 193956 [details] [diff] [review]
fix logic when falling off the end of a text path

r=jwatt
Attachment #193956 - Flags: review?(jonathan.watt) → review+
Logic fix checked in on trunk.

another testcase showing the problem with dynamic updates.
Thanks for those testcases, Holger.
Attachment #194183 - Flags: review?(alex)
Comment on attachment 194183 [details] [diff] [review]
observe path so invalidations occur

r=afri
Attachment #194183 - Flags: review?(alex) → review+
Comment on attachment 194183 [details] [diff] [review]
observe path so invalidations occur

Observer patch checked in on trunk.
#include "nsIDOMSVGANimatedPathData.h"

The N should be lowercase, this is currently making prometheus burn.
Example of textPath centered on a line.  Renders fine with IE6+ASV but does not
render the text in Mozilla.
Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.9a1) Gecko/20050829
i believe the last testcase is invalid, i couldn't find anything in the spec
alowing textPath inside tspan elements.
i've just send an email to the svg-developers list, to clearify this issue.
(In reply to comment #37)
> i believe the last testcase is invalid, i couldn't find anything in the spec
> alowing textPath inside tspan elements.
> i've just send an email to the svg-developers list, to clearify this issue.

Confirmed this is the the cause of the text not rendering - awaiting 
clarification.
Bruce, it's perfectly valid to put the <tspan> inside the <textPath> instead,
and that should do what you want in both ASV and Firefox. For the record ASV
will allow dy on the <textPath> too. I'm  not sure if that or what you have in
your testcase is valid though off the top of my head. I suspect it isn't (or at
least shouldn't be).
Jon Ferraiolo just confirmed that this is indeed a bug in ASV.
here is his response:
http://groups.yahoo.com/group/svg-developers/message/51477
Thanks - I will adjust my code to reflect to correct coding.  Sorry about the 
posting.
without your testcase i wouldnt be able to track down the problem ;-) so thanks
to you !
Comment on attachment 193962 [details] [diff] [review]
fix missed use of a null surface

Null surface patch checked in.
Closing bug - please file any more problems found as seperate bugs.
Status: NEW → RESOLVED
Closed: 19 years ago
Resolution: --- → FIXED
i've uploaded a set of testcases for textPath,
http://www.treebuilder.de/firefox/textPath/
i will add more cases later...
Comment on attachment 192523 [details] [diff] [review]
update to trunk

per discussion, not taking <svg:textPath> for 1.8
Attachment #192523 - Flags: approval1.8b4? → approval1.8b4-
Flags: blocking1.8.1?
Keywords: fixed1.8.1
Flags: blocking1.8.1?
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: