summaryrefslogtreecommitdiffstats
path: root/scribus/fonts
diff options
context:
space:
mode:
authorcraig <craig@11d20701-8431-0410-a711-e3c959e3b870>2012-01-01 11:40:09 +0000
committercraig <craig@11d20701-8431-0410-a711-e3c959e3b870>2012-01-01 11:40:09 +0000
commit7ed83b6c6666eb8b6b104c211ae7e52907350372 (patch)
tree4430b556abac0ad660a0aacf1887d77f85d8be02 /scribus/fonts
downloadscribus-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.txt22
-rw-r--r--scribus/fonts/ftface.cpp332
-rw-r--r--scribus/fonts/ftface.h121
-rw-r--r--scribus/fonts/scface.cpp460
-rw-r--r--scribus/fonts/scface.h391
-rw-r--r--scribus/fonts/scface_ps.cpp114
-rw-r--r--scribus/fonts/scface_ps.h222
-rw-r--r--scribus/fonts/scface_ttf.cpp662
-rw-r--r--scribus/fonts/scface_ttf.h105
-rw-r--r--scribus/fonts/scfontmetrics.cpp623
-rw-r--r--scribus/fonts/scfontmetrics.h37
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