From 0c0a3f7afdd6b28f7e177276ddca1c898a6c9da9 Mon Sep 17 00:00:00 2001 From: Pelle Nilsson Date: Tue, 11 Jun 2013 00:02:19 +0200 Subject: [PATCH] Basic quoting of different output formats. Need to be improved and also add strict filtering later. --- examples/format.gamebook | 11 +++++++++++ formatgamebook.py | 19 +++++++++++-------- output.py | 24 ++++++++++++++---------- quote.py | 26 ++++++++++++++++++++++++++ todo.org | 8 ++++++-- 5 files changed, 68 insertions(+), 20 deletions(-) create mode 100644 quote.py diff --git a/examples/format.gamebook b/examples/format.gamebook index 54dfde6..cb6b042 100644 --- a/examples/format.gamebook +++ b/examples/format.gamebook @@ -2,7 +2,18 @@ title = Format * 1 start This examples tests gamebook formatting, not so much game mechanics or references. Currently there is nothing here really. +This section contains some tricky characters to quote, +like } and { and " and ' and \. +HTML will probably not like
or &boom;. +If something broke, turn to [[bad]], +otherwise turn to [[good]]. * dum :dummy: Sections tagged as dummy will not be visible in output at all. + +* good +Good! + +* bad +Bad. diff --git a/formatgamebook.py b/formatgamebook.py index 56ba3d4..b177193 100755 --- a/formatgamebook.py +++ b/formatgamebook.py @@ -33,6 +33,7 @@ import os.path import sys import json +import quote import sections import templates import verifygamebook @@ -40,18 +41,19 @@ from output import OutputFormat USAGE = "usage: %prog [options] inputfile(s)... outputfile" -def of(extension, name): +def of(extension, name, quote): return {'extension' : extension, 'name' : name, + 'quote' : quote } OUTPUT_FORMATS = [ - of('tex', 'LaTeX'), - of('rtf', 'Rich Text Format'), - of('dot', 'Graphviz section flowchart'), - of('html', 'HTML+JS playable in browser'), - of('txt', 'Plain text'), - of('debug', 'Gamebook Debug Output'), + of('tex', 'LaTeX', quote.latex), + of('rtf', 'Rich Text Format', quote.rtf), + of('dot', 'Graphviz section flowchart', quote.no), + of('html', 'HTML+JS playable in browser', quote.html), + of('txt', 'Plain text', quote.no), + of('debug', 'Gamebook Debug Output', quote.no), ] def make_supported_formats_list_string(): @@ -123,7 +125,8 @@ def make_output(outputfilename, templatedirs): for of in OUTPUT_FORMATS: extension = of['extension'] if outputfilename.endswith('.' + extension): - return OutputFormat(templates.Templates(templatedirs, extension)) + return OutputFormat(templates.Templates(templatedirs, extension), + of['quote']) raise Exception("Unsupported or unknown output format for %s." % outputfilename) diff --git a/output.py b/output.py index aaecb12..15ef76e 100644 --- a/output.py +++ b/output.py @@ -4,10 +4,12 @@ import sys class OutputFormat (object): "Handles book output. Big FIXME required to make sense." - def __init__(self, templates): + def __init__(self, templates, quote): self.templates = templates + self.quote = quote def write_begin(self, book, output): + # FIXME make sure book config is properly quoted print >> output, self.format_with_template("begin", book.config) def write_shuffled_sections(self, shuffled_sections, output): @@ -20,7 +22,8 @@ class OutputFormat (object): def write_section(self, section, shuffled_sections, output): refs = [] refsdict = ReferenceFormatter(section, shuffled_sections, - self.format_with_template("section_ref")) + self.format_with_template("section_ref"), + self.quote) formatted_text = self.format_section(section, refsdict) print >> output, self.format_with_template("section", { 'nr' : shuffled_sections.to_nr[section], @@ -37,7 +40,7 @@ class OutputFormat (object): ref_start = section.text.find('[[', i) tag_start = section.text.find('[', i) if ref_start >= 0 and ref_start <= tag_start: - res += section.text[i:ref_start] + res += self.quote(section.text[i:ref_start]) ref_end = section.text.find(']]', ref_start) if ref_end > ref_start: ref = section.text[ref_start+2:ref_end] @@ -52,7 +55,7 @@ class OutputFormat (object): raise Exception('Mismatched ref start [[ in section %s' % self.name) elif tag_start >= 0: - res += section.text[i:tag_start] + res += self.quote(section.text[i:tag_start]) tag_end = section.text.find(']', tag_start) if tag_end < 0: raise Exception('Mismatched tag start [ in section %s' % @@ -68,18 +71,18 @@ class OutputFormat (object): tag, self.name)) inner = section.text[tag_end+1:end_tag_start] # FIXME this pollutes the mutable references object - references['inner'] = inner + references['inner'] = self.quote(self.quote(inner)) for i, arg in enumerate(tagparts[1:]): - references['arg%d' % (i+1)] = arg + references['arg%d' % (i+1)] = self.quote(arg) f = self.format_with_template(tagname, references) if len(f) > 0: res += f else: - res += inner + res += self.quote(inner) i = section.text.find(']', end_tag_start) + 1 else: - res += section.text[i:] + res += self.quote(section.text[i:]) break return res @@ -100,16 +103,17 @@ class OutputFormat (object): class ReferenceFormatter (object): "There is probably a better way, but this hack seems to work." - def __init__(self, section, shuffled_sections, ref_template): + def __init__(self, section, shuffled_sections, ref_template, quote): self.section = section self.shuffled_sections = shuffled_sections self.found = set() self.ref_template = ref_template self.items = {'nr' : shuffled_sections.to_nr[section]} + self.quote = quote def __getitem__(self, key): if key in self.items: - return self.items[key] + return self.quote(self.items[key]) to_section = self.shuffled_sections.from_name[key] res = self.ref_template % { 'nr' : self.shuffled_sections.to_nr[to_section], diff --git a/quote.py b/quote.py new file mode 100644 index 0000000..1f1738d --- /dev/null +++ b/quote.py @@ -0,0 +1,26 @@ +#FIXME entire file is a hack + +def latex(s): + return (s.replace('\\', '\\textbackslash') + .replace('&', '\&') + .replace('%', '\%') + .replace('$', '\$') + .replace('#', '\#') + .replace('_', '\_') + .replace('{', '\{') + .replace('}', '\}') + .replace('~', '\\textasciitilde') + .replace('^', '\\textasciicircum')) + +def rtf(s): + return (s.replace('\\', '\\\\') + .replace('{', '\\{') + .replace('}', '\\}')) + +import cgi + +def html(s): + return cgi.escape(s) + +def no(s): + return s diff --git a/todo.org b/todo.org index 787d70a..35f4d5a 100644 --- a/todo.org +++ b/todo.org @@ -1,4 +1,4 @@ -* TODO [32/57] [56%] +* TODO [33/59] [55%] - [X] Debug output - [X] DOT output - [X] LaTeX output @@ -34,13 +34,15 @@ - [X] Book option to set title - [X] Book option to set author - [X] Book option to set max section number to use -- [ ] Quote strings to not break formatting. +- [X] Quote strings to not break formatting. - [X] Include other templates from a template. - [ ] Template for book introduction (including rules etc) Sections with some markup (has number 0?) are added as chapters of introduction, otherwise formatted identical to other sections. - [ ] More formatting possibilities in sections Look at existing gamebooks to get ideas. +- [ ] Only accept specific characters in identifiers (eg section names) + eg [a-z][a-z_0-9]+ - [ ] Random pick of link to follow from a section. - [ ] Possibility to make predictable random numbers and shuffling for testing - [ ] Test generate examples and compare to expected output in all formats @@ -63,6 +65,8 @@ - [ ] Some way to insert character sheet in book introduction - [ ] Some way to insert dice at bottom of pages for LaTeX - [ ] Some way to insert optional random numbers table at end of book +- [ ] Defensive removal of any weird unicode not handled by quoting. +- [ ] Somewhat user-friendly error messages - [ ] Document Gamebook format - [ ] HTML CSS - [ ] Higher level text-language for Gamebooks