/* Copyright 1996 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.
 */
/****************************************************************************
 * This source file was written by Acorn Computers Limited. It is part of   *
 * the "DrawFile" library for rendering RISCOS Draw files from applications *
 * in C. It may be used freely in the creation of programs for Archimedes.  *
 * It should be used with Acorn's C Compiler Release 2 or later.            *
 *                                                                          *
 * No support can be given to programmers using this code and, while we     *
 * believe that it is correct, no correspondence can be entered into        *
 * concerning behaviour or bugs.                                            *
 *                                                                          *
 * Upgrades of this code may or may not appear, and while every effort will *
 * be made to keep such upgrades upwards compatible, no guarantees can be   *
 * given.                                                                   *
 ***************************************************************************/

/* -> c.drawTextC
 *
 * DrawFile module, internal code
 * History:
 * Version 0.1: 15 Feb 89, DAHE: created
 *              13-Apr 89  IDJ   fixed draw_text_paint mix of old/new style
 *                               parameters
 *              13-May-91  ECN   turned off stack checking on selected
 *                               functions
 *
 * This file consists of code taken from the !Draw version of c.drawTextC
 * (0.61), for use in the DrawFile module (release 0.2).
 *
 * To handle errors, we do a prescan just after the file has been loaded to
 * check that all special sequences are valid. Errors are reported at this
 * stage. During rendering, errors that could not be trapped earlier are
 * passed back up as far as possible.
 *
 * Fonts: when the file is being verified, we make sure that there is at least
 * one font that can be loaded, and report and error if there is not. The
 * handle for this font is used as the default, and also for sections where the
 * font is unrecognised.
 *
 * When reading text, we must create a temporary store so that we can verify it
 * and find the number of columns. There is thus a short term overhead of
 * memory equal to the size of the text file.
 *
 * To get the underlining and vertical move state at the start of each output
 * line, some extra control sequences have to be inserted: this leads to some
 * horrible code, but I can't see how to get round it.
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define isdigit(c) (c) >= '0' && (c) <= '9'

#include "h.os"
#include "h.bbc"
#include "h.font"
#include "h.sprite"

#define scalefactor dr_scalefactor /* Name equivalence */
#include "h.DrawIntern.drawfile1"
#include "h.drawfdiag"
#include "h.DrawIntern.drawfile2"
#include "h.drawferror"

extern BOOL Draw_memoryError; /* Must be defined elsewhere! */

/*--------------------Macros, types, and globals ----------------------------*/

#define scaleup(xx)            ((int)((xx) * scalefactor))
#define scaleupX(xx)           ((int)(orgx + (xx) * scalefactor))
#define scaleupY(xx)           ((int)(orgy + (xx) * scalefactor))

/* Symbolic values for text alignment */
typedef enum {alignLeft, alignRight, alignCentre, alignDouble} draw_align;

#define draw_text_maxFonts 99

/* Font usage counter - more convenient for this to be global */
static int *draw_usedFont  /*[256]*/;

/* Symbolic codes for font manager operations */
#define draw_font_vmove     11
#define draw_font_colour    18
#define draw_true_font_colour 19
#define draw_font_comment   21
#define draw_font_underline 25
#define draw_font_setfont   26

/* Total length of underline and vmove sequences */
#define draw_insert 7

/* Structure representing the parameter state at the start of an output line */
typedef struct
{
  int        leading, paraLeading;  /* In font units */
  int        lmargin, rmargin;      /* In font units */
  font       defaultFont;
  int        under1, under2;
  int        vmove;
  draw_align align;
} draw_linedata;


/* Macro for matching terminators */
#define isTerm(c) (c == '\n' || c == '/')

/* Globals used for address and maximum length of current chunk */
static char *chunk;
static int  chunkLen;

#define draw_bigvalue 1 << 28  /* A large positive value */


/*
 Function    : draw_text_findParent
 Purpose     : find the parent of a text column
 Parameters  : pointer to text area column (as an column pointer)
 Returns     : object pointer
 Description : looks backwards in steps of the size of the header until it
               finds the parent tag.
*/

static draw_objptr draw_text_findParent(draw_textcolhdr *from)
{
    draw_objptr parent;

    while (from->tag == draw_OBJTEXTCOL) from -= 1;

    parent.textcolp = from;               /* Minor type fiddle here */
    return(parent);
}

/*
 Function    : draw_text_findEnd
 Purpose     : find end section of a text area
 Parameters  : header pointer
 Returns     : pointer to end section
 Description : skips over the columns and returns a pointer to the end object
*/

static draw_textareaend *draw_text_findEnd(draw_objptr hdrptr)
{
    draw_textcolhdr *column;

    column = &(hdrptr.textareastrp->column);
    while (column->tag == draw_OBJTEXTCOL) column++;
    return ((draw_textareaend *)column);
}

/*
 Function    : draw_text_getFontNum
 Purpose     : get a one or two digit font number
 Parameters  : pointer to string
               OUT: font number
 Returns     : pointer to next character
 Description : read either one or two characters are forms a font number
               from them. Returns -1 if the number is too high or if there
               are no digits in the string.
*/

static char *draw_text_getFontNum(char *text, int *fontNumber)
{
    int num = draw_text_maxFonts+1;  /* Forces an error if bad number */

    if (isdigit(*text))
    {
        num = *text++ - '0';

        if (isdigit(*text)) num = num * 10 + *text++ - '0';
    }

    *fontNumber = (num <= draw_text_maxFonts) ? num : -1;
    if (*text == '/') text += 1;
    return (text);
}

/*
 Function    : draw_getNum
 Purpose     : get an unsigned integer out of a string
 Parameters  : offset into string
               pointer to string
               pointer to int (NULL -> no assignment)
               flag: TRUE if negative numbers are allowed
               OUT: updated offset
 Returns     : termination code (see below)
 Description : skip leading space; read an integer, skip trailing space.
               Sets the termination code on the basis of what was read, as
               follows: OK: number read ok, terminator was \n or /
                            (output pointer is character after terminator)
                        BAD: no number could be read, or invalid terminator
                        MORE: number read ok, terminator was another digit
                              (output pointer points to digit)
               The string is accessed via offsets to avoid flex block problems.
*/

#define draw_numOK    0
#define draw_numBAD   1
#define draw_numMORE 2

static int  draw_getNum(int from, char *base, int *to, BOOL negative, int *rest)
{
    int  r;

    while (base[from] == ' ') from += 1;
    if (isdigit(base[from])) r = from;
    else if (negative && base[from] == '-') r = from+1;
    else return(draw_numBAD);

    if (to) *to = atoi(base + from);

    while (isdigit(base[r])) r += 1;
    if (r == from) return (draw_numBAD);

    while (base[r] == ' ') r += 1;
    if (isdigit(base[r]))
    {
        *rest = r;
        return (draw_numMORE);
    }
    else if (isTerm(base[r]))
    {
        *rest = r + 1;
        return (draw_numOK);
    }
    else
        return (draw_numBAD);
}

/*
 Function    : draw_text_setFont
 Purpose     : locate and record font
 Parameters  : pointer to start of font definition sequence
               pointer to fonts array
               flag: TRUE = whinge on missing font
               OUT: font handle (-1 if we failed)
 Returns     : new input pointer (character after newline)
 Description : this is used to handle the font definition sequence. The input
               has the form:
                <digit*><name><space><pointsize><newline>.
               or:
                <digit*><name><space><pointsize><space><pointwidth><newline>.

               This routine can also be called during verification, since we
               try to make sure that there is at least one valid font. Since
               this can happen before any scalefactor is set up, for a drop
               onto the icon bar, if the scalefactor is 0, we pretend it is 1.
*/

static char *draw_text_setFont(char *in, font *fonts, BOOL whinge, int *handle)
{
    int      fontNumber;
    char     *name;
    int      end;
    char     displaced;
    int      pointSize,  scaleSize;
    int      pointWidth, scaleWidth;
    os_error *err;
    font     fontHandle;

    *handle = -1;
    whinge = whinge; /* Gobstopper */

    /* Get font number */
    while (*in == ' ') in += 1;
    in = draw_text_getFontNum(in, &fontNumber);

    /* Skip leading spaces in font name */
    while (*in == ' ') in++;

    /* Find end of font name */
    for (name = in; *in != ' ' ; in++) ;
    displaced = *in;
    *in = '\0';

    /* Get point size, and maybe width */
    if (draw_getNum(1, in, &pointSize, FALSE, &end) == draw_numMORE)
        draw_getNum(end, in, &pointWidth, FALSE, &end);
    else
        pointWidth = pointSize;

    /* Find sizes at current scaling */
    scaleSize  = (int)(pointSize  * ((scalefactor==0.0)? 16: 16 *scalefactor));
    scaleWidth = (int)(pointWidth * ((scalefactor==0.0)? 16: 16 *scalefactor));

    if ((err = font_find(name, scaleWidth, scaleSize, 0, 0, &fontHandle)) == 0
        && fontHandle != 0)
    {
        /* Record font reference, handle and size */
        fonts[fontNumber] = fontHandle;
        draw_usedFont[*handle = fontHandle] += 1;
    }
    else
    {
        fonts[fontNumber] = -1;
    }

    *in = displaced;
    return (in + end);
}

/* Colour handling SWIs */
#define SWI_ColourTrans_ReturnFontColour 0x4074e
#define SWI_ColourTrans_SetFontColour    0x4074f

/*
 Function    : draw_text_setColour
 Purpose     : set colour for a given r, g, b value
 Parameters  : r, g, b intensities (as a palette entry, i.e. BBGGRRxx)
               (foreground and background)
 Returns     : void
 Description : selects the best colour for the given font and rgb values.
*/

#pragma -s1

static void  draw_text_setColour(int colour, int backcolour)
{
    os_regset r;

    r.r[0] = -1;
    r.r[1] = backcolour;
    r.r[2] = colour;
    r.r[3] = 14;

    /* Set the best approximation to the given colours */
    os_swi(SWI_ColourTrans_SetFontColour, &r);
}

/*
 Function    : draw_text_setup_colour
 Purpose     : set a gcol for a given r, g, b value
 Parameters  : r, g, b intensities (as a palette entry, i.e. BBGGRRxx)
               (foreground and background)
               offset into string to place colour sequence at
 Returns     : void
 Description : finds the best gcol value for text, and puts this into a colour
               change sequence in the string
*/

static void draw_text_setup_colour(int colour, int backcolour, int out)
{
#ifdef draw_true_font_colour
    chunk[out++] = draw_true_font_colour;
    chunk[out++] = (char)((backcolour >> 8) & 0xff);
    chunk[out++] = (char)((backcolour >> 16) & 0xff);
    chunk[out++] = (char)((backcolour >> 24) & 0xff);
    chunk[out++] = (char)((colour >> 8) & 0xff);
    chunk[out++] = (char)((colour >> 16) & 0xff);
    chunk[out++] = (char)((colour >> 24) & 0xff);
    chunk[out++] = (char)(14);
#else
    os_regset r;

    r.r[0] = -1;
    r.r[1] = backcolour;
    r.r[2] = colour;
    r.r[3] = 14;

    /* Set the best approximation to the given colours */
    os_swi(SWI_ColourTrans_ReturnFontColour, &r);

    chunk[out++] = draw_font_colour;
    chunk[out++] = (char)(r.r[1] & 0xff);
    chunk[out++] = (char)(r.r[2] & 0xff);
    chunk[out++] = (char)(r.r[3] & 0xff);
#endif
}

#pragma -s0

/* Static variables - used for the forground and background RGB values last
   seen. We need to hold these statically, because both are needed to set a
   colour, and it is not always easy to find the last values. See at the start
   of rendering from the object colours
 */

static draw_coltyp draw_text_fg, draw_text_bg;

/*
 Function    : draw_setVmove
 Purpose     : set vertical move
 Parameters  : buffer offset
               move in points
 Returns     : void
*/

static void draw_setVmove(int out, int move)
{
    chunk[out++] = draw_font_vmove;
    chunk[out++] = move & 0xff;
    chunk[out++] = (move & 0xff00) >> 8;
    chunk[out++] = (move & 0xff0000) >> 16;
}

/*
 Function    : draw_setLMPstate
 Purpose     : set state on L, M and P commands; skip hyphens
 Parameters  : offset into source string, at L, M or P
               base address of string
               state data
 Returns     : offset to rest of string
 Description : sets the state on an L, M or P command, either in the original
               string, or when processing the output. Skips hyphens, which can
               appear in output comments.
*/

static int draw_setLMPstate(int from, char *base, draw_linedata *state)
{
    int i;

    switch (base[from++])
    {
        case 'L':
            draw_getNum(from, base, &i, FALSE, &from);
            state->leading = draw_pointsToFont(i);
            break;

        case 'P':
            draw_getNum(from, base, &i, FALSE, &from);
            state->paraLeading = draw_pointsToFont(i);
            break;

        case 'M':
            draw_getNum(from, base, &i, FALSE, &from);
            state->lmargin = draw_pointsToFont(i);
            draw_getNum(from, base, &i, FALSE, &from);
            state->rmargin = draw_pointsToFont(i);
            break;

        case '-':
            from += 1;   /* Skip hyphen comment terminator */
            break;
    }

    return (from);
}

/*
 Function    : draw_text_allocate
 Purpose     : allocate memory for string
 Parameters  : pointer to string
 Returns     : TRUE if memory can be allocated
 Description : this calculates the size of a buffer large enough to hold the
               string that will be generated by get String. The size <=
               number of characters up to \<nl>, <nl><nl>, <nl>\0, \0.
               + total length of numbers in \L sequences
               + total length of numbers in \P sequences
               + total length of numbers in \M sequences
               + number of \V sequences * 2
               + number of \U sequences
               + number of \C sequences * 2
               + number of \B sequences * 2
               + number of \- sequences
               + space for terminator and initial settings

               This does not allow for the string getting broken by a \A --
               but this is hard to test for, because of the initial case.

               Memory is then allocated if the current space is not large
               enough. If not, we don't report an error, but we do return
               FALSE.
*/

static BOOL  draw_text_allocate(char *from)
{
    int  extra = 10;     /* terminator, initial colour, underline, vmove */
    char *at;
    BOOL getOut = FALSE;
    int  length;

    /* Look for \ and newline */
    at = from;
    do
    {
        at += strcspn(at, "\n\\");
        if (*at == '\\')
        {
            switch (*(++at))
            {
                case 'L': case 'M': case 'P':
                    extra += 2+strcspn(at, "\n/");
                    at += strcspn(at, "\n/") + 1;
                    break;
                case 'B': case 'C':
                    extra += 2;
                    at += strcspn(at, "\n/") + 1;
                    break;
                case 'U':
                    extra += 1;
                    if (*(++at) != '.') at += strcspn(at, "\n/");
                    at += 1;
                    break;
                case '-':
                    extra += 1;
                    at += 1;
                    break;
                case 'V':
                    extra += 2;
                    if (*(++at) == '-') at += 2; else at += 1;
                    break;
                case '\n':
                    getOut = TRUE; break;
            }
        }
        else if (*at == '\n')
        {
            getOut = (*(++at) == '\n');
        }
    } while (*at != '\0' && !getOut);

    if ((length = extra + at - from) > chunkLen)
    {   int flexCode;

        /* Allocate new memory, or extend existing memory, in 1k units */
        chunkLen = ((length / 1024) + 1) * 1024;

        if (chunk == NULL)
        {
          if (Draw_allocator)
            flexCode = Draw_allocator((void **)&chunk, chunkLen);
          else flexCode = 0;
        }
        else
        {
          if (Draw_extender)
            flexCode = Draw_extender((void **)&chunk, chunkLen);
          else flexCode = 0;
        }
        Draw_memoryError = (flexCode == 0);
    }

    return (TRUE);
}

/*
 Function    : draw_text_getString
 Purpose     : get a cleaned string
 Parameters  : pointer to input string
               pointer to font array
               IN/OUT: line state data
 Returns     : pointer to rest of string (NULL on error)
 Description : this takes the string from the current location up to the end
               of the paragraph or to a change in the align and builds a string
               that is closer to what the font manager can handle. Some further
               processing will still be necessary. Sequences that cannot
               immediately be coded up are replaced by font manager comment
               sequences. The output string is terminated by a null character.
               If the break was caused by a paragraph break, the null is
               preceded by a newline.
               The interpretation proceeds as follows:
               1. control characters are deleted, except for tab, which is
                  replaced by a space.
               2. All other characters are copied, except '\', which is
                  interpreted according to the next character, and newline
                  (see below)
               3. \\ is replaced by \
               4. \; .. <newline> is deleted (comment).
               5. \F<digit*><name><space><size><space><width><newline> defines
                  a font. The font reference is recorded in a table, and an
                  Arthur font handle generated for it. The handle is logged, so
                  we can lose it later. Both the size and width are in points.
                  The font name may have leading spaces.
               6. \<digit*> inserts a font change sequence.
               7. \L<number><newline> is replaced by a comment containing the
                  number and the newline - the rendition code must look for
                  this to determine the leading.
               8. \V[-]<digit> is replaced by a vertical move sequence, in
                  scaled units.
               9. \Ax sets the align flag. Note that if any characters appear
                  before this, the string is terminated just before it. x = L
                  (left), R (right), C (centre), D (justified both sides).
                  Other characters are ignored; the string is not terminated in
                  this case.
               10. \U<number1><space><number2><newline> starts underlining.
                   \U. (no newline) turns underlining off (so does \U with
                   <number2> = 0)
               11a.\C<numberR><space><numberG><space><numberB><newline>
                 b.\B<numberR><space><numberG><space><numberB><newline>
                   Both of these generate a colour change sequence in the
                   string.
               12. \D<number><newline> is skipped.
               13. \P<number><newline> is treated as for \L.
               14. \- is replaced a comment.
               15. \<newline> forces a line break, but not a paragraph break.
               16. \M<L><spaces><R><newline> replaced by a margin setting
                   comment.
               17. \other: (never occurs - gets weeded out in verify)
               18. newline: before any printable text, this causes a paragraph
                   space to be inserted. In the body of text, a sequence of n
                   newlines cause n-1 paragraph spaces. A single newline is
                   ignored, except that if it is not preceded or followed by
                   a space (or tab), it generates a space in the output.

               Any command may be terminated by '/'. Where the command would
               normally be terminated by a newline, it replaced the newline.

               Note that \ sequences are case sensitive.
               <digit*> is interpreted as a number consisting of either one or
               two digits, depending on whether the character following the
               first digit is numeric or not.

               When there is an error in the font, the previous font persists.
               There is guaranteed to be a least some text in a valid font, as
               a result of the verification. Any characters before the first
               font change are skipped, including newlines; however, we must
               retain all leading, etc. changes. The default font contains -1
               until we see a good font. The last font seen is returned for the
               next default font.
               If the chunk of text gets too long, we force a line break in a
               rather ugly way: this ought never to happen.

               Before the first call, the \! version string must have been
               skipped.

               Those commands which affect only the state data, if they occur
               at the start of a paragraph, are applied here, rather than
               producing anything in the output string.
*/

/* Macro for skipping optional terminator */
#define draw_text_termSkip(match, in) if (*(match) == '/') in += 1

static char *draw_text_getString(char *in, font *fonts, draw_linedata *state)
{
    int  getOut = FALSE;       /* Deep break */
    font currentFont;          /* Last font seen (default initially) */
    int  printing = FALSE;     /* TRUE when a print character seen */
    int  out;                  /* Offset into output buffer */

    currentFont = state->defaultFont;
    out = draw_insert;

    /* Allocate space for chunk */
    if (!draw_text_allocate(in)) return (NULL);

    /* Process end of string, or until we break out */
    while (*in && !getOut)
    {
        /* Length check */
        if (out >= chunkLen)
        {
           break;
        }

        if (*in == '\t')  /* tab -> space */
        {
            if (currentFont != -1) { chunk[out++] = ' '; printing = TRUE; }
            in += 1;
        }
        else if (*in == '\n')
        {
            if (currentFont == -1)  /* Skip newlines before first valid font */
            {
                in += 1;
            }
            else
            {
                /* Newline - paragraph break if either followed by another
                   newline, or if it is the first printable character */
                if (!printing || *(++in) == '\n')
                {
                    /* Paragraph termination */
                    chunk[out++] = '\n';
                    in += 1;
                    getOut = TRUE;
                }
                else
                {
                    /* Newline not preceded or followed by a space generates
                       a space */
                   if (chunk[out-1] != ' ' && *in != ' ' && *in != '\t')
                       chunk[out++] = ' ';
                }
            }
        }
        else if (*in < ' ') /* skip control character */
        {
            in += 1;
        }
        else if (*in != '\\')                    /* Ordinary character: copy */
        {
            if (currentFont != -1) { chunk[out++] = *in; printing = TRUE; }
            in += 1;
        }
        else                                        /* \ -> special sequence */
        {
            switch (*(++in))
            {
                case '\\':                              /* \\ : replace by \ */
                    if (currentFont != -1)
                    { chunk[out++] = '\\'; printing = TRUE; }
                    draw_text_termSkip(++in, in);
                    break;
                case ';':                          /* \; : delete to newline */
                    while (*in++ != '\n') ;
                    break;
                case '-':                                 /* \-: soft hyphen */
                    chunk[out++] = draw_font_comment;
                    chunk[out++] = '-';
                    chunk[out++] = '\n';
                    draw_text_termSkip(++in, in);
                    break;
                case '\n':                           /* \<nl> : split string */
                    draw_text_termSkip(++in, in);
                    getOut = TRUE;
                    break;
                case 'A':         /* \A : align sequence; maybe break string */
                {
                    /* If we have done any output, break on an align */
                    if (printing)
                    {
                        /* Step back over \A sequence */
                        in -= 1;
                        getOut = TRUE;
                    }
                    else
                    {
                        /* Get align code */
                        switch (*(++in))
                        {
                            case 'L': state->align = alignLeft;   break;
                            case 'R': state->align = alignRight;  break;
                            case 'C': state->align = alignCentre; break;
                            case 'D': state->align = alignDouble; break;
                        }
                        draw_text_termSkip(++in, in);
                    }
                    break;
                }
                case 'B':
                case 'C':                           /* \B, \C: colour change */
                {
                    int r, g, b;
                    BOOL foreground = (*in == 'C');

                    sscanf(++in, "%d %d %d", &r, &g, &b);
                    in = strpbrk(in, "\n/") + 1;

                    if (r < 0) r = 0; else if (r > 255) r = 255;
                    if (g < 0) g = 0; else if (g > 255) g = 255;
                    if (b < 0) b = 0; else if (b > 255) b = 255;

                    if (foreground)
                        draw_text_fg = b << 24 | g << 16 | r << 8;
                    else
                        draw_text_bg = b << 24 | g << 16 | r << 8;

                    draw_text_setup_colour(draw_text_fg, draw_text_bg, out);
                    out += 4;
                    break;
                }
                case 'D':
                {   int i;
                    draw_getNum(1, in, NULL, FALSE, &i);
                    in += i;
                    break;
                }
                case 'F':                     /* \F : handle font definition */
                {
                    int dummy;

                    in = draw_text_setFont(++in, fonts, FALSE, &dummy);
                    break;
                }
                case 'L': case 'M': case 'P':               /* \L, \P, \M */
                    if (printing)
                    {
                        chunk[out++] = draw_font_comment;
                        while (!isTerm(*in)) chunk[out++] = *in++;
                        chunk[out++] = '\n';
                        in += 1;
                    }
                    else
                    {
                        in += draw_setLMPstate(0, in, state);
                    }
                    break;
                case 'U':                   /* \U : start or end underlining */
                {
                    if (*(++in) == '.')
                    {
                        /* Turn underlining off */
                        chunk[out++] = draw_font_underline;
                        chunk[out++] = 0;
                        chunk[out++] = 0;
                        draw_text_termSkip(++in, in);
                    }
                    else
                    {
                        int shift, thick;

                        /* Fetch two number from string */
                        sscanf(in, "%d %d", &shift, &thick);
                        in = strpbrk(in, "\n/") + 1;

                        shift = scaleup(shift);
                        thick = scaleup(thick);

                        if (shift > 127)  shift = 127;
                        if (shift < -128) shift = -128;
                        if (shift < 0)    shift = 256 + shift;

                        if (thick < 0)   thick = 0;
                        if (thick > 255) thick = 255;

                        /* Output underline sequence */
                        chunk[out++] = draw_font_underline;
                        chunk[out++] = shift & 255;
                        chunk[out++] = thick & 255;
                    }
                    break;
                }
                case 'V':                              /* \V : vertical move */
                {
                    int sign = 1000;    /* sign and conversion factor */
                    int points;
                    char *start;

                    start = ++in;
                    if (*in == '-') {sign = -1000; in += 1;}
                    points = scaleup((*in++ - '0') * sign);
                    draw_setVmove(out, points);
                    out += 4;

                    draw_text_termSkip(in, in);
                    break;
                }
                default:
                    if (isdigit(*in))
                    {
                        int fontNumber;
                        in = draw_text_getFontNum(in, &fontNumber);

                        /* \<number> : font selection */
                        if (fonts[fontNumber] != -1)
                        {
                            if (currentFont == -1 || !printing)
                            {
                                currentFont = fonts[fontNumber];
                                if (font_setfont(currentFont) != NULL)
                                    return (NULL);
                            }
                            else
                            {   /* Font change sequence */
                                chunk[out++] = draw_font_setfont;
                                chunk[out++] = currentFont = fonts[fontNumber];
                            }
                        }
                        /* else:  Bad font: no change */
                    }
                    break;
            }
        }
    }

    chunk[out] = '\0';
    state->defaultFont = currentFont;
    return (in);
}

/*
 Function    : draw_text_getLine
 Purpose     : extract an output line of text from a chunk
 Parameters  : offset into chunk
               error block pointer
               scaled max. width
               OUT: actual width (scaled)
               OUT: character displaced from split location
 Returns     : offset to rest of chunk (-1 on error)
 Description : a split location for the line is found.

               Try to split at a space; if this is not possible, we allow
               splitting at any character. Then we backspace from the given
               position, to a soft hyphen, if any, and split there. The scan
               back stops at the start of the string or at a preceding space.

               When we use a hyphen, a hyphen character is written over the
               comment character, and a null placed over the character that
               follows it. The displaced character is then returned
               as the comment character. The code that restores the displaced
               character must make special allowance for this.
*/

static int  draw_text_getLine(offset, error, width, trueWidth, displaced)
    int      offset;
    os_error **error;
    int      width;
    int      *trueWidth;
    char     *displaced;
{
    font_string fs;
    int         next = -1;
    int         i, space, hyphen, term;

    /* Width may be too small: produce a null string */
    if (width <= 0)
    {
        *displaced = chunk[offset];
        chunk[offset] = '\0';
        return (0);
    }

    /* Find where to split string */
    fs.x     = width;
    fs.y     = draw_bigvalue;
    fs.split = 32;
    fs.term  = draw_bigvalue;
    fs.s     = chunk + offset;

    if ((*error = font_strwidth(&fs)) != NULL)
        return(-1);

    /* Check that we were able to split the string at a space */
    if (fs.term == 0)
    {
        /* Split anywhere */
        fs.x     = width;
        fs.y     = draw_bigvalue;
        fs.split = -1;
        fs.term  = draw_bigvalue;
        fs.s     = chunk + offset;
        if ((*error = font_strwidth(&fs)) != NULL)
            return(-1);
    }

    /* Find hyphenation location, unless whole word fits */
    if (chunk[term = offset + fs.term] != '\n' && chunk[term] != '\0')
    {
        for (i = space = hyphen = offset ; i < term ; )
        {
            switch (chunk[i])
            {
                case draw_font_setfont: i += 2; break;
                case draw_font_vmove: case draw_font_colour: i += 4; break;
                case draw_font_underline: i += 3; break;
                case draw_font_comment:
                   if (chunk[i+1] != '-')
                   {
                       while (chunk[i++] != '\n');
                   }
                   else
                   {
                       hyphen = i;
                       i += 2;
                   }
                   break;
               case ' ': space = i++; break;
               default : i += 1; break;
           }
        }

        if (hyphen > offset && hyphen > space)
        {
            /* Insert the hyphen, and set the special displaced character */
            chunk[hyphen] = '-';
            next = hyphen + 1;
            *displaced  = draw_font_comment;
            chunk[next] = '\0';

            /* Recalculate true width */
            fs.x     = width;
            fs.y     = draw_bigvalue;
            fs.split = -1;
            fs.term  = draw_bigvalue;
            fs.s     = chunk + offset;
            if ((*error = font_strwidth(&fs)) != NULL)  return (-1);
            term = offset + fs.term;
        }
    }

    /* Break at the termination location, if we didn't hyphenate */
    if (next == -1)
    {
        /* Backtrack over spaces */
        next = term;
        if (chunk[next] == ' ')
        {
            while (chunk[--next] == ' ') ;
            next += 1;
        }

        /* Split the string */
        *displaced  = chunk[next];
        chunk[next] = '\0';

        /* Recalculate the exact width, if spaces were found */
        if (next != term)
        {
          fs.x     = width;
          fs.y     = draw_bigvalue;
          fs.split = -1;
          fs.term  = draw_bigvalue;
          fs.s     = chunk + offset;
          if ((*error = font_strwidth(&fs)) != NULL)  return (-1);
        }
    }
    *trueWidth = fs.x;

    return (next);
}

/*
 Function    : draw_text_newColumn
 Purpose     : set parameters for a new column
 Parameters  : column pointer
               pointer to bounding box to set (font units)
               OUT: base y location
 Returns     : void
 Description : sets the parameters from the header of the given column.
*/

static void draw_text_newColumn(column, box, basey)
    draw_textcolhdr *column;
    draw_bboxtyp *box;
    int *basey;
{
    draw_bboxtyp *bbox;
    bbox = &(column->bbox);

    box->x0 = draw_drawToFont(bbox->x0);
    box->y0 = draw_drawToFont(bbox->y0);
    box->x1 = draw_drawToFont(bbox->x1);
    *basey = box->y1 = draw_drawToFont(bbox->y1);
}


/*
 Function    : draw_text_paintCheck
 Purpose     : check that text will be painted within a given region
 Parameters  : chunk offset
               y base (unscaled, font units)
               clip box (in font units, no origin shift, scaled)
               region box (in font units, unscaled)
 Returns     : symbolic code (see below)
 Description : finds the string bbox, and makes sure that it does not protrude
               beyond the limits of the given region box. If text overlaps the
               top of the box, we should not render it - however, the font and
               colour changes (etc.) must still be executed. If the text sticks
               out below the bottom of the box, either in position or as a
               result of descenders, we should move to a new column.
               Source level clipping is also carried out here; the x test for
               this is an approximation.

               If the string is empty, we force a move to the next area,
               since we certainly can't plot it in this one.

               The return codes are:
                OK - go ahead with paint
                SKIP - text is too tall to fit in box, or is clipped
                LOW - text is to low: new column needed
*/

#define draw_paint_OK   0
#define draw_paint_SKIP 1
#define draw_paint_LOW  2

static int draw_text_paintCheck(offset, y, clip, box)
    int  offset;
    int  y;
    draw_bboxtyp *clip, *box;
{
    font_info fi;

    /* Check base position is ok, and that the text is non-empty */
    if (y <= box->y0 || strlen(chunk+offset) == 0) return (draw_paint_LOW);
    y = scaleup(y);

    /* Get a bounding box for the string, and check it against boxes */
    font_stringbbox(chunk+offset, &fi);
    if (fi.miny + y < scaleup(box->y0)) return (draw_paint_LOW);

    if (fi.maxy + y > scaleup(box->y1)
        || scaleup(box->x1) < clip->x0 || scaleup(box->x0) > clip->x1
        || fi.maxy + y < clip->y0 || fi.miny + y > clip->y1)
    {
        int t;  /* Text index */

        /* Implement special sequences */
        for (t = offset ; chunk[t] != '\0' ; )
        {
            switch (chunk[t])
            {
                case draw_font_vmove: t += 4; break;
                case draw_font_colour:
                    font_setcolour(0, chunk[t+1], chunk[t+2], chunk[t+3]);
                    t += 4;
                    break;
                case draw_font_comment:
                    while (chunk[t++] != '\n') ;
                    break;
                case draw_font_underline: t += 3; break;
                case draw_font_setfont:
                    font_setfont((font)chunk[t+1]);
                    t += 2;
                    break;
                default: t += 1; break;
            }
        }

        return (draw_paint_SKIP);
    }

    return (draw_paint_OK);
}

/*
 Function    : draw_text_paint
 Purpose     : paint the current line
 Parameters  : chunk offset
               displaced character from line
               left limit, right limit
               align code
               true width
               x origin
               y location (origin + offset)
 Returns     : os error, or NULL
 Description : the text is plotted with appropriate alignment. Before this is
               done, we measure its bounding box, and it any part of it lies
               outside the given box, then nothing is painted. This is NOT the
               same as the check for a new column -- it is a test that avoids
               problems with redraw. We can be sure that it fits horizontally.
*/

static os_error *draw_text_paint(int offset,
                          char displace,
                          int x0, int x1,
                          draw_align align,
                          int trueWidth,
                          int orgx, int y)
    /*int  offset;
    char displace;
    int  x0, x1;
    draw_align align;
    int  trueWidth, orgx, y;*/
{
    os_error *err = NULL;
    int      x;
    int      plotType = font_ABS;

    /* Get plot point, allowing for alignment and scaling */
    if (align == alignDouble && displace != '\0' && displace != '\n')
        plotType |= font_JUSTIFY;

    if (align == alignRight)
        x = (int)(scalefactor * x1 - trueWidth);
    else if (align == alignCentre)
        x = (int)((scalefactor * (x1 + x0) - trueWidth)/2);
    else
    {
        if (plotType & font_JUSTIFY)
        {
            /* Do move for the alignment box */
            bbc_move((int)draw_fontToOS(scaleupX(x1)),
                     (int)draw_fontToOS(y));
        }
        x = scaleup(x0);
    }

    /* Paint the text, unless empty. */
    if (chunk[offset])
    {
        err = font_paint(chunk+offset, plotType, orgx+x, y);
    }

    return (err);
}

/*
 Function    : draw_textPatchup
 Purpose     : patchup when exiting draw_textArea
 Parameters  : void
 Returns     : void
 Description : frees the chunk if need be, and releases all the fonts
*/

static void draw_textPatchup()
{
    int  i;

    if (chunk && Draw_freer) Draw_freer((void **) &chunk);

    /* Lose all the fonts */
    for (i = 0 ; i < 256 ; i++)
    {
        while (draw_usedFont[i] > 0)
        {
            font_lose((font)i);
            draw_usedFont[i] -= 1;
        }
    }
}

/*
 Function    : draw_text_stateScan
 Purpose     : scan for state parameters
 Parameters  : pointer to chunk
               start offset in chunk
               end offset in chunk
               pointer to state data block
 Returns     : void
 Description : called after a line has been output to note the state parameters
               that will apply to the next line.
               Affects underline, vertical move, margin and line/paragraph
               spacing state.
*/

static void draw_text_stateScan(char *chunk, int start, int end, draw_linedata *state)
{
    int c;

    /* Scan for state parameters */
    for (c = start ; c < end ; )
    {
        switch (chunk[c++])
        {
            case draw_font_setfont: c += 1; break;
            case draw_font_colour:  c += 3; break;
            case draw_font_vmove:
                state->vmove += chunk[c] + chunk[c+1] <<8 + chunk[c+2] <<16;
                c += 3;
                break;
            case draw_font_underline:
                state->under1 = chunk[c];
                state->under2 = chunk[c+1];
                c += 2;
                break;
            case draw_font_comment:
                c = draw_setLMPstate(c, chunk, state);
                break;
/*          default: do nothing */
        }
    }
}

/*
 Function    : draw_textArea
 Purpose     : create a text area object
 Parameters  : object pointer
               x, y origin (bottom left)
               clipping box in scaled font units
               os error pointer
 Returns     : TRUE if ok
 Description : creates a text area as follows:
               the font tables are initialised, and some other defaults set up.
               Then we draw as much as possible in each column, and continue
               until either the end of the text is reached, or all the columns
               are full.
               Within each column, we grab a chunk, i.e. up to a newline break,
               and then progressively split it into lines, using the font
               manager. When we run out of space for a line, we move to a new
               column, resetting the bounding box and position parameters.
               Newlines at the start of a new column are not output (this
               happens without need for a special case from the way paintCheck
               works).
               When all text has been painted, we lose the fonts.
               A colour change to the given initial colour is inserted before
               the first string.

               On an error, FALSE is returned, with the os error pointer set,
               or with NULL for an internal error.

               Calculations are done throughout in 1/72000 inch units rather
               than draw units.

               Before each physical output line, the vertical move and
               underline must be set up.
*/

static BOOL draw_textArea(hdrptr, orgx, orgy, clip, error)
    draw_objptr  hdrptr;
    int          orgx, orgy;
    draw_bboxtyp *clip;
    os_error     **error;
{
    int      i, y;
    char     *text;
    os_error *err;
    BOOL     newColumn = FALSE;
    font     fonts[draw_text_maxFonts+1];
    draw_bboxtyp     box;
    draw_textareaend *endptr = draw_text_findEnd(hdrptr);
    draw_textcolhdr  *column = &(hdrptr.textareastrp->column);
    draw_linedata    state;

    /* Malloc draw_usedFont to save space in the shared library */
    if (draw_usedFont == 0) draw_usedFont = malloc(256*sizeof(int));

    /* Find start colours and save them */
    draw_text_fg = endptr->textcolour;
    draw_text_bg = endptr->backcolour;
    if (draw_text_fg == TRANSPARENT)
      draw_text_fg = draw_text_bg;

    /* Set default state data */
    state.defaultFont = -1;
    state.leading     = draw_pointsToFont(10);
    state.paraLeading = draw_pointsToFont(10);
    state.lmargin     = draw_pointsToFont(1);
    state.rmargin     = draw_pointsToFont(1);
    state.align       = alignLeft;
    state.under1      = state.under2 = 0;
    state.vmove       = 0;

    /* Set initial colour */
    draw_text_setColour(draw_text_fg, draw_text_bg);

    /* Some size constants and initial position */
    orgx = draw_drawToFont(orgx);
    orgy = draw_drawToFont(orgy);

    /* Initialise arrays and defaults */
    for (i = 0 ; i < 256 ; i++) draw_usedFont[i] = 0;
    for (i = 0 ; i <= draw_text_maxFonts  ; i++) fonts[i] = -1;

    /* Point to text and skip ID string (guaranteed present) */
    for (text = &(endptr->text[0]) ; !isTerm(*text) ; text++) ;

    /* Load parameters for first column */
    draw_text_newColumn(column++, &box, &y);

    /* While there is text to process */
    while (*text)
    {
        int  c = draw_insert;        /* Offset in chunk */

        /* Select default font, if any */
        if (state.defaultFont != -1 &&
            (err = font_setfont(state.defaultFont)) != NULL)
        {
            *error = err;
            draw_textPatchup();
            return (FALSE);
        }

        /* Set up chunk */
        if ((text = draw_text_getString(text, fonts, &state)) == NULL)
        {
            /* Error in chunk: probably too long */
            *error = NULL;
            draw_textPatchup();
            return (FALSE);
        }

        /* Ensure chunk is not empty */
        if (chunk[c] != '\0' && chunk[c] != '\n')
        {
            int  trueWidth, scaledWidth, x0, x1, next;
            char displaced;

            /* Loop over each line in the chunk */
            while (chunk[c] != '\0' && chunk[c] != '\n')
            {
                /* If necessary, move to a new column */
                if (newColumn)
                {
                    if (column->tag == draw_OBJTEXTCOL)
                    {
                        /* Find new column parameters */
                        draw_text_newColumn(column++, &box, &y);
                        newColumn = FALSE;
                    }
                    else /* No more columns */
                    {
                        draw_textPatchup();
                        return (TRUE);
                    }
                }

                /* Shift the box sides to allow for the margin */
                x0 = box.x0 + state.lmargin;
                x1 = box.x1 - state.rmargin;

                /* Insert the state data into the string (yukky) */
                c -= draw_insert;       /* Guaranteed to be enough room */
                chunk[c]   = draw_font_underline;
                chunk[c+1] = state.under1;
                chunk[c+2] = state.under2;
                draw_setVmove(c+3, state.vmove);

                /* Get line */
                scaledWidth = scaleup(x1 - x0);
                if ((next = draw_text_getLine(c, error, scaledWidth,
                                              &trueWidth, &displaced)) == -1)
                {
                    draw_textPatchup();
                    return (FALSE);
                }

                /* Set new base position */
                y -= state.leading;

                /* Plot if we are still in box (may fail with descenders) */
                switch (draw_text_paintCheck(c, y, clip, &box))
                {
                    case draw_paint_OK:
                    {
                        if (state.defaultFont != -1 &&
                            (err = draw_text_paint(c, displaced, x0, x1,
                                    state.align, trueWidth, orgx, scaleupY(y)))
                             != NULL)
                        {
                            draw_textPatchup();
                            return (FALSE);
                        }
                        /* Fall into skip case */
                    }
                    case draw_paint_SKIP:
                        draw_text_stateScan(chunk, c, next, &state);
                        c = (displaced == ' ') ? next + 1 :
                            (displaced == draw_font_comment) ? next + 2 : next;
                        while (chunk[c] == ' ') c++;   /* Skip extra spaces */
                        break;

                    case draw_paint_LOW:
                        newColumn = TRUE;
                        c += draw_insert; /* Teeny fiddle */
                        break;
                }

                /* Restore split character, allowing for hyphens */
                if (displaced == draw_font_comment)
                {
                    chunk[next-1] = draw_font_comment;
                    chunk[next]   = '-';
                }
                else
                    chunk[next] = displaced;

                /* At the end of a paragraph, shift by para leading */
                if (displaced == '\n') y -= state.paraLeading;
            }
        }
        else
        {
            /* Chunk is empty - treat it as a null paragraph */
            y -= state.paraLeading;
        }
    }

    *error = NULL;
    draw_textPatchup();
    return (TRUE);
}


/*
 Function    : draw_verifyTextArea
 Purpose     : check text area definition
 Parameters  : pointer to start
               OUT: error code
               OUT: error location
               OUT: number of columna
 Returns     : TRUE if ok
 Description : check that all special sequences in the object are OK.

               The default font is set to the number of the first font seen.
               If an error occurs, we return immediately - means checking is
               minimal, but avoids a plethora of messages.

               All texts must start with an ID line of the form:
               \! <number><newline>, where <number> is the text area
               version. If the first character is not a backslash, we prepend
               a standard header.

               The only error that is 'non-fatal' is the font warning.

               The text must end with a newline. Ensureing this is so
               simplified some tests later (i.e. no need to test for hitting
               end of the string as well as terminator).
*/

BOOL draw_verifyTextArea(char *text, int *errcode, char **location, int *columns)
{
    char *end;
    font fonts[draw_text_maxFonts+1]; /* Font table: digit->fontHandle */
    int  seenColumns = FALSE;
    int  handle;

    end = text + strlen(text);
    *columns = 1;
    *location = text;

    /* Check ID heading */
    if (*text++ != '\\' || *text++ != '!')
    {
        *errcode = draw_CorruptTextArea;
        return (FALSE);
    }
    else
    {
        int  version = -1, offset;

        if (draw_getNum(0, text, &version, TRUE, &offset) != draw_numOK
            || version != draw_text_VERSION)
        {
            *errcode = draw_TextAreaVersion;
            return (FALSE);
        }
        text += offset;
    }

    /* Ensure clean termination so we don't need to check for null later */
    if (*(end - 1) != '\n')
    {
        *errcode = draw_MissingNewline;
        return (FALSE);
    }

    /* Look for special sequence */
    while (text < end && *(text += strcspn(text, "\\\n")) != '\0')
    {
        if (*text == '\n')
        {
            text    += 1;
            continue;
        }

        *location = text+1;

        switch (*(++text))
        {
            case '-': case '\\':
                text += 1; break;
            case '\n':
                text += 1; break;
            case ';':                                           /* \;: skip */
                while (*text++ != '\n') ;
                break;
            case 'A':               /* \A: must be followed by L, R, C or D */
                if (strchr("LRCD", *(++text)) == NULL)
                {
                    *errcode = draw_BadAlign;
                    return (FALSE);
                }
                if (*(++text) == '/') text += 1;
                break;
            case 'B':
            case 'C':             /* \B or \C followed by three numbers */
            {
                int  i;

                if (draw_getNum (1, text, NULL, FALSE, &i) != draw_numMORE
                    || draw_getNum(i, text, NULL, FALSE, &i) != draw_numMORE
                    || draw_getNum(i, text, NULL, FALSE, &i) != draw_numOK)
                {
                    *errcode = draw_BadTerminator;
                    return (FALSE);
                }
                text += i;
                break;
            }
            case 'D':                             /* \D: get no. of columns */
            {
                if (seenColumns)
                {
                    *errcode = draw_ManyDCommands;
                    return (FALSE);
                }
                else
                {   int i;
                    if (draw_getNum(1, text, columns, FALSE, &i) != draw_numOK)
                    {
                        *errcode = draw_BadTerminator;
                        return (FALSE);
                    }
                    else
                    {
                        seenColumns = TRUE;
                        text += i;
                    }
                }
                break;
            }
            case 'F':                          /* \F: check font definition */
            {
                int fontNum;
                char *fontDefText;

                while (*(++text) == ' ') ;
                text = draw_text_getFontNum(fontDefText = text, &fontNum);
                if (fontNum == -1)
                {
                    *errcode = draw_BadFontNumber;
                    return (FALSE);
                }
                else
                {
                    int  sizeTerm, i;

                    /* Skip to a non-space, i.e. to start of name */
                    while (text < end && *text == ' ') text += 1;

                    /* Look for terminating space, i.e. to end of name */
                    while (text < end && *text != ' ') text += 1;
                    if (*text != ' ')
                    {
                        *errcode = draw_UnexpectedCharacter;
                        return (FALSE);
                    }

                    if ((sizeTerm = draw_getNum(0, text, NULL, FALSE, &i))
                                      == draw_numMORE)
                    {
                        /* Read second number */
                        if (draw_getNum(i, text, NULL, FALSE, &i) !=draw_numOK)
                        {
                            *errcode = draw_BadFontWidth;
                            return (FALSE);
                        }
                    }
                    else if (sizeTerm == draw_numBAD)
                    {
                        *errcode = draw_BadFontSize;
                        return (FALSE);
                    }

                    /* Try to load the font */
                    draw_text_setFont(fontDefText, fonts, FALSE, &handle);

                    if (handle != -1) font_lose(handle);
                    text += i;
                }
                break;
            }
            case 'L': case 'P':  /* \L, \P: must be followed by <number><nl> */
            {
                int  i;

                if (draw_getNum(1, text, NULL, FALSE, &i) != draw_numOK)
                {
                    *errcode = draw_BadTerminator;
                    return (FALSE);
                }
                text += i;
                break;
            }
            case 'M':               /* \M: check numbers */
            {   int i;

                if (draw_getNum(1, text, NULL, FALSE, &i) != draw_numMORE
                    || draw_getNum(i, text, NULL, FALSE, &i) != draw_numOK)
                {
                    *errcode = draw_BadTerminator;
                    return (FALSE);
                }
                text += i;
                break;
            }
            case 'U':               /* \U: \U. or \U<n1><space><n2> */
                if (*(++text) != '.')
                {   int i;

                    if (draw_getNum(0, text, NULL, TRUE, &i) != draw_numMORE
                        || draw_getNum(i, text, NULL, FALSE, &i) != draw_numOK)
                    {
                        *errcode = draw_BadTerminator;
                        return (FALSE);
                    }
                    text += i;
                }
                break;
            case 'V':        /* \V: must be followed by -<digit> or <digit> */
                if (*(++text) == '-') text += 1;
                if (!isdigit(*text++))
                {
                    *errcode = draw_NonDigitV;
                    return (FALSE);
                }
                if (*(++text) == '/') text += 1;
                break;
            default:                               /* \<other>: digits only */
                if (!isdigit(*text))
                {
                    *errcode = draw_BadEscape;
                    return (FALSE);
                }
                else
                {
                    int fontNumber;
                    text = draw_text_getFontNum(text, &fontNumber);
                    if (fontNumber == -1)
                    {
                        *errcode = draw_BadFontNumber;
                        return (FALSE);
                    }
                    if (*text == '/') text += 1;
                }
                break;
        }
    }

    if (*columns < 1)
    {
        *errcode = draw_FewColumns;
        return (FALSE);
    }

    return (TRUE);
}

/*
 Function    : do_objtextarea
 Purpose     : render a text area object
 Parameters  : header pointer
               x, y origins
               clipping box in data base coordinates
 Returns     : os_error or Null
 Description : draw the object. Clipping is carried out on a line by line
               basis, in the paiting check -- it is most convenient to have the
               clipping box in scaled font units at this point, but without the
               origin shift applied.
*/

os_error *do_objtextarea(draw_objptr hdrptr, int orgx, int orgy,
                                             draw_bboxtyp *clip)
{
    os_error *err;

    /* Convert clipping box into screen coordinates */
    clip->x0 = scaleup(draw_drawToFont(clip->x0));
    clip->y0 = scaleup(draw_drawToFont(clip->y0));
    clip->x1 = scaleup(draw_drawToFont(clip->x1));
    clip->y1 = scaleup(draw_drawToFont(clip->y1));

    chunk = NULL;
    chunkLen = 0;

    draw_textArea(hdrptr, orgx, orgy, clip, &err); /* No error check */
    return(err);
}

/*
 Function    : do_objtextcol
 Purpose     : render a text column object
 Parameters  : header pointer
               x, y origins
               cliiping box in data base coordinates
 Returns     : os_error or Null
 Description : find the parent and render it
*/

os_error *do_objtextcol(draw_objptr hdrptr, int orgx,int orgy,
                                            draw_bboxtyp *clip)
{
  return(do_objtextarea(draw_text_findParent(hdrptr.textcolp),
                        orgx, orgy, clip
                       )
        );
}


BOOL drawtextc_init(void)
{
    return ((draw_usedFont = malloc(256)) !=0);
}