Back to index

lightning-sunbird  0.9+nobinonly
nsSVGGDIPlusGlyphGeometry.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Mozilla SVG project.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Crocodile Clips Ltd..
00019  * Portions created by the Initial Developer are Copyright (C) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include <windows.h>
00040 
00041 // unknwn.h is needed to build with WIN32_LEAN_AND_MEAN
00042 #include <unknwn.h>
00043 
00044 #include <Gdiplus.h>
00045 using namespace Gdiplus;
00046 
00047 #include "nsCOMPtr.h"
00048 #include "nsSVGGDIPlusGlyphGeometry.h"
00049 #include "nsISVGRendererGlyphGeometry.h"
00050 #include "nsISVGGDIPlusCanvas.h"
00051 #include "nsIDOMSVGMatrix.h"
00052 #include "nsSVGGDIPlusRegion.h"
00053 #include "nsISVGGDIPlusRegion.h"
00054 #include "nsISVGRendererRegion.h"
00055 #include "nsISVGGlyphGeometrySource.h"
00056 #include "nsPromiseFlatString.h"
00057 #include "nsSVGGDIPlusGlyphMetrics.h"
00058 #include "nsISVGGDIPlusGlyphMetrics.h"
00059 #include "nsPresContext.h"
00060 #include "nsMemory.h"
00061 #include "nsSVGGDIPlusGradient.h"
00062 #include "nsSVGTypeCIDs.h"
00063 #include "nsIComponentManager.h"
00064 #include "nsIDOMSVGRect.h"
00065 
00066 #ifndef M_PI
00067 #define M_PI 3.1415926
00068 #endif
00069 
00074 
00075 
00078 class nsSVGGDIPlusGlyphGeometry : public nsISVGRendererGlyphGeometry
00079 {
00080 protected:
00081   friend nsresult NS_NewSVGGDIPlusGlyphGeometry(nsISVGRendererGlyphGeometry **result,
00082                                                 nsISVGGlyphGeometrySource *src);
00083   friend void gradCBPath(Graphics *gfx, Brush *brush, void *cbStruct);
00084 
00085   nsSVGGDIPlusGlyphGeometry();
00086   ~nsSVGGDIPlusGlyphGeometry();
00087   nsresult Init(nsISVGGlyphGeometrySource* src);
00088 
00089 public:
00090   // nsISupports interface:
00091   NS_DECL_ISUPPORTS
00092 
00093   // nsISVGRendererGlyphGeometry interface:
00094   NS_DECL_NSISVGRENDERERGLYPHGEOMETRY
00095 
00096 protected:
00097   void DrawFill(Graphics* g, Brush& b, nsISVGGradient *aGrad,
00098                 const WCHAR* start, INT length, float x, float y);
00099   void GetGlobalTransform(Matrix *matrix);
00100   void UpdateStroke();
00101   void UpdateRegions(); // update covered region & hit-test region
00102   
00103   nsCOMPtr<nsISVGGlyphGeometrySource> mSource;
00104   nsCOMPtr<nsISVGRendererRegion> mCoveredRegion;
00105   Region *mHitTestRegion;
00106   GraphicsPath *mStroke;
00107 };
00108 
00111 //----------------------------------------------------------------------
00112 // implementation:
00113 
00114 nsSVGGDIPlusGlyphGeometry::nsSVGGDIPlusGlyphGeometry()
00115     : mStroke(nsnull), mHitTestRegion(nsnull)
00116 {
00117 }
00118 
00119 nsSVGGDIPlusGlyphGeometry::~nsSVGGDIPlusGlyphGeometry()
00120 {
00121   if (mStroke) delete mStroke;
00122   if (mHitTestRegion) delete mHitTestRegion;
00123 }
00124 
00125 nsresult
00126 nsSVGGDIPlusGlyphGeometry::Init(nsISVGGlyphGeometrySource* src)
00127 {
00128   mSource = src;
00129   return NS_OK;
00130 }
00131 
00132 
00133 nsresult
00134 NS_NewSVGGDIPlusGlyphGeometry(nsISVGRendererGlyphGeometry **result,
00135                               nsISVGGlyphGeometrySource *src)
00136 {
00137   *result = nsnull;
00138   
00139   nsSVGGDIPlusGlyphGeometry* gg = new nsSVGGDIPlusGlyphGeometry();
00140   if (!gg) return NS_ERROR_OUT_OF_MEMORY;
00141 
00142   NS_ADDREF(gg);
00143 
00144   nsresult rv = gg->Init(src);
00145 
00146   if (NS_FAILED(rv)) {
00147     NS_RELEASE(gg);
00148     return rv;
00149   }
00150   
00151   *result = gg;
00152   return rv;
00153 }
00154 
00155 //----------------------------------------------------------------------
00156 // nsISupports methods:
00157 
00158 NS_IMPL_ADDREF(nsSVGGDIPlusGlyphGeometry)
00159 NS_IMPL_RELEASE(nsSVGGDIPlusGlyphGeometry)
00160 
00161 NS_INTERFACE_MAP_BEGIN(nsSVGGDIPlusGlyphGeometry)
00162   NS_INTERFACE_MAP_ENTRY(nsISVGRendererGlyphGeometry)
00163   NS_INTERFACE_MAP_ENTRY(nsISupports)
00164 NS_INTERFACE_MAP_END
00165 
00166 
00167 //----------------------------------------------------------------------
00168 // nsISVGRendererGlyphGeometry methods:
00169 
00170 static void gradCBPath(Graphics *gfx, Brush *brush, void *cbStruct)
00171 {
00172   nsSVGGDIPlusGlyphGeometry *geom = (nsSVGGDIPlusGlyphGeometry *)cbStruct;
00173   gfx->FillPath(brush, geom->mStroke);
00174 }
00175 
00177 NS_IMETHODIMP
00178 nsSVGGDIPlusGlyphGeometry::Render(nsISVGRendererCanvas *canvas)
00179 {
00180   // make sure that we have constructed our render objects
00181   if (!mCoveredRegion) {
00182     nsCOMPtr<nsISVGRendererRegion> region;
00183     Update(nsISVGGeometrySource::UPDATEMASK_ALL, getter_AddRefs(region));
00184   }
00185 
00186   nsCOMPtr<nsISVGGDIPlusCanvas> gdiplusCanvas = do_QueryInterface(canvas);
00187   NS_ASSERTION(gdiplusCanvas, "wrong svg canvas for geometry!");
00188   if (!gdiplusCanvas) return NS_ERROR_FAILURE;
00189 
00190   PRUint16 canvasRenderMode;
00191   canvas->GetRenderMode(&canvasRenderMode);
00192   if (canvasRenderMode == nsISVGRendererCanvas::SVG_RENDER_MODE_CLIP) {
00193     nsCOMPtr<nsISVGGDIPlusGlyphMetrics> metrics;
00194     {
00195       nsCOMPtr<nsISVGRendererGlyphMetrics> xpmetrics;
00196       mSource->GetMetrics(getter_AddRefs(xpmetrics));
00197       metrics = do_QueryInterface(xpmetrics);
00198       NS_ASSERTION(metrics, "wrong metrics object!");
00199       if (!metrics)
00200         return NS_ERROR_FAILURE;
00201     }
00202   
00203     FontFamily fontFamily;
00204     metrics->GetFont()->GetFamily(&fontFamily);
00205   
00206     nsAutoString text;
00207     mSource->GetCharacterData(text);
00208 
00209     float x,y;
00210     mSource->GetX(&x);
00211     mSource->GetY(&y);
00212   
00213     GraphicsPath path;
00214     path.AddString(PromiseFlatString(text).get(), -1,
00215                    &fontFamily, metrics->GetFont()->GetStyle(),
00216                    metrics->GetFont()->GetSize(),
00217                    PointF(x,y), StringFormat::GenericTypographic());
00218 
00219     PRUint16 rule;
00220     mSource->GetClipRule(&rule);
00221     if (rule == nsISVGGeometrySource::FILL_RULE_EVENODD)
00222       path.SetFillMode(FillModeAlternate);
00223     else
00224       path.SetFillMode(FillModeWinding);
00225 
00226     Region *region = gdiplusCanvas->GetClipRegion();
00227     if (region)
00228       region->Union(&path);
00229     return NS_OK;
00230   }
00231 
00232   PRBool hasFill = PR_FALSE, hasStroke = PR_FALSE;
00233   PRUint16 filltype, stroketype;
00234   PRUint16 fillServerType = 0, strokeServerType = 0;
00235 
00236   mSource->GetFillPaintType(&filltype);
00237   if (filltype != nsISVGGeometrySource::PAINT_TYPE_NONE)
00238     hasFill = PR_TRUE;
00239 
00240   if (filltype == nsISVGGeometrySource::PAINT_TYPE_SERVER) {
00241     if(NS_FAILED(mSource->GetFillPaintServerType(&fillServerType)))
00242       hasFill = PR_FALSE;
00243   }
00244 
00245   mSource->GetStrokePaintType(&stroketype);
00246   if (stroketype != nsISVGGeometrySource::PAINT_TYPE_NONE && mStroke)
00247     hasStroke = PR_TRUE;
00248 
00249   if (stroketype == nsISVGGeometrySource::PAINT_TYPE_SERVER) {
00250     if(NS_FAILED(mSource->GetStrokePaintServerType(&strokeServerType)))
00251       hasStroke = PR_FALSE;
00252   }
00253 
00254   if (!hasFill && !hasStroke) return NS_OK; // nothing to paint
00255   
00256   gdiplusCanvas->GetGraphics()->SetSmoothingMode(SmoothingModeAntiAlias);
00257   //gdiplusCanvas->GetGraphics()->SetPixelOffsetMode(PixelOffsetModeHalf);
00258   
00259   nsAutoString text;
00260   mSource->GetCharacterData(text);
00261 
00262   float x,y;
00263   mSource->GetX(&x);
00264   mSource->GetY(&y);
00265 
00266 
00267   if (hasFill) {
00268     nscolor color;
00269     mSource->GetFillPaint(&color);
00270 
00271     float opacity;
00272     mSource->GetFillOpacity(&opacity);
00273 
00274     SolidBrush brush(Color((BYTE)(opacity*255),NS_GET_R(color),NS_GET_G(color),NS_GET_B(color)));
00275   
00276     nsCOMPtr<nsISVGGradient> aGrad;
00277     if (filltype != nsISVGGeometrySource::PAINT_TYPE_SOLID_COLOR) {
00278       if (fillServerType == nsISVGGeometrySource::PAINT_TYPE_GRADIENT) {
00279         mSource->GetFillGradient(getter_AddRefs(aGrad));
00280       }
00281     }
00282   
00283     nsAutoString text;
00284     mSource->GetCharacterData(text);
00285 
00286     DrawFill(gdiplusCanvas->GetGraphics(), brush, aGrad,
00287              PromiseFlatString(text).get(), text.Length(), x, y);
00288   }
00289   
00290   if (hasStroke) {
00291     nscolor color;
00292     mSource->GetStrokePaint(&color);
00293         
00294     float opacity;
00295     mSource->GetStrokeOpacity(&opacity);
00296   
00297     nsCOMPtr<nsISVGGradient> aGrad;
00298     if (stroketype != nsISVGGeometrySource::PAINT_TYPE_SOLID_COLOR) {
00299       if (strokeServerType == nsISVGGeometrySource::PAINT_TYPE_GRADIENT) {
00300         mSource->GetStrokeGradient(getter_AddRefs(aGrad));
00301       }
00302     }
00303 
00304     SolidBrush brush(Color((BYTE)(opacity*255), NS_GET_R(color), NS_GET_G(color), NS_GET_B(color)));
00305 
00306     // this is the 'normal' case
00307     if (stroketype == nsISVGGeometrySource::PAINT_TYPE_SOLID_COLOR) {
00308       gdiplusCanvas->GetGraphics()->FillPath(&brush, mStroke);
00309     } else if (strokeServerType == nsISVGGeometrySource::PAINT_TYPE_GRADIENT) {
00310       nsCOMPtr<nsISVGRendererRegion> region;
00311       GetCoveredRegion(getter_AddRefs(region));
00312       nsCOMPtr<nsISVGGDIPlusRegion> aRegion = do_QueryInterface(region);
00313       nsCOMPtr<nsIDOMSVGMatrix> ctm;
00314       mSource->GetCanvasTM(getter_AddRefs(ctm));
00315         
00316       GDIPlusGradient(aRegion, aGrad, ctm,
00317                       gdiplusCanvas->GetGraphics(), mSource,
00318                       gradCBPath, this);
00319     }
00320   }
00321   
00322   return NS_OK;
00323 }
00324 
00326 NS_IMETHODIMP
00327 nsSVGGDIPlusGlyphGeometry::Update(PRUint32 updatemask, nsISVGRendererRegion **_retval)
00328 {
00329   *_retval = nsnull;
00330 
00331   const unsigned long strokemask =
00332     nsISVGGlyphMetricsSource::UPDATEMASK_FONT           |
00333     nsISVGGlyphMetricsSource::UPDATEMASK_CHARACTER_DATA |
00334     nsISVGGlyphGeometrySource::UPDATEMASK_METRICS       |
00335     nsISVGGlyphGeometrySource::UPDATEMASK_X             |
00336     nsISVGGlyphGeometrySource::UPDATEMASK_Y             |
00337     nsISVGGeometrySource::UPDATEMASK_STROKE_PAINT_TYPE  |
00338     nsISVGGeometrySource::UPDATEMASK_STROKE_WIDTH       |
00339     nsISVGGeometrySource::UPDATEMASK_STROKE_LINECAP     |
00340     nsISVGGeometrySource::UPDATEMASK_STROKE_LINEJOIN    |
00341     nsISVGGeometrySource::UPDATEMASK_STROKE_MITERLIMIT  |
00342     nsISVGGeometrySource::UPDATEMASK_STROKE_DASH_ARRAY  |
00343     nsISVGGeometrySource::UPDATEMASK_STROKE_DASHOFFSET  |
00344     nsISVGGeometrySource::UPDATEMASK_CANVAS_TM;
00345   
00346   const unsigned long regionsmask =
00347     nsISVGGlyphGeometrySource::UPDATEMASK_METRICS |
00348     nsISVGGlyphGeometrySource::UPDATEMASK_X       |
00349     nsISVGGlyphGeometrySource::UPDATEMASK_Y       |
00350     nsISVGGeometrySource::UPDATEMASK_CANVAS_TM;
00351 
00352   
00353   nsCOMPtr<nsISVGRendererRegion> regionBefore = mCoveredRegion;
00354 
00355     
00356   if ((updatemask & strokemask)!=0) {
00357     UpdateStroke();
00358   }
00359 
00360   if ((updatemask & regionsmask)!=0) {
00361     UpdateRegions();
00362     if (regionBefore) {
00363       regionBefore->Combine(mCoveredRegion, _retval);
00364     }
00365     else {
00366       *_retval = mCoveredRegion;
00367       NS_IF_ADDREF(*_retval);
00368     }
00369   }
00370 
00371   if (!*_retval) {
00372     // region hasn't changed, but something has. so invalidate whole area:
00373     *_retval = regionBefore;
00374     NS_IF_ADDREF(*_retval);
00375   }    
00376       
00377   return NS_OK;
00378 }
00379 
00381 NS_IMETHODIMP
00382 nsSVGGDIPlusGlyphGeometry::GetCoveredRegion(nsISVGRendererRegion **_retval)
00383 {
00384   *_retval = mCoveredRegion;
00385   NS_IF_ADDREF(*_retval);
00386   return NS_OK;
00387 }
00388 
00390 NS_IMETHODIMP
00391 nsSVGGDIPlusGlyphGeometry::ContainsPoint(float x, float y, PRBool *_retval)
00392 {
00393   *_retval = PR_FALSE;
00394 
00395    if (mHitTestRegion && mHitTestRegion->IsVisible(x,y)) {
00396      *_retval = PR_TRUE;
00397    }
00398 
00399   return NS_OK;
00400 }
00401 
00402 //----------------------------------------------------------------------
00403 //
00404 
00405 struct gradCallbackStruct {
00406   const WCHAR *start;
00407   INT length;
00408   nsISVGGDIPlusGlyphMetrics *metrics;
00409   float x, y;
00410   StringFormat *stringformat;
00411   nsSVGCharacterPosition *cp;
00412 };
00413 
00414 static void gradCBString(Graphics *gfx, Brush *brush, void *cbStruct)
00415 {
00416   gradCallbackStruct *info = (gradCallbackStruct *)cbStruct;
00417   gfx->DrawString(info->start, info->length, info->metrics->GetFont(),
00418                   PointF(info->x, info->y), info->stringformat, brush);
00419   
00420   nsSVGCharacterPosition *cp = info->cp;
00421 
00422   if (!cp) {
00423     gfx->DrawString(info->start, info->length, info->metrics->GetFont(),
00424                     PointF(info->x, info->y), info->stringformat, brush);
00425   } else {
00426     for (PRUint32 i=0; i<info->length; i++) {
00427       if (cp[i].draw == PR_FALSE)
00428         continue;
00429       GraphicsState state = gfx->Save();
00430       gfx->TranslateTransform(cp[i].x, cp[i].y);
00431       gfx->RotateTransform(cp[i].angle * 180.0 / M_PI);
00432       gfx->DrawString(info->start + i, 1, info->metrics->GetFont(), PointF(0, 0),
00433                       info->stringformat, brush);
00434       gfx->Restore(state);
00435     }
00436   }
00437 }
00438 
00439 void
00440 nsSVGGDIPlusGlyphGeometry::DrawFill(Graphics* g, Brush& b, nsISVGGradient *aGrad,
00441                                     const WCHAR* start, INT length,
00442                                     float x, float y)
00443 {
00444   nsCOMPtr<nsISVGGDIPlusGlyphMetrics> metrics;
00445   {
00446     nsCOMPtr<nsISVGRendererGlyphMetrics> xpmetrics;
00447     mSource->GetMetrics(getter_AddRefs(xpmetrics));
00448     metrics = do_QueryInterface(xpmetrics);
00449     NS_ASSERTION(metrics, "wrong metrics object!");
00450     if (!metrics) return;
00451   }
00452 
00453   GraphicsState state = g->Save();
00454   
00455   Matrix m;
00456   GetGlobalTransform(&m);
00457   g->MultiplyTransform(&m);
00458   g->SetTextRenderingHint(metrics->GetTextRenderingHint());
00459 
00460   StringFormat stringFormat(StringFormat::GenericTypographic());
00461   stringFormat.SetFormatFlags(stringFormat.GetFormatFlags() |
00462                               StringFormatFlagsMeasureTrailingSpaces);
00463   stringFormat.SetLineAlignment(StringAlignmentNear);
00464 
00465   nsSVGCharacterPosition *cp;
00466   if (NS_FAILED(mSource->GetCharacterPosition(&cp)))
00467     return;
00468   
00469   if (aGrad) {
00470     nsCOMPtr<nsISVGRendererRegion> region;
00471     GetCoveredRegion(getter_AddRefs(region));
00472     nsCOMPtr<nsISVGGDIPlusRegion> aRegion = do_QueryInterface(region);
00473     nsCOMPtr<nsIDOMSVGMatrix> ctm;
00474     mSource->GetCanvasTM(getter_AddRefs(ctm));
00475     
00476     gradCallbackStruct cb;
00477     cb.start = start;
00478     cb.length = length;
00479     cb.metrics = metrics;
00480     cb.x = x;  cb.y = y;
00481     cb.stringformat = &stringFormat;
00482     cb.cp = cp;
00483 
00484     GDIPlusGradient(aRegion, aGrad, ctm, g, mSource, gradCBString, &cb);
00485   }
00486   else {
00487     if (!cp) {
00488       g->DrawString(start, length, metrics->GetFont(), PointF(x,y),
00489                     &stringFormat, &b);
00490     } else {
00491       for (PRUint32 i=0; i<length; i++) {
00492         if (cp[i].draw == PR_FALSE)
00493           continue;
00494         GraphicsState state = g->Save();
00495         g->TranslateTransform(cp[i].x, cp[i].y);
00496         g->RotateTransform(cp[i].angle * 180.0 / M_PI);
00497         g->DrawString(start + i, 1, metrics->GetFont(), PointF(0, 0),
00498                       &stringFormat, &b);
00499         g->Restore(state);
00500       }
00501     }
00502   }
00503   
00504   delete [] cp;
00505   
00506   g->Restore(state);
00507 }
00508 
00509 void
00510 nsSVGGDIPlusGlyphGeometry::GetGlobalTransform(Matrix *matrix)
00511 {
00512   nsCOMPtr<nsIDOMSVGMatrix> ctm;
00513   mSource->GetCanvasTM(getter_AddRefs(ctm));
00514   NS_ASSERTION(ctm, "graphic source didn't specify a ctm");
00515   
00516   float m[6];
00517   float val;
00518   ctm->GetA(&val);
00519   m[0] = val;
00520   
00521   ctm->GetB(&val);
00522   m[1] = val;
00523   
00524   ctm->GetC(&val);  
00525   m[2] = val;  
00526   
00527   ctm->GetD(&val);  
00528   m[3] = val;  
00529   
00530   ctm->GetE(&val);
00531   m[4] = val;
00532   
00533   ctm->GetF(&val);
00534   m[5] = val;
00535 
00536   matrix->SetElements(m[0],m[1],m[2],m[3],m[4],m[5]);
00537 }
00538 
00539 void
00540 nsSVGGDIPlusGlyphGeometry::UpdateStroke()
00541 {
00542   if (mStroke) {
00543     delete mStroke;
00544     mStroke = nsnull;
00545   }
00546 
00547   PRUint16 type;
00548   mSource->GetStrokePaintType(&type);
00549   if (type == nsISVGGeometrySource::PAINT_TYPE_NONE)
00550     return;
00551 
00552   float width;
00553   mSource->GetStrokeWidth(&width);
00554   
00555   if (width==0.0f) return;
00556 
00557   nsCOMPtr<nsISVGGDIPlusGlyphMetrics> metrics;
00558   {
00559     nsCOMPtr<nsISVGRendererGlyphMetrics> xpmetrics;
00560     mSource->GetMetrics(getter_AddRefs(xpmetrics));
00561     metrics = do_QueryInterface(xpmetrics);
00562     NS_ASSERTION(metrics, "wrong metrics object!");
00563     if (!metrics) return;
00564   }
00565 
00566   
00567   mStroke = new GraphicsPath();
00568   if (!mStroke) {
00569     NS_ERROR("couldn't construct graphicspath");
00570     return;
00571   }
00572 
00573   FontFamily fontFamily;
00574   metrics->GetFont()->GetFamily(&fontFamily);
00575   
00576   nsAutoString text;
00577   mSource->GetCharacterData(text);
00578 
00579   float x,y;
00580   mSource->GetX(&x);
00581   mSource->GetY(&y);
00582 
00583   nsSVGCharacterPosition *cp;
00584   if (NS_FAILED(mSource->GetCharacterPosition(&cp)))
00585     return;
00586 
00587   if (!cp) {
00588     mStroke->AddString(PromiseFlatString(text).get(), -1, &fontFamily,
00589                        metrics->GetFont()->GetStyle(),
00590                        metrics->GetFont()->GetSize(),
00591                        PointF(x,y), StringFormat::GenericTypographic());
00592   } else {
00593     for (PRUint32 i=0; i<text.Length(); i++) {
00594       if (cp[i].draw == PR_FALSE)
00595         continue;
00596       GraphicsPath glyph;
00597       Matrix matrix;
00598 
00599       matrix.Translate(cp[i].x, cp[i].y);
00600       matrix.Rotate(cp[i].angle * 180.0 / M_PI);
00601       glyph.AddString(PromiseFlatString(text).get() + i, 1, &fontFamily,
00602                       metrics->GetFont()->GetStyle(),
00603                       metrics->GetFont()->GetSize(),
00604                       PointF(0,0), StringFormat::GenericTypographic());
00605       glyph.Transform(&matrix);
00606       mStroke->AddPath(&glyph, FALSE);
00607     }
00608   }
00609   
00610   delete [] cp;
00611 
00612   // gdi+ seems to widen paths to >1 width unit. If our pen width is
00613   // smaller, we need to widen an appropriately enlarged path and then
00614   // scale it down after widening:
00615   PRBool scaleToUnitPen = width<1.0f;
00616   
00617   Pen pen(Color(), scaleToUnitPen ? 1.0f : width);
00618 
00619   // set linecap style
00620   PRUint16 capStyle;
00621   LineCap lcap;
00622   DashCap dcap;
00623   mSource->GetStrokeLinecap(&capStyle);
00624   switch (capStyle) {
00625     case nsISVGGeometrySource::STROKE_LINECAP_BUTT:
00626       lcap = LineCapFlat;
00627       dcap = DashCapFlat;
00628       break;
00629     case nsISVGGeometrySource::STROKE_LINECAP_ROUND:
00630       lcap = LineCapRound;
00631       dcap = DashCapRound;
00632       break;
00633     case nsISVGGeometrySource::STROKE_LINECAP_SQUARE:
00634       lcap = LineCapSquare;
00635       dcap = DashCapFlat;
00636       break;
00637   }
00638   pen.SetLineCap(lcap, lcap, dcap);
00639 
00640 
00641   // set linejoin style:
00642   PRUint16 joinStyle;
00643   LineJoin join;
00644   mSource->GetStrokeLinejoin(&joinStyle);
00645   switch(joinStyle) {
00646     case nsISVGGeometrySource::STROKE_LINEJOIN_MITER:
00647       join = LineJoinMiterClipped;
00648       break;
00649     case nsISVGGeometrySource::STROKE_LINEJOIN_ROUND:
00650       join = LineJoinRound;
00651       break;
00652     case nsISVGGeometrySource::STROKE_LINEJOIN_BEVEL:
00653       join = LineJoinBevel;
00654       break;
00655   }
00656   pen.SetLineJoin(join);
00657   
00658   // set miterlimit:
00659   float miterlimit;
00660   mSource->GetStrokeMiterlimit(&miterlimit);
00661   pen.SetMiterLimit(miterlimit);
00662 
00663   // set pen dashpattern
00664   float *dashArray;
00665   PRUint32 count;
00666   mSource->GetStrokeDashArray(&dashArray, &count);
00667   if (count>0) {
00668     if (!scaleToUnitPen) {
00669       for (PRUint32 i=0; i<count; ++i)
00670         dashArray[i]/=width;
00671     }
00672     
00673     pen.SetDashPattern(dashArray, count);
00674     nsMemory::Free(dashArray);
00675 
00676     float offset;
00677     mSource->GetStrokeDashoffset(&offset);
00678     if (!scaleToUnitPen)
00679       offset/=width;
00680     pen.SetDashOffset(offset);
00681   }
00682 
00683   Matrix m;
00684   GetGlobalTransform(&m);
00685 
00686   if (scaleToUnitPen) {
00687     Matrix enlarge(1/width, 0, 0, 1/width, 0, 0);
00688     mStroke->Transform(&enlarge);
00689     m.Scale(width,width);
00690   }
00691 
00692   
00693   mStroke->Widen(&pen);
00694   mStroke->Transform(&m);
00695 //  mStroke->Outline();
00696 }
00697 
00698 void
00699 nsSVGGDIPlusGlyphGeometry::UpdateRegions()
00700 {
00701   if (mHitTestRegion) {
00702     delete mHitTestRegion;
00703     mHitTestRegion = nsnull;
00704   }
00705 
00706   nsCOMPtr<nsISVGGDIPlusGlyphMetrics> metrics;
00707   {
00708     nsCOMPtr<nsISVGRendererGlyphMetrics> xpmetrics;
00709     mSource->GetMetrics(getter_AddRefs(xpmetrics));
00710     metrics = do_QueryInterface(xpmetrics);
00711     NS_ASSERTION(metrics, "wrong metrics object!");
00712     if (!metrics) return;
00713   }
00714 
00715   nsAutoString text;
00716   mSource->GetCharacterData(text);
00717   nsSVGCharacterPosition *cp;
00718   if (NS_FAILED(mSource->GetCharacterPosition(&cp)))
00719     return;
00720 
00721   if (!cp) {
00722     mHitTestRegion = new Region(*metrics->GetBoundingRect());
00723     if (!mHitTestRegion)
00724       return;
00725     
00726     float x,y;
00727     mSource->GetX(&x);
00728     mSource->GetY(&y);
00729     mHitTestRegion->Translate(x, y);
00730   } else {
00731     GraphicsPath string;
00732     for (PRUint32 i=0; i<text.Length(); i++) {
00733       if (cp[i].draw == PR_FALSE)
00734         continue;
00735       GraphicsPath glyph;
00736       Matrix matrix;
00737 
00738       matrix.Translate(cp[i].x, cp[i].y);
00739       matrix.Rotate(cp[i].angle * 180.0 / M_PI);
00740 
00741       RectF bounds;
00742       metrics->GetSubBoundingRect(i, 1, &bounds);
00743       bounds.X = bounds.Y = 0;
00744       glyph.AddRectangle(bounds);
00745       glyph.Transform(&matrix);
00746       string.AddPath(&glyph, FALSE);
00747     }
00748     mHitTestRegion = new Region(&string);
00749     if (!mHitTestRegion) {
00750       delete [] cp;
00751       return;
00752     }
00753   }
00754 
00755   delete [] cp;
00756 
00757   Matrix m;
00758   GetGlobalTransform(&m);
00759   mHitTestRegion->Transform(&m);
00760 
00761   // clone the covered region from the hit-test region:
00762 
00763   nsCOMPtr<nsPresContext> presContext;
00764   mSource->GetPresContext(getter_AddRefs(presContext));
00765 
00766   NS_NewSVGGDIPlusClonedRegion(getter_AddRefs(mCoveredRegion),
00767                                mHitTestRegion,
00768                                presContext);
00769 }
00770 
00771 NS_IMETHODIMP
00772 nsSVGGDIPlusGlyphGeometry::GetBoundingBox(nsIDOMSVGRect * *aBoundingBox)
00773 {
00774   *aBoundingBox = nsnull;
00775 
00776   nsCOMPtr<nsIDOMSVGRect> rect = do_CreateInstance(NS_SVGRECT_CONTRACTID);
00777   NS_ASSERTION(rect, "could not create rect");
00778   if (!rect)
00779     return NS_ERROR_FAILURE;
00780 
00781   nsCOMPtr<nsISVGGDIPlusGlyphMetrics> metrics;
00782   {
00783     nsCOMPtr<nsISVGRendererGlyphMetrics> xpmetrics;
00784     mSource->GetMetrics(getter_AddRefs(xpmetrics));
00785     metrics = do_QueryInterface(xpmetrics);
00786     NS_ASSERTION(metrics, "wrong metrics object!");
00787     if (!metrics) return NS_ERROR_FAILURE;
00788   }
00789 
00790   nsAutoString text;
00791   mSource->GetCharacterData(text);
00792   if (!text.Length())
00793     return NS_OK;
00794 
00795   nsSVGCharacterPosition *cp;
00796   if (NS_FAILED(mSource->GetCharacterPosition(&cp)))
00797     return NS_ERROR_FAILURE;
00798 
00799   RectF bounds;
00800   if (!cp) {
00801     metrics->GetSubBoundingRect(0, text.Length(), &bounds, PR_FALSE);
00802   } else {
00803     GraphicsPath string;
00804     for (PRUint32 i=0; i<text.Length(); i++) {
00805       if (cp[i].draw == PR_FALSE)
00806         continue;
00807       GraphicsPath glyph;
00808       Matrix matrix;
00809 
00810       matrix.Translate(cp[i].x, cp[i].y);
00811       matrix.Rotate(cp[i].angle * 180.0 / M_PI);
00812 
00813       RectF bounds;
00814       metrics->GetSubBoundingRect(i, 1, &bounds, PR_FALSE);
00815       bounds.X = bounds.Y = 0;
00816       glyph.AddRectangle(bounds);
00817       glyph.Transform(&matrix);
00818       string.AddPath(&glyph, FALSE);
00819     }
00820     string.GetBounds(&bounds);
00821   }
00822 
00823   delete [] cp;
00824 
00825   float x,y;
00826   mSource->GetX(&x);
00827   mSource->GetY(&y);
00828 
00829   rect->SetX(bounds.X + x);
00830   rect->SetY(bounds.Y + y);
00831   rect->SetWidth(bounds.Width);
00832   rect->SetHeight(bounds.Height);
00833 
00834   *aBoundingBox = rect;
00835   NS_ADDREF(*aBoundingBox);
00836   
00837   return NS_OK;
00838 }