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"
54 NS_IMPL_ISUPPORTS3(DeviceContextImpl
, nsIDeviceContext
, nsIObserver
, nsISupportsWeakReference
)
56 DeviceContextImpl::DeviceContextImpl()
58 mAppUnitsPerDevPixel
= -1;
59 mAppUnitsPerInch
= -1;
60 mAppUnitsPerDevNotScaledPixel
= -1;
65 mFontAliasTable
= nsnull
;
68 mInitialized
= PR_FALSE
;
72 static PRBool PR_CALLBACK
DeleteValue(nsHashKey
* aKey
, void* aValue
, void* closure
)
74 delete ((nsString
*)aValue
);
78 DeviceContextImpl::~DeviceContextImpl()
80 nsCOMPtr
<nsIObserverService
> obs(do_GetService("@mozilla.org/observer-service;1"));
82 obs
->RemoveObserver(this, "memory-pressure");
84 if (nsnull
!= mFontCache
)
90 if (nsnull
!= mFontAliasTable
) {
91 mFontAliasTable
->Enumerate(DeleteValue
);
92 delete mFontAliasTable
;
98 DeviceContextImpl::Observe(nsISupports
* aSubject
, const char* aTopic
, const PRUnichar
* aSomeData
)
100 if (mFontCache
&& !nsCRT::strcmp(aTopic
, "memory-pressure")) {
101 mFontCache
->Compact();
106 NS_IMETHODIMP
DeviceContextImpl::Init(nsNativeWidget aWidget
)
115 void DeviceContextImpl::CommonInit(void)
118 NS_ASSERTION(!mInitialized
, "device context is initialized twice!");
119 mInitialized
= PR_TRUE
;
122 // register as a memory-pressure observer to free font resources
123 // in low-memory situations.
124 nsCOMPtr
<nsIObserverService
> obs(do_GetService("@mozilla.org/observer-service;1"));
126 obs
->AddObserver(this, "memory-pressure", PR_TRUE
);
129 NS_IMETHODIMP
DeviceContextImpl::CreateRenderingContext(nsIView
*aView
, nsIRenderingContext
*&aContext
)
134 nsCOMPtr
<nsIRenderingContext
> pContext
;
135 rv
= CreateRenderingContextInstance(*getter_AddRefs(pContext
));
136 if (NS_SUCCEEDED(rv
)) {
137 rv
= InitRenderingContext(pContext
, aView
->GetWidget());
138 if (NS_SUCCEEDED(rv
)) {
147 NS_IMETHODIMP
DeviceContextImpl::CreateRenderingContext(nsIWidget
*aWidget
, nsIRenderingContext
*&aContext
)
152 nsCOMPtr
<nsIRenderingContext
> pContext
;
153 rv
= CreateRenderingContextInstance(*getter_AddRefs(pContext
));
154 if (NS_SUCCEEDED(rv
)) {
155 rv
= InitRenderingContext(pContext
, aWidget
);
156 if (NS_SUCCEEDED(rv
)) {
165 NS_IMETHODIMP
DeviceContextImpl::CreateRenderingContextInstance(nsIRenderingContext
*&aContext
)
167 static NS_DEFINE_CID(kRenderingContextCID
, NS_RENDERING_CONTEXT_CID
);
170 nsCOMPtr
<nsIRenderingContext
> pContext
= do_CreateInstance(kRenderingContextCID
, &rv
);
171 if (NS_SUCCEEDED(rv
)) {
178 nsresult
DeviceContextImpl::InitRenderingContext(nsIRenderingContext
*aContext
, nsIWidget
*aWin
)
180 return aContext
->Init(this, aWin
);
183 NS_IMETHODIMP
DeviceContextImpl::CreateFontCache()
185 mFontCache
= new nsFontCache();
187 return NS_ERROR_OUT_OF_MEMORY
;
189 return mFontCache
->Init(this);
192 NS_IMETHODIMP
DeviceContextImpl::FontMetricsDeleted(const nsIFontMetrics
* aFontMetrics
)
195 mFontCache
->FontMetricsDeleted(aFontMetrics
);
201 DeviceContextImpl::GetLocaleLangGroup(void)
203 if (!mLocaleLangGroup
) {
204 nsCOMPtr
<nsILanguageAtomService
> langService
;
205 langService
= do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID
);
207 mLocaleLangGroup
= langService
->GetLocaleLanguageGroup();
209 if (!mLocaleLangGroup
) {
210 mLocaleLangGroup
= do_GetAtom("x-western");
215 NS_IMETHODIMP
DeviceContextImpl::GetMetricsFor(const nsFont
& aFont
,
216 nsIAtom
* aLangGroup
, nsIFontMetrics
*& aMetrics
)
218 if (nsnull
== mFontCache
) {
219 nsresult rv
= CreateFontCache();
224 // XXX temporary fix for performance problem -- erik
225 GetLocaleLangGroup();
228 // XXX figure out why aLangGroup is NULL sometimes
230 aLangGroup
= mLocaleLangGroup
;
233 return mFontCache
->GetMetricsFor(aFont
, aLangGroup
, aMetrics
);
236 NS_IMETHODIMP
DeviceContextImpl::GetMetricsFor(const nsFont
& aFont
, nsIFontMetrics
*& aMetrics
)
238 if (nsnull
== mFontCache
) {
239 nsresult rv
= CreateFontCache();
244 // XXX temporary fix for performance problem -- erik
245 GetLocaleLangGroup();
247 return mFontCache
->GetMetricsFor(aFont
, mLocaleLangGroup
, aMetrics
);
250 NS_IMETHODIMP
DeviceContextImpl::GetDepth(PRUint32
& aDepth
)
256 NS_IMETHODIMP
DeviceContextImpl::GetPaletteInfo(nsPaletteInfo
& aPaletteInfo
)
258 aPaletteInfo
.isPaletteDevice
= PR_FALSE
;
259 aPaletteInfo
.sizePalette
= 0;
260 aPaletteInfo
.numReserved
= 0;
261 aPaletteInfo
.palette
= nsnull
;
265 struct FontEnumData
{
266 FontEnumData(nsIDeviceContext
* aDC
, nsString
& aFaceName
)
267 : mDC(aDC
), mFaceName(aFaceName
)
269 nsIDeviceContext
* mDC
;
273 static PRBool
FontEnumCallback(const nsString
& aFamily
, PRBool aGeneric
, void *aData
)
275 FontEnumData
* data
= (FontEnumData
*)aData
;
276 // XXX for now, all generic fonts are presumed to exist
277 // we may want to actually check if there's an installed conversion
279 data
->mFaceName
= aFamily
;
280 return PR_FALSE
; // found one, stop.
285 data
->mDC
->GetLocalFontName(aFamily
, local
, aliased
);
286 if (aliased
|| (NS_SUCCEEDED(data
->mDC
->CheckFontExistence(local
)))) {
287 data
->mFaceName
= local
;
288 return PR_FALSE
; // found one, stop.
291 return PR_TRUE
; // didn't exist, continue looking
294 NS_IMETHODIMP
DeviceContextImpl::FirstExistingFont(const nsFont
& aFont
, nsString
& aFaceName
)
296 FontEnumData
data(this, aFaceName
);
297 if (aFont
.EnumerateFamilies(FontEnumCallback
, &data
)) {
298 return NS_ERROR_FAILURE
; // ran out
303 class FontAliasKey
: public nsHashKey
306 FontAliasKey(const nsString
& aString
)
307 {mString
.Assign(aString
);}
309 virtual PRUint32
HashCode(void) const;
310 virtual PRBool
Equals(const nsHashKey
*aKey
) const;
311 virtual nsHashKey
*Clone(void) const;
316 PRUint32
FontAliasKey::HashCode(void) const
319 const PRUnichar
* string
= mString
.get();
321 while ((ch
= *string
++) != 0) {
322 // FYI: hash = hash*37 + ch
323 ch
= ToUpperCase(ch
);
324 hash
= ((hash
<< 5) + (hash
<< 2) + hash
) + ch
;
329 PRBool
FontAliasKey::Equals(const nsHashKey
*aKey
) const
331 return mString
.Equals(((FontAliasKey
*)aKey
)->mString
, nsCaseInsensitiveStringComparator());
334 nsHashKey
* FontAliasKey::Clone(void) const
336 return new FontAliasKey(mString
);
338 nsresult
DeviceContextImpl::CreateFontAliasTable()
340 nsresult result
= NS_OK
;
342 if (nsnull
== mFontAliasTable
) {
343 mFontAliasTable
= new nsHashtable();
344 if (nsnull
!= mFontAliasTable
) {
346 nsAutoString times
; times
.AssignLiteral("Times");
347 nsAutoString timesNewRoman
; timesNewRoman
.AssignLiteral("Times New Roman");
348 nsAutoString timesRoman
; timesRoman
.AssignLiteral("Times Roman");
349 nsAutoString arial
; arial
.AssignLiteral("Arial");
350 nsAutoString helvetica
; helvetica
.AssignLiteral("Helvetica");
351 nsAutoString courier
; courier
.AssignLiteral("Courier");
352 nsAutoString courierNew
; courierNew
.AssignLiteral("Courier New");
353 nsAutoString nullStr
;
355 AliasFont(times
, timesNewRoman
, timesRoman
, PR_FALSE
);
356 AliasFont(timesRoman
, timesNewRoman
, times
, PR_FALSE
);
357 AliasFont(timesNewRoman
, timesRoman
, times
, PR_FALSE
);
358 AliasFont(arial
, helvetica
, nullStr
, PR_FALSE
);
359 AliasFont(helvetica
, arial
, nullStr
, PR_FALSE
);
360 AliasFont(courier
, courierNew
, nullStr
, PR_TRUE
);
361 AliasFont(courierNew
, courier
, nullStr
, PR_FALSE
);
364 result
= NS_ERROR_OUT_OF_MEMORY
;
370 nsresult
DeviceContextImpl::AliasFont(const nsString
& aFont
,
371 const nsString
& aAlias
, const nsString
& aAltAlias
,
374 nsresult result
= NS_OK
;
376 if (nsnull
!= mFontAliasTable
) {
377 if (aForceAlias
|| NS_FAILED(CheckFontExistence(aFont
))) {
378 if (NS_SUCCEEDED(CheckFontExistence(aAlias
))) {
379 nsString
* entry
= new nsString(aAlias
);
380 if (nsnull
!= entry
) {
381 FontAliasKey
key(aFont
);
382 mFontAliasTable
->Put(&key
, entry
);
385 result
= NS_ERROR_OUT_OF_MEMORY
;
388 else if (!aAltAlias
.IsEmpty() && NS_SUCCEEDED(CheckFontExistence(aAltAlias
))) {
389 nsString
* entry
= new nsString(aAltAlias
);
390 if (nsnull
!= entry
) {
391 FontAliasKey
key(aFont
);
392 mFontAliasTable
->Put(&key
, entry
);
395 result
= NS_ERROR_OUT_OF_MEMORY
;
401 result
= NS_ERROR_FAILURE
;
406 NS_IMETHODIMP
DeviceContextImpl::GetLocalFontName(const nsString
& aFaceName
, nsString
& aLocalName
,
409 nsresult result
= NS_OK
;
411 if (nsnull
== mFontAliasTable
) {
412 result
= CreateFontAliasTable();
415 if (nsnull
!= mFontAliasTable
) {
416 FontAliasKey
key(aFaceName
);
417 const nsString
* alias
= (const nsString
*)mFontAliasTable
->Get(&key
);
418 if (nsnull
!= alias
) {
423 aLocalName
= aFaceName
;
430 NS_IMETHODIMP
DeviceContextImpl::FlushFontCache(void)
432 if (nsnull
!= mFontCache
)
438 /////////////////////////////////////////////////////////////
440 nsFontCache::nsFontCache()
442 MOZ_COUNT_CTOR(nsFontCache
);
446 nsFontCache::~nsFontCache()
448 MOZ_COUNT_DTOR(nsFontCache
);
453 nsFontCache::Init(nsIDeviceContext
* aContext
)
455 NS_PRECONDITION(nsnull
!= aContext
, "null ptr");
456 // Note: we don't hold a reference to the device context, because it
457 // holds a reference to us and we don't want circular references
463 nsFontCache::GetDeviceContext(nsIDeviceContext
*&aContext
) const
466 NS_IF_ADDREF(aContext
);
471 nsFontCache::GetMetricsFor(const nsFont
& aFont
, nsIAtom
* aLangGroup
,
472 nsIFontMetrics
*&aMetrics
)
474 // First check our cache
475 // start from the end, which is where we put the most-recent-used element
478 PRInt32 n
= mFontMetrics
.Count() - 1;
479 for (PRInt32 i
= n
; i
>= 0; --i
) {
480 fm
= static_cast<nsIFontMetrics
*>(mFontMetrics
[i
]);
481 if (fm
->Font().Equals(aFont
)) {
482 nsCOMPtr
<nsIAtom
> langGroup
;
483 fm
->GetLangGroup(getter_AddRefs(langGroup
));
484 if (aLangGroup
== langGroup
.get()) {
486 // promote it to the end of the cache
487 mFontMetrics
.MoveElement(i
, n
);
489 NS_ADDREF(aMetrics
= fm
);
495 // It's not in the cache. Get font metrics and then cache them.
498 nsresult rv
= CreateFontMetricsInstance(&fm
);
499 if (NS_FAILED(rv
)) return rv
;
500 rv
= fm
->Init(aFont
, aLangGroup
, mContext
);
501 if (NS_SUCCEEDED(rv
)) {
502 // the mFontMetrics list has the "head" at the end, because append is
503 // cheaper than insert
504 mFontMetrics
.AppendElement(fm
);
512 // One reason why Init() fails is because the system is running out of resources.
513 // e.g., on Win95/98 only a very limited number of GDI objects are available.
514 // Compact the cache and try again.
517 rv
= CreateFontMetricsInstance(&fm
);
518 if (NS_FAILED(rv
)) return rv
;
519 rv
= fm
->Init(aFont
, aLangGroup
, mContext
);
520 if (NS_SUCCEEDED(rv
)) {
521 mFontMetrics
.AppendElement(fm
);
529 // could not setup a new one, send an old one (XXX search a "best match"?)
531 n
= mFontMetrics
.Count() - 1; // could have changed in Compact()
533 aMetrics
= static_cast<nsIFontMetrics
*>(mFontMetrics
[n
]);
538 NS_POSTCONDITION(NS_SUCCEEDED(rv
), "font metrics should not be null - bug 136248");
542 /* PostScript module may override this method to create
543 * nsIFontMetrics objects with their own classes
546 nsFontCache::CreateFontMetricsInstance(nsIFontMetrics
** fm
)
548 static NS_DEFINE_CID(kFontMetricsCID
, NS_FONT_METRICS_CID
);
549 return CallCreateInstance(kFontMetricsCID
, fm
);
552 nsresult
nsFontCache::FontMetricsDeleted(const nsIFontMetrics
* aFontMetrics
)
554 mFontMetrics
.RemoveElement((void*)aFontMetrics
);
558 nsresult
nsFontCache::Compact()
560 // Need to loop backward because the running element can be removed on the way
561 for (PRInt32 i
= mFontMetrics
.Count()-1; i
>= 0; --i
) {
562 nsIFontMetrics
* fm
= static_cast<nsIFontMetrics
*>(mFontMetrics
[i
]);
563 nsIFontMetrics
* oldfm
= fm
;
564 // Destroy() isn't here because we want our device context to be notified
565 NS_RELEASE(fm
); // this will reset fm to nsnull
566 // if the font is really gone, it would have called back in
567 // FontMetricsDeleted() and would have removed itself
568 if (mFontMetrics
.IndexOf(oldfm
) >= 0) {
569 // nope, the font is still there, so let's hold onto it too
576 nsresult
nsFontCache::Flush()
578 for (PRInt32 i
= mFontMetrics
.Count()-1; i
>= 0; --i
) {
579 nsIFontMetrics
* fm
= static_cast<nsIFontMetrics
*>(mFontMetrics
[i
]);
580 // Destroy() will unhook our device context from the fm so that we won't
581 // waste time in triggering the notification of FontMetricsDeleted()
582 // in the subsequent release
587 mFontMetrics
.Clear();
593 DeviceContextImpl::PrepareNativeWidget(nsIWidget
*aWidget
, void **aOut
)
595 return NS_ERROR_NOT_IMPLEMENTED
;
599 DeviceContextImpl::ClearCachedSystemFonts()