summaryrefslogtreecommitdiff
path: root/pcl/pcfsel.c
blob: 8fc0644db589294125bb9ca3e80c819a7f5ad699 (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
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
/* Portions Copyright (C) 2001 artofcode LLC.
   Portions Copyright (C) 1996, 2001 Artifex Software Inc.
   Portions Copyright (C) 1988, 2000 Aladdin Enterprises.
   This software is based in part on the work of the Independent JPEG Group.
   All Rights Reserved.

   This software is distributed under license and may not be copied, modified
   or distributed except as expressly authorized under the terms of that
   license.  Refer to licensing information at http://www.artifex.com/ or
   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
   San Rafael, CA  94903, (415)492-9861, for further information. */
/*$Id$ */

/* pcfsel.c */
/* PCL5 / HP-GL/2 font selection */
#include "stdio_.h"		/* stdio for gdebug */
#include "gdebug.h"
#include "pcommand.h"
#include "pcstate.h"
#include "pcfont.h"
#include "pcfsel.h"
#include "pcsymbol.h"

/* hack to avoid compiler message */
#ifndef abs
extern  int     abs( int );
#endif

/* Vector for scoring how well a font matches selection criteria.  It
 * would be nice to do this with a single scalar, but pitch/height in
 * particular require too much info.  Elements of the vector correspond
 * to the selection criteria, in order - TRM Ch 8. */
typedef enum {
  score_first = 0,
  score_symbol_set=score_first,
  score_spacing,
  score_pitch,
  score_height,
  score_style,
  score_weight,
  score_typeface,
  score_location,
  score_orientation,
  score_fontnumber,
  score_limit
} score_index_t;
typedef	int match_score_t[score_limit];

#ifdef DEBUG

static const char * const score_name[] = {
    "symbol",
    "spacing",
    "pitch",
    "height",
    "style",
    "weight",
    "typeface",
    "location",
    "orientation",
    "fontnumber"
};

static void
dprint_cc(const byte *pcc)
{	dprintf8("cc=%02x %02x %02x %02x %02x %02x %02x %02x", pcc[0],
		 pcc[1], pcc[2], pcc[3], pcc[4], pcc[5], pcc[6], pcc[7]);
}
void
dprint_font_params_t(const pl_font_params_t *pfp)
{	dprintf8("symset=%u %s pitch=%g ht=%u style=%u wt=%d face=%u:%u\n",
		 pfp->symbol_set,
		 (pfp->proportional_spacing ? "prop." : "fixed"),
		 pl_fp_pitch_cp(pfp) / 100.0, pfp->height_4ths / 4, pfp->style,
		 pfp->stroke_weight, pfp->typeface_family, (0x07FF &pfp->typeface_family) );
}

#include "plftable.h"

static void
dprint_font_name(const pl_font_t *pfont) 
{
    int i;
    bool found = false;

    for (i = 0; strlen(resident_table[i].full_font_name); i++) {
        if (!memcmp(&resident_table[i].params,
                    &pfont->params, sizeof(pfont->params))) {

            found = true;
            break;
        }
    }
    if (found) {
        dprintf1("%s ", resident_table[i].full_font_name);
    } else {
        if (pfont->storage == pcds_internal) {
            dprintf("internal font not found in resident table");
            dprintf1("%s\n", pfont->font_file);
        }
        dprintf("external font ");
    }
}

static void
dprint_font_t(const pl_font_t *pfont)
{	
    dprint_font_name(pfont);
    dprintf3("storage=%d scaling=%d type=%d ",
             pfont->storage, pfont->scaling_technology, pfont->font_type);
    dprint_cc(pfont->character_complement);
    dputs(";\n   ");
    dprint_font_params_t(&pfont->params);
}

static void
dprintf_font_scoring(const char *type, const pl_font_t *pfont, match_score_t score)
{
    int i;
    dprintf1("%s: ", type);
    dprint_font_t(pfont);
    dputs("   score:");
    for ( i = 0; i < score_limit; ++i )
        dprintf2(" %s: %d", score_name[i], score[i]);
    dputs("\n");
}

#endif

/* Decide whether an unbound font supports a symbol set (mostly
   convenient setup for pcl_check_symbol_support(). (HAS) Has the
   peculiar side effect of setting the symbol table to the default if
   the requested map is not available or to the requested symbol set
   if it is available. 2 is returned for no matching symbol set, 1 for
   mismatched vocabulary and 0 if the sets match. */
static int
check_support(const pcl_state_t *pcs, uint symbol_set, const pl_font_t *fp,
    pl_symbol_map_t **mapp)
{
    pl_glyph_vocabulary_t gv;
    byte id[2];

    id[0] = symbol_set >> 8;
    id[1] = symbol_set;
    gv = pl_complement_to_vocab(fp->character_complement);
    *mapp = pcl_find_symbol_map(pcs, id, gv);
    if ( *mapp == 0 ) {
	/* Default to Roman 8: 277 0x115 not the default symbol set!
	 * Basically roman8
	 */
	id[0] = 0x01;
	id[1] = 0x15;
	*mapp = pcl_find_symbol_map(pcs, id, gv);
	return 0; /* worst */
    }

    if ( pcl_check_symbol_support((*mapp)->character_requirements,
                                   fp->character_complement) )
	return 2; /* best */
    else
	return 1;
}

/* a font may be scalable but we want to treat it a bitmap for the
   purpose of selection.  Right now lineprinter is the only example of
   this */
static bool
font_is_scalable_selection_wise(const pl_font_t *fp)
{
    if (fp->params.typeface_family == 0)
        return false;
    else
        return pl_font_is_scalable(fp);
}
    
    
/* Compute a font's score against selection parameters.  TRM 8-27.
 * Also set *mapp to the symbol map to be used if this font wins. */
static void
score_match(const pcl_state_t *pcs, const pcl_font_selection_t *pfs,
    const pl_font_t *fp, pl_symbol_map_t **mapp, match_score_t score)
{
	int tscore;

	/* 1.  Symbol set.  2 for exact match or full support, 1 for
	 * default supported, 0 for no luck. */
	if ( pl_font_is_bound(fp) )
	  {
	    score[score_symbol_set] =
		pfs->params.symbol_set == fp->params.symbol_set? 2:
		    (fp->params.symbol_set == pcs->default_symbol_set_value);
	    *mapp = 0;		/* bound fonts have no map */
	  }
	else
	  score[score_symbol_set] = check_support(pcs, pfs->params.symbol_set, fp, mapp);
	/* 2.  Spacing. */
	score[score_spacing] =
	  pfs->params.proportional_spacing == fp->params.proportional_spacing;

	/* 3.  Pitch. */
	/* Need to score this so that (1) exact match is highest, (2) any
	 * higher-than-requested pitch is better than any lower-than-
	 * requested pitch, (3) within these categories, nearer is better. */
	if ( pfs->params.proportional_spacing )
	    score[score_pitch] = 0;	/* should not influence score */
	else { /* fixed space selection */
            if ( fp->params.proportional_spacing )
                /* scalable; match is worst possible */
                score[score_pitch] = 0;
	    else { 
                int delta = pl_fp_pitch_per_inch_x100(&fp->params) -
		    pl_fp_pitch_per_inch_x100(&pfs->params);

		/* If within one unit, call it exact; otherwise give
		 * preference to higher pitch than requested. */
		if (font_is_scalable_selection_wise(fp) || (abs(delta) <= 10 ))
                    score[score_pitch] = 0x2000000;
		else if ( delta > 0 )
                    score[score_pitch] = 0x2000000 - delta;
		else 
                    score[score_pitch] = 0x1000000 + delta;
            }
        }
	/* 4.  Height. */
	/* Closest match scores highest (no preference for + or -). Otherwise
	 * similar to pitch, and again, values assigned have no meaning out-
	 * side this code. */
	if ( font_is_scalable_selection_wise(fp) )
	  score[score_height] = 0x1000000;
	else
	  { int delta = abs(pfs->params.height_4ths - fp->params.height_4ths);

	    /* As before, allow one unit of error to appear "exact".  */
	    if ( delta <= 1 )
	      delta = 0;
	    score[score_height] = 0x1000000 - delta;
	  }

	/* 5.  Style. */
	if ( pfs->params.style == fp->params.style )
	    score[score_style] = 2;
	else if ( fp->params.style == 0 && pfs->params.style != 0 )
	    score[score_style] = 1;  /* prefer NO style fonts to wrong style */
	else
	    score[score_style] = 0;

	/* 6.  Stroke Weight. */
	/* If not exact match, prefer closest value away from 0 (more
	 * extreme).  If none, then prefer closest value nearer 0.  Once
	 * again, the actual values assigned here have no meaning outside
	 * this chunk of code. */
	{ /* Worst cases (font value toward zero from request) 0..13.
	   * Nearest more extreme: 14..21.  21 is exact.  */
	  int fwt = fp->params.stroke_weight;
	  int pwt = pfs->params.stroke_weight;
	  int delta = pwt - fwt;

	  /* With a little time, this could be collapsed. */
	  if ( pwt >= 0 )
	    if ( fwt >= pwt )
	      tscore = 21 + delta;
	    else
	      tscore = 14 - delta;
	  else
	    if ( fwt <= pwt )
	      tscore = 21 - delta;
	    else
	      tscore = 14 + delta;
	  score[score_weight] = tscore;
	}

	/* 7.  Typeface family. */
	{ 
            uint diff = pfs->params.typeface_family - fp->params.typeface_family;
            if (diff == 0)
                score[score_typeface] = 2;
            else {
                diff = (pfs->params.typeface_family & 0x7ff) - (fp->params.typeface_family & 0x7ff);
                if (diff == 0)
                    score[score_typeface] = 1;
                else
                    score[score_typeface] = 0;
            }
	}

	/* 8. Location. */
	/* Rearrange the value from "storage" into priority for us.  We
	 * only care that we get the priority sequence: soft-font, cartridge,
	 * SIMM, and internal, and that we order at least 2 cartridges.  (TRM
	 * implies that SIMMs are *not* to be ordered.)  Once again, values
	 * assigned here have no external meaning. */
	/* 1 bit at bottom for SIMM _vs_ internal, then cartridge-number
	 * bits, then soft-font bit at top. */
	if ( fp->storage & pcds_downloaded )
	  tscore = 1 << (pcds_cartridge_max + 1);
	else if ( fp->storage & pcds_all_cartridges )
	  tscore = (fp->storage & pcds_all_cartridges) >>
	      (pcds_cartridge_shift - 1);
	else
	  /* no priority among SIMMs, and internal is 0 */
	  tscore = (fp->storage & pcds_all_simms)? 1: 0;
	score[score_location] = tscore;

	/* 9.  Orientation */
	if ( fp->scaling_technology != plfst_bitmap )
	  score[score_orientation] = 1;
	else
	  { pcl_font_header_t *fhp = (pcl_font_header_t *)(fp->header);

	    score[score_orientation] = fhp?
	        fhp->Orientation == pcs->xfm_state.lp_orient :
		0;
	  }

	/* 10. Location: 11 in the implementors Guide. 
	 * lower typeface ID's are better.
	 * supposed to be ordered by bitmap/scalable and SoftFont ... Internal but
	 * these didn't seem to have an effect.
	 *  
	 * Lineprinter has a special case, making it harder to accidently select.
	 */

	if (fp->params.typeface_family == 0)
	    score[score_fontnumber] = 0x100000 - fp->params.typeface_family; 
	else
	    score[score_fontnumber] = 0x200000 - fp->params.typeface_family; 

#ifdef DEBUG
	if ( gs_debug_c('=') )
            dprintf_font_scoring("candidate", fp, score);
#endif

}

/* Recompute the current font from the descriptive parameters. */
/* This is used by both PCL and HP-GL/2. */
int
pcl_reselect_font(pcl_font_selection_t *pfs, const pcl_state_t *pcs)
{	if ( pfs->font == 0 )
	  { pl_dict_enum_t dictp;
	    gs_const_string key;
	    void *value;
	    pl_font_t *best_font = 0;
	    pl_symbol_map_t *best_map = 0;
	    pl_symbol_map_t *mapp = 0;
	    match_score_t best_match;
            score_index_t i;

#ifdef DEBUG
	    if ( gs_debug_c('=') )
	      { dputs("[=]request: ");
	        dprint_font_params_t(&pfs->params);
	      }
#endif
	    /* if the font table is set up to select character by id
               we attempt to reselect the font by id.  As a fallback
               we use family selection.  NB We do not correctly handle
               the fonts with alphanumeric id's */
	    if ( (int)pfs->selected_id >= 0 ) {
		byte id_key[2];
		void *value;
		id_key[0] = pfs->selected_id >> 8;
		id_key[1] = (byte)(pfs->selected_id);
		if ( pl_dict_find((pl_dict_t *)&pcs->soft_fonts, id_key, 2, &value) ) {

		    pfs->font = (pl_font_t *)value;
		    /* probably not necessary */
		    if ( !pl_font_is_bound(pfs->font) ) { 
			if ( check_support(pcs, pfs->params.symbol_set, pfs->font, &pfs->map) )
			    DO_NOTHING;
			else if ( check_support(pcs, pcs->default_symbol_set_value,
						pfs->font, &pfs->map)
				  )
			    DO_NOTHING;
			else { /*
				* This font doesn't support the required symbol set.
				* Punt -- map 1-for-1.
				*/
			}
		    }
		    return 0;
		}
	    }
	    /* Initialize the best match to be worse than any real font. */
            for (i=(score_index_t)0; i<score_limit; i++)
                best_match[i] = -1;

	    pl_dict_enum_begin(&pcs->soft_fonts, &dictp);
	    while ( pl_dict_enum_next(&dictp, &key, &value) )
	      { pl_font_t *fp = (pl_font_t *)value;
		match_score_t match;
		score_match(pcs, pfs, fp, &mapp, match);
#ifdef DEBUG
                if ( gs_debug_c('=') ) {
                    if (best_match[0] != -1) /* skip sentinel */
                        dprintf_font_scoring("best", best_font, best_match);
                }
#endif
		for (i=(score_index_t)0; i<score_limit; i++)
		  if ( match[i] != best_match[i] )
		    {
		      if ( match[i] > best_match[i] )
			{
			  best_font = fp;
			  best_map = mapp;
			  memcpy((void*)best_match, (void*)match,
			      sizeof(match));
#ifdef DEBUG
                          if ( gs_debug_c('=') ) {
                              dprintf_font_scoring("usurper", fp, best_match);
                              dprintf1("   better %s***)\n", score_name[i]);

                          }
#endif

			}
		      break;
		    }
	      }
	    if ( best_font == 0 )
                return -1;	/* no fonts */
#ifdef DEBUG
            if ( gs_debug_c('=') ) {
                dprintf_font_scoring("champion", best_font, best_match);
            }
#endif

	    pfs->font = best_font;
	    pfs->map = best_map;
	  }
	pfs->selected_id = (uint)-1;
	return 0;
}

/* set font parameters after an id selection */
void
pcl_set_id_parameters(const pcl_state_t *pcs, 
		      pcl_font_selection_t *pfs, pl_font_t *fp, uint id)
{
	/* Transfer parameters from the selected font into the selection
	 * parameters, being careful with the softer parameters. */
	pfs->font = fp;
	pfs->selected_id = id;
	pfs->map = 0;
	if ( pl_font_is_bound(fp) )
	  pfs->params.symbol_set = fp->params.symbol_set;
	else
	  { if ( check_support(pcs, pfs->params.symbol_set, fp, &pfs->map) )
	      DO_NOTHING;
	    else if ( check_support(pcs, pcs->default_symbol_set_value,
				    fp, &pfs->map)
		    )
	      DO_NOTHING;
	    else
	      { /*
		 * This font doesn't support the required symbol set.
		 * Punt -- map 1-for-1.
		 */
	      }
	  }
	pfs->params.proportional_spacing = fp->params.proportional_spacing;
	if ( !pfs->params.proportional_spacing && !pl_font_is_scalable(fp) )
	  pfs->params.pitch = fp->params.pitch;
	if ( !pl_font_is_scalable(fp) )
	  pfs->params.height_4ths = fp->params.height_4ths;
	pfs->params.style = fp->params.style;
	pfs->params.stroke_weight = fp->params.stroke_weight;
	pfs->params.typeface_family = fp->params.typeface_family;
	return;
}
  
/* Select a font by ID, updating the selection parameters. */
/* Return 0 normally, 1 if no font was found, or an error code. */
int
pcl_select_font_by_id(pcl_font_selection_t *pfs, uint id, pcl_state_t *pcs)
{	byte id_key[2];
	void *value;
	pl_font_t *fp;

	id_key[0] = id >> 8;
	id_key[1] = (byte)id;
	if ( !pl_dict_find(&pcs->soft_fonts, id_key, 2, &value) )
	  return 1;		/* font not found */
	fp = (pl_font_t *)value;
	pcl_set_id_parameters(pcs, pfs, fp, id);
	return 0;
}