{"id":631,"date":"2026-03-25T12:02:04","date_gmt":"2026-03-25T15:02:04","guid":{"rendered":"https:\/\/wiki.gobiernoriocuarto.gob.ar\/?p=631"},"modified":"2026-03-30T09:40:55","modified_gmt":"2026-03-30T12:40:55","slug":"como-usar-archivos-docx-en-gx18","status":"publish","type":"post","link":"https:\/\/wiki.gobiernoriocuarto.gob.ar\/?p=631","title":{"rendered":"Gu\u00eda T\u00e9cnica: Generaci\u00f3n y Descarga de Documentos Word (.docx) con DocxHelper en GeneXus"},"content":{"rendered":"<h2 data-path-to-node=\"6\">1. Requisitos Previos e Instalaci\u00f3n<\/h2>\n<h2 data-path-to-node=\"7\">1.1 Librer\u00edas Java (Apache POI)<\/h2>\n<p data-path-to-node=\"8\">Para que <code data-path-to-node=\"8\" data-index-in-node=\"9\">DocxHelper<\/code> funcione correctamente, el entorno debe contar con las librer\u00edas de Apache POI. Asegurarse de tener los siguientes archivos <code data-path-to-node=\"8\" data-index-in-node=\"144\">.jar<\/code> (versi\u00f3n 5.2.2 o superior) en el classpath del servidor\/proyecto:<br \/>Subi los archivos a una carpeta de Google Drive para el que las necesite. Link de Descarga:\u00a0<br \/>https:\/\/drive.google.com\/drive\/folders\/1E2Z1-K9ZQ_ur9SHQZd1BGq8rwO0X5Kya?usp=sharing<\/p>\n<ul data-path-to-node=\"9\">\n<li>\n<p data-path-to-node=\"9,0,0\"><code data-path-to-node=\"9,0,0\" data-index-in-node=\"0\">poi-5.2.2.jar<\/code><\/p>\n<\/li>\n<li>\n<p data-path-to-node=\"9,1,0\"><code data-path-to-node=\"9,1,0\" data-index-in-node=\"0\">poi-ooxml-5.2.2.jar<\/code><\/p>\n<\/li>\n<li>\n<p data-path-to-node=\"9,2,0\"><code data-path-to-node=\"9,2,0\" data-index-in-node=\"0\">poi-ooxml-lite-5.2.2.jar<\/code><\/p>\n<\/li>\n<li>\n<p data-path-to-node=\"9,3,0\"><code data-path-to-node=\"9,3,0\" data-index-in-node=\"0\">poi-scratchpad-5.2.2.jar<\/code><\/p>\n<p>En mi caso las rutas serian:<br \/>C:\\Program Files\\Apache Software Foundation\\Tomcat 10.1\\webapps\\GX18_GesRec_DSRDesarrollo\\WEB-INF\\lib<br \/>D:\\Proyectos\\GX18\\GX18_GesRec_PrdFuncional\\JavaDB2iSeries002\\Web\\lib<\/p>\n<h2 data-path-to-node=\"10\">1.2 Formato de Plantillas Obligatorio (.docx)<\/h2>\n<p data-path-to-node=\"11\">Las plantillas utilizadas <b data-path-to-node=\"11\" data-index-in-node=\"26\">deben<\/b> ser obligatoriamente formato <code data-path-to-node=\"11\" data-index-in-node=\"61\">.docx<\/code> real. Los formatos <code data-path-to-node=\"11\" data-index-in-node=\"86\">.doc<\/code>, <code data-path-to-node=\"11\" data-index-in-node=\"92\">.dot<\/code>, o <code data-path-to-node=\"11\" data-index-in-node=\"100\">.dotx<\/code> fallar\u00e1n.<\/p>\n<p data-path-to-node=\"12\"><b data-path-to-node=\"12\" data-index-in-node=\"0\">Script de Conversi\u00f3n Masiva (PowerShell):<\/b> Si existen plantillas heredadas en formatos antiguos, ejecutar el siguiente script en una PC con Microsoft Word instalado para convertirlas por lotes a <code data-path-to-node=\"12\" data-index-in-node=\"194\">.docx<\/code>.<br \/><strong>SCRIPT:<\/strong><br \/>\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/<\/p>\n<p>$word = New-Object -ComObject Word.Application<br \/>$word.Visible = $false<\/p>\n<p>$path = &#8220;D:\\PLANTILLAS&#8221; # \u2190 CAMBIAR ESTA RUTA POR LA CARPETA DE PLANTILLAS<\/p>\n<p>Get-ChildItem -Path $path -Recurse -Include *.doc, *.dot, *.dotx | ForEach-Object {<br \/>$inputPath = $_.FullName<br \/>$outputPath = [System.IO.Path]::ChangeExtension($inputPath, &#8220;.docx&#8221;)<\/p>\n<p>try {<br \/>$doc = $word.Documents.Open($inputPath)<br \/>$doc.SaveAs([ref] $outputPath, [ref] 16) # 16 = formato DOCX<br \/>$doc.Close()<br \/>Write-Host &#8220;Convertido con \u00e9xito:&#8221; $inputPath<br \/>}<br \/>catch {<br \/>Write-Host &#8220;Error al convertir:&#8221; $inputPath<br \/>}<br \/>}<\/p>\n<p>$word.Quit()<br \/>\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/<\/p>\n<p data-path-to-node=\"14\"><i data-path-to-node=\"14\" data-index-in-node=\"0\">Notas sobre la conversi\u00f3n:<\/i><\/p>\n<ul data-path-to-node=\"15\">\n<li>\n<p data-path-to-node=\"15,0,0\">Los archivos originales NO se eliminan.<\/p>\n<\/li>\n<li>\n<p data-path-to-node=\"15,1,0\">Los archivos <code data-path-to-node=\"15,1,0\" data-index-in-node=\"13\">.dot<\/code> y <code data-path-to-node=\"15,1,0\" data-index-in-node=\"20\">.dotx<\/code> (plantillas de Word) pasan a ser documentos normales.<\/p>\n<\/li>\n<li>\n<p data-path-to-node=\"15,2,0\"><b data-path-to-node=\"15,2,0\" data-index-in-node=\"0\">Importante:<\/b> Si alguna plantilla conten\u00eda macros, estas no se conservar\u00e1n en el nuevo <code data-path-to-node=\"15,2,0\" data-index-in-node=\"85\">.docx<\/code>.<\/p>\n<h2 data-path-to-node=\"17\">2. Paso a Paso: L\u00f3gica de Generaci\u00f3n del Documento<\/h2>\n<p data-path-to-node=\"18\">El proceso de generaci\u00f3n debe realizarse utilizando rutas absolutas para evitar que Tomcat pierda la referencia del archivo.<\/p>\n<h2 data-path-to-node=\"19\">2.1 Obtener la ruta de la plantilla<\/h2>\n<p data-path-to-node=\"20\">Se debe utilizar el procedimiento que devuelve la ruta f\u00edsica (<code data-path-to-node=\"20\" data-index-in-node=\"63\">PCrtFileWeb<\/code>), descartando versiones anteriores (<code data-path-to-node=\"20\" data-index-in-node=\"111\">PCrtFile<\/code> o <code data-path-to-node=\"20\" data-index-in-node=\"122\">PCrtFileV2<\/code>) que no lo hac\u00edan.<br \/><strong>Fragmento de codigo a modo ejemplo:<\/strong><\/p>\n<p>\/\/ Obtiene la ruta f\u00edsica de la plantilla en &amp;PlantillaPath<br \/>Call(PCrtFileWeb, &amp;PlaCod, &#8221;, &amp;NomArcDes, &#8216;N&#8217;, &amp;PlaAbr, &amp;Archi, &amp;CantCopias, &amp;Ok, &amp;Mensaje, &amp;PlantillaPath)<\/p>\n<p>If &amp;Ok &lt;&gt; &#8216;S&#8217;<br \/>\u00a0 \u00a0 \u00a0Msg(&#8216;Error al obtener plantilla: &#8216; + &amp;Mensaje)<br \/>\u00a0 \u00a0 \u00a0Return<br \/>EndIf<\/p>\n<h2 data-path-to-node=\"22\">2.2 Generaci\u00f3n del nombre y manejo de concurrencia<\/h2>\n<p data-path-to-node=\"23\">Para evitar que dos usuarios pisen el mismo archivo si generan un documento al mismo tiempo, se a\u00f1ade un <i data-path-to-node=\"23\" data-index-in-node=\"105\">timestamp<\/i> al nombre del archivo de salida.<br \/><strong>Fragmento de codigo a modo ejemplo:<\/strong><\/p>\n<p>&amp;Now = ServerNow()<br \/>&amp;SufijoHora = Trim(Str(Year(&amp;Now), 4, 0)) + Trim(Str(Month(&amp;Now), 2, 0)) + Trim(Str(Day(&amp;Now), 2, 0)) + Trim(Str(Hour(&amp;Now), 2, 0)) + Trim(Str(Minute(&amp;Now), 2, 0)) + Trim(Str(Second(&amp;Now), 2, 0))<\/p>\n<p>\/\/ OBLIGATORIO: Usar ruta f\u00edsica absoluta del servidor (Tomcat)<br \/>&amp;DirectorioTomcat = !&#8221;C:\\Program Files\\Apache Software Foundation\\Tomcat 10.1\\static\\publictempstorage\\&#8221;<br \/>&amp;PathFinal = &amp;DirectorioTomcat + Trim(&amp;NomArcDes) + !&#8221;_&#8221; + &amp;SufijoHora + !&#8221;.docx&#8221;<\/p>\n<h2 data-path-to-node=\"25\">2.3 Reemplazo de Tags con DocxHelper<\/h2>\n<p data-path-to-node=\"26\">La l\u00f3gica de reemplazo consta de dos fases cr\u00edticas:<\/p>\n<ol start=\"1\" data-path-to-node=\"27\">\n<li>\n<p data-path-to-node=\"27,0,0\"><b data-path-to-node=\"27,0,0\" data-index-in-node=\"0\">Reemplazo Inicial (Creaci\u00f3n):<\/b> Lee de la <code data-path-to-node=\"27,0,0\" data-index-in-node=\"40\">&amp;PlantillaPath<\/code> original y genera el archivo nuevo en <code data-path-to-node=\"27,0,0\" data-index-in-node=\"93\">&amp;PathFinal<\/code>.<\/p>\n<\/li>\n<li>\n<p data-path-to-node=\"27,1,0\"><b data-path-to-node=\"27,1,0\" data-index-in-node=\"0\">Reemplazos Subsiguientes (Actualizaci\u00f3n):<\/b> Leen de <code data-path-to-node=\"27,1,0\" data-index-in-node=\"50\">&amp;PathFinal<\/code> y sobrescriben el mismo <code data-path-to-node=\"27,1,0\" data-index-in-node=\"85\">&amp;PathFinal<\/code>.<\/p>\n<p><strong>Fragmento de codigo a modo ejemplo:<\/strong><\/p>\n<\/li>\n<\/ol>\n<p>\/\/ 1. EL PRIMER REEMPLAZO: Crea el archivo final a partir de la plantilla<br \/>DocxHelper.replaceText(&amp;PlantillaPath, &amp;PathFinal, &#8216;&lt;&lt;FECHA&gt;&gt;&#8217;, Trim(dtoc(&amp;Today)))<\/p>\n<p>\/\/ 2. TODOS LOS DEM\u00c1S REEMPLAZOS: Operan sobre el archivo reci\u00e9n creado<br \/>&amp;HoyLetras = Trim(str(Day(&amp;Today))) + &#8216; de &#8216; + Trim(cmonth(&amp;Today)) + &#8216; de &#8216; + Trim(str(year(&amp;Today)))<br \/>DocxHelper.replaceText(&amp;PathFinal, &amp;PathFinal, &#8216;&lt;&lt;FECHALETRAS&gt;&gt;&#8217;, Trim(&amp;HoyLetras))<br \/>DocxHelper.replaceText(&amp;PathFinal, &amp;PathFinal, &#8216;&lt;&lt;HORA&gt;&gt;&#8217;, Trim(Time()))<br \/>\/\/ &#8230; continuar con el resto de las variables &#8230;<\/p>\n<h2 data-path-to-node=\"30\">3. Paso a Paso: Descarga del Archivo (HTTP Response)<\/h2>\n<p data-path-to-node=\"31\"><b data-path-to-node=\"31\" data-index-in-node=\"0\">\u26a0\ufe0f REGLA DE ARQUITECTURA CR\u00cdTICA:<\/b> La manipulaci\u00f3n de <code data-path-to-node=\"31\" data-index-in-node=\"53\">&amp;HttpResponse<\/code> para forzar la descarga <b data-path-to-node=\"31\" data-index-in-node=\"91\">NUNCA<\/b> debe hacerse dentro de un evento de un Web Panel. Hacerlo corromper\u00e1 el HTML de la pantalla. Todo el c\u00f3digo de generaci\u00f3n y descarga debe estar en un <b data-path-to-node=\"31\" data-index-in-node=\"247\">Procedure (Procedimiento)<\/b> configurado de la siguiente manera:<\/p>\n<ul data-path-to-node=\"32\">\n<li>\n<p data-path-to-node=\"32,0,0\"><b data-path-to-node=\"32,0,0\" data-index-in-node=\"0\">Main program:<\/b> <code data-path-to-node=\"32,0,0\" data-index-in-node=\"14\">True<\/code><\/p>\n<\/li>\n<li>\n<p data-path-to-node=\"32,1,0\"><b data-path-to-node=\"32,1,0\" data-index-in-node=\"0\">Call protocol:<\/b> <code data-path-to-node=\"32,1,0\" data-index-in-node=\"15\">HTTP<\/code><\/p>\n<\/li>\n<\/ul>\n<h2 data-path-to-node=\"33\">3.1 Configuraci\u00f3n de Cabeceras y Descarga<\/h2>\n<p data-path-to-node=\"34\">Al final del Procedure, una vez generado el <code data-path-to-node=\"34\" data-index-in-node=\"44\">.docx<\/code>, se inyecta el binario en la respuesta HTTP:<\/p>\n<p>If &amp;PlaImp = &#8216;Y&#8217; OR &amp;PlaAbr = &#8216;Y&#8217;<br \/>&amp;OutputFile.Source = &amp;PathFinal \/\/ Asignamos la ruta absoluta<\/p>\n<p>If &amp;OutputFile.Exists()<br \/>&amp;NombreArchivoDescarga = &amp;OutputFile.GetName() + !&#8221;.docx&#8221;<\/p>\n<p>\/\/ Configuramos cabeceras para forzar descarga de Word<br \/>&amp;HttpResponse.AddHeader(!&#8221;Content-Type&#8221;, !&#8221;application\/vnd.openxmlformats-officedocument.wordprocessingml.document&#8221;)<br \/>&amp;HttpResponse.AddHeader(!&#8221;X-Frame-Options&#8221;, !&#8221;deny&#8221;)<br \/>&amp;HttpResponse.AddHeader(!&#8221;X-Content-Type-Options&#8221;, !&#8221;nosniff&#8221;)<\/p>\n<p>\/\/ Instrucci\u00f3n para el navegador: &#8220;Descarga esto&#8221;<br \/>&amp;HttpResponse.AddHeader(!&#8221;Content-Disposition&#8221;, !&#8221;attachment;filename=&#8221; + Trim(&amp;NombreArchivoDescarga))<\/p>\n<p>\/\/ Enviamos el archivo f\u00edsico<br \/>&amp;HttpResponse.AddFile(&amp;OutputFile.GetAbsoluteName())<br \/>Else<br \/>msg(&#8216;Error cr\u00edtico: No se pudo encontrar el documento generado en la ruta: &#8216; + &amp;PathFinal)<br \/>EndIf<br \/>EndIf<\/p>\n<p><\/p>\n<h2 data-path-to-node=\"36\">3.2 Invocaci\u00f3n desde el Web Panel<\/h2>\n<p data-path-to-node=\"37\">Para ejecutar este procedimiento desde la pantalla del usuario sin romper la interfaz, se debe utilizar el m\u00e9todo <code data-path-to-node=\"37\" data-index-in-node=\"114\">.Link()<\/code>.<br \/><strong>Fragmento de codigo a modo ejemplo:<\/strong><br \/>Event &#8216;DescargarDocumento&#8217;<br \/>\/\/ Se invoca al procedure pas\u00e1ndole los par\u00e1metros necesarios<br \/>&amp;UrlDescarga = PimpCedNew.Link(&amp;ORGCOD, &amp;TDeNume, &amp;TDeSec, &amp;ICPCod, &amp;PlaCod, &#8216;N&#8217;, &#8216;Y&#8217;, &#8216;Y&#8217;)<\/p>\n<p>\/\/ Navegamos a esa URL, el navegador detectar\u00e1 los headers y bajar\u00e1 el archivo<br \/>Link(&amp;UrlDescarga)<br \/>EndEvent<\/p>\n<p data-path-to-node=\"20\">\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/<br \/>Espero que les alla sido util y cualquier cosa mi codigo completo esta en el archivo PImpCedNew, el cual es llamado desde CerDeuWW.<\/p>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p data-path-to-node=\"12\">\n<p data-path-to-node=\"9,3,0\">\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>1. Requisitos Previos e Instalaci\u00f3n 1.1 Librer\u00edas Java (Apache POI) Para que DocxHelper funcione correctamente, el entorno debe contar con las librer\u00edas de Apache POI. Asegurarse de tener los siguientes archivos .jar (versi\u00f3n 5.2.2 o superior) en el classpath del servidor\/proyecto:Subi los archivos a una carpeta de Google Drive para el que las necesite. Link [&hellip;]<\/p>\n","protected":false},"author":10,"featured_media":634,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[20],"tags":[],"class_list":["post-631","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-docs"],"_links":{"self":[{"href":"https:\/\/wiki.gobiernoriocuarto.gob.ar\/index.php?rest_route=\/wp\/v2\/posts\/631","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wiki.gobiernoriocuarto.gob.ar\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wiki.gobiernoriocuarto.gob.ar\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wiki.gobiernoriocuarto.gob.ar\/index.php?rest_route=\/wp\/v2\/users\/10"}],"replies":[{"embeddable":true,"href":"https:\/\/wiki.gobiernoriocuarto.gob.ar\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=631"}],"version-history":[{"count":1,"href":"https:\/\/wiki.gobiernoriocuarto.gob.ar\/index.php?rest_route=\/wp\/v2\/posts\/631\/revisions"}],"predecessor-version":[{"id":633,"href":"https:\/\/wiki.gobiernoriocuarto.gob.ar\/index.php?rest_route=\/wp\/v2\/posts\/631\/revisions\/633"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wiki.gobiernoriocuarto.gob.ar\/index.php?rest_route=\/wp\/v2\/media\/634"}],"wp:attachment":[{"href":"https:\/\/wiki.gobiernoriocuarto.gob.ar\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=631"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wiki.gobiernoriocuarto.gob.ar\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=631"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wiki.gobiernoriocuarto.gob.ar\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=631"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}