Here we presents the integration of [The Birds Lake] into the abiword wordprocessor.
Abiword is a free, multiplatform wordprocessor. A complete integration of the birds lake into abiword will enable it in the future to have access to million of importation/exportation/isomorph filters !!! And be multiplatform the same time !!!
How do we do that ? In fact the code of the filters are not on the same machine than the wordprocessor. Abiword scan the internet like peer2peer apps for so called "Filters server", (This can be done on a dedicated thread, the server can be on the same machine). Once it got some servers it will contact them to get some informations about the server like its name, a logo, its filters trees. All the filters tree will be merged together...
Now we come back to abiword. Let's suppose that we had created highly semantic styles : TblTitle, TblChapter, TblSection and TblSubSection like in LaTeX. The following picture shows the Abiword manual structured with this styles. Click here to get this document. We have put a yellow background in the style to see them better, but what's really important is there name.
You have got a complex dialog, you select the filter on your left in the filters tree. And you will get a description of the filter on your right. Abiword communicates with the server using CORBA and by receiving xml files which describes authors, filters, server and so on ... We 've got a just in time strategy, for example we don't get a preview of filter until the user has clicked on it. We have also a cache system ...
Here we have only one filters server : Lioubovnik Python test, but you can have lot (but less than 32000 in the current implementation :-) ) You click on OK !! The server will launch a function to answer the filter what forms (called interface) he needs to be filled. The client will get them and will show them, here we have only one interface : SunBeach (tab).
The interfaces are also written in xml, the current example :
<?xml version="1.0"?> <IGenInterface name="HTML_SunBeach" label="Sunbeach"> <IGenFrame name="colors" label="Colors"> <IGenInput name="titlesForegroundColor" label="Titles foreground" info="Choose the color that will be used for TblTitle, and TblSubSection and also for the menu section list bullet" type="color" >ffff00</IGenInput> <IGenInput name="titlesBackgroundColor" label="Titles background" info="Choose the background color that will be used for TblTitle, and TblSubSection and also for the menu section list bullet" type="color" >006565</IGenInput> <IGenInput name="menuBackgroundColor" label="Menu background" info="Choose the background color that will be used for the menu on the left" type="color">ffffff</IGenInput> </IGenFrame> <IGenFrame name="chaptersSurfboards" label="Chapters surfboard"> <IGenInput name="display" type="boolean" label="Display ?" info="Do you want to have a surfboard with the current chapter written on it" >yes</IGenInput> <IGenInput name="ttfFont" type="selection" label="Which font ?" info="Choose a font from those which are proposed" items="TSCu_Comic;babelfish;ganymede;independ;electroh;betadance;edgewater;actionis" >electroh</IGenInput> <IGenInput name="shading" type="boolean" label="Shading ?" info="Do you want the text to be shaded ?" >yes</IGenInput> </IGenFrame> </IGenInterface>
Easy no ? We use LibIGen to render this interfaces. It supports GTK2 for now, but other toolkits could be used (I have started to work on a QT3 front-end). -> MULTIPLATFORM
The options you have chosen will be sent back to the server and used to apply the filter.
After all that you have a well selected and well customized document (the result is a directory, if you have called on file selection dialog 'out.tbl' you will get a 'out.tbl.dir' folder which contains the html, graphics, css, etc files ...
Click here to see the result of this filter online.
Writting a filter is very easy, since these can be written in python using LioubovnikPython (you can use other langages, you have just to write the Lioubovnik, the job of communication with the client is done in Lioubliou, the code of LioubovnikPython is less than 400 lines of C code !!!)
It is easy also because you write the class which handles with the miscellanceous inner xml of the Abiword1 format once and then reuse the code indefinitly. You just writte some CSS code, generates some graphics dynamically, javascript, PHP, etc ...
For example, here is the code of the SunBeach filter which creates a clone of michel.strasser.free.fr web site :
import emb import sys import os import os.path from Base.AbwReadAndExecute import Input from Base.AbwReadAndExecute import PageBase_CS from Base.AbwReadAndExecute import AbwHtmlReadAndExecute import tarfile import Image, ImageDraw, ImageFont filter={} def interfaces(portsDir, workingDir): filter["Filter"]=Filter(workingDir) filter["PortsDir"]=portsDir return ["Interfaces/HTML/TblIf-HTML_SunBeach.ign"] def main(portsDir, workingDir, ig): print "Hello, special greetings from main func, yeah !!!\n" print "workin'Dir is" + workingDir print "portsDir is" + portsDir filter["Filter"].run(ig) class Filter: def __init__(self, dir): self.input=Input(dir) pB=PageBase_CS(self.input) self.ab=AbwHtmlReadAndExecute(self.input, pB) def printDocStyleForChapter(self, docStyleFd, chapterId): toWrite=""" #TblChapterBis__""" + chapterId + """ { background-image: url(""" + chapterId + """.png); display: block; } """ docStyleFd.write(toWrite) def createChaptersPics(self, display, font, shading): res=self.input.ctxt.xpathEval("/abiword/section/p[@style='TblChapter']") out=self.input.dir + os.sep + "out" theme=out + os.sep + "theme" im=Image.open(theme + os.sep + "sunBeach" + os.sep + "surfboard2.png") fontFile=filter["PortsDir"] + os.sep + "Ressources" + os.sep + "Fonts" + os.sep + font + ".ttf" if(not os.path.exists(fontFile)): fontFile=filter["PortsDir"] + os.sep + "Ressources" + os.sep + "Fonts" + os.sep + "electroh.ttf" print "fontFile is " + fontFile font2=ImageFont.truetype(fontFile, 18, 0) docStyle=open(out + os.sep + "docStyle.css", "w") if(display): for i in res: id=i.prop("TblId") print id txt=i.content if(len(txt)>12): txt=txt[:9] + "..." im2=im.copy() draw=ImageDraw.Draw(im2) if(shading): draw.text((40, 16), txt, font=font2, fill=(89, 9, 10)) draw.text((38, 14), txt, font=font2, fill=(255, 255, 255)) del draw file=self.input.dir + os.sep + "out" + os.sep + id + ".png" im3=im2.rotate(90) im3.save(file, "PNG") self.printDocStyleForChapter(docStyle, id) else: toWrite=""" .TblChapterBis { display: none; } """ docStyle.write("toWrite") docStyle.close() def drawOutside(self, draw, xy1, xy2, color): (x1, y1)=xy1 (x2, y2)=xy2 dy=y2-y1 r=dy/2 draw.arc([x1, y1, x1 + dy, y2], 90, 270, color) draw.arc([x2 - dy, y1, x2, y2], 270, 90, color) draw.line([x1 + r, y1, x2 - r, y1], color) draw.line([x1 + r, y2, x2 - r, y2], color) def createHBackground2(self, colorS1, colorS2, file, size, coords): im=Image.new("RGBA", size) draw=ImageDraw.Draw(im) rX1=coords[0] rY1=coords[1] rX2=coords[2] rY2=coords[3] cR=(rY2-rY1)/2 color=colorS2 self.drawOutside(draw, (rX1 - cR - 8, rY1 - 8), (rX2 + cR + 8, rY2 + 8), color) self.drawOutside(draw, (rX1 - cR - 6, rY1 - 6), (rX2 + cR + 6, rY2 + 6), color) self.drawOutside(draw, (rX1 - cR - 4, rY1 - 4), (rX2 + cR + 4, rY2 + 4), color) self.drawOutside(draw, (rX1 - cR - 2, rY1 - 2), (rX2 + cR + 2, rY2 + 2), color) color=colorS1 draw.setfill(1) draw.ellipse([rX1 - cR, rY1, rX1 + cR, rY2], fill=color) draw.ellipse([rX2 - cR, rY1, rX2 + cR, rY2], fill=color) draw.rectangle([rX1, rY1, rX2, rY2], fill=color) del draw im.save(file, "PNG") def createHBackgrounds(self, colorS1, colorS2): out=self.input.dir + os.sep + "out" theme=out + os.sep + "theme" sunBeach=theme + os.sep + "sunBeach" self.createHBackground2(colorS1, colorS2, sunBeach + os.sep + "backH1.png", (822, 130), [65, 27, 761, 103]) self.createHBackground2(colorS1, colorS2, sunBeach + os.sep + "backH2Page.png", (630, 50), [40, 10, 590, 40]) def createBul2(self, colorS1, colorS2, menuBackColor): out=self.input.dir + os.sep + "out" theme=out + os.sep + "theme" sunBeach=theme + os.sep + "sunBeach" im=Image.new("RGBA", [13, 13]) draw=ImageDraw.Draw(im) draw.rectangle([0, 0, 12, 12], fill=menuBackColor) draw.rectangle([5, 5, 7, 7], fill=colorS1) for i in [[5, 1, 7, 3], [1, 5, 3, 7], [9, 5, 11, 7], [5, 9, 7, 11]]: draw.rectangle(i, fill=colorS2) del draw im.save(sunBeach + os.sep + "bul2.gif", "GIF") def hPageEtc(self, color1, color2, menuBackgroundColor): self.createHBackgrounds(color1, color2) out=self.input.dir + os.sep + "out" docStyle=open(out + os.sep + "docStyle.css", "a") toWrite=""" #TblTitle { color: """ + color2 + """; } .TblSection { color: """ + color2 + """; } .MenuGenerator a:hover { color: """ + color2 + """; background-color: """ + color1 + """; } .MenuGenerator li { background-color: """ + menuBackgroundColor + """; } """ docStyle.write(toWrite) docStyle.close() self.createBul2(color1, color2, menuBackgroundColor) def run(self, ig): cTitlesFg=emb.get(ig, "/HTML_SunBeach/colors/titlesForegroundColor") cTitlesBg=emb.get(ig, "/HTML_SunBeach/colors/titlesBackgroundColor") cMenuBg=emb.get(ig, "/HTML_SunBeach/colors/menuBackgroundColor") sbDisplay=emb.get(ig, "/HTML_SunBeach/chaptersSurfboards/display") sbFont=emb.get(ig, "/HTML_SunBeach/chaptersSurfboards/ttfFont") sbShading=emb.get(ig, "/HTML_SunBeach/chaptersSurfboards/shading") self.ab.run() portsDir=filter["PortsDir"] themeFile=portsDir + os.sep + "Export" + os.sep + "HTML" + os.sep + "SunBeach" + os.sep + "theme.tgz" tar = tarfile.open(themeFile, "r:gz") for i in tar.getmembers(): tar.extract(i, self.input.dir + os.sep + "out") tar.close() self.createChaptersPics((sbDisplay=="yes"), sbFont, (sbShading=="yes")) self.hPageEtc("#" + cTitlesBg, "#" + cTitlesFg, "#" + cMenuBg) del filter["Filter"]
And you can have much better since I am not a web designer or graphist. See what they do at csszengarden.com !