Gerando documentos usando o ONLYOFFICE DocumentBuilder


Oi Habr.


Quero falar sobre um utilitário chamado DocumentBuilder, que permite gerar documentos, tabelas e apresentações e também mostrar como você pode usá-lo para resolver seus problemas, usando o exemplo da criação de um currículo a partir de modelos.


O construtor funciona da seguinte maneira: você escreve código em js usando métodos especiais da documentação, fornece-o ao utilitário e ele coleta o documento. Ou não coleta se houver erros.


É recomendável usar um construtor se você deseja resolver tarefas do seguinte tipo:


  • Você precisa criar muitos documentos com pequenas variações ou documentos com base em muitos dados.
  • Você precisa integrar a geração de documentos em qualquer serviço.

Existem serviços que permitem criar um currículo: o usuário preenche os campos necessários e o sistema gera um documento e o entrega ao usuário. Este será um exemplo de como usar o construtor.


Como ferramentas, usarei o Node.js (express).


Plano:


  1. O usuário insere dados no formulário na página do navegador, o formulário é enviado ao servidor.
  2. No servidor Node.js, ele cria um script para o construtor com base nos dados do usuário.
  3. O Node.js fornece o script para o construtor.
  4. O construtor cria um documento usando um script.
  5. Node.js retorna o link para o documento para o usuário.

Criação de formulário


Primeiro, crie um formulário no qual o usuário insira seus dados. O formulário terá 8 campos: "Nome completo", "Número de telefone", "E-mail", "Perfil", "Diploma", "Universidade", "Localização", "Ano", "Habilidade". O campo de habilidade pode ser clonado.


Crie um arquivo index.html e adicione o código do modelo.


<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> 

Aqui eu uso duas funções: add_skill_by_enter (event) e add_skill (). Eles são necessários para adicionar vários campos pressionando o botão + ou Enter. Descreverei essas funções um pouco mais tarde.


Adicione um botão para enviar o formulário ao servidor:


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

Agora vamos escrever funções para trabalhar com o formulário.


A primeira função é 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(); } }; 

Adicionamos uma função simples para coletar dados dos campos e enviá-los para o 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)); }; 

O lado do servidor para trabalhar com o formulário


Eu escrevo a parte do servidor no express. A conexão de todas as bibliotecas, a configuração do servidor e a descrição dos métodos get e post são assim:


 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}!`)); 

Execute express:
node main.js


Abra o endereço no navegador:
http:localhost:3000


Vemos o formulário criado. Nós a preenchemos com dados arbitrários:


Temos o seguinte 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"]}}; 

Agora você precisa escrever um script para o construtor. Tomei como base o modelo que o Google Docs oferece (modelo de currículo).


Este modelo é assim:
imagem


Usando esses dados e o modelo, é necessário criar um script com o qual o construtor criará o documento.


Existem várias opções de como fazer isso, e a mais simples é baixar a versão para desktop dos editores do ONLYOFFICE e escrever uma macro que criará o documento usando os dados. E depois adicione à macro a criação e o salvamento do arquivo - você obtém um script para o construtor. Isso funcionará porque as macros e o construtor usam a mesma API.


Criando um script para um construtor


Começamos inicializando o objeto de página e adicionando os dados que vieram do usuário:


 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"]}}; 

Agora você precisa adicionar o parágrafo com o nome de usuário completo. Está escrito em negrito e este parágrafo tem um espaçamento entre linhas 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); //     

Também adicionamos os parágrafos 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); 

Ao executar este script, obtemos o seguinte documento:


Agora é hora de adicionar recursos para escrever código de script em um arquivo e gerar um documento.


Geramos o script -> escrevemos no arquivo -> entregamos o arquivo ao construtor -> retornamos o link ao arquivo para o usuário.


Adicionamos uma conexão de complementos para trabalhar com arquivos e executar comandos usando o Node.js, além de criar uma pasta "pública" e torná-la pública:


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

A função para gerar texto com um script será muito simples - ela simplesmente retornará uma sequência com todo o código do construtor, enquanto adiciona dados do usuário. É importante adicionar um caractere de quebra de linha no final de cada linha, caso contrário 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; }; 

Agora você precisa gravar o script em um arquivo e entregá-lo ao construtor. De fato, todo o trabalho com o construtor será reduzido ao fato de que precisamos executar o comando documentbuilder path/script.js Node.js


Escrevemos uma função de compilação que fará isso:


 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 }); }); }); }; 

Adicione uma chamada ao método build (req.body, res); quando pedido pós


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

E você terminou. Por precaução, publiquei o código de exemplo completo aqui .


Assim, você pode incorporar o ONLYOFFICE DocumentBuilder em um aplicativo da web.


Espero que, apesar de um grande número de simplificações, tudo esteja claro. Usei apenas o código mínimo necessário para mostrar como tudo funciona.


No momento, há pensamentos para concluir a utilidade e expandir a gama de problemas a serem resolvidos. Ficaria muito grato se você compartilhar os casos reais de geração de documentos (bem, de tabelas com apresentações, é claro também) nos comentários ou no PM.

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


All Articles