Add support for AW5 inverse and MouseText

AppleWorks 5 added inverse text and MouseText.  We can handle inverse
text with RTF features, and convert MouseText to something vaguely
similar in the Unicode symbol set.
This commit is contained in:
Andy McFadden 2017-11-11 14:53:28 -08:00
parent 640959dad5
commit 35a897b248
3 changed files with 156 additions and 21 deletions

View File

@ -24,6 +24,20 @@
* array of line records
* $ff $ff
* optional tags
*
* AppleWorks 5.0 introduced inverse and MouseText characters.
* These just use previously-unused byte ranges. The full set
* of values is thus:
* 00-1f special
* 20-7f plain ASCII
* 80-9f inverse upper (map to 40-5f)
* a0-bf inverse symbols/numbers (map to 20-3f)
* c0-df MouseText
* e0-ff inverse lower (map to 60-7f)
*
* We can output MouseText as Unicode symbols. Inverse text can use the Rich
* Text "highlight" feature; the "background color" feature doesn't seem to
* have any effect.
*/
/*
@ -308,6 +322,7 @@ int ReformatAWP::HandleTextRecord(uint8_t lineRecData,
uint8_t byteCountPlusCR;
int byteCount = lineRecData;
bool noOutput = false;
bool inverse = false;
int ic;
tabFlags = Read8(pSrcPtr, pLength);
@ -372,40 +387,40 @@ int ReformatAWP::HandleTextRecord(uint8_t lineRecData,
break;
case kSpecialCharEnterKeyboard:
if (fShowEmbeds) {
RTFSetColor(kColorBlue);
TextColor oldColor = RTFSetColor(kColorBlue);
BufPrintf("<kdb-entry>");
RTFSetColor(kColorNone);
RTFSetColor(oldColor);
}
break;
case kSpecialCharPrintPageNumber:
if (fShowEmbeds) {
RTFSetColor(kColorBlue);
TextColor oldColor = RTFSetColor(kColorBlue);
BufPrintf("<page#>");
RTFSetColor(kColorNone);
RTFSetColor(oldColor);
}
break;
case kSpecialCharStickySpace:
/* MSWord uses "\~", but RichEdit ignores that */
BufPrintf(" ");
BufPrintf("\u00a0"); // Unicode NO-BREAK SPACE
break;
case kSpecialCharMailMerge:
if (fShowEmbeds) {
RTFSetColor(kColorBlue);
TextColor oldColor = RTFSetColor(kColorBlue);
BufPrintf("<mail-merge>");
RTFSetColor(kColorNone);
RTFSetColor(oldColor);
}
case kSpecialCharPrintDate:
if (fShowEmbeds) {
RTFSetColor(kColorBlue);
TextColor oldColor = RTFSetColor(kColorBlue);
BufPrintf("<date>");
RTFSetColor(kColorNone);
RTFSetColor(oldColor);
}
break;
case kSpecialCharPrintTime:
if (fShowEmbeds) {
RTFSetColor(kColorBlue);
TextColor oldColor = RTFSetColor(kColorBlue);
BufPrintf("<time>");
RTFSetColor(kColorNone);
RTFSetColor(oldColor);
}
break;
case kSpecialCharTab:
@ -421,19 +436,61 @@ int ReformatAWP::HandleTextRecord(uint8_t lineRecData,
default:
LOGI(" AWP unhandled special char 0x%02x", ic);
if (fShowEmbeds) {
RTFSetColor(kColorBlue);
TextColor oldColor = RTFSetColor(kColorBlue);
BufPrintf("^");
RTFSetColor(kColorNone);
RTFSetColor(oldColor);
}
}
} else {
if (fUseRTF)
RTFPrintChar(ic);
else
// Character.
bool wantInverse = false;
uint16_t mtLow = 0, mtHigh = 0;
if (ic >= 0x80 && ic <= 0x9f) {
// inverse upper; map 100x xxxx --> 010x xxxx
ic ^= 0xc0;
wantInverse = true;
} else if (ic >= 0xa0 && ic <= 0xbf || ic >= 0xe0 && ic <= 0xff) {
// inverse symbols; map 101x xxxx --> 001x xxxx
// inverse lower; map 111x xxxx --> 011x xxxx
ic ^= 0x80;
wantInverse = true;
} else if (ic >= 0xc0 && ic <= 0xdf) {
// MouseText characters
MouseTextToUTF16(ic & 0x1f, &mtLow, &mtHigh);
ic = '?';
} else {
// plain ASCII
}
if (wantInverse && !inverse) {
inverse = true;
RTFInverseOn();
} else if (!wantInverse && inverse) {
inverse = false;
RTFInverseOff();
}
if (fUseRTF) {
if (mtLow != 0) {
if (mtHigh != 0) {
RTFPrintUTF16Char(mtHigh);
}
RTFPrintUTF16Char(mtLow);
} else {
RTFPrintChar(ic);
}
} else {
// Plain text output.
BufPrintf("%c", PrintableChar(ic));
}
}
}
if (inverse) {
RTFInverseOff();
}
/* if there's a carriage return at the end of the line, output it now */
if (byteCountPlusCR & kCRatEOL) {
RTFNewPara();

View File

@ -241,6 +241,28 @@ void ReformatText::RTFUnderlineOff(void)
}
}
void ReformatText::RTFInverseOn(void)
{
if (fInverseEnabled)
return;
if (fUseRTF) {
RTFSetColor(TextColor::kColorWhite);
BufPrintf("\\highlight1 "); // black
fInverseEnabled = true;
}
}
void ReformatText::RTFInverseOff(void)
{
if (!fInverseEnabled)
return;
if (fUseRTF) {
RTFSetColor(TextColor::kColorNone);
BufPrintf("\\highlight0 ");
fInverseEnabled = false;
}
}
void ReformatText::RTFOutlineOn(void)
{
if (fOutlineEnabled)
@ -321,14 +343,14 @@ void ReformatText::RTFSuperscriptOff(void)
}
}
void ReformatText::RTFSetColor(TextColor color)
ReformatText::TextColor ReformatText::RTFSetColor(TextColor color)
{
if (color == fTextColor)
return;
if (fUseRTF) {
TextColor oldColor = fTextColor;
if (color != fTextColor && fUseRTF) {
BufPrintf("\\cf%d ", color);
fTextColor = color;
}
return oldColor;
}
/*
@ -721,6 +743,53 @@ void ReformatText::BufHexDump(const uint8_t* srcBuf, long srcLen)
}
}
// Thanks to: http://hoop-la.ca/apple2/docs/mousetext/unicode.html
static const uint32_t gMouseTextConv[32] = {
0xd83cdf4e, // 00 U+1f34e RED APPLE
0xd83cdf4f, // 01 U+1f34f GREEN APPLE
//0x00002316, // 02 U+2316 POSITION INDICATOR
0x000025c4, // 02 U+25c4 BLACK LEFT-POINTING POINTER
0x000023f3, // 03 U+23f3 HOURGLASS WITH FLOWING SAND
0x00002713, // 04 U+2713 CHECK MARK
0x00002705, // 05 U+2705 WHITE HEAVY CHECK MARK
0x000023ce, // 06 U+23ce RETURN SYMBOL
//0xd83cdf54, // 07 U+1f354 HAMBURGER
0x00002630, // 07 U+2630 TRIGRAM FOR HEAVEN (actually want 4 lines, not 3)
0x00002190, // 08 U+2190 LEFTWARDS ARROW
0x00002026, // 09 U+2026 HORIZONTAL ELLIPSIS
0x00002193, // 0a U+2193 DOWNWARDS ARROW
0x00002191, // 0b U+2191 UPWARDS ARROW
0x00002594, // 0c U+2594 UPPER ONE EIGHTH BLOCK
0x000021b5, // 0d U+21b5 DOWNWARDS ARROW WITH CORNER LEFTWARDS
0x00002589, // 0e U+2589 LEFT SEVEN EIGHTHS BLOCK
0x000021e4, // 0f U+21e4 LEFTWARDS ARROW TO BAR
0x000021e5, // 10 U+21e5 RIGHTWARDS ARROW TO BAR
0x00002913, // 11 U+2913 DOWNWARDS ARROW TO BAR
0x00002912, // 12 U+2912 UPWARDS ARROW TO BAR
0x00002500, // 13 U+2500 BOX DRAWINGS LIGHT HORIZONTAL
0x0000231e, // 14 U+231e BOTTOM LEFT CORNER
0x00002192, // 15 U+2192 UPWARDS ARROW TO BAR
//0xd83dde7e, // 16 U+1f67e CHECKER BOARD
0x00002591, // 16 U+2591 LIGHT SHADE
//0xd83dde7f, // 17 U+1f67f REVERSE CHECKER BOARD
0x00002592, // 17 U+2592 MEDIUM SHADE
0xd83ddcc1, // 18 U+1f4c1 FILE FOLDER
0xd83ddcc2, // 19 U+1f4c2 OPEN FILE FOLDER
0x00002595, // 1a U+2595 RIGHT ONE EIGHTH BLOCK
0x00002666, // 1b U+2666 BLACK DIAMOND SUIT
0x0000203e, // 1c U+203e OVERLINE -- wrong, want top/bottom
0x0000256c, // 1d U+256c BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
//0xd83ddcbe, // 1e U+1f4be FLOPPY DISK
0x000022a1, // 1e U+22a1 SQUARED DOT OPERATOR (seems better than 25a3)
0x0000258f, // 1f U+258f LEFT ONE EIGHTH BLOCK
};
void ReformatText::MouseTextToUTF16(uint8_t mtVal, uint16_t* pLow, uint16_t* pHigh) {
ASSERT(mtVal < 32);
*pLow = gMouseTextConv[mtVal] & 0xffff;
*pHigh = gMouseTextConv[mtVal] >> 16;
}
/*
* ==========================================================================

View File

@ -198,6 +198,7 @@ public:
fBoldEnabled(false),
fItalicEnabled(false),
fUnderlineEnabled(false),
fInverseEnabled(false),
fOutlineEnabled(false),
fShadowEnabled(false),
fSuperscriptEnabled(false),
@ -305,6 +306,8 @@ protected:
void RTFItalicOff(void);
void RTFUnderlineOn(void);
void RTFUnderlineOff(void);
void RTFInverseOn(void);
void RTFInverseOff(void);
void RTFParaLeft(void);
void RTFParaRight(void);
void RTFParaCenter(void);
@ -317,7 +320,7 @@ protected:
void RTFOutlineOff(void);
void RTFShadowOn(void);
void RTFShadowOff(void);
void RTFSetColor(TextColor color);
TextColor RTFSetColor(TextColor color);
void RTFSetFont(RTFFont font);
void RTFSetFontSize(int points);
void RTFLeftMargin(int margin);
@ -382,6 +385,11 @@ protected:
fExpBuf.Printf("%c", ch);
}
// Converts a MouseText value (0-31) to a 16-bit UTF-16 value. If
// the character is in the BMP then only low is used. If it requires
// encoding as a surrogate pair, high will be nonzero.
void MouseTextToUTF16(uint8_t mtVal, uint16_t* pLow, uint16_t* pHigh);
private:
DECLARE_COPY_AND_OPEQ(ReformatText)
int CreateWorkBuf(void);
@ -394,6 +402,7 @@ private:
bool fBoldEnabled;
bool fItalicEnabled;
bool fUnderlineEnabled;
bool fInverseEnabled;
bool fOutlineEnabled;
bool fShadowEnabled;
bool fSuperscriptEnabled;