gramps/gramps/plugins/docgen/asciidoc.py
John Ralls fccfdecd03 [r21148]GrampsLocale: Simplify the file-finding functions
Making use of the fact that GrampsLocale now knows what
encoding to use, and noting that filesystems don't use
more than one encoding to write filenames in directories.

Also specify the encoding on some more files

svn: r21396
2013-02-24 00:52:26 +00:00

447 lines
17 KiB
Python

#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham
# Copyright (C) 2007-2009 Brian G. Matherly
# Copyright (C) 2009-2010 Benny Malengier <benny.malengier@gramps-project.org>
# Copyright (C) 2010 Peter Landgren
# Copyright (C) 2011 Adam Stein <adam@csh.rit.edu>
# Copyright (C) 2012 Paul Franklin
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
#------------------------------------------------------------------------
#
# python modules
#
#------------------------------------------------------------------------
import io
import sys
#------------------------------------------------------------------------
#
# Gramps modules
#
#------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.get_translation().gettext
from gramps.gen.plug.docgen import (BaseDoc, TextDoc,
PARA_ALIGN_RIGHT, PARA_ALIGN_CENTER)
from gramps.gen.errors import ReportError
from gramps.gen.plug.menu import NumberOption
from gramps.gen.plug.report import DocOptions
#------------------------------------------------------------------------
#
# Constants
#
#------------------------------------------------------------------------
LEFT,RIGHT,CENTER = 'LEFT','RIGHT','CENTER'
#------------------------------------------------------------------------
#
# This routine was written by David Mertz and placed into the public
# domain. It is sample code from his book, "Text Processing in Python"
#
# Modified by Alex Roitman: right-pad with spaces, if right_pad==1;
# return empty string if no text was given
# Another argument: "first" is the first line indent in characters
# _relative_ to the "left" margin. It can be negative!
#
#------------------------------------------------------------------------
def reformat_para(para='',left=0,right=72,just=LEFT,right_pad=0,first=0):
if not para.strip():
return "\n"
lines = []
real_left = left+first
alllines = para.split('\n')
for realline in alllines:
words = realline.split()
line = ''
word = 0
end_words = 0
while not end_words:
if not words:
lines.append("\n")
break
if len(words[word]) > right-real_left: # Handle very long words
line = words[word]
word +=1
if word >= len(words):
end_words = 1
else: # Compose line of words
while len(line)+len(words[word]) <= right-real_left:
line += words[word]
word += 1
if word >= len(words):
end_words = 1
break
elif len(line) < right-real_left:
line += ' ' # add a space since there is still room
lines.append(line)
#first line finished, discard first
real_left = left
line = ''
if just==CENTER:
if right_pad:
return '\n'.join(
[' '*(left+first) + ln.center(right-left-first)
for ln in lines[0:1] ] +
[ ' '*left + ln.center(right-left) for ln in lines[1:] ]
)
else:
return '\n'.join(
[' '*(left+first) + ln.center(right-left-first).rstrip()
for ln in lines[0:1] ] +
[' '*left + ln.center(right-left).rstrip()
for ln in lines[1:] ]
)
elif just==RIGHT:
if right_pad:
return '\n'.join([line.rjust(right) for line in lines])
else:
return '\n'.join([line.rjust(right).rstrip() for line in lines])
else: # left justify
if right_pad:
return '\n'.join(
[' '*(left+first) + line.ljust(right-left-first)
for line in lines[0:1] ] +
[' '*left + line.ljust(right-left) for line in lines[1:] ]
)
else:
return '\n'.join(
[' '*(left+first) + line for line in lines[0:1] ] +
[' '*left + line for line in lines[1:] ]
)
#------------------------------------------------------------------------
#
# Ascii
#
#------------------------------------------------------------------------
class AsciiDoc(BaseDoc,TextDoc):
def __init__(self, styles, type, options=None):
BaseDoc.__init__(self, styles, type)
self.__note_format = False
self._cpl = 72 # characters per line, in case the options are ignored
if options:
menu = options.menu
self._cpl = menu.get_option_by_name('linechars').get_value()
#--------------------------------------------------------------------
#
# Opens the file, resets the text buffer.
#
#--------------------------------------------------------------------
def open(self,filename):
if filename[-4:] != ".txt":
self.filename = filename + ".txt"
else:
self.filename = filename
try:
self.f = io.open(self.filename,"w",
encoding='ascii',
errors = 'backslashreplace')
except Exception as msg:
errmsg = "%s\n%s" % (_("Could not create %s") % self.filename, msg)
raise ReportError(errmsg)
self.in_cell = 0
self.text = ""
#--------------------------------------------------------------------
#
# Close the file. Call the app if required.
#
#--------------------------------------------------------------------
def close(self):
self.f.close()
def get_usable_width(self):
return self._cpl
#--------------------------------------------------------------------
#
# Force a section page break
#
#--------------------------------------------------------------------
def page_break(self):
self.f.write('\012')
def start_bold(self):
pass
def end_bold(self):
pass
def start_superscript(self):
self.text = self.text + '['
def end_superscript(self):
self.text = self.text + ']'
#--------------------------------------------------------------------
#
# Starts a paragraph.
#
#--------------------------------------------------------------------
def start_paragraph(self,style_name,leader=None):
styles = self.get_style_sheet()
self.p = styles.get_paragraph_style(style_name)
self.leader = leader
#--------------------------------------------------------------------
#
# End a paragraph. First format it to the desired widths.
# If not in table cell, write it immediately. If in the cell,
# add it to the list for this cell after formatting.
#
#--------------------------------------------------------------------
def end_paragraph(self):
if self.p.get_alignment() == PARA_ALIGN_RIGHT:
fmt = RIGHT
elif self.p.get_alignment() == PARA_ALIGN_CENTER:
fmt = CENTER
else:
fmt = LEFT
if self.in_cell:
right = self.cell_widths[self.cellnum]
else:
right = self.get_usable_width()
# Compute indents in characters. Keep first_indent relative!
regular_indent = 0
first_indent = 0
if self.p.get_left_margin():
regular_indent = int(4*self.p.get_left_margin())
if self.p.get_first_indent():
first_indent = int(4*self.p.get_first_indent())
if self.in_cell and self.cellnum < self.ncols - 1:
right_pad = 1
the_pad = ' '*right
else:
right_pad = 0
the_pad = ''
# Depending on the leader's presence, treat the first line differently
if self.leader:
# If we have a leader then we need to reformat the text
# as if there's no special treatment for the first line.
# Then add leader and eat up the beginning of the first line pad.
# Do not reformat if preformatted notes
if not self.__note_format:
self.leader += ' '
start_at = regular_indent + min(len(self.leader)+first_indent,0)
this_text = reformat_para(self.text,regular_indent,right,fmt,
right_pad)
this_text = (' '*(regular_indent+first_indent) +
self.leader +
this_text[start_at:]
)
else:
this_text = self.text
else:
# If no leader then reformat the text according to the first
# line indent, as specified by style.
# Do not reformat if preformatted notes
if not self.__note_format:
this_text = reformat_para(self.text,regular_indent,right,fmt,
right_pad,first_indent)
else:
this_text = ' '*(regular_indent+first_indent) + self.text
if self.__note_format:
# don't add an extra LF before the_pad if preformatted notes.
if this_text != '\n':
# don't add LF if there is this_text is a LF
this_text += the_pad + '\n'
else:
this_text += '\n' + the_pad + '\n'
if self.in_cell:
self.cellpars[self.cellnum] += this_text
else:
self.f.write(this_text)
self.text = ""
#--------------------------------------------------------------------
#
# Start a table. Grab the table style, and store it.
#
#--------------------------------------------------------------------
def start_table(self, name,style_name):
styles = self.get_style_sheet()
self.tbl_style = styles.get_table_style(style_name)
self.ncols = self.tbl_style.get_columns()
#--------------------------------------------------------------------
#
# End a table. Turn off the self.in_cell flag
#
#--------------------------------------------------------------------
def end_table(self):
self.in_cell = 0
#--------------------------------------------------------------------
#
# Start a row. Initialize lists for cell contents, number of lines,
# and the widths. It is necessary to keep a list of cell contents
# that is to be written after all the cells are defined.
#
#--------------------------------------------------------------------
def start_row(self):
self.cellpars = [''] * self.ncols
self.cell_lines = [0] * self.ncols
self.cell_widths = [0] * self.ncols
self.cellnum = -1
self.maxlines = 0
table_width = self.get_usable_width() * self.tbl_style.get_width() / 100.0
for cell in range(self.ncols):
self.cell_widths[cell] = int(table_width *
self.tbl_style.get_column_width(cell) / 100.0)
#--------------------------------------------------------------------
#
# End a row. Write the cell contents. Write the line of spaces
# if the cell has fewer lines than the maximum number.
#
#--------------------------------------------------------------------
def end_row(self):
self.in_cell = 0
cell_text = [None]*self.ncols
for cell in range(self.ncols):
if self.cell_widths[cell]:
blanks = ' '*self.cell_widths[cell] + '\n'
if self.cell_lines[cell] < self.maxlines:
self.cellpars[cell] += blanks * (
self.maxlines - self.cell_lines[cell]
)
cell_text[cell] = self.cellpars[cell].split('\n')
for line in range(self.maxlines):
for cell in range(self.ncols):
if self.cell_widths[cell]:
self.f.write(cell_text[cell][line])
self.f.write('\n')
#--------------------------------------------------------------------
#
# Start a cell. Set the self.in_cell flag, increment the curren cell number.
#
#--------------------------------------------------------------------
def start_cell(self,style_name,span=1):
self.in_cell = 1
self.cellnum = self.cellnum + span
span -= 1
while span:
self.cell_widths[self.cellnum] += (
self.cell_widths[self.cellnum-span]
)
self.cell_widths[self.cellnum-span] = 0
span -= 1
#--------------------------------------------------------------------
#
# End a cell. Find out the number of lines in this cell, correct
# the maximum number of lines if necessary.
#
#--------------------------------------------------------------------
def end_cell(self):
self.in_cell = 0
self.cell_lines[self.cellnum] = self.cellpars[self.cellnum].count('\n')
if self.cell_lines[self.cellnum] > self.maxlines:
self.maxlines = self.cell_lines[self.cellnum]
def add_media_object(self, name, align, w_cm, h_cm, alt='', style_name=None, crop=None):
this_text = '(photo)'
if self.in_cell:
self.cellpars[self.cellnum] += this_text
else:
self.f.write(this_text)
def write_styled_note(self, styledtext, format, style_name,
contains_html=False, links=False):
"""
Convenience function to write a styledtext to the ASCII doc.
styledtext : assumed a StyledText object to write
format : = 0 : Flowed, = 1 : Preformatted
style_name : name of the style to use for default presentation
contains_html: bool, the backend should not check if html is present.
If contains_html=True, then the textdoc is free to handle that in
some way. Eg, a textdoc could remove all tags, or could make sure
a link is clickable. AsciiDoc prints the html without handling it
links: bool, make the URL in the text clickable (if supported)
"""
if contains_html:
return
text = str(styledtext)
if format:
#Preformatted note, keep all white spaces, tabs, LF's
self.__note_format = True
for line in text.split('\n'):
self.start_paragraph(style_name)
self.write_text(line)
self.end_paragraph()
# Add an extra empty para all lines in each preformatted note
self.start_paragraph(style_name)
self.end_paragraph()
self.__note_format = False
else:
for line in text.split('\n\n'):
self.start_paragraph(style_name)
#line = line.replace('\n',' ')
#line = ' '.join(line.split())
self.write_text(line)
self.end_paragraph()
#--------------------------------------------------------------------
#
# Writes text.
#--------------------------------------------------------------------
def write_text(self,text,mark=None,links=False):
self.text = self.text + text
#------------------------------------------------------------------------
#
# AsciiDocOptions class
#
#------------------------------------------------------------------------
class AsciiDocOptions(DocOptions):
"""
Defines options and provides handling interface.
"""
def __init__(self, name, dbase):
DocOptions.__init__(self, name)
def add_menu_options(self, menu):
"""
Add options to the document menu for the AsciiDoc docgen.
"""
category_name = 'Document Options' # internal name: don't translate
linechars = NumberOption(_('Characters per line'), 72, 20, 9999)
linechars.set_help(_("The number of characters per line"))
menu.add_option(category_name, 'linechars', linechars)