Compare commits

...

14 Commits

Author SHA1 Message Date
Clémence Fernandez
e80fddc752 Fix images aspect with ratio 2025-09-16 16:41:03 +02:00
Clémence Fernandez
53af6731db Adapt anchor type 2025-09-16 16:41:03 +02:00
Clémence Fernandez
118ae457dd Add pictures in manifest.xml to fix corrupted file 2025-09-16 16:41:03 +02:00
Clémence Fernandez
c15ef66e77 Test if there are two draw:image in the generated document 2025-09-16 16:41:03 +02:00
Clémence Fernandez
b0fe1d8987 Add a draw image and a draw frame into odt file 2025-09-16 16:41:03 +02:00
Clémence Fernandez
69d146defd Regenerate yo odt to inspect it 2025-09-16 16:40:57 +02:00
Clémence Fernandez
ecb6a48cb0 create addImageToOdtFile 2025-09-16 16:40:05 +02:00
Clémence Fernandez
1c54f226cb Create OfjsImage type 2025-09-16 16:40:05 +02:00
Clémence Fernandez
b3d77cfdb9 WORK IN PROGRESS - trouver et évaluer la balise image 2025-09-16 16:40:05 +02:00
Clémence Fernandez
a4173b1249 Ajout d'un test pour vérifier que le texte du template est bon 2025-09-16 16:40:04 +02:00
Clémence Fernandez
ef5da802db image marker regex 2025-09-16 16:40:04 +02:00
Clémence Fernandez
947b722230 Add test for insert 2 images 2025-09-16 16:40:04 +02:00
Clémence Fernandez
77fb6e998a Rename template 2025-09-16 16:40:04 +02:00
Clémence Fernandez
7c10cb27c0 add template 2025-09-16 16:40:04 +02:00
13 changed files with 246 additions and 27 deletions

18
package-lock.json generated
View File

@ -10,6 +10,7 @@
"dependencies": { "dependencies": {
"@xmldom/xmldom": "^0.9.8", "@xmldom/xmldom": "^0.9.8",
"@zip.js/zip.js": "^2.7.57", "@zip.js/zip.js": "^2.7.57",
"image-size": "^2.0.2",
"ses": "^1.12.0" "ses": "^1.12.0"
}, },
"devDependencies": { "devDependencies": {
@ -2185,6 +2186,18 @@
"node": ">=10 <11 || >=12 <13 || >=14" "node": ">=10 <11 || >=12 <13 || >=14"
} }
}, },
"node_modules/image-size": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz",
"integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==",
"license": "MIT",
"bin": {
"image-size": "bin/image-size.js"
},
"engines": {
"node": ">=16.x"
}
},
"node_modules/immutable": { "node_modules/immutable": {
"version": "4.2.4", "version": "4.2.4",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz",
@ -6129,6 +6142,11 @@
"integrity": "sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==", "integrity": "sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==",
"dev": true "dev": true
}, },
"image-size": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz",
"integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w=="
},
"immutable": { "immutable": {
"version": "4.2.4", "version": "4.2.4",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz",

View File

@ -41,6 +41,7 @@
"dependencies": { "dependencies": {
"@xmldom/xmldom": "^0.9.8", "@xmldom/xmldom": "^0.9.8",
"@zip.js/zip.js": "^2.7.57", "@zip.js/zip.js": "^2.7.57",
"image-size": "^2.0.2",
"ses": "^1.12.0" "ses": "^1.12.0"
} }
} }

View File

@ -7,7 +7,7 @@ import {parseXML, Node} from '../../DOMUtils.js'
* @param {ODTFile} odtFile * @param {ODTFile} odtFile
* @returns {Promise<Document>} * @returns {Promise<Document>}
*/ */
async function getContentDocument(odtFile) { export async function getContentDocument(odtFile) {
const reader = new ZipReader(new Uint8ArrayReader(new Uint8Array(odtFile))); const reader = new ZipReader(new Uint8ArrayReader(new Uint8Array(odtFile)));
const entries = await reader.getEntries(); const entries = await reader.getEntries();

View File

@ -1,5 +1,8 @@
import {traverse, Node, getAncestors, findCommonAncestor} from "../../DOMUtils.js"; import {traverse, Node, getAncestors, findCommonAncestor} from "../../DOMUtils.js";
import {closingIfMarker, eachClosingMarker, eachStartMarkerRegex, elseMarker, ifStartMarkerRegex, variableRegex} from './markers.js' import {closingIfMarker, eachClosingMarker, eachStartMarkerRegex, elseMarker, ifStartMarkerRegex, imageMarkerRegex, variableRegex} from './markers.js'
import {isOdfjsImage} from "../../shared.js"
import imageSize from "image-size";
/** @import {OdfjsImage} from "../../types.js" */
/** /**
* @typedef TextPlaceToFill * @typedef TextPlaceToFill
@ -182,12 +185,18 @@ class TemplateBlock{
/** @type {Node[]} */ /** @type {Node[]} */
#middleContent; #middleContent;
/**@type {any} */
#addImageToOdtFile;
/** /**
* *
* @param {Node} startNode * @param {Node} startNode
* @param {Node} endNode * @param {Node} endNode
* @param {(OdfjsImage) => string} addImageToOdtFile
*/ */
constructor(startNode, endNode){ constructor(startNode, endNode, addImageToOdtFile){
this.#addImageToOdtFile = addImageToOdtFile
// @ts-expect-error xmldom.Node // @ts-expect-error xmldom.Node
this.#commonAncestor = findCommonAncestor(startNode, endNode) this.#commonAncestor = findCommonAncestor(startNode, endNode)
@ -231,7 +240,7 @@ class TemplateBlock{
const startChild = this.startBranch.at(1) const startChild = this.startBranch.at(1)
if(startChild /*&& startChild !== */){ if(startChild /*&& startChild !== */){
//console.log('[fillBlockContentTemplate] startChild', startChild.nodeName, startChild.textContent) //console.log('[fillBlockContentTemplate] startChild', startChild.nodeName, startChild.textContent)
fillOdtElementTemplate(startChild, compartement) fillOdtElementTemplate(startChild, compartement, this.#addImageToOdtFile)
} }
//console.log('[fillBlockContentTemplate] after startChild') //console.log('[fillBlockContentTemplate] after startChild')
@ -239,7 +248,7 @@ class TemplateBlock{
// if content consists of several parts of an {#each}{/each} // if content consists of several parts of an {#each}{/each}
// when arriving to the {/each}, it will be alone (and imbalanced) // when arriving to the {/each}, it will be alone (and imbalanced)
// and will trigger an error // and will trigger an error
fillOdtElementTemplate(Array.from(this.#middleContent), compartement) fillOdtElementTemplate(Array.from(this.#middleContent), compartement, this.#addImageToOdtFile)
//console.log('[fillBlockContentTemplate] after middleContent') //console.log('[fillBlockContentTemplate] after middleContent')
@ -249,7 +258,7 @@ class TemplateBlock{
if(endChild){ if(endChild){
//console.log('[fillBlockContentTemplate] endChild', endChild.nodeName, endChild.textContent) //console.log('[fillBlockContentTemplate] endChild', endChild.nodeName, endChild.textContent)
fillOdtElementTemplate(endChild, compartement) fillOdtElementTemplate(endChild, compartement, this.#addImageToOdtFile)
} }
//console.log('[fillBlockContentTemplate] after endChild') //console.log('[fillBlockContentTemplate] after endChild')
@ -347,7 +356,7 @@ class TemplateBlock{
} }
} }
return new TemplateBlock(startLeafCloneNode, endLeafCloneNode) return new TemplateBlock(startLeafCloneNode, endLeafCloneNode, this.#addImageToOdtFile)
} }
} }
@ -426,8 +435,9 @@ function findPlacesToFillInString(str, compartment) {
* @param {Node} ifClosingMarkerNode * @param {Node} ifClosingMarkerNode
* @param {string} ifBlockConditionExpression * @param {string} ifBlockConditionExpression
* @param {Compartment} compartment * @param {Compartment} compartment
* // TODO type,addImageToOdtFile
*/ */
function fillIfBlock(ifOpeningMarkerNode, ifElseMarkerNode, ifClosingMarkerNode, ifBlockConditionExpression, compartment) { function fillIfBlock(ifOpeningMarkerNode, ifElseMarkerNode, ifClosingMarkerNode, ifBlockConditionExpression, compartment, addImageToOdtFile) {
//const docEl = ifOpeningMarkerNode.ownerDocument.documentElement //const docEl = ifOpeningMarkerNode.ownerDocument.documentElement
const conditionValue = compartment.evaluate(ifBlockConditionExpression) const conditionValue = compartment.evaluate(ifBlockConditionExpression)
@ -443,11 +453,11 @@ function fillIfBlock(ifOpeningMarkerNode, ifElseMarkerNode, ifClosingMarkerNode,
ifElseMarkerNode.childNodes.length, ifElseMarkerNode.textContent ifElseMarkerNode.childNodes.length, ifElseMarkerNode.textContent
)*/ )*/
thenTemplateBlock = new TemplateBlock(ifOpeningMarkerNode, ifElseMarkerNode) thenTemplateBlock = new TemplateBlock(ifOpeningMarkerNode, ifElseMarkerNode, addImageToOdtFile)
elseTemplateBlock = new TemplateBlock(ifElseMarkerNode, ifClosingMarkerNode) elseTemplateBlock = new TemplateBlock(ifElseMarkerNode, ifClosingMarkerNode, addImageToOdtFile)
} }
else { else {
thenTemplateBlock = new TemplateBlock(ifOpeningMarkerNode, ifClosingMarkerNode) thenTemplateBlock = new TemplateBlock(ifOpeningMarkerNode, ifClosingMarkerNode, addImageToOdtFile)
} }
if(conditionValue) { if(conditionValue) {
@ -486,14 +496,15 @@ function fillIfBlock(ifOpeningMarkerNode, ifElseMarkerNode, ifClosingMarkerNode,
* @param {string} itemExpression * @param {string} itemExpression
* @param {Node} endNode * @param {Node} endNode
* @param {Compartment} compartment * @param {Compartment} compartment
* // TODO type addImageToOdtFile
*/ */
function fillEachBlock(startNode, iterableExpression, itemExpression, endNode, compartment) { function fillEachBlock(startNode, iterableExpression, itemExpression, endNode, compartment, addImageToOdtFile) {
//console.log('fillEachBlock', iterableExpression, itemExpression) //console.log('fillEachBlock', iterableExpression, itemExpression)
const docEl = startNode.ownerDocument.documentElement const docEl = startNode.ownerDocument.documentElement
//console.log('[fillEachBlock] docEl', docEl.textContent) //console.log('[fillEachBlock] docEl', docEl.textContent)
const repeatedTemplateBlock = new TemplateBlock(startNode, endNode) const repeatedTemplateBlock = new TemplateBlock(startNode, endNode, addImageToOdtFile)
// Find the iterable in the data // Find the iterable in the data
let iterable = compartment.evaluate(iterableExpression) let iterable = compartment.evaluate(iterableExpression)
@ -554,6 +565,29 @@ function fillEachBlock(startNode, iterableExpression, itemExpression, endNode, c
} }
/**
* @param {string} str
* @param {Compartement} compartment
* @returns { {expression: string, odfjsImage: OdfjsImage | undefined} | undefined}
*/
function findImageMarker(str, compartment) {
const imageRexExp = new RegExp(imageMarkerRegex.source, 'g');
const match = imageRexExp.exec(str)
if (match===null){
return;
}
const expression = match[1]
const value = compartment.evaluate(expression)
if (isOdfjsImage(value)) {
return { expression, odfjsImage: value}
} else {
return { expression }
}
}
const IF = ifStartMarkerRegex.source const IF = ifStartMarkerRegex.source
const EACH = eachStartMarkerRegex.source const EACH = eachStartMarkerRegex.source
@ -564,9 +598,10 @@ const EACH = eachStartMarkerRegex.source
* *
* @param {RootElementArgument | RootElementArgument[]} rootElements * @param {RootElementArgument | RootElementArgument[]} rootElements
* @param {Compartment} compartment * @param {Compartment} compartment
* @param {(OdfjsImage) => string} addImageToOdtFile
* @returns {void} * @returns {void}
*/ */
export default function fillOdtElementTemplate(rootElements, compartment) { export default function fillOdtElementTemplate(rootElements, compartment, addImageToOdtFile) {
if(!Array.isArray(rootElements)){ if(!Array.isArray(rootElements)){
rootElements = [rootElements] rootElements = [rootElements]
@ -653,7 +688,7 @@ export default function fillOdtElementTemplate(rootElements, compartment) {
// execute replacement loop // execute replacement loop
//console.log('start of fillEachBlock') //console.log('start of fillEachBlock')
fillEachBlock(eachOpeningMarkerNode, eachBlockIterableExpression, eachBlockItemExpression, eachClosingMarkerNode, compartment) fillEachBlock(eachOpeningMarkerNode, eachBlockIterableExpression, eachBlockItemExpression, eachClosingMarkerNode, compartment, addImageToOdtFile)
//console.log('end of fillEachBlock') //console.log('end of fillEachBlock')
@ -728,7 +763,7 @@ export default function fillOdtElementTemplate(rootElements, compartment) {
// found an {#if} and its corresponding {/if} // found an {#if} and its corresponding {/if}
// execute replacement loop // execute replacement loop
fillIfBlock(ifOpeningMarkerNode, ifElseMarkerNode, ifClosingMarkerNode, ifBlockConditionExpression, compartment) fillIfBlock(ifOpeningMarkerNode, ifElseMarkerNode, ifClosingMarkerNode, ifBlockConditionExpression, compartment, addImageToOdtFile)
ifOpeningMarkerNode = undefined ifOpeningMarkerNode = undefined
ifElseMarkerNode = undefined ifElseMarkerNode = undefined
@ -761,6 +796,54 @@ export default function fillOdtElementTemplate(rootElements, compartment) {
const newTextNode = currentNode.ownerDocument?.createTextNode(newText) const newTextNode = currentNode.ownerDocument?.createTextNode(newText)
// @ts-ignore // @ts-ignore
currentNode.parentNode?.replaceChild(newTextNode, currentNode) currentNode.parentNode?.replaceChild(newTextNode, currentNode)
} else {
const imageMarker = findImageMarker(currentNode.data, compartment)
if (imageMarker){
console.log({imageMarker}, "dans le if imageMarker")
if (imageMarker.odfjsImage) {
const href = addImageToOdtFile(imageMarker.odfjsImage)
const newImageNode = currentNode.ownerDocument?.createElement("draw:image")
newImageNode.setAttribute("xlink:href", href)
newImageNode.setAttribute("xlink:type", "simple")
newImageNode.setAttribute("xlink:show", "embed")
newImageNode.setAttribute("xlink:actuate", "onLoad")
newImageNode.setAttribute("draw:mime-type", imageMarker.odfjsImage.mediaType)
const newFrameNode = currentNode.ownerDocument?.createElement('draw:frame')
newFrameNode.setAttribute("text:anchor-type", "as-char")
const buffer = new Uint8Array(imageMarker.odfjsImage.content)
const dimensions = imageSize(buffer)
const MAX_WIDTH = 10 // cm
const MAX_HEIGHT = 10 // cm
let width;
let height;
if(dimensions.width > dimensions.height){
// image in landscape
width = MAX_WIDTH;
height = width*dimensions.height/dimensions.width
}
else{
// image in portrait
height = MAX_HEIGHT;
width = height*dimensions.width/dimensions.height
}
newFrameNode.setAttribute("svg:width", `${width}cm`)
newFrameNode.setAttribute("svg:height", `${height}cm`)
newFrameNode.appendChild(newImageNode)
currentNode.parentNode?.replaceChild(newFrameNode, currentNode)
} else {
throw new Error(`No valid OdfjsImage value has been found for expression: ${imageMarker.expression}`)
}
}
} }
} }
} }

View File

@ -11,7 +11,8 @@ lockdown();
/** @import {Reader, ZipWriterAddDataOptions} from '@zip.js/zip.js' */ /** @import {Reader, ZipWriterAddDataOptions} from '@zip.js/zip.js' */
/** @import {ODFManifest} from '../manifest.js' */ /** @import {ODFManifest, ODFManifestFileEntry} from '../manifest.js' */
/** @import {OdfjsImage} from '../../types.js' */
/** @typedef {ArrayBuffer} ODTFile */ /** @typedef {ArrayBuffer} ODTFile */
@ -23,11 +24,12 @@ const ODTMimetype = 'application/vnd.oasis.opendocument.text'
* *
* @param {Document} document * @param {Document} document
* @param {Compartment} compartment * @param {Compartment} compartment
* @param {(OdfjsImage) => string} addImageToOdtFile
* @returns {void} * @returns {void}
*/ */
function fillOdtDocumentTemplate(document, compartment) { function fillOdtDocumentTemplate(document, compartment, addImageToOdtFile) {
prepareTemplateDOMTree(document) prepareTemplateDOMTree(document)
fillOdtElementTemplate(document, compartment) fillOdtElementTemplate(document, compartment, addImageToOdtFile)
} }
@ -64,6 +66,21 @@ export default async function fillOdtTemplate(odtTemplate, data) {
/** @type {{filename: string, content: Reader, options?: ZipWriterAddDataOptions}[]} */ /** @type {{filename: string, content: Reader, options?: ZipWriterAddDataOptions}[]} */
const zipEntriesToAdd = [] const zipEntriesToAdd = []
/** @type {ODFManifestFileEntry[]} */
const newManifestEntries = []
/**
* Return href
* @param {OdfjsImage} odfjsImage
* @returns {string}
*/
function addImageToOdtFile(odfjsImage) {
// console.log({odfjsImage})
const filename = `Pictures/${odfjsImage.fileName}`
zipEntriesToAdd.push({content: new Uint8ArrayReader(new Uint8Array(odfjsImage.content)), filename})
newManifestEntries.push({fullPath: filename, mediaType: odfjsImage.mediaType})
return filename
}
// Parcourir chaque entrée du fichier ODT // Parcourir chaque entrée du fichier ODT
for await(const entry of entries) { for await(const entry of entries) {
@ -97,12 +114,14 @@ export default async function fillOdtTemplate(odtTemplate, data) {
const contentXml = await entry.getData(new TextWriter()); const contentXml = await entry.getData(new TextWriter());
const contentDocument = parseXML(contentXml); const contentDocument = parseXML(contentXml);
const compartment = new Compartment({ const compartment = new Compartment({
globals: data, globals: data,
__options__: true __options__: true
}) })
fillOdtDocumentTemplate(contentDocument, compartment) fillOdtDocumentTemplate(contentDocument, compartment, addImageToOdtFile)
const updatedContentXml = serializeToString(contentDocument) const updatedContentXml = serializeToString(contentDocument)
@ -138,6 +157,9 @@ export default async function fillOdtTemplate(odtTemplate, data) {
} }
} }
for(const {fullPath, mediaType} of newManifestEntries){
manifestFileData.fileEntries.set(fullPath, {fullPath, mediaType})
}
for(const {filename, content, options} of zipEntriesToAdd) { for(const {filename, content, options} of zipEntriesToAdd) {
await writer.add(filename, content, options); await writer.add(filename, content, options);

View File

@ -1,5 +1,6 @@
// the regexps below are shared, so they shoudn't have state (no 'g' flag) // the regexps below are shared, so they shoudn't have state (no 'g' flag)
export const variableRegex = /\{([^{#\/:]+?)\}/ export const variableRegex = /\{([^{#\/:]+?)\}/
export const imageMarkerRegex = /{#image\s+([^}]+?)\s*}/;
export const ifStartMarkerRegex = /{#if\s+([^}]+?)\s*}/; export const ifStartMarkerRegex = /{#if\s+([^}]+?)\s*}/;
export const elseMarker = '{:else}' export const elseMarker = '{:else}'

View File

@ -4,7 +4,7 @@ import { Uint8ArrayReader, ZipReader, TextWriter } from '@zip.js/zip.js';
import {parseXML} from './DOMUtils.js' import {parseXML} from './DOMUtils.js'
/** @import {Entry} from '@zip.js/zip.js'*/ /** @import {Entry} from '@zip.js/zip.js'*/
/** @import {SheetName, SheetRawContent, SheetRowRawContent, SheetCellRawContent} from './types.js' */ /** @import {SheetName, SheetRawContent, SheetRowRawContent, SheetCellRawContent, OdfjsImage} from './types.js' */
// https://dom.spec.whatwg.org/#interface-node // https://dom.spec.whatwg.org/#interface-node
@ -160,6 +160,22 @@ export function convertCellValue({value, type}) {
} }
/**
* @param {unknown} value
* @returns {value is OdfjsImage}
*/
export function isOdfjsImage(value) {
if (typeof value === 'object' && value!==null
&& "content" in value && value.content instanceof ArrayBuffer
&& "fileName" in value && typeof value.fileName === 'string'
&& "mediaType" in value && typeof value.mediaType === 'string'
) {
return true
} else {
return false
}
}

View File

@ -10,4 +10,12 @@
/** @typedef {string} SheetName */ /** @typedef {string} SheetName */
/**
* @typedef OdfjsImage
* @prop {ArrayBuffer} content
* @prop {string} fileName
* @prop {string} mediaType
*
*/
export {} export {}

View File

@ -1,13 +1,15 @@
import test from 'ava'; import test from 'ava';
import {join} from 'node:path'; import {join} from 'node:path';
import { readFile } from 'node:fs/promises'
import {getOdtTemplate} from '../../scripts/odf/odtTemplate-forNode.js' import {getOdtTemplate} from '../../scripts/odf/odtTemplate-forNode.js'
import {fillOdtTemplate} from '../../exports.js' import {fillOdtTemplate, getOdtTextContent} from '../../exports.js'
import { listZipEntries } from '../helpers/zip-analysis.js'; import { listZipEntries } from '../helpers/zip-analysis.js';
import { getContentDocument } from '../../scripts/odf/odt/getOdtTextContent.js';
test('template filling preserves images', async t => { test.skip('template filling preserves images', async t => {
const templatePath = join(import.meta.dirname, '../fixtures/template-avec-image.odt') const templatePath = join(import.meta.dirname, '../fixtures/template-avec-image.odt')
const data = { const data = {
@ -36,3 +38,47 @@ test('template filling preserves images', async t => {
) )
}) })
test('insert 2 images', async t => {
const templatePath = join(import.meta.dirname, '../fixtures/basic-image-insertion.odt')
const odtTemplate = await getOdtTemplate(templatePath)
const templateContent = `{title}
{#each photos as photo}
{#image photo}
{/each}
`
const templateTextContent = await getOdtTextContent(odtTemplate)
t.is(templateTextContent, templateContent, 'reconnaissance du template')
const photo1Path = join(import.meta.dirname, '../fixtures/pitchou-1.png')
const photo2Path = join(import.meta.dirname, '../fixtures/pitchou-2.png')
const photo1Buffer = (await readFile(photo1Path)).buffer
const photo2Buffer = (await readFile(photo2Path)).buffer
const photos = [{content: photo1Buffer, fileName: 'pitchou-1.png', mediaType: 'image/png'}, {content: photo2Buffer, fileName: 'pitchou-2.png', mediaType: 'image/png'}]
const data = {
title: 'Titre de mon projet',
photos,
}
const odtResult = await fillOdtTemplate(odtTemplate, data)
const resultEntries = await listZipEntries(odtResult)
t.is(
resultEntries.filter(entry => entry.filename.startsWith('Pictures/')).length, 2,
`Two pictures in 'Pictures/' folder are expected`
)
const odtContentDocument = await getContentDocument(odtResult)
const drawImageElements = odtContentDocument.getElementsByTagName('draw:image')
t.is(drawImageElements.length, 2, 'Two draw:image elements should be in the generated document.')
})

BIN
tests/fixtures/basic-image-insertion.odt vendored Normal file

Binary file not shown.

BIN
tests/fixtures/pitchou-1.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 KiB

BIN
tests/fixtures/pitchou-2.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 799 KiB

View File

@ -1,4 +1,4 @@
import {writeFile} from 'node:fs/promises' import {writeFile, readFile} from 'node:fs/promises'
import {join} from 'node:path'; import {join} from 'node:path';
import {getOdtTemplate} from '../scripts/odf/odtTemplate-forNode.js' import {getOdtTemplate} from '../scripts/odf/odtTemplate-forNode.js'
@ -114,9 +114,33 @@ const data = {
] ]
} }
*/ */
// const templatePath = join(import.meta.dirname, '../tests/fixtures/text-after-closing-each.odt')
// const data = {
// saison: 'Printemps',
// légumes: [
// 'Asperge',
// 'Betterave',
// 'Blette'
// ]
// }
const templatePath = join(import.meta.dirname, '../tests/fixtures/if-then-each.odt') // const templatePath = join(import.meta.dirname, '../tests/fixtures/if-then-each.odt')
const data = {liste_départements : ['95', '33']} // const data = {liste_départements : ['95', '33']}
const templatePath = join(import.meta.dirname, '../tests/fixtures/basic-image-insertion.odt')
const photo1Path = join(import.meta.dirname, '../tests/fixtures/pitchou-1.png')
const photo2Path = join(import.meta.dirname, '../tests/fixtures/pitchou-2.png')
const photo1Buffer = (await readFile(photo1Path)).buffer
const photo2Buffer = (await readFile(photo2Path)).buffer
const photos = [{content: photo1Buffer, fileName: 'pitchou-1.png', mediaType: 'image/png'}, {content: photo2Buffer, fileName: 'pitchou-2.png', mediaType: 'image/png'}]
const data = {
title: 'Titre de mon projet',
photos,
}
const odtTemplate = await getOdtTemplate(templatePath) const odtTemplate = await getOdtTemplate(templatePath)