You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegraphics/kviewshell/plugins/djvu/libdjvu/DjVuAnno.cpp

1515 lines
39 KiB

//C- -*- C++ -*-
//C- -------------------------------------------------------------------
//C- DjVuLibre-3.5
//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun.
//C- Copyright (c) 2001 AT&T
//C-
//C- This software is subject to, and may be distributed under, the
//C- GNU General Public License, Version 2. The license should have
//C- accompanied the software or you may obtain a copy of the license
//C- from the Free Software Foundation at http://www.fsf.org .
//C-
//C- This program is distributed in the hope that it will be useful,
//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//C- GNU General Public License for more details.
//C-
//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library
//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech
//C- Software authorized us to replace the original DjVu(r) Reference
//C- Library notice by the following text (see doc/lizard2002.djvu):
//C-
//C- ------------------------------------------------------------------
//C- | DjVu (r) Reference Library (v. 3.5)
//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
//C- | The DjVu Reference Library is protected by U.S. Pat. No.
//C- | 6,058,214 and patents pending.
//C- |
//C- | This software is subject to, and may be distributed under, the
//C- | GNU General Public License, Version 2. The license should have
//C- | accompanied the software or you may obtain a copy of the license
//C- | from the Free Software Foundation at http://www.fsf.org .
//C- |
//C- | The computer code originally released by LizardTech under this
//C- | license and unmodified by other parties is deemed "the LIZARDTECH
//C- | ORIGINAL CODE." Subject to any third party intellectual property
//C- | claims, LizardTech grants recipient a worldwide, royalty-free,
//C- | non-exclusive license to make, use, sell, or otherwise dispose of
//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
//C- | General Public License. This grant only confers the right to
//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
//C- | the extent such infringement is reasonably necessary to enable
//C- | recipient to make, have made, practice, sell, or otherwise dispose
//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
//C- | any greater extent that may be necessary to utilize further
//C- | modifications or combinations.
//C- |
//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
//C- +------------------------------------------------------------------
//
// $Id: DjVuAnno.cpp,v 1.12 2004/04/17 23:56:11 leonb Exp $
// $Name: release_3_5_15 $
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#if NEED_GNUG_PRAGMAS
# pragma implementation
#endif
#include "DjVuAnno.h"
#include "GContainer.h"
#include "GException.h"
#include "IFFByteStream.h"
#include "BSByteStream.h"
#include "GMapAreas.h"
#include "debug.h"
#include <ctype.h>
#ifdef HAVE_NAMESPACES
namespace DJVU {
# ifdef NOT_DEFINED // Just to fool emacs c++ mode
}
#endif
#endif
// GLParser.h and GLParser.cpp used to be separate files capable to decode
// that weird ANTa chunk format into C++ structures and lists. But since
// its implementation is temporary and is used only in this file (DjVuAnno.cpp)
// it appears reasonable to build it in here.
//***************************************************************************
//****************************** GLParser.h *********************************
//***************************************************************************
class GLObject : public GPEnabled
{
public:
enum GLObjectType { INVALID=0, NUMBER=1, STRING=2, SYMBOL=3, LIST=4 };
static const char * const GLObjectString[LIST+1];
GLObject(int _number=0);
GLObject(GLObjectType type, const char * str);
GLObject(const char * name, const GPList<GLObject> & list);
virtual ~GLObject(void);
int get_number(void) const;
GUTF8String get_string(void) const;
GUTF8String get_symbol(void) const;
GPList<GLObject> & get_list(void);
GP<GLObject> operator[](int n) const;
GLObjectType get_type(void) const;
GUTF8String get_name(void) const;
void print(ByteStream & str, int compact=1, int indent=0, int * cur_pos=0) const;
private:
GLObjectType type;
GUTF8String name;
int number;
GUTF8String string;
GUTF8String symbol;
GPList<GLObject> list;
void throw_can_not_convert_to(const GLObjectType to) const;
};
const char * const GLObject::GLObjectString[]=
{"invalid", "number", "string", "symbol", "list"};
inline GLObject::GLObjectType
GLObject::get_type(void) const { return type; }
inline
GLObject::~GLObject(void) {}
class GLToken
{
public:
enum GLTokenType { OPEN_PAR, CLOSE_PAR, OBJECT };
GLTokenType type;
GP<GLObject> object;
GLToken(GLTokenType type, const GP<GLObject> & object);
};
inline
GLToken::GLToken(GLTokenType xtype, const GP<GLObject> & xobject) :
type(xtype), object(xobject) {}
class GLParser
{
public:
void parse(const char * str);
GPList<GLObject> & get_list(void);
GP<GLObject> get_object(const char * name, bool last=true);
void print(ByteStream & str, int compact=1);
GLParser(void);
GLParser(const char * str);
~GLParser(void);
private:
GPList<GLObject> list;
bool compat;
void skip_white_space(const char * & start);
void check_compat(const char *str);
GLToken get_token(const char * & start);
void parse(const char * cur_name, GPList<GLObject> & list,
const char * & start);
};
GLParser::GLParser(void)
: compat(false)
{
}
GLParser::~GLParser(void)
{
}
GPList<GLObject> &
GLParser::get_list(void)
{
return list;
}
GLParser::GLParser(const char * str)
: compat(false)
{
parse(str);
}
//***************************************************************************
//***************************** GLParser.cpp ********************************
//***************************************************************************
GLObject::GLObject(int xnumber) : type(NUMBER), number(xnumber) {}
GLObject::GLObject(GLObjectType xtype, const char * str) : type(xtype)
{
if (type!=STRING && type!=SYMBOL)
G_THROW( ERR_MSG("DjVuAnno.bad_type") );
if (type==STRING)
string=str;
else symbol=str;
}
GLObject::GLObject(const char * xname, const GPList<GLObject> & xlist) :
type(LIST), name(xname), list(xlist) {}
void
GLObject::print(ByteStream & str, int compact, int indent, int * cur_pos) const
{
int local_cur_pos = 0;
if (!cur_pos) { cur_pos = &local_cur_pos; }
GUTF8String buffer;
const char * to_print=0;
switch(type)
{
case NUMBER:
to_print=buffer.format("%d",number);
break;
case STRING:
{
int length = string.length();
const char *data = (const char*)string;
buffer = GUTF8String("\"");
while (*data && length>0)
{
int span = 0;
while (span<length && (unsigned char)(data[span])>=0x20 &&
data[span]!=0x7f && data[span]!='"' && data[span]!='\\' )
span++;
if (span > 0)
{
buffer = buffer + GUTF8String(data, span);
data += span;
length -= span;
}
else
{
char buf[8];
static const char *tr1 = "\"\\tnrbf";
static const char *tr2 = "\"\\\t\n\r\b\f";
sprintf(buf,"\\%03o", (int)(((unsigned char*)data)[span]));
for (int i=0; tr2[i]; i++)
if (data[span] == tr2[i])
buf[1] = tr1[i];
if (buf[1]<'0' || buf[1]>'3')
buf[2] = 0;
buffer = buffer + GUTF8String(buf);
data += 1;
length -= 1;
}
}
buffer = buffer + GUTF8String("\"");
to_print = buffer;
}
break;
case SYMBOL:
to_print=buffer.format("%s",(const char *)symbol);
break;
case LIST:
to_print=buffer.format("(%s",(const char *)name);
break;
case INVALID:
break;
}
if (!compact && *cur_pos+strlen(to_print)>70)
{
char ch='\n';
str.write(&ch, 1);
ch=' ';
for(int i=0;i<indent;i++) str.write(&ch, 1);
*cur_pos=indent;
}
str.write(to_print, strlen(to_print));
char ch=' ';
str.write(&ch, 1);
*cur_pos+=strlen(to_print)+1;
if (type==LIST)
{
int indent=*cur_pos-strlen(to_print);
for(GPosition pos=list;pos;++pos)
list[pos]->print(str, compact, indent, cur_pos);
str.write(") ", 2);
*cur_pos+=2;
}
}
// This function constructs message names for external lookup.
// The message names are constructed to avoid the problems of concatenating
// phrases (which does not translate well into other languages). The
// message names that can be generated are (listed here to appease the
// auditing program which reads comments):
// ERR_MSG("DjVuAnno.invalid2number"), ERR_MSG("DjVuAnno.string2number"),
// ERR_MSG("DjVuAnno.symbol2number"), ERR_MSG("DjVuAnno.list2number")
// ERR_MSG("DjVuAnno.invalid2string"), ERR_MSG("DjVuAnno.number2string"),
// ERR_MSG("DjVuAnno.symbol2string"), ERR_MSG("DjVuAnno.list2string")
// ERR_MSG("DjVuAnno.invalid2symbol"), ERR_MSG("DjVuAnno.number2symbol"),
// ERR_MSG("DjVuAnno.string2symbol"), ERR_MSG("DjVuAnno.list2symbol")
// ERR_MSG("DjVuAnno.invalid2list"), ERR_MSG("DjVuAnno.number2list"),
// ERR_MSG("DjVuAnno.string2list"), ERR_MSG("DjVuAnno.symbol2list")
void
GLObject::throw_can_not_convert_to(const GLObjectType to) const
{
static const GUTF8String two('2');
static const GUTF8String tab('\t');
GUTF8String mesg("DjVuAnno.");
switch(type)
{
case NUMBER:
mesg+=GLObjectString[NUMBER]+two+GLObjectString[to]+tab+GUTF8String(number);
break;
case STRING:
mesg+=GLObjectString[STRING]+two+GLObjectString[to]+tab+string;
break;
case SYMBOL:
mesg+=GLObjectString[SYMBOL]+two+GLObjectString[to]+tab+symbol;
break;
case LIST:
mesg+=GLObjectString[LIST]+two+GLObjectString[to]+tab+name;
break;
default:
mesg+=GLObjectString[INVALID]+two+GLObjectString[to];
break;
}
G_THROW(mesg);
}
GUTF8String
GLObject::get_string(void) const
{
if (type!=STRING)
{
throw_can_not_convert_to(STRING);
}
return string;
}
GUTF8String
GLObject::get_symbol(void) const
{
if (type!=SYMBOL)
{
throw_can_not_convert_to(SYMBOL);
}
return symbol;
}
int
GLObject::get_number(void) const
{
if (type!=NUMBER)
{
throw_can_not_convert_to(NUMBER);
}
return number;
}
GUTF8String
GLObject::get_name(void) const
{
if (type!=LIST)
{
throw_can_not_convert_to(LIST);
}
return name;
}
GP<GLObject>
GLObject::operator[](int n) const
{
if (type!=LIST)
{
throw_can_not_convert_to(LIST);
}
if (n>=list.size()) G_THROW( ERR_MSG("DjVuAnno.too_few") "\t"+name);
int i;
GPosition pos;
for(i=0, pos=list;i<n && pos;i++, ++pos)
continue;
return list[pos];
}
GPList<GLObject> &
GLObject::get_list(void)
{
if (type!=LIST)
{
throw_can_not_convert_to(LIST);
}
return list;
}
//********************************** GLParser *********************************
void
GLParser::skip_white_space(const char * & start)
{
while(*start && isspace(*start)) start++;
if (!*start)
G_THROW( ByteStream::EndOfFile );
}
GLToken
GLParser::get_token(const char * & start)
{
skip_white_space(start);
char c = *start;
if (c == '(')
{
start++;
return GLToken(GLToken::OPEN_PAR, 0);
}
else if (c == ')')
{
start++;
return GLToken(GLToken::CLOSE_PAR, 0);
}
else if (c=='-' || (c>='0' && c<='9'))
{
return GLToken(GLToken::OBJECT,
new GLObject(strtol(start, (char **) &start, 10)));
}
else if (c=='"')
{
GUTF8String str;
start++;
while(1)
{
int span = 0;
while (start[span] && start[span]!='\\' && start[span]!='\"')
span++;
if (span > 0)
{
str = str + GUTF8String(start,span);
start += span;
}
else if (start[0]=='\"')
{
start += 1;
break;
}
else if (start[0]=='\\' && compat)
{
char c = start[1];
if (c == '\"')
{
start += 2;
str += '\"';
}
else
{
start += 1;
str += '\\';
}
}
else if (start[0]=='\\' && start[1])
{
char c = *++start;
if (c>='0' && c<='7')
{
int x = 0;
for (int i=0; i<3 && c>='0' && c<='7'; i++)
{
x = x * 8 + c - '0';
c = *++start;
}
str += (char)(x & 0xff);
}
else
{
const static char *tr1 = "tnrbfva";
const static char *tr2 = "\t\n\r\b\f\013\007";
for (int i=0; tr1[i]; i++)
if (c == tr1[i])
c = tr2[i];
start += 1;
str += c;
}
}
else
{
G_THROW( ByteStream::EndOfFile );
}
}
return GLToken(GLToken::OBJECT,
new GLObject(GLObject::STRING, str));
}
else
{
GUTF8String str;
while(1)
{
char ch=*start++;
if (!ch)
G_THROW( ByteStream::EndOfFile );
if (ch==')') { start--; break; }
if (isspace(ch)) break;
str+=ch;
}
return GLToken(GLToken::OBJECT, new GLObject(GLObject::SYMBOL, str));
}
}
void
GLParser::parse(const char * cur_name, GPList<GLObject> & list,
const char * & start)
{
DEBUG_MSG("GLParse::parse(): Parsing contents of object '" << cur_name << "'\n");
DEBUG_MAKE_INDENT(3);
while(1)
{
GLToken token=get_token(start);
if (token.type==GLToken::OPEN_PAR)
{
if (isspace(*start))
{
GUTF8String mesg=GUTF8String( ERR_MSG("DjVuAnno.paren") "\t")+cur_name;
G_THROW(mesg);
}
GLToken tok=get_token(start);
GP<GLObject> object=tok.object; // This object should be SYMBOL
// We will convert it to LIST later
if (tok.type!=GLToken::OBJECT || object->get_type()!=GLObject::SYMBOL)
{
if (tok.type==GLToken::OPEN_PAR ||
tok.type==GLToken::CLOSE_PAR)
{
GUTF8String mesg=GUTF8String( ERR_MSG("DjVuAnno.no_paren") "\t")+cur_name;
G_THROW(mesg);
}
if (tok.type==GLToken::OBJECT)
{
GLObject::GLObjectType type=object->get_type();
if (type==GLObject::NUMBER)
{
GUTF8String mesg( ERR_MSG("DjVuAnno.no_number") "\t");
mesg += cur_name;
G_THROW(mesg);
}
else if (type==GLObject::STRING)
{
GUTF8String mesg( ERR_MSG("DjVuAnno.no_string") "\t");
mesg += cur_name;
G_THROW(mesg);
}
}
}
// OK. Get the object contents
GPList<GLObject> new_list;
G_TRY
{
parse(object->get_symbol(), new_list, start);
}
G_CATCH(exc)
{
if (exc.cmp_cause(ByteStream::EndOfFile))
G_RETHROW;
}
G_ENDCATCH;
list.append(new GLObject(object->get_symbol(), new_list));
continue;
}
if (token.type==GLToken::CLOSE_PAR)
return;
list.append(token.object);
}
}
void
GLParser::check_compat(const char *s)
{
int state = 0;
while (s && *s && !compat)
{
switch(state)
{
case 0:
if (*s == '\"')
state = '\"';
break;
case '\"':
if (*s == '\"')
state = 0;
else if (*s == '\\')
state = '\\';
else if ((unsigned char)(*s)<0x20 || *s==0x7f)
compat = true;
break;
case '\\':
if (!strchr("01234567tnrbfva\"\\",*s))
compat = true;
state = '\"';
break;
}
s += 1;
}
}
void
GLParser::parse(const char * str)
{
DEBUG_MSG("GLParser::parse(): parsing string contents\n");
DEBUG_MAKE_INDENT(3);
G_TRY
{
check_compat(str);
parse("toplevel", list, str);
} G_CATCH(exc)
{
if (exc.cmp_cause(ByteStream::EndOfFile))
G_RETHROW;
} G_ENDCATCH;
}
void
GLParser::print(ByteStream & str, int compact)
{
for(GPosition pos=list;pos;++pos)
list[pos]->print(str, compact);
}
GP<GLObject>
GLParser::get_object(const char * name, bool last)
{
GP<GLObject> object;
for(GPosition pos=list;pos;++pos)
{
GP<GLObject> obj=list[pos];
if (obj->get_type()==GLObject::LIST &&
obj->get_name()==name)
{
object=obj;
if (!last) break;
}
}
return object;
}
//***************************************************************************
//********************************** ANT ************************************
//***************************************************************************
static const char *zoom_strings[]={
"default","page","width","one2one","stretch"};
static const int zoom_strings_size=sizeof(zoom_strings)/sizeof(const char *);
static const char *mode_strings[]={
"default","color","fore","back","bw"};
static const int mode_strings_size=sizeof(mode_strings)/sizeof(const char *);
static const char *align_strings[]={
"default","left","center","right","top","bottom"};
static const int align_strings_size=sizeof(align_strings)/sizeof(const char *);
#define PNOTE_TAG "pnote"
#define BACKGROUND_TAG "background"
#define ZOOM_TAG "zoom"
#define MODE_TAG "mode"
#define ALIGN_TAG "align"
#define HALIGN_TAG "halign"
#define VALIGN_TAG "valign"
#define METADATA_TAG "metadata"
static const unsigned long default_bg_color=0xffffffff;
DjVuANT::DjVuANT(void)
{
bg_color=default_bg_color;
zoom=0;
mode=MODE_UNSPEC;
hor_align=ver_align=ALIGN_UNSPEC;
}
DjVuANT::~DjVuANT()
{
}
GUTF8String
DjVuANT::get_paramtags(void) const
{
GUTF8String retval;
if(zoom > 0)
{
retval+="<PARAM name=\"" ZOOM_TAG "\" value=\""+GUTF8String(zoom)+="\" />\n";
}else if(zoom && ((-zoom)<zoom_strings_size))
{
retval+="<PARAM name=\"" ZOOM_TAG "\" value=\""+GUTF8String(zoom_strings[-zoom])+"\" />\n";
}
if((mode>0)&&(mode<mode_strings_size))
{
retval+="<PARAM name=\"" MODE_TAG "\" value=\""+GUTF8String(mode_strings[mode])+"\" />\n";
}
if((hor_align>ALIGN_UNSPEC)&&(hor_align<align_strings_size))
{
retval+="<PARAM name=\"" HALIGN_TAG "\" value=\""+GUTF8String(align_strings[hor_align])+"\" />\n";
}
if((ver_align>ALIGN_UNSPEC)&&(ver_align<align_strings_size))
{
retval+="<PARAM name=\"" VALIGN_TAG "\" value=\""+GUTF8String(align_strings[ver_align])+"\" />\n";
}
if((bg_color&0xffffff) == bg_color)
{
retval+="<PARAM name=\"" BACKGROUND_TAG "\" value=\""+GUTF8String().format("#%06lX",bg_color)+"\" />\n";
}
return retval;
}
void
DjVuANT::writeParam(ByteStream &str_out) const
{
str_out.writestring(get_paramtags());
}
GUTF8String
DjVuANT::get_xmlmap(const GUTF8String &name,const int height) const
{
GUTF8String retval("<MAP name=\""+name.toEscaped()+"\" >\n");
for(GPosition pos(map_areas);pos;++pos)
{
retval+=map_areas[pos]->get_xmltag(height);
}
return retval+"</MAP>\n";
}
void
DjVuANT::writeMap(
ByteStream &str_out,const GUTF8String &name,const int height) const
{
str_out.writestring("<MAP name=\""+name.toEscaped()+"\" >\n");
for(GPosition pos(map_areas);pos;++pos)
{
str_out.writestring(GUTF8String(map_areas[pos]->get_xmltag(height)));
}
str_out.writestring(GUTF8String("</MAP>\n"));
}
GUTF8String
DjVuANT::read_raw(ByteStream & str)
{
GUTF8String raw;
char buffer[1024];
int length;
while((length=str.read(buffer, 1024)))
raw+=GUTF8String(buffer, length);
return raw;
}
void
DjVuANT::decode(class GLParser & parser)
{
bg_color=get_bg_color(parser);
zoom=get_zoom(parser);
mode=get_mode(parser);
hor_align=get_hor_align(parser);
ver_align=get_ver_align(parser);
map_areas=get_map_areas(parser);
#ifndef NO_METADATA_IN_ANT_CHUNK
metadata=get_metadata(parser);
#endif
}
void
DjVuANT::decode(ByteStream & str)
{
GLParser parser(read_raw(str));
decode(parser);
}
void
DjVuANT::merge(ByteStream & str)
{
GLParser parser(encode_raw());
GUTF8String add_raw=read_raw(str);
parser.parse(add_raw);
decode(parser);
}
void
DjVuANT::encode(ByteStream &bs)
{
GUTF8String raw=encode_raw();
bs.writall((const char*) raw, raw.length());
}
unsigned int
DjVuANT::get_memory_usage() const
{
return sizeof(DjVuANT);
}
unsigned char
DjVuANT::decode_comp(char ch1, char ch2)
{
unsigned char dig1=0;
if (ch1)
{
ch1=toupper(ch1);
if (ch1>='0' && ch1<='9') dig1=ch1-'0';
if (ch1>='A' && ch1<='F') dig1=10+ch1-'A';
unsigned char dig2=0;
if (ch2)
{
ch2=toupper(ch2);
if (ch2>='0' && ch2<='9') dig2=ch2-'0';
if (ch2>='A' && ch2<='F') dig2=10+ch2-'A';
return (dig1 << 4) | dig2;
}
return dig1;
}
return 0;
}
unsigned long int
DjVuANT::cvt_color(const char * color, unsigned long int def)
{
if (color[0]!='#') return def;
unsigned long int color_rgb=0;
color++;
const char * start, * end;
// Do blue
end=color+strlen(color); start=end-2;
if (start<color) start=color;
if (end>start)
color_rgb|=decode_comp(start[0], start+1<end ? start[1] : 0);
// Do green
end=color+strlen(color)-2; start=end-2;
if (start<color) start=color;
if (end>start)
color_rgb|=decode_comp(start[0], start+1<end ? start[1] : 0) << 8;
// Do red
end=color+strlen(color)-4; start=end-2;
if (start<color) start=color;
if (end>start)
color_rgb|=decode_comp(start[0], start+1<end ? start[1] : 0) << 16;
// Do the fourth byte
end=color+strlen(color)-6; start=end-2;
if (start<color) start=color;
if (end>start)
color_rgb|=decode_comp(start[0], start+1<end ? start[1] : 0) << 24;
return color_rgb;
}
unsigned long int
DjVuANT::get_bg_color(GLParser & parser)
{
unsigned long retval=default_bg_color;
DEBUG_MSG("DjVuANT::get_bg_color(): getting background color ...\n");
DEBUG_MAKE_INDENT(3);
G_TRY
{
GP<GLObject> obj=parser.get_object(BACKGROUND_TAG);
if (obj && obj->get_list().size()==1)
{
GUTF8String color=(*obj)[0]->get_symbol();
DEBUG_MSG("color='" << color << "'\n");
retval=cvt_color(color, 0xffffff);
}
#ifndef NDEBUG
if(retval == default_bg_color)
{
DEBUG_MSG("can't find any.\n");
}
#endif // NDEBUG
} G_CATCH_ALL {} G_ENDCATCH;
#ifndef NDEBUG
if(retval == default_bg_color)
{
DEBUG_MSG("resetting color to 0xffffffff (UNSPEC)\n");
}
#endif // NDEBUG
return retval;
}
int
DjVuANT::get_zoom(GLParser & parser)
// Returns:
// <0 - special zoom (like ZOOM_STRETCH)
// =0 - not set
// >0 - numeric zoom (%%)
{
int retval=ZOOM_UNSPEC;
DEBUG_MSG("DjVuANT::get_zoom(): getting zoom factor ...\n");
DEBUG_MAKE_INDENT(3);
G_TRY
{
GP<GLObject> obj=parser.get_object(ZOOM_TAG);
if (obj && obj->get_list().size()==1)
{
const GUTF8String zoom((*obj)[0]->get_symbol());
DEBUG_MSG("zoom='" << zoom << "'\n");
for(int i=0;(i<zoom_strings_size);++i)
{
if(zoom == zoom_strings[i])
{
retval=(-i);
break;
}
}
if(retval == ZOOM_UNSPEC)
{
if (zoom[0]!='d')
{
G_THROW( ERR_MSG("DjVuAnno.bad_zoom") );
}else
{
retval=zoom.substr(1, zoom.length()).toInt(); //atoi((const char *) zoom+1);
}
}
}
#ifndef NDEBUG
if(retval == ZOOM_UNSPEC)
{
DEBUG_MSG("can't find any.\n");
}
#endif // NDEBUG
} G_CATCH_ALL {} G_ENDCATCH;
#ifndef NDEBUG
if(retval == ZOOM_UNSPEC)
{
DEBUG_MSG("resetting zoom to 0 (UNSPEC)\n");
}
#endif // NDEBUG
return retval;
}
int
DjVuANT::get_mode(GLParser & parser)
{
int retval=MODE_UNSPEC;
DEBUG_MSG("DjVuAnt::get_mode(): getting default mode ...\n");
DEBUG_MAKE_INDENT(3);
G_TRY
{
GP<GLObject> obj=parser.get_object(MODE_TAG);
if (obj && obj->get_list().size()==1)
{
const GUTF8String mode((*obj)[0]->get_symbol());
DEBUG_MSG("mode='" << mode << "'\n");
for(int i=0;(i<mode_strings_size);++i)
{
if(mode == mode_strings[i])
{
retval=i;
break;
}
}
}
#ifndef NDEBUG
if(retval == MODE_UNSPEC)
{
DEBUG_MSG("can't find any.\n");
}
#endif // NDEBUG
} G_CATCH_ALL {} G_ENDCATCH;
#ifndef NDEBUG
if(retval == MODE_UNSPEC)
{
DEBUG_MSG("resetting mode to MODE_UNSPEC\n");
}
#endif // NDEBUG
return retval;
}
static inline DjVuANT::alignment
legal_halign(const int i)
{
DjVuANT::alignment retval;
switch((DjVuANT::alignment)i)
{
case DjVuANT::ALIGN_LEFT:
case DjVuANT::ALIGN_CENTER:
case DjVuANT::ALIGN_RIGHT:
retval=(DjVuANT::alignment)i;
break;
default:
retval=DjVuANT::ALIGN_UNSPEC;
break;
}
return retval;
}
static inline DjVuANT::alignment
legal_valign(const int i)
{
DjVuANT::alignment retval;
switch((DjVuANT::alignment)i)
{
case DjVuANT::ALIGN_CENTER:
case DjVuANT::ALIGN_TOP:
case DjVuANT::ALIGN_BOTTOM:
retval=(DjVuANT::alignment)i;
break;
default:
retval=DjVuANT::ALIGN_UNSPEC;
break;
}
return retval;
}
DjVuANT::alignment
DjVuANT::get_hor_align(GLParser & parser)
{
DEBUG_MSG("DjVuAnt::get_hor_align(): getting hor page alignemnt ...\n");
DEBUG_MAKE_INDENT(3);
alignment retval=ALIGN_UNSPEC;
G_TRY
{
GP<GLObject> obj=parser.get_object(ALIGN_TAG);
if (obj && obj->get_list().size()==2)
{
const GUTF8String align((*obj)[0]->get_symbol());
DEBUG_MSG("hor_align='" << align << "'\n");
for(int i=(int)ALIGN_UNSPEC;(i<align_strings_size);++i)
{
const alignment j=legal_halign(i);
if((i == (int)j)&&(align == align_strings[i]))
{
retval=j;
break;
}
}
}
#ifndef NDEBUG
if(retval == ALIGN_UNSPEC)
{
DEBUG_MSG("can't find any.\n");
}
#endif // NDEBUG
} G_CATCH_ALL {} G_ENDCATCH;
#ifndef NDEBUG
if(retval == ALIGN_UNSPEC)
{
DEBUG_MSG("resetting alignment to ALIGN_UNSPEC\n");
}
#endif // NDEBUG
return retval;
}
DjVuANT::alignment
DjVuANT::get_ver_align(GLParser & parser)
{
DEBUG_MSG("DjVuAnt::get_ver_align(): getting vert page alignemnt ...\n");
DEBUG_MAKE_INDENT(3);
alignment retval=ALIGN_UNSPEC;
G_TRY
{
GP<GLObject> obj=parser.get_object(ALIGN_TAG);
if (obj && obj->get_list().size()==2)
{
const GUTF8String align((*obj)[1]->get_symbol());
DEBUG_MSG("ver_align='" << align << "'\n");
for(int i=(int)ALIGN_UNSPEC;(i<align_strings_size);++i)
{
const alignment j=legal_valign(i);
if((i == (int)j)&&(align == align_strings[i]))
{
retval=j;
break;
}
}
}
#ifndef NDEBUG
if(retval == ALIGN_UNSPEC)
{
DEBUG_MSG("can't find any.\n");
}
#endif // NDEBUG
} G_CATCH_ALL {} G_ENDCATCH;
#ifndef NDEBUG
if(retval == ALIGN_UNSPEC)
{
DEBUG_MSG("resetting alignment to ALIGN_UNSPEC\n");
}
#endif // NDEBUG
return retval;
}
#ifndef NO_METADATA_IN_ANT_CHUNK
GMap<GUTF8String, GUTF8String>
DjVuANT::get_metadata(GLParser & parser)
{
DEBUG_MSG("DjVuANT::get_map_areas(): forming and returning back list of map areas\n");
DEBUG_MAKE_INDENT(3);
GMap<GUTF8String, GUTF8String> mdata;
GPList<GLObject> list=parser.get_list();
for(GPosition pos=list;pos;++pos)
{
GLObject & obj=*list[pos];
if (obj.get_type()==GLObject::LIST && obj.get_name()==METADATA_TAG)
{
G_TRY
{
for(int obj_num=0;obj_num<obj.get_list().size();obj_num++)
{
GLObject & el=*obj[obj_num];
const int type = el.get_type();
if (type == GLObject::LIST)
{
const GUTF8String & name=el.get_name();
mdata[name]=(el[0])->get_string();
}
}
}
G_CATCH_ALL { } G_ENDCATCH;
}
}
return mdata;
}
#endif
GPList<GMapArea>
DjVuANT::get_map_areas(GLParser & parser)
{
DEBUG_MSG("DjVuANT::get_map_areas(): forming and returning back list of map areas\n");
DEBUG_MAKE_INDENT(3);
GPList<GMapArea> map_areas;
GPList<GLObject> list=parser.get_list();
for(GPosition pos=list;pos;++pos)
{
GLObject & obj=*list[pos];
const int type=obj.get_type();
if (type == GLObject::LIST)
{
const GUTF8String name=obj.get_name();
if(name == GMapArea::MAPAREA_TAG)
{
G_TRY {
// Getting the url
GUTF8String url;
GUTF8String target=GMapArea::TARGET_SELF;
GLObject & url_obj=*(obj[0]);
if (url_obj.get_type()==GLObject::LIST)
{
if (url_obj.get_name()!=GMapArea::URL_TAG)
G_THROW( ERR_MSG("DjVuAnno.bad_url") );
url=(url_obj[0])->get_string();
target=(url_obj[1])->get_string();
} else url=url_obj.get_string();
// Getting the comment
GUTF8String comment=(obj[1])->get_string();
DEBUG_MSG("found maparea '" << comment << "' (" <<
url << ":" << target << ")\n");
GLObject * shape=obj[2];
GP<GMapArea> map_area;
if (shape->get_type()==GLObject::LIST)
{
if (shape->get_name()==GMapArea::RECT_TAG)
{
DEBUG_MSG("it's a rectangle.\n");
GRect grect((*shape)[0]->get_number(),
(*shape)[1]->get_number(),
(*shape)[2]->get_number(),
(*shape)[3]->get_number());
GP<GMapRect> map_rect=GMapRect::create(grect);
map_area=(GMapRect *)map_rect;
} else if (shape->get_name()==GMapArea::POLY_TAG)
{
DEBUG_MSG("it's a polygon.\n");
int points=shape->get_list().size()/2;
GTArray<int> xx(points-1), yy(points-1);
for(int i=0;i<points;i++)
{
xx[i]=(*shape)[2*i]->get_number();
yy[i]=(*shape)[2*i+1]->get_number();
}
GP<GMapPoly> map_poly=GMapPoly::create(xx,yy,points);
map_area=(GMapPoly *)map_poly;
} else if (shape->get_name()==GMapArea::OVAL_TAG)
{
DEBUG_MSG("it's an ellipse.\n");
GRect grect((*shape)[0]->get_number(),
(*shape)[1]->get_number(),
(*shape)[2]->get_number(),
(*shape)[3]->get_number());
GP<GMapOval> map_oval=GMapOval::create(grect);
map_area=(GMapOval *)map_oval;
}
}
if (map_area)
{
map_area->url=url;
map_area->target=target;
map_area->comment=comment;
for(int obj_num=3;obj_num<obj.get_list().size();obj_num++)
{
GLObject * el=obj[obj_num];
if (el->get_type()==GLObject::LIST)
{
const GUTF8String & name=el->get_name();
if (name==GMapArea::BORDER_AVIS_TAG)
map_area->border_always_visible=true;
else if (name==GMapArea::HILITE_TAG)
{
GLObject * obj=el->get_list()[el->get_list().firstpos()];
if (obj->get_type()==GLObject::SYMBOL)
map_area->hilite_color=cvt_color(obj->get_symbol(), 0xff);
} else
{
int border_type=
name==GMapArea::NO_BORDER_TAG ? GMapArea::NO_BORDER :
name==GMapArea::XOR_BORDER_TAG ? GMapArea::XOR_BORDER :
name==GMapArea::SOLID_BORDER_TAG ? GMapArea::SOLID_BORDER :
name==GMapArea::SHADOW_IN_BORDER_TAG ? GMapArea::SHADOW_IN_BORDER :
name==GMapArea::SHADOW_OUT_BORDER_TAG ? GMapArea::SHADOW_OUT_BORDER :
name==GMapArea::SHADOW_EIN_BORDER_TAG ? GMapArea::SHADOW_EIN_BORDER :
name==GMapArea::SHADOW_EOUT_BORDER_TAG ? GMapArea::SHADOW_EOUT_BORDER : -1;
if (border_type>=0)
{
map_area->border_type=(GMapArea::BorderType) border_type;
for(GPosition pos=el->get_list();pos;++pos)
{
GLObject * obj=el->get_list()[pos];
if (obj->get_type()==GLObject::SYMBOL)
map_area->border_color=cvt_color(obj->get_symbol(), 0xff);
if (obj->get_type()==GLObject::NUMBER)
map_area->border_width=obj->get_number();
}
}
}
} // if (el->get_type()==...)
} // for(int obj_num=...)
map_areas.append(map_area);
} // if (map_area) ...
} G_CATCH_ALL {} G_ENDCATCH;
}
}
} // while(item==...)
DEBUG_MSG("map area list size = " << list.size() << "\n");
return map_areas;
}
void
DjVuANT::del_all_items(const char * name, GLParser & parser)
{
GPList<GLObject> & list=parser.get_list();
GPosition pos=list;
while(pos)
{
GLObject & obj=*list[pos];
if (obj.get_type()==GLObject::LIST &&
obj.get_name()==name)
{
GPosition this_pos=pos;
++pos;
list.del(this_pos);
} else ++pos;
}
}
GUTF8String
DjVuANT::encode_raw(void) const
{
GUTF8String buffer;
GLParser parser;
//*** Background color
del_all_items(BACKGROUND_TAG, parser);
if (bg_color!=default_bg_color)
{
buffer.format("(" BACKGROUND_TAG " #%02X%02X%02X)",
(unsigned int)((bg_color & 0xff0000) >> 16),
(unsigned int)((bg_color & 0xff00) >> 8),
(unsigned int)(bg_color & 0xff));
parser.parse(buffer);
}
//*** Zoom
del_all_items(ZOOM_TAG, parser);
if (zoom!=ZOOM_UNSPEC)
{
buffer="(" ZOOM_TAG " ";
const int i=1-zoom;
if((i>=0)&& (i<zoom_strings_size))
{
buffer+=zoom_strings[i];
}else
{
buffer+="d"+GUTF8String(zoom);
}
buffer+=")";
parser.parse(buffer);
}
//*** Mode
del_all_items(MODE_TAG, parser);
if (mode!=MODE_UNSPEC)
{
const int i=mode-1;
if((i>=0)&& (i<mode_strings_size))
{
buffer="(" MODE_TAG " " + GUTF8String(mode_strings[mode]) + ")";
}
parser.parse(buffer);
}
//*** Alignment
del_all_items(ALIGN_TAG, parser);
if (hor_align!=ALIGN_UNSPEC || ver_align!=ALIGN_UNSPEC)
{
buffer= GUTF8String("(" ALIGN_TAG " ")
+align_strings[((hor_align<ALIGN_UNSPEC)||
(hor_align>=align_strings_size))?ALIGN_UNSPEC:hor_align]
+" "+align_strings[((ver_align<ALIGN_UNSPEC)||
(ver_align>=align_strings_size))?ALIGN_UNSPEC:ver_align]+")";
parser.parse(buffer);
}
//*** Metadata
#ifndef NO_METADATA_IN_ANT_CHUNK
del_all_items(METADATA_TAG, parser);
if (!metadata.isempty())
{
GUTF8String mdatabuffer("(");
mdatabuffer += METADATA_TAG ;
for (GPosition pos=metadata; pos; ++pos)
mdatabuffer +=" (" + metadata.key(pos)+" \""+metadata[pos]+"\")";
mdatabuffer += " )";
parser.parse(mdatabuffer);
}
#endif
//*** Mapareas
del_all_items(GMapArea::MAPAREA_TAG, parser);
for(GPosition pos=map_areas;pos;++pos)
parser.parse(map_areas[pos]->print());
GP<ByteStream> gstr=ByteStream::create();
ByteStream &str=*gstr;
parser.print(str, 1);
GUTF8String ans;
int size = str.size();
str.seek(0);
str.read(ans.getbuf(size), size);
return ans;
}
bool
DjVuANT::is_empty(void) const
{
GUTF8String raw=encode_raw();
for(int i=raw.length()-1;i>=0;i--)
if (isspace(raw[i])) raw.setat(i, 0);
else break;
return raw.length()==0;
}
GP<DjVuANT>
DjVuANT::copy(void) const
{
GP<DjVuANT> ant=new DjVuANT(*this);
// Now process the list of hyperlinks.
ant->map_areas.empty();
for(GPosition pos=map_areas;pos;++pos)
ant->map_areas.append(map_areas[pos]->get_copy());
return ant;
}
//***************************************************************************
//******************************** DjVuAnno *********************************
//***************************************************************************
GUTF8String
DjVuAnno::get_xmlmap(const GUTF8String &name,const int height) const
{
return ant
?(ant->get_xmlmap(name,height))
:("<MAP name=\""+name.toEscaped()+"\"/>\n");
}
void
DjVuAnno::writeMap(ByteStream &str_out,const GUTF8String &name,const int height) const
{
if(ant)
{
ant->writeMap(str_out,name,height);
}else
{
str_out.writestring(get_xmlmap(name,height));
}
}
GUTF8String
DjVuAnno::get_paramtags(void) const
{
return ant
?(ant->get_paramtags())
:GUTF8String();
}
void
DjVuAnno::writeParam(ByteStream &str_out) const
{
str_out.writestring(get_paramtags());
}
void
DjVuAnno::decode(const GP<ByteStream> &gbs)
{
GUTF8String chkid;
GP<IFFByteStream> giff=IFFByteStream::create(gbs);
IFFByteStream &iff=*giff;
while( iff.get_chunk(chkid) )
{
if (chkid == "ANTa")
{
if (ant) {
ant->merge(*iff.get_bytestream());
} else {
ant=DjVuANT::create();
ant->decode(*iff.get_bytestream());
}
}
else if (chkid == "ANTz")
{
GP<ByteStream> gbsiff=BSByteStream::create(giff->get_bytestream());
if (ant) {
ant->merge(*gbsiff);
} else {
ant=DjVuANT::create();
ant->decode(*gbsiff);
}
}
// Add decoding of other chunks here
iff.close_chunk();
}
}
void
DjVuAnno::encode(const GP<ByteStream> &gbs)
{
GP<IFFByteStream> giff=IFFByteStream::create(gbs);
IFFByteStream &iff=*giff;
if (ant)
{
#if 0
iff.put_chunk("ANTa");
ant->encode(iff);
iff.close_chunk();
#else
iff.put_chunk("ANTz");
{
// GP<ByteStream> bsbinput = giff.get_bytestream();
GP<ByteStream> bsb = BSByteStream::create(giff->get_bytestream(), 50);
ant->encode(*bsb);
}
iff.close_chunk();
#endif
}
// Add encoding of other chunks here
}
GP<DjVuAnno>
DjVuAnno::copy(void) const
{
GP<DjVuAnno> anno= new DjVuAnno;
// Copy any primitives (if any)
*anno=*this;
// Copy each substructure
if (ant) anno->ant = ant->copy();
return anno;
}
void
DjVuAnno::merge(const GP<DjVuAnno> & anno)
{
if (anno)
{
GP<ByteStream> gstr=ByteStream::create();
encode(gstr);
anno->encode(gstr);
gstr->seek(0);
decode(gstr);
}
}
#ifdef HAVE_NAMESPACES
}
# ifndef NOT_USING_DJVU_NAMESPACE
using namespace DJVU;
# endif
#endif