diff --git a/CMake/options.cmake b/CMake/options.cmake index ac7fa5bb68..2bb439ae63 100644 --- a/CMake/options.cmake +++ b/CMake/options.cmake @@ -463,18 +463,23 @@ endif (OPTION_USE_PANGO) ####################################################################### if (X11_Xft_FOUND AND OPTION_USE_PANGO) pkg_check_modules(PANGOXFT pangoxft) + pkg_check_modules(PANGOCAIRO pangocairo) + pkg_check_modules(CAIRO cairo) # message (STATUS "PANGOXFT_FOUND=" ${PANGOXFT_FOUND}) - if (PANGOXFT_FOUND) - include_directories (${PANGOXFT_INCLUDE_DIRS}) + if (PANGOXFT_FOUND AND PANGOCAIRO_FOUND AND CAIRO_FOUND) + include_directories (${PANGOXFT_INCLUDE_DIRS} ${CAIRO_INCLUDE_DIRS}) find_library(HAVE_LIB_PANGO pango-1.0 ${CMAKE_LIBRARY_PATH}) find_library(HAVE_LIB_PANGOXFT pangoxft-1.0 ${CMAKE_LIBRARY_PATH}) + find_library(HAVE_LIB_PANGOCAIRO pangocairo-1.0 ${CMAKE_LIBRARY_PATH}) + find_library(HAVE_LIB_CAIRO cairo ${CMAKE_LIBRARY_PATH}) + find_library(HAVE_LIB_GOBJECT gobject-2.0 ${CMAKE_LIBRARY_PATH}) set (USE_PANGO TRUE) - list (APPEND FLTK_LDLIBS -lpango-1.0 -lpangoxft-1.0 -lgobject-2.0) + list (APPEND FLTK_LDLIBS -lpango-1.0 -lpangoxft-1.0 -lpangocairo-1.0 -lcairo -lgobject-2.0) if (APPLE) get_filename_component(PANGO_L_PATH ${HAVE_LIB_PANGO} PATH) set (LDFLAGS "${LDFLAGS} -L${PANGO_L_PATH}") endif (APPLE) - else(PANGOXFT_FOUND) + else(PANGOXFT_FOUND AND PANGOCAIRO_FOUND AND CAIRO_FOUND) # this covers Debian, Ubuntu, FreeBSD, NetBSD, Darwin if (APPLE AND OPTION_APPLE_X11) @@ -511,7 +516,7 @@ if (X11_Xft_FOUND AND OPTION_USE_PANGO) include_directories (${PANGO_H_PREFIX}/pango-1.0 ${GLIB_H_PATH} ${PANGOLIB_DIR}/glib-2.0/include) list (APPEND FLTK_LDLIBS -lpango-1.0 -lpangoxft-1.0 -lgobject-2.0) endif (HAVE_LIB_PANGO AND HAVE_LIB_PANGOXFT AND HAVE_LIB_GOBJECT) -endif (PANGOXFT_FOUND) +endif (PANGOXFT_FOUND AND PANGOCAIRO_FOUND AND CAIRO_FOUND) endif (X11_Xft_FOUND AND OPTION_USE_PANGO) if (OPTION_USE_XFT) diff --git a/FL/Fl_PostScript.H b/FL/Fl_PostScript.H index 489916d6bf..dc31cb1166 100644 --- a/FL/Fl_PostScript.H +++ b/FL/Fl_PostScript.H @@ -15,7 +15,7 @@ // /** \file Fl_PostScript.H - \brief declaration of classes Fl_PostScript_Graphics_Driver, Fl_PostScript_File_Device. + \brief declaration of classes Fl_PostScript_File_Device and Fl_EPS_File_Surface. */ #ifndef Fl_PostScript_H @@ -25,204 +25,23 @@ #include #include -/* Signature of Fl_PostScript::close_command() functions passed as parameters. */ +/** Signature of Fl_PostScript::close_command() functions passed as parameters. */ extern "C" { - typedef int (Fl_PostScript_Close_Command)(FILE *); + typedef int (*Fl_PostScript_Close_Command)(FILE *); } -/** - \cond DriverDev - \addtogroup DriverDeveloper - \{ - */ - -/** - PostScript graphical backend. - */ -class FL_EXPORT Fl_PostScript_Graphics_Driver : public Fl_Graphics_Driver { -private: - void transformed_draw_extra(const char* str, int n, double x, double y, int w, bool rtl); - void *prepare_rle85(); - void write_rle85(uchar b, void *data); - void close_rle85(void *data); - void *prepare85(); - void write85(void *data, const uchar *p, int len); - void close85(void *data); - int scale_for_image_(Fl_Image *img, int XP, int YP, int WP, int HP,int cx, int cy); -protected: - uchar **mask_bitmap() {return &mask;} -public: - Fl_PostScript_Graphics_Driver(); -#ifndef FL_DOXYGEN - enum SHAPE{NONE=0, LINE, LOOP, POLYGON, POINTS}; - - class Clip { - public: - int x, y, w, h; - Clip *prev; - }; - Clip * clip_; - - int lang_level_; - int gap_; - int pages_; - - double width_; - double height_; - - int shape_; - int linewidth_;// need for clipping, lang level 1-2 - int linestyle_;// - int interpolate_; //interpolation of images - unsigned char cr_,cg_,cb_; - char linedash_[256];//should be enough - void concat(); // transform ror scalable dradings... - void reconcat(); //invert - void recover(); //recovers the state after grestore (such as line styles...) - void reset(); - - uchar * mask; - int mx; // width of mask; - int my; // mask lines - //Fl_Color bg_; - Fl_PostScript_Close_Command* close_cmd_; - int page_policy_; - int nPages; - int orientation_; - - float scale_x; - float scale_y; - float angle; - int left_margin; - int top_margin; - - FILE *output; - double pw_, ph_; - - uchar bg_r, bg_g, bg_b; - int start_postscript (int pagecount, enum Fl_Paged_Device::Page_Format format, enum Fl_Paged_Device::Page_Layout layout); - /* int alpha_mask(const uchar * data, int w, int h, int D, int LD=0); - */ - void transformed_draw(const char* s, int n, double x, double y); //precise text placing - void transformed_draw(const char* s, double x, double y); - int alpha_mask(const uchar * data, int w, int h, int D, int LD=0); - - enum Fl_Paged_Device::Page_Format page_format_; - char *ps_filename_; - - void page_policy(int p); - int page_policy(){return page_policy_;}; - void close_command(Fl_PostScript_Close_Command* cmd){close_cmd_=cmd;}; - FILE * file() {return output;}; - //void orientation (int o); - //Fl_PostScript_Graphics_Driver(FILE *o, int lang_level, int pages = 0); // ps (also multi-page) constructor - //Fl_PostScript_Graphics_Driver(FILE *o, int lang_level, int x, int y, int w, int h); //eps constructor - void interpolate(int i){interpolate_=i;}; - int interpolate(){return interpolate_;} - - void page(double pw, double ph, int media = 0); - void page(int format); -#endif // FL_DOXYGEN - - // implementation of drawing methods - void color(Fl_Color c); - void color(uchar r, uchar g, uchar b); - - void push_clip(int x, int y, int w, int h); - int clip_box(int x, int y, int w, int h, int &X, int &Y, int &W, int &H); - int not_clipped(int x, int y, int w, int h); - void push_no_clip(); - void pop_clip(); - - void line_style(int style, int width=0, char* dashes=0); - - void rect(int x, int y, int w, int h); - void rectf(int x, int y, int w, int h); - - void xyline(int x, int y, int x1); - void xyline(int x, int y, int x1, int y2); - void xyline(int x, int y, int x1, int y2, int x3); - - void yxline(int x, int y, int y1); - void yxline(int x, int y, int y1, int x2); - void yxline(int x, int y, int y1, int x2, int y3); - - void line(int x1, int y1, int x2, int y2); - void line(int x1, int y1, int x2, int y2, int x3, int y3); - - void loop(int x0, int y0, int x1, int y1, int x2, int y2); - void loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3); - void polygon(int x0, int y0, int x1, int y1, int x2, int y2); - void polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3); - void point(int x, int y); - - void begin_points(); - void begin_line(); - void begin_loop(); - void begin_polygon(); - void vertex(double x, double y); - void curve(double x, double y, double x1, double y1, double x2, double y2, double x3, double y3); - void circle(double x, double y, double r); - void arc(double x, double y, double r, double start, double a); - void arc(int x, int y, int w, int h, double a1, double a2); - void pie(int x, int y, int w, int h, double a1, double a2); - void end_points(); - void end_line(); - void end_loop(); - void end_polygon(); - void begin_complex_polygon(){begin_polygon();}; - void gap(){gap_=1;}; - void end_complex_polygon(){end_polygon();}; - void transformed_vertex(double x, double y); - - void draw_image(const uchar* d, int x,int y,int w,int h, int delta=3, int ldelta=0); - void draw_image_mono(const uchar* d, int x,int y,int w,int h, int delta=1, int ld=0); - void draw_image(Fl_Draw_Image_Cb call, void* data, int x,int y, int w, int h, int delta=3); - void draw_image_mono(Fl_Draw_Image_Cb call, void* data, int x,int y, int w, int h, int delta=1); - - void draw(const char* s, int nBytes, int x, int y) {transformed_draw(s,nBytes,x,y); }; - void draw(const char* s, int nBytes, float x, float y) {transformed_draw(s,nBytes,x,y); }; - void draw(int angle, const char *str, int n, int x, int y); - void rtl_draw(const char* s, int n, int x, int y); - void font(int face, int size); - double width(const char *, int); - double width(unsigned int u); - void text_extents(const char *c, int n, int &dx, int &dy, int &w, int &h); - int height(); - int descent(); - void draw_pixmap(Fl_Pixmap * pxm,int XP, int YP, int WP, int HP, int cx, int cy); - void draw_bitmap(Fl_Bitmap * bitmap,int XP, int YP, int WP, int HP, int cx, int cy); - void draw_rgb(Fl_RGB_Image * rgb,int XP, int YP, int WP, int HP, int cx, int cy); - /** Shields output PostScript data from modifications of the current locale. - It typically avoids PostScript errors caused if the current locale uses comma instead of dot - as "decimal point". - \param format directives controlling output PostScript data - \return value returned by vfprintf() call - */ - int clocale_printf(const char *format, ...); - ~Fl_PostScript_Graphics_Driver(); - // --- - Fl_Bitmask create_bitmask(int w, int h, const uchar *array) { return 0L; } - virtual int has_feature(driver_feature feature_mask) { return feature_mask & PRINTER; } - - int start_eps(int width, int height); - void ps_origin(int x, int y); - void ps_translate(int, int); - void ps_untranslate(); -}; - -/** - \} - \endcond - */ +class Fl_PostScript_Graphics_Driver; /** To send graphical output to a PostScript file. This class is used exactly as the Fl_Printer class except for the begin_job() call, two variants of which are usable and allow to specify what page format and layout are desired. - PostScript text uses vectorial fonts when using the FLTK standard fonts - and the latin alphabet or a few other characters listed in the following table. + Processing of text: Text uses vectorial fonts under the X11 + pango platform. + With other platforms, only text restricted to the Latin alphabet (and a few other characters + listed in the table below) and to FLTK standard fonts is vectorized. All other unicode characters + or all other fonts (FL_FREE_FONT and above) are output as a bitmap. + FLTK standard fonts are output using the corresponding PostScript standard fonts. The latin alphabet means all unicode characters between U+0020 and U+017F, or, in other words, the ASCII, Latin-1 Supplement and Latin Extended-A charts. @@ -241,37 +60,31 @@ public:
U+2018quoteleft/U+2044fractionU+FB02fl
U+2019quoterightU+20ACEuroU+F8FFapple (Mac OS only)
-
All other unicode characters or all other fonts (FL_FREE_FONT and above) are output as a bitmap. -
FLTK standard fonts are output using the corresponding PostScript standard fonts. - + */ class FL_EXPORT Fl_PostScript_File_Device : public Fl_Paged_Device { protected: /** \brief Returns the PostScript driver of this drawing surface. */ - Fl_PostScript_Graphics_Driver *driver(); + inline Fl_PostScript_Graphics_Driver *driver() { return (Fl_PostScript_Graphics_Driver*)Fl_Surface_Device::driver(); } public: - /** - @brief The constructor. - */ - Fl_PostScript_File_Device(); - /** - @brief The destructor. - */ + /** The constructor. */ + Fl_PostScript_File_Device(); + /** The destructor. */ ~Fl_PostScript_File_Device(); /** Don't use with this class. */ int begin_job(int pagecount, int* from, int* to, char **perr_message); - /** - @brief Begins the session where all graphics requests will go to a local PostScript file. - * - Opens a file dialog entitled with Fl_PostScript_File_Device::file_chooser_title to select an output PostScript file. + /** Begins the session where all graphics requests will go to a local PostScript file. + Opens a file dialog to select an output PostScript file. + This member function makes end_job() close the resulting PostScript file and display an + alert message with fl_alert() in case of any output error. @param pagecount The total number of pages to be created. Use 0 if this number is unknown when this function is called. @param format Desired page format. @param layout Desired page layout. @return 0 if OK, 1 if user cancelled the file dialog, 2 if fopen failed on user-selected output file. */ - int begin_job(int pagecount = 0, enum Fl_Paged_Device::Page_Format format = Fl_Paged_Device::A4, + int begin_job(int pagecount = 0, enum Fl_Paged_Device::Page_Format format = Fl_Paged_Device::A4, enum Fl_Paged_Device::Page_Layout layout = Fl_Paged_Device::PORTRAIT); /** Synonym of begin_job(). For API compatibility with FLTK 1.3.x */ @@ -279,9 +92,9 @@ public: enum Fl_Paged_Device::Page_Layout layout = Fl_Paged_Device::PORTRAIT) { return begin_job(pagecount, format, layout); } - /** - @brief Begins the session where all graphics requests will go to FILE pointer. - * + /** Begins the session where all graphics requests will go to FILE pointer. + This member function prevents end_job() from closing \p ps_output, so the user can check with \p ferror(ps_output) + for output errors. @param ps_output A writable FILE pointer that will receive PostScript output and that should not be closed until after end_job() has been called. @param pagecount The total number of pages to be created. Use 0 if this number is unknown when this function is called. @@ -308,11 +121,14 @@ public: void translate(int x, int y); void untranslate(void); int end_page (void); + /** Finishes all PostScript output. + This also closes the underlying \p fclose(file()) unless close_command() was used to set another function. + */ void end_job(void); - /** \brief Label of the PostScript file chooser window */ + /** Label of the PostScript file chooser window */ static const char *file_chooser_title; /** Returns the underlying FILE* receiving all PostScript data */ - FILE *file() { return driver()->file(); } + FILE *file(); }; /** Encapsulated PostScript drawing surface. @@ -329,13 +145,10 @@ public: surface->draw_decorated_window(win); Fl_Surface_Device::pop_current(); delete surface; // the .eps file is not complete until the destructor was run - fclose(eps); } \endcode */ class FL_EXPORT Fl_EPS_File_Surface : public Fl_Widget_Surface { -private: - void complete_(); protected: /** Returns the PostScript driver of this drawing surface. */ inline Fl_PostScript_Graphics_Driver *driver() { return (Fl_PostScript_Graphics_Driver*)Fl_Surface_Device::driver(); } @@ -343,27 +156,37 @@ public: /** Constructor. \param width,height Width and height of the EPS drawing area - \param eps A writable FILE pointer where the Encapsulated PostScript data will be sent - \param background Color expected to cover the background of the EPS drawing area. - This parameter affects only the drawing of transparent Fl_RGB_Image objects: - transparent areas of RGB images are blended with the \p background color. + \param eps_output A writable FILE pointer where the Encapsulated PostScript data will be sent + \param background Color expected to cover the background of the EPS drawing area. + This parameter affects only the drawing of transparent Fl_RGB_Image objects: + transparent areas of RGB images are blended with the \p background color. + Under the X11 + pango platform, transparent RGB images are correctly blended to their background, + thus this parameter has no effect. + \param closef If not NULL, the destructor or close() will call \p closef(eps_output) after all + EPS data has been sent. If NULL, \p fclose(eps_output) is called instead. This allows to close the FILE + pointer by, e.g., \p pclose, or, using a function such as \p "int keep_open(FILE*){return 0;}", to keep it open after + completion of all output to \p eps_output. Function \p closef should return non zero to indicate an error. */ - Fl_EPS_File_Surface(int width, int height, FILE *eps, Fl_Color background = FL_WHITE); + Fl_EPS_File_Surface(int width, int height, FILE *eps_output, + Fl_Color background = FL_WHITE, Fl_PostScript_Close_Command closef = NULL); /** Destructor. - The underlying FILE pointer remains open after destruction of the Fl_EPS_File_Surface object - unless close() was called. + By default, the destructor closes with function \p fclose() the underlying FILE. See the constructor for how + to close it differently or to keep it open. Use close() before object destruction to receive the status code + of output operations. If close() is not used and if EPS output results in error, the destructor displays an alert message + with fl_alert(). */ ~Fl_EPS_File_Surface(); virtual int printable_rect(int *w, int *h); /** Returns the underlying FILE pointer */ - FILE *file() { return driver() ? driver()->output : NULL; } + FILE *file(); virtual void origin(int x, int y); virtual void origin(int *px, int *py); virtual void translate(int x, int y); virtual void untranslate(); - /** Closes using fclose() the underlying FILE pointer. - The only operation possible with the Fl_EPS_File_Surface object after calling close() is its destruction. */ + /** Completes all EPS output. + The only operation possible with the Fl_EPS_File_Surface object after calling close() is its destruction. + \return The status code of output operations to the FILE object. 0 indicates success. */ int close(); }; diff --git a/configure.ac b/configure.ac index 800ef780a7..d2202c469f 100644 --- a/configure.ac +++ b/configure.ac @@ -1108,7 +1108,14 @@ case $host_os_gui in if test x$PKGCONFIG != x; then CXXFLAGS="`$PKGCONFIG --cflags pangoxft` $CXXFLAGS" LIBS="`$PKGCONFIG --libs pangoxft` $LIBS" - else + CXXFLAGS="`$PKGCONFIG --cflags pangocairo` $CXXFLAGS" + LIBS="`$PKGCONFIG --libs pangocairo` $LIBS" + case $host_os in + darwin*) + LDFLAGS="-L/opt/sw/lib -L/sw/lib $LDFLAGS" + ;; + esac + else case $host_os in linux*) CXXFLAGS="-I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include $CXXFLAGS" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d5eee5dc82..ff15cf04a3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -529,10 +529,10 @@ if (HAVE_XRENDER) endif (HAVE_XRENDER) if (USE_PANGO) - list (APPEND OPTIONAL_LIBS ${HAVE_LIB_PANGO} ${HAVE_LIB_PANGOXFT}) - if (NOT APPLE) - list (APPEND OPTIONAL_LIBS ${HAVE_LIB_GOBJECT} ) - endif (NOT APPLE) + list (APPEND OPTIONAL_LIBS ${HAVE_LIB_PANGO} ${HAVE_LIB_PANGOXFT} ${HAVE_LIB_PANGOCAIRO} ${HAVE_LIB_CAIRO} ${HAVE_LIB_GOBJECT}) + #if (NOT APPLE) + # list (APPEND OPTIONAL_LIBS ${HAVE_LIB_GOBJECT} ) + #endif (NOT APPLE) endif (USE_PANGO) if (USE_XFT) diff --git a/src/Fl_Printer.cxx b/src/Fl_Printer.cxx index 8c587afb92..0155923326 100644 --- a/src/Fl_Printer.cxx +++ b/src/Fl_Printer.cxx @@ -2,7 +2,7 @@ // Encompasses platform-specific printing-support code and // PostScript output code for the Fast Light Tool Kit (FLTK). // -// Copyright 2010-2016 by Bill Spitzak and others. +// Copyright 2010-2020 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -80,15 +80,19 @@ void Fl_PostScript_File_Device::translate(int x, int y) {} void Fl_PostScript_File_Device::untranslate(void) {} int Fl_PostScript_File_Device::end_page (void) {return 1;} void Fl_PostScript_File_Device::end_job(void) {} +FILE* Fl_PostScript_File_Device::file() {return NULL;} Fl_PostScript_File_Device::~Fl_PostScript_File_Device(void) {} -Fl_EPS_File_Surface::Fl_EPS_File_Surface(int width, int height, FILE *eps, Fl_Color background) : Fl_Widget_Surface(NULL) {} +Fl_EPS_File_Surface::Fl_EPS_File_Surface(int width, int height, FILE *eps_output, + Fl_Color background, Fl_PostScript_Close_Command closef) : Fl_Widget_Surface(NULL) {} Fl_EPS_File_Surface::~Fl_EPS_File_Surface() {} void Fl_EPS_File_Surface::origin(int, int) {} void Fl_EPS_File_Surface::origin(int*, int*) {} int Fl_EPS_File_Surface::printable_rect(int*, int*) {return 1;} void Fl_EPS_File_Surface::translate(int, int) {} void Fl_EPS_File_Surface::untranslate() {} +FILE* Fl_EPS_File_Surface::file() {return NULL;} +int Fl_EPS_File_Surface::close() {return 1;} #else diff --git a/src/Fl_x.cxx b/src/Fl_x.cxx index 9472036796..31a76f4e9a 100644 --- a/src/Fl_x.cxx +++ b/src/Fl_x.cxx @@ -3087,7 +3087,7 @@ void Fl_X11_Window_Driver::show() { } -//#define USE_PRINT_BUTTON 1 +#define USE_PRINT_BUTTON 1 #ifdef USE_PRINT_BUTTON // to test the Fl_Printer class creating a "Print front window" button in a separate window @@ -3112,6 +3112,7 @@ void printFront(Fl_Widget *o, void *data) scale = (float)w/ww; if ((float)h/wh < scale) scale = (float)h/wh; printer.scale(scale, scale); + printer.printable_rect(&w, &h); } // #define ROTATE 20.0 @@ -3120,11 +3121,12 @@ void printFront(Fl_Widget *o, void *data) printer.printable_rect(&w, &h); printer.origin(w/2, h/2 ); printer.rotate(ROTATE); - printer.print_widget( win, - win->w()/2, - win->h()/2 ); + printer.print_window( win, - win->w()/2, - win->h()/2); //printer.print_window_part( win, 0,0, win->w(), win->h(), - win->w()/2, - win->h()/2 ); #else - printer.print_window(win); - //printer.print_window_part( win, 0,0, win->w(), win->h(), 0,0 ); + printer.origin(w/2, h/2 ); + printer.print_window(win, -ww/2, -wh/2); + //printer.print_window_part( win, 0,0, win->w(), win->h(), -ww/2, -wh/2 ); #endif printer.end_page(); diff --git a/src/drivers/Posix/Fl_Posix_Printer_Driver.cxx b/src/drivers/Posix/Fl_Posix_Printer_Driver.cxx index dbb966a767..5fcb4645ae 100644 --- a/src/drivers/Posix/Fl_Posix_Printer_Driver.cxx +++ b/src/drivers/Posix/Fl_Posix_Printer_Driver.cxx @@ -1,7 +1,7 @@ // // PostScript priting support for the Fast Light Tool Kit (FLTK). // -// Copyright 2010-2016 by Bill Spitzak and others. +// Copyright 2010-2020 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -19,6 +19,7 @@ #if defined(FL_CFG_PRN_PS) && !defined(FL_NO_PRINT_SUPPORT) #include +#include "../PostScript/Fl_PostScript_Graphics_Driver.H" #include #include @@ -326,7 +327,7 @@ int Fl_Posix_Printer_Driver::begin_job(int pages, int *firstpage, int *lastpage, return 2; } ps->close_command(pclose); - this->set_current(); + Fl_Surface_Device::push_current(this); return ps->start_postscript(pages, format, layout); // start printing } diff --git a/src/drivers/PostScript/Fl_PostScript.cxx b/src/drivers/PostScript/Fl_PostScript.cxx index 5adf20562c..958bbd1a63 100644 --- a/src/drivers/PostScript/Fl_PostScript.cxx +++ b/src/drivers/PostScript/Fl_PostScript.cxx @@ -20,57 +20,32 @@ #include #include #include +#include "Fl_PostScript_Graphics_Driver.H" #include #include #include "../../Fl_System_Driver.H" #include +#include #include #include +#if USE_PANGO +#include // for M_PI +#include +#include +#include "../Xlib/Fl_Xlib_Graphics_Driver.H" +#endif const char *Fl_PostScript_File_Device::file_chooser_title = "Select a .ps file"; -/** - \cond DriverDev - \addtogroup DriverDeveloper - \{ - */ - -/** - \brief The constructor. - */ -Fl_PostScript_Graphics_Driver::Fl_PostScript_Graphics_Driver(void) -{ - close_cmd_ = 0; - //lang_level_ = 3; - lang_level_ = 2; - mask = 0; - ps_filename_ = NULL; - scale_x = scale_y = 1.; - bg_r = bg_g = bg_b = 255; -} - -/** \brief The destructor. */ -Fl_PostScript_Graphics_Driver::~Fl_PostScript_Graphics_Driver() { - if(ps_filename_) free(ps_filename_); -} - -/** - \} - \endcond - */ - - Fl_PostScript_File_Device::Fl_PostScript_File_Device(void) { Fl_Surface_Device::driver( new Fl_PostScript_Graphics_Driver() ); } -Fl_PostScript_Graphics_Driver *Fl_PostScript_File_Device::driver() -{ - return (Fl_PostScript_Graphics_Driver*)Fl_Surface_Device::driver(); +FILE *Fl_PostScript_File_Device::file() { + return driver()->file(); } - int Fl_PostScript_File_Device::begin_job (int pagecount, enum Fl_Paged_Device::Page_Format format, enum Fl_Paged_Device::Page_Layout layout) { @@ -86,7 +61,7 @@ int Fl_PostScript_File_Device::begin_job (int pagecount, enum Fl_Paged_Device::P if(ps->output == NULL) return 2; ps->ps_filename_ = fl_strdup(fnfc.filename()); ps->start_postscript(pagecount, format, layout); - this->set_current(); + Fl_Surface_Device::push_current(this); return 0; } @@ -105,7 +80,7 @@ int Fl_PostScript_File_Device::begin_job (FILE *ps_output, int pagecount, ps->ps_filename_ = NULL; ps->start_postscript(pagecount, format, layout); ps->close_command(dont_close); // so that end_job() doesn't close the file - this->set_current(); + Fl_Surface_Device::push_current(this); return 0; } @@ -125,6 +100,158 @@ Fl_PostScript_File_Device::~Fl_PostScript_File_Device() { \{ */ +static const int dashes_flat[5][7]={ +{-1,0,0,0,0,0,0}, +{3,1,-1,0,0,0,0}, +{1,1,-1,0,0,0,0}, +{3,1,1,1,-1,0,0}, +{3,1,1,1,1,1,-1} +}; + +//yeah, hack... +static const double dashes_cap[5][7]={ +{-1,0,0,0,0,0,0}, +{2,2,-1,0,0,0,0}, +{0.01,1.99,-1,0,0,0,0}, +{2,2,0.01,1.99,-1,0,0}, +{2,2,0.01,1.99,0.01,1.99,-1} +}; + +/** + \brief The constructor. + */ +Fl_PostScript_Graphics_Driver::Fl_PostScript_Graphics_Driver(void) +{ + close_cmd_ = 0; + //lang_level_ = 3; + lang_level_ = 2; + mask = 0; + ps_filename_ = NULL; + scale_x = scale_y = 1.; + bg_r = bg_g = bg_b = 255; + clip_ = NULL; +} + +/** \brief The destructor. */ +Fl_PostScript_Graphics_Driver::~Fl_PostScript_Graphics_Driver() { + if(ps_filename_) free(ps_filename_); +} + + +#if ! USE_PANGO +static const char *_fontNames[] = { +"Helvetica2B", +"Helvetica-Bold2B", +"Helvetica-Oblique2B", +"Helvetica-BoldOblique2B", +"Courier2B", +"Courier-Bold2B", +"Courier-Oblique2B", +"Courier-BoldOblique2B", +"Times-Roman2B", +"Times-Bold2B", +"Times-Italic2B", +"Times-BoldItalic2B", +"Symbol", +"Courier2B", +"Courier-Bold2B", +"ZapfDingbats" +}; +#endif + +void Fl_PostScript_Graphics_Driver::font(int f, int s) { + Fl_Graphics_Driver& driver = Fl_Graphics_Driver::default_driver(); + driver.font(f,s); // Use display fonts for font measurement + Fl_Graphics_Driver::font(f, s); + Fl_Font_Descriptor *desc = driver.font_descriptor(); + this->font_descriptor(desc); +#if ! USE_PANGO + if (f < FL_FREE_FONT) { + fprintf(output, "/%s SF\n" , _fontNames[f]); + float ps_size = driver.scale_font_for_PostScript(desc, s); + clocale_printf("%.1f FS\n", ps_size); + } +#endif +} + +double Fl_PostScript_Graphics_Driver::width(const char *s, int n) { + return Fl_Graphics_Driver::default_driver().width(s, n); +} + +double Fl_PostScript_Graphics_Driver::width(unsigned u) { + return Fl_Graphics_Driver::default_driver().width(u); +} + +int Fl_PostScript_Graphics_Driver::height() { + return Fl_Graphics_Driver::default_driver().height(); +} + +int Fl_PostScript_Graphics_Driver::descent() { + return Fl_Graphics_Driver::default_driver().descent(); +} + +void Fl_PostScript_Graphics_Driver::text_extents(const char *c, int n, int &dx, int &dy, int &w, int &h) { + Fl_Graphics_Driver::default_driver().text_extents(c, n, dx, dy, w, h); +} + + +void Fl_PostScript_Graphics_Driver::color(Fl_Color c) { + Fl::get_color(c, cr_, cg_, cb_); + color(cr_, cg_, cb_); +} + +void Fl_PostScript_Graphics_Driver::point(int x, int y){ + rectf(x,y,1,1); +} + +int Fl_PostScript_Graphics_Driver::not_clipped(int x, int y, int w, int h) { + if (!clip_) return 1; + if (clip_->w < 0) return 1; + int X = 0, Y = 0, W = 0, H = 0; + clip_box(x, y, w, h, X, Y, W, H); + if (W) return 1; + return 0; +} + +int Fl_PostScript_Graphics_Driver::clip_box(int x, int y, int w, int h, int &X, int &Y, int &W, int &H) { + if (!clip_) { + X = x; Y = y; W = w; H = h; + return 0; + } + if (clip_->w < 0) { + X = x; Y = y; W = w; H = h; + return 1; + } + int ret = 0; + if (x > (X=clip_->x)) {X=x; ret=1;} + if (y > (Y=clip_->y)) {Y=y; ret=1;} + if ((x+w) < (clip_->x+clip_->w)) { + W=x+w-X; + + ret=1; + + }else + W = clip_->x + clip_->w - X; + if(W<0){ + W=0; + return 1; + } + if ((y+h) < (clip_->y+clip_->h)) { + H=y+h-Y; + ret=1; + }else + H = clip_->y + clip_->h - Y; + if(H<0){ + W=0; + H=0; + return 1; + } + return ret; +} + + +#if ! USE_PANGO + int Fl_PostScript_Graphics_Driver::clocale_printf(const char *format, ...) { va_list args; @@ -583,8 +710,8 @@ int Fl_PostScript_Graphics_Driver::start_postscript (int pagecount, } int Fl_PostScript_Graphics_Driver::start_eps (int width, int height) { - width_ = width; - height_ = height; + pw_ = width; + ph_ = height; fputs("%!PS-Adobe-3.0 EPSF-3.0\n", output); fputs("%%Creator: (FLTK)\n", output); fprintf(output,"%%%%BoundingBox: 1 1 %d %d\n", width, height); @@ -610,7 +737,7 @@ int Fl_PostScript_Graphics_Driver::start_eps (int width, int height) { reset(); nPages=0; fprintf(output, "GS\n"); - clocale_printf( "%g %g TR\n", (double)0, height_); + clocale_printf( "%g %g TR\n", (double)0, ph_); fprintf(output, "1 -1 SC\n"); line_style(0); fprintf(output, "GS GS\n"); @@ -854,29 +981,6 @@ void Fl_PostScript_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int fprintf(output, "GR\n"); } -void Fl_PostScript_Graphics_Driver::point(int x, int y){ - rectf(x,y,1,1); -} - -static const int dashes_flat[5][7]={ -{-1,0,0,0,0,0,0}, -{3,1,-1,0,0,0,0}, -{1,1,-1,0,0,0,0}, -{3,1,1,1,-1,0,0}, -{3,1,1,1,1,1,-1} -}; - - -//yeah, hack... -static const double dashes_cap[5][7]={ -{-1,0,0,0,0,0,0}, -{2,2,-1,0,0,0,0}, -{0.01,1.99,-1,0,0,0,0}, -{2,2,0.01,1.99,-1,0,0}, -{2,2,0.01,1.99,0.01,1.99,-1} -}; - - void Fl_PostScript_Graphics_Driver::line_style(int style, int width, char* dashes){ //line_styled_=1; @@ -935,84 +1039,26 @@ void Fl_PostScript_Graphics_Driver::line_style(int style, int width, char* dashe fprintf(output, "] 0 setdash\n"); } -static const char *_fontNames[] = { -"Helvetica2B", -"Helvetica-Bold2B", -"Helvetica-Oblique2B", -"Helvetica-BoldOblique2B", -"Courier2B", -"Courier-Bold2B", -"Courier-Oblique2B", -"Courier-BoldOblique2B", -"Times-Roman2B", -"Times-Bold2B", -"Times-Italic2B", -"Times-BoldItalic2B", -"Symbol", -"Courier2B", -"Courier-Bold2B", -"ZapfDingbats" -}; - -void Fl_PostScript_Graphics_Driver::font(int f, int s) { - Fl_Graphics_Driver& driver = Fl_Graphics_Driver::default_driver(); - driver.font(f,s); // Use display fonts for font measurement - Fl_Graphics_Driver::font(f, s); - Fl_Font_Descriptor *desc = driver.font_descriptor(); - this->font_descriptor(desc); - if (f < FL_FREE_FONT) { - fprintf(output, "/%s SF\n" , _fontNames[f]); - float ps_size = driver.scale_font_for_PostScript(desc, s); - clocale_printf("%.1f FS\n", ps_size); +void Fl_PostScript_Graphics_Driver::color(unsigned char r, unsigned char g, unsigned char b) { + Fl_Graphics_Driver::color( fl_rgb_color(r, g, b) ); + cr_ = r; cg_ = g; cb_ = b; + if (r == g && g == b) { + double gray = r/255.0; + clocale_printf("%g GL\n", gray); + } else { + double fr, fg, fb; + fr = r/255.0; + fg = g/255.0; + fb = b/255.0; + clocale_printf("%g %g %g SRGB\n", fr , fg , fb); } } -double Fl_PostScript_Graphics_Driver::width(const char *s, int n) { - return Fl_Graphics_Driver::default_driver().width(s, n); -} - -double Fl_PostScript_Graphics_Driver::width(unsigned u) { - return Fl_Graphics_Driver::default_driver().width(u); -} - -int Fl_PostScript_Graphics_Driver::height() { - return Fl_Graphics_Driver::default_driver().height(); -} - -int Fl_PostScript_Graphics_Driver::descent() { - return Fl_Graphics_Driver::default_driver().descent(); -} - -void Fl_PostScript_Graphics_Driver::text_extents(const char *c, int n, int &dx, int &dy, int &w, int &h) { - Fl_Graphics_Driver::default_driver().text_extents(c, n, dx, dy, w, h); -} - - -void Fl_PostScript_Graphics_Driver::color(Fl_Color c) { - Fl::get_color(c, cr_, cg_, cb_); - color(cr_, cg_, cb_); -} - -void Fl_PostScript_Graphics_Driver::color(unsigned char r, unsigned char g, unsigned char b) { - Fl_Graphics_Driver::color( fl_rgb_color(r, g, b) ); - cr_ = r; cg_ = g; cb_ = b; - if (r == g && g == b) { - double gray = r/255.0; - clocale_printf("%g GL\n", gray); - } else { - double fr, fg, fb; - fr = r/255.0; - fg = g/255.0; - fb = b/255.0; - clocale_printf("%g %g %g SRGB\n", fr , fg , fb); - } -} - -void Fl_PostScript_Graphics_Driver::draw(int rotation, const char *str, int n, int x, int y) -{ - fprintf(output, "GS %d %d translate %d rotate\n", x, y, -rotation); - this->transformed_draw(str, n, 0, 0); - fprintf(output, "GR\n"); +void Fl_PostScript_Graphics_Driver::draw(int rotation, const char *str, int n, int x, int y) +{ + fprintf(output, "GS %d %d translate %d rotate\n", x, y, -rotation); + this->transformed_draw(str, n, 0, 0); + fprintf(output, "GR\n"); } @@ -1375,67 +1421,530 @@ void Fl_PostScript_Graphics_Driver::pop_clip() { recover(); } -int Fl_PostScript_Graphics_Driver::clip_box(int x, int y, int w, int h, int &X, int &Y, int &W, int &H) { - if (!clip_) { - X = x; Y = y; W = w; H = h; - return 0; +void Fl_PostScript_Graphics_Driver::ps_origin(int x, int y) +{ + clocale_printf("GR GR GS %d %d TR %f %f SC %d %d TR %f rotate GS\n", + left_margin, top_margin, scale_x, scale_y, x, y, angle); +} + +void Fl_PostScript_Graphics_Driver::ps_translate(int x, int y) +{ + fprintf(output, "GS %d %d translate GS\n", x, y); +} + +void Fl_PostScript_Graphics_Driver::ps_untranslate(void) +{ + fprintf(output, "GR GR\n"); +} + +# else + +/* Cairo-based implementation of the PostScript graphics driver */ + +static cairo_status_t write_to_cairo_stream(FILE *output, unsigned char *data, unsigned int length) { + size_t l = fwrite(data, 1, length, output); + return (l == length ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR); +} + +static int init_cairo_postscript(FILE* output, cairo_t* &cairo_, PangoLayout* &pango_layout, + int w, int h) { + cairo_surface_t* cs = cairo_ps_surface_create_for_stream((cairo_write_func_t)write_to_cairo_stream, output, w, h); + if (cairo_surface_status(cs) != CAIRO_STATUS_SUCCESS) return 1; + cairo_ps_surface_restrict_to_level(cs, CAIRO_PS_LEVEL_2); + cairo_ = cairo_create(cs); + pango_layout = pango_cairo_create_layout(cairo_); + return 0; +} + +int Fl_PostScript_Graphics_Driver::start_postscript(int pagecount, + enum Fl_Paged_Device::Page_Format format, enum Fl_Paged_Device::Page_Layout layout) +//returns 0 iff OK +{ + if (format == Fl_Paged_Device::A4) { + left_margin = 18; + top_margin = 18; } - if (clip_->w < 0) { - X = x; Y = y; W = w; H = h; - return 1; + else { + left_margin = 12; + top_margin = 12; } - int ret = 0; - if (x > (X=clip_->x)) {X=x; ret=1;} - if (y > (Y=clip_->y)) {Y=y; ret=1;} - if ((x+w) < (clip_->x+clip_->w)) { - W=x+w-X; + page_format_ = (enum Fl_Paged_Device::Page_Format)(format | layout); + if (layout & Fl_Paged_Device::LANDSCAPE){ + ph_ = Fl_Paged_Device::page_formats[format].width; + pw_ = Fl_Paged_Device::page_formats[format].height; + } else { + pw_ = Fl_Paged_Device::page_formats[format].width; + ph_ = Fl_Paged_Device::page_formats[format].height; + } + if (init_cairo_postscript(output, cairo_, pango_layout_, Fl_Paged_Device::page_formats[format].width, Fl_Paged_Device::page_formats[format].height)) return 1; + nPages=0; + char feature[250]; + sprintf(feature, "%%%%BeginFeature: *PageSize %s\n<>setpagedevice\n%%%%EndFeature", + Fl_Paged_Device::page_formats[format].name, Fl_Paged_Device::page_formats[format].width, Fl_Paged_Device::page_formats[format].height); + cairo_ps_surface_dsc_comment(cairo_get_target(cairo_), feature); + return 0; +} - ret=1; +int Fl_PostScript_Graphics_Driver::start_eps(int width, int height) { + pw_ = width; + ph_ = height; + if (init_cairo_postscript(output, cairo_, pango_layout_, width, height)) return 1; + cairo_ps_surface_set_eps(cairo_get_target(cairo_), true); + nPages=0; //useful? + return 0; +} - }else - W = clip_->x + clip_->w - X; - if(W<0){ - W=0; - return 1; +void Fl_PostScript_Graphics_Driver::rectf(int x, int y, int w, int h) { + cairo_rectangle(cairo_, x, y, w, h); + cairo_fill(cairo_); + check_status(); +} + +void Fl_PostScript_Graphics_Driver::rect(int x, int y, int w, int h) { + cairo_rectangle(cairo_, x, y, w, h); + cairo_stroke(cairo_); + check_status(); +} + +void Fl_PostScript_Graphics_Driver::line(int x1, int y1, int x2, int y2) { + cairo_new_path(cairo_); + cairo_move_to(cairo_, x1, y1); + cairo_line_to(cairo_, x2, y2); + cairo_stroke(cairo_); +} + +void Fl_PostScript_Graphics_Driver::line(int x0, int y0, int x1, int y1, int x2, int y2) { + cairo_new_path(cairo_); + cairo_move_to(cairo_, x0, y0); + cairo_line_to(cairo_, x1, y1); + cairo_line_to(cairo_, x2, y2); + cairo_stroke(cairo_); +} + +void Fl_PostScript_Graphics_Driver::xyline(int x, int y, int x1) { + cairo_move_to(cairo_, x, y); + cairo_line_to(cairo_, x1, y); + cairo_stroke(cairo_); + check_status(); +} + +void Fl_PostScript_Graphics_Driver::xyline(int x, int y, int x1, int y2) { + cairo_move_to(cairo_, x, y); + cairo_line_to(cairo_, x1, y); + cairo_line_to(cairo_, x1, y2); + cairo_stroke(cairo_); + check_status(); +} + +void Fl_PostScript_Graphics_Driver::xyline(int x, int y, int x1, int y2, int x3) { + cairo_move_to(cairo_, x, y); + cairo_line_to(cairo_, x1, y); + cairo_line_to(cairo_, x1, y2); + cairo_line_to(cairo_, x3, y2); + cairo_stroke(cairo_); + check_status(); +} + +void Fl_PostScript_Graphics_Driver::yxline(int x, int y, int y1) { + cairo_move_to(cairo_, x, y); + cairo_line_to(cairo_, x, y1); + cairo_stroke(cairo_); + check_status(); +} + +void Fl_PostScript_Graphics_Driver::yxline(int x, int y, int y1, int x2) { + cairo_move_to(cairo_, x, y); + cairo_line_to(cairo_, x, y1); + cairo_line_to(cairo_, x2, y1); + cairo_stroke(cairo_); + check_status(); +} + +void Fl_PostScript_Graphics_Driver::yxline(int x, int y, int y1, int x2, int y3) { + cairo_move_to(cairo_, x, y); + cairo_line_to(cairo_, x, y1); + cairo_line_to(cairo_, x2, y1); + cairo_line_to(cairo_, x2, y3); + cairo_stroke(cairo_); + check_status(); +} + +void Fl_PostScript_Graphics_Driver::loop(int x0, int y0, int x1, int y1, int x2, int y2) { + cairo_save(cairo_); + cairo_new_path(cairo_); + cairo_move_to(cairo_, x0, y0); + cairo_line_to(cairo_, x1, y1); + cairo_line_to(cairo_, x2, y2); + cairo_close_path(cairo_); + cairo_stroke(cairo_); + cairo_restore(cairo_); +} + +void Fl_PostScript_Graphics_Driver::loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) { + cairo_save(cairo_); + cairo_new_path(cairo_); + cairo_move_to(cairo_, x0, y0); + cairo_line_to(cairo_, x1, y1); + cairo_line_to(cairo_, x2, y2); + cairo_line_to(cairo_, x3, y3); + cairo_close_path(cairo_); + cairo_stroke(cairo_); + cairo_restore(cairo_); + +} + +void Fl_PostScript_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, int y2) { + cairo_save(cairo_); + cairo_new_path(cairo_); + cairo_move_to(cairo_, x0, y0); + cairo_line_to(cairo_, x1, y1); + cairo_line_to(cairo_, x2, y2); + cairo_close_path(cairo_); + cairo_fill(cairo_); + cairo_restore(cairo_); +} + +void Fl_PostScript_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) { + cairo_save(cairo_); + cairo_new_path(cairo_); + cairo_move_to(cairo_, x0, y0); + cairo_line_to(cairo_, x1, y1); + cairo_line_to(cairo_, x2, y2); + cairo_line_to(cairo_, x3, y3); + cairo_close_path(cairo_); + cairo_fill(cairo_); + cairo_restore(cairo_); +} + +void Fl_PostScript_Graphics_Driver::line_style(int style, int width, char* dashes) { + linewidth_=width; + linestyle_=style; + if(dashes){ + if(dashes != linedash_) + strcpy(linedash_,dashes); + + } else + linedash_[0]=0; + char width0 = 0; + if (!width){ + width=1; //for screen drawing compatibility + width0=1; } - if ((y+h) < (clip_->y+clip_->h)) { - H=y+h-Y; - ret=1; - }else - H = clip_->y + clip_->h - Y; - if(H<0){ - W=0; - H=0; - return 1; + cairo_set_line_width(cairo_, width); + + if(!style && (!dashes || !(*dashes)) && width0) //system lines + style = FL_CAP_SQUARE; + + int cap = (style &0xf00); + cairo_line_cap_t c_cap; + if (cap == FL_CAP_SQUARE) c_cap = CAIRO_LINE_CAP_SQUARE; + else if (cap == FL_CAP_FLAT) c_cap = CAIRO_LINE_CAP_BUTT; + else if (cap == FL_CAP_ROUND) c_cap = CAIRO_LINE_CAP_ROUND; + else c_cap = CAIRO_LINE_CAP_BUTT; + cairo_set_line_cap(cairo_, c_cap); + + int join = (style & 0xf000); + cairo_line_join_t c_join; + if (join == FL_JOIN_MITER) c_join = CAIRO_LINE_JOIN_MITER; + else if (join == FL_JOIN_ROUND)c_join = CAIRO_LINE_JOIN_ROUND; + else if (join == FL_JOIN_BEVEL) c_join = CAIRO_LINE_JOIN_BEVEL; + else c_join = CAIRO_LINE_JOIN_MITER; + cairo_set_line_join(cairo_, c_join); + + double *ddashes = NULL; + int l = 0; + if (dashes && *dashes){ + ddashes = new double[strlen(dashes)]; + while (dashes[l]) {ddashes[l] = dashes[l]; l++; } + } else if (style & 0xff) { + ddashes = new double[6]; + if (style & 0x200){ // round and square caps, dash length need to be adjusted + const double *dt = dashes_cap[style & 0xff]; + while (*dt >= 0){ + ddashes[l++] = width * (*dt); + dt++; + } + } else { + const int *ds = dashes_flat[style & 0xff]; + while (*ds >= 0){ + ddashes[l++] = width * (*ds); + ds++; + } + } } - return ret; + cairo_set_dash(cairo_, ddashes, l, 0); + delete[] ddashes; + check_status(); } -int Fl_PostScript_Graphics_Driver::not_clipped(int x, int y, int w, int h) { - if (!clip_) return 1; - if (clip_->w < 0) return 1; - int X = 0, Y = 0, W = 0, H = 0; - clip_box(x, y, w, h, X, Y, W, H); - if (W) return 1; - return 0; +void Fl_PostScript_Graphics_Driver::color(unsigned char r, unsigned char g, unsigned char b) { + Fl_Graphics_Driver::color( fl_rgb_color(r, g, b) ); + cr_ = r; cg_ = g; cb_ = b; + double fr, fg, fb; + fr = r/255.0; + fg = g/255.0; + fb = b/255.0; + cairo_set_source_rgb(cairo_, fr, fg, fb); + check_status(); } -void Fl_PostScript_Graphics_Driver::ps_origin(int x, int y) +void Fl_PostScript_Graphics_Driver::draw(int rotation, const char *str, int n, int x, int y) { - clocale_printf("GR GR GS %d %d TR %f %f SC %d %d TR %f rotate GS\n", - left_margin, top_margin, scale_x, scale_y, x, y, angle); + cairo_save(cairo_); + cairo_translate(cairo_, x, y); + cairo_rotate(cairo_, -rotation * M_PI / 180); + this->transformed_draw(str, n, 0, 0); + cairo_restore(cairo_); +} + +void Fl_PostScript_Graphics_Driver::transformed_draw(const char* str, int n, double x, double y) { + if (!n) return; + pango_layout_set_font_description(pango_layout_, Fl_Xlib_Graphics_Driver::pango_font_description(Fl_Graphics_Driver::font())); + int pwidth, pheight; + cairo_save(cairo_); + pango_layout_set_text(pango_layout_, str, n); + pango_layout_get_size(pango_layout_, &pwidth, &pheight); + if (pwidth > 0) { + double s = width(str, n); + cairo_translate(cairo_, x, y - height() + descent()); + s = (s/pwidth) * PANGO_SCALE; + cairo_scale(cairo_, s, s); + pango_cairo_show_layout(cairo_, pango_layout_); + } + cairo_restore(cairo_); + check_status(); +} + +void Fl_PostScript_Graphics_Driver::rtl_draw(const char* str, int n, int x, int y) { + int w = (int)width(str, n); + transformed_draw(str, n, x - w, y); +} + +void Fl_PostScript_Graphics_Driver::concat(){ + cairo_matrix_t mat = {fl_matrix->a , fl_matrix->b , fl_matrix->c , fl_matrix->d , fl_matrix->x , fl_matrix->y}; + cairo_transform(cairo_, &mat); +} + +void Fl_PostScript_Graphics_Driver::reconcat(){ + cairo_matrix_t mat = {fl_matrix->a , fl_matrix->b , fl_matrix->c , fl_matrix->d , fl_matrix->x , fl_matrix->y}; + cairo_status_t stat = cairo_matrix_invert(&mat); + if (stat != CAIRO_STATUS_SUCCESS) { + fputs("error in cairo_matrix_invert\n", stderr); + } + cairo_transform(cairo_, &mat); +} + +void Fl_PostScript_Graphics_Driver::begin_points() { + cairo_save(cairo_); + concat(); + cairo_new_path(cairo_); + gap_=1; + shape_=POINTS; +} + +void Fl_PostScript_Graphics_Driver::begin_line() { + cairo_save(cairo_); + concat(); + cairo_new_path(cairo_); + gap_=1; + shape_=LINE; +} + +void Fl_PostScript_Graphics_Driver::begin_loop() { + cairo_save(cairo_); + concat(); + cairo_new_path(cairo_); + gap_=1; + shape_=LOOP; +} + +void Fl_PostScript_Graphics_Driver::begin_polygon() { + cairo_save(cairo_); + concat(); + cairo_new_path(cairo_); + gap_=1; + shape_=POLYGON; +} + +void Fl_PostScript_Graphics_Driver::vertex(double x, double y) { + if(shape_==POINTS){ + cairo_move_to(cairo_, x, y); + gap_=1; + return; + } + if(gap_){ + cairo_move_to(cairo_, x, y); + gap_=0; + }else + cairo_line_to(cairo_, x, y); +} + +void Fl_PostScript_Graphics_Driver::curve(double x, double y, double x1, double y1, double x2, double y2, double x3, double y3) +{ + if(shape_==NONE) return; + if(gap_) + cairo_move_to(cairo_, x, y); + else + cairo_line_to(cairo_, x, y); + gap_=0; + cairo_curve_to(cairo_, x1 , y1 , x2 , y2 , x3 , y3); +} + +void Fl_PostScript_Graphics_Driver::circle(double x, double y, double r){ + if (shape_==NONE){ + cairo_save(cairo_); + concat(); + cairo_arc(cairo_, x, y, r, 0, 2*M_PI); + reconcat(); + cairo_restore(cairo_); + } else + cairo_arc(cairo_, x, y, r, 0, 2*M_PI); +} + +void Fl_PostScript_Graphics_Driver::arc(double x, double y, double r, double start, double a){ + if (shape_==NONE) return; + gap_ = 0; + if(start > a) + cairo_arc(cairo_, x, y, r, -start*M_PI/180, -a*M_PI/180); + else + cairo_arc_negative(cairo_, x, y, r, -start*M_PI/180, -a*M_PI/180); +} + +void Fl_PostScript_Graphics_Driver::arc(int x, int y, int w, int h, double a1, double a2) { + if (w <= 1 || h <= 1) return; + cairo_save(cairo_); + begin_line(); + cairo_translate(cairo_, x + w/2.0 -0.5 , y + h/2.0 - 0.5); + cairo_scale(cairo_, (w-1)/2.0 , (h-1)/2.0); + arc(0,0,1,a2,a1); + cairo_scale(cairo_, 2.0/(w-1) , 2.0/(h-1)); + cairo_translate(cairo_, -x - w/2.0 +0.5 , -y - h/2.0 +0.5); + end_line(); + cairo_restore(cairo_); +} + +void Fl_PostScript_Graphics_Driver::pie(int x, int y, int w, int h, double a1, double a2) { + cairo_save(cairo_); + begin_polygon(); + cairo_translate(cairo_, x + w/2.0 -0.5 , y + h/2.0 - 0.5); + cairo_scale(cairo_, (w-1)/2.0 , (h-1)/2.0); + vertex(0,0); + arc(0.0,0.0, 1, a2, a1); + end_polygon(); + cairo_restore(cairo_); +} + +void Fl_PostScript_Graphics_Driver::end_points() { + end_line(); +} + +void Fl_PostScript_Graphics_Driver::end_line() { + gap_=1; + reconcat(); + cairo_stroke(cairo_); + cairo_restore(cairo_); + shape_=NONE; +} + +void Fl_PostScript_Graphics_Driver::end_loop(){ + gap_=1; + reconcat(); + cairo_close_path(cairo_); + cairo_stroke(cairo_); + cairo_restore(cairo_); + shape_=NONE; +} + +void Fl_PostScript_Graphics_Driver::end_polygon() { + gap_=1; + reconcat(); + cairo_close_path(cairo_); + cairo_fill(cairo_); + cairo_restore(cairo_); + shape_=NONE; +} + +void Fl_PostScript_Graphics_Driver::transformed_vertex(double x, double y) { + reconcat(); + if(gap_){ + cairo_move_to(cairo_, x, y); + gap_=0; + }else + cairo_line_to(cairo_, x, y); + concat(); +} + +void Fl_PostScript_Graphics_Driver::push_clip(int x, int y, int w, int h) { + Clip * c=new Clip(); + clip_box(x,y,w,h,c->x,c->y,c->w,c->h); + c->prev=clip_; + clip_=c; + cairo_save(cairo_); + cairo_rectangle(cairo_, clip_->x-0.5 , clip_->y-0.5 , clip_->w , clip_->h); + cairo_clip(cairo_); + check_status(); +} + +void Fl_PostScript_Graphics_Driver::push_no_clip() { + Clip * c = new Clip(); + c->prev=clip_; + clip_=c; + clip_->x = clip_->y = clip_->w = clip_->h = -1; + cairo_save(cairo_); + cairo_reset_clip(cairo_); + check_status(); +} + +void Fl_PostScript_Graphics_Driver::pop_clip() { + if(!clip_)return; + Clip * c=clip_; + clip_=clip_->prev; + delete c; + cairo_restore(cairo_); + check_status(); +} + +void Fl_PostScript_Graphics_Driver::ps_origin(int x, int y) { + cairo_restore(cairo_); + cairo_restore(cairo_); + cairo_save(cairo_); + cairo_scale(cairo_, scale_x, scale_y); + cairo_translate(cairo_, x, y); + cairo_rotate(cairo_, angle * M_PI / 180); + cairo_save(cairo_); + check_status(); } void Fl_PostScript_Graphics_Driver::ps_translate(int x, int y) { - fprintf(output, "GS %d %d translate GS\n", x, y); + cairo_save(cairo_); + cairo_translate(cairo_, x, y); + cairo_save(cairo_); + check_status(); } void Fl_PostScript_Graphics_Driver::ps_untranslate(void) { - fprintf(output, "GR GR\n"); + cairo_restore(cairo_); + cairo_restore(cairo_); + check_status(); } +void Fl_PostScript_Graphics_Driver::check_status(void) { +#ifdef DEBUG + if (cairo_status(cairo_) != CAIRO_STATUS_SUCCESS) { + fprintf(stderr,"we have a problem"); + } +#endif +} + +#endif // USE_PANGO + +/** +\} +\endcond +*/ + void Fl_PostScript_File_Device::margins(int *left, int *top, int *right, int *bottom) // to implement { Fl_PostScript_Graphics_Driver *ps = driver(); @@ -1472,16 +1981,35 @@ void Fl_PostScript_File_Device::scale (float s_x, float s_y) Fl_PostScript_Graphics_Driver *ps = driver(); ps->scale_x = s_x; ps->scale_y = s_y; +#if USE_PANGO + cairo_restore(ps->cr()); + cairo_restore(ps->cr()); + cairo_save(ps->cr()); + cairo_scale(ps->cr(), s_x, s_y); + cairo_rotate(ps->cr(), ps->angle * M_PI / 180); + cairo_save(ps->cr()); +#else ps->clocale_printf("GR GR GS %d %d TR %f %f SC %f rotate GS\n", ps->left_margin, ps->top_margin, ps->scale_x, ps->scale_y, ps->angle); +#endif } void Fl_PostScript_File_Device::rotate (float rot_angle) { Fl_PostScript_Graphics_Driver *ps = driver(); ps->angle = - rot_angle; +#if USE_PANGO + cairo_restore(ps->cr()); + cairo_restore(ps->cr()); + cairo_save(ps->cr()); + cairo_scale(ps->cr(), ps->scale_x, ps->scale_y); + cairo_translate(ps->cr(), x_offset, y_offset); + cairo_rotate(ps->cr(), ps->angle * M_PI / 180); + cairo_save(ps->cr()); +#else ps->clocale_printf("GR GR GS %d %d TR %f %f SC %d %d TR %f rotate GS\n", ps->left_margin, ps->top_margin, ps->scale_x, ps->scale_y, x_offset, y_offset, ps->angle); +#endif } void Fl_PostScript_File_Device::translate(int x, int y) @@ -1497,17 +2025,45 @@ void Fl_PostScript_File_Device::untranslate(void) int Fl_PostScript_File_Device::begin_page (void) { Fl_PostScript_Graphics_Driver *ps = driver(); +#if USE_PANGO + cairo_ps_surface_dsc_begin_page_setup(cairo_get_target(ps->cr())); + char feature[200]; + sprintf(feature, "%%%%PageOrientation: %s", ps->pw_ > ps->ph_ ? "Landscape" : "Portrait"); + cairo_ps_surface_dsc_comment(cairo_get_target(ps->cr()), feature); + if (ps->pw_ > ps->ph_) { + cairo_translate(ps->cr(), 0, ps->pw_); + cairo_rotate(ps->cr(), -M_PI/2); + } + cairo_translate(ps->cr(), ps->left_margin, ps->top_margin); + cairo_set_line_width(ps->cr(), 1); + cairo_set_source_rgb(ps->cr(), 1.0, 1.0, 1.0); // white background + cairo_save(ps->cr()); + cairo_save(ps->cr()); + cairo_save(ps->cr()); + ps->check_status(); +#else ps->page(ps->page_format_); +#endif x_offset = 0; y_offset = 0; ps->scale_x = ps->scale_y = 1.; ps->angle = 0; +#if ! USE_PANGO fprintf(ps->output, "GR GR GS %d %d translate GS\n", ps->left_margin, ps->top_margin); +#endif return 0; } int Fl_PostScript_File_Device::end_page (void) { +#if USE_PANGO + Fl_PostScript_Graphics_Driver *ps = (Fl_PostScript_Graphics_Driver*)driver(); + cairo_restore(ps->cr()); + cairo_restore(ps->cr()); + cairo_restore(ps->cr()); + cairo_show_page(ps->cr()); + ps->check_status(); +#endif return 0; } @@ -1515,6 +2071,20 @@ void Fl_PostScript_File_Device::end_job (void) // finishes PostScript & closes file { Fl_PostScript_Graphics_Driver *ps = driver(); + int error = 0; +#if USE_PANGO + cairo_surface_t *s = cairo_get_target(ps->cr()); + cairo_surface_finish(s); + error = cairo_surface_status(s); + if (error) { + fclose(ps->output); + fputs("\n", ps->output); // creates an stdio error + } + cairo_destroy(ps->cr()); + cairo_surface_destroy(s); + g_object_unref(ps->pango_layout()); + if (!error) error = fflush(ps->output); +#else if (ps->nPages) { // for eps nPages is 0 so it is fine .... fprintf(ps->output, "CR\nGR\nGR\nGR\nSP\n restore\n"); if (!ps->pages_){ @@ -1524,81 +2094,103 @@ void Fl_PostScript_File_Device::end_job (void) } else fprintf(ps->output, "GR\n restore\n"); fputs("%%EOF",ps->output); - ps->reset(); fflush(ps->output); - if(ferror(ps->output)) { - fl_alert ("Error during PostScript data output."); - } - if (ps->close_cmd_) { - (*ps->close_cmd_)(ps->output); - } else { - fclose(ps->output); - } + error = ferror(ps->output); + ps->reset(); +#endif while (ps->clip_){ Fl_PostScript_Graphics_Driver::Clip * c= ps->clip_; ps->clip_= ps->clip_->prev; delete c; } - Fl_Display_Device::display_device()->set_current(); + Fl_Surface_Device::pop_current(); + int err2 = (ps->close_cmd_ ? (ps->close_cmd_)(ps->output) : fclose(ps->output) ); + if (!error) error = err2; + if (error && ps->close_cmd_ == NULL) { + fl_alert ("Error during PostScript data output."); + } } -/** -\} -\endcond -*/ - -Fl_EPS_File_Surface::Fl_EPS_File_Surface(int width, int height, FILE *eps, Fl_Color background) : +Fl_EPS_File_Surface::Fl_EPS_File_Surface(int width, int height, FILE *eps, Fl_Color background, Fl_PostScript_Close_Command closef) : Fl_Widget_Surface(new Fl_PostScript_Graphics_Driver()) { Fl_PostScript_Graphics_Driver *ps = driver(); ps->output = eps; + ps->close_cmd_ = closef; if (ps->output) { float s = Fl::screen_scale(0); ps->start_eps(width*s, height*s); +#if USE_PANGO + cairo_save(ps->cr()); + ps->left_margin = ps->top_margin = 0; + cairo_scale(ps->cr(), s, s); + cairo_set_line_width(ps->cr(), 1); + cairo_set_source_rgb(ps->cr(), 1.0, 1.0, 1.0); // white background + cairo_save(ps->cr()); + cairo_save(ps->cr()); + ps->check_status(); +#else if (s != 1) { ps->clocale_printf("GR GR GS %f %f SC GS\n", s, s); - ps->scale_x = ps->scale_y = s; } +#endif + ps->scale_x = ps->scale_y = s; Fl::get_color(background, ps->bg_r, ps->bg_g, ps->bg_b); } } -void Fl_EPS_File_Surface::complete_() { +int Fl_EPS_File_Surface::close() { + int error = 0; Fl_PostScript_Graphics_Driver *ps = driver(); +#if USE_PANGO + cairo_surface_t *s = cairo_get_target(ps->cr()); + cairo_surface_finish(s); + cairo_status_t status = cairo_surface_status(s); + cairo_destroy(ps->cr()); + cairo_surface_destroy(s); + g_object_unref(ps->pango_layout()); + fflush(ps->output); + error = ferror(ps->output); + if (status != CAIRO_STATUS_SUCCESS) error = status; +#else if(ps->output) { fputs("GR\nend %matches begin of FLTK dict\n", ps->output); fputs("restore\n", ps->output); fputs("%%EOF\n", ps->output); ps->reset(); fflush(ps->output); - if(ferror(ps->output)) { - fl_alert ("Error during PostScript data output."); - } + error = ferror(ps->output); } +#endif + int err2 = (ps->close_cmd_ ? (ps->close_cmd_)(ps->output) : fclose(ps->output)); + if (err2) error = err2; while (ps->clip_){ Fl_PostScript_Graphics_Driver::Clip * c= ps->clip_; ps->clip_= ps->clip_->prev; delete c; } + ps->output = NULL; + return error; } Fl_EPS_File_Surface::~Fl_EPS_File_Surface() { - Fl_PostScript_Graphics_Driver *ps = driver(); - if(ps->output) complete_(); - delete ps; + if (driver()->output) { + if ( close() ) { + fl_open_display(); + fl_alert ("Error during encapsulated PostScript data output."); + } + } + delete driver(); } -int Fl_EPS_File_Surface::close() { - complete_(); +FILE *Fl_EPS_File_Surface::file() { Fl_PostScript_Graphics_Driver *ps = driver(); - int retval = fclose(ps->output); - ps->output = NULL; - return retval; + return ps ? ps->output : NULL; } int Fl_EPS_File_Surface::printable_rect(int *w, int *h) { Fl_PostScript_Graphics_Driver *ps = driver(); - *w = ps->width_; - *h = ps->height_; + *w = int(ps->pw_); + *h = int(ps->ph_); return 0; } diff --git a/src/drivers/PostScript/Fl_PostScript_Graphics_Driver.H b/src/drivers/PostScript/Fl_PostScript_Graphics_Driver.H new file mode 100644 index 0000000000..6491872bcd --- /dev/null +++ b/src/drivers/PostScript/Fl_PostScript_Graphics_Driver.H @@ -0,0 +1,231 @@ +// +// Support for graphics output to PostScript file for the Fast Light Tool Kit (FLTK). +// +// Copyright 2010-2020 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +/** \file Fl_Pango_PostScript_Graphics_Driver.H + Declaration of class Fl_PostScript_Graphics_Driver. +*/ + +#ifndef FL_POSTSCRIPT_GRAPHICS_DRIVER_H +#define FL_POSTSCRIPT_GRAPHICS_DRIVER_H + +#include "../../config_lib.h" +#include + +#ifndef USE_PANGO +#define USE_PANGO 0 +#endif + +#if USE_PANGO +typedef struct _cairo_surface cairo_surface_t; +typedef struct _cairo cairo_t; +typedef struct _PangoLayout PangoLayout; +#endif + +/** + \cond DriverDev + \addtogroup DriverDeveloper + \{ + */ + +/** + PostScript graphical backend. + */ + +class FL_EXPORT Fl_PostScript_Graphics_Driver : public Fl_Graphics_Driver { +private: +#if USE_PANGO + cairo_t *cairo_; + PangoLayout *pango_layout_; + void draw_rgb_bitmap_(Fl_Image *img,int XP, int YP, int WP, int HP, int cx, int cy); +#else + void transformed_draw_extra(const char* str, int n, double x, double y, int w, bool rtl); + void *prepare_rle85(); + void write_rle85(uchar b, void *data); + void close_rle85(void *data); + void *prepare85(); + void write85(void *data, const uchar *p, int len); + void close85(void *data); + int scale_for_image_(Fl_Image *img, int XP, int YP, int WP, int HP,int cx, int cy); +#endif +protected: + uchar **mask_bitmap() {return &mask;} +public: + Fl_PostScript_Graphics_Driver(); +#ifndef FL_DOXYGEN + enum SHAPE{NONE=0, LINE, LOOP, POLYGON, POINTS}; + + class Clip { + public: + int x, y, w, h; + Clip *prev; + }; + Clip * clip_; + + int lang_level_; + int gap_; + int pages_; + + int shape_; + int linewidth_;// need for clipping, lang level 1-2 + int linestyle_;// + int interpolate_; //interpolation of images + unsigned char cr_,cg_,cb_; + char linedash_[256];//should be enough + void concat(); // transform ror scalable dradings... + void reconcat(); //invert + void recover(); //recovers the state after grestore (such as line styles...) + void reset(); + + uchar * mask; + int mx; // width of mask; + int my; // mask lines + Fl_PostScript_Close_Command close_cmd_; + int page_policy_; + int nPages; + int orientation_; + + float scale_x; + float scale_y; + float angle; + int left_margin; + int top_margin; + + FILE *output; + double pw_, ph_; + + uchar bg_r, bg_g, bg_b; + int start_postscript (int pagecount, enum Fl_Paged_Device::Page_Format format, enum Fl_Paged_Device::Page_Layout layout); + int start_eps(int width, int height); + /* int alpha_mask(const uchar * data, int w, int h, int D, int LD=0); + */ + void transformed_draw(const char* s, int n, double x, double y); //precise text placing + void transformed_draw(const char* s, double x, double y); + int alpha_mask(const uchar * data, int w, int h, int D, int LD=0); + + enum Fl_Paged_Device::Page_Format page_format_; + char *ps_filename_; + + void page_policy(int p); + int page_policy(){return page_policy_;}; + void close_command(Fl_PostScript_Close_Command cmd){close_cmd_=cmd;}; + FILE * file() {return output;}; + //void orientation (int o); + //Fl_PostScript_Graphics_Driver(FILE *o, int lang_level, int pages = 0); // ps (also multi-page) constructor + void interpolate(int i){interpolate_=i;}; + int interpolate(){return interpolate_;} + + void page(double pw, double ph, int media = 0); + void page(int format); +#endif // FL_DOXYGEN + + // implementation of drawing methods + void color(Fl_Color c); + void color(uchar r, uchar g, uchar b); + + void push_clip(int x, int y, int w, int h); + int clip_box(int x, int y, int w, int h, int &X, int &Y, int &W, int &H); + int not_clipped(int x, int y, int w, int h); + void push_no_clip(); + void pop_clip(); + + void line_style(int style, int width=0, char* dashes=0); + + void rect(int x, int y, int w, int h); + void rectf(int x, int y, int w, int h); + + void xyline(int x, int y, int x1); + void xyline(int x, int y, int x1, int y2); + void xyline(int x, int y, int x1, int y2, int x3); + + void yxline(int x, int y, int y1); + void yxline(int x, int y, int y1, int x2); + void yxline(int x, int y, int y1, int x2, int y3); + + void line(int x1, int y1, int x2, int y2); + void line(int x1, int y1, int x2, int y2, int x3, int y3); + + void loop(int x0, int y0, int x1, int y1, int x2, int y2); + void loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3); + void polygon(int x0, int y0, int x1, int y1, int x2, int y2); + void polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3); + void point(int x, int y); + + void begin_points(); + void begin_line(); + void begin_loop(); + void begin_polygon(); + void vertex(double x, double y); + void curve(double x, double y, double x1, double y1, double x2, double y2, double x3, double y3); + void circle(double x, double y, double r); + void arc(double x, double y, double r, double start, double a); + void arc(int x, int y, int w, int h, double a1, double a2); + void pie(int x, int y, int w, int h, double a1, double a2); + void end_points(); + void end_line(); + void end_loop(); + void end_polygon(); + void begin_complex_polygon(){begin_polygon();}; + void gap(){gap_=1;}; + void end_complex_polygon(){end_polygon();}; + void transformed_vertex(double x, double y); + + void draw_image(const uchar* d, int x,int y,int w,int h, int delta=3, int ldelta=0); + void draw_image_mono(const uchar* d, int x,int y,int w,int h, int delta=1, int ld=0); + void draw_image(Fl_Draw_Image_Cb call, void* data, int x,int y, int w, int h, int delta=3); + void draw_image_mono(Fl_Draw_Image_Cb call, void* data, int x,int y, int w, int h, int delta=1); + + void draw(const char* s, int nBytes, int x, int y) {transformed_draw(s,nBytes,x,y); }; + void draw(const char* s, int nBytes, float x, float y) {transformed_draw(s,nBytes,x,y); }; + void draw(int angle, const char *str, int n, int x, int y); + void rtl_draw(const char* s, int n, int x, int y); + void font(int face, int size); + double width(const char *, int); + double width(unsigned int u); + void text_extents(const char *c, int n, int &dx, int &dy, int &w, int &h); + int height(); + int descent(); + void draw_pixmap(Fl_Pixmap * pxm,int XP, int YP, int WP, int HP, int cx, int cy); + void draw_bitmap(Fl_Bitmap * bitmap,int XP, int YP, int WP, int HP, int cx, int cy); + void draw_rgb(Fl_RGB_Image * rgb,int XP, int YP, int WP, int HP, int cx, int cy); +#if USE_PANGO + cairo_t *cr() { return cairo_; } + PangoLayout *pango_layout() {return pango_layout_;}; + void check_status(void); +#else + /** Shields output PostScript data from modifications of the current locale. + It typically avoids PostScript errors caused if the current locale uses comma instead of dot + as "decimal point". + \param format directives controlling output PostScript data + \return value returned by vfprintf() call + */ + int clocale_printf(const char *format, ...); +#endif + ~Fl_PostScript_Graphics_Driver(); + // --- + Fl_Bitmask create_bitmask(int w, int h, const uchar *array) { return 0L; } + virtual int has_feature(driver_feature feature_mask) { return feature_mask & PRINTER; } + + void ps_origin(int x, int y); + void ps_translate(int, int); + void ps_untranslate(); +}; + +/** +\} +\endcond +*/ + +#endif // FL_POSTSCRIPT_GRAPHICS_DRIVER_H diff --git a/src/drivers/PostScript/Fl_PostScript_image.cxx b/src/drivers/PostScript/Fl_PostScript_image.cxx index 43f9b36c25..5c1f1d1f71 100644 --- a/src/drivers/PostScript/Fl_PostScript_image.cxx +++ b/src/drivers/PostScript/Fl_PostScript_image.cxx @@ -1,7 +1,7 @@ // // Postscript image drawing implementation for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2015 by Bill Spitzak and others. +// Copyright 1998-2020 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -22,10 +22,48 @@ #include #include +#include "Fl_PostScript_Graphics_Driver.H" #include #include #include +#if USE_PANGO +#include +#endif + +struct callback_data { + const uchar *data; + int D, LD; +}; + +static void draw_image_cb(void *data, int x, int y, int w, uchar *buf) { + struct callback_data *cb_data; + const uchar *curdata; + + cb_data = (struct callback_data*)data; + curdata = cb_data->data + x*cb_data->D + y*cb_data->LD; + + memcpy(buf, curdata, w*cb_data->D); +} + +void Fl_PostScript_Graphics_Driver::draw_image(const uchar *data, int ix, int iy, int iw, int ih, int D, int LD) { + if (D<3){ //mono + draw_image_mono(data, ix, iy, iw, ih, D, LD); + return; + } + + struct callback_data cb_data; + + if (!LD) LD = iw*D; + + cb_data.data = data; + cb_data.D = D; + cb_data.LD = LD; + + draw_image(draw_image_cb, &cb_data, ix, iy, iw, ih, D); +} + +#if ! USE_PANGO // // Implementation of the /ASCII85Encode PostScript filter @@ -345,41 +383,6 @@ static inline uchar swap_byte(const uchar b) { return (swapped[b & 0xF] << 4) | swapped[b >> 4]; } - -struct callback_data { - const uchar *data; - int D, LD; -}; - - -static void draw_image_cb(void *data, int x, int y, int w, uchar *buf) { - struct callback_data *cb_data; - const uchar *curdata; - - cb_data = (struct callback_data*)data; - curdata = cb_data->data + x*cb_data->D + y*cb_data->LD; - - memcpy(buf, curdata, w*cb_data->D); -} - - -void Fl_PostScript_Graphics_Driver::draw_image(const uchar *data, int ix, int iy, int iw, int ih, int D, int LD) { - if (D<3){ //mono - draw_image_mono(data, ix, iy, iw, ih, D, LD); - return; - } - - struct callback_data cb_data; - - if (!LD) LD = iw*D; - - cb_data.data = data; - cb_data.D = D; - cb_data.LD = LD; - - draw_image(draw_image_cb, &cb_data, ix, iy, iw, ih, D); -} - void Fl_PostScript_Graphics_Driver::draw_image(Fl_Draw_Image_Cb call, void *data, int ix, int iy, int iw, int ih, int D) { double x = ix, y = iy, w = iw, h = ih; @@ -628,4 +631,144 @@ int Fl_PostScript_Graphics_Driver::scale_for_image_(Fl_Image *img, int XP, int Y return 0; } +#else + +void Fl_PostScript_Graphics_Driver::draw_image(Fl_Draw_Image_Cb call, void *data, int ix, int iy, int iw, int ih, int D) +{ + uchar *array = new uchar[iw * D * ih]; + for (int l = 0; l < ih; l++) { + call(data, 0, l, iw, array + l*D*iw); + if (D%2 == 0) for (int i = 0; i < iw; i++) { + *(array + l*D*iw + i*D + D-1) = 0xff; + } + } + Fl_RGB_Image *rgb = new Fl_RGB_Image(array, iw, ih, D); + rgb->alloc_array = 1; + draw_rgb(rgb, ix, iy, iw, ih, 0, 0); + delete rgb; +} + +void Fl_PostScript_Graphics_Driver::draw_image_mono(const uchar *data, int ix, int iy, int iw, int ih, int D, int LD) +{ + struct callback_data cb_data; + if (!LD) LD = iw*D; + cb_data.data = data; + cb_data.D = D; + cb_data.LD = LD; + draw_image(draw_image_cb, &cb_data, ix, iy, iw, ih, D); +} + +void Fl_PostScript_Graphics_Driver::draw_image_mono(Fl_Draw_Image_Cb call, void *data, int ix, int iy, int iw, int ih, int D) +{ + draw_image(call, data, ix, iy, iw, ih, D); +} + +static void destroy_BGRA(void *data) { + delete[] (uchar*)data; +} + +void Fl_PostScript_Graphics_Driver::draw_pixmap(Fl_Pixmap *pxm,int XP, int YP, int WP, int HP, int cx, int cy) { + Fl_RGB_Image *rgb = new Fl_RGB_Image(pxm); + draw_rgb_bitmap_(rgb, XP, YP, WP, HP, cx, cy); + delete rgb; +} + +void Fl_PostScript_Graphics_Driver::draw_rgb(Fl_RGB_Image *rgb,int XP, int YP, int WP, int HP, int cx, int cy) { + draw_rgb_bitmap_(rgb, XP, YP, WP, HP, cx, cy); +} + +void Fl_PostScript_Graphics_Driver::draw_bitmap(Fl_Bitmap *bitmap,int XP, int YP, int WP, int HP, int cx, int cy) { + draw_rgb_bitmap_(bitmap, XP, YP, WP, HP, cx, cy); +} + +void Fl_PostScript_Graphics_Driver::draw_rgb_bitmap_(Fl_Image *img,int XP, int YP, int WP, int HP, int cx, int cy) +{ + cairo_surface_t *surf; + cairo_format_t format = (img->d() >= 1 ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_A1); + int stride = cairo_format_stride_for_width(format, img->data_w()); + uchar *BGRA = new uchar[stride * img->data_h()]; + memset(BGRA, 0, stride * img->data_h()); + if (img->d() >= 1) { // process Fl_RGB_Image of all depths + Fl_RGB_Image *rgb = (Fl_RGB_Image*)img; + int lrgb = rgb->ld() ? rgb->ld() : rgb->data_w() * rgb->d(); + uchar A = 0xff, R,G,B, *q; + const uchar *r; + float f = 1; + if (rgb->d() >= 3) { // color images + for (int j = 0; j < rgb->data_h(); j++) { + r = rgb->array + j * lrgb; + q = BGRA + j * stride; + for (int i = 0; i < rgb->data_w(); i++) { + R = *r; + G = *(r+1); + B = *(r+2); + if (rgb->d() == 4) { + A = *(r+3); + f = float(A)/0xff; + } + *q = B * f; + *(q+1) = G * f; + *(q+2) = R * f; + *(q+3) = A; + r += rgb->d(); q += 4; + } + } + } else if (rgb->d() == 1 || rgb->d() == 2) { // B&W + for (int j = 0; j < rgb->data_h(); j++) { + r = rgb->array + j * lrgb; + q = BGRA + j * stride; + for (int i = 0; i < rgb->data_w(); i++) { + G = *r; + if (rgb->d() == 2) { + A = *(r+1); + f = float(A)/0xff; + } + *(q) = G * f; + *(q+1) = G * f; + *(q+2) = G * f; + *(q+3) = A; + r += rgb->d(); q += 4; + } + } + } + } else { + Fl_Bitmap *bm = (Fl_Bitmap*)img; + uchar *r, p; + unsigned *q; + for (int j = 0; j < bm->data_h(); j++) { + r = (uchar*)bm->array + j * ((bm->data_w() + 7)/8); + q = (unsigned*)(BGRA + j * stride); + unsigned k = 0, mask32 = 1; + p = *r; + for (int i = 0; i < bm->data_w(); i++) { + if (p&1) (*q) |= mask32; + k++; + if (k % 8 != 0) p >>= 1; else p = *(++r); + if (k % 32 != 0) mask32 <<= 1; else {q++; mask32 = 1;} + } + } + } + surf = cairo_image_surface_create_for_data(BGRA, format, img->data_w(), img->data_h(), stride); + if (cairo_surface_status(surf) == CAIRO_STATUS_SUCCESS) { + static cairo_user_data_key_t key = {}; + (void)cairo_surface_set_user_data(surf, &key, BGRA, destroy_BGRA); + cairo_pattern_t *pat = cairo_pattern_create_for_surface(surf); + cairo_save(cairo_); + cairo_rectangle(cairo_, XP-0.5, YP-0.5, WP+1, HP+1); + cairo_clip(cairo_); // still to be tested + if (img->d() >= 1) cairo_set_source(cairo_, pat); + cairo_matrix_t matrix; + cairo_matrix_init_scale(&matrix, double(img->data_w())/img->w(), double(img->data_h())/img->h()); + cairo_matrix_translate(&matrix, -XP+cx, -YP+cy); + cairo_pattern_set_matrix(pat, &matrix); + cairo_mask(cairo_, pat); + cairo_pattern_destroy(pat); + cairo_surface_destroy(surf); + cairo_restore(cairo_); + check_status(); + } +} + +#endif // USE_PANGO + #endif // !defined(FL_DOXYGEN) && !defined(FL_NO_PRINT_SUPPORT) diff --git a/src/drivers/Xlib/Fl_Xlib_Graphics_Driver.H b/src/drivers/Xlib/Fl_Xlib_Graphics_Driver.H index 322b45c68b..4f1906d59d 100644 --- a/src/drivers/Xlib/Fl_Xlib_Graphics_Driver.H +++ b/src/drivers/Xlib/Fl_Xlib_Graphics_Driver.H @@ -1,7 +1,7 @@ // // Definition of class Fl_Xlib_Graphics_Driver for the Fast Light Tool Kit (FLTK). // -// Copyright 2010-2018 by Bill Spitzak and others. +// Copyright 2010-2020 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -82,6 +82,9 @@ protected: static PangoContext *pctxt_; static PangoFontMap *pfmap_; static PangoLayout *playout_; +public: + static PangoFontDescription* pango_font_description(Fl_Font fnum) { return pfd_array[fnum]; } +private: static PangoFontDescription **pfd_array; // one array element for each Fl_Font static int pfd_array_length; void do_draw(int from_right, const char *str, int n, int x, int y); diff --git a/test/device.cxx b/test/device.cxx index 154bdb844f..8b04e53d22 100644 --- a/test/device.cxx +++ b/test/device.cxx @@ -617,7 +617,12 @@ void copy(Fl_Widget *, void *data) { } if (!err) { p->begin_page(); - if (target->as_window()) p->print_window(target->as_window()); + if (target->as_window()) { + int w, h; + p->printable_rect(&w, &h); + p->origin(w/2, h/2); + p->print_window(target->as_window(), -target->w()/2, -target->h()/2); + } else p->print_widget(target); p->end_page(); p->end_job(); @@ -646,10 +651,8 @@ void copy(Fl_Widget *, void *data) { if (p.file()) { if (target->as_window()) p.draw_decorated_window(target->as_window()); else p.draw(target); - //p.close(); } } - fclose(eps); } }