00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 
00036 
00037 
00038 
00039 
00040 
00041 
00042 
00043 
00044 
00045 
00046 
00047 
00048 
00049 
00050 
00051 
00052 
00053 
00054 
00055 
00056 
00057 #ifdef HAVE_CONFIG_H
00058 # include "config.h"
00059 #endif
00060 #if NEED_GNUG_PRAGMAS
00061 # pragma implementation
00062 #endif
00063 
00064 #include "DjVuAnno.h"
00065 #include "GContainer.h"
00066 #include "GException.h"
00067 #include "IFFByteStream.h"
00068 #include "BSByteStream.h"
00069 #include "GMapAreas.h"
00070 
00071 #include "debug.h"
00072 
00073 #include <ctype.h>
00074 
00075 
00076 #ifdef HAVE_NAMESPACES
00077 namespace DJVU {
00078 # ifdef NOT_DEFINED // Just to fool emacs c++ mode
00079 }
00080 #endif
00081 #endif
00082 
00083 
00084 
00085 
00086 
00087 
00088 
00089 
00090 
00091 
00092 
00093 
00094 class GLObject : public GPEnabled
00095 {
00096 public:
00097    enum GLObjectType { INVALID=0, NUMBER=1, STRING=2, SYMBOL=3, LIST=4 };
00098    static const char * const GLObjectString[LIST+1];
00099 
00100    GLObject(int _number=0);
00101    GLObject(GLObjectType type, const char * str);
00102    GLObject(const char * name, const GPList<GLObject> & list);
00103    virtual ~GLObject(void);
00104    
00105    int      get_number(void) const;
00106    GUTF8String  get_string(void) const;
00107    GUTF8String  get_symbol(void) const;
00108    GPList<GLObject> & get_list(void);
00109    GP<GLObject> operator[](int n) const;
00110    
00111    GLObjectType get_type(void) const;
00112    GUTF8String  get_name(void) const;
00113    void     print(ByteStream & str, int compact=1, int indent=0, int * cur_pos=0) const;
00114 private:
00115    GLObjectType type;
00116    GUTF8String  name;
00117    
00118    int      number;
00119    GUTF8String  string;
00120    GUTF8String  symbol;
00121    GPList<GLObject> list;
00122    void throw_can_not_convert_to(const GLObjectType to) const;
00123 };
00124 
00125 const char * const GLObject::GLObjectString[]=
00126   {"invalid", "number", "string", "symbol", "list"};
00127 
00128 inline GLObject::GLObjectType
00129 GLObject::get_type(void) const { return type; }
00130 
00131 inline
00132 GLObject::~GLObject(void) {}
00133 
00134 class GLToken
00135 {
00136 public:
00137    enum GLTokenType { OPEN_PAR, CLOSE_PAR, OBJECT };
00138    GLTokenType  type;
00139    GP<GLObject> object;
00140    
00141    GLToken(GLTokenType type, const GP<GLObject> & object);
00142 };
00143 
00144 inline
00145 GLToken::GLToken(GLTokenType xtype, const GP<GLObject> & xobject) :
00146       type(xtype), object(xobject) {}
00147 
00148 class GLParser
00149 {
00150 public:
00151    void     parse(const char * str);
00152    GPList<GLObject> & get_list(void);
00153    GP<GLObject>     get_object(const char * name, bool last=true);
00154    void     print(ByteStream & str, int compact=1);
00155 
00156    GLParser(void);
00157    GLParser(const char * str);
00158    ~GLParser(void);
00159 private:
00160    GPList<GLObject> list;
00161 
00162    bool     compat;
00163    void     skip_white_space(const char * & start);
00164    void     check_compat(const char *str);
00165    GLToken  get_token(const char * & start);
00166    void     parse(const char * cur_name, GPList<GLObject> & list,
00167               const char * & start);
00168 };
00169 
00170 GLParser::GLParser(void) 
00171   : compat(false)
00172 {
00173 }
00174 
00175 GLParser::~GLParser(void) 
00176 {
00177 }
00178 
00179 GPList<GLObject> &
00180 GLParser::get_list(void) 
00181 { 
00182   return list; 
00183 }
00184 
00185 GLParser::GLParser(const char * str) 
00186   : compat(false)
00187 {
00188   parse(str); 
00189 }
00190 
00191 
00192 
00193 
00194 
00195 
00196 
00197 GLObject::GLObject(int xnumber) : type(NUMBER), number(xnumber) {}
00198 
00199 GLObject::GLObject(GLObjectType xtype, const char * str) : type(xtype)
00200 {
00201    if (type!=STRING && type!=SYMBOL)
00202       G_THROW( ERR_MSG("DjVuAnno.bad_type") );
00203    if (type==STRING) 
00204       string=str;
00205    else symbol=str;
00206 }
00207 
00208 GLObject::GLObject(const char * xname, const GPList<GLObject> & xlist) :
00209       type(LIST), name(xname), list(xlist) {}
00210 
00211 void
00212 GLObject::print(ByteStream & str, int compact, int indent, int * cur_pos) const
00213 {
00214   int local_cur_pos = 0;
00215   if (!cur_pos) { cur_pos = &local_cur_pos; }
00216   
00217   GUTF8String buffer;
00218   const char * to_print=0;
00219   switch(type)
00220   {
00221   case NUMBER:
00222     to_print=buffer.format("%d",number);
00223     break;
00224   case STRING:
00225     {
00226        int length = string.length();
00227        const char *data = (const char*)string;
00228        buffer = GUTF8String("\"");
00229        while (*data && length>0) 
00230          {
00231            int span = 0;
00232            while (span<length && (unsigned char)(data[span])>=0x20 && 
00233                   data[span]!=0x7f && data[span]!='"' && data[span]!='\\' )
00234              span++;
00235            if (span > 0) 
00236              {  
00237                buffer = buffer + GUTF8String(data, span);
00238                data += span;
00239                length -= span;
00240              }  
00241            else 
00242              {
00243                char buf[8];
00244                static char *tr1 = "\"\\tnrbf";
00245                static char *tr2 = "\"\\\t\n\r\b\f";
00246                sprintf(buf,"\\%03o", (int)(((unsigned char*)data)[span]));
00247                for (int i=0; tr2[i]; i++)
00248                  if (data[span] == tr2[i])
00249                    buf[1] = tr1[i];
00250                if (buf[1]<'0' || buf[1]>'3')
00251                  buf[2] = 0;
00252                buffer = buffer + GUTF8String(buf);
00253                data += 1;
00254                length -= 1;
00255              }
00256          }
00257        buffer = buffer + GUTF8String("\"");
00258        to_print = buffer;
00259     }
00260     break;
00261   case SYMBOL:
00262     to_print=buffer.format("%s",(const char *)symbol);
00263     break;
00264   case LIST:
00265     to_print=buffer.format("(%s",(const char *)name);
00266     break;
00267   case INVALID:
00268     break;
00269   }
00270   if (!compact && *cur_pos+strlen(to_print)>70)
00271   {
00272     char ch='\n';
00273     str.write(&ch, 1);
00274     ch=' ';
00275     for(int i=0;i<indent;i++) str.write(&ch, 1);
00276     *cur_pos=indent;
00277   }
00278   str.write(to_print, strlen(to_print));
00279   char ch=' ';
00280   str.write(&ch, 1);
00281   *cur_pos+=strlen(to_print)+1;
00282   if (type==LIST)
00283   {
00284     int indent=*cur_pos-strlen(to_print);
00285     for(GPosition pos=list;pos;++pos)
00286       list[pos]->print(str, compact, indent, cur_pos);
00287     str.write(") ", 2);
00288     *cur_pos+=2;
00289   }
00290 }
00291 
00292 
00293 
00294 
00295 
00296 
00297 
00298 
00299 
00300 
00301 
00302 
00303 
00304 
00305 void
00306 GLObject::throw_can_not_convert_to(const GLObjectType to) const
00307 {
00308   static const GUTF8String two('2');
00309   static const GUTF8String tab('\t');
00310   GUTF8String mesg("DjVuAnno.");
00311   switch(type)
00312   {
00313     case NUMBER:
00314       mesg+=GLObjectString[NUMBER]+two+GLObjectString[to]+tab+GUTF8String(number);
00315       break;
00316     case STRING:
00317       mesg+=GLObjectString[STRING]+two+GLObjectString[to]+tab+string;
00318       break;
00319     case SYMBOL:
00320       mesg+=GLObjectString[SYMBOL]+two+GLObjectString[to]+tab+symbol;
00321       break;
00322     case LIST:
00323       mesg+=GLObjectString[LIST]+two+GLObjectString[to]+tab+name;
00324       break;
00325     default:
00326       mesg+=GLObjectString[INVALID]+two+GLObjectString[to];
00327       break;
00328   }
00329   G_THROW(mesg);
00330 }
00331 
00332 GUTF8String
00333 GLObject::get_string(void) const
00334 {
00335    if (type!=STRING)
00336    {
00337       throw_can_not_convert_to(STRING);
00338    }
00339    return string;
00340 }
00341 
00342 GUTF8String
00343 GLObject::get_symbol(void) const
00344 {
00345    if (type!=SYMBOL)
00346    {
00347       throw_can_not_convert_to(SYMBOL);
00348    }
00349    return symbol;
00350 }
00351 
00352 int
00353 GLObject::get_number(void) const
00354 {
00355    if (type!=NUMBER)
00356    {
00357       throw_can_not_convert_to(NUMBER);
00358    }
00359    return number;
00360 }
00361 
00362 GUTF8String
00363 GLObject::get_name(void) const
00364 {
00365    if (type!=LIST)
00366    {
00367       throw_can_not_convert_to(LIST);
00368    }
00369    return name;
00370 }
00371 
00372 GP<GLObject>
00373 GLObject::operator[](int n) const
00374 {
00375    if (type!=LIST)
00376    {
00377       throw_can_not_convert_to(LIST);
00378    }
00379    if (n>=list.size()) G_THROW( ERR_MSG("DjVuAnno.too_few") "\t"+name);
00380    int i;
00381    GPosition pos;
00382    for(i=0, pos=list;i<n && pos;i++, ++pos)
00383         continue;
00384    return list[pos];
00385 }
00386 
00387 GPList<GLObject> &
00388 GLObject::get_list(void)
00389 {
00390    if (type!=LIST)
00391    {
00392       throw_can_not_convert_to(LIST);
00393    }
00394    return list;
00395 }
00396 
00397 
00398 
00399 void
00400 GLParser::skip_white_space(const char * & start)
00401 {
00402    while(*start && isspace(*start)) start++;
00403    if (!*start) 
00404        G_THROW( ByteStream::EndOfFile );
00405 }
00406 
00407 GLToken
00408 GLParser::get_token(const char * & start)
00409 {
00410    skip_white_space(start);
00411    char c = *start;
00412    if (c == '(')
00413      {
00414        start++;
00415        return GLToken(GLToken::OPEN_PAR, 0);
00416      }
00417    else if (c == ')')
00418      {
00419        start++;
00420        return GLToken(GLToken::CLOSE_PAR, 0);
00421      }
00422    else if (c=='-' || (c>='0' && c<='9'))
00423      {
00424        return GLToken(GLToken::OBJECT,
00425                       new GLObject(strtol(start, (char **) &start, 10)));
00426      }
00427    else if (c=='"')
00428      {
00429        GUTF8String str;
00430        start++;
00431        while(1)
00432      {
00433            int span = 0;
00434            while (start[span] && start[span]!='\\' && start[span]!='\"')
00435              span++;
00436            if (span > 0)
00437              {
00438                str = str + GUTF8String(start,span);
00439                start += span;
00440              }
00441            else if (start[0]=='\"')
00442              {
00443                start += 1;
00444                break;
00445              }
00446            else if (start[0]=='\\' && compat)
00447              {
00448                char c = start[1];
00449                if (c == '\"')
00450                  {
00451                    start += 2;
00452                    str += '\"';
00453                  }
00454                else
00455                  {
00456                    start += 1;
00457                    str += '\\';
00458                  }
00459              }
00460            else if (start[0]=='\\' && start[1])
00461              {
00462                char c = *++start;
00463                if (c>='0' && c<='7')
00464                  {
00465                    int x = 0;
00466                    for (int i=0; i<3 && c>='0' && c<='7'; i++) 
00467                      {
00468                        x = x * 8 + c - '0';
00469                        c = *++start;
00470                      }
00471                    str += (char)(x & 0xff);
00472                  }
00473                else
00474                  {
00475                    static char *tr1 = "tnrbfva";
00476                    static char *tr2 = "\t\n\r\b\f\013\007";
00477                    for (int i=0; tr1[i]; i++)
00478                      if (c == tr1[i])
00479                        c = tr2[i];
00480                    start += 1;
00481                    str += c;
00482                  }
00483              }
00484            else 
00485              {
00486                G_THROW( ByteStream::EndOfFile );
00487              }
00488          }
00489        return GLToken(GLToken::OBJECT, 
00490                       new GLObject(GLObject::STRING, str));
00491      }
00492    else
00493      {
00494        GUTF8String str;
00495        while(1)
00496      {
00497            char ch=*start++;
00498            if (!ch)
00499              G_THROW( ByteStream::EndOfFile );
00500            if (ch==')') { start--; break; }
00501            if (isspace(ch)) break;
00502            str+=ch;
00503      }
00504        return GLToken(GLToken::OBJECT, new GLObject(GLObject::SYMBOL, str));
00505      }
00506 } 
00507 
00508 void
00509 GLParser::parse(const char * cur_name, GPList<GLObject> & list,
00510         const char * & start)
00511 {
00512   DEBUG_MSG("GLParse::parse(): Parsing contents of object '" << cur_name << "'\n");
00513   DEBUG_MAKE_INDENT(3);
00514   
00515   while(1)
00516   {
00517     GLToken token=get_token(start);
00518     if (token.type==GLToken::OPEN_PAR)
00519     {
00520       if (isspace(*start))
00521       {
00522         GUTF8String mesg=GUTF8String( ERR_MSG("DjVuAnno.paren") "\t")+cur_name;
00523         G_THROW(mesg);
00524       }
00525       
00526       GLToken tok=get_token(start);
00527       GP<GLObject> object=tok.object;   
00528       
00529       if (tok.type!=GLToken::OBJECT || object->get_type()!=GLObject::SYMBOL)
00530       {
00531         if (tok.type==GLToken::OPEN_PAR ||
00532           tok.type==GLToken::CLOSE_PAR)
00533         {
00534           GUTF8String mesg=GUTF8String( ERR_MSG("DjVuAnno.no_paren") "\t")+cur_name;
00535           G_THROW(mesg);
00536         }
00537         if (tok.type==GLToken::OBJECT)
00538         {
00539           GLObject::GLObjectType type=object->get_type();
00540           if (type==GLObject::NUMBER)
00541           {
00542             GUTF8String mesg( ERR_MSG("DjVuAnno.no_number") "\t");
00543             mesg += cur_name;
00544             G_THROW(mesg);
00545           }
00546           else if (type==GLObject::STRING)
00547           {
00548             GUTF8String mesg( ERR_MSG("DjVuAnno.no_string") "\t");
00549             mesg += cur_name;
00550             G_THROW(mesg);
00551           }
00552         }
00553       }
00554       
00555       
00556       GPList<GLObject> new_list;
00557       G_TRY
00558       {
00559         parse(object->get_symbol(), new_list, start);
00560       } 
00561       G_CATCH(exc)
00562       {
00563         if (exc.cmp_cause(ByteStream::EndOfFile))
00564           G_RETHROW;
00565       } 
00566       G_ENDCATCH;
00567       list.append(new GLObject(object->get_symbol(), new_list));
00568       continue;
00569     }
00570     if (token.type==GLToken::CLOSE_PAR) 
00571       return;
00572     list.append(token.object);
00573   }
00574 }
00575 
00576 void 
00577 GLParser::check_compat(const char *s)
00578 {
00579   int state = 0;
00580   while (s && *s && !compat)
00581     {
00582       switch(state)
00583         {
00584         case 0:
00585           if (*s == '\"')
00586             state = '\"';
00587           break;
00588         case '\"':
00589           if (*s == '\"')
00590             state = 0;
00591           else if (*s == '\\')
00592             state = '\\';
00593           else if ((unsigned char)(*s)<0x20 || *s==0x7f)
00594             compat = true;
00595           break;
00596         case '\\':
00597           if (!strchr("01234567tnrbfva\"\\",*s))
00598             compat = true;
00599           state = '\"';
00600           break;
00601         }
00602       s += 1;
00603     }
00604 }
00605 
00606 void
00607 GLParser::parse(const char * str)
00608 {
00609    DEBUG_MSG("GLParser::parse(): parsing string contents\n");
00610    DEBUG_MAKE_INDENT(3);
00611    
00612    G_TRY
00613    {
00614       check_compat(str);
00615       parse("toplevel", list, str);
00616    } G_CATCH(exc)
00617    {
00618       if (exc.cmp_cause(ByteStream::EndOfFile))
00619         G_RETHROW;
00620    } G_ENDCATCH;
00621 }
00622 
00623 void
00624 GLParser::print(ByteStream & str, int compact)
00625 {
00626    for(GPosition pos=list;pos;++pos)
00627       list[pos]->print(str, compact);
00628 }
00629 
00630 GP<GLObject>
00631 GLParser::get_object(const char * name, bool last)
00632 {
00633    GP<GLObject> object;
00634    for(GPosition pos=list;pos;++pos)
00635    {
00636       GP<GLObject> obj=list[pos];
00637       if (obj->get_type()==GLObject::LIST &&
00638       obj->get_name()==name)
00639       {
00640      object=obj;
00641      if (!last) break;
00642       }
00643    }
00644    return object;
00645 }
00646 
00647 
00648 
00649 
00650 
00651 static const char *zoom_strings[]={
00652   "default","page","width","one2one","stretch"};
00653 static const int zoom_strings_size=sizeof(zoom_strings)/sizeof(const char *);
00654 
00655 static const char *mode_strings[]={
00656   "default","color","fore","back","bw"};
00657 static const int mode_strings_size=sizeof(mode_strings)/sizeof(const char *);
00658 
00659 static const char *align_strings[]={
00660   "default","left","center","right","top","bottom"};
00661 static const int align_strings_size=sizeof(align_strings)/sizeof(const char *);
00662 
00663 #define PNOTE_TAG   "pnote"
00664 #define BACKGROUND_TAG  "background"
00665 #define ZOOM_TAG    "zoom"
00666 #define MODE_TAG    "mode"
00667 #define ALIGN_TAG   "align"
00668 #define HALIGN_TAG  "halign"
00669 #define VALIGN_TAG  "valign"
00670 #define METADATA_TAG    "metadata"
00671 
00672 static const unsigned long default_bg_color=0xffffffff;
00673 
00674 DjVuANT::DjVuANT(void)
00675 {
00676    bg_color=default_bg_color;
00677    zoom=0;
00678    mode=MODE_UNSPEC;
00679    hor_align=ver_align=ALIGN_UNSPEC;
00680 }
00681 
00682 DjVuANT::~DjVuANT()
00683 {
00684 }
00685 
00686 GUTF8String
00687 DjVuANT::get_paramtags(void) const
00688 {
00689   GUTF8String retval;
00690   if(zoom > 0)
00691   {
00692     retval+="<PARAM name=\"" ZOOM_TAG "\" value=\""+GUTF8String(zoom)+="\" />\n";
00693   }else if(zoom && ((-zoom)<zoom_strings_size))
00694   {
00695     retval+="<PARAM name=\"" ZOOM_TAG "\" value=\""+GUTF8String(zoom_strings[-zoom])+"\" />\n";
00696   }
00697   if((mode>0)&&(mode<mode_strings_size))
00698   {
00699     retval+="<PARAM name=\"" MODE_TAG "\" value=\""+GUTF8String(mode_strings[mode])+"\" />\n";
00700   }
00701   if((hor_align>ALIGN_UNSPEC)&&(hor_align<align_strings_size))
00702   {
00703     retval+="<PARAM name=\"" HALIGN_TAG "\" value=\""+GUTF8String(align_strings[hor_align])+"\" />\n";
00704   }
00705   if((ver_align>ALIGN_UNSPEC)&&(ver_align<align_strings_size))
00706   {
00707     retval+="<PARAM name=\"" VALIGN_TAG "\" value=\""+GUTF8String(align_strings[ver_align])+"\" />\n";
00708   }
00709   if((bg_color&0xffffff) == bg_color)
00710   {
00711     retval+="<PARAM name=\"" BACKGROUND_TAG "\" value=\""+GUTF8String().format("#%06lX",bg_color)+"\" />\n";
00712   }
00713   return retval;
00714 }
00715 
00716 void
00717 DjVuANT::writeParam(ByteStream &str_out) const
00718 {
00719   str_out.writestring(get_paramtags());
00720 }
00721 
00722 GUTF8String
00723 DjVuANT::get_xmlmap(const GUTF8String &name,const int height) const
00724 {
00725   GUTF8String retval("<MAP name=\""+name.toEscaped()+"\" >\n");
00726   for(GPosition pos(map_areas);pos;++pos)
00727   {
00728     retval+=map_areas[pos]->get_xmltag(height);
00729   }
00730   return retval+"</MAP>\n";
00731 }
00732 
00733 void
00734 DjVuANT::writeMap(
00735   ByteStream &str_out,const GUTF8String &name,const int height) const
00736 {
00737   str_out.writestring("<MAP name=\""+name.toEscaped()+"\" >\n");
00738   for(GPosition pos(map_areas);pos;++pos)
00739   {
00740     str_out.writestring(GUTF8String(map_areas[pos]->get_xmltag(height)));
00741   }
00742   str_out.writestring(GUTF8String("</MAP>\n"));
00743 }
00744 
00745 GUTF8String
00746 DjVuANT::read_raw(ByteStream & str)
00747 {
00748    GUTF8String raw;
00749    char buffer[1024];
00750    int length;
00751    while((length=str.read(buffer, 1024)))
00752       raw+=GUTF8String(buffer, length);
00753    return raw;
00754 }
00755 
00756 void
00757 DjVuANT::decode(class GLParser & parser)
00758 {
00759    bg_color=get_bg_color(parser);
00760    zoom=get_zoom(parser);
00761    mode=get_mode(parser);
00762    hor_align=get_hor_align(parser);
00763    ver_align=get_ver_align(parser);
00764    map_areas=get_map_areas(parser);
00765 #ifndef NO_METADATA_IN_ANT_CHUNK
00766    metadata=get_metadata(parser); 
00767 #endif
00768 }
00769 
00770 
00771 void 
00772 DjVuANT::decode(ByteStream & str)
00773 {
00774    GLParser parser(read_raw(str));
00775    decode(parser);
00776 }
00777 
00778 void
00779 DjVuANT::merge(ByteStream & str)
00780 {
00781    GLParser parser(encode_raw());
00782    GUTF8String add_raw=read_raw(str);
00783    parser.parse(add_raw);
00784    decode(parser);
00785 }
00786 
00787 void
00788 DjVuANT::encode(ByteStream &bs)
00789 {
00790   GUTF8String raw=encode_raw();
00791   bs.writall((const char*) raw, raw.length());
00792 }
00793 
00794 unsigned int 
00795 DjVuANT::get_memory_usage() const
00796 {
00797   return sizeof(DjVuANT);
00798 }
00799 
00800 unsigned char
00801 DjVuANT::decode_comp(char ch1, char ch2)
00802 {
00803    unsigned char dig1=0;
00804    if (ch1)
00805    {
00806       ch1=toupper(ch1);
00807       if (ch1>='0' && ch1<='9') dig1=ch1-'0';
00808       if (ch1>='A' && ch1<='F') dig1=10+ch1-'A';
00809       
00810       unsigned char dig2=0;
00811       if (ch2)
00812       {
00813      ch2=toupper(ch2);
00814      if (ch2>='0' && ch2<='9') dig2=ch2-'0';
00815      if (ch2>='A' && ch2<='F') dig2=10+ch2-'A';
00816      return (dig1 << 4) | dig2;
00817       }
00818       return dig1;
00819    }
00820    return 0;
00821 }
00822 
00823 unsigned long int
00824 DjVuANT::cvt_color(const char * color, unsigned long int def)
00825 {
00826    if (color[0]!='#') return def;
00827 
00828    unsigned long int color_rgb=0;
00829    color++;
00830    const char * start, * end;
00831    
00832       
00833    end=color+strlen(color); start=end-2;
00834    if (start<color) start=color;
00835    if (end>start)
00836       color_rgb|=decode_comp(start[0], start+1<end ? start[1] : 0);
00837    
00838       
00839    end=color+strlen(color)-2; start=end-2;
00840    if (start<color) start=color;
00841    if (end>start)
00842       color_rgb|=decode_comp(start[0], start+1<end ? start[1] : 0) << 8;
00843    
00844       
00845    end=color+strlen(color)-4; start=end-2;
00846    if (start<color) start=color;
00847    if (end>start)
00848       color_rgb|=decode_comp(start[0], start+1<end ? start[1] : 0) << 16;
00849 
00850       
00851    end=color+strlen(color)-6; start=end-2;
00852    if (start<color) start=color;
00853    if (end>start)
00854       color_rgb|=decode_comp(start[0], start+1<end ? start[1] : 0) << 24;
00855    
00856    return color_rgb;
00857 }
00858 
00859 unsigned long int
00860 DjVuANT::get_bg_color(GLParser & parser)
00861 {
00862   unsigned long retval=default_bg_color;
00863   DEBUG_MSG("DjVuANT::get_bg_color(): getting background color ...\n");
00864   DEBUG_MAKE_INDENT(3);
00865   G_TRY
00866   {
00867     GP<GLObject> obj=parser.get_object(BACKGROUND_TAG);
00868     if (obj && obj->get_list().size()==1)
00869     {
00870       GUTF8String color=(*obj)[0]->get_symbol();
00871       DEBUG_MSG("color='" << color << "'\n");
00872       retval=cvt_color(color, 0xffffff);
00873     }
00874 #ifndef NDEBUG
00875     if(retval == default_bg_color)
00876     {
00877       DEBUG_MSG("can't find any.\n");
00878     }
00879 #endif // NDEBUG
00880   } G_CATCH_ALL {} G_ENDCATCH;
00881 #ifndef NDEBUG
00882   if(retval == default_bg_color)
00883   {
00884     DEBUG_MSG("resetting color to 0xffffffff (UNSPEC)\n");
00885   }
00886 #endif // NDEBUG
00887   return retval;
00888 }
00889 
00890 int
00891 DjVuANT::get_zoom(GLParser & parser)
00892       
00893       
00894       
00895       
00896 {
00897   int retval=ZOOM_UNSPEC;
00898   DEBUG_MSG("DjVuANT::get_zoom(): getting zoom factor ...\n");
00899   DEBUG_MAKE_INDENT(3);
00900   G_TRY
00901   {
00902     GP<GLObject> obj=parser.get_object(ZOOM_TAG);
00903     if (obj && obj->get_list().size()==1)
00904     {
00905       const GUTF8String zoom((*obj)[0]->get_symbol());
00906       DEBUG_MSG("zoom='" << zoom << "'\n");
00907      
00908       for(int i=0;(i<zoom_strings_size);++i)
00909       {
00910         if(zoom == zoom_strings[i])
00911         {
00912           retval=(-i);
00913           break;
00914         }
00915       }
00916       if(retval == ZOOM_UNSPEC)
00917       {
00918         if (zoom[0]!='d')
00919         {
00920           G_THROW( ERR_MSG("DjVuAnno.bad_zoom") );
00921         }else
00922         {
00923           retval=zoom.substr(1, zoom.length()).toInt(); 
00924         }
00925       }
00926     }
00927 #ifndef NDEBUG
00928     if(retval == ZOOM_UNSPEC)
00929     {
00930       DEBUG_MSG("can't find any.\n");
00931     }
00932 #endif // NDEBUG
00933   } G_CATCH_ALL {} G_ENDCATCH;
00934 #ifndef NDEBUG
00935   if(retval == ZOOM_UNSPEC)
00936   {
00937     DEBUG_MSG("resetting zoom to 0 (UNSPEC)\n");
00938   }
00939 #endif // NDEBUG
00940   return retval;
00941 }
00942 
00943 int
00944 DjVuANT::get_mode(GLParser & parser)
00945 {
00946   int retval=MODE_UNSPEC;
00947   DEBUG_MSG("DjVuAnt::get_mode(): getting default mode ...\n");
00948   DEBUG_MAKE_INDENT(3);
00949   G_TRY
00950   {
00951     GP<GLObject> obj=parser.get_object(MODE_TAG);
00952     if (obj && obj->get_list().size()==1)
00953     {
00954       const GUTF8String mode((*obj)[0]->get_symbol());
00955       DEBUG_MSG("mode='" << mode << "'\n");
00956       for(int i=0;(i<mode_strings_size);++i)
00957       {
00958         if(mode == mode_strings[i])
00959         {
00960           retval=i;
00961           break;
00962         }
00963       }
00964     }
00965 #ifndef NDEBUG
00966     if(retval == MODE_UNSPEC)
00967     {
00968       DEBUG_MSG("can't find any.\n");
00969     }
00970 #endif // NDEBUG
00971   } G_CATCH_ALL {} G_ENDCATCH;
00972 #ifndef NDEBUG
00973   if(retval == MODE_UNSPEC)
00974   {
00975     DEBUG_MSG("resetting mode to MODE_UNSPEC\n");
00976   }
00977 #endif // NDEBUG
00978   return retval;
00979 }
00980 
00981 static inline DjVuANT::alignment
00982 legal_halign(const int i)
00983 {
00984   DjVuANT::alignment retval;
00985   switch((DjVuANT::alignment)i)
00986   {
00987   case DjVuANT::ALIGN_LEFT:
00988   case DjVuANT::ALIGN_CENTER:
00989   case DjVuANT::ALIGN_RIGHT:
00990     retval=(DjVuANT::alignment)i;
00991     break;
00992   default:
00993     retval=DjVuANT::ALIGN_UNSPEC;
00994     break;
00995   }
00996   return retval;
00997 }
00998 
00999 static inline DjVuANT::alignment
01000 legal_valign(const int i)
01001 {
01002   DjVuANT::alignment retval;
01003   switch((DjVuANT::alignment)i)
01004   {
01005   case DjVuANT::ALIGN_CENTER:
01006   case DjVuANT::ALIGN_TOP:
01007   case DjVuANT::ALIGN_BOTTOM:
01008     retval=(DjVuANT::alignment)i;
01009     break;
01010   default:
01011     retval=DjVuANT::ALIGN_UNSPEC;
01012     break;
01013   }
01014   return retval;
01015 }
01016 
01017 DjVuANT::alignment
01018 DjVuANT::get_hor_align(GLParser & parser)
01019 {
01020   DEBUG_MSG("DjVuAnt::get_hor_align(): getting hor page alignemnt ...\n");
01021   DEBUG_MAKE_INDENT(3);
01022   alignment retval=ALIGN_UNSPEC;
01023   G_TRY
01024   {
01025     GP<GLObject> obj=parser.get_object(ALIGN_TAG);
01026     if (obj && obj->get_list().size()==2)
01027     {
01028       const GUTF8String align((*obj)[0]->get_symbol());
01029       DEBUG_MSG("hor_align='" << align << "'\n");
01030       
01031       for(int i=(int)ALIGN_UNSPEC;(i<align_strings_size);++i)
01032       {
01033         const alignment j=legal_halign(i);
01034         if((i == (int)j)&&(align == align_strings[i]))
01035         {
01036           retval=j;
01037           break;
01038         }
01039       }
01040     }
01041 #ifndef NDEBUG
01042     if(retval == ALIGN_UNSPEC)
01043     {
01044       DEBUG_MSG("can't find any.\n");
01045     }
01046 #endif // NDEBUG
01047   } G_CATCH_ALL {} G_ENDCATCH;
01048 #ifndef NDEBUG
01049   if(retval == ALIGN_UNSPEC)
01050   {
01051     DEBUG_MSG("resetting alignment to ALIGN_UNSPEC\n");
01052   }
01053 #endif // NDEBUG
01054   return retval;
01055 }
01056 
01057 DjVuANT::alignment
01058 DjVuANT::get_ver_align(GLParser & parser)
01059 {
01060   DEBUG_MSG("DjVuAnt::get_ver_align(): getting vert page alignemnt ...\n");
01061   DEBUG_MAKE_INDENT(3);
01062   alignment retval=ALIGN_UNSPEC;
01063   G_TRY
01064   {
01065     GP<GLObject> obj=parser.get_object(ALIGN_TAG);
01066     if (obj && obj->get_list().size()==2)
01067     {
01068       const GUTF8String align((*obj)[1]->get_symbol());
01069       DEBUG_MSG("ver_align='" << align << "'\n");
01070       for(int i=(int)ALIGN_UNSPEC;(i<align_strings_size);++i)
01071       {
01072         const alignment j=legal_valign(i);
01073         if((i == (int)j)&&(align == align_strings[i]))
01074         {
01075           retval=j;
01076           break;
01077         }
01078       }
01079     }
01080 #ifndef NDEBUG
01081     if(retval == ALIGN_UNSPEC)
01082     {
01083       DEBUG_MSG("can't find any.\n");
01084     }
01085 #endif // NDEBUG
01086   } G_CATCH_ALL {} G_ENDCATCH;
01087 #ifndef NDEBUG
01088   if(retval == ALIGN_UNSPEC)
01089   {
01090     DEBUG_MSG("resetting alignment to ALIGN_UNSPEC\n");
01091   }
01092 #endif // NDEBUG
01093   return retval;
01094 }
01095 
01096 #ifndef NO_METADATA_IN_ANT_CHUNK
01097 GMap<GUTF8String, GUTF8String>
01098 DjVuANT::get_metadata(GLParser & parser)
01099 {
01100   DEBUG_MSG("DjVuANT::get_map_areas(): forming and returning back list of map areas\n");
01101   DEBUG_MAKE_INDENT(3);
01102   
01103   GMap<GUTF8String, GUTF8String> mdata;
01104   
01105   GPList<GLObject> list=parser.get_list();
01106   for(GPosition pos=list;pos;++pos)
01107     {
01108       GLObject & obj=*list[pos];
01109       if (obj.get_type()==GLObject::LIST && obj.get_name()==METADATA_TAG)  
01110         { 
01111           G_TRY 
01112             {
01113               for(int obj_num=0;obj_num<obj.get_list().size();obj_num++)
01114                 {
01115                   GLObject & el=*obj[obj_num];
01116                   const int type = el.get_type();
01117                   if (type == GLObject::LIST)
01118                     { 
01119                       const GUTF8String & name=el.get_name();  
01120                       mdata[name]=(el[0])->get_string();
01121                     }
01122                 }
01123             } 
01124           G_CATCH_ALL { } G_ENDCATCH;
01125         }
01126     }
01127   return mdata;
01128 }
01129 #endif
01130 
01131 GPList<GMapArea>
01132 DjVuANT::get_map_areas(GLParser & parser)
01133 {
01134   DEBUG_MSG("DjVuANT::get_map_areas(): forming and returning back list of map areas\n");
01135   DEBUG_MAKE_INDENT(3);
01136   
01137   GPList<GMapArea> map_areas;
01138   
01139   GPList<GLObject> list=parser.get_list();
01140 
01141   for(GPosition pos=list;pos;++pos)
01142   {
01143     GLObject & obj=*list[pos];
01144     const int type=obj.get_type();
01145     if (type == GLObject::LIST)
01146     {
01147       const GUTF8String name=obj.get_name();
01148       if(name == GMapArea::MAPAREA_TAG)
01149       {
01150         G_TRY {
01151            
01152           GUTF8String url;
01153           GUTF8String target=GMapArea::TARGET_SELF;
01154           GLObject & url_obj=*(obj[0]);
01155           if (url_obj.get_type()==GLObject::LIST)
01156           {
01157             if (url_obj.get_name()!=GMapArea::URL_TAG)
01158               G_THROW( ERR_MSG("DjVuAnno.bad_url") );
01159             url=(url_obj[0])->get_string();
01160             target=(url_obj[1])->get_string();
01161           } else url=url_obj.get_string();
01162         
01163            
01164           GUTF8String comment=(obj[1])->get_string();
01165         
01166           DEBUG_MSG("found maparea '" << comment << "' (" <<
01167             url << ":" << target << ")\n");
01168         
01169           GLObject * shape=obj[2];
01170           GP<GMapArea> map_area;
01171           if (shape->get_type()==GLObject::LIST)
01172           {
01173             if (shape->get_name()==GMapArea::RECT_TAG)
01174             {
01175               DEBUG_MSG("it's a rectangle.\n");
01176               GRect grect((*shape)[0]->get_number(),
01177                           (*shape)[1]->get_number(),
01178                           (*shape)[2]->get_number(),
01179                           (*shape)[3]->get_number());
01180               GP<GMapRect> map_rect=GMapRect::create(grect);
01181               map_area=(GMapRect *)map_rect;
01182             } else if (shape->get_name()==GMapArea::POLY_TAG)
01183             {
01184               DEBUG_MSG("it's a polygon.\n");
01185               int points=shape->get_list().size()/2;
01186               GTArray<int> xx(points-1), yy(points-1);
01187               for(int i=0;i<points;i++)
01188               {
01189                 xx[i]=(*shape)[2*i]->get_number();
01190                 yy[i]=(*shape)[2*i+1]->get_number();
01191               }
01192               GP<GMapPoly> map_poly=GMapPoly::create(xx,yy,points);
01193               map_area=(GMapPoly *)map_poly;
01194             } else if (shape->get_name()==GMapArea::OVAL_TAG)
01195             {
01196               DEBUG_MSG("it's an ellipse.\n");
01197               GRect grect((*shape)[0]->get_number(),
01198                           (*shape)[1]->get_number(),
01199                           (*shape)[2]->get_number(),
01200                           (*shape)[3]->get_number());
01201               GP<GMapOval> map_oval=GMapOval::create(grect);
01202               map_area=(GMapOval *)map_oval;
01203             }
01204           }
01205         
01206           if (map_area)
01207           {
01208             map_area->url=url;
01209             map_area->target=target;
01210             map_area->comment=comment;
01211             for(int obj_num=3;obj_num<obj.get_list().size();obj_num++)
01212             {
01213               GLObject * el=obj[obj_num];
01214               if (el->get_type()==GLObject::LIST)
01215               {
01216                 const GUTF8String & name=el->get_name();
01217                 if (name==GMapArea::BORDER_AVIS_TAG)
01218                   map_area->border_always_visible=true;
01219                 else if (name==GMapArea::HILITE_TAG)
01220                 {
01221                   GLObject * obj=el->get_list()[el->get_list().firstpos()];
01222                   if (obj->get_type()==GLObject::SYMBOL)
01223                     map_area->hilite_color=cvt_color(obj->get_symbol(), 0xff);
01224                 } else
01225                 {
01226                   int border_type=
01227                     name==GMapArea::NO_BORDER_TAG ? GMapArea::NO_BORDER :
01228                     name==GMapArea::XOR_BORDER_TAG ? GMapArea::XOR_BORDER :
01229                     name==GMapArea::SOLID_BORDER_TAG ? GMapArea::SOLID_BORDER :
01230                     name==GMapArea::SHADOW_IN_BORDER_TAG ? GMapArea::SHADOW_IN_BORDER :
01231                     name==GMapArea::SHADOW_OUT_BORDER_TAG ? GMapArea::SHADOW_OUT_BORDER :
01232                     name==GMapArea::SHADOW_EIN_BORDER_TAG ? GMapArea::SHADOW_EIN_BORDER :
01233                     name==GMapArea::SHADOW_EOUT_BORDER_TAG ? GMapArea::SHADOW_EOUT_BORDER : -1;
01234                   if (border_type>=0)
01235                   {
01236                     map_area->border_type=(GMapArea::BorderType) border_type;
01237                     for(GPosition pos=el->get_list();pos;++pos)
01238                     {
01239                       GLObject * obj=el->get_list()[pos];
01240                       if (obj->get_type()==GLObject::SYMBOL)
01241                         map_area->border_color=cvt_color(obj->get_symbol(), 0xff);
01242                       if (obj->get_type()==GLObject::NUMBER)
01243                         map_area->border_width=obj->get_number();
01244                     }
01245                   }
01246                 }       
01247               } 
01248             } 
01249             map_areas.append(map_area);
01250           } 
01251         } G_CATCH_ALL {} G_ENDCATCH;
01252       }
01253     }
01254   } 
01255    
01256   DEBUG_MSG("map area list size = " << list.size() << "\n");
01257   
01258   return map_areas;
01259 }
01260 
01261 void
01262 DjVuANT::del_all_items(const char * name, GLParser & parser)
01263 {
01264    GPList<GLObject> & list=parser.get_list();
01265    GPosition pos=list;
01266    while(pos)
01267    {
01268       GLObject & obj=*list[pos];
01269       if (obj.get_type()==GLObject::LIST &&
01270       obj.get_name()==name)
01271       {
01272      GPosition this_pos=pos;
01273      ++pos;
01274      list.del(this_pos);
01275       } else ++pos;
01276    }
01277 }
01278 
01279 GUTF8String
01280 DjVuANT::encode_raw(void) const
01281 {
01282    GUTF8String buffer;
01283    GLParser parser;
01284 
01285       
01286    del_all_items(BACKGROUND_TAG, parser);
01287    if (bg_color!=default_bg_color)
01288    {
01289       buffer.format("(" BACKGROUND_TAG " #%02X%02X%02X)",
01290           (unsigned int)((bg_color & 0xff0000) >> 16),
01291           (unsigned int)((bg_color & 0xff00) >> 8),
01292           (unsigned int)(bg_color & 0xff));
01293       parser.parse(buffer);
01294    }
01295 
01296       
01297    del_all_items(ZOOM_TAG, parser);
01298    if (zoom!=ZOOM_UNSPEC)
01299    {
01300       buffer="(" ZOOM_TAG " ";
01301       const int i=1-zoom;
01302       if((i>=0)&& (i<zoom_strings_size))
01303       {
01304         buffer+=zoom_strings[i];
01305       }else
01306       {
01307         buffer+="d"+GUTF8String(zoom);
01308       }
01309       buffer+=")";
01310       parser.parse(buffer);
01311    }
01312 
01313       
01314    del_all_items(MODE_TAG, parser);
01315    if (mode!=MODE_UNSPEC)
01316    {
01317       const int i=mode-1;
01318       if((i>=0)&& (i<mode_strings_size))
01319       { 
01320         buffer="(" MODE_TAG " " + GUTF8String(mode_strings[mode]) + ")";
01321       }
01322       parser.parse(buffer);
01323    }
01324 
01325       
01326    del_all_items(ALIGN_TAG, parser);
01327    if (hor_align!=ALIGN_UNSPEC || ver_align!=ALIGN_UNSPEC)
01328    {
01329       buffer= GUTF8String("(" ALIGN_TAG " ")
01330         +align_strings[((hor_align<ALIGN_UNSPEC)||
01331                         (hor_align>=align_strings_size))?ALIGN_UNSPEC:hor_align]
01332         +" "+align_strings[((ver_align<ALIGN_UNSPEC)||
01333                             (ver_align>=align_strings_size))?ALIGN_UNSPEC:ver_align]+")";
01334       parser.parse(buffer);
01335    }
01336       
01337 #ifndef NO_METADATA_IN_ANT_CHUNK
01338    del_all_items(METADATA_TAG, parser);
01339    if (!metadata.isempty())
01340      {
01341        GUTF8String mdatabuffer("(");
01342        mdatabuffer +=  METADATA_TAG ;
01343        for (GPosition pos=metadata; pos; ++pos)
01344          mdatabuffer +=" (" + metadata.key(pos)+" \""+metadata[pos]+"\")";
01345        mdatabuffer += " )";
01346        parser.parse(mdatabuffer);
01347      }
01348 #endif   
01349      
01350    del_all_items(GMapArea::MAPAREA_TAG, parser);
01351    for(GPosition pos=map_areas;pos;++pos)
01352       parser.parse(map_areas[pos]->print());
01353 
01354    GP<ByteStream> gstr=ByteStream::create();
01355    ByteStream &str=*gstr;
01356    parser.print(str, 1);
01357    GUTF8String ans;
01358    int size = str.size();
01359    str.seek(0);
01360    str.read(ans.getbuf(size), size);
01361    return ans;
01362 }
01363 
01364 bool
01365 DjVuANT::is_empty(void) const
01366 {
01367    GUTF8String raw=encode_raw();
01368    for(int i=raw.length()-1;i>=0;i--)
01369       if (isspace(raw[i])) raw.setat(i, 0);
01370       else break;
01371    return raw.length()==0;
01372 }
01373 
01374 GP<DjVuANT>
01375 DjVuANT::copy(void) const
01376 {
01377    GP<DjVuANT> ant=new DjVuANT(*this);
01378 
01379 
01380       
01381    ant->map_areas.empty();
01382    for(GPosition pos=map_areas;pos;++pos)
01383       ant->map_areas.append(map_areas[pos]->get_copy());
01384 
01385    return ant;
01386 }
01387 
01388 
01389 
01390 
01391 
01392 GUTF8String
01393 DjVuAnno::get_xmlmap(const GUTF8String &name,const int height) const
01394 {
01395   return ant
01396     ?(ant->get_xmlmap(name,height))
01397     :("<MAP name=\""+name.toEscaped()+"\"/>\n");
01398 }
01399 
01400 void
01401 DjVuAnno::writeMap(ByteStream &str_out,const GUTF8String &name,const int height) const
01402 {
01403   if(ant)
01404   {
01405     ant->writeMap(str_out,name,height);
01406   }else
01407   {
01408     str_out.writestring(get_xmlmap(name,height));
01409   }
01410 }
01411 
01412 GUTF8String
01413 DjVuAnno::get_paramtags(void) const
01414 {
01415   return ant
01416     ?(ant->get_paramtags())
01417     :GUTF8String();
01418 }
01419 
01420 void
01421 DjVuAnno::writeParam(ByteStream &str_out) const
01422 {
01423   str_out.writestring(get_paramtags());
01424 }
01425 
01426 
01427 void
01428 DjVuAnno::decode(const GP<ByteStream> &gbs)
01429 {
01430   GUTF8String chkid;
01431   GP<IFFByteStream> giff=IFFByteStream::create(gbs);
01432   IFFByteStream &iff=*giff;
01433   while( iff.get_chunk(chkid) )
01434   {
01435     if (chkid == "ANTa")
01436     {
01437       if (ant) {
01438         ant->merge(*iff.get_bytestream());
01439       } else {
01440         ant=DjVuANT::create();
01441         ant->decode(*iff.get_bytestream());
01442       }
01443     }
01444     else if (chkid == "ANTz")
01445     {
01446       GP<ByteStream> gbsiff=BSByteStream::create(giff->get_bytestream());
01447       if (ant) {
01448         ant->merge(*gbsiff);
01449       } else {
01450         ant=DjVuANT::create();
01451         ant->decode(*gbsiff);
01452       }
01453     }
01454     
01455     iff.close_chunk();
01456   }
01457 }
01458 
01459 void
01460 DjVuAnno::encode(const GP<ByteStream> &gbs)
01461 {
01462   GP<IFFByteStream> giff=IFFByteStream::create(gbs);
01463   IFFByteStream &iff=*giff;
01464   if (ant)
01465     {
01466 #if 0
01467       iff.put_chunk("ANTa");
01468       ant->encode(iff);
01469       iff.close_chunk();
01470 #else
01471       iff.put_chunk("ANTz");
01472       {
01473 
01474      GP<ByteStream> bsb = BSByteStream::create(giff->get_bytestream(), 50);
01475      ant->encode(*bsb);
01476       }
01477       iff.close_chunk();
01478 #endif
01479     }
01480   
01481 }
01482 
01483 
01484 GP<DjVuAnno>
01485 DjVuAnno::copy(void) const
01486 {
01487    GP<DjVuAnno> anno= new DjVuAnno;
01488       
01489    *anno=*this;
01490       
01491    if (ant) anno->ant = ant->copy();
01492    return anno;
01493 }
01494 
01495 void
01496 DjVuAnno::merge(const GP<DjVuAnno> & anno)
01497 {
01498    if (anno)
01499    {
01500       GP<ByteStream> gstr=ByteStream::create();
01501       encode(gstr);
01502       anno->encode(gstr);
01503       gstr->seek(0);
01504       decode(gstr);
01505    }
01506 }
01507 
01508 
01509 #ifdef HAVE_NAMESPACES
01510 }
01511 # ifndef NOT_USING_DJVU_NAMESPACE
01512 using namespace DJVU;
01513 # endif
01514 #endif