[Scummvm-git-logs] scummvm master -> 67ab2657d8a20b0defb52be24280e87fc58b68c2

sev- noreply at scummvm.org
Tue Oct 24 00:38:29 UTC 2023


This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
4e614216b2 GRAPHICS: MACGUI: Add equality test for MacFontRun
37deafe413 GRAPHICS: MACGUI: Moved more methods to MacTextCanvas
67ab2657d8 GRAPHICS: MACGUI: Split out MacTextCanvas into a separate file


Commit: 4e614216b26dffd551e56092fbcd489e73bfa366
    https://github.com/scummvm/scummvm/commit/4e614216b26dffd551e56092fbcd489e73bfa366
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2023-10-24T02:38:14+02:00

Commit Message:
GRAPHICS: MACGUI: Add equality test for MacFontRun

Changed paths:
    graphics/macgui/mactext.h


diff --git a/graphics/macgui/mactext.h b/graphics/macgui/mactext.h
index 96d647a0cdc..ae158e36132 100644
--- a/graphics/macgui/mactext.h
+++ b/graphics/macgui/mactext.h
@@ -115,6 +115,17 @@ struct MacFontRun {
 	Common::CodePage getEncoding();
 	bool plainByteMode();
 	Common::String getEncodedText();
+
+	bool equals(const MacFontRun *x, const MacFontRun *y) {
+		return (x->fontId    == y->fontId &&
+				x->textSlant == y->textSlant &&
+				x->fontSize  == y->fontSize &&
+				x->palinfo1  == y->palinfo1 &&
+				x->palinfo2  == y->palinfo2 &&
+				x->palinfo3  == y->palinfo3 &&
+				x->fgcolor   == y->fgcolor);
+	}
+
 };
 
 struct MacTextLine;


Commit: 37deafe413e7e7a984c76f9a67f20c9d3844e040
    https://github.com/scummvm/scummvm/commit/37deafe413e7e7a984c76f9a67f20c9d3844e040
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2023-10-24T02:38:17+02:00

Commit Message:
GRAPHICS: MACGUI: Moved more methods to MacTextCanvas

Changed paths:
    graphics/macgui/mactext.cpp
    graphics/macgui/mactext.h


diff --git a/graphics/macgui/mactext.cpp b/graphics/macgui/mactext.cpp
index 57adb0a5bc9..616db8bc6d3 100644
--- a/graphics/macgui/mactext.cpp
+++ b/graphics/macgui/mactext.cpp
@@ -338,7 +338,7 @@ void MacText::setMaxWidth(int maxWidth) {
 		return;
 	}
 
-	Common::U32String str = getTextChunk(0, 0, -1, -1, true, true);
+	Common::U32String str = _canvas.getTextChunk(0, 0, -1, -1, true, true);
 
 	// keep the cursor pos
 	int ppos = 0;
@@ -1807,7 +1807,7 @@ Common::U32String MacText::getSelection(bool formatted, bool newlines) {
 		SWAP(s.startCol, s.endCol);
 	}
 
-	return getTextChunk(s.startRow, s.startCol, s.endRow, s.endCol, formatted, newlines);
+	return _canvas.getTextChunk(s.startRow, s.startCol, s.endRow, s.endCol, formatted, newlines);
 }
 
 void MacText::clearSelection() {
@@ -1909,7 +1909,7 @@ bool MacText::isCutAllowed() {
 }
 
 Common::U32String MacText::getEditedString() {
-	return getTextChunk(_editableRow, 0, -1, -1);
+	return _canvas.getTextChunk(_editableRow, 0, -1, -1);
 }
 
 Common::U32String MacText::getPlainText() {
@@ -1934,7 +1934,7 @@ Common::U32String MacText::cutSelection() {
 		SWAP(s.startCol, s.endCol);
 	}
 
-	Common::U32String selection = MacText::getTextChunk(s.startRow, s.startCol, s.endRow, s.endCol, true, true);
+	Common::U32String selection = _canvas.getTextChunk(s.startRow, s.startCol, s.endRow, s.endCol, true, true);
 
 	deleteSelection();
 	clearSelection();
@@ -2452,39 +2452,43 @@ void MacText::getRowCol(int x, int y, int *sx, int *sy, int *row, int *col, int
 		*chunk_ = (int)chunk;
 }
 
+Common::U32String MacText::getTextChunk(int startRow, int startCol, int endRow, int endCol, bool formatted, bool newlines) {
+	return _canvas.getTextChunk(startRow, startCol, endRow, endCol, formatted, newlines);
+}
+
 // If adjacent chunks have same format, then skip the format definition
 // This happens when a long paragraph is split into several lines
 #define ADDFORMATTING()                                                                      \
 	if (formatted) {                                                                         \
-		formatting = Common::U32String(_canvas._text[i].chunks[chunk].toString()); \
+		formatting = Common::U32String(_text[i].chunks[chunk].toString()); \
 		if (formatting != prevformatting) {                                                  \
 			res += formatting;                                                               \
 			prevformatting = formatting;                                                     \
 		}                                                                                    \
 	}
 
-Common::U32String MacText::getTextChunk(int startRow, int startCol, int endRow, int endCol, bool formatted, bool newlines) {
+Common::U32String MacTextCanvas::getTextChunk(int startRow, int startCol, int endRow, int endCol, bool formatted, bool newlines) {
 	Common::U32String res("");
 
 	if (endRow == -1)
-		endRow = _canvas._text.size() - 1;
+		endRow = _text.size() - 1;
 
 	if (endCol == -1)
-		endCol = _canvas.getLineCharWidth(endRow);
-	if (_canvas._text.empty()) {
+		endCol = getLineCharWidth(endRow);
+	if (_text.empty()) {
 		return res;
 	}
 
-	startRow = CLIP(startRow, 0, (int)_canvas._text.size() - 1);
-	endRow = CLIP(endRow, 0, (int)_canvas._text.size() - 1);
+	startRow = CLIP(startRow, 0, (int)_text.size() - 1);
+	endRow = CLIP(endRow, 0, (int)_text.size() - 1);
 
 	Common::U32String formatting(""), prevformatting("");
 
 	for (int i = startRow; i <= endRow; i++) {
 		// We requested only part of one line
 		if (i == startRow && i == endRow) {
-			for (uint chunk = 0; chunk < _canvas._text[i].chunks.size(); chunk++) {
-				if (_canvas._text[i].chunks[chunk].text.empty()) {
+			for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
+				if (_text[i].chunks[chunk].text.empty()) {
 					// skip empty chunks, but keep them formatted,
 					// a text input box needs to keep the formatting even when all text is removed.
 					ADDFORMATTING();
@@ -2494,68 +2498,68 @@ Common::U32String MacText::getTextChunk(int startRow, int startCol, int endRow,
 				if (startCol <= 0) {
 					ADDFORMATTING();
 
-					if (endCol >= (int)_canvas._text[i].chunks[chunk].text.size())
-						res += _canvas._text[i].chunks[chunk].text;
+					if (endCol >= (int)_text[i].chunks[chunk].text.size())
+						res += _text[i].chunks[chunk].text;
 					else
-						res += _canvas._text[i].chunks[chunk].text.substr(0, endCol);
-				} else if ((int)_canvas._text[i].chunks[chunk].text.size() > startCol) {
+						res += _text[i].chunks[chunk].text.substr(0, endCol);
+				} else if ((int)_text[i].chunks[chunk].text.size() > startCol) {
 					ADDFORMATTING();
-					res += _canvas._text[i].chunks[chunk].text.substr(startCol, endCol - startCol);
+					res += _text[i].chunks[chunk].text.substr(startCol, endCol - startCol);
 				}
 
-				startCol -= _canvas._text[i].chunks[chunk].text.size();
-				endCol -= _canvas._text[i].chunks[chunk].text.size();
+				startCol -= _text[i].chunks[chunk].text.size();
+				endCol -= _text[i].chunks[chunk].text.size();
 
 				if (endCol <= 0)
 					break;
 			}
 		// We are at the top line and it is not completely requested
 		} else if (i == startRow && startCol != 0) {
-			for (uint chunk = 0; chunk < _canvas._text[i].chunks.size(); chunk++) {
-				if (_canvas._text[i].chunks[chunk].text.empty()) // skip empty chunks
+			for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
+				if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
 					continue;
 
 				if (startCol <= 0) {
 					ADDFORMATTING();
-					res += _canvas._text[i].chunks[chunk].text;
-				} else if ((int)_canvas._text[i].chunks[chunk].text.size() > startCol) {
+					res += _text[i].chunks[chunk].text;
+				} else if ((int)_text[i].chunks[chunk].text.size() > startCol) {
 					ADDFORMATTING();
-					res += _canvas._text[i].chunks[chunk].text.substr(startCol);
+					res += _text[i].chunks[chunk].text.substr(startCol);
 				}
 
-				startCol -= _canvas._text[i].chunks[chunk].text.size();
+				startCol -= _text[i].chunks[chunk].text.size();
 			}
-			if (newlines && _canvas._text[i].paragraphEnd)
+			if (newlines && _text[i].paragraphEnd)
 				res += '\n';
 		// We are at the end row, and it could be not completely requested
 		} else if (i == endRow) {
-			for (uint chunk = 0; chunk < _canvas._text[i].chunks.size(); chunk++) {
-				if (_canvas._text[i].chunks[chunk].text.empty()) // skip empty chunks
+			for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
+				if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
 					continue;
 
 				ADDFORMATTING();
 
-				if (endCol >= (int)_canvas._text[i].chunks[chunk].text.size())
-					res += _canvas._text[i].chunks[chunk].text;
+				if (endCol >= (int)_text[i].chunks[chunk].text.size())
+					res += _text[i].chunks[chunk].text;
 				else
-					res += _canvas._text[i].chunks[chunk].text.substr(0, endCol);
+					res += _text[i].chunks[chunk].text.substr(0, endCol);
 
-				endCol -= _canvas._text[i].chunks[chunk].text.size();
+				endCol -= _text[i].chunks[chunk].text.size();
 
 				if (endCol <= 0)
 					break;
 			}
 		// We are in the middle of requested range, pass whole line
 		} else {
-			for (uint chunk = 0; chunk < _canvas._text[i].chunks.size(); chunk++) {
-				if (_canvas._text[i].chunks[chunk].text.empty()) // skip empty chunks
+			for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
+				if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
 					continue;
 
 				ADDFORMATTING();
-				res += _canvas._text[i].chunks[chunk].text;
+				res += _text[i].chunks[chunk].text;
 			}
 
-			if (newlines && _canvas._text[i].paragraphEnd)
+			if (newlines && _text[i].paragraphEnd)
 				res += '\n';
 		}
 	}
@@ -2583,8 +2587,8 @@ void MacText::insertTextFromClipboard() {
 			ppos += _canvas.getLineCharWidth(i);
 		ppos += _cursorCol;
 
-		Common::U32String pre_str = getTextChunk(start, 0, _cursorRow, _cursorCol, true, true);
-		Common::U32String sub_str = getTextChunk(_cursorRow, _cursorCol, end, _canvas.getLineCharWidth(end, true), true, true);
+		Common::U32String pre_str = _canvas.getTextChunk(start, 0, _cursorRow, _cursorCol, true, true);
+		Common::U32String sub_str = _canvas.getTextChunk(_cursorRow, _cursorCol, end, _canvas.getLineCharWidth(end, true), true, true);
 
 		// Remove it from the text
 		for (int i = start; i <= end; i++) {
@@ -2655,7 +2659,7 @@ void MacText::insertChar(byte c, int *row, int *col) {
 	(*col)++;
 
 	if (_canvas.getLineWidth(*row) - oldw + chunkw > _canvas._maxWidth) { // Needs reshuffle
-		reshuffleParagraph(row, col);
+		_canvas.reshuffleParagraph(row, col, _defaultFormatting);
 		_fullRefresh = true;
 		recalcDims();
 		render();
@@ -2695,7 +2699,7 @@ void MacText::deleteSelection() {
 		deletePreviousCharInternal(&row, &col);
 	}
 
-	reshuffleParagraph(&row, &col);
+	_canvas.reshuffleParagraph(&row, &col, _defaultFormatting);
 
 	_fullRefresh = true;
 	recalcDims();
@@ -2755,7 +2759,7 @@ void MacText::deletePreviousChar(int *row, int *col) {
 	}
 	D(9, "**deleteChar cursor row %d col %d", _cursorRow, _cursorCol);
 
-	reshuffleParagraph(row, col);
+	_canvas.reshuffleParagraph(row, col, _defaultFormatting);
 
 	_fullRefresh = true;
 	recalcDims();
@@ -2799,7 +2803,7 @@ void MacText::addNewLine(int *row, int *col) {
 	(*row)++;
 	*col = 0;
 
-	reshuffleParagraph(row, col);
+	_canvas.reshuffleParagraph(row, col, _defaultFormatting);
 
 	for (int i = 0; i < (int)_canvas._text.size(); i++) {
 		D(9, "** addNewLine line %d", i);
@@ -2814,42 +2818,42 @@ void MacText::addNewLine(int *row, int *col) {
 	render();
 }
 
-void MacText::reshuffleParagraph(int *row, int *col) {
+void MacTextCanvas::reshuffleParagraph(int *row, int *col, MacFontRun &defaultFormatting) {
 	// First, we looking for the paragraph start and end
 	int start = *row, end = *row;
 
-	while (start && !_canvas._text[start - 1].paragraphEnd)
+	while (start && !_text[start - 1].paragraphEnd)
 		start--;
 
-	while (end < (int)_canvas._text.size() - 1 && !_canvas._text[end].paragraphEnd) // stop at last line
+	while (end < (int)_text.size() - 1 && !_text[end].paragraphEnd) // stop at last line
 		end++;
 
 	// Get character pos within paragraph
 	int ppos = 0;
 
 	for (int i = start; i < *row; i++)
-		ppos += _canvas.getLineCharWidth(i);
+		ppos += getLineCharWidth(i);
 
 	ppos += *col;
 
 	// Get whole paragraph
-	Common::U32String paragraph = getTextChunk(start, 0, end, _canvas.getLineCharWidth(end, true), true, true);
+	Common::U32String paragraph = getTextChunk(start, 0, end, getLineCharWidth(end, true), true, true);
 
 	// Remove it from the text
 	for (int i = start; i <= end; i++) {
-		_canvas._text.remove_at(start);
+		_text.remove_at(start);
 	}
 
 	// And now read it
 	D(9, "start %d end %d", start, end);
-	_canvas.splitString(paragraph, start, _defaultFormatting);
+	splitString(paragraph, start, defaultFormatting);
 
 	// Find new pos within paragraph after reshuffling
 	*row = start;
 
 	warning("FIXME, bad design");
-	while (ppos > _canvas.getLineCharWidth(*row, true)) {
-		ppos -= _canvas.getLineCharWidth(*row, true);
+	while (ppos > getLineCharWidth(*row, true)) {
+		ppos -= getLineCharWidth(*row, true);
 		(*row)++;
 	}
 	*col = ppos;
diff --git a/graphics/macgui/mactext.h b/graphics/macgui/mactext.h
index ae158e36132..69f1d940568 100644
--- a/graphics/macgui/mactext.h
+++ b/graphics/macgui/mactext.h
@@ -176,6 +176,14 @@ public:
 	const Common::U32String::value_type *splitString(const Common::U32String::value_type *s, int curLine, MacFontRun &defaultFormatting);
 
 	void chopChunk(const Common::U32String &str, int *curLinePtr, int indent, int maxWidth);
+	Common::U32String getTextChunk(int startRow, int startCol, int endRow, int endCol, bool formatted = false, bool newlines = true);
+
+	/**
+	 * Rewraps paragraph containing given text row.
+	 * When text is modified, we redo whole thing again without touching
+	 * other paragraphs. Also, cursor position is returned in the arguments
+	 */
+	void reshuffleParagraph(int *row, int *col, MacFontRun &defaultFormatting);
 
 	void processTable(int line, int maxWidth);
 };
@@ -381,13 +389,6 @@ private:
 	void init(uint32 fgcolor, uint32 bgcolor, int maxWidth, TextAlign textAlignment, int interlinear, uint16 textShadow, bool macFontMode);
 	bool isCutAllowed();
 
-	/**
-	 * Rewraps paragraph containing given text row.
-	 * When text is modified, we redo whole thing again without touching
-	 * other paragraphs. Also, cursor position is returned in the arguments
-	 */
-	void reshuffleParagraph(int *row, int *col);
-
 	void recalcDims();
 
 	void drawSelection(int xoff, int yoff);


Commit: 67ab2657d8a20b0defb52be24280e87fc58b68c2
    https://github.com/scummvm/scummvm/commit/67ab2657d8a20b0defb52be24280e87fc58b68c2
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2023-10-24T02:38:17+02:00

Commit Message:
GRAPHICS: MACGUI: Split out MacTextCanvas into a separate file

Changed paths:
  A graphics/macgui/mactext-canvas.cpp
  A graphics/macgui/mactext-canvas.h
    graphics/macgui/mactext.cpp
    graphics/macgui/mactext.h
    graphics/module.mk


diff --git a/graphics/macgui/mactext-canvas.cpp b/graphics/macgui/mactext-canvas.cpp
new file mode 100644
index 00000000000..77e94891ea2
--- /dev/null
+++ b/graphics/macgui/mactext-canvas.cpp
@@ -0,0 +1,1101 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/tokenizer.h"
+#include "common/unicode-bidi.h"
+
+#include "graphics/macgui/mactext.h"
+
+namespace Graphics {
+
+#define DEBUG 0
+
+#if DEBUG
+#define D(...)  debug(__VA_ARGS__)
+#else
+#define D(...)  ;
+#endif
+
+// Adds the given string to the end of the last line/chunk
+// while observing the _canvas._maxWidth and keeping this chunk's
+// formatting
+void MacTextCanvas::chopChunk(const Common::U32String &str, int *curLinePtr, int indent, int maxWidth) {
+	int curLine = *curLinePtr;
+	int curChunk;
+	MacFontRun *chunk;
+
+	curChunk = _text[curLine].chunks.size() - 1;
+	chunk = &_text[curLine].chunks[curChunk];
+
+	// Check if there is nothing to add, then remove the last chunk
+	// This happens when the previous run is finished only with
+	// empty formatting, or when we were adding text for the first time
+	if (chunk->text.empty() && str.empty()) {
+		D(9, "** chopChunk, replaced formatting, line %d", curLine);
+
+		_text[curLine].chunks.pop_back();
+
+		return;
+	}
+
+	if (maxWidth == -1) {
+		chunk->text += str;
+
+		return;
+	}
+
+	Common::Array<Common::U32String> text;
+
+	int w = getLineWidth(curLine, true);
+	D(9, "** chopChunk before wrap \"%s\"", Common::toPrintable(str.encode()).c_str());
+
+	chunk->getFont()->wordWrapText(str, maxWidth, text, w);
+
+	if (text.size() == 0) {
+		warning("chopChunk: too narrow width, >%d", maxWidth);
+		chunk->text += str;
+		getLineCharWidth(curLine, true);
+
+		return;
+	}
+
+	for (int i = 0; i < (int)text.size(); i++) {
+		D(9, "** chopChunk result %d \"%s\"", i, toPrintable(text[i].encode()).c_str());
+	}
+	chunk->text += text[0];
+
+	// Recalc dims
+	getLineWidth(curLine, true);
+
+	D(9, "** chopChunk, subchunk: \"%s\" (%d lines, maxW: %d)", toPrintable(text[0].encode()).c_str(), text.size(), maxWidth);
+
+	// We do not overlap, so we're done
+	if (text.size() == 1)
+		return;
+
+	// Now add rest of the chunks
+	MacFontRun newchunk = _text[curLine].chunks[curChunk];
+
+	for (uint i = 1; i < text.size(); i++) {
+		newchunk.text = text[i];
+
+		curLine++;
+		_text.insert_at(curLine, MacTextLine());
+		_text[curLine].chunks.push_back(newchunk);
+		_text[curLine].indent = indent;
+		_text[curLine].firstLineIndent = 0;
+
+		D(9, "** chopChunk, added line (firstIndent: %d): \"%s\"", _text[curLine].firstLineIndent, toPrintable(text[i].encode()).c_str());
+	}
+
+	*curLinePtr = curLine;
+}
+
+void MacTextCanvas::splitString(const Common::U32String &str, int curLine, MacFontRun &defaultFormatting) {
+	D(9, "** splitString(\"%s\", %d)", toPrintable(str.encode()).c_str(), curLine);
+
+	if (str.empty()) {
+		D(9, "** splitString, empty line");
+		return;
+	}
+
+	(void)splitString(str.c_str(), curLine, defaultFormatting);
+}
+
+const Common::U32String::value_type *MacTextCanvas::splitString(const Common::U32String::value_type *s, int curLine, MacFontRun &defaultFormatting) {
+	if (_text.empty()) {
+		_text.resize(1);
+		_text[0].chunks.push_back(defaultFormatting);
+		D(9, "** splitString, added default formatting");
+	} else {
+		D(9, "** splitString, continuing, %d lines", _text.size());
+	}
+
+	Common::U32String tmp;
+
+	if (curLine == -1 || curLine >= (int)_text.size())
+		curLine = _text.size() - 1;
+
+	int curChunk = _text[curLine].chunks.size() - 1;
+	MacFontRun chunk = _text[curLine].chunks[curChunk];
+	int indentSize = 0;
+	int firstLineIndent = 0;
+	bool inTable = false;
+
+	while (*s) {
+		firstLineIndent = 0;
+
+		tmp.clear();
+
+		MacTextLine *curTextLine = &_text[curLine];
+
+		while (*s) {
+			bool endOfLine = false;
+
+			// Scan till next font change or end of line
+			while (*s && *s != '\001') {
+				if (*s == '\r') {
+					s++;
+					if (*s == '\n')	// Skip whole '\r\n'
+						s++;
+
+					endOfLine = true;
+
+					break;
+				}
+
+				// deal with single \n
+				if (*s == '\n') {
+					s++;
+
+					endOfLine = true;
+					break;
+				}
+
+				tmp += *s;
+
+				s++;
+			}
+
+			if (*s == '\001')	// If it was \001, skip it
+				s++;
+
+			if (*s == '\001') { // \001\001 -> \001
+				tmp += *s++;
+
+				if (*s)	// Check we reached end of line
+					continue;
+			}
+
+			D(9, "** splitString, chunk: \"%s\"", Common::toPrintable(tmp.encode()).c_str());
+
+			// Okay, now we are either at the end of the line, or in the next
+			// chunk definition. That means, that we have to store the previous chunk
+			chopChunk(tmp, &curLine, indentSize, _maxWidth > 0 ? _maxWidth - indentSize : _maxWidth);
+
+			curTextLine = &_text[curLine];
+
+			firstLineIndent = curTextLine->firstLineIndent;
+
+			tmp.clear();
+
+			// If it is end of the line, we're done
+			if (!*s) {
+				D(9, "** splitString, end of line");
+
+				break;
+			}
+
+			// get format (sync with stripFormat() )
+			if (*s == '\016') {	// human-readable format
+				s++;
+
+				// First two digits is slant, third digit is Header number
+				switch (*s) {
+				case '+': { // \016+XXYZ  -- opening textSlant, H<Y>, indent<+Z>
+					uint16 textSlant, headSize, indent;
+					s++;
+
+					s = readHex(&textSlant, s, 2);
+
+					chunk.textSlant |= textSlant; // Setting the specified bit
+
+					s = readHex(&headSize, s, 1);
+					if (headSize >= 1 && headSize <= 6) { // set
+						const float sizes[] = { 1, 2.0f, 1.41f, 1.155f, 1.0f, .894f, .816f };
+						chunk.fontSize = defaultFormatting.fontSize * sizes[headSize];
+					}
+
+					s = readHex(&indent, s, 1);
+
+					if (s)
+						indentSize += indent * chunk.fontSize * 2;
+
+					D(9, "** splitString+: fontId: %d, textSlant: %d, fontSize: %d, indent: %d",
+							chunk.fontId, chunk.textSlant, chunk.fontSize,
+							indent);
+
+					break;
+					}
+				case '-': { // \016-XXYZ  -- closing textSlant, H<Y>, indent<+Z>
+					uint16 textSlant, headSize, indent;
+					s++;
+
+					s = readHex(&textSlant, s, 2);
+
+					chunk.textSlant &= ~textSlant; // Clearing the specified bit
+
+					s = readHex(&headSize, s, 1);
+					if (headSize == 0xf) // reset
+						chunk.fontSize = defaultFormatting.fontSize;
+
+					s = readHex(&indent, s, 1);
+
+					if (s)
+						indentSize -= indent * chunk.fontSize * 2;
+
+					D(9, "** splitString-: fontId: %d, textSlant: %d, fontSize: %d, indent: %d",
+							chunk.fontId, chunk.textSlant, chunk.fontSize,
+							indent);
+					break;
+					}
+
+				case '[': { // \016[RRGGBB  -- setting color
+					uint16 palinfo1, palinfo2, palinfo3;
+					s++;
+
+					s = readHex(&palinfo1, s, 4);
+					s = readHex(&palinfo2, s, 4);
+					s = readHex(&palinfo3, s, 4);
+
+					chunk.palinfo1 = palinfo1;
+					chunk.palinfo2 = palinfo2;
+					chunk.palinfo3 = palinfo3;
+					chunk.fgcolor  = _wm->findBestColor(palinfo1 & 0xff, palinfo2 & 0xff, palinfo3 & 0xff);
+
+					D(9, "** splitString[: %08x", chunk.fgcolor);
+					break;
+					}
+
+				case ']': { // \016]  -- setting default color
+					s++;
+
+					chunk.palinfo1 = defaultFormatting.palinfo1;
+					chunk.palinfo2 = defaultFormatting.palinfo2;
+					chunk.palinfo3 = defaultFormatting.palinfo3;
+					chunk.fgcolor  = defaultFormatting.fgcolor;
+
+					D(9, "** splitString]: %08x", chunk.fgcolor);
+					break;
+					}
+
+				case '*': { // \016*XXsssssss  -- negative indent, XX size, sssss is the string
+					s++;
+
+					uint16 len;
+
+					s = readHex(&len, s, 2);
+
+					Common::U32String bullet = Common::U32String(s, len);
+
+					s += len;
+
+					firstLineIndent = -chunk.getFont()->getStringWidth(bullet);
+
+					D(9, "** splitString*: %02x '%s' (%d)", len, bullet.encode().c_str(), firstLineIndent);
+					break;
+					}
+
+				case 'i': { // \016iXXNNnnnnAAaaaaTTttt -- image, XX% width,
+										//          NN, nnnn -- filename len and text
+										//          AA, aaaa -- alt len and text
+										//          TT, tttt -- text (tooltip) len and text
+					s++;
+
+					uint16 len;
+
+					s = readHex(&_text[curLine].picpercent, s, 2);
+					s = readHex(&len, s, 2);
+					_text[curLine].picfname = Common::U32String(s, len).encode();
+					s += len;
+
+					s = readHex(&len, s, 2);
+					_text[curLine].picalt = Common::U32String(s, len);
+					s += len;
+
+					s = readHex(&len, s, 2);
+					_text[curLine].pictitle = Common::U32String(s, len);
+					s += len;
+
+					D(9, "** splitString[i]: %d%% fname: '%s'  alt: '%s'  title: '%s'",
+						_text[curLine].picpercent,
+						_text[curLine].picfname.c_str(), _text[curLine].picalt.encode().c_str(),
+						_text[curLine].pictitle.encode().c_str());
+					break;
+					}
+
+				case 't': { // \016tXXXX -- switch to the requested font id
+					s++;
+
+					uint16 fontId;
+
+					s = readHex(&fontId, s, 4);
+
+					chunk.fontId = fontId == 0xffff ? defaultFormatting.fontId : fontId;
+
+					D(9, "** splitString[t]: fontId: %d", fontId);
+					break;
+					}
+
+				case 'l': { // \016lLLllll -- link len and text
+					s++;
+
+					uint16 len;
+
+					s = readHex(&len, s, 2);
+					chunk.link = Common::U32String(s, len);
+					s += len;
+
+					D(9, "** splitString[l]: link: %s", chunk.link.c_str());
+					break;
+					}
+
+				case 'T': { // \016T -- table
+					s++;
+
+					char cmd = *s++;
+
+					if (cmd == 'h') { // Header, beginning of the table
+						curTextLine->table = new Common::Array<MacTextTableRow>();
+						inTable = true;
+
+						D(9, "** splitString[table header]");
+					} else if (cmd == 'b') { // Body start
+						D(9, "** splitString[body start]");
+					} else if (cmd == 'B') { // Body end
+						inTable = false;
+
+						D(9, "** splitString[body end]");
+						processTable(curLine, _maxWidth);
+
+						continue;
+					} else if (cmd == 'r') { // Row
+						curTextLine->table->push_back(MacTextTableRow());
+						D(9, "** splitString[row]");
+					} else if (cmd == 'c') { // Cell start
+						uint16 align;
+						s = readHex(&align, s, 2);
+
+						curTextLine->table->back().cells.push_back(MacTextCanvas());
+
+						MacTextCanvas *cellCanvas = &curTextLine->table->back().cells.back();
+						cellCanvas->_textAlignment = (TextAlign)align;
+						cellCanvas->_wm = _wm;
+						cellCanvas->_macText = _macText;
+						cellCanvas->_maxWidth = -1;
+						cellCanvas->_macFontMode = _macFontMode;
+						cellCanvas->_tfgcolor = _tfgcolor;
+						cellCanvas->_tbgcolor = _tbgcolor;
+
+						D(9, "** splitString[cell start]: align: %d", align);
+
+						D(9, "** splitString[RECURSION start]");
+
+						s = cellCanvas->splitString(s, curLine, defaultFormatting);
+
+						D(9, "** splitString[RECURSION end]");
+					} else if (cmd == 'C') { // Cell end
+						D(9, "** splitString[cell end]");
+
+						return s;
+					} else {
+						error("MacText: Unknown table subcommand (%c)", cmd);
+					}
+					break;
+					}
+
+				default: {
+					uint16 fontId, textSlant, fontSize, palinfo1, palinfo2, palinfo3;
+
+					s = readHex(&fontId, s, 4);
+					s = readHex(&textSlant, s, 2);
+					s = readHex(&fontSize, s, 4);
+					s = readHex(&palinfo1, s, 4);
+					s = readHex(&palinfo2, s, 4);
+					s = readHex(&palinfo3, s, 4);
+
+					chunk.setValues(_wm, fontId, textSlant, fontSize, palinfo1, palinfo2, palinfo3);
+
+					D(9, "** splitString: fontId: %d, textSlant: %d, fontSize: %d, fg: %04x",
+							fontId, textSlant, fontSize, chunk.fgcolor);
+
+					// So far, we enforce single font here, though in the future, font size could be altered
+					if (!_macFontMode)
+						chunk.font = defaultFormatting.font;
+					}
+				}
+			}
+
+
+			curTextLine->indent = indentSize;
+			curTextLine->firstLineIndent = firstLineIndent;
+
+			// Push new formatting
+			curTextLine->chunks.push_back(chunk);
+
+			// If we reached end of paragraph, go to outer loop
+			if (endOfLine)
+				break;
+		}
+
+		// We avoid adding new lines while in table. Recursive cell rendering
+		// has this flag as false (obviously)
+		if (inTable)
+			continue;
+
+		// Add new line
+		D(9, "** splitString: new line");
+
+		curTextLine->paragraphEnd = true;
+		// if the chunks is empty, which means the line will not be rendered properly
+		// so we add a empty string here
+		if (curTextLine->chunks.empty()) {
+			curTextLine->chunks.push_back(defaultFormatting);
+		}
+
+		curLine++;
+		_text.insert_at(curLine, MacTextLine());
+		_text[curLine].chunks.push_back(chunk);
+
+		curTextLine = &_text[curLine];
+	}
+
+#if DEBUG
+	for (uint i = 0; i < _text.size(); i++) {
+		debugN(9, "** splitString: %2d ", i);
+
+		for (uint j = 0; j < _text[i].chunks.size(); j++)
+			debugN(9, "[%d] \"%s\"", _text[i].chunks[j].text.size(), Common::toPrintable(_text[i].chunks[j].text.encode()).c_str());
+
+		debugN(9, "\n");
+	}
+	debug(9, "** splitString: done");
+#endif
+
+	return s;
+}
+
+
+void MacTextCanvas::reallocSurface() {
+	// round to closest 10
+	//TODO: work out why this rounding doesn't correctly fill the entire width
+	//int requiredH = (_text.size() + (_text.size() * 10 + 9) / 10) * lineH
+
+	if (!_surface) {
+		_surface = new ManagedSurface(_maxWidth, _textMaxHeight, _wm->_pixelformat);
+
+		if (_textShadow)
+			_shadowSurface = new ManagedSurface(_maxWidth, _textMaxHeight, _wm->_pixelformat);
+
+		return;
+	}
+
+	if (_surface->w < _maxWidth || _surface->h < _textMaxHeight) {
+		// realloc surface and copy old content
+		ManagedSurface *n = new ManagedSurface(_maxWidth, _textMaxHeight, _wm->_pixelformat);
+		n->clear(_tbgcolor);
+		n->blitFrom(*_surface, Common::Point(0, 0));
+
+		delete _surface;
+		_surface = n;
+
+		// same as shadow surface
+		if (_textShadow) {
+			ManagedSurface *newShadowSurface = new ManagedSurface(_maxWidth, _textMaxHeight, _wm->_pixelformat);
+			newShadowSurface->clear(_tbgcolor);
+			newShadowSurface->blitFrom(*_shadowSurface, Common::Point(0, 0));
+
+			delete _shadowSurface;
+			_shadowSurface = newShadowSurface;
+		}
+	}
+}
+
+void MacTextCanvas::render(int from, int to, int shadow) {
+	int w = MIN(_maxWidth, _textMaxWidth);
+	ManagedSurface *surface = shadow ? _shadowSurface : _surface;
+
+	int myFrom = from, myTo = to + 1, delta = 1;
+
+	if (_wm->_language == Common::HE_ISR) {
+		myFrom = to;
+		myTo = from - 1;
+		delta = -1;
+	}
+
+	for (int i = myFrom; i != myTo; i += delta) {
+		if (!_text[i].picfname.empty()) {
+			const Surface *image = _macText->getImageSurface(_text[i].picfname);
+
+			int xOffset = (_text[i].width - _text[i].charwidth) / 2;
+			Common::Rect bbox(xOffset, _text[i].y, xOffset + _text[i].charwidth, _text[i].y + _text[i].height);
+
+			if (image) {
+				surface->blitFrom(image, Common::Rect(0, 0, image->w, image->h), bbox);
+
+				D(9, "MacTextCanvas::render: Image %d x %d bbox: %d, %d, %d, %d", image->w, image->h, bbox.left, bbox.top,
+						bbox.right, bbox.bottom);
+			}
+
+			continue;
+		}
+
+		if (_text[i].tableSurface) {
+			surface->blitFrom(*_text[i].tableSurface, Common::Point(0, _text[i].y));
+
+			D(9, "MacTextCanvas::render: Table %d x %d at: %d, %d", _text[i].tableSurface->w, _text[i].tableSurface->h, 0, _text[i].y);
+
+			continue;
+		}
+
+		int xOffset = getAlignOffset(i) + _text[i].indent + _text[i].firstLineIndent;
+		xOffset++;
+
+		int start = 0, end = _text[i].chunks.size();
+		if (_wm->_language == Common::HE_ISR) {
+			start = _text[i].chunks.size() - 1;
+			end = -1;
+		}
+
+		int maxAscentForRow = 0;
+		for (int j = start; j != end; j += delta) {
+			if (_text[i].chunks[j].font->getFontAscent() > maxAscentForRow)
+				maxAscentForRow = _text[i].chunks[j].font->getFontAscent();
+		}
+
+		// TODO: _canvas._textMaxWidth, when -1, was not rendering ANY text.
+		for (int j = start; j != end; j += delta) {
+			D(9, "MacTextCanvas::render: line %d[%d] h:%d at %d,%d (%s) fontid: %d fontsize: %d on %dx%d, fgcolor: %08x bgcolor: %08x",
+				  i, j, _text[i].height, xOffset, _text[i].y, _text[i].chunks[j].text.encode().c_str(),
+				  _text[i].chunks[j].fontId, _text[i].chunks[j].fontSize, _surface->w, _surface->h, _text[i].chunks[j].fgcolor, _tbgcolor);
+
+			if (_text[i].chunks[j].text.empty())
+				continue;
+
+			int yOffset = 0;
+			if (_text[i].chunks[j].font->getFontAscent() < maxAscentForRow) {
+				yOffset = maxAscentForRow - _text[i].chunks[j].font->getFontAscent();
+			}
+
+			if (_text[i].chunks[j].plainByteMode()) {
+				Common::String str = _text[i].chunks[j].getEncodedText();
+				_text[i].chunks[j].getFont()->drawString(surface, str, xOffset, _text[i].y + yOffset, w, shadow ? _wm->_colorBlack : _text[i].chunks[j].fgcolor, kTextAlignLeft, 0, true);
+				xOffset += _text[i].chunks[j].getFont()->getStringWidth(str);
+			} else {
+				if (_wm->_language == Common::HE_ISR)
+					_text[i].chunks[j].getFont()->drawString(surface, convertBiDiU32String(_text[i].chunks[j].text, Common::BIDI_PAR_RTL), xOffset, _text[i].y + yOffset, w, shadow ? _wm->_colorBlack : _text[i].chunks[j].fgcolor, kTextAlignLeft, 0, true);
+				else
+					_text[i].chunks[j].getFont()->drawString(surface, convertBiDiU32String(_text[i].chunks[j].text), xOffset, _text[i].y + yOffset, w, shadow ? _wm->_colorBlack : _text[i].chunks[j].fgcolor, kTextAlignLeft, 0, true);
+				xOffset += _text[i].chunks[j].getFont()->getStringWidth(_text[i].chunks[j].text);
+			}
+		}
+	}
+}
+
+void MacTextCanvas::render(int from, int to) {
+	if (_text.empty())
+		return;
+
+	reallocSurface();
+
+	from = MAX<int>(0, from);
+	to = MIN<int>(to, _text.size() - 1);
+
+	// Clear the screen
+	_surface->fillRect(Common::Rect(0, _text[from].y, _surface->w, _text[to].y + getLineHeight(to)), _tbgcolor);
+
+	// render the shadow surface;
+	if (_textShadow)
+		render(from, to, _textShadow);
+
+	render(from, to, 0);
+
+	for (uint i = 0; i < _text.size(); i++) {
+		debugN(9, "MacTextCanvas::render: %2d (firstInd: %d indent: %d) ", i, _text[i].firstLineIndent, _text[i].indent);
+
+		for (uint j = 0; j < _text[i].chunks.size(); j++)
+			debugN(9, "[%d (%d)] \"%s\" ", _text[i].chunks[j].fontId, _text[i].chunks[j].textSlant, _text[i].chunks[j].text.encode().c_str());
+
+		debug(9, "%s", "");
+	}
+}
+
+int getStringMaxWordWidth(MacFontRun &format, const Common::U32String &str) {
+	if (format.plainByteMode()) {
+		Common::StringTokenizer tok(Common::convertFromU32String(str, format.getEncoding()));
+		int maxW = 0;
+
+		while (!tok.empty()) {
+			int w = format.getFont()->getStringWidth(tok.nextToken());
+
+			maxW = MAX(maxW, w);
+		}
+
+		return maxW;
+	} else {
+		Common::U32StringTokenizer tok(str);
+		int maxW = 0;
+
+		while (!tok.empty()) {
+			int w = format.getFont()->getStringWidth(tok.nextToken());
+
+			maxW = MAX(maxW, w);
+		}
+
+		return maxW;
+	}
+}
+
+int MacTextCanvas::getLineWidth(int lineNum, bool enforce, int col) {
+	if ((uint)lineNum >= _text.size())
+		return 0;
+
+	MacTextLine *line = &_text[lineNum];
+
+	if (line->width != -1 && !enforce && col == -1)
+		return line->width;
+
+	if (!line->picfname.empty()) {
+		const Surface *image = _macText->getImageSurface(line->picfname);
+
+		if (image) {
+			float ratio = _maxWidth * line->picpercent / 100.0 / (float)image->w;
+			line->width = _maxWidth;
+			line->height = image->h * ratio;
+			line->charwidth = image->w * ratio;
+		} else {
+			line->width = _maxWidth;
+			line->height = 1;
+			line->charwidth = 1;
+		}
+
+		return line->width;
+	}
+
+	if (line->table) {
+		line->width = _maxWidth;
+		line->height = line->tableSurface->h;
+		line->charwidth = _maxWidth;
+
+		return line->width;
+	}
+
+	int width = line->indent + line->firstLineIndent;
+	int height = 0;
+	int charwidth = 0;
+	int minWidth = 0;
+	bool firstWord = true;
+
+	for (uint i = 0; i < line->chunks.size(); i++) {
+		if (enforce && _macFontMode)
+			line->chunks[i].font = nullptr;
+
+		if (col >= 0) {
+			if (col >= (int)line->chunks[i].text.size()) {
+				col -= line->chunks[i].text.size();
+			} else {
+				Common::U32String tmp = line->chunks[i].text.substr(0, col);
+
+				width += getStringWidth(line->chunks[i], tmp);
+
+				return width;
+			}
+		}
+
+		if (!line->chunks[i].text.empty()) {
+			int w = getStringWidth(line->chunks[i], line->chunks[i].text);
+			int mW = getStringMaxWordWidth(line->chunks[i], line->chunks[i].text);
+
+			if (firstWord) {
+				minWidth = mW + width; // Take indent into account
+				firstWord = false;
+			} else {
+				minWidth = MAX(minWidth, mW);
+			}
+			width += w;
+			charwidth += line->chunks[i].text.size();
+		}
+
+		height = MAX(height, line->chunks[i].getFont()->getFontHeight());
+	}
+
+
+	line->width = width;
+	line->minWidth = minWidth;
+	line->height = height;
+	line->charwidth = charwidth;
+
+	return width;
+}
+
+int MacTextCanvas::getLineCharWidth(int line, bool enforce) {
+	if ((uint)line >= _text.size())
+		return 0;
+
+	if (_text[line].charwidth != -1 && !enforce)
+		return _text[line].charwidth;
+
+	int width = 0;
+
+	for (uint i = 0; i < _text[line].chunks.size(); i++) {
+		if (!_text[line].chunks[i].text.empty())
+			width += _text[line].chunks[i].text.size();
+	}
+
+	_text[line].charwidth = width;
+
+	return width;
+}
+
+int MacTextCanvas::getLineHeight(int line) {
+	if ((uint)line >= _text.size())
+		return 0;
+
+	(void)getLineWidth(line); // This calculates height also
+
+	return _text[line].height;
+}
+
+void MacTextCanvas::recalcDims() {
+	if (_text.empty())
+		return;
+
+	int y = 0;
+	_textMaxWidth = 0;
+
+	for (uint i = 0; i < _text.size(); i++) {
+		_text[i].y = y;
+
+		// We must calculate width first, because it enforces
+		// the computation. Calling Height() will return cached value!
+		_textMaxWidth = MAX(_textMaxWidth, getLineWidth(i, true));
+		y += MAX(getLineHeight(i), _interLinear);
+	}
+
+	_textMaxHeight = y;
+}
+
+int MacTextCanvas::getAlignOffset(int row) {
+	int alignOffset = 0;
+	if (_textAlignment == kTextAlignRight)
+		alignOffset = MAX<int>(0, _maxWidth - getLineWidth(row) - 1);
+	else if (_textAlignment == kTextAlignCenter)
+		alignOffset = (_maxWidth / 2) - (getLineWidth(row) / 2);
+	return alignOffset;
+}
+
+// If adjacent chunks have same format, then skip the format definition
+// This happens when a long paragraph is split into several lines
+#define ADDFORMATTING()                                                                      \
+	if (formatted) {                                                                         \
+		formatting = Common::U32String(_text[i].chunks[chunk].toString()); \
+		if (formatting != prevformatting) {                                                  \
+			res += formatting;                                                               \
+			prevformatting = formatting;                                                     \
+		}                                                                                    \
+	}
+
+Common::U32String MacTextCanvas::getTextChunk(int startRow, int startCol, int endRow, int endCol, bool formatted, bool newlines) {
+	Common::U32String res("");
+
+	if (endRow == -1)
+		endRow = _text.size() - 1;
+
+	if (endCol == -1)
+		endCol = getLineCharWidth(endRow);
+	if (_text.empty()) {
+		return res;
+	}
+
+	startRow = CLIP(startRow, 0, (int)_text.size() - 1);
+	endRow = CLIP(endRow, 0, (int)_text.size() - 1);
+
+	Common::U32String formatting(""), prevformatting("");
+
+	for (int i = startRow; i <= endRow; i++) {
+		// We requested only part of one line
+		if (i == startRow && i == endRow) {
+			for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
+				if (_text[i].chunks[chunk].text.empty()) {
+					// skip empty chunks, but keep them formatted,
+					// a text input box needs to keep the formatting even when all text is removed.
+					ADDFORMATTING();
+					continue;
+				}
+
+				if (startCol <= 0) {
+					ADDFORMATTING();
+
+					if (endCol >= (int)_text[i].chunks[chunk].text.size())
+						res += _text[i].chunks[chunk].text;
+					else
+						res += _text[i].chunks[chunk].text.substr(0, endCol);
+				} else if ((int)_text[i].chunks[chunk].text.size() > startCol) {
+					ADDFORMATTING();
+					res += _text[i].chunks[chunk].text.substr(startCol, endCol - startCol);
+				}
+
+				startCol -= _text[i].chunks[chunk].text.size();
+				endCol -= _text[i].chunks[chunk].text.size();
+
+				if (endCol <= 0)
+					break;
+			}
+		// We are at the top line and it is not completely requested
+		} else if (i == startRow && startCol != 0) {
+			for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
+				if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
+					continue;
+
+				if (startCol <= 0) {
+					ADDFORMATTING();
+					res += _text[i].chunks[chunk].text;
+				} else if ((int)_text[i].chunks[chunk].text.size() > startCol) {
+					ADDFORMATTING();
+					res += _text[i].chunks[chunk].text.substr(startCol);
+				}
+
+				startCol -= _text[i].chunks[chunk].text.size();
+			}
+			if (newlines && _text[i].paragraphEnd)
+				res += '\n';
+		// We are at the end row, and it could be not completely requested
+		} else if (i == endRow) {
+			for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
+				if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
+					continue;
+
+				ADDFORMATTING();
+
+				if (endCol >= (int)_text[i].chunks[chunk].text.size())
+					res += _text[i].chunks[chunk].text;
+				else
+					res += _text[i].chunks[chunk].text.substr(0, endCol);
+
+				endCol -= _text[i].chunks[chunk].text.size();
+
+				if (endCol <= 0)
+					break;
+			}
+		// We are in the middle of requested range, pass whole line
+		} else {
+			for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
+				if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
+					continue;
+
+				ADDFORMATTING();
+				res += _text[i].chunks[chunk].text;
+			}
+
+			if (newlines && _text[i].paragraphEnd)
+				res += '\n';
+		}
+	}
+
+	return res;
+}
+
+void MacTextCanvas::reshuffleParagraph(int *row, int *col, MacFontRun &defaultFormatting) {
+	// First, we looking for the paragraph start and end
+	int start = *row, end = *row;
+
+	while (start && !_text[start - 1].paragraphEnd)
+		start--;
+
+	while (end < (int)_text.size() - 1 && !_text[end].paragraphEnd) // stop at last line
+		end++;
+
+	// Get character pos within paragraph
+	int ppos = 0;
+
+	for (int i = start; i < *row; i++)
+		ppos += getLineCharWidth(i);
+
+	ppos += *col;
+
+	// Get whole paragraph
+	Common::U32String paragraph = getTextChunk(start, 0, end, getLineCharWidth(end, true), true, true);
+
+	// Remove it from the text
+	for (int i = start; i <= end; i++) {
+		_text.remove_at(start);
+	}
+
+	// And now read it
+	D(9, "start %d end %d", start, end);
+	splitString(paragraph, start, defaultFormatting);
+
+	// Find new pos within paragraph after reshuffling
+	*row = start;
+
+	warning("FIXME, bad design");
+	while (ppos > getLineCharWidth(*row, true)) {
+		ppos -= getLineCharWidth(*row, true);
+		(*row)++;
+	}
+	*col = ppos;
+}
+
+void MacTextCanvas::processTable(int line, int maxWidth) {
+	Common::Array<MacTextTableRow> *table = _text[line].table;
+	uint numCols = table->front().cells.size();
+	uint numRows = table->size();
+	Common::Array<int> maxW(numCols), maxL(numCols), colW(numCols), rowH(numRows);
+	Common::Array<bool> flex(numCols), wrap(numCols);
+
+	int width = maxWidth * 0.9;
+	int gutter = 10;
+
+	// Compute column widths, both minimal and maximal
+	for (auto &row : *table) {
+		int i = 0;
+		for (auto &cell : row.cells) {
+			int cW = 0, cL = 0;
+			for (uint l = 0; l < cell._text.size(); l++) {
+				(void)cell.getLineWidth(l); // calculate it
+
+				cW = MAX(cW, cell._text[l].width);
+				cL = MAX(cL, cell._text[l].minWidth);
+			}
+
+			maxW[i] = MAX(maxW[i], cW);
+			maxL[i] = MAX(maxL[i], cL);
+
+			i++;
+		}
+	}
+
+	for (uint i = 0; i < numCols; i++) {
+		warning("%d: %d - %d", i, maxL[i], maxW[i]);
+
+		wrap[i] = (maxW[i] != maxL[i]);
+	}
+
+	int left = width - (numCols - 1) * gutter;
+	int avg = left / numCols;
+	int nflex = 0;
+
+	// determine whether columns should be flexible and assign
+	// width of non-flexible cells
+	for (uint i = 0; i < numCols; i++) {
+		flex[i] = (maxW[i] > 2 * avg);
+		if (flex[i]) {
+			nflex++;
+		} else {
+			colW[i] = maxW[i];
+			left -= colW[i];
+		}
+	}
+
+	// if there is not enough space, make columns that could
+	// be word-wrapped flexible, too
+	if (left < nflex * avg) {
+		for (uint i = 0; i < numCols; i++) {
+			if (!flex[i] && wrap[i]) {
+				left += colW[i];
+				colW[i] = 0;
+				flex[i] = true;
+				nflex += 1;
+			}
+		}
+	}
+
+	// Calculate weights for flexible columns. The max width
+	// is capped at the page width to treat columns that have to
+	// be wrapped more or less equal
+	int tot = 0;
+	for (uint i = 0; i < numCols; i++) {
+		if (flex[i]) {
+			maxW[i] = MIN(maxW[i], width);
+			tot += maxW[i];
+		}
+	}
+
+	// Now assign the actual width for flexible columns. Make
+	// sure that it is at least as long as the longest word length
+	for (uint i = 0; i < numCols; i++) {
+		if (flex[i]) {
+			colW[i] = left * maxW[i] / tot;
+			colW[i] = MAX(colW[i], maxL[i]);
+			left -= colW[i];
+		}
+	}
+
+	for (uint i = 0; i < numCols; i++) {
+		warning("%d: %d", i, colW[i]);
+	}
+
+	int r = 0;
+	for (auto &row : *table) {
+		int c = 0;
+		rowH[r] = 0;
+		for (auto &cell : row.cells) {
+			cell._maxWidth = colW[c];
+
+			cell.recalcDims();
+			cell.reallocSurface();
+			cell._surface->clear(_tbgcolor);
+			cell.render(0, cell._text.size());
+
+			rowH[r] = MAX(rowH[r], cell._textMaxHeight);
+
+			c++;
+		}
+
+		r++;
+	}
+
+	int tW = 1, tH = 1;
+	for (uint i = 0; i < table->size(); i++)
+		tH += rowH[i] + gutter * 2 + 1;
+
+	for (uint i = 0; i < table->front().cells.size(); i++)
+		tW += colW[i] + gutter * 2 + 1;
+
+	ManagedSurface *surf = new ManagedSurface(tW, tH, _wm->_pixelformat);
+	_text[line].tableSurface = surf;
+	_text[line].height = tH;
+	_text[line].width = tW;
+	surf->clear(_tbgcolor);
+
+	surf->hLine(0, 0, tW, _tfgcolor);
+	surf->vLine(0, 0, tH, _tfgcolor);
+
+	int y = 1;
+	for (uint i = 0; i < table->size(); i++) {
+		y += gutter * 2 + rowH[i];
+		surf->hLine(0, y, tW, _tfgcolor);
+		y++;
+	}
+
+	int x = 1;
+	for (uint i = 0; i < table->front().cells.size(); i++) {
+		x += gutter * 2 + colW[i];
+		surf->vLine(x, 0, tH, _tfgcolor);
+		x++;
+	}
+
+	r = 0;
+	y = 1 + gutter;
+	for (auto &row : *table) {
+		int c = 0;
+		x = 1 + gutter;
+		for (auto &cell : row.cells) {
+			surf->blitFrom(*cell._surface, Common::Point(x, y));
+			x += gutter * 2 + 1 + colW[c];
+			c++;
+		}
+		y += gutter * 2 + 1 + rowH[r];
+		r++;
+	}
+}
+
+} // End of namespace Graphics
diff --git a/graphics/macgui/mactext-canvas.h b/graphics/macgui/mactext-canvas.h
new file mode 100644
index 00000000000..89dfdfd727e
--- /dev/null
+++ b/graphics/macgui/mactext-canvas.h
@@ -0,0 +1,215 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GRAPHICS_MACGUI_MACTEXTCANVAS_H
+#define GRAPHICS_MACGUI_MACTEXTCANVAS_H
+
+#include "graphics/macgui/macwindowmanager.h"
+
+namespace Graphics {
+
+class MacText;
+
+struct MacFontRun {
+	Common::U32String text;
+
+	uint16 fontId;
+	byte textSlant;
+	uint16 fontSize;
+	uint16 palinfo1;
+	uint16 palinfo2;
+	uint16 palinfo3;
+	uint32 fgcolor;
+	// to determine whether the next word is part of this one
+	bool wordContinuation;
+	const Font *font;
+	MacWindowManager *wm;
+	Common::String link;  // Substitute to return when hover or click
+
+	MacFontRun() {
+		wm = nullptr;
+		fontId = textSlant = fontSize = 0;
+		palinfo1 = palinfo2 = palinfo3 = 0;
+		fgcolor = 0;
+		font = nullptr;
+		wordContinuation = false;
+	}
+
+	MacFontRun(MacWindowManager *wm_) {
+		wm = wm_;
+		fontId = textSlant = fontSize = 0;
+		palinfo1 = palinfo2 = palinfo3 = 0;
+		fgcolor = 0;
+		font = nullptr;
+		wordContinuation = false;
+	}
+
+	MacFontRun(MacWindowManager *wm_, uint16 fontId_, byte textSlant_, uint16 fontSize_,
+			uint16 palinfo1_, uint16 palinfo2_, uint16 palinfo3_) {
+		setValues(wm_, fontId_, textSlant_, fontSize_, palinfo1_, palinfo2_, palinfo3_);
+		wordContinuation = false;
+	}
+
+	MacFontRun(MacWindowManager *wm_, const Font *font_, byte textSlant_, uint16 fontSize_,
+			uint16 palinfo1_, uint16 palinfo2_, uint16 palinfo3_) {
+		setValues(wm_, 0, textSlant_, fontSize_, palinfo1_, palinfo2_, palinfo3_);
+		font = font_;
+		wordContinuation = false;
+	}
+
+	void setValues(MacWindowManager *wm_, uint16 fontId_, byte textSlant_, uint16 fontSize_,
+			uint16 palinfo1_, uint16 palinfo2_, uint16 palinfo3_) {
+		wm        = wm_;
+		fontId    = fontId_;
+		textSlant = textSlant_;
+		fontSize  = fontSize_;
+		palinfo1  = palinfo1_;
+		palinfo2  = palinfo2_;
+		palinfo3  = palinfo3_;
+		fgcolor   = wm_->findBestColor(palinfo1_ & 0xff, palinfo2_ & 0xff, palinfo3_ & 0xff);
+		font      = nullptr;
+	}
+
+	const Font *getFont();
+
+	const Common::String toString();
+	bool equals(MacFontRun &to);
+
+	Common::CodePage getEncoding();
+	bool plainByteMode();
+	Common::String getEncodedText();
+
+	bool equals(const MacFontRun *x, const MacFontRun *y) {
+		return (x->fontId    == y->fontId &&
+				x->textSlant == y->textSlant &&
+				x->fontSize  == y->fontSize &&
+				x->palinfo1  == y->palinfo1 &&
+				x->palinfo2  == y->palinfo2 &&
+				x->palinfo3  == y->palinfo3 &&
+				x->fgcolor   == y->fgcolor);
+	}
+
+};
+
+struct MacTextLine;
+
+class MacTextCanvas {
+public:
+	Common::Array<MacTextLine> _text;
+	ManagedSurface *_surface = nullptr, *_shadowSurface = nullptr;
+	int _maxWidth = 0;
+	int _textMaxWidth = 0;
+	int _textMaxHeight = 0;
+	TextAlign _textAlignment = kTextAlignLeft;
+	int _interLinear = 0;
+	int _textShadow = 0;
+	MacWindowManager *_wm = nullptr;
+	uint32 _tfgcolor = 0;
+	uint32 _tbgcolor = 0;
+	bool _macFontMode = true;
+	MacText *_macText;
+
+public:
+	~MacTextCanvas() {
+		delete _surface;
+		delete _shadowSurface;
+	}
+
+	void recalcDims();
+	void reallocSurface();
+	void render(int from, int to);
+	void render(int from, int to, int shadow);
+	int getAlignOffset(int row);
+
+	/**
+	 * Returns line width in pixels. This takes into account chunks.
+	 * The result is cached for faster subsequent calls.
+	 *
+	 * @param line Line number
+	 * @param enforce Flag for indicating skipping the cache and computing the width,
+	 *                must be called when text gets changed
+	 * @param col Compute line width up to specified column, including this column
+	 * @return line width in pixels, or 0 for non-existent lines
+	 */
+	int getLineWidth(int line, bool enforce = false, int col = -1);
+	int getLineHeight(int line);
+	int getLineCharWidth(int line, bool enforce = false);
+
+	void splitString(const Common::U32String &str, int curLine, MacFontRun &defaultFormatting);
+	const Common::U32String::value_type *splitString(const Common::U32String::value_type *s, int curLine, MacFontRun &defaultFormatting);
+
+	void chopChunk(const Common::U32String &str, int *curLinePtr, int indent, int maxWidth);
+	Common::U32String getTextChunk(int startRow, int startCol, int endRow, int endCol, bool formatted = false, bool newlines = true);
+
+	/**
+	 * Rewraps paragraph containing given text row.
+	 * When text is modified, we redo whole thing again without touching
+	 * other paragraphs. Also, cursor position is returned in the arguments
+	 */
+	void reshuffleParagraph(int *row, int *col, MacFontRun &defaultFormatting);
+
+	void processTable(int line, int maxWidth);
+};
+
+struct MacTextTableRow {
+	Common::Array<MacTextCanvas> cells;
+	int heght = -1;
+};
+
+struct MacTextLine {
+	int width = -1;
+	int height = -1;
+	int minWidth = -1;
+	int y = 0;
+	int charwidth = -1;
+	bool paragraphEnd = false;
+	int indent = 0; // in units
+	int firstLineIndent = 0; // in pixels
+	Common::String picfname;
+	Common::U32String picalt, pictitle;
+	uint16 picpercent = 50;
+	Common::Array<MacTextTableRow> *table = nullptr;
+	ManagedSurface *tableSurface = nullptr;
+
+	Common::Array<MacFontRun> chunks;
+
+	MacFontRun &firstChunk() { return chunks[0]; }
+	MacFontRun &lastChunk() { return chunks[chunks.size() - 1]; }
+
+	/**
+	 * Search for a chunk at given char column.
+	 *
+	 * @param col Requested column, gets modified with in-chunk column
+	 * @returns Chunk number
+	 *
+	 * @note If requested column is too big, returns last character in the line
+	 */
+	uint getChunkNum(int *col);
+
+	~MacTextLine() {
+		delete table;
+		delete tableSurface;
+	}
+};
+
+} // End of namespace Graphics
+
+#endif
diff --git a/graphics/macgui/mactext.cpp b/graphics/macgui/mactext.cpp
index 616db8bc6d3..b04a1ab15db 100644
--- a/graphics/macgui/mactext.cpp
+++ b/graphics/macgui/mactext.cpp
@@ -21,17 +21,9 @@
 
 #include "common/file.h"
 #include "common/timer.h"
-#include "common/tokenizer.h"
-#include "common/unicode-bidi.h"
 #include "common/compression/unzip.h"
 
-#include "graphics/font.h"
 #include "graphics/macgui/mactext.h"
-#include "graphics/macgui/macwindowmanager.h"
-#include "graphics/macgui/macfontmanager.h"
-#include "graphics/macgui/macmenu.h"
-#include "graphics/macgui/macwidget.h"
-#include "graphics/macgui/macwindow.h"
 
 #ifdef USE_PNG
 #include "image/png.h"
@@ -302,33 +294,6 @@ int getStringWidth(MacFontRun &format, const Common::U32String &str) {
 		return format.getFont()->getStringWidth(str);
 }
 
-int getStringMaxWordWidth(MacFontRun &format, const Common::U32String &str) {
-	if (format.plainByteMode()) {
-		Common::StringTokenizer tok(Common::convertFromU32String(str, format.getEncoding()));
-		int maxW = 0;
-
-		while (!tok.empty()) {
-			int w = format.getFont()->getStringWidth(tok.nextToken());
-
-			maxW = MAX(maxW, w);
-		}
-
-		return maxW;
-	} else {
-		Common::U32StringTokenizer tok(str);
-		int maxW = 0;
-
-		while (!tok.empty()) {
-			int w = format.getFont()->getStringWidth(tok.nextToken());
-
-			maxW = MAX(maxW, w);
-		}
-
-		return maxW;
-	}
-}
-
-
 void MacText::setMaxWidth(int maxWidth) {
 	if (maxWidth == _canvas._maxWidth)
 		return;
@@ -587,491 +552,6 @@ void MacText::setDefaultFormatting(uint16 fontId, byte textSlant, uint16 fontSiz
 	_defaultFormatting.font = _wm->_fontMan->getFont(macFont);
 }
 
-// Adds the given string to the end of the last line/chunk
-// while observing the _canvas._maxWidth and keeping this chunk's
-// formatting
-void MacTextCanvas::chopChunk(const Common::U32String &str, int *curLinePtr, int indent, int maxWidth) {
-	int curLine = *curLinePtr;
-	int curChunk;
-	MacFontRun *chunk;
-
-	curChunk = _text[curLine].chunks.size() - 1;
-	chunk = &_text[curLine].chunks[curChunk];
-
-	// Check if there is nothing to add, then remove the last chunk
-	// This happens when the previous run is finished only with
-	// empty formatting, or when we were adding text for the first time
-	if (chunk->text.empty() && str.empty()) {
-		D(9, "** chopChunk, replaced formatting, line %d", curLine);
-
-		_text[curLine].chunks.pop_back();
-
-		return;
-	}
-
-	if (maxWidth == -1) {
-		chunk->text += str;
-
-		return;
-	}
-
-	Common::Array<Common::U32String> text;
-
-	int w = getLineWidth(curLine, true);
-	D(9, "** chopChunk before wrap \"%s\"", Common::toPrintable(str.encode()).c_str());
-
-	chunk->getFont()->wordWrapText(str, maxWidth, text, w);
-
-	if (text.size() == 0) {
-		warning("chopChunk: too narrow width, >%d", maxWidth);
-		chunk->text += str;
-		getLineCharWidth(curLine, true);
-
-		return;
-	}
-
-	for (int i = 0; i < (int)text.size(); i++) {
-		D(9, "** chopChunk result %d \"%s\"", i, toPrintable(text[i].encode()).c_str());
-	}
-	chunk->text += text[0];
-
-	// Recalc dims
-	getLineWidth(curLine, true);
-
-	D(9, "** chopChunk, subchunk: \"%s\" (%d lines, maxW: %d)", toPrintable(text[0].encode()).c_str(), text.size(), maxWidth);
-
-	// We do not overlap, so we're done
-	if (text.size() == 1)
-		return;
-
-	// Now add rest of the chunks
-	MacFontRun newchunk = _text[curLine].chunks[curChunk];
-
-	for (uint i = 1; i < text.size(); i++) {
-		newchunk.text = text[i];
-
-		curLine++;
-		_text.insert_at(curLine, MacTextLine());
-		_text[curLine].chunks.push_back(newchunk);
-		_text[curLine].indent = indent;
-		_text[curLine].firstLineIndent = 0;
-
-		D(9, "** chopChunk, added line (firstIndent: %d): \"%s\"", _text[curLine].firstLineIndent, toPrintable(text[i].encode()).c_str());
-	}
-
-	*curLinePtr = curLine;
-}
-
-void MacTextCanvas::splitString(const Common::U32String &str, int curLine, MacFontRun &defaultFormatting) {
-	D(9, "** splitString(\"%s\", %d)", toPrintable(str.encode()).c_str(), curLine);
-
-	if (str.empty()) {
-		D(9, "** splitString, empty line");
-		return;
-	}
-
-	(void)splitString(str.c_str(), curLine, defaultFormatting);
-}
-
-const Common::U32String::value_type *MacTextCanvas::splitString(const Common::U32String::value_type *s, int curLine, MacFontRun &defaultFormatting) {
-	if (_text.empty()) {
-		_text.resize(1);
-		_text[0].chunks.push_back(defaultFormatting);
-		D(9, "** splitString, added default formatting");
-	} else {
-		D(9, "** splitString, continuing, %d lines", _text.size());
-	}
-
-	Common::U32String tmp;
-
-	if (curLine == -1 || curLine >= (int)_text.size())
-		curLine = _text.size() - 1;
-
-	int curChunk = _text[curLine].chunks.size() - 1;
-	MacFontRun chunk = _text[curLine].chunks[curChunk];
-	int indentSize = 0;
-	int firstLineIndent = 0;
-	bool inTable = false;
-
-	while (*s) {
-		firstLineIndent = 0;
-
-		tmp.clear();
-
-		MacTextLine *curTextLine = &_text[curLine];
-
-		while (*s) {
-			bool endOfLine = false;
-
-			// Scan till next font change or end of line
-			while (*s && *s != '\001') {
-				if (*s == '\r') {
-					s++;
-					if (*s == '\n')	// Skip whole '\r\n'
-						s++;
-
-					endOfLine = true;
-
-					break;
-				}
-
-				// deal with single \n
-				if (*s == '\n') {
-					s++;
-
-					endOfLine = true;
-					break;
-				}
-
-				tmp += *s;
-
-				s++;
-			}
-
-			if (*s == '\001')	// If it was \001, skip it
-				s++;
-
-			if (*s == '\001') { // \001\001 -> \001
-				tmp += *s++;
-
-				if (*s)	// Check we reached end of line
-					continue;
-			}
-
-			D(9, "** splitString, chunk: \"%s\"", Common::toPrintable(tmp.encode()).c_str());
-
-			// Okay, now we are either at the end of the line, or in the next
-			// chunk definition. That means, that we have to store the previous chunk
-			chopChunk(tmp, &curLine, indentSize, _maxWidth > 0 ? _maxWidth - indentSize : _maxWidth);
-
-			curTextLine = &_text[curLine];
-
-			firstLineIndent = curTextLine->firstLineIndent;
-
-			tmp.clear();
-
-			// If it is end of the line, we're done
-			if (!*s) {
-				D(9, "** splitString, end of line");
-
-				break;
-			}
-
-			// get format (sync with stripFormat() )
-			if (*s == '\016') {	// human-readable format
-				s++;
-
-				// First two digits is slant, third digit is Header number
-				switch (*s) {
-				case '+': { // \016+XXYZ  -- opening textSlant, H<Y>, indent<+Z>
-					uint16 textSlant, headSize, indent;
-					s++;
-
-					s = readHex(&textSlant, s, 2);
-
-					chunk.textSlant |= textSlant; // Setting the specified bit
-
-					s = readHex(&headSize, s, 1);
-					if (headSize >= 1 && headSize <= 6) { // set
-						const float sizes[] = { 1, 2.0f, 1.41f, 1.155f, 1.0f, .894f, .816f };
-						chunk.fontSize = defaultFormatting.fontSize * sizes[headSize];
-					}
-
-					s = readHex(&indent, s, 1);
-
-					if (s)
-						indentSize += indent * chunk.fontSize * 2;
-
-					D(9, "** splitString+: fontId: %d, textSlant: %d, fontSize: %d, indent: %d",
-							chunk.fontId, chunk.textSlant, chunk.fontSize,
-							indent);
-
-					break;
-					}
-				case '-': { // \016-XXYZ  -- closing textSlant, H<Y>, indent<+Z>
-					uint16 textSlant, headSize, indent;
-					s++;
-
-					s = readHex(&textSlant, s, 2);
-
-					chunk.textSlant &= ~textSlant; // Clearing the specified bit
-
-					s = readHex(&headSize, s, 1);
-					if (headSize == 0xf) // reset
-						chunk.fontSize = defaultFormatting.fontSize;
-
-					s = readHex(&indent, s, 1);
-
-					if (s)
-						indentSize -= indent * chunk.fontSize * 2;
-
-					D(9, "** splitString-: fontId: %d, textSlant: %d, fontSize: %d, indent: %d",
-							chunk.fontId, chunk.textSlant, chunk.fontSize,
-							indent);
-					break;
-					}
-
-				case '[': { // \016[RRGGBB  -- setting color
-					uint16 palinfo1, palinfo2, palinfo3;
-					s++;
-
-					s = readHex(&palinfo1, s, 4);
-					s = readHex(&palinfo2, s, 4);
-					s = readHex(&palinfo3, s, 4);
-
-					chunk.palinfo1 = palinfo1;
-					chunk.palinfo2 = palinfo2;
-					chunk.palinfo3 = palinfo3;
-					chunk.fgcolor  = _wm->findBestColor(palinfo1 & 0xff, palinfo2 & 0xff, palinfo3 & 0xff);
-
-					D(9, "** splitString[: %08x", chunk.fgcolor);
-					break;
-					}
-
-				case ']': { // \016]  -- setting default color
-					s++;
-
-					chunk.palinfo1 = defaultFormatting.palinfo1;
-					chunk.palinfo2 = defaultFormatting.palinfo2;
-					chunk.palinfo3 = defaultFormatting.palinfo3;
-					chunk.fgcolor  = defaultFormatting.fgcolor;
-
-					D(9, "** splitString]: %08x", chunk.fgcolor);
-					break;
-					}
-
-				case '*': { // \016*XXsssssss  -- negative indent, XX size, sssss is the string
-					s++;
-
-					uint16 len;
-
-					s = readHex(&len, s, 2);
-
-					Common::U32String bullet = Common::U32String(s, len);
-
-					s += len;
-
-					firstLineIndent = -chunk.getFont()->getStringWidth(bullet);
-
-					D(9, "** splitString*: %02x '%s' (%d)", len, bullet.encode().c_str(), firstLineIndent);
-					break;
-					}
-
-				case 'i': { // \016iXXNNnnnnAAaaaaTTttt -- image, XX% width,
-										//          NN, nnnn -- filename len and text
-										//          AA, aaaa -- alt len and text
-										//          TT, tttt -- text (tooltip) len and text
-					s++;
-
-					uint16 len;
-
-					s = readHex(&_text[curLine].picpercent, s, 2);
-					s = readHex(&len, s, 2);
-					_text[curLine].picfname = Common::U32String(s, len).encode();
-					s += len;
-
-					s = readHex(&len, s, 2);
-					_text[curLine].picalt = Common::U32String(s, len);
-					s += len;
-
-					s = readHex(&len, s, 2);
-					_text[curLine].pictitle = Common::U32String(s, len);
-					s += len;
-
-					D(9, "** splitString[i]: %d%% fname: '%s'  alt: '%s'  title: '%s'",
-						_text[curLine].picpercent,
-						_text[curLine].picfname.c_str(), _text[curLine].picalt.encode().c_str(),
-						_text[curLine].pictitle.encode().c_str());
-					break;
-					}
-
-				case 't': { // \016tXXXX -- switch to the requested font id
-					s++;
-
-					uint16 fontId;
-
-					s = readHex(&fontId, s, 4);
-
-					chunk.fontId = fontId == 0xffff ? defaultFormatting.fontId : fontId;
-
-					D(9, "** splitString[t]: fontId: %d", fontId);
-					break;
-					}
-
-				case 'l': { // \016lLLllll -- link len and text
-					s++;
-
-					uint16 len;
-
-					s = readHex(&len, s, 2);
-					chunk.link = Common::U32String(s, len);
-					s += len;
-
-					D(9, "** splitString[l]: link: %s", chunk.link.c_str());
-					break;
-					}
-
-				case 'T': { // \016T -- table
-					s++;
-
-					char cmd = *s++;
-
-					if (cmd == 'h') { // Header, beginning of the table
-						curTextLine->table = new Common::Array<MacTextTableRow>();
-						inTable = true;
-
-						D(9, "** splitString[table header]");
-					} else if (cmd == 'b') { // Body start
-						D(9, "** splitString[body start]");
-					} else if (cmd == 'B') { // Body end
-						inTable = false;
-
-						D(9, "** splitString[body end]");
-						processTable(curLine, _maxWidth);
-
-						continue;
-					} else if (cmd == 'r') { // Row
-						curTextLine->table->push_back(MacTextTableRow());
-						D(9, "** splitString[row]");
-					} else if (cmd == 'c') { // Cell start
-						uint16 align;
-						s = readHex(&align, s, 2);
-
-						curTextLine->table->back().cells.push_back(MacTextCanvas());
-
-						MacTextCanvas *cellCanvas = &curTextLine->table->back().cells.back();
-						cellCanvas->_textAlignment = (TextAlign)align;
-						cellCanvas->_wm = _wm;
-						cellCanvas->_macText = _macText;
-						cellCanvas->_maxWidth = -1;
-						cellCanvas->_macFontMode = _macFontMode;
-						cellCanvas->_tfgcolor = _tfgcolor;
-						cellCanvas->_tbgcolor = _tbgcolor;
-
-						D(9, "** splitString[cell start]: align: %d", align);
-
-						D(9, "** splitString[RECURSION start]");
-
-						s = cellCanvas->splitString(s, curLine, defaultFormatting);
-
-						D(9, "** splitString[RECURSION end]");
-					} else if (cmd == 'C') { // Cell end
-						D(9, "** splitString[cell end]");
-
-						return s;
-					} else {
-						error("MacText: Unknown table subcommand (%c)", cmd);
-					}
-					break;
-					}
-
-				default: {
-					uint16 fontId, textSlant, fontSize, palinfo1, palinfo2, palinfo3;
-
-					s = readHex(&fontId, s, 4);
-					s = readHex(&textSlant, s, 2);
-					s = readHex(&fontSize, s, 4);
-					s = readHex(&palinfo1, s, 4);
-					s = readHex(&palinfo2, s, 4);
-					s = readHex(&palinfo3, s, 4);
-
-					chunk.setValues(_wm, fontId, textSlant, fontSize, palinfo1, palinfo2, palinfo3);
-
-					D(9, "** splitString: fontId: %d, textSlant: %d, fontSize: %d, fg: %04x",
-							fontId, textSlant, fontSize, chunk.fgcolor);
-
-					// So far, we enforce single font here, though in the future, font size could be altered
-					if (!_macFontMode)
-						chunk.font = defaultFormatting.font;
-					}
-				}
-			}
-
-
-			curTextLine->indent = indentSize;
-			curTextLine->firstLineIndent = firstLineIndent;
-
-			// Push new formatting
-			curTextLine->chunks.push_back(chunk);
-
-			// If we reached end of paragraph, go to outer loop
-			if (endOfLine)
-				break;
-		}
-
-		// We avoid adding new lines while in table. Recursive cell rendering
-		// has this flag as false (obviously)
-		if (inTable)
-			continue;
-
-		// Add new line
-		D(9, "** splitString: new line");
-
-		curTextLine->paragraphEnd = true;
-		// if the chunks is empty, which means the line will not be rendered properly
-		// so we add a empty string here
-		if (curTextLine->chunks.empty()) {
-			curTextLine->chunks.push_back(defaultFormatting);
-		}
-
-		curLine++;
-		_text.insert_at(curLine, MacTextLine());
-		_text[curLine].chunks.push_back(chunk);
-
-		curTextLine = &_text[curLine];
-	}
-
-#if DEBUG
-	for (uint i = 0; i < _text.size(); i++) {
-		debugN(9, "** splitString: %2d ", i);
-
-		for (uint j = 0; j < _text[i].chunks.size(); j++)
-			debugN(9, "[%d] \"%s\"", _text[i].chunks[j].text.size(), Common::toPrintable(_text[i].chunks[j].text.encode()).c_str());
-
-		debugN(9, "\n");
-	}
-	debug(9, "** splitString: done");
-#endif
-
-	return s;
-}
-
-
-void MacTextCanvas::reallocSurface() {
-	// round to closest 10
-	//TODO: work out why this rounding doesn't correctly fill the entire width
-	//int requiredH = (_text.size() + (_text.size() * 10 + 9) / 10) * lineH
-
-	if (!_surface) {
-		_surface = new ManagedSurface(_maxWidth, _textMaxHeight, _wm->_pixelformat);
-
-		if (_textShadow)
-			_shadowSurface = new ManagedSurface(_maxWidth, _textMaxHeight, _wm->_pixelformat);
-
-		return;
-	}
-
-	if (_surface->w < _maxWidth || _surface->h < _textMaxHeight) {
-		// realloc surface and copy old content
-		ManagedSurface *n = new ManagedSurface(_maxWidth, _textMaxHeight, _wm->_pixelformat);
-		n->clear(_tbgcolor);
-		n->blitFrom(*_surface, Common::Point(0, 0));
-
-		delete _surface;
-		_surface = n;
-
-		// same as shadow surface
-		if (_textShadow) {
-			ManagedSurface *newShadowSurface = new ManagedSurface(_maxWidth, _textMaxHeight, _wm->_pixelformat);
-			newShadowSurface->clear(_tbgcolor);
-			newShadowSurface->blitFrom(*_shadowSurface, Common::Point(0, 0));
-
-			delete _shadowSurface;
-			_shadowSurface = newShadowSurface;
-		}
-	}
-}
-
 void MacText::render() {
 	if (_fullRefresh) {
 		_canvas._surface->clear(_canvas._tbgcolor);
@@ -1093,216 +573,6 @@ void MacText::render() {
 	}
 }
 
-void MacTextCanvas::render(int from, int to, int shadow) {
-	int w = MIN(_maxWidth, _textMaxWidth);
-	ManagedSurface *surface = shadow ? _shadowSurface : _surface;
-
-	int myFrom = from, myTo = to + 1, delta = 1;
-
-	if (_wm->_language == Common::HE_ISR) {
-		myFrom = to;
-		myTo = from - 1;
-		delta = -1;
-	}
-
-	for (int i = myFrom; i != myTo; i += delta) {
-		if (!_text[i].picfname.empty()) {
-			const Surface *image = _macText->getImageSurface(_text[i].picfname);
-
-			int xOffset = (_text[i].width - _text[i].charwidth) / 2;
-			Common::Rect bbox(xOffset, _text[i].y, xOffset + _text[i].charwidth, _text[i].y + _text[i].height);
-
-			if (image) {
-				surface->blitFrom(image, Common::Rect(0, 0, image->w, image->h), bbox);
-
-				D(9, "MacTextCanvas::render: Image %d x %d bbox: %d, %d, %d, %d", image->w, image->h, bbox.left, bbox.top,
-						bbox.right, bbox.bottom);
-			}
-
-			continue;
-		}
-
-		if (_text[i].tableSurface) {
-			surface->blitFrom(*_text[i].tableSurface, Common::Point(0, _text[i].y));
-
-			D(9, "MacTextCanvas::render: Table %d x %d at: %d, %d", _text[i].tableSurface->w, _text[i].tableSurface->h, 0, _text[i].y);
-
-			continue;
-		}
-
-		int xOffset = getAlignOffset(i) + _text[i].indent + _text[i].firstLineIndent;
-		xOffset++;
-
-		int start = 0, end = _text[i].chunks.size();
-		if (_wm->_language == Common::HE_ISR) {
-			start = _text[i].chunks.size() - 1;
-			end = -1;
-		}
-
-		int maxAscentForRow = 0;
-		for (int j = start; j != end; j += delta) {
-			if (_text[i].chunks[j].font->getFontAscent() > maxAscentForRow)
-				maxAscentForRow = _text[i].chunks[j].font->getFontAscent();
-		}
-
-		// TODO: _canvas._textMaxWidth, when -1, was not rendering ANY text.
-		for (int j = start; j != end; j += delta) {
-			D(9, "MacTextCanvas::render: line %d[%d] h:%d at %d,%d (%s) fontid: %d fontsize: %d on %dx%d, fgcolor: %08x bgcolor: %08x",
-				  i, j, _text[i].height, xOffset, _text[i].y, _text[i].chunks[j].text.encode().c_str(),
-				  _text[i].chunks[j].fontId, _text[i].chunks[j].fontSize, _surface->w, _surface->h, _text[i].chunks[j].fgcolor, _tbgcolor);
-
-			if (_text[i].chunks[j].text.empty())
-				continue;
-
-			int yOffset = 0;
-			if (_text[i].chunks[j].font->getFontAscent() < maxAscentForRow) {
-				yOffset = maxAscentForRow - _text[i].chunks[j].font->getFontAscent();
-			}
-
-			if (_text[i].chunks[j].plainByteMode()) {
-				Common::String str = _text[i].chunks[j].getEncodedText();
-				_text[i].chunks[j].getFont()->drawString(surface, str, xOffset, _text[i].y + yOffset, w, shadow ? _wm->_colorBlack : _text[i].chunks[j].fgcolor, kTextAlignLeft, 0, true);
-				xOffset += _text[i].chunks[j].getFont()->getStringWidth(str);
-			} else {
-				if (_wm->_language == Common::HE_ISR)
-					_text[i].chunks[j].getFont()->drawString(surface, convertBiDiU32String(_text[i].chunks[j].text, Common::BIDI_PAR_RTL), xOffset, _text[i].y + yOffset, w, shadow ? _wm->_colorBlack : _text[i].chunks[j].fgcolor, kTextAlignLeft, 0, true);
-				else
-					_text[i].chunks[j].getFont()->drawString(surface, convertBiDiU32String(_text[i].chunks[j].text), xOffset, _text[i].y + yOffset, w, shadow ? _wm->_colorBlack : _text[i].chunks[j].fgcolor, kTextAlignLeft, 0, true);
-				xOffset += _text[i].chunks[j].getFont()->getStringWidth(_text[i].chunks[j].text);
-			}
-		}
-	}
-}
-
-void MacTextCanvas::render(int from, int to) {
-	if (_text.empty())
-		return;
-
-	reallocSurface();
-
-	from = MAX<int>(0, from);
-	to = MIN<int>(to, _text.size() - 1);
-
-	// Clear the screen
-	_surface->fillRect(Common::Rect(0, _text[from].y, _surface->w, _text[to].y + getLineHeight(to)), _tbgcolor);
-
-	// render the shadow surface;
-	if (_textShadow)
-		render(from, to, _textShadow);
-
-	render(from, to, 0);
-
-	for (uint i = 0; i < _text.size(); i++) {
-		debugN(9, "MacTextCanvas::render: %2d (firstInd: %d indent: %d) ", i, _text[i].firstLineIndent, _text[i].indent);
-
-		for (uint j = 0; j < _text[i].chunks.size(); j++)
-			debugN(9, "[%d (%d)] \"%s\" ", _text[i].chunks[j].fontId, _text[i].chunks[j].textSlant, _text[i].chunks[j].text.encode().c_str());
-
-		debug(9, "%s", "");
-	}
-}
-
-int MacTextCanvas::getLineWidth(int lineNum, bool enforce, int col) {
-	if ((uint)lineNum >= _text.size())
-		return 0;
-
-	MacTextLine *line = &_text[lineNum];
-
-	if (line->width != -1 && !enforce && col == -1)
-		return line->width;
-
-	if (!line->picfname.empty()) {
-		const Surface *image = _macText->getImageSurface(line->picfname);
-
-		if (image) {
-			float ratio = _maxWidth * line->picpercent / 100.0 / (float)image->w;
-			line->width = _maxWidth;
-			line->height = image->h * ratio;
-			line->charwidth = image->w * ratio;
-		} else {
-			line->width = _maxWidth;
-			line->height = 1;
-			line->charwidth = 1;
-		}
-
-		return line->width;
-	}
-
-	if (line->table) {
-		line->width = _maxWidth;
-		line->height = line->tableSurface->h;
-		line->charwidth = _maxWidth;
-
-		return line->width;
-	}
-
-	int width = line->indent + line->firstLineIndent;
-	int height = 0;
-	int charwidth = 0;
-	int minWidth = 0;
-	bool firstWord = true;
-
-	for (uint i = 0; i < line->chunks.size(); i++) {
-		if (enforce && _macFontMode)
-			line->chunks[i].font = nullptr;
-
-		if (col >= 0) {
-			if (col >= (int)line->chunks[i].text.size()) {
-				col -= line->chunks[i].text.size();
-			} else {
-				Common::U32String tmp = line->chunks[i].text.substr(0, col);
-
-				width += getStringWidth(line->chunks[i], tmp);
-
-				return width;
-			}
-		}
-
-		if (!line->chunks[i].text.empty()) {
-			int w = getStringWidth(line->chunks[i], line->chunks[i].text);
-			int mW = getStringMaxWordWidth(line->chunks[i], line->chunks[i].text);
-
-			if (firstWord) {
-				minWidth = mW + width; // Take indent into account
-				firstWord = false;
-			} else {
-				minWidth = MAX(minWidth, mW);
-			}
-			width += w;
-			charwidth += line->chunks[i].text.size();
-		}
-
-		height = MAX(height, line->chunks[i].getFont()->getFontHeight());
-	}
-
-
-	line->width = width;
-	line->minWidth = minWidth;
-	line->height = height;
-	line->charwidth = charwidth;
-
-	return width;
-}
-
-int MacTextCanvas::getLineCharWidth(int line, bool enforce) {
-	if ((uint)line >= _text.size())
-		return 0;
-
-	if (_text[line].charwidth != -1 && !enforce)
-		return _text[line].charwidth;
-
-	int width = 0;
-
-	for (uint i = 0; i < _text[line].chunks.size(); i++) {
-		if (!_text[line].chunks[i].text.empty())
-			width += _text[line].chunks[i].text.size();
-	}
-
-	_text[line].charwidth = width;
-
-	return width;
-}
-
 int MacText::getLastLineWidth() {
 	if (_canvas._text.size() == 0)
 		return 0;
@@ -1314,15 +584,6 @@ int MacText::getLineHeight(int line) {
 	return _canvas.getLineHeight(line);
 }
 
-int MacTextCanvas::getLineHeight(int line) {
-	if ((uint)line >= _text.size())
-		return 0;
-
-	(void)getLineWidth(line); // This calculates height also
-
-	return _text[line].height;
-}
-
 void MacText::setInterLinear(int interLinear) {
 	_canvas._interLinear = interLinear;
 
@@ -1352,25 +613,6 @@ void MacText::recalcDims() {
 	}
 }
 
-void MacTextCanvas::recalcDims() {
-	if (_text.empty())
-		return;
-
-	int y = 0;
-	_textMaxWidth = 0;
-
-	for (uint i = 0; i < _text.size(); i++) {
-		_text[i].y = y;
-
-		// We must calculate width first, because it enforces
-		// the computation. Calling Height() will return cached value!
-		_textMaxWidth = MAX(_textMaxWidth, getLineWidth(i, true));
-		y += MAX(getLineHeight(i), _interLinear);
-	}
-
-	_textMaxHeight = y;
-}
-
 void MacText::setAlignOffset(TextAlign align) {
 	if (_canvas._textAlignment == align)
 		return;
@@ -2365,15 +1607,6 @@ Common::U32String MacText::getMouseLink(int x, int y) {
 	return Common::U32String();
 }
 
-int MacTextCanvas::getAlignOffset(int row) {
-	int alignOffset = 0;
-	if (_textAlignment == kTextAlignRight)
-		alignOffset = MAX<int>(0, _maxWidth - getLineWidth(row) - 1);
-	else if (_textAlignment == kTextAlignCenter)
-		alignOffset = (_maxWidth / 2) - (getLineWidth(row) / 2);
-	return alignOffset;
-}
-
 void MacText::getRowCol(int x, int y, int *sx, int *sy, int *row, int *col, int *chunk_) {
 	int nsx = 0, nsy = 0, nrow = 0, ncol = 0;
 
@@ -2456,117 +1689,6 @@ Common::U32String MacText::getTextChunk(int startRow, int startCol, int endRow,
 	return _canvas.getTextChunk(startRow, startCol, endRow, endCol, formatted, newlines);
 }
 
-// If adjacent chunks have same format, then skip the format definition
-// This happens when a long paragraph is split into several lines
-#define ADDFORMATTING()                                                                      \
-	if (formatted) {                                                                         \
-		formatting = Common::U32String(_text[i].chunks[chunk].toString()); \
-		if (formatting != prevformatting) {                                                  \
-			res += formatting;                                                               \
-			prevformatting = formatting;                                                     \
-		}                                                                                    \
-	}
-
-Common::U32String MacTextCanvas::getTextChunk(int startRow, int startCol, int endRow, int endCol, bool formatted, bool newlines) {
-	Common::U32String res("");
-
-	if (endRow == -1)
-		endRow = _text.size() - 1;
-
-	if (endCol == -1)
-		endCol = getLineCharWidth(endRow);
-	if (_text.empty()) {
-		return res;
-	}
-
-	startRow = CLIP(startRow, 0, (int)_text.size() - 1);
-	endRow = CLIP(endRow, 0, (int)_text.size() - 1);
-
-	Common::U32String formatting(""), prevformatting("");
-
-	for (int i = startRow; i <= endRow; i++) {
-		// We requested only part of one line
-		if (i == startRow && i == endRow) {
-			for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
-				if (_text[i].chunks[chunk].text.empty()) {
-					// skip empty chunks, but keep them formatted,
-					// a text input box needs to keep the formatting even when all text is removed.
-					ADDFORMATTING();
-					continue;
-				}
-
-				if (startCol <= 0) {
-					ADDFORMATTING();
-
-					if (endCol >= (int)_text[i].chunks[chunk].text.size())
-						res += _text[i].chunks[chunk].text;
-					else
-						res += _text[i].chunks[chunk].text.substr(0, endCol);
-				} else if ((int)_text[i].chunks[chunk].text.size() > startCol) {
-					ADDFORMATTING();
-					res += _text[i].chunks[chunk].text.substr(startCol, endCol - startCol);
-				}
-
-				startCol -= _text[i].chunks[chunk].text.size();
-				endCol -= _text[i].chunks[chunk].text.size();
-
-				if (endCol <= 0)
-					break;
-			}
-		// We are at the top line and it is not completely requested
-		} else if (i == startRow && startCol != 0) {
-			for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
-				if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
-					continue;
-
-				if (startCol <= 0) {
-					ADDFORMATTING();
-					res += _text[i].chunks[chunk].text;
-				} else if ((int)_text[i].chunks[chunk].text.size() > startCol) {
-					ADDFORMATTING();
-					res += _text[i].chunks[chunk].text.substr(startCol);
-				}
-
-				startCol -= _text[i].chunks[chunk].text.size();
-			}
-			if (newlines && _text[i].paragraphEnd)
-				res += '\n';
-		// We are at the end row, and it could be not completely requested
-		} else if (i == endRow) {
-			for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
-				if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
-					continue;
-
-				ADDFORMATTING();
-
-				if (endCol >= (int)_text[i].chunks[chunk].text.size())
-					res += _text[i].chunks[chunk].text;
-				else
-					res += _text[i].chunks[chunk].text.substr(0, endCol);
-
-				endCol -= _text[i].chunks[chunk].text.size();
-
-				if (endCol <= 0)
-					break;
-			}
-		// We are in the middle of requested range, pass whole line
-		} else {
-			for (uint chunk = 0; chunk < _text[i].chunks.size(); chunk++) {
-				if (_text[i].chunks[chunk].text.empty()) // skip empty chunks
-					continue;
-
-				ADDFORMATTING();
-				res += _text[i].chunks[chunk].text;
-			}
-
-			if (newlines && _text[i].paragraphEnd)
-				res += '\n';
-		}
-	}
-
-	return res;
-}
-
 // mostly, we refering reshuffleParagraph to implement this function
 void MacText::insertTextFromClipboard() {
 	int ppos = 0;
@@ -2818,47 +1940,6 @@ void MacText::addNewLine(int *row, int *col) {
 	render();
 }
 
-void MacTextCanvas::reshuffleParagraph(int *row, int *col, MacFontRun &defaultFormatting) {
-	// First, we looking for the paragraph start and end
-	int start = *row, end = *row;
-
-	while (start && !_text[start - 1].paragraphEnd)
-		start--;
-
-	while (end < (int)_text.size() - 1 && !_text[end].paragraphEnd) // stop at last line
-		end++;
-
-	// Get character pos within paragraph
-	int ppos = 0;
-
-	for (int i = start; i < *row; i++)
-		ppos += getLineCharWidth(i);
-
-	ppos += *col;
-
-	// Get whole paragraph
-	Common::U32String paragraph = getTextChunk(start, 0, end, getLineCharWidth(end, true), true, true);
-
-	// Remove it from the text
-	for (int i = start; i <= end; i++) {
-		_text.remove_at(start);
-	}
-
-	// And now read it
-	D(9, "start %d end %d", start, end);
-	splitString(paragraph, start, defaultFormatting);
-
-	// Find new pos within paragraph after reshuffling
-	*row = start;
-
-	warning("FIXME, bad design");
-	while (ppos > getLineCharWidth(*row, true)) {
-		ppos -= getLineCharWidth(*row, true);
-		(*row)++;
-	}
-	*col = ppos;
-}
-
 //////////////////
 // Cursor stuff
 static void cursorTimerHandler(void *refCon) {
@@ -2946,158 +2027,4 @@ const Surface *MacText::getImageSurface(Common::String &fname) {
 #endif // USE_PNG
 }
 
-void MacTextCanvas::processTable(int line, int maxWidth) {
-	Common::Array<MacTextTableRow> *table = _text[line].table;
-	uint numCols = table->front().cells.size();
-	uint numRows = table->size();
-	Common::Array<int> maxW(numCols), maxL(numCols), colW(numCols), rowH(numRows);
-	Common::Array<bool> flex(numCols), wrap(numCols);
-
-	int width = maxWidth * 0.9;
-	int gutter = 10;
-
-	// Compute column widths, both minimal and maximal
-	for (auto &row : *table) {
-		int i = 0;
-		for (auto &cell : row.cells) {
-			int cW = 0, cL = 0;
-			for (uint l = 0; l < cell._text.size(); l++) {
-				(void)cell.getLineWidth(l); // calculate it
-
-				cW = MAX(cW, cell._text[l].width);
-				cL = MAX(cL, cell._text[l].minWidth);
-			}
-
-			maxW[i] = MAX(maxW[i], cW);
-			maxL[i] = MAX(maxL[i], cL);
-
-			i++;
-		}
-	}
-
-	for (uint i = 0; i < numCols; i++) {
-		warning("%d: %d - %d", i, maxL[i], maxW[i]);
-
-		wrap[i] = (maxW[i] != maxL[i]);
-	}
-
-	int left = width - (numCols - 1) * gutter;
-	int avg = left / numCols;
-	int nflex = 0;
-
-	// determine whether columns should be flexible and assign
-	// width of non-flexible cells
-	for (uint i = 0; i < numCols; i++) {
-		flex[i] = (maxW[i] > 2 * avg);
-		if (flex[i]) {
-			nflex++;
-		} else {
-			colW[i] = maxW[i];
-			left -= colW[i];
-		}
-	}
-
-	// if there is not enough space, make columns that could
-	// be word-wrapped flexible, too
-	if (left < nflex * avg) {
-		for (uint i = 0; i < numCols; i++) {
-			if (!flex[i] && wrap[i]) {
-				left += colW[i];
-				colW[i] = 0;
-				flex[i] = true;
-				nflex += 1;
-			}
-		}
-	}
-
-	// Calculate weights for flexible columns. The max width
-	// is capped at the page width to treat columns that have to
-	// be wrapped more or less equal
-	int tot = 0;
-	for (uint i = 0; i < numCols; i++) {
-		if (flex[i]) {
-			maxW[i] = MIN(maxW[i], width);
-			tot += maxW[i];
-		}
-	}
-
-	// Now assign the actual width for flexible columns. Make
-	// sure that it is at least as long as the longest word length
-	for (uint i = 0; i < numCols; i++) {
-		if (flex[i]) {
-			colW[i] = left * maxW[i] / tot;
-			colW[i] = MAX(colW[i], maxL[i]);
-			left -= colW[i];
-		}
-	}
-
-	for (uint i = 0; i < numCols; i++) {
-		warning("%d: %d", i, colW[i]);
-	}
-
-	int r = 0;
-	for (auto &row : *table) {
-		int c = 0;
-		rowH[r] = 0;
-		for (auto &cell : row.cells) {
-			cell._maxWidth = colW[c];
-
-			cell.recalcDims();
-			cell.reallocSurface();
-			cell._surface->clear(_tbgcolor);
-			cell.render(0, cell._text.size());
-
-			rowH[r] = MAX(rowH[r], cell._textMaxHeight);
-
-			c++;
-		}
-
-		r++;
-	}
-
-	int tW = 1, tH = 1;
-	for (uint i = 0; i < table->size(); i++)
-		tH += rowH[i] + gutter * 2 + 1;
-
-	for (uint i = 0; i < table->front().cells.size(); i++)
-		tW += colW[i] + gutter * 2 + 1;
-
-	ManagedSurface *surf = new ManagedSurface(tW, tH, _wm->_pixelformat);
-	_text[line].tableSurface = surf;
-	_text[line].height = tH;
-	_text[line].width = tW;
-	surf->clear(_tbgcolor);
-
-	surf->hLine(0, 0, tW, _tfgcolor);
-	surf->vLine(0, 0, tH, _tfgcolor);
-
-	int y = 1;
-	for (uint i = 0; i < table->size(); i++) {
-		y += gutter * 2 + rowH[i];
-		surf->hLine(0, y, tW, _tfgcolor);
-		y++;
-	}
-
-	int x = 1;
-	for (uint i = 0; i < table->front().cells.size(); i++) {
-		x += gutter * 2 + colW[i];
-		surf->vLine(x, 0, tH, _tfgcolor);
-		x++;
-	}
-
-	r = 0;
-	y = 1 + gutter;
-	for (auto &row : *table) {
-		int c = 0;
-		x = 1 + gutter;
-		for (auto &cell : row.cells) {
-			surf->blitFrom(*cell._surface, Common::Point(x, y));
-			x += gutter * 2 + 1 + colW[c];
-			c++;
-		}
-		y += gutter * 2 + 1 + rowH[r];
-		r++;
-	}
-}
-
 } // End of namespace Graphics
diff --git a/graphics/macgui/mactext.h b/graphics/macgui/mactext.h
index 69f1d940568..8fcd3539cf5 100644
--- a/graphics/macgui/mactext.h
+++ b/graphics/macgui/mactext.h
@@ -22,18 +22,7 @@
 #ifndef GRAPHICS_MACGUI_MACTEXT_H
 #define GRAPHICS_MACGUI_MACTEXT_H
 
-#include "common/timer.h"
-#include "common/system.h"
-
-#include "graphics/macgui/macwindowmanager.h"
-#include "graphics/macgui/macfontmanager.h"
-#include "graphics/macgui/macmenu.h"
-#include "graphics/macgui/macwidget.h"
-#include "graphics/macgui/macwindow.h"
-
-namespace Common {
-class Archive;
-}
+#include "graphics/macgui/mactext-canvas.h"
 
 namespace Image {
 class PNGDecoder;
@@ -41,194 +30,6 @@ class PNGDecoder;
 
 namespace Graphics {
 
-class MacMenu;
-class MacText;
-class MacWidget;
-class MacWindow;
-class MacWindowManager;
-
-struct MacFontRun {
-	Common::U32String text;
-
-	uint16 fontId;
-	byte textSlant;
-	uint16 fontSize;
-	uint16 palinfo1;
-	uint16 palinfo2;
-	uint16 palinfo3;
-	uint32 fgcolor;
-	// to determine whether the next word is part of this one
-	bool wordContinuation;
-	const Font *font;
-	MacWindowManager *wm;
-	Common::String link;  // Substitute to return when hover or click
-
-	MacFontRun() {
-		wm = nullptr;
-		fontId = textSlant = fontSize = 0;
-		palinfo1 = palinfo2 = palinfo3 = 0;
-		fgcolor = 0;
-		font = nullptr;
-		wordContinuation = false;
-	}
-
-	MacFontRun(MacWindowManager *wm_) {
-		wm = wm_;
-		fontId = textSlant = fontSize = 0;
-		palinfo1 = palinfo2 = palinfo3 = 0;
-		fgcolor = 0;
-		font = nullptr;
-		wordContinuation = false;
-	}
-
-	MacFontRun(MacWindowManager *wm_, uint16 fontId_, byte textSlant_, uint16 fontSize_,
-			uint16 palinfo1_, uint16 palinfo2_, uint16 palinfo3_) {
-		setValues(wm_, fontId_, textSlant_, fontSize_, palinfo1_, palinfo2_, palinfo3_);
-		wordContinuation = false;
-	}
-
-	MacFontRun(MacWindowManager *wm_, const Font *font_, byte textSlant_, uint16 fontSize_,
-			uint16 palinfo1_, uint16 palinfo2_, uint16 palinfo3_) {
-		setValues(wm_, 0, textSlant_, fontSize_, palinfo1_, palinfo2_, palinfo3_);
-		font = font_;
-		wordContinuation = false;
-	}
-
-	void setValues(MacWindowManager *wm_, uint16 fontId_, byte textSlant_, uint16 fontSize_,
-			uint16 palinfo1_, uint16 palinfo2_, uint16 palinfo3_) {
-		wm        = wm_;
-		fontId    = fontId_;
-		textSlant = textSlant_;
-		fontSize  = fontSize_;
-		palinfo1  = palinfo1_;
-		palinfo2  = palinfo2_;
-		palinfo3  = palinfo3_;
-		fgcolor   = wm_->findBestColor(palinfo1_ & 0xff, palinfo2_ & 0xff, palinfo3_ & 0xff);
-		font      = nullptr;
-	}
-
-	const Font *getFont();
-
-	const Common::String toString();
-	bool equals(MacFontRun &to);
-
-	Common::CodePage getEncoding();
-	bool plainByteMode();
-	Common::String getEncodedText();
-
-	bool equals(const MacFontRun *x, const MacFontRun *y) {
-		return (x->fontId    == y->fontId &&
-				x->textSlant == y->textSlant &&
-				x->fontSize  == y->fontSize &&
-				x->palinfo1  == y->palinfo1 &&
-				x->palinfo2  == y->palinfo2 &&
-				x->palinfo3  == y->palinfo3 &&
-				x->fgcolor   == y->fgcolor);
-	}
-
-};
-
-struct MacTextLine;
-
-class MacTextCanvas {
-public:
-	Common::Array<MacTextLine> _text;
-	ManagedSurface *_surface = nullptr, *_shadowSurface = nullptr;
-	int _maxWidth = 0;
-	int _textMaxWidth = 0;
-	int _textMaxHeight = 0;
-	TextAlign _textAlignment = kTextAlignLeft;
-	int _interLinear = 0;
-	int _textShadow = 0;
-	MacWindowManager *_wm = nullptr;
-	uint32 _tfgcolor = 0;
-	uint32 _tbgcolor = 0;
-	bool _macFontMode = true;
-	MacText *_macText;
-
-public:
-	~MacTextCanvas() {
-		delete _surface;
-		delete _shadowSurface;
-	}
-
-	void recalcDims();
-	void reallocSurface();
-	void render(int from, int to);
-	void render(int from, int to, int shadow);
-	int getAlignOffset(int row);
-
-	/**
-	 * Returns line width in pixels. This takes into account chunks.
-	 * The result is cached for faster subsequent calls.
-	 *
-	 * @param line Line number
-	 * @param enforce Flag for indicating skipping the cache and computing the width,
-	 *                must be called when text gets changed
-	 * @param col Compute line width up to specified column, including this column
-	 * @return line width in pixels, or 0 for non-existent lines
-	 */
-	int getLineWidth(int line, bool enforce = false, int col = -1);
-	int getLineHeight(int line);
-	int getLineCharWidth(int line, bool enforce = false);
-
-	void splitString(const Common::U32String &str, int curLine, MacFontRun &defaultFormatting);
-	const Common::U32String::value_type *splitString(const Common::U32String::value_type *s, int curLine, MacFontRun &defaultFormatting);
-
-	void chopChunk(const Common::U32String &str, int *curLinePtr, int indent, int maxWidth);
-	Common::U32String getTextChunk(int startRow, int startCol, int endRow, int endCol, bool formatted = false, bool newlines = true);
-
-	/**
-	 * Rewraps paragraph containing given text row.
-	 * When text is modified, we redo whole thing again without touching
-	 * other paragraphs. Also, cursor position is returned in the arguments
-	 */
-	void reshuffleParagraph(int *row, int *col, MacFontRun &defaultFormatting);
-
-	void processTable(int line, int maxWidth);
-};
-
-struct MacTextTableRow {
-	Common::Array<MacTextCanvas> cells;
-	int heght = -1;
-};
-
-struct MacTextLine {
-	int width = -1;
-	int height = -1;
-	int minWidth = -1;
-	int y = 0;
-	int charwidth = -1;
-	bool paragraphEnd = false;
-	int indent = 0; // in units
-	int firstLineIndent = 0; // in pixels
-	Common::String picfname;
-	Common::U32String picalt, pictitle;
-	uint16 picpercent = 50;
-	Common::Array<MacTextTableRow> *table = nullptr;
-	ManagedSurface *tableSurface = nullptr;
-
-	Common::Array<MacFontRun> chunks;
-
-	MacFontRun &firstChunk() { return chunks[0]; }
-	MacFontRun &lastChunk() { return chunks[chunks.size() - 1]; }
-
-	/**
-	 * Search for a chunk at given char column.
-	 *
-	 * @param col Requested column, gets modified with in-chunk column
-	 * @returns Chunk number
-	 *
-	 * @note If requested column is too big, returns last character in the line
-	 */
-	uint getChunkNum(int *col);
-
-	~MacTextLine() {
-		delete table;
-		delete tableSurface;
-	}
-};
-
 struct SelectedText {
 	int startX, startY;
 	int endX, endY;
@@ -442,6 +243,8 @@ private:
 	Common::Archive *_imageArchive = nullptr;
 };
 
+int getStringWidth(MacFontRun &format, const Common::U32String &str);
+
 } // End of namespace Graphics
 
 #endif
diff --git a/graphics/module.mk b/graphics/module.mk
index c2a2113896c..1cedbcd6e01 100644
--- a/graphics/module.mk
+++ b/graphics/module.mk
@@ -29,6 +29,7 @@ MODULE_OBJS := \
 	macgui/macmenu.o \
 	macgui/macpopupmenu.o \
 	macgui/mactext.o \
+	macgui/mactext-canvas.o \
 	macgui/mactext-md.o \
 	macgui/mactextwindow.o \
 	macgui/macwidget.o \




More information about the Scummvm-git-logs mailing list