summaryrefslogtreecommitdiffstats
path: root/kghostview
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit47d455dd55be855e4cc691c32f687f723d9247ee (patch)
tree52e236aaa2576bdb3840ebede26619692fed6d7d /kghostview
downloadtdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.tar.gz
tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegraphics@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kghostview')
-rw-r--r--kghostview/AUTHORS9
-rw-r--r--kghostview/ChangeLog40
-rw-r--r--kghostview/Makefile.am69
-rw-r--r--kghostview/README27
-rw-r--r--kghostview/TODO41
-rw-r--r--kghostview/configuration.kcfgc4
-rw-r--r--kghostview/data/Makefile.am3
-rw-r--r--kghostview/data/pdf_sec.ps386
-rw-r--r--kghostview/displayoptions.cpp150
-rw-r--r--kghostview/displayoptions.h112
-rw-r--r--kghostview/dscparse.cpp3432
-rw-r--r--kghostview/dscparse.h473
-rw-r--r--kghostview/dscparse_adapter.cpp420
-rw-r--r--kghostview/dscparse_adapter.h386
-rw-r--r--kghostview/fullscreenfilter.cpp54
-rw-r--r--kghostview/fullscreenfilter.h44
-rw-r--r--kghostview/generalsettingswidget.ui111
-rw-r--r--kghostview/generalsettingswidget.ui.h9
-rw-r--r--kghostview/gssettingswidget.ui158
-rw-r--r--kghostview/gssettingswidget.ui.h14
-rw-r--r--kghostview/hi128-app-kghostview.pngbin0 -> 15812 bytes
-rw-r--r--kghostview/hi16-app-kghostview.pngbin0 -> 991 bytes
-rw-r--r--kghostview/hi22-app-kghostview.pngbin0 -> 1247 bytes
-rw-r--r--kghostview/hi32-app-kghostview.pngbin0 -> 2218 bytes
-rw-r--r--kghostview/hi48-app-kghostview.pngbin0 -> 3910 bytes
-rw-r--r--kghostview/hi64-app-kghostview.pngbin0 -> 6493 bytes
-rw-r--r--kghostview/infodialog.cpp131
-rw-r--r--kghostview/infodialog.h44
-rw-r--r--kghostview/kdscerrordialog.cpp170
-rw-r--r--kghostview/kdscerrordialog.h74
-rw-r--r--kghostview/kghostview.desktop86
-rw-r--r--kghostview/kghostview.kcfg77
-rw-r--r--kghostview/kghostview.upd13
-rw-r--r--kghostview/kghostview_part.desktop19
-rw-r--r--kghostview/kghostviewui.rc43
-rw-r--r--kghostview/kgv.h15
-rw-r--r--kghostview/kgv_miniwidget.cpp573
-rw-r--r--kghostview/kgv_miniwidget.h173
-rw-r--r--kghostview/kgv_part.rc110
-rw-r--r--kghostview/kgv_view.cpp1037
-rw-r--r--kghostview/kgv_view.h268
-rw-r--r--kghostview/kgvconfigdialog.cpp154
-rw-r--r--kghostview/kgvconfigdialog.h32
-rw-r--r--kghostview/kgvdocument.cpp864
-rw-r--r--kghostview/kgvdocument.h195
-rw-r--r--kghostview/kgvfactory.cpp105
-rw-r--r--kghostview/kgvfactory.h50
-rw-r--r--kghostview/kgvmainwidget.cpp50
-rw-r--r--kghostview/kgvmainwidget.h45
-rw-r--r--kghostview/kgvpagedecorator.cpp106
-rw-r--r--kghostview/kgvpagedecorator.h81
-rw-r--r--kghostview/kgvpageview.cpp254
-rw-r--r--kghostview/kgvpageview.h105
-rw-r--r--kghostview/kgvshell.cpp370
-rw-r--r--kghostview/kgvshell.h92
-rw-r--r--kghostview/kpswidget.cpp530
-rw-r--r--kghostview/kpswidget.h387
-rw-r--r--kghostview/logwindow.cpp76
-rw-r--r--kghostview/logwindow.h55
-rw-r--r--kghostview/main.cpp71
-rw-r--r--kghostview/marklist.cpp242
-rw-r--r--kghostview/marklist.h88
-rw-r--r--kghostview/part_init.cpp22
-rw-r--r--kghostview/ps.c188
-rw-r--r--kghostview/ps.h44
-rw-r--r--kghostview/scrollbox.cpp129
-rw-r--r--kghostview/scrollbox.h60
-rw-r--r--kghostview/thumbnailservice.cpp161
-rw-r--r--kghostview/thumbnailservice.h92
-rw-r--r--kghostview/update-to-xt-names.pl36
-rw-r--r--kghostview/version.h4
-rw-r--r--kghostview/viewcontrol.cpp194
-rw-r--r--kghostview/viewcontrol.h44
73 files changed, 13701 insertions, 0 deletions
diff --git a/kghostview/AUTHORS b/kghostview/AUTHORS
new file mode 100644
index 00000000..001afaa8
--- /dev/null
+++ b/kghostview/AUTHORS
@@ -0,0 +1,9 @@
+Copyright (C) 1997-1998 Mark Donohoe <donohoe@kde.org>
+Copyright (C) 2000 Daniel Duley <mosfet@kde.org>
+Copyright (C) 2000-2002 Wilco Greven <greven@kde.org>
+Copyright (C) 1997 Markkhu Hihnala <mah@ee.oulu.fi>
+Copyright (C) 2000 Espen Sand <espen@kde.org>
+Copyright (C) 1999-2000 David Sweet <dsweet@kde.org>
+Copyright (C) 2002-2003 Luís-Pedro Coelho <luis@luispedro.org>
+
+KGhostView is based on original work by Tim Theisen.
diff --git a/kghostview/ChangeLog b/kghostview/ChangeLog
new file mode 100644
index 00000000..3695036c
--- /dev/null
+++ b/kghostview/ChangeLog
@@ -0,0 +1,40 @@
+Version 0.8:
+ * [Mario Weilguni]
+ Made it work with Qt 2.0
+ Fixed a lot of layout problems
+ Fixed segfault caused by wrong accelerator in ::changeAccelerators()
+ There are still a few problems with changed color handling,
+ currently outcommented (look for "TODO" if you wish to fix it)
+
+Version 0.7:
+ * [Mark Donohoe] Patch from Jake Hamy <jehamby@lightside.com> which
+ incorporates code from Johanes Plass' GV. This adds
+ (1) Compressed file support, (2) PDF support
+ ( Currently limited by ghostscript's poor handling of
+ this format )
+ * [Mark Donohoe] Fixed major performance bug which caused gs to be
+ repeatedly restarted
+ * [Mark Donohoe] Improved print dialog
+ - Print to file
+ - Print all, current, marked, or range of pages
+ - Setup spooler command, printer name
+ - Reverse order of printing
+ * [Mark Donohoe] Much better keyboard support
+ * [Mark Donohoe] Improved geometry management
+ * [Mark Donohoe] Improved warning and error dialogs
+
+Version 0.6.3:
+ * [Mark Donohoe] Session management
+ * [Mark Donohoe] Patch from Markus Duda <Duda@physik.rwth-aachen.de> for
+ pagelist.
+ ( Fixes a problem with documents that have named rather
+ than numbered pages I believe )
+ * [Mark Donohoe] Imporvements to page list interface
+ * [Mark Donohoe] Fixed mix up of Magnification and Orientation on
+ View Control dialog
+ * [Mark Donohoe] Few geometry management fixes
+ * [Mark Donohoe] New icon
+
+Version 0.6.1:
+ * [Robert Williams] Added version.h and ChangeLog
+ * [Robert Williams] Added -caption "%c" to kghostview.kdelnk
diff --git a/kghostview/Makefile.am b/kghostview/Makefile.am
new file mode 100644
index 00000000..93209382
--- /dev/null
+++ b/kghostview/Makefile.am
@@ -0,0 +1,69 @@
+SUBDIRS = data
+
+INCLUDES= $(all_includes)
+
+####### Files
+
+bin_PROGRAMS = kghostview
+lib_LTLIBRARIES = libkghostviewlib.la
+kde_module_LTLIBRARIES = libkghostviewpart.la
+noinst_LTLIBRARIES = libdscparse.la
+
+libkghostviewlib_la_LDFLAGS = $(all_libraries)
+libkghostviewlib_la_LIBADD = $(LIB_KFILE) $(LIB_KPARTS) -lkdeprint libdscparse.la
+
+libkghostviewpart_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+libkghostviewpart_la_LIBADD = libkghostviewlib.la
+
+# Check "make final" after making changes to the following line!!
+libkghostviewlib_la_SOURCES = kgvshell.cpp kgvdocument.cpp kgv_miniwidget.cpp \
+ marklist.cpp logwindow.cpp infodialog.cpp \
+ kgvpageview.cpp ps.c kgv_view.cpp scrollbox.cpp kgvpagedecorator.cpp \
+ kgvconfigdialog.cpp kgvmainwidget.cpp \
+ kdscerrordialog.cpp displayoptions.cpp kpswidget.cpp \
+ fullscreenfilter.cpp kgvfactory.cpp \
+ generalsettingswidget.ui gssettingswidget.ui thumbnailservice.cpp \
+ configuration.kcfgc
+
+libkghostviewpart_la_SOURCES = part_init.cpp
+
+kghostview_SOURCES = main.cpp
+kghostview_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+kghostview_LDADD = libkghostviewlib.la
+
+libdscparse_la_LDFLAGS = $(all_libraries) -no-undefined
+libdscparse_la_LIBADD = $(LIB_QT)
+libdscparse_la_SOURCES = dscparse.cpp dscparse_adapter.cpp
+
+noinst_HEADERS = marklist.h logwindow.h infodialog.h kgvshell.h \
+ kpswidget.h kgvpageview.h ps.h kgv_miniwidget.h kgv_view.h scrollbox.h \
+ kgvpagedecorator.h kgvconfigdialog.h kgvmainwidget.h dscparse.h \
+ dscparse_adapter.h kdscerrordialog.h kgvdocument.h displayoptions.h \
+ fullscreenfilter.h kgvfactory.h thumbnailservice.h
+
+METASOURCES = AUTO
+EXTRA_DIST = kghostview.desktop
+
+KDE_ICON = kghostview
+
+xdg_apps_DATA = kghostview.desktop
+kde_kcfg_DATA = kghostview.kcfg
+
+partdir = $(kde_datadir)/kghostview
+part_DATA = kgv_part.rc kghostviewui.rc
+
+partdesktopdir = $(kde_servicesdir)
+partdesktop_DATA = kghostview_part.desktop
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp *.h -o $(podir)/kghostview.pot
+
+
+updatedir = $(kde_datadir)/kconf_update
+update_DATA = kghostview.upd
+update_SCRIPTS = update-to-xt-names.pl
+
+# check_PROGRAMS = testdsc
+
+# testdsc_SOURCES = testdsc.cpp kdsc.cpp ps.c
+# testdsc_LDADD = -lqt
diff --git a/kghostview/README b/kghostview/README
new file mode 100644
index 00000000..eb92b02b
--- /dev/null
+++ b/kghostview/README
@@ -0,0 +1,27 @@
+KGHOSTVIEW for KDE 3.x
+
+This is Tim Theisen's Ghostview program ported to the K
+Desktop Environment. Ghostscript is used to view PostScript
+documents on Unix X11 systems.
+
+REQUIREMENTS
+
+1. GNU ghostscript
+2. KDE 3.x
+
+If ghostscript works for you then so should KGhostview. ghostscript can be obtained
+at http://www.cs.wisc.edu/~ghost/.
+
+Due to security reasons, kghostview works only with recent versions of ghostscript.
+Versions greater or equal to than GNU version 6.53 or GNU version 7.05 should work.
+
+Other versions or implementation of ghostscript may or may not work. In the
+configuration dialog, found under the Settings menu in the app, it
+is possible to configure the interpreter to use.
+
+Send bugs via http://bugs.kde.org
+
+CURRENT MAINTANER
+
+Luís-Pedro Coelho <luis_pedro@netcabo.pt>
+
diff --git a/kghostview/TODO b/kghostview/TODO
new file mode 100644
index 00000000..f747ff13
--- /dev/null
+++ b/kghostview/TODO
@@ -0,0 +1,41 @@
+- Port to KViewShell / KMultiPage
+
+- Progress bar(s) when loading/converting (the latter can be very slow).
+
+- Find dialog.
+
+- Different icons for Read Up and Read Down.
+
+- Use pdftops which comes with xpdf for pdf->ps conversion. It's output
+ is much better, and it allows the conversion of selected pages, which is
+ important when you want to print a small number of pages from a large
+ document.
+ I don't think I'll switch to pdftops. Ghostscript also has the possibility
+ to convert selected pages only. And for printing its output is fine.
+
+- Provide more information via statusbar messages. See gsview, for how it
+ should be.
+
+- Move the page navigation options from the View menu to the Go menu.
+ Currently Konqueror doesn't merge the Go menu, so that should be fixed first.
+
+- Fit page to view-width, auto-zoom on widget resize.
+
+- Better handling of Orientation and Paper Size choice.
+
+- Continuous and Continuous Facing viewing of pages, like Acroread.
+
+- Find a good solution which helps the user to keep his eyes focused when
+ scrolling (smooth scrolling or using helper lines like gv).
+
+- Fix all the bugs.
+
+
+Done:
+- Scrollbox should show a thumbnail (like kdvi).
+
+- New implementation for the marklist because QTableView won't be in Qt-3.0
+ anymore. (I'll keep using QTableView, simply because there's no suitable
+ alternative in Qt3.)
+
+- Switch to the DSC parser provided by Ghostscript.
diff --git a/kghostview/configuration.kcfgc b/kghostview/configuration.kcfgc
new file mode 100644
index 00000000..5617f368
--- /dev/null
+++ b/kghostview/configuration.kcfgc
@@ -0,0 +1,4 @@
+File=kghostview.kcfg
+ClassName=Configuration
+Mutators=true
+Singleton=true
diff --git a/kghostview/data/Makefile.am b/kghostview/data/Makefile.am
new file mode 100644
index 00000000..f6a27c16
--- /dev/null
+++ b/kghostview/data/Makefile.am
@@ -0,0 +1,3 @@
+ps_DATA = pdf_sec.ps
+psdir = ${kde_datadir}/kghostview
+
diff --git a/kghostview/data/pdf_sec.ps b/kghostview/data/pdf_sec.ps
new file mode 100644
index 00000000..5fe9598a
--- /dev/null
+++ b/kghostview/data/pdf_sec.ps
@@ -0,0 +1,386 @@
+% Copyright (C) 1996-1998 Geoffrey Keating.
+% This file may be freely distributed with or without modifications,
+% so long as modified versions are marked as such and copyright notices are
+% not removed.
+
+% pdf_sec.ps (version 1.0.4)
+% Implementation of security hooks for PDF reader.
+
+% This file contains the procedures that have to take encryption into
+% account when reading a PDF file. It replaces the stub version of this
+% file that is shipped with GhostScript. It requires GhostScript version 4.02
+% or later.
+
+% Documentation for using this file is available at
+% http://www.ozemail.com.au/%7Egeoffk/pdfencrypt/
+
+/.setlanguagelevel where { pop 2 .setlanguagelevel } if
+.currentglobal true .setglobal
+/pdfdict where { pop } { /pdfdict 100 dict def } ifelse
+pdfdict begin
+
+% Older ghostscript versions do not have .pdftoken, so we use 'token' instead.
+/.pdftoken where { pop } { /.pdftoken /token load def } ifelse
+
+% An implementation of an algorithm compatible with the RSA Data Security
+% Inc. RC4 stream encryption algorithm.
+
+% <string> rc4setkey <dict>
+/rc4setkey
+{
+ 6 dict begin
+ /k exch def
+ /a 256 string def
+ 0 1 255 { a exch dup put } for
+ /l k length def
+ /j 0 def
+ 0 1 255
+ {
+ /i exch def
+ /j a i get k i l mod get add j add 255 and def
+ a i a j get a j a i get put put
+ } for
+ 3 dict dup begin
+ /a a def
+ /x 0 def
+ /y 0 def
+ end
+ end
+} bind def
+
+% <rc4key> <string> rc4 <string> <rc4key>
+/rc4
+{
+ 1 index begin
+ dup dup length 1 sub 0 exch 1 exch
+ {
+ /x x 1 add 255 and def
+ /y a x get y add 255 and def
+ a x a y get a y a x get put put
+% stack: string string index
+ 2 copy get
+ a dup x get a y get add 255 and get
+ xor put dup
+ } for
+ pop
+ end
+} bind def
+
+% take a stream and rc4 decrypt it.
+% <stream> <key> rc4decodefilter <stream>
+/rc4decodefilter {
+ currentglobal exch true setglobal
+ dup length string copy rc4setkey
+ exch setglobal
+ exch 512 string
+ % stack: <key> <stream> <string>
+ { readstring pop rc4 exch pop } aload pop
+ 8 array astore cvx 0 () /SubFileDecode filter
+} bind def
+
+% MD5 derived from RFC 1321, "The MD5 Message-Digest Algorithm",
+% R. Rivest, MIT, RSADSI; implemented in PostScript by Geoffrey Keating.
+
+% We construct the MD5 transform by a sort of inline expansion.
+% this takes up quite a bit of memory (around 17k), but gives a
+% factor-of-two speed increase.
+% This also allows us to take advantage of interpreters with 64-bit
+% wide integers.
+% This will not run on interpreters with 16-bit wide integers, if such
+% things exist.
+20 dict begin
+
+/T [
+16#d76aa478 16#e8c7b756 16#242070db 16#c1bdceee
+16#f57c0faf 16#4787c62a 16#a8304613 16#fd469501
+16#698098d8 16#8b44f7af 16#ffff5bb1 16#895cd7be
+16#6b901122 16#fd987193 16#a679438e 16#49b40821
+16#f61e2562 16#c040b340 16#265e5a51 16#e9b6c7aa
+16#d62f105d 16#02441453 16#d8a1e681 16#e7d3fbc8
+16#21e1cde6 16#c33707d6 16#f4d50d87 16#455a14ed
+16#a9e3e905 16#fcefa3f8 16#676f02d9 16#8d2a4c8a
+16#fffa3942 16#8771f681 16#6d9d6122 16#fde5380c
+16#a4beea44 16#4bdecfa9 16#f6bb4b60 16#bebfbc70
+16#289b7ec6 16#eaa127fa 16#d4ef3085 16#04881d05
+16#d9d4d039 16#e6db99e5 16#1fa27cf8 16#c4ac5665
+16#f4292244 16#432aff97 16#ab9423a7 16#fc93a039
+16#655b59c3 16#8f0ccc92 16#ffeff47d 16#85845dd1
+16#6fa87e4f 16#fe2ce6e0 16#a3014314 16#4e0811a1
+16#f7537e82 16#bd3af235 16#2ad7d2bb 16#eb86d391
+] def
+/F [
+{ c d /xor b /and d /xor } { b c /xor d /and c /xor }
+{ b c /xor d /xor } { d /not b /or c /xor }
+] def
+/R [
+16#0007 16#010c 16#0211 16#0316 16#0407 16#050c 16#0611 16#0716
+16#0807 16#090c 16#0a11 16#0b16 16#0c07 16#0d0c 16#0e11 16#0f16
+16#0105 16#0609 16#0b0e 16#0014 16#0505 16#0a09 16#0f0e 16#0414
+16#0905 16#0e09 16#030e 16#0814 16#0d05 16#0209 16#070e 16#0c14
+16#0504 16#080b 16#0b10 16#0e17 16#0104 16#040b 16#0710 16#0a17
+16#0d04 16#000b 16#0310 16#0617 16#0904 16#0c0b 16#0f10 16#0217
+16#0006 16#070a 16#0e0f 16#0515 16#0c06 16#030a 16#0a0f 16#0115
+16#0806 16#0f0a 16#060f 16#0d15 16#0406 16#0b0a 16#020f 16#0915
+] def
+
+/W 1 31 bitshift 0 gt def
+/A W { /add } { /md5add } ifelse def
+/t W { 1744 } { 1616 } ifelse array def
+/C 0 def
+
+0 1 63 {
+ /i exch def
+ /r R i get def
+ /a/b/c/d 4 i 3 and roll [ /d/c/b/a ] { exch def } forall
+
+ t C [
+ a F i -4 bitshift get exec
+ a A /x r -8 bitshift /get A T i get A
+ W { 1 32 bitshift 1 sub /and } if
+ /dup r 31 and /bitshift /exch r 31 and 32 sub /bitshift /or
+ b A
+ /def
+ ] dup length C add /C exch def putinterval
+} for
+
+1 1 C 1 sub {
+ dup 1 sub t exch get /def cvx eq
+ {pop}
+ {t exch 2 copy get cvx put}
+ ifelse
+} for
+
+% If we could put t into a _packed_ array, its memory requirements
+% would go from about 13k to about 4k. Unfortunately, we'd need around
+% 1600 stack positions, around 3 times what we can expect to have
+% available---and if that kind of memory is available, we don't really
+% need to pack t. Sigh.
+
+% In fact, it's worse than that. You can't even determine what t will
+% be and write it in directly (something like
+% { /a c d xor b and d xor a md5add x 0 get md5add -680876936 md5add dup 7
+% bitshift exch -25 bitshift or b md5add def /d b c xor a ...
+% ) because the scanner uses the operand stack to accumulate procedures.
+% So the only way to have md5transform as a single procedure is the above
+% trick.
+
+W /md5transform t end cvx bind def
+
+% Unfortunately, PostScript & its imitators convert large
+% integers to floating-point. Worse, the fp representation probably
+% won't have 32 significant bits.
+% This procedure accounts for about 35% of the total time on 32-bit
+% machines.
+not {
+ /md5add {
+ 2 copy xor 0 lt
+ % if one is positive and one is negative, can't overflow
+ { add }
+ % if both are positive or negative
+ { 16#80000000 xor add 16#80000000 xor }
+ % same as subtracting (or adding) 2^31 and then subtracting (or
+ % adding) it back.
+ ifelse
+ } bind def
+} {
+ /md5add {
+ add 16#0FFFFFFFF and
+ } bind def
+} ifelse
+
+/md5 {
+ 20 dict begin
+
+ % initialise a,b,c,d,x
+ /a 16#67452301 def
+ /b 16#efcdab89 def
+ /c 16#98badcfe def
+ /d 16#10325476 def
+ /x 16 array def
+
+ % parameters
+ /origs exch def
+ /oslen origs length def
+
+ % pad string to multiple of 512 bits
+ /s oslen 72 add 64 idiv 64 mul dup /slen exch def string def
+ s 0 origs putinterval
+ s oslen 16#80 put
+ s slen 8 sub oslen 31 and 3 bitshift put
+ s slen 7 sub oslen -5 bitshift 255 and put
+ s slen 6 sub oslen -13 bitshift 255 and put
+
+ 0 64 slen 64 sub {
+ dup 1 exch 63 add { s exch get } for
+ 15 -1 0 { x exch 6 2 roll 3 { 8 bitshift or } repeat put } for
+ a b c d
+ md5transform
+ d md5add /d exch def
+ c md5add /c exch def
+ b md5add /b exch def
+ a md5add /a exch def
+ } for
+
+ 16 string
+ [ [ a b c d ] { 3 { dup -8 bitshift } repeat } forall ]
+ 0 1 15 {
+ 3 copy dup 3 1 roll get 255 and put pop
+ } for
+ pop
+
+ end
+} bind def
+
+% Pad a key out to 32 bytes.
+/pdf_pad_key % <key> pdf_pad_key <padded key>
+ { dup length 32 gt { 0 32 getinterval } if
+ <28bf4e5e4e758a41 64004e56fffa0108
+ 2e2e00b6d0683e80 2f0ca9fe6453697a>
+ 0 32 3 index length sub getinterval
+ concatstrings
+ } bind def
+
+% Try a user key for a PDF file.
+/pdf_try_key % <key> pdf_try_key <filekey> true
+ % <key> pdf_try_key false
+ { Trailer /Encrypt oget
+ dup /O oget exch
+ /P oget 4 string exch
+ 2 copy 255 and 0 exch put
+ 2 copy -8 bitshift 255 and 1 exch put
+ 2 copy -16 bitshift 255 and 2 exch put
+ 2 copy -24 bitshift 255 and 3 exch put pop
+ Trailer /ID oget 0 oget
+ 3 { concatstrings } repeat
+ md5 0 5 getinterval
+ dup
+ Trailer /Encrypt oget /U oget dup length string copy exch
+ rc4setkey exch rc4 exch pop
+ <28bf4e5e4e758a41 64004e56fffa0108
+ 2e2e00b6d0683e80 2f0ca9fe6453697a> eq
+ dup not { exch pop } if
+ } bind def
+
+% Process the encryption information in the Trailer.
+/pdf_process_Encrypt
+ { Trailer /Encrypt oget
+ /Filter oget /Standard eq not
+ { (****This file uses an unknown security handler.\n) print flush
+ /pdf_process_Encrypt cvx /undefined signalerror
+ }
+ if
+ () pdf_pad_key pdf_try_key
+ { /FileKey exch def }
+ { /PDFPassword where
+ { pop PDFPassword pdf_pad_key pdf_try_key
+ { true }
+ { PDFPassword pdf_pad_key md5 0 5 getinterval rc4setkey
+ Trailer /Encrypt oget /O oget dup length string copy
+ rc4 exch pop
+ pdf_try_key
+ }
+ ifelse
+ { /FileKey exch def }
+ { (****Password did not work.\n) print flush
+ /pdf_process_Encrypt cvx /invalidfileaccess signalerror
+ }
+ ifelse
+ }
+ { (****This file has a user password set.\n) print flush
+ /pdf_process_Encrypt cvx /invalidfileaccess signalerror
+ }
+ ifelse
+ }
+ ifelse
+ Trailer /Encrypt oget /P oget 4 and 0 eq #? and
+ { (****This owner of this file has requested you do not print it.\n)
+ print flush
+ /pdf_process_Encrypt cvx /invalidfileaccess signalerror
+ }
+ if
+ } bind def
+
+% Calculate the key used to decrypt an object (to pass to .decpdfrun or
+% put into a stream dictionary).
+/computeobjkey % <object#> <generation#> computeobjkey <keystring>
+{
+ exch
+ 10 string
+ dup 0 FileKey putinterval
+ exch
+ % stack: gen# string obj#
+ 2 copy 255 and 5 exch put
+ 2 copy -8 bitshift 255 and 6 exch put
+ 2 copy -16 bitshift 255 and 7 exch put
+ pop exch
+ 2 copy 255 and 8 exch put
+ 2 copy -8 bitshift 255 and 9 exch put
+ pop md5 0 10 getinterval
+} bind def
+
+% As .pdfrun, but decrypt strings with key <key>.
+/.decpdfrun % <file> <keystring> <opdict> .decpdfrun -
+ { % Construct a procedure with the file, opdict and key bound into it.
+ 2 index cvlit mark mark 5 2 roll
+ { .pdftoken not { (%%EOF) cvn cvx } if
+ dup xcheck
+ { DEBUG { dup == flush } if
+ 3 -1 roll pop
+ 2 copy .knownget
+ { exch pop exch pop exec }
+ { (%stderr) (w) file
+ dup (****************Unknown operator: ) writestring
+ dup 3 -1 roll .writecvs dup (\n) writestring flushfile
+ pop
+ }
+ ifelse
+ }
+ { exch pop DEBUG { dup ==only ( ) print flush } if
+ dup type /stringtype eq
+ { exch rc4setkey exch rc4 }
+ if
+ exch pop
+ }
+ ifelse
+ }
+ aload pop .packtomark cvx
+ /loop cvx 2 packedarray cvx
+ { stopped /PDFsource } aload pop
+ PDFsource
+ { store { stop } if } aload pop .packtomark cvx
+ /PDFsource 3 -1 roll store exec
+ } bind def
+
+% Run the code to resolve an object reference.
+/pdf_run_resolve
+ { /FileKey where
+ { pop
+ 2 copy computeobjkey dup 4 1 roll
+ PDFfile exch resolveopdict .decpdfrun
+ dup dup dup 5 2 roll
+ % stack: object object key object object
+ xcheck exch type /dicttype eq and
+ { /StreamKey exch put }
+ { pop pop }
+ ifelse
+ }
+ { PDFfile resolveopdict .pdfrun }
+ ifelse
+ } bind def
+
+% Prefix a decryption filter to a stream if needed.
+% Stack: readdata? dict parms file/string filternames
+% (both before and after).
+/pdf_decrypt_stream
+ { 3 index /StreamKey known
+ {
+ exch
+ % Stack: readdata? dict parms filternames file/string
+ 3 index /Length oget () /SubFileDecode filter
+ 3 index /StreamKey get rc4decodefilter
+ exch
+ } if
+ } bind def
+
+end % pdfdict
+.setglobal
diff --git a/kghostview/displayoptions.cpp b/kghostview/displayoptions.cpp
new file mode 100644
index 00000000..48d45e7e
--- /dev/null
+++ b/kghostview/displayoptions.cpp
@@ -0,0 +1,150 @@
+/**
+ * Copyright (C) 2003, Lus Pedro Coelho
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include "displayoptions.h"
+#include <kdebug.h>
+#include <qregexp.h>
+#include <kcmdlineargs.h>
+#include <kconfig.h>
+
+namespace {
+ const double allowedMagnifications[] = {
+ 0.125,
+ 0.25,
+ 0.3333,
+ 0.5,
+ 0.6667,
+ 0.75,
+ 1,
+ 1.25,
+ 1.50,
+ 2,
+ 3,
+ 4,
+ 6,
+ 8
+ };
+ const size_t numberOfMagnifications = ( sizeof( allowedMagnifications ) / sizeof( allowedMagnifications[ 0 ] ) );
+}
+
+DisplayOptions DisplayOptions::parse( KCmdLineArgs* args )
+{
+ DisplayOptions res;
+#define CHECK_ORIENTATION( tag, value ) \
+ if ( args->isSet( tag ) ) res._overrideOrientation = value; \
+ if ( args->getOption( "orientation" ) == tag ) res._overrideOrientation = value;
+
+ CHECK_ORIENTATION( "landscape", CDSC_LANDSCAPE );
+ CHECK_ORIENTATION( "seascape", CDSC_SEASCAPE );
+ CHECK_ORIENTATION( "portrait", CDSC_PORTRAIT);
+ CHECK_ORIENTATION( "upsidedown", CDSC_UPSIDEDOWN);
+
+ res.setMagnification( args->getOption( "scale" ).toFloat() );
+ res.setPage( args->getOption( "page" ).toInt() - 1 ); // transform from 1-based into 0-based
+ //res._overridePageMedia = args->getOption( "paper" );
+ kdDebug(4500 ) << "Parsed options: " << DisplayOptions::toString( res ) << endl;
+ return res;
+}
+
+namespace {
+ const char* const rformat = ".page: (\\d+); .magnification: ([\\d\\.]+); .orientation = (\\d+); .media = ([^;]*);";
+ const char* const qformat = ".page: %1; .magnification: %2; .orientation = %3; .media = %4;";
+}
+
+QString DisplayOptions::toString( const DisplayOptions& options )
+{
+ return QString( qformat )
+ .arg( options.page() )
+ .arg( options.magnification() )
+ .arg( options.overrideOrientation() )
+ .arg( options.overridePageMedia().utf8() );
+}
+
+bool DisplayOptions::fromString( DisplayOptions& out, const QString& in )
+{
+ QRegExp regex( QString::fromLatin1( rformat ) );
+ if ( regex.search( in ) < 0 ) return false;
+
+ out.reset();
+ out.setPage( regex.cap( 1 ).toInt() );
+ out.setMagnification( regex.cap( 2 ).toDouble() );
+ out.setOverrideOrientation( static_cast<CDSC_ORIENTATION_ENUM>( regex.cap( 3 ).toInt() ) );
+ if ( !regex.cap( 4 ).isEmpty() ) out.setOverridePageMedia( regex.cap( 4 ) );
+ return true;
+}
+
+
+bool DisplayOptions::canZoomIn() const
+{
+ return unsigned( closestIndex() ) < ( numberOfMagnifications - 1 );
+}
+
+bool DisplayOptions::zoomIn()
+{
+ if ( !canZoomIn() ) return false;
+ _magnification = allowedMagnifications[ closestIndex() + 1 ];
+ return true;
+}
+
+bool DisplayOptions::canZoomOut() const
+{
+ return closestIndex() > 0;
+}
+
+bool DisplayOptions::zoomOut()
+{
+ if ( !canZoomOut() ) return false;
+ _magnification = allowedMagnifications[ closestIndex() - 1 ];
+ return true;
+}
+
+double DisplayOptions::magnification() const
+{
+ return _magnification;
+}
+
+
+void DisplayOptions::setMagnification( double newZoom ) {
+ _magnification = newZoom;
+}
+
+unsigned DisplayOptions::closestIndex() const {
+ kdDebug(4500) << "DisplayOptions::closestIndex(" << _magnification << ")" << endl;
+ unsigned res = 0;
+ while ( res < numberOfMagnifications
+ && allowedMagnifications[ res ] < _magnification ) ++res;
+ if ( res >= ( numberOfMagnifications - 1 ) ) return numberOfMagnifications - 1;
+ if ( res == 0 ) return 0;
+
+ if ( ( allowedMagnifications[ res ] - _magnification ) > ( _magnification - allowedMagnifications[ res - 1 ] ) ) {
+ --res;
+ }
+ kdDebug(4500) << "DisplayOptions::closestIndex(" << res << "): nearest allowed magnification: "
+ << allowedMagnifications[ res ] << endl;
+ return res;
+}
+
+QValueList<double> DisplayOptions::normalMagnificationValues() {
+ QValueList<double> res;
+ for ( const double *first = allowedMagnifications, *last = allowedMagnifications + numberOfMagnifications;
+ first != last;
+ ++first ) {
+ res << *first;
+ }
+ return res;
+}
+
diff --git a/kghostview/displayoptions.h b/kghostview/displayoptions.h
new file mode 100644
index 00000000..e929d8b7
--- /dev/null
+++ b/kghostview/displayoptions.h
@@ -0,0 +1,112 @@
+/**
+ * Copyright (C) 2003, Lus Pedro Coelho
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef DISPLAYOPTIONS_H
+#define DISPLAYOPTIONS_H
+#include <qstring.h>
+#include <qvaluelist.h>
+#include "dscparse_adapter.h"
+#include <kdemacros.h>
+class KCmdLineArgs;
+class KConfig;
+
+class KDE_EXPORT DisplayOptions
+{
+ public:
+ DisplayOptions();
+
+ // default generated DisplayOptions(const DisplayOptions&);
+ // default generated ~DisplayOptions();
+ // default generated DisplayOptions& operator = (const DisplayOptions&);
+
+ void reset() { *this = DisplayOptions(); }
+
+ void restoreOverrideOrientation() { setOverrideOrientation( CDSC_ORIENT_UNKNOWN ); }
+ void setOverrideOrientation(CDSC_ORIENTATION_ENUM e) { _overrideOrientation = e; }
+ CDSC_ORIENTATION_ENUM overrideOrientation() const { return _overrideOrientation; }
+
+ void restoreOverridePageMedia() { _overridePageMedia = QString::null; }
+ void setOverridePageMedia(const QString& newMedia) { _overridePageMedia = newMedia; }
+ const QString& overridePageMedia() const { return _overridePageMedia; }
+
+ /**
+ * The current page.
+ * Note that the pages here are 0-based, although the user should see 1-based pages.
+ *
+ * In parsing cmdline-args, we transform --page=1 into setPage( 0 );
+ */
+ int page() const { return _page; }
+ void setPage( int newPage ) { _page = newPage; }
+
+ double magnification() const;
+ void setMagnification( double );
+ /**
+ * Goes to the next degree of magnification if possible. If we cannot zoom in any more, it returns false,
+ * leaving the magnification untouched.
+ */
+ bool zoomIn();
+ bool zoomOut();
+
+ bool canZoomIn() const;
+ bool canZoomOut() const;
+
+ /**
+ * Parses command line options.
+ */
+ static DisplayOptions parse ( KCmdLineArgs * );
+ /**
+ * Transforms the object in a string representation.
+ * Useful for storing in config files, for example.
+ *
+ * \return string representation or null on error.
+ *
+ * \sa fromString
+ */
+ static QString toString( const DisplayOptions& );
+ /**
+ * Reads the display options from a string formatted by toString.
+ *
+ * \return true if the convertion succeeded.
+ */
+ static bool fromString( DisplayOptions&, const QString& );
+
+ /**
+ * Returns a list of values that normally we can get through
+ * zoomIn() & zoomOut()
+ */
+ static QValueList<double> normalMagnificationValues();
+
+ private:
+
+ unsigned closestIndex() const;
+
+ CDSC_ORIENTATION_ENUM _overrideOrientation;
+ QString _overridePageMedia;
+ int _page;
+ double _magnification;
+};
+
+inline
+DisplayOptions::DisplayOptions()
+ :_overrideOrientation( CDSC_ORIENT_UNKNOWN ),
+ _overridePageMedia ( QString::null ),
+ _page( 0 )
+{
+ setMagnification( 1.0 );
+}
+
+#endif // DISPLAYOPTIONS_H
diff --git a/kghostview/dscparse.cpp b/kghostview/dscparse.cpp
new file mode 100644
index 00000000..b5d2c3a7
--- /dev/null
+++ b/kghostview/dscparse.cpp
@@ -0,0 +1,3432 @@
+/* Copyright (C) 2000-2001, Ghostgum Software Pty Ltd. All rights reserved.
+
+ This file is part of GSview.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public Licence for full details.
+
+ Everyone is granted permission to copy, modify and redistribute this
+ file, but only under the conditions described in the GNU General
+ Public Licence. A copy of this license is supposed to have been given
+ to you along with this file so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+*/
+
+/* $Id$ */
+
+/* dscparse.c - DSC parser */
+
+/*
+ * This is a DSC parser, based on the DSC 3.0 spec,
+ * with a few DSC 2.1 additions for page size.
+ *
+ * Current limitations:
+ * %%+ may be used after any comment in the comment or trailer,
+ * but is currently only supported by
+ * %%DocumentMedia
+ *
+ * DSC 2.1 additions (discontinued in DSC 3.0):
+ * %%DocumentPaperColors:
+ * %%DocumentPaperForms:
+ * %%DocumentPaperSizes:
+ * %%DocumentPaperWeights:
+ * %%PaperColor: (ignored)
+ * %%PaperForm: (ignored)
+ * %%PaperSize:
+ * %%PaperWeight: (ignored)
+ *
+ * Other additions for defaults or page section
+ % %%ViewingOrientation: xx xy yx yy
+*/
+
+#include <stdio.h> /* for sprintf(), not file I/O */
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#define MAXSTR 256
+
+#include "dscparse.h"
+
+/* Macros for comparing string literals
+ * For maximum speed, the length of the second macro argument is
+ * computed at compile time.
+ * THE SECOND MACRO ARGUMENT MUST BE A STRING LITERAL.
+ */
+#define COMPARE(p,str) (strncmp((const char *)(p), (str), sizeof(str)-1)==0)
+#define IS_DSC(line, str) (COMPARE((line), (str)))
+
+/* Macros for comparing the first one or two characters */
+#define IS_WHITE(ch) (((ch)==' ') || ((ch)=='\t'))
+#define IS_EOL(ch) (((ch)=='\r') || ((ch)=='\n'))
+#define IS_WHITE_OR_EOL(ch) (IS_WHITE(ch) || IS_EOL(ch))
+#define IS_BLANK(str) (IS_EOL(str[0]))
+#define NOT_DSC_LINE(str) (((str)[0]!='%') || ((str)[1]!='%'))
+
+/* Macros for document offset to start and end of line */
+#define DSC_START(dsc) ((dsc)->data_offset + (dsc)->data_index - (dsc)->line_length)
+#define DSC_END(dsc) ((dsc)->data_offset + (dsc)->data_index)
+
+/* dsc_scan_SECTION() functions return one of
+ * CDSC_ERROR, CDSC_OK, CDSC_NOTDSC
+ * or one of the following
+ */
+/* The line should be passed on to the next section parser. */
+#define CDSC_PROPAGATE 10
+
+/* If document is DOS EPS and we haven't read 30 bytes, ask for more. */
+#define CDSC_NEEDMORE 11
+
+template<class T>
+inline T min (T a, T b) { return a < b ? a : b; }
+
+/* local prototypes */
+dsc_private void * dsc_memalloc(P2(CDSC *dsc, size_t size));
+dsc_private void dsc_memfree(P2(CDSC*dsc, void *ptr));
+dsc_private CDSC * dsc_init2(P1(CDSC *dsc));
+dsc_private void dsc_reset(P1(CDSC *dsc));
+dsc_private void dsc_section_join(P3(unsigned long begin, unsigned long *pend, unsigned long **pplast));
+dsc_private int dsc_read_line(P1(CDSC *dsc));
+dsc_private int dsc_read_doseps(P1(CDSC *dsc));
+dsc_private char * dsc_alloc_string(P3(CDSC *dsc, const char *str, int len));
+dsc_private char * dsc_add_line(P3(CDSC *dsc, const char *line, unsigned int len));
+dsc_private char * dsc_copy_string(P5(char *str, unsigned int slen,
+ char *line, unsigned int len, unsigned int *offset));
+dsc_private GSDWORD dsc_get_dword(P1(const unsigned char *buf));
+dsc_private GSWORD dsc_get_word(P1(const unsigned char *buf));
+dsc_private int dsc_get_int(P3(const char *line, unsigned int len, unsigned int *offset));
+dsc_private float dsc_get_real(P3(const char *line, unsigned int len,
+ unsigned int *offset));
+dsc_private int dsc_stricmp(P2(const char *s, const char *t));
+dsc_private void dsc_unknown(P1(CDSC *dsc));
+dsc_private GSBOOL dsc_is_section(char *line);
+dsc_private int dsc_parse_pages(P1(CDSC *dsc));
+dsc_private int dsc_parse_bounding_box(P3(CDSC *dsc, CDSCBBOX** pbbox, int offset));
+dsc_private int dsc_parse_float_bounding_box(P3(CDSC *dsc, CDSCFBBOX** pfbbox, int offset));
+dsc_private int dsc_parse_orientation(P3(CDSC *dsc, unsigned int *porientation,
+ int offset));
+dsc_private int dsc_parse_order(P1(CDSC *dsc));
+dsc_private int dsc_parse_media(P2(CDSC *dsc, const CDSCMEDIA **page_media));
+dsc_private int dsc_parse_document_media(P1(CDSC *dsc));
+dsc_private int dsc_parse_viewing_orientation(P2(CDSC *dsc, CDSCCTM **pctm));
+dsc_private int dsc_parse_page(P1(CDSC *dsc));
+dsc_private void dsc_save_line(P1(CDSC *dsc));
+dsc_private int dsc_scan_type(P1(CDSC *dsc));
+dsc_private int dsc_scan_comments(P1(CDSC *dsc));
+dsc_private int dsc_scan_preview(P1(CDSC *dsc));
+dsc_private int dsc_scan_defaults(P1(CDSC *dsc));
+dsc_private int dsc_scan_prolog(P1(CDSC *dsc));
+dsc_private int dsc_scan_setup(P1(CDSC *dsc));
+dsc_private int dsc_scan_page(P1(CDSC *dsc));
+dsc_private int dsc_scan_trailer(P1(CDSC *dsc));
+dsc_private int dsc_error(P4(CDSC *dsc, unsigned int explanation,
+ char *line, unsigned int line_len));
+
+/* DSC error reporting */
+dsc_private const int dsc_severity[] = {
+ CDSC_ERROR_WARN, /* CDSC_MESSAGE_BBOX */
+ CDSC_ERROR_WARN, /* CDSC_MESSAGE_EARLY_TRAILER */
+ CDSC_ERROR_WARN, /* CDSC_MESSAGE_EARLY_EOF */
+ CDSC_ERROR_ERROR, /* CDSC_MESSAGE_PAGE_IN_TRAILER */
+ CDSC_ERROR_ERROR, /* CDSC_MESSAGE_PAGE_ORDINAL */
+ CDSC_ERROR_ERROR, /* CDSC_MESSAGE_PAGES_WRONG */
+ CDSC_ERROR_ERROR, /* CDSC_MESSAGE_EPS_NO_BBOX */
+ CDSC_ERROR_ERROR, /* CDSC_MESSAGE_EPS_PAGES */
+ CDSC_ERROR_WARN, /* CDSC_MESSAGE_NO_MEDIA */
+ CDSC_ERROR_WARN, /* CDSC_MESSAGE_ATEND */
+ CDSC_ERROR_INFORM, /* CDSC_MESSAGE_DUP_COMMENT */
+ CDSC_ERROR_INFORM, /* CDSC_MESSAGE_DUP_TRAILER */
+ CDSC_ERROR_WARN, /* CDSC_MESSAGE_BEGIN_END */
+ CDSC_ERROR_INFORM, /* CDSC_MESSAGE_BAD_SECTION */
+ CDSC_ERROR_INFORM, /* CDSC_MESSAGE_LONG_LINE */
+ CDSC_ERROR_WARN, /* CDSC_MESSAGE_INCORRECT_USAGE */
+ 0
+};
+
+#define DSC_MAX_ERROR ((sizeof(dsc_severity) / sizeof(int))-2)
+
+const CDSCMEDIA dsc_known_media[CDSC_KNOWN_MEDIA] = {
+ /* These sizes taken from Ghostscript gs_statd.ps */
+ {"11x17", 792, 1224, 0, NULL, NULL, NULL},
+ {"A0", 2380, 3368, 0, NULL, NULL, NULL},
+ {"A1", 1684, 2380, 0, NULL, NULL, NULL},
+ {"A2", 1190, 1684, 0, NULL, NULL, NULL},
+ {"A3", 842, 1190, 0, NULL, NULL, NULL},
+ {"A4", 595, 842, 0, NULL, NULL, NULL},
+ {"A5", 421, 595, 0, NULL, NULL, NULL},
+ {"A6", 297, 421, 0, NULL, NULL, NULL},
+ {"A7", 210, 297, 0, NULL, NULL, NULL},
+ {"A8", 148, 210, 0, NULL, NULL, NULL},
+ {"A9", 105, 148, 0, NULL, NULL, NULL},
+ {"A10", 74, 105, 0, NULL, NULL, NULL},
+ {"B0", 2836, 4008, 0, NULL, NULL, NULL},
+ {"B1", 2004, 2836, 0, NULL, NULL, NULL},
+ {"B2", 1418, 2004, 0, NULL, NULL, NULL},
+ {"B3", 1002, 1418, 0, NULL, NULL, NULL},
+ {"B4", 709, 1002, 0, NULL, NULL, NULL}, /* ISO, but not Adobe standard */
+ {"B5", 501, 709, 0, NULL, NULL, NULL}, /* ISO, but not Adobe standard */
+ {"B6", 354, 501, 0, NULL, NULL, NULL},
+ {"C0", 2600, 3677, 0, NULL, NULL, NULL},
+ {"C1", 1837, 2600, 0, NULL, NULL, NULL},
+ {"C2", 1298, 1837, 0, NULL, NULL, NULL},
+ {"C3", 918, 1298, 0, NULL, NULL, NULL},
+ {"C4", 649, 918, 0, NULL, NULL, NULL},
+ {"C5", 459, 649, 0, NULL, NULL, NULL},
+ {"C6", 323, 459, 0, NULL, NULL, NULL},
+ {"Ledger", 1224, 792, 0, NULL, NULL, NULL},
+ {"Legal", 612, 1008, 0, NULL, NULL, NULL},
+ {"Letter", 612, 792, 0, NULL, NULL, NULL},
+ {"Note", 612, 792, 0, NULL, NULL, NULL},
+// ISO and JIS B sizes are different....
+ {"jisb0", 2916, 4128, 0, NULL, NULL, NULL},
+ {"jisb1", 2064, 2916, 0, NULL, NULL, NULL},
+ {"jisb2", 1458, 2064, 0, NULL, NULL, NULL},
+ {"jisb3", 1032, 1458, 0, NULL, NULL, NULL},
+ {"jisb4", 729, 1032, 0, NULL, NULL, NULL},
+ {"jisb5", 516, 729, 0, NULL, NULL, NULL},
+ {"jisb6", 363, 516, 0, NULL, NULL, NULL},
+// U.S. CAD standard paper sizes
+ {"archE", 2592, 3456, 0, NULL, NULL, NULL},
+ {"archD", 1728, 2592, 0, NULL, NULL, NULL},
+ {"archC", 1296, 1728, 0, NULL, NULL, NULL},
+ {"archB", 864, 1296, 0, NULL, NULL, NULL},
+ {"archA", 648, 864, 0, NULL, NULL, NULL},
+// Other paper sizes
+ {"flsa", 612, 936, 0, NULL, NULL, NULL}, /* U.S. foolscap */
+ {"flse", 612, 936, 0, NULL, NULL, NULL}, /* European foolscap */
+ {"halfletter", 396, 612, 0, NULL, NULL, NULL},
+ {NULL, 0, 0, 0, NULL, NULL, NULL}
+};
+
+/* parser state */
+enum CDSC_SCAN_SECTION {
+ scan_none = 0,
+ scan_comments = 1,
+ scan_pre_preview = 2,
+ scan_preview = 3,
+ scan_pre_defaults = 4,
+ scan_defaults = 5,
+ scan_pre_prolog = 6,
+ scan_prolog = 7,
+ scan_pre_setup = 8,
+ scan_setup = 9,
+ scan_pre_pages = 10,
+ scan_pages = 11,
+ scan_pre_trailer = 12,
+ scan_trailer = 13,
+ scan_eof = 14
+};
+
+static const char * const dsc_scan_section_name[15] = {
+ "Type", "Comments",
+ "pre-Preview", "Preview",
+ "pre-Defaults", "Defaults",
+ "pre-Prolog", "Prolog",
+ "pre-Setup", "Setup",
+ "pre-Page", "Page",
+ "pre-Trailer", "Trailer",
+ "EOF"
+};
+
+/******************************************************************/
+/* Public functions */
+/******************************************************************/
+
+/* constructor */
+CDSC *
+dsc_init(void *caller_data)
+{
+ CDSC *dsc = (CDSC *)malloc(sizeof(CDSC));
+ if (dsc == NULL)
+ return NULL;
+ memset(dsc, 0, sizeof(CDSC));
+ dsc->caller_data = caller_data;
+
+ return dsc_init2(dsc);
+}
+
+/* constructor, with caller supplied memalloc */
+CDSC *
+dsc_init_with_alloc(
+ void *caller_data,
+ void *(*memalloc)(size_t size, void *closure_data),
+ void (*memfree)(void *ptr, void *closure_data),
+ void *closure_data)
+{
+ CDSC *dsc = (CDSC *)memalloc(sizeof(CDSC), closure_data);
+ if (dsc == NULL)
+ return NULL;
+ memset(dsc, 0, sizeof(CDSC));
+ dsc->caller_data = caller_data;
+
+ dsc->memalloc = memalloc;
+ dsc->memfree = memfree;
+ dsc->mem_closure_data = closure_data;
+
+ return dsc_init2(dsc);
+}
+
+
+
+/* destructor */
+void
+dsc_free(CDSC *dsc)
+{
+ if (dsc == NULL)
+ return;
+ dsc_reset(dsc);
+ dsc_memfree(dsc, dsc);
+}
+
+
+/* Tell DSC parser how long document will be, to allow ignoring
+ * of early %%Trailer and %%EOF. This is optional.
+ */
+void
+dsc_set_length(CDSC *dsc, unsigned long len)
+{
+ dsc->file_length = len;
+}
+
+/* Process a buffer containing DSC comments and PostScript */
+/* Return value is < 0 for error, >=0 for OK.
+ * CDSC_ERROR
+ * CDSC_OK
+ * CDSC_NOTDSC (DSC will be ignored)
+ * other values indicate the last DSC comment read
+ */
+int
+dsc_scan_data(CDSC *dsc, const char *data, int length)
+{
+ int bytes_read;
+ int code = 0;
+
+ if (dsc == NULL)
+ return CDSC_ERROR;
+
+ if (dsc->id == CDSC_NOTDSC)
+ return CDSC_NOTDSC;
+ dsc->id = CDSC_OK;
+ if (dsc->eof)
+ return CDSC_OK; /* ignore */
+
+ if (length == 0) {
+ /* EOF, so process what remains */
+ dsc->eof = TRUE;
+ }
+
+ do {
+ if (dsc->id == CDSC_NOTDSC)
+ break;
+
+ if (length != 0) {
+ /* move existing data if needed */
+ if (dsc->data_length > CDSC_DATA_LENGTH/2) {
+ memmove(dsc->data, dsc->data + dsc->data_index,
+ dsc->data_length - dsc->data_index);
+ dsc->data_offset += dsc->data_index;
+ dsc->data_length -= dsc->data_index;
+ dsc->data_index = 0;
+ }
+ /* append to buffer */
+ bytes_read = min(length, (int)(CDSC_DATA_LENGTH - dsc->data_length));
+ memcpy(dsc->data + dsc->data_length, data, bytes_read);
+ dsc->data_length += bytes_read;
+ data += bytes_read;
+ length -= bytes_read;
+ }
+ if (dsc->scan_section == scan_none) {
+ code = dsc_scan_type(dsc);
+ if (code == CDSC_NEEDMORE) {
+ /* need more characters before we can identify type */
+ code = CDSC_OK;
+ break;
+ }
+ dsc->id = code;
+ }
+
+ if (code == CDSC_NOTDSC) {
+ dsc->id = CDSC_NOTDSC;
+ break;
+ }
+
+ while ((code = dsc_read_line(dsc)) > 0) {
+ if (dsc->id == CDSC_NOTDSC)
+ break;
+ if (dsc->doseps_end &&
+ (dsc->data_offset + dsc->data_index > dsc->doseps_end)) {
+ /* have read past end of DOS EPS PostScript section */
+ return CDSC_OK; /* ignore */
+ }
+ if (dsc->eof)
+ return CDSC_OK;
+ if (dsc->skip_document)
+ continue; /* embedded document */
+ if (dsc->skip_lines)
+ continue; /* embedded lines */
+ if (IS_DSC(dsc->line, "%%BeginData:"))
+ continue;
+ if (IS_DSC(dsc->line, "%%BeginBinary:"))
+ continue;
+ if (IS_DSC(dsc->line, "%%EndDocument"))
+ continue;
+ if (IS_DSC(dsc->line, "%%EndData"))
+ continue;
+ if (IS_DSC(dsc->line, "%%EndBinary"))
+ continue;
+
+ do {
+ switch (dsc->scan_section) {
+ case scan_comments:
+ code = dsc_scan_comments(dsc);
+ break;
+ case scan_pre_preview:
+ case scan_preview:
+ code = dsc_scan_preview(dsc);
+ break;
+ case scan_pre_defaults:
+ case scan_defaults:
+ code = dsc_scan_defaults(dsc);
+ break;
+ case scan_pre_prolog:
+ case scan_prolog:
+ code = dsc_scan_prolog(dsc);
+ break;
+ case scan_pre_setup:
+ case scan_setup:
+ code = dsc_scan_setup(dsc);
+ break;
+ case scan_pre_pages:
+ case scan_pages:
+ code = dsc_scan_page(dsc);
+ break;
+ case scan_pre_trailer:
+ case scan_trailer:
+ code = dsc_scan_trailer(dsc);
+ break;
+ case scan_eof:
+ code = CDSC_OK;
+ break;
+ default:
+ /* invalid state */
+ code = CDSC_ERROR;
+ }
+ /* repeat if line is start of next section */
+ } while (code == CDSC_PROPAGATE);
+
+ /* if DOS EPS header not complete, ask for more */
+ if (code == CDSC_NEEDMORE) {
+ code = CDSC_OK;
+ break;
+ }
+ if (code == CDSC_NOTDSC) {
+ dsc->id = CDSC_NOTDSC;
+ break;
+ }
+ }
+ } while (length != 0);
+
+ return (code < 0) ? code : dsc->id;
+}
+
+/* Tidy up from incorrect DSC comments */
+int
+dsc_fixup(CDSC *dsc)
+{
+ unsigned int i;
+ char buf[32];
+ unsigned long *last;
+
+ if (dsc->id == CDSC_NOTDSC)
+ return 0;
+
+ /* flush last partial line */
+ dsc_scan_data(dsc, NULL, 0);
+
+ /* Fix DSC error: code between %%EndSetup and %%Page */
+ if (dsc->page_count && (dsc->page[0].begin != dsc->endsetup)
+ && (dsc->endsetup != dsc->beginsetup)) {
+ dsc->endsetup = dsc->page[0].begin;
+ dsc_debug_print(dsc, "Warning: code included between setup and first page\n");
+ }
+
+ /* Last page contained a false trailer, */
+ /* so extend last page to start of trailer */
+ if (dsc->page_count && (dsc->begintrailer != 0) &&
+ (dsc->page[dsc->page_count-1].end != dsc->begintrailer)) {
+ dsc_debug_print(dsc, "Ignoring earlier misplaced trailer\n");
+ dsc_debug_print(dsc, "and extending last page to start of trailer\n");
+ dsc->page[dsc->page_count-1].end = dsc->begintrailer;
+ }
+
+ /*
+ * Join up all sections.
+ * There might be extra code between them, or we might have
+ * missed including the \n which followed \r.
+ */
+ last = &dsc->endcomments;
+ dsc_section_join(dsc->beginpreview, &dsc->endpreview, &last);
+ dsc_section_join(dsc->begindefaults, &dsc->enddefaults, &last);
+ dsc_section_join(dsc->beginprolog, &dsc->endprolog, &last);
+ dsc_section_join(dsc->beginsetup, &dsc->endsetup, &last);
+ for (i=0; i<dsc->page_count; i++)
+ dsc_section_join(dsc->page[i].begin, &dsc->page[i].end, &last);
+ if (dsc->begintrailer)
+ *last = dsc->begintrailer;
+
+ if ((dsc->page_pages == 0) && (dsc->page_count == 1)) {
+ /* don't flag an error if %%Pages absent but one %%Page found */
+ /* adjust incorrect page count */
+ dsc->page_pages = dsc->page_count;
+ }
+
+ /* Warnings and Errors that we can now identify */
+ if ((dsc->page_count != dsc->page_pages)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_PAGES_WRONG, NULL, 0);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* adjust incorrect page count */
+ dsc->page_pages = dsc->page_count;
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ break;;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+
+ if (dsc->epsf && (dsc->bbox == (CDSCBBOX *)NULL)) {
+ /* EPS files MUST include a BoundingBox */
+ int rc = dsc_error(dsc, CDSC_MESSAGE_EPS_NO_BBOX, NULL, 0);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* Assume that it is EPS */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* Is NOT an EPS file */
+ dsc->epsf = FALSE;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+
+ if (dsc->epsf && ((dsc->page_count > 1) || (dsc->page_pages > 1))) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_EPS_PAGES, NULL, 0);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* Is an EPS file */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* Is NOT an EPS file */
+ dsc->epsf = FALSE;
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+
+ if ((dsc->media_count == 1) && (dsc->page_media == NULL)) {
+ /* if one only media was specified, and default page media */
+ /* was not specified, assume that default is the only media. */
+ dsc->page_media = dsc->media[0];
+ }
+
+ if ((dsc->media_count != 0) && (dsc->page_media == NULL)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_NO_MEDIA, NULL, 0);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* default media is first listed */
+ dsc->page_media = dsc->media[0];
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* No default media */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+
+ /* make sure all pages have a label */
+ for (i=0; i<dsc->page_count; i++) {
+ if (strlen(dsc->page[i].label) == 0) {
+ sprintf(buf, "%d", i+1);
+ if ((dsc->page[i].label = dsc_alloc_string(dsc, buf, strlen(buf)))
+ == (char *)NULL)
+ return CDSC_ERROR; /* no memory */
+ }
+ }
+ return CDSC_OK;
+}
+
+/* Install a function to be used for displaying messages about
+ * DSC errors and warnings, and to request advice from user.
+ * Installing an error function is optional.
+ */
+void
+dsc_set_error_function(CDSC *dsc,
+ int (*fn)(P5(void *caller_data, CDSC *dsc,
+ unsigned int explanation, const char *line, unsigned int line_len)))
+{
+ dsc->dsc_error_fn = fn;
+}
+
+
+/* Install a function for printing debug messages */
+/* This is optional */
+void
+dsc_set_debug_function(CDSC *dsc,
+ void (*debug_fn)(P2(void *caller_data, const char *str)))
+{
+ dsc->debug_print_fn = debug_fn;
+}
+
+/* Doesn't need to be public for PostScript documents */
+/* Made public so GSview can add pages when processing PDF files */
+int
+dsc_add_page(CDSC *dsc, int ordinal, char *label)
+{
+ dsc->page[dsc->page_count].ordinal = ordinal;
+ dsc->page[dsc->page_count].label =
+ dsc_alloc_string(dsc, label, strlen(label)+1);
+ dsc->page[dsc->page_count].begin = 0;
+ dsc->page[dsc->page_count].end = 0;
+ dsc->page[dsc->page_count].orientation = CDSC_ORIENT_UNKNOWN;
+ dsc->page[dsc->page_count].media = NULL;
+ dsc->page[dsc->page_count].bbox = NULL;
+ dsc->page[dsc->page_count].viewing_orientation = NULL;
+
+ dsc->page_count++;
+ if (dsc->page_count >= dsc->page_chunk_length) {
+ CDSCPAGE *new_page = (CDSCPAGE *)dsc_memalloc(dsc,
+ (CDSC_PAGE_CHUNK+dsc->page_count) * sizeof(CDSCPAGE));
+ if (new_page == NULL)
+ return CDSC_ERROR; /* out of memory */
+ memcpy(new_page, dsc->page,
+ dsc->page_count * sizeof(CDSCPAGE));
+ dsc_memfree(dsc, dsc->page);
+ dsc->page= new_page;
+ dsc->page_chunk_length = CDSC_PAGE_CHUNK+dsc->page_count;
+ }
+ return CDSC_OK;
+}
+
+/* Doesn't need to be public for PostScript documents */
+/* Made public so GSview can store PDF MediaBox */
+int
+dsc_add_media(CDSC *dsc, CDSCMEDIA *media)
+{
+ CDSCMEDIA **newmedia_array;
+ CDSCMEDIA *newmedia;
+
+ /* extend media array */
+ newmedia_array = (CDSCMEDIA **)dsc_memalloc(dsc,
+ (dsc->media_count + 1) * sizeof(CDSCMEDIA *));
+ if (newmedia_array == NULL)
+ return CDSC_ERROR; /* out of memory */
+ if (dsc->media != NULL) {
+ memcpy(newmedia_array, dsc->media,
+ dsc->media_count * sizeof(CDSCMEDIA *));
+ dsc_memfree(dsc, dsc->media);
+ }
+ dsc->media = newmedia_array;
+
+ /* allocate new media */
+ newmedia = dsc->media[dsc->media_count] =
+ (CDSCMEDIA *)dsc_memalloc(dsc, sizeof(CDSCMEDIA));
+ if (newmedia == NULL)
+ return CDSC_ERROR; /* out of memory */
+ newmedia->name = NULL;
+ newmedia->width = 595.0;
+ newmedia->height = 842.0;
+ newmedia->weight = 80.0;
+ newmedia->colour = NULL;
+ newmedia->type = NULL;
+ newmedia->mediabox = NULL;
+
+ dsc->media_count++;
+
+ if (media->name) {
+ newmedia->name = dsc_alloc_string(dsc, media->name,
+ strlen(media->name));
+ if (newmedia->name == NULL)
+ return CDSC_ERROR; /* no memory */
+ }
+ newmedia->width = media->width;
+ newmedia->height = media->height;
+ newmedia->weight = media->weight;
+ if (media->colour) {
+ newmedia->colour = dsc_alloc_string(dsc, media->colour,
+ strlen(media->colour));
+ if (newmedia->colour == NULL)
+ return CDSC_ERROR; /* no memory */
+ }
+ if (media->type) {
+ newmedia->type = dsc_alloc_string(dsc, media->type,
+ strlen(media->type));
+ if (newmedia->type == NULL)
+ return CDSC_ERROR; /* no memory */
+ }
+ newmedia->mediabox = NULL;
+
+ if (media->mediabox) {
+ newmedia->mediabox = (CDSCBBOX *)dsc_memalloc(dsc, sizeof(CDSCBBOX));
+ if (newmedia->mediabox == NULL)
+ return CDSC_ERROR; /* no memory */
+ *newmedia->mediabox = *media->mediabox;
+ }
+ return CDSC_OK;
+}
+
+/* Doesn't need to be public for PostScript documents */
+/* Made public so GSview can store PDF CropBox */
+int
+dsc_set_page_bbox(CDSC *dsc, unsigned int page_number,
+ int llx, int lly, int urx, int ury)
+{
+ CDSCBBOX *bbox;
+ if (page_number >= dsc->page_count)
+ return CDSC_ERROR;
+ bbox = dsc->page[page_number].bbox;
+ if (bbox == NULL)
+ dsc->page[page_number].bbox = bbox =
+ (CDSCBBOX *)dsc_memalloc(dsc, sizeof(CDSCBBOX));
+ if (bbox == NULL)
+ return CDSC_ERROR;
+ bbox->llx = llx;
+ bbox->lly = lly;
+ bbox->urx = urx;
+ bbox->ury = ury;
+ return CDSC_OK;
+}
+
+
+/******************************************************************/
+/* Private functions below here. */
+/******************************************************************/
+
+dsc_private void *
+dsc_memalloc(CDSC *dsc, size_t size)
+{
+ if (dsc->memalloc)
+ return dsc->memalloc(size, dsc->mem_closure_data);
+ return malloc(size);
+}
+
+dsc_private void
+dsc_memfree(CDSC*dsc, void *ptr)
+{
+ if (dsc->memfree)
+ dsc->memfree(ptr, dsc->mem_closure_data);
+ else
+ free(ptr);
+}
+
+/* private constructor */
+dsc_private CDSC *
+dsc_init2(CDSC *dsc)
+{
+ dsc_reset(dsc);
+
+ dsc->string_head = (CDSCSTRING *)dsc_memalloc(dsc, sizeof(CDSCSTRING));
+ if (dsc->string_head == NULL) {
+ dsc_free(dsc);
+ return NULL; /* no memory */
+ }
+ dsc->string = dsc->string_head;
+ dsc->string->next = NULL;
+ dsc->string->data = (char *)dsc_memalloc(dsc, CDSC_STRING_CHUNK);
+ if (dsc->string->data == NULL) {
+ dsc_free(dsc);
+ return NULL; /* no memory */
+ }
+ dsc->string->index = 0;
+ dsc->string->length = CDSC_STRING_CHUNK;
+
+ dsc->page = (CDSCPAGE *)dsc_memalloc(dsc, CDSC_PAGE_CHUNK * sizeof(CDSCPAGE));
+ if (dsc->page == NULL) {
+ dsc_free(dsc);
+ return NULL; /* no memory */
+ }
+ dsc->page_chunk_length = CDSC_PAGE_CHUNK;
+ dsc->page_count = 0;
+
+ dsc->line = NULL;
+ dsc->data_length = 0;
+ dsc->data_index = dsc->data_length;
+
+ return dsc;
+}
+
+
+dsc_private void
+dsc_reset(CDSC *dsc)
+{
+ unsigned int i;
+ /* Clear public members */
+ dsc->dsc = FALSE;
+ dsc->ctrld = FALSE;
+ dsc->pjl = FALSE;
+ dsc->epsf = FALSE;
+ dsc->pdf = FALSE;
+ dsc->epsf = FALSE;
+ dsc->preview = CDSC_NOPREVIEW;
+ dsc->dsc_version = NULL; /* stored in dsc->string */
+ dsc->language_level = 0;
+ dsc->document_data = CDSC_DATA_UNKNOWN;
+ dsc->begincomments = 0;
+ dsc->endcomments = 0;
+ dsc->beginpreview = 0;
+ dsc->endpreview = 0;
+ dsc->begindefaults = 0;
+ dsc->enddefaults = 0;
+ dsc->beginprolog = 0;
+ dsc->endprolog = 0;
+ dsc->beginsetup = 0;
+ dsc->endsetup = 0;
+ dsc->begintrailer = 0;
+ dsc->endtrailer = 0;
+
+ for (i=0; i<dsc->page_count; i++) {
+ /* page media is pointer to an element of media or dsc_known_media */
+ /* do not free it. */
+
+ if (dsc->page[i].bbox)
+ dsc_memfree(dsc, dsc->page[i].bbox);
+ if (dsc->page[i].viewing_orientation)
+ dsc_memfree(dsc, dsc->page[i].viewing_orientation);
+ }
+ if (dsc->page)
+ dsc_memfree(dsc, dsc->page);
+ dsc->page = NULL;
+
+ dsc->page_count = 0;
+ dsc->page_pages = 0;
+ dsc->page_order = CDSC_ORDER_UNKNOWN;
+ dsc->page_orientation = CDSC_ORIENT_UNKNOWN;
+ if (dsc->viewing_orientation)
+ dsc_memfree(dsc, dsc->viewing_orientation);
+ dsc->viewing_orientation = NULL;
+
+ if (dsc->media) {
+ for (i=0; i<dsc->media_count; i++) {
+ if (dsc->media[i]) {
+ if (dsc->media[i]->mediabox)
+ dsc_memfree(dsc, dsc->media[i]->mediabox);
+ dsc_memfree(dsc, dsc->media[i]);
+ }
+ }
+ dsc_memfree(dsc, dsc->media);
+ }
+ dsc->media_count = 0;
+ dsc->media = NULL;
+
+ /* page_media is pointer to an element of media or dsc_known_media */
+ /* do not free it. */
+ dsc->page_media = NULL;
+
+ if (dsc->bbox)
+ dsc_memfree(dsc, dsc->bbox);
+ dsc->bbox = NULL;
+ if (dsc->page_bbox)
+ dsc_memfree(dsc, dsc->page_bbox);
+ dsc->page_bbox = NULL;
+ if (dsc->doseps)
+ dsc_memfree(dsc, dsc->doseps);
+ dsc->doseps = NULL;
+
+ dsc->dsc_title = NULL;
+ dsc->dsc_creator = NULL;
+ dsc->dsc_date = NULL;
+ dsc->dsc_for = NULL;
+
+
+ dsc->max_error = DSC_MAX_ERROR;
+ dsc->severity = dsc_severity;
+
+ /* Clear private members */
+ /* Don't touch dsc->caller_data */
+ dsc->id = CDSC_OK;
+ dsc->scan_section = scan_none;
+ dsc->doseps_end = 0;
+ dsc->page_chunk_length = 0;
+ dsc->file_length = 0;
+ dsc->skip_document = 0;
+ dsc->skip_bytes = 0;
+ dsc->skip_lines = 0;
+ dsc->skip_pjl = 0;
+ dsc->begin_font_count = 0;
+ dsc->begin_feature_count = 0;
+ dsc->begin_resource_count = 0;
+ dsc->begin_procset_count = 0;
+
+ dsc->data_length = 0;
+ dsc->data_index = 0;
+ dsc->data_offset = 0;
+
+ dsc->eof = 0;
+
+ dsc->line = 0;
+ dsc->line_length = 0;
+ dsc->eol = 0;
+ dsc->last_cr = FALSE;
+ dsc->line_count = 1;
+ dsc->long_line = FALSE;
+ memset(dsc->last_line, 0, sizeof(dsc->last_line));
+
+ dsc->string = dsc->string_head;
+ while (dsc->string != (CDSCSTRING *)NULL) {
+ if (dsc->string->data)
+ dsc_memfree(dsc, dsc->string->data);
+ dsc->string_head = dsc->string;
+ dsc->string = dsc->string->next;
+ dsc_memfree(dsc, dsc->string_head);
+ }
+ dsc->string_head = NULL;
+ dsc->string = NULL;
+
+ /* don't touch caller functions */
+
+ /* public data */
+ if (dsc->hires_bbox)
+ dsc_memfree(dsc, dsc->hires_bbox);
+ dsc->hires_bbox = NULL;
+ if (dsc->crop_box)
+ dsc_memfree(dsc, dsc->crop_box);
+ dsc->crop_box = NULL;
+}
+
+/*
+* Join up all sections.
+* There might be extra code between them, or we might have
+* missed including the \n which followed \r.
+* begin is the start of this section
+* pend is a pointer to the end of this section
+* pplast is a pointer to a pointer of the end of the previous section
+*/
+dsc_private void
+dsc_section_join(unsigned long begin, unsigned long *pend, unsigned long **pplast)
+{
+ if (begin)
+ **pplast = begin;
+ if (*pend > begin)
+ *pplast = pend;
+}
+
+
+/* return value is 0 if no line available, or length of line */
+dsc_private int
+dsc_read_line(CDSC *dsc)
+{
+ char *p, *last;
+ dsc->line = NULL;
+
+ if (dsc->eof) {
+ /* return all that remains, even if line incomplete */
+ dsc->line = dsc->data + dsc->data_index;
+ dsc->line_length = dsc->data_length - dsc->data_index;
+ dsc->data_index = dsc->data_length;
+ return dsc->line_length;
+ }
+
+ /* ignore embedded bytes */
+ if (dsc->skip_bytes) {
+ int cnt = min(dsc->skip_bytes,
+ (int)(dsc->data_length - dsc->data_index));
+ dsc->skip_bytes -= cnt;
+ dsc->data_index += cnt;
+ if (dsc->skip_bytes != 0)
+ return 0;
+ }
+
+ do {
+ dsc->line = dsc->data + dsc->data_index;
+ last = dsc->data + dsc->data_length;
+ if (dsc->data_index == dsc->data_length) {
+ dsc->line_length = 0;
+ return 0;
+ }
+ if (dsc->eol) {
+ /* if previous line was complete, increment line count */
+ dsc->line_count++;
+ if (dsc->skip_lines)
+ dsc->skip_lines--;
+ }
+
+ /* skip over \n which followed \r */
+ if (dsc->last_cr && dsc->line[0] == '\n') {
+ dsc->data_index++;
+ dsc->line++;
+ }
+ dsc->last_cr = FALSE;
+
+ /* look for EOL */
+ dsc->eol = FALSE;
+ for (p = dsc->line; p < last; p++) {
+ if (*p == '\r') {
+ p++;
+ if ((p<last) && (*p == '\n'))
+ p++; /* include line feed also */
+ else
+ dsc->last_cr = TRUE; /* we might need to skip \n */
+ dsc->eol = TRUE; /* dsc->line is a complete line */
+ break;
+ }
+ if (*p == '\n') {
+ p++;
+ dsc->eol = TRUE; /* dsc->line is a complete line */
+ break;
+ }
+ if (*p == '\032') { /* MS-DOS Ctrl+Z */
+ dsc->eol = TRUE;
+ }
+ }
+ if (dsc->eol == FALSE) {
+ /* we haven't got a complete line yet */
+ if (dsc->data_length - dsc->data_index < sizeof(dsc->data)/2) {
+ /* buffer is less than half full, ask for some more */
+ dsc->line_length = 0;
+ return 0;
+ }
+ }
+ dsc->data_index += dsc->line_length = (p - dsc->line);
+ } while (dsc->skip_lines && dsc->line_length);
+
+ if (dsc->line_length == 0)
+ return 0;
+
+ if ((dsc->line[0]=='%') && (dsc->line[1]=='%')) {
+ /* handle recursive %%BeginDocument */
+ if ((dsc->skip_document) && dsc->line_length &&
+ COMPARE(dsc->line, "%%EndDocument")) {
+ dsc->skip_document--;
+ }
+
+ /* handle embedded lines or binary data */
+ if (COMPARE(dsc->line, "%%BeginData:")) {
+ /* %%BeginData: <numberof>[ <type> [ <bytesorlines> ] ]
+ * <numberof> ::= <uint> (Lines or physical bytes)
+ * <type> ::= Hex | Binary | ASCII (Type of data)
+ * <bytesorlines> ::= Bytes | Lines (Read in bytes or lines)
+ */
+ char begindata[MAXSTR+1];
+ int cnt;
+ unsigned int num;
+ const char *numberof, *bytesorlines;
+ if ((num = dsc->line_length) >= sizeof(begindata)-1)
+ num = sizeof(begindata)-1;
+
+ memcpy(begindata, dsc->line, num);
+ begindata[num] = '\0';
+ numberof = strtok(begindata+12, " \r\n");
+ strtok(NULL, " \r\n"); /* dump type */
+ bytesorlines = strtok(NULL, " \r\n");
+ if (bytesorlines == NULL)
+ bytesorlines = "Bytes";
+
+ if ( (numberof == NULL) || (bytesorlines == NULL) ) {
+ /* invalid usage of %%BeginData */
+ /* ignore that we ever saw it */
+ int rc = dsc_error(dsc, CDSC_MESSAGE_INCORRECT_USAGE,
+ dsc->line, dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return 0;
+ }
+ }
+ else {
+ cnt = atoi(numberof);
+ if (cnt) {
+ if (bytesorlines && (dsc_stricmp(bytesorlines, "Lines")==0)) {
+ /* skip cnt lines */
+ if (dsc->skip_lines == 0) {
+ /* we are not already skipping lines */
+ dsc->skip_lines = cnt+1;
+ }
+ }
+ else {
+ /* byte count doesn't includes \n or \r\n */
+ /* or \r of %%BeginData: */
+ /* skip cnt bytes */
+ if (dsc->skip_bytes == 0) {
+ /* we are not already skipping lines */
+ dsc->skip_bytes = cnt;
+ }
+
+ }
+ }
+ }
+ }
+ else if (COMPARE(dsc->line, "%%BeginBinary:")) {
+ /* byte count doesn't includes \n or \r\n or \r of %%BeginBinary:*/
+ unsigned long cnt = atoi(dsc->line + 14);
+ if (dsc->skip_bytes == 0) {
+ /* we are not already skipping lines */
+ dsc->skip_bytes = cnt;
+ }
+ }
+ }
+
+ if ((dsc->line[0]=='%') && (dsc->line[1]=='%') &&
+ COMPARE(dsc->line, "%%BeginDocument:") ) {
+ /* Skip over embedded document, recursively */
+ dsc->skip_document++;
+ }
+
+ if (!dsc->long_line && (dsc->line_length > DSC_LINE_LENGTH)) {
+ dsc_error(dsc, CDSC_MESSAGE_LONG_LINE, dsc->line, dsc->line_length);
+ dsc->long_line = TRUE;
+ }
+
+ return dsc->line_length;
+}
+
+
+/* Save last DSC line, for use with %%+ */
+dsc_private void
+dsc_save_line(CDSC *dsc)
+{
+ int len = min((size_t) sizeof(dsc->last_line), (size_t) dsc->line_length);
+ memcpy(dsc->last_line, dsc->line, len);
+}
+
+/* display unknown DSC line */
+dsc_private void
+dsc_unknown(CDSC *dsc)
+{
+ if (dsc->debug_print_fn) {
+ char line[DSC_LINE_LENGTH];
+ unsigned int length = min((unsigned)DSC_LINE_LENGTH-1, dsc->line_length);
+ sprintf(line, "Unknown in %s section at line %d:\n ",
+ dsc_scan_section_name[dsc->scan_section], dsc->line_count);
+ dsc_debug_print(dsc, line);
+ strncpy(line, dsc->line, length);
+ line[length] = '\0';
+ dsc_debug_print(dsc, line);
+ }
+}
+
+
+dsc_private GSBOOL
+dsc_is_section(char *line)
+{
+ if ( !((line[0]=='%') && (line[1]=='%')) )
+ return FALSE;
+ if (IS_DSC(line, "%%BeginPreview"))
+ return TRUE;
+ if (IS_DSC(line, "%%BeginDefaults"))
+ return TRUE;
+ if (IS_DSC(line, "%%BeginProlog"))
+ return TRUE;
+ if (IS_DSC(line, "%%BeginSetup"))
+ return TRUE;
+ if (IS_DSC(line, "%%Page:"))
+ return TRUE;
+ if (IS_DSC(line, "%%Trailer"))
+ return TRUE;
+ if (IS_DSC(line, "%%EOF"))
+ return TRUE;
+ return FALSE;
+}
+
+
+dsc_private GSDWORD
+dsc_get_dword(const unsigned char *buf)
+{
+ GSDWORD dw;
+ dw = (GSDWORD)buf[0];
+ dw += ((GSDWORD)buf[1])<<8;
+ dw += ((GSDWORD)buf[2])<<16;
+ dw += ((GSDWORD)buf[3])<<24;
+ return dw;
+}
+
+dsc_private GSWORD
+dsc_get_word(const unsigned char *buf)
+{
+ GSWORD w;
+ w = (GSWORD)buf[0];
+ w |= (GSWORD)(buf[1]<<8);
+ return w;
+}
+
+dsc_private int
+dsc_read_doseps(CDSC *dsc)
+{
+ unsigned char *line = (unsigned char *)dsc->line;
+ if ((dsc->doseps = (CDSCDOSEPS *)dsc_memalloc(dsc, sizeof(CDSCDOSEPS))) == NULL)
+ return CDSC_ERROR; /* no memory */
+
+ dsc->doseps->ps_begin = dsc_get_dword(line+4);
+ dsc->doseps->ps_length = dsc_get_dword(line+8);
+ dsc->doseps->wmf_begin = dsc_get_dword(line+12);
+ dsc->doseps->wmf_length = dsc_get_dword(line+16);
+ dsc->doseps->tiff_begin = dsc_get_dword(line+20);
+ dsc->doseps->tiff_length = dsc_get_dword(line+24);
+ dsc->doseps->checksum = dsc_get_word(line+28);
+
+ dsc->doseps_end = dsc->doseps->ps_begin + dsc->doseps->ps_length;
+
+ /* move data_index backwards to byte after doseps header */
+ dsc->data_index -= dsc->line_length - 30;
+ /* we haven't read a line of PostScript code yet */
+ dsc->line_count = 0;
+ /* skip from current position to start of PostScript section */
+ dsc->skip_bytes = dsc->doseps->ps_begin - 30;
+
+ if (dsc->doseps->tiff_begin)
+ dsc->preview = CDSC_TIFF;
+ if (dsc->doseps->wmf_begin)
+ dsc->preview = CDSC_WMF;
+
+ return CDSC_OK;
+}
+
+
+
+dsc_private int
+dsc_parse_pages(CDSC *dsc)
+{
+ int ip, io;
+ unsigned int i;
+ char *p;
+ int n;
+ if ((dsc->page_pages != 0) && (dsc->scan_section == scan_comments)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_COMMENT, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ return CDSC_OK; /* ignore duplicate comments in header */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if ((dsc->page_pages != 0) && (dsc->scan_section == scan_trailer)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_TRAILER, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ break; /* use duplicate comments in header */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+
+ n = IS_DSC(dsc->line, "%%+") ? 3 : 8;
+ while (IS_WHITE(dsc->line[n]))
+ n++;
+ p = dsc->line + n;
+ if (COMPARE(p, "atend")) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_ATEND, dsc->line, dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* assume (atend) */
+ /* we should mark it as deferred */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* ignore it */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ else if (COMPARE(p, "(atend)")) {
+ /* do nothing */
+ /* we should mark it as deferred */
+ }
+ else {
+ ip = dsc_get_int(dsc->line+n, dsc->line_length-n, &i);
+ if (i) {
+ n+=i;
+ dsc->page_pages = ip;
+ io = dsc_get_int(dsc->line+n, dsc->line_length-n, &i);
+ if (i) {
+ /* DSC 2 uses extra integer to indicate page order */
+ /* DSC 3 uses %%PageOrder: */
+ if (dsc->page_order == CDSC_ORDER_UNKNOWN)
+ switch (io) {
+ case -1:
+ dsc->page_order = CDSC_DESCEND;
+ break;
+ case 0:
+ dsc->page_order = CDSC_SPECIAL;
+ break;
+ case 1:
+ dsc->page_order = CDSC_ASCEND;
+ break;
+ }
+ }
+ }
+ else {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_INCORRECT_USAGE, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ /* ignore it */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ }
+ return CDSC_OK;
+}
+
+dsc_private int
+dsc_parse_bounding_box(CDSC *dsc, CDSCBBOX** pbbox, int offset)
+{
+ unsigned int i, n;
+ int llx, lly, urx, ury;
+ float fllx, flly, furx, fury;
+ char *p;
+ /* Process first %%BoundingBox: in comments, and last in trailer */
+ if ((*pbbox != NULL) && (dsc->scan_section == scan_comments)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_COMMENT, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ return CDSC_OK; /* ignore duplicate comments in header */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if ((*pbbox != NULL) && (dsc->scan_section == scan_pages)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_COMMENT, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ return CDSC_OK; /* ignore duplicate comments in header */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if ((*pbbox != NULL) && (dsc->scan_section == scan_trailer)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_TRAILER, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ break; /* use duplicate comments in trailer */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if (*pbbox != NULL) {
+ dsc_memfree(dsc, *pbbox);
+ *pbbox = NULL;
+ }
+
+ /* should only process first %%BoundingBox: */
+
+ while (IS_WHITE(dsc->line[offset]))
+ offset++;
+ p = dsc->line + offset;
+
+ if (COMPARE(p, "atend")) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_ATEND, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* assume (atend) */
+ /* we should mark it as deferred */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* ignore it */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ else if (COMPARE(p, "(atend)")) {
+ /* do nothing */
+ /* we should mark it as deferred */
+ }
+ else {
+ /* llx = */ lly = urx = ury = 0;
+ n = offset;
+ llx = dsc_get_int(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ lly = dsc_get_int(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ urx = dsc_get_int(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ ury = dsc_get_int(dsc->line+n, dsc->line_length-n, &i);
+ if (i) {
+ *pbbox = (CDSCBBOX *)dsc_memalloc(dsc, sizeof(CDSCBBOX));
+ if (*pbbox == NULL)
+ return CDSC_ERROR; /* no memory */
+ (*pbbox)->llx = llx;
+ (*pbbox)->lly = lly;
+ (*pbbox)->urx = urx;
+ (*pbbox)->ury = ury;
+ }
+ else {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_BBOX, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* fllx = */ flly = furx = fury = 0.0;
+ n = offset;
+ n += i;
+ fllx = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ flly = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ furx = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ fury = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ if (i) {
+ *pbbox = (CDSCBBOX *)dsc_memalloc(dsc, sizeof(CDSCBBOX));
+ if (*pbbox == NULL)
+ return CDSC_ERROR; /* no memory */
+ (*pbbox)->llx = (int)fllx;
+ (*pbbox)->lly = (int)flly;
+ (*pbbox)->urx = (int)(furx+0.999);
+ (*pbbox)->ury = (int)(fury+0.999);
+ }
+ return CDSC_OK;
+ case CDSC_RESPONSE_CANCEL:
+ return CDSC_OK;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ }
+ return CDSC_OK;
+}
+
+dsc_private int
+dsc_parse_float_bounding_box(CDSC *dsc, CDSCFBBOX** pbbox, int offset)
+{
+ unsigned int i, n;
+ float fllx, flly, furx, fury;
+ char *p;
+ /* Process first %%HiResBoundingBox: or %%CropBox: in comments,
+ * and last in trailer.
+ */
+ if ((*pbbox != NULL) && (dsc->scan_section == scan_comments)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_COMMENT, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ return CDSC_OK; /* ignore duplicate comments in header */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if ((*pbbox != NULL) && (dsc->scan_section == scan_pages)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_COMMENT, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ return CDSC_OK; /* ignore duplicate comments in header */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if ((*pbbox != NULL) && (dsc->scan_section == scan_trailer)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_TRAILER, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ break; /* use duplicate comments in trailer */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if (*pbbox != NULL) {
+ dsc_memfree(dsc, *pbbox);
+ *pbbox = NULL;
+ }
+
+ /* should only process first %%BoundingBox: */
+
+ while (IS_WHITE(dsc->line[offset]))
+ offset++;
+ p = dsc->line + offset;
+
+ if (COMPARE(p, "atend")) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_ATEND, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* assume (atend) */
+ /* we should mark it as deferred */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* ignore it */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ else if (COMPARE(p, "(atend)")) {
+ /* do nothing */
+ /* we should mark it as deferred */
+ }
+ else {
+ /* fllx = */ flly = furx = fury = 0.0;
+ n = offset;
+ fllx = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ flly = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ furx = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ fury = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ if (i) {
+ *pbbox = (CDSCFBBOX *)dsc_memalloc(dsc, sizeof(CDSCFBBOX));
+ if (*pbbox == NULL)
+ return CDSC_ERROR; /* no memory */
+ (*pbbox)->fllx = fllx;
+ (*pbbox)->flly = flly;
+ (*pbbox)->furx = furx;
+ (*pbbox)->fury = fury;
+ }
+ }
+ return CDSC_OK;
+}
+
+dsc_private int
+dsc_parse_orientation(CDSC *dsc, unsigned int *porientation, int offset)
+{
+ char *p;
+ if ((dsc->page_orientation != CDSC_ORIENT_UNKNOWN) &&
+ (dsc->scan_section == scan_comments)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_COMMENT, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ return CDSC_OK; /* ignore duplicate comments in header */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if ((dsc->page_orientation != CDSC_ORIENT_UNKNOWN) &&
+ (dsc->scan_section == scan_trailer)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_TRAILER, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ break; /* use duplicate comments in header; */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ p = dsc->line + offset;
+ while (IS_WHITE(*p))
+ p++;
+ if (COMPARE(p, "atend")) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_ATEND, dsc->line, dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* assume (atend) */
+ /* we should mark it as deferred */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* ignore it */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ else if (COMPARE(p, "(atend)")) {
+ /* do nothing */
+ /* we should mark it as deferred */
+ }
+ else if (COMPARE(p, "Portrait")) {
+ *porientation = CDSC_PORTRAIT;
+ }
+ else if (COMPARE(p, "Landscape")) {
+ *porientation = CDSC_LANDSCAPE;
+ }
+ else {
+ dsc_unknown(dsc);
+ }
+ return CDSC_OK;
+}
+
+dsc_private int
+dsc_parse_order(CDSC *dsc)
+{
+ char *p;
+ if ((dsc->page_order != CDSC_ORDER_UNKNOWN) &&
+ (dsc->scan_section == scan_comments)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_COMMENT, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ return CDSC_OK; /* ignore duplicate comments in header */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if ((dsc->page_order != CDSC_ORDER_UNKNOWN) &&
+ (dsc->scan_section == scan_trailer)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_TRAILER, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ break; /* use duplicate comments in trailer */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+
+ p = dsc->line + (IS_DSC(dsc->line, "%%+") ? 3 : 13);
+ while (IS_WHITE(*p))
+ p++;
+ if (COMPARE(p, "atend")) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_ATEND, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* assume (atend) */
+ /* we should mark it as deferred */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* ignore it */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ else if (COMPARE(p, "(atend)")) {
+ /* do nothing */
+ /* we should mark it as deferred */
+ }
+ else if (COMPARE(p, "Ascend")) {
+ dsc->page_order = CDSC_ASCEND;
+ }
+ else if (COMPARE(p, "Descend")) {
+ dsc->page_order = CDSC_DESCEND;
+ }
+ else if (COMPARE(p, "Special")) {
+ dsc->page_order = CDSC_SPECIAL;
+ }
+ else {
+ dsc_unknown(dsc);
+ }
+ return CDSC_OK;
+}
+
+
+dsc_private int
+dsc_parse_media(CDSC *dsc, const CDSCMEDIA **page_media)
+{
+ char media_name[MAXSTR];
+ int n = IS_DSC(dsc->line, "%%+") ? 3 : 12; /* %%PageMedia: */
+ unsigned int i;
+
+ if (dsc_copy_string(media_name, sizeof(media_name)-1,
+ dsc->line+n, dsc->line_length-n, NULL)) {
+ for (i=0; i<dsc->media_count; i++) {
+ if (dsc->media[i]->name &&
+ (dsc_stricmp(media_name, dsc->media[i]->name) == 0)) {
+ *page_media = dsc->media[i];
+ return CDSC_OK;
+ }
+ }
+ }
+ dsc_unknown(dsc);
+
+ return CDSC_OK;
+}
+
+
+dsc_private int
+dsc_parse_document_media(CDSC *dsc)
+{
+ unsigned int i, n;
+ CDSCMEDIA lmedia;
+ GSBOOL blank_line;
+
+ if (IS_DSC(dsc->line, "%%DocumentMedia:"))
+ n = 16;
+ else if (IS_DSC(dsc->line, "%%+"))
+ n = 3;
+ else
+ return CDSC_ERROR; /* error */
+
+ /* check for blank remainder of line */
+ blank_line = TRUE;
+ for (i=n; i<dsc->line_length; i++) {
+ if (!IS_WHITE_OR_EOL(dsc->line[i])) {
+ blank_line = FALSE;
+ break;
+ }
+ }
+
+ if (!blank_line) {
+ char name[MAXSTR];
+ char colour[MAXSTR];
+ char type[MAXSTR];
+ lmedia.name = lmedia.colour = lmedia.type = (char *)NULL;
+ lmedia.width = lmedia.height = lmedia.weight = 0;
+ lmedia.mediabox = (CDSCBBOX *)NULL;
+ lmedia.name = dsc_copy_string(name, sizeof(name)-1,
+ dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i)
+ lmedia.width = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i)
+ lmedia.height = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i)
+ lmedia.weight = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i)
+ lmedia.colour = dsc_copy_string(colour, sizeof(colour)-1,
+ dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i)
+ lmedia.type = dsc_copy_string(type, sizeof(type)-1,
+ dsc->line+n, dsc->line_length-n, &i);
+
+ if (i==0)
+ dsc_unknown(dsc); /* we didn't get all fields */
+ else {
+ if (dsc_add_media(dsc, &lmedia))
+ return CDSC_ERROR; /* out of memory */
+ }
+ }
+ return CDSC_OK;
+}
+
+/* viewing orientation is believed to be the first four elements of
+ * a CTM matrix
+ */
+dsc_private int
+dsc_parse_viewing_orientation(CDSC *dsc, CDSCCTM **pctm)
+{
+ CDSCCTM ctm;
+ unsigned int i, n;
+
+ if (*pctm != NULL) {
+ dsc_memfree(dsc, *pctm);
+ *pctm = NULL;
+ }
+
+ n = IS_DSC(dsc->line, "%%+") ? 3 : 21; /* %%ViewingOrientation: */
+ while (IS_WHITE(dsc->line[n]))
+ n++;
+
+ /* ctm.xx = */ ctm.xy = ctm.yx = ctm.yy = 0.0;
+ ctm.xx = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ ctm.xy = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ ctm.yx = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ ctm.yy = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ if (i==0) {
+ dsc_unknown(dsc); /* we didn't get all fields */
+ }
+ else {
+ *pctm = (CDSCCTM *)dsc_memalloc(dsc, sizeof(CDSCCTM));
+ if (*pctm == NULL)
+ return CDSC_ERROR; /* no memory */
+ **pctm = ctm;
+ }
+ return CDSC_OK;
+}
+
+
+/* This is called before dsc_read_line(), since we may
+ * need to skip a binary header which contains a new line
+ * character
+ */
+dsc_private int
+dsc_scan_type(CDSC *dsc)
+{
+ unsigned char *p;
+ unsigned char *line = (unsigned char *)(dsc->data + dsc->data_index);
+ int length = dsc->data_length - dsc->data_index;
+
+ /* Types that should be known:
+ * DSC
+ * EPSF
+ * PJL + any of above
+ * ^D + any of above
+ * DOS EPS
+ * PDF
+ * non-DSC
+ */
+
+ /* First process any non PostScript headers */
+ /* At this stage we do not have a complete line */
+
+ if (length == 0)
+ return CDSC_NEEDMORE;
+
+ if (dsc->skip_pjl) {
+ /* skip until first PostScript comment */
+ while (length >= 2) {
+ while (length && !IS_EOL(line[0])) {
+ /* skip until EOL character */
+ line++;
+ dsc->data_index++;
+ length--;
+ }
+ while ((length >= 2) && IS_EOL(line[0]) && IS_EOL(line[1])) {
+ /* skip until EOL followed by non-EOL */
+ line++;
+ dsc->data_index++;
+ length--;
+ }
+ if (length < 2)
+ return CDSC_NEEDMORE;
+
+ if (IS_EOL(line[0]) && line[1]=='%') {
+ line++;
+ dsc->data_index++;
+ length--;
+ dsc->skip_pjl = FALSE;
+ break;
+ }
+ else {
+ /* line++; */
+ dsc->data_index++;
+ /* length--; */
+ return CDSC_NEEDMORE;
+ }
+ }
+ if (dsc->skip_pjl)
+ return CDSC_NEEDMORE;
+ }
+
+ if (length == 0)
+ return CDSC_NEEDMORE;
+
+ if (line[0] == '\004') {
+ line++;
+ dsc->data_index++;
+ length--;
+ dsc->ctrld = TRUE;
+ }
+
+ if (line[0] == '\033') {
+ /* possibly PJL */
+ if (length < 9)
+ return CDSC_NEEDMORE;
+ if (COMPARE(line, "\033%-12345X")) {
+ dsc->skip_pjl = TRUE; /* skip until first PostScript comment */
+ dsc->pjl = TRUE;
+ dsc->data_index += 9;
+ return dsc_scan_type(dsc);
+ }
+ }
+
+ if ((line[0]==0xc5) && (length < 4))
+ return CDSC_NEEDMORE;
+ if ((line[0]==0xc5) && (line[1]==0xd0) &&
+ (line[2]==0xd3) && (line[3]==0xc6) ) {
+ /* id is "EPSF" with bit 7 set */
+ /* read DOS EPS header, then ignore all bytes until the PS section */
+ if (length < 30)
+ return CDSC_NEEDMORE;
+ dsc->line = (char *)line;
+ if (dsc_read_doseps(dsc))
+ return CDSC_ERROR;
+ }
+ else {
+ if (length < 2)
+ return CDSC_NEEDMORE;
+ if ((line[0] == '%') && (line[1] == 'P')) {
+ if (length < 5)
+ return CDSC_NEEDMORE;
+ if (COMPARE(line, "%PDF-")) {
+ dsc->pdf = TRUE;
+ dsc->scan_section = scan_comments;
+ return CDSC_OK;
+ }
+ }
+ }
+
+ /* Finally process PostScript headers */
+
+ if (dsc_read_line(dsc) <= 0)
+ return CDSC_NEEDMORE;
+
+ dsc->dsc_version = dsc_add_line(dsc, dsc->line, dsc->line_length);
+ if (COMPARE(dsc->line, "%!PS-Adobe")) {
+ dsc->dsc = TRUE;
+ dsc->begincomments = DSC_START(dsc);
+ if (dsc->dsc_version == NULL)
+ return CDSC_ERROR; /* no memory */
+ p = (unsigned char *)dsc->line + 14;
+ while (IS_WHITE(*p))
+ p++;
+ if (COMPARE(p, "EPSF-"))
+ dsc->epsf = TRUE;
+ dsc->scan_section = scan_comments;
+ return CDSC_PSADOBE;
+ }
+ if (COMPARE(dsc->line, "%!")) {
+ dsc->scan_section = scan_comments;
+ return CDSC_NOTDSC;
+ }
+
+ dsc->scan_section = scan_comments;
+ return CDSC_NOTDSC; /* unrecognised */
+}
+
+
+
+dsc_private int
+dsc_scan_comments(CDSC *dsc)
+{
+ /* Comments section ends at */
+ /* %%EndComments */
+ /* another section */
+ /* line that does not start with %% */
+ /* Save a few important lines */
+
+ char *line = dsc->line;
+ GSBOOL continued = FALSE;
+ dsc->id = CDSC_OK;
+ if (IS_DSC(line, "%%EndComments")) {
+ dsc->id = CDSC_ENDCOMMENTS;
+ dsc->endcomments = DSC_END(dsc);
+ dsc->scan_section = scan_pre_preview;
+ return CDSC_OK;
+ }
+ else if (IS_DSC(line, "%%BeginComments")) {
+ /* ignore because we are in this section */
+ dsc->id = CDSC_BEGINCOMMENTS;
+ }
+ else if (dsc_is_section(line)) {
+ dsc->endcomments = DSC_START(dsc);
+ dsc->scan_section = scan_pre_preview;
+ return CDSC_PROPAGATE;
+ }
+ else if (line[0] == '%' && IS_WHITE_OR_EOL(line[1])) {
+ dsc->endcomments = DSC_START(dsc);
+ dsc->scan_section = scan_pre_preview;
+ return CDSC_PROPAGATE;
+ }
+ else if (line[0] != '%') {
+ dsc->id = CDSC_OK;
+ dsc->endcomments = DSC_START(dsc);
+ dsc->scan_section = scan_pre_preview;
+ return CDSC_PROPAGATE;
+ }
+ else if (IS_DSC(line, "%%Begin")) {
+ dsc->endcomments = DSC_START(dsc);
+ dsc->scan_section = scan_pre_preview;
+ return CDSC_PROPAGATE;
+ }
+
+ /* Handle continuation lines.
+ * To simply processing, we assume that contination lines
+ * will only occur if repeat parameters are allowed and that
+ * a complete set of these parameters appears on each line.
+ * This is more restrictive than the DSC specification, but
+ * is valid for the DSC comments understood by this parser
+ * for all documents that we have seen.
+ */
+ if (IS_DSC(line, "%%+")) {
+ line = dsc->last_line;
+ continued = TRUE;
+ }
+ else
+ dsc_save_line(dsc);
+
+ if (IS_DSC(line, "%%Pages:")) {
+ dsc->id = CDSC_PAGES;
+ if (dsc_parse_pages(dsc) != 0)
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%Creator:")) {
+ dsc->id = CDSC_CREATOR;
+ dsc->dsc_creator = dsc_add_line(dsc, dsc->line+10, dsc->line_length-10);
+ if (dsc->dsc_creator==NULL)
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%CreationDate:")) {
+ dsc->id = CDSC_CREATIONDATE;
+ dsc->dsc_date = dsc_add_line(dsc, dsc->line+15, dsc->line_length-15);
+ if (dsc->dsc_date==NULL)
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%Title:")) {
+ dsc->id = CDSC_TITLE;
+ dsc->dsc_title = dsc_add_line(dsc, dsc->line+8, dsc->line_length-8);
+ if (dsc->dsc_title==NULL)
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%For:")) {
+ dsc->id = CDSC_FOR;
+ dsc->dsc_for = dsc_add_line(dsc, dsc->line+6, dsc->line_length-6);
+ if (dsc->dsc_for==NULL)
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%LanguageLevel:")) {
+ unsigned int n = continued ? 3 : 16;
+ unsigned int i;
+ int ll;
+ dsc->id = CDSC_LANGUAGELEVEL;
+ ll = dsc_get_int(dsc->line+n, dsc->line_length-n, &i);
+ if (i) {
+ if ( (ll==1) || (ll==2) || (ll==3) )
+ dsc->language_level = ll;
+ else {
+ dsc_unknown(dsc);
+ }
+ }
+ else
+ dsc_unknown(dsc);
+ }
+ else if (IS_DSC(line, "%%BoundingBox:")) {
+ dsc->id = CDSC_BOUNDINGBOX;
+ if (dsc_parse_bounding_box(dsc, &(dsc->bbox), continued ? 3 : 14))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%HiResBoundingBox:")) {
+ dsc->id = CDSC_HIRESBOUNDINGBOX;
+ if (dsc_parse_float_bounding_box(dsc, &(dsc->hires_bbox),
+ continued ? 3 : 19))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%CropBox:")) {
+ dsc->id = CDSC_CROPBOX;
+ if (dsc_parse_float_bounding_box(dsc, &(dsc->crop_box),
+ continued ? 3 : 10))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%Orientation:")) {
+ dsc->id = CDSC_ORIENTATION;
+ if (dsc_parse_orientation(dsc, &(dsc->page_orientation),
+ continued ? 3 : 14))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%PageOrder:")) {
+ dsc->id = CDSC_PAGEORDER;
+ if (dsc_parse_order(dsc))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%DocumentMedia:")) {
+ dsc->id = CDSC_DOCUMENTMEDIA;
+ if (dsc_parse_document_media(dsc))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%DocumentPaperSizes:")) {
+ /* DSC 2.1 */
+ unsigned int n = continued ? 3 : 21;
+ unsigned int count = 0;
+ unsigned int i = 1;
+ char name[MAXSTR];
+ char *p;
+ dsc->id = CDSC_DOCUMENTPAPERSIZES;
+ while (i && (dsc->line[n]!='\r') && (dsc->line[n]!='\n')) {
+ p = dsc_copy_string(name, sizeof(name)-1,
+ dsc->line+n, dsc->line_length-n, &i);
+ if (i && p) {
+ const CDSCMEDIA *m = dsc_known_media;
+ if (count >= dsc->media_count) {
+ /* set some default values */
+ CDSCMEDIA lmedia;
+ lmedia.name = p;
+ lmedia.width = 595.0;
+ lmedia.height = 842.0;
+ lmedia.weight = 80.0;
+ lmedia.colour = NULL;
+ lmedia.type = NULL;
+ lmedia.mediabox = NULL;
+ if (dsc_add_media(dsc, &lmedia))
+ return CDSC_ERROR;
+ }
+ else
+ dsc->media[count]->name =
+ dsc_alloc_string(dsc, p, strlen(p));
+ /* find in list of known media */
+ while (m && m->name) {
+ if (dsc_stricmp(p, m->name)==0) {
+ dsc->media[count]->width = m->width;
+ dsc->media[count]->height = m->height;
+ break;
+ }
+ m++;
+ }
+ }
+ n+=i;
+ count++;
+ }
+ }
+ else if (IS_DSC(line, "%%DocumentPaperForms:")) {
+ /* DSC 2.1 */
+ unsigned int n = continued ? 3 : 21;
+ unsigned int count = 0;
+ unsigned int i = 1;
+ char type[MAXSTR];
+ char *p;
+ dsc->id = CDSC_DOCUMENTPAPERFORMS;
+ while (i && (dsc->line[n]!='\r') && (dsc->line[n]!='\n')) {
+ p = dsc_copy_string(type, sizeof(type)-1,
+ dsc->line+n, dsc->line_length-n, &i);
+ if (i && p) {
+ if (count >= dsc->media_count) {
+ /* set some default values */
+ CDSCMEDIA lmedia;
+ lmedia.name = NULL;
+ lmedia.width = 595.0;
+ lmedia.height = 842.0;
+ lmedia.weight = 80.0;
+ lmedia.colour = NULL;
+ lmedia.type = p;
+ lmedia.mediabox = NULL;
+ if (dsc_add_media(dsc, &lmedia))
+ return CDSC_ERROR;
+ }
+ else
+ dsc->media[count]->type =
+ dsc_alloc_string(dsc, p, strlen(p));
+ }
+ n+=i;
+ count++;
+ }
+ }
+ else if (IS_DSC(line, "%%DocumentPaperColors:")) {
+ /* DSC 2.1 */
+ unsigned int n = continued ? 3 : 22;
+ unsigned int count = 0;
+ unsigned int i = 1;
+ char colour[MAXSTR];
+ char *p;
+ dsc->id = CDSC_DOCUMENTPAPERCOLORS;
+ while (i && (dsc->line[n]!='\r') && (dsc->line[n]!='\n')) {
+ p = dsc_copy_string(colour, sizeof(colour)-1,
+ dsc->line+n, dsc->line_length-n, &i);
+ if (i && p) {
+ if (count >= dsc->media_count) {
+ /* set some default values */
+ CDSCMEDIA lmedia;
+ lmedia.name = NULL;
+ lmedia.width = 595.0;
+ lmedia.height = 842.0;
+ lmedia.weight = 80.0;
+ lmedia.colour = p;
+ lmedia.type = NULL;
+ lmedia.mediabox = NULL;
+ if (dsc_add_media(dsc, &lmedia))
+ return CDSC_ERROR;
+ }
+ else
+ dsc->media[count]->colour =
+ dsc_alloc_string(dsc, p, strlen(p));
+ }
+ n+=i;
+ count++;
+ }
+ }
+ else if (IS_DSC(line, "%%DocumentPaperWeights:")) {
+ /* DSC 2.1 */
+ unsigned int n = continued ? 3 : 23;
+ unsigned int count = 0;
+ unsigned int i = 1;
+ float w;
+ dsc->id = CDSC_DOCUMENTPAPERWEIGHTS;
+ while (i && (dsc->line[n]!='\r') && (dsc->line[n]!='\n')) {
+ w = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ if (i) {
+ if (count >= dsc->media_count) {
+ /* set some default values */
+ CDSCMEDIA lmedia;
+ lmedia.name = NULL;
+ lmedia.width = 595.0;
+ lmedia.height = 842.0;
+ lmedia.weight = w;
+ lmedia.colour = NULL;
+ lmedia.type = NULL;
+ lmedia.mediabox = NULL;
+ if (dsc_add_media(dsc, &lmedia))
+ return CDSC_ERROR;
+ }
+ else
+ dsc->media[count]->weight = w;
+ }
+ n+=i;
+ count++;
+ }
+ }
+ else if (IS_DSC(line, "%%DocumentData:")) {
+ unsigned int n = continued ? 3 : 15;
+ char *p = dsc->line + n;
+ while (IS_WHITE(*p))
+ p++;
+ dsc->id = CDSC_DOCUMENTDATA;
+ if (COMPARE(p, "Clean7Bit"))
+ dsc->document_data = CDSC_CLEAN7BIT;
+ else if (COMPARE(p, "Clean8Bit"))
+ dsc->document_data = CDSC_CLEAN8BIT;
+ else if (COMPARE(p, "Binary"))
+ dsc->document_data = CDSC_BINARY;
+ else
+ dsc_unknown(dsc);
+ }
+ else if (IS_DSC(line, "%%Requirements:")) {
+ dsc->id = CDSC_REQUIREMENTS;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%DocumentNeededFonts:")) {
+ dsc->id = CDSC_DOCUMENTNEEDEDFONTS;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%DocumentSuppliedFonts:")) {
+ dsc->id = CDSC_DOCUMENTSUPPLIEDFONTS;
+ /* ignore */
+ }
+ else if (dsc->line[0] == '%' && IS_WHITE_OR_EOL(dsc->line[1])) {
+ dsc->id = CDSC_OK;
+ /* ignore */
+ }
+ else {
+ dsc->id = CDSC_UNKNOWNDSC;
+ dsc_unknown(dsc);
+ }
+
+ dsc->endcomments = DSC_END(dsc);
+ return CDSC_OK;
+}
+
+
+dsc_private int
+dsc_scan_preview(CDSC *dsc)
+{
+ /* Preview section ends at */
+ /* %%EndPreview */
+ /* another section */
+ /* Preview section must start with %%BeginPreview */
+ char *line = dsc->line;
+ dsc->id = CDSC_OK;
+
+ if (dsc->scan_section == scan_pre_preview) {
+ if (IS_BLANK(line))
+ return CDSC_OK; /* ignore blank lines before preview */
+ else if (IS_DSC(line, "%%BeginPreview")) {
+ dsc->id = CDSC_BEGINPREVIEW;
+ dsc->beginpreview = DSC_START(dsc);
+ dsc->endpreview = DSC_END(dsc);
+ dsc->scan_section = scan_preview;
+ /* Don't mark the preview as EPSI if a DOS EPS header is present */
+ if (dsc->preview == CDSC_NOPREVIEW)
+ dsc->preview = CDSC_EPSI;
+ return CDSC_OK;
+ }
+ else {
+ dsc->scan_section = scan_pre_defaults;
+ return CDSC_PROPAGATE;
+ }
+ }
+
+ if (IS_DSC(line, "%%BeginPreview")) {
+ /* ignore because we are in this section */
+ }
+ else if (dsc_is_section(line)) {
+ dsc->endpreview = DSC_START(dsc);
+ dsc->scan_section = scan_pre_defaults;
+ return CDSC_PROPAGATE;
+ }
+ else if (IS_DSC(line, "%%EndPreview")) {
+ dsc->id = CDSC_ENDPREVIEW;
+ dsc->endpreview = DSC_END(dsc);
+ dsc->scan_section = scan_pre_defaults;
+ return CDSC_OK;
+ }
+ else if (line[0] == '%' && line[1] != '%') {
+ /* Ordinary comments are OK */
+ }
+ else {
+ dsc->id = CDSC_UNKNOWNDSC;
+ /* DSC comments should not occur in preview */
+ dsc_unknown(dsc);
+ }
+
+ dsc->endpreview = DSC_END(dsc);
+ return CDSC_OK;
+}
+
+dsc_private int
+dsc_scan_defaults(CDSC *dsc)
+{
+ /* Defaults section ends at */
+ /* %%EndDefaults */
+ /* another section */
+ /* Defaults section must start with %%BeginDefaults */
+ char *line = dsc->line;
+ dsc->id = CDSC_OK;
+
+ if (dsc->scan_section == scan_pre_defaults) {
+ if (IS_BLANK(line))
+ return CDSC_OK; /* ignore blank lines before defaults */
+ else if (IS_DSC(line, "%%BeginDefaults")) {
+ dsc->id = CDSC_BEGINDEFAULTS;
+ dsc->begindefaults = DSC_START(dsc);
+ dsc->enddefaults = DSC_END(dsc);
+ dsc->scan_section = scan_defaults;
+ return CDSC_OK;
+ }
+ else {
+ dsc->scan_section = scan_pre_prolog;
+ return CDSC_PROPAGATE;
+ }
+ }
+
+ if (NOT_DSC_LINE(line)) {
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%BeginPreview")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginDefaults")) {
+ /* ignore because we are in this section */
+ }
+ else if (dsc_is_section(line)) {
+ dsc->enddefaults = DSC_START(dsc);
+ dsc->scan_section = scan_pre_prolog;
+ return CDSC_PROPAGATE;
+ }
+ else if (IS_DSC(line, "%%EndDefaults")) {
+ dsc->id = CDSC_ENDDEFAULTS;
+ dsc->enddefaults = DSC_END(dsc);
+ dsc->scan_section = scan_pre_prolog;
+ return CDSC_OK;
+ }
+ else if (IS_DSC(line, "%%PageMedia:")) {
+ dsc->id = CDSC_PAGEMEDIA;
+ dsc_parse_media(dsc, &dsc->page_media);
+ }
+ else if (IS_DSC(line, "%%PageOrientation:")) {
+ dsc->id = CDSC_PAGEORIENTATION;
+ /* This can override %%Orientation: */
+ if (dsc_parse_orientation(dsc, &(dsc->page_orientation), 18))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%PageBoundingBox:")) {
+ dsc->id = CDSC_PAGEBOUNDINGBOX;
+ if (dsc_parse_bounding_box(dsc, &(dsc->page_bbox), 18))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%ViewingOrientation:")) {
+ dsc->id = CDSC_VIEWINGORIENTATION;
+ if (dsc_parse_viewing_orientation(dsc, &dsc->viewing_orientation))
+ return CDSC_ERROR;
+ }
+ else {
+ dsc->id = CDSC_UNKNOWNDSC;
+ /* All other DSC comments are unknown, but not an error */
+ dsc_unknown(dsc);
+ }
+ dsc->enddefaults = DSC_END(dsc);
+ return CDSC_OK;
+}
+
+/* CDSC_RESPONSE_OK and CDSC_RESPONSE_CANCEL mean ignore the
+ * mismatch (default) */
+dsc_private int
+dsc_check_match_prompt(CDSC *dsc, const char *str, int count)
+{
+ if (count != 0) {
+ char buf[MAXSTR+MAXSTR] = "";
+ if (dsc->line_length < (unsigned int)(sizeof(buf)/2-1)) {
+ strncpy(buf, dsc->line, dsc->line_length);
+ buf[dsc->line_length] = '\0';
+ }
+ sprintf(buf+strlen(buf), "\n%%%%Begin%.40s: / %%%%End%.40s\n", str, str);
+ return dsc_error(dsc, CDSC_MESSAGE_BEGIN_END, buf, strlen(buf));
+ }
+ return CDSC_RESPONSE_CANCEL;
+}
+
+dsc_private int
+dsc_check_match_type(CDSC *dsc, const char *str, int count)
+{
+ if (dsc_check_match_prompt(dsc, str, count) == CDSC_RESPONSE_IGNORE_ALL)
+ return CDSC_NOTDSC;
+ return CDSC_OK;
+}
+
+/* complain if Begin/End blocks didn't match */
+/* return non-zero if we should ignore all DSC */
+dsc_private int
+dsc_check_match(CDSC *dsc)
+{
+ int rc = 0;
+ const char *font = "Font";
+ const char *feature = "Feature";
+ const char *resource = "Resource";
+ const char *procset = "ProcSet";
+
+ if (!rc)
+ rc = dsc_check_match_type(dsc, font, dsc->begin_font_count);
+ if (!rc)
+ rc = dsc_check_match_type(dsc, feature, dsc->begin_feature_count);
+ if (!rc)
+ rc = dsc_check_match_type(dsc, resource, dsc->begin_resource_count);
+ if (!rc)
+ rc = dsc_check_match_type(dsc, procset, dsc->begin_procset_count);
+
+ dsc->begin_font_count = 0;
+ dsc->begin_feature_count = 0;
+ dsc->begin_resource_count = 0;
+ dsc->begin_procset_count = 0;
+ return rc;
+}
+
+
+dsc_private int
+dsc_scan_prolog(CDSC *dsc)
+{
+ /* Prolog section ends at */
+ /* %%EndProlog */
+ /* another section */
+ /* Prolog section may start with %%BeginProlog or non-dsc line */
+ char *line = dsc->line;
+ dsc->id = CDSC_OK;
+
+ if (dsc->scan_section == scan_pre_prolog) {
+ if (dsc_is_section(line) && (!IS_DSC(line, "%%BeginProlog"))) {
+ dsc->scan_section = scan_pre_setup;
+ return CDSC_PROPAGATE;
+ }
+ dsc->id = CDSC_BEGINPROLOG;
+ dsc->beginprolog = DSC_START(dsc);
+ dsc->endprolog = DSC_END(dsc);
+ dsc->scan_section = scan_prolog;
+ if (IS_DSC(line, "%%BeginProlog"))
+ return CDSC_OK;
+ }
+
+ if (NOT_DSC_LINE(line)) {
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%BeginPreview")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginDefaults")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginProlog")) {
+ /* ignore because we are in this section */
+ }
+ else if (dsc_is_section(line)) {
+ dsc->endprolog = DSC_START(dsc);
+ dsc->scan_section = scan_pre_setup;
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_PROPAGATE;
+ }
+ else if (IS_DSC(line, "%%EndProlog")) {
+ dsc->id = CDSC_ENDPROLOG;
+ dsc->endprolog = DSC_END(dsc);
+ dsc->scan_section = scan_pre_setup;
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_OK;
+ }
+ else if (IS_DSC(line, "%%BeginFont:")) {
+ dsc->id = CDSC_BEGINFONT;
+ /* ignore Begin/EndFont, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_font_count++;
+ }
+ else if (IS_DSC(line, "%%EndFont")) {
+ dsc->id = CDSC_ENDFONT;
+ dsc->begin_font_count--;
+ }
+ else if (IS_DSC(line, "%%BeginFeature:")) {
+ dsc->id = CDSC_BEGINFEATURE;
+ /* ignore Begin/EndFeature, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_feature_count++;
+ }
+ else if (IS_DSC(line, "%%EndFeature")) {
+ dsc->id = CDSC_ENDFEATURE;
+ dsc->begin_feature_count--;
+ }
+ else if (IS_DSC(line, "%%BeginResource:")) {
+ dsc->id = CDSC_BEGINRESOURCE;
+ /* ignore Begin/EndResource, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_resource_count++;
+ }
+ else if (IS_DSC(line, "%%EndResource")) {
+ dsc->id = CDSC_ENDRESOURCE;
+ dsc->begin_resource_count--;
+ }
+ else if (IS_DSC(line, "%%BeginProcSet:")) {
+ dsc->id = CDSC_BEGINPROCSET;
+ /* ignore Begin/EndProcSet, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_procset_count++;
+ }
+ else if (IS_DSC(line, "%%EndProcSet")) {
+ dsc->id = CDSC_ENDPROCSET;
+ dsc->begin_procset_count--;
+ }
+ else {
+ /* All other DSC comments are unknown, but not an error */
+ dsc->id = CDSC_UNKNOWNDSC;
+ dsc_unknown(dsc);
+ }
+
+ dsc->endprolog = DSC_END(dsc);
+ return CDSC_OK;
+}
+
+dsc_private int
+dsc_scan_setup(CDSC *dsc)
+{
+ /* Setup section ends at */
+ /* %%EndSetup */
+ /* another section */
+ /* Setup section must start with %%BeginSetup */
+
+ char *line = dsc->line;
+ dsc->id = CDSC_OK;
+
+ if (dsc->scan_section == scan_pre_setup) {
+ if (IS_BLANK(line))
+ return CDSC_OK; /* ignore blank lines before setup */
+ else if (IS_DSC(line, "%%BeginSetup")) {
+ dsc->id = CDSC_BEGINSETUP;
+ dsc->beginsetup = DSC_START(dsc);
+ dsc->endsetup = DSC_END(dsc);
+ dsc->scan_section = scan_setup;
+ return CDSC_OK;
+ }
+ else {
+ dsc->scan_section = scan_pre_pages;
+ return CDSC_PROPAGATE;
+ }
+ }
+
+ if (NOT_DSC_LINE(line)) {
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%BeginPreview")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginDefaults")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginProlog")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginSetup")) {
+ /* ignore because we are in this section */
+ }
+ else if (dsc_is_section(line)) {
+ dsc->endsetup = DSC_START(dsc);
+ dsc->scan_section = scan_pre_pages;
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_PROPAGATE;
+ }
+ else if (IS_DSC(line, "%%EndSetup")) {
+ dsc->id = CDSC_ENDSETUP;
+ dsc->endsetup = DSC_END(dsc);
+ dsc->scan_section = scan_pre_pages;
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_OK;
+ }
+ else if (IS_DSC(line, "%%BeginFeature:")) {
+ dsc->id = CDSC_BEGINFEATURE;
+ /* ignore Begin/EndFeature, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_feature_count++;
+ }
+ else if (IS_DSC(line, "%%EndFeature")) {
+ dsc->id = CDSC_ENDFEATURE;
+ dsc->begin_feature_count--;
+ }
+ else if (IS_DSC(line, "%%Feature:")) {
+ dsc->id = CDSC_FEATURE;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%BeginResource:")) {
+ dsc->id = CDSC_BEGINRESOURCE;
+ /* ignore Begin/EndResource, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_resource_count++;
+ }
+ else if (IS_DSC(line, "%%EndResource")) {
+ dsc->id = CDSC_ENDRESOURCE;
+ dsc->begin_resource_count--;
+ }
+ else if (IS_DSC(line, "%%PaperColor:")) {
+ dsc->id = CDSC_PAPERCOLOR;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%PaperForm:")) {
+ dsc->id = CDSC_PAPERFORM;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%PaperWeight:")) {
+ dsc->id = CDSC_PAPERWEIGHT;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%PaperSize:")) {
+ /* DSC 2.1 */
+ GSBOOL found_media = FALSE;
+ int i;
+ int n = 12;
+ char buf[MAXSTR];
+ buf[0] = '\0';
+ dsc->id = CDSC_PAPERSIZE;
+ dsc_copy_string(buf, sizeof(buf)-1, dsc->line+n, dsc->line_length-n,
+ NULL);
+ for (i=0; i<(int)dsc->media_count; i++) {
+ if (dsc->media[i] && dsc->media[i]->name &&
+ (dsc_stricmp(buf, dsc->media[i]->name)==0)) {
+ dsc->page_media = dsc->media[i];
+ found_media = TRUE;
+ break;
+ }
+ }
+ if (!found_media) {
+ /* It didn't match %%DocumentPaperSizes: */
+ /* Try our known media */
+ const CDSCMEDIA *m = dsc_known_media;
+ while (m->name) {
+ if (dsc_stricmp(buf, m->name)==0) {
+ dsc->page_media = m;
+ break;
+ }
+ m++;
+ }
+ if (m->name == NULL)
+ dsc_unknown(dsc);
+ }
+ }
+ else {
+ /* All other DSC comments are unknown, but not an error */
+ dsc->id = CDSC_UNKNOWNDSC;
+ dsc_unknown(dsc);
+ }
+
+ dsc->endsetup = DSC_END(dsc);
+ return CDSC_OK;
+}
+
+dsc_private int
+dsc_scan_page(CDSC *dsc)
+{
+ /* Page section ends at */
+ /* %%Page */
+ /* %%Trailer */
+ /* %%EOF */
+ char *line = dsc->line;
+ dsc->id = CDSC_OK;
+
+ if (dsc->scan_section == scan_pre_pages) {
+ if (IS_DSC(line, "%%Page:")) {
+ dsc->scan_section = scan_pages;
+ /* fall through */
+ }
+ else {
+ /* %%Page: didn't follow %%EndSetup
+ * Keep reading until reach %%Page or %%Trailer
+ * and add it to previous section.
+ */
+ unsigned long *last;
+ if (dsc->endsetup != 0)
+ last = &dsc->endsetup;
+ else if (dsc->endprolog != 0)
+ last = &dsc->endprolog;
+ else if (dsc->enddefaults != 0)
+ last = &dsc->enddefaults;
+ else if (dsc->endpreview != 0)
+ last = &dsc->endpreview;
+ else if (dsc->endcomments != 0)
+ last = &dsc->endcomments;
+ else
+ last = &dsc->begincomments;
+ *last = DSC_START(dsc);
+ if (IS_DSC(line, "%%Trailer") || IS_DSC(line, "%%EOF")) {
+ dsc->scan_section = scan_pre_trailer;
+ return CDSC_PROPAGATE;
+ }
+ return CDSC_OK;
+ }
+ }
+
+ if (NOT_DSC_LINE(line)) {
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%Page:")) {
+ dsc->id = CDSC_PAGE;
+ if (dsc->page_count) {
+ dsc->page[dsc->page_count-1].end = DSC_START(dsc);
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ }
+
+ if (dsc_parse_page(dsc) != 0)
+ return CDSC_ERROR;
+
+ return CDSC_OK;
+ }
+ else if (IS_DSC(line, "%%BeginPreview")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginDefaults")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginProlog")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginSetup")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (dsc_is_section(line)) {
+ if (IS_DSC(line, "%%Trailer")) {
+ dsc->page[dsc->page_count-1].end = DSC_START(dsc);
+ if (dsc->file_length) {
+ if ((!dsc->doseps &&
+ ((DSC_END(dsc) + 32768) < dsc->file_length)) ||
+ ((dsc->doseps) &&
+ ((DSC_END(dsc) + 32768) < dsc->doseps_end))) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_EARLY_TRAILER,
+ dsc->line, dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* ignore early trailer */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* this is the trailer */
+ dsc->scan_section = scan_pre_trailer;
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_PROPAGATE;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ else {
+ dsc->scan_section = scan_pre_trailer;
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_PROPAGATE;
+ }
+ }
+ else {
+ dsc->scan_section = scan_pre_trailer;
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_PROPAGATE;
+ }
+ }
+ else if (IS_DSC(line, "%%EOF")) {
+ dsc->page[dsc->page_count-1].end = DSC_START(dsc);
+ if (dsc->file_length) {
+ if ((DSC_END(dsc)+100 < dsc->file_length) ||
+ (dsc->doseps && (DSC_END(dsc) + 100 < dsc->doseps_end))) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_EARLY_EOF,
+ dsc->line, dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* %%EOF is wrong, ignore it */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* %%EOF is correct */
+ dsc->scan_section = scan_eof;
+ dsc->eof = TRUE;
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_PROPAGATE;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ }
+ else {
+ /* ignore it */
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_OK;
+ }
+ }
+ else {
+ /* Section comment, probably from a badly */
+ /* encapsulated EPS file. */
+ int rc = dsc_error(dsc, CDSC_MESSAGE_BAD_SECTION,
+ dsc->line, dsc->line_length);
+ if (rc == CDSC_RESPONSE_IGNORE_ALL)
+ return CDSC_NOTDSC;
+ }
+ }
+ else if (IS_DSC(line, "%%PageTrailer")) {
+ dsc->id = CDSC_PAGETRAILER;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%BeginPageSetup")) {
+ dsc->id = CDSC_BEGINPAGESETUP;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%EndPageSetup")) {
+ dsc->id = CDSC_ENDPAGESETUP;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%PageMedia:")) {
+ dsc->id = CDSC_PAGEMEDIA;
+ dsc_parse_media(dsc, &(dsc->page[dsc->page_count-1].media));
+ }
+ else if (IS_DSC(line, "%%PaperColor:")) {
+ dsc->id = CDSC_PAPERCOLOR;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%PaperForm:")) {
+ dsc->id = CDSC_PAPERFORM;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%PaperWeight:")) {
+ dsc->id = CDSC_PAPERWEIGHT;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%PaperSize:")) {
+ /* DSC 2.1 */
+ GSBOOL found_media = FALSE;
+ int i;
+ int n = 12;
+ char buf[MAXSTR];
+ buf[0] = '\0';
+ dsc_copy_string(buf, sizeof(buf)-1, dsc->line+n,
+ dsc->line_length-n, NULL);
+ for (i=0; i<(int)dsc->media_count; i++) {
+ if (dsc->media[i] && dsc->media[i]->name &&
+ (dsc_stricmp(buf, dsc->media[i]->name)==0)) {
+ dsc->page_media = dsc->media[i];
+ found_media = TRUE;
+ break;
+ }
+ }
+ if (!found_media) {
+ /* It didn't match %%DocumentPaperSizes: */
+ /* Try our known media */
+ const CDSCMEDIA *m = dsc_known_media;
+ while (m->name) {
+ if (dsc_stricmp(buf, m->name)==0) {
+ dsc->page[dsc->page_count-1].media = m;
+ break;
+ }
+ m++;
+ }
+ if (m->name == NULL)
+ dsc_unknown(dsc);
+ }
+ }
+ else if (IS_DSC(line, "%%PageOrientation:")) {
+ dsc->id = CDSC_PAGEORIENTATION;
+ if (dsc_parse_orientation(dsc,
+ &(dsc->page[dsc->page_count-1].orientation) ,18))
+ return CDSC_NOTDSC;
+ }
+ else if (IS_DSC(line, "%%PageBoundingBox:")) {
+ dsc->id = CDSC_PAGEBOUNDINGBOX;
+ if (dsc_parse_bounding_box(dsc, &dsc->page[dsc->page_count-1].bbox, 18))
+ return CDSC_NOTDSC;
+ }
+ else if (IS_DSC(line, "%%ViewingOrientation:")) {
+ dsc->id = CDSC_VIEWINGORIENTATION;
+ if (dsc_parse_viewing_orientation(dsc,
+ &dsc->page[dsc->page_count-1].viewing_orientation))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%BeginFont:")) {
+ dsc->id = CDSC_BEGINFONT;
+ /* ignore Begin/EndFont, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_font_count++;
+ }
+ else if (IS_DSC(line, "%%EndFont")) {
+ dsc->id = CDSC_BEGINFONT;
+ dsc->begin_font_count--;
+ }
+ else if (IS_DSC(line, "%%BeginFeature:")) {
+ dsc->id = CDSC_BEGINFEATURE;
+ /* ignore Begin/EndFeature, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_feature_count++;
+ }
+ else if (IS_DSC(line, "%%EndFeature")) {
+ dsc->id = CDSC_ENDFEATURE;
+ dsc->begin_feature_count--;
+ }
+ else if (IS_DSC(line, "%%BeginResource:")) {
+ dsc->id = CDSC_BEGINRESOURCE;
+ /* ignore Begin/EndResource, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_resource_count++;
+ }
+ else if (IS_DSC(line, "%%EndResource")) {
+ dsc->id = CDSC_ENDRESOURCE;
+ dsc->begin_resource_count--;
+ }
+ else if (IS_DSC(line, "%%BeginProcSet:")) {
+ dsc->id = CDSC_BEGINPROCSET;
+ /* ignore Begin/EndProcSet, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_procset_count++;
+ }
+ else if (IS_DSC(line, "%%EndProcSet")) {
+ dsc->id = CDSC_ENDPROCSET;
+ dsc->begin_procset_count--;
+ }
+ else if (IS_DSC(line, "%%IncludeFont:")) {
+ dsc->id = CDSC_INCLUDEFONT;
+ /* ignore */
+ }
+ else {
+ /* All other DSC comments are unknown, but not an error */
+ dsc->id = CDSC_UNKNOWNDSC;
+ dsc_unknown(dsc);
+ }
+
+ dsc->page[dsc->page_count-1].end = DSC_END(dsc);
+ return CDSC_OK;
+}
+
+/* Valid Trailer comments are
+ * %%Trailer
+ * %%EOF
+ * or the following deferred with (atend)
+ * %%BoundingBox:
+ * %%DocumentCustomColors:
+ * %%DocumentFiles:
+ * %%DocumentFonts:
+ * %%DocumentNeededFiles:
+ * %%DocumentNeededFonts:
+ * %%DocumentNeededProcSets:
+ * %%DocumentNeededResources:
+ * %%DocumentProcSets:
+ * %%DocumentProcessColors:
+ * %%DocumentSuppliedFiles:
+ * %%DocumentSuppliedFonts:
+ * %%DocumentSuppliedProcSets:
+ * %%DocumentSuppliedResources:
+ * %%Orientation:
+ * %%Pages:
+ * %%PageOrder:
+ *
+ * Our supported subset is
+ * %%Trailer
+ * %%EOF
+ * %%BoundingBox:
+ * %%Orientation:
+ * %%Pages:
+ * %%PageOrder:
+ * In addition to these, we support
+ * %%DocumentMedia:
+ *
+ * A %%PageTrailer can have the following:
+ * %%PageBoundingBox:
+ * %%PageCustomColors:
+ * %%PageFiles:
+ * %%PageFonts:
+ * %%PageOrientation:
+ * %%PageProcessColors:
+ * %%PageResources:
+ */
+
+dsc_private int
+dsc_scan_trailer(CDSC *dsc)
+{
+ /* Trailer section start at */
+ /* %%Trailer */
+ /* and ends at */
+ /* %%EOF */
+ char *line = dsc->line;
+ GSBOOL continued = FALSE;
+ dsc->id = CDSC_OK;
+
+ if (dsc->scan_section == scan_pre_trailer) {
+ if (IS_DSC(line, "%%Trailer")) {
+ dsc->id = CDSC_TRAILER;
+ dsc->begintrailer = DSC_START(dsc);
+ dsc->endtrailer = DSC_END(dsc);
+ dsc->scan_section = scan_trailer;
+ return CDSC_OK;
+ }
+ else if (IS_DSC(line, "%%EOF")) {
+ dsc->id = CDSC_EOF;
+ dsc->begintrailer = DSC_START(dsc);
+ dsc->endtrailer = DSC_END(dsc);
+ dsc->scan_section = scan_trailer;
+ /* Continue, in case we found %%EOF in an embedded document */
+ return CDSC_OK;
+ }
+ else {
+ /* %%Page: didn't follow %%EndSetup
+ * Keep reading until reach %%Page or %%Trailer
+ * and add it to setup section
+ */
+ /* append to previous section */
+ if (dsc->beginsetup)
+ dsc->endsetup = DSC_END(dsc);
+ else if (dsc->beginprolog)
+ dsc->endprolog = DSC_END(dsc);
+ else {
+ /* horribly confused */
+ }
+ return CDSC_OK;
+ }
+ }
+
+ /* Handle continuation lines.
+ * See comment above about our restrictive processing of
+ * continuation lines
+ */
+ if (IS_DSC(line, "%%+")) {
+ line = dsc->last_line;
+ continued = TRUE;
+ }
+ else
+ dsc_save_line(dsc);
+
+ if (NOT_DSC_LINE(line)) {
+ /* ignore */
+ }
+ else if (IS_DSC(dsc->line, "%%EOF")) {
+ /* Keep scanning, in case we have a false trailer */
+ dsc->id = CDSC_EOF;
+ }
+ else if (IS_DSC(dsc->line, "%%Trailer")) {
+ /* Cope with no pages with code after setup and before trailer. */
+ /* Last trailer is the correct one. */
+ dsc->id = CDSC_TRAILER;
+ dsc->begintrailer = DSC_START(dsc);
+ }
+ else if (IS_DSC(line, "%%Pages:")) {
+ dsc->id = CDSC_PAGES;
+ if (dsc_parse_pages(dsc) != 0)
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%BoundingBox:")) {
+ dsc->id = CDSC_BOUNDINGBOX;
+ if (dsc_parse_bounding_box(dsc, &(dsc->bbox), continued ? 3 : 14))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%HiResBoundingBox:")) {
+ dsc->id = CDSC_HIRESBOUNDINGBOX;
+ if (dsc_parse_float_bounding_box(dsc, &(dsc->hires_bbox),
+ continued ? 3 : 19))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%CropBox:")) {
+ dsc->id = CDSC_CROPBOX;
+ if (dsc_parse_float_bounding_box(dsc, &(dsc->crop_box),
+ continued ? 3 : 10))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%Orientation:")) {
+ dsc->id = CDSC_ORIENTATION;
+ if (dsc_parse_orientation(dsc, &(dsc->page_orientation), continued ? 3 : 14))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%PageOrder:")) {
+ dsc->id = CDSC_PAGEORDER;
+ if (dsc_parse_order(dsc))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%DocumentMedia:")) {
+ dsc->id = CDSC_DOCUMENTMEDIA;
+ if (dsc_parse_document_media(dsc))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(dsc->line, "%%Page:")) {
+ /* This should not occur in the trailer, but we might see
+ * this if a document has been incorrectly embedded.
+ */
+ int rc = dsc_error(dsc, CDSC_MESSAGE_PAGE_IN_TRAILER,
+ dsc->line, dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* Assume that we are really in the previous */
+ /* page, not the trailer */
+ dsc->scan_section = scan_pre_pages;
+ if (dsc->page_count)
+ dsc->page[dsc->page_count-1].end = DSC_START(dsc);
+ return CDSC_PROPAGATE; /* try again */
+ case CDSC_RESPONSE_CANCEL:
+ /* ignore pages in trailer */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ else if (IS_DSC(line, "%%DocumentNeededFonts:")) {
+ dsc->id = CDSC_DOCUMENTNEEDEDFONTS;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%DocumentSuppliedFonts:")) {
+ dsc->id = CDSC_DOCUMENTSUPPLIEDFONTS;
+ /* ignore */
+ }
+ else {
+ /* All other DSC comments are unknown, but not an error */
+ dsc->id = CDSC_UNKNOWNDSC;
+ dsc_unknown(dsc);
+ }
+
+ dsc->endtrailer = DSC_END(dsc);
+ return CDSC_OK;
+}
+
+
+dsc_private char *
+dsc_alloc_string(CDSC *dsc, const char *str, int len)
+{
+ char *p;
+ if (dsc->string_head == NULL) {
+ dsc->string_head = (CDSCSTRING *)dsc_memalloc(dsc, sizeof(CDSCSTRING));
+ if (dsc->string_head == NULL)
+ return NULL; /* no memory */
+ dsc->string = dsc->string_head;
+ dsc->string->next = NULL;
+ dsc->string->data = (char *)dsc_memalloc(dsc, CDSC_STRING_CHUNK);
+ if (dsc->string->data == NULL) {
+ dsc_reset(dsc);
+ return NULL; /* no memory */
+ }
+ dsc->string->index = 0;
+ dsc->string->length = CDSC_STRING_CHUNK;
+ }
+ if ( dsc->string->index + len + 1 > dsc->string->length) {
+ /* allocate another string block */
+ CDSCSTRING *newstring = (CDSCSTRING *)dsc_memalloc(dsc, sizeof(CDSCSTRING));
+ if (newstring == NULL) {
+ dsc_debug_print(dsc, "Out of memory\n");
+ return NULL;
+ }
+ newstring->next = NULL;
+ newstring->length = 0;
+ newstring->index = 0;
+ newstring->data = (char *)dsc_memalloc(dsc, CDSC_STRING_CHUNK);
+ if (newstring->data == NULL) {
+ dsc_memfree(dsc, newstring);
+ dsc_debug_print(dsc, "Out of memory\n");
+ return NULL; /* no memory */
+ }
+ newstring->length = CDSC_STRING_CHUNK;
+ dsc->string->next = newstring;
+ dsc->string = newstring;
+ }
+ if ( dsc->string->index + len + 1 > dsc->string->length)
+ return NULL; /* failed */
+ p = dsc->string->data + dsc->string->index;
+ memcpy(p, str, len);
+ *(p+len) = '\0';
+ dsc->string->index += len + 1;
+ return p;
+}
+
+/* store line, ignoring leading spaces */
+dsc_private char *
+dsc_add_line(CDSC *dsc, const char *line, unsigned int len)
+{
+ char *newline;
+ unsigned int i;
+ while (len && (IS_WHITE(*line))) {
+ len--;
+ line++;
+ }
+ newline = dsc_alloc_string(dsc, line, len);
+ if (newline == NULL)
+ return NULL;
+
+ for (i=0; i<len; i++) {
+ if (newline[i] == '\r') {
+ newline[i]='\0';
+ break;
+ }
+ if (newline[i] == '\n') {
+ newline[i]='\0';
+ break;
+ }
+ }
+ return newline;
+}
+
+
+/* Copy string on line to new allocated string str */
+/* String is always null terminated */
+/* String is no longer than len */
+/* Return pointer to string */
+/* Store number of used characters from line */
+/* Don't copy enclosing () */
+dsc_private char *
+dsc_copy_string(char *str, unsigned int slen, char *line,
+ unsigned int len, unsigned int *offset)
+{
+ int quoted = FALSE;
+ int instring=0;
+ unsigned int newlength = 0;
+ unsigned int i = 0;
+ unsigned char ch;
+ if (len > slen)
+ len = slen-1;
+ while ( (i<len) && IS_WHITE(line[i]))
+ i++; /* skip leading spaces */
+ if (line[i]=='(') {
+ quoted = TRUE;
+ instring++;
+ i++; /* don't copy outside () */
+ }
+ while (i < len) {
+ str[newlength] = ch = line[i];
+ i++;
+ if (quoted) {
+ if (ch == '(')
+ instring++;
+ if (ch == ')')
+ instring--;
+ if (instring==0)
+ break;
+ }
+ else if (ch == ' ')
+ break;
+
+ if (ch == '\r')
+ break;
+ if (ch == '\n')
+ break;
+ else if ( (ch == '\\') && (i+1 < len) ) {
+ ch = line[i];
+ if ((ch >= '0') && (ch <= '9')) {
+ /* octal coded character */
+ int j = 3;
+ ch = 0;
+ while (j && (i < len) && line[i]>='0' && line[i]<='7') {
+ ch = (unsigned char)((ch<<3) + (line[i]-'0'));
+ i++;
+ j--;
+ }
+ str[newlength] = ch;
+ }
+ else if (ch == '(') {
+ str[newlength] = ch;
+ i++;
+ }
+ else if (ch == ')') {
+ str[newlength] = ch;
+ i++;
+ }
+ else if (ch == 'b') {
+ str[newlength] = '\b';
+ i++;
+ }
+ else if (ch == 'f') {
+ str[newlength] = '\b';
+ i++;
+ }
+ else if (ch == 'n') {
+ str[newlength] = '\n';
+ i++;
+ }
+ else if (ch == 'r') {
+ str[newlength] = '\r';
+ i++;
+ }
+ else if (ch == 't') {
+ str[newlength] = '\t';
+ i++;
+ }
+ else if (ch == '\\') {
+ str[newlength] = '\\';
+ i++;
+ }
+ }
+ newlength++;
+ }
+ str[newlength] = '\0';
+ if (offset != (unsigned int *)NULL)
+ *offset = i;
+ return str;
+}
+
+dsc_private int
+dsc_get_int(const char *line, unsigned int len, unsigned int *offset)
+{
+ char newline[MAXSTR];
+ int newlength = 0;
+ unsigned int i = 0;
+ unsigned char ch;
+
+ len = min((size_t) len, (size_t) sizeof(newline)-1);
+ while ((i<len) && IS_WHITE(line[i]))
+ i++; /* skip leading spaces */
+ while (i < len) {
+ newline[newlength] = ch = line[i];
+ if (!(isdigit(ch) || (ch=='-') || (ch=='+')))
+ break; /* not part of an integer number */
+ i++;
+ newlength++;
+ }
+ while ((i<len) && IS_WHITE(line[i]))
+ i++; /* skip trailing spaces */
+ newline[newlength] = '\0';
+ if (offset != (unsigned int *)NULL)
+ *offset = i;
+ return atoi(newline);
+}
+
+dsc_private float
+dsc_get_real(const char *line, unsigned int len, unsigned int *offset)
+{
+ char newline[MAXSTR];
+ int newlength = 0;
+ unsigned int i = 0;
+ unsigned char ch;
+
+ len = min((size_t) len, (size_t) sizeof(newline)-1);
+ while ((i<len) && IS_WHITE(line[i]))
+ i++; /* skip leading spaces */
+ while (i < len) {
+ newline[newlength] = ch = line[i];
+ if (!(isdigit(ch) || (ch=='.') || (ch=='-') || (ch=='+')
+ || (ch=='e') || (ch=='E')))
+ break; /* not part of a real number */
+ i++;
+ newlength++;
+ }
+ while ((i<len) && IS_WHITE(line[i]))
+ i++; /* skip trailing spaces */
+
+ newline[newlength] = '\0';
+
+ if (offset != (unsigned int *)NULL)
+ *offset = i;
+ return (float)atof(newline);
+}
+
+dsc_private int
+dsc_stricmp(const char *s, const char *t)
+{
+ while (toupper(*s) == toupper(*t)) {
+ if (*s == '\0')
+ return 0;
+ s++;
+ t++;
+ }
+ return (toupper(*s) - toupper(*t));
+}
+
+
+dsc_private int
+dsc_parse_page(CDSC *dsc)
+{
+ char *p;
+ unsigned int i;
+ char page_label[MAXSTR];
+ char *pl;
+ int page_ordinal;
+ int page_number;
+
+ p = dsc->line + 7;
+ pl = dsc_copy_string(page_label, sizeof(page_label)-1, p, dsc->line_length-7, &i);
+ if (pl == NULL)
+ return CDSC_ERROR;
+ p += i;
+ page_ordinal = atoi(p);
+
+ if ( (page_ordinal == 0) || (strlen(page_label) == 0) ||
+ (dsc->page_count &&
+ (page_ordinal != dsc->page[dsc->page_count-1].ordinal+1)) ) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_PAGE_ORDINAL, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* ignore this page */
+ return CDSC_OK;
+ case CDSC_RESPONSE_CANCEL:
+ /* accept the page */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+
+ page_number = dsc->page_count;
+ dsc_add_page(dsc, page_ordinal, page_label);
+ dsc->page[page_number].begin = DSC_START(dsc);
+ dsc->page[page_number].end = DSC_START(dsc);
+
+ if (dsc->page[page_number].label == NULL)
+ return CDSC_ERROR; /* no memory */
+
+ return CDSC_OK;
+}
+
+
+
+/* DSC error reporting */
+
+void
+dsc_debug_print(CDSC *dsc, const char *str)
+{
+ if (dsc->debug_print_fn)
+ dsc->debug_print_fn(dsc->caller_data, str);
+}
+
+
+/* Display a message about a problem with the DSC comments.
+ *
+ * explanation = an index to to a multiline explanation in dsc_message[]
+ * line = pointer to the offending DSC line (if any)
+ * return code =
+ * CDSC_RESPONSE_OK DSC was wrong, make a guess about what
+ * was really meant.
+ * CDSC_RESPONSE_CANCEL Assume DSC was correct, ignore if it
+ * is misplaced.
+ * CDSC_RESPONSE_IGNORE_ALL Ignore all DSC.
+ */
+/* Silent operation. Don't display errors. */
+dsc_private int
+dsc_error(CDSC *dsc, unsigned int explanation,
+ char *line, unsigned int line_len)
+{
+ /* if error function provided, use it */
+ if (dsc->dsc_error_fn)
+ return dsc->dsc_error_fn(dsc->caller_data, dsc,
+ explanation, line, line_len);
+
+ /* treat DSC as being correct */
+ return CDSC_RESPONSE_CANCEL;
+}
+
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/dscparse.h b/kghostview/dscparse.h
new file mode 100644
index 00000000..10d2746c
--- /dev/null
+++ b/kghostview/dscparse.h
@@ -0,0 +1,473 @@
+/* Copyright (C) 2000-2001, Ghostgum Software Pty Ltd. All rights reserved.
+
+ This file is part of GSview.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+ to anyone for the consequences of using it or for whether it serves any
+ particular purpose or works at all, unless he says so in writing. Refer
+ to the GNU General Public Licence for full details.
+
+ Everyone is granted permission to copy, modify and redistribute this
+ file, but only under the conditions described in the GNU General
+ Public Licence. A copy of this license is supposed to have been given
+ to you along with this file so you can know your rights and
+ responsibilities. It should be in a file named COPYING. Among other
+ things, the copyright notice and this notice must be preserved on all
+ copies.
+*/
+
+/* $Id$ */
+
+/* dscparse.h */
+/* Interface for the DSC parser. */
+
+#ifndef _DSCPARSE_H_
+#define _DSCPARSE_H_
+
+/* Some local types that may need modification */
+typedef bool GSBOOL;
+typedef unsigned long GSDWORD; /* must be at least 32 bits */
+typedef unsigned int GSWORD; /* must be at least 16 bits */
+
+#ifndef FALSE
+# define FALSE ((GSBOOL)0)
+# define TRUE ((GSBOOL)(!FALSE))
+#endif
+
+#ifndef dsc_private
+# ifdef private
+# define dsc_private private
+# else
+# define dsc_private static
+# endif
+#endif
+
+/* macros to allow conversion of function declarations to K&R */
+#ifndef P0
+#define P0() void
+#define P1(t1) t1
+#define P2(t1,t2) t1,t2
+#define P3(t1,t2,t3) t1,t2,t3
+#define P4(t1,t2,t3,t4) t1,t2,t3,t4
+#define P5(t1,t2,t3,t4,t5) t1,t2,t3,t4,t5
+#define P6(t1,t2,t3,t4,t5,t6) t1,t2,t3,t4,t5,t6
+#endif
+
+/* maximum legal length of lines in a DSC compliant file */
+#define DSC_LINE_LENGTH 255
+
+/* memory for strings is allocated in chunks of this length */
+#define CDSC_STRING_CHUNK 4096
+
+/* page array is allocated in chunks of this many pages */
+#define CDSC_PAGE_CHUNK 128
+
+/* buffer length for storing lines passed to dsc_scan_data() */
+/* must be at least 2 * DSC_LINE_LENGTH */
+/* We choose 8192 as twice the length passed to us by GSview */
+#define CDSC_DATA_LENGTH 8192
+
+/* Return codes from dsc_scan_data()
+ * < 0 = error
+ * >=0 = OK
+ *
+ * -1 = error, usually insufficient memory.
+ * 0-9 = normal
+ * 10-99 = internal codes, should not be seen.
+ * 100-999 = identifier of last DSC comment processed.
+ */
+
+typedef enum {
+ CDSC_ERROR = -1, /* Fatal error, usually insufficient memory */
+
+ CDSC_OK = 0, /* OK, no DSC comment found */
+ CDSC_NOTDSC = 1, /* Not DSC, or DSC is being ignored */
+
+/* Any section */
+ CDSC_UNKNOWNDSC = 100, /* DSC comment not recognised */
+
+/* Header section */
+ CDSC_PSADOBE = 200, /* %!PS-Adobe- */
+ CDSC_BEGINCOMMENTS = 201, /* %%BeginComments */
+ CDSC_ENDCOMMENTS = 202, /* %%EndComments */
+ CDSC_PAGES = 203, /* %%Pages: */
+ CDSC_CREATOR = 204, /* %%Creator: */
+ CDSC_CREATIONDATE = 205, /* %%CreationDate: */
+ CDSC_TITLE = 206, /* %%Title: */
+ CDSC_FOR = 207, /* %%For: */
+ CDSC_LANGUAGELEVEL = 208, /* %%LanguageLevel: */
+ CDSC_BOUNDINGBOX = 209, /* %%BoundingBox: */
+ CDSC_ORIENTATION = 210, /* %%Orientation: */
+ CDSC_PAGEORDER = 211, /* %%PageOrder: */
+ CDSC_DOCUMENTMEDIA = 212, /* %%DocumentMedia: */
+ CDSC_DOCUMENTPAPERSIZES = 213, /* %%DocumentPaperSizes: */
+ CDSC_DOCUMENTPAPERFORMS = 214, /* %%DocumentPaperForms: */
+ CDSC_DOCUMENTPAPERCOLORS = 215, /* %%DocumentPaperColors: */
+ CDSC_DOCUMENTPAPERWEIGHTS = 216, /* %%DocumentPaperWeights: */
+ CDSC_DOCUMENTDATA = 217, /* %%DocumentData: */
+ CDSC_REQUIREMENTS = 218, /* IGNORED %%Requirements: */
+ CDSC_DOCUMENTNEEDEDFONTS = 219, /* IGNORED %%DocumentNeededFonts: */
+ CDSC_DOCUMENTSUPPLIEDFONTS = 220, /* IGNORED %%DocumentSuppliedFonts: */
+ CDSC_HIRESBOUNDINGBOX = 221, /* %%HiResBoundingBox: */
+ CDSC_CROPBOX = 222, /* %%CropBox: */
+
+/* Preview section */
+ CDSC_BEGINPREVIEW = 301, /* %%BeginPreview */
+ CDSC_ENDPREVIEW = 302, /* %%EndPreview */
+
+/* Defaults section */
+ CDSC_BEGINDEFAULTS = 401, /* %%BeginDefaults */
+ CDSC_ENDDEFAULTS = 402, /* %%EndDefaults */
+/* also %%PageMedia, %%PageOrientation, %%PageBoundingBox */
+
+/* Prolog section */
+ CDSC_BEGINPROLOG = 501, /* %%BeginProlog */
+ CDSC_ENDPROLOG = 502, /* %%EndProlog */
+ CDSC_BEGINFONT = 503, /* IGNORED %%BeginFont */
+ CDSC_ENDFONT = 504, /* IGNORED %%EndFont */
+ CDSC_BEGINFEATURE = 505, /* IGNORED %%BeginFeature */
+ CDSC_ENDFEATURE = 506, /* IGNORED %%EndFeature */
+ CDSC_BEGINRESOURCE = 507, /* IGNORED %%BeginResource */
+ CDSC_ENDRESOURCE = 508, /* IGNORED %%EndResource */
+ CDSC_BEGINPROCSET = 509, /* IGNORED %%BeginProcSet */
+ CDSC_ENDPROCSET = 510, /* IGNORED %%EndProcSet */
+
+/* Setup section */
+ CDSC_BEGINSETUP = 601, /* %%BeginSetup */
+ CDSC_ENDSETUP = 602, /* %%EndSetup */
+ CDSC_FEATURE = 603, /* IGNORED %%Feature: */
+ CDSC_PAPERCOLOR = 604, /* IGNORED %%PaperColor: */
+ CDSC_PAPERFORM = 605, /* IGNORED %%PaperForm: */
+ CDSC_PAPERWEIGHT = 606, /* IGNORED %%PaperWeight: */
+ CDSC_PAPERSIZE = 607, /* %%PaperSize: */
+/* also %%Begin/EndFeature, %%Begin/EndResource */
+
+/* Page section */
+ CDSC_PAGE = 700, /* %%Page: */
+ CDSC_PAGETRAILER = 701, /* IGNORED %%PageTrailer */
+ CDSC_BEGINPAGESETUP = 702, /* IGNORED %%BeginPageSetup */
+ CDSC_ENDPAGESETUP = 703, /* IGNORED %%EndPageSetup */
+ CDSC_PAGEMEDIA = 704, /* %%PageMedia: */
+/* also %%PaperColor, %%PaperForm, %%PaperWeight, %%PaperSize */
+ CDSC_PAGEORIENTATION = 705, /* %%PageOrientation: */
+ CDSC_PAGEBOUNDINGBOX = 706, /* %%PageBoundingBox: */
+/* also %%Begin/EndFont, %%Begin/EndFeature */
+/* also %%Begin/EndResource, %%Begin/EndProcSet */
+ CDSC_INCLUDEFONT = 707, /* IGNORED %%IncludeFont: */
+ CDSC_VIEWINGORIENTATION = 708, /* %%ViewingOrientation: */
+
+/* Trailer section */
+ CDSC_TRAILER = 800, /* %%Trailer */
+/* also %%Pages, %%BoundingBox, %%Orientation, %%PageOrder, %%DocumentMedia */
+/* %%Page is recognised as an error */
+/* also %%DocumentNeededFonts, %%DocumentSuppliedFonts */
+
+/* End of File */
+ CDSC_EOF = 900 /* %%EOF */
+} CDSC_RETURN_CODE;
+
+
+/* stored in dsc->preview */
+typedef enum {
+ CDSC_NOPREVIEW = 0,
+ CDSC_EPSI = 1,
+ CDSC_TIFF = 2,
+ CDSC_WMF = 3,
+ CDSC_PICT = 4
+} CDSC_PREVIEW_TYPE;
+
+/* stored in dsc->page_order */
+typedef enum {
+ CDSC_ORDER_UNKNOWN = 0,
+ CDSC_ASCEND = 1,
+ CDSC_DESCEND = 2,
+ CDSC_SPECIAL = 3
+} CDSC_PAGE_ORDER;
+
+/* stored in dsc->page_orientation and dsc->page[pagenum-1].orientation */
+typedef enum {
+ CDSC_ORIENT_UNKNOWN = 0,
+ CDSC_PORTRAIT = 1,
+ CDSC_LANDSCAPE = 2,
+ CDSC_UPSIDEDOWN = 3,
+ CDSC_SEASCAPE = 4
+} CDSC_ORIENTATION_ENUM;
+
+/* stored in dsc->document_data */
+typedef enum {
+ CDSC_DATA_UNKNOWN = 0,
+ CDSC_CLEAN7BIT = 1,
+ CDSC_CLEAN8BIT = 2,
+ CDSC_BINARY = 3
+} CDSC_DOCUMENT_DATA ;
+
+typedef struct CDSCBBOX_S {
+ int llx;
+ int lly;
+ int urx;
+ int ury;
+} CDSCBBOX;
+
+typedef struct CDSCFBBOX_S {
+ float fllx;
+ float flly;
+ float furx;
+ float fury;
+} CDSCFBBOX;
+
+typedef struct CDSCMEDIA_S {
+ const char *name;
+ float width; /* PostScript points */
+ float height;
+ float weight; /* GSM */
+ const char *colour;
+ const char *type;
+ CDSCBBOX *mediabox; /* Used by GSview for PDF MediaBox */
+} CDSCMEDIA;
+
+#define CDSC_KNOWN_MEDIA 46
+extern const CDSCMEDIA dsc_known_media[CDSC_KNOWN_MEDIA];
+
+typedef struct CDSCCTM_S { /* used for %%ViewingOrientation */
+ float xx;
+ float xy;
+ float yx;
+ float yy;
+ /* float ty; */
+ /* float ty; */
+} CDSCCTM;
+
+typedef struct CDSCPAGE_S {
+ int ordinal;
+ const char *label;
+ unsigned long begin;
+ unsigned long end;
+ unsigned int orientation;
+ const CDSCMEDIA *media;
+ CDSCBBOX *bbox; /* PageBoundingBox, also used by GSview for PDF CropBox */
+ CDSCCTM *viewing_orientation;
+} CDSCPAGE;
+
+/* binary DOS EPS header */
+typedef struct CDSCDOSEPS_S {
+ GSDWORD ps_begin;
+ GSDWORD ps_length;
+ GSDWORD wmf_begin;
+ GSDWORD wmf_length;
+ GSDWORD tiff_begin;
+ GSDWORD tiff_length;
+ GSWORD checksum;
+} CDSCDOSEPS;
+
+/* rather than allocated every string with malloc, we allocate
+ * chunks of 4k and place the (usually) short strings in these
+ * chunks.
+ */
+typedef struct CDSCSTRING_S CDSCSTRING;
+struct CDSCSTRING_S {
+ unsigned int index;
+ unsigned int length;
+ char *data;
+ CDSCSTRING *next;
+};
+
+
+/* DSC error reporting */
+
+typedef enum {
+ CDSC_MESSAGE_BBOX = 0,
+ CDSC_MESSAGE_EARLY_TRAILER = 1,
+ CDSC_MESSAGE_EARLY_EOF = 2,
+ CDSC_MESSAGE_PAGE_IN_TRAILER = 3,
+ CDSC_MESSAGE_PAGE_ORDINAL = 4,
+ CDSC_MESSAGE_PAGES_WRONG = 5,
+ CDSC_MESSAGE_EPS_NO_BBOX = 6,
+ CDSC_MESSAGE_EPS_PAGES = 7,
+ CDSC_MESSAGE_NO_MEDIA = 8,
+ CDSC_MESSAGE_ATEND = 9,
+ CDSC_MESSAGE_DUP_COMMENT = 10,
+ CDSC_MESSAGE_DUP_TRAILER = 11,
+ CDSC_MESSAGE_BEGIN_END = 12,
+ CDSC_MESSAGE_BAD_SECTION = 13,
+ CDSC_MESSAGE_LONG_LINE = 14,
+ CDSC_MESSAGE_INCORRECT_USAGE = 15
+} CDSC_MESSAGE_ERROR;
+
+/* severity */
+typedef enum {
+ CDSC_ERROR_INFORM = 0, /* Not an error */
+ CDSC_ERROR_WARN = 1, /* Not a DSC error itself, */
+ CDSC_ERROR_ERROR = 2 /* DSC error */
+} CDSC_MESSAGE_SEVERITY;
+
+/* response */
+typedef enum {
+ CDSC_RESPONSE_OK = 0,
+ CDSC_RESPONSE_CANCEL = 1,
+ CDSC_RESPONSE_IGNORE_ALL = 2
+} CDSC_RESPONSE;
+
+extern const char * const dsc_message[];
+
+typedef struct CDSC_S CDSC;
+struct CDSC_S {
+ /* public data */
+ GSBOOL dsc; /* TRUE if DSC comments found */
+ GSBOOL ctrld; /* TRUE if has CTRLD at start of stream */
+ GSBOOL pjl; /* TRUE if has HP PJL at start of stream */
+ GSBOOL epsf; /* TRUE if EPSF */
+ GSBOOL pdf; /* TRUE if Portable Document Format */
+ unsigned int preview; /* enum CDSC_PREVIEW_TYPE */
+ char *dsc_version; /* first line of file */
+ unsigned int language_level;
+ unsigned int document_data; /* Clean7Bit, Clean8Bit, Binary */
+ /* enum CDSC_DOCUMENT_DATA */
+ /* DSC sections */
+ unsigned long begincomments;
+ unsigned long endcomments;
+ unsigned long beginpreview;
+ unsigned long endpreview;
+ unsigned long begindefaults;
+ unsigned long enddefaults;
+ unsigned long beginprolog;
+ unsigned long endprolog;
+ unsigned long beginsetup;
+ unsigned long endsetup;
+ unsigned long begintrailer;
+ unsigned long endtrailer;
+ CDSCPAGE *page;
+ unsigned int page_count; /* number of %%Page: pages in document */
+ unsigned int page_pages; /* number of pages in document from %%Pages: */
+ unsigned int page_order; /* enum CDSC_PAGE_ORDER */
+ unsigned int page_orientation; /* the default page orientation */
+ /* enum CDSC_ORIENTATION */
+ CDSCCTM *viewing_orientation;
+ unsigned int media_count; /* number of media items */
+ CDSCMEDIA **media; /* the array of media */
+ const CDSCMEDIA *page_media;/* the default page media */
+ CDSCBBOX *bbox; /* the document bounding box */
+ CDSCBBOX *page_bbox; /* the default page bounding box */
+ CDSCDOSEPS *doseps; /* DOS binary header */
+ char *dsc_title;
+ char *dsc_creator;
+ char *dsc_date;
+ char *dsc_for;
+
+ unsigned int max_error; /* highest error number that will be reported */
+ const int *severity; /* array of severity values, one per error */
+
+
+ /* private data */
+ void *caller_data; /* pointer to be provided when calling */
+ /* error and debug callbacks */
+ int id; /* last DSC comment found */
+ int scan_section; /* section currently being scanned */
+ /* enum CDSC_SECTION */
+
+ unsigned long doseps_end; /* ps_begin+ps_length, otherwise 0 */
+ unsigned int page_chunk_length; /* number of pages allocated */
+ unsigned long file_length; /* length of document */
+ /* If provided we try to recognise %%Trailer and %%EOF */
+ /* incorrectly embedded inside document. */
+ /* Can be left set to default value of 0 */
+ int skip_document; /* recursion level of %%BeginDocument: */
+ int skip_bytes; /* #bytes to ignore from BeginData: */
+ /* or DOSEPS preview section */
+ int skip_lines; /* #lines to ignore from BeginData: */
+ GSBOOL skip_pjl; /* TRUE if skip PJL until first PS comment */
+ int begin_font_count; /* recursion level of %%BeginFont */
+ int begin_feature_count; /* recursion level of %%BeginFeature */
+ int begin_resource_count; /* recursion level of %%BeginResource */
+ int begin_procset_count; /* recursion level of %%BeginProcSet */
+
+ /* buffer for input */
+ char data[CDSC_DATA_LENGTH];/* start of buffer */
+ unsigned int data_length; /* length of data in buffer */
+ unsigned int data_index; /* offset to next char in buffer */
+ unsigned long data_offset; /* offset from start of document */
+ /* to byte in data[0] */
+ GSBOOL eof; /* TRUE if there is no more data */
+
+ /* information about DSC line */
+ char *line; /* pointer to last read DSC line */
+ /* not null terminated */
+ unsigned int line_length; /* number of characters in line */
+ GSBOOL eol; /* TRUE if dsc_line contains EOL */
+ GSBOOL last_cr; /* TRUE if last line ended in \r */
+ /* check next time for \n */
+ unsigned int line_count; /* line number */
+ GSBOOL long_line; /* TRUE if found a line longer than 255 characters */
+ char last_line[256]; /* previous DSC line, used for %%+ */
+
+ /* more efficient string storage (for short strings) than malloc */
+ CDSCSTRING *string_head; /* linked list head */
+ CDSCSTRING *string; /* current list item */
+
+ /* memory allocation routines */
+ void *(*memalloc)(P2(size_t size, void *closure_data));
+ void (*memfree)(P2(void *ptr, void *closure_data));
+ void *mem_closure_data;
+
+ /* function for printing debug messages */
+ void (*debug_print_fn)(P2(void *caller_data, const char *str));
+
+ /* function for reporting errors in DSC comments */
+ int (*dsc_error_fn)(P5(void *caller_data, CDSC *dsc,
+ unsigned int explanation, const char *line, unsigned int line_len));
+
+ /* public data */
+ /* Added 2001-10-01 */
+ CDSCFBBOX *hires_bbox; /* the hires document bounding box */
+ CDSCFBBOX *crop_box; /* the size of the trimmed page */
+};
+
+
+/* Public functions */
+
+/* Create and initialise DSC parser */
+CDSC *dsc_init(P1(void *caller_data));
+
+CDSC *dsc_init_with_alloc(P4(
+ void *caller_data,
+ void *(*memalloc)(size_t size, void *closure_data),
+ void (*memfree)(void *ptr, void *closure_data),
+ void *closure_data));
+
+/* Free the DSC parser */
+void dsc_free(P1(CDSC *dsc));
+
+/* Tell DSC parser how long document will be, to allow ignoring
+ * of early %%Trailer and %%EOF. This is optional.
+ */
+void dsc_set_length(P2(CDSC *dsc, unsigned long len));
+
+/* Process a buffer containing DSC comments and PostScript */
+int dsc_scan_data(P3(CDSC *dsc, const char *data, int len));
+
+/* All data has been processed, fixup any DSC errors */
+int dsc_fixup(P1(CDSC *dsc));
+
+/* Install error query function */
+void dsc_set_error_function(P2(CDSC *dsc,
+ int (*dsc_error_fn)(P5(void *caller_data, CDSC *dsc,
+ unsigned int explanation, const char *line, unsigned int line_len))));
+
+/* Install print function for debug messages */
+void dsc_set_debug_function(P2(CDSC *dsc,
+ void (*debug_fn)(P2(void *caller_data, const char *str))));
+
+/* Print a message to debug output, if provided */
+void dsc_debug_print(P2(CDSC *dsc, const char *str));
+
+/* should be internal only functions, but made available to
+ * GSview for handling PDF
+ */
+int dsc_add_page(P3(CDSC *dsc, int ordinal, char *label));
+int dsc_add_media(P2(CDSC *dsc, CDSCMEDIA *media));
+int dsc_set_page_bbox(P6(CDSC *dsc, unsigned int page_number,
+ int llx, int lly, int urx, int ury));
+
+#endif
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/dscparse_adapter.cpp b/kghostview/dscparse_adapter.cpp
new file mode 100644
index 00000000..4db450bb
--- /dev/null
+++ b/kghostview/dscparse_adapter.cpp
@@ -0,0 +1,420 @@
+/**
+ * Copyright (C) 2001 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "dscparse_adapter.h"
+
+using namespace std;
+
+/*-- KDSCBBOX implementation -----------------------------------------------*/
+
+KDSCBBOX::KDSCBBOX() :
+ _llx( 0 ), _lly( 0 ),
+ _urx( 0 ), _ury( 0 )
+{}
+
+KDSCBBOX::KDSCBBOX( const KDSCBBOX& b ) :
+ _llx( b._llx ), _lly( b._lly ),
+ _urx( b._urx ), _ury( b._ury )
+{}
+
+KDSCBBOX::KDSCBBOX( int llx, int lly, int urx, int ury ) :
+ _llx( llx ), _lly( lly ),
+ _urx( urx ), _ury( ury )
+{}
+
+KDSCBBOX::KDSCBBOX( const CDSCBBOX& bbox ) :
+ _llx( bbox.llx ), _lly( bbox.lly ),
+ _urx( bbox.urx ), _ury( bbox.ury )
+{}
+
+KDSCBBOX& KDSCBBOX::operator = ( const KDSCBBOX& b )
+{
+ _llx = b._llx; _lly = b._lly; _urx = b._urx; _ury = b._ury;
+ return *this;
+}
+
+bool KDSCBBOX::operator == ( const KDSCBBOX& b )
+{
+ return ( _llx == b._llx && _lly == b._lly
+ && _urx == b._urx && _ury == b._ury );
+}
+
+bool KDSCBBOX::operator != ( const KDSCBBOX& b )
+{
+ return !( *this == b );
+}
+
+int KDSCBBOX::llx() const { return _llx; }
+int KDSCBBOX::lly() const { return _lly; }
+int KDSCBBOX::urx() const { return _urx; }
+int KDSCBBOX::ury() const { return _ury; }
+
+int KDSCBBOX::width() const { return _urx - _llx; }
+int KDSCBBOX::height() const { return _ury - _lly; }
+
+QSize KDSCBBOX::size() const { return QSize( width(), height() ); }
+
+ostream& operator << ( ostream& os, const KDSCBBOX& source )
+{
+ os << "{ llx: "<< source.llx() << ", lly: " << source.lly()
+ << " urx: "<< source.urx() << ", ury: " << source.ury() << " }";
+ return os;
+}
+
+/*-- KDSCError implementation ----------------------------------------------*/
+
+KDSCError::KDSCError( Type type, Severity severity, const QCString& line,
+ unsigned int lineNumber ) :
+ _type( type ),
+ _severity( severity ),
+ _line( line ),
+ _lineNumber( lineNumber )
+{}
+
+KDSCError::Type KDSCError::type() const
+{
+ return _type;
+}
+
+KDSCError::Severity KDSCError::severity() const
+{
+ return _severity;
+}
+
+QCString KDSCError::line() const
+{
+ return _line;
+}
+
+unsigned int KDSCError::lineNumber() const
+{
+ return _lineNumber;
+}
+
+/*-- KDSCOkErrorHandler implementation -------------------------------------*/
+
+KDSCErrorHandler::Response KDSCOkErrorHandler::error( const KDSCError& err )
+{
+ cout << "KDSC: error in line " << err.lineNumber() << endl;
+ cout << err.line() << endl;
+ return Ok;
+}
+
+/*-- KDSC implementation ---------------------------------------------------*/
+
+KDSC::KDSC() :
+ _errorHandler( 0 ),
+ _commentHandler( 0 )
+{
+ _cdsc = dsc_init( this );
+ Q_ASSERT( _cdsc != 0 );
+ _scanHandler = new KDSCScanHandler( _cdsc );
+}
+
+KDSC::~KDSC()
+{
+ dsc_free( _cdsc );
+ delete _scanHandler;
+}
+
+QString KDSC::dsc_version() const
+{
+ return QString( _cdsc->dsc_version );
+}
+
+bool KDSC::dsc() const
+{
+ return ( _cdsc->dsc == TRUE );
+}
+
+bool KDSC::ctrld() const
+{
+ return ( _cdsc->ctrld == TRUE );
+}
+
+bool KDSC::pjl() const
+{
+ return ( _cdsc->pjl == TRUE );
+}
+
+bool KDSC::epsf() const
+{
+ return ( _cdsc->epsf == TRUE );
+}
+
+bool KDSC::pdf() const
+{
+ return ( _cdsc->pdf == TRUE );
+}
+
+unsigned int KDSC::preview() const
+{
+ return _cdsc->preview;
+}
+
+unsigned int KDSC::language_level() const
+{
+ return _cdsc->language_level;
+}
+
+unsigned int KDSC::document_data() const
+{
+ return _cdsc->document_data;
+}
+
+unsigned long KDSC::begincomments() const
+{
+ return _cdsc->begincomments;
+}
+
+unsigned long KDSC::endcomments() const
+{
+ return _cdsc->endcomments;
+}
+
+unsigned long KDSC::beginpreview() const
+{
+ return _cdsc->beginpreview;
+}
+
+unsigned long KDSC::endpreview() const
+{
+ return _cdsc->endpreview;
+}
+
+unsigned long KDSC::begindefaults() const
+{
+ return _cdsc->begindefaults;
+}
+
+unsigned long KDSC::enddefaults() const
+{
+ return _cdsc->enddefaults;
+}
+
+unsigned long KDSC::beginprolog() const
+{
+ return _cdsc->beginprolog;
+}
+
+unsigned long KDSC::endprolog() const
+{
+ return _cdsc->endprolog;
+}
+
+unsigned long KDSC::beginsetup() const
+{
+ return _cdsc->beginsetup;
+}
+
+unsigned long KDSC::endsetup() const
+{
+ return _cdsc->endsetup;
+}
+
+unsigned long KDSC::begintrailer() const
+{
+ return _cdsc->begintrailer;
+}
+
+unsigned long KDSC::endtrailer() const
+{
+ return _cdsc->endtrailer;
+}
+
+CDSCPAGE* KDSC::page() const
+{
+ return _cdsc->page;
+}
+
+unsigned int KDSC::page_count() const
+{
+ return _cdsc->page_count;
+}
+
+unsigned int KDSC::page_pages() const
+{
+ return _cdsc->page_pages;
+}
+
+unsigned int KDSC::page_order() const
+{
+ return _cdsc->page_order;
+}
+
+unsigned int KDSC::page_orientation() const
+{
+ return _cdsc->page_orientation;
+}
+
+CDSCCTM* KDSC::viewing_orientation() const
+{
+ return _cdsc->viewing_orientation;
+}
+
+unsigned int KDSC::media_count() const
+{
+ return _cdsc->media_count;
+}
+
+CDSCMEDIA** KDSC::media() const
+{
+ return _cdsc->media;
+}
+
+const CDSCMEDIA* KDSC::page_media() const
+{
+ return _cdsc->page_media;
+}
+
+auto_ptr<KDSCBBOX> KDSC::bbox() const
+{
+ if( _cdsc->bbox == 0 )
+ return auto_ptr<KDSCBBOX>( 0 );
+ else
+ return auto_ptr<KDSCBBOX>( new KDSCBBOX( *_cdsc->bbox ) );
+}
+
+auto_ptr<KDSCBBOX> KDSC::page_bbox() const
+{
+ if( _cdsc->page_bbox == 0 )
+ return auto_ptr<KDSCBBOX>( 0 );
+ else
+ return auto_ptr<KDSCBBOX>( new KDSCBBOX( *_cdsc->page_bbox ) );
+}
+
+QString KDSC::dsc_title() const
+{
+ return QString( _cdsc->dsc_title );
+}
+
+QString KDSC::dsc_creator() const
+{
+ return QString( _cdsc->dsc_creator );
+}
+
+QString KDSC::dsc_date() const
+{
+ return QString( _cdsc->dsc_date );
+}
+
+QString KDSC::dsc_for() const
+{
+ return QString( _cdsc->dsc_for );
+}
+
+bool KDSC::scanData( char* buffer, unsigned int count )
+{
+ return _scanHandler->scanData( buffer, count );
+}
+
+int KDSC::fixup()
+{
+ return dsc_fixup( _cdsc );
+}
+
+KDSCErrorHandler* KDSC::errorHandler() const
+{
+ return _errorHandler;
+}
+
+void KDSC::setErrorHandler( KDSCErrorHandler* errorHandler )
+{
+ _errorHandler = errorHandler;
+ if( errorHandler == 0 )
+ dsc_set_error_function( _cdsc, 0 );
+ else
+ dsc_set_error_function( _cdsc, &errorFunction );
+}
+
+KDSCCommentHandler* KDSC::commentHandler() const
+{
+ return _commentHandler;
+}
+
+void KDSC::setCommentHandler( KDSCCommentHandler* commentHandler )
+{
+ if( _commentHandler != 0 && commentHandler == 0 )
+ {
+ delete _scanHandler;
+ _scanHandler = new KDSCScanHandler( _cdsc );
+ }
+ else if( _commentHandler == 0 && commentHandler != 0 )
+ {
+ delete _scanHandler;
+ _scanHandler = new KDSCScanHandlerByLine( _cdsc, commentHandler );
+ }
+ _commentHandler = commentHandler;
+}
+
+bool KDSC::isStructured() const
+{
+ return epsf() ? ( page_count() > 1 ) : ( page_count() > 0 );
+}
+
+CDSC* KDSC::cdsc() const
+{
+ return _cdsc;
+}
+
+int KDSC::errorFunction( void* caller_data, CDSC* dsc,
+ unsigned int explanation, const char* line, unsigned int line_len )
+{
+ KDSCError error(
+ static_cast< KDSCError::Type >( explanation ),
+ static_cast< KDSCError::Severity >( dsc->severity[explanation] ),
+ QCString( line, line_len + 1 ),
+ dsc->line_count
+ );
+
+ KDSC* kdsc = static_cast< KDSC* >( caller_data );
+ Q_ASSERT( kdsc );
+
+ return kdsc->errorHandler()->error( error );
+}
+
+bool KDSCScanHandlerByLine::scanData( char* buf, unsigned int count )
+{
+ char* lineStart = buf;
+ char* it = buf;
+ while( it < buf + count )
+ {
+ if( *it++ == '\n' )
+ {
+ int retval = dsc_scan_data( _cdsc, lineStart, it - lineStart );
+ if( retval < 0 )
+ return false;
+ else if( retval > 0 )
+ {
+ _commentHandler->comment(
+ static_cast<KDSCCommentHandler::Name>( retval ) );
+ }
+ lineStart = it;
+ }
+ }
+
+ if( it != lineStart )
+ {
+ // Scan the remaining part of the string.
+ return ( dsc_scan_data( _cdsc, lineStart, it - lineStart ) < 0 );
+ }
+ else
+ return true;
+}
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/dscparse_adapter.h b/kghostview/dscparse_adapter.h
new file mode 100644
index 00000000..9e41f3c2
--- /dev/null
+++ b/kghostview/dscparse_adapter.h
@@ -0,0 +1,386 @@
+/**
+ * Copyright (C) 2001 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef DSCPARSE_ADAPTER_H
+#define DSCPARSE_ADAPTER_H
+
+#include <iostream>
+#include <map>
+#include <memory>
+
+#include <qsize.h>
+#include <qstring.h>
+
+#include "dscparse.h"
+
+#if defined(__GNUC__)
+#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 93)
+/*
+ * We add a quick 'n' dirty inline implementation of auto_ptr for older
+ * releases of GCC, which don't include an auto_ptr implementation in
+ * <memory>.
+ */
+
+template <class T> class auto_ptr {
+private:
+ T* _ptr;
+
+public:
+ typedef T element_type;
+ explicit auto_ptr(T* p = 0) : _ptr(p) {}
+ auto_ptr(auto_ptr& a) : _ptr(a.release()) {}
+ template <class T1> auto_ptr(auto_ptr<T1>& a) : _ptr(a.release()) {}
+ auto_ptr& operator=(auto_ptr& a) {
+ if (&a != this) {
+ delete _ptr;
+ _ptr = a.release();
+ }
+ return *this;
+ }
+ template <class T1>
+ auto_ptr& operator=(auto_ptr<T1>& a) {
+ if (a.get() != this->get()) {
+ delete _ptr;
+ _ptr = a.release();
+ }
+ return *this;
+ }
+ ~auto_ptr() { delete _ptr; }
+
+ T& operator*() const { return *_ptr; }
+ T* operator->() const { return _ptr; }
+ T* get() const { return _ptr; }
+ T* release() { T* tmp = _ptr; _ptr = 0; return tmp; }
+ void reset(T* p = 0) { delete _ptr; _ptr = p; }
+};
+
+#endif
+#endif
+
+
+class KDSCBBOX
+{
+public:
+ KDSCBBOX();
+ KDSCBBOX( const KDSCBBOX& b );
+ KDSCBBOX( int llx, int lly, int urx, int ury );
+ KDSCBBOX( const CDSCBBOX& bbox );
+
+ KDSCBBOX& operator = ( const KDSCBBOX& b );
+
+ bool operator == ( const KDSCBBOX& b );
+ bool operator != ( const KDSCBBOX& b );
+
+ int llx() const;
+ int lly() const;
+ int urx() const;
+ int ury() const;
+
+ int width() const;
+ int height() const;
+
+ QSize size() const;
+
+private:
+ int _llx, _lly, _urx, _ury;
+};
+
+std::ostream& operator << ( std::ostream&, const KDSCBBOX& );
+
+
+class KDSCError
+{
+public:
+ enum Type
+ {
+ BBox = CDSC_MESSAGE_BBOX,
+ EarlyTrailer = CDSC_MESSAGE_EARLY_TRAILER,
+ EarlyEOF = CDSC_MESSAGE_EARLY_EOF,
+ PageInTrailer = CDSC_MESSAGE_PAGE_IN_TRAILER,
+ PageOrdinal = CDSC_MESSAGE_PAGE_ORDINAL,
+ PagesWrong = CDSC_MESSAGE_PAGES_WRONG,
+ EPSNoBBox = CDSC_MESSAGE_EPS_NO_BBOX,
+ EPSPages = CDSC_MESSAGE_EPS_PAGES,
+ NoMedia = CDSC_MESSAGE_NO_MEDIA,
+ AtEnd = CDSC_MESSAGE_ATEND,
+ DuplicateComment = CDSC_MESSAGE_DUP_COMMENT,
+ DuplicateTrailer = CDSC_MESSAGE_DUP_TRAILER,
+ BeginEnd = CDSC_MESSAGE_BEGIN_END,
+ BadSection = CDSC_MESSAGE_BAD_SECTION,
+ LongLine = CDSC_MESSAGE_LONG_LINE,
+ IncorrectUsage = CDSC_MESSAGE_INCORRECT_USAGE
+ };
+
+ enum Severity
+ {
+ Information = CDSC_ERROR_INFORM,
+ Warning = CDSC_ERROR_WARN,
+ Error = CDSC_ERROR_ERROR
+ };
+
+ KDSCError( Type, Severity, const QCString& line,
+ unsigned int lineNumber );
+
+ Type type() const;
+ Severity severity() const;
+ QCString line() const;
+ unsigned int lineNumber() const;
+
+private:
+ Type _type;
+ Severity _severity;
+ QCString _line;
+ unsigned int _lineNumber;
+};
+
+
+class KDSCErrorHandler
+{
+public:
+ virtual ~KDSCErrorHandler() {}
+ enum Response
+ {
+ Ok = CDSC_RESPONSE_OK,
+ Cancel = CDSC_RESPONSE_CANCEL,
+ IgnoreAll = CDSC_RESPONSE_IGNORE_ALL
+ };
+
+ virtual Response error( const KDSCError& ) = 0;
+};
+
+class KDSCOkErrorHandler : public KDSCErrorHandler
+{
+ Response error( const KDSCError& );
+};
+
+class KDSCCommentHandler
+{
+public:
+ virtual ~KDSCCommentHandler() {}
+ enum Name
+ {
+ // Header section
+ PSAdobe = CDSC_PSADOBE,
+ BeginComments = CDSC_BEGINCOMMENTS,
+ EndComments = CDSC_ENDCOMMENTS,
+ Pages = CDSC_PAGES,
+ Creator = CDSC_CREATOR,
+ CreationDate = CDSC_CREATIONDATE,
+ Title = CDSC_TITLE,
+ For = CDSC_FOR,
+ LanguageLevel = CDSC_LANGUAGELEVEL,
+ BoundingBox = CDSC_BOUNDINGBOX,
+ Orientation = CDSC_ORIENTATION,
+ PageOrder = CDSC_PAGEORDER,
+ DocumentMedia = CDSC_DOCUMENTMEDIA,
+ DocumentPaperSizes = CDSC_DOCUMENTPAPERSIZES,
+ DocumentPaperForms = CDSC_DOCUMENTPAPERFORMS,
+ DocumentPaperColors = CDSC_DOCUMENTPAPERCOLORS,
+ DocumentPaperWeights = CDSC_DOCUMENTPAPERWEIGHTS,
+ DocumentData = CDSC_DOCUMENTDATA,
+ Requirements = CDSC_REQUIREMENTS,
+ DocumentNeededFonts = CDSC_DOCUMENTNEEDEDFONTS,
+ DocumentSuppliedFonts = CDSC_DOCUMENTSUPPLIEDFONTS,
+ HiResBoundingBox = CDSC_HIRESBOUNDINGBOX,
+ CropBox = CDSC_CROPBOX,
+
+ // Preview section
+ BeginPreview = CDSC_BEGINPREVIEW,
+ EndPreview = CDSC_ENDPREVIEW,
+
+ // Defaults section
+ BeginDefaults = CDSC_BEGINDEFAULTS,
+ EndDefaults = CDSC_ENDDEFAULTS,
+ // also %%PageMedia, %%PageOrientation, %%PageBoundingBox
+
+ // Prolog section
+ BeginProlog = CDSC_BEGINPROLOG,
+ EndProlog = CDSC_ENDPROLOG,
+ BeginFont = CDSC_BEGINFONT,
+ EndFont = CDSC_ENDFONT,
+ BeginFeature = CDSC_BEGINFEATURE,
+ EndFeature = CDSC_ENDFEATURE,
+ BeginResource = CDSC_BEGINRESOURCE,
+ EndResource = CDSC_ENDRESOURCE,
+ BeginProcset = CDSC_BEGINPROCSET,
+ EndProcset = CDSC_ENDPROCSET,
+
+ // Setup section
+ BeginSetup = CDSC_BEGINSETUP,
+ EndSetup = CDSC_ENDSETUP,
+ Feature = CDSC_FEATURE,
+ PaperColor = CDSC_PAPERCOLOR,
+ PaperForm = CDSC_PAPERFORM,
+ PaperWeight = CDSC_PAPERWEIGHT,
+ PaperSize = CDSC_PAPERSIZE,
+ // also %%Begin/EndFeature, %%Begin/EndResource
+
+ // Page section
+ Page = CDSC_PAGE,
+ PageTrailer = CDSC_PAGETRAILER,
+ BeginPageSetup = CDSC_BEGINPAGESETUP,
+ EndPageSetup = CDSC_ENDPAGESETUP,
+ PageMedia = CDSC_PAGEMEDIA,
+ // also %%PaperColor, %%PaperForm, %%PaperWeight, %%PaperSize
+ PageOrientation = CDSC_PAGEORIENTATION,
+ PageBoundingBox = CDSC_PAGEBOUNDINGBOX,
+ // also %%Begin/EndFont, %%Begin/EndFeature
+ // also %%Begin/EndResource, %%Begin/EndProcSet
+ IncludeFont = CDSC_INCLUDEFONT,
+ ViewingOrientation = CDSC_VIEWINGORIENTATION,
+
+ // Trailer section
+ Trailer = CDSC_TRAILER,
+ // also %%Pages, %%BoundingBox, %%Orientation, %%PageOrder,
+ // %%DocumentMedia
+ // %%Page is recognised as an error
+ // also %%DocumentNeededFonts, %%DocumentSuppliedFonts
+
+ // End of File */
+ Eof = CDSC_EOF
+ };
+
+ virtual void comment( Name name ) { std::cout << name << std::endl; }
+};
+
+class KDSCScanHandler;
+class KDSC
+{
+public:
+ KDSC();
+ ~KDSC();
+
+ /*--- Adapter for CDSC ------------------------------------------------*/
+ QString dsc_version() const;
+
+ bool dsc() const;
+ bool ctrld() const;
+ bool pjl() const;
+ bool epsf() const;
+ bool pdf() const;
+
+ unsigned int preview() const;
+ unsigned int language_level() const;
+ unsigned int document_data() const;
+
+ unsigned long begincomments() const;
+ unsigned long endcomments() const;
+ unsigned long beginpreview() const;
+ unsigned long endpreview() const;
+ unsigned long begindefaults() const;
+ unsigned long enddefaults() const;
+ unsigned long beginprolog() const;
+ unsigned long endprolog() const;
+ unsigned long beginsetup() const;
+ unsigned long endsetup() const;
+ unsigned long begintrailer() const;
+ unsigned long endtrailer() const;
+
+ CDSCPAGE* page() const;
+
+ unsigned int page_count() const;
+ unsigned int page_pages() const;
+ unsigned int page_order() const;
+ unsigned int page_orientation() const;
+
+ CDSCCTM* viewing_orientation() const;
+
+ unsigned int media_count() const;
+ CDSCMEDIA** media() const;
+ const CDSCMEDIA* page_media() const;
+
+#if defined(__GNUC__) && (__GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 93))
+ auto_ptr<KDSCBBOX> bbox() const;
+ auto_ptr<KDSCBBOX> page_bbox() const;
+#else
+ std::auto_ptr<KDSCBBOX> bbox() const;
+ std::auto_ptr<KDSCBBOX> page_bbox() const;
+#endif
+
+ // CDSCDOSEPS *doseps;
+
+ QString dsc_title() const;
+ QString dsc_creator() const;
+ QString dsc_date() const;
+ QString dsc_for() const;
+
+ // unsigned int max_error
+
+ bool scanData( char*, unsigned int );
+
+ /**
+ * Tidy up from incorrect DSC comments.
+ */
+ int fixup();
+
+ KDSCErrorHandler* errorHandler() const;
+ void setErrorHandler( KDSCErrorHandler* );
+
+ KDSCCommentHandler* commentHandler() const;
+ void setCommentHandler( KDSCCommentHandler* );
+
+ /*--- Extra methods for convenience -----------------------------------*/
+ bool isStructured() const;
+
+ /*--- Temporary -------------------------------------------------------*/
+ CDSC* cdsc() const;
+
+protected:
+ static int errorFunction( void* caller_data, CDSC* dsc,
+ unsigned int explanation,
+ const char* line, unsigned int line_len );
+
+private:
+ CDSC* _cdsc;
+ KDSCErrorHandler* _errorHandler;
+ KDSCCommentHandler* _commentHandler;
+ KDSCScanHandler* _scanHandler;
+};
+
+class KDSCScanHandler
+{
+public:
+ virtual ~KDSCScanHandler() {}
+ KDSCScanHandler( CDSC* cdsc ) : _cdsc( cdsc ) {}
+
+ virtual bool scanData( char* buf, unsigned int count )
+ {
+ return ( dsc_scan_data( _cdsc, buf, count ) >= 0 );
+ }
+
+protected:
+ CDSC* _cdsc;
+};
+
+class KDSCScanHandlerByLine : public KDSCScanHandler
+{
+public:
+ KDSCScanHandlerByLine( CDSC* cdsc, KDSCCommentHandler* commentHandler ) :
+ KDSCScanHandler( cdsc ),
+ _commentHandler( commentHandler )
+ {}
+
+ virtual bool scanData( char* buf, unsigned int count );
+
+protected:
+ KDSCCommentHandler* _commentHandler;
+};
+
+#endif
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/fullscreenfilter.cpp b/kghostview/fullscreenfilter.cpp
new file mode 100644
index 00000000..ec208169
--- /dev/null
+++ b/kghostview/fullscreenfilter.cpp
@@ -0,0 +1,54 @@
+/**
+ * Copyright (C) 2003, Lus Pedro Coelho
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include "fullscreenfilter.h"
+
+#include "kgvshell.h"
+#include "kgv_view.h"
+#include "kgv_miniwidget.h"
+#include "kgvpageview.h"
+
+FullScreenFilter::FullScreenFilter( KGVShell& parent )
+ :QObject( &parent, "full-screen-filter" ),
+ parent( parent )
+{
+}
+
+bool FullScreenFilter::eventFilter( QObject* /*object*/, QEvent* ev) {
+ if ( QKeyEvent* keyevent = dynamic_cast<QKeyEvent*>( ev ) ) {
+ if ( keyevent->key() == Key_Escape ) {
+ parent.setFullScreen( false );
+ keyevent->accept();
+ return true;
+ }
+ }
+ if ( QMouseEvent* mouseevent = dynamic_cast<QMouseEvent*>( ev ) ) {
+ if ( mouseevent->stateAfter() & mouseevent->button() & LeftButton ) {
+ // if ( The whole image is visible at once )
+ if ( parent.m_gvpart->pageView()->contentsHeight() <= parent.m_gvpart->widget()->height() &&
+ parent.m_gvpart->pageView()->contentsWidth() <= parent.m_gvpart->widget()->width() ) {
+ parent.m_gvpart->miniWidget()->nextPage();
+ mouseevent->accept();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+#include "fullscreenfilter.moc"
+
diff --git a/kghostview/fullscreenfilter.h b/kghostview/fullscreenfilter.h
new file mode 100644
index 00000000..e60090a1
--- /dev/null
+++ b/kghostview/fullscreenfilter.h
@@ -0,0 +1,44 @@
+/**
+ * Copyright (C) 2003, Lus Pedro Coelho
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef DB_FULLSCREENFILTER_H1055977622_INCLUDE_GUARD_
+#define DB_FULLSCREENFILTER_H1055977622_INCLUDE_GUARD_
+
+#include <qobject.h>
+class KGVShell;
+
+/* @class FullScreenFilter
+ *
+ * @short This class is to have in one place the special event all the special
+ * key/mouse handling related to full-screen mode without bloating further KGVPart
+ */
+class FullScreenFilter : public QObject {
+ Q_OBJECT
+ public:
+ FullScreenFilter( KGVShell& parent );
+
+ /**
+ * @reimplemented
+ */
+ virtual bool eventFilter( QObject*, QEvent* );
+ private:
+ KGVShell& parent;
+};
+
+
+
+#endif /* DB_FULLSCREENFILTER_H1055977622_INCLUDE_GUARD_ */
diff --git a/kghostview/generalsettingswidget.ui b/kghostview/generalsettingswidget.ui
new file mode 100644
index 00000000..60ffbdf3
--- /dev/null
+++ b/kghostview/generalsettingswidget.ui
@@ -0,0 +1,111 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>GeneralSettingsWidget</class>
+<author>Nadeem Hasan &lt;nhasan@kde.org&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GeneralSettingsWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>397</width>
+ <height>143</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_Antialiasing</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Enable anti-aliasing of fonts and images</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Anti-aliasing makes the result look better, but it makes the display take longer</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_PlatformFonts</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Use platform fonts</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_Messages</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Show Ghostscript messages in a separate box</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Ghostscript is the basic renderer (the program which draws the picture)&lt;br&gt;
+In case of problems you might want to see its error messages</string>
+ </property>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>kcfg_Palette</cstring>
+ </property>
+ <property name="title">
+ <string>Palette</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>Mono</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Monochrome</string>
+ </property>
+ <property name="buttonGroupId">
+ <number>2</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>Gray</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Grayscale</string>
+ </property>
+ <property name="buttonGroupId">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>Color</cstring>
+ </property>
+ <property name="text">
+ <string>Co&amp;lor</string>
+ </property>
+ <property name="buttonGroupId">
+ <number>0</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<includes>
+ <include location="global" impldecl="in implementation">kdialog.h</include>
+ <include location="local" impldecl="in implementation">generalsettingswidget.ui.h</include>
+</includes>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+</UI>
diff --git a/kghostview/generalsettingswidget.ui.h b/kghostview/generalsettingswidget.ui.h
new file mode 100644
index 00000000..8dd6920d
--- /dev/null
+++ b/kghostview/generalsettingswidget.ui.h
@@ -0,0 +1,9 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
diff --git a/kghostview/gssettingswidget.ui b/kghostview/gssettingswidget.ui
new file mode 100644
index 00000000..06779f3b
--- /dev/null
+++ b/kghostview/gssettingswidget.ui
@@ -0,0 +1,158 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>GSSettingsWidget</class>
+<author>Nadeem Hasan &lt;nhasan@kde.org&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GSSettingsWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>395</width>
+ <height>243</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KPushButton" row="0" column="0">
+ <property name="name">
+ <cstring>mConfigureButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Auto Con&amp;figure</string>
+ </property>
+ </widget>
+ <spacer row="0" column="1">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>286</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Settings</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Interpreter:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_Interpreter</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>kcfg_Interpreter</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Ghostscript is the basic renderer (i.e. the program which draws)</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>mDetectedVersion</cstring>
+ </property>
+ <property name="text">
+ <string>(detected gs version: %1)</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Non-antialiasing arguments:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_NonAntialiasingArguments</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>kcfg_NonAntialiasingArguments</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Anti-aliasing makes the result look better, but it makes the display take longer</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>An&amp;tialiasing arguments:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_AntialiasingArguments</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>kcfg_AntialiasingArguments</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Anti-aliasing makes the result look better, but it makes the display take longer</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<includes>
+ <include location="global" impldecl="in implementation">kdialog.h</include>
+ <include location="local" impldecl="in implementation">gssettingswidget.ui.h</include>
+</includes>
+<signals>
+ <signal>configClicked()</signal>
+</signals>
+<slots>
+ <slot specifier="non virtual">setDetectedVersion( QString v )</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kghostview/gssettingswidget.ui.h b/kghostview/gssettingswidget.ui.h
new file mode 100644
index 00000000..87937c76
--- /dev/null
+++ b/kghostview/gssettingswidget.ui.h
@@ -0,0 +1,14 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+void GSSettingsWidget::setDetectedVersion( QString v)
+{
+ mDetectedVersion->setText(mDetectedVersion->text().arg( v ));
+}
+
diff --git a/kghostview/hi128-app-kghostview.png b/kghostview/hi128-app-kghostview.png
new file mode 100644
index 00000000..82048b94
--- /dev/null
+++ b/kghostview/hi128-app-kghostview.png
Binary files differ
diff --git a/kghostview/hi16-app-kghostview.png b/kghostview/hi16-app-kghostview.png
new file mode 100644
index 00000000..9603332f
--- /dev/null
+++ b/kghostview/hi16-app-kghostview.png
Binary files differ
diff --git a/kghostview/hi22-app-kghostview.png b/kghostview/hi22-app-kghostview.png
new file mode 100644
index 00000000..2109ade8
--- /dev/null
+++ b/kghostview/hi22-app-kghostview.png
Binary files differ
diff --git a/kghostview/hi32-app-kghostview.png b/kghostview/hi32-app-kghostview.png
new file mode 100644
index 00000000..4726452e
--- /dev/null
+++ b/kghostview/hi32-app-kghostview.png
Binary files differ
diff --git a/kghostview/hi48-app-kghostview.png b/kghostview/hi48-app-kghostview.png
new file mode 100644
index 00000000..0289cea7
--- /dev/null
+++ b/kghostview/hi48-app-kghostview.png
Binary files differ
diff --git a/kghostview/hi64-app-kghostview.png b/kghostview/hi64-app-kghostview.png
new file mode 100644
index 00000000..e8bab3f4
--- /dev/null
+++ b/kghostview/hi64-app-kghostview.png
Binary files differ
diff --git a/kghostview/infodialog.cpp b/kghostview/infodialog.cpp
new file mode 100644
index 00000000..b9cc8a91
--- /dev/null
+++ b/kghostview/infodialog.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2000 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <qframe.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qdatetime.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kstdguiitem.h>
+
+#include "infodialog.h"
+
+//
+// Using KDialogBase in message box mode (gives centered action button)
+//
+InfoDialog::InfoDialog( QWidget *parent, const char *name, bool modal )
+ :KDialogBase( i18n("Document Information"), Yes, Yes, Yes, parent,
+ name, modal, true, KStdGuiItem::ok() )
+{
+ QFrame *page = makeMainWidget();
+ QVBoxLayout *topLayout = new QVBoxLayout( page, 0, spacingHint() );
+ QGridLayout *glay = new QGridLayout( topLayout, 3, 2 );
+ glay->setColStretch(1,1);
+
+ QLabel *label = new QLabel( i18n("File name:" ), page );
+ glay->addWidget( label, 0, 0, AlignRight|AlignVCenter );
+ mFileLabel = new QLabel( page );
+ glay->addWidget( mFileLabel, 0, 1 );
+
+ label = new QLabel( i18n("Document title:" ), page );
+ glay->addWidget( label, 1, 0, AlignRight|AlignVCenter );
+ mTitleLabel = new QLabel( page );
+ glay->addWidget( mTitleLabel, 1, 1 );
+
+ label = new QLabel( i18n("Publication date:" ), page );
+ glay->addWidget( label, 2, 0, AlignRight|AlignVCenter );
+ mDateLabel = new QLabel( page );
+ glay->addWidget( mDateLabel, 2, 1 );
+
+ topLayout->addStretch(1);
+}
+
+namespace {
+ /* For PDF files, the dates are in a standard format.
+ *
+ * According to the spec at http://partners.adobe.com/asn/tech/pdf/specifications.jsp
+ * That format is "(D:YYYYMMDDHHmmSSOHH'mm')", where
+ * YYYY is year,
+ * MM month
+ * DD day
+ * HH hour
+ * mm minute
+ * SS second
+ * O is "+" or "-"
+ * HH is hour
+ * mm is minute
+ *
+ * OHH'mm' form together the desviation to UCT time ( the timezone ).
+ * Everything after the YYYY is optional.
+ * The D: is "highly recommended", but legally optional
+ *
+ * For PS files, there is no such standard and dates appear
+ * in any format they desire.
+ */
+ QString parseDate( const QString& dateStr ) {
+ kdDebug( 4500 ) << "parseDate( \"" << dateStr << "\" )" << endl;
+ QRegExp exp( "\\((?:D:)?"
+ "(\\d\\d\\d\\d)"
+ "(\\d\\d)?(\\d\\d)?(\\d\\d)?.*"
+ "(\\d\\d)?(\\d\\d)?.*"
+ "(?:(\\+|\\-)(\\d\\d)\'?(\\d\\d)\'?)?"
+ "\\)" );
+ if ( exp.exactMatch( dateStr ) ) {
+ QStringList list = exp.capturedTexts();
+ QStringList::iterator iter = list.begin();
+ ++iter; // whole string!
+#undef GET
+#define GET( variable, def ) \
+ unsigned variable = def; \
+ if ( iter != list.end() ) { \
+ variable = ( *iter ).toUInt();\
+ ++iter; \
+ }
+ GET( year, 1 )
+ GET( month, 1 )
+ GET( day, 1 )
+ GET( hour, 0 )
+ GET( min, 0 )
+ GET( sec, 0 )
+#undef GET
+ // FIXME: this ignores the timezone
+ QDate date( year, month, day );
+ QTime time( hour, min, sec );
+ KLocale locale( "kghostview" );
+ return locale.formatDateTime( QDateTime( date, time ) );
+ }
+ kdDebug( 4500 ) << "parseDate failed." << endl;
+ return dateStr;
+ }
+}
+
+void InfoDialog::setup( const QString &fileName, const QString &documentTitle,
+ const QString &publicationDate )
+{
+ mFileLabel->setText( fileName );
+ mTitleLabel->setText( documentTitle );
+ mDateLabel->setText( parseDate( publicationDate ) );
+}
+
+#include "infodialog.moc"
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/infodialog.h b/kghostview/infodialog.h
new file mode 100644
index 00000000..1c857fc1
--- /dev/null
+++ b/kghostview/infodialog.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2000 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _INFO_DIALOG_H_
+#define _INFO_DIALOG_H_
+
+class QLabel;
+
+#include <kdialogbase.h>
+
+class InfoDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ InfoDialog( QWidget *parent=0, const char *name=0, bool modal=true );
+ void setup( const QString &fileName, const QString &documentTitle,
+ const QString &publicationDate );
+
+ private:
+ QLabel *mFileLabel;
+ QLabel *mTitleLabel;
+ QLabel *mDateLabel;
+};
+
+#endif
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/kdscerrordialog.cpp b/kghostview/kdscerrordialog.cpp
new file mode 100644
index 00000000..601f5fc5
--- /dev/null
+++ b/kghostview/kdscerrordialog.cpp
@@ -0,0 +1,170 @@
+/**
+ * Copyright (C) 2001 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtextedit.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kseparator.h>
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+
+#include "kdscerrordialog.h"
+#include "kdscerrordialog.moc"
+
+KDSCErrorThreshold::KDSCErrorThreshold( int threshold,
+ KDSCErrorHandler* errorHandler ) :
+ _threshold( threshold ),
+ _errorHandler( errorHandler )
+{}
+
+KDSCErrorHandler::Response KDSCErrorThreshold::error( const KDSCError& err )
+{
+ if( _errorHandler != 0 && err.severity() >= _threshold )
+ return _errorHandler->error( err );
+ else
+ // Cancel is the default handling strategy for dsc_error_fn, so
+ // we keep it. This cancels error handling and *not* document
+ // parsing!
+ return Cancel;
+}
+
+KDSCErrorDialog::KDSCErrorDialog( QWidget* parent ) :
+ KDialog( parent, "dscerrordialog", true ),
+ _response( Ok )
+{
+ QVBoxLayout* vbox = new QVBoxLayout( this, marginHint(), spacingHint() );
+
+ _lineNumberLabel = new QLabel( this );
+ vbox->addWidget( _lineNumberLabel );
+
+ _lineLabel = new QTextEdit( this );
+ _lineLabel->setReadOnly( true );
+ vbox->addWidget( _lineLabel );
+
+ _descriptionLabel = new QLabel( this );
+ vbox->addWidget( _descriptionLabel );
+
+ KSeparator* sep = new KSeparator( KSeparator::HLine, this );
+ vbox->addWidget( sep );
+
+ QHBoxLayout* hbox = new QHBoxLayout( vbox );
+
+ hbox->addStretch();
+
+ _okButton = new KPushButton( KStdGuiItem::ok(), this );
+ hbox->addWidget( _okButton );
+ _cancelButton = new KPushButton( KStdGuiItem::cancel(), this );
+ hbox->addWidget( _cancelButton );
+ _ignoreAllButton = new QPushButton( i18n("Ignore All"), this );
+ hbox->addWidget( _ignoreAllButton );
+
+ connect( _okButton, SIGNAL( clicked() ), this, SLOT( slotOk() ) );
+ connect( _cancelButton, SIGNAL( clicked() ), this, SLOT( slotCancel() ) );
+ connect( _ignoreAllButton, SIGNAL( clicked() ),
+ this, SLOT( slotIgnoreAll() ) );
+}
+
+KDSCErrorHandler::Response KDSCErrorDialog::error( const KDSCError& err )
+{
+ switch( err.severity() )
+ {
+ case KDSCError::Information:
+ setCaption( i18n( "DSC Information" ) );
+ break;
+ case KDSCError::Warning:
+ setCaption( i18n( "DSC Warning" ) );
+ break;
+ case KDSCError::Error:
+ setCaption( i18n( "DSC Error" ) );
+ break;
+ }
+
+ _lineNumberLabel->setText( i18n( "On line %1:" ).arg( err.lineNumber() ) );
+ _lineLabel->setText( err.line() );
+ _descriptionLabel->setText( description( err.type() ) );
+
+ exec();
+
+ kdDebug(4500) << "KDSCErrorDialog: returning " << _response << endl;
+
+ return _response;
+}
+
+QString KDSCErrorDialog::description( KDSCError::Type type ) const
+{
+ switch( type )
+ {
+ case KDSCError::BBox:
+ return "TODO";
+ case KDSCError::EarlyTrailer:
+ return "TODO";
+ case KDSCError::EarlyEOF:
+ return "TODO";
+ case KDSCError::PageInTrailer:
+ return "TODO";
+ case KDSCError::PageOrdinal:
+ return "TODO";
+ case KDSCError::PagesWrong:
+ return "TODO";
+ case KDSCError::EPSNoBBox:
+ return "TODO";
+ case KDSCError::EPSPages:
+ return "TODO";
+ case KDSCError::NoMedia:
+ return "TODO";
+ case KDSCError::AtEnd:
+ return "TODO";
+ case KDSCError::DuplicateComment:
+ return "TODO";
+ case KDSCError::DuplicateTrailer:
+ return "TODO";
+ case KDSCError::BeginEnd:
+ return "TODO";
+ case KDSCError::BadSection:
+ return "TODO";
+ case KDSCError::LongLine:
+ return i18n( "Lines in DSC documents must be shorter than 255 "
+ "characters." );
+ case KDSCError::IncorrectUsage:
+ return "TODO";
+ default: return "TODO";
+ }
+}
+
+void KDSCErrorDialog::slotOk()
+{
+ _response = Ok;
+ accept();
+}
+
+void KDSCErrorDialog::slotCancel()
+{
+ _response = Cancel;
+ accept();
+}
+
+void KDSCErrorDialog::slotIgnoreAll()
+{
+ _response = IgnoreAll;
+ accept();
+}
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/kdscerrordialog.h b/kghostview/kdscerrordialog.h
new file mode 100644
index 00000000..0aee24d2
--- /dev/null
+++ b/kghostview/kdscerrordialog.h
@@ -0,0 +1,74 @@
+/**
+ * Copyright (C) 2001 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KDSCERRORDIALOG_H
+#define KDSCERRORDIALOG_H
+
+#include <kdialog.h>
+
+#include "dscparse_adapter.h"
+
+class QLabel;
+class QPushButton;
+class QTextEdit;
+
+class KDSCErrorThreshold : public KDSCErrorHandler
+{
+public:
+ KDSCErrorThreshold( int threshold, KDSCErrorHandler* );
+
+ Response error( const KDSCError& );
+
+private:
+ int _threshold;
+ KDSCErrorHandler* _errorHandler;
+};
+
+class KDSCErrorDialog : public KDialog, public KDSCErrorHandler
+{
+ Q_OBJECT
+
+public:
+ KDSCErrorDialog( QWidget* parent = 0 );
+
+ Response error( const KDSCError& );
+
+protected:
+ QString description( KDSCError::Type ) const;
+
+protected slots:
+ void slotOk();
+ void slotCancel();
+ void slotIgnoreAll();
+
+private:
+ QLabel* _lineNumberLabel;
+ QTextEdit* _lineLabel;
+ QLabel* _descriptionLabel;
+
+ QPushButton* _okButton;
+ QPushButton* _cancelButton;
+ QPushButton* _ignoreAllButton;
+
+ Response _response;
+};
+
+#endif
+
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/kghostview.desktop b/kghostview/kghostview.desktop
new file mode 100644
index 00000000..66d3ee3f
--- /dev/null
+++ b/kghostview/kghostview.desktop
@@ -0,0 +1,86 @@
+[Desktop Entry]
+Name=KGhostView
+Name[af]=Kghostview
+Name[ar]=برنامج KGhostView
+Name[eo]=Postskriptrigardilo
+Name[hi]=के-घोस्ट-व्यू
+Name[hu]=KGhostview
+Name[ne]=केडीई घोस्ट दृश्य
+Name[sv]=Kghostview
+Name[ta]=கேமற்றொரு காட்சி
+Name[tr]=GhostView
+Name[ven]=Mbonalelo ya tshipuku tsha K
+Name[xh]=Imboniselo yeKGhost
+MimeType=application/pdf;application/postscript;image/x-eps;application/x-gzpostscript;application/illustrator;
+InitialPreference=6
+Exec=kghostview %u -caption "%c" %i %m
+Icon=kghostview
+Type=Application
+DocPath=kghostview/index.html
+GenericName=PS/PDF Viewer
+GenericName[af]=Ps/Pdf Aansig
+GenericName[ar]=عارض PS/PDF
+GenericName[bg]=Преглед на документи PS/PDF
+GenericName[br]=Gweler PS/PDF
+GenericName[bs]=Preglednik PS/PDF dokumenata
+GenericName[ca]=Visualitzador de PS/PDF
+GenericName[cs]=Prohlížeč PS/PDF souborů
+GenericName[cy]=Gwelydd PS/PDF
+GenericName[da]=PS/PDF-fremviser
+GenericName[de]=PS/PDF-Betrachter
+GenericName[el]=Προβολέας PS/PDF
+GenericName[eo]=PS/PDF-rigardilo
+GenericName[es]=Visor de documentos PS/PDF
+GenericName[et]=PS/PDF-failide näitaja
+GenericName[eu]=PS/PDF ikustailua
+GenericName[fa]=مشاهده‌گر PS/PDF
+GenericName[fi]=PS/PDF-näytin
+GenericName[fr]=Afficheur PostScript et PDF
+GenericName[ga]=Amharcán PS/PDF
+GenericName[gl]=Visor PS/PDF
+GenericName[he]=מציג PS/PDF
+GenericName[hi]=PS/PDF प्रदर्शक
+GenericName[hr]=Preglednik PS/PDF dokumenata
+GenericName[hu]=PS/PDF-megjelenítő
+GenericName[is]=PS/PDF sjá
+GenericName[it]=Visore PS/PDF
+GenericName[ja]=PS/PDF ビューア
+GenericName[kk]=PS/PDF файлдарын қарау
+GenericName[km]=កម្មវិធី​មើល PS/PDF
+GenericName[lt]=PS/PDF žiūriklis
+GenericName[lv]=PS/PDF Skatītājs
+GenericName[ms]=Pemapar PS/PDF
+GenericName[nb]=PS-/PDF-fremviser
+GenericName[nds]=PostScript-/PDF-Kieker
+GenericName[ne]=PS/PDF दर्शक
+GenericName[nl]=PostScript/PDF-weergaveprogramma
+GenericName[nn]=PS/PDF-lesar
+GenericName[nso]=Molebeledi wa PS/PDF
+GenericName[pa]=PS/PDF ਦਰਸ਼ਕ
+GenericName[pl]=Przeglądarka plików PS/PDF
+GenericName[pt]=Visualizador de PS/PDF
+GenericName[pt_BR]=Visualizador PDF/PS
+GenericName[ro]=Vizualizor PS/PDF
+GenericName[ru]=Просмотр Postscript и PDF
+GenericName[se]=PS/PDF-čájeheaddji
+GenericName[sk]=Prehliadač PS/PDF
+GenericName[sl]=Pregledovalnik datotek PS/PDF
+GenericName[sr]=PS/PDF приказивач
+GenericName[sr@Latn]=PS/PDF prikazivač
+GenericName[sv]=PS/PDF-visare
+GenericName[ta]=PS/PDF காட்சி
+GenericName[tg]=Хондани PS/PDF
+GenericName[th]=เครื่องมือแสดงแฟ้มโพสต์สคริปต์ PS/PDF
+GenericName[tr]=PS/PDF Görüntüleyici
+GenericName[uk]=Переглядач PDF/PS
+GenericName[uz]=PS/PDF koʻruvchi
+GenericName[uz@cyrillic]=PS/PDF кўрувчи
+GenericName[ven]=Muvhoni wa PS/PDF
+GenericName[wa]=Håyneu di documints PS/PDF
+GenericName[xh]=Umboniseli we PS/PDF
+GenericName[zh_CN]=PS/PDF 查看器
+GenericName[zh_HK]=PS/PDF 檢視器
+GenericName[zh_TW]=PS/PDF 檢視器
+GenericName[zu]=Umboniseli we PS/PDF
+Terminal=false
+Categories=Qt;KDE;Graphics;
diff --git a/kghostview/kghostview.kcfg b/kghostview/kghostview.kcfg
new file mode 100644
index 00000000..362191f3
--- /dev/null
+++ b/kghostview/kghostview.kcfg
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <kcfgfile name="kghostviewrc"/>
+ <group name="General">
+ <entry name="Antialiasing" type="Bool">
+ <label>Whether to use anti-aliasing.</label>
+ <whatsthis>Anti-aliasing makes the result look better, specially regarding text, but it makes the display take longer</whatsthis>
+ </entry>
+ <entry name="Palette" type="Enum">
+ <choices>
+ <choice name="Color" />
+ <choice name="Grayscale" />
+ <choice name="Monochrome" />
+ </choices>
+ </entry>
+ <entry name="Messages" type="Bool">
+ <label>Whether to see a window with Ghostscript messages</label>
+ <whatsthis>Whether to see a window with Ghostscript messages. This can give you additional information on the files you see. In case of an error, a window will popup regardless of this option.</whatsthis>
+ <default>false</default>
+ </entry>
+ <entry name="PlatformFonts" type="Bool">
+ <label>Use Platform Fonts</label>
+ <whatsthis></whatsthis>
+ <default>true</default>
+ </entry>
+ <entry name="ShowPageList" type="Bool">
+ <label>Whether to show the page list</label>
+ <whatsthis></whatsthis>
+ <default>true</default>
+ </entry>
+ <entry name="ShowPageNames" type="Bool">
+ <label>Whether to show page names instead of numbers</label>
+ <whatsthis>Sometimes information is available on page names which can be used in the list panner instead of just the numbers. Most often, these names are, in fact, another numbering. Often, the first few pages use roman numbering (i, ii, iii, iv ...) followed by arabic numbers from one (1, 2, 3...) when the real content starts.</whatsthis>
+ <default>true</default>
+ </entry>
+ <entry name="ShowScrollBars" type="Bool">
+ <label>Whether to show scroll bars when pages are too big</label>
+ <whatsthis></whatsthis>
+ <default>true</default>
+ </entry>
+ <entry name="WatchFile" type="Bool">
+ <label>Watch File</label>
+ <whatsthis>If this is on, then the file will be reloaded whenever it changes on disk</whatsthis>
+ <default>false</default>
+ </entry>
+ </group>
+ <group name="Ghostscript">
+ <entry name="Interpreter" type="String">
+ <label>The ghostscript interpreter to use</label>
+ <whatsthis>Kghostview does not, itself, display the document: it relies on ghostscript, and therefore needs it to be available. Here you can define the ghostscript interpreter to use.</whatsthis>
+ <default>gs</default>
+ </entry>
+ <entry name="AntialiasingArguments" type="String">
+ <label>Arguments for ghostscript if running with antialiasing</label>
+ <whatsthis></whatsthis>
+ <default>-sDEVICE=x11 -dTextAlphaBits=4 -dGraphicsAlphaBits=2 -dMaxBitmap=10000000</default>
+ </entry>
+ <entry name="NonAntialiasingArguments" type="String">
+ <label></label>
+ <whatsthis></whatsthis>
+ <default>-sDEVICE=x11</default>
+ </entry>
+ <entry name="Version" type="String">
+ <label>This is the ghostscript version you are running</label>
+ <whatsthis>This is the version of ghostscript you are running. Normally you will not need to change this since it gets detected automatically.</whatsthis>
+ </entry>
+ <entry name="RedetectionCounter" type="UInt">
+ <label>This is an internal setting</label>
+ <whatsthis></whatsthis>
+ </entry>
+ </group>
+</kcfg>
+<!-- vim:set ts=4 -->
+
diff --git a/kghostview/kghostview.upd b/kghostview/kghostview.upd
new file mode 100644
index 00000000..a61fffaf
--- /dev/null
+++ b/kghostview/kghostview.upd
@@ -0,0 +1,13 @@
+Id=1changeToKConfigXT
+File=kghostviewrc
+Group=General
+Key=Platform Fonts,PlatformFonts
+Group=Ghostscript
+Key=Antialiasing arguments,AntialiasingArguments
+Key=Non-antialiasing arguments,NonAntialiasingArguments
+Key=Redetection Counter,RedetectionCounter
+#
+Id=2changeToKConfigXT
+File=kghostviewrc
+Group=General
+Script=update-to-xt-names.pl,perl
diff --git a/kghostview/kghostview_part.desktop b/kghostview/kghostview_part.desktop
new file mode 100644
index 00000000..a468fc02
--- /dev/null
+++ b/kghostview/kghostview_part.desktop
@@ -0,0 +1,19 @@
+[Desktop Entry]
+Name=KGhostView
+Name[af]=Kghostview
+Name[ar]=برنامج KGhostView
+Name[eo]=Postskriptrigardilo
+Name[hi]=के-घोस्ट-व्यू
+Name[hu]=KGhostview
+Name[ne]=केडीई घोस्ट दृश्य
+Name[sv]=Kghostview
+Name[ta]=கேமற்றொரு காட்சி
+Name[tr]=GhostView
+Name[ven]=Mbonalelo ya tshipuku tsha K
+Name[xh]=Imboniselo yeKGhost
+MimeType=application/pdf;application/postscript;image/x-eps;application/x-gzpostscript;application/illustrator
+InitialPreference=6
+Icon=kghostview
+ServiceTypes=KParts/ReadOnlyPart,Browser/View
+X-KDE-Library=libkghostviewpart
+Type=Service
diff --git a/kghostview/kghostviewui.rc b/kghostview/kghostviewui.rc
new file mode 100644
index 00000000..3903a725
--- /dev/null
+++ b/kghostview/kghostviewui.rc
@@ -0,0 +1,43 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="KGhostViewShell" version="11">
+<State name="initState">
+ <Disable>
+ <Action name="file_print" />
+ <Action name="reload" />
+ <Action name="maximize"/>
+ <Action name="fullscreen"/>
+ <Action name="showmenubar"/>
+ </Disable>
+</State>
+<State name="documentState">
+ <Enable>
+ <Action name="file_print" />
+ <Action name="reload" />
+ <Action name="fullscreen"/>
+ <Action name="showmenubar"/>
+ <Action name="maximize"/>
+ </Enable>
+</State>
+<MenuBar>
+ <Menu name="file">
+ <DefineGroup name="file_save" append="save_merge" />
+ <DefineGroup name="file_print" append="print_merge" />
+ </Menu>
+ <Menu name="edit">
+ <MergeLocal/>
+ </Menu>
+ <Menu name="view">
+ <Action name="reload" />
+ <Action name="maximize"/>
+ <Action name="fullscreen"/>
+ </Menu>
+ <Menu name="settings">
+ <Action name="option_show_toolbar" />
+ </Menu>
+ <Merge/>
+</MenuBar>
+<ToolBar name="mainToolBar">
+ <text>&amp;Main Toolbar</text>
+</ToolBar>
+</kpartgui>
+
diff --git a/kghostview/kgv.h b/kghostview/kgv.h
new file mode 100644
index 00000000..db7a1679
--- /dev/null
+++ b/kghostview/kgv.h
@@ -0,0 +1,15 @@
+#ifndef KGV_H
+#define KGV_H
+
+#include <qvaluelist.h>
+
+namespace KGV
+{
+
+typedef QValueList<int> PageList;
+
+}
+
+#endif
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/kgv_miniwidget.cpp b/kghostview/kgv_miniwidget.cpp
new file mode 100644
index 00000000..33045b17
--- /dev/null
+++ b/kghostview/kgv_miniwidget.cpp
@@ -0,0 +1,573 @@
+/**
+ * Copyright (C) 1997-2003 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <math.h>
+
+#include <qlistbox.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kinstance.h>
+#include <kdeversion.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+// KLineEditDlg is depricated as of 3.2. use KInputDialog instead
+#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,90)
+ #include <kinputdialog.h>
+#else
+ #include <qvalidator.h>
+ #include <qwidget.h>
+ #include <klineeditdlg.h>
+#endif
+
+#include <config.h>
+#include <stdlib.h>
+
+#include "infodialog.h"
+#include "marklist.h"
+#include "kgvdocument.h"
+#include "kgv_view.h"
+#include "version.h"
+#include "scrollbox.h"
+
+#include "kgv_miniwidget.h"
+
+#include "kpswidget.h"
+
+using namespace KGV;
+
+KGVMiniWidget::KGVMiniWidget( KGVPart* part, const char* name ) :
+ QObject( part, name ),
+ _document( 0 ),
+ _part( part ),
+ _psWidget( 0 ),
+ _usePageLabels( true ),
+ _visiblePage( -1 )
+{
+ KLocale locale( "kghostview" );
+ _fallBackPageMedia = pageSizeToString(
+ static_cast< QPrinter::PageSize >( locale.pageSize() ) );
+ _thumbnailService = new ThumbnailService( this );
+
+ connect( this, SIGNAL( newPageShown( int ) ),
+ SLOT( updateStatusBarText( int ) ) );
+}
+
+void KGVMiniWidget::setDocument( KGVDocument* document )
+{
+ _document = document;
+ if( _document )
+ connect( _document, SIGNAL( completed() ),
+ SLOT( slotDocumentOpened() ) );
+}
+
+QString KGVMiniWidget::pageSizeToString( QPrinter::PageSize pageSize )
+{
+ switch( pageSize )
+ {
+ case QPrinter::A3: return "A3";
+ case QPrinter::A4: return "A4";
+ case QPrinter::A5: return "A5";
+ case QPrinter::B4: return "B4";
+ case QPrinter::Ledger: return "Ledger";
+ case QPrinter::Legal: return "Legal";
+ case QPrinter::Letter: return "Letter";
+ default: return "Unknown";
+ }
+}
+
+void KGVMiniWidget::reset()
+{
+ /*
+ if( _psWidget )
+ _psWidget->disableInterpreter();
+ */
+
+ // return to document defaults
+ _options.reset();
+ emit setStatusBarText( "" );
+}
+
+void KGVMiniWidget::setPSWidget( KPSWidget* psWidget )
+{
+ _psWidget = psWidget;
+ // setMagnification( _magnification );
+ connect( _psWidget, SIGNAL( newPageImage( QPixmap ) ),
+ this, SLOT( sendPage() ) );
+}
+
+void KGVMiniWidget::goToPage()
+{
+#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,90)
+ int num;
+ bool ok = false;
+ num = KInputDialog::getInteger(i18n("Go to Page"), i18n("Page:"), 1,
+ 1, dsc()->page_count(), 1, 10, &ok, _part->widget());
+ if (ok) goToPage( num-1 );
+#else
+ QString num;
+ bool b = false;
+ num = KLineEditDlg::getText(i18n("Go to Page"), i18n("Page:"), QString::null, &b, _part->widget(), new QIntValidator(1, dsc()->page_count(), this));
+ if (b) goToPage( num.toInt() - 1 );
+#endif
+}
+
+void KGVMiniWidget::info()
+{
+ if( !document()->isOpen() )
+ return;
+
+ InfoDialog* infoDialog = new InfoDialog( _part->widget(), "info", true );
+ infoDialog->setup( _part->url().prettyURL(),
+ dsc()->dsc_title(), dsc()->dsc_date() );
+ infoDialog->exec();
+ delete infoDialog;
+}
+
+void KGVMiniWidget::goToPage( int page )
+{
+ if( _options.page() != page ) {
+ _options.setPage( page );
+ showPage( _options.page() );
+ }
+}
+
+void KGVMiniWidget::zoomIn()
+{
+ if ( _options.zoomIn() ) showPage( _options.page() );
+}
+
+void KGVMiniWidget::zoomOut()
+{
+ if ( _options.zoomOut() ) showPage( _options.page() );
+}
+
+bool KGVMiniWidget::atMaxZoom() const
+{
+ return !_options.canZoomIn();
+}
+
+bool KGVMiniWidget::atMinZoom() const
+{
+ return !_options.canZoomOut();
+}
+
+void KGVMiniWidget::fitWidth( unsigned int width )
+{
+ if ( (orientation() == CDSC_LANDSCAPE) || (orientation() == CDSC_SEASCAPE) )
+ setMagnification( ( (double)width / QPaintDevice::x11AppDpiY()) /
+ ( (double)boundingBox().height() / 72) );
+ else // default
+ setMagnification( ( (double)width / QPaintDevice::x11AppDpiX() ) /
+ ( (double)boundingBox().width() / 72) );
+}
+
+void KGVMiniWidget::fitHeight( unsigned int height )
+{
+ if ( (orientation() == CDSC_LANDSCAPE) || (orientation() == CDSC_SEASCAPE) )
+ setMagnification( ( (double)height / QPaintDevice::x11AppDpiY()) /
+ ( (double)boundingBox().width() / 72) );
+ else //default
+ setMagnification( ( (double)height / QPaintDevice::x11AppDpiY()) /
+ ( (double)boundingBox().height() / 72) );
+}
+
+void KGVMiniWidget::fitWidthHeight( unsigned int w, unsigned int h )
+{
+ double magnification = std::min<double>(
+ ( ( double )h / QPaintDevice::x11AppDpiY() ) /
+ ( ( double )boundingBox().height() / 72.0 ),
+ ( ( double )w / QPaintDevice::x11AppDpiX() ) /
+ ( ( double )boundingBox().width() / 72.0 ) );
+ setMagnification( magnification );
+}
+
+void KGVMiniWidget::firstPage()
+{
+ goToPage( 0 );
+}
+
+void KGVMiniWidget::lastPage()
+{
+ if ( !dsc() ) return;
+
+ goToPage( dsc()->page_count() - 1 );
+}
+
+bool KGVMiniWidget::prevPage()
+{
+ if ( !dsc() ) return false;
+
+ int new_page = 0;
+
+ if( dsc()->isStructured() ) {
+ new_page = _options.page() - 1;
+ if( new_page < 0 )
+ return false;
+ }
+
+ goToPage( new_page );
+ return true;
+}
+
+bool KGVMiniWidget::nextPage()
+{
+ if ( !dsc() ) return false;
+
+ int new_page = 0;
+
+ if( dsc()->isStructured() ) {
+ new_page = _options.page() + 1;
+ if( (unsigned int)new_page >= dsc()->page_count() )
+ return false;
+ }
+
+ goToPage( new_page );
+ return true;
+}
+
+
+void KGVMiniWidget::redisplay ()
+{
+ if( !document()->psFile() )
+ return;
+
+ _psWidget->stopInterpreter();
+ showPage( _options.page() );
+}
+
+void KGVMiniWidget::restoreOverrideOrientation()
+{
+ _options.restoreOverrideOrientation();
+ showPage( _options.page() );
+}
+
+void KGVMiniWidget::setOverrideOrientation( CDSC_ORIENTATION_ENUM orientation )
+{
+ _options.setOverrideOrientation( orientation );
+ showPage( _options.page() );
+}
+
+CDSC_ORIENTATION_ENUM KGVMiniWidget::orientation() const
+{
+ if( _options.overrideOrientation() != CDSC_ORIENT_UNKNOWN )
+ return _options.overrideOrientation();
+ else if( dsc()->page_orientation() != CDSC_ORIENT_UNKNOWN )
+ return static_cast< CDSC_ORIENTATION_ENUM >( dsc()->page_orientation());
+ else if( dsc()->bbox().get() != 0
+ && dsc()->bbox()->width() > dsc()->bbox()->height() )
+ return CDSC_LANDSCAPE;
+ else
+ return CDSC_PORTRAIT;
+}
+
+CDSC_ORIENTATION_ENUM KGVMiniWidget::orientation( int pagenumber ) const
+{
+ if ( !dsc() || unsigned( pagenumber ) >= dsc()->page_count() ) {
+ return orientation();
+ }
+ if( _options.overrideOrientation() != CDSC_ORIENT_UNKNOWN ) {
+ return _options.overrideOrientation();
+ }
+
+ if( dsc()->page()[ pagenumber ].orientation != CDSC_ORIENT_UNKNOWN ) {
+ return static_cast< CDSC_ORIENTATION_ENUM >( dsc()->page()[ pagenumber ].orientation );
+ }
+ if( dsc()->page_orientation() != CDSC_ORIENT_UNKNOWN ) {
+ return static_cast< CDSC_ORIENTATION_ENUM >( dsc()->page_orientation());
+ }
+ if( !dsc()->epsf() ) {
+ return CDSC_PORTRAIT;
+ }
+ if( dsc()->bbox().get() != 0
+ && dsc()->bbox()->width() > dsc()->bbox()->height() ) {
+ return CDSC_LANDSCAPE;
+ }
+ return CDSC_PORTRAIT;
+}
+
+void KGVMiniWidget::restoreOverridePageMedia()
+{
+ _options.restoreOverridePageMedia();
+ redisplay();
+ showPage( _options.page() );
+}
+
+void KGVMiniWidget::setOverridePageMedia( const QString& mediaName )
+{
+ _options.setOverridePageMedia( mediaName );
+ showPage( _options.page() );
+}
+
+QString KGVMiniWidget::pageMedia() const
+{
+ if( !_options.overridePageMedia().isNull() )
+ return _options.overridePageMedia();
+ else if( dsc()->page_media() != 0 )
+ return QString( dsc()->page_media()->name );
+ else if( dsc()->bbox().get() != 0 )
+ return QString( "BoundingBox" );
+ else
+ return _fallBackPageMedia;
+}
+
+QString KGVMiniWidget::pageMedia( int pagenumber ) const
+{
+ kdDebug( 4500 ) << "KGVMiniWidget::pageMedia( " << pagenumber << " )" << endl;
+ if ( !dsc() ) return pageMedia();
+ if ( unsigned( pagenumber ) >= dsc()->page_count() ) return pageMedia();
+ if( !_options.overridePageMedia().isNull() )
+ return _options.overridePageMedia();
+ else if( dsc()->page()[ pagenumber ].media != 0 )
+ return QString( dsc()->page()[ pagenumber ].media->name );
+ else if( dsc()->page_media() != 0 )
+ return QString( dsc()->page_media()->name );
+ else if( dsc()->bbox().get() != 0 )
+ return QString( "BoundingBox" );
+ else
+ return _fallBackPageMedia;
+}
+
+KDSCBBOX KGVMiniWidget::boundingBox() const
+{
+ QString currentMedia = pageMedia();
+ if( currentMedia == "BoundingBox" )
+ return KDSCBBOX( *dsc()->bbox().get() );
+ else {
+ QSize size = document()->computePageSize( currentMedia );
+ return KDSCBBOX( 0, 0, size.width(), size.height() );
+ }
+}
+
+KDSCBBOX KGVMiniWidget::boundingBox( int pageNo ) const
+{
+ QString currentMedia = pageMedia( pageNo );
+ if( currentMedia == "BoundingBox" )
+ return KDSCBBOX( *dsc()->bbox().get() );
+ else {
+ QSize size = document()->computePageSize( currentMedia );
+ return KDSCBBOX( 0, 0, size.width(), size.height() );
+ }
+}
+
+bool KGVMiniWidget::atFirstPage() const
+{
+ return ( _options.page() == 0 );
+}
+
+bool KGVMiniWidget::atLastPage() const
+{
+ return ( _options.page() == static_cast<int>( dsc()->page_count() ) - 1 );
+}
+
+void KGVMiniWidget::showPage( int pagenumber )
+{
+ if( !document()->isOpen() )
+ return;
+
+ kdDebug(4500) << "KGVMiniWidget::showPage( " << pagenumber << " )" << endl;
+
+ static_cast< QWidget* >( _psWidget->parent() )->show();
+
+ _psWidget->setFileName(_document->fileName(), dsc()->isStructured() );
+ _psWidget->clear();
+
+ if( dsc()->isStructured() )
+ {
+ // Coerce page number to fall in range
+ if( ( unsigned int)pagenumber >= dsc()->page_count() )
+ pagenumber = dsc()->page_count() - 1;
+ if( pagenumber < 0 )
+ pagenumber = 0;
+
+ _options.setPage( pagenumber );
+
+
+ _psWidget->setOrientation( orientation( _options.page() ) );
+ _psWidget->setBoundingBox( boundingBox( _options.page() ) );
+ _psWidget->setMagnification( _options.magnification() );
+
+ if( !_psWidget->isInterpreterRunning() )
+ {
+ // Start interpreter, send preamble and send the current page.
+ if( _psWidget->startInterpreter() )
+ {
+ _psWidget->sendPS( psFile(), dsc()->beginprolog(),
+ dsc()->endprolog() );
+ _psWidget->sendPS( psFile(), dsc()->beginsetup(),
+ dsc()->endsetup() );
+ _psWidget->sendPS( psFile(), dsc()->page()[ _options.page() ].begin,
+ dsc()->page()[ _options.page() ].end );
+ _visiblePage = _options.page();
+ }
+ }
+ else
+ sendPage();
+ }
+ else
+ {
+ _psWidget->setOrientation( orientation() );
+ _psWidget->setBoundingBox( boundingBox() );
+ _psWidget->setMagnification( _options.magnification() );
+
+ if( !_psWidget->isInterpreterRunning() )
+ {
+ // This is not a structured document -- start interpreter
+ _psWidget->startInterpreter();
+ if( !dsc() )
+ _psWidget->stopInterpreter();
+ }
+ else if( _psWidget->isInterpreterReady() )
+ _psWidget->nextPage();
+ else
+ {
+ /*
+ KNotifyClient::userEvent
+ (i18n("KGhostview cannot load the document, \"%1\".\n"
+ "It appears to be broken.").arg( _fileName ),
+ KNotifyClient::Messagebox);
+ _psWidget->disableInterpreter();
+ _psFile=0;
+
+ //TODO: More to do to turn off display?
+ */
+ return;
+ }
+ }
+ // Do this after ajusting pagenumber above
+ _thumbnailService->cancelRequests( -1 , _part->scrollBox(), SLOT( setThumbnail( QPixmap ) ) );
+ _thumbnailService->delayedGetThumbnail( pagenumber, _part->scrollBox(), SLOT( setThumbnail( QPixmap ) ), true );
+
+ emit newPageShown( pagenumber );
+}
+
+void KGVMiniWidget::sendPage()
+{
+ // Send the page to the interpreter.
+ if( !_psWidget->isInterpreterBusy() && _visiblePage != _options.page() )
+ {
+ // Interpreter ready - Fire off next page
+ _psWidget->clear();
+ _psWidget->nextPage();
+ _psWidget->sendPS( psFile(), dsc()->page()[ _options.page() ].begin,
+ dsc()->page()[ _options.page() ].end );
+ _visiblePage = _options.page();
+ }
+}
+
+void KGVMiniWidget::updateStatusBarText( int pageNumber )
+{
+ if( !dsc() )
+ return;
+
+ if( dsc()->isStructured() )
+ {
+ QString text;
+
+ if( pageNumber == -1 )
+ text = i18n( "Page 1" );
+ else
+ if( !_usePageLabels || document()->format() == KGVDocument::PDF )
+ text = i18n( "Page %1 of %2" )
+ .arg( pageNumber + 1 )
+ .arg( dsc()->page_count() );
+ else
+ text = i18n( "Page %1 (%2 of %3)" )
+ .arg( dsc()->page()[ _options.page() ].label )
+ .arg( pageNumber + 1 )
+ .arg( dsc()->page_count() );
+
+ emit setStatusBarText( text );
+ }
+}
+
+void KGVMiniWidget::buildTOC()
+{
+ if( !dsc() )
+ return;
+
+ // Build table of contents
+ // Well, that's what it used to be called !!
+
+ int last_page = 0;
+
+ MarkList* marklist = _part->markList();
+
+ if( dsc()->isStructured() ) {
+ if( _usePageLabels )
+ for( unsigned i = 0; i < dsc()->page_count(); ++i ) {
+ unsigned j = i;
+ if( dsc()->page_order() == CDSC_DESCEND )
+ j = ( dsc()->page_count() - 1 ) - i;
+ last_page = atoi( dsc()->page()[j].label );
+ }
+
+ // finally set marked list
+ QString s;
+ for( unsigned i = 0; i < dsc()->page_count(); ++i ) {
+ const char * label = dsc()->page()[ i ].label;
+ QString tip = QString::fromLocal8Bit( label ? label : "" );
+
+ if( !_usePageLabels )
+ s.setNum( i + 1 );
+ else
+ s = tip;
+
+ marklist->insertItem( s, i, tip );
+ }
+ }
+ else {
+ marklist->insertItem( QString::fromLatin1( "1" ), 0 );
+ }
+}
+
+void KGVMiniWidget::setMagnification( double magnification )
+{
+ if ( magnification != _options.magnification() ) {
+ _options.setMagnification( magnification );
+ showPage( _options.page() );
+ }
+}
+
+void KGVMiniWidget::enablePageLabels( bool b )
+{
+ if( _usePageLabels != b )
+ {
+ _usePageLabels = b;
+ updateStatusBarText( _options.page() );
+ buildTOC();
+ }
+}
+
+void KGVMiniWidget::slotDocumentOpened()
+{
+ buildTOC();
+ showPage( _options.page() );
+}
+
+void KGVMiniWidget::setDisplayOptions( const DisplayOptions& newOptions )
+{
+ _options = newOptions;
+}
+
+#include "kgv_miniwidget.moc"
+
+
+// vim:sw=4:sts=4:ts=8:sta:tw=78:noet
diff --git a/kghostview/kgv_miniwidget.h b/kghostview/kgv_miniwidget.h
new file mode 100644
index 00000000..3c445d14
--- /dev/null
+++ b/kghostview/kgv_miniwidget.h
@@ -0,0 +1,173 @@
+/**
+ * Copyright (C) 1997-2003 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KGV_MINIWIDGET_H
+#define __KGV_MINIWIDGET_H
+
+#include <qprinter.h>
+
+#include "dscparse_adapter.h"
+#include "kgv.h"
+#include "kgvdocument.h"
+#include "displayoptions.h"
+
+#include "thumbnailservice.h"
+
+#include <qobject.h>
+
+class InfoDialog;
+class KGVPart;
+class KPSWidget;
+class MarkList;
+
+class KGVMiniWidget : public QObject
+{
+ Q_OBJECT
+
+public:
+ KGVMiniWidget( KGVPart* part, const char* name = 0 );
+
+ void setDocument( KGVDocument* document );
+
+ static QString pageSizeToString( QPrinter::PageSize );
+
+ void setPSWidget( KPSWidget* psWidget );
+
+ void setDisplayOptions( const DisplayOptions& newOptions );
+ const DisplayOptions& displayOptions() const { return _options; }
+
+ void restoreOverrideOrientation();
+ void setOverrideOrientation( CDSC_ORIENTATION_ENUM );
+
+ void restoreOverridePageMedia();
+ void setOverridePageMedia( const QString& mediaName );
+
+ ThumbnailService* getThumbnailService() { return _thumbnailService; }
+
+ /**
+ * Enable/disable fancy, document-supplied page labels.
+ **/
+ void enablePageLabels( bool e = true );
+ bool arePageLabelsEnabled () { return _usePageLabels; }
+
+ /**
+ * Return true if the current page is the first page, false otherwise.
+ */
+ bool atFirstPage() const;
+ /**
+ * Return true if the current page is the last page, false otherwise.
+ */
+ bool atLastPage() const;
+
+ /**
+ * Return true if we're zoomed in fully, false otherwise.
+ */
+ bool atMaxZoom() const;
+ /**
+ * Return true if we're zoomed out fully, false otherwise.
+ */
+ bool atMinZoom() const;
+
+ int currentPage() const { return _options.page(); }
+
+public slots:
+ bool prevPage();
+ bool nextPage();
+ void firstPage();
+ void lastPage();
+ void goToPage();
+ void goToPage( int page );
+
+ void zoomIn();
+ void zoomOut();
+
+ void fitWidth( unsigned int );
+ void fitHeight( unsigned int );
+ void fitWidthHeight( unsigned int, unsigned int );
+
+ void info();
+
+ /**
+ * Redisplay the page if the file has changed on disk.
+ **/
+ void redisplay();
+
+signals:
+ /**
+ * Page changed.
+ */
+ void newPageShown( int pageNumber ); // Should this one be under DOCUMENT?
+
+ void newPageImage( QPixmap image );
+
+ void setStatusBarText( const QString& );
+
+protected:
+ void showPage( int pageNumber );
+ void buildTOC();
+
+protected slots:
+ void sendPage();
+ void updateStatusBarText( int pageNumber );
+
+protected:
+ void reset();
+
+private:
+
+ CDSC_ORIENTATION_ENUM orientation() const;
+ CDSC_ORIENTATION_ENUM orientation( int pageNo ) const;
+
+ QString pageMedia() const;
+ QString pageMedia( int pageNo ) const;
+
+ KDSCBBOX boundingBox() const;
+ KDSCBBOX boundingBox( int pageNo ) const;
+
+ void setMagnification( double );
+
+ KDSC* const dsc() const
+ { return _document ? _document->dsc() : 0; }
+ FILE* psFile()
+ { return document()->psFile(); }
+ KGVDocument* const document() const
+ { return _document; }
+
+private slots:
+ void slotDocumentOpened();
+
+private:
+
+ friend class ThumbnailService;
+ KGVDocument* _document;
+
+ KGVPart* _part;
+ KPSWidget* _psWidget;
+ ThumbnailService* _thumbnailService;
+
+ bool _usePageLabels;
+
+ int _visiblePage;
+
+ DisplayOptions _options;
+ QString _fallBackPageMedia;
+};
+
+#endif
+
+// vim:sw=4:sts=4:ts=8:sta:tw=78:noet
diff --git a/kghostview/kgv_part.rc b/kghostview/kgv_part.rc
new file mode 100644
index 00000000..1ecc846a
--- /dev/null
+++ b/kghostview/kgv_part.rc
@@ -0,0 +1,110 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="KGhostView" version="22">
+<State name="initState">
+ <Disable>
+ <Action name="file_save_as" />
+ <Action name="info" />
+ <Action name="orientation_menu" />
+ <Action name="media_menu" />
+ <Action name="zoomOut" />
+ <Action name="zoomTo" />
+ <Action name="zoomIn" />
+ <Action name="fit_to_page" />
+ <Action name="fit_to_screen" />
+ <Action name="prevPage" />
+ <Action name="nextPage" />
+ <Action name="goToStart" />
+ <Action name="goToEnd" />
+ <Action name="goToPage" />
+ <Action name="readUp" />
+ <Action name="readDown" />
+ <Action name="mark_current" />
+ <Action name="mark_all" />
+ <Action name="mark_even" />
+ <Action name="mark_odd" />
+ <Action name="toggle" />
+ <Action name="remove" />
+ <Action name="no_flicker" />
+ </Disable>
+</State>
+<State name="documentState">
+ <Enable>
+ <Action name="file_save_as" />
+ <Action name="info" />
+ <Action name="orientation_menu" />
+ <Action name="media_menu" />
+ <Action name="zoomOut" />
+ <Action name="zoomTo" />
+ <Action name="zoomIn" />
+ <Action name="fit_to_page" />
+ <Action name="fit_to_screen" />
+ <Action name="prevPage" />
+ <Action name="nextPage" />
+ <Action name="goToStart" />
+ <Action name="goToEnd" />
+ <Action name="goToPage" />
+ <Action name="readUp" />
+ <Action name="readDown" />
+ <Action name="mark_current" />
+ <Action name="mark_all" />
+ <Action name="mark_even" />
+ <Action name="mark_odd" />
+ <Action name="toggle" />
+ <Action name="remove" />
+ <Action name="no_flicker" />
+ </Enable>
+</State>
+<MenuBar>
+ <Menu name="file"><text>&amp;File</text>
+ <Action name="file_save_as" group="file_save" />
+ <Action name="info" group="file_print" />
+ </Menu>
+ <Menu name="edit"><text>&amp;Edit</text>
+ <Action name="mark_current"/>
+ <Action name="mark_all"/>
+ <Action name="mark_even"/>
+ <Action name="mark_odd"/>
+ <Action name="toggle"/>
+ <Action name="remove"/>
+ </Menu>
+ <Menu name="view"><text>&amp;View</text>
+ <Action name="orientation_menu"/>
+ <Action name="media_menu"/>
+ <Separator/>
+ <Action name="zoomIn"/>
+ <Action name="zoomTo" />
+ <Action name="zoomOut"/>
+ <Action name="fit_to_page"/>
+ <Action name="fit_to_screen" />
+ <Action name="prevPage"/>
+ <Action name="nextPage"/>
+ <Action name="goToStart"/>
+ <Action name="goToEnd"/>
+ <Action name="goToPage"/>
+ <Action name="readUp"/>
+ <Action name="readDown"/>
+ </Menu>
+ <Menu name="settings"><text>&amp;Settings</text>
+ <Action name="show_scrollbars"/>
+ <Action name="show_page_list"/>
+ <Action name="show_page_labels"/>
+ <Separator/>
+ <Action name="watch_file"/>
+ <Action name="no_flicker" />
+ <Separator/>
+ <Action name="options_configure_keybinding"/>
+ <Action name="options_configure" group="options_configure"/>
+ </Menu>
+</MenuBar>
+<ToolBar name="mainToolBar">
+ <text>&amp;Main Toolbar</text>
+ <Action name="zoomIn"/>
+ <Action name="zoomTo" />
+ <Action name="zoomOut"/>
+ <Action name="goToStart"/>
+ <Action name="readUp"/>
+ <Action name="readDown"/>
+ <Action name="goToEnd"/>
+</ToolBar>
+</kpartgui>
+
diff --git a/kghostview/kgv_view.cpp b/kghostview/kgv_view.cpp
new file mode 100644
index 00000000..3c25ef41
--- /dev/null
+++ b/kghostview/kgv_view.cpp
@@ -0,0 +1,1037 @@
+/**
+ * Copyright (C) 2000-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qframe.h>
+#include <qtable.h>
+#include <qlayout.h>
+#include <qregexp.h>
+
+#include <kaction.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kdirwatch.h>
+#include <kglobalsettings.h>
+#include <kiconloader.h>
+#include <kinstance.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kstdaction.h>
+#include <kstdaccel.h>
+#include <ktempfile.h>
+#include <kio/scheduler.h>
+#include <kaboutdata.h>
+
+
+#include "kgv_view.h"
+#include "kgv_miniwidget.h"
+#include "kgvconfigdialog.h"
+#include "kgvdocument.h"
+#include "kgvpagedecorator.h"
+#include "kgvpageview.h"
+#include "kgvmainwidget.h"
+#include "kpswidget.h"
+#include "kgvfactory.h"
+#include "logwindow.h"
+#include "marklist.h"
+#include "scrollbox.h"
+#include "version.h"
+#include "configuration.h"
+
+#include <cmath>
+
+namespace KGV {
+ /*
+ * This is because Qt's iterators
+ * are not standard iterators bc of missing typedefs,
+ * so they are only *almost* STL compatible
+ */
+ template <typename T>
+ unsigned distance( T a, T b ) {
+ unsigned res = 0;
+ while ( a != b ) {
+ ++res;
+ ++a;
+ }
+ return res;
+ }
+}
+
+KGVPart::KGVPart( QWidget* parentWidget, const char*,
+ QObject* parent, const char* name,
+ const QStringList &args ) :
+ KParts::ReadOnlyPart( parent, name ),
+ _fitTimer( new QTimer( this ) ),
+ _job( 0 ),
+ _mimetypeScanner( 0 ),
+ _dirtyHandler( new QTimer( this ) ),
+ _isGuiInitialized( false ),
+ _isFileDirty( false ),
+ _stickyOptions( false ),
+ _embeddedInKGhostView( !args.contains( "KParts::ReadOnlyPart" ) ),
+ _customZoomIndex( -1 )
+{
+ setInstance( KGVFactory::instance() );
+
+ // Don't show the progress info dialog if we're embedded in Konqueror.
+ setProgressInfoEnabled( !args.contains( "Browser/View") );
+
+ _document = new KGVDocument( this );
+ connect( _document, SIGNAL( fileChangeFailed() ),
+ this, SLOT( slotCancelWatch() ) );
+ connect( _document, SIGNAL( completed() ),
+ this, SLOT( slotOpenFileCompleted() ) );
+ connect( _document, SIGNAL( canceled( const QString& ) ),
+ this, SIGNAL( canceled( const QString& ) ) );
+
+ _fileWatcher = new KDirWatch( this );
+ connect( _fileWatcher, SIGNAL( dirty( const QString& ) ),
+ this, SLOT( slotFileDirty( const QString& ) ) );
+ connect( _dirtyHandler, SIGNAL( timeout() ),
+ this, SLOT( slotDoFileDirty() ) );
+
+ // Setup main widget
+ _mainWidget = new KGVMainWidget( parentWidget );
+ _mainWidget->setFocusPolicy( QWidget::StrongFocus );
+ _mainWidget->installEventFilter( this );
+ _mainWidget->setAcceptDrops( true );
+ connect( _mainWidget, SIGNAL( spacePressed() ),
+ this, SLOT( slotReadDown() ) );
+ connect( _mainWidget, SIGNAL( urlDropped( const KURL& ) ),
+ this, SLOT( openURL( const KURL& ) ) );
+
+ QHBoxLayout* hlay = new QHBoxLayout( _mainWidget, 0, 0 );
+ QVBoxLayout* vlay = new QVBoxLayout( hlay );
+
+ const int PAGELIST_WIDTH = 75;
+
+ _scrollBox = new ScrollBox( _mainWidget , "scrollbox" );
+ _scrollBox->setFixedWidth( PAGELIST_WIDTH );
+ _scrollBox->setMinimumHeight( PAGELIST_WIDTH );
+ vlay->addWidget( _scrollBox );
+
+ _divider = new QFrame( _mainWidget, "divider" );
+ _divider->setFrameStyle( QFrame::Panel | QFrame::Raised );
+ _divider->setLineWidth( 1 );
+ _divider->setMinimumWidth( 3 );
+ hlay->addWidget( _divider );
+
+ _pageView = new KGVPageView( _mainWidget, "pageview" );
+ _pageView->viewport()->setBackgroundMode( QWidget::PaletteMid );
+ hlay->addWidget( _pageView, 1 );
+ _mainWidget->setFocusProxy( _pageView );
+ setWidget( _mainWidget );
+
+ _pageDecorator = new KGVPageDecorator( _pageView->viewport() );
+ _pageDecorator->hide();
+
+
+ _psWidget = new KPSWidget( _pageDecorator );
+ _psWidget->readSettings();
+ _pageView->setPage( _pageDecorator );
+ connect( _psWidget, SIGNAL( output( char*, int ) ),
+ this, SLOT( slotGhostscriptOutput( char*, int ) ) );
+
+ connect( _psWidget, SIGNAL( ghostscriptError( const QString& ) ),
+ this, SLOT( slotGhostscriptError( const QString& ) ) );
+
+
+ _logWindow = new LogWindow( i18n( "Ghostscript Messages" ), _mainWidget, "logwindow" );
+ _showLogWindow = false;
+
+ connect( _logWindow, SIGNAL( configureGS() ), SLOT( slotConfigure() ) );
+
+ _docManager = new KGVMiniWidget( this );
+ _docManager->setPSWidget( _psWidget );
+ _docManager->setDocument( document() );
+
+ _markList = new MarkList( _mainWidget, "marklist", _docManager );
+ _markList->setFixedWidth( PAGELIST_WIDTH );
+ vlay->addWidget( _markList, 1 );
+ connect( _markList, SIGNAL( contextMenuRequested ( int, int, const QPoint& ) ),
+ this, SLOT( showPopup( int, int, const QPoint& ) ) );
+
+
+ connect( _markList, SIGNAL( selected( int ) ),
+ _docManager, SLOT( goToPage( int ) ) );
+ connect( _docManager, SIGNAL( newPageShown( int ) ),
+ _markList, SLOT( select( int ) ) );
+ connect( _docManager, SIGNAL( setStatusBarText( const QString& ) ),
+ this, SIGNAL( setStatusBarText( const QString& ) ) );
+ connect( _scrollBox, SIGNAL( valueChangedRelative( int, int ) ),
+ _pageView, SLOT( scrollBy( int, int ) ) );
+ connect( _pageView, SIGNAL( pageSizeChanged( const QSize& ) ),
+ _scrollBox, SLOT( setPageSize( const QSize& ) ) );
+ connect( _pageView, SIGNAL( viewSizeChanged( const QSize& ) ),
+ _scrollBox, SLOT( setViewSize( const QSize& ) ) );
+ connect( _pageView, SIGNAL( contentsMoving( int, int ) ),
+ _scrollBox, SLOT( setViewPos( int, int ) ) );
+
+ //-- File Menu ----------------------------------------------------------
+ KStdAction::saveAs( document(), SLOT( saveAs() ),
+ actionCollection() );
+ new KAction( i18n( "Document &Info" ), 0,
+ miniWidget(), SLOT( info() ),
+ actionCollection(), "info" );
+
+ //-- Edit Menu -----------------------------------------------------
+ _popup = new KPopupMenu( _markList, "marklist_menu" );
+
+ KAction *act = new KAction( i18n( "Mark Current Page" ), "flag", CTRL+SHIFT+Key_M,
+ _markList, SLOT( markCurrent() ),
+ actionCollection(), "mark_current" );
+ act->plug( _popup );
+ act = new KAction( i18n( "Mark &All Pages" ), 0,
+ _markList, SLOT( markAll() ),
+ actionCollection(), "mark_all" );
+ act->plug( _popup );
+ act = new KAction( i18n( "Mark &Even Pages" ), 0,
+ _markList, SLOT( markEven() ),
+ actionCollection(), "mark_even" );
+ act->plug( _popup );
+ act = new KAction( i18n( "Mark &Odd Pages" ), 0,
+ _markList, SLOT( markOdd() ),
+ actionCollection(), "mark_odd" );
+ act->plug( _popup );
+ act = new KAction( i18n( "&Toggle Page Marks" ), 0,
+ _markList, SLOT( toggleMarks() ),
+ actionCollection(), "toggle" );
+ act->plug( _popup );
+ act = new KAction( i18n("&Remove Page Marks"), 0,
+ _markList, SLOT( removeMarks() ),
+ actionCollection(), "remove" );
+ act->plug( _popup );
+
+ // TODO -- disable entry if there aren't any page names
+
+ //-- View Menu ----------------------------------------------------------
+ _selectOrientation = new KSelectAction( i18n( "&Orientation" ), 0, 0, 0,
+ actionCollection(), "orientation_menu" );
+ _selectMedia = new KSelectAction( i18n( "Paper &Size" ), 0, 0, 0,
+ actionCollection(), "media_menu" );
+
+ _flick = new KToggleAction( i18n( "No &Flicker" ), 0,
+ this, SLOT( slotFlicker() ),
+ actionCollection(), "no_flicker" );
+
+ QStringList orientations;
+ orientations.append( i18n( "Auto" ) );
+ orientations.append( i18n( "Portrait" ) );
+ orientations.append( i18n( "Landscape" ) );
+ orientations.append( i18n( "Upside Down" ) );
+ orientations.append( i18n( "Seascape" ) );
+ _selectOrientation->setItems( orientations );
+
+ connect( _selectOrientation, SIGNAL( activated( int ) ),
+ this, SLOT( slotOrientation( int ) ) );
+ connect( _selectMedia, SIGNAL( activated( int ) ),
+ this, SLOT( slotMedia( int ) ) );
+
+ {
+ KShortcut zoomInShort = KStdAccel::zoomIn();
+ zoomInShort.append( KKey( CTRL+Key_Equal ) );
+ _zoomIn = KStdAction::zoomIn( this, SLOT( slotZoomIn() ),
+ actionCollection(), "zoomIn" );
+ _zoomIn->setShortcut( zoomInShort );
+ }
+ _zoomOut = KStdAction::zoomOut( this, SLOT( slotZoomOut() ),
+ actionCollection(), "zoomOut" );
+ _zoomTo = new KSelectAction( i18n( "Zoom" ), "viewmag", 0, actionCollection(), "zoomTo" );
+ connect( _zoomTo, SIGNAL( activated( const QString & ) ), this, SLOT( slotZoom( const QString& ) ) );
+ _zoomTo->setEditable( true );
+ _zoomTo->clear();
+ QValueList<double> mags = DisplayOptions::normalMagnificationValues();
+ QStringList zooms;
+ int idx = 0;
+ int cur = 0;
+ for ( QValueList<double>::iterator first = mags.begin(), last = mags.end();
+ first != last;
+ ++first ) {
+ QString str = QString( "%1%" ).arg( KGlobal::locale()->formatNumber( *first * 100.0, 2 ));
+ str.remove( KGlobal::locale()->decimalSymbol() + "00" );
+ zooms << str;
+ if ( *first == 1.0 ) idx = cur;
+ ++cur;
+ }
+ _zoomTo->setItems( zooms );
+ _zoomTo->setCurrentItem( idx );
+
+ _fitWidth = new KAction( i18n( "&Fit to Page Width" ), 0, this,
+ SLOT( slotFitToPage() ), actionCollection(),
+ "fit_to_page");
+ _fitScreen = new KAction( i18n( "&Fit to Screen" ), Key_S, this,
+ SLOT( slotFitToScreen() ), actionCollection(),
+ "fit_to_screen");
+
+ _prevPage = new KAction( i18n( "Previous Page" ), CTRL+Key_PageUp, this, SLOT( slotPrevPage() ),
+ actionCollection(), "prevPage" );
+ _prevPage->setWhatsThis( i18n( "Moves to the previous page of the document" ) );
+
+ _nextPage = new KAction( i18n( "Next Page" ), CTRL + Key_PageDown, this, SLOT( slotNextPage() ),
+ actionCollection(), "nextPage" );
+ _nextPage->setWhatsThis( i18n( "Moves to the next page of the document" ) );
+
+ _firstPage = KStdAction::firstPage( this, SLOT( slotGotoStart() ),
+ actionCollection(), "goToStart" );
+ _firstPage->setWhatsThis( i18n( "Moves to the first page of the document" ) );
+
+ _lastPage = KStdAction::lastPage( this, SLOT( slotGotoEnd() ),
+ actionCollection(), "goToEnd" );
+ _lastPage->setWhatsThis( i18n( "Moves to the last page of the document" ) );
+
+ KShortcut readUpShort = KStdAccel::shortcut( KStdAccel::Prior );
+ readUpShort.append( KKey( SHIFT+Key_Space ) );
+ _readUp = new KAction( i18n( "Read Up" ), "up",
+ readUpShort, this, SLOT( slotReadUp() ),
+ actionCollection(), "readUp" );
+
+ KShortcut readDownShort = KStdAccel::shortcut( KStdAccel::Next );
+ readDownShort.append( KKey( Key_Space ) );
+ _readDown = new KAction( i18n( "Read Down" ), "down",
+ readDownShort, this, SLOT( slotReadDown() ),
+ actionCollection(), "readDown" );
+
+ _gotoPage = KStdAction::gotoPage( _docManager, SLOT( goToPage() ),
+ actionCollection(), "goToPage" );
+
+ //-- Settings Menu ------------------------------------------------------
+ _showScrollBars = new KToggleAction( i18n( "Show &Scrollbars" ), 0,
+ actionCollection(), "show_scrollbars" );
+ _showScrollBars->setCheckedState(i18n("Hide &Scrollbars"));
+ _watchFile = new KToggleAction( i18n( "&Watch File" ), 0,
+ this, SLOT( slotWatchFile() ),
+ actionCollection(), "watch_file" );
+ _showPageList = new KToggleAction( i18n( "Show &Page List" ), 0,
+ actionCollection(), "show_page_list" );
+ _showPageList->setCheckedState(i18n("Hide &Page List"));
+ _showPageLabels = new KToggleAction( i18n("Show Page &Labels"), 0,
+ actionCollection(), "show_page_labels" );
+ _showPageLabels->setCheckedState(i18n("Hide Page &Labels"));
+ KStdAction::preferences( this, SLOT( slotConfigure() ), actionCollection() );
+ connect( _showScrollBars, SIGNAL( toggled( bool ) ),
+ SLOT( showScrollBars( bool ) ) );
+ connect( _showPageList, SIGNAL( toggled( bool ) ),
+ SLOT( showMarkList( bool ) ) );
+ connect( _showPageLabels, SIGNAL( toggled( bool ) ),
+ SLOT( showPageLabels( bool ) ) );
+
+ _extension = new KGVBrowserExtension( this );
+
+ setXMLFile( "kgv_part.rc" );
+
+ connect( miniWidget(), SIGNAL( newPageShown( int ) ),
+ this, SLOT( slotNewPage( int ) ) );
+ connect( _pageView, SIGNAL( contentsMoving( int, int ) ),
+ this, SLOT( slotPageMoved( int, int ) ) );
+
+ connect( _pageView, SIGNAL( nextPage() ), SLOT( slotNextPage() ));
+ connect( _pageView, SIGNAL( prevPage() ), SLOT( slotPrevPage() ));
+ connect( _pageView, SIGNAL( zoomIn() ), SLOT( slotZoomIn() ));
+ connect( _pageView, SIGNAL( zoomOut() ), SLOT( slotZoomOut() ));
+ connect( _pageView, SIGNAL( ReadUp() ), SLOT( slotReadUp() ));
+ connect( _pageView, SIGNAL( ReadDown() ), SLOT( slotReadDown() ));
+
+ QStringList items = document()->mediaNames();
+ items.prepend( i18n( "Auto ") );
+ _selectMedia->setItems( items );
+
+ readSettings();
+
+ updatePageDepActions();
+}
+
+KGVPart::~KGVPart()
+{
+ if ( _job ) _job -> kill();
+ delete _mimetypeScanner;
+ writeSettings();
+}
+
+KAboutData* KGVPart::createAboutData()
+{
+ KAboutData* about = new KAboutData( "kghostview", I18N_NOOP( "KGhostView"),
+ KGHOSTVIEW_VERSION,
+ I18N_NOOP( "Viewer for PostScript (.ps, .eps) and Portable Document Format (.pdf) files"),
+ KAboutData::License_GPL,
+ "(C) 1998 Mark Donohoe, (C) 1999-2000 David Sweet, "
+ "(C) 2000-2003 Wilco Greven",
+ I18N_NOOP( "KGhostView displays, prints, and saves "
+ "PostScript and PDF files.\n"
+ "Based on original work by Tim Theisen." ) );
+ about->addAuthor( "Luís Pedro Coelho",
+ I18N_NOOP( "Current maintainer" ),
+ "luis@luispedro.org",
+ "http://luispedro.org" );
+ about->addAuthor( "Wilco Greven",
+ I18N_NOOP( "Maintainer 2000-2003" ),
+ "greven@kde.org" );
+ about->addAuthor( "David Sweet",
+ I18N_NOOP( "Maintainer 1999-2000" ),
+ "dsweet@kde.org",
+ "http://www.andamooka.org/~dsweet" );
+ about->addAuthor( "Mark Donohoe",
+ I18N_NOOP( "Original author" ),
+ "donohoe@kde.org" );
+ about->addAuthor( "David Faure",
+ I18N_NOOP( "Basis for shell"),
+ "faure@kde.org" );
+ about->addAuthor( "Daniel Duley",
+ I18N_NOOP( "Port to KParts" ),
+ "mosfet@kde.org" );
+ about->addAuthor( "Espen Sand",
+ I18N_NOOP( "Dialog boxes" ),
+ "espen@kde.org" );
+ about->addCredit( "Russell Lang of Ghostgum Software Pty Ltd",
+ I18N_NOOP( "for contributing GSView's DSC parser." ),
+ 0,
+ "http://www.ghostgum.com.au/" );
+ about->addCredit( "The Ghostscript authors",
+ 0, 0,
+ "http://www.cs.wisc.edu/~ghost/" );
+ return about;
+}
+
+bool KGVPart::closeURL()
+{
+ document()->close();
+ _psWidget->stopInterpreter();
+ _docManager->getThumbnailService()->reset();
+ _markList->clear();
+ _pageDecorator->hide();
+ _scrollBox->clear();
+ _isFileDirty = false;
+ if ( _job )
+ {
+ _job -> kill();
+ _job = 0;
+ }
+ if( _mimetypeScanner != 0 )
+ _mimetypeScanner->abort();
+ if( !m_file.isEmpty() )
+ _fileWatcher->removeFile( m_file );
+ _mimetype = QString::null;
+ updatePageDepActions();
+ stateChanged( "initState" );
+ return KParts::ReadOnlyPart::closeURL();
+}
+
+void KGVPart::writeSettings()
+{
+ KConfigGroup general( KGVFactory::instance()->config(), "General" );
+ if ( !_embeddedInKGhostView )
+ general.writeEntry( "Display Options", DisplayOptions::toString( miniWidget()->displayOptions() ) );
+ general.sync();
+}
+
+void KGVPart::readSettings()
+{
+ KConfigGroup general( KGVFactory::instance()->config(), "General" );
+
+ _showScrollBars->setChecked( Configuration::showScrollBars() );
+ showScrollBars( _showScrollBars->isChecked() );
+
+ _watchFile->setChecked( Configuration::watchFile() );
+ slotWatchFile();
+
+ _showPageList->setChecked( Configuration::showPageList() );
+ showMarkList( _showPageList->isChecked() );
+
+ _showPageLabels->setChecked( Configuration::watchFile() );
+ showPageLabels( _showPageLabels->isChecked() );
+
+ _showLogWindow = Configuration::messages();
+ if ( !_embeddedInKGhostView ) {
+ DisplayOptions options;
+ if ( DisplayOptions::fromString( options, general.readEntry( "Display Options" ) ) )
+ setDisplayOptions( options );
+ }
+ _psWidget->readSettings();
+}
+
+void KGVPart::slotScrollLeft()
+{
+ _pageView->scrollLeft();
+}
+
+void KGVPart::slotFlicker()
+{
+ if ( _psWidget ) _psWidget->setDoubleBuffering( _flick->isChecked() );
+}
+
+void KGVPart::slotScrollRight()
+{
+ _pageView->scrollRight();
+}
+
+void KGVPart::slotScrollUp()
+{
+ _pageView->scrollUp();
+}
+
+void KGVPart::slotScrollDown()
+{
+ _pageView->scrollDown();
+}
+
+void KGVPart::slotReadUp()
+{
+ if( !( document() && document()->isOpen() ) )
+ return;
+
+ if( !_pageView->readUp() ) {
+ if (_docManager->prevPage())
+ _pageView->scrollBottom();
+ }
+}
+
+void KGVPart::slotReadDown()
+{
+ if( !( document() && document()->isOpen() ) )
+ return;
+
+ if( !_pageView->readDown() ) {
+ if( _docManager->nextPage() )
+ _pageView->scrollTop();
+ }
+}
+
+void KGVPart::slotPrevPage()
+{
+ if( !document() || !document()->isOpen() ) return;
+ _docManager->prevPage();
+}
+
+void KGVPart::slotNextPage()
+{
+ if( !document() || !document()->isOpen() ) return;
+ _docManager->nextPage();
+}
+
+void KGVPart::slotGotoStart()
+{
+ _docManager->firstPage();
+ _pageView->scrollTop();
+}
+
+void KGVPart::slotGotoEnd()
+{
+ _docManager->lastPage();
+ _pageView->scrollTop();
+}
+
+void KGVPart::slotWatchFile()
+{
+ if( _watchFile->isChecked() )
+ _fileWatcher->startScan();
+ else {
+ _dirtyHandler->stop();
+ _fileWatcher->stopScan();
+ }
+}
+
+void KGVPart::slotCancelWatch()
+{
+ _fileWatcher->stopScan();
+ _watchFile->setChecked( false );
+}
+
+/*
+void KGVPart::slotFitWidth()
+{
+ _docManager->fitWidth( pageView()->viewport()->width() -
+ 2*( pageDecorator()->margin() + pageDecorator()->borderWidth() ) );
+}
+*/
+
+void KGVPart::updateZoomActions()
+{
+ if( !( document() && document()->isOpen() ) )
+ return;
+
+ _zoomIn->setEnabled(!_docManager->atMaxZoom());
+ _zoomOut->setEnabled(!_docManager->atMinZoom());
+ _zoomTo->setEnabled( true );
+ QStringList items = _zoomTo->items();
+ bool updateItems = false;
+ if (_customZoomIndex != -1)
+ {
+ items.remove(items.at(_customZoomIndex));
+ _customZoomIndex = -1;
+ updateItems = true;
+ }
+ double zoom = floor(miniWidget()->displayOptions().magnification()*1000.0) / 10.0;
+ unsigned idx = 0;
+ for ( QStringList::iterator first = items.begin(), last = items.end();
+ first != last;
+ ++first ) {
+ QString cur = *first;
+ cur.remove( cur.find( '%' ), 1 );
+ cur = cur.simplifyWhiteSpace();
+ bool ok = false;
+ double z = cur.toDouble(&ok);
+ if ( ok ) {
+ if (std::abs( z - zoom ) < 0.1 ) {
+ if (updateItems)
+ _zoomTo->setItems( items );
+ _zoomTo->setCurrentItem( idx );
+ return;
+ }
+ if ( z > zoom )
+ break;
+ }
+ ++idx;
+ }
+
+ // Show percentage that isn't predefined
+ QString str = QString( "%1%" ).arg( KGlobal::locale()->formatNumber( zoom, 2 ));
+ str.remove( KGlobal::locale()->decimalSymbol() + "00" );
+ items.insert( items.at(idx), 1, str );
+ _zoomTo->setItems( items );
+ _zoomTo->setCurrentItem( idx );
+ _customZoomIndex = idx;
+}
+
+void KGVPart::updatePageDepActions()
+{
+ bool hasDoc = document() && document()->isOpen();
+
+ _fitWidth->setEnabled( hasDoc );
+ _fitScreen->setEnabled( hasDoc );
+
+ _prevPage->setEnabled( hasDoc && !_docManager->atFirstPage() );
+ _firstPage->setEnabled( hasDoc && !_docManager->atFirstPage() );
+ _nextPage->setEnabled( hasDoc && !_docManager->atLastPage() );
+ _lastPage->setEnabled( hasDoc && !_docManager->atLastPage() );
+ _gotoPage->setEnabled( hasDoc &&
+ !(_docManager->atFirstPage() && _docManager->atLastPage()) );
+
+ updateReadUpDownActions();
+}
+
+void KGVPart::updateReadUpDownActions()
+{
+ if( !( document() && document()->isOpen() ) )
+ {
+ _readUp->setEnabled( false );
+ _readDown->setEnabled( false );
+ return;
+ }
+
+ if( _docManager->atFirstPage() && _pageView->atTop() )
+ _readUp->setEnabled( false );
+ else
+ _readUp->setEnabled( true );
+
+ if( _docManager->atLastPage() && _pageView->atBottom() )
+ _readDown->setEnabled( false );
+ else
+ _readDown->setEnabled( true );
+}
+
+bool KGVPart::openURL( const KURL& url )
+{
+ if( !url.isValid() )
+ return false;
+ if( !closeURL() )
+ return false;
+
+ m_url = url;
+ if ( !_stickyOptions ) _options.reset();
+
+ emit setWindowCaption( m_url.prettyURL() );
+
+ _mimetypeScanner = new KGVRun( m_url, 0, m_url.isLocalFile(), false );
+ connect( _mimetypeScanner, SIGNAL( finished( const QString& ) ),
+ SLOT( slotMimetypeFinished( const QString& ) ) );
+ connect( _mimetypeScanner, SIGNAL( error() ),
+ SLOT( slotMimetypeError() ) );
+
+ return true;
+}
+
+void KGVPart::openURLContinue()
+{
+ kdDebug(4500) << "KGVPart::openURLContinue()" << endl;
+ if( m_url.isLocalFile() )
+ {
+ emit started( 0 );
+ m_file = m_url.path();
+ document()->openFile( m_file, _mimetype );
+ }
+ else
+ {
+ m_bTemp = true;
+ // Use same extension as remote file. This is important for
+ // mimetype-determination (e.g. koffice)
+ QString extension;
+ QString fileName = m_url.fileName();
+ int extensionPos = fileName.findRev( '.' );
+ if( extensionPos != -1 )
+ extension = fileName.mid( extensionPos ); // keep the '.'
+ KTempFile tempFile( QString::null, extension );
+ m_file = tempFile.name();
+ _tmpFile.setName( m_file );
+ _tmpFile.open( IO_ReadWrite );
+
+ /*
+ d->m_job = KIO::file_copy( m_url, m_file, 0600, true, false, d->m_showProgressInfo );
+ emit started( d->m_job );
+ connect( d->m_job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotJobFinished ( KIO::Job * ) ) );
+ */
+
+ _job = KIO::get( m_url, false, isProgressInfoEnabled() );
+
+ connect( _job, SIGNAL( data( KIO::Job*, const QByteArray& ) ),
+ SLOT( slotData( KIO::Job*, const QByteArray& ) ) );
+ connect( _job, SIGNAL( result( KIO::Job* ) ),
+ SLOT( slotJobFinished( KIO::Job* ) ) );
+
+ emit started( _job );
+ }
+}
+
+bool KGVPart::openFile()
+{
+ return false;
+}
+
+void KGVPart::slotOpenFileCompleted()
+{
+ _docManager->getThumbnailService()->setEnabled( true );
+ if( _isFileDirty )
+ {
+ _docManager->redisplay();
+ _isFileDirty = false;
+ }
+ else
+ {
+ if ( !_stickyOptions ) setDisplayOptions( DisplayOptions() );
+ _stickyOptions = false;
+
+ stateChanged( "documentState" );
+ if ( !_fileWatcher->contains( m_file ) )
+ _fileWatcher->addFile( m_file );
+ slotWatchFile();
+ updateZoomActions();
+ emit completed();
+ }
+}
+
+void KGVPart::slotGhostscriptOutput( char* data, int len )
+{
+ _logWindow->append( QString::fromLocal8Bit( data, len ) );
+ if( _showLogWindow )
+ _logWindow->show();
+}
+
+
+void KGVPart::slotGhostscriptError( const QString& error )
+{
+ _logWindow->setLabel( i18n( "<qt>An error occurred in rendering.<br>"
+ "<strong>%1</strong><br>"
+ "The display may contain errors.<br>"
+ "Below are any error messages which were received from Ghostscript "
+ "(<nobr><strong>%2</strong></nobr>) "
+ "which may help you.</qt>" )
+ .arg( error )
+ .arg( Configuration::interpreter() ),
+ true );
+ // The true above makes it show a "configure gs" option, but maybe we
+ // should trigger an auto-redetection?
+ // LPC (13 Apr 2003)
+ _logWindow->show();
+}
+
+
+void KGVPart::guiActivateEvent( KParts::GUIActivateEvent* event )
+{
+ if( event->activated() && !_isGuiInitialized )
+ {
+ stateChanged( "initState" );
+ _isGuiInitialized = true;
+ }
+ KParts::ReadOnlyPart::guiActivateEvent( event );
+}
+
+void KGVPart::slotData( KIO::Job* job, const QByteArray& data )
+{
+ Q_ASSERT( _job == job );
+
+ kdDebug(4500) << "KGVPart::slotData: received " << data.size() << " bytes." << endl;
+
+ _tmpFile.writeBlock( data );
+}
+
+void KGVPart::slotMimetypeFinished( const QString& type )
+{
+ kdDebug(4500) << "KGVPart::slotMimetypeFinished( " << type << " )" << endl;
+ _mimetype = type;
+ if( !_mimetypeScanner || _mimetypeScanner->hasError() )
+ emit canceled( QString::null );
+ else
+ openURLContinue();
+ _mimetypeScanner = 0;
+}
+
+void KGVPart::slotMimetypeError()
+{
+ kdDebug(4500) << "KGVPart::slotMimetypeError()" << endl;
+ _mimetypeScanner = 0;
+ emit started( 0 );
+ //kapp->processEvents();
+ emit canceled( QString::null );
+}
+
+void KGVPart::slotJobFinished( KIO::Job* job )
+{
+ Q_ASSERT( _job == job );
+
+ kdDebug(4500) << "KGVPart::slotJobFinished" << endl;
+
+ _job = 0;
+
+ _tmpFile.close();
+
+ if( job->error() )
+ emit canceled( job->errorString() );
+ else
+ document()->openFile( m_file, _mimetype );
+}
+
+void KGVPart::slotFileDirty( const QString& fileName )
+{
+ // The beauty of this is that each start cancels the previous one.
+ // This means that timeout() is only fired when there have
+ // no changes to the file for the last 750 milisecs.
+ // This is supposed to ensure that we don't update on every other byte
+ // that gets written to the file.
+ if ( fileName == m_file )
+ {
+ _dirtyHandler->start( 750, true );
+ }
+}
+
+void KGVPart::slotDoFileDirty()
+{
+ kdDebug(4500) << "KGVPart::File changed" << endl;
+ _isFileDirty = true;
+ reloadFile();
+}
+
+void KGVPart::slotNewPage( int )
+{
+ updatePageDepActions();
+ //media->setCurrentItem (miniWidget()->getSize()-1);
+ //orientation->setCurrentItem (miniWidget()->getOrientation()-1);
+ //TODO -- zoom
+}
+
+void KGVPart::slotPageMoved( int, int )
+{
+ updateReadUpDownActions();
+}
+
+void KGVPart::slotOrientation( int id )
+{
+ switch( id ) {
+ case 0: miniWidget()->restoreOverrideOrientation(); break;
+ case 1: miniWidget()->setOverrideOrientation( CDSC_PORTRAIT ); break;
+ case 2: miniWidget()->setOverrideOrientation( CDSC_LANDSCAPE ); break;
+ case 3: miniWidget()->setOverrideOrientation( CDSC_UPSIDEDOWN ); break;
+ case 4: miniWidget()->setOverrideOrientation( CDSC_SEASCAPE ); break;
+ default: ;
+ }
+}
+
+void KGVPart::slotMedia( int id )
+{
+ if( id == 0 )
+ miniWidget()->restoreOverridePageMedia();
+ else
+ miniWidget()->setOverridePageMedia( _document->mediaNames()[id-1] );
+}
+
+void KGVPart::showScrollBars( bool show )
+{
+ _pageView->enableScrollBars( show );
+}
+
+void KGVPart::showMarkList( bool show )
+{
+ _markList->setShown( show );
+ _scrollBox->setShown( show );
+ _divider->setShown( show );
+}
+
+void KGVPart::showPageLabels( bool show )
+{
+ _docManager->enablePageLabels( show );
+}
+
+void KGVPart::slotZoomIn()
+{
+ _docManager->zoomIn();
+ updateZoomActions();
+}
+
+void KGVPart::slotZoomOut()
+{
+ _docManager->zoomOut();
+ updateZoomActions();
+}
+
+void KGVPart::slotZoom( const QString& nz )
+{
+ QString z = nz;
+ double zoom;
+ z.remove( z.find( '%' ), 1 );
+ zoom = KGlobal::locale()->readNumber( z ) / 100;
+ kdDebug( 4500 ) << "ZOOM = " << nz << ", setting zoom = " << zoom << endl;
+
+ DisplayOptions options = miniWidget()->displayOptions();
+ options.setMagnification( zoom );
+ miniWidget()->setDisplayOptions( options );
+ miniWidget()->redisplay();
+ _mainWidget->setFocus();
+ updateZoomActions();
+}
+
+void KGVPart::slotFitToPage()
+{
+ kdDebug(4500) << "KGVPart::slotFitToPage()" << endl;
+ if( pageView()->page() )
+ miniWidget()->fitWidth( pageView()->viewport()->width() - 16 );
+ // We subtract 16 pixels because of the page decoration.
+ updateZoomActions();
+}
+
+void KGVPart::slotFitToScreen()
+{
+ kdDebug(4500) << "KGVPart::slotFitToScreen()" << endl;
+ if ( _fitTimer->isActive() ) {
+ disconnect( _fitTimer, SIGNAL( timeout() ), this, 0 );
+ connect( _fitTimer, SIGNAL( timeout() ), SLOT( slotDoFitToScreen() ) );
+ }
+ else slotDoFitToScreen();
+}
+
+void KGVPart::slotDoFitToScreen()
+{
+ kdDebug(4500) << "KGVPart::slotDoFitToScreen()" << endl;
+ if( pageView()->page() )
+ miniWidget()->fitWidthHeight( pageView()->viewport()->width() - 16,
+ pageView()->viewport()->height() - 16 );
+ updateZoomActions();
+}
+
+void KGVPart::reloadFile()
+{
+ _psWidget->stopInterpreter();
+ _docManager->getThumbnailService()->reset();
+ document()->openFile( m_file, _mimetype );
+}
+
+void KGVPart::slotConfigure()
+{
+ ConfigDialog::showSettings( this );
+}
+
+void KGVPart::slotConfigurationChanged()
+{
+ readSettings();
+ _psWidget->readSettings();
+ miniWidget()->redisplay();
+}
+
+void KGVPart::setDisplayOptions( const DisplayOptions& options )
+{
+ kdDebug(4500) << "KGVPart::setDisplayOptions()" << endl;
+ _stickyOptions = true;
+ _markList->select( options.page() );
+ _docManager->setDisplayOptions( options );
+ _selectOrientation->setCurrentItem( options.overrideOrientation() );
+ QStringList medias = document()->mediaNames();
+ QStringList::Iterator now = medias.find( options.overridePageMedia() );
+ if ( now != medias.end() ){
+ // The options are displayed in inverted order.
+ // Therefore, size() - index gets you the display index
+ _selectMedia->setCurrentItem( medias.size() - KGV::distance( medias.begin(), now ) );
+ } else {
+ _selectMedia->setCurrentItem( 0 );
+ }
+}
+
+
+KGVBrowserExtension::KGVBrowserExtension( KGVPart *parent ) :
+ KParts::BrowserExtension( parent, "KGVBrowserExtension" )
+{
+ emit enableAction( "print", true );
+ setURLDropHandlingEnabled( true );
+}
+
+void KGVBrowserExtension::print()
+{
+ ((KGVPart *)parent())->document()->print();
+}
+
+
+KGVRun::KGVRun( const KURL& url, mode_t mode, bool isLocalFile,
+ bool showProgressInfo ) :
+ KRun( url, mode, isLocalFile, showProgressInfo )
+{
+ connect( this, SIGNAL( finished() ), SLOT( emitFinishedWithMimetype() ) );
+}
+
+KGVRun::~KGVRun()
+{}
+
+void KGVRun::foundMimeType( const QString& mimetype )
+{
+ kdDebug(4500) << "KGVRun::foundMimeType( " << mimetype << " )" << endl;
+
+ if( m_job && m_job->inherits( "KIO::TransferJob" ) )
+ {
+ KIO::TransferJob *job = static_cast< KIO::TransferJob* >( m_job );
+ job->putOnHold();
+ m_job = 0;
+ }
+
+ _mimetype = mimetype;
+
+ m_bFinished = true;
+ m_timer.start( 0, true );
+}
+
+
+void KGVPart::updateFullScreen( bool fs )
+{
+ if ( fs ) showMarkList( false );
+ else showMarkList( _showPageList->isChecked() );
+}
+
+void KGVPart::showPopup( int, int, const QPoint& pos )
+{
+ _popup->exec( pos );
+}
+
+#include "kgv_view.moc"
+
+
+// vim:sw=4:sts=4:ts=8:sta:tw=78:noet
diff --git a/kghostview/kgv_view.h b/kghostview/kgv_view.h
new file mode 100644
index 00000000..3a426f58
--- /dev/null
+++ b/kghostview/kgv_view.h
@@ -0,0 +1,268 @@
+/**
+ * Copyright (C) 2000-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KGV_VIEW_H
+#define __KGV_VIEW_H
+
+#include <qcstring.h> // QByteArray
+#include <qfile.h>
+
+#include <kio/job.h>
+#include <kparts/browserextension.h>
+#include <krun.h>
+
+#include "displayoptions.h"
+
+class QFrame;
+class QWidget;
+
+class KAboutData;
+class KAction;
+class KActionCollection;
+class KDirWatch;
+class KInstance;
+class KPopupMenu;
+class KSelectAction;
+class KToggleAction;
+
+class KGVBrowserExtension;
+class KGVConfigDialog;
+class KGVDocument;
+class KGVMiniWidget;
+class KGVPageView;
+class KGVPageDecorator;
+class KGVRun;
+class KPSWidget;
+class LogWindow;
+class MarkList;
+class ScrollBox;
+
+class KGVPart: public KParts::ReadOnlyPart
+{
+ Q_OBJECT
+public:
+ KGVPart( QWidget* parentWidget, const char* widgetName,
+ QObject* parent, const char* name,
+ const QStringList& args = QStringList() );
+
+ virtual ~KGVPart();
+
+ KGVMiniWidget* miniWidget() const { return _docManager; }
+ MarkList* markList() const { return _markList; }
+ ScrollBox* scrollBox() { return _scrollBox; }
+ KGVPageView* pageView() const { return _pageView; }
+ KGVPageDecorator* pageDecorator() const { return _pageDecorator; }
+ KGVDocument* document() const { return _document; }
+
+
+ /**
+ * Reimplemented from ReadOnlyPart in order to delete the file from
+ * KDirWatch's list.
+ */
+ virtual bool closeURL();
+
+ KDE_EXPORT static KAboutData* createAboutData();
+
+public slots:
+ /**
+ * Reimplemented from ReadOnlyPart so that incoming data can be sent
+ * through the DSC parser immediately on arrival.
+ */
+ virtual bool openURL( const KURL& );
+ virtual void openURLContinue();
+
+ /**
+ * Reloads the current file.
+ * No action if no file is loaded
+ */
+ void reloadFile();
+
+ void updateFullScreen( bool );
+
+ void showPopup( int, int, const QPoint &pos );
+
+ void slotScrollLeft();
+ void slotScrollRight();
+ void slotScrollUp();
+ void slotScrollDown();
+ void slotReadDown();
+ void slotFlicker();
+ void slotReadUp();
+ void slotPrevPage();
+ void slotNextPage();
+ void slotGotoStart();
+ void slotGotoEnd();
+
+ void slotFitToPage();
+ void slotFitToScreen();
+ void slotDoFitToScreen();
+
+ void showScrollBars( bool );
+ void slotCancelWatch();
+ void showMarkList( bool );
+ void showPageLabels( bool );
+
+ void slotZoomIn();
+ void slotZoomOut();
+
+ void slotZoom( const QString& );
+
+ void slotConfigure();
+ void slotConfigurationChanged();
+
+ /**
+ * Sets the display options in a sticky way.
+ * This means that the file being opened or the next one to be open will
+ * get these options. This is useful for session management or commandline
+ * arguments
+ */
+ void setDisplayOptions( const DisplayOptions& opts );
+
+protected slots:
+ void slotData( KIO::Job*, const QByteArray& );
+ void slotJobFinished( KIO::Job* );
+
+ void slotMimetypeFinished( const QString& );
+ void slotMimetypeError();
+
+ void slotFileDirty( const QString& );
+ void slotDoFileDirty();
+
+ void slotOrientation (int);
+ void slotMedia (int);
+ void slotNewPage( int );
+ void slotPageMoved( int, int );
+ void slotWatchFile();
+
+ void slotOpenFileCompleted();
+
+protected:
+ virtual void guiActivateEvent( KParts::GUIActivateEvent* );
+
+ // reimplemented from ReadOnlyPart
+ virtual bool openFile();
+
+ void updatePageDepActions();
+ void updateZoomActions();
+ void updateReadUpDownActions();
+
+ void readSettings();
+ void writeSettings();
+
+private slots:
+ void slotGhostscriptOutput( char* data, int len );
+ void slotGhostscriptError( const QString& );
+
+private:
+ KGVBrowserExtension* _extension;
+
+ KGVDocument* _document;
+
+ QWidget* _mainWidget;
+ KGVPageView* _pageView;
+ KGVPageDecorator* _pageDecorator;
+ KPSWidget* _psWidget;
+ ScrollBox* _scrollBox;
+ QFrame* _divider;
+ MarkList* _markList;
+ KGVMiniWidget* _docManager;
+
+ LogWindow* _logWindow;
+
+ QTimer* _fitTimer;
+
+ KSelectAction* _selectOrientation;
+ KSelectAction* _selectMedia;
+ KAction* _zoomIn;
+ KAction* _zoomOut;
+ KSelectAction* _zoomTo;
+ KAction * _fitWidth;
+ KAction * _fitScreen;
+ KAction* _prevPage;
+ KAction* _nextPage;
+ KAction* _firstPage;
+ KAction* _lastPage;
+ KAction* _readUp;
+ KAction* _readDown;
+ KAction* _gotoPage;
+ KToggleAction* _showScrollBars;
+ KToggleAction* _watchFile;
+ KToggleAction* _flick;
+ KToggleAction* _showPageList;
+ KToggleAction* _showPageLabels;
+ KPopupMenu* _popup;
+
+ QFile _tmpFile;
+ KIO::TransferJob* _job;
+ KDirWatch* _fileWatcher;
+ KGVRun* _mimetypeScanner;
+ QTimer* _dirtyHandler;
+
+ QString _mimetype;
+
+ bool _isGuiInitialized : 1;
+ bool _isFileDirty : 1;
+ bool _showLogWindow : 1;
+ bool _stickyOptions : 1;
+ bool _embeddedInKGhostView : 1;
+
+ int _customZoomIndex;
+
+ DisplayOptions _options;
+};
+
+
+class KGVBrowserExtension : public KParts::BrowserExtension
+{
+ Q_OBJECT
+ friend class KGVPart; // emits our signals
+public:
+ KGVBrowserExtension( KGVPart* parent );
+ virtual ~KGVBrowserExtension() {}
+
+public slots:
+ // Automatically detected by konqueror
+ void print();
+};
+
+class KGVRun : public KRun
+{
+ Q_OBJECT
+
+public:
+ KGVRun( const KURL& url, mode_t mode = 0,
+ bool isLocalFile = false, bool showProgressInfo = true );
+
+ virtual ~KGVRun();
+
+signals:
+ void finished( const QString& mimetype );
+
+protected:
+ void foundMimeType( const QString& mimetype );
+
+protected slots:
+ void emitFinishedWithMimetype() { emit finished( _mimetype ); }
+
+private:
+ QString _mimetype;
+};
+
+#endif
+
+// vim:sw=4:sts=4:ts=8:sta:tw=78:noet
diff --git a/kghostview/kgvconfigdialog.cpp b/kghostview/kgvconfigdialog.cpp
new file mode 100644
index 00000000..a9952008
--- /dev/null
+++ b/kghostview/kgvconfigdialog.cpp
@@ -0,0 +1,154 @@
+/**
+ * Copyright (C) 2000-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+// Add header files alphabetically
+
+#include <qlayout.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kinstance.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+#include <kstandarddirs.h>
+#include <kconfigdialog.h>
+
+#include "configuration.h"
+#include "kgv_view.h"
+#include "kgvfactory.h"
+
+#include "generalsettingswidget.h"
+#include "gssettingswidget.h"
+
+#include "kgvconfigdialog.h"
+
+
+namespace {
+ QString getGSVersion( QString fullPathToExec )
+ {
+ QString res;
+ QString chkVersion = KProcess::quote(fullPathToExec) + " --version";
+ FILE* p = popen( QFile::encodeName(chkVersion), "r" );
+ if( p ) {
+ // FIXME: a badly configured interpreter can hang us
+ QFile qp;
+ qp.open( IO_ReadOnly, p );
+ qp.readLine( res, 80 );
+ qp.close();
+ pclose( p );
+ res = res.stripWhiteSpace();
+ }
+ kdDebug(4500) << "kgvconfigdialog.cpp::{unamed}::getGSVersion() returning " << res << endl;
+ return res;
+ }
+
+
+ /* A mechanism for triggering redetection of gs versions:
+ *
+ * The idea is that whenever we find we need to workaround a certain version of gs,
+ * we cannot rely that there will solelly be a new kghostviewrc, but that users will
+ * upgrade kghostview. Therefore, whenever we want to trigger redection on a new version,
+ * we increment the counter below. It should have the old value from the previous version
+ * of kghostview and this function will get called.
+ *
+ */
+
+ /* On a related note:
+ * We don't detect upgrades (or downgrades, for that matter) of gs.
+ * I am seeing if I can get the version out of gs as a side effect to displaying a file.
+ * This way, using kconfig:/Ghostscript/GS Version we will see whether the version has changed
+ * and trigger a redetection without the trouble of running "gs --version" on each launch.
+ *
+ * LPC (9 April 2003)
+ */
+ /* currentRedetection value log:
+ *
+ * 1- remove -dMaxBitmap for gs 6.5x
+ * 2- see if it supports .setsafe ( see bug:57291 ).
+ */
+ const int currentRedetection = 2;
+
+
+ /* Here are the issues we found so far in version of gs:
+ *
+ * - gs before 6.50 uses device x11alpha instead of x11
+ * - gs 6.5x does not work well with -dMaxBitmap=...
+ * - gs 6.52 and earlier as well as 7.0x for x < 4 don't support the .setsafe operator
+ *
+ */
+
+ QString recommendSetSafe( QString version )
+ {
+ if ( version < QString::number( 6.53 ) ) return QString::number( 6.53 );
+ if ( version[ 0 ] == '7' && version < QString::number( 7.04 ) ) return QString::number( 7.05 );
+ return QString::null;
+ }
+ // This function should contain all the gs version specific workarounds.
+ void redoGSDetection()
+ {
+ kdDebug(4500) << "kgvconfigdialog.cpp::{unnamed}::redoGSDetection()" << endl;
+ QString version = getGSVersion( Configuration::interpreter() );
+ QString recommended = recommendSetSafe( version );
+ if ( !recommended.isNull() ) {
+ KMessageBox::sorry( 0,
+ i18n( "Your version of gs (version %1) is too old, since it has security issues "
+ "which are impossible to resolve. Please upgrade to a newer version.\n"
+ "KGhostView will try to work with it, but it may not display any files at all.\n"
+ "Version %2 seems to be appropriate on your system, although newer versions will work as well." )
+ .arg( version )
+ .arg( recommended ) );
+ }
+ if ( version < QString::number( 7.00 ) )
+ {
+ QStringList arguments = QStringList::split( ' ', Configuration::antialiasingArguments() );
+ arguments.remove( QString::fromLatin1( "-dMaxBitmap=10000000" ) );
+ QString antiAliasArgs = arguments.join( " " );
+
+ Configuration::setAntialiasingArguments( antiAliasArgs );
+ }
+
+ Configuration::setRedetectionCounter( currentRedetection );
+ Configuration::setVersion( version );
+ }
+} // namespace
+
+void ConfigDialog::showSettings( KGVPart* main ) {
+ const char* name = "kghostview-settings";
+ if ( KConfigDialog::showDialog( name ) ) return;
+
+ if ( Configuration::redetectionCounter() < currentRedetection ) redoGSDetection();
+
+ KConfigDialog* dialog = new KConfigDialog( 0, name,
+ Configuration::self(), KDialogBase::IconList );
+ dialog->addPage( new GeneralSettingsWidget( 0, "general-settings" ),
+ i18n( "General" ), QString::fromLatin1( "kghostview" ) );
+ GSSettingsWidget *gssw = new GSSettingsWidget( 0, "gs-settings" );
+ dialog->addPage( gssw, i18n( "Ghostscript\nConfiguration" ), QString::fromLatin1( "pdf" ) );
+
+ gssw->setDetectedVersion(Configuration::version());
+
+ QObject::connect( dialog, SIGNAL( settingsChanged() ), main, SLOT( slotConfigurationChanged() ) );
+ dialog->show();
+}
+
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/kgvconfigdialog.h b/kghostview/kgvconfigdialog.h
new file mode 100644
index 00000000..3f7ddead
--- /dev/null
+++ b/kghostview/kgvconfigdialog.h
@@ -0,0 +1,32 @@
+/**
+ * Copyright (C) 2000-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _INTERPRETER_DIALOG_H_
+#define _INTERPRETER_DIALOG_H_
+
+class KGVPart;
+
+namespace ConfigDialog {
+ void showSettings( KGVPart* );
+
+}
+
+#endif
+
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/kgvdocument.cpp b/kghostview/kgvdocument.cpp
new file mode 100644
index 00000000..d5b7df41
--- /dev/null
+++ b/kghostview/kgvdocument.cpp
@@ -0,0 +1,864 @@
+/**
+ * Copyright (C) 1997-2003 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <algorithm>
+#include <memory>
+
+#include <qfileinfo.h>
+
+#include <kconfig.h>
+#include <kfiledialog.h>
+#include <kfilterdev.h>
+#include <kinstance.h>
+#include <kmessagebox.h>
+#include <kmimetype.h>
+#include <kprinter.h>
+#include <kprocess.h>
+#include <ktempfile.h>
+#include <kio/netaccess.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "configuration.h"
+#include "kdscerrordialog.h"
+#include "kgv_miniwidget.h"
+#include "marklist.h"
+#include "kgvfactory.h"
+
+extern "C" {
+#include "ps.h"
+}
+
+#include "kgvdocument.h"
+
+using namespace std;
+using namespace KGV;
+
+KGVDocument::KGVDocument( KGVPart* part, const char* name ) :
+ QObject( part, name ),
+ _psFile( 0 ),
+ _part( part ),
+ _tmpUnzipped( 0 ),
+ _tmpFromPDF( 0 ),
+ _tmpDSC( 0 ),
+ _isFileOpen( false ),
+ _dsc( 0 )
+
+{
+ readSettings();
+
+ _pdf2dsc = new Pdf2dsc( _interpreterPath, this );
+ connect( _pdf2dsc, SIGNAL( finished( bool ) ),
+ SLOT( openPDFFileContinue( bool ) ) );
+}
+
+KGVDocument::~KGVDocument()
+{
+ close();
+}
+
+void KGVDocument::readSettings()
+{
+ _interpreterPath = Configuration::interpreter();
+}
+
+/*- OPENING and READING ---------------------------------------------------*/
+
+void KGVDocument::openFile( const QString& name, const QString& mimetype )
+{
+ kdDebug(4500) << "KGVDocument::openFile" << endl;
+
+ close();
+ _fileName = name;
+ _mimetype = mimetype;
+
+ QTimer::singleShot( 0, this, SLOT( doOpenFile() ) );
+}
+
+void KGVDocument::doOpenFile()
+{
+ QFileInfo fileInfo( _fileName );
+ if( !fileInfo.exists() )
+ {
+ KMessageBox::sorry( _part->widget(),
+ i18n( "<qt>Could not open <nobr><strong>%1</strong></nobr>: "
+ "File does not exist.</qt>" )
+ .arg( _fileName ) );
+ emit canceled( QString() );
+ return;
+ }
+ if( !fileInfo.isReadable() )
+ {
+ KMessageBox::sorry( _part->widget(),
+ i18n( "<qt>Could not open <nobr><strong>%1</strong></nobr>: "
+ "Permission denied.</qt>" )
+ .arg( _fileName ) );
+ emit canceled( QString() );
+ return;
+ }
+
+ if( uncompressFile() )
+ {
+ kdDebug( 4500 ) << "FILENAME: " << _fileName << endl;
+ KMimeType::Ptr mimetype = KMimeType::findByPath( _fileName );
+ kdDebug(4500) << "KGVDocument::mimetype: " << mimetype->name()
+ << endl;
+ _mimetype = mimetype->name();
+ }
+
+ // If the file contains a PDF document, create a DSC description file
+ // of the PDF document. This can be passed to Ghostscript just like
+ // an ordinary PS file.
+ if( _mimetype == "application/pdf"
+ || _mimetype == "application/x-pdf" ) // see bug:67474
+ {
+ _tmpDSC = new KTempFile( QString::null, ".ps" );
+ Q_CHECK_PTR( _tmpDSC );
+ if( _tmpDSC->status() != 0 ) {
+ KMessageBox::error( _part->widget(),
+ i18n( "Could not create temporary file: %1" )
+ .arg( strerror( _tmpDSC->status() ) ) );
+ emit canceled( QString() );
+ return;
+ }
+
+ // When pdf2dsc has finished the program will continue with
+ // openPDFFileContinue.
+ _pdf2dsc->run( _fileName, _tmpDSC->name() );
+ return;
+ }
+ else if( _mimetype == "application/postscript"
+ || _mimetype == "application/x-postscript" // see bug:71546
+ || _mimetype == "application/illustrator"
+ || _mimetype == "image/x-eps"
+ || _mimetype == "text/plain" )
+ {
+ _format = PS;
+ openPSFile();
+ return;
+ }
+ else
+ {
+ KMessageBox::sorry( _part->widget(),
+ i18n( "<qt>Could not open <nobr><strong>%1</strong></nobr> "
+ "which has type <strong>%2</strong>. KGhostview can "
+ "only load PostScript (.ps, .eps) and Portable "
+ "Document Format (.pdf) files.</qt>" )
+ .arg( _fileName )
+ .arg( _mimetype ) );
+ emit canceled( QString() );
+ return;
+ }
+}
+
+bool KGVDocument::uncompressFile()
+{
+ // If the file is gzipped, gunzip it to the temporary file _tmpUnzipped.
+ kdDebug(4500) << "KGVDocument::uncompressFile()" << endl;
+
+ auto_ptr<QIODevice> filterDev( KFilterDev::deviceForFile( _fileName, _mimetype, true ) );
+ if ( !filterDev.get() ) {
+ KMimeType::Ptr mt = KMimeType::mimeType(_mimetype);
+ if ( (_fileName.right( 3 ) == ".gz") || mt->is("application/x-gzip") ) {
+ kdDebug(4500) << "KGVDocument::uncompressFile(): manually guessing gzip" << endl;
+ filterDev.reset( KFilterDev::deviceForFile( _fileName, "application/x-gzip", true ) );
+ } else if ( (_fileName.right( 4 ) == ".bz2") || mt->is("application/x-bzip2") ) {
+ kdDebug(4500) << "KGVDocument::uncompressFile(): manually guessing bzip2" << endl;
+ filterDev.reset( KFilterDev::deviceForFile( _fileName, "application/x-bzip2", true ) );
+ } else {
+ kdDebug( 4500 ) << "KGVDocument::uncompressFile(): Unable to guess " << _fileName << endl;
+ }
+ if ( !filterDev.get() )
+ return false;
+ }
+ if( !filterDev->open( IO_ReadOnly ) )
+ {
+ KMessageBox::error( _part->widget(),
+ i18n( "<qt>Could not uncompress <nobr><strong>%1</strong></nobr>.</qt>" )
+ .arg( _fileName ) );
+ emit canceled( QString() );
+ return false;
+ }
+
+ _tmpUnzipped = new KTempFile;
+ Q_CHECK_PTR( _tmpUnzipped );
+ if( _tmpUnzipped->status() != 0 )
+ {
+ KMessageBox::error( _part->widget(),
+ i18n( "Could not create temporary file: %2" )
+ .arg( strerror( _tmpUnzipped->status() ) ) );
+ emit canceled( QString() );
+ return false;
+ }
+
+
+ QByteArray buf( 8192 );
+ int read = 0, wrtn = 0;
+ while( ( read = filterDev->readBlock( buf.data(), buf.size() ) )
+ > 0 )
+ {
+ wrtn = _tmpUnzipped->file()->writeBlock( buf.data(), read );
+ if( read != wrtn )
+ break;
+ }
+
+ if( read != 0 )
+ {
+ KMessageBox::error( _part->widget(),
+ i18n( "<qt>Could not uncompress <nobr><strong>%1</strong></nobr>.</qt>" )
+ .arg( _fileName ) );
+ emit canceled( QString() );
+ return false;
+ }
+
+ _tmpUnzipped->close();
+ _fileName = _tmpUnzipped->name();
+ return true;
+}
+
+void KGVDocument::openPDFFileContinue( bool pdf2dscResult )
+{
+ kdDebug(4500) << "KGVDocument::openPDFFileContinue" << endl;
+
+ if( !pdf2dscResult )
+ {
+ KMessageBox::error( _part->widget(),
+ i18n( "<qt>Could not open file <nobr><strong>%1</strong></nobr>.</qt>" )
+ .arg( _part->url().url() ) );
+ emit canceled( QString() );
+ return;
+ }
+
+ _tmpDSC->close();
+ _format = PDF;
+
+ openPSFile(_tmpDSC->name());
+}
+
+void KGVDocument::openPSFile(const QString &file)
+{
+ QString fileName = file.isEmpty() ? _fileName : file;
+ kdDebug(4500) << "KGVDocument::openPSFile (" << fileName << ")" << endl;
+
+ FILE* fp = fopen( QFile::encodeName( fileName ), "r");
+ if( fp == 0 )
+ {
+ KMessageBox::error( _part->widget(),
+ i18n( "<qt>Error opening file <nobr><strong>%1</strong></nobr>: %2</qt>" )
+ .arg( _part->url().url() )
+ .arg( strerror( errno ) ) );
+ emit canceled( "" );
+ return;
+ }
+ else
+ {
+ _psFile = fp;
+ _isFileOpen = true;
+ scanDSC();
+ emit completed();
+ }
+}
+
+void KGVDocument::fileChanged( const QString& name )
+{
+ kdDebug(4500) << "KGVDocument: fileChanged " << name << endl;
+
+ if( !_psFile )
+ return;
+
+ // unsigned int savepage = _currentPage;
+
+ /*
+ if( openFile( name ) )
+ goToPage( savepage );
+ else
+ emit fileChangeFailed();
+ */
+}
+
+void KGVDocument::scanDSC()
+{
+ _dsc = new KDSC();
+
+ // Disable errorDialog for now while KDSCErrorDialog isn't fully
+ // implemented
+ // KDSCErrorDialog errorDialog;
+ // KDSCErrorThreshold errorHandler( 3, &errorDialog );
+ // _dsc->setErrorHandler( &errorHandler );
+
+ char buf[4096];
+ int count;
+ /*
+ QTime clock;
+ clock.start();
+ */
+ while( ( count = fread( buf, sizeof(char), sizeof( buf ), _psFile ) ) != 0 )
+ {
+ _dsc->scanData( buf, count );
+ /*
+ if( clock.elapsed() > 10 )
+ {
+ kapp->processEvents();
+ clock.start();
+ }
+ */
+ }
+ _dsc->fixup();
+ // _dsc->setErrorHandler( 0 );
+}
+
+void KGVDocument::close()
+{
+ _pdf2dsc->kill();
+ _isFileOpen = false;
+
+ delete _dsc;
+ _dsc = 0;
+
+ if( _psFile )
+ {
+ fclose( _psFile );
+ _psFile = 0;
+ }
+
+ clearTemporaryFiles();
+}
+
+bool KGVDocument::convertFromPDF( const QString& saveFileName,
+ unsigned int firstPage,
+ unsigned int lastPage )
+{
+ // TODO -- timeout/fail on this conversion (it can hang on a bad pdf)
+ // TODO -- use output from gs (leave out -q) to drive a progress bar
+ KProcess process;
+ process << _interpreterPath
+ << "-q"
+ << "-dNOPAUSE"
+ << "-dBATCH"
+ << "-dSAFER"
+ << "-dPARANOIDSAFER"
+ << "-sDEVICE=pswrite"
+ << ( QCString("-sOutputFile=")+QFile::encodeName(saveFileName) )
+ << ( QString("-dFirstPage=")+QString::number( firstPage ) )
+ << ( QString("-dLastPage=")+QString::number( lastPage ) )
+ << "-c"
+ << "save"
+ << "pop"
+ << "-f"
+ << QFile::encodeName(_fileName);
+
+ /*QValueList<QCString> args = process.args();
+ QValueList<QCString>::Iterator it = args.begin();
+ for ( ; it != args.end() ; ++it )
+ kdDebug(4500) << ( *it ) << endl;*/
+
+ if( !process.start( KProcess::Block ) )
+ {
+ kdError() << "convertFromPDF: Couldn't start gs process" << endl;
+ // TODO -- error message (gs not found?)
+ return false;
+ }
+ if ( !process.normalExit() || process.exitStatus() != 0 )
+ {
+ kdError() << "convertFromPDF: normalExit=" << process.normalExit() << " exitStatus=" << process.exitStatus() << endl;
+ // TODO -- error message (can't open, strerr())
+ return false;
+ }
+
+ return true;
+}
+
+void KGVDocument::clearTemporaryFiles()
+{
+ if( _tmpUnzipped ) {
+ _tmpUnzipped->setAutoDelete( true );
+ delete _tmpUnzipped;
+ _tmpUnzipped = 0;
+ }
+ if( _tmpFromPDF ) {
+ _tmpFromPDF->setAutoDelete( true );
+ delete _tmpFromPDF;
+ _tmpFromPDF = 0;
+ }
+ if( _tmpDSC ) {
+ _tmpDSC->setAutoDelete( true );
+ delete _tmpDSC;
+ _tmpDSC = 0;
+ }
+}
+
+
+/*- DOCUMENT --------------------------------------------------------------*/
+
+QStringList KGVDocument::mediaNames() const
+{
+ QStringList names;
+
+ const CDSCMEDIA* m = dsc_known_media;
+ while( m->name ) {
+ names << m->name;
+ m++;
+ }
+
+ if( isOpen() && dsc()->media() ) {
+ for( unsigned int i = 0; i < dsc()->media_count(); i++ ) {
+ if( dsc()->media()[i] && dsc()->media()[i]->name )
+ names << dsc()->media()[i]->name;
+ }
+ }
+
+ return names;
+}
+
+const CDSCMEDIA* KGVDocument::findMediaByName( const QString& mediaName ) const
+{
+ if( !isOpen() )
+ return 0;
+
+ if( dsc()->media() ) {
+ for( unsigned int i = 0; i < dsc()->media_count(); i++ ) {
+ if( dsc()->media()[i] && dsc()->media()[i]->name
+ && qstricmp( mediaName.local8Bit(),
+ dsc()->media()[i]->name ) == 0 ) {
+ return dsc()->media()[i];
+ }
+ }
+ }
+ /* It didn't match %%DocumentPaperSizes: */
+ /* Try our known media */
+ const CDSCMEDIA *m = dsc_known_media;
+ while( m->name ) {
+ if( qstricmp( mediaName.local8Bit(), m->name ) == 0 ) {
+ return m;
+ }
+ m++;
+ }
+
+ return 0;
+}
+
+QSize KGVDocument::computePageSize( const QString& mediaName ) const
+{
+ kdDebug(4500) << "KGVDocument::computePageSize( " << mediaName << " )" << endl;
+
+ if( mediaName == "BoundingBox" ) {
+ if( dsc()->bbox().get() != 0 )
+ return dsc()->bbox()->size();
+ else
+ return QSize( 0, 0 );
+ }
+
+ const CDSCMEDIA* m = findMediaByName( mediaName );
+ Q_ASSERT( m );
+ return QSize( static_cast<int>( m->width ), static_cast<int>( m->height ) );
+}
+
+
+/*- PRINTING and SAVING ---------------------------------------------------*/
+
+QString KGVDocument::pageListToRange( const PageList& pageList )
+{
+ QString range;
+
+ // Iterators marking the begin and end of a successive sequence
+ // of pages.
+ PageList::const_iterator bss( pageList.begin() );
+ PageList::const_iterator ess;
+
+ PageList::const_iterator it ( pageList.begin() );
+
+ while( it != pageList.end() )
+ {
+ ess = it++;
+
+ // If ess points to the end of a successive sequence of pages,
+ // add the stringrepresentation of the sequence to range and
+ // update bss.
+ if( it == pageList.end() || *it != (*ess) + 1 )
+ {
+ if( !range.isEmpty() )
+ range += ",";
+
+ if( bss == ess )
+ range += QString::number( *ess );
+ else
+ range += QString( "%1-%2" ).arg( *bss ).arg( *ess );
+
+ bss = it;
+ }
+ }
+
+ return range;
+}
+
+void KGVDocument::print()
+{
+ if( !dsc() ) return;
+
+ KPrinter printer;
+
+ if( dsc()->isStructured() )
+ {
+ printer.setPageSelection( KPrinter::ApplicationSide );
+
+ printer.setCurrentPage( _part->miniWidget()->displayOptions().page() + 1 );
+ printer.setMinMax( 1, dsc()->page_count() );
+ printer.setOption( "kde-range",
+ pageListToRange( _part->markList()->markList() ) );
+
+ if( printer.setup( _part->widget(), i18n("Print %1").arg(_part->url().fileName()) ) )
+ {
+ KTempFile tf( QString::null, ".ps" );
+ if( tf.status() == 0 )
+ {
+ if ( printer.pageList().empty() ) {
+ KMessageBox::sorry( 0,
+ i18n( "Printing failed because the list of "
+ "pages to be printed was empty." ),
+ i18n( "Error Printing" ) );
+ } else if ( savePages( tf.name(), printer.pageList() ) ) {
+ printer.printFiles( QStringList( tf.name() ), true );
+ } else {
+ KMessageBox::error( 0, i18n( "<qt><strong>Printing failure:</strong><br>Could not convert to PostScript</qt>" ) );
+ }
+ }
+ else
+ {
+ // TODO: Proper error handling
+ ;
+ }
+ }
+ }
+ else
+ {
+ printer.setPageSelection( KPrinter::SystemSide );
+
+ if( printer.setup( _part->widget(), i18n("Print %1").arg(_part->url().fileName()) ) )
+ printer.printFiles( _fileName );
+ }
+}
+
+void KGVDocument::saveAs()
+{
+ if( !isOpen() )
+ return;
+
+ KURL saveURL = KFileDialog::getSaveURL(
+ _part->url().isLocalFile()
+ ? _part->url().url()
+ : _part->url().fileName(),
+ QString::null,
+ _part->widget(),
+ QString::null );
+ if( !KIO::NetAccess::upload( _fileName,
+ saveURL,
+ static_cast<QWidget*>( 0 ) ) ) {
+ // TODO: Proper error dialog
+ }
+}
+
+bool KGVDocument::savePages( const QString& saveFileName,
+ const PageList& pageList )
+{
+ if( pageList.empty() )
+ return true;
+
+ if( _format == PDF )
+ {
+ KTempFile psSaveFile( QString::null, ".ps" );
+ psSaveFile.setAutoDelete( true );
+ if( psSaveFile.status() != 0 )
+ return false;
+
+ // Find the minimum and maximum pagenumber in pageList.
+ int minPage = pageList.first(), maxPage = pageList.first();
+ for( PageList::const_iterator ci = pageList.begin();
+ ci != pageList.end(); ++ci )
+ {
+ minPage = QMIN( *ci, minPage );
+ maxPage = QMAX( *ci, maxPage );
+ }
+
+
+ // TODO: Optimize "print whole document" case
+ //
+ // The convertion below from PDF to PS creates a temporary file which, in
+ // the case where we are printing the whole document will then be
+ // completelly copied to another temporary file.
+ //
+ // In very large files, the inefficiency is visible (measured in
+ // seconds).
+ //
+ // luis_pedro 4 Jun 2003
+
+
+
+ // Convert the pages in the range [minPage,maxPage] from PDF to
+ // PostScript.
+ if( !convertFromPDF( psSaveFile.name(),
+ minPage, maxPage ) )
+ return false;
+
+ // The page minPage in the original file becomes page 1 in the
+ // converted file. We still have to select the desired pages from
+ // this file, so we need to take into account that difference.
+ PageList normedPageList;
+ transform( pageList.begin(), pageList.end(),
+ back_inserter( normedPageList ),
+ bind2nd( minus<int>(), minPage - 1 ) );
+
+ // Finally select the desired pages from the converted file.
+ psCopyDoc( psSaveFile.name(), saveFileName, normedPageList );
+ }
+ else
+ {
+ psCopyDoc( _fileName, saveFileName, pageList );
+ }
+
+ return true;
+}
+
+// length calculates string length at compile time
+// can only be used with character constants
+#define length( a ) ( sizeof( a ) - 1 )
+
+// Copy the headers, marked pages, and trailer to fp
+
+bool KGVDocument::psCopyDoc( const QString& inputFile,
+ const QString& outputFile, const PageList& pageList )
+{
+ FILE* from;
+ FILE* to;
+ char text[ PSLINELENGTH ];
+ char* comment;
+ bool pages_written = false;
+ bool pages_atend = false;
+ unsigned int i = 0;
+ unsigned int pages = 0;
+ long here;
+
+ kdDebug(4500) << "KGVDocument: Copying pages from " << inputFile << " to "
+ << outputFile << endl;
+
+ pages = pageList.size();
+
+ if( pages == 0 ) {
+ KMessageBox::sorry( 0,
+ i18n( "Printing failed because the list of "
+ "pages to be printed was empty." ),
+ i18n( "Error Printing" ) );
+ return false;
+ }
+
+ from = fopen( QFile::encodeName( inputFile ), "r" );
+ to = fopen( QFile::encodeName( outputFile ), "w" );
+
+ // Hack in order to make printing of PDF files work. FIXME
+ CDSC* dsc;
+
+ if( _format == PS )
+ dsc = _dsc->cdsc();
+ else {
+ FILE* fp = fopen( QFile::encodeName( inputFile ), "r");
+ char buf[256];
+ int count;
+ dsc = dsc_init( 0 );
+ while( ( count = fread( buf, 1, sizeof( buf ), fp ) ) != 0 )
+ dsc_scan_data( dsc, buf, count );
+ fclose( fp );
+ if( !dsc )
+ return false;
+
+ dsc_fixup( dsc );
+ }
+
+ here = dsc->begincomments;
+ while( ( comment = pscopyuntil( from, to, here,
+ dsc->endcomments, "%%Pages:" ) ) ) {
+ here = ftell( from );
+ if( pages_written || pages_atend ) {
+ free( comment );
+ continue;
+ }
+ sscanf( comment + length("%%Pages:" ), "%256s", text );
+ text[256] = 0; // Just in case of an overflow
+ if( strcmp( text, "(atend)" ) == 0 ) {
+ fputs( comment, to );
+ pages_atend = true;
+ }
+ else {
+ switch ( sscanf( comment + length( "%%Pages:" ), "%*d %u", &i ) ) {
+ case 1:
+ fprintf( to, "%%%%Pages: %d %d\n", pages, i );
+ break;
+ default:
+ fprintf( to, "%%%%Pages: %d\n", pages );
+ break;
+ }
+ pages_written = true;
+ }
+ free(comment);
+ }
+ pscopy( from, to, dsc->beginpreview, dsc->endpreview );
+ pscopy( from, to, dsc->begindefaults, dsc->enddefaults );
+ pscopy( from, to, dsc->beginprolog, dsc->endprolog );
+ pscopy( from, to, dsc->beginsetup, dsc->endsetup );
+
+ //TODO -- Check that a all dsc attributes are copied
+
+ unsigned int count = 1;
+ PageList::const_iterator it;
+ for( it = pageList.begin(); it != pageList.end(); ++it ) {
+ i = (*it) - 1;
+ comment = pscopyuntil( from, to, dsc->page[i].begin,
+ dsc->page[i].end, "%%Page:" );
+ if ( comment ) free( comment );
+ fprintf( to, "%%%%Page: %s %d\n", dsc->page[i].label,
+ count++ );
+ pscopy( from, to, -1, dsc->page[i].end );
+ }
+
+ here = dsc->begintrailer;
+ while( ( comment = pscopyuntil( from, to, here,
+ dsc->endtrailer, "%%Pages:" ) ) ) {
+ here = ftell( from );
+ if ( pages_written ) {
+ free( comment );
+ continue;
+ }
+ switch ( sscanf( comment + length( "%%Pages:" ), "%*d %u", &i ) ) {
+ case 1:
+ fprintf( to, "%%%%Pages: %d %d\n", pages, i );
+ break;
+ default:
+ fprintf( to, "%%%%Pages: %d\n", pages );
+ break;
+ }
+ pages_written = true;
+ free( comment );
+ }
+
+ fclose( from );
+ fclose( to );
+
+ if( _format == PDF )
+ dsc_free( dsc );
+
+ return true;
+}
+
+#undef length
+
+
+/*- Conversion stuff ------------------------------------------------------*/
+
+/*
+void KGVDocument::runPdf2ps( const QString& pdfName,
+ const QString& dscName )
+{
+ KProcess process;
+ process << _interpreterPath
+ << "-dNODISPLAY"
+ << "-dQUIET"
+ << QString( "-sPDFname=%1" ).arg( pdfName )
+ << QString( "-sDSCnamale locale( "kghostview" );
+ _fallBackPageMedia = pageSizeToString(
+ static_cast< QPrinter::PageSize >( locale.pageSize() ) );
+
+ _usePageLabels = false;
+e=%1" ).arg( dscName )
+ << "pdf2dsc.ps"
+ << "-c"
+ << "quit";
+
+ connect( &process, SIGNAL( processExited( KProcess* ) ),
+ this, SLOT( pdf2psExited( KProcess* ) ) );
+
+ kdDebug(4500) << "KGVDocument: pdf2ps started" << endl;
+ process.start( KProcess::NotifyOnExit );
+}
+
+void KGVDocument::pdf2psExited( KProcess* process )
+{
+ kdDebug(4500) << "KGVDocument: pdf2ps exited" << endl;
+
+ emit pdf2psFinished( process.normalExit() && process.exitStatus() != 0 );
+}
+*/
+
+Pdf2dsc::Pdf2dsc( const QString& ghostscriptPath, QObject* parent, const char* name ) :
+ QObject( parent, name ),
+ _process( 0 ),
+ _ghostscriptPath( ghostscriptPath )
+{}
+
+Pdf2dsc::~Pdf2dsc()
+{
+ kill();
+}
+
+void Pdf2dsc::run( const QString& pdfName, const QString& dscName )
+{
+ kill();
+
+ _process = new KProcess;
+ *_process << _ghostscriptPath
+ << "-dSAFER"
+ << "-dPARANOIDSAFER"
+ << "-dDELAYSAFER"
+ << "-dNODISPLAY"
+ << "-dQUIET"
+ << QString( "-sPDFname=%1" ).arg( pdfName )
+ << QString( "-sDSCname=%1" ).arg( dscName )
+ << "-c"
+ << "<< /PermitFileReading [ PDFname ] /PermitFileWriting [ DSCname ] /PermitFileControl [] >> setuserparams .locksafe"
+ << "-f"
+ << "pdf2dsc.ps"
+ << "-c"
+ << "quit";
+
+ connect( _process, SIGNAL( processExited( KProcess* ) ),
+ this, SLOT( processExited() ) );
+
+ kdDebug(4500) << "Pdf2dsc: started" << endl;
+ _process->start( KProcess::NotifyOnExit );
+}
+
+void Pdf2dsc::kill()
+{
+ if( _process != 0 )
+ {
+ kdDebug(4500) << "Pdf2dsc: killing current process" << endl;
+ delete _process;
+ _process = 0;
+ }
+}
+
+void Pdf2dsc::processExited()
+{
+ kdDebug(4500) << "Pdf2dsc: process exited" << endl;
+
+ emit finished( _process->normalExit() && _process->exitStatus() == 0 );
+ delete _process;
+ _process = 0;
+}
+
+#include "kgvdocument.moc"
+
+
+// vim:sw=4:sts=4:ts=8:sta:tw=78:noet
diff --git a/kghostview/kgvdocument.h b/kghostview/kgvdocument.h
new file mode 100644
index 00000000..960843f8
--- /dev/null
+++ b/kghostview/kgvdocument.h
@@ -0,0 +1,195 @@
+/**
+ * Copyright (C) 1997-2003 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KGV_DOCUMENT_H__
+#define __KGV_DOCUMENT_H__
+
+#include <qprinter.h>
+#include <qsize.h>
+#include <qstring.h>
+
+#include "kgv.h"
+#include "dscparse_adapter.h"
+#include "kgv_view.h"
+
+class KPrinter;
+class KTempFile;
+class Pdf2dsc;
+
+class KGVDocument : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum Format { PS, PDF };
+
+ KGVDocument( KGVPart* part, const char* name = 0 );
+ ~KGVDocument();
+
+ void readSettings();
+
+ /**
+ * Is a file currently open?
+ */
+ bool isOpen() const;
+
+ /**
+ * Open the @em local file @p filename asynchronously.
+ */
+ void openFile( const QString& filename, const QString& mimetype );
+
+ /**
+ * Close the document.
+ */
+ void close();
+
+ const QString& fileName() const { return _fileName; }
+ FILE* psFile() { return _psFile; }
+
+ Format format() const { return _format; }
+
+ /**
+ * @return the document structure for the current document, or 0 if no
+ * file is loaded.
+ */
+ KDSC* const dsc() const;
+
+ /**
+ * A list of page media (sizes).
+ */
+ QStringList mediaNames() const;
+
+ const CDSCMEDIA* findMediaByName( const QString& mediaName ) const;
+
+ QSize computePageSize( const QString& pageMedia ) const;
+
+ static QString pageSizeToString( QPrinter::PageSize );
+
+ /**
+ * Returns a QString which contains a range representation of @p pageList.
+ * Examples: [1,3] -> "1,3"
+ * [1,2,3] -> "1-3"
+ * [1,3,4,5,8] -> "1,3-5,8"
+ */
+ static QString pageListToRange( const KGV::PageList& );
+
+public slots:
+ void fileChanged( const QString& );
+
+ void saveAs();
+ void print();
+
+signals:
+ /**
+ * Emitted if a fileChanged() call fails.
+ */
+ void fileChangeFailed();
+
+ void completed();
+ void canceled( const QString& );
+
+protected:
+ void scanDSC();
+ void clearTemporaryFiles();
+
+ /**
+ * Tries to uncompress the file. Returns true if it uncompressed the file
+ * ( it changes _fileName to point to the uncompressed version of the file ).
+ * Else, it does nothing and returns false.
+ *
+ * What type of files this is able to uncompress will depend on the
+ * kdelibs installed. Generally it will work for .gz and .bz2
+ */
+ bool uncompressFile();
+ void openPSFile(const QString &file=QString::null);
+
+protected:
+ bool savePages( const QString& saveFileName,
+ const KGV::PageList& pageList );
+
+ bool psCopyDoc( const QString& inputFile, const QString& outputFile,
+ const KGV::PageList& pageList );
+
+ bool convertFromPDF( const QString& saveFileName,
+ unsigned int firstPage, unsigned int lastPage );
+
+protected slots:
+ void doOpenFile();
+ void openPDFFileContinue( bool pdf2dscResult );
+
+private:
+ FILE* _psFile;
+
+ QString _fileName;
+ QString _mimetype;
+
+ KGVPart* _part;
+
+ Format _format;
+
+ KTempFile* _tmpUnzipped;
+ KTempFile* _tmpFromPDF;
+ KTempFile* _tmpDSC;
+
+ Pdf2dsc* _pdf2dsc;
+
+ QString _interpreterPath;
+
+ bool _isFileOpen;
+
+ KDSC* _dsc;
+};
+
+
+class Pdf2dsc : public QObject
+{
+ Q_OBJECT
+
+public:
+ Pdf2dsc( const QString& ghostscriptPath, QObject* parent = 0, const char* name = 0 );
+ ~Pdf2dsc();
+
+ void run( const QString& pdfName, const QString& dscName );
+ void kill();
+
+signals:
+ void finished( bool result );
+
+protected slots:
+ void processExited();
+
+private:
+ KProcess* _process;
+ QString _ghostscriptPath;
+};
+
+
+inline KDSC* const KGVDocument::dsc() const
+{
+ return _dsc;
+}
+
+inline bool KGVDocument::isOpen() const
+{
+ return _isFileOpen;
+}
+
+
+#endif
+
+// vim:sw=4:sts=4:ts=8:sta:tw=78:noet
diff --git a/kghostview/kgvfactory.cpp b/kghostview/kgvfactory.cpp
new file mode 100644
index 00000000..edf20144
--- /dev/null
+++ b/kghostview/kgvfactory.cpp
@@ -0,0 +1,105 @@
+/**
+ * Copyright (C) 2003, Lus Pedro Coelho,
+ * based on kdelibs/kparts/genericfactory.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <kparts/factory.h>
+#include <kparts/part.h>
+#include <kgenericfactory.h>
+#include <kaboutdata.h>
+#include <kdebug.h>
+#include "kgv_view.h"
+
+#include "kgvfactory.h"
+
+KGVFactory::KGVFactory()
+{
+ if ( s_self )
+ kdWarning() << "KGVFactory instantiated more than once!" << endl;
+ s_self = this;
+}
+
+KGVFactory::~KGVFactory()
+{
+ delete s_aboutData;
+ delete s_instance;
+ s_aboutData = 0;
+ s_instance = 0;
+ s_self = 0;
+}
+
+KInstance *KGVFactory::createInstance()
+{
+ KInstance* res = new KInstance( aboutData() );
+ return res;
+}
+
+KGVFactory *KGVFactory::s_self;
+KInstance *KGVFactory::s_instance;
+KAboutData *KGVFactory::s_aboutData;
+
+KParts::Part *KGVFactory::createPartObject( QWidget *parentWidget, const char *widgetName,
+ QObject *parent, const char *name,
+ const char *className,
+ const QStringList &args_ )
+{
+ QStringList args = args_;
+ /* Below is the reason why we must
+ * have our own factory instead of
+ * typedef KParts::GenericFactory<KGVPart> KGVFactory
+ *
+ * as we did before.
+ */
+ args << QString::fromLatin1( className );
+ if ( !strcmp( className, "Browser/View" ) ) {
+ className = "KParts::ReadOnlyPart";
+ }
+ KGVPart *part = KDEPrivate::ConcreteFactory<KGVPart>::create( parentWidget,
+ widgetName,
+ parent,
+ name,
+ className,
+ args );
+
+ if ( part && !qstrcmp( className, "KParts::ReadOnlyPart" ) )
+ {
+ KParts::ReadWritePart *rwp = dynamic_cast<KParts::ReadWritePart *>( part );
+ if ( rwp )
+ rwp->setReadWrite( false );
+ }
+ return part;
+}
+
+KInstance *KGVFactory::instance()
+{
+ if ( !s_instance )
+ {
+ if ( s_self )
+ s_instance = s_self->createInstance();
+ else
+ s_instance = new KInstance( aboutData() );
+ }
+ return s_instance;
+}
+
+KAboutData *KGVFactory::aboutData()
+{
+ if ( !s_aboutData )
+ s_aboutData = KGVPart::createAboutData();
+ return s_aboutData;
+}
+
diff --git a/kghostview/kgvfactory.h b/kghostview/kgvfactory.h
new file mode 100644
index 00000000..238e3557
--- /dev/null
+++ b/kghostview/kgvfactory.h
@@ -0,0 +1,50 @@
+/**
+ * Copyright (C) 2003, Lus Pedro Coelho,
+ * based on kdelibs/kparts/genericfactory.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef KGVPart_H_INCLUDE_GUARD_
+#define KGVPart_H_INCLUDE_GUARD_
+
+#include <kparts/factory.h>
+#include <kparts/part.h>
+
+class KInstance;
+class KAboutData;
+
+class KDE_EXPORT KGVFactory : public KParts::Factory
+{
+ public:
+ KGVFactory();
+ virtual ~KGVFactory();
+ static KInstance *instance();
+ static KAboutData *aboutData();
+
+ virtual KParts::Part *createPartObject( QWidget *parentWidget, const char *widgetName,
+ QObject *parent, const char *name,
+ const char *className,
+ const QStringList &args );
+
+ protected:
+ virtual KInstance *createInstance();
+ private:
+ static KGVFactory*s_self;
+ static KInstance *s_instance;
+ static KAboutData *s_aboutData;
+};
+
+#endif
+
diff --git a/kghostview/kgvmainwidget.cpp b/kghostview/kgvmainwidget.cpp
new file mode 100644
index 00000000..2bad7f84
--- /dev/null
+++ b/kghostview/kgvmainwidget.cpp
@@ -0,0 +1,50 @@
+/**
+ * Copyright (C) 2001-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kgvmainwidget.h"
+#include <kurl.h>
+#include <kurldrag.h>
+
+KGVMainWidget::KGVMainWidget( QWidget* parent, const char* name )
+ : QWidget( parent, name ) {}
+
+void KGVMainWidget::keyPressEvent( QKeyEvent* event )
+{
+ if( event->key() == Key_Space && event->state() != ShiftButton ) {
+ event->accept();
+ emit spacePressed();
+ }
+}
+
+void KGVMainWidget::dropEvent( QDropEvent* ev )
+{
+ KURL::List lst;
+ if ( KURLDrag::decode( ev, lst ) ) {
+ emit urlDropped( lst.first() );
+ }
+}
+
+
+void KGVMainWidget::dragEnterEvent( QDragEnterEvent * ev )
+{
+ ev->accept();
+}
+
+#include "kgvmainwidget.moc"
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/kgvmainwidget.h b/kghostview/kgvmainwidget.h
new file mode 100644
index 00000000..0e79fbd6
--- /dev/null
+++ b/kghostview/kgvmainwidget.h
@@ -0,0 +1,45 @@
+/**
+ * Copyright (C) 2001-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KGVMAINWIDGET_H
+#define KGVMAINWIDGET_H
+
+#include <qwidget.h>
+
+class KURL;
+
+class KGVMainWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ KGVMainWidget( QWidget* parent = 0, const char* name = 0 );
+
+signals:
+ void spacePressed();
+ void urlDropped( const KURL& );
+
+protected:
+ virtual void keyPressEvent( QKeyEvent* );
+ virtual void dragEnterEvent( QDragEnterEvent* );
+ virtual void dropEvent( QDropEvent* );
+};
+
+#endif
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/kgvpagedecorator.cpp b/kghostview/kgvpagedecorator.cpp
new file mode 100644
index 00000000..e1eccfc7
--- /dev/null
+++ b/kghostview/kgvpagedecorator.cpp
@@ -0,0 +1,106 @@
+/**
+ * Copyright (C) 2001-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <qbitmap.h>
+#include <qdrawutil.h>
+#include <qpainter.h>
+#include <qregion.h>
+
+#include "kgvpagedecorator.h"
+
+KGVPageDecorator::KGVPageDecorator( QWidget* parent, const char* name ) :
+ QHBox( parent, name ),
+ _margin( 5 ),
+ _borderWidth( 1 ),
+ _shadowOffset( 2, 2 )
+{
+ setFrameStyle( Box | Plain );
+ setLineWidth( _margin + _borderWidth );
+ setBackgroundMode( NoBackground );
+ setAutoMask( true );
+}
+
+bool KGVPageDecorator::eventFilter( QObject* o, QEvent* e )
+{
+ switch( e->type() ) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ return event( e );
+ default:
+ ;
+ }
+ return QHBox::eventFilter( o, e );
+}
+
+void KGVPageDecorator::childEvent( QChildEvent* e )
+{
+ if( e->child()->isWidgetType() && e->inserted() )
+ e->child()->installEventFilter( this );
+}
+
+void KGVPageDecorator::drawFrame( QPainter* p )
+{
+ QRect r( frameRect().topLeft() + QPoint(_margin,_margin),
+ frameRect().bottomRight() - QPoint(_margin,_margin) );
+
+ if( !r.isValid() )
+ return;
+
+ const QColorGroup& cg = colorGroup();
+
+ r.moveCenter( r.center() + _shadowOffset );
+ qDrawPlainRect( p, r, cg.shadow(), _shadowOffset.manhattanLength() );
+
+ r.moveCenter( r.center() - _shadowOffset );
+ qDrawPlainRect( p, r, cg.foreground(), _borderWidth );
+}
+
+void KGVPageDecorator::drawMask( QPainter* p )
+{
+ QRect r( frameRect().topLeft() + QPoint(_margin,_margin),
+ frameRect().bottomRight() - QPoint(_margin,_margin) );
+
+ if( !r.isValid() )
+ return;
+
+ QColorGroup cg( color1, color1, color1, color1, color1, color1, color1,
+ color1, color0 );
+ QBrush brush( cg.foreground() );
+
+ r.moveCenter( r.center() + _shadowOffset );
+ qDrawPlainRect( p, r, cg.foreground(), _shadowOffset.manhattanLength() );
+
+ r.moveCenter( r.center() - _shadowOffset );
+ qDrawPlainRect( p, r, cg.foreground(), _borderWidth, &brush );
+}
+
+void KGVPageDecorator::updateMask()
+{
+ QBitmap bm( size() );
+ bm.fill( color0 );
+ QPainter p( &bm, this );
+ p.setPen( color1 );
+ p.setBrush( color1 );
+ drawMask( &p );
+ p.end();
+ setMask( bm );
+}
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/kgvpagedecorator.h b/kghostview/kgvpagedecorator.h
new file mode 100644
index 00000000..72ceb956
--- /dev/null
+++ b/kghostview/kgvpagedecorator.h
@@ -0,0 +1,81 @@
+/**
+ * Copyright (C) 2001-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KGVPAGEDECORATOR_H
+#define KGVPAGEDECORATOR_H
+
+#include <qhbox.h>
+
+class KGVPageDecorator : public QHBox
+{
+public:
+ KGVPageDecorator( QWidget* parent = 0, const char* name = 0 );
+ ~KGVPageDecorator() { ; }
+
+ unsigned int margin() const;
+ unsigned int borderWidth() const;
+
+ /**
+ * Reimplemented from QObject to let mouse events from child widgets
+ * appear to come from this widget.
+ */
+ bool eventFilter( QObject*, QEvent* );
+
+protected:
+ /**
+ * Reimplemented from QObject to automatically insert an event filter
+ * on child widgets.
+ */
+ virtual void childEvent( QChildEvent* );
+
+ /**
+ * Reimplemented from QFrame to draw a pageshadow like frame.
+ */
+ virtual void drawFrame( QPainter* );
+
+ /**
+ * Draw the mask of both the frame and the contents in order to create a
+ * partially transparent frame.
+ */
+ virtual void drawMask( QPainter* );
+
+ /**
+ * Reimplemented from QWidget. It uses @ref drawMask() to draw the mask
+ * of the frame when transparency is required.
+ */
+ virtual void updateMask();
+
+private:
+ unsigned int _margin;
+ unsigned int _borderWidth;
+ QPoint _shadowOffset;
+};
+
+inline unsigned int KGVPageDecorator::margin() const
+{
+ return _margin;
+}
+
+inline unsigned int KGVPageDecorator::borderWidth() const
+{
+ return _borderWidth;
+}
+
+#endif
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/kgvpageview.cpp b/kghostview/kgvpageview.cpp
new file mode 100644
index 00000000..8a8af585
--- /dev/null
+++ b/kghostview/kgvpageview.cpp
@@ -0,0 +1,254 @@
+/**
+ * Copyright (C) 2001-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <qdatetime.h>
+
+#include <kdebug.h>
+
+#include "kgvpageview.h"
+
+KGVPageView::KGVPageView( QWidget* parent, const char* name )
+ : QScrollView( parent, name )
+{
+ _page = 0;
+
+ setFocusPolicy( QWidget::StrongFocus );
+ viewport()->setFocusPolicy( QWidget::WheelFocus );
+}
+
+void KGVPageView::setPage( QWidget* page )
+{
+ if( page != 0 ) {
+ addChild( page );
+ centerContents();
+ _page = page;
+ }
+}
+
+bool KGVPageView::atTop() const
+{
+ return verticalScrollBar()->value() == verticalScrollBar()->minValue();
+}
+
+bool KGVPageView::atBottom() const
+{
+ return verticalScrollBar()->value() == verticalScrollBar()->maxValue();
+}
+
+bool KGVPageView::eventFilter( QObject* o, QEvent* e )
+{
+ if ( o == _page && e->type() == QEvent::Resize ) {
+ // We need to call QScrollView::eventFilter before centerContents,
+ // otherwise a loop will be introduced.
+ bool result = QScrollView::eventFilter( o, e );
+ centerContents();
+ emit pageSizeChanged( _page->size() );
+ return result;
+ }
+ return QScrollView::eventFilter( o, e );
+}
+
+
+void KGVPageView::wheelEvent( QWheelEvent *e )
+{
+ int delta = e->delta();
+ e->accept();
+ if ((e->state() & ControlButton) == ControlButton) {
+ if ( e->delta() < 0 )
+ emit zoomOut();
+ else
+ emit zoomIn();
+ }
+ else if ( delta <= -120 && atBottom() )
+ {
+ emit ReadDown();
+ }
+ else if ( delta >= 120 && atTop())
+ {
+ emit ReadUp();
+ }
+
+ else
+ QScrollView::wheelEvent( e );
+}
+void KGVPageView::mousePressEvent( QMouseEvent * e )
+{
+ if ( e->button() & LeftButton )
+ {
+ _dragGrabPos = e -> globalPos();
+ setCursor( sizeAllCursor );
+ }
+ else if ( e->button() & MidButton )
+ {
+ emit ReadDown();
+ }
+ else if ( e -> button() & RightButton )
+ {
+ emit rightClick();
+ }
+}
+
+void KGVPageView::mouseReleaseEvent( QMouseEvent *e )
+{
+ if ( e -> button() & LeftButton )
+ {
+ setCursor( arrowCursor );
+ }
+}
+
+void KGVPageView::mouseMoveEvent( QMouseEvent * e )
+{
+ if ( e->state() & LeftButton )
+ {
+ QPoint delta = _dragGrabPos - e->globalPos();
+ scrollBy( delta.x(), delta.y() );
+ _dragGrabPos = e->globalPos();
+ }
+}
+
+bool KGVPageView::readUp()
+{
+ if( atTop() )
+ return false;
+ else {
+ int newValue = QMAX( verticalScrollBar()->value() - height() + 50,
+ verticalScrollBar()->minValue() );
+
+ /*
+ int step = 10;
+ int value = verticalScrollBar()->value();
+ while( value > newValue - step ) {
+ verticalScrollBar()->setValue( value );
+ value -= step;
+ }
+ */
+
+ verticalScrollBar()->setValue( newValue );
+ return true;
+ }
+}
+
+bool KGVPageView::readDown()
+{
+ if( atBottom() )
+ return false;
+ else {
+ int newValue = QMIN( verticalScrollBar()->value() + height() - 50,
+ verticalScrollBar()->maxValue() );
+
+ /*
+ int step = 10;
+ int value = verticalScrollBar()->value();
+ while( value < newValue + step ) {
+ verticalScrollBar()->setValue( value );
+ value += step;
+ }
+ */
+
+ verticalScrollBar()->setValue( newValue );
+ return true;
+ }
+}
+
+void KGVPageView::scrollRight()
+{
+ horizontalScrollBar()->addLine();
+}
+
+void KGVPageView::scrollLeft()
+{
+ horizontalScrollBar()->subtractLine();
+}
+
+void KGVPageView::scrollDown()
+{
+ verticalScrollBar()->addLine();
+}
+
+void KGVPageView::scrollUp()
+{
+ verticalScrollBar()->subtractLine();
+}
+
+void KGVPageView::scrollBottom()
+{
+ verticalScrollBar()->setValue( verticalScrollBar()->maxValue() );
+}
+
+void KGVPageView::scrollTop()
+{
+ verticalScrollBar()->setValue( verticalScrollBar()->minValue() );
+}
+
+void KGVPageView::enableScrollBars( bool b )
+{
+ setHScrollBarMode( b ? Auto : AlwaysOff );
+ setVScrollBarMode( b ? Auto : AlwaysOff );
+}
+
+void KGVPageView::keyPressEvent( QKeyEvent* e )
+{
+ switch ( e->key() ) {
+ case Key_Up:
+ scrollUp();
+ break;
+ case Key_Down:
+ scrollDown();
+ break;
+ case Key_Left:
+ scrollLeft();
+ break;
+ case Key_Right:
+ scrollRight();
+ break;
+ default:
+ e->ignore();
+ return;
+ }
+ e->accept();
+}
+
+void KGVPageView::viewportResizeEvent( QResizeEvent* e )
+{
+ QScrollView::viewportResizeEvent( e );
+ emit viewSizeChanged( viewport()->size() );
+ centerContents();
+}
+
+void KGVPageView::centerContents()
+{
+ if( !_page )
+ return;
+
+ int newX = 0;
+ int newY = 0;
+
+ QSize newViewportSize = viewportSize( _page->width(),
+ _page->height() );
+
+ if( newViewportSize.width() > _page->width() )
+ newX = ( newViewportSize.width() - _page->width() )/2;
+ if( newViewportSize.height() > _page->height() )
+ newY = ( newViewportSize.height() - _page->height() )/2;
+
+ moveChild( _page, newX, newY );
+}
+
+#include "kgvpageview.moc"
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/kgvpageview.h b/kghostview/kgvpageview.h
new file mode 100644
index 00000000..9a1abac3
--- /dev/null
+++ b/kghostview/kgvpageview.h
@@ -0,0 +1,105 @@
+/**
+ * Copyright (C) 2001-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#ifndef KGVPAGEVIEW_H
+#define KGVPAGEVIEW_H
+
+#include <qscrollview.h>
+
+/**
+ * KGVPageView is a customized QScrollView, which can hold one page. This page
+ * will be centered on the viewport. Furthermore it adds the ability to scroll
+ * the page by dragging it using the mouse.
+ */
+class KGVPageView : public QScrollView
+{
+ Q_OBJECT
+
+public:
+ KGVPageView( QWidget* parent = 0, const char* name = 0 );
+ ~KGVPageView() { ; }
+
+ void setPage( QWidget* );
+ QWidget* page() const { return _page; }
+
+ /**
+ * Return true if the top resp. bottom of the page is visible.
+ */
+ bool atTop() const;
+ bool atBottom() const;
+
+ /**
+ * Turn the scrollbars on/off.
+ */
+ void enableScrollBars( bool);
+
+ /**
+ * @reimplemented
+ */
+ bool eventFilter( QObject*, QEvent* );
+
+public slots:
+ bool readUp();
+ bool readDown();
+ void scrollUp();
+ void scrollDown();
+ void scrollRight();
+ void scrollLeft();
+ void scrollBottom();
+ void scrollTop();
+
+signals:
+ void viewSizeChanged( const QSize& );
+ void pageSizeChanged( const QSize& );
+ void nextPage();
+ void zoomOut();
+ void zoomIn();
+ void prevPage();
+ void rightClick();
+ void ReadUp();
+ void ReadDown();
+
+protected:
+ virtual void keyPressEvent( QKeyEvent* );
+
+ /**
+ * Reimplemented to from QScrollView to make sure that the page is centered
+ * when it fits in the viewport.
+ */
+ virtual void viewportResizeEvent( QResizeEvent* );
+
+ virtual void mousePressEvent( QMouseEvent *e );
+ virtual void mouseReleaseEvent( QMouseEvent *e );
+ virtual void mouseMoveEvent( QMouseEvent *e );
+ virtual void wheelEvent( QWheelEvent * );
+
+ /**
+ * If the viewport is larger than the page, center the page on the
+ * viewport.
+ */
+ void centerContents();
+
+private:
+ QPoint _dragGrabPos;
+ QWidget* _page;
+};
+
+#endif
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/kgvshell.cpp b/kghostview/kgvshell.cpp
new file mode 100644
index 00000000..1dc546d5
--- /dev/null
+++ b/kghostview/kgvshell.cpp
@@ -0,0 +1,370 @@
+/**
+ * Copyright (C) 2000-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <kaction.h>
+#include <kapplication.h>
+#include <kfiledialog.h>
+#include <kiconloader.h>
+#include <klibloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kstatusbar.h>
+#include <kstdaction.h>
+#include <kstdaccel.h>
+#include <ktempfile.h>
+#include <kmenubar.h>
+#include <kedittoolbar.h>
+#include <kdebug.h>
+
+#include <kicontheme.h>
+#include <kglobal.h>
+#include <kpopupmenu.h>
+#include <kparts/componentfactory.h>
+
+#include <kwin.h>
+
+#include <qcursor.h>
+
+#include "kgv_miniwidget.h"
+#include "kgv_view.h"
+#include "kgvpageview.h"
+#include "displayoptions.h"
+#include "fullscreenfilter.h"
+
+#undef Always // avoid X11/Qt namespace clash
+#include "kgvshell.moc"
+
+//TODO -- disable GUI when no file
+//TODO -- don't stay open when no file, go directly to KFileDialog
+
+KGVShell::KGVShell() :
+ _tmpFile( 0 )
+{
+ m_gvpart = KParts::ComponentFactory::createPartInstanceFromLibrary< KGVPart >( "libkghostviewpart", this, "kgvpart",
+ this, "kgvpart" );
+
+ /*---- File -----------------------------------------------------------*/
+ openact =
+ KStdAction::open( this, SLOT( slotFileOpen() ),
+ actionCollection() );
+ recent =
+ KStdAction::openRecent( this, SLOT( openURL( const KURL& ) ),
+ actionCollection() );
+ KStdAction::print( m_gvpart->document(), SLOT( print() ),
+ actionCollection() );
+ (void)
+ KStdAction::quit( this, SLOT( slotQuit() ), actionCollection() );
+
+ /*---- View -----------------------------------------------------------*/
+ new KAction( i18n( "&Reload" ), "reload",
+ KStdAccel::shortcut( KStdAccel::Reload ),
+ m_gvpart, SLOT( reloadFile() ),
+ actionCollection(), "reload" );
+ new KAction( i18n( "&Maximize" ), Key_M, this,
+ SLOT( slotMaximize() ), actionCollection(),
+ "maximize");
+ _showMenuBarAction = KStdAction::showMenubar( this, SLOT( slotShowMenubar() ), actionCollection() );
+
+ /*---- Settings -------------------------------------------------------*/
+#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,90)
+ createStandardStatusBarAction();
+#endif
+ setAutoSaveSettings();
+ setStandardToolBarMenuEnabled(true);
+#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,90)
+ m_fullScreenAction = KStdAction::fullScreen( this, SLOT( slotUpdateFullScreen() ), actionCollection(), this );
+#else
+ m_fullScreenAction = new KToggleAction( this, SLOT( slotUpdateFullScreen() ) );
+#endif
+ KStdAction::configureToolbars( this, SLOT( slotConfigureToolbars() ), actionCollection() );
+ KStdAction::keyBindings(guiFactory(), SLOT(configureShortcuts()),
+actionCollection());
+
+ //_popup = new KPopupMenu( i18n( "Full Screen Options" ), this, "rmb popup" );
+ _popup = new KPopupMenu( this, "rmb popup" );
+ _popup->insertTitle( i18n( "Full Screen Options" ) );
+ m_fullScreenAction->plug( _popup );
+ _showMenuBarAction->plug( _popup );
+
+ m_fsFilter = new FullScreenFilter( *this );
+
+ // Just save them automatically is destructor. (TODO: of kgv_view!)
+ //KStdAction::saveOptions ( this, SLOT (slotWriteSettings()), actionCollection());
+
+ setXMLFile( "kghostviewui.rc" );
+
+ // We could, at the user's option, make this connection and kghostview
+ // will always resize to fit the width of the page. But, for now,
+ // let's not.
+ // connect ( m_gvpart->widget(), SIGNAL (sizeHintChanged()), this, SLOT (slotResize ()) );
+
+ setCentralWidget( m_gvpart->widget() );
+ createGUI( m_gvpart );
+
+ connect( m_gvpart->pageView(), SIGNAL( rightClick() ),SLOT( slotRMBClick() ) );
+ connect( m_gvpart, SIGNAL( canceled(const QString&) ),SLOT( slotReset() ) );
+ connect( m_gvpart, SIGNAL( completed() ), SLOT( slotDocumentState() ) );
+
+ if (!initialGeometrySet())
+ resize(640,400);
+
+ readSettings();
+ stateChanged( "initState" );
+
+ // Make sure the view has the keyboard focus.
+ m_gvpart->widget()->setFocus();
+}
+
+KGVShell::~KGVShell()
+{
+ writeSettings();
+
+ if( _tmpFile )
+ {
+ _tmpFile->setAutoDelete( true );
+ delete _tmpFile;
+ _tmpFile = 0;
+ }
+
+ // delete m_gvpart;
+}
+
+void
+KGVShell::slotQuit()
+{
+ kapp->closeAllWindows();
+}
+
+void KGVShell::slotShowMenubar()
+{
+ if ( _showMenuBarAction->isChecked() ) menuBar()->show();
+ else menuBar()->hide();
+}
+
+void
+KGVShell::setDisplayOptions( const DisplayOptions& options )
+{
+ m_gvpart->setDisplayOptions( options );
+}
+
+void KGVShell::slotReset()
+{
+ kdDebug( 4500 ) << "KGVShell::slotReset()" << endl;
+ stateChanged( "initState" );
+}
+
+void
+KGVShell::readProperties( KConfig *config )
+{
+ KURL url = KURL::fromPathOrURL( config->readPathEntry( "URL" ) );
+ if ( url.isValid() ) {
+ openURL( url );
+ DisplayOptions options;
+ if ( DisplayOptions::fromString( options, config->readEntry( "Display Options" ) ) ) m_gvpart->setDisplayOptions( options );
+ }
+}
+
+void
+KGVShell::saveProperties( KConfig* config )
+{
+ config->writePathEntry( "URL", m_gvpart->url().prettyURL() );
+ config->writeEntry( "Display Options", DisplayOptions::toString( m_gvpart->miniWidget()->displayOptions() ) );
+}
+
+void
+KGVShell::readSettings()
+{
+ recent->loadEntries( KGlobal::config() );
+ QStringList items = recent->items();
+
+// Code copied from kviewshell.cpp:
+// Constant source of annoyance in KDVI < 1.0: the 'recent-files'
+// menu contains lots of files which don't exist (any longer). Thus,
+// we'll sort out the non-existent files here.
+
+ for ( QStringList::Iterator it = items.begin(); it != items.end(); ++it ) {
+ KURL url(*it);
+ if (url.isLocalFile()) {
+ QFileInfo info(url.path());
+ if (!info.exists())
+ recent->removeURL(url);
+ }
+ }
+
+ applyMainWindowSettings(KGlobal::config(), "MainWindow");
+
+ KGlobal::config()->setDesktopGroup();
+ bool fullScreen = KGlobal::config()->readBoolEntry( "FullScreen", false );
+ setFullScreen( fullScreen );
+ _showMenuBarAction->setChecked( menuBar()->isVisible() );
+}
+
+void
+KGVShell::writeSettings()
+{
+ saveMainWindowSettings(KGlobal::config(), "MainWindow");
+
+ recent->saveEntries( KGlobal::config() );
+
+ KGlobal::config()->setDesktopGroup();
+ KGlobal::config()->writeEntry( "FullScreen", m_fullScreenAction->isChecked());
+
+ KGlobal::config()->sync();
+}
+
+void
+KGVShell::openURL( const KURL & url )
+{
+ if( m_gvpart->openURL( url ) ) recent->addURL (url);
+}
+
+void
+KGVShell::openStdin()
+{
+ if( _tmpFile )
+ {
+ _tmpFile->setAutoDelete( true );
+ delete _tmpFile;
+ }
+
+ _tmpFile = new KTempFile;
+ _tmpFile->setAutoDelete( true );
+
+ if( _tmpFile->status() != 0 ) {
+ KMessageBox::error( this,
+ i18n( "Could not create temporary file: %1" )
+ .arg( strerror( _tmpFile->status() ) ) );
+ return;
+ }
+
+ QByteArray buf( BUFSIZ );
+ int read = 0, wrtn = 0;
+ while( ( read = fread( buf.data(), sizeof(char), buf.size(), stdin ) )
+ > 0 ) {
+ wrtn = _tmpFile->file()->writeBlock( buf.data(), read );
+ if( read != wrtn )
+ break;
+ kapp->processEvents();
+ }
+
+ if( read != 0 ) {
+ KMessageBox::error( this,
+ i18n( "Could not open standard input stream: %1" )
+ .arg( strerror( errno ) ) );
+ return;
+ }
+
+ _tmpFile->close();
+
+ if( m_gvpart->openURL( KURL::fromPathOrURL( _tmpFile->name() ) ) ) setCaption( "stdin" );
+}
+
+void KGVShell::slotFileOpen()
+{
+ KURL url = KFileDialog::getOpenURL( cwd, i18n(
+ "*.ps *.ps.bz2 *.ps.gz *.eps *.eps.gz *.pdf|All Document Files\n"
+ "*.ps *.ps.bz2 *.ps.gz|PostScript Files\n"
+ "*.pdf *.pdf.gz *.pdf.bz2|Portable Document Format (PDF) Files\n"
+ "*.eps *.eps.gz *.eps.bz2|Encapsulated PostScript Files\n"
+ "*|All Files" ) );
+
+ if( !url.isEmpty() ) {
+ openURL( url );
+ }
+}
+
+void KGVShell::slotDocumentState()
+{
+ stateChanged( "documentState" );
+}
+
+
+void KGVShell::slotMaximize()
+{
+ kdDebug(4500) << "KGVShell::slotMaximize()" << endl;
+ KWin::setState( winId(), NET::MaxHoriz | NET::MaxVert );
+ // If we do it now, it comes to nothing since it would work
+ // on the current (non-maximized) size
+ QTimer::singleShot( 800, m_gvpart, SLOT( slotFitToPage() ) );
+}
+
+void KGVShell::slotResize()
+{
+ resize( m_gvpart->pageView()->sizeHint().width(), height() );
+}
+
+void KGVShell::setFullScreen( bool useFullScreen )
+{
+ if( useFullScreen )
+ showFullScreen();
+ else if( isFullScreen())
+ showNormal();
+}
+
+void KGVShell::slotUpdateFullScreen()
+{
+ if( m_fullScreenAction->isChecked())
+ {
+ menuBar()->hide();
+ statusBar()->hide();
+ toolBar()->hide();
+ m_gvpart->updateFullScreen( true );
+ showFullScreen();
+ kapp->installEventFilter( m_fsFilter );
+ if ( m_gvpart->document()->isOpen() )
+ m_gvpart->slotFitToPage();
+ }
+ else
+ {
+ kapp->removeEventFilter( m_fsFilter );
+ m_gvpart->updateFullScreen( false );
+ menuBar()->show();
+#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,90)
+ KToggleAction *statusbarAction = dynamic_cast<KToggleAction *>(actionCollection()->action(KStdAction::name(KStdAction::ShowStatusbar)));
+ assert( statusbarAction );
+ if (statusbarAction->isChecked()) statusBar()->show();
+#endif
+ toolBar()->show();
+ showNormal();
+ }
+}
+
+void KGVShell::slotConfigureToolbars()
+{
+ saveMainWindowSettings( KGlobal::config(), "MainWindow" );
+ KEditToolbar dlg( factory() );
+ connect(&dlg,SIGNAL(newToolbarConfig()),this,SLOT(slotNewToolbarConfig()));
+ dlg.exec();
+}
+
+void KGVShell::slotNewToolbarConfig()
+{
+ applyMainWindowSettings( KGlobal::config(), "MainWindow" );
+}
+
+void KGVShell::slotRMBClick()
+{
+ _popup->exec( QCursor::pos() );
+}
+
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/kgvshell.h b/kghostview/kgvshell.h
new file mode 100644
index 00000000..872430c1
--- /dev/null
+++ b/kghostview/kgvshell.h
@@ -0,0 +1,92 @@
+/**
+ * Copyright (C) 2000-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KGVSHELL_H__
+#define __KGVSHELL_H__
+
+#include <qstring.h>
+
+#include <kparts/mainwindow.h>
+
+class QTimer;
+
+class KRecentFilesAction;
+class ScrollBox;
+class KGVPart;
+class KAction;
+class KConfig;
+class KTempFile;
+class KPopupMenu;
+class DisplayOptions;
+class FullScreenFilter;
+
+class KDE_EXPORT KGVShell : public KParts::MainWindow
+{
+ Q_OBJECT
+
+public:
+ KGVShell();
+ virtual ~KGVShell();
+
+public slots:
+ void openURL( const KURL& url );
+ void openStdin();
+ void setDisplayOptions( const DisplayOptions& );
+ void slotRMBClick();
+
+protected slots:
+ void slotFileOpen();
+ void slotShowMenubar();
+ void slotQuit();
+ void slotMaximize();
+ void slotResize();
+ void slotUpdateFullScreen();
+ void slotReset();
+ void slotDocumentState();
+ void slotConfigureToolbars();
+ void slotNewToolbarConfig();
+
+protected:
+ // session management
+ virtual void saveProperties( KConfig *config );
+ virtual void readProperties( KConfig *config );
+
+ void readSettings();
+ void writeSettings();
+ void enableStateDepActions( bool enable );
+ void setFullScreen( bool );
+
+private:
+
+ friend class FullScreenFilter;
+
+ KGVPart* m_gvpart;
+ QString cwd;
+
+ KAction* openact;
+ KToggleAction* _showMenuBarAction;
+ KToggleAction* m_fullScreenAction;
+ FullScreenFilter* m_fsFilter;
+ KPopupMenu* _popup;
+ KRecentFilesAction* recent;
+ KTempFile* _tmpFile; // Used for storing data received from stdin
+};
+
+#endif
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/kpswidget.cpp b/kghostview/kpswidget.cpp
new file mode 100644
index 00000000..b51cda5e
--- /dev/null
+++ b/kghostview/kpswidget.cpp
@@ -0,0 +1,530 @@
+/**
+ * Copyright (C) 2000-2003 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kpswidget.h"
+
+#include <stdlib.h>
+#include <math.h>
+
+#include <qstringlist.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+
+#include "configuration.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+int handler( Display* d, XErrorEvent* e )
+{
+ char msg[80], req[80], number[80];
+
+ XGetErrorText( d, e->error_code, msg, sizeof( msg ) );
+ sprintf( number, "%d", e->request_code );
+ XGetErrorDatabaseText( d, "XRequest", number, "<unknown>",
+ req, sizeof( req ) );
+ return 0;
+}
+
+int orientation2angle( CDSC_ORIENTATION_ENUM orientation )
+{
+ Q_ASSERT( orientation != CDSC_ORIENT_UNKNOWN );
+
+ int angle = 0;
+
+ switch( orientation )
+ {
+ case CDSC_ORIENT_UNKNOWN: break; // Catched by Q_ASSERT
+ case CDSC_PORTRAIT: angle = 0; break;
+ case CDSC_LANDSCAPE: angle = 90; break;
+ case CDSC_UPSIDEDOWN: angle = 180; break;
+ case CDSC_SEASCAPE: angle = 270; break;
+ }
+
+ return angle;
+}
+
+QCString palette2String( Configuration::EnumPalette::type palette )
+{
+ QCString str;
+
+ switch( palette )
+ {
+ case Configuration::EnumPalette::Color: str = "Color"; break;
+ case Configuration::EnumPalette::Grayscale: str = "Grayscale"; break;
+ case Configuration::EnumPalette::Monochrome: str = "Monochrome"; break;
+ default: kdWarning( 4500 ) << "palette2String(): unkown palette" << endl;
+ str = "Color";
+ }
+
+ return str;
+}
+
+
+KPSWidget::KPSWidget( QWidget* parent, const char* name ) :
+ QWidget ( parent, name ),
+ _gsWindow ( None ),
+ _usePipe ( false ),
+ _doubleBuffer ( false ),
+ _ghostscriptDirty ( false ),
+ _orientation ( CDSC_PORTRAIT ),
+ _magnification ( 1 ),
+ _palette ( Configuration::EnumPalette::Color ),
+ _widgetDirty ( true ),
+ _process ( 0 ),
+ _buffer ( 0 ),
+ _stdinReady ( false ),
+ _interpreterBusy ( false ),
+ _interpreterReady ( false )
+{
+ XSetErrorHandler( handler );
+
+ // Create the Atoms used to communicate with Ghostscript.
+ const char* const atomNames[] = { "GHOSTVIEW", "GHOSTVIEW_COLORS",
+ "NEXT", "PAGE", "DONE" };
+ XInternAtoms( x11Display(), const_cast<char**>( atomNames ),
+ 5, false, _atoms );
+
+ // readSettings() TODO
+}
+
+KPSWidget::~KPSWidget()
+{
+ if ( _buffer ) operator delete( _buffer );
+ stopInterpreter();
+}
+
+bool KPSWidget::isInterpreterReady() const
+{
+ return isInterpreterRunning() && _interpreterReady;
+}
+
+bool KPSWidget::isInterpreterBusy() const
+{
+ return _interpreterBusy;
+}
+
+bool KPSWidget::isInterpreterRunning() const
+{
+ return ( _process && _process->isRunning() );
+}
+
+bool KPSWidget::nextPage()
+{
+ if( !isInterpreterReady() )
+ return false;
+
+ if( _gsWindow == None ) {
+ kdDebug(4500) << "communication window unknown!" << endl;
+ return false;
+ }
+
+ _interpreterReady = false;
+ _interpreterBusy = true;
+ setCursor( waitCursor );
+
+ XEvent e;
+ e.xclient.type = ClientMessage;
+ e.xclient.display = x11Display();
+ e.xclient.window = _gsWindow;
+ e.xclient.message_type = _atoms[NEXT];
+ e.xclient.format = 32;
+
+ XSendEvent( x11Display(), _gsWindow, false, 0, &e );
+ XFlush( x11Display() );
+ return true;
+}
+
+
+void KPSWidget::clear()
+{
+ //_backgroundPixmap.fill();
+}
+
+
+bool KPSWidget::sendPS( FILE* fp, unsigned int begin, unsigned int end )
+{
+ kdDebug(4500) << "KPSWidget::sendPS" << endl;
+
+ if( !isInterpreterRunning() )
+ return false;
+
+ // Create a new record to add to the queue.
+ _inputQueue.push( Record( fp, begin, end - begin ) );
+
+ // Start processing the queue.
+ if( _stdinReady )
+ gs_input(_process);
+
+ return true;
+}
+
+void KPSWidget::setGhostscriptPath( const QString& path )
+{
+ kdDebug() << "KPSWidget::setGhostscriptPath( " << path << " )" << endl;
+ if( _ghostscriptPath != path )
+ {
+ _ghostscriptPath = path;
+ stopInterpreter();
+ _ghostscriptDirty = true;
+ }
+}
+
+void KPSWidget::setGhostscriptArguments( const QStringList& arguments )
+{
+ if( _ghostscriptArguments != arguments )
+ {
+ _ghostscriptArguments = arguments;
+ stopInterpreter();
+ _ghostscriptDirty = true;
+ }
+}
+
+void KPSWidget::setFileName( const QString& fileName, bool usePipe )
+{
+ if(( _fileName != fileName ) || (_usePipe != usePipe))
+ {
+ _usePipe = usePipe;
+ _fileName = fileName;
+ stopInterpreter();
+ _ghostscriptDirty = true;
+ }
+}
+
+void KPSWidget::setOrientation( CDSC_ORIENTATION_ENUM orientation )
+{
+ if( _orientation != orientation )
+ {
+ _orientation = orientation;
+ stopInterpreter();
+ _widgetDirty = true;
+ }
+}
+
+void KPSWidget::setBoundingBox( const KDSCBBOX& boundingBox )
+{
+ if( _boundingBox != boundingBox )
+ {
+ _boundingBox = boundingBox;
+ stopInterpreter();
+ _widgetDirty = true;
+ }
+}
+
+void KPSWidget::setMagnification( double magnification )
+{
+ if( kAbs( magnification - _magnification ) > 0.0001 )
+ {
+ _magnification = magnification;
+ stopInterpreter();
+ _widgetDirty = true;
+ }
+}
+
+void KPSWidget::setPalette( Configuration::EnumPalette::type palette )
+{
+ if( _palette != palette )
+ {
+ _palette = palette;
+ stopInterpreter();
+ _widgetDirty = true;
+ }
+}
+
+void KPSWidget::setDoubleBuffering( bool db )
+{
+ if( _doubleBuffer != db )
+ {
+ _doubleBuffer = db;
+ stopInterpreter();
+ _widgetDirty = true;
+ }
+}
+
+namespace {
+ /* Rounding up is better than normal rounding because it is better to have a pixel too many than one too little.
+ * If we have one too many, no one will notice. If we have one too little, gs complains.
+ *
+ * I have a file which isn't displayed (gs error) without this fix.
+ */
+ inline int round_up( double x )
+ {
+ return static_cast<int>( ceil( x ) );
+ }
+}
+
+void KPSWidget::setupWidget()
+{
+ if( !_widgetDirty )
+ return;
+
+ Q_ASSERT( orientation() != CDSC_ORIENT_UNKNOWN );
+
+ const float dpiX = _magnification * x11AppDpiX();
+ const float dpiY = _magnification * x11AppDpiY();
+
+ int newWidth = 0, newHeight = 0;
+ if( orientation() == CDSC_PORTRAIT || orientation() == CDSC_UPSIDEDOWN )
+ {
+ newWidth = round_up( boundingBox().width() * dpiX / 72.0 );
+ newHeight = round_up( boundingBox().height() * dpiY / 72.0 );
+ }
+ else
+ {
+ newWidth = round_up( boundingBox().height() * dpiX / 72.0 );
+ newHeight = round_up( boundingBox().width() * dpiY / 72.0 );
+ }
+
+ if( newWidth != width() || newHeight != height() )
+ {
+ setEraseColor( white );
+ setFixedSize( newWidth, newHeight );
+ kapp->processEvents();
+
+ _backgroundPixmap.resize( size() );
+ _backgroundPixmap.fill( white );
+ // The line below is needed to work around certain "features" of styles such as liquid
+ // see bug:61711 for more info (LPC, 20 Aug '03)
+ setBackgroundOrigin( QWidget::WidgetOrigin );
+ setErasePixmap( _backgroundPixmap );
+ }
+
+ char data[512];
+
+ sprintf( data, "%ld %d %d %d %d %d %g %g",
+ ( _doubleBuffer ? 0 : _backgroundPixmap.handle() ),
+ orientation2angle( orientation() ),
+ boundingBox().llx(), boundingBox().lly(),
+ boundingBox().urx(), boundingBox().ury(),
+ dpiX, dpiY );
+ XChangeProperty( x11Display(), winId(),
+ _atoms[GHOSTVIEW],
+ XA_STRING, 8, PropModeReplace,
+ (unsigned char*) data, strlen( data ) );
+
+ sprintf( data, "%s %d %d",
+ palette2String( _palette ).data(),
+ (int)BlackPixel( x11Display(), DefaultScreen( x11Display() ) ),
+ (int)WhitePixel( x11Display(), DefaultScreen( x11Display() ) ) );
+ XChangeProperty( x11Display(), winId(),
+ _atoms[GHOSTVIEW_COLORS],
+ XA_STRING, 8, PropModeReplace,
+ (unsigned char*) data, strlen( data ) );
+
+ // Make sure the properties are updated immediately.
+ XSync( x11Display(), false );
+
+ repaint();
+
+ _widgetDirty = false;
+}
+
+bool KPSWidget::startInterpreter()
+{
+ setupWidget();
+
+ _process = new KProcess;
+ if ( _doubleBuffer ) _process->setEnvironment( "GHOSTVIEW", QString( "%1 %2" ).arg( winId() ).arg( _backgroundPixmap.handle() ) );
+ else _process->setEnvironment( "GHOSTVIEW", QString::number( winId() ) );
+
+ *_process << _ghostscriptPath.local8Bit();
+ *_process << _ghostscriptArguments;
+
+ if( _usePipe )
+ *_process <<
+ // The following two lines are their to ensure that we are allowed to read _fileName
+ "-dDELAYSAFER" << "-sInputFile="+_fileName << "-c" <<
+ "<< /PermitFileReading [ InputFile ] /PermitFileWriting [] /PermitFileControl [] >> setuserparams .locksafe" <<
+ "-";
+ else
+ *_process << _fileName << "-c" << "quit";
+
+ connect( _process, SIGNAL( processExited( KProcess* ) ),
+ this, SLOT( slotProcessExited( KProcess* ) ) );
+ connect( _process, SIGNAL( receivedStdout( KProcess*, char*, int ) ),
+ this, SLOT( gs_output( KProcess*, char*, int ) ) );
+ connect( _process, SIGNAL( receivedStderr( KProcess*, char*, int ) ),
+ this, SLOT( gs_output( KProcess*, char*, int ) ) );
+ connect( _process, SIGNAL( wroteStdin( KProcess*) ),
+ this, SLOT( gs_input( KProcess* ) ) );
+
+ kapp->flushX();
+
+ // Finally fire up the interpreter.
+ kdDebug(4500) << "KPSWidget: starting interpreter" << endl;
+ if( _process->start( KProcess::NotifyOnExit,
+ _usePipe ? KProcess::All : KProcess::AllOutput ) )
+ {
+ _interpreterBusy = true;
+ setCursor( waitCursor );
+
+ _stdinReady = true;
+ _interpreterReady = false;
+ _ghostscriptDirty = false;
+
+ return true;
+ }
+ else
+ {
+ KMessageBox::error( this,
+ i18n( "Could not start Ghostscript. This is most likely "
+ "caused by an incorrectly specified interpreter." ) );
+ return false;
+ }
+}
+
+void KPSWidget::stopInterpreter()
+{
+ kdDebug(4500) << "KPSWidget::stopInterpreter()" << endl;
+ // if( !_interpreterBusy ) return;
+
+ if( isInterpreterRunning() )
+ _process->kill( SIGHUP );
+
+ _process = 0;
+ while ( !_inputQueue.empty() ) _inputQueue.pop();
+
+ _interpreterBusy = false;
+ unsetCursor();
+}
+
+void KPSWidget::interpreterFailed()
+{
+ stopInterpreter();
+}
+
+void KPSWidget::slotProcessExited( KProcess* process )
+{
+ kdDebug(4500) << "KPSWidget: process exited" << endl;
+
+ if ( process == _process )
+ {
+ kdDebug( 4500 ) << "KPSWidget::slotProcessExited(): looks like it was not a clean exit." << endl;
+ if ( process->normalExit() ) {
+ emit ghostscriptError( QString( i18n( "Exited with error code %1." ).arg( process->exitStatus() ) ) );
+ } else {
+ emit ghostscriptError( QString( i18n( "Process killed or crashed." ) ) );
+ }
+ _process = 0;
+ stopInterpreter();
+ unsetCursor();
+ }
+}
+
+void KPSWidget::gs_output( KProcess*, char* buffer, int len )
+{
+ emit output( buffer, len );
+}
+
+void KPSWidget::gs_input( KProcess* process )
+{
+ kdDebug(4500) << "KPSWidget::gs_input" << endl;
+
+ if (process != _process)
+ {
+ kdDebug(4500) << "KPSWidget::gs_input(): process != _process" << endl;
+ return;
+ }
+ _stdinReady = true;
+
+ while( ! _inputQueue.empty() && _inputQueue.front().len == 0 ) _inputQueue.pop();
+ if( _inputQueue.empty() ) {
+ _interpreterReady = true;
+ return;
+ }
+
+ Record& current = _inputQueue.front();
+
+ if ( fseek( current.fp, current.begin, SEEK_SET ) ) {
+ kdDebug(4500) << "KPSWidget::gs_input(): seek failed!" << endl;
+ interpreterFailed();
+ return;
+ }
+ Q_ASSERT( current.len > 0 );
+
+ const unsigned buffer_size = 4096;
+ if ( !_buffer ) _buffer = static_cast<char*>( operator new( buffer_size ) );
+ const int bytesRead = fread( _buffer, sizeof (char),
+ QMIN( buffer_size, current.len ),
+ current.fp );
+ if( bytesRead > 0 )
+ {
+ current.begin += bytesRead;
+ current.len -= bytesRead;
+ if( process && process->writeStdin( _buffer, bytesRead ) )
+ _stdinReady = false;
+ else
+ interpreterFailed();
+ }
+ else
+ interpreterFailed();
+}
+
+void KPSWidget::readSettings()
+{
+ setGhostscriptPath( Configuration::interpreter() );
+
+ QStringList arguments;
+
+ if( Configuration::antialiasing() )
+ arguments = QStringList::split( " ", Configuration::antialiasingArguments() );
+ else
+ arguments = QStringList::split( " ", Configuration::nonAntialiasingArguments() );
+
+ if( !Configuration::platformFonts() )
+ arguments << "-dNOPLATFONTS";
+
+ arguments << "-dNOPAUSE" << "-dQUIET" << "-dSAFER" << "-dPARANOIDSAFER";
+
+ setGhostscriptArguments( arguments );
+
+ setPalette( static_cast<Configuration::EnumPalette::type>( Configuration::palette() ) );
+}
+
+bool KPSWidget::x11Event( XEvent* e )
+{
+ if( e->type == ClientMessage )
+ {
+ _gsWindow = e->xclient.data.l[0];
+
+ if( e->xclient.message_type == _atoms[PAGE] )
+ {
+ kdDebug(4500) << "KPSWidget: received PAGE" << endl;
+ _interpreterBusy = false;
+ unsetCursor();
+ emit newPageImage( _backgroundPixmap );
+ if ( _doubleBuffer ) setErasePixmap( _backgroundPixmap );
+ return true;
+ }
+ else if( e->xclient.message_type == _atoms[DONE] )
+ {
+ kdDebug(4500) << "KPSWidget: received DONE" << endl;
+ stopInterpreter();
+ return true;
+ }
+ }
+ return QWidget::x11Event( e );
+}
+
+#include "kpswidget.moc"
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/kpswidget.h b/kghostview/kpswidget.h
new file mode 100644
index 00000000..66ef0b14
--- /dev/null
+++ b/kghostview/kpswidget.h
@@ -0,0 +1,387 @@
+/**
+ * Copyright (C) 2000-2003 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KPSWIDGET_H__
+#define __KPSWIDGET_H__
+
+#include <queue>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qwidget.h>
+#include <qpixmap.h>
+
+#include "dscparse_adapter.h"
+#include "configuration.h"
+
+#include <X11/X.h>
+
+class KProcess;
+
+class KGVConfigDialog;
+class MessagesDialog;
+
+/**
+ * @class KPSWidget
+ *
+ * @brief KPSWidget is a widget on which Ghostscript can render.
+ *
+ * @ref ghostscript_interface
+ */
+
+class KPSWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ KPSWidget( QWidget* parent = 0, const char* name = 0 );
+ ~KPSWidget();
+
+ /**
+ * Start up the Ghostscript interpreter. Returns true if Ghostscript
+ * could be started; otherwise false.
+ */
+ bool startInterpreter();
+
+ /**
+ * Stop the interpreter process.
+ */
+ void stopInterpreter();
+
+ /**
+ * Returns true if the interpreter is ready for new input.
+ */
+ bool isInterpreterReady() const;
+
+ bool isInterpreterBusy() const;
+
+ /**
+ * Returns true if the interpreter is running; otherwise returns false.
+ */
+ bool isInterpreterRunning() const;
+
+ /**
+ * Tell ghostscript to start the next page.
+ * Returns false if ghostscript is not running, or not ready to start
+ * another page. If another page is started, sets the _interpreterReady
+ * flag and cursor.
+ */
+ bool nextPage();
+
+ /**
+ * Queue a portion of a PostScript file for output to ghostscript and
+ * start processing the queue.
+ *
+ * fp: FILE* of the file in question. Should be seek()able.
+ * begin: position in file to start.
+ * len: number of bytes to write.
+ *
+ * If an interpreter is not running, nothing is queued and false is
+ * returned.
+ */
+ bool sendPS( FILE*, unsigned int begin, unsigned int end );
+
+ /**
+ * Sets the filename of the ghostscript input.
+ * @p usePipe indicates whether we use a pipe for
+ * communication or let ghoscript read the file itself.
+ */
+ void setFileName( const QString&, bool usePipe );
+
+ /**
+ * Set the bounding box of the drawable. See my comment in the source
+ * file.
+ */
+ void setBoundingBox( const KDSCBBOX& );
+
+ /**
+ * Set the orientation of the page.
+ */
+ void setOrientation( CDSC_ORIENTATION_ENUM );
+
+ /**
+ * Sets the resolution according to the physical resolution of the screen
+ * and the magnification value.
+ */
+ void setMagnification( double magnification );
+
+ /**
+ * @return the boundingbox of the drawable.
+ */
+ const KDSCBBOX& boundingBox() const;
+
+ /**
+ * @return the current orientation.
+ */
+ CDSC_ORIENTATION_ENUM orientation() const;
+
+ /**
+ * Double buffering means that all the drawing is done outside the
+ * screen and the finished picture is then flashed to the screen.
+ * This reduces flicker ( to almost none ) at the price of speed.
+ *
+ * By default, KPSWidget is *not* double buffered.
+ */
+ bool isDoubleBuffered() const { return _doubleBuffer; }
+ void setDoubleBuffering( bool n );
+
+
+ void clear();
+
+public slots:
+ /**
+ * Call this when the settings have changed.
+ */
+ void readSettings();
+signals:
+ /**
+ * This signal gets emited whenever a page is finished, but contains a reference to the pixmap
+ * used to hold the image.
+ *
+ * Don't change the pixmap or bad things will happen. This is the backing pixmap of the display.
+ */
+ void newPageImage( QPixmap image );
+
+ /**
+ * This signal is emitted whenever the ghostscript process has
+ * written data to stdout or stderr.
+ */
+ void output( char* data, int len );
+
+ /**
+ * This means that gs exited uncleanly
+ *
+ * @param msg a <strong>translated</strong> error message to display the user which may be null if we cannot tell anything important
+ */
+ void ghostscriptError( const QString& mgs );
+
+protected:
+ struct Record
+ {
+ Record( FILE* fp_, long begin_, unsigned len_ )
+ :fp( fp_ ), begin( begin_ ), len( len_ ) { }
+
+ FILE* fp;
+ long begin;
+ unsigned int len;
+ };
+
+ // void resizeEvent( QResizeEvent* );
+ bool x11Event( XEvent* );
+
+ /**
+ * Setup the widget according to the current settings for the
+ * boundingBox, the resolution and the orientation. This involves
+ * the following things:
+ * - Resize the widget
+ * - Resize and clear the background pixmap.
+ * - Setup the GHOSTVIEW and GHOSTVIEW_COLORS properties.
+ *
+ * Make sure ghostscript isn't running when calling this method.
+ */
+ void setupWidget();
+
+ void setGhostscriptPath( const QString& );
+ void setGhostscriptArguments( const QStringList& );
+ void setPalette( Configuration::EnumPalette::type );
+
+protected slots:
+ void gs_input( KProcess* );
+ void gs_output( KProcess*, char* buffer, int len );
+ void interpreterFailed();
+ void slotProcessExited( KProcess* );
+
+private:
+ Window _gsWindow; // Destination of ghostscript messages.
+
+ enum AtomName { GHOSTVIEW = 0, GHOSTVIEW_COLORS, NEXT, PAGE, DONE };
+ Atom _atoms[5];
+
+ QPixmap _backgroundPixmap;
+
+ /**
+ * The following properties determine how Ghostscript is started.
+ * If any of these is changed, Ghostscript needs to be restarted.
+ */
+ QString _ghostscriptPath;
+ QStringList _ghostscriptArguments;
+ QString _fileName;
+ bool _usePipe;
+ bool _doubleBuffer;
+
+ /**
+ * Flag set when one of the properties _ghostscriptPath,
+ * _ghostscriptArguments or _fileName has been changed.
+ */
+ bool _ghostscriptDirty;
+
+ /**
+ * The following properties determine how Ghostscript renders its
+ * pages. If any of these is changed, the widget needs to be setup,
+ * and Ghostscript needs to be restarted.
+ */
+ CDSC_ORIENTATION_ENUM _orientation;
+ KDSCBBOX _boundingBox;
+ float _magnification;
+ Configuration::EnumPalette::type _palette;
+
+ /**
+ * Flag set when one of the properties _orientation, _boundingBox,
+ * _dpi[X|Y] or _palette has been changed.
+ */
+ bool _widgetDirty;
+
+ KProcess* _process;
+ char* _buffer;
+
+ std::queue<Record> _inputQueue;
+
+ bool _stdinReady;
+ bool _interpreterBusy;
+ bool _interpreterReady;
+};
+
+inline const KDSCBBOX& KPSWidget::boundingBox() const
+{
+ return _boundingBox;
+}
+
+inline CDSC_ORIENTATION_ENUM KPSWidget::orientation() const
+{
+ return _orientation;
+}
+
+/**
+ * @page ghostview_interface Ghostview interface to Ghostscript
+ *
+ * When the GHOSTVIEW environment variable is set, Ghostscript draws on
+ * an existing drawable rather than creating its own window. Ghostscript
+ * can be directed to draw on either a window or a pixmap.
+ *
+ *
+ * @section window Drawing on a Window
+ *
+ * The GHOSTVIEW environment variable contains the window id of the target
+ * window. The window id is an integer. Ghostscript will use the attributes
+ * of the window to obtain the width, height, colormap, screen, and visual of
+ * the window. The remainder of the information is gotten from the GHOSTVIEW
+ * property on that window.
+ *
+ *
+ * @section pixmap Drawing on a Pixmap
+ *
+ * The GHOSTVIEW environment variable contains a window id and a pixmap id.
+ * They are integers separated by white space. Ghostscript will use the
+ * attributes of the window to obtain the colormap, screen, and visual to use.
+ * The width and height will be obtained from the pixmap. The remainder of the
+ * information, is gotten from the GHOSTVIEW property on the window. In this
+ * case, the property is deleted when read.
+ *
+ *
+ * @section gv_variable The GHOSTVIEW environment variable
+ *
+ * @par parameters:
+ * <tt> window-id [pixmap-id] </tt>
+ *
+ * @par scanf format:
+ * @code "%d %d" @endcode
+ *
+ * @par Explanation of the parameters
+ * @li @e window-id:
+ * tells Ghostscript where to:
+ * - read the GHOSTVIEW property
+ * - send events
+ * If pixmap-id is not present, Ghostscript will draw on this window.
+ *
+ * @li @e pixmap-id:
+ * If present, tells Ghostscript that a pixmap will be used as the
+ * final destination for drawing. The window will not be touched for
+ * drawing purposes.
+ *
+ *
+ * @section gv_property The GHOSTVIEW property
+ *
+ * @par type:
+ * STRING
+ *
+ * @par parameters:
+ * <tt> bpixmap orient llx lly urx ury xdpi ydpi [left bottom top right]
+ * </tt>
+ *
+ * @par scanf format:
+ * @code "%d %d %d %d %d %d %f %f %d %d %d %d" @endcode
+ *
+ * @par Explanation of the parameters
+ * @li @e bpixmap:
+ * pixmap id of the backing pixmap for the window. If no pixmap is to
+ * be used, this parameter should be zero. This parameter must be zero
+ * when drawing on a pixmap.
+ *
+ * @li <em>orient:</em>
+ * orientation of the page. The number represents clockwise rotation
+ * of the paper in degrees. Permitted values are 0, 90, 180, 270.
+ *
+ * @li <em>llx, lly, urx, ury:</em>
+ * Bounding box of the drawable. The bounding box is specified in
+ * PostScript points in default user coordinates. (Note the word
+ * @e drawable. This means that this bounding box is generally not
+ * the same as the BoundingBox specified in the DSC. In case
+ * DocumentMedia is specified, it is equal to the Media's bounding
+ * box.)
+ *
+ * @li <em>xdpi, ydpi:</em>
+ * Resolution of window. (This can be derived from the other
+ * parameters, but not without roundoff error. These values are
+ * included to avoid this error.)
+ *
+ * @li <em>left, bottom, top, right (optional):</em>
+ * Margins around the window. The margins extend the imageable area
+ * beyond the boundaries of the window. This is primarily used for
+ * popup zoom windows. I have encountered several instances of
+ * PostScript programs that position themselves with respect to the
+ * imageable area. The margins are specified in PostScript points.
+ * If omitted, the margins are assumed to be 0.
+ *
+ *
+ * @section events Events from Ghostscript
+ *
+ * If the final destination is a pixmap, the client will get a property
+ * notify event when Ghostscript reads the GHOSTVIEW property causing it to
+ * be deleted.
+ *
+ * Ghostscript sends events to the window where it read the GHOSTVIEW
+ * property. These events are of type ClientMessage. The message_type is set
+ * to either PAGE or DONE. The first long data value gives the window to be
+ * used to send replies to Ghostscript. The second long data value gives the
+ * primary drawable. If rendering to a pixmap, it is the primary drawable.
+ * If rendering to a window, the backing pixmap is the primary drawable. If
+ * no backing pixmap is employed, then the window is the primary drawable.
+ * This field is necessary to distinguish multiple Ghostscripts rendering to
+ * separate pixmaps where the GHOSTVIEW property was placed on the same
+ * window.
+ *
+ * The PAGE message indicates that a "page" has completed. Ghostscript will
+ * wait until it receives a ClientMessage whose message_type is NEXT before
+ * continuing.
+ *
+ * The DONE message indicates that Ghostscript has finished processing.
+ */
+
+#endif // __KPSWIDGET_H__
+
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/logwindow.cpp b/kghostview/logwindow.cpp
new file mode 100644
index 00000000..df2e5765
--- /dev/null
+++ b/kghostview/logwindow.cpp
@@ -0,0 +1,76 @@
+/**
+ * Copyright (C) 2003 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <qtextedit.h>
+#include <qlabel.h>
+#include <qvbox.h>
+
+#include <kglobalsettings.h>
+#include <kurllabel.h>
+#include <klocale.h>
+
+#include "logwindow.h"
+
+LogWindow::LogWindow( const QString& caption,
+ QWidget* parent, const char* name) :
+ KDialogBase( parent, name, false, caption, User1|Close, Close, false,
+ KStdGuiItem::clear() )
+{
+ QVBox * display = makeVBoxMainWidget();
+
+ _errorIndication = new QLabel( "", display, "logview-label" );
+ _errorIndication->hide();
+
+ _configureGS = new KURLLabel( i18n( "Configure Ghostscript" ), QString::null, display );
+ _configureGS->hide();
+
+ _logView = new QTextEdit( display, "logview" );
+ _logView->setTextFormat( Qt::PlainText );
+ _logView->setReadOnly( true );
+ _logView->setWordWrap( QTextEdit::NoWrap );
+ _logView->setFont( KGlobalSettings::fixedFont() );
+ _logView->setMinimumWidth( 80 * fontMetrics().width( " " ) );
+
+ connect( this, SIGNAL( user1Clicked() ), SLOT( clear() ) );
+ connect( _configureGS, SIGNAL( leftClickedURL() ), SLOT( emitConfigureGS() ) );
+}
+
+void LogWindow::emitConfigureGS() {
+ emit configureGS();
+}
+
+void LogWindow::append( const QString& message )
+{
+ _logView->append( message );
+}
+
+void LogWindow::clear()
+{
+ _logView->clear();
+ _errorIndication->clear();
+}
+
+void LogWindow::setLabel( const QString& text, bool showConfigureGS )
+{
+ _errorIndication->setText( text );
+ _errorIndication->show();
+ if ( showConfigureGS ) _configureGS->show();
+ else _configureGS->hide();
+}
+
+#include "logwindow.moc"
diff --git a/kghostview/logwindow.h b/kghostview/logwindow.h
new file mode 100644
index 00000000..4eb099e0
--- /dev/null
+++ b/kghostview/logwindow.h
@@ -0,0 +1,55 @@
+/**
+ * Copyright (C) 2003 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef LOGWINDOW_H
+#define LOGWINDOW_H
+
+#include <kdialogbase.h>
+
+class QLabel;
+class QTextEdit;
+class KURLLabel;
+
+class LogWindow : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ LogWindow( const QString& caption,
+ QWidget* parent = 0, const char* name = 0 );
+
+public slots:
+ void append( const QString& message );
+ void clear();
+ void setLabel( const QString&, bool showConfigureGSLink );
+
+private slots:
+ void emitConfigureGS();
+
+signals:
+ void configureGS();
+
+private:
+ QLabel* _errorIndication;
+ QTextEdit* _logView;
+ KURLLabel* _configureGS;
+};
+
+#endif
+
+// vim:sw=4:sts=4:ts=8:sta:tw=78:noet
diff --git a/kghostview/main.cpp b/kghostview/main.cpp
new file mode 100644
index 00000000..26574ddf
--- /dev/null
+++ b/kghostview/main.cpp
@@ -0,0 +1,71 @@
+/**
+ * Copyright (C) 1997-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <qdir.h>
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+#include <kurl.h>
+
+#include "kgvshell.h"
+#include "kgv_view.h"
+#include "displayoptions.h"
+
+static KCmdLineOptions options[] =
+{
+ { "page <page-no>", I18N_NOOP( "Page to open. Use --page=3 to show the third page, for example. Note that if the page does not exist, any other page may be displayed" ), "1" },
+ { "scale <factor>", I18N_NOOP( "Magnification of the display" ), "1.0" },
+ { "orientation <orientation>", I18N_NOOP( "The orientation of the shown image. Use either \"auto\", \"portrait\", \"landscape\", \"upsidedown\" or \"seascape\"" ), "auto" },
+ { "portrait", I18N_NOOP( "Equivalent to orientation=portrait" ), 0 },
+ { "landscape", I18N_NOOP( "Equivalent to orientation=landscape" ), 0 },
+ { "upsidedown", I18N_NOOP( "Equivalent to orientation=upsidedown" ), 0 },
+ { "seascape", I18N_NOOP( "Equivalent to orientation=seascape" ), 0 },
+ // { "watch", I18N_NOOP( "Turns on watching of a file. This means that whenever the file changes while you are viewing it, kghostview automatically reloads it. This option (which can also be turned on in the menu) is especially useful if you are generating your files from latex or a similar tool." ), 0 },
+ // { "page-size", I18N_NOOP( "The page size.\nUse either something like \"A4\" or you can display exact pixel size width-height" ), "auto" }
+ { "+[URL]", I18N_NOOP( "Location to open" ), 0 },
+ KCmdLineLastOption
+};
+
+int main( int argc, char** argv )
+{
+ KCmdLineArgs::init( argc, argv, KGVPart::createAboutData() );
+ KCmdLineArgs::addCmdLineOptions( options );
+ KApplication app;
+ KCmdLineArgs* args = KCmdLineArgs::parsedArgs();
+
+ if( kapp->isRestored() )
+ RESTORE( KGVShell )
+ else {
+ KGVShell* shell = new KGVShell;
+ if( args->count() == 1 ) {
+ if( QString( args->arg(0) ) == "-" ) {
+ shell->openStdin();
+ } else {
+ shell->openURL( args->url(0) );
+ }
+ shell->setDisplayOptions( DisplayOptions::parse( args ) );
+ }
+ shell->show();
+ }
+ args->clear();
+ return app.exec();
+}
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/marklist.cpp b/kghostview/marklist.cpp
new file mode 100644
index 00000000..674630ed
--- /dev/null
+++ b/kghostview/marklist.cpp
@@ -0,0 +1,242 @@
+/**
+ * Copyright (C) 1997-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "marklist.moc"
+
+#include <cassert>
+
+#include <qimage.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qlabel.h>
+#include <qwhatsthis.h>
+
+#include <kglobalsettings.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "kgv_miniwidget.h"
+
+MarkListItem::MarkListItem(QWidget *parent, const QString &text, const QString &tip, const QColor &color, KGVMiniWidget* miniW, int pageNum)
+ : QWidget( parent ),
+ _miniWidget( miniW ),
+ _pageNum( pageNum ),
+ _requested( false )
+{
+ //kdDebug( 4500 ) << "MarkListItem::MarkListItem( _ , " << text <<" , " << tip << " , " << color << ", _ ," << pageNum << " )" << endl;
+ QBoxLayout *l = new QVBoxLayout( this, 5, 0 );
+ _thumbnailW = new QWidget( this );
+ _checkBox = new QCheckBox( text, this );
+ l->addWidget( _thumbnailW, 1 );
+ l->addWidget( _checkBox, 0, Qt::AlignHCenter );
+ QWhatsThis::add( _checkBox, i18n( "Using this checkbox you can select pages for printing." ) );
+ setFixedHeight( 100 );
+ _backgroundColor = color;
+ setPaletteBackgroundColor( _backgroundColor );
+ QToolTip::add(this, tip);
+ // TODO: Put a little page number or other place-holder when there is no thumbnail to display.
+}
+
+bool MarkListItem::isChecked() const
+{
+ return _checkBox->isChecked();
+}
+
+void MarkListItem::toggle()
+{
+ _checkBox->toggle();
+}
+
+void MarkListItem::setChecked( bool checked )
+{
+ _checkBox->setChecked(checked);
+}
+
+void MarkListItem::setPixmap( QPixmap thumbnail )
+{
+ // The line below is needed to work around certain "features" of styles such as liquid
+ // see bug:61711 for more info (LPC, 20 Aug '03)
+ _thumbnailW->setBackgroundOrigin( QWidget::WidgetOrigin );
+ _thumbnailW->setPaletteBackgroundPixmap( thumbnail.convertToImage().smoothScale( _thumbnailW->size() ) );
+ _requested = false;
+}
+
+void MarkListItem::setSelected( bool selected )
+{
+ if (selected)
+ setPaletteBackgroundColor( QApplication::palette().active().highlight() );
+ else
+ setPaletteBackgroundColor( _backgroundColor );
+}
+
+void MarkListItem::resizeEvent( QResizeEvent * )
+{
+ if ( _thumbnailW->paletteBackgroundPixmap() )
+ _thumbnailW->setPaletteBackgroundPixmap( _thumbnailW->paletteBackgroundPixmap()->convertToImage().smoothScale( _thumbnailW->size() ) );
+}
+
+void MarkListItem::paintEvent( QPaintEvent* )
+{
+ /* TODO:
+ * We should cancel things which flipped into view and then flipped out.
+ *
+ * Now, if one scrolls through a 1000 page document to the end and then lingers on the
+ * last pages, these will take forever to appear in thumbnail form.
+ */
+ if ( _requested ) return;
+ if ( !_thumbnailW->paletteBackgroundPixmap() || _thumbnailW->paletteBackgroundPixmap()->isNull() ) {
+ _miniWidget->getThumbnailService()->delayedGetThumbnail( _pageNum, this, SLOT( setPixmap( QPixmap ) ) );
+ _requested = true;
+ }
+}
+
+
+/* MarkList */
+
+MarkList::MarkList( QWidget* parent, const char* name, KGVMiniWidget* mini)
+ : QTable( parent, name ),
+ _selected ( -1 ),
+_miniWidget( mini )
+{
+ setNumCols( 1 );
+ setLeftMargin( 0 ); // we don't want the vertical header
+ horizontalHeader()->setLabel( 0, i18n("Page") );
+
+ connect( this, SIGNAL( currentChanged( int, int ) ),
+ this, SIGNAL( selected( int ) ) );
+}
+
+QValueList<int> MarkList::markList() const
+{
+ QValueList<int> list;
+ MarkListItem *_item;
+ for(int i = 0; i < numRows(); i++)
+ {
+ _item = dynamic_cast<MarkListItem *>( cellWidget( i, 0 ) );
+ assert( _item );
+ if ( _item->isChecked() ) list << (i + 1);
+ }
+ return list;
+}
+
+void MarkList::insertItem( const QString& text, int index, const QString& tip)
+{
+ MarkListItem *_item;
+ _item = new MarkListItem( this, text, tip, viewport()->paletteBackgroundColor(), _miniWidget, index );
+ setNumRows( index + 1 );
+ setCellWidget( index, 0, _item );
+ setRowHeight( index, _item->height() );
+}
+
+void MarkList::select( int index )
+{
+ MarkListItem *_item;
+ setCurrentCell( index, 0 );
+ _item = dynamic_cast<MarkListItem *>( cellWidget( _selected, 0 ) );
+ if (_item) _item -> setSelected( false );
+ _selected = index;
+ _item = dynamic_cast<MarkListItem *>( cellWidget( _selected, 0 ) );
+ if (_item) _item -> setSelected( true );
+ clearFocus();
+}
+
+void MarkList::clear()
+{
+ for ( int i = 0; i != numRows() ; ++i ) {
+ clearCellWidget( i, 0 );
+ }
+ setNumRows( 0 );
+}
+
+void MarkList::markCurrent()
+{
+ MarkListItem *_item = dynamic_cast<MarkListItem *>( cellWidget( currentRow(), 0 ) );
+ assert( _item );
+ _item->toggle();
+}
+
+void MarkList::markAll()
+{
+ MarkListItem *_item;
+ for(int i = 0; i < numRows(); i++)
+ {
+ _item = dynamic_cast<MarkListItem *>( cellWidget( i, 0 ) );
+ assert( _item );
+ _item->setChecked( true );
+ }
+}
+
+void MarkList::markEven()
+{
+ MarkListItem *_item;
+ for(int i = 1; i < numRows(); i = i + 2)
+ {
+ _item = dynamic_cast<MarkListItem *>( cellWidget( i, 0 ) );
+ assert( _item );
+ _item->setChecked( true );
+ }
+}
+
+void MarkList::markOdd()
+{
+ MarkListItem *_item;
+ for(int i = 0; i < numRows(); i = i + 2)
+ {
+ _item = dynamic_cast<MarkListItem *>( cellWidget( i, 0 ) );
+ assert( _item );
+ _item->setChecked( true );
+ }
+}
+
+void MarkList::toggleMarks()
+{
+ MarkListItem *_item;
+ for(int i = 0; i < numRows(); i++)
+ {
+ _item = dynamic_cast<MarkListItem *>( cellWidget( i, 0 ) );
+ assert( _item );
+ _item->toggle();
+ }
+}
+
+void MarkList::removeMarks()
+{
+ MarkListItem *_item;
+ for( int i = 0; i < numRows(); i++ ) {
+ _item = dynamic_cast<MarkListItem *>( cellWidget( i, 0 ) );
+ assert( _item );
+ _item->setChecked( false );
+ }
+}
+
+void MarkList::viewportResizeEvent ( QResizeEvent * )
+{
+ MarkListItem *_item;
+ if( visibleWidth() != columnWidth( 0 ) )
+ {
+ setColumnWidth( 0, visibleWidth() );
+ for( int i = 0; i < numRows(); ++i )
+ {
+ _item = dynamic_cast<MarkListItem *>( cellWidget( i, 0 ) );
+ assert( _item );
+ _item->setFixedSize( visibleWidth(), _item->height() );
+ }
+ }
+}
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/marklist.h b/kghostview/marklist.h
new file mode 100644
index 00000000..18589af3
--- /dev/null
+++ b/kghostview/marklist.h
@@ -0,0 +1,88 @@
+/**
+ * Copyright (C) 1997-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef MARKLIST_H
+#define MARKLIST_H
+
+#include <qcheckbox.h>
+#include <qtable.h>
+
+class KGVMiniWidget;
+
+class MarkListItem : public QWidget
+{
+ Q_OBJECT
+public:
+ MarkListItem( QWidget *parent, const QString &text, const QString &tip, const QColor &color, KGVMiniWidget*, int );
+
+ bool isChecked() const;
+
+public slots:
+ void toggle();
+ void setChecked( bool checked );
+ void setPixmap( QPixmap thumbnail );
+
+ void setSelected( bool selected );
+
+private:
+ void resizeEvent( QResizeEvent * );
+ void paintEvent( QPaintEvent* );
+private:
+ QWidget *_thumbnailW;
+ QCheckBox *_checkBox;
+ QColor _backgroundColor;
+ KGVMiniWidget* _miniWidget;
+ const int _pageNum;
+ bool _requested;
+};
+
+class MarkList: public QTable
+{
+ Q_OBJECT
+
+public:
+ MarkList( QWidget* parent = 0, const char* name = 0, KGVMiniWidget* = 0 );
+
+ QValueList<int> markList() const;
+ void insertItem( const QString& text, int index = -1,
+ const QString& tip = QString::null );
+
+public slots:
+ void select( int index );
+ void markCurrent();
+ void markAll();
+ void markEven();
+ void markOdd();
+ void toggleMarks();
+ void removeMarks();
+ void clear();
+
+protected:
+ virtual void viewportResizeEvent ( QResizeEvent * );
+
+signals:
+ void selected( int );
+
+private:
+ int _selected;
+ KGVMiniWidget* _miniWidget;
+};
+
+#endif
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/part_init.cpp b/kghostview/part_init.cpp
new file mode 100644
index 00000000..76036948
--- /dev/null
+++ b/kghostview/part_init.cpp
@@ -0,0 +1,22 @@
+// part_init.cpp
+// Copyright (C) 2004 Dominique Devriese <devriese@kde.org>
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301, USA.
+
+#include "kgvfactory.h"
+
+K_EXPORT_COMPONENT_FACTORY( libkghostviewpart, KGVFactory )
+
diff --git a/kghostview/ps.c b/kghostview/ps.c
new file mode 100644
index 00000000..1ff4effa
--- /dev/null
+++ b/kghostview/ps.c
@@ -0,0 +1,188 @@
+/*
+ * ps.c -- Postscript scanning and copying routines.
+ * Copyright (C) 1992 Timothy O. Theisen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Author: Tim Theisen Systems Programmer
+ * Internet: tim@cs.wisc.edu Department of Computer Sciences
+ * UUCP: uwvax!tim University of Wisconsin-Madison
+ * Phone: (608)262-0438 1210 West Dayton Street
+ * FAX: (608)262-9777 Madison, WI 53706
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ps.h"
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+
+/* length calculates string length at compile time */
+/* can only be used with character constants */
+#define length(a) (sizeof(a)-1)
+#define iscomment(a, b) (strncmp(a, b, length(b)) == 0)
+#define DSCcomment(a) (a[0] == '%' && a[1] == '%')
+
+/*
+ * pscopy -- copy lines of Postscript from a section of one file
+ * to another file.
+ * Automatically switch to binary copying whenever
+ * %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
+ * comments are encountered.
+ */
+
+void
+pscopy(from, to, begin, end)
+ FILE *from;
+ FILE *to;
+ long begin; /* set negative to avoid initial seek */
+ long end;
+{
+ char line[PSLINELENGTH]; /* 255 characters + 1 newline + 1 NULL */
+ char text[PSLINELENGTH]; /* Temporary storage for text */
+ unsigned int num;
+ unsigned int i;
+ char buf[BUFSIZ];
+
+ if (begin >= 0) fseek(from, begin, SEEK_SET);
+ while (ftell(from) < end) {
+
+ fgets(line, sizeof line, from);
+ fputs(line, to);
+
+ if (!(DSCcomment(line) && iscomment(line+2, "Begin"))) {
+ /* Do nothing */
+ } else if (iscomment(line+7, "Data:")) {
+ int rc = 0;
+ text[0] = '\0';
+ rc = sscanf(line+length("%%BeginData:"), "%d %*s %256s", &num,text);
+ text[256] = '\0';
+ if (rc >= 1) {
+ if (strcmp(text, "Lines") == 0) {
+ for (i=0; i < num; i++) {
+ fgets(line, sizeof line, from);
+ fputs(line, to);
+ }
+ } else {
+ while (num > BUFSIZ) {
+ fread(buf, sizeof (char), BUFSIZ, from);
+ fwrite(buf, sizeof (char), BUFSIZ, to);
+ num -= BUFSIZ;
+ }
+ fread(buf, sizeof (char), num, from);
+ fwrite(buf, sizeof (char), num, to);
+ }
+ }
+ } else if (iscomment(line+7, "Binary:")) {
+ if(sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
+ while (num > BUFSIZ) {
+ fread(buf, sizeof (char), BUFSIZ, from);
+ fwrite(buf, sizeof (char), BUFSIZ, to);
+ num -= BUFSIZ;
+ }
+ fread(buf, sizeof (char), num, from);
+ fwrite(buf, sizeof (char), num, to);
+ }
+ }
+ }
+}
+
+/*
+ * pscopyuntil -- copy lines of Postscript from a section of one file
+ * to another file until a particular comment is reached.
+ * Automatically switch to binary copying whenever
+ * %%BeginBinary/%%EndBinary or %%BeginData/%%EndData
+ * comments are encountered.
+ */
+
+char *
+pscopyuntil(from, to, begin, end, comment)
+ FILE *from;
+ FILE *to;
+ long begin; /* set negative to avoid initial seek */
+ long end;
+ const char *comment;
+{
+ char line[PSLINELENGTH]; /* 255 characters + 1 newline + 1 NULL */
+ char text[PSLINELENGTH]; /* Temporary storage for text */
+ unsigned int num;
+ unsigned int i;
+ int comment_length;
+ char buf[BUFSIZ];
+ char *cp;
+
+ comment_length = strlen(comment);
+ if (begin >= 0) fseek(from, begin, SEEK_SET);
+ while (ftell(from) < end) {
+
+ fgets(line, sizeof line, from);
+
+ /* iscomment cannot be used here,
+ * because comment_length is not known at compile time. */
+ if (strncmp(line, comment, comment_length) == 0) {
+ cp = (char *) malloc(strlen(line)+1);
+ if (cp == NULL) {
+ fprintf(stderr, "Fatal Error: Dynamic memory exhausted.\n");
+ exit(-1);
+ }
+ strcpy(cp, line);
+ return cp;
+ }
+ fputs(line, to);
+ if (!(DSCcomment(line) && iscomment(line+2, "Begin"))) {
+ /* Do nothing */
+ } else if (iscomment(line+7, "Data:")) {
+ int rc = 0;
+ text[0] = '\0';
+ rc = sscanf(line+length("%%BeginData:"), "%d %*s %256s", &num,text);
+ text[256] = '\0';
+ if (rc >= 1) {
+ if (strcmp(text, "Lines") == 0) {
+ for (i=0; i < num; i++) {
+ fgets(line, sizeof line, from);
+ fputs(line, to);
+ }
+ } else {
+ while (num > BUFSIZ) {
+ fread(buf, sizeof (char), BUFSIZ, from);
+ fwrite(buf, sizeof (char), BUFSIZ, to);
+ num -= BUFSIZ;
+ }
+ fread(buf, sizeof (char), num, from);
+ fwrite(buf, sizeof (char), num, to);
+ }
+ }
+ } else if (iscomment(line+7, "Binary:")) {
+ if(sscanf(line+length("%%BeginBinary:"), "%d", &num) == 1) {
+ while (num > BUFSIZ) {
+ fread(buf, sizeof (char), BUFSIZ, from);
+ fwrite(buf, sizeof (char), BUFSIZ, to);
+ num -= BUFSIZ;
+ }
+ fread(buf, sizeof (char), num, from);
+ fwrite(buf, sizeof (char), num, to);
+ }
+ }
+ }
+ return NULL;
+}
+
diff --git a/kghostview/ps.h b/kghostview/ps.h
new file mode 100644
index 00000000..3cb4ea9c
--- /dev/null
+++ b/kghostview/ps.h
@@ -0,0 +1,44 @@
+/*
+ * ps.h -- Include file for PostScript routines.
+ * Copyright (C) 1992 Timothy O. Theisen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Author: Tim Theisen Systems Programmer
+ * Internet: tim@cs.wisc.edu Department of Computer Sciences
+ * UUCP: uwvax!tim University of Wisconsin-Madison
+ * Phone: (608)262-0438 1210 West Dayton Street
+ * FAX: (608)262-9777 Madison, WI 53706
+ */
+
+#ifndef _PS_H
+#define _PS_H
+
+#define PSLINELENGTH 257 /* 255 characters + 1 newline + 1 NULL */
+
+ /* Copy a portion of the PostScript file */
+
+void pscopy(FILE *from, FILE *to, long begin, long end);
+
+ /* Copy a portion of the PostScript file upto a comment */
+
+char *pscopyuntil(FILE *from, FILE *to, long begin, long end,
+ const char *comment);
+
+#endif
+
+/*
+ * vim:sw=4:sts=4:ts=8:noet
+ */
diff --git a/kghostview/scrollbox.cpp b/kghostview/scrollbox.cpp
new file mode 100644
index 00000000..8be3ab61
--- /dev/null
+++ b/kghostview/scrollbox.cpp
@@ -0,0 +1,129 @@
+/**
+ * Copyright (C) 1997-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <qdrawutil.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+
+#include "scrollbox.h"
+
+ScrollBox::ScrollBox( QWidget* parent, const char* name )
+ : QFrame( parent, name )
+{
+ setFrameStyle( Panel | Sunken );
+}
+
+void ScrollBox::mousePressEvent( QMouseEvent* e )
+{
+ mouse = e->pos();
+ if( e->button() == RightButton )
+ emit button3Pressed();
+ if( e->button() == MidButton )
+ emit button2Pressed();
+}
+
+void ScrollBox::mouseMoveEvent( QMouseEvent* e )
+{
+ if( e->state() != LeftButton )
+ return;
+
+ int dx = ( e->pos().x() - mouse.x() ) * pagesize.width() / width();
+ int dy = ( e->pos().y() - mouse.y() ) * pagesize.height() / height();
+
+ // Notify the word what the view position has changed
+ // The word in turn will notify as that view position has changed
+ // Even if coordinates are out of range QScrollView handles
+ // this properly
+ emit valueChanged( QPoint( viewpos.x() + dx, viewpos.y() + dy ) );
+ emit valueChangedRelative( dx, dy );
+
+ mouse = e->pos();
+}
+
+void ScrollBox::resizeEvent( QResizeEvent * )
+{
+ if ( paletteBackgroundPixmap() )
+ setPaletteBackgroundPixmap( paletteBackgroundPixmap()->convertToImage().smoothScale( size() ) );
+}
+
+void ScrollBox::drawContents( QPainter* paint )
+{
+ if ( pagesize.isEmpty() )
+ return;
+
+
+ /* FIXME:
+ *
+ * The logic below is flawed because the page info given to us
+ * contains the borders used for page decoration, while we assume
+ * that it means only the actual displayed document.
+ *
+ */
+
+ QRect c( contentsRect() );
+
+ paint -> setPen( Qt::red );
+
+ int len = pagesize.width();
+ int x = c.x() + c.width() * viewpos.x() / len;
+ int w = c.width() * viewsize.width() / len ;
+ if ( w > c.width() ) w = c.width();
+
+ len = pagesize.height();
+ int y = c.y() + c.height() * viewpos.y() / len;
+ int h = c.height() * viewsize.height() / len;
+ if ( h > c.height() ) h = c.height();
+
+ paint->drawRect( x, y, w, h );
+}
+
+void ScrollBox::setPageSize( const QSize& s )
+{
+ pagesize = s;
+ setFixedHeight( s.height() * width() / s.width() );
+ repaint();
+}
+
+void ScrollBox::setViewSize( const QSize& s )
+{
+ viewsize = s;
+ repaint();
+}
+
+void ScrollBox::setViewPos( const QPoint& pos )
+{
+ viewpos = pos;
+ repaint();
+}
+
+void ScrollBox::setThumbnail( QPixmap img )
+{
+ // The line below is needed to work around certain "features" of styles such as liquid
+ // see bug:61711 for more info (LPC, 20 Aug '03)
+ setBackgroundOrigin( QWidget::WidgetOrigin );
+ setPaletteBackgroundPixmap( img.convertToImage().smoothScale( size() ) );
+}
+
+void ScrollBox::clear()
+{
+ unsetPalette();
+}
+
+#include "scrollbox.moc"
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/scrollbox.h b/kghostview/scrollbox.h
new file mode 100644
index 00000000..a815c655
--- /dev/null
+++ b/kghostview/scrollbox.h
@@ -0,0 +1,60 @@
+/**
+ * Copyright (C) 1997-2002 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __SCROLLBOX_H__
+#define __SCROLLBOX_H__
+
+#include <qframe.h>
+#include <qimage.h>
+
+class ScrollBox: public QFrame
+{
+ Q_OBJECT
+
+public:
+ ScrollBox( QWidget* parent = 0, const char* name = 0 );
+
+public slots:
+ void setPageSize( const QSize& );
+ void setViewSize( const QSize& );
+ void setViewPos( const QPoint& );
+ void setViewPos( int x, int y ) { setViewPos( QPoint( x, y ) ); }
+ void setThumbnail( QPixmap img );
+ void clear();
+
+signals:
+ void valueChanged( const QPoint& );
+ void valueChangedRelative( int dx, int dy );
+ void button2Pressed();
+ void button3Pressed();
+
+protected:
+ void mousePressEvent( QMouseEvent *);
+ void mouseMoveEvent( QMouseEvent *);
+ void drawContents( QPainter *);
+ void resizeEvent( QResizeEvent * );
+
+private:
+ QPoint viewpos, mouse;
+ QSize pagesize;
+ QSize viewsize;
+};
+
+#endif
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/thumbnailservice.cpp b/kghostview/thumbnailservice.cpp
new file mode 100644
index 00000000..4d653cfa
--- /dev/null
+++ b/kghostview/thumbnailservice.cpp
@@ -0,0 +1,161 @@
+/**
+ * Copyright (C) 1997-2003 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "thumbnailservice.h"
+
+#include "kgv_miniwidget.h"
+#include "kpswidget.h"
+#include "kgv_view.h"
+
+#include <kdebug.h>
+#include <qtimer.h>
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+
+ThumbnailService::ThumbnailService( KGVMiniWidget* parent, const char* name ) :
+ QObject( parent, name ),
+ _mini( parent ),
+ timer_( new QTimer( this ) ),
+ _busy( false ),
+ _enabled( false )
+{
+ _thumbnailDrawer = new KPSWidget( parent->_part->widget(), "thumbnail-drawer" );
+ _thumbnailDrawer->readSettings();
+ connect( _thumbnailDrawer, SIGNAL( newPageImage( QPixmap ) ), SLOT( slotDone( QPixmap ) ) );
+ connect( timer_, SIGNAL( timeout() ), SLOT( processOne() ) );
+ _thumbnailDrawer->hide();
+}
+
+ThumbnailService::~ThumbnailService()
+{
+}
+
+bool ThumbnailService::Request::operator < ( ThumbnailService::Request b ) const
+{
+ if ( urgent != b.urgent ) return urgent;
+ if ( page != b.page ) return page < b.page;
+ // below is just so that == can be in terms of "<"
+ if ( receiver != b.receiver ) return std::less<QObject*>()( receiver, b.receiver );
+ if ( slot != b.slot ) return std::strcmp( slot, b.slot ) < 0;
+ return false;
+}
+
+void ThumbnailService::delayedGetThumbnail( const int page, QObject* rec, const char* slot, bool urgent )
+{
+ kdDebug( 4500 ) << "ThumbnailService::delayedGetThumbnail: request for page " << page << endl;
+ pending.insert( Request( page, rec, slot, urgent ) );
+ if ( !_busy ) {
+ _busy = true;
+ // The reason for the code below might not be obvious:
+ // It is much nicer to have the the thumbnails appear from top to bottom.
+ // However, when several are requested at once (or almost), then we cannot control the order
+ // unless we delay a bit the first one
+ if ( urgent ) processOne();
+ else timer_->start( 150, true );
+ }
+}
+
+void ThumbnailService::cancelRequests( const int page, QObject* rec, const char* slot )
+{
+ std::set<Request>::iterator first = pending.begin(), last = pending.end();
+ while ( first != last ) {
+ if ( ( page == -1 || page == first->page ) &&
+ ( !rec || rec == first->receiver ) &&
+ ( !slot || !strcmp( slot, first->slot ) ) ) {
+ std::set<Request>::iterator next = first;
+ ++next;
+ pending.erase( first );
+ first = next;
+ } else {
+ ++first;
+ }
+ }
+}
+
+void ThumbnailService::reset()
+{
+ kdDebug( 4500 ) << "ThumbnailService::reset()" << endl;
+ timer_->stop();
+ pending.clear();
+ assert( _thumbnailDrawer );
+ _thumbnailDrawer->stopInterpreter();
+ _busy = false;
+ _enabled = false;
+}
+
+void ThumbnailService::setEnabled( const bool e )
+{
+ kdDebug( 4500 ) << "ThumbnailService::setEnabled( " << ( e ? "true" : "false" ) << " )" << endl;
+ _enabled = e;
+ if ( _enabled && _busy ) processOne();
+}
+
+void ThumbnailService::slotDone( QPixmap pix )
+{
+ kdDebug( 4500 ) << "ThumbnailService::slotDone(): got page" << endl;
+ pix.detach();
+ emit relayPixmap( pix );
+ processOne();
+}
+
+
+void ThumbnailService::processOne()
+{
+ kdDebug( 4500 ) << "ThumbnailService::processOne()" << endl;
+ if ( !_enabled ) return;
+ if ( !_mini || !_mini->dsc() || !_mini->dsc()->isStructured() ) {
+ _busy = false;
+ pending.clear();
+ return;
+ }
+ assert( _thumbnailDrawer );
+ if ( pending.empty() ) {
+ _busy = false;
+ return;
+ }
+ _busy = true;
+ FILE* file = _mini->psFile();
+ Request req = *pending.begin();
+ kdDebug( 4500 ) << "ThumbnailService::processOne(): processing " << req.page << "(of " << pending.size() << " requests)" << endl;
+ disconnect( SIGNAL( relayPixmap( QPixmap ) ) );
+ while ( !pending.empty() && req.page == pending.begin()->page ) {
+ req = *pending.begin();
+ connect( this, SIGNAL( relayPixmap( QPixmap ) ), req.receiver, req.slot );
+ pending.erase( pending.begin() );
+ }
+ _thumbnailDrawer->setOrientation( _mini->orientation( req.page ) );
+ _thumbnailDrawer->setBoundingBox( _mini->boundingBox( req.page ) );
+ _thumbnailDrawer->setMagnification( 0.2 );
+ if ( !_thumbnailDrawer->isInterpreterRunning() ) {
+ _thumbnailDrawer->setFileName( _mini->_document->fileName(), true );
+ _thumbnailDrawer->startInterpreter();
+ _thumbnailDrawer->sendPS( file, _mini->dsc()->beginprolog(),
+ _mini->dsc()->endprolog() );
+ _thumbnailDrawer->sendPS( file, _mini->dsc()->beginsetup(),
+ _mini->dsc()->endsetup() );
+ }
+ else {
+ _thumbnailDrawer->nextPage();
+ }
+ _thumbnailDrawer->sendPS( file, _mini->dsc()->page()[ req.page ].begin,
+ _mini->dsc()->page()[ req.page ].end );
+}
+
+#include "thumbnailservice.moc"
+
diff --git a/kghostview/thumbnailservice.h b/kghostview/thumbnailservice.h
new file mode 100644
index 00000000..d1981242
--- /dev/null
+++ b/kghostview/thumbnailservice.h
@@ -0,0 +1,92 @@
+#ifndef THUMBNAILSERVICE_H
+#define THUMBNAILSERVICE_H
+/**
+ * Copyright (C) 1997-2003 the KGhostView authors. See file AUTHORS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <set>
+#include <qobject.h>
+#include <qpixmap.h>
+#include <qguardedptr.h>
+#include <stdio.h>
+
+class KPSWidget;
+class KGVMiniWidget;
+class QTimer;
+
+class ThumbnailService : public QObject {
+ Q_OBJECT
+ public:
+ ThumbnailService( KGVMiniWidget* parent, const char* name = 0 );
+ ~ThumbnailService();
+
+ public slots:
+ void delayedGetThumbnail( int page, QObject* receiver, const char* slot, bool urgent = false );
+ /**
+ * Cancels the request matching the signature below.
+ *
+ * @param page the page of the request. Use "-1" for wildcard
+ * @param receiver the receiver. Use null for wildcard
+ * @param slot Use null for wildcard
+ */
+ void cancelRequests( int page, QObject* receiver, const char* slot);
+
+ void reset();
+ /**
+ * This "suspends" the service.
+ * Unlike @ref reset(), if you call setEnabled( false ),
+ * old requests will be kept and will be serviced when
+ * you call setEnabled( true ) later.
+ */
+ void setEnabled( bool );
+
+ signals:
+ /**
+ * Don't connect to this directly
+ */
+ void relayPixmap( QPixmap );
+
+ private slots:
+ void slotDone( QPixmap );
+ void processOne();
+
+ private:
+
+ struct Request {
+ Request( int p, QObject* r, const char* s ) :
+ page( p ), receiver( r ), slot( s ), urgent( false ) { }
+ Request( int p, QObject* r, const char* s, bool u ) :
+ page( p ), receiver( r ), slot( s ), urgent( u ) { }
+
+ int page;
+ QObject* receiver;
+ const char* slot;
+ bool urgent;
+ bool operator < ( Request ) const;
+ };
+ static bool pageCmp( Request, Request );
+ std::set<Request> pending;
+ QGuardedPtr<KPSWidget> _thumbnailDrawer;
+ KGVMiniWidget* _mini;
+ QTimer* timer_;
+ bool _busy;
+ bool _enabled;
+};
+
+// vim:sw=4:sts=4:ts=8:sta:tw=78:noet
+#endif // THUMBNAILSERVICE_H
+
diff --git a/kghostview/update-to-xt-names.pl b/kghostview/update-to-xt-names.pl
new file mode 100644
index 00000000..1aa07403
--- /dev/null
+++ b/kghostview/update-to-xt-names.pl
@@ -0,0 +1,36 @@
+#!/usr/bin/perl
+
+my $group = '';
+my $key = '';
+my $value = '';
+
+while (<>) {
+ if (/^\s.*$/) { next }
+ if (/^\s*#.*$/) { next }
+ if (/\[(.*)\]/) {
+ $group = $1;
+ } elsif (/^(.*)=(.*)$/) {
+ $key = $1;
+ $value = $2;
+ } else { next }
+
+ if (!$group || !$key) { next }
+
+ if ( $group eq 'General' ) {
+ if ( $key eq 'Palette' ) {
+ if ($value eq 'color') {
+ print "Palette=Color\n";
+ } elsif ($value eq 'monochrome') {
+ print "Palette=Monochrome\n";
+ } elsif ($value eq 'grayscale') {
+ print "Palette=Grayscale\n";
+ } else {
+ print "Palette=Color\n";
+ }
+ }
+ if ( $key eq 'Interpreter' ) {
+ print "# DELETE [General]Interpreter\n";
+ }
+ }
+}
+
diff --git a/kghostview/version.h b/kghostview/version.h
new file mode 100644
index 00000000..81cf5df2
--- /dev/null
+++ b/kghostview/version.h
@@ -0,0 +1,4 @@
+#define KGHOSTVIEW_VERSION "0.20"
+
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/viewcontrol.cpp b/kghostview/viewcontrol.cpp
new file mode 100644
index 00000000..cc66d598
--- /dev/null
+++ b/kghostview/viewcontrol.cpp
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** A dialog for the selection of the view of a document.
+**
+** Copyright (C) 1997 by Mark Donohoe.
+** Based on original work by Tim Theisen.
+**
+** This code is freely distributable under the GNU Public License.
+**
+*****************************************************************************/
+
+#include <qcombobox.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qlayout.h>
+
+
+#include "viewcontrol.h"
+#include "viewcontrol.moc"
+
+#include <klocale.h>
+#include <kapplication.h>
+#include <kbuttonbox.h>
+#include <kseparator.h>
+
+ViewControl::ViewControl( QWidget *parent, const char *name )
+ : QDialog( parent, name )
+{
+ setFocusPolicy(QWidget::StrongFocus);
+
+ QBoxLayout *topLayout = new QVBoxLayout( this, 10 );
+
+ QGroupBox* vcGroupBox;
+ vcGroupBox = new QGroupBox( this );
+ vcGroupBox->setFrameStyle( QFrame::NoFrame );
+ //vcGroupBox->setTitle( i18n("Force Changes To") );
+ //vcGroupBox->setAlignment( 1 );
+
+ topLayout->addWidget( vcGroupBox, 10 );
+
+ QGridLayout *grid = new QGridLayout( vcGroupBox, 3, 2, 10 );
+
+ grid->setRowStretch(0,0);
+ grid->setRowStretch(1,10);
+
+
+ grid->setColStretch(0,0);
+ grid->setColStretch(1,10);
+
+
+ magComboBox = new QComboBox( FALSE, vcGroupBox );
+ magComboBox->setFixedHeight( magComboBox->sizeHint().height() );
+
+
+ //magComboBox->hide();
+
+ connect ( magComboBox, SIGNAL (activated (int)),
+ this, SLOT (slotMagSelection (int)) );
+ grid->addWidget( magComboBox, 0, 1 );
+
+
+
+ mediaComboBox = new QComboBox( FALSE, vcGroupBox );
+ mediaComboBox->setFixedHeight( magComboBox->sizeHint().height() );
+
+ connect ( mediaComboBox, SIGNAL (activated (int)),
+ this, SLOT (slotMediaSelection (int)) );
+
+ grid->addWidget( mediaComboBox, 1, 1 );
+
+ orientComboBox = new QComboBox( FALSE, vcGroupBox );
+ orientComboBox->insertItem(i18n("Portrait"));
+ orientComboBox->insertItem(i18n("Landscape"));
+ orientComboBox->insertItem(i18n("Seascape"));
+ orientComboBox->insertItem(i18n("Upside Down"));
+ orientComboBox->setFixedHeight( magComboBox->sizeHint().height() );
+
+ connect ( orientComboBox, SIGNAL (activated (int)),
+ this, SLOT (slotOrientSelection (int)) );
+ grid->addWidget( orientComboBox, 2, 1 );
+
+ int labelWidth = 0;
+
+ QLabel* vcLabel;
+ vcLabel = new QLabel( magComboBox, i18n("&Magnification"), vcGroupBox );
+ vcLabel->setAlignment( AlignRight | AlignVCenter | ShowPrefix );
+ if ( vcLabel->sizeHint().width() > labelWidth )
+ labelWidth = vcLabel->sizeHint().width();
+ vcLabel->setMinimumWidth( labelWidth );
+
+ vcLabel->hide();
+
+ grid->addWidget( vcLabel, 0, 0 );
+
+
+ vcLabel = new QLabel( mediaComboBox, i18n("M&edia"), vcGroupBox );
+ vcLabel->setAlignment( AlignRight | AlignVCenter | ShowPrefix );
+ if ( vcLabel->sizeHint().width() > labelWidth )
+ labelWidth = vcLabel->sizeHint().width();
+ vcLabel->setMinimumWidth( labelWidth );
+
+ grid->addWidget( vcLabel, 1, 0 );
+
+ vcLabel = new QLabel( orientComboBox, i18n("&Orientation"), vcGroupBox );
+ vcLabel->setAlignment( AlignRight | AlignVCenter | ShowPrefix );
+ if ( vcLabel->sizeHint().width() > labelWidth )
+ labelWidth = vcLabel->sizeHint().width();
+ vcLabel->setMinimumWidth( labelWidth );
+
+ grid->addWidget( vcLabel, 2, 0 );
+
+ vcGroupBox->setMinimumHeight( 2*orientComboBox->sizeHint().height()+20 );
+ vcGroupBox->setMinimumWidth(
+ 40 + labelWidth + orientComboBox->sizeHint().width() );
+
+ KSeparator* sep = new KSeparator( KSeparator::HLine, this);
+ topLayout->addWidget( sep );
+
+ // CREATE BUTTONS
+
+ KButtonBox *bbox = new KButtonBox( this );
+ bbox->addStretch( 10 );
+
+ apply = bbox->addButton( KStdGuiItem::apply() );
+ connect( apply, SIGNAL(clicked()), SLOT(slotApplyClicked()) );
+
+ QPushButton *closebtn = bbox->addButton( KStdGuiItem::close() );
+ connect( closebtn, SIGNAL(clicked()), SLOT(reject()) );
+
+
+ bbox->layout();
+ topLayout->addWidget( bbox );
+
+ topLayout->activate();
+
+ prevmag = prevmedia = prevorient = 0;
+ applyEnable (false);
+}
+
+void
+ViewControl::updateMag (int mag)
+{
+ magComboBox->setCurrentItem (mag);
+ prevmag = mag;
+}
+
+
+void
+ViewControl::applyEnable (bool enable)
+{
+ apply->setEnabled (enable);
+}
+
+void
+ViewControl::slotApplyClicked()
+{
+ emit applyChanges();
+ applyEnable (false);
+}
+
+void
+ViewControl::slotMagSelection (int i)
+{
+ if (i != prevmag)
+ {
+ applyEnable (true);
+ prevmag = i;
+ }
+}
+
+void
+ViewControl::slotMediaSelection (int i)
+{
+ if (i != prevmedia)
+ {
+ applyEnable (true);
+ prevmedia = i;
+ }
+}
+
+void
+ViewControl::slotOrientSelection (int i)
+{
+ if (i != prevorient)
+ {
+ applyEnable (true);
+ prevorient = i;
+ }
+}
+
+
+// vim:sw=4:sts=4:ts=8:noet
diff --git a/kghostview/viewcontrol.h b/kghostview/viewcontrol.h
new file mode 100644
index 00000000..3e8bcd70
--- /dev/null
+++ b/kghostview/viewcontrol.h
@@ -0,0 +1,44 @@
+#ifndef VIEWCONTROL_H
+#define VIEWCONTROL_H
+
+#include <qdialog.h>
+class QComboBox;
+class QPushButton;
+
+
+class ViewControl : public QDialog
+{
+ Q_OBJECT
+public:
+ ViewControl( QWidget *parent, const char *name );
+ QComboBox* magComboBox;
+ QComboBox* mediaComboBox;
+ QComboBox* orientComboBox;
+ QPushButton *apply;
+
+ /**
+ * Update the mag combo box.
+ **/
+ void updateMag (int mag);
+
+ /**
+ * Enable/disable the apply button.
+ **/
+ void applyEnable (bool enable);
+
+protected:
+ int prevmag, prevmedia, prevorient;
+
+public slots:
+ void slotApplyClicked();
+ void slotMagSelection (int i);
+ void slotMediaSelection (int i);
+ void slotOrientSelection (int i);
+
+signals:
+ void applyChanges();
+};
+
+#endif
+
+// vim:sw=4:sts=4:ts=8:noet