diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2016-03-19 17:36:09 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2016-03-19 17:36:09 +0000 |
commit | 31d8f30d9c901a14cb49da404f1148ad5d09351a (patch) | |
tree | 31fc51e087b1030c2722845143dc945a78f2dc3f /generator | |
parent | d9bcc1ab220d2091362bcc86c5829ba86652c71b (diff) | |
download | netpbm-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.c | 837 |
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, + ¤tPosition, &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, + ¤tPosition, &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; } + + + |