pax_global_header 0000666 0000000 0000000 00000000064 11646530735 0014524 g ustar 00root root 0000000 0000000 52 comment=403959e6056b23946bc753216a93f363a36233e7 trac-odtexport-0.6.0+svn10787/ 0000775 0000000 0000000 00000000000 11646530735 0015717 5 ustar 00root root 0000000 0000000 trac-odtexport-0.6.0+svn10787/README 0000664 0000000 0000000 00000000102 11646530735 0016570 0 ustar 00root root 0000000 0000000 For information go to: http://trac-hacks.org/wiki/OdtExportPlugin trac-odtexport-0.6.0+svn10787/TODO 0000664 0000000 0000000 00000000340 11646530735 0016404 0 ustar 00root root 0000000 0000000 - merge new version of xhtml2odt - resize image if it's too large - comments !! - check page inclusion - add WikiMacros for user-defined fields in the ODT template - syntax coloring with pygments (that may be impossible...) trac-odtexport-0.6.0+svn10787/odtexport/ 0000775 0000000 0000000 00000000000 11646530735 0017747 5 ustar 00root root 0000000 0000000 trac-odtexport-0.6.0+svn10787/odtexport/OdtTemplate.py 0000664 0000000 0000000 00000001013 11646530735 0022536 0 ustar 00root root 0000000 0000000 """ This is a pseudo-macro which does not output anything, but is used by the ODT renderer to choose which file to use as the ODT template. """ from trac.wiki.macros import WikiMacroBase class OdtTemplateMacro(WikiMacroBase): # pylint: disable-msg=W0223 """ Choose which file to use as the ODT template, see the [http://trac-hacks.org/wiki/OdtExportPlugin#Choosingatemplate wiki page] for details. """ def expand_macro(self, formatter, name, args): # pylint: disable-msg=W0613 return '' trac-odtexport-0.6.0+svn10787/odtexport/__init__.py 0000664 0000000 0000000 00000000000 11646530735 0022046 0 ustar 00root root 0000000 0000000 trac-odtexport-0.6.0+svn10787/odtexport/odtexport.py 0000664 0000000 0000000 00000047316 11646530735 0022364 0 ustar 00root root 0000000 0000000 """ This plugin can convert a wiki page to the OpenDocument Text (ODT) format, standardized as ISO/IEC 26300:2006, and the native format of office suites such as OpenOffice.org, KOffice, and others. It uses a template ODT file which will be filled with the converted content of the exported Wiki page. """ from StringIO import StringIO import tempfile import shutil import re import os import zipfile import urllib2 import urlparse import datetime # pylint: disable-msg=E0611 from pkg_resources import resource_filename from lxml import etree import tidy from PIL import Image #from trac.core import * from trac.core import Component, implements from trac.mimeview.api import IContentConverter, Context from trac.wiki import Formatter from trac.util.html import Markup from trac.util.text import unicode_quote from trac.web.chrome import Chrome from trac.config import Option, IntOption, BoolOption, ListOption from trac.attachment import Attachment #pylint: disable-msg=C0301,C0111 __all__ = ("ODTExportPlugin") INCH_TO_CM = 2.54 class ODTExportError(Exception): pass class ODTExportPlugin(Component): """Convert Wiki pages to ODT.""" implements(IContentConverter) img_width = Option('odtexport', 'img_default_width', '8cm') img_height = Option('odtexport', 'img_default_height', '6cm') img_dpi = IntOption('odtexport', 'dpi', '96') get_remote_images = BoolOption('odtexport', 'get_remote_images', True) replace_keyword = Option('odtexport', 'replace_keyword', 'TRAC-ODT-INSERT') wikiversion_keyword = Option('odtexport', 'wikiversion_keyword', 'TRAC-ODT-WIKIVERSION') wikiname_keyword = Option('odtexport', 'wikiname_keyword', 'TRAC-ODT-WIKINAME') timestamp_keyword = Option('odtexport', 'timestamp_keyword', 'TRAC-ODT-TIMESTAMP') cut_start_keyword = Option('odtexport', 'cut_start_keyword', 'TRAC-ODT-CUT-START') cut_stop_keyword = Option('odtexport', 'cut_stop_keyword', 'TRAC-ODT-CUT-STOP') remove_macros = ListOption('odtexport', 'remove_macros', "PageOutline, TracGuideToc, TOC, TranslatedPages") # IContentConverter methods def get_supported_conversions(self): yield ('odt', 'OpenDocument', 'odt', 'text/x-trac-wiki', 'application/vnd.oasis.opendocument.text', 5) def convert_content(self, req, input_type, content, output_type): # pylint: disable-msg=W0613 self.page_name = req.args.get('page', 'WikiStart') #wikipage = WikiPage(self.env, self.page_name) template = self.get_template_name(content) html = self.wiki_to_html(content, req) #return (html, "text/plain") odtfile = ODTFile(self.page_name, req.args.get('version', 'latest'), template, self.env, # pylint: disable-msg=E1101 options={ "img_width": self.img_width, "img_height": self.img_height, "img_dpi": self.img_dpi, "get_remote_images": self.get_remote_images, "replace_keyword": self.replace_keyword, "wikiversion_keyword": self.wikiversion_keyword, "wikiname_keyword": self.wikiname_keyword, "timestamp_keyword": self.timestamp_keyword, "cut_start_keyword": self.cut_start_keyword, "cut_stop_keyword": self.cut_stop_keyword, }) odtfile.open() #return (odtfile.import_xhtml(html), "text/plain") odtfile.import_xhtml(html) newdoc = odtfile.save() return (newdoc, "application/vnd.oasis.opendocument.text") def get_template_name(self, wikitext): template_macro = re.search('\[\[OdtTemplate\(([^)]+)\)\]\]', wikitext) if template_macro: tpl = template_macro.group(1) if tpl.endswith(".odt"): return tpl else: return "%s.odt" % tpl return "wikipage.odt" def wiki_to_html(self, wikitext, req): self.env.log.debug('start function wiki_to_html') # pylint: disable-msg=E1101 # Remove some macros (TOC is better handled in ODT itself) for macro in self.remove_macros: wikitext = re.sub('\[\[%s(\([^)]*\))?\]\]' % macro, "", wikitext) # Now convert wiki to HTML out = StringIO() context = Context.from_request(req, absurls=True) Formatter(self.env, # pylint: disable-msg=E1101 context('wiki', self.page_name)).format(wikitext, out) html = Markup(out.getvalue()) html = html.encode("utf-8", 'replace') # Clean up the HTML html = re.sub('.', '', html) # Remove external link icon tidy_options = dict(output_xhtml=1, add_xml_decl=1, indent=1, tidy_mark=0, input_encoding='utf8', output_encoding='utf8', doctype='auto', wrap=0, char_encoding='utf8') html = tidy.parseString(html, **tidy_options) # Replace nbsp with entity: # http://www.mail-archive.com/analog-help@lists.meer.net/msg03670.html html = str(html).replace(" ", " ") # Tidy creates newlines after
(by indenting)
html = re.sub(']*)>\n', '', html)
return html
# def get_title(self, wikitext, req):
# '''Get page title from first header in outline'''
# out = StringIO()
# context = Context(Resource('wiki', self.page_name), req.abs_href, req.perm)
# context.req = req
# outline = OutlineFormatter(self.env, context)
# outline.format(wikitext, out, 1, 1)
# for depth, anchor, text in outline.outline:
# if depth == 1:
# return text
# return self.page_name
class ODTFile(object):
"""
The class doing the actual conversion from XHTML to ODT
The ODT template name is given as an argument to the constructor, and will
be looked for in the directories of `template_dirs`. Those
directories are, in order:
1. the attachments of the current page
2. the ``templates`` subdirectory of the Trac instance
3. the ``templates`` subdirectory of the odt plugin installation
:ivar page_name: the wiki page name
:type page_name: str
:ivar template: the filename of the ODT template (without the path)
:type template: str
:ivar template_dirs: the list of directories to search for the ODT
template. The first match found is used.
"""
def __init__(self, page_name, page_version, template, env, options):
self.page_name = page_name
self.page_version = page_version
self.template = template
self.env = env
self.options = options
self.template_dirs = [
self.env.get_templates_dir(),
resource_filename(__name__, 'templates'),
]
self.template_dirs.insert(0, os.path.join(self.env.path,
'attachments', "wiki", unicode_quote(self.page_name)))
self.xml = {
"content": "",
"styles": "",
}
self.tmpdir = tempfile.mkdtemp(prefix="trac-odtexport")
self.styles = {}
self.autostyles = {}
self.style_name_re = re.compile('style:name="([^"]+)"')
self.fonts = {}
self.zfile = None
def get_template_path(self):
for tpldir in self.template_dirs:
cur_filename = os.path.join(tpldir, self.template)
if os.path.exists(cur_filename):
return os.path.normpath(cur_filename)
raise ODTExportError("Can't find ODT template %s" % self.template)
def open(self):
template = self.get_template_path()
self.zfile = zipfile.ZipFile(template, "r")
for name in self.zfile.namelist():
fname = os.path.join(self.tmpdir, name)
if not os.path.exists(os.path.dirname(fname)):
os.makedirs(os.path.dirname(fname))
if name[-1] == "/":
if not os.path.exists(fname):
os.mkdir(fname)
continue
fname_h = open(fname, "w")
fname_h.write(self.zfile.read(name))
fname_h.close()
for xmlfile in self.xml:
self.xml[xmlfile] = self.zfile.read("%s.xml" % xmlfile)
def import_xhtml(self, xhtml):
odt = self.xhtml_to_odt(xhtml)
#self.env.log.debug(odt) # debug
self.insert_content(odt)
self.add_styles()
def xhtml_to_odt(self, xhtml):
xsl_dir = resource_filename(__name__, 'xsl')
xslt_doc = etree.parse(os.path.join(xsl_dir, "xhtml2odt.xsl"))
transform = etree.XSLT(xslt_doc)
xhtml = self.handle_images(xhtml)
#self.env.log.debug(xhtml) # debug
xhtml = etree.fromstring(xhtml) # must be valid xml
if hasattr(etree.XSLT, "strparam"):
url = etree.XSLT.strparam(
self.env.abs_href("/wiki/%s" % self.page_name))
img_width = etree.XSLT.strparam(self.options["img_width"])
img_height = etree.XSLT.strparam(self.options["img_height"])
else: # lxml < 2.2
url = "'%s'" % self.env.abs_href("/wiki/%s" % self.page_name)
img_width = "'%s'" % self.options["img_width"]
img_height = "'%s'" % self.options["img_height"]
odt = transform(xhtml, url=url,
heading_minus_level="0",
img_default_width=img_width,
img_default_height=img_height,
)
return str(odt).replace('','')
def handle_images(self, html):
base_url = self.env.abs_href("/")
# Make relative links where it makes sense
#html = re.sub('
]*)src="%s' % base_url, '
]*)src="(/[^"]+)"',
'
]*)href="(/[^"]+)"',
']*src="(%s/raw-attachment/([^/]+)(?:/([^"]*))?)"'
% base_url, self.handle_attached_img, html)
# Handle chrome images
html = re.sub('
]*src="(%s/chrome/([^"]+))"'
% base_url, self.handle_chrome_img, html)
# Handle remote images
if self.options["get_remote_images"] and \
str(self.options["get_remote_images"]).lower() != "false":
html = re.sub('
]*src="(https?://[^"]+)"',
self.handle_remote_img, html)
return html
def handle_attached_img(self, img_mo):
self.env.log.debug('handling local image: %s' % img_mo.group())
src, realm, path = img_mo.groups()
path_segments = path.split("/")
parent_id = '/'.join(path_segments[:-1])
filename = len(path_segments) > 1 and path_segments[-1]
attachment = Attachment(self.env, realm, parent_id, filename)
filename = attachment.path
if not os.path.exists(filename): # fallback
return self.handle_remote_img(img_mo)
return self.handle_img(img_mo.group(), src, filename)
def handle_chrome_img(self, img_mo):
"""Copied from Chrome.process_request()"""
self.env.log.debug('handling chrome image: %s' % img_mo.group())
src, filename = img_mo.groups()
# Normalize the filename. Some people find it funny to create URLs such as
# /chrome/site/../common/trac_logo_mini.png. Yes, that's you WikiFormatting.
normed_filename = os.path.normpath(filename)
normed_string = img_mo.group().replace(filename, normed_filename)
base_url = self.env.abs_href("/")
img_mo = re.match('
]*src="(%s/chrome/([^/]+)/+([^"]+))"'
% base_url, normed_string)
src, prefix, filename = img_mo.groups()
chrome = Chrome(self.env)
for provider in chrome.template_providers:
for tpdir in [os.path.normpath(htdir[1]) for htdir
in provider.get_htdocs_dirs() if htdir[0] == prefix]:
path = os.path.normpath(os.path.join(tpdir, filename))
assert os.path.commonprefix([tpdir, path]) == tpdir
if os.path.isfile(path):
return self.handle_img(img_mo.group(), src, path)
# fallback
return self.handle_remote_img(img_mo)
def handle_remote_img(self, img_mo):
"""
Download the image to a temporary location and call
handle_img(html, img_mo, temp_location)
"""
self.env.log.debug('handling remote image: %s' % img_mo.group())
src = img_mo.group(1)
self.env.log.debug('Downloading image: %s' % src)
# TODO: proxy support
try:
remoteimg = urllib2.urlopen(src)
tmpimg_fd, tmpfile = tempfile.mkstemp()
tmpimg = os.fdopen(tmpimg_fd, 'w')
tmpimg.write(remoteimg.read())
tmpimg.close()
remoteimg.close()
ret = self.handle_img(img_mo.group(), src, tmpfile)
os.remove(tmpfile)
return ret
except (urllib2.HTTPError, urllib2.URLError):
return img_mo.group()
def handle_img(self, full_tag, src, filename):
self.env.log.debug('Importing image: %s' % filename)
if not os.path.exists(filename):
raise ODTExportError('Image "%s" is not readable or does not exist'
% filename)
# TODO: generate a filename (with tempfile.mkstemp) to avoid weird
# filenames. Maybe use img.format for the extension
if not os.path.exists(os.path.join(self.tmpdir, "Pictures")):
os.mkdir(os.path.join(self.tmpdir, "Pictures"))
shutil.copy(filename, os.path.join(self.tmpdir, "Pictures",
os.path.basename(filename)))
newsrc = "Pictures/%s" % os.path.basename(filename)
try:
img = Image.open(filename)
except IOError:
self.env.log.warn('Failed to identify image: %s' % filename)
else:
width, height = img.size
self.env.log.debug('Detected size: %spx x %spx' % (width, height))
width_mo = re.search('width="([0-9]+)(?:px)?"', full_tag)
if width_mo:
newwidth = (float(width_mo.group(1)) /
float(self.options["img_dpi"]) * INCH_TO_CM)
height = height * newwidth / width
width = newwidth
self.env.log.debug('Forced width: %spx. Size will be: %scm x %scm'
% (width_mo.group(1), width, height))
full_tag = full_tag.replace(width_mo.group(), "")
else:
width = width / float(self.options["img_dpi"]) * INCH_TO_CM
height = height / float(self.options["img_dpi"]) * INCH_TO_CM
newsrc += '" width="%scm" height="%scm' % (width, height)
return full_tag.replace(src, newsrc)
def insert_content(self, content):
for kw in ["replace", "wikiversion", "wikiname", "timestamp",
"cut_start", "cut_stop"]:
self.options[kw+"_keyword"] = str(self.options[kw+"_keyword"])
for k, v in {'wikiversion': str(self.page_version),
'wikiname': str(self.page_name),
'timestamp': str(datetime.datetime.now())}.iteritems():
for xmltype in self.xml.keys():
#self.env.log.debug("Key %s, Value %s, xml %s\n"%(k,v,xmltype))
if self.options[k+"_keyword"] and self.xml[xmltype].count(
self.options[k+"_keyword"]) > 0:
self.xml[xmltype] = re.sub(
re.escape(self.options[k+"_keyword"]),
v, self.xml[xmltype])
if self.options["replace_keyword"] and self.xml["content"].count(
self.options["replace_keyword"]) > 0:
self.xml["content"] = re.sub(
"]*>" +
re.escape(self.options["replace_keyword"])
+" ", content, self.xml["content"])
else:
self.xml["content"] = self.xml["content"].replace(
'',
content + '')
# Cut unwanted text
if self.xml["content"].count(self.options["cut_start_keyword"]) > 0 \
and self.xml["content"].count(
self.options["cut_stop_keyword"]) > 0:
self.xml["content"] = re.sub(
re.escape(self.options["cut_start_keyword"])
+ ".*" +
re.escape(self.options["cut_stop_keyword"]),
"", self.xml["content"])
def add_styles(self):
"""
Add missing styles and fonts using the dedicated stylesheet from
xhtml2odt
"""
xsl_dir = resource_filename(__name__, 'xsl')
xslt_doc = etree.parse(os.path.join(xsl_dir, "styles.xsl"))
transform = etree.XSLT(xslt_doc)
contentxml = etree.fromstring(self.xml["content"])
stylesxml = etree.fromstring(self.xml["styles"])
self.xml["content"] = str(transform(contentxml))
self.xml["styles"] = str(transform(stylesxml))
def compile(self):
# autostyles
if self.autostyles:
autostyles = "\n".join(self.autostyles.values())
for xmlfile in ["content", "styles"]:
if self.xml[xmlfile].count(" ") > 0:
self.xml[xmlfile] = self.xml[xmlfile].replace(
" ",
"%s " %
autostyles)
else:
self.xml[xmlfile] = self.xml[xmlfile].replace(
"",
"%s" % autostyles)
# main styles
if self.styles:
styles = "\n".join(self.styles.values())
self.xml["styles"] = self.xml["styles"].replace(
"", "%s" % styles)
# fonts
if self.fonts:
fonts = "\n".join(self.fonts.values())
for xmlfile in ["content", "styles"]:
self.xml[xmlfile] = self.xml[xmlfile].replace(
"",
"%s" % fonts)
# Store the new content
for xmlfile in self.xml:
xmlf = open(os.path.join(self.tmpdir, "%s.xml" % xmlfile), "w")
xmlf.write(self.xml[xmlfile])
xmlf.close()
def save(self):
self.compile()
# Create the zip file
document = StringIO()
newzf = zipfile.ZipFile(document, "w", zipfile.ZIP_DEFLATED)
for root, dirs, files in os.walk(self.tmpdir):
for cur_file in files:
realpath = os.path.join(root, cur_file)
internalpath = os.path.join(root.replace(self.tmpdir, ""), cur_file)
newzf.write(realpath, internalpath)
newzf.close()
shutil.rmtree(self.tmpdir)
# Return the StringIO
return document.getvalue()
trac-odtexport-0.6.0+svn10787/odtexport/odtstyle.py 0000664 0000000 0000000 00000004345 11646530735 0022176 0 ustar 00root root 0000000 0000000 """
This module contains an ODT style library
"""
import os
import re
from glob import glob
__all__ = ("add_styles",)
# pylint: disable-msg=C0103
style_name_re = re.compile('style:name="([^"]+)"')
need_font_re = re.compile('font-name="([^"]+)"')
def _build_style_lib(template_dirs):
"""build a library of available styles"""
template_files = _build_templates_list(template_dirs)
style_lib = {}
for style_file in template_files.values():
style_tpl = open(style_file)
style_xml = style_tpl.read()
style_tpl.close()
style = _build_style(style_xml)
if not style:
continue
style_lib[style["name"]] = style
return style_lib
def _build_templates_list(template_dirs):
"""select the preferred template (used-defined or default)"""
template_files = {}
for template_dir in template_dirs:
for style_file in glob(os.path.join(template_dir, "*.txt")):
basename = os.path.basename(style_file)
if basename not in template_files:
template_files[basename] = style_file
return template_files
def _build_style(style_xml):
style_name_mo = style_name_re.search(style_xml)
if not style_name_mo:
return False
style = {
"name": style_name_mo.group(1),
"xml": style_xml,
}
is_mainstyle = (style_xml.count("style:display-name=") > 0)
style["mainstyle"] = is_mainstyle
need_font = need_font_re.search(style_xml)
if need_font:
style["need_font"] = need_font.group(1)
return style
def add_styles(template_dirs, content_xml, import_style_callback,
import_font_callback):
"""
Add the missing styles using callbacks
"""
style_lib = _build_style_lib(template_dirs)
for stylename in style_lib:
if content_xml.count('style-name="%s"' % stylename) == 0:
continue # style is not used
style_xml = style_lib[stylename]["xml"]
is_mainstyle = style_lib[stylename]["mainstyle"]
import_style_callback(style_xml, is_mainstyle)
if "need_font" in style_lib[stylename]:
font_name = style_lib[stylename]["need_font"]
font_xml = style_lib[font_name]["xml"]
import_font_callback(font_xml)
trac-odtexport-0.6.0+svn10787/odtexport/templates/ 0000775 0000000 0000000 00000000000 11646530735 0021745 5 ustar 00root root 0000000 0000000 trac-odtexport-0.6.0+svn10787/odtexport/templates/wikipage.odt 0000664 0000000 0000000 00000015632 11646530735 0024264 0 ustar 00root root 0000000 0000000 PK C;^2' ' mimetypeapplication/vnd.oasis.opendocument.textPK C; Configurations2/statusbar/PK C; ' Configurations2/accelerator/current.xml PK PK C; Configurations2/floater/PK C; Configurations2/popupmenu/PK C; Configurations2/progressbar/PK C; Configurations2/menubar/PK C; Configurations2/toolbar/PK C; Configurations2/images/Bitmaps/PK C; content.xmlV]O0}߯2io)ldM&4i^N .4łJs=>cv,U>Hb*VU[q_d]SDJ,Qˉ0̲.lJjK9ѥAlYe.}0͎%=8f5d=]*a1fE-뼁>QeT1M @ub"
L״yF0i0LAT}K- *h೪*#6k^^ˡY,}Ss9UH&yTG=?;wN;E
Q#L)l\#sƣ/nhM8|ӗ@rE]'H#L[!"G(# 3:ַ rl#B*WpmWi)2b3c"t@mo 糰,|;U~C6[B2ﱜ]ԟhwbE]mE.1Q Pnm<'
&H#
TUSr(N.DwTWΖYg?HK0X=S+%i_,ԏQ?\z
/icd?[c]5>N$
y3G&Z=apWؗ)=cY-km[R8o
v/mQore5/qi~ku#PK, y PK C;
styles.xmlZ[o6~߯0Tlo,']Nu(VC%b+IvΡD%GM!{s/E>cJs)A4_3&bp]~xE7$Q&zU}wUs2"ދė-FbrMjy\+^NfK)[(P\,.o{w}acϲ4[biAVxQOCJL$ޠ;Jp@P5X !< fMkܸNJ)IXudzoĹfj
=B3y[~`vȖ 8 S∥&M%Ax_h-f @TvuNTq|bf0
;8 l w L5Ӕ
}Іa
Ǫ9=a)f)q[Eˌǁm&UC0q4tٶ#_3C`1 #,#1yIſt#,OYLz:qKqzmJi,(CjZ6 5xdJ2Qp. 6l8 WlҀa+ҿ0<ݲ#TFe.aa1A_i,=˩VtGQg-BzDNs#(w4VS}l?f%~t<)E?8HPfL&iX,@Hz[DlZpK7tDe)QW-)[3Ry@^ya:?3ФKl~18yXA [0z,Q `9sH2'L^D]8*c%1rLnXy
(* F^N5luXD]IīQupоJa(g O`
πJK;e4?g` Gˆd}lӗ;za4+_M\vJTeJ'Qm'O1|70ar393~ X]}ڧ۶m)v|xL%`̻NQDŽǖ'S5l=n7bjnr(Y2Gxoh8f)&4'2YXLځx Ҏ GanD)8.t+|^EM+>x.)׳G~0
D|[)Q9k ʩ5c~2yC'I)57>Գ0Po4!@>ЩGťnCc(鼐44.beĞ*[mc2~NGp;Mݣ},`Zʎ'C2/Q [B6g6&H& Ts7mw>:9T
ދm.0hV:u/EtqG6nn'ZD<47!|]@}V]ڽg'_$]*c_hSTQx
[5}d1{%|O}89nPK
" PK C;c c meta.xml
2009-12-28T22:07:51 OpenOffice.org/3.1$Linux OpenOffice.org_project/310m19$Build-9420 2009-12-30T09:31:16 PT00H01M32S 4 PK C; Thumbnails/thumbnail.pngsb``p [8{8Ty{i#'ρ
|??tCÛw~29K&xrrVoʓԎ_y2cTpTp3
*L~. PK׃| PK C; settings.xmlY]s:}"w$i0 CJ/
fn߄odGdI'[~=+_]PxӚw"0qܩ\z_\t1Hc@kZNhPJ@",AmVYd#|͵Nb8]UWWWUv4@1fVŬ0VYvQNCqMk6y+~*$lvKWy'%1&^%&k֮ڃv )
|ܳh6yO
q1s&f6L905L0]ђPp!I2_YRDK};*pJK\iH%yRV;*p8zX.i+tXTTzJnTa!cfs&r%20mi,69^/+|rKeIWC!Hzpw<|+Esڽ:=4{J#a8`e(aA RDcdF?զOH/4|ǘzD]}\YG}07w&PB'JХ+\8g,:nDpBǕ0#xG6CpQXN71,8ןN (\a,y@&ئ|զE+LG(A;(7±
L,Ee!wrg0Q/gY
ҵRnۣ@m5ᭇ,QD?"1:hwmmI"'|OzT of1enWQFwo!Cl\x*bb@LP4&T{jNG^i/m$-Յ۩&SlaMv!^|ۨ3"HXip`&Y2W]ы|3nu%:p+٢?_
Y({ظ[BYE!1,ů/ȭT.>s8#,zlSe ˷3GXj:76Y
(4_kiͩtl"7N`