/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
  *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"

#include <math.h>

#include "psputil.hxx"
#include "glyphset.hxx"

#include "vcl/printergfx.hxx"
#include "vcl/fontmanager.hxx"
#include "vcl/helper.hxx"

#include "osl/thread.h"

#include "sal/alloca.h"

using namespace psp ;

namespace psp {
/*
 container for a font and its helper fonts:
 1st font is the font substitute e.g. helvetica substitutes arial on the printer
 2nd is the font itself
 3rd is a fallback font, usually a font with unicode glyph repertoir (e.g. andale)
 symbol fonts (adobe-fontspecific) may need special glyphmapping 
 (symbol page vc. latin page)
*/
class Font3
{
    private:

        #define Font3Size 3

        fontID  mpFont [Font3Size];
        bool	mbSymbol;	

    public:

        fontID	GetFont (int nIdx) const
                    { return nIdx < Font3Size ? mpFont[nIdx] : -1 ; }
        bool	IsSymbolFont () const
                    { return mbSymbol; }

        Font3 (const PrinterGfx &rGfx);
        ~Font3 () {}
};

Font3::Font3(const PrinterGfx &rGfx)
{
    mpFont[0] = rGfx.getFontSubstitute();
    mpFont[1] = rGfx.GetFontID(); 
    mpFont[2] = rGfx.getFallbackID();
    // mpFont[2] = rGfx.GetFontID();

       PrintFontManager &rMgr = PrintFontManager::get(); 
    mbSymbol = mpFont[1] != -1 ? 
                rMgr.getFontEncoding(mpFont[1]) == RTL_TEXTENCODING_SYMBOL : false; 
}

} // namespace psp

static int getVerticalDeltaAngle( sal_Unicode nChar )
{
    int nAngle = 0;
    if( ( nChar >= 0x1100 && nChar < 0x11fa ) ||
        ( nChar >= 0x3000 && nChar < 0xfb00 ) ||
        ( nChar >= 0xfe20 && nChar < 0xfe70 ) ||
        ( nChar >= 0xff00 && nChar < 0xff64 )
        )
    {
        /* #i52932# remember:
         nChar == 0x2010 || nChar == 0x2015
         nChar == 0x2016 || nChar == 0x2026
         
         are nAngle = 0 also, but already handled in the first if
        */
        if( ( nChar >= 0x3008 && nChar < 0x3019 && nChar != 0x3012 ) ||
            nChar == 0xff3b || nChar == 0xff3d ||
            (nChar >= 0xff6b && nChar < 0xff64 ) ||
            nChar == 0xffe3
            )
            nAngle = 0;
        else if( nChar == 0x30fc )
            nAngle = -900;
        else
            nAngle = 900;
    }
    return nAngle;
}

void
PrinterGfx::PSUploadPS1Font (sal_Int32 nFontID)
{
    std::list< sal_Int32 >::iterator aFont;
    // already in the document header ?
    for (aFont = maPS1Font.begin(); aFont != maPS1Font.end(); ++aFont )
        if( nFontID == *aFont )
            return;

    // no occurrenc yet, mark for download
    // add the fontid to the list
    maPS1Font.push_back (nFontID);
}

/*
 * implement text handling printer routines,
 */

sal_uInt16  
PrinterGfx::SetFont(
                    sal_Int32 nFontID,
                    sal_Int32 nHeight,
                    sal_Int32 nWidth,
                    sal_Int32 nAngle,
                    bool bVertical,
                    bool bArtItalic,
                    bool bArtBold
                    )
{
    // font and encoding will be set by drawText again immediately
    // before PSShowText
    mnFontID                          = nFontID;
    maVirtualStatus.maFont            = rtl::OString();
    maVirtualStatus.maEncoding        = RTL_TEXTENCODING_DONTKNOW;
    maVirtualStatus.mnTextHeight      = nHeight;
    maVirtualStatus.mnTextWidth       = nWidth;
    maVirtualStatus.mbArtItalic		  = bArtItalic;
    maVirtualStatus.mbArtBold		  = bArtBold;
    mnTextAngle                       = nAngle;
    mbTextVertical                    = bVertical;

    return 0;
}

sal_uInt16      
PrinterGfx::SetFallbackFont ( sal_Int32 nFontID )
{
    mnFallbackID = nFontID;
    return 0;
}

void PrinterGfx::drawGlyphs(
                            const Point& rPoint,
                            sal_uInt32* pGlyphIds,
                            sal_Unicode* pUnicodes,
                            sal_Int16 nLen,
                            sal_Int32* pDeltaArray
                            )
{

    // draw the string  
    // search for a glyph set matching the set font
    std::list< GlyphSet >::iterator aIter;
    for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); ++aIter)
        if ( ((*aIter).GetFontID()  == mnFontID) 
             && ((*aIter).IsVertical() == mbTextVertical))
        {
            (*aIter).DrawGlyphs (*this, rPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray);
            break;
        }
    
    // not found ? create a new one 
    if (aIter == maPS3Font.end())
    {
        maPS3Font.push_back (GlyphSet(mnFontID, mbTextVertical));
        maPS3Font.back().DrawGlyphs (*this, rPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray);
    }
}

void PrinterGfx::DrawGlyphs(
                            const Point& rPoint,
                            sal_GlyphId* pGlyphIds,
                            sal_Unicode* pUnicodes,
                            sal_Int16 nLen,
                            sal_Int32* pDeltaArray
                            )
{
    if( nLen <= 0 )
        return;

    if ( !mrFontMgr.isFontDownloadingAllowed( mnFontID ) )
    {
        LicenseWarning(rPoint, pUnicodes, nLen, pDeltaArray);
        return;
    }

    if( mrFontMgr.getFontType( mnFontID ) != fonttype::TrueType )
    {
        DrawText( rPoint, pUnicodes, nLen, pDeltaArray );
        return;
    }

    // move and rotate the user coordinate system
    // avoid the gsave/grestore for the simple cases since it allows
    // reuse of the current font if it hasn't changed
    sal_Int32 nCurrentTextAngle = mnTextAngle;
    Point aPoint( rPoint );

    if (nCurrentTextAngle != 0)
    {
        PSGSave ();
        PSTranslate (rPoint);
        PSRotate (nCurrentTextAngle);
        mnTextAngle = 0;
        aPoint = Point( 0, 0 );
    }

    if( mbTextVertical )
    {
        // vertical glyphs can have an additional rotation ... sigh.
        // so break up text in chunks of normal glyphs and print out
        // specially rotated glyphs extra
        sal_uInt32* pTempGlyphIds = (sal_uInt32*)alloca(sizeof(sal_Int32)*nLen);
        sal_Int32* pTempDelta = (sal_Int32*)alloca(sizeof(sal_Int32)*nLen);
        sal_Unicode* pTempUnicodes = (sal_Unicode*)alloca(sizeof(sal_Unicode)*nLen);
        sal_Int16 nTempLen = 0;
        sal_Int32 nTempFirstDelta = 0;
        Point aRotPoint;
        sal_Int32 nTextHeight = maVirtualStatus.mnTextHeight;
        sal_Int32 nTextWidth  = maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight;
        sal_Int32 nAscend = mrFontMgr.getFontAscend( mnFontID );
        sal_Int32 nDescend = mrFontMgr.getFontDescend( mnFontID );

        nDescend = nDescend * nTextHeight / 1000;
        nAscend = nAscend * nTextHeight / 1000;

        for( sal_Int16 i = 0; i < nLen; i++ )
        {
            const sal_GlyphId nRot = pGlyphIds[i] & GF_ROTMASK;
            if( nRot == GF_NONE )
            {
                pTempUnicodes[nTempLen]	= pUnicodes[i];
                pTempGlyphIds[nTempLen] = pGlyphIds[i];
                if( nTempLen > 0 )
                    pTempDelta[nTempLen-1]	= pDeltaArray[i-1]-nTempFirstDelta;
                else
                {
                    // the first element in pDeltaArray shows
                    // the offset of the second character
                    // so if the first glyph is normal
                    // then we do not need to move the delta indices
                    // else we have to move them down by one and
                    // recalculate aPoint and all deltas
                    if( i != 0 )
                        nTempFirstDelta = pDeltaArray[ i-1 ];
                }
                nTempLen++;
            }
            else
            {
                sal_Int32 nOffset = i > 0 ? pDeltaArray[i-1] : 0;
                sal_Int32 nRotAngle = 0;
                switch( nRot )
                {
                    case GF_ROTR:
                        nRotAngle = 2700;
                        aRotPoint = Point( -nAscend*nTextWidth/nTextHeight, -nDescend*nTextWidth/nTextHeight - nOffset );
                        break;
                    case GF_VERT:
                        nRotAngle = 1800;
                        aRotPoint = Point( -nOffset, (nAscend+nDescend) );
                        break;
                    case GF_ROTL:
                        nRotAngle = 900;
                        aRotPoint = Point( -nDescend*nTextWidth/nTextHeight, nOffset + nAscend*nTextWidth/nTextHeight );
                        break;
                }
                sal_GlyphId nRotGlyphId		= pGlyphIds[i];
                sal_Unicode nRotUnicode		= pUnicodes[i];
                sal_Int32 nRotDelta			= 0;

                // transform matrix to new individual direction
                PSGSave ();
                GraphicsStatus aSaveStatus = maVirtualStatus;
                if( nRot != 2 ) // switch font aspect
                {
                    maVirtualStatus.mnTextWidth = nTextHeight;
                    maVirtualStatus.mnTextHeight = nTextWidth;
                }
                if( aPoint.X() || aPoint.Y() )
                    PSTranslate( aPoint );
                PSRotate (nRotAngle);
                // draw the rotated glyph
                drawGlyphs( aRotPoint, &nRotGlyphId, &nRotUnicode, 1, &nRotDelta );

                // restore previous state
                maVirtualStatus = aSaveStatus;
                PSGRestore();
            }
        }

        pGlyphIds = pTempGlyphIds;
        pUnicodes = pTempUnicodes;
        pDeltaArray = pTempDelta;
        nLen = nTempLen;

        aPoint.X() += nTempFirstDelta;
    }

    if( nLen > 0 )
        drawGlyphs( aPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray );

    // restore the user coordinate system
    if (nCurrentTextAngle != 0)
    {
        PSGRestore ();
        mnTextAngle = nCurrentTextAngle;
    }
}

void
PrinterGfx::DrawText (
                      const Point& rPoint,
                      const sal_Unicode* pStr,
                      sal_Int16 nLen, 
                      const sal_Int32* pDeltaArray
                      )
{
    fontID nRestoreFont = mnFontID;

    // setup font[substitutes] and map the string into the symbol area in case of 
    // symbol font
    Font3 aFont(*this);
    sal_Unicode *pEffectiveStr;
    if ( aFont.IsSymbolFont() )
    {
        pEffectiveStr = (sal_Unicode*)alloca(nLen * sizeof(pStr[0]));
        for (int i = 0; i < nLen; i++)
            pEffectiveStr[i] = pStr[i] < 256 ? pStr[i] + 0xF000 : pStr[i];
    }
    else
    {
        pEffectiveStr = const_cast<sal_Unicode*>(pStr); 
    }

    fontID    *pFontMap   = (fontID*)    alloca(nLen * sizeof(fontID));
    sal_Int32 *pCharWidth = (sal_Int32*) alloca(nLen * sizeof(sal_Int32));

    for( int n = 0; n < nLen; n++ )
    {
        CharacterMetric aBBox;
        pFontMap[n]   = getCharMetric (aFont, pEffectiveStr[n], &aBBox);
        pCharWidth[n] = getCharWidth  (mbTextVertical, pEffectiveStr[n], &aBBox);
    }

    // setup a new delta array, use virtual resolution of 1000
    sal_Int32* pNewDeltaArray = (sal_Int32*)alloca( sizeof( sal_Int32 )*nLen );
    if ( pDeltaArray != 0)
    {
        for (int i = 0; i < nLen - 1; i++)
            pNewDeltaArray[i] = 1000 * pDeltaArray[i];
        pNewDeltaArray[nLen - 1] = 0;
    }
    else
    {
        pNewDeltaArray[0] = pCharWidth[0];
        for (int i = 1; i < nLen; i++)
            pNewDeltaArray[i] = pNewDeltaArray[i-1] + pCharWidth[i];
    }

    // move and rotate the user coordinate system
    // avoid the gsave/grestore for the simple cases since it allows
    // reuse of the current font if it hasn't changed
    sal_Int32 nCurrentTextAngle = mnTextAngle;
    sal_Int32 nCurrentPointX;
    sal_Int32 nCurrentPointY;

    if (nCurrentTextAngle != 0)
    {
        PSGSave ();
        PSTranslate (rPoint);
        PSRotate (nCurrentTextAngle);
        mnTextAngle = 0;
        
        nCurrentPointX = 0;
        nCurrentPointY = 0;
    }
    else
    {
        nCurrentPointX = rPoint.X();
        nCurrentPointY = rPoint.Y();
    }

    // draw the string  
    sal_Int32 nDelta = 0;
    for (int nTo = 0; nTo < nLen; )
    {
        int    nFrom = nTo;
        fontID nFont = pFontMap[ nFrom ];

        while ((nTo < nLen) && (nFont == pFontMap[nTo]))
        {
            pNewDeltaArray[ nTo ] = (sal_Int32)(((0.5 + pNewDeltaArray[ nTo ]) / 1000.0) - nDelta);
            nTo++ ;
        }

        SetFont( nFont,
                 maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth, 
                 mnTextAngle,
                 mbTextVertical,
                 maVirtualStatus.mbArtItalic,
                 maVirtualStatus.mbArtBold
                 );
        
        if (mbTextVertical)
        {
            drawVerticalizedText( 
                    Point(nCurrentPointX + nDelta, nCurrentPointY), 
                    pEffectiveStr + nFrom, nTo - nFrom, 
                    pNewDeltaArray + nFrom );
        }
        else
        {
            drawText( 
                    Point(nCurrentPointX + nDelta, nCurrentPointY), 
                    pEffectiveStr + nFrom, nTo - nFrom, 
                    pDeltaArray == NULL ? NULL : pNewDeltaArray + nFrom );
        }
        nDelta += pNewDeltaArray[ nTo - 1 ];
    }

    // restore the user coordinate system
    if (nCurrentTextAngle != 0)
    {
        PSGRestore ();
        mnTextAngle = nCurrentTextAngle;
    }

    // restore the original font settings
    SetFont( nRestoreFont,
             maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth, 
             mnTextAngle, mbTextVertical,
             maVirtualStatus.mbArtItalic,
             maVirtualStatus.mbArtBold
             );
}

void PrinterGfx::drawVerticalizedText(
                                      const Point& rPoint,
                                      const sal_Unicode* pStr,
                                      sal_Int16 nLen, 
                                      const sal_Int32* pDeltaArray
                                      )
{
    sal_Int32* pDelta = (sal_Int32*)alloca( nLen * sizeof(sal_Int32) );

    int nTextScale   = maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight;
    int nNormalAngle = mnTextAngle;
    int nDeltaAngle, nLastPos = 0;

    double fSin = sin( -2.0*M_PI*nNormalAngle/3600 );
    double fCos = cos( -2.0*M_PI*nNormalAngle/3600 );

    PrintFontManager &rMgr = PrintFontManager::get();
    PrintFontInfo aInfo;
    rMgr.getFontInfo( mnFontID, aInfo );

    bool* pGsubFlags = (bool*)alloca( nLen * sizeof(bool) );
    rMgr.hasVerticalSubstitutions( mnFontID, pStr, nLen, pGsubFlags );

    Point aPoint( rPoint );
    for( int i = 0; i < nLen; )
    {
        while( ( nDeltaAngle = getVerticalDeltaAngle( pStr[i] ) ) == 0 && i < nLen )
            i++;
        if( i <= nLen && i > nLastPos )
        {
            for( int n = nLastPos; n < i; n++ )
                pDelta[n] = pDeltaArray[n] - (aPoint.X() - rPoint.X() );
            
            SetFont( mnFontID,
                     maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth,
                     nNormalAngle, mbTextVertical,
                     maVirtualStatus.mbArtItalic,
                     maVirtualStatus.mbArtBold );
            drawText( aPoint, pStr + nLastPos, i - nLastPos, pDelta + nLastPos );
            
            aPoint.X() = (sal_Int32)(rPoint.X() + ((double)pDeltaArray[i-1] * fCos));
            aPoint.Y() = (sal_Int32)(rPoint.Y() + ((double)pDeltaArray[i-1] * fSin));
        }
        if( i < nLen )
        {
            int nOldWidth	= maVirtualStatus.mnTextWidth;
            int nOldHeight	= maVirtualStatus.mnTextHeight;
            SetFont( mnFontID,
                     nTextScale,
                     maVirtualStatus.mnTextHeight,
                     nNormalAngle + nDeltaAngle, 
                     mbTextVertical,
                     maVirtualStatus.mbArtItalic,
                     maVirtualStatus.mbArtBold );

            double nA = nTextScale * aInfo.m_nAscend / 1000.0;
            double nD = nTextScale * aInfo.m_nDescend / 1000.0;
            double fStretch = (double)maVirtualStatus.mnTextWidth / maVirtualStatus.mnTextHeight; 
            if( !pGsubFlags[i] )
                nD *= fStretch;

            Point aPos( aPoint );
            switch( nDeltaAngle )
            {
                case +900:
                    aPos.X() += (sal_Int32)(+nA * fCos + nD * fSin);
                    aPos.Y() += (sal_Int32)(-nA * fSin + nD * fCos);
                    break;
                case -900:
                    aPos.X() += (sal_Int32)(+nA * fSin + nD * fCos);
                    aPos.Y() += (sal_Int32)(-(nTextScale*fStretch - nD) * fCos);
                    break;
            }
            drawText( aPos, pStr+i, 1, NULL );
            if( i < nLen-1 && pDeltaArray )
            {
                aPoint.X() = (sal_Int32)(rPoint.X() + ((double)pDeltaArray[i] * fCos));
                aPoint.Y() = (sal_Int32)(rPoint.Y() + ((double)pDeltaArray[i] * fSin));
            }

            // swap text width/height again
            SetFont( mnFontID,
                     nOldHeight,
                     nOldWidth,
                     nNormalAngle, 
                     mbTextVertical,
                     maVirtualStatus.mbArtItalic,
                     maVirtualStatus.mbArtBold );
        }
        i++;
        nLastPos = i;
    }
    mnTextAngle = nNormalAngle;
}

void        
PrinterGfx::LicenseWarning(const Point& rPoint, const sal_Unicode* pStr,
                           sal_Int16 nLen, const sal_Int32* pDeltaArray)
{
    // treat it like a builtin font in case a user has that font also in the 
    // printer. This is not so unlikely as it may seem; no print embedding 
    // licensed fonts are often used (or so they say) in companies:
    // they are installed on displays and printers, but get not embedded in 
    // they are installed on displays and printers, but get not embedded in 
    // print files or documents because they are not licensed for use outside 
    // the company.
    rtl::OString aMessage( "The font " );
    aMessage += rtl::OUStringToOString( mrFontMgr.getPSName(mnFontID), 
            RTL_TEXTENCODING_ASCII_US );
    aMessage += " could not be downloaded\nbecause its license does not allow for that";
    PSComment( aMessage.getStr() );

    rtl::OString aFontName = rtl::OUStringToOString( 
            mrFontMgr.getPSName(mnFontID), 
            RTL_TEXTENCODING_ASCII_US);
    PSSetFont (aFontName, RTL_TEXTENCODING_ISO_8859_1);

    sal_Size  nSize    = 4 * nLen;
    sal_uChar* pBuffer = (sal_uChar*)alloca (nSize* sizeof(sal_uChar));

    ConverterFactory* pCvt = GetConverterFactory ();
    nSize = pCvt->Convert (pStr, nLen, pBuffer, nSize, RTL_TEXTENCODING_ISO_8859_1);

    PSMoveTo (rPoint);
    PSShowText (pBuffer, nLen, nSize, pDeltaArray);
}

void        
PrinterGfx::drawText(
                     const Point& rPoint,
                     const sal_Unicode* pStr,
                     sal_Int16 nLen, 
                     const sal_Int32* pDeltaArray
                     )
{
    if (!(nLen > 0))
        return;

    fonttype::type   eType          = mrFontMgr.getFontType (mnFontID);

    if (eType == fonttype::Type1)
        PSUploadPS1Font (mnFontID);

    if (   eType == fonttype::TrueType 
        && !mrFontMgr.isFontDownloadingAllowed(mnFontID))
    {
        LicenseWarning(rPoint, pStr, nLen, pDeltaArray);
        return;
    }

    if( mrFontMgr.getUseOnlyFontEncoding( mnFontID ) )
    {
        GlyphSet aGSet( mnFontID, mbTextVertical );
        aGSet.DrawText( *this, rPoint, pStr, nLen, pDeltaArray );
        return;
    }

    // search for a glyph set matching the set font
    std::list< GlyphSet >::iterator aIter;
    for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); ++aIter)
        if (   ((*aIter).GetFontID()  == mnFontID) 
            && ((*aIter).IsVertical() == mbTextVertical))
        {
            (*aIter).DrawText (*this, rPoint, pStr, nLen, pDeltaArray);
            break;
        }

    // not found ? create a new one 
    if (aIter == maPS3Font.end())
    {
        maPS3Font.push_back (GlyphSet(mnFontID, mbTextVertical));
        maPS3Font.back().DrawText (*this, rPoint, pStr, nLen, pDeltaArray);
    }
}

int
PrinterGfx::getCharWidth (sal_Bool b_vert, sal_Unicode n_char, CharacterMetric *p_bbox)
{
    b_vert = b_vert && (getVerticalDeltaAngle(n_char) != 0);
    int w = b_vert ? p_bbox->height : p_bbox->width;
    w *= maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight; 
    return w;
}

fontID 
PrinterGfx::getCharMetric (const Font3 &rFont, sal_Unicode n_char, CharacterMetric *p_bbox)
{
    p_bbox->width  = -1;
    p_bbox->height = -1;

    for (fontID n = 0; n < 3; n++)
    {
        fontID n_font = rFont.GetFont(n);
        if (n_font != -1)
        {
            if( mbStrictSO52Compatibility )
            {
                fonttype::type eType = mrFontMgr.getFontType( n_font );
                if( (eType == fonttype::Builtin || eType == fonttype::Type1) )
                {
                    // note: any character exchanged here MUST also be changed
                    // in the compatibility ISO encoding vector in the prolog
                    // in printerjob.cxx
                    sal_Unicode aRepl = 0;
                    if( n_char == 0x2d )
                        aRepl = 0x2212;
                    else if( n_char == 0x27 )
                        aRepl = 0x2019;
                    /*
                    additional characters that may need backwards compatibility:
                    ISO5589   StdEnc   Unicode    suggested n_char -> aRepl
                    0264      0302     0x00B4     0x00B4 (acute) -> 0x2019 (quiteright)
                    0246      -        0x00A6     0x00A6 (brokenbar) -> 0x007C (bar)
                    0225      0267     0x0095     0x0095 () -> 0x2022 (bullet)
                    0140      0301     0x0060     0x0060 (grave) -> ?
                    */
                    if( aRepl )
                    {
                        mrFontMgr.getMetrics( n_font, aRepl, aRepl, p_bbox );
                        if (p_bbox->width >= 0 && p_bbox->height >= 0)
                            return n_font;
                    }
                }
            }
            mrFontMgr.getMetrics( n_font, n_char, n_char, p_bbox );
        }
        if (p_bbox->width >= 0 && p_bbox->height >= 0)
            return n_font;
    }
    if (n_char != '?')
        return getCharMetric (rFont, '?', p_bbox);

    return rFont.GetFont(0) != -1 ? rFont.GetFont(0) : rFont.GetFont(1);
}

fontID
PrinterGfx::getFontSubstitute () const
{
    if( mpFontSubstitutes )
    {
        ::boost::unordered_map< fontID, fontID >::const_iterator it = 
              mpFontSubstitutes->find( mnFontID );
        if( it != mpFontSubstitutes->end() )
            return it->second;
    }

    return -1;
}

sal_Int32
PrinterGfx::GetCharWidth (sal_Unicode nFrom, sal_Unicode nTo, long *pWidthArray)
{
    Font3 aFont(*this); 
    if (aFont.IsSymbolFont() && (nFrom < 256) && (nTo < 256))
    {
        nFrom += 0xF000;
        nTo   += 0xF000;
    }	

    for( int n = 0; n < (nTo - nFrom + 1); n++ )
    {
        CharacterMetric aBBox;
        getCharMetric (aFont, n + nFrom, &aBBox);
        pWidthArray[n] = getCharWidth (mbTextVertical, n + nFrom, &aBBox);
    }

    // returned metrics have postscript precision
    return 1000;
}

const ::std::list< KernPair >& PrinterGfx::getKernPairs( bool bVertical ) const
{
    /*
     *  Note: this is only a 80% solution: if a font is only
     *  partially substituted in a string due to missing glyphs
     *  the results may not be perfect; the more so the more the
     *  substitution differs from the original metricwise. But
     *  vcl only asks for KernPairs for each font once and NOT
     *  in a string context this is the best we can do.
     *  In future the kerning should be done on a per string basis.
     */
    fontID nFont = mnFontID;
    if( mpFontSubstitutes )
    {
        ::boost::unordered_map< fontID, fontID >::const_iterator it =
              mpFontSubstitutes->find( mnFontID );
        if( it != mpFontSubstitutes->end() )
            nFont = it->second;
    }
    return mrFontMgr.getKernPairs( nFont, bVertical );
}

/*
 * advanced glyph handling
 */

sal_Bool    
PrinterGfx::GetGlyphBoundRect (sal_Unicode /*c*/, Rectangle& /*rOutRect*/)
{
    return 0;
}

sal_uInt32  
PrinterGfx::GetGlyphOutline (sal_Unicode /*c*/, 
                             sal_uInt16 **/*ppPolySizes*/, Point **/*ppPoints*/, sal_uInt8 **/*ppFlags*/)
{
    return 0;
}

/*
 * spool the converted truetype fonts to the page header after the page body is 
 * complete
 * for Type1 fonts spool additional reencoding vectors that are necessary to access the
 * whole font
 */

void
PrinterGfx::OnEndPage ()
{
}

void
PrinterGfx::OnEndJob ()
{
    maPS3Font.clear();
    maPS1Font.clear();
}

void
PrinterGfx::writeResources( osl::File* pFile, std::list< rtl::OString >& rSuppliedFonts, std::list< rtl::OString >& rNeededFonts )
{
    // write all type 1 fonts
    std::list< sal_Int32 >::iterator aFont;
    // already in the document header ?
    for (aFont = maPS1Font.begin(); aFont != maPS1Font.end(); ++aFont)
    {
        const rtl::OString& rSysPath (mrFontMgr.getFontFileSysPath(*aFont) );
        rtl::OUString aUNCPath;
        osl::File::getFileURLFromSystemPath (OStringToOUString (rSysPath, osl_getThreadTextEncoding()), aUNCPath);
        osl::File aFontFile (aUNCPath);

        // provide the pfb or pfa font as a (pfa-)font resource
        rtl::OString aPostScriptName =
            rtl::OUStringToOString ( mrFontMgr.getPSName(*aFont),
                                     RTL_TEXTENCODING_ASCII_US );

        WritePS (pFile, "%%BeginResource: font ");
        WritePS (pFile, aPostScriptName.getStr()); 
        WritePS (pFile, "\n");

        osl::File::RC nError = aFontFile.open(osl_File_OpenFlag_Read);
        if (nError == osl::File::E_None)
        {
            convertPfbToPfa (aFontFile, *pFile);
            aFontFile.close ();

            pFile->setPos(osl_Pos_Current, -1);
            char lastchar = '\n';
            sal_uInt64 uBytes(1);
            pFile->read((void *)(&lastchar), uBytes, uBytes);
            if (lastchar != '\n')
                WritePS (pFile, "\n");
        }
        WritePS (pFile, "%%EndResource\n");
        rSuppliedFonts.push_back( aPostScriptName );
    }

    // write glyphsets and reencodings
    std::list< GlyphSet >::iterator aIter;
    for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); ++aIter)
    {
        if (aIter->GetFontType() == fonttype::TrueType)
        {
            aIter->PSUploadFont (*pFile, *this, mbUploadPS42Fonts ? true : false, rSuppliedFonts );
        }
        else
        // (   aIter->GetFontType() == fonttype::Type1 
        //  || aIter->GetFontType() == fonttype::Builtin )
        {
            aIter->PSUploadEncoding (pFile, *this);
            if( aIter->GetFontType() == fonttype::Builtin )
                rNeededFonts.push_back(
                      rtl::OUStringToOString(
                           mrFontMgr.getPSName( aIter->GetFontID() ),
                           RTL_TEXTENCODING_ASCII_US ) );
        }
    }
}

bool PrinterGfx::getStrictSO52Compatibility() const
{
    return mbStrictSO52Compatibility;
}

void PrinterGfx::setStrictSO52Compatibility( bool bCompat)
{
    mbStrictSO52Compatibility = bCompat;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
