1732648
[gecko.git] / 
blob1732648102c2673d3edf5eea9f89ace5777545bd
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  *
3  * The contents of this file are subject to the Netscape Public
4  * License Version 1.1 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of
6  * the License at http://www.mozilla.org/NPL/
7  *
8  * Software distributed under the License is distributed on an "AS
9  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10  * implied. See the License for the specific language governing
11  * rights and limitations under the License.
12  *
13  * The Original Code is mozilla.org code.
14  *
15  * The Initial Developer of the Original Code is Netscape
16  * Communications Corporation.  Portions created by Netscape are
17  * Copyright (C) 1998 Netscape Communications Corporation. All
18  * Rights Reserved.
19  *
20  * Contributor(s): 
21  *   Roland Mainz <Roland.Mainz@informatik.med.uni-giessen.de>
22  *
23  * This Original Code has been modified by IBM Corporation. Modifications made by IBM 
24  * described herein are Copyright (c) International Business Machines Corporation, 2000.
25  * Modifications to Mozilla code or documentation identified per MPL Section 3.3
26  *
27  * Date             Modified by     Description of modification
28  * 04/20/2000       IBM Corp.      OS/2 VisualAge build.
29  */
31 #include "nsDeviceContext.h"
32 #include "nsFont.h"
33 #include "nsIView.h"
34 #include "nsGfxCIID.h"
35 #include "nsVoidArray.h"
36 #include "nsIFontMetrics.h"
37 #include "nsHashtable.h"
38 #include "nsILanguageAtomService.h"
39 #include "nsIServiceManager.h"
41 // Needed for Localization
42 #include "nsIServiceManager.h"
43 #include "nsIIOService.h"
44 #include "nsIURI.h"
45 #include "nsIStringBundle.h"
46 #include "nsITextContent.h"
47 #include "nsISupportsArray.h"
48 #include "nsXPIDLString.h"
49 #include "nsNetCID.h"
51 static NS_DEFINE_CID(kIOServiceCID,            NS_IOSERVICE_CID);
52 static NS_DEFINE_CID(kStringBundleServiceCID,  NS_STRINGBUNDLESERVICE_CID);
53 // done I10N
55 NS_IMPL_ISUPPORTS1(DeviceContextImpl, nsIDeviceContext)
57 DeviceContextImpl :: DeviceContextImpl()
59   NS_INIT_REFCNT();
60   mFontCache = nsnull;
61   mDevUnitsToAppUnits = 1.0f;
62   mAppUnitsToDevUnits = 1.0f;
63   mGammaValue = 1.0f;
64   mCPixelScale = 1.0f;
65   mGammaTable = new PRUint8[256];
66   mZoom = 1.0f;
67   mTextZoom = 1.0f;
68   mWidget = nsnull;
69   mFontAliasTable = nsnull;
72 static PRBool PR_CALLBACK DeleteValue(nsHashKey* aKey, void* aValue, void* closure)
74   delete ((nsString*)aValue);
75   return PR_TRUE;
78 DeviceContextImpl :: ~DeviceContextImpl()
80   if (nsnull != mFontCache)
81   {
82     delete mFontCache;
83     mFontCache = nsnull;
84   }
86   if (nsnull != mGammaTable)
87   {
88     delete[] mGammaTable;
89     mGammaTable = nsnull;
90   }
92   if (nsnull != mFontAliasTable) {
93     mFontAliasTable->Enumerate(DeleteValue);
94     delete mFontAliasTable;
95   }
98 NS_IMETHODIMP DeviceContextImpl :: Init(nsNativeWidget aWidget)
100   mWidget = aWidget;
102   CommonInit();
104   return NS_OK;
107 void DeviceContextImpl :: CommonInit(void)
109   for (PRInt32 cnt = 0; cnt < 256; cnt++)
110     mGammaTable[cnt] = cnt;
113 NS_IMETHODIMP DeviceContextImpl :: GetTwipsToDevUnits(float &aTwipsToDevUnits) const
115   aTwipsToDevUnits = mTwipsToPixels;
116   return NS_OK;
119 NS_IMETHODIMP DeviceContextImpl :: GetDevUnitsToTwips(float &aDevUnitsToTwips) const
121   aDevUnitsToTwips = mPixelsToTwips;
122   return NS_OK;
125 NS_IMETHODIMP DeviceContextImpl :: SetAppUnitsToDevUnits(float aAppUnits)
127   mAppUnitsToDevUnits = aAppUnits;
128   return NS_OK;
131 NS_IMETHODIMP DeviceContextImpl :: SetDevUnitsToAppUnits(float aDevUnits)
133   mDevUnitsToAppUnits = aDevUnits;
134   return NS_OK;
137 NS_IMETHODIMP DeviceContextImpl :: GetAppUnitsToDevUnits(float &aAppUnits) const
139   aAppUnits = mAppUnitsToDevUnits;
140   return NS_OK;
143 NS_IMETHODIMP DeviceContextImpl :: GetDevUnitsToAppUnits(float &aDevUnits) const
145   aDevUnits = mDevUnitsToAppUnits;
146   return NS_OK;
149 NS_IMETHODIMP DeviceContextImpl :: GetCanonicalPixelScale(float &aScale) const
151   aScale = mCPixelScale;
152   return NS_OK;
155 static NS_DEFINE_CID(kRCCID, NS_RENDERING_CONTEXT_CID);
157 NS_IMETHODIMP DeviceContextImpl :: CreateRenderingContext(nsIView *aView, nsIRenderingContext *&aContext)
159   nsIRenderingContext *pContext;
160   nsIWidget           *win;
161   aView->GetWidget(win);
162   nsresult             rv;
164   aContext = nsnull;
165   rv = nsComponentManager::CreateInstance(kRCCID, nsnull, NS_GET_IID(nsIRenderingContext), (void **)&pContext);
167   if (NS_OK == rv) {
168     rv = InitRenderingContext(pContext, win);
169     if (NS_OK != rv) {
170       NS_RELEASE(pContext);
171     }
172   }
174   NS_IF_RELEASE(win);
175   aContext = pContext;
176   return rv;
179 NS_IMETHODIMP DeviceContextImpl :: CreateRenderingContext(nsIWidget *aWidget, nsIRenderingContext *&aContext)
181   nsIRenderingContext *pContext;
182   nsresult             rv;
184   aContext = nsnull;
185   rv = nsComponentManager::CreateInstance(kRCCID, nsnull, NS_GET_IID(nsIRenderingContext), (void **)&pContext);
187   if (NS_OK == rv) {
188     rv = InitRenderingContext(pContext, aWidget);
189     if (NS_OK != rv) {
190       NS_RELEASE(pContext);
191     }
192   }
194   aContext = pContext;
195   return rv;
198 NS_IMETHODIMP DeviceContextImpl :: InitRenderingContext(nsIRenderingContext *aContext, nsIWidget *aWin)
200   return aContext->Init(this, aWin);
203 NS_IMETHODIMP DeviceContextImpl::CreateFontCache()
205   mFontCache = new nsFontCache();
206   if (nsnull == mFontCache) {
207     return NS_ERROR_OUT_OF_MEMORY;
208   }
209   mFontCache->Init(this);
210   return NS_OK;
213 void
214 DeviceContextImpl::GetLocaleLangGroup(void)
216   if (!mLocaleLangGroup) {
217     nsCOMPtr<nsILanguageAtomService> langService;
218     langService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
219     if (langService) {
220       langService->GetLocaleLanguageGroup(getter_AddRefs(mLocaleLangGroup));
221     }
222     if (!mLocaleLangGroup) {
223       mLocaleLangGroup = getter_AddRefs(NS_NewAtom("x-western"));
224     }
225   }
228 NS_IMETHODIMP DeviceContextImpl::GetMetricsFor(const nsFont& aFont,
229   nsIAtom* aLangGroup, nsIFontMetrics*& aMetrics)
231   if (nsnull == mFontCache) {
232     nsresult  rv = CreateFontCache();
233     if (NS_FAILED(rv)) {
234       aMetrics = nsnull;
235       return rv;
236     }
237     // XXX temporary fix for performance problem -- erik
238     GetLocaleLangGroup();
239   }
241   // XXX figure out why aLangGroup is NULL sometimes
242   if (!aLangGroup) {
243     aLangGroup = mLocaleLangGroup;
244   }
246   return mFontCache->GetMetricsFor(aFont, aLangGroup, aMetrics);
249 NS_IMETHODIMP DeviceContextImpl::GetMetricsFor(const nsFont& aFont, nsIFontMetrics*& aMetrics)
251   if (nsnull == mFontCache) {
252     nsresult  rv = CreateFontCache();
253     if (NS_FAILED(rv)) {
254       aMetrics = nsnull;
255       return rv;
256     }
257     // XXX temporary fix for performance problem -- erik
258     GetLocaleLangGroup();
259   }
260   return mFontCache->GetMetricsFor(aFont, mLocaleLangGroup, aMetrics);
263 NS_IMETHODIMP DeviceContextImpl :: SetZoom(float aZoom)
265   if (mZoom != aZoom) {
266     mZoom = aZoom;
267     FlushFontCache();
268   }
269   return NS_OK;
272 NS_IMETHODIMP DeviceContextImpl :: GetZoom(float &aZoom) const
274   aZoom = mZoom;
275   return NS_OK;
278 NS_IMETHODIMP DeviceContextImpl :: SetTextZoom(float aTextZoom)
280   if (mTextZoom != aTextZoom) {
281     mTextZoom = aTextZoom;
282     FlushFontCache();
283   }
284   return NS_OK;
287 NS_IMETHODIMP DeviceContextImpl :: GetTextZoom(float &aTextZoom) const
289   aTextZoom = mTextZoom;
290   return NS_OK;
293 NS_IMETHODIMP DeviceContextImpl :: GetGamma(float &aGamma)
295   aGamma = mGammaValue;
296   return NS_OK;
299 NS_IMETHODIMP DeviceContextImpl :: SetGamma(float aGamma)
301   if (aGamma != mGammaValue)
302   {
303     //we don't need to-recorrect existing images for this case
304     //so pass in 1.0 for the current gamma regardless of what it
305     //really happens to be. existing images will get a one time
306     //re-correction when they're rendered the next time. MMP
308     SetGammaTable(mGammaTable, 1.0f, aGamma);
310     mGammaValue = aGamma;
311   }
312   return NS_OK;
315 NS_IMETHODIMP DeviceContextImpl :: GetGammaTable(PRUint8 *&aGammaTable)
317   //XXX we really need to ref count this somehow. MMP
318   aGammaTable = mGammaTable;
319   return NS_OK;
322 void DeviceContextImpl :: SetGammaTable(PRUint8 * aTable, float aCurrentGamma, float aNewGamma)
324   double fgval = (1.0f / aCurrentGamma) * (1.0f / aNewGamma);
326   for (PRInt32 cnt = 0; cnt < 256; cnt++)
327     aTable[cnt] = (PRUint8)(pow((double)cnt * (1. / 256.), fgval) * 255.99999999);
330 NS_IMETHODIMP DeviceContextImpl::GetDepth(PRUint32& aDepth)
332   aDepth = 24;
333   return NS_OK;
336 struct FontEnumData {
337   FontEnumData(nsIDeviceContext* aDC, nsString& aFaceName)
338     : mDC(aDC), mFaceName(aFaceName)
339   {}
340   nsIDeviceContext* mDC;
341   nsString&         mFaceName;
344 static PRBool FontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData)
346   FontEnumData* data = (FontEnumData*)aData;
347   // XXX for now, all generic fonts are presumed to exist
348   //     we may want to actually check if there's an installed conversion
349   if (aGeneric) {
350     data->mFaceName = aFamily;
351     return PR_FALSE; // found one, stop.
352   }
353   else {
354     nsAutoString  local;
355     PRBool        aliased;
356     data->mDC->GetLocalFontName(aFamily, local, aliased);
357     if (aliased || (NS_OK == data->mDC->CheckFontExistence(local))) {
358       data->mFaceName = local;
359       return PR_FALSE; // found one, stop.
360     }
361   }
362   return PR_TRUE; // didn't exist, continue looking
365 NS_IMETHODIMP DeviceContextImpl::FirstExistingFont(const nsFont& aFont, nsString& aFaceName)
367   FontEnumData  data(this, aFaceName);
368   if (aFont.EnumerateFamilies(FontEnumCallback, &data)) {
369     return NS_ERROR_FAILURE;  // ran out
370   }
371   return NS_OK;
374 class FontAliasKey: public nsHashKey 
376 public:
377   FontAliasKey(const nsString& aString)
378   {mString.Assign(aString);}
380   virtual PRUint32 HashCode(void) const;
381   virtual PRBool Equals(const nsHashKey *aKey) const;
382   virtual nsHashKey *Clone(void) const;
384   nsAutoString  mString;
387 PRUint32 FontAliasKey::HashCode(void) const
389   PRUint32 hash = 0;
390   const PRUnichar* string = mString.get();
391   PRUnichar ch;
392   while ((ch = *string++) != 0) {
393     // FYI: hash = hash*37 + ch
394     ch = nsCRT::ToUpper(ch);
395     hash = ((hash << 5) + (hash << 2) + hash) + ch;
396   }
397   return hash;
400 PRBool FontAliasKey::Equals(const nsHashKey *aKey) const
402   return mString.EqualsIgnoreCase(((FontAliasKey*)aKey)->mString);
405 nsHashKey* FontAliasKey::Clone(void) const
407   return new FontAliasKey(mString);
409 nsresult DeviceContextImpl::CreateFontAliasTable()
411   nsresult result = NS_OK;
413   if (nsnull == mFontAliasTable) {
414     mFontAliasTable = new nsHashtable();
415     if (nsnull != mFontAliasTable) {
417       nsAutoString  times;              times.AssignWithConversion("Times");
418       nsAutoString  timesNewRoman;      timesNewRoman.AssignWithConversion("Times New Roman");
419       nsAutoString  timesRoman;         timesRoman.AssignWithConversion("Times Roman");
420       nsAutoString  arial;              arial.AssignWithConversion("Arial");
421       nsAutoString  helvetica;          helvetica.AssignWithConversion("Helvetica");
422       nsAutoString  courier;            courier.AssignWithConversion("Courier");
423       nsAutoString  courierNew;         courierNew.AssignWithConversion("Courier New");
424       nsAutoString  nullStr;
426       AliasFont(times, timesNewRoman, timesRoman, PR_FALSE);
427       AliasFont(timesRoman, timesNewRoman, times, PR_FALSE);
428       AliasFont(timesNewRoman, timesRoman, times, PR_FALSE);
429       AliasFont(arial, helvetica, nullStr, PR_FALSE);
430       AliasFont(helvetica, arial, nullStr, PR_FALSE);
431       AliasFont(courier, courierNew, nullStr, PR_TRUE);
432       AliasFont(courierNew, courier, nullStr, PR_FALSE);
433     }
434     else {
435       result = NS_ERROR_OUT_OF_MEMORY;
436     }
437   }
438   return result;
441 nsresult DeviceContextImpl::AliasFont(const nsString& aFont, 
442                                       const nsString& aAlias, const nsString& aAltAlias,
443                                       PRBool aForceAlias)
445   nsresult result = NS_OK;
447   if (nsnull != mFontAliasTable) {
448     if (aForceAlias || (NS_OK != CheckFontExistence(aFont))) {
449       if (NS_OK == CheckFontExistence(aAlias)) {
450         nsString* entry = aAlias.ToNewString();
451         if (nsnull != entry) {
452           FontAliasKey key(aFont);
453           mFontAliasTable->Put(&key, entry);
454         }
455         else {
456           result = NS_ERROR_OUT_OF_MEMORY;
457         }
458       }
459       else if ((0 < aAltAlias.Length()) && (NS_OK == CheckFontExistence(aAltAlias))) {
460         nsString* entry = aAltAlias.ToNewString();
461         if (nsnull != entry) {
462           FontAliasKey key(aFont);
463           mFontAliasTable->Put(&key, entry);
464         }
465         else {
466           result = NS_ERROR_OUT_OF_MEMORY;
467         }
468       }
469     }
470   }
471   else {
472     result = NS_ERROR_FAILURE;
473   }
474   return result;
477 NS_IMETHODIMP DeviceContextImpl::GetLocalFontName(const nsString& aFaceName, nsString& aLocalName,
478                                                   PRBool& aAliased)
480   nsresult result = NS_OK;
482   if (nsnull == mFontAliasTable) {
483     result = CreateFontAliasTable();
484   }
486   if (nsnull != mFontAliasTable) {
487     FontAliasKey key(aFaceName);
488     const nsString* alias = (const nsString*)mFontAliasTable->Get(&key);
489     if (nsnull != alias) {
490       aLocalName = *alias;
491       aAliased = PR_TRUE;
492     }
493     else {
494       aLocalName = aFaceName;
495       aAliased = PR_FALSE;
496     }
497   }
498   return result;
501 NS_IMETHODIMP DeviceContextImpl :: FlushFontCache(void)
503   if (nsnull != mFontCache)
504     mFontCache->Flush();
506   return NS_OK;
509 //----------------------------------------------------------------------------------
510 // Return localized bundle for resource strings
511 nsresult
512 DeviceContextImpl::GetLocalizedBundle(const char * aPropFileName, nsIStringBundle** aStrBundle)
514   NS_ENSURE_ARG_POINTER(aPropFileName);
515   NS_ENSURE_ARG_POINTER(aStrBundle);
517   nsresult rv;
518   nsCOMPtr<nsIStringBundle> bundle;
519   
520   // Create a URL for the string resource file
521   // Create a bundle for the localization
522   nsCOMPtr<nsIIOService> pNetService(do_GetService(kIOServiceCID, &rv));
523   if (NS_SUCCEEDED(rv) && pNetService) {
524     nsCOMPtr<nsIURI> uri;
525     rv = pNetService->NewURI(aPropFileName, nsnull, getter_AddRefs(uri));
526     if (NS_SUCCEEDED(rv) && uri) {
528       // Create bundle
529       nsCOMPtr<nsIStringBundleService> stringService = 
530                do_GetService(kStringBundleServiceCID, &rv);
531       if (NS_SUCCEEDED(rv) && stringService) {
532         nsXPIDLCString spec;
533         rv = uri->GetSpec(getter_Copies(spec));
534         if (NS_SUCCEEDED(rv) && spec) {
535           rv = stringService->CreateBundle(spec, aStrBundle);
536         }
537       }
538     }
539   }
540   return rv;
543 //--------------------------------------------------------
544 // Return localized string 
545 nsresult
546 DeviceContextImpl::GetLocalizedString(nsIStringBundle* aStrBundle, const char* aKey, nsString& oVal)
548   NS_ENSURE_ARG_POINTER(aStrBundle);
549   NS_ENSURE_ARG_POINTER(aKey);
551   // Determine default label from string bundle
552   nsXPIDLString valUni;
553   nsAutoString key; 
554   key.AssignWithConversion(aKey);
555   nsresult rv = aStrBundle->GetStringFromName(key.get(), getter_Copies(valUni));
556   if (NS_SUCCEEDED(rv) && valUni) {
557     oVal.Assign(valUni);
558   } else {
559     oVal.Truncate();
560   }
561   return rv;
565 /////////////////////////////////////////////////////////////
567 MOZ_DECL_CTOR_COUNTER(nsFontCache)
569 nsFontCache :: nsFontCache()
571   MOZ_COUNT_CTOR(nsFontCache);
572   mContext = nsnull;
575 nsFontCache :: ~nsFontCache()
577   MOZ_COUNT_DTOR(nsFontCache);
578   Flush();
581 NS_IMETHODIMP 
582 nsFontCache :: Init(nsIDeviceContext* aContext)
584   NS_PRECONDITION(nsnull != aContext, "null ptr");
585   // Note: we don't hold a reference to the device context, because it
586   // holds a reference to us and we don't want circular references
587   mContext = aContext;
588   return NS_OK;
591 NS_IMETHODIMP 
592 nsFontCache :: GetDeviceContext(nsIDeviceContext *&aContext) const
594   NS_IF_ADDREF(mContext);
595   aContext = mContext;
596   return NS_OK;
599 NS_IMETHODIMP 
600 nsFontCache :: GetMetricsFor(const nsFont& aFont, nsIAtom* aLangGroup,
601   nsIFontMetrics *&aMetrics)
603   // First check our cache
604   PRInt32 n = mFontMetrics.Count();
606   for (PRInt32 cnt = 0; cnt < n; cnt++)
607   {
608     nsIFontMetrics* metrics = NS_STATIC_CAST(nsIFontMetrics*, mFontMetrics[cnt]);
610     const nsFont* font;
611     metrics->GetFont(font);
612     if (aFont.Equals(*font)) {
613       nsCOMPtr<nsIAtom> langGroup;
614       metrics->GetLangGroup(getter_AddRefs(langGroup));
615       if (aLangGroup == langGroup.get()) {
616         if (cnt != 0) {
617           // promote it to the front of the cache
618           for (PRInt32 i = cnt; i > 0; --i)
619             mFontMetrics.ReplaceElementAt(mFontMetrics[i - 1], i);
621           mFontMetrics.ReplaceElementAt(metrics, 0);
622         }
623         NS_ADDREF(aMetrics = metrics);
624         return NS_OK;
625       }
626     }
627   }
629   // It's not in the cache. Get font metrics and then cache them.
631   nsIFontMetrics *fm = nsnull;
632   nsresult rv = CreateFontMetricsInstance(&fm);
634   if (NS_FAILED(rv)) {
635     aMetrics = nsnull;
636     return rv;
637   }
639   rv = fm->Init(aFont, aLangGroup, mContext);
641   if (NS_FAILED(rv)) {
642     aMetrics = nsnull;
643     return rv;
644   }
646   mFontMetrics.AppendElement(fm);
648   NS_ADDREF(fm);
649   aMetrics = fm;
650   return NS_OK;
653 /* PostScript and Xprint module may override this method to create 
654  * nsIFontMetrics objects with their own classes 
655  */
656 NS_IMETHODIMP
657 nsFontCache::CreateFontMetricsInstance(nsIFontMetrics** fm)
659   static NS_DEFINE_CID(kFontMetricsCID, NS_FONT_METRICS_CID);
660   return CallCreateInstance(kFontMetricsCID, fm);
664 nsresult nsFontCache :: Flush()
666   PRInt32 i, n = mFontMetrics.Count();
668   for (i = 0; i < n; i++)
669   {
670     nsIFontMetrics* fm = (nsIFontMetrics*) mFontMetrics.ElementAt(i);
671     fm->Destroy();
672     NS_RELEASE(fm);
673   }
675   mFontMetrics.Clear();
677   return NS_OK;