From 944c68ee8545eae5e6360985b5ef89ea30d10d37 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Wed, 5 Jul 2023 19:46:21 +0200 Subject: [PATCH] [Editor] Try to make the position of an edited FreeText the more accurated as possible - Take into account the page translation, - Take into account the correct translation for the editor border, - Take into account the position of the first glyph in the annotation, - Take into account the rotation of the editor. Close #16633. --- package-lock.json | 58 ++--- package.json | 1 + src/core/annotation.js | 38 ++- src/display/annotation_layer.js | 4 + src/display/editor/editor.js | 21 ++ src/display/editor/freetext.js | 49 +++- test/integration/freetext_editor_spec.js | 300 +++++++++++++++++++++++ test/integration/test_utils.js | 10 +- test/pdfs/.gitignore | 2 + test/pdfs/issue16633.pdf | Bin 0 -> 35196 bytes test/pdfs/rotated_freetexts.pdf | Bin 0 -> 13693 bytes 11 files changed, 424 insertions(+), 59 deletions(-) create mode 100755 test/pdfs/issue16633.pdf create mode 100755 test/pdfs/rotated_freetexts.pdf diff --git a/package-lock.json b/package-lock.json index 8d785591a..8540aa1e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,6 +45,7 @@ "mkdirp": "^3.0.1", "needle": "^3.2.0", "path2d-polyfill": "^2.0.1", + "pngjs": "^7.0.0", "postcss": "^8.4.24", "postcss-dir-pseudo-class": "^7.0.2", "prettier": "^2.8.8", @@ -4337,16 +4338,6 @@ "url": "https://bevry.me/fund" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "optional": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -7329,13 +7320,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, "node_modules/fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -7599,25 +7583,6 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -13202,7 +13167,7 @@ }, "node_modules/npm/node_modules/lodash._baseindexof": { "version": "3.1.0", - "extraneous": true, + "dev": true, "inBundle": true, "license": "MIT" }, @@ -13218,19 +13183,19 @@ }, "node_modules/npm/node_modules/lodash._bindcallback": { "version": "3.0.1", - "extraneous": true, + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/lodash._cacheindexof": { "version": "3.0.2", - "extraneous": true, + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/lodash._createcache": { "version": "3.1.2", - "extraneous": true, + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -13245,7 +13210,7 @@ }, "node_modules/npm/node_modules/lodash._getnative": { "version": "3.9.1", - "extraneous": true, + "dev": true, "inBundle": true, "license": "MIT" }, @@ -13263,7 +13228,7 @@ }, "node_modules/npm/node_modules/lodash.restparam": { "version": "3.6.1", - "extraneous": true, + "dev": true, "inBundle": true, "license": "MIT" }, @@ -16130,6 +16095,15 @@ "node": ">=4" } }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "dev": true, + "engines": { + "node": ">=14.19.0" + } + }, "node_modules/posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", diff --git a/package.json b/package.json index 325597997..78ddaea4e 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "mkdirp": "^3.0.1", "needle": "^3.2.0", "path2d-polyfill": "^2.0.1", + "pngjs": "^7.0.0", "postcss": "^8.4.24", "postcss-dir-pseudo-class": "^7.0.2", "prettier": "^2.8.8", diff --git a/src/core/annotation.js b/src/core/annotation.js index a6f658e80..bfa9caabe 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -546,7 +546,7 @@ class Annotation { const MK = dict.get("MK"); this.setBorderAndBackgroundColors(MK); - this.setRotation(MK); + this.setRotation(MK, dict); this.ref = params.ref instanceof Ref ? params.ref : null; this._streams = []; @@ -838,18 +838,21 @@ class Annotation { } } - setRotation(mk) { + setRotation(mk, dict) { this.rotation = 0; + let angle; if (mk instanceof Dict) { - let angle = mk.get("R") || 0; - if (Number.isInteger(angle) && angle !== 0) { - angle %= 360; - if (angle < 0) { - angle += 360; - } - if (angle % 90 === 0) { - this.rotation = angle; - } + angle = mk.get("R") || 0; + } else { + angle = dict.get("Rotate") || 0; + } + if (Number.isInteger(angle) && angle !== 0) { + angle %= 360; + if (angle < 0) { + angle += 360; + } + if (angle % 90 === 0) { + this.rotation = angle; } } } @@ -1069,6 +1072,7 @@ class Annotation { const text = []; const buffer = []; + let firstPosition = null; const sink = { desiredSize: Math.Infinity, ready: true, @@ -1078,6 +1082,7 @@ class Annotation { if (item.str === undefined) { continue; } + firstPosition ||= item.transform.slice(-2); buffer.push(item.str); if (item.hasEOL) { text.push(buffer.join("")); @@ -1102,6 +1107,17 @@ class Annotation { } if (text.length > 1 || text[0]) { + const appearanceDict = this.appearance.dict; + const bbox = appearanceDict.getArray("BBox") || [0, 0, 1, 1]; + const matrix = appearanceDict.getArray("Matrix") || [1, 0, 0, 1, 0, 0]; + const rect = this.data.rect; + const transform = getTransformMatrix(rect, bbox, matrix); + transform[4] -= rect[0]; + transform[5] -= rect[1]; + firstPosition = Util.applyTransform(firstPosition, transform); + firstPosition = Util.applyTransform(firstPosition, matrix); + + this.data.textPosition = firstPosition; this.data.textContent = text; } } diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index 5c576635b..a9d219bc8 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -304,6 +304,9 @@ class AnnotationElement { } setRotation(angle, container = this.container) { + if (!this.data.rect) { + return; + } const { pageWidth, pageHeight } = this.parent.viewport.rawDims; const { width, height } = getRectDims(this.data.rect); @@ -2210,6 +2213,7 @@ class FreeTextAnnotationElement extends AnnotationElement { ); super(parameters, { isRenderable, ignoreBorder: true }); this.textContent = parameters.data.textContent; + this.textPosition = parameters.data.textPosition; this.annotationEditorType = AnnotationEditorType.FREETEXT; } diff --git a/src/display/editor/editor.js b/src/display/editor/editor.js index 826a70fbc..371f70596 100644 --- a/src/display/editor/editor.js +++ b/src/display/editor/editor.js @@ -296,6 +296,24 @@ class AnnotationEditor { } } + /** + * Convert a page translation into a screen one. + * @param {number} x + * @param {number} y + */ + pageTranslationToScreen(x, y) { + switch (this.parentRotation) { + case 90: + return [-y, x]; + case 180: + return [-x, -y]; + case 270: + return [y, -x]; + default: + return [x, y]; + } + } + get parentScale() { return this._uiManager.viewParameters.realScale; } @@ -398,6 +416,9 @@ class AnnotationEditor { this.#hasBeenSelected = true; } + /** + * Convert the current rect into a page one. + */ getRect(tx, ty) { const scale = this.parentScale; const [pageWidth, pageHeight] = this.pageDimensions; diff --git a/src/display/editor/freetext.js b/src/display/editor/freetext.js index e28536f9e..59a248b67 100644 --- a/src/display/editor/freetext.js +++ b/src/display/editor/freetext.js @@ -502,8 +502,47 @@ class FreeTextEditor extends AnnotationEditor { // This editor was created in using copy (ctrl+c). const [parentWidth, parentHeight] = this.parentDimensions; if (this.annotationElementId) { - const [tx] = this.getInitialTranslation(); - this.setAt(baseX * parentWidth, baseY * parentHeight, tx, tx); + // This stuff is hard to test: if something is changed here, please + // test with the following PDF file: + // - freetexts.pdf + // - rotated_freetexts.pdf + // Only small variations between the original annotation and its editor + // are allowed. + + // position is the position of the first glyph in the annotation + // and it's relative to its container. + const { position } = this.#initialData; + let [tx, ty] = this.getInitialTranslation(); + [tx, ty] = this.pageTranslationToScreen(tx, ty); + const [pageWidth, pageHeight] = this.pageDimensions; + const [pageX, pageY] = this.pageTranslation; + let posX, posY; + switch (this.rotation) { + case 0: + posX = baseX + (position[0] - pageX) / pageWidth; + posY = baseY + this.height - (position[1] - pageY) / pageHeight; + break; + case 90: + posX = baseX + (position[0] - pageX) / pageWidth; + posY = baseY - (position[1] - pageY) / pageHeight; + [tx, ty] = [ty, -tx]; + break; + case 180: + posX = baseX - this.width + (position[0] - pageX) / pageWidth; + posY = baseY - (position[1] - pageY) / pageHeight; + [tx, ty] = [-tx, -ty]; + break; + case 270: + posX = + baseX + + (position[0] - pageX - this.height * pageHeight) / pageWidth; + posY = + baseY + + (position[1] - pageY - this.width * pageWidth) / pageHeight; + [tx, ty] = [-ty, tx]; + break; + } + this.setAt(posX * parentWidth, posY * parentHeight, tx, ty); } else { this.setAt( baseX * parentWidth, @@ -521,6 +560,10 @@ class FreeTextEditor extends AnnotationEditor { this.editorDiv.contentEditable = true; } + if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) { + this.div.setAttribute("annotation-id", this.annotationElementId); + } + return this.div; } @@ -554,6 +597,7 @@ class FreeTextEditor extends AnnotationEditor { id, }, textContent, + textPosition, parent: { page: { pageNumber }, }, @@ -569,6 +613,7 @@ class FreeTextEditor extends AnnotationEditor { color: Array.from(fontColor), fontSize, value: textContent.join("\n"), + position: textPosition, pageIndex: pageNumber - 1, rect, rotation, diff --git a/test/integration/freetext_editor_spec.js b/test/integration/freetext_editor_spec.js index 69062ff5a..ad80a14f5 100644 --- a/test/integration/freetext_editor_spec.js +++ b/test/integration/freetext_editor_spec.js @@ -25,6 +25,8 @@ const { waitForStorageEntries, } = require("./test_utils.js"); +const PNG = require("pngjs").PNG; + const copyPaste = async page => { let promise = waitForEvent(page, "copy"); await page.keyboard.down("Control"); @@ -1379,4 +1381,302 @@ describe("FreeText Editor", () => { ); }); }); + + describe("FreeText (open existing)", () => { + let pages; + + beforeAll(async () => { + pages = await loadAndWait( + "issue16633.pdf", + ".annotationEditorLayer", + 100 + ); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must open an existing annotation and check that the position are good", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await page.click("#editorFreeText"); + + await page.evaluate(() => { + document.getElementById("editorFreeTextParamsToolbar").remove(); + }); + + const toBinary = buf => { + for (let i = 0; i < buf.length; i += 4) { + const gray = + (0.2126 * buf[i] + 0.7152 * buf[i + 1] + 0.0722 * buf[i + 2]) / + 255; + buf[i] = buf[i + 1] = buf[i + 2] = gray <= 0.5 ? 0 : 255; + } + }; + + // We want to detect the first non-white pixel in the image. + // But we can have some antialiasing... + // The idea to just try to detect the beginning of the vertical bar + // of the "H" letter. + // Hence we just take the first non-white pixel in the image which is + // the most repeated one. + const getFirstPixel = (buf, width, height) => { + toBinary(buf); + const firsts = []; + const stats = {}; + // Get the position of the first pixels. + // The position of char depends on a lot of different parameters, + // hence it's possible to not have a pixel where we expect to have + // it. So we just collect the positions of the first black pixel and + // take the first one where its abscissa is the most frequent. + for (let i = height - 1; i >= 0; i--) { + for (let j = 0; j < width; j++) { + const idx = (width * i + j) << 2; + if (buf[idx] === 0) { + firsts.push([j, i]); + stats[j] = (stats[j] || 0) + 1; + break; + } + } + } + + let maxValue = -Infinity; + let maxJ = 0; + for (const [j, count] of Object.entries(stats)) { + if (count > maxValue) { + maxValue = count; + maxJ = j; + } + } + maxJ = parseInt(maxJ, 10); + for (const [j, i] of firsts) { + if (j === maxJ) { + return [j, i]; + } + } + return null; + }; + + for (const n of [0, 1, 2, 3, 4]) { + const rect = await page.$eval(getEditorSelector(n), el => { + // With Chrome something is wrong when serializing a DomRect, + // hence we extract the values and just return them. + const { x, y, width, height } = el.getBoundingClientRect(); + return { x, y, width, height }; + }); + const editorPng = await page.screenshot({ + clip: rect, + type: "png", + }); + const editorImage = PNG.sync.read(editorPng); + const editorFirstPix = getFirstPixel( + editorImage.data, + editorImage.width, + editorImage.height + ); + + await page.evaluate(N => { + const editor = document.getElementById( + `pdfjs_internal_editor_${N}` + ); + const annotationId = editor.getAttribute("annotation-id"); + const annotation = document.querySelector( + `[data-annotation-id="${annotationId}"]` + ); + editor.hidden = true; + annotation.hidden = false; + }, n); + await page.waitForTimeout(10); + const annotationPng = await page.screenshot({ + clip: rect, + type: "png", + }); + const annotationImage = PNG.sync.read(annotationPng); + const annotationFirstPix = getFirstPixel( + annotationImage.data, + annotationImage.width, + annotationImage.height + ); + + expect( + Math.abs(editorFirstPix[0] - annotationFirstPix[0]) <= 3 && + Math.abs(editorFirstPix[1] - annotationFirstPix[1]) <= 3 + ) + .withContext( + `In ${browserName}, first pix coords in editor: ${editorFirstPix} and in annotation: ${annotationFirstPix}` + ) + .toEqual(true); + } + }) + ); + }); + }); + + describe("FreeText (open existing and rotated)", () => { + let pages; + + beforeAll(async () => { + pages = await loadAndWait( + "rotated_freetexts.pdf", + ".annotationEditorLayer", + 100 + ); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must open an existing rotated annotation and check that the position are good", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await page.click("#editorFreeText"); + + await page.evaluate(() => { + document.getElementById("editorFreeTextParamsToolbar").remove(); + }); + + const toBinary = buf => { + for (let i = 0; i < buf.length; i += 4) { + const gray = + (0.2126 * buf[i] + 0.7152 * buf[i + 1] + 0.0722 * buf[i + 2]) / + 255; + buf[i] = buf[i + 1] = buf[i + 2] = gray >= 0.5 ? 255 : 0; + } + }; + + const getFirstPixel = (buf, width, height, start) => { + toBinary(buf); + const firsts = []; + const stats = {}; + switch (start) { + case "TL": + for (let j = 0; j < width; j++) { + for (let i = 0; i < height; i++) { + const idx = (width * i + j) << 2; + if (buf[idx] === 0) { + firsts.push([j, i]); + stats[j] = (stats[j] || 0) + 1; + break; + } + } + } + break; + case "TR": + for (let i = 0; i < height; i++) { + for (let j = width - 1; j >= 0; j--) { + const idx = (width * i + j) << 2; + if (buf[idx] === 0) { + firsts.push([j, i]); + stats[j] = (stats[j] || 0) + 1; + break; + } + } + } + break; + case "BR": + for (let j = width - 1; j >= 0; j--) { + for (let i = height - 1; i >= 0; i--) { + const idx = (width * i + j) << 2; + if (buf[idx] === 0) { + firsts.push([j, i]); + stats[j] = (stats[j] || 0) + 1; + break; + } + } + } + break; + case "BL": + for (let i = height - 1; i >= 0; i--) { + for (let j = 0; j < width; j++) { + const idx = (width * i + j) << 2; + if (buf[idx] === 0) { + firsts.push([j, i]); + stats[j] = (stats[j] || 0) + 1; + break; + } + } + } + break; + } + + let maxValue = -Infinity; + let maxJ = 0; + for (const [j, count] of Object.entries(stats)) { + if (count > maxValue) { + maxValue = count; + maxJ = j; + } + } + maxJ = parseInt(maxJ, 10); + for (const [j, i] of firsts) { + if (j === maxJ) { + return [j, i]; + } + } + return null; + }; + + for (const [n, start] of [ + [0, "BL"], + [1, "BR"], + [2, "TR"], + [3, "TL"], + ]) { + const rect = await page.$eval(getEditorSelector(n), el => { + // With Chrome something is wrong when serializing a DomRect, + // hence we extract the values and just return them. + const { x, y, width, height } = el.getBoundingClientRect(); + return { x, y, width, height }; + }); + const editorPng = await page.screenshot({ + clip: rect, + type: "png", + }); + const editorImage = PNG.sync.read(editorPng); + const editorFirstPix = getFirstPixel( + editorImage.data, + editorImage.width, + editorImage.height, + start + ); + + await page.evaluate(N => { + const editor = document.getElementById( + `pdfjs_internal_editor_${N}` + ); + const annotationId = editor.getAttribute("annotation-id"); + const annotation = document.querySelector( + `[data-annotation-id="${annotationId}"]` + ); + editor.hidden = true; + annotation.hidden = false; + }, n); + await page.waitForTimeout(10); + const annotationPng = await page.screenshot({ + clip: rect, + type: "png", + }); + const annotationImage = PNG.sync.read(annotationPng); + const annotationFirstPix = getFirstPixel( + annotationImage.data, + annotationImage.width, + annotationImage.height, + start + ); + + expect( + Math.abs(editorFirstPix[0] - annotationFirstPix[0]) <= 3 && + Math.abs(editorFirstPix[1] - annotationFirstPix[1]) <= 3 + ) + .withContext( + `In ${browserName}, first pix coords in editor: ${editorFirstPix} and in annotation: ${annotationFirstPix}` + ) + .toEqual(true); + } + }) + ); + }); + }); }); diff --git a/test/integration/test_utils.js b/test/integration/test_utils.js index 454103f3f..23b315508 100644 --- a/test/integration/test_utils.js +++ b/test/integration/test_utils.js @@ -13,7 +13,7 @@ * limitations under the License. */ -exports.loadAndWait = (filename, selector) => +exports.loadAndWait = (filename, selector, zoom) => Promise.all( global.integrationSessions.map(async session => { const page = await session.browser.newPage(); @@ -33,9 +33,11 @@ exports.loadAndWait = (filename, selector) => }); }); - await page.goto( - `${global.integrationBaseUrl}?file=/test/pdfs/${filename}` - ); + let url = `${global.integrationBaseUrl}?file=/test/pdfs/${filename}`; + if (zoom) { + url += `#zoom=${zoom}`; + } + await page.goto(url); await page.bringToFront(); await page.waitForSelector(selector, { timeout: 0, diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 252d61271..a57c8ef4b 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -602,3 +602,5 @@ !freetexts.pdf !issue16553.pdf !empty.pdf +!rotated_freetexts.pdf +!issue16633.pdf diff --git a/test/pdfs/issue16633.pdf b/test/pdfs/issue16633.pdf new file mode 100755 index 0000000000000000000000000000000000000000..425823d36e34d7469632311c9b2ec3f6a90d9374 GIT binary patch literal 35196 zcmeIb2b>dC*EkG_0;@EsqG(t;%Wfu zt_ae#J6(u%LrM_pqTTy9N)8CoWuc@!;B^jiLjyDh4~o(Z?g63hbTJJ@=rRLc9Cf)2 zu5{dxiMY&BZ`>87EB|$KDrL8@KM}A;i-*_=#O3DknZ+Eti&4yC^H{}vC(0_eGo4HZ z!EhlCo@;1#x(3?G4WF*1A#|OKi1(+VOjZel!Jskt+!8*EO+#7S5;l`bLs7toKH-Q% zU#dGsH&h~p!$c4c1AYYX!$%M<4}Ms1&qfdw5WuOt=ZGqwWBF%XRvDcd9UTjW!tt`Q z#s#jBGe;qcE>)z$@o+j%69%({g`zCzO$owgqXX$;Z-7FJH~^Fox`?pTMK5=S$he2b z=fQ|#;Y5^h#emXcKr;NPiE~Ke_Lz%$LYKM%NmtxU*y*AWK=Fo1y4f4TLNRZ3x@z!L ze;H-D>n=jV(O_9wEDn4TY+R6@Q*;X5!$dT7T8!S7?IJD^i?ffp3q;EcP84JY%N|+Q z8yz@(*XfpT4PI8KW!Eugrp6wHDL|uI(9!4yZ#>}Yrm>SQTGH!^lCJJG0Z>({00@DhDp|~8 zwZQ`}AEkv7fdKFlT@p5gYgS}XC37|V54_6=z>9a`RnrT`7lo#)sYPS3p&0Z{We>Vw z$|j=Kw8_yc&t(*;YqeO5%lhC?ntJxcL;+#7JZXiPr6A`y4Qh(fmFaV~zqjrK4l5{fR4dlQET<-YYRMbm);`E?5 zl!(zIu4vp7PQ>gXXTBQ%-U0NePUNcQItWklHFK%E`86_gjpeGxqjsk&Xpj2oZZGtU zULFfjJyeV6`aG(vPT(RKbc}AxX8^(iu2{@V59Eu6UG!M~NjCR~#^4}yQ6d_K8y1~N zL@63%Ai)6=g#E6N!ybhck6sO!2uCvc&cVyY?ScWrl%p}YC_NA+LE;2J^3hA+m(%5@ zM_r^h1`^HXqzCN;MNFrQgx3H)5=g}IxQeI3u>>H@8;;WB9(Y!rvJ;88iylnC%Y;dx z)EjnEq|ZSkTuyHwV5bA!tII=wg7z4Zpkx5l;ZYAO680!1QQ_8O54iI!RA$EDt$@<8 z9AnV2d}~+@X2WuqrCS63FOt`$rK;@(IgLs#dCYs^&CY=QnKa^Fu7wrFDS;eU#tEv=dh1W?bR(a*MQqWNv zZ!KM!QA_3imqsDVylWKZOviAv8wO^Mi-y4~9uz^E2K0#m-8igbhc`Z;I~ZXl%o4CF z8SL(KW!QPInF*E^6SaYjnKLt?t}{WnqTN7YQ0r+XS2PA%7mYHN045H~mlNzIF^2J)a9NB88mJT&Dd=FHRobcy20~Zb<54e|xl{`X z{4TBpTwDox;DgLr$`qlYc`QKZffWe7E8d?D1g2A-gMpA)Ww%nz=0{~wJA7d5-!)jJ zV^K}Fv*<^Am|P}T2bTL@CYQ_7*V)$v?9l8C_YAOI2V@3}8Zf%xv3?CZyc%odYt+Dp zYNQQ(MQ5W0C-z;Fe$)Mtx3vvrk323|TQuwGr+$9jGOK9hqKG%`=`qq-v3_*q(P-WJ zZR>1#{MlC@XuRgQs)`N3bCR>j0TBi_{32=KP0@k9UZrZlEeuf@CaN;3>^~T9rKL~V zfV7neqrO`-Q*Ox8kV9{DL3Wz&|1^0=?02SBTprIcq4~62g?v)+QeR*mLxz@DCKI?)$%1&(m{?C%jis4{Ij4 z`PaDVM^?UZ^ziz*re^Q7YJKa)KUxYOExt9Z0jtC0DTVY7(d*k!cle;$!KkCjQ?X#+ zF|^gkLzX5Ena01Q-)H$G$^Ff?@#MyzMlL-+#yOxP@p`v;mWi-lBo0eF2 zVK26`?Dk?egYD#@F0KoLs@?M;)BjyMEdx|u(5Z7eZ%v){-)NNgDz!DVUi7X>ajyzn zThFb9_Pk1aze3ujBNy%Uua#1Ws+6$#5N75xN+5_1UITs!3g$f%0kfKY&s+!n6y|r{ zO8Mt1DYK?cR~1?=26qV)Vw1cQ4zv!T?*B*t@X-6_KiH5DocCgj7@_^+d0%vDU(@B9 z$mPTtRI`W8I>`KxrCYs+$z*C@jE>W--@N|vx;gk~1y5Y*`E^TNx9-Z(!XKV!`NYJQ z_`Y?l-mF&m`|Ab>)(sf3t$ws_ofA(sk3V=;*VNG~JrYca+EC$vC z5(EcQl}*>D&`vqTN<*Cy&BTk-^ z-xK_FD3gvb=?qpegHg)hm$Ept%B!%f0PcE2=&e*I6j!zfsaiHRPgj-!f>g5de81#S9m{M6?^?}U=gAc43sezAL_22{ zCsFFAoB;7Mdn6KoAa5=Z4#@0Hzx$|0dBBrWSqlEF1Vv$)uq=`AI!jqR29xV@*^5yw zpI^*E`LKeAFLP$Uj2vLv%ev*FVC*KPvo?yB?zJA7S7h6@JKF|83|h zxNB@c#JW#m3QUOy8#FcJ*7;Sz5c3UE%YX&pKq455(YO#Rh3GI54?~a;1?R;B>^+&V ze{X@1BNCx3j4NWZaD;_n94-^ZI4n#ogU}Wo zEDSVLkzrW)3=!R`b-(F3grm-gJ9l3HjMcH@NcUix^yRj`NLj}NKi}zhnZ1RJ^nKJf ztZ<}zPMt*~F5e;T>t24N6tWKSwEn36sdBAp6n{b=+|KuX@)t(?sQ(lTGL}^%VNg)}_r*pxe zoaU>}#hbfXLwL+?Q6Wm9(1>YeLL4(n@KnSqm)ZhENELCQY@1vn&R9`T#2GYYOadL^ zVB%J}gzvQnO+KeosmCyhBrN7>g$UcAM@?*-S%(urRoHF;7;%wIVA5qAC?6p)Nm3;6 zmkUK=ffjM9%#5_t65x|ov)pGx<4LE*j*(V{&?i?I0_+NJ#188&%gwArl#6E=M!zFu ziaAs~tt5r0g=tJD@+ggBoz)`2{dP$@<&=7Oq)adK$pyxsJVQnlUVp4y&q^u<{-na7 zjH!f1L}s{(cjKW3A_SS2Qw6UvAzx`0cn zlM{$7;*yyBdWk7wLp_KkjTxNMK+0}rhqM+QLIh#!0F)&da-%K)^`y1ynb3gWDe<^P zxHAc5Lr~A-5SdtZvrPl(fFq>yI3XoGKWnkblTL{kv77k`E%XPIrNtuqS|sme_>1!E1K_*4$9k16dqN8BYxCotX;<>HktHB9pHr>ut@?4 zf#@OxD)vHqI3dLYR*T%@2&w`GCdHHT2++o6mx!}Q)Z?*9_)N1!9U93kwIGP#cU@5t z31uW*6kQ}zy$)B2bpfZ^m|+87Fak0}DlTVna6FKulbA)X7X=i0M%Zsf(;kAUN?4(c zS)lU)uCfGb%9{LaLymTKn2=(R+K5!nd6i0SNUO9aQ^aBjC=AOkHD zae_btK0-pIl|Ey}B2gu+C?D!W86S~SYa;}teGcGvYW@(pIT}dVvUpW~ikdUFAV1?U z;9QHumV~*ESc7T8=EDhFNW}yhoP~TJ@O1`ok0=E=;3SROEYM$6#C2;UP%l4koDz9V z%}8f#W^G1gFcCJUJYX}ka`ym}VKc}WDg%rmX!0w?=@9%4IG~;UGb|5F5FwziDXWHG{}beW6D^~X@W4T z2m<;J@{6Klz!9{00MALn2dp1R2LVoy$Pz3Wz_a;eq+W_*BBxxYH>cwkBimrGm=Jgm z#B4@N21Dz`M)3&@E|5tsZ~CYTH}|CjZjyv6PuvDfKZ#pq$FN}Nf4e+C?Y|m zq-6$4AmK>Kv>1lRfv++~zf&L+du(PeCe4zV0K@zsTd*`d2b!o-29ru+$_XP}t}`l0 zcwb4i6sUVHe%y*-02RZ8hH|4vD+5UXM;9*k5mGqBrSsA~ zoiT~!)FY9GdW@w*_oTh|q;v8Dib4%ll8^?dcYfF@z{1vypHv$BY&9hZEbv|f{i56i z^h4t?_q3!I<;P*ZC0sC2kbPNeDhGx7rO(mRo1-lXFi@2j$&~v<)dg}lxICNV6H!2{ z&05oHgB2||$P&sN;%XQDrzwgC@M_Cn8kG(PN8RJ%SlVhfg&)9a@HEe_`8pV$q)MqZ z6ql&Awctq?1O1e!BDLVj6;$;iXiR`+QFzazci*{UKa4Yjhe2MX)LvfF3EM8q zLFck&*giW|t;Y3F%GRj%AKcVCUwa$|qv0eAc+q%Sy{cx_~VxW;kF*=IE1` z`3%B~XMx^6n;whHq?DYg#_)eXtb0as4}ZmsK~oU)N|5oYfJ|(Q0Dpo!CMcdvQ2HD7 z&Xs{olKmIFRW6427T6RvGaJT*o2j`+>5wu?r-b)QP8as#5yyYU%lGs*my}ZdBJxjg zwI387qb0+FiOK_}(!^mBi$tjyhA}drY{6y9$;*6sQA!RkjND8xrRgDu+2kSa&aa#( z$``!ncQ_}t1fVN~^3=hF#(axpDvz$uXm(Go6Y#Y#hf^`3m{fYO3?fuxDj|mOgl19|#F#Rnn^Y?>wpAD=)d`Fn7N%1g z16G8=;52L;vjUB^BrZTi>Xc4~i;W^{N|(l^ERhcb)lWf+;-mr8Mx}_Q1x#yLVxiVZ z;QE+YO`7Dm2?n1sfwgND2S{@ecNoQKt;K}9jAF1_B_NF@@|2ZHA;YY-Rp6nR#E03P zcm+?A!W=q0p_K5oV8NvQQbmg3;|Nb`)jAQJWt9fCt{BecNfFW=!0Dt+kRsgzp-iSD zJ$8YFB_m0%S)d5ZlB7>BP!qDElwT{*8|7*;pb?n0a+fx!5!kiz1R2r@Tt<0ODy$QD z357-*F$#iV1*xsD3MyENv^GizlFD*HD&`er{pCh&JR)FWhXaVk7gxyVK8@w6-`@*DLwTp3b8C^jOq+(u?91VJVr!pvYK4D*&%iyCKhfLioI5|UT2Gm!#s1$<1mYp zVT)Af#Kox5;?=nlV!q76Pm>Ob2(#LBUWG)#vodr(jzo{ybh5x-GYV8V7 zL?;Om_K+tkmLyURF&^VeP^H7`Nn|AaFoAhe6;iQ`aHYXaQ3COGh)ZfDoCJ>Aq=eGR z#aU*lA9L9~9HTVKa&dG#gEY%>+jT`oDVOKwcrlYqBqALGFea5+k|z)mG9&46>Lnf- z!SWPkWFc9==q0_1q%4m31YRXehFX0Afkq&sV}5Byr;9Px)-u*Ff%PlbhglCF@7!eOt^sL)y?YC#~Xuptpd5EdwW z#tOTj!l8)qqL?>^Do}qkq)+O~F=I@vPbbP1vKS+SD9eqaxYLV9%Uz;`*vnRyhh>R) zhMOqQXp?4dk*=IaBnReS;_vPY|yrC1rE zU#vFD*zRnQr*``}DqjRqM`avtHWpW-F>Zw~8P*60p3RW)Yt%}<%*XI(>>}_WvPex3 zD+&v_q$VSxTMb2CO_3Y}yVI`)6@-n5B3g3SPg77I;`^GKqtJi%aK~OKgZes!QThmC?!7@o*V#^pxx6Vi^PRJN0Ijyuuht>3vRx z+ZYv5Om9RIc72gtsqm*WhVqCC^D`6%n@YtLu>yu5TOCJum;niB{0QA?6gahnNx(H~ zZ91b^;xM{wdZkIuGsc_-5kuuRav}zPKqoNC_(qgr44W(_Qz~H9m;z~Yg$cZ1h|b~< zxJk2sZ*`k|{X3}Xl1uBB(q{*Q(B~0cb6QMF?Bo>v;DQ7TP79!x11lTc4 zl;H+%tH;7sk+>PRSQQbHE)i?2cDaXdmdmY?w3jDQ39W3WkAvzkn_TDTn2mg!O&(yQ zR=zDF4{{^~-^NyjIA)S=m)pWzNkC|~MIt&?YCNqDNlb(r&*+(oAdwP-pG?hi;^3>38kA1GhvBhU zJWd}Q4Kf`pC&R{!F-e_E!e(Wq{tA~hz-BWe5?6(Zi%a7^7f;TUSu(g=E#_-2sK-r; zi>x88z@1UiJyLpr6uGbpnMgsJ6L{7tOMwScz?Z2kB)38&wdygCS}oSG%ux>sqYOJN zo@_!IVv!=RBq+;<{aLRqD5r-bes8RzTq#SKz4VOI%E~Hy28k*tW72&=yE+@@p*}W8 zi%0N;UuD;6*pjf{BhkAeuK?CwCS!s4{K$)G4>P00QJ zpiN+-%Ol}n-0aXs63GyrA-uLMGi25~kq8?LRdC!=TTyw4UP0RJLTlLQ^294-!EnUx z!z)y1xXA7|RT!iZgEvrNx7s7&L=d)CCn80Hu(84?sxY`B74|Su5iW{i6>&yIQ7~pv zWVKPFKAupp;?W98Qe06~9>WAFr$bl~GYis;sJuKDQ)JcUuw_kPMndIAL)=<{Vh$TS zo)j?c1?U2lr?RH_29c34sME%zIHR;l(lL!xOStG6AybxA`Z*a#NTF6%Ff&<4c|4Y4 zWXmlo6@fCcK8HFU<1w>bpH}S@a1cXQmyFBkNK9(bsWcLXC}hm=O=^ZyYBo6?Rt6($ zVW~WRRPD1lRG~Bq2M`qT6QI~x2yYzGGHni*RK@W!Ga+}h2t!%rOpi`2Rk8vaFIuGX zv5Jy@qR7BwTMa>>3p8TTK`UHjhEtx6=n^3L3sdtf|dS{3We36 zGCM-J+8K)QQei>HZ3dsB3H)>5YXy5(<$$s{_?knGAcOLMgAb1nehxACFZoP@0Rjm8 z>|h^I{vHYVU5!M@l&JD6n<3ti^Sg@D5%3Lx|A&LgWS9#9-%psMCrLcl|6qhc`FX&9 z6cn?=6*eW+&k8v51N`uiU+piv-}hrzM=Fpsl*83V2Icb-gD*{-vWr!S%bX4{6lfxv zR;OYPMb1}hL<1a~3H+p}O#=SD5W%2)N8sZ(8+}?4DBt-@E0SSeQlPRy93ciV6|k37 z9;#3AsM8GikbrLz2}kq&v_*qnn?Fm@ft#da5rDs%u?8FT1!4yY8%M!6{JZCTd052d zS3LA@{?GU~trp6^N%_e&f^?|J=aL3E@KVIYVayU{ruptLW+icyGU5Cb?XyY-<-f-D zj9hF&;9z?2n(>85jH^LZQhkobh`2n@_wuAmBu+Sjd<1;glusLAqya{CJcI#!n4#h% z5X-3r4}6k3hzS|M-<+};?}b72qcTp|hf|UY%#p&g zDX9;0kq{4&8FF?bCgWj#p1?}VBUlI#xJfx33yTC~&Td2mzLdg>MZ$t$N$PPwf~gAdn;M0~A*hijE$A!fAUx|mq1HM()VNUQ+e8P~G_Q^pK#u!_}~ zk%JpjVhw4M;3mJ=ikYIgg%pEUR!+IFD=>2scZkHXl$nD&c$7UV#RduAyGw2AIB9;Q8cAzc*^af z1fu7A%K4&{)gaU<&7w4Eka&~?s0{{1TA2}LjRv(x$rm9qgDI`Di%{^fQ-hFMjXs@T zCzdOX0iE6=mdA`Co!%{0h>YR1J`6!8V_Ii$h&5r8&|~yh2||p#>yt$8`d6wSiw&Ya z-K!$ku($Yb@LC9~1Yo7FJ({6H+xb)DSa&zj=&Lt9;Qx9r4Yi^9?ipkGjSa8@fPBD~ z8Q9#<;MuA+_g5au#%Gssc{DaGdFH^GY)l5bgb#ZkIS9Xmk1!u}E?c)?I7^-PQ+r<~ ztaGRw0K00hGi+l1|Ih$(YZ3Ab5GV>%EkLMR#gD*3@Dc`#$ANtV)b@e@a2Y=X<=)*~ zPh(Y{Z}6XN%&9Ay^m#!&Ml-?1VWLUd?+&X1AI)7+m>$^n_eaJ(JF?Zmzx!HDnyBo@ z&d#k9TD5eI9W{KRY@8R3Y`!_^>F3+-R%)N#yu>s_&F$6gG265`#Y91u*0Yj{&v#<- zhQD|Q{XWxU4LHv8hR`>+nQ-Aqo7*F9|5<1FPe0xM;a1(N`wIgPUYSwzlrUKC5{~L& zg}w`jEQrAhlKepm)Pfh->IInw17zk`qd?|^4iKn;^Xfx=`qO0s0kxgiNrRR4)KZoj zC${Bssz`%T=ktYNnK)hON_q*GPJ%yZtxe@Z7!=NKfI}IXH5R$lojW6C&>wC+X7@(! z#yw9K(mG#hmu*Db*Gt-}zILqaMNPfYBfBJpkBVD1?{0D$KC3GkQ%HzfKK}Acjzf(u zH`8qUTq_ivK03kQ=;A8&AIE0j-flg9`0PiA4wDy$9{RYB=GIH~H^(h?Ut}$?w7hWc z^8rt``09&m_1ZS8+r(1$vlDK2eaUrUy>D-A?DP8T%cR@ZT)j~8qI1LdJwMfSi59--ov`IIbCnMe$CdW&^z_Nyz*LibTk8Py`a%sD+^D4@owPi(lbRH zcFvmF<gXY-m&L|0LRE{9k;}ygKP6$I2W_njf0wJ^k|z z_l-+y^nW=RMboz^PrQ;Y-+g866O)iOFTNX|A*$Qi8hqU|^|w{e{Pvsm;2F@fZ9K0C5EjhDKd-#rhTFwc5CaDF9yW9vFsJK5f7zh={G#yV?*kM%o!Vx{^bt#RLC=Z|Mc2cDny#ZOOkeSG}+^MP31`TmlkpSd^ZzZCxR)6x8EuYYynrm5GGHho8Q zl(k~IM5mW5U7`?v{q++~X6|<0zSiu{$# z9$}eM;w%_B{J@FfZ8=>VJT+{tzojQJsLs$)e9bypgB{^Mbp#zB=@jaltvB>s|7MJW z9h*M!teX06Y%-et`|OS99v?6+bfO$9)StQf#AIi*^IJWiT6lb9;PZM^7m|9!0NyE+9J9aQ&8GGHYfqPDZF{`@_5y*X&y|kxb{{t!`0|Z68|g~5T{;tl4 zFCJdq>zRg<&0qZ}Ot+fTpv`CBpOCL>UDo8I&23IR{d=9BqXun$wPC*<4IkMnwlsYD zk`SL_=~TCQ2mjQ0Cd=;`$nrBf?TJ@VL?1@>8OTW@>nLsm(?b(Bym)R);}cyjr9bI$tmVa*ntmSp-SI+)O$EcB8UF0? z$lEl@Xx6Ub*@m}AHs8$Bj##FS7mjY0`C@>h`+JkV!#aLFOwQ*riXGjU8UyS?ZKT+gkfQuYRY~eA=MSO{ewMOz+mE zrCR=s2tTai%h&hI^vehq>l!Z$eQSAp*2i;g>W#vo@}YFetIU~y%sjbs)vGI}`d>RY zb4X|P^g}aKGY6H-?>eZ%aQWX$Zmp}Q{k-4j`Vk9Pn-+*qtlivaluR`Ht=Al{IA%M> zI$r*Xy;a-!lW8NSee|SydB0y)@9Vs{Lpyx`;>^BvYn5wDkGPH??IqkrH}}8#%hoH; z{k-u?BUfiv|I^)0KXba>=`~;K9@~rzzRuJ=wRpm-6E;kk{0HmI`rRjYf6sZ>`~00x zy1({Uk5G>nUMqaf{aW4ECcj;73?FEDVD8(&x7FVwzAiqy@38OYfsz#ycQ4(YnzwuI zAl98zx5oUs`s1FJJVA7P zd+Lh4Jr>#)_S(iHKCBpbsqAF`F004Rl}(dP+^N)m_w6ym;E#8&-Tj3BjHe501$)Ex zw^sjPwXPH&e3QSW_l_49X5RR#=Z7eUs5b1r+T?2C%`P_?4f>2p zkJ;Gd+IR6}w+$nQ9%z2<=M(F8U7B#RLDwN&-`g)+dVH>RSNC0wcP)JUi8jBt`L?)i zbZ^Cpb{Vri!LhCF8eG zU;66VcDo;c{*{^Unx~rI{b<@X$3@@8rDdgMW6LI%wb+#0v|=dzy8U*&+sPY8|2Xvf z*&By$s_XOWuWa;l!J_)&`lbeDkE4yAd^Ayi%jb1J-|*6N^nHUm4fwWoJ^Yp+IjVOf z_7ekJ-H5d<>B(Bz<$PwHF+D7us$cAY#iCzqkWSvQZ--?3#MX+|De+FpR?z{8QZz&~ zQ>2(_>1kbaN<6_ffwrVi>*tGELk3>H>z5J`) z?@Sr=xwWFlvrj+Q{&=%HV_R%qe!BdfD}&ajPS-#5>V`2lr=8tabavvUf!AHPrq-W1 z^8Jx}g)_exIF^vTBWoo)Sx=39@MGfrL6=9IT0VWs^o8A-%l9t-#yfKD^|kx)&h~Mu ziv}|Lblphb!di$3KK{~s;DWKmTmAM%SUY1~ih&Ob~$cicME{MXU1ua5j}D}sHcb6+^p}tSu)OZD zK7V-6J3kIwb^7beZ(aUqgZca>lf8e%;HKWm&dF=hW$$m4zw476So-UtUq`f?Jp0VX zUx$43+uS4Dzd!Wp@!;_NbtgWuwL#glEuZ;h_>OBD|0*oF)&9eYm0Nb7d+XPaKP86` z8ZP`zdTHuz$tcOwqJkl;=Ct%AI^*$`t~nfZ_mH_;U3#K+k%5X931=MB93=R`HrgUHy3I=}C9yKK;n}$8KCG*)-(N^FQ?YW$3ZndmAio`h4H7v%K4$ z+r;^kZ*?tD_n7x~-&MB{TtDPqzVXWWHxCWoz2+5i^5}H}`kmMQdRM*W(cQ~;7m3%3 zf0e9~?4Iz~grnK_u5A7Ns|hprpB(jR_rE{9_S)VR^M|EwZX_K$zxdt1_uT7!<;Rw7 z;@>!UHF(zf^QrwQ@5Loc#~oX-sqfVhw-?+bu56$B%X#&6^=tDd_D#Le=+@XT@4_8(qUy|KLdgelnAOX$LI*k{!VQ=qlNQOW|3JxYV4TPf|i zYTFttA!pS%VY#YEmHl9dCS25h<-5l-z%dTaxCf3oD~K>?DMvqd@s* zFrdlYV_5L+LTRkZ1?SXwa!2pnhY%T@*ybg$+^N9SDP`$=PZ)5rH(eih1*J3|J9lHE zj@-@-v^H|+mdft3QPAsa47TPf{Rb?+uQCbv9Cqb8c?MX(OjxX6wHQ5JcdKPc#BkuL zJG+LS4@s9bs{dW5ksH7NYTEWLqu+GAD4p?5NZN36zZWkQPCnY{>1orYj|mERdnPD~{PbzC)Lxo*(hNfU;?vasIz+jBSAJ5NCWdilL; zU#~y@#|vkBeHy#Kzjo{(+uxBj=ZI|4+nrC$$fED=711&ud~;&PtT{Itd9uO&E1rDy zwE2}Ue*3ey=nW#e8Gq}Gxkctx&w2Xxc&ord{MEyA-#q#B?EZ(g zp8B?Fzu*efp(fu+4%7P2-g54-8%zI~^4iVK-%Oo#=V!y7OT{ZLh9~Y^m>zbx*kAa_ z-#zHe#CQug)6uOL^hBzE7rRw+n4=_53y8{&C-rqiYK;&hTuVGynA~;fqIy zT^PUPwUu8;f`jm>gSVY*y>`v1D`%T^W_(BQQr7OHS-(YE#BFEX8#}Fe)^@_Z^t+1L zs+6vYYhS}z9WUt5UpuuCi@mg@$-X_^ulD|C!H}OrUryrg?=JkKT)C~^o6pW}H2<97 z+OcM{vxjBVmhD=#h8Q=TSa@`){)7HY3%-5w+YOqd7oJM`NcDczx3`xSvfmG`eKt|_ z>&a!hIlbyE9}_$`Eimu-59jSW+*AG5Da)+cXI3{=#6MZU{AzF4_Tlf3_uaK`^}3SP zYu`CB9Upqe?PVV^|y)M?KUeh&CIS_8JyTmqDT2j^tM&6oxJAdz;mbHA3E?I z>!jybw++D(n7z-=?Xzd#yn#ROJN(%0QuAW+Kd=A zo5Mg~KJ>$Hhh~Krukzc+rH@|t^3ih}zPHW(^>*_E&*^SA|8~lUt5$vf&IgCOeK~bW z>yU<3fiGL~;fVbapw4J;?debMpziW5QJn`^_(VJ#e#2R1pKSljtu}x20-P+({c;(e& z$HyI5e*Nj9R>d1^D`tJ#>CNsZ-c*xn!7u$4ovvx$f8nL~PbcLw3wB`6PbS`Ivh?=& z36oFF?7sff!`DCJHQpd@`Yt|P#`tq@$F-B2ygF)L-TBI|eqoeunf-f&zIb7L(!_<| zS8$|@_6KfsWKKA=eBQ7}?wCrI8-8hc^~<44Cs$U~^&e0X%iFm+_WH8ql`)IQOz1jc zc)Q#E3g3wC^}mwb`)>0)i2S*ZXUM+JRe?6&7pF%y7`1R;>rD9_!+SO|2xN}>%i*GJ2$!xeatMhwzrd9lqKAaxE?MKVHnFsfD znQ(zMck%9@i@FpZw48D5pEa{s;w5j6eD>hy*^xaCZr@eV;P8g?L$7w<&~pbq4UtD4 zU$wbpfu2|VNf*wz4O_QwO>I@|i(9AkI@Pqe`%{nTB~ENMGv@Z=AswqTxI zyngLVo7)B-pEU0|qU9J(6XM|Wi)Q{j!Fr4&ekmMUx8*An2mcay@{O0#ncon9uAJIu zXI874z5a!g!G$C1cM!GhIHA?squMR7dfFU&^2pXF80I;hOPV)b_(|cs7WDBmJ6qdz zpE%7eZ?$%yZ^qTPdKdTUn2-&hwsgn-)9Wug)(sU`J9o|gvE94FjxYbQyzLuP=eeI_W(~jYXx@G2 zmRFw~)A_rVt*uW!D$Wf1pywh_3w$;UE2i&0`+mF8g)hu497zt``{iRdJq6)5qrFp> zHkkJ(H=T9No42m3!ZdC9LSb8wHzmK8CPXp54el50O z%7&LL-9y0%zwX}_xW4D_57HC=2!Fb3S)+H}k94vv$>87DDQP1ZP!Jwtu&$!5T-~|t zp6_Oi=#-wM+%bLW)4f;&7IYCQG#h<_wcYDSyx5yXM_&zH*wxPX=*RnddzVa^e~38F z`ey9jYo~VanE&}NtffQs6NcS-b=Kb#%T&L;@%#%-*G92t>TGMSDVnl$$+xrTE|Y%v z%JD)`%T>koRO?=PVoN|+ws`i7?2aF^4jY>vJhI~5#e07o-?@$Na-#F1#cwQ~&TV){ z!^-q<7yP~R?I$;mJlT8E^S7L-**&AFGyPI|Z1#8K~F3f^KR?B6y{elTKp z_znKjDaH+VN*{GLX}sI2*xG1NtB6nDtl*Q-8RM_hHLZA?znk>tt_j7Zf1yKupH%#g zrAh4BLB5G)SDJR{+jQK3uIs-4V>)_ez_vrgT;JzJ6uR)WMM+Kehaw=c#wi)2#XI#|mGZ{q{>MBC}_W*>9SoX*KJ)Jx7K< zHG6xTFM1CC>#Fh5wPh=R7~JgnQJ2c6GKPOJ>2&W#{5EGc50msPcs_Kbd12d$muDTQ zdtmvWqJ>Xs|NdmFbMD@W1-oCwyI$Db;gEGmt0ONh+x0}O$(*-acpv32NjYSL`fJn6 zKjm+J^<-w@>7zT6?~je_EymMdYMRX}?KE=fR%ZK=5k_?0w&t78ZV;qz&_*gklU8?~ z$DKB3M4(TX&BywWjQpZ$cC~RM`|#fTv4%4{kbNcTGpR9=zi1dFBCXG&HE|oXvFksZ2vlS!H4o@|tw;dVz^PA@2a(`M@f`9n9a%7w1 zg(HU}Ek`~gzhD_(J#@yLz)dxMu& znrXLBgZH+7{Y<*sltWK6x_l(PmIw)_0oaQ?mU^jbn*9|n-+`S*LlxNUSHL(Yoh}*R<@WcI*q0;^-gPw zmNlFmnU~!=&hy@eHdCE@#*RuY;_2VlY~0u6_Ls*`w;1%=ulweFb^X#yo2I@x;KfZ( zF8|`>+RhVNJc>CsBP*2ef7~;ABf|OmHK|u7+j6bf_qN{^oiZ25t+97n?^gajr$vwM zCEe%gG%tLxF7lJ~*f;IF_qgEO{?v&tUuT*ZGcS)_(SGx|cjcqDn9sgJY-v2ae&O}d z?InsA_XXRv-SinDwf??)y8HL9&Xr7F=^MY6b8z&D7Hgv9cixqZNQGz)`B|Gock0l4 zwLJYm<0}_#stO7Gw?=Egy2QEojU)^~2ho}wn5sTv{}a&~c7Cl@E`IYr9IeSUT{YN;(VBlej{zDC`k&Vj5Z;mdjOs z_XlM&N;oJRHmz{rBOvfmP<9D}N^;(?#RPe}~lcumr8aGvrn$ol6P_uXXmDCA% znhkznC@vj!xf>V2w@Bbu^#z|-V#CLMs0Wo7bwVw5(L&XEENW*|RUQ|}TK$Aeg|Vuh z*Tjh0}^-@SPZg!LV+)%b_gl6REZH zm>l%Jc9|SlCRtq$&f=}c5tEP97<;Y=!a|VyddXtIHoa;H@J-Wd9I@Ec7fz{*8Yg_; zmQ{s47KdBoNv;UOW+V6Yn9YHYo>W7~m*a5xHOj%rt9#7h!A!ZgH2;38av8iDtpg8K zL*T-M=7ITuu#az>&cWZsnjEDA4I1;3!gy!E(S+gnQpG zP=rT)x|h1DSqAbbZ%pkxCi^}%LYZ9ZJ8buq<8$tV$AwLp_m+dI_toVBXWUl~zSMC~ z9ur|O@6Tf*_u_zwuvz!QV=~$BVWQf7W-@s-83fKy*u60@y)%US>F4%Y1 zyLIeDR1H;82?9h>t2PxsP}I^Q+7i?&d7pRQcb<9nvSl<9m59vRx4iYImw(e9A_QTSO&n8B{=pbp zd5nmi%%<$ocz(73Blc%!=4LYmq$;C%JC#m4**qe&r$>pVXB<1PL}%iT?cSmcMkcy7 zj8$EYTD=4Py49zn{t?f?MZ4d2#BaujLoJ>rPB@&Z=jVJzb_JN~!FJ6m&-u5wk!KwBT zAXRKy#H`@N^Ugxvo@x)__Ry9s10&J)kcT5crp{4^Ki(Sn{gd(MaOJ9%t4ME8T-f@i=}ec4G8|_Nfz3EzZBBytwstd}jXF+isTE z?7pUR-Cy3{{LHZ%zINf`|N8LTTdZS;9)IrO!JX|7?)*MFcI4UPu}5!H?>+g#!;i!s zIP&a!v3pLwaOU`XvEdV+`TG9;CEe$f}>Rt#h%;sicMWBV*nM@(Pu)QM= zb-)h8dwyBzK%$9r=k|_X{sr1QJc|0Wc^g?Wk&+k_2)9h*IAx}+ZHQv3Dp*yBCJ_=Q zR#;{}~v6B*Pnzj@twl3L8ErE$e;}&LO8pFf>du#d~%)nNWN!nh&79rTusUfANuq|nr zsFH;fNhvZo z{MdJ{eC_xA1pKCc>5~DI@yQo;C!?2-jby3WP;Jzmj=Xz#+w(8HTo~Lo_}ko56!>{oR+ods=xVH}w3-?oZ~1mU2V z&M$st4G1zXufO=Z09bs23?Zx}$#}{|sJj~}Md<=faN!7a3$9x1LnYCo`nq4PKuSrk zKwPkZ&qc)o3D1ziH4CKlW-?i)fF_6mLyRF}x-UXpfs-=-1b%Zp3C{c>!UY)qhR?1q zUa+HC&_|SA_RK-sfs>2Zw|luL3Ah&tQU)@h$)z*XNEuIOdNYNzcfmIRpN2O8k3eyT z$kP{)63yFo%wB+D#d!+OO8kh1IwN6zw!{Y3i9syFx;O+$2Tf4IWD_x_$rMrDl(B}G zrOO=2NH6LXv*+4eYzoGnhTHnVDSmcFodPo$;f@T3>rteE*V%l^&b!cK=%Ng3?&n8uY?Azq|#6m$oXWssevY~>fdMBY1+IKY!g=>w5;$yD-}8%Pyi7kD=C(g0$% zHw!3UaVMXKuJ|-CB1m%6Qxf?JVJ@oU#4@00x%D>mxscbI-;bH4tP41tA|(O(Pm+l%p<#XlGffX)(>zRLevl5{{!xs4o_R+=qX$q&H_? z5CR7>p?2_rc%jh^6U9>HLeYg{2cQ>;sIo4;fN;T;fmrDFvL^3U1(pxgye>y_yRd0L9rCU@F0N& zFNxv_FnKwkS4X3ztOy>8KuLq4yu2VwCx$4}^+QtR!mWj*ex+?aD56ivE#Lm2V1H0} z=0i>6RZ8TCBr3cQ6hwJHeBGKV!Met3%M{jY@}qH`3K+<8!{M!l2H_AJOZ{*ZNxTS$ zcs>d1#(jKg!PemtHU6lQ^P$p{Oyqy;$v?pJP)?lq^IUEiHwHH*Hx?M!c)`F21jP{y zZ$~w5bVLC5!3dh@+_`qoKY#GtH+S84?Zc5zR3XPD@Tj@g4Ncx=A)0+TyDba<*$Od!T|H}%eeV7m@9t@=Bi_0uGtLc+V6q+ z#11gmz0}rLTgpppK4dTzyU$lc|KnVKsp^I zER&Vd>jqv`GHaEW_)2Ddt`Eo5pRvvJMR5uaO3kGW#z&6i~8nCAWh?6 zzZkb30nuj-1BVstk3b*;=Reh9mS$27SS98C0Ir0nmabZ=R%huqi$16NRRc9-;B}zq zx$8kM*fyM*UAE2HWy^d!h-z~V4*Loi&cP|?;4lIh;NVcwSDSBNzYZw$^~b@WgJ5nL z1k-UHH-87SDF$ZqX)rhTg1PBwFkRhC^KCdq>FocAa8uPFTm!7ow%uJF=&5EbTid?1 z0p0P-h){g0$c}hW;H}ZtgnFQG)y`CHme`URCl-xpX(LxEC^jT~wJ-jyX0?2Q-rJ(d%YM&EMiKs1~RO=N&Yy(ma(n^_C7iDUKWd?hcZd!(A8U`~#TY-Q! zqIA_#JMTJ(NkgFP%10vQTF1CfRagPS0Ti0GN}-F7Aau~Hsu0!%g>H!OGDtHOTI!Mz z`xUyTmvOyf)jGs(kJTDBVHHQiCh2nVfeSE`)_`t;ATJ5E|D4}E)Iz2RDy1s`oA)X4 zjMr7)IlOvFH`Jy*W?Wp-7|3CZNZ0D#RdO`+&7YesiL+nsaA~kvq``vpttJnKiVU2W ztIbfWz(D50e{bOixbZpQ!8gwVwcUC-H*bR3#^q({5EywEH;;qC?O;d_41EoZ*57~! LtEyoYdGLP#Go(;Z literal 0 HcmV?d00001