From: Jérôme <rom...@ya...> - 2011-02-19 10:10:44
|
Hi, I do not understand all these "Enhanced" modules! Is it not possible to enhance existing code? 'Trunk' developpement allows new features. If something is missing or could be useful later, then I can try to make patchs against last revisions? I will try to patch[1] trunk and to use your new FilteredReports and enhanced modules on trunk. Note, you do not really need to add *.pyc or *.pyo modules on your archives, *.py files should be enough for running new FilteredReports and related modules. [1] http://www.gramps-project.org/bugs/view.php?id=4593 Thank you for the plugin Jérôme Adam Stein a écrit : > Just created a wiki page and added my report plugin (for Gramps v3.2) > for FilteredReports. This first version is more of a proof-of-concept > than a fully featured plugin (even though it does work given the > limitations specified on the page). > > It's derived from the Detailed Descendant Report, but uses filters > instead of a specific individual to start from. > > You can find the complete description at: > > http://www.gramps-project.org/wiki/index.php?title=Filtered_Reports > |
From: Adam S. <ad...@pp...> - 2011-02-21 12:06:20
|
You are answering your own questions before I can! I didn't want to go into the core and make a lot of changes that might break other things. Since Python is OO, I can derive classes from those that exist, only changing the parts I needed to. For the one thing I couldn't do that without copying over a whole part of the infrastructure, I put in a bug request along with a patch (bug #4593). Having my own enhanced set, I can show what my changes would do so that other developers can take a look. Maybe there are better ways to do what I changed. I already found out that in the next version, there is a much more enhanced SubstKeywords that can pretty much do the little bit that I added plus lots more. Also, it sounds like the way of getting information will be updated to use the privacy flag so that reports wouldn't have to do it themselves. In the meantime, my enhanced changes look at the privacy flag. I'm hoping that the core changes are of enough interest to make it into the core, even if not exactly how I did, but get that functionality in there somehow. If not, people and still use my plugin with my enhanced classes. When I make a report, I spend a fair bit of time tweaking it to get exactly what I want. I thought it would be more beneficial for the computer to do the work for me (cropped images, image captions, respecting the privacy flag, etc.) Adam On Sat, 2011-02-19 at 11:38 +0100, Jérôme wrote: > Oh, you just made new modules for your own use (FilteredReports)! > Yes, it breaks current plugins ecosystem, but this might also work. > > I only looked at ODF enhancement against Gramps 3.2.x (patch added). > > > Jérôme > > > Jérôme a écrit : > > Hi, > > > > > > I do not understand all these "Enhanced" modules! > > Is it not possible to enhance existing code? > > > > 'Trunk' developpement allows new features. If something is missing or > > could be useful later, then I can try to make patchs against last revisions? > > > > I will try to patch[1] trunk and to use your new FilteredReports and > > enhanced modules on trunk. > > > > Note, you do not really need to add *.pyc or *.pyo modules on your > > archives, *.py files should be enough for running new FilteredReports > > and related modules. > > > > > > [1] http://www.gramps-project.org/bugs/view.php?id=4593 > > > > > > Thank you for the plugin > > > > Jérôme > > > > > > > > Adam Stein a écrit : > >> Just created a wiki page and added my report plugin (for Gramps v3.2) > >> for FilteredReports. This first version is more of a proof-of-concept > >> than a fully featured plugin (even though it does work given the > >> limitations specified on the page). > >> > >> It's derived from the Detailed Descendant Report, but uses filters > >> instead of a specific individual to start from. > >> > >> You can find the complete description at: > >> > >> http://www.gramps-project.org/wiki/index.php?title=Filtered_Reports > >> > > > > > > ------------------------------------------------------------------------------ > > The ultimate all-in-one performance toolkit: Intel(R) Parallel Studio XE: > > Pinpoint memory and threading errors before they happen. > > Find and fix more than 250 security defects in the development cycle. > > Locate bottlenecks in serial and parallel code that limit performance. > > http://p.sf.net/sfu/intel-dev2devfeb > > _______________________________________________ > > Gramps-devel mailing list > > Gra...@li... > > https://lists.sourceforge.net/lists/listinfo/gramps-devel > differences between files attachment (ODFDoc_Enhanced.patch) > --- ODFDoc.py 2010-11-19 14:38:42.000000000 +0100 > +++ ODFDoc_Enhanced.py 2011-02-07 13:53:44.000000000 +0100 > @@ -1,17 +1,11 @@ > -# > -# Gramps - a GTK+/GNOME based genealogy program > -# > -# Copyright (C) 2000-2006 Donald N. Allingham > -# Copyright (C) 2005-2009 Serge Noiraud > -# Copyright (C) 2007-2009 Brian G. Matherly > -# Copyright (C) 2010 Peter Landgren > +# Copyright (C) 2011 Adam Stein <ad...@cs...> > # > # 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, > +# 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. > @@ -19,12 +13,9 @@ > # 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: ODFDoc.py 16209 2010-11-18 20:18:06Z bmcage $ > > """ > -ODFDoc : used to generate Open Office Document > +ODFDoc_Enhanced : used to generate document in ODF format (used by OpenOffice.org, LibreOffice, etc.) > """ > > #------------------------------------------------------------------------- > @@ -86,7 +77,7 @@ > from libodfbackend import OdfBackend > import const > from ReportBase import ReportUtils > -import ImgManip > +from ImgManip_Enhanced import ImgManip_Enhanced > import Errors > > #------------------------------------------------------------------------- > @@ -118,7 +109,7 @@ > # ODFDoc > # > #------------------------------------------------------------------------- > -class ODFDoc(BaseDoc, TextDoc, DrawDoc): > +class ODFDoc_Enhanced(BaseDoc, TextDoc, DrawDoc): > """ > The ODF document class > """ > @@ -148,7 +139,8 @@ > self.new_cell = 0 > self.page = 0 > 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): > """ > @@ -183,7 +175,6 @@ > else: > self.lang = "en-US" > > - self.StyleList = [] # styles to create depending on styled notes. > self.cntnt1.write('<?xml version="1.0" encoding="UTF-8"?>\n') > self.cntnt1.write('<office:document-content ') > self.cntnt1.write('xmlns:office="urn:oasis:names:tc:opendocument') > @@ -583,12 +574,18 @@ > The content.xml file is closed. > """ > 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_styles() > + > + self.StyleList_photos = self.uniq(self.StyleList_photos) > + self.add_styled_photo_styles() > + > self.cntntx.write(self.cntnt1.getvalue()) > self.cntntx.write(self.cntnt2.getvalue()) > self.cntntx.write(self.cntnt.getvalue()) > + > self.cntnt1.close() > self.cntnt2.close() > self.cntnt.close() > @@ -614,7 +611,7 @@ > Add the new fonts for Styled notes in the font-face-decls section. > """ > # Need to add new font for styled notes here. > - for style in self.StyleList: > + for style in self.StyleList_notes: > if ( style[1] == "FontFace" ): > self.cntnt1.write('<style:font-face style:name="%s"' % > style[2] ) > @@ -627,7 +624,7 @@ > Add the new styles for Styled notes in the automatic-styles section. > """ > # Need to add new style for styled notes here. > - for style in self.StyleList: > + for style in self.StyleList_notes: > if ( style[1] == "FontSize" ): > self.cntnt2.write('<style:style style:name="FontSize__%s__"' % > style[2] ) > @@ -662,25 +659,95 @@ > self.cntnt2.write(' style:font-pitch="variable"/>') > self.cntnt2.write('</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. > + """ > + > + for style in self.StyleList_photos: > + if style[0] == "Left": > + self.cntnt2.write('<style:style style:name="Left_%s" ' % str(style[1])) > + self.cntnt2.write('style:family="graphic"') > + self.cntnt2.write(' style:parent-style-name="photo">') > + self.cntnt2.write('<style:graphic-properties ') > + self.cntnt2.write('style:run-through="foreground"') > + self.cntnt2.write(' style:wrap="dynamic"') > + self.cntnt2.write(' style:number-wrapped-paragraphs="no-limit"') > + self.cntnt2.write(' style:wrap-contour="false"') > + self.cntnt2.write(' style:vertical-pos="from-top"') > + self.cntnt2.write(' style:vertical-rel="paragraph-content"') > + self.cntnt2.write(' style:horizontal-pos="left"') > + self.cntnt2.write(' style:horizontal-rel="paragraph-content"') > + self.cntnt2.write(' style:mirror="none" fo:clip="rect(%fin %fin %fin %fin)"' % style[1]) > + self.cntnt2.write(' draw:luminance="0%" draw:contrast="0" draw:red="0%"') > + self.cntnt2.write(' draw:green="0%" draw:blue="0%" draw:gamma="1"') > + self.cntnt2.write(' draw:color-inversion="false"') > + self.cntnt2.write(' draw:transparency="-100%"') > + self.cntnt2.write(' draw:color-mode="standard"/>') > + self.cntnt2.write('</style:style>\n') > + elif style[0] == "Right": > + self.cntnt2.write('<style:style style:name="Right_%s" ' % str(style[1])) > + self.cntnt2.write('style:family="graphic"') > + self.cntnt2.write(' style:parent-style-name="photo">') > + self.cntnt2.write('<style:graphic-properties ') > + self.cntnt2.write('style:run-through="foreground"') > + self.cntnt2.write(' style:wrap="dynamic"') > + self.cntnt2.write(' style:number-wrapped-paragraphs="no-limit"') > + self.cntnt2.write(' style:wrap-contour="false" ') > + self.cntnt2.write(' style:vertical-pos="from-top"') > + self.cntnt2.write(' style:vertical-rel="paragraph-content"') > + self.cntnt2.write(' style:horizontal-pos="right"') > + self.cntnt2.write(' style:horizontal-rel="paragraph-content"') > + self.cntnt2.write(' style:mirror="none" fo:clip="rect(%fin %fin %fin %fin)"' % style[1]) > + self.cntnt2.write(' draw:luminance="0%" draw:contrast="0" draw:red="0%"') > + self.cntnt2.write(' draw:green="0%" draw:blue="0%" draw:gamma="1"') > + self.cntnt2.write(' draw:color-inversion="false"') > + self.cntnt2.write(' draw:transparency="-100%"') > + self.cntnt2.write(' draw:color-mode="standard"/>') > + self.cntnt2.write('</style:style>\n') > + elif style[0] == "Single": > + self.cntnt2.write('<style:style style:name="Single_%s" ' % str(style[1])) > + self.cntnt2.write('style:family="graphic"') > + self.cntnt2.write(' style:parent-style-name="Graphics"> ') > + self.cntnt2.write('<style:graphic-properties ') > + self.cntnt2.write('style:vertical-pos="from-top"') > + self.cntnt2.write(' style:mirror="none" fo:clip="rect(%fin %fin %fin %fin)"' % style[1]) > + self.cntnt2.write(' draw:luminance="0%" draw:contrast="0" draw:red="0%"') > + self.cntnt2.write(' draw:green="0%" draw:blue="0%" draw:gamma="1"') > + self.cntnt2.write(' draw:color-inversion="false"') > + self.cntnt2.write(' draw:transparency="-100%"') > + self.cntnt2.write(' draw:color-mode="standard"/> ') > + self.cntnt2.write('</style:style>\n') > + else: > + self.cntnt2.write('<style:style style:name="Row_%s" ' % str(style[1])) > + self.cntnt2.write('style:family="graphic"') > + self.cntnt2.write(' style:parent-style-name="Graphics">') > + self.cntnt2.write('<style:graphic-properties ') > + self.cntnt2.write('style:vertical-pos="from-top"') > + self.cntnt2.write(' style:vertical-rel="paragraph"') > + self.cntnt2.write(' style:horizontal-pos="from-left"') > + self.cntnt2.write(' style:horizontal-rel="paragraph"') > + self.cntnt2.write(' style:mirror="none" fo:clip="rect(%fin %fin %fin %fin)"' % style[1]) > + self.cntnt2.write(' draw:luminance="0%" draw:contrast="0" draw:red="0%"') > + self.cntnt2.write(' draw:green="0%" draw:blue="0%" draw:gamma="1"') > + self.cntnt2.write(' draw:color-inversion="false"') > + self.cntnt2.write(' draw:transparency="-100%"') > + self.cntnt2.write(' draw:color-mode="standard"/>') > + self.cntnt2.write('</style:style>\n') > + > + def add_media_object(self, style_name, file_name, pos, x_cm, y_cm, alt='', crop=None): > """ > Add multi-media documents : photos > """ > > + img = ImgManip_Enhanced(file_name) > + > # try to open the image. If the open fails, it probably wasn't > # a valid image (could be a PDF, or a non-image) > - (x, y) = ImgManip.image_size(file_name) > + (x, y) = img.image_size() > if (x, y) == (0, 0): > 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) > odf_name = md5(file_name).hexdigest() + extension > > @@ -693,14 +760,47 @@ > > if self.new_cell: > self.cntnt.write('<text:p>') > + > if pos == "left": > - self.cntnt.write('<draw:frame draw:style-name="Left" ') > + frame_pos = "Left" > elif pos == "right": > - self.cntnt.write('<draw:frame draw:style-name="Right" ') > + frame_pos = "Right" > elif pos == "single": > - self.cntnt.write('<draw:frame draw:style-name="Single" ') > + frame_pos = "Single" > else: > - self.cntnt.write('<draw:frame draw:style-name="Row" ') > + frame_pos = "Row" > + > + if crop: > + dpi = img.image_dpi() > + > + if dpi: > + (act_width, act_height) = self._image_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( > + [frame_pos, crop] > + ) > + > + frame_pos += "_" + str(crop) > + else: > + (act_width, act_height) = self._image_size(x_cm, y_cm, x, y) > + else: > + (act_width, act_height) = self._image_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"> ' % (frame_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('<draw:frame draw:style-name="%s" ' % frame_pos) > > self.cntnt.write('draw:name="%s" ' % tag) > self.cntnt.write('text:anchor-type="paragraph" ') > @@ -712,6 +812,13 @@ > self.cntnt.write('" xlink:type="simple" xlink:show="embed" ') > self.cntnt.write('xlink:actuate="onLoad"/>\n') > self.cntnt.write('</draw:frame>\n') > + > + if len(alt): > + self.cntnt.write(alt) > + self.cntnt.write('</text:p>') > + self.cntnt.write('</draw:text-box>') > + self.cntnt.write('</draw:frame>') > + > if self.new_cell: > self.cntnt.write('</text:p>\n') > > @@ -802,6 +909,22 @@ > zipinfo.external_attr = 0644 << 16L > zfile.writestr(zipinfo, data) > > + def _image_size(self, x_cm, y_cm, x, y): > + """ > + Calculate what the actual width & height of the image should be > + """ > + > + 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) > + > def _write_zip(self): > """ > Create the odt file. This is a zip file > @@ -1192,7 +1315,7 @@ > self.cntnt.write('</text:h>\n') > 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 > """ > @@ -1201,6 +1324,10 @@ > # Replace multiple spaces: have to go from the largest number down > for n in range(text.count(' '), 1, -1): > text = text.replace(' '*n, ' <text:s text:c="%d"/>' % (n-1) ) > + > + if links == True: > + text = self._make_links(text) > + > self.start_paragraph(style_name) > # self.cntnt.write('<text:span text:style-name="GRAMPS-preformat">') > self.cntnt.write('<text:span text:style-name="Standard">') > @@ -1209,7 +1336,7 @@ > self.end_paragraph() > > 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. > styledtext : assumed a StyledText object to write > @@ -1221,7 +1348,15 @@ > a link is clickable. ODFDoc prints the html without handling it > """ > text = str(styledtext) > + > s_tags = styledtext.get_tags() > + text = text.replace('&', '\1') # must be the first > + text = text.replace('<', '\2') > + text = text.replace('>', '\3') > + > + if links == True: > + text = self._make_links(text) > + > markuptext = self._backend.add_markup_from_styled(text, s_tags, '\n') > # we need to know if we have new styles to add. > # if markuptext contains : FontColor, FontFace, FontSize ... > @@ -1234,12 +1369,15 @@ > m = NewStyle.search(markuptext, start) > if not m: > break > - self.StyleList.append([m.group(1)+m.group(2), > - m.group(1), > - m.group(2)]) > + self.StyleList_notes.append([m.group(1)+m.group(2), > + m.group(1), > + m.group(2)]) > start = m.end() > linenb = 1 > self.start_paragraph(style_name) > + markuptext = markuptext.replace('\1', '&') # must be the first > + markuptext = markuptext.replace('\2', '<') > + markuptext = markuptext.replace('\3', '>') > for line in markuptext.split('\n'): > [line, sigcount] = process_spaces(line, format) > if sigcount == 0: > @@ -1253,12 +1391,16 @@ > linenb += 1 > 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 > entities. The _esc_map dictionary allows us to add our own > mappings. > """ > + > + if links == True: > + text = self._make_links(text) > + > if mark: > key = escape(mark.key, _esc_map) > key = key.replace('"', '"') > @@ -1271,6 +1413,16 @@ > self.cntnt.write('text:outline-level="%d" />' % mark.level) > self.cntnt.write(escape(text, _esc_map)) > > + def _make_links(self, text): > + import re > + > + p = r'''(((https?|mailto):)(//([^/?#"]*))?([^?#"]*)(\?([^#"]*))?(#([^"]*))?)''' > + r = r'''<text:a xlink:type="simple" xlink:href="\1">\1</text:a>''' > + > + text = re.sub(p, r, text) > + > + return text > + > def _write_manifest(self): > """ > create the manifest.xml file > @@ -1592,4 +1744,4 @@ > sigcount += 1 > txt += char > return [txt, sigcount] > - > \ Pas de fin de ligne à la fin du fichier. > + -- Adam (ad...@cs...) |
From: Adam S. <ad...@pp...> - 2011-02-21 12:08:13
|
Also, since I didn't mention it, the addition of the .pyo files was purely a mistake. I know I didn't need to package them. Just tar'ed the directory without thinking. On Sat, 2011-02-19 at 11:58 -0500, Adam Stein wrote: > You are answering your own questions before I can! > > I didn't want to go into the core and make a lot of changes that might > break other things. Since Python is OO, I can derive classes from those > that exist, only changing the parts I needed to. For the one thing I > couldn't do that without copying over a whole part of the > infrastructure, I put in a bug request along with a patch (bug #4593). > > Having my own enhanced set, I can show what my changes would do so that > other developers can take a look. Maybe there are better ways to do > what I changed. I already found out that in the next version, there is > a much more enhanced SubstKeywords that can pretty much do the little > bit that I added plus lots more. Also, it sounds like the way of > getting information will be updated to use the privacy flag so that > reports wouldn't have to do it themselves. In the meantime, my enhanced > changes look at the privacy flag. > > I'm hoping that the core changes are of enough interest to make it into > the core, even if not exactly how I did, but get that functionality in > there somehow. If not, people and still use my plugin with my enhanced > classes. > > When I make a report, I spend a fair bit of time tweaking it to get > exactly what I want. I thought it would be more beneficial for the > computer to do the work for me (cropped images, image captions, > respecting the privacy flag, etc.) > > Adam > > On Sat, 2011-02-19 at 11:38 +0100, Jérôme wrote: > > Oh, you just made new modules for your own use (FilteredReports)! > > Yes, it breaks current plugins ecosystem, but this might also work. > > > > I only looked at ODF enhancement against Gramps 3.2.x (patch added). > > > > > > Jérôme > > > > > > Jérôme a écrit : > > > Hi, > > > > > > > > > I do not understand all these "Enhanced" modules! > > > Is it not possible to enhance existing code? > > > > > > 'Trunk' developpement allows new features. If something is missing or > > > could be useful later, then I can try to make patchs against last revisions? > > > > > > I will try to patch[1] trunk and to use your new FilteredReports and > > > enhanced modules on trunk. > > > > > > Note, you do not really need to add *.pyc or *.pyo modules on your > > > archives, *.py files should be enough for running new FilteredReports > > > and related modules. > > > > > > > > > [1] http://www.gramps-project.org/bugs/view.php?id=4593 > > > > > > > > > Thank you for the plugin > > > > > > Jérôme > > > > > > > > > > > > Adam Stein a écrit : > > >> Just created a wiki page and added my report plugin (for Gramps v3.2) > > >> for FilteredReports. This first version is more of a proof-of-concept > > >> than a fully featured plugin (even though it does work given the > > >> limitations specified on the page). > > >> > > >> It's derived from the Detailed Descendant Report, but uses filters > > >> instead of a specific individual to start from. > > >> > > >> You can find the complete description at: > > >> > > >> http://www.gramps-project.org/wiki/index.php?title=Filtered_Reports > > >> > > > > > > > > > ------------------------------------------------------------------------------ > > > The ultimate all-in-one performance toolkit: Intel(R) Parallel Studio XE: > > > Pinpoint memory and threading errors before they happen. > > > Find and fix more than 250 security defects in the development cycle. > > > Locate bottlenecks in serial and parallel code that limit performance. > > > http://p.sf.net/sfu/intel-dev2devfeb > > > _______________________________________________ > > > Gramps-devel mailing list > > > Gra...@li... > > > https://lists.sourceforge.net/lists/listinfo/gramps-devel > > differences between files attachment (ODFDoc_Enhanced.patch) > > --- ODFDoc.py 2010-11-19 14:38:42.000000000 +0100 > > +++ ODFDoc_Enhanced.py 2011-02-07 13:53:44.000000000 +0100 > > @@ -1,17 +1,11 @@ > > -# > > -# Gramps - a GTK+/GNOME based genealogy program > > -# > > -# Copyright (C) 2000-2006 Donald N. Allingham > > -# Copyright (C) 2005-2009 Serge Noiraud > > -# Copyright (C) 2007-2009 Brian G. Matherly > > -# Copyright (C) 2010 Peter Landgren > > +# Copyright (C) 2011 Adam Stein <ad...@cs...> > > # > > # 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, > > +# 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. > > @@ -19,12 +13,9 @@ > > # 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: ODFDoc.py 16209 2010-11-18 20:18:06Z bmcage $ > > > > """ > > -ODFDoc : used to generate Open Office Document > > +ODFDoc_Enhanced : used to generate document in ODF format (used by OpenOffice.org, LibreOffice, etc.) > > """ > > > > #------------------------------------------------------------------------- > > @@ -86,7 +77,7 @@ > > from libodfbackend import OdfBackend > > import const > > from ReportBase import ReportUtils > > -import ImgManip > > +from ImgManip_Enhanced import ImgManip_Enhanced > > import Errors > > > > #------------------------------------------------------------------------- > > @@ -118,7 +109,7 @@ > > # ODFDoc > > # > > #------------------------------------------------------------------------- > > -class ODFDoc(BaseDoc, TextDoc, DrawDoc): > > +class ODFDoc_Enhanced(BaseDoc, TextDoc, DrawDoc): > > """ > > The ODF document class > > """ > > @@ -148,7 +139,8 @@ > > self.new_cell = 0 > > self.page = 0 > > 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): > > """ > > @@ -183,7 +175,6 @@ > > else: > > self.lang = "en-US" > > > > - self.StyleList = [] # styles to create depending on styled notes. > > self.cntnt1.write('<?xml version="1.0" encoding="UTF-8"?>\n') > > self.cntnt1.write('<office:document-content ') > > self.cntnt1.write('xmlns:office="urn:oasis:names:tc:opendocument') > > @@ -583,12 +574,18 @@ > > The content.xml file is closed. > > """ > > 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_styles() > > + > > + self.StyleList_photos = self.uniq(self.StyleList_photos) > > + self.add_styled_photo_styles() > > + > > self.cntntx.write(self.cntnt1.getvalue()) > > self.cntntx.write(self.cntnt2.getvalue()) > > self.cntntx.write(self.cntnt.getvalue()) > > + > > self.cntnt1.close() > > self.cntnt2.close() > > self.cntnt.close() > > @@ -614,7 +611,7 @@ > > Add the new fonts for Styled notes in the font-face-decls section. > > """ > > # Need to add new font for styled notes here. > > - for style in self.StyleList: > > + for style in self.StyleList_notes: > > if ( style[1] == "FontFace" ): > > self.cntnt1.write('<style:font-face style:name="%s"' % > > style[2] ) > > @@ -627,7 +624,7 @@ > > Add the new styles for Styled notes in the automatic-styles section. > > """ > > # Need to add new style for styled notes here. > > - for style in self.StyleList: > > + for style in self.StyleList_notes: > > if ( style[1] == "FontSize" ): > > self.cntnt2.write('<style:style style:name="FontSize__%s__"' % > > style[2] ) > > @@ -662,25 +659,95 @@ > > self.cntnt2.write(' style:font-pitch="variable"/>') > > self.cntnt2.write('</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. > > + """ > > + > > + for style in self.StyleList_photos: > > + if style[0] == "Left": > > + self.cntnt2.write('<style:style style:name="Left_%s" ' % str(style[1])) > > + self.cntnt2.write('style:family="graphic"') > > + self.cntnt2.write(' style:parent-style-name="photo">') > > + self.cntnt2.write('<style:graphic-properties ') > > + self.cntnt2.write('style:run-through="foreground"') > > + self.cntnt2.write(' style:wrap="dynamic"') > > + self.cntnt2.write(' style:number-wrapped-paragraphs="no-limit"') > > + self.cntnt2.write(' style:wrap-contour="false"') > > + self.cntnt2.write(' style:vertical-pos="from-top"') > > + self.cntnt2.write(' style:vertical-rel="paragraph-content"') > > + self.cntnt2.write(' style:horizontal-pos="left"') > > + self.cntnt2.write(' style:horizontal-rel="paragraph-content"') > > + self.cntnt2.write(' style:mirror="none" fo:clip="rect(%fin %fin %fin %fin)"' % style[1]) > > + self.cntnt2.write(' draw:luminance="0%" draw:contrast="0" draw:red="0%"') > > + self.cntnt2.write(' draw:green="0%" draw:blue="0%" draw:gamma="1"') > > + self.cntnt2.write(' draw:color-inversion="false"') > > + self.cntnt2.write(' draw:transparency="-100%"') > > + self.cntnt2.write(' draw:color-mode="standard"/>') > > + self.cntnt2.write('</style:style>\n') > > + elif style[0] == "Right": > > + self.cntnt2.write('<style:style style:name="Right_%s" ' % str(style[1])) > > + self.cntnt2.write('style:family="graphic"') > > + self.cntnt2.write(' style:parent-style-name="photo">') > > + self.cntnt2.write('<style:graphic-properties ') > > + self.cntnt2.write('style:run-through="foreground"') > > + self.cntnt2.write(' style:wrap="dynamic"') > > + self.cntnt2.write(' style:number-wrapped-paragraphs="no-limit"') > > + self.cntnt2.write(' style:wrap-contour="false" ') > > + self.cntnt2.write(' style:vertical-pos="from-top"') > > + self.cntnt2.write(' style:vertical-rel="paragraph-content"') > > + self.cntnt2.write(' style:horizontal-pos="right"') > > + self.cntnt2.write(' style:horizontal-rel="paragraph-content"') > > + self.cntnt2.write(' style:mirror="none" fo:clip="rect(%fin %fin %fin %fin)"' % style[1]) > > + self.cntnt2.write(' draw:luminance="0%" draw:contrast="0" draw:red="0%"') > > + self.cntnt2.write(' draw:green="0%" draw:blue="0%" draw:gamma="1"') > > + self.cntnt2.write(' draw:color-inversion="false"') > > + self.cntnt2.write(' draw:transparency="-100%"') > > + self.cntnt2.write(' draw:color-mode="standard"/>') > > + self.cntnt2.write('</style:style>\n') > > + elif style[0] == "Single": > > + self.cntnt2.write('<style:style style:name="Single_%s" ' % str(style[1])) > > + self.cntnt2.write('style:family="graphic"') > > + self.cntnt2.write(' style:parent-style-name="Graphics"> ') > > + self.cntnt2.write('<style:graphic-properties ') > > + self.cntnt2.write('style:vertical-pos="from-top"') > > + self.cntnt2.write(' style:mirror="none" fo:clip="rect(%fin %fin %fin %fin)"' % style[1]) > > + self.cntnt2.write(' draw:luminance="0%" draw:contrast="0" draw:red="0%"') > > + self.cntnt2.write(' draw:green="0%" draw:blue="0%" draw:gamma="1"') > > + self.cntnt2.write(' draw:color-inversion="false"') > > + self.cntnt2.write(' draw:transparency="-100%"') > > + self.cntnt2.write(' draw:color-mode="standard"/> ') > > + self.cntnt2.write('</style:style>\n') > > + else: > > + self.cntnt2.write('<style:style style:name="Row_%s" ' % str(style[1])) > > + self.cntnt2.write('style:family="graphic"') > > + self.cntnt2.write(' style:parent-style-name="Graphics">') > > + self.cntnt2.write('<style:graphic-properties ') > > + self.cntnt2.write('style:vertical-pos="from-top"') > > + self.cntnt2.write(' style:vertical-rel="paragraph"') > > + self.cntnt2.write(' style:horizontal-pos="from-left"') > > + self.cntnt2.write(' style:horizontal-rel="paragraph"') > > + self.cntnt2.write(' style:mirror="none" fo:clip="rect(%fin %fin %fin %fin)"' % style[1]) > > + self.cntnt2.write(' draw:luminance="0%" draw:contrast="0" draw:red="0%"') > > + self.cntnt2.write(' draw:green="0%" draw:blue="0%" draw:gamma="1"') > > + self.cntnt2.write(' draw:color-inversion="false"') > > + self.cntnt2.write(' draw:transparency="-100%"') > > + self.cntnt2.write(' draw:color-mode="standard"/>') > > + self.cntnt2.write('</style:style>\n') > > + > > + def add_media_object(self, style_name, file_name, pos, x_cm, y_cm, alt='', crop=None): > > """ > > Add multi-media documents : photos > > """ > > > > + img = ImgManip_Enhanced(file_name) > > + > > # try to open the image. If the open fails, it probably wasn't > > # a valid image (could be a PDF, or a non-image) > > - (x, y) = ImgManip.image_size(file_name) > > + (x, y) = img.image_size() > > if (x, y) == (0, 0): > > 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) > > odf_name = md5(file_name).hexdigest() + extension > > > > @@ -693,14 +760,47 @@ > > > > if self.new_cell: > > self.cntnt.write('<text:p>') > > + > > if pos == "left": > > - self.cntnt.write('<draw:frame draw:style-name="Left" ') > > + frame_pos = "Left" > > elif pos == "right": > > - self.cntnt.write('<draw:frame draw:style-name="Right" ') > > + frame_pos = "Right" > > elif pos == "single": > > - self.cntnt.write('<draw:frame draw:style-name="Single" ') > > + frame_pos = "Single" > > else: > > - self.cntnt.write('<draw:frame draw:style-name="Row" ') > > + frame_pos = "Row" > > + > > + if crop: > > + dpi = img.image_dpi() > > + > > + if dpi: > > + (act_width, act_height) = self._image_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( > > + [frame_pos, crop] > > + ) > > + > > + frame_pos += "_" + str(crop) > > + else: > > + (act_width, act_height) = self._image_size(x_cm, y_cm, x, y) > > + else: > > + (act_width, act_height) = self._image_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"> ' % (frame_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('<draw:frame draw:style-name="%s" ' % frame_pos) > > > > self.cntnt.write('draw:name="%s" ' % tag) > > self.cntnt.write('text:anchor-type="paragraph" ') > > @@ -712,6 +812,13 @@ > > self.cntnt.write('" xlink:type="simple" xlink:show="embed" ') > > self.cntnt.write('xlink:actuate="onLoad"/>\n') > > self.cntnt.write('</draw:frame>\n') > > + > > + if len(alt): > > + self.cntnt.write(alt) > > + self.cntnt.write('</text:p>') > > + self.cntnt.write('</draw:text-box>') > > + self.cntnt.write('</draw:frame>') > > + > > if self.new_cell: > > self.cntnt.write('</text:p>\n') > > > > @@ -802,6 +909,22 @@ > > zipinfo.external_attr = 0644 << 16L > > zfile.writestr(zipinfo, data) > > > > + def _image_size(self, x_cm, y_cm, x, y): > > + """ > > + Calculate what the actual width & height of the image should be > > + """ > > + > > + 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) > > + > > def _write_zip(self): > > """ > > Create the odt file. This is a zip file > > @@ -1192,7 +1315,7 @@ > > self.cntnt.write('</text:h>\n') > > 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 > > """ > > @@ -1201,6 +1324,10 @@ > > # Replace multiple spaces: have to go from the largest number down > > for n in range(text.count(' '), 1, -1): > > text = text.replace(' '*n, ' <text:s text:c="%d"/>' % (n-1) ) > > + > > + if links == True: > > + text = self._make_links(text) > > + > > self.start_paragraph(style_name) > > # self.cntnt.write('<text:span text:style-name="GRAMPS-preformat">') > > self.cntnt.write('<text:span text:style-name="Standard">') > > @@ -1209,7 +1336,7 @@ > > self.end_paragraph() > > > > 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. > > styledtext : assumed a StyledText object to write > > @@ -1221,7 +1348,15 @@ > > a link is clickable. ODFDoc prints the html without handling it > > """ > > text = str(styledtext) > > + > > s_tags = styledtext.get_tags() > > + text = text.replace('&', '\1') # must be the first > > + text = text.replace('<', '\2') > > + text = text.replace('>', '\3') > > + > > + if links == True: > > + text = self._make_links(text) > > + > > markuptext = self._backend.add_markup_from_styled(text, s_tags, '\n') > > # we need to know if we have new styles to add. > > # if markuptext contains : FontColor, FontFace, FontSize ... > > @@ -1234,12 +1369,15 @@ > > m = NewStyle.search(markuptext, start) > > if not m: > > break > > - self.StyleList.append([m.group(1)+m.group(2), > > - m.group(1), > > - m.group(2)]) > > + self.StyleList_notes.append([m.group(1)+m.group(2), > > + m.group(1), > > + m.group(2)]) > > start = m.end() > > linenb = 1 > > self.start_paragraph(style_name) > > + markuptext = markuptext.replace('\1', '&') # must be the first > > + markuptext = markuptext.replace('\2', '<') > > + markuptext = markuptext.replace('\3', '>') > > for line in markuptext.split('\n'): > > [line, sigcount] = process_spaces(line, format) > > if sigcount == 0: > > @@ -1253,12 +1391,16 @@ > > linenb += 1 > > 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 > > entities. The _esc_map dictionary allows us to add our own > > mappings. > > """ > > + > > + if links == True: > > + text = self._make_links(text) > > + > > if mark: > > key = escape(mark.key, _esc_map) > > key = key.replace('"', '"') > > @@ -1271,6 +1413,16 @@ > > self.cntnt.write('text:outline-level="%d" />' % mark.level) > > self.cntnt.write(escape(text, _esc_map)) > > > > + def _make_links(self, text): > > + import re > > + > > + p = r'''(((https?|mailto):)(//([^/?#"]*))?([^?#"]*)(\?([^#"]*))?(#([^"]*))?)''' > > + r = r'''<text:a xlink:type="simple" xlink:href="\1">\1</text:a>''' > > + > > + text = re.sub(p, r, text) > > + > > + return text > > + > > def _write_manifest(self): > > """ > > create the manifest.xml file > > @@ -1592,4 +1744,4 @@ > > sigcount += 1 > > txt += char > > return [txt, sigcount] > > - > > \ Pas de fin de ligne à la fin du fichier. > > + > -- Adam Stein @ Xerox Corporation Email: ad...@pp... Disclaimer: Any/All views expressed here have been proven to be my own. [http://www.csh.rit.edu/~adam/] |