common printing interface for Postscript and TrueType fonts

bug=144665 r=bstell@ix.netcom.com sr=jaggernaut@netscape.com
This commit is contained in:
pete.zha%sun.com 2002-09-27 07:46:19 +00:00
Родитель dea3487d09
Коммит 07569956eb
4 изменённых файлов: 352 добавлений и 135 удалений

Просмотреть файл

@ -39,6 +39,7 @@
#include "nsFontMetricsPS.h"
#include "nsDeviceContextPS.h"
#include "nsRenderingContextPS.h"
/** ---------------------------------------------------
* See documentation in nsFontMetricsPS.h
@ -60,11 +61,6 @@ nsFontMetricsPS :: ~nsFontMetricsPS()
mFont = nsnull;
}
if(nsnull != mAFMInfo){
delete mAFMInfo;
mAFMInfo = nsnull;
}
if (mDeviceContext) {
// Notify our device context that owns us so that it can update its font cache
mDeviceContext->FontMetricsDeleted(this);
@ -88,22 +84,8 @@ nsFontMetricsPS :: Init(const nsFont& aFont, nsIAtom* aLangGroup,
//don't addref this to avoid circular refs
mDeviceContext = (nsDeviceContextPS *)aContext;
// get the AFM information
mAFMInfo = new nsAFMObject();
mAFMInfo->Init(mFont->size/20);
// first see if the primary font is available
mFontIndex = mAFMInfo->CheckBasicFonts(aFont,PR_TRUE);
if( mFontIndex < 0){
// look in an AFM file for the primary font
if (PR_FALSE == mAFMInfo->AFM_ReadFile(aFont) ) {
// look for secondary fonts
mFontIndex = mAFMInfo->CheckBasicFonts(aFont,PR_FALSE);
if( mFontIndex < 0){
mFontIndex = mAFMInfo->CreateSubstituteFont(aFont);
}
}
}
mFontPS = nsFontPS::FindFont(aFont, NS_STATIC_CAST(nsIFontMetrics*, this));
NS_ENSURE_TRUE(mFontPS, NS_ERROR_FAILURE);
RealizeFont();
return NS_OK;
@ -123,53 +105,12 @@ nsFontMetricsPS :: Destroy()
*/
void
nsFontMetricsPS::RealizeFont()
{
float fontsize;
float dev2app;
float offset;
mDeviceContext->GetDevUnitsToAppUnits(dev2app);
nscoord onePixel = NSToCoordRound(1 * dev2app);
// convert the font size which is in twips to points
fontsize = mFont->size/20.0f;
offset=NSFloatPointsToTwips(fontsize*mAFMInfo->mPSFontInfo->mXHeight)/1000.0f;
mXHeight = NSToCoordRound(offset);
//mXHeight = NSToCoordRound((float)((fontsize*mAFMInfo->mPSFontInfo->mXHeight)/1000.0)*dev2app);
mSuperscriptOffset = mXHeight;
mSubscriptOffset = mXHeight;
mStrikeoutSize = onePixel;
mStrikeoutOffset = (nscoord)(mXHeight / 2.0f);
mUnderlineSize = onePixel;
offset=NSFloatPointsToTwips(fontsize*mAFMInfo->mPSFontInfo->mUnderlinePosition)/1000.0f;
mUnderlineOffset = NSToCoordRound(offset);
mHeight = NSToCoordRound(fontsize * dev2app);
offset=NSFloatPointsToTwips(fontsize*mAFMInfo->mPSFontInfo->mAscender)/1000.0f;
mAscent = NSToCoordRound(offset);
offset=NSFloatPointsToTwips(fontsize*mAFMInfo->mPSFontInfo->mDescender)/1000.0f;
mDescent = -(NSToCoordRound(offset));
mLeading = 0;
mEmHeight = mHeight;
mEmAscent = mAscent;
mEmDescent = mDescent;
mMaxHeight = mHeight;
mMaxAscent = mAscent;
mMaxDescent = mDescent;
mMaxAdvance = mHeight;
GetStringWidth(" ", mSpaceWidth, 1);
GetStringWidth("x", mAveCharWidth, 1);
{
if (mFont && mDeviceContext) {
float dev2app;
mDeviceContext->GetDevUnitsToAppUnits(dev2app);
mFontPS->RealizeFont(this, dev2app);
}
}
/** ---------------------------------------------------
@ -394,11 +335,8 @@ nsFontMetricsPS::GetFontHandle(nsFontHandle &aHandle)
NS_IMETHODIMP
nsFontMetricsPS :: GetStringWidth(const char *aString,nscoord& aWidth,nscoord aLength)
{
if(mAFMInfo){
mAFMInfo->GetStringWidth(aString,aWidth,aLength);
}
NS_ENSURE_TRUE(mFontPS, NS_ERROR_NULL_POINTER);
aWidth = mFontPS->GetWidth(aString, aLength);
return NS_OK;
}
@ -411,10 +349,202 @@ nsFontMetricsPS :: GetStringWidth(const char *aString,nscoord& aWidth,nscoord aL
NS_IMETHODIMP
nsFontMetricsPS :: GetStringWidth(const PRUnichar *aString,nscoord& aWidth,nscoord aLength)
{
NS_ENSURE_TRUE(mFontPS, NS_ERROR_NULL_POINTER);
aWidth = mFontPS->GetWidth(aString, aLength);
return NS_OK;
}
if(mAFMInfo){
mAFMInfo->GetStringWidth(aString,aWidth,aLength);
// nsFontPS
nsFontPS*
nsFontPS::FindFont(const nsFont& aFont, nsIFontMetrics* aFontMetrics)
{
/* Currently, we only select font from afm file.
* In the future, this function is responsible for font selection
* from different type of fonts.
*/
nsFontPSAFM* fontAFM = new nsFontPSAFM(aFont, aFontMetrics);
if (!fontAFM) return nsnull;
if (fontAFM->mFontIndex < 0) {
delete fontAFM;
return nsnull;
}
return fontAFM;
}
nsFontPS::nsFontPS(const nsFont& aFont, nsIFontMetrics* aFontMetrics) :
mFontIndex(-1)
{
mFont = new nsFont(aFont);
if (!mFont) return;
mFontMetrics = aFontMetrics;
}
nsFontPS::~nsFontPS()
{
if (mFont) {
delete mFont;
mFont = nsnull;
}
if (mCCMap) {
FreeCCMap(mCCMap);
}
mFontMetrics = nsnull;
}
// nsFontPSAFM
nsFontPSAFM::nsFontPSAFM(const nsFont& aFont, nsIFontMetrics* aFontMetrics) :
nsFontPS(aFont, aFontMetrics)
{
if (!mFont) return;
// get the AFM information
mAFMInfo = new nsAFMObject();
if (!mAFMInfo) return;
mAFMInfo->Init(mFont->size / TWIPS_PER_POINT_FLOAT);
// first see if the primary font is available
mFontIndex = mAFMInfo->CheckBasicFonts(aFont, PR_TRUE);
if (mFontIndex < 0) {
// look in an AFM file for the primary font
if (PR_FALSE == mAFMInfo->AFM_ReadFile(aFont)) {
// look for secondary fonts
mFontIndex = mAFMInfo->CheckBasicFonts(aFont, PR_FALSE);
if (mFontIndex < 0) {
mFontIndex = mAFMInfo->CreateSubstituteFont(aFont);
}
}
}
mFamilyName.AssignWithConversion((char*)mAFMInfo->mPSFontInfo->mFamilyName);
}
nsFontPSAFM::~nsFontPSAFM()
{
if (mAFMInfo) {
delete mAFMInfo;
mAFMInfo = nsnull;
}
}
nscoord
nsFontPSAFM::GetWidth(const char* aString, PRUint32 aLength)
{
nscoord width;
if (mAFMInfo) {
mAFMInfo->GetStringWidth(aString, width, aLength);
}
return width;
}
nscoord
nsFontPSAFM::GetWidth(const PRUnichar* aString, PRUint32 aLength)
{
nscoord width;
if (mAFMInfo) {
mAFMInfo->GetStringWidth(aString, width, aLength);
}
return width;
}
nscoord
nsFontPSAFM::DrawString(nsRenderingContextPS* aContext,
nscoord aX, nscoord aY,
const char* aString, PRUint32 aLength)
{
NS_ENSURE_TRUE(aContext, 0);
nsPostScriptObj* psObj = aContext->GetPostScriptObj();
NS_ENSURE_TRUE(psObj, 0);
psObj->moveto(aX, aY);
psObj->show(aString, aLength, "");
return GetWidth(aString, aLength);
}
nscoord
nsFontPSAFM::DrawString(nsRenderingContextPS* aContext,
nscoord aX, nscoord aY,
const PRUnichar* aString, PRUint32 aLength)
{
NS_ENSURE_TRUE(aContext, 0);
nsPostScriptObj* psObj = aContext->GetPostScriptObj();
NS_ENSURE_TRUE(psObj, 0);
psObj->moveto(aX, aY);
psObj->show(aString, aLength, "");
return GetWidth(aString, aLength);
}
nsresult
nsFontPSAFM::RealizeFont(nsFontMetricsPS* aFontMetrics, float dev2app)
{
NS_ENSURE_ARG_POINTER(aFontMetrics);
float fontSize;
float offset;
nscoord onePixel = NSToCoordRound(1 * dev2app);
// convert the font size which is in twips to points
fontSize = mFont->size / TWIPS_PER_POINT_FLOAT;
offset = NSFloatPointsToTwips(fontSize * mAFMInfo->mPSFontInfo->mXHeight) / 1000.0f;
nscoord xHeight = NSToCoordRound(offset);
aFontMetrics->SetXHeight(xHeight);
aFontMetrics->SetSuperscriptOffset(xHeight);
aFontMetrics->SetSubscriptOffset(xHeight);
aFontMetrics->SetStrikeout((nscoord)(xHeight / TWIPS_PER_POINT_FLOAT), onePixel);
offset = NSFloatPointsToTwips(fontSize * mAFMInfo->mPSFontInfo->mUnderlinePosition) / 1000.0f;
aFontMetrics->SetUnderline(NSToCoordRound(offset), onePixel);
nscoord size = NSToCoordRound(fontSize * dev2app);
aFontMetrics->SetHeight(size);
aFontMetrics->SetEmHeight(size);
aFontMetrics->SetMaxAdvance(size);
aFontMetrics->SetMaxHeight(size);
offset = NSFloatPointsToTwips(fontSize * mAFMInfo->mPSFontInfo->mAscender) / 1000.0f;
nscoord ascent = NSToCoordRound(offset);
aFontMetrics->SetAscent(ascent);
aFontMetrics->SetEmAscent(ascent);
aFontMetrics->SetMaxAscent(ascent);
offset = NSFloatPointsToTwips(fontSize * mAFMInfo->mPSFontInfo->mDescender) / 1000.0f;
nscoord descent = -(NSToCoordRound(offset));
aFontMetrics->SetDescent(descent);
aFontMetrics->SetEmDescent(descent);
aFontMetrics->SetMaxDescent(descent);
aFontMetrics->SetLeading(0);
nscoord spaceWidth = GetWidth(" ", 1);
aFontMetrics->SetSpaceWidth(spaceWidth);
nscoord aveCharWidth = GetWidth("x", 1);
aFontMetrics->SetAveCharWidth(aveCharWidth);
return NS_OK;
}
#ifdef MOZ_MATHML
nsresult
nsFontPSAFM::GetBoundingMetrics(const char* aString,
PRUint32 aLength,
nsBoundingMetrics& aBoundingMetrics)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsFontPSAFM::GetBoundingMetrics(const PRUnichar* aString,
PRUint32 aLength,
nsBoundingMetrics& aBoundingMetrics)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
#endif

Просмотреть файл

@ -48,8 +48,12 @@
#include "nsIDeviceContext.h"
#include "nsCOMPtr.h"
#include "nsCRT.h"
#include "nsCompressedCharMap.h"
#include "nsPostScriptObj.h"
class nsDeviceContextPS;
class nsRenderingContextPS;
class nsFontPS;
class nsFontMetricsPS : public nsIFontMetrics
{
@ -88,9 +92,28 @@ public:
NS_IMETHOD GetFontHandle(nsFontHandle &aHandle);
NS_IMETHOD GetStringWidth(const char *String,nscoord &aWidth,nscoord aLength);
NS_IMETHOD GetStringWidth(const PRUnichar *aString,nscoord &aWidth,nscoord aLength);
inline void SetXHeight(nscoord aXHeight) { mXHeight = aXHeight; };
inline void SetSuperscriptOffset(nscoord aSuperscriptOffset) { mSuperscriptOffset = aSuperscriptOffset; };
inline void SetSubscriptOffset(nscoord aSubscriptOffset) { mSubscriptOffset = aSubscriptOffset; };
inline void SetStrikeout(nscoord aOffset, nscoord aSize) { mStrikeoutOffset = aOffset; mStrikeoutSize = aSize; };
inline void SetUnderline(nscoord aOffset, nscoord aSize) { mUnderlineOffset = aOffset; mUnderlineSize = aSize; };
inline void SetHeight(nscoord aHeight) { mHeight = aHeight; };
inline void SetLeading(nscoord aLeading) { mLeading = aLeading; };
inline void SetAscent(nscoord aAscent) { mAscent = aAscent; };
inline void SetDescent(nscoord aDescent) { mDescent = aDescent; };
inline void SetEmHeight(nscoord aEmHeight) { mEmHeight = aEmHeight; };
inline void SetEmAscent(nscoord aEmAscent) { mEmAscent = aEmAscent; };
inline void SetEmDescent(nscoord aEmDescent) { mEmDescent = aEmDescent; };
inline void SetMaxHeight(nscoord aMaxHeight) { mMaxHeight = aMaxHeight; };
inline void SetMaxAscent(nscoord aMaxAscent) { mMaxAscent = aMaxAscent; };
inline void SetMaxDescent(nscoord aMaxDescent) { mMaxDescent = aMaxDescent; };
inline void SetMaxAdvance(nscoord aMaxAdvance) { mMaxAdvance = aMaxAdvance; };
inline void SetAveCharWidth(nscoord aAveCharWidth) { mAveCharWidth = aAveCharWidth; };
inline void SetSpaceWidth(nscoord aSpaceWidth) { mSpaceWidth = aSpaceWidth; };
PRInt16 GetFontIndex() { return mFontIndex; }
inline nsFontPS* GetFontPS() { return mFontPS; }
#if defined(XP_WIN)
// this routine is defined here so the PostScript module can be debugged
// on the windows platform
@ -130,10 +153,84 @@ protected:
nscoord mUnderlineOffset;
nscoord mSpaceWidth;
nscoord mAveCharWidth;
PRInt16 mFontIndex;
nsFontPS* mFontPS;
};
class nsFontPS
{
public:
nsAFMObject *mAFMInfo;
nsFontPS();
nsFontPS(const nsFont& aFont, nsIFontMetrics* aFontMetrics);
virtual ~nsFontPS();
NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
static nsFontPS* FindFont(const nsFont& aFont, nsIFontMetrics* aFontMetrics);
inline PRInt32 SupportsChar(PRUnichar aChar)
{ return mCCMap && CCMAP_HAS_CHAR(mCCMap, aChar); };
inline PRInt16 GetFontIndex() { return mFontIndex; };
inline const nsString& GetFamilyName() { return mFamilyName; };
virtual nscoord GetWidth(const char* aString, PRUint32 aLength) = 0;
virtual nscoord GetWidth(const PRUnichar* aString, PRUint32 aLength) = 0;
virtual nscoord DrawString(nsRenderingContextPS* aContext,
nscoord aX, nscoord aY,
const char* aString, PRUint32 aLength) = 0;
virtual nscoord DrawString(nsRenderingContextPS* aContext,
nscoord aX, nscoord aY,
const PRUnichar* aString, PRUint32 aLength) = 0;
virtual nsresult RealizeFont(nsFontMetricsPS* aFontMetrics, float dev2app) = 0;
#ifdef MOZ_MATHML
virtual nsresult
GetBoundingMetrics(const char* aString,
PRUint32 aLength,
nsBoundingMetrics& aBoundingMetrics) = 0;
virtual nsresult
GetBoundingMetrics(const PRUnichar* aString,
PRUint32 aLength,
nsBoundingMetrics& aBoundingMetrics) = 0;
#endif
protected:
nsFont* mFont;
PRUint16* mCCMap;
nsCOMPtr<nsIFontMetrics> mFontMetrics;
PRInt16 mFontIndex;
nsString mFamilyName;
};
class nsFontPSAFM : public nsFontPS
{
public:
nsFontPSAFM(const nsFont& aFont, nsIFontMetrics* aFontMetrics);
virtual ~nsFontPSAFM();
NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
nscoord GetWidth(const char* aString, PRUint32 aLength);
nscoord GetWidth(const PRUnichar* aString, PRUint32 aLength);
nscoord DrawString(nsRenderingContextPS* aContext,
nscoord aX, nscoord aY,
const char* aString, PRUint32 aLength);
nscoord DrawString(nsRenderingContextPS* aContext,
nscoord aX, nscoord aY,
const PRUnichar* aString, PRUint32 aLength);
nsresult RealizeFont(nsFontMetricsPS* aFontMetrics, float dev2app);
#ifdef MOZ_MATHML
nsresult
GetBoundingMetrics(const char* aString,
PRUint32 aLength,
nsBoundingMetrics& aBoundingMetrics);
nsresult
GetBoundingMetrics(const PRUnichar* aString,
PRUint32 aLength,
nsBoundingMetrics& aBoundingMetrics);
#endif
nsAFMObject* mAFMInfo;
};
#endif

Просмотреть файл

@ -1092,52 +1092,44 @@ nsRenderingContextPS :: GetTextDimensions(const PRUnichar* aString, PRUint32 aLe
* See documentation in nsIRenderingContext.h
* @update 12/21/98 dwc
*/
NS_IMETHODIMP
NS_IMETHODIMP
nsRenderingContextPS :: DrawString(const char *aString, PRUint32 aLength,
nscoord aX, nscoord aY,
const nscoord* aSpacing)
{
PRInt32 x = aX;
PRInt32 y = aY;
NS_ENSURE_TRUE(mTranMatrix && mPSObj && mFontMetrics, NS_ERROR_NULL_POINTER);
nsFontMetricsPS *metrics = NS_REINTERPRET_CAST(nsFontMetricsPS *, mFontMetrics.get());
NS_ENSURE_TRUE(metrics, NS_ERROR_FAILURE);
nsFontPS* fontPS = metrics->GetFontPS();
NS_ENSURE_TRUE(fontPS, NS_ERROR_FAILURE);
PRInt32 x = aX;
PRInt32 y = aY;
mPSObj->setlanggroup(nsnull);
SetupFontAndColor();
PRInt32 dxMem[500];
PRInt32* dx0 = 0;
if (nsnull != aSpacing) {
if (aSpacing) {
dx0 = dxMem;
if (aLength > 500) {
dx0 = new PRInt32[aLength];
NS_ENSURE_TRUE(dx0, NS_ERROR_OUT_OF_MEMORY);
}
mTranMatrix->ScaleXCoords(aSpacing, aLength, dx0);
}
mTranMatrix->TransformCoord(&x, &y);
PostscriptTextOut(aString, aLength, NS_PIXELS_TO_POINTS(x), NS_PIXELS_TO_POINTS(y), aLength, (const nscoord*) (aSpacing ? dx0 : nsnull), PR_FALSE);
mTranMatrix->TransformCoord(&x, &y);
fontPS->DrawString(this, NS_PIXELS_TO_POINTS(x), NS_PIXELS_TO_POINTS(y), aString, aLength);
if ((nsnull != aSpacing) && (dx0 != dxMem)) {
if ((aSpacing) && (dx0 != dxMem)) {
delete [] dx0;
}
#if 0
//this doesn't need to happen here anymore, but a
//new api will come along that will need this stuff. MMP
if (mFontMetrics) {
nsFont *font;
mFontMetrics->GetFont(font);
PRUint8 decorations = font->decorations;
if (decorations & NS_FONT_DECORATION_OVERLINE){
nscoord offset;
nscoord size;
mFontMetrics->GetUnderline(offset, size);
FillRect(aX, aY, aWidth, size);
}
}
#endif
return NS_OK;
}
@ -1150,9 +1142,16 @@ nsRenderingContextPS :: DrawString(const PRUnichar *aString, PRUint32 aLength,
nscoord aX, nscoord aY, PRInt32 aFontID,
const nscoord* aSpacing)
{
PRInt32 x = aX;
PRInt32 y = aY;
nsIFontMetrics *fMetrics;
NS_ENSURE_TRUE(mTranMatrix && mPSObj && mFontMetrics, NS_ERROR_NULL_POINTER);
nsFontMetricsPS *metrics = NS_REINTERPRET_CAST(nsFontMetricsPS *, mFontMetrics.get());
NS_ENSURE_TRUE(metrics, NS_ERROR_FAILURE);
nsFontPS* fontPS = metrics->GetFontPS();
NS_ENSURE_TRUE(fontPS, NS_ERROR_FAILURE);
PRInt32 x = aX;
PRInt32 y = aY;
nsCOMPtr<nsIAtom> langGroup = nsnull;
mFontMetrics->GetLangGroup(getter_AddRefs(langGroup));
@ -1163,36 +1162,22 @@ nsIFontMetrics *fMetrics;
SetupFontAndColor();
if (nsnull != aSpacing){
if (aSpacing) {
// Slow, but accurate rendering
const PRUnichar* end = aString + aLength;
while (aString < end){
x = aX;
y = aY;
mTranMatrix->TransformCoord(&x, &y);
PostscriptTextOut((PRUnichar *)aString, 1, NS_PIXELS_TO_POINTS(x), NS_PIXELS_TO_POINTS(y), aFontID, aSpacing, PR_TRUE);
fontPS->DrawString(this, NS_PIXELS_TO_POINTS(x), NS_PIXELS_TO_POINTS(y), aString, 1);
aX += *aSpacing++;
aString++;
}
} else {
mTranMatrix->TransformCoord(&x, &y);
PostscriptTextOut(aString, aLength, NS_PIXELS_TO_POINTS(x), NS_PIXELS_TO_POINTS(y), aFontID, aSpacing, PR_TRUE);
fontPS->DrawString(this, NS_PIXELS_TO_POINTS(x), NS_PIXELS_TO_POINTS(y), aString, aLength);
}
fMetrics = mFontMetrics;
if (fMetrics) {
const nsFont *font;
fMetrics->GetFont(font);
PRUint8 decorations = font->decorations;
if (decorations & NS_FONT_DECORATION_OVERLINE){
nscoord offset;
nscoord size;
fMetrics->GetUnderline(offset, size);
//FillRect(aX, aY, aWidth, size);
}
}
return NS_OK;
}
@ -1426,26 +1411,29 @@ NS_IMETHODIMP nsRenderingContextPS::RetrieveCurrentNativeGraphicData(PRUint32 *
void
nsRenderingContextPS :: SetupFontAndColor(void)
{
nscoord fontHeight = 0;
PRInt16 fontIndex;
const nsFont *font;
nsFontHandle fontHandle;
nsAutoString fontFamily;
if (!mPSObj) return;
nscoord fontHeight = 0;
PRInt16 fontIndex = -1;
const nsFont *font;
mFontMetrics->GetHeight(fontHeight);
mFontMetrics->GetFont(font);
mFontMetrics->GetFontHandle(fontHandle);
//mStates->mFont = mCurrFont = tfont;
mStates->mFontMetrics = mFontMetrics;
nsFontMetricsPS *metrics = NS_REINTERPRET_CAST(nsFontMetricsPS *, mFontMetrics.get());
// get the fontfamily we are using, not what we want, but what we are using
fontFamily.AssignWithConversion(metrics->mAFMInfo->mPSFontInfo->mFamilyName);
fontIndex = metrics->GetFontIndex();
if (!metrics) return;
mPSObj->setscriptfont(fontIndex,fontFamily,fontHeight,font->style,font->variant,font->weight,font->decorations);
nsFontPS* fontPS = metrics->GetFontPS();
if (!fontPS) return;
fontIndex = fontPS->GetFontIndex();
mPSObj->setscriptfont(fontPS->GetFontIndex(), fontPS->GetFamilyName(),
fontHeight, font->style, font->variant,
font->weight, font->decorations);
}
/** ---------------------------------------------------

Просмотреть файл

@ -224,6 +224,8 @@ public:
nscoord aX, nscoord aY, PRInt32 aFontID,
const nscoord* aSpacing, PRBool aIsUnicode);
inline nsPostScriptObj* GetPostScriptObj() { return mPSObj; };
#ifdef XP_WIN
// this define is here only so the postscript can be compiled
// and tested on the windows platform.