/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Reformat AWGS files.
*/
#include "StdAfx.h"
#include "AWGS.h"
/*
* Decide whether or not we want to handle this file.
*/
void ReformatAWGS_WP::Examine(ReformatHolder* pHolder)
{
ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot;
if (pHolder->GetFileType() == kTypeGWP && pHolder->GetAuxType() == 0x8010)
applies = ReformatHolder::kApplicYes;
pHolder->SetApplic(ReformatHolder::kReformatAWGS_WP, applies,
ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
}
/*
* Convert AWGS into formatted text.
*/
int ReformatAWGS_WP::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 = true;
Chunk doc, header, footer;
uint16_t val;
Charset::CheckGSCharConv();
/* must at least have the doc header and globals */
if (srcLen < kMinExpectedLen) {
LOGI("Too short to be AWGS");
return -1;
}
RTFBegin(kRTFFlagColorTable);
/*
* Pull interesting values out of the document header.
*/
val = Get16LE(srcBuf + 0);
if (val != kExpectedVersion1 && val != kExpectedVersion2) {
LOGI("AWGS_WP: unexpected version number (got 0x%04x, wanted 0x%04x)",
val, kExpectedVersion1);
DebugBreak();
}
val = Get16LE(srcBuf + 2);
if (val != kDocHeaderLen) {
LOGI("Unexpected doc header len (got 0x%04x, wanted 0x%04x)",
val, kDocHeaderLen);
return -1;
}
/* the color table is 32 bytes at +56, should we be interested */
srcBuf += kDocHeaderLen;
srcLen -= kDocHeaderLen;
/*
* Pull interesting values out of the WP global variables section.
*/
val = Get16LE(srcBuf + 0);
if (val > kExpectedIntVersion) {
LOGI("Unexpected internal version number (got %d, expected %d)",
val, kExpectedIntVersion);
return -1;
}
/* date/time are pascal strings */
LOGI("File saved at '%.26hs' '%.10s'", srcBuf + 6, srcBuf + 32);
srcBuf += kWPGlobalsLen;
srcLen -= kWPGlobalsLen;
/*
* Now come the three chunks, in order: main document, header, footer.
*/
LOGI("AWGS_WP: scanning doc");
if (!ReadChunk(&srcBuf, &srcLen, &doc))
return -1;
LOGI("AWGS_WP: scanning header");
if (!ReadChunk(&srcBuf, &srcLen, &header))
return -1;
LOGI("AWGS_WP: scanning footer");
if (!ReadChunk(&srcBuf, &srcLen, &footer))
return -1;
if (srcLen != 0) {
LOGI("AWGS NOTE: %ld bytes left in file", srcLen);
}
/*
* Dump the chunks, starting with header and footer.
*/
RTFSetColor(kColorMediumBlue);
RTFSetFont(kFontCourierNew);
RTFSetFontSize(10);
BufPrintf("");
RTFSetColor(kColorNone);
RTFNewPara();
PrintChunk(&header);
RTFSetColor(kColorMediumBlue);
RTFSetFont(kFontCourierNew);
RTFSetFontSize(10);
BufPrintf("");
RTFSetColor(kColorNone);
RTFNewPara();
RTFSetColor(kColorMediumBlue);
RTFSetFont(kFontCourierNew);
RTFSetFontSize(10);
BufPrintf("");
RTFSetColor(kColorNone);
RTFNewPara();
LOGI("AWGS_WP: rendering document");
PrintChunk(&doc);
RTFEnd();
SetResultBuffer(pOutput, true);
return 0;
}
/*
* Read one of the chunks of the file.
*/
bool ReformatAWGS_WP::ReadChunk(const uint8_t** pSrcBuf, long* pSrcLen,
Chunk* pChunk)
{
/* starts with the saveArray count */
pChunk->saveArrayCount = Get16LE(*pSrcBuf);
if (pChunk->saveArrayCount == 0) {
/* AWGS always has at least 1 paragraph */
LOGI("Save array is empty");
return false;
}
*pSrcBuf += 2;
*pSrcLen -= 2;
/* locate and move past the SaveArray */
pChunk->saveArray = *pSrcBuf;
*pSrcBuf += pChunk->saveArrayCount * kSaveArrayEntryLen;
*pSrcLen -= pChunk->saveArrayCount * kSaveArrayEntryLen;
if (*pSrcLen <= 0) {
LOGI("SaveArray exceeds file length (count=%d len now %ld)",
pChunk->saveArrayCount, *pSrcLen);
return false;
}
/*
* Scan the "save array" to find the highest-numbered ruler. This tells
* us how many rulers there are.
*/
pChunk->numRulers = GetNumRulers(pChunk->saveArray, pChunk->saveArrayCount);
if (*pSrcLen < pChunk->numRulers * kRulerEntryLen) {
LOGI("Not enough room for rulers (rem=%ld, needed=%ld)",
*pSrcLen, pChunk->numRulers * kRulerEntryLen);
return false;
}
LOGI("+++ found %d rulers", pChunk->numRulers);
pChunk->rulers = *pSrcBuf;
*pSrcBuf += pChunk->numRulers * kRulerEntryLen;
*pSrcLen -= pChunk->numRulers * kRulerEntryLen;
/*
* Now we're at the docTextBlocks section.
*/
pChunk->textBlocks = *pSrcBuf;
pChunk->numTextBlocks = GetNumTextBlocks(pChunk->saveArray,
pChunk->saveArrayCount);
if (!SkipTextBlocks(pSrcBuf, pSrcLen, pChunk->numTextBlocks))
return false;
return true;
}
/*
* Output a single chunk. We do this by walking down the saveArray.
*/
void ReformatAWGS_WP::PrintChunk(const Chunk* pChunk)
{
const int kDefaultStatusBits = kAWGSJustifyLeft | kAWGSSingleSpace;
SaveArrayEntry sae;
const uint8_t* saveArray;
int saCount;
const uint8_t* blockPtr;
long blockLen;
const uint8_t* pRuler;
uint16_t rulerStatusBits;
saveArray = pChunk->saveArray;
saCount = pChunk->saveArrayCount;
for ( ; saCount > 0; saCount--, saveArray += kSaveArrayEntryLen) {
UnpackSaveArrayEntry(saveArray, &sae);
/*
* Page-break paragraphs have no real data and an invalid value
* in the "rulerNum" field. So we just throw out a page break
* here and call it a day.
*/
if (sae.attributes == 0x0001) {
/* this is a page-break paragraph */
RTFSetColor(kColorMediumBlue);
RTFSetFont(kFontCourierNew);
RTFSetFontSize(10);
BufPrintf("");
RTFSetColor(kColorNone);
RTFNewPara();
RTFPageBreak(); // only supported by Word
continue;
}
if (sae.rulerNum < pChunk->numRulers) {
pRuler = pChunk->rulers + sae.rulerNum * kRulerEntryLen;
rulerStatusBits = Get16LE(pRuler + 2);
} else {
LOGI("AWGS_WP GLITCH: invalid ruler index %d", sae.rulerNum);
rulerStatusBits = kDefaultStatusBits;
}
if (rulerStatusBits & kAWGSJustifyFull)
RTFParaJustify();
else if (rulerStatusBits & kAWGSJustifyRight)
RTFParaRight();
else if (rulerStatusBits & kAWGSJustifyCenter)
RTFParaCenter();
else if (rulerStatusBits & kAWGSJustifyLeft)
RTFParaLeft();
RTFSetPara();
/*
* Find the text block that holds this paragraph. We could speed
* this up by creating an array of entries rather than walking the
* list every time. However, the block count tends to be fairly
* small (e.g. 7 for a 16K doc).
*/
blockPtr = FindTextBlock(pChunk, sae.textBlock);
if (blockPtr == NULL) {
LOGI("AWGS_WP bad textBlock %d", sae.textBlock);
return;
}
blockLen = (long) Get32LE(blockPtr);
if (blockLen <= 0 || blockLen > 65535) {
LOGI("AWGS_WP invalid block len %d", blockLen);
return;
}
blockPtr += 4;
if (sae.offset >= blockLen) {
LOGI("AWGS_WP bad offset: %d, blockLen=%ld",
sae.offset, blockLen);
return;
}
PrintParagraph(blockPtr + sae.offset, blockLen - sae.offset);
}
}
/*
* Print the contents of the text blocks.
*
* We're assured that the text block format is correct because we had to
* skip through them earlier. We don't really need to worry about running
* off the end due to a bad file.
*/
const uint8_t* ReformatAWGS_WP::FindTextBlock(const Chunk* pChunk, int blockNum)
{
const uint8_t* blockPtr = pChunk->textBlocks;
uint32_t blockSize;
while (blockNum--) {
blockSize = Get32LE(blockPtr);
blockPtr += 4 + blockSize;
}
return blockPtr;
}
/*
* Print one paragraph.
*
* Stop when we hit '\r'. We watch "maxLen" just to be safe.
*
* Returns the #of bytes consumed.
*/
int ReformatAWGS_WP::PrintParagraph(const uint8_t* ptr, long maxLen)
{
const uint8_t* startPtr = ptr;
uint16_t firstFont;
uint8_t firstStyle, firstSize, firstColor;
uint8_t uch;
if (maxLen < 7) {
LOGI("AWGS_WP GLITCH: not enough storage for para header (%d)",
maxLen);
return 1; // don't return zero or we might loop forever
}
/* pull out the paragraph header */
firstFont = Get16LE(ptr);
firstStyle = *(ptr + 2);
firstSize = *(ptr + 3);
firstColor = *(ptr + 4);
ptr += 7;
maxLen -= 7;
/*
* 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.
*/
//LOGI("+++ Para start: font=0x%04x size=%d style=0x%02x",
// firstFont, firstSize, firstStyle);
RTFSetGSFont(firstFont);
RTFSetGSFontSize(firstSize);
RTFSetGSFontStyle(firstStyle);
while (maxLen > 0) {
uch = *ptr++;
maxLen--;
switch (uch) {
case 0x01: // font change - two bytes follow
if (maxLen >= 2) {
RTFSetGSFont(Get16LE(ptr));
ptr += 2;
maxLen -= 2;
}
break;
case 0x02: // text style change
if (maxLen >= 1) {
RTFSetGSFontStyle(*ptr++);
maxLen--;
}
break;
case 0x03: // text size change
if (maxLen >= 1) {
RTFSetGSFontSize(*ptr++);
maxLen--;
}
break;
case 0x04: // color change (0-15)
if (maxLen >= 1) {
ptr++;
maxLen--;
}
break;
case 0x05: // page token (replace with page #)
case 0x06: // date token (replace with date)
case 0x07: // time token (replace with time)
RTFSetColor(kColorMediumBlue);
if (uch == 0x05)
BufPrintf("");
else if (uch == 0x06)
BufPrintf("");
else
BufPrintf("