/********************************************************************************
*                                                                               *
*                   P o s t c r i p t   F o n t   O b j e c t                   *
*                                                                               *
*********************************************************************************
* Copyright (C) 1997,2003 by Jeroen van der Zijp.   All Rights Reserved.        *
*********************************************************************************
* This library is free software; you can redistribute it and/or                 *
* modify it under the terms of the GNU Lesser General Public                    *
* License as published by the Free Software Foundation;                         *
* version 2.1 of the License.                                                   *
*                                                                               *
* This library is distributed in the hope that it will be useful,               *
* but WITHOUT ANY WARRANTY; without even the implied warranty of                *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU             *
* Lesser General Public License for more details.                               *
*                                                                               *
* You should have received a copy of the GNU Lesser General Public              *
* License along with this library; if not, write to the Free Software           *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.    *
*
* Together with the FOX addendum that permits distribution of statically linked *
* binaries.                                                                     *
*********************************************************************************
* $Id: FXPostscriptFont.cpp,v 1.65 2003/10/27 23:59:21 fox Exp $                *
********************************************************************************/

// This file was written by A C Norman based on FXFont.c, for which the
// above copyright etc apply.

/* Signature: 6af519ba 19-Jun-2007 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#else
#define HAVE_LIBFOX 1
#endif
                                                                                
#ifdef HAVE_LIBFOX

#include <fx.h>
#include "FXPostscriptFont.h"
#include <ctype.h>

using namespace FX;

namespace FX {


/*******************************************************************************/


#ifdef WANTED_AND_ACTUAL
// A bunch of field-names changed between 1.1.45 and 1.1.49 so here for
// older releases I map onto the newer names. It may be that I should be a
// lot more carefull wrt actualXxx and wantedXxx.


// version 1.1.x is SO old now that I might properly disard this...

#define actualName      name     
#define actualSize      size     
#define actualWeight    weight   
#define actualSlant     slant    
#define actualEncoding  encoding 
#define actualSetwidth  setwidth 

#endif

// Object implementation
FXIMPLEMENT(FXPostscriptFont,FXFont,NULL,0)


// Deserialization
FXPostscriptFont::FXPostscriptFont(){
  font=(void*)-1L;
  }


// Construct font from X11 font spec. Well for Postscript Fonts
// I will not support decoding X11 long-names...

FXPostscriptFont::FXPostscriptFont(FXApp* a,const FXString& nm):FXFont(a,nm){
  FXTRACE((100,"FXPostscriptFont::FXPostscriptFont %p\n",this));
  actualSize=0;
  actualWeight=0;
  actualSlant=0;
  actualEncoding=FONTENCODING_DEFAULT;
#if (FOX_MINOR<=4)
  actualSetwidth=FONTSETWIDTH_DONTCARE;
#else
  actualSetwidth=0;
#endif
  font=NULL;
  }


// Construct a font with given face name, size in points(pixels), weight, slant, character set encoding, setwidth, and hints
FXPostscriptFont::FXPostscriptFont(FXApp* a, const FXString &face, FXuint sz,
    FXuint wt, FXuint sl, FXuint enc, FXuint setw, FXuint h):FXFont(a,face){
  FXTRACE((100,"FXPostscriptFont::FXPostscriptFont %p\n",this));
  actualSize=10*sz;
  actualWeight=wt;
  actualSlant=sl;
  actualEncoding=enc;
  actualSetwidth=setw;
#if (FOX_MINOR<=4)
  hints=(h&~FONTHINT_X11);
#else
  hints=(h&~FXFont::X11);
#endif
  font=NULL;
  }


// Construct font from font description
FXPostscriptFont::FXPostscriptFont(FXApp* a,
     const FXFontDesc &fontdesc):FXFont(a, fontdesc){
  FXTRACE((100,"FXPostscriptFont::FXPostscriptFont %p\n",this));
  actualName=fontdesc.face;
  actualSize=fontdesc.size;
  actualWeight=fontdesc.weight;
  actualSlant=fontdesc.slant;
  actualEncoding=fontdesc.encoding;
  actualSetwidth=fontdesc.setwidth;
  hints=fontdesc.flags; 
  font=NULL;
  }


/*******************************************************************************/




//
// When generating Postscript I need font metrics. These can be extracted
// from "afm" files that Adobe supply. I have a program "get-adobe-metrics.c"
// that can be run on a typical Linux system to extract the (minimal) metric
// information that I need here and build a file "font-info.c". By including
// that file here I collect info about all the standard Postscript Fonts.
// I will not permit a user to use any other fonts than these!
//


//
// There are 35 standard Postscript fonts that might be used with this
// print package. They are:
//      AvantGarde-Book 
//      AvantGarde-BookOblique 
//      AvantGarde-Demi 
//      AvantGarde-DemiOblique 
//      Bookman-Demi 
//      Bookman-DemiItalic 
//      Bookman-Light 
//      Bookman-LightItalic 
// *    Courier-Bold 
// *    Courier-BoldOblique 
// *    Courier 
// *    Courier-Oblique 
// *    Helvetica-Bold 
// *    Helvetica-BoldOblique 
//      Helvetica-NarrowBold 
//      Helvetica-NarrowBoldOblique 
// *    Helvetica 
// *    Helvetica-Oblique 
//      Helvetica-Narrow 
//      Helvetica-NarrowOblique 
//      NewCenturySchlbk-Bold 
//      NewCenturySchlbk-BoldItalic 
//      NewCenturySchlbk-Italic 
//      NewCenturySchlbk-Roman 
//      Palatino-Bold 
//      Palatino-BoldItalic 
//      Palatino-Italic 
//      Palatino-Roman 
// *    Symbol 
// *    Times-Bold 
// *    Times-BoldItalic 
// *    Times-Italic 
// *    Times-Roman 
//      ZapfChancery-MediumItalic 
//      ZapfDingbats 
// [those marked * are the original 13]
//
//

// Font metrics: I extract these from .afm files so that I can have
// rapid access to the information that I need throughout this package.
// The effect is that I fill a few kilobytes of memory with font tables.
// To avoid going over the top I will not keep per-character bearing
// information.  The information in the font tables is based on having a
// nominal character cell that is 1000 units high, which means that
// integer metrics are quite accurate enough for everybody.

#include "font-info.c"

// Here is the sort of info found in "font-info.c". Each font
// listed comes with its full Adobe name.
// [It will shortly need (max)width, height, ascent, descent,
// leading, (max)left-bearing (max)right-bearing]
// The main data is then an array of 256 short integers. This contains
// -1 if the character is not present, otherwise the character width.
// Note once again that these numbers are all based on a nominal 1pt font
// with units of 1/1000. Another way to think of this is that numbers are
// in units of 1/72000in and will need to be multiplied by the point size
// of a font before use.
//
//  typedef struct font_info {
//     char *name;
//     short int isfixed, fontwidth, maxleftbearing, maxrightbearing;
//     short int capheight, xheight, ascent, descent;
//     short int charwidth[256];
//  } font_info;
//  
//  static font_info font_widths[] = {
//  {"AvantGarde-Demi", 0, 1280, 233, 234, 740, 555, 740, -185, {
//      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
//      ...
//      -1,   874,   760,   946,   771,   865,   771,   888,
//     967,   888,   831,   873,   927,   970,   918,    -1 }},
//  {(char *)0, {}}};
// 

static font_info *find_metrics(const char *name)
{
    font_info *p = font_widths;
    while (p->name != NULL && strcmp(name, p->name)!=0) p++;
    if (p->name != NULL) return p;
    return &font_widths[0];  // this should never occur!
} 


// Create font. In this case I take that to mean the process of refining
// the font name and fetching associated metrics. 

enum family {
    AvantGarde,
    Bookman,
    Courier,
    Helvetica,
    NewCenturySchlbk,
    Palatino,
    Symbol,
    Times,
    ZapfChancery,
    ZapfDingbats
};

// return true of s has p as an initial sub-string, ignoring case

static FXbool matches(const char *s, const char *p)
{
    while (*p != 0)
    {   if (*s==0 || tolower(*s)!=tolower(*p)) return FALSE;
        s++;
        p++;
    }
    return TRUE;
}

// return true if s has p as a substring, ignoring case

static FXbool contains(const char *s, const char *p)
{
    while (*s != 0)
    {   if (matches(s, p)) return TRUE;
        s++;
    }
    return FALSE;
}

void FXPostscriptFont::create()
{
// I will start by massaging the given name etc to provide me with one of the
// canonical Postscript names. Note that the tests on font families
// are done in an order so that later test acn override tentative decisions
// made by earlier ones. Eg the font with name "roman" will first be
// identified as asking for Times (roman, bold etc), but if the word
// "palatino" is see too then you get Palatino-Roman. If the name is
// not recognised at all you will get Times
    family fam = Times;    // a default family to use
    const char *n = actualName.text();
#if (FOX_MINOR<=4)
    if (contains(n, "bold")) actualWeight = FONTWEIGHT_BOLD;
    if (contains(n, "light")) actualWeight = FONTWEIGHT_LIGHT;
    if (contains(n, "italic")) actualSlant = FONTSLANT_ITALIC;
    if (contains(n, "oblique")) actualSlant = FONTSLANT_OBLIQUE;
    if (contains(n, "narrow")) actualSetwidth = FONTSETWIDTH_NARROW;
#else
    if (contains(n, "bold")) actualWeight = FXFont::Bold;
    if (contains(n, "light")) actualWeight = FXFont::Light;
    if (contains(n, "italic")) actualSlant = FXFont::Italic;
    if (contains(n, "oblique")) actualSlant = FXFont::Oblique;
    if (contains(n, "narrow")) actualSetwidth = FXFont::Condensed;
#endif
    if (contains(n, "times") ||
        contains(n, "serif") ||
        contains(n, "roman")) fam = Times;
    if (contains(n, "avant")) fam = AvantGarde;
    if (contains(n, "bookman")) fam = Bookman;
    if (contains(n, "courier") ||
        contains(n, "fixed")) fam = Courier;
    if (contains(n, "helvet") ||
        contains(n, "ariel") ||
        contains(n, "sanserif") ||
        contains(n, "swiss")) fam = Helvetica;
    if (contains(n, "newcent")) fam = NewCenturySchlbk;
    if (contains(n, "palatin")) fam = Palatino;
    if (contains(n, "symbol")) fam = Symbol;
    if (contains(n, "chancery") ||
        contains(n, "zapf")) fam = ZapfChancery;
    if (contains(n, "dingbat")) fam = ZapfDingbats;
// Now a few cases that override
#if (FOX_MINOR<=4)
    if (actualSetwidth != FONTSETWIDTH_DONTCARE &&
        actualSetwidth <= FONTSETWIDTH_COMPRESSED) fam = Helvetica;
    if (hints & (FONTPITCH_FIXED|FONTHINT_MODERN)) fam = Courier;
    else if ((hints & FONTPITCH_VARIABLE) && fam==Courier) fam = Helvetica;
 
#define four(reg, bold, ital, boldital) \
       (actualWeight<=FONTWEIGHT_NORMAL ? \
         (actualSlant<=FONTSLANT_REGULAR ? reg : ital) : \
         (actualSlant<=FONTSLANT_REGULAR ? bold : boldital))
#else
    if (actualSetwidth != 0 &&
        actualSetwidth <= FXFont::Condensed) fam = Helvetica;
    if (hints & (FXFont::Fixed|FXFont::Modern)) fam = Courier;
    else if ((hints & FXFont::Variable) && fam==Courier) fam = Helvetica;
 
#define four(reg, bold, ital, boldital) \
       (actualWeight<=FXFont::Normal ? \
         (actualSlant<=FXFont::Straight ? reg : ital) : \
         (actualSlant<=FXFont::Straight ? bold : boldital))
#endif
    switch (fam)
    {
case AvantGarde:
        actualName = four("AvantGarde-Book", "AvantGarde-Demi",
                    "AvantGarde-BookOblique", "AvantGarde-DemiOblique");
        break;
case Bookman:
        actualName = four("Bookman-Light", "Bookman-Demi",
                    "Bookman-LightItalic", "Bookman-DemiItalic");
        break;
case Courier:
        actualName = four("Courier", "Courier-Bold",
                    "Courier-Oblique", "Courier-BoldOblique");
        break;
case Helvetica:
#if (FOX_MINOR<=4)
        if (actualSetwidth == FONTSETWIDTH_NARROW)
#else
        if (actualSetwidth == FXFont::Condensed)
#endif
          actualName = four("Helvetica-Narrowi", "Helvetica-NarrowBold",
                      "Helvetica-NarrowOblique", "Helvetica-NarrowBoldOblique");
        else
          actualName = four("Helvetica", "Helvetica-Bold",
                      "Helvetica-Oblique", "Helvetica-BoldOblique");
        break;
case NewCenturySchlbk:
        actualName = four("NewCenturySchlbk", "NewCenturySchlbk-Bold",
                    "NewCenturySchlbk-Italic", "NewCenturySchlbk-BoldItalic");
        break;
case Palatino:
        actualName = four("Palatino-Roman", "Palatino-Bold",
                    "Palatino-Italic", "Palatino-BoldItalic");
        break;
case Symbol:
        actualName = "Symbol";
#if (FOX_MINOR<=4)
        actualWeight = FONTWEIGHT_NORMAL;
        actualSlant = FONTSLANT_REGULAR;
#else
        actualWeight = FXFont::Normal;
        actualSlant = FXFont::Straight;
#endif
        break;
case Times:
default:
        actualName = four("Times-Roman", "Times-Bold",
                    "Times-Italic", "Times-BoldItalic");
        break;
case ZapfChancery:
        actualName = "ZapfChancery-MediumItalic";
#if (FOX_MINOR<=4)
        actualWeight = FONTWEIGHT_NORMAL;
        actualSlant = FONTSLANT_ITALIC;
#else
        actualWeight = FXFont::Normal;
        actualSlant = FXFont::Italic;
#endif
        break;
case ZapfDingbats:
        actualName = "ZapfDingbats";
#if (FOX_MINOR<=4)
        actualWeight = FONTWEIGHT_NORMAL;
        actualSlant = FONTSLANT_REGULAR;
#else
        actualWeight = FXFont::Normal;
        actualSlant = FXFont::Straight;
#endif
    }
    metrics = find_metrics(actualName.text());
}


// Does font have given character glyph?
FXbool FXPostscriptFont::hasChar(FXint ch) const
{
    return metrics->charwidth[ch & 0xff] != -1;
}


// Get first character glyph in font
#if (FOX_MINOR<=4)
FXint FXPostscriptFont::getMinChar() const
#else
FXwchar FXPostscriptFont::getMinChar() const
#endif
{
    int i;
    for (i=0; i<256; i++)
        if (metrics->charwidth[i] != -1) return i;
    return 256;
}


// Get last character glyph in font
#if (FOX_MINOR<=4)
FXint FXPostscriptFont::getMaxChar() const 
#else
FXwchar FXPostscriptFont::getMaxChar() const 
#endif
{
    int i;
    for (i=255; i>=0; i--)
        if (metrics->charwidth[i] != -1) return i;
    return 0;
}


// Get font leading [that is lead-ing as in Pb!]
FXint FXPostscriptFont::getFontLeading() const 
{
// I use 0.2 of the point-size as my leading. Point sizes are stored in
// decipoints. I return a value in millipoints.
    return (200/10)*actualSize;
}


// Get font line spacing [height+leading]
FXint FXPostscriptFont::getFontSpacing() const 
{
    return (1200/10)*actualSize;
}


// Left bearing
FXint FXPostscriptFont::leftBearing(FXchar ch) const 
{
    return (metrics->maxleftbearing*actualSize)/10;
}


// Right bearing
FXint FXPostscriptFont::rightBearing(FXchar ch) const 
{
    return (metrics->maxrightbearing*actualSize)/10;
}


// Is it a mono space font
FXbool FXPostscriptFont::isFontMono() const 
{
    return metrics->isfixed;
}


// Get font width
FXint FXPostscriptFont::getFontWidth() const 
{
    return (metrics->fontwidth*actualSize)/10;
}


// Get font height
FXint FXPostscriptFont::getFontHeight() const 
{
    return (1000/10)*actualSize;
}


// Get font ascent
FXint FXPostscriptFont::getFontAscent() const 
{
    return (metrics->ascent*actualSize)/10;
}


// Get font descent
FXint FXPostscriptFont::getFontDescent() const 
{
    return (metrics->descent*actualSize)/10;
}


// Text width
FXint FXPostscriptFont::getTextWidth(const FXchar *text, FXuint n) const 
{
    int w = 0;
    while (*text != 0 && (n--)!=0)  // stop on null char as well as at length
    {   w += metrics->charwidth[*text & 0xff];
        text++;
    }
    return (w*actualSize)/10;
}


// Text width
FXint FXPostscriptFont::getTextWidth(const FXString &text) const 
{
    return getTextWidth(text.text(), text.length());
}


// Text height
FXint FXPostscriptFont::getTextHeight(const FXchar *text, FXuint n) const 
{
    return (1000/10)*actualSize;    // use font height
}


// Text height
FXint FXPostscriptFont::getTextHeight(const FXString &text) const 
{
    return getTextHeight(text.text(), text.length());
}


/******************************************************************************/



// Clean up
FXPostscriptFont::~FXPostscriptFont()
{
    FXTRACE((100,"FXPostscriptFont::~FXPostscriptFont %p\n",this));
    destroy();
}

}

#endif // HAVE_LIBFOX

// end of FXPostscriptFont.cpp

