diff --git a/docs/developer-guide/mapstore-migration-guide.md b/docs/developer-guide/mapstore-migration-guide.md
index 37b4da6b92..aeee142c44 100644
--- a/docs/developer-guide/mapstore-migration-guide.md
+++ b/docs/developer-guide/mapstore-migration-guide.md
@@ -22,6 +22,33 @@ This is a list of things to check if you want to update from a previous version
## Migration from 2024.01.00 to 2024.01.02
+### Enable showing credits/attribution text in Print config
+
+Due to showing layers' credits/attributions of printed map which will be displayed at the bottom of the map section, the MapStore `config.yaml` file should be reviewed and updated. Below are reported the relevant changes that need to be applied also to `config.yaml` of MapStore downstream projects where the printing engine is present.
+
+- Added a section for credits into `config.yaml` file at the end of the mainPage for each layout, for more details see [here](https://github.com/geosolutions-it/MapStore2/pull/10451/files#diff-3599ba7c628c7c764665046828bad74c0c8576aad03f5497cf426b59010a6d07R27)
+- In this added section, proper values for `absoluteX` and `absoluteY` should be applied to be consistent with overall layout
+- There are some edits to the value of `absoluteY` for the section located directly above credit/attribution section based on the layout
+
+example:
+
+```yaml
+ mainPage:
+ ....
+ items:
+ ....
+ - !columns
+ absoluteX: 42
+ absoluteY: 35
+ width: 1111
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
+```
+
### Option to hide the group info of logged in user from user details modal window
Recently, we have added the option to hide the `user group info` from the user details modal.
diff --git a/java/printing/resources/geoserver/print/config.yaml b/java/printing/resources/geoserver/print/config.yaml
index 3f5573a2dc..aef6247730 100644
--- a/java/printing/resources/geoserver/print/config.yaml
+++ b/java/printing/resources/geoserver/print/config.yaml
@@ -111,7 +111,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 30
- absoluteY: 740
+ absoluteY: 745
width: 395
widths: [395]
items:
@@ -122,7 +122,7 @@ layouts:
text: '${mapTitle}'
- !columns
absoluteX: 30
- absoluteY: 710
+ absoluteY: 715
width: 395
widths: [395]
items:
@@ -134,7 +134,7 @@ layouts:
vertAlign: middle
- !columns
absoluteX: 30
- absoluteY: 55
+ absoluteY: 60
width: 395
widths: [240, 240]
items:
@@ -153,6 +153,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 30
+ absoluteY: 40
+ width: 395
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
#=======A4 landscape with legend============================================
A4_landscape :
#===========================================================================
@@ -214,7 +224,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 30
- absoluteY: 55
+ absoluteY: 60
width: 782
widths: [240, 300, 240]
items:
@@ -244,6 +254,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 30
+ absoluteY: 40
+ width: 592
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
#=======A4 portrait no legend=============================================
A4_no_legend :
#=========================================================================
@@ -277,7 +297,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 30
- absoluteY: 740
+ absoluteY: 745
width: 395
widths: [395]
items:
@@ -288,7 +308,7 @@ layouts:
text: '${mapTitle}'
- !columns
absoluteX: 30
- absoluteY: 710
+ absoluteY: 715
width: 395
widths: [395]
items:
@@ -300,7 +320,7 @@ layouts:
vertAlign: middle
- !columns
absoluteX: 30
- absoluteY: 55
+ absoluteY: 60
width: 535
widths: [240, 240]
items:
@@ -319,6 +339,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 30
+ absoluteY: 40
+ width: 535
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
#=======A4 landscape no legend=============================================
A4_no_legend_landscape :
#===========================================================================
@@ -352,7 +382,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 30
- absoluteY: 55
+ absoluteY: 60
width: 782
widths: [240, 300, 240]
items:
@@ -382,6 +412,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 30
+ absoluteY: 40
+ width: 780
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
#=======A4 portrait with 2 pages legend=====================================
A4_2_pages_legend :
#===========================================================================
@@ -415,7 +455,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 30
- absoluteY: 740
+ absoluteY: 745
width: 395
widths: [395]
items:
@@ -426,7 +466,7 @@ layouts:
text: '${mapTitle}'
- !columns
absoluteX: 30
- absoluteY: 710
+ absoluteY: 715
width: 395
widths: [395]
items:
@@ -438,7 +478,7 @@ layouts:
vertAlign: middle
- !columns
absoluteX: 30
- absoluteY: 55
+ absoluteY: 60
width: 535
widths: [240, 240]
items:
@@ -457,6 +497,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 30
+ absoluteY: 40
+ width: 535
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
lastPage:
pageSize: 595 842
landscape: false
@@ -527,7 +577,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 30
- absoluteY: 55
+ absoluteY: 60
width: 782
widths: [240, 300, 240]
items:
@@ -557,6 +607,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 30
+ absoluteY: 40
+ width: 780
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
lastPage:
pageSize: 842 595
landscape: false
@@ -655,7 +715,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 42
- absoluteY: 1050
+ absoluteY: 1060
width: 424
widths: [424]
items:
@@ -666,7 +726,7 @@ layouts:
text: '${mapTitle}'
- !columns
absoluteX: 42
- absoluteY: 1000
+ absoluteY: 1010
width: 424
widths: [424]
items:
@@ -678,7 +738,7 @@ layouts:
vertAlign: middle
- !columns
absoluteX: 42
- absoluteY: 50
+ absoluteY: 65
width: 550
widths: [340, 424]
items:
@@ -697,6 +757,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 42
+ absoluteY: 40
+ width: 550
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
#=======A3 landscape with legend============================================
A3_landscape :
#===========================================================================
@@ -758,7 +828,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 42
- absoluteY: 78
+ absoluteY: 85
width: 1105
widths: [340, 424, 340]
items:
@@ -788,6 +858,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 42
+ absoluteY: 60
+ width: 837
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
#=======A3 portrait no legend============================================
A3_no_legend :
#========================================================================
@@ -821,7 +901,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 42
- absoluteY: 1050
+ absoluteY: 1060
width: 424
widths: [424]
items:
@@ -832,7 +912,7 @@ layouts:
text: '${mapTitle}'
- !columns
absoluteX: 42
- absoluteY: 1000
+ absoluteY: 1010
width: 424
widths: [424]
items:
@@ -844,7 +924,7 @@ layouts:
vertAlign: middle
- !columns
absoluteX: 42
- absoluteY: 50
+ absoluteY: 65
width: 760
widths: [340, 424]
items:
@@ -863,6 +943,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 42
+ absoluteY: 40
+ width: 760
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
#=======A3 landscape no legend============================================
A3_no_legend_landscape :
#=========================================================================
@@ -896,7 +986,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 42
- absoluteY: 78
+ absoluteY: 85
width: 1105
widths: [339, 424, 339]
items:
@@ -926,6 +1016,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 42
+ absoluteY: 60
+ width: 1102
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
#=======A3 portrait two pages legend======================================
A3_2_pages_legend :
#=========================================================================
@@ -959,7 +1059,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 42
- absoluteY: 1050
+ absoluteY: 1060
width: 424
widths: [424]
items:
@@ -970,7 +1070,7 @@ layouts:
text: '${mapTitle}'
- !columns
absoluteX: 42
- absoluteY: 1000
+ absoluteY: 1010
width: 424
widths: [424]
items:
@@ -982,7 +1082,7 @@ layouts:
vertAlign: middle
- !columns
absoluteX: 42
- absoluteY: 50
+ absoluteY: 65
width: 760
widths: [340, 424]
items:
@@ -1001,6 +1101,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 42
+ absoluteY: 40
+ width: 760
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
lastPage:
pageSize: 842 1190
landscape: false
@@ -1071,7 +1181,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 42
- absoluteY: 78
+ absoluteY: 85
width: 1105
widths: [339, 424, 339]
items:
@@ -1101,6 +1211,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 42
+ absoluteY: 60
+ width: 1102
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
lastPage:
pageSize: 1190 842
landscape: false
@@ -1199,7 +1319,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 42
- absoluteY: 1520
+ absoluteY: 1525
width: 1105
widths: [424]
items:
@@ -1210,7 +1330,7 @@ layouts:
text: '${mapTitle}'
- !columns
absoluteX: 42
- absoluteY: 1480
+ absoluteY: 1485
width: 1105
widths: [1105]
items:
@@ -1222,7 +1342,7 @@ layouts:
vertAlign: middle
- !columns
absoluteX: 42
- absoluteY: 50
+ absoluteY: 55
width: 837
widths: [340, 424]
items:
@@ -1247,6 +1367,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 42
+ absoluteY: 35
+ width: 837
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
#=======A2 landscape with legend============================================
A2_landscape :
#===========================================================================
@@ -1307,7 +1437,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 42
- absoluteY: 90
+ absoluteY: 85
width: 1105
widths: [339, 423, 339]
items:
@@ -1337,6 +1467,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 42
+ absoluteY: 65
+ width: 1184
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
#=======A2 portrait no legend============================================
A2_no_legend :
#========================================================================
@@ -1370,7 +1510,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 42
- absoluteY: 1520
+ absoluteY: 1525
width: 1105
widths: [424]
items:
@@ -1381,7 +1521,7 @@ layouts:
text: '${mapTitle}'
- !columns
absoluteX: 42
- absoluteY: 1480
+ absoluteY: 1485
width: 1105
widths: [1105]
items:
@@ -1393,7 +1533,7 @@ layouts:
vertAlign: middle
- !columns
absoluteX: 42
- absoluteY: 50
+ absoluteY: 55
width: 1111
widths: [340, 424]
items:
@@ -1418,6 +1558,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 42
+ absoluteY: 35
+ width: 1111
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
#=======A2 landscape no legend============================================
A2_no_legend_landscape :
#=========================================================================
@@ -1451,7 +1601,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 42
- absoluteY: 90
+ absoluteY: 95
width: 1600
widths: [339, 423, 339]
items:
@@ -1481,6 +1631,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 42
+ absoluteY: 75
+ width: 1600
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
#=======A2 portrait two pages legend======================================
A2_2_pages_legend :
#=========================================================================
@@ -1514,7 +1674,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 42
- absoluteY: 1520
+ absoluteY: 1525
width: 1105
widths: [424]
items:
@@ -1525,7 +1685,7 @@ layouts:
text: '${mapTitle}'
- !columns
absoluteX: 42
- absoluteY: 1480
+ absoluteY: 1485
width: 1105
widths: [1105]
items:
@@ -1537,7 +1697,7 @@ layouts:
vertAlign: middle
- !columns
absoluteX: 42
- absoluteY: 50
+ absoluteY: 55
width: 1111
widths: [340, 424]
items:
@@ -1562,6 +1722,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 42
+ absoluteY: 35
+ width: 1111
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
lastPage:
pageSize: 1191 1684
landscape: false
@@ -1632,7 +1802,7 @@ layouts:
rotation: '${rotation}'
- !columns
absoluteX: 42
- absoluteY: 90
+ absoluteY: 95
width: 1600
widths: [339, 423, 339]
items:
@@ -1662,6 +1832,16 @@ layouts:
maxSize: 200
type: 'bar sub'
intervals: 5
+ - !columns
+ absoluteX: 42
+ absoluteY: 75
+ width: 1600
+ items:
+ - !text
+ align: left
+ vertAlign: middle
+ fontSize: 6
+ text: '${credits}'
lastPage:
pageSize: 1684 1191
landscape: false
diff --git a/web/client/utils/PrintUtils.js b/web/client/utils/PrintUtils.js
index 18d2cfd5ff..9068137b69 100644
--- a/web/client/utils/PrintUtils.js
+++ b/web/client/utils/PrintUtils.js
@@ -229,6 +229,46 @@ export const getMapSize = (layout, maxWidth) => {
export const mapProjectionSelector = (state) => state?.print?.map?.projection ?? "EPSG:3857";
+/**
+ * Parse credit/attribution text by removing html tags within its text plus removing '|' symbol
+ * @param {string} creditText the layer credit/attribution text
+ * @returns {string} the parsed credit/attribution text after removing html tags plus '|' symbol within
+ * @memberof utils.PrintUtils
+ */
+export function parseCreditRemovingTagsOrSymbol(creditText = "") {
+ let parsedCredit = creditText;
+ do {
+ let tagStartIndex = parsedCredit.indexOf("<");
+ let tagEndIndex = parsedCredit.indexOf(">");
+ if (tagStartIndex !== -1 && tagEndIndex !== -1) {
+ parsedCredit = parsedCredit.replace(parsedCredit.substring(tagStartIndex, tagEndIndex + 1), "");
+ }
+ } while (parsedCredit.includes("<") || parsedCredit.includes(">"));
+ let hasOrSymbol = parsedCredit && parsedCredit.includes("|");
+ if (hasOrSymbol) {
+ parsedCredit = parsedCredit?.replaceAll("|", "")?.replaceAll(" ", " ");
+ }
+ return parsedCredit;
+}
+/**
+ * Gets the credits of layers in one text with '|' separated
+ * @param {object} layers the map layers for print
+ * @returns {string} the layers credits as a text '|' separated
+ * @memberof utils.PrintUtils
+ */
+export const getLayersCredits = (layers) => {
+ const layerCredits = layers.filter(lay => lay?.credits?.title).map((layer) => {
+ const layerCreditTitle = layer?.credits?.title || '';
+ const hasOrSymbol = layerCreditTitle.includes('|');
+ const hasHtmlTag = layerCreditTitle.includes('<');
+ const layerCredit = (hasHtmlTag || hasOrSymbol)
+ ? parseCreditRemovingTagsOrSymbol(layerCreditTitle)
+ : layerCreditTitle;
+ return layerCredit;
+ }).join(' | ');
+ return layerCredits;
+};
+
/**
* Creates the mapfish print specification from the current configuration
* @param {object} spec the current configuration
@@ -271,6 +311,7 @@ export const getMapfishPrintSpecification = (rawSpec, state) => {
}
],
"legends": PrintUtils.getMapfishLayersSpecification(spec.layers, projectedSpec, state, 'legend'),
+ "credits": getLayersCredits(spec.layers),
...params
};
};
diff --git a/web/client/utils/__tests__/PrintUtils-test.js b/web/client/utils/__tests__/PrintUtils-test.js
index ab8ad094d9..aa9f8a6cdf 100644
--- a/web/client/utils/__tests__/PrintUtils-test.js
+++ b/web/client/utils/__tests__/PrintUtils-test.js
@@ -27,7 +27,9 @@ import {
getPrintVendorParams,
resetDefaultPrintingService,
getDefaultPrintingService,
- getLegendIconsSize
+ getLegendIconsSize,
+ parseCreditRemovingTagsOrSymbol,
+ getLayersCredits
} from '../PrintUtils';
import ConfigUtils from '../ConfigUtils';
import { KVP1, REST1 } from '../../test-resources/layers/wmts';
@@ -1000,5 +1002,55 @@ describe('PrintUtils', () => {
expect(validation["map-preview"].errors).toEqual(["error1", "error2"]);
});
});
+ describe('getting credits text', () => {
+ beforeEach(() => {
+ resetDefaultPrintingService();
+ });
+ it("test parseCreditRemovingTagsOrSymbol", () => {
+ const layerObj = {
+ center: [10, 20],
+ name: "layer 01",
+ credits: {
+ title: 'OSM Simple Light | Rendering GeoSolutions | Data © OpenStreetMap contributors, ODbL'
+ }
+ };
+ const parsedCreditTxt = parseCreditRemovingTagsOrSymbol(layerObj.credits.title);
+ expect(parsedCreditTxt).toEqual('OSM Simple Light Rendering GeoSolutions Data © OpenStreetMap contributors, ODbL');
+ });
+ it("test getLayersCredits", () => {
+ const layersArr = [{
+ center: [10, 20],
+ name: "layer 01",
+ credits: {
+ title: 'OSM Simple Light | Rendering GeoSolutions | Data © OpenStreetMap contributors, ODbL'
+ }
+ },
+ {
+ center: [10, 30],
+ name: "layer 02",
+ credits: {
+ title: 'Attribution layer 02'
+ }
+ }, {
+ center: [20, 30],
+ name: "layer 03"
+ }, {
+ center: [40, 45],
+ name: "layer 04",
+ credits: {
+ title: ''
+ }
+ },
+ {
+ center: [22, 33],
+ name: "layer 05",
+ credits: {
+ title: 'Attribution layer 03 @ | polygon layer'
+ }
+ }];
+ const reqLayersCreditTxt = getLayersCredits(layersArr);
+ expect(reqLayersCreditTxt).toEqual('OSM Simple Light Rendering GeoSolutions Data © OpenStreetMap contributors, ODbL | Attribution layer 02 | Attribution layer 03 @ polygon layer');
+ });
+ });
});
});