Closed Bug 339039 Opened 18 years ago Closed 18 years ago

Text spans return incorrect number of characters with getNumberOfChars()

Categories

(Core :: SVG, defect)

x86
Windows XP
defect
Not set
normal

Tracking

()

RESOLVED FIXED

People

(Reporter: wisniewskit, Assigned: longsonr)

References

Details

Attachments

(4 files, 5 obsolete files)

User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9a1) Gecko/20060523 Minefield/3.0a1
Build Identifier: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9a1) Gecko/20060523 Minefield/3.0a1

When multiple tspans are in a text element, the number of characters in those tspans considers all sibling elements after the one being considered.

Reproducible: Always

Steps to Reproduce:
1. View page given in "addition information"
2.
3.

Actual Results:  
t1: 19
t2: 14
t3: 9
t4: 4

Expected Results:  
t1: 4
t2: 4
t3: 4
t4: 4

<?xml version="1.0" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" onload="window.setTimeout('delay()', 1000);">
	<script><![CDATA[
		function delay() {
			alert( "t1:" + document.getElementById("t1").getNumberOfChars() + "\nt2:" + document.getElementById("t2").getNumberOfChars() + "\nt3:" + document.getElementById("t3").getNumberOfChars() + "\nt4:" + document.getElementById("t4").getNumberOfChars() );
		}
	]]></script>
	<text fill="black" transform="translate(0,20)">
		<tspan id="t1">Test</tspan>
		<tspan id="t2">Test</tspan>
		<tspan id="t3">Test</tspan>
		<tspan id="t4">Test</tspan>
	</text>
</svg>
Attached the testcase described by the author.
Assignee: general → longsonr
Status: UNCONFIRMED → NEW
Ever confirmed: true
Attached patch patch (obsolete) — Splinter Review
If we traverse to a frame with a different parent than that of our initiating frame then we've run out of child frames. Testcase now produces expected results.
Attachment #223178 - Flags: review?(tor)
Offhand it seems that we're adding this root parameter everywhere to keep working in an interative fashion instead of using recursion.  Whether that's desired or not is another question.
Is there a reason to not use recursion?
Comment on attachment 223178 [details] [diff] [review]
patch

I'll try to figure out how to do it as you suggest.
Attachment #223178 - Attachment is obsolete: true
Attachment #223178 - Flags: review?(tor)
GetNumberOfChars, GetComputedTextLength, GetSubStringLength and GetCharNumAtPosition now implemented recursively.

Also fixed problems with textPaths if they are not the first child of <text>

Moved duplicated code from nsSVGTextFrame and nsSVGTSpanFrame into nsSVGUtils:
BuildGlyphFragmentTree, GetFirstGlyphFragmentChildNode and GetNextGlyphFragmentChildNode
Attachment #223781 - Flags: review?(tor)
Attached patch fix argument validation (obsolete) — Splinter Review
I realised the validation of arguments is incorrect. The previous patch contains useless checks that unsigned value are >= 0 and misses out the validation check that we have characters before trying to determine their length.
Attachment #223781 - Attachment is obsolete: true
Attachment #223896 - Flags: review?(tor)
Attachment #223781 - Flags: review?(tor)
Comment on attachment 223896 [details] [diff] [review]
fix argument validation

For new code, CallQueryInterface should be used instead of do_QueryInterface.

	

nsSVGGlyphFrame::GetCharacterPosition and nsSVGGlyphFrame::IsChildOfTextPath contain much the same code.  Rename and slightly modify the latter to FindTextPathParent to share code?

nsSVGGlyphFrame::GetCharNumAtPosition adjusts the point for non-textpath children.  Why not leave this for nsSVGCairoGlyphMetrics::GetCharNumAtPosition?

Instead of passing mFrames.FirstChild() to all the nsSVGUtil methods, why not have the methods accept mFrameList so mFrames can be passed directly?  

nsSVGUtils text methods that only set a single out parameter and always return NS_OK should be changed to just return the value.

For things like GetSubStringLength and GetStartPositionOfChar you have sanity checks on the arguments in the frame code.  Why not move these to the nsSVGUtil methods?
Attached patch address review comments (obsolete) — Splinter Review
> For new code, CallQueryInterface should be used instead of do_QueryInterface.

Done.

> nsSVGGlyphFrame::GetCharacterPosition and nsSVGGlyphFrame::IsChildOfTextPath
> contain much the same code.  Rename and slightly modify the latter to
> FindTextPathParent to share code?

Done.

> nsSVGGlyphFrame::GetCharNumAtPosition adjusts the point for non-textpath
> children.  Why not leave this for nsSVGCairoGlyphMetrics::GetCharNumAtPosition?

Done for all methods that need to modify their results for non-textpath children.

> Instead of passing mFrames.FirstChild() to all the nsSVGUtil methods, why not
> have the methods accept mFrameList so mFrames can be passed directly?  

Done.

> nsSVGUtils text methods that only set a single out parameter and always return
> NS_OK should be changed to just return the value.

Done.

> For things like GetSubStringLength and GetStartPositionOfChar you have sanity
> checks on the arguments in the frame code.  Why not move these to the nsSVGUtil
> methods?

The nsSVGUtils methods are called recursively from nsSVGTSpanFrame.cpp. If I moved the sanity checks to nsSVGUtils then they would be called for every node, however they only need to validate the user supplied arguments are OK. After that arguments must be valid all the way down the recursion stack. 

The sanity check code is rather shorter now that your other comments have been actioned.
Attachment #223896 - Attachment is obsolete: true
Attachment #224195 - Flags: review?(tor)
Attachment #223896 - Flags: review?(tor)
Comment on attachment 224195 [details] [diff] [review]
address review comments

It seems that whenever GetGlyphPositionX is called, so is GetGlyphPositionY.  Combine into one method?

You have a number of methods in nsSVGCairoGlyphMetrics calling FindTextPathParent to check if an offset needs to be done.  This doesn't need to be called, as the !cp branch of the code means you're in that circumstance.

With the previous corrected, the only place FindTextPathParent is called is in nsSVGGlyphFrame::GetCharacterPosition, so the code could be moved there.
Attachment #224195 - Attachment is obsolete: true
Attachment #224195 - Flags: review?(tor)
> It seems that whenever GetGlyphPositionX is called, so is GetGlyphPositionY. 
> Combine into one method?

Done.

> You have a number of methods in nsSVGCairoGlyphMetrics calling
> FindTextPathParent to check if an offset needs to be done.  This doesn't need
> to be called, as the !cp branch of the code means you're in that circumstance.

Done.

> With the previous corrected, the only place FindTextPathParent is called is in
> nsSVGGlyphFrame::GetCharacterPosition, so the code could be moved there.

As we were :-)
Attachment #224415 - Flags: review?(tor)
Attachment #224415 - Attachment is obsolete: true
Attachment #224575 - Flags: review?(tor)
Attachment #224415 - Flags: review?(tor)
Attachment #224575 - Flags: review?(tor) → review+
Attachment #224575 - Flags: superreview?(roc)
Why can't error checks like

+  if (charnum >= nsSVGUtils::GetNumberOfChars(&mFrames)) {
+    *_retval = nsnull;
+    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+  }

be folded into the nsSVGUtils functions that follow?
In the nsISVGGlyphFragmentNode interface in nsSVGTSpanFrame I call nsSVGUtils::GetSubStringLength recursively. The arguments must be valid at that point as long as the original arguments passed validation in the nsISVGTextContentMetrics implementation (nsSVGTSpanFrame and nsSVGTextFrame).

I therefore put all the argument validation in the same files - nsSVGTSpanFrame and nsSVGTextFrame, rather than having some nsSVGUtils functions do validation and some not. 

Validation for those functions that work iteratively (GetStartPositionOfChar, GetEndPositionOfChar, GetExtentOfChar and GetRotationOfChar) rather than recursively (GetSubStringLength) could be moved to nsSVGUtils without affecting performance.

So I see 3 alternatives:
a) Move all validation to nsSVGUtils
b) Move non-recursive function validation to nsSVGUtils
c) As I am, possibly with additional code comments.

I'm happy to go with your preferred option as Tor asked the same question in comment 8.
I vote for option d), move all validation to nsSVGUtils, and for GetSubStringLength, validate the parameters and then call a GetSubStringLengthRecursive function (which could be a static global function).
Attached patch option d)Splinter Review
Attachment #224851 - Flags: superreview?(roc)
Attachment #224575 - Flags: superreview?(roc)
How do we know that TSpanFrame::GetSubStringLength can only be called by GetSubStringLengthRecursive?
nsSVGTSpanFrame implements two interfaces, one which must validate its arguments and one which does not have to.

I could create a new class nsSVGTextContainerFrame say, inheriting from nsSVGDisplayContainerFrame acting as a base class for nsSVGTSpanFrame, nsSVGTextFrame and possibly nsSVGTextPathFrame. The new class could implement all the common functionality rather than having it in nsSVGUtils. That way making the methods protected would limit their use somewhat although it would not be foolproof.

Alternatively we could just always validate the arguments for both interfaces and not have a nsSVGUtils::GetSubStringLengthRecursive function at all.
Comment on attachment 224851 [details] [diff] [review]
option d)

Okay, let's not call it *Recursive, let's call it *NoValidation instead.
Attachment #224851 - Flags: superreview?(roc) → superreview+
Checked in. With NoValidation change.
Status: NEW → RESOLVED
Closed: 18 years ago
Resolution: --- → FIXED
Depends on: 318597, 331347, 338010
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: