ciderpress/reformat/Teach.cpp
Andy McFadden 63b9996009 Normalize indentation and EOL
This updates all source files to use spaces instead of tabs for
indentation.  It also normalizes the end-of-line markers to be
Windows-style CRLF, and ensures that all files end with EOL.

No substantive changes were made; "diff -w" is empty.
2014-11-03 16:26:53 -08:00

421 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 unsigned char* srcBuf = pHolder->GetSourceBuf(part);
long srcLen = pHolder->GetSourceLen(part);
fUseRTF = false;
CheckGSCharConv();
RTFBegin();
/* convert EOL markers and IIgs characters */
unsigned char 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", ConvertGSChar(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 unsigned char* dataBuf;
const unsigned char* rsrcBuf;
long dataLen, rsrcLen;
const unsigned char* 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 == nil || rsrcBuf == nil || dataLen <= 0 || rsrcLen <= 0) {
WMSG0("Teach reformatter missing one fork of the file\n");
return -1;
}
CheckGSCharConv();
/* find the rStyleBlock */
if (!ReformatResourceFork::GetResource(rsrcBuf, rsrcLen, 0x8012, 0x0001,
&styleBlock, &styleLen))
{
WMSG0("Resource fork of Teach Text file not found\n");
return -1;
}
RStyleBlock rStyleBlock;
if (!rStyleBlock.Create(styleBlock, styleLen)) {
WMSG0("Unable to unpack rStyleBlock\n");
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 != nil);
if (pRuler->GetJustification() != RStyleBlock::TERuler::kJustLeft) {
WMSG0("WARNING: not using left justified Teach text\n");
/* 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 != nil);
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 */
unsigned char uch;
while (numBytes--) {
if (!dataLen) {
WMSG1("WARNING: Teach underrun (%ld wanted)\n", numBytes);
break;
}
uch = *dataBuf;
if (uch == '\r') {
RTFNewPara();
} else if (uch == '\t') {
RTFTab();
} else {
RTFPrintExtChar(ConvertGSChar(uch));
}
dataBuf++;
dataLen--;
}
}
if (dataLen) {
WMSG1("WARNING: Teach overrun (%ld remain)\n", dataLen);
/* no big deal */
}
RTFEnd();
SetResultBuffer(pOutput, true);
return 0;
}
/*
* ==========================================================================
* RStyleBlock functions
* ==========================================================================
*/
/*
* Unpack an rStyleBlock resource.
*/
bool
RStyleBlock::Create(const unsigned char* buf, long len)
{
unsigned short version;
unsigned long partLen;
int i;
assert(buf != nil);
if (len < kMinLen) {
WMSG1("Too short to be rStyleBlock (%d)\n", len);
return false;
}
version = Reformat::Read16(&buf, &len);
if (version != kExpectedVersion) {
WMSG1("Bad rStyleBlock version (%d)\n", version);
return false;
}
/* extract ruler(s) */
partLen = Reformat::Read32(&buf, &len);
if (partLen > (unsigned long) (len+8)) {
/* not enough to satisfy data + two more counts */
WMSG2("Invalid part1 length (%d vs %d)\n", partLen, len);
return false;
}
fNumRulers = 1;
fpRulers = new TERuler[fNumRulers];
if (fpRulers == nil)
return false;
if (fpRulers->Create(buf, partLen) <= 0)
return false;
buf += partLen;
len -= partLen;
/* extract TEStyles */
partLen = Reformat::Read32(&buf, &len);
if (partLen > (unsigned long) (len+4)) {
WMSG2("Invalid part2 length (%d vs %d)\n", partLen, len);
return false;
}
if ((partLen % TEStyle::kDataLen) != 0) {
WMSG2("Invalid part2 length (%d mod %d)\n",
partLen, TEStyle::kDataLen);
return false;
}
fNumStyles = partLen / TEStyle::kDataLen;
fpStyles = new TEStyle[fNumStyles];
if (fpStyles == nil)
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::kDataLen;
if (partLen > (unsigned long) len) {
WMSG2("Invalid part3 length (%d vs %d)\n", partLen, len);
return false;
}
fpStyleItems = new StyleItem[fNumStyleItems];
if (fpStyleItems == nil)
return false;
for (i = 0; i < fNumStyleItems; i++) {
fpStyleItems[i].Create(buf);
if ((fpStyleItems[i].GetOffset() % TEStyle::kDataLen) != 0) {
WMSG2("Invalid offset %d (mod %d)\n",
fpStyleItems[i].GetOffset(), TEStyle::kDataLen);
return false;
}
buf += StyleItem::kDataLen;
len -= StyleItem::kDataLen;
}
if (len != 0) {
WMSG1("WARNING: at end of rStyleBlock, len is %ld\n", 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 unsigned char* 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:
WMSG1("Invalid tab type %d\n", fTabType);
return -1;
}
WMSG2("TERuler consumed %ld bytes (%ld left over)\n", origLen - len, len);
return origLen - len;
}
/*
* Extract a TEStyle object from the buffer.
*/
void
RStyleBlock::TEStyle::Create(const unsigned char* buf)
{
fFontID = Reformat::Get32LE(buf);
fForeColor = Reformat::Get16LE(buf + 4);
fBackColor = Reformat::Get16LE(buf + 6);
fUserData = Reformat::Get32LE(buf + 8);
WMSG4(" TEStyle: font fam=0x%04x size=%-2d style=0x%02x fore=0x%04x\n",
GetFontFamily(), GetFontSize(), GetTextStyle(), fForeColor);
}
/*
* Extract a StyleItem object from the buffer.
*/
void
RStyleBlock::StyleItem::Create(const unsigned char* buf)
{
fLength = Reformat::Get32LE(buf);
fOffset = Reformat::Get32LE(buf + 4);
WMSG2(" StyleItem: len=%ld off=%ld\n", fLength, fOffset);
}