From f45fbb88ae892f0ee3aa5c6116ba5eb5a3aea1f6 Mon Sep 17 00:00:00 2001 From: "tor@cs.brown.edu" Date: Tue, 17 Apr 2007 15:48:28 -0700 Subject: [PATCH] Bug 375141 - convert svg text to thebes. r=longsonr, sr=roc --- layout/svg/base/src/nsSVGGlyphFrame.cpp | 589 +++++++++++------------- layout/svg/base/src/nsSVGGlyphFrame.h | 51 +- 2 files changed, 300 insertions(+), 340 deletions(-) diff --git a/layout/svg/base/src/nsSVGGlyphFrame.cpp b/layout/svg/base/src/nsSVGGlyphFrame.cpp index c438107c20ee..2c8f0d1e09cd 100644 --- a/layout/svg/base/src/nsSVGGlyphFrame.cpp +++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp @@ -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 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 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; iCurrentMatrix(); + 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 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 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 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 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); } diff --git a/layout/svg/base/src/nsSVGGlyphFrame.h b/layout/svg/base/src/nsSVGGlyphFrame.h index fa68f1fdc770..69a097be4a49 100644 --- a/layout/svg/base/src/nsSVGGlyphFrame.h +++ b/layout/svg/base/src/nsSVGGlyphFrame.h @@ -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 mCT; + nsAutoPtr 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 mFontGroup; + nsAutoPtr mFontStyle; + gfxPoint mPosition; + PRUint8 mWhitespaceHandling; }; #endif