//
// Copyright © 2008,2012 by Tobias Hoffmann.
//
// Licensed under Apache License v2.0.  See the file "LICENSE" for more
// information.
//

#include <cupsfilters/fontembed-private.h>
#include <cupsfilters/debug-internal.h>
#include "embed-pdf-private.h"
#include "embed-sfnt-private.h"
#include "frequent-private.h"
#include <errno.h>
#include <string.h>
#include <time.h>


// NOTE: these must be in sync with the _cf_fontembed_emb_format_t enum
static const char *emb_pdf_font_subtype[][2] =
{ // {{{ (output_format, multibyte)
  {"Type1", NULL},
  {"TrueType", "CIDFontType2"},
  {"Type1", "CIDFontType0"},
  {"Type1", "CIDFontType0"},
  {"Type1", NULL}};
// }}}

static const char *emb_pdf_fontfile_key[] =
{ // {{{ (output_format)
  "FontFile",
  "FontFile2",
  "FontFile3",
  "FontFile3",
  NULL};
// }}}

// ... PDF1.6 here
static const char *emb_pdf_fontfile_subtype[][2] =
{ // {{{ (output_format,multibyte)
  {NULL, NULL},
  {NULL, NULL},
  {"OpenType", "OpenType"},
  {"Type1C", "CIDFontType0C"},
  {NULL, NULL}};
// }}}


static inline int
emb_multibyte(_cf_fontembed_emb_params_t *emb) // {{{
{
  return ((emb->plan & _CF_FONTEMBED_EMB_A_MULTIBYTE) ? 1 : 0);
}
// }}}


static const char
*emb_pdf_escape_name(const char *name,
		     int len) // {{{ // - statically allocated buffer
{
  DEBUG_assert(name);
  if (len == -1)
    len = strlen(name);
  DEBUG_assert(len <= 127); // PDF implementation limit

  static char buf[128 * 3];
  int iA, iB;
  const char hex[]="0123456789abcdef";

  for (iA = 0, iB = 0; iA < len; iA ++, iB ++)
  {
    if (((unsigned char)name[iA] < 33) || ((unsigned char)name[iA] > 126) ||
	(strchr("#()<>[]{}/%", name[iA])))
    {
      buf[iB] = '#';
      buf[++iB] = hex[(name[iA] >> 4) & 0x0f];
      buf[++iB] = hex[name[iA] & 0xf];
    }
    else
      buf[iB] = name[iA];
  }
  buf[iB] = 0;
  return (buf);
}
// }}}


// this is in the font dict

const char *
_cfFontEmbedEmbPDFGetFontSubType(_cf_fontembed_emb_params_t *emb) // {{{
{
  DEBUG_assert(emb);
  return (emb_pdf_font_subtype[emb->outtype][emb_multibyte(emb)]);
}
// }}}


// in font descriptor

const char *
_cfFontEmbedEmbPDFGetFontFileKey(_cf_fontembed_emb_params_t *emb) // {{{
{
  DEBUG_assert(emb);
  return (emb_pdf_fontfile_key[emb->outtype]);
}
// }}}


// this is what to put in the font-stream dict

const char *
_cfFontEmbedEmbPDFGetFontFileSubType(_cf_fontembed_emb_params_t *emb) // {{{
{
  DEBUG_assert(emb);
  return (emb_pdf_fontfile_subtype[emb->outtype][emb_multibyte(emb)]);
}
// }}}


// {{{ static _cf_fontembed_emb_pdf_font_descr_t *
//     emb_pdf_fd_new(fontname, subset_tag, cid_registry, cid_ordering,
//                    cid_supplement, panose)

static _cf_fontembed_emb_pdf_font_descr_t *
emb_pdf_fd_new(const char *fontname,
	       const char *subset_tag,
	       const char *cid_registry, // or supplement==-1
	       const char *cid_ordering, // or supplement==-1
	       int cid_supplement) // -1 for non-cid
{
  DEBUG_assert(fontname);
  _cf_fontembed_emb_pdf_font_descr_t *ret;

  int len = sizeof(_cf_fontembed_emb_pdf_font_descr_t);
  if (subset_tag)
  {
    DEBUG_assert(strlen(subset_tag) == 6);
    len += 7;
  }
  len += strlen(fontname) + 1;
  if (cid_supplement >= 0)
  { // cid font
    len += 12; // space for panose
    DEBUG_assert(cid_registry);
    DEBUG_assert(cid_ordering);
    len += strlen(cid_registry) + 1;
    len += strlen(cid_ordering) + 1;
  }
  ret = calloc(1, len);
  if (!ret)
  {
    fprintf(stderr,"Bad alloc: %s\n", strerror(errno));
    DEBUG_assert(0);
    return (NULL);
  }

  // now fill the struct
  len = 0;
  if (cid_supplement >= 0) // free space for panose is at beginning
    len += 12;
  ret->fontname = ret->data + len;
  len += strlen(fontname) + 1;
  if (subset_tag)
  {
    snprintf(ret->fontname, 6, "%s", subset_tag);
    ret->fontname[6] = '+';
    strcpy(ret->fontname + 7, fontname);
    len += 7;
  }
  else
  {
    strcpy(ret->fontname, fontname);
  }
  ret->italicAngle = 90;
  if (cid_supplement >= 0)
  {
    ret->registry = ret->data + len;
    strcpy(ret->registry, cid_registry);
    len += strlen(cid_registry) + 1;

    ret->ordering = ret->data + len;
    strcpy(ret->ordering, cid_ordering);
    len += strlen(cid_registry) + 1;
  }
  ret->supplement = cid_supplement;

  return (ret);
}
// }}}


_cf_fontembed_emb_pdf_font_descr_t *
_cfFontEmbedEmbPDFFontDescr(_cf_fontembed_emb_params_t *emb) // {{{
                                                    // -  to be freed by user
{
  DEBUG_assert(emb);

  const char *subset_tag = NULL;
  // {{{ generate pdf subtag
  static unsigned int rands = 0;
  if (!rands)
    rands = time(NULL);

  char subtag[7];
  subtag[6] = 0;
  if (emb->plan & _CF_FONTEMBED_EMB_A_SUBSET)
  {
    int iA;
    for (iA = 0; iA < 6; iA ++)
    {
      const int x = (int)(26.0 * (rand_r(&rands) / (RAND_MAX + 1.0)));
      subtag[iA] = 'A' + x;
    }
    subset_tag = subtag;
  }
  // }}}

  const char *fontname = NULL;
  if ((emb->intype == _CF_FONTEMBED_EMB_FMT_TTF) ||
      (emb->intype == _CF_FONTEMBED_EMB_FMT_OTF))
  { // TODO? use fontinfo from CFF when outtype==CFT, etc.?
    DEBUG_assert(emb->font->sfnt);
    fontname = __cfFontEmbedEmbOTFGetFontName(emb->font->sfnt);
  }
  else if (emb->outtype == _CF_FONTEMBED_EMB_FMT_STDFONT)
    return (NULL);
  else
  {
    fprintf(stderr, "NOT IMPLEMENTED\n");
    DEBUG_assert(0);
    return (NULL);
  }

  _cf_fontembed_emb_pdf_font_descr_t *ret;
  if (emb->plan & _CF_FONTEMBED_EMB_A_MULTIBYTE) // multibyte
    ret = emb_pdf_fd_new(fontname, subset_tag, "Adobe", "Identity", 0);
                                                      // TODO other /ROS ?
  else
    ret = emb_pdf_fd_new(fontname, subset_tag, NULL, NULL, -1);
  if (!ret)
    return (NULL);

  if ((emb->intype == _CF_FONTEMBED_EMB_FMT_TTF) ||
      (emb->intype == _CF_FONTEMBED_EMB_FMT_OTF))
    __cfFontEmbedEmbOTFGetPDFFontDescr(emb->font->sfnt, ret);
  else
    DEBUG_assert(0);
  return (ret);
}
// }}}


_cf_fontembed_emb_pdf_font_widths_t *
__cfFontEmbedEmbPDFFWNew(int datasize) // {{{
{
  DEBUG_assert(datasize >= 0);
  _cf_fontembed_emb_pdf_font_widths_t *ret =
    calloc(1, sizeof(_cf_fontembed_emb_pdf_font_widths_t) +
	   datasize * sizeof(int));
  if (!ret)
  {
    fprintf(stderr, "Bad alloc: %s\n", strerror(errno));
    DEBUG_assert(0);
    return (NULL);
  }
  return (ret);
}
// }}}


// if default_width == -1: default_width will be estimated

_cf_fontembed_emb_pdf_font_widths_t *
__cfFontEmbedEmbPDFFWCIDWidths(const _cf_fontembed_bit_set_t glyphs,
			       int len,
			       int default_width,
			       int (*getGlyphWidth)(void *context, int gid),
			       void *context) // {{{ glyphs == NULL ->
                                              //                 output all
{
  DEBUG_assert(getGlyphWidth);

  __cf_fontembed_frequent_t *freq = NULL;
  if (default_width < 0)
    freq = __cfFontEmbedFrequentNew(3);

  int iA, b, c;
  int size = 0,
      in_region = 0; // current number of elements in after region start

  // first pass: find continuous regions, calculate needed size, estimate dw
  for (iA = 0, b = 0, c = 1; iA < len; iA ++, c <<= 1)
  {
    if (!c)
    {
      b ++;
      c = 1;
    }
    if ((!glyphs) || (glyphs[b] & c))
    {
      if (freq)
      {
        const int w = (*getGlyphWidth)(context, iA);
        __cfFontEmbedFrequentAdd(freq, w);
      }
      if (in_region)
        in_region ++;
      else
      {
	// start new region
        size += 2; // len c
        in_region=1;
      }
    }
    else
    {
      // region end
      size += in_region;
      in_region = 0;
    }
  }
  size += in_region;

  if (freq)
  {
    default_width = __cfFontEmbedFrequentGet(freq, 0);
    free(freq);
  }
  DEBUG_assert(default_width > 0);

  // now create the array
  _cf_fontembed_emb_pdf_font_widths_t *ret = __cfFontEmbedEmbPDFFWNew(size + 1);
  if (!ret)
    return (NULL);
  ret->default_width = default_width;
  ret->warray = ret->data;

  // second pass
  in_region = 0;
  size = 0;
  int *rlen = 0; // position of current len field (only valid if in_region != 0)
  for (iA = 0, b = 0, c = 1; iA < len; iA ++, c <<= 1)
  {
    if (!c)
    {
      b ++;
      c = 1;
    }
    if ((!glyphs) || (glyphs[b] & c))
    {
      const int w = (*getGlyphWidth)(context, iA);
      if (in_region > 0)
      {
	// in array region
        if ((w == default_width) && (ret->warray[size - 1] == default_width))
	{
	  // omit this and prev entry
          size --;
          *rlen = in_region - 1; // !=0, as it does not start
	                         // with > default_width
          in_region = 0; // end region, immediate restart will take just the
	                 // same amount of space
        }
	else if ((in_region >= 4) &&
		 (ret->warray[size - 1] == w) && (ret->warray[size - 2] == w) &&
		 (ret->warray[size - 3] == w) && (ret->warray[size - 4] == w))
	{
          // five in a row. c1 c2 w [l c] is equally short and can be extended
	  // (-len c1 w)  [w/ cost of array-region restart]
          if (in_region == 4) // completely replace
            size -= 6;
          else
	  {
	    // first end previous region
            size -= 4;
            *rlen = in_region - 4;
          }
          in_region =- 4; // start range region instead
          rlen = &ret->warray[size++];
          ret->warray[size ++] = iA - 4;
          ret->warray[size ++] = w;
        }
	else
	{
	  // just add
          in_region ++;
          ret->warray[size ++] = w;
        }
        continue;
      }
      else if (in_region < 0)
      {
	// in range region
        if (ret->warray[size - 1] == w)
	{
          in_region --; // just add
          continue;
        }
        *rlen = in_region; // end
        in_region = 0;
      }
      if (w != default_width)
      {
	// start new array region
        in_region = 1;
        rlen = &ret->warray[size ++];
        ret->warray[size ++] = iA; // c
        ret->warray[size ++] = w;
      }
    }
    else if (in_region)
    {
      // TODO? no need to stop range region?
      // } else if (in_region<0) { inregion--; }
      *rlen = in_region;
      in_region = 0;
    }
  }
  if (in_region)
    *rlen = in_region;
  ret->warray[size] = 0; // terminator
  return (ret);
}
// }}}


// TODO: Encoding into _cf_fontembed_emb_params_t (emb_new_enc(..., encoding,
//       len, to_unicode));
//   -> will then change interpretation of _cf_fontembed_bit_set_t...
//        (?really?); can we allow dynamic encoding map generation?
//   -> encoding has a "len";  len < 256

_cf_fontembed_emb_pdf_font_widths_t *
_cfFontEmbedEmbPDFFontWidths(_cf_fontembed_emb_params_t *emb) // {{{
{
  DEBUG_assert(emb);

  if ((emb->intype == _CF_FONTEMBED_EMB_FMT_TTF) ||
      (emb->intype == _CF_FONTEMBED_EMB_FMT_OTF))
  {
    DEBUG_assert(emb->font->sfnt);
    if (emb->plan & _CF_FONTEMBED_EMB_A_MULTIBYTE)
      return (__cfFontEmbedEmbOTFGetPDFCIDWidths(emb->font->sfnt, emb->subset));
    else
      return (__cfFontEmbedEmbOTFGetPDFWidths(emb->font->sfnt, /*encoding*/NULL,
					      emb->font->sfnt->numGlyphs,
					      emb->subset)); // TODO: encoding
  }
  else
  {
    fprintf(stderr,"NOT IMPLEMENTED\n");
    DEBUG_assert(0);
    return (NULL);
  }
}
// }}}


// *** PDF out stuff ***
#include "dynstring-private.h"

#define NEXT /* {{{ */ \
  if ((len < 0) || (len >= size)) \
  { \
    DEBUG_assert(0); \
    free(ret); \
    return (NULL);				\
  } \
  pos += len; \
  size -= len; /* }}} */


// TODO? /CIDSet    TODO... /FontFamily /FontStretch /FontWeight (PDF1.5?)
// would be nice...

char *
_cfFontEmbedEmbPDFSimpleFontDescr(_cf_fontembed_emb_params_t *emb,
				  _cf_fontembed_emb_pdf_font_descr_t *fdes,
				  int fontfile_obj_ref) // {{{ - to be freed
                                                        //       by user
{
  DEBUG_assert(emb);
  DEBUG_assert(fdes);

  char *ret = NULL, *pos;
  int len, size;

  size = 300;
  pos = ret = malloc(size);
  if (!ret)
  {
    fprintf(stderr,"Bad alloc: %s\n", strerror(errno));
    return (NULL);
  }

  len = snprintf(pos, size,
		 "<</Type /FontDescriptor\n"
		 "  /FontName /%s\n" // TODO? handle quoting in struct?
		 "  /Flags %d\n"
		 "  /ItalicAngle %d\n",
		 emb_pdf_escape_name(fdes->fontname, -1),
		 fdes->flags,
		 fdes->italicAngle);
  NEXT;

  if (1)
  {
    // TODO type!=EMB_PDF_TYPE3
    len = snprintf(pos, size,
		   "  /FontBBox [%d %d %d %d]\n"
		   "  /Ascent %d\n"
		   "  /Descent %d\n"
		   "  /CapHeight %d\n" // if font has Latin chars
		   "  /StemV %d\n",
		   fdes->bbxmin, fdes->bbymin, fdes->bbxmax, fdes->bbymax,
		   fdes->ascent,
		   fdes->descent,
		   fdes->capHeight,
		   fdes->stemV);
    NEXT;
  }
  if (fdes->xHeight)
  {
    len = snprintf(pos, size, "  /XHeight %d\n", fdes->xHeight);
    NEXT;
  }
  if (fdes->avgWidth)
  {
    len = snprintf(pos, size, "  /AvgWidth %d\n", fdes->avgWidth);
    NEXT;
  }
  if (fdes->panose)
  {
    int iA;
    len = snprintf(pos, size, "  /Style << /Panose <");
    NEXT;
    if (size < 30)
    {
      DEBUG_assert(0);
      free(ret);
      return (NULL);
    }
    for (iA = 0; iA < 12; iA ++)
      snprintf(pos + iA * 2, size - iA * 2, "%02x", fdes->panose[iA]);
    size -= 24;
    pos += 24;
    len = snprintf(pos, size, "> >>\n");
    NEXT;
  }
  // TODO (for Type0)? CIDSet  -> simply our glyphs _cf_fontembed_bit_set_t
  // (ok. endianess?)
  len = snprintf(pos, size,
		 "  /%s %d 0 R\n"
		 ">>\n",
		 _cfFontEmbedEmbPDFGetFontFileKey(emb),
		 fontfile_obj_ref);
  NEXT;

  return (ret);
}
// }}}


char *
_cfFontEmbedEmbPDFSimpleFont(_cf_fontembed_emb_params_t *emb,
			     _cf_fontembed_emb_pdf_font_descr_t *fdes,
			     _cf_fontembed_emb_pdf_font_widths_t *fwid,
			     int fontdescr_obj_ref) // {{{ - to be freed by user
{
  DEBUG_assert(emb);
  DEBUG_assert(fdes);
  DEBUG_assert(fwid);

  int iA, iB;
  __cf_fontembed_dyn_string_t ret;

  if (__cfFontEmbedDynInit(&ret, 500) == -1)
    return (NULL);

  __cfFontEmbedDynPrintF(&ret,
			 "<</Type /Font\n"
			 "  /Subtype /%s\n"
			 "  /BaseFont /%s\n"
			 "  /FontDescriptor %d 0 R\n",
			 _cfFontEmbedEmbPDFGetFontSubType(emb),
			 emb_pdf_escape_name(fdes->fontname,-1),
			 fontdescr_obj_ref);

  if (emb->plan & _CF_FONTEMBED_EMB_A_MULTIBYTE)
  {
    // multibyte
    DEBUG_assert(fwid->warray);
    __cfFontEmbedDynPrintF(&ret,
			   "  /CIDSystemInfo <<\n"
			   "    /Registry (%s)\n"
			   "    /Ordering (%s)\n"
			   "    /Supplement %d\n"
			   "  >>\n"
			   "  /DW %d\n",
			   //"  /CIDToGIDMap /Id...\n" // TrueType only, default
			   // Identity  [optional?  which
			   // PDF version says what?]
			   fdes->registry,
			   fdes->ordering,
			   fdes->supplement,
			   fwid->default_width);

    if (fwid->warray[0])
    {
      __cfFontEmbedDynPrintF(&ret, "  /W [");
      for (iA = 0; fwid->warray[iA];)
      {
        if (fwid->warray[iA] < 0)
	{
	  // c1 (c1-len) w
          __cfFontEmbedDynPrintF(&ret, " %d %d %d",
				 fwid->warray[iA + 1],
				 fwid->warray[iA + 1] - fwid->warray[iA],
				 fwid->warray[iA + 2]);
          iA += 3;
        }
	else
	{
	  // c [w ... w]
          iB = fwid->warray[iA ++]; // len
          __cfFontEmbedDynPrintF(&ret, " %d [", fwid->warray[iA ++]); // c
          for (; iB > 0; iB --)
            __cfFontEmbedDynPrintF(&ret, " %d", fwid->warray[iA ++]);
          __cfFontEmbedDynPrintF(&ret, "]");
        }
      }
      __cfFontEmbedDynPrintF(&ret, "]\n");
    }
  }
  else
  {
    // "not std14"
    DEBUG_assert(fwid->widths);
    __cfFontEmbedDynPrintF(&ret,
			   "  /Encoding /MacRomanEncoding\n"// optional; TODO!
			   //"  /ToUnicode ?\n"  // optional
			   "  /FirstChar %d\n"
			   "  /LastChar %d\n"
			   "  /Widths [",
			   fwid->first,
			   fwid->last);
    for (iA = 0, iB = fwid->first; iB <= fwid->last; iA ++, iB ++)
      __cfFontEmbedDynPrintF(&ret, " %d", fwid->widths[iA]);
    __cfFontEmbedDynPrintF(&ret, "]\n");
  }
  __cfFontEmbedDynPrintF(&ret, ">>\n");
  if (ret.len == -1)
  {
    __cfFontEmbedDynFree(&ret);
    DEBUG_assert(0);
    return (NULL);
  }

  return (ret.buf);
}
// }}}


// TODO? + encoding as param?  TODO + ToUnicode cmap
//                             => we need another struct EMB_PDF_FONTMAP
// (TODO?? fontname here without subset-tag [_some_ pdfs out there seem
//  to be that way])
// TODO? don't do the CidType0 check here?
// NOTE: this is _additionally_ to _cfFontEmbedEmbPDFSimpleFont()!

char *
_cfFontEmbedEmbPDFSimpleCIDFont(_cf_fontembed_emb_params_t *emb,
		       const char *fontname,
		       int descendant_obj_ref) // {{{ - to be freed by user
{
  DEBUG_assert(emb);
  DEBUG_assert(fontname);

  char *ret = NULL, *pos;
  int len, size;

  size = 250;
  pos = ret = malloc(size);
  if (!ret)
  {
    fprintf(stderr, "Bad alloc: %s\n", strerror(errno));
    return (NULL);
  }
  // for CFF: one of:
  // UniGB-UCS2-H, UniCNS-UCS2-H, UniJIS-UCS2-H, UniKS-UCS2-H
  const char *encoding = "Identity-H", *addenc = "-";
  if (emb->outtype == _CF_FONTEMBED_EMB_FMT_TTF)
    // !=CidType0
    addenc = "";

  len = snprintf(pos, size,
		 "<</Type /Font\n"
		 "  /Subtype /Type0\n"
		 "  /BaseFont /%s%s%s\n"
		 "  /Encoding /%s\n"
		 "  /DescendantFonts [%d 0 R]\n",
//               "  /ToUnicode ?\n" // TODO
		 emb_pdf_escape_name(fontname, -1),
		 addenc, ((addenc[0]) ? encoding : ""),
		 encoding,
		 descendant_obj_ref);
  NEXT;

  len = snprintf(pos, size, ">>\n");
  NEXT;

  return (ret);
}
// }}}


char *
_cfFontEmbedEmbPDFSimpleStdFont(_cf_fontembed_emb_params_t *emb)
                                                 // {{{ - to be freed by user
{
  DEBUG_assert(emb);
  DEBUG_assert(emb->font->stdname);

  char *ret = NULL, *pos;
  int len, size;

  size=300;
  pos = ret = malloc(size);
  if (!ret)
  {
    fprintf(stderr, "Bad alloc: %s\n", strerror(errno));
    return (NULL);
  }

  len = snprintf(pos, size,
		 "<</Type/Font\n"
		 "  /Subtype /Type1\n"
		 "  /BaseFont /%s\n"
		 ">>\n",
//               _cfFontEmbedEmbPDFGetFontSubType(emb),
		 emb->font->stdname);
  NEXT;

  return (ret);
}
// }}}


#undef NEXT
