Bug 392233. Take the current contxt scale into account when constructing textruns for SVG, and simplify the SVG text code. r=longsonr,sr=vlad

This commit is contained in:
roc+@cs.cmu.edu 2008-03-17 18:45:05 -07:00
parent b903fbc5b9
commit f3f684e136
11 changed files with 863 additions and 585 deletions

View File

@ -47,6 +47,8 @@ include $(topsrcdir)/config/rules.mk
_TEST_FILES = \
test_getSubStringLength.xhtml \
getSubStringLength-helper.svg \
test_text.html \
text-helper.svg \
$(NULL)
libs:: $(_TEST_FILES)

View File

@ -0,0 +1,180 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=392233
-->
<head>
<title>Test for Bug 392233</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=392233">Mozilla Bug 392233</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<iframe id="svg" src="text-helper.svg"></iframe>
<pre id="test">
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
function runTest()
{
var doc = $("svg").contentWindow.document;
var text1 = doc.getElementById("text1");
var text2 = doc.getElementById("text2");
var text3 = doc.getElementById("text3");
var charWidth = text1.getSubStringLength(0, 1);
function isPoint(pt1, x, y, str)
{
is(pt1.x, x, str + " x");
is(pt1.y, y, str + " y");
}
function ymost(r)
{
return r.y + r.height;
}
function xmost(r)
{
return r.x + r.width;
}
var p = text1.getStartPositionOfChar(0);
// Simple horizontal string
is(text1.getNumberOfChars(), 3, "text1 length");
ok(text1.getComputedTextLength() > 0, "text1 measured length");
is(text1.getComputedTextLength(), text1.getSubStringLength(0, 3), "text1 substring length");
isPoint(text1.getStartPositionOfChar(0), 5, 25, "text1 char 0 start offset");
isPoint(text1.getStartPositionOfChar(1), 5 + charWidth, 25, "text1 char 1 start offset");
isPoint(text1.getStartPositionOfChar(2), 5 + 2*charWidth, 25, "text1 char 2 start offset");
isPoint(text1.getEndPositionOfChar(0), 5 + charWidth, 25, "text1 char 0 end offset");
isPoint(text1.getEndPositionOfChar(1), 5 + 2*charWidth, 25, "text1 char 1 end offset");
isPoint(text1.getEndPositionOfChar(2), 5 + 3*charWidth, 25, "text1 char 2 end offset");
is(text1.getExtentOfChar(0).x, 5, "text1 char 0 extent x");
is(text1.getExtentOfChar(0).width, text1.getSubStringLength(0, 1), "text1 char 0 extent width");
ok(text1.getExtentOfChar(0).y < 25, "text1 char 0 extent y");
ok(ymost(text1.getExtentOfChar(0)) > 25, "text1 char 0 extent height");
is(text1.getExtentOfChar(1).x, 5 + charWidth, "text1 char 1 extent x");
is(text1.getExtentOfChar(1).width, text1.getSubStringLength(0, 1), "text1 char 1 extent width");
is(text1.getExtentOfChar(1).y, text1.getExtentOfChar(0).y, "text1 char 0/1 extent y");
is(text1.getExtentOfChar(1).height, text1.getExtentOfChar(0).height, "text1 char 0/1 extent height");
is(text1.getExtentOfChar(2).x, 5 + 2*charWidth, "text1 char 2 extent x");
is(text1.getExtentOfChar(2).width, text1.getSubStringLength(0, 1), "text1 char 2 extent width");
is(text1.getExtentOfChar(2).y, text1.getExtentOfChar(0).y, "text1 char 0/2 extent y");
is(text1.getExtentOfChar(2).height, text1.getExtentOfChar(0).height, "text1 char 0/2 extent height");
is(text1.getRotationOfChar(0), 0, "text1 char 0 rotation");
is(text1.getRotationOfChar(1), 0, "text1 char 0 rotation");
is(text1.getRotationOfChar(2), 0, "text1 char 0 rotation");
p.x = 5 + 0.1;
p.y = 25;
is(text1.getCharNumAtPosition(p), 0, "text1 finding char 0 left edge");
p.x = 5 + charWidth - 0.1;
is(text1.getCharNumAtPosition(p), 0, "text1 finding char 0 on right");
p.x = 5 - 0.1;
is(text1.getCharNumAtPosition(p), -1, "text1 finding no char on left");
p.x = 5 + 0.1;
p.y = text1.getExtentOfChar(0).y - 0.1;
is(text1.getCharNumAtPosition(p), -1, "text1 finding no char on top");
p.y = text1.getExtentOfChar(0).y + 0.1;
is(text1.getCharNumAtPosition(p), 0, "text1 finding char 0 top edge");
p.x = 5 + 3*charWidth - 0.1;
is(text1.getCharNumAtPosition(p), 2, "text1 finding char 2 top edge");
p.x = 5 + 3*charWidth + 0.1;
is(text1.getCharNumAtPosition(p), -1, "text1 finding no char on right");
// Simple rotated-90 string ... width might change because of hinting
charWidth = text2.getSubStringLength(0, 1);
is(text2.getNumberOfChars(), 3, "text2 length");
ok(text2.getComputedTextLength() > 0, "text2 measured length");
is(text2.getComputedTextLength(), text2.getSubStringLength(0, 3), "text2 substring length");
isPoint(text2.getStartPositionOfChar(0), 100, 125, "text2 char 0 start offset");
isPoint(text2.getStartPositionOfChar(1), 100, 125 + charWidth, "text2 char 1 start offset");
isPoint(text2.getStartPositionOfChar(2), 100, 125 + 2*charWidth, "text2 char 2 start offset");
isPoint(text2.getEndPositionOfChar(0), 100, 125 + charWidth, "text2 char 0 end offset");
isPoint(text2.getEndPositionOfChar(1), 100, 125 + 2*charWidth, "text2 char 1 end offset");
isPoint(text2.getEndPositionOfChar(2), 100, 125 + 3*charWidth, "text2 char 2 end offset");
is(text2.getExtentOfChar(0).y, 125, "text2 char 0 extent y");
is(text2.getExtentOfChar(0).height, charWidth, "text2 char 0 extent height");
ok(text2.getExtentOfChar(0).width < 100, "text2 char 0 extent x");
ok(xmost(text2.getExtentOfChar(0)) > 100, "text2 char 0 extent width");
is(text2.getExtentOfChar(1).y, 125 + charWidth, "text2 char 1 extent x");
is(text2.getExtentOfChar(1).height, text2.getSubStringLength(0, 1), "text2 char 1 extent width");
is(text2.getExtentOfChar(1).x, text2.getExtentOfChar(0).x, "text2 char 0/1 extent y");
is(text2.getExtentOfChar(1).width, text2.getExtentOfChar(0).width, "text2 char 0/1 extent height");
is(text2.getExtentOfChar(2).y, 125 + 2*charWidth, "text2 char 2 extent x");
is(text2.getExtentOfChar(2).height, text2.getSubStringLength(0, 1), "text2 char 2 extent width");
is(text2.getExtentOfChar(2).x, text2.getExtentOfChar(0).x, "text2 char 0/2 extent y");
is(text2.getExtentOfChar(2).width, text2.getExtentOfChar(0).width, "text2 char 0/2 extent height");
is(text2.getRotationOfChar(0), 90, "text2 char 0 rotation");
is(text2.getRotationOfChar(1), 90, "text2 char 0 rotation");
is(text2.getRotationOfChar(2), 90, "text2 char 0 rotation");
p.y = 125 + 0.1;
p.x = 100;
is(text2.getCharNumAtPosition(p), 0, "text2 finding char 0 top edge");
p.y = 125 + charWidth - 0.1;
is(text2.getCharNumAtPosition(p), 0, "text2 finding char 0 on bottom");
p.y = 125 - 0.1;
is(text2.getCharNumAtPosition(p), -1, "text2 finding no char on top");
p.y = 125 + 0.1;
p.x = text2.getExtentOfChar(0).x - 0.1;
is(text2.getCharNumAtPosition(p), -1, "text2 finding no char on left");
p.x = text2.getExtentOfChar(0).x + 0.1;
is(text2.getCharNumAtPosition(p), 0, "text2 finding char 0 left edge");
p.y = 125 + 3*charWidth - 0.1;
is(text2.getCharNumAtPosition(p), 2, "text2 finding char 2 bottom edge");
p.y = 1225 + 3*charWidth + 0.1;
is(text2.getCharNumAtPosition(p), -1, "text2 finding no char on bottom");
// Text along a thin rectangle path
charWidth = text3.getSubStringLength(0, 1);
is(text3.getNumberOfChars(), 26, "text3 length");
ok(text3.getComputedTextLength() > 0, "text3 measured length");
is(text3.getComputedTextLength(), text3.getSubStringLength(0, 26), "text3 substring length");
// character 12 should be on the bottom side
is(text3.getStartPositionOfChar(12).y, 253, "text3 char 12 start offset");
is(text3.getEndPositionOfChar(12).y, 253, "text3 char 12 end offset");
ok(text3.getExtentOfChar(12).y < 253, "text3 char 12 extent y");
ok(ymost(text3.getExtentOfChar(12)) > 253, "text3 char 12 extent height");
is(text3.getRotationOfChar(12), 180, "text3 char 12 rotation");
p.x = text3.getExtentOfChar(12).x + 0.1;
p.y = ymost(text3.getExtentOfChar(12)) - 0.1;
is(text3.getCharNumAtPosition(p), 12, "text3 finding char 12");
// This next test is tricky. The glyph for character 3 may overlap from the above
// but character 12 wins because it's the last to render
p.y = text3.getExtentOfChar(12).y + 0.1;
is(text3.getCharNumAtPosition(p), 12, "text3 finding last rendered char");
// character 25 should be beyond the end of the path
// Not sure what should happen here. Currently we throw, which seems wrong
// is(text3.getStartPositionOfChar(25).x, 0, "text3 char 25 start offset");
}
function runTests() {
runTest();
var doc = $("svg").contentWindow.document;
doc.getElementById("g").setAttribute("transform", "rotate(90 200 200)");
runTest();
SimpleTest.finish();
}
window.addEventListener("load", runTests, false);
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,17 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" width="750"
xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<style type="text/css">
text { font: 20px monospace; }
</style>
<g id="g">
<text id="text1" x="5" y="25">abc</text>
<path id="MyPath2" d="M 100 125 L 100 200" stroke="red" fill="none"/>
<text id="text2"><textPath xlink:href="#MyPath2">abc</textPath></text>
<path id="MyPath" d="M 5 250 L 105 250 L 105 253 L 5 253 z" stroke="red" fill="none"/>
<text id="text3"><textPath xlink:href="#MyPath">abcdefghijklmnopqrstuvwxyz</textPath></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 603 B

View File

@ -56,6 +56,7 @@ fails == style-property-not-on-script-element-01.svg pass.svg
== svg-in-foreignObject-02.xhtml svg-in-foreignObject-01-ref.xhtml # reuse -01-ref.xhtml
random-if(MOZ_WIDGET_TOOLKIT=="gtk2") == text-font-weight-01.svg text-font-weight-01-ref.svg # bug 386713
== text-in-link-01.svg text-in-link-01-ref.svg
== text-scale-01.svg text-scale-01-ref.svg
== text-style-01a.svg text-style-01-ref.svg
== text-style-01b.svg text-style-01-ref.svg
== text-style-01c.svg text-style-01-ref.svg

View File

@ -0,0 +1,14 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<title>Reference for text scaling</title>
<!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=392233 -->
<text x="0" y="100" font-size="100">Hello</text>
<text x="0" y="200" font-size="100">Hello</text>
<text x="0" y="300" font-size="100">Hello</text>
</svg>

After

Width:  |  Height:  |  Size: 448 B

View File

@ -0,0 +1,20 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<title>Testcase for text scaling</title>
<!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=392233 -->
<g transform="scale(10)">
<text x="0" y="10" font-size="10">Hello</text>
</g>
<g transform="scale(100)">
<text x="0" y="2" font-size="1">Hello</text>
</g>
<g transform="scale(1000)">
<text x="0" y="0.3" font-size="0.1">Hello</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 555 B

View File

@ -46,9 +46,11 @@ class nsIDOMSVGPoint;
class nsIDOMSVGRect;
class nsSVGTextPathFrame;
// {2C466AED-CF7B-4479-A807-98151215A645}
// {ec9a9965-3ff2-4bb5-b0e2-dd8830e9f41a}
#define NS_ISVGGLYPHFRAGMENTLEAF_IID \
{ 0x2c466aed, 0xcf7b, 0x4479, { 0xa8, 0x7, 0x98, 0x15, 0x12, 0x15, 0xa6, 0x45 } }
{ 0xec9a9965, 0x3ff2, 0x4bb5, \
{ 0xb0, 0xe2, 0xdd, 0x88, 0x30, 0xe9, 0xf4, 0x1a } }
class nsISVGGlyphFragmentLeaf : public nsISVGGlyphFragmentNode
{
@ -70,8 +72,9 @@ public:
enum { BASELINE_TEXT_BEFORE_EDGE = 6U };
enum { BASELINE_TEXT_AFTER_EDGE = 7U };
NS_IMETHOD_(float) GetBaselineOffset(PRUint16 baselineIdentifier)=0;
NS_IMETHOD_(float) GetAdvance()=0;
NS_IMETHOD_(float) GetBaselineOffset(PRUint16 baselineIdentifier,
PRBool aForceGlobalTransform)=0;
NS_IMETHOD_(float) GetAdvance(PRBool aForceGlobalTransform)=0;
NS_IMETHOD_(void) SetGlyphPosition(float x, float y)=0;
NS_IMETHOD_(nsSVGTextPathFrame*) FindTextPathParent()=0;

File diff suppressed because it is too large Load Diff

View File

@ -44,11 +44,11 @@
#include "nsISVGChildFrame.h"
#include "gfxContext.h"
#include "gfxFont.h"
#include "gfxTextRunCache.h"
struct nsSVGCharacterPosition;
class nsSVGTextFrame;
class nsSVGGlyphFrame;
class CharacterIterator;
struct CharacterPosition;
typedef nsSVGGeometryFrame nsSVGGlyphFrameBase;
@ -61,8 +61,14 @@ class nsSVGGlyphFrame : public nsSVGGlyphFrameBase,
nsIFrame* parentFrame, nsStyleContext* aContext);
protected:
nsSVGGlyphFrame(nsStyleContext* aContext)
: nsSVGGlyphFrameBase(aContext),
mWhitespaceHandling(COMPRESS_WHITESPACE) {}
: nsSVGGlyphFrameBase(aContext),
mTextRun(nsnull),
mWhitespaceHandling(COMPRESS_WHITESPACE)
{}
~nsSVGGlyphFrame()
{
ClearTextRun();
}
public:
// nsISupports interface:
@ -107,10 +113,13 @@ public:
#endif
// nsISVGChildFrame interface:
// These four always use the global transform, even if NS_STATE_NONDISPLAY_CHILD
NS_IMETHOD PaintSVG(nsSVGRenderState *aContext, nsRect *aDirtyRect);
NS_IMETHOD GetFrameForPointSVG(float x, float y, nsIFrame** hit);
NS_IMETHOD_(nsRect) GetCoveredRegion();
NS_IMETHOD UpdateCoveredRegion();
NS_IMETHOD GetBBox(nsIDOMSVGRect **_retval);
NS_IMETHOD_(nsRect) GetCoveredRegion();
NS_IMETHOD InitialUpdate();
virtual void NotifySVGChanged(PRUint32 aFlags);
NS_IMETHOD NotifyRedrawSuspended();
@ -118,7 +127,6 @@ public:
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate) { return NS_OK; }
NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM) { return NS_ERROR_FAILURE; }
virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM() { return nsnull; }
NS_IMETHOD GetBBox(nsIDOMSVGRect **_retval);
NS_IMETHOD_(PRBool) IsDisplayContainer() { return PR_FALSE; }
NS_IMETHOD_(PRBool) HasValidCoveredRect() { return PR_TRUE; }
@ -127,12 +135,22 @@ public:
virtual nsresult UpdateGraphic(PRBool suppressInvalidation = PR_FALSE);
// nsISVGGlyphFragmentLeaf interface:
// These do not use the global transform if NS_STATE_NONDISPLAY_CHILD
NS_IMETHOD GetStartPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval);
NS_IMETHOD GetEndPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval);
NS_IMETHOD GetExtentOfChar(PRUint32 charnum, nsIDOMSVGRect **_retval);
NS_IMETHOD GetRotationOfChar(PRUint32 charnum, float *_retval);
NS_IMETHOD_(float) GetBaselineOffset(PRUint16 baselineIdentifier);
NS_IMETHOD_(float) GetAdvance();
/**
* @param aForceGlobalTransform controls whether to use the
* global transform even when NS_STATE_NONDISPLAY_CHILD
*/
NS_IMETHOD_(float) GetBaselineOffset(PRUint16 baselineIdentifier,
PRBool aForceGlobalTransform);
/**
* @param aForceGlobalTransform controls whether to use the
* global transform even when NS_STATE_NONDISPLAY_CHILD
*/
NS_IMETHOD_(float) GetAdvance(PRBool aForceGlobalTransform);
NS_IMETHOD_(void) SetGlyphPosition(float x, float y);
NS_IMETHOD_(nsSVGTextPathFrame*) FindTextPathParent();
@ -147,6 +165,7 @@ public:
NS_IMETHOD_(PRBool) IsAbsolutelyPositioned();
// nsISVGGlyphFragmentNode interface:
// These do not use the global transform if NS_STATE_NONDISPLAY_CHILD
NS_IMETHOD_(PRUint32) GetNumberOfChars();
NS_IMETHOD_(float) GetComputedTextLength();
NS_IMETHOD_(float) GetSubStringLength(PRUint32 charnum, PRUint32 fragmentChars);
@ -156,66 +175,47 @@ public:
NS_IMETHOD_(void) SetWhitespaceHandling(PRUint8 aWhitespaceHandling);
protected:
struct nsSVGCharacterPosition {
gfxPoint pos;
gfxFloat angle;
PRBool draw;
};
friend class CharacterIterator;
// VC6 does not allow the inner class to access protected members
// of the outer class
class nsSVGAutoGlyphHelperContext;
friend class nsSVGAutoGlyphHelperContext;
// A helper class to deal with gfxTextRuns and temporary thebes
// contexts.
class nsSVGAutoGlyphHelperContext
{
public:
nsSVGAutoGlyphHelperContext(nsSVGGlyphFrame *aSource,
const nsString &aText)
{
Init(aSource, aText);
}
nsSVGAutoGlyphHelperContext(nsSVGGlyphFrame *aSource,
const nsString &aText,
nsSVGCharacterPosition **cp);
gfxContext *GetContext() { return mCT; }
gfxTextRun *GetTextRun() { return mTextRun.get(); }
private:
void Init(nsSVGGlyphFrame *aSource, const nsString &aText);
nsRefPtr<gfxContext> mCT;
gfxTextRunCache::AutoTextRun mTextRun;
};
// The textrun must be released via gfxTextRunCache::AutoTextRun
gfxTextRun *GetTextRun(gfxContext *aCtx,
const nsString &aText);
// Use a power of 2 here. It's not so important to match
// nsIDeviceContext::AppUnitsPerDevPixel, but since we do a lot of
// multiplying by 1/GetTextRunUnitsFactor, it's good for it to be a
// power of 2 to avoid accuracy loss.
static PRUint32 GetTextRunUnitsFactor() { return 64; }
/**
* @aParam aDrawScale font drawing must be scaled into user units
* by this factor
* @param aMetricsScale font metrics must be scaled into user units
* by this factor
* @param aForceGlobalTransform set to true if we should force use of
* the global transform; otherwise we won't use the global transform
* if we're a NONDISPLAY_CHILD
*/
PRBool EnsureTextRun(float *aDrawScale, float *aMetricsScale,
PRBool aForceGlobalTransform);
void ClearTextRun();
PRBool GetCharacterData(nsAString & aCharacterData);
nsresult GetCharacterPosition(gfxContext *aContext,
const nsString &aText,
nsSVGCharacterPosition **aCharacterPosition);
PRBool GetCharacterPositions(nsTArray<CharacterPosition>* aCharacterPositions,
float aMetricsScale);
enum FillOrStroke { FILL, STROKE};
void LoopCharacters(gfxContext *aCtx, const nsString &aText,
const nsSVGCharacterPosition *aCP,
FillOrStroke aFillOrStroke);
void AddCharactersToPath(CharacterIterator *aIter,
gfxContext *aContext);
void AddBoundingBoxesToPath(CharacterIterator *aIter,
gfxContext *aContext);
void FillCharacters(CharacterIterator *aIter,
gfxContext *aContext);
void UpdateGeometry(PRBool bRedraw, PRBool suppressInvalidation);
void UpdateMetrics();
PRBool ContainsPoint(float x, float y);
nsresult GetGlobalTransform(gfxContext *aContext);
PRBool GetGlobalTransform(gfxMatrix *aContext);
nsresult GetHighlight(PRUint32 *charnum, PRUint32 *nchars,
nscolor *foreground, nscolor *background);
nsRefPtr<gfxFontGroup> mFontGroup;
nsAutoPtr<gfxFontStyle> mFontStyle;
// Owning pointer, must call gfxTextRunWordCache::RemoveTextRun before deleting
gfxTextRun *mTextRun;
gfxPoint mPosition;
PRUint8 mWhitespaceHandling;
};

View File

@ -73,15 +73,6 @@ NS_NewSVGTextFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContex
//----------------------------------------------------------------------
// nsIFrame methods
NS_IMETHODIMP
nsSVGTextFrame::SetInitialChildList(nsIAtom* aListName,
nsIFrame* aChildList)
{
nsresult rv = nsSVGTextFrameBase::SetInitialChildList(aListName, aChildList);
NotifyGlyphMetricsChange();
return rv;
}
NS_IMETHODIMP
nsSVGTextFrame::AttributeChanged(PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
@ -127,7 +118,7 @@ nsSVGTextFrame::GetType() const
NS_IMETHODIMP
nsSVGTextFrame::GetNumberOfChars(PRInt32 *_retval)
{
UpdateGlyphPositioning();
UpdateGlyphPositioning(PR_FALSE);
return nsSVGTextFrameBase::GetNumberOfChars(_retval);
}
@ -135,7 +126,7 @@ nsSVGTextFrame::GetNumberOfChars(PRInt32 *_retval)
NS_IMETHODIMP
nsSVGTextFrame::GetComputedTextLength(float *_retval)
{
UpdateGlyphPositioning();
UpdateGlyphPositioning(PR_FALSE);
return nsSVGTextFrameBase::GetComputedTextLength(_retval);
}
@ -143,7 +134,7 @@ nsSVGTextFrame::GetComputedTextLength(float *_retval)
NS_IMETHODIMP
nsSVGTextFrame::GetSubStringLength(PRUint32 charnum, PRUint32 nchars, float *_retval)
{
UpdateGlyphPositioning();
UpdateGlyphPositioning(PR_FALSE);
return nsSVGTextFrameBase::GetSubStringLength(charnum, nchars, _retval);
}
@ -151,7 +142,7 @@ nsSVGTextFrame::GetSubStringLength(PRUint32 charnum, PRUint32 nchars, float *_re
NS_IMETHODIMP
nsSVGTextFrame::GetStartPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval)
{
UpdateGlyphPositioning();
UpdateGlyphPositioning(PR_FALSE);
return nsSVGTextFrameBase::GetStartPositionOfChar(charnum, _retval);
}
@ -159,7 +150,7 @@ nsSVGTextFrame::GetStartPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retva
NS_IMETHODIMP
nsSVGTextFrame::GetEndPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval)
{
UpdateGlyphPositioning();
UpdateGlyphPositioning(PR_FALSE);
return nsSVGTextFrameBase::GetEndPositionOfChar(charnum, _retval);
}
@ -167,7 +158,7 @@ nsSVGTextFrame::GetEndPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval)
NS_IMETHODIMP
nsSVGTextFrame::GetExtentOfChar(PRUint32 charnum, nsIDOMSVGRect **_retval)
{
UpdateGlyphPositioning();
UpdateGlyphPositioning(PR_FALSE);
return nsSVGTextFrameBase::GetExtentOfChar(charnum, _retval);
}
@ -175,7 +166,7 @@ nsSVGTextFrame::GetExtentOfChar(PRUint32 charnum, nsIDOMSVGRect **_retval)
NS_IMETHODIMP
nsSVGTextFrame::GetRotationOfChar(PRUint32 charnum, float *_retval)
{
UpdateGlyphPositioning();
UpdateGlyphPositioning(PR_FALSE);
return nsSVGTextFrameBase::GetRotationOfChar(charnum, _retval);
}
@ -183,7 +174,7 @@ nsSVGTextFrame::GetRotationOfChar(PRUint32 charnum, float *_retval)
NS_IMETHODIMP
nsSVGTextFrame::GetCharNumAtPosition(nsIDOMSVGPoint *point, PRInt32 *_retval)
{
UpdateGlyphPositioning();
UpdateGlyphPositioning(PR_FALSE);
return nsSVGTextFrameBase::GetCharNumAtPosition(point, _retval);
}
@ -225,7 +216,6 @@ NS_IMETHODIMP
nsSVGTextFrame::NotifyRedrawUnsuspended()
{
mMetricsState = unsuspended;
UpdateGlyphPositioning();
return nsSVGTextFrameBase::NotifyRedrawUnsuspended();
}
@ -252,10 +242,34 @@ nsSVGTextFrame::GetOverrideCTM()
return matrix;
}
NS_IMETHODIMP
nsSVGTextFrame::PaintSVG(nsSVGRenderState* aContext, nsRect *aDirtyRect)
{
UpdateGlyphPositioning(PR_TRUE);
return nsSVGTextFrameBase::PaintSVG(aContext, aDirtyRect);
}
NS_IMETHODIMP
nsSVGTextFrame::GetFrameForPointSVG(float x, float y, nsIFrame** hit)
{
UpdateGlyphPositioning(PR_TRUE);
return nsSVGTextFrameBase::GetFrameForPointSVG(x, y, hit);
}
NS_IMETHODIMP
nsSVGTextFrame::UpdateCoveredRegion()
{
UpdateGlyphPositioning(PR_TRUE);
return nsSVGTextFrameBase::UpdateCoveredRegion();
}
NS_IMETHODIMP
nsSVGTextFrame::GetBBox(nsIDOMSVGRect **_retval)
{
UpdateGlyphPositioning();
UpdateGlyphPositioning(PR_TRUE);
return nsSVGTextFrameBase::GetBBox(_retval);
}
@ -308,7 +322,6 @@ void
nsSVGTextFrame::NotifyGlyphMetricsChange()
{
mPositioningDirty = PR_TRUE;
UpdateGlyphPositioning();
}
static void
@ -332,7 +345,7 @@ GetSingleValue(nsISVGGlyphFragmentLeaf *fragment,
}
void
nsSVGTextFrame::UpdateGlyphPositioning()
nsSVGTextFrame::UpdateGlyphPositioning(PRBool aForceGlobalTransform)
{
if (mMetricsState == suspended || !mPositioningDirty)
return;
@ -425,7 +438,7 @@ nsSVGTextFrame::UpdateGlyphPositioning()
float dx = 0.0f;
nsCOMPtr<nsIDOMSVGLengthList> list = fragment->GetDx();
GetSingleValue(fragment, list, &dx);
chunkLength += dx + fragment->GetAdvance();
chunkLength += dx + fragment->GetAdvance(aForceGlobalTransform);
fragment = fragment->GetNextGlyphFragment();
if (fragment && fragment->IsAbsolutelyPositioned())
break;
@ -452,10 +465,11 @@ nsSVGTextFrame::UpdateGlyphPositioning()
GetSingleValue(fragment, list, &dy);
}
float baseline_offset = fragment->GetBaselineOffset(baseline);
float baseline_offset =
fragment->GetBaselineOffset(baseline, aForceGlobalTransform);
fragment->SetGlyphPosition(x + dx, y + dy - baseline_offset);
x += dx + fragment->GetAdvance();
x += dx + fragment->GetAdvance(aForceGlobalTransform);
y += dy;
fragment = fragment->GetNextGlyphFragment();
if (fragment && fragment->IsAbsolutelyPositioned())

View File

@ -52,12 +52,10 @@ protected:
: nsSVGTextFrameBase(aContext),
mMetricsState(unsuspended),
mPropagateTransform(PR_TRUE),
mPositioningDirty(PR_FALSE) {}
mPositioningDirty(PR_TRUE) {}
public:
// nsIFrame:
NS_IMETHOD SetInitialChildList(nsIAtom* aListName,
nsIFrame* aChildList);
NS_IMETHOD AttributeChanged(PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
PRInt32 aModType);
@ -84,6 +82,11 @@ public:
virtual void NotifySVGChanged(PRUint32 aFlags);
NS_IMETHOD NotifyRedrawSuspended();
NS_IMETHOD NotifyRedrawUnsuspended();
// Override these four to ensure that UpdateGlyphPositioning is called
// to bring glyph positions up to date
NS_IMETHOD PaintSVG(nsSVGRenderState* aContext, nsRect *aDirtyRect);
NS_IMETHOD GetFrameForPointSVG(float x, float y, nsIFrame** hit);
NS_IMETHOD UpdateCoveredRegion();
NS_IMETHOD GetBBox(nsIDOMSVGRect **_retval);
// nsSVGContainerFrame methods:
@ -103,7 +106,12 @@ public:
void NotifyGlyphMetricsChange();
private:
void UpdateGlyphPositioning();
/**
* @param aForceGlobalTransform passed down to nsSVGGlyphFrames to
* control whether they should use the global transform even when
* NS_STATE_NONDISPLAY_CHILD
*/
void UpdateGlyphPositioning(PRBool aForceGlobalTransform);
nsCOMPtr<nsIDOMSVGMatrix> mCanvasTM;
nsCOMPtr<nsIDOMSVGMatrix> mOverrideCTM;