/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set cindent ts=2 sts=2 sw=2 et: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Netscape Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Yannick Koehler * Christian M Hoffman * Makoto hamanaka * Paul Ashford * * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the NPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsQuickSort.h" #include "nsFontMetricsBeOS.h" #include "nsIServiceManager.h" #include "nsICharsetConverterManager.h" #include "nsISaveAsCharset.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsCOMPtr.h" #include "nspr.h" #include "nsHashtable.h" #include "nsReadableUtils.h" #include #undef USER_DEFINED #define USER_DEFINED "x-user-def" #undef NOISY_FONTS #undef REALLY_NOISY_FONTS nsFontMetricsBeOS::nsFontMetricsBeOS() :mEmulateBold(PR_FALSE) { } nsFontMetricsBeOS::~nsFontMetricsBeOS() { if (nsnull != mFont) { delete mFont; mFont = nsnull; } if (mDeviceContext) { // Notify our device context that owns us so that it can update its font cache mDeviceContext->FontMetricsDeleted(this); mDeviceContext = nsnull; } } NS_IMPL_ISUPPORTS1(nsFontMetricsBeOS, nsIFontMetrics) // a structure to hold a font family list typedef struct nsFontEnumParamsBeOS { nsFontMetricsBeOS *metrics; nsStringArray family; nsVoidArray isgeneric; } NS_FONT_ENUM_PARAMS_BEOS; // a callback func to get a font family list from a nsFont object static PRBool FontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData) { NS_FONT_ENUM_PARAMS_BEOS *param = (NS_FONT_ENUM_PARAMS_BEOS *) aData; param->family.AppendString(aFamily); param->isgeneric.AppendElement((void*) aGeneric); if (aGeneric) return PR_FALSE; return PR_TRUE; } NS_IMETHODIMP nsFontMetricsBeOS::Init(const nsFont& aFont, nsIAtom* aLangGroup, nsIDeviceContext* aContext) { NS_ASSERTION(!(nsnull == aContext), "attempt to init fontmetrics with null device context"); mLangGroup = aLangGroup; mDeviceContext = aContext; // get font family list NS_FONT_ENUM_PARAMS_BEOS param; param.metrics = this; aFont.EnumerateFamilies(FontEnumCallback, ¶m); PRInt16 face = 0; mFont = new nsFont(aFont); if (!mFont) return NS_ERROR_OUT_OF_MEMORY; float app2dev, app2twip; app2dev = aContext->AppUnitsToDevUnits(); app2twip = aContext->DevUnitsToTwips(); app2twip *= app2dev; float rounded = ((float)NSIntPointsToTwips(NSTwipsToFloorIntPoints(nscoord(mFont->size * app2twip)))) / app2twip; // process specified fonts from first item of the array. // stop processing next when a real font found; PRBool fontfound = PR_FALSE; PRBool isfixed = PR_FALSE; for (int i=0 ; iGetUTF8String( &lang ); char prop[256]; snprintf( prop, sizeof(prop), "%s.%s", family.get(), lang ); // look up prefs nsXPIDLCString real_family; nsresult res; //NS_WITH_SERVICE( nsIPref, prefs, kPrefCID, &res ); nsCOMPtr prefs = do_GetService( NS_PREFSERVICE_CONTRACTID, &res ); if (NS_SUCCEEDED(res)) { nsCOMPtr branch; prefs->GetBranch("font.name.", getter_AddRefs(branch)); branch->GetCharPref(prop, getter_Copies(real_family)); if (!real_family.IsEmpty() && real_family.Length() <= B_FONT_FAMILY_LENGTH && count_font_styles((font_family)real_family.get()) > 0) { mFontHandle.SetFamilyAndStyle( (font_family)real_family.get(), NULL ); fontfound = PR_TRUE; break; } } // not successful. use system font. if (isfixed) mFontHandle = be_fixed_font; else mFontHandle = be_plain_font; fontfound = PR_TRUE; break; } } // if got no font, then use system font. if (!fontfound) { if (isfixed) mFontHandle = be_fixed_font; else mFontHandle = be_plain_font; } if (aFont.style == NS_FONT_STYLE_ITALIC) face |= B_ITALIC_FACE; if ( aFont.weight > NS_FONT_WEIGHT_NORMAL ) face |= B_BOLD_FACE; // I don't think B_UNDERSCORE_FACE and B_STRIKEOUT_FACE really works... // instead, nsTextFrame do them for us. ( my guess... Makoto Hamanaka ) if ( aFont.decorations & NS_FONT_DECORATION_UNDERLINE ) face |= B_UNDERSCORE_FACE; if ( aFont.decorations & NS_FONT_DECORATION_LINE_THROUGH ) face |= B_STRIKEOUT_FACE; mFontHandle.SetFace( face ); // emulate italic and bold if the selected family has no such style if (aFont.style == NS_FONT_STYLE_ITALIC && !(mFontHandle.Face() & B_ITALIC_FACE)) mFontHandle.SetShear(105.0); if ( aFont.weight > NS_FONT_WEIGHT_NORMAL && !(mFontHandle.Face() & B_BOLD_FACE)) mEmulateBold = PR_TRUE; mFontHandle.SetSize( rounded * app2dev ); fflush(stdout); #ifdef NOISY_FONTS #ifdef DEBUG fprintf(stderr, "looking for font %s (%d)", wildstring, aFont.size / app2twip); #endif #endif RealizeFont(aContext); return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::Destroy() { mDeviceContext = nsnull; return NS_OK; } void nsFontMetricsBeOS::RealizeFont(nsIDeviceContext* aContext) { float f; f = aContext->DevUnitsToAppUnits(); struct font_height height; mFontHandle.GetHeight( &height ); struct font_height emHeight; mFontHandle.GetHeight(&emHeight); //be_plain_font->GetHeight(&emHeight); int lineSpacing = nscoord(height.ascent + height.descent); if (lineSpacing > (emHeight.ascent + emHeight.descent)) mLeading = nscoord((lineSpacing - (emHeight.ascent + emHeight.descent)) * f); else mLeading = 0; mEmHeight = PR_MAX(1, nscoord((emHeight.ascent + emHeight.descent) * f)); mEmAscent = nscoord(height.ascent * (emHeight.ascent + emHeight.descent) * f / lineSpacing); mEmDescent = mEmHeight - mEmAscent; mMaxHeight = nscoord((height.ascent + height.descent) * f) ; mMaxAscent = nscoord(height.ascent * f) ; mMaxDescent = nscoord(height.descent * f); mMaxAdvance = nscoord((mFontHandle.BoundingBox().Width()+1) * f); //fyy +1 float rawWidth = mFontHandle.StringWidth("x"); mAveCharWidth = NSToCoordRound(rawWidth * f); // 56% of ascent, best guess for non-true type mXHeight = NSToCoordRound((float) height.ascent* f * 0.56f); rawWidth = mFontHandle.StringWidth(" "); mSpaceWidth = NSToCoordRound(rawWidth * f); /* Temp */ mUnderlineOffset = -NSToIntRound(MAX (1, floor (0.1 * (height.ascent + height.descent + height.leading) + 0.5)) * f); mUnderlineSize = NSToIntRound(MAX(1, floor (0.05 * (height.ascent + height.descent + height.leading) + 0.5)) * f); mSuperscriptOffset = mXHeight; mSubscriptOffset = mXHeight; /* need better way to calculate this */ mStrikeoutOffset = NSToCoordRound(mXHeight / 2.0); mStrikeoutSize = mUnderlineSize; } NS_IMETHODIMP nsFontMetricsBeOS::GetXHeight(nscoord& aResult) { aResult = mXHeight; return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetSuperscriptOffset(nscoord& aResult) { aResult = mSuperscriptOffset; return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetSubscriptOffset(nscoord& aResult) { aResult = mSubscriptOffset; return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetStrikeout(nscoord& aOffset, nscoord& aSize) { aOffset = mStrikeoutOffset; aSize = mStrikeoutSize; // aOffset = nscoord( ( mAscent / 2 ) - mDescent ); // aSize = nscoord( 20 ); // FIXME Put 1 pixel which equal 20 twips.. return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetUnderline(nscoord& aOffset, nscoord& aSize) { aOffset = mUnderlineOffset; aSize = mUnderlineSize; //aOffset = nscoord( 0 ); // FIXME //aSize = nscoord( 20 ); // FIXME return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetHeight(nscoord &aHeight) { aHeight = mMaxHeight; return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetNormalLineHeight(nscoord &aHeight) { aHeight = mEmHeight + mLeading; return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetLeading(nscoord &aLeading) { aLeading = mLeading; return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetEmHeight(nscoord &aHeight) { aHeight = mEmHeight; return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetEmAscent(nscoord &aAscent) { aAscent = mEmAscent; return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetEmDescent(nscoord &aDescent) { aDescent = mEmDescent; return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetMaxHeight(nscoord &aHeight) { aHeight = mMaxHeight; return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetMaxAscent(nscoord &aAscent) { aAscent = mMaxAscent; return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetMaxDescent(nscoord &aDescent) { aDescent = mMaxDescent; return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetMaxAdvance(nscoord &aAdvance) { aAdvance = mMaxAdvance; return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetAveCharWidth(nscoord &aAveCharWidth) { aAveCharWidth = mAveCharWidth; return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetSpaceWidth(nscoord &aSpaceWidth) { aSpaceWidth = mSpaceWidth; return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetFont(const nsFont*& aFont) { aFont = mFont; return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetLangGroup(nsIAtom** aLangGroup) { if (!aLangGroup) return NS_ERROR_NULL_POINTER; *aLangGroup = mLangGroup; NS_IF_ADDREF(*aLangGroup); return NS_OK; } NS_IMETHODIMP nsFontMetricsBeOS::GetFontHandle(nsFontHandle &aHandle) { aHandle = (nsFontHandle)&mFontHandle; return NS_OK; } nsresult nsFontMetricsBeOS::FamilyExists(const nsString& aName) { //Do we really need it here? BeOS supports UTF-8 overall natively, //including UTF-8 fonts names with non-ascii chars inside if (!IsASCII(aName)) return NS_ERROR_FAILURE; nsCAutoString name; name.AssignWithConversion(aName.get()); ToLowerCase(name); PRBool isthere = PR_FALSE; int32 numFamilies = count_font_families(); for (int32 i = 0; i < numFamilies; i++) { font_family family; uint32 flags; if (get_font_family(i, &family, &flags) == B_OK) { if (name.Equals(family) == 0) { isthere = PR_TRUE; break; } } } //printf("%s there? %s\n", name.get(), isthere?"Yes":"No" ); if (PR_TRUE == isthere) return NS_OK; else return NS_ERROR_FAILURE; } // The Font Enumerator nsFontEnumeratorBeOS::nsFontEnumeratorBeOS() { } NS_IMPL_ISUPPORTS1(nsFontEnumeratorBeOS, nsIFontEnumerator) static int CompareFontNames(const void* aArg1, const void* aArg2, void* aClosure) { const PRUnichar* str1 = *((const PRUnichar**) aArg1); const PRUnichar* str2 = *((const PRUnichar**) aArg2); // XXX add nsICollation stuff return nsCRT::strcmp(str1, str2); } static int FontMatchesGenericType(font_family family, uint32 flags, const char* aGeneric, const char* aLangGroup) { //Call from EnumerateAllFonts. Matches all. //Return 1 immediately, because nsnull as argument of strstr causes crashes if(aGeneric == nsnull || aLangGroup == nsnull) return 1; if (!strcmp(aLangGroup, "ja")) return 1; if (strstr(aLangGroup, "zh")) return 1; if (!strcmp(aLangGroup, "ko")) return 1; if (!strcmp(aLangGroup, "th")) return 1; if (!strcmp(aLangGroup, "he")) return 1; if (!strcmp(aLangGroup, "ar")) return 1; if (strstr(aLangGroup, "user-def")) return 1; if (!strcmp(aLangGroup, "unicode")) return 1; if (strstr(aGeneric, "fantasy") // Let's use all possible fonts as decorative #if 0 && (strstr(family, "Baskerville") || strstr(family, "Chicago") || strstr(family, "Copprpl") || strstr(family, "Embassy") || strstr(family, "Europe") || strstr(family, "Garmnd") || strstr(family, "Impact") || strstr(family, "ProFont") || strstr(family, "VAGRounded")) #endif ) return 1; // Hack. Sniffing is based on wide-spread names for serif and sansserif. No function in BeOS to get full font-info. // NB! "Haru Tohaba" and "Haru" need EXACT match - !strcmp seems suspicious in that case, timeless !!! if (!strcmp(aGeneric, "serif") && (strstr(family, "Dutch") || strstr(family, "Times") || strstr(family, "Roman") || strstr(family, "CentSchbook") || strstr(family, "Georgia") || strstr(family, "Baskerville") || strstr(family, "Garmnd") || strstr(family, "Cyberbit") || strcmp(family, "Haru Tohaba") == 0)) return 1; if (!strcmp(aGeneric, "sans-serif") && (strstr(family, "Arial") || strstr(family, "Chianti") || strstr(family, "Helv") || strstr(family, "Humnst") || strstr(family, "Swiss") || strstr(family, "Tahoma") || strstr(family, "Sans") || strstr(family, "sans") || strstr(family, "Verdana") || strstr(family, "Zurich") || strcmp(family, "Haru") == 0)) return 1; if ((strstr(aGeneric, "monospace") || strstr(aGeneric, "-moz-fixed")) && (flags & B_IS_FIXED || strstr(family, "Cour") || strstr(family, "Consol") || strstr(family, "Fixed") || strstr(family, "Kurier") || strstr(family, "Lucida") || strstr(family, "Mono") || strstr(family, "console") || strstr(family, "mono") || strstr(family, "fixed"))) return 1; if (strstr(aGeneric, "cursive") && (strstr(family, "Cursiv") || strstr(family, "Kursiv") || strstr(family, "Script") || strstr(family, "kursiv") || strstr(family, "Embassy") || strstr(family, "script") || strstr(family, "Brush"))) return 1; return 0; } static int MatchesLangGroup(font_family family, const char* aLangGroup) { BFont font; font.SetFamilyAndStyle(family, NULL); unicode_block lang = font.Blocks(); int match = 0; //No restrictions if ((strstr(aLangGroup, "user-def") || strstr(aLangGroup, "unicode"))) return 1; // "tr" and "central-euro" need more testing, but seems OK if ((strstr(aLangGroup, "baltic") || strstr(aLangGroup, "central-euro") || strstr(aLangGroup, "western")) && lang.Includes(B_LATIN1_SUPPLEMENT_BLOCK)) return 1; if (strstr(aLangGroup, "tr") && lang.Includes(B_LATIN_EXTENDED_A_BLOCK)) return 1; if (strstr(aLangGroup, "el") && lang.Includes(B_BASIC_GREEK_BLOCK)) return 1; if (strstr(aLangGroup, "cyrillic") && lang.Includes(B_CYRILLIC_BLOCK)) return 1; if (strstr(aLangGroup, "he") && lang.Includes(B_BASIC_HEBREW_BLOCK)) return 1; if (strstr(aLangGroup, "ar") && lang.Includes(B_BASIC_ARABIC_BLOCK)) return 1; if (strstr(aLangGroup, "th") && lang.Includes(B_THAI_BLOCK)) return 1; // CKJ settings need more verification if ((strstr(aLangGroup, "ja") || strstr(aLangGroup, "ko") || strstr(aLangGroup, "zh") ) && (lang.Includes(B_CJK_UNIFIED_IDEOGRAPHS_BLOCK) || lang.Includes(B_CJK_MISCELLANEOUS_BLOCK) || lang.Includes(B_ENCLOSED_CJK_LETTERS_AND_MONTHS_BLOCK) || lang.Includes(B_CJK_COMPATIBILITY_BLOCK) || lang.Includes(B_CJK_COMPATIBILITY_IDEOGRAPHS_BLOCK) || lang.Includes(B_CJK_COMPATIBILITY_FORMS_BLOCK))) match = 1; // additional check for partial CKJ blocks if (strstr(aLangGroup, "ja") && (lang.Includes(B_HIRAGANA_BLOCK) || lang.Includes(B_KATAKANA_BLOCK) )) match = 1; if (strstr(aLangGroup, "ko") && (lang.Includes(B_HANGUL_BLOCK))) match = 1; if (strstr(aLangGroup, "zh") && (lang.Includes(B_HIGH_SURROGATES_BLOCK) || lang.Includes(B_LOW_SURROGATES_BLOCK) )) match = 1; return match; } static nsresult EnumFonts(const char * aLangGroup, const char* aGeneric, PRUint32* aCount, PRUnichar*** aResult) { nsString font_name; int32 numFamilies = count_font_families(); PRUnichar** array = (PRUnichar**) nsMemory::Alloc(numFamilies * sizeof(PRUnichar*)); if (!array) return NS_ERROR_OUT_OF_MEMORY; int j = 0; for(int32 i = 0; i < numFamilies; i++) { font_family family; uint32 flags; if (get_font_family(i, &family, &flags) == B_OK) { if (family && (!aLangGroup || MatchesLangGroup(family, aLangGroup))) { if(FontMatchesGenericType(family, flags, aGeneric, aLangGroup)) { font_name.AssignWithConversion(family); if (!(array[j] = ToNewUnicode(font_name))) break; ++j; } } } } NS_QuickSort(array, j, sizeof(PRUnichar*), CompareFontNames, nsnull); *aCount = j; if (*aCount) *aResult = array; else nsMemory::Free(array); return NS_OK; } NS_IMETHODIMP nsFontEnumeratorBeOS::EnumerateAllFonts(PRUint32* aCount, PRUnichar*** aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = nsnull; NS_ENSURE_ARG_POINTER(aCount); *aCount = 0; return EnumFonts(nsnull, nsnull, aCount, aResult); } NS_IMETHODIMP nsFontEnumeratorBeOS::EnumerateFonts(const char* aLangGroup, const char* aGeneric, PRUint32* aCount, PRUnichar*** aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = nsnull; NS_ENSURE_ARG_POINTER(aCount); *aCount = 0; // aLangGroup=null or "" means any (i.e., don't care) // aGeneric=null or "" means any (i.e, don't care) const char* langGroup = nsnull; if (aLangGroup && *aLangGroup) langGroup = aLangGroup; const char* generic = nsnull; if (aGeneric && *aGeneric) generic = aGeneric; return EnumFonts(langGroup, generic, aCount, aResult); } NS_IMETHODIMP nsFontEnumeratorBeOS::HaveFontFor(const char* aLangGroup, PRBool* aResult) { NS_ENSURE_ARG_POINTER(aLangGroup); NS_ENSURE_ARG_POINTER(aResult); *aResult = PR_TRUE; // XXX stub return NS_OK; } NS_IMETHODIMP nsFontEnumeratorBeOS::GetDefaultFont(const char *aLangGroup, const char *aGeneric, PRUnichar **aResult) { // aLangGroup=null or "" means any (i.e., don't care) // aGeneric=null or "" means any (i.e, don't care) NS_ENSURE_ARG_POINTER(aResult); *aResult = nsnull; return NS_OK; } NS_IMETHODIMP nsFontEnumeratorBeOS::UpdateFontList(PRBool *updateFontList) { *updateFontList = PR_FALSE; // always return false for now return NS_OK; }