viernes, 5 de octubre de 2012

Lazarus y OpenOffice

Para abrir este blog, os propongo un vistazo de como crear documentos y manipularlos desde Lazarus con el OpenOffice (en windows).
En este caso el documento a crear es una hoja de calculo.
A menudo es rentable volcar datos elaborados desde un programa a una hoja de calculo puesto que importarlos en bruto, aunque sea desde una elaborada sentencia SQL y dependiendo de la complejidad es un autentico martirio para el usuario. Se puede tomar varias aproximaciones como crear un fichero que sirva de puente para exportar/importar los datos pero queda mucho más practico que el usuario desde su programa de gestión habitual con solo pulsar un botón  abra (o no) la hoja de calculo con los datos que previamente haya seleccionado.
Lo bonito de los paquetes tipo office es que tienen un mecanismo de comunicación y son programables.
Este bocado de código es para crear una hoja de calculo gracias a la capa COM automation sobre el UNO del OpenOffice en windows.
A estas alturas todos sabemos que es OpenOffice, pero poca gente sabe que es el UNO. UNO es una interface de componentes propia del openoffice tal como CORBA, COM etc.., permite crear objetos de openoffice y manipularlos como una extensión del lenguaje que utilizas, en nuestro caso FreePascal.
Como hasta la fecha no hay un puente aún escrito entre UNO y FreePascal la forma más practica de hacer lo que queremos es usando el Component Object Model puesto que el OO para windows proporciona su UNO envuelto en COM.

 

(** *  *   *    *     *      *       *      *     *    *   *  * * ** 
Ejemplo para pasar datos a una hoja de calculo de OpenOffice, 
Lazarus para Com / Windows.
(c)2011 - Pascal Peregrina Castillo. (chutipascal)
Para más información
http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/OpenOffice.org_Developers_Guide 

 ** *  *   *    *     *      *       *      *     *    *   *  * * **)

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls, ComObj, Variants;

type

{ TForm1 }

TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
SaveDialog1: TSaveDialog;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);

private
procedure CreaHoja(ModoOculto : Boolean);
{ private declarations }
public
{ public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }




procedure TForm1.Button1Click(Sender: TObject);
begin
CreaHoja(False); // presenta OO con la hoja por pantalla
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
CreaHoja(True); // Abre OpenOffice en modo oculto, genera la hoja y la guarda
end;

procedure TForm1.CreaHoja(ModoOculto : Boolean);
var
ServiceManager, Desktop, Document, Sheet, Range, wProperties, formula : Variant;
args : Variant;
V : variant;
j: Integer;
begin

// Abre el ServiceManager del OpenOffice


ServiceManager := CreateOleObject('com.sun.star.ServiceManager');

// Crea un Desktop


Desktop := ServiceManager.createInstance('com.sun.star.frame.Desktop');
args:= VarArrayCreate([0, 0], varVariant);
args[0] := ServiceManager.Bridge_GetStruct('com.sun.star.beans.PropertyValue');
if ModoOculto then
begin
    args[0].Name := 'Hidden';
    args[0].Value := True;
end;


// LLamamos a la 'fabrica' pidiendo una hoja de calculo en blanco.
Document := Desktop.loadComponentFromURL('private:factory/scalc', '_blank', 0, args);
Sheet := Document.getSheets.getByIndex(0);

V:='Fecha:'+DateToStr(Date); 


Sheet.getcellbyposition(1,0).setString('Fecha:'+DateToStr(date));


// Ojo con la codificación de carácteres nos puede juegar una mala pasada.

Sheet.getcellbyposition(1,0).setString('HOJA DE PRUEBAS');
Sheet.getcellbyposition(2,0).setString(V);


// Cambia propiedades de los caracteres en fila 3

Range := Sheet.getCellRangeByName('A3:F3');
Range.charHeight := 14;
Range.SetPropertyvalue('CharPosture', 1); // ITALIC
Range.SetPropertyValue('CharWeight',1); // BOLD
Range.SetPropertyValue('CellBackColor', 13421823);


// Unos titulos en la fila 3 (2 en base 0)


V:=utf8toansi('Familia');
Sheet.getcellbyposition(0,2).setString(V);
V:=utf8toansi('Código'); // V es una variable de tipo variant, las cadenas que queremos pasar de Lazarus (utf8)
Sheet.getcellbyposition(1,2).setString(V); // deben antes cambiarse a ansi para que las tildes, eñes y cedillas se conserven.
V:=utf8toansi('Descripción');
Sheet.getcellbyposition(2,2).setString(V);
Sheet.getcellbyposition(3,2).setString('Coste'); // Estas cadenas pasaran correctamente puesto que no tienen caracteres especiales.
Sheet.getcellbyposition(4,2).setString('Stock'); // Aunque como norma general es mejor pasar variables y tipos variants si trabajamos con COM
Sheet.getcellbyposition(5,2).setString('Valor Stock.');




// Para añadir registros de una base de datos
// Esta sería la idea.
//
// Por ej.
//
// j:=0;
// ZQuery1.first;
// while not(zquery1.eof)
// begin
//     Sheet.getcellbyposition(3,j).setvalue(ZQuery1.fields[0].value);
//     inc(j);
// end;
//
// Las cadenadas y las formulas se pasan con setFormula()
// Hay que recordar que la base de la tabla es 0 para programar y 1 para el usuario así que
// para ir a la celda A1 tenemos que poner getcellbyposition(0,0)


j:=3;


Sheet.getcellbyposition(0,j).setString('LICORES'); // Unas cadenas pasadas a pelo, aunque sería mejor pasar una variable variant con
Sheet.getcellbyposition(1,j).setString('D78585'); // con el string original pasado a ANSI
Sheet.getcellbyposition(2,j).setString('BOTELLA RON');
Sheet.getcellbyposition(3,j).setvalue(7.50); // Unos valores númericos
Sheet.getcellbyposition(4,j).setvalue(10);
Formula:='= D'+trim(IntToStr(j+1))+'*E'+trim(IntToStr(j+1)); // ilustra el tema de la base 0 para las celdas.
Sheet.getcellbyposition(5,j).setFormula(Formula); // Una formula = D4*E4

inc(j);

// Misma historia.

Sheet.getcellbyposition(0,j).setString('VINOS');
Sheet.getcellbyposition(1,j).setString('V11225');
Sheet.getcellbyposition(2,j).setString('BOTELLA VINO');
Sheet.getcellbyposition(3,j).setvalue(2.50);
Sheet.getcellbyposition(4,j).setvalue(60);
Formula:='= D'+trim(IntToStr(j+1))+'*E'+trim(IntToStr(j+1));
Sheet.getcellbyposition(5,j).setFormula(Formula); // Una formula = D5*E5

// Cambiamos el formato de los valores númericos.
// Valor 4 a Numberformat porque con las pruebas es el que hace -1.234,56
Range:= Sheet.getCellRangeByName ('D4:F5');
Range.setPropertyValue( 'NumberFormat', 4 );

// Cambia el ancho de las columna b, c y f
Sheet.getColumns.getByIndex(1).SetPropertyValue('Width',8000);
Sheet.getColumns.getByIndex(2).SetPropertyValue('Width',8000);
Sheet.getColumns.getByIndex(5).SetPropertyValue('Width',7000);


if modooculto then
begin
    SaveDialog1.DefaultExt:='.xls'; // Definir el archivo como excel


    // Preparar par guardar la hoja
    wProperties := VarArrayCreate([0,0], varVariant);
    wProperties[0] := ServiceManager.Bridge_GetStruct('com.sun.star.beans.PropertyValue');
    wProperties[0].Name := 'FilterName';
    wProperties[0].Value := 'MS Excel 97'; // Consultar doc para más formatos..

    try
        SaveDialog1.Execute;
        V:='file:///'+StringReplace(Savedialog1.FileName,'\','/',

            [rfIgnoreCase,rfReplaceAll]);
        Document.StoreASURL(V, wProperties);
    except
    // Recuperar excepción si hubo error....
    end; // del try


    // Termina y elimina el OpenOffice de la memoria
    Document.Close(false);
    Document := Unassigned;
    Desktop.Terminate;
    Desktop := UnAssigned;
end; // de if modooculto
end; // de la procedure
end.