source: trunk/libdjvu/ddjvuapi.cpp @ 101

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

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

File size: 82.2 KB
Line 
1/*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//C- */ 
54
55/* $Id: ddjvuapi.cpp,v 1.55 2006/02/21 16:10:29 docbill Exp $ */
56
57#ifdef HAVE_CONFIG_H
58# include "config.h"
59#endif
60#if NEED_GNUG_PRAGMAS
61# pragma implementation "ddjvuapi.h"
62#endif
63
64#include <stdlib.h>
65#include <stdio.h>
66#include <string.h>
67#include <ctype.h>
68#include <locale.h>
69
70#ifdef HAVE_NAMESPACES
71namespace DJVU {
72  struct ddjvu_context_s;
73  struct ddjvu_job_s;
74  struct ddjvu_document_s;
75  struct ddjvu_page_s;
76  struct ddjvu_format_s;
77  struct ddjvu_message_p;
78  struct ddjvu_thumbnail_p;
79  struct ddjvu_runnablejob_s;
80  struct ddjvu_printjob_s;
81  struct ddjvu_savejob_s;
82}
83using namespace DJVU;
84# define DJVUNS DJVU::
85#else
86# define DJVUNS /**/
87#endif
88
89#include "GException.h"
90#include "GSmartPointer.h"
91#include "GThreads.h"
92#include "GContainer.h"
93#include "ByteStream.h"
94#include "IFFByteStream.h"
95#include "BSByteStream.h"
96#include "GString.h"
97#include "GBitmap.h"
98#include "GPixmap.h"
99#include "GScaler.h"
100#include "DjVuPort.h"
101#include "DataPool.h"
102#include "DjVuInfo.h"
103#include "IW44Image.h"
104#include "DjVuImage.h"
105#include "DjVuFileCache.h"
106#include "DjVuDocument.h"
107#include "DjVuMessageLite.h"
108#include "DjVuMessage.h"
109#include "DjVmNav.h"
110#include "DjVuText.h"
111#include "DjVuAnno.h"
112#include "DjVuToPS.h"
113#include "DjVmDir.h"
114#include "DjVmDoc.h"
115
116
117#include "miniexp.h"
118#include "ddjvuapi.h"
119
120#if HAVE_STDINT_H
121# include <stdint.h>
122#else
123typedef unsigned short uint16_t;
124typedef unsigned int uint32_t;
125#endif
126
127
128// ----------------------------------------
129// Private structures
130
131
132struct DJVUNS ddjvu_message_p : public GPEnabled
133{
134  GNativeString tmp1;
135  GNativeString tmp2;
136  ddjvu_message_t p;
137  ddjvu_message_p() { memset(&p, 0, sizeof(p)); }
138};
139
140struct DJVUNS ddjvu_thumbnail_p : public GPEnabled
141{
142  ddjvu_document_t *document;
143  int pagenum;
144  GTArray<char> data;
145  GP<DataPool> pool;
146  static void callback(void *);
147}; 
148
149
150// ----------------------------------------
151// Context, Jobs, Document, Pages
152
153
154struct DJVUNS ddjvu_context_s : public GPEnabled
155{
156  GMonitor monitor;
157  GP<DjVuFileCache> cache;
158  GPList<ddjvu_message_p> mlist;
159  GP<ddjvu_message_p> mpeeked;
160  int uniqueid;
161  ddjvu_message_callback_t callbackfun;
162  void *callbackarg;
163};
164
165struct DJVUNS ddjvu_job_s : public DjVuPort
166{
167  GMonitor monitor;
168  void *userdata;
169  GP<ddjvu_context_s> myctx;
170  GP<ddjvu_document_s> mydoc;
171  virtual ~ddjvu_job_s();
172  // virtual port functions:
173  virtual bool inherits(const GUTF8String&);
174  virtual bool notify_error(const DjVuPort*, const GUTF8String&); 
175  virtual bool notify_status(const DjVuPort*, const GUTF8String&);
176  // default implementation of virtual job functions:
177  virtual ddjvu_status_t status() {return DDJVU_JOB_NOTSTARTED;}
178  virtual void release() {}
179  virtual void stop() {}
180};
181
182struct DJVUNS ddjvu_document_s : public ddjvu_job_s
183{
184  GP<DjVuDocument> doc;
185  GPMap<int,DataPool> streams;
186  GPMap<int,ddjvu_thumbnail_p> thumbnails;
187  int streamid;
188  bool fileflag;
189  bool urlflag;
190  bool docinfoflag;
191  bool pageinfoflag;
192  minivar_t protect;
193  // virtual job functions:
194  virtual ddjvu_status_t status();
195  virtual void release();
196  // virtual port functions:
197  virtual bool inherits(const GUTF8String&);
198  virtual bool notify_error(const DjVuPort*, const GUTF8String&); 
199  virtual bool notify_status(const DjVuPort*, const GUTF8String&);
200  virtual void notify_doc_flags_changed(const DjVuDocument*, long, long);
201  virtual void notify_file_flags_changed(const DjVuFile*, long, long);
202  virtual GP<DataPool> request_data(const DjVuPort*, const GURL&);
203};
204
205struct DJVUNS ddjvu_page_s : public ddjvu_job_s
206{
207  GP<DjVuImage> img;
208  ddjvu_job_t *job;
209  bool pageinfoflag;            // was the first m_pageinfo sent?
210  bool pagedoneflag;            // was the final m_pageinfo sent?
211  // virtual job functions:
212  virtual ddjvu_status_t status();
213  virtual void release();
214  // virtual port functions:
215  virtual bool inherits(const GUTF8String&);
216  virtual bool notify_error(const DjVuPort*, const GUTF8String&); 
217  virtual bool notify_status(const DjVuPort*, const GUTF8String&);
218  virtual void notify_file_flags_changed(const DjVuFile*, long, long);
219  virtual void notify_relayout(const class DjVuImage*);
220  virtual void notify_redisplay(const class DjVuImage*);
221  virtual void notify_chunk_done(const DjVuPort*, const GUTF8String &);
222  void sendmessages();
223};
224
225
226// ----------------------------------------
227// Helpers
228
229
230// Hack to increment counter
231static void 
232ref(GPEnabled *p)
233{
234  GPBase n(p);
235  char *gn = (char*)&n;
236  *(GPEnabled**)gn = 0;
237  n.assign(0);
238}
239
240// Hack to decrement counter
241static void 
242unref(GPEnabled *p)
243{
244  GPBase n;
245  char *gn = (char*)&n;
246  *(GPEnabled**)gn = p;
247  n.assign(0);
248}
249
250// Allocate strings
251static char *
252xstr(const char *s)
253{
254  int l = strlen(s);
255  char *p = (char*)malloc(l + 1);
256  if (p) 
257    {
258      strcpy(p, s);
259      p[l] = 0;
260    }
261  return p;
262}
263
264// Allocate strings
265static char *
266xstr(const GNativeString &n)
267{
268  return xstr( (const char*) n );
269}
270
271// Allocate strings
272static char *
273xstr(const GUTF8String &u)
274{
275  GNativeString n(u);
276  return xstr( n );
277}
278
279// Fill a message head
280static ddjvu_message_any_t
281xhead(ddjvu_message_tag_t tag,
282      ddjvu_context_t *context)
283{
284  ddjvu_message_any_t any;
285  any.tag = tag;
286  any.context = context;
287  any.document = 0;
288  any.page = 0;
289  any.job = 0;
290  return any;
291}
292static ddjvu_message_any_t
293xhead(ddjvu_message_tag_t tag,
294      ddjvu_job_t *job)
295{
296  ddjvu_message_any_t any;
297  any.tag = tag;
298  any.context = job->myctx;
299  any.document = job->mydoc;
300  any.page = 0;
301  any.job = job;
302  return any;
303}
304static ddjvu_message_any_t
305xhead(ddjvu_message_tag_t tag,
306      ddjvu_document_t *document)
307{
308  ddjvu_message_any_t any;
309  any.tag = tag;
310  any.context = document->myctx;
311  any.document = document;
312  any.page = 0;
313  any.job = document;
314  return any;
315}
316static ddjvu_message_any_t
317xhead(ddjvu_message_tag_t tag,
318      ddjvu_page_t *page)
319{
320  ddjvu_message_any_t any;
321  any.tag = tag;
322  any.context = page->myctx;
323  any.document = page->mydoc;
324  any.page = page;
325  any.job = page->job;
326  return any;
327}
328
329
330// ----------------------------------------
331// Context
332
333
334ddjvu_context_t *
335ddjvu_context_create(const char *programname)
336{
337  ddjvu_context_t *ctx = 0;
338  G_TRY
339    {
340      setlocale(LC_ALL,"");
341      if (programname)
342        djvu_programname(programname);
343      DjVuMessage::use_language();
344      DjVuMessageLite::create();
345      ctx = new ddjvu_context_s;
346      ref(ctx);
347      ctx->uniqueid = 0;
348      ctx->callbackfun = 0;
349      ctx->callbackarg = 0;
350      ctx->cache = DjVuFileCache::create();
351    }
352  G_CATCH_ALL
353    {
354      if (ctx)
355        unref(ctx);
356      ctx = 0;
357    }
358  G_ENDCATCH;
359  return ctx;
360}
361
362void 
363ddjvu_context_release(ddjvu_context_t *ctx)
364{
365  G_TRY
366    {
367      if (ctx)
368        unref(ctx);
369    }
370  G_CATCH_ALL
371    {
372    }
373  G_ENDCATCH;
374}
375
376
377// ----------------------------------------
378// Message helpers
379
380
381// post a new message
382static void
383msg_push(const ddjvu_message_any_t &head,
384         GP<ddjvu_message_p> msg = 0)
385{
386  ddjvu_context_t *ctx = head.context;
387  if (! msg) msg = new ddjvu_message_p;
388  msg->p.m_any = head; 
389  GMonitorLock lock(&ctx->monitor);
390  if (ctx->callbackfun) 
391    (*ctx->callbackfun)(ctx, ctx->callbackarg);
392  ctx->mlist.append(msg);
393  ctx->monitor.broadcast();
394}
395
396static void
397msg_push_nothrow(const ddjvu_message_any_t &head,
398                 GP<ddjvu_message_p> msg = 0)
399{
400  G_TRY
401    {
402      msg_push(head, msg);
403    }
404  G_CATCH_ALL
405    {
406    }
407  G_ENDCATCH;
408}
409
410// prepare error message from string
411static GP<ddjvu_message_p>
412msg_prep_error(GUTF8String message,
413               const char *function=0, 
414               const char *filename=0, 
415               int lineno=0)
416{
417  GP<ddjvu_message_p> p = new ddjvu_message_p;
418  p->p.m_error.message = 0;
419  p->p.m_error.function = function;
420  p->p.m_error.filename = filename;
421  p->p.m_error.lineno = lineno;
422  G_TRY
423    { 
424      p->tmp1 = DjVuMessageLite::LookUpUTF8(message);
425      p->p.m_error.message = (const char*)(p->tmp1);
426    }
427  G_CATCH_ALL
428    {
429    } 
430  G_ENDCATCH;
431  return p;
432}
433
434// prepare error message from exception
435static GP<ddjvu_message_p>
436msg_prep_error(const GException &ex,
437               const char *function=0, 
438               const char *filename=0, 
439               int lineno=0)
440{
441  GP<ddjvu_message_p> p = new ddjvu_message_p;
442  p->p.m_error.message = 0;
443  p->p.m_error.function = function;
444  p->p.m_error.filename = filename;
445  p->p.m_error.lineno = lineno;
446  G_TRY
447    { 
448      p->tmp1 = DjVuMessageLite::LookUpUTF8(ex.get_cause());
449      p->p.m_error.message = (const char*)(p->tmp1);
450      p->p.m_error.function = ex.get_function();
451      p->p.m_error.filename = ex.get_file();
452      p->p.m_error.lineno = ex.get_line();
453    }
454  G_CATCH_ALL
455    {
456    } 
457  G_ENDCATCH;
458  return p;
459}
460
461// prepare status message
462static GP<ddjvu_message_p>
463msg_prep_info(GUTF8String message)
464{
465  GP<ddjvu_message_p> p = new ddjvu_message_p;
466  p->tmp1 = DjVuMessageLite::LookUpUTF8(message); // i18n nonsense!
467  p->p.m_info.message = (const char*)(p->tmp1);
468  return p;
469}
470
471// ----------------------------------------
472
473
474#ifdef __GNUG__
475# define ERROR1(x, m) \
476    msg_push_nothrow(xhead(DDJVU_ERROR,x),\
477                     msg_prep_error(m,__func__,__FILE__,__LINE__))
478#else
479# define ERROR1(x, m) \
480    msg_push_nothrow(xhead(DDJVU_ERROR,x),\
481                     msg_prep_error(m,0,__FILE__,__LINE__))
482#endif
483
484
485// ----------------------------------------
486// Cache
487
488void
489ddjvu_cache_set_size(ddjvu_context_t *ctx,
490                     unsigned long cachesize)
491{
492  G_TRY
493    {
494      GMonitorLock lock(&ctx->monitor);
495      if (ctx->cache && cachesize>0)
496        ctx->cache->set_max_size(cachesize);
497    }
498  G_CATCH(ex) 
499    {
500      ERROR1(ctx, ex);
501    }
502  G_ENDCATCH;
503}
504
505DDJVUAPI unsigned long
506ddjvu_cache_get_size(ddjvu_context_t *ctx)
507{
508  G_TRY
509    {
510      GMonitorLock lock(&ctx->monitor);
511      if (ctx->cache)
512        return ctx->cache->get_max_size();
513    }
514  G_CATCH(ex) 
515    { 
516      ERROR1(ctx, ex);
517    }
518  G_ENDCATCH;
519  return 0;
520}
521
522void
523ddjvu_cache_clear(ddjvu_context_t *ctx)
524{
525  G_TRY
526    {
527      GMonitorLock lock(&ctx->monitor);
528      if (ctx->cache)
529      {
530        ctx->cache->clear();
531        return;
532      }
533    }
534  G_CATCH(ex)
535    {
536      ERROR1(ctx, ex);
537    }
538  G_ENDCATCH;
539 }
540
541
542// ----------------------------------------
543// Jobs
544
545ddjvu_job_s::~ddjvu_job_s()
546{
547  G_TRY
548    {
549      ddjvu_context_t *ctx = myctx;
550      if (!ctx) return;
551      GMonitorLock lock(&ctx->monitor);
552      GPosition p = ctx->mlist;
553      while (p) {
554        GPosition s = p; ++p;
555        if (ctx->mlist[s]->p.m_any.job == this ||
556            ctx->mlist[s]->p.m_any.document == this ||
557            ctx->mlist[s]->p.m_any.page == this )
558          ctx->mlist.del(s);
559      }
560    }
561  G_CATCH_ALL
562    {
563    }
564  G_ENDCATCH;
565}
566
567bool
568ddjvu_job_s::inherits(const GUTF8String &classname)
569{
570  return (classname == "ddjvu_job_s") 
571    || DjVuPort::inherits(classname);
572}
573
574bool 
575ddjvu_job_s::notify_error(const DjVuPort *, const GUTF8String &m)
576{
577  msg_push(xhead(DDJVU_ERROR, this), msg_prep_error(m));
578  return true;
579}
580
581bool 
582ddjvu_job_s::notify_status(const DjVuPort *p, const GUTF8String &m)
583{
584  msg_push(xhead(DDJVU_INFO, this), msg_prep_info(m));
585  return true;
586}
587
588
589void
590ddjvu_job_release(ddjvu_job_t *job)
591{
592  G_TRY
593    {
594      if (!job)
595        return;
596      job->release();
597      job->userdata = 0;
598      unref(job);
599    }
600  G_CATCH_ALL
601    {
602    }
603  G_ENDCATCH;
604}
605
606ddjvu_status_t
607ddjvu_job_status(ddjvu_job_t *job)
608{
609  G_TRY
610    {
611      if (! job)
612        return DDJVU_JOB_NOTSTARTED;
613      return job->status();
614    }
615  G_CATCH(ex)
616    {
617      ERROR1(job, ex);
618    }
619  G_ENDCATCH;
620  return DDJVU_JOB_FAILED;
621}
622
623void
624ddjvu_job_stop(ddjvu_job_t *job)
625{
626  G_TRY
627    {
628      if (job)
629        job->stop();
630    }
631  G_CATCH(ex)
632    {
633      ERROR1(job, ex);
634    }
635  G_ENDCATCH;
636}
637
638void
639ddjvu_job_set_user_data(ddjvu_job_t *job, void *userdata)
640{
641  if (job)
642    job->userdata = userdata;
643}
644
645void *
646ddjvu_job_get_user_data(ddjvu_job_t *job)
647{
648  if (job)
649    return job->userdata;
650  return 0;
651}
652
653
654// ----------------------------------------
655// Message queue
656
657
658ddjvu_message_t *
659ddjvu_message_peek(ddjvu_context_t *ctx)
660{
661  G_TRY
662    {
663      GMonitorLock lock(&ctx->monitor);
664      if (ctx->mpeeked)
665        return &ctx->mpeeked->p;       
666      GPosition p = ctx->mlist;
667      if (! p)
668        return 0;
669      ctx->mpeeked = ctx->mlist[p];
670      ctx->mlist.del(p);
671      return &ctx->mpeeked->p;       
672    }
673  G_CATCH_ALL
674    {
675    }
676  G_ENDCATCH;
677  return 0;
678}
679
680ddjvu_message_t *
681ddjvu_message_wait(ddjvu_context_t *ctx)
682{
683  G_TRY
684    {
685      GMonitorLock lock(&ctx->monitor);
686      if (ctx->mpeeked)
687        return &ctx->mpeeked->p;       
688      while (! ctx->mlist.size())
689        ctx->monitor.wait();
690      GPosition p = ctx->mlist;
691      if (! p)
692        return 0;
693      ctx->mpeeked = ctx->mlist[p];
694      ctx->mlist.del(p);
695      return &ctx->mpeeked->p;       
696    }
697  G_CATCH_ALL
698    {
699    }
700  G_ENDCATCH;
701  return 0;
702}
703
704void
705ddjvu_message_pop(ddjvu_context_t *ctx)
706{
707  G_TRY
708    {
709      GMonitorLock lock(&ctx->monitor);
710      ctx->mpeeked = 0;
711    }
712  G_CATCH_ALL
713    {
714    }
715  G_ENDCATCH;
716}
717
718void
719ddjvu_message_set_callback(ddjvu_context_t *ctx,
720                           ddjvu_message_callback_t callback,
721                           void *closure)
722{
723  GMonitorLock lock(&ctx->monitor);
724  ctx->callbackfun = callback;
725  ctx->callbackarg = closure;
726}
727
728
729// ----------------------------------------
730// Document callbacks
731
732
733void
734ddjvu_document_s::release()
735{
736  GPosition p;
737  GMonitorLock lock(&monitor);
738  doc = 0;
739  for (p=thumbnails; p; ++p)
740    {
741      ddjvu_thumbnail_p *thumb = thumbnails[p];
742      if (thumb->pool)
743        thumb->pool->del_trigger(ddjvu_thumbnail_p::callback, (void*)thumb);
744    }
745  for (p = streams; p; ++p)
746    streams[p]->stop();
747}
748
749ddjvu_status_t
750ddjvu_document_s::status()
751{
752  if (!doc)
753    return DDJVU_JOB_NOTSTARTED;
754  long flags = doc->get_doc_flags();
755  if (flags & DjVuDocument::DOC_INIT_OK)
756    return DDJVU_JOB_OK;
757  else if (flags & DjVuDocument::DOC_INIT_FAILED)
758    return DDJVU_JOB_FAILED;
759  return DDJVU_JOB_STARTED;
760}
761
762bool
763ddjvu_document_s::inherits(const GUTF8String &classname)
764{
765  return (classname == "ddjvu_document_s")
766    || ddjvu_job_s::inherits(classname);
767}
768
769bool 
770ddjvu_document_s::notify_error(const DjVuPort *, const GUTF8String &m)
771{
772  if (!doc) return false;
773  msg_push(xhead(DDJVU_ERROR, this), msg_prep_error(m));
774  return true;
775}
776 
777bool 
778ddjvu_document_s::notify_status(const DjVuPort *p, const GUTF8String &m)
779{
780  if (!doc) return false;
781  msg_push(xhead(DDJVU_INFO, this), msg_prep_info(m));
782  return true;
783}
784
785void 
786ddjvu_document_s::notify_doc_flags_changed(const DjVuDocument *, long, long)
787{
788  GMonitorLock lock(&monitor);
789  if (docinfoflag || !doc) return;
790  long flags = doc->get_doc_flags();
791  if ((flags & DjVuDocument::DOC_INIT_OK) ||
792      (flags & DjVuDocument::DOC_INIT_FAILED) )
793  {
794    msg_push(xhead(DDJVU_DOCINFO, this));
795    docinfoflag = true;
796  }
797}
798
799void 
800ddjvu_document_s::notify_file_flags_changed(const DjVuFile*, long s, long)
801{
802  if (pageinfoflag && !fileflag)
803    if (s & DjVuFile::ALL_DATA_PRESENT)
804      msg_push(xhead(DDJVU_PAGEINFO, this));
805}
806
807GP<DataPool> 
808ddjvu_document_s::request_data(const DjVuPort *p, const GURL &url)
809{
810  GMonitorLock lock(&monitor);
811  GP<DataPool> pool;
812  if (fileflag)
813    {
814      if (doc && url.is_local_file_url())
815        return DataPool::create(url);
816    }
817  else if (doc)
818    {
819      if (++streamid > 0)
820        streams[streamid] = pool = DataPool::create();
821      else
822        pool = streams[(streamid = 0)];
823      // build message
824      GP<ddjvu_message_p> p = new ddjvu_message_p;
825      p->p.m_newstream.streamid = streamid;
826      // Note: the following line try to restore
827      //       the bytes stored in the djvu file
828      //       despite LT's i18n and gurl classes.
829      p->tmp1 = (const char*)url.fname(); 
830      p->p.m_newstream.name = (const char*)(p->tmp1);
831      p->p.m_newstream.url = 0;
832      if (urlflag)
833        {
834          // Should be urlencoded.
835          p->tmp2 = (const char*)url.get_string();
836          p->p.m_newstream.url = (const char*)(p->tmp2);
837        }
838      msg_push(xhead(DDJVU_NEWSTREAM, this), p);
839    }
840  return pool;
841}
842
843
844// ----------------------------------------
845// Documents
846
847
848ddjvu_document_t *
849ddjvu_document_create(ddjvu_context_t *ctx,
850                      const char *url,
851                      int cache)
852{
853  ddjvu_document_t *d = 0;
854  G_TRY
855    {
856      DjVuFileCache *xcache = ctx->cache;
857      if (! cache) xcache = 0;
858      d = new ddjvu_document_s;
859      ref(d);
860      GMonitorLock lock(&d->monitor);
861      d->streams[0] = DataPool::create();
862      d->streamid = -1;
863      d->fileflag = false;
864      d->docinfoflag = false;
865      d->pageinfoflag = false;
866      d->myctx = ctx;
867      d->mydoc = 0;
868      d->userdata = 0;
869      d->doc = DjVuDocument::create_noinit();
870      if (url)
871        {
872          GURL gurl = GUTF8String(url);
873          d->urlflag = true;
874          d->doc->start_init(gurl, d, xcache);
875        }
876      else
877        {
878          GUTF8String s;
879          s.format("ddjvu:///doc%d/index.djvu", ++(ctx->uniqueid));;
880          GURL gurl = s;
881          d->urlflag = false;
882          d->doc->start_init(gurl, d, xcache);
883        }
884    }
885  G_CATCH(ex)
886    {
887      if (d) 
888        unref(d);
889      d = 0;
890      ERROR1(ctx, ex);
891    }
892  G_ENDCATCH;
893  return d;
894}
895
896ddjvu_document_t *
897ddjvu_document_create_by_filename(ddjvu_context_t *ctx,
898                                  const char *filename,
899                                  int cache)
900{
901  ddjvu_document_t *d = 0;
902  G_TRY
903    {
904      DjVuFileCache *xcache = ctx->cache;
905      if (! cache) xcache = 0;
906      GURL gurl = GURL::Filename::UTF8(filename);
907      d = new ddjvu_document_s;
908      ref(d);
909      GMonitorLock lock(&d->monitor);
910      d->streamid = -1;
911      d->fileflag = true;
912      d->pageinfoflag = false;
913      d->urlflag = false;
914      d->docinfoflag = false;
915      d->myctx = ctx;
916      d->mydoc = 0;
917      d->userdata = 0;
918      d->doc = DjVuDocument::create_noinit();
919      d->doc->start_init(gurl, d, xcache);
920    }
921  G_CATCH(ex)
922    {
923      if (d)
924        unref(d);
925      d = 0;
926      ERROR1(ctx, ex);
927    }
928  G_ENDCATCH;
929  return d;
930}
931
932ddjvu_job_t *
933ddjvu_document_job(ddjvu_document_t *document)
934{
935  return document;
936}
937
938
939// ----------------------------------------
940// Streams
941
942
943void
944ddjvu_stream_write(ddjvu_document_t *doc,
945                   int streamid,
946                   const char *data,
947                   unsigned long datalen )
948{
949  G_TRY
950    {
951      GP<DataPool> pool;
952      { 
953        GMonitorLock lock(&doc->monitor); 
954        GPosition p = doc->streams.contains(streamid);
955        if (p) pool = doc->streams[p];
956      }
957      if (! pool)
958        G_THROW("Unknown stream ID");
959      if (datalen > 0)
960        pool->add_data(data, datalen);
961    }
962  G_CATCH(ex)
963    {
964      ERROR1(doc,ex);
965    }
966  G_ENDCATCH;
967}
968
969void
970ddjvu_stream_close(ddjvu_document_t *doc,
971                   int streamid,
972                   int stop )
973{
974  G_TRY
975    {
976      GP<DataPool> pool;
977      { 
978        GMonitorLock lock(&doc->monitor); 
979        GPosition p = doc->streams.contains(streamid);
980        if (p) pool = doc->streams[p];
981      }
982      if (! pool)
983        G_THROW("Unknown stream ID");
984      if (stop)
985        pool->stop(true);
986      pool->set_eof();
987    }
988  G_CATCH(ex)
989    {
990      ERROR1(doc, ex);
991    }
992  G_ENDCATCH;
993}
994
995
996// ----------------------------------------
997// Document queries
998
999
1000ddjvu_document_type_t
1001ddjvu_document_get_type(ddjvu_document_t *document)
1002{
1003  G_TRY
1004    {
1005      DjVuDocument *doc = document->doc;
1006      if (doc)
1007        {
1008          switch (doc->get_doc_type())
1009            {
1010            case DjVuDocument::OLD_BUNDLED:
1011              return DDJVU_DOCTYPE_OLD_BUNDLED;
1012            case DjVuDocument::OLD_INDEXED:
1013              return DDJVU_DOCTYPE_OLD_INDEXED;
1014            case DjVuDocument::BUNDLED:
1015              return DDJVU_DOCTYPE_BUNDLED;
1016            case DjVuDocument::INDIRECT:
1017              return DDJVU_DOCTYPE_INDIRECT;
1018            case DjVuDocument::SINGLE_PAGE:
1019              return DDJVU_DOCTYPE_SINGLEPAGE;
1020            default:
1021              break;
1022            }
1023        }
1024    }
1025  G_CATCH(ex)
1026    {
1027      ERROR1(document,ex);
1028    }
1029  G_ENDCATCH;
1030  return DDJVU_DOCTYPE_UNKNOWN;
1031}
1032
1033int
1034ddjvu_document_get_pagenum(ddjvu_document_t *document)
1035{
1036  G_TRY
1037    {
1038      DjVuDocument *doc = document->doc;
1039      if (doc)
1040        return doc->get_pages_num();
1041    }
1042  G_CATCH(ex)
1043    {
1044      ERROR1(document,ex);
1045    }
1046  G_ENDCATCH;
1047  return 1;
1048}
1049
1050
1051int
1052ddjvu_document_get_filenum(ddjvu_document_t *document)
1053{
1054  G_TRY
1055    {
1056      DjVuDocument *doc = document->doc;
1057      if (! (doc && doc->is_init_ok()))
1058        return 0;
1059      GP<DjVmDir> dir = doc->get_djvm_dir();
1060      if (dir)
1061        return dir->get_files_num();
1062    }
1063  G_CATCH(ex)
1064    {
1065      ERROR1(document,ex);
1066    }
1067  G_ENDCATCH;
1068  return 1;
1069}
1070
1071ddjvu_status_t
1072ddjvu_document_get_fileinfo(ddjvu_document_t *document, int fileno, 
1073                            ddjvu_fileinfo_t *info)
1074{
1075  G_TRY
1076    {
1077      memset(info, 0, sizeof(ddjvu_fileinfo_t));
1078      DjVuDocument *doc = document->doc;
1079      if (! doc)
1080        return DDJVU_JOB_NOTSTARTED;
1081      if (! doc->is_init_ok())
1082        return document->status();
1083      GP<DjVmDir> dir = doc->get_djvm_dir();
1084      GP<DjVmDir::File> file = dir->pos_to_file(fileno, &info->pageno);
1085      if (! file)
1086        G_THROW("Illegal file number");
1087      if (file->is_page())
1088        info->type = 'P';
1089      else if (file->is_thumbnails())
1090        info->type = 'T';
1091      else if (file->is_shared_anno())
1092        info->type = 'S';
1093      else
1094        info->type = 'I';
1095      info->size = file->size;
1096      info->id = file->get_load_name();
1097      info->name = file->get_save_name();
1098      info->title = file->get_title();
1099      return DDJVU_JOB_OK;
1100    }
1101  G_CATCH(ex)
1102    {
1103      ERROR1(document,ex);
1104    }
1105  G_ENDCATCH;
1106  return DDJVU_JOB_FAILED;
1107}
1108
1109int
1110ddjvu_document_search_pageno(ddjvu_document_t *document, const char *name)
1111{
1112  G_TRY
1113    {
1114      DjVuDocument *doc = document->doc;
1115      if (! (doc && doc->is_init_ok()))
1116        return -1;
1117      GP<DjVmDir> dir = doc->get_djvm_dir();
1118      if (! dir)
1119        return 0;
1120      GP<DjVmDir::File> file;
1121      if (! (file = dir->id_to_file(GUTF8String(name))))
1122        if (! (file = dir->name_to_file(GUTF8String(name))))
1123          if (! (file = dir->title_to_file(GUTF8String(name))))
1124            {
1125              char *edata=0;
1126              long int p = strtol(name, &edata, 10);
1127              if (edata!=name && !*edata && p>=0)
1128                file = dir->page_to_file(p);
1129            }
1130      if (file)
1131        {
1132          int pageno = -1;
1133          int fileno = dir->get_file_pos(file);
1134          if (dir->pos_to_file(fileno, &pageno))
1135            return pageno;
1136        }
1137    }
1138  G_CATCH(ex)
1139    {
1140      ERROR1(document,ex);
1141    }
1142  G_ENDCATCH;
1143  return -1;
1144}
1145
1146
1147int 
1148ddjvu_document_check_pagedata(ddjvu_document_t *document, int pageno)
1149{
1150  G_TRY
1151    {
1152      document->pageinfoflag = true;
1153      DjVuDocument *doc = document->doc;
1154      if (doc && doc->is_init_ok())
1155        {
1156          GP<DjVuFile> file;
1157          if (doc->get_doc_type()==DjVuDocument::INDIRECT)
1158            file = doc->get_djvu_file(pageno, true);
1159          else
1160            file = doc->get_djvu_file(pageno, false);           
1161          if (file && file->is_all_data_present())
1162            return 1;
1163        }
1164    }
1165  G_CATCH(ex)
1166    {
1167      ERROR1(document,ex);
1168    }
1169  G_ENDCATCH;
1170  return 0;
1171}
1172
1173
1174ddjvu_status_t
1175ddjvu_document_get_pageinfo(ddjvu_document_t *document, int pageno, 
1176                            ddjvu_pageinfo_t *pageinfo)
1177{
1178  G_TRY
1179    {
1180      DjVuDocument *doc = document->doc;
1181      if (doc)
1182        {
1183          document->pageinfoflag = true;
1184          GP<DjVuFile> file = doc->get_djvu_file(pageno);
1185          if (! file || ! file->is_data_present() )
1186            return DDJVU_JOB_STARTED;
1187          const GP<ByteStream> pbs(file->get_djvu_bytestream(false, false));
1188          const GP<IFFByteStream> iff(IFFByteStream::create(pbs));
1189          GUTF8String chkid;
1190          if (iff->get_chunk(chkid))
1191            {
1192              if (chkid == "FORM:DJVU")
1193                {
1194                  while (iff->get_chunk(chkid) && chkid!="INFO")
1195                    iff->close_chunk();
1196                  if (chkid == "INFO")
1197                    {
1198                      GP<ByteStream> gbs = iff->get_bytestream();
1199                      GP<DjVuInfo> info=DjVuInfo::create();
1200                      info->decode(*gbs);
1201                      int rot = info->orientation;
1202                      if (pageinfo)
1203                        {
1204                          pageinfo->width = 
1205                            (rot&1) ? info->height : info->width;
1206                          pageinfo->height = 
1207                            (rot&1) ? info->width : info->height;
1208                          pageinfo->dpi = info->dpi;
1209                        }
1210                      return DDJVU_JOB_OK;
1211                    }
1212                }
1213              else if (chkid == "FORM:BM44" || chkid == "FORM:PM44")
1214                {
1215                  while (iff->get_chunk(chkid) && 
1216                         chkid!="BM44" && chkid!="PM44")
1217                    iff->close_chunk();
1218                  if (chkid=="BM44" || chkid=="PM44")
1219                    {
1220                      GP<ByteStream> gbs = iff->get_bytestream();
1221                      if (gbs->read8() == 0)
1222                        {
1223                          gbs->read8();
1224                          gbs->read8();
1225                          gbs->read8();
1226                          unsigned char xhi = gbs->read8();
1227                          unsigned char xlo = gbs->read8();
1228                          unsigned char yhi = gbs->read8();
1229                          unsigned char ylo = gbs->read8();
1230                          if (pageinfo)
1231                            {
1232                              pageinfo->width = (xhi<<8)+xlo;
1233                              pageinfo->height = (yhi<<8)+ylo;
1234                              pageinfo->dpi = 100;
1235                            }
1236                          return DDJVU_JOB_OK;
1237                        }
1238                    }
1239                }
1240            }
1241        }
1242    }
1243  G_CATCH(ex)
1244    {
1245      ERROR1(document, ex);
1246    }
1247  G_ENDCATCH;
1248  return DDJVU_JOB_FAILED;
1249}
1250
1251
1252// ----------------------------------------
1253// Page
1254
1255static ddjvu_page_t *
1256ddjvu_page_create(ddjvu_document_t *document, ddjvu_job_t *job,
1257                  const char *pageid, int pageno)
1258{
1259  ddjvu_page_t *p = 0;
1260  G_TRY
1261    {
1262      DjVuDocument *doc = document->doc;
1263      if (! doc) return 0;
1264      p = new ddjvu_page_s;
1265      ref(p);
1266      GMonitorLock lock(&p->monitor);
1267      p->myctx = document->myctx;
1268      p->mydoc = document;
1269      p->userdata = 0;
1270      p->pageinfoflag = false;
1271      p->pagedoneflag = false;
1272      p->job = job = ((job) ? job : p);
1273      if (pageid)
1274        p->img = doc->get_page(GNativeString(pageid), false, job);
1275      else
1276        p->img = doc->get_page(pageno, false, job);
1277    }
1278  G_CATCH(ex)
1279    {
1280      if (p)
1281        unref(p);
1282      p = 0;
1283      ERROR1(document, ex);
1284    }
1285  G_ENDCATCH;
1286  return p;
1287}
1288
1289ddjvu_page_t *
1290ddjvu_page_create_by_pageno(ddjvu_document_t *document, int pageno)
1291{
1292  return ddjvu_page_create(document, 0, 0, pageno);
1293}
1294
1295ddjvu_page_t *
1296ddjvu_page_create_by_pageid(ddjvu_document_t *document, const char *pageid)
1297{
1298  return ddjvu_page_create(document, 0, pageid, 0);
1299}
1300
1301ddjvu_job_t *
1302ddjvu_page_job(ddjvu_page_t *page)
1303{
1304  return page;
1305}
1306
1307
1308// ----------------------------------------
1309// Page callbacks
1310
1311void
1312ddjvu_page_s::release()
1313{
1314  img = 0;
1315}
1316
1317ddjvu_status_t
1318ddjvu_page_s::status()
1319{
1320  if (! img)
1321    return DDJVU_JOB_NOTSTARTED;       
1322  DjVuFile *file = img->get_djvu_file();
1323  if (! file)
1324    return DDJVU_JOB_NOTSTARTED;
1325  else if (file->is_decode_stopped())
1326    return DDJVU_JOB_STOPPED;
1327  else if (file->is_decode_failed())
1328    return DDJVU_JOB_FAILED;
1329  else if (file->is_decode_ok())
1330    return DDJVU_JOB_OK;
1331  else if (file->is_decoding())
1332    return DDJVU_JOB_STARTED;
1333  return DDJVU_JOB_NOTSTARTED;
1334}
1335
1336bool
1337ddjvu_page_s::inherits(const GUTF8String &classname)
1338{
1339  return (classname == "ddjvu_page_s")
1340    || ddjvu_job_s::inherits(classname);
1341}
1342
1343bool 
1344ddjvu_page_s::notify_error(const DjVuPort *, const GUTF8String &m)
1345{
1346  if (!img) return false;
1347  msg_push(xhead(DDJVU_ERROR, this), msg_prep_error(m));
1348  return true;
1349}
1350 
1351bool 
1352ddjvu_page_s::notify_status(const DjVuPort *p, const GUTF8String &m)
1353{
1354  if (!img) return false;
1355  msg_push(xhead(DDJVU_INFO, this), msg_prep_info(m));
1356  return true;
1357}
1358
1359void 
1360ddjvu_page_s::notify_file_flags_changed(const DjVuFile *sender, long, long)
1361{
1362  GMonitorLock lock(&monitor);
1363  if (!img) return;
1364  DjVuFile *file = img->get_djvu_file();
1365  if (file==0 || file!=sender) return;
1366  long flags = file->get_flags();
1367  if ((flags & DjVuFile::DECODE_OK) ||
1368      (flags & DjVuFile::DECODE_FAILED) ||
1369      (flags & DjVuFile::DECODE_STOPPED) )
1370    {
1371      if (pagedoneflag) return;
1372      msg_push(xhead(DDJVU_PAGEINFO, this));
1373      pageinfoflag = pagedoneflag = true;
1374    }
1375}
1376
1377void 
1378ddjvu_page_s::notify_relayout(const DjVuImage *dimg)
1379{
1380  GMonitorLock lock(&monitor);
1381  if (img && !pageinfoflag)
1382    {
1383      msg_push(xhead(DDJVU_PAGEINFO, this));
1384      msg_push(xhead(DDJVU_RELAYOUT, this));
1385      pageinfoflag = true;
1386    }
1387}
1388
1389void 
1390ddjvu_page_s::notify_redisplay(const DjVuImage *dimg)
1391{
1392  GMonitorLock lock(&monitor);
1393  if (img && !pageinfoflag)
1394    {
1395      msg_push(xhead(DDJVU_PAGEINFO, this));
1396      msg_push(xhead(DDJVU_RELAYOUT, this));
1397      pageinfoflag = true;
1398    }
1399  if (img && pageinfoflag)
1400    msg_push(xhead(DDJVU_REDISPLAY, this));
1401}
1402
1403void 
1404ddjvu_page_s::notify_chunk_done(const DjVuPort*, const GUTF8String &name)
1405{
1406  GMonitorLock lock(&monitor);
1407  if (! img) return;
1408  GP<ddjvu_message_p> p = new ddjvu_message_p;
1409  p->tmp1 = name;
1410  p->p.m_chunk.chunkid = (const char*)(p->tmp1);
1411  msg_push(xhead(DDJVU_CHUNK,this), p);
1412}
1413
1414
1415// ----------------------------------------
1416// Page queries
1417
1418int
1419ddjvu_page_get_width(ddjvu_page_t *page)
1420{
1421  G_TRY
1422    {
1423      if (page && page->img)
1424        return page->img->get_width();
1425    }
1426  G_CATCH(ex)
1427    {
1428      ERROR1(page, ex);
1429    }
1430  G_ENDCATCH;
1431  return 0;
1432}
1433
1434int
1435ddjvu_page_get_height(ddjvu_page_t *page)
1436{
1437  G_TRY
1438    {
1439      if (page && page->img)
1440        return page->img->get_height();
1441    }
1442  G_CATCH(ex)
1443    {
1444      ERROR1(page, ex);
1445    }
1446  G_ENDCATCH;
1447  return 0;
1448}
1449
1450int
1451ddjvu_page_get_resolution(ddjvu_page_t *page)
1452{
1453  G_TRY
1454    {
1455      if (page && page->img)
1456        return page->img->get_dpi();
1457    }
1458  G_CATCH(ex)
1459    {
1460      ERROR1(page, ex);
1461    }
1462  G_ENDCATCH;
1463  return 0;
1464}
1465
1466double
1467ddjvu_page_get_gamma(ddjvu_page_t *page)
1468{
1469  G_TRY
1470    {
1471      if (page && page->img)
1472        return page->img->get_gamma();
1473    }
1474  G_CATCH(ex)
1475    {
1476      ERROR1(page, ex);
1477    }
1478  G_ENDCATCH;
1479  return 2.2;
1480}
1481
1482int
1483ddjvu_page_get_version(ddjvu_page_t *page)
1484{
1485  G_TRY
1486    {
1487      if (page && page->img)
1488        return page->img->get_version();
1489    }
1490  G_CATCH(ex)
1491    {
1492      ERROR1(page, ex);
1493    }
1494  G_ENDCATCH;
1495  return DJVUVERSION;
1496}
1497
1498int
1499ddjvu_code_get_version(void)
1500{
1501  return DJVUVERSION;
1502}
1503
1504ddjvu_page_type_t
1505ddjvu_page_get_type(ddjvu_page_t *page)
1506{
1507  G_TRY
1508    {
1509      if (! (page && page->img))
1510        return DDJVU_PAGETYPE_UNKNOWN;
1511      else if (page->img->is_legal_bilevel())
1512        return DDJVU_PAGETYPE_BITONAL;
1513      else if (page->img->is_legal_photo())
1514        return DDJVU_PAGETYPE_PHOTO;
1515      else if (page->img->is_legal_compound())
1516        return DDJVU_PAGETYPE_COMPOUND;
1517    }
1518  G_CATCH(ex)
1519    {
1520      ERROR1(page, ex);
1521    }
1522  G_ENDCATCH;
1523  return DDJVU_PAGETYPE_UNKNOWN;
1524}
1525
1526char *
1527ddjvu_page_get_short_description(ddjvu_page_t *page)
1528{
1529  G_TRY
1530    {
1531      if (page && page->img)
1532        {
1533          const char *desc = page->img->get_short_description();
1534          return xstr(DjVuMessageLite::LookUpUTF8(desc));
1535        }
1536    }
1537  G_CATCH(ex)
1538    {
1539      ERROR1(page, ex);
1540    }
1541  G_ENDCATCH;
1542  return 0;
1543}
1544
1545char *
1546ddjvu_page_get_long_description(ddjvu_page_t *page)
1547{
1548  G_TRY
1549    {
1550      if (page && page->img)
1551        {
1552          const char *desc = page->img->get_long_description();
1553          return xstr(DjVuMessageLite::LookUpUTF8(desc));
1554        }
1555    }
1556  G_CATCH(ex)
1557    {
1558      ERROR1(page, ex);
1559    }
1560  G_ENDCATCH;
1561  return 0;
1562}
1563
1564
1565// ----------------------------------------
1566// Rotations
1567
1568void
1569ddjvu_page_set_rotation(ddjvu_page_t *page,
1570                        ddjvu_page_rotation_t rot)
1571{
1572  G_TRY
1573    {
1574      switch(rot)
1575        {
1576        case DDJVU_ROTATE_0:
1577        case DDJVU_ROTATE_90:
1578        case DDJVU_ROTATE_180:
1579        case DDJVU_ROTATE_270:
1580          if (page && page->img && page->img->get_info())
1581            page->img->set_rotate((int)rot);
1582          break;
1583        default:
1584          G_THROW("Illegal ddjvu rotation code");
1585          break;
1586        }
1587    }
1588  G_CATCH(ex)
1589    {
1590      ERROR1(page, ex);
1591    }
1592  G_ENDCATCH;
1593}
1594
1595ddjvu_page_rotation_t
1596ddjvu_page_get_rotation(ddjvu_page_t *page)
1597{
1598  ddjvu_page_rotation_t rot = DDJVU_ROTATE_0;
1599  G_TRY
1600    {
1601      if (page && page->img)
1602        rot = (ddjvu_page_rotation_t)(page->img->get_rotate() & 3);
1603    }
1604  G_CATCH(ex)
1605    {
1606      ERROR1(page, ex);
1607    }
1608  G_ENDCATCH;
1609  return rot;
1610}
1611
1612ddjvu_page_rotation_t
1613ddjvu_page_get_initial_rotation(ddjvu_page_t *page)
1614{
1615  ddjvu_page_rotation_t rot = DDJVU_ROTATE_0;
1616  G_TRY
1617    {
1618      GP<DjVuInfo> info;
1619      if (page && page->img)
1620        info = page->img->get_info();
1621      if (info)
1622        rot = (ddjvu_page_rotation_t)(info->orientation & 3);
1623    }
1624  G_CATCH(ex)
1625    {
1626      ERROR1(page, ex);
1627    }
1628  G_ENDCATCH;
1629  return rot;
1630}
1631
1632
1633// ----------------------------------------
1634// Rectangles
1635
1636static void
1637rect2grect(const ddjvu_rect_t *r, GRect &g)
1638{
1639  g.xmin = r->x;
1640  g.ymin = r->y;
1641  g.xmax = r->x + r->w;
1642  g.ymax = r->y + r->h;
1643}
1644
1645static void
1646grect2rect(const GRect &g, ddjvu_rect_t *r)
1647{
1648  if (g.isempty())
1649    {
1650      r->x = r->y = 0;
1651      r->w = r->h = 0;
1652    }
1653  else
1654    {
1655      r->x = g.xmin;
1656      r->y = g.ymin;
1657      r->w = g.width();
1658      r->h = g.height();
1659    }
1660}
1661
1662ddjvu_rectmapper_t *
1663ddjvu_rectmapper_create(ddjvu_rect_t *input, ddjvu_rect_t *output)
1664{
1665  GRect ginput, goutput;
1666  rect2grect(input, ginput);
1667  rect2grect(output, goutput);
1668  GRectMapper *mapper = new GRectMapper;
1669  if (!ginput.isempty())
1670    mapper->set_input(ginput);
1671  if (!goutput.isempty())
1672    mapper->set_output(goutput);
1673  return (ddjvu_rectmapper_t*)mapper;
1674}
1675
1676void
1677ddjvu_rectmapper_modify(ddjvu_rectmapper_t *mapper,
1678                        int rotation, int mirrorx, int mirrory)
1679{
1680  GRectMapper *gmapper = (GRectMapper*)mapper;
1681  if (! gmapper) return;
1682  gmapper->rotate(rotation);
1683  if (mirrorx & 1)
1684    gmapper->mirrorx();
1685  if (mirrory & 1)
1686    gmapper->mirrory();
1687}
1688
1689void 
1690ddjvu_rectmapper_release(ddjvu_rectmapper_t *mapper)
1691{
1692  GRectMapper *gmapper = (GRectMapper*)mapper;
1693  if (! gmapper) return;
1694  delete gmapper;
1695}
1696
1697void 
1698ddjvu_map_point(ddjvu_rectmapper_t *mapper, int *x, int *y)
1699{
1700  GRectMapper *gmapper = (GRectMapper*)mapper;
1701  if (! gmapper) return;
1702  gmapper->map(*x,*y);
1703}
1704
1705void 
1706ddjvu_map_rect(ddjvu_rectmapper_t *mapper, ddjvu_rect_t *rect)
1707{
1708  GRectMapper *gmapper = (GRectMapper*)mapper;
1709  if (! gmapper) return;
1710  GRect grect;
1711  rect2grect(rect,grect);
1712  gmapper->map(grect);
1713  grect2rect(grect,rect);
1714}
1715
1716void 
1717ddjvu_unmap_point(ddjvu_rectmapper_t *mapper, int *x, int *y)
1718{
1719  GRectMapper *gmapper = (GRectMapper*)mapper;
1720  if (! gmapper) return;
1721  gmapper->unmap(*x,*y);
1722}
1723
1724void 
1725ddjvu_unmap_rect(ddjvu_rectmapper_t *mapper, ddjvu_rect_t *rect)
1726{
1727  GRectMapper *gmapper = (GRectMapper*)mapper;
1728  if (! gmapper) return;
1729  GRect grect;
1730  rect2grect(rect,grect);
1731  gmapper->unmap(grect);
1732  grect2rect(grect,rect);
1733}
1734
1735
1736// ----------------------------------------
1737// Render
1738
1739struct DJVUNS ddjvu_format_s
1740{
1741  ddjvu_format_style_t style;
1742  uint32_t rgb[3][256];
1743  uint32_t palette[6*6*6];
1744  uint32_t xorval;
1745  double gamma;
1746  char ditherbits;
1747  bool rtoptobottom;
1748  bool ytoptobottom;
1749};
1750
1751static ddjvu_format_t *
1752fmt_error(ddjvu_format_t *fmt)
1753{
1754  delete fmt;
1755  return 0;
1756}
1757
1758ddjvu_format_t *
1759ddjvu_format_create(ddjvu_format_style_t style,
1760                    int nargs, unsigned int *args)
1761{
1762  ddjvu_format_t *fmt = new ddjvu_format_s;
1763  memset(fmt, 0, sizeof(ddjvu_format_t));
1764  fmt->style = style; 
1765  fmt->rtoptobottom = false;
1766  fmt->ytoptobottom = false;
1767  fmt->gamma = 2.2;
1768  // Ditherbits
1769  fmt->ditherbits = 32;
1770  if (style==DDJVU_FORMAT_RGBMASK16)
1771    fmt->ditherbits = 16;
1772  else if (style==DDJVU_FORMAT_PALETTE8)
1773    fmt->ditherbits = 8;
1774  else if (style==DDJVU_FORMAT_MSBTOLSB || style==DDJVU_FORMAT_LSBTOMSB)
1775    fmt->ditherbits = 1;
1776  // Args
1777  switch(style)
1778    {
1779    case DDJVU_FORMAT_RGBMASK16:
1780    case DDJVU_FORMAT_RGBMASK32: 
1781      {
1782        if (sizeof(uint16_t)!=2 || sizeof(uint32_t)!=4)
1783          return fmt_error(fmt);
1784        if (!args || nargs<3 || nargs>4)
1785          return fmt_error(fmt);
1786        { // extra nesting for windows
1787          for (int j=0; j<3; j++)
1788          {
1789            int shift = 0;
1790            uint32_t mask = args[j];
1791            for (shift=0; shift<32 && !(mask & 1); shift++)
1792              mask >>= 1;
1793            if ((shift>=32) || (mask&(mask+1)))
1794              return fmt_error(fmt);
1795            for (int i=0; i<256; i++)
1796              fmt->rgb[j][i] = (mask & ((int)((i*mask+127.0)/255.0)))<<shift;
1797          }
1798        }
1799        if (nargs >= 4)
1800          fmt->xorval = args[3];
1801        break;
1802      }
1803    case DDJVU_FORMAT_PALETTE8:
1804      {
1805        if (nargs!=6*6*6 || !args)
1806          return fmt_error(fmt);
1807        { // extra nesting for windows
1808          for (int k=0; k<6*6*6; k++)
1809            fmt->palette[k] = args[k];
1810        }
1811        { // extra nesting for windows
1812          int j=0;
1813          for(int i=0; i<6; i++)
1814            for(; j < (i+1)*0x33 - 0x19 && j<256; j++)
1815            {
1816              fmt->rgb[0][j] = i * 6 * 6;
1817              fmt->rgb[1][j] = i * 6;
1818              fmt->rgb[2][j] = i;
1819            }
1820        }
1821        break;
1822      }
1823    case DDJVU_FORMAT_RGB24:
1824    case DDJVU_FORMAT_BGR24:
1825    case DDJVU_FORMAT_GREY8:
1826    case DDJVU_FORMAT_LSBTOMSB:
1827    case DDJVU_FORMAT_MSBTOLSB:
1828      if (!nargs) 
1829        break;
1830    default:
1831      return fmt_error(fmt);
1832    }
1833  return fmt;
1834}
1835
1836void
1837ddjvu_format_set_row_order(ddjvu_format_t *format, int top_to_bottom)
1838{
1839  format->rtoptobottom = !! top_to_bottom;
1840}
1841
1842void
1843ddjvu_format_set_y_direction(ddjvu_format_t *format, int top_to_bottom)
1844{
1845  format->ytoptobottom = !! top_to_bottom;
1846}
1847
1848void
1849ddjvu_format_set_ditherbits(ddjvu_format_t *format, int bits)
1850{
1851  if (bits>0 && bits<=64)
1852    format->ditherbits = bits;
1853}
1854
1855void
1856ddjvu_format_set_gamma(ddjvu_format_t *format, double gamma)
1857{
1858  if (gamma>=0.5 && gamma<=5.0)
1859    format->gamma = gamma;
1860}
1861
1862void
1863ddjvu_format_release(ddjvu_format_t *format)
1864{
1865  delete format;
1866}
1867
1868static void
1869fmt_convert_row(const GPixel *p, int w, 
1870                const ddjvu_format_t *fmt, char *buf)
1871{
1872  const uint32_t (*r)[256] = fmt->rgb;
1873  const uint32_t xorval = fmt->xorval;
1874  switch(fmt->style)
1875    {
1876    case DDJVU_FORMAT_BGR24:    /* truecolor 24 bits in BGR order */
1877      {
1878        memcpy(buf, (const char*)p, 3*w);
1879        break;
1880      }
1881    case DDJVU_FORMAT_RGB24:    /* truecolor 24 bits in RGB order */
1882      { 
1883        while (--w >= 0) { 
1884          buf[0]=p->r; buf[1]=p->g; buf[2]=p->b; 
1885          buf+=3; p+=1; 
1886        }
1887        break;
1888      }
1889    case DDJVU_FORMAT_RGBMASK16: /* truecolor 16 bits with masks */
1890      {
1891        uint16_t *b = (uint16_t*)buf;
1892        while (--w >= 0) {
1893          b[0]=(r[0][p->r]|r[1][p->g]|r[2][p->b])^xorval; 
1894          b+=1; p+=1; 
1895        }
1896        break;
1897      }
1898    case DDJVU_FORMAT_RGBMASK32: /* truecolor 32 bits with masks */
1899      {
1900        uint32_t *b = (uint32_t*)buf;
1901        while (--w >= 0) {
1902          b[0]=(r[0][p->r]|r[1][p->g]|r[2][p->b])^xorval; 
1903          b+=1; p+=1; 
1904        }
1905        break;
1906      }
1907    case DDJVU_FORMAT_GREY8:    /* greylevel 8 bits */
1908      {
1909        while (--w >= 0) { 
1910          buf[0]=(5*p->r + 9*p->g + 2*p->b)>>4; 
1911          buf+=1; p+=1; 
1912        }
1913        break;
1914      }
1915    case DDJVU_FORMAT_PALETTE8: /* paletized 8 bits (6x6x6 color cube) */
1916      {
1917        const uint32_t *u = fmt->palette;
1918        while (--w >= 0) {
1919          buf[0] = u[r[0][p->r]+r[1][p->g]+r[2][p->b]]; 
1920          buf+=1; p+=1; 
1921        }
1922        break;
1923      }
1924    case DDJVU_FORMAT_MSBTOLSB: /* packed bits, msb on the left */
1925      {
1926        unsigned char s=0, m=0x80;
1927        while (--w >= 0) {
1928          if ( 5*p->r + 9*p->g + 2*p->b < 0xc00 ) { s |= m; }
1929          if (! (m >>= 1)) { *buf++ = s; s=0; m=0x80; }
1930          p += 1;
1931        }
1932        if (m < 0x80) { *buf++ = s; }
1933        break;
1934      }
1935    case DDJVU_FORMAT_LSBTOMSB: /* packed bits, lsb on the left */
1936      {
1937        unsigned char s=0, m=0x1;
1938        while (--w >= 0) {
1939          if ( 5*p->r + 9*p->g + 2*p->b < 0xc00 ) { s |= m; }
1940          if (! (m <<= 1)) { *buf++ = s; s=0; m=0x1; }
1941          p += 1;
1942        }
1943        if (m > 0x1) { *buf++ = s; }
1944        break;
1945      }
1946    }
1947}
1948
1949static void
1950fmt_convert(GPixmap *pm, const ddjvu_format_t *fmt, char *buffer, int rowsize)
1951{
1952  int w = pm->columns();
1953  int h = pm->rows();
1954  // Loop on rows
1955  if (fmt->rtoptobottom)
1956    {
1957      for(int r=h-1; r>=0; r--, buffer+=rowsize)
1958        fmt_convert_row((*pm)[r], w, fmt, buffer);
1959    }
1960  else
1961    {
1962      for(int r=0; r<h; r++, buffer+=rowsize)
1963        fmt_convert_row((*pm)[r], w, fmt, buffer);
1964    }
1965}
1966
1967static void
1968fmt_convert_row(unsigned char *p, unsigned char *g, int w, 
1969                const ddjvu_format_t *fmt, char *buf)
1970{
1971  const uint32_t (*r)[256] = fmt->rgb;
1972  const uint32_t xorval = fmt->xorval;
1973  switch(fmt->style)
1974    {
1975    case DDJVU_FORMAT_BGR24:    /* truecolor 24 bits in BGR order */
1976    case DDJVU_FORMAT_RGB24:    /* truecolor 24 bits in RGB order */
1977      { 
1978        while (--w >= 0) { 
1979          buf[0]=buf[1]=buf[2]=g[*p];
1980          buf+=3; p+=1; 
1981        }
1982        break;
1983      }
1984    case DDJVU_FORMAT_RGBMASK16: /* truecolor 16 bits with masks */
1985      {
1986        uint16_t *b = (uint16_t*)buf;
1987        while (--w >= 0) {
1988          unsigned char x = g[*p];
1989          b[0]=(r[0][x]|r[1][x]|r[2][x])^xorval; 
1990          b+=1; p+=1; 
1991        }
1992        break;
1993      }
1994    case DDJVU_FORMAT_RGBMASK32: /* truecolor 32 bits with masks */
1995      {
1996        uint32_t *b = (uint32_t*)buf;
1997        while (--w >= 0) {
1998          unsigned char x = g[*p];
1999          b[0]=(r[0][x]|r[1][x]|r[2][x])^xorval; 
2000          b+=1; p+=1; 
2001        }
2002        break;
2003      }
2004    case DDJVU_FORMAT_GREY8:    /* greylevel 8 bits */
2005      {
2006        while (--w >= 0) { 
2007          buf[0]=g[*p];
2008          buf+=1; p+=1; 
2009        }
2010        break;
2011      }
2012    case DDJVU_FORMAT_PALETTE8: /* paletized 8 bits (6x6x6 color cube) */
2013      {
2014        const uint32_t *u = fmt->palette;
2015        while (--w >= 0) {
2016          buf[0] = u[g[*p]*(1+6+36)];
2017          buf+=1; p+=1; 
2018        }
2019        break;
2020      }
2021    case DDJVU_FORMAT_MSBTOLSB: /* packed bits, msb on the left */
2022      {
2023        unsigned char s=0, m=0x80;
2024        while (--w >= 0) {
2025          if (g[*p] < 0xc0) { s |= m; }
2026          if (! (m >>= 1)) { *buf++ = s; s=0; m=0x80; }
2027          p += 1;
2028        }
2029        if (m < 0x80) { *buf++ = s; }
2030        break;
2031      }
2032    case DDJVU_FORMAT_LSBTOMSB: /* packed bits, lsb on the left */
2033      {
2034        unsigned char s=0, m=0x1;
2035        while (--w >= 0) {
2036          if (g[*p] < 0xc0) { s |= m; }
2037          if (! (m <<= 1)) { *buf++ = s; s=0; m=0x1; }
2038          p += 1;
2039        }
2040        if (m > 0x1) { *buf++ = s; }
2041        break;
2042      }
2043    }
2044}
2045
2046static void
2047fmt_convert(GBitmap *bm, const ddjvu_format_t *fmt, char *buffer, int rowsize)
2048{
2049  int w = bm->columns();
2050  int h = bm->rows();
2051  int m = bm->get_grays();
2052  // Gray levels
2053  int i;
2054  unsigned char g[256];
2055  for (i=0; i<m; i++)
2056    g[i] = 255 - ( i * 255 + (m - 1)/2 ) / (m - 1);
2057  for (i=m; i<256; i++)
2058    g[i] = 0;
2059  // Loop on rows
2060  if (fmt->rtoptobottom)
2061    {
2062      for(int r=h-1; r>=0; r--, buffer+=rowsize)
2063        fmt_convert_row((*bm)[r], g, w, fmt, buffer);
2064    }
2065  else
2066    {
2067      for(int r=0; r<h; r++, buffer+=rowsize)
2068        fmt_convert_row((*bm)[r], g, w, fmt, buffer);
2069    }
2070}
2071
2072static void
2073fmt_dither(GPixmap *pm, const ddjvu_format_t *fmt, int x, int y)
2074{
2075  if (fmt->ditherbits < 8)
2076    return;
2077  else if (fmt->ditherbits < 15)
2078    pm->ordered_666_dither(x, y);
2079  else if (fmt->ditherbits < 24)
2080    pm->ordered_32k_dither(x, y);
2081}
2082
2083
2084// ----------------------------------------
2085
2086int
2087ddjvu_page_render(ddjvu_page_t *page,
2088                  const ddjvu_render_mode_t mode,
2089                  const ddjvu_rect_t *pagerect,
2090                  const ddjvu_rect_t *renderrect,
2091                  const ddjvu_format_t *pixelformat,
2092                  unsigned long rowsize,
2093                  char *imagebuffer )
2094{
2095  G_TRY
2096    {
2097      GP<GPixmap> pm;
2098      GP<GBitmap> bm;
2099      GRect prect, rrect;
2100      rect2grect(pagerect, prect);
2101      rect2grect(renderrect, rrect);
2102      if (pixelformat && pixelformat->ytoptobottom)
2103        {
2104          prect.ymin = renderrect->y + renderrect->h;
2105          prect.ymax = prect.ymin + pagerect->h;
2106          rrect.ymin = pagerect->y + pagerect->h;
2107          rrect.ymax = rrect.ymin + renderrect->h;
2108        }
2109
2110      DjVuImage *img = page->img;
2111      if (img) 
2112        {
2113          switch (mode)
2114            {
2115            case DDJVU_RENDER_COLOR:
2116              pm = img->get_pixmap(rrect, prect, pixelformat->gamma);
2117              if (! pm) 
2118                bm = img->get_bitmap(rrect, prect);
2119              break;
2120            case DDJVU_RENDER_BLACK:
2121              bm = img->get_bitmap(rrect, prect);
2122              if (! bm)
2123                pm = img->get_pixmap(rrect, prect, pixelformat->gamma);
2124              break;
2125            case DDJVU_RENDER_MASKONLY:
2126              bm = img->get_bitmap(rrect, prect);
2127              break;
2128            case DDJVU_RENDER_COLORONLY:
2129              pm = img->get_pixmap(rrect, prect, pixelformat->gamma);
2130              break;
2131            case DDJVU_RENDER_BACKGROUND:
2132              pm = img->get_bg_pixmap(rrect, prect, pixelformat->gamma);
2133              break;
2134            case DDJVU_RENDER_FOREGROUND:
2135              pm = img->get_fg_pixmap(rrect, prect, pixelformat->gamma);
2136              if (! pm) 
2137                bm = img->get_bitmap(rrect, prect);
2138              break;
2139            }
2140        }
2141      if (pm)
2142        {
2143          int dx = rrect.xmin - prect.xmin;
2144          int dy = rrect.ymin - prect.xmin;
2145          fmt_dither(pm, pixelformat, dx, dy);
2146          fmt_convert(pm, pixelformat, imagebuffer, rowsize);
2147          return 2;
2148        }
2149      else if (bm)
2150        {
2151          fmt_convert(bm, pixelformat, imagebuffer, rowsize);
2152          return 1;
2153        }
2154    }
2155  G_CATCH(ex)
2156    {
2157      ERROR1(page, ex);
2158    }
2159  G_ENDCATCH;
2160  return 0;
2161}
2162
2163
2164// ----------------------------------------
2165// Thumbnails
2166
2167void
2168ddjvu_thumbnail_p::callback(void *cldata)
2169{
2170  ddjvu_thumbnail_p *thumb = (ddjvu_thumbnail_p*)cldata;
2171  if (thumb->document)
2172    {
2173      GMonitorLock lock(&thumb->document->monitor);
2174      if (thumb->pool && thumb->pool->is_eof())
2175        {
2176          GP<DataPool> pool = thumb->pool;
2177          int size = pool->get_size();
2178          thumb->pool = 0;
2179          G_TRY
2180            {
2181              thumb->data.resize(0,size-1);
2182              pool->get_data( (void*)(char*)thumb->data, 0, size);
2183            }
2184          G_CATCH_ALL
2185            {
2186              thumb->data.empty();
2187              G_RETHROW;
2188            }
2189          G_ENDCATCH;
2190          if (thumb->document->doc)
2191            {
2192              GP<ddjvu_message_p> p = new ddjvu_message_p;
2193              p->p.m_thumbnail.pagenum = thumb->pagenum;
2194              msg_push(xhead(DDJVU_THUMBNAIL, thumb->document), p);
2195            } 
2196        }
2197    }
2198}
2199
2200ddjvu_status_t
2201ddjvu_thumbnail_status(ddjvu_document_t *document, int pagenum, int start)
2202{
2203  G_TRY
2204    {
2205      GMonitorLock lock(&document->monitor);
2206      GPosition p = document->thumbnails.contains(pagenum);
2207      GP<ddjvu_thumbnail_p> thumb;
2208      if (p)
2209        {
2210          thumb = document->thumbnails[p];
2211        } 
2212      else
2213        {
2214          DjVuDocument *doc = document->doc;
2215          GP<DataPool> pool = doc->get_thumbnail(pagenum, !start);
2216          if (pool)
2217            {
2218              thumb = new ddjvu_thumbnail_p;
2219              thumb->document = document;
2220              thumb->pagenum = pagenum;
2221              thumb->pool = pool;
2222              document->thumbnails[pagenum] = thumb;
2223              pool->add_trigger(-1, ddjvu_thumbnail_p::callback, 
2224                                (void*)(ddjvu_thumbnail_p*)thumb);
2225            }
2226        }
2227      if (! thumb)
2228        return DDJVU_JOB_NOTSTARTED;       
2229      else if (thumb->pool)
2230        return DDJVU_JOB_STARTED;
2231      else if (thumb->data.size() > 0)
2232        return DDJVU_JOB_OK;
2233    }
2234  G_CATCH(ex)
2235    {
2236      ERROR1(document, ex);
2237    }
2238  G_ENDCATCH;
2239  return DDJVU_JOB_FAILED;
2240}
2241 
2242int
2243ddjvu_thumbnail_render(ddjvu_document_t *document, int pagenum, 
2244                       int *wptr, int *hptr,
2245                       const ddjvu_format_t *pixelformat,
2246                       unsigned long rowsize,
2247                       char *imagebuffer)
2248{
2249  G_TRY
2250    {
2251      GP<ddjvu_thumbnail_p> thumb;
2252      ddjvu_status_t status = ddjvu_thumbnail_status(document,pagenum,FALSE);
2253      if (status == DDJVU_JOB_OK)
2254        {
2255          GMonitorLock lock(&document->monitor);
2256          thumb = document->thumbnails[pagenum];
2257        }
2258      if (! (thumb && wptr && hptr))
2259        return FALSE;
2260      if (! (thumb->data.size() > 0))
2261        return FALSE;
2262      /* Decode wavelet data */
2263      int size = thumb->data.size();
2264      char *data = (char*)thumb->data;
2265      GP<IW44Image> iw = IW44Image::create_decode();
2266      iw->decode_chunk(ByteStream::create_static((void*)data, size));
2267      int w = iw->get_width();
2268      int h = iw->get_height();
2269      /* Restore aspect ratio */
2270      double dw = (double)w / *wptr;
2271      double dh = (double)h / *hptr;
2272      if (dw > dh) 
2273        *hptr = (int)(h / dw);
2274      else
2275        *wptr = (int)(w / dh);
2276      if (! imagebuffer)
2277        return TRUE;
2278      /* Render and scale image */
2279      GP<GPixmap> pm = iw->get_pixmap();
2280      double thumbgamma = document->doc->get_thumbnails_gamma();
2281      pm->color_correct(pixelformat->gamma / thumbgamma);
2282      GP<GPixmapScaler> scaler = GPixmapScaler::create(w, h, *wptr, *hptr);
2283      GP<GPixmap> scaledpm = GPixmap::create();
2284      GRect scaledrect(0, 0, *wptr, *hptr);
2285      scaler->scale(GRect(0, 0, w, h), *pm, scaledrect, *scaledpm);
2286      /* Convert */
2287      fmt_dither(scaledpm, pixelformat, 0, 0);
2288      fmt_convert(scaledpm, pixelformat, imagebuffer, rowsize);
2289      return TRUE;
2290    }
2291  G_CATCH(ex)
2292    {
2293      ERROR1(document, ex);
2294    }
2295  G_ENDCATCH;
2296  return FALSE;
2297}
2298
2299
2300// ----------------------------------------
2301// Threaded jobs
2302
2303struct DJVUNS ddjvu_runnablejob_s : public ddjvu_job_s
2304{
2305  bool mystop;
2306  int  myprogress;
2307  ddjvu_status_t mystatus;
2308  // methods
2309  ddjvu_runnablejob_s();
2310  ddjvu_status_t start();
2311  void progress(int p);
2312  // thread function
2313  virtual ddjvu_status_t run() = 0;
2314  // virtual port functions:
2315  virtual bool inherits(const GUTF8String&);
2316  virtual ddjvu_status_t status();
2317  virtual void stop();
2318private:
2319  static void cbstart(void*);
2320};
2321
2322ddjvu_runnablejob_s::ddjvu_runnablejob_s()
2323  : mystop(false), myprogress(-1),
2324    mystatus(DDJVU_JOB_NOTSTARTED) 
2325{
2326}
2327
2328void 
2329ddjvu_runnablejob_s::progress(int x)
2330{
2331  if ((mystatus>=DDJVU_JOB_OK) || (x>myprogress && x<100))
2332    {
2333      GMonitorLock lock(&monitor);
2334      GP<ddjvu_message_p> p = new ddjvu_message_p;
2335      p->p.m_progress.status = mystatus;
2336      p->p.m_progress.percent = myprogress = x;
2337      msg_push(xhead(DDJVU_PROGRESS,this),p);
2338    }
2339}
2340
2341ddjvu_status_t
2342ddjvu_runnablejob_s::start()
2343{
2344  GMonitorLock lock(&monitor);
2345  if (mystatus==DDJVU_JOB_NOTSTARTED && myctx)
2346    {
2347      GThread thr;
2348      thr.create(cbstart, (void*)this);
2349      monitor.wait();
2350    }
2351  return mystatus;
2352}
2353
2354void
2355ddjvu_runnablejob_s::cbstart(void *arg)
2356{
2357  GP<ddjvu_runnablejob_s> self = (ddjvu_runnablejob_s*)arg;
2358  {
2359    GMonitorLock lock(&self->monitor);
2360    self->mystatus = DDJVU_JOB_STARTED;
2361    self->monitor.signal();
2362  }
2363  ddjvu_status_t r;
2364  G_TRY
2365    {
2366      self->progress(0);
2367      r = self->run();
2368    }
2369  G_CATCH_ALL
2370    {
2371      r = DDJVU_JOB_FAILED;
2372      if (self && self->mystop)
2373        r = DDJVU_JOB_STOPPED;
2374    }
2375  G_ENDCATCH;
2376  {
2377    GMonitorLock lock(&self->monitor);
2378    self->mystatus = r;
2379  }
2380  if (self && self->mystatus> DDJVU_JOB_OK)
2381    self->progress(self->myprogress);
2382  else
2383    self->progress(100);
2384}
2385
2386bool 
2387ddjvu_runnablejob_s::inherits(const GUTF8String &classname)
2388{
2389  return (classname == "ddjvu_runnablejob_s") 
2390    || ddjvu_job_s::inherits(classname);
2391}
2392
2393ddjvu_status_t
2394ddjvu_runnablejob_s::status()
2395{
2396  return mystatus;
2397}
2398
2399void
2400ddjvu_runnablejob_s::stop()
2401{
2402  mystop = true;
2403}
2404
2405
2406// ----------------------------------------
2407// Printing
2408
2409struct DJVUNS ddjvu_printjob_s : public ddjvu_runnablejob_s
2410{
2411  DjVuToPS printer;
2412  GUTF8String pages;
2413  GP<ByteStream> obs;
2414  virtual ddjvu_status_t run();
2415  // virtual port functions:
2416  virtual bool inherits(const GUTF8String&);
2417  // progress
2418  static void cbrefresh(void*);
2419  static void cbprogress(double, void*);
2420  static void cbinfo(int, int, int, DjVuToPS::Stage, void*);
2421  double progress_low;
2422  double progress_high;
2423};
2424
2425bool 
2426ddjvu_printjob_s::inherits(const GUTF8String &classname)
2427{
2428  return (classname == "ddjvu_printjob_s") 
2429    || ddjvu_runnablejob_s::inherits(classname);
2430}
2431
2432ddjvu_status_t
2433ddjvu_printjob_s::run()
2434{
2435  mydoc->doc->wait_for_complete_init();
2436  progress_low = 0;
2437  progress_high = 1;
2438  printer.set_refresh_cb(cbrefresh, (void*)this);
2439  printer.set_dec_progress_cb(cbprogress, (void*)this);
2440  printer.set_prn_progress_cb(cbprogress, (void*)this);
2441  printer.set_info_cb(cbinfo, (void*)this);
2442  printer.print(*obs, mydoc->doc, pages);
2443  return DDJVU_JOB_OK;
2444}
2445
2446void
2447ddjvu_printjob_s::cbrefresh(void *data)
2448{
2449  ddjvu_printjob_s *self = (ddjvu_printjob_s*)data;
2450  if (self->mystop)
2451    {
2452      msg_push(xhead(DDJVU_INFO,self), msg_prep_info("Print job stopped"));
2453      G_THROW(DataPool::Stop);
2454    }
2455}
2456
2457void
2458ddjvu_printjob_s::cbprogress(double done, void *data)
2459{
2460  ddjvu_printjob_s *self = (ddjvu_printjob_s*)data;
2461  double &low = self->progress_low;
2462  double &high = self->progress_high;
2463  double progress = low;
2464  if (done >= 1)
2465    progress = high;
2466  else if (done >= 0)
2467    progress = low + done * (high-low);
2468  self->progress((int)(progress * 100));
2469  ddjvu_printjob_s::cbrefresh(data);
2470}
2471
2472void
2473ddjvu_printjob_s::cbinfo(int pnum, int pcnt, int ptot,
2474                         DjVuToPS::Stage stage, void *data)
2475{
2476  ddjvu_printjob_s *self = (ddjvu_printjob_s*)data;
2477  double &low = self->progress_low;
2478  double &high = self->progress_high;
2479  low = 0;
2480  high = 1;
2481  if (ptot > 0) 
2482    {
2483      double step = 1.0 / (double)ptot;
2484      low = (double)pcnt * step;
2485      if (stage != DjVuToPS::DECODING) 
2486        low += step / 2.0;
2487      high = low  + step / 2.0;
2488    }
2489  if (low < 0)
2490    low = 0;
2491  if (low > 1) 
2492    low = 1;
2493  if (high < low) 
2494    high = low;
2495  if (high > 1)
2496    high = 1;
2497  self->progress((int)(low * 100));
2498  ddjvu_printjob_s::cbrefresh(data);
2499}
2500
2501static void
2502complain(GUTF8String opt, const char *msg)
2503{
2504  GUTF8String message;
2505  if (opt.length() > 0)
2506    message = "Parsing \"" + opt + "\": " + msg;
2507  else
2508    message = msg;
2509  G_EMTHROW(GException((const char*)message));
2510}
2511
2512ddjvu_job_t *
2513ddjvu_document_print(ddjvu_document_t *document, FILE *output,
2514                     int optc, const char * const * optv)
2515{
2516  ddjvu_printjob_s *job = 0;
2517  G_TRY
2518    {
2519      job = new ddjvu_printjob_s;
2520      ref(job);
2521      job->myctx = document->myctx;
2522      job->mydoc = document;
2523      // parse options (see djvups(1))
2524      DjVuToPS::Options &options = job->printer.options;
2525      GUTF8String &pages = job->pages;
2526      while (optc>0)
2527        {
2528          // normalize
2529          GNativeString narg(optv[0]);
2530          GUTF8String uarg = narg;
2531          const char *s1 = (const char*)narg;
2532          if (s1[0] == '-') s1++;
2533          if (s1[0] == '-') s1++;
2534          // separate arguments
2535          const char *s2 = s1;
2536          while (*s2 && *s2 != '=') s2++;
2537          GUTF8String s( s1, s2-s1 );
2538          GUTF8String arg( s2[0] && s2[1] ? s2+1 : "" );
2539          // rumble!
2540          if (s == "page" || s == "pages")
2541            {
2542              if (pages.length())
2543                pages = pages + ",";
2544              pages = pages + arg;
2545            }
2546          else if (s == "format")
2547            {
2548              if (arg == "ps")
2549                options.set_format(DjVuToPS::Options::PS);
2550              else if (arg == "eps")
2551                options.set_format(DjVuToPS::Options::EPS);
2552              else
2553                complain(uarg,"Invalid format. Use \"ps\" or \"eps\".");
2554            }
2555          else if (s == "level")
2556            {
2557              int endpos;
2558              int lvl = arg.toLong(0, endpos);
2559              if (endpos != (int)arg.length() || lvl < 1 || lvl > 4)
2560                complain(uarg,"Invalid Postscript language level.");
2561              options.set_level(lvl);
2562            }
2563          else if (s == "orient" || s == "orientation")
2564            {
2565              if (arg == "a" || arg == "auto" )
2566                options.set_orientation(DjVuToPS::Options::AUTO);
2567              if (arg == "l" || arg == "landscape" )
2568                options.set_orientation(DjVuToPS::Options::LANDSCAPE);
2569              if (arg == "p" || arg == "portrait" )
2570                options.set_orientation(DjVuToPS::Options::PORTRAIT);
2571              else
2572                complain(uarg,"Invalid orientation. Use \"auto\", "
2573                         "\"landscape\" or \"portrait\".");
2574            }
2575          else if (s == "mode")
2576            {
2577              if (arg == "c" || arg == "color" )
2578                options.set_mode(DjVuToPS::Options::COLOR);
2579              else if (arg == "black" || arg == "bw")
2580                options.set_mode(DjVuToPS::Options::BW);
2581              else if (arg == "fore" || arg == "foreground")
2582                options.set_mode(DjVuToPS::Options::FORE);
2583              else if (arg == "back" || arg == "background" )
2584                options.set_mode(DjVuToPS::Options::BACK);
2585              else
2586                complain(uarg,"Invalid mode. Use \"color\", \"bw\", "
2587                         "\"foreground\", or \"background\".");
2588            }
2589          else if (s == "zoom")
2590            {
2591              if (arg == "auto" || arg == "fit" || arg == "fit_page")
2592                options.set_zoom(0);
2593              else if (arg == "1to1" || arg == "onetoone")
2594                options.set_zoom(100);               
2595              else 
2596                {
2597                  int endpos;
2598                  int z = arg.toLong(0,endpos);
2599                  if (endpos != (int)arg.length() || z < 25 || z > 2400)
2600                    complain(uarg,"Invalid zoom factor.");
2601                  options.set_zoom(z);
2602                }
2603            }
2604          else if (s == "color")
2605            {
2606              if (arg == "yes" || arg == "")
2607                options.set_color(true);
2608              else if (arg == "no")
2609                options.set_color(false);
2610              else
2611                complain(uarg,"Invalid argument. Use \"yes\" or \"no\".");
2612            }
2613          else if (s == "gray" || s == "grayscale")
2614            {
2615              if (arg.length())
2616                complain(uarg,"No argument was expected.");
2617              options.set_color(false);
2618            }
2619          else if (s == "srgb" || s == "colormatch")
2620            {
2621              if (arg == "yes" || arg == "")
2622                options.set_sRGB(true);
2623              else if (arg == "no")
2624                options.set_sRGB(false);
2625              else
2626                complain(uarg,"Invalid argument. Use \"yes\" or \"no\".");
2627            }
2628          else if (s == "gamma")
2629            {
2630              int endpos;
2631              double g = arg.toDouble(0,endpos);
2632              if (endpos != (int)arg.length() || g < 0.3 || g > 5.0)
2633                complain(uarg,"Invalid gamma factor. "
2634                              "Use a number in range 0.3 ... 5.0.");
2635              options.set_gamma(g);
2636            }
2637          else if (s == "copies")
2638            {
2639              int endpos;
2640              int n = arg.toLong(0, endpos);
2641              if (endpos != (int)arg.length() || n < 1 || n > 999999)
2642                complain(uarg,"Invalid number of copies.");
2643              options.set_copies(n);
2644            }
2645          else if (s == "frame")
2646            {
2647              if (arg == "yes" || arg == "")
2648                options.set_frame(true);
2649              else if (arg == "no")
2650                options.set_frame(false);
2651              else
2652                complain(uarg,"Invalid argument. Use \"yes\" or \"no\".");
2653            }
2654          else if (s == "cropmarks")
2655            {
2656              if (arg == "yes" || arg == "")
2657                options.set_cropmarks(true);
2658              else if (arg == "no")
2659                options.set_cropmarks(false);
2660              else
2661                complain(uarg,"Invalid argument. Use \"yes\" or \"no\".");
2662            }
2663          else if (s == "text")
2664            {
2665              if (arg == "yes" || arg == "")
2666                options.set_text(true);
2667              else if (arg == "no")
2668                options.set_text(false);
2669              else
2670                complain(uarg,"Invalid argument. Use \"yes\" or \"no\".");
2671            }
2672          else if (s == "booklet")
2673            {
2674              if (arg == "no")
2675                options.set_bookletmode(DjVuToPS::Options::OFF);
2676              else if (arg == "recto")
2677                options.set_bookletmode(DjVuToPS::Options::RECTO);
2678              else if (arg == "verso")
2679                options.set_bookletmode(DjVuToPS::Options::VERSO);
2680              else if (arg == "rectoverso" || arg=="yes" || arg=="")
2681                options.set_bookletmode(DjVuToPS::Options::RECTOVERSO);
2682              else 
2683                complain(uarg,"Invalid argument."
2684                         "Use \"no\", \"yes\", \"recto\", or \"verso\".");
2685            }
2686          else if (s == "bookletmax")
2687            {
2688              int endpos;
2689              int n = arg.toLong(0, endpos);
2690              if (endpos != (int)arg.length() || n < 0 || n > 999999)
2691                complain(uarg,"Invalid argument.");
2692              options.set_bookletmax(n);
2693            }
2694          else if (s == "bookletalign")
2695            {
2696              int endpos;
2697              int n = arg.toLong(0, endpos);
2698              if (endpos != (int)arg.length() || n < -720 || n > +720)
2699                complain(uarg,"Invalid argument.");
2700              options.set_bookletalign(n);
2701            }
2702          else if (s == "bookletfold")
2703            {
2704              int endpos = 0;
2705              int m = 250;
2706              int n = arg.toLong(0, endpos);
2707              if (endpos>0 && endpos<(int)arg.length() && arg[endpos]=='+')
2708                m = arg.toLong(endpos+1, endpos);
2709              if (endpos != (int)arg.length() || m<0 || m>720 || n<0 || n>9999 )
2710                complain(uarg,"Invalid argument.");
2711              options.set_bookletfold(n,m);
2712            }
2713          else
2714            {
2715              complain(uarg, "Unrecognized option.");
2716            }
2717          // Next option
2718          optc -= 1;
2719          optv += 1;
2720        }
2721      // go
2722      job->obs = ByteStream::create(output, "wb", false);
2723      job->start();
2724    }
2725  G_CATCH(ex)
2726    {
2727      if (job) 
2728        unref(job);
2729      job = 0;
2730      ERROR1(document, ex);
2731    }
2732  G_ENDCATCH;
2733  return job;
2734}
2735
2736// ----------------------------------------
2737// Saving (insufficiently tested)
2738
2739struct DJVUNS ddjvu_savejob_s : public ddjvu_runnablejob_s
2740{
2741  GP<ByteStream> obs;
2742  virtual ddjvu_status_t run();
2743  // virtual port functions:
2744  virtual bool inherits(const GUTF8String&);
2745  virtual void notify_file_flags_changed(const DjVuFile*, long, long);
2746  // data
2747  GMonitor monitor;
2748};
2749
2750bool 
2751ddjvu_savejob_s::inherits(const GUTF8String &classname)
2752{
2753  return (classname == "ddjvu_savejob_s") 
2754    || ddjvu_runnablejob_s::inherits(classname);
2755}
2756
2757void
2758ddjvu_savejob_s::notify_file_flags_changed(const DjVuFile *file, long mask, long)
2759{
2760  if (mask & (DjVuFile::ALL_DATA_PRESENT ||
2761              DjVuFile::DECODE_FAILED || DjVuFile::DECODE_STOPPED ||
2762              DjVuFile::STOPPED || DjVuFile::DECODE_STOPPED ))
2763    {
2764      GMonitorLock lock(&monitor);
2765      monitor.signal();
2766    }
2767}
2768
2769ddjvu_status_t
2770ddjvu_savejob_s::run()
2771{
2772  DjVuDocument *doc = mydoc->doc;
2773  doc->wait_for_complete_init();
2774  // Determine which components to save
2775  int ncomp;
2776  GArray<GUTF8String> comp_ids;
2777  GPArray<DjVuFile> comp_files;
2778  if (doc->get_doc_type()==DjVuDocument::BUNDLED ||
2779      doc->get_doc_type()==DjVuDocument::INDIRECT)
2780    {
2781      GP<DjVmDir> dir = doc->get_djvm_dir();
2782      ncomp = dir->get_files_num();
2783      comp_ids.resize(ncomp - 1);
2784      comp_files.resize(ncomp - 1);
2785      GPList<DjVmDir::File> flist = dir->get_files_list();
2786      GPosition pos=flist;
2787      for (int comp=0; comp<ncomp; ++pos, ++comp)
2788        comp_ids[comp] = flist[pos]->get_load_name();
2789    } 
2790  else 
2791    { 
2792      ncomp = doc->get_pages_num();
2793      comp_ids.resize(ncomp - 1);
2794      comp_files.resize(ncomp - 1);
2795      { // extra nesting for windows
2796        for (int comp=0; comp<ncomp; comp++)
2797          comp_ids[comp] = GUTF8String(comp);
2798      }
2799    }
2800  // Monitoring download progress
2801  int lo = 0;
2802  int hi = 0;
2803  get_portcaster()->add_route(doc, this);
2804  while (lo < ncomp && !mystop)
2805    {
2806      int in_progress = 0;
2807      GMonitorLock lock(&monitor);
2808      while (lo<hi && comp_files[lo]->is_data_present())
2809        lo += 1;
2810      { // extra nesting for windows
2811        for (int comp=lo; comp<hi; comp++)
2812          if (! comp_files[comp]->is_data_present())
2813            in_progress += 1;
2814      }
2815      while (hi<ncomp && in_progress < 2)
2816        {
2817          comp_files[hi] = doc->get_djvu_file(comp_ids[hi]);
2818          in_progress += 1;
2819          hi += 1;
2820        }
2821      if (in_progress > 0)
2822        monitor.wait();
2823    }
2824  if (mystop)
2825    G_THROW("STOP");
2826  // Saving!
2827  doc->write(obs);
2828  return DDJVU_JOB_OK;
2829}
2830
2831
2832ddjvu_job_t *
2833ddjvu_document_save(ddjvu_document_t *document, FILE *output, 
2834                    int optc, const char * const * optv)
2835{
2836  ddjvu_savejob_s *job = 0;
2837  G_TRY
2838    {
2839      job = new ddjvu_savejob_s;
2840      ref(job);
2841      job->myctx = document->myctx;
2842      job->mydoc = document;
2843      // parse options
2844      while (optc>0)
2845        {
2846          GNativeString narg(optv[0]);
2847          GUTF8String uarg = narg;
2848          complain(uarg, "Unrecognized option.");
2849          optc -= 1;
2850          optv += 1;
2851        }
2852      // go
2853      job->obs = ByteStream::create(output, "wb", false);
2854      job->start();
2855    }
2856  G_CATCH(ex)
2857    {
2858      if (job) 
2859        unref(job);
2860      job = 0;
2861      ERROR1(document, ex);
2862    }
2863  G_ENDCATCH;
2864  return job;
2865}
2866
2867
2868
2869
2870// ----------------------------------------
2871// S-Expressions (generic)
2872
2873static miniexp_t
2874miniexp_status(ddjvu_status_t status)
2875{
2876  if (status < DDJVU_JOB_OK)
2877    return miniexp_dummy;
2878  else if (status == DDJVU_JOB_STOPPED)
2879    return miniexp_symbol("stopped");
2880  else if (status > DDJVU_JOB_OK)
2881    return miniexp_symbol("failed");   
2882  return miniexp_nil;
2883}
2884
2885static void
2886miniexp_protect(ddjvu_document_t *document, miniexp_t expr)
2887{
2888  { // extra nesting for windows
2889    for(miniexp_t p=document->protect; miniexp_consp(p); p=miniexp_cdr(p))
2890      if (miniexp_car(p) == expr)
2891        return;
2892  }
2893  if (miniexp_consp(expr) || miniexp_objectp(expr))
2894    document->protect = miniexp_cons(expr, document->protect);
2895}
2896
2897void
2898ddjvu_miniexp_release(ddjvu_document_t *document, miniexp_t expr)
2899{
2900  miniexp_t q = miniexp_nil;
2901  miniexp_t p = document->protect;
2902  while (miniexp_consp(p))
2903    {
2904      if (miniexp_car(p) != expr)
2905        q = p;
2906      else if (q)
2907        miniexp_rplacd(q, miniexp_cdr(p));
2908      else
2909        document->protect = miniexp_cdr(p);
2910      p = miniexp_cdr(p);
2911    }
2912}
2913
2914
2915
2916// ----------------------------------------
2917// S-Expressions (outline)
2918
2919static miniexp_t
2920outline_sub(const GP<DjVmNav> &nav, int &pos, int count)
2921{
2922  GP<DjVmNav::DjVuBookMark> entry;
2923  minivar_t p,q,s;
2924  while (count > 0 && pos < nav->getBookMarkCount())
2925    {
2926      nav->getBookMark(entry, pos++);
2927      q = outline_sub(nav, pos, entry->count);
2928      s = miniexp_string((const char*)(entry->url));
2929      q = miniexp_cons(s, q);
2930      s = miniexp_string((const char*)(entry->displayname));
2931      q = miniexp_cons(s, q);
2932      p = miniexp_cons(q, p);
2933      count--;
2934    }
2935  return miniexp_reverse(p);
2936}
2937
2938miniexp_t
2939ddjvu_document_get_outline(ddjvu_document_t *document)
2940{
2941  G_TRY
2942    {
2943      ddjvu_status_t status = document->status();
2944      if (status != DDJVU_JOB_OK)
2945        return miniexp_status(status);
2946      DjVuDocument *doc = document->doc;
2947      if (doc)
2948        {
2949          GP<DjVmNav> nav = doc->get_djvm_nav();
2950          if (! nav) 
2951            return miniexp_nil;
2952          minivar_t result;
2953          int pos = 0;
2954          result = outline_sub(nav, pos, nav->getBookMarkCount());
2955          result = miniexp_cons(miniexp_symbol("bookmarks"), result);
2956          miniexp_protect(document, result);
2957          return result;
2958        }
2959    }
2960  G_CATCH(ex)
2961    {
2962      ERROR1(document, ex);
2963    }
2964  G_ENDCATCH;
2965  return miniexp_status(DDJVU_JOB_FAILED);
2966}
2967
2968
2969// ----------------------------------------
2970// S-Expressions (text)
2971
2972static struct zone_names_s {
2973  char *name;
2974  DjVuTXT::ZoneType ztype;
2975  char separator;
2976} zone_names[] = {
2977  { "page",   DjVuTXT::PAGE,      0 },
2978  { "column", DjVuTXT::COLUMN,    DjVuTXT::end_of_column },
2979  { "region", DjVuTXT::REGION,    DjVuTXT::end_of_region },
2980  { "para",   DjVuTXT::PARAGRAPH, DjVuTXT::end_of_paragraph },
2981  { "line",   DjVuTXT::LINE,      DjVuTXT::end_of_line },
2982  { "word",   DjVuTXT::WORD,      ' ' },
2983  { "char",   DjVuTXT::CHARACTER, 0 },
2984  { 0, (DjVuTXT::ZoneType)0 ,0 }
2985};
2986
2987static miniexp_t
2988pagetext_sub(const GP<DjVuTXT> &txt, DjVuTXT::Zone &zone, 
2989             DjVuTXT::ZoneType detail)
2990{
2991  int zinfo;
2992  for (zinfo=0; zone_names[zinfo].name; zinfo++)
2993    if (zone.ztype == zone_names[zinfo].ztype)
2994      break;
2995  minivar_t p;
2996  minivar_t a;
2997  bool gather = zone.children.isempty();
2998  { // extra nesting for windows
2999    for (GPosition pos=zone.children; pos; ++pos)
3000      if (zone.children[pos].ztype > detail)
3001        gather = true;
3002  }
3003  if (gather)
3004    {
3005      const char *data = (const char*)(txt->textUTF8) + zone.text_start;
3006      int length = zone.text_length;
3007      if (length>0 && data[length-1]==zone_names[zinfo].separator)
3008        length -= 1;
3009      a = miniexp_substring(data, length);
3010      p = miniexp_cons(a, p);
3011    }
3012  else
3013    {
3014      for (GPosition pos=zone.children; pos; ++pos)
3015        {
3016          a = pagetext_sub(txt, zone.children[pos], detail);
3017          p = miniexp_cons(a, p);
3018        }
3019    }
3020  p = miniexp_reverse(p);
3021  const char *s = zone_names[zinfo].name;
3022  if (s)
3023    {
3024      p = miniexp_cons(miniexp_number(zone.rect.ymax), p);
3025      p = miniexp_cons(miniexp_number(zone.rect.xmax), p);
3026      p = miniexp_cons(miniexp_number(zone.rect.ymin), p);
3027      p = miniexp_cons(miniexp_number(zone.rect.xmin), p);
3028      p = miniexp_cons(miniexp_symbol(s), p);
3029      return p;
3030    }
3031  return miniexp_nil;
3032}
3033
3034miniexp_t
3035ddjvu_document_get_pagetext(ddjvu_document_t *document, int pageno,
3036                            const char *maxdetail)
3037{
3038  G_TRY
3039    {
3040      DjVuDocument *doc = document->doc;
3041      if (doc)
3042        {
3043          document->pageinfoflag = true;
3044          GP<DjVuFile> file = doc->get_djvu_file(pageno);
3045          if (! file || ! file->is_data_present() )
3046            return miniexp_dummy;
3047          GP<ByteStream> bs = file->get_text();
3048          if (! bs)
3049            return miniexp_nil;
3050          GP<DjVuText> text = DjVuText::create();
3051          text->decode(bs);
3052          GP<DjVuTXT> txt = text->txt;
3053          if (! txt)
3054            return miniexp_nil;
3055          minivar_t result;
3056          DjVuTXT::ZoneType detail = DjVuTXT::CHARACTER;
3057          { // extra nesting for windows
3058            for (int i=0; zone_names[i].name; i++)
3059              if (maxdetail && !strcmp(maxdetail, zone_names[i].name))
3060                detail = zone_names[i].ztype;
3061          }
3062          result = pagetext_sub(txt, txt->page_zone, detail);
3063          miniexp_protect(document, result);
3064          return result;
3065        }
3066    }
3067  G_CATCH(ex)
3068    {
3069      ERROR1(document, ex);
3070    }
3071  G_ENDCATCH;
3072  return miniexp_status(DDJVU_JOB_FAILED);
3073}
3074
3075
3076// ----------------------------------------
3077// S-Expressions (annotations)
3078
3079// The difficulty here lies with the syntax of strings in annotation chunks.
3080// - Early versions of djvu only had one possible escape
3081//   sequence (\") in annotation strings. All other characters
3082//   are accepted literally until reaching the closing double quote.
3083// - Current versions of djvu understand the usual backslash escapes.
3084//   All non printable ascii characters must however be escaped.
3085//   This is a subset of the miniexp syntax.
3086// We first check if strings in the annotation chunk obey the modern syntax.
3087// The compatibility mode is turned on if they contain non printable ascii
3088// characters or illegal backslash sequences. Function <anno_getc()> then
3089// creates the proper escapes on the fly.
3090
3091static struct {
3092  const char *s;
3093  char buf[8];
3094  int  blen;
3095  int  state;
3096  bool compat;
3097  bool eof;
3098} anno_dat;
3099
3100static bool
3101anno_compat(const char *s)
3102{
3103  int state = 0;
3104  bool compat = false;
3105  while (s && *s && !compat)
3106    {
3107      int i = (int)(unsigned char)*s++;
3108      switch(state)
3109        {
3110        case 0:
3111          if (i == '\"')
3112            state = '\"';
3113          break;
3114        case '\"':
3115          if (i == '\"')
3116            state = 0;
3117          else if (i == '\\')
3118            state = '\\';
3119          else if (isascii(i) && !isprint(i))
3120            compat = true;
3121          break;
3122        case '\\':
3123          if (!strchr("01234567abtnvfr\"\\",i))
3124            compat = true;
3125          state = '\"';
3126          break;
3127        }
3128    }
3129  return compat;
3130}
3131
3132static int
3133anno_getc(void)
3134{
3135  if (anno_dat.blen>0)
3136    {
3137      anno_dat.blen--;
3138      char c = anno_dat.buf[0];
3139      { // extra nesting for windows
3140        for (int i=0; i<anno_dat.blen; i++)
3141          anno_dat.buf[i] = anno_dat.buf[i+1];
3142      }
3143      return c;
3144    }
3145  if (! *anno_dat.s)
3146    return EOF;
3147  int c = (int)(unsigned char)*anno_dat.s++;
3148  if (anno_dat.compat)
3149    {
3150      switch (anno_dat.state)
3151        {
3152        case 0:
3153          if (c == '\"') 
3154            anno_dat.state = '\"';
3155          break;
3156        case '\"':
3157          if (c == '\"') 
3158            anno_dat.state = 0;
3159          else if (c == '\\')
3160            anno_dat.state = '\\';
3161          else if (isascii(c) && !isprint(c))
3162            {
3163              sprintf(anno_dat.buf,"%03o", c);
3164              anno_dat.blen = strlen(anno_dat.buf);
3165              c = '\\';
3166            }
3167          break;
3168        case '\\':
3169          anno_dat.state = '\"';
3170          if (c != '\"')
3171            {
3172              sprintf(anno_dat.buf,"\\%03o", c);
3173              anno_dat.blen = strlen(anno_dat.buf);
3174              c = '\\';
3175            }
3176          break;
3177        }
3178    }
3179  return c;
3180}
3181
3182static int
3183anno_ungetc(int c)
3184{
3185  if (c == EOF)
3186    return EOF;
3187  if (anno_dat.blen>=(int)sizeof(anno_dat.buf))
3188    return EOF;
3189  { // extra nesting for windows
3190    for (int i=anno_dat.blen; i>0; i--)
3191      anno_dat.buf[i] = anno_dat.buf[i-1];
3192  }
3193  anno_dat.blen += 1;
3194  anno_dat.buf[0] = c;
3195  return c;
3196}
3197
3198static void
3199anno_sub(ByteStream *bs, miniexp_t &result)
3200{
3201  // Read bs
3202  GUTF8String raw;
3203  char buffer[1024];
3204  int length;
3205  while ((length=bs->read(buffer, sizeof(buffer))))
3206    raw += GUTF8String(buffer, length);
3207  // Prepare
3208  miniexp_t a;
3209  anno_dat.s = (const char*)raw;
3210  anno_dat.compat = anno_compat(anno_dat.s);
3211  anno_dat.blen = 0;
3212  anno_dat.state = 0;
3213  anno_dat.eof = false;
3214  int (*saved_getc)(void) = minilisp_getc;
3215  int (*saved_ungetc)(int) = minilisp_ungetc;
3216  // Process
3217  minilisp_getc = anno_getc;
3218  minilisp_ungetc = anno_ungetc;
3219  while (* anno_dat.s )
3220    if ((a = miniexp_read()) != miniexp_dummy)
3221      result = miniexp_cons(a, result);
3222  // Restore
3223  minilisp_getc = saved_getc;
3224  minilisp_ungetc = saved_ungetc;
3225}
3226
3227miniexp_t
3228ddjvu_document_get_pageanno(ddjvu_document_t *document, int pageno)
3229{
3230  G_TRY
3231    {
3232      DjVuDocument *doc = document->doc;
3233      if (doc)
3234        {
3235          document->pageinfoflag = true;
3236          GP<DjVuFile> file = doc->get_djvu_file(pageno);
3237          // Make sure all data is present
3238          if (! file || ! file->is_all_data_present())
3239            {
3240              if (file->is_data_present())
3241                {
3242                  if (! file->are_incl_files_created())
3243                    file->process_incl_chunks();
3244                  if (! file->are_incl_files_created())
3245                    return miniexp_status(DDJVU_JOB_FAILED);
3246                }
3247              return miniexp_dummy;
3248            }
3249          // Access annotation data
3250          GP<ByteStream> annobs = file->get_merged_anno();
3251          if (! (annobs && annobs->size()))
3252            return miniexp_nil;
3253          minivar_t result;
3254          GP<IFFByteStream> iff = IFFByteStream::create(annobs);
3255          GUTF8String chkid;
3256          while (iff->get_chunk(chkid))
3257            {
3258              GP<ByteStream> bs;
3259              if (chkid == "ANTa") 
3260                bs = iff->get_bytestream();
3261              else if (chkid == "ANTz")
3262                bs = BSByteStream::create(iff->get_bytestream());
3263              if (bs)
3264                anno_sub(bs, result);
3265              iff->close_chunk();
3266            }
3267          result = miniexp_reverse(result);
3268          miniexp_protect(document, result);
3269          return result;
3270        }
3271    }
3272  G_CATCH(ex)
3273    {
3274      ERROR1(document, ex);
3275    }
3276  G_ENDCATCH;
3277  return miniexp_status(DDJVU_JOB_FAILED);
3278}
3279
3280
3281
3282/* ------ helpers for annotations ---- */
3283
3284static const char *
3285simple_anno_sub(miniexp_t p, miniexp_t s, int i)
3286{
3287  const char *result = 0;
3288  while (miniexp_consp(p))
3289    {
3290      miniexp_t a = miniexp_car(p);
3291      p = miniexp_cdr(p);
3292      if (miniexp_car(a) == s)
3293        {
3294          miniexp_t q = miniexp_nth(i, a);
3295          if (miniexp_symbolp(q))
3296            result = miniexp_to_name(q);
3297        }
3298    }
3299  return result;
3300}
3301
3302const char *
3303ddjvu_anno_get_bgcolor(miniexp_t p)
3304{
3305  return simple_anno_sub(p, miniexp_symbol("background"), 1);
3306}
3307
3308const char *
3309ddjvu_anno_get_zoom(miniexp_t p)
3310{
3311  return simple_anno_sub(p, miniexp_symbol("zoom"), 1);
3312}
3313
3314const char *
3315ddjvu_anno_get_mode(miniexp_t p)
3316{
3317  return simple_anno_sub(p, miniexp_symbol("mode"), 1);
3318}
3319
3320const char *
3321ddjvu_anno_get_horizalign(miniexp_t p)
3322{
3323  return simple_anno_sub(p, miniexp_symbol("align"), 1);
3324}
3325
3326const char *
3327ddjvu_anno_get_vertalign(miniexp_t p)
3328{
3329  return simple_anno_sub(p, miniexp_symbol("align"), 2);
3330}
3331
3332miniexp_t *
3333ddjvu_anno_get_hyperlinks(miniexp_t annotations)
3334{
3335  miniexp_t p;
3336  miniexp_t s_maparea = miniexp_symbol("maparea");
3337  int i = 0;
3338  for (p = annotations; miniexp_consp(p); p = miniexp_cdr(p))
3339    if (miniexp_caar(p) == s_maparea)
3340      i += 1;
3341  miniexp_t *k = (miniexp_t*)malloc((1+i)*sizeof(miniexp_t));
3342  if (! k) return 0;
3343  i = 0;
3344  for (p = annotations; miniexp_consp(p); p = miniexp_cdr(p))
3345    if (miniexp_caar(p) == s_maparea)
3346      k[i++] = miniexp_car(p);
3347  k[i] = 0;
3348  return k;
3349}
3350
3351static void
3352metadata_sub(miniexp_t p, GMap<miniexp_t,miniexp_t> &m)
3353{
3354  miniexp_t s_metadata = miniexp_symbol("metadata");
3355  while (miniexp_consp(p))
3356    {
3357      if (miniexp_caar(p) == s_metadata)
3358        {
3359          miniexp_t q = miniexp_cdar(p);
3360          while (miniexp_consp(q))
3361            {
3362              miniexp_t a = miniexp_car(q);
3363              q = miniexp_cdr(q);
3364              if (miniexp_consp(a) && 
3365                  miniexp_symbolp(miniexp_car(a)) &&
3366                  miniexp_stringp(miniexp_cadr(a)) )
3367                {
3368                  m[miniexp_car(a)] = miniexp_cadr(a);
3369                }
3370            }
3371        }
3372      p = miniexp_cdr(p);
3373    }
3374}
3375
3376miniexp_t *
3377ddjvu_anno_get_metadata_keys(miniexp_t p)
3378{
3379  minivar_t l;
3380  GMap<miniexp_t,miniexp_t> m;
3381  metadata_sub(p, m);
3382  int i = m.size();
3383  miniexp_t *k = (miniexp_t*)malloc((1+i)*sizeof(miniexp_t));
3384  if (! k) return 0;
3385  i = 0;
3386  { // extra nesting for windows
3387    for (GPosition p=m; p; ++p)
3388      k[i++] = m.key(p);
3389  }
3390  k[i] = 0;
3391  return k;
3392}
3393
3394const char *
3395ddjvu_anno_get_metadata(miniexp_t p, miniexp_t key)
3396{
3397  GMap<miniexp_t,miniexp_t> m;
3398  metadata_sub(p, m);
3399  if (m.contains(key))
3400    return miniexp_to_str(m[key]);
3401  return 0;
3402}
3403
3404
3405// ----------------------------------------
3406// Backdoors
3407
3408GP<DjVuImage>
3409ddjvu_get_DjVuImage(ddjvu_page_t *page)
3410{
3411  return page->img;
3412}
3413
3414
3415GP<DjVuDocument>
3416ddjvu_get_DjVuDocument(ddjvu_document_t *document)
3417{
3418  return document->doc;
3419}
3420
3421
Note: See TracBrowser for help on using the repository browser.