diff -r 597445a1063c src/Editor.cxx
--- a/src/Editor.cxx	Mon Apr 30 12:48:10 2012 +1000
+++ b/src/Editor.cxx	Thu May 03 10:59:40 2012 +1000
@@ -39,6 +39,7 @@
 #include "Selection.h"
 #include "PositionCache.h"
 #include "Editor.h"
+#include "UniConversion.h"
 
 #ifdef SCI_NAMESPACE
 using namespace Scintilla;
@@ -221,6 +222,32 @@
 
 	llc.SetLevel(LineLayoutCache::llcCaret);
 	posCache.SetSize(0x400);
+
+	//reprs.SetRepresentation("M", "mm");
+	const int heightIm = 10;
+	const int widthIm = 12;
+	RGBAImage rgba(widthIm, heightIm, 0);
+	for (int i=0;i<heightIm;i++) {
+		rgba.SetPixel(i + 1, i, ColourDesired(0xa0,0,0));
+	}
+	// Substitute slash for DEGREE CELSIUS
+	reprs.SetRepresentation("\xe2\x84\x83", rgba.GetWidth(), rgba.GetHeight(), rgba.Pixels());
+
+	RGBAImage rgba3(widthIm, heightIm, 0);
+	for (int bar=0;bar<3;bar++) {
+		for (int x=1;x<widthIm-2;x++) {
+			rgba3.SetPixel(x, bar * 3 + 2, ColourDesired(0,0x90,0));
+		}
+	}
+	// Substitute bars for DEGREE FAHRENHEIT
+	reprs.SetRepresentation("\xe2\x84\x89", rgba3.GetWidth(), rgba3.GetHeight(), rgba3.Pixels());
+
+	//reprs.SetRepresentation("N", "big N");
+	reprs.SetRepresentation("\xe2\x84\xa6", "U+2126 \xe2\x84\xa6");
+	reprs.SetRepresentation("\xe2\x84\xa7", "2127 \xe2\x84\xa7 INVERTED OHM SIGN");
+	reprs.SetRepresentation("\xc6\xbf", "LATIN LETTER WYNN");
+	reprs.SetRepresentation("\xc7\xb7", "LATIN CAPITAL LETTER WYNN");
+	reprs.SetRepresentation("\xe1\x9a\xb9", "\xe1\x9a\xb9 RUNIC LETTER WUNJO WYNN W");
 }
 
 Editor::~Editor() {
@@ -2261,13 +2288,20 @@
 		bool isControlNext = IsControlCharacter(ll->chars[0]);
 		int trailBytes = 0;
 		bool isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars, numCharsInLine, trailBytes);
+		SRepresentation *reprNext = 0;
+		if (IsUnicodeMode())
+			reprNext = reprs.Representation(ll->chars, numCharsInLine);
 		for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
 			bool isControl = isControlNext;
 			isControlNext = IsControlCharacter(ll->chars[charInLine + 1]);
 			bool isBadUTF = isBadUTFNext;
 			isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars + charInLine + 1, numCharsInLine - charInLine - 1, trailBytes);
+			SRepresentation *repr = reprNext;
+			int reprLength = 0;
+			if (IsUnicodeMode() && !repr)
+				reprNext = reprs.Representation(ll->chars + charInLine + 1, numCharsInLine - charInLine - 1);
 			if ((ll->styles[charInLine] != ll->styles[charInLine + 1]) ||
-			        isControl || isControlNext || isBadUTF || isBadUTFNext) {
+			        isControl || isControlNext || isBadUTF || isBadUTFNext || repr || reprNext) {
 				ll->positions[startseg] = 0;
 				if (vstyle.styles[ll->styles[charInLine]].visible) {
 					if (isControl) {
@@ -2294,6 +2328,16 @@
 						ll->positions[charInLine + 1] =
 						    surface->WidthText(ctrlCharsFont, hexits, istrlen(hexits)) + 3;
 					} else {	// Regular character
+						if (repr) {
+							// Need to find number of bytes in thing being reprd
+							reprLength = UTF8CharLength(static_cast<unsigned char>(ll->chars[charInLine]));
+							XYPOSITION endRepr = repr->width;
+							if (endRepr == 0)
+								endRepr = surface->WidthText(ctrlCharsFont, repr->stringRep.c_str(), repr->stringRep.length()) + 3;
+							for (int ii=0; ii < reprLength; ii++)
+							ll->positions[startseg + 1 + ii] = endRepr;
+							reprNext = reprs.Representation(ll->chars + charInLine + reprLength, numCharsInLine - charInLine - reprLength);
+						} else {
 						int lenSeg = charInLine - startseg + 1;
 						if ((lenSeg == 1) && (' ' == ll->chars[startseg])) {
 							lastSegItalics = false;
@@ -2305,11 +2349,14 @@
 							        lenSeg, ll->positions + startseg + 1, pdoc);
 						}
 					}
+					}
 				} else {    // invisible
 					for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) {
 						ll->positions[posToZero] = 0;
 					}
 				}
+				if (reprLength)
+					charInLine += reprLength - 1;
 				for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
 					ll->positions[posToIncrease] += startsegx;
 				}
@@ -2931,7 +2978,7 @@
 
 	ll->psel = &sel;
 
-	BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, xStartVisible, selBackDrawn, pdoc);
+	BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, xStartVisible, selBackDrawn, pdoc, &reprs);
 	int next = bfBack.First();
 
 	// Background drawing loop
@@ -3026,7 +3073,7 @@
 	inIndentation = subLine == 0;	// Do not handle indentation except on first subline.
 	// Foreground drawing loop
 	BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, xStartVisible,
-		((!twoPhaseDraw && selBackDrawn) || vsDraw.selforeset), pdoc);
+		((!twoPhaseDraw && selBackDrawn) || vsDraw.selforeset), pdoc, &reprs);
 	next = bfFore.First();
 
 	while (next < lineEnd) {
@@ -3107,6 +3154,23 @@
 				sprintf(hexits, "x%2X", ll->chars[i] & 0xff);
 				DrawTextBlob(surface, vsDraw, rcSegment, hexits, textBack, textFore, twoPhaseDraw);
 			} else {
+				SRepresentation *repr = 0;
+				if (IsUnicodeMode()) 
+					repr = reprs.Representation(ll->chars + startseg, i - startseg + 1);
+				if (repr) {
+					if (repr->width) {
+						// RGBA
+						RGBAImage rgbaImage(repr->width, repr->height, &(repr->pixelBytes[0]));
+						PRectangle rcImage;
+						rcImage.left = static_cast<int>(rcSegment.left+0.5);
+						rcImage.right = rcImage.left + repr->width;
+						rcImage.top = static_cast<int>(rcSegment.top + (rcSegment.Height() - repr->height) / 2);
+						rcImage.bottom = rcImage.top + repr->height;
+						surface->DrawRGBAImage(rcImage, repr->width, repr->height, rgbaImage.Pixels());
+					} else {
+						DrawTextBlob(surface, vsDraw, rcSegment, repr->stringRep.c_str(), textBack, textFore, twoPhaseDraw);
+					}
+				} else {
 				// Normal text display
 				if (vsDraw.styles[styleMain].visible) {
 					if (twoPhaseDraw) {
@@ -3160,6 +3224,7 @@
 					}
 				}
 			}
+			}
 			if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd) {
 				PRectangle rcUL = rcSegment;
 				rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
diff -r 597445a1063c src/Editor.h
--- a/src/Editor.h	Mon Apr 30 12:48:10 2012 +1000
+++ b/src/Editor.h	Thu May 03 10:59:40 2012 +1000
@@ -181,6 +181,7 @@
 
 	LineLayoutCache llc;
 	PositionCache posCache;
+	SpecialRepresentations reprs;
 
 	KeyMap kmap;
 
diff -r 597445a1063c src/PositionCache.cxx
--- a/src/PositionCache.cxx	Mon Apr 30 12:48:10 2012 +1000
+++ b/src/PositionCache.cxx	Thu May 03 10:59:40 2012 +1000
@@ -350,6 +350,116 @@
 	}
 }
 
+void SpecialRepresentations::SetRepresentation(const char *key, const char *value) {
+	mapReprs[key] = value;
+}
+
+void SpecialRepresentations::SetRepresentation(const char *key, int width, int height, const unsigned char *pixels) {
+	SRepresentation repr(width, height, pixels);
+	mapReprs[key] = repr;
+}
+
+static bool GoodTrailByte(int v) {
+	return (v >= 0x80) && (v < 0xc0);
+}
+
+enum { UTF8MaskWidth=0x7, UTF8MaskInvalid=0x8 };
+
+int Document_ClassifyUTF8(const unsigned char *us, int len) {
+	// For the rules: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+	if (*us < 0x80) {
+		// Single bytes easy
+		return 1;
+	} else if (*us > 0xF4) {
+		// Characters longer than 4 bytes not possible in current UTF-8
+		return UTF8MaskInvalid | 1;
+	} else if (*us >= 0xF0) {
+		// 4 bytes
+		if (len < 4)
+			return UTF8MaskInvalid | 1;
+		if (GoodTrailByte(us[1]) && GoodTrailByte(us[2]) && GoodTrailByte(us[3])) {
+			if (*us == 0xf4) {
+				// Check if encoding a value beyond the last Unicode character 10FFFF
+				if (us[1] > 0x8f) {
+					return UTF8MaskInvalid | 1;
+				} else if (us[1] == 0x8f) {
+					if (us[2] > 0xbf) {
+						return UTF8MaskInvalid | 1;
+					} else if (us[2] == 0xbf) {
+						if (us[3] > 0xbf) {
+							return UTF8MaskInvalid | 1;
+						}
+					}
+				}
+			} else if ((*us == 0xf0) && ((us[1] & 0xf0) == 0x80)) {
+				// Overlong
+				return UTF8MaskInvalid | 1;
+			}
+			return 4;
+		} else {
+			return UTF8MaskInvalid | 1;
+		}
+	} else if (*us >= 0xE0) {
+		// 3 bytes
+		if (len < 3)
+			return true;
+		if (GoodTrailByte(us[1]) && GoodTrailByte(us[2])) {
+			if ((*us == 0xe0) && ((us[1] & 0xe0) == 0x80)) {
+				// Overlong
+				return UTF8MaskInvalid | 1;
+			}
+			if ((*us == 0xed) && ((us[1] & 0xe0) == 0xa0)) {
+				// Surrogate
+				return UTF8MaskInvalid | 1;
+			}
+			if ((*us == 0xef) && (us[1] == 0xbf) && (us[2] == 0xbe)) {
+				// U+FFFE non-character - 3 bytes long
+				return UTF8MaskInvalid | 3;
+			}
+			if ((*us == 0xef) && (us[1] == 0xbf) && (us[2] == 0xbf)) {
+				// U+FFFF non-character - 3 bytes long
+				return UTF8MaskInvalid | 3;
+			}
+			return 3;
+		} else {
+			return UTF8MaskInvalid | 1;
+		}
+	} else if (*us >= 0xC2) {
+		// 2 bytes
+		if (len < 2)
+			return UTF8MaskInvalid | 1;
+		if (GoodTrailByte(us[1])) {
+			return 2;
+		} else {
+			return UTF8MaskInvalid | 1;
+		}
+	} else if (*us >= 0xC0) {
+		// Overlong encoding
+		return UTF8MaskInvalid | 1;
+	} else {
+		// Trail byte
+		return UTF8MaskInvalid | 1;
+	}
+}
+
+SRepresentation *SpecialRepresentations::Representation(const char *s, int maxLen) {
+	int utf8status = Document_ClassifyUTF8(reinterpret_cast<const unsigned char *>(s), maxLen);
+	if (!(utf8status & UTF8MaskInvalid)) {
+		std::string ss(s, utf8status & UTF8MaskWidth);
+		MapRepresentation::iterator it = mapReprs.find(ss);
+		if (it != mapReprs.end()) {
+			return &(it->second);
+		}
+	}
+	return 0;
+}
+
+bool SpecialRepresentations::Contains(const char *s, int len) const {
+	std::string ss(s, len);
+	MapRepresentation::const_iterator it = mapReprs.find(ss);
+	return it != mapReprs.end();
+}
+
 void BreakFinder::Insert(int val) {
 	// Expand if needed
 	if (saeLen >= saeSize) {
@@ -393,7 +503,7 @@
 }
 
 BreakFinder::BreakFinder(LineLayout *ll_, int lineStart_, int lineEnd_, int posLineStart_,
-	int xStart, bool breakForSelection, Document *pdoc_) :
+	int xStart, bool breakForSelection, Document *pdoc_, SpecialRepresentations *preprs_) :
 	ll(ll_),
 	lineStart(lineStart_),
 	lineEnd(lineEnd_),
@@ -404,7 +514,8 @@
 	saeCurrentPos(0),
 	saeNext(0),
 	subBreak(-1),
-	pdoc(pdoc_) {
+	pdoc(pdoc_),
+	preprs(preprs_) {
 	saeSize = 8;
 	selAndEdge = new int[saeSize];
 	for (unsigned int j=0; j < saeSize; j++) {
@@ -446,6 +557,19 @@
 			Insert(pos-1);
 			Insert(pos);
 		}
+		if (preprs && !preprs->mapReprs.empty()) {
+			for (int posRepr=0; posRepr<lineEnd;) {
+				int utf8status = Document_ClassifyUTF8(reinterpret_cast<unsigned char *>(ll->chars) + posRepr, lineEnd - posRepr);
+				int lenCharacter = utf8status & UTF8MaskWidth;
+				if (!(utf8status & UTF8MaskInvalid)) {
+					if (preprs->Contains(ll->chars + posRepr, lenCharacter)) {
+						Insert(posRepr - 1);
+						Insert(posRepr + lenCharacter - 1);
+					}
+				}
+				posRepr += utf8status & UTF8MaskWidth;
+			}
+		}
 	}
 	saeNext = (saeLen > 0) ? selAndEdge[0] : -1;
 }
diff -r 597445a1063c src/PositionCache.h
--- a/src/PositionCache.h	Mon Apr 30 12:48:10 2012 +1000
+++ b/src/PositionCache.h	Thu May 03 10:59:40 2012 +1000
@@ -115,6 +115,30 @@
 	void ResetClock();
 };
 
+class SRepresentation {
+public:
+	std::string stringRep;
+	int height;
+	int width;
+	std::vector<unsigned char> pixelBytes;
+	SRepresentation(const char *value="") : stringRep(value), height(0), width(0) {
+	}
+	SRepresentation(int width_, int height_, const unsigned char *pixels_) : height(height_), width(width_) {
+		pixelBytes.assign(pixels_, pixels_ + (width * height * 4));
+	}
+};
+
+typedef std::map<std::string, SRepresentation> MapRepresentation;
+
+class SpecialRepresentations {
+public:
+	MapRepresentation mapReprs;
+	void SetRepresentation(const char *key, const char *value);
+	void SetRepresentation(const char *key, int width, int height, const unsigned char *pixels);
+	SRepresentation *Representation(const char *s, int maxLen);
+	bool Contains(const char *s, int len) const;
+};
+
 // Class to break a line of text into shorter runs at sensible places.
 class BreakFinder {
 	LineLayout *ll;
@@ -129,6 +153,7 @@
 	int saeNext;
 	int subBreak;
 	Document *pdoc;
+	SpecialRepresentations *preprs;
 	void Insert(int val);
 public:
 	// If a whole run is longer than lengthStartSubdivision then subdivide
@@ -137,7 +162,7 @@
 	// Try to make each subdivided run lengthEachSubdivision or shorter.
 	enum { lengthEachSubdivision = 100 };
 	BreakFinder(LineLayout *ll_, int lineStart_, int lineEnd_, int posLineStart_,
-		int xStart, bool breakForSelection, Document *pdoc_);
+		int xStart, bool breakForSelection, Document *pdoc_, SpecialRepresentations *preprs_);
 	~BreakFinder();
 	int First() const;
 	int Next();
