diff --git a/lib/docx.dart b/lib/docx.dart index 7f4b598..676a8e9 100644 --- a/lib/docx.dart +++ b/lib/docx.dart @@ -1,4 +1,5 @@ export 'src/replace.dart'; -export 'src/docx.dart' - if (dart.library.js_interop) 'src/docx_web.dart' - if (dart.library.io) 'src/docx.dart'; \ No newline at end of file +export 'src/docx.dart'; + +export 'src/export.dart' + if (dart.library.js_interop) 'src/export_web.dart'; diff --git a/lib/src/docx.dart b/lib/src/docx.dart index 8cedf26..ab92bc0 100644 --- a/lib/src/docx.dart +++ b/lib/src/docx.dart @@ -12,6 +12,7 @@ class DocxEditor { Uint8List? binaryInDocx; String? pathOutDocx; String? name; + Map replaceMap; /// [fileInDocx] The Docx file /// [binaryInDocx] The Docx binary (for web generaly) @@ -22,7 +23,7 @@ class DocxEditor { this.binaryInDocx, this.name, this.pathOutDocx, - required Map replaceMap + required this.replaceMap }) { if (fileInDocx != null) { @@ -34,7 +35,7 @@ class DocxEditor { throw(ArgumentError('file or binary is mandatory to be define')); List documentsXml = editById(replaceMap); - save(documentsXml, replaceMap); + save(documentsXml, this); } /// This function search ID in [replaceMap] and replace by his value or by a image @@ -45,8 +46,6 @@ class DocxEditor { XmlDocument documentXmlRels = addImageinArchive(archive, replaceMap); - //print(documentXml.toXmlString(pretty: true, indent: ' ')); - // sort descending orderz final keys = replaceMap.keys.toList()..sort((a, b) => b.length.compareTo(a.length)); @@ -167,59 +166,4 @@ class DocxEditor { return documentXmlRels; } - /// Recreate a Archive to add modification and save it on [fileInDocx] - /// [documentsXml] is list -> ['word/document.xml', 'word/_rels/document.xml.rels'] - /// [replaceMap] for upload file image in docx file - void save(List documentsXml, Map replaceMap) { - try { - final archive = ZipDecoder().decodeBytes(binaryInDocx!); - - final Uint8List uListDocumentXml = utf8.encode(documentsXml[0].toXmlString()); - final Uint8List uListDocumentXmlRels = utf8.encode(documentsXml[1].toXmlString()); - - final newArchive = Archive(); - - for (ArchiveFile file in archive.files) { - if (file.name == 'word/document.xml') { - newArchive.addFile(ArchiveFile( - 'word/document.xml', - uListDocumentXml.length, - uListDocumentXml, - )); - } - else if (file.name == 'word/_rels/document.xml.rels') { - newArchive.addFile(ArchiveFile( - 'word/_rels/document.xml.rels', - uListDocumentXmlRels.length, - uListDocumentXmlRels, - )); - } else { - newArchive.addFile(file); - } - } - for (ReplaceContent replaceCnt in replaceMap.values.where((rm) => rm.img != null)) { - final imageBytes = replaceCnt.img!.bytes; - newArchive.addFile( - ArchiveFile( - 'word/media/${replaceCnt.img!.name}', - imageBytes.length, - imageBytes, - ) - ); - } - - final zipData = ZipEncoder().encode(newArchive); - - if (fileInDocx != null) { - File newFile = pathOutDocx == null ? fileInDocx! : File(pathOutDocx!); - newFile.writeAsBytesSync(zipData); - } - else { - File newFile = pathOutDocx == null ? File('.') : File(pathOutDocx!); - newFile.writeAsBytesSync(zipData); - } - } catch (e) { - throw StateError('DOCX BUILDER ERROR: $e'); - } - } } diff --git a/lib/src/docx_web.dart b/lib/src/docx_web.dart deleted file mode 100644 index b50a7c2..0000000 --- a/lib/src/docx_web.dart +++ /dev/null @@ -1,222 +0,0 @@ -import 'dart:convert'; - -// Web -import 'package:web/web.dart' as web; -import 'dart:js_interop'; - -import 'dart:typed_data'; -import 'package:archive/archive.dart'; -import 'package:xml/xml.dart'; -import 'package:docx/docx.dart'; - -class DocxEditor { - - Uint8List binaryInDocx; - String? pathOutDocx; - String? name; - - /// [binaryInDocx] The Docx binary (required) - /// [pathOutDocx] The path where new file is save (optional) - /// [replaceMap] All text and image to change (required) - DocxEditor({ - required this.binaryInDocx, - this.name, - this.pathOutDocx, - required Map replaceMap - }) { - - List documentsXml = editById(replaceMap); - save(documentsXml, replaceMap); - } - - /// This function search ID in [replaceMap] and replace by his value or by a image - List editById(Map replaceMap) { - Archive archive = ZipDecoder().decodeBytes(binaryInDocx); - ArchiveFile archiveFile = archive.firstWhere((f) => f.name == 'word/document.xml'); - XmlDocument documentXml = XmlDocument.parse(utf8.decode(archiveFile.content as List)); - - XmlDocument documentXmlRels = addImageinArchive(archive, replaceMap); - - //print(documentXml.toXmlString(pretty: true, indent: ' ')); - - // sort descending orderz - final keys = replaceMap.keys.toList()..sort((a, b) => b.length.compareTo(a.length)); - - final pattern = RegExp( - keys.map((key) => '(?<=^|\\s)${RegExp.escape(key)}(?=\\s|\$)').join('|'), - ); - - // replace image - for (XmlElement node in documentXml.findAllElements('w:t')) { - String ndText = node.innerText.toLowerCase(); - if (ndText.isEmpty) - continue; - XmlElement? parent; - for (String key in replaceMap.keys) { - ReplaceContent replaceCnt = replaceMap[key]!; - if (replaceCnt.img == null) - continue; - // ignore: unused_local_variable - for (RegExpMatch regMatch in RegExp('(?<=^|\\s)${key.toLowerCase()}(?=\\s|\$)').allMatches(ndText)) - parent = putImage(node.parentElement, replaceCnt, parent); - } - } - - // Only replace text! - for (XmlElement paragraph in documentXml.findAllElements('w:p')) { - List texts = paragraph.findAllElements('w:t').toList(); - if (texts.isEmpty) - continue; - - String original = texts.map((t) { - return t.innerText; - }).join(); - - String replaced = original.replaceAllMapped(pattern, (match) { - ReplaceContent rc = replaceMap[match[0]]!; - return rc.img == null ? rc.value! : ''; - }); - - if (original == replaced) - continue; - - final firstRun = paragraph.findElements('w:r').first; - final newRun = firstRun.copy(); - newRun.findAllElements('w:t').first.innerText = replaced; // Update with new text - - // replace the first w:r with the new - firstRun.replace(newRun); - } - - return [documentXml, documentXmlRels]; - } - - // TODO: Find a solution to put multiple image in only one - /// Replaces the text ([node]) with an image ([replaceContent]) - XmlElement? putImage(XmlElement? node, ReplaceContent replaceContent, XmlElement? newParent) { - - if (node!.parentElement == null && newParent != null) - node.attachParent(newParent); - XmlElement? parent = node.parentElement; - String nodeStr = node.toXmlString(); - node.replace(XmlDocumentFragment.parse(""" - $nodeStr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - """)); - return parent; - } - - /// Add all image in [replaceMap] to Archive in 'word/_rels/document.xml.rels' - XmlDocument addImageinArchive(Archive archive, Map replaceMap) { - - ArchiveFile archiveFile = archive.firstWhere((f) => f.name == 'word/_rels/document.xml.rels'); - XmlDocument documentXmlRels = XmlDocument.parse(utf8.decode(archiveFile.content as List)); - String documentXmlStr = documentXmlRels.toXmlString(); - RegExp exp = RegExp(r'Id="rId([0-9]+)"'); - Iterable matches = exp.allMatches(documentXmlStr); - int maxId = matches.isEmpty ? 1 : int.parse(matches.last.group(1)!); - - for (ReplaceContent replaceCnt in replaceMap.values.where((rm) => rm.img != null)) { - replaceCnt.img!.id = ++maxId; - String text = '\n'; - int lastIndex = documentXmlStr.lastIndexOf('/>') + 3; - documentXmlStr = documentXmlStr.replaceRange(lastIndex, lastIndex, text); - } - documentXmlRels = XmlDocument.parse(documentXmlStr); - return documentXmlRels; - } - - /// Recreate a Archive to add modification and save it on [fileInDocx] - /// [documentsXml] is list -> ['word/document.xml', 'word/_rels/document.xml.rels'] - /// [replaceMap] for upload file image in docx file - void save(List documentsXml, Map replaceMap) { - try { - final archive = ZipDecoder().decodeBytes(binaryInDocx); - - final Uint8List uListDocumentXml = utf8.encode(documentsXml[0].toXmlString()); - final Uint8List uListDocumentXmlRels = utf8.encode(documentsXml[1].toXmlString()); - - final newArchive = Archive(); - - for (ArchiveFile file in archive.files) { - if (file.name == 'word/document.xml') { - newArchive.addFile(ArchiveFile( - 'word/document.xml', - uListDocumentXml.length, - uListDocumentXml, - )); - } - else if (file.name == 'word/_rels/document.xml.rels') { - newArchive.addFile(ArchiveFile( - 'word/_rels/document.xml.rels', - uListDocumentXmlRels.length, - uListDocumentXmlRels, - )); - } else { - newArchive.addFile(file); - } - } - for (ReplaceContent replaceCnt in replaceMap.values.where((rm) => rm.img != null)) { - final imageBytes = replaceCnt.img!.bytes; - newArchive.addFile( - ArchiveFile( - 'word/media/${replaceCnt.img!.name}', - imageBytes.length, - imageBytes, - ) - ); - } - - String url = web.URL.createObjectURL( - web.Blob( - [binaryInDocx.toJS].toJS, - web.BlobPropertyBag(type: 'application/octet-stream'), - ), - ); - web.Document htmlDocument = web.document; - web.HTMLAnchorElement anchor = htmlDocument.createElement('a') as web.HTMLAnchorElement; - anchor.href = url; - anchor.style.display = '${name ?? 'template'}.docx'; - anchor.download = '${name ?? 'template'}.docx'; - web.document.body!.add(anchor); - anchor.click(); - anchor.remove(); - - } catch (e) { - throw StateError('DOCX BUILDER ERROR: $e'); - } - } -} diff --git a/lib/src/export.dart b/lib/src/export.dart new file mode 100644 index 0000000..4c7afce --- /dev/null +++ b/lib/src/export.dart @@ -0,0 +1,65 @@ +import 'package:xml/xml.dart'; +import 'package:archive/archive.dart'; +import 'package:docx/docx.dart'; +import 'dart:typed_data'; +import 'dart:io'; +import 'dart:convert'; + + /// Recreate a Archive to add modification and save it on [fileInDocx] + /// [documentsXml] is list -> ['word/document.xml', 'word/_rels/document.xml.rels'] + /// [docxEditor] Instance of docxEditor for Bytes, path,... + void save(List documentsXml, DocxEditor docxEditor) { + try { + final archive = ZipDecoder().decodeBytes(docxEditor.binaryInDocx!); + + final Uint8List uListDocumentXml = utf8.encode(documentsXml[0].toXmlString()); + final Uint8List uListDocumentXmlRels = utf8.encode(documentsXml[1].toXmlString()); + + final newArchive = Archive(); + + for (ArchiveFile file in archive.files) { + if (file.name == 'word/document.xml') { + newArchive.addFile(ArchiveFile( + 'word/document.xml', + uListDocumentXml.length, + uListDocumentXml, + )); + } + else if (file.name == 'word/_rels/document.xml.rels') { + newArchive.addFile(ArchiveFile( + 'word/_rels/document.xml.rels', + uListDocumentXmlRels.length, + uListDocumentXmlRels, + )); + } else { + newArchive.addFile(file); + } + } + for (ReplaceContent replaceCnt in docxEditor.replaceMap.values.where((rm) => rm.img != null)) { + final imageBytes = replaceCnt.img!.bytes; + newArchive.addFile( + ArchiveFile( + 'word/media/${replaceCnt.img!.name}', + imageBytes.length, + imageBytes, + ) + ); + } + + List zipData = ZipEncoder().encode(newArchive); + + File? fileInDocx = docxEditor.fileInDocx; + String? pathOutDocx = docxEditor.pathOutDocx; + + if (fileInDocx != null) { + File newFile = pathOutDocx == null ? fileInDocx : File(pathOutDocx); + newFile.writeAsBytesSync(zipData); + } + else { + File newFile = pathOutDocx == null ? File('.') : File(pathOutDocx); + newFile.writeAsBytesSync(zipData); + } + } catch (e) { + throw StateError('DOCX BUILDER ERROR: $e'); + } + } diff --git a/lib/src/export_web.dart b/lib/src/export_web.dart new file mode 100644 index 0000000..3f3dd20 --- /dev/null +++ b/lib/src/export_web.dart @@ -0,0 +1,73 @@ + import 'dart:convert'; + +// Web +import 'package:web/web.dart' as web; +import 'dart:js_interop'; + +import 'dart:typed_data'; +import 'package:archive/archive.dart'; +import 'package:xml/xml.dart'; +import 'package:docx/docx.dart'; + +/// Recreate a Archive to add modification and save it on [fileInDocx] +/// [documentsXml] is list -> ['word/document.xml', 'word/_rels/document.xml.rels'] +/// [docxEditor] Instance of docxEditor for Bytes, path,... +void save(List documentsXml, DocxEditor docxEditor) { + try { + final archive = ZipDecoder().decodeBytes(docxEditor.binaryInDocx!); + + final Uint8List uListDocumentXml = utf8.encode(documentsXml[0].toXmlString()); + final Uint8List uListDocumentXmlRels = utf8.encode(documentsXml[1].toXmlString()); + + final newArchive = Archive(); + + for (ArchiveFile file in archive.files) { + if (file.name == 'word/document.xml') { + newArchive.addFile(ArchiveFile( + 'word/document.xml', + uListDocumentXml.length, + uListDocumentXml, + )); + } + else if (file.name == 'word/_rels/document.xml.rels') { + newArchive.addFile(ArchiveFile( + 'word/_rels/document.xml.rels', + uListDocumentXmlRels.length, + uListDocumentXmlRels, + )); + } else { + newArchive.addFile(file); + } + } + for (ReplaceContent replaceCnt in docxEditor.replaceMap.values.where((rm) => rm.img != null)) { + final imageBytes = replaceCnt.img!.bytes; + newArchive.addFile( + ArchiveFile( + 'word/media/${replaceCnt.img!.name}', + imageBytes.length, + imageBytes, + ) + ); + } + + Uint8List zipData = ZipEncoder().encodeBytes(newArchive); + + String url = web.URL.createObjectURL( + web.Blob( + [zipData.toJS].toJS, + web.BlobPropertyBag(type: 'application/octet-stream'), + ), + ); + web.Document htmlDocument = web.document; + web.HTMLAnchorElement anchor = htmlDocument.createElement('a') as web.HTMLAnchorElement; + anchor.href = url; + anchor.style.display = '${docxEditor.name ?? 'template'}.docx'; + anchor.download = '${docxEditor.name ?? 'template'}.docx'; + web.document.body!.add(anchor); + anchor.click(); + anchor.remove(); + + } catch (e) { + throw StateError('DOCX BUILDER ERROR: $e'); + } +} diff --git a/lib/src/replace.dart b/lib/src/replace.dart index 7d381cf..3d813ba 100644 --- a/lib/src/replace.dart +++ b/lib/src/replace.dart @@ -11,6 +11,11 @@ class ReplaceContent { if (img == null && value == null) throw StateError('You need define value or image'); } + + @override + String toString() { + return value ?? img!.name; + } } /// 16cm * 360 000 = 5 760 000 EMU