From 3a3ed0453bb450c6c1dc4740239f97026661c4f2 Mon Sep 17 00:00:00 2001 From: Anthony LC Date: Tue, 30 Dec 2025 13:01:08 +0100 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9A=A1=EF=B8=8F(export)=20improve=20svg?= =?UTF-8?q?=20width=20when=20undefined?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We improved the svg width calculation when the width attribute is undefined by trying to use the style attribute before falling back to a default value. --- .../doc-export/blocks-mapping/imagePDF.tsx | 3 -- .../src/features/docs/doc-export/utils.ts | 28 +++++++++++++------ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/imagePDF.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/imagePDF.tsx index 1a3513cb..1471e933 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/imagePDF.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/imagePDF.tsx @@ -21,9 +21,6 @@ export const blockMappingImagePDF: DocsExporterPDF['mappings']['blockMapping'][' if (blob.type.includes('svg')) { const svgText = await blob.text(); - const FALLBACK_SIZE = 536; - previewWidth = previewWidth || FALLBACK_SIZE; - const result = await convertSvgToPng(svgText, previewWidth); pngConverted = result.png; dimensions = { width: result.width, height: result.height }; diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/utils.ts b/src/frontend/apps/impress/src/features/docs/doc-export/utils.ts index 73e09c7d..cf7cc404 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-export/utils.ts +++ b/src/frontend/apps/impress/src/features/docs/doc-export/utils.ts @@ -36,7 +36,7 @@ export function downloadFile(blob: Blob, filename: string) { */ export async function convertSvgToPng( svgText: string, - width: number, + width?: number, ): Promise<{ png: string; width: number; height: number }> { // Create a canvas and render the SVG onto it const canvas = document.createElement('canvas'); @@ -54,26 +54,36 @@ export async function convertSvgToPng( const svgElement = svgDoc.documentElement; // Get viewBox or fallback to width/height attributes - let height; + let calculatedHeight: number | undefined; const svgWidth = svgElement.getAttribute?.('width'); const svgHeight = svgElement.getAttribute?.('height'); const viewBox = svgElement.getAttribute('viewBox')?.split(' ').map(Number); const originalWidth = svgWidth ? parseInt(svgWidth) : viewBox?.[2]; const originalHeight = svgHeight ? parseInt(svgHeight) : viewBox?.[3]; - if (originalWidth && originalHeight) { - const aspectRatio = originalHeight / originalWidth; - height = Math.round(width * aspectRatio); - } const svg = Canvg.fromString(ctx, svgText); - svg.resize(width, height, true); + + const FALLBACK_WIDTH = 536; + + // Resize if width provided, preserving aspect ratio + if (originalWidth && originalHeight && width) { + const aspectRatio = originalHeight / originalWidth; + calculatedHeight = Math.round(width * aspectRatio); + svg.resize(width, calculatedHeight, true); + } else if (!width && !originalWidth) { + svg.resize(FALLBACK_WIDTH, undefined, true); + } + await svg.render(); + const returnWidth = width || originalWidth || FALLBACK_WIDTH; + const returnHeight = calculatedHeight || returnWidth; + return { png: canvas.toDataURL('image/png'), - width, - height: height || width, + width: returnWidth, + height: returnHeight, }; } From ab92fc43d69a07c1848baf769f3e3da8e9d65393 Mon Sep 17 00:00:00 2001 From: Anthony LC Date: Tue, 30 Dec 2025 13:02:53 +0100 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=85(export)=20add=20PDF=20regression?= =?UTF-8?q?=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To avoid regression issues in PDF export functionality, this commit introduces end-to-end tests that compare exported PDFs against known good reference files. We compare the PDF on most of the blocks that the editor supports. If during a Blocknote release or pull request there are intentional changes, the reference files would need to be updated accordingly. It can be done by uncommenting the line in the test that saves the newly generated PDF to the assets folder. --- CHANGELOG.md | 1 + .../assets/base-content-test-pdf.txt | 1 + .../assets/doc-export-regressions.pdf | Bin 0 -> 56932 bytes .../__tests__/app-impress/doc-editor.spec.ts | 2 +- .../__tests__/app-impress/doc-export.spec.ts | 150 +++++++++++++++++- .../__tests__/app-impress/doc-version.spec.ts | 4 +- .../__tests__/app-impress/language.spec.ts | 10 +- .../e2e/__tests__/app-impress/utils-editor.ts | 4 +- 8 files changed, 164 insertions(+), 8 deletions(-) create mode 100644 src/frontend/apps/e2e/__tests__/app-impress/assets/base-content-test-pdf.txt create mode 100644 src/frontend/apps/e2e/__tests__/app-impress/assets/doc-export-regressions.pdf diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e90cb15..518941fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to ### Added - ✨(backend) add documents/all endpoint with descendants #1553 +- ✅(export) add PDF regression tests #1762 ## [4.3.0] - 2026-01-05 diff --git a/src/frontend/apps/e2e/__tests__/app-impress/assets/base-content-test-pdf.txt b/src/frontend/apps/e2e/__tests__/app-impress/assets/base-content-test-pdf.txt new file mode 100644 index 00000000..d030dbd9 --- /dev/null +++ b/src/frontend/apps/e2e/__tests__/app-impress/assets/base-content-test-pdf.txt @@ -0,0 +1 @@ +"AtACsvXrmg0ABwEOZG9jdW1lbnQtc3RvcmUDCmJsb2NrR3JvdXAHALL165oNAAMOYmxvY2tDb250YWluZXIHALL165oNAQMJcGFyYWdyYXBoBwCy9euaDQIGBACy9euaDQMKSGVsbG8gVGV4dCgAsvXrmg0CD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oNAgl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDQINdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oNAQJpZAF3DmluaXRpYWxCbG9ja0lkgbL165oNAQEABYey9euaDRIDDmJsb2NrQ29udGFpbmVyBwCy9euaDRgDB2hlYWRpbmcHALL165oNGQYGALL165oNGgRib2xkAnt9hLL165oNGw9IZWxsbyBIZWFkaW5nIDGGsvXrmg0qBGJvbGQEbnVsbCgAsvXrmg0ZD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oNGQl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDRkNdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oNGQVsZXZlbAF9ASgAsvXrmg0ZDGlzVG9nZ2xlYWJsZQF5KACy9euaDRgCaWQBdyQ1MjZhZWIwYy1mMTk1LTRjNzUtODVmZS0yNWZlN2FhYTYyYzSBsvXrmg0YAQAFh7L165oNMgMOYmxvY2tDb250YWluZXIHALL165oNOAMHaGVhZGluZwcAsvXrmg05BgYAsvXrmg06BGJvbGQCe32EsvXrmg07D0hlbGxvIEhlYWRpbmcgMoay9euaDUoEYm9sZARudWxsKACy9euaDTkPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgAsvXrmg05CXRleHRDb2xvcgF3B2RlZmF1bHQoALL165oNOQ10ZXh0QWxpZ25tZW50AXcEbGVmdCgAsvXrmg05BWxldmVsAX0CKACy9euaDTkMaXNUb2dnbGVhYmxlAXkoALL165oNOAJpZAF3JDY2Yzg1NTU5LTZlN2ItNGQxOS04MDU1LWEzYzE3YzcyNWE1ZoGy9euaDTgBAAWHsvXrmg1SAw5ibG9ja0NvbnRhaW5lcgcAsvXrmg1YAwdoZWFkaW5nBwCy9euaDVkGBgCy9euaDVoEYm9sZAJ7fYSy9euaDVsPSGVsbG8gSGVhZGluZyAzhrL165oNagRib2xkBG51bGwoALL165oNWQ9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDVkJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg1ZDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDVkFbGV2ZWwBfQMoALL165oNWQxpc1RvZ2dsZWFibGUBeSgAsvXrmg1YAmlkAXckOWM1ZTFlYWYtZTVhZi00MzA3LWI0Y2YtMmUwMWJjNDUwZjhih7L165oNWAMOYmxvY2tDb250YWluZXIHALL165oNcgMNY2hlY2tMaXN0SXRlbQcAsvXrmg1zBgQAsvXrmg10EENoZWNrbGlzdCBJdGVtIDEoALL165oNcw9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDXMJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg1zDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDXMHY2hlY2tlZAF5KACy9euaDXICaWQBdyQxYjQ2Y2E3Ny0wYWJhLTQyNDAtYmFiZC0xNDY5NzA3OGJkZDiHsvXrmg1yAw5ibG9ja0NvbnRhaW5lcgcAsvXrmg2KAQMNY2hlY2tMaXN0SXRlbQcAsvXrmg2LAQYEALL165oNjAEQQ2hlY2tsaXN0IEl0ZW0gMigAsvXrmg2LAQ9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDYsBCXRleHRDb2xvcgF3B2RlZmF1bHQoALL165oNiwENdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oNiwEHY2hlY2tlZAF5KACy9euaDYoBAmlkAXckYjkyYjY2MGUtNjFhNi00NzE5LTgyMDctMTc3ZTQwMzBhZGZih7L165oNigEDDmJsb2NrQ29udGFpbmVyAQCy9euaDaIBAQAEKACy9euaDaIBAmlkAXckOTE0YWY3YTUtYmY4ZS00ZjkzLTg5NTItYjlkODM4NmI3ODM4h7L165oNogEDDmJsb2NrQ29udGFpbmVyBwCy9euaDakBAwVxdW90ZQcAsvXrmg2qAQYEALL165oNqwELSGVsbG8gUXVvdGUoALL165oNqgEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgAsvXrmg2qAQl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDakBAmlkAXckOWE2Y2FlMjUtMjAwMi00OTViLTlmZWEtMmRjNzc4NDQ1OGNhh7L165oNqQEDDmJsb2NrQ29udGFpbmVyBwCy9euaDboBAwlwYXJhZ3JhcGgoALL165oNuwEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgAsvXrmg27AQl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDbsBDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDboBAmlkAXckNDU3MGI4NWEtYjliMy00MWViLWEyMTEtMDU3MGQ4OTk5OTk4h7L165oNugEDDmJsb2NrQ29udGFpbmVyBwCy9euaDcABAw50b2dnbGVMaXN0SXRlbQcAsvXrmg3BAQYEALL165oNwgETSGVsbG8gVG9nZ2xlIExpc3QgMSgAsvXrmg3BAQ9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDcEBCXRleHRDb2xvcgF3B2RlZmF1bHQoALL165oNwQENdGV4dEFsaWdubWVudAF3BGxlZnSHsvXrmg3BAQMKYmxvY2tHcm91cAcAsvXrmg3ZAQMOYmxvY2tDb250YWluZXIHALL165oN2gEDCXBhcmFncmFwaAcAsvXrmg3bAQYEALL165oN3AEbSGVsbG8gVG9nZ2xlIExpc3QgMSBDb250ZW50KACy9euaDdsBD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oN2wEJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg3bAQ10ZXh0QWxpZ25tZW50AXcEbGVmdCgAsvXrmg3aAQJpZAF3JDMwOTk5YzZjLWNlMTctNGEzOS1iNzcwLWMxZTMzZmJlMzlhMoey9euaDdoBAw5ibG9ja0NvbnRhaW5lcgcAsvXrmg38AQMJcGFyYWdyYXBoKACy9euaDf0BD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oN/QEJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg39AQ10ZXh0QWxpZ25tZW50AXcEbGVmdCgAsvXrmg38AQJpZAF3JGJjMDU4ODY5LTRhYTYtNDc1MC04Nzc1LTkyOWRmNjZiNGMwMygAsvXrmg3AAQJpZAF3JDUxZjNlZGUzLTllYjQtNDNiNS05ODA1LTZhZWY2ZDNhNTRmY4ey9euaDcABAw5ibG9ja0NvbnRhaW5lcgcAsvXrmg2DAgMQbnVtYmVyZWRMaXN0SXRlbQcAsvXrmg2EAgYEALL165oNhQIUTnVtYmVyZWQgTGlzdCBJdGVtIDEoALL165oNhAIPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgAsvXrmg2EAgl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDYQCDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDYQCBXN0YXJ0AX8oALL165oNgwICaWQBdyRiZDQ1MzRjMy05YTNmLTQ4OWQtOGRhYS01MjNhY2U5OWU0MWGHsvXrmg2DAgMOYmxvY2tDb250YWluZXIHALL165oNnwIDEG51bWJlcmVkTGlzdEl0ZW0HALL165oNoAIGBACy9euaDaECFE51bWJlcmVkIExpc3QgSXRlbSAyKACy9euaDaACD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oNoAIJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg2gAg10ZXh0QWxpZ25tZW50AXcEbGVmdCgAsvXrmg2gAgVzdGFydAF/KACy9euaDZ8CAmlkAXckM2EzYTQ2ZTktMTc0OS00ZTU0LWFjNjQtMmRkZjM3MDFhZWUyh7L165oNnwIDDmJsb2NrQ29udGFpbmVyAQCy9euaDbsCAQAEKACy9euaDbsCAmlkAXckMGRiNmJkYTUtYzkzZi00OWUwLThmYmYtNjQ2ZTQzYzc0ZGE1h7L165oNuwIDDmJsb2NrQ29udGFpbmVyBwCy9euaDcICAw5idWxsZXRMaXN0SXRlbQcAsvXrmg3DAgYEALL165oNxAISQnVsbGV0IExpc3QgSXRlbSAxKACy9euaDcMCD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oNwwIJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg3DAg10ZXh0QWxpZ25tZW50AXcEbGVmdCgAsvXrmg3CAgJpZAF3JDZlYmZjNDQ1LTI4ZDEtNGI2OS05ZjViLTlmMTI4NmJmZTJmOIey9euaDcICAw5ibG9ja0NvbnRhaW5lcgcAsvXrmg3bAgMOYnVsbGV0TGlzdEl0ZW0HALL165oN3AIGBACy9euaDd0CEkJ1bGxldCBMaXN0IEl0ZW0gMigAsvXrmg3cAg9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDdwCCXRleHRDb2xvcgF3B2RlZmF1bHQoALL165oN3AINdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oN2wICaWQBdyQwYzNlZDljYS1hNjVjLTQ3YzUtYTljYy1hOGI1MzUxOTJlYjGHsvXrmg3bAgMOYmxvY2tDb250YWluZXIBALL165oN9AIBAAMoALL165oN9AICaWQBdyRjMmE0Yjg2OC01OGExLTRlZWMtOTgwYy04ZmYzYzI0Y2RiODeHsvXrmg30AgMOYmxvY2tDb250YWluZXIHALL165oN+gIDCWNvZGVCbG9jawcAsvXrmg37AgYGALL165oN/AIJdGV4dENvbG9yJHsic3RyaW5nVmFsdWUiOiJyZ2IoMjQ5LCAxMTcsIDEzMSkifYSy9euaDf0CBWNvbnN0gbL165oNggMChLL165oNhAMBIIGy9euaDYUDAoSy9euaDYcDAWGBsvXrmg2IAwKEsvXrmg2KAwEggbL165oNiwMChLL165oNjQMBPYGy9euaDY4DAoSy9euaDZADASCBsvXrmg2RAwKEsvXrmg2TAwIxMIGy9euaDZUDAoSy9euaDZcDATuGsvXrmg2YAwl0ZXh0Q29sb3IEbnVsbIGy9euaDZkDAigAsvXrmg37AghsYW5ndWFnZQF3CmphdmFzY3JpcHQoALL165oN+gICaWQBdyQ3M2I0M2FkNS04ZDA5LTRiZjQtYWRlZS1lOGM2MDUwNThiNjOHsvXrmg36AgMOYmxvY2tDb250YWluZXIHALL165oNngMDB2RpdmlkZXIoALL165oNngMCaWQBdyQ1YTMzOTRiMy0wMGI3LTRmOTMtOWNkYS1jMTQ1ZjI3NWJhMTWHsvXrmg2eAwMOYmxvY2tDb250YWluZXIHALL165oNoQMDCXBhcmFncmFwaCgAsvXrmg2iAw9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDaIDCXRleHRDb2xvcgF3B2RlZmF1bHQoALL165oNogMNdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oNoQMCaWQBdyRkNDEwNjM5YS02MjA4LTQ4OTYtOTRkYy1jY2VkYzkxMjMxNzeHsvXrmg2hAwMOYmxvY2tDb250YWluZXIHALL165oNpwMDCXBhZ2VCcmVhaygAsvXrmg2nAwJpZAF3JDE0NWZlMGZhLTQ0NmItNDNkYS1hNGY2LTE4ZWFjZmVkYWM1MIey9euaDacDAwpjb2x1bW5MaXN0BwCy9euaDaoDAwZjb2x1bW4HALL165oNqwMDDmJsb2NrQ29udGFpbmVyBwCy9euaDawDAwlwYXJhZ3JhcGgHALL165oNrQMGBACy9euaDa4DDUNvbHVtbiAxIFRleHQoALL165oNrQMPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgAsvXrmg2tAwl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDa0DDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDawDAmlkAXckNTcwM2IxOTYtMDQ1Yy00NDFhLTk0YTctODhjZTY5YzgwMTcyKACy9euaDasDAmlkAXckODNlYTNjNGItYzA3My00ZTExLThmMTEtYjNjMDllYzFlY2EwKACy9euaDasDBXdpZHRoAX0Bh7L165oNqwMDBmNvbHVtbgcAsvXrmg3CAwMOYmxvY2tDb250YWluZXIHALL165oNwwMDCXBhcmFncmFwaAcAsvXrmg3EAwYEALL165oNxQMNQ29sdW1uIDIgVGV4dCgAsvXrmg3EAw9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDcQDCXRleHRDb2xvcgF3B2RlZmF1bHQoALL165oNxAMNdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oNwwMCaWQBdyQyNGJjNWM5NC05OTA0LTQxZjktYWUwNi1mZWNiMWRkZTQ2YjkoALL165oNwgMCaWQBdyQ2NTY1M2M1ZC03NzBhLTQxNGMtYmY0Yi0zMTMyMzNkZDAwNjYoALL165oNwgMFd2lkdGgBfQGHsvXrmg3CAwMGY29sdW1uBwCy9euaDdkDAw5ibG9ja0NvbnRhaW5lcgcAsvXrmg3aAwMJcGFyYWdyYXBoBwCy9euaDdsDBgQAsvXrmg3cAw1Db2x1bW4gMyBUZXh0KACy9euaDdsDD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oN2wMJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg3bAw10ZXh0QWxpZ25tZW50AXcEbGVmdCgAsvXrmg3aAwJpZAF3JDQ2YzgyMzJjLTNkOTQtNDIyZi1iNTU3LWYwMWM0N2U1MWIwYoey9euaDdoDAw5ibG9ja0NvbnRhaW5lcgcAsvXrmg3uAwMJcGFyYWdyYXBoKACy9euaDe8DD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oN7wMJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg3vAw10ZXh0QWxpZ25tZW50AXcEbGVmdCgAsvXrmg3uAwJpZAF3JGU5NmUzNGJkLWJmN2EtNGUyZS04MWUxLTc2ODBmNjI2Mzg3MCgAsvXrmg3ZAwJpZAF3JDU0ZjFiYjIxLTQzMzAtNDgwZS1hNmYwLThhNzM3ODcwMmE5NSgAsvXrmg3ZAwV3aWR0aAF9ASgAsvXrmg2qAwJpZAF3JGRiODIxZmQ2LTVhZGUtNDVjZi04Y2E0LTAwNjZiODFmN2Q5MYey9euaDaoDAw5ibG9ja0NvbnRhaW5lcgcAsvXrmg33AwMHY2FsbG91dCgAsvXrmg34Aw10ZXh0QWxpZ25tZW50AXcEbGVmdCgAsvXrmg34Aw9iYWNrZ3JvdW5kQ29sb3IBdwZ5ZWxsb3coALL165oN+AMFZW1vamkBdwTwn5KhKACy9euaDfcDAmlkAXckNTFkZTBmZDUtN2VmZS00MDZmLTkzMmMtNzI1YjMxMDNmZTVkh7L165oN9wMDDmJsb2NrQ29udGFpbmVyBwCy9euaDf0DAwlwYXJhZ3JhcGgoALL165oN/gMPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgAsvXrmg3+Awl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDf4DDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDf0DAmlkAXckMGEzNGU4ZDAtNjcyNi00NTZhLWE1OWEtMmViMDhmYzI3ZTA0h7L165oN/QMDDmJsb2NrQ29udGFpbmVyBwCy9euaDYMEAwV0YWJsZQcAsvXrmg2EBAMIdGFibGVSb3cHALL165oNhQQDCXRhYmxlQ2VsbAcAsvXrmg2GBAMOdGFibGVQYXJhZ3JhcGgHALL165oNhwQGBACy9euaDYgEBkNlbGwgMSgAsvXrmg2GBAl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDYYED2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oNhgQNdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oNhgQHY29sc3BhbgF9ASgAsvXrmg2GBAdyb3dzcGFuAX0Bh7L165oNhgQDCXRhYmxlQ2VsbAcAsvXrmg2UBAMOdGFibGVQYXJhZ3JhcGgHALL165oNlQQGBACy9euaDZYEBkNlbGwgMigAsvXrmg2UBAl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDZQED2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oNlAQNdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oNlAQHY29sc3BhbgF9ASgAsvXrmg2UBAdyb3dzcGFuAX0Bh7L165oNlAQDCXRhYmxlQ2VsbAcAsvXrmg2iBAMOdGFibGVQYXJhZ3JhcGgHALL165oNowQGBACy9euaDaQEBkNlbGwgMygAsvXrmg2iBAl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDaIED2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oNogQNdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oNogQHY29sc3BhbgF9ASgAsvXrmg2iBAdyb3dzcGFuAX0Bh7L165oNhQQDCHRhYmxlUm93BwCy9euaDbAEAwl0YWJsZUNlbGwHALL165oNsQQDDnRhYmxlUGFyYWdyYXBoBwCy9euaDbIEBgQAsvXrmg2zBAZDZWxsIDQoALL165oNsQQJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg2xBA9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDbEEDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDbEEB2NvbHNwYW4BfQEoALL165oNsQQHcm93c3BhbgF9AYey9euaDbEEAwl0YWJsZUNlbGwHALL165oNvwQDDnRhYmxlUGFyYWdyYXBoBwCy9euaDcAEBgQAsvXrmg3BBAZDZWxsIDUoALL165oNvwQJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg2/BA9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDb8EDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDb8EB2NvbHNwYW4BfQEoALL165oNvwQHcm93c3BhbgF9AYey9euaDb8EAwl0YWJsZUNlbGwHALL165oNzQQDDnRhYmxlUGFyYWdyYXBoBwCy9euaDc4EBgQAsvXrmg3PBAZDZWxsIDaBsvXrmg3PBAIoALL165oNzQQJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg3NBA9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDc0EDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDc0EB2NvbHNwYW4BfQEoALL165oNzQQHcm93c3BhbgF9ASgAsvXrmg2EBAl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDYMEAmlkAXckNTJhNmNkNDMtMDYxMy00YWQ4LWI0OTctZTY2MThjMGUwZTMwgbL165oNgwQBAAmHsvXrmg3fBAMOYmxvY2tDb250YWluZXIHALL165oN6QQDCXBhcmFncmFwaCgAsvXrmg3qBA9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDeoECXRleHRDb2xvcgF3B2RlZmF1bHQoALL165oN6gQNdGV4dEFsaWdubWVudAF3BGxlZnQhALL165oN6QQCaWQBR7L165oNowEDCXBhcmFncmFwaCgAsvXrmg3vBA9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDe8ECXRleHRDb2xvcgF3B2RlZmF1bHQoALL165oN7wQNdGV4dEFsaWdubWVudAF3BGxlZnRHsvXrmg28AgMJcGFyYWdyYXBoKACy9euaDfMED2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oN8wQJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg3zBA10ZXh0QWxpZ25tZW50AXcEbGVmdEey9euaDfUCAwlwYXJhZ3JhcGgoALL165oN9wQPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgAsvXrmg33BAl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDfcEDXRleHRBbGlnbm1lbnQBdwRsZWZ0xrL165oNggOy9euaDYMDCXRleHRDb2xvciR7InN0cmluZ1ZhbHVlIjoicmdiKDIyNSwgMjI4LCAyMzIpIn3GsvXrmg2FA7L165oNhgMJdGV4dENvbG9yJHsic3RyaW5nVmFsdWUiOiJyZ2IoMTIxLCAxODQsIDI1NSkifcay9euaDYgDsvXrmg2JAwl0ZXh0Q29sb3IkeyJzdHJpbmdWYWx1ZSI6InJnYigyMjUsIDIyOCwgMjMyKSJ9xrL165oNiwOy9euaDYwDCXRleHRDb2xvciR7InN0cmluZ1ZhbHVlIjoicmdiKDI0OSwgMTE3LCAxMzEpIn3GsvXrmg2OA7L165oNjwMJdGV4dENvbG9yJHsic3RyaW5nVmFsdWUiOiJyZ2IoMjI1LCAyMjgsIDIzMikifcay9euaDZEDsvXrmg2SAwl0ZXh0Q29sb3IkeyJzdHJpbmdWYWx1ZSI6InJnYigxMjEsIDE4NCwgMjU1KSJ9xrL165oNlQOy9euaDZYDCXRleHRDb2xvciR7InN0cmluZ1ZhbHVlIjoicmdiKDIyNSwgMjI4LCAyMzIpIn3HsvXrmg36ArL165oNngMDDmJsb2NrQ29udGFpbmVyBwCy9euaDYIFAwlwYXJhZ3JhcGgoALL165oNgwUPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgAsvXrmg2DBQl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDYMFDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDYIFAmlkAXckY2RkNjFjMWYtNzhkMC00OWM1LTgwNjMtMGY1M2I0ODA4OGEzobL165oN7gQBqLL165oNiAUBdyQ2OGQwMDRmOS1kNzZhLTQ2YzctOWZmMy03NzA5NTFjN2EzYTOBsvXrmg3XBAHfAvnE4N4MAKiy9euaDYkFAXckOWQ2YzU2M2ItMmMwNi00OTg0LTgwMTgtZmMzYTk1MmJlNjAzx7L165oNgwSy9euaDd8EAw5ibG9ja0NvbnRhaW5lcgcA+cTg3gwBAwlwYXJhZ3JhcGgHAPnE4N4MAgYEAPnE4N4MAwEvKAD5xODeDAIPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gwCCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MAg10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gwBAmlkAXckNjhkMDA0ZjktZDc2YS00NmM3LTlmZjMtNzcwOTUxYzdhM2Ezx/nE4N4MAbL165oN3wQDDmJsb2NrQ29udGFpbmVyBwD5xODeDAkDCXBhcmFncmFwaCgA+cTg3gwKD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MCgl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDAoNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MCQJpZAF3JDg3Yjk5ODk5LTIyYzgtNDZlMi04OWFmLTM1M2FlZDk4MmVlOcf5xODeDAmy9euaDd8EAw5ibG9ja0NvbnRhaW5lcgcA+cTg3gwPAwlwYXJhZ3JhcGgoAPnE4N4MEA9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KAD5xODeDBAJdGV4dENvbG9yAXcHZGVmYXVsdCgA+cTg3gwQDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDA8CaWQBdyQ4ZTk3YWQxNS0wODhmLTQ0M2YtYTJlYy0xYWU0ZmEwNzIzNmPH+cTg3gwPsvXrmg3fBAMOYmxvY2tDb250YWluZXIHAPnE4N4MFQMJcGFyYWdyYXBoKAD5xODeDBYPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gwWCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MFg10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gwVAmlkAXckZjk1ZmVkZDQtODkwMy00ZjVjLWJhM2QtNjI1Y2Y5Mjg4M2Iyx/nE4N4MFbL165oN3wQDDmJsb2NrQ29udGFpbmVyBwD5xODeDBsDCXBhcmFncmFwaCgA+cTg3gwcD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MHAl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDBwNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MGwJpZAF3JGM4MzM3YmYyLTM2YTUtNDkyOS04NTI1LTQwNWRiZWQzNzk5N8f5xODeDBuy9euaDd8EAw5ibG9ja0NvbnRhaW5lcgcA+cTg3gwhAwlwYXJhZ3JhcGgoAPnE4N4MIg9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KAD5xODeDCIJdGV4dENvbG9yAXcHZGVmYXVsdCgA+cTg3gwiDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDCECaWQBdyRlMzRjMjI2NS0xZWNjLTQ1YjAtOWU2Zi1iYmQ1YWVhMDRiNTbH+cTg3gwhsvXrmg3fBAMOYmxvY2tDb250YWluZXIHAPnE4N4MJwMJcGFyYWdyYXBoKAD5xODeDCgPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gwoCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MKA10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gwnAmlkAXckNjViZTM5OWUtMjFmOC00YWM0LWJmYjktYjFjNmI5NjgyM2Fjx/nE4N4MJ7L165oN3wQDDmJsb2NrQ29udGFpbmVyBwD5xODeDC0DCXBhcmFncmFwaCgA+cTg3gwuD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MLgl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDC4NdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MLQJpZAF3JDdhMjg0NTdiLTZhMGItNGQ0Ny04MzgzLTYzYzAxMDFlMzM4NIT5xODeDAQBL0f5xODeDAMDGGludGVybGlua2luZ1NlYXJjaElubGluZSgA+cTg3gw0B3RyaWdnZXIBdwEvKAD5xODeDDQIZGlzYWJsZWQBeaj5xODeDDYBeMf5xODeDDT5xODeDAMDFmludGVybGlua2luZ0xpbmtJbmxpbmUoAPnE4N4MOAN1cmwBdyovZG9jcy8wMjJkMDQ4Yy02ZDA3LTRlNTgtYjNmYy05YzQzYTI4NzEyZTkoAPnE4N4MOAVkb2NJZAF3JDAyMmQwNDhjLTZkMDctNGU1OC1iM2ZjLTljNDNhMjg3MTJlOSgA+cTg3gw4BXRpdGxlAXcPVGVzdCBSZWdyZXNzaW9ux/nE4N4MAfnE4N4MCQMOYmxvY2tDb250YWluZXIHAPnE4N4MPAMJcGFyYWdyYXBoKAD5xODeDD0PYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gw9CXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MPQ10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gw8AmlkAXckNWY1ZGRlMTgtYzg1ZC00ZmExLWIzNTctYTBlNThlNTk4Nzk0x/nE4N4MPPnE4N4MCQMOYmxvY2tDb250YWluZXIHAPnE4N4MQgMJcGFyYWdyYXBoKAD5xODeDEMPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gxDCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MQw10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gxCAmlkAXckODk0MTk2ZjQtOGIxNi00NWYzLTgzNzQtYjE2ZmM4NjM5NDE1BwD5xODeDEMGBAD5xODeDEgBL8f5xODeDBv5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDEoDCXBhcmFncmFwaCgA+cTg3gxLD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MSwl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDEsNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MSgJpZAF3JGU4YTdjNWVjLTI4NTYtNDMzNi1iYjE2LTlkYWE4OGRhMzc5Ncf5xODeDEr5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDFADCXBhcmFncmFwaCgA+cTg3gxRD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MUQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDFENdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MUAJpZAF3JDZiYjRlNmZiLTIyYTUtNGEyMC04YjczLWQxZjhkOWY0MjBiZMf5xODeDFD5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDFYDCXBhcmFncmFwaCgA+cTg3gxXD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MVwl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDFcNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MVgJpZAF3JDI3NmZmODE2LTY3ZWUtNDNlMi05Njg3LTdjYmFhZDMzMmRhZMf5xODeDFb5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDFwDCXBhcmFncmFwaCgA+cTg3gxdD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MXQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDF0NdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MXAJpZAF3JDE4ZmUwOWI5LWRmMmYtNGZmYS1iMWU4LTJmNDRhM2EzYmIyZMf5xODeDFz5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDGIDCXBhcmFncmFwaCgA+cTg3gxjD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MYwl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDGMNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MYgJpZAF3JGY3YTZjMWI0LTc2OGMtNDc4Yi05MTczLTliZjkxMmUxMTJhYsf5xODeDGL5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDGgDCXBhcmFncmFwaCgA+cTg3gxpD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MaQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDGkNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MaAJpZAF3JGNiNDU3MWZlLWFjNjYtNGI3Mi05MzZlLTE0MzExMmJmNGQ0Ncf5xODeDGj5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDG4DCXBhcmFncmFwaCgA+cTg3gxvD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4Mbwl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDG8NdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MbgJpZAF3JDhlNDU3MDA1LTIxODMtNDczNy1hMDA3LTk1NTk3MGQ2OTlkOMf5xODeDG75xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDHQDCXBhcmFncmFwaCgA+cTg3gx1D2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MdQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDHUNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MdAJpZAF3JGU3NjdlM2UyLTg1ODQtNDRjNC05NDU1LTU0ZThmODQ5MDY0Ncf5xODeDHT5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDHoDCXBhcmFncmFwaCgA+cTg3gx7D2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4Mewl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDHsNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MegJpZAF3JGQ1Y2E3ZGZkLWE3ZGEtNDM0NC04MDhlLWQ3ZjNhZDA3YzFkZsf5xODeDHr5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDIABAwlwYXJhZ3JhcGgoAPnE4N4MgQEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gyBAQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDIEBDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDIABAmlkAXckNTBmMzZiZDktYzQyYS00OTQxLWE0ZDUtYjg0ZDMzN2VhMGFhx/nE4N4MgAH5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDIYBAwlwYXJhZ3JhcGgoAPnE4N4MhwEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gyHAQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDIcBDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDIYBAmlkAXckNzk5N2QzMTMtY2NhZS00MTg3LWEzNjgtYWYyOTA1MTJhZGI4x/nE4N4MPPnE4N4MQgMOYmxvY2tDb250YWluZXIHAPnE4N4MjAEDCXBhcmFncmFwaCgA+cTg3gyNAQ9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KAD5xODeDI0BCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MjQENdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MjAECaWQBdyQwOGEwYzUxNC0wODFkLTRmNDAtYTU0Mi1hNTFkZjFmYzgyNzAHAPnE4N4MjQEGBAD5xODeDJIBAi8vR/nE4N4MjQEDB2hlYWRpbmcoAPnE4N4MlQEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gyVAQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDJUBDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDJUBBWxldmVsAX0BKAD5xODeDJUBDGlzVG9nZ2xlYWJsZQF4BwD5xODeDJUBBgQA+cTg3gybARJIZWxsbyBUaXRsZSBUb2dnbGXH+cTg3gyVAfnE4N4MjQEDCmJsb2NrR3JvdXAHAPnE4N4MrgEDDmJsb2NrQ29udGFpbmVyBwD5xODeDK8BAwlwYXJhZ3JhcGgoAPnE4N4MsAEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gywAQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDLABDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDK8BAmlkAXckNDViNTBlZWMtZDQ2MC00MWMzLTkxNGQtZmI2Y2Y3NWIyOTU1BwD5xODeDLABBgQA+cTg3gy1ARNIZWxsbyB0aXRsZSBjb250ZW50BwD5xODeDAoGBAD5xODeDMkBAS9H+cTg3gwKAwdoZWFkaW5nKAD5xODeDMsBD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MywEJdGV4dENvbG9yAXcHZGVmYXVsdCgA+cTg3gzLAQ10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gzLAQVsZXZlbAF9BCgA+cTg3gzLAQxpc1RvZ2dsZWFibGUBeYf5xODeDK8BAw5ibG9ja0NvbnRhaW5lcgcA+cTg3gzRAQMJcGFyYWdyYXBoKAD5xODeDNIBD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4M0gEJdGV4dENvbG9yAXcHZGVmYXVsdCgA+cTg3gzSAQ10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gzRAQJpZAF3JGY1YzQyNGY3LTI5OTMtNDk3ZC04ZDkyLTM3NDAyZDQwZTQwZMf5xODeDIwB+cTg3gxCAw5ibG9ja0NvbnRhaW5lcgcA+cTg3gzXAQMJcGFyYWdyYXBoKAD5xODeDNgBD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4M2AEJdGV4dENvbG9yAXcHZGVmYXVsdCgA+cTg3gzYAQ10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gzXAQJpZAF3JGY1YzQyNGY3LTI5OTMtNDk3ZC04ZDkyLTM3NDAyZDQwZTQwZAcA+cTg3gzLAQYEAPnE4N4M3QENSGVsbG8gdGl0bGUgNMf5xODeDAn5xODeDA8DDmJsb2NrQ29udGFpbmVyBwD5xODeDOsBAwlwYXJhZ3JhcGgoAPnE4N4M7AEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gzsAQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDOwBDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDOsBAmlkAXckODQ3YjQxZTktNTA3Yy00YjI2LTgyOGItMmUyN2I0MDhlZjhhx/nE4N4M6wH5xODeDA8DDmJsb2NrQ29udGFpbmVyBwD5xODeDPEBAwlwYXJhZ3JhcGgoAPnE4N4M8gEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gzyAQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDPIBDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDPEBAmlkAXckZWNjOTYwNmQtZmFjNS00Y2FkLWI1YjUtYzFlNWViMGJiYWVmBwD5xODeDPIBBgQA+cTg3gz3AQEvR/nE4N4M8gEDB2hlYWRpbmcoAPnE4N4M+QEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gz5AQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDPkBDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDPkBBWxldmVsAX0FKAD5xODeDPkBDGlzVG9nZ2xlYWJsZQF5BwD5xODeDPkBBgQA+cTg3gz/AQ1IZWxsbyB0aXRsZSA1x/nE4N4M8QH5xODeDA8DDmJsb2NrQ29udGFpbmVyBwD5xODeDI0CAwlwYXJhZ3JhcGgoAPnE4N4MjgIPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gyOAgl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDI4CDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDI0CAmlkAXckMmJlZWEwMGQtYWExNS00ZGE1LWFkOWItN2YyZTk1Y2NmMTY0x/nE4N4MjQL5xODeDA8DDmJsb2NrQ29udGFpbmVyBwD5xODeDJMCAwlwYXJhZ3JhcGgoAPnE4N4MlAIPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gyUAgl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDJQCDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDJMCAmlkAXckYjcwMjk5NTUtNGIzNS00MjI5LWI0MjctNTU5M2M1MjU1ZmQxBwD5xODeDJQCBgQA+cTg3gyZAgEvR/nE4N4MlAIDB2hlYWRpbmcoAPnE4N4MmwIPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gybAgl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDJsCDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDJsCBWxldmVsAX0GKAD5xODeDJsCDGlzVG9nZ2xlYWJsZQF5BwD5xODeDJsCBgQA+cTg3gyhAgtIZWxsbyB0aXRsZYT5xODeDKwCATbE+cTg3gysAvnE4N4MrQIBIMf5xODeDA/5xODeDBUDDmJsb2NrQ29udGFpbmVyBwD5xODeDK8CAwlwYXJhZ3JhcGgoAPnE4N4MsAIPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gywAgl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDLACDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDK8CAmlkAXckMWQ0NWMzZjktNGQ0YS00ZWVjLWI2ODEtOWRmM2NkYWY2MDc4BwD5xODeDLACBgQA+cTg3gy1AgIvOoT5xODeDLcCGfCfp5nigI3imYLvuI8gTWFnaWMgZW1vamnHsvXrmg1YsvXrmg1yAw5ibG9ja0NvbnRhaW5lcgcA+cTg3gzJAgMJcGFyYWdyYXBoKAD5xODeDMoCD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MygIJdGV4dENvbG9yAXcHZGVmYXVsdCgA+cTg3gzKAg10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gzJAgJpZAF3JGUzZDcyYTNlLTFhNTUtNGJkZS04MzI3LWQ2MGQ1ZDFjZTRmNqj5xODeDAgBdyRjODgzZDRmZS00MzI2LTQzNDgtYTljNy03MGIyNzlmYjZhMjTHsvXrmg2DBPnE4N4MAQMOYmxvY2tDb250YWluZXIHAPnE4N4M0AIDCXBhcmFncmFwaCgA+cTg3gzRAg9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KAD5xODeDNECCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4M0QINdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4M0AICaWQBdyQ2OGQwMDRmOS1kNzZhLTQ2YzctOWZmMy03NzA5NTFjN2EzYTMHALL165oN+AMGBAD5xODeDNYCAk15hPnE4N4M2AICwqCE+cTg3gzZAgcgQ3VzdG9thPnE4N4M4AICwqCE+cTg3gzhAgYgYmxvY2vH+cTg3gyvAvnE4N4MFQMOYmxvY2tDb250YWluZXIHAPnE4N4M6AIDCXBhcmFncmFwaCgA+cTg3gzpAg9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KAD5xODeDOkCCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4M6QINdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4M6AICaWQBdyRiYWQ4YmE3Yi00ZGNjLTRiMDEtOGNjNi0xZGVkMjFmYWViNTTH+cTg3gzoAvnE4N4MFQMOYmxvY2tDb250YWluZXIHAPnE4N4M7gIDCXBhcmFncmFwaCgA+cTg3gzvAg9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KAD5xODeDO8CCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4M7wINdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4M7gICaWQBdyRjMjIxNTY4OS1mNTdhLTQ4ZDUtOTM4Zi02OGY5MjE1MWU4MTQHAPnE4N4M7wIGBAD5xODeDPQCAS/GsvXrmg0bsvXrmg0cCXRleHRDb2xvchd7InN0cmluZ1ZhbHVlIjoiZ3JlZW4ifcay9euaDSqy9euaDSsJdGV4dENvbG9yBG51bGzGsvXrmg0b+cTg3gz2Agl0ZXh0Q29sb3IWeyJzdHJpbmdWYWx1ZSI6ImJsdWUifcay9euaDSr5xODeDPcCCXRleHRDb2xvchd7InN0cmluZ1ZhbHVlIjoiZ3JlZW4ifcay9euaDVuy9euaDVwPYmFja2dyb3VuZENvbG9yF3sic3RyaW5nVmFsdWUiOiJncmVlbiJ9xrL165oNarL165oNaw9iYWNrZ3JvdW5kQ29sb3IEbnVsbKiy9euaDaABAXiosvXrmg2CAgF3JDE0MmU5YmE2LWRlZGYtNDhlMy1iNTVhLTY2YWViNzE4NWMzMsey9euaDboBsvXrmg3AAQMOYmxvY2tDb250YWluZXIHAPnE4N4M/gIDDnRvZ2dsZUxpc3RJdGVtBwD5xODeDP8CBgQA+cTg3gyAAxNIZWxsbyBUb2dnbGUgTGlzdCAxKAD5xODeDP8CD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4M/wIJdGV4dENvbG9yAXcHZGVmYXVsdCgA+cTg3gz/Ag10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gz+AgJpZAF3JDUxZjNlZGUzLTllYjQtNDNiNS05ODA1LTZhZWY2ZDNhNTRmY0ey9euaDcEBAwlwYXJhZ3JhcGgoAPnE4N4MmAMPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gyYAwl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDJgDDXRleHRBbGlnbm1lbnQBdwRsZWZ0qPnE4N4M/QIBdyRiYzA1ODg2OS00YWE2LTQ3NTAtODc3NS05MjlkZjY2YjRjMDPH+cTg3gz+ArL165oNwAEDDmJsb2NrQ29udGFpbmVyBwD5xODeDJ0DAwlwYXJhZ3JhcGgHAPnE4N4MngMGBAD5xODeDJ8DG0hlbGxvIFRvZ2dsZSBMaXN0IDEgQ29udGVudCgA+cTg3gyeAw9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KAD5xODeDJ4DCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MngMNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MnQMCaWQBdyQzMDk5OWM2Yy1jZTE3LTRhMzktYjc3MC1jMWUzM2ZiZTM5YTKH+cTg3gz/AgMKYmxvY2tHcm91cAcA+cTg3gy/AwMOYmxvY2tDb250YWluZXIHAPnE4N4MwAMDCXBhcmFncmFwaAcA+cTg3gzBAwYEAPnE4N4MwgMbSGVsbG8gVG9nZ2xlIExpc3QgMSBDb250ZW50KAD5xODeDMEDD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MwQMJdGV4dENvbG9yAXcHZGVmYXVsdCgA+cTg3gzBAw10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gzAAwJpZAF3JDMwOTk5YzZjLWNlMTctNGEzOS1iNzcwLWMxZTMzZmJlMzlhMsf5xODeDOgC+cTg3gzuAgMOYmxvY2tDb250YWluZXIHAPnE4N4M4gMDCXBhcmFncmFwaCgA+cTg3gzjAw9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KAD5xODeDOMDCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4M4wMNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4M4gMCaWQBdyQ2MGQzYzE2MC0yOGRmLTRhNDgtYmU5NS03OTEyZjk0OGIxNDWo+cTg3gznAwF3JGI2Nzc0N2ZmLTllMDYtNGIwMi1iMTI4LTAwMzc4YTgzOTBmYUf5xODeDOMDAwdoZWFkaW5nBwD5xODeDOkDBgQA+cTg3gzqAwxjb3B5L3Bhc3RpbmcoAPnE4N4M6QMPYmFja2dyb3VuZENvbG9yAXcPcmdiKDEzLCAxNywgMjMpKAD5xODeDOkDCXRleHRDb2xvcgF3EnJnYigyNDAsIDI0NiwgMjUyKSgA+cTg3gzpAw10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gzpAwVsZXZlbAF9ASgA+cTg3gzpAwxpc1RvZ2dsZWFibGUBeUf5xODeDOkDAwlwYXJhZ3JhcGgHAPnE4N4M/AMGBAD5xODeDP0DDGNvcHkvcGFzdGluZygA+cTg3gz8Aw9iYWNrZ3JvdW5kQ29sb3IBdw9yZ2IoMTMsIDE3LCAyMykoAPnE4N4M/AMJdGV4dENvbG9yAXcScmdiKDI0MCwgMjQ2LCAyNTIpKAD5xODeDPwDDXRleHRBbGlnbm1lbnQBdwRsZWZ0x/nE4N4M/AP5xODeDOkDAwdoZWFkaW5nBwD5xODeDI0EBgQA+cTg3gyOBAxjb3B5L3Bhc3RpbmcoAPnE4N4MjQQPYmFja2dyb3VuZENvbG9yAXcPcmdiKDEzLCAxNywgMjMpKAD5xODeDI0ECXRleHRDb2xvcgF3EnJnYigyNDAsIDI0NiwgMjUyKSgA+cTg3gyNBA10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gyNBAVsZXZlbAF9ASgA+cTg3gyNBAxpc1RvZ2dsZWFibGUBeYT5xODeDJoEASCE+cTg3gygBAJHdYT5xODeDKIECm91dCBvZiBkb2PH+cTg3gziA/nE4N4M7gIDDmJsb2NrQ29udGFpbmVyBwD5xODeDK0EAwlwYXJhZ3JhcGgoAPnE4N4MrgQPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gyuBAl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDK4EDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDK0EAmlkAXckYWM3ZTI0NGEtMmM5ZS00YzkxLThkNDMtNWYwMThlNmVkNmU2ArL165oNExIGMgZSBqABAaMBBcEBQrwCBfUCBIMDAoYDAokDAowDAo8DApIDApYDApoDAtYEAt8EEIgFA/nE4N4MGAABAwIIAQoEGxk2ATxQjQEEkgEDyQEC0QEG8gEE9wEClAIEmQICtgIC2QIB4QIB6AIP/QIBnQMi4wMF6QMkoQQC" \ No newline at end of file diff --git a/src/frontend/apps/e2e/__tests__/app-impress/assets/doc-export-regressions.pdf b/src/frontend/apps/e2e/__tests__/app-impress/assets/doc-export-regressions.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6839736b65c60e08789105bffc8cf8ea40392585 GIT binary patch literal 56932 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 { await verifyDocName(page, randomDoc); - const editor = await openSuggestionMenu({ page }); + const { editor } = await openSuggestionMenu({ page }); await page.getByText('Embedded file').click(); await page.getByText('Upload file').click(); diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts index 943ec3de..e1f343fb 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts @@ -1,6 +1,7 @@ +import fs from 'fs'; import path from 'path'; -import { expect, test } from '@playwright/test'; +import { Download, expect, test } from '@playwright/test'; import cs from 'convert-stream'; import JSZip from 'jszip'; import { PDFParse } from 'pdf-parse'; @@ -633,4 +634,151 @@ test.describe('Doc Export', () => { const download = await downloadPromise; expect(download.suggestedFilename()).toBe(`${docChild}.odt`); }); + + test('it exports the doc to PDF and checks regressions', async ({ + page, + browserName, + }) => { + // Override content prop with assets/base-content-test-pdf.txt + await page.route(/\**\/documents\/\**/, async (route) => { + const request = route.request(); + if ( + request.method().includes('GET') && + !request.url().includes('page=') && + !request.url().includes('versions') && + !request.url().includes('accesses') && + !request.url().includes('invitations') + ) { + const response = await route.fetch(); + const json = await response.json(); + json.content = fs.readFileSync( + path.join(__dirname, 'assets/base-content-test-pdf.txt'), + 'utf-8', + ); + void route.fulfill({ + response, + body: JSON.stringify(json), + }); + } else { + await route.continue(); + } + }); + + const [randomDoc] = await createDoc( + page, + 'doc-export-regressions', + browserName, + 1, + ); + + await verifyDocName(page, randomDoc); + + // Add Image SVG + await page.keyboard.press('Enter'); + const { suggestionMenu } = await openSuggestionMenu({ page }); + await suggestionMenu.getByText('Resizable image with caption').click(); + const fileChooserPromise = page.waitForEvent('filechooser'); + await page.getByText('Upload image').click(); + const fileChooser = await fileChooserPromise; + await fileChooser.setFiles(path.join(__dirname, 'assets/test.svg')); + const image = page + .locator('.--docs--editor-container img.bn-visual-media[src$=".svg"]') + .first(); + await expect(image).toBeVisible(); + await page.keyboard.press('Enter'); + + // Add Image PNG + await openSuggestionMenu({ page }); + await suggestionMenu.getByText('Resizable image with caption').click(); + const fileChooserPNGPromise = page.waitForEvent('filechooser'); + await page.getByText('Upload image').click(); + const fileChooserPNG = await fileChooserPNGPromise; + await fileChooserPNG.setFiles( + path.join(__dirname, 'assets/logo-suite-numerique.png'), + ); + const imagePng = page + .locator('.--docs--editor-container img.bn-visual-media[src$=".png"]') + .first(); + await expect(imagePng).toBeVisible(); + + await page + .getByRole('button', { + name: 'Export the document', + }) + .click(); + + await expect( + page.getByTestId('doc-open-modal-download-button'), + ).toBeVisible(); + + const downloadPromise = page.waitForEvent('download', (download) => { + return download.suggestedFilename().includes(`${randomDoc}.pdf`); + }); + + await page.getByTestId('doc-export-download-button').click(); + + const download = await downloadPromise; + expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`); + + // If we need to update the PDF regression fixture, uncomment the line below + //await savePDFToAssetFolder(download); + + // Assert the generated PDF matches "assets/doc-export-regressions.pdf" + await comparePDFWithAssetFolder(download); + }); }); + +export const savePDFToAssetFolder = async (download: Download) => { + const pdfBuffer = await cs.toBuffer(await download.createReadStream()); + const pdfPath = path.join(__dirname, 'assets', `doc-export-regressions.pdf`); + fs.writeFileSync(pdfPath, pdfBuffer); +}; + +export const comparePDFWithAssetFolder = async (download: Download) => { + const pdfBuffer = await cs.toBuffer(await download.createReadStream()); + + // Load reference PDF for comparison + const referencePdfPath = path.join( + __dirname, + 'assets', + 'doc-export-regressions.pdf', + ); + + const referencePdfBuffer = fs.readFileSync(referencePdfPath); + + // Parse both PDFs + const generatedPdf = new PDFParse({ data: pdfBuffer }); + const referencePdf = new PDFParse({ data: referencePdfBuffer }); + + const [generatedInfo, referenceInfo] = await Promise.all([ + generatedPdf.getInfo(), + referencePdf.getInfo(), + ]); + + const [generatedScreenshot, referenceScreenshot] = await Promise.all([ + generatedPdf.getScreenshot(), + referencePdf.getScreenshot(), + ]); + generatedScreenshot.pages[0].data; + + const [generatedText, referenceText] = await Promise.all([ + generatedPdf.getText(), + referencePdf.getText(), + ]); + + // Compare page count + expect(generatedInfo.total).toBe(referenceInfo.total); + + // Compare text content + expect(generatedText.text).toBe(referenceText.text); + + // Compare screenshots page by page + for (let i = 0; i < generatedScreenshot.pages.length; i++) { + const genPage = generatedScreenshot.pages[i]; + const refPage = referenceScreenshot.pages[i]; + + expect(genPage.width).toBe(refPage.width); + expect(genPage.height).toBe(refPage.height); + expect(genPage.data).toStrictEqual(refPage.data); + } +}; diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts index 97777711..38604672 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts @@ -42,8 +42,8 @@ test.describe('Doc Version', () => { // Write more await writeInEditor({ page, text: 'It will create a version' }); - await openSuggestionMenu({ page }); - await page.getByText('Add a callout block').click(); + const { suggestionMenu } = await openSuggestionMenu({ page }); + await suggestionMenu.getByText('Add a callout block').click(); const calloutBlock = page .locator('div[data-content-type="callout"]') diff --git a/src/frontend/apps/e2e/__tests__/app-impress/language.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/language.spec.ts index 127caeb7..454763d7 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/language.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/language.spec.ts @@ -109,8 +109,10 @@ test.describe('Language', () => { }) => { await createDoc(page, 'doc-toolbar', browserName, 1); - const editor = await openSuggestionMenu({ page }); - await expect(page.getByText('Headings', { exact: true })).toBeVisible(); + const { editor, suggestionMenu } = await openSuggestionMenu({ page }); + await expect( + suggestionMenu.getByText('Headings', { exact: true }), + ).toBeVisible(); await editor.click(); // close the menu @@ -121,6 +123,8 @@ test.describe('Language', () => { // Trigger slash menu to show french menu await openSuggestionMenu({ page }); - await expect(page.getByText('Titres', { exact: true })).toBeVisible(); + await expect( + suggestionMenu.getByText('Titres', { exact: true }), + ).toBeVisible(); }); }); diff --git a/src/frontend/apps/e2e/__tests__/app-impress/utils-editor.ts b/src/frontend/apps/e2e/__tests__/app-impress/utils-editor.ts index cbd11891..f5c2d7c9 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/utils-editor.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/utils-editor.ts @@ -11,7 +11,9 @@ export const openSuggestionMenu = async ({ page }: { page: Page }) => { await editor.click(); await writeInEditor({ page, text: '/' }); - return editor; + const suggestionMenu = page.locator('.bn-suggestion-menu'); + + return { editor, suggestionMenu }; }; export const writeInEditor = async ({ From e1d2d9e5c8c49d135b26ab1104a5290557324141 Mon Sep 17 00:00:00 2001 From: Anthony LC Date: Tue, 30 Dec 2025 15:38:36 +0100 Subject: [PATCH 3/4] =?UTF-8?q?=E2=9C=85(export)=20simplify=20export=20tes?= =?UTF-8?q?ting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The regression test is asserting most of the blocks of the editor, we can remove some redundant tests. We improved as well the odt and docx tests by overriding as well the document content like for pdf, it will assert more blocks and have less code to maintain. --- .../__tests__/app-impress/doc-export.spec.ts | 483 ++++-------------- 1 file changed, 94 insertions(+), 389 deletions(-) diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts index e1f343fb..dc101095 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts @@ -1,19 +1,19 @@ import fs from 'fs'; import path from 'path'; -import { Download, expect, test } from '@playwright/test'; +import { Download, Page, expect, test } from '@playwright/test'; import cs from 'convert-stream'; import JSZip from 'jszip'; import { PDFParse } from 'pdf-parse'; import { + BrowserName, TestLanguage, createDoc, verifyDocName, waitForLanguageSwitch, } from './utils-common'; import { openSuggestionMenu, writeInEditor } from './utils-editor'; -import { createRootSubPage } from './utils-sub-pages'; test.beforeEach(async ({ page }) => { await page.goto('/'); @@ -47,81 +47,14 @@ test.describe('Doc Export', () => { await expect(page.getByTestId('doc-export-download-button')).toBeVisible(); }); - test('it exports the doc with pdf line break', async ({ - page, - browserName, - }) => { - const [randomDoc] = await createDoc( - page, - 'doc-editor-line-break', - browserName, - 1, - ); - - await verifyDocName(page, randomDoc); - - const editor = await writeInEditor({ page, text: 'Hello' }); - await page.keyboard.press('Enter'); - await openSuggestionMenu({ page }); - await page.getByText('Page Break').click(); - - await expect( - editor.locator('div[data-content-type="pageBreak"]'), - ).toBeVisible(); - - await writeInEditor({ page, text: 'World' }); - - await page - .getByRole('button', { - name: 'Export the document', - }) - .click(); - - const downloadPromise = page.waitForEvent('download', (download) => { - return download.suggestedFilename().includes(`${randomDoc}.pdf`); - }); - - void page.getByTestId('doc-export-download-button').click(); - - const download = await downloadPromise; - expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`); - - const pdfBuffer = await cs.toBuffer(await download.createReadStream()); - const pdfParse = new PDFParse({ data: pdfBuffer }); - const pdfInfo = await pdfParse.getInfo(); - const pdfText = await pdfParse.getText(); - - expect(pdfInfo.total).toBe(2); - expect(pdfText.pages).toStrictEqual([ - { text: 'Hello', num: 1 }, - { text: 'World', num: 2 }, - ]); - expect(pdfInfo?.info.Title).toBe(randomDoc); - }); - + /** + * We override the document content to ensure that the exported DOCX + * contains various elements for testing. + * We don't check the content of the DOCX here, just that the export works + * and the file is correctly named. + */ test('it exports the doc to docx', async ({ page, browserName }) => { - const [randomDoc] = await createDoc(page, 'doc-editor', browserName, 1); - - await verifyDocName(page, randomDoc); - - await page.locator('.ProseMirror.bn-editor').click(); - await page.locator('.ProseMirror.bn-editor').fill('Hello World'); - - await page.keyboard.press('Enter'); - await page.locator('.bn-block-outer').last().fill('/'); - await page.getByText('Resizable image with caption').click(); - - const fileChooserPromise = page.waitForEvent('filechooser'); - await page.getByText('Upload image').click(); - - const fileChooser = await fileChooserPromise; - await fileChooser.setFiles(path.join(__dirname, 'assets/test.svg')); - - const image = page - .locator('.--docs--editor-container img.bn-visual-media') - .first(); - - await expect(image).toBeVisible(); + const randomDoc = await overrideDocContent({ page, browserName }); await page .getByRole('button', { @@ -144,29 +77,14 @@ test.describe('Doc Export', () => { expect(download.suggestedFilename()).toBe(`${randomDoc}.docx`); }); + /** + * We override the document content to ensure that the exported ODT + * contains various elements for testing. + * We don't check the content of the ODT here, just that the export works + * and the file is correctly named. + */ test('it exports the doc to odt', async ({ page, browserName }) => { - const [randomDoc] = await createDoc(page, 'doc-editor-odt', browserName, 1); - - await verifyDocName(page, randomDoc); - - await page.locator('.ProseMirror.bn-editor').click(); - await page.locator('.ProseMirror.bn-editor').fill('Hello World ODT'); - - await page.keyboard.press('Enter'); - await page.locator('.bn-block-outer').last().fill('/'); - await page.getByText('Resizable image with caption').click(); - - const fileChooserPromise = page.waitForEvent('filechooser'); - await page.getByText('Upload image').click(); - - const fileChooser = await fileChooserPromise; - await fileChooser.setFiles(path.join(__dirname, 'assets/test.svg')); - - const image = page - .locator('.--docs--editor-container img.bn-visual-media') - .first(); - - await expect(image).toBeVisible(); + const randomDoc = await overrideDocContent({ page, browserName }); await page .getByRole('button', { @@ -359,108 +277,6 @@ test.describe('Doc Export', () => { expect(pdfText.text).toContain('Hello World'); }); - test('it exports the doc with quotes', async ({ page, browserName }) => { - const [randomDoc] = await createDoc(page, 'export-quotes', browserName, 1); - - const editor = page.locator('.ProseMirror.bn-editor'); - // Trigger slash menu to show menu - await editor.click(); - await editor.fill('/'); - await page.getByText('Quote or excerpt').click(); - - await expect( - editor.locator('.bn-block-content[data-content-type="quote"]'), - ).toBeVisible(); - - await editor - .locator('.bn-block-content[data-content-type="quote"]') - .fill('Hello World'); - - await expect(editor.getByText('Hello World')).toHaveCSS( - 'font-style', - 'italic', - ); - - await page - .getByRole('button', { - name: 'Export the document', - }) - .click(); - - await expect(page.getByTestId('doc-export-download-button')).toBeVisible(); - - const downloadPromise = page.waitForEvent('download', (download) => { - return download.suggestedFilename().includes(`${randomDoc}.pdf`); - }); - - void page.getByTestId('doc-export-download-button').click(); - - const download = await downloadPromise; - expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`); - - const pdfBuffer = await cs.toBuffer(await download.createReadStream()); - const pdfParse = new PDFParse({ data: pdfBuffer }); - const pdfText = await pdfParse.getText(); - expect(pdfText.text).toContain('Hello World'); - }); - - test('it exports the doc with multi columns', async ({ - page, - browserName, - }) => { - const [randomDoc] = await createDoc( - page, - 'doc-multi-columns', - browserName, - 1, - ); - - await page.locator('.bn-block-outer').last().fill('/'); - - await page.getByText('Three Columns', { exact: true }).click(); - - await page.locator('.bn-block-column').first().fill('Column 1'); - await page.locator('.bn-block-column').nth(1).fill('Column 2'); - await page.locator('.bn-block-column').last().fill('Column 3'); - - expect(await page.locator('.bn-block-column').count()).toBe(3); - await expect( - page.locator('.bn-block-column[data-node-type="column"]').first(), - ).toHaveText('Column 1'); - await expect( - page.locator('.bn-block-column[data-node-type="column"]').nth(1), - ).toHaveText('Column 2'); - await expect( - page.locator('.bn-block-column[data-node-type="column"]').last(), - ).toHaveText('Column 3'); - - await page - .getByRole('button', { - name: 'Export the document', - }) - .click(); - - await expect( - page.getByTestId('doc-open-modal-download-button'), - ).toBeVisible(); - - const downloadPromise = page.waitForEvent('download', (download) => { - return download.suggestedFilename().includes(`${randomDoc}.pdf`); - }); - - void page.getByTestId('doc-export-download-button').click(); - - const download = await downloadPromise; - expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`); - - const pdfBuffer = await cs.toBuffer(await download.createReadStream()); - const pdfParse = new PDFParse({ data: pdfBuffer }); - const pdfText = await pdfParse.getText(); - expect(pdfText.text).toContain('Column 1'); - expect(pdfText.text).toContain('Column 2'); - expect(pdfText.text).toContain('Column 3'); - }); - test('it injects the correct language attribute into PDF export', async ({ page, browserName, @@ -507,199 +323,11 @@ test.describe('Doc Export', () => { expect(pdfString).toContain('/Lang (fr)'); }); - test('it exports the doc with interlinking', async ({ - page, - browserName, - }) => { - const [randomDoc] = await createDoc( - page, - 'export-interlinking', - browserName, - 1, - ); - - await verifyDocName(page, randomDoc); - - const { name: docChild } = await createRootSubPage( - page, - browserName, - 'export-interlink-child', - ); - - await verifyDocName(page, docChild); - - const editor = await openSuggestionMenu({ page }); - await page.getByText('Link a doc').first().click(); - - const input = page.locator( - "span[data-inline-content-type='interlinkingSearchInline'] input", - ); - const searchContainer = page.locator('.quick-search-container'); - - await input.fill('export-interlink'); - - await expect(searchContainer).toBeVisible(); - await expect(searchContainer.getByText(randomDoc)).toBeVisible(); - - // We are in docChild, we want to create a link to randomDoc (parent) - await searchContainer.getByText(randomDoc).click(); - - // Search the interlinking link in the editor (not in the document tree) - const interlink = editor - .locator('.--docs--interlinking-link-inline-content') - .first(); - - await expect(interlink).toContainText(randomDoc); - - const downloadPromise = page.waitForEvent('download', (download) => { - return download.suggestedFilename().includes(`${docChild}.pdf`); - }); - - await page - .getByRole('button', { - name: 'Export the document', - }) - .click(); - - void page.getByTestId('doc-export-download-button').click(); - - const download = await downloadPromise; - expect(download.suggestedFilename()).toBe(`${docChild}.pdf`); - - const pdfBuffer = await cs.toBuffer(await download.createReadStream()); - const pdfParse = new PDFParse({ data: pdfBuffer }); - const pdfText = await pdfParse.getText(); - expect(pdfText.text).toContain(randomDoc); - }); - - test('it exports the doc with interlinking to odt', async ({ - page, - browserName, - }) => { - const [randomDoc] = await createDoc( - page, - 'export-interlinking-odt', - browserName, - 1, - ); - - await verifyDocName(page, randomDoc); - - const { name: docChild } = await createRootSubPage( - page, - browserName, - 'export-interlink-child-odt', - ); - - await verifyDocName(page, docChild); - - const editor = await openSuggestionMenu({ page }); - await page.getByText('Link a doc').first().click(); - - const input = page.locator( - "span[data-inline-content-type='interlinkingSearchInline'] input", - ); - const searchContainer = page.locator('.quick-search-container'); - - await input.fill('export-interlink'); - - await expect(searchContainer).toBeVisible(); - await expect(searchContainer.getByText(randomDoc)).toBeVisible(); - - // We are in docChild, we want to create a link to randomDoc (parent) - await searchContainer.getByText(randomDoc).click(); - - // Search the interlinking link in the editor (not in the document tree) - const interlink = editor - .locator('.--docs--interlinking-link-inline-content') - .first(); - - await expect(interlink).toContainText(randomDoc); - - await page - .getByRole('button', { - name: 'Export the document', - }) - .click(); - - await page.getByRole('combobox', { name: 'Format' }).click(); - await page.getByRole('option', { name: 'Odt' }).click(); - - const downloadPromise = page.waitForEvent('download', (download) => { - return download.suggestedFilename().includes(`${docChild}.odt`); - }); - - void page.getByTestId('doc-export-download-button').click(); - - const download = await downloadPromise; - expect(download.suggestedFilename()).toBe(`${docChild}.odt`); - }); - test('it exports the doc to PDF and checks regressions', async ({ page, browserName, }) => { - // Override content prop with assets/base-content-test-pdf.txt - await page.route(/\**\/documents\/\**/, async (route) => { - const request = route.request(); - if ( - request.method().includes('GET') && - !request.url().includes('page=') && - !request.url().includes('versions') && - !request.url().includes('accesses') && - !request.url().includes('invitations') - ) { - const response = await route.fetch(); - const json = await response.json(); - json.content = fs.readFileSync( - path.join(__dirname, 'assets/base-content-test-pdf.txt'), - 'utf-8', - ); - void route.fulfill({ - response, - body: JSON.stringify(json), - }); - } else { - await route.continue(); - } - }); - - const [randomDoc] = await createDoc( - page, - 'doc-export-regressions', - browserName, - 1, - ); - - await verifyDocName(page, randomDoc); - - // Add Image SVG - await page.keyboard.press('Enter'); - const { suggestionMenu } = await openSuggestionMenu({ page }); - await suggestionMenu.getByText('Resizable image with caption').click(); - const fileChooserPromise = page.waitForEvent('filechooser'); - await page.getByText('Upload image').click(); - const fileChooser = await fileChooserPromise; - await fileChooser.setFiles(path.join(__dirname, 'assets/test.svg')); - const image = page - .locator('.--docs--editor-container img.bn-visual-media[src$=".svg"]') - .first(); - await expect(image).toBeVisible(); - await page.keyboard.press('Enter'); - - // Add Image PNG - await openSuggestionMenu({ page }); - await suggestionMenu.getByText('Resizable image with caption').click(); - const fileChooserPNGPromise = page.waitForEvent('filechooser'); - await page.getByText('Upload image').click(); - const fileChooserPNG = await fileChooserPNGPromise; - await fileChooserPNG.setFiles( - path.join(__dirname, 'assets/logo-suite-numerique.png'), - ); - const imagePng = page - .locator('.--docs--editor-container img.bn-visual-media[src$=".png"]') - .first(); - await expect(imagePng).toBeVisible(); + const randomDoc = await overrideDocContent({ page, browserName }); await page .getByRole('button', { @@ -782,3 +410,80 @@ export const comparePDFWithAssetFolder = async (download: Download) => { expect(genPage.data).toStrictEqual(refPage.data); } }; + +/** + * Override the document content API response to use a test content + * This test content contains many blocks to facilitate testing + * @param page + */ +export const overrideDocContent = async ({ + page, + browserName, +}: { + page: Page; + browserName: BrowserName; +}) => { + // Override content prop with assets/base-content-test-pdf.txt + await page.route(/\**\/documents\/\**/, async (route) => { + const request = route.request(); + if ( + request.method().includes('GET') && + !request.url().includes('page=') && + !request.url().includes('versions') && + !request.url().includes('accesses') && + !request.url().includes('invitations') + ) { + const response = await route.fetch(); + const json = await response.json(); + json.content = fs.readFileSync( + path.join(__dirname, 'assets/base-content-test-pdf.txt'), + 'utf-8', + ); + void route.fulfill({ + response, + body: JSON.stringify(json), + }); + } else { + await route.continue(); + } + }); + + const [randomDoc] = await createDoc( + page, + 'doc-export-override-content', + browserName, + 1, + ); + + await verifyDocName(page, randomDoc); + + // Add Image SVG + await page.keyboard.press('Enter'); + const { suggestionMenu } = await openSuggestionMenu({ page }); + await suggestionMenu.getByText('Resizable image with caption').click(); + const fileChooserPromise = page.waitForEvent('filechooser'); + await page.getByText('Upload image').click(); + const fileChooser = await fileChooserPromise; + await fileChooser.setFiles(path.join(__dirname, 'assets/test.svg')); + const image = page + .locator('.--docs--editor-container img.bn-visual-media[src$=".svg"]') + .first(); + await expect(image).toBeVisible(); + await page.keyboard.press('Enter'); + + // Add Image PNG + await openSuggestionMenu({ page }); + await suggestionMenu.getByText('Resizable image with caption').click(); + const fileChooserPNGPromise = page.waitForEvent('filechooser'); + await page.getByText('Upload image').click(); + const fileChooserPNG = await fileChooserPNGPromise; + await fileChooserPNG.setFiles( + path.join(__dirname, 'assets/logo-suite-numerique.png'), + ); + const imagePng = page + .locator('.--docs--editor-container img.bn-visual-media[src$=".png"]') + .first(); + await expect(imagePng).toBeVisible(); + + return randomDoc; +}; From f9c9e444c924eb03456e22170ef37d1b4ae48569 Mon Sep 17 00:00:00 2001 From: Anthony LC Date: Tue, 30 Dec 2025 16:32:32 +0100 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=90=9B(export)=20fix=20heading=204=20?= =?UTF-8?q?5=206=20in=20PDF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Heading 4 5 6 in PDF were not correctly mapped to their corresponding styles in the exported document. The new export is now different than the regression pdf, we need to update the regression PDF to match the new correct export. --- .../assets/doc-export-regressions.pdf | Bin 56932 -> 56963 bytes .../doc-export/blocks-mapping/headingPDF.tsx | 15 ++++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/frontend/apps/e2e/__tests__/app-impress/assets/doc-export-regressions.pdf b/src/frontend/apps/e2e/__tests__/app-impress/assets/doc-export-regressions.pdf index 6839736b65c60e08789105bffc8cf8ea40392585..af6918447647cdbcdde7da9611002780081a00e8 100644 GIT binary patch delta 3274 zcmai0X&{vQ8c$>yTVm{LvX)Zsyfg1RGdU=`Y}wb0eaV&~Da%my5C*4&V~fnm#hH*$ zmago|Zfr@Z?B`GkG?B*61^v$#=XhA`m!XY-6 zKXe*e@YtX7?>-7iD1=?#%*M#Tin1y+^S3BOSwIf`L#5G$Tk-p0kaEFRm{KXpLte7i z_n&|dK3%DJdBi$)h2z3TYB6uWMiiF)Iwwh2s9x!{?~J(TPK1t-oQaU0UOl~soEY^Ucaf%UR)z+hb~Z*K1$$0QZtSc05T&y+B=sDrH7F$& z5{()o5gQw~zVvM_k6f+zdDAOyi7aOPiCAy7lBfG=OVIvP!7~tSAE|Ztd8)w9^Q(fb zfbHbeit4vWG`s5Ds{3X8Rtenu<|843{SnUB%)AnKW~_gq*2PbmXB^dt>3qzsKzX z`p_NG=B)Nqu9WyJ(+-c5t@VNJ7ZeLK<2!&}y!^>9T zQtn5qkdaqq;xTwD^Uki`Pr7l)Xp3Hv2GO4+jc(H#M7$gTA39&?^r&h`Kq1?ke3Mm| zvubQXDb#O)RgD=69as~)GFALoO~rK&x390Y$HF(Cp_dM|Fs z4iP#3<$INbnDuSsqsW5(nbDb!+Ev=r!E$GMFMPv+JGZL%he6qF@@g+}-*c{g$_J#N z+WWRD3Zt8Td}Bz?+BoNhkwf`2(GmTD4%A13o7Jv8T?Pd~s zv(hQ37>^NwFHjWjN;7U@IbX}=+$EG~#@%{;jmtQPRld|jn4svAUcD(gK%jo^s}{rJ z^gu&RXRX@?f^`si`e@E=;Tr6a4S%MPo^IeVsb)1Hlait;C0ROmMc)I*i7C$(C*`kP zd4+RraibFo53gx2-K_9`;WESKoo8}{H}H0j+1A7JmNSs_fLy9nIqF)YW>29n zQ1lx5{!SLK=WNpOp%@10iVC%fC3s>a@Na<2iJ18%pXPgH6(i3Ny7$Z5TpSh;%bNLW zi(Bv8;_6lwm4p_`D+8y(PJHltQe1A64VL}ddD&57JiY`P_jH54&#A{)1hj!+#_Gw% zc6J|IEm>zlud^=~*3NTyZVv1cG8yl5;Y{DNSEV5;OzAGeB{4@Pd`fu9o7fk!Er zH|S8+?KY=WF^@qBGW|wQ4K2Cfau$6$m_NM?mf<}a`HgK(iYF!eljSJEn_V&(cd>u? zq~YCqk%gA1WFMKV{eH!nLmT(Fk(U1!>snReg3|qb@AKR7CUq6Fo~!6PA@x}QJ}W&7 z+tweY8!r7(!J4Z6`MzSYc}xa1)f8z;RX=Z`EAF$kCz7AqP3X*898Ho=tbP^t9ll(K zNKlD9W^q&u`jMLYtM3g{lpzSnrj8e-dzmM5uIy!q?c1$t2yE{TaZKMb#`7UrE7+`b)Lok1Fp+C| zjOY4CZD4)Aq{g{Ip_VwcI(Um-n_Co66!Sgp!`#$5w-iged$u+{t1HiaLmL)9KDkZ`S8SJ1nl+=yq2mmc z=LiZM3*svZI{V4w?nxv2t4k$z`8KEO<|e4~s^(Cj)h%I;NA&el+SFzfIXJG`IIkg_Q`-`Mb-kOc02^g#|JZj>&{0GmU4ZI;%qfq0&nrC3$0{iD zO3IouX?cD9*$gRGwrhy^^%t}C1FWs9L?}zFJ0zO%Hc%QqCh;ItMx7VeVj@kLA4v)F91#!#9P>TN){GlD?zCAHFEnT-`a{<(5QEj@}0Q)?!$%>ii&qO5EV}L(J<+HSF4>@ zaQo1ibj{7sw{SzNQFY(i;)~T%Khg7#IBrar_}EonnJkk0`OQunb}C@IYKhZzfH?FR zP<>pv>OQ&iXq>7~qgho%x?@2U@cTpgp%8llfCJGOCK~`S#lZv!%M=H~0D}#U$Kzm1 z`=~JzV(!Np!jJ93k@l1d)1ZWQs`5(7~ z#$XAIDA5=^fhifgyA4K=SUl4`p|J#}(?CPu{|vp`fA{VF$}=4!1YsDPpz$CKGc*Q4 zXcxj>mA|$CVgQ(t1rP`08GB$Lv@n{nAOob4Tog!s8Hfa$3gPi2ne&Tot$g delta 3240 zcmai0XH-+^7NwVnA_gK=LFvt#eglHioAfRQq&Mk75RglkCIUf50~w`=PUuo>$WVL~ z9R!CCf*=y42qJYvmZdWU+3h4}^rhLJOPKXGXpSz78D)Fs09v;M&O`!2Bk zjmbO*GP!iDO)N|t>nI2KS%2Va{=pxZoGy^YC z=>pCuo=`)SJp>TKA`$qn_-}UGv+0@nqO@!{&T+ z2V}kBy}B>~SAe1u%43(S`1n3;JRPP*ygzdGTYP>}{uckgI#+;>mQL|WzAXR9na?M; zPP&V?W$~qIR@|Z&jCY8ztZ?11!U)ptF{i?y&Ad&ey zkCKrf=kxTXSLv0|AE;x-QWu2X9}{*QpJzjXsybJ5TAuMV>ZY6t)~(Mo)^rzHJ|U&^ zORC*5>Gqr$`GtyYdZ}zeoqMXNUuha^DOLKR+|?KEQquZ@NoJ;|NA*eRv@c<-i;}eV zJZ$g1h1&Yzh*Nh@DdOpgJDaD<-)_AfzJK_WA#az@OCD>RV0JqDW?bg&6nL?;y@Stw z$c%UXbHPSTd1KjSEj@J~=WDYomsh9ebL!hub?(ghMP7Y%{3ofqYgsRgouariiBEJ# z6GuKh&VNJx^ufh!>#kPsMZY2gq%h?f>rzAeg5_Lq!9wnow;O&~lY)xWvU7QuWaq4&H-I8Yp9nHH=kXrNlvqZBzlCuu6+tUK z+zy$N(rdfFgo-S-7uNt^etb#fq25*?NAWUg=mRoTSIWk^+YKxox{Bm4aXCI zA5u0=8;JA-Ypw=x^iu-r?~6iVMQt?DU8GsANPnQRa^1B&K7@)nKd`uF%P^@zj1qE5thXzMW%XVVlKBVT5F!RCUt%&m5a=xoOo2vx1*Lq*W6nJ#iKEo)iw4R zIILwZc-)A7S2*TvdmmQKdQ&xi>HIIoi!we4(?nj*>eUn8@zcgE%WqpqipCv9;Wq@b zM*FAnN4Rgg>NgarKwZA-BhU$bB3P3XkYv?z^?x})`pbdMTGHw9+zt!1gZIp(Cpf}vyHxFy-_f$q3FVe+huRue z!O{`~e)lX!kr)^oT5wv`^05IE2ag9{SSa zY!!^at^P<+`ulsrDK5sgJXh_K4V5FmKz#K7WO$48-cufKdDVIMyI8p}yb9KBE5*w) zN6R(^zF5(;zQ0u#WUdP6oUQ?~)`J(3afpvOWqg%Dy0~hDrfsC;S)RK^r zJoxT6%A4!Dw=Z_eg4YL}CN4LgYb!lg z7S#Uq*T#m{w4$My%8WHxM(WBLC%TLr_oC>EtbzWw8_*N#!_A&Dr`wLI zkLa&y3$7J@^eV*u1Lr4FvYIp=fArq+>X}R~s$>Nkg>MR!#91m$>@*jUR*|(HrY&DEVinQ!70JaHiUN}G> zdDP5mH^$DtSkX(=hk_&bolTRjUA`E$7fhNp10PuAX>{j?+7 z+h@Ql(FJ_nPx7CvwdiTr+!=&yusUh%$1ypB#sUbC(ds?7@L2NPgoX