<OpenTag>Presenter

opentag-presenter on GitHub »

Tag Writer's Guide

Table of Contents

  1. Introduction
  2. Tag Definition Basics
  3. Presentation's Internal Tree Structure
  4. PresentationObject
  5. PresentationObject Methods
    1. __init__()
    2. getContainer()
    3. getContents()
    4. getContent()
    5. setContent()
    6. hasProperty()
    7. getProperty()
    8. setProperty()
    9. setPropertyAlias()
    10. setKeepWhitespace()
    11. keepWhitespace()
    12. getSlide()
    13. getSlideshow()
    14. isAnimated()
    15. hideAnimated()
    16. getContainerOfType()
    17. hasContainerOfType()
    18. move()
    19. show()
    20. hide()
    21. setCached()
    22. isCached()
    23. getMaxWidth()
    24. getCenter()
    25. export()
  6. Required Methods to Define
    1. __init__()
    2. render()
    3. getCanvasItems()
    4. cacheObjects()
    5. getHtml()
  7. SlideApplication
    1. getWidth()
    2. getHeight()
    3. getMainWindow()
    4. getCanvasView()
    5. getConfig()
    6. getFileName()
    7. getFileDirName()
    8. settings()
  8. Examples
    1. <b> in b.py
    2. <title> in title.py
    3. <equation> in equation.py

1. Introduction

Presenter presentations are saved in XML files. Each time the XML parser reads a tag, a Python class definition matching that tag is imported. The purpose of this guide is to show how to define these Python classes to create new XML tags. Tags are used to display QCanvasItems objects in the display. See the Qt documentation to see what capabilities exist.



2. Tag Definition Basics

An XML tag in a document matches up to a Python class with the same name. This Python class is defined in a file of the same name (plus the ".py" extension) placed in the tags/ subdirectory within <OpenTag>Presenter's directory. For example, take presentation written in XML:
<slideshow>
    <slide title="XML Tags">
        <titlebox/>

        When the XML parser reads a tag, it imports a
        Python class definition matching that tag.
    </slide>
</slideshow>

Python classes named slideshow, slide, font (for the string data "When the..."), and titlebox are instantiated. In the tags/ subdirectory, there's a file named slide.py which contains a Python class named slide which is imported when the tag is parsed. Likewise, there are Python class definitions in slideshow.py, font.py, and titlebox.py.




3. Presentation's Internal Tree Structure

The resulting structure of instantiated these Python classes is a tree of the instances and the string data contained within the tags. For example, this Python code will translate:
<slideshow>
    <slide>
        <list>
            <bullet>Hello</bullet>
            <bullet>world!</bullet>
        </list>

        Goodbye, cruel world.
    </slide>
</slideshow>

The two <bullet> instances are contents (children) of the <list> instance and <list> a container (parent) of the <bullet>s. "Hello" is in the contents of the first <bullet>.




4. PresentationObject

PresentationObject is the name of the Python class used as a base of all tag definitions. To create a new tag named <flowgraph>, in a file named flowgraph.py in the tags/ subdirectory, declare a new class:
from PresentationObject import PresentationObject

class flowgraph( PresentationObject ):
    # put class definition code here

This base class contains methods used to manipulate properties and create the internal tree structure. Note that there is a file PresentationObject.py in the tags/ subdirectory.




5. PresentationObject Methods

  1. __init__( container, contents, properties )
    • Parameters
      • container, parent PresentationObject
      • contents, list of child PresentationObjects
      • properties, dictionary of attributes
    • Description
      • Constructor, call apply( PresentationObject.__init__, (self,)+args ) from the new tag's constructor.
  2. getContainer()
    • Returns
      • Reference to parent PresentationObject
    • Description
      • Get the parent tag object.
  3. getContents()
    • Returns
      • List of child PresentationObjects
    • Description
      • Get the nested tag objects.
  4. getContent(i)
    • Parameters
      • i, integer for index in the getContents() list
    • Returns
      • PresentationObject at the index or None if the index is out of range
    • Description
      • Same as getContents()[i]
  5. setContent( i, content )
    • Parameters
      • i, integer for index in the getContents() list
      • content, string or PresentationObject
    • Description
      • Use to replace a child with another object instance.
  6. hasProperty( propertyName )
    • Parameters
      • propertyName, string for the attribute to check for
    • Returns
      • True (non-zero) if the property was defined in this tag instance. Otherwise, false (zero).
    • Description
      • Use to make sure the getProperty() return value is coming from the calling PresentationObject.
  7. getProperty( propertyName )
    • Parameters
      • propertyName, string for the attribute to fetch
    • Returns
      • int for integer values (which must be set from within the PresentationObject definition). Otherwise, a string.
    • Description
      • From the current tag, find the nearest definition of a property. If it isn't defined in the current tag, it is searched for by going up to each container. If it is not defined, the property's default value will be found in the DefaultDocument, which is the uppermost object in the internal PresentationObject tree. To set a default, edit tags/DefaultDocument.py to add the value to the property dictionary. If a default value isn't added and an undefined property is requested, None will be returned.
  8. setProperty( propertyName, propertyValue )
    • Parameters
      • propertyName, string for the attribute name to set/replace
      • propertyValue, string value for the attribute
    • Description
      • Set a property for the current tag. If it's already set, the value will be replaced.
  9. setPropertyAlias( propertyAlias, propertyName )
    • Parameters
      • propertyAlias, another name for a property
      • propertyName, property to set an alias for
    • Description
      • This is used for alternate names for the same property. For instance, color and colour can both be used for font adjustment. To set an alias, make sure to set the call in the PresentationObject construct in tags/PresentationObject.py so the alias is set for every tag.
  10. setKeepWhitespace( keepWhitespace )
    • Parameters
      • keepWhitespace, boolean value (true = non-zero, false = zero)
    • Description
      • Call this in the tag definition constructor to set whether to keep boundary whitespace (before and after text blocks) to set as a container of an object. By default this is disabled, but it is useful for some tags such as <pre> for preformated text.
  11. keepWhitespace()
    • Returns
      • Returns true (non-zero) if whitespace has been kept, false if not.
    • Description
      • See above. Checking functions mostly used in tags/font.py.
  12. getSlide()
    • Parameters
    • Returns
      • Containing <slide> object or None if this tag isn't contained within a <slide>.
    • Description
      • Get the containing <slide> tag. This is useful for finding information about the QCanvas. It is the same as calling:
        from slide import slide
        self.getContainerOfType(slide)
        
  13. getSlideshow()
    • Returns
      • Containing <slideshow> object or None if this tag isn't contained within a <slideshow>.
    • Description
      • Get the containing <slideshow> tag. This is useful for finding information about the configuration file or the directory the presentation file is located in. It is the same as calling:
        from slideshow import slideshow
        self.getContainerOfType(slideshow)
        
  14. isAnimated()
    • Returns
      • True (non-zero) if the animation property has been set in this tag or any container. Otherwise, false (zero).
    • Description
      • Useful for finding if QCanvas objects must be hidden when the slide is first rendered to the screen.
  15. hideAnimated()
    • Description
      • If isAnimated() is true, objects and nested tags will be hidden from view. This is usually called before render() returns (see Section 6 for render() info).
  16. getContainerOfType( objectType )
    • Parameters
      • objectType, reference to Python class definition to check for
    • Returns
      • Reference to the nearest instance of the objectType
    • Description
      • Useful for finding parent tags of a certain type. For instance, <bullet> must find the parent <list> and <td> must find <tr>. If there is no containing instance of objectType, None is returned.
  17. hasContainerOfType( objectType )
    • Parameters
      • objectType, reference to Python class definition to check for
    • Returns
      • True (non-zero) if a containing tag of objectType exists, false (zero) if not.
    • Description
      • Same as: getContainerOfType(...) == None
  18. move( x, y )
    • Parameters
      • x, integer coordinate
      • y, integer coordinate
    • Description
      • Move Qt objects and nested tags to coordinates x, y (relative to the current x(), y() coordinates).
  19. show()
    • Description
      • Ensures Qt objects and nested tags are shown on the canvas.
    Description
  20. hide()
    • Description
      • Hides Qt objects and nested tags from view.
  21. setCached( cached )
    • Parameters
      • cached, boolean value, true (non-zero) or false (zero)
    • Description
      • When an object is first drawn to the screen, the Qt objects are created. The next time it's drawn, those objects are kept (but maybe resized). The purpose of the cached value is to indicate whether it's the first draw or not. It's set to false by default. Call setCached(1) before returning from render().
  22. isCached()
    • Returns
      • True (non-zero) or false (zero)
    • Description
      • Used for deciding to create Qt objects or manipulate existing ones.
  23. getMaxWidth()
    • Returns
      • Integer representing pixels on the screen.
    • Description
      • Get the maximum width a Qt object can appear in the display. For instance, if the display is 1024 pixels wide and margins are 32, the return value is 960. If a tag is defined to act as a box (such as <table>, <td>, <zschema>, etc.), this method in tags/PresentationObject.py must be edited.
  24. getCenter()
    • Returns
      • Integer on x-axis
    • Description
      • Retrieve the x coordinate pixel to align object(s) on for them to be centred.
  25. export( exportType )
    • Parameters
      • exportType, string which defines what type to export the presentation to
    • Returns
      • string data returned by the "get + exportType" method. If this method doesn't exist, an AttributeError is raised
    • Description
      • It works by searching for a method matching the name passed with a "get" prefix. For instance, if calling export( "pdf" ), this method will try to call getpdf() to get the output to PDF. getpdf() must be defined for this to work.
    • Exceptions
      • AttributeError, get* method isn't defined



6. Required Methods to Define

When creating a new tag, the Python class must extend PresentationObject or another class which extends PresentationObject. This class must also call the PresentationObject constructor.
  1. __init__( *args )
    • Parameters
      • *args, tuple of (container, contents, properties)
    • Description
      • Constructor which should call apply( tag_def.__init__, (self,)+args ) where tag_def is the class which the defined tag extends. Usually the extended class is PresentationObject.
  2. render( app, x, y )
    • Parameters
      • app, SlideApplication object. See Section 7 for more info.
      • x, horizontal coordinate to start drawing at
      • y, vertical coordinate to start drawing at
    • Returns
      • Tuple of x, y coordinates where the rendering left off. i.e., where the next object can start drawing).
    • Description
      • This is where Qt objects are created/resized and aligned onto the canvas according to the current size of the display. See <slide> documentation for methods on finding the baseline to align to, font height, etc. Make sure to also call render() in child PresentationObjects.
  3. getCanvasItems()
    • Returns
      • List of QCanvasItem objects created in the tag definition.
    • Description
      • Used for move(), show(), hide(), animation, etc.
  4. cacheObjects()
    • Description
      • Create any objects needed when the presentation is first loaded. For instance, the <img> tag reads the src="" image into memory.
  5. getHtml()
    • Returns
      • List of strings with each entry being a line in the HTML file.
    • Description
      • Translate the tag into HTML. Make sure to also retrieve the HTML for child PresentationObjects.



7. SlideApplication

  1. getWidth()
    • Returns
      • integer
    • Description
      • Retrieve the width of the application window.
  2. getHeight()
    • Returns
      • integer
    • Description
      • Retrieve the height of the application window.
  3. getMainWindow()
    • Returns
      • SlideMainWindow object (derived from QMainWindow)
    • Description
      • Retrieve the main widget - the application window.
  4. getCanvasView()
    • Returns
      • SlideCanvasView object (derived from QCanvasView)
    • Description
      • Retrieve the canvas widget displayed in the main window.
  5. getConfig()
    • Returns
      • PresenterConfig object.
    • Description
      • Same as self.getSlideshow().getConfig()
  6. getFileName()
    • Returns
      • string
    • Description
      • File name of the presentation XML file.
  7. getFileDirName()
    • Returns
      • string
    • Description
      • Directory name of the presentation XML file.
  8. settings()
    • Description
      • Popup the settings dialog window.



8. Examples

8.1 <b> in tags/b.py

This tag/class makes containing text bold.
from font import font
from qt import QFont

class b( font ):
    def __init__( self, *args ):
        apply( font.__init__, (self,) + args )
            # call setWeight() in font.py
            self.setWeight( QFont.Bold )
        def getHtml( self ):
            return [ "<b>" ] + \
                   font.getHtml( self ) + \
                   [ "</b>" ]

8.2 <title> in tags/title.py

This tag/class centres text in the display.
import re, string
from qt import *
from qtcanvas import QCanvasText
from font import font

true = 1
false = 0

class title( font ):
	def __init__( self, *args ):
		apply( font.__init__, (self,) + args )

		if self.getProperty("titleignore") == "yes" or \
		   self.getProperty("titleignore") == "true":
			return

		if not self.hasProperty("color")  and \
		   not self.hasProperty("colour") and \
		   self.getProperty("titlecolor") != None:
			self.setProperty( "color",
			                  self.getProperty("titlecolor") )

		if not self.hasProperty("face") and \
		   self.getProperty("titleface") != None:
			self.setProperty( "face",
			                  self.getProperty("titleface") )

		if not self.hasProperty("size") and \
		   self.getProperty("titlesize") != None:
			self.setProperty( "size",
			                  self.getProperty("titlesize") )

	def render( self, app, x, y, doLinebreaks=true ):
		if not self.isCached():
			self.setContents( [] )
			self.addContent( self.getProperty("title") )

		# Draw the text to the canvas, it will be re-aligned.
		font.render( self, app, x, y )

		# Only do a line break if not at the top of the slide.
		padding = string.lower( self.getProperty("padding") )
		if doLinebreaks                       and \
		   y != self.getProperty("margintop") and \
		   (padding == "yes" or padding == "true"):
			x, y = self.lineBreak( app, y )

		# Centre text by fitting as many words as possible on each
		# line.
		width = first = 0
		centrePixel = self.getCentre()
		maxWidth = self.getMaxWidth()

		for item in self.getCanvasWords():
			if width + item.boundingRect().width() > maxWidth:
				x = centrePixel - ( width / 2 )
				last = self.getCanvasWords().index( item )
				for i in range(first, last):
					prevItem = self.getCanvasWords()[i]
					prevItem.move( x, y )
					x = x + prevItem.boundingRect().width()
				first = last
				x, y = self.lineBreak( app, y )
				width = item.boundingRect().width()
				height = item.boundingRect().height()
				self.getSlide().setFontHeight( height )
			else:
				width = width + item.boundingRect().width()

		# Centre text that may be "leftover" on the last line.
		x = centrePixel - ( width / 2 )
		for item in self.getCanvasWords()[first:]:
			item.move( x, y )
			x = x + item.boundingRect().width()

		if doLinebreaks:
			x, y = self.lineBreak( app, y )

		return x, y

	def getHtml( self ):
		return [ "<center>" ]       + \
		       font.getHtml( self ) + \
		       [ "</center>" ]

8.3 <equation> in tags/equation.py

This class/tag shows a bitmap graphic, which is generated by LaTeX.
from PresentationObject import PresentationObject
from img import img
from font import font
from qt import QColor
import math
import os
import time

true = success = 1
false = failure = 0

class equation( img ):
	def __init__( self, *args ):
		apply( img.__init__, (self,) + args )

	def render( self, app, x, y ):
		if not app.getConfig().latexEnabled:
			return x, y

		emptyPixels = self.setSize( app.getHeight() )

		x, y = img.render( self, app, x, y )
		self.move( self.x(), self.y() + emptyPixels/2 )
		return x, y

	def setSize( self, displayHeight=600 ):
		image = self.getImage()["QImageOriginal"]
		height, emptyPixels = self.getHeightInfo( displayHeight )
		width = float( height ) / image.height()
		width = int( width * image.width() )

		self.setProperty( "width", width )
		self.setProperty( "height", height )

		return emptyPixels

	def getHeightInfo( self, displayHeight ):
		image = self.getImage()["QImageOriginal"]

		fontHeight = font(self).getActualFontHeight(displayHeight)
		if fontHeight == 0:
			fontHeight = self.getSlide().getFontHeight()

		scale = self.getSlideshow().getConfig().latexScale
		pixelsPerLine = self.pixelsPerLine( scale )
		numLines = math.ceil(float(image.height()) / pixelsPerLine)
		height = int( float(image.height())      / \
		              (pixelsPerLine * numLines) * \
		              (fontHeight * numLines) )

		return height, fontHeight * numLines - height

	def pixelsPerLine( self, scale ):
		if scale <= 2:
			return 45
		elif scale == 3:
			return 62
		elif scale == 4:
			return 78
		elif scale == 5:
			return 96
		elif scale == 6:
			return 113
		elif scale == 7:
			return 130
		elif scale == 8:
			return 147
		elif scale == 9:
			return 165
		elif scale == 10:
			return 182
		elif scale == 11:
			return 199
		elif scale == 12:
			return 216
		elif scale == 13:
			return 233
		elif scale == 14:
			return 251
		elif scale == 15:
			return 267
		else:
			return 286

	def getHexColors( self ):
		color = self.getProperty( "color" )
		bgColor = self.getProperty( "bgcolor" )

		return str(QColor(color).name()), str(QColor(bgColor).name())

	def createEquationImage( self, dirName ):
		import re

		fileName = self.getNewImageName()
		stub = "%s/%s" % ( dirName, fileName )

		dviFile = "%s.dvi" % fileName
		auxFile = "%s.aux" % fileName
		logFile = "%s.log" % fileName
		texFile = "%s.tex" % stub
		psFile  = "%s.ps"  % stub
		ppmFile = "%s.ppm" % stub
		pngFile = "%s.png" % stub
		outFile = "%s.stdout" % stub # redirect stdout
		errFile = "%s.stderr" % stub # redirect stderr
		gsFile = "%s_gs.input" % stub
		filesToRemove = []

		regex = re.compile( r"%%BoundingBox: "            \
		                     "(?P\d*) (?P\d*) " \
		                     "(?P\d*) (?P\d*)" )

		texfp = open( texFile, "w" )
		texfp.write( "\\documentclass{amsart}\n" \
		             "\\pagestyle{empty}\n"      \
		             "\\thispagestyle{empty}\n"  \
		             "\\begin{document}\n"       \
		             "\\huge\n"                  \
		             "{\n"                       \
		             "$" )
		texfp.write( self.getEquationString() )
		texfp.write( "$\n" \
		             "}\n" \
		             "\\end{document}\n" )
		texfp.close()

		filesToRemove.append( outFile )
		filesToRemove.append( errFile )
		filesToRemove.append( texFile )
		filesToRemove.append( dviFile )
		filesToRemove.append( auxFile )
		filesToRemove.append( logFile )
		exitCode = os.system( 'echo q | latex "%s" >"%s" 2>"%s"' \
		                      % (texFile, outFile, errFile) )
		if exitCode:
			self.removeFiles( filesToRemove )
			return None

		filesToRemove.append( dviFile )
		exitCode = os.system( 'dvips -E -o "%s" "%s" >"%s" 2>"%s"' \
		                      % (psFile, dviFile, outFile, errFile) )
		if exitCode:
			self.removeFiles( filesToRemove )
			return None

		dviArgsSet = false
		dvifp = open( psFile );
		for line in dvifp:
			match = regex.match( line );
			if match:
				llx, lly, urx, ury = match.group( "llx",
				                                  "lly",
				                                  "urx",
				                                  "ury" )
				dviArgsSet = true
				break
		dvifp.close()

		if not dviArgsSet:
			self.removeFiles( filesToRemove )
			return None

		scaleFactor = self.getSlideshow().getConfig().latexScale
		scaleFactor = float( scaleFactor )

		gsArgs = {}
		gsArgs["ppmFile"] = ppmFile
		gsArgs["width"] = int( scaleFactor * (int(urx)-int(llx)) )
		gsArgs["height"] = int( scaleFactor * (int(ury)-int(lly)) )
		gsArgs["resolv"] = "%dx%d" % ( scaleFactor*72, scaleFactor*72 )
		gsArgs["psFile"] = psFile
		gsArgs["gsFile"] = gsFile
		gsArgs["outFile"] = outFile
		gsArgs["errFile"] = errFile

		gsfp = open( gsFile, "w" )
		gsfp.write( "%s neg %s neg translate" % (llx, lly) )
		gsfp.close()

		filesToRemove.append( gsFile )
		exitCode = os.system( 'gs -sDEVICE=ppmraw '          \
		                      '-sOutputFile="%(ppmFile)s" '  \
		                      '-g%(width)sx%(height)s '      \
		                      '-r%(resolv)s - "%(psFile)s" ' \
		                      '< "%(gsFile)s" '              \
		                      '> "%(outFile)s" '             \
		                      '2> "%(errFile)s"' % gsArgs )
		if exitCode:
			self.removeFiles( filesToRemove )
			return None

		color, bgColor = self.getHexColors()
		color = "rgb:%s/%s/%s" % ( color[1:3], color[3:5], color[5:] )
		bgColor = "rgb:%s/%s/%s" \
		          % ( bgColor[1:3], bgColor[3:5], bgColor[5:] )
		tempColor = "rgb:83/95/67"

		filesToRemove.append( psFile )
		filesToRemove.append( ppmFile )
		exitCode = os.system( 'pnmgamma 1.0 "%s" | '                 \
		                      'pnmcrop | '                           \
		                      'pnmpad -white -l5 -r5 -t5 -b5 | '     \
		                      'ppmchange rgb:00/00/00 %s | '         \
		                      'ppmchange rgb:ff/ff/ff %s | '         \
		                      'ppmchange %s %s | '                   \
		                      'pnmtopng -interlace -transparent %s ' \
		                      '> "%s"' %                             \
		                      (ppmFile, tempColor, bgColor,
		                       tempColor, color, bgColor, pngFile) )

		if exitCode:
			self.removeFiles( filesToRemove )
			return None

		self.removeFiles( filesToRemove )

		if self.getSlideshow().getConfig().latexCacheEnabled:
			self.addEquationImage( "%s.png" % fileName )

		return pngFile

	def getEquationString( self ):
		equation = ""
		for content in self.getContents():
			if not isinstance(content, PresentationObject):
				equation = "%s%s" % ( equation, content )
		try:
			equation = equation.encode( "ISO 8859-1" )
		except AttributeError:
			pass

		return equation

	def removeFiles( self, fileList=None ):
		for file in fileList:
			try:
				os.remove( file )
			except OSError:
				pass

	def getNewImageName( self ):
		if self.getSlideshow().getConfig().latexCacheEnabled:
			dirName = self.getSlideshow().getConfig().latexCacheDir
		else:
			dirName = self.getSlideshow().getDirName()

		name = stub = time.strftime( "%y%m%d%H%M%S", time.localtime() )
		num = 0
		while 1:
			try:
				os.stat( "%s/%s.png" % (dirName, name) )
			except OSError:
				return name
			name = "%s_%03d" % ( stub, num )
			num = num + 1
			if num > 999:
				return "%s_1000" % stub

		return name

	def addEquationImage( self, imgName, equation=None ):
		cacheDir = self.getSlideshow().getConfig().latexCacheDir
		indexPath = "%s/index.dat" % cacheDir

		if equation == None:
			equation = self.getEquationString()

		try:
			fp = open( indexPath, "a" )
		except IOError:
			try:
				os.makedirs( cacheDir )
			except OSError, e:
				if e.errno != 17:
					return
				try:
					fp = open( indexPath, "a" )
				except IOError:
					return

		color, bgColor = self.getHexColors()
		fp.write( "\n[%s color=%s bgcolor=%s scale=%d]\n" \
		          % (imgName, color, bgColor,             \
		             self.getSlideshow().getConfig().latexScale) )
		fp.write( equation )
		fp.write( "\n[/%s]\n" % imgName )
		fp.close()

	def findEquationImage( self, equation=None ):
		import re
		imgRegex = re.compile( r"^\[(?P(.)+) "    \
		                        "color=(?P"      \
		                          "#([A-Za-z0-9]){6}) " \
		                        "bgcolor=(?P"  \
		                          "#([A-Za-z0-9]){6}) " \
		                        "scale=(?P[0-9]+)\]$" )
		cacheDir = self.getSlideshow().getConfig().latexCacheDir

		if equation == None:
			equation = self.getEquationString()

		try:
			fp = open(  "%s/index.dat" % cacheDir, "r" )
		except IOError:
			return None

		imgName = None
		line = fp.readline()
		while len(line):
			match = imgRegex.match( line );
			if match:
				imgName, color, bgColor, scale = \
					match.group( "name",
					             "color",
					             "bgcolor",
					             "scale" )
				endRegex = re.compile( "^\[/%s\]$" % imgName )

				currColor, currBgColor = self.getHexColors()
				currScale = \
				    self.getSlideshow().getConfig().latexScale
				if color != currColor     or \
				   bgColor != currBgColor or \
				   int(scale) != currScale:
				   	line = fp.readline()
					while not endRegex.match(line):
						line = fp.readline()
					line = fp.readline()
					continue

				imgEq = ""
				line = fp.readline()
				while len(line) and not endRegex.match(line):
					imgEq = "%s%s" % ( imgEq, line )
					line = fp.readline()

				if len(imgEq) and imgEq[-1] == "\n":
					imgEq = imgEq[:-1]
				if imgEq == equation:
					fp.close()
					return "%s/%s" % ( cacheDir, imgName )
			line = fp.readline()

		fp.close()
		return None

	def cacheObjects( self ):
		config = self.getSlideshow().getConfig()
		if config.latexCacheEnabled:
			imgFile = self.findEquationImage()
			if imgFile == None:
				dirName = config.latexCacheDir
				imgFile = self.createEquationImage( dirName )
		else:
			dirName = self.getSlideshow().getDirName()
			imgFile = self.createEquationImage( dirName )

		if imgFile != None and len(imgFile):
			imgFile = os.path.abspath( imgFile )
			self.setProperty( "src", imgFile )
		else:
			self.setProperty( "src", "images/broken_equation.png" )

		img.cacheObjects( self )

		if not config.latexCacheEnabled:
			self.getSlideshow().addFileToRemove( imgFile )

	def getHtml( self ):
		self.setSize()
		return img.getHtml( self )