jodconverter für OpenOffice Dateiformatkonvertierungen

Der zweite Schritt meiner Rechnungserstellung ist die Konvertierung in PDF. Auch dazu gibt es eine prima Bibliothek: http://www.artofsolving.com/opensource/jodconverter. (Einziger Nachteil: viele Jars.) Damit kann man Word, PowerPoint, RTF und alles andere in ein bliebiges Zielformat bringen.

private static void ensureStartedOpenOfficeService()
{
try
{
new Socket( "127.0.0.1", 8100 );
}
catch ( Exception e )
{
String path = "C:/Programme/OpenOffice.org 2.3/program/";
ProcessBuilder processBuilder = new ProcessBuilder( path+"soffice", "-headless", "-accept=\"socket,host=127.0.0.1,port=8100;urp;\"", "-nofirststartwizard" );
try
{
processBuilder.start();
}
catch ( IOException ioe )
{
throw new RuntimeException( ioe );
}
}
}
public static void convert( String source, String destination )
{
ensureStartedOpenOfficeService();

OpenOfficeConnection connection = null;
try
{
connection = new SocketOpenOfficeConnection( 8100 );
connection.connect();
DocumentConverter converter = new OpenOfficeDocumentConverter( connection );
File inputFile = new File( source );
File outputFile = new File( destination );
converter.convert( inputFile, outputFile );
}
catch ( ConnectException e )
{
throw new RuntimeException( e );
}
finally
{
connection.disconnect();
}
}

Für meine Rechnungen also:

String destination    = "S:/Private/Traida/Bills/bill1";
String destinationOds = destination + ".ods";
String destinationPdf = destination + ".pdf";
convert( destinationOds, destinationPdf );

OpenOffice als Template-Engine: Dokument einlesen, verändern, schreiben

Da ich meine Rechnungen automatisch generiert und als PDF konvertiert haben möchte, habe nach einer Lösung gesucht, wie ich die Rechnungsdaten aus einer Datenquelle in der Template einsetzten kann, und am Ende eine PDF bekomme. Klar sind Report-Generatoren dafür auf der Welt, aber meine Vorlagen möchte ich nicht für Eclipse BIRT oder Jasper schreiben, sondern in Word bzw. OpenOffice. RTF ist relativ leicht zu schreiben und für ein Template meines Erachtens ganz gut. Meine Gedanken kreisten daher einige Zeit um RTF->PDF, doch da gibt es keine freie Lösung. Auch Wege wie RTF->FO->PDF sind möglich, aber dafür gibt es ebenfalls keine leichtgewichtigen freien Lösungen.

Schon für meinen PowerPoint->PDF-Konverter habe ich mit OpenOffice gearbeitet und das klappte ganz gut. Das habe ich für meine Templates nun wieder überlegt — ein recht harter Weg für simple RTFs zwar, aber es funktioniert. Doch anstatt RTF zu nutzen, wollte ich gleich das XML-Format verwenden. Dazu muss man wissen, dass OO ein Zip-Archiv für das OO-Dokument vorsieht und dort in einer XML-Datei den Content ablegt. Mit der großartigen Open-Source-Bibliothek https://truezip.dev.java.net/ ist der Zugriff auf Archive sehr einfach.

Mit TrueZIP ist eine einfache Lösung entstanden, ein OO-Dokument mit Template-Anweisungen wie ${address} zu lesen, Ersetzungen vorzunehmen, und alles wieder zu schreiben. Diesen Text hier zu schreiben hat länger gedauert, als die 90 Zeilen Quellcode. Also los:


import java.io.*;
import java.nio.channels.FileChannel;
import de.schlichtherle.io.ArchiveDetector;

public class OpenOfficeUtils
{
public static void main( String[] args )
{
String source = "S:/Private/Traida/Bills/template.ods";
String destination = "S:/Private/Traida/Bills/bill1.ods";

copyFile( source, destination );

String content = readOpenOfficeContent( source );

content = content.replace( "${addressline1}", "Christian Ullenboom" );

writeOpenOfficeContent( destination, content );
}

public static String readOpenOfficeContent( String filename )
{
Reader is = null;

try
{
de.schlichtherle.io.File file = new de.schlichtherle.io.File( filename + "/content.xml", ArchiveDetector.ALL );
char[] fileContent = new char[ (int) file.length() ];
is = new de.schlichtherle.io.FileReader( file ); // TODO: <?xml version="1.0" encoding="UTF-8"?>
is.read( fileContent );

return new String(fileContent);
}
catch ( IOException e )
{
throw new IllegalArgumentException( e );
}
finally
{
try { is.close(); } catch ( Exception e ) { }
}
}

public static void writeOpenOfficeContent( String filename, String content )
{
Writer os = null;

try
{
de.schlichtherle.io.File file = new de.schlichtherle.io.File( filename + "/content.xml", ArchiveDetector.ALL );
os = new de.schlichtherle.io.FileWriter( file );
os.write( content );
}
catch ( IOException e )
{
throw new IllegalArgumentException( e );
}
finally
{
try { os.close(); } catch ( Exception e ) { }
}
}

public static void copyFile( String in, String out )
{
FileChannel inChannel = null;
FileChannel outChannel = null;

try
{
inChannel = new FileInputStream( new File(in) ).getChannel();
outChannel = new FileOutputStream( new File(out) ).getChannel();
inChannel.transferTo( 0, inChannel.size(), outChannel );
}
catch ( IOException e )
{
throw new IllegalArgumentException( e );
}
finally
{
try { inChannel.close(); } catch ( Exception e ) { }
try { outChannel.close(); } catch ( Exception e ) { }
}
}
}

SQL für Objekte: JoSQL (SQL for Java Objects)

JoSQL (SQL for Java Objects) unter http://josql.sourceforge.net/index.html ist eine Open-Source Biblitohek, um SQL-Anfragen an Objektgrafen zu stellen. Die Webseite gibt interessante Beispiele.

String query = "SELECT * FROM   java.io.File WHERE  name LIKE '%.mp3'";
Query q = new Query();
q.parse( query );
QueryResults results = q.execute( Arrays.asList( new File("C:/Data/Musik/").listFiles() ) );
System.out.println( results.getResults() );

Oder für Ausdrücke:

/*
* Use as a file filter.
*/
String exp = "lastModified BETWEEN toDate('10/May/2007') AND toDate('28/Jun/2007') " +
"AND " +
"length >= 10 * 1024" +
"AND " +
"path LIKE '%/subdir/%'";

ExpressionEvaluator ee = new ExpressionEvaluator (exp, File.class);

if (ee.isTrue (myfile))
{
// Process the file.
}

TeX-Formulate in Webseiten mit jsMath

http://www.math.union.edu/~dpvc/jsMath/ ist eine Layout-Engine, die TeX-Formulare in Webseiten rendert. Dabei werden nicht, wie bei anderen Ansätzen, Grafiken generiert, sondern die Ausdrücke mit CSS positioniert. Damit skaliert das Ganze schön. Das Ergebnis ist beeindruckend und die Engine wird auch von einigen Wiki-Systemen verwendet. Einige Beispiele: Examples of jsMath. Im Interactive jsMath lab kann man Formen eingeben und es kommt HTML raus, was man nur noch zusammen mit dem CSS auf die eigene Webseite setzen muss. Aus x_i^2 wird etwa:

<SPAN CLASS=“typeset“><nobr><span class=“scale“><span class=“icmmi10″>x</span><span style=“position: relative; top:0.372em;“><span class=“size2″><span class=“icmmi10″>i</span></span><span class=“spacer“ style=“margin-left:0.05em“></span></span><span style=“position: relative; margin-left:-0.260em; top:-0.362em;“><span class=“size2″><span class=“icmr10″>2</span></span><span class=“spacer“ style=“margin-left:0.05em“></span></span><span class=“blank“ style=“height:1.331em;vertical-align:0.908em“></span></span></nobr></SPAN>

Jetzt fehlen nur noch jsMath.js und, falls etwa Summen-Zeichen oder sonstiges Sonderzeichen verwendet werden, im fonts-Ordner die TeX-Fonts.

PHP in purem Java mit Quercus

Quercus (http://quercus.caucho.com/) implementiert eine PHP 5 Engine in purem Java. Die Software ist unter LGPL und stammt von Caucho, die auch den performanten Resin-Server implementieren. Laut Herstellerangaben laufen bisher

Von den PHP-Funktionen fehlen bisher:

  • call_user_method, call_user_method_array, get_declared_interfaces, interface_exists, property_exists
  • date_default_timezone_get, date_default_timezone_set, date_sunrise, date_sunset, idate, localtime, strptime
  • debug_print_backtrace, restore_exception_handler, set_exception_handler
  • exif_read_data, exif_thumbnail, exif_tagname
  • disk_free_space (stub), disk_total_space (stub), readlink (stub), umask (stub)
  • register_tick_function, unregister_tick_function
  • bind_textdomain_codeset (stub)
  • headers_list, setrawcookie
  • ob_iconv_handler
  • imagecolorclosesthwb, imagecolorset, imagecolorstotal, imageftbbox, imagefttext, imagegammacorrect, imageinterlace, imagelayereffect, imageloadfont, imagepalettecopy, imagepsbbox, imagepscopyfont, imagepsencodefont, imagepsextendfont, imagepsfreefont, imagepsloadfont, imagepsslantfont, imagepstext, imagerotate, iptcembed, image2wbmp, jpeg2wbmp, png2wbmp, imagecreatefromgd2, imagecreatefromgd2part, imagecreatefromgd, imagegd2, imagegd
  • ezmlm_hash
  • mhash_keygen_s2k
  • connection_aborted, connection_status, connection_timeout, __halt_compiler, highlight_file, highlight_string, ignore_user_abort, php_check_syntax, php_strip_whitespace, show_source, sys_getloadavg, time_nanosleep, time_sleep_until
  • mysqli_debug, mysqli_disable_reads_from_master, mysqli_disable_rpl_parse, mysqli_info, mysqli_kill, mysqli_master_query, mysqli_report, mysqli_rpl_parse_enabled, mysqli_rpl_probe, mysqli_rpl_query_type, mysqli_send_long_data, mysqli_send_query, mysqli_server_end, mysqli_server_init, mysqli_ssl_set, mysqli_stmt_send_long_data, mysqli_thread_id, mysqli_thread_safe
  • dns_get_record, inet_ntop, inet_pton, long2ip, pfsockopen, socket_get_status, socket_get_blocking, socket_get_timeout
  • assert_options, dl (stub), get_current_user (stub), get_included_files, get_magic_quotes_gpc, get_magic_quotes_runtime, get_required_files, getopt, getrusage, ini_restore, memory_get_usage, php_ini_scanned_files, php_logo_gid, phpcredits, putenv, restore_include_path, set_magic_quotes_runtime, zend_logo_gid, zend_version
  • proc_close, proc_get_status, proc_nice, proc_open, proc_terminate
  • stream_bucket_append, stream_bucket_make_writeable, stream_bucket_new, stream_bucket_prepend, stream_filter_append, stream_filter_prepend, stream_filter_register, stream_filter_remove, stream_get_filters, stream_get_meta_data, stream_select, stream_set_blocking, stream_set_timeout (stub), stream_set_write_buffer (stub), stream_socket_accept, stream_socket_server, stream_socket_client, stream_socket_enable_crypto, stream_socket_get_name, stream_socket_pair, stream_socket_recvform, stream_socket_sendto, stream_socket_server
  • xml_error_string, xml_get_current_byte_index, xml_get_current_column_number, xml_get_current_line_number, xml_get_error_code, xml_set_external_entity_ref_handler
  • und noch ein paar

Aus PHP heraus lassen sich Java-Objekte bauen und auf die gesamte API zugreifen:

<?php

$a = new java(„java.util.Date“, 1234567);

echo $a->day();

?>

Neu eingeführt in PHP wurde auch das import Schlüsselwort.

Beiträge, die sich mit Quercus beschäftigen und zeigen, wie das MediaWiki und Forums-Software läuft, sind:

Axis2/XFire Zoff. Der Gewinner: JAXWS 2.1?

Im Moment tobt der Kampf zwischen Axis2 und XFire um die Performance-Krone. Einige Benchmarks zeigen Axis2 vorne, doch das wohl nur, weil eigentlich XML-Bindings-Frameworks (JAXB bei XFire, ADB (Axis2 Data Binding) bei Asix2), also vergleichen werden, und nicht der Serialisierer selbst. Andere wiederum erfreuen sich über die Rechtschreibfehler in der Axis2-Doku bis zur Frustration:

  • isOneHanlder
    boolean isOneHanlder
    this is want if the phaseFirst and phaseLst same hanlder that is for this phase there is only one phase
  • This send the SOAP Message to other SOAP nodes and this alone write the SOAP Message to the wire. Out flow must be end with one of this kind.
  • cloneOMElement
    public OMElement cloneOMElement()
    Clones this element. Since both elements are build compleletely, you will lose the differed building capability.
    Returns:
    Returns OMElement.

Im Moment sieht gar nicht so schlecht für die Referenzimplementierung aus, insbesondere seit der neuen Version JAXWS 2.1 FCS. Die Performance-Ergebnisse sind ausgezeichnet und Spring-Support gibt’s auch. Was will man mehr?

small.png

Einiges mehr zur Debatte:

SwingX Demos

Unter http://swinglabs.org/demos.jsp gibt’s ein Demo (JNLP) aller zentraler SwingX-Komponenten. Im Incubator ist auch einiges interssantes dabei — eine JXSplitButton Demo habe ich immer schon vermisst.

JNLP-Demos:

XFire + JSR 181 Annotations + Jetty = Simple Web-Service

package xfire;

import javax.jws.WebMethod;
import javax.jws.WebService;

@WebService( serviceName = "GreeterService" )
public class GreeterService
{
@WebMethod
public String greet( Person p )
{
return "Hello " + p.getName() + "!";
}
}


package xfire;

public class Person
{
private String name;

public Person()
{
}

public Person( String name )
{
this.name = name;
}


public String getName()
{
return name;
}

public void setName( String name )
{
this.name = name;
}
}


package xfire;

import org.codehaus.xfire.XFire;
import org.codehaus.xfire.XFireFactory;
import org.codehaus.xfire.annotations.AnnotationServiceFactory;
import org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations;
import org.codehaus.xfire.server.http.XFireHttpServer;
import org.codehaus.xfire.service.Service;

public class ServiceServer
{
public static void main( String[] args ) throws Exception
{
XFire xfire = XFireFactory.newInstance().getXFire();
AnnotationServiceFactory factory = new AnnotationServiceFactory(
new Jsr181WebAnnotations(),
xfire.getTransportManager() );
Service service = factory.create( GreeterService.class );
xfire.getServiceRegistry().register( service );
new XFireHttpServer().start();
// http://localhost:8081/GreeterService?wsdl
}
}

package xfire;

import java.net.MalformedURLException;

import org.codehaus.xfire.client.XFireProxyFactory;
import org.codehaus.xfire.service.Service;
import org.codehaus.xfire.service.binding.ObjectServiceFactory;

public class ServiceClient
{
public static void main( String[] args ) throws MalformedURLException
{
ObjectServiceFactory serviceFactory = new ObjectServiceFactory();
Service serviceModel = serviceFactory.create( GreeterService.class );

GreeterService service = (GreeterService)
new XFireProxyFactory().create( serviceModel,
"http://localhost:8081/GreeterService" );
String s = service.greet( new Person( "Ulli" ) );
System.out.println( s );
}
}

Jettison – JSON-Dokumente nach dem StAX-Modell schreiben

Viele JSON-Implementierungen nehmen ein direktes Mapping der Java-Objekte nach JSON vor. http://jettison.codehaus.org/ geht da einen anderen Weg. Mit den aus StAX bekannten Methoden zum Schreiben/Lesen von Elementen wird nicht in ein XML-Format „gemarshalled“, sondern in das JSON-Format. Ein Testfall für MappedXMLStreamWriterTest macht deutlich, was hier vor sich geht:

StringWriter strWriter = new StringWriter();
MappedNamespaceConvention con = new MappedNamespaceConvention();
AbstractXMLStreamWriter w = new MappedXMLStreamWriter(con, strWriter); 

w.writeStartDocument();w.writeStartElement("root"); 

w.writeStartElement("child1");w.writeStartElement("subchild1");w.writeCharacters("test");w.writeEndElement(); 

w.writeStartElement("subchild2");w.writeCharacters("test");w.writeEndElement(); 

w.writeEndElement(); 

w.writeStartElement("child2");w.writeStartElement("subchild");w.writeCharacters("test");w.writeEndElement();w.writeEndElement(); 

w.writeEndElement();w.writeEndDocument(); 

w.close();strWriter.close(); 

System.out.println(strWriter.toString()); 

assertEquals("{\"root\":{" +"\"child1\":{\"subchild1\":\"test\",\"subchild2\":\"test\"}," +"\"child2\":{\"subchild\":\"test\"}}}",
             strWriter.toString());

Aus http://svn.jettison.codehaus.org/browse/jettison/trunk/src/test/java/org/codehaus/jettison/mapped/MappedXMLStreamWriterTest.java?r=11.

Ein Servlet/JSP würde statt in den StringWriter in den response Strom schreiben.

Prefuse: Immer wieder schöne Graphen

Das „prefuse visualization toolkit“ ist eine feine API zur Informationsvisualisierung. In der visualization gallery gibt es etwa folgendes zu sehen und auszuprobieren:

 


Small-World Networks by Stephen Frowe Ingram

 

 

Vizster by Jeffrey Heer and danah boyd

 

 

Flow Map Layout by Doantam Phan

 

 
NameVoyager by Martin Wattenberg, rebuilt by Jeffrey Heer

 

 
¢ongress by Jeffrey Heer

 

 
zipdecode by Ben Fry, rebuilt by Jeffrey Heer

 

 
TreeMap Demo by Jeffrey Heer

 

 
RadialGraphView Demo by Jeffrey Heer

 


TreeView Demo by Jeffrey Heer

 

 
GraphView Demo by Jeffrey Heer

 

 
FisheyeMenu Demo by Jeffrey Heer

 

 
DataMountain Demo by Jeffrey Heer

mime4j für MS-Web-Archive MHT

Während Word und Excel beim Export einzelnde (mehr oder wenig schöne) XML-Dateien erzeugen (können), macht PPT das nicht: Es generiert entweder eine Sammlung von XML/HTML/GIF/.. Dateien, oder packt alle Dateien in ein MHT-Datei. Möchte man diese Datei auseinandernehmen, kann man gut http://mime4j.sourceforge.net/apidocs/ nutzen, denn MS bündelt die Dokumente wie eine MIME-EMail. Zunächst benötigt man einen Handler:

package TEST;

import java.io.IOException;
import java.io.InputStream;

import org.mime4j.BodyDescriptor;
import org.mime4j.SimpleContentHandler;
import org.mime4j.message.Header;

public class MyHandler extends SimpleContentHandler
{
@Override
public void headers( Header header )
{
System.out.println( header );
}

@Override
public void bodyDecoded( BodyDescriptor bd, InputStream is ) throws IOException
{
System.out.println( bd );
}
}

Der macht jetzt nicht viel, man sieht aber das Prinzip.

Der Quellcode des Parsers ist auch kurz:

package TEST;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

import org.mime4j.ContentHandler;
import org.mime4j.MimeStreamParser;

public class Mime4J
{
public static void main( String[] args ) throws IOException
{
ContentHandler handler = new MyHandler();
MimeStreamParser parser = new MimeStreamParser();
parser.setContentHandler( handler );
parser.parse( new BufferedInputStream( new FileInputStream( „c:/a.mht“ ) ) );
}
}

Auf der Konsole folgen dann Ausgaben wie

MIME-Version: 1.0
Content-Type: multipart/related; boundary=“—-=_NextPart_01C617A0.DCEFFF40″

Content-Location: file:///C:/EC2C4D01/a.htm
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset=“us-ascii“

text/html
Content-Location: file:///C:/EC2C4D01/a-Dateien/master29.htm
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset=“us-ascii“

text/html
Content-Location: file:///C:/EC2C4D01/a-Dateien/master29.xml
Content-Transfer-Encoding: quoted-printable
Content-Type: text/xml; charset=“utf-8″

Ein Java-Programm (nicht quelloffen), was das schon alles macht — und für Mac/Linux ganz spannend ist, ist unmhtml von http://www.joecheng.com/code/.

JSmooth mit Ant Build Skript

JSmooth nutze ich schon eine ganze Zeit, um unter Windows ausführbare EXE zu
erzeugen. Statt der Gui lässt sich JSmooth aber auch über Ant steuern.

Zunächst muss ein neuer Ant-Task definiert werden:

<property name="jsmooth.dir" value="C:/Programme/JSmooth 0.9.7">
<taskdef name="jsmoothgen" classname="net.charabia.jsmoothgen.ant.JSmoothGen"
         classpath="${jsmooth.dir}/lib/jsmoothgen-ant.jar">

Jetzt kann man den neuen Task jsmoothgen nutzen:

<target name="build-exe-createmetadata">
  <jsmoothgen project="createmetadata.jsmooth"
              skeletonroot="${jsmooth.dir}/skeletons" />
</target>