1 /********************************************************************************
2 * *
3 * P o s t c r i p t F o n t O b j e c t *
4 * *
5 *********************************************************************************
6 * Copyright (C) 1997,2003 by Jeroen van der Zijp. All Rights Reserved. *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or *
9 * modify it under the terms of the GNU Lesser General Public *
10 * License as published by the Free Software Foundation; *
11 * version 2.1 of the License. *
12 * *
13 * This library is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
16 * Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public *
19 * License along with this library; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
21 *
22 * I would have released this updated version under the FOX license addendum *
23 * that permits static linking, but its terms make that infeasible so this is *
24 * JUST under LGPL *
25 *********************************************************************************
26 * $Id: FXPostscriptFont.cpp 3297 2015-12-14 20:30:04Z arthurcnorman $ *
27 ********************************************************************************/
28
29
30 // This file was written by A C Norman based on FXFont.c, for which the
31 // above copyright etc apply. The "FOX addendum" referred to can only apply to
32 // variants on FOX distributed by Jeroen, not to any modifications that he has
33 // not adopted, and hence does not apply here, thus this code is subject
34 // to just LGPL 2.1. Note that FXFont.cpp was under "LGPL 2.1 or later" but
35 // in making this derived work I choose just to select version 2.1.
36 //
37 // However as a special exception to LGPL 2.1 I grant permission for my code
38 // to be merged or linked with other code that is subject to LGPL version 3
39 // or GPL version 3. This provision does not represent permission to alter the
40 // license of my code to be that of LGPL 3 or GPL 3 - of itself and when
41 // removed from any LGPL 3 context it remains LGPL 2.1 and the freedom
42 // enshrined by that can not be reduced by adding in the additional
43 // constraints that LGPL 3 views as protections. However clearly the combined
44 // work that then includes my work would be subject to "3". But as per LGPL
45 // 2.1 (and the same would be true if I had used a BSD-style license here)
46 // notices explaining the license terms related to my code should not be
47 // removed. Anybody who changes or extends my code is permitted but not
48 // obliged to apply this exception, and perhaps by doing do they do not lock
49 // out (L)GPL 3 users but guarantee continued support for (L)GPL 2.1 in a way
50 // that the "or later" clause does not (since that permits anybody to
51 // unilaterally select just one version of the library to use, to the
52 // potential detriment of those whose choice differs).
53
54 /* $Id: FXPostscriptFont.cpp 3297 2015-12-14 20:30:04Z arthurcnorman $ */
55
56 #ifdef HAVE_CONFIG_H
57 #include "config.h"
58 #endif
59
60
61 #include <fx.h>
62 #include "FXPostscriptFont.h"
63 #include <ctype.h>
64
65 using namespace FX;
66
67 namespace FX {
68
69
70 /*******************************************************************************/
71
72
73 #ifdef WANTED_AND_ACTUAL
74 // A bunch of field-names changed between 1.1.45 and 1.1.49 so here for
75 // older releases I map onto the newer names. It may be that I should be a
76 // lot more carefull wrt actualXxx and wantedXxx.
77
78
79 // version 1.1.x is SO old now that I might properly disard this...
80
81 #define actualName name
82 #define actualSize size
83 #define actualWeight weight
84 #define actualSlant slant
85 #define actualEncoding encoding
86 #define actualSetwidth setwidth
87
88 #endif
89
90 // Object implementation
91 FXIMPLEMENT(FXPostscriptFont,FXFont,NULL,0)
92
93
94 // Deserialization
FXPostscriptFont()95 FXPostscriptFont::FXPostscriptFont(){
96 font=(void*)-1L;
97 }
98
99
100 // Construct font from X11 font spec. Well for Postscript Fonts
101 // I will not support decoding X11 long-names...
102
FXPostscriptFont(FXApp * a,const FXString & nm)103 FXPostscriptFont::FXPostscriptFont(FXApp* a,const FXString& nm):FXFont(a,nm){
104 FXTRACE((100,"FXPostscriptFont::FXPostscriptFont %p\n",this));
105 actualSize=0;
106 actualWeight=0;
107 actualSlant=0;
108 actualEncoding=FONTENCODING_DEFAULT;
109 #if (FOX_MINOR<=4)
110 actualSetwidth=FONTSETWIDTH_DONTCARE;
111 #else
112 actualSetwidth=0;
113 #endif
114 font=NULL;
115 }
116
117
118 // Construct a font with given face name, size in points(pixels), weight, slant, character set encoding, setwidth, and hints
FXPostscriptFont(FXApp * a,const FXString & face,FXuint sz,FXuint wt,FXuint sl,FXuint enc,FXuint setw,FXuint h)119 FXPostscriptFont::FXPostscriptFont(FXApp* a, const FXString &face, FXuint sz,
120 FXuint wt, FXuint sl, FXuint enc, FXuint setw, FXuint h):FXFont(a,face){
121 FXTRACE((100,"FXPostscriptFont::FXPostscriptFont %p\n",this));
122 actualSize=10*sz;
123 actualWeight=wt;
124 actualSlant=sl;
125 actualEncoding=enc;
126 actualSetwidth=setw;
127 #if (FOX_MINOR<=4)
128 hints=(h&~FONTHINT_X11);
129 #else
130 hints=(h&~FXFont::X11);
131 #endif
132 font=NULL;
133 }
134
135
136 // Construct font from font description
FXPostscriptFont(FXApp * a,const FXFontDesc & fontdesc)137 FXPostscriptFont::FXPostscriptFont(FXApp* a,
138 const FXFontDesc &fontdesc):FXFont(a, fontdesc){
139 FXTRACE((100,"FXPostscriptFont::FXPostscriptFont %p\n",this));
140 actualName=fontdesc.face;
141 actualSize=fontdesc.size;
142 actualWeight=fontdesc.weight;
143 actualSlant=fontdesc.slant;
144 actualEncoding=fontdesc.encoding;
145 actualSetwidth=fontdesc.setwidth;
146 hints=fontdesc.flags;
147 font=NULL;
148 }
149
150
151 /*******************************************************************************/
152
153
154
155
156 //
157 // When generating Postscript I need font metrics. These can be extracted
158 // from "afm" files that Adobe supply. I have a program "get-adobe-metrics.c"
159 // that can be run on a typical Linux system to extract the (minimal) metric
160 // information that I need here and build a file "font-info.cpp". By including
161 // that file here I collect info about all the standard Postscript Fonts.
162 // I will not permit a user to use any other fonts than these!
163 //
164
165
166 //
167 // There are 35 standard Postscript fonts that might be used with this
168 // print package. They are:
169 // AvantGarde-Book
170 // AvantGarde-BookOblique
171 // AvantGarde-Demi
172 // AvantGarde-DemiOblique
173 // Bookman-Demi
174 // Bookman-DemiItalic
175 // Bookman-Light
176 // Bookman-LightItalic
177 // * Courier-Bold
178 // * Courier-BoldOblique
179 // * Courier
180 // * Courier-Oblique
181 // * Helvetica-Bold
182 // * Helvetica-BoldOblique
183 // Helvetica-NarrowBold
184 // Helvetica-NarrowBoldOblique
185 // * Helvetica
186 // * Helvetica-Oblique
187 // Helvetica-Narrow
188 // Helvetica-NarrowOblique
189 // NewCenturySchlbk-Bold
190 // NewCenturySchlbk-BoldItalic
191 // NewCenturySchlbk-Italic
192 // NewCenturySchlbk-Roman
193 // Palatino-Bold
194 // Palatino-BoldItalic
195 // Palatino-Italic
196 // Palatino-Roman
197 // * Symbol
198 // * Times-Bold
199 // * Times-BoldItalic
200 // * Times-Italic
201 // * Times-Roman
202 // ZapfChancery-MediumItalic
203 // ZapfDingbats
204 // [those marked * are the original 13]
205 //
206 //
207
208 // Font metrics: I extract these from .afm files so that I can have
209 // rapid access to the information that I need throughout this package.
210 // The effect is that I fill a few kilobytes of memory with font tables.
211 // To avoid going over the top I will not keep per-character bearing
212 // information. The information in the font tables is based on having a
213 // nominal character cell that is 1000 units high, which means that
214 // integer metrics are quite accurate enough for everybody.
215
216 #include "font-info.cpp"
217
218 // Here is the sort of info found in "font-info.cpp". Each font
219 // listed comes with its full Adobe name.
220 // [It will shortly need (max)width, height, ascent, descent,
221 // leading, (max)left-bearing (max)right-bearing]
222 // The main data is then an array of 256 short integers. This contains
223 // -1 if the character is not present, otherwise the character width.
224 // Note once again that these numbers are all based on a nominal 1pt font
225 // with units of 1/1000. Another way to think of this is that numbers are
226 // in units of 1/72000in and will need to be multiplied by the point size
227 // of a font before use.
228 //
229 // typedef struct font_info {
230 // char *name;
231 // short int isfixed, fontwidth, maxleftbearing, maxrightbearing;
232 // short int capheight, xheight, ascent, descent;
233 // short int charwidth[256];
234 // } font_info;
235 //
236 // static font_info font_widths[] = {
237 // {"AvantGarde-Demi", 0, 1280, 233, 234, 740, 555, 740, -185, {
238 // -1, -1, -1, -1, -1, -1, -1, -1,
239 // ...
240 // -1, 874, 760, 946, 771, 865, 771, 888,
241 // 967, 888, 831, 873, 927, 970, 918, -1 }},
242 // {(char *)0, {}}};
243 //
244
find_metrics(const char * name)245 static font_info *find_metrics(const char *name)
246 {
247 font_info *p = font_widths;
248 while (p->name != NULL && strcmp(name, p->name)!=0) p++;
249 if (p->name != NULL) return p;
250 return &font_widths[0]; // this should never occur!
251 }
252
253
254 // Create font. In this case I take that to mean the process of refining
255 // the font name and fetching associated metrics.
256
257 enum family {
258 AvantGarde,
259 Bookman,
260 Courier,
261 Helvetica,
262 NewCenturySchlbk,
263 Palatino,
264 Symbol,
265 Times,
266 ZapfChancery,
267 ZapfDingbats
268 };
269
270 // return true of s has p as an initial sub-string, ignoring case
271
matches(const char * s,const char * p)272 static FXbool matches(const char *s, const char *p)
273 {
274 while (*p != 0)
275 { if (*s==0 || tolower(*s)!=tolower(*p)) return FALSE;
276 s++;
277 p++;
278 }
279 return TRUE;
280 }
281
282 // return true if s has p as a substring, ignoring case
283
contains(const char * s,const char * p)284 static FXbool contains(const char *s, const char *p)
285 {
286 while (*s != 0)
287 { if (matches(s, p)) return TRUE;
288 s++;
289 }
290 return FALSE;
291 }
292
create()293 void FXPostscriptFont::create()
294 {
295 // I will start by massaging the given name etc to provide me with one of the
296 // canonical Postscript names. Note that the tests on font families
297 // are done in an order so that later test acn override tentative decisions
298 // made by earlier ones. Eg the font with name "roman" will first be
299 // identified as asking for Times (roman, bold etc), but if the word
300 // "palatino" is see too then you get Palatino-Roman. If the name is
301 // not recognised at all you will get Times
302 family fam = Times; // a default family to use
303 const char *n = actualName.text();
304 #if (FOX_MINOR<=4)
305 if (contains(n, "bold")) actualWeight = FONTWEIGHT_BOLD;
306 if (contains(n, "light")) actualWeight = FONTWEIGHT_LIGHT;
307 if (contains(n, "italic")) actualSlant = FONTSLANT_ITALIC;
308 if (contains(n, "oblique")) actualSlant = FONTSLANT_OBLIQUE;
309 if (contains(n, "narrow")) actualSetwidth = FONTSETWIDTH_NARROW;
310 #else
311 if (contains(n, "bold")) actualWeight = FXFont::Bold;
312 if (contains(n, "light")) actualWeight = FXFont::Light;
313 if (contains(n, "italic")) actualSlant = FXFont::Italic;
314 if (contains(n, "oblique")) actualSlant = FXFont::Oblique;
315 if (contains(n, "narrow")) actualSetwidth = FXFont::Condensed;
316 #endif
317 if (contains(n, "times") ||
318 contains(n, "serif") ||
319 contains(n, "roman")) fam = Times;
320 if (contains(n, "avant")) fam = AvantGarde;
321 if (contains(n, "bookman")) fam = Bookman;
322 if (contains(n, "courier") ||
323 contains(n, "fixed")) fam = Courier;
324 if (contains(n, "helvet") ||
325 contains(n, "ariel") ||
326 contains(n, "sanserif") ||
327 contains(n, "swiss")) fam = Helvetica;
328 if (contains(n, "newcent")) fam = NewCenturySchlbk;
329 if (contains(n, "palatin")) fam = Palatino;
330 if (contains(n, "symbol")) fam = Symbol;
331 if (contains(n, "chancery") ||
332 contains(n, "zapf")) fam = ZapfChancery;
333 if (contains(n, "dingbat")) fam = ZapfDingbats;
334 // Now a few cases that override
335 #if (FOX_MINOR<=4)
336 if (actualSetwidth != FONTSETWIDTH_DONTCARE &&
337 actualSetwidth <= FONTSETWIDTH_COMPRESSED) fam = Helvetica;
338 if (hints & (FONTPITCH_FIXED|FONTHINT_MODERN)) fam = Courier;
339 else if ((hints & FONTPITCH_VARIABLE) && fam==Courier) fam = Helvetica;
340
341 #define four(reg, bold, ital, boldital) \
342 (actualWeight<=FONTWEIGHT_NORMAL ? \
343 (actualSlant<=FONTSLANT_REGULAR ? reg : ital) : \
344 (actualSlant<=FONTSLANT_REGULAR ? bold : boldital))
345 #else
346 if (actualSetwidth != 0 &&
347 actualSetwidth <= FXFont::Condensed) fam = Helvetica;
348 if (hints & (FXFont::Fixed|FXFont::Modern)) fam = Courier;
349 else if ((hints & FXFont::Variable) && fam==Courier) fam = Helvetica;
350
351 #define four(reg, bold, ital, boldital) \
352 (actualWeight<=FXFont::Normal ? \
353 (actualSlant<=FXFont::Straight ? reg : ital) : \
354 (actualSlant<=FXFont::Straight ? bold : boldital))
355 #endif
356 switch (fam)
357 {
358 case AvantGarde:
359 actualName = four("AvantGarde-Book", "AvantGarde-Demi",
360 "AvantGarde-BookOblique", "AvantGarde-DemiOblique");
361 break;
362 case Bookman:
363 actualName = four("Bookman-Light", "Bookman-Demi",
364 "Bookman-LightItalic", "Bookman-DemiItalic");
365 break;
366 case Courier:
367 actualName = four("Courier", "Courier-Bold",
368 "Courier-Oblique", "Courier-BoldOblique");
369 break;
370 case Helvetica:
371 #if (FOX_MINOR<=4)
372 if (actualSetwidth == FONTSETWIDTH_NARROW)
373 #else
374 if (actualSetwidth == FXFont::Condensed)
375 #endif
376 actualName = four("Helvetica-Narrowi", "Helvetica-NarrowBold",
377 "Helvetica-NarrowOblique", "Helvetica-NarrowBoldOblique");
378 else
379 actualName = four("Helvetica", "Helvetica-Bold",
380 "Helvetica-Oblique", "Helvetica-BoldOblique");
381 break;
382 case NewCenturySchlbk:
383 actualName = four("NewCenturySchlbk", "NewCenturySchlbk-Bold",
384 "NewCenturySchlbk-Italic", "NewCenturySchlbk-BoldItalic");
385 break;
386 case Palatino:
387 actualName = four("Palatino-Roman", "Palatino-Bold",
388 "Palatino-Italic", "Palatino-BoldItalic");
389 break;
390 case Symbol:
391 actualName = "Symbol";
392 #if (FOX_MINOR<=4)
393 actualWeight = FONTWEIGHT_NORMAL;
394 actualSlant = FONTSLANT_REGULAR;
395 #else
396 actualWeight = FXFont::Normal;
397 actualSlant = FXFont::Straight;
398 #endif
399 break;
400 case Times:
401 default:
402 actualName = four("Times-Roman", "Times-Bold",
403 "Times-Italic", "Times-BoldItalic");
404 break;
405 case ZapfChancery:
406 actualName = "ZapfChancery-MediumItalic";
407 #if (FOX_MINOR<=4)
408 actualWeight = FONTWEIGHT_NORMAL;
409 actualSlant = FONTSLANT_ITALIC;
410 #else
411 actualWeight = FXFont::Normal;
412 actualSlant = FXFont::Italic;
413 #endif
414 break;
415 case ZapfDingbats:
416 actualName = "ZapfDingbats";
417 #if (FOX_MINOR<=4)
418 actualWeight = FONTWEIGHT_NORMAL;
419 actualSlant = FONTSLANT_REGULAR;
420 #else
421 actualWeight = FXFont::Normal;
422 actualSlant = FXFont::Straight;
423 #endif
424 }
425 metrics = find_metrics(actualName.text());
426 }
427
428
429 // Does font have given character glyph?
hasChar(FXint ch) const430 FXbool FXPostscriptFont::hasChar(FXint ch) const
431 {
432 return metrics->charwidth[ch & 0xff] != -1;
433 }
434
435
436 // Get first character glyph in font
437 #if (FOX_MINOR<=4)
getMinChar() const438 FXint FXPostscriptFont::getMinChar() const
439 #else
440 FXwchar FXPostscriptFont::getMinChar() const
441 #endif
442 {
443 int i;
444 for (i=0; i<256; i++)
445 if (metrics->charwidth[i] != -1) return i;
446 return 256;
447 }
448
449
450 // Get last character glyph in font
451 #if (FOX_MINOR<=4)
getMaxChar() const452 FXint FXPostscriptFont::getMaxChar() const
453 #else
454 FXwchar FXPostscriptFont::getMaxChar() const
455 #endif
456 {
457 int i;
458 for (i=255; i>=0; i--)
459 if (metrics->charwidth[i] != -1) return i;
460 return 0;
461 }
462
463
464 // Get font leading [that is lead-ing as in Pb!]
getFontLeading() const465 FXint FXPostscriptFont::getFontLeading() const
466 {
467 // I use 0.2 of the point-size as my leading. Point sizes are stored in
468 // decipoints. I return a value in millipoints.
469 return (200/10)*actualSize;
470 }
471
472
473 // Get font line spacing [height+leading]
getFontSpacing() const474 FXint FXPostscriptFont::getFontSpacing() const
475 {
476 return (1200/10)*actualSize;
477 }
478
479
480 // Left bearing
leftBearing(FXchar ch) const481 FXint FXPostscriptFont::leftBearing(FXchar ch) const
482 {
483 return (metrics->maxleftbearing*actualSize)/10;
484 }
485
486
487 // Right bearing
rightBearing(FXchar ch) const488 FXint FXPostscriptFont::rightBearing(FXchar ch) const
489 {
490 return (metrics->maxrightbearing*actualSize)/10;
491 }
492
493
494 // Is it a mono space font
isFontMono() const495 FXbool FXPostscriptFont::isFontMono() const
496 {
497 return metrics->isfixed;
498 }
499
500
501 // Get font width
getFontWidth() const502 FXint FXPostscriptFont::getFontWidth() const
503 {
504 return (metrics->fontwidth*actualSize)/10;
505 }
506
507
508 // Get font height
getFontHeight() const509 FXint FXPostscriptFont::getFontHeight() const
510 {
511 return (1000/10)*actualSize;
512 }
513
514
515 // Get font ascent
getFontAscent() const516 FXint FXPostscriptFont::getFontAscent() const
517 {
518 return (metrics->ascent*actualSize)/10;
519 }
520
521
522 // Get font descent
getFontDescent() const523 FXint FXPostscriptFont::getFontDescent() const
524 {
525 return (metrics->descent*actualSize)/10;
526 }
527
528
529 // Text width
getTextWidth(const FXchar * text,FXuint n) const530 FXint FXPostscriptFont::getTextWidth(const FXchar *text, FXuint n) const
531 {
532 int w = 0;
533 while (*text != 0 && (n--)!=0) // stop on null char as well as at length
534 { w += metrics->charwidth[*text & 0xff];
535 text++;
536 }
537 return (w*actualSize)/10;
538 }
539
540
541 // Text width
getTextWidth(const FXString & text) const542 FXint FXPostscriptFont::getTextWidth(const FXString &text) const
543 {
544 return getTextWidth(text.text(), text.length());
545 }
546
547
548 // Text height
getTextHeight(const FXchar * text,FXuint n) const549 FXint FXPostscriptFont::getTextHeight(const FXchar *text, FXuint n) const
550 {
551 return (1000/10)*actualSize; // use font height
552 }
553
554
555 // Text height
getTextHeight(const FXString & text) const556 FXint FXPostscriptFont::getTextHeight(const FXString &text) const
557 {
558 return getTextHeight(text.text(), text.length());
559 }
560
561
562 /******************************************************************************/
563
564
565
566 // Clean up
~FXPostscriptFont()567 FXPostscriptFont::~FXPostscriptFont()
568 {
569 FXTRACE((100,"FXPostscriptFont::~FXPostscriptFont %p\n",this));
570 destroy();
571 }
572
573 }
574
575 // end of FXPostscriptFont.cpp
576
577