Bug 375141 - convert svg text to thebes. r=longsonr, sr=roc

This commit is contained in:
tor%cs.brown.edu 2007-04-18 21:09:31 +00:00
Родитель 175053964a
Коммит 5337daca94
3 изменённых файлов: 305 добавлений и 342 удалений

Просмотреть файл

@ -53,7 +53,12 @@
#include "nsDOMError.h"
#include "gfxContext.h"
#include "gfxMatrix.h"
#include "cairo.h"
#include "gfxPlatform.h"
// XXX: This initial straightforward conversion from accessing cairo
// directly to Thebes doesn't handle clusters. Pretty much all code
// that measures or draws single characters (textPath code and some
// DOM accessors) will need to be reworked.
//----------------------------------------------------------------------
// Implementation
@ -117,6 +122,35 @@ nsSVGGlyphFrame::DidSetStyleContext()
{
nsSVGGlyphFrameBase::DidSetStyleContext();
const nsStyleFont* fontData = GetStyleFont();
nsFont font = fontData->mFont;
// Since SVG has its own scaling, we really don't want
// fonts in SVG to respond to the browser's "TextZoom"
// (Ctrl++,Ctrl+-)
nsPresContext *presContext = PresContext();
float textZoom = presContext->TextZoom();
double size = presContext->AppUnitsToDevPixels(fontData->mSize) / textZoom;
nsCAutoString langGroup;
nsIAtom *langGroupAtom = presContext->GetLangGroup();
if (langGroupAtom) {
const char* lg;
langGroupAtom->GetUTF8String(&lg);
langGroup.Assign(lg);
}
// XXX decorations are ignored by gfxFontStyle - still need to implement
mFontStyle = new gfxFontStyle(font.style, font.variant,
font.weight, font.decorations,
size, langGroup, font.sizeAdjust,
font.systemFont, font.familyNameQuirks);
if (mFontStyle) {
mFontGroup =
gfxPlatform::GetPlatform()->CreateFontGroup(font.name, mFontStyle);
}
return UpdateGraphic();
}
@ -179,23 +213,43 @@ nsSVGGlyphFrame::GetType() const
// nsISVGChildFrame methods
void
nsSVGGlyphFrame::LoopCharacters(cairo_t *aCtx, const nsAString &aText,
nsSVGGlyphFrame::LoopCharacters(gfxContext *aCtx, const nsString &aText,
const nsSVGCharacterPosition *aCP,
void (*aFunc)(cairo_t *cr, const char *utf8))
FillOrStroke aFillOrStroke)
{
nsAutoPtr<gfxTextRun> textRun(GetTextRun(aCtx, aText));
if (!textRun)
return;
if (!aCP) {
aFunc(aCtx, NS_ConvertUTF16toUTF8(aText).get());
if (aFillOrStroke == STROKE) {
textRun->DrawToPath(aCtx, mPosition, 0, aText.Length(), nsnull, nsnull);
} else {
textRun->Draw(aCtx, mPosition, 0, aText.Length(),
nsnull, nsnull, nsnull);
}
} else {
for (PRUint32 i = 0; i < aText.Length(); i++) {
/* character actually on the path? */
if (aCP[i].draw == PR_FALSE)
continue;
cairo_matrix_t matrix;
cairo_get_matrix(aCtx, &matrix);
cairo_move_to(aCtx, aCP[i].x, aCP[i].y);
cairo_rotate(aCtx, aCP[i].angle);
aFunc(aCtx, NS_ConvertUTF16toUTF8(Substring(aText, i, 1)).get());
cairo_set_matrix(aCtx, &matrix);
gfxMatrix matrix = aCtx->CurrentMatrix();
gfxMatrix rot;
rot.Rotate(aCP[i].angle);
aCtx->Multiply(rot);
rot.Invert();
gfxPoint pt = rot.Transform(aCP[i].pos);
if (aFillOrStroke == STROKE) {
textRun->DrawToPath(aCtx, pt, i, 1, nsnull, nsnull);
} else {
textRun->Draw(aCtx, pt, i, 1, nsnull, nsnull, nsnull);
}
aCtx->SetMatrix(matrix);
}
}
}
@ -214,70 +268,62 @@ nsSVGGlyphFrame::PaintSVG(nsSVGRenderState *aContext, nsRect *aDirtyRect)
nsAutoArrayPtr<nsSVGCharacterPosition> cp;
gfxContext *gfx = aContext->GetGfxContext();
cairo_t *ctx = gfx->GetCairo();
SelectFont(gfx);
nsresult rv = GetCharacterPosition(gfx, text, getter_Transfers(cp));
cairo_matrix_t matrix;
gfxMatrix matrix;
PRUint16 renderMode = aContext->GetRenderMode();
if (renderMode == nsSVGRenderState::NORMAL) {
/* save/pop the state so we don't screw up the xform */
cairo_save(ctx);
gfx->Save();
}
else {
cairo_get_matrix(ctx, &matrix);
matrix = gfx->CurrentMatrix();
}
rv = GetGlobalTransform(gfx);
if (NS_FAILED(rv)) {
if (renderMode == nsSVGRenderState::NORMAL)
cairo_restore(ctx);
gfx->Restore();
return rv;
}
if (!cp)
cairo_move_to(ctx, mX, mY);
if (renderMode != nsSVGRenderState::NORMAL) {
if (GetClipRule() == NS_STYLE_FILL_RULE_EVENODD)
cairo_set_fill_rule(ctx, CAIRO_FILL_RULE_EVEN_ODD);
gfx->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
else
cairo_set_fill_rule(ctx, CAIRO_FILL_RULE_WINDING);
gfx->SetFillRule(gfxContext::FILL_RULE_WINDING);
if (renderMode == nsSVGRenderState::CLIP_MASK) {
cairo_set_antialias(ctx, CAIRO_ANTIALIAS_NONE);
cairo_set_source_rgba(ctx, 1.0f, 1.0f, 1.0f, 1.0f);
LoopCharacters(ctx, text, cp, cairo_show_text);
gfx->SetAntialiasMode(gfxContext::MODE_ALIASED);
gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f));
LoopCharacters(gfx, text, cp, FILL);
} else {
LoopCharacters(ctx, text, cp, cairo_text_path);
LoopCharacters(gfx, text, cp, STROKE);
}
cairo_set_matrix(ctx, &matrix);
gfx->SetMatrix(matrix);
return NS_OK;
}
void *closure;
if (HasFill() && NS_SUCCEEDED(SetupCairoFill(gfx, &closure))) {
LoopCharacters(ctx, text, cp, cairo_show_text);
LoopCharacters(gfx, text, cp, FILL);
CleanupCairoFill(gfx, closure);
}
if (HasStroke() && NS_SUCCEEDED(SetupCairoStroke(gfx, &closure))) {
cairo_new_path(ctx);
if (!cp)
cairo_move_to(ctx, mX, mY);
LoopCharacters(ctx, text, cp, cairo_text_path);
cairo_stroke(ctx);
gfx->NewPath();
LoopCharacters(gfx, text, cp, STROKE);
gfx->Stroke();
CleanupCairoStroke(gfx, closure);
cairo_new_path(ctx);
gfx->NewPath();
}
cairo_restore(ctx);
gfx->Restore();
return NS_OK;
}
@ -362,60 +408,64 @@ nsSVGGlyphFrame::UpdateCoveredRegion()
nsSVGAutoGlyphHelperContext ctx(this, text, getter_Transfers(cp));
nsresult rv = GetGlobalTransform(ctx);
gfxContext *gfx = ctx.GetContext();
gfxTextRun *textRun = ctx.GetTextRun();
if (!gfx || !textRun)
return NS_ERROR_OUT_OF_MEMORY;
nsresult rv = GetGlobalTransform(gfx);
NS_ENSURE_SUCCESS(rv, rv);
if (!cp) {
cairo_move_to(ctx, mX, mY);
if (hasStroke) {
cairo_text_path(ctx, NS_ConvertUTF16toUTF8(text).get());
textRun->DrawToPath(gfx, mPosition, 0, text.Length(), nsnull, nsnull);
} else {
cairo_text_extents_t extent;
cairo_text_extents(ctx,
NS_ConvertUTF16toUTF8(text).get(),
&extent);
cairo_rectangle(ctx, mX + extent.x_bearing, mY + extent.y_bearing,
extent.width, extent.height);
gfxTextRun::Metrics metrics =
textRun->MeasureText(0, text.Length(), PR_FALSE, nsnull);
gfx->Rectangle(metrics.mBoundingBox + mPosition);
}
} else {
cairo_matrix_t matrix;
for (PRUint32 i=0; i<text.Length(); i++) {
/* character actually on the path? */
if (cp[i].draw == PR_FALSE)
continue;
cairo_get_matrix(ctx, &matrix);
cairo_move_to(ctx, cp[i].x, cp[i].y);
cairo_rotate(ctx, cp[i].angle);
gfxMatrix matrix = gfx->CurrentMatrix();
if (hasStroke) {
cairo_text_path(ctx, NS_ConvertUTF16toUTF8(Substring(text, i, 1)).get());
gfxMatrix rot;
rot.Rotate(cp[i].angle);
gfx->Multiply(rot);
rot.Invert();
gfxPoint pt = rot.Transform(cp[i].pos);
textRun->DrawToPath(gfx, pt, i, 1, nsnull, nsnull);
} else {
cairo_text_extents_t extent;
cairo_text_extents(ctx,
NS_ConvertUTF16toUTF8(Substring(text, i, 1)).get(),
&extent);
cairo_rel_move_to(ctx, extent.x_bearing, extent.y_bearing);
cairo_rel_line_to(ctx, extent.width, 0);
cairo_rel_line_to(ctx, 0, extent.height);
cairo_rel_line_to(ctx, -extent.width, 0);
cairo_close_path(ctx);
gfx->MoveTo(cp[i].pos);
gfx->Rotate(cp[i].angle);
gfxTextRun::Metrics metrics =
textRun->MeasureText(i, 1, PR_FALSE, nsnull);
gfx->Rectangle(metrics.mBoundingBox + gfx->CurrentPoint());
}
cairo_set_matrix(ctx, &matrix);
gfx->SetMatrix(matrix);
}
}
double xmin, ymin, xmax, ymax;
gfxRect extent;
if (hasStroke) {
SetupCairoStrokeGeometry(ctx);
cairo_stroke_extents(ctx, &xmin, &ymin, &xmax, &ymax);
nsSVGUtils::UserToDeviceBBox(ctx, &xmin, &ymin, &xmax, &ymax);
SetupCairoStrokeGeometry(gfx);
extent = gfx->GetUserStrokeExtent();
extent = gfx->UserToDevice(extent);
} else {
cairo_identity_matrix(ctx);
cairo_fill_extents(ctx, &xmin, &ymin, &xmax, &ymax);
gfx->IdentityMatrix();
extent = gfx->GetUserFillExtent();
}
mRect = nsSVGUtils::ToBoundingPixelRect(xmin, ymin, xmax, ymax);
mRect = nsSVGUtils::ToBoundingPixelRect(extent);
return NS_OK;
}
@ -473,21 +523,18 @@ nsSVGGlyphFrame::GetBBox(nsIDOMSVGRect **_retval)
nsSVGAutoGlyphHelperContext ctx(this, text, getter_Transfers(cp));
nsresult rv = GetGlobalTransform(ctx);
gfxContext *gfx = ctx.GetContext();
if (!gfx)
return NS_ERROR_OUT_OF_MEMORY;
nsresult rv = GetGlobalTransform(gfx);
NS_ENSURE_SUCCESS(rv, rv);
if (!cp)
cairo_move_to(ctx, mX, mY);
LoopCharacters(gfx, text, cp, STROKE);
gfx->IdentityMatrix();
gfxRect rect = gfx->GetUserFillExtent();
LoopCharacters(ctx, text, cp, cairo_text_path);
cairo_identity_matrix(ctx);
double xmin, ymin, xmax, ymax;
cairo_fill_extents(ctx, &xmin, &ymin, &xmax, &ymax);
return NS_NewSVGRect(_retval, xmin, ymin, xmax - xmin, ymax - ymin);
return NS_NewSVGRect(_retval, rect);
}
//----------------------------------------------------------------------
@ -540,11 +587,9 @@ nsSVGGlyphFrame::GetCharacterData(nsAString & aCharacterData)
nsresult
nsSVGGlyphFrame::GetCharacterPosition(gfxContext *aContext,
const nsAString &aText,
const nsString &aText,
nsSVGCharacterPosition **aCharacterPosition)
{
cairo_t *ctx = aContext->GetCairo();
*aCharacterPosition = nsnull;
NS_ASSERTION(!aText.IsEmpty(), "no text");
@ -565,20 +610,18 @@ nsSVGGlyphFrame::GetCharacterPosition(gfxContext *aContext,
float length = data->GetLength();
PRUint32 strLength = aText.Length();
nsAutoPtr<gfxTextRun> textRun(GetTextRun(aContext, aText));
if (!textRun)
return NS_ERROR_OUT_OF_MEMORY;
nsSVGCharacterPosition *cp = new nsSVGCharacterPosition[strLength];
for (PRUint32 k = 0; k < strLength; k++)
cp[k].draw = PR_FALSE;
float x = mX;
float x = mPosition.x;
for (PRUint32 i = 0; i < strLength; i++) {
cairo_text_extents_t extent;
cairo_text_extents(ctx,
NS_ConvertUTF16toUTF8(Substring(aText, i, 1)).get(),
&extent);
float halfAdvance = extent.x_advance / 2.0;
float halfAdvance = textRun->GetAdvanceWidth(i, 1, nsnull) / 2.0;
/* have we run off the end of the path? */
if (x + halfAdvance > length)
@ -591,10 +634,10 @@ nsSVGGlyphFrame::GetCharacterPosition(gfxContext *aContext,
// add y (normal)
// add rotation
// move point back along tangent
gfxPoint pt = data->FindPoint(gfxPoint(x + halfAdvance, mY),
gfxPoint pt = data->FindPoint(gfxPoint(x + halfAdvance, mPosition.y),
&(cp[i].angle));
cp[i].x = pt.x - cos(cp[i].angle) * halfAdvance;
cp[i].y = pt.y - sin(cp[i].angle) * halfAdvance;
cp[i].pos =
pt - gfxPoint(cos(cp[i].angle), sin(cp[i].angle)) * halfAdvance;
}
x += 2 * halfAdvance;
}
@ -751,8 +794,7 @@ nsSVGGlyphFrame::GetHighlight(PRUint32 *charnum, PRUint32 *nchars,
NS_IMETHODIMP_(void)
nsSVGGlyphFrame::SetGlyphPosition(float x, float y)
{
mX = x;
mY = y;
mPosition.MoveTo(x, y);
UpdateGeometry(PR_TRUE, PR_FALSE);
}
@ -768,35 +810,26 @@ nsSVGGlyphFrame::GetStartPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retv
nsSVGAutoGlyphHelperContext ctx(this, text, getter_Transfers(cp));
float x, y;
gfxPoint pt;
if (cp) {
if (cp[charnum].draw == PR_FALSE) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
x = cp[charnum].x;
y = cp[charnum].y;
pt = cp[charnum].pos;
} else {
x = mX;
y = mY;
pt = mPosition;
if (charnum > 0) {
cairo_text_extents_t extent;
cairo_text_extents(ctx,
NS_ConvertUTF16toUTF8(Substring(text,
0,
charnum)).get(),
&extent);
x += extent.x_advance;
y += extent.y_advance;
gfxTextRun *textRun = ctx.GetTextRun();
if (!textRun)
return NS_ERROR_OUT_OF_MEMORY;
pt.x += textRun->GetAdvanceWidth(0, charnum, nsnull);
}
}
return NS_NewSVGPoint(_retval, x, y);
return NS_NewSVGPoint(_retval, pt.x, pt.y);
}
NS_IMETHODIMP
@ -808,33 +841,29 @@ nsSVGGlyphFrame::GetEndPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval
GetCharacterData(text);
nsAutoArrayPtr<nsSVGCharacterPosition> cp;
nsSVGAutoGlyphHelperContext ctx(this, text, getter_Transfers(cp));
cairo_text_extents_t extent;
nsSVGAutoGlyphHelperContext ctx(this, text, getter_Transfers(cp));
gfxTextRun *textRun = ctx.GetTextRun();
if (!textRun)
return NS_ERROR_OUT_OF_MEMORY;
if (cp) {
if (cp[charnum].draw == PR_FALSE) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
cairo_text_extents(ctx,
NS_ConvertUTF16toUTF8(Substring(text, charnum, 1)).get(),
&extent);
float advance = textRun->GetAdvanceWidth(charnum, 1, nsnull);
float s = sin(cp[charnum].angle);
float c = cos(cp[charnum].angle);
return NS_NewSVGPoint(_retval,
cp[charnum].x + extent.x_advance * c - extent.y_advance * s,
cp[charnum].y + extent.y_advance * c + extent.x_advance * s);
return NS_NewSVGPoint(_retval,
cp[charnum].pos.x + advance * cos(cp[charnum].angle),
cp[charnum].pos.y + advance * sin(cp[charnum].angle));
}
cairo_text_extents(ctx,
NS_ConvertUTF16toUTF8(Substring(text, 0, charnum + 1)).get(),
&extent);
return NS_NewSVGPoint(_retval, mX + extent.x_advance, mY + extent.y_advance);
return NS_NewSVGPoint(_retval,
mPosition.x + textRun->GetAdvanceWidth(0,
charnum + 1,
nsnull),
mPosition.y);
}
NS_IMETHODIMP
@ -848,60 +877,47 @@ nsSVGGlyphFrame::GetExtentOfChar(PRUint32 charnum, nsIDOMSVGRect **_retval)
nsAutoArrayPtr<nsSVGCharacterPosition> cp;
nsSVGAutoGlyphHelperContext ctx(this, text, getter_Transfers(cp));
gfxTextRun *textRun = ctx.GetTextRun();
if (!textRun)
return NS_ERROR_OUT_OF_MEMORY;
cairo_text_extents_t extent;
cairo_text_extents(ctx,
NS_ConvertUTF16toUTF8(Substring(text, charnum, 1)).get(),
&extent);
gfxTextRun::Metrics metrics =
textRun->MeasureText(charnum, 1, PR_FALSE, nsnull);
if (cp) {
if (cp[charnum].draw == PR_FALSE) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
cairo_matrix_t matrix;
cairo_get_matrix(ctx, &matrix);
cairo_move_to(ctx, cp[charnum].x, cp[charnum].y);
cairo_rotate(ctx, cp[charnum].angle);
gfxContext *gfx = ctx.GetContext();
if (!gfx)
return NS_ERROR_OUT_OF_MEMORY;
cairo_rel_move_to(ctx, extent.x_bearing, extent.y_bearing);
cairo_rel_line_to(ctx, extent.width, 0);
cairo_rel_line_to(ctx, 0, extent.height);
cairo_rel_line_to(ctx, -extent.width, 0);
cairo_close_path(ctx);
cairo_identity_matrix(ctx);
gfxMatrix matrix = gfx->CurrentMatrix();
double xmin, ymin, xmax, ymax;
gfx->MoveTo(cp[charnum].pos);
gfx->Rotate(cp[charnum].angle);
cairo_fill_extents(ctx, &xmin, &ymin, &xmax, &ymax);
gfx->Rectangle(metrics.mBoundingBox + gfx->CurrentPoint());
cairo_set_matrix(ctx, &matrix);
gfx->IdentityMatrix();
return NS_NewSVGRect(_retval, xmin, ymin, xmax - xmin, ymax - ymin);
gfxRect rect = gfx->GetUserFillExtent();
gfx->SetMatrix(matrix);
return NS_NewSVGRect(_retval, rect);
}
float x = mX;
float y = mY;
x += extent.x_bearing;
y += extent.y_bearing;
cairo_text_extents_t precedingExtent;
gfxPoint pt = mPosition;
if (charnum > 0) {
// add the space taken up by the text which comes before charnum
// to the position of the charnum character
cairo_text_extents(ctx,
NS_ConvertUTF16toUTF8(Substring(text,
0,
charnum)).get(),
&precedingExtent);
x += precedingExtent.x_advance;
y += precedingExtent.y_advance;
pt.x += textRun->GetAdvanceWidth(0, charnum, nsnull);
}
return NS_NewSVGRect(_retval, x, y, extent.width, extent.height);
return NS_NewSVGRect(_retval, metrics.mBoundingBox + pt);
}
NS_IMETHODIMP
@ -932,32 +948,38 @@ NS_IMETHODIMP_(float)
nsSVGGlyphFrame::GetBaselineOffset(PRUint16 baselineIdentifier)
{
float _retval;
cairo_font_extents_t extents;
nsSVGAutoGlyphHelperContext ctx(this);
nsAutoString text;
GetCharacterData(text);
cairo_font_extents(ctx, &extents);
nsSVGAutoGlyphHelperContext ctx(this, text);
gfxTextRun *textRun = ctx.GetTextRun();
if (!textRun)
return 0.0f;
gfxTextRun::Metrics metrics =
textRun->MeasureText(0, text.Length(), PR_FALSE, nsnull);
switch (baselineIdentifier) {
case BASELINE_HANGING:
// not really right, but the best we can do with the information provided
// FALLTHROUGH
case BASELINE_TEXT_BEFORE_EDGE:
_retval = -extents.ascent;
_retval = -metrics.mAscent;
break;
case BASELINE_TEXT_AFTER_EDGE:
_retval = extents.descent;
_retval = metrics.mDescent;
break;
case BASELINE_CENTRAL:
case BASELINE_MIDDLE:
_retval = - (extents.ascent - extents.descent) / 2.0;
_retval = - (metrics.mAscent - metrics.mDescent) / 2.0;
break;
case BASELINE_ALPHABETIC:
default:
_retval = 0.0;
break;
}
return _retval;
}
@ -969,14 +991,12 @@ nsSVGGlyphFrame::GetAdvance()
return 0.0f;
}
nsSVGAutoGlyphHelperContext ctx(this);
nsSVGAutoGlyphHelperContext ctx(this, text);
gfxTextRun *textRun = ctx.GetTextRun();
if (!textRun)
return 0.0f;
cairo_text_extents_t extents;
cairo_text_extents(ctx,
NS_ConvertUTF16toUTF8(text).get(),
&extents);
return extents.x_advance;
return textRun->GetAdvanceWidth(0, text.Length(), nsnull);
}
NS_IMETHODIMP_(nsSVGTextPathFrame*)
@ -1104,19 +1124,7 @@ nsSVGGlyphFrame::GetNumberOfChars()
NS_IMETHODIMP_(float)
nsSVGGlyphFrame::GetComputedTextLength()
{
nsAutoString text;
if (!GetCharacterData(text)) {
return 0.0f;
}
nsSVGAutoGlyphHelperContext ctx(this);
cairo_text_extents_t extent;
cairo_text_extents(ctx,
NS_ConvertUTF16toUTF8(text).get(),
&extent);
return fabs(extent.x_advance) + fabs(extent.y_advance);
return GetAdvance();
}
NS_IMETHODIMP_(float)
@ -1125,14 +1133,12 @@ nsSVGGlyphFrame::GetSubStringLength(PRUint32 charnum, PRUint32 fragmentChars)
nsAutoString text;
GetCharacterData(text);
nsSVGAutoGlyphHelperContext ctx(this);
nsSVGAutoGlyphHelperContext ctx(this, text);
gfxTextRun *textRun = ctx.GetTextRun();
if (!textRun)
return 0.0f;
cairo_text_extents_t extent;
cairo_text_extents(ctx,
NS_ConvertUTF16toUTF8(Substring(text, charnum, fragmentChars)).get(),
&extent);
return fabs(extent.x_advance) + fabs(extent.y_advance);
return textRun->GetAdvanceWidth(charnum, fragmentChars, nsnull);
}
NS_IMETHODIMP_(PRInt32)
@ -1149,55 +1155,48 @@ nsSVGGlyphFrame::GetCharNumAtPosition(nsIDOMSVGPoint *point)
nsSVGAutoGlyphHelperContext ctx(this, text, getter_Transfers(cp));
float x, y;
gfxPoint pt;
if (!cp) {
x = mX;
y = mY;
pt = mPosition;
}
gfxContext *gfx = ctx.GetContext();
gfxTextRun *textRun = ctx.GetTextRun();
if (!gfx || !textRun)
return NS_ERROR_OUT_OF_MEMORY;
for (PRUint32 charnum = 0; charnum < text.Length(); charnum++) {
/* character actually on the path? */
if (cp && cp[charnum].draw == PR_FALSE)
continue;
cairo_matrix_t matrix;
cairo_get_matrix(ctx, &matrix);
cairo_new_path(ctx);
gfxMatrix matrix = gfx->CurrentMatrix();
gfx->NewPath();
if (cp) {
cairo_move_to(ctx, cp[charnum].x, cp[charnum].y);
cairo_rotate(ctx, cp[charnum].angle);
gfx->MoveTo(cp[charnum].pos);
gfx->Rotate(cp[charnum].angle);
} else {
if (charnum > 0) {
cairo_text_extents_t extent;
cairo_text_extents(ctx,
NS_ConvertUTF16toUTF8(Substring(text,
0,
charnum)).get(),
&extent);
cairo_move_to(ctx, x + extent.x_advance, y + extent.y_advance);
gfx->MoveTo(pt + gfxPoint(textRun->GetAdvanceWidth(0, charnum, nsnull),
0));
} else {
cairo_move_to(ctx, x, y);
gfx->MoveTo(pt);
}
}
cairo_text_extents_t extent;
cairo_text_extents(ctx,
NS_ConvertUTF16toUTF8(Substring(text, charnum, 1)).get(),
&extent);
cairo_rel_move_to(ctx, extent.x_bearing, extent.y_bearing);
cairo_rel_line_to(ctx, extent.width, 0);
cairo_rel_line_to(ctx, 0, extent.height);
cairo_rel_line_to(ctx, -extent.width, 0);
cairo_close_path(ctx);
gfxTextRun::Metrics metrics =
textRun->MeasureText(charnum, 1, PR_FALSE, nsnull);
cairo_identity_matrix(ctx);
if (cairo_in_fill(ctx, xPos, yPos)) {
gfx->Rectangle(metrics.mBoundingBox + gfx->CurrentPoint());
gfx->IdentityMatrix();
if (gfx->PointInFill(gfxPoint(xPos, yPos))) {
gfx->SetMatrix(matrix);
return charnum;
}
cairo_set_matrix(ctx, &matrix);
gfx->SetMatrix(matrix);
}
return -1;
}
@ -1237,68 +1236,6 @@ nsSVGGlyphFrame::SetWhitespaceHandling(PRUint8 aWhitespaceHandling)
//----------------------------------------------------------------------
//
void nsSVGGlyphFrame::SelectFont(gfxContext *aContext)
{
cairo_t *ctx = aContext->GetCairo();
const nsStyleFont* fontData = GetStyleFont();
nsFont font = fontData->mFont;
// XXX eventually we will have to treat decorations separately from
// fonts, because they can have a different color than the current
// glyph.
NS_ASSERTION(mParent, "no parent");
nsStyleContext *parentContext = mParent->GetStyleContext();
NS_ASSERTION(parentContext, "no style context on parent");
PRUint8 styleDecorations =
parentContext->GetStyleTextReset()->mTextDecoration;
if (styleDecorations & NS_STYLE_TEXT_DECORATION_UNDERLINE)
font.decorations |= NS_FONT_DECORATION_UNDERLINE;
if (styleDecorations & NS_STYLE_TEXT_DECORATION_OVERLINE)
font.decorations |= NS_FONT_DECORATION_OVERLINE;
if (styleDecorations & NS_STYLE_TEXT_DECORATION_LINE_THROUGH)
font.decorations |= NS_FONT_DECORATION_LINE_THROUGH;
cairo_font_slant_t slant;
cairo_font_weight_t weight = CAIRO_FONT_WEIGHT_NORMAL;
switch (font.style) {
case NS_FONT_STYLE_NORMAL:
slant = CAIRO_FONT_SLANT_NORMAL;
break;
case NS_FONT_STYLE_ITALIC:
slant = CAIRO_FONT_SLANT_ITALIC;
break;
case NS_FONT_STYLE_OBLIQUE:
slant = CAIRO_FONT_SLANT_OBLIQUE;
break;
}
if (font.weight % 100 == 0) {
if (font.weight >= 600)
weight = CAIRO_FONT_WEIGHT_BOLD;
} else if (font.weight % 100 < 50) {
weight = CAIRO_FONT_WEIGHT_BOLD;
}
nsAutoString family;
font.GetFirstFamily(family);
cairo_select_font_face(ctx,
NS_ConvertUTF16toUTF8(family).get(),
slant,
weight);
// Since SVG has its own scaling, we really don't want
// fonts in SVG to respond to the browser's "TextZoom"
// (Ctrl++,Ctrl+-)
nsPresContext *presContext = PresContext();
float textZoom = presContext->TextZoom();
cairo_set_font_size(ctx, presContext->AppUnitsToDevPixels(fontData->mSize) / textZoom);
}
void nsSVGGlyphFrame::UpdateGeometry(PRBool bRedraw,
PRBool suppressInvalidation)
{
@ -1343,52 +1280,47 @@ nsSVGGlyphFrame::ContainsPoint(float x, float y)
nsAutoArrayPtr<nsSVGCharacterPosition> cp;
nsSVGAutoGlyphHelperContext ctx(this, text, getter_Transfers(cp));
gfxContext *gfx = ctx.GetContext();
gfxTextRun *textRun = ctx.GetTextRun();
if (!gfx || !textRun)
return NS_ERROR_OUT_OF_MEMORY;
nsresult rv = GetGlobalTransform(ctx);
nsresult rv = GetGlobalTransform(gfx);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
float xx = 0, yy = 0;
gfxPoint pt;
if (!cp) {
xx = mX;
yy = mY;
pt = mPosition;
}
cairo_matrix_t matrix;
for (PRUint32 i = 0; i < text.Length(); i++) {
/* character actually on the path? */
if (cp && cp[i].draw == PR_FALSE)
continue;
cairo_get_matrix(ctx, &matrix);
gfxMatrix matrix = gfx->CurrentMatrix();
if (cp) {
cairo_move_to(ctx, cp[i].x, cp[i].y);
cairo_rotate(ctx, cp[i].angle);
gfx->MoveTo(cp[i].pos);
gfx->Rotate(cp[i].angle);
} else {
cairo_move_to(ctx, xx, yy);
gfx->MoveTo(pt);
}
cairo_text_extents_t extent;
cairo_text_extents(ctx,
NS_ConvertUTF16toUTF8(Substring(text, i, 1)).get(),
&extent);
cairo_rel_move_to(ctx, extent.x_bearing, extent.y_bearing);
cairo_rel_line_to(ctx, extent.width, 0);
cairo_rel_line_to(ctx, 0, extent.height);
cairo_rel_line_to(ctx, -extent.width, 0);
cairo_close_path(ctx);
gfxTextRun::Metrics metrics =
textRun->MeasureText(i, 1, PR_FALSE, nsnull);
cairo_set_matrix(ctx, &matrix);
gfx->Rectangle(metrics.mBoundingBox + gfx->CurrentPoint());
gfx->SetMatrix(matrix);
if (!cp) {
xx += extent.x_advance;
yy += extent.y_advance;
pt.x += metrics.mAdvanceWidth;
}
}
cairo_identity_matrix(ctx);
return cairo_in_fill(ctx, x, y);
gfx->IdentityMatrix();
return gfx->PointInFill(gfxPoint(x, y));
}
nsresult
@ -1410,15 +1342,38 @@ nsSVGGlyphFrame::GetGlobalTransform(gfxContext *aContext)
return NS_OK;
}
gfxTextRun *
nsSVGGlyphFrame::GetTextRun(gfxContext *aCtx, const nsString &aText)
{
// XXX: should really pass in GetPresContext()->AppUnitsPerDevPixel()
// instead of "1" and do the appropriate unit conversions when sending
// coordinates into thebes and pulling metrics out.
//
// References:
// https://bugzilla.mozilla.org/show_bug.cgi?id=375141
// http://weblogs.mozillazine.org/roc/archives/2007/03/text_text_text.html
gfxTextRunFactory::Parameters params =
{ aCtx, nsnull, nsnull,
nsnull, nsnull, nsnull,
1, // see note above
0 };
if (!mFontGroup)
return nsnull;
return mFontGroup->MakeTextRun(aText.get(), aText.Length(), &params);
}
//----------------------------------------------------------------------
// helper class
nsSVGGlyphFrame::nsSVGAutoGlyphHelperContext::nsSVGAutoGlyphHelperContext(
nsSVGGlyphFrame *aSource,
const nsAString &aText,
const nsString &aText,
nsSVGCharacterPosition **cp)
{
Init(aSource);
Init(aSource, aText);
nsresult rv = aSource->GetCharacterPosition(mCT, aText, cp);
if NS_FAILED(rv) {
@ -1426,8 +1381,10 @@ nsSVGGlyphFrame::nsSVGAutoGlyphHelperContext::nsSVGAutoGlyphHelperContext(
}
}
void nsSVGGlyphFrame::nsSVGAutoGlyphHelperContext::Init(nsSVGGlyphFrame *aSource)
void
nsSVGGlyphFrame::nsSVGAutoGlyphHelperContext::Init(nsSVGGlyphFrame *aSource,
const nsString &aText)
{
mCT = new gfxContext(nsSVGUtils::GetThebesComputationalSurface());
aSource->SelectFont(mCT);
mTextRun = aSource->GetTextRun(mCT, aText);
}

Просмотреть файл

@ -43,7 +43,9 @@
#include "nsISVGGlyphFragmentLeaf.h"
#include "nsISVGChildFrame.h"
#include "gfxContext.h"
#include "gfxFont.h"
struct nsSVGCharacterPosition;
class nsSVGTextFrame;
class nsSVGGlyphFrame;
@ -151,8 +153,8 @@ public:
protected:
struct nsSVGCharacterPosition {
gfxPoint pos;
gfxFloat angle;
float x, y;
PRBool draw;
};
@ -161,45 +163,44 @@ protected:
class nsSVGAutoGlyphHelperContext;
friend class nsSVGAutoGlyphHelperContext;
// A helper class to deal with temporary cairo contexts.
// It destroys the context when it goes out of scope.
// A helper class to deal with gfxTextRuns and temporary thebes
// contexts. It destroys them when it goes out of scope.
class nsSVGAutoGlyphHelperContext
{
public:
nsSVGAutoGlyphHelperContext(nsSVGGlyphFrame *aSource)
nsSVGAutoGlyphHelperContext(nsSVGGlyphFrame *aSource,
const nsString &aText)
{
Init(aSource);
Init(aSource, aText);
}
nsSVGAutoGlyphHelperContext(nsSVGGlyphFrame *aSource,
const nsAString &aText,
const nsString &aText,
nsSVGCharacterPosition **cp);
operator gfxContext * ()
{
return mCT;
}
operator cairo_t * ()
{
return mCT->GetCairo();
}
gfxContext *GetContext() { return mCT; }
gfxTextRun *GetTextRun() { return mTextRun; }
private:
void Init (nsSVGGlyphFrame *aSource);
void Init(nsSVGGlyphFrame *aSource, const nsString &aText);
nsRefPtr<gfxContext> mCT;
nsAutoPtr<gfxTextRun> mTextRun;
};
void SelectFont(gfxContext *aContext);
gfxTextRun *GetTextRun(gfxContext *aCtx,
const nsString &aText);
PRBool GetCharacterData(nsAString & aCharacterData);
nsresult GetCharacterPosition(gfxContext *aContext,
const nsAString &aText,
const nsString &aText,
nsSVGCharacterPosition **aCharacterPosition);
static void LoopCharacters(cairo_t *aCtx,
const nsAString &aText,
const nsSVGCharacterPosition *aCP,
void (*aFunc)(cairo_t *cr, const char *utf8));
enum FillOrStroke { FILL, STROKE};
void LoopCharacters(gfxContext *aCtx, const nsString &aText,
const nsSVGCharacterPosition *aCP,
FillOrStroke aFillOrStroke);
void UpdateGeometry(PRBool bRedraw, PRBool suppressInvalidation);
void UpdateMetrics();
@ -208,8 +209,10 @@ protected:
nsresult GetHighlight(PRUint32 *charnum, PRUint32 *nchars,
nscolor *foreground, nscolor *background);
float mX, mY;
PRUint8 mWhitespaceHandling;
nsRefPtr<gfxFontGroup> mFontGroup;
nsAutoPtr<gfxFontStyle> mFontStyle;
gfxPoint mPosition;
PRUint8 mWhitespaceHandling;
};
#endif

Просмотреть файл

@ -80,6 +80,7 @@
#include "gfxImageSurface.h"
#include "gfxMatrix.h"
#include "nsStubMutationObserver.h"
#include "gfxPlatform.h"
class nsSVGFilterProperty : public nsStubMutationObserver {
public:
@ -1157,8 +1158,10 @@ gfxASurface *
nsSVGUtils::GetThebesComputationalSurface()
{
if (!mThebesComputationalSurface) {
mThebesComputationalSurface =
new gfxImageSurface(GetCairoComputationalSurface());
nsRefPtr<gfxASurface> surface =
gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(1, 1),
gfxASurface::ImageFormatARGB32);
mThebesComputationalSurface = surface;
// we want to keep this surface around
NS_IF_ADDREF(mThebesComputationalSurface);
}