viernes, 11 de febrero de 2011

C#: Acceso directo a ficheros de Word 2010

- Introducción.

A veces es útil acceder directamente a un fichero de Word en formato .docx desde un programa en C#. Por ejemplo, yo lo uso para generar documentos personalizados más allá de lo que permite la opción "combinar correspondencia", insertar imágenes, etc.

Antes, los ficheros de word eran binarios y esas tareas había que hacerlas a través de la aplicación Word con  objetos COM. Desde la introducción del formato open XML (.docx), el mundo es un lugar mejor para los programadores.

- El meollo de la cuestión.

Un fichero .docx no es más que un fichero comprimido .zip , que contiene todos los ficheros e imágenes del documento de Word en formato XML.

Si quieres hacer la prueba, crea un fichero prueba.docx, escribe algo de texto, inserta una imagen, guárdalo, sal de Word, renómbralo como prueba.xml, y ábrelo. Verás un fichero XML, y varias carpetas. Entra en la carpeta "word", y abre el fichero "document.xml" Ahí está tu texto. Si abres la carpeta "media", verás tus imágenes.

Finalmente, cierra los ficheros, y renombra la carpeta prueba.zip por prueba.docx Podrás abrirlo sin problema con word.

En los siguientes apartados veremos cómo modificar el fichero a través de C# Voy a usar las siguientes variables:

string directorio_trabajo = Path.GetTempPath();
string fichero_entrada ="prueba";
string fichero_salida = "prueba2.docx";

... donde, evidentemente, directorio_trabajo es un directorio temporal donde comprimir/descomprimir los ficheros, fichero_entrada es el fichero "plantilla" que nos va a servir de base para generar la documentación, y fichero_salida va a contener el resultado.

En los ejemplos de código siguientes, vamos a suponer que fichero_entrada se encuentra dentro del directorio_trabajo.

- Descomprimiendo el fichero.

Hace años, cuando investigué la forma de acceder a ficheros OpenXML, C# no tenía forma de acceder a una carpeta ZIP. La solución que encontré (no muy elegante), es usar un programa externo para comprimir y descomprimir los ficheros. Busca (en Google) una versión libre de zip.exe y unzip.exe como esta....

http://stahlworks.com/dev/index.php?tool=zipunzip

Una vez tengas esos ejecutables, añádelos a tu proyecto, y usa este código para llamarlo:

string orden = "\"" + Application.StartupPath + "\\unzip.exe\"";
string parametros = "\"" + Application.StartupPath + "\\prueba.docx\"";


System.Diagnostics.Process p = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo info = new System.Diagnostics.ProcessStartInfo(orden);
info.UseShellExecute = true;
info.Arguments = parametros;
info.WorkingDirectory = dir_trabajo;
info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;


p.StartInfo = info;
while (!p.Start()) ;
while (!p.HasExited) ;


Dependiendo de dónde esté tu fichero docx, tendrás que ajustar un poco las rutas de los ficheros en tu código.

- Cambiando el texto del fichero.

Dentro del árbol de ficheros que aparece tras descomprimir el .docx, debes abrir el fichero \word\document.xml.

La forma elegante de hacer los cambios es cargar el fichero XML con la clase XmlDocument , buscar los nodos que contienen el texto que quieres sustituir y volver a crear el fichero. Desgraciadamente, me pagan por programar rápido y no por programar elegantemente :)

La forma más rápida de hacer cambios es cargar el fichero XML en una cadena de texto:

// Leemos toda la cadena en un fichero.
string documento = File.ReadAllText(fichero + "\\word\\document.xml");


// Pon aquí los cambios que necesites. En el ejemplo, sustituyo la cadena "{nombre}" en 
// la plantilla por un nombre cualquiera.
documento = documento.Replace("{nombre}", "Armando Bronca Segura");


// Volvemos a grabar el fichero.
File.WriteAllText(fichero + "\\word\\document.xml", documento);

- Cambiando imágenes.

La forma más sencilla de cambiar imágenes en un documento es insertar una imagen desde Word en la plantilla, y luego sustituirla por otra en tu programa.

Las imágenes están en la carpeta \word\media\image1.gif , \word\media\image2.gif ... Antes de de escribir el programa, debes ver qué nombre le ha dado Word a tu imagen, para poder sustituirla por otra.

Si tienes un fichero con la nueva imagen, solo tienes que copiarlo usando la librería IO:

File.Copy ("mi_imagen.gif", @" \word\media\image2.gif");

Si quieres copiar el contenido del portapapeles como una imagen, usa este código:

if (Clipboard.GetDataObject() != null)
{
         IDataObject data = Clipboard.GetDataObject();


         if (data.GetDataPresent(DataFormats.Bitmap))
         {
                  Image image = (Image)data.GetData(DataFormats.Bitmap, true);
                  image.Save(Directory.GetCurrentDirectory() + "\\" + fichero + "\\word\\media\\image1.gif", System.Drawing.Imaging.ImageFormat.Gif);
         }
}


- Comprimiendo el fichero.

Una vez hechos todos los cambios, hay que volver a crear el documento .docx

string orden = "\"" + Application.StartupPath + "\\zip.exe\"";
string parametros = " -r -9 \"" + dir_salida + @"\prueba2.docx\" *";

System.Diagnostics.Process p = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo info = new System.Diagnostics.ProcessStartInfo(orden);
info.UseShellExecute = true;
info.Arguments = parametros;
info.WorkingDirectory = dir_trabajo;
info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;

p.StartInfo = info;
while (!p.Start()) ;
while (!p.HasExited) ;

- Abriendo el fichero.


Probablemente querrás abrir el fichero con Word  desde tu aplicación para que el usuario no tenga que buscarlo y abrirlo él.

System.Diagnostics.Process p = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo info = new System.Diagnostics.ProcessStartInfo(plantilla);
info.UseShellExecute = true;
p.StartInfo = info;
while(!p.Start());

Saludos :)

No hay comentarios:

Publicar un comentario