mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-25 11:22:55 +00:00
1208 lines
43 KiB
C++
1208 lines
43 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2007 by Tobias Koenig <tokoe@kde.org> *
|
|
* *
|
|
* Based on code written by Bill Janssen 2002 *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
***************************************************************************/
|
|
|
|
#include <QtCore/QDateTime>
|
|
#include <QtCore/QFile>
|
|
#include <QtCore/QHash>
|
|
#include <QtCore/QStack>
|
|
#include <QtCore/QString>
|
|
#include <QtCore/QUrl>
|
|
#include <QtCore/QDateTime>
|
|
#include <QtGui/QAbstractTextDocumentLayout>
|
|
#include <QtGui/QFont>
|
|
#include <QtGui/qtextobject.h>
|
|
#include <QtGui/qtextformat.h>
|
|
#include <QtGui/QTextCursor>
|
|
#include <QtGui/QTextDocument>
|
|
|
|
|
|
#include <QtGui/QLabel>
|
|
|
|
#include <core/action.h>
|
|
#include <core/document.h>
|
|
|
|
#include "qunpluck.h"
|
|
#include "image.h"
|
|
|
|
#define GET_FUNCTION_CODE_TYPE(x) (((x)>>3) & 0x1F)
|
|
#define GET_FUNCTION_CODE_DATALEN(x) ((x) & 0x7)
|
|
|
|
#define CELLS(row,col) cells[row*cols+col]
|
|
|
|
#define READ_BIGENDIAN_SHORT(p) (((p)[0] << 8)|((p)[1]))
|
|
#define READ_BIGENDIAN_LONG(p) (((p)[0] << 24)|((p)[1] << 16)|((p)[2] << 8)|((p)[3]))
|
|
/*
|
|
static void LinkRecords
|
|
(
|
|
char* dir
|
|
)
|
|
{
|
|
RecordNode* ptr;
|
|
char* realfilename;
|
|
char* linkname;
|
|
|
|
realfilename = (char*)malloc (strlen (dir) + 20);
|
|
linkname = (char*)malloc (strlen (dir) + 20);
|
|
|
|
for (ptr = records; ptr != NULL; ptr = ptr->next) {
|
|
if (ptr->page_id != ptr->index) {
|
|
sprintf (realfilename, "%s/r%d.html", dir, ptr->page_id);
|
|
sprintf (linkname, "%s/r%d.html", dir, ptr->index);
|
|
link (realfilename, linkname);
|
|
}
|
|
}
|
|
|
|
free (realfilename);
|
|
free (linkname);
|
|
}
|
|
*/
|
|
|
|
class Context
|
|
{
|
|
public:
|
|
int recordId;
|
|
QTextDocument *document;
|
|
QTextCursor *cursor;
|
|
QStack<QTextCharFormat> stack;
|
|
QList<int> images;
|
|
|
|
QString linkUrl;
|
|
int linkStart;
|
|
int linkPage;
|
|
};
|
|
|
|
class RecordNode
|
|
{
|
|
public:
|
|
int index;
|
|
int page_id;
|
|
bool done;
|
|
};
|
|
|
|
static Okular::DocumentViewport calculateViewport( QTextDocument *document, const QTextBlock &block )
|
|
{
|
|
if ( !block.isValid() )
|
|
return Okular::DocumentViewport();
|
|
|
|
const QRectF rect = document->documentLayout()->blockBoundingRect( block );
|
|
const QSizeF size = document->size();
|
|
|
|
int page = qRound( rect.y() ) / qRound( size.height() );
|
|
|
|
Okular::DocumentViewport viewport( page );
|
|
viewport.rePos.normalizedX = (double)rect.x() / (double)size.width();
|
|
viewport.rePos.normalizedY = (double)rect.y() / (double)size.height();
|
|
viewport.rePos.enabled = true;
|
|
viewport.rePos.pos = Okular::DocumentViewport::Center;
|
|
|
|
return viewport;
|
|
}
|
|
|
|
QUnpluck::QUnpluck()
|
|
: mDocument( 0 )
|
|
{
|
|
}
|
|
|
|
QUnpluck::~QUnpluck()
|
|
{
|
|
mLinks.clear();
|
|
mNamedTargets.clear();
|
|
mPages.clear();
|
|
}
|
|
|
|
bool QUnpluck::open( const QString &fileName )
|
|
{
|
|
mLinks.clear();
|
|
mNamedTargets.clear();
|
|
mPages.clear();
|
|
|
|
mDocument = plkr_OpenDBFile( QFile::encodeName( fileName ).data() );
|
|
if ( !mDocument ) {
|
|
mErrorString = QObject::tr( "Unable to open document" );
|
|
return false;
|
|
}
|
|
|
|
bool status = true;
|
|
|
|
mInfo.insert( "name", plkr_GetName( mDocument ) );
|
|
mInfo.insert( "title", plkr_GetTitle( mDocument ) );
|
|
mInfo.insert( "author", plkr_GetAuthor( mDocument ) );
|
|
mInfo.insert( "time", QDateTime::fromTime_t( plkr_GetPublicationTime( mDocument ) ).toString() );
|
|
|
|
AddRecord( plkr_GetHomeRecordID( mDocument ) );
|
|
|
|
int number = GetNextRecordNumber();
|
|
while ( number > 0 ) {
|
|
status = TranscribeRecord( number );
|
|
number = GetNextRecordNumber ();
|
|
}
|
|
|
|
// Iterate over all records again to add those which aren't linked directly
|
|
for ( int i = 1; i < plkr_GetRecordCount( mDocument ); ++i )
|
|
AddRecord( plkr_GetUidForIndex( mDocument, i ) );
|
|
|
|
number = GetNextRecordNumber();
|
|
while ( number > 0 ) {
|
|
status = TranscribeRecord( number );
|
|
number = GetNextRecordNumber ();
|
|
}
|
|
|
|
for ( int i = 0; i < mRecords.count(); ++i )
|
|
delete mRecords[ i ];
|
|
|
|
mRecords.clear();
|
|
|
|
plkr_CloseDoc( mDocument );
|
|
|
|
/**
|
|
* Calculate hash map
|
|
*/
|
|
QHash<int, int> pageHash;
|
|
for ( int i = 0; i < mContext.count(); ++i )
|
|
pageHash.insert( mContext[ i ]->recordId, i );
|
|
|
|
/**
|
|
* Convert ids
|
|
*/
|
|
for ( int i = 0; i < mContext.count(); ++i ) {
|
|
Context *context = mContext[ i ];
|
|
for ( int j = 0; j < context->images.count(); ++j ) {
|
|
int imgNumber = context->images[ j ];
|
|
context->document->addResource( QTextDocument::ImageResource,
|
|
QUrl( QString( "%1.jpg" ).arg( imgNumber ) ),
|
|
mImages[ imgNumber ] );
|
|
}
|
|
|
|
mPages.append( context->document );
|
|
}
|
|
qDeleteAll( mContext );
|
|
mContext.clear();
|
|
|
|
// convert record_id into page
|
|
for ( int i = 0; i < mLinks.count(); ++i ) {
|
|
mLinks[ i ].page = pageHash[ mLinks[ i ].page ];
|
|
if ( mLinks[ i ].url.startsWith( "page:" ) ) {
|
|
int page = mLinks[ i ].url.mid( 5 ).toInt();
|
|
Okular::DocumentViewport viewport( pageHash[ page ] );
|
|
viewport.rePos.normalizedX = 0;
|
|
viewport.rePos.normalizedY = 0;
|
|
viewport.rePos.enabled = true;
|
|
viewport.rePos.pos = Okular::DocumentViewport::TopLeft;
|
|
mLinks[ i ].link = new Okular::GotoAction( QString(), viewport );
|
|
} else if ( mLinks[ i ].url.startsWith( "para:" ) ) {
|
|
QPair<int, QTextBlock> data = mNamedTargets[ mLinks[ i ].url ];
|
|
|
|
QTextDocument *document = mPages[ mLinks[ i ].page ];
|
|
|
|
Okular::DocumentViewport viewport = calculateViewport( document, data.second );
|
|
|
|
mLinks[ i ].link = new Okular::GotoAction( QString(), viewport );
|
|
} else {
|
|
mLinks[ i ].link = new Okular::BrowseAction( mLinks[ i ].url );
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int QUnpluck::GetNextRecordNumber()
|
|
{
|
|
int index = 0;
|
|
|
|
for ( int pos = 0; pos < mRecords.count(); ++pos ) {
|
|
if ( !mRecords[ pos ]->done ) {
|
|
index = mRecords[ pos ]->index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
int QUnpluck::GetPageID( int index )
|
|
{
|
|
for ( int pos = 0; pos < mRecords.count(); ++pos ) {
|
|
if ( mRecords[ pos ]->index == index ) {
|
|
return mRecords[ pos ]->page_id;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void QUnpluck::AddRecord( int index )
|
|
{
|
|
for ( int pos = 0; pos < mRecords.count(); ++pos ) {
|
|
if ( mRecords[ pos ]->index == index ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
RecordNode *node = new RecordNode;
|
|
node->done = false;
|
|
node->index = index;
|
|
node->page_id = index;
|
|
|
|
mRecords.append( node );
|
|
}
|
|
|
|
void QUnpluck::MarkRecordDone( int index )
|
|
{
|
|
for ( int pos = 0; pos < mRecords.count(); ++pos ) {
|
|
if ( mRecords[ pos ]->index == index ) {
|
|
mRecords[ pos ]->done = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
AddRecord( index );
|
|
MarkRecordDone( index );
|
|
}
|
|
|
|
void QUnpluck::SetPageID( int index, int page_id )
|
|
{
|
|
for ( int pos = 0; pos < mRecords.count(); ++pos ) {
|
|
if ( mRecords[ pos ]->index == index ) {
|
|
mRecords[ pos ]->page_id = page_id;
|
|
return;
|
|
}
|
|
}
|
|
|
|
AddRecord( index );
|
|
SetPageID( index, page_id );
|
|
}
|
|
|
|
QString QUnpluck::MailtoURLFromBytes( unsigned char* record_data )
|
|
{
|
|
unsigned char* bytes = record_data + 8;
|
|
|
|
int to_offset = (bytes[0] << 8) + bytes[1];
|
|
int cc_offset = (bytes[2] << 8) + bytes[3];
|
|
int subject_offset = (bytes[4] << 8) + bytes[5];
|
|
int body_offset = (bytes[6] << 8) + bytes[7];
|
|
|
|
QString url( "mailto:" );
|
|
if ( to_offset != 0 )
|
|
url += QString::fromLatin1( (char *)(bytes + to_offset) );
|
|
|
|
if ( (cc_offset != 0) || (subject_offset != 0) || (body_offset != 0) )
|
|
url += QLatin1String( "?" );
|
|
|
|
if ( cc_offset != 0 )
|
|
url += QLatin1String( "cc=" ) + QString::fromLatin1( (char *)(bytes + cc_offset) );
|
|
|
|
if ( subject_offset != 0 )
|
|
url += QLatin1String( "subject=" ) + QString::fromLatin1( (char *)(bytes + subject_offset) );
|
|
|
|
if ( body_offset != 0 )
|
|
url += QLatin1String( "body=" ) + QString::fromLatin1( (char *)(bytes + body_offset) );
|
|
|
|
return url;
|
|
}
|
|
|
|
QImage QUnpluck::TranscribeImageRecord( unsigned char* bytes )
|
|
{
|
|
QImage image;
|
|
|
|
TranscribePalmImageToJPEG( bytes + 8, image );
|
|
|
|
return image;
|
|
}
|
|
|
|
void QUnpluck::DoStyle( Context* context, int style, bool start )
|
|
{
|
|
if ( start ) {
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
context->stack.push( format );
|
|
|
|
int pointSize = qRound( format.fontPointSize() );
|
|
switch (style) {
|
|
case 1:
|
|
format.setFontWeight( QFont::Bold );
|
|
pointSize += 3;
|
|
break;
|
|
case 2:
|
|
format.setFontWeight( QFont::Bold );
|
|
pointSize += 2;
|
|
break;
|
|
case 3:
|
|
format.setFontWeight( QFont::Bold );
|
|
pointSize += 1;
|
|
break;
|
|
case 4:
|
|
format.setFontWeight( QFont::Bold );
|
|
break;
|
|
case 5:
|
|
format.setFontWeight( QFont::Bold );
|
|
pointSize += -1;
|
|
break;
|
|
case 6:
|
|
format.setFontWeight( QFont::Bold );
|
|
pointSize += -2;
|
|
break;
|
|
case 7:
|
|
format.setFontWeight( QFont::Bold );
|
|
break;
|
|
case 8:
|
|
format.setFontFamily( QString::fromLatin1( "Courier New,courier" ) );
|
|
break;
|
|
}
|
|
format.setFontPointSize( qMax( pointSize, 1 ) );
|
|
context->cursor->setCharFormat( format );
|
|
} else {
|
|
if ( !context->stack.isEmpty() )
|
|
context->cursor->setCharFormat( context->stack.pop() );
|
|
}
|
|
}
|
|
|
|
void QUnpluck::ParseText
|
|
(
|
|
plkr_Document* doc,
|
|
unsigned char* ptr,
|
|
int text_len,
|
|
int* font,
|
|
int* style,
|
|
Context* context
|
|
)
|
|
{
|
|
unsigned char* end;
|
|
int fctype;
|
|
int fclen;
|
|
|
|
end = ptr + text_len;
|
|
while (ptr < end) {
|
|
if (ptr[0]) {
|
|
context->cursor->insertText( QString( (char*)ptr ) );
|
|
ptr += strlen ((char*)ptr);
|
|
}
|
|
else {
|
|
fctype = GET_FUNCTION_CODE_TYPE (ptr[1]);
|
|
fclen = 2 + GET_FUNCTION_CODE_DATALEN (ptr[1]);
|
|
switch (fctype) {
|
|
case PLKR_TFC_LINK:
|
|
switch (fclen) {
|
|
case 4: /* ANCHOR_BEGIN */
|
|
{
|
|
int record_id =
|
|
(ptr[2] << 8) + ptr[3];
|
|
|
|
/** TODO:
|
|
plkr_DataRecordType type =
|
|
(plkr_DataRecordType)plkr_GetRecordType (doc, record_id);
|
|
if (type ==
|
|
PLKR_DRTYPE_IMAGE
|
|
|| type ==
|
|
PLKR_DRTYPE_IMAGE_COMPRESSED)
|
|
output += QString( "<A HREF=\"r%1.jpg\">" ).arg(record_id);
|
|
else
|
|
output += QString( "<A HREF=\"r%1.html\">" ).arg(record_id);
|
|
*/
|
|
AddRecord (record_id);
|
|
}
|
|
break;
|
|
case 2: /* ANCHOR_END */
|
|
//TODO: output += QString( "</A>" );
|
|
break;
|
|
}
|
|
ptr += fclen;
|
|
break;
|
|
case PLKR_TFC_FONT:
|
|
DoStyle (context, *style, false);
|
|
*style = ptr[2];
|
|
DoStyle (context, *style, true);
|
|
ptr += fclen;
|
|
break;
|
|
case PLKR_TFC_NEWLINE:
|
|
{
|
|
// TODO: remove the setCharFormat when Qt is fixed
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
context->cursor->insertText( "\n" );
|
|
context->cursor->setCharFormat( format );
|
|
ptr += fclen;
|
|
break;
|
|
}
|
|
case PLKR_TFC_BITALIC:
|
|
{
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
format.setFontItalic( true );
|
|
context->cursor->setCharFormat( format );
|
|
ptr += fclen;
|
|
break;
|
|
}
|
|
case PLKR_TFC_EITALIC:
|
|
{
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
format.setFontItalic( false );
|
|
context->cursor->setCharFormat( format );
|
|
ptr += fclen;
|
|
break;
|
|
}
|
|
case PLKR_TFC_COLOR:
|
|
if (*font) {
|
|
*font--;
|
|
if ( !context->stack.isEmpty() )
|
|
context->cursor->setCharFormat( context->stack.pop() );
|
|
}
|
|
|
|
{
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
context->stack.push( format );
|
|
|
|
format.setForeground( QColor((ptr[2] << 16), (ptr[3] << 8), ptr[4]) );
|
|
context->cursor->setCharFormat( format );
|
|
}
|
|
|
|
*font++;
|
|
ptr += fclen;
|
|
break;
|
|
case PLKR_TFC_BULINE:
|
|
{
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
format.setFontUnderline( true );
|
|
context->cursor->setCharFormat( format );
|
|
ptr += fclen;
|
|
}
|
|
break;
|
|
case PLKR_TFC_EULINE:
|
|
{
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
format.setFontUnderline( false );
|
|
context->cursor->setCharFormat( format );
|
|
ptr += fclen;
|
|
}
|
|
break;
|
|
case PLKR_TFC_BSTRIKE:
|
|
{
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
format.setFontStrikeOut( true );
|
|
context->cursor->setCharFormat( format );
|
|
ptr += fclen;
|
|
break;
|
|
}
|
|
case PLKR_TFC_ESTRIKE:
|
|
{
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
format.setFontStrikeOut( false );
|
|
context->cursor->setCharFormat( format );
|
|
ptr += fclen;
|
|
break;
|
|
}
|
|
case PLKR_TFC_TABLE:
|
|
if (fclen == 4) {
|
|
int record_id, datalen;
|
|
plkr_DataRecordType type = (plkr_DataRecordType)0;
|
|
unsigned char *bytes = NULL;
|
|
|
|
record_id = (ptr[2] << 8) + ptr[3];
|
|
bytes =
|
|
plkr_GetRecordBytes
|
|
(doc, record_id, &datalen, &type);
|
|
TranscribeTableRecord (doc, context, bytes);
|
|
}
|
|
ptr += fclen;
|
|
break;
|
|
default:
|
|
ptr += fclen;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool QUnpluck::TranscribeTableRecord
|
|
(
|
|
plkr_Document* doc,
|
|
Context* context,
|
|
unsigned char* bytes
|
|
)
|
|
{
|
|
unsigned char* ptr = &bytes[24];
|
|
unsigned char* end;
|
|
// char* align_names[] = { "left", "right", "center" };
|
|
bool in_row = false;
|
|
int cols;
|
|
int size;
|
|
int rows;
|
|
int border;
|
|
int record_id;
|
|
int align;
|
|
int text_len;
|
|
int colspan;
|
|
int rowspan;
|
|
int font = 0;
|
|
int style = 0;
|
|
int fctype;
|
|
int fclen;
|
|
long border_color;
|
|
long link_color;
|
|
|
|
size = (bytes[8] << 8) + bytes[9];
|
|
cols = (bytes[10] << 8) + bytes[11];
|
|
rows = (bytes[12] << 8) + bytes[13];
|
|
border = bytes[15];
|
|
border_color = (bytes[17] << 16) + (bytes[18] << 8) + (bytes[19] << 8);
|
|
link_color = (bytes[21] << 16) + (bytes[22] << 8) + (bytes[23] << 8);
|
|
|
|
end = ptr + size - 1;
|
|
/**
|
|
output += QString( "<TABLE border=%1 bordercolor=\"#%2\" "
|
|
"linkcolor=\"#%3\">\n" ).arg(border, border_color, link_color);
|
|
*/
|
|
while (ptr < end) {
|
|
if (ptr[0] == '\0') {
|
|
fctype = GET_FUNCTION_CODE_TYPE (ptr[1]);
|
|
fclen = 2 + GET_FUNCTION_CODE_DATALEN (ptr[1]);
|
|
switch (fctype) {
|
|
case PLKR_TFC_TABLE:
|
|
switch (fclen) {
|
|
case 2: /* NEW_ROW */
|
|
/*
|
|
if (in_row)
|
|
output += QString( "</TR>\n" );
|
|
output += QString( "<TR>\n" );
|
|
*/
|
|
in_row = true;
|
|
ptr += fclen;
|
|
break;
|
|
case 9: /* NEW_CELL */
|
|
align = ptr[2];
|
|
colspan = ptr[5];
|
|
rowspan = ptr[6];
|
|
/**
|
|
output += QString( "<TD align=\"%1\" colspan=%2 "
|
|
"rowspan=%3 bordercolor=\"#\">" ).arg(
|
|
align_names[align], colspan, rowspan );
|
|
// border_color);
|
|
*/
|
|
if ( (record_id = READ_BIGENDIAN_SHORT (&ptr[3])) ) {
|
|
QTextCharFormat format = context->cursor->charFormat();
|
|
context->cursor->insertImage( QString( "%1.jpg" ).arg(record_id) );
|
|
context->cursor->setCharFormat( format );
|
|
context->images.append( record_id );
|
|
AddRecord (record_id);
|
|
}
|
|
DoStyle (context, style, true);
|
|
text_len = READ_BIGENDIAN_SHORT (&ptr[7]);
|
|
ptr += fclen;
|
|
ParseText (doc, ptr, text_len, &font, &style, context);
|
|
ptr += text_len;
|
|
DoStyle (context, style, false);
|
|
//output += QString( "</TD>\n" );
|
|
break;
|
|
default:
|
|
ptr += fclen;
|
|
}
|
|
break;
|
|
default:
|
|
ptr += fclen;
|
|
}
|
|
}
|
|
else {
|
|
//output += QString( "</TABLE>\n" );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// output += QString( "</TABLE>\n" );
|
|
return true;
|
|
}
|
|
|
|
typedef struct {
|
|
int size;
|
|
int attributes;
|
|
} ParagraphInfo;
|
|
|
|
static ParagraphInfo *ParseParagraphInfo
|
|
(
|
|
unsigned char* bytes,
|
|
int* nparas
|
|
)
|
|
{
|
|
ParagraphInfo* paragraph_info;
|
|
int j;
|
|
int n;
|
|
|
|
n = (bytes[2] << 8) + bytes[3];
|
|
paragraph_info = (ParagraphInfo *) malloc (sizeof (ParagraphInfo) * n);
|
|
for (j = 0; j < n; j++) {
|
|
paragraph_info[j].size =
|
|
(bytes[8 + (j * 4) + 0] << 8) + bytes[8 + (j * 4) + 1];
|
|
paragraph_info[j].attributes =
|
|
(bytes[8 + (j * 4) + 2] << 8) + bytes[8 + (j * 4) + 3];
|
|
}
|
|
*nparas = n;
|
|
return paragraph_info;
|
|
}
|
|
|
|
bool QUnpluck::TranscribeTextRecord
|
|
(
|
|
plkr_Document* doc,
|
|
int id,
|
|
Context *context,
|
|
unsigned char* bytes,
|
|
plkr_DataRecordType type
|
|
)
|
|
{
|
|
unsigned char* ptr;
|
|
unsigned char* run;
|
|
unsigned char* para_start;
|
|
unsigned char* data;
|
|
unsigned char* start;
|
|
ParagraphInfo* paragraphs;
|
|
bool first_record_of_page = true;
|
|
bool current_link;
|
|
bool current_italic;
|
|
bool current_struckthrough;
|
|
bool current_underline;
|
|
int fctype;
|
|
int fclen;
|
|
int para_index;
|
|
int para_len;
|
|
int textlen;
|
|
int data_len;
|
|
int current_font;
|
|
int record_index;
|
|
int current_alignment;
|
|
int current_left_margin;
|
|
int current_right_margin;
|
|
int nparagraphs;
|
|
long current_color;
|
|
|
|
record_index = id;
|
|
|
|
paragraphs = ParseParagraphInfo (bytes, &nparagraphs);
|
|
start = bytes + 8 + ((bytes[2] << 8) + bytes[3]) * 4;
|
|
|
|
for (para_index = 0, ptr = start, run = start;
|
|
para_index < nparagraphs; para_index++) {
|
|
|
|
para_len = paragraphs[para_index].size;
|
|
|
|
/* If the paragraph is the last in the record, and it consists
|
|
of a link to the next record in the logical page, we trim off
|
|
the paragraph and instead insert the whole page */
|
|
|
|
if (((para_index + 1) == nparagraphs) &&
|
|
(para_len == (sizeof ("Click here for the next part") + 5)) &&
|
|
(*ptr == 0) && (ptr[1] == ((PLKR_TFC_LINK << 3) + 2)) &&
|
|
(strcmp ((char*)(ptr + 4), "Click here for the next part") == 0)) {
|
|
|
|
record_index = (ptr[2] << 8) + ptr[3];
|
|
if ((data =
|
|
plkr_GetRecordBytes (doc, record_index, &data_len,
|
|
&type)) == NULL) {
|
|
// ShowWarning ("Can't open record %d!", record_index);
|
|
free (paragraphs);
|
|
return false;
|
|
}
|
|
else if (!(type == PLKR_DRTYPE_TEXT_COMPRESSED ||
|
|
type == PLKR_DRTYPE_TEXT)) {
|
|
// ShowWarning ("Bad record type %d in record linked from end of record %d", type, id);
|
|
free (paragraphs);
|
|
return false;
|
|
}
|
|
first_record_of_page = false;
|
|
para_index = 0;
|
|
ptr = data + 8 + ((data[2] << 8) + data[3]) * 4;
|
|
run = ptr;
|
|
free (paragraphs);
|
|
paragraphs = ParseParagraphInfo (data, &nparagraphs);
|
|
para_len = paragraphs[para_index].size;
|
|
MarkRecordDone (record_index);
|
|
SetPageID (record_index, id);
|
|
}
|
|
|
|
if ((para_index == 0) && !first_record_of_page &&
|
|
(*ptr == 0) && (ptr[1] == ((PLKR_TFC_LINK << 3) + 2)) &&
|
|
(strcmp ((char*)(ptr + 4), "Click here for the previous part") == 0)) {
|
|
/* throw away this inserted paragraph */
|
|
ptr += para_len;
|
|
run = ptr;
|
|
continue;
|
|
}
|
|
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
QTextBlockFormat blockFormat( context->cursor->blockFormat() );
|
|
blockFormat.setAlignment( Qt::AlignLeft );
|
|
context->cursor->insertBlock( blockFormat );
|
|
context->cursor->setCharFormat( format );
|
|
|
|
mNamedTargets.insert( QString( "para:%1-%2" ).arg( record_index ).arg( para_index ),
|
|
QPair<int, QTextBlock>( GetPageID( record_index ), context->cursor->block() ) );
|
|
|
|
current_link = false;
|
|
|
|
/* at the beginning of a paragraph, we start with a clean graphics context */
|
|
current_font = 0;
|
|
current_alignment = 0;
|
|
current_color = 0;
|
|
current_italic = false;
|
|
current_underline = false;
|
|
current_struckthrough = false;
|
|
current_left_margin = 0;
|
|
current_right_margin = 0;
|
|
|
|
for (para_start = ptr, textlen = 0; (ptr - para_start) < para_len;) {
|
|
|
|
if (*ptr == 0) {
|
|
/* function code */
|
|
|
|
if ((ptr - run) > 0) {
|
|
/* write out any pending text */
|
|
context->cursor->insertText( QString::fromLatin1( (char*)run, ptr - run ) );
|
|
textlen += (ptr - run);
|
|
}
|
|
|
|
ptr++;
|
|
fctype = GET_FUNCTION_CODE_TYPE (*ptr);
|
|
fclen = GET_FUNCTION_CODE_DATALEN (*ptr);
|
|
ptr++;
|
|
|
|
if (fctype == PLKR_TFC_NEWLINE) {
|
|
// TODO: remove the setCharFormat when Qt is fixed
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
context->cursor->insertText( "\n" );
|
|
context->cursor->setCharFormat( format );
|
|
}
|
|
else if (fctype == PLKR_TFC_LINK) {
|
|
int record_id, real_record_id,
|
|
datalen;
|
|
plkr_DataRecordType type = (plkr_DataRecordType)0;
|
|
unsigned char *bytes = NULL;
|
|
char *url = NULL;
|
|
|
|
if (fclen == 0) {
|
|
if (current_link) {
|
|
if ( !context->stack.isEmpty() )
|
|
context->cursor->setCharFormat( context->stack.pop() );
|
|
|
|
if ( !context->linkUrl.isEmpty() ) {
|
|
Link link;
|
|
link.url = context->linkUrl;
|
|
link.start = context->linkStart;
|
|
link.end = context->cursor->position();
|
|
link.page = GetPageID( id );
|
|
mLinks.append( link );
|
|
}
|
|
}
|
|
current_link = false;
|
|
}
|
|
else {
|
|
record_id = (ptr[0] << 8) + ptr[1];
|
|
bytes =
|
|
plkr_GetRecordBytes (doc, record_id, &datalen,
|
|
&type);
|
|
if (!bytes) {
|
|
url = plkr_GetRecordURL (doc, record_id);
|
|
}
|
|
if (bytes && (type == PLKR_DRTYPE_MAILTO)) {
|
|
context->linkUrl = MailtoURLFromBytes( bytes );
|
|
context->linkStart = context->cursor->position();
|
|
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
context->stack.push( format );
|
|
format.setForeground( Qt::blue );
|
|
format.setUnderlineStyle( QTextCharFormat::SingleUnderline );
|
|
context->cursor->setCharFormat( format );
|
|
current_link = true;
|
|
}
|
|
else if (!bytes && url) {
|
|
context->linkUrl = QString::fromLatin1( url );
|
|
context->linkStart = context->cursor->position();
|
|
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
context->stack.push( format );
|
|
format.setForeground( Qt::blue );
|
|
format.setUnderlineStyle( QTextCharFormat::SingleUnderline );
|
|
context->cursor->setCharFormat( format );
|
|
current_link = true;
|
|
}
|
|
else if (bytes && (fclen == 2)) {
|
|
AddRecord (record_id);
|
|
real_record_id = GetPageID (record_id);
|
|
if (type == PLKR_DRTYPE_IMAGE
|
|
|| type == PLKR_DRTYPE_IMAGE_COMPRESSED) {
|
|
|
|
context->linkUrl = QString( "%1.jpg" ).arg( record_id );
|
|
context->linkStart = context->cursor->position();
|
|
}
|
|
else {
|
|
context->linkUrl = QString( "page:%1" ).arg( real_record_id );
|
|
context->linkStart = context->cursor->position();
|
|
}
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
context->stack.push( format );
|
|
format.setForeground( Qt::blue );
|
|
format.setUnderlineStyle( QTextCharFormat::SingleUnderline );
|
|
context->cursor->setCharFormat( format );
|
|
current_link = true;
|
|
}
|
|
else if (bytes && (fclen == 4)) {
|
|
AddRecord (record_id);
|
|
|
|
context->linkUrl = QString( "para:%1-%2" ).arg( record_id ).arg( (ptr[2] << 8) + ptr[3] );
|
|
context->linkStart = context->cursor->position();
|
|
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
context->stack.push( format );
|
|
format.setForeground( Qt::blue );
|
|
format.setUnderlineStyle( QTextCharFormat::SingleUnderline );
|
|
context->cursor->setCharFormat( format );
|
|
current_link = true;
|
|
}
|
|
else {
|
|
// ShowWarning("odd link found: record_id=%d, bytes=0x%p, type=%d, url=%s", record_id, bytes, type, (url ? url : "0x0"));
|
|
}
|
|
}
|
|
|
|
}
|
|
else if (fctype == PLKR_TFC_FONT) {
|
|
if (current_font != *ptr) {
|
|
|
|
if ( !context->stack.isEmpty() )
|
|
context->cursor->setCharFormat( context->stack.pop() );
|
|
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
context->stack.push( format );
|
|
|
|
int pointSize = qRound( format.fontPointSize() );
|
|
if (*ptr == 1) {
|
|
format.setFontWeight( QFont::Bold );
|
|
pointSize += 3;
|
|
}
|
|
else if (*ptr == 2) {
|
|
format.setFontWeight( QFont::Bold );
|
|
pointSize += 2;
|
|
}
|
|
else if (*ptr == 3) {
|
|
format.setFontWeight( QFont::Bold );
|
|
pointSize += 1;
|
|
}
|
|
else if (*ptr == 4) {
|
|
format.setFontWeight( QFont::Bold );
|
|
}
|
|
else if (*ptr == 5) {
|
|
format.setFontWeight( QFont::Bold );
|
|
pointSize += -1;
|
|
}
|
|
else if (*ptr == 6) {
|
|
format.setFontWeight( QFont::Bold );
|
|
pointSize += -2;
|
|
}
|
|
else if (*ptr == 7) {
|
|
format.setFontWeight( QFont::Bold );
|
|
}
|
|
else if (*ptr == 8) {
|
|
format.setFontFamily( QString::fromLatin1( "Courier New,courier" ) );
|
|
}
|
|
else if (*ptr == 11) {
|
|
format.setVerticalAlignment( QTextCharFormat::AlignSuperScript );
|
|
}
|
|
format.setFontPointSize( qMax( pointSize, 1 ) );
|
|
|
|
context->cursor->setCharFormat( format );
|
|
|
|
current_font = *ptr;
|
|
}
|
|
|
|
}
|
|
else if (fctype == PLKR_TFC_BITALIC) {
|
|
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
format.setFontItalic( true );
|
|
context->cursor->setCharFormat( format );
|
|
|
|
current_italic = true;
|
|
|
|
}
|
|
else if (fctype == PLKR_TFC_EITALIC) {
|
|
|
|
if (current_italic) {
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
format.setFontItalic( false );
|
|
context->cursor->setCharFormat( format );
|
|
current_italic = false;
|
|
}
|
|
|
|
}
|
|
else if (fctype == PLKR_TFC_BULINE) {
|
|
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
format.setFontUnderline( true );
|
|
context->cursor->setCharFormat( format );
|
|
current_underline = true;
|
|
|
|
}
|
|
else if (fctype == PLKR_TFC_EULINE) {
|
|
|
|
if (current_underline) {
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
format.setFontUnderline( false );
|
|
context->cursor->setCharFormat( format );
|
|
current_underline = false;
|
|
}
|
|
|
|
}
|
|
else if (fctype == PLKR_TFC_BSTRIKE) {
|
|
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
format.setFontStrikeOut( true );
|
|
context->cursor->setCharFormat( format );
|
|
current_struckthrough = true;
|
|
|
|
}
|
|
else if (fctype == PLKR_TFC_ESTRIKE) {
|
|
|
|
if (current_struckthrough) {
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
format.setFontStrikeOut( false );
|
|
context->cursor->setCharFormat( format );
|
|
current_struckthrough = false;
|
|
}
|
|
|
|
}
|
|
else if (fctype == PLKR_TFC_HRULE) {
|
|
QTextCharFormat charFormat = context->cursor->charFormat();
|
|
QTextBlockFormat oldBlockFormat = context->cursor->blockFormat();
|
|
|
|
QTextBlockFormat blockFormat;
|
|
blockFormat.setProperty( QTextFormat::BlockTrailingHorizontalRulerWidth, "100%");
|
|
context->cursor->insertBlock( blockFormat );
|
|
context->cursor->insertBlock( oldBlockFormat );
|
|
context->cursor->setCharFormat( charFormat );
|
|
}
|
|
else if (fctype == PLKR_TFC_ALIGN) {
|
|
current_alignment = 0;
|
|
|
|
if (*ptr < 4) {
|
|
QTextBlockFormat format( context->cursor->blockFormat() );
|
|
if (*ptr == 0)
|
|
format.setAlignment( Qt::AlignLeft );
|
|
else if (*ptr == 1)
|
|
format.setAlignment( Qt::AlignRight );
|
|
else if (*ptr == 2)
|
|
format.setAlignment( Qt::AlignCenter );
|
|
else if (*ptr == 3)
|
|
format.setAlignment( Qt::AlignJustify );
|
|
|
|
QTextCharFormat charFormat( context->cursor->charFormat() );
|
|
context->cursor->insertBlock( format );
|
|
context->cursor->setCharFormat( charFormat );
|
|
|
|
current_alignment = (*ptr) + 1;
|
|
}
|
|
|
|
}
|
|
else if (fctype == PLKR_TFC_MARGINS) {
|
|
|
|
/* Not easy to set, in HTML */
|
|
#if 0
|
|
output += QString( "<!-- margins: %1, %2 -->" ).arg(ptr[0], ptr[1]);
|
|
if (current_left_margin != ptr[0]
|
|
|| current_right_margin != ptr[1]) {
|
|
if (current_right_margin != 0)
|
|
fprintf (fp, "</td><td width=%d> ",
|
|
current_right_margin);
|
|
fprintf (fp, "</td></tr></table>\n");
|
|
}
|
|
current_left_margin = ptr[0];
|
|
current_right_margin = ptr[1];
|
|
if (current_right_margin > 0
|
|
|| current_left_margin > 0) {
|
|
fprintf (fp, "<table border=1><tr>");
|
|
if (current_left_margin != 0) {
|
|
fprintf (fp, "<td width=%d align=right>",
|
|
current_left_margin);
|
|
if ((ptr - run) > 2) {
|
|
fwrite (run, 1, ((ptr - 2) - run), fp);
|
|
textlen += ((ptr - 2) - run);
|
|
}
|
|
else {
|
|
fprintf (fp, " ");
|
|
}
|
|
fprintf (fp, "</td>");
|
|
}
|
|
fprintf (fp, "<td>");
|
|
if (current_left_margin == 0 && (ptr - run) > 2) {
|
|
fwrite (run, 1, ((ptr - 2) - run), fp);
|
|
textlen += ((ptr - 2) - run);
|
|
}
|
|
}
|
|
else {
|
|
if ((ptr - run) > 2) {
|
|
fwrite (run, 1, ((ptr - 2) - run), fp);
|
|
textlen += ((ptr - 2) - run);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
current_left_margin = ptr[0];
|
|
current_right_margin = ptr[1];
|
|
|
|
}
|
|
else if (fctype == PLKR_TFC_COLOR) {
|
|
|
|
/* not sure what to do here yet */
|
|
/*
|
|
fprintf (fp, "<!-- color=\"#%02x%02x%02x\" -->",
|
|
ptr[0], ptr[1], ptr[2]);*/
|
|
current_color =
|
|
(ptr[0] << 16) + (ptr[1] << 8) + ptr[2];
|
|
|
|
} else if (fctype == PLKR_TFC_IMAGE || fctype == PLKR_TFC_IMAGE2) {
|
|
QTextCharFormat format = context->cursor->charFormat();
|
|
context->cursor->insertImage( QString( "%1.jpg" ).arg( (ptr[0] << 8) + ptr[1] ) );
|
|
context->images.append( (ptr[0] << 8) + ptr[1] );
|
|
context->cursor->setCharFormat( format );
|
|
AddRecord ((ptr[0] << 8) + ptr[1]);
|
|
|
|
}
|
|
else if (fctype == PLKR_TFC_TABLE) {
|
|
|
|
int record_id, datalen;
|
|
plkr_DataRecordType type = (plkr_DataRecordType)0;
|
|
unsigned char *bytes = NULL;
|
|
|
|
record_id = (ptr[0] << 8) + ptr[1];
|
|
bytes =
|
|
plkr_GetRecordBytes (doc, record_id, &datalen,
|
|
&type);
|
|
|
|
TranscribeTableRecord (doc, context, bytes);
|
|
|
|
}
|
|
else if (fctype == PLKR_TFC_UCHAR) {
|
|
|
|
if (fclen == 3)
|
|
context->cursor->insertText( QChar( (ptr[1] << 8) + ptr[2] ) );
|
|
else if (fclen == 5)
|
|
context->cursor->insertText( QChar( (ptr[3] << 8) + ptr[4] ) );
|
|
/* skip over alternate text */
|
|
ptr += ptr[0];
|
|
|
|
}
|
|
else {
|
|
|
|
/* ignore function */
|
|
//output += QString( "<!-- function code %1 ignored -->" ).arg(fctype);
|
|
|
|
}
|
|
|
|
ptr += fclen;
|
|
run = ptr;
|
|
}
|
|
else {
|
|
ptr++;
|
|
}
|
|
|
|
}
|
|
|
|
if ((ptr - run) > 0) {
|
|
/* output any pending text at the end of the paragraph */
|
|
context->cursor->insertText( QString::fromLatin1( (char *)run, ptr - run ) );
|
|
textlen += (ptr - run);
|
|
run = ptr;
|
|
}
|
|
|
|
/* clear the graphics state again */
|
|
|
|
if (current_font > 0 && current_font < 9 ) {
|
|
if ( !context->stack.isEmpty() )
|
|
context->cursor->setCharFormat( context->stack.pop() );
|
|
}
|
|
|
|
if (current_italic) {
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
format.setFontItalic( false );
|
|
context->cursor->setCharFormat( format );
|
|
}
|
|
if (current_underline) {
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
format.setFontUnderline( false );
|
|
context->cursor->setCharFormat( format );
|
|
}
|
|
if (current_struckthrough) {
|
|
QTextCharFormat format( context->cursor->charFormat() );
|
|
format.setFontStrikeOut( false );
|
|
context->cursor->setCharFormat( format );
|
|
}
|
|
#if 0
|
|
if (current_alignment > 0) {
|
|
context->cursor->insertBlock();
|
|
}
|
|
|
|
if (current_right_margin > 0)
|
|
fprintf (fp, "</td><td width=%d> </td></tr></table>",
|
|
current_right_margin);
|
|
else if (current_left_margin > 0)
|
|
fprintf (fp, "</td></tr></table>");
|
|
|
|
/* end the paragraph */
|
|
context->cursor->insertBlock();
|
|
#endif
|
|
}
|
|
free (paragraphs);
|
|
return true;
|
|
}
|
|
|
|
bool QUnpluck::TranscribeRecord( int index )
|
|
{
|
|
plkr_DataRecordType type;
|
|
int data_len;
|
|
bool status = true;
|
|
|
|
unsigned char *data = plkr_GetRecordBytes( mDocument, index, &data_len, &type);
|
|
if ( !data ) {
|
|
MarkRecordDone( index );
|
|
return false;
|
|
}
|
|
|
|
if (type == PLKR_DRTYPE_TEXT_COMPRESSED || type == PLKR_DRTYPE_TEXT) {
|
|
QTextDocument *document = new QTextDocument;
|
|
|
|
QTextFrameFormat format( document->rootFrame()->frameFormat() );
|
|
format.setMargin( 20 );
|
|
document->rootFrame()->setFrameFormat( format );
|
|
|
|
Context *context = new Context;
|
|
context->recordId = index;
|
|
context->document = document;
|
|
context->cursor = new QTextCursor( document );
|
|
|
|
QTextCharFormat charFormat;
|
|
charFormat.setFontPointSize( 10 );
|
|
charFormat.setFontFamily( "Helvetica" );
|
|
context->cursor->setCharFormat( charFormat );
|
|
|
|
status = TranscribeTextRecord( mDocument, index, context, data, type );
|
|
document->setTextWidth( 600 );
|
|
|
|
delete context->cursor;
|
|
mContext.append( context );
|
|
} else if (type == PLKR_DRTYPE_IMAGE_COMPRESSED || type == PLKR_DRTYPE_IMAGE) {
|
|
QImage image = TranscribeImageRecord( data );
|
|
mImages.insert( index, image );
|
|
} else if (type == PLKR_DRTYPE_MULTIIMAGE) {
|
|
QImage image;
|
|
if ( TranscribeMultiImageRecord( mDocument, image, data ) )
|
|
mImages.insert( index, image );
|
|
} else {
|
|
status = false;
|
|
}
|
|
|
|
// plkr_GetHomeRecordID (doc)))
|
|
|
|
MarkRecordDone( index );
|
|
|
|
return status;
|
|
}
|
|
|
|
|