${pageTwoB.title}`);
+ });
+
+ it('should created subsections', async () => {
+ const notes = await importNote(`${supportDir}/onenote/subsections.zip`);
+ const folders = await Folder.all();
+
+ const parentSection = folders.find(f => f.title === 'Group Section 1');
+ const subSection = folders.find(f => f.title === 'Group Section 1-a');
+ const subSection1 = folders.find(f => f.title === 'Subsection 1');
+ const subSection2 = folders.find(f => f.title === 'Subsection 2');
+ const notesFromParentSection = notes.filter(n => n.parent_id === parentSection.id);
+
+ expect(parentSection.id).toBe(subSection1.parent_id);
+ expect(parentSection.id).toBe(subSection2.parent_id);
+ expect(parentSection.id).toBe(subSection.parent_id);
+ expect(folders.length).toBe(7);
+ expect(notes.length).toBe(6);
+ expect(notesFromParentSection.length).toBe(2);
+ });
+
+ it('should expect notes to be rendered the same', async () => {
+ let idx = 0;
+ BaseModel.setIdGenerator(() => String(idx++));
+ const notes = await importNote(`${supportDir}/onenote/complex_notes.zip`);
+
+ for (const note of notes) {
+ expect(note.body).toMatchSnapshot(note.title);
+ }
+ BaseModel.setIdGenerator(uuid.create);
+ });
+
+ it('should render the proper tree for notebook with group sections', async () => {
+ const notes = await importNote(`${supportDir}/onenote/group_sections.zip`);
+ const folders = await Folder.all();
+
+ const mainFolder = folders.find(f => f.title === 'Notebook created on OneNote App');
+ const section = folders.find(f => f.title === 'Section');
+ const sectionA1 = folders.find(f => f.title === 'Section A1');
+ const sectionA = folders.find(f => f.title === 'Section A');
+ const sectionB1 = folders.find(f => f.title === 'Section B1');
+ const sectionB = folders.find(f => f.title === 'Section B');
+ const sectionD1 = folders.find(f => f.title === 'Section D1');
+ const sectionD = folders.find(f => f.title === 'Section D');
+
+ expect(section.parent_id).toBe(mainFolder.id);
+ expect(sectionA.parent_id).toBe(mainFolder.id);
+ expect(sectionD.parent_id).toBe(mainFolder.id);
+
+ expect(sectionA1.parent_id).toBe(sectionA.id);
+ expect(sectionB.parent_id).toBe(sectionA.id);
+
+ expect(sectionB1.parent_id).toBe(sectionB.id);
+ expect(sectionD1.parent_id).toBe(sectionD.id);
+
+ expect(notes.filter(n => n.parent_id === sectionA1.id).length).toBe(2);
+ expect(notes.filter(n => n.parent_id === sectionB1.id).length).toBe(2);
+ expect(notes.filter(n => n.parent_id === sectionD1.id).length).toBe(1);
+ });
+});
diff --git a/packages/lib/services/interop/InteropService_Importer_OneNote.ts b/packages/lib/services/interop/InteropService_Importer_OneNote.ts
new file mode 100644
index 00000000000..9383443e211
--- /dev/null
+++ b/packages/lib/services/interop/InteropService_Importer_OneNote.ts
@@ -0,0 +1,70 @@
+import { ImportExportResult, ImportModuleOutputFormat } from './types';
+
+import InteropService_Importer_Base from './InteropService_Importer_Base';
+import { NoteEntity } from '../database/types';
+import { rtrimSlashes } from '../../path-utils';
+import { oneNoteConverter } from '@joplin/onenote-converter';
+import * as AdmZip from 'adm-zip';
+import InteropService_Importer_Md from './InteropService_Importer_Md';
+import { join, resolve } from 'path';
+import Logger from '@joplin/utils/Logger';
+import path = require('path');
+
+const logger = Logger.create('InteropService_Importer_OneNote');
+
+export default class InteropService_Importer_OneNote extends InteropService_Importer_Base {
+ protected importedNotes: Record = {};
+
+ private getEntryDirectory(unzippedPath: string, entryName: string) {
+ const withoutBasePath = entryName.replace(unzippedPath, '');
+ return path.normalize(withoutBasePath).split(path.sep)[0];
+ }
+
+ public async exec(result: ImportExportResult) {
+ const sourcePath = rtrimSlashes(this.sourcePath_);
+ const unzipTempDirectory = await this.temporaryDirectory_(true);
+ const zip = new AdmZip(sourcePath);
+ logger.info('Unzipping files...');
+ zip.extractAllTo(unzipTempDirectory, false);
+
+ const files = zip.getEntries();
+ if (files.length === 0) {
+ result.warnings.push('Zip file has no files.');
+ return result;
+ }
+
+ // files that don't have a name seems to be local only and shouldn't be processed
+
+ const tempOutputDirectory = await this.temporaryDirectory_(true);
+ const baseFolder = this.getEntryDirectory(unzipTempDirectory, files[0].entryName);
+ const notebookBaseDir = path.join(unzipTempDirectory, baseFolder, path.sep);
+ const outputDirectory2 = path.join(tempOutputDirectory, baseFolder);
+
+ const notebookFiles = zip.getEntries().filter(e => e.name !== '.onetoc2' && e.name !== 'OneNote_RecycleBin.onetoc2');
+
+ logger.info('Extracting OneNote to HTML');
+ for (const notebookFile of notebookFiles) {
+ const notebookFilePath = join(unzipTempDirectory, notebookFile.entryName);
+ try {
+ await oneNoteConverter(notebookFilePath, resolve(outputDirectory2), notebookBaseDir);
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ logger.info('Importing HTML into Joplin');
+ const importer = new InteropService_Importer_Md();
+ importer.setMetadata({ fileExtensions: ['html'] });
+ await importer.init(tempOutputDirectory, {
+ ...this.options_,
+ format: 'html',
+ outputFormat: ImportModuleOutputFormat.Html,
+
+ });
+ logger.info('Finished');
+ result = await importer.exec(result);
+
+ // remover temp directories?
+ return result;
+ }
+}
diff --git a/packages/lib/services/interop/__snapshots__/InteropService_Importer_OneNote.test.js.snap b/packages/lib/services/interop/__snapshots__/InteropService_Importer_OneNote.test.js.snap
new file mode 100644
index 00000000000..57761e914f3
--- /dev/null
+++ b/packages/lib/services/interop/__snapshots__/InteropService_Importer_OneNote.test.js.snap
@@ -0,0 +1,403 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`InteropService_Importer_OneNote should expect notes to be rendered the same: A page can have any width it wants 1`] = `
+"
+
+
+
+ A page can have any width it wants?
+
+
+
+
+
A page can have any width it wants?
+
quinta-feira, 25 de abril de 2024
+
15:01
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam eget convallis velit. Nullam at luctus libero. Phasellus id pharetra odio. Duis luctus lorem ut tellus imperdiet, a aliquet elit pretium. Donec sit amet urna et mi gravida cursus et id felis. Ut quis congue velit, eget mollis tortor. Vestibulum porttitor lobortis justo, in imperdiet leo porta id. Sed ornare ex nisi, sed laoreet nulla suscipit a. Cras nec lectus porta, fermentum quam ac, sagittis ipsum. Ut massa lacus, ornare in hendrerit sit amet, tempor quis ligula. Nulla facilisi. Maecenas quam dolor, lacinia id magna nec, blandit tincidunt ipsum. Proin placerat dui gravida, lacinia tortor eu, rhoncus ex. Pellentesque accumsan nunc id venenatis condimentum. Aenean sodales tortor id risus varius, id tincidunt libero tincidunt. Curabitur quis interdum metus.
+
+
This is another paragraph by the right side
+
+
+
+
+"
+`;
+
+exports[`InteropService_Importer_OneNote should expect notes to be rendered the same: A page with a lot of svgs 1`] = `
+"
+
+
+
+ A page with a lot of svgs
+
+
+
+
+
A page with a lot of svgs
+
quinta-feira, 25 de abril de 2024
+
10:40
+
This is a text paragraph that should apppear behind the drawings
+
+
+
+
+"
+`;
+
+exports[`InteropService_Importer_OneNote should expect notes to be rendered the same: A page with text and drawing above it 1`] = `
+"
+
+
+
+ A page with text and drawing above it
+
+
+
+
+
A page with text and drawing above it
+
quinta-feira, 25 de abril de 2024
+
02:48
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam eget convallis velit. Nullam at luctus libero. Phasellus id pharetra odio. Duis luctus lorem ut tellus imperdiet, a aliquet elit pretium. Donec sit amet urna et mi gravida cursus et id felis. Ut quis congue velit, eget mollis tortor. Vestibulum porttitor lobortis justo, in imperdiet leo porta id. Sed ornare ex nisi, sed laoreet nulla suscipit a. Cras nec lectus porta, fermentum quam ac, sagittis ipsum. Ut massa lacus, ornare in hendrerit sit amet, tempor quis ligula. Nulla facilisi. Maecenas quam dolor, lacinia id magna nec, blandit tincidunt ipsum. Proin placerat dui gravida, lacinia tortor eu, rhoncus ex. Pellentesque accumsan nunc id venenatis condimentum. Aenean sodales tortor id risus varius, id tincidunt libero tincidunt. Curabitur quis interdum metus.
+
Vestibulum sed sem nec nulla tincidunt maximus. Nam nulla sapien, vestibulum ac eros a, eleifend sollicitudin lectus. Praesent pellentesque pulvinar porttitor. Morbi rutrum, erat nec blandit commodo, nunc nulla venenatis massa, at viverra leo nisi eu nisl. Sed cursus quam a sem mattis suscipit. Duis gravida tellus ut nibh congue aliquam. Nulla velit orci, pretium sed hendrerit a, vulputate in lacus. Sed vitae ligula ex.
+
Nullam ut ullamcorper arcu, a porta lectus. Nulla suscipit lorem et nibh viverra eleifend. Pellentesque placerat fermentum ligula. Vivamus sit amet justo quis enim convallis condimentum. Ut non aliquet dui, vel vestibulum libero. In mauris ligula, pharetra eu maximus ut, ultrices ac justo. Donec varius condimentum augue eget tincidunt. Nunc eu egestas est.
+
Quisque scelerisque commodo maximus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed egestas et felis consectetur bibendum. Fusce interdum purus nec commodo porta. Praesent gravida efficitur rutrum. Phasellus semper erat urna, vitae hendrerit velit dictum et. Aenean eget dignissim tellus.
+
Proin ullamcorper quam quis justo maximus, eget elementum justo porttitor. Duis tellus leo, vestibulum vel felis sit amet, luctus vestibulum arcu. Nullam mauris quam, consequat eget varius nec, pellentesque quis ante. Fusce vitae sollicitudin orci. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vestibulum ac rutrum ipsum. Nulla in sapien lorem. Etiam sit amet leo eros. Vestibulum lacinia ipsum lobortis lacus congue, a posuere urna sollicitudin. Donec fermentum, ipsum nec tempor dictum, mi sapien sodales mi, eget tempor diam diam ut erat. Integer euismod sit amet tortor ut sollicitudin. Etiam at elit massa. Vivamus faucibus ipsum eget neque semper, at maximus lectus posuere. Nam metus orci, ultricies et lorem at, iaculis placerat diam.
+
Vestibulum massa magna, pulvinar id tempus vitae, egestas at eros. Maecenas sollicitudin tincidunt est eget accumsan. Ut ut hendrerit lectus. Pellentesque efficitur lacus in nulla posuere convallis. Ut vulputate erat id odio tincidunt, rhoncus eleifend metus vulputate. Phasellus blandit sem diam, at auctor diam consectetur a. Aliquam sit amet fermentum massa, id ultrices ligula. Aenean tincidunt quam risus, vel aliquet massa tristique at. In lectus nulla, dapibus quis vulputate eu, luctus vel nunc. Duis sollicitudin consequat dui, nec placerat dolor euismod ut. Quisque posuere leo nec accumsan posuere. Vestibulum tristique gravida justo egestas vestibulum. Nunc placerat semper erat vel egestas. Mauris massa sapien, sodales vitae fringilla vel, volutpat dapibus velit.
+
+
+
+
+
+"
+`;
+
+exports[`InteropService_Importer_OneNote should expect notes to be rendered the same: A simple filename 1`] = `
+"
+
+
+
+ A simple filename
+
+
+
+
+
A simple filename
+
Friday, April 19, 2024
+
5:46 PM
+
+
+
+
+
+"
+`;
+
+exports[`InteropService_Importer_OneNote should expect notes to be rendered the same: Page with more than one font size 1`] = `
+"
+
+
+
+ Page with more than one font size
+
+
+
+
+
Page with more than one font size
+
segunda-feira, 29 de abril de 2024
+
10:27
+
Suspendisse vitae odio nibh. Etiam fringilla mattis dapibus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Fusce vel ultricies ligula. Sed a nunc ante. Praesent suscipit fermentum magna. Aliquam convallis porttitor lacus ac posuere. Vestibulum maximus leo vel tortor condimentum, et tristique leo maximus. Nulla elementum, augue eu sollicitudin tempus, arcu ex lacinia enim, ut posuere lectus libero non eros. Vestibulum a libero leo. Donec id leo commodo, ornare ante ac, molestie tellus. Aenean a neque quis turpis euismod porta. Quisque vulputate augue vitae orci accumsan, a lobortis leo luctus. Nunc sodales sapien vitae lacus faucibus hendrerit. In ac lacinia diam.
+
Nam tempor urna eget posuere mollis. Aliquam erat volutpat. Sed ipsum massa, dictum eget sagittis id, fermentum a justo. Vivamus in iaculis libero. Pellentesque malesuada felis dictum turpis placerat, at ultrices justo viverra. Praesent nisi lectus, tincidunt ut tellus in, convallis euismod urna. Phasellus molestie porttitor odio vitae efficitur. Curabitur vulputate congue tincidunt. Fusce mattis orci at porttitor fermentum. Cras eu placerat odio. Fusce eu tortor sit amet massa pretium efficitur. Nam consequat, mauris at blandit placerat, est sapien feugiat felis, quis imperdiet sapien neque in justo. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Phasellus vestibulum rhoncus dolor, ut ullamcorper purus scelerisque eu. Integer sem felis, pellentesque in rutrum id, porta a ante.Vivamus finibus imperdiet massa, at interdum turpis rhoncus et. Phasellus leo nibh, mattis vel tortor at, gravida finibus felis. Donec bibendum enim euismod, dignissim ipsum eu, laoreet nisl. Ut auctor sollicitudin eros dictum gravida. Vestibulum pellentesque, ex quis vulputate efficitur, dolor metus efficitur nisl, id elementum mi nulla sit amet orci. Nam odio sem, bibendum at hendrerit finibus, vestibulum vitae dolor. In hac habitasse platea dictumst. Curabitur et ligula elit. Donec vulputate, diam non gravida efficitur, mi odio imperdiet ipsum, nec rhoncus mi nibh non magna.
+
Suspendisse varius enim vel odio congue sodales. Integer sit amet nisi sagittis, dapibus mi ut, tincidunt magna. Duis posuere est felis, et rhoncus magna volutpat a. Nullam tempor dignissim suscipit. Vestibulum cursus felis vitae libero pulvinar molestie. Donec at metus eget arcu blandit tincidunt. Donec purus felis, malesuada ac egestas eu, interdum sed erat. Praesent nec accumsan orci. Nunc bibendum rutrum erat, vel luctus odio. Pellentesque iaculis gravida arcu, eu consequat turpis congue sit amet. Interdum et malesuada fames ac ante ipsum primis in faucibus. Duis eget urna vel erat aliquet fringilla. Praesent vel luctus ligula, nec viverra nisl. Sed ac sem consectetur, sodales ante sodales, feugiat arcu.
+
It was a bright cold day in April, and the clocks were striking thirteen. Winston Smith, his chin nuzzled into his breast in an effort to escape the vile wind, slipped quickly through the glass doors of Victory Mansions, though not quickly enough to prevent a swirl of gritty dust from entering along with him. The hallway smelt of boiled cabbage and old rag mats. At one end of it a coloured poster, too large for indoor display, had been tacked to the wall. It depicted simply an enormous face, more than a metre wide: the face of a man of about forty-five, with a heavy black moustache and ruggedly handsome features. Winston made for the stairs. It was no use trying the lift. Even at the best of times it was seldom working, and at present the electric current was cut off during daylight hours. It was part of the economy drive in preparation for Hate Week. The flat was seven flights up, and Winston, who was thirty-nine and had a varicose ulcer above his right ankle, went slowly, resting several times on the way. On each landing, opposite the lift-shaft, the poster with the enormous face gazed from the wall. It was one of those pictures which are so contrived that the eyes follow you about when you move. BIG BROTHER IS WATCHING YOU, the caption beneath it ran. Inside the flat a fruity voice was reading out a list of figures which had something to do with the production of pig-iron. The voice came from an oblong metal plaque like a dulled mirror which formed part of the surface of the right-hand wall. Winston turned a switch and the voice sank somewhat, though the words were still distinguishable. The instrument (the telescreen, it was called) could be dimmed, but there was no way of shutting it off completely. He moved over to the window: a smallish, frail figure, the meagreness of his body merely emphasized by the blue overalls which were the uniform of the party. His hair was very fair, his face naturally sanguine, his skin roughened by coarse soap and blunt razor blades and the cold of the winter that had just ended.
+
+
+
+
+"
+`;
+
+exports[`InteropService_Importer_OneNote should expect notes to be rendered the same: Quick Notes 1`] = `
+"
+
+
+
+ Quick Notes
+
+
+
+
+
+
+
+
+
+
+
+"
+`;
+
+exports[`InteropService_Importer_OneNote should expect notes to be rendered the same: text 1`] = `
+"
+
+
+
+ text
+
+
+
+
+
text
+
quinta-feira, 25 de abril de 2024
+
15:39
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus egestas, enim vel tempor scelerisque, metus magna imperdiet purus, facilisis consequat mi augue eget risus. Vestibulum tincidunt lobortis turpis, id consectetur mauris consectetur et. Aliquam interdum ante ut lectus varius, ut mattis turpis tincidunt. Donec vitae sem sagittis, porta ex a, mattis tortor. Curabitur non velit suscipit, eleifend lectus quis, convallis velit. Nunc a blandit nisl. Cras rhoncus, enim a malesuada commodo, dolor dui pulvinar eros, non lobortis diam velit quis est. Donec pharetra, dolor a faucibus tincidunt, turpis dolor fermentum nibh, facilisis mollis neque urna a neque. Sed sit amet efficitur elit. Duis at elit non quam semper semper non quis mauris. Integer dignissim sodales urna, eu mattis urna viverra sit amet. In porta arcu id mauris hendrerit, in congue erat porta. In hac habitasse platea dictumst. Maecenas finibus sem pharetra blandit suscipit. Proin in nisl ac est pellentesque finibus. Cras ligula tellus, tempor eget leo in, vulputate interdum turpis.
+
Nam sit amet massa vehicula, elementum nisl feugiat, fermentum quam. Donec eros urna, ultrices vel fringilla suscipit, pretium non ligula. Sed sit amet pellentesque lorem, quis pharetra augue. Integer vitae sodales ex, luctus imperdiet arcu. Integer luctus urna eu urna ultricies ultricies. Aliquam sit amet maximus orci. Sed molestie vehicula vehicula. Morbi lacinia, dolor eu consectetur commodo, ipsum ante suscipit sem, eget facilisis nibh nisi venenatis magna. Donec ac risus ligula. In sit amet dapibus ante, sit amet pellentesque dolor. Nulla facilisi. Sed a nibh viverra, placerat purus at, rutrum justo. Fusce finibus consequat mattis. Sed felis tellus, consequat id nunc non, cursus tempus ligula. In hac habitasse platea dictumst. Praesent eget consectetur elit, ac mollis est.
+
Quisque facilisis justo diam, eget tincidunt augue lobortis non. Quisque rutrum diam sed diam feugiat, quis dictum ex bibendum. Nunc sagittis quam erat, sed pharetra nunc consequat a. Etiam in sollicitudin nunc. Aliquam non dolor laoreet ex egestas efficitur vel ut ligula. Duis mollis ornare laoreet. Nullam vitae velit feugiat leo bibendum faucibus. Morbi nisl nisl, sodales nec sodales vel, consequat in mi. Pellentesque bibendum erat iaculis dui volutpat ornare. Etiam ultricies tincidunt ipsum a congue. In at lacinia massa. Ut auctor id elit et pellentesque.
+
Donec ac condimentum dui, tincidunt rhoncus augue. Maecenas aliquam non nisl ac fringilla. Aliquam pulvinar enim sit amet accumsan tristique. Cras sapien ipsum, ultricies eu dui eget, efficitur ornare elit. Curabitur hendrerit mauris dolor, gravida elementum enim convallis quis. Vivamus varius luctus massa, in egestas mi egestas id. Nullam elementum scelerisque nisi sit amet pellentesque. In varius mollis risus, vel laoreet tortor. Pellentesque et blandit velit, nec auctor nunc. Aliquam quis purus vel ligula auctor rhoncus. Duis sed tempus metus. Praesent ac libero sed leo posuere feugiat id vel felis. Aenean commodo dapibus hendrerit. Sed eleifend, tortor sed placerat auctor, dolor dolor efficitur dolor, ut rhoncus eros libero sed ante. Mauris quis blandit sem. Nullam porta urna eros, at viverra sem iaculis in.
+
+
+
+
+
+"
+`;
+
+exports[`InteropService_Importer_OneNote should import a simple OneNote notebook: Page title 1`] = `
+"
+
+
+
+ Page title
+
+
+
+
+
Page title
+
Friday, May 3, 2024
+
6:30 PM
+
Page content
+
+
+
+
+"
+`;
diff --git a/packages/lib/shim-init-node.ts b/packages/lib/shim-init-node.ts
index eb5a1c26d99..f83b869d8ca 100644
--- a/packages/lib/shim-init-node.ts
+++ b/packages/lib/shim-init-node.ts
@@ -869,6 +869,12 @@ function shimInit(options: ShimInitOptions = null) {
const doc = await loadPdf(pdfPath);
return { pageCount: doc.numPages };
};
+
+ shim.getParentFolderName = (path: string) => {
+ return require('path').basename(
+ require('path').dirname(path),
+ );
+ };
}
module.exports = { shimInit, setupProxySettings };
diff --git a/packages/lib/shim.ts b/packages/lib/shim.ts
index 8de50090d4b..04af344346b 100644
--- a/packages/lib/shim.ts
+++ b/packages/lib/shim.ts
@@ -486,6 +486,9 @@ const shim = {
throw new Error('Not implemented');
},
+ getParentFolderName: (_path: string): string => {
+ throw new Error('Not implemented');
+ },
};
export default shim;
diff --git a/packages/onenote-converter/.gitignore b/packages/onenote-converter/.gitignore
new file mode 100644
index 00000000000..17200d6f75a
--- /dev/null
+++ b/packages/onenote-converter/.gitignore
@@ -0,0 +1,5 @@
+/target
+/output
+
+/.idea
+*.iml
diff --git a/packages/onenote-converter/.vscode/settings.json b/packages/onenote-converter/.vscode/settings.json
new file mode 100644
index 00000000000..352a6265a0d
--- /dev/null
+++ b/packages/onenote-converter/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+ "rust-analyzer.linkedProjects": [
+ "./Cargo.toml"
+ ]
+}
\ No newline at end of file
diff --git a/packages/onenote-converter/Cargo.lock b/packages/onenote-converter/Cargo.lock
new file mode 100644
index 00000000000..46b36e4f30f
--- /dev/null
+++ b/packages/onenote-converter/Cargo.lock
@@ -0,0 +1,1040 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7a2e47a1fbe209ee101dd6d61285226744c6c8d3c21c8dc878ba6cb9f467f3a"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "ansi_term"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "approx"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "arrayvec"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
+
+[[package]]
+name = "askama"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d298738b6e47e1034e560e5afe63aa488fea34e25ec11b855a76f0d7b8e73134"
+dependencies = [
+ "askama_derive",
+ "askama_escape",
+ "askama_shared",
+]
+
+[[package]]
+name = "askama_derive"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2925c4c290382f9d2fa3d1c1b6a63fa1427099721ecca4749b154cc9c25522"
+dependencies = [
+ "askama_shared",
+ "proc-macro2",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "askama_escape"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
+
+[[package]]
+name = "askama_shared"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d6083ccb191711e9c2b80b22ee24a8381a18524444914c746d4239e21d1afaf"
+dependencies = [
+ "askama_escape",
+ "humansize",
+ "nom",
+ "num-traits",
+ "percent-encoding",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "syn 1.0.109",
+ "toml",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
+
+[[package]]
+name = "backtrace"
+version = "0.3.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4717cfcbfaa661a0fd48f8453951837ae7e8f81e481fbb136e3202d72805a744"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitvec"
+version = "0.19.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33"
+dependencies = [
+ "funty",
+ "radium",
+ "tap",
+ "wyz",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
+[[package]]
+name = "bytes"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
+
+[[package]]
+name = "cc"
+version = "1.0.96"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "2.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
+dependencies = [
+ "ansi_term",
+ "atty",
+ "bitflags",
+ "strsim",
+ "textwrap",
+ "unicode-width",
+ "vec_map",
+]
+
+[[package]]
+name = "color-eyre"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f1885697ee8a177096d42f158922251a41973117f6d8a234cee94b9509157b7"
+dependencies = [
+ "backtrace",
+ "color-spantrace",
+ "eyre",
+ "indenter",
+ "once_cell",
+ "owo-colors",
+ "tracing-error",
+]
+
+[[package]]
+name = "color-spantrace"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6eee477a4a8a72f4addd4de416eb56d54bc307b284d6601bafdee1f4ea462d1"
+dependencies = [
+ "once_cell",
+ "owo-colors",
+ "tracing-core",
+ "tracing-error",
+]
+
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "either"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "enum-primitive-derive"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c375b9c5eadb68d0a6efee2999fef292f45854c3444c86f09d8ab086ba942b0e"
+dependencies = [
+ "num-traits",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "eyre"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
+dependencies = [
+ "indenter",
+ "once_cell",
+]
+
+[[package]]
+name = "funty"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
+
+[[package]]
+name = "getrandom"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "gimli"
+version = "0.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189"
+
+[[package]]
+name = "heck"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "humansize"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026"
+
+[[package]]
+name = "indenter"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "lexical-core"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
+dependencies = [
+ "arrayvec",
+ "bitflags",
+ "cfg-if",
+ "ryu",
+ "static_assertions",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.154"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "memchr"
+version = "2.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "mime_guess"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
+dependencies = [
+ "mime",
+ "unicase",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
+dependencies = [
+ "adler",
+ "autocfg",
+]
+
+[[package]]
+name = "nom"
+version = "6.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6a7a9657c84d5814c6196b68bb4429df09c18b1573806259fba397ea4ad0d44"
+dependencies = [
+ "bitvec",
+ "funty",
+ "lexical-core",
+ "memchr",
+ "version_check",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "object"
+version = "0.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170"
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "onenote-converter"
+version = "0.0.1"
+dependencies = [
+ "askama",
+ "bytes",
+ "color-eyre",
+ "console_error_panic_hook",
+ "encoding_rs",
+ "enum-primitive-derive",
+ "itertools",
+ "log",
+ "mime_guess",
+ "num-traits",
+ "once_cell",
+ "palette",
+ "paste",
+ "percent-encoding",
+ "regex",
+ "sanitize-filename",
+ "structopt",
+ "thiserror",
+ "uuid",
+ "wasm-bindgen",
+ "web-sys",
+ "widestring",
+]
+
+[[package]]
+name = "owo-colors"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55"
+
+[[package]]
+name = "palette"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a05c0334468e62a4dfbda34b29110aa7d70d58c7fdb2c9857b5874dd9827cc59"
+dependencies = [
+ "approx",
+ "num-traits",
+ "palette_derive",
+ "phf",
+ "phf_codegen",
+]
+
+[[package]]
+name = "palette_derive"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b4b5f600e60dd3a147fb57b4547033d382d1979eb087af310e91cb45a63b1f4"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "phf"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
+dependencies = [
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
+dependencies = [
+ "phf_shared",
+ "rand",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "radium"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
+
+[[package]]
+name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+dependencies = [
+ "getrandom",
+ "libc",
+ "rand_chacha",
+ "rand_core",
+ "rand_hc",
+ "rand_pcg",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "rand_pcg"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "regex"
+version = "1.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "ryu"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+
+[[package]]
+name = "sanitize-filename"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf18934a12018228c5b55a6dae9df5d0641e3566b3630cb46cc55564068e7c2f"
+dependencies = [
+ "lazy_static",
+ "regex",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.200"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.200"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "siphasher"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strsim"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+
+[[package]]
+name = "structopt"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
+dependencies = [
+ "clap",
+ "lazy_static",
+ "structopt-derive",
+]
+
+[[package]]
+name = "structopt-derive"
+version = "0.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tap"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
+
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-error"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4d7c0b83d4a500748fa5879461652b361edf5c9d51ede2a2ac03875ca185e24"
+dependencies = [
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.2.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71"
+dependencies = [
+ "sharded-slab",
+ "thread_local",
+ "tracing-core",
+]
+
+[[package]]
+name = "unicase"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
+
+[[package]]
+name = "uuid"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
+
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+
+[[package]]
+name = "web-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "widestring"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "wyz"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
diff --git a/packages/onenote-converter/Cargo.toml b/packages/onenote-converter/Cargo.toml
new file mode 100644
index 00000000000..4331c3e91ae
--- /dev/null
+++ b/packages/onenote-converter/Cargo.toml
@@ -0,0 +1,41 @@
+[package]
+name = "onenote-converter"
+version = "0.0.1"
+authors = ["Pedro Luiz "]
+edition = "2018"
+description = "Convert Microsoft OneNote® notebooks to HTML"
+license = "MIT"
+repository = "https://github.com/laurent22/joplin"
+keywords = ["onenote"]
+
+[dependencies]
+askama = "0.10"
+color-eyre = "0.5"
+log = "0.4.11"
+mime_guess = "2.0.3"
+once_cell = "1.4.1"
+palette = "0.5.0"
+percent-encoding = "2.1.0"
+regex = "1"
+sanitize-filename = "0.3.0"
+structopt = "0.3"
+console_error_panic_hook = "0.1.7"
+bytes = "1.2.0"
+encoding_rs = "0.8.31"
+enum-primitive-derive = "0.2.2"
+itertools = "0.10.3"
+num-traits = "0.2"
+paste = "1.0"
+thiserror = "1.0"
+uuid = "1.1.2"
+widestring = "1.0.2"
+wasm-bindgen = "0.2"
+
+[dependencies.web-sys]
+version = "0.3"
+features = [
+ "console"
+]
+
+[lib]
+crate-type = ["cdylib"]
\ No newline at end of file
diff --git a/packages/onenote-converter/README.md b/packages/onenote-converter/README.md
new file mode 100644
index 00000000000..ce0bb2ed6ca
--- /dev/null
+++ b/packages/onenote-converter/README.md
@@ -0,0 +1,59 @@
+# OneNote Converter
+
+This package is used to process OneNote backup files and output HTML that Joplin can import.
+
+The code is based on the projects created by https://github.com/msiemens
+
+We adapted it to target WebAssembly, adding Node.js functions that could interface with the host machine. For that to
+happen we are using custom-made functions (see `node_functions.js`) and the Node.js standard library (see
+`src/utils.rs`).
+
+
+### Project structure:
+
+```
+- onenote-converter
+ - package.json -> file to store scripts on how to build the project
+ - node_functions.js -> where the custom-made functions used inside rust goes
+ ...
+ - pkg -> artifact folder generated by wasm-pack
+ - package.json -> library that gets used by the Joplin Project
+ ...
+ - src
+ - lib.rs -> starting point
+```
+
+### How to develop:
+
+To work with the code you will need:
+
+- Rust https://www.rust-lang.org/learn/get-started
+- wasm-pack https://rustwasm.github.io/wasm-pack/
+
+`wasm-pack` is the tool used to compile the Rust code to WebAssembly.
+
+To build, inside the `onenote-converter/package.json` I added two scripts to build it to release and dev.
+For the dev build, when using the code, it might print out a lot of logs, but they can be disabled in the macro `log!()`
+
+During development, it will be easier to test it where this library is called.
+`InteropService_Importer_Onenote.ts` is the code that depends on this and already has some tests
+
+
+### Publishing release
+
+`onenote-converter/package.json` has a `buildProduction` script
+
+
+### Security concerns
+
+We are using WebAssembly with Node.js calls to the file system, reading and writing files and directories, which means
+it is not isolated (no more than Node.js is, for that matter). But since we opted for not requiring every dev to install
+Rust and wasm-pack just to compile this library, we need to acknowledge that depending on how the `.wasm` binary is
+added to the project it might be a security concern.
+
+While the review process is always important, if the binary is added to git by a malicious user he might be able
+to include anything he wants inside the .wasm file, so that is why adding a CI step to generate the code is very
+important.
+
+Our idea is to generate the WebAssembly package inside the CI so we can be sure that the code that was generated is
+the one that was accepted in the PR. Hopefully, this will be already implemented before this is merged.
diff --git a/packages/onenote-converter/askama.toml b/packages/onenote-converter/askama.toml
new file mode 100644
index 00000000000..50559d54831
--- /dev/null
+++ b/packages/onenote-converter/askama.toml
@@ -0,0 +1,2 @@
+[general]
+dirs = ["src/templates"]
\ No newline at end of file
diff --git a/packages/onenote-converter/assets/icons/License b/packages/onenote-converter/assets/icons/License
new file mode 100644
index 00000000000..261eeb9e9f8
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/License
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/packages/onenote-converter/assets/icons/arrow-right-line.svg b/packages/onenote-converter/assets/icons/arrow-right-line.svg
new file mode 100755
index 00000000000..f46779f7337
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/arrow-right-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/award-line.svg b/packages/onenote-converter/assets/icons/award-line.svg
new file mode 100755
index 00000000000..25849f302b4
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/award-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/book-open-line.svg b/packages/onenote-converter/assets/icons/book-open-line.svg
new file mode 100755
index 00000000000..cbcbbfb9596
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/book-open-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/chat-4-line.svg b/packages/onenote-converter/assets/icons/chat-4-line.svg
new file mode 100755
index 00000000000..c94a0c60efb
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/chat-4-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/check-line.svg b/packages/onenote-converter/assets/icons/check-line.svg
new file mode 100755
index 00000000000..a28368fca94
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/check-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/checkbox-blank-circle-fill.svg b/packages/onenote-converter/assets/icons/checkbox-blank-circle-fill.svg
new file mode 100755
index 00000000000..5f7ebd1d99c
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/checkbox-blank-circle-fill.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/checkbox-blank-circle-line.svg b/packages/onenote-converter/assets/icons/checkbox-blank-circle-line.svg
new file mode 100755
index 00000000000..9e627e944be
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/checkbox-blank-circle-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/checkbox-blank-fill.svg b/packages/onenote-converter/assets/icons/checkbox-blank-fill.svg
new file mode 100755
index 00000000000..30364974fff
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/checkbox-blank-fill.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/checkbox-blank-line.svg b/packages/onenote-converter/assets/icons/checkbox-blank-line.svg
new file mode 100755
index 00000000000..c56cacf082b
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/checkbox-blank-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/checkbox-fill.svg b/packages/onenote-converter/assets/icons/checkbox-fill.svg
new file mode 100755
index 00000000000..5c439dbf306
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/checkbox-fill.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/contacts-line.svg b/packages/onenote-converter/assets/icons/contacts-line.svg
new file mode 100755
index 00000000000..6856442df01
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/contacts-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/error-warning-line.svg b/packages/onenote-converter/assets/icons/error-warning-line.svg
new file mode 100755
index 00000000000..1df56a6c0bd
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/error-warning-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/file-list-2-line.svg b/packages/onenote-converter/assets/icons/file-list-2-line.svg
new file mode 100755
index 00000000000..0242aa78408
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/file-list-2-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/film-line.svg b/packages/onenote-converter/assets/icons/film-line.svg
new file mode 100755
index 00000000000..868b3f3fbb6
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/film-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/flag-fill.svg b/packages/onenote-converter/assets/icons/flag-fill.svg
new file mode 100755
index 00000000000..0a1f84f6853
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/flag-fill.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/home-4-line.svg b/packages/onenote-converter/assets/icons/home-4-line.svg
new file mode 100755
index 00000000000..a799b6ace49
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/home-4-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/lightbulb-line.svg b/packages/onenote-converter/assets/icons/lightbulb-line.svg
new file mode 100755
index 00000000000..fe10f634ca7
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/lightbulb-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/link.svg b/packages/onenote-converter/assets/icons/link.svg
new file mode 100755
index 00000000000..3b7c8e0697e
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/link.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/lock-line.svg b/packages/onenote-converter/assets/icons/lock-line.svg
new file mode 100755
index 00000000000..c9448583773
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/lock-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/mark-pen-line.svg b/packages/onenote-converter/assets/icons/mark-pen-line.svg
new file mode 100755
index 00000000000..7f742b4a081
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/mark-pen-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/music-fill.svg b/packages/onenote-converter/assets/icons/music-fill.svg
new file mode 100755
index 00000000000..f0d21411eb9
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/music-fill.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/phone-line.svg b/packages/onenote-converter/assets/icons/phone-line.svg
new file mode 100755
index 00000000000..2719ef9765d
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/phone-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/question-mark.svg b/packages/onenote-converter/assets/icons/question-mark.svg
new file mode 100755
index 00000000000..487fcd68470
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/question-mark.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/send-plane-2-line.svg b/packages/onenote-converter/assets/icons/send-plane-2-line.svg
new file mode 100755
index 00000000000..fabf7f23ad1
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/send-plane-2-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/assets/icons/star-fill.svg b/packages/onenote-converter/assets/icons/star-fill.svg
new file mode 100644
index 00000000000..2d59353112d
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/star-fill.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/onenote-converter/assets/icons/user-line.svg b/packages/onenote-converter/assets/icons/user-line.svg
new file mode 100755
index 00000000000..9e64bb5632c
--- /dev/null
+++ b/packages/onenote-converter/assets/icons/user-line.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/onenote-converter/build.js b/packages/onenote-converter/build.js
new file mode 100644
index 00000000000..6bb08040937
--- /dev/null
+++ b/packages/onenote-converter/build.js
@@ -0,0 +1,8 @@
+const { execSync } = require('child_process');
+
+const build = () => {
+ const profile = process.env.NODE_ENV === 'production' ? '--release' : '--debug';
+ return execSync(`wasm-pack build --target nodejs ${profile}`);
+};
+
+build();
diff --git a/packages/onenote-converter/deny.toml b/packages/onenote-converter/deny.toml
new file mode 100644
index 00000000000..40061b126d5
--- /dev/null
+++ b/packages/onenote-converter/deny.toml
@@ -0,0 +1,22 @@
+[advisories]
+vulnerability = "deny"
+unmaintained = "warn"
+yanked = "warn"
+notice = "deny"
+
+[licenses]
+unlicensed = "deny"
+allow-osi-fsf-free = "either"
+copyleft = "allow"
+default = "deny"
+
+[bans]
+multiple-versions = "deny"
+wildcards = "warn"
+skip = [
+ { name = "cfg-if" },
+]
+
+[sources]
+unknown-registry = "deny"
+unknown-git = "deny"
diff --git a/packages/onenote-converter/node_functions.js b/packages/onenote-converter/node_functions.js
new file mode 100644
index 00000000000..b0bb47914c7
--- /dev/null
+++ b/packages/onenote-converter/node_functions.js
@@ -0,0 +1,49 @@
+
+const fs = require('node:fs');
+const path = require('node:path');
+
+function mkdirSyncRecursive(filepath) {
+ if (!fs.existsSync(filepath)) {
+ mkdirSyncRecursive(filepath.substring(0, filepath.lastIndexOf(path.sep)));
+ fs.mkdirSync(filepath);
+ }
+}
+
+function isDirectory(filepath) {
+ if (!fs.existsSync(filepath)) return false;
+ return fs.lstatSync(filepath).isDirectory();
+}
+
+function readDir(filepath) {
+ const dirContents = fs.readdirSync(filepath, { withFileTypes: true });
+ return dirContents.map(entry => filepath + path.sep + entry.name).join('\n');
+}
+
+function removePrefix(basePath, prefix) {
+ return basePath.replace(prefix, '');
+}
+
+function getOutputPath(inputDir, outputDir, filePath) {
+ const basePathFromInputFolder = filePath.replace(inputDir, '');
+ const newOutput = path.join(outputDir, basePathFromInputFolder);
+ return path.dirname(newOutput);
+}
+
+function getParentDir(filePath) {
+ return path.basename(path.dirname(filePath));
+}
+
+function normalizeAndWriteFile(filePath, data) {
+ filePath = path.normalize(filePath);
+ fs.writeFileSync(filePath, data);
+}
+
+module.exports = {
+ mkdirSyncRecursive,
+ isDirectory,
+ readDir,
+ removePrefix,
+ getOutputPath,
+ getParentDir,
+ normalizeAndWriteFile,
+};
diff --git a/packages/onenote-converter/package.json b/packages/onenote-converter/package.json
new file mode 100644
index 00000000000..2079463b636
--- /dev/null
+++ b/packages/onenote-converter/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "onenote-builder",
+ "collaborators": [
+ "Pedro Luiz "
+ ],
+ "description": "This package file only exists to build the @joplin/onenote-converter",
+ "version": "0.0.1",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/laurent22/joplin"
+ },
+ "scripts": {
+ "buildProduction": "wasm-pack build --release --target nodejs --scope joplin",
+ "buildDev": "wasm-pack build --dev --target nodejs --scope joplin"
+ },
+ "devDependencies": {
+ "wasm-pack": "0.12.1"
+ }
+}
diff --git a/packages/onenote-converter/pkg/onenote_converter.d.ts b/packages/onenote-converter/pkg/onenote_converter.d.ts
new file mode 100644
index 00000000000..62a45046af1
--- /dev/null
+++ b/packages/onenote-converter/pkg/onenote_converter.d.ts
@@ -0,0 +1,8 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+* @param {string} input
+* @param {string} output
+* @param {string} base_path
+*/
+export function oneNoteConverter(input: string, output: string, base_path: string): void;
diff --git a/packages/onenote-converter/pkg/onenote_converter.js b/packages/onenote-converter/pkg/onenote_converter.js
new file mode 100644
index 00000000000..344e07ff5e4
--- /dev/null
+++ b/packages/onenote-converter/pkg/onenote_converter.js
@@ -0,0 +1,372 @@
+let imports = {};
+imports['__wbindgen_placeholder__'] = module.exports;
+let wasm;
+const { isDirectory, readDir, mkdirSyncRecursive, removePrefix, getOutputPath, getParentDir, normalizeAndWriteFile } = require(String.raw`./snippets/onenote-converter-6981a13478d338f0/node_functions.js`);
+const { readFileSync, existsSync } = require(`fs`);
+const { basename, extname, dirname, join } = require(`path`);
+const { TextEncoder, TextDecoder } = require(`util`);
+
+const heap = new Array(128).fill(undefined);
+
+heap.push(undefined, null, true, false);
+
+function getObject(idx) { return heap[idx]; }
+
+let WASM_VECTOR_LEN = 0;
+
+let cachedUint8Memory0 = null;
+
+function getUint8Memory0() {
+ if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) {
+ cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer);
+ }
+ return cachedUint8Memory0;
+}
+
+let cachedTextEncoder = new TextEncoder('utf-8');
+
+const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
+ ? function (arg, view) {
+ return cachedTextEncoder.encodeInto(arg, view);
+}
+ : function (arg, view) {
+ const buf = cachedTextEncoder.encode(arg);
+ view.set(buf);
+ return {
+ read: arg.length,
+ written: buf.length
+ };
+});
+
+function passStringToWasm0(arg, malloc, realloc) {
+
+ if (realloc === undefined) {
+ const buf = cachedTextEncoder.encode(arg);
+ const ptr = malloc(buf.length, 1) >>> 0;
+ getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
+ WASM_VECTOR_LEN = buf.length;
+ return ptr;
+ }
+
+ let len = arg.length;
+ let ptr = malloc(len, 1) >>> 0;
+
+ const mem = getUint8Memory0();
+
+ let offset = 0;
+
+ for (; offset < len; offset++) {
+ const code = arg.charCodeAt(offset);
+ if (code > 0x7F) break;
+ mem[ptr + offset] = code;
+ }
+
+ if (offset !== len) {
+ if (offset !== 0) {
+ arg = arg.slice(offset);
+ }
+ ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
+ const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
+ const ret = encodeString(arg, view);
+
+ offset += ret.written;
+ ptr = realloc(ptr, len, offset, 1) >>> 0;
+ }
+
+ WASM_VECTOR_LEN = offset;
+ return ptr;
+}
+
+function isLikeNone(x) {
+ return x === undefined || x === null;
+}
+
+let cachedInt32Memory0 = null;
+
+function getInt32Memory0() {
+ if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) {
+ cachedInt32Memory0 = new Int32Array(wasm.memory.buffer);
+ }
+ return cachedInt32Memory0;
+}
+
+let heap_next = heap.length;
+
+function dropObject(idx) {
+ if (idx < 132) return;
+ heap[idx] = heap_next;
+ heap_next = idx;
+}
+
+function takeObject(idx) {
+ const ret = getObject(idx);
+ dropObject(idx);
+ return ret;
+}
+
+let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
+
+cachedTextDecoder.decode();
+
+function getStringFromWasm0(ptr, len) {
+ ptr = ptr >>> 0;
+ return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
+}
+
+function addHeapObject(obj) {
+ if (heap_next === heap.length) heap.push(heap.length + 1);
+ const idx = heap_next;
+ heap_next = heap[idx];
+
+ heap[idx] = obj;
+ return idx;
+}
+
+function debugString(val) {
+ // primitive types
+ const type = typeof val;
+ if (type == 'number' || type == 'boolean' || val == null) {
+ return `${val}`;
+ }
+ if (type == 'string') {
+ return `"${val}"`;
+ }
+ if (type == 'symbol') {
+ const description = val.description;
+ if (description == null) {
+ return 'Symbol';
+ } else {
+ return `Symbol(${description})`;
+ }
+ }
+ if (type == 'function') {
+ const name = val.name;
+ if (typeof name == 'string' && name.length > 0) {
+ return `Function(${name})`;
+ } else {
+ return 'Function';
+ }
+ }
+ // objects
+ if (Array.isArray(val)) {
+ const length = val.length;
+ let debug = '[';
+ if (length > 0) {
+ debug += debugString(val[0]);
+ }
+ for(let i = 1; i < length; i++) {
+ debug += ', ' + debugString(val[i]);
+ }
+ debug += ']';
+ return debug;
+ }
+ // Test for built-in
+ const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
+ let className;
+ if (builtInMatches.length > 1) {
+ className = builtInMatches[1];
+ } else {
+ // Failed to match the standard '[object ClassName]'
+ return toString.call(val);
+ }
+ if (className == 'Object') {
+ // we're a user defined class or Object
+ // JSON.stringify avoids problems with cycles, and is generally much
+ // easier than looping through ownProperties of `val`.
+ try {
+ return 'Object(' + JSON.stringify(val) + ')';
+ } catch (_) {
+ return 'Object';
+ }
+ }
+ // errors
+ if (val instanceof Error) {
+ return `${val.name}: ${val.message}\n${val.stack}`;
+ }
+ // TODO we could test for more things here, like `Set`s and `Map`s.
+ return className;
+}
+
+function handleError(f, args) {
+ try {
+ return f.apply(this, args);
+ } catch (e) {
+ wasm.__wbindgen_exn_store(addHeapObject(e));
+ }
+}
+
+function getArrayU8FromWasm0(ptr, len) {
+ ptr = ptr >>> 0;
+ return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
+}
+/**
+* @param {string} input
+* @param {string} output
+* @param {string} base_path
+*/
+module.exports.oneNoteConverter = function(input, output, base_path) {
+ const ptr0 = passStringToWasm0(input, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(output, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ const ptr2 = passStringToWasm0(base_path, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len2 = WASM_VECTOR_LEN;
+ wasm.oneNoteConverter(ptr0, len0, ptr1, len1, ptr2, len2);
+};
+
+module.exports.__wbg_join_13fcc6aa248ce243 = function() { return handleError(function (arg0, arg1, arg2, arg3) {
+ const ret = join(getStringFromWasm0(arg0, arg1), getStringFromWasm0(arg2, arg3));
+ return addHeapObject(ret);
+}, arguments) };
+
+module.exports.__wbindgen_string_get = function(arg0, arg1) {
+ const obj = getObject(arg1);
+ const ret = typeof(obj) === 'string' ? obj : undefined;
+ var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ var len1 = WASM_VECTOR_LEN;
+ getInt32Memory0()[arg0 / 4 + 1] = len1;
+ getInt32Memory0()[arg0 / 4 + 0] = ptr1;
+};
+
+module.exports.__wbindgen_object_drop_ref = function(arg0) {
+ takeObject(arg0);
+};
+
+module.exports.__wbg_existsSync_fef3d1ee40dd0bc2 = function() { return handleError(function (arg0, arg1) {
+ const ret = existsSync(getStringFromWasm0(arg0, arg1));
+ return ret;
+}, arguments) };
+
+module.exports.__wbg_isDirectory_8e0fb33be0dac663 = function() { return handleError(function (arg0, arg1) {
+ const ret = isDirectory(getStringFromWasm0(arg0, arg1));
+ return ret;
+}, arguments) };
+
+module.exports.__wbg_normalizeAndWriteFile_ec3f9624dc064f6f = function() { return handleError(function (arg0, arg1, arg2, arg3) {
+ const ret = normalizeAndWriteFile(getStringFromWasm0(arg0, arg1), getArrayU8FromWasm0(arg2, arg3));
+ return addHeapObject(ret);
+}, arguments) };
+
+module.exports.__wbg_readDir_f1ea9d82729fd652 = function() { return handleError(function (arg0, arg1) {
+ const ret = readDir(getStringFromWasm0(arg0, arg1));
+ return addHeapObject(ret);
+}, arguments) };
+
+module.exports.__wbg_readFileSync_dce2cb2a612e5268 = function() { return handleError(function (arg0, arg1) {
+ const ret = readFileSync(getStringFromWasm0(arg0, arg1));
+ return addHeapObject(ret);
+}, arguments) };
+
+module.exports.__wbg_mkdirSyncRecursive_b92f184dee879e44 = function() { return handleError(function (arg0, arg1) {
+ const ret = mkdirSyncRecursive(getStringFromWasm0(arg0, arg1));
+ return addHeapObject(ret);
+}, arguments) };
+
+module.exports.__wbg_removePrefix_7a999edd559fd0e6 = function() { return handleError(function (arg0, arg1, arg2, arg3) {
+ const ret = removePrefix(getStringFromWasm0(arg0, arg1), getStringFromWasm0(arg2, arg3));
+ return addHeapObject(ret);
+}, arguments) };
+
+module.exports.__wbg_getOutputPath_c8878b16ae903260 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5) {
+ const ret = getOutputPath(getStringFromWasm0(arg0, arg1), getStringFromWasm0(arg2, arg3), getStringFromWasm0(arg4, arg5));
+ return addHeapObject(ret);
+}, arguments) };
+
+module.exports.__wbg_getParentDir_3ed7ef3bfa04fc4c = function() { return handleError(function (arg0, arg1) {
+ const ret = getParentDir(getStringFromWasm0(arg0, arg1));
+ return addHeapObject(ret);
+}, arguments) };
+
+module.exports.__wbg_basename_8509b1ba32f9422a = function() { return handleError(function (arg0, arg1) {
+ const ret = basename(getStringFromWasm0(arg0, arg1));
+ return addHeapObject(ret);
+}, arguments) };
+
+module.exports.__wbg_extname_0c314acf648141d7 = function() { return handleError(function (arg0, arg1) {
+ const ret = extname(getStringFromWasm0(arg0, arg1));
+ return addHeapObject(ret);
+}, arguments) };
+
+module.exports.__wbg_dirname_a04a74019d90c213 = function() { return handleError(function (arg0, arg1) {
+ const ret = dirname(getStringFromWasm0(arg0, arg1));
+ return addHeapObject(ret);
+}, arguments) };
+
+module.exports.__wbindgen_string_new = function(arg0, arg1) {
+ const ret = getStringFromWasm0(arg0, arg1);
+ return addHeapObject(ret);
+};
+
+module.exports.__wbg_warn_1982e858bdcc0c42 = function(arg0, arg1) {
+ console.warn(getObject(arg0), getObject(arg1));
+};
+
+module.exports.__wbg_buffer_12d079cc21e14bdb = function(arg0) {
+ const ret = getObject(arg0).buffer;
+ return addHeapObject(ret);
+};
+
+module.exports.__wbg_new_63b92bc8671ed464 = function(arg0) {
+ const ret = new Uint8Array(getObject(arg0));
+ return addHeapObject(ret);
+};
+
+module.exports.__wbg_set_a47bac70306a19a7 = function(arg0, arg1, arg2) {
+ getObject(arg0).set(getObject(arg1), arg2 >>> 0);
+};
+
+module.exports.__wbg_length_c20a40f15020d68a = function(arg0) {
+ const ret = getObject(arg0).length;
+ return ret;
+};
+
+module.exports.__wbg_new_abda76e883ba8a5f = function() {
+ const ret = new Error();
+ return addHeapObject(ret);
+};
+
+module.exports.__wbg_stack_658279fe44541cf6 = function(arg0, arg1) {
+ const ret = getObject(arg1).stack;
+ const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ getInt32Memory0()[arg0 / 4 + 1] = len1;
+ getInt32Memory0()[arg0 / 4 + 0] = ptr1;
+};
+
+module.exports.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) {
+ let deferred0_0;
+ let deferred0_1;
+ try {
+ deferred0_0 = arg0;
+ deferred0_1 = arg1;
+ console.error(getStringFromWasm0(arg0, arg1));
+ } finally {
+ wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
+ }
+};
+
+module.exports.__wbindgen_debug_string = function(arg0, arg1) {
+ const ret = debugString(getObject(arg1));
+ const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ getInt32Memory0()[arg0 / 4 + 1] = len1;
+ getInt32Memory0()[arg0 / 4 + 0] = ptr1;
+};
+
+module.exports.__wbindgen_throw = function(arg0, arg1) {
+ throw new Error(getStringFromWasm0(arg0, arg1));
+};
+
+module.exports.__wbindgen_memory = function() {
+ const ret = wasm.memory;
+ return addHeapObject(ret);
+};
+
+const path = require('path').join(__dirname, 'onenote_converter_bg.wasm');
+const bytes = require('fs').readFileSync(path);
+
+const wasmModule = new WebAssembly.Module(bytes);
+const wasmInstance = new WebAssembly.Instance(wasmModule, imports);
+wasm = wasmInstance.exports;
+module.exports.__wasm = wasm;
+
diff --git a/packages/onenote-converter/pkg/onenote_converter_bg.wasm b/packages/onenote-converter/pkg/onenote_converter_bg.wasm
new file mode 100644
index 00000000000..f08b2b6992b
Binary files /dev/null and b/packages/onenote-converter/pkg/onenote_converter_bg.wasm differ
diff --git a/packages/onenote-converter/pkg/onenote_converter_bg.wasm.d.ts b/packages/onenote-converter/pkg/onenote_converter_bg.wasm.d.ts
new file mode 100644
index 00000000000..e196cea26af
--- /dev/null
+++ b/packages/onenote-converter/pkg/onenote_converter_bg.wasm.d.ts
@@ -0,0 +1,8 @@
+/* tslint:disable */
+/* eslint-disable */
+export const memory: WebAssembly.Memory;
+export function oneNoteConverter(a: number, b: number, c: number, d: number, e: number, f: number): void;
+export function __wbindgen_malloc(a: number, b: number): number;
+export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number;
+export function __wbindgen_exn_store(a: number): void;
+export function __wbindgen_free(a: number, b: number, c: number): void;
diff --git a/packages/onenote-converter/pkg/package.json b/packages/onenote-converter/pkg/package.json
new file mode 100644
index 00000000000..9df61acf0ce
--- /dev/null
+++ b/packages/onenote-converter/pkg/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "@joplin/onenote-converter",
+ "collaborators": [
+ "Pedro Luiz "
+ ],
+ "description": "Convert Microsoft OneNote® notebooks to HTML",
+ "version": "0.0.1",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/laurent22/joplin"
+ },
+ "files": [
+ "onenote_converter_bg.wasm",
+ "onenote_converter.js",
+ "onenote_converter.d.ts"
+ ],
+ "main": "onenote_converter.js",
+ "types": "onenote_converter.d.ts",
+ "keywords": [
+ "onenote"
+ ]
+}
\ No newline at end of file
diff --git a/packages/onenote-converter/pkg/snippets/onenote-converter-6981a13478d338f0/node_functions.js b/packages/onenote-converter/pkg/snippets/onenote-converter-6981a13478d338f0/node_functions.js
new file mode 100644
index 00000000000..b0bb47914c7
--- /dev/null
+++ b/packages/onenote-converter/pkg/snippets/onenote-converter-6981a13478d338f0/node_functions.js
@@ -0,0 +1,49 @@
+
+const fs = require('node:fs');
+const path = require('node:path');
+
+function mkdirSyncRecursive(filepath) {
+ if (!fs.existsSync(filepath)) {
+ mkdirSyncRecursive(filepath.substring(0, filepath.lastIndexOf(path.sep)));
+ fs.mkdirSync(filepath);
+ }
+}
+
+function isDirectory(filepath) {
+ if (!fs.existsSync(filepath)) return false;
+ return fs.lstatSync(filepath).isDirectory();
+}
+
+function readDir(filepath) {
+ const dirContents = fs.readdirSync(filepath, { withFileTypes: true });
+ return dirContents.map(entry => filepath + path.sep + entry.name).join('\n');
+}
+
+function removePrefix(basePath, prefix) {
+ return basePath.replace(prefix, '');
+}
+
+function getOutputPath(inputDir, outputDir, filePath) {
+ const basePathFromInputFolder = filePath.replace(inputDir, '');
+ const newOutput = path.join(outputDir, basePathFromInputFolder);
+ return path.dirname(newOutput);
+}
+
+function getParentDir(filePath) {
+ return path.basename(path.dirname(filePath));
+}
+
+function normalizeAndWriteFile(filePath, data) {
+ filePath = path.normalize(filePath);
+ fs.writeFileSync(filePath, data);
+}
+
+module.exports = {
+ mkdirSyncRecursive,
+ isDirectory,
+ readDir,
+ removePrefix,
+ getOutputPath,
+ getParentDir,
+ normalizeAndWriteFile,
+};
diff --git a/packages/onenote-converter/src/lib.rs b/packages/onenote-converter/src/lib.rs
new file mode 100644
index 00000000000..162b2520206
--- /dev/null
+++ b/packages/onenote-converter/src/lib.rs
@@ -0,0 +1,86 @@
+pub use crate::parser::Parser;
+use color_eyre::eyre::eyre;
+use color_eyre::eyre::Result;
+use std::panic;
+use wasm_bindgen::prelude::wasm_bindgen;
+
+use crate::utils::utils::{log, log_warn};
+use crate::utils::{get_file_extension, get_file_name, get_output_path, get_parent_dir};
+
+mod notebook;
+mod page;
+mod parser;
+mod section;
+mod templates;
+mod utils;
+
+extern crate console_error_panic_hook;
+extern crate web_sys;
+
+#[wasm_bindgen]
+pub fn oneNoteConverter(input: &str, output: &str, base_path: &str) {
+ panic::set_hook(Box::new(console_error_panic_hook::hook));
+
+ if let Err(e) = _main(input, output, base_path) {
+ log_warn!("{:?}", e);
+ }
+}
+
+fn _main(input_path: &str, output_dir: &str, base_path: &str) -> Result<()> {
+ log!("Starting parsing of the file: {:?}", input_path);
+ convert(&input_path, &output_dir, base_path)?;
+
+ Ok(())
+}
+
+pub fn convert(path: &str, output_dir: &str, base_path: &str) -> Result<()> {
+ let mut parser = Parser::new();
+
+ let extension: String = unsafe { get_file_extension(path) }
+ .unwrap()
+ .as_string()
+ .unwrap();
+
+ match extension.as_str() {
+ ".one" => {
+ let name: String = unsafe { get_file_name(path) }.unwrap().as_string().unwrap();
+ log!("Parsing .one file: {}", name);
+
+ if path.contains("OneNote_RecycleBin") {
+ return Ok(());
+ }
+
+ let section = parser.parse_section(path.to_owned())?;
+
+ let section_output_dir = unsafe { get_output_path(base_path, output_dir, path) }
+ .unwrap()
+ .as_string()
+ .unwrap();
+
+ section::Renderer::new().render(§ion, section_output_dir.to_owned())?;
+ }
+ ".onetoc2" => {
+ let name: String = unsafe { get_file_name(path) }.unwrap().as_string().unwrap();
+ log!("Parsing .onetoc2 file: {}", name);
+
+ let notebook = parser.parse_notebook(path.to_owned())?;
+
+ let notebook_name = unsafe { get_parent_dir(path) }
+ .expect("Input file has no parent folder")
+ .as_string()
+ .expect("Parent folder has no name");
+ log!("notebook name: {:?}", notebook_name);
+
+ let notebook_output_dir = unsafe { get_output_path(base_path, output_dir, path) }
+ .unwrap()
+ .as_string()
+ .unwrap();
+ log!("Notebok directory: {:?}", notebook_output_dir);
+
+ notebook::Renderer::new().render(¬ebook, ¬ebook_name, ¬ebook_output_dir)?;
+ }
+ ext => return Err(eyre!("Invalid file extension: {}, file: {}", ext, path)),
+ }
+
+ Ok(())
+}
diff --git a/packages/onenote-converter/src/notebook.rs b/packages/onenote-converter/src/notebook.rs
new file mode 100644
index 00000000000..b868c5adc46
--- /dev/null
+++ b/packages/onenote-converter/src/notebook.rs
@@ -0,0 +1,120 @@
+use crate::parser::notebook::Notebook;
+use crate::parser::property::common::Color;
+use crate::parser::section::{Section, SectionEntry};
+use crate::templates::notebook::Toc;
+use crate::utils::utils::{log, log_warn};
+use crate::utils::{join_path, make_dir, remove_prefix, write_file};
+use crate::{section, templates};
+use color_eyre::eyre::{eyre, Result};
+use palette::rgb::Rgb;
+use palette::{Alpha, ConvertFrom, Hsl, Saturate, Shade, Srgb};
+use std::path::Path;
+
+pub(crate) type RgbColor = Alpha, f32>;
+
+pub(crate) struct Renderer;
+
+impl Renderer {
+ pub fn new() -> Self {
+ Renderer
+ }
+
+ pub fn render(&mut self, notebook: &Notebook, name: &str, output_dir: &str) -> Result<()> {
+ log!("Notebook name: {:?} {:?}", name, output_dir);
+ let _ = unsafe { make_dir(output_dir) };
+
+ // let notebook_dir = unsafe { join_path(output_dir, sanitize_filename::sanitize(name).as_str()) }.unwrap().as_string().unwrap();
+ let notebook_dir = output_dir.to_owned();
+
+ let _ = unsafe { make_dir(¬ebook_dir) };
+
+ let mut toc = Vec::new();
+
+ for entry in notebook.entries() {
+ match entry {
+ SectionEntry::Section(section) => {
+ toc.push(Toc::Section(self.render_section(
+ section,
+ notebook_dir.clone(),
+ output_dir.into(),
+ )?));
+ }
+ SectionEntry::SectionGroup(group) => {
+ let dir_name = sanitize_filename::sanitize(group.display_name());
+ let section_group_dir =
+ unsafe { join_path(notebook_dir.as_str(), dir_name.as_str()) }
+ .unwrap()
+ .as_string()
+ .unwrap();
+
+ log!("Section group directory: {:?}", section_group_dir);
+ let _ = unsafe { make_dir(section_group_dir.as_str()) };
+
+ let mut entries = Vec::new();
+
+ for entry in group.entries() {
+ if let SectionEntry::Section(section) = entry {
+ entries.push(self.render_section(
+ section,
+ section_group_dir.clone(),
+ output_dir.to_owned(),
+ )?);
+ }
+ }
+
+ toc.push(templates::notebook::Toc::SectionGroup(
+ group.display_name().to_string(),
+ entries,
+ ))
+ }
+ }
+ }
+
+ let toc_html = templates::notebook::render(name, &toc)?;
+ let toc_path = unsafe { join_path(output_dir, format!("{}.html", name).as_str()) }
+ .unwrap()
+ .as_string()
+ .unwrap();
+ // let _ = unsafe { write_file(toc_path.as_str(), toc_html.as_bytes()) };
+
+ Ok(())
+ }
+
+ fn render_section(
+ &mut self,
+ section: &Section,
+ notebook_dir: String,
+ base_dir: String,
+ ) -> Result {
+ let mut renderer = section::Renderer::new();
+ let section_path = renderer.render(section, notebook_dir)?;
+ log!("section_path: {:?}", section_path);
+
+ let path_from_base_dir = unsafe { remove_prefix(section_path.as_str(), base_dir.as_str()) }
+ .unwrap()
+ .as_string()
+ .unwrap();
+ log!("path_from_base_dir: {:?}", path_from_base_dir);
+ Ok(templates::notebook::Section {
+ name: section.display_name().to_string(),
+ path: path_from_base_dir,
+ color: section.color().map(prepare_color),
+ })
+ }
+}
+
+fn prepare_color(color: Color) -> RgbColor {
+ Alpha {
+ alpha: color.alpha() as f32 / 255.0,
+ color: Srgb::convert_from(
+ Hsl::convert_from(Srgb::new(
+ color.r() as f32 / 255.0,
+ color.g() as f32 / 255.0,
+ color.b() as f32 / 255.0,
+ ))
+ .darken(0.2)
+ .saturate(1.0),
+ )
+ .into_format(),
+ }
+}
diff --git a/packages/onenote-converter/src/page/content.rs b/packages/onenote-converter/src/page/content.rs
new file mode 100644
index 00000000000..f9e41dea6ae
--- /dev/null
+++ b/packages/onenote-converter/src/page/content.rs
@@ -0,0 +1,22 @@
+use crate::page::Renderer;
+use color_eyre::Result;
+use log::warn;
+// use crate::something_else::contents::Content;
+use crate::parser::contents::Content;
+
+impl<'a> Renderer<'a> {
+ pub(crate) fn render_content(&mut self, content: &Content) -> Result {
+ match content {
+ Content::RichText(text) => self.render_rich_text(text),
+ Content::Image(image) => self.render_image(image),
+ Content::EmbeddedFile(file) => self.render_embedded_file(file),
+ Content::Table(table) => self.render_table(table),
+ Content::Ink(ink) => Ok(self.render_ink(ink, None, false)),
+ Content::Unknown => {
+ warn!("Page with unknown content");
+
+ Ok(String::new())
+ }
+ }
+ }
+}
diff --git a/packages/onenote-converter/src/page/embedded_file.rs b/packages/onenote-converter/src/page/embedded_file.rs
new file mode 100644
index 00000000000..391e94e653b
--- /dev/null
+++ b/packages/onenote-converter/src/page/embedded_file.rs
@@ -0,0 +1,89 @@
+use crate::page::Renderer;
+use crate::parser::contents::EmbeddedFile;
+use crate::parser::property::embedded_file::FileType;
+use crate::utils::utils::log;
+use crate::utils::{join_path, write_file};
+use color_eyre::eyre::ContextCompat;
+use color_eyre::Result;
+use std::path::PathBuf;
+
+impl<'a> Renderer<'a> {
+ pub(crate) fn render_embedded_file(&mut self, file: &EmbeddedFile) -> Result {
+ let content;
+
+ let filename = self.determine_filename(file.filename())?;
+ let path = unsafe { join_path(self.output.as_str(), filename.as_str()) }
+ .unwrap()
+ .as_string()
+ .unwrap();
+ log!("Rendering embedded file: {:?}", path);
+ let _ = unsafe { write_file(path.as_str(), file.data()) };
+
+ let file_type = Self::guess_type(file);
+
+ match file_type {
+ FileType::Audio => content = format!("", filename),
+ FileType::Video => content = format!("", filename),
+ FileType::Unknown => {
+ content = format!(
+ "