bugGNU Octave - Bugs: bug #44186, print command gives incorrect...


bug #44186: print command gives incorrect postscript file

Submitter:  Avinoam Kalma <avinoam>
Submitted:  Wed 04 Feb 2015 05:33:36 PM UTC
Category:  Plotting Severity:  5 - Blocker
Priority:  5 - Normal Item Group:  Incorrect Result
Status:  Fixed Assigned to:  lostbard
Originator Name:  Avinoam Open/Closed:  * Closed
Release:  * dev Operating System:  * Microsoft Windows
* Mandatory Fields

Add a New Comment (Rich Markup)

Comment Type & Canned Response:


( Jump to the original submission )

Fri 20 Feb 2015 05:25:55 PM UTC, comment #61: 

Fix for pread/pwrite issue is here:


Dan Sebald <sebald>
Thu 19 Feb 2015 07:09:19 PM UTC, comment #60: 

OK, thanks for testing.  The fix for binary open is confirmed then, and I'll have ghostscript developers close their associated bug report.  Also, I should open a new bug report at ghostscript bugzilla for the pread/pwrite.  I will do this later today.

Dan Sebald <sebald>
Thu 19 Feb 2015 01:31:54 PM UTC, comment #59: 

Issues in the mingw build  with the latest pre-release 9.16 code.

The latest gp_unifs.cpp uses pread/pwrite that are not available in mingw, and so need replacement functions defined.

John Donoghue <lostbard>
Project Member
Wed 18 Feb 2015 06:14:59 PM UTC, comment #58: 

ghostscript developers have updated their repository and requested someone test.


I've found a couple ways to get the source.  The git ghostscript repository is quite big.  Also, ghostscript is the gs subdirectory of a bigger project.  To get a snapshot of the head of just the gs subdirectory, go to


(Or just sort of navigate around the "tree" from the changeset diff listed above until within the "gs" subdirectory.)  When within the source tree for the "gs" subdirectory, look for the widget that says "snapshot" at the second or third line on the page.  That will give you a tar file to use.

Extract the archive then run "autogen.sh" within the source code.  I ran into a problem in which none of the automake tools were visible, e.g.,:

configure: error: cannot find install-sh, install.sh, or shtool in "." "./.." "./../.."

I created links for the following three utilities:

ln -s /usr/share/automake-1.11/install-sh
ln -s /usr/share/automake-1.11/config.sub
ln -s /usr/share/automake-1.11/config.guess

and "autogen.sh" followed by "configure" (probably redundant) followed by "make" worked for me.

All I need to do to test in Octave environment is rename my "/usr/bin/gs" file and create a link to the newly generated "gs" executable.

Please let me know if anyone else has compilation problems and needs to make the fix I did.

If one wants the whole repository, try:

git clone git://git.ghostscript.com/ghostpdl.git

Again, that takes a while to download and is between 100 and 200 MB worth.

Dan Sebald <sebald>
Tue 17 Feb 2015 09:52:46 PM UTC, comment #57: 


Dan Sebald <sebald>
Tue 17 Feb 2015 12:07:58 PM UTC, comment #56: 

Sounds like a volunteer to me!

John Donoghue <lostbard>
Project Member
Tue 17 Feb 2015 04:10:57 AM UTC, comment #55: 

From "gs -h", ghostscript bugs can be reported here:


Requires a bugzilla account.  If no one else wants to, I'll report the missing pre-process conditional.

Dan Sebald <sebald>
Tue 17 Feb 2015 12:25:20 AM UTC, comment #54: 

Marking bug as fixed and closing report.

I still have been able to segfault Octave on a Windows XP machine when trying various print combinations, but I'll have to see if I can make it reproducible.  I'll open up a new bug report if I can.

Rik <rik5>
Project Administrator
Tue 17 Feb 2015 12:09:03 AM UTC, comment #53: 
John Donoghue <lostbard>
Project Member
Mon 16 Feb 2015 10:33:19 PM UTC, comment #52: 

Just tested the patch and it works for me so I think it can be committed.

As for the eps format, jwe hates checking version numbers.  The best way is to test capabilities.  For that, I think using 'gs -h' and strfind are the way to go.

I checked in a changeset that does that here (http://hg.savannah.gnu.org/hgweb/octave/rev/abc158e7122e).

Rik <rik5>
Project Administrator
Mon 16 Feb 2015 08:11:52 PM UTC, comment #51: 

Or in _ghostscript_.m, before using the opts.device:

  % check version  number and use eps2write if required
  if (dos_shell)
    ver_cmd = sprintf("%s -v", strrep (opts.binary, '/', '\'));
    ver_cmd = sprintf("%s -v", opts.binary);
  [sout, vertext] = system(ver_cmd);
  vername= 'Ghostscript ';
  gs_version = sscanf(vertext(strfind(vertext, vername) + length(vername):end), "%f", 1);

  if (opts.device == "epswrite" && gs_version >= 9.10)
    opts.device = "eps2write";

John Donoghue <lostbard>
Project Member
Mon 16 Feb 2015 07:55:21 PM UTC, comment #50: 

what about something like:

>> [sout, vertext] = system('gs -h');
>> strfind (vertext, 'eps2write');

Avinoam Kalma <avinoam>
Project Member
Mon 16 Feb 2015 06:50:47 PM UTC, comment #49: 

OK, nice.

For the 9.15 version I compiled, I see from "gs -h" that eps2write is in the list but epswrite is not in the list.  For 8.71 I see epswrite, but no eps2write.  So neither of those device names covers both cases, and yes we'll have to condition on the ghostscript version.  If that info hasn't already been determined somewhere in the source code, the following grabs the version on my system:

>> [sout, vertext] = system('gs -v');
>> vername= 'Ghostscript ';
>> sscanf(vertext(strfind(vertext, vername) + length(vername):end), "%f", 1)
ans =  8.7100

Dan Sebald <sebald>
Mon 16 Feb 2015 05:08:37 PM UTC, comment #48: 

yep - works for me still - updated patch for current mxe repo, including removing old ghostscript patch for gnu-Linux.

All image formats I tried work for me except for eps as per comment 39.

(file #33103)

John Donoghue <lostbard>
Project Member
Mon 16 Feb 2015 02:03:38 AM UTC, comment #47: 

Or make the conditional the same as what they've used:

#if (defined(__MINGW32__) && __MINGW32__ == 1) || (defined(__CYGWIN__) && __CYGWIN__ == 1)

as, like you say, it does seem incomplete.  If that works, we'll alert gs developers about a possible bug.

Dan Sebald <sebald>
Mon 16 Feb 2015 01:53:10 AM UTC, comment #46: 

Checking the output of mingw gcc, it is defined as 1 in the preprocessor output.

However sometimes gp_fmode_wb etc has been used, and in other places gp_fmode_binary_suffix is obviously being used, so they have a partial implementation going for mingw with regards to binary mode.

#ifdef __MINGW32__
const char gp_fmode_binary_suffix[] = "b";
const char gp_fmode_binary_suffix[] = "";

should do the trick I hope.

John Donoghue <lostbard>
Project Member
Mon 16 Feb 2015 01:46:20 AM UTC, comment #45: 

"Perhaps just defining gp_fmode_binary_suffix differently for mingw?"

That would have been my thought.  If defining _MINGW32_ to 1 doesn't fix ghostscript.  Perhaps creating a file gp_mingw.c that tweaks settings in gp_unifn.c and then patching the Makefile to include gp_mingw.$(OBJ) instead is better?  Not worth too much extra effort though, and hopefully the _MINGW32_ path works.

Dan Sebald <sebald>
Mon 16 Feb 2015 01:36:20 AM UTC, comment #44: 

Sure enough.  Dependence on the numerical value of a definition isn't commonly done.

Dan Sebald <sebald>
Mon 16 Feb 2015 01:30:17 AM UTC, comment #43: 

Interestingsly, already in gp_unifn.c:

/* Define the file modes for binary reading or writing. */
#if (defined(__MINGW32__) && __MINGW32__ == 1) || (defined(__CYGWIN__) && __CYGWIN__ == 1)
const char gp_fmode_rb[] = "rb";
const char gp_fmode_wb[] = "wb";
const char gp_fmode_rb[] = "r";
const char gp_fmode_wb[] = "w";

I guess _MINGW32_ is not defined as 1 though

John Donoghue <lostbard>
Project Member
Mon 16 Feb 2015 01:25:04 AM UTC, comment #42: 

I originally tried making it include the gp_mswin.c stuff, however that led to lots more compile errors.

gp_msdos appears to have some DOS interrupt calls in it, which wont work in Windows.

And using configure appeared to pull in the unix files and appeared convoluted with the way they have implmented variaus make files that are pulled from which seems every which way. Compiling for visual c has its own makefile, which of course isnt even remotely compatible with gnu make.

I had decided then the easiest way was using the unix files as they already compiled ok - although were broken.

Currently it is added as a patch that will only be applied if compiling for mingw. But I will take a look and see if it can be applied differently.

Perhaps just defining gp_fmode_binary_suffix differently for mingw?

John Donoghue <lostbard>
Project Member
Mon 16 Feb 2015 12:51:59 AM UTC, comment #41: 

I see your modification.  Seems like the right idea, and glad it works.  It's not the most portable across ghostscript versions, however.  Is there another way of accomplishing this?

For example, the first code hunk

+#ifdef __MINGW32__
+    // add 'b' to ensure in binary mode
+    char tmpmode[32];
+    strcpy(tmpmode, mode);
+    strcat(tmpmode,"b");
+    mode = tmpmode;

already has some kind of binary switch option in the ghostscript code, here:

/* Open a temporary file, with or without a stream. */
static int
pdf_open_temp_file(gx_device_pdf *pdev, pdf_temp_file_t *ptf)
    char fmode[4];

    if (strlen(gp_fmode_binary_suffix) > 2)

    strcpy(fmode, "w+");
    strcat(fmode, gp_fmode_binary_suffix);
    ptf->file =        gp_open_scratch_file_64(pdev->memory,
    if (ptf->file == 0)
    return 0;

So, all that is needed is for "gp_fmode_binary_suffix" to be defined to "b", as is the case for several operating systems:

base/gp_dosfs.c:const char gp_fmode_binary_suffix[] = "b";
base/gp.h:extern const char gp_fmode_binary_suffix[];
base/gp_macio.c:const char gp_fmode_binary_suffix[] = "b";
base/gp_ntfs.c:const char gp_fmode_binary_suffix[] = "b";
base/gp_os2fs.c:const char gp_fmode_binary_suffix[] = "b";
base/gp_unifn.c:const char gp_fmode_binary_suffix[] = "";
base/gp_vms.c:const char gp_fmode_binary_suffix[] = "";
base/gxclfile.c:        strcat(fmode, gp_fmode_binary_suffix);
base/gxclist.c:    strcat(fmode, gp_fmode_binary_suffix);
base/gxclread.c:        strncat(fmode, gp_fmode_binary_suffix, 1);
base/gxclthrd.c:    strncat(fmode, gp_fmode_binary_suffix, 1);
base/gxclthrd.c:            strncat(fmode, gp_fmode_binary_suffix, 1);
base/gxclthrd.c:            strncat(fmode, gp_fmode_binary_suffix, 1);
base/sfxcommon.c:    strcat(fmode, gp_fmode_binary_suffix);

It seems to me that MXE should be using base/gp_dosfs.c.  Is that correct?  In your previous post you mentioned something about the MXE build using Unix functions/libraries.  How can the compilation be made to use the DOS-oriented files?

The second code hunk you changed, I'm not sure about.  It's the same idea, but for some reason doesn't have the suffix addition code.  Then again, maybe that is done upstream somewhere.  I do see

strncat(fmode, gp_fmode_binary_suffix, 1);

used in several locations.  So, I'd say try configuring the compilation so that base/gp_dosfs.c is compiled and its object file included.  For example, on my linux system (again, linux) I see the file obj/gp_unifn.o but no other gp_*.o file from that group above that contain gp_fmode_binary_suffix definitions.  I would think on the MXE build there should be only a file obj/gp_dosfs.obj.

Dan Sebald <sebald>
Sun 15 Feb 2015 09:45:16 PM UTC, comment #40: 
John Donoghue <lostbard>
Project Member
Sun 15 Feb 2015 08:23:31 PM UTC, comment #39: 

Work fine for me, very good job!

surf(peaks) & print was tested on Windows-7,
with Qt, fltk & gnuplot, print  -dps & print -dpsc.

However, print -deps  does not work:

>> print -deps -debug a12A.ps
Unknown device: epswrite
Unrecoverable error: syntaxerror in --nostringval--
Operand stack:
Unrecoverable error: undefined in .uninstallpagedevice
Operand stack:

The default device for eps should be eps2write, but what
about users with older version of GS, without eps2write device?
Should the default device be depend on GS version?

Avinoam Kalma <avinoam>
Project Member
Sun 15 Feb 2015 06:17:39 PM UTC, comment #38: 

This (attached) appears to work for me.

(file #33084)

John Donoghue <lostbard>
Project Member
Sun 15 Feb 2015 05:54:41 PM UTC, comment #37: 

Using the idea of binary mode, I think I have it - will post patch in a few ...

John Donoghue <lostbard>
Project Member
Sun 15 Feb 2015 05:35:47 PM UTC, comment #36: 

Raising the severity of this bug to Blocker for the 4.0 release.  Currently we don't have a way to print on Windows.

Rik <rik5>
Project Administrator
Sun 15 Feb 2015 03:30:56 PM UTC, comment #35: 

No O_BINARY appears in the logs, however usr/i686-w64-mingw32/include/fcntl.h has a #define in it

Looking at some warnings from the compile that may be clues:

./base/gp_unifs.c:37:0: warning: "ftello" redefined
 #define ftello ftell
In file included from ./base/std.h:158:0,
                 from ./base/stdio_.h:26,
                 from ./base/gp_unifs.c:22:
/home/jade/mxe-octave/usr/i686-w64-mingw32/include/stdio.h:480:0: note: this is the location of the previous definition
 #define ftello ftello64
./base/gp_unifs.c:38:0: warning: "fseeko" redefined
 #define fseeko fseek
In file included from ./base/std.h:158:0,
                 from ./base/stdio_.h:26,
                 from ./base/gp_unifs.c:22:
/home/jade/mxe-octave/usr/i686-w64-mingw32/include/stdio.h:468:0: note: this is the location of the previous definition
 #define fseeko fseeko64

So it is calling the unix sources rather than anything from the VC++/DOS win32.


/home/jade/mxe-octave/usr/bin/i686-w64-mingw32-gcc  -DHAVE_FILE64  -DHAVE_SETLOCALE -DHAVE_BSWAP32  -DHAVE_STRERROR -O -Wall -Wstrict-prototypes -Wundef -Wmissing-declarations -Wmissing-prototypes -Wwrite-strings -Wno-strict-aliasing -Wdeclaration-after-statement -fno-builtin -fno-common -DHAVE_STDINT_H=2 -DHAVE_DIRENT_H=1 -DHAVE_SYS_TIME_H=1 -DHAVE_INTTYPES_H=1 -DGX_COLOR_INDEX_TYPE="unsigned long long" -DNOCONTRIB -DUSE_LIBICONV_GNU -DGS_NO_UTF8=1   -I./obj -I./base -I./devices  -DWHICH_CMS="lcms2" -o ./obj/gdevpipe.o -c ./base/gdevpipe.c
In file included from ./base/gdevpipe.c:19:0:
./base/pipe_.h:41:1: warning: function declaration isn't a prototype [-Wstrict-prototypes]
 extern FILE *popen( /* const char *, const char * */ );
In file included from ./base/std.h:158:0,
                 from ./base/errno_.h:22,
                 from ./base/gdevpipe.c:18:
./base/pipe_.h:41:14: warning: '_popen' redeclared without dllimport attribute: previous dllimport ignored [-Wattributes]
 extern FILE *popen( /* const char *, const char * */ );
./base/pipe_.h:42:12: warning: '_pclose' redeclared without dllimport attribute: previous dllimport ignored [-Wattributes]
 extern int pclose(FILE *);

John Donoghue <lostbard>
Project Member
Sun 15 Feb 2015 09:36:10 AM UTC, comment #34: 

OK, let me retract my statement about binary write (read) being a red herring, step back and say not sure, could be.  :-)

If one looks at the garbage data, if it were the case that gp_ftell_64() (i.e., ultimately ftell) were to indicate a file size larger than the actual size (or the correct size but data is decoded when processed), there could be a last pass of this loop:

    while (left > 0) {
        uint copy = min(left, sbuf_size);

        r = fread(buf, 1, copy, file);
        if (r < 1) {
        if (ss)
            s_arcfour_process_buffer(ss, buf, copy);
        stream_write(s, buf, copy);
        left -= copy;

which grabs garbage data.  An fread() is used, and if that file pointer is associated with a non-binary file, ftell may not match the length of the file if, say, <CR> are tossed in decoding.

I've tried to follow backward through the ghostscript code where the open() is for "gx_device * dev", but because of the use of function pointers in a structure it's difficult to search.

Here is one routine I notice (if ultimately it is used, I'm not sure):

 * Open a temporary file, using O_EXCL and S_I*USR to prevent race
 * conditions and symlink attacks.
static FILE *
gp_fopentemp_generic(const char *fname, const char *mode, bool b64)
    int flags = O_EXCL;
    /* Scan the mode to construct the flags. */
    const char *p = mode;
    int fildes;
    FILE *file;

#if defined (O_LARGEFILE)
    /* It works for Linux/gcc. */
    if (b64)
        flags |= O_LARGEFILE;
    /* fixme : Not sure what to do. Unimplemented. */
    /* MSVC has no O_LARGEFILE, but MSVC build never calls this function. */
    while (*p)
        switch (*p++) {
        case 'a':
            flags |= O_CREAT | O_APPEND;
        case 'r':
            flags |= O_RDONLY;
        case 'w':
            flags |= O_CREAT | O_WRONLY | O_TRUNC;
#ifdef O_BINARY
            /* Watcom C insists on this non-ANSI flag being set. */
        case 'b':
            flags |= O_BINARY;
        case '+':
            flags = (flags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
        default:                /* e.g., 'b' */
    fildes = open(fname, flags, S_IRUSR | S_IWUSR);
    if (fildes < 0)
        return 0;
     * The DEC VMS C compiler incorrectly defines the second argument of
     * fdopen as (char *), rather than following the POSIX.1 standard,
     * which defines it as (const char *).  Patch this here.
    file = fdopen(fildes, (char *)mode); /* still really const */
    if (file == 0)
    return file;

I see in my configuration file

 UFST_CFLAGS="-DGCCx86_64 -DO_BINARY=0 -Dstrcmpi=strcasecmp"

Does -DO_BINARY=0 appear in the MXE build configuration?

Dan Sebald <sebald>
Sun 15 Feb 2015 03:18:48 AM UTC, comment #33: 

I think this binary write() is a red herring.  One reason is that the data between %%EndResource and %%EndProlog is different for the a1.ps and a2.ps examples.

The second reason is that I've compiled ghostscript 9.15 and tried following the flow of events in the code by writing some comments into the EPS file.  Here is the a2.ps code where we see problems:


By use of comments, I've traced this down to the following code hunk inside gdevpdf.c:

    /* Copy the resources into the main file. */

    s = pdev->strm;
    resource_pos = stell(s);
        FILE *rfile = pdev->asides.file;
        int64_t res_end = gp_ftell_64(rfile);

        gp_fseek_64(rfile, 0L, SEEK_SET);
        pdf_copy_data(s, rfile, res_end, NULL);

    if (pdev->ForOPDFRead && pdev->ProduceDSC) {
        int j;

        code = 0;
        pagecount = 1;

        /* All resources and procsets written, end the prolog */
        stream_puts(pdev->strm, "%%EndProlog\n");

That %%EndProlog above is the one written write after the garbage data.  Notice above the code where %%EndProlog is written is the output routine pdf_copy_data().  Let's look at that one:

/* Copy data from a temporary file to a stream. */
pdf_copy_data(stream *s, FILE *file, gs_offset_t count, stream_arcfour_state *ss)
    gs_offset_t r, left = count;
    byte buf[sbuf_size];

stream_puts(s, "%%BEFORE LOOP\n");
    while (left > 0) {
        uint copy = min(left, sbuf_size);

        r = fread(buf, 1, copy, file);
        if (r < 1) {
        if (ss)
            s_arcfour_process_buffer(ss, buf, copy);
        stream_write(s, buf, copy);
stream_puts(s, "%%JUST WROTE TO STREAM\n");
        left -= copy;

OK, with those comments, what I'm seeing in the data is %%JUST WROTE TO STREAM throughout the whole generated EPS file.  In other words, the pdf_copy_data() is used both before and after the %%EndProlog.  So, I have to conclude the routine is fundamentally sound, because if it weren't we wouldn't be able to fix the broken EPS file by removing the garbage data between %%EndResource and %%EndProlog.  There's be bad data throughout the file.

Right before that %%EndProlog it appears that some resource data is being put in place.  Where does that come from?  Are there some resource files somewhere that are to be put at the head of the PostScript file?  Font descriptions?  Default formating?  I don't know.  I'm still trying to figure out how things are pieced together with Octave/ghostscript.

So obviously this pdf_copy_data() is getting data from some bogus location and copying it into the output file.  What could it be?  Maybe

1) Some bad data in one of the resource locations?  Is MXE missing some resource file?  But the garbage data is different in both examples, so not likely.

2) This hunk of code might be dodgy:

    resource_pos = stell(s);
        FILE *rfile = pdev->asides.file;
        int64_t res_end = gp_ftell_64(rfile);

        gp_fseek_64(rfile, 0L, SEEK_SET);
        pdf_copy_data(s, rfile, res_end, NULL);

Notice how there really is no check on the validity of the file stream.  The gp_ftell_64() is supposed to indicate the length of the file, I guess.  gp_fseek_64() is setting the file pointer to the head of the file, presumably.  But what if that rfile pointer is not valid?  In that case gp_fseek_64() would return an error value, in theory.  There really should be a conditional test, e.g.,

        if (!gp_fseek_64(rfile, 0L, SEEK_SET))
            pdf_copy_data(s, rfile, res_end, NULL);

Of course, if res_end is reported as zero, then no problems.  But I notice that the gp_fseek_64 and gp_ftell_64 routines are very system dependent:

base/gp_macio.c:int64_t gp_ftell_64(FILE *strm)
base/gp_mswin.c:gs_offset_t gp_ftell_64(FILE *strm)
base/gp_os2.c:int64_t gp_ftell_64(FILE *strm)
base/gp_unifs.c:int64_t gp_ftell_64(FILE *strm)
base/gp_vms.c:int64_t gp_ftell_64(FILE *strm)

base/gp_macio.c:int gp_fseek_64(FILE *strm, int64_t offset, int origin)
base/gp_mswin.c:int gp_fseek_64(FILE *strm, gs_offset_t offset, int origin)
base/gp_os2.c:int gp_fseek_64(FILE *strm, int64_t offset, int origin)
base/gp_unifs.c:int gp_fseek_64(FILE *strm, int64_t offset, int origin)
base/gp_vms.c:int gp_fseek_64(FILE *strm, int64_t offset, int origin)

I'm wondering if something is not working exactly correctly with the above file routines on MXE.

Let's assume that some bogus file makes it to the point of pdf_copy_data().  There is another chance to catch the issue, in that routine:

        r = fread(buf, 1, copy, file);
        if (r < 1) {

If the read doesn't produce any data (because maybe the "file" pointer is bogus), an error is noted and then returned.  So for bogus data to get through (assuming it isn't originating from the file's contents) the value of r reported back needs to be 1 or greater.  Maybe on MXE that is a meaningless value for a bogus file, while on linux a value of zero comes back for a bogus file.

Just speculating, but I think I'm sniffing more in the general area now.  If someone on MXE can make this change

        if (!gp_fseek_64(rfile, 0L, SEEK_SET))
            pdf_copy_data(s, rfile, res_end, NULL);

and recompile and see if that fixes the problem, maybe it will lead somewhere.

Dan Sebald <sebald>
Sun 15 Feb 2015 12:31:05 AM UTC, comment #32: 

Yes binary open with write() could be the issue.  Let's look at what that "garbage" data is.  I extracted that hunk of 16 bytes, then read it into Octave.  Here is what that 16 bytes of data is:

octave:210> reshape(data, 4, 4)'
ans =

     5   162   117   128
    10    35     1   144
     0     0     0   128
    10    35     1   135

Thinking that write() might be displaying 4 characters as 32-bit float, or 8 characters as 16-bit unsinged, I tried reading in the data with all combinations of float/int/32-bit/16-bit/big-endian/little-endian.  Nothing seems to make sense.  There is some repetition, for sure.

Dan Sebald <sebald>
Sat 14 Feb 2015 09:23:54 PM UTC, comment #31: 

On looking around, I have had some references around about mingw open not being in binary mode vs linux, which was causing extra bytes within files ghostscript at one stage, but it wasnt clear if it is still an issue, which I thought may have something to do with it, but hadnt found dug further into that yet - but would potentally be related then to issues with write at that point.

I will try a recompile and take a look at the log - I would think it should complain about /p being an invalid escale code if thats the issue (I hope)

John Donoghue <lostbard>
Project Member
Sat 14 Feb 2015 08:59:48 PM UTC, comment #30: 

Good little bit of investigation with the extra 16 bytes...

I've attempted a download of the source code for ghostscript at the website downloads.ghostscript.com.  Just to not overlook something, my experience is that the tar expansion reported an error on the file ghostscript-9.15.tar.gz:

[@ ghostscript]$ tar -xzf ghostscript-9.15.tar.gz

gzip: stdin: unexpected end of file
tar: Unexpected EOF in archive
tar: Unexpected EOF in archive
tar: Error is not recoverable: exiting now

while it appears that most of the files expanded in the directory.  Using bunzip2 on the file ghostscript-9.15.tar.bz2 produced no errors about the archive.

OK, so I'm looking at the code in the bunzip directory version, and I will toss out some observations that might lead to ideas.

I see five occurrences of "EndResource" in what looks like it could be file generation code (I've snipped results from the many postscript examples in the tree):

[sebald@moorglade ghostscript-9.15]$ grep EndResource */*/*
devices/vector/gdevpdfu.c:            stream_puts(pdev->strm, "%%EndResource\n");
devices/vector/gdevpsfm.c:        stream_puts(s, "%%EndResource\n");
devices/vector/gdevpsu.c:    "%%EndResource",                /* ProcSet */
lcms2/src/cmsps2.c:        _cmsIOPrintf(mem, "%%%%EndResource\n");
lcms/src/cmsps2.c:    Writef(mem, "%%%%EndResource\n");
Binary file Resource/CIDFont/ArtifexBullet matches

1) In gdevpsu.c the following is intended to be printed at that point where the 16 "garbage" bytes are appearing:

static const char *const psw_end_prolog[] = {
    "end def",
    "%%EndResource",                /* ProcSet */
    "/pagesave null def",        /* establish binding */

In the sample postscript file, I've replaced the garbage bytes with "/pagesave null def" and the PostScript file is readable by "gv".  Could it be that under MXE compilation (remember, I'm clueless about MXE), that the "/p" portion of that string is treated as some kind of escape character causing the printf statement to put out some bogus characters?  (The string is 18 characters long, minus "/p" makes it 16 characters.)  Could MXE require two slashes, "//pagesave null def"?

2) In gdevpsfm.c is the following:

    if (!pcmap->ToUnicode) {
        stream_puts(s, "%%EndResource\n");
        stream_puts(s, "%%EOF\n");

which doesn't look pertinent.  There's no EOF right after EndResource in the sample file.

3) In gdevpdfu.c is

pdf_end_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
    stream_puts(pdev->strm, "endobj\n");
    if (pdev->ForOPDFRead && pdev->ProduceDSC) {
        switch(type) {
            case resourcePage:
            stream_puts(pdev->strm, "%%EndResource\n");
    return 0;

There is an "endobj\n" just previous to the %%EndResource in the sample file.  Just previous to the "endobj" is ">>", that appears in a function called pdf_store_page_resources:

        if (s) {
            stream_puts(s, ">>\n");
            pdf_end_separate(pdev, i);
        /* If an object isn't used, we still need to emit it :-( This is because
         * we reserved an object number for it, and the xref will have an entry
         * for it. If we don't actually emit it then the xref will be invalid.
         * An alternative would be to modify the xref to mark the object as unused.
        if (i != resourceFont && i != resourceProperties)
            pdf_write_resource_objects(pdev, i);

This (gdvpdfu.c) is starting to look like the path the ghostscript program is taking when creating the sample EPS file.  So where does "pdf_write_resource_objects()" go, because that is where the garbage bytes would be?  Basically, it calls:

                    code = cos_write_object(pres->object, pdev, rtype);

and cos_write_object (in gdevpdfo.c) utilizes

    code = cos_write(pco, pdev, pco->id);

which is

/* Write a cos object on the output. */
cos_proc_write(cos_write);        /* check prototype */
cos_write(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
    return pco->cos_procs->write(pco, pdev, object_id);

Whew, getting deep.  And pco is a structure.  And cos_procs is a structure of functions(??), for which write() is called.

#define cos_object_struct(otype_s, etype)\
struct otype_s {\
    const cos_object_procs_t *cos_procs;        /* must be first */\

I can't follow beyond that point.  I could compile the program and print out some of what is being produced here, but if the problem only happens under MXE, then there's no point.  I would say though that this structure function call and object pointer stuff is pretty complex, so it wouldn't surprise me a compiler has problems, if that in fact ends up being the issue.

4) In cmsps2.c is the following code hunk:

    if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {

    Writef(mem, "%%%%EndResource\n");
    Writef(mem, "\n%% CRD End\n");

That doesn't look pertinent.  There's no "CR End" in the sample EPS file.

5) In dscparse.c there looks to be a check against EndResource:

    else if (IS_DSC(line, "%%EndResource")) {
        dsc->id = CDSC_ENDRESOURCE;

I think this is the parsing file for reading postscript input, not generating output.

So, sorry if that was lengthy, and didn't really resolve anything, but I think the warmest trail is #3 above.  If there is someone who can compile under MXE and put some fprintf(stderr,) calls in precise locations involving that pco->cos_procs->write(pco, pdev, object_id);, it might go somewhere.


OK, wait.  Just a little more.  Where might this "cos_write" come from?  I see a header file ijs/unistd.h that has the following:

/* unistd_.h */
#if defined(_WIN32)
#  include <process.h>
#  include <io.h>
#  include <fcntl.h>
#if !defined(__WATCOMC__)
   /* everything except watcom have _read()/_write(),
      watcom has read()/write() but not _read()/_write() */
#  define read(handle, buffer, count) _read(handle, buffer, count)
#  define write(handle, buffer, count) _write(handle, buffer, count)
#endif /* !__WATCOMC__ */
#ifdef _MSC_VER
   /* MSVC alone require this? */
#  define close(fd) _close(fd)
#endif /* _MSC_VER */
#  include <unistd.h>

which has a WIN3D precompile conditional.  So, basically, could it be that after all the structure deciphering, the plain old "write()" command for the compiler is called?  Note that all the other ASCII in that test file, the "endobj", the "%%EndResource", etc. is done using stream_puts() whereas that little bit of garbage data between %%EndResource and %%EndProlog looks like it may be originating from write() instead via what I described in #3.

So, MXE's write() routine... any thoughts?

Dan Sebald <sebald>
Thu 12 Feb 2015 09:31:36 PM UTC, comment #29: 

If you remove the 16 characters between %%EndComments and
%%BeginProlog, the file appears valid. (At least when I remove them with vi I can then display the file ok)

So just have to work out what is adding those 16 bytes...

John Donoghue <lostbard>
Project Member
Wed 11 Feb 2015 09:16:55 PM UTC, comment #28: 

I believe their distributions are created via VC++

John Donoghue <lostbard>
Project Member
Wed 11 Feb 2015 05:17:50 PM UTC, comment #27: 

Bummer.  So there is some magic about how ghostscript gets compiled.  Is there a way to ask, or figure out, how the binaries at downloads.ghostscript.com are generated?

Rik <rik5>
Project Administrator
Wed 11 Feb 2015 11:16:36 AM UTC, comment #26: 

I just did a native mingw compile of ghostscript and it has the same issues creating [e]ps files so it looks like an issue with mingw build rather than any cross compiling.

John Donoghue <lostbard>
Project Member
Tue 10 Feb 2015 07:55:01 PM UTC, comment #25: 

As I wrote, the patch created gs915, but print command creates postscript file with error. If I put in the bin directory
gs 915 + dll, it create a correct file.
Both files are attached, (gs_915mxe.ps is the one created by
mxe octave and gs_915.ps is the file create by windows ghostscript).
The only difference between  them are 16 (grabage?) characters in
line 8220, before %%EndProlog

More remarks:
1. print -deps does not work, since gs 9.15 does not have epswrite device.
2. print-deps2write works, but create bugos file with gs_mxe,
   and correct file with original gs 9.15

Even in natual Windows-XP (not vitrual) "surf (peaks)" causes
crashes (not allways).

Avinoam Kalma <avinoam>
Project Member
Tue 10 Feb 2015 12:41:27 AM UTC, comment #24: 

So other outputs appear to function correctly expect those using eps2write ?

John Donoghue <lostbard>
Project Member
Mon 09 Feb 2015 09:09:14 PM UTC, comment #23: 

It seems that there is a problem with the MXE gs, because if we use original gs it works fine.

Avinoam Kalma <avinoam>
Project Member
Mon 09 Feb 2015 09:06:49 PM UTC, comment #22: 

I did check it. Unfortunaley, it did not work.
The postscript file has errors:

GPL Ghostscript 9.15 (2014-09-22)
Copyright (C) 2014 Artifex Software, Inc.  All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
Loading NimbusSanL-Regu font from %rom%Resource/Font/NimbusSanL-Regu... 4265444
2699638 8045420 6515251 1 done.
%%[ Error handled by opdfread.ps : undefined; OffendingCommand: "u ]%%
>>showpage, press <return> to continue<<

The postscript file is attached (gs_915.ps)

Avinoam Kalma <avinoam>
Project Member
Mon 09 Feb 2015 08:37:30 PM UTC, comment #21: 

I'll have to let Avinoam test this.  Printing workings for simple images, but the exact sequence with "surf (peaks)" causes a segfault.  This could be just my very ancient Windows XP virtual machine.

Rik <rik5>
Project Administrator
Mon 09 Feb 2015 01:19:13 PM UTC, comment #20: 

Another patch for ghostscript 9.15 - it compiles - I haven't had time to run it with anything.

(file #33038)

John Donoghue <lostbard>
Project Member
Sun 08 Feb 2015 10:06:23 PM UTC, comment #19: 

I had the same results as Rik:
I could compile ghostscript 9.10 with John's patch, but
the result ps file was buggy (attached as gs_910mxe.ps).
Using original ghostscript 9.10, I got a better result (gs_910win.ps), though with a minor bug,
(%BeginResource was written instead of %BeginResource)
which was fixed in version 9.14.

As Rik suggested, I think also that we should consider ver. 9.15

BTW, the new gs 9.10 solves bug 44185.

(file #33034, file #33035)

Avinoam Kalma <avinoam>
Project Member
Sun 08 Feb 2015 07:52:28 PM UTC, comment #18: 

I checked in a cset for part #2, using del rather than rm to remove files on Windows platforms (http://hg.savannah.gnu.org/hgweb/octave/rev/f1270e5a3117).

Unfortunately, I had no luck with John's build patch for ghostscript 9.10.  MXE correctly builds in version 9.10.  I can check inside Octave with

system ("gs.exe --version")

However the output is not right.  I have a different version of ghostscript 9.10 that I downloaded directly from downloads.ghostscript.com.  When I copy over gs.exe from that distribution into the bin directory of Octave it works.  Maybe we should just try and move directly to version 9.15 in MXE.

Rik <rik5>
Project Administrator
Sat 07 Feb 2015 10:18:36 PM UTC, comment #17: 

Comment #15 is correct.  We have to distinguish between true Windows and Cygwin.

Rik <rik5>
Project Administrator
Sat 07 Feb 2015 09:09:23 PM UTC, comment #16: 

Heres a patch (attached) to build ghostscript  9.10.
It compiled ok, but I havent had time as yet to rebuld my system and verify/see if it works better or worse.

(file #33028)

John Donoghue <lostbard>
Project Member
Sat 07 Feb 2015 07:26:25 PM UTC, comment #15: 

I believe cygwin (not having used cygwin) ispc = true, isunix = true

John Donoghue <lostbard>
Project Member
Sat 07 Feb 2015 07:01:39 PM UTC, comment #14: 

I can prepare a changeset.
Just a question:

dos_shell = (ispc () && ! isunix ());

why not

dos_shell = ispc ();

Avinoam Kalma <avinoam>
Project Member
Sat 07 Feb 2015 06:48:23 PM UTC, comment #13: 

It seems like this bug should be split into two.

Part 1 is upgrading ghostscript in MXE octave to a recent version with ps2write support.

The second is a bug using Unix 'rm' command on Windows platforms.

I used grep on all of the m-files to find occurrences of rm.  See below:

./plot/util/private/__gnuplot_print__.m:62:          cleanup = [" ; rm " tmp_file];
./plot/util/private/__gnuplot_print__.m:135:        cmd = sprintf ("%s ; rm %s", cmd, opts.ghostscript.source);

./plot/util/private/__ghostscript__.m:116:      cleanup_cmd = sprintf ("rm %s", offsetfile);
./plot/util/private/__ghostscript__.m:151:      cleanup_cmd = sprintf ("rm %s", opts.prepend);
./plot/util/private/__ghostscript__.m:153:      cleanup_cmd = sprintf ("%s ; rm %s", cleanup_cmd, opts.prepend);

./plot/util/print.m:491:      cleanup = sprintf ("; rm %s ", filein);
./plot/util/print.m:503:      cleanup = horzcat (cleanup, sprintf ("; rm %s ", fileout));

The only file with a problem is _ghostscript_.m.  Examples of how to fix it using a conditional test on dos_shell are in _gnuplot_print_.m or print.m.  Could someone prepare a changeset for that?  We should take care of this before the 4.0 release.

Rik <rik5>
Project Administrator
Sat 07 Feb 2015 05:30:17 PM UTC, comment #12: 

Ah, in _ghostscript_.m.  I saw a similar construct in print.m which correctly uses "del" rather than "rm", so that is why I wondered about "dos_shell" value.  Do you want to create a changeset for that?

Dan Sebald <sebald>
Sat 07 Feb 2015 07:33:33 AM UTC, comment #11: 

answer to comment #9:
dos_shell is evaluating to "true".
The problem is in __ghostscript__.m.
In lines 150-154

   if (isempty (cleanup_cmd))
      cleanup_cmd = sprintf ("rm %s", opts.prepend);
      cleanup_cmd = sprintf ("%s ; rm %s", cleanup_cmd, opts.prepend);

the cleanup_cmd always uses "rm" without checking if it is unix or dos. It works well becuase MXE-octave has rm.exe in the bin directory.

Avinoam Kalma <avinoam>
Project Member
Sat 07 Feb 2015 05:48:31 AM UTC, comment #10: 

I just tested with the installed ghostscript version of 9.07 and version 9.10 that I downloaded directly and copied into the Octave bin directory.  The newer edition works correctly.  See also this discussion: http://octave.1599824.n4.nabble.com/mxe-octave-ghostscript-9-07-why-not-9-10-td4665817.html.

The best course of action seems to be to update MXE to use a newer version of ghostscript.  I'm adding John D. to the CC list since he does a lot of the work with MXE.

Rik <rik5>
Project Administrator
Fri 06 Feb 2015 08:36:34 PM UTC, comment #9: 

"Note that there is another bug which uses rm instead of del to delete oct-9hn7O3.ps."

So it seems.  In other words "dos_shell" is not evaluating to "true" when running under MXE?  Is that correct?  (I'm not Windows proficient.)

Dan Sebald <sebald>
Fri 06 Feb 2015 04:07:10 PM UTC, comment #8: 

I meant "No, I didn't build..."

Avinoam Kalma <avinoam>
Project Member
Fri 06 Feb 2015 04:00:58 PM UTC, comment #7: 

No, I did build a new ghostscript to the mxe project
(I wish I knew how to do it...).
I have replaced gs.exe with gs.exe from ver 9.15,
and add directory C:\Octave\Octave-3.8.2\share\ghostscript\9.15
with files from the 9.15 version.
After replacing the gs.exe the print was OK.

Avinoam Kalma <avinoam>
Project Member
Fri 06 Feb 2015 01:59:09 PM UTC, comment #6: 

@Avinoam: Did you download a binary gs 9.15 built for Windows or did you try adding a new ghostscript 9.15 to the mxe build project? In other words, are you sure that it's the new version that fixes the problem, or is it the mxe cross-compiled version of ghostscript that introduces the problem?

Mike Miller <mtmiller>
Project Administrator
Fri 06 Feb 2015 01:28:39 PM UTC, comment #5: 


>> graphics_toolkit gnuplot
>> surf (peaks)
>> print -debug -dpsc a3.ps
---- begin C:\Users\avinoam\AppData\Local\Temp\oct-9hn7O3.ps ----
<< /Margins [0 0] /.HWMargins [0 0 0 0] /PageOffset [18 180] >> setpagedevic
----- end C:\Users\avinoam\AppData\Local\Temp\oct-9hn7O3.ps -----
Ghostscript command: 'C:\Octave\Octave-3.9.0+\bin\gs.exe -dQUIET -dNOPAUSE -
dBATCH -dSAFER -sDEVICE=ps2write -dLanguageLevel=2 -dEmbedAllFonts=true -dEP
SCrop -sPAPERSIZE=letter -dFIXEDMEDIA -sOutputFile="a3.ps" C:\Users\avinoam\
AppData\Local\Temp\oct-9hn7O3.ps C:\Users\avinoam\AppData\Local\Temp\oct-9AP
gs_cmd = C:\Octave\Octave-3.9.0+\bin\gs.exe -dQUIET -dNOPAUSE -dBATCH -dSAFE
R -sDEVICE=ps2write -dLanguageLevel=2 -dEmbedAllFonts=true -dEPSCrop -sPAPER
SIZE=letter -dFIXEDMEDIA -sOutputFile="a3.ps" C:\Users\avinoam\AppData\Local
\Temp\oct-9hn7O3.ps C:\Users\avinoam\AppData\Local\Temp\oct-9APtK3.eps
gnuplot-pipeline: 'C:\Octave\Octave-3.9.0+\bin\gs.exe -dQUIET -dNOPAUSE -dBA
TCH -dSAFER -sDEVICE=ps2write -dLanguageLevel=2 -dEmbedAllFonts=true -dEPSCr
op -sPAPERSIZE=letter -dFIXEDMEDIA -sOutputFile="a3.ps" C:\Users\avinoam\App
Data\Local\Temp\oct-9hn7O3.ps C:\Users\avinoam\AppData\Local\Temp\oct-9APtK3
.eps & rm C:\Users\avinoam\AppData\Local\Temp\oct-9hn7O3.ps'

file oct-9APtK3.eps is attached. Note that there is another bug
which uses rm instead of del to delete oct-9hn7O3.ps.

gs version bundled with Octave under windows is 9.07.
When I have changed it manually to 9.15 (current version)
the plots where good.

(file #33016)

Avinoam Kalma <avinoam>
Project Member
Fri 06 Feb 2015 08:14:44 AM UTC, comment #4: 

I've looked at this a bit.  You are probably correct that the ghostscript executable is the source of the problem.  Clearly what your conversion tool is doing and what my conversion tool is doing are quite different.  It looks like your version of the conversion is using compression (e.g., Lempel Ziv)

currentfile /ASCII85Decode filter /LZWDecode filter cvx exec

I've attached a diff file that you can use to display the exact command that is being used as well as to retain the intermediate file.  You may then experiment.  For example, here is what I see:

>> graphics_toolkit gnuplot
>> surf (peaks)
>> print -dps2write dana2-ps2write.ps
gs_cmd = /usr/bin/gs -dQUIET -dNOPAUSE -dBATCH -dSAFER -sDEVICE=ps2write -dLanguageLevel=2-dEmbedAllFonts=true -dEPSCrop -sOutputFile="a2-ps2write.ps" /tmp/oct-ynDS7Y.eps

My ghostscript is GPL Ghostscript 8.71 (2010-02-10).  Perhaps you have a newer version that features better use of compression but may be buggy still.

I would say that if you can read the intermediate file in ghostscript, (e.g., "gv /tmp/oct-ynDS7Y.eps"), yet the output that ghostscript creates is incorrect, then it is likely ghostscript.  Maybe try updating your version of ghostscript.

(file #33013)

Dan Sebald <sebald>
Thu 05 Feb 2015 09:19:38 PM UTC, comment #3: 

Now with the files

(file #33004, file #33005)

Avinoam Kalma <avinoam>
Project Member
Thu 05 Feb 2015 09:17:56 PM UTC, comment #2: 

Both gnuplot & fltk toolkits

>> graphics_toolkit fltk
>> surf (peaks)
>> print -dpsc a1.ps
>> close all
>> graphics_toolkit gnuplot
>> surf (peaks)
>> print -dpsc a2.ps

a1.ps and a2.ps attached.

If I change the device from ps2write to pswrite I get correct files.

Avinoam Kalma <avinoam>
Project Member
Thu 05 Feb 2015 06:41:46 PM UTC, comment #1: 

gnuplot graphics toolkit, I assume.  Is there some intermediate file that is created?  If so, could you please upload an example?

Here is the options:

  gs_opts = sprintf ("-dQUIET -dNOPAUSE -dBATCH -dSAFER -sDEVICE=%s", opts.device);

and up to that point there is nothing in the code that differentiates between PostScript Level 1 and Level 2, so the only difference seems to be the -spswrite and -sps2write.  Maybe you could experiment with that outside of Octave/gnuplot.

From a sample PS file, we might be able to detect that something was wrong in the PostScript code regardless of whether an update to ghostscript in mxe-octave solves the problem.

Dan Sebald <sebald>
Wed 04 Feb 2015 05:33:36 PM UTC, original submission:  

When using
print -dps
in windows, the resulting postscript file is displayed as empty
The reason for that is prbably the change from device pswrite to ps2write, which seems to be malfunctioned in version 9.07, the
version of gs bundled with mxe-octave.

There are (at least) two solutions:

1. Change the default device again from ps2write to pswrite (only if ispc ()).
2. Bundle a newer version of ghostscript with mxe-octave.

option 2 will also solve bug 44185

Avinoam Kalma <avinoam>
Project Member


(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:

Attached Files
file #33103:  gs_update.patch added by lostbard (4KiB - application/octet-stream)
file #33084:  ghostscript915.patch added by lostbard (19KiB - text/x-patch)
file #33038:  ghost15.patch added by lostbard (16KiB - application/octet-stream)
file #33035:  gs_910win.ps added by avinoam (283KiB - application/postscript)
file #33034:  gs_910mxe.ps added by avinoam (283KiB - application/postscript)
file #33028:  ghostscript-up.patch added by lostbard (14KiB - text/x-patch)
file #33016:  oct-9APtK3.eps added by avinoam (341KiB - application/postscript)
file #33013:  djs_showcommand_2015feb06.diff added by sebald (1KiB - application/octet-stream)
file #33005:  a2.ps added by avinoam (274KiB - application/postscript)
file #33004:  a1.ps added by avinoam (1021KiB - application/postscript)


Depends on the following items: None found

Items that depend on this one: None found


Carbon-Copy List
  • -email is unavailable- added by lostbard (Posted a comment)
  • -email is unavailable- added by rik5 (Posted a comment)
  • -email is unavailable- added by rik5
  • -email is unavailable- added by mtmiller (Posted a comment)
  • -email is unavailable- added by sebald (Posted a comment)
  • -email is unavailable- added by avinoam (Submitted the item)

    There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

    Only project members can vote.





    Follow 18 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2015-02-17 rik5 Open/ClosedOpen Closed
    2015-02-17 rik5 StatusIn Progress Fixed
    2015-02-16 lostbard Attached File- Added gs_update.patch, #33103
    2015-02-15 lostbard Attached File- Added ghostscript915.patch, #33084
    2015-02-15 rik5 Severity3 - Normal 5 - Blocker
    2015-02-09 lostbard Attached File- Added ghost15.patch, #33038
    2015-02-08 avinoam Attached File- Added gs_910mxe.ps, #33034
        Attached File- Added gs_910win.ps, #33035
    2015-02-08 rik5 StatusNeed Info In Progress
    2015-02-07 lostbard Attached File- Added ghostscript-up.patch, #33028
    2015-02-07 rik5 Carbon-Copy- Added lostbard
    2015-02-07 rik5 Assigned toNone lostbard
    2015-02-06 mtmiller CategoryOctave Function Plotting
        StatusNone Need Info
    2015-02-06 avinoam Attached File- Added oct-9APtK3.eps, #33016
    2015-02-06 sebald Attached File- Added djs_showcommand_2015feb06.diff, #33013
    2015-02-05 avinoam Attached File- Added a1.ps, #33004
        Attached File- Added a2.ps, #33005

    Back to the top

    Powered by Savane 3.9