Generación de documentos usando ONLYOFFICE DocumentBuilder


Hola Habr


Quiero hablar sobre una utilidad llamada DocumentBuilder, que le permite generar documentos, tablas y presentaciones, y también mostrar cómo puede usarla para resolver sus problemas, usando el ejemplo de creación de currículums a partir de plantillas.


El constructor funciona de la siguiente manera: usted escribe código en js utilizando métodos especiales de la documentación, se lo entrega a la utilidad y recopila el documento. O no recoge si hay errores.


Se recomienda utilizar un generador si desea resolver tareas del siguiente tipo:


  • Necesita hacer muchos documentos con pequeñas variaciones o documentos basados ​​en una gran cantidad de datos.
  • Necesita integrar la generación de documentos en cualquier servicio.

Hay servicios que le permiten crear un currículum: el usuario completa los campos necesarios y el sistema genera un documento y se lo entrega al usuario. Este será un ejemplo de cómo usar el constructor.


Como herramientas usaré Node.js (express).


Plan:


  1. El usuario ingresa datos en el formulario en la página en el navegador, el formulario se envía al servidor.
  2. En el servidor Node.js, crea un script para el generador basado en los datos del usuario.
  3. Node.js le da el script al constructor.
  4. El constructor crea un documento utilizando un script.
  5. Node.js devuelve el enlace al documento al usuario.

Creación de formularios


Primero, cree un formulario en el que el usuario ingresará sus datos. El formulario tendrá 8 campos: "Nombre completo", "Número de teléfono", "correo electrónico", "perfil", "título", "universidad", "ubicación", "año", "habilidad". El campo de habilidad puede ser clonado.


Cree un archivo index.html y agregue el código de la plantilla.


<div class="fill-name"> <input type="text" id="fill-name" placeholder="full name"> </div> <div class="phone-number"> <input type="number" id="phone-number" placeholder="phone number"> </div> <div class="email"> <input type="text" id="email" placeholder="email"> </div> <div class="profile"> <textarea id="profile" placeholder="Insert a brief description of yourself"></textarea> </div> <div class="education"> <input type="text" id="degree" placeholder="degree"> <input type="text" id="university" placeholder="university"> <input type="text" id="location" placeholder="location"> <input type="date" id="year" placeholder="year"> </div> <div class="skills"> <div class="skill"> <input type="text" id="new-skill" placeholder="skill" onkeyup="add_skill_by_enter(event)"> <button onclick="add_skill()">+</button> </div> </div> 

Aquí uso dos funciones: add_skill_by_enter (event) y add_skill (). Se necesitan para agregar varios campos presionando el botón + o Enter. Describiré estas funciones un poco más tarde.


Agregue un botón para enviar el formulario al servidor:


 <button onclick="sendForm()">Send</button> 

Ahora escribiremos funciones para trabajar con el formulario.


La primera función es add_skill ()


 add_skill = () => { const newSkill = document.getElementById("new-skill"); if (newSkill.value === '') {return; } //   ,       const div = document.createElement("div"); .// div const span = document.createElement("span"); //   const button = document.createElement("button"); //    span.innerText += newSkill.value; //   span   newSkill.value = ''; //      newSkill.focus(); //        button.innerText += "-"; button.onclick = () => { //      div.remove(); }; div.appendChild(span); //  span  div div.appendChild(button); //   document.getElementsByClassName('skills')[0].appendChild(div); //     }; add_skill_by_enter() add_skill_by_enter = (event) => { if (event.code === "Enter") { //  ,      enter add_skill(); } }; 

Agregamos una función simple para recopilar datos de los campos y enviarlos al servidor.


  get_skill_values = () => { const skills = []; if (document.getElementById('new-skill').value !== '') { skills.push(document.getElementById('new-skill').value); } Array.from(document.getElementsByClassName('skillfield')).forEach(current_element => { skills.push(current_element.innerHTML); }); return skills; }; sendForm() sendForm = () => { fetch('/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userData: { fillName: document.getElementById('fill-name').value, phoneNumber: document.getElementById('phone-number').value, email: document.getElementById('email').value, profile: document.getElementById('profile').value, education: { degree: document.getElementById('degree').value, university: document.getElementById('university').value, location: document.getElementById('location').value, year: document.getElementById('year').value, }, skills: get_skill_values() } }) }).then(res => res.json()) .then(response => { location.replace('/' + response.filename); //  ,       }) .catch(error => console.error('Error:', error)); }; 

El lado del servidor para trabajar con el formulario


Escribo la parte del servidor en express. La conexión de todas las bibliotecas, la configuración del servidor y la descripción de los métodos get y post se ven así:


 const path = require('path'); const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(bodyParser.json()); app.get('/', (req, res) => { res.sendFile(path.join(__dirname + '/index.html')); }); app.post('/', (req, res) => { //        }); app.listen(3000, () => console.log(`Example app listening on port ${3000}!`)); 

Ejecute express:
node main.js


Abra la dirección en el navegador:
http:localhost:3000


Vemos la forma creada. Lo llenamos con datos arbitrarios:


Obtenemos el siguiente json:


 {"userData":{"fillName":"Rotatyy Dmitryi","phoneNumber":"89879879898","email":"flamine@list.ru","profile":"Hi, my name is Joe\nAnd I work in a button factory\nI got a wife and two kids\nOne day, my boss says, “Joe, are you busy?”\nI said, “no”\n“Then push this button with your right hand”","country":"Russia","city":"Nizhniy Novgorod","education":{"degree":"Master of Pupets","university":"Nizhny novgorod state technical university","location":"Nizhniy Novgorod","year":"2015-05-02"},"skills":["apple.js","vintage.js","zerg.js","css","html","linux"]}}; 

Ahora necesita escribir un script para el constructor. Tomé como base la plantilla que ofrece Google Docs (plantilla de currículum).


Esta plantilla se ve así:
imagen


Con estos datos y la plantilla, debe crear un script con el que el constructor creará el documento.


Hay varias opciones sobre cómo hacer esto, y la más simple es descargar la versión de escritorio de los editores de ONLYOFFICE y escribir una macro que creará el documento utilizando los datos. Y luego agregue a la macro la creación y el almacenamiento del archivo: obtendrá un script para el generador. Esto funcionará porque las macros y el constructor usan la misma API.


Crear un script para un constructor


Comenzamos inicializando el objeto de página y agregando los datos que provienen del usuario:


 const Document = Api.GetDocument(); const data = {"userData":{"fillName":"Rotatyy Dmitryi","phoneNumber":"89879879898","email":"flamine@list.ru","profile":"Hi, my name is Joe\nAnd I work in a button factory\nI got a wife and two kids\nOne day, my boss says, “Joe, are you busy?”\nI said, “no”\n“Then push this button with your right hand”","country":"Russia","city":"Nizhniy Novgorod","education":{"degree":"Master of Pupets","university":"Nizhny novgorod state technical university","location":"Nizhniy Novgorod","year":"2015-05-02"},"skills":["apple.js","vintage.js","zerg.js","css","html","linux"]}}; 

Ahora necesita agregar el párrafo con el nombre de usuario completo. Está escrito en negrita, y este párrafo tiene un espacio entre líneas de 1.15.


 let paragraph = document.GetElement(0); //       FullName_style = Document.CreateStyle("FullName"); //    FullName_style.GetTextPr().SetFontSize(28); //    FullName_style.GetTextPr().SetBold(true); //  bold paragraph.SetStyle(FullName_style); //      paragraph.SetSpacingLine(1.15 * 240, "auto"); //    paragraph.AddText(data.userData.fillName); //     

También agregamos los párrafos restantes:
 // Country and city const CountryCity_style = Document.CreateStyle("CountryCity"); CountryCity_style.GetTextPr().SetFontSize(20); CountryCity_style.GetTextPr().SetCaps(true); CountryCity_style.GetTextPr().SetBold(true); paragraph = Api.CreateParagraph(); paragraph.AddText(data.userData.country + ', ' + data.userData.city); paragraph.SetStyle(CountryCity_style); paragraph.SetSpacingAfter(0); Document.Push(paragraph); // phone number const PhoneNumber_style = Document.CreateStyle("PhoneNumber"); PhoneNumber_style.GetTextPr().SetFontSize(20); PhoneNumber_style.GetParaPr().SetSpacingAfter(0); PhoneNumber_style.GetTextPr().SetBold(true); paragraph = Api.CreateParagraph(); paragraph.AddText(data.userData.phoneNumber); paragraph.SetStyle(PhoneNumber_style); Document.Push(paragraph); // email const Email_style = Document.CreateStyle("Email"); Email_style.GetTextPr().SetFontSize(18); Email_style.GetParaPr().SetSpacingAfter(0); Email_style.GetTextPr().SetBold(true); paragraph = Api.CreateParagraph(); paragraph.AddText(data.userData.email); paragraph.SetStyle(Email_style); Document.Push(paragraph); // SectionHeader style const SectionHeader = Document.CreateStyle("SectionHeader"); SectionHeader.GetTextPr().SetBold(true); SectionHeader.GetTextPr().SetColor(247, 93, 93, false); SectionHeader.GetTextPr().SetFontSize(28); SectionHeader.GetParaPr().SetSpacingBefore(1.33 * 240); SectionHeader.GetParaPr().SetSpacingLine(1 * 240, "auto"); // add header Profile: paragraph = Api.CreateParagraph(); paragraph.AddText("Profile:") paragraph.SetStyle(SectionHeader); Document.Push(paragraph); // add profile text: paragraph = Api.CreateParagraph(); paragraph.AddText(data.userData.profile) Document.Push(paragraph); // add header Education: paragraph = Api.CreateParagraph(); paragraph.AddText("Education:") paragraph.SetStyle(SectionHeader); Document.Push(paragraph); // add education year: const EducationYear_style = Document.CreateStyle("EducationYear"); EducationYear_style.GetTextPr().SetColor(102, 102, 102); EducationYear_style.GetTextPr().SetFontSize(18); EducationYear_style.GetParaPr().SetSpacingAfter(0); paragraph = Api.CreateParagraph(); paragraph.SetStyle(EducationYear_style); paragraph.AddText(data.userData.education.year) Document.Push(paragraph); // add education university: paragraph = Api.CreateParagraph(); run = Api.CreateRun(); run.AddText(data.userData.education.university) run.AddText(', ') run.AddText(data.userData.education.location) run.SetBold(true); paragraph.AddElement(run); run = Api.CreateRun(); run.AddText(' – ' + data.userData.education.degree) paragraph.AddElement(run); Document.Push(paragraph); // add header Skills: paragraph = Api.CreateParagraph(); paragraph.AddText("Skills:") paragraph.SetStyle(SectionHeader); Document.Push(paragraph); // add skills text: paragraph = Api.CreateParagraph(); const skills = data.userData.skills.map(x => ' ' + x).toString(); paragraph.AddText(skills) Document.Push(paragraph); 

Al ejecutar este script, obtenemos el siguiente documento:


Ahora es el momento de agregar funciones para escribir código de script en un archivo y generar un documento.


Generamos el script -> escribimos en el archivo -> entregamos el archivo al constructor -> devolvemos el enlace al archivo al usuario.


Agregamos una conexión de complementos para trabajar con archivos y ejecutar comandos usando Node.js, y también creamos una carpeta "pública" y la hacemos pública:


 const {exec} = require('child_process'); const fs = require('fs'); app.use(express.static('public')); 

La función para generar texto con un script será muy simple: simplemente devolverá una cadena con todo el código para el generador, al tiempo que agrega datos de usuario. Es importante agregar un carácter de salto de línea al final de cada línea, de lo contrario, nada funcionará.


 generate_script = (data) => { let first_template = 'builder.CreateFile("docx");\n' + 'const Document = Api.GetDocument();\n'; first_template += 'const data = ' + JSON.stringify(data) + ';\n'; first_template += 'let paragraph = Document.GetElement(0);\n' + 'FullName_style = Document.CreateStyle("FullName");\n' + ....   ~~~~~~~~~~~ return first_template; }; 

Ahora necesita escribir el script en un archivo y entregárselo al constructor. De hecho, todo el trabajo con el constructor se reducirá al hecho de que necesitamos ejecutar el comando documentbuilder path/script.js Node.js


Escribimos una función de compilación que hará esto:


 build = (data, res) => { const filename = Math.random().toString(36).substring(7) + '.docx'; //    let script = generate_script(data); script += 'builder.SaveFile("docx", "' + __dirname + '/public/' + filename + '");\n' + 'builder.CloseFile();'; fs.writeFile('public/' + filename + 'js', script, () => { exec('documentbuilder ' + 'public/' + filename + 'js', () => { res.send({'filename': filename }); }); }); }; 

Agregue una llamada al método de compilación (req.body, res); cuando post solicitud


 app.post('/', (req, res) => { build(req.body, res); }); 

Y ya terminaste. Por si acaso, publiqué el código de ejemplo completo aquí .


Por lo tanto, puede incrustar ONLYOFFICE DocumentBuilder en una aplicación web.


Espero que a pesar de una gran cantidad de simplificaciones, todo esté claro. Usé solo el código mínimo necesario para mostrar cómo funciona todo.


Por el momento, hay pensamientos para finalizar la utilidad y ampliar el rango de problemas a resolver. Le agradecería que comparta los casos reales de generación de documentos (bueno, de tablas con presentaciones, por supuesto, también) en los comentarios o en PM.

Source: https://habr.com/ru/post/466217/


All Articles