Back to index

lightning-sunbird  0.9+nobinonly
nsSVGLibartGlyphMetricsFT.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  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Leon Sha <leon.sha@sun.com>
00024  *   Alex Fritze <alex@croczilla.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsCOMPtr.h"
00041 #include "nsISVGGlyphMetricsSource.h"
00042 #include "nsISVGRendererGlyphMetrics.h"
00043 #include "nsPromiseFlatString.h"
00044 #include "nsFont.h"
00045 #include "nsIFontMetrics.h"
00046 #include "nsPresContext.h"
00047 #include "float.h"
00048 #include "nsIDOMSVGMatrix.h"
00049 #include "nsIDOMSVGRect.h"
00050 #include "nsSVGTypeCIDs.h"
00051 #include "nsIComponentManager.h"
00052 #include "nsISVGLibartGlyphMetricsFT.h"
00053 #include "nsIDeviceContext.h"
00054 #include "nsSVGLibartFreetype.h"
00055 #include "libart-incs.h"
00056 #include "nsArray.h"
00057 #include "nsDataHashtable.h"
00058 
00063 
00064 
00068 class nsSVGLibartGlyphMetricsFT : public nsISVGLibartGlyphMetricsFT
00069 {
00070 protected:
00071   friend nsresult NS_NewSVGLibartGlyphMetricsFT(nsISVGRendererGlyphMetrics **result,
00072                                                 nsISVGGlyphMetricsSource *src);
00073   friend void NS_InitSVGLibartGlyphMetricsFTGlobals();
00074   friend void NS_FreeSVGLibartGlyphMetricsFTGlobals();
00075 
00076   nsSVGLibartGlyphMetricsFT(nsISVGGlyphMetricsSource *src);
00077   ~nsSVGLibartGlyphMetricsFT();
00078 
00079   // nsISupports interface:
00080   NS_DECL_ISUPPORTS
00081 
00082   // nsISVGRendererGlyphMetrics interface:
00083   NS_DECL_NSISVGRENDERERGLYPHMETRICS
00084 
00085   // nsISVGLibartGlyphMetrics interface:
00086   NS_IMETHOD_(FT_Face) GetFTFace();
00087   NS_IMETHOD_(float) GetPixelScale();
00088   NS_IMETHOD_(float) GetTwipsToPixels();
00089   NS_IMETHOD_(const FT_BBox*) GetBoundingBox();
00090   NS_IMETHOD_(PRUint32) GetGlyphCount();
00091   NS_IMETHOD_(FT_Glyph) GetGlyphAt(PRUint32 pos);
00092 
00093   // helpers :
00094   void InitializeFace();
00095   void ClearFace() { mFace = nsnull; }
00096   void InitializeGlyphArray();
00097   void ClearGlyphArray();
00098 private:
00099   FT_Face mFace;
00100 
00101   struct GlyphDescriptor {
00102     GlyphDescriptor() : index(0), image(nsnull) {};
00103     ~GlyphDescriptor() { if (image) nsSVGLibartFreetype::ft2->DoneGlyph(image); }
00104     FT_UInt   index; // glyph index
00105     FT_Glyph  image; // glyph image translated to correct position within string
00106   };
00107   
00108   GlyphDescriptor *mGlyphArray;
00109   PRUint32 mGlyphArrayLength;
00110   FT_BBox mBBox;
00111   
00112   nsCOMPtr<nsISVGGlyphMetricsSource> mSource;
00113     
00114 public:
00115   static nsDataHashtable<nsStringHashKey,const nsString*> sFontAliases;  
00116 };
00117 
00120 //----------------------------------------------------------------------
00121 // nsSVGLibartGlyphMetricsFT implementation:
00122 
00123 nsDataHashtable<nsStringHashKey,const nsString*>
00124 nsSVGLibartGlyphMetricsFT::sFontAliases;
00125 
00126 
00127 nsSVGLibartGlyphMetricsFT::nsSVGLibartGlyphMetricsFT(nsISVGGlyphMetricsSource *src)
00128     : mSource(src), mFace(nsnull), mGlyphArray(nsnull), mGlyphArrayLength(0)
00129 {
00130 }
00131 
00132 nsSVGLibartGlyphMetricsFT::~nsSVGLibartGlyphMetricsFT()
00133 {
00134   ClearGlyphArray();
00135   ClearFace();
00136 }
00137 
00138 nsresult
00139 NS_NewSVGLibartGlyphMetricsFT(nsISVGRendererGlyphMetrics **result,
00140                               nsISVGGlyphMetricsSource *src)
00141 {
00142   *result = new nsSVGLibartGlyphMetricsFT(src);
00143   if (!*result) return NS_ERROR_OUT_OF_MEMORY;
00144   
00145   NS_ADDREF(*result);
00146   return NS_OK;
00147 }
00148 
00149 void NS_InitSVGLibartGlyphMetricsFTGlobals()
00150 {
00151   
00152   NS_ASSERTION(!nsSVGLibartGlyphMetricsFT::sFontAliases.IsInitialized(),
00153                "font aliases already initialized");
00154   nsSVGLibartGlyphMetricsFT::sFontAliases.Init(3);
00155 
00156   static NS_NAMED_LITERAL_STRING(arial, "arial");
00157   nsSVGLibartGlyphMetricsFT::sFontAliases.Put(NS_LITERAL_STRING("helvetica"),
00158                                               &arial);
00159 
00160   static NS_NAMED_LITERAL_STRING(courier, "courier new");
00161   nsSVGLibartGlyphMetricsFT::sFontAliases.Put(NS_LITERAL_STRING("courier"),
00162                                               &courier);
00163 
00164   static NS_NAMED_LITERAL_STRING(times, "times new roman");
00165   nsSVGLibartGlyphMetricsFT::sFontAliases.Put(NS_LITERAL_STRING("times"),
00166                                               &times);
00167 }
00168 
00169 void NS_FreeSVGLibartGlyphMetricsFTGlobals()
00170 {
00171     NS_ASSERTION(nsSVGLibartGlyphMetricsFT::sFontAliases.IsInitialized(),
00172                "font aliases never initialized");
00173   nsSVGLibartGlyphMetricsFT::sFontAliases.Clear();
00174 }
00175 
00176 //----------------------------------------------------------------------
00177 // nsISupports methods:
00178 
00179 NS_IMPL_ADDREF(nsSVGLibartGlyphMetricsFT)
00180 NS_IMPL_RELEASE(nsSVGLibartGlyphMetricsFT)
00181 
00182 NS_INTERFACE_MAP_BEGIN(nsSVGLibartGlyphMetricsFT)
00183   NS_INTERFACE_MAP_ENTRY(nsISVGRendererGlyphMetrics)
00184   NS_INTERFACE_MAP_ENTRY(nsISVGLibartGlyphMetricsFT)
00185   NS_INTERFACE_MAP_ENTRY(nsISupports)
00186 NS_INTERFACE_MAP_END
00187 
00188 //----------------------------------------------------------------------
00189 // nsISVGRendererGlyphMetrics methods:
00190 
00192 NS_IMETHODIMP
00193 nsSVGLibartGlyphMetricsFT::GetBaselineOffset(PRUint16 baselineIdentifier, float *_retval)
00194 {
00195   *_retval = 0.0f;
00196   
00197   switch (baselineIdentifier) {
00198     case BASELINE_TEXT_BEFORE_EDGE:
00199       break;
00200     case BASELINE_TEXT_AFTER_EDGE:
00201       break;
00202     case BASELINE_CENTRAL:
00203     case BASELINE_MIDDLE:
00204       break;
00205     case BASELINE_ALPHABETIC:
00206     default:
00207     break;
00208   }
00209   
00210   return NS_OK;
00211 }
00212 
00213 
00215 NS_IMETHODIMP
00216 nsSVGLibartGlyphMetricsFT::GetAdvance(float *aAdvance)
00217 {
00218   InitializeGlyphArray();
00219   *aAdvance = mBBox.xMax-mBBox.yMin;
00220   return NS_OK;
00221 }
00222 
00223 NS_IMETHODIMP
00224 nsSVGLibartGlyphMetrics::GetAdvanceOfChar(PRUint32 charnum, float *advance)
00225 {
00226   *advance = 0.0f;
00227   return NS_ERROR_NOT_IMPLEMENTED;
00228 }
00229 
00231 NS_IMETHODIMP
00232 nsSVGLibartGlyphMetricsFT::GetExtentOfChar(PRUint32 charnum, nsIDOMSVGRect **_retval)
00233 {
00234   *_retval = nsnull;
00235 
00236   nsCOMPtr<nsIDOMSVGRect> rect = do_CreateInstance(NS_SVGRECT_CONTRACTID);
00237 
00238   NS_ASSERTION(rect, "could not create rect");
00239   if (!rect) return NS_ERROR_FAILURE;
00240   
00241   rect->SetX(0);
00242   rect->SetY(0);
00243   rect->SetWidth(0);
00244   rect->SetHeight(0);
00245 
00246   *_retval = rect;
00247   NS_ADDREF(*_retval);
00248   
00249   return NS_OK;
00250 }
00251 
00253 NS_IMETHODIMP
00254 nsSVGLibartGlyphMetricsFT::Update(PRUint32 updatemask, PRBool *_retval)
00255 {
00256   *_retval = PR_FALSE;
00257   return NS_OK;
00258 }
00259 
00260 //----------------------------------------------------------------------
00261 // nsISVGLibartGlyphMetrics methods:
00262 
00263 NS_IMETHODIMP_(FT_Face)
00264 nsSVGLibartGlyphMetricsFT::GetFTFace()
00265 {
00266   InitializeFace();
00267   return mFace;
00268 }
00269 
00270 NS_IMETHODIMP_(float)
00271 nsSVGLibartGlyphMetricsFT::GetPixelScale()
00272 {
00273   nsCOMPtr<nsPresContext> presContext;
00274   mSource->GetPresContext(getter_AddRefs(presContext));
00275   if (!presContext) {
00276     NS_ERROR("null prescontext");
00277     return 1.0f;
00278   }
00279 
00280   nsIDeviceContext* devicecontext = presContext->DeviceContext();
00281 
00282   float scale;
00283   devicecontext->GetCanonicalPixelScale(scale);
00284   return scale;
00285 }  
00286 
00287 NS_IMETHODIMP_(float)
00288 nsSVGLibartGlyphMetricsFT::GetTwipsToPixels()
00289 {
00290   nsCOMPtr<nsPresContext> presContext;
00291   mSource->GetPresContext(getter_AddRefs(presContext));
00292   if (!presContext) {
00293     NS_ERROR("null prescontext");
00294     return 1.0f;
00295   }
00296   float twipstopixel;
00297   twipstopixel = presContext->TwipsToPixels();
00298   return twipstopixel;
00299 }  
00300 
00301 NS_IMETHODIMP_(const FT_BBox*)
00302 nsSVGLibartGlyphMetricsFT::GetBoundingBox()
00303 {
00304   InitializeGlyphArray();
00305   return &mBBox;
00306 }
00307 
00308 NS_IMETHODIMP_(PRUint32)
00309 nsSVGLibartGlyphMetricsFT::GetGlyphCount()
00310 {
00311   InitializeGlyphArray();
00312   return mGlyphArrayLength;
00313 }
00314 
00315 NS_IMETHODIMP_(FT_Glyph)
00316 nsSVGLibartGlyphMetricsFT::GetGlyphAt(PRUint32 pos)
00317 {
00318   NS_ASSERTION(pos<mGlyphArrayLength, "out of range");
00319   return mGlyphArray[pos].image;
00320 }
00321 
00322 //----------------------------------------------------------------------
00323 // Implementation helpers:
00324 
00325 
00326 struct FindFontStruct {
00327   nsCOMPtr<nsITrueTypeFontCatalogEntry> font_entry;
00328   nsFont font;
00329 };
00330 
00331 static PRBool
00332 FindFont(const nsString& aFamily, PRBool aGeneric, void *aData)
00333 {
00334   PRBool retval = PR_TRUE;
00335   
00336   FindFontStruct* font_data = (FindFontStruct*) aData;
00337   
00338 #ifdef DEBUG
00339   printf("trying to instantiate font %s, generic=%d\n", NS_ConvertUCS2toUTF8(aFamily).get(),
00340          aGeneric);
00341 #endif
00342 
00343   nsCAutoString family_name;
00344   
00345   if (aGeneric) {
00346     PRUint8 id;
00347     nsFont::GetGenericID(aFamily, &id);
00348     switch (id) {
00349       case kGenericFont_serif:
00350         family_name.AssignLiteral("times new roman");
00351         break;
00352       case kGenericFont_monospace:
00353         family_name.AssignLiteral("courier new");
00354         break;
00355       case kGenericFont_sans_serif:
00356       default:
00357         family_name.AssignLiteral("arial");
00358         break;
00359     }
00360   }
00361   else {
00362     family_name = NS_ConvertUCS2toUTF8(aFamily);
00363     ToLowerCase(family_name);
00364   }
00365   
00366   nsCAutoString language;
00367   PRUint16 slant = nsIFontCatalogService::kFCSlantRoman;
00368   if (font_data->font.style && NS_FONT_STYLE_ITALIC)
00369     slant = nsIFontCatalogService::kFCSlantItalic;
00370     
00371   nsCOMPtr<nsIArray> arr;
00372   nsSVGLibartFreetype::fontCatalog->GetFontCatalogEntries(family_name,
00373                                                           language,
00374                                                           font_data->font.weight,
00375                                                           nsIFontCatalogService::kFCWeightAny,
00376                                                           slant,
00377                                                           nsIFontCatalogService::kFCSpacingAny,
00378                                                           getter_AddRefs(arr));
00379   PRUint32 count;
00380   arr->GetLength(&count);
00381 #ifdef DEBUG
00382   printf("FindFont(%s): number of fonts found: %d\n", family_name.get(), count);
00383 #endif
00384   if (count>0) {
00385     retval =  PR_FALSE; // break
00386     // just take the first one for now:
00387     arr->QueryElementAt(0, NS_GET_IID(nsITrueTypeFontCatalogEntry),
00388                         getter_AddRefs(font_data->font_entry));
00389   }
00390   else {
00391     // try alias if there is one:
00392     const nsString *alias = nsnull;
00393     nsSVGLibartGlyphMetricsFT::sFontAliases.Get(NS_ConvertUTF8toUCS2(family_name),
00394                                                 &alias);
00395     if (alias) {
00396       // XXX this might cause a stack-overflow if there are cyclic
00397       // aliases in sFontAliases
00398       retval = FindFont(*alias, PR_FALSE, aData);      
00399     }
00400   }
00401   
00402   return retval;      
00403 }
00404 
00405 void
00406 nsSVGLibartGlyphMetricsFT::InitializeFace()
00407 {
00408   if (mFace) return; // already initialized
00409   
00410   FindFontStruct font_data;
00411   mSource->GetFont(&font_data.font);
00412 
00413   font_data.font.EnumerateFamilies(FindFont, (void*)&font_data);
00414 
00415   if (!font_data.font_entry) {
00416     // try to find *any* font
00417     nsAutoString empty;
00418     FindFont(empty, PR_FALSE, (void*)&font_data);
00419   }
00420   
00421   if (!font_data.font_entry) {
00422     NS_ERROR("svg libart renderer can't find a font (let alone a suitable one)");
00423     return;
00424   }
00425 
00426   FTC_Image_Desc imageDesc;
00427   imageDesc.font.face_id=(void*)font_data.font_entry.get(); // XXX do we need to addref?
00428   float twipstopixel = GetTwipsToPixels();
00429   float scale = GetPixelScale();
00430   imageDesc.font.pix_width = (int)((float)(font_data.font.size)*twipstopixel/scale);
00431   imageDesc.font.pix_height = (int)((float)(font_data.font.size)*twipstopixel/scale);
00432   imageDesc.image_type |= ftc_image_grays;
00433 
00434   // get the face
00435   nsresult rv;
00436   FTC_Manager mgr;
00437   nsSVGLibartFreetype::ft2->GetFTCacheManager(&mgr);
00438   rv = nsSVGLibartFreetype::ft2->ManagerLookupSize(mgr, &imageDesc.font, &mFace, nsnull);
00439   NS_ASSERTION(mFace, "failed to get face/size");
00440 }
00441 
00442 void nsSVGLibartGlyphMetricsFT::InitializeGlyphArray()
00443 {
00444   if (mGlyphArray) return; // already initialized
00445 
00446   InitializeFace();
00447   if (!mFace) {
00448     NS_ERROR("no face");
00449     return;
00450   }
00451   FT_GlyphSlot glyphslot=mFace->glyph;
00452 
00453   mBBox.xMin = mBBox.yMin = 3200;
00454   mBBox.xMax = mBBox.yMax = -3200;
00455   
00456   nsAutoString text;
00457   mSource->GetCharacterData(text);
00458   mGlyphArrayLength = text.Length();
00459   if (mGlyphArrayLength == 0) return;
00460 
00461   mGlyphArray = new GlyphDescriptor[mGlyphArrayLength];
00462   NS_ASSERTION(mGlyphArray, "could not allocate glyph descriptor array");
00463 
00464   GlyphDescriptor* glyph = mGlyphArray;
00465   bool use_kerning = FT_HAS_KERNING(mFace);
00466   FT_UInt previous_glyph = 0;
00467   FT_Vector pen; // pen position in 26.6 format
00468   pen.x = 0;
00469   pen.y = 0;
00470   
00471   nsAString::const_iterator start, end;
00472   text.BeginReading(start);
00473   text.EndReading(end);
00474   PRUint32 size;
00475   
00476   for ( ; start!=end; start.advance(size)) {
00477     const PRUnichar* buf = start.get();
00478     size = start.size_forward();
00479     // fragment at 'buf' is 'size' characters long
00480     for (PRUint32 i=0; i<size; ++i) {
00481       nsSVGLibartFreetype::ft2->GetCharIndex(mFace, buf[i], &glyph->index);
00482       
00483       if (use_kerning && previous_glyph && glyph->index) {
00484         FT_Vector delta;
00485         nsSVGLibartFreetype::ft2->GetKerning(mFace, previous_glyph,
00486                                              glyph->index,
00487                                              FT_KERNING_DEFAULT,
00488                                              &delta);
00489         pen.x += delta.x;
00490       }
00491 
00492       // load glyph into the face's shared glyph slot:
00493       if (NS_FAILED(nsSVGLibartFreetype::ft2->LoadGlyph(mFace,
00494                                                         glyph->index,
00495                                                         FT_LOAD_DEFAULT))) {
00496         NS_ERROR("error loading glyph");
00497         continue;
00498       }
00499 
00500       // copy glyph image into array:
00501       if (NS_FAILED(nsSVGLibartFreetype::ft2->GetGlyph(glyphslot, &glyph->image))) {
00502         NS_ERROR("error copying glyph");
00503         continue;
00504       }
00505 
00506       // translate glyph image to correct location within string:
00507       nsSVGLibartFreetype::ft2->GlyphTransform(glyph->image, 0, &pen);
00508 
00509       // update the string's bounding box:
00510       FT_BBox glyph_bbox;
00511       nsSVGLibartFreetype::ft2->GlyphGetCBox(glyph->image, ft_glyph_bbox_pixels, &glyph_bbox);
00512       mBBox.xMin = PR_MIN(mBBox.xMin, glyph_bbox.xMin);
00513       mBBox.xMax = PR_MAX(mBBox.xMax, glyph_bbox.xMax);
00514       mBBox.yMin = PR_MIN(mBBox.yMin, glyph_bbox.yMin);
00515       mBBox.yMax = PR_MAX(mBBox.yMax, glyph_bbox.yMax);
00516       
00517       pen.x += glyphslot->advance.x;
00518       previous_glyph = glyph->index;
00519       ++glyph;
00520     }
00521   }
00522 }
00523 
00524 void nsSVGLibartGlyphMetricsFT::ClearGlyphArray()
00525 {
00526   if (mGlyphArray)
00527     delete[] mGlyphArray;
00528   mGlyphArray = nsnull;
00529 }