Looplex Knowledge Base
Codificação com Office Render
Codificação com Office Render
Introdução
O office-render é um motor de template inspirado no Mustache, mas focado em arquivos Microsoft Office. Ele funciona expandindo {{tags}} presentes no template com valores, expressões, funções e subtemplates fornecidos em formato json. Pode ser importado como módulo de serviço em runtime node.js ou função remota serverless utilizando API.
O diferencial desse motor é o uso de Angular Expressions + Angular Filters. Isso quer dizer que, além dos convencionais valores json e lambdas do Mustache, também pode-se utilizar expressões parecidas com Javascript ganhando maior flexibilidade, sem deixar de lado a segurança.
TL;DR;
O office-render segue a arquitetura de requisição e respostas. Seu uso se dá através do envio de um payload de configuração e recebe uma promessa de entrega de um documento tipo Microsoft Office no caso de sucesso ou erro se não for possível gerar o documento com as configurações enviadas.
// valores marcados entre '/*  */' são opcionais// , ... significa que valores adicionais podem ser enviadosconst payload = {  "template": "url || base64",  "datasource": { "key": "value"/*, ...*/ },  /* "partials": { "name": "url || base64", ... }, */  /* "lambdas": { "name": ["param", ..., "functionBody"], ... }, */  /* "path": "looplex-ged:domain.com/shared/office-render/out/result.docx" */}Para usar como serviço:
import { generate as createDocument } from "~/path/to/office-render.js";const /* base64 */ document = await createDocument(payload);Para usar como API:
Definir header Content-Type: application/json; charset=utf-8 e enviar:
POST`${basepath}/api/${version}/office-render`;JSON.stringify(payload);// { "status": "SUCCESS|FAILURE", "message": "base64" }Por exemplo, imagine o template docx abaixo hospedado na url https://assets.domain.com/templates/office/congratulation.docx:
Olá {{username}},Parabéns, Você acabou de ganhar {{amount|numberFormat:'pt-BR':'{ "style": "currency", "currency": "BRL" }'}}!{{#in_br}}Bom, {{(amount * 0.3625)|numberFormat:'pt-BR':'{ "style": "currency", "currency": "BRL" }'}}, após pagar os impostos.{{/}}Dado o seguinte config:
{  "template": "https://assets.domain.com/templates/office/congratulation.docx",  "datasource": {    "username": "João Ninguém",    "amount": 1000.0,    "in_br": true  }}Irá produzir:
Olá João Ninguém,Parabéns, Você acabou de ganhar R$ 1.000,00!Bom, R$ 362,50, após pagar os impostos.MUSTACHE TAGS
Tags são indicadas através da marcação com chave dupla {{tag}} ou chave tripla {{{tag}}}. Vejamos seus diferentes tipos:
Undefined
O desacoplamento entre a view criada pelo designer e o model fornecido pela engenharia tem como conseqüencia um caso especial que é quando a view está esperando um valor, mas ele está ausente no model. Nesse caso, a tag não será interpolada e permanecerá no documento final.
Variáveis
O tipo mais simples de tag é a {{variable}}. Ela vai procurar pelo valor guardado na chave variable do contexto atual. Se não houver variable no contexto atual, os contextos superiores serão avaliados recursivamente.
::: important
Variáveis marcadas com chave dupla {{html_escaped}} passam, por padrão de segurança por HTML escape. Se o autor precisar do conteúdo raw, deve-se utilizar o mustache triplo: {{{unescaped_html}}}
:::
Seções e Seções Invertidas
Seções são úteis para alterar o contexto por uma parte. O comportamento é diferente dependendo do valor associado à chave. Uma seção começa com uma cerquilha # e termina com uma barra /. Isto é, {{section}} começa uma seção e {{/section}} termina uma seção (também pode ser apenas {{/}}).
Enquanto as seções são úteis para alterar o contexto por uma parte, as seções invertidas são úteis para capturar a ausência de conteúdo. Uma seção invertida começa com um acento circunflexo ^ e termina com uma barra /. Isto é, {{^inverted_section}} começa uma seção invertida e {{/inverted_section}} termina a seção invertida (também pode ser apenas {{/}}).
Booleanos
Se a seção {{#boolean}} for verdadeira, o conteúdo definido na seção será renderizado; se for falsa, o conteúdo será ignorado. Por exemplo:
Template
Parágrafo fixo.{{#boolean}}Parágrafo condicional caso verdadeiro.{{/}}{{^boolean}}Parágrafo condicional caso falso.{{/}}Config
{  "datasource": {    "boolean": false  }}Resultado
Parágrafo fixo.Parágrafo condicional caso falso.Listas
Se a seção {{#array}} existir e possuir conteúdo, a parte será reavaliada para cada valor da lista; se estiver vazia, o conteúdo será ignorado. Por exemplo:
Template
{{#array}}* {{.}}{{/}}{{^array}}A lista está vazia{{/}}Config
{  "datasource": {    "list": ["Alice", "Bob", "Charlie"]  }}Resultado
* Alice* Bob* CharlieObjetos
Se a seção {{#object}} existir, a parte será reavaliada com o contexto atualizado para object. Por exemplo:
Template:
{{#postalAddress}}{{streetAddress}}{{postOfficeBoxNumber}}{{addressLocality}}, {{addressRegion}} {{postalCode}}{{addressCountry}}{{/}}Config:
{  "datasource": {    "postalAddress": {      "streetAddress": "1600 Amphitheatre Pkwy",      "postOfficeBoxNumber": "1982",      "addressLocality": "Mountain View",      "addressRegion": "California",      "addressCountry": "USA",      "postalCode": "94043"    }  }}Resultado:
1600 Amphitheatre PkwyTower Z, 1982Mountain View, California 94043USALambdas
Quando o valor associado à chave é uma função, ela será executada com o atual contexto scope como primeiro argumento e o gerenciador de contextos scopeManager como segundo argumento. Por exemplo:
Template:
{{#users}}{{greeter}}{{/}}Config:
{  "datasource": {    "users": [      { "name": "Alice" },      { "name": "Bob" },      { "name": "Charlie" }    ]  },  "lambdas": {    "greeter": "return `Olá ${scope.name}, como vai?`"  }}Resultado:
Olá Alice, como vai?Olá Bob, como vai?Olá Charlie, como vai?Outro exemplo:
Template:
O recorrente {{fullname}}, {{identidade}} ...Config:
{  "user": {    "fullname": "Alice Bates",    "isOrganization": false,    "federalTaxNumber": "051-96-9495"  },  "lambda": {    "identidade": "if (scope.user.isOrganization) { return `SSN: ${scope.user.federalTaxNumber}` } else { return `DUNS: ${scope.user.federalTaxNumber}` }"  }}Resultado:
O recorrente Alice Bates, SSN 051-96-9495 ...::: tip
Este foi um exemplo ilustrativo de if-else. No contexto anterior, o ideal seria utilizar um operador ternário. e.g. const {isOrganization, federalTaxNumber} = scope.user; return ${isOrganization ? 'DUNS' : 'SSN'} ${federalTaxNumber}.
:::
ANGULARJS EXPRESSIONS
Em matemática, quando combinamos números e variáveis de uma forma válida, utilizando operadores como adição, subtração, multiplicação, divisão, exponenciação et cetera e funções, damos o nome de expressão matemática à agregação combinada desses símbolos.
Em linguagens de programação, uma expressão é uma unidade de código que pode ser reduzida à um valor. No Javascript, existem dois tipos de expressões: as que produzem efeitos colaterais (como atribuir valor) e aquelas que não (como resultado de um cálculo).
A expressão x = 7 é um exemplo do primeiro tipo. Essa expressão usa o operador = para atribuir o valor sete para a variável x. O resultado da expressão resulta em 7.
A expressão 3 + 4 é um exemplo do segundo tipo. Essa expressão usa o operador + para adicionar 3 e 4 produzindo o valor 7 conhecido como soma.
Como dito inicialmente, o grande diferencial desse motor é o uso das AngularJS Expressions. Extraídas do código fonte do projeto angular.js, elas permitem a criação simplificada de templates complexos. São exemplos de AngularJS Expressions:
Operadores de atribuição
Um operador de atribuição associa o valor ou resultado do operando direito ao operando esquerdo. O mais simples dos operadores de atribuição é o igual (=) e um exemplo clássico do uso desse operador é y = f(x).
| Operador | Descrição | Exemplo | 
|---|---|---|
| Assignment = | Atribui o valor do operando à direita ao operando da esquerda | x = 1 | 
Operadores de comparação
Um operador de comparação avalia seus operandos e retorna um valor lógico baseado no fato da comparação se mostrar verdadeira ou não. Os operandos podem ser numéricos, textuais, booleanos ou propriedades de objetos. Valores textuais são comparados utilizando a ordem lexicográfica padrão usando valores unicode.
| Operador | Descrição | Exemplo | 
|---|---|---|
| Equal == | Retorna truese os operandos são iguais | 1 == '1' | 
| Not Equal != | Retorna truese os operandos não são iguais | 1 != 2 | 
| Strict Equal === | Retorna truese os operandos são iguais e possuem o mesmo tipo | 1 === 1 | 
| Strict not equal !== | Retorna truese os operandos não são iguais | 1 !== '1' | 
| Greater than > | Retorna truese o operando da esquerda é maior que o operando da direita | 2 > 1 | 
| Greater than or equal >= | Retorna truese o operando da esquerda é maior ou igual ao operador da direita | 1 >= 1 | 
| Less than < | Retorna truese o operando da esquerda é menor que o operando da direita | 1 < 2 | 
| Less than or equal | Retorna truese o operando da esquerda é menor ou igual ao operando da direita | 1 <= 1 | 
Operadores aritméticos
Operadores aritméticos utilizam valores numéricos, tanto literais como variáveis, como seus operandos e entregam como resultado outro valor numérico. Os operadores aritméticos padrão são adição +, subtração -, multiplicação * e divisão /. Esses operadores funcionam de forma análoga ao esperado em outras linguagens de programação quando consideramos o operador $\circ$:
$$\circ : IEEE754^n \rightarrow IEEE754$$
::: important Uma particularidade da divisão nesse motor é que a divisão por zero produz Infinity devido à natureza aproximada da especificação de ponto flutuante. :::
Além dos já consagrados +, -, *, / segue abaixo a relação de outros operadores aritméticos disponíveis:
| Operador | Descrição | Exemplo | 
|---|---|---|
| Remainder % | Operador binário. Retorna o valor inteiro resto da divisão entre dois operandos | 12 % 5resulta em2 | 
| Unary negation - | Operador unário. Retorna o inverso aditivo do operando | Se x = 1, o valor de-xé-1 | 
| Unary plus + | Operador unário. Retorna a versão numérica de um operando | +'1'retorna1 | 
Operadores lógicos
Geralmente, operadores lógicos são utilizados com valores booleanos, mas nesse motor funcionam com valores falsy e truthy. Operandos como && e || retornam o valor de um dos seus operandos, então se os valores forem não booleanos, as expressões retornam esse valor. Abaixo, segue a definição e tabela de quem são os valores falsy e truthy:
| Operador | Descrição | 
|---|---|
| false | Palavra reservada false | 
| 0 | O número ZERO (também 0.0,0x0, i.e. qualquer representação de ZERO) | 
| -0 | A negação do número ZERO | 
| "",'' | Conteúdo textual vazio | 
| null | Palavra reservada nullrepresenta a ausência intencional de objeto | 
| undefined | Palavra reservada undefinedrepresenta que o tipo e valor da variável ainda são desconhecidos | 
| NaN | Palavra reservada NaNrepresenta um resultado algébrico que não pode ser representado como número. Exemplos:'render'**2,'nagao'/2 | 
| document.all | Objetos são considerados falsyse, e somente se, possuem um slot interno IsHTMLDDA definidos pelo motor de execução. | 
Definidos falsy e truthy, podemos descrever os operadores lógicos a seguir:
| Operador | Descrição | Exemplo | 
|---|---|---|
| Logical AND && | Retorna expr1, se for falsy eexpr2caso contrário. i.e.&&resulta emtruequando ambos operandos são truthy | expr1 && expr2 | 
| Logical OR ` | ` | |
| Logical NOT ! | Operador unário, retorna falsese o operando for truthy | !expr | 
Operadores de texto
Além dos operadores de comparação, que também servem para texto, o operador de concatenação + concatena dois operandos do tipo texto, retornando a união seqüencial da esquerda para direita dos operandos.
'ET, ' + 'telefone, ' + 'minha casa...'// imprime 'ET, telefone, minha casa...'Operador condicional ternário
Existe apenas um operador padrão que recebe três operandos: o operador condicional ternário. Nele, o resultado varia entre dois valores dependendo da condição. Sua sintaxe é:
condição ? resultado_caso_verdadeira : resultado_caso_falsaPor exemplo:
idade >= 18 ? 'adulto' : 'menor'Essa expressão retorna 'adulto' se a variável idade assumir valor maior ou igual a dezoito anos e 'menor' caso contrário.
Diferenças entre AngularJS Expressions e Javascript Expressions
- Contexto: AngularJS Expressions utilizam como contexto o objeto scope;
- Indulgente: No Javascript, a avaliação de propriedades com undefineddisparaReferenceErrorouTypeError. No AngularJS Expressions, a avaliação é permissiva paraundefinedenull;
- Filters: É possível aplicar transformações adicionais ao valor da expressão antes da impressão;
- Ausência de controladores de fluxo: AngularJS Expressions não oferece suporte a declarações condicionais, loops ou disparo de erros;
- Impossibilidade de declarar funções: Em AngularJS Expressions, não é possível declarar novas funções;
- Impossibilidade de declarar expressões regulares: Em AngularJS Expressions, não é possível declarar expressões regulares;
- Impossibilidade de criação de novos objetos utilizando o operador new: AngularJS Expressions não interpreta o operador new;
- Ausência da notação de atalho para operações de atribuição: Em AngularJS Expressions, não é possível utilizar +=,-=,*=,/=,%=,**=,<<=,>>=,>>>=,&=,^=,|=,&&=,||=,??=;
- Ausência de operadores aritméticos increment, decrement e exponentiation: Não é possível utilizar operados ++,--e**,
- Ausência de operadores bitwise, comma e void: Não é possível utilizar operadores bitwise, ,, ouvoid;
- Ausência de operadores relacionais: Não é possível utilizar operadores relacionais ineinstanceof.
::: tip Para regras complexas, deve-se utilizar expressões lambda que são chamadas pelo nome no template. :::
ANGULARJS FILTERS
Filtros permitem a transformação do resultado da expressão antes de aplicar a interpolação. Para chamar um filtro, deve-se utilizar a seguinte notação:
{{ expression | filter }}É possível passar argumentos aos filtros. Por exemplo:
{{users|separator:', ':' e '}}Imprime
Alice, Bob e CharlieFiltros permitem chaining. Por exemplo:
{{expression | filter_0 | filter_1 | filter_2}}Segue abaixo a relação de filtros disponíveis:
| Identificador | Parâmetros | Exemplo | 
|---|---|---|
| separator | middle, last | `{{users | 
| uppercase | `{{'Office Render' | |
| lowercase | `{{'Office Render' | |
| imageSize | width, height | `{{%image | 
| imageMaxSize | width, height | `{{%image | 
IMAGENS
É possível inserir imagens dinamicamente utilizando as tags {{%image}} para inserção text-level e {{%%image}} para inserção block-level. São permitidos conteúdos tanto no formato binário base64 como também de url pública. Exemplos:
Template:

Config:
{  "picture": "/9j/4AAQSkZJRgABAQAAAQABAAD//gAgQ29tcHJlc3NlZCBieSBqcGVnLXJlY29tcHJlc3MA/",  "fullname": "Angelica Astrom",  "role": "Partner",  "bio": "Sócia em direito societário, financeiro e de infraestrutura, Angelica é especialista em operações estruturadas e project finance. Altamente experiente em questões envolvendo os setores de transporte, logística e mineração.",  "email": "angelica.astrom@example.com",  "phone": "(11) 91234-5678",  "linkedin": "/in/angelica.astrom",  "city": "Nova Iorque, NY",  "areas_of_practice": [    "Bancário, seguros e financeiro",    "Financiamento de Projetos e infraestrutura",    "Mercado de capitais",    "Serviços financeiros",    "Reestruturação e insolvência",    "Contratos e negociações complexas"  ],  "associations": [    "Ordem dos Advogados do Brasil (OAB);",    "International Bar Association (IBA);",    "Instituto Brasileiro de Estudos de Direito da Energia (IBDE)."  ],  // continues ...}Resultado:

Outra forma ainda é utilizar uma imagem como placeholder para outra imagem. Para isso, clicar com botão direito na imagem placeholder, selecionar Edit Alt Text ... e colocar {{%image}} como conteúdo.
| Botão Direito | Alt Text | 
|---|---|
|  { width=290px } |  | 
WORD
HTML e Markdown
Criadores de conteúdo web podem ficar felizes, pois o motor aceita aplicar valores Markdown e HTML. Em linhas gerais, elementos HTML permitidos no <body> são de block-level ou text-level (também conhecido por inline). A distinção é fundamentada em diversos conceitos:
- Conteúdo: Geralmente, elementos block-level podem conter outros elementos block-level e também text-level; enquanto os text-level possuem apenas dados e outros elementos text-level. A idéia chave dessa separação estrutural é que elementos block-level criam estruturas maiores que elementos text-level.
- Formatação: Por padrão, a formatação de elementos block-level e text-level é diferente. Elementos block-level começam novas linhas e text-level não.
- Direcionalidade: Por razões técnicas envolvendo o funcionamento do algorítmo de texto bidirecional unicode, os elementos block-level e text-level diferem na forma como trabalham a direcionalidade do conteúdo.
Portanto, é importante utilizar a semântica correta para informar o office-render na hora de enviar conteúdo HTML. Para indicar que a tag irá receber um valor text-level (imprimir dentro do parágrafo), deve-se utilizar a notação {{~inline}} e para indicar que a tag irá receber um valor block-level (irá criar um novo parágrafo), deve-se utilizar a notação {{~~block}}. Analogamente, pode-se utilizar Markdown utilizando {{~inline|markdown}} para conteúdos text-level e {{~~block|markdown}} para conteúdos block-level.
O que é Markdown?
Markdown é a ferramenta de conversão text-to-HTML para escritores que venceu as adversárias no começo dos anos 2000 e se tornou padrão de mercado. Ela define a escrita de conteúdo semântico textual utilizando uma notação fácil-de-ler, fácil-de-escrever que é convertida mecanicamente em HTML. Por exemplo, esse manual foi todo escrito utilizando Markdown. No Wordpress e no GitHub, utiliza-se Markdown para escrever artigos. Para maiores informações sobre sua syntax, visite o playground.
Relação de tags text-level suportadas: <br />, <span>, <small>, <ins>, <del>, <strong>, <em>, <a>, <sub>, <sup>.
Relação de tags block-level suportadas: <p>, <h1>, <h2>, <h3>, <h4>, <h5>, <h6>, <ul>, <ol>, <li>, <pre>, <code>, <table>, <thead>, <tfoot>, <tr>, <th>, <td>, <img>.
Relação de propriedade de estilo suportadas: font-family, font-size, color, background-color, text-decoration, text-align, vertical-align, border, break-before, break-after, width, height, padding-left.
::: important Não é possível utilizar HTML e Markdown em arquivos PowerPoint e Excel. O fato do Word compartilhar da mesma natureza de fluxo linear, facilita a conversão. Já documentos PowerPoint possuem múltiplos slides, cada um armazenado em um arquivo diferente com cada elemento posicionado absolutamente em relação ao slide. :::
Notas de rodapé
Para inserir notas de rodapé em uma página, deve-se utilizar a tag {{:footnote id}}, onde id é a chave no datasource que contém a nota de rodapé.
Template:
Grace Brewster Murray Hopper (née Murray; December 9, 1906 – January 1, 1992) was an American computer scientist, mathematician, and United States Navy rear admiral.{{:footnote gracehopper}}Config:
{  "datasource": {    "gracehopper": "<w:r><w:rPr><w:b/></w:rPr><w:t xml:space='preserve'>Amazing Grace: Rear Adm. Grace Hopper, USN, was a pioneer in computer science.</w:t></w:r><w:r><w:rPr><w:i/></w:rPr><w:t xml:space='preserve'>Military Officer. Vol. 12, no. 3. Military Officers Association of America. pp. 52–55, 106. Retrieved March 1, 2014.</w:t></w:r>"  }}Resultado:
Amazing Grace: Rear Adm. Grace Hopper, USN, was a pioneer in computer science. Military Officer. Vol. 12, no. 3. Military Officers Association of America. pp. 52–55, 106. Retrieved March 1, 2014.
Subtemplates E Segments
A operação de transclusão no office-render é realizada utilizando subtemplates. Ou seja, incluindo outros documentos Word. Para isso, o motor oferece dois tipos diferentes de tags: {{:include id}}, {{:segment}} e {{:includesegment}}.
 { width=480px }
{ width=480px }
Subtemplates
A tag {{:include id}} permite a inserção de um documento ou template "ancestral" no corpo do template em construção. Por exemplo:
Templates:
fooLorem ipsum{{:include B}}dolor sit ametConfig:
{  "datasource": {},  "partials": {    "B": "base64|url"  }}Resultado:
Lorem ipsumfoodolor sit ametSegments
A tag {{:includesegment id}} permite a inserção de um trecho de conteúdo ou template dentro do próprio template. Por exemplo:
Template:
| Endereço de Cobrança | Endereço de Entrega | 
|---|---|
| {{#billingAddress}}{{:includesegment address}}{{/}} | {{#shippingAddress}}{{:includesegment address}}{{/}} | 
Config:
{  "datasource": {    "billingAddress": {      "name": "HOME",      "line1": "1600 Amphitheatre Pkwy",      "postOfficeBox": "1982",      "city": "Mountain View",      "stateOrProvince": "California",      "country": "USA",      "postalCode": "94043"    },    "shippingAddress": {      "name": "WORK"      "line1": "1600 Amphitheatre Pkwy",      "postOfficeBox": "1982",      "city": "Mountain View",      "stateOrProvince": "California",      "country": "USA",      "postalCode": "94043"    }  }}Resultado:
 { width=290px }
{ width=290px }
POWERPOINT
É possível criar arquivos PowerPoint através de duas estratégias diferentes: explícita, onde o engenheiro especifica quais slides serão utilizados e com quais conteúdos e implícita, onde o designer utiliza marcações para indicar se um slide deve aparecer condicionalmente, repetir durante o fluxo definido no template original.
Implícito
O uso implícito é inspirado em uma jornada ou story, é muito parecido com o fluxo de um documento Word. Só que no caso do PowerPoint, a estória é contada da esquerda para a direita.
Template:
|  { width=290px } |  { width=290px } | 
|---|---|
|  { width=290px } |  { width=290px } | 
Config:
{  "intro": {    "motto": "Estamos reinterpretando o trabalho dos advogados no século 21",    "company": "Looplex"  },  "differentiation": {    "title": "Nosso diferencial",    "description": "Respostas rápidas, Qualidade, Preço Baixo. Utilizamos tecnologia na construção do seu contrato impecável, da sua contestação que utiliza as melhores estratégias, explora as provas e que não deixa pedra sobre pedra.",    "pain": "Chega de desperdiçar horas e horas, buscando teses que mais ou menos funcionam, com copia e cola, ajustes de texto e formatação!",    "solution": "Legal Digital Experience (DX) Platform"  },  "tooling": {    "title": "Complicado virando Simples",    "description": "Virando o jogo com o apoio da tecnologia e levando sua prática para um novo patamar diferenciando você dos concorrentes.",    "first": {      "title": "Engenharia Jurídica",      "description": "Conversão do seu conhecimento para o formato digital."    },    "second": {      "title": "Interfaces Online",      "description": "Para advogados, clientes e sistemas interajam."    },    "third": {      "title": "Smart Documents",      "description": "Muito além do documento impresso, os documentos inteligentes realizam tarefas e conversam com outros sistemas."    },    "fourth": {      "title": "Dados Estruturados",      "description": "Conteúdo que permite aplicar estatística, gerar reports e analisar estratégias."    },  },  "verifiableCustomerWins": {    "title": "Nossos Clientes",    "description": "Junte-se ao incrível grupo de vanguarda que está revolucionando a prática do direito e economizando até 95% do tempo sem trabalhos repetitivos.",    "brands": [...]  }}Resultado:
|  { width=290px } |  { width=290px } | 
|---|---|
|  { width=290px } |  { width=290px } | 
Explícito
O uso explícito é inspirado em lego blocks. Cada slide pode ser considerado um bloco que fica disponível para o engenheiro seqüenciar e repetir ao seu sabor.
Template:
|  { width=290px } |  { width=290px } | 
|---|---|
|  { width=290px } |  { width=290px } | 
Config:
{  "datasource": {    "slides": [      {        "$slide": 1,        "subtitle": "action",        "title": "office-render"      },      {        "$slide": 4,        "motto": "Criação automática de documentos Microsoft Office focada em pessoas"      }    ]  }}Resultado:
| Slide 1 | Slide 2 | 
|---|---|
|  { width=290px } |  { width=290px } | 
TÓPICOS AVANÇADOS
Raw OOXML
O padrão que define os arquivos docx, pptx e xslx se chama ECMA-376. Para quem conhece o dialeto, é possível injetar ooxmls utilizando a notação {{@ooxml}}. Com essa notação, o parágrafo inteiro (<w:p>) é substituído pelo conteúdo da chave ooxml. Por exemplo:
Template:
{{@ooxml}}Config:
{  "datasource": {    "ooxml": "<w:p><w:pPr><w:spacing w:before='120' w:after='120'/><w:ind w:left='720' w:right='720'/><w:rPr><w:sz w:val='18'/></w:rPr></w:pPr><w:r><w:rPr><w:sz w:val='18'/></w:rPr><w:t xml:space='preserve'>Humpty Dumpty sat on a wall. </w:t></w:r></w:p>"  }}Dash Syntax
Geralmente, quando usamos seções, o motor vai supor qual elemento do office que utilizará na iteração. Por exemplo, se entre as {{#tags}} ... {{/}}:
- Existir uma célula de tabela (<w:tc>ou<a:tc>), o motor irá iterar sobre a linha da tabela (<w:tr>ou<a:tr>),
- Em outros casos, o motor não expandirá o loop e ficará restrito à parte definida na seção.
Com a notação Dash Syntax o autor pode definir o elemento ooxml que ele deseja iterar. Por exemplo, se o desejo for criar um novo parágrafo para cada elemento de uma lista, pode-se fazer o seguinte:
{{-w:p paragraphs}}{{.}}{{/}}Javascript
O motor office-render, suas extensões e lambdas utilizadas em runtime utilizam a linguagem Javascript. O runtime de execução é continuamente atualizado de acordo com a disponibilidade de novas versões do NodeJS e V8 nos ambientes de hospedagem serverless. Isso faz com que novas funcionalidades que apareçam na camada de motor e linguagem fiquem também disponíveis na confecção dos templates.