/* Copyright (C) 1991, 2000 artofcode LLC.  All rights reserved.
  
  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  59 Temple Place, Suite 330, Boston, MA, 02111-1307.

*/

/*$Id: zfont1.c,v 1.9.4.1.2.1 2003/01/17 00:49:06 giles Exp $ */
/* Type 1 and Type 4 font creation operators */
#include "memory_.h"
#include "ghost.h"
#include "oper.h"
#include "gxfixed.h"
#include "gsmatrix.h"
#include "gxdevice.h"
#include "gxfont.h"
#include "gxfont1.h"
#include "bfont.h"
#include "ialloc.h"
#include "icharout.h"
#include "ichar1.h"
#include "idict.h"
#include "idparam.h"
#include "ifont1.h"
#include "iname.h"		/* for name_index in enumerate_glyph */
#include "store.h"

/* Type 1 font procedures (defined in zchar1.c) */
extern const gs_type1_data_procs_t z1_data_procs;
font_proc_glyph_info(z1_glyph_info);
/* Font procedures defined here */
private font_proc_same_font(z1_same_font);

/* ------ Private utilities ------ */

private void
find_zone_height(float *pmax_height, int count, const float *values)
{
    int i;
    float zone_height;

    for (i = 0; i < count; i += 2)
	if ((zone_height = values[i + 1] - values[i]) > *pmax_height)
	    *pmax_height = zone_height;
}

/* ------ Font procedures ------ */

private int
z1_enumerate_glyph(gs_font * pfont, int *pindex, gs_glyph_space_t ignored,
		   gs_glyph * pglyph)
{
    const gs_font_type1 *const pfont1 = (gs_font_type1 *)pfont;
    const ref *pcsdict = &pfont_data(pfont1)->CharStrings;

    return zchar_enumerate_glyph(pcsdict, pindex, pglyph);
}

/* ------ Public procedures ------ */

/* Extract pointers to internal structures. */
int
charstring_font_get_refs(const_os_ptr op, charstring_font_refs_t *pfr)
{
    check_type(*op, t_dictionary);
    if (dict_find_string(op, "Private", &pfr->Private) <= 0 ||
	!r_has_type(pfr->Private, t_dictionary)
	)
	return_error(e_invalidfont);
    make_empty_array(&pfr->no_subrs, 0);
    if (dict_find_string(pfr->Private, "OtherSubrs", &pfr->OtherSubrs) > 0) {
	if (!r_is_array(pfr->OtherSubrs))
	    return_error(e_typecheck);
    } else
	pfr->OtherSubrs = &pfr->no_subrs;
    if (dict_find_string(pfr->Private, "Subrs", &pfr->Subrs) > 0) {
	if (!r_is_array(pfr->Subrs))
	    return_error(e_typecheck);
    } else
	pfr->Subrs = &pfr->no_subrs;
    pfr->GlobalSubrs = &pfr->no_subrs;
    return 0;
}

/* Get the parameters of a CharString-based font or a FDArray entry. */
int
charstring_font_params(const_os_ptr op, charstring_font_refs_t *pfr,
		       gs_type1_data *pdata1)
{
    const ref *pprivate = pfr->Private;
    int code;

    /* Get the rest of the information from the Private dictionary. */
    if ((code = dict_int_param(pprivate, "lenIV", -1, 255, pdata1->lenIV,
			       &pdata1->lenIV)) < 0 ||
	(code = dict_uint_param(pprivate, "subroutineNumberBias",
				0, max_uint, pdata1->subroutineNumberBias,
				&pdata1->subroutineNumberBias)) < 0 ||
	(code = dict_int_param(pprivate, "BlueFuzz", 0, 1999, 1,
			       &pdata1->BlueFuzz)) < 0 ||
	(code = dict_float_param(pprivate, "BlueScale", 0.039625,
				 &pdata1->BlueScale)) < 0 ||
	(code = dict_float_param(pprivate, "BlueShift", 7.0,
				 &pdata1->BlueShift)) < 0 ||
	(code = pdata1->BlueValues.count =
	 dict_float_array_param(pprivate, "BlueValues", max_BlueValues * 2,
				&pdata1->BlueValues.values[0], NULL)) < 0 ||
	(code = dict_float_param(pprivate, "ExpansionFactor", 0.06,
				 &pdata1->ExpansionFactor)) < 0 ||
	(code = pdata1->FamilyBlues.count =
	 dict_float_array_param(pprivate, "FamilyBlues", max_FamilyBlues * 2,
				&pdata1->FamilyBlues.values[0], NULL)) < 0 ||
	(code = pdata1->FamilyOtherBlues.count =
	 dict_float_array_param(pprivate,
				"FamilyOtherBlues", max_FamilyOtherBlues * 2,
			    &pdata1->FamilyOtherBlues.values[0], NULL)) < 0 ||
	(code = dict_bool_param(pprivate, "ForceBold", false,
				&pdata1->ForceBold)) < 0 ||
    /*
     * We've seen a few fonts with out-of-range LanguageGroup values;
     * if it weren't for this, the only legal values should be 0 or 1.
     */
	(code = dict_int_param(pprivate, "LanguageGroup", min_int, max_int, 0,
			       &pdata1->LanguageGroup)) < 0 ||
	(code = pdata1->OtherBlues.count =
	 dict_float_array_param(pprivate, "OtherBlues", max_OtherBlues * 2,
				&pdata1->OtherBlues.values[0], NULL)) < 0 ||
	(code = dict_bool_param(pprivate, "RndStemUp", true,
				&pdata1->RndStemUp)) < 0 ||
	(code = pdata1->StdHW.count =
	 dict_float_array_check_param(pprivate, "StdHW", 1,
				      &pdata1->StdHW.values[0], NULL,
				      0, e_rangecheck)) < 0 ||
	(code = pdata1->StdVW.count =
	 dict_float_array_check_param(pprivate, "StdVW", 1,
				      &pdata1->StdVW.values[0], NULL,
				      0, e_rangecheck)) < 0 ||
	(code = pdata1->StemSnapH.count =
	 dict_float_array_param(pprivate, "StemSnapH", max_StemSnap,
				&pdata1->StemSnapH.values[0], NULL)) < 0 ||
	(code = pdata1->StemSnapV.count =
	 dict_float_array_param(pprivate, "StemSnapV", max_StemSnap,
				&pdata1->StemSnapV.values[0], NULL)) < 0 ||
    /* The WeightVector is in the font dictionary, not Private. */
	(code = pdata1->WeightVector.count =
	 dict_float_array_param(op, "WeightVector", max_WeightVector,
				pdata1->WeightVector.values, NULL)) < 0
	)
	return code;
    /*
     * According to section 5.6 of the "Adobe Type 1 Font Format",
     * there is a requirement that BlueScale times the maximum
     * alignment zone height must be less than 1.  Some fonts
     * produced by Fontographer have ridiculously large BlueScale
     * values, so we force BlueScale back into range here.
     */
    {
	float max_zone_height = 1.0;

#define SCAN_ZONE(z)\
    find_zone_height(&max_zone_height, pdata1->z.count, pdata1->z.values);

	SCAN_ZONE(BlueValues);
	SCAN_ZONE(OtherBlues);
	SCAN_ZONE(FamilyBlues);
	SCAN_ZONE(FamilyOtherBlues);

#undef SCAN_ZONE

	if (pdata1->BlueScale * max_zone_height > 1.0)
	    pdata1->BlueScale = 1.0 / max_zone_height;
    }
    /*
     * According to the same Adobe book, section 5.11, only values
     * 0 and 1 are allowed for LanguageGroup and we have encountered
     * fonts with other values. If the value is anything else, map it to 0
     * so that the remainder of the graphics library won't see an
     * unexpected value.
     */
    if (pdata1->LanguageGroup > 1 || pdata1->LanguageGroup < 0)
	pdata1->LanguageGroup = 0;
    return 0;
}

/* Fill in a newly built CharString-based font or FDArray entry. */
int
charstring_font_init(gs_font_type1 *pfont, const charstring_font_refs_t *pfr,
		     const gs_type1_data *pdata1)
{
    font_data *pdata;

    pdata = pfont_data(pfont);
    pfont->data = *pdata1;
    ref_assign(&pdata->u.type1.OtherSubrs, pfr->OtherSubrs);
    ref_assign(&pdata->u.type1.Subrs, pfr->Subrs);
    ref_assign(&pdata->u.type1.GlobalSubrs, pfr->GlobalSubrs);
    pfont->data.procs = z1_data_procs;
    pfont->data.proc_data = (char *)pdata;
    pfont->procs.same_font = z1_same_font;
    pfont->procs.glyph_info = z1_glyph_info;
    pfont->procs.enumerate_glyph = z1_enumerate_glyph;
    pfont->procs.glyph_outline = zchar1_glyph_outline;
    return 0;
}

/* Build a Type 1, Type 2, or Type 4 font. */
int
build_charstring_font(i_ctx_t *i_ctx_p, os_ptr op, build_proc_refs *pbuild,
		      font_type ftype, charstring_font_refs_t *pfr,
		      gs_type1_data *pdata1, build_font_options_t options)
{
    int code = charstring_font_params(op, pfr, pdata1);
    gs_font_type1 *pfont;

    if (code < 0)
	return code;
    code = build_gs_primitive_font(i_ctx_p, op, (gs_font_base **)&pfont, ftype,
				   &st_gs_font_type1, pbuild, options);
    if (code != 0)
	return code;
    /* This is a new font, fill it in. */
    charstring_font_init(pfont, pfr, pdata1);
    return define_gs_font((gs_font *)pfont);
}

/* ------ Operators ------ */

/* Build a Type 1 or Type 4 font. */
private int
buildfont1or4(i_ctx_t *i_ctx_p, os_ptr op, build_proc_refs * pbuild,
	      font_type ftype, build_font_options_t options)
{
    charstring_font_refs_t refs;
    int code = charstring_font_get_refs(op, &refs);
    gs_type1_data data1;

    if (code < 0)
	return code;
    data1.interpret = gs_type1_interpret;
    data1.subroutineNumberBias = 0;
    data1.lenIV = DEFAULT_LENIV_1;
    return build_charstring_font(i_ctx_p, op, pbuild, ftype, &refs, &data1,
				 options);
}

/* <string|name> <font_dict> .buildfont1 <string|name> <font> */
/* Build a type 1 (Adobe encrypted) font. */
private int
zbuildfont1(i_ctx_t *i_ctx_p)
{
    os_ptr op = osp;
    build_proc_refs build;
    int code = build_proc_name_refs(&build,
				    "%Type1BuildChar", "%Type1BuildGlyph");

    if (code < 0)
	return code;
    return buildfont1or4(i_ctx_p, op, &build, ft_encrypted,
			 bf_notdef_required);
}

/* <string|name> <font_dict> .buildfont4 <string|name> <font> */
/* Build a type 4 (disk-based Adobe encrypted) font. */
private int
zbuildfont4(i_ctx_t *i_ctx_p)
{
    os_ptr op = osp;
    build_proc_refs build;
    int code = build_gs_font_procs(op, &build);

    if (code < 0)
	return code;
    return buildfont1or4(i_ctx_p, op, &build, ft_disk_based, bf_options_none);
}

/* ------ Initialization procedure ------ */

const op_def zfont1_op_defs[] =
{
    {"2.buildfont1", zbuildfont1},
    {"2.buildfont4", zbuildfont4},
    op_def_end(0)
};

/* ------ Font procedures for Type 1 fonts ------ */

/* same_font procedure */
private bool
same_font_dict(const font_data *pdata, const font_data *podata,
	       const char *key)
{
    ref *pvalue;
    bool present = dict_find_string(&pdata->dict, key, &pvalue) > 0;
    ref *povalue;
    bool opresent = dict_find_string(&podata->dict, key, &povalue) > 0;

    return (present == opresent &&
	    (present <= 0 || obj_eq(pvalue, povalue)));
}
private int
z1_same_font(const gs_font *font, const gs_font *ofont, int mask)
{
    if (ofont->FontType != font->FontType)
	return 0;
    while (font->base != font)
	font = font->base;
    while (ofont->base != ofont)
	ofont = ofont->base;
    if (ofont == font)
	return mask;
    {
	int same = gs_base_same_font(font, ofont, mask);
	int check = mask & ~same;
	const gs_font_type1 *const pfont1 = (const gs_font_type1 *)font;
	const font_data *const pdata = pfont_data(pfont1);
	const gs_font_type1 *pofont1 = (const gs_font_type1 *)ofont;
	const font_data *const podata = pfont_data(pofont1);

	if ((check & (FONT_SAME_OUTLINES | FONT_SAME_METRICS)) &&
	    !memcmp(&pofont1->data.procs, &z1_data_procs, sizeof(z1_data_procs)) &&
	    obj_eq(&pdata->CharStrings, &podata->CharStrings) &&
	    /*
	     * We use same_font_dict for convenience: we know that
	     * both fonts do have Private dictionaries.
	     */
	    same_font_dict(pdata, podata, "Private")
	    )
	    same |= FONT_SAME_OUTLINES;

	if ((check & FONT_SAME_METRICS) && (same & FONT_SAME_OUTLINES) &&
	    !memcmp(&pofont1->data.procs, &z1_data_procs, sizeof(z1_data_procs)) &&
	    /* Metrics may be affected by CDevProc, Metrics, Metrics2. */
	    same_font_dict(pdata, podata, "Metrics") &&
	    same_font_dict(pdata, podata, "Metrics2") &&
	    same_font_dict(pdata, podata, "CDevProc")
	    )
	    same |= FONT_SAME_METRICS;

	if ((check & FONT_SAME_ENCODING) &&
	    pofont1->procs.same_font == z1_same_font &&
	    obj_eq(&pdata->Encoding, &podata->Encoding)
	    )
	    same |= FONT_SAME_ENCODING;

	return same & mask;
    }
}

