diff options
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | src/GsWidget.H | 22 | ||||
-rw-r--r-- | src/GsWidget.cxx | 266 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/PSEditor.H | 4 | ||||
-rw-r--r-- | src/PSEditor.cxx | 10 | ||||
-rw-r--r-- | src/PostscriptDSC.H | 45 | ||||
-rw-r--r-- | src/PostscriptDSC.cxx | 185 | ||||
-rw-r--r-- | src/flpsed.cxx | 56 |
10 files changed, 526 insertions, 71 deletions
@@ -1,5 +1,10 @@ flpsed ChangeLog ================= +flpsed-0.3.6 + - Add support for PostScript Document Structure Convention. + - Pages can now be accessed in random order. + - Document sizes other than A4 are handled better. + - Add -geometry and -z (zoom) options. flpsed-0.3.5 - Add text color support. Click on the (initially) black button and diff --git a/configure.ac b/configure.ac index 2ffb66e..cdee12a 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(flpsed, 0.3.6-pre1, Johannes.Hofmann@gmx.de) +AC_INIT(flpsed, 0.3.6, Johannes.Hofmann@gmx.de) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([src/GsWidget.H]) AC_CONFIG_HEADER(config.h) diff --git a/src/GsWidget.H b/src/GsWidget.H index 99ef08a..659bc69 100644 --- a/src/GsWidget.H +++ b/src/GsWidget.H @@ -34,8 +34,11 @@ #include <FL/Fl_Window.H> #include <FL/x.H> +#include "PostscriptDSC.H" class GsWidget : public Fl_Widget { +private: + PostscriptDSC *dsc; int gs_win; Atom atoms[5]; pid_t gs_pid; @@ -43,6 +46,7 @@ class GsWidget : public Fl_Widget { int in_fd, reload_needed; int zoom_percent; int initial_width, initial_height; + int feeding; protected: int page; @@ -56,26 +60,38 @@ public: GsWidget(int X,int Y,int W, int H); ~GsWidget(); - - int load(char *f); + + int open_file(char *f); + + int open_file(int fd); + + int load(); - int load(int fd); + int load_page(int p); int reload(); int next(); + int prev(); + int handleX11(int ev); int zoom(int p); int get_page(); + int get_pages(); + private: void setProps(); + + void exec_gs(); void kill_gs(); + int fd_copy(int to, int from, size_t len); + bool gs_active(); }; #endif diff --git a/src/GsWidget.cxx b/src/GsWidget.cxx index 1bb2e53..e92a71d 100644 --- a/src/GsWidget.cxx +++ b/src/GsWidget.cxx @@ -29,11 +29,14 @@ #include <errno.h> #include <sys/wait.h> #include <fcntl.h> +#include <signal.h> #include <FL/fl_draw.H> #include "GsWidget.H" +#include "PostscriptDSC.H" +#define MIN(A,B) ((A)<(B)?(A):(B)) void GsWidget::draw() { if (!offscreen) { @@ -66,7 +69,7 @@ void GsWidget::setProps() { atoms[3] = XInternAtom(fl_display,"PAGE" , false); atoms[4] = XInternAtom(fl_display,"DONE" , false); - snprintf(data, 512, "%d %d %d %d %d %d %d.0 %d.0", + snprintf(data, sizeof(data), "%d %d %d %d %d %d %d.0 %d.0", 0, 0, 0, 0, paper_x, paper_y, xdpi, ydpi); int xid = fl_xid(window()); @@ -75,7 +78,7 @@ void GsWidget::setProps() { XA_STRING, 8, PropModeReplace, (unsigned char*) data, strlen(data)); - snprintf(data, 512, "%s %d %d", "Color", + snprintf(data, sizeof(data), "%s %d %d", "Color", (int) BlackPixel(fl_display, DefaultScreen(fl_display)), (int) WhitePixel(fl_display, DefaultScreen(fl_display))); @@ -111,6 +114,8 @@ GsWidget::GsWidget(int X,int Y,int W, int H) : Fl_Widget(X, Y, W, H) { initial_height = H; in_fd = -1; reload_needed = 0; + dsc = NULL; + feeding = 0; } GsWidget::~GsWidget() { @@ -120,22 +125,58 @@ GsWidget::~GsWidget() { } } -int GsWidget::load(char *f) { +int GsWidget::open_file(char *f) { int fd = open(f, O_RDONLY); if (fd == -1) { perror("open"); return 1; } - return load(fd); + return open_file(fd); } -int GsWidget::load(int fd) { +int GsWidget::open_file(int fd) { if (in_fd >= 0 && fd != in_fd) { close (in_fd); } - in_fd = fd; + if (dsc) { + delete(dsc); + } + dsc = new PostscriptDSC(); + + if (dsc->parse(in_fd) == 0) { + int bb_x, bb_y, bb_w, bb_h; + + dsc->get_bounding_box(&bb_x, &bb_y, &bb_w, &bb_h); + paper_x = bb_w; + paper_y = bb_h; + } else { + delete(dsc); + dsc = NULL; + paper_x = 594; // DIN A4 + paper_y = 841; // + } + + page = 0; + + return 0; +} + + +int GsWidget::load() { + pid_t pid; + + if (dsc) { + return load_page(1); + } + + if (in_fd < 0) { + return 1; + } + + lseek(in_fd, 0L, SEEK_SET); + fl_cursor(FL_CURSOR_WAIT); kill_gs(); @@ -146,48 +187,145 @@ int GsWidget::load(int fd) { setProps(); - pid_t pid = fork(); + pid = fork(); if (pid == (pid_t) 0) { - char *argv[16]; - char gvenv[256]; - int d_null = open("/dev/null", O_WRONLY); - - dup2(d_null, STDOUT_FILENO); - close(d_null); dup2(in_fd, STDIN_FILENO); - snprintf(gvenv, 256, "%d %d", (int) fl_xid(window()), (int) offscreen); - - setenv("GHOSTVIEW", gvenv, 1); - argv[0] = "gs"; - argv[1] = "-dSAFER"; - argv[2] = "-dQUIET"; - argv[3] = "-sDEVICE=x11alpha"; - argv[4] = "-dNOPLATFONTS"; - argv[5] = "-"; - argv[6] = NULL; - execvp(argv[0], argv); - perror("exec"); - fprintf(stderr, "Please install ghostscript and make sure 'gs' " - "is in the PATH.\n"); - exit(1); + exec_gs(); } else { gs_pid = pid; - page = 0; + page = 1; } return 0; } +int +GsWidget::load_page(int p) { + pid_t pid; + int pdes[2]; + + if (feeding || in_fd < 0) { + return 1; + } + + if (p < 1 || p > dsc->get_pages()) { + fprintf(stderr, "Page %d not found in document\n", p); + return 1; + } + + fl_cursor(FL_CURSOR_WAIT); + kill_gs(); + + page = p; + + if (!offscreen) { + reload_needed = 1; + return 0; + } + + if (pipe(pdes) < 0) { + perror("pipe"); + return 1; + } + + feeding = 1; + + lseek(in_fd, 0L, SEEK_SET); + setProps(); + + pid = fork(); + if (pid == (pid_t) 0) { + close(in_fd); + close(pdes[1]); + dup2(pdes[0], STDIN_FILENO); + exec_gs(); + } else { + size_t len; + + gs_pid = pid; + + close(pdes[0]); + + lseek(in_fd, 0L, SEEK_SET); + len = dsc->get_setup_len(); + if (fd_copy(pdes[1], in_fd, len) != 0) { + close(pdes[1]); + feeding = 0; + return 1; + } + + lseek(in_fd, dsc->get_page_off(p), SEEK_SET); + len = dsc->get_page_len(p); + if (fd_copy(pdes[1], in_fd, len) != 0) { + close(pdes[1]); + feeding = 0; + return 1; + } + + close(pdes[1]); + } + + feeding = 0; + return 0; +} + +int GsWidget::fd_copy(int to, int from, size_t len) { + size_t n, r; + char buf[1024]; + + n = 0; + while(len > 0) { + Fl::check(); // let fltk do its stuff - otherwise + // gs can't get the GHOSTVIEW property + r = read(from, buf, MIN(sizeof(buf), len)); + + if (r < 0) { + perror("read"); + return 1; + } + + write(to, buf, r); + len -= r; + } + + return 0; +} + +void GsWidget::exec_gs() { + char *argv[16]; + char gvenv[256]; + int d_null = open("/dev/null", O_WRONLY); + + dup2(d_null, STDOUT_FILENO); + + snprintf(gvenv, sizeof(gvenv), "%d %d", + (int) fl_xid(window()), (int) offscreen); + + setenv("GHOSTVIEW", gvenv, 1); + argv[0] = "gs"; + argv[1] = "-dSAFER"; + argv[2] = "-dQUIET"; + argv[3] = "-sDEVICE=x11alpha"; + argv[4] = "-dNOPLATFONTS"; + argv[5] = "-dNOPAUSE"; + argv[6] = "-"; + argv[7] = NULL; + execvp(argv[0], argv); + perror("exec"); + fprintf(stderr, "Please install ghostscript and make sure 'gs' " + "is in the PATH.\n"); + exit(1); +} + int GsWidget::reload() { int ret; if (in_fd >= 0) { - ret = lseek(in_fd, 0L, SEEK_SET); - if (ret == -1) { - perror("lseek"); - return 1; + if (dsc) { + load_page(page); + } else { + load(); } - load(in_fd); return 0; } else { return 1; @@ -195,35 +333,54 @@ int GsWidget::reload() { } int GsWidget::next() { - if (!gs_active()) { - return 1; + if (dsc) { + load_page(page + 1); } else { - fl_cursor(FL_CURSOR_WAIT); - - XEvent e; - e.xclient.type = ClientMessage; - e.xclient.display = fl_display; - e.xclient.window = gs_win; - e.xclient.message_type = atoms[2]; - e.xclient.format = 32; - - XSendEvent(fl_display, gs_win, false, 0, &e); - XFlush(fl_display); + if (!gs_active()) { + return 1; + } else { + fl_cursor(FL_CURSOR_WAIT); + + XEvent e; + e.xclient.type = ClientMessage; + e.xclient.display = fl_display; + e.xclient.window = gs_win; + e.xclient.message_type = atoms[2]; + e.xclient.format = 32; + + XSendEvent(fl_display, gs_win, false, 0, &e); + XFlush(fl_display); + } } return 0; } +int GsWidget::prev() { + if (dsc) { + load_page(page - 1); + return 0; + } else { + return 1; + } +} + int GsWidget::handleX11(int ev) { if (fl_xevent->type == ClientMessage) { gs_win = fl_xevent->xclient.data.l[0]; - if(fl_xevent->xclient.message_type == atoms[3]) { - page++; // PAGE revceived + if(fl_xevent->xclient.message_type == atoms[3]) { // PAGE received damage(FL_DAMAGE_ALL); fl_cursor(FL_CURSOR_DEFAULT); + if (dsc) { + kill_gs(); + } else { + page++; + } } else if(fl_xevent->xclient.message_type == atoms[4] ) { - reload(); // go back to page 1 + if (!dsc) { + reload(); // go back to page 1 + } } return 1; } @@ -260,3 +417,12 @@ int GsWidget::zoom(int p) { return 0; } + +int +GsWidget::get_pages() { + if (dsc) { + return dsc->get_pages(); + } else { + return 0; + } +} diff --git a/src/Makefile.am b/src/Makefile.am index 62c767c..8a0e366 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,6 +7,7 @@ flpsed_SOURCES = \ PSEditText.cxx \ PSEditModel.cxx \ Postscript.cxx \ + PostscriptDSC.cxx \ flpsed.cxx \ util.c @@ -15,6 +16,7 @@ noinst_HEADERS = \ PSEditModel.H \ PSEditText.H \ Postscript.H \ + PostscriptDSC.H \ PSEditor.H \ PSEditWidget.H \ util.h diff --git a/src/PSEditor.H b/src/PSEditor.H index 6bba6d3..a1926ee 100644 --- a/src/PSEditor.H +++ b/src/PSEditor.H @@ -37,9 +37,9 @@ class PSEditor : public PSEditWidget { public: PSEditor(int X,int Y,int W, int H); - int load(FILE *fp); + int open_file(FILE *fp); - int load(const char *f); + int open_file(const char *f); int save(FILE *fp); diff --git a/src/PSEditor.cxx b/src/PSEditor.cxx index 665adfc..a2df43f 100644 --- a/src/PSEditor.cxx +++ b/src/PSEditor.cxx @@ -110,7 +110,7 @@ int PSEditor::handle(int event) { } -int PSEditor::load(FILE *fp) { +int PSEditor::open_file(FILE *fp) { if (tmp_fd) { close(tmp_fd); } @@ -122,11 +122,11 @@ int PSEditor::load(FILE *fp) { } else { mod = 0; loaded = 1; - return GsWidget::load(tmp_fd); + return GsWidget::open_file(tmp_fd); } } -int PSEditor::load(const char *f) { +int PSEditor::open_file(const char *f) { FILE *fp; int ret; @@ -136,7 +136,7 @@ int PSEditor::load(const char *f) { return 1; } - ret = load(fp); + ret = open_file(fp); fclose(fp); return ret; @@ -194,7 +194,7 @@ int PSEditor::import(char *f) { } p2 = new PSParser_2(model); - while (fgets(linebuf, 1024, fp) != NULL) { + while (fgets(linebuf, sizeof(linebuf), fp) != NULL) { p2->parse(linebuf); } diff --git a/src/PostscriptDSC.H b/src/PostscriptDSC.H new file mode 100644 index 0000000..503cb28 --- /dev/null +++ b/src/PostscriptDSC.H @@ -0,0 +1,45 @@ +// +// Copyright 2006 by Johannes Hofmann +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA. +// + +#ifndef POSTSCRIPTDSC_H +#define POSTSCRIPTDSC_H + +class PostscriptDSC { + private: + int bb_x, bb_y, bb_w, bb_h; + size_t setup_len; + int pages; + + size_t *page_off; + size_t *page_len; + + public: + PostscriptDSC(); + ~PostscriptDSC(); + + int parse(int fd); + int get_pages(); + void get_bounding_box(int *x, int *y, int *w, int *h); + size_t get_setup_len(); + size_t get_page_off(int p); + size_t get_page_len(int p); + void print(); +}; + +#endif diff --git a/src/PostscriptDSC.cxx b/src/PostscriptDSC.cxx new file mode 100644 index 0000000..b8b998a --- /dev/null +++ b/src/PostscriptDSC.cxx @@ -0,0 +1,185 @@ +// +// Postscript Document Structure Convention handling routines. +// +// Copyright 2006 by Johannes Hofmann +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA. +// + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include "PostscriptDSC.H" + +PostscriptDSC::PostscriptDSC() { + bb_x = 0; + bb_y = 0; + bb_w = 594; // A4 + bb_h = 841; // + setup_len = 0; + pages = 0; + page_off = NULL; + page_len = NULL; +} + +PostscriptDSC::~PostscriptDSC() { + if (page_off) { + free(page_off); + } + if (page_len) { + free(page_len); + } +} + +int +PostscriptDSC::parse(int fd) { + FILE *fp; + char linebuf[1024]; + int x, y, w, h; + int p1, p2, ps; + int i = 0; + int bb_read = 0; + + bb_x = 0; + bb_y = 0; + bb_w = 594; // A4 + bb_h = 841; // + setup_len = 0; + pages = 0; + + if (page_off) { + free(page_off); + page_off = NULL; + } + if(page_len) { + free(page_len); + page_len = NULL; + } + + fp = fdopen(fd, "r"); + if (!fp) { + perror("fdopen"); + return 1; + } + + while (fgets(linebuf, sizeof(linebuf), fp) != NULL) { + if (!bb_read && + sscanf(linebuf, "%%%%BoundingBox: %d %d %d %d", &x, &y, &w, &h) == 4) { + bb_x = x; + bb_y = y; + bb_w = w; + bb_h = h; + bb_read++; + } else if (strncmp(linebuf, "%%EndSetup", strlen("%%EndSetup")) == 0) { + setup_len = ftello(fp); + } else if (pages == 0 && sscanf(linebuf, "%%%%Pages: %d", &ps) == 1) { + + pages = ps; + page_off = (size_t*) malloc(sizeof(size_t) * pages); + memset(page_off, 0, sizeof(size_t) * pages); + page_len = (size_t*) malloc(sizeof(size_t) * pages); + memset(page_len, 0, sizeof(size_t) * pages); + + } else if (sscanf(linebuf, "%%%%Page: %d %d", &p1, &p2) == 2) { + + if (!page_off || !page_len) { + fprintf(stderr, "Number of pages not defined\n"); + return 1; + } + + if (p1 > pages || p1 < 1) { + fprintf(stderr, "Page %d out of range (1 - %d)\n", p1, pages); + return 1; + } + if (page_off[p1 - 1] != 0) { + fprintf(stderr, "Page %d already defined\n", p1); + return 1; + } + if (p1 > 1 && page_off[p1 - 2] == NULL) { + fprintf(stderr, "Page %d not yet defined\n", p1 - 1); + return 1; + } + + page_off[p1 - 1] = ftello(fp); + if (p1 > 1) { + page_len[p1 - 2] = page_off[p1 - 1] - page_off[p1 - 2]; + } + } + } + + if (page_len && page_off) { + page_len[p1 - 1] = ftello(fp) - page_off[p1 - 1]; + + for (int i=0; i<pages; i++) { + if (page_off[i] == 0 || page_len[i] == 0) { + fprintf(stderr, "Page %d not defined\n", i); + return 1; + } + } + } else { + return 1; + } + + return 0; +} + +void +PostscriptDSC::get_bounding_box(int *x, int *y, int *w, int *h) { + *x = bb_x; + *y = bb_y; + *w = bb_w; + *h = bb_h; +} + +int +PostscriptDSC::get_pages() { + return pages; +} + +size_t +PostscriptDSC::get_setup_len() { + return setup_len; +} + +size_t +PostscriptDSC::get_page_off(int p) { + if (p < 1 || p > pages) { + return 0; + } else { + return page_off[p - 1]; + } +} + +size_t +PostscriptDSC::get_page_len(int p) { + if (p < 1 || p > pages) { + return 0; + } else { + return page_len[p - 1]; + } +} + +void +PostscriptDSC::print() { + int i; + + printf("x %d, y %d, w %d, h %d\n", bb_x, bb_y, bb_w, bb_h); + printf("setup_len %d\n", setup_len); + for (i=0; i<pages; i++) { + printf("p %d, off %d, len %d\n", i, page_off[i], page_len[i]); + } +} + diff --git a/src/flpsed.cxx b/src/flpsed.cxx index b9f879f..1492c5d 100644 --- a/src/flpsed.cxx +++ b/src/flpsed.cxx @@ -24,7 +24,6 @@ #include <stdio.h> #include <string.h> #include <unistd.h> -#include <signal.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> @@ -41,6 +40,7 @@ #include <FL/Fl_Menu_Bar.H> #include <FL/Fl_Menu_Item.H> #include <FL/Fl_Color_Chooser.H> +#include <FL/Fl_Hold_Browser.H> #include "PSEditor.H" #include "util.h" @@ -48,6 +48,7 @@ PSEditor *psed_p = NULL; Fl_Scroll *scroll = NULL; +Fl_Hold_Browser *page_sel = NULL; int xev_handler(int ev) { if (psed_p) { @@ -77,14 +78,43 @@ int check_save(void) { char filename[256] = ""; + +void page_sel_cb(Fl_Widget *w, void *) { + int p = page_sel->value(); + if (p > 0) { + psed_p->load_page(page_sel->value()); + } +} + +void page_sel_fill() { + char buf[64]; + int p = psed_p->get_pages(); + + page_sel->clear(); + + if (p == 0) { + return; + } + + for(int i=1; i<=p; i++) { + snprintf(buf, sizeof(buf), "%d", i); + page_sel->add(buf); + } + page_sel->select(psed_p->get_page()); +} + void open_cb() { if (!check_save()) return; char *file = fl_file_chooser("Open File?", "*.ps", filename); if(file != NULL) { - psed_p->load(file); + + psed_p->open_file(file); + psed_p->load(); + page_sel_fill(); } } + void import_pdf_cb() { char *file; FILE *p; @@ -103,7 +133,9 @@ void import_pdf_cb() { p = pexecvp("pdftops", args, &pid, "r"); if (p) { - psed_p->load(p); + psed_p->open_file(p); + psed_p->load(); + page_sel_fill(); fclose(p); waitpid(pid, &status, 0); if (WEXITSTATUS(status) == 127 || WEXITSTATUS(status) == 126) { @@ -163,11 +195,13 @@ void import_cb() { } void first_cb() { - psed_p->reload(); + psed_p->load(); + page_sel->select(psed_p->get_page()); } void next_cb() { psed_p->next(); + page_sel->select(psed_p->get_page()); } void quit_cb() { @@ -211,7 +245,7 @@ void print_cb() { void about_cb() { fl_message("flpsed -- a pseudo PostScript editor\n" "Version %s\n\n" - "(c) Johannes Hofmann 2004, 2005\n\n" + "(c) Johannes Hofmann 2004-2006\n\n" "PostScript is a registered trademark of Adobe Systems", VERSION); } @@ -524,8 +558,10 @@ int main(int argc, char** argv) { color_b->shortcut(FL_ALT + 'c'); color_b->tooltip("Text Color"); props.end(); - - scroll = new Fl_Scroll(0, 55, win->w(), win->h()-55); + page_sel = new Fl_Hold_Browser(0, 55, 40, win->h()-55); + page_sel->callback(page_sel_cb); + page_sel->end(); + scroll = new Fl_Scroll(40, 55, win->w()-40, win->h()-55); psed_p = new PSEditor(0, 0, 700, 900); psed_p->property_changed_callback(property_changed_cb); scroll->end(); @@ -546,9 +582,9 @@ int main(int argc, char** argv) { } if (in_fp) { - sleep(1); // this seems to be necessary on fast systems to make the - // GHOSTVIEW property available to ghostsscript. - psed_p->load(in_fp); + psed_p->open_file(in_fp); + psed_p->load(); + page_sel_fill(); fclose(in_fp); } |