Ejemplo n.º 1
0
static void
add_letter(marqueestruct * mp, char * letter)
{
	int width;
	int b = charBytes(letter[0]);
	int i;

	if (mp->t < 253 - b) {
		for (i = 0; i < b; i++) {
			mp->modwords[mp->t] = letter[i];
			mp->t++;
		}
		mp->modwords[mp->t] = '\0';
		(void) strcat(mp->modwords, "  ");
	}
	mp->x -= charWidth(letter);
	width = charWidth(mp->modwords);
	if (mp->x <= -width) {
		mp->x += width;
		(void) memcpy(mp->modwords, &(mp->modwords[1]), mp->t);
		for (i = 0; i < b; i++) {
			mp->modwords[mp->t] = ' ';
			mp->t--;
		}
	} else
		mp->nonblanks = mp->t;
}
Ejemplo n.º 2
0
int MSTableColumn::columnPixelWidth(void)
{
  if (table()!=0)
   {
     unsigned len=columnWidth();
     int cw=(clipMode()==MSNoClipping)?charWidth('W'):charWidth('0');
     return (len*cw+2*table()->columnSpacing());
   }
  else return 0;
}
Ejemplo n.º 3
0
void
SFont_WriteMaxWidth (SFont_Font * Font, int x, int y, int w, int align,
		     char *tag, char *text)
{
  int tagw = SFont_TextWidth (Font, tag);
  int width = SFont_TextWidth (Font, text);
  int i = 0;
  char *buf = NULL;

  if (width >= w - tagw)
    {
      buf = malloc (sizeof (char) * (strlen (text) + 1));
      width = 0;
      while (width <= w - tagw)
	{
	  buf[i] = text[i];
	  buf[i + 1] = '\0';
	  width += charWidth (Font, text[i]);
	  i++;
	}
      width -= charWidth (Font, text[i]);
      buf[--i] = '\0';
      strcat (buf, tag);
      width += tagw;
    }
  else
    {
      buf = text;
    }

  switch (align)
    {
    case ALEFT:
      SFont_Write (Font, x, y, buf);
      break;
    case ARIGHT:
      SFont_Write (Font, x + w - width, y, buf);
      break;
    case ACENTER:
      SFont_Write (Font, x + (w - width) / 2, y, buf);
      break;
    default:
      break;
    }

  if (buf != text)
    free (buf);

}
Ejemplo n.º 4
0
float acVectorFont::charWidth(unsigned char c) {
  if (c != ' ') {
    return width[c];
  } else { 
    return charWidth('n');
  }
}
Ejemplo n.º 5
0
mintcount_t EmacsBuffer::countColumns(mintcount_t from, mintcount_t to) const {
    mintcount_t cols = 0;
    while (from < to) {
        cols += charWidth(cols, _text[from++]);
    } // while
    return cols;
} // countColumn
Ejemplo n.º 6
0
int
SFont_FillWith (SFont_Font * Font, int x, int y, int w, char c)
{
  int charoffset = ((int) c - 33) * 2 + 1;
  int charw = charWidth (Font, c);
  int i, j = 0;
  SDL_Rect srcrect, dstrect;

  srcrect.y = 1;
  dstrect.y = y;
  srcrect.h = dstrect.h = Font->Surface->h - 1;
  for (i = x + (x % charw); i <= x + w - charw; i += charw)
    {
      srcrect.w = dstrect.w =
	(Font->CharPos[charoffset + 2] + Font->CharPos[charoffset + 1]) / 2 -
	(Font->CharPos[charoffset] + Font->CharPos[charoffset - 1]) / 2;
      srcrect.x =
	(Font->CharPos[charoffset] + Font->CharPos[charoffset - 1]) / 2;
      dstrect.x =
	i - (float) (Font->CharPos[charoffset] -
		     Font->CharPos[charoffset - 1]) / 2;

      //JPB_PrintSurface(Font->Surface, &srcrect, &dstrect);
      JPB_PrintSurface (Font->Surface, &srcrect, &dstrect);
      j++;
    }
  return j;
}
Ejemplo n.º 7
0
    void ScriptWidget::showAutocompletion(const QStringList &list, const QString &prefix)
    {
        // do not show single autocompletion which is identical to existing prefix
        // or if it identical to prefix + '('.
        if (list.count() == 1) {
            if (list.at(0) == prefix ||
                list.at(0) == (prefix + "(")) {
                return;
            }
        }

        // update list of completions
        QStringListModel * model = static_cast<QStringListModel *>(_completer->model());
        model->setStringList(list);

        int currentLine = 0;
        int currentIndex = 0;
        _queryText->getCursorPosition(&currentLine, &currentIndex);
        int physicalLine = currentLine - _queryText->firstVisibleLine(); // "physical" line number in text editor (not logical)
        int lineIndexLeft = _currentAutoCompletionInfo.lineIndexLeft();

        QRect rect = _queryText->rect();
        rect.setWidth(550);
        rect.setHeight(editorHeight(physicalLine + 1));
        rect.moveLeft(charWidth() * lineIndexLeft + autocompletionBoxLeftPosition());

        _completer->complete(rect);
        _completer->popup()->setCurrentIndex(_completer->completionModel()->index(0, 0));
        _queryText->setIgnoreEnterKey(true);
        _queryText->setIgnoreTabKey(true);
    }
Ejemplo n.º 8
0
QSize FastoHexEdit::fullSize() const {
  const int charW = charWidth();
  const int charH = charHeight();

  const QRect rect = stableRect(viewport()->rect());
  const int yPosStart = rect.top();
  const int xPosStart = rect.left();
  const int yPosEnd = rect.bottom();
  const int xPosEnd = rect.right();

  const int wid = xPosEnd - xPosStart;
  const int widchars = wid - TextMarginXY * 2;
  const int xPosAscii = widchars/4 * 3;  // line pos

  int acharInLine = asciiCharInLine(widchars);

  int width = xPosAscii + (acharInLine * charW);
  int height = data_.size() / acharInLine;
  if (data_.size() % acharInLine) {
    height++;
  }

  height *= charH;

  return QSize(width, height);
}
Ejemplo n.º 9
0
bool Font::getLine(Common::String &s, int maxWidth, Common::String &line, int &width) {
	assert(maxWidth > 0);
	width = 0;
	const char *src = s.c_str();
	char c;

	while ((c = *src) != '\0') {
		if (c == '\r') {
			// End of line, so return calculated line
			line = Common::String(s.c_str(), src);
			s = Common::String(src + 1);
			return false;
		}

		++src;
		width += charWidth(c);
		if (width < maxWidth)
			continue;

		// Reached maximum allowed size
		// If this was the last character of the string, let it go
		if (*src == '\0') {
			line = Common::String(s.c_str(), src);
			s.clear();
			return true;
		}

		// Work backwards to find space at the start of the current word
		// as a point to split the line on
		while (src >= s.c_str() && *src != ' ') {
			width -= charWidth(*src);
			--src;
		}
		if (src < s.c_str())
			error("Could not fit line");

		// Split the line around the space
		line = Common::String(s.c_str(), src);
		s = Common::String(src + 1);
		return false;
	}

	// Return entire string
	line = s;
	s = Common::String();
	return true;
}
Ejemplo n.º 10
0
int Font::stringWidth(const Common::String &msg) {
	int total = 0;

	for (const char *c = msg.c_str(); *c != '\0'; ++c)
		total += charWidth(*c);

	return total;
}
Ejemplo n.º 11
0
void Foam::functionObjects::writeFile::writeTabbed
(
    Ostream& os,
    const string& str
) const
{
    os  << tab << setw(charWidth()) << str.c_str();
}
Ejemplo n.º 12
0
void EmacsBuffer::setColumn(mintcount_t col) {
    mintcount_t pos = getMarkPosition(MARK_BOL);
    mintcount_t cur_col = 0;
    while ((pos < _text.size()) && (cur_col < col) && (_text[pos] != EOLCHAR)) {
        cur_col += charWidth(cur_col, _text[pos++]);
    } // while
    _point = pos;
} // setColumn
Ejemplo n.º 13
0
bool doNewLine(string::const_iterator begin,
               string::const_iterator end,
               float cur_pos,
               float end_pos,
               float metrics, 
               bool last_row) {
  if (*begin=='\n')
    return true;
  if (*begin==' '&&!last_row) {
    cur_pos+=charWidth(*begin,metrics);
    *begin++;
    for (;begin!=end&&cur_pos<=end_pos&&!isspace(*begin);begin++) {
      cur_pos+=charWidth(*begin,metrics);
    }
    return cur_pos>end_pos;
  }
  return cur_pos+((begin+1!=end)?charWidth(*begin,metrics):0)>=end_pos;
}
Ejemplo n.º 14
0
void Foam::functionObjects::writeFile::writeHeader
(
    Ostream& os,
    const string& str
) const
{
    os  << setw(1) << "#" << setw(1) << ' '
        << setf(ios_base::left) << setw(charWidth() - 2) << str.c_str() << nl;
}
Ejemplo n.º 15
0
void Foam::functionObjects::writeFile::writeCommented
(
    Ostream& os,
    const string& str
) const
{
    os  << setw(1) << "#" << setw(1) << ' '
        << setw(charWidth() - 2) << str.c_str();
}
Ejemplo n.º 16
0
int MSTableColumn::charWidth(char aChar_) const
{
  int r=charWidth();
  if (_fontStruct->per_char!=0&&
      aChar_>=_fontStruct->min_char_or_byte2&&aChar_<=_fontStruct->max_char_or_byte2)
   {
     r=charStruct(aChar_)->width;
   }
  return r;
}
Ejemplo n.º 17
0
int
wordWidth (SFont_Font * Font, char *text, int i, int end)
{
  int width = 0;
  for (; i < end; i++)
    {
      width += charWidth (Font, text[i]);
    }
  return width;
}
Ejemplo n.º 18
0
int Fonts::stringWidth(const Common::String &str) {
	int width = 0;

	if (!_font)
		return 0;

	for (const char *c = str.c_str(); *c; ++c)
		width += charWidth(*c);

	return width;
}
Ejemplo n.º 19
0
float acBitmapFont::charWidth(unsigned char c) {
  // width of the horizontal space char takes up
  //if (c != ' ') {
  if (charExists(c)) {
    return (float)setWidth[win2mac[c]-33] / 64.0f;

  } else {
    //return charWidth('i')*1.2f;
    return charWidth('n'); // support monospace better
  }
}
Ejemplo n.º 20
0
void Foam::functionObjects::writeFile::writeHeaderValue
(
    Ostream& os,
    const string& property,
    const Type& value
) const
{
    os  << setw(1) << '#' << setw(1) << ' '
        << setf(ios_base::left) << setw(charWidth() - 2) << property.c_str()
        << setw(1) << ':' << setw(1) << ' ' << value << nl;
}
Ejemplo n.º 21
0
Text::Text(ObjectMan *pObjMan, ResMan *pResMan, bool czechVersion) {
	_objMan = pObjMan;
	_resMan = pResMan;
	_textCount = 0;
	_fontId = (czechVersion) ? CZECH_GAME_FONT : GAME_FONT;
	_font = (uint8*)_resMan->openFetchRes(_fontId);

	_joinWidth = charWidth( SPACE ) - 2 * OVERLAP;
	_charHeight = _resMan->getUint16(_resMan->fetchFrame(_font, 0)->height); // all chars have the same height
	for (int i = 0; i < MAX_TEXT_OBS; i++)
		_textBlocks[i] = NULL;
}
Ejemplo n.º 22
0
static int
text_font_width(char *string)
{
	int         n = 0, x = 0, t = 0;

	/* The following does not handle a tab or other weird junk */
	while (*string != '\0') {
		if (x > n)
			n = x;
		switch (*string) {
			case '\v':
			case '\f':
			case '\n':
				x = 0;
				t = 0;
				break;
			case '\b':
				/* we handle only char, ^H, char smartly, if
				 * we have something different, we use the
				 * (probably wrong) assumption that we have
				 * a monospaced font. */
				if (t) {
					t--;
					x -= char_width[(int) (' ')];
				}
				break;
			case '\t':
				x += char_width[(int) (' ')] * (8 - (t % 8));
				t = ((t + 8) / 8) * 8;
				break;
			case '\r':
				break;
			default:
				t++;
				/* handle char, ^H, char */
				if (is_char_back_char(string)) {
					x += char_back_char_width(string);
					string += 2;
				} else {
					x += charWidth(string);
				}
		}
		string++;
	}
	return n;
}
Ejemplo n.º 23
0
    int FastoHexEdit::positionAtPoint(const QPoint &point) const
    {
        const int px = point.x();
        const int py = point.y();
        const int charW = charWidth();
        const int charH = charHeight();

        const QRect rect = stableRect(viewport()->rect());
        const int yPosStart = rect.top();
        const int xPosStart = rect.left();
        const int yPosEnd = rect.bottom();
        const int xPosEnd = rect.right();

        const int wid = xPosEnd - xPosStart;
        const int widchars = wid - TextMarginXY * 2;
        const int xPosAscii = widchars/4 * 3; //line pos

        int acharInLine = asciiCharInLine(widchars);
        if(acharInLine < 0){
            acharInLine = 0;
        }

        if ((px >= xPosStart && px < xPosAscii) && (py >= yPosStart && py < yPosEnd)){
            int posx = (xPosStart + px) / charW;
            int div = posx / 3;
            int mod = posx % 3;

            int pos = 0; //symbol pos in data;
            if(mod == 0){
                pos = div * 2;
            }
            else{
                pos = (div * 2) + 1;
            }

            int firstLineIdx = verticalScrollBar()->value();
            int posy = (py - yPosStart) / charH;
            pos = pos + (firstLineIdx + posy) * acharInLine * 2;
            return pos;
        }

        return -1;
    }
Ejemplo n.º 24
0
static int
add_blanks(marqueestruct * mp)
{
	int width;

	if (mp->t < 251) {
		mp->modwords[mp->t] = ' ';
		mp->t++;
		mp->modwords[mp->t] = ' ';
		mp->t++;
		mp->modwords[mp->t] = '\0';
		(void) strcat(mp->modwords, "  ");
	}
	mp->x -= 2 * char_width[(int) (' ')];
	width = charWidth(mp->modwords);
	if (mp->x <= -width) {
		mp->x += width;
		(void) memcpy(mp->modwords, &(mp->modwords[1]), mp->nonblanks);
		mp->nonblanks--;
	}
	return (mp->nonblanks < 0);
}
Ejemplo n.º 25
0
uint16 Text::analyzeSentence(uint8 *text, uint16 maxWidth, LineInfo *line) {
	uint16 lineNo = 0;

	bool firstWord = true;
	while (*text) {
		uint16 wordWidth = 0;
		uint16 wordLength = 0;

		while ((*text != SPACE) && *text) {
			wordWidth += charWidth(*text) - OVERLAP;
			wordLength++;
			text++;
		}
		if (*text == SPACE)
			text++;

		wordWidth += OVERLAP; // no overlap on final letter of word!
		if ( firstWord )	{ // first word on first line, so no separating SPACE needed
			line[0].width = wordWidth;
			line[0].length = wordLength;
			firstWord = false;
		} else {
			// see how much extra space this word will need to fit on current line
			// (with a separating space character - also overlapped)
			uint16 spaceNeeded = _joinWidth + wordWidth;

			if (line[lineNo].width + spaceNeeded <= maxWidth ) {
				line[lineNo].width += spaceNeeded;
				line[lineNo].length += 1 + wordLength; // NB. space+word characters
			} else {	// put word (without separating SPACE) at start of next line
				lineNo++;
				assert( lineNo < MAX_LINES );
				line[lineNo].width = wordWidth;
				line[lineNo].length = wordLength;
			}
		}
	}
	return lineNo+1;	// return no of lines
}
Ejemplo n.º 26
0
void
draw_marquee(ModeInfo * mi)
{
	Display    *display = MI_DISPLAY(mi);
	Window      window = MI_WINDOW(mi);
	char       *space = (char *) "        ";
	unsigned char       *ch;
	marqueestruct *mp = &marquees[MI_SCREEN(mi)];

	if (marquees == NULL)
		return;
	mp = &marquees[MI_SCREEN(mi)];
	if (mp->gc == None && mode_font != None)
		return;

	MI_IS_DRAWN(mi) = True;
	ch = (unsigned char*) mp->words;
	if (isRibbon()) {
		ch = (unsigned char*) mp->words;
		switch (*ch) {
			case '\0':
				if (add_blanks(mp)) {
					init_marquee(mi);
					return;
				}
				break;
			case '\b':
			case '\r':
			case '\n':
			case '\t':
			case '\v':
			case '\f':
				add_letter(mp, " ");
				mp->words++;
				break;
			default:
				add_letter(mp, (char *) ch);
				mp->words++;
		}
		if (MI_NPIXELS(mi) > 2) {
			XSetForeground(display, mp->gc, MI_PIXEL(mi, mp->color));
			if (++mp->color == MI_NPIXELS(mi))
				mp->color = 0;
		} else
			XSetForeground(display, mp->gc, MI_WHITE_PIXEL(mi));
		(void) XDrawImageString(display, MI_WINDOW(mi), mp->gc,
			 mp->x, mp->y + mp->ascent, mp->modwords, mp->t + 2);
	} else {
		switch (*ch) {
			case '\0':
				if (++mp->time > 16)
					init_marquee(mi);
				return;
			case '\b':
				if (mp->t) {
					/* see note in text_font_width */
					mp->t--;
					mp->x -= char_width[(int) (' ')];
				}
				break;
			case '\v':
			case '\f':
			case '\n':
				mp->x = mp->startx;
				mp->t = 0;
				mp->y += mp->height;
				if (mp->y + mp->height > mp->win_height) {
					XCopyArea(display, window, window, mp->gc,
						  0, mp->height, mp->win_width, mp->y - mp->height, 0, 0);
					XSetForeground(display, mp->gc, MI_BLACK_PIXEL(mi));
					mp->y -= mp->height;
					XFillRectangle(display, window, mp->gc,
					0, mp->y, mp->win_width, mp->height);
				}
				break;
			case '\t':
				(void) XDrawString(display, window, mp->gc, mp->x, mp->y + mp->ascent,
						   space, 8 - (mp->t % 8));
				mp->x += char_width[(int) (' ')] * (8 - (mp->t % 8));
				mp->t = ((mp->t + 8) / 8) * 8;
				break;
			case '\r':
				break;
			default:
				if (MI_NPIXELS(mi) > 2) {
					XSetForeground(display, mp->gc, MI_PIXEL(mi, mp->color));
					if (++mp->color == MI_NPIXELS(mi))
						mp->color = 0;
				} else
					XSetForeground(display, mp->gc, MI_WHITE_PIXEL(mi));
				if (is_char_back_char((char *) ch)) {
					int         xmid = mp->x + (char_back_char_width((char *) ch) + 1) / 2;

					(void) XDrawString(display, window, mp->gc,
							   xmid - char_width[(int) (const char) *ch] / 2,
						mp->y + mp->ascent, (char *) ch, 1);
					(void) XDrawString(display, window, mp->gc,
							   xmid - char_width[(int) (const char) *(ch + 2)] / 2,
						mp->y + mp->ascent, (char *) ch + 2, 1);
					mp->x += char_back_char_width((char *) ch);
					mp->words += 2;
				} else {
					int mb = charBytes(*ch);

					(void) XDrawString(display, window, mp->gc,
						mp->x, mp->y + mp->ascent, (char *) ch, mb);
					mp->x += charWidth((char *) ch);
				}
				mp->t++;
		}
		mp->words += charBytes(*ch);
	}
}
Ejemplo n.º 27
0
int FastoHexEdit::asciiCharInLine(int wid) const {
  int res = wid / 4 / charWidth();
  return res;
}
Ejemplo n.º 28
0
int DMDFrame::drawChar(const int x, const int y, const char letter, DMDGraphicsMode mode, const uint8_t *font)
{
  if(!font)
    font = this->font;
  if(x >= (int)pixel_width() || y >= (int)pixel_height())
    return -1;

  struct FontHeader header;
  memcpy_P(&header, (void*)font, sizeof(FontHeader));

  char c = letter;
  if (c == ' ') {
    int charWide = charWidth(' ');
    this->drawFilledBox(x, y, x + charWide, y + header.height, mode);
    return charWide;
  }
  uint8_t width = 0;
  uint8_t bytes = (header.height + 7) / 8;
  uint16_t index = 0;

  if (c < header.firstChar || c >= (header.firstChar + header.charCount))
    return 0;
  c -= header.firstChar;

  if (header.size == 0) {
    // zero length is flag indicating fixed width font (array does not contain width data entries)
    width = header.fixedWidth;
    index = sizeof(FontHeader) + c * bytes * width;
  } else {
    // variable width font, read width data, to get the index
    for (uint8_t i = 0; i < c; i++) {
      index += pgm_read_byte(font + sizeof(FontHeader) + i);
    }
    index = index * bytes + header.charCount + sizeof(FontHeader);
    width = pgm_read_byte(font + sizeof(FontHeader) + c);
  }
  if (x < -width || y < -header.height)
    return width;
    
  bool inverse = false;
  if (mode == GRAPHICS_INVERSE) {
      inverse = true;
  }

  // last but not least, draw the character
  for (uint8_t j = 0; j < width; j++) { // Width
    for (uint8_t i = bytes - 1; i < 254; i--) { // Vertical Bytes
      uint8_t data = pgm_read_byte(font + index + j + (i * width));
      int offset = (i * 8);
      if ((i == bytes - 1) && bytes > 1) {
        offset = header.height - 8;
      }
      for (uint8_t k = 0; k < 8; k++) { // Vertical bits
        if ((offset+k >= i*8) && (offset+k <= header.height)) {
          if (data & (1 << k)) {
              if(inverse) {
                setPixel(x + j, y + offset + k, GRAPHICS_OFF);
              } else {
                setPixel(x + j, y + offset + k, GRAPHICS_NORMAL);
              }
          } else {
              if(inverse) {
                  setPixel(x + j, y + offset + k, GRAPHICS_NORMAL);
              } else {
                  setPixel(x + j, y + offset + k, GRAPHICS_OFF);
              }
          }
        }
      }
    }
  }
  return width;
}
Ejemplo n.º 29
0
void FastoHexEdit::paintEvent(QPaintEvent *event) {
  if (mode_ == HEX_MODE) {
    QPainter painter(viewport());

    QSize areaSize = viewport()->size();
    QSize widgetSize = fullSize();

    const int charW = charWidth();
    const int charH = charHeight();

    int firstLineIdx = verticalScrollBar()->value();
    int lastLineIdx = firstLineIdx + areaSize.height() / charH;

    const QRect rect = stableRect(event->rect());
    const int yPosStart = rect.top();
    const int xPosStart = rect.left();
    const int yPosEnd = rect.bottom();
    const int xPosEnd = rect.right();

    const int wid = xPosEnd - xPosStart;
    const int height = yPosEnd - yPosStart;
    const int widchars = wid - TextMarginXY * 2;
    const int acharInLine = asciiCharInLine(widchars);
    if (acharInLine <= 0) {
      return;
    }

    const int xPosAscii = widchars/4 * 3;  // line pos
    const int xPosAsciiStart = xPosAscii + TextMarginXY;

    int indexCount = data_.size() / acharInLine;
    if (lastLineIdx > indexCount) {
      lastLineIdx = indexCount;
      if (data_.size() % acharInLine) {
        lastLineIdx++;
      }
    }
    verticalScrollBar()->setPageStep(areaSize.height() / charH);
    verticalScrollBar()->setRange(0, (widgetSize.height() - areaSize.height()) / charH + 1);

    painter.setPen(Qt::gray);
    painter.drawLine(xPosAscii, yPosStart, xPosAscii, yPosEnd);

    painter.setPen(Qt::black);

    int size = data_.size();
    for (int lineIdx = firstLineIdx, yPos = yPosStart;
         lineIdx < lastLineIdx; lineIdx += 1, yPos += charH) {
      QByteArray part = data_.begin() + (lineIdx * acharInLine);
      int part_size = size / acharInLine ? acharInLine : size % acharInLine;
      part.resize(part_size);
      size -= part_size;
      QByteArray hex = part.toHex();

      painter.setBackgroundMode(Qt::OpaqueMode);
      for (int xPos = xPosStart, i = 0; i < hex.size(); i++, xPos += 3 * charW) {
        QString val = hex.mid(i * 2, 2);
        QRect hexrect(xPos, yPos, 3 * charW, charH);
        painter.drawText(hexrect, Qt::AlignLeft, val);
        char ch = part[i];
        if ((ch < 0x20) || (ch > 0x7e)) {
          part[i] = '.';
        }
      }

      painter.setBackgroundMode(Qt::TransparentMode);
      QRect asciirect(xPosAsciiStart, yPos, acharInLine * charW, charH);
      painter.drawText(asciirect, Qt::AlignLeft, part);
    }
  } else {
    base_class::paintEvent(event);
  }
}
Ejemplo n.º 30
0
uint16 FontRenderer::analyzeSentence(byte *sentence, uint16 maxWidth, uint32 fontRes, LineInfo *line) {
	// joinWidth = how much extra space is needed to append a word to a
	// line. NB. SPACE requires TWICE the '_charSpacing' to join a word
	// to line

	uint16 joinWidth = charWidth(SPACE, fontRes) + 2 * _charSpacing;

	uint16 lineNo = 0;
	uint16 pos = 0;
	bool firstWord = true;

	byte ch;

	do {
		uint16 wordWidth = 0;
		uint16 wordLength = 0;

		// Calculate the width of the word.

		ch = sentence[pos++];

		while (ch && ch != SPACE) {
			wordWidth += charWidth(ch, fontRes) + _charSpacing;
			wordLength++;
			ch = sentence[pos++];
		}

		// Don't include any character spacing at the end of the word.
		wordWidth -= _charSpacing;

		// 'ch' is now the SPACE or NULL following the word
		// 'pos' indexes to the position following 'ch'

		if (firstWord) {
			// This is the first word on the line, so no separating
			// space is needed.

			line[0].width = wordWidth;
			line[0].length = wordLength;
			firstWord = false;
		} else {
			// See how much extra space this word will need to
			// fit on current line (with a separating space
			// character - also overlapped)

			uint16 spaceNeeded = joinWidth + wordWidth;

			if (line[lineNo].width + spaceNeeded <= maxWidth) {
				// The word fits on this line.
				line[lineNo].width += spaceNeeded;
				line[lineNo].length += (1 + wordLength);
			} else {
				// The word spills over to the next line, i.e.
				// no separating space.

				lineNo++;

				assert(lineNo < MAX_LINES);

				line[lineNo].width = wordWidth;
				line[lineNo].length = wordLength;
			}
		}
	} while (ch);

	return lineNo + 1;
}