From dcd3def9425f955e3c24589f0f6cf9a467fb271e Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Fri, 25 Aug 2023 15:13:36 +0100 Subject: [PATCH] All: Resolves #8684: Apply correct size to images imported from ENEX files --- .../images_with_and_without_size.enex | 384 ++++++++++++++++++ .../images_with_and_without_size.md | 17 + packages/lib/import-enex-md-gen.test.ts | 56 ++- packages/lib/import-enex-md-gen.ts | 72 +++- 4 files changed, 492 insertions(+), 37 deletions(-) create mode 100644 packages/app-cli/tests/enex_to_md/images_with_and_without_size.enex create mode 100644 packages/app-cli/tests/enex_to_md/images_with_and_without_size.md diff --git a/packages/app-cli/tests/enex_to_md/images_with_and_without_size.enex b/packages/app-cli/tests/enex_to_md/images_with_and_without_size.enex new file mode 100644 index 00000000000..838c06348a1 --- /dev/null +++ b/packages/app-cli/tests/enex_to_md/images_with_and_without_size.enex @@ -0,0 +1,384 @@ + + + +Dashboard | MassPay + + +

Last Transfer

Next Day Bank Deposit / USD
March 5, 2023 04:28AM

Processing
Confirmation: ILbwHO5Z06p7meW


]]>
20230305T042718Z20230305T043507ZMLNPweb.clip7https://members.masspay.io/homewebclipper.evernote +PHN2ZyBpZD0iQ2FwYV8xIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA1MTIgNTEyIiBoZWln +aHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHdpZHRoPSI1MTIiIHhtbG5zPSJodHRwOi8v +d3d3LnczLm9yZy8yMDAwL3N2ZyI+PGc+PHBhdGggZD0ibTQ4MS45OTIgNDg4LjExMi00MS44LTU2 +LjgzMXYtMjQyLjIyNmgxOS43MTZjOC4yODQgMCAxNS02LjcxNiAxNS0xNXYtNjAuNzk2YzAtOC4y +ODQtNi43MTYtMTUtMTUtMTVoLTMwLjY3N2wtMTY1LjY5OC05Ni4yM2MtNC42NTgtMi43MDUtMTAu +NDA4LTIuNzA1LTE1LjA2NiAwbC0xNjUuNjk5IDk2LjIzaC0zMC42NzZjLTguMjg0IDAtMTUgNi43 +MTYtMTUgMTV2NjAuNzk2YzAgOC4yODQgNi43MTYgMTUgMTUgMTVoMTkuNzE2djI0Mi4yMjZsLTQx +Ljc5OSA1Ni44MzFjLTMuMzUyIDQuNTU3LTMuODU0IDEwLjYxMS0xLjMwMSAxNS42NTlzNy43Mjcg +OC4yMjkgMTMuMzg0IDguMjI5aDQyNy44MTdjNS42NTYgMCAxMC44MzItMy4xODIgMTMuMzg1LTgu +MjI5czIuMDUtMTEuMTAyLTEuMzAyLTE1LjY1OXptLTcxLjgtNjYuOTA4aC0zMC43OTZ2LTIzMi4x +NDloMzAuNzk2em0tMjQ3LjU4OCAwdi0yMzIuMTQ5aDE4Ni43OTJ2MjMyLjE0OGgtMTg2Ljc5Mnpt +OTMuMzk2LTM4OC44NTggMTEzLjQ5NiA2NS45MTNoLTIyNi45OTJ6bS0xODguOTA4IDk1LjkxM2gz +NzcuODE3djMwLjc5NmgtMzc3LjgxN3ptNjUuNTEyIDYwLjc5NnYyMzIuMTQ4aC0zMC43OTZ2LTIz +Mi4xNDh6bS02MC44NiAyOTIuOTQ1IDIyLjY1MS0zMC43OTZoMzIzLjIwOWwyMi42NTIgMzAuNzk2 +eiIvPjxwYXRoIGQ9Im0yNzQuNDExIDI4OS45NzljLTExLjAyMS0zLjg5Ni0yMy4xODYtOC41ODMt +MjkuODQ5LTEzLjgwOS0xLjM5OS0xLjA5Ny0xLjk5LTQuMTQ1LTEuNDA1LTcuMjQ4LjMyLTEuNjk2 +IDEuODAxLTcuMzYzIDcuNDQ0LTkuMDYzIDEwLjQzMi0zLjE0MyAxNy41MDcuOTY1IDIwLjA2NCAy +Ljg3OCA2LjYzNSA0Ljk2IDE2LjAzNSAzLjYwMiAyMC45OTYtMy4wMzMgNC45Ni02LjYzNSAzLjYw +My0xNi4wMzUtMy4wMzMtMjAuOTk2LTIuNTUyLTEuOTA4LTguNjczLTUuOTMyLTE3LjQyOS04LjI0 +NXYtMy4wMDdjMC04LjI4NC02LjcxNi0xNS0xNS0xNXMtMTUgNi43MTYtMTUgMTV2My45MzNjLTE0 +LjEzNSA0LjU2OC0yNC42NTEgMTYuNzM5LTI3LjUyMyAzMS45NzctMi43MDUgMTQuMzUxIDIuMDM2 +IDI4LjMwMiAxMi4zNzIgMzYuNDA5IDEwLjA1MSA3Ljg4MyAyNC4zNzkgMTMuNTQ1IDM4LjM2NSAx +OC40ODkgMTAuMzAzIDMuNjQyIDEwLjYxMSAxMS41MDQgOS44NzcgMTUuODY2LTEuMjQzIDcuMzk2 +LTcuNDY2IDE1LjM4OS0xOC4xOTIgMTUuNDYxLTExLjcwNi4wNzctMTQuNzY0LS40MDQtMjMuNDk0 +LTYuMTE1LTYuOTMzLTQuNTM1LTE2LjIyOS0yLjU5MS0yMC43NjQgNC4zNDEtNC41MzUgNi45MzMt +Mi41OTEgMTYuMjI5IDQuMzQxIDIwLjc2NCA5LjUxMiA2LjIyMyAxNi43MTMgOS4wMzggMjUuMDE3 +IDEwLjIyN3YzLjM2MmMwIDguMjg0IDYuNzE2IDE1IDE1IDE1czE1LTYuNzE2IDE1LTE1di00Ljk1 +MWMxOC4yOTEtNS45MzIgMjkuOTg5LTIyLjEyOCAzMi42NzctMzguMTE3IDMuNjc1LTIxLjg1My04 +LjE2Ni00MS41OTUtMjkuNDY0LTQ5LjEyM3oiLz48L2c+PC9zdmc+ +image/svg+xmlhttps://members.masspay.io/assets/img/bank.svgbank.svg +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 +7l4AAAAASUVORK5CYII= +image/png1574138 +Date/TimedatelineMarch5,S,S.5.s2023201304:28AM04:29AM04:23AM04: ?March4,4.I'mifLt,202305:22PMOS:22PM05:22PIII05:22PIlI05:21PIlI05:22P105:22PIl05:22PlIDescriptionNextDayBankSankDepositConfirmation:I Confirmation:I Confirmation;ConfirmationI Confirmationi ? Confirmation:i ? Confirmation;i ? Confirmation[confirmationllbwHOSZ06p7meWILbwH05Z06p7meWILbwHOSZ06p7meWFebruaryFebruarvPayoutAmount$12.50$11.50$12050USD$1350$13.50USDFeeFPPReceivedRPr:PiVPrl$1Slsi125012.SOUSD115012.50 USDUsaUFOUSD$0sogo13.5013.SOUSD13.50 USDUSDStatusProcessingPrecessingCompletedCompleteBalance$0.00$0000$0.00USD$0.00 USDUSD$13.50$13050$13.SOUSD$13.50 USD53.50513.50$1350USD +]]>
diff --git a/packages/app-cli/tests/enex_to_md/images_with_and_without_size.md b/packages/app-cli/tests/enex_to_md/images_with_and_without_size.md new file mode 100644 index 00000000000..bceac53ec91 --- /dev/null +++ b/packages/app-cli/tests/enex_to_md/images_with_and_without_size.md @@ -0,0 +1,17 @@ +![](:/RESOURCE_ID_1) + +#### Last Transfer + +bank.svg + +##### **Next Day Bank Deposit / USD** + +###### **March 5, 2023 04:28AM** + +* * * + +Processing +**Confirmation**: ILbwHO5Z06p7meW + +![](https://joplinapp.org/images/logo-text.svg) + \ No newline at end of file diff --git a/packages/lib/import-enex-md-gen.test.ts b/packages/lib/import-enex-md-gen.test.ts index bb525190ec5..77095b1dde7 100644 --- a/packages/lib/import-enex-md-gen.test.ts +++ b/packages/lib/import-enex-md-gen.test.ts @@ -1,7 +1,7 @@ import { NoteEntity, ResourceEntity, TagEntity } from './services/database/types'; import shim from './shim'; -const fs = require('fs-extra'); +import { readFile, stat } from 'fs/promises'; const os = require('os'); const { filename } = require('./path-utils'); import { setupDatabaseAndSynchronizer, switchClient, expectNotThrow, supportDir, expectThrow } from './testing/test-utils'; @@ -13,6 +13,16 @@ import Resource from './models/Resource'; const enexSampleBaseDir = `${supportDir}/../enex_to_md`; +const importEnexFile = async (filename: string) => { + const filePath = `${enexSampleBaseDir}/${filename}`; + await importEnex('', filePath); +}; + +const readExpectedFile = async (filename: string) => { + const filePath = `${enexSampleBaseDir}/${filename}`; + return readFile(filePath, 'utf8'); +}; + describe('import-enex-md-gen', () => { beforeEach(async () => { @@ -65,8 +75,7 @@ describe('import-enex-md-gen', () => { }); it('should import ENEX metadata', async () => { - const filePath = `${enexSampleBaseDir}/sample-enex.xml`; - await importEnex('', filePath); + await importEnexFile('sample-enex.xml'); const note: NoteEntity = (await Note.all())[0]; expect(note.title).toBe('Test Note for Export'); @@ -87,37 +96,33 @@ describe('import-enex-md-gen', () => { const resource: ResourceEntity = (await Resource.all())[0]; expect(resource.id).toBe('3d0f4d01abc02cf8c4dc1c796df8c4b2'); - const stat = await fs.stat(Resource.fullPath(resource)); - expect(stat.size).toBe(277); + const s = await stat(Resource.fullPath(resource)); + expect(s.size).toBe(277); }); it('should handle invalid dates', async () => { - const filePath = `${enexSampleBaseDir}/invalid_date.enex`; - await importEnex('', filePath); + await importEnexFile('invalid_date.enex'); const note: NoteEntity = (await Note.all())[0]; expect(note.created_time).toBe(1521822724000); // 20180323T163204Z expect(note.updated_time).toBe(1521822724000); // Because this date was invalid, it is set to the created time instead }); it('should handle empty resources', async () => { - const filePath = `${enexSampleBaseDir}/empty_resource.enex`; - await expectNotThrow(() => importEnex('', filePath)); + await expectNotThrow(() => importEnexFile('empty_resource.enex')); const all = await Resource.all(); expect(all.length).toBe(1); expect(all[0].size).toBe(0); }); it('should handle tasks', async () => { - const filePath = `${enexSampleBaseDir}/tasks.enex`; - await importEnex('', filePath); + await importEnexFile('tasks.enex'); const expectedMd = await shim.fsDriver().readFile(`${enexSampleBaseDir}/tasks.md`); const note: NoteEntity = (await Note.all())[0]; expect(note.body).toEqual(expectedMd); }); it('should handle empty note content', async () => { - const filePath = `${enexSampleBaseDir}/empty_content.enex`; - await expectNotThrow(() => importEnex('', filePath)); + await importEnexFile('empty_content.enex'); const all = await Note.all(); expect(all.length).toBe(1); expect(all[0].title).toBe('China and the case for stimulus.'); @@ -131,8 +136,7 @@ describe('import-enex-md-gen', () => { // type "application/octet-stream", which can later cause problems to // open the file. // https://discourse.joplinapp.org/t/importing-a-note-with-a-zip-file/12123?u=laurent - const filePath = `${enexSampleBaseDir}/WithInvalidMime.enex`; - await importEnex('', filePath); + await importEnexFile('WithInvalidMime.enex'); const all = await Resource.all(); expect(all.length).toBe(1); expect(all[0].mime).toBe('application/zip'); @@ -154,8 +158,26 @@ describe('import-enex-md-gen', () => { }); it('should throw an error and stop if the outer XML is invalid', async () => { - const filePath = `${enexSampleBaseDir}/invalid_html.enex`; - await expectThrow(async () => importEnex('', filePath)); + await expectThrow(async () => importEnexFile('invalid_html.enex')); + }); + + it('should import images with sizes', async () => { + await importEnexFile('images_with_and_without_size.enex'); + let expected = await readExpectedFile('images_with_and_without_size.md'); + + const note: NoteEntity = (await Note.all())[0]; + + const all: ResourceEntity[] = await Resource.all(); + + expect(all.length).toBe(2); + + const svgResource = all.find(r => r.mime === 'image/svg+xml'); + const pngResource = all.find(r => r.mime === 'image/png'); + + expected = expected.replace(/RESOURCE_ID_1/, pngResource.id); + expected = expected.replace(/RESOURCE_ID_2/, svgResource.id); + + expect(note.body).toBe(expected); }); }); diff --git a/packages/lib/import-enex-md-gen.ts b/packages/lib/import-enex-md-gen.ts index d56e04ec1a0..a8a459a5d08 100644 --- a/packages/lib/import-enex-md-gen.ts +++ b/packages/lib/import-enex-md-gen.ts @@ -1,5 +1,6 @@ import markdownUtils from './markdownUtils'; import { ResourceEntity } from './services/database/types'; +import { htmlentities } from '@joplin/utils/html'; const stringPadding = require('string-padding'); const stringToStream = require('string-to-stream'); const resourceUtils = require('./resourceUtils.js'); @@ -368,26 +369,49 @@ function tagAttributeToMdText(attr: string): string { return attr; } -function addResourceTag(lines: string[], resource: ResourceEntity, alt = ''): string[] { - // Note: refactor to use Resource.markdownTag - if (!alt) alt = resource.title; - if (!alt) alt = resource.filename; - if (!alt) alt = ''; +interface AddResourceOptions { + alt?: string; + width?: number; + height?: number; +} - alt = tagAttributeToMdText(alt); - if (resourceUtils.isImageMimeType(resource.mime)) { - lines.push('!['); - lines.push(alt); - lines.push(`](:/${resource.id})`); +const addResourceTag = (lines: string[], src: string, mime: string, options: AddResourceOptions): string[] => { + const alt = options.alt ? tagAttributeToMdText(options.alt) : ''; + + if (resourceUtils.isImageMimeType(mime)) { + if (!!options.width || !!options.height) { + const attrs: Record = { src }; + if (options.width) attrs.width = options.width.toString(); + if (options.height) attrs.height = options.height.toString(); + if (alt) attrs.alt = alt; + + const attrsHtml: string[] = []; + for (const [key, value] of Object.entries(attrs)) { + attrsHtml.push(`${key}="${htmlentities(value)}"`); + } + + lines.push(``); + } else { + lines.push('!['); + lines.push(alt); + lines.push(`](${markdownUtils.escapeLinkUrl(src)})`); + } } else { lines.push('['); lines.push(alt); - lines.push(`](:/${resource.id})`); + lines.push(`](${markdownUtils.escapeLinkUrl(src)})`); } return lines; -} +}; + +const altFromResource = (resource: ResourceEntity): string => { + let alt = ''; + if (!alt) alt = resource.title; + if (!alt) alt = resource.filename; + return alt; +}; function isBlockTag(n: string) { return ['div', 'p', 'dl', 'dd', 'dt', 'center', 'address'].indexOf(n) >= 0; @@ -806,12 +830,14 @@ function enexXmlToMdArray(stream: any, resources: ResourceEntity[], tasks: Extra } else if (n === 'q') { section.lines.push('"'); } else if (n === 'img') { + // Many (most?) img tags don't have no source associated, + // especially when they were imported from HTML if (nodeAttributes.src) { - // Many (most?) img tags don't have no source associated, especially when they were imported from HTML - let s = '!['; - if (nodeAttributes.alt) s += tagAttributeToMdText(nodeAttributes.alt); - s += `](${markdownUtils.escapeLinkUrl(nodeAttributes.src)})`; - section.lines.push(s); + section.lines = addResourceTag(section.lines, nodeAttributes.src, 'image/png', { + width: nodeAttributes.width ? Number(nodeAttributes.width) : 0, + height: nodeAttributes.height ? Number(nodeAttributes.height) : 0, + alt: nodeAttributes.alt ? nodeAttributes.alt : '', + }); } } else if (isAnchor(n)) { state.anchorAttributes.push(nodeAttributes); @@ -928,7 +954,11 @@ function enexXmlToMdArray(stream: any, resources: ResourceEntity[], tasks: Extra // means it's an attachement. It will be appended along with the // other remaining resources at the bottom of the markdown text. if (resource && !!resource.id) { - section.lines = addResourceTag(section.lines, resource, nodeAttributes.alt); + section.lines = addResourceTag(section.lines, `:/${resource.id}`, resource.mime, { + alt: nodeAttributes.alt ? nodeAttributes.alt : altFromResource(resource), + width: nodeAttributes.width ? Number(nodeAttributes.width) : 0, + height: nodeAttributes.height ? Number(nodeAttributes.height) : 0, + }); } } else if (n === 'span') { if (isSpanWithStyle(nodeAttributes)) { @@ -1411,7 +1441,9 @@ async function enexXmlToMd(xmlString: string, resources: ResourceEntity[], tasks const r = result.resources[i]; if (firstAttachment) mdLines.push(NEWLINE); mdLines.push(NEWLINE); - mdLines = addResourceTag(mdLines, r, r.filename); + mdLines = addResourceTag(mdLines, `:/${r.id}`, r.mime, { + alt: altFromResource(r), + }); firstAttachment = false; } @@ -1422,4 +1454,4 @@ async function enexXmlToMd(xmlString: string, resources: ResourceEntity[], tasks return output.join('\n'); } -export { enexXmlToMd, processMdArrayNewLines, NEWLINE, addResourceTag, cssValue }; +export { enexXmlToMd, processMdArrayNewLines, NEWLINE, cssValue };