Subversion Repositories Scribus

Rev

Rev 8972 | Blame | Compare with Previous | Last modification | View Log | RSS feed



#include "scribusapi.h"
#include "fonts/scface.h"
#include "text/storytext.h"

ScFace::ScFaceData::ScFaceData() :
        refs(0),
        usage(0),
        scName(""),
        fontFile("(None)"),
        faceIndex(-1),
        psName(""),
        family(""),
        style(""),
        variant(""),
        forDocument(""),
        status(ScFace::NULLFACE),
        typeCode(ScFace::UNKNOWN_TYPE),
        formatCode(ScFace::UNKNOWN_FORMAT),
        usable(false),
        embedPs(false),
        subset(false),
        isStroked(false),
        isFixedPitch(false),
        hasNames(false),
        maxGlyph(0),
        cachedStatus(ScFace::UNKNOWN)
{
}

double ScFace::ScFaceData::glyphKerning(uint /*gl1*/, uint /*gl2*/, double /*sz*/) const
{
        return 0.0;
}


bool ScFace::ScFaceData::glyphNames(QMap<uint, std::pair<QChar, QString> >& /*gList*/) const
{
        return false;
}


QMap<QString,QString> ScFace::ScFaceData::fontDictionary(double /*sz*/) const
{
        return QMap<QString, QString>();
}


GlyphMetrics ScFace::ScFaceData::glyphBBox(uint gl, double sz) const
{
        GlyphMetrics res;
        if (gl == 0 || gl >= CONTROL_GLYPHS)
        {       res.width   = glyphWidth(gl, sz);
                res.ascent  = (gl == 0? sz : 0);
                res.descent = 0;
                return res;
        }
        else if (! m_glyphWidth.contains(gl)) {
                loadGlyph(gl);
        }                      
        const struct GlyphData & data(m_glyphOutline[gl]);
        res.width = data.bbox_width * sz;
        res.ascent = data.bbox_ascent * sz;
        res.descent = data.bbox_descent * sz;  
        return res;
}


double ScFace::ScFaceData::glyphWidth(uint gl, double size) const
{
        if (gl >= CONTROL_GLYPHS)
                return 0.0;
        else if (gl == 0)
                return size;
        else if (! m_glyphWidth.contains(gl)) {
                loadGlyph(gl);
        }              
        return m_glyphWidth[gl] * size;
}


FPointArray ScFace::ScFaceData::glyphOutline(uint gl, double sz) const
{
        if (gl >= CONTROL_GLYPHS)
                return FPointArray();
        else if (gl == 0) {
                sz *= 10;
                FPointArray sq;
                sq.addQuadPoint(0,0,0,0,sz,0,sz,0);
                sq.addQuadPoint(sz,0,sz,0,sz,sz,sz,sz);
                sq.addQuadPoint(sz,sz,sz,sz,0,sz,0,sz);
                sq.addQuadPoint(0,sz,0,sz,0,0,0,0);
                return sq;
        }
        else if (! m_glyphWidth.contains(gl)) {
                loadGlyph(gl);
        }                      
        FPointArray res = m_glyphOutline[gl].Outlines.copy();
        if (sz != 1.0)
                res.scale(sz, sz);
        return res;
}


FPoint ScFace::ScFaceData::glyphOrigin(uint gl, double sz) const
{
        if (gl == 0 || gl >= CONTROL_GLYPHS)
                return FPoint(0,0);
        else if (! m_glyphWidth.contains(gl)) {
                loadGlyph(gl);
        }                      
        const struct GlyphData & res(m_glyphOutline[gl]);
        return FPoint(res.x, res.y) * sz;
}


/*****
   ScFace lifecycle:  unchecked -> loaded -> glyphs checked
                               |         \-> broken glyphs
                                                           \-> broken
   usable() == ! broken
   embeddable() == glyphs_checked
   
   canRender(unicode) -> CharMap cache? -> loadChar/Glyph -> !broken
   Glyphs:  width    status
            -1000    unkown
            -2000    broken
            >= 0     ok, outline valid
   CharMap:  unicode -> glyph index
             uint[256][256]
   unicode ignores: < 32, ...
   unicode emulate: spaces, hyphen, ligatures?, diacritics?
 *****/


ScFace::ScFace() :  replacedName(), replacedInDoc()
{
        m = new ScFaceData();
        m->refs = 1;
        m->usage = 0;
}


ScFace::ScFace(ScFaceData* data) : replacedName(), replacedInDoc()
{
        m = data;
        ++(m->refs);
        m->cachedStatus = ScFace::UNKNOWN;
}

ScFace::ScFace(const ScFace& other) : replacedName(other.replacedName), replacedInDoc(other.replacedInDoc)
{
        m = other.m;
        ++(m->refs);
}

ScFace::~ScFace()
{
        if ( m && --(m->refs) == 0 ) {
                m->unload();
                delete m;
                m = 0;
        }
}


ScFace& ScFace::operator=(const ScFace& other)
{
        if (m != other.m)
        {
                if (other.m)
                        ++(other.m->refs);
                if ( m && --(m->refs) == 0 ) {
                        m->unload();
                        delete m;
                }
                m = other.m;
        }
        replacedName = other.replacedName;
        return *this;
}


/** two ScFaces are equal if they either are both NULLFACEs or they
agree on family, style, variant and fontpath
*/

bool ScFace::operator==(const ScFace& other) const
{
        return replacedName == other.replacedName &&
                ( (isNone() && other.isNone() )
                         || (m == other.m)
                         || (m->family == other.m->family
                                 && m->style == other.m->style
                                 && m->variant == other.m->variant
                                 && m->fontFile == other.m->fontFile
                                 && m-> faceIndex == other.m->faceIndex) );
}


const ScFace& ScFace::none()
{
        static ScFace NONE;
        return NONE;
}


void ScFace::increaseUsage() const
{
        m->usage++;
}


void ScFace::decreaseUsage() const
{
        if (m->usage == 1)
                unload();
        m->usage--;
}


void ScFace::unload() const
{
        if (m->status >= ScFace::LOADED && usable()) {
                m->unload();
        }
        // clear caches
        m->m_glyphWidth.clear();
        m->m_glyphOutline.clear();
        m->m_cMap.clear();
        m->status = ScFace::UNKNOWN;
}


uint ScFace::emulateGlyph(QChar ch) const
{
        if (ch == SpecialChars::LINEBREAK || ch == SpecialChars::PARSEP
                || ch == SpecialChars::FRAMEBREAK || ch == SpecialChars::COLBREAK
                || ch == SpecialChars::TAB || ch == SpecialChars::SHYPHEN
                 || ch == SpecialChars::ZWSPACE || ch == SpecialChars::ZWNBSPACE)
                return CONTROL_GLYPHS + ch.unicode();
        else if (ch == SpecialChars::NBSPACE)
                return  m->char2CMap(QChar(' '));
        else if(ch == SpecialChars::NBHYPHEN)
                return  m->char2CMap(QChar('-'));
        else
                return 0;
}


uint ScFace::char2CMap(QChar ch) const
{
        if (m->status == ScFace::UNKNOWN) {
                m->load();
        }
       
        if (ch == SpecialChars::SHYPHEN)
                return emulateGlyph(ch);

        uint gl = m->char2CMap(ch);
       
        if (gl == 0)
                return emulateGlyph(ch);
        else
                return gl;
}


bool ScFace::canRender(QChar ch) const
{
        if (!usable())
                return false;
        else {
                uint gl = char2CMap(ch);    //  calls load()
                if (gl >= CONTROL_GLYPHS)   //  those are always empty
                        return true;
                else if (gl != 0) {
                        m->loadGlyph(gl);
                        return ! m->m_glyphOutline[gl].broken;
                }
                else  {
                        return false;
                }
        }
}


double ScFace::charWidth(QChar ch, double size, QChar ch2) const
{
        if (!canRender(ch)) // calls loadGlyph()
                return size;
        else if (ch.unicode() == 28 || ch.unicode() == 13 || ch.unicode() == 9)
                return ch.unicode() == 9 ? 1.0 : 0.0;
        else {
                uint gl1 = char2CMap(ch);
                uint gl2 = char2CMap(ch2);
                double width = glyphWidth(gl1, size);
                if (gl2 != 0)
                        width += glyphKerning(gl1, gl2, size);
//              qDebug(QString("scface::glyphkerning: %1_%2 = %3 (%4, %5)").arg(ch).arg(ch2).arg(glyphKerning(gl1, gl2,size)).arg(gl1).arg(gl2));
                return width;
        }
}


bool ScFace::EmbedFont(QString &str)
{
        if (m->status == ScFace::UNKNOWN) {
                m->load();
        }
        return m->EmbedFont(str);
}


bool ScFace::glyphNames(QMap<uint, std::pair<QChar, QString> >& gList)
{
        if (m->status == ScFace::UNKNOWN) {
                m->load();
        }
        return m->glyphNames(gList);
}


void ScFace::RawData(QByteArray & bb)
{
        if (m->status == ScFace::UNKNOWN) {
                m->load();
        }
        m->RawData(bb);
}

void ScFace::checkAllGlyphs()
{
        if (m->status == ScFace::UNKNOWN) {
                m->load();
        }
        if (m->status != ScFace::LOADED) {
                return;
        }
        for (uint gl=0; gl <= m->maxGlyph; ++gl) {
                if (! m->m_glyphWidth.contains(gl)) {
                        m->loadGlyph(gl);
                        m->m_glyphWidth.remove(gl);
                        m->m_glyphOutline.remove(gl);
                }
        }
}