mirror of
https://github.com/fadden/ciderpress.git
synced 2024-11-26 02:49:20 +00:00
2adbe9591f
The AWP5 MouseText output is okay for some things but not others. This adds an ASCII conversion, enabled through a preference.
1041 lines
29 KiB
C++
1041 lines
29 KiB
C++
/*
|
|
* CiderPress
|
|
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
|
|
* See the file LICENSE for distribution terms.
|
|
*/
|
|
/*
|
|
* Reformatter base class implementation.
|
|
*/
|
|
#include "StdAfx.h"
|
|
#include "ReformatBase.h"
|
|
#include <math.h>
|
|
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* ReformatText
|
|
* ==========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Set the output format and buffer.
|
|
*
|
|
* Clears our work buffer pointer so we don't free it.
|
|
*/
|
|
void ReformatText::SetResultBuffer(ReformatOutput* pOutput, bool multiFont)
|
|
{
|
|
char* buf;
|
|
long len;
|
|
fExpBuf.SeizeBuffer(&buf, &len);
|
|
pOutput->SetTextBuf(buf, len, true);
|
|
|
|
if (pOutput->GetTextBuf() == NULL) {
|
|
/*
|
|
* Force "raw" mode if there's no output. This can happen if we,
|
|
* say, try to format an empty file as a hex dump. We never
|
|
* produce any output, so no buffer gets allocated.
|
|
*
|
|
* We set the mode to "raw" so that applications can assume that
|
|
* results of type "text" actually have text to look at -- though
|
|
* it's possible the length will be zero, we promise that there'll
|
|
* be a buffer there. I'm not sure it's important to do this,
|
|
* but it does reduce the #of situations in which we have to
|
|
* worry about NULL pointers.
|
|
*/
|
|
pOutput->SetOutputKind(ReformatOutput::kOutputRaw);
|
|
LOGI("ReformatText returning a null pointer");
|
|
} else {
|
|
if (fUseRTF)
|
|
pOutput->SetOutputKind(ReformatOutput::kOutputRTF);
|
|
else
|
|
pOutput->SetOutputKind(ReformatOutput::kOutputText);
|
|
}
|
|
|
|
if (fUseRTF && multiFont)
|
|
pOutput->SetMultipleFontsFlag(true);
|
|
}
|
|
|
|
/*
|
|
* Output the RTF header.
|
|
*
|
|
* The color table is the standard MS Word color table, except that entry
|
|
* #17 (dark grey) has been lightened from (51,51,51) because it's nearly
|
|
* indistinguishable from black on the screen.
|
|
*
|
|
* The default font is Courier New (\f0) at 10 points (\fs20).
|
|
*/
|
|
void ReformatText::RTFBegin(int flags)
|
|
{
|
|
// static const char* rtfHdr =
|
|
//"{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fmodern\\fprq1\\fcharset0 Courier New;}}\r\n"
|
|
//"\\viewkind4\\uc1\\pard\\f0\\fs20 ";
|
|
|
|
static const char* rtfHdrStart =
|
|
"{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033\\deflangfe1033{\\fonttbl"
|
|
"{\\f0\\fmodern\\fprq1\\fcharset0 Courier New;}"
|
|
"{\\f1\\froman\\fprq2\\fcharset0 Times New Roman;}"
|
|
"{\\f2\\fswiss\\fprq2\\fcharset0 Arial;}"
|
|
"{\\f3\\froman\\fprq2\\fcharset2 Symbol;}"
|
|
"}\r\n";
|
|
|
|
static const char* rtfColorTable =
|
|
"{\\colortbl;"
|
|
"\\red0\\green0\\blue0;\\red0\\green0\\blue255;\\red0\\green255\\blue255;\\red0\\green255\\blue0;"
|
|
"\\red255\\green0\\blue255;\\red255\\green0\\blue0;\\red255\\green255\\blue0;\\red255\\green255\\blue255;"
|
|
"\\red0\\green0\\blue128;\\red0\\green128\\blue128;\\red0\\green128\\blue0;\r\n"
|
|
"\\red128\\green0\\blue128;\\red128\\green0\\blue0;\\red128\\green128\\blue0;\\red128\\green128\\blue128;"
|
|
"\\red192\\green192\\blue192;\\red64\\green64\\blue64;\\red255\\green153\\blue0;}\r\n";
|
|
|
|
static const char* rtfHdrEnd =
|
|
"\\viewkind4\\uc1\\pard\\f0\\fs20 ";
|
|
|
|
if (fUseRTF) {
|
|
BufPrintf("%s", rtfHdrStart);
|
|
if ((flags & kRTFFlagColorTable) != 0)
|
|
BufPrintf("%s", rtfColorTable);
|
|
BufPrintf("%s", rtfHdrEnd);
|
|
}
|
|
|
|
fPointSize = 10;
|
|
}
|
|
|
|
/*
|
|
* Output the RTF footer.
|
|
*/
|
|
void ReformatText::RTFEnd(void)
|
|
{
|
|
if (fUseRTF) BufPrintf("}\r\n%c", '\0');
|
|
}
|
|
|
|
/*
|
|
* Output RTF paragraph definition marker. Do this every time we change some
|
|
* aspect of paragraph formatting, such as margins or justification.
|
|
*/
|
|
void ReformatText::RTFSetPara(void)
|
|
{
|
|
if (!fUseRTF)
|
|
return;
|
|
|
|
BufPrintf("\\pard\\nowidctlpar");
|
|
|
|
if (fLeftMargin != 0 || fRightMargin != 0) {
|
|
/* looks like RTF thinks we're getting 12 chars per inch? */
|
|
if (fLeftMargin != 0)
|
|
BufPrintf("\\li%d",
|
|
(int) (fLeftMargin * (kRTFUnitsPerInch/12)));
|
|
if (fLeftMargin != 0)
|
|
BufPrintf("\\ri%d",
|
|
(int) (fRightMargin * (kRTFUnitsPerInch/12)));
|
|
}
|
|
|
|
switch (fJustified) {
|
|
case kJustifyLeft: break;
|
|
case kJustifyRight: BufPrintf("\\qr"); break;
|
|
case kJustifyCenter: BufPrintf("\\qc"); break;
|
|
case kJustifyFull: BufPrintf("\\qj"); break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
// Ideally we'd suppress this if the next thing is an RTF
|
|
// formatting command, esp. "\\par".
|
|
BufPrintf(" ");
|
|
}
|
|
|
|
/*
|
|
* Output a new paragraph marker.
|
|
*
|
|
* If you're producing RTF output, this is the right way to output an
|
|
* end-of-line character.
|
|
*/
|
|
void ReformatText::RTFNewPara(void)
|
|
{
|
|
if (fUseRTF)
|
|
BufPrintf("\\par\r\n");
|
|
else
|
|
BufPrintf("\r\n");
|
|
}
|
|
|
|
|
|
/*
|
|
* Insert a page break. This isn't supported by the Rich Edit control,
|
|
* so it won't appear in CiderPress or WordPad, but it will come out in
|
|
* Microsoft Word if you extract to a file.
|
|
*/
|
|
void ReformatText::RTFPageBreak(void)
|
|
{
|
|
if (fUseRTF)
|
|
BufPrintf("\\page ");
|
|
}
|
|
|
|
/*
|
|
* RTF tab character.
|
|
*/
|
|
void ReformatText::RTFTab(void)
|
|
{
|
|
if (fUseRTF)
|
|
BufPrintf("\\tab ");
|
|
}
|
|
|
|
/*
|
|
* Minor formatting.
|
|
*/
|
|
void ReformatText::RTFBoldOn(void)
|
|
{
|
|
if (fBoldEnabled)
|
|
return;
|
|
if (fUseRTF) {
|
|
BufPrintf("\\b ");
|
|
fBoldEnabled = true;
|
|
}
|
|
}
|
|
|
|
void ReformatText::RTFBoldOff(void)
|
|
{
|
|
if (!fBoldEnabled)
|
|
return;
|
|
if (fUseRTF) {
|
|
BufPrintf("\\b0 ");
|
|
fBoldEnabled = false;
|
|
}
|
|
}
|
|
|
|
void ReformatText::RTFItalicOn(void)
|
|
{
|
|
if (fItalicEnabled)
|
|
return;
|
|
if (fUseRTF) {
|
|
BufPrintf("\\i ");
|
|
fItalicEnabled = true;
|
|
}
|
|
}
|
|
|
|
void ReformatText::RTFItalicOff(void)
|
|
{
|
|
if (!fItalicEnabled)
|
|
return;
|
|
if (fUseRTF) {
|
|
BufPrintf("\\i0 ");
|
|
fItalicEnabled = false;
|
|
}
|
|
}
|
|
|
|
void ReformatText::RTFUnderlineOn(void)
|
|
{
|
|
if (fUnderlineEnabled)
|
|
return;
|
|
if (fUseRTF) {
|
|
BufPrintf("\\ul ");
|
|
fUnderlineEnabled = true;
|
|
}
|
|
}
|
|
|
|
void ReformatText::RTFUnderlineOff(void)
|
|
{
|
|
if (!fUnderlineEnabled)
|
|
return;
|
|
if (fUseRTF) {
|
|
BufPrintf("\\ulnone ");
|
|
fUnderlineEnabled = false;
|
|
}
|
|
}
|
|
|
|
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)
|
|
return;
|
|
if (fUseRTF) {
|
|
BufPrintf("\\outl ");
|
|
fOutlineEnabled = true;
|
|
}
|
|
}
|
|
|
|
void ReformatText::RTFOutlineOff(void)
|
|
{
|
|
if (!fOutlineEnabled)
|
|
return;
|
|
if (fUseRTF) {
|
|
BufPrintf("\\outl0 ");
|
|
fOutlineEnabled = false;
|
|
}
|
|
}
|
|
|
|
void ReformatText::RTFShadowOn(void)
|
|
{
|
|
if (fShadowEnabled)
|
|
return;
|
|
if (fUseRTF) {
|
|
BufPrintf("\\shad ");
|
|
fShadowEnabled = true;
|
|
}
|
|
}
|
|
|
|
void ReformatText::RTFShadowOff(void)
|
|
{
|
|
if (!fShadowEnabled)
|
|
return;
|
|
if (fUseRTF) {
|
|
BufPrintf("\\shad0 ");
|
|
fShadowEnabled = false;
|
|
}
|
|
}
|
|
|
|
void ReformatText::RTFSubscriptOn(void)
|
|
{
|
|
if (fSubscriptEnabled)
|
|
return;
|
|
if (fUseRTF) {
|
|
BufPrintf("\\sub ");
|
|
fSubscriptEnabled = true;
|
|
}
|
|
}
|
|
|
|
void ReformatText::RTFSubscriptOff(void)
|
|
{
|
|
if (!fSubscriptEnabled)
|
|
return;
|
|
if (fUseRTF) {
|
|
BufPrintf("\\nosupersub ");
|
|
fSubscriptEnabled = false;
|
|
}
|
|
}
|
|
|
|
void ReformatText::RTFSuperscriptOn(void)
|
|
{
|
|
if (fSuperscriptEnabled)
|
|
return;
|
|
if (fUseRTF) {
|
|
BufPrintf("\\super ");
|
|
fSuperscriptEnabled = true;
|
|
}
|
|
}
|
|
|
|
void ReformatText::RTFSuperscriptOff(void)
|
|
{
|
|
if (!fSuperscriptEnabled)
|
|
return;
|
|
if (fUseRTF) {
|
|
BufPrintf("\\nosupersub ");
|
|
fSuperscriptEnabled = false;
|
|
}
|
|
}
|
|
|
|
ReformatText::TextColor ReformatText::RTFSetColor(TextColor color)
|
|
{
|
|
TextColor oldColor = fTextColor;
|
|
if (color != fTextColor && fUseRTF) {
|
|
BufPrintf("\\cf%d ", color);
|
|
fTextColor = color;
|
|
}
|
|
return oldColor;
|
|
}
|
|
|
|
/*
|
|
* Change paragraph formatting.
|
|
*/
|
|
void ReformatText::RTFParaLeft(void)
|
|
{
|
|
if (fJustified != kJustifyLeft) {
|
|
fJustified = kJustifyLeft;
|
|
RTFSetPara();
|
|
}
|
|
}
|
|
|
|
void ReformatText::RTFParaRight(void)
|
|
{
|
|
if (fJustified != kJustifyRight) {
|
|
fJustified = kJustifyRight;
|
|
RTFSetPara();
|
|
}
|
|
}
|
|
|
|
void ReformatText::RTFParaCenter(void)
|
|
{
|
|
if (fJustified != kJustifyCenter) {
|
|
fJustified = kJustifyCenter;
|
|
RTFSetPara();
|
|
}
|
|
}
|
|
|
|
void ReformatText::RTFParaJustify(void)
|
|
{
|
|
if (fJustified != kJustifyFull) {
|
|
fJustified = kJustifyFull;
|
|
RTFSetPara();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Page margins, in 1/10th inches.
|
|
*/
|
|
void ReformatText::RTFLeftMargin(int margin)
|
|
{
|
|
//LOGI("+++ Left margin now %d", margin);
|
|
fLeftMargin = margin;
|
|
RTFSetPara();
|
|
}
|
|
|
|
void ReformatText::RTFRightMargin(int margin)
|
|
{
|
|
//LOGI("+++ Right margin now %d", margin);
|
|
fRightMargin = margin;
|
|
RTFSetPara();
|
|
}
|
|
|
|
/*
|
|
* Switch to a different font size.
|
|
*/
|
|
void ReformatText::RTFSetFontSize(int points)
|
|
{
|
|
if (fUseRTF && fPointSize != points)
|
|
BufPrintf("\\fs%d ", points * 2);
|
|
fPointSize = points;
|
|
}
|
|
/*
|
|
* Switch to a different font.
|
|
*/
|
|
void ReformatText::RTFSetFont(RTFFont font)
|
|
{
|
|
if (fUseRTF)
|
|
BufPrintf("\\f%d ", font);
|
|
}
|
|
|
|
/*
|
|
* Set the font by specifying a IIgs QuickDraw II font family number.
|
|
*/
|
|
void ReformatText::RTFSetGSFont(uint16_t family)
|
|
{
|
|
float newMult;
|
|
|
|
if (!fUseRTF)
|
|
return;
|
|
|
|
/*
|
|
* Apple II fonts seem to be about 1.5x in a WYSIWYG way, except
|
|
* for Times, which is about 1:1.
|
|
*/
|
|
switch (family) {
|
|
case kGSFontTimes:
|
|
RTFSetFont(kFontTimesRoman);
|
|
newMult = 0.9f;
|
|
break;
|
|
case kGSFontNewYork:
|
|
RTFSetFont(kFontTimesRoman);
|
|
newMult = 1.1f;
|
|
break;
|
|
|
|
case kGSFontSymbol:
|
|
RTFSetFont(kFontSymbol);
|
|
newMult = 1.0f;
|
|
break;
|
|
|
|
case kGSFontMonaco:
|
|
RTFSetFont(kFontCourierNew);
|
|
newMult = 0.80f;
|
|
break;
|
|
case kGSFontCourier:
|
|
case kGSFontPCMonospace:
|
|
case kGSFontAppleM:
|
|
case kGSFontGenesys:
|
|
RTFSetFont(kFontCourierNew);
|
|
newMult = 1.5f;
|
|
break;
|
|
|
|
case kGSFontClassical:
|
|
case kGSFontGenoa:
|
|
case kGSFontWestern:
|
|
RTFSetFont(kFontArial);
|
|
newMult = 0.80f;
|
|
break;
|
|
case kGSFontChicago:
|
|
case kGSFontVenice:
|
|
case kGSFontGeneva:
|
|
case kGSFontStarfleet:
|
|
case kGSFontUnknown1:
|
|
case kGSFontUnknown2:
|
|
RTFSetFont(kFontArial);
|
|
newMult = 1.0f;
|
|
break;
|
|
case kGSFontLondon:
|
|
case kGSFontAthens:
|
|
case kGSFontSanFran:
|
|
case kGSFontShaston:
|
|
case kGSFontToronto:
|
|
case kGSFontCairo:
|
|
case kGSFontLosAngeles:
|
|
case kGSFontHelvetica:
|
|
case kGSFontTaliesin:
|
|
RTFSetFont(kFontArial);
|
|
newMult = 1.5f;
|
|
break;
|
|
default:
|
|
LOGI("Unrecognized font family 0x%04x, using Arial", family);
|
|
RTFSetFont(kFontArial);
|
|
newMult = 1.0f;
|
|
break;
|
|
}
|
|
|
|
if (newMult != fGSFontSizeMult) {
|
|
fGSFontSizeMult = newMult;
|
|
RTFSetGSFontSize(fPreMultPointSize);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set the font size of a IIgs font. We factor the size multiplier in.
|
|
*
|
|
* BUG: we should track the state of the "underline" mode, and turn it
|
|
* on and off based on the font size (8-point fonts aren't underlined).
|
|
*/
|
|
void ReformatText::RTFSetGSFontSize(int points)
|
|
{
|
|
RTFSetFontSize((int) roundf(points * fGSFontSizeMult));
|
|
|
|
fPreMultPointSize = points;
|
|
}
|
|
|
|
/*
|
|
* Set bold/italic/underline etc.
|
|
*
|
|
* Note that "Teach" does not show underlining on text that is 8 points
|
|
* or smaller. We have to emulate this behavior or some documents, such
|
|
* as ModZap's "MZ.Manual", look terrible.
|
|
*
|
|
* Set the font size before calling here.
|
|
*
|
|
* Some characters, such as '=' in Shaston 8, look the same in
|
|
* bold as they do in plain. This doesn't hold true for Windows
|
|
* fonts, so we're going to look different in some circumstances.
|
|
*/
|
|
void ReformatText::RTFSetGSFontStyle(uint8_t qdStyle)
|
|
{
|
|
if (!fUseRTF)
|
|
return;
|
|
|
|
if ((qdStyle & kQDStyleBold) != 0) {
|
|
RTFBoldOn();
|
|
} else {
|
|
RTFBoldOff();
|
|
}
|
|
if ((qdStyle & kQDStyleItalic) != 0) {
|
|
RTFItalicOn();
|
|
} else {
|
|
RTFItalicOff();
|
|
}
|
|
if ((qdStyle & kQDStyleUnderline) != 0 && fPreMultPointSize > 8) {
|
|
RTFUnderlineOn();
|
|
} else {
|
|
RTFUnderlineOff();
|
|
}
|
|
if ((qdStyle & kQDStyleOutline) != 0) {
|
|
RTFOutlineOn();
|
|
} else {
|
|
RTFOutlineOff();
|
|
}
|
|
if ((qdStyle & kQDStyleShadow) != 0) {
|
|
RTFShadowOn();
|
|
} else {
|
|
RTFShadowOff();
|
|
}
|
|
if ((qdStyle & kQDStyleSuperscript) != 0) {
|
|
RTFSuperscriptOn();
|
|
} else {
|
|
RTFSuperscriptOff();
|
|
}
|
|
if ((qdStyle & kQDStyleSubscript) != 0) {
|
|
RTFSubscriptOn();
|
|
} else {
|
|
RTFSubscriptOff();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
void
|
|
ReformatText::RTFProportionalOn(void) {
|
|
if (fUseRTF)
|
|
BufPrintf("\\f%d ", kFontTimesRoman);
|
|
}
|
|
void
|
|
ReformatText::RTFProportionalOff(void) {
|
|
if (fUseRTF)
|
|
BufPrintf("\\f%d ", kFontCourierNew);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Convert the EOL markers in a buffer. The output is written to the work
|
|
* buffer. The input buffer may be CR, LF, or CRLF.
|
|
*
|
|
* If "stripHiBits" is set, the high bit of each character is cleared before
|
|
* the value is considered.
|
|
*/
|
|
void ReformatText::ConvertEOL(const uint8_t* srcBuf, long srcLen,
|
|
bool stripHiBits)
|
|
{
|
|
/* Compatibility - assume we're not stripping nulls */
|
|
ConvertEOL(srcBuf, srcLen, stripHiBits, false);
|
|
}
|
|
|
|
/*
|
|
* Convert the EOL markers in a buffer. The output is written to the work
|
|
* buffer. The input buffer may be CR, LF, or CRLF.
|
|
*
|
|
* If "stripHiBits" is set, the high bit of each character is cleared before
|
|
* the value is considered.
|
|
*
|
|
* If "stripNulls" is true, no null values will make it through.
|
|
*/
|
|
void ReformatText::ConvertEOL(const uint8_t* srcBuf, long srcLen,
|
|
bool stripHiBits, bool stripNulls)
|
|
{
|
|
uint8_t ch;
|
|
int mask;
|
|
|
|
assert(!fUseRTF); // else we have to use RTFPrintChar
|
|
|
|
if (stripHiBits)
|
|
mask = 0x7f;
|
|
else
|
|
mask = 0xff;
|
|
|
|
/*
|
|
* Could probably speed this up by taking things a line at a time,
|
|
* but this is fast enough and much more straightforward.
|
|
*/
|
|
while (srcLen) {
|
|
ch = (*srcBuf++) & mask;
|
|
srcLen--;
|
|
|
|
if (ch == '\r') {
|
|
/* got CR, check for CRLF */
|
|
if (srcLen != 0 && ((*srcBuf) & mask) == '\n') {
|
|
srcBuf++;
|
|
srcLen--;
|
|
}
|
|
BufPrintf("\r\n");
|
|
} else if (ch == '\n') {
|
|
BufPrintf("\r\n");
|
|
} else {
|
|
/* Strip out null bytes if requested */
|
|
if ((stripNulls && ch != 0x00) || !stripNulls)
|
|
BufPrintf("%c", ch);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Write a hex dump into the buffer.
|
|
*/
|
|
void ReformatText::BufHexDump(const uint8_t* srcBuf, long srcLen)
|
|
{
|
|
const uint8_t* origSrcBuf = srcBuf;
|
|
char chBuf[17];
|
|
int i, remLen;
|
|
|
|
ASSERT(srcBuf != NULL);
|
|
ASSERT(srcLen >= 0);
|
|
|
|
chBuf[16] = '\0';
|
|
|
|
while (srcLen > 0) {
|
|
BufPrintf("%08lx: ", srcBuf - origSrcBuf);
|
|
|
|
if (srcLen >= 16) {
|
|
if (!fUseRTF) {
|
|
/* the really easy (and relatively fast) way */
|
|
BufPrintf("%02x %02x %02x %02x %02x %02x %02x %02x "
|
|
"%02x %02x %02x %02x %02x %02x %02x %02x ",
|
|
srcBuf[0], srcBuf[1], srcBuf[2], srcBuf[3],
|
|
srcBuf[4], srcBuf[5], srcBuf[6], srcBuf[7],
|
|
srcBuf[8], srcBuf[9], srcBuf[10], srcBuf[11],
|
|
srcBuf[12], srcBuf[13], srcBuf[14], srcBuf[15]);
|
|
} else {
|
|
/* the fairly easy (and fairly fast) way */
|
|
RTFBoldOn();
|
|
BufPrintf("%02x %02x %02x %02x ",
|
|
srcBuf[0], srcBuf[1], srcBuf[2], srcBuf[3]);
|
|
RTFBoldOff();
|
|
BufPrintf("%02x %02x %02x %02x ",
|
|
srcBuf[4], srcBuf[5], srcBuf[6], srcBuf[7]);
|
|
RTFBoldOn();
|
|
BufPrintf("%02x %02x %02x %02x ",
|
|
srcBuf[8], srcBuf[9], srcBuf[10], srcBuf[11]);
|
|
RTFBoldOff();
|
|
BufPrintf("%02x %02x %02x %02x ",
|
|
srcBuf[12], srcBuf[13], srcBuf[14], srcBuf[15]);
|
|
}
|
|
} else {
|
|
/* the not-so-easy (and not-so-fast) way */
|
|
remLen = srcLen;
|
|
|
|
for (i = 0; i < remLen; i++) {
|
|
if (i == 0 || i == 8)
|
|
RTFBoldOn();
|
|
else if (i == 4 || i == 12)
|
|
RTFBoldOff();
|
|
BufPrintf("%02x ", srcBuf[i]);
|
|
}
|
|
RTFBoldOff();
|
|
for ( ; i < 16; i++)
|
|
BufPrintf(" ");
|
|
|
|
/* blank out the char buf, since we're only filling part in */
|
|
for (i = 0; i < 16; i++)
|
|
chBuf[i] = ' ';
|
|
}
|
|
|
|
bool hosed = false;
|
|
remLen = srcLen;
|
|
if (remLen > 16)
|
|
remLen = 16;
|
|
int i;
|
|
for (i = 0; i < remLen; i++) {
|
|
chBuf[i] = PrintableChar(srcBuf[i]);
|
|
if (fUseRTF &&
|
|
(chBuf[i] == '\\' || chBuf[i] == '{' || chBuf[i] == '}'))
|
|
{
|
|
hosed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hosed) {
|
|
BufPrintf(" %s", chBuf);
|
|
} else {
|
|
/* escaped chars in RTF mode; have to do this one the hard way */
|
|
ASSERT(fUseRTF);
|
|
BufPrintf(" ");
|
|
for (i = 0; i < remLen; i++) {
|
|
RTFPrintChar(srcBuf[i]);
|
|
}
|
|
}
|
|
|
|
RTFNewPara();
|
|
|
|
srcBuf += 16;
|
|
srcLen -= 16;
|
|
}
|
|
}
|
|
|
|
// Thanks to http://hoop-la.ca/apple2/docs/mousetext/unicode.html for Unicode.
|
|
// Thanks to Hugh Hood for extracting the ASCII conversions from SEG.WP.
|
|
static const struct {
|
|
uint32_t unicode; // UTF-16 single code point or surrogate pair
|
|
int8_t ascii; // 7-bit ASCII
|
|
} 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, 'v' }, // 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, 'v' }, // 11 U+2913 DOWNWARDS ARROW TO BAR
|
|
{ 0x00002912, '^' }, // 12 U+2912 UPWARDS ARROW TO BAR
|
|
{ 0x00002500, '-' }, // 13 U+2500 BOX DRAWINGS LIGHT HORIZONTAL
|
|
{ 0x0000231e, 'L' }, // 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, 'O' }, // 1e U+1f4be FLOPPY DISK
|
|
{ 0x000022a1, 'O' }, // 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].unicode & 0xffff;
|
|
*pHigh = gMouseTextConv[mtVal].unicode >> 16;
|
|
}
|
|
int8_t ReformatText::MouseTextToASCII(uint8_t mtVal) {
|
|
ASSERT(mtVal < 32);
|
|
return gMouseTextConv[mtVal].ascii;
|
|
}
|
|
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* ReformatGraphics
|
|
* ==========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Initialize the Apple II color palette, used for Hi-Res and DHR
|
|
* conversions. Could also be used for lo-res mode.
|
|
*/
|
|
void ReformatGraphics::InitPalette(void)
|
|
{
|
|
ASSERT(kPaletteSize == 16);
|
|
|
|
static const RGBQUAD stdPalette[kPaletteSize] = {
|
|
/* blue, green, red, reserved */
|
|
{ 0x00, 0x00, 0x00 }, // $0 black
|
|
{ 0x33, 0x00, 0xdd }, // $1 red (magenta)
|
|
{ 0x99, 0x00, 0x00 }, // $2 dark blue
|
|
{ 0xdd, 0x22, 0xdd }, // $3 purple (violet)
|
|
{ 0x22, 0x77, 0x00 }, // $4 dark green
|
|
{ 0x55, 0x55, 0x55 }, // $5 grey1 (dark)
|
|
{ 0xff, 0x22, 0x22 }, // $6 medium blue
|
|
{ 0xff, 0xaa, 0x66 }, // $7 light blue
|
|
{ 0x00, 0x55, 0x88 }, // $8 brown
|
|
{ 0x00, 0x66, 0xff }, // $9 orange
|
|
{ 0xaa, 0xaa, 0xaa }, // $A grey2 (light)
|
|
{ 0x88, 0x99, 0xff }, // $B pink
|
|
{ 0x00, 0xdd, 0x11 }, // $C green (a/k/a light green)
|
|
{ 0x00, 0xff, 0xff }, // $D yellow
|
|
{ 0x99, 0xff, 0x44 }, // $E aqua
|
|
{ 0xff, 0xff, 0xff }, // $F white
|
|
};
|
|
|
|
memcpy(fPalette, stdPalette, sizeof(fPalette));
|
|
}
|
|
|
|
/*
|
|
* Stuff out DIB into the output fields, and set the appropriate flags.
|
|
*/
|
|
void ReformatGraphics::SetResultBuffer(ReformatOutput* pOutput, MyDIBitmap* pDib)
|
|
{
|
|
ASSERT(pOutput != NULL);
|
|
ASSERT(pDib != NULL);
|
|
pOutput->SetOutputKind(ReformatOutput::kOutputBitmap);
|
|
pOutput->SetDIB(pDib);
|
|
}
|
|
|
|
/*
|
|
* Unpack the Apple PackBytes format.
|
|
*
|
|
* Format is:
|
|
* <flag><data> ...
|
|
*
|
|
* Flag values (first 6 bits of flag byte):
|
|
* 00xxxxxx: (0-63) 1 to 64 bytes follow, all different
|
|
* 01xxxxxx: (0-63) 1 to 64 repeats of next byte
|
|
* 10xxxxxx: (0-63) 1 to 64 repeats of next 4 bytes
|
|
* 11xxxxxx: (0-63) 1 to 64 repeats of next byte taken as 4 bytes
|
|
* (as in 10xxxxxx case)
|
|
*
|
|
* Pass the destination buffer in "dst", source buffer in "src", source
|
|
* length in "srcLen", and expected sizes of output in "dstRem".
|
|
*
|
|
* Returns the number of bytes unpacked on success, negative if the buffer is
|
|
* overfilled.
|
|
*/
|
|
int ReformatGraphics::UnpackBytes(uint8_t* dst, const uint8_t* src,
|
|
long dstRem, long srcLen)
|
|
{
|
|
const uint8_t* origDst = dst;
|
|
|
|
while (srcLen > 0) {
|
|
uint8_t flag = *src++;
|
|
int count = (flag & 0x3f) +1;
|
|
uint8_t val;
|
|
uint8_t valSet[4];
|
|
int i;
|
|
|
|
srcLen--;
|
|
|
|
switch (flag & 0xc0) {
|
|
case 0x00:
|
|
for (i = 0; i < count; i++) {
|
|
if (srcLen == 0 || dstRem == 0) {
|
|
LOGI(" SHR unpack overrun1 (srcLen=%ld dstRem=%ld)",
|
|
srcLen, dstRem);
|
|
return -1;
|
|
}
|
|
*dst++ = *src++;
|
|
srcLen--;
|
|
dstRem--;
|
|
}
|
|
break;
|
|
case 0x40:
|
|
//if (count != 3 || count != 5 || count != 6 || count != 7) {
|
|
// LOGI(" SHR unpack funky len %d?", count);
|
|
//}
|
|
if (srcLen == 0) {
|
|
LOGI(" SHR unpack underrun2");
|
|
return -1;
|
|
}
|
|
val = *src++;
|
|
srcLen--;
|
|
for (i = 0; i < count; i++) {
|
|
if (dstRem == 0) {
|
|
LOGI(" SHR unpack overrun2 (srcLen=%d, i=%d of %d)",
|
|
srcLen, i, count);
|
|
return -1;
|
|
}
|
|
*dst++ = val;
|
|
dstRem--;
|
|
}
|
|
break;
|
|
case 0x80:
|
|
if (srcLen < 4) {
|
|
LOGI(" SHR unpack underrun3");
|
|
return -1;
|
|
}
|
|
valSet[0] = *src++;
|
|
valSet[1] = *src++;
|
|
valSet[2] = *src++;
|
|
valSet[3] = *src++;
|
|
srcLen -= 4;
|
|
for (i = 0; i < count; i++) {
|
|
if (dstRem < 4) {
|
|
LOGI(" SHR unpack overrun3 (srcLen=%ld dstRem=%ld)",
|
|
srcLen, dstRem);
|
|
return -1;
|
|
}
|
|
*dst++ = valSet[0];
|
|
*dst++ = valSet[1];
|
|
*dst++ = valSet[2];
|
|
*dst++ = valSet[3];
|
|
dstRem -= 4;
|
|
}
|
|
break;
|
|
case 0xc0:
|
|
if (srcLen == 0) {
|
|
LOGI(" SHR unpack underrun4");
|
|
return -1;
|
|
}
|
|
val = *src++;
|
|
srcLen--;
|
|
for (i = 0; i < count; i++) {
|
|
if (dstRem < 4) {
|
|
LOGI(" SHR unpack overrun4 (srcLen=%ld dstRem=%ld count=%d)",
|
|
srcLen, dstRem, count);
|
|
return -1;
|
|
}
|
|
*dst++ = val;
|
|
*dst++ = val;
|
|
*dst++ = val;
|
|
*dst++ = val;
|
|
dstRem -= 4;
|
|
}
|
|
break;
|
|
default:
|
|
ASSERT(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT(srcLen == 0);
|
|
|
|
if (false) {
|
|
/* require that we completely fill the buffer */
|
|
if (dstRem != 0) {
|
|
LOGI(" SHR unpack dstRem at %d", dstRem);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return dst - origDst;
|
|
}
|
|
|
|
/*
|
|
* Unpack Macintosh PackBits format. See Technical Note TN1023.
|
|
*
|
|
* Read a byte.
|
|
* If the high bit is set, count is 2s complement +1 (i.e. count = (-byte)+1).
|
|
* Read the next byte, then write that byte 'count' times.
|
|
* If the high bit is clear, count is 1+value (i.e. count = byte+1). Read and
|
|
* copy that many bytes.
|
|
* After "destLen" bytes have been written, return (even if in the middle of
|
|
* a run).
|
|
*
|
|
* NOTE: if the count byte is 0x80, Apple says it's an invalid value and
|
|
* should be skipped over. Use the following byte as the count byte. This
|
|
* is probably because PackBits is only supposed to crunch 127 bytes, though
|
|
* that suggests 0x81 and 0x7f are also impossible.
|
|
*
|
|
* We have to watch for underruns on the input and overruns on the output.
|
|
*/
|
|
void ReformatGraphics::UnPackBits(const uint8_t** pSrcBuf, long* pSrcLen,
|
|
uint8_t** pOutPtr, long dstLen, uint8_t xorVal)
|
|
{
|
|
const uint8_t* srcBuf = *pSrcBuf;
|
|
long length = *pSrcLen;
|
|
uint8_t* outPtr = *pOutPtr;
|
|
int pixByte = 0;
|
|
|
|
while (pixByte < dstLen && length > 0) {
|
|
uint8_t countByte;
|
|
int count;
|
|
|
|
countByte = *srcBuf++;
|
|
length--;
|
|
if (countByte & 0x80) {
|
|
/* RLE string */
|
|
uint8_t ch;
|
|
count = (countByte ^ 0xff)+1 +1;
|
|
ch = *srcBuf++;
|
|
length--;
|
|
while (count-- && pixByte < dstLen) {
|
|
*outPtr++ = ch ^ xorVal;
|
|
pixByte++;
|
|
}
|
|
} else {
|
|
/* series of bytes */
|
|
count = countByte +1;
|
|
while (count && pixByte < dstLen && length > 0) {
|
|
*outPtr++ = *srcBuf++ ^ xorVal;
|
|
count--;
|
|
length--;
|
|
pixByte++;
|
|
}
|
|
}
|
|
}
|
|
if (pixByte != 72) {
|
|
/* can happen if we run out of input early */
|
|
LOGI(" MP unexpected pixByte=%d", pixByte);
|
|
/* keep going */
|
|
}
|
|
|
|
*pSrcBuf = srcBuf;
|
|
*pSrcLen = length;
|
|
*pOutPtr = outPtr;
|
|
}
|