/* Copyright (C) 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: gdevpdfe.c,v 1.10.2.2.2.1 2003/01/17 00:49:01 giles Exp $ */
/* Embedded font writing for pdfwrite driver. */
#include "memory_.h"
#include "string_.h"
#include "gx.h"
#include "gserrors.h"
#include "gsmatrix.h"
#include "gxfcid.h"
#include "gxfont.h"
#include "gxfont0.h"
#include "gdevpdfx.h"
#include "gdevpdff.h"
#include "gdevpsf.h"
#include "scommon.h"

/* ---------------- Utilities ---------------- */

/* Begin writing FontFile* data. */
private int
pdf_begin_fontfile(gx_device_pdf *pdev, long FontFile_id,
		   const char *entries, long len1, pdf_data_writer_t *pdw)
{
    stream *s;

    pdf_open_separate(pdev, FontFile_id);
    s = pdev->strm;
    stream_puts(s, "<<");
    if (entries)
	stream_puts(pdev->strm, entries);
    if (len1 >= 0)
	pprintld1(pdev->strm, "/Length1 %ld", len1);
    return pdf_begin_data(pdev, pdw);
}

/* Finish writing FontFile* data. */
private int
pdf_end_fontfile(gx_device_pdf *pdev, pdf_data_writer_t *pdw)
{
    return pdf_end_data(pdw);
}

/* ---------------- Individual font types ---------------- */

/* ------ Type 1 family ------ */

/*
 * Acrobat Reader apparently doesn't accept CFF fonts with Type 1
 * CharStrings, so we need to convert them.  Also remove lenIV,
 * so Type 2 fonts will compress better.
 */
#define TYPE2_OPTIONS (WRITE_TYPE2_NO_LENIV | WRITE_TYPE2_CHARSTRINGS)

/* Write the FontFile[3] data for an embedded Type 1, Type 2, or */
/* CIDFontType 0 font. */
private int
pdf_embed_font_as_type1(gx_device_pdf *pdev, gs_font_type1 *font,
			long FontFile_id, gs_glyph subset_glyphs[256],
			uint subset_size, const gs_const_string *pfname)
{
    stream poss;
    int lengths[3];
    int code;
    pdf_data_writer_t writer;
#define MAX_INT_CHARS ((sizeof(int) * 8 + 2) / 3)
    char lengths_str[9 + MAX_INT_CHARS + 11];  /* /Length2 %d/Length3 0\0 */
#undef MAX_INT_CHARS

    swrite_position_only(&poss);
    /*
     * We omit the 512 zeros and the cleartomark, and set Length3 to 0.
     * Note that the interpreter adds them implicitly (per documentation),
     * so we must set MARK so that the encrypted portion pushes a mark on
     * the stack.
     *
     * NOTE: the options were set to 0 in the first checked-in version of
     * this file.  We can't explain this: Acrobat Reader requires eexec
     * encryption, so the code can't possibly have worked.
     *
     * Acrobat Reader 3 allows lenIV = -1 in Type 1 fonts, but Acrobat
     * Reader 4 doesn't.  Therefore, we can't allow it.
     */
#define TYPE1_OPTIONS (WRITE_TYPE1_EEXEC | WRITE_TYPE1_EEXEC_MARK |\
		       WRITE_TYPE1_WITH_LENIV)
    code = psf_write_type1_font(&poss, font, TYPE1_OPTIONS,
				 subset_glyphs, subset_size, pfname, lengths);
    if (code < 0)
	return code;
    sprintf(lengths_str, "/Length2 %d/Length3 0", lengths[1]);
    code = pdf_begin_fontfile(pdev, FontFile_id, lengths_str, lengths[0],
			      &writer);
    if (code < 0)
	return code;
    code = psf_write_type1_font(writer.binary.strm, font, TYPE1_OPTIONS,
				subset_glyphs, subset_size, pfname,
				lengths /*ignored*/);
#undef TYPE1_OPTIONS
    pdf_end_fontfile(pdev, &writer);
    return code;
}

/* Embed a font as Type 2. */
private int
pdf_embed_font_as_type2(gx_device_pdf *pdev, gs_font_type1 *font,
			long FontFile_id, gs_glyph subset_glyphs[256],
			uint subset_size, const gs_const_string *pfname,
			gs_int_rect *FontBBox)
{
    int code;
    pdf_data_writer_t writer;
    int options = TYPE2_OPTIONS |
	(pdev->CompatibilityLevel < 1.3 ? WRITE_TYPE2_AR3 : 0);

    code = pdf_begin_fontfile(pdev, FontFile_id, "/Subtype/Type1C", -1L,
			      &writer);
    if (code < 0)
	return code;
    code = psf_write_type2_font(writer.binary.strm, font, options,
				subset_glyphs, subset_size, pfname,
				FontBBox);
    pdf_end_fontfile(pdev, &writer);
    return code;
}

/* Embed a Type 1 or Type 2 font. */
private int
pdf_embed_font_type1(gx_device_pdf *pdev, gs_font_type1 *font,
		     long FontFile_id, gs_glyph subset_glyphs[256],
		     uint subset_size, const gs_const_string *pfname,
		     gs_int_rect *FontBBox)
{
    switch (((const gs_font *)font)->FontType) {
    case ft_encrypted:
	if (pdev->CompatibilityLevel < 1.2)
	    return pdf_embed_font_as_type1(pdev, font, FontFile_id,
					   subset_glyphs, subset_size, pfname);
	/* For PDF 1.2 and later, write Type 1 fonts as Type1C. */
    case ft_encrypted2:
	return pdf_embed_font_as_type2(pdev, font, FontFile_id,
			       subset_glyphs, subset_size, pfname, FontBBox);
    default:
	return_error(gs_error_rangecheck);
    }
}

/* Embed a CIDFontType0 font. */
private int
pdf_embed_font_cid0(gx_device_pdf *pdev, gs_font_cid0 *font,
		    long FontFile_id, const byte *subset_cids,
		    uint subset_size, const gs_const_string *pfname)
{
    int code;
    pdf_data_writer_t writer;

    if (pdev->CompatibilityLevel < 1.2)
	return_error(gs_error_rangecheck);
    code = pdf_begin_fontfile(pdev, FontFile_id, "/Subtype/CIDFontType0C", -1L,
			      &writer);
    if (code < 0)
	return code;
    code = psf_write_cid0_font(writer.binary.strm, font, TYPE2_OPTIONS,
			       subset_cids, subset_size, pfname);
    pdf_end_fontfile(pdev, &writer);
    return code;
}

/* ------ TrueType family ------ */

/* Embed a TrueType font. */
private int
pdf_embed_font_type42(gx_device_pdf *pdev, gs_font_type42 *font,
		      long FontFile_id, gs_glyph subset_glyphs[256],
		      uint subset_size, const gs_const_string *pfname)
{
    /* Acrobat Reader 3 doesn't handle cmap format 6 correctly. */
    const int options = WRITE_TRUETYPE_CMAP | WRITE_TRUETYPE_NAME |
	(pdev->CompatibilityLevel <= 1.2 ?
	 WRITE_TRUETYPE_NO_TRIMMED_TABLE : 0);
    stream poss;
    int code;
    pdf_data_writer_t writer;

    swrite_position_only(&poss);
    code = psf_write_truetype_font(&poss, font, options,
				   subset_glyphs, subset_size, pfname);
    if (code < 0)
	return code;
    code = pdf_begin_fontfile(pdev, FontFile_id, NULL, stell(&poss), &writer);
    if (code < 0)
	return code;
    psf_write_truetype_font(writer.binary.strm, font, options,
			    subset_glyphs, subset_size, pfname);
    pdf_end_fontfile(pdev, &writer);
    return 0;
}

/* Embed a CIDFontType2 font. */
private int
pdf_embed_font_cid2(gx_device_pdf *pdev, gs_font_cid2 *font,
		    long FontFile_id, const byte *subset_bits,
		    uint subset_size, const gs_const_string *pfname)
{
    /* CIDFontType 2 fonts don't use cmap, name, OS/2, or post. */
#define OPTIONS 0
    int code;
    pdf_data_writer_t writer;

    code = pdf_begin_fontfile(pdev, FontFile_id, NULL, -1L, &writer);
    if (code < 0)
	return code;
    psf_write_cid2_font(writer.binary.strm, font, OPTIONS,
			subset_bits, subset_size, pfname);
#undef OPTIONS
    pdf_end_fontfile(pdev, &writer);
    return 0;
}

/* ---------------- Entry point ---------------- */

/*
 * Write the FontDescriptor and FontFile* data for an embedded font.
 * Return a rangecheck error if the font can't be embedded.
 */
int
pdf_write_embedded_font(gx_device_pdf *pdev, pdf_font_descriptor_t *pfd)
{
    gs_font *font = pfd->base_font;
    gs_const_string font_name;
    byte *fnchars = pfd->FontName.chars;
    uint fnsize = pfd->FontName.size;
    bool do_subset;
    long FontFile_id = pfd->FontFile_id;
    gs_glyph subset_glyphs[256];
    gs_glyph *subset_list = 0;	/* for non-CID fonts */
    byte *subset_bits = 0;	/* for CID fonts */
    uint subset_size = 0;
    gs_matrix save_mat;
    int code;

    /* Determine whether to subset the font. */
    switch (pfd->do_subset) {
    case FONT_SUBSET_OK:
	do_subset = pdev->params.SubsetFonts && pdev->params.MaxSubsetPct > 0;
	break;
    case FONT_SUBSET_YES:
	do_subset = true;
	break;
    case FONT_SUBSET_NO:
	do_subset = false;
	break;
    }
    if (do_subset) {
	int max_pct = pdev->params.MaxSubsetPct;
	int used, i;

	for (i = 0, used = 0; i < pfd->chars_used.size; ++i)
	    used += byte_count_bits[pfd->chars_used.data[i]];
	/*
	 * We want to subset iff used / total <= MaxSubsetPct / 100, i.e.,
	 * iff total >= used * 100 / MaxSubsetPct.  This observation
	 * allows us to stop the enumeration loop early.
	 */
	if (max_pct < 100) {
	    do_subset = false;
	    if (max_pct > 0) {
		int max_total = used * 100 / max_pct;
		int index, total;
		gs_glyph ignore_glyph;

		for (index = 0, total = 0;
		     (font->procs.enumerate_glyph(font, &index,
						  GLYPH_SPACE_INDEX,
						  &ignore_glyph), index != 0);
		     )
		    if (++total >= max_total) {
			do_subset = true;
			break;
		    }
	    }
	}
    }

    /* Generate an appropriate font name. */
    if (pdf_has_subset_prefix(fnchars, fnsize)) {
	/* Strip off any existing subset prefix. */
	fnsize -= SUBSET_PREFIX_SIZE;
	memmove(fnchars, fnchars + SUBSET_PREFIX_SIZE, fnsize);
    }
    if (do_subset) {
	memmove(fnchars + SUBSET_PREFIX_SIZE, fnchars, fnsize);
	pdf_make_subset_prefix(pdev, fnchars, FontFile_id);
	fnsize += SUBSET_PREFIX_SIZE;
    }
    font_name.data = fnchars;
    font_name.size = pfd->FontName.size = fnsize;
    code = pdf_write_FontDescriptor(pdev, pfd);
    if (code >= 0) {
	pfd->written = true;

	/*
	 * Finally, write the font (or subset), using the original
	 * (unscaled) FontMatrix.
	 */
	save_mat = font->FontMatrix;
	font->FontMatrix = pfd->orig_matrix;
	switch (font->FontType) {
	case ft_composite:
	    /* Nothing to embed -- the descendant fonts do it all. */
	    break;
	case ft_encrypted:
	case ft_encrypted2:
	    if (do_subset) {
		subset_size = psf_subset_glyphs(subset_glyphs, font,
						pfd->chars_used.data);
		subset_list = subset_glyphs;
	    }
	    code = pdf_embed_font_type1(pdev, (gs_font_type1 *)font,
					FontFile_id, subset_list,
					subset_size, &font_name, 
					&pfd->values.FontBBox);
	    break;
	case ft_TrueType:
	    if (do_subset) {
		subset_size = psf_subset_glyphs(subset_glyphs, font,
						pfd->chars_used.data);
		subset_list = subset_glyphs;
	    }
	    code = pdf_embed_font_type42(pdev, (gs_font_type42 *)font,
					 FontFile_id, subset_list,
					 subset_size, &font_name);
	    break;
	case ft_CID_encrypted:
	    if (do_subset) {
		subset_size = pfd->chars_used.size << 3;
		subset_bits = pfd->chars_used.data;
	    }
	    code = pdf_embed_font_cid0(pdev, (gs_font_cid0 *)font,
				       FontFile_id, subset_bits,
				       subset_size, &font_name);
	    break;
	case ft_CID_TrueType:
	    if (do_subset) {
		/*
		 * Note that the subset mask for CIDFontType 2 fonts is
		 * indexed by the TrueType glyph number, not the CID.
		 */
		subset_size = pfd->glyphs_used.size << 3;
		subset_bits = pfd->glyphs_used.data;
	    }
	    code = pdf_embed_font_cid2(pdev, (gs_font_cid2 *)font,
				       FontFile_id, subset_bits,
				       subset_size, &font_name);
	    break;
	default:
	    code = gs_note_error(gs_error_rangecheck);
	}
	font->FontMatrix = save_mat;
    }
    return code;
}
