diff options
| author | craig <craig@11d20701-8431-0410-a711-e3c959e3b870> | 2012-01-01 11:40:09 +0000 |
|---|---|---|
| committer | craig <craig@11d20701-8431-0410-a711-e3c959e3b870> | 2012-01-01 11:40:09 +0000 |
| commit | 7ed83b6c6666eb8b6b104c211ae7e52907350372 (patch) | |
| tree | 4430b556abac0ad660a0aacf1887d77f85d8be02 /scribus/fonts | |
| download | scribus-7ed83b6c6666eb8b6b104c211ae7e52907350372.tar.gz scribus-7ed83b6c6666eb8b6b104c211ae7e52907350372.tar.xz scribus-7ed83b6c6666eb8b6b104c211ae7e52907350372.zip | |
Branch 1.3.5 tree to 1.4.x tree, goodbye 1.3.x
git-svn-id: svn://scribus.net/branches/Version14x/Scribus@17163 11d20701-8431-0410-a711-e3c959e3b870
Diffstat (limited to 'scribus/fonts')
| -rw-r--r-- | scribus/fonts/CMakeLists.txt | 22 | ||||
| -rw-r--r-- | scribus/fonts/ftface.cpp | 332 | ||||
| -rw-r--r-- | scribus/fonts/ftface.h | 121 | ||||
| -rw-r--r-- | scribus/fonts/scface.cpp | 460 | ||||
| -rw-r--r-- | scribus/fonts/scface.h | 391 | ||||
| -rw-r--r-- | scribus/fonts/scface_ps.cpp | 114 | ||||
| -rw-r--r-- | scribus/fonts/scface_ps.h | 222 | ||||
| -rw-r--r-- | scribus/fonts/scface_ttf.cpp | 662 | ||||
| -rw-r--r-- | scribus/fonts/scface_ttf.h | 105 | ||||
| -rw-r--r-- | scribus/fonts/scfontmetrics.cpp | 623 | ||||
| -rw-r--r-- | scribus/fonts/scfontmetrics.h | 37 |
11 files changed, 3089 insertions, 0 deletions
diff --git a/scribus/fonts/CMakeLists.txt b/scribus/fonts/CMakeLists.txt new file mode 100644 index 0000000..8154e64 --- /dev/null +++ b/scribus/fonts/CMakeLists.txt @@ -0,0 +1,22 @@ +INCLUDE_DIRECTORIES( +${CMAKE_SOURCE_DIR} +${CMAKE_SOURCE_DIR}/scribus +${FREETYPE_INCLUDE_DIRS} +) + + +SET(SCRIBUS_FONTS_LIB_SOURCES + scface.cpp + ftface.cpp + scface_ps.cpp + scface_ttf.cpp + scfontmetrics.cpp +) +SET(SCRIBUS_FONTS_LIB "scribus_fonts_lib") +ADD_LIBRARY(${SCRIBUS_FONTS_LIB} STATIC ${SCRIBUS_FONTS_LIB_SOURCES}) +# This is a convenience library that for linkage purposes is part of Scribus's +# main API. +SET_TARGET_PROPERTIES(${SCRIBUS_FONTS_LIB} + PROPERTIES + COMPILE_FLAGS -DCOMPILE_SCRIBUS_MAIN_APP + ) diff --git a/scribus/fonts/ftface.cpp b/scribus/fonts/ftface.cpp new file mode 100644 index 0000000..6889312 --- /dev/null +++ b/scribus/fonts/ftface.cpp @@ -0,0 +1,332 @@ +/* +For general Scribus (>=1.3.2) copyright and licensing information please refer +to the COPYING file provided with the program. Following this notice may exist +a copyright and/or license notice that predates the release of Scribus 1.3.2 +for which a new license (GPL+exception) is in place. +*/ + +#include "fonts/ftface.h" + +#include FT_OUTLINE_H +#include FT_GLYPH_H + +#include <QObject> +#include <QFile> + +#include "scfonts.h" +#include "util.h" +#include "fonts/scfontmetrics.h" + +// static: +FT_Library FtFace::library = NULL; + +/***** + 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? + *****/ + +FtFace::FtFace(QString fam, QString sty, QString vari, QString scname, + QString psname, QString path, int face) +: ScFaceData(), m_face(NULL) +{ + family = fam; + style = sty; + variant = vari; + scName = scname; + psName = psname; + fontFile = path; + faceIndex = face; + if (!library) { + if (FT_Init_FreeType( &library )) + sDebug(QObject::tr("Freetype2 library not available")); + } +} + + +FtFace::~FtFace() { + unload(); +} + + +FT_Face FtFace::ftFace() const { + if (!m_face) { + if (FT_New_Face( library, QFile::encodeName(fontFile), faceIndex, & m_face )) { + status = ScFace::BROKEN; + m_face = NULL; + sDebug(QObject::tr("Font %1(%2) is broken").arg(fontFile).arg(faceIndex)); + } + else { + load(); + } + } + return m_face; +} + +void FtFace::load() const +{ + ScFaceData::load(); + + if (!m_face) { + if (FT_New_Face( library, QFile::encodeName(fontFile), faceIndex, & m_face )) { + status = ScFace::BROKEN; + m_face = NULL; + sDebug(QObject::tr("Font %1(%2) is broken").arg(fontFile).arg(faceIndex)); + return; + } + } + + const_cast<FtFace*>(this)->isStroked = false; + m_encoding = 0; + + m_uniEM = static_cast<qreal>(m_face->units_per_EM); + + m_descent = m_face->descender / m_uniEM; + m_ascent = m_face->ascender / m_uniEM; + m_height = m_face->height / m_uniEM; + +/* Temporary fix for the broken "Dutch Initials" font */ + if ((m_ascent == 0) && (m_descent == 0)) + { + m_ascent = (m_face->bbox.yMax - m_face->bbox.yMin) / m_uniEM; + m_height = m_ascent; + } + + m_xHeight = m_height; + m_capHeight = m_height; + m_maxAdvanceWidth = m_face->max_advance_width / m_uniEM; + m_underlinePos = m_face->underline_position / m_uniEM; + m_strikeoutPos = m_ascent / 3; + m_strokeWidth = m_face->underline_thickness / m_uniEM; + const_cast<FtFace*>(this)->isFixedPitch = m_face->face_flags & 4; + Ascent = QString::number(m_face->ascender); + CapHeight = QString::number(m_face->height); + Descender = QString::number(m_face->descender); + FontBBox = QString::number(m_face->bbox.xMin)+" "+QString::number(m_face->bbox.yMin)+" "+QString::number(m_face->bbox.xMax)+" "+QString::number(m_face->bbox.yMax); + ItalicAngle = "0"; + +//FIXME: FT_Set_Charmap(m_face, m_face->charmaps[m_encoding]); + setBestEncoding(m_face); + + FT_UInt gindex = 0; + FT_ULong charcode = FT_Get_First_Char( m_face, &gindex ); + int goodGlyph = 0; + int invalidGlyph = 0; + bool error; + + while ( gindex != 0 ) + { + error = FT_Load_Glyph( m_face, gindex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ); + if (error) + { + ++invalidGlyph; + sDebug(QObject::tr("Font %1 has broken glyph %2 (charcode %3)").arg(fontFile).arg(gindex).arg(charcode)); + charcode = FT_Get_Next_Char( m_face, charcode, &gindex ); + continue; + } + + if (gindex > maxGlyph) + const_cast<FtFace*>(this)->maxGlyph = gindex; + + ++goodGlyph; + if (m_face->glyph->format == FT_GLYPH_FORMAT_PLOTTER) + const_cast<FtFace*>(this)->isStroked = true; + charcode = FT_Get_Next_Char( m_face, charcode, &gindex ); + } + if (invalidGlyph > 0) { + status = ScFace::BROKENGLYPHS; + } +} + + +void FtFace::unload() const +{ + if (m_face) { + FT_Done_Face( m_face ); + m_face = NULL; + } + // clear caches + ScFaceData::unload(); +} + + +uint FtFace::char2CMap(QChar ch) const +{ + // FIXME use cMap cache + FT_Face face = ftFace(); + uint gl = FT_Get_Char_Index(face, ch.unicode()); + return gl; +} + + +void FtFace::loadGlyph(uint gl) const +{ + if (m_glyphWidth.contains(gl)) + return; + + ScFace::GlyphData GRec; + FT_Face face = ftFace(); + if (FT_Load_Glyph( face, gl, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP )) + { + sDebug(QObject::tr("Font %1 has broken glyph %2").arg(fontFile).arg(gl)); + m_glyphWidth[gl] = 1; + } + else { + qreal ww = qreal(face->glyph->metrics.horiAdvance) / m_uniEM; + qreal w = (face->glyph->metrics.width + qAbs(qreal(face->glyph->metrics.horiBearingX))) / m_uniEM; + GRec.bbox_width = qMax(w, ww); + qreal height = qreal(face->glyph->metrics.height) / m_uniEM; + GRec.bbox_ascent = qreal(face->glyph->metrics.horiBearingY) / m_uniEM; + GRec.bbox_descent = height - GRec.bbox_ascent; +// qDebug() << QString("glyphmetrics %1: EM %2 bearing (%3,%4) size (%5,%6) advance %7 bbox (%8,%9)") +// .arg(gl).arg(m_uniEM).arg(face->glyph->metrics.horiBearingX).arg(face->glyph->metrics.horiBearingY) +// .arg(face->glyph->metrics.width).arg(face->glyph->metrics.height).arg(face->glyph->metrics.horiAdvance) +// .arg(w).arg(height); + + qreal x, y; + bool error = false; + error = FT_Set_Char_Size( face, 0, 10, 72, 72 ); + if (error) + m_glyphWidth[gl] = 1; + FPointArray outlines = traceGlyph(face, gl, 10, &x, &y, &error); + if (!error) + { + m_glyphWidth[gl] = ww; + GRec.Outlines = outlines; + GRec.x = x; + GRec.y = y; + GRec.broken = false; + } + else { + m_glyphWidth[gl] = 1; + } + } + m_glyphOutline[gl] = GRec; + if (GRec.broken && status < ScFace::BROKENGLYPHS) + status = ScFace::BROKENGLYPHS; +} + + +qreal FtFace::glyphKerning(uint gl, uint gl2, qreal size) const +{ + FT_Vector delta; + FT_Face face = ftFace(); + qreal result = 0; + /**** + Ok, this looks like a regression between Freetype 2.1.9 -> 2.1.10. + Ignoring the value of FT_HAS_KERNING for now -- AV + ****/ + if (true || FT_HAS_KERNING(face) ) + { + FT_Error error = FT_Get_Kerning(face, gl, gl2, FT_KERNING_UNSCALED, &delta); + if (error) { + sDebug(QString("Error %2 when accessing kerning pair for font %1").arg(scName).arg(error)); + } + else { + result = delta.x / m_uniEM * size; + } + } + else { + sDebug(QString("Font %1 has no kerning pairs (according to Freetype)").arg(scName)); + } + return result; +} + +/* +GlyphMetrics FtFace::glyphBBox (uint gl, qreal sz) const +{ + FT_Face face = ftFace(); + GlyphMetrics result; + FT_Error error = FT_Load_Glyph( face, gl, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ); + if (!error) { + qreal w = (face->glyph->metrics.width + QABS((qreal)face->glyph->metrics.horiBearingX)) / m_uniEM * sz; + result.width = qMax(w, face->glyph->metrics.horiAdvance / m_uniEM * sz); + qreal height = face->glyph->metrics.height / m_uniEM * sz; + result.ascent = face->glyph->metrics.horiBearingY / m_uniEM * sz; + result.descent = height - result.ascent; + } + else { + result.width = result.ascent = sz; + result.descent = 0; + } + return result; +} +*/ + + +/// copied from Freetype's FT_Stream_ReadAt() +FT_Error ftIOFunc( FT_Stream stream, unsigned long pos, unsigned char* buffer, unsigned long count) +{ + FT_Error error = FT_Err_Ok; + FT_ULong read_bytes; + + if ( pos >= stream->size ) + { + qDebug( "ftIOFunc: invalid i/o; pos = 0x%lx, size = 0x%lx\n", pos, stream->size ); + + return FT_Err_Invalid_Stream_Operation; + } + + if ( stream->read ) + read_bytes = stream->read( stream, pos, buffer, count ); + else + { + read_bytes = stream->size - pos; + if ( read_bytes > count ) + read_bytes = count; + + memcpy( buffer, stream->base + pos, read_bytes ); + } + + stream->pos = pos + read_bytes; + + if ( read_bytes < count ) + { + qDebug( "ftIOFunc: invalid read; expected %lu bytes, got %lu\n", count, read_bytes ); + + error = FT_Err_Invalid_Stream_Operation; + } + + return error; +} + + +bool FtFace::glyphNames(QMap<uint, std::pair<QChar, QString> >& GList) const +{ + return GlyNames(ftFace(), GList); +} + + +void FtFace::RawData(QByteArray & bb) const +{ + FT_Stream fts = ftFace()->stream; + bb.resize(fts->size); + bool error = ftIOFunc(fts, 0L, reinterpret_cast<FT_Byte *>(bb.data()), fts->size); + if (error) + { + sDebug(QObject::tr("Font %1 is broken (read stream), no embedding").arg(fontFile)); + bb.resize(0); + status = qMax(status, ScFace::BROKENGLYPHS); + } +/* +// if (showFontInformation) + { + QFile f(fontFile); + qDebug(QObject::tr("RawData for Font %1(%2): size=%3 filesize=%4").arg(fontFile).arg(faceIndex).arg(bb.size()).arg(f.size())); + } +*/ +} + + diff --git a/scribus/fonts/ftface.h b/scribus/fonts/ftface.h new file mode 100644 index 0000000..434d10a --- /dev/null +++ b/scribus/fonts/ftface.h @@ -0,0 +1,121 @@ +/* +For general Scribus (>=1.3.2) copyright and licensing information please refer +to the COPYING file provided with the program. Following this notice may exist +a copyright and/or license notice that predates the release of Scribus 1.3.2 +for which a new license (GPL+exception) is in place. +*/ + +#ifndef FT_FACE_H +#define FT_FACE_H + +#include <QString> +#include <QMap> + +#include "scribusapi.h" + +#include "fonts/scface.h" + +#include <ft2build.h> +#include FT_FREETYPE_H + +#include "fpointarray.h" + +FT_Error ftIOFunc( FT_Stream stream, unsigned long pos, unsigned char* buffer, unsigned long count); + + +/*! \brief Base Class FtFace provides an ScFace private implementation + for Freetype based fonts. Subclasses are ScFace_ps and ScFace_ttf. + +Below is the old docs for class Foi: + +This is subclassed by a class to handle Type1 fonts, a class +to handle TrueType fonts, and potentially any other type that becomes appropriate in +the future. +Note the virtual destructor, needed to ensure that the correct destructor is called +for subclasses + +The RealName field has been changed from a data member to a member function. +This is because the only place the PostScript real name of a font is required is +the printing code, so it's cheaper to extract this information only when it is +required, for just the used fonts, than for every one of potentially hundreds at +application startup! This also allows for the fact that truetype fonts will require +a different method of extracting their names. + +One implication of using a base class/subclass model for fonts: It is no longer +possible to store the ScFace structures in a QMap. This is because QMap allocates +its own structures, and copies the supplied data to them. A QMap<QString,ScFace> +would demote all subclasses to ScFace classes, and hence break the polymorphism. +QDict can be used instead, with very little change to the rest of the code, since +it stores references to the data instead of copying the data. With AutoDelete set +to true, it will automatically dispose of all data when its destructor is called, +so there are no extra cleaning-up chores to take care of. +*/ +struct SCRIBUS_API FtFace : public ScFace::ScFaceData +{ + + FtFace(QString fam, QString sty, QString variant, QString scname, + QString psname, QString path, int face); + + FT_Face ftFace() const; + + virtual ~FtFace(); + + // font metrics + qreal ascent(qreal sz=1.0) const { return m_ascent * sz; } + qreal descent(qreal sz=1.0) const { return m_descent * sz; } + qreal xHeight(qreal sz=1.0) const { return m_xHeight * sz; } + qreal capHeight(qreal sz=1.0) const { return m_capHeight * sz; } + qreal height(qreal sz=1.0) const { return m_height * sz; } + qreal strikeoutPos(qreal sz=1.0) const { return m_strikeoutPos * sz; } + qreal underlinePos(qreal sz=1.0) const { return m_underlinePos * sz; } + qreal strokeWidth(qreal /*sz*/) const { return m_strokeWidth; } + qreal maxAdvanceWidth(qreal sz=1.0) const { return m_maxAdvanceWidth * sz; } + QString ascentAsString() const { return Ascent; } + QString descentAsString() const { return Descender; } + QString capHeightAsString() const { return CapHeight; } + QString FontBBoxAsString() const { return FontBBox; } + QString ItalicAngleAsString() const { return ItalicAngle; } + + +//FIXME QMap<QString,QString> fontDictionary(qreal sz=1.0) const; + + uint char2CMap(QChar ch) const; + + virtual qreal glyphKerning (uint gl1, uint gl2, qreal sz) const; +// GlyphMetrics glyphBBox (uint gl, qreal sz) const; + + void RawData (QByteArray & bb) const; + bool glyphNames(QMap<uint, std::pair<QChar, QString> >& GList) const; + void load () const; + void unload () const; + void loadGlyph (uint ch) const; + +protected: + mutable FT_Face m_face; + + static FT_Library library; + + mutable QString Ascent; + mutable QString CapHeight; + mutable QString Descender; + mutable QString ItalicAngle; + mutable QString StdVW; + QString FontEnc; + mutable QString FontBBox; + + mutable int m_encoding; + + mutable qreal m_uniEM; + mutable qreal m_ascent; + mutable qreal m_descent; + mutable qreal m_height; + mutable qreal m_xHeight; + mutable qreal m_capHeight; + mutable qreal m_maxAdvanceWidth; + mutable qreal m_underlinePos; + mutable qreal m_strikeoutPos; + mutable qreal m_strokeWidth; + +}; + +#endif diff --git a/scribus/fonts/scface.cpp b/scribus/fonts/scface.cpp new file mode 100644 index 0000000..8be80bc --- /dev/null +++ b/scribus/fonts/scface.cpp @@ -0,0 +1,460 @@ +/* +For general Scribus (>=1.3.2) copyright and licensing information please refer +to the COPYING file provided with the program. Following this notice may exist +a copyright and/or license notice that predates the release of Scribus 1.3.2 +for which a new license (GPL+exception) is in place. +*/ + +#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) +{ +} + +qreal ScFace::ScFaceData::glyphKerning(uint /*gl1*/, uint /*gl2*/, qreal /*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(qreal /*sz*/) const +{ + return QMap<QString, QString>(); +} + + +GlyphMetrics ScFace::ScFaceData::glyphBBox(uint gl, qreal sz) const +{ + GlyphMetrics res; + if (gl == 0 || gl >= CONTROL_GLYPHS) + { res.width = glyphWidth(gl, sz); + res.ascent = (gl == 0? ascent(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; +} + + +qreal ScFace::ScFaceData::glyphWidth(uint gl, qreal 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, qreal 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, qreal 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; +} + +QString ScFace::ascentAsString() const +{ + if (m->status == ScFace::UNKNOWN) { + m->load(); + } + return m->ascentAsString(); +} + +QString ScFace::descentAsString() const +{ + if (m->status == ScFace::UNKNOWN) { + m->load(); + } + return m->descentAsString(); +} +QString ScFace::capHeightAsString() const +{ + if (m->status == ScFace::UNKNOWN) { + m->load(); + } + return m->capHeightAsString(); +} + +QString ScFace::fontBBoxAsString() const +{ + if (m->status == ScFace::UNKNOWN) { + m->load(); + } + return m->FontBBoxAsString(); +} + +QString ScFace::italicAngleAsString() const +{ + if (m->status == ScFace::UNKNOWN) { + m->load(); + } + return m->ItalicAngleAsString(); +} + +qreal ScFace::ascent(qreal sz) const +{ + if (m->status == ScFace::UNKNOWN) { + m->load(); + } + return m->ascent(sz); +} + +qreal ScFace::descent(qreal sz) const +{ + if (m->status == ScFace::UNKNOWN) { + m->load(); + } + return m->descent(sz); +} +qreal ScFace::xHeight(qreal sz) const +{ + if (m->status == ScFace::UNKNOWN) { + m->load(); + } + return m->xHeight(sz); +} + +qreal ScFace::capHeight(qreal sz) const +{ + if (m->status == ScFace::UNKNOWN) { + m->load(); + } + return m->capHeight(sz); +} + +qreal ScFace::height(qreal sz) const +{ + if (m->status == ScFace::UNKNOWN) { + m->load(); + } + return m->height(sz); +} + +qreal ScFace::strikeoutPos(qreal sz) const +{ + if (m->status == ScFace::UNKNOWN) { + m->load(); + } + return m->strikeoutPos(sz); +} + +qreal ScFace::underlinePos(qreal sz) const +{ + if (m->status == ScFace::UNKNOWN) { + m->load(); + } + return m->underlinePos(sz); +} + +qreal ScFace::strokeWidth(qreal sz) const +{ + if (m->status == ScFace::UNKNOWN) { + m->load(); + } + return m->strokeWidth(sz); +} + +qreal ScFace::maxAdvanceWidth(qreal sz) const +{ + if (m->status == ScFace::UNKNOWN) { + m->load(); + } + return m->maxAdvanceWidth(sz); +} + +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; + } + } +} + + +qreal ScFace::charWidth(QChar ch, qreal 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); + qreal 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); + } + } +} diff --git a/scribus/fonts/scface.h b/scribus/fonts/scface.h new file mode 100644 index 0000000..8281505 --- /dev/null +++ b/scribus/fonts/scface.h @@ -0,0 +1,391 @@ +/* +For general Scribus (>=1.3.2) copyright and licensing information please refer +to the COPYING file provided with the program. Following this notice may exist +a copyright and/or license notice that predates the release of Scribus 1.3.2 +for which a new license (GPL+exception) is in place. +*/ + +#ifndef SC_FACE_H +#define SC_FACE_H + +/* ScFace responsibilities: + +font storage: type, format, filepath, index, document-local, substitute, etc. +usage: use, embed, subset, ... +face info: family, effect, alternative, flags, charset +encoding: CMap2String, glyphnames, +metrics: cwidth, bearing, bbox, "real" widths, paths +opentype: apply features, script support (-) +embedding: fontdictionary, rawdata, embedPS, embedPDF, subsetPS, subsetPDF +virtual: dispatch to constituents, handle embedding (-) +*/ + +#include <QString> +//#include <QVector> +#include <QMap> +//#include <QArray> +#include <utility> + +#include "fpointarray.h" + + + +struct GlyphMetrics { + qreal width; + qreal ascent; + qreal descent; +}; + + + +/*! \brief Base Class ScFace : This is a total rewrite of the old Foi class. + +It uses a shared private implementation which must be a subclass of ScFontData. +ScFace objects are quite small and can be handled like value objects. Reference +counting ensures that the shared data is freed when the last ScFace object is +destructed. + +ScFaceData has caches for face and glyph data. load() fills those caches for +face data, loadGlyph() fills the cache for glyphs. caches are always filled by +need, so you can call unload() any time (well, better not multithreaded...) +without producing errors. the increaseUsage() and decreaseUsage() keep track +of at how many places a face is used and automatically unload when the count +reaches zero. +Other data is recalculated on demand. The implementation can choose to do its +own caching for this data. + +ScFace uses a sophisticated lifecycle which controls the state of initialisation +and the performed checks. the normal lilecycle goes: + +UNKNOWN --load()--> LOADED --checkAllGlyphs()--> CHECKED + +If the face can not be loaded, the status changes to BROKEN. +If only some glyphs can not be loaded, the status changes to +BROKENGLYPHS. These fonts can still be used and subsetted(outlined), +but not embedded. + +ScFace constructors are only accessible to the ScFonts class which does +some checking before creating a new ScFace. These checks are recorded +in a cache and only reexecuted if the font file changes. +The field 'cachedStatus' allows load() to switch directly to one of the +states BROKEN, CHECKED or BROKENGLYPHS if this state was reached during a +previous run of Scribus. +*/ +class SCRIBUS_API ScFace +{ +public: + enum Status { UNKNOWN, LOADED, CHECKED, BROKENGLYPHS, BROKEN, NULLFACE }; + enum FontType { TYPE0, TYPE1, TYPE3, TTF, CFF, OTF, UNKNOWN_TYPE }; + enum FontFormat { PFA, PFB, TYPE2, TYPE42, + // handled by freetype: PFB_MAC, DFONT, HQX, MACBIN, + SFNT, TTCF, UNKNOWN_FORMAT }; + + static const uint CONTROL_GLYPHS = 2000000000; // 2 billion + + struct GlyphData { + FPointArray Outlines; + qreal x; + qreal y; + qreal bbox_width; + qreal bbox_ascent; + qreal bbox_descent; + bool broken; + GlyphData() : Outlines(), x(0), y(0), bbox_width(1), bbox_ascent(1), bbox_descent(0), broken(true) {} + }; + + + /// see accessors for ScFace for docs + class ScFaceData { + public: + /// controls destruction + mutable int refs; + /// controls load() + mutable int usage; + + QString scName; + QString fontFile; + int faceIndex; + QString psName; + QString family; + QString style; + QString variant; + + QString forDocument; + + mutable ScFace::Status status; + ScFace::FontType typeCode; + ScFace::FontFormat formatCode; + + bool usable; + bool embedPs; + bool subset; + + bool isStroked; + bool isFixedPitch; + bool hasNames; + uint maxGlyph; + + ScFaceData(); + virtual ~ScFaceData() { }; + protected: + + friend class ScFace; + Status cachedStatus; + + // caches + mutable QMap<uint,qreal> m_glyphWidth; + mutable QMap<uint,GlyphData> m_glyphOutline; + mutable QMap<uint, uint> m_cMap; + + // fill caches & members + + virtual void load() const + { + m_glyphWidth.clear(); + m_glyphOutline.clear(); + m_cMap.clear(); + + status = qMax(cachedStatus, ScFace::LOADED); + } + + virtual void unload() const + { + m_glyphWidth.clear(); + m_glyphOutline.clear(); + m_cMap.clear(); + + status = ScFace::UNKNOWN; + } + + virtual void loadGlyph(uint /*gl*/) const {} + + // dummy implementations + virtual qreal ascent(qreal sz) const { return sz; } + virtual QString ascentAsString() const { return "0" ; } + virtual QString descentAsString() const { return "0"; } + virtual QString capHeightAsString() const { return "0"; } + virtual QString FontBBoxAsString() const { return "0 0 0 0"; } + virtual QString ItalicAngleAsString() const { return "0"; } + virtual qreal descent(qreal /*sz*/) const { return 0.0; } + virtual qreal xHeight(qreal sz) const { return sz; } + virtual qreal capHeight(qreal sz) const { return sz; } + virtual qreal height(qreal sz) const { return sz; } + virtual qreal strikeoutPos(qreal sz) const { return sz / 2; } + virtual qreal underlinePos(qreal /*sz*/) const { return -1.0; } + virtual qreal strokeWidth(qreal /*sz*/) const { return 0.1; } + virtual qreal maxAdvanceWidth(qreal sz) const { return sz; } + virtual uint char2CMap(QChar /*ch*/) const { return 0; } + virtual qreal glyphKerning(uint gl1, uint gl2, qreal sz) const; + virtual QMap<QString,QString> fontDictionary(qreal sz=1.0) const; + virtual GlyphMetrics glyphBBox(uint gl, qreal sz) const; + virtual bool EmbedFont(QString &/*str*/) const { return false; } + virtual void RawData(QByteArray & /*bb*/) const {} + virtual bool glyphNames(QMap<uint, std::pair<QChar, QString> >& gList) const; + + // these use the cache: + virtual qreal glyphWidth(uint gl, qreal sz) const; + virtual FPointArray glyphOutline(uint gl, qreal sz) const; + virtual FPoint glyphOrigin (uint gl, qreal sz) const; + + }; + + + + ScFace(); + ScFace(const ScFace& other); + ~ScFace(); + + /// used as a null object + static const ScFace& none(); + + /// test for null object + bool isNone() const { return m->status == NULLFACE; } + + ScFace& operator=(const ScFace& other); + /** two ScFaces are equal if they either are both NULLFACEs or they + agree on family, style, variant and fontpath + */ + bool operator==(const ScFace& other) const ; + bool operator!=(const ScFace& other) const { return ! (*this == other); } + + + bool EmbedFont(QString &str); + void RawData(QByteArray & bb); + bool glyphNames(QMap<uint, std::pair<QChar, QString> >& gList); + + /// prevent unloading of face data + void increaseUsage() const; + + /// unload face data if not used any more + void decreaseUsage() const; + + /// unload face data. It will be reloaded on need + void unload() const; + + /// the name Scribus uses for this font + QString scName() const { return replacedName.isEmpty() ? m->scName : replacedName; } + + /// the name of the font which was used for replacement + QString replacementName() const { return m->scName; } + + /// the name of the font which was used for replacement + QString replacementForDoc() const { return replacedInDoc; } + + /// check if this is a replacement font + bool isReplacement() const { return !replacedName.isEmpty(); } + + /// makes a repalcement font for font "name" using this fonts data + ScFace mkReplacementFor(QString name, QString doc) { + ScFace result(m); + result.replacedName = name; + result.replacedInDoc = doc; + return result; + } + + void chReplacementTo(ScFace& other, QString doc) { + QString oldName = replacedName; + (*this) = other; + replacedName = oldName; + replacedInDoc = doc; + } + + /// the name PostScript uses for this font + QString psName() const { return m->psName; } + + /// the physical location of the fontfile + QString fontPath() const { return m->faceIndex >= 0 ? QString("%1(%2)").arg(m->fontFile).arg(m->faceIndex+1) : m->fontFile; } + + /// the file path of the fontfile + QString fontFilePath() const { return m->fontFile; } + + /// if the fontfile contains more than one face, the index, else -1 + int faceIndex() const { return m->faceIndex; } + + /// path name of the document this face is local to + QString localForDocument() const { return m->forDocument; } + + /// font type, eg. Type1 or TTF + FontType type() const { return m->typeCode; } + + /// font format, which might be a little more complicated + FontFormat format()const { return m->formatCode; } + + /// test if this face can be used in documents + bool usable() const { return m->usable && !isNone(); } + + /// test if this face can be embedded in PS/PDF + bool embedPs() const { return m->embedPs && m->status < BROKENGLYPHS; } + + /// test if this face can be embedded as outlines in PS/PDF + bool subset() const { return m->subset && m->status < BROKEN; } + + void usable(bool flag) { m->usable = flag; } + void embedPs(bool flag) { m->embedPs = flag; } + void subset(bool flag) { m->subset = flag; } + + /// deprecated? tells if the face has PS names + bool hasNames() const { return m->hasNames; } + + /// tells if this font is an outline font + bool isStroked() const { return m->isStroked; } + + /// tells if this font is a fixed pitch font + bool isFixedPitch()const { return m->isFixedPitch; } + + /// tells if this is an OTF/CFF font + bool isOTF() const { return m->typeCode == OTF; } + + /// returns the highest glyph index in this face + uint maxGlyph() const { return m->maxGlyph; } + + /// returns the font family as seen by Scribus + QString family() const { return m->family; } + + /// returns the font style as seen by Scribus (eg. bold, Italic) + QString style() const { return m->style; } + + /// returns an additional discriminating String for this face + QString variant() const { return m->variant; } + + // font metrics + QString ascentAsString() const; + QString descentAsString() const; + QString capHeightAsString() const; + QString fontBBoxAsString() const; + QString italicAngleAsString() const; + qreal ascent(qreal sz=1.0) const; + qreal descent(qreal sz=1.0) const; + qreal xHeight(qreal sz=1.0) const; + qreal capHeight(qreal sz=1.0) const; + qreal height(qreal sz=1.0) const; + qreal strikeoutPos(qreal sz=1.0) const; + qreal underlinePos(qreal sz=1.0) const; + qreal strokeWidth(qreal sz=1.0) const; + qreal maxAdvanceWidth(qreal sz=1.0) const; + + /// deprecated + QString stemV(qreal sz=1.0) const { return fontDictionary(sz)["/StemV"]; } + + /// deprecated + QString italicAngle(qreal sz=1.0) const { return fontDictionary(sz)["/ItalicAngle"]; } + + /// deprecated + QString fontBBox(qreal sz=1.0) const { return fontDictionary(sz)["/FontBBox"]; } + + /// returns a map of values used for font dictionaries in PS/PDF + QMap<QString,QString> fontDictionary(qreal sz=1.0) const { return m->fontDictionary(sz); } + // glyph interface + + /// returns the glyphs normal advance width at size 'sz' + qreal glyphWidth(uint gl, qreal sz=1.0) const { return m->glyphWidth(gl, sz); } + + /// returns the glyph kerning between 'gl1' and 'gl2' at size 'sz' + qreal glyphKerning(uint gl1, uint gl2, qreal sz=1.0) const { return qMax(gl1,gl2) < CONTROL_GLYPHS ? m->glyphKerning(gl1, gl2, sz) : 0; } + + /// returns the glyphs bounding box at size 'sz', ie. the area where this glyph will produce marks + GlyphMetrics glyphBBox(uint gl, qreal sz=1.0) const { return m->glyphBBox(gl, sz); } + + /// returns the glyph's outline as a cubic Bezier path + FPointArray glyphOutline(uint gl, qreal sz=1.0) const { return m->glyphOutline(gl, sz); } + + /// returns the glyph's origin FIXME: what's that exactly? + FPoint glyphOrigin(uint gl, qreal sz=1.0) const { return m->glyphOrigin(gl, sz); } + + // char interface + + /// test if the face can render this char + bool canRender(QChar ch) const; + + /// translate unicode to glyph index + uint char2CMap(QChar ch) const; + + /// returns the combined glyph width and kerning for 'ch' if followed by 'ch2' + qreal charWidth(QChar ch, qreal sz=1.0, QChar ch2 = QChar(0)) const; + + /// deprecated, see glyphBBox() + qreal realCharWidth(QChar ch, qreal sz=1.0) const { return glyphBBox(char2CMap(ch),sz).width; } + + /// deprecated, see glyphBBox() + qreal realCharHeight(QChar ch, qreal sz=1.0) const { GlyphMetrics gm=glyphBBox(char2CMap(ch),sz); return gm.ascent + gm.descent; } + + /// deprecated, see glyphBBox() + qreal realCharAscent(QChar ch, qreal sz=1.0) const { return glyphBBox(char2CMap(ch),sz).ascent; } + + /// deprecated, see glyphBBox() + qreal realCharDescent(QChar ch, qreal sz=1.0) const { return glyphBBox(char2CMap(ch),sz).descent; } + +private: + + friend class SCFonts; + + ScFace(ScFaceData* md); + ScFaceData* m; + QString replacedName; + QString replacedInDoc; + + void initFaceData(); + void checkAllGlyphs(); + uint emulateGlyph(QChar c) const; +}; + +#endif diff --git a/scribus/fonts/scface_ps.cpp b/scribus/fonts/scface_ps.cpp new file mode 100644 index 0000000..949814f --- /dev/null +++ b/scribus/fonts/scface_ps.cpp @@ -0,0 +1,114 @@ +/* +For general Scribus (>=1.3.2) copyright and licensing information please refer +to the COPYING file provided with the program. Following this notice may exist +a copyright and/or license notice that predates the release of Scribus 1.3.2 +for which a new license (GPL+exception) is in place. +*/ +#include <QDebug> +#include <QDir> +#include <QFileInfo> + +#include "ftface.h" +#include "scface_ps.h" + + + +QStringList ScFace_postscript::findFontMetrics(const QString& fontPath) const +{ + QStringList metricsFiles; + QFileInfo fi(fontPath); + + QString fontDir = fi.absolutePath(); + QString fontFile = fi.fileName(); + + metricsFiles += findFontMetrics(fontDir, fontFile); + + //if no metrics found look in afm and pfm subdirs + if ( metricsFiles.size() <= 0 ) + { + QDir dir; + if (dir.exists(fontDir + "/AFMs")) + metricsFiles += findFontMetrics(fontDir + "/AFMs", fontFile); + if (dir.exists(fontDir + "/afm") && metricsFiles.size() <= 0) + metricsFiles += findFontMetrics(fontDir + "/afm", fontFile); + if (dir.exists(fontDir + "/Pfm") && metricsFiles.size() <= 0) + metricsFiles += findFontMetrics(fontDir + "/Pfm", fontFile); + if (dir.exists(fontDir + "/pfm") && metricsFiles.size() <= 0) + metricsFiles += findFontMetrics(fontDir + "/pfm", fontFile); + } + + return metricsFiles; +} + +QStringList ScFace_postscript::findFontMetrics(const QString& baseDir, const QString& baseName) const +{ + QStringList metricsFiles; + QString basePath = baseDir + "/" + baseName; + QString afnm = basePath.left(basePath.length()-3); + + // Look for afm files + QString afmName(afnm+"afm"); + if(QFile::exists(afmName)) + metricsFiles.append(afmName); + else + { + afmName = afnm+"Afm"; + if(QFile::exists(afmName)) + metricsFiles.append(afmName); + else + { + afmName = afnm+"AFM"; + if(QFile::exists(afmName)) + metricsFiles.append(afmName); + } + } + + // Look for pfm files + QString pfmName(afnm+"pfm"); + if(QFile::exists(pfmName)) + metricsFiles.append(pfmName); + else + { + pfmName = afnm+"Pfm"; + if(QFile::exists(pfmName)) + metricsFiles.append(pfmName); + else + { + afmName = afnm+"PFM"; + if(QFile::exists(pfmName)) + metricsFiles.append(pfmName); + } + } + + return metricsFiles; +} + +bool ScFace_postscript::loadFontMetrics(FT_Face face, const QString& fontPath) const +{ + bool metricsFound = false; + QStringList fontMetrics = findFontMetrics(fontPath); + if (fontMetrics.size() > 0) + { + bool brokenMetric = false; + QString metricsFile; + for (int i = 0; i < fontMetrics.size(); ++i) + { + metricsFile = fontMetrics.at(i); + if (FT_Attach_File(face, metricsFile.toLocal8Bit().constData())) + { + qDebug() << QObject::tr("Font %1 has broken metrics in file %2, ignoring metrics").arg(fontPath).arg(metricsFile); + brokenMetric = true; + } + else + { + if (brokenMetric) + qDebug() << QObject::tr("Valid metrics were found for font %1, using metrics in file %2").arg(fontFile).arg(metricsFile); + metricsFound = true; + break; + } + } + } + else + qDebug() << QObject::tr("No metrics found for font %1, ignoring font").arg(fontPath); + return metricsFound; +} diff --git a/scribus/fonts/scface_ps.h b/scribus/fonts/scface_ps.h new file mode 100644 index 0000000..5b38714 --- /dev/null +++ b/scribus/fonts/scface_ps.h @@ -0,0 +1,222 @@ +/* + For general Scribus (>=1.3.2) copyright and licensing information please refer + to the COPYING file provided with the program. Following this notice may exist + a copyright and/or license notice that predates the release of Scribus 1.3.2 + for which a new license (GPL+exception) is in place. + */ +#ifndef SCFACE_PS_H +#define SCFACE_PS_H + +#include <QString> +#include <QStringList> +#include <QFont> +#include <QMap> + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_GLYPH_H + +#include "scribusapi.h" +#include "fpointarray.h" +#include "scconfig.h" + + +/* + Class ScFace_postscript + Subclass of ScFace, for PostScript fonts that could possibly have a .afm file + associated with them for metrics information. +*/ + +class ScFace_postscript : public FtFace +{ + public: + ScFace_postscript(QString fam, QString sty, QString alt, QString scname, QString psname, QString path, int face) : + FtFace(fam,sty,alt,scname,psname,path,face) + { + isFixedPitch = false; + typeCode = ScFace::TYPE1; + } + + virtual QStringList findFontMetrics(const QString& fontPath) const; + virtual QStringList findFontMetrics(const QString& baseDir, const QString& baseName) const; + virtual bool loadFontMetrics(FT_Face face, const QString& fontPath) const; + + virtual void load() const // routine by Franz Schmid - modified by Alastair M. Robinson + { + FtFace::load(); +// bool error; + FT_Face face = ftFace(); + if (!face) + { + const_cast<ScFace_postscript*>(this)->usable = false; + qDebug("%s", QObject::tr("Font %1 is broken (no Face), discarding it").arg(fontFile).toLocal8Bit().constData()); + return; + } + if (loadFontMetrics(face, fontFile)) + { + // re-initialize: ScFaceData::load() just clears caches, + // FtFace::load() skips FT_New_Face if m_face is already defined. + // dont mind checking glyphs again for now (PS files have only 255 glyphs max, anyway) + FtFace::load(); + } +// Ascent = tmp.setNum(face->ascender); +// Descender = tmp.setNum(face->descender); +// CapHeight = Ascent; +// ItalicAngle = "0"; +// StdVW = "1"; +// FontBBox = tmp.setNum(face->bbox.xMin)+" "+tmp2.setNum(face->bbox.yMin)+" "+tmp3.setNum(face->bbox.xMax)+" "+tmp4.setNum(face->bbox.yMax); +/* + setBestEncoding(face); + gindex = 0; + charcode = FT_Get_First_Char( face, &gindex ); + int goodGlyph = 0; + int invalidGlyph = 0; + while ( gindex != 0 ) + { + error = FT_Load_Glyph( face, gindex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ); + if (error) + { + ++invalidGlyph; + sDebug(QObject::tr("Font %1 has broken glyph %2 (charcode %3)").arg(fontPath()).arg(gindex).arg(charcode)); + charcode = FT_Get_Next_Char( face, charcode, &gindex ); + continue; + } + ++goodGlyph; + qreal ww = face->glyph->metrics.horiAdvance / uniEM; + if (face->glyph->format == FT_GLYPH_FORMAT_PLOTTER) + isStroked = true; + error = false; + outlines = traceChar(face, charcode, 10, &x, &y, &error); + if (!error) + { + CharWidth.insert(charcode, ww); + GRec.Outlines = outlines.copy(); + GRec.x = x; + GRec.y = y; + GlyphArray.insert(charcode, GRec); + } + charcode = FT_Get_Next_Char( face, charcode, &gindex ); + } + */ + } +}; + +/* + Class ScFace_pfb + Subclass of ScFace, specifically for Adobe type 1 .pfb fonts. + Implements: RealName() and EmbedFont(). +*/ + +class ScFace_pfb : public ScFace_postscript +{ + public: + ScFace_pfb(QString fam, QString sty, QString alt, QString scname, QString psname, QString path, int face) : + ScFace_postscript(fam,sty,alt,scname,psname,path,face) + { + formatCode = ScFace::PFB; + } + + virtual bool EmbedFont(QString &str) const + { + QByteArray bb; + RawData(bb); + QString tmp2 = ""; + if ((bb.size() > 2) && (bb[0] == char(0x80)) && (static_cast<int>(bb[1]) == 1)) + { + QString tmp3=""; + QString tmp4 = ""; + int posi,cxxc=0; + for (posi = 6; posi < bb.size(); ++posi) + { + if ((bb[posi] == char(0x80)) && (posi+1 < bb.size()) && (static_cast<int>(bb[posi+1]) == 2)) + break; + str += bb[posi]; + } + int ulen; + if (posi+6 < bb.size()) + { + ulen = bb[posi+2] & 0xff; + ulen |= (bb[posi+3] << 8) & 0xff00; + ulen |= (bb[posi+4] << 16) & 0xff0000; + ulen |= (bb[posi+5] << 24) & 0xff000000; + posi += 6; + if (posi + ulen > bb.size()) + ulen = bb.size() - posi - 1; + char linebuf[80]; + cxxc=0; + for (int j = 0; j < ulen; ++j) + { + unsigned char u=bb[posi]; + linebuf[cxxc]=((u >> 4) & 15) + '0'; + if(u>0x9f) linebuf[cxxc]+='a'-':'; + ++cxxc; + u&=15; linebuf[cxxc]=u + '0'; + if(u>0x9) linebuf[cxxc]+='a'-':'; + ++posi; + ++cxxc; + if (cxxc > 72) + { + linebuf[cxxc++]='\n'; + linebuf[cxxc++]=0; + str += linebuf; + cxxc = 0; + } + } + linebuf[cxxc]=0; + str += linebuf; + str += "\n"; + } + posi += 6; + for (int j = posi; j < bb.size(); ++j) + { + if ((bb[j] == static_cast<char>(0x80)) && (j+1 < bb.size()) && (static_cast<int>(bb[j+1]) == 3)) + break; + if(bb[j]=='\r') + str+="\n"; + else + str += bb[j]; + } + str += "\n"; + cxxc = 0; + return true; + } + else + { + qDebug("%s", QObject::tr("Font %1 cannot be read, no embedding").arg(fontFile).toLatin1().constData()); + return false; + } + } +}; + +/* + Class ScFace_pfa + Subclass of ScFace, specifically for Adobe type 1 and type 3 .pfa fonts. + Implements: RealName() and EmbedFont(). +*/ + +class ScFace_pfa : public ScFace_postscript +{ + public: + ScFace_pfa(QString fam, QString sty, QString alt, QString scname, QString psname, QString path, int face) : + ScFace_postscript(fam,sty,alt,scname,psname,path,face) + { + formatCode = ScFace::PFA; + } + virtual bool EmbedFont(QString &str) const + { + QByteArray bb; + RawData(bb); + if (bb.size() > 2 && bb[0] == '%' && bb[1] == '!') + { + // this is ok since bb will not contain '\0' + str.append(bb); + return true; + } + qDebug("%s", QObject::tr("Font %1 cannot be read, no embedding").arg(fontFile).toLatin1().constData()); + return false; + } +}; + + +#endif diff --git a/scribus/fonts/scface_ttf.cpp b/scribus/fonts/scface_ttf.cpp new file mode 100644 index 0000000..7e44223 --- /dev/null +++ b/scribus/fonts/scface_ttf.cpp @@ -0,0 +1,662 @@ +/* +For general Scribus (>=1.3.2) copyright and licensing information please refer +to the COPYING file provided with the program. Following this notice may exist +a copyright and/or license notice that predates the release of Scribus 1.3.2 +for which a new license (GPL+exception) is in place. +*/ + +#include <QFile> +#include <QString> +#include <QObject> +#include <QDebug> + +#include <sys/types.h> + +#include "fonts/scface_ttf.h" +#include "fonts/scfontmetrics.h" +#include "util.h" +#include "scconfig.h" + +KernFeature::KernFeature ( FT_Face face ) + :m_valid ( true ) +{ + FontName = QString ( face->family_name ) + " " + QString ( face->style_name ) ; +// qDebug() <<"KF"<<FontName; +// QTime t; +// t.start(); + FT_ULong length = 0; + if ( !FT_Load_Sfnt_Table ( face, TTAG_GPOS , 0, NULL, &length ) ) + { +// qDebug() <<"\t"<<"GPOS table len"<<length; + if ( length > 32 ) + { + GPOSTableRaw.resize ( length ); + FT_Load_Sfnt_Table ( face, TTAG_GPOS, 0, reinterpret_cast<FT_Byte*> ( GPOSTableRaw.data() ), &length ); + + makeCoverage(); + } + else + m_valid = false; + + GPOSTableRaw.clear(); +// coverages.clear(); + } + else + m_valid = false; + + if ( !m_valid ) + pairs.clear(); +// qDebug() <<"\t"<<m_valid; +// qDebug() <<"\t"<<t.elapsed(); +} + +KernFeature::KernFeature ( const KernFeature & kf ) +{ + m_valid = kf.m_valid; + if ( m_valid ) + pairs = kf.pairs; + +} + + +KernFeature::~ KernFeature() +{ +} + +double KernFeature::getPairValue ( unsigned int glyph1, unsigned int glyph2 ) const +{ + if ( m_valid ) + { + + if ( pairs.contains( glyph1 ) + && pairs[glyph1].contains(glyph2)) + { + return pairs[glyph1][glyph2]; + } + else + { + //qDebug()<<"Search in classes"; + foreach (const quint16& coverageId, coverages.keys()) + { + // for each pairpos table, coverage lists covered _first_ (left) glyph + if(coverages[coverageId].contains(glyph1)) + { + foreach(const quint16& classDefOffset, classGlyphFirst[coverageId].keys()) + { + const ClassDefTable& cdt(classGlyphFirst[coverageId][classDefOffset]); + foreach(const quint16& classIndex, cdt.keys()) + { + const QList<quint16>& gl(cdt[classIndex]); + if(gl.contains(glyph1)) + { + //qDebug()<<"Found G1"<<glyph1<<"in Class"<<classIndex<<"at pos"<<gl.indexOf(glyph1); + // Now we got the index of the first glyph class, see if glyph2 is in one of the left glyphs classes attached to this subtable. + foreach(const quint16& classDefOffset2, classGlyphSecond[coverageId].keys()) + { + const ClassDefTable& cdt2(classGlyphSecond[coverageId][classDefOffset2]); + foreach(const quint16& classIndex2, cdt2.keys()) + { + const QList<quint16>& gl2(cdt2[classIndex2]); + if(gl2.contains(glyph2)) + { + //qDebug()<<"Found G2"<<glyph2<<"in Class"<<classIndex2<<"at pos"<<gl2.indexOf(glyph2); + + double v(classValue[coverageId][classIndex][classIndex2]); + // Cache this pair into "pairs" map. + pairs[glyph1][glyph2] = v; + return v; + } + } + } + } + } + } + } + } + } + } + return 0.0; +} + +void KernFeature::makeCoverage() +{ + if ( GPOSTableRaw.isEmpty() ) + return; + + quint16 FeatureList_Offset= toUint16 ( 6 ); + quint16 LookupList_Offset = toUint16 ( 8 ); + + // Find the offsets of the kern feature tables + quint16 FeatureCount = toUint16 ( FeatureList_Offset );; + QList<quint16> FeatureKern_Offset; + for ( quint16 FeatureRecord ( 0 ); FeatureRecord < FeatureCount; ++ FeatureRecord ) + { + int rawIdx ( FeatureList_Offset + 2 + ( 6 * FeatureRecord ) ); + quint32 tag ( FT_MAKE_TAG ( GPOSTableRaw.at ( rawIdx ), + GPOSTableRaw.at ( rawIdx + 1 ), + GPOSTableRaw.at ( rawIdx + 2 ), + GPOSTableRaw.at ( rawIdx + 3 ) ) ); + if ( tag == TTAG_kern ) + { + FeatureKern_Offset << ( toUint16 ( rawIdx + 4 ) + FeatureList_Offset ); + + } + } + + // Extract indices of lookups for feture kern + QList<quint16> LookupListIndex; + foreach ( quint16 kern, FeatureKern_Offset ) + { + quint16 LookupCount ( toUint16 ( kern + 2 ) ); + for ( int llio ( 0 ) ; llio < LookupCount; ++llio ) + { + quint16 Idx ( toUint16 ( kern + 4 + ( llio * 2 ) ) ); + if ( !LookupListIndex.contains ( Idx ) ) + { + LookupListIndex <<Idx ; + } + } + } + + + // Extract offsets of lookup tables for feature kern + QList<quint16> LookupTables; + QList<quint16> PairAdjustmentSubTables; + for ( int i ( 0 ); i < LookupListIndex.count(); ++i ) + { + int rawIdx ( LookupList_Offset + 2 + ( LookupListIndex[i] * 2 ) ); + quint16 Lookup ( toUint16 ( rawIdx ) + LookupList_Offset ); + quint16 SubTableCount ( toUint16 ( Lookup + 4 ) ); + for ( int stIdx ( 0 ); stIdx < SubTableCount; ++ stIdx ) + { + quint16 SubTable ( toUint16 ( Lookup + 6 + ( 2 * stIdx ) ) + Lookup ); + +// quint16 PosFormat ( toUint16 ( SubTable ) ); + quint16 Coverage_Offset ( toUint16 ( SubTable + 2 ) + SubTable ); + quint16 CoverageFormat ( toUint16 ( Coverage_Offset ) ); + + if ( 1 == CoverageFormat ) // glyph indices based + { + quint16 GlyphCount ( toUint16 ( Coverage_Offset + 2 ) ); + quint16 GlyphID ( Coverage_Offset + 4 ); + if (GlyphCount == 0) continue; + + for ( unsigned int gl ( 0 ); gl < GlyphCount; ++gl ) + { + coverages[SubTable] << toUint16 ( GlyphID + ( gl * 2 ) ); + } + } + else if ( 2 == CoverageFormat ) // Coverage Format2 => ranges based + { + quint16 RangeCount ( toUint16 ( Coverage_Offset + 2 ) ); + if (RangeCount == 0) continue; + +// int gl_base ( 0 ); + for ( int r ( 0 ); r < RangeCount; ++r ) + { + quint16 rBase ( Coverage_Offset + 4 + ( r * 6 ) ); + quint16 Start ( toUint16 ( rBase ) ); + quint16 End ( toUint16 ( rBase + 2 ) ); +// quint16 StartCoverageIndex ( toUint16 ( rBase + 4 ) ); + // #9842 : for some font such as Gabriola Regular + // the range maybe be specified in reverse order + if (Start <= End) + { + for ( unsigned int gl ( Start ); gl <= End; ++gl ) + coverages[SubTable] << gl; + } + else + { + for ( int gl ( Start ); gl >= (int) End; --gl ) + coverages[SubTable] << gl; + } + } + } + else + { +// qDebug() <<"Unknow Coverage Format:"<<CoverageFormat; + continue; + } + + makePairs ( SubTable ); + } + + } + + +} + + +void KernFeature::makePairs ( quint16 subtableOffset ) +{ + /* + Lookup Type 2: + Pair Adjustment Positioning Subtable + */ + + quint16 PosFormat ( toUint16 ( subtableOffset ) ); + + if ( PosFormat == 1 ) + { + quint16 ValueFormat1 ( toUint16 ( subtableOffset +4 ) ); + quint16 ValueFormat2 ( toUint16 ( subtableOffset +6 ) ); + quint16 PairSetCount ( toUint16 ( subtableOffset +8 ) ); + if ( ValueFormat1 && ValueFormat2 ) + { + for ( int psIdx ( 0 ); psIdx < PairSetCount; ++ psIdx ) + { + unsigned int FirstGlyph ( coverages[subtableOffset][psIdx] ); + quint16 PairSetOffset ( toUint16 ( subtableOffset +10 + ( 2 * psIdx ) ) + subtableOffset ); + quint16 PairValueCount ( toUint16 ( PairSetOffset ) ); + quint16 PairValueRecord ( PairSetOffset + 2 ); + for ( int pvIdx ( 0 );pvIdx < PairValueCount; ++pvIdx ) + { + quint16 recordBase ( PairValueRecord + ( ( 2 + 2 + 2 ) * pvIdx ) ); + quint16 SecondGlyph ( toUint16 ( recordBase ) ); + qint16 Value1 ( toInt16 ( recordBase + 2 ) ); + pairs[FirstGlyph][SecondGlyph] = double ( Value1 ); + } + + } + } + else if ( ValueFormat1 && ( !ValueFormat2 ) ) + { + for ( int psIdx ( 0 ); psIdx < PairSetCount; ++ psIdx ) + { + unsigned int FirstGlyph ( coverages[subtableOffset][psIdx] ); + quint16 PairSetOffset ( toUint16 ( subtableOffset +10 + ( 2 * psIdx ) ) + subtableOffset ); + quint16 PairValueCount ( toUint16 ( PairSetOffset ) ); + quint16 PairValueRecord ( PairSetOffset + 2 ); + for ( int pvIdx ( 0 );pvIdx < PairValueCount; ++pvIdx ) + { + quint16 recordBase ( PairValueRecord + ( ( 2 + 2 ) * pvIdx ) ); + quint16 SecondGlyph ( toUint16 ( recordBase ) ); + qint16 Value1 ( toInt16 ( recordBase + 2 ) ); + pairs[FirstGlyph][SecondGlyph] = double ( Value1 ); + } + } + } + else + { +// qDebug() <<"ValueFormat1 is null or both ValueFormat1 and ValueFormat2 are null"; + } + } + else if ( PosFormat == 2 ) // class kerning + { + quint16 ValueFormat1 ( toUint16 ( subtableOffset +4 ) ); + quint16 ValueFormat2 ( toUint16 ( subtableOffset +6 ) ); + quint16 ClassDef1 ( toUint16 ( subtableOffset +8 ) + subtableOffset ); + quint16 ClassDef2 ( toUint16 ( subtableOffset +10 ) + subtableOffset ); + quint16 Class1Count ( toUint16 ( subtableOffset +12 ) ); + quint16 Class2Count ( toUint16 ( subtableOffset +14 ) ); + quint16 Class1Record ( subtableOffset +16 ); + + // first extract classses + getClass(true, ClassDef1 , subtableOffset ); + getClass(false, ClassDef2 , subtableOffset ); + + if ( ValueFormat1 && ValueFormat2 ) + { + for ( quint16 C1 ( 0 );C1 < Class1Count; ++C1 ) + { + quint16 Class2Record ( Class1Record + ( C1 * ( 2 * 2 * Class2Count ) ) ); + for ( quint16 C2 ( 0 );C2 < Class2Count; ++C2 ) + { + qint16 Value1 ( toInt16 ( Class2Record + ( C2 * ( 2 * 2 ) ) ) ); + if(Value1 != 0) + { + classValue[subtableOffset][C1][C2] = double ( Value1 ); + } + } + } + } + else if ( ValueFormat1 && ( !ValueFormat2 ) ) + { + for ( quint16 C1 ( 1 );C1 < Class1Count; ++C1 ) + { + quint16 Class2Record ( Class1Record + ( C1 * ( 2 * Class2Count ) ) ); + for ( quint16 C2 ( 1 );C2 < Class2Count; ++C2 ) + { + qint16 Value1 ( toInt16 ( Class2Record + ( C2 * 2 ) ) ); + if(Value1 != 0) + { + classValue[subtableOffset][C1][C2] = double ( Value1 ); + } + } + } + } + else + { +// qDebug() <<"ValueFormat1 is null or both ValueFormat1 and ValueFormat2 are null"; + } + + } + else + qDebug() <<"unknown PosFormat"<<PosFormat; +} + +KernFeature::ClassDefTable KernFeature::getClass ( bool leftGlyph, quint16 classDefOffset, quint16 coverageId ) +{ + if(leftGlyph) + { + if(classGlyphFirst.contains(coverageId) && classGlyphFirst[coverageId].contains(classDefOffset)) + return classGlyphFirst[coverageId][classDefOffset]; + } + else + { + if(classGlyphSecond.contains(coverageId) && classGlyphSecond[coverageId].contains(classDefOffset)) + return classGlyphSecond[coverageId][classDefOffset]; + } + + ClassDefTable ret; + + QList<quint16> excludeList; + quint16 ClassFormat ( toUint16 ( classDefOffset ) ); + if ( ClassFormat == 1 ) + { + quint16 StartGlyph ( toUint16 ( classDefOffset +2 ) ); + quint16 GlyphCount ( toUint16 ( classDefOffset +4 ) ); + quint16 ClassValueArray ( classDefOffset + 6 ); + + for ( quint16 CV ( 0 );CV < GlyphCount; ++CV ) + { + excludeList<<StartGlyph + CV; + ret[ toUint16 ( ClassValueArray + ( CV * 2 ) ) ] << StartGlyph + CV; + } + } + else if ( ClassFormat == 2 ) + { + quint16 ClassRangeCount ( toUint16 ( classDefOffset + 2 ) ); + quint16 ClassRangeRecord ( classDefOffset + 4 ); + for ( int CRR ( 0 ); CRR < ClassRangeCount; ++CRR ) + { + quint16 Start ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) ) ); + quint16 End ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) + 2 ) ); + quint16 Class ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) + 4 ) ); + + for ( int gl ( Start ); gl <= (int) End; ++gl ) + { + excludeList<< (quint16) gl; + ret[Class] << gl; + } + } + } + else + qDebug() <<"Unknown Class Table type"; + + // if possible (all glyphs are "classed"), avoid to pass through this slow piece of code. + if(excludeList.count() != coverages[coverageId].count()) + { + foreach(const quint16& gidx, coverages[coverageId]) + { + if(!excludeList.contains(gidx)) + ret[0] << gidx; + } + } + if(leftGlyph) + classGlyphFirst[coverageId][classDefOffset] = ret; + else + classGlyphSecond[coverageId][classDefOffset] = ret; + + return ret; +} + +quint16 KernFeature::toUint16 ( quint16 index ) +{ + if ( ( index + 2 ) > GPOSTableRaw.count() ) + { +// qDebug() << "HORROR!" << index << GPOSTableRaw.count() << FontName ; + // Rather no kerning at all than random kerning +// m_valid = false; + return 0; + } + // FIXME I just do not know how it has to be done *properly* + quint16 c1 ( GPOSTableRaw.at ( index ) ); + quint16 c2 ( GPOSTableRaw.at ( index + 1 ) ); + c1 &= 0xFF; + c2 &= 0xFF; + quint16 ret ( ( c1 << 8 ) | c2 ); + return ret; +} + +qint16 KernFeature::toInt16 ( quint16 index ) +{ + if ( ( index + 2 ) > GPOSTableRaw.count() ) + { + return 0; + } + // FIXME I just do not know how it has to be done *properly* + quint16 c1 ( GPOSTableRaw.at ( index ) ); + quint16 c2 ( GPOSTableRaw.at ( index + 1 ) ); + c1 &= 0xFF; + c2 &= 0xFF; + qint16 ret ( ( c1 << 8 ) | c2 ); + return ret; +} + + +namespace { +uint word(QByteArray const & bb, uint pos) +{ + const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos; + return pp[0] << 24 | pp[1] << 16 | pp[2] << 8 | pp[3]; +} +uint word16(QByteArray const & bb, uint pos) +{ + const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos; + return pp[0] << 8 | pp[1]; +} +QString tag(QByteArray const & bb, uint pos) +{ + char buf[5] = "1234"; + buf[0] = bb.data()[pos]; + buf[1] = bb.data()[pos+1]; + buf[2] = bb.data()[pos+2]; + buf[3] = bb.data()[pos+3]; + return buf; +} +bool copy(QByteArray & dst, uint to, QByteArray & src, uint from, uint len) +{ + if (!dst.data()) + return false; + if (!src.data()) + return false; + if (to + len > static_cast<uint>(dst.size())) + return false; + if (from + len > static_cast<uint>(src.size())) + return false; + + memcpy(dst.data() + to, src.data() + from, len); + return true; +} +} //namespace + +ScFace_ttf::ScFace_ttf ( QString fam, QString sty, QString alt, QString scname, QString psname, QString path, int face ) + : FtFace ( fam, sty, alt, scname, psname, path, face ) +{ + formatCode = ScFace::SFNT; + kernFeature = 0; +} + +ScFace_ttf::~ ScFace_ttf() +{ + if ( kernFeature ) + delete kernFeature; +} + + +void ScFace_ttf::load() const +{ + if ( !kernFeature ) + kernFeature = new KernFeature ( ftFace() ); + FtFace::load(); +} + +void ScFace_ttf::unload() const +{ + if ( kernFeature ) + delete kernFeature; + kernFeature = 0; + FtFace::unload(); +} + +qreal ScFace_ttf::glyphKerning ( uint gl1, uint gl2, qreal sz ) const +{ + if ( kernFeature->isValid() ) + return kernFeature->getPairValue ( gl1,gl2 ) / m_uniEM * sz; + return FtFace::glyphKerning ( gl1, gl2, sz ); +} + +void ScFace_ttf::RawData(QByteArray & bb) const { + if (formatCode == ScFace::TTCF) { + QByteArray coll; + FtFace::RawData(coll); + // access table for faceIndex + if (faceIndex >= static_cast<int>(word(coll, 8))) + { + bb.resize(0); + return; + } + static const uint OFFSET_TABLE_LEN = 12; + static const uint TDIR_ENTRY_LEN = 16; + uint faceOffset = word(coll, 12 + 4 * faceIndex); + uint nTables = word16(coll, faceOffset + 4); + sDebug(QObject::tr("extracting face %1 from font %2 (offset=%3, nTables=%4)").arg(faceIndex).arg(fontFile).arg(faceOffset).arg(nTables)); + uint headerLength = OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * nTables; + uint tableLengths = 0; + // sum table lengths incl padding + for (uint i=0; i < nTables; ++i) + { + tableLengths += word(coll, faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 12); + tableLengths = (tableLengths+3) & ~3; + } + bb.resize(headerLength + tableLengths); + if (! bb.data()) + return; + // write header + sDebug(QObject::tr("memcpy header: %1 %2 %3").arg(0).arg(faceOffset).arg(headerLength)); + if (!copy(bb, 0, coll, faceOffset, headerLength)) + return; + + uint pos = headerLength; + for (uint i=0; i < nTables; ++i) + { + uint tableSize = word(coll, faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 12); + uint tableStart = word(coll, faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 8); + sDebug(QObject::tr("table '%1'").arg(tag(coll, tableStart))); + sDebug(QObject::tr("memcpy table: %1 %2 %3").arg(pos).arg(tableStart).arg(tableSize)); + if (!copy(bb, pos, coll, tableStart, tableSize)) break; + // write new offset to table entry + sDebug(QObject::tr("memcpy offset: %1 %2 %3").arg(OFFSET_TABLE_LEN + TDIR_ENTRY_LEN*i + 8).arg(pos).arg(4)); + memcpy(bb.data() + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 8, &pos, 4); + pos += tableSize; + // pad + while ((pos & 3) != 0) + bb.data()[pos++] = '\0'; + } + } + else if (formatCode == ScFace::TYPE42) { + FtFace::RawData(bb); + } + else { + FtFace::RawData(bb); + } +} + +bool ScFace_ttf::EmbedFont(QString &str) const +{ + if (formatCode == ScFace::TYPE42) { + //easy: + QByteArray bb; + FtFace::RawData(bb); + str += bb; + return true; + } + QString tmp4; + QString tmp2 = ""; + QString tmp3 = ""; + int counter = 0; + char *buf[50]; + FT_ULong charcode; + FT_UInt gindex; + FT_Face face = ftFace(); + if (!face) { + return false; + } + const FT_Stream fts = face->stream; + if (ftIOFunc(fts, 0L, NULL, 0)) { + return(false); + } + str+="%!PS-TrueTypeFont\n"; + str+="11 dict begin\n"; + str+="/FontName /" + psName + " def\n"; + str+="/Encoding /ISOLatin1Encoding where {pop ISOLatin1Encoding} {StandardEncoding} ifelse def\n"; + str+="/PaintType 0 def\n/FontMatrix [1 0 0 1 0 0] def\n"; + str+="/FontBBox ["+FontBBox+"] def\n"; + str+="/FontType 42 def\n"; + str+="/FontInfo 8 dict dup begin\n"; + str+="/FamilyName (" + psName + ") def\n"; + str+="end readonly def\n"; + unsigned char *tmp = new unsigned char[65535]; + int length; + char linebuf[80]; + str += "/sfnts ["; + int poso=0; + do { + int posi=0; + length= fts->size - fts->pos; + if (length > 65534) { + length = 65534; + } + if (!ftIOFunc(fts, 0L, tmp, length)) + { + str+="\n<\n"; + for (int j = 0; j < length; j++) + { + unsigned char u=tmp[posi]; + linebuf[poso]=((u >> 4) & 15) + '0'; + if(u>0x9f) linebuf[poso]+='a'-':'; + ++poso; + u&=15; linebuf[poso]=u + '0'; + if(u>0x9) linebuf[poso]+='a'-':'; + ++posi; + ++poso; + if (poso > 70) + { + linebuf[poso++]='\n'; + linebuf[poso++]=0; + str += linebuf; + poso = 0; + } + } + linebuf[poso++]=0; + str += linebuf; + poso = 0; + str += "00\n>"; + } + else { + sDebug(QObject::tr("Font %1 is broken (read stream), no embedding").arg(fontFile)); + str += "\n] def\n"; + status = qMax(status,ScFace::BROKENGLYPHS); + return false; + } + } while (length==65534); + + str += "\n] def\n"; + delete[] tmp; + gindex = 0; + charcode = FT_Get_First_Char(face, &gindex ); + while (gindex != 0) + { + FT_Get_Glyph_Name(face, gindex, buf, 50); + tmp2 += "/"+QString(reinterpret_cast<char*>(buf))+" "+tmp3.setNum(gindex)+" def\n"; + charcode = FT_Get_Next_Char(face, charcode, &gindex ); + counter++; + } + tmp4.setNum(counter); + str += "/CharStrings " + tmp4 + " dict dup begin\n"+tmp2; + str += "end readonly def\n"; + str += "FontName currentdict end definefont pop\n"; + return(true); +} + diff --git a/scribus/fonts/scface_ttf.h b/scribus/fonts/scface_ttf.h new file mode 100644 index 0000000..dc57b33 --- /dev/null +++ b/scribus/fonts/scface_ttf.h @@ -0,0 +1,105 @@ +/* +For general Scribus (>=1.3.2) copyright and licensing information please refer +to the COPYING file provided with the program. Following this notice may exist +a copyright and/or license notice that predates the release of Scribus 1.3.2 +for which a new license (GPL+exception) is in place. +*/ +#ifndef SCFACE_TTF_H +#define SCFACE_TTF_H + +#include "scribusapi.h" +#include "fonts/ftface.h" + + +#include FT_TRUETYPE_TABLES_H +#include FT_TRUETYPE_TAGS_H + + +/** + An object holding a table of kerning pairs extracted from + a kern feature such as found in a GPOS table + */ +class SCRIBUS_API KernFeature +{ + typedef QMap<quint16, QList<quint16> > ClassDefTable; // <Class index (0 to N) , list of glyphs > + + public: + /** + * Build a ready-to-use kerning pairs table + * @param face a valid FT_Face, It won’t be store by KernFeature + */ + KernFeature ( FT_Face face ); + KernFeature ( const KernFeature& kf ); + ~KernFeature(); + + /** + * Get the kerning value for a pair of glyph indexes. + * @param glyph1 Index of the left glyph in logical order + * @param glyph2 Index of the right glyph in logical order + * @return the unscaled delta to apply to xadvance of the first glyph + */ + double getPairValue ( unsigned int glyph1, unsigned int glyph2 ) const; + + /** + * The table can have been invalidated if something went wrong at any moment. + * @return True if valid, False otherwise. + */ + bool isValid() const {return m_valid;} + + private: + bool m_valid; + QByteArray GPOSTableRaw; + QMap<quint16,QList<quint16> > coverages; + mutable QMap<quint16, QMap<quint16, double> > pairs; + QMap< quint16, QMap<quint16, ClassDefTable> > classGlyphFirst; // < subtable offset, map<offset, class definition table> > for first glyph + QMap< quint16, QMap<quint16, ClassDefTable> > classGlyphSecond; // < subtable offset, map<offset, class definition table> > for second glyph + QMap< quint16, QMap<int, QMap<int, double> > > classValue; // < subtable offset, map<class1, map<class2, value> > > + + void makeCoverage(); + void makePairs ( quint16 subtableOffset ); + + ClassDefTable getClass (bool leftGlyph, quint16 classDefOffset, quint16 coverageId ); + inline quint16 toUint16 ( quint16 index ); + inline qint16 toInt16 ( quint16 index ); + + enum ValueFormat + { + XPlacement = 0x0001, + YPlacement = 0x0002, + XAdvance = 0x0004, + YAdvance = 0x0008, + XPlaDevice =0x0010, + YPlaDevice =0x0020, + XAdvDevice =0x0040, + YAdvDevice =0x0080 + }; + QString FontName;// for debugging purpose +}; + + + +/* + Class ScFace_ttf + Subclass of ScFace, specifically for TrueType fonts. + Implements: RealName() and EmbedFont(). +*/ + +class SCRIBUS_API ScFace_ttf : public FtFace +{ +public: + ScFace_ttf ( QString fam, QString sty, QString alt, QString scname, QString psname, QString path, int face ); + ~ScFace_ttf(); + + void load () const; + void unload () const; + + bool EmbedFont(QString &str) const; + void RawData(QByteArray & bb) const; + + qreal glyphKerning ( uint gl1, uint gl2, qreal sz ) const; + +private: + mutable KernFeature * kernFeature; +}; + +#endif diff --git a/scribus/fonts/scfontmetrics.cpp b/scribus/fonts/scfontmetrics.cpp new file mode 100644 index 0000000..8706361 --- /dev/null +++ b/scribus/fonts/scfontmetrics.cpp @@ -0,0 +1,623 @@ +/* +For general Scribus (>=1.3.2) copyright and licensing information please refer +to the COPYING file provided with the program. Following this notice may exist +a copyright and/or license notice that predates the release of Scribus 1.3.2 +for which a new license (GPL+exception) is in place. +*/ +#include <QColor> +#include <QDebug> +#include <QMap> +#include <QMatrix> +#include <QPainter> +#include <QPixmap> +#include <QRegExp> +#include <QStringList> + +#include "fpoint.h" +#include "fpointarray.h" +#include "page.h" +#include "scfontmetrics.h" +#include "scfonts.h" +#include "scpainter.h" +#include "scribusdoc.h" +#include "style.h" +#include "util_math.h" + + +// this code contains a set of font related functions +// that don't really fit within ScFonts. + +static FPoint firstP; +static bool FirstM; +static QMap<FT_ULong, QString> adobeGlyphNames; +static const char* table[] = { +//#include "glyphnames.txt.q" + NULL}; + +// private functions +static void readAdobeGlyphNames(); +static QString adobeGlyphName(FT_ULong charcode); +static int traceMoveto( FT_Vector *to, FPointArray *composite ); +static int traceLineto( FT_Vector *to, FPointArray *composite ); +static int traceQuadraticBezier( FT_Vector *control, FT_Vector *to, FPointArray *composite ); +static int traceCubicBezier( FT_Vector *p, FT_Vector *q, FT_Vector *to, FPointArray *composite ); + +FT_Outline_Funcs OutlineMethods = + { + (FT_Outline_MoveTo_Func) traceMoveto, + (FT_Outline_LineTo_Func) traceLineto, + (FT_Outline_ConicTo_Func) traceQuadraticBezier, + (FT_Outline_CubicTo_Func) traceCubicBezier, + 0, + 0 + }; + + +const qreal FTSCALE = 64.0; + + +int setBestEncoding(FT_Face face) +{ + FT_ULong charcode; + FT_UInt gindex; + bool foundEncoding = false; + int countUniCode = 0; + int chmapUniCode = -1; + int chmapCustom = -1; + int retVal = 0; + //FT_CharMap defaultEncoding = face->charmap; +// int defaultchmap=face->charmap ? FT_Get_Charmap_Index(face->charmap) : 0; +// Since the above function is only available in FreeType 2.1.10 its replaced by +// the following line, assuming that the default charmap has the index 0 + int defaultchmap = 0; + for(int u = 0; u < face->num_charmaps; u++) + { + if (face->charmaps[u]->encoding == FT_ENCODING_UNICODE ) + { + FT_Set_Charmap(face, face->charmaps[u]); + chmapUniCode = u; + gindex = 0; + charcode = FT_Get_First_Char( face, &gindex ); + while ( gindex != 0 ) + { + countUniCode++; + charcode = FT_Get_Next_Char( face, charcode, &gindex ); + } +// qDebug() << "found Unicode enc for" << face->family_name << face->style_name << "as map" << chmapUniCode << "with" << countUniCode << "glyphs"; + + } + if (face->charmaps[u]->encoding == FT_ENCODING_ADOBE_CUSTOM) + { + chmapCustom = u; + foundEncoding = true; + retVal = 1; +// qDebug() << "found Custom enc for" << face->family_name << face->style_name; + break; + } + else if (face->charmaps[u]->encoding == FT_ENCODING_MS_SYMBOL) + { +// qDebug() << "found Symbol enc for" << face->family_name << face->style_name; + + chmapCustom = u; + foundEncoding = true; + retVal = 2; + break; + } + } + int mapToSet=defaultchmap; + if (chmapUniCode >= 0 && countUniCode >= face->num_glyphs-1) + { +// qDebug() << "using Unicode enc for" << face->family_name << face->style_name; + + mapToSet=chmapUniCode; + //FT_Set_Charmap(face, face->charmaps[chmapUniCode]); + retVal = 0; + } + else + if (foundEncoding) + { +// qDebug() << "using special enc for" << face->family_name << face->style_name; + mapToSet=chmapCustom; + //FT_Set_Charmap(face, face->charmaps[chmapCustom]); + } + else + { +// qDebug() << "using default enc for" << face->family_name << face->style_name; + mapToSet=defaultchmap; + //FT_Set_Charmap(face, defaultEncoding); + retVal = 0; + } + + //Fixes #2199, missing glyphs from 1.2.1->1.2.2 + //If the currently wanted character map is not already Unicode... + //if (FT_Get_Charmap_Index(face->charmap)!=chmapUniCode) + if (mapToSet!=chmapUniCode) + { + //Change map so we can count the chars in it + FT_Set_Charmap(face, face->charmaps[mapToSet]); + //Count the characters in the current map + gindex = 0; + int countCurrMap=0; + charcode = FT_Get_First_Char( face, &gindex ); + while ( gindex != 0 ) + { + countCurrMap++; + charcode = FT_Get_Next_Char( face, charcode, &gindex ); + } + //If the last Unicode map we found before has more characters, + //then set it to be the current map. + + if (countUniCode>countCurrMap) + { +// qDebug() << "override with Unicode enc for" << face->family_name << face->style_name << "map" << mapToSet << "has only" << countCurrMap << "glyphs"; + mapToSet=chmapUniCode; + //FT_Set_Charmap(face, face->charmaps[chmapUniCode]); + retVal = 0; + } + } +// if (face->charmap == NULL || mapToSet!=FT_Get_Charmap_Index(face->charmap)) + FT_Set_Charmap(face, face->charmaps[mapToSet]); +// qDebug() << "set map" << mapToSet << "for" << face->family_name << face->style_name; +// qDebug() << "glyphsForNumbers 0-9:" << FT_Get_Char_Index(face, QChar('0').unicode()) +// << FT_Get_Char_Index(face, QChar('1').unicode()) << FT_Get_Char_Index(face, QChar('2').unicode()) << FT_Get_Char_Index(face, QChar('3').unicode()) +// << FT_Get_Char_Index(face, QChar('4').unicode()) << FT_Get_Char_Index(face, QChar('5').unicode()) << FT_Get_Char_Index(face, QChar('6').unicode()) +// << FT_Get_Char_Index(face, QChar('7').unicode()) << FT_Get_Char_Index(face, QChar('8').unicode()) << FT_Get_Char_Index(face, QChar('9').unicode()); + return retVal; +} + +FPointArray traceGlyph(FT_Face face, FT_UInt glyphIndex, int chs, qreal *x, qreal *y, bool *err) +{ + bool error = false; + //AV: not threadsave, but tracechar is only used in ReadMetrics() and fontSample() + static FPointArray pts; + FPointArray pts2; + pts.resize(0); + pts2.resize(0); + firstP = FPoint(0,0); + FirstM = true; + error = FT_Set_Char_Size( face, 0, chs*6400, 72, 72 ); + if (error) + { + *err = error; + return pts2; + } + if (glyphIndex == 0) + { + *err = true; + return pts2; + } + error = FT_Load_Glyph( face, glyphIndex, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP ); + if (error) + { + *err = error; + return pts2; + } + error = FT_Outline_Decompose(&face->glyph->outline, &OutlineMethods, reinterpret_cast<void*>(&pts)); + if (error) + { + *err = error; + return pts2; + } + *x = face->glyph->metrics.horiBearingX / 6400.0; + *y = face->glyph->metrics.horiBearingY / 6400.0; + QMatrix ma; + ma.scale(0.01, -0.01); + pts.map(ma); + pts.translate(0, chs); + pts2.putPoints(0, pts.size()-2, pts, 0); + + return pts2; +} + + +FPointArray traceChar(FT_Face face, uint chr, int chs, qreal *x, qreal *y, bool *err) +{ + bool error = false; + FT_UInt glyphIndex; + error = FT_Set_Char_Size( face, 0, chs*64, 72, 72 ); + if (error) + { + *err = error; + return FPointArray(); + } + glyphIndex = FT_Get_Char_Index(face, chr); + return traceGlyph(face, glyphIndex, chs, x, y, err); +} + + +QPixmap FontSample(const ScFace& fnt, int s, QString ts, QColor back, bool force) +{ + FT_Face face; + FT_Library library; + qreal x, y, ymax; + bool error; + int pen_x; + FPoint gp; + error = FT_Init_FreeType( &library ); + error = FT_New_Face( library, QFile::encodeName(fnt.fontFilePath()), fnt.faceIndex(), &face ); + int encode = setBestEncoding(face); + qreal uniEM = static_cast<qreal>(face->units_per_EM); + + qreal m_descent = face->descender / uniEM; + qreal m_height = face->height / uniEM; + if (m_height == 0) + m_height = (face->bbox.yMax - face->bbox.yMin) / uniEM; + + int h = qRound(m_height * s) + 1; + qreal a = m_descent * s + 1; + int w = qRound((face->bbox.xMax - face->bbox.xMin) / uniEM) * s * (ts.length()+1); + if (w < 1) + w = s * (ts.length()+1); + if (h < 1) + h = s; + QImage pm(w, h, QImage::Format_ARGB32); + pen_x = 0; + ymax = 0.0; + ScPainter *p = new ScPainter(&pm, pm.width(), pm.height()); + p->clear(back); + p->setFillMode(1); + p->setLineWidth(0.0); +// p->setBrush(back); +// p->drawRect(0.0, 0.0, static_cast<qreal>(w), static_cast<qreal>(h)); + p->setBrush(Qt::black); + FPointArray gly; + uint dv; + dv = ts[0].unicode(); + error = false; + gly = traceChar(face, dv, s, &x, &y, &error); + if (((encode != 0) || (error)) && (!force)) + { + error = false; + FT_ULong charcode; + FT_UInt gindex; + gindex = 0; + charcode = FT_Get_First_Char(face, &gindex ); + for (int n = 0; n < ts.length(); ++n) + { + gly = traceChar(face, charcode, s, &x, &y, &error); + if (error) + break; + if (gly.size() > 3) + { + gly.translate(static_cast<qreal>(pen_x) / 6400.0, a); + gp = getMaxClipF(&gly); + ymax = qMax(ymax, gp.y()); + p->setupPolygon(&gly); + p->fillPath(); + } + pen_x += face->glyph->advance.x; + charcode = FT_Get_Next_Char(face, charcode, &gindex ); + if (gindex == 0) + break; + } + } + else + { + for (int n = 0; n < ts.length(); ++n) + { + dv = ts[n].unicode(); + error = false; + gly = traceChar(face, dv, s, &x, &y, &error); + if (gly.size() > 3) + { + gly.translate(static_cast<qreal>(pen_x) / 6400.0, a); + gp = getMaxClipF(&gly); + ymax = qMax(ymax, gp.y()); + p->setupPolygon(&gly); + p->fillPath(); + } + pen_x += face->glyph->advance.x; + } + } + p->end(); + QPixmap pmr; + pmr=QPixmap::fromImage(pm.copy(0, 0, qMin(qRound(gp.x()), w), qMin(qRound(ymax), h))); +// this one below gives some funny results +// pmr.convertFromImage(pm.scaled(qMin(qRound(gp.x()), w), qMin(qRound(ymax), h), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); +// pmr.resize(qMin(qRound(gp.x()), w), qMin(qRound(ymax), h)); + delete p; + FT_Done_FreeType( library ); + return pmr; +} + +/** Same as FontSample() with \n strings support added. +09/26/2004 petr vanek + +QPixmap fontSamples(ScFace * fnt, int s, QString ts, QColor back) +{ + QStringList lines = QStringList::split("\n", ts); + QPixmap ret(640, 480); + QPixmap sample; + QPainter *painter = new QPainter(&ret); + int y = 0; + int x = 0; + ret.fill(back); + for ( QStringList::Iterator it = lines.begin(); it != lines.end(); ++it ) + { + sample = FontSample(fnt, s, *it, back); + if (!sample.isNull()) + painter->drawPixmap(0, y, sample, 0, 0); + y = y + sample.height(); + if (x < sample.width()) + x = sample.width(); + } // for + delete(painter); + QPixmap final(x, y); + if ((x != 0) && (y != 0)) + { + QPainter *fpainter = new QPainter(&final); + fpainter->drawPixmap(0, 0, ret, 0, 0, x, y); + delete(fpainter); + } + return final; +} +*/ + +bool GlyNames(FT_Face face, QMap<uint, std::pair<QChar, QString> >& GList) +{ + char buf[50]; + FT_ULong charcode; + FT_UInt gindex; + setBestEncoding(face); + gindex = 0; + charcode = FT_Get_First_Char(face, &gindex ); + const bool hasPSNames = FT_HAS_GLYPH_NAMES(face); + if (adobeGlyphNames.empty()) + readAdobeGlyphNames(); +// qDebug() << "reading metrics for" << face->family_name << face->style_name; + while (gindex != 0) + { + bool notfound = true; + if (hasPSNames) + notfound = FT_Get_Glyph_Name(face, gindex, &buf, 50); + + // just in case FT gives empty string or ".notdef" + // no valid glyphname except ".notdef" starts with '.' +// qDebug() << "\t" << gindex << " '" << charcode << "' --> '" << (notfound? "notfound" : buf) << "'"; + if (notfound || buf[0] == '\0' || buf[0] == '.') + GList.insert(gindex, std::make_pair(QChar(static_cast<uint>(charcode)),adobeGlyphName(charcode))); + else + GList.insert(gindex, std::make_pair(QChar(static_cast<uint>(charcode)),QString(reinterpret_cast<char*>(buf)))); + + charcode = FT_Get_Next_Char(face, charcode, &gindex ); + } + // Let's see if we can find some more... + int maxSlot1 = face->num_glyphs; + if (hasPSNames) + for (int gindex = 1; gindex < maxSlot1; ++gindex) + { + if (!GList.contains(gindex)) + { + bool found = ! FT_Get_Glyph_Name(face, gindex, &buf, 50); + if (found) + { + QString glyphname(reinterpret_cast<char*>(buf)); + charcode = 0; + QMap<uint,std::pair<QChar,QString> >::Iterator gli; + for (gli = GList.begin(); gli != GList.end(); ++gli) + { + if (glyphname == gli.value().second) + { + charcode = gli.value().first.unicode(); + break; + } + } +// qDebug() << "\tmore: " << gindex << " '" << charcode << "' --> '" << buf << "'"; + GList.insert(gindex, std::make_pair(QChar(static_cast<uint>(charcode)), glyphname)); + } + } + } + + return true; +} + + +static int traceMoveto( FT_Vector *to, FPointArray *composite ) +{ + qreal tox = ( to->x / FTSCALE ); + qreal toy = ( to->y / FTSCALE ); + if (!FirstM) + { + composite->addPoint(firstP); + composite->addPoint(firstP); + composite->setMarker(); + } + else + FirstM = false; + composite->addPoint(tox, toy); + composite->addPoint(tox, toy); + firstP.setXY(tox, toy); + return 0; +} + +static int traceLineto( FT_Vector *to, FPointArray *composite ) +{ + qreal tox = ( to->x / FTSCALE ); + qreal toy = ( to->y / FTSCALE ); + if ( !composite->hasLastQuadPoint(tox, toy, tox, toy, tox, toy, tox, toy)) + composite->addQuadPoint(tox, toy, tox, toy, tox, toy, tox, toy); + return 0; +} + +static int traceQuadraticBezier( FT_Vector *control, FT_Vector *to, FPointArray *composite ) +{ + qreal x1 = ( control->x / FTSCALE ); + qreal y1 = ( control->y / FTSCALE ); + qreal x2 = ( to->x / FTSCALE ); + qreal y2 = ( to->y / FTSCALE ); + if ( !composite->hasLastQuadPoint(x2, y2, x1, y1, x2, y2, x2, y2)) + composite->addQuadPoint(x2, y2, x1, y1, x2, y2, x2, y2); + return 0; +} + +static int traceCubicBezier( FT_Vector *p, FT_Vector *q, FT_Vector *to, FPointArray *composite ) +{ + qreal x1 = ( p->x / FTSCALE ); + qreal y1 = ( p->y / FTSCALE ); + qreal x2 = ( q->x / FTSCALE ); + qreal y2 = ( q->y / FTSCALE ); + qreal x3 = ( to->x / FTSCALE ); + qreal y3 = ( to->y / FTSCALE ); + if ( !composite->hasLastQuadPoint(x3, y3, x2, y2, x3, y3, x3, y3) ) + { + composite->setPoint(composite->size()-1, FPoint(x1, y1)); + composite->addQuadPoint(x3, y3, x2, y2, x3, y3, x3, y3); + } + return 0; +} + +/// init the Adobe Glyph List +void readAdobeGlyphNames() +{ + adobeGlyphNames.clear(); + QRegExp pattern("(\\w*);([0-9A-Fa-f]{4})"); + for (uint i=0; table[i]; ++i) { + if (pattern.indexIn(table[i]) >= 0) { + FT_ULong unicode = pattern.cap(2).toULong(0, 16); + qDebug() << QString("reading glyph name %1 for unicode %2(%3)").arg(pattern.cap(1)).arg(unicode).arg(pattern.cap(2)); + adobeGlyphNames.insert(unicode, pattern.cap(1)); + } + } +} + + +/// if in AGL, use that name, else use "uni1234" or "u12345" +QString adobeGlyphName(FT_ULong charcode) +{ + static const char HEX[] = "0123456789ABCDEF"; + QString result; + if (adobeGlyphNames.contains(charcode)) + return adobeGlyphNames[charcode]; + else if (charcode < 0x10000) { + result = QString("uni") + HEX[charcode>>12 & 0xF] + + HEX[charcode>> 8 & 0xF] + + HEX[charcode>> 4 & 0xF] + + HEX[charcode & 0xF]; + } + else { + result = QString("u"); + for (int i= 28; i >= 0; i-=4) { + if (charcode & (0xF << i)) + result += HEX[charcode >> i & 0xF]; + } + } + return result; +} + +/* +qreal Cwidth(ScribusDoc *, ScFace* scFace, QString ch, int Size, QString ch2) +{ + qreal width; + FT_Vector delta; + FT_Face face; + uint c1 = ch.at(0).unicode(); + uint c2 = ch2.at(0).unicode(); + qreal size10=Size/10.0; + if (scFace->canRender(ch[0])) + { + width = scFace->charWidth(ch[0])*size10; + face = scFace->ftFace(); + /\**** + Ok, this looks like a regression between Freetype 2.1.9 -> 2.1.10. + Ignoring the value of FT_HAS_KERNING for now -- AV + ****\/ + if (true || FT_HAS_KERNING(face) ) + { + uint cl = FT_Get_Char_Index(face, c1); + uint cr = FT_Get_Char_Index(face, c2); + FT_Error error = FT_Get_Kerning(face, cl, cr, FT_KERNING_UNSCALED, &delta); + if (error) { + qDebug() << QString("Error %2 when accessing kerning pair for font %1").arg(scFace->scName()).arg(error); + } + else { + qreal uniEM = static_cast<qreal>(face->units_per_EM); + width += delta.x / uniEM * size10; + } + } + else { + qDebug() << QString("Font %1 has no kerning pairs (according to Freetype)").arg(scFace->scName()); + } + return width; + } + else + return size10; +} + +qreal RealCWidth(ScribusDoc *, ScFace* scFace, QString ch, int Size) +{ + qreal w, ww; + uint c1 = ch.at(0).unicode(); + FT_Face face; + if (scFace->canRender(ch.at(0))) + { + face = scFace->ftFace(); + uint cl = FT_Get_Char_Index(face, c1); + int error = FT_Load_Glyph(face, cl, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ); + if (!error) { + qreal uniEM = static_cast<qreal>(face->units_per_EM); + w = (face->glyph->metrics.width + fabs((qreal)face->glyph->metrics.horiBearingX)) / uniEM * (Size / 10.0); + ww = face->glyph->metrics.horiAdvance / uniEM * (Size / 10.0); + return qMax(ww, w); + } + else + sDebug(QString("internal error: missing glyph: %1 (char %2) error=%3").arg(c1).arg(ch).arg(error)); + + } + return static_cast<qreal>(Size / 10.0); +} + +qreal RealCHeight(ScribusDoc *, ScFace* scFace, QString ch, int Size) +{ + qreal w; + uint c1 = ch.at(0).unicode(); + FT_Face face; + if (scFace->canRender(ch.at(0))) + { + face = scFace->ftFace(); + uint cl = FT_Get_Char_Index(face, c1); + int error = FT_Load_Glyph(face, cl, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ); + if (!error) { + qreal uniEM = static_cast<qreal>(face->units_per_EM); + w = face->glyph->metrics.height / uniEM * (Size / 10.0); + } + else { + sDebug(QString("internal error: missing glyph: %1 (char %2) error=%3").arg(c1).arg(ch).arg(error)); + w = Size / 10.0; + } + return w; + } + else + return static_cast<qreal>(Size / 10.0); +} + +qreal RealCAscent(ScribusDoc *, ScFace* scFace, QString ch, int Size) +{ + qreal w; + uint c1 = ch.at(0).unicode(); + FT_Face face; + if (scFace->canRender(ch.at(0))) + { + face = scFace->ftFace(); + uint cl = FT_Get_Char_Index(face, c1); + int error = FT_Load_Glyph(face, cl, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ); + if (! error) { + qreal uniEM = static_cast<qreal>(face->units_per_EM); + w = face->glyph->metrics.horiBearingY / uniEM * (Size / 10.0); + } + else { + sDebug(QString("internal error: missing glyph: %1 (char %2) error=%3").arg(c1).arg(ch).arg(error)); + w = Size / 10.0; + } + return w; + } + else + return static_cast<qreal>(Size / 10.0); +} + +qreal RealFHeight(ScribusDoc *, ScFace* scFace, int Size) +{ + FT_Face face = scFace->ftFace(); + qreal uniEM = static_cast<qreal>(face->units_per_EM); + return face->height / uniEM * (Size / 10.0); +} +*/ diff --git a/scribus/fonts/scfontmetrics.h b/scribus/fonts/scfontmetrics.h new file mode 100644 index 0000000..0440325 --- /dev/null +++ b/scribus/fonts/scfontmetrics.h @@ -0,0 +1,37 @@ +/* +For general Scribus (>=1.3.2) copyright and licensing information please refer +to the COPYING file provided with the program. Following this notice may exist +a copyright and/or license notice that predates the release of Scribus 1.3.2 +for which a new license (GPL+exception) is in place. +*/ + +#ifndef SCFONTMETRICS_H +#define SCFONTMETRICS_H + +#include <utility> +#include <QGlobalStatic> +#include <QString> +#include <QColor> +//Added by qt3to4: +#include <QPixmap> + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_GLYPH_H + +#include "scribusapi.h" +#include "fpoint.h" +#include "fpointarray.h" + +class ScFace; +class Scribusdoc; + +int SCRIBUS_API setBestEncoding(FT_Face face); +FPointArray SCRIBUS_API traceChar(FT_Face face, uint chr, int chs, qreal *x, qreal *y, bool *err); +FPointArray SCRIBUS_API traceGlyph(FT_Face face, uint chr, int chs, qreal *x, qreal *y, bool *err); +QPixmap SCRIBUS_API FontSample(const ScFace& fnt, int s, QString ts, QColor back, bool force = false); +//QPixmap SCRIBUS_API fontSamples(const ScFace& fnt, int s, QString ts, QColor back); +bool SCRIBUS_API GlyNames(FT_Face face, QMap<uint, std::pair<QChar, QString> >& GList); + +#endif |
