From 2d097ff74844d96abc02b2434ff49da210d6eb3f Mon Sep 17 00:00:00 2001 From: mahmoud adel <58145645+mahmoudadel54@users.noreply.github.com> Date: Fri, 5 Jul 2024 17:29:16 +0300 Subject: [PATCH] #10279: Show credits/attribution text in printed page (#10451) * #10279: Show credits/attribution text in printed page Description: - add credit layer values to print configuration object to send it to the server - create a util function to parse credit text by removing tags and | symbol - edit config.yaml of print - add migration guide - add unit test --- .../mapstore-migration-guide.md | 27 ++ .../resources/geoserver/print/config.yaml | 252 +++++++++++++++--- web/client/utils/PrintUtils.js | 41 +++ web/client/utils/__tests__/PrintUtils-test.js | 54 +++- 4 files changed, 337 insertions(+), 37 deletions(-) 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'); + }); + }); }); });