Skip to content

Commit

Permalink
Add GeoPDF, SVG and JPG print formats (#623)
Browse files Browse the repository at this point in the history
* Add GEOPDF option

* Add FORMAT_OPTIONS with WRITE_GEO_PDF:TRUE in case of geopdf

* 🐛 extract forma from service state to has not responsive from state.format service changes

* Use GET http method fro geoPDF.  QGIS 3.34.6-Prizren 'Prizren' (623828f58c2) doesn't support POST

* Fix flow of print

* closing iframe tag #623 (review)

* add `jpg` and `svg` formats + remove unusued props

* 🐛 Resize window when an open page content is show

---------

Co-authored-by: Raruto <Raruto@users.noreply.github.com>
  • Loading branch information
volterra79 and Raruto authored May 8, 2024
1 parent 579c901 commit b39725f
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 72 deletions.
21 changes: 18 additions & 3 deletions src/app/constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,14 +224,29 @@ export const MAP_SETTINGS = {
* @since v3.5
*/
export const PRINT_FORMATS = [
{
value: 'png',
label: 'PNG'
},
/** @since 3.10.0 */
{
value: 'jpg',
label: 'JPG'
},
/** @since 3.10.0 */
{
value: 'svg',
label: 'SVG'
},
{
value: 'pdf',
label: 'PDF'
},
/** @since 3.10.0 */
{
value: 'png',
label: 'PNG'
}
value: 'geopdf',
label: 'GEOPDF'
},
];

/**
Expand Down
78 changes: 44 additions & 34 deletions src/components/Print.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
<div class="box-body">

<transition :duration="500" name="fade">
<bar-loader :loading="state.loading"/>
<bar-loader :loading="state.loading" />
</transition>

<helpdiv message='sdk.print.help'/>
<helpdiv message='sdk.print.help' />

<!-- PRINT TEMPLATE -->
<label for="templates" v-t="'sdk.print.template'"></label>
Expand All @@ -39,11 +39,7 @@
v-select2 = "'state.scale'"
@change = "changeScale"
>
<option
v-for="scale in state.scales"
:value="scale.value">
{{ scale.label }}
</option>
<option v-for="scale in state.scales" :value="scale.value">{{ scale.label }}</option>
</select>

<!-- PRINT DPI -->
Expand All @@ -56,6 +52,7 @@
>
<option v-for="dpi in state.dpis">{{ dpi }}</option>
</select>

<!-- PRINT ROTATION -->
<label for="rotation" v-t="'sdk.print.rotation'"></label>
<input
Expand All @@ -76,11 +73,7 @@
class = "form-control"
v-select2 = "'state.format'"
>
<option
v-for="format in state.formats"
:value="format.value">
{{ format.label }}
</option>
<option v-for="format in state.formats" :value="format.value">{{ format.label }}</option>
</select>

</template>
Expand Down Expand Up @@ -111,7 +104,6 @@
<div
v-if = "state.labels && state.labels.length > 0"
class = "print-labels-content"
style = "color: white"
>
<span
class = "skin-color"
Expand Down Expand Up @@ -172,6 +164,8 @@ import { getMetersFromDegrees } from 'utils/getMetersFromDegrees';
import { downloadFile } from 'utils/downloadFile';
import { printAtlas } from 'utils/printAtlas';
import { print } from 'utils/print';
import { promisify } from 'utils/promisify';


import resizeMixin from 'mixins/resize';

Expand All @@ -192,7 +186,7 @@ export default {
state: this.state || {},
disabled: false,
/** @since 3.10.0 */
atlas_values: [],
atlas_values: [],
};
},

Expand Down Expand Up @@ -244,6 +238,10 @@ export default {
formats: PRINT_FORMATS,
format: PRINT_FORMATS[0].value,
});

/**@since v3.10 Store map extent for print in case of already open print page*/
this.print_extent = null;

},

resize() {
Expand Down Expand Up @@ -319,9 +317,18 @@ export default {
*/
getPrintExtent() {
const map = GUI.getService('map').viewer.map;
const [xmin, ymin] = map.getCoordinateFromPixel([this.state.inner[0], this.state.inner[1]]);
const [xmax, ymax] = map.getCoordinateFromPixel([this.state.inner[2], this.state.inner[3]]);
return (GUI.getService('map').isAxisOrientationInverted() ? [ymin, xmin, ymax, xmax] : [xmin, ymin, xmax, ymax]).join();
// Need to check in case di an open print page
try {
const [xmin, ymin] = map.getCoordinateFromPixel([this.state.inner[0], this.state.inner[1]]);
const [xmax, ymax] = map.getCoordinateFromPixel([this.state.inner[2], this.state.inner[3]]);
this.print_extent = (GUI.getService('map').isAxisOrientationInverted() ? [ymin, xmin, ymax, xmax] : [xmin, ymin, xmax, ymax]).join();
}
catch(e) {
//in case of already open content print page
console.warn(e);
}

return this.print_extent;
},

/**
Expand All @@ -338,6 +345,11 @@ export default {
// disable sidebar
GUI.disableSideBar(true);

// close print page if already open
if (this._page) {
await promisify(GUI.closeContent());
}

// ATLAS PRINT
if (has_atlas) {
download_id = ApplicationService.setDownload(true);
Expand All @@ -358,13 +370,15 @@ export default {
this.state.url = null;
this.state.layers = true;

//In case of already print page open, need to close it otherwise is appended on a dom element
if (this._page) {
GUI.closeContent();
}

this._page = new Component({ service: { state: this.state }, vueComponentObject: vueComp });

// show print page with loading state
GUI.setContent({
content: this._page,
title: 'print',
perc: 100
});

const output = await print(
{
rotation: this.state.rotation,
Expand All @@ -382,30 +396,25 @@ export default {
})),
},
ProjectsRegistry.getCurrentProject().getOwsMethod()
);

)
this.state.url = output.url;
this.state.layers = output.layers;
//after component mount
this._page.getInternalComponent().$on('hook:mounted', () => this.state.loading = false);
// set print area after closing content
this._page.unmount = () => {
GUI.getService('map').viewer.map.once('postrender', this._setPrintArea.bind(this));
this.state.downloading = false;
this.state.loading = false;
return Component.prototype.unmount.call(this._page);
const promise = Component.prototype.unmount.call(this._page);
this._page = null;
return promise;
};

GUI.setContent({
content: this._page,
title: 'print',
perc: 100
});
}

} catch(e) {
err = e;
this.state.loading = false;
// enable sidebar
GUI.disableSideBar(false);
console.warn(e);
}

Expand Down Expand Up @@ -435,7 +444,7 @@ export default {
// close content if open
const reset = !show;
if (reset && this.select2) { this.select2.val(null).trigger('change'); }
if (reset) { this.atlas_values = []; }
if (reset) { this.atlas_values = []; this.print_extent = null; }
if (reset && !this.has_autocomplete) { this.disabled = true }
GUI
.closeContent()
Expand Down Expand Up @@ -689,6 +698,7 @@ export default {
<style scoped>
.print-labels-content {
margin-top: 5px;
color: white;
}
.print-labels-content > span.skin-color {
font-weight: bold;
Expand Down
31 changes: 18 additions & 13 deletions src/components/PrintPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,27 @@

<template>
<div id="print-output">

<transition :duration="500" name="fade">
<bar-loader :loading="state.loading && state.layers"/>
<bar-loader :loading="state.loading && state.layers" />
</transition>

<template v-if="state.layers">
<!-- PRINT as PDF -->
<iframe
v-if = "'pdf' === state.format"
ref = "out"
:src = "state.url"
></iframe>

<!-- PRINT as PDF or GEOPDF-->
<iframe
v-if = "['pdf', 'geopdf'].includes(format)"
ref = "out"
:src = "state.url"
></iframe>

<!-- PRINT as PNG -->
<div
v-else-if = "'png' === state.format"
v-else
class = "g3w-print-png-output"
>
<div id="g3w-print-header">
<div :class="{ 'g3w-disabled': !!(state.downloading && state.layers) }">
<a :href="state.url" :download="`download.${state.format}`">
<a :href="state.url" :download="`download.${format}`">
<button
@click.stop = "downloadImage"
class = "btn skin-button skin-tooltip-left"
Expand All @@ -51,11 +51,13 @@
</div>

</template>

<!---NO PRINT LAYERS-->
<h4
v-else
v-t="'sdk.print.no_layers'">
</h4>

</div>
</template>

Expand All @@ -70,8 +72,11 @@ export default {
name: 'print-page',

data() {
const state = this.$options.service.state || {};
return {
state: this.$options.service.state || {},
state,
// extract `state.format` so it doesnt' react to Print.vue changes
format: state.format,
}
},

Expand All @@ -81,8 +86,8 @@ export default {
try {
GUI.disableSideBar(true);
this.state.downloading = true;
if (['jpg', 'png'].includes(this.state.format)) {
await imageToDataURL({ src: this.state.url, type: `image/${this.state.format}` });
if (['jpg', 'png'].includes(this.format)) {
await imageToDataURL({ src: this.state.url, type: `image/${this.format}` });
setTimeout(() => {
GUI.disableSideBar(false);
this.state.downloading = false;
Expand Down
46 changes: 24 additions & 22 deletions src/utils/print.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,33 +92,35 @@ export function print(opts = {}, method = 'GET') {
return Promise.resolve({layers: false})
}

// force GET request for geopdf because qgiserver support only that method [QGIS 3.34.6-Prizren 'Prizren' (623828f58c2)]
if ('geopdf' === opts.format) {
method = 'GET';
}

const LAYERS = layers.map(l => l.getPrintLayerName()).join();

return FETCH[method]({
url: store.getWmsUrl(),
mime_type: ({ pdf: 'application/pdf', jpg: 'image/jpeg' })[opts.format],
mime_type: ({ pdf: 'application/pdf', jpg: 'image/jpeg', svg: 'image/svg' })[opts.format] || opts.format,
params: {
SERVICE: 'WMS',
VERSION: '1.3.0',
REQUEST: 'GetPrint',
TEMPLATE: opts.template,
DPI: opts.dpi,
STYLES: layers.map(l => l.getStyle()).join(','),
LAYERS: opts.is_maps_preset_theme ? undefined : LAYERS,
FORMAT: opts.format,
CRS: store.getProjection().getCode(),
filtertoken: ApplicationState.tokens.filtertoken,
...(opts.maps || []).reduce((params, map) => {
params[map.name + ':SCALE'] = map.scale;
params[map.name + ':EXTENT'] = map.extent;
params[map.name + ':ROTATION'] = opts.rotation;
params[map.name + ':LAYERS'] = opts.is_maps_preset_theme && undefined === map.preset_theme ? LAYERS : undefined;
return params;
}, {}),
...(opts.labels || []).reduce((params, label) => {
params[label.id] = label.text;
return params;
}, {})
SERVICE: 'WMS',
VERSION: '1.3.0',
REQUEST: 'GetPrint',
TEMPLATE: opts.template,
DPI: opts.dpi,
STYLES: layers.map(l => l.getStyle()).join(','),
LAYERS: opts.is_maps_preset_theme ? undefined : LAYERS,
FORMAT: ({ png: 'png', pdf: 'application/pdf', geopdf: 'application/pdf' })[opts.format] || opts.format,
FORMAT_OPTIONS: 'geopdf' === opts.format ? 'WRITE_GEO_PDF:TRUE': undefined, //@since 3.10.0
CRS: store.getProjection().getCode(),
filtertoken: ApplicationState.tokens.filtertoken,
...(opts.maps || []).reduce((params, map) => Object.assign(params, {
[map.name + ':SCALE']: map.scale,
[map.name + ':EXTENT']: map.extent,
[map.name + ':ROTATION']: opts.rotation,
[map.name + ':LAYERS']: opts.is_maps_preset_theme && undefined === map.preset_theme ? LAYERS : undefined,
}), {}),
...(opts.labels || []).reduce((params, label) => Object.assign(params, { [label.id]: label.text }), {})
},
});
}

0 comments on commit b39725f

Please sign in to comment.