summaryrefslogtreecommitdiffstats
path: root/kviewshell/plugins/djvu/libdjvu/DjVuAnno.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kviewshell/plugins/djvu/libdjvu/DjVuAnno.cpp')
-rw-r--r--kviewshell/plugins/djvu/libdjvu/DjVuAnno.cpp1514
1 files changed, 1514 insertions, 0 deletions
diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuAnno.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuAnno.cpp
new file mode 100644
index 00000000..a6772e61
--- /dev/null
+++ b/kviewshell/plugins/djvu/libdjvu/DjVuAnno.cpp
@@ -0,0 +1,1514 @@
+//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 char *tr1 = "\"\\tnrbf";
+ static 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
+ {
+ static char *tr1 = "tnrbfva";
+ 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