/* Copyright 1997 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/***************************************************/
/* File   : FontManage.c                           */
/*                                                 */
/* Purpose: A range of font management veneers.    */
/*                                                 */
/* Author : Merlyn Kline for Customer browser     */
/*          This source adapted by A.D.Hodgkinson  */
/*                                                 */
/* History: 05-Dec-96: Created.                    */
/*          14-Apr-97: Merged in a bug fix to      */
/*                     fm_get_string_width spotted */
/*                     by T.Cheal (see source for  */
/*                     more info).                 */
/***************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#include "swis.h"

#include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */

#include "wimp.h"
#include "event.h"

#include "svcprint.h"
#include "Global.h"
#include "FromROSLib.h"
#include "MiscDefs.h"
#include "Utils.h"

#include "Redraw.h"

#include "FontManage.h"

/* Conditional includes */

#ifdef UNIFONT
  #include "Unicode/iso10646.h"
  #include "Unicode/utf8.h"
  #include "Unifont.h"
#endif

#ifndef Font_WideFormat
  #define Font_WideFormat 0x400A9
#endif

/**************************************************************************************************/
/*                                                                                                */
/* Merlyn's original overview comments (corrected where necessary, due to changes in the source): */
/* ============================================================================================== */
/*                                                                                                */
/* RISC OS Font management                                                                        */
/*                                                                                                */
/* Merlyn Kline, 1995                                                                             */
/*                                                                                                */
/* This module provides a range of veneers for font management. It allows a typeface              */
/* to be defined including bold, italic and bold-italic attributes. These typefaces               */
/* are given names by which they are referred to in subsequent use. When defining a               */
/* typeface an alternative to be used can be specified for cases where the primary                */
/* selection is unavailable for some reason. If the alternative does not exist, *its*             */
/* alternative is checked. This goes on for several attempts (about 4) before giving              */
/* up after which the ordinary system font is used, scaled appropriately. This limit              */
/* on the number of attempts improves performance and means you don't have to worry too           */
/* much about defining loops (eg pairs of typefaces which specify each other as alternatives).    */
/*                                                                                                */
/* The system provides veneers for all font related functions and these use an internal           */
/* font handle. This font handle is either the RISC OS font handle when an appropriate            */
/* font was available or a negative number made up from the x and y font sizes.                   */
/*                                                                                                */
/* Note the use of the word 'typeface' to describe the four-font structure used by this           */
/* module. This is to avoid confusion with the RISC OS term 'font' underlying it.                 */
/*                                                                                                */
/* Typefaces are defined by passing strings to the font definition routine, which contain         */
/* a description of the desired font, like:                                                       */
/*                                                                                                */
/*    name=normal:italic:bold:bolditalic;alternative                                              */
/*                                                                                                */
/* where:                                                                                         */
/*                                                                                                */
/*    name         is the name you want to refer to this typeface by (case *sensitive* for speed) */
/*    normal       is the RISC OS font name to use in normal rendering                            */
/*    italic       is the RISC OS font name to use in italic rendering                            */
/*    bold         is the RISC OS font name to use in bold rendering                              */
/*    bolditalic   is the RISC OS font name to use in bold plus italic rendering                  */
/*    alternative  is the alternative typeface name to use if necessary                           */
/*                                                                                                */
/* Any of these fields may be missing except <name>. If the font names used for                   */
/* bold/italic rendering are missing, the normal one will be used instead. If                     */
/* the normal font name is missing, the alternative typface is used. If the                       */
/* alternative is missing, system font will be used when the requested fonts are                  */
/* not available.                                                                                 */
/*                                                                                                */
/* When defining typefaces for optimum performance define the one which will be used              */
/* most, last. This is because they are kept in a linked list and new ones are added to           */
/* the start of the list and so will be found more quickly.                                       */
/*                                                                                                */
/* If you define a typeface with the same name as an existing one the new definition              */
/* overwrites the old one.                                                                        */
/*                                                                                                */
/* Default font definitions are available as follows:                                             */
/*                                                                                                */
/*   system=System.Fixed:Corpus.Medium.Oblique:Corpus.Bold:Corpus.Bold.Oblique;fixed              */
/*   sans=Homerton.Medium:Homerton.Medium.Oblique:Homerton.Bold:Homerton.Bold.Oblique;serif       */
/*   serif=Trinity.Medium:Trinity.Medium.Italic:Trinity.Bold:Trinity.Bold.Italic;sans             */
/*   fixed=Corpus.Medium:Corpus.Medium.Oblique:Corpus.Bold:Corpus.Bold.Oblique                    */
/*                                                                                                */
/* Font sizes are defined in sixteenths of a point. The system font is treated as a font          */
/* with a 2:1 aspect ratio so set both xsize and ysize to the same to get normal looking text.    */
/* A size of 205 (=8*2*16*72/90 ie:                                                               */
/*                                                                                                */
/*    pixels * os_units_per_pixel * sixteenths_of_a _point_per_inch / os_units_per_inch )         */
/*                                                                                                */
/* will get system font at its normal size. This size should therefore be used for any display    */
/* of information where size is pretty arbitrary, ensuring a reasonable appearnce should system   */
/* font be used. To help, it is #defined as FM_Standard_Size in FontManage.h.                     */
/*                                                                                                */
/*                     ------------------------------------------------------                     */
/*                                                                                                */
/* (Merlyn then goes on to describe the font menu display and interpretation functions, which are */
/* more or less redundant in this source due to the Toolbox's automation).                        */
/*                                                                                                */
/* Note that some specialisation of the library for use in the browser has been necessary, so     */
/* for example references to browser_data structures would need to be removed were this version   */
/* of the library to be used elsewhere.                                                           */
/*                                                                                                */
/**************************************************************************************************/

/* Statics */

static fm_typeface * fm_new_typeface  (void);
static fm_face       fm_sub_find_font (fm_typeface * f, int xsize, int ysize, int italic, int bold);

static fm_facelist * fm_list       = NULL;
static int           fm_systemfont = 0;
static int           fm_allowblend = -1;

/* Locals */

/* fm_faces allows typeface numbers to be defined internally */
/* which relate to typeface names held by the font managing  */
/* routines; these are easier to deal with than strings.     */

char * fm_faces[] = {
                      "serif",
                      "sans",
                      "fixed"
                    };

/* fm_handles records usage of font handles. For every font  */
/* font claim, an appropriate bit is examined in the         */
/* browser_data structure of the claimant (if available). If */
/* the bit is unset, it is set and the entry in fm_handles   */
/* incremented by one. For every font discard, the bit is    */
/* examined again and if set, it is cleared, and the entry   */
/* in fm_handles decremented by one. If that entry is now    */
/* zero, Font_LoseFont is called for the handle.             */

int fm_handles[Limits_OS_FontHandles];

/* fm_sizes allows quick lookup of a font size number, which */
/* otherwise would needed to be calculated on the fly (this  */
/* is slow). Initialisation is done in fm_init, and the size */
/* is looked up through fm_size.                             */
/*                                                           */
/* Whilst fm_find_token_font scales using this, fm_find_font */
/* doesn't. For speed, it's quicker if the caller can do the */
/* scaling, if needed, when falling fm_find_font.            */

int fm_sizes[Limits_FontSizes + 1]; /* (Indices 1 to Limits_FontSizes are used, with index 0 */
                                    /* filled with base size in case something goes wrong    */
                                    /* and a size of 0 is given)                             */

/*************************************************/
/* fm_find_typeface()                            */
/*                                               */
/* Goes through the linked list of fm_facelist   */
/* structures looking for one which is named,    */
/* returning the address of the fm_typeface      */
/* structure containing that name if it is found */
/* (or NULL if not). The name should be in lower */
/* case charaters.                               */
/*                                               */
/* Parameters: A pointer to a string holding the */
/*             name of the typeface to find, in  */
/*             lower case.                       */
/*                                               */
/* Returns:    Pointer to the fm_typeface struct */
/*             if found, else NULL.              */
/*************************************************/

fm_typeface * fm_find_typeface(char * name)
{
  fm_facelist * f;

  /* Set f to point to the start of the list */
  /* of fm_facelist structures, and go up    */
  /* the list looking for the named font.    */
  /* Return the address of the fm_typeface   */
  /* structure itself (rather than the       */
  /* fm_facelist structure that contains it) */
  /* if found, NULL if not.                  */

  f = fm_list;

  while (f)
  {
    if (!strcmp(f->face.name, name)) return &f->face;
    f = f->next;
  }

  return NULL;
}

/*************************************************/
/* fm_new_typeface()                             */
/*                                               */
/* Adds a new typeface to the list of typefaces. */
/* Returns a pointer to the fm_typeface struct   */
/* itself, when added, or NULL if the malloc     */
/* claim failed (so error check this externally) */
/*                                               */
/* Returns:    Pointer to the new fm_typeface    */
/*             struct, or NULL if malloc failed. */
/*************************************************/

static fm_typeface * fm_new_typeface(void)
{
  fm_facelist * f;

  /* Allocate memory for the fm_facelist struct */

  #ifdef TRACE
    if (tl & (1u<<12)) Printf("fm_new_typeface: malloc %d for 'fm_facelist' structure\n",sizeof(fm_facelist));
  #endif

  f = calloc(1, sizeof(fm_facelist));

  if (f)
  {
    #ifdef TRACE
      malloccount += sizeof(fm_facelist);
      if (tl & (1u<<13)) Printf("** malloccount (fm_new_typeface): \0211%d\0217\n",malloccount);
    #endif

    /* If the claim succeeded, link the new item into */
    /* the list, and return with the address of the   */
    /* fm_typeface structure.                         */

    f->next = fm_list;
    fm_list = f;

    return &f->face;
  }

  /* The malloc claim failed, so return NULL */

  return NULL;
}

/*************************************************/
/* fm_define_typeface_explicit()                 */
/*                                               */
/* Adds a new fm_facelist structure to the list  */
/* of fm_facelist structures, but also names     */
/* the new typeface and only allocates a new     */
/* list item if one of the same name wasn't      */
/* already present.                              */
/*                                               */
/* Parameters: A pointer to an fm_typeface       */
/*             structure, in which only the      */
/*             'name' field is filled in. This   */
/*             becomes the name for the new      */
/*             typeface and is used to check if  */
/*             a typeface of that name is        */
/*             already present.                  */
/*************************************************/

_kernel_oserror * fm_define_typeface_explicit(fm_typeface * f)
{
  fm_typeface * fp;

  /* Try to find a typeface of the same name in the list */

  fp = fm_find_typeface(f->name);

  /* If there isn't one, create a new typeface entry */

  if (!fp) fp = fm_new_typeface();

  /* Report an error if the new typeface entry couldn't be created */

  if (!fp)
  {
    erb.errnum = Utils_Error_Custom_Fatal;

    StrNCpy0(erb.errmess,
             lookup_token("FontMem:There is not enough free memory for font handling (%0).",
                          0,
                          "1"));

    return(&erb);
  }

  /* Copy whole structure over to fp */

  *fp = *f;

  return(NULL);
}

/*************************************************/
/* fm_define_typeface()                          */
/*                                               */
/* This function defines a whole new typeface in */
/* full, according to a description string the   */
/* format of which is described in Merlyn's      */
/* comments at the top of this file.             */
/*                                               */
/* Parameters: A pointer to a string containing  */
/*             the typeface description.         */
/*************************************************/

_kernel_oserror * fm_define_typeface(char * description)
{
  fm_typeface   f;
  char        * p = description;
  int           i = 0;

  /* Ensure that the fm_typeface struct has been */
  /* allocated locally, then zero the contents   */

  f.name[0] = 0;
  memset(&f,0,sizeof(f));

  /* Travel through the description string looking for */
  /* an equals sign - this separates the name of the   */
  /* typeface from the actual RISC OS fonts it uses.   */
  /* Copy the text - which is the typeface name - into */
  /* the 'name' field of the new typeface structure    */
  /* that was filled with zeros above.                 */

  while ((*p) && (*p != '='))
  {
    if (i < (sizeof(f.name) - 1)) f.name[i++] = *p++;
  }

  /* If we're not now at the end of the string, get the */
  /* four RISC OS font names from the string.           */

  if (*p)
  {
    int n;

    for (n = 0; n < 4; n++)
    {
      p++; /* Skip past the =, : or ; that we last ended on */
      i=0;

      /* Copy between the current position and the next separator */
      /* (i.e. the font name) into the relevant fontname field of */
      /* the fm_typeface structure.                               */

      while ((*p) && (*p != ':') && (*p != ';'))
      {
        if (i < sizeof(f.fontnames[n]) - 1) f.fontnames[n][i++] = *p++;
      }

      /* If we've reached the end of the string or a ';', exit now */
      /* as there are no more font names.                          */

      if ((!*p) || (*p == ';')) break;
    }

    /* If there's a semicolon, there is an alternative typeface */
    /* name defined                                             */

    if (*p == ';')
    {
      p++; /* Skip past the ; */

      /* Copy the alternative typeface name to the fm_typeface struct */

      strncpy(f.alternative, p, sizeof(f.alternative) - 1);
    }
  }

  /* If there's at least one font name present, */
  /* actually allocate the memory for the       */
  /* typeface (or copy over an old one with the */
  /* same name).                                */

  if (f.name[0]) return fm_define_typeface_explicit(&f);

  /* There are no font names - the typeface is badly defined */

  erb.errnum = Utils_Error_Custom_Fatal;

  StrNCpy0(erb.errmess,
           lookup_token("BadType:Internal error - Bad typeface definition in %0.",
                        0,
                        "fm_define_typeface"));
  return &erb;
}

/*************************************************/
/* fm_define_default_typefaces()                 */
/*                                               */
/* Sets up the default typeface settings, for    */
/* system, fixed space, sans serif and serif     */
/* font 'banks'.                                 */
/*************************************************/

_kernel_oserror * fm_define_default_typefaces(void)
{
  /* These will be overridden if typefaces of the same name are read */
  /* in by load_choices later.                                       */

  RetError(fm_define_typeface("fixed=Corpus.Medium:Corpus.Medium.Oblique:Corpus.Bold:Corpus.Bold.Oblique"));
  RetError(fm_define_typeface("sans=Homerton.Medium:Homerton.Medium.Oblique:Homerton.Bold:Homerton.Bold.Oblique;serif"));
  RetError(fm_define_typeface("serif=Trinity.Medium:Trinity.Medium.Italic:Trinity.Bold:Trinity.Bold.Italic;sans"));

  return NULL;
}

/*************************************************/
/* fm_claim_basic_typefaces()                    */
/*                                               */
/* Claim a few basic fonts which will probably   */
/* be needed for the duration of the application */
/* run time.                                     */
/*                                               */
/* Parameters: Base font size to claim at.       */
/*************************************************/

void fm_claim_basic_typefaces(int base_size)
{
  fm_face h;

  #define FMCIncHandles(f) {                                              \
                             if (f >= 0)                                  \
                             {                                            \
                               if (!fm_handles[(f)]) fm_handles[(f)] = 1; \
                               else fm_lose_font(NULL, (f));              \
                             }                                            \
                           }

  if (!choices.system_font)
  {
    /* Need to be a bit careful here... Must not claim a font twice, if the */
    /* typeface is the same (e.g. System.Fixed for both fixed width normal  */
    /* and fixed width bold). The fm_handles array is an *internal* usage   */
    /* count - as far as the RISC OS Font Manager is concerned, for every   */
    /* font marked as used inside fm_handles, there's only one user in the  */
    /* browser. So if we claim something two or more times here, it'll only */
    /* get released once later on, thereby 'leaking' the font handle.       */

    h = fm_find_font(NULL, "sans",  base_size, base_size, 0, 0); FMCIncHandles(h)
    h = fm_find_font(NULL, "sans",  base_size, base_size, 0, 1); FMCIncHandles(h)
    h = fm_find_font(NULL, "sans",  base_size, base_size, 1, 0); FMCIncHandles(h)
    h = fm_find_font(NULL, "sans",  base_size, base_size, 1, 1); FMCIncHandles(h)

    h = fm_find_font(NULL, "serif", base_size, base_size, 0, 0); FMCIncHandles(h)
    h = fm_find_font(NULL, "serif", base_size, base_size, 1, 0); FMCIncHandles(h)
    h = fm_find_font(NULL, "serif", base_size, base_size, 0, 1); FMCIncHandles(h)
    h = fm_find_font(NULL, "serif", base_size, base_size, 1, 1); FMCIncHandles(h)

    h = fm_find_font(NULL, "fixed", base_size, base_size, 0, 0); FMCIncHandles(h)
    h = fm_find_font(NULL, "fixed", base_size, base_size, 0, 1); FMCIncHandles(h)
  }
}

/*************************************************/
/* fm_sub_find_font()                            */
/*                                               */
/* When asked for a standard, italic, bold or    */
/* bold italic variant of a typeface, this will  */
/* return the font handle for the requested font */
/* or -1 if it can't be found. The routine       */
/* will try for just a standard or bold variant  */
/* if an italic or bold italic variant isn't     */
/* defined, then drop to just standard, before   */
/* giving up.                                    */
/*                                               */
/* Parameters: Pointer to the fm_typeface struct */
/*             for the typeface in question;     */
/*                                               */
/*             The X and Y sizes, in 1/16ths of  */
/*             a point, of the font to claim;    */
/*                                               */
/*             1 to specify italics, else 0;     */
/*                                               */
/*             1 to specify bold, else 0.        */
/*                                               */
/* Returns:    The RISC OS font manager's font   */
/*             handle for the requested font (as */
/*             an fm_face) or -1 if the font     */
/*             couldn't be found either by this  */
/*             routine, or by the font manager   */
/*             itself.                           */
/*************************************************/

static fm_face fm_sub_find_font(fm_typeface * f, int xsize, int ysize, int italic, int bold)
{
  int        n;
  fm_face    h;

  /* Set bit 0 of n if italic was specified, and bit 1 */
  /* if bold was specified.                            */

  n=(italic ? 1 : 0) | (bold ? 2 : 0);

  /* So n = 0 for standard, 1 for italic, 2 for bold */
  /* and 3 for bold italic.                          */

  /* If there is no fontname defined for that combination, */
  /* AND n with 2 - i.e., drop the italic specifier and    */
  /* just try plain / bold.                                */

  if (!f->fontnames[n][0]) n &= 2;

  /* If there is still no font name, try the standard only */

  if (!f->fontnames[n][0]) n = 0;

  /* If there is *still* no font name, we can't match this */
  /* so flag that by returning -1.                         */

  if (!f->fontnames[n][0]) return -1;

  /* If the font manager can't find the font, again flag the */
  /* problem by returning -1. Otherwise, return the font     */
  /* handle returned by the font manager.                    */

  if (_swix(Font_FindFont,
            _INR(1,5) | _OUT(0),

            f->fontnames[n],     /* Font name to find    */
            xsize,               /* x size in 16ths pt   */
            ysize,               /* y size in 16ths pt   */
            180 / wimpt_dx(),    /* Current mode's x dpi */
            180 / wimpt_dy(),    /* Current mode's y dpi */

            &h))                 /* Font handle put in h */

            return -1;

  return h;
}

/*************************************************/
/* fm_size()                                     */
/*                                               */
/* Given an abstracted font size (from 1 to 7,   */
/* with 3 being 'base font' size), returns the   */
/* actual size (in 16ths of a point) to use.     */
/*                                               */
/* Parameters: The font size, from 1 to 7.       */
/*                                               */
/* Returns:    The equivalent font size, in      */
/*             16ths of a point.                 */
/*                                               */
/* Assumes:    For speed, the parameter is not   */
/*             limit checked, so if it falls     */
/*             outside the allowed range the     */
/*             returned value will be undefined  */
/*             and probably very inappropriate   */
/*             for a font size!                  */
/*************************************************/

int fm_size(int size)
{
  return fm_sizes[size];
}

/*************************************************/
/* fm_find_font()                                */
/*                                               */
/* Returns the font handle of a requested        */
/* typeface variant, optionally recording the    */
/* usage in a given browser_data structure.      */
/*                                               */
/* Parameters: Pointer to the browser_data       */
/*             struct relevant to the font;      */
/*                                               */
/*             Pointer to the typeface name;     */
/*                                               */
/*             X and Y sizes in 16ths of a pt;   */
/*                                               */
/*             1 for italics, 0 for not italic;  */
/*                                               */
/*             1 for bold, 0 for not bold.       */
/*                                               */
/* Returns:    A RISC OS font handle for the     */
/*             requested font or the closest     */
/*             available match to it; this may   */
/*             be the system font if no good     */
/*             match could be found.             */
/*                                               */
/* Assumes:    The browser_data struct pointer   */
/*             may be NULL.                      */
/*************************************************/

fm_face fm_find_font(browser_data * b, char * name, int xsize, int ysize, int italic, int bold)
{
  fm_typeface * f;
  fm_face       s,h;
  int           i;

  /* Work out the system font 'handle' for the given point size */

  s = FM_System_Handle(xsize,ysize); /* (Macro defined in FontManage.h) */

  /* If system font has been configured locally through the */
  /* fm_set_system_font function, return that handle now    */

  if (fm_systemfont) return s;

  /* The slower alternative - try to find the named typeface */

  f = fm_find_typeface(name);

  /* If it can't be found, return the system font */

  if (!f) return s;

  /* Get the RISC OS font handle for that typeface */

  h = fm_sub_find_font(f, xsize, ysize, italic, bold);

  /* If fm_sub_find_font returns -1, keep trying   */
  /* alternative typefaces as long as there are    */
  /* some defined, to a maximum of 4 alternatives. */

  for (
        i = 0;
        i < 3 && h < 0 && f->alternative[0];
        i++
      )
  {
    f = fm_find_typeface(f->alternative);
    if (!f) return s;
    h = fm_sub_find_font(f, xsize, ysize, italic, bold);
  }

  /* If we still haven't got a RISC OS font handle */
  /* return the system font handle.                */

  if (h < 0) return s;

  /* Success, eventually! */

  /* If the font has already been claimed (it has an entry in fm_handles) */
  /* don't want to keep the second claim caused by the Font_FindFont that */
  /* gave the handle. So, lose the font - we know that the Font Manager   */
  /* will still have a usage of at least 1 for the font as long as        */
  /* fm_handles has a non-zero usage count itself. This is somewhat       */
  /* inefficient, but otherwise, for every claimed font handle, the face  */
  /* name, style, and size would have to be recorded with it, and for     */
  /* every font claim these preclaimed fonts would need to checked        */
  /* through. This would end up being quite slow.                         */

  if (b && fm_handles[h]) _swix(Font_LoseFont, _IN(0), h);

  /* Record the usage of this font */

  if (b && !(Bitfield_Is_Set(&b->handles, h)))
  {
    fm_handles[h]++;
    Bitfield_Set_Bit(&b->handles, h);
  }

  return h;
}

/*************************************************/
/* fm_lose_font()                                */
/*                                               */
/* Forgets about the usage of a given font by a  */
/* given browser, and if there are no users at   */
/* this point, tell the RISC OS Font Manager     */
/* that the font is no longer in use.            */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the font;             */
/*                                               */
/*             The RISC OS font handle for the   */
/*             font to forget.                   */
/*                                               */
/* Assumes:    The pointer to the browser_data   */
/*             struct *may* be NULL, in which    */
/*             case the RISC OS Font Manager is  */
/*             told the font isn't needed        */
/*             straight away.                    */
/*************************************************/

void fm_lose_font(browser_data * b, fm_face h)
{
  if (h >= 0)
  {
    if (b)
    {
      if (Bitfield_Is_Set(&b->handles, h))
      {
        fm_handles[h]--;

        Bitfield_Clear_Bit(&b->handles, h);
      }

      if (!fm_handles[h]) _swix(Font_LoseFont,_IN(0),h);
      else if (fm_handles[h] < 0) fm_handles[h] = 0;
    }
    else _swix(Font_LoseFont,_IN(0),h);
  }
}

/*************************************************/
/* fm_lose_fonts()                               */
/*                                               */
/* Forgets about the usage of all fonts used by  */
/* a given browser, and if there are no longer   */
/* any users of any of the fonts, tells the      */
/* RISC OS Font Manager that those fonts are no  */
/* longer in use.                                */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the fonts.            */
/*************************************************/

void fm_lose_fonts(browser_data * b)
{
  int fh;

  for (fh = 0; fh < Limits_OS_FontHandles; fh++)
  {
    if (Bitfield_Is_Set(&b->handles, fh)) fm_lose_font(b, fh);
  }
}

/*************************************************/
/* fm_rescale_fonts()                            */
/*                                               */
/* For all currently claimed fonts, reclaim them */
/* with the current screen DPI. Useful for (say) */
/* mode changes, when the Desktop scaling may    */
/* have changed.                                 */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the fonts to rescale. */
/*************************************************/

_kernel_oserror * fm_rescale_fonts(browser_data * b)
{
  _kernel_oserror * e;
  browser_data      temp;
  int               fh, size, xsize, ysize;
  char              name[Limits_FontName];

  /* Can't lose and reclaim fonts within the same browser, */
  /* whilst scanning through its list of claimed fonts...  */
  /* if ever a font handle higher than the one just lost   */
  /* was returned, things would go very wrong. So copy     */
  /* the list of used fonts into a temporary store and use */
  /* this for the loses and claims, then copy that back to */
  /* the actual browser_data structure when finished.      */

  temp.handles = b->handles;

  /* Now loop round all the font handles */

  for (fh = 0; fh < Limits_OS_FontHandles; fh++)
  {
    /* If this font is claimed... */

    if (Bitfield_Is_Set(&b->handles, fh))
    {
      /* How much space does the font name need? */

      e = _swix(Font_ReadDefn,
                _INR(0,1) | _IN(3) | _OUT(2),

                fh,              /* Handle */
                0,               /* Return buffer size needed for name */
                Magic_Word_FULL, /* See MiscDefs.h */

                &size);

      if (e) return e;

      /* If the font name will fit, then get the name and size details */

      if (size < sizeof(name)) /* Not '<=' as need terminator */
      {
        int h;

        /* Fill the name buffer with zeros first, to ensure the */
        /* name string is correctly terminated.                 */

        memset(name, 0, sizeof(name));

        /* Now read the font information */

        e = _swix(Font_ReadDefn,
                  _INR(0,1) | _IN(3) | _OUTR(2,3),

                  fh,
                  name,
                  Magic_Word_FULL, /* See MiscDefs.h */

                  &xsize,
                  &ysize);

        if (e) return e;

        /* Lose the old font, and reclaim the same name and size */
        /* at a new resolution.                                  */

        fm_lose_font(&temp, fh);

        e = _swix(Font_FindFont,
                  _INR(1,5) | _OUT(0),

                  name,             /* Font name to find    */
                  xsize,            /* x size in 16ths pt   */
                  ysize,            /* y size in 16ths pt   */
                  180 / wimpt_dx(), /* Current mode's x dpi */
                  180 / wimpt_dy(), /* Current mode's y dpi */

                  &h);              /* Font handle put in h */

        if (e) return e;

        /* See fm_find_font for full comments on this bit, no need */
        /* to repeat it all here.                                  */

        if (fm_handles[h]) _swix(Font_LoseFont, _IN(0), h);

        /* Record the usage of this font */

        fm_handles[h]++;
        Bitfield_Set_Bit(&temp.handles, h);

      /* Closure of long 'if' to see if the font name would fit */
      /* in the allocated buffer. The code above executes if it */
      /* would.                                                 */
      }

    /* Closure of long 'if' to see if the current font handle */
    /* was claimed; the code above executes if it was.        */
    }

  /* Closure of 'for' loop going found all font handles. */
  }

  /* Copy the font usage information back to the actual browser. */

  b->handles = temp.handles;

  return NULL;
}

/*************************************************/
/* fm_font_box()                                 */
/*                                               */
/* Gets a bounding box large enough to cover any */
/* character in a given font, in OS units.       */
/*                                               */
/* Parameters: RISC OS font handle of the font;  */
/*                                               */
/*             Pointer to a BBox into which the  */
/*             bounding box is placed (min X and */
/*             Y are inclusive, max X and Y are  */
/*             exclusive).                       */
/*************************************************/

_kernel_oserror * fm_font_box(fm_face h, BBox * box)
{
  if (h < 0)
  {
    /* If we're passed a system font handle, */
    /* treat that as a special case.         */

    int xs,ys;

    xs = FM_System_Size_X(h); /* (Macros defined in FontManage.h) */
    ys = FM_System_Size_Y(h);

    /* The code is derived from this:        */
    /*                                       */
    /* box->xmin=0;                          */
    /* box->ymin=ys * (10 * -3) / (64 * 16); */
    /*                                       */
    /* box->xmax=xs * 10 / 128;              */
    /* box->ymax=ys * (10 * 13) / (64 * 16); */

    box->xmin = 0;
    box->ymin = (ys * -30) / (1024);

    box->xmax = (xs * 10) / 128;
    box->ymax = (ys * 130) / (1024);
  }
  else
  {
    /* Otherwise, ask the Font Manager for the bounding box */

    // this probably needs to a char by char scan of the font and cache the result

    return _swix(Font_ReadInfo,
                 _IN(0) | _OUTR(1,4),

                 h,            /* Handle                    */

                 &box->xmin,   /* Minimum X of bounding box */
                 &box->ymin,   /* Minimum Y of bounding box */
                 &box->xmax,   /* Maximum X of bounding box */
                 &box->ymax);  /* Maximum Y of bounding box */
  }

  return NULL;
}

/*************************************************/
/* fm_char_box()                                 */
/*                                               */
/* Gets the bounding box of a char, in OS units. */
/*                                               */
/* Parameters: RISC OS font handle of the font;  */
/*                                               */
/*             Character to measure;             */
/*                                               */
/*             Pointer to a BBox into which the  */
/*             bounding box is placed (min X and */
/*             Y are inclusive, max X and Y are  */
/*             exclusive).                       */
/*************************************************/

_kernel_oserror * fm_char_box(fm_face h, char c, BBox * box)
{
  /* Very similar structure to fm_font_box, so see that for */
  /* more comments.                                         */

  if (h < 0)
  {
    int xs,ys;

    xs = FM_System_Size_X(h);
    ys = FM_System_Size_Y(h);

    box->xmin = 0;
    box->ymin = (ys * -30) / (1024);

    box->xmax = (xs * 10) / 128;
    box->ymax = (ys * 130) / (1024);
  }
  else
  {
    #ifdef UNIFONT
      _swix(Font_WideFormat, _INR(0,1), h, 12);
    #endif

    return _swix(Font_CharBBox,
                 _INR(0,2) | _OUTR(1,4),

                 h,            /* Handle                    */
                 c,            /* Character                 */
                 FM_OSUnits,   /* Flags -> OS units, please */

                 &box->xmin,   /* Minimum X of bounding box */
                 &box->ymin,   /* Minimum Y of bounding box */
                 &box->xmax,   /* Maximum X of bounding box */
                 &box->ymax);  /* Maximum Y of bounding box */
  }

  return NULL;
}

/*************************************************/
/* fm_set_font_colour()                          */
/*                                               */
/* Sets the font colour for subsequent painting. */
/*                                               */
/* Parameters: The RISC OS font handle of the    */
/*             font in question;                 */
/*                                               */
/*             The foreground colour;            */
/*                                               */
/*             The background colour.            */
/*                                               */
/*             Colours are As palette entries,   */
/*             i.e. are in the form BBGGRRcc     */
/*             where cc = GCOL, else use the Red */
/*             Green and Blue bytes.             */
/*************************************************/

_kernel_oserror * fm_set_font_colour(fm_face h, int fore, int back)
{
  /* First, the system font case */

  if (h < 0)
  {
    #ifdef UNIFONT

      /* Need to set the background colour too */

      _swix(ColourTrans_SetGCOL,

                            _IN(0) | _INR(3,4),

                            fore,    /* Palette entry */
                            0x80,    /* Flags         */
                            0);      /* GCOL action   */

    #endif

    return _swix(ColourTrans_SetGCOL,
                          _IN(0) | _INR(3,4),

                          fore,    /* Palette entry */
                          0,       /* Flags         */
                          0);      /* GCOL action   */
  }
  else
  {
    /* Alternatively, deal with an outline font */

    return _swix(ColourTrans_SetFontColours,
                 _INR(0,3),

                 h,       /* Handle                            */
                 back,    /* Background colour palette entry   */
                 fore,    /* Foreground colour palette entry   */
                 14);     /* Maximum foreground colour offset  */
  }
}

/*************************************************/
/* fm_puts()                                     */
/*                                               */
/* Outputs a string in a given font at specified */
/* coordinates in a colour determined by calls   */
/* to fm_set_font_colour() prior to calling this */
/* function.                                     */
/*                                               */
/* Parameters: A RISC OS font handle;            */
/*                                               */
/*             X and Y coordinates of the bottom */
/*             left of the bounding box of the   */
/*             plotted string. These may be in   */
/*             OS units or millipoints (see      */
/*             below);                           */
/*                                               */
/*             Pointer to the string to output;  */
/*                                               */
/*             1 if using OS coords, else 0;     */
/*                                               */
/*             1 to use font blending, else 0 -  */
/*             do NOT use font blending on a     */
/*             Font Manager earlier than v3.37!  */
/*************************************************/

_kernel_oserror * fm_puts(fm_face h, int x, int y, const char * s, int os, int blend)
{
  #ifdef TRACE
    if (tl & (1u<<10)) Printf("\nfm_puts: Called with handle %p\n         String '%s'\n",(void *) h, s);
  #endif

  if (h < 0)
  {
    /* System font output */

    int xs,ys;

    if (!os) convert_pair_to_os(x, y, &x, &y);

    /* Work out the pixel size of the font; note that the */
    /* Y size is twice the X size for aesthetic reasons   */

    xs = FM_System_Size_X(h) * 10 / (128 * wimpt_dx());
    ys = FM_System_Size_Y(h) * 10 / (64  * wimpt_dy());

    /* Set the size and spacing */

    bbc_vduq(23,
             17,
             7,
             6,
             xs & 255,
             xs >> 8,
             ys & 255,
             ys >> 8,
             0,
             0);

    /* Move to the requested (converted) coordinates. 10/64 */
    /* converts to OS units, 13/16 is height above base     */

    bbc_move(x, y + (FM_System_Size_Y(h) * (10*13) / (64*16)) - 1);

    /* Output the string */

    #ifdef UNIFONT
      unifont_write0(s, x, y + (FM_System_Size_Y(h) * (10*13) / (64*16)) - 1, xs, ys);
    #else
      _swix(OS_Write0, _IN(0), s);
    #endif

    /* Set the text output size back (certainly get interesting */
    /* effects from Make if you don't do this...!)              */

    bbc_vduq(23,
             17,
             7,
             6,
             16 / wimpt_dx(),
             0,
             32 / wimpt_dy(),
             0,
             0,
             0);

    return NULL;
  }
  else
  {
    unsigned int flags;

    /* Outline font output */

    if (fm_allowblend < 0)
    {
      /* If we haven't already, check the Font Manager */
      /* version - for 3.37 or later, allow background */
      /* blending (fm_allowblend will set bit 11 of    */
      /* the flags for Font_Paint), else don't allow   */
      /* blending (earlier versions of the Font        */
      /* Manager will object strongly to bit 11 being  */
      /* set).                                         */

      int version;

      if (_swix(Font_CacheAddr,_OUT(0),&version)) version = 0;

      fm_allowblend = (version >= 337) ? FM_Blend : 0;
    }

    /* Paint the string. The parameters are a pointer to the string, */
    /* flags, and x and y coordinates. The flags have the blending   */
    /* set if asked for, specify absolute coordaintes, and tell the  */
    /* Font Manager whether x and y are OS units or millipoints.     */

    #ifdef TRACE
      if (tl & (1u<<10))
      {
        unsigned char * scan = (unsigned char *) s;

        while (*scan)
        {
          if (*scan < 32 && *scan != 10 && *scan != 13) Printf("fm_puts: Warning, non-CR/LF control char %d in string\n",(unsigned int) *scan);
          scan ++;
        }

        Printf("fm_puts: Calling Font_Paint prior to exiting\n");
      }
    #endif

    if (printing) flags = 0;
    else          flags = blend ? fm_allowblend : 0;

    flags |= (
               (FM_GivenHandle) |
               (FM_Kern)        |

               (os ? FM_OSUnits : 0)
             );

    #ifdef UNIFONT
      _swix(Font_WideFormat, _INR(0,1), h, 12);
    #endif

    return _swix(Font_Paint,
                 _INR(0,7),

                 h,
                 s,
                 flags,
                 x,
                 y,
                 NULL,NULL,0);
  }
}

/*************************************************/
/* fm_putsl()                                    */
/*                                               */
/* As fm_puts above, but takes an extra          */
/* parameter after the string pointer which says */
/* how many characters of the string should be   */
/* plotted.                                      */
/*                                               */
/* This will *write a character into the string* */
/* during operation, so the string must be owned */
/* by the caller and not in ROM.                 */
/*                                               */
/* Parameters: As fm_puts above, but after the   */
/*             pointer to the string to output,  */
/*             pass the number of characters in  */
/*             the string that will be plotted.  */
/*************************************************/

_kernel_oserror * fm_putsl(fm_face handle, int x, int y, char * s, int chars, int os, int blend)
{
  char              c;
  _kernel_oserror * e = NULL;

  /* If chars doesn't point past the end of the */
  /* string, then forcibly terminate it and     */
  /* call fm_puts. Restore the character that   */
  /* the terminator wrote over whther or not    */
  /* there is an error.                         */

  if (s[chars] != '\0')
  {
    c        = s[chars];
    s[chars] = '\0';

    e = fm_puts(handle,x,y,s,os,blend);

    s[chars] = c;
  }

  /* If the number of chars is longer than the */
  /* string, plot as much as there is of it.   */

  else e = fm_puts(handle,x,y,s,os,blend);

  return e;
}

/*************************************************/
/* fm_write_descriptor()                         */
/*                                               */
/* Writes the descriptor string for a typeface   */
/* into a given buffer.                          */
/*                                               */
/* Parameters: Pointer to the typeface name;     */
/*                                               */
/*             Pointer to the buffer.            */
/*                                               */
/* Assumes:    That the buffer is big enough.    */
/*************************************************/

_kernel_oserror * fm_write_descriptor(char * name, char * buffer)
{
  fm_typeface * f;

  /* Try to find the typeface */

  f = fm_find_typeface(name);

  /* If the typeface isn't found, give an error */

  if (!f)
  {
    erb.errnum = Utils_Error_Custom_Fatal;

    StrNCpy0(erb.errmess,
             lookup_token("BadTRef:Internal error - Reference to undefined typeface in %0.",
                          0,
                          "fm_write_descriptor"));
    return &erb;
  }

  /* Else fill in the buffer */

  sprintf(buffer,
          "%s=%s:%s:%s:%s;%s",
          f->name,
          f->fontnames[0],
          f->fontnames[1],
          f->fontnames[2],
          f->fontnames[3],
          f->alternative);

  return NULL;
}

/*************************************************/
/* fm_write_name()                               */
/*                                               */
/* Writes the RISC OS font name for a given      */
/* typeface variant to a given buffer            */
/*                                               */
/* Parameters: Typeface number from the fm_faces */
/*             array;                            */
/*                                               */
/*             Pointer to the buffer;            */
/*                                               */
/*             1 for italics, else 0;            */
/*                                               */
/*             1 for bold, else 0.               */
/*                                               */
/* Assumes:    That the buffer is big enough.    */
/*************************************************/

_kernel_oserror * fm_write_name(int number, char * buffer, int italic, int bold)
{
  fm_typeface * f;
  int           n;
  char        * name;

  if (number < 0 || number >= sizeof(fm_faces))
  {
    #ifdef TRACE

      erb.errnum = Utils_Error_Custom_Normal;

      sprintf(erb.errmess,
              "Illegal typeface number %d in fm_write_name.",
              number);

      return &erb;

    #endif

    return NULL;
  }
  else name = fm_faces[number];

  /* Index the correct font name using the bold and italic flags */

  n = (italic ? 1 : 0) | (bold ? 2 : 0);

  /* Try to find the typeface */

  f = fm_find_typeface(name);

  /* If the typeface isn't found, give an error */

  if (!f)
  {
    erb.errnum = Utils_Error_Custom_Fatal;

    StrNCpy0(erb.errmess,
             lookup_token("BadTRef:Internal error - Reference to undefined typeface in %0.",
                          0,
                          "fm_write_name"));
    return &erb;
  }

  /* Else fill in the buffer */

  sprintf(buffer,"%s",f->fontnames[n]);

  return NULL;
}

/*************************************************/
/* fm_read_typeface()                            */
/*                                               */
/* Given a typeface name inside an fm_typeface   */
/* structure, the rest of the typeface details   */
/* are filled in. If the typeface name isn't     */
/* found an error will be raised.                */
/*                                               */
/* Parameters: A pointer to the fm_typeface with */
/*             the name to look up inside its    */
/*             'name' field.                     */
/*************************************************/

_kernel_oserror * fm_read_typeface(fm_typeface * face)
{
  fm_typeface * f;

  /* Try to find the named typeface */

  f = fm_find_typeface(face->name);

  /* Raise an error if it can't be found */

  if (!f)
  {
    erb.errnum = Utils_Error_Custom_Fatal;

    StrNCpy0(erb.errmess,
             lookup_token("BadTRef:Internal error - Reference to undefined typeface in %0.",
                          0,
                          "fm_read_typeface"));
    return &erb;
  }

  /* Copy the found typeface contents across to the supplied typeface */

  *face = *f;

  return NULL;
}

/*************************************************/
/* fm_destroy_typefaces()                        */
/*                                               */
/* Destroys *all* typeface records, therefore    */
/* emptying the list of typefaces.               */
/*************************************************/

void fm_destroy_typefaces(void)
{
  fm_facelist * f, * c;

  /* Point f to the head of the list */

  f = fm_list;

  /* As long as f is non-null, there is a typeface. */
  /* Remember the typeface address and point f to   */
  /* the next item in the list; then free the       */
  /* typeface structure memory.                     */

  while(f)
  {
    c = f;
    f = f->next;

    #ifdef TRACE
      if (tl & (1u<<12)) Printf("fm_destroy_typefaces: free block %p, which held 'fm_facelist' structure\n",c);
      malloccount -= sizeof(fm_facelist);
      if (tl & (1u<<13)) Printf("** malloccount (fm_destroy_typefaces): \0212%d\0217\n",malloccount);
    #endif

    free(c);
  }

  /* Ensure that the list is flagged as empty. */

  fm_list=NULL;
}

/*************************************************/
/* fm_set_system_font()                          */
/*                                               */
/* Sets the static local flag to say that system */
/* fonts only should be used for font handling.  */
/* This will only affect any fonts claimed with  */
/* e.g. fm_find_font after this function has     */
/* been called.                                  */
/*                                               */
/* Parameters: 1 to use system font, 0 to allow  */
/*             outline fonts.                    */
/*************************************************/

void fm_set_system_font(int systemfont)
{
  fm_systemfont = systemfont;
}

/*************************************************/
/* fm_system_font()                              */
/*                                               */
/* Returns 1 if the font library is currently    */
/* using system font for any new font finds, or  */
/* 0 if outline fonts are allowed. This is       */
/* intended for external callers only (it is     */
/* faster to use the fm_systemfont variable      */
/* directly in internal routines).               */
/*************************************************/

int fm_system_font(void)
{
  return fm_systemfont;
}

/*************************************************/
/* fm_init()                                     */
/*                                               */
/* Initialises the library for use.              */
/*                                               */
/* Parameters: 1 if the library is to only use   */
/*             the system font from the outset,  */
/*             or 0 to allow outline fonts;      */
/*                                               */
/*             The 'base font' size, if going    */
/*             to use the fm_size function to    */
/*             allow an abstracted font size     */
/*             selection method (see fm_size).   */
/*************************************************/

void fm_init(int systemfont, int base_size)
{
  fm_destroy_typefaces();
  fm_set_system_font(systemfont);
  fm_init_sizes(base_size);
}

/*************************************************/
/* fm_init_sizes()                               */
/*                                               */
/* Sets up the size array for FONT SIZE=xxx and  */
/* related attributes, based on the given base   */
/* font size.                                    */
/*                                               */
/* Parameters: The 'base font' size.             */
/*************************************************/

void fm_init_sizes(int base_size)
{
  int size, scaleby;

  /* Set up the size array */

  fm_sizes[0] = base_size; /* Just in case 0 gets given to fm_size by accident - more likely than a large out of range value */

  for (size = 1; size <= 7; size ++)
  {
    scaleby        = size - 3;
    fm_sizes[size] = (int) (base_size * pow(1.2, scaleby));
  }
}

/*************************************************/
/* fm_shutdown()                                 */
/*                                               */
/* Tells the RISC OS Font Manager that all fonts */
/* currently marked as used in fm_handles are    */
/* not needed regardless of the internal usage   */
/* counter therein, and resets those counters to */
/* zero.                                         */
/*************************************************/

void fm_shutdown(void)
{
  int fh;

  for (fh = 0; fh < Limits_OS_FontHandles; fh++)
  {
    if (fm_handles[fh])
    {
      _swix(Font_LoseFont, _IN(0), fh);
      fm_handles[fh] = 0;
    }
  }
}

/*************************************************/
/* fm_get_string_width()                         */
/*                                               */
/* Given a string, and the number of chars to    */
/* consider in that string, work out the width   */
/* of the string. A maximum width can be given,  */
/* and if it is possible to split the string     */
/* (pass the character to split on), the width   */
/* returned will reflect this consideration (but */
/* can still be greater than maxwid, if no split */
/* characters were found for far enough). The    */
/* number of bytes used to work out the final    */
/* returned width is itself returned, though any */
/* trailing spaces are included in this value    */
/* even if they aren't used to get the width.    */
/*                                               */
/* Parameters: The RISC OS font handle to use in */
/*             width calculations;               */
/*                                               */
/*             Pointer to the string to use;     */
/*                                               */
/*             Maximum width to allow if         */
/*             splitting the line;               */
/*                                               */
/*             The maximum number of bytes of    */
/*             string to consider;               */
/*                                               */
/*             The character to split at (such   */
/*             as a space), or -1 for 'don't     */
/*             split';                           */
/*                                               */
/*             Pointer to an int, into which the */
/*             number of characters used to get  */
/*             the returned width is placed. Any */
/*             trailing spaces are skipped past  */
/*             and included in this returned     */
/*             value;                            */
/*                                               */
/*             Pointer to an int, into which the */
/*             calculated width (in millipoints) */
/*             is placed.                        */
/*                                               */
/* Assumes:    The two int pointers may be NULL. */
/*************************************************/

_kernel_oserror * fm_get_string_width(fm_face h, const char * s, int maxwid, int maxbytes,
                                      int split, int * bytes, int * width)
{
  int retwidth = 0;
  int retbytes = 0;

  /* Now have to do some real work. */

  if (h >= 0)
  {
    /* The outline font case */

    _kernel_oserror * e = NULL;
    int               buffer[9];
    int               reached;

    /* For Font_ScanString, negative values of maxwid will  */
    /* produce a positive return as it copes with RTL text. */
    /* We don't want this, so skip the call if maxwid <= 0. */

    #ifdef UNIFONT
      _swix(Font_WideFormat, _INR(0,1), h, 12);
    #endif

    if (maxwid > 0)
    {
      buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0;
      buffer[4] = split;

      e = _swix(Font_ScanString,
                _INR(0,5) | _IN(7) | _OUT(1) | _OUT(3),

                h,
                s,
                FM_GivenLength | FM_GivenBuffer | FM_Kern | FM_GivenHandle,
                maxwid,
                0x1000000,
                buffer,
                maxbytes,

                &reached,
                &retwidth);

      if (!e) retbytes = reached - ((int) s);

//      #ifdef TRACE
//        {
//          int wi, by;
//
//          e = _swix(Font_SetFont,_IN(0),h);
//
//          if (!e) e = _swix(Font_StringWidth,
//                            _INR(1,5) | _OUT(2) | _OUT(5),
//
//                            s,         /* String to find width of                   */
//                            maxwid,    /* Maximum width if splitting is allowed     */
//                            0x1000000, /* Maximum height if splitting is allowed    */
//                            split,     /* Character to split on, or -1 for no split */
//                            maxbytes,  /* Maximum string length                     */
//
//                            &wi,       /* Width of scanned string                   */
//                            &by);      /* Length of scanned string                  */
//
//          if (abs(wi - retwidth) > 2000 || by != retbytes)
//          {
//            Printf("fm_get_string_width: Width/Bytes mismatch in first call...\n");
//            Printf("                     Font_ScanString  -> %d, %d\n", retwidth, retbytes);
//            Printf("                     Font_StringWidth -> %d, %d\n", wi, by);
//          }
//        }
//      #endif
    }

    /* If there was no error, a split character was specified */
    /* but the width is zero, the Font Manager did not find a */
    /* split char before the maximum width given to it was    */
    /* exceeded. We want to be able to spill over in this     */
    /* case, so recall the Font_StringWidth in a non-         */
    /* splitting mode, but only look as far as the first      */
    /* split char (if any) to allow the string to still split */
    /* up. This keeps the width as small as possible, even    */
    /* though individual words are wider than the space we    */
    /* had hoped to fit them into.                            */

    if (!e && !retwidth && split > 0)
    {
      const char * p = s;

      /* Scan only as far as the first split character.         */
      /*                                                        */
      /* Thanks to Tony Cheal for spotting this when doing      */
      /* tables stuff - the original Customer browser sources  */
      /* recognised 'some problem' with Font_StringWidth but    */
      /* didn't then allow any sort of splitting in this part   */
      /* of the code.                                           */

      while (*p && *(p++) != split);

      /* Now call the Font Manager without asking for a split, knowing that */
      /* the string length only runs up to the split character.             */

      _swix(Font_ScanString,
            _INR(0,4) | _IN(7) | _OUT(3),

            h,
            s,
            FM_GivenLength | FM_Kern | FM_GivenHandle,
            0x1000000,
            0x1000000,
            p - s,

            &retwidth);

      retbytes = p - s;

//      #ifdef TRACE
//        {
//          int wi, by;
//
//          _swix(Font_StringWidth,
//                _INR(1,5) | _OUT(2) | _OUT(5),
//
//                s,            /* String to find width of                   */
//                0x1000000,    /* Maximum width if splitting is allowed     */
//                0x1000000,    /* Maximum height if splitting is allowed    */
//                -1,           /* Character to split on, or -1 for no split */
//                p - s,        /* String length                             */
//
//                &wi,          /* Width of scanned string                   */
//                &by);         /* Length of scanned string                  */
//
//          if (abs(wi != retwidth) > 2000 || by != retbytes)
//          {
//            Printf("fm_get_string_width: Width/Bytes mismatch in second call...\n");
//            Printf("                     Font_ScanString  -> %d, %d\n", retwidth, retbytes);
//            Printf("                     Font_StringWidth -> %d, %d\n", wi, by);
//          }
//        }
//      #endif
    }

    /* Exit if there was some error */

    if (e) return e;

    /* Include any trailing spaces in the byte count */

    while (retbytes < maxbytes && s[retbytes] == ' ') retbytes++;
  }
  else
  {
    /* The system font case. */

    int wCurr, wLastSplit, oLastSplit, oCurr;

    /* (Macro defined in FontManage.h). Important that we take */
    /* account of rounding correctly - compare with formula in */
    /* fm_puts().                                              */

    int wChar = (FM_System_Size_X(h) * 10 / (128 * wimpt_dx())) * wimpt_dx() * 400;

    wCurr = wLastSplit = oLastSplit = oCurr = 0;

    /* Whilst the byte counter is less than maxbytes and the width */
    /* calculated so far is less than maxwidth, or we haven't      */
    /* found a split character yet, go through adding              */
    /* the width of a character to the width total for each char   */
    /* of the string. If a split character is met during this, set */
    /* oLastSplit and wLastSplit to the byte counter and thus far  */
    /* calculated width at that point.                             */

    while (
            oCurr < maxbytes &&
            (
              (wCurr <= (unsigned) maxwid) ||
              (split > 0) && !oLastSplit
            )
          )
    {
      #ifdef UNIFONT
        UCS4 ucs;
      #endif

      if ((split > 0) && (s[oCurr] == split))
      {
        oLastSplit = oCurr;
        wLastSplit = wCurr;
      }

      #ifdef UNIFONT
        oCurr += UTF8_to_UCS4(s + oCurr, &ucs);
        wCurr += unifont_widechar(ucs) ? wChar * 2 : wChar;
      #else
        wCurr += wChar;
        oCurr ++;
      #endif
    }

    /* If the current width is greater than the maximum width and there is */
    /* a split point, go back to that last split point.                    */

    if ((wCurr > (unsigned) maxwid) && oLastSplit)
    {
      oCurr = oLastSplit;
      wCurr = wLastSplit;
    }

    /* Include any trailing spaces in the byte count */

    while ((oCurr < maxbytes) && (s[oCurr] == ' ')) oCurr++;

    retbytes = oCurr;
    retwidth = wCurr;
  }

  if (bytes) *bytes = retbytes;
  if (width) *width = retwidth;

  return NULL;
}

/*************************************************/
/* fm_token_font_info()                          */
/*                                               */
/* Looks at a token and returns information on   */
/* the best font to use with it, e.g. bold,      */
/* italic, a typeface number, etc.               */
/*                                               */
/* The returned typeface number is based on the  */
/* array of typeface names defined at the top of */
/* this file (called redraw_faces).              */
/*                                               */
/* Parameters: Pointer to the HStream struct     */
/*             (token) of interest;              */
/*                                               */
/*             Address of an int into which the  */
/*             typeface number is placed;        */
/*                                               */
/*             Address of an int into which the  */
/*             font size to use is returned (in  */
/*             16ths of a point);                */
/*                                               */
/*             Address of an int into which 1 is */
/*             placed if italics should be used  */
/*             (else 0 is returned);             */
/*                                               */
/*             Address of an int into which 1 is */
/*             placed if bold should be used     */
/*             (else 0 is returned).             */
/*************************************************/

void fm_token_font_info(HStream * t, int * facenum, int * size, int * italic, int * bold)
{
  int flags;

  flags = t->style;

  *facenum = 0; /* 0 = Serif */

  *bold    = *italic = 0;

  /* May want to scale the size for certain tag types */

  if (ISSUP(t) || ISSUB(t)) *size = (choices.font_size * 3) / 5;
  else                      *size = choices.font_size;

  /* Scale the size according to the font size specifier */
  /* in the token                                        */

  #ifdef TRACE
    if (t->fontsize < 1 || t->fontsize > 7)
    {
      if (tl & (1u<<10)) Printf("fm_token_font_info: fontsize '%d' out of range\n",t->fontsize);
      t->fontsize = 3;
    }
  #endif

  *size += fm_size(t->fontsize) - choices.font_size;

  /* Set font details according to specific tags */
  /* (or any header, with H_MASK). The macros    */
  /* defined in HTMLLib:tags.h.                  */

  if (flags & (ITALIC | BLOCKQUOTE)) *italic  = 1;
  if (flags & (BOLD   | DT))         *bold    = 1;
  if (flags & (H_MASK | ADDRESS))    *facenum = 1; /* 1 = Sans serif */

  if (flags & (PRE    | TT))         *facenum = 2; /* 2 = Monospaced */

  /* Set bold, italic and size details according  */
  /* to the header level (if the token represents */
  /* header information, that is).                */

  switch (redraw_header(flags))
  {
    case 1: *bold = 1, *size *= 2;              break;
    case 2: *bold = 1, *size = (*size * 8) / 5; break;
    case 3: *bold = 1, *size = (*size * 3) / 2; break;
    case 4: *bold = 1, *size = (*size * 4) / 3; break;
    case 5: *bold = 1, *size = (*size * 8) / 7; break;
    case 6: *bold = 1;                          break;
  }
}

/*************************************************/
/* fm_find_token_font()                          */
/*                                               */
/* Returns an appropriate RISC OS font handle in */
/* which the contents of a given token should be */
/* rendered, optionally recording the usage in a */
/* given browser_data structure.                 */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the font (may be NULL */
/*             for a non-browser specific font   */
/*             claim);                           */
/*                                               */
/*             Pointer to the HStream struct     */
/*             (token) to find the font for;     */
/*                                               */
/*             1 to ignore bold and italic flags */
/*             and just find plain text, else 0. */
/*************************************************/

fm_face fm_find_token_font(browser_data * b, HStream * t, int ignore_styles)
{
  int facenum, size, bold, italic;

  /* Get the recommended font */

  fm_token_font_info(t, &facenum, &size, &italic, &bold);

  if (ignore_styles) facenum = italic = bold = 0;

  /* Return the font handle - note that the typeface name is */
  /* passed to the font managing routines by looking up the  */
  /* typeface number in the redraw_faces array defined at    */
  /* the top of this file, and that 100% aspect ratio is     */
  /* always used due to 'size' being passed in for both the  */
  /* x and y sizes of the font.                              */

  if (facenum == 2) return fm_find_font(b, fm_faces[facenum], (size * choices.tt_aspect) / 100, size, italic, bold);
  else              return fm_find_font(b, fm_faces[facenum], size,                             size, italic, bold);
}