Support markup in TextDoc, add implementation in CairoDoc, use in indiv complete report

svn: r11913
This commit is contained in:
Benny Malengier 2009-02-08 09:59:36 +00:00
parent 0b2394fbd9
commit ac50776d33
3 changed files with 289 additions and 17 deletions

View File

@ -1406,12 +1406,41 @@ class BaseDoc:
# TextDoc # TextDoc
# #
#------------------------------------------------------------------------ #------------------------------------------------------------------------
def noescape(text):
return text
class TextDoc: class TextDoc:
""" """
Abstract Interface for text document generators. Output formats for Abstract Interface for text document generators. Output formats for
text reports must implment this interface to be used by the report text reports must implment this interface to be used by the report
system. system.
""" """
BOLD = 0
ITALIC = 1
UNDERLINE = 2
FONTFACE = 3
FONTSIZE = 4
FONTCOLOR = 5
HIGHLIGHT = 6
SUPERSCRIPT = 7
SUPPORTED_MARKUP = []
ESCAPE_FUNC = lambda x: noescape
#Map between styletypes and internally used values. This map is needed
# to make TextDoc officially independant of gen.lib.styledtexttag
STYLETYPE_MAP = {
}
CLASSMAP = None
#STYLETAGTABLE to store markup for write_markup associated with style tags
STYLETAG_MARKUP = {
BOLD : ("", ""),
ITALIC : ("", ""),
UNDERLINE : ("", ""),
SUPERSCRIPT : ("", ""),
}
def page_break(self): def page_break(self):
"Forces a page break, creating a new page" "Forces a page break, creating a new page"
raise NotImplementedError raise NotImplementedError
@ -1477,6 +1506,29 @@ class TextDoc:
"Ends the current table cell" "Ends the current table cell"
raise NotImplementedError raise NotImplementedError
def write_text(self, text, mark=None):
"""
Writes the text in the current paragraph. Should only be used after a
start_paragraph and before an end_paragraph.
@param text: text to write.
@param mark: IndexMark to use for indexing (if supported)
"""
raise NotImplementedError
def write_markup(self, text, s_tags):
"""
Writes the text in the current paragraph. Should only be used after a
start_paragraph and before an end_paragraph. Not all backends support
s_tags, then the same happens as with write_text. Backends supporting
write_markup will overwrite this method
@param text: text to write. The text is assumed to be _not_ escaped
@param s_tags: assumed to be list of styledtexttags to apply to the
text
"""
self.write_text(text)
def write_note(self, text, format, style_name): def write_note(self, text, format, style_name):
""" """
Writes the note's text and take care of paragraphs, Writes the note's text and take care of paragraphs,
@ -1488,15 +1540,17 @@ class TextDoc:
""" """
raise NotImplementedError raise NotImplementedError
def write_text(self, text, mark=None): def write_styled_note(self, styledtext, format, style_name):
""" """
Writes the text in the current paragraph. Should only be used after a Convenience function to write a styledtext to the cairo doc.
start_paragraph and before an end_paragraph. styledtext : assumed a StyledText object to write
format : = 0 : Flowed, = 1 : Preformatted
style_name : name of the style to use for default presentation
@param text: text to write. overwrite this method if the backend supports styled notes
@param mark: IndexMark to use for indexing (if supported)
""" """
raise NotImplementedError text = str(styledtext)
self.write_note(text, format, style_name)
def add_media_object(self, name, align, w_cm, h_cm): def add_media_object(self, name, align, w_cm, h_cm):
""" """
@ -1510,6 +1564,135 @@ class TextDoc:
""" """
raise NotImplementedError raise NotImplementedError
def find_tag_by_stag(self, s_tag):
"""
@param s_tag: object: assumed styledtexttag
@param s_tagvalue: None/int/str: value associated with the tag
A styled tag is type with a value.
Every styled tag must be converted to the tags used in the corresponding
markup for the backend, eg <b>text</b> for bold in html.
These markups are stored in STYLETAG_MARKUP. They are tuples for begin
and end tag
If a markup is not present yet, it is created, using the
_create_xmltag method you can overwrite
"""
type = s_tag.name
if not self.STYLETYPE_MAP or \
self.CLASSMAP <> type.__class__.__name__ :
self.CLASSMAP == type.__class__.__name__
self.STYLETYPE_MAP[type.__class__.BOLD] = self.BOLD
self.STYLETYPE_MAP[type.ITALIC] = self.ITALIC
self.STYLETYPE_MAP[type.UNDERLINE] = self.UNDERLINE
self.STYLETYPE_MAP[type.FONTFACE] = self.FONTFACE
self.STYLETYPE_MAP[type.FONTSIZE] = self.FONTSIZE
self.STYLETYPE_MAP[type.FONTCOLOR] = self.FONTCOLOR
self.STYLETYPE_MAP[type.HIGHLIGHT] = self.HIGHLIGHT
self.STYLETYPE_MAP[type.SUPERSCRIPT] = self.SUPERSCRIPT
typeval = int(s_tag.name)
s_tagvalue = s_tag.value
tag_name = None
if type.STYLE_TYPE[typeval] == bool:
return self.STYLETAG_MARKUP[self.STYLETYPE_MAP[typeval]]
elif type.STYLE_TYPE[typeval] == str:
tag_name = "%d %s" % (typeval, s_tagvalue)
elif type.STYLE_TYPE[typeval] == int:
tag_name = "%d %d" % (typeval, s_tagvalue)
if not tag_name:
return None
tags = self.STYLETAG_MARKUP.get(tag_name)
if tags is not None:
return tags
#no tag known yet, create the markup, add to lookup, and return
tags = self._create_xmltag(self.STYLETYPE_MAP[typeval], s_tagvalue)
self.STYLETAG_MARKUP[tag_name] = tags
return tags
def _create_xmltag(self, type, value):
"""
Create the xmltags for the backend.
Overwrite this method to create functionality with a backend
"""
if type not in self.SUPPORTED_MARKUP:
return None
return ('', '')
def _add_markup_from_styled(self, text, s_tags):
"""
Input is plain text, output is text with markup added according to the
s_tags which are assumed to be styledtexttags.
As adding markup means original text must be escaped, ESCAPE_FUNC is
used
This can be used to convert the text of a styledtext to the format
needed for a document backend
Do not call this method in a report, use the write_markup method
@note: the algorithm is complex as it assumes mixing of tags is not
allowed: eg <b>text<i> here</b> not</i> is assumed invalid
as markup. If the s_tags require such a setup, what is returned
is <b>text</b><i><b> here</b> not</i>
overwrite this method if this complexity is not needed.
"""
FIRST = 0
LAST = 1
tagspos = {}
for s_tag in s_tags:
tag = self.find_tag_by_stag(s_tag)
if tag is not None:
for (start, end) in s_tag.ranges:
if start in tagspos:
tagspos[start] += [(tag, FIRST)]
else:
tagspos[start] = [(tag, FIRST)]
if end in tagspos:
tagspos[end] = [(tag, LAST)] + tagspos[end]
else:
tagspos[end] = [(tag, LAST)]
start = 0
end = len(text)
keylist = tagspos.keys()
keylist.sort()
keylist = [x for x in keylist if x<=len(text)]
opentags = []
otext = u"" #the output, text with markup
for pos in keylist:
#write text up to tag
if pos > start:
otext += self.ESCAPE_FUNC()(text[start:pos])
#write out tags
for tag in tagspos[pos]:
#close open tags starting from last open
opentags.reverse()
for opentag in opentags:
otext += opentag[1]
opentags.reverse()
#if start, add to opentag in beginning as first to open
if tag[1] == FIRST:
opentags = [tag[0]] + opentags
else:
#end tag, is closed already, remove from opentag
opentags = [x for x in opentags if not x == tag[0] ]
#now all tags are closed, open the ones that should open
for opentag in opentags:
otext += opentag[0]
start = pos
otext += self.ESCAPE_FUNC()(text[start:end])
#opentags should be empty. If not, user gave tags on positions that
# are over the end of the text. Just close the tags still open
if opentags:
print 'WARNING: TextDoc : More style tags in text than length '\
'of text allows.\n', opentags
opentags.reverse()
for opentag in opentags:
otext += opentag[1]
return otext
#------------------------------------------------------------------------ #------------------------------------------------------------------------
# #
# DrawDoc # DrawDoc

View File

@ -1065,6 +1065,32 @@ class CairoDoc(BaseDoc.BaseDoc, BaseDoc.TextDoc, BaseDoc.DrawDoc):
page style. page style.
""" """
STYLETAG_TO_PROPERTY = {
BaseDoc.TextDoc.FONTCOLOR : 'foreground',
BaseDoc.TextDoc.HIGHLIGHT : 'background',
BaseDoc.TextDoc.FONTFACE : 'face',
BaseDoc.TextDoc.FONTSIZE : 'size',
}
# overwrite base class attributes
BaseDoc.TextDoc.SUPPORTED_MARKUP = [
BaseDoc.TextDoc.BOLD,
BaseDoc.TextDoc.ITALIC,
BaseDoc.TextDoc.UNDERLINE,
BaseDoc.TextDoc.FONTFACE,
BaseDoc.TextDoc.FONTSIZE,
BaseDoc.TextDoc.FONTCOLOR,
BaseDoc.TextDoc.HIGHLIGHT,
BaseDoc.TextDoc.SUPERSCRIPT ]
BaseDoc.TextDoc.STYLETAG_MARKUP = {
BaseDoc.TextDoc.BOLD : ("<b>", "</b>"),
BaseDoc.TextDoc.ITALIC : ("<i>", "</i>"),
BaseDoc.TextDoc.UNDERLINE : ("<u>", "</u>"),
BaseDoc.TextDoc.SUPERSCRIPT : ("<sup>", "</sup>"),
}
BaseDoc.TextDoc.ESCAPE_FUNC = lambda x: escape
# BaseDoc implementation # BaseDoc implementation
@ -1142,6 +1168,20 @@ class CairoDoc(BaseDoc.BaseDoc, BaseDoc.TextDoc, BaseDoc.DrawDoc):
def end_cell(self): def end_cell(self):
self._active_element = self._active_element.get_parent() self._active_element = self._active_element.get_parent()
def _create_xmltag(self, type, value):
"""
overwrites the method in BaseDoc.TextDoc.
creates the pango xml tags needed for non bool style types
"""
if type not in self.SUPPORTED_MARKUP:
return None
if type == BaseDoc.TextDoc.FONTSIZE:
#size is in thousandths of a point in pango
value = str(1000 * value)
return ('<span %s="%s">' % (self.STYLETAG_TO_PROPERTY[type], value),
'</span>')
def write_note(self, text, format, style_name): def write_note(self, text, format, style_name):
""" """
Method to write the note objects text on a Method to write the note objects text on a
@ -1152,11 +1192,15 @@ class CairoDoc(BaseDoc.BaseDoc, BaseDoc.TextDoc, BaseDoc.DrawDoc):
# The markup in the note editor is not in the text so is not # The markup in the note editor is not in the text so is not
# considered. It must be added by pango too # considered. It must be added by pango too
if format == 1: if format == 1:
for line in text.split('\n'): #preformatted, retain whitespace. Cairo retains \n automatically,
#so use \n\n for paragraph detection
#this is bad code, a user can type spaces between paragraph!
for line in text.split('\n\n'):
self.start_paragraph(style_name) self.start_paragraph(style_name)
self.write_text(line) self.write_text(line)
self.end_paragraph() self.end_paragraph()
elif format == 0: elif format == 0:
#this is bad code, a user can type spaces between paragraph!
for line in text.split('\n\n'): for line in text.split('\n\n'):
self.start_paragraph(style_name) self.start_paragraph(style_name)
line = line.replace('\n',' ') line = line.replace('\n',' ')
@ -1164,14 +1208,46 @@ class CairoDoc(BaseDoc.BaseDoc, BaseDoc.TextDoc, BaseDoc.DrawDoc):
self.write_text(line) self.write_text(line)
self.end_paragraph() self.end_paragraph()
def write_styled_note(self, styledtext, format, style_name):
"""
Convenience function to write a styledtext to the cairo doc.
styledtext : assumed a StyledText object to write
format : = 0 : Flowed, = 1 : Preformatted
style_name : name of the style to use for default presentation
@note: text=normal text, p_text=text with pango markup, s_tags=styled
text tags, p
"""
text = str(styledtext)
s_tags = styledtext.get_tags()
markuptext = self._add_markup_from_styled(text, s_tags)
if format == 1:
#preformatted, retain whitespace. Cairo retains \n automatically,
#so use \n\n for paragraph detection
#FIXME: following split should be regex to match \n\s*\n instead?
for line in markuptext.split('\n\n'):
self.start_paragraph(style_name)
self.__write_text(line, markup=True)
self.end_paragraph()
elif format == 0:
#flowed
#FIXME: following split should be regex to match \n\s*\n instead?
for line in markuptext.split('\n\n'):
self.start_paragraph(style_name)
#flowed, make normal whitespace go away
line = line.replace('\n',' ')
line = ' '.join(line.split())
self.__write_text(line, markup=True)
self.end_paragraph()
def __write_text(self, text, mark=None, markup=False): def __write_text(self, text, mark=None, markup=False):
""" """
@param text: text to write. @param text: text to write.
@param mark: IndexMark to use for indexing (if supported) @param mark: IndexMark to use for indexing (if supported)
@param markup: True if text already contains markup info. @param markup: True if text already contains markup info.
Then text will no longer be escaped Then text will no longer be escaped
Private method: reports should not add markup in text to override
the style
""" """
if not markup: if not markup:
# We need to escape the text here for later pango.Layout.set_markup # We need to escape the text here for later pango.Layout.set_markup
@ -1187,7 +1263,19 @@ class CairoDoc(BaseDoc.BaseDoc, BaseDoc.TextDoc, BaseDoc.DrawDoc):
@param text: text to write. @param text: text to write.
@param mark: IndexMark to use for indexing (if supported) @param mark: IndexMark to use for indexing (if supported)
""" """
self. __write_text(text, mark) self.__write_text(text, mark)
def write_markup(self, text, s_tags):
"""
Writes the text in the current paragraph. Should only be used after a
start_paragraph and before an end_paragraph.
@param text: text to write. The text is assumed to be _not_ escaped
@param s_tags: assumed to be list of styledtexttags to apply to the
text
"""
markuptext = self._add_markup_from_styled(text, s_tags)
self.__write_text(text, markup=True)
def add_media_object(self, name, pos, x_cm, y_cm): def add_media_object(self, name, pos, x_cm, y_cm):
new_image = GtkDocPicture(pos, name, x_cm, y_cm) new_image = GtkDocPicture(pos, name, x_cm, y_cm)

View File

@ -128,9 +128,9 @@ class IndivCompleteReport(Report):
for notehandle in event.get_note_list(): for notehandle in event.get_note_list():
note = self.database.get_note_from_handle(notehandle) note = self.database.get_note_from_handle(notehandle)
text = note.get() text = note.get_styledtext()
format = note.get_format() format = note.get_format()
self.doc.write_note(text,format, 'IDS-Normal') self.doc.write_styled_note(text, format, 'IDS-Normal')
self.doc.end_cell() self.doc.end_cell()
self.doc.end_row() self.doc.end_row()
@ -163,11 +163,12 @@ class IndivCompleteReport(Report):
for notehandle in notelist: for notehandle in notelist:
note = self.database.get_note_from_handle(notehandle) note = self.database.get_note_from_handle(notehandle)
text = note.get() text = note.get_styledtext()
format = note.get_format() format = note.get_format()
self.doc.start_row() self.doc.start_row()
self.doc.start_cell('IDS-NormalCell', 2) self.doc.start_cell('IDS-NormalCell', 2)
self.doc.write_note(text,format, 'IDS-Normal') self.doc.write_styled_note(text, format, 'IDS-Normal')
self.doc.end_cell() self.doc.end_cell()
self.doc.end_row() self.doc.end_row()