summaryrefslogtreecommitdiffstats
path: root/scribus/fonts/scface.h
blob: 82815054dd8e4e5b90e15f4340ce60b8857057db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
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