source: trunk/libdjvu/ddjvuapi.cpp @ 15

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

needed libs update

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