Commits

Jason McKesson committed 104337c

SvgWriter: Added multiline text. This is done with an automated <tspan> process.
XmlWriter: Allowed the user to turn off pretty-print when pushing an element.

  • Participants
  • Parent commits e514e70

Comments (0)

Files changed (2)

File Tools/SvgWriter.lua

 	self.xmlFile:PopElement();
 end
 
+function SvgMembers.TextMultiline(self, textLines, position, lineHeight, styleOrClass, id)
+	self.xmlFile:PushElement("text", nil, true);
+	
+	self.xmlFile:AddAttribute{x=position[1], y=position[2]};
+
+	SvgCommonAttribs(self, styleOrClass, id);
+	
+	for i, line in ipairs(textLines) do
+		self.xmlFile:PushElement("tspan");
+			if(i ~= 1) then
+				self.xmlFile:AddAttribute("dy", lineHeight);
+			end
+			self.xmlFile:AddAttribute("x", position[1]);
+			self.xmlFile:AddText(line);
+		self.xmlFile:PopElement();
+	end
+	
+	self.xmlFile:PopElement();
+end
+
 -----------------------------------
 -- Structure and Groups
 function SvgMembers.BeginGroup(self, styleOrClass, id)

File Tools/XmlWriter.lua

 --[[
-This library is for writing XML files. The files are always UTF-8, version 1.0, with the appropriate XML header.
+self library is for writing XML files. The files are always UTF-8, version 1.0, with the appropriate XML header.
 
-To create a writer, use the function XmlWriter.XmlWriter, passing in a filename that you want written to. This function will automatically create the XML header. The function returns an XmlWriter "class", which is why creation looks like a class constructor. All of the following are member functions of the class.
+To create a writer, use the function XmlWriter.XmlWriter, passing in a filename that you want written to. self function will automatically create the XML header. The function returns an XmlWriter "class", which is why creation looks like a class constructor. All of the following are member functions of the class.
 
 == PushElement/PopElement ==
 Elements are added in a push/pop fasion. PushElement creates an element, and PopElement creates the end-tag (or the closing "/>" for empty elements). PushElement takes 2 parameters: the element's QName, and the optional default namespace. No checking is made to see if the element name has a qualifier at the same time as a default namespace.
 Redundant default namespace definitions will not be printed, so feel free to always pass a namespace parameter. If no namespace is passed in, the currently set default namespace is used.
 
 == AddAttribute ==
-Attributes can be added, within the scope of a Push/PopElement, and before any other node additions (IE: adding elements, text, PIs, or comments), with the AddAttribute function. This function takes an attribute QName and a string. Currently, it does not attempt to properly adjust the string for the right kind of quotes, always using double-quotes.
+Attributes can be added, within the scope of a Push/PopElement, and before any other node additions (IE: adding elements, text, PIs, or comments), with the AddAttribute function. self function takes an attribute QName and a string. Currently, it does not attempt to properly adjust the string for the right kind of quotes, always using double-quotes.
 
 AddAttribute can add multiple attributes at once. If you pass in a table rather than 2 parameters, it will walk all of the string members of that table and set the element's attributes to key="value" for each one.
 
 == AddNamespace ==
-If you want to add namespace prefix definitions to an element, use AddNamespace. This function takes a prefix (NCName) and a namespace string. Basically, it's a utility version of AddAttribute, except that it doesn't have a table version.
+If you want to add namespace prefix definitions to an element, use AddNamespace. self function takes a prefix (NCName) and a namespace string. Basically, it's a utility version of AddAttribute, except that it doesn't have a table version.
 
 == AddText/AddCDataText ==
 The AddText function can be used to add text. CDATA text can be added with AddCDataText. As with similar Lua writing functions, you may pass multiple strings as multiple parameters, and they will be written out in sequence. For AddCDataText, all the strings will be in one big CData. No escaping or checks of any kind is done here.
 	end
 end
 
-local function CloseElement(this, postChar)
-	if(this.needCloseElem) then
-		this.hFile:write(">");
-		this.needCloseElem = false;
-		this.lastWasElement = true;
+local function CloseElement(self, postChar)
+	if(self.needCloseElem) then
+		self.hFile:write(">");
+		self.needCloseElem = false;
+		self.lastWasElement = true;
 	end
 end
 
-function ClassMembers.PushElement(this, elementName, namespace)
-	CloseElement(this);
+function ClassMembers.PushElement(self, elementName, namespace, noPrettyPrint)
+	CloseElement(self);
 	
-	if(#this.elemStack == 0) then
-		assert(not this.hasRoot, "There can only be one root element in an XML file.");
-		this.hasRoot = true;
+	if(#self.elemStack == 0) then
+		assert(not self.hasRoot, "There can only be one root element in an XML file.");
+		self.hasRoot = true;
 	end
 	
 	--Intention and line spacing.
-	if(this.lastWasElement or #this.elemStack == 0) then
-		this.hFile:write("\n", string.rep("\t", #this.elemStack));
+	if(self.lastWasElement or #self.elemStack == 0) then
+		local topOfStack = self.formatStack[#self.formatStack];
+		if(not topOfStack or (topOfStack.form == false)) then
+			self.hFile:write("\n", string.rep("\t", #self.elemStack));
+		end
 	end
 
-	this.hFile:write("<", elementName, " ");
-	this.needCloseElem = true;
-	this.elemStack[#this.elemStack + 1] = elementName;
+	self.hFile:write("<", elementName, " ");
+	self.needCloseElem = true;
+	self.elemStack[#self.elemStack + 1] = elementName;
 	if(namespace) then
-		local topOfStack = this.nsStack[#this.nsStack];
+		local topOfStack = self.nsStack[#self.nsStack];
 		if(not topOfStack or topOfStack.ns ~= namespace) then
 			--Change the current namespace.
-			this.hFile:write("xmlns=\"", namespace, "\" ");
-			this.nsStack[#this.nsStack + 1] = { ns=namespace, loc=(#this.elemStack) };
+			self.hFile:write("xmlns=\"", namespace, "\" ");
+			self.nsStack[#self.nsStack + 1] = { ns=namespace, loc=(#self.elemStack) };
+		end
+	end
+	
+	if(type(noPrettyPrint) == "boolean") then
+		self.formatStack[#self.formatStack + 1] = {form=noPrettyPrint, loc=(#self.elemStack)};
+	end
+end
+
+function ClassMembers.PopElement(self)
+	assert(#self.elemStack > 0, "Element stack underflow.");
+
+	if(self.needCloseElem) then
+		self.hFile:write("/>");
+		self.needCloseElem = false;
+	else
+		--Intention and line spacing.
+		if(self.lastWasElement) then
+			local topOfStack = self.formatStack[#self.formatStack];
+			if(not topOfStack or (topOfStack.form == false)) then
+				self.hFile:write("\n", string.rep("\t", #self.elemStack - 1));
+			end
+		end
+
+		self.hFile:write("</", self.elemStack[#self.elemStack], ">");
+		self.lastWasElement = true;
+	end
+	self.elemStack[#self.elemStack] = nil;
+	
+	local topOfStack = self.nsStack[#self.nsStack];
+	if(topOfStack) then
+		if(#self.elemStack < topOfStack.loc) then
+			self.nsStack[#self.nsStack] = nil;
+		end
+	end
+
+	topOfStack = self.formatStack[#self.formatStack];
+	if(topOfStack) then
+		if(#self.elemStack < topOfStack.loc) then
+			self.formatStack[#self.formatStack] = nil;
 		end
 	end
 end
 
-function ClassMembers.PopElement(this)
-	assert(#this.elemStack > 0, "Element stack underflow.");
-
-	if(this.needCloseElem) then
-		this.hFile:write("/>");
-		this.needCloseElem = false;
-	else
-		--Intention and line spacing.
-		if(this.lastWasElement) then
-			this.hFile:write("\n", string.rep("\t", #this.elemStack - 1));
-		end
-
-		this.hFile:write("</", this.elemStack[#this.elemStack], ">");
-		this.lastWasElement = true;
-	end
-	this.elemStack[#this.elemStack] = nil;
-	
-	local topOfStack = this.nsStack[#this.nsStack];
-	if(topOfStack) then
-		if(#this.elemStack < topOfStack.loc) then
-			this.nsStack[#this.nsStack] = nil;
-		end
-	end
-end
-
-local function PutAttrib(this, attribName, data)
+local function PutAttrib(self, attribName, data)
 	assert(attribName ~= "xmlns", "You cannot manually change the default namespace.");
 
 	--TODO: Figure out a way to set the quote type automatically.
-	this.hFile:write(attribName, "=\"", data, "\" ");
+	self.hFile:write(attribName, "=\"", data, "\" ");
 end
 
-function ClassMembers.AddAttribute(this, attribName, data)
-	assert(this.needCloseElem, "The element has been closed. Cannot add attributes");
+function ClassMembers.AddAttribute(self, attribName, data)
+	assert(self.needCloseElem, "The element has been closed. Cannot add attributes");
 
 	if(type(attribName) == "table") then
 		for attrib, attribData in pairs(attribName) do
 			if(type(attrib) == "string") then
-				PutAttrib(this, attrib, attribData)
+				PutAttrib(self, attrib, attribData)
 			end
 		end
 	else
-		PutAttrib(this, attribName, data)
+		PutAttrib(self, attribName, data)
 	end
 end
 
-function ClassMembers.AddNamespace(this, prefix, namespace)
-	assert(this.needCloseElem, "The element has been closed. Cannot add namespace " .. namespace);
+function ClassMembers.AddNamespace(self, prefix, namespace)
+	assert(self.needCloseElem, "The element has been closed. Cannot add namespace " .. namespace);
 
-	PutAttrib(this, "xmlns:" .. prefix, namespace);
+	PutAttrib(self, "xmlns:" .. prefix, namespace);
 end
 
-function ClassMembers.AddText(this, ...)
-	CloseElement(this);
-	assert(#this.elemStack > 0, "Text cannot be written at the top of the scope; only within an element.");
-	--TODO: Make this properly escape the text.
-	this.hFile:write(...);
-	this.lastWasElement = false;
+function ClassMembers.AddText(self, ...)
+	CloseElement(self);
+	assert(#self.elemStack > 0, "Text cannot be written at the top of the scope; only within an element.");
+	--TODO: Make self properly escape the text.
+	self.hFile:write(...);
+	self.lastWasElement = false;
 end
 
-function ClassMembers.AddCDataText(this, ...)
-	CloseElement(this);
-	assert(#this.elemStack > 0, "CDATA Text cannot be written at the top of the scope; only within an element.");
-	this.hFile:write("<![CDATA[", ..., "]]>");
-	this.lastWasElement = false;
+function ClassMembers.AddCDataText(self, ...)
+	CloseElement(self);
+	assert(#self.elemStack > 0, "CDATA Text cannot be written at the top of the scope; only within an element.");
+	self.hFile:write("<![CDATA[", ..., "]]>");
+	self.lastWasElement = false;
 end
 
-function ClassMembers.AddProcessingInstruction(this, name, data)
-	CloseElement(this);
-	this.hFile:write("<?", name, " ", data, "?>");
-	if(#this.elemStack == 0) then this.hFile:write("\n"); end
-	this.lastWasElement = false;
+function ClassMembers.AddProcessingInstruction(self, name, data)
+	CloseElement(self);
+	self.hFile:write("<?", name, " ", data, "?>");
+	if(#self.elemStack == 0) then self.hFile:write("\n"); end
+	self.lastWasElement = false;
 end
 
 ClassMembers.AddPI = ClassMembers.AddProcessingInstruction;
 
-function ClassMembers.AddComment(this, ...)
-	CloseElement(this);
-	this.hFile:write("<!--", ..., "-->");
-	this.lastWasElement = false;
+function ClassMembers.AddComment(self, ...)
+	CloseElement(self);
+	self.hFile:write("<!--", ..., "-->");
+	self.lastWasElement = false;
 end
 
-function ClassMembers.AddDocTypePublic(this, doctype, loc1, loc2)
-	assert(not this.hasRoot, "You can only set the doctype before a root element has been created.");
-	assert(not this.hasDocType, "You can only set the doctype once.");
+function ClassMembers.AddDocTypePublic(self, doctype, loc1, loc2)
+	assert(not self.hasRoot, "You can only set the doctype before a root element has been created.");
+	assert(not self.hasDocType, "You can only set the doctype once.");
 	
-	this.hasDocType = true;
+	self.hasDocType = true;
 	
-	this.hFile:write(string.format([[<!DOCTYPE %s PUBLIC %s %s>]], doctype, loc1, loc2), "\n");
+	self.hFile:write(string.format([[<!DOCTYPE %s PUBLIC %s %s>]], doctype, loc1, loc2), "\n");
 end
 
-function ClassMembers.Close(this, elementName)
-	assert(#this.elemStack == 0, "You must pop all of the elements before closing.");
-	this.hFile:close();
+function ClassMembers.Close(self, elementName)
+	assert(#self.elemStack == 0, "You must pop all of the elements before closing.");
+	self.hFile:close();
 end
 
 function XmlWriter(strFilename)
 	local writer = {};
 	writer.elemStack = {};
 	writer.nsStack = {};
+	writer.formatStack = {};
 	writer.needCloseElem = false;
 	
 	writer.hFile = assert(io.open(strFilename, "w"), "Could not open file " .. strFilename);