ciderpress/reformat/Teach.cpp

412 lines
12 KiB
C++

/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Reformat "generic" GWP and Teach files.
*/
#include "StdAfx.h"
#include "Teach.h"
#include "ResourceFork.h"
/*
* ==========================================================================
* ReformatGWP
* ==========================================================================
*/
/*
* Decide whether or not we want to treat this file as a "generic" IIgs
* document. Possibly useful for converting the special characters. We'll
* activate it for any GWP file other than the two known types.
*/
void ReformatGWP::Examine(ReformatHolder* pHolder)
{
ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot;
if (pHolder->GetFileType() == kTypeGWP &&
(pHolder->GetAuxType() != 0x5445 && pHolder->GetAuxType() != 0x8010))
{
applies = ReformatHolder::kApplicProbably;
}
pHolder->SetApplic(ReformatHolder::kReformatGWP, applies,
ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
}
/*
* Convert GWP into formatted text.
*/
int ReformatGWP::Process(const ReformatHolder* pHolder,
ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
ReformatOutput* pOutput)
{
const uint8_t* srcBuf = pHolder->GetSourceBuf(part);
long srcLen = pHolder->GetSourceLen(part);
fUseRTF = false;
Charset::CheckGSCharConv();
RTFBegin();
/* convert EOL markers and IIgs characters */
uint8_t ch;
while (srcLen) {
ch = *srcBuf++;
srcLen--;
if (ch == '\r') {
/* got CR, check for CRLF -- not really expected on IIgs */
if (srcLen != 0 && *srcBuf == '\n') {
srcBuf++;
srcLen--;
}
BufPrintf("\r\n");
} else if (ch == '\n') {
BufPrintf("\r\n");
} else {
// RTF is always off, so just use BufPrintf
BufPrintf("%c", Charset::ConvertMacRomanTo1252(ch));
}
}
RTFEnd();
SetResultBuffer(pOutput);
return 0;
}
/*
* ==========================================================================
* ReformatTeach
* ==========================================================================
*/
/*
* Decide whether or not we want to handle this file.
*/
void ReformatTeach::Examine(ReformatHolder* pHolder)
{
ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot;
if (pHolder->GetFileType() == kTypeGWP && pHolder->GetAuxType() == 0x5445)
applies = ReformatHolder::kApplicYes;
pHolder->SetApplic(ReformatHolder::kReformatTeach, applies,
ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
}
/*
* Convert Teach Text into formatted text.
*
* The text is in the data fork and the formatting is in the resource fork.
*/
int ReformatTeach::Process(const ReformatHolder* pHolder,
ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
ReformatOutput* pOutput)
{
const uint8_t* dataBuf;
const uint8_t* rsrcBuf;
long dataLen, rsrcLen;
const uint8_t* styleBlock;
long styleLen;
if (part != ReformatHolder::kPartData)
return -1;
dataBuf = pHolder->GetSourceBuf(ReformatHolder::kPartData);
dataLen = pHolder->GetSourceLen(ReformatHolder::kPartData);
rsrcBuf = pHolder->GetSourceBuf(ReformatHolder::kPartRsrc);
rsrcLen = pHolder->GetSourceLen(ReformatHolder::kPartRsrc);
if (dataBuf == NULL || rsrcBuf == NULL || dataLen <= 0 || rsrcLen <= 0) {
LOGI("Teach reformatter missing one fork of the file");
return -1;
}
Charset::CheckGSCharConv();
/* find the rStyleBlock */
if (!ReformatResourceFork::GetResource(rsrcBuf, rsrcLen, 0x8012, 0x0001,
&styleBlock, &styleLen))
{
LOGI("Resource fork of Teach Text file not found");
return -1;
}
RStyleBlock rStyleBlock;
if (!rStyleBlock.Create(styleBlock, styleLen)) {
LOGI("Unable to unpack rStyleBlock");
return -1;
}
fUseRTF = true;
RTFBegin();
/*
* Configure ruler options. The format allows us to have more than one
* ruler, but doesn't provide a way to select between them, so we have
* to assume that the first ruler is global.
*
* In practice, the Teach application doesn't provide a way to set
* margins or tab stops, so there's really not much to do. It seems to
* create tab stops with 64-pixel widths, which translates to 10 tab
* stops per page. WordPad seems to default to 12, so I figure there's
* not much point in futzing with it since the fonts are all different
* anyway.
*/
RStyleBlock::TERuler* pRuler = rStyleBlock.GetRuler(0);
assert(pRuler != NULL);
if (pRuler->GetJustification() != RStyleBlock::TERuler::kJustLeft) {
LOGI("WARNING: not using left justified Teach text");
/* ignore it */
}
for (int style = 0; style < rStyleBlock.GetNumStyleItems(); style++) {
RStyleBlock::StyleItem* pStyleItem;
RStyleBlock::TEStyle* pStyle;
int numBytes;
/* set up the style */
pStyleItem = rStyleBlock.GetStyleItem(style);
assert(pStyleItem != NULL);
if (pStyleItem->GetLength() == RStyleBlock::StyleItem::kUnusedItem)
continue;
numBytes = pStyleItem->GetLength();
pStyle = rStyleBlock.GetStyle(pStyleItem->GetStyleIndex());
/*
* Set the font first; that defines the point size multiplier. Set
* the size second, because the point size determines whether we
* show underline. Set the style last.
*/
RTFSetGSFont(pStyle->GetFontFamily());
RTFSetGSFontSize(pStyle->GetFontSize());
RTFSetGSFontStyle(pStyle->GetTextStyle());
/*
* The Teach app doesn't let you set text colors, but it appears
* there's a way to do it, since the SpyHunterGS Read.Me file has
* a bit of red in it.
*/
if (pStyle->GetTextColor() != 0) {
// do something with color, someday
}
/* output the characters */
uint8_t uch;
while (numBytes--) {
if (!dataLen) {
LOGI("WARNING: Teach underrun (%ld wanted)", numBytes);
break;
}
uch = *dataBuf;
if (uch == '\r') {
RTFNewPara();
} else if (uch == '\t') {
RTFTab();
} else {
RTFPrintUTF16Char(Charset::ConvertMacRomanToUTF16(uch));
}
dataBuf++;
dataLen--;
}
}
if (dataLen) {
LOGI("WARNING: Teach overrun (%ld remain)", dataLen);
/* no big deal */
}
RTFEnd();
SetResultBuffer(pOutput, true);
return 0;
}
/*
* ==========================================================================
* RStyleBlock functions
* ==========================================================================
*/
/*
* Unpack an rStyleBlock resource.
*/
bool RStyleBlock::Create(const uint8_t* buf, long len)
{
uint16_t version;
uint32_t partLen;
int i;
assert(buf != NULL);
if (len < kMinLen) {
LOGI("Too short to be rStyleBlock (%d)", len);
return false;
}
version = Reformat::Read16(&buf, &len);
if (version != kExpectedVersion) {
LOGI("Bad rStyleBlock version (%d)", version);
return false;
}
/* extract ruler(s) */
partLen = Reformat::Read32(&buf, &len);
if (partLen > (uint32_t) (len+8)) {
/* not enough to satisfy data + two more counts */
LOGI("Invalid part1 length (%d vs %d)", partLen, len);
return false;
}
fNumRulers = 1;
fpRulers = new TERuler[fNumRulers];
if (fpRulers == NULL)
return false;
if (fpRulers->Create(buf, partLen) <= 0)
return false;
buf += partLen;
len -= partLen;
/* extract TEStyles */
partLen = Reformat::Read32(&buf, &len);
if (partLen > (uint32_t) (len + 4)) {
LOGI("Invalid part2 length (%d vs %d)", partLen, len);
return false;
}
if ((partLen % TEStyle::kDataLen) != 0) {
LOGI("Invalid part2 length (%d mod %d)",
partLen, TEStyle::kDataLen);
return false;
}
fNumStyles = partLen / TEStyle::kDataLen;
fpStyles = new TEStyle[fNumStyles];
if (fpStyles == NULL)
return false;
for (i = 0; i < fNumStyles; i++) {
fpStyles[i].Create(buf);
buf += TEStyle::kDataLen;
len -= TEStyle::kDataLen;
}
/* extract StyleItems */
fNumStyleItems = (int) Reformat::Read32(&buf, &len);
partLen = fNumStyleItems * StyleItem::kItemDataLen;
if (partLen > (uint32_t) len) {
LOGI("Invalid part3 length (%d vs %d)", partLen, len);
return false;
}
fpStyleItems = new StyleItem[fNumStyleItems];
if (fpStyleItems == NULL)
return false;
for (i = 0; i < fNumStyleItems; i++) {
fpStyleItems[i].Create(buf);
if ((fpStyleItems[i].GetOffset() % TEStyle::kDataLen) != 0) {
LOGI("Invalid offset %d (mod %d)",
fpStyleItems[i].GetOffset(), TEStyle::kDataLen);
return false;
}
buf += StyleItem::kItemDataLen;
len -= StyleItem::kItemDataLen;
}
if (len != 0) {
LOGI("WARNING: at end of rStyleBlock, len is %ld", len);
}
return true;
}
/*
* Construct a TERuler from a chunk of data.
*
* Returns the #of bytes consumed, or -1 on failure.
*/
int RStyleBlock::TERuler::Create(const uint8_t* buf, long len)
{
long origLen = len;
if (len < kMinLen)
return -1;
fLeftMargin = Reformat::Get16LE(buf);
fLeftIndent = Reformat::Get16LE(buf + 2);
fRightMargin = Reformat::Get16LE(buf + 4);
fJust = Reformat::Get16LE(buf + 6);
fExtraLS = Reformat::Get16LE(buf + 8);
fFlags = Reformat::Get16LE(buf + 10);
fUserData = Reformat::Get32LE(buf + 12);
fTabType = Reformat::Get16LE(buf + 16);
buf += 18;
len -= 18;
/*
* What we do now depends on the value of fTabType:
* 0: no tabs are set, there's nothing else to read.
* 1: regularly-spaced tabs; spacing defined by fTabTerminator field.
* 2: irregular tabs, defined by array terminated by 0xffff in the
* fTabTerminator field.
*
* At present we're just throwing the tab array out.
*/
switch (fTabType) {
case 0:
fTabTerminator = 0;
break;
case 1:
fTabTerminator = Reformat::Get16LE(buf);
buf += 2;
len -= 2;
break;
case 2:
while (len >= 2) {
TabItem tabItem;
tabItem.tabKind = Reformat::Get16LE(buf);
buf += 2;
len -= 2;
if (tabItem.tabKind == kTabArrayEnd || len < 2)
break;
tabItem.tabData = Reformat::Get16LE(buf);
buf += 2;
len -= 2;
}
break;
default:
LOGI("Invalid tab type %d", fTabType);
return -1;
}
LOGI("TERuler consumed %ld bytes (%ld left over)", origLen - len, len);
return origLen - len;
}
/*
* Extract a TEStyle object from the buffer.
*/
void RStyleBlock::TEStyle::Create(const uint8_t* buf)
{
fFontID = Reformat::Get32LE(buf);
fForeColor = Reformat::Get16LE(buf + 4);
fBackColor = Reformat::Get16LE(buf + 6);
fUserData = Reformat::Get32LE(buf + 8);
LOGI(" TEStyle: font fam=0x%04x size=%-2d style=0x%02x fore=0x%04x",
GetFontFamily(), GetFontSize(), GetTextStyle(), fForeColor);
}
/*
* Extract a StyleItem object from the buffer.
*/
void RStyleBlock::StyleItem::Create(const uint8_t* buf)
{
fLength = Reformat::Get32LE(buf);
fOffset = Reformat::Get32LE(buf + 4);
LOGI(" StyleItem: len=%ld off=%ld", fLength, fOffset);
}