/* font.c */

/************************************************************************

  Part of the dvipng distribution

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as
  published by the Free Software Foundation, either version 3 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this program. If not, see
  <http://www.gnu.org/licenses/>.

  Copyright (C) 2002-2015 Jan-Åke Larsson

************************************************************************/

#include "dvipng.h"

void CheckChecksum(uint32_t c1, uint32_t c2, const char* name)
{
  /* Report a warning if both checksums are nonzero, they don't match,
     and the user hasn't turned it off.  */
  if (c1 && c2 && c1 != c2
#ifdef HAVE_LIBKPATHSEA
      && !kpse_tex_hush ("checksum")
#endif
      ) {
     Warning ("checksum mismatch in %s", name) ;
   }
}


static double ActualFactor(uint32_t unmodsize)
/* compute the actual size factor given the approximation */
/* actually factor * 1000 */
{
  double  realsize;     /* the actual magnification factor */
  realsize = (double)unmodsize / 1000.0;
  if (abs((int)(unmodsize - 1095l))<2)
    realsize = 1.095445115; /*stephalf*/
  else if (abs((int)(unmodsize - 1315l))<2)
    realsize = 1.31453414; /*stepihalf*/
  else if (abs((int)(unmodsize - 1577l))<2)
    realsize = 1.57744097; /*stepiihalf*/
  else if (abs((int)(unmodsize - 1893l))<2)
    realsize = 1.89292916; /*stepiiihalf*/
  else if (abs((int)(unmodsize - 2074l))<2)
    realsize = 2.0736;   /*stepiv*/
  else if (abs((int)(unmodsize - 2488l))<2)
    realsize = 2.48832;  /*stepv*/
  else if (abs((int)(unmodsize - 2986l))<2)
    realsize = 2.985984; /*stepvi*/
  /* the remaining magnification steps are represented with sufficient
     accuracy already */
  return(realsize);
}


void FontDef(unsigned char* command, void* parent)
{
  int32_t k;
  uint32_t   c, s, d;
  uint8_t    a, l;
  unsigned char* current;
  struct font_entry *tfontptr; /* temporary font_entry pointer   */
  struct font_num *tfontnump = NULL;  /* temporary font_num pointer   */
  unsigned short i;

  current = command + 1;
  k = UNumRead(current, (int)*command - FNT_DEF1 + 1);
  current += (int)*command - FNT_DEF1 + 1;
  c = UNumRead(current, 4); /* checksum */
  s = UNumRead(current+4, 4); /* space size */
  d = UNumRead(current+8, 4); /* design size */
  a = UNumRead(current+12, 1); /* length for font name */
  l = UNumRead(current+13, 1); /* device length */
  if (((struct font_entry*)parent)->type==FONT_TYPE_VF) {
    DEBUG_PRINT(DEBUG_VF,(" %d %d %d",k,c,s));
    /* Rescale. s is relative to the actual scale /(1<<20) */
    s = (uint32_t)((uint64_t) s * (((struct font_entry*) parent)->s)
		   / (1<<20));
    DEBUG_PRINT(DEBUG_VF,(" (%d) %d",s,d));
    /* Oddly, d differs in the DVI and the VF that my system produces */
    d = (uint32_t)((uint64_t) d * ((struct font_entry*)parent)->d
		   / ((struct font_entry*)parent)->designsize);
    DEBUG_PRINT(DEBUG_VF,(" (%d)",d));
    DEBUG_PRINT(DEBUG_VF,(" %d %d '%.*s'",a,l,a+l,current+14));
#ifdef DEBUG
  } else {
    DEBUG_PRINT(DEBUG_DVI,(" %d %d %d %d %d %d '%.*s'",k,c,s,d,a,l,
		 a+l,current+14));
#endif
  }
  if (a+l > STRSIZE-1)
    Fatal("too long font name for font %ld",k);

  /* Find entry with this font number in use */
  switch (((struct font_entry*)parent)->type) {
  case FONT_TYPE_VF:
    tfontnump = ((struct font_entry*)parent)->vffontnump;
    break;
  case DVI_TYPE:
    tfontnump = ((struct dvi_data*)parent)->fontnump;
  }
  while (tfontnump != NULL && tfontnump->k != k) {
    tfontnump = tfontnump->next;
  }
  /* If found, return if it is correct */
  if (tfontnump!=NULL
      && tfontnump->fontp->s == s
      && tfontnump->fontp->d == d
      && strlen(tfontnump->fontp->n) == a+l
      && strncmp(tfontnump->fontp->n,(char*)current+14,a+l) == 0) {
    DEBUG_PRINT((DEBUG_DVI|DEBUG_VF),("\n  FONT %d:\tMatch found",k));
    return;
  }
  /* If not found, create new */
  if (tfontnump==NULL) {
    if ((tfontnump=malloc(sizeof(struct font_num)))==NULL)
      Fatal("cannot malloc memory for new font number");
    tfontnump->k=k;
    switch (((struct font_entry*)parent)->type) {
    case FONT_TYPE_VF:
      tfontnump->next=((struct font_entry*)parent)->vffontnump;
      ((struct font_entry*)parent)->vffontnump=tfontnump;
      break;
    case DVI_TYPE:
      tfontnump->next=((struct dvi_data*)parent)->fontnump;
      ((struct dvi_data*)parent)->fontnump=tfontnump;
    }
  }

  /* Search font list for possible match */
  tfontptr = hfontptr;
  while (tfontptr != NULL
	 && (tfontptr->s != s
	     || tfontptr->d != d
	     || strlen(tfontptr->n) != a+l
	     || strncmp(tfontptr->n,(char*)current+14,a+l) != 0 ) ) {
    tfontptr = tfontptr->next;
  }
  /* If found, set its number and return */
  if (tfontptr!=NULL) {
    DEBUG_PRINT((DEBUG_DVI|DEBUG_VF),("\n  FONT %d:\tMatch found, number set",k));
    tfontnump->fontp = tfontptr;
    return;
  }

  DEBUG_PRINT((DEBUG_DVI|DEBUG_VF),("\n  FONT %d:\tNew entry created",k));
  /* No fitting font found, create new entry. */
  if ((tfontptr = calloc(1,sizeof(struct font_entry))) == NULL)
    Fatal("cannot malloc space for font_entry");
  tfontptr->next = hfontptr;
  hfontptr = tfontptr;
  tfontnump->fontp = tfontptr;
#ifndef WIN32
  tfontptr->fmmap.fd = 0;
#else  /* WIN32 */
  tfontptr->fmmap.hFile = INVALID_HANDLE_VALUE;
#endif
  tfontptr->c = c; /* checksum */
  tfontptr->s = s; /* space size */
  tfontptr->d = d; /* design size */
  tfontptr->a = a; /* length for font name */
  tfontptr->l = l; /* device length */
  strncpy(tfontptr->n,(char*)current+14,a+l); /* full font name */
  tfontptr->n[a+l] = '\0';

  tfontptr->name = NULL;
  for (i = FIRSTFNTCHAR; i <= LASTFNTCHAR; i++) {
    tfontptr->chr[i] = NULL;
  }

  tfontptr->dpi =
    (uint32_t)((ActualFactor((uint32_t)(1000.0*tfontptr->s
				  /(double)tfontptr->d+0.5))
	     * ActualFactor(dvi->mag) * dpi*shrinkfactor) + 0.5);
#ifdef HAVE_FT2
  tfontptr->psfontmap=NULL;
#endif
}

#ifdef HAVE_FT2
static char* kpse_find_t1_or_tt(char* filename)
{
    char* filepath = kpse_find_file(filename, kpse_type1_format, false);
    if ((option_flags & USE_FREETYPE) && filepath==NULL)
      filepath = kpse_find_file(filename, kpse_truetype_format, false);
    return(filepath);
}
#endif

static void FontFind(struct font_entry * tfontptr)
{
  kpse_glyph_file_type font_ret;

  /* tfontptr->dpi = kpse_magstep_fix (tfontptr->dpi, resolution, NULL); */
  DEBUG_PRINT(DEBUG_DVI,("\n  FIND FONT:\t%s %d",tfontptr->n,tfontptr->dpi));

  tfontptr->name = kpse_find_vf (tfontptr->n);
  if (tfontptr->name!=NULL)
    InitVF(tfontptr);
#ifdef HAVE_FT2
  else if (option_flags & USE_FREETYPE) {
    tfontptr->psfontmap = FindPSFontMap(tfontptr->n);
    if (tfontptr->psfontmap!=NULL)
      tfontptr->name=kpse_find_t1_or_tt(tfontptr->psfontmap->psfile);
    else
      tfontptr->name=kpse_find_t1_or_tt(tfontptr->n);
    if (tfontptr->name!=NULL) {
      char* tfmname=kpse_find_file(tfontptr->n, kpse_tfm_format, false);
      if (tfmname!=NULL) {
        if (!ReadTFM(tfontptr,tfmname)) {
          Warning("unable to read tfm file %s", tfmname);
          free(tfontptr->name);
          tfontptr->name=NULL;
        } else if ((option_flags & USE_FREETYPE)==0 || !InitFT(tfontptr)) {
          /* if Freetype loading fails for some reason, fall back to PK font */
          free(tfontptr->name);
          tfontptr->name=NULL;
        }
        free(tfmname);
      }
    }
  }
#endif /* HAVE_FT2 */
  if (tfontptr->name==NULL) {
    tfontptr->name=kpse_find_pk (tfontptr->n, tfontptr->dpi, &font_ret);
    if (tfontptr->name!=NULL) {
      if (!FILESTRCASEEQ (tfontptr->n, font_ret.name)) {
        page_flags |= PAGE_GAVE_WARN;
        Warning("font %s not found, using %s at %d dpi instead",
                tfontptr->n, font_ret.name, font_ret.dpi);
        tfontptr->c = 0; /* no checksum warning */
      } else if (!kpse_bitmap_tolerance ((double)font_ret.dpi,
           (double) tfontptr->dpi)) {
        page_flags |= PAGE_GAVE_WARN;
        Warning("font %s at %d dpi not found, using %d dpi instead",
                tfontptr->n, tfontptr->dpi, font_ret.dpi);
      }
      InitPK(tfontptr);
    } else {
      page_flags |= PAGE_GAVE_WARN;
      Warning("font %s at %d dpi not found, characters will be left blank",
        tfontptr->n, tfontptr->dpi);
#ifndef WIN32
      tfontptr->fmmap.fd = 0;
#else  /* WIN32 */
      tfontptr->fmmap.hFile = INVALID_HANDLE_VALUE;
#endif
      tfontptr->magnification = 0;
      tfontptr->designsize = 0;
    }
  }
}


static void DoneFont(struct font_entry *tfontp)
{
  switch (tfontp->type) {
  case FONT_TYPE_PK:
    DonePK(tfontp);
    break;
  case FONT_TYPE_VF:
    DoneVF(tfontp);
    break;
#ifdef HAVE_FT2
  case FONT_TYPE_FT:
    DoneFT(tfontp);
    break;
#endif
  }
}


void FreeFontNumP(struct font_num *hfontnump)
{
  struct font_num *tmp;
  while(hfontnump!=NULL) {
    tmp=hfontnump->next;
    free(hfontnump);
    hfontnump=tmp;
  }
}

void ClearFonts(void)
{
  struct font_entry *tmp;

  while(hfontptr!=NULL) {
    tmp=hfontptr->next;
    DoneFont(hfontptr);
    if (hfontptr->name != NULL)
      free(hfontptr->name);
    free(hfontptr);
    hfontptr=tmp;
  }
  if (dvi!=NULL)
    FreeFontNumP(dvi->fontnump);
}

/*-->SetFntNum*/
/**********************************************************************/
/****************************  SetFntNum  *****************************/
/**********************************************************************/
void SetFntNum(int32_t k, void* parent /* dvi/vf */)
/*  this routine is used to specify the font to be used in printing future
    characters */
{
  struct font_num *tfontnump=NULL;  /* temporary font_num pointer   */

  switch (((struct font_entry*)parent)->type) {
  case FONT_TYPE_VF:
    tfontnump = ((struct font_entry*)parent)->vffontnump;
    break;
  case DVI_TYPE:
    tfontnump = ((struct dvi_data*)parent)->fontnump;
  }
  while (tfontnump != NULL && tfontnump->k != k)
    tfontnump = tfontnump->next;
  if (tfontnump == NULL)
    Fatal("font %d undefined", k);

  currentfont = tfontnump->fontp;
  if (currentfont->name==NULL)
    FontFind(currentfont);
}