diff --git a/gramps/gen/plug/docgen/basedoc.py b/gramps/gen/plug/docgen/basedoc.py index 4af054745..a82d8e2ba 100644 --- a/gramps/gen/plug/docgen/basedoc.py +++ b/gramps/gen/plug/docgen/basedoc.py @@ -6,6 +6,7 @@ # Copyright (C) 2007 Brian G. Matherly # Copyright (C) 2009 Benny Malengier # Copyright (C) 2009 Gary Burton +# Copyright (C) 2017 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 @@ -78,6 +79,13 @@ class BaseDoc(metaclass=ABCMeta): self._creator = "" self.init_called = False self.uistate = uistate + self._rtl_doc = False # does the document have right-to-left text? + + def set_rtl_doc(self, value): + self._rtl_doc = value + + def get_rtl_doc(self): + return self._rtl_doc def init(self): self.init_called = True diff --git a/gramps/gen/plug/report/_reportbase.py b/gramps/gen/plug/report/_reportbase.py index 962e1b4d1..94e417a28 100644 --- a/gramps/gen/plug/report/_reportbase.py +++ b/gramps/gen/plug/report/_reportbase.py @@ -79,6 +79,7 @@ class Report: self._get_date = locale.get_date self._get_type = locale.get_type self._ldd = locale.date_displayer + self.doc.set_rtl_doc(locale.rtl_locale) self._name_display = NameDisplay(locale) # a legacy/historical name self._name_display.set_name_format(self.database.name_formats) fmt_default = config.get('preferences.name-format') diff --git a/gramps/gen/utils/grampslocale.py b/gramps/gen/utils/grampslocale.py index 485ea5bdd..b9f12cfa4 100644 --- a/gramps/gen/utils/grampslocale.py +++ b/gramps/gen/utils/grampslocale.py @@ -112,6 +112,9 @@ _LOCALE_NAMES = { 'zh_TW': ('Chinese_Taiwan', '950', _("Chinese (Traditional)")), } +# locales with right-to-left text +_RTL_LOCALES = ('ar', 'he') + # locales with less than 70% currently translated INCOMPLETE_TRANSLATIONS = ('ar', 'bg', 'he', 'ja', 'sq', 'ta', 'tr') @@ -526,6 +529,10 @@ class GrampsLocale: self._win_bindtextdomain(self.localedomain.encode('utf-8'), self.localedir.encode('utf-8')) + self.rtl_locale = False + if self.language[0] in _RTL_LOCALES: + self.rtl_locale = True # right-to-left + def _init_secondary_locale(self): """ Init a secondary locale. Secondary locales are used to provide @@ -560,6 +567,9 @@ class GrampsLocale: self.numeric = self.currency = self.calendar = self.collation = self.lang + self.rtl_locale = False + if self.language[0] in _RTL_LOCALES: + self.rtl_locale = True # right-to-left def __init__(self, localedir=None, lang=None, domain=None, languages=None): """ diff --git a/gramps/plugins/docgen/asciidoc.py b/gramps/plugins/docgen/asciidoc.py index 19b84e5c3..38f739b8c 100644 --- a/gramps/plugins/docgen/asciidoc.py +++ b/gramps/plugins/docgen/asciidoc.py @@ -6,7 +6,7 @@ # Copyright (C) 2009-2010 Benny Malengier # Copyright (C) 2010 Peter Landgren # Copyright (C) 2011 Adam Stein -# Copyright (C) 2012 Paul Franklin +# Copyright (C) 2012,2017 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 @@ -153,6 +153,7 @@ class AsciiDoc(BaseDoc, TextDoc): self.tbl_style = None self.in_cell = None self.ncols = 0 + self.column_order = [] self.cellpars = [] self.cell_lines = [] self.cell_widths = [] @@ -309,6 +310,11 @@ class AsciiDoc(BaseDoc, TextDoc): styles = self.get_style_sheet() self.tbl_style = styles.get_table_style(style_name) self.ncols = self.tbl_style.get_columns() + self.column_order = [] + for cell in range(self.ncols): + self.column_order.append(cell) + if self.get_rtl_doc(): + self.column_order.reverse() #-------------------------------------------------------------------- # @@ -333,7 +339,7 @@ class AsciiDoc(BaseDoc, TextDoc): self.maxlines = 0 table_width = (self.get_usable_width() * self.tbl_style.get_width() / 100.0) - for cell in range(self.ncols): + for cell in self.column_order: self.cell_widths[cell] = int( table_width * self.tbl_style.get_column_width(cell) / 100.0) @@ -346,7 +352,7 @@ class AsciiDoc(BaseDoc, TextDoc): def end_row(self): self.in_cell = 0 cell_text = [None]*self.ncols - for cell in range(self.ncols): + for cell in self.column_order: if self.cell_widths[cell]: blanks = ' '*self.cell_widths[cell] + '\n' if self.cell_lines[cell] < self.maxlines: @@ -355,7 +361,7 @@ class AsciiDoc(BaseDoc, TextDoc): ) cell_text[cell] = self.cellpars[cell].split('\n') for line in range(self.maxlines): - for cell in range(self.ncols): + for cell in self.column_order: if self.cell_widths[cell]: self.file.write(cell_text[cell][line]) self.file.write('\n') diff --git a/gramps/plugins/docgen/latexdoc.py b/gramps/plugins/docgen/latexdoc.py index fef030db5..0e2a648fc 100644 --- a/gramps/plugins/docgen/latexdoc.py +++ b/gramps/plugins/docgen/latexdoc.py @@ -67,7 +67,7 @@ _CLICKABLE = '\\url{\\1}' # #------------------------------------------------------------------------ # For an interim mark e.g. for an intended linebreak I use a special pattern. -# It shouldn't interfere with normal text. In LaTeX charackter '&' is used +# It shouldn't interfere with normal text. In LaTeX character '&' is used # for column separation in tables and may occur there in series. The pattern # is used here before column separation is set. On the other hand incoming # text can't show this pattern for it would have been replaced by '\&\&'. @@ -703,7 +703,6 @@ class LaTeXDoc(BaseDoc, TextDoc): if self.in_multrow_cell: # cols of rows: convert to rows of cols self.repack_row() else: - self.tabmem = TabMem(text) self.tabmem.rows.append(self.tabrow) elif tab_state == TAB_BEG: # text: \\begin{longtable}[l]{ self._backend.write(''.join(('\\grinittab{\\textwidth}{', @@ -770,8 +769,6 @@ class LaTeXDoc(BaseDoc, TextDoc): self.tabmem.rows[-1].addit = self.tabrow.addit self.in_multrow_cell = False - return - def calc_latex_widths(self): """ @@ -954,7 +951,7 @@ class LaTeXDoc(BaseDoc, TextDoc): self._backend.open() # Font size control seems to be limited. For now, ignore - # any style constraints, and use 12pt has the default + # any style constraints, and use 12pt as the default options = "12pt" @@ -1026,7 +1023,6 @@ class LaTeXDoc(BaseDoc, TextDoc): thisstyle.first_line_indent = first self.latexstyle[style_name] = thisstyle - def close(self): """Clean up and close the document""" if self.in_list: @@ -1132,6 +1128,11 @@ class LaTeXDoc(BaseDoc, TextDoc): styles = self.get_style_sheet() self.tblstyle = styles.get_table_style(style_name) self.numcols = self.tblstyle.get_columns() + self.column_order = [] + for cell in range(self.numcols): + self.column_order.append(cell) + if self.get_rtl_doc(): + self.column_order.reverse() tblfmt = '*{%d}{l}' % self.numcols self.emit('\\begin{longtable}[l]{%s}\n' % (tblfmt), TAB_BEG) @@ -1148,7 +1149,7 @@ class LaTeXDoc(BaseDoc, TextDoc): self.doline = False self.skipfirst = False self.curcol = 0 - self.currow = self.currow + 1 + self.currow += 1 def end_row(self): """End the row (new line)""" @@ -1168,7 +1169,7 @@ class LaTeXDoc(BaseDoc, TextDoc): We always place our data inside braces for safety of formatting.""" self.colspan = span - self.curcol = self.curcol + self.colspan + self.curcol += self.colspan styles = self.get_style_sheet() self.cstyle = styles.get_cell_style(style_name) @@ -1208,12 +1209,10 @@ class LaTeXDoc(BaseDoc, TextDoc): self.emit('\\multicolumn{%d}{%s}' % (span, cellfmt), CELL_BEG, span) - def end_cell(self): """Prepares for next cell""" self.emit('', CELL_END) - def add_media(self, infile, pos, x, y, alt='', style_name=None, crop=None): """Add photo to report""" outfile = os.path.splitext(infile)[0] diff --git a/gramps/plugins/docgen/odfdoc.py b/gramps/plugins/docgen/odfdoc.py index 206c8818f..40abc7ab5 100644 --- a/gramps/plugins/docgen/odfdoc.py +++ b/gramps/plugins/docgen/odfdoc.py @@ -7,7 +7,7 @@ # Copyright (C) 2010 Peter Landgren # Copyright (C) 2010 Jakim Friant # Copyright (C) 2011 Adam Stein -# Copyright (C) 2012 Paul Franklin +# Copyright (C) 2012,2017 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 @@ -424,6 +424,8 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.filename = None self.lang = None self._backend = None + self.column_order = None + self.row_cells = None self.span = 0 self.level = 0 self.time = "0000-00-00T00:00:00" @@ -1046,7 +1048,12 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): styles = self.get_style_sheet() table = styles.get_table_style(style_name) - for col in range(table.get_columns()): + self.column_order = [] + for cell in range(table.get_columns()): + self.column_order.append(cell) + if self.get_rtl_doc(): + self.column_order.reverse() + for col in self.column_order: self.cntnt.write( '\n' % (style_name, str(chr(ord('A')+col))) @@ -1063,17 +1070,25 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): open a row """ self.cntnt.write('\n') + self.row_cells = [] + self.cntnt_saved = self.cntnt # save the content up to now def end_row(self): """ close a row """ + self.cntnt = self.cntnt_saved # restore the original contents + if self.get_rtl_doc(): + self.row_cells.reverse() + for cell in self.row_cells: + self.cntnt.write(cell) self.cntnt.write('\n') def start_cell(self, style_name, span=1): """ open a cell """ + self.cntnt = StringIO() # start a new buffer (with the expected name) self.span = span self.cntnt.write( '\n') self.new_cell = 0 + self.row_cells.append(self.cntnt.getvalue()) # save the cell, for now def start_bold(self): """ diff --git a/gramps/plugins/docgen/rtfdoc.py b/gramps/plugins/docgen/rtfdoc.py index 75507026f..19610bcfb 100644 --- a/gramps/plugins/docgen/rtfdoc.py +++ b/gramps/plugins/docgen/rtfdoc.py @@ -6,6 +6,7 @@ # Copyright (C) 2009 Gary Burton # Copyright (C) 2010 Peter Landgren # Copyright (C) 2011 Adam Stein +# Copyright (C) 2017 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 @@ -133,6 +134,7 @@ class RTFDoc(BaseDoc, TextDoc): ) self.in_table = 0 self.text = "" + self.p_wide = float(self.paper.get_usable_width()) #-------------------------------------------------------------------- # @@ -153,7 +155,7 @@ class RTFDoc(BaseDoc, TextDoc): #-------------------------------------------------------------------- # - # Starts a paragraph. Instead of using a style sheet, generate the + # Starts a paragraph. Instead of using a style sheet, generate # the style for each paragraph on the fly. Not the ideal, but it # does work. # @@ -313,7 +315,20 @@ class RTFDoc(BaseDoc, TextDoc): def start_table(self, name, style_name): self.in_table = 1 styles = self.get_style_sheet() - self.tbl_style = styles.get_table_style(style_name) + table_style = styles.get_table_style(style_name) + self._cells = [] + if self.get_rtl_doc(): + cell_percent = 100.0 + else: + cell_percent = 0.0 + for cell in range(table_style.get_columns()): + if self.get_rtl_doc(): + cell_percent -= float(table_style.get_column_width(cell)) + else: + cell_percent += float(table_style.get_column_width(cell)) + self._cells.append(twips((self.p_wide * cell_percent) / 100.0)) + if self._cells[-1:] == [0]: + self._cells[-1:] = [twips(self.p_wide)] # left edge => right edge #-------------------------------------------------------------------- # @@ -328,24 +343,32 @@ class RTFDoc(BaseDoc, TextDoc): # Start a row. RTF uses the \trowd to start a row. RTF also specifies # all the cell data after it has specified the cell definitions for # the row. Therefore it is necessary to keep a list of cell contents - # that is to be written after all the cells are defined. + # that is to be written after all the cells are defined. It is also + # necessary to keep a list of the row columns definitions, since for + # RTL columns the columns which are processed last (in LTR) come first, + # and we don't know which will be skipped with a non-one "span" argument + # until we process them # #-------------------------------------------------------------------- def start_row(self): self.contents = [] + self.columns = [] self.cell = 0 - self.prev = 0 - self.cell_percent = 0.0 self.text = "" self.file.write('\\trowd\n') #-------------------------------------------------------------------- # # End a row. Write the cell contents, separated by the \cell marker, - # then terminate the row + # then terminate the row with a \row marker # #-------------------------------------------------------------------- def end_row(self): + for column in sorted(self.columns): + for line in column[1]: + self.file.write(line) + if self.get_rtl_doc(): + self.contents.reverse() self.file.write('{') for line in self.contents: self.file.write(line) @@ -356,29 +379,26 @@ class RTFDoc(BaseDoc, TextDoc): # # Start a cell. Dump out the cell specifics, such as borders. Cell # widths are kind of interesting. RTF doesn't specify how wide a cell - # is, but rather where it's right edge is in relationship to the + # is, but rather where its right edge is in relationship to the # left margin. This means that each cell is the cumlative of the # previous cells plus its own width. # #-------------------------------------------------------------------- def start_cell(self, style_name, span=1): + cell_data = [] styles = self.get_style_sheet() s = styles.get_cell_style(style_name) - self.remain = span -1 if s.get_top_border(): - self.file.write('\\clbrdrt\\brdrs\\brdrw10\n') + cell_data.append('\\clbrdrt\\brdrs\\brdrw10\n') if s.get_bottom_border(): - self.file.write('\\clbrdrb\\brdrs\\brdrw10\n') + cell_data.append('\\clbrdrb\\brdrs\\brdrw10\n') if s.get_left_border(): - self.file.write('\\clbrdrl\\brdrs\\brdrw10\n') + cell_data.append('\\clbrdrl\\brdrs\\brdrw10\n') if s.get_right_border(): - self.file.write('\\clbrdrr\\brdrs\\brdrw10\n') - table_width = float(self.paper.get_usable_width()) - for cell in range(self.cell, self.cell+span): - self.cell_percent += float(self.tbl_style.get_column_width(cell)) - cell_width = twips((table_width * self.cell_percent)/100.0) + cell_data.append('\\clbrdrr\\brdrs\\brdrw10\n') self.need_nl = False - self.file.write('\\cellx%d\n' % cell_width) + cell_data.append('\\cellx%d\n' % self._cells[self.cell + span - 1]) + self.columns.append([self._cells[self.cell + span - 1], cell_data]) self.cell += 1 #-------------------------------------------------------------------- diff --git a/gramps/plugins/lib/libcairodoc.py b/gramps/plugins/lib/libcairodoc.py index 60b383ea6..c6b4d1820 100644 --- a/gramps/plugins/lib/libcairodoc.py +++ b/gramps/plugins/lib/libcairodoc.py @@ -7,7 +7,7 @@ # Copyright (C) 2009 Brian Matherly # Copyright (C) 2010 Peter Landgren # Copyright (C) 2010 Jakim Friant -# Copyright (C) 2011-2012 Paul Franklin +# Copyright (C) 2011-2017 Paul Franklin # Copyright (C) 2012 Craig Anderson # # This program is free software; you can redistribute it and/or modify @@ -1450,6 +1450,8 @@ class CairoDoc(BaseDoc, TextDoc, DrawDoc): # this is an ugly hack, but got no better idea. self._active_row_style = list(map(style.get_column_width, list(range(style.get_columns())))) + if self.get_rtl_doc(): + self._active_row_style.reverse() def end_table(self): self._active_element = self._active_element.get_parent() @@ -1460,6 +1462,8 @@ class CairoDoc(BaseDoc, TextDoc, DrawDoc): self._active_element = new_row def end_row(self): + if self.get_rtl_doc(): + self._active_element._children.reverse() self._active_element = self._active_element.get_parent() def start_cell(self, style_name, span=1): diff --git a/gramps/plugins/textreport/familygroup.py b/gramps/plugins/textreport/familygroup.py index 8644ce74d..bc5237742 100644 --- a/gramps/plugins/textreport/familygroup.py +++ b/gramps/plugins/textreport/familygroup.py @@ -919,12 +919,14 @@ class FamilyGroupOptions(MenuReportOptions): cell.set_padding(0.1) cell.set_bottom_border(1) cell.set_left_border(1) + cell.set_right_border(1) # for RTL default_style.add_cell_style('FGR-TextContents', cell) cell = TableCellStyle() cell.set_padding(0.1) cell.set_bottom_border(0) cell.set_left_border(1) + cell.set_right_border(1) # for RTL cell.set_padding(0.1) default_style.add_cell_style('FGR-TextChild1', cell) @@ -932,6 +934,7 @@ class FamilyGroupOptions(MenuReportOptions): cell.set_padding(0.1) cell.set_bottom_border(1) cell.set_left_border(1) + cell.set_right_border(1) # for RTL cell.set_padding(0.1) default_style.add_cell_style('FGR-TextChild2', cell)