зеркало из https://github.com/mozilla/pjs.git
Bug 375141 - convert svg text to thebes. r=longsonr, sr=roc
This commit is contained in:
Родитель
2ae2f9b017
Коммит
784ad9d9d1
|
@ -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);
|
||||
if (hasStroke) {
|
||||
cairo_text_path(ctx, NS_ConvertUTF16toUTF8(Substring(text, i, 1)).get());
|
||||
} 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);
|
||||
}
|
||||
cairo_set_matrix(ctx, &matrix);
|
||||
}
|
||||
}
|
||||
|
||||
double xmin, ymin, xmax, ymax;
|
||||
gfxMatrix matrix = gfx->CurrentMatrix();
|
||||
|
||||
if (hasStroke) {
|
||||
SetupCairoStrokeGeometry(ctx);
|
||||
cairo_stroke_extents(ctx, &xmin, &ymin, &xmax, &ymax);
|
||||
nsSVGUtils::UserToDeviceBBox(ctx, &xmin, &ymin, &xmax, &ymax);
|
||||
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_identity_matrix(ctx);
|
||||
cairo_fill_extents(ctx, &xmin, &ymin, &xmax, &ymax);
|
||||
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());
|
||||
}
|
||||
gfx->SetMatrix(matrix);
|
||||
}
|
||||
}
|
||||
|
||||
mRect = nsSVGUtils::ToBoundingPixelRect(xmin, ymin, xmax, ymax);
|
||||
gfxRect extent;
|
||||
|
||||
if (hasStroke) {
|
||||
SetupCairoStrokeGeometry(gfx);
|
||||
extent = gfx->GetUserStrokeExtent();
|
||||
extent = gfx->UserToDevice(extent);
|
||||
} else {
|
||||
gfx->IdentityMatrix();
|
||||
extent = gfx->GetUserFillExtent();
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -810,31 +843,27 @@ nsSVGGlyphFrame::GetEndPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval
|
|||
nsAutoArrayPtr<nsSVGCharacterPosition> cp;
|
||||
|
||||
nsSVGAutoGlyphHelperContext ctx(this, text, getter_Transfers(cp));
|
||||
|
||||
cairo_text_extents_t extent;
|
||||
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 s = sin(cp[charnum].angle);
|
||||
float c = cos(cp[charnum].angle);
|
||||
float advance = textRun->GetAdvanceWidth(charnum, 1, nsnull);
|
||||
|
||||
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);
|
||||
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,25 +948,31 @@ 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:
|
||||
|
@ -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(), ¶ms);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// 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,
|
||||
|
||||
enum FillOrStroke { FILL, STROKE};
|
||||
|
||||
void LoopCharacters(gfxContext *aCtx, const nsString &aText,
|
||||
const nsSVGCharacterPosition *aCP,
|
||||
void (*aFunc)(cairo_t *cr, const char *utf8));
|
||||
FillOrStroke aFillOrStroke);
|
||||
|
||||
void UpdateGeometry(PRBool bRedraw, PRBool suppressInvalidation);
|
||||
void UpdateMetrics();
|
||||
|
@ -208,7 +209,9 @@ protected:
|
|||
nsresult GetHighlight(PRUint32 *charnum, PRUint32 *nchars,
|
||||
nscolor *foreground, nscolor *background);
|
||||
|
||||
float mX, mY;
|
||||
nsRefPtr<gfxFontGroup> mFontGroup;
|
||||
nsAutoPtr<gfxFontStyle> mFontStyle;
|
||||
gfxPoint mPosition;
|
||||
PRUint8 mWhitespaceHandling;
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче