Opened 8 years ago

Last modified 5 years ago

#3033 new enhancement

Cairo and PS drivers display only one raster or vector for SVG and PS

Reported by: wenzeslaus Owned by: grass-dev@…
Priority: major Milestone: 7.6.2
Component: Display Version: svn-trunk
Keywords: d.mon, cairo, ps, SVG, vector graphics Cc:
CPU: Unspecified Platform: Unspecified

Description

Running the following creates a image with elevation raster and roadsmajor vector on top of it:

g.region raster=elevation
d.mon cairo o=o.png
d.rast elevation
d.vect roadsmajor
d.mon stop=cairo

However, using Cairo driver with SVG output, you get only the vector which is rendered last:

g.region raster=elevation
d.mon cairo o=o.svg
d.rast elevation
d.vect roadsmajor
d.mon stop=cairo

Same applies for the PS driver:

g.region raster=elevation
d.mon ps o=o.ps
d.rast elevation
d.vect roadsmajor
d.mon stop=ps

It seems that there is no way in GRASS to get full vector graphics output with display monitors.

Change History (17)

in reply to:  description comment:1 by glynn, 8 years ago

Replying to wenzeslaus:

However, using Cairo driver with SVG output, you get only the vector which is rendered last:

This is a limitation of cairo's SVG (and PostScript) "surface" and the fact that d.* commands run as distinct processes. It isn't possible to initialise a surface from an existing SVG file so that subsequent drawing operations will be appended to the document structure, and you can't simply concatenate the files.

In other words, GRASS_RENDER_FILE_READ=TRUE doesn't work for vector formats. You need to generate multiple output files then combine them with external tools.

Same applies for the PS driver:

The PS driver does support combining multiple commands into a single stream, via the environment variables GRASS_RENDER_PS_HEADER and GRASS_RENDER_PS_TRAILER (although I have no idea whether these can be controlled via d.mon).

With GRASS_RENDER_PS_HEADER=FALSE, the output file is opened for append rather than write (i.e. the existing contents are retained) and no header (prologue) is written.

With GRASS_RENDER_PS_TRAILER=FALSE, no trailer is written.

Both variables default to TRUE, meaning that each command will replace any previous file and a header and trailer will be included. To combine the output from multiple commands into a single file, all commands but the first should have GRASS_RENDER_PS_HEADER=FALSE, while all commands but the last should have GRASS_RENDER_PS_TRAILER=FALSE.

Version 0, edited 8 years ago by glynn (next)

comment:2 by wenzeslaus, 8 years ago

Replying to glynn:

It isn't possible to initialise a surface from an existing SVG file so that subsequent drawing operations will be appended to the document structure, and you can't simply concatenate the files.

This is serious limitation. Is there a way to change the rendering to not have this problem.

You need to generate multiple output files then combine them with external tools.

I was able to use HTML:

g.region rast=elevation
d.mon cairo output=elevation.svg
d.rast elevation
d.mon stop=cairo
d.mon cairo output=streams.svg
d.vect streams color=blue
d.mon stop=cairo
d.mon cairo output=roadsmajor.svg
d.vect roadsmajor
d.mon stop=cairo
<html>
<head>
<style>
object {
    position: absolute;
    top: 0;
    left: 0;
}
</style>
<head>
<body>
# also svg element can be used
<object type="image/svg+xml" data="elevation.svg"></object>
<object type="image/svg+xml" data="streams.svg"></object>
<object type="image/svg+xml" data="roadsmajor.svg"></object>
</body>
</html>

Unfortunately, this is not a sufficient workaround if you need to modify the file.

Combining manually in Inkscape was not possible; I was not able to align the layers. Command line Inkscape doesn't read multiple files. ImageMagic creates a raster.

To combine the output from multiple commands into a single file, all commands but the first should have GRASS_RENDER_PS_HEADER=FALSE, while all commands but the last should have GRASS_RENDER_PS_TRAILER=FALSE.

Thanks for the tip, this worked:

g.region rast=elevation
export GRASS_RENDER_PS_HEADER=TRUE
export GRASS_RENDER_PS_TRAILER=FALSE
d.mon ps output=all.ps
d.rast elevation
export GRASS_RENDER_PS_HEADER=FALSE
d.vect streams color=blue
export GRASS_RENDER_PS_TRAILER=TRUE
d.vect roadsmajor
# export here worked as well
d.mon stop=ps

I followed with

pdf2svg all.pdf all.svg 
ps2pdf all.ps all.pdf

to get SVG. But it seems that PS driver is not as powerful as Cairo and same things are lost during the subsequent conversions, page size was wrong as well.

Unfortunately, this doesn't seem to be applicable for GUI which would be likely used when one wants to edit the file afterwards.

in reply to:  2 ; comment:3 by glynn, 8 years ago

Replying to wenzeslaus:

This is serious limitation. Is there a way to change the rendering to not have this problem.

Write SVG to a temporary file, read and parse the original file and temporary file, merge SVG documents, write XML.

Merging the documents isn't exactly trivial, e.g. you need to rename elements in <defs> sections (and any references to them) to avoid name collisions. This might be feasible if we can figure out some bounds on what we can expect cairo to generate, but it's still a fair amount of work and seems fragile (e.g. future versions of cairo may change the document structure).

Combining manually in Inkscape was not possible; I was not able to align the layers.

Inkscape is garbage for anything with technical constraints. I usually end up writing Python scripts to manipulate the XML.

But it seems that PS driver is not as powerful as Cairo and same things are lost during the subsequent conversions, page size was wrong as well.

Converting PostScript invariably loses information. Being a programming language rather than a data format means that you can't extract high-levels structure, you just have to execute it and capture the rendering primitives.

comment:4 by mlennert, 8 years ago

Just thinking out loud here: it seems to me that by trying to do all this we will end up with a second ps.map... Wouldn't it be easier to try to develop a way to translate the d. commands in the current display to a ps.map instruction file and then just run that ?

in reply to:  3 ; comment:5 by wenzeslaus, 8 years ago

Replying to glynn:

Merging the documents isn't exactly trivial...

That's why I wanted to avoid that. It seems that having the rendering functionality split into commands creates many problems.

Inkscape is garbage for anything with technical constraints. I usually end up writing Python scripts to manipulate the XML.

So, it seems. Having such a script in GRASS and also incorporated in GUI would be nice. The role of Inkscape here is really the manual fine tuning afterwards (the main motivation for this ticket).

Converting PostScript invariably loses information.

Makes sense, of course. That's why we need Cairo driver to support it.

in reply to:  4 comment:6 by wenzeslaus, 8 years ago

Replying to mlennert:

Just thinking out loud here: it seems to me that by trying to do all this we will end up with a second ps.map...

I'm not sure what you mean by all this, but my original idea before creating this ticket was to create a map (ideally in GUI) and instead of saving it as PNG, save it in a SVG, so I can edit it Inkscape.

Wouldn't it be easier to try to develop a way to translate the d. commands in the current display to a ps.map instruction file and then just run that ?

That's of course interesting (as well as the other way around is), not even mentioning never finished ps.output. However, PS outputs are not ideal for further editing in Inkscape.

in reply to:  5 comment:7 by glynn, 8 years ago

Replying to wenzeslaus:

That's why I wanted to avoid that. It seems that having the rendering functionality split into commands creates many problems.

It also provides flexibility that a monolithic approach such as ps.map inherently lacks.

Makes sense, of course. That's why we need Cairo driver to support it.

Another option, which isn't entirely trivial but possibly simpler than (reliably) merging SVG documents, would be a "capture" driver which serialises the driver calls and their parameters to a file for subsequent rendering. This way, you could capture the output from multiple d.* commands then render the final result via the cairo driver (or any other driver, but it's only really useful in conjunction with cairo's vector output formats).

Structurally, this would be quite similar to the PS driver. Actually, parsing the output from the PS driver would probably work (mostly), and avoid the need for an additional driver.

Except, we'd need to add the text methods (the cairo driver supports "driver" fonts which cause text rendering to be passed to the driver; the PS driver currently only supports stroke and freetype fonts which are converted to lines or rasters by the driver library then rendered using the driver's line/raster methods).

comment:8 by kuszinger, 8 years ago

Hello,

Starting from the utf-8 ps.map problem I arrived to the very same issues your are talking about here. I think that PS (PDF) output is essential when print is the final target. I happened to me quite often so I worked with ps.map (manual assembly of ps-map command files, no gui, but this is my taste only). When talking ps embedded font or even glyphs converted to paths (lines, strokes, whatever you call them) is also essential for quality. Also, lines as lines, vector as vector. I never missed opacity options in final products however I like it on screen visualizations.

Creating ps layers and merge them would be enough for me in case of utf-8 handling and font->path conversion.

Until then I started to create some scripts which runs well and generates bitmap. I chose oversampling (600dpi) for 300 dpi target. But this is not for a book, so print quality (=unlimited zoom and vector where possible) is not essential.

I'd take part in any effort to make good PS output solution. I'll also share my bitmap map generator once it is good enough.

What does 'good enough' means: I'd like to make it read workspace files since compositions are already there, so no double effort is necessary. Input will be: workspace file, target layout (landscape A4 for example) a saved region and a dpi value. Options and gui later...

It will create one or more BMP fileas at a given resolution also applying opacity as set in the workspace file and then merge them into one file. At this point I use imagemagick. It is available on all platforms as far as I know.

Anyway, I wanted to add one vote for PS output enhancement...

comment:9 by mlennert, 8 years ago

Is this really a major bug, or rather an enhancement wish ?

comment:10 by wenzeslaus, 8 years ago

Type: defectenhancement

All things considered this is an enhancement ticket.

in reply to:  8 comment:11 by wenzeslaus, 8 years ago

For people intersted in alternatives:

Replying to kuszinger:

Starting from the utf-8 ps.map problem ... scripts which runs well and generates bitmap. I chose oversampling (600dpi) for 300 dpi target. ... I'll also share my bitmap map generator once it is good enough. ... read workspace files since compositions are already there, so no double effort is necessary. Input will be: workspace file, target layout (landscape A4 for example) a saved region and a dpi value. ... BMP files at a given resolution also applying opacity as set in the workspace file and then merge them into one file. At this point I use ImageMagick.

This is now available as m.printws in GRASS Addons repository thanks to kuszinger:

comment:12 by wenzeslaus, 8 years ago

Milestone: 7.2.07.4.0

comment:13 by neteler, 6 years ago

Milestone: 7.4.07.4.1

Ticket retargeted after milestone closed

comment:14 by neteler, 6 years ago

Milestone: 7.4.17.4.2

comment:15 by martinl, 6 years ago

Milestone: 7.4.27.6.0

All enhancement tickets should be assigned to 7.6 milestone.

comment:16 by martinl, 5 years ago

Milestone: 7.6.07.6.1

Ticket retargeted after milestone closed

comment:17 by martinl, 5 years ago

Milestone: 7.6.17.6.2

Ticket retargeted after milestone closed

Note: See TracTickets for help on using tickets.