source: trunk/libdjvu/DjVuText.cpp @ 17

Last change on this file since 17 was 17, checked in by Eugene Romanenko, 15 years ago

update makefiles, remove absolute paths, update djvulibre to version 3.5.17

File size: 24.9 KB
Line 
1//C-  -*- C++ -*-
2//C- -------------------------------------------------------------------
3//C- DjVuLibre-3.5
4//C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
5//C- Copyright (c) 2001  AT&T
6//C-
7//C- This software is subject to, and may be distributed under, the
8//C- GNU General Public License, Version 2. The license should have
9//C- accompanied the software or you may obtain a copy of the license
10//C- from the Free Software Foundation at http://www.fsf.org .
11//C-
12//C- This program is distributed in the hope that it will be useful,
13//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
14//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15//C- GNU General Public License for more details.
16//C-
17//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library
18//C- distributed by Lizardtech Software.  On July 19th 2002, Lizardtech
19//C- Software authorized us to replace the original DjVu(r) Reference
20//C- Library notice by the following text (see doc/lizard2002.djvu):
21//C-
22//C-  ------------------------------------------------------------------
23//C- | DjVu (r) Reference Library (v. 3.5)
24//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
25//C- | The DjVu Reference Library is protected by U.S. Pat. No.
26//C- | 6,058,214 and patents pending.
27//C- |
28//C- | This software is subject to, and may be distributed under, the
29//C- | GNU General Public License, Version 2. The license should have
30//C- | accompanied the software or you may obtain a copy of the license
31//C- | from the Free Software Foundation at http://www.fsf.org .
32//C- |
33//C- | The computer code originally released by LizardTech under this
34//C- | license and unmodified by other parties is deemed "the LIZARDTECH
35//C- | ORIGINAL CODE."  Subject to any third party intellectual property
36//C- | claims, LizardTech grants recipient a worldwide, royalty-free,
37//C- | non-exclusive license to make, use, sell, or otherwise dispose of
38//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
39//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
40//C- | General Public License.   This grant only confers the right to
41//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
42//C- | the extent such infringement is reasonably necessary to enable
43//C- | recipient to make, have made, practice, sell, or otherwise dispose
44//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
45//C- | any greater extent that may be necessary to utilize further
46//C- | modifications or combinations.
47//C- |
48//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
49//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
50//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
51//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
52//C- +------------------------------------------------------------------
53//
54// $Id: DjVuText.cpp,v 1.10 2004/07/07 19:23:36 leonb Exp $
55// $Name:  $
56
57#ifdef HAVE_CONFIG_H
58# include "config.h"
59#endif
60#if NEED_GNUG_PRAGMAS
61# pragma implementation
62#endif
63
64#include "DjVuText.h"
65#include "IFFByteStream.h"
66#include "BSByteStream.h"
67#include "debug.h"
68#include <ctype.h>
69
70
71
72#ifdef HAVE_NAMESPACES
73namespace DJVU {
74# ifdef NOT_DEFINED // Just to fool emacs c++ mode
75}
76#endif
77#endif
78
79
80
81#ifdef min
82#undef min
83#endif
84template<class TYPE>
85static inline TYPE min(TYPE a,TYPE b) { return (a<b)?a:b; }
86
87//***************************************************************************
88//******************************** DjVuTXT **********************************
89//***************************************************************************
90
91const char DjVuTXT::end_of_column    = 013;      // VT: Vertical Tab
92const char DjVuTXT::end_of_region    = 035;      // GS: Group Separator
93const char DjVuTXT::end_of_paragraph = 037;      // US: Unit Separator
94const char DjVuTXT::end_of_line      = 012;      // LF: Line Feed
95
96const int DjVuTXT::Zone::version  = 1;
97
98DjVuTXT::Zone::Zone()
99  : ztype(DjVuTXT::PAGE), text_start(0), text_length(0), zone_parent(0)
100{
101}
102
103DjVuTXT::Zone *
104DjVuTXT::Zone::append_child()
105{
106  Zone empty;
107  empty.ztype = ztype;
108  empty.text_start = 0;
109  empty.text_length = 0;
110  empty.zone_parent=this;
111  children.append(empty);
112  return & children[children.lastpos()];
113}
114
115void
116DjVuTXT::Zone::cleartext()
117{
118  text_start = 0;
119  text_length = 0;
120  for (GPosition i=children; i; ++i)
121    children[i].cleartext();
122}
123
124void
125DjVuTXT::Zone::normtext(const char *instr, GUTF8String &outstr)
126{
127  if (text_length == 0)
128    {
129      // Descend collecting text below
130      text_start = outstr.length();
131      for (GPosition i=children; i; ++i)
132        children[i].normtext(instr, outstr);
133      text_length = outstr.length() - text_start;
134      // Ignore empty zones
135      if (text_length == 0)
136        return;
137    }
138  else
139    {
140      // Collect text at this level
141      int new_start = outstr.length();
142      outstr = outstr + GUTF8String(instr+text_start, text_length);
143      text_start = new_start;
144      // Clear textual information on lower level nodes
145      for (GPosition i=children; i; ++i)
146        children[i].cleartext();
147    }
148  // Determine standard separator
149  char sep;
150  switch (ztype)
151    {
152    case COLUMN:
153      sep = end_of_column; break;
154    case REGION:
155      sep = end_of_region; break;
156    case PARAGRAPH: 
157      sep = end_of_paragraph; break;
158    case LINE:
159      sep = end_of_line; break;
160    case WORD:
161      sep = ' '; break;
162    default:
163      return;
164    }
165  // Add separator if not present yet.
166  if (outstr[text_start+text_length-1] != sep)
167    {
168      outstr = outstr + GUTF8String(&sep, 1);
169      text_length += 1;
170    }
171}
172
173unsigned int 
174DjVuTXT::Zone::memuse() const
175{
176  int memuse = sizeof(*this);
177  for (GPosition i=children; i; ++i)
178    memuse += children[i].memuse();
179  return memuse;
180}
181
182
183#ifndef NEED_DECODER_ONLY
184void 
185DjVuTXT::Zone::encode(
186  const GP<ByteStream> &gbs, const Zone * parent, const Zone * prev) const
187{
188  ByteStream &bs=*gbs;
189  // Encode type
190  bs.write8(ztype);
191 
192  // Modify text_start and bounding rectangle based on the context
193  // (whether there is a previous non-zero same-level-child or parent)
194  int start=text_start;
195  int x=rect.xmin, y=rect.ymin;
196  int width=rect.width(), height=rect.height();
197  if (prev)
198  {
199    if (ztype==PAGE || ztype==PARAGRAPH || ztype==LINE)
200    {
201      // Encode offset from the lower left corner of the previous
202      // child in the coord system in that corner with x to the
203      // right and y down
204      x=x-prev->rect.xmin;
205      y=prev->rect.ymin-(y+height);
206    } else // Either COLUMN or WORD or CHARACTER
207    {
208      // Encode offset from the lower right corner of the previous
209      // child in the coord system in that corner with x to the
210      // right and y up
211      x=x-prev->rect.xmax;
212      y=y-prev->rect.ymin;
213    }
214    start-=prev->text_start+prev->text_length;
215  } else if (parent)
216  {
217    // Encode offset from the upper left corner of the parent
218    // in the coord system in that corner with x to the right and y down
219    x=x-parent->rect.xmin;
220    y=parent->rect.ymax-(y+height);
221    start-=parent->text_start;
222  }
223  // Encode rectangle
224  bs.write16(0x8000+x);
225  bs.write16(0x8000+y);
226  bs.write16(0x8000+width);
227  bs.write16(0x8000+height);
228  // Encode text info
229  bs.write16(0x8000+start);
230  bs.write24(text_length);
231  // Encode number of children
232  bs.write24(children.size());
233 
234  const Zone * prev_child=0;
235  // Encode all children
236  for (GPosition i=children; i; ++i)
237  {
238    children[i].encode(gbs, this, prev_child);
239    prev_child=&children[i];
240  }
241}
242#endif
243
244void 
245DjVuTXT::Zone::decode(const GP<ByteStream> &gbs, int maxtext,
246                      const Zone * parent, const Zone * prev)
247{
248  ByteStream &bs=*gbs;
249  // Decode type
250  ztype = (ZoneType) bs.read8();
251  if ( ztype<PAGE || ztype>CHARACTER )
252    G_THROW( ERR_MSG("DjVuText.corrupt_text") );
253
254  // Decode coordinates
255  int x=(int) bs.read16()-0x8000;
256  int y=(int) bs.read16()-0x8000;
257  int width=(int) bs.read16()-0x8000;
258  int height=(int) bs.read16()-0x8000;
259
260  // Decode text info
261  text_start = (int) bs.read16()-0x8000;
262//  int start=text_start;
263  text_length = bs.read24();
264  if (prev)
265  {
266    if (ztype==PAGE || ztype==PARAGRAPH || ztype==LINE)
267    {
268      x=x+prev->rect.xmin;
269      y=prev->rect.ymin-(y+height);
270    } else // Either COLUMN or WORD or CHARACTER
271    {
272      x=x+prev->rect.xmax;
273      y=y+prev->rect.ymin;
274    }
275    text_start+=prev->text_start+prev->text_length;
276  } else if (parent)
277  {
278    x=x+parent->rect.xmin;
279    y=parent->rect.ymax-(y+height);
280    text_start+=parent->text_start;
281  }
282  rect=GRect(x, y, width, height);
283  // Get children size
284  int size = bs.read24();
285
286  // Checks
287  if (rect.isempty() || text_start<0 || text_start+text_length>maxtext )
288    G_THROW( ERR_MSG("DjVuText.corrupt_text") );
289
290  // Process children
291  const Zone * prev_child=0;
292  children.empty();
293  while (size-- > 0) 
294  {
295    Zone *z = append_child();
296    z->decode(gbs, maxtext, this, prev_child);
297    prev_child=z;
298  }
299}
300
301void 
302DjVuTXT::normalize_text()
303{
304  GUTF8String newtextUTF8;
305  page_zone.normtext( (const char*)textUTF8, newtextUTF8 );
306  textUTF8 = newtextUTF8;
307}
308
309int 
310DjVuTXT::has_valid_zones() const
311{
312  if (!textUTF8)
313    return false;
314  if (page_zone.children.isempty() || page_zone.rect.isempty()) 
315    return false;
316  return true;
317}
318
319
320#ifndef NEED_DECODER_ONLY
321void 
322DjVuTXT::encode(const GP<ByteStream> &gbs) const
323{
324  ByteStream &bs=*gbs;
325  if (! textUTF8 )
326    G_THROW( ERR_MSG("DjVuText.no_text") );
327  // Encode text
328  int textsize = textUTF8.length();
329  bs.write24( textsize );
330  bs.writall( (void*)(const char*)textUTF8, textsize );
331  // Encode zones
332  if (has_valid_zones())
333  {
334    bs.write8(Zone::version);
335    page_zone.encode(gbs);
336  }
337}
338#endif
339
340void 
341DjVuTXT::decode(const GP<ByteStream> &gbs)
342{
343  ByteStream &bs=*gbs;
344  // Read text
345  textUTF8.empty();
346  int textsize = bs.read24();
347  char *buffer = textUTF8.getbuf(textsize);
348  int readsize = bs.read(buffer,textsize);
349  buffer[readsize] = 0;
350  if (readsize < textsize)
351    G_THROW( ERR_MSG("DjVuText.corrupt_chunk") );
352  // Try reading zones
353  unsigned char version;
354  if ( bs.read( (void*) &version, 1 ) == 1) 
355  {
356    if (version != Zone::version)
357      G_THROW( ERR_MSG("DjVuText.bad_version") "\t" + GUTF8String(version) );
358    page_zone.decode(gbs, textsize);
359  }
360}
361
362GP<DjVuTXT> 
363DjVuTXT::copy(void) const
364{
365  return new DjVuTXT(*this);
366}
367
368
369static inline bool
370intersects_zone(GRect box, const GRect &zone)
371{
372  return
373    ((box.xmin < zone.xmin)
374      ?(box.xmax >= zone.xmin)
375      :(box.xmin <= zone.xmax))
376    &&((box.ymin < zone.ymin)
377      ?(box.ymax >= zone.ymin)
378      :(box.ymin <= zone.ymax));
379}
380
381void
382DjVuTXT::Zone::get_text_with_rect(const GRect &box, 
383                                  int &string_start, int &string_end) const
384{
385  GPosition pos=children;
386  if(pos?box.contains(rect):intersects_zone(box,rect))
387  {
388    const int text_end=text_start+text_length;
389    if(string_start == string_end)
390    {
391      string_start=text_start;
392      string_end=text_end;
393    }else
394    {
395      if (string_end < text_end)
396        string_end=text_end;
397      if(text_start < string_start)
398        string_start=text_start;
399    }
400  }else if(pos&&intersects_zone(box,rect))
401  {
402    do
403    {
404      children[pos].get_text_with_rect(box,string_start,string_end);
405    } while(++pos);
406  }
407}
408
409void
410DjVuTXT::Zone::find_zones(GList<Zone *> &list, 
411                          const int string_start, const int string_end) const
412{
413  const int text_end=text_start+text_length;
414  if(text_start >= string_start)
415    {
416      if(text_end <= string_end)
417        {
418          list.append(const_cast<Zone *>(this));
419        }
420      else if(text_start < string_end)
421        {
422          if (children.size())
423            for (GPosition pos=children; pos; ++pos)
424              children[pos].find_zones(list,string_start,string_end);
425          else
426            list.append(const_cast<Zone *>(this));
427        }
428    }
429  else if( text_end > string_start)
430    {
431      if (children.size())
432        for (GPosition pos=children; pos; ++pos)
433          children[pos].find_zones(list,string_start,string_end);
434      else
435        list.append(const_cast<Zone *>(this));
436    }
437}
438
439void
440DjVuTXT::Zone::get_smallest(GList<GRect> &list) const
441{
442  GPosition pos=children;
443  if(pos)
444    {
445      do {
446        children[pos].get_smallest(list);
447      } while (++pos);
448    }
449  else
450    {
451      list.append(rect);
452    }
453}
454
455void
456DjVuTXT::Zone::get_smallest(GList<GRect> &list, const int padding) const
457{
458  GPosition pos=children;
459  if(pos)
460    {
461      do {
462        children[pos].get_smallest(list,padding);
463      } while (++pos);
464    }
465  else if(zone_parent && zone_parent->ztype >= PARAGRAPH)
466    {
467      const GRect &xrect=zone_parent->rect;
468      if(xrect.height() < xrect.width())
469        {
470          list.append(GRect(rect.xmin-padding,xrect.ymin-padding,rect.width()
471                            +2*padding,xrect.height()+2*padding));
472        }
473      else
474        {
475          list.append(GRect(xrect.xmin-padding,rect.ymin-padding,xrect.width()
476                            +2*padding,rect.height()+2*padding));
477        }
478    }
479  else
480    {
481      list.append(GRect(rect.xmin-padding,rect.ymin-padding,rect.width()
482                        +2*padding,rect.height()+2*padding));
483    }
484}
485
486void
487DjVuTXT::get_zones(int zone_type, const Zone *parent, 
488                   GList<Zone *> & zone_list) const 
489   // get all the zones of  type zone_type under zone node parent
490{
491   // search all branches under parent
492   const Zone *zone=parent;
493   for( int cur_ztype=zone->ztype; cur_ztype<zone_type; ++cur_ztype )
494   {
495      GPosition pos;
496      for(pos=zone->children; pos; ++pos)
497      {
498         Zone *zcur=(Zone *)&zone->children[pos];
499         if ( zcur->ztype == zone_type )
500         {
501            GPosition zpos=zone_list;
502            if ( !zone_list.search(zcur,zpos) )
503               zone_list.append(zcur);
504         }
505         else if ( zone->children[pos].ztype < zone_type )
506            get_zones(zone_type, &zone->children[pos], zone_list);
507      }
508   }
509}
510
511GList<GRect>
512DjVuTXT::find_text_with_rect(const GRect &box, GUTF8String &text, 
513                             const int padding) const
514{
515  GList<GRect> retval;
516  int text_start=0;
517  int text_end=0;
518  page_zone.get_text_with_rect(box,text_start,text_end);
519  if(text_start != text_end)
520  {
521    GList<Zone *> zones;
522    page_zone.find_zones(zones,text_start,text_end);
523    GPosition pos=zones;
524    if(pos)
525    {
526      do
527      {
528        if(padding >= 0)
529        {
530          zones[pos]->get_smallest(retval,padding);
531        }else
532        {
533          zones[pos]->get_smallest(retval);
534        }
535      } while(++pos);
536    }
537  }
538  text=textUTF8.substr(text_start,text_end-text_start);
539  return retval;
540}
541
542
543GList<DjVuTXT::Zone *>
544DjVuTXT::find_text_in_rect(GRect target_rect, GUTF8String &text) const
545   // returns a list of zones of type WORD in the nearest/selected paragraph
546{
547   GList<Zone *> zone_list;
548   GList<Zone *> lines;
549
550   get_zones((int)PARAGRAPH, &page_zone, zone_list);
551   // it's possible that no paragraph structure exists for reasons that 
552   // 1) ocr engine is not capable 2) file was modified by user. In such case,
553   // we can only make a rough guess, i.e., select all the lines intersected with
554   // target_rect
555   if (zone_list.isempty())
556   {
557      get_zones((int)LINE, &page_zone, zone_list);
558      GPosition pos;
559      for(pos=zone_list; pos; ++pos)
560      {
561         GRect rect=zone_list[pos]->rect;
562         int h0=rect.height()/2;
563         if(rect.intersect(rect,target_rect) && rect.height()>h0)
564            lines.append(zone_list[pos]);
565      }
566   } else 
567   {
568      GPosition pos, pos_sel=zone_list;
569      float ar=0;
570      for(pos=zone_list; pos; ++pos)
571      {
572         GRect rect=zone_list[pos]->rect;
573         int area=rect.area();
574         if (rect.intersect(rect, target_rect))
575         {
576            float ftmp=rect.area()/(float)area;
577            if ( !ar || ar<ftmp )
578            {
579               ar=ftmp;
580               pos_sel=pos;
581            }
582         }
583      }
584      Zone *parag = 0;
585      if ( ar>0 ) parag=zone_list[pos_sel];
586      zone_list.empty();
587      if ( ar>0 ) 
588      {
589         get_zones((int)LINE, parag, zone_list);
590         if ( !zone_list.isempty() )
591         {
592            for(GPosition pos=zone_list; pos; ++pos)
593            {
594               GRect rect=zone_list[pos]->rect;
595               int h0=rect.height()/2;
596               if(rect.intersect(rect,target_rect) && rect.height()>h0)
597                  lines.append(zone_list[pos]);
598            }
599         }
600      }
601   }
602
603   zone_list.empty();
604   if (!lines.isempty()) 
605   {
606      int i=1, lsize=lines.size();
607
608      GList<Zone *> words;
609      for (GPosition pos=lines; pos; ++pos, ++i)
610      {
611         words.empty();
612         get_zones((int)WORD, lines[pos], words);
613
614         if ( lsize==1 )
615         {
616            for(GPosition p=words;p;++p)
617            {
618               GRect rect=words[p]->rect;
619               if(rect.intersect(rect,target_rect))
620               //if (target_rect.contains(words[p]->rect))
621                  zone_list.append(words[p]);
622            }
623         } else
624         {
625            if (i==1)
626            {
627               bool start=true;
628               for(GPosition p=words; p; ++p)
629               {
630                  if ( start )
631                  {
632                     GRect rect=words[p]->rect;
633                     if(rect.intersect(rect,target_rect))
634                        //if (target_rect.contains(words[p]->rect))
635                     {
636                        start=false;
637                        zone_list.append(words[p]);
638                     }
639                  } else 
640                     zone_list.append(words[p]);
641               }
642            } else if (i==lsize)
643            {
644               bool end=true;
645               for(GPosition p=words.lastpos();p;--p)
646               {
647                  if ( end )
648                  {
649                     GRect rect=words[p]->rect;
650                     if(rect.intersect(rect,target_rect))
651                        //if(target_rect.contains(words[p]->rect) )
652                     {
653                        end=false;
654                        zone_list.append(words[p]);
655                     }
656                  } else 
657                     zone_list.append(words[p]);
658               }
659            }
660
661            if (i!=1 && i!=lsize )
662            {
663               for(GPosition p=words;p;++p)
664                  zone_list.append(words[p]);
665            }
666         }
667      }
668   } 
669
670   return zone_list;
671}
672
673unsigned int 
674DjVuTXT::get_memory_usage() const
675{
676  return sizeof(*this) + textUTF8.length() + page_zone.memuse() - sizeof(page_zone); 
677}
678
679
680
681//***************************************************************************
682//******************************** DjVuText *********************************
683//***************************************************************************
684
685void
686DjVuText::decode(const GP<ByteStream> &gbs)
687{
688  GUTF8String chkid;
689  GP<IFFByteStream> giff=IFFByteStream::create(gbs);
690  IFFByteStream &iff=*giff;
691  while( iff.get_chunk(chkid) )
692  {
693    if (chkid == "TXTa")
694    {
695      if (txt)
696        G_THROW( ERR_MSG("DjVuText.dupl_text") );
697      txt = DjVuTXT::create();
698      txt->decode(iff.get_bytestream());
699    }
700    else if (chkid == "TXTz")
701    {
702      if (txt)
703        G_THROW( ERR_MSG("DjVuText.dupl_text") );
704      txt = DjVuTXT::create();
705      const GP<ByteStream> gbsiff=BSByteStream::create(iff.get_bytestream());
706      txt->decode(gbsiff);
707    }
708    // Add decoding of other chunks here
709    iff.close_chunk();
710  }
711}
712
713void
714DjVuText::encode(const GP<ByteStream> &gbs)
715{
716  if (txt)
717  {
718    const GP<IFFByteStream> giff=IFFByteStream::create(gbs);
719    IFFByteStream &iff=*giff;
720    iff.put_chunk("TXTz");
721    {
722      GP<ByteStream> gbsiff=BSByteStream::create(iff.get_bytestream(),50);
723      txt->encode(gbsiff);
724    }
725    iff.close_chunk();
726  }
727  // Add encoding of other chunks here
728}
729
730
731GP<DjVuText>
732DjVuText::copy(void) const
733{
734   GP<DjVuText> text= new DjVuText;
735      // Copy any primitives (if any)
736   *text=*this;
737      // Copy each substructure
738   if (txt)
739     text->txt = txt->copy();
740   return text;
741}
742
743static GUTF8String
744indent ( int spaces)
745{
746  GUTF8String ret;
747  for( int i = 0 ; i < spaces ; i++ )
748    ret += ' ';
749  return ret;
750}
751
752static const char *tags[8]=
753{ 0,
754  "HIDDENTEXT",
755  "PAGECOLUMN",
756  "REGION",
757  "PARAGRAPH",
758  "LINE",
759  "WORD",
760  "CHARACTER" };
761static const int tags_size=sizeof(tags)/sizeof(const char *);
762
763static GUTF8String
764start_tag(const DjVuTXT::ZoneType zone)
765{
766  GUTF8String retval;
767  if((tags_size > (int)zone)&&((int)zone > 0))
768  {
769    switch (zone)
770    {
771      case DjVuTXT::CHARACTER:
772        retval="<"+GUTF8String(tags[zone])+">";
773        break;
774      case DjVuTXT::WORD:
775        retval=indent(2*(int)zone+2)+"<"+tags[zone]+">";
776        break;
777      default:
778        retval=indent(2*(int)zone+2)+"<"+tags[zone]+">\n";
779        break;
780    }
781  }
782  return retval;
783}
784
785static GUTF8String
786start_tag(const DjVuTXT::ZoneType zone, const GUTF8String &attributes)
787{
788  GUTF8String retval;
789  if((tags_size > (int)zone)&&((int)zone > 0))
790  {
791    switch (zone)
792    {
793      case DjVuTXT::CHARACTER:
794        retval="<"+GUTF8String(tags[zone])+" "+attributes+">";
795        break;
796      case DjVuTXT::WORD:
797        retval=indent(2*(int)zone+2)+"<"+tags[zone]+" "+attributes+">";
798        break;
799      default:
800        retval=indent(2*(int)zone+2)+"<"+tags[zone]+" "+attributes+">\n";
801        break;
802    }
803  }
804  return retval;
805}
806
807static inline GUTF8String
808start_tag(const int layer)
809{
810  return start_tag((const DjVuTXT::ZoneType)layer);
811}
812
813
814static GUTF8String
815end_tag(const DjVuTXT::ZoneType zone)
816{
817  GUTF8String retval;
818  if((tags_size > (int)zone)&&((int)zone >= 0))
819  {
820    switch (zone)
821    {
822      case DjVuTXT::CHARACTER:
823        retval="</"+GUTF8String(tags[zone])+">";
824        break;
825      case DjVuTXT::WORD:
826        retval="</"+GUTF8String(tags[zone])+">\n";
827        break;
828      default:
829        retval=indent(2*(int)zone+2)+"</"+tags[zone]+">\n";
830        break;
831    }
832  }
833  return retval;
834}
835
836static inline GUTF8String
837end_tag(const int layer)
838{
839  return end_tag((const DjVuTXT::ZoneType)layer);
840}
841
842static GUTF8String
843tolayer(int &layer, const DjVuTXT::ZoneType next_layer)
844{
845  GUTF8String retval;
846  for( ;layer < (int)next_layer;layer++ )
847  {
848    retval+=start_tag(layer);
849  }
850  while (layer > (int)next_layer )
851  {
852    retval+=end_tag(--layer);
853  }
854  return retval;
855}
856
857static void
858writeText( ByteStream & str_out,
859            const GUTF8String &textUTF8,
860            const DjVuTXT::Zone &zone,
861            const int WindowHeight );
862
863static void
864writeText( ByteStream & str_out,
865           const GUTF8String &textUTF8,
866           const DjVuTXT::ZoneType zlayer,
867           const GList<DjVuTXT::Zone> &children,
868           const int WindowHeight )
869{
870//  assert( txt->has_valid_zones() );
871//  DEBUG_MSG( "--zonetype=" << txt->page_zone.ztype << "\n" );
872
873  //  Beginning tags for missing layers
874  int layer=(int)zlayer;
875  //  Output the next layer
876  for(GPosition pos=children ; pos ; ++pos )
877  {
878    str_out.writestring(tolayer(layer,children[pos].ztype));
879    writeText( str_out,
880                textUTF8,
881                children[pos],
882                WindowHeight );
883  }
884  str_out.writestring(tolayer(layer,zlayer));
885}
886
887static void
888writeText( ByteStream & str_out,
889            const GUTF8String &textUTF8,
890            const DjVuTXT::Zone &zone,
891            const int WindowHeight )
892{
893//  DEBUG_MSG( "--zonetype=" << zone.ztype << "\n" );
894
895  const GUTF8String xindent(indent( 2 * zone.ztype + 2 ));
896  GPosition pos=zone.children;
897  // Build attribute string
898  if( ! pos )
899  {
900    GUTF8String coords;
901    coords.format("coords=\"%d,%d,%d,%d\"",
902      zone.rect.xmin, WindowHeight - 1 - zone.rect.ymin,
903      zone.rect.xmax, WindowHeight - 1 - zone.rect.ymax);
904    const int start=zone.text_start;
905    const int end=textUTF8.firstEndSpace(start,zone.text_length);
906    str_out.writestring(start_tag(zone.ztype,coords));
907    str_out.writestring(textUTF8.substr(start,end-start).toEscaped());
908    str_out.writestring(end_tag(zone.ztype));
909  } else
910  {
911    writeText(str_out,textUTF8,zone.ztype,zone.children,WindowHeight);
912  }
913}
914
915void
916DjVuTXT::writeText(ByteStream &str_out,const int height) const
917{
918  if(has_valid_zones())
919  {
920    ::writeText(str_out,textUTF8,DjVuTXT::PAGE,page_zone.children,height);
921  }else
922  {
923    str_out.writestring(start_tag(DjVuTXT::PAGE));
924    str_out.writestring(end_tag(DjVuTXT::PAGE));
925  }
926}
927
928void
929DjVuText::writeText(ByteStream &str_out,const int height) const
930{
931  if(txt)
932  {
933    txt->writeText(str_out,height);
934  }else
935  {
936    str_out.writestring("<"+GUTF8String(tags[DjVuTXT::PAGE])+"/>\n");
937  }
938   
939}
940GUTF8String
941DjVuTXT::get_xmlText(const int height) const
942{
943  GP<ByteStream> gbs(ByteStream::create());
944  ByteStream &bs=*gbs;
945  writeText(bs,height);
946  bs.seek(0L);
947  return bs.getAsUTF8();
948}
949
950GUTF8String
951DjVuText::get_xmlText(const int height) const
952{
953  GUTF8String retval;
954  if(txt)
955  {
956    retval=txt->get_xmlText(height);
957  }else
958  {
959    retval="<"+GUTF8String(tags[DjVuTXT::PAGE])+"/>\n";
960  }
961  return retval;
962}
963
964
965#ifdef HAVE_NAMESPACES
966}
967# ifndef NOT_USING_DJVU_NAMESPACE
968using namespace DJVU;
969# endif
970#endif
971
Note: See TracBrowser for help on using the repository browser.