Patch from Adam Stein <adam@csh.rit.edu>: Add support for links and cropped images in ODF document output. Also related to: http://www.gramps-project.org/bugs/view.php?id=4774

svn: r17451
This commit is contained in:
Brian Matherly
2011-05-08 03:29:36 +00:00
parent dc73ac9268
commit 3d4f4f031c
10 changed files with 506 additions and 48 deletions

View File

@@ -2,6 +2,7 @@
# Gramps - a GTK+/GNOME based genealogy program # Gramps - a GTK+/GNOME based genealogy program
# #
# Copyright (C) 2000-2006 Donald N. Allingham # Copyright (C) 2000-2006 Donald N. Allingham
# Copyright (C) 2011 Adam Stein <adam@csh.rit.edu>
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -69,6 +70,150 @@ def resize_to_jpeg(source, destination, width, height):
scaled = img.scale_simple(width, height, gtk.gdk.INTERP_BILINEAR) scaled = img.scale_simple(width, height, gtk.gdk.INTERP_BILINEAR)
scaled.save(destination, 'jpeg') scaled.save(destination, 'jpeg')
#-------------------------------------------------------------------------
#
# image_dpi
#
#-------------------------------------------------------------------------
def image_dpi(source):
"""
Return the dpi found in the image header. None is returned if no dpi attribute
is available.
:param source: source image file, in any format that PIL recognizes
:type source: unicode
:rtype: int
:returns: the DPI setting in the image header
"""
try:
import PIL.Image
except ImportError:
import sys
print >> sys.stderr, _("WARNING: PIL module not loaded. "
"Image cropping in report files will not be available.")
dpi = None
else:
try:
img = PIL.Image.open(source)
except IOError:
dpi = None
else:
try:
dpi = img.info["dpi"]
except AttributeError, KeyError:
dpi = None
return dpi
#-------------------------------------------------------------------------
#
# image_dpi
#
#-------------------------------------------------------------------------
def image_dpi(source):
"""
Return the dpi found in the image header. None is returned if no dpi attribute
is available.
:param source: source image file, in any format that PIL recognizes
:type source: unicode
:rtype: int
:returns: the DPI setting in the image header
"""
try:
import PIL.Image
except ImportError:
import sys
print >> sys.stderr, _("WARNING: PIL module not loaded. "
"Image cropping in report files will not be available.")
dpi = None
else:
try:
img = PIL.Image.open(source)
except IOError:
dpi = None
else:
try:
dpi = img.info["dpi"]
except AttributeError, KeyError:
dpi = None
return dpi
#-------------------------------------------------------------------------
#
# image_dpi
#
#-------------------------------------------------------------------------
def image_dpi(source):
"""
Return the dpi found in the image header. None is returned if no dpi attribute
is available.
:param source: source image file, in any format that PIL recognizes
:type source: unicode
:rtype: int
:returns: the DPI setting in the image header
"""
try:
import PIL.Image
except ImportError:
import sys
print >> sys.stderr, _("WARNING: PIL module not loaded. "
"Image cropping in report files will not be available.")
dpi = None
else:
try:
img = PIL.Image.open(source)
except IOError:
dpi = None
else:
try:
dpi = img.info["dpi"]
except AttributeError, KeyError:
dpi = None
return dpi
#-------------------------------------------------------------------------
#
# image_dpi
#
#-------------------------------------------------------------------------
def image_dpi(source):
"""
Return the dpi found in the image header. None is returned if no dpi attribute
is available.
:param source: source image file, in any format that PIL recognizes
:type source: unicode
:rtype: int
:returns: the DPI setting in the image header
"""
try:
import PIL.Image
except ImportError:
import sys
print >> sys.stderr, _("WARNING: PIL module not loaded. "
"Image cropping in report files will not be available.")
dpi = None
else:
try:
img = PIL.Image.open(source)
except IOError:
dpi = None
else:
try:
dpi = img.info["dpi"]
except AttributeError, KeyError:
dpi = None
return dpi
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# image_size # image_size
@@ -94,6 +239,138 @@ def image_size(source):
height = 0 height = 0
return (width, height) return (width, height)
#-------------------------------------------------------------------------
#
# image_cropped_size
#
#-------------------------------------------------------------------------
def image_actual_size(x_cm, y_cm, x, y):
"""
Calculate what the actual width & height of the image should be.
:param x_cm: width in centimeters
:type source: int
:param y_cm: height in centimeters
:type source: int
:param x: desired width in pixels
:type source: int
:param y: desired height in pixels
:type source: int
:rtype: tuple(int, int)
:returns: a tuple consisting of the width and height in centimeters
"""
print "[Actual Size] CM = [", x_cm, ",", y_cm, "], Desired = [", x, ",", y, "]"
ratio = float(x_cm)*float(y)/(float(y_cm)*float(x))
if ratio < 1:
act_width = x_cm
act_height = y_cm*ratio
else:
act_height = y_cm
act_width = x_cm/ratio
print "[Actual Size] Size = [", act_width, ",", act_height, "]"
return (act_width, act_height)
#-------------------------------------------------------------------------
#
# image_cropped_size
#
#-------------------------------------------------------------------------
def image_actual_size(x_cm, y_cm, x, y):
"""
Calculate what the actual width & height of the image should be.
:param x_cm: width in centimeters
:type source: int
:param y_cm: height in centimeters
:type source: int
:param x: desired width in pixels
:type source: int
:param y: desired height in pixels
:type source: int
:rtype: tuple(int, int)
:returns: a tuple consisting of the width and height in centimeters
"""
print "[Actual Size] CM = [", x_cm, ",", y_cm, "], Desired = [", x, ",", y, "]"
ratio = float(x_cm)*float(y)/(float(y_cm)*float(x))
if ratio < 1:
act_width = x_cm
act_height = y_cm*ratio
else:
act_height = y_cm
act_width = x_cm/ratio
print "[Actual Size] Size = [", act_width, ",", act_height, "]"
return (act_width, act_height)
#-------------------------------------------------------------------------
#
# image_cropped_size
#
#-------------------------------------------------------------------------
def image_actual_size(x_cm, y_cm, x, y):
"""
Calculate what the actual width & height of the image should be.
:param x_cm: width in centimeters
:type source: int
:param y_cm: height in centimeters
:type source: int
:param x: desired width in pixels
:type source: int
:param y: desired height in pixels
:type source: int
:rtype: tuple(int, int)
:returns: a tuple consisting of the width and height in centimeters
"""
ratio = float(x_cm)*float(y)/(float(y_cm)*float(x))
if ratio < 1:
act_width = x_cm
act_height = y_cm*ratio
else:
act_height = y_cm
act_width = x_cm/ratio
return (act_width, act_height)
#-------------------------------------------------------------------------
#
# image_cropped_size
#
#-------------------------------------------------------------------------
def image_actual_size(x_cm, y_cm, x, y):
"""
Calculate what the actual width & height of the image should be.
:param x_cm: width in centimeters
:type source: int
:param y_cm: height in centimeters
:type source: int
:param x: desired width in pixels
:type source: int
:param y: desired height in pixels
:type source: int
:rtype: tuple(int, int)
:returns: a tuple consisting of the width and height in centimeters
"""
ratio = float(x_cm)*float(y)/(float(y_cm)*float(x))
if ratio < 1:
act_width = x_cm
act_height = y_cm*ratio
else:
act_height = y_cm
act_width = x_cm/ratio
return (act_width, act_height)
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# resize_to_jpeg_buffer # resize_to_jpeg_buffer

View File

@@ -3,6 +3,7 @@
# #
# Copyright (C) 2009 B. Malengier # Copyright (C) 2009 B. Malengier
# Copyright (C) 2010 Jakim Friant # Copyright (C) 2010 Jakim Friant
# Copyright (C) 2011 Adam Stein <adam@csh.rit.edu>
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -34,6 +35,6 @@ from paragraphstyle import ParagraphStyle, PARA_ALIGN_CENTER, PARA_ALIGN_LEFT,\
from tablestyle import TableStyle, TableCellStyle from tablestyle import TableStyle, TableCellStyle
from stylesheet import StyleSheetList, StyleSheet, SheetParser from stylesheet import StyleSheetList, StyleSheet, SheetParser
from graphicstyle import GraphicsStyle, SOLID, DASHED, DOTTED from graphicstyle import GraphicsStyle, SOLID, DASHED, DOTTED
from textdoc import TextDoc, IndexMark,INDEX_TYPE_ALP, INDEX_TYPE_TOC from textdoc import TextDoc, IndexMark,INDEX_TYPE_ALP, INDEX_TYPE_TOC, URL_PATTERN
from drawdoc import DrawDoc from drawdoc import DrawDoc
from graphdoc import GVDoc from graphdoc import GVDoc

View File

@@ -7,6 +7,7 @@
# Copyright (C) 2009 Benny Malengier # Copyright (C) 2009 Benny Malengier
# Copyright (C) 2009 Gary Burton # Copyright (C) 2009 Gary Burton
# Copyright (C) 2010 Peter Landgren # Copyright (C) 2010 Peter Landgren
# Copyright (C) 2011 Adam Stein <adam@csh.rit.edu>
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -47,6 +48,13 @@
import logging import logging
log = logging.getLogger(".textdoc") log = logging.getLogger(".textdoc")
#-------------------------------------------------------------------------
#
# URL string pattern
#
#-------------------------------------------------------------------------
URL_PATTERN = r'''(((https?|mailto):)(//([^/?#"]*))?([^?#"]*)(\?([^#"]*))?(#([^"]*))?)'''
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# IndexMark types # IndexMark types
@@ -160,13 +168,14 @@ class TextDoc(object):
""" """
raise NotImplementedError raise NotImplementedError
def write_text(self, text, mark=None): def write_text(self, text, mark=None, links=False):
""" """
Writes the text in the current paragraph. Should only be used after a Writes the text in the current paragraph. Should only be used after a
start_paragraph and before an end_paragraph. start_paragraph and before an end_paragraph.
@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 links: make URLs in the text clickable (if supported)
""" """
raise NotImplementedError raise NotImplementedError
@@ -194,17 +203,18 @@ class TextDoc(object):
""" """
raise NotImplementedError raise NotImplementedError
def write_endnotes_ref(self, text, style_name): def write_endnotes_ref(self, text, style_name, links=False):
""" """
Writes the note's text and take care of paragraphs, Writes the note's text and take care of paragraphs,
@param text: text to write. @param text: text to write.
@param style_name: style to be used. @param style_name: style to be used.
@param links: make URLs in the text clickable (if supported)
""" """
raise NotImplementedError raise NotImplementedError
def write_styled_note(self, styledtext, format, style_name, def write_styled_note(self, styledtext, format, style_name,
contains_html=False): contains_html=False, links=False):
""" """
Convenience function to write a styledtext to the cairo doc. Convenience function to write a styledtext to the cairo doc.
styledtext : assumed a StyledText object to write styledtext : assumed a StyledText object to write
@@ -214,6 +224,7 @@ class TextDoc(object):
If contains_html=True, then the textdoc is free to handle that in 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 some way. Eg, a textdoc could remove all tags, or could make sure
a link is clickable. a link is clickable.
links: bool, make URLs in the text clickable (if supported)
overwrite this method if the backend supports styled notes overwrite this method if the backend supports styled notes
""" """
@@ -252,7 +263,7 @@ class TextDoc(object):
else: else:
self.write_text(piece) self.write_text(piece)
def add_media_object(self, name, align, w_cm, h_cm, alt=''): def add_media_object(self, name, align, w_cm, h_cm, alt='', style_name=None, crop=None):
""" """
Add a photo of the specified width (in centimeters). Add a photo of the specified width (in centimeters).
@@ -262,6 +273,8 @@ class TextDoc(object):
@param w_cm: width in centimeters @param w_cm: width in centimeters
@param h_cm: height in centimeters @param h_cm: height in centimeters
@param alt: an alternative text to use. Useful for eg html reports @param alt: an alternative text to use. Useful for eg html reports
@param style_name: style to use for captions
@param crop: image cropping parameters
""" """
raise NotImplementedError raise NotImplementedError
@@ -293,3 +306,4 @@ class TextDoc(object):
so that docgen ntypes are not required to have this. so that docgen ntypes are not required to have this.
""" """
pass pass

View File

@@ -4,6 +4,7 @@
# Copyright (C) 2007 Brian G. Matherly # Copyright (C) 2007 Brian G. Matherly
# Copyright (C) 2010 Peter Landgren # Copyright (C) 2010 Peter Landgren
# Copyright (C) 2010 Jakim Friant # Copyright (C) 2010 Jakim Friant
# Copyright (C) 2011 Adam Stein <adam@csh.rit.edu>
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -90,7 +91,7 @@ def cite_source(bibliography, obj):
txt += key txt += key
return txt return txt
def write_endnotes(bibliography, database, doc, printnotes=False): def write_endnotes(bibliography, database, doc, printnotes=False, links=False):
""" """
Write all the entries in the bibliography as endnotes. Write all the entries in the bibliography as endnotes.
@@ -103,6 +104,8 @@ def write_endnotes(bibliography, database, doc, printnotes=False):
@param printnotes: Indicate if the notes attached to a source must be @param printnotes: Indicate if the notes attached to a source must be
written too. written too.
@type printnotes: bool @type printnotes: bool
@param links: Indicate if URL links should be makde 'clickable'.
@type links: bool
""" """
if bibliography.get_citation_count() == 0: if bibliography.get_citation_count() == 0:
return return
@@ -121,7 +124,7 @@ def write_endnotes(bibliography, database, doc, printnotes=False):
src_txt = _format_source_text(source) src_txt = _format_source_text(source)
doc.write_text(src_txt) doc.write_text(src_txt, links=links)
doc.end_paragraph() doc.end_paragraph()
ref_list = citation.get_ref_list() ref_list = citation.get_ref_list()
@@ -136,7 +139,7 @@ def write_endnotes(bibliography, database, doc, printnotes=False):
first = False first = False
else: else:
reflines += ('\n%s' % txt) reflines += ('\n%s' % txt)
doc.write_endnotes_ref(reflines,'Endnotes-Ref') doc.write_endnotes_ref(reflines,'Endnotes-Ref', links=links)
if printnotes: if printnotes:
note_list = source.get_note_list() note_list = source.get_note_list()
@@ -151,7 +154,8 @@ def write_endnotes(bibliography, database, doc, printnotes=False):
doc.write_styled_note(note.get_styledtext(), doc.write_styled_note(note.get_styledtext(),
note.get_format(),'Endnotes-Notes', note.get_format(),'Endnotes-Notes',
contains_html= note.get_type() \ contains_html= note.get_type() \
== NoteType.HTML_CODE) == NoteType.HTML_CODE,
links=links)
ind += 1 ind += 1
def _format_source_text(source): def _format_source_text(source):

View File

@@ -5,6 +5,7 @@
# Copyright (C) 2007-2009 Brian G. Matherly # Copyright (C) 2007-2009 Brian G. Matherly
# Copyright (C) 2009-2010 Benny Malengier <benny.malengier@gramps-project.org> # Copyright (C) 2009-2010 Benny Malengier <benny.malengier@gramps-project.org>
# Copyright (C) 2010 Peter Landgren # Copyright (C) 2010 Peter Landgren
# Copyright (C) 2011 Adam Stein <adam@csh.rit.edu>
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -361,7 +362,7 @@ class AsciiDoc(BaseDoc,TextDoc):
if self.cell_lines[self.cellnum] > self.maxlines: if self.cell_lines[self.cellnum] > self.maxlines:
self.maxlines = self.cell_lines[self.cellnum] self.maxlines = self.cell_lines[self.cellnum]
def add_media_object(self, name, align, w_cm, h_cm, alt=''): def add_media_object(self, name, align, w_cm, h_cm, alt='', style_name=None, crop=None):
this_text = '(photo)' this_text = '(photo)'
if self.in_cell: if self.in_cell:
self.cellpars[self.cellnum] += this_text self.cellpars[self.cellnum] += this_text
@@ -369,7 +370,7 @@ class AsciiDoc(BaseDoc,TextDoc):
self.f.write(this_text) self.f.write(this_text)
def write_styled_note(self, styledtext, format, style_name, def write_styled_note(self, styledtext, format, style_name,
contains_html=False): contains_html=False, links=False):
""" """
Convenience function to write a styledtext to the ASCII doc. Convenience function to write a styledtext to the ASCII doc.
styledtext : assumed a StyledText object to write styledtext : assumed a StyledText object to write
@@ -379,6 +380,7 @@ class AsciiDoc(BaseDoc,TextDoc):
If contains_html=True, then the textdoc is free to handle that in 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 some way. Eg, a textdoc could remove all tags, or could make sure
a link is clickable. AsciiDoc prints the html without handling it 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: if contains_html:
return return
@@ -402,7 +404,7 @@ class AsciiDoc(BaseDoc,TextDoc):
self.write_text(line) self.write_text(line)
self.end_paragraph() self.end_paragraph()
def write_endnotes_ref(self, text, style_name): def write_endnotes_ref(self, text, style_name, links=False):
""" """
Overwrite base method for lines of endnotes references Overwrite base method for lines of endnotes references
""" """
@@ -415,5 +417,5 @@ class AsciiDoc(BaseDoc,TextDoc):
# #
# Writes text. # Writes text.
#-------------------------------------------------------------------- #--------------------------------------------------------------------
def write_text(self,text,mark=None): def write_text(self,text,mark=None,links=False):
self.text = self.text + text self.text = self.text + text

View File

@@ -6,6 +6,7 @@
# Copyright (C) 2009-2010 Benny Malengier <benny.malengier@gramps-project.org> # Copyright (C) 2009-2010 Benny Malengier <benny.malengier@gramps-project.org>
# Copyright (C) 2010 Peter Landgren # Copyright (C) 2010 Peter Landgren
# Copyright (C) 2010 Tim Lyons # Copyright (C) 2010 Tim Lyons
# Copyright (C) 2011 Adam Stein <adam@csh.rit.edu>
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@@ -316,7 +317,7 @@ class HtmlDoc(BaseDoc, TextDoc):
""" """
self.__write_text('&nbsp;', markup=True) self.__write_text('&nbsp;', markup=True)
def write_text(self, text, mark=None): def write_text(self, text, mark=None, links=False):
""" """
Overwrite base method Overwrite base method
""" """
@@ -448,7 +449,7 @@ class HtmlDoc(BaseDoc, TextDoc):
""" """
self.__reduce_list() self.__reduce_list()
def write_endnotes_ref(self, text, style_name): def write_endnotes_ref(self, text, style_name, links=False):
""" """
Overwrite base method for lines of endnotes references Overwrite base method for lines of endnotes references
""" """
@@ -465,7 +466,7 @@ class HtmlDoc(BaseDoc, TextDoc):
self.__reduce_list() self.__reduce_list()
def write_styled_note(self, styledtext, format, style_name, def write_styled_note(self, styledtext, format, style_name,
contains_html=False): contains_html=False, links=False):
""" """
Convenience function to write a styledtext to the html doc. Convenience function to write a styledtext to the html doc.
styledtext : assumed a StyledText object to write styledtext : assumed a StyledText object to write
@@ -532,7 +533,7 @@ class HtmlDoc(BaseDoc, TextDoc):
#end div element #end div element
self.__reduce_list() self.__reduce_list()
def add_media_object(self, name, pos, w_cm, h_cm, alt=''): def add_media_object(self, name, pos, w_cm, h_cm, alt='', style_name=None, crop=None):
""" """
Overwrite base method Overwrite base method
""" """

View File

@@ -8,6 +8,7 @@
# 2003 Alex Roitman # 2003 Alex Roitman
# 2009 Benny Malengier # 2009 Benny Malengier
# 2010 Peter Landgren # 2010 Peter Landgren
# Copyright (C) 2011 Adam Stein <adam@csh.rit.edu>
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -569,7 +570,7 @@ class LaTeXDoc(BaseDoc, TextDoc):
if self.curcol < self.numcols: if self.curcol < self.numcols:
self._backend.write('& ') self._backend.write('& ')
def add_media_object(self, name, pos, x, y, alt=''): def add_media_object(self, name, pos, x, y, alt='', style_name=None, crop=None):
"""Add photo to report""" """Add photo to report"""
return return
@@ -591,7 +592,7 @@ class LaTeXDoc(BaseDoc, TextDoc):
else: else:
self._backend.write('\\centerline{\\includegraphics[%s]{%s}}\n' % (mysize,picf)) self._backend.write('\\centerline{\\includegraphics[%s]{%s}}\n' % (mysize,picf))
def write_text(self,text,mark=None): def write_text(self,text,mark=None,links=False):
"""Write the text to the file""" """Write the text to the file"""
if text == '\n': if text == '\n':
text = '\\newline\n' text = '\\newline\n'
@@ -601,7 +602,7 @@ class LaTeXDoc(BaseDoc, TextDoc):
self._backend.write(text) self._backend.write(text)
def write_styled_note(self, styledtext, format, style_name, def write_styled_note(self, styledtext, format, style_name,
contains_html=False): contains_html=False, links=False):
""" """
Convenience function to write a styledtext to the latex doc. Convenience function to write a styledtext to the latex doc.
styledtext : assumed a StyledText object to write styledtext : assumed a StyledText object to write
@@ -644,7 +645,7 @@ class LaTeXDoc(BaseDoc, TextDoc):
self.end_paragraph() self.end_paragraph()
self._backend.write("\n\\vspace*{0.5cm} \n\\end{minipage}\n\n") self._backend.write("\n\\vspace*{0.5cm} \n\\end{minipage}\n\n")
def write_endnotes_ref(self, text, style_name): def write_endnotes_ref(self, text, style_name, links=False):
""" """
Overwrite base method for lines of endnotes references Overwrite base method for lines of endnotes references
""" """

View File

@@ -6,6 +6,7 @@
# Copyright (C) 2007-2009 Brian G. Matherly # Copyright (C) 2007-2009 Brian G. Matherly
# Copyright (C) 2010 Peter Landgren # Copyright (C) 2010 Peter Landgren
# Copyright (C) 2010 Jakim Friant # Copyright (C) 2010 Jakim Friant
# Copyright (C) 2011 Adam Stein <adam@csh.rit.edu>
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -81,7 +82,7 @@ from xml.sax.saxutils import escape
from gen.plug.docgen import (BaseDoc, TextDoc, DrawDoc, graphicstyle, from gen.plug.docgen import (BaseDoc, TextDoc, DrawDoc, graphicstyle,
FONT_SANS_SERIF, SOLID, PAPER_PORTRAIT, FONT_SANS_SERIF, SOLID, PAPER_PORTRAIT,
INDEX_TYPE_TOC, PARA_ALIGN_CENTER, PARA_ALIGN_LEFT, INDEX_TYPE_TOC, PARA_ALIGN_CENTER, PARA_ALIGN_LEFT,
INDEX_TYPE_ALP, PARA_ALIGN_RIGHT) INDEX_TYPE_ALP, PARA_ALIGN_RIGHT, URL_PATTERN)
from gen.plug.docgen.fontscale import string_width from gen.plug.docgen.fontscale import string_width
from libodfbackend import OdfBackend from libodfbackend import OdfBackend
import const import const
@@ -416,6 +417,8 @@ _SHEADER_FOOTER = '''\
</style:style>\n </style:style>\n
''' '''
_CLICKABLE = r'''<text:a xlink:type="simple" xlink:href="\1">\1</text:a>'''
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# ODFDoc # ODFDoc
@@ -451,7 +454,8 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
self.new_cell = 0 self.new_cell = 0
self.page = 0 self.page = 0
self.first_page = 1 self.first_page = 1
self.StyleList = [] # styles to create depending on styled notes. self.StyleList_notes = [] # styles to create depending on styled notes.
self.StyleList_photos = [] # styles to create depending on clipped images.
def open(self, filename): def open(self, filename):
""" """
@@ -484,7 +488,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
self.lang = current_locale[0] self.lang = current_locale[0]
self.lang = self.lang.replace('_', '-') if self.lang else "en-US" self.lang = self.lang.replace('_', '-') if self.lang else "en-US"
self.StyleList = [] # styles to create depending on styled notes. self.StyleList_notes = [] # styles to create depending on styled notes.
wrt1('<?xml version="1.0" encoding="UTF-8"?>\n' wrt1('<?xml version="1.0" encoding="UTF-8"?>\n'
'<office:document-content\n' + '<office:document-content\n' +
_XMLNS + _XMLNS +
@@ -783,9 +787,11 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
The content.xml file is closed. The content.xml file is closed.
""" """
self.cntntx = StringIO() self.cntntx = StringIO()
self.StyleList = self.uniq(self.StyleList) self.StyleList_notes = self.uniq(self.StyleList_notes)
self.add_styled_notes_fonts() self.add_styled_notes_fonts()
self.add_styled_notes_styles() self.add_styled_notes_styles()
self.StyleList_photos = self.uniq(self.StyleList_photos)
self.add_styled_photo_styles()
self.cntntx.write(self.cntnt1.getvalue()) self.cntntx.write(self.cntnt1.getvalue())
self.cntntx.write(self.cntnt2.getvalue()) self.cntntx.write(self.cntnt2.getvalue())
self.cntntx.write(self.cntnt.getvalue()) self.cntntx.write(self.cntnt.getvalue())
@@ -815,7 +821,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
""" """
# Need to add new font for styled notes here. # Need to add new font for styled notes here.
wrt1 = self.cntnt1.write wrt1 = self.cntnt1.write
for style in self.StyleList: for style in self.StyleList_notes:
if style[1] == "FontFace": if style[1] == "FontFace":
wrt1( wrt1(
'<style:font-face ' + '<style:font-face ' +
@@ -830,7 +836,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
""" """
# Need to add new style for styled notes here. # Need to add new style for styled notes here.
wrt2 = self.cntnt2.write wrt2 = self.cntnt2.write
for style in self.StyleList: for style in self.StyleList_notes:
if style[1] == "FontSize": if style[1] == "FontSize":
wrt2( wrt2(
'<style:style ' + '<style:style ' +
@@ -874,7 +880,114 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
'</style:style>\n' '</style:style>\n'
) )
def add_media_object(self, file_name, pos, x_cm, y_cm, alt=''): def add_styled_photo_styles(self):
"""
Add the new styles for clipped images in the automatic-styles section.
"""
wrt2 = self.cntnt2.write
for style in self.StyleList_photos:
if style[0] == "Left":
wrt2(
'<style:style ' +
'style:name="Left_%s" ' % str(style[1]) +
'style:family="graphic" ' +
'style:parent-style-name="photo">' +
'<style:graphic-properties ' +
'style:run-through="foreground" ' +
'style:wrap="dynamic" ' +
'style:number-wrapped-paragraphs="no-limit" ' +
'style:wrap-contour="false" ' +
'style:vertical-pos="from-top" ' +
'style:vertical-rel="paragraph-content" ' +
'style:horizontal-pos="left" ' +
'style:horizontal-rel="paragraph-content" ' +
'style:mirror="none" ' +
'fo:clip="rect(%fin %fin %fin %fin)" ' % style[1] +
'draw:luminance="0%" ' +
'draw:contrast="0" ' +
'draw:red="0%" ' +
'draw:green="0%" ' +
'draw:blue="0%" ' +
'draw:gamma="1" ' +
'draw:color-inversion="false" ' +
'draw:transparency="-100%" ' +
'draw:color-mode="standard"/>' +
'</style:style>\n'
)
elif style[0] == "Right":
wrt2(
'<style:style ' +
'style:name="Right_%s" ' % str(style[1]) +
'style:family="graphic" ' +
'style:parent-style-name="photo">' +
'<style:graphic-properties ' +
'style:run-through="foreground" ' +
'style:wrap="dynamic" ' +
'style:number-wrapped-paragraphs="no-limit" ' +
'style:wrap-contour="false" ' +
'style:vertical-pos="from-top" ' +
'style:vertical-rel="paragraph-content" ' +
'style:horizontal-pos="right" ' +
'style:horizontal-rel="paragraph-content" ' +
'style:mirror="none" ' +
'fo:clip="rect(%fin %fin %fin %fin)" ' % style[1] +
'draw:luminance="0%" ' +
'draw:contrast="0" ' +
'draw:red="0%" ' +
'draw:green="0%" ' +
'draw:blue="0%" ' +
'draw:gamma="1" ' +
'draw:color-inversion="false" ' +
'draw:transparency="-100%" ' +
'draw:color-mode="standard"/>' +
'</style:style>\n'
)
elif style[0] == "Single":
wrt2(
'<style:style ' +
'style:name="Single_%s" ' % str(style[1]) +
'style:family="graphic" ' +
'<style:graphic-properties ' +
'style:vertical-pos="from-top" ' +
'style:mirror="none" ' +
'fo:clip="rect(%fin %fin %fin %fin)" ' % style[1] +
'draw:luminance="0%" ' +
'draw:contrast="0" ' +
'draw:red="0%" ' +
'draw:green="0%" ' +
'draw:blue="0%" ' +
'draw:gamma="1" ' +
'draw:color-inversion="false" ' +
'draw:transparency="-100%" ' +
'draw:color-mode="standard"/>' +
'</style:style>\n'
)
else:
wrt2(
'<style:style ' +
'style:name="Row_%s" ' % str(style[1]) +
'style:family="graphic" ' +
'style:parent-style-name="Graphics">' +
'<style:graphic-properties ' +
'style:vertical-pos="from-top" ' +
'style:vertical-rel="paragraph" ' +
'style:horizontal-pos="from-left" ' +
'style:horizontal-rel="paragraph" ' +
'style:mirror="none" ' +
'fo:clip="rect(%fin %fin %fin %fin)" ' % style[1] +
'draw:luminance="0%" ' +
'draw:contrast="0" ' +
'draw:red="0%" ' +
'draw:green="0%" ' +
'draw:blue="0%" ' +
'draw:gamma="1" ' +
'draw:color-inversion="false" ' +
'draw:transparency="-100%" ' +
'draw:color-mode="standard"/>' +
'</style:style>\n'
)
def add_media_object(self, file_name, pos, x_cm, y_cm, alt='', style_name=None, crop=None):
""" """
Add multi-media documents : photos Add multi-media documents : photos
""" """
@@ -885,14 +998,6 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
if (x, y) == (0, 0): if (x, y) == (0, 0):
return return
ratio = float(x_cm) * float(y) / (float(y_cm) * float(x))
if ratio < 1:
act_width = x_cm
act_height = y_cm * ratio
else:
act_height = y_cm
act_width = x_cm / ratio
not_extension, extension = os.path.splitext(file_name) not_extension, extension = os.path.splitext(file_name)
odf_name = md5(file_name).hexdigest() + extension odf_name = md5(file_name).hexdigest() + extension
@@ -907,6 +1012,37 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
self.cntnt.write('<text:p>') self.cntnt.write('<text:p>')
pos = pos.title() if pos in ['left', 'right', 'single'] else 'Row' pos = pos.title() if pos in ['left', 'right', 'single'] else 'Row'
if crop:
dpi = ImgManip.image_dpi(file_name)
if dpi:
(act_width, act_height) = ImgManip.image_actual_size(
x_cm, y_cm, crop[2] - crop[0], crop[3] - crop[1]
)
left = ((crop[0]/100.0)*x)/dpi[0]
right = (x - ((crop[2]/100.0)*x))/dpi[0]
top = ((crop[1]/100.0)*y)/dpi[1]
bottom = (y - ((crop[3]/100.0)*y))/dpi[1]
crop = (top, right, bottom, left)
self.StyleList_photos.append(
[pos, crop]
)
pos += "_" + str(crop)
else:
(act_width, act_height) = ImgManip.image_actual_size(x_cm, y_cm, x, y)
else:
(act_width, act_height) = ImgManip.image_actual_size(x_cm, y_cm, x, y)
if len(alt):
self.cntnt.write('<draw:frame draw:style-name="%s" draw:name="caption_%s" text:anchor-type="paragraph" svg:y="0in" svg:width="%.2fcm" draw:z-index="34"> ' % (pos, tag, act_width))
self.cntnt.write('<draw:text-box fo:min-height="%.2fcm"> ' % act_height)
self.cntnt.write('<text:p text:style-name="%s">' % style_name)
self.cntnt.write( self.cntnt.write(
'<draw:frame draw:style-name="%s" ' % pos + '<draw:frame draw:style-name="%s" ' % pos +
'draw:name="%s" ' % tag + 'draw:name="%s" ' % tag +
@@ -920,6 +1056,14 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
'</draw:frame>\n' '</draw:frame>\n'
) )
if len(alt):
self.cntnt.write(
'%s' % alt +
'</text:p>' +
'</draw:text-box>' +
'</draw:frame>'
)
if self.new_cell: if self.new_cell:
self.cntnt.write('</text:p>\n') self.cntnt.write('</text:p>\n')
@@ -1355,7 +1499,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
) )
self.new_cell = 1 self.new_cell = 1
def write_endnotes_ref(self, text, style_name): def write_endnotes_ref(self, text, style_name, links=False):
""" """
Overwrite base method for lines of endnotes references Overwrite base method for lines of endnotes references
""" """
@@ -1364,6 +1508,10 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
# Replace multiple spaces: have to go from the largest number down # Replace multiple spaces: have to go from the largest number down
for n in range(text.count(' '), 1, -1): for n in range(text.count(' '), 1, -1):
text = text.replace(' '*n, ' <text:s text:c="%d"/>' % (n-1) ) text = text.replace(' '*n, ' <text:s text:c="%d"/>' % (n-1) )
if links == True:
text = re.sub(URL_PATTERN, _CLICKABLE, text)
self.start_paragraph(style_name) self.start_paragraph(style_name)
# self.cntnt.write('<text:span text:style-name="GRAMPS-preformat">') # self.cntnt.write('<text:span text:style-name="GRAMPS-preformat">')
self.cntnt.write( self.cntnt.write(
@@ -1374,7 +1522,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
self.end_paragraph() self.end_paragraph()
def write_styled_note(self, styledtext, format, style_name, def write_styled_note(self, styledtext, format, style_name,
contains_html=False): contains_html=False, links=False):
""" """
Convenience function to write a styledtext to the ODF doc. Convenience function to write a styledtext to the ODF doc.
styledtext : assumed a StyledText object to write styledtext : assumed a StyledText object to write
@@ -1384,10 +1532,15 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
If contains_html=True, then the textdoc is free to handle that in 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 some way. Eg, a textdoc could remove all tags, or could make sure
a link is clickable. ODFDoc prints the html without handling it a link is clickable. ODFDoc prints the html without handling it
links: bool, make URLs clickable if True
""" """
text = str(styledtext) text = str(styledtext)
s_tags = styledtext.get_tags() s_tags = styledtext.get_tags()
markuptext = self._backend.add_markup_from_styled(text, s_tags, '\n') markuptext = self._backend.add_markup_from_styled(text, s_tags, '\n')
if links == True:
markuptext = re.sub(URL_PATTERN, _CLICKABLE, markuptext)
# we need to know if we have new styles to add. # we need to know if we have new styles to add.
# if markuptext contains : FontColor, FontFace, FontSize ... # if markuptext contains : FontColor, FontFace, FontSize ...
# we must prepare the new styles for the styles.xml file. # we must prepare the new styles for the styles.xml file.
@@ -1399,7 +1552,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
m = NewStyle.search(markuptext, start) m = NewStyle.search(markuptext, start)
if not m: if not m:
break break
self.StyleList.append([m.group(1)+m.group(2), self.StyleList_notes.append([m.group(1)+m.group(2),
m.group(1), m.group(1),
m.group(2)]) m.group(2)])
start = m.end() start = m.end()
@@ -1418,12 +1571,15 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
linenb += 1 linenb += 1
self.end_paragraph() self.end_paragraph()
def write_text(self, text, mark=None): def write_text(self, text, mark=None, links=False):
""" """
Uses the xml.sax.saxutils.escape function to convert XML Uses the xml.sax.saxutils.escape function to convert XML
entities. The _esc_map dictionary allows us to add our own entities. The _esc_map dictionary allows us to add our own
mappings. mappings.
""" """
if links == True:
text = re.sub(URL_PATTERN, _CLICKABLE, text)
if mark: if mark:
key = escape(mark.key, _esc_map) key = escape(mark.key, _esc_map)
key = key.replace('"', '&quot;') key = key.replace('"', '&quot;')

View File

@@ -4,6 +4,7 @@
# Copyright (C) 2000-2006 Donald N. Allingham # Copyright (C) 2000-2006 Donald N. Allingham
# Copyright (C) 2007-2009 Brian G. Matherly # Copyright (C) 2007-2009 Brian G. Matherly
# Copyright (C) 2010 Jakim Friant # Copyright (C) 2010 Jakim Friant
# Copyright (C) 2011 Adam Stein <adam@csh.rit.edu>
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -147,7 +148,7 @@ class PSDrawDoc(BaseDoc, DrawDoc):
) )
self.file.close() self.file.close()
def write_text(self, text, mark=None): def write_text(self, text, mark=None, links=False):
pass pass
def start_page(self): def start_page(self):

View File

@@ -5,6 +5,7 @@
# Copyright (C) 2007-2009 Brian G. Matherly # Copyright (C) 2007-2009 Brian G. Matherly
# Copyright (C) 2009 Gary Burton # Copyright (C) 2009 Gary Burton
# Copyright (C) 2010 Peter Landgren # Copyright (C) 2010 Peter Landgren
# Copyright (C) 2011 Adam Stein <adam@csh.rit.edu>
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -372,7 +373,7 @@ class RTFDoc(BaseDoc,TextDoc):
# dumped as a string of HEX numbers. # dumped as a string of HEX numbers.
# #
#-------------------------------------------------------------------- #--------------------------------------------------------------------
def add_media_object(self, name, pos, x_cm, y_cm, alt=''): def add_media_object(self, name, pos, x_cm, y_cm, alt='', style_name=None, crop=None):
nx, ny = ImgManip.image_size(name) nx, ny = ImgManip.image_size(name)
@@ -408,7 +409,7 @@ class RTFDoc(BaseDoc,TextDoc):
self.f.write('}}\\par\n') self.f.write('}}\\par\n')
def write_styled_note(self, styledtext, format, style_name, def write_styled_note(self, styledtext, format, style_name,
contains_html=False): contains_html=False, links=False):
""" """
Convenience function to write a styledtext to the RTF doc. Convenience function to write a styledtext to the RTF doc.
styledtext : assumed a StyledText object to write styledtext : assumed a StyledText object to write
@@ -446,7 +447,7 @@ class RTFDoc(BaseDoc,TextDoc):
self.write_text('\n') self.write_text('\n')
self.end_paragraph() self.end_paragraph()
def write_endnotes_ref(self,text,style_name): def write_endnotes_ref(self,text,style_name,links=False):
""" """
Overwrite base method for lines of endnotes references Overwrite base method for lines of endnotes references
""" """
@@ -469,7 +470,7 @@ class RTFDoc(BaseDoc,TextDoc):
# the form of \`XX. Make sure to escape braces. # the form of \`XX. Make sure to escape braces.
# #
#-------------------------------------------------------------------- #--------------------------------------------------------------------
def write_text(self,text,mark=None): def write_text(self,text,mark=None,links=False):
# Convert to unicode, just in case it's not. Fix of bug 2449. # Convert to unicode, just in case it's not. Fix of bug 2449.
text = unicode(text) text = unicode(text)
text = text.replace('\n','\n\\par ') text = text.replace('\n','\n\\par ')