gecko-dev/cmd/macfe/gui/CFontReference.cp

792 lines
18 KiB
C++
Raw Blame History

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "CFontReference.h"
#include <Printing.h>
#include "uprefd.h"
#include "xp_hash.h"
#include "xp_mem.h"
#include "lo_ele.h"
#include "FontTypes.h"
#include "shist.h"
#include "resgui.h"
// Set this to 1 to force WebFont lookup instead of native font lookup
#define TEST_NATIVE_DISPLAYER 0
#if TEST_NATIVE_DISPLAYER
#include "mcfp.h"
#endif
#include "libi18n.h"
#include "UPropFontSwitcher.h"
#include "UFixedFontSwitcher.h"
#include "UUTF8TextHandler.h"
#define WEIGHT_BOLD 700
#define WEIGHT_NORMAL 400
class NameList
{
public:
NameList(char *list);
~NameList();
Boolean Empty();
void Next();
char *Current();
private:
char *list, *name;
int start, next;
};
NameList::NameList(char *list)
{
int length = strlen(list);
this->list = list;
start = next = 0;
if (length > 0)
{
name = new char[length + 1];
this->Next();
}
else
{
name = NULL;
}
}
NameList::~NameList()
{
if (name != NULL)
delete[] name;
}
char *NameList::Current()
{
if (Empty())
return NULL;
return name;
}
Boolean NameList::Empty()
{
return list == NULL || list[start] == '\0';
}
void NameList::Next()
{
start = next;
if (Empty())
return;
// skip over leading whitespace and commas to find the start of the name
while (list[start] != '\0' && (list[start] == ',' || isspace(list[start])))
{
start += 1;
}
// find the end of the name
next = start;
while(list[next] != '\0' && list[next] != ',')
{
if (list[next] == '"' || list[next] == '\'')
{
char final = list[next];
// find the matching quote
next += 1;
while (list[next] != '\0' && list[next] != final)
{
next += 1;
}
// if list[next] is null, there was no matching quote, so bail
if (list[next] == '\0')
{
break;
}
}
next += 1;
}
// strip off trailing whitespace
int end = next - 1;
while (end >= start && isspace(list[end]))
{
end -= 1;
}
// copy what's between start and end into name
int src = start;
int dst = 0;
// if it's quoted, strip off the quotes
if ((list[start] == '"' || list[start] == '\'') && list[end] == list[start])
{
src += 1;
end -= 1;
}
// copy the characters from src to end into the name
while (src <= end)
{
name[dst++] = list[src++];
}
name[dst] = '\0';
}
void FE_ReleaseTextAttrFeData(MWContext * /* context */, LO_TextAttr *attr)
{
CFontReference *fontReference = (CFontReference *) attr->FE_Data;
if (fontReference != NULL)
delete fontReference;
}
short CFontReference::GetScaledTextSize(short size, short attrSize)
{
short scaledSize;
if ( attrSize < 1 )
attrSize = 1;
else if ( attrSize > 7 )
attrSize = 7;
// <20> Houck's font table from WINFE
switch ( attrSize )
{
case 0: scaledSize = size / 2 ; break;
case 1: scaledSize = 7 * size / 10 ; break;
case 2: scaledSize = 85 * size / 100 ; break;
case 3: scaledSize = size ; break;
case 4: scaledSize = 12 * size / 10 ; break;
case 5: scaledSize = 3 * size / 2 ; break;
case 6: scaledSize = 2 * size ; break;
case 7: scaledSize = 3 * size ; break;
}
// should this test be at a higher level?
if ( scaledSize < 9 ) // looks bad at anything lower
scaledSize = 9;
return scaledSize;
}
CFontReference *CFontReference::GetFontReference(const CCharSet *charSet, LO_TextAttr* attr, MWContext *context, Boolean underlineLinks)
{
CFontReference *result = NULL;
if (attr->FE_Data != NULL)
{
result = (CFontReference *) attr->FE_Data;
result->SynchToPort(qd.thePort);
return result;
}
for (NameList list(attr->font_face); !list.Empty(); list.Next())
{
result = CWebFontReference::LookupWebFont(list.Current(), charSet, attr, context, underlineLinks);
if (result != NULL)
goto cache_result;
result = CNativeFontReference::LookupNativeFont(list.Current(), charSet, attr, underlineLinks);
if (result != NULL)
goto cache_result;
result = CNativeFontReference::LookupGenericFont(list.Current(), charSet, attr, underlineLinks);
if (result != NULL)
goto cache_result;
}
// if we get this far, we don't have any matching fonts
// just return the default font for the character set.
short fontID;
if ( attr->fontmask & LO_FONT_FIXED )
fontID = charSet->fFixedFontNum;
else
fontID = charSet->fPropFontNum;
result = new CNativeFontReference(fontID, charSet, attr, underlineLinks);
cache_result:
attr->FE_Data = (void *) result;
return result;
}
CFontReference::CFontReference(const CCharSet *charSet, const LO_TextAttr *attr)
{
fIsGetFontInfoDirty = true;
fMode = srcOr;
if (attr->point_size == 0)
{
short textSize;
if (attr->fontmask & LO_FONT_FIXED)
textSize = charSet->fFixedFontSize;
else
textSize = charSet->fPropFontSize;
fSize = GetScaledTextSize(textSize, attr->size);
}
else
{
fSize = attr->point_size;
}
}
CFontReference::~CFontReference()
{
// **** is there anything to do here?
}
void CNativeFontReference::Apply()
{
if ( qd.thePort->txFont != fFont )
::TextFont( fFont );
if ( qd.thePort->txSize != fSize )
::TextSize( fSize );
if ( qd.thePort->txFace != fStyle )
::TextFace( fStyle );
if ( qd.thePort->txMode != fMode )
::TextMode( fMode );
}
CFontReference *CNativeFontReference::LookupNativeFont(char *fontName, const CCharSet *charSet, const LO_TextAttr *attr,
Boolean underlineLinks)
{
short fontID;
CStr255 pName(fontName);
::GetFNum(pName, &fontID);
if (fontID == 0)
{
/*
Font ID 0 is the system font. Did we get this
ID because the named font doesn't exist, or
because we asked for the system font by name?
*/
static CStr255 systemFontName;
if (systemFontName[0] == 0)
::GetFontName(0, systemFontName);
if (pName != systemFontName)
return NULL;
}
return new CNativeFontReference(fontID, charSet, attr, underlineLinks);
}
CFontReference *CNativeFontReference::LookupGenericFont(char *fontName, const CCharSet *charSet, const LO_TextAttr *attr,
Boolean underlineLinks)
{
struct GenericFontFamily
{
char *genericName;
char *nativeName;
};
// NOTE: These need to be in the same order as they are
// in the resource.
static GenericFontFamily genericNames[] =
{
{"serif", NULL},
{"sans-serif", NULL},
{"cursive", NULL},
{"fantasy", NULL},
{"monospace", NULL}
};
static const int genericNameCount = sizeof(genericNames) / sizeof(GenericFontFamily);
for (int nameIndex = 0; nameIndex < genericNameCount; nameIndex += 1)
{
if (!XP_STRCASECMP( fontName, genericNames[nameIndex].genericName))
{
if (genericNames[nameIndex].nativeName == NULL)
{
Str255 nativeName;
::GetIndString(nativeName, GENERIC_FONT_NAMES_RESID, nameIndex + 1);
XP_ASSERT(nativeName[0] != 0);
// allocate memory for a copy of the name
genericNames[nameIndex].nativeName = (char *) XP_ALLOC(nativeName[0] + 1);
// bail if no memory for the name
if (genericNames[nameIndex].nativeName == NULL)
return NULL;
// copy it as a C string
strncpy(genericNames[nameIndex].nativeName, (char *) &nativeName[1], nativeName[0]);
genericNames[nameIndex].nativeName[nativeName[0]] = '\0';
}
return CNativeFontReference::LookupNativeFont(genericNames[nameIndex].nativeName, charSet, attr, underlineLinks);
}
}
return NULL;
}
CNativeFontReference::CNativeFontReference(short font, const CCharSet *charSet, const LO_TextAttr *attr,
Boolean /* underlineLinks */) :
CFontReference(charSet, attr)
{
fFont = font;
fStyle = 0;
if ((charSet->fCSID & MULTIBYTE) != SINGLEBYTE)
{
switch(charSet->fCSID)
{
case CS_UTF8:
fTextHandler = UUTF8TextHandler::Instance();
break;
/*
// **** really want handlers for all multi-byte text
// use system for now...
case CS_SJIS:
fTextHandler = SJISTextHandler::Instance();
break;
default:
fTextHandler = MBTextHandler::Instance();
*/
default:
fTextHandler = NULL;
}
if (attr->fontmask & LO_FONT_FIXED)
fFontSwitcher = UFixedFontSwitcher::Instance();
else
fFontSwitcher = UPropFontSwitcher::Instance();
}
else
{
fTextHandler = NULL;
fFontSwitcher = NULL;
}
if (attr->font_weight >= WEIGHT_BOLD || (attr->fontmask & LO_FONT_BOLD) != 0)
fStyle |= bold;
if (attr->fontmask & LO_FONT_ITALIC)
fStyle |= italic;
if (attr->attrmask & LO_ATTR_UNDERLINE)
fStyle |= underline;
}
CNativeFontReference::~CNativeFontReference()
{
// **** Anything interesting to do here?
}
void CNativeFontReference::DrawText(int x, int y, char *text, int start, int end)
{
::MoveTo(x, y);
if (fTextHandler != NULL)
fTextHandler->DrawText(fFontSwitcher, &text[start], end - start + 1);
else
::DrawText(text, start, end - start + 1);
}
short CNativeFontReference::TextWidth(char *text, int firstByte, int byteCount)
{
if (fTextHandler != NULL)
return fTextHandler->TextWidth(fFontSwitcher, &text[firstByte], byteCount);
else
return ::TextWidth(text, firstByte, byteCount);
}
short CNativeFontReference::MeasureText(char *text, int firstByte, int byteCount, short* charLocs)
{
if (fTextHandler != NULL)
return 0;
else
{
text = &text[firstByte];
::MeasureText(byteCount, text, charLocs);
// remove null chars widths
short widthOffset = 0;
short nullCharWidth = 0;
for (int i = 1; i <= byteCount; i ++)
{
if (text[i-1] == '\0')
{
if (nullCharWidth == 0)
{
nullCharWidth = charLocs[i] - charLocs[i-1];
if (nullCharWidth == 0)
break;
}
widthOffset += nullCharWidth;
}
charLocs[i] -= widthOffset;
}
return byteCount;
}
}
void CNativeFontReference::GetFontInfo(FontInfo *fontInfo)
{
if (fIsGetFontInfoDirty)
{
if (fTextHandler != NULL)
fTextHandler->GetFontInfo(fFontSwitcher, &fCachedFontInfoValues);
else
::GetFontInfo(&fCachedFontInfoValues);
fIsGetFontInfoDirty = false;
}
*fontInfo = fCachedFontInfoValues;
}
FontBrokerHandle CWebFontReference::sBroker = NULL;
FontBrokerUtilityHandle CWebFontReference::sUtility = NULL;
XP_HashTable CWebFontReference::sPortHash = NULL;
char *CWebFontReference::sCatalogPath = NULL;
#define REQUIRED_GUTS_PATH "/usr/local/netscape/RequiredGuts/"
void CWebFontReference::Init()
{
jint jresult;
char *displayerPath;
Str31 essentialFiles, dynamicFonts, dynamicFontCatalog;
FontBrokerDisplayerHandle brokerDisplayer;
#if TEST_NATIVE_DISPLAYER
FontDisplayerHandle nativeDisplayer;
#endif
sPortHash = XP_HashTableNew(10, PortHash, PortCompFunction);
sBroker = NF_FontBrokerInitialize();
if (sBroker == NULL)
{
// error?
return;
}
brokerDisplayer = (FontBrokerDisplayerHandle) nffbc_getInterface(sBroker, &nffbp_ID, NULL);
if(brokerDisplayer == NULL)
{
// error?
return;
}
#if TEST_NATIVE_DISPLAYER
nativeDisplayer = (FontDisplayerHandle) cfpFactory_Create(NULL, brokerDisplayer);
if (nativeDisplayer != NULL)
{
nffbp_RegisterFontDisplayer(brokerDisplayer, nativeDisplayer, NULL);
}
#endif
sUtility = (FontBrokerUtilityHandle) nffbc_getInterface(sBroker, &nffbu_ID, NULL);
if (sUtility == NULL)
{
// error?
return;
}
::GetIndString(essentialFiles, 14000, 1);
::GetIndString(dynamicFonts, 14000, 3);
::GetIndString(dynamicFontCatalog, 14000, 4);
XP_ASSERT(essentialFiles[0] != 0 && dynamicFonts[0] != 0 && dynamicFontCatalog[0] != 0);
// "+ 4" is for three colons plus the null
sCatalogPath = (char *) XP_ALLOC(essentialFiles[0] + dynamicFonts[0] + dynamicFontCatalog[0] + 4);
if (sCatalogPath != NULL)
{
// Build ":Essential Files:DynamicFonts:Dynamic Font Catalog"
strcpy(sCatalogPath, PATH_SEPARATOR_STR);
strncat(sCatalogPath, (char *) &essentialFiles[1], essentialFiles[0]);
strcat(sCatalogPath, PATH_SEPARATOR_STR);
strncat(sCatalogPath, (char *) &dynamicFonts[1], dynamicFonts[0]);
strcat(sCatalogPath, PATH_SEPARATOR_STR);
strncat(sCatalogPath, (char *) &dynamicFontCatalog[1], dynamicFontCatalog[0]);
// Don't care if this fails, file will be created when we save it.
jresult = nffbu_LoadCatalog(sUtility, sCatalogPath, NULL);
}
// "+ 2" is for one slash and the null
displayerPath = (char *) XP_ALLOC(sizeof(REQUIRED_GUTS_PATH) + dynamicFonts[0] + 2);
if (displayerPath != NULL)
{
// Build REQUIRED_GUTS_PATH "Dynamic Fonts/"
strcpy(displayerPath, REQUIRED_GUTS_PATH);
strncat(displayerPath, (char *) &dynamicFonts[1], dynamicFonts[0]);
strcat(displayerPath, DIRECTORY_SEPARATOR_STR);
// Result is number of displayers created, we don't really care
jresult = nffbp_ScanForFontDisplayers(brokerDisplayer, displayerPath, NULL);
XP_FREE(displayerPath);
}
}
void CWebFontReference::Finish()
{
jint result;
if (sCatalogPath != NULL)
{
result = nffbu_SaveCatalog(sUtility, sCatalogPath, NULL);
XP_FREE(sCatalogPath);
sCatalogPath = NULL;
}
}
// Make sure the rc's port is port...
void CWebFontReference::SynchToPort(GrafPtr port)
{
struct rc_data rcd = nfrc_GetPlatformData(fRenderingContext, NULL);
if (rcd.t.directRc.port != port)
{
rcd.t.directRc.port = port;
nfrc_SetPlatformData(fRenderingContext, &rcd, NULL);
}
}
// It's not clear to me that this is good hash, but it's fast!
uint32 CWebFontReference::PortHash(const void *port)
{
return (uint32) port;
}
// Should I look at the port fields if the addresses aren't equal?
int CWebFontReference::PortCompFunction(const void *port1, const void *port2)
{
return port1 != port2;
}
RenderingContextHandle CWebFontReference::GetCachedRenderingContext(GrafPtr port)
{
RenderingContextHandle rc = (RenderingContextHandle) XP_Gethash(sPortHash, port, NULL);
if (rc == NULL)
{
void *rcArgs[1];
rcArgs[0] = port;
rc = nffbu_CreateRenderingContext(sUtility, NF_RC_DIRECT, 0, rcArgs, 1, NULL);
if (rc != NULL)
{
XP_Puthash(sPortHash, port, rc);
}
// error?
}
return rc;
}
CFontReference *CWebFontReference::LookupWebFont(char *fontName, const CCharSet *charSet, const LO_TextAttr *attr, MWContext *context, Boolean underlineLinks)
{
FontMatchInfoHandle fmi;
RenderingContextHandle renderingContext;
WebFontHandle webFont;
char encoding[64]; // Should get the "64" from a header file...
char *charset = NULL; // Is this the right value?
int weight;
int style = nfStyleNormal;
int escapement = nfSpacingProportional;
int underline = nfUnderlineNo;
int strikeout = nfStrikeOutNo;
int resolutionX, resolutionY;
if (attr->font_weight != 0)
weight = attr->font_weight;
else if (attr->fontmask & LO_FONT_BOLD)
weight = WEIGHT_BOLD;
else
weight = WEIGHT_NORMAL;
if (attr->fontmask & LO_FONT_ITALIC)
style = nfStyleItalic;
if (attr->fontmask & LO_FONT_FIXED)
escapement = nfSpacingMonospaced;
if (attr->attrmask & LO_ATTR_UNDERLINE)
underline = nfUnderlineYes;
if ((attr->attrmask & LO_ATTR_ANCHOR) != 0 && underlineLinks)
underline = nfUnderlineYes;
if (attr->attrmask & LO_ATTR_STRIKEOUT)
strikeout = nfStrikeOutYes;
if (context->type == MWContextPrint)
{
THPrint hp = CPrefs::GetPrintRecord();
if (hp != NULL)
{
::PrValidate(hp);
resolutionX = (**hp).prInfo.iHRes;
resolutionY = (**hp).prInfo.iVRes;
}
else
{
// **** What is a good default in this case?
resolutionX = 72;
resolutionY = 72;
}
}
else
{
resolutionX = context->XpixelsPerPoint * 72.0;
resolutionY = context->YpixelsPerPoint * 72.0;
}
INTL_CharSetIDToName(charSet->fCSID, encoding);
fmi = nffbu_CreateFontMatchInfo(sUtility, fontName, charset, encoding,
weight, escapement, style, underline, strikeout,
resolutionX, resolutionY,
NULL);
if (fmi == NULL)
{
// if we can't get an fmi, just bail
return NULL;
}
// **** Is qd.thePort the right place to get the port from?
History_entry *he = SHIST_GetCurrent(&context->hist);
char *url = NULL;
if (he != NULL)
url = he->address;
renderingContext = GetCachedRenderingContext(qd.thePort);
webFont = nffbc_LookupFont(sBroker, renderingContext, fmi, url, NULL);
if (webFont == NULL)
{
nffbu_LookupFailed(sUtility, context, renderingContext, fmi, NULL);
nffmi_release(fmi, NULL);
return NULL;
}
return new CWebFontReference(webFont, charSet, attr);
}
CWebFontReference::CWebFontReference(WebFontHandle webFont, const CCharSet *charSet, const LO_TextAttr *attr) :
CFontReference(charSet, attr)
{
fRenderingContext = GetCachedRenderingContext(qd.thePort);
fRenderableFont = nff_GetRenderableFont(webFont, fRenderingContext, fSize, NULL);
nff_release(webFont, NULL);
}
CWebFontReference::~CWebFontReference()
{
nfrf_release(fRenderableFont, NULL);
}
void CWebFontReference::Apply()
{
if ( qd.thePort->txMode != fMode )
::TextMode( fMode );
}
void CWebFontReference::DrawText(int x, int y, char *text, int start, int end)
{
nfrf_DrawText(fRenderableFont, fRenderingContext, x, y, 0, (jbyte*)&text[start], end - start + 1, NULL);
}
short CWebFontReference::TextWidth(char *text, int firstByte, int byteCount)
{
jint totalLength;
jint *charLocs;
// **** Do we need to calculate the character count here?
charLocs = (jint *) XP_ALLOC(byteCount * sizeof(jint));
totalLength = nfrf_MeasureText(fRenderableFont, fRenderingContext, 0, (jbyte*)&text[firstByte], byteCount, charLocs, byteCount, NULL);
XP_FREE(charLocs);
return (short) totalLength;
}
void CWebFontReference::GetFontInfo(FontInfo *fontInfo)
{
if (fIsGetFontInfoDirty)
{
fCachedFontInfoValues.ascent = nfrf_GetFontAscent(fRenderableFont, NULL);
fCachedFontInfoValues.descent = nfrf_GetFontDescent(fRenderableFont, NULL);
fCachedFontInfoValues.widMax = nfrf_GetMaxWidth(fRenderableFont, NULL);
// **** Can we do better here?
fontInfo->leading = 0;
fIsGetFontInfoDirty = false;
}
*fontInfo = fCachedFontInfoValues;
}