1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
24 * Roland Mainz <Roland.Mainz@informatik.med.uni-giessen.de>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include "nsDeviceContext.h"
44 #include "nsGfxCIID.h"
45 #include "nsVoidArray.h"
46 #include "nsIFontMetrics.h"
47 #include "nsHashtable.h"
48 #include "nsILanguageAtomService.h"
49 #include "nsIServiceManager.h"
50 #include "nsUnicharUtils.h"
52 #include "nsIRenderingContext.h"
53 #include "gfxUserFontSet.h"
54 #include "nsIThebesFontMetrics.h"
56 NS_IMPL_ISUPPORTS3(DeviceContextImpl
, nsIDeviceContext
, nsIObserver
, nsISupportsWeakReference
)
58 DeviceContextImpl::DeviceContextImpl()
60 mAppUnitsPerDevPixel
= -1;
61 mAppUnitsPerInch
= -1;
62 mAppUnitsPerDevNotScaledPixel
= -1;
67 mFontAliasTable
= nsnull
;
70 mInitialized
= PR_FALSE
;
74 static PRBool
DeleteValue(nsHashKey
* aKey
, void* aValue
, void* closure
)
76 delete ((nsString
*)aValue
);
80 DeviceContextImpl::~DeviceContextImpl()
82 nsCOMPtr
<nsIObserverService
> obs(do_GetService("@mozilla.org/observer-service;1"));
84 obs
->RemoveObserver(this, "memory-pressure");
86 if (nsnull
!= mFontCache
)
92 if (nsnull
!= mFontAliasTable
) {
93 mFontAliasTable
->Enumerate(DeleteValue
);
94 delete mFontAliasTable
;
100 DeviceContextImpl::Observe(nsISupports
* aSubject
, const char* aTopic
, const PRUnichar
* aSomeData
)
102 if (mFontCache
&& !nsCRT::strcmp(aTopic
, "memory-pressure")) {
103 mFontCache
->Compact();
108 NS_IMETHODIMP
DeviceContextImpl::Init(nsNativeWidget aWidget
)
117 void DeviceContextImpl::CommonInit(void)
120 NS_ASSERTION(!mInitialized
, "device context is initialized twice!");
121 mInitialized
= PR_TRUE
;
124 // register as a memory-pressure observer to free font resources
125 // in low-memory situations.
126 nsCOMPtr
<nsIObserverService
> obs(do_GetService("@mozilla.org/observer-service;1"));
128 obs
->AddObserver(this, "memory-pressure", PR_TRUE
);
131 NS_IMETHODIMP
DeviceContextImpl::CreateRenderingContext(nsIView
*aView
, nsIRenderingContext
*&aContext
)
136 nsCOMPtr
<nsIRenderingContext
> pContext
;
137 rv
= CreateRenderingContextInstance(*getter_AddRefs(pContext
));
138 if (NS_SUCCEEDED(rv
)) {
139 rv
= InitRenderingContext(pContext
, aView
->GetWidget());
140 if (NS_SUCCEEDED(rv
)) {
149 NS_IMETHODIMP
DeviceContextImpl::CreateRenderingContext(nsIWidget
*aWidget
, nsIRenderingContext
*&aContext
)
154 nsCOMPtr
<nsIRenderingContext
> pContext
;
155 rv
= CreateRenderingContextInstance(*getter_AddRefs(pContext
));
156 if (NS_SUCCEEDED(rv
)) {
157 rv
= InitRenderingContext(pContext
, aWidget
);
158 if (NS_SUCCEEDED(rv
)) {
167 NS_IMETHODIMP
DeviceContextImpl::CreateRenderingContextInstance(nsIRenderingContext
*&aContext
)
169 static NS_DEFINE_CID(kRenderingContextCID
, NS_RENDERING_CONTEXT_CID
);
172 nsCOMPtr
<nsIRenderingContext
> pContext
= do_CreateInstance(kRenderingContextCID
, &rv
);
173 if (NS_SUCCEEDED(rv
)) {
180 nsresult
DeviceContextImpl::InitRenderingContext(nsIRenderingContext
*aContext
, nsIWidget
*aWin
)
182 return aContext
->Init(this, aWin
);
185 NS_IMETHODIMP
DeviceContextImpl::CreateFontCache()
187 mFontCache
= new nsFontCache();
189 return NS_ERROR_OUT_OF_MEMORY
;
191 return mFontCache
->Init(this);
194 NS_IMETHODIMP
DeviceContextImpl::FontMetricsDeleted(const nsIFontMetrics
* aFontMetrics
)
197 mFontCache
->FontMetricsDeleted(aFontMetrics
);
203 DeviceContextImpl::GetLocaleLangGroup(void)
205 if (!mLocaleLangGroup
) {
206 nsCOMPtr
<nsILanguageAtomService
> langService
;
207 langService
= do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID
);
209 mLocaleLangGroup
= langService
->GetLocaleLanguageGroup();
211 if (!mLocaleLangGroup
) {
212 mLocaleLangGroup
= do_GetAtom("x-western");
217 NS_IMETHODIMP
DeviceContextImpl::GetMetricsFor(const nsFont
& aFont
,
218 nsIAtom
* aLangGroup
, gfxUserFontSet
*aUserFontSet
, nsIFontMetrics
*& aMetrics
)
220 if (nsnull
== mFontCache
) {
221 nsresult rv
= CreateFontCache();
226 // XXX temporary fix for performance problem -- erik
227 GetLocaleLangGroup();
230 // XXX figure out why aLangGroup is NULL sometimes
232 aLangGroup
= mLocaleLangGroup
;
235 return mFontCache
->GetMetricsFor(aFont
, aLangGroup
, aUserFontSet
, aMetrics
);
238 NS_IMETHODIMP
DeviceContextImpl::GetMetricsFor(const nsFont
& aFont
,
239 gfxUserFontSet
*aUserFontSet
,
240 nsIFontMetrics
*& aMetrics
)
242 if (nsnull
== mFontCache
) {
243 nsresult rv
= CreateFontCache();
248 // XXX temporary fix for performance problem -- erik
249 GetLocaleLangGroup();
251 return mFontCache
->GetMetricsFor(aFont
, mLocaleLangGroup
, aUserFontSet
,
255 NS_IMETHODIMP
DeviceContextImpl::GetDepth(PRUint32
& aDepth
)
261 NS_IMETHODIMP
DeviceContextImpl::GetPaletteInfo(nsPaletteInfo
& aPaletteInfo
)
263 aPaletteInfo
.isPaletteDevice
= PR_FALSE
;
264 aPaletteInfo
.sizePalette
= 0;
265 aPaletteInfo
.numReserved
= 0;
266 aPaletteInfo
.palette
= nsnull
;
270 struct FontEnumData
{
271 FontEnumData(nsIDeviceContext
* aDC
, nsString
& aFaceName
)
272 : mDC(aDC
), mFaceName(aFaceName
)
274 nsIDeviceContext
* mDC
;
278 static PRBool
FontEnumCallback(const nsString
& aFamily
, PRBool aGeneric
, void *aData
)
280 FontEnumData
* data
= (FontEnumData
*)aData
;
281 // XXX for now, all generic fonts are presumed to exist
282 // we may want to actually check if there's an installed conversion
284 data
->mFaceName
= aFamily
;
285 return PR_FALSE
; // found one, stop.
290 data
->mDC
->GetLocalFontName(aFamily
, local
, aliased
);
291 if (aliased
|| (NS_SUCCEEDED(data
->mDC
->CheckFontExistence(local
)))) {
292 data
->mFaceName
= local
;
293 return PR_FALSE
; // found one, stop.
296 return PR_TRUE
; // didn't exist, continue looking
299 NS_IMETHODIMP
DeviceContextImpl::FirstExistingFont(const nsFont
& aFont
, nsString
& aFaceName
)
301 FontEnumData
data(this, aFaceName
);
302 if (aFont
.EnumerateFamilies(FontEnumCallback
, &data
)) {
303 return NS_ERROR_FAILURE
; // ran out
308 class FontAliasKey
: public nsHashKey
311 FontAliasKey(const nsString
& aString
)
312 {mString
.Assign(aString
);}
314 virtual PRUint32
HashCode(void) const;
315 virtual PRBool
Equals(const nsHashKey
*aKey
) const;
316 virtual nsHashKey
*Clone(void) const;
321 PRUint32
FontAliasKey::HashCode(void) const
324 const PRUnichar
* string
= mString
.get();
326 while ((ch
= *string
++) != 0) {
327 // FYI: hash = hash*37 + ch
328 ch
= ToUpperCase(ch
);
329 hash
= ((hash
<< 5) + (hash
<< 2) + hash
) + ch
;
334 PRBool
FontAliasKey::Equals(const nsHashKey
*aKey
) const
336 return mString
.Equals(((FontAliasKey
*)aKey
)->mString
, nsCaseInsensitiveStringComparator());
339 nsHashKey
* FontAliasKey::Clone(void) const
341 return new FontAliasKey(mString
);
343 nsresult
DeviceContextImpl::CreateFontAliasTable()
345 nsresult result
= NS_OK
;
347 if (nsnull
== mFontAliasTable
) {
348 mFontAliasTable
= new nsHashtable();
349 if (nsnull
!= mFontAliasTable
) {
351 nsAutoString times
; times
.AssignLiteral("Times");
352 nsAutoString timesNewRoman
; timesNewRoman
.AssignLiteral("Times New Roman");
353 nsAutoString timesRoman
; timesRoman
.AssignLiteral("Times Roman");
354 nsAutoString arial
; arial
.AssignLiteral("Arial");
355 nsAutoString helvetica
; helvetica
.AssignLiteral("Helvetica");
356 nsAutoString courier
; courier
.AssignLiteral("Courier");
357 nsAutoString courierNew
; courierNew
.AssignLiteral("Courier New");
358 nsAutoString nullStr
;
360 AliasFont(times
, timesNewRoman
, timesRoman
, PR_FALSE
);
361 AliasFont(timesRoman
, timesNewRoman
, times
, PR_FALSE
);
362 AliasFont(timesNewRoman
, timesRoman
, times
, PR_FALSE
);
363 AliasFont(arial
, helvetica
, nullStr
, PR_FALSE
);
364 AliasFont(helvetica
, arial
, nullStr
, PR_FALSE
);
365 AliasFont(courier
, courierNew
, nullStr
, PR_TRUE
);
366 AliasFont(courierNew
, courier
, nullStr
, PR_FALSE
);
369 result
= NS_ERROR_OUT_OF_MEMORY
;
375 nsresult
DeviceContextImpl::AliasFont(const nsString
& aFont
,
376 const nsString
& aAlias
, const nsString
& aAltAlias
,
379 nsresult result
= NS_OK
;
381 if (nsnull
!= mFontAliasTable
) {
382 if (aForceAlias
|| NS_FAILED(CheckFontExistence(aFont
))) {
383 if (NS_SUCCEEDED(CheckFontExistence(aAlias
))) {
384 nsString
* entry
= new nsString(aAlias
);
385 if (nsnull
!= entry
) {
386 FontAliasKey
key(aFont
);
387 mFontAliasTable
->Put(&key
, entry
);
390 result
= NS_ERROR_OUT_OF_MEMORY
;
393 else if (!aAltAlias
.IsEmpty() && NS_SUCCEEDED(CheckFontExistence(aAltAlias
))) {
394 nsString
* entry
= new nsString(aAltAlias
);
395 if (nsnull
!= entry
) {
396 FontAliasKey
key(aFont
);
397 mFontAliasTable
->Put(&key
, entry
);
400 result
= NS_ERROR_OUT_OF_MEMORY
;
406 result
= NS_ERROR_FAILURE
;
411 NS_IMETHODIMP
DeviceContextImpl::GetLocalFontName(const nsString
& aFaceName
, nsString
& aLocalName
,
414 nsresult result
= NS_OK
;
416 if (nsnull
== mFontAliasTable
) {
417 result
= CreateFontAliasTable();
420 if (nsnull
!= mFontAliasTable
) {
421 FontAliasKey
key(aFaceName
);
422 const nsString
* alias
= (const nsString
*)mFontAliasTable
->Get(&key
);
423 if (nsnull
!= alias
) {
428 aLocalName
= aFaceName
;
435 NS_IMETHODIMP
DeviceContextImpl::FlushFontCache(void)
437 if (nsnull
!= mFontCache
)
443 /////////////////////////////////////////////////////////////
445 nsFontCache::nsFontCache()
447 MOZ_COUNT_CTOR(nsFontCache
);
451 nsFontCache::~nsFontCache()
453 MOZ_COUNT_DTOR(nsFontCache
);
458 nsFontCache::Init(nsIDeviceContext
* aContext
)
460 NS_PRECONDITION(nsnull
!= aContext
, "null ptr");
461 // Note: we don't hold a reference to the device context, because it
462 // holds a reference to us and we don't want circular references
468 nsFontCache::GetDeviceContext(nsIDeviceContext
*&aContext
) const
471 NS_IF_ADDREF(aContext
);
476 nsFontCache::GetMetricsFor(const nsFont
& aFont
, nsIAtom
* aLangGroup
,
477 gfxUserFontSet
*aUserFontSet
, nsIFontMetrics
*&aMetrics
)
479 // First check our cache
480 // start from the end, which is where we put the most-recent-used element
483 PRInt32 n
= mFontMetrics
.Count() - 1;
484 for (PRInt32 i
= n
; i
>= 0; --i
) {
485 fm
= static_cast<nsIFontMetrics
*>(mFontMetrics
[i
]);
486 nsIThebesFontMetrics
* tfm
= static_cast<nsIThebesFontMetrics
*>(fm
);
487 if (fm
->Font().Equals(aFont
) && tfm
->GetUserFontSet() == aUserFontSet
) {
488 nsCOMPtr
<nsIAtom
> langGroup
;
489 fm
->GetLangGroup(getter_AddRefs(langGroup
));
490 if (aLangGroup
== langGroup
.get()) {
492 // promote it to the end of the cache
493 mFontMetrics
.MoveElement(i
, n
);
495 tfm
->GetThebesFontGroup()->UpdateFontList();
496 NS_ADDREF(aMetrics
= fm
);
502 // It's not in the cache. Get font metrics and then cache them.
505 nsresult rv
= CreateFontMetricsInstance(&fm
);
506 if (NS_FAILED(rv
)) return rv
;
507 rv
= fm
->Init(aFont
, aLangGroup
, mContext
, aUserFontSet
);
508 if (NS_SUCCEEDED(rv
)) {
509 // the mFontMetrics list has the "head" at the end, because append is
510 // cheaper than insert
511 mFontMetrics
.AppendElement(fm
);
519 // One reason why Init() fails is because the system is running out of resources.
520 // e.g., on Win95/98 only a very limited number of GDI objects are available.
521 // Compact the cache and try again.
524 rv
= CreateFontMetricsInstance(&fm
);
525 if (NS_FAILED(rv
)) return rv
;
526 rv
= fm
->Init(aFont
, aLangGroup
, mContext
, aUserFontSet
);
527 if (NS_SUCCEEDED(rv
)) {
528 mFontMetrics
.AppendElement(fm
);
536 // could not setup a new one, send an old one (XXX search a "best match"?)
538 n
= mFontMetrics
.Count() - 1; // could have changed in Compact()
540 aMetrics
= static_cast<nsIFontMetrics
*>(mFontMetrics
[n
]);
545 NS_POSTCONDITION(NS_SUCCEEDED(rv
), "font metrics should not be null - bug 136248");
549 /* PostScript module may override this method to create
550 * nsIFontMetrics objects with their own classes
553 nsFontCache::CreateFontMetricsInstance(nsIFontMetrics
** fm
)
555 static NS_DEFINE_CID(kFontMetricsCID
, NS_FONT_METRICS_CID
);
556 return CallCreateInstance(kFontMetricsCID
, fm
);
559 nsresult
nsFontCache::FontMetricsDeleted(const nsIFontMetrics
* aFontMetrics
)
561 mFontMetrics
.RemoveElement((void*)aFontMetrics
);
565 nsresult
nsFontCache::Compact()
567 // Need to loop backward because the running element can be removed on the way
568 for (PRInt32 i
= mFontMetrics
.Count()-1; i
>= 0; --i
) {
569 nsIFontMetrics
* fm
= static_cast<nsIFontMetrics
*>(mFontMetrics
[i
]);
570 nsIFontMetrics
* oldfm
= fm
;
571 // Destroy() isn't here because we want our device context to be notified
572 NS_RELEASE(fm
); // this will reset fm to nsnull
573 // if the font is really gone, it would have called back in
574 // FontMetricsDeleted() and would have removed itself
575 if (mFontMetrics
.IndexOf(oldfm
) >= 0) {
576 // nope, the font is still there, so let's hold onto it too
583 nsresult
nsFontCache::Flush()
585 for (PRInt32 i
= mFontMetrics
.Count()-1; i
>= 0; --i
) {
586 nsIFontMetrics
* fm
= static_cast<nsIFontMetrics
*>(mFontMetrics
[i
]);
587 // Destroy() will unhook our device context from the fm so that we won't
588 // waste time in triggering the notification of FontMetricsDeleted()
589 // in the subsequent release
594 mFontMetrics
.Clear();
600 DeviceContextImpl::PrepareNativeWidget(nsIWidget
*aWidget
, void **aOut
)
602 return NS_ERROR_NOT_IMPLEMENTED
;
606 DeviceContextImpl::ClearCachedSystemFonts()