GURL 
getDjViewDataFile(const char *fname)
{
  GList<GURL> paths = DjVuMessage::GetProfilePaths();
  GUTF8String file = fname;

  // end hack alert
  static const char *osi = "/osi/";
  static const char *djview3 = "/djview3/";
  for (GPosition pos=paths; pos; ++pos)
    {
      GURL url = GURL(file, paths[pos]);
      GUTF8String urls = (const char*)url;
      int pos = urls.search(osi);
      if (pos >= 0)
        {
          GUTF8String urlx;
          urlx += urls.substr(0, pos);
          urlx += djview3;
          urlx += urls.substr(pos+strlen(osi), -1);
          GURL url = GURL::UTF8(urlx);
          if (url.is_file())
            return url;
        }
    }
  // end hack alert
  for (GPosition pos=paths; pos; ++pos)
    {
      GURL url = GURL(file, paths[pos]);
      if(url.is_file())
        return url;
    }
  return GURL();
}
void
GIFFChunk::set_name(GUTF8String name)
{
  DEBUG_MSG("GIFFChunk::set_name(): name='" << name << "'\n");
  DEBUG_MAKE_INDENT(3);

  const int colon=name.search(':');
  if(colon>=0)
  {
    type=name.substr(0,colon);
    name=name.substr(colon+1,(unsigned int)-1);
    if(name.search(':')>=0)
      G_THROW( ERR_MSG("GIFFManager.one_colon") );
  }

  DEBUG_MSG("auto-setting type to '" << type << "'\n");

  if (name.contains(".[]")>=0)
    G_THROW( ERR_MSG("GIFFManager.bad_char") );

  strncpy(GIFFChunk::name, (const char *)name, 4);
  GIFFChunk::name[4]=0;
  for(int i=strlen(GIFFChunk::name);i<4;i++)
    GIFFChunk::name[i]=' ';
}
// Expands a single message and inserts the arguments. Single_Message
// contains no separators (newlines), but includes all the parameters
// separated by tabs.
GUTF8String
DjVuMessageLite::LookUpSingle( const GUTF8String &Single_Message ) const
{
#if HAS_CTRL_C_IN_ERR_MSG
  if (Single_Message[0] != '\003')
    return Single_Message;
#endif
  //  Isolate the message ID and get the corresponding message text
  int ending_posn = Single_Message.contains("\t\v");
  if( ending_posn < 0 )
    ending_posn = Single_Message.length();
  GUTF8String msg_text;
  GUTF8String msg_number;
  const GUTF8String message=Single_Message.substr(0,ending_posn);
  LookUpID( message, msg_text, msg_number );

  //  Check whether we found anything
  if( !msg_text.length())
  {
    if(message == unrecognized)
    {
      msg_text = unrecognized_default;
    }else if(message == uparameter)
    {
      msg_text = uparameter_default;
    }else if(message == failed_to_parse_XML)
    {
      msg_text = failed_to_parse_XML_default;
    }else
    {
      return LookUpSingle(unrecognized + ("\t" + Single_Message));
    }
  }
    
  //  Insert the parameters (if any)
  unsigned int param_num = 0;
  while( (unsigned int)ending_posn < Single_Message.length() )
  {
    GUTF8String arg;
    const int start_posn = ending_posn+1;
    if(Single_Message[ending_posn] == '\v')
    {
      ending_posn=Single_Message.length();
      arg=LookUpSingle(Single_Message.substr(start_posn,ending_posn));
    }else
    {
      ending_posn = Single_Message.contains("\v\t",start_posn);
      if( ending_posn < 0 )
        ending_posn = Single_Message.length();
      arg=Single_Message.substr(start_posn, ending_posn-start_posn);
    }
    InsertArg( msg_text, ++param_num, arg);
  }
  //  Insert the message number
  InsertArg( msg_text, 0, msg_number );

  return msg_text;
}
void
GIFFManager::del_chunk(GUTF8String name)
      // "name" should be fully qualified, that is contain dots.
      // It may also end with [] to set the chunk order number
{
  DEBUG_MSG("GIFFManager::del_chunk(): Deleting chunk '" << name << "'\n");
  DEBUG_MAKE_INDENT(3);
   
  if (!name.length())
    G_THROW( ERR_MSG("GIFFManager.del_empty") );

  if (name[0]=='.')
  {
    const int next_dot=name.search('.',1);
    if (next_dot < 0)
    {
      if (top_level->check_name(name.substr(1,(unsigned int)-1)))
      {
        DEBUG_MSG("Removing top level chunk..\n");
        top_level=GIFFChunk::create();
        return;
      }
      G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+name.substr(1,(unsigned int)-1));
    }
    const GUTF8String top_name=name.substr(1,next_dot-1);
    if (!top_level->check_name(top_name))
      G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+top_name);
    name=name.substr(next_dot+1,(unsigned int)-1);
  }
   
  GP<GIFFChunk> cur_sec=top_level;
  const char * start, * end=(const char *)name-1;
  do
  {
    for(start=++end;*end&&(*end!='.');end++)
      EMPTY_LOOP;
    if (end>start && *end=='.')
      cur_sec=cur_sec->get_chunk(GUTF8String(start, end-start));
    if (!cur_sec)
      G_THROW( ERR_MSG("GIFFManager.cant_find") "\t"+GUTF8String(name));
  } while(*end);
   
  if (!start[0])
  {
    G_THROW(GUTF8String( ERR_MSG("GIFFManager.malformed") "\t")+name);
  }
   
  cur_sec->del_chunk(start);
}
Exemple #5
0
static void
writeText( ByteStream & str_out,
            const GUTF8String &textUTF8,
            const DjVuTXT::Zone &zone,
            const int WindowHeight )
{
//  DEBUG_MSG( "--zonetype=" << zone.ztype << "\n" );

  const GUTF8String xindent(indent( 2 * zone.ztype + 2 ));
  GPosition pos=zone.children;
  // Build attribute string
  if( ! pos )
  {
    GUTF8String coords;
    coords.format("coords=\"%d,%d,%d,%d\"",
      zone.rect.xmin, WindowHeight - 1 - zone.rect.ymin,
      zone.rect.xmax, WindowHeight - 1 - zone.rect.ymax);
    const int start=zone.text_start;
    const int end=textUTF8.firstEndSpace(start,zone.text_length);
    str_out.writestring(start_tag(zone.ztype,coords));
    str_out.writestring(textUTF8.substr(start,end-start).toEscaped());
    str_out.writestring(end_tag(zone.ztype));
  } else
  {
    writeText(str_out,textUTF8,zone.ztype,zone.children,WindowHeight);
  }
}
void DjVuSource::ReadAnnotations(GP<ByteStream> pInclStream,
		set<GUTF8String>& processed, GP<ByteStream> pAnnoStream)
{
	// Look for shared annotations
	GUTF8String strInclude;
	char buf[1024];
	int nLength;
	while ((nLength = pInclStream->read(buf, 1024)))
		strInclude += GUTF8String(buf, nLength);

	// Eat '\n' in the beginning and at the end
	while (strInclude.length() > 0 && strInclude[0] == '\n')
		strInclude = strInclude.substr(1, static_cast<unsigned int>(-1));

	while (strInclude.length() > 0 && strInclude[static_cast<int>(strInclude.length()) - 1] == '\n')
		strInclude.setat(strInclude.length() - 1, 0);

	if (strInclude.length() > 0 && processed.find(strInclude) == processed.end())
	{
		processed.insert(strInclude);

		GURL urlInclude = m_pDjVuDoc->id_to_url(strInclude);
		GP<DataPool> pool = m_pDjVuDoc->request_data(NULL, urlInclude);
		GP<ByteStream> stream = pool->get_stream();
		GP<IFFByteStream> iff(IFFByteStream::create(stream));

		// Check file format
		GUTF8String chkid;
		if (!iff->get_chunk(chkid) ||
			(chkid != "FORM:DJVI" && chkid != "FORM:DJVU" &&
			chkid != "FORM:PM44" && chkid != "FORM:BM44"))
		{
			return;
		}

		// Find chunk with page info
		while (iff->get_chunk(chkid) != 0)
		{
			GP<ByteStream> chunk_stream = iff->get_bytestream();

			if (chkid == "INCL")
			{
				ReadAnnotations(pInclStream, processed, pAnnoStream);
			}
			else if (chkid == "FORM:ANNO")
			{
				pAnnoStream->copy(*chunk_stream);
			}
			else if (chkid == "ANTa" || chkid == "ANTz")
			{
				const GP<IFFByteStream> iffout = IFFByteStream::create(pAnnoStream);
				iffout->put_chunk(chkid);
				iffout->copy(*chunk_stream);
				iffout->close_chunk();
			}

			iff->seek_close_chunk();
		}
	}
}
// Looks up the msgID in the file of messages and returns a pointer to
// the beginning of the translated message, if found; and an empty string
// otherwise.
void
DjVuMessageLite::LookUpID( const GUTF8String &xmsgID,
                       GUTF8String &message_text,
                       GUTF8String &message_number ) const
{
  if(!Map.isempty())
  {
    GUTF8String msgID = xmsgID;
#if HAS_CTRL_C_IN_ERR_MSG
    int start = 0;
    while (msgID[start] == '\003') 
      start ++;
    if (start > 0)
      msgID = msgID.substr(start, msgID.length() - start);
#endif
    GPosition pos=Map.contains(msgID);
    if(pos)
    {
      const GP<lt_XMLTags> tag=Map[pos];
      GPosition valuepos=tag->get_args().contains(valuestring);
      if(valuepos)
      {
        message_text=tag->get_args()[valuepos];
      }else
      {
        const GUTF8String raw(tag->get_raw());
        const int start_line=raw.search((unsigned long)'\n',0);
      
        const int start_text=raw.nextNonSpace(0);
        const int end_text=raw.firstEndSpace(0);
        if(start_line<0 || start_text<0 || start_text < start_line)
        {
          message_text=raw.substr(0,end_text).fromEscaped();
        }else
        {
          message_text=raw.substr(start_line+1,end_text-start_line-1).fromEscaped();
        }
      }
      GPosition numberpos=tag->get_args().contains(numberstring);
      if(numberpos)
      {
        message_number=tag->get_args()[numberpos];
      }
    }
  }
}
bool
GIFFChunk::check_name(GUTF8String name)
{
  GUTF8String type;
  const int colon=name.search(':');
  if(colon>=0)
    {
      type=name.substr(0,colon);
      name=name.substr(colon+1,(unsigned int)-1);
    }

  const GUTF8String sname=(name.substr(0,4)+"    ").substr(0,4);

  DEBUG_MSG("GIFFChunk::check_name(): type='" << type << "' name='" << sname << "'\n");
  return (type==GIFFChunk::type || (!type.length() && GIFFChunk::type=="FORM"))
    && sname==GIFFChunk::name;
}
GP<GIFFChunk>
GIFFManager::get_chunk(GUTF8String name, int * pos_num)
      // "name" should be fully qualified, that is contain dots.
      // It may also end with [] to set the chunk order number
{
  DEBUG_MSG("GIFFManager::get_chunk(): Returning chunk '" << name << "'\n");
  DEBUG_MAKE_INDENT(3);
   
  if (!name.length())
    G_THROW( ERR_MSG("GIFFManager.get_empty") );

  if (name[0]=='.')
  {
    const int next_dot=name.search('.',1);
    if (next_dot < 0)
    {
      if (top_level->check_name(name.substr(1,(unsigned int)-1)))
      {
        DEBUG_MSG("Removing top level chunk..\n");
        return top_level;
      }
      G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+name.substr(1,(unsigned int)-1));
    }
    const GUTF8String top_name=name.substr(1,next_dot-1);
    if (!top_level->check_name(top_name))
      G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+top_name);
    name=name.substr(next_dot+1,(unsigned int)-1);
  }
   
  GP<GIFFChunk> cur_sec=top_level;
  const char * start, * end=(const char *) name-1;
  do
  {
    for(start=++end;*end&&(*end!='.');end++)
      EMPTY_LOOP;
    if (end>start)
      cur_sec=cur_sec->get_chunk(GUTF8String(start, end-start), pos_num);
    if (!cur_sec)
      break;
  } while(*end);
   
  return cur_sec;
}
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;
}
GUTF8String 
GIFFChunk::decode_name(const GUTF8String &name, int &number)
{
  DEBUG_MSG("GIFFChunk::decode_name(): Checking brackets in name '" << name << "'\n");
  DEBUG_MAKE_INDENT(3);
   
  if (name.search('.')>=0)
    G_THROW( ERR_MSG("GIFFManager.no_dots") );

  number=0;
  const int obracket=name.search('[');
  GUTF8String short_name;
  if (obracket >= 0)
  {
    const int cbracket=name.search(']',obracket+1);
    if (cbracket < 0)
      G_THROW( ERR_MSG("GIFFManager.unmatched") );
    if (name.length() > (unsigned int)(cbracket+1))
      G_THROW( ERR_MSG("GIFFManager.garbage") );
//    number =atoi((const char *)name.substr(obracket+1,cbracket-obracket-1));
    number= name.substr(obracket+1,cbracket-obracket-1).toInt(); 
    short_name=name.substr(0,obracket);
  }else
  {
    short_name=name;
  }

  const int colon=short_name.search(':');
  if (colon>=0)
    short_name=short_name.substr(colon+1,(unsigned int)-1);

  for(int i=short_name.length();i<4;i++)
    short_name.setat(i, ' ');
   
  DEBUG_MSG("short_name='" << short_name << "'\n");
  DEBUG_MSG("number=" << number << "\n");
   
  return short_name;
}
void
GIFFManager::add_chunk(GUTF8String name, const TArray<char> & data)
      // name is fully qualified name of the chunk TO BE INSERTED.
      //      it may contain brackets at the end to set the position
      // All the required chunks will be created
{
  DEBUG_MSG("GIFFManager::add_chunk(): adding plain chunk with name='" << name << "'\n");
  DEBUG_MAKE_INDENT(3);

  GUTF8String chunk_name;
  const int lastdot=name.rsearch('.');
  if(lastdot < 0)
  {
    chunk_name=name;
    name=name.substr(0,lastdot);
  }else
  {
    chunk_name=name.substr(lastdot+1,(unsigned int)-1);
  }

  int pos=-1;
  const int obracket=chunk_name.search('[');
  if (obracket >= 0)
  {
    const int cbracket=chunk_name.search(']',obracket+1);
    if (cbracket < 0)
      G_THROW( ERR_MSG("GIFFManager.unmatched") );
    if (name.length() > (unsigned int)(cbracket+1))
      G_THROW( ERR_MSG("GIFFManager.garbage") );
//    pos=atoi((const char *)chunk_name.substr(obracket+1,cbracket-obracket-1));
    pos = chunk_name.substr(obracket+1,cbracket-obracket-1).toInt();
    chunk_name=chunk_name.substr(0,obracket);
  }
  DEBUG_MSG("Creating new chunk with name " << chunk_name << "\n");
  GP<GIFFChunk> chunk;
  chunk=GIFFChunk::create(chunk_name, data);
  add_chunk(name, chunk, pos);
}
int
GIFFManager::get_chunks_number(const GUTF8String &name)
   // Returns the number of chunks with given fully qualified name
{
  DEBUG_MSG("GIFFManager::get_chunks_number(): name='" << name << "'\n");
  DEBUG_MAKE_INDENT(3);

  int retval;
  const int last_dot=name.rsearch('.');
  if (last_dot<0)
  {
    retval=top_level->get_chunks_number(name);
  }else if(!last_dot)
  {
    retval=(top_level->get_name()==name.substr(1,(unsigned int)-1))?1:0;
  }else
  {
    GP<GIFFChunk> chunk=get_chunk(name.substr(0,last_dot));
    retval=( chunk
      ?(chunk->get_chunks_number(name.substr(last_dot+1,(unsigned int)-1)))
      :0 );
  }
  return retval;
}
Exemple #14
0
static unsigned long
convertToColor(const GUTF8String &s)
{
  unsigned long retval=0;
  if(s.length())
  {
    int endpos;
    if(s[0] == '#')
    {
      retval=s.substr(1,-1).toULong(0,endpos,16);
    }
    if(endpos < 0)
    {
      G_THROW( (ERR_MSG("XMLAnno.bad_color") "\t")+s );
    }
  }
  return retval;
}
//  Expands message lists by looking up the message IDs and inserting
//  arguments into the retrieved messages.
//  N.B. The resulting string may be encoded in UTF-8 format (ISO 10646-1 Annex R)
//       and SHOULD NOT BE ASSUMED TO BE ASCII.
GUTF8String
DjVuMessageLite::LookUp( const GUTF8String & MessageList ) const
{
//  DEBUG_MSG( "========== DjVuMessageLite::LookUp ==========\n" <<
//             MessageList <<
//             "\n========== DjVuMessageLite::LookUp ==========\n" );
  GUTF8String result;                       // Result string; begins empty
  if(errors.length())
  {
    const GUTF8String err1(errors);
    (const_cast<GUTF8String &>(errors)).empty();
    result=LookUp(err1)+"\n";
  }

  int start = 0;                            // Beginning of next message
  int end = MessageList.length();           // End of the message string

  //  Isolate single messages and process them
  while( start < end )
  {
    if( MessageList[start] == '\n' )
    {
      result += MessageList[start++];       // move the newline to the result
                                            // and advance to the next message
    }
    else
    {
      //  Find the end of the next message and process it
      int next_ending = MessageList.search((unsigned long)'\n', start);
      if( next_ending < 0 )
        next_ending = end;
      result += LookUpSingle( MessageList.substr(start, next_ending-start) );
      //  Advance to the next message
      start = next_ending;
    }
  }

  //  All done 
  return result;
}
Exemple #16
0
// used to build the zone tree
// true is returned if the GRect is known for this object,
// and false, if the rectangle's size is just the parent size.
static bool
make_child_layer(
  DjVuTXT::Zone &parent,
  const lt_XMLTags &tag, ByteStream &bs,
  const int height, const double ws, const double hs)
{
  bool retval=true;
  // the plugin thinks there are only Pages, Lines and Words
  // so we don't make Paragraphs, Regions and Columns zones
  // if we did the plugin is not able to search the text but 
  // DjVuToText writes out all the text anyway
  DjVuTXT::Zone *self_ptr;
  char sepchar;
  const GUTF8String name(tag.get_name());
  if(name == charactertag)
  {
    self_ptr=parent.append_child();
    self_ptr->ztype = DjVuTXT::CHARACTER;
    sepchar=0;  
  }else if(name == wordtag)
  {
    self_ptr=parent.append_child();
    self_ptr->ztype = DjVuTXT::WORD;
    sepchar=' ';
  }else if(name == linetag)
  {
    self_ptr=parent.append_child();
    self_ptr->ztype = DjVuTXT::LINE;
    sepchar=DjVuTXT::end_of_line;
  }else if(name == paragraphtag)
  {
    self_ptr=parent.append_child();
    self_ptr->ztype = DjVuTXT::PARAGRAPH;
    sepchar=DjVuTXT::end_of_paragraph;
  }else if(name == regiontag)
  {
    self_ptr=parent.append_child();
    self_ptr->ztype = DjVuTXT::REGION;
    sepchar=DjVuTXT::end_of_region;
  }else if(name == pagecolumntag)
  {
    self_ptr=parent.append_child();
    self_ptr->ztype = DjVuTXT::COLUMN;
    sepchar=DjVuTXT::end_of_column;
  }else
  {
    self_ptr = &parent;
    self_ptr->ztype = DjVuTXT::PAGE;
    sepchar=0;
  }
  DjVuTXT::Zone &self = *self_ptr;
  self.text_start = bs.tell();
  int &xmin=self.rect.xmin, &ymin=self.rect.ymin, 
    &xmax=self.rect.xmax, &ymax=self.rect.ymax;
  GRect default_rect;
  default_rect.xmin=max(parent.rect.xmax,parent.rect.xmin);
  default_rect.xmax=min(parent.rect.xmax,parent.rect.xmin);
  default_rect.ymin=max(parent.rect.ymax,parent.rect.ymin);
  default_rect.ymax=min(parent.rect.ymax,parent.rect.ymin);
  // Now if there are coordinates, use those.
  GPosition pos(tag.get_args().contains("coords"));
  if(pos)
  {
    GList<int> rectArgs;
    intList(tag.get_args()[pos], rectArgs);
    if((pos=rectArgs))
    {
      xmin=(int)(ws*(double)rectArgs[pos]);
      if(++pos)
      {
        ymin=(height-1)-(int)(hs*(double)rectArgs[pos]);
        if(++pos)
        {
          xmax=(int)(ws*(double)rectArgs[pos]);
          if(++pos)
          {
            ymax=(height-1)-(int)(hs*(double)rectArgs[pos]);
            if(xmin>xmax) // Make sure xmin is really minimum
            {
              const int t=xmin;
              xmin=xmax;
              xmax=t;
            }
            if(ymin>ymax) // Make sure ymin is really minimum
            {
              const int t=ymin;
              ymin=ymax;
              ymax=t;
            }
          }
        }
      }
    }
  }
  if(self.ztype == DjVuTXT::CHARACTER)
  {
    if(! pos)
    {
      self.rect=default_rect;
      retval=false;
    }
    const GUTF8String raw(tag.get_raw().fromEscaped());
    const int i=raw.nextNonSpace(0);
    bs.writestring(raw.substr(i,raw.firstEndSpace(i)-i));
    if(sepchar)
      bs.write8(sepchar);
    self.text_length = bs.tell() - self.text_start;
  }else if(pos)
  {
    pos=tag.get_content();
    if(pos)
    {
      for(pos=tag.get_content(); pos; ++pos)
      {
        const GP<lt_XMLTags> t(tag.get_content()[pos].tag);
        make_child_layer(self, *t, bs, height,ws,hs);
      }
      if(sepchar)
        bs.write8(sepchar);
      self.text_length = bs.tell() - self.text_start;
    }else
    {
      const GUTF8String raw(tag.get_raw().fromEscaped());
      const int i=raw.nextNonSpace(0);
      bs.writestring(raw.substr(i,raw.firstEndSpace(i)-i));
      if(sepchar)
        bs.write8(sepchar);
      self.text_length = bs.tell() - self.text_start;
    }
  }else
  {
    self.rect=default_rect;
    if((pos=tag.get_content()))
    {
      do
      {
        const GP<lt_XMLTags> t(tag.get_content()[pos].tag);
        const GRect save_rect(self.rect);
        self.rect=default_rect;
	if ((retval = make_child_layer(self, *t, bs, height, ws, hs)))
        {
          xmin=min(save_rect.xmin,xmin);
          xmax=max(save_rect.xmax,xmax);
          ymin=min(save_rect.ymin,ymin);
          ymax=max(save_rect.ymax,ymax);
        }else
        {
          // If the child doesn't have coordinates, we need to use a box
          // at least as big as the parent's coordinates.
          xmin=min(save_rect.xmin,default_rect.xmax);
          xmax=max(save_rect.xmax,default_rect.xmin);
          ymin=min(save_rect.ymin,default_rect.ymax);
          ymax=max(save_rect.ymax,default_rect.ymin);
          for(; pos; ++pos)
          {
            const GP<lt_XMLTags> t(tag.get_content()[pos].tag);
            make_child_layer(self, *t, bs, height,ws,hs);
          }
          break;
        }
      } while(++pos);
      if(sepchar)
        bs.write8(sepchar);
      self.text_length = bs.tell() - self.text_start;
    }else
    {
      const GUTF8String raw(tag.get_raw().fromEscaped());
      const int i=raw.nextNonSpace(0);
      bs.writestring(raw.substr(i,raw.firstEndSpace(i)-i));
      if(sepchar)
        bs.write8(sepchar);
      self.text_length = bs.tell() - self.text_start;
    }
  }
  parent.rect.xmin=min(xmin,parent.rect.xmin);
  parent.rect.ymin=min(ymin,parent.rect.ymin);
  parent.rect.xmax=max(xmax,parent.rect.xmax);
  parent.rect.ymax=max(ymax,parent.rect.ymax);
  if(xmin>xmax)
  {
    const int t=xmin;
    xmin=xmax;
    xmax=t;
  }
  if(ymin>ymax)
  {
    const int t=ymin;
    ymin=ymax;
    ymax=t;
  }
//  DjVuPrintMessage("(%d,%d)(%d,%d)<<<\\%o>>>\n",
//    xmin,ymin,xmax,ymax, sepchar);
  return retval;
}
void
GIFFManager::add_chunk(GUTF8String parent_name, const GP<GIFFChunk> & chunk,
		       int pos)
      // parent_name is the fully qualified name of the PARENT
      //             IT MAY BE EMPTY
      // All the required chunks will be created
      // pos=-1 means to append the chunk
{
  DEBUG_MSG("GIFFManager::add_chunk(): Adding chunk to name='" << parent_name << "'\n");
  DEBUG_MAKE_INDENT(3);
   
  if (!top_level->get_name().length())
  {
    if ((!parent_name.length())||(parent_name[0]!='.'))
      G_THROW( ERR_MSG("GIFFManager.no_top_name") );
    if (parent_name.length() < 2)
    {
      // 'chunk' is actually the new top-level chunk
      DEBUG_MSG("since parent_name=='.', making the chunk top-level\n");
      if (!chunk->is_container())
        G_THROW( ERR_MSG("GIFFManager.no_top_cont") );
      top_level=chunk;
      return;
    }

    DEBUG_MSG("Setting the name of the top-level chunk\n");
    const int next_dot=parent_name.search('.',1);
    if(next_dot>=0)
    {
      top_level->set_name(parent_name.substr(1,next_dot-1));
    }else
    {
      top_level->set_name(parent_name.substr(1,(unsigned int)-1));
    }
  }

  DEBUG_MSG("top level chunk name='" << top_level->get_name() << "'\n");
   
  if (parent_name.length() && parent_name[0] == '.')
  {
    int next_dot=parent_name.search('.',1);
    if(next_dot<0)
    {
      next_dot=parent_name.length();
    }
    GUTF8String top_name=parent_name.substr(1,next_dot-1);
    if (!top_level->check_name(top_name))
      G_THROW( ERR_MSG("GIFFManager.wrong_name") "\t"+top_name);
    parent_name=parent_name.substr(next_dot,(unsigned int)-1);
  }

  GP<GIFFChunk> cur_sec=top_level;
  const char * start, * end=(const char *)parent_name-1;
  do
  {
    for(start=++end;*end&&(*end!='.');end++)
      EMPTY_LOOP;
    if (end>start)
    {
      GUTF8String name(start,end-start);
      GUTF8String short_name;
      int number=0;
      const int obracket=name.search('[');
      if (obracket >= 0)
      {
        const int cbracket=name.search(']',obracket+1);
        if (cbracket < 0)
          G_THROW( ERR_MSG("GIFFManager.unmatched") );
//        number=atoi((const char *)name.substr(obracket+1,cbracket-obracket-1));
        number = name.substr(obracket+1,cbracket-obracket-1).toInt();
        short_name=name.substr(0,obracket);
      }else
      {
        short_name=name;
      }

      for(int i=cur_sec->get_chunks_number(short_name);i<number+1;i++)
        cur_sec->add_chunk(GIFFChunk::create(short_name));
      cur_sec=cur_sec->get_chunk(name);
      if (!cur_sec)
        G_THROW( ERR_MSG("GIFFManager.unknown") "\t"+name);
    }
  } while(*end);
  cur_sec->add_chunk(chunk, pos);
}
// Insert a string into the message text. Will insert into any field
// description.  Except for an ArgId of zero (message number), if the ArgId
// is not found, the routine adds a line with the parameter so information
// will not be lost.
void
DjVuMessageLite::InsertArg( GUTF8String &message,
  const int ArgId, const GUTF8String &arg ) const
{
    // argument target string
  const GUTF8String target= "%"+GUTF8String(ArgId)+"!";
    // location of target string
  int format_start = message.search( (const char *)target );
  if( format_start >= 0 )
  {
    do
    {
      const int n=format_start+target.length()+1;
      const int format_end=message.search((unsigned long)'!',n);
      if(format_end > format_start)
      { 
        const int len=1+format_end-n;
        if(len && isascii(message[n-1]))
        {
          GUTF8String narg;
          GUTF8String format="%"+message.substr(n-1,len);
          switch(format[len])
          {
            case 'd':
            case 'i':
              narg.format((const char *)format,arg.toInt());
              break;
            case 'u':
            case 'o':
            case 'x':
            case 'X':
              narg.format((const char *)format,(unsigned int)arg.toInt());
              break;
            case 'f':
            case 'g':
            case 'e':
              {
                int endpos;
                narg.format((const char *)format, arg.toDouble(0,endpos));
                if( endpos < 0 )
                  narg = arg;
              }
              break;
            default:
              narg.format((const char *)format,(const char *)arg);
              break;
          }
          message = message.substr( 0, format_start )+narg
            +message.substr( format_end+1, -1 );
        }else
        {
          message = message.substr( 0, format_start )+arg
            +message.substr( format_end+1, -1 );
        }
      }
      format_start=message.search((const char*)target, format_start+arg.length());
    } while(format_start >= 0);
  }
  else
  {
    //  Not found, fake it
    if( ArgId != 0 )
    {
      message += "\n"+LookUpSingle(uparameter+("\t"+arg));
    }
  }
}