about summary refs log tree commit diff
path: root/generator
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2016-03-19 17:36:09 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2016-03-19 17:36:09 +0000
commit31d8f30d9c901a14cb49da404f1148ad5d09351a (patch)
tree31fc51e087b1030c2722845143dc945a78f2dc3f /generator
parentd9bcc1ab220d2091362bcc86c5829ba86652c71b (diff)
downloadnetpbm-mirror-31d8f30d9c901a14cb49da404f1148ad5d09351a.tar.gz
netpbm-mirror-31d8f30d9c901a14cb49da404f1148ad5d09351a.tar.xz
netpbm-mirror-31d8f30d9c901a14cb49da404f1148ad5d09351a.zip
Lots of Pbmtext/libpbmfont fixes and enhancements from Akira Urushibata
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@2686 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'generator')
-rw-r--r--generator/pbmtext.c837
1 files changed, 436 insertions, 401 deletions
diff --git a/generator/pbmtext.c b/generator/pbmtext.c
index 9f4366d4..221f215e 100644
--- a/generator/pbmtext.c
+++ b/generator/pbmtext.c
@@ -14,6 +14,7 @@
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
 #include <string.h>
+#include <math.h>
 #include <limits.h>
 #include <assert.h>
 
@@ -23,7 +24,9 @@
 #include "pbm.h"
 #include "pbmfont.h"
 
-struct cmdlineInfo {
+#define  MAXLINECHARS 5000
+
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -41,10 +44,9 @@ struct cmdlineInfo {
 
 
 
-
 static void
 parseCommandLine(int argc, const char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
@@ -80,7 +82,23 @@ parseCommandLine(int argc, const char ** argv,
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
-        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+    /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (cmdlineP->width > 0 && cmdlineP->nomargins == TRUE) {
+        pm_message("-nomargins has no effect when -width is specified");
+        cmdlineP->nomargins = FALSE;
+    } else if (cmdlineP->width > INT_MAX-10)
+        pm_error("-width value too large");
+
+    if (cmdlineP->space > pbm_maxfontwidth())
+        pm_error("-space value too large");
+    else if (cmdlineP->space < -pbm_maxfontwidth())
+        pm_error("negative -space value too large");
+
+    if (cmdlineP->lspace > pbm_maxfontheight())
+        pm_error("-lspace value too large");
+    else if (cmdlineP->lspace < -pbm_maxfontheight())
+        pm_error("negative -lspace value too large");
 
     if (argc-1 == 0)
         cmdlineP->text = NULL;
@@ -90,22 +108,22 @@ parseCommandLine(int argc, const char ** argv,
         int totaltextsize;
 
         totaltextsize = 1;  /* initial value */
-        
-        text = malloc(totaltextsize);  /* initial allocation */
+
+        MALLOCARRAY(text, MAXLINECHARS+1);        
+
+        if (!text)
+            pm_error("Unable to allocate memory for a buffer of up to %u "
+                     "characters of text", MAXLINECHARS);
+
         text[0] = '\0';
         
         for (i = 1; i < argc; ++i) {
             if (i > 1) {
-                totaltextsize += 1;
-                text = realloc(text, totaltextsize);
-                if (text == NULL)
-                    pm_error("out of memory allocating space for input text");
                 strcat(text, " ");
             } 
-            totaltextsize += strlen(argv[i]);
-            text = realloc(text, totaltextsize);
-            if (text == NULL)
-                pm_error("out of memory allocating space for input text");
+            totaltextsize += strlen(argv[i]) + 1;
+            if (totaltextsize > MAXLINECHARS)
+                pm_error("input text too long");
             strcat(text, argv[i]);
         }
         cmdlineP->text = text;
@@ -135,7 +153,7 @@ reportFont(struct font * const fontP) {
 
 
 static void
-computeFont(struct cmdlineInfo const cmdline,
+computeFont(struct CmdlineInfo const cmdline,
             struct font **     const fontPP) {
 
     struct font * fontP;
@@ -176,13 +194,15 @@ allocTextArray(struct text * const textP,
 
     unsigned int line;
 
-    textP->allocatedLineCount = maxLineCount;
-
+    textP->allocatedLineCount = maxColumnCount > 0 ? maxLineCount : 0;
     MALLOCARRAY_NOFAIL(textP->textArray, maxLineCount);
-    
-    for (line = 0; line < maxLineCount; ++line)
-        MALLOCARRAY_NOFAIL(textP->textArray[line], maxColumnCount+1);
 
+    for (line = 0; line < maxLineCount; ++line) {
+        if(maxColumnCount > 0)
+            MALLOCARRAY_NOFAIL(textP->textArray[line], maxColumnCount+1);
+    else
+        textP->textArray[line] = NULL;
+    }
     textP->lineCount = 0;
 }
 
@@ -254,11 +274,17 @@ fixControlChars(const char *  const input,
             unsigned int const nextTabStop =
                 (outCursor + tabSize) / tabSize * tabSize;
 
+        if (fontP->glyph[(unsigned char)' '] == NULL)
+            pm_error("space character not defined in font");
+
             while (outCursor < nextTabStop)
                 output[outCursor++] = ' ';
         } else if (!fontP->glyph[(unsigned char)input[inCursor]]) {
             /* Turn this unknown char into a single space. */
-            output[outCursor++] = ' ';
+            if(fontP->glyph[(unsigned char) ' '] == NULL)
+                pm_error("space character not defined in font");
+            else
+                output[outCursor++] = ' ';
         } else
             output[outCursor++] = input[inCursor];
 
@@ -274,18 +300,16 @@ fixControlChars(const char *  const input,
 
 
 static void
-fill_rect(bit** const bits, 
-          int   const row0, 
-          int   const col0, 
-          int   const height, 
-          int   const width, 
-          bit   const color) {
-
-    int row;
-
-    for (row = row0; row < row0 + height; ++row) {
-        int col;
-        for (col = col0; col < col0 + width; ++col)
+fillRect(bit** const bits, 
+         int   const height, 
+         int   const width, 
+         bit   const color) {
+
+    unsigned int row;
+
+    for (row = 0; row < height; ++row) {
+        unsigned int col;
+        for (col = 0; col < width; ++col)
             bits[row][col] = color;
     }
 }
@@ -293,102 +317,227 @@ fill_rect(bit** const bits,
 
 
 static void
-get_line_dimensions(const char line[], const struct font * const font_p, 
-                    const float intercharacter_space,
-                    double * const bwidP, int * const backup_space_needed_p) {
+getEdges(double               const currentPosition,
+         char                 const currentChar,
+         const struct glyph * const glyphP, 
+         int                  const currLeftEdge,
+         double               const currRightEdge,
+         int                * const newLeftEdgeP,
+         double             * const newRightEdgeP) {
+
+    int leftEdge;
+    double rightEdge;
+  
+    if (glyphP == NULL)
+        pm_error("Unrenderable char: %c", currentChar);
+    else {
+        leftEdge  =  (int) MIN(currentPosition + glyphP->x, currLeftEdge);
+        rightEdge =  MAX(currentPosition + glyphP->x + glyphP->width,
+                         currRightEdge);
+    }
+    *newLeftEdgeP  = leftEdge;
+    *newRightEdgeP = rightEdge;
+}
+
+
+
+static void
+advancePosition(double               const currentPosition,
+                char                 const currentChar,
+                const struct glyph * const glyphP,
+                float                const space,
+                double               const accumulatedSpace,
+                double             * const newPositionP,
+                double             * const newAccumulatedSpaceP) {
 /*----------------------------------------------------------------------------
-   Determine the width in pixels of the line of text line[] in the font
-   *font_p, and return it as *bwidP.  Also determine how much of this
-   width goes to the left of the nominal starting point of the line because
-   the first character in the line has a "backup" distance.  Return that
-   as *backup_space_needed_p.
+  Advance position according to value for glyph.
+  Add extra intercharacter space if -space option was used.
+
+  The advance value must be zero or positive.
+----------------------------------------------------------------------------*/
+
+    /* Start position of next character */
+    /* Must not move left from current position */
+    int const fullPixels = (int) (accumulatedSpace + space);
+        /* round toward 0 */
+    int const advance    = (int) glyphP->xadd + fullPixels;
+    
+    if (advance < 0) {
+        if (space < 0)
+            pm_error("Negative -space value too large"); 
+        else
+            pm_error("Abnormal horizontal advance value %d "
+                     "for char '%c' 0x%x.",
+                     glyphP->xadd, currentChar, (unsigned int) currentChar);
+    }
+    else if (currentPosition + advance > INT_MAX)
+        pm_error("Image is too wide");
+    else {
+        *newPositionP = currentPosition + advance;
+        *newAccumulatedSpaceP = accumulatedSpace + space
+            - (double) fullPixels;
+    }
+}
+
+
+
+static void
+getLineDimensions(char                const line[],
+                  const struct font * const fontP, 
+                  float               const intercharacterSpace,
+                  double *            const rightEdgeP,
+                  int    *            const leftEdgeP) {
+/*----------------------------------------------------------------------------
+   Determine the left edge and right edge in pixels of the line of text
+   line[] in the font *fontP, and return them as *leftEdgeP and *rightEdgeP.
+   *leftEdgeP will be negative if the leftmost character in the line has a
+   "backup" distance.
+
+   Note that the right (left) edge may not belong to the last (first)
+   character in the text line.  This happens when the font is slanted
+   (xadd is smaller than width) and/or intercharacter space is negative.
+   This is illustrated by the following:
+
+     pbmtext -nomargin "ART." | pnmshear -30 -noantialias
+
+   Also note that there may be no black pixels on what is reported as an edge.
+   This often happens with fixed-width font in which the white areas on the
+   sides are not trimmed.
 -----------------------------------------------------------------------------*/
     int cursor;  /* cursor into the line of text */
-    double accumulatedIcs;
-        /* accumulated intercharacter space so far in the line we are 
-           stepping through.  Because the intercharacter space might not be
-           an integer, we accumulate it here and realize full pixels whenever
-           we have more than one pixel.  Note that this can be negative
-           (which means were crowding, rather than spreading, text).
+    double currentPosition;
+        /* sum of xadd values and intercharacter space so far in line.  this
+           is never negative.
         */
-    double bwid;
-    bool no_chars_yet; 
-        /* We haven't seen any renderable characters yet in the line. */
-    struct glyph * lastGlyphP;
-        /* Glyph of last character processed so far.  Undefined if
-           'no_chars_yet'.
+    double accumulatedIcs;
+        /* accumulated intercharacter space so far in the line we are stepping
+           through.  Because the intercharacter space might not be an integer,
+           we accumulate it here and realize full pixels whenever we have more
+           than one pixel.  Note that this can be negative (which means were
+           crowding, rather than spreading, text).
         */
+    int leftEdge;
+    double rightEdge;
 
-    no_chars_yet = TRUE;   /* initial value */
-    accumulatedIcs = 0.0;  /* initial value */
-    bwid = 0.0;  /* initial value */
+    currentPosition = 0;  /* initial value */
+    accumulatedIcs  = 0.0;  /* initial value */
+
+    leftEdge  = INT_MAX;  /* initial value */
+    rightEdge = INT_MIN;  /* initial value */
     
-    for (cursor = 0; line[cursor] != '\0'; cursor++) {
+    for (cursor = 0; line[cursor] != '\0'; ++cursor) {
+        char const currentChar = line[cursor];
         struct glyph * const glyphP = 
-            font_p->glyph[(unsigned char)line[cursor]];
-
-        if (glyphP) {
-            if (no_chars_yet) {
-                no_chars_yet = FALSE;
-                if (glyphP->x < 0) 
-                    *backup_space_needed_p = -glyphP->x;
-                else {
-                    *backup_space_needed_p = 0;
-                    bwid += glyphP->x;
-                }
-            } else {
-                /* handle extra intercharacter space (-space option) */
-                accumulatedIcs += intercharacter_space;
-                if (accumulatedIcs >= INT_MAX)
-                    pm_error("Image width too large.");
-                if (accumulatedIcs <= INT_MIN)
-                    pm_error("Absurdly large negative -space value.");
-                {
-                    int const fullPixels = (int) accumulatedIcs;
-                    bwid           += fullPixels;
-                    accumulatedIcs -= fullPixels;
-                }
-            }
-            lastGlyphP = glyphP;
-            bwid += glyphP->xadd;
-        }
+            fontP->glyph[(unsigned char) currentChar];
+
+        getEdges(currentPosition, currentChar, glyphP, leftEdge, rightEdge,
+                 &leftEdge, &rightEdge);
+
+        advancePosition(currentPosition, currentChar, glyphP,
+                        intercharacterSpace, accumulatedIcs,
+                        &currentPosition, &accumulatedIcs);
     }
-    if (no_chars_yet)
-        /* Line has no renderable characters */
-        *backup_space_needed_p = 0;
-    else {
-        /* Line has at least one renderable character.
-           Recalculate width of last character in line so it ends
-           right at the right edge of the glyph (no extra space to
-           anticipate another character).
-        */
-        bwid -= lastGlyphP->xadd;
-        bwid += lastGlyphP->width + lastGlyphP->x;
+
+    if (line[0] == '\0') {     /* Empty line */
+        leftEdge  = 0;
+        rightEdge = 0.0;
+    }
+
+    *leftEdgeP  = leftEdge;
+    *rightEdgeP = rightEdge;
+}
+
+
+
+static void
+getCharsWithinWidth(char                const line[],
+                    const struct font * const fontP, 
+                    float               const intercharacter_space,
+                    unsigned int        const targetWidth,
+                    unsigned int      * const charCountP,
+                    int               * const leftEdgeP) {
+/*----------------------------------------------------------------------------
+   Determine how many characters of text line[] fit into an image of target
+   width targetWidth.
+
+   *leftEdgeP will be negative if the leftmost character in the line has a
+   "backup" distance and zero if it does not.
+-----------------------------------------------------------------------------*/
+    if (line[0] == '\0') {
+        /* Empty line */
+        *leftEdgeP = 0;
+        *charCountP = 0;
+    } else {
+        unsigned int cursor;  /* cursor into the line of text */
+        double currentPosition;
+        double accumulatedIcs;
+        int leftEdge;
+        double rightEdge;
+        unsigned int currentWidth;
+
+        currentPosition = 0;    /* initial value */
+        accumulatedIcs  = 0.0;  /* initial value */
+
+        leftEdge     = INT_MAX;  /* initial value */
+        rightEdge    = INT_MIN;  /* initial value */
+    
+        for (cursor = 0, currentWidth = 0;
+             currentWidth <= targetWidth && line[cursor] != '\0';
+             ++cursor) {
+            char const currentChar = line[cursor];
+            struct glyph * const glyphP = 
+                fontP->glyph[(unsigned char) currentChar];
+
+            getEdges(currentPosition, currentChar, glyphP, leftEdge, rightEdge,
+                     &leftEdge, &rightEdge);
+
+            advancePosition(currentPosition, currentChar, glyphP,
+                            intercharacter_space, accumulatedIcs,
+                            &currentPosition, &accumulatedIcs);
+
+            currentWidth = rightEdge - ((leftEdge > 0 ) ? 0 : leftEdge);
+        }
+
+        if (currentWidth > targetWidth) {
+            if (cursor == 1)
+                pm_error("-width value too small "
+                         "to accomodate single character");
+            else
+                *charCountP = cursor - 1;
+        } else
+            *charCountP = cursor;
+
+        *leftEdgeP  = leftEdge;
     }
-    if (bwid > INT_MAX)
-        pm_error("Image width too large.");
-    else
-        *bwidP = bwid; 
 }
 
 
 
 static void
-insert_character(const struct glyph * const glyph, 
-                 int                  const toprow, 
-                 int                  const leftcol,
-                 bit **               const bits) {
+insertCharacter(const struct glyph * const glyph, 
+                int                  const toprow, 
+                int                  const leftcol,
+                unsigned int         const cols,
+                unsigned int         const rows,
+                bit **               const bits) {
 /*----------------------------------------------------------------------------
    Insert one character (whose glyph is 'glyph') into the image bits[].
    Its top left corner shall be row 'toprow', column 'leftcol'.
 -----------------------------------------------------------------------------*/
+    unsigned int glyph_y;  /* Y position within the glyph */
 
-    int glyph_y, glyph_x;  /* position within the glyph */
+    if (leftcol + glyph->x < 0 ||
+        leftcol + glyph->x + glyph->width > cols ||
+        toprow < 0 ||
+        toprow + glyph->height >rows )
+        pm_error("internal error.  Rendering out of bounds");
 
-    for (glyph_y = 0; glyph_y < glyph->height; glyph_y++) {
-        for (glyph_x = 0; glyph_x < glyph->width; glyph_x++) {
+    for (glyph_y = 0; glyph_y < glyph->height; ++glyph_y) {
+        unsigned int glyph_x;  /* position within the glyph */
+
+        for (glyph_x = 0; glyph_x < glyph->width; ++glyph_x) {
             if (glyph->bmap[glyph_y * glyph->width + glyph_x])
-                bits[toprow+glyph_y][leftcol+glyph->x+glyph_x] = 
+                bits[toprow + glyph_y][leftcol + glyph->x + glyph_x] = 
                     PBM_BLACK;
         }
     }
@@ -397,25 +546,27 @@ insert_character(const struct glyph * const glyph,
 
 
 static void
-insert_characters(bit **        const bits, 
-                  struct text   const lp,
-                  struct font * const fontP, 
-                  int           const topmargin, 
-                  int           const leftmargin,
-                  float         const intercharacter_space,
-                  int           const lspace) {
+insertCharacters(bit **        const bits, 
+                 struct text   const lp,
+                 struct font * const fontP, 
+                 int           const topmargin, 
+                 int           const leftmargin,
+                 float         const intercharacter_space,
+                 unsigned int  const cols,
+                 unsigned int  const rows,
+                 int           const lspace) {
 /*----------------------------------------------------------------------------
    Render the text 'lp' into the image 'bits' using font *fontP and
    putting 'intercharacter_space' pixels between characters and
    'lspace' pixels between the lines.
 -----------------------------------------------------------------------------*/
-    int line;  /* Line number in input text */
+    unsigned int line;  /* Line number in input text */
 
     for (line = 0; line < lp.lineCount; ++line) {
-        int row;  /* row in image of top of current typeline */
-        int leftcol;  /* Column in image of left edge of current glyph */
-        int cursor;  /* cursor into a line of input text */
-        float accumulated_ics;
+        unsigned int row;  /* row in image of top of current typeline */
+        double leftcol;  /* Column in image of left edge of current glyph */
+        unsigned int cursor;  /* cursor into a line of input text */
+        double accumulatedIcs;
             /* accumulated intercharacter space so far in the line we
                are building.  Because the intercharacter space might
                not be an integer, we accumulate it here and realize
@@ -424,309 +575,171 @@ insert_characters(bit **        const bits,
 
         row = topmargin + line * (fontP->maxheight + lspace);
         leftcol = leftmargin;
-        accumulated_ics = 0.0;  /* initial value */
+        accumulatedIcs = 0.0;  /* initial value */
     
         for (cursor = 0; lp.textArray[line][cursor] != '\0'; ++cursor) {
-            unsigned int const glyphIndex = 
-                (unsigned char)lp.textArray[line][cursor];
-            struct glyph* glyph;   /* the glyph for this character */
-
-            glyph = fontP->glyph[glyphIndex];
-            if (glyph != NULL) {
-                const int toprow = row + fontP->maxheight + fontP->y 
-                    - glyph->height - glyph->y;
-                    /* row number in image of top row in glyph */
-                
-                insert_character(glyph, toprow, leftcol, bits);
-
-                leftcol += glyph->xadd;
-                {
-                    /* handle extra intercharacter space (-space option) */
-                    int full_pixels;  /* integer part of accumulated_ics */
-                    accumulated_ics += intercharacter_space;
-                    full_pixels = (int) accumulated_ics;
-                    if (full_pixels > 0) {
-                        leftcol += full_pixels;
-                        accumulated_ics -= full_pixels;
-                    }
-                }
-            }
+            char const currentChar = lp.textArray[line][cursor];
+            unsigned int const glyphIndex = (unsigned int) currentChar;
+            struct glyph * const glyphP = fontP->glyph[glyphIndex];
+            int const toprow = row + fontP->maxheight + fontP->y 
+                - glyphP->height - glyphP->y;
+                /* row number in image of top row in glyph */
+
+            assert(glyphP != NULL);
+            
+            insertCharacter(glyphP, toprow, leftcol, cols, rows, bits);
+
+            advancePosition(leftcol, currentChar, glyphP,
+                            intercharacter_space, accumulatedIcs,
+                            &leftcol, &accumulatedIcs);
         }
     }
 }
 
 
-struct outputTextCursor {
-    struct text text;
-        /* The output text.  The lineCount field of this represents
-           the number of lines we have completed.  The line after that
-           is the one we are currently filling.
-        */
-    unsigned int maxWidth;
-        /* A line of output can't be wider than this many pixels */
-    float intercharacterSpace;
-        /* The amount of extra space, in characters, that should be added
-           between every two characters (Pbmtext -space option)
-        */
-    unsigned int columnNo;
-        /* The column Number (starting at 0) in the current line that we are
-           filling where the next character goes.
-        */
-    bool noCharsYet;
-        /* We haven't put any renderable characters yet in the
-           output line. 
-        */
-    unsigned int widthSoFar;
-        /* The accumulated width, in pixels, of all the characters now
-           in the current output line 
-        */
-    float accumulatedIcs;
-        /* accumulated intercharacter space so far in the line we
-           are stepping through.  Because the intercharacter space
-           might not be an integer, we accumulate it here and
-           realize full pixels whenever we have more than one
-           pixel.  Note that this is negative if we're crowding, rather
-           than spreading, characters.
-        */
-};
-
-
 
 static void
-initializeFlowedOutputLine(struct outputTextCursor * const cursorP) {
-
-    cursorP->columnNo = 0;
-    cursorP->noCharsYet = TRUE;
-    cursorP->widthSoFar = 0.0;
-    cursorP->accumulatedIcs = 0.0;
-}
-
+flowText(struct text    const inputText,
+         int            const targetWidth, 
+         struct font  * const fontP, 
+         float          const intercharacterSpace,
+         struct text  * const outputTextP,
+         unsigned int * const maxleftbP) {
+    
+    unsigned int outputLineNum;
+    unsigned int incursor;   /* cursor into the line we are reading */
+    unsigned int const maxLineCount = 50; /* max output lines */
+    int leftEdge;
+    int leftExtreme = 0;
+    unsigned int charCount;
 
+    allocTextArray(outputTextP, maxLineCount, 0);
 
-static void
-initializeFlowedOutput(struct outputTextCursor * const cursorP,
-                       unsigned int              const maxLines,
-                       unsigned int              const maxWidth,
-                       float                     const intercharacterSpace) {
-    
-    allocTextArray(&cursorP->text, maxLines, maxWidth);
-    cursorP->maxWidth = maxWidth;
-    cursorP->intercharacterSpace = intercharacterSpace;
-    initializeFlowedOutputLine(cursorP);
-}
+    for (incursor = 0, outputLineNum = 0;
+         inputText.textArray[0][incursor] != '\0'; ) {
 
+        unsigned int outcursor;
 
+        getCharsWithinWidth(&inputText.textArray[0][incursor], fontP, 
+                            intercharacterSpace, targetWidth,
+                            &charCount, &leftEdge);
 
-static void
-finishOutputLine(struct outputTextCursor * const cursorP) {
+        MALLOCARRAY(outputTextP->textArray[outputLineNum], charCount+1);
 
-    if (cursorP->text.lineCount < cursorP->text.allocatedLineCount) {
-        char * const currentLine = 
-            cursorP->text.textArray[cursorP->text.lineCount];
-        currentLine[cursorP->columnNo++] = '\0';
-        ++cursorP->text.lineCount;
-    }
-}
+        if (!outputTextP->textArray[outputLineNum])
+            pm_error("Unable to allocate memory for the text of line %u, "
+                     "%u characters long", outputLineNum, charCount);
 
+        ++outputTextP->allocatedLineCount;
 
+        for (outcursor = 0; outcursor < charCount; ++outcursor, ++incursor) 
+            outputTextP->textArray[outputLineNum][outcursor] = 
+                inputText.textArray[0][incursor];
 
-static void
-placeCharacterInOutput(char                      const lastch,
-                       struct font *             const fontP, 
-                       struct outputTextCursor * const cursorP) {
-/*----------------------------------------------------------------------------
-   Place a character of text in the text array at the position indicated
-   by *cursorP, keeping track of what space this character will occupy
-   when this text array is ultimately rendered using font *fontP.
+        outputTextP->textArray[outputLineNum][charCount] = '\0'; 
+        ++outputLineNum;
+        if (outputLineNum >= maxLineCount)
+            pm_error("-width too small.  too many output lines");
 
-   Note that while we compute how much space the character will take when
-   rendered, we don't render it.
------------------------------------------------------------------------------*/
-    if (cursorP->text.lineCount < cursorP->text.allocatedLineCount) {
-        unsigned int const glyphIndex = (unsigned char)lastch;
-        if (fontP->glyph[glyphIndex]) {
-            if (cursorP->noCharsYet) {
-                cursorP->noCharsYet = FALSE;
-                if (fontP->glyph[glyphIndex]->x > 0) 
-                    cursorP->widthSoFar += fontP->glyph[glyphIndex]->x;
-            } else {
-                /* handle extra intercharacter space (-space option) */
-                cursorP->accumulatedIcs += cursorP->intercharacterSpace;
-                {
-                    int const fullPixels = (int)cursorP->accumulatedIcs;
-                    cursorP->widthSoFar     += fullPixels;
-                    cursorP->accumulatedIcs -= fullPixels;
-                }
-            }
-            cursorP->widthSoFar += fontP->glyph[glyphIndex]->xadd;
-        }
-        if (cursorP->widthSoFar < cursorP->maxWidth) {
-            char * const currentLine = 
-                cursorP->text.textArray[cursorP->text.lineCount];
-            currentLine[cursorP->columnNo++] = lastch;
-        } else {
-            /* Line is full; finish it off, start the next one, and
-               place the character there.
-            */
-            /* TODO: We really should back up to the previous white space
-               character and move the rest of the line to the next line
-            */
-            finishOutputLine(cursorP);
-            initializeFlowedOutputLine(cursorP);
-            placeCharacterInOutput(lastch, fontP, cursorP);
-        }
+        leftExtreme = MIN(leftEdge, leftExtreme);
     }
+    outputTextP->lineCount = outputLineNum;
+    *maxleftbP = (unsigned int) -leftExtreme;
 }
 
 
 
 static void
-flowText(struct text    const inputText,
-         int            const width, 
-         struct font *  const fontP, 
-         float          const intercharacterSpace,
-         struct text *  const outputTextP) {
-    
-    unsigned int const maxLineCount = 50;
-
-    unsigned int inputLine;  
-        /* Input line number on which we are currently working */
-    struct outputTextCursor outputCursor;
-
-    for (inputLine = 0; inputLine < inputText.lineCount; ++inputLine) {
-        unsigned int incursor;   /* cursor into the line we are reading */
-
-        initializeFlowedOutput(&outputCursor, maxLineCount,
-                               width, intercharacterSpace);
-        
-        for (incursor = 0; 
-             inputText.textArray[inputLine][incursor] != '\0'; 
-             ++incursor)
-            placeCharacterInOutput(inputText.textArray[inputLine][incursor],
-                                   fontP, &outputCursor);
-        finishOutputLine(&outputCursor);
-    }
-    *outputTextP = outputCursor.text;
-}
+truncateText(struct text    const inputText, 
+             unsigned int   const targetWidth, 
+             struct font  * const fontP, 
+             float          const intercharacterSpace,
+             unsigned int * const maxleftbP) {
 
+    unsigned int lineNum;  /* Line number on which we are currently working */
+    int leftEdge;
+    int leftExtreme = 0;
 
+    for (lineNum = 0; lineNum < inputText.lineCount; ++lineNum) {
+        char * const currentLine = inputText.textArray[lineNum];
 
-static void
-truncateText(struct text   const inputText, 
-             unsigned int  const width, 
-             struct font * const fontP, 
-             float         const intercharacterSpace,
-             struct text * const outputTextP) {
-
-    struct text truncatedText;
-    int line;  /* Line number on which we are currently working */
-
-    allocTextArray(&truncatedText, inputText.lineCount, width);
-
-    for (line = 0; line < inputText.lineCount; ++line){
-        int cursor;  /* cursor into the line of text */
-        unsigned char lastch;  /* line[cursor] */
-        int widthSoFar;
-            /* How long the line we've built, in pixels, is so far */
-        float accumulatedIcs;
-        /* accumulated intercharacter space so far in the line we are 
-           stepping through.  Because the intercharacter space might not be
-           an integer, we accumulate it here and realize full pixels whenever
-           we have more than one pixel.  Note that this is negative if we're
-           crowding, not spreading, characters.
-        */
+        unsigned int charCount;
 
-        int noCharsYet; 
-        /* logical: we haven't seen any renderable characters yet in 
-           the line.
-        */
-        noCharsYet = TRUE;   /* initial value */
-        widthSoFar = 0;  /* initial value */
-        accumulatedIcs = 0.0;  /* initial value */
+        getCharsWithinWidth(currentLine, fontP, 
+                            intercharacterSpace, targetWidth,
+                            &charCount, &leftEdge);
 
-        truncatedText.textArray[line][0] = '\0';  /* Start with empty line */
-
-        for (cursor = 0; 
-             inputText.textArray[line][cursor] != '\0' && widthSoFar < width; 
-             cursor++) {
-            lastch = inputText.textArray[line][cursor];
-            if (fontP->glyph[(unsigned char)lastch]) {
-                if (noCharsYet) {
-                    noCharsYet = FALSE;
-                    if (fontP->glyph[lastch]->x > 0) 
-                        widthSoFar += fontP->glyph[lastch]->x;
-                } else {
-                    /* handle extra intercharacter space (-space option) */
-                    accumulatedIcs += intercharacterSpace;
-                    {
-                        int const fullPixels = (int) intercharacterSpace;
-                        widthSoFar     += fullPixels;
-                        accumulatedIcs -= fullPixels;
-                    }
-                }
-                widthSoFar += fontP->glyph[lastch]->xadd;
-            }
-            if (widthSoFar < width) {
-                truncatedText.textArray[line][cursor] = 
-                    inputText.textArray[line][cursor];
-                truncatedText.textArray[line][cursor+1] = '\0';
-            }
+        if (currentLine[charCount] != '\0') {
+            pm_message("truncating line %u from %u to %u characters",
+                       lineNum, (unsigned) strlen(currentLine), charCount); 
+            currentLine[charCount] = '\0';
         }
+
+        leftExtreme = MIN(leftEdge, leftExtreme);
     }
-    truncatedText.lineCount = inputText.lineCount;
-    *outputTextP = truncatedText;
+    *maxleftbP = (unsigned int) - leftExtreme;
 }
 
 
 
 static void
-getText(const char          cmdline_text[], 
+getText(char          const cmdlineText[], 
         struct font * const fontP,
-        struct text * const input_textP) {
+        struct text * const inputTextP) {
 
-    struct text input_text;
+    struct text inputText;
 
-    if (cmdline_text) {
-        MALLOCARRAY_NOFAIL(input_text.textArray, 1);
-        input_text.allocatedLineCount = 1;
-        input_text.lineCount = 1;
-        fixControlChars(cmdline_text, fontP,
-                        (const char**)&input_text.textArray[0]);
+    if (cmdlineText) {
+        MALLOCARRAY_NOFAIL(inputText.textArray, 1);
+        inputText.allocatedLineCount = 1;
+        inputText.lineCount = 1;
+        fixControlChars(cmdlineText, fontP,
+                        (const char**)&inputText.textArray[0]);
     } else {
         /* Read text from stdin. */
 
-        unsigned int maxlines;  
-            /* Maximum number of lines for which we presently have space
-               in the text array 
+        unsigned int maxlines;
+            /* Maximum number of lines for which we presently have space in
+               the text array
             */
-        char buf[5000];
-        char ** text_array;
+        char * buf;
+        char ** textArray;
         unsigned int lineCount;
 
+        MALLOCARRAY(buf, MAXLINECHARS+1);
+
+        if (!buf)
+            pm_error("Unable to allocate memory for up to %u characters of "
+                     "text", MAXLINECHARS);
+
         maxlines = 50;  /* initial value */
-        MALLOCARRAY_NOFAIL(text_array, maxlines);
+        MALLOCARRAY(textArray, maxlines);
         
+        if (!textArray)
+            pm_error("Unable to allocate memory for a buffer for up to %u "
+                     "lines of text", maxlines);
+
         lineCount = 0;  /* initial value */
-        while (fgets(buf, sizeof(buf), stdin) != NULL) {
-            if (strlen(buf) + 1 >= sizeof(buf))
+        while (fgets(buf, MAXLINECHARS, stdin) != NULL) {
+            if (strlen(buf) + 1 >= MAXLINECHARS)
                 pm_error("A line of input text is longer than %u characters."
-                         "Cannot process.", (unsigned)sizeof(buf)-1);
+                         "Cannot process", (unsigned int) MAXLINECHARS-1);
             if (lineCount >= maxlines) {
                 maxlines *= 2;
-                REALLOCARRAY(text_array, maxlines);
-                if (text_array == NULL)
+                REALLOCARRAY(textArray, maxlines);
+                if (textArray == NULL)
                     pm_error("out of memory");
             }
-            fixControlChars(buf, fontP, (const char **)&text_array[lineCount]);
-            if (text_array[lineCount] == NULL)
+            fixControlChars(buf, fontP, (const char **)&textArray[lineCount]);
+            if (textArray[lineCount] == NULL)
                 pm_error("out of memory");
             ++lineCount;
         }
-        input_text.textArray = text_array;
-        input_text.lineCount = lineCount;
-        input_text.allocatedLineCount = lineCount;
+        inputText.textArray = textArray;
+        inputText.lineCount = lineCount;
+        inputText.allocatedLineCount = lineCount;
     }
-    *input_textP = input_text;
+    *inputTextP = inputText;
 }
 
 
@@ -736,7 +749,7 @@ computeImageHeight(struct text         const formattedText,
                    const struct font * const fontP,
                    int                 const interlineSpace,
                    unsigned int        const vmargin,
-                   unsigned int *      const rowsP) {
+                   unsigned int      * const rowsP) {
 
     if (interlineSpace < 0 && fontP->maxheight < -interlineSpace)
         pm_error("-lspace value (%d) negative and exceeds font height.",
@@ -761,43 +774,44 @@ computeImageWidth(struct text         const formattedText,
                   float               const intercharacterSpace,
                   unsigned int        const hmargin,
                   unsigned int *      const colsP,
-                  int *               const maxleftbP) {
+                  unsigned int *      const maxleftbP) {
 
     if (intercharacterSpace < 0 && fontP->maxwidth < -intercharacterSpace)
-        pm_error("-space value (%f) negative; exceeds font width.",
+        pm_error("negative -space value %.2f exceeds font width",
                  intercharacterSpace);     
     else {
         /* Find the widest line, and the one that backs up the most past
            the nominal start of the line.
         */
     
-        unsigned int line;
-        double maxwidth;
-        int maxleftb;
+        unsigned int lineNum;
+        double rightExtreme;
+        int leftExtreme;
         double colsD;
 
-        for (line = 0, maxwidth = 0.0, maxleftb = 0;
-             line < formattedText.lineCount;
-             ++line) {
+        rightExtreme = 0.0;  /* initial value */
+        leftExtreme = 0;     /* initial value */
 
-            double bwid;
-            int backupSpaceNeeded;
-            
-            get_line_dimensions(formattedText.textArray[line], fontP,
-                                intercharacterSpace,
-                                &bwid, &backupSpaceNeeded);
+        for (lineNum = 0; lineNum < formattedText.lineCount;  ++lineNum) {
+            double rightEdge;
+            int leftEdge;
             
-            maxwidth = MAX(maxwidth, bwid);
-            maxleftb = MAX(maxleftb, backupSpaceNeeded);
+            getLineDimensions(formattedText.textArray[lineNum], fontP,
+                              intercharacterSpace,
+                              &rightEdge, &leftEdge);
+            rightExtreme = MAX(rightExtreme, rightEdge);
+            leftExtreme  = MIN(leftExtreme,  leftEdge);
         }
-        colsD = 2 * (double) hmargin + (double) maxwidth;
-    
+        leftExtreme = MIN(leftExtreme, 0);
+
+        colsD = (double) (-leftExtreme) + rightExtreme + 2 * hmargin;
+
         if (colsD > INT_MAX-10)
             pm_error("Image width too large.");
         else
             *colsP = (unsigned int) colsD;
     
-        *maxleftbP = maxleftb;
+        *maxleftbP = (unsigned int) - leftExtreme;
     }
 }
 
@@ -806,14 +820,14 @@ computeImageWidth(struct text         const formattedText,
 int
 main(int argc, const char *argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     bit ** bits;
     unsigned int rows, cols;
     struct font * fontP;
-    unsigned int vmargin, hmargin;
+    unsigned int vmargin, hmargin, fontMargin;
     struct text inputText;
     struct text formattedText;
-    int maxleftb;
+    unsigned int maxleftb, maxleftb0;
 
     pm_proginit(&argc, argv);
 
@@ -837,40 +851,58 @@ main(int argc, const char *argv[]) {
     }
     
     if (cmdline.width > 0) {
+        fontMargin = fontP->x < 0 ? -fontP->x : 0;
+
         if (cmdline.width > INT_MAX -10)
             pm_error("-width value too large: %u", cmdline.width);
-            
+        else if (cmdline.width < 2 * hmargin)
+            pm_error("-width value too small: %u", cmdline.width);
         /* Flow or truncate lines to meet user's width request */
-        if (inputText.lineCount == 1) 
-            flowText(inputText, cmdline.width, fontP, cmdline.space,
-                     &formattedText);
-        else
-            truncateText(inputText, cmdline.width, fontP, cmdline.space,
-                         &formattedText);
-        freeTextArray(inputText);
+        else if (inputText.lineCount == 1) {
+            flowText(inputText, cmdline.width - fontMargin,
+                     fontP, cmdline.space, &formattedText, &maxleftb0);
+            freeTextArray(inputText);
+        } else {
+            truncateText(inputText, cmdline.width - fontMargin,
+                         fontP, cmdline.space, &maxleftb0);
+            formattedText = inputText;
+        }
     } else
         formattedText = inputText;
         
     if (formattedText.lineCount == 0)
-        pm_error("No input text.");
+        pm_error("No input text");
     
     computeImageHeight(formattedText, fontP, cmdline.lspace, vmargin,
                        &rows);
 
-    computeImageWidth(formattedText, fontP, cmdline.space, hmargin,
-                      &cols, &maxleftb);
+    computeImageWidth(formattedText, fontP, cmdline.space,
+              cmdline.width > 0 ? 0 : hmargin, &cols, &maxleftb);
 
     if (cols == 0 || rows == 0)
         pm_error("Input is all whitespace and/or non-renderable characters.");
 
+    if (cmdline.width > 0) {
+      if(cmdline.width < cols)
+      pm_error("internal error: calculated image width (%u) exceeds "
+                   "specified -width value: %u",
+                   cols, cmdline.width);
+      else if(maxleftb0 != maxleftb)
+      pm_error("internal error: contradicting backup values");
+      else {
+      hmargin = MIN(hmargin, (cmdline.width - cols) / 2);
+          cols = cmdline.width;
+      }
+    }
+
     bits = pbm_allocarray(cols, rows);
 
     /* Fill background with white */
-    fill_rect(bits, 0, 0, rows, cols, PBM_WHITE);
+    fillRect(bits, rows, cols, PBM_WHITE);
 
     /* Put the text in  */
-    insert_characters(bits, formattedText, fontP, vmargin, hmargin + maxleftb, 
-                      cmdline.space, cmdline.lspace);
+    insertCharacters(bits, formattedText, fontP, vmargin, hmargin + maxleftb, 
+                     cmdline.space, cols, rows, cmdline.lspace);
 
     pbm_writepbm(stdout, bits, cols, rows, 0);
 
@@ -881,3 +913,6 @@ main(int argc, const char *argv[]) {
 
     return 0;
 }
+
+
+