pax_global_header00006660000000000000000000000064137567540160014530gustar00rootroot0000000000000052 comment=0a0309e8633b5907543d74318c54786e68d4d53a sorl-thumbnail-12.7.0/000077500000000000000000000000001375675401600145575ustar00rootroot00000000000000sorl-thumbnail-12.7.0/.editorconfig000066400000000000000000000006521375675401600172370ustar00rootroot00000000000000; This file is for unifying the coding style for different editors and IDEs. ; More information at http://EditorConfig.org root = true [*] end_of_line = lf insert_final_newline = true charset = utf-8 [*.py] insert_final_newline = true trim_trailing_whitespace = true indent_style = space indent_size = 4 [*.rst] trim_trailing_whitespace = false [Makefile] indent_style = tab [*.yml] indent_style = space indent_size = 2 sorl-thumbnail-12.7.0/.github/000077500000000000000000000000001375675401600161175ustar00rootroot00000000000000sorl-thumbnail-12.7.0/.github/workflows/000077500000000000000000000000001375675401600201545ustar00rootroot00000000000000sorl-thumbnail-12.7.0/.github/workflows/release.yml000066400000000000000000000025231375675401600223210ustar00rootroot00000000000000name: Release on: push: branches: - master release: types: - published jobs: build: if: github.repository == 'jazzband/sorl-thumbnail' runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - name: Set up Python uses: actions/setup-python@v2 with: python-version: 3.8 - name: Get pip cache dir id: pip-cache run: | echo "::set-output name=dir::$(pip cache dir)" - name: Cache uses: actions/cache@v2 with: path: ${{ steps.pip-cache.outputs.dir }} key: release-${{ hashFiles('**/setup.py') }} restore-keys: | release- - name: Install dependencies run: | python -m pip install -U pip python -m pip install -U setuptools twine wheel - name: Build package run: | python setup.py --version python setup.py sdist --format=gztar bdist_wheel twine check dist/* - name: Upload packages to Jazzband if: github.event.action == 'published' uses: pypa/gh-action-pypi-publish@master with: user: jazzband password: ${{ secrets.JAZZBAND_RELEASE_KEY }} repository_url: https://jazzband.co/projects/sorl-thumbnail/upload sorl-thumbnail-12.7.0/.github/workflows/test.yml000066400000000000000000000026211375675401600216570ustar00rootroot00000000000000name: Test on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: matrix: python-version: [3.6, 3.7, 3.8] target: [pil, imagemagick, graphicsmagick, redis, wand, dbm, qa] steps: - uses: actions/checkout@v2 - name: Start Redis uses: supercharge/redis-github-action@1.1.0 - name: Install system dependencies run: sudo apt-get install libgraphicsmagick1-dev graphicsmagick libjpeg62 zlib1g-dev - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Get pip cache dir id: pip-cache run: | echo "::set-output name=dir::$(pip cache dir)" - name: Cache uses: actions/cache@v2 with: path: ${{ steps.pip-cache.outputs.dir }} key: test-${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.py') }} restore-keys: | test-${{ matrix.python-version }}-v1- - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install --upgrade tox tox-gh-actions - name: Tox tests shell: bash run: | tox env: TARGET: ${{ matrix.target }} - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 with: name: Python ${{ matrix.python-version }} sorl-thumbnail-12.7.0/.gitignore000066400000000000000000000012311375675401600165440ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # Distribution / packaging env/ bin/ build/ develop-eggs/ dist/ eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ *.egg .eggs .installed.cfg # Installer logs pip-log.txt pip-delete-this-directory.txt # Translations *.mo # Related to Desktops and OS .DS_Store .AppleDouble .LSOverride ._* .~ .bak *.swp *.swo *.swn # VCS and DVCS .svn # Common IDE's .idea .project .pydevproject .ropeproject # Sphinx documentation docs/_build/ # Virtualenv .python-version .env/ # dbm stuff tests/thumbnail_kvstore tests/thumbnail_kvstore.db tests/thumbnail_kvstore.lock # test related .coverage .tox htmlcov/ sorl-thumbnail-12.7.0/AUTHORS000066400000000000000000000001161375675401600156250ustar00rootroot00000000000000Take a look at https://github.com/jazzband/sorl-thumbnail/graphs/contributors sorl-thumbnail-12.7.0/CHANGES.rst000066400000000000000000000056171375675401600163720ustar00rootroot00000000000000======= Changes ======= 12.7.0 ====== * Drop support for Django 1.11 * Added support for Django 3.1 * Moved to GitHub Action for continuous integration. * Correction in convert_engine with unknown exif orientation * Using more resilient _get_exif_orientation logic in convert engine * Update wand_engine.py for ImageMagick 7 * Fix cannot write mode RGBA as JPEG when thumbnailing a GIF 12.6.3 ====== * Deprecate Python 2 compatibility shims in favor of Python 3 only codebase. #623 * Fix README on notes about ImageField cleaning up references on delete. #624 * Fix image ratios with EXIF orientation. #619 * Fix test coverage tracking. #622 and #617 12.6.2 ====== * Fix rST syntax errors from 12.6.0 and 12.6.1 that blocked release. #613 * Improve QA setup and add rST validation to Travis and tox test matrix. #613 12.6.1 ====== * Deprecate explicit support for Python 3.4 and 3.5 in order to simplify the test matrix #610 * Add requirement for ``setuptools_scm`` to automatically resolve version from git tags #610 * Removed property ``thumbnail.__version__`` #610 12.6.0 ====== * Add Cropbox feature in Wand/Convert Engine * Add testing for Django 2.2 * Remove "django.utils.six" to support Django 3.0+ * Remove Python 2 support 12.5.0 ====== * Make the template tag accept a falsey image * Update identify (of convert_engine) for faster multi-page PDF thumbnailing * Fix Redis KVStore timeout * Fix format conversion in Wand engine * Added setting THUMBNAIL_REMOVE_URL_ARGS * Add testing for Django 2.1 * Drop support for Django < 1.11 * Added ssl parameter to Redis object instantiation * Fix 2 ResourceWarning: unclosed file, in tests * Fix AdminImageWidget with Django 2.1 * Test in release version of Python 3.7 * Remove unused unittest imports in thumbnail_tests.compat * Add a __str__ method to ImageFile 12.4.1 ====== sorl-thumbnail was welcomed into the `Jazzband organization and project `__. Jazzband is open to all, and any member of Jazzband can contribute directly to sorl-thumbnail's GitHub repo. We hope this will encourage more open source programmers to contribute. Thank you @mariocesar for taking this step and for the years of effort in this project. 12.4.1 is the first release on PyPI since the migration to the Jazzband project, and includes two years' worth of changes. Thank you to all contributors. These are some of the highlights: * Target Django versions are now 1.8, 1.10, 1.11 and 2.0 * Target Python versions are now 2.7, 3.3, 3.4, 3.5 and 3.6 * Enable GIF support (#263) * Enable WebP support (#460) * New ``sorl_thumbnail`` templatetag library that mirrors traditional ``thumbnail`` * Fix bug RGBA mode not compatible with JPEG on PILLOW >=3.7 (#503) * Don't check EXIF orientation with GraphicsMagick * Bug fix for handling non-ASCII characters in filenames (#434) * Better error detection and handling in some cases (#492) * Improve automated testing * Improve documentation sorl-thumbnail-12.7.0/CONTRIBUTING.rst000066400000000000000000000005331375675401600172210ustar00rootroot00000000000000|Jazzband| This is a `Jazzband `__ project. By contributing you agree to abide by the `Contributor Code of Conduct `__ and follow the `guidelines `__. .. |Jazzband| image:: https://jazzband.co/static/img/jazzband.svg :target: https://jazzband.co/ sorl-thumbnail-12.7.0/LICENSE000066400000000000000000000027561375675401600155760ustar00rootroot00000000000000Copyright (c) 2010, Mikko Hellsing All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the sorl-thumbnail the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. sorl-thumbnail-12.7.0/MANIFEST.in000066400000000000000000000002161375675401600163140ustar00rootroot00000000000000include LICENSE README.rst CHANGES.rst AUTHORS recursive-include docs * recursive-include tests * recursive-exclude * *.pyc prune docs/_build sorl-thumbnail-12.7.0/README.rst000066400000000000000000000154101375675401600162470ustar00rootroot00000000000000|jazzband-badge| |pypi| |docs| |gh-actions| |codecov| Thumbnails for Django. Features at a glance ==================== - Support for Django 2.2, 3.0 and 3.1 following the `Django supported versions policy`_ - Python 3 support - Storage support - Pluggable Engine support for `Pillow`_, `ImageMagick`_, `PIL`_, `Wand`_, `pgmagick`_, and `vipsthumbnail`_ - Pluggable Key Value Store support (cached db, redis, and dynamodb by AWS) - Pluggable Backend support - Admin integration with possibility to delete - Dummy generation (placeholders) - Flexible, simple syntax, generates no html - ImageField for model that deletes thumbnails (only compatible with django 1.2.5 or less) - CSS style cropping options - Back smart cropping, and remove borders from the images when cropping - Margin calculation for vertical positioning - Alternative resolutions versions of a thumbnail Read more in `the documentation (latest version) `_ Developers ========== |jazzband| This is a `Jazzband `_ project. By contributing you agree to abide by the `Contributor Code of Conduct `_ and follow the `guidelines `_. Feel free to create a new Pull request if you want to propose a new feature. If you need development support or want to discuss with other developers join us in the channel #sorl-thumnbnail at freenode.net or Gitter. For releases updates and more in deep development discussion use our mailing list in Google Groups. - IRC Channel: irc://irc.freenode.net/#sorl-thumbnail - Mailing List: sorl-thumbnail@googlegroups.com https://groups.google.com/d/forum/sorl-thumbnail Tests ----- The tests should run with tox and pytest. Running `tox` will run all tests for all environments. However, it is possible to run a certain environment with `tox -e `, a list of all environments can be found with `tox -l`. These tests require the dependencies of the different engines defined in the documentation. It is possible to install these dependencies into a vagrant image with the Vagrantfile in the repo. User Support ============ If you need help using sorl-thumbnail browse http://stackoverflow.com/questions/tagged/sorl-thumbnail and posts your questions with the `sorl-thumbnail` tag. How to Use ========== Get the code ------------ Getting the code for the latest stable release use 'pip'. :: $ pip install sorl-thumbnail Install in your project ----------------------- Then register 'sorl.thumbnail', in the 'INSTALLED_APPS' section of your project's settings. :: INSTALLED_APPS = [ 'django.contrib.auth', 'django.contrib.admin', 'django.contrib.sites', 'django.contrib.comments', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.contenttypes', 'sorl.thumbnail', ] Templates Usage --------------- All of the examples assume that you first load the thumbnail template tag in your template.:: {% load thumbnail %} A simple usage. :: {% thumbnail item.image "100x100" crop="center" as im %} {% endthumbnail %} See more examples in the section `Template examples`_ in the Documentation Model Usage ----------- Using the ImageField that automatically deletes references to itself in the key value store and its thumbnail references and the thumbnail files when deleted. Please note that this is only compatible with django 1.2.5 or less.:: from django.db import models from sorl.thumbnail import ImageField class Item(models.Model): image = ImageField(upload_to='whatever') See more examples in the section `Model examples`_ in the Documentation Low level API ------------- You can use the 'get_thumbnail':: from sorl.thumbnail import get_thumbnail from sorl.thumbnail import delete im = get_thumbnail(my_file, '100x100', crop='center', quality=99) delete(my_file) See more examples in the section `Low level API examples`_ in the Documentation Using in combination with other thumbnailers -------------------------------------------- Alternatively, you load the templatetags by {% load sorl_thumbnail %} instead of traditional {% load thumbnail %}. It's especially useful in projects that do make use of multiple thumbnailer libraries that use the same name (``thumbnail``) for the templatetag module:: {% load sorl_thumbnail %} {% thumbnail item.image "100x100" crop="center" as im %} {% endthumbnail %} Frequently asked questions ========================== Is so slow in Amazon S3! ------------------------ Possible related to the implementation of your Amazon S3 Backend, see the `issue #351`_ due the storage backend reviews if there is an existing thumbnail when tries to generate the thumbnail that makes an extensive use of the S3 API A fast workaround if you are not willing to tweak your storage backend is to set:: THUMBNAIL_FORCE_OVERWRITE = True So it will avoid to overly query the S3 API. .. |gh-actions| image:: https://github.com/jazzband/sorl-thumbnail/workflows/Test/badge.svg :target: https://github.com/jazzband/sorl-thumbnail/actions .. |docs| image:: https://readthedocs.org/projects/pip/badge/?version=latest :alt: Documentation for latest version :target: http://sorl-thumbnail.rtfd.org/en/latest/ .. |pypi| image:: https://img.shields.io/pypi/v/sorl-thumbnail.svg :target: https://pypi.python.org/pypi/sorl-thumbnail :alt: sorl-thumbnail on PyPI .. |codecov| image:: https://codecov.io/gh/jazzband/sorl-thumbnail/branch/master/graph/badge.svg :target: https://codecov.io/gh/jazzband/sorl-thumbnail :alt: Coverage .. |jazzband-badge| image:: https://jazzband.co/static/img/badge.svg :target: https://jazzband.co/ :alt: Jazzband .. |jazzband| image:: https://jazzband.co/static/img/jazzband.svg :target: https://jazzband.co/ :alt: Jazzband .. _`Pillow`: http://pillow.readthedocs.org/en/latest/ .. _`ImageMagick`: http://www.imagemagick.org/script/index.php .. _`PIL`: http://www.pythonware.com/products/pil/ .. _`Wand`: http://docs.wand-py.org/ .. _`pgmagick`: http://pgmagick.readthedocs.org/en/latest/ .. _`vipsthumbnail`: http://www.vips.ecs.soton.ac.uk/index.php?title=VIPS .. _`Template examples`: http://sorl-thumbnail.readthedocs.org/en/latest/examples.html#template-examples .. _`Model examples`: http://sorl-thumbnail.readthedocs.org/en/latest/examples.html#model-examples .. _`Low level API examples`: http://sorl-thumbnail.readthedocs.org/en/latest/examples.html#low-level-api-examples .. _`issue #351`: https://github.com/jazzband/sorl-thumbnail/issues/351 .. _`Django supported versions policy`: https://www.djangoproject.com/download/#supported-versions sorl-thumbnail-12.7.0/Vagrantfile000066400000000000000000000004271375675401600167470ustar00rootroot00000000000000# -*- mode: ruby -*- # vi: set ft=ruby : VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "precise64" config.vm.box_url = "http://files.vagrantup.com/precise64.box" config.vm.provision :shell, :path => "vagrant.sh" end sorl-thumbnail-12.7.0/codecov.yml000066400000000000000000000000731375675401600167240ustar00rootroot00000000000000coverage: precision: 2 round: down range: "60...100" sorl-thumbnail-12.7.0/docs/000077500000000000000000000000001375675401600155075ustar00rootroot00000000000000sorl-thumbnail-12.7.0/docs/Makefile000066400000000000000000000110121375675401600171420ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/sorlthumbnail.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/sorlthumbnail.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/sorlthumbnail" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/sorlthumbnail" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." sorl-thumbnail-12.7.0/docs/_theme/000077500000000000000000000000001375675401600167505ustar00rootroot00000000000000sorl-thumbnail-12.7.0/docs/_theme/nature/000077500000000000000000000000001375675401600202465ustar00rootroot00000000000000sorl-thumbnail-12.7.0/docs/_theme/nature/static/000077500000000000000000000000001375675401600215355ustar00rootroot00000000000000sorl-thumbnail-12.7.0/docs/_theme/nature/static/nature.css_t000066400000000000000000000074401375675401600240750ustar00rootroot00000000000000/** * Sphinx stylesheet -- default theme * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @import url("basic.css"); /* -- page layout ----------------------------------------------------------- */ body { font-family: Arial, sans-serif; font-size: 100%; background-color: #111; color: #555; margin: 0; padding: 0; } div.documentwrapper { float: left; width: 100%; } div.bodywrapper { margin: 0 0 0 230px; } hr{ border: 1px solid #B1B4B6; } div.document { background-color: #eee; } div.body { background-color: #ffffff; color: #3E4349; padding: 0 30px 30px 30px; font-size: 0.8em; } div.footer { color: #555; width: 100%; padding: 13px 0; text-align: center; font-size: 75%; } div.footer a { color: #444; text-decoration: underline; } div.related { background-color: #6BA81E; line-height: 32px; color: #fff; text-shadow: 0px 1px 0 #444; font-size: 0.80em; } div.related a { color: #E2F3CC; } div.sphinxsidebar { font-size: 0.75em; line-height: 1.5em; } div.sphinxsidebarwrapper{ padding: 20px 0; } div.sphinxsidebar h3, div.sphinxsidebar h4 { font-family: Arial, sans-serif; color: #222; font-size: 1.2em; font-weight: normal; margin: 0; padding: 5px 10px; background-color: #ddd; text-shadow: 1px 1px 0 white } div.sphinxsidebar h4{ font-size: 1.1em; } div.sphinxsidebar h3 a { color: #444; } div.sphinxsidebar p { color: #888; padding: 5px 20px; } div.sphinxsidebar p.topless { } div.sphinxsidebar ul { margin: 10px 20px; padding: 0; color: #000; } div.sphinxsidebar a { color: #444; } div.sphinxsidebar input { border: 1px solid #ccc; font-family: sans-serif; font-size: 1em; } div.sphinxsidebar input[type=text]{ margin-left: 20px; } /* -- body styles ----------------------------------------------------------- */ a { color: #005B81; text-decoration: none; } a:hover { color: #E32E00; text-decoration: underline; } div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { font-family: Arial, sans-serif; background-color: #BED4EB; font-weight: normal; color: #212224; margin: 30px 0px 10px 0px; padding: 5px 0 5px 10px; text-shadow: 0px 1px 0 white } div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; } div.body h2 { font-size: 150%; background-color: #C8D5E3; } div.body h3 { font-size: 120%; background-color: #D8DEE3; } div.body h4 { font-size: 110%; background-color: #D8DEE3; } div.body h5 { font-size: 100%; background-color: #D8DEE3; } div.body h6 { font-size: 100%; background-color: #D8DEE3; } a.headerlink { color: #c60f0f; font-size: 0.8em; padding: 0 4px 0 4px; text-decoration: none; } a.headerlink:hover { background-color: #c60f0f; color: white; } div.body p, div.body dd, div.body li { line-height: 1.5em; } div.admonition p.admonition-title + p { display: inline; } div.highlight{ background-color: white; } div.note { background-color: #eee; border: 1px solid #ccc; } div.seealso { background-color: #ffc; border: 1px solid #ff6; } div.topic { background-color: #eee; } div.warning { background-color: #ffe4e4; border: 1px solid #f66; } p.admonition-title { display: inline; } p.admonition-title:after { content: ":"; } pre { padding: 10px; background-color: White; color: #222; line-height: 1.2em; border: 1px solid #C6C9CB; font-size: 1.2em; margin: 1.5em 0 1.5em 0; -webkit-box-shadow: 1px 1px 1px #d8d8d8; -moz-box-shadow: 1px 1px 1px #d8d8d8; } tt { background-color: #ecf0f3; color: #222; padding: 1px 2px; font-size: 1.2em; font-family: monospace; } sorl-thumbnail-12.7.0/docs/_theme/nature/static/pygments.css000066400000000000000000000052351375675401600241220ustar00rootroot00000000000000.c { color: #999988; font-style: italic } /* Comment */ .k { font-weight: bold } /* Keyword */ .o { font-weight: bold } /* Operator */ .cm { color: #999988; font-style: italic } /* Comment.Multiline */ .cp { color: #999999; font-weight: bold } /* Comment.preproc */ .c1 { color: #999988; font-style: italic } /* Comment.Single */ .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .ge { font-style: italic } /* Generic.Emph */ .gr { color: #aa0000 } /* Generic.Error */ .gh { color: #999999 } /* Generic.Heading */ .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .go { color: #111 } /* Generic.Output */ .gp { color: #555555 } /* Generic.Prompt */ .gs { font-weight: bold } /* Generic.Strong */ .gu { color: #aaaaaa } /* Generic.Subheading */ .gt { color: #aa0000 } /* Generic.Traceback */ .kc { font-weight: bold } /* Keyword.Constant */ .kd { font-weight: bold } /* Keyword.Declaration */ .kp { font-weight: bold } /* Keyword.Pseudo */ .kr { font-weight: bold } /* Keyword.Reserved */ .kt { color: #445588; font-weight: bold } /* Keyword.Type */ .m { color: #009999 } /* Literal.Number */ .s { color: #bb8844 } /* Literal.String */ .na { color: #008080 } /* Name.Attribute */ .nb { color: #999999 } /* Name.Builtin */ .nc { color: #445588; font-weight: bold } /* Name.Class */ .no { color: #ff99ff } /* Name.Constant */ .ni { color: #800080 } /* Name.Entity */ .ne { color: #990000; font-weight: bold } /* Name.Exception */ .nf { color: #990000; font-weight: bold } /* Name.Function */ .nn { color: #555555 } /* Name.Namespace */ .nt { color: #000080 } /* Name.Tag */ .nv { color: purple } /* Name.Variable */ .ow { font-weight: bold } /* Operator.Word */ .mf { color: #009999 } /* Literal.Number.Float */ .mh { color: #009999 } /* Literal.Number.Hex */ .mi { color: #009999 } /* Literal.Number.Integer */ .mo { color: #009999 } /* Literal.Number.Oct */ .sb { color: #bb8844 } /* Literal.String.Backtick */ .sc { color: #bb8844 } /* Literal.String.Char */ .sd { color: #bb8844 } /* Literal.String.Doc */ .s2 { color: #bb8844 } /* Literal.String.Double */ .se { color: #bb8844 } /* Literal.String.Escape */ .sh { color: #bb8844 } /* Literal.String.Heredoc */ .si { color: #bb8844 } /* Literal.String.Interpol */ .sx { color: #bb8844 } /* Literal.String.Other */ .sr { color: #808000 } /* Literal.String.Regex */ .s1 { color: #bb8844 } /* Literal.String.Single */ .ss { color: #bb8844 } /* Literal.String.Symbol */ .bp { color: #999999 } /* Name.Builtin.Pseudo */ .vc { color: #ff99ff } /* Name.Variable.Class */ .vg { color: #ff99ff } /* Name.Variable.Global */ .vi { color: #ff99ff } /* Name.Variable.Instance */ .il { color: #009999 } /* Literal.Number.Integer.Long */sorl-thumbnail-12.7.0/docs/_theme/nature/theme.conf000066400000000000000000000001071375675401600222150ustar00rootroot00000000000000[theme] inherit = basic stylesheet = nature.css pygments_style = tango sorl-thumbnail-12.7.0/docs/conf.py000066400000000000000000000153231375675401600170120ustar00rootroot00000000000000# sorl-thumbnail documentation build configuration file, created by # sphinx-quickstart on Fri Nov 12 00:51:21 2010. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import pkg_resources # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'sorl-thumbnail' copyright = '2010, Mikko Hellsing' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = pkg_resources.get_distribution("sorl-thumbnail").version # The full version, including alpha/beta/rc tags. release = version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. #html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = ['_theme'] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'sorlthumbnaildoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'sorlthumbnail.tex', 'sorl-thumbnail Documentation', 'Mikko Hellsing', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'sorlthumbnail', 'sorl-thumbnail Documentation', ['Mikko Hellsing'], 1) ] sorl-thumbnail-12.7.0/docs/contributing.rst000066400000000000000000000052171375675401600207550ustar00rootroot00000000000000************ Contributing ************ Feel free to create a new Pull request if you want to propose a new feature or fix a bug. If you need development support or want to discuss with other developers, join us in the channel #sorl-thumnbnail at freenode.net irc://irc.freenode.net/#sorl-thumbnail Running testsuit ================ For occasional developers we recommend using `GitHub Actions`_ to run testsuite, for those who want to run tests locally, read on. Since sorl-thumbnail supports a variety of image backends, python and Django versions, we provide an easy way to test locally across all of them. We use `Vagrant`_ for simple interaction with virtual machines and `tox`_ for managing python virtual environments. Some dependencies like pgmagick takes a lot of time to compiling. To speed up your vagrant box you can edit `Vagrant file`_ with mem and cpu or simply install `vagrant-faster`_. The resulting .tox folder containing all virtualenvs requires ~ * `Install Vagrant`_ * ``cd`` in your source directory * Run ``vagrant up`` to prepare VM. It will download Ubuntu image and install all necessary dependencies. * Run ``vagrant ssh`` to log in the VM * Launch all tests via ``tox`` (will take some time to build envs first time) To run only tests against only one configuration use ``-e`` option:: tox -e py34-django16-pil Py34 stands for python version, 1.6 is Django version and the latter is image library. For full list of tox environments, see ``tox.ini`` You can get away without using Vagrant if you install all packages locally yourself, however, this is not recommended. .. _GitHub Actions: https://github.com/jazzband/sorl-thumbnail/actions .. _Vagrant: http://www.vagrantup.com/ .. _tox: https://testrun.org/tox/latest/ .. _Install Vagrant: http://docs.vagrantup.com/v2/installation/index.html .. _Vagrant file: https://docs.vagrantup.com/v2/virtualbox/configuration.html .. _vagrant-faster: https://github.com/rdsubhas/vagrant-faster Sending pull requests ===================== 1. Fork the repo:: git@github.com:jazzband/sorl-thumbnail.git 2. Create a branch for your specific changes:: $ git checkout master $ git pull $ git checkout -b feature/foobar To simplify things, please, make one branch per issue (pull request). It's also important to make sure your branch is up-to-date with upstream master, so that maintainers can merge changes easily. 3. Commit changes. Please update docs, if relevant. 4. Don't forget to run tests to check than nothing breaks. 5. Ideally, write your own tests for new feature/bug fix. 6. Submit a `pull request`_. .. _pull request: https://help.github.com/articles/using-pull-requests sorl-thumbnail-12.7.0/docs/examples.rst000066400000000000000000000115331375675401600200620ustar00rootroot00000000000000******** Examples ******** Template examples ================= .. highlight:: html+django All of the examples assume that you first load the ``thumbnail`` template tag in your template:: {% load thumbnail %} Simple:: {% thumbnail item.image "100x100" crop="center" as im %} {% endthumbnail %} Crop using margin filter, x, y aliases:: {% thumbnail item.image "100x700" as im %} {% endthumbnail %} Using external images and advanced cropping:: {% thumbnail "http://www.aino.se/media/i/logo.png" "40x40" crop="80% top" as im %} {% endthumbnail %} Using the empty feature, the empty section is rendered when the source is resolved to an empty value or an invalid image source, you can think of it as rendering when the thumbnail becomes undefined:: {% thumbnail item.image my_size_string crop="left" as im %} {% empty %}

No image

{% endthumbnail %} Nesting tags and setting size (geometry) for width only:: {% thumbnail item.image "1000" as big %} {% thumbnail item.image "50x50" crop="center" as small %} {% endthumbnail %} {% endthumbnail %} Setting geometry for height only:: {% thumbnail item.image "x300" as im %} {% endthumbnail %} Setting format and using the is_portrait filter:: {% if item.image|is_portrait %}
{% thumbnail item.image "100" crop="10px 10px" format="PNG" as im %} {% endthumbnail %}
{% else %}
{% thumbnail item.image "50" crop="bottom" format="PNG" as im %} {% endthumbnail %}

Undefined behaviour

{% endif %} Using HTML filter:: {{ text|html_thumbnails }} Using markdown filter:: {{ text|markdown_thumbnails }} .. highlight:: python Model examples ============== Using the ImageField that automatically deletes references to itself in the key value store and its thumbnail references when deleted:: from django.db import models from sorl.thumbnail import ImageField class Item(models.Model): image = ImageField(upload_to='whatever') .. note:: You do not need to use the ``sorl.thumbnail.ImageField`` to use ``sorl.thumbnail``. The standard ``django.db.models.ImageField`` is fine except that using the ``sorl.thumbnail.ImageField`` lets you plugin the nice admin addition explained in the next section. Another example on how to use ``sorl.thumbnail.ImageField`` in your existing project with only small code changes:: # util/models.py from django.db.models import * from sorl.thumbnail import ImageField # myapp/models.py from util import models class MyModel(models.Model): logo = ImageField(upload_to='/dev/null') Admin examples ============== Recommended usage using ``sorl.thumbnail.admin.AdminImageMixin`` (note that this requires use of ``sorl.thumbnail.ImageField`` in your models as explained above):: # myapp/admin.py from django.contrib import admin from myapp.models import MyModel from sorl.thumbnail.admin import AdminImageMixin class MyModelAdmin(AdminImageMixin, admin.ModelAdmin): pass And the same thing For inlines:: # myapp/admin.py from django.contrib import admin from myapp.models import MyModel, MyInlineModel from sorl.thumbnail.admin import AdminImageMixin class MyInlineModelAdmin(AdminImageMixin, admin.TabularInline): model = MyInlineModel class MyModelAdmin(admin.ModelAdmin): inlines = [MyInlineModelAdmin] Easy to plugin solution example with little code to change:: # util/admin.py from django.contrib.admin import * from sorl.thumbnail.admin import AdminImageMixin class ModelAdmin(AdminImageMixin, ModelAdmin): pass class TabularInline(AdminImageMixin, TabularInline): pass class StackedInline(AdminImageMixin, StackedInline): pass # myapp/admin.py from util import admin from myapp.models import MyModel class MyModelAdmin(admin.ModelAdmin): pass Low level API examples ====================== How to get make a thumbnail in your python code:: from sorl.thumbnail import get_thumbnail im = get_thumbnail(my_file, '100x100', crop='center', quality=99) How to delete a file, its thumbnails as well as references in the Key Value Store:: from sorl.thumbnail import delete delete(my_file) sorl-thumbnail-12.7.0/docs/index.rst000066400000000000000000000005461375675401600173550ustar00rootroot00000000000000****************************** sorl-thumbnail's documentation ****************************** Contents: .. toctree:: :maxdepth: 2 examples installation requirements template management logging operation reference/index contributing Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sorl-thumbnail-12.7.0/docs/installation.rst000066400000000000000000000010461375675401600207430ustar00rootroot00000000000000******************** Installation & Setup ******************** Installation ============ First you need to make sure to read the :doc:`requirements`. To install sorl-thumbnail is easy:: pip install sorl-thumbnail Or you can go to `the github page `_ Setup ===== .. highlight:: python 1. Add ``sorl.thumbnail`` to your ``settings.INSTALLED_APPS``. 2. Configure your ``settings`` 3. If you are using the cached database key value store you need to sync the database:: python manage.py migrate sorl-thumbnail-12.7.0/docs/logging.rst000066400000000000000000000021641375675401600176720ustar00rootroot00000000000000**************** Errors & Logging **************** .. highlight:: python Background ========== When ``THUMBNAIL_DEBUG = False`` errors will be suppressed if they are raised during rendering the ``thumbnail`` tag or raised within the included filters. This is the recommended production setting. However it can still be useful to be notified of those errors. Thus sorl-thumbnail logs errors to a logger and provides a log handler that sends emails to ``settings.ADMINS``. How to setup logging ==================== To enable logging you need to add a handler to the 'sorl.thumbnail' logger. The following example adds the provided handler that sends emails to site admins in case an error is raised with debugging off:: import logging from sorl.thumbnail.log import ThumbnailLogHandler handler = ThumbnailLogHandler() handler.setLevel(logging.ERROR) logging.getLogger('sorl.thumbnail').addHandler(handler) You will need to load this code somewhere in your django project, it could be in urls.py, settings.py or project/app __init__.py file for example. You could of course also provide your own logging handler. sorl-thumbnail-12.7.0/docs/make.bat000066400000000000000000000100301375675401600171060ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\sorlthumbnail.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\sorlthumbnail.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end sorl-thumbnail-12.7.0/docs/management.rst000066400000000000000000000041761375675401600203650ustar00rootroot00000000000000******************* Management commands ******************* .. highlight:: python .. _thumbnail-cleanup: thumbnail cleanup ================= ``python manage.py thumbnail cleanup`` This cleans up the Key Value Store from stale cache. It removes references to images that do not exist and thumbnail references and their actual files for images that do not exist. It removes thumbnails for unknown images. .. _thumbnail-clear: thumbnail clear =============== ``python manage.py thumbnail clear`` This totally empties the Key Value Store of all keys that start with the ``settings.THUMBNAIL_KEY_PREFIX``. It does not delete any files. The Key Value store will update when you hit the template tags, and if the thumbnails files still exist they will be used and not overwritten/regenerated. This can be useful if your Key Value Store has garbage data not dealt with by cleanup or you're switching Key Value Store backend. .. _thumbnail-clear-delete-referenced: thumbnail clear_delete_referenced ================================= ``python manage.py thumbnail clear_delete_referenced`` Equivalent to to ``clear`` but first it will delete all thumbnail files referenced by the Key Value Store. It is generally safe to run this if you do not reference the generated thumbnails by name somewhere else in your code. As long as all the original images still exist this will trigger a regeneration of all the thumbnails the Key Value Store knows about. .. _thumbnail-clear-delete-all: thumbnail clear_delete_all ========================== ``python manage.py thumbnail clear_delete_all`` Equivalent to to ``clear`` but afterwards it will delete all thumbnail files including any orphans not in the Key Value Store. This can be thought of as a more aggressive version of ``clear_delete_referenced``. Caution should be exercised with this command if multiple Django sites (as in ``SITE_ID``) or projects are using the same ``MEDIA_ROOT`` since this will clear out absolutely everything in the thumbnail cache directory causing thumbnail regeneration for all sites and projects. When file system storage is used, it is equivalent to ``rm -rf MEDIA_ROOT + THUMBNAIL_PREFIX`` sorl-thumbnail-12.7.0/docs/operation.rst000066400000000000000000000061731375675401600202500ustar00rootroot00000000000000*************************** How sorl-thumbnail operates *************************** .. highlight:: python When you use the ``thumbnail`` template tag sorl-thumbnail looks up the thumbnail in a :ref:`kvstore-requirements`. The key for a thumbnail is generated from its filename and storage. The thumbnail filename in turn is generated from the source and requested thumbnail size and options. If the key for the thumbnail is found in the |kvstore|, the serialized thumbnail information is fetched from it and returned. If the thumbnail key is not found there sorl-thumbnail continues to generate the thumbnail and stores necessary information in the |kvstore|. It is worth noting that sorl-thumbnail does not check if source or thumbnail exists if the thumbnail key is found in the |kvstore|. .. note:: This means that if you change or delete a source file or delete the thumbnail, sorl-thumbnail will still fetch from the |kvstore|. Therefore it is important that if you delete or change a source or thumbnail file notify the |kvstore|. If you change or delete a source or a thumbnail for some reason, you can use the ``delete`` method of the ``ThumbnailBackend`` class or subclass:: from sorl.thumbnail import delete # Delete the Key Value Store reference but **not** the file. # Use this if you have changed the source delete(my_file, delete_file=False) # Delete the Key Value Store reference and the file # Use this if you want to delete the source file delete(my_file) # delete_file=True is default The ``sorl.thumbnail.delete`` method always deletes the input files thumbnail Key Value Store references as well as thumbnail files. You can use this method on thumbnails as well as source files. Alternatively if you have **deleted** a file you can use the management command :ref:`thumbnail-cleanup`. Deleting an image using the ``sorl.thumbnail.ImageField`` will notify the |kvstore| to delete references to it and delete all of its thumbnail references and files, exactly like the above code example. **Why you ask?** Why go through all the trouble with a |kvstore| and risk stale cache? Why not use a database to cache if you are going to do that? The reason is speed and especially with storages other than local file storage. Checking if a file exists before serving it will cost too much. Speed is also the reason for not choosing to use a standard database for this kind of persistent caching. However sorl-thumbnail does ship with a *cached* database |kvstore|. .. note:: We have to assume the thumbnail exists if the thumbnail key exists in the |kvstore| **There are bonuses**. We can store meta data in the |kvstore| that would be too costly to retrieve even for local file storage. Today this meta data consists only of the image size but this could be expanded to for example EXIF data. The other bonus is that we can keep track of what thumbnails has been generated from a particular source and deleting them too when the source is deleted. `Schematic view of how things are done `_ .. |kvstore| replace:: Key Value Store sorl-thumbnail-12.7.0/docs/reference/000077500000000000000000000000001375675401600174455ustar00rootroot00000000000000sorl-thumbnail-12.7.0/docs/reference/image.rst000066400000000000000000000036131375675401600212640ustar00rootroot00000000000000********* ImageFile ********* .. highlight:: html+django ``ImageFile`` is an image abstraction that contains useful attributes when working with images. The ``thumbnail`` template tag puts the generated thumbnail in context as an ``ImageFile`` instance. In the following example:: {% thumbnail item.image "100x100" as im %} {% endthumbnail %} ``im`` will be an ``ImageFile`` instance. .. highlight:: python ImageFile attributes ==================== ``name`` -------- Name of the image as returned from the underlying storage. ``storage`` ----------- Returns the storage instance. ``width`` --------- Returns the width of the image in pixels. ``x`` ----- Alias of ``width`` ``height`` ---------- Returns the height of the image in pixels. ``y`` ----- Alias of ``height`` ``ratio`` --------- Returns the image ratio (y/x) as a float ``url`` ------- URL of the image url as returned by the underlying storage. ``src`` ------- Alias of ``url`` ``size`` -------- Returns the image size in pixels as a (x, y) tuple ``key`` ------- Returns a unique key based on ``name`` and ``storage``. ImageFile methods ================= ``exists`` ---------- Returns whether the file exists as returned by the underlying storage. ``is_portrait`` --------------- Returns ``True`` if ``y > x``, else ``False`` ``set_size`` ------------ Sets the size of the image, takes an optional size tuple (x, y) as argument. ``read`` -------- Reads the file as done from the underlying storage. ``write`` --------- Writes content to the file. Takes content as argument. Content is either raw data or an instance of ``django.core.files.base.ContentFile``. ``delete`` ---------- Deletes the file from underlying storage. ``serialize`` ------------- Returns a serialized version of self. ``serialize_storage`` --------------------- Returns the ``self.storage`` as a serialized dot name path string. sorl-thumbnail-12.7.0/docs/reference/index.rst000066400000000000000000000001231375675401600213020ustar00rootroot00000000000000********* Reference ********* .. toctree:: :maxdepth: 2 image settings sorl-thumbnail-12.7.0/docs/reference/settings.rst000066400000000000000000000236741375675401600220530ustar00rootroot00000000000000******** Settings ******** .. highlight:: python ``THUMBNAIL_DEBUG`` =================== - Default: ``False`` When set to ``True`` the ``ThumbnailNode.render`` method can raise errors. Django recommends that tags never raise errors in the ``Node.render`` method but since sorl-thumbnail is such a complex tag we will need to have more debugging available. ``THUMBNAIL_BACKEND`` ===================== - Default: ``'sorl.thumbnail.base.ThumbnailBackend'`` This is the entry point for generating thumbnails, you probably want to keep the default one but just in case you would like to generate thumbnails filenames differently or need some special functionality you can override this and use your own implementation. ``THUMBNAIL_KVSTORE`` ===================== - Default: ``'sorl.thumbnail.kvstores.cached_db_kvstore.KVStore'`` sorl-thumbnail needs a Key Value Store to :doc:`/operation`. sorl-thumbnail ships with support for three Key Value Stores: Cached DB --------- ``sorl.thumbnail.kvstores.cached_db_kvstore.KVStore``. This is the default and preferred Key Value Store. Features ^^^^^^^^ * Fast persistent storage * First query uses database which is slow. Successive queries are cached and if you use memcached this is very fast. * Easy to transfer data between environments since the data is in the default database. * If you get the database and fast cache out of sync there could be problems. Redis ----- ``sorl.thumbnail.kvstores.redis_kvstore.KVStore``. It requires you to install a Redis server as well as a `redis python client `_. Features ^^^^^^^^ * Fast persistent storage * More dependencies * Requires a little extra work to transfer data between environments Dbm --- ``sorl.thumbnail.kvstores.dbm_kvstore.KVStore``. A simple Key Value Store has no dependencies outside the standard Python library and uses the DBM modules to store the data. Features ^^^^^^^^ * No external dependencies, besides the standard library * No extra components required, e.g., database or cache * Specially indicated for local development environments ``THUMBNAIL_KEY_DBCOLUMN`` ========================== - Default ``'key'`` Since MSSQL reserved the ``key`` name for db columns you can change this to something else using this setting. ``THUMBNAIL_ENGINE`` ==================== - Default: ``'sorl.thumbnail.engines.pil_engine.Engine'`` This is the processing class for sorl-thumbnail. It does all the resizing, cropping or whatever processing you want to perform. sorl-thumbnail ships with four engines: PIL --- ``'sorl.thumbnail.engines.pil_engine.Engine'``. This is the default engine because it is what most people have installed already. Features: * Easy to install * Produces good quality images but not the best * It is fast * Can not handle CMYK sources Pgmagick -------- ``'sorl.thumbnail.engines.pgmagick_engine.Engine'``. Pgmagick uses `Graphics `_. Fatures: * Not easy to install unless on linux, very slow to compile * Produces high quality images * It is a tad slow? * Can handle CMYK sources ImageMagick / GraphicsMagick ---------------------------- ``'sorl.thumbnail.engines.convert_engine.Engine'``. This engine uses the ImageMagick ``convert`` or GraphicsMagic ``gm convert`` command. Features: * Easy to install * Produces high quality images * It is pretty fast * Can handle CMYK sources * It is a command line command, that is less than ideal, Wand ---------------------------- ``'sorl.thumbnail.engines.wand_engine.Engine'``. This engine uses `Wand `_, a ctypes-based simple ImageMagick binding for Python. Features: * Easy to install * Produces high quality images * Can handle CMYK sources * Works on Python 2.6, 2.7, 3.2, 3.3, and PyPy ``THUMBNAIL_CONVERT`` ===================== - Default ``'convert'`` Path to convert command, use ``'gm convert'`` for GraphicsMagick. Only applicable for the convert Engine. ``THUMBNAIL_IDENTIFY`` ====================== - Default ``'identify'`` Path to identify command, use ``'gm identify'`` for GraphicsMagick. Only applicable for the convert Engine. ``THUMBNAIL_STORAGE`` ===================== - Default: ``settings.DEFAULT_FILE_STORAGE`` The storage class to use for the generated thumbnails. ``THUMBNAIL_REDIS_URL`` ======================= The Redis database URL to connect as used by `redis-py `_ When specified, other ``THUMBNAIL_REDIS_*`` connection settings will be ignored. ``THUMBNAIL_REDIS_DB`` ====================== - Default: ``0`` The Redis database. Only applicable for the Redis Key Value Store ``THUMBNAIL_REDIS_PASSWORD`` ============================ - Default: ``''`` The password for Redis server. Only applicable for the Redis Key Value Store ``THUMBNAIL_REDIS_HOST`` ======================== - Default: ``'localhost'`` The host for Redis server. Only applicable for the Redis Key Value Store ``THUMBNAIL_REDIS_PORT`` ======================== - Default: ``6379`` The port for Redis server. Only applicable for the Redis Key Value Store ``THUMBNAIL_REDIS_TIMEOUT`` =========================== - Default: ``3600 * 24 * 365 * 10`` Cache timeout for Redis Key Value Store in seconds. You should probably keep this at maximum or ``None``. ``THUMBNAIL_DBM_FILE`` ====================== - Default: ``thumbnail_kvstore`` Filename of the DBM database. Depending on the DBM engine selected by your Python installation, this will be used as a prefix because multiple files may be created. This can be an absolute path. ``THUMBNAIL_DBM_MODE`` ====================== - Default: ``0x644`` Permission bits to use when creating new DBM files ``THUMBNAIL_CACHE_TIMEOUT`` =========================== - Default: ``3600 * 24 * 365 * 10`` Cache timeout for Cached DB Key Value Store in seconds. You should probably keep this at maximum or ``None`` if your caching backend can handle that as infinite. Only applicable for the Cached DB Key Value Store. ``THUMBNAIL_CACHE`` =================== - Default: ``'default'`` Cache configuration for Cached DB Key Value Store. Defaults to the ``'default'`` cache but some applications might have multiple cache clusters. ``THUMBNAIL_KEY_PREFIX`` ======================== - Default: ``'sorl-thumbnail'`` Key prefix used by the key value store. ``THUMBNAIL_PREFIX`` ==================== - Default: ``'cache/'`` The generated thumbnails filename prefix. ``THUMBNAIL_FORMAT`` ==================== - Default: ``'JPEG'`` Default image format, supported formats are: ``'JPEG'``, ``'PNG'``. This also implicitly sets the filename extension. This can be overridden by individual options. ``THUMBNAIL_PRESERVE_FORMAT`` ============================= - Default: ``False`` If ``True``, the format of the input file will be preserved. If ``False``, ``THUMBNAIL_FORMAT`` will be used. ``THUMBNAIL_COLORSPACE`` ======================== - Default: ``'RGB'`` Default thumbnail color space, engines are required to implement: ``'RGB'``, ``'GRAY'`` Setting this to None will keep the original color space. This can be overridden by individual options. ``THUMBNAIL_UPSCALE`` ===================== - Default: ``True`` Should we upscale by default? ``True`` means we upscale images by default. ``False`` means we don't. This can be overridden by individual options. ``THUMBNAIL_QUALITY`` ===================== - Default: ``95`` Default thumbnail quality. A value between 0 and 100 is allowed. This can be overridden by individual options. ``THUMBNAIL_PROGRESSIVE`` ========================= - Default: ``True`` Saves jpeg thumbnails as progressive jpegs. This can be overridden by individual options. ``THUMBNAIL_ORIENTATION`` ========================= - Default: ``True`` Orientate the thumbnail with respect to source EXIF orientation tag ``THUMBNAIL_DUMMY`` =================== - Default: ``False`` This is a very powerful option which came from real world frustration. The use case is when you want to do development on a deployed project that has image references in its database. Instead of downloading all the image files from the server hosting the deployed project and all its thumbnails we just set this option to ``True``. This will generate placeholder images for all thumbnails missing input source. ``THUMBNAIL_DUMMY_SOURCE`` ========================== - Default ``http://dummyimage.com/%(width)sx%(height)s`` This is the generated thumbnail whensource of the presented thumbnail. Width and Height is passed to the string for formatting. Other options are for example: - ``http://placehold.it/%(width)sx%(height)s`` - ``http://placekitten.com/%(width)s/%(height)s`` ``THUMBNAIL_DUMMY_RATIO`` ========================= - Default: ``1.5`` This value sets an image ratio to all thumbnails that are not defined by width **and** height since we cannot determine from the file input (since we don't have that). ``THUMBNAIL_ALTERNATIVE_RESOLUTIONS`` ===================================== - Default: ``[]`` - Example: ``[1.5, 2]`` This value enables creation of additional high-resolution ("Retina") thumbnails for every thumbnail. Resolution multiplicators, e.g. value 2 means for every thumbnail of regular size x\*y, additional thumbnail of 2x\*2y size is created. ``THUMBNAIL_FILTER_WIDTH`` ========================== - Default: ``500`` This value sets the width of thumbnails inserted when running filters one texts that regex replaces references to images with thumbnails. ``THUMBNAIL_URL_TIMEOUT`` ========================= - Default: ``None`` This value sets the timeout value in seconds when retrieving a source image from a URL. If no timeout value is specified, it will wait indefinitely for a response. ``THUMBNAIL_REMOVE_URL_ARGS`` ============================= - Default: ``True`` This value sets if URL arguments will be removed from the source URL of the image we want to generate a thumbnail of. E.g. if our source image is at ``/picture?height=600&width=600`` a ``True`` value would instead attempt to generate a thumbnail from ``/picture``. sorl-thumbnail-12.7.0/docs/requirements.rst000066400000000000000000000064311375675401600207700ustar00rootroot00000000000000************ Requirements ************ Base requirements ================= - `Python`_ 3.6+ - `Django`_ - :ref:`kvstore-requirements` - :ref:`image-library` .. _kvstore-requirements: Key Value Store =============== sorl-thumbnail needs a Key Value Store for its operation. You can choose between a **cached database** which requires no special installation to your normal Django setup besides installing a proper cache like memcached **or** you can setup **redis** which requires a little bit more work. Cached DB --------- All you need to use the cached database key value store is a database and `cache `_ setup properly using memcached. This cache needs to be really fast so **using anything else than memcached is not recomended**. Redis ----- Redis is a fast key value store also suited for the job. To use the `redis`_ key value store you first need to install the `redis server `_. After that install the `redis client `_:: pip install redis .. _image-library: Image Library ============= You need to have an image library installed. sorl-thumbnail ships with support for `Python Imaging Library`_, `pgmagick`_, `ImageMagick`_ (or `GraphicsMagick`) command line tools. `pgmagick`_ are python bindings for `GraphicsMagick`_ (Magick++)`, The `ImageMagick`_ based engine ``sorl.thumbnail.engines.convert_engine.Engine`` by default calls ``convert`` and ``identify`` shell commands. You can change the paths to these tools by setting ``THUMBNAIL_CONVERT`` and ``THUMBNAIL_IDENTIFY`` respectively. Note that you need to change these to use `GraphicsMagick`_ to ``/path/to/gm convert`` and ``/path/to/gm identify``. Python Imaging Library installation ----------------------------------- Prerequisites: - libjpeg - zlib Ubuntu 10.04 package installation:: sudo apt-get install libjpeg62 libjpeg62-dev zlib1g-dev Installing `Python Imaging Library`_ using pip:: pip install Pillow Watch the output for messages on what support got compiled in, you at least want to see the following:: --- JPEG support available --- ZLIB (PNG/ZIP) support available pgmagick installation --------------------- Prerequisites: - GraphicsMagick - Boost.Python Ubuntu 10.04 package installation:: sudo apt-get install libgraphicsmagick++-dev sudo apt-get install libboost-python1.40-dev Fedora installation:: yum install GraphicsMagick-c++-devel yum install boost-devel Installing `pgmagick`_ using pip:: pip install pgmagick ImageMagick installation ------------------------ Ubuntu 10.04 package installation:: sudo apt-get install imagemagick Or if you prefer `GraphicsMagick`_:: sudo apt-get install graphicsmagick Wand installation ------------------------ Ubuntu installation:: apt-get install libmagickwand-dev pip install Wand .. _Python Imaging Library: http://www.pythonware.com/products/pil/ .. _ImageMagick: http://imagemagick.com/ .. _GraphicsMagick: http://www.graphicsmagick.org/ .. _redis: http://code.google.com/p/redis/ .. _redis-py: https://github.com/andymccurdy/redis-py/ .. _Django: http://www.djangoproject.com/ .. _Python: http://www.python.org/ .. _pgmagick: http://bitbucket.org/hhatto/pgmagick/src .. _wand: http://wand-py.org sorl-thumbnail-12.7.0/docs/template.rst000066400000000000000000000220561375675401600200610ustar00rootroot00000000000000************************* Template tags and filters ************************* .. highlight:: html+django Sorl-thumbnail comes with one template tag `thumbnail`_ and three filters: `is_portrait`_, `margin`_ and `resolution`_. To use any of them in you templates you first need to load them:: {% load thumbnail %} .. _thumbnail: thumbnail ========= Syntax:: {% thumbnail source geometry [key1=value1, key2=value2...] as var %} {% endthumbnail %} Alternative syntax using empty:: {% thumbnail source geometry [key1=value1, key2=value2...] as var %} {% empty %} {% endthumbnail %} The ``{% empty %}`` section is rendered if the thumbnail source is resolved to an empty value or an invalid image source, you can think of it as rendering when the thumbnail becomes undefined. .. _source: Source ------ .. highlight:: python Source can be an ImageField, FileField, a file name (assuming default_storage), a url. What we need to know is name and storage, see how ImageFile figures these things out:: from django.utils.encoding import force_str class ImageFile(BaseImageFile): _size = None def __init__(self, file_, storage=None): if not file_: raise ThumbnailError('File is empty.') # figure out name if hasattr(file_, 'name'): self.name = file_.name else: self.name = force_str(file_) # figure out storage if storage is not None: self.storage = storage elif hasattr(file_, 'storage'): self.storage = file_.storage elif url_pat.match(self.name): self.storage = UrlStorage() else: self.storage = default_storage Geometry -------- .. highlight:: html+django Geometry is specified as ``widthxheight``, ``width`` or ``xheight``. Width and height are in pixels. Geometry can either be a string or resolve into a valid geometry string. Examples:: {% thumbnail item.image "200x100" as im %} {% endthumbnail %} {% thumbnail item.image "200" as im %} {% endthumbnail %} {% thumbnail item.image "x100" as im %} {% endthumbnail %} {% thumbnail item.image geometry as im %} {% endthumbnail %} If width and height are given the image is rescaled to maximum values of height and width given. Aspect ratio preserved. Options ------- Options are passed on to the backend and engine, the backend generates the thumbnail filename from it and the engine can use it for processing. Option keys are not resolved in context but values are. Passing all options to the engine means that you can easily subclass an engine and create new features like rounded corners or what ever processing you like. The options described below are how they are used and interpreted in the shipped engines. ``cropbox`` ^^^^^^^^^^^ This option is used to crop to a specific set of coordinates. ``cropbox`` takes ``x, y, x2, y2`` as arguments to crop the image down via those set of coordinates. Note that ``cropbox`` is applied before ``crop``. .. code-block:: python img = get_thumbnail(sorl_img, cropbox="{0},{1},{2},{3}".format( x, y, x2, y2)) ``crop`` ^^^^^^^^ This option is only used if both width and height is given. Crop behaves much like `css background-position`_. The image is first rescaled to minimum values of height and width given, this will be equivalent to the `padding box` in the above text. After it is rescaled it will apply the cropping options. There are some differences to the `css background-position`_: - Only % and px are valid lengths (units) - ``noop`` (No Operation) is a valid option which means there is no cropping after the initial rescaling to minimum of width and height. There are many overlapping options here for example ``center`` is equivalent to ``50%``. There is not a problem with that in it self but it is a bit of a problem if you will for sorl-thumbnail. Sorl-thumbnail will generate a new thumbnail for every unique source, geometry and options. This is a design choice because we want to stay flexible with the options and not interpret them anywhere else but in the engine methods. In clear words, be consistent in your cropping options if you don't want to generate unnecessary thumbnails. In case you are wondering, sorl-thumbnail sorts the options so the order does not matter, same options but in different order will generate only one thumbnail. ``upscale`` ^^^^^^^^^^^ Upscale is a boolean and controls if the image can be upscaled or not. For example if your source is 100x100 and you request a thumbnail of size 200x200 and upscale is False this will return a thumbnail of size 100x100. If upscale was True this would result in a thumbnail size 200x200 (upscaled). The default value is ``True``. ``quality`` ^^^^^^^^^^^ Quality is a value between 0-100 and controls the thumbnail write quality. Default value is ``95``. ``progressive`` ^^^^^^^^^^^^^^^ This controls whether to save jpeg thumbnails as progressive jpegs. Default value is ``True``. ``orientation`` ^^^^^^^^^^^^^^^ This controls whether to orientate the resulting thumbnail with respect to the source EXIF tags for orientation. Default value is ``True``. ``format`` ^^^^^^^^^^ This controls the write format and thumbnail extension. Formats supported by the shipped engines are ``'JPEG'`` and ``'PNG'``. Default value is ``'JPEG'``. ``colorspace`` ^^^^^^^^^^^^^^ This controls the resulting thumbnails color space, valid values are: ``'RGB'`` and ``'GRAY'``. Default value is ``'RGB'``. ``padding`` ^^^^^^^^^^^ Padding is a boolean and controls if the image should be padded to fit the specified geometry. If your image is ``200x100``:: {% thumbnail image "100x100" padding=True as im %} ``im`` will be ``100x100`` with white padding at the top and bottom. The color of the padding can be controlled with ``padding_color`` or the setting ``THUMBNAIL_PADDING_COLOR`` which defaults to ``#ffffff``. Images are not padded by default, but this can be changed by setting ``THUMBNAIL_PADDING`` to ``True``. ``padding_color`` ^^^^^^^^^^^^^^^^^ This is the color to use for padding the image. It defaults to ``#ffffff`` and can be globally set with the setting ``THUMBNAIL_PADDING_COLOR``. ``options`` ^^^^^^^^^^^ Yes this option is called ``options``. This needs to be a context variable that resolves to a dictionary. This dictionary can contain multiple options, for example:: options = {'colorspace': 'GRAY', 'quality': 75, 'crop': 'center'} You can use this option together with the other options but beware that the order will matter. As soon as the keyword ``options`` is encountered all the options that have a key in ``options`` are overwritten. Similarly, options in the ``options`` dict will be overwritten by options set after the options keyword argument to the thumbnail tag. is_portrait =========== This filter returns True if the image height is larger than the image width. Examples:: {% thumbnail item.image "100x100" %} {% if item.image|is_portrait %}
{% else %}
{% endif %} {% endthumbnail %} {% if item.image|is_portrait %} {% thumbnail item.image "100x200" crop="center" %} {% endthumbnail %} {% else %} {% thumbnail item.image "200x100" crop="center" %} {% endthumbnail %} {% endif %} margin ====== Margin is a filter for calculating margins against a padding box. For example lets say you have an image ``item.image`` and you want to pad it vertically in a 1000x1000 box, you would simply write::
The above is a rather synthetic example the more common use case is when you want boxes of images of a certain size but you do not want to crop them:: {% for profile in profiles %}
{% thumbnail profile.photo "100x100" as im %} {% empty %} {% endthumbnail %}
{% enfor %} The more problematic is to get the top margin, however the margin filter outputs all values. .. _css background-position: http://www.w3.org/TR/CSS2/colors.html#propdef-background-position resolution ========== Resolution is a filter for obtaining alternative resolution versions of the thumbnail. Your provided resolution must be one of the ``THUMBNAIL_ALTERNATIVE_RESOLUTIONS`` settings values (default: no alternative resolutions) For example, let's say you have an image ``item.image`` and you want to get the 2x DPI version of it. You would simply write::
sorl-thumbnail-12.7.0/setup.cfg000066400000000000000000000015601375675401600164020ustar00rootroot00000000000000[bdist_wheel] universal = 0 ;; flake8 analyzes and detect varios errors in the source code. [flake8] # D105 - Missing docstring in magic method `__func__` ignore = D105 max-line-length = 100 exclude = .git, .tox, docs/, */migrations/*, tests/settings/ sorl/thumbnail/__init__.py, sorl/thumbnail/admin/__init__.py max_complexity = 15 ;; Coverage.py, Code coverage testing for Python. [cov:run] source = sorl omit = */sorl-thumbnail/sorl/__init__.py */sorl/thumbnail/__init__.py */sorl/thumbnail/conf/__init__.py */sorl/thumbnail/admin/__init__.py [cov:report] exclude_lines = pragma: no cover if __name__ == .__main__.: ;; The pytest framework [tool:pytest] python_files = test_*.py *tests.py norecursedirs = .* tmp* __pycache__ testpaths = tests django_find_project = false sorl-thumbnail-12.7.0/setup.py000066400000000000000000000030131375675401600162660ustar00rootroot00000000000000from setuptools import setup, find_packages from setuptools.command.test import test class TestCommand(test): def run(self): from tests.runtests import runtests runtests() setup( name='sorl-thumbnail', use_scm_version=True, description='Thumbnails for Django', long_description=open('README.rst').read(), author="Mikko Hellsing", author_email='mikko@aino.se', maintainer="Jazzband", maintainer_email="roadies@jazzband.co", license="BSD", url='https://github.com/jazzband/sorl-thumbnail', packages=find_packages(exclude=['tests', 'tests.*']), platforms='any', python_requires='>=3.4', zip_safe=False, classifiers=[ 'Development Status :: 2 - Pre-Alpha', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Multimedia :: Graphics', 'Framework :: Django', 'Framework :: Django :: 1.11', 'Framework :: Django :: 2.2', 'Framework :: Django :: 3.0', ], cmdclass={"test": TestCommand}, setup_requires=['setuptools_scm'], ) sorl-thumbnail-12.7.0/sorl/000077500000000000000000000000001375675401600155365ustar00rootroot00000000000000sorl-thumbnail-12.7.0/sorl/__init__.py000066400000000000000000000002521375675401600176460ustar00rootroot00000000000000from pkg_resources import get_distribution, DistributionNotFound try: __version__ = get_distribution("sorl-thumbnail").version except DistributionNotFound: pass sorl-thumbnail-12.7.0/sorl/thumbnail/000077500000000000000000000000001375675401600175215ustar00rootroot00000000000000sorl-thumbnail-12.7.0/sorl/thumbnail/__init__.py000066400000000000000000000001511375675401600216270ustar00rootroot00000000000000from sorl.thumbnail.fields import ImageField from sorl.thumbnail.shortcuts import get_thumbnail, delete sorl-thumbnail-12.7.0/sorl/thumbnail/admin/000077500000000000000000000000001375675401600206115ustar00rootroot00000000000000sorl-thumbnail-12.7.0/sorl/thumbnail/admin/__init__.py000066400000000000000000000002241375675401600227200ustar00rootroot00000000000000from django.forms import ClearableFileInput from .current import AdminImageMixin AdminInlineImageMixin = AdminImageMixin # backwards compatibility sorl-thumbnail-12.7.0/sorl/thumbnail/admin/current.py000066400000000000000000000042571375675401600226550ustar00rootroot00000000000000import logging from django import forms from django.utils.safestring import mark_safe from sorl.thumbnail.fields import ImageField from sorl.thumbnail.shortcuts import get_thumbnail logger = logging.getLogger(__name__) class AdminImageWidget(forms.ClearableFileInput): """ An ImageField Widget for django.contrib.admin that shows a thumbnailed image as well as a link to the current one if it hase one. """ template_with_initial = ( '%(clear_template)s
' '' ) template_with_clear = '' def render(self, name, value, attrs=None, **kwargs): output = super().render(name, value, attrs, **kwargs) if value and hasattr(value, 'url'): ext = 'JPEG' try: aux_ext = str(value).split('.') if aux_ext[len(aux_ext) - 1].lower() == 'png': ext = 'PNG' elif aux_ext[len(aux_ext) - 1].lower() == 'gif': ext = 'GIF' except Exception: pass try: mini = get_thumbnail(value, 'x80', upscale=False, format=ext) except Exception as e: logger.warning("Unable to get the thumbnail", exc_info=e) else: try: output = ( '
' '' '%s
' ) % (mini.width, value.url, mini.url, output) except (AttributeError, TypeError): pass return mark_safe(output) class AdminImageMixin: """ This is a mix-in for InlineModelAdmin subclasses to make ``ImageField`` show nicer form widget """ def formfield_for_dbfield(self, db_field, request, **kwargs): if isinstance(db_field, ImageField): return db_field.formfield(widget=AdminImageWidget) return super().formfield_for_dbfield(db_field, request, **kwargs) sorl-thumbnail-12.7.0/sorl/thumbnail/base.py000066400000000000000000000176131375675401600210150ustar00rootroot00000000000000import logging import os import re from sorl.thumbnail.conf import settings, defaults as default_settings from sorl.thumbnail.helpers import tokey, serialize from sorl.thumbnail.images import ImageFile, DummyImageFile from sorl.thumbnail import default from sorl.thumbnail.parsers import parse_geometry logger = logging.getLogger(__name__) EXTENSIONS = { 'JPEG': 'jpg', 'PNG': 'png', 'GIF': 'gif', 'WEBP': 'webp', } class ThumbnailBackend: """ The main class for sorl-thumbnail, you can subclass this if you for example want to change the way destination filename is generated. """ default_options = { 'format': settings.THUMBNAIL_FORMAT, 'quality': settings.THUMBNAIL_QUALITY, 'colorspace': settings.THUMBNAIL_COLORSPACE, 'upscale': settings.THUMBNAIL_UPSCALE, 'crop': False, 'cropbox': None, 'rounded': None, 'padding': settings.THUMBNAIL_PADDING, 'padding_color': settings.THUMBNAIL_PADDING_COLOR, } extra_options = ( ('progressive', 'THUMBNAIL_PROGRESSIVE'), ('orientation', 'THUMBNAIL_ORIENTATION'), ('blur', 'THUMBNAIL_BLUR'), ) def file_extension(self, source): return os.path.splitext(source.name)[1].lower() def _get_format(self, source): file_extension = self.file_extension(source) if file_extension == '.jpg' or file_extension == '.jpeg': return 'JPEG' elif file_extension == '.png': return 'PNG' elif file_extension == '.gif': return 'GIF' elif file_extension == '.webp': return 'WEBP' else: from django.conf import settings return getattr(settings, 'THUMBNAIL_FORMAT', default_settings.THUMBNAIL_FORMAT) def get_thumbnail(self, file_, geometry_string, **options): """ Returns thumbnail as an ImageFile instance for file with geometry and options given. First it will try to get it from the key value store, secondly it will create it. """ logger.debug('Getting thumbnail for file [%s] at [%s]', file_, geometry_string) if file_: source = ImageFile(file_) else: raise ValueError('falsey file_ argument in get_thumbnail()') # preserve image filetype if settings.THUMBNAIL_PRESERVE_FORMAT: options.setdefault('format', self._get_format(source)) for key, value in self.default_options.items(): options.setdefault(key, value) # For the future I think it is better to add options only if they # differ from the default settings as below. This will ensure the same # filenames being generated for new options at default. for key, attr in self.extra_options: value = getattr(settings, attr) if value != getattr(default_settings, attr): options.setdefault(key, value) name = self._get_thumbnail_filename(source, geometry_string, options) thumbnail = ImageFile(name, default.storage) cached = default.kvstore.get(thumbnail) if cached: return cached # We have to check exists() because the Storage backend does not # overwrite in some implementations. if settings.THUMBNAIL_FORCE_OVERWRITE or not thumbnail.exists(): try: source_image = default.engine.get_image(source) except Exception as e: logger.exception(e) if settings.THUMBNAIL_DUMMY: return DummyImageFile(geometry_string) else: # if storage backend says file doesn't exist remotely, # don't try to create it and exit early. # Will return working empty image type; 404'd image logger.warning( 'Remote file [%s] at [%s] does not exist', file_, geometry_string, ) return thumbnail # We might as well set the size since we have the image in memory image_info = default.engine.get_image_info(source_image) options['image_info'] = image_info size = default.engine.get_image_size(source_image) source.set_size(size) try: self._create_thumbnail(source_image, geometry_string, options, thumbnail) self._create_alternative_resolutions(source_image, geometry_string, options, thumbnail.name) finally: default.engine.cleanup(source_image) # If the thumbnail exists we don't create it, the other option is # to delete and write but this could lead to race conditions so I # will just leave that out for now. default.kvstore.get_or_set(source) default.kvstore.set(thumbnail, source) return thumbnail def delete(self, file_, delete_file=True): """ Deletes file_ references in Key Value store and optionally the file_ it self. """ image_file = ImageFile(file_) if delete_file: image_file.delete() default.kvstore.delete(image_file) def _create_thumbnail(self, source_image, geometry_string, options, thumbnail): """ Creates the thumbnail by using default.engine """ logger.debug('Creating thumbnail file [%s] at [%s] with [%s]', thumbnail.name, geometry_string, options) ratio = default.engine.get_image_ratio(source_image, options) geometry = parse_geometry(geometry_string, ratio) image = default.engine.create(source_image, geometry, options) default.engine.write(image, options, thumbnail) # It's much cheaper to set the size here size = default.engine.get_image_size(image) thumbnail.set_size(size) def _create_alternative_resolutions(self, source_image, geometry_string, options, name): """ Creates the thumbnail by using default.engine with multiple output sizes. Appends @x to the file name. """ ratio = default.engine.get_image_ratio(source_image, options) geometry = parse_geometry(geometry_string, ratio) file_name, dot_file_ext = os.path.splitext(name) for resolution in settings.THUMBNAIL_ALTERNATIVE_RESOLUTIONS: resolution_geometry = (int(geometry[0] * resolution), int(geometry[1] * resolution)) resolution_options = options.copy() if 'crop' in options and isinstance(options['crop'], str): crop = options['crop'].split(" ") for i in range(len(crop)): s = re.match(r"(\d+)px", crop[i]) if s: crop[i] = "%spx" % int(int(s.group(1)) * resolution) resolution_options['crop'] = " ".join(crop) image = default.engine.create(source_image, resolution_geometry, options) thumbnail_name = '%(file_name)s%(suffix)s%(file_ext)s' % { 'file_name': file_name, 'suffix': '@%sx' % resolution, 'file_ext': dot_file_ext } thumbnail = ImageFile(thumbnail_name, default.storage) default.engine.write(image, resolution_options, thumbnail) size = default.engine.get_image_size(image) thumbnail.set_size(size) def _get_thumbnail_filename(self, source, geometry_string, options): """ Computes the destination filename. """ key = tokey(source.key, geometry_string, serialize(options)) # make some subdirs path = '%s/%s/%s' % (key[:2], key[2:4], key) return '%s%s.%s' % (settings.THUMBNAIL_PREFIX, path, EXTENSIONS[options['format']]) sorl-thumbnail-12.7.0/sorl/thumbnail/conf/000077500000000000000000000000001375675401600204465ustar00rootroot00000000000000sorl-thumbnail-12.7.0/sorl/thumbnail/conf/__init__.py000066400000000000000000000007521375675401600225630ustar00rootroot00000000000000from django.conf import settings as user_settings from sorl.thumbnail.conf import defaults class Settings: """ Settings proxy that will lookup first in the django settings, and then in the conf defaults. """ def __getattr__(self, name): if name != name.upper(): raise AttributeError(name) try: return getattr(user_settings, name) except AttributeError: return getattr(defaults, name) settings = Settings() sorl-thumbnail-12.7.0/sorl/thumbnail/conf/defaults.py000066400000000000000000000100401375675401600226220ustar00rootroot00000000000000from django.conf import settings # When True ThumbnailNode.render can raise errors THUMBNAIL_DEBUG = False # Backend THUMBNAIL_BACKEND = 'sorl.thumbnail.base.ThumbnailBackend' # Key-value store, ships with: # sorl.thumbnail.kvstores.cached_db_kvstore.KVStore # sorl.thumbnail.kvstores.redis_kvstore.KVStore # Redis requires some more work, see docs THUMBNAIL_KVSTORE = 'sorl.thumbnail.kvstores.cached_db_kvstore.KVStore' # Change this to something else for MSSQL THUMBNAIL_KEY_DBCOLUMN = 'key' # Engine, ships with: # sorl.thumbnail.engines.convert_engine.Engine # sorl.thumbnail.engines.pil_engine.Engine # sorl.thumbnail.engines.pgmagick_engine.Engine # convert is preferred but requires imagemagick or graphicsmagick, se docs THUMBNAIL_ENGINE = 'sorl.thumbnail.engines.pil_engine.Engine' # Path to Imagemagick or Graphicsmagick ``convert`` and ``identify``. THUMBNAIL_CONVERT = 'convert' THUMBNAIL_IDENTIFY = 'identify' # Path to ``vipsthumbnail`` and ``vipsheader`` THUMBNAIL_VIPSTHUMBNAIL = 'vipsthumbnail' THUMBNAIL_VIPSHEADER = 'vipsheader' # Storage for the generated thumbnails THUMBNAIL_STORAGE = settings.DEFAULT_FILE_STORAGE # Redis settings THUMBNAIL_REDIS_DB = 0 THUMBNAIL_REDIS_PASSWORD = '' THUMBNAIL_REDIS_HOST = 'localhost' THUMBNAIL_REDIS_PORT = 6379 THUMBNAIL_REDIS_UNIX_SOCKET_PATH = None THUMBNAIL_REDIS_SSL = False THUMBNAIL_REDIS_TIMEOUT = 3600 * 24 * 365 * 10 # 10 years # DBM settings THUMBNAIL_DBM_FILE = "thumbnail_kvstore" THUMBNAIL_DBM_MODE = 0o644 # Cache timeout for ``cached_db`` store. You should probably keep this at # maximum or ``0`` if your caching backend can handle that as infinite. THUMBNAIL_CACHE_TIMEOUT = 3600 * 24 * 365 * 10 # 10 years # The cache configuration to use for storing thumbnail data THUMBNAIL_CACHE = 'default' # Key prefix used by the key value store THUMBNAIL_KEY_PREFIX = 'sorl-thumbnail' # Thumbnail filename prefix THUMBNAIL_PREFIX = 'cache/' # Image format, common formats are: JPEG, PNG, GIF # Make sure the backend can handle the format you specify THUMBNAIL_FORMAT = 'JPEG' THUMBNAIL_PRESERVE_FORMAT = False # Colorspace, backends are required to implement: RGB, GRAY # Setting this to None will keep the original colorspace. THUMBNAIL_COLORSPACE = 'RGB' # Should we upscale images by default THUMBNAIL_UPSCALE = True # Quality, 0-100 THUMBNAIL_QUALITY = 95 # Gaussian blur radius THUMBNAIL_BLUR = 0 # Adds padding around the image to match the requested size without cropping THUMBNAIL_PADDING = False THUMBNAIL_PADDING_COLOR = '#ffffff' # Save as progressive when saving as jpeg THUMBNAIL_PROGRESSIVE = True # Orientate the thumbnail with respect to source EXIF orientation tag THUMBNAIL_ORIENTATION = True # This means sorl.thumbnail will generate and serve a generated dummy image # regardless of the thumbnail source content THUMBNAIL_DUMMY = False # Thumbnail dummy (placeholder) source. Some you might try are: # http://placekitten.com/%(width)s/%(height)s # http://placekitten.com/g/%(width)s/%(height)s # http://placehold.it/%(width)sx%(height)s THUMBNAIL_DUMMY_SOURCE = 'http://dummyimage.com/%(width)sx%(height)s' # Sets the source image ratio for dummy generation of images with only width # or height given THUMBNAIL_DUMMY_RATIO = 1.5 # Enables creation of multiple-resolution (aka "Retina") images. # We don't create retina images by default to optimize performance. THUMBNAIL_ALTERNATIVE_RESOLUTIONS = [] # Lazy fill empty thumbnail like THUMBNAIL_DUMMY THUMBNAIL_LAZY_FILL_EMPTY = False # Timeout, in seconds, to use when retrieving images with urllib2 THUMBNAIL_URL_TIMEOUT = None # Default width when using filters for texts THUMBNAIL_FILTER_WIDTH = 500 # Should we flatten images by default (fixes a lot of transparency issues with # imagemagick) THUMBNAIL_FLATTEN = False # Whenever we will check an existing thumbnail exists and avoid to overwrite or not. # Set this to true if you have an slow .exists() implementation on your storage backend of choice. THUMBNAIL_FORCE_OVERWRITE = False # Should we remove GET arguments from URLs? (suggested for Amazon S3 image urls) THUMBNAIL_REMOVE_URL_ARGS = True sorl-thumbnail-12.7.0/sorl/thumbnail/default.py000066400000000000000000000013031375675401600215140ustar00rootroot00000000000000from django.utils.functional import LazyObject from sorl.thumbnail.conf import settings from sorl.thumbnail.helpers import get_module_class class Backend(LazyObject): def _setup(self): self._wrapped = get_module_class(settings.THUMBNAIL_BACKEND)() class KVStore(LazyObject): def _setup(self): self._wrapped = get_module_class(settings.THUMBNAIL_KVSTORE)() class Engine(LazyObject): def _setup(self): self._wrapped = get_module_class(settings.THUMBNAIL_ENGINE)() class Storage(LazyObject): def _setup(self): self._wrapped = get_module_class(settings.THUMBNAIL_STORAGE)() backend = Backend() kvstore = KVStore() engine = Engine() storage = Storage() sorl-thumbnail-12.7.0/sorl/thumbnail/engines/000077500000000000000000000000001375675401600211515ustar00rootroot00000000000000sorl-thumbnail-12.7.0/sorl/thumbnail/engines/__init__.py000066400000000000000000000000001375675401600232500ustar00rootroot00000000000000sorl-thumbnail-12.7.0/sorl/thumbnail/engines/base.py000066400000000000000000000200351375675401600224350ustar00rootroot00000000000000from sorl.thumbnail.conf import settings from sorl.thumbnail.helpers import toint from sorl.thumbnail.parsers import parse_crop from sorl.thumbnail.parsers import parse_cropbox class EngineBase: """ ABC for Thumbnail engines, methods are static """ def create(self, image, geometry, options): """ Processing conductor, returns the thumbnail as an image engine instance """ image = self.cropbox(image, geometry, options) image = self.orientation(image, geometry, options) image = self.colorspace(image, geometry, options) image = self.remove_border(image, options) image = self.scale(image, geometry, options) image = self.crop(image, geometry, options) image = self.rounded(image, geometry, options) image = self.blur(image, geometry, options) image = self.padding(image, geometry, options) return image def cropbox(self, image, geometry, options): """ Wrapper for ``_cropbox`` """ cropbox = options['cropbox'] if not cropbox: return image x, y, x2, y2 = parse_cropbox(cropbox) return self._cropbox(image, x, y, x2, y2) def orientation(self, image, geometry, options): """ Wrapper for ``_orientation`` """ if options.get('orientation', settings.THUMBNAIL_ORIENTATION): return self._orientation(image) self.reoriented = True return image def flip_dimensions(self, image, geometry=None, options=None): options = options or {} reoriented = hasattr(self, 'reoriented') if options.get('orientation', settings.THUMBNAIL_ORIENTATION) and not reoriented: return self._flip_dimensions(image) return False def colorspace(self, image, geometry, options): """ Wrapper for ``_colorspace`` """ colorspace = options['colorspace'] return self._colorspace(image, colorspace) def remove_border(self, image, options): if options.get('remove_border', False): x_image, y_image = self.get_image_size(image) image = self._remove_border(image, x_image, y_image) return image def _calculate_scaling_factor(self, x_image, y_image, geometry, options): crop = options['crop'] factors = (geometry[0] / x_image, geometry[1] / y_image) return max(factors) if crop else min(factors) def scale(self, image, geometry, options): """ Wrapper for ``_scale`` """ upscale = options['upscale'] x_image, y_image = map(float, self.get_image_size(image)) if self.flip_dimensions(image): x_image, y_image = y_image, x_image factor = self._calculate_scaling_factor(x_image, y_image, geometry, options) if factor < 1 or upscale: width = toint(x_image * factor) height = toint(y_image * factor) image = self._scale(image, width, height) return image def crop(self, image, geometry, options): """ Wrapper for ``_crop`` """ crop = options['crop'] x_image, y_image = self.get_image_size(image) if not crop or crop == 'noop': return image elif crop == 'smart': # Smart cropping is suitably different from regular cropping # to warrent it's own function return self._entropy_crop(image, geometry[0], geometry[1], x_image, y_image) # Handle any other crop option with the backend crop function. geometry = (min(x_image, geometry[0]), min(y_image, geometry[1])) x_offset, y_offset = parse_crop(crop, (x_image, y_image), geometry) return self._crop(image, geometry[0], geometry[1], x_offset, y_offset) def rounded(self, image, geometry, options): """ Wrapper for ``_rounded`` """ r = options['rounded'] if not r: return image return self._rounded(image, int(r)) def blur(self, image, geometry, options): """ Wrapper for ``_blur`` """ if options.get('blur'): return self._blur(image, int(options.get('blur'))) return image def padding(self, image, geometry, options): """ Wrapper for ``_padding`` """ if options.get('padding') and self.get_image_size(image) != geometry: return self._padding(image, geometry, options) return image def write(self, image, options, thumbnail): """ Wrapper for ``_write`` """ format_ = options['format'] quality = options['quality'] image_info = options.get('image_info', {}) # additional non-default-value options: progressive = options.get('progressive', settings.THUMBNAIL_PROGRESSIVE) raw_data = self._get_raw_data( image, format_, quality, image_info=image_info, progressive=progressive ) thumbnail.write(raw_data) def cleanup(self, image): """Some backends need to manually cleanup after thumbnails are created""" pass def get_image_ratio(self, image, options): """ Calculates the image ratio. If cropbox option is used, the ratio may have changed. """ cropbox = options['cropbox'] if cropbox: x, y, x2, y2 = parse_cropbox(cropbox) x = x2 - x y = y2 - y else: x, y = self.get_image_size(image) ratio = float(x) / y if self.flip_dimensions(image): ratio = 1.0 / ratio return ratio def get_image_info(self, image): """ Returns metadata of an ImageFile instance """ return {} # Methods which engines need to implement # The ``image`` argument refers to a backend image object def get_image(self, source): """ Returns the backend image objects from an ImageFile instance """ raise NotImplementedError() def get_image_size(self, image): """ Returns the image width and height as a tuple """ raise NotImplementedError() def is_valid_image(self, raw_data): """ Checks if the supplied raw data is valid image data """ raise NotImplementedError() def _orientation(self, image): """ Read orientation exif data and orientate the image accordingly """ return image def _colorspace(self, image, colorspace): """ `Valid colorspaces `_. Backends need to implement the following:: RGB, GRAY """ raise NotImplementedError() def _remove_border(self, image, image_width, image_height): """ Remove borders around images """ raise NotImplementedError() def _entropy_crop(self, image, geometry_width, geometry_height, image_width, image_height): """ Crop the image to the correct aspect ratio by removing the lowest entropy parts """ raise NotImplementedError() def _scale(self, image, width, height): """ Does the resizing of the image """ raise NotImplementedError() def _crop(self, image, width, height, x_offset, y_offset): """ Crops the image """ raise NotImplementedError() def _get_raw_data(self, image, format_, quality, image_info=None, progressive=False): """ Gets raw data given the image, format and quality. This method is called from :meth:`write` """ raise NotImplementedError() def _padding(self, image, geometry, options): """ Pads the image """ raise NotImplementedError() def _cropbox(self, image, x, y, x2, y2): raise NotImplementedError() def _rounded(self, image, r): raise NotImplementedError() def _blur(self, image, radius): raise NotImplementedError() sorl-thumbnail-12.7.0/sorl/thumbnail/engines/convert_engine.py000066400000000000000000000152451375675401600245370ustar00rootroot00000000000000import re import os import subprocess import logging from collections import OrderedDict from django.utils.encoding import smart_str from django.core.files.temp import NamedTemporaryFile from sorl.thumbnail.base import EXTENSIONS from sorl.thumbnail.conf import settings from sorl.thumbnail.engines.base import EngineBase logger = logging.getLogger(__name__) size_re = re.compile(r'^(?:.+) (?:[A-Z]+) (?P\d+)x(?P\d+)') class Engine(EngineBase): """ Image object is a dict with source path, options and size """ def write(self, image, options, thumbnail): """ Writes the thumbnail image """ if options['format'] == 'JPEG' and options.get( 'progressive', settings.THUMBNAIL_PROGRESSIVE): image['options']['interlace'] = 'line' image['options']['quality'] = options['quality'] args = settings.THUMBNAIL_CONVERT.split(' ') args.append(image['source'] + '[0]') for k in image['options']: v = image['options'][k] args.append('-%s' % k) if v is not None: args.append('%s' % v) flatten = "on" if 'flatten' in options: flatten = options['flatten'] if settings.THUMBNAIL_FLATTEN and not flatten == "off": args.append('-flatten') suffix = '.%s' % EXTENSIONS[options['format']] with NamedTemporaryFile(suffix=suffix, mode='rb') as fp: args.append(fp.name) args = map(smart_str, args) p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) returncode = p.wait() out, err = p.communicate() if returncode: raise EngineError( "The command %r exited with a non-zero exit code and printed this to stderr: %s" % (args, err) ) elif err: logger.error("Captured stderr: %s", err) thumbnail.write(fp.read()) def cleanup(self, image): os.remove(image['source']) # we should not need this now def get_image(self, source): """ Returns the backend image objects from a ImageFile instance """ with NamedTemporaryFile(mode='wb', delete=False) as fp: fp.write(source.read()) return {'source': fp.name, 'options': OrderedDict(), 'size': None} def get_image_size(self, image): """ Returns the image width and height as a tuple """ if image['size'] is None: args = settings.THUMBNAIL_IDENTIFY.split(' ') args.append(image['source'] + '[0]') p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p.wait() m = size_re.match(str(p.stdout.read())) image['size'] = int(m.group('x')), int(m.group('y')) return image['size'] def is_valid_image(self, raw_data): """ This is not very good for imagemagick because it will say anything is valid that it can use as input. """ with NamedTemporaryFile(mode='wb') as fp: fp.write(raw_data) fp.flush() args = settings.THUMBNAIL_IDENTIFY.split(' ') args.append(fp.name + '[0]') p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) retcode = p.wait() return retcode == 0 def _get_exif_orientation(self, image): args = settings.THUMBNAIL_IDENTIFY.split() args.extend(['-format', '%[exif:orientation]', image['source']]) p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p.wait() result = p.stdout.read().strip() try: return int(result) except ValueError: return None def _orientation(self, image): # return image # XXX need to get the dimensions right after a transpose. if settings.THUMBNAIL_CONVERT.endswith('gm convert'): orientation = self._get_exif_orientation(image) if orientation: options = image['options'] if orientation == 2: options['flop'] = None elif orientation == 3: options['rotate'] = '180' elif orientation == 4: options['flip'] = None elif orientation == 5: options['rotate'] = '90' options['flop'] = None elif orientation == 6: options['rotate'] = '90' elif orientation == 7: options['rotate'] = '-90' options['flop'] = None elif orientation == 8: options['rotate'] = '-90' else: # ImageMagick also corrects the orientation exif data for # destination image['options']['auto-orient'] = None return image def _flip_dimensions(self, image): orientation = self._get_exif_orientation(image) return orientation and orientation in [5, 6, 7, 8] def _colorspace(self, image, colorspace): """ `Valid colorspaces `_. Backends need to implement the following:: RGB, GRAY """ image['options']['colorspace'] = colorspace return image def _crop(self, image, width, height, x_offset, y_offset): """ Crops the image """ image['options']['crop'] = '%sx%s+%s+%s' % (width, height, x_offset, y_offset) image['size'] = (width, height) # update image size return image def _cropbox(self, image, x, y, x2, y2): """ Crops the image to a set of x,y coordinates (x,y) is top left, (x2,y2) is bottom left """ image['options']['crop'] = '%sx%s+%s+%s' % (x2 - x, y2 - y, x, y) image['size'] = (x2 - x, y2 - y) # update image size return image def _scale(self, image, width, height): """ Does the resizing of the image """ image['options']['scale'] = '%sx%s!' % (width, height) image['size'] = (width, height) # update image size return image def _padding(self, image, geometry, options): """ Pads the image """ # The order is important. The gravity option should come before extent. image['options']['background'] = options.get('padding_color') image['options']['gravity'] = 'center' image['options']['extent'] = '%sx%s' % (geometry[0], geometry[1]) return image class EngineError(Exception): pass sorl-thumbnail-12.7.0/sorl/thumbnail/engines/pgmagick_engine.py000066400000000000000000000057001375675401600246340ustar00rootroot00000000000000from pgmagick import Blob, Geometry, Image, ImageType from pgmagick import InterlaceType, OrientationType from sorl.thumbnail.engines.base import EngineBase try: from pgmagick._pgmagick import get_blob_data except ImportError: from base64 import b64decode def get_blob_data(blob): return b64decode(blob.base64()) class Engine(EngineBase): def get_image(self, source): blob = Blob() blob.update(source.read()) return Image(blob) def get_image_size(self, image): geometry = image.size() return geometry.width(), geometry.height() def is_valid_image(self, raw_data): blob = Blob() blob.update(raw_data) im = Image(blob) return im.isValid() def _cropbox(self, image, x, y, x2, y2): geometry = Geometry(x2 - x, y2 - y, x, y) image.crop(geometry) return image def _orientation(self, image): orientation = image.orientation() if orientation == OrientationType.TopRightOrientation: image.flop() elif orientation == OrientationType.BottomRightOrientation: image.rotate(180) elif orientation == OrientationType.BottomLeftOrientation: image.flip() elif orientation == OrientationType.LeftTopOrientation: image.rotate(90) image.flop() elif orientation == OrientationType.RightTopOrientation: image.rotate(90) elif orientation == OrientationType.RightBottomOrientation: image.rotate(-90) image.flop() elif orientation == OrientationType.LeftBottomOrientation: image.rotate(-90) image.orientation(OrientationType.TopLeftOrientation) return image def _flip_dimensions(self, image): return image.orientation() in [ OrientationType.LeftTopOrientation, OrientationType.RightTopOrientation, OrientationType.RightBottomOrientation, OrientationType.LeftBottomOrientation, ] def _colorspace(self, image, colorspace): if colorspace == 'RGB': image.type(ImageType.TrueColorMatteType) elif colorspace == 'GRAY': image.type(ImageType.GrayscaleMatteType) else: return image return image def _scale(self, image, width, height): geometry = Geometry(width, height) image.scale(geometry) return image def _crop(self, image, width, height, x_offset, y_offset): geometry = Geometry(width, height, x_offset, y_offset) image.crop(geometry) return image def _get_raw_data(self, image, format_, quality, image_info=None, progressive=False): image.magick(format_.encode('utf8')) image.quality(quality) if format_ == 'JPEG' and progressive: image.interlaceType(InterlaceType.LineInterlace) blob = Blob() image.write(blob) return get_blob_data(blob) sorl-thumbnail-12.7.0/sorl/thumbnail/engines/pil_engine.py000066400000000000000000000237641375675401600236500ustar00rootroot00000000000000from io import BytesIO from sorl.thumbnail.engines.base import EngineBase try: from PIL import Image, ImageFile, ImageDraw, ImageFilter, ImageMode except ImportError: import Image import ImageFile import ImageDraw import ImageMode EXIF_ORIENTATION = 0x0112 def color_count(image): """ Return the number of color values in the input image -- this is the number of pixels times the band count of the image. """ mode_descriptor = ImageMode.getmode(image.mode) width, height = image.size return width * height * len(mode_descriptor.bands) def histogram_entropy_py(image): """ Calculate the entropy of an images' histogram. """ from math import log2, fsum histosum = float(color_count(image)) histonorm = (histocol / histosum for histocol in image.histogram()) return -fsum(p * log2(p) for p in histonorm if p != 0.0) # Select the Pillow native histogram entropy function - if # available - and fall back to the Python implementation: histogram_entropy = getattr(Image.Image, 'entropy', histogram_entropy_py) def round_corner(radius, fill): """Draw a round corner""" corner = Image.new('L', (radius, radius), 0) # (0, 0, 0, 0)) draw = ImageDraw.Draw(corner) draw.pieslice((0, 0, radius * 2, radius * 2), 180, 270, fill=fill) return corner def round_rectangle(size, radius, fill): """Draw a rounded rectangle""" width, height = size rectangle = Image.new('L', size, 255) # fill corner = round_corner(radius, 255) # fill rectangle.paste(corner, (0, 0)) rectangle.paste(corner.rotate(90), (0, height - radius)) # Rotate the corner and paste it rectangle.paste(corner.rotate(180), (width - radius, height - radius)) rectangle.paste(corner.rotate(270), (width - radius, 0)) return rectangle class GaussianBlur(ImageFilter.Filter): name = "GaussianBlur" def __init__(self, radius=2): self.radius = radius def filter(self, image): return image.gaussian_blur(self.radius) class Engine(EngineBase): def get_image(self, source): buffer = BytesIO(source.read()) return Image.open(buffer) def get_image_size(self, image): return image.size def get_image_info(self, image): return image.info or {} def is_valid_image(self, raw_data): buffer = BytesIO(raw_data) try: trial_image = Image.open(buffer) trial_image.verify() except Exception: return False return True def colorspace(self, image, geometry, options): """ Wrapper for ``_colorspace`` """ colorspace = options['colorspace'] format = options['format'] return self._colorspace(image, colorspace, format) def _cropbox(self, image, x, y, x2, y2): return image.crop((x, y, x2, y2)) def _get_exif_orientation(self, image): try: exif = image._getexif() except Exception: exif = None if exif: return exif.get(EXIF_ORIENTATION) else: return None def _orientation(self, image): orientation = self._get_exif_orientation(image) if orientation: if orientation == 2: image = image.transpose(Image.FLIP_LEFT_RIGHT) elif orientation == 3: image = image.rotate(180) elif orientation == 4: image = image.transpose(Image.FLIP_TOP_BOTTOM) elif orientation == 5: image = image.rotate(-90, expand=1).transpose(Image.FLIP_LEFT_RIGHT) elif orientation == 6: image = image.rotate(-90, expand=1) elif orientation == 7: image = image.rotate(90, expand=1).transpose(Image.FLIP_LEFT_RIGHT) elif orientation == 8: image = image.rotate(90, expand=1) return image def _flip_dimensions(self, image): orientation = self._get_exif_orientation(image) return orientation and orientation in [5, 6, 7, 8] def _colorspace(self, image, colorspace, format): if colorspace == 'RGB': # Pillow JPEG doesn't allow RGBA anymore. It was converted to RGB before. if image.mode == 'RGBA' and format != 'JPEG': return image # RGBA is just RGB + Alpha if image.mode == 'LA' or ( image.mode == 'P' and 'transparency' in image.info and format != 'JPEG' ): newimage = image.convert('RGBA') transparency = image.info.get('transparency') if transparency is not None: mask = image.convert('RGBA').split()[-1] newimage.putalpha(mask) return newimage return image.convert('RGB') if colorspace == 'GRAY': return image.convert('L') return image def _remove_border(self, image, image_width, image_height): borders = { 'top': lambda iy, dy, y: (dy, dy + y), 'right': lambda ix, dx, x: (ix - dx - x, ix - dx), 'bottom': lambda iy, dy, y: (iy - dy - y, iy - dy), 'left': lambda ix, dx, x: (dx, dx + x), } offset = {'top': 0, 'right': 0, 'bottom': 0, 'left': 0, } for border in ['top', 'bottom']: # Don't remove too much, the image may just be plain while offset[border] < image_height / 3.5: slice_size = min(image_width / 20, 10) y_range = borders[border](image_height, offset[border], slice_size) section = image.crop((0, y_range[0], image_width, y_range[1])) # If this section is below the threshold; remove it if self._get_image_entropy(section) < 2.0: offset[border] += slice_size else: break for border in ['left', 'right']: while offset[border] < image_width / 3.5: slice_size = min(image_height / 20, 10) x_range = borders[border](image_width, offset[border], slice_size) section = image.crop((x_range[0], 0, x_range[1], image_height)) if self._get_image_entropy(section) < 2.0: offset[border] += slice_size else: break return image.crop((offset['left'], offset['top'], image_width - offset['right'], image_height - offset['bottom'])) # Credit to chrisopherhan https://github.com/christopherhan/pycrop # This is just a slight rework of pycrops implimentation def _entropy_crop(self, image, geometry_width, geometry_height, image_width, image_height): geometry_ratio = geometry_width / geometry_height # The is proportionally wider than it should be while image_width / image_height > geometry_ratio: slice_width = max(image_width - geometry_width, 10) right = image.crop((image_width - slice_width, 0, image_width, image_height)) left = image.crop((0, 0, slice_width, image_height)) if self._get_image_entropy(left) < self._get_image_entropy(right): image = image.crop((slice_width, 0, image_width, image_height)) else: image = image.crop((0, 0, image_height - slice_width, image_height)) image_width -= slice_width # The image is proportionally taller than it should be while image_width / image_height < geometry_ratio: slice_height = min(image_height - geometry_height, 10) bottom = image.crop((0, image_height - slice_height, image_width, image_height)) top = image.crop((0, 0, image_width, slice_height)) if self._get_image_entropy(bottom) < self._get_image_entropy(top): image = image.crop((0, 0, image_width, image_height - slice_height)) else: image = image.crop((0, slice_height, image_width, image_height)) image_height -= slice_height return image # Add the histogram_entropy fumnction as a static method: _get_image_entropy = staticmethod(histogram_entropy) def _scale(self, image, width, height): return image.resize((width, height), resample=Image.ANTIALIAS) def _crop(self, image, width, height, x_offset, y_offset): return image.crop((x_offset, y_offset, width + x_offset, height + y_offset)) def _rounded(self, image, r): i = round_rectangle(image.size, r, "notusedblack") image.putalpha(i) return image def _blur(self, image, radius): return image.filter(GaussianBlur(radius)) def _padding(self, image, geometry, options): x_image, y_image = self.get_image_size(image) left = int((geometry[0] - x_image) / 2) top = int((geometry[1] - y_image) / 2) color = options.get('padding_color') im = Image.new(image.mode, geometry, color) im.paste(image, (left, top)) return im def _get_raw_data(self, image, format_, quality, image_info=None, progressive=False): # Increase (but never decrease) PIL buffer size ImageFile.MAXBLOCK = max(ImageFile.MAXBLOCK, image.size[0] * image.size[1]) bf = BytesIO() params = { 'format': format_, 'quality': quality, 'optimize': 1, } # keeps icc_profile if 'icc_profile' in image_info: params['icc_profile'] = image_info['icc_profile'] raw_data = None if format_ == 'JPEG' and progressive: params['progressive'] = True try: # Do not save unnecessary exif data for smaller thumbnail size params.pop('exif', {}) image.save(bf, **params) except OSError: # Try without optimization. params.pop('optimize') image.save(bf, **params) else: raw_data = bf.getvalue() finally: bf.close() return raw_data sorl-thumbnail-12.7.0/sorl/thumbnail/engines/vipsthumbnail_engine.py000066400000000000000000000074761375675401600257530ustar00rootroot00000000000000import re import os import subprocess from collections import OrderedDict from django.utils.encoding import smart_str from django.core.files.temp import NamedTemporaryFile from sorl.thumbnail.base import EXTENSIONS from sorl.thumbnail.conf import settings from sorl.thumbnail.engines.base import EngineBase size_re = re.compile(r'^(?:.+) (?P\d+)x(?P\d+)') class Engine(EngineBase): """ Image object is a dict with source path, options and size """ def write(self, image, options, thumbnail): """ Writes the thumbnail image """ args = settings.THUMBNAIL_VIPSTHUMBNAIL.split(' ') args.append(image['source']) for k in image['options']: v = image['options'][k] args.append('--%s' % k) if v is not None: args.append('%s' % v) suffix = '.%s' % EXTENSIONS[options['format']] write_options = [] if options['format'] == 'JPEG' and options.get( 'progressive', settings.THUMBNAIL_PROGRESSIVE): write_options.append("interlace") if options['quality']: if options['format'] == 'JPEG': write_options.append("Q=%d" % options['quality']) with NamedTemporaryFile(suffix=suffix, mode='rb') as fp: # older vipsthumbails used -o, this was renamed to -f in 8.0, use # -o here for commpatibility args.append("-o") args.append(fp.name + "[%s]" % ",".join(write_options)) args = map(smart_str, args) p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p.wait() out, err = p.communicate() if err: raise Exception(err) thumbnail.write(fp.read()) def cleanup(self, image): os.remove(image['source']) # we should not need this now def get_image(self, source): """ Returns the backend image objects from a ImageFile instance """ with NamedTemporaryFile(mode='wb', delete=False) as fp: fp.write(source.read()) return {'source': fp.name, 'options': OrderedDict(), 'size': None} def get_image_size(self, image): """ Returns the image width and height as a tuple """ if image['size'] is None: args = settings.THUMBNAIL_VIPSHEADER.split(' ') args.append(image['source']) p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p.wait() m = size_re.match(str(p.stdout.read())) image['size'] = int(m.group('x')), int(m.group('y')) return image['size'] def is_valid_image(self, raw_data): """ vipsheader will try a lot of formats, including all those supported by imagemagick if compiled with magick support, this can take a while """ with NamedTemporaryFile(mode='wb') as fp: fp.write(raw_data) fp.flush() args = settings.THUMBNAIL_VIPSHEADER.split(' ') args.append(fp.name) p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) retcode = p.wait() return retcode == 0 def _orientation(self, image): # vipsthumbnail also corrects the orientation exif data for # destination image['options']['rotate'] = None return image def _colorspace(self, image, colorspace): """ vipsthumbnail does not support greyscaling of images, but pretend it does """ return image def _scale(self, image, width, height): """ Does the resizing of the image """ image['options']['size'] = '%sx%s' % (width, height) image['size'] = (width, height) # update image size return image sorl-thumbnail-12.7.0/sorl/thumbnail/engines/wand_engine.py000066400000000000000000000056501375675401600240070ustar00rootroot00000000000000''' Wand (>=v0.3.0) engine for Sorl-thumbnail ''' from wand.image import Image from wand.version import MAGICK_VERSION_NUMBER from wand import exceptions from sorl.thumbnail.engines.base import EngineBase class Engine(EngineBase): def get_image(self, source): return Image(blob=source.read()) def get_image_size(self, image): return image.size def is_valid_image(self, raw_data): ''' Wand library makes sure when opening any image that is fine, when the image is corrupted raises an exception. ''' try: Image(blob=raw_data) return True except (exceptions.CorruptImageError, exceptions.MissingDelegateError): return False def _orientation(self, image): orientation = image.orientation if orientation == 'top_right': image.flop() elif orientation == 'bottom_right': image.rotate(degree=180) elif orientation == 'bottom_left': image.flip() elif orientation == 'left_top': image.rotate(degree=90) image.flop() elif orientation == 'right_top': image.rotate(degree=90) elif orientation == 'right_bottom': image.rotate(degree=-90) image.flop() elif orientation == 'left_bottom': image.rotate(degree=-90) image.orientation = 'top_left' return image def _flip_dimensions(self, image): return image.orientation in ['left_top', 'right_top', 'right_bottom', 'left_bottom'] def _colorspace(self, image, colorspace): if colorspace == 'RGB': if image.alpha_channel: if MAGICK_VERSION_NUMBER < 0x700: image.type = 'truecolormatte' else: image.type = 'truecoloralpha' else: image.type = 'truecolor' elif colorspace == 'GRAY': if image.alpha_channel: if MAGICK_VERSION_NUMBER < 0x700: image.type = 'grayscalematte' else: image.type = 'grayscalealpha' else: image.type = 'grayscale' else: return image return image def _scale(self, image, width, height): image.resize(width, height) return image def _crop(self, image, width, height, x_offset, y_offset): image.crop(left=x_offset, top=y_offset, width=width, height=height) return image def _cropbox(self, image, x, y, x2, y2): image.crop(left=x, top=y, width=x2 - x, height=y2 - y) return image def _get_raw_data(self, image, format_, quality, image_info=None, progressive=False): image.compression_quality = quality if format_ == 'JPEG' and progressive: image.format = 'pjpeg' else: image.format = format_ return image.make_blob() sorl-thumbnail-12.7.0/sorl/thumbnail/fields.py000066400000000000000000000045341375675401600213470ustar00rootroot00000000000000from django.db import models from django.db.models import Q from django import forms from django.utils.translation import gettext_lazy as _ from sorl.thumbnail import default __all__ = ('ImageField', 'ImageFormField') class ImageField(models.ImageField): def delete_file(self, instance, sender, **kwargs): """ Adds deletion of thumbnails and key value store references to the parent class implementation. Only called in Django < 1.2.5 """ file_ = getattr(instance, self.attname) # If no other object of this type references the file, and it's not the # default value for future objects, delete it from the backend. query = Q(**{self.name: file_.name}) & ~Q(pk=instance.pk) qs = sender._default_manager.filter(query) if (file_ and file_.name != self.default and not qs): default.backend.delete(file_) elif file_: # Otherwise, just close the file, so it doesn't tie up resources. file_.close() def formfield(self, **kwargs): defaults = {'form_class': ImageFormField} defaults.update(kwargs) return super().formfield(**defaults) def save_form_data(self, instance, data): if data is not None: setattr(instance, self.name, data or '') class ImageFormField(forms.FileField): default_error_messages = { 'invalid_image': _("Upload a valid image. The file you uploaded was " "either not an image or a corrupted image."), } def to_python(self, data): """ Checks that the file-upload field data contains a valid image (GIF, JPG, PNG, possibly others -- whatever the engine supports). """ f = super().to_python(data) if f is None: return None # We need to get a file raw data to validate it. if hasattr(data, 'temporary_file_path'): with open(data.temporary_file_path(), 'rb') as fp: raw_data = fp.read() elif hasattr(data, 'read'): raw_data = data.read() else: raw_data = data['content'] if not default.engine.is_valid_image(raw_data): raise forms.ValidationError(self.default_error_messages['invalid_image']) if hasattr(f, 'seek') and callable(f.seek): f.seek(0) return f sorl-thumbnail-12.7.0/sorl/thumbnail/helpers.py000066400000000000000000000032321375675401600215350ustar00rootroot00000000000000import hashlib import json import math from importlib import import_module from django.core.exceptions import ImproperlyConfigured from django.utils.encoding import force_str class ThumbnailError(Exception): pass class SortedJSONEncoder(json.JSONEncoder): """ A json encoder that sorts the dict keys """ def __init__(self, **kwargs): kwargs['sort_keys'] = True super().__init__(**kwargs) def toint(number): """ Helper to return rounded int for a float or just the int it self. """ if isinstance(number, float): if number > 1: number = round(number, 0) else: # The following solves when image has small dimensions (like 1x54) # then scale factor 1 * 0.296296 and `number` will store `0` # that will later raise ZeroDivisionError. number = round(math.ceil(number), 0) return int(number) def tokey(*args): """ Computes a unique key from arguments given. """ salt = '||'.join([force_str(arg) for arg in args]) return hashlib.md5(salt.encode()).hexdigest() def serialize(obj): return json.dumps(obj, cls=SortedJSONEncoder) def deserialize(s): if isinstance(s, bytes): return json.loads(s.decode('utf-8')) return json.loads(s) def get_module_class(class_path): """ imports and returns module class from ``path.to.module.Class`` argument """ mod_name, cls_name = class_path.rsplit('.', 1) try: mod = import_module(mod_name) except ImportError as e: raise ImproperlyConfigured(('Error importing module %s: "%s"' % (mod_name, e))) return getattr(mod, cls_name) sorl-thumbnail-12.7.0/sorl/thumbnail/images.py000066400000000000000000000163641375675401600213520ustar00rootroot00000000000000import json import os import platform import re from urllib.error import URLError from urllib.parse import quote, quote_plus, urlsplit, urlunsplit from urllib.request import urlopen, Request from django.core.files.base import File, ContentFile from django.core.files.storage import Storage # , default_storage from django.utils.encoding import force_str from django.utils.functional import LazyObject, empty from sorl.thumbnail import default from sorl.thumbnail.conf import settings from sorl.thumbnail.default import storage as default_storage from sorl.thumbnail.helpers import ThumbnailError, tokey, get_module_class, deserialize from sorl.thumbnail.parsers import parse_geometry url_pat = re.compile(r'^(https?|ftp):\/\/') def serialize_image_file(image_file): if image_file.size is None: raise ThumbnailError('Trying to serialize an ``ImageFile`` with a ' '``None`` size.') data = { 'name': image_file.name, 'storage': image_file.serialize_storage(), 'size': image_file.size, } return json.dumps(data) def deserialize_image_file(s): data = deserialize(s) class LazyStorage(LazyObject): def _setup(self): self._wrapped = get_module_class(data['storage'])() image_file = ImageFile(data['name'], LazyStorage()) image_file.set_size(data['size']) return image_file class BaseImageFile: size = [] def exists(self): raise NotImplementedError() @property def width(self): return self.size[0] x = width @property def height(self): return self.size[1] y = height def is_portrait(self): return self.y > self.x @property def ratio(self): return float(self.x) / float(self.y) @property def url(self): raise NotImplementedError() src = url class ImageFile(BaseImageFile): _size = None def __init__(self, file_, storage=None): if not file_: raise ThumbnailError('File is empty.') # figure out name if hasattr(file_, 'name'): self.name = file_.name else: self.name = force_str(file_) # TODO: Add a customizable naming method as a signal # Remove query args from names. Fixes cache and signature arguments # from third party services, like Amazon S3 and signature args. if settings.THUMBNAIL_REMOVE_URL_ARGS: self.name = self.name.split('?')[0] # Support for relative protocol urls if self.name.startswith('//'): self.name = 'http:' + self.name # figure out storage if storage is not None: self.storage = storage elif hasattr(file_, 'storage'): self.storage = file_.storage elif url_pat.match(self.name): self.storage = UrlStorage() else: self.storage = default_storage if hasattr(self.storage, 'location'): location = self.storage.location if not self.storage.location.endswith("/"): location += "/" if self.name.startswith(location): self.name = self.name[len(location):] def __str__(self): return self.name def exists(self): return self.storage.exists(self.name) def set_size(self, size=None): # set the size if given if size is not None: pass # Don't try to set the size the expensive way if it already has a # value. elif self._size is not None: return elif hasattr(self.storage, 'image_size'): # Storage backends can implement ``image_size`` method that # optimizes this. size = self.storage.image_size(self.name) else: # This is the worst case scenario image = default.engine.get_image(self) size = default.engine.get_image_size(image) if self.flip_dimensions(image): size = list(size) size.reverse() self._size = list(size) def flip_dimensions(self, image): """ Do not manipulate image, but ask engine whether we'd be doing a 90deg rotation at some point. """ return default.engine.flip_dimensions(image) @property def size(self): return self._size @property def url(self): return self.storage.url(self.name) def read(self): f = self.storage.open(self.name) try: return f.read() finally: f.close() def write(self, content): if not isinstance(content, File): content = ContentFile(content) self._size = None self.name = self.storage.save(self.name, content) return self.name def delete(self): return self.storage.delete(self.name) def serialize_storage(self): if isinstance(self.storage, LazyObject): # if storage is wrapped in a lazy object we need to get the real # thing. if self.storage._wrapped is empty: self.storage._setup() cls = self.storage._wrapped.__class__ else: cls = self.storage.__class__ return '%s.%s' % (cls.__module__, cls.__name__) @property def key(self): return tokey(self.name, self.serialize_storage()) def serialize(self): return serialize_image_file(self) class DummyImageFile(BaseImageFile): def __init__(self, geometry_string): self.size = parse_geometry( geometry_string, settings.THUMBNAIL_DUMMY_RATIO, ) def exists(self): return True @property def url(self): return settings.THUMBNAIL_DUMMY_SOURCE % ( {'width': self.x, 'height': self.y} ) class UrlStorage(Storage): def normalize_url(self, url, charset="utf-8"): # Convert URL to ASCII before processing url = url.encode(charset, errors="ignore") url = url.decode("ascii", errors="ignore") scheme, netloc, path, qs, anchor = urlsplit(url) path = quote(path, b"/%") qs = quote_plus(qs, b":&%=") return urlunsplit((scheme, netloc, path, qs, anchor)) def open(self, name, mode='rb'): url = self.normalize_url(name) python_version = platform.python_version_tuple()[0] user_agent = "python-urllib{python_version}/0.6".format(python_version=python_version) req = Request(url, headers={"User-Agent": user_agent}) return urlopen(req, timeout=settings.THUMBNAIL_URL_TIMEOUT) def exists(self, name): try: self.open(name) except URLError: return False return True def url(self, name): return name def delete(self, name): pass def delete_all_thumbnails(): storage = default.storage path = settings.THUMBNAIL_PREFIX def walk(path): dirs, files = storage.listdir(path) for f in files: storage.delete(os.path.join(path, f)) for d in dirs: directory = os.path.join(path, d) walk(directory) try: full_path = storage.path(directory) except Exception: continue os.rmdir(full_path) walk(path) sorl-thumbnail-12.7.0/sorl/thumbnail/kvstores/000077500000000000000000000000001375675401600214015ustar00rootroot00000000000000sorl-thumbnail-12.7.0/sorl/thumbnail/kvstores/__init__.py000066400000000000000000000000001375675401600235000ustar00rootroot00000000000000sorl-thumbnail-12.7.0/sorl/thumbnail/kvstores/base.py000066400000000000000000000155621375675401600226760ustar00rootroot00000000000000from sorl.thumbnail.conf import settings from sorl.thumbnail.helpers import serialize, deserialize, ThumbnailError from sorl.thumbnail.images import serialize_image_file, deserialize_image_file def add_prefix(key, identity='image'): """ Adds prefixes to the key """ return '||'.join([settings.THUMBNAIL_KEY_PREFIX, identity, key]) def del_prefix(key): """ Removes prefixes from the key """ return key.split('||')[-1] class KVStoreBase: def get(self, image_file): """ Gets the ``image_file`` from store. Returns ``None`` if not found. """ return self._get(image_file.key) def set(self, image_file, source=None): """ Updates store for the `image_file`. Makes sure the `image_file` has a size set. """ image_file.set_size() # make sure its got a size self._set(image_file.key, image_file) if source is not None: if not self.get(source): # make sure the source is in kvstore raise ThumbnailError('Cannot add thumbnails for source: `%s` ' 'that is not in kvstore.' % source.name) # Update the list of thumbnails for source. thumbnails = self._get(source.key, identity='thumbnails') or [] thumbnails = set(thumbnails) thumbnails.add(image_file.key) self._set(source.key, list(thumbnails), identity='thumbnails') def get_or_set(self, image_file): cached = self.get(image_file) if cached is not None: return cached self.set(image_file) return image_file def delete(self, image_file, delete_thumbnails=True): """ Deletes the reference to the ``image_file`` and deletes the references to thumbnails as well as thumbnail files if ``delete_thumbnails`` is `True``. Does not delete the ``image_file`` is self. """ if delete_thumbnails: self.delete_thumbnails(image_file) self._delete(image_file.key) def delete_thumbnails(self, image_file): """ Deletes references to thumbnails as well as thumbnail ``image_files``. """ thumbnail_keys = self._get(image_file.key, identity='thumbnails') if thumbnail_keys: # Delete all thumbnail keys from store and delete the # thumbnail ImageFiles. for key in thumbnail_keys: thumbnail = self._get(key) if thumbnail: self.delete(thumbnail, False) thumbnail.delete() # delete the actual file # Delete the thumbnails key from store self._delete(image_file.key, identity='thumbnails') def delete_all_thumbnail_files(self): for key in self._find_keys(identity='thumbnails'): thumbnail_keys = self._get(key, identity='thumbnails') if thumbnail_keys: for key in thumbnail_keys: thumbnail = self._get(key) if thumbnail: thumbnail.delete() def cleanup(self): """ Cleans up the key value store. In detail: 1. Deletes all key store references for image_files that do not exist and all key references for its thumbnails *and* their image_files. 2. Deletes or updates all invalid thumbnail keys """ for key in self._find_keys(identity='image'): image_file = self._get(key) if image_file and not image_file.exists(): self.delete(image_file) for key in self._find_keys(identity='thumbnails'): # We do not need to check for file existence in here since we # already did that above for all image references image_file = self._get(key) if image_file: # if there is an image_file then we check all of its thumbnails # for existence thumbnail_keys = self._get(key, identity='thumbnails') or [] thumbnail_keys_set = set(thumbnail_keys) for thumbnail_key in thumbnail_keys: if not self._get(thumbnail_key): thumbnail_keys_set.remove(thumbnail_key) thumbnail_keys = list(thumbnail_keys_set) if thumbnail_keys: self._set(key, thumbnail_keys, identity='thumbnails') continue # if there is no image_file then this thumbnails key is just # hangin' loose, If the thumbnail_keys ended up empty there is no # reason for keeping it either self._delete(key, identity='thumbnails') def clear(self): """ Brutely clears the key value store for keys with THUMBNAIL_KEY_PREFIX prefix. Use this in emergency situations. Normally you would probably want to use the ``cleanup`` method instead. """ all_keys = self._find_keys_raw(settings.THUMBNAIL_KEY_PREFIX) if all_keys: self._delete_raw(*all_keys) def _get(self, key, identity='image'): """ Deserializing, prefix wrapper for _get_raw """ value = self._get_raw(add_prefix(key, identity)) if not value: return None if identity == 'image': return deserialize_image_file(value) return deserialize(value) def _set(self, key, value, identity='image'): """ Serializing, prefix wrapper for _set_raw """ if identity == 'image': s = serialize_image_file(value) else: s = serialize(value) self._set_raw(add_prefix(key, identity), s) def _delete(self, key, identity='image'): """ Prefix wrapper for _delete_raw """ self._delete_raw(add_prefix(key, identity)) def _find_keys(self, identity='image'): """ Finds and returns all keys for identity, """ prefix = add_prefix('', identity) raw_keys = self._find_keys_raw(prefix) or [] for raw_key in raw_keys: yield del_prefix(raw_key) # # Methods which key-value stores need to implement # def _get_raw(self, key): """ Gets the value from keystore, returns `None` if not found. """ raise NotImplementedError() def _set_raw(self, key, value): """ Sets value associated to key. Key is expected to be shorter than 200 chars. Value is a ``unicode`` object with an unknown (reasonable) length. """ raise NotImplementedError() def _delete_raw(self, *keys): """ Deletes the keys. Silent failure for missing keys. """ raise NotImplementedError() def _find_keys_raw(self, prefix): """ Finds all keys with prefix """ raise NotImplementedError() sorl-thumbnail-12.7.0/sorl/thumbnail/kvstores/cached_db_kvstore.py000066400000000000000000000040611375675401600254050ustar00rootroot00000000000000from django.core.cache import cache, caches, InvalidCacheBackendError from sorl.thumbnail.kvstores.base import KVStoreBase from sorl.thumbnail.conf import settings from sorl.thumbnail.models import KVStore as KVStoreModel class EMPTY_VALUE: pass class KVStore(KVStoreBase): def __init__(self): super().__init__() @property def cache(self): try: kv_cache = caches[settings.THUMBNAIL_CACHE] except InvalidCacheBackendError: kv_cache = cache return kv_cache def clear(self, delete_thumbnails=False): """ We can clear the database more efficiently using the prefix here rather than calling :meth:`_delete_raw`. """ prefix = settings.THUMBNAIL_KEY_PREFIX for key in self._find_keys_raw(prefix): self.cache.delete(key) KVStoreModel.objects.filter(key__startswith=prefix).delete() if delete_thumbnails: self.delete_all_thumbnail_files() def _get_raw(self, key): value = self.cache.get(key) if value is None: try: value = KVStoreModel.objects.get(key=key).value except KVStoreModel.DoesNotExist: # we set the cache to prevent further db lookups value = EMPTY_VALUE self.cache.set(key, value, settings.THUMBNAIL_CACHE_TIMEOUT) if value == EMPTY_VALUE: return None return value def _set_raw(self, key, value): kvstore_value, created = KVStoreModel.objects.get_or_create( key=key, defaults={'value': value}) if not created: kvstore_value.value = value kvstore_value.save() self.cache.set(key, value, settings.THUMBNAIL_CACHE_TIMEOUT) def _delete_raw(self, *keys): KVStoreModel.objects.filter(key__in=keys).delete() for key in keys: self.cache.delete(key) def _find_keys_raw(self, prefix): qs = KVStoreModel.objects.filter(key__startswith=prefix) return qs.values_list('key', flat=True) sorl-thumbnail-12.7.0/sorl/thumbnail/kvstores/dbm_kvstore.py000066400000000000000000000051221375675401600242720ustar00rootroot00000000000000import os from sorl.thumbnail.kvstores.base import KVStoreBase from sorl.thumbnail.conf import settings try: import anydbm as dbm except KeyError: import dbm except ImportError: # Python 3, hopefully import dbm # # OS filesystem locking primitives. TODO: Test Windows versions # if os.name == 'nt': import msvcrt def lock(f, readonly): msvcrt.locking(f.fileno(), msvcrt.LK_LOCK, 1) def unlock(f): msvcrt.locking(f.fileno(), msvcrt.LK_UNLCK, 1) else: import fcntl def lock(f, readonly): fcntl.lockf(f.fileno(), fcntl.LOCK_SH if readonly else fcntl.LOCK_EX) def unlock(f): fcntl.lockf(f.fileno(), fcntl.LOCK_UN) class DBMContext: """ A context manager to access the key-value store in a concurrent-safe manner. """ __slots__ = ('filename', 'mode', 'readonly', 'lockfile', 'db') def __init__(self, filename, mode, readonly): self.filename = filename self.mode = mode self.readonly = readonly self.lockfile = open(filename + ".lock", 'w+b') def __enter__(self): lock(self.lockfile, self.readonly) self.db = dbm.open(self.filename, 'c', self.mode) return self.db def __exit__(self, exval, extype, tb): self.db.close() unlock(self.lockfile) self.lockfile.close() class KVStore(KVStoreBase): # Please note that all the coding effort is devoted to provide correct # semantics, not performance. Therefore, use this store only in development # environments. def __init__(self): super().__init__() self.filename = settings.THUMBNAIL_DBM_FILE self.mode = settings.THUMBNAIL_DBM_MODE def _cast_key(self, key): return key if isinstance(key, bytes) else key.encode('utf-8') def _get_raw(self, key): with DBMContext(self.filename, self.mode, True) as db: try: return db[self._cast_key(key)] except KeyError: return None def _set_raw(self, key, value): with DBMContext(self.filename, self.mode, False) as db: db[self._cast_key(key)] = value def _delete_raw(self, *keys): with DBMContext(self.filename, self.mode, False) as db: for key in keys: try: del db[self._cast_key(key)] except KeyError: pass def _find_keys_raw(self, prefix): with DBMContext(self.filename, self.mode, True) as db: p = self._cast_key(prefix) return [k.decode('utf-8') for k in db.keys() if k.startswith(p)] sorl-thumbnail-12.7.0/sorl/thumbnail/kvstores/dynamodb_kvstore.py000066400000000000000000000023651375675401600253330ustar00rootroot00000000000000from boto.dynamodb2.table import Table import boto from sorl.thumbnail.kvstores.base import KVStoreBase from sorl.thumbnail.conf import settings class KVStore(KVStoreBase): def __init__(self): super().__init__() region = settings.AWS_REGION_NAME access_key = settings.AWS_ACCESS_KEY_ID secret = settings.AWS_SECRET_ACCESS_KEY conn = boto.dynamodb2.connect_to_region(region, aws_access_key_id=access_key, aws_secret_access_key=secret) self.table = Table(settings.THUMBNAIL_DYNAMODB_NAME, connection=conn) def _get_raw(self, key): try: return self.table.get_item(key=key)['value'] except boto.dynamodb2.exceptions.ItemNotFound: pass def _set_raw(self, key, value): try: item = self.table.get_item(key=key) except boto.dynamodb2.exceptions.ItemNotFound: item = self.table.new_item() item['key'] = key item['value'] = value item.save(overwrite=True) def _delete_raw(self, *keys): [self.table.delete_item(key=k) for k in keys] def _find_keys_raw(self, prefix): return [i['key'] for i in self.table.scan(key__beginswith=prefix)] sorl-thumbnail-12.7.0/sorl/thumbnail/kvstores/redis_kvstore.py000066400000000000000000000022611375675401600246370ustar00rootroot00000000000000import redis from sorl.thumbnail.kvstores.base import KVStoreBase from sorl.thumbnail.conf import settings class KVStore(KVStoreBase): def __init__(self): super().__init__() if hasattr(settings, 'THUMBNAIL_REDIS_URL'): self.connection = redis.from_url(settings.THUMBNAIL_REDIS_URL) else: self.connection = redis.Redis( host=settings.THUMBNAIL_REDIS_HOST, port=settings.THUMBNAIL_REDIS_PORT, db=settings.THUMBNAIL_REDIS_DB, ssl=settings.THUMBNAIL_REDIS_SSL, password=settings.THUMBNAIL_REDIS_PASSWORD, unix_socket_path=settings.THUMBNAIL_REDIS_UNIX_SOCKET_PATH, ) def _get_raw(self, key): return self.connection.get(key) def _set_raw(self, key, value): return self.connection.set( key, value, ex=settings.THUMBNAIL_REDIS_TIMEOUT) def _delete_raw(self, *keys): return self.connection.delete(*keys) def _find_keys_raw(self, prefix): pattern = prefix + '*' return list(map(lambda key: key.decode('utf-8'), self.connection.keys(pattern=pattern))) sorl-thumbnail-12.7.0/sorl/thumbnail/log.py000066400000000000000000000022421375675401600206540ustar00rootroot00000000000000import logging from django.core.mail.message import EmailMessage from sorl.thumbnail.conf import settings class ThumbnailLogHandler(logging.Handler): """ An exception log handler for thumbnail errors. """ def emit(self, record): import traceback if not settings.ADMINS: return try: # Hack to try to get request from context request = record.exc_info[2].tb_frame.f_locals['context']['request'] request_repr = repr(request) request_path = request.path except Exception: request_repr = "Request unavailable" request_path = 'Unknown URL' if record.exc_info: stack_trace = '\n'.join(traceback.format_exception(*record.exc_info)) else: stack_trace = 'No stack trace available' message = "%s\n\n%s" % (stack_trace, request_repr) msg = EmailMessage( '[sorl-thumbnail] %s: %s' % (record.levelname, request_path), message, settings.SERVER_EMAIL, [a[1] for a in settings.ADMINS], connection=None ) msg.send(fail_silently=True) sorl-thumbnail-12.7.0/sorl/thumbnail/management/000077500000000000000000000000001375675401600216355ustar00rootroot00000000000000sorl-thumbnail-12.7.0/sorl/thumbnail/management/__init__.py000066400000000000000000000000001375675401600237340ustar00rootroot00000000000000sorl-thumbnail-12.7.0/sorl/thumbnail/management/commands/000077500000000000000000000000001375675401600234365ustar00rootroot00000000000000sorl-thumbnail-12.7.0/sorl/thumbnail/management/commands/__init__.py000066400000000000000000000000001375675401600255350ustar00rootroot00000000000000sorl-thumbnail-12.7.0/sorl/thumbnail/management/commands/thumbnail.py000066400000000000000000000042231375675401600257740ustar00rootroot00000000000000import sys from django.core.management.base import LabelCommand, CommandError from sorl.thumbnail import default from sorl.thumbnail.images import delete_all_thumbnails class Command(LabelCommand): help = ( 'Handles thumbnails and key value store' ) missing_args_message = 'Enter one of [cleanup, clear clear_delete_referenced clear_delete_all]' def handle(self, *labels, **options): verbosity = int(options.get('verbosity')) # Django 1.4 compatibility fix stdout = options.get('stdout', None) stdout = stdout if stdout else sys.stdout stderr = options.get('stderr', None) stderr = stderr if stderr else sys.stderr if not labels: print(self.print_help('thumbnail', ''), file=stderr) sys.exit(1) if len(labels) != 1: raise CommandError('`%s` is not a valid argument' % labels) label = labels[0] if label not in ['cleanup', 'clear', 'clear_delete_referenced', 'clear_delete_all']: raise CommandError('`%s` unknown action' % label) if label == 'cleanup': if verbosity >= 1: print("Cleanup thumbnails", end=' ... ', file=stdout) default.kvstore.cleanup() if verbosity >= 1: print("[Done]", file=stdout) return if label == 'clear_delete_referenced': if verbosity >= 1: print("Delete all thumbnail files referenced in " "Key Value Store", end=' ... ', file=stdout) default.kvstore.delete_all_thumbnail_files() if verbosity >= 1: print('[Done]', file=stdout) if verbosity >= 1: print("Clear the Key Value Store", end=' ... ', file=stdout) default.kvstore.clear() if verbosity >= 1: print('[Done]', file=stdout) if label == 'clear_delete_all': if verbosity >= 1: print("Delete all thumbnail files in THUMBNAIL_PREFIX", end=' ... ', file=stdout) delete_all_thumbnails() if verbosity >= 1: print('[Done]', file=stdout) sorl-thumbnail-12.7.0/sorl/thumbnail/migrations/000077500000000000000000000000001375675401600216755ustar00rootroot00000000000000sorl-thumbnail-12.7.0/sorl/thumbnail/migrations/0001_initial.py000066400000000000000000000010141375675401600243340ustar00rootroot00000000000000from django.db import models, migrations class Migration(migrations.Migration): dependencies = [ ] operations = [ migrations.CreateModel( name='KVStore', fields=[ ('key', models.CharField(serialize=False, db_column='key', max_length=200, primary_key=True)), ('value', models.TextField()), ], ), ] sorl-thumbnail-12.7.0/sorl/thumbnail/migrations/__init__.py000066400000000000000000000000001375675401600237740ustar00rootroot00000000000000sorl-thumbnail-12.7.0/sorl/thumbnail/models.py000066400000000000000000000004631375675401600213610ustar00rootroot00000000000000from django.db import models from sorl.thumbnail.conf import settings class KVStore(models.Model): key = models.CharField( max_length=200, primary_key=True, db_column=settings.THUMBNAIL_KEY_DBCOLUMN ) value = models.TextField() def __str__(self): return self.key sorl-thumbnail-12.7.0/sorl/thumbnail/parsers.py000066400000000000000000000054171375675401600215610ustar00rootroot00000000000000import re from sorl.thumbnail.helpers import ThumbnailError, toint bgpos_pat = re.compile(r'^(?P\d+)(?P%|px)$') geometry_pat = re.compile(r'^(?P\d+)?(?:x(?P\d+))?$') class ThumbnailParseError(ThumbnailError): pass def parse_geometry(geometry, ratio=None): """ Parses a geometry string syntax and returns a (width, height) tuple """ m = geometry_pat.match(geometry) def syntax_error(): return ThumbnailParseError('Geometry does not have the correct ' 'syntax: %s' % geometry) if not m: raise syntax_error() x = m.group('x') y = m.group('y') if x is None and y is None: raise syntax_error() if x is not None: x = int(x) if y is not None: y = int(y) # calculate x or y proportionally if not set but we need the image ratio # for this if ratio is not None: ratio = float(ratio) if x is None: x = toint(y * ratio) elif y is None: y = toint(x / ratio) return x, y def parse_crop(crop, xy_image, xy_window): """ Returns x, y offsets for cropping. The window area should fit inside image but it works out anyway """ x_alias_percent = { 'left': '0%', 'center': '50%', 'right': '100%', } y_alias_percent = { 'top': '0%', 'center': '50%', 'bottom': '100%', } xy_crop = crop.split(' ') if len(xy_crop) == 1: if crop in x_alias_percent: x_crop = x_alias_percent[crop] y_crop = '50%' elif crop in y_alias_percent: y_crop = y_alias_percent[crop] x_crop = '50%' else: x_crop, y_crop = crop, crop elif len(xy_crop) == 2: x_crop, y_crop = xy_crop x_crop = x_alias_percent.get(x_crop, x_crop) y_crop = y_alias_percent.get(y_crop, y_crop) else: raise ThumbnailParseError('Unrecognized crop option: %s' % crop) def get_offset(crop, epsilon): m = bgpos_pat.match(crop) if not m: raise ThumbnailParseError('Unrecognized crop option: %s' % crop) value = int(m.group('value')) # we only take ints in the regexp unit = m.group('unit') if unit == '%': value = epsilon * value / 100.0 # return ∈ [0, epsilon] return int(max(0, min(value, epsilon))) offset_x = get_offset(x_crop, xy_image[0] - xy_window[0]) offset_y = get_offset(y_crop, xy_image[1] - xy_window[1]) return offset_x, offset_y def parse_cropbox(cropbox): """ Returns x, y, x2, y2 tuple for cropping. """ if isinstance(cropbox, str): return tuple([int(x.strip()) for x in cropbox.split(',')]) else: return tuple(cropbox) sorl-thumbnail-12.7.0/sorl/thumbnail/shortcuts.py000066400000000000000000000006151375675401600221330ustar00rootroot00000000000000from sorl.thumbnail import default def get_thumbnail(file_, geometry_string, **options): """ A shortcut for the Backend ``get_thumbnail`` method """ return default.backend.get_thumbnail(file_, geometry_string, **options) def delete(file_, delete_file=True): """ A shortcut for the Backend ``delete`` method """ return default.backend.delete(file_, delete_file) sorl-thumbnail-12.7.0/sorl/thumbnail/templatetags/000077500000000000000000000000001375675401600222135ustar00rootroot00000000000000sorl-thumbnail-12.7.0/sorl/thumbnail/templatetags/__init__.py000066400000000000000000000000001375675401600243120ustar00rootroot00000000000000sorl-thumbnail-12.7.0/sorl/thumbnail/templatetags/sorl_thumbnail.py000066400000000000000000000004711375675401600256110ustar00rootroot00000000000000""" This allows usage of sorl-thumbnail in templates by {% load sorl_thumbnail %} instead of traditional {% load thumbnail %}. It's specifically useful in projects that do make use of multiple thumbnailer libraries (for instance `easy-thumbnails` alongside `sorl-thumbnail`). """ from .thumbnail import * # noqa sorl-thumbnail-12.7.0/sorl/thumbnail/templatetags/thumbnail.py000066400000000000000000000210001375675401600245410ustar00rootroot00000000000000import decimal import logging import sys import re import os from functools import wraps from django.template import Library, Node, NodeList, TemplateSyntaxError from django.utils.encoding import smart_str from django.conf import settings from sorl.thumbnail.conf import settings as sorl_settings from sorl.thumbnail import default from sorl.thumbnail.images import ImageFile, DummyImageFile from sorl.thumbnail.parsers import parse_geometry from sorl.thumbnail.shortcuts import get_thumbnail register = Library() kw_pat = re.compile(r'^(?P[\w]+)=(?P.+)$') logger = logging.getLogger('sorl.thumbnail') def safe_filter(error_output=''): """ A safe filter decorator only raising errors when ``THUMBNAIL_DEBUG`` is ``True`` otherwise returning ``error_output``. """ def inner(f): @wraps(f) def wrapper(*args, **kwargs): try: return f(*args, **kwargs) except Exception as err: if sorl_settings.THUMBNAIL_DEBUG: raise logger.error('Thumbnail filter failed: %s' % str(err), exc_info=sys.exc_info()) return error_output return wrapper return inner class ThumbnailNodeBase(Node): """ A Node that renders safely """ nodelist_empty = NodeList() def render(self, context): try: return self._render(context) except Exception: if sorl_settings.THUMBNAIL_DEBUG: raise error_message = 'Thumbnail tag failed' if context.template.engine.debug: try: error_message_template = ( "Thumbnail tag failed " "in template {template_name}, error at: " "{tag_text}" ) template_origin, (position_start, position_end) = self.source template_text = template_origin.reload() tag_text = template_text[position_start:position_end] error_message = error_message_template.format( template_name=template_origin.name, tag_text=tag_text, ) except Exception: pass logger.exception(error_message) return self.nodelist_empty.render(context) def _render(self, context): raise NotImplementedError() class ThumbnailNode(ThumbnailNodeBase): child_nodelists = ('nodelist_file', 'nodelist_empty') error_msg = ('Syntax error. Expected: ``thumbnail source geometry ' '[key1=val1 key2=val2...] as var``') def __init__(self, parser, token): bits = token.split_contents() self.file_ = parser.compile_filter(bits[1]) self.geometry = parser.compile_filter(bits[2]) self.options = [] self.as_var = None self.nodelist_file = None if bits[-2] == 'as': options_bits = bits[3:-2] else: options_bits = bits[3:] for bit in options_bits: m = kw_pat.match(bit) if not m: raise TemplateSyntaxError(self.error_msg) key = smart_str(m.group('key')) expr = parser.compile_filter(m.group('value')) self.options.append((key, expr)) if bits[-2] == 'as': self.as_var = bits[-1] self.nodelist_file = parser.parse(('empty', 'endthumbnail',)) if parser.next_token().contents == 'empty': self.nodelist_empty = parser.parse(('endthumbnail',)) parser.delete_first_token() def _render(self, context): file_ = self.file_.resolve(context) geometry = self.geometry.resolve(context) options = {} for key, expr in self.options: noresolve = {'True': True, 'False': False, 'None': None} value = noresolve.get(str(expr), expr.resolve(context)) if key == 'options': options.update(value) else: options[key] = value thumbnail = None if file_: thumbnail = get_thumbnail(file_, geometry, **options) elif sorl_settings.THUMBNAIL_DUMMY: thumbnail = DummyImageFile(geometry) if not thumbnail or (isinstance(thumbnail, DummyImageFile) and self.nodelist_empty): if self.nodelist_empty: return self.nodelist_empty.render(context) else: return '' if self.as_var: context.push() context[self.as_var] = thumbnail output = self.nodelist_file.render(context) context.pop() else: output = thumbnail.url return output def __repr__(self): return "" def __iter__(self): for node in self.nodelist_file: yield node for node in self.nodelist_empty: yield node @register.filter def resolution(file_, resolution_string): """ A filter to return the URL for the provided resolution of the thumbnail. """ if sorl_settings.THUMBNAIL_DUMMY: dummy_source = sorl_settings.THUMBNAIL_DUMMY_SOURCE source = dummy_source.replace('%(width)s', '(?P[0-9]+)') source = source.replace('%(height)s', '(?P[0-9]+)') source = re.compile(source) try: resolution = decimal.Decimal(resolution_string.strip('x')) info = source.match(file_).groupdict() info = {dimension: int(int(size) * resolution) for (dimension, size) in info.items()} return dummy_source % info except (AttributeError, TypeError, KeyError): # If we can't manipulate the dummy we shouldn't change it at all return file_ filename, extension = os.path.splitext(file_) return '%s@%s%s' % (filename, resolution_string, extension) @register.tag def thumbnail(parser, token): return ThumbnailNode(parser, token) @safe_filter(error_output=False) @register.filter def is_portrait(file_): """ A very handy filter to determine if an image is portrait or landscape. """ if sorl_settings.THUMBNAIL_DUMMY: return sorl_settings.THUMBNAIL_DUMMY_RATIO < 1 if not file_: return False image_file = default.kvstore.get_or_set(ImageFile(file_)) return image_file.is_portrait() @safe_filter(error_output='auto') @register.filter def margin(file_, geometry_string): """ Returns the calculated margin for an image and geometry """ if not file_ or (sorl_settings.THUMBNAIL_DUMMY or isinstance(file_, DummyImageFile)): return 'auto' margin = [0, 0, 0, 0] image_file = default.kvstore.get_or_set(ImageFile(file_)) x, y = parse_geometry(geometry_string, image_file.ratio) ex = x - image_file.x margin[3] = ex / 2 margin[1] = ex / 2 if ex % 2: margin[1] += 1 ey = y - image_file.y margin[0] = ey / 2 margin[2] = ey / 2 if ey % 2: margin[2] += 1 return ' '.join(['%dpx' % n for n in margin]) @safe_filter(error_output='auto') @register.filter def background_margin(file_, geometry_string): """ Returns the calculated margin for a background image and geometry """ if not file_ or sorl_settings.THUMBNAIL_DUMMY: return 'auto' margin = [0, 0] image_file = default.kvstore.get_or_set(ImageFile(file_)) x, y = parse_geometry(geometry_string, image_file.ratio) ex = x - image_file.x margin[0] = ex / 2 ey = y - image_file.y margin[1] = ey / 2 return ' '.join(['%spx' % n for n in margin]) def text_filter(regex_base, value): """ Helper method to regex replace images with captions in different markups """ regex = regex_base % { 're_cap': r'[a-zA-Z0-9\.\,:;/_ \(\)\-\!\?"]+', 're_img': r'[a-zA-Z0-9\.:/_\-\% ]+' } images = re.findall(regex, value) for i in images: image = i[1] if image.startswith(settings.MEDIA_URL): image = image[len(settings.MEDIA_URL):] im = get_thumbnail(image, str(sorl_settings.THUMBNAIL_FILTER_WIDTH)) value = value.replace(i[1], im.url) return value @safe_filter(error_output='auto') @register.filter def markdown_thumbnails(value): return text_filter(r'!\[(%(re_cap)s)?\][ ]?\((%(re_img)s)\)', value) @safe_filter(error_output='auto') @register.filter def html_thumbnails(value): return text_filter(r' % #'7f$459EGIWXgh?A mg^=LBWWhH (Df8+DKwU`,6Z`2o[պacLSm].b]QwyԻݫakohl mRwoڈl|7{Dm"'M R?Yz;̚JrW%"ZqGxeQ24.IrF . ϼ %!x\U6eD ק7Fo>j1Qz}\%'sorl-thumbnail-12.7.0/tests/data/2_topright.jpg000066400000000000000000000010271375675401600214150ustar00rootroot00000000000000JFIFHH"ExifII*CCreated with GIMP on a Mac   A   $%&6F#'579IWXefgh? [FڨtjbtL1:ig}PUQ/gF}N:rqY=۠3%!]gʣvyAxx=SyNMsI㲼m ).]$ۧ"q' ʪ .tw]|;?? /2osorl-thumbnail-12.7.0/tests/data/3_bottomright.jpg000066400000000000000000000010211375675401600221120ustar00rootroot00000000000000JFIFHH"ExifII*CCreated with GIMP on a Mac  C  %&'#$(456789DGIWXefgh? ϮݙO‹؛bPŤ_nKd4i:ǜb)}C4bA3@B,qRJ]d35I d\#*I- N%pn#1pZm'^S=̳₪2vwo?IYI`! GwqG]w֫jnh&e/$'daDS )М^Ue>TAsorl-thumbnail-12.7.0/tests/data/4_bottomleft.jpg000066400000000000000000000010161375675401600217340ustar00rootroot00000000000000JFIFHH"ExifII*CCreated with GIMP on a Mac   > & 769#$%5CFIWXdegh?r֎מnL'Q‹țiiה32YgeL ?/76ښ]1/d@\LGhZ\d/ӧ\A1L =YeԱRⶼ[Y$D\ qr9Wo4ɚ\F"s9mBb Ϊ*@Wf_m}f 7sorl-thumbnail-12.7.0/tests/data/5_lefttop.jpg000066400000000000000000000010251375675401600212330ustar00rootroot00000000000000JFIFHH"ExifII*CCreated with GIMP on a Mac  > % 8Gf#$&'4569EHXi?;u&lf.JJjVB?eُ8!E y3X: 6۞N 0q+e.v>EJQ׿p1. V'ݳ'y;[b @QՕ^7_ mp}|!Uv_Q?sorl-thumbnail-12.7.0/tests/data/6_righttop.jpg000066400000000000000000000010311375675401600214140ustar00rootroot00000000000000JFIFHH"ExifII*CCreated with GIMP on a Mac   @   $%&6F#5789GHXefi?+u& UV_(WJF9gc6NBɤRy}O% dDB@DVaRaf3B,NOēebF2jb>K"cQ'O>vYniƓ4U|)uUIΤq؋raGơЌgvW>^7sk4WNNHT<4ZiWGŗO\^ww9rɿ,c?O ,wjsorl-thumbnail-12.7.0/tests/data/7_rightbottom.jpg000066400000000000000000000010221375675401600221170ustar00rootroot00000000000000JFIFHH"ExifII*CCreated with GIMP on a Mac   @   %'7G #$&)4589DHXefi?-uf-TvwoB >o yO1wb3dNxEqVj{1SZ3@B,qJRJN ӄf\FrBwRgj1Q|\RMπEX#.ޤ5څϵ!;4+JՊK:F7.Ly \j]L"2tv}dT)O ?#;* it=>*Y-dsorl-thumbnail-12.7.0/tests/data/8_leftbottom.jpg000066400000000000000000000010241375675401600217370ustar00rootroot00000000000000JFIFHH"ExifII*CCreated with GIMP on a Mac  ?  & #'678$%)59CFGHXdei?_BUa㳛Y7sorl-thumbnail-12.7.0/tests/data/animation_w_transparency.gif000066400000000000000000003671041375675401600244320ustar00rootroot00000000000000GIF89a  !!!"""##"###$$$%%%&&&'''(''((()))***+*++++,,,---...///000111222333444555666777888999:::;;;;;<<<<===>>>???@@@AAABBBBBCCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbbbccccdddeeefffggghhhiiijjjkkklllmmmnnnnnoonnoooooppppqppqqqrrrssstttuuuuuvvvvwwwxxxyyyzyzzzz{{{{{||||||}}|{}||}}}~~~~~~~~! NETSCAPE2.0!Created with GIMP!,g H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷L[jݻ,˷߿ LP^̸⅓IjLrVNm$I#G-BthѤ [!ɲ:1 Aͧwo݁9B}0Ca+_RD 8DH}D $hC萠 2K̯ BG !ނ R{!!-%w8 AH(mH"BH=ԊgLBnd(ЁW)Bhdc)D7"!#h@,)F%%v䘖ѵ! H(]"$#zBI柌wYi&H" ]%2f# b(A\^&\`:P(H,Pij_ b章rhb}Ut iŠYebj BԦ 7){BHB%j|CxZl2!E9 l8"$!7 jtD;,Gge)! b/XNHk '%t}_BH",օ!b!0yp-tJ "'J4BluQaFĉJ_Kb# L*{#է l ^zݱEˈ$Ewv߈$z):&qIJ%#X"A%ʉ5h v8# &G&{"E*JP(r{L&2Hu˵\LjDl ߆8 C࿤xj-"q/2p- &^+_«'J `ONn~DrBe ]jH y U+!-zp'O I;5DAf~7TږIM!4tSI`AD0A!HR @.d0bn9 %& d?nk40f&!ITI#[$&- g  0 b("%5ocgFcJ.9kj6*AP!vĒ$=zrTC!G [d$1JqO@8nǩP(Bc2f~ Ddd3pQ.IlZ+F]FHDE#p5*\)EɅDŽBLX's W nġl6XStIYZhKlb Bw F%t ,d%Y#jV}hvU.uUf9(UV]9; 7d"DTMrc~6bAhQm)ِX?( (%f Xe d[ĥ"b3q}FkDHr1Dm8.r+5в>r5&3G%xc7Gt(t݉.!XlQgd/D.K҇I4;clp4)kCjcRDdZvDܫVT}G $:޻Ib^IĨPa^u۫CwDOf;^Ŧ1|,OHS{8Qa D![zװؤJS Q 1SFq D &=(G5Be3d婤16270rd1b?>y! ˉD{8((}{S>ftD˶Fk6A)w6Ȃ0J8wVA8̏BqkcIP<MPTk)SA> sx A 9率[1F6LD77,_k}rӈYy&KItE\feXX@ @е[I%]&lAtRUZ+`Ţ3-v t$*<,?oъӯ/4:HMGc-@XT\ 1H8R(,iXQnG )TKrڤ_#'㕔4 v/.tQaM.9vWX"j[)&t`"HBZ7&- Է;5Q~`|gs1TvIM"($C@\P (-'_A'qLO*eQ:a'2C$! /;ńSuH$)'=9w>gC*1"!7֕I38P7 6Ӣ@{^#ȥ&P5-1F"r[w{4Jvq4!dwAYò!VTÒN#U #K on1x 1le]siPCD Xrf &{s61RtT 1 )0$.z6'pV?!6l-##9aTu  )@7#xU=lU0uiCӁXr.rr,ie,5"1QLVB@-L1t&'#8f\GLI/OsnBR66}i0HQ6V3}ӧ@or&:V=>Lt7% d9=gK !0%b=L*te6#/&*0h3cQh}$8WH2r&Es#T<:@Jm#N@M$N(sÌS_ZN!Rwxiv0`&4&09Z6d$|wb~5%Az1GPLIN YEC'9b[Qb/h:0:7c76cu WRh(9o8Ҙ39m8v}Eqj2S81`lBJ36U=Z'!%6;q*2>&"Ws2cBMsEbD+a'.4iF)#~d30 $S4,S! /`*>WMj/Õ :B0Rh,S*2)$ivʧAi PK7p!3w2@zB67FBD8QTńRۃy>y 0b'a_ġe=P]; %=4:w|q|F!#[$g)b.n:ӇZ&mN';%&ccG).p(n?AW!Mh1 #v1 ơ0W>L-Db'N;'*0wC(6 [ЫgkV^ (,'2ӛYl0ѳ"RP%s? FaT' 5要07y?DH(_MGW/ֽ chW%yL%A@ai DEr | qYhYV^wR.Dnе*"#%sE-!Y   i:,Иr!v,YG[-K2d1O]p@Fᚤ44Aq#h_$rh| cE?؊QI0_`h$@Ea;͡Qe)Ah.C:"qCjn|1&g泀K0v@=AMJ"]˓E:%?(o$,:@fy@e`qzշN;1k@c9^7n4(,f @0pPMَ>eH^%Ks ѱ#1ؘWrQvTҪp&x&12~=a1ᚡ@S+4hpf[$S50b1_!#c%(~>`t4YpYU1WrB4sp '<$. cGt1ZQv[v=% Ap-vK+;iA$|9Pj[{Ax:6X>ѲfM=4Haz<D24J5BtN00r) u`9fFd<*AiG"`EM5s0P%Ngh6U&zAP2qc(@B0wo>m};w!鐠@fDx0!?N-cE - :">|MjEhѦ@4$HF-]SL5męSN=}yʑ!G4N24iѥwNiu@:%Vn@aRP4HI$2ȒIQNbtPfTj eqG@nN*rӠƉZTU1WFjn\,IΚ - !>Z~pM/9D@jDd&1 fJMYDDC,6\ĐA|" >D$q1^5:e0fPk1%dE@A& 0J)J)***˥SyJ x?DEi@AD3DOdrΐ:h0d!F\àK  JQG%TSejDD+47PHJQ(Ađ$0$IOgSBKC6TKEY7Z9FZ#iAB9d=^{3$CShGhAD1CF !D\͜Up /aV*@1CZDk~%yy!$k`592aD f 6@"&H"Za~`- P.Z`@DL"B1*3^]fVA*VYNNQ q%XX% C=w^9U 0tA  HZB=nD8 ,B :b#cM6?1 R/HSPpK`#t AxnX)`%/f0֊ x?K`@A+c)6 Ͳ1K"uC"6$&ԊVf>Y*bBNQ[C!x?Aqp9Sm BDo,j-0_p fP rp)x lC-aR$@&\ O4J$yP\"9A[Xg(>'>rC Nc:>(o((Wm88B ԰)`R! jm`Ĭ:5oZZ{B s F,d  j4[SSpH"p/bhN0G)G¶Ne0FXŽ; ޹9,hPk=kkCaQش#HP聛5"0H sr %#+4p"bZ/7AASi\*I0bk 8? ѳ1>06Ht1ɛDHcݘi80 Y'[I` YEpI 'B;ى5p-(% |3 $؀$@:".P'h8ȈpC xTǘ9D*p2Ȁ!ax| H?ಞؔ*,C8#ddCkҢ-g !<6+D$LԔ"&D-h6>XL!x m2,h1rOR(딄ۉh I(ԨlaEXp 90S sA⛅Ix4Q(6dG*8$A 00h.8K@ \3ƍ:I<% _ُoQƘ@9%P AؓK6,_҉PRI2,x=d8剡ќ a=!$ӞK:/iʟDF*0MTahO;>p/`ГLʼnZԠ(9D'Ox<8\ A(0=̀bΛ)MUp: X 'H!5ۂԝ? ]DhLnp PXQ@@Ha4XVMαM1xiJ\Ha7()ׂu h& 0LEp캒h \@`M @P40 sR)8 70ؔ= ! ՘Q`@ȎC(H@B R Hx@Cc '` v+781WD'kP`m1AP=Qَ"들lPVژlP=MaB(,0 #y!B:)?(Ԛp9\eP<)ar BHYH':[ј ҈Cx 2L,PFXIY%MC0-XBĉy(`7Xw"t ; G+P |#iQ G)E`Lf'7xY ݰ!%*m/a% D_I-/0A -[z ֥ ؑ TôMɨ)Ёz`DP̀VY  DD;  AL͗ ?M܌SܕB8] NG,M(uB$VZ$Ip (xEHObrё50/X\+UP$> !Z DVfy@^:N;ı]j"H@AD\Wu]:(`z[[a ׊踋?AVRhB88rq^`>E7'M(q)cb ̐NR@%5>P0XLJc’P5ƯA]!IFUr ?Py ):F"v9() 2Ls29 OEn&?B\@^)! ) ,ȼB l"ҁNAXڌA87@ p%+ s/4U@V) ^Q21 vs4sZa&$1hMpD+X7X33Y%s@%_^:z^j'YPrM%P5 DC@Ho7-;Ё"Ł˝(-`xMwQ'-FICx, ݠrSUtp!48yP*sGB`їGX'iǃ9D3踂ИHcyij(VߧA|h„ 2l!Ĉ& 4(ԬS- Bbt ;Zp q"̆sJhIA >Z-lHХB-j(ҤJ2mK&1QCx2u'f0:-ա@Nκu!Fb" 'r>d*b895$Al2̚3B$ɑ$UEi(n`AuG;:2t:a+6#L$PKwa`rT1Ɣ#' :ڷmQPih+-O%B& O hB[VSCȗ'‡!\ !!x#$t "rHP12 "Ͳ |AEt$wՓ$HTq`q u/pE dB!$i&86Kxn.#NE y9mMHl6e z$“#i1B 7%EٹB)"LR~*!"F 2' mH!b*D" qHu1iF_xQnd]+gj$r",TgJrȸ|b eD&P oL>z T]+i _\- 2ɹkD:"!P]2 "t'tI¬Fh;e2 n @gAc1I #O\"&QoU H([6!,rutLqBϭ l#9CH"v  S&*7< #r! b h[m/J >%m(lֹ3LĚNa rP>;!X#)tz:ģ!LPh 돛DRhD5 gg yDT2囟$"!. ̒wl$c\*0+xpbDX!$xVy8A#A /TDܗ"qXYp!hC N$0C.]aT&΋ξB-6}T1D F\Bkџ$* ڌFw&.M'cVOх $! ?BF m`!@Hy IR2&B 'A8QmjHRMJdfˆa#@ 0)@-XBǐLbPlZ 3$_J2pID|ix0DLjLa!+8Z )DpẃR2<gD<0()% "XGNAB$r@ #!D ш߮ + `X&diShHO" $ M,tOl f]!,rC ;V$1#\)n5fO#>>emxQuD xgH W@5k(vV7ij $0DPNBP-sf.vat! :+n?)FsۅQSl8-4ÐvM78E)<xxwHBO^MdjOB!_嶾"+Τ6Mz-&9('3n_h- i xЄ`aE C ?!\Xfe/x3pSMDnH bVfEL 3E":'nD:@ h 1A!r1cp!"r‡/:k,祔(͏3.  J]{&DPr%g#PT[l"v0^.\R!SySg ͝3#G1#ܔE$a\͂;13΄k~P`ul ;; J|>ADwHA. PC@@A~/Cy p +T8'~_R.򾹡$D;Ђ?[<~!ȗVg~hD[1PfWA9p! O0j!ԁWBH#J@YLI$hbXѳP)ar`̩DaB?#8\ \(e|YLBV8$А⼐$HVΟ,"$Nf΋&xA"0dB,مm!`[ |>I· }h B % )LXNͭe& L ,E@y뷮"XzA@ hOE\pADrHvUl9… (  h%%@d0eehO¢RDlm$1|ҒKU)l xi,B"X2mw ^llƜB"8 ,)3e!nǖ ,b|-?ʭƜa8 ,B w5#XrjG(-BimrݾθR$8xE"%P.FH-AM O*}) @y%B )lux@ICA$ 2HbMbYcDJBj&/_BA 4QnDdZT*=)^:N0@XAʁpSULxLf6 ~ 8ppY].AQA& w`m#tIIbTRW"A@ zh oRDUf" d%!eWV 1 󈱝@v :Nz'!bQG jm,F0Z$HA AdaM DQ  #zO*%`  l4f$d App3m_ AtH |f"Xc:ެ, hl0l֚g3 eiQ)puXHG i!܀HX¨ KלY_6 ꥶxgQ+,0vX$@u謁L@&! Ef3eRM'j !", AD͍ PIg0B8"J ˗vHB܁Ol$=3@h8#8A8a60ш;sL}3w,$H촂@@ mK`/kƩw8wu! UK P#s dyo"\:ZxG5g佹#DB@Ztn-@4̉@YH6@~BxoGd{v0B_z,G8 ' TYtF .˩>P٤FH=fBߌ`1HwʔsA TA"X}0@Hz<Jg+8'5Ԁ"<]-#8nÒ@J (q~QbJ!SOy ,k n[}W T!:3(9 LЈslb4DOm "CxM iҹls 2 /V8I@LլYrdaC!F8bE]cGIXR9L2 n:#CBMlu鐠C$-2tѦSN<0"PYqEPW^2t B_;@( Xa4< X+!-dBYTKyd\eu6^@B>nH$7ATtj4L$DFFdMBZjCIeE6QExb8M2EH8_ۦS >9(!n,E E @Iz0CLxH}6QXϏ&DXFV=hAzC ق!zkAC) *=GD# >(n{zMRMt@#HA |C"DA8vb)D MxB0(| pc&GjOy9QҢVA(-1;ybTpgP X&D"Jo.AC/{>`.8*5A X`LC Z0tA n"V@ȘŊ$6ˇFxeM̡1(he tn`5% l^'ِ=$AwX(a kM~ T-a$0P6puC+DP,]CHW+QDaUՖ()VHҐHBQp'OA /xTAv-PuBJ/؁a֥c•~ kԲf9>j86hƐLss `ZC"PLh0Jd!T9w$F9 j1 )Py^jOH1KFF TCLTX IX7#²e\YG/[n/D[> .,A,PAnpl!ɩDz >L@X!\TE&+{{G!.zUvAh8 C-'tPB ̰K܎cP!t9Ha4D|p*\Z̈́Y ~Cbl§ rf;,MR 5Iv` d *n@!dJg 1"DB^H0҈&+ f g( .P!Zq+j_]CQ~V.ҫi~l( ?d_qcE,` g!tȻkdzzsyӹx^S<{^CBP  @D L O3~n=dFBh~Lؠ: vFrO"$ p B$a T@ M(vP8` <0 !& ^ Bj?@l .G.d Sb`~X=R  J :0.!x`T$^X @΁Ĉ7~(V-Fj-v a L dae< a Dpޠ⪅ )j4P)&Z LAh v#5Ba T=>4Z)p ]qbv$ap`Z!"'=jo.6 T wH8~N~?jIpL"6 ~v0N@.`)a& 7Ȥ_$R QxccQfqHf,NР/d(La >2 T:z ##|c ɅLȧrbLbH1pA gf3:!a rp )} Ԅc^r* .F%%@h @@ D@^ $#B/Z T( afnI_0sL$i!|Hhd R ` A @`j"1C *CXR,kdkpP#&/g^);EI*smF *;# ^v "TJ\X !.#31sa&jŏ*d)KGHhAJc e9 n,4)#cs%2^k p-D $9M$::{Z| h͂>B;!'(R۫` :d4!#nQJwc9rr EIt{;GT;d$rXI.iiPI%\`y;[CY3XZ1vae&d{H|y RM bX e-ol y@dcDa @.B9fl&8hhP8Uhi*__;Bu"\ZF| TC+)=˺pzWs^mPԆI ݏ^er`h"i& : n%` (D vIIaԠ jZ p폫yfUq73i0e$TL1/ّiȧLIK'f+*7KfxXPc*j'CQ"#[ BփO' T΀=՗gl Nt}&J  9Rb T dž]IXn ˿Shj.^O:Ldĵ)`Sep;x| Tki^¢;ڭI% |}=Pswݟ z>,E$6#Hc<'f!پ%"ڙ+$)T)z[̅=H9qc:ZTw:9i LI㐷5bD#8jkaw%]|Dj8Sӕ T%Uf$+eX vzVB"`'&[一wYs4m" a8EF#x#3ۥT)jEXw,"G <0… t)DMjPM :`+C02CM?& d( 3yPYtS[]2gMzd Պ"xϡΧ[|()Fz֩Ar/wuP+. rkLrsP+"I]2$\r%B%bnhpg4b$ " H=)0R%H1Wwg#cpBF 1t1#,]v !|,N"Q!w∌@ȗX. "v !i( K$D} 2$Hg ;*_ ˆ|ADD%)Br$b!AFlBF!-$q↢(hy #xx*_JHxT!ԝbDiH a}đla: q E$ %f,X$ A A,2vGr !4h_00x,rꆉ i2#~0 t!Mȱn"#"βVh"2u׌~#l),$(R%H :JXlsG!sBF!R {Opi}K.")Z]*Q!doEbBX" {!8TqEp!,# ! 1 B|ESUAn]!='xQȫPx ȠZ_ԚŴ."-h<(DLF t2 6ldP" !N&H P,!L+*bf`vS1/`hW<,Bх +$ňDʞךM \ b3p#Zȫxɇċ'@@G0 :e pp88A[R"-eiAW6tڜ0XBB20~Gߒ &:Wr0I$bZ1t(~(qIb}C  Da4B C nD :$;ʄ0H8eT"mq '"^9>̭=2[* h8xdQCe6C,w;K+A$}@ܫNvbCn  j"C(7 Q, 1 Ϙ* y cv3fsT' 5ȨqJ]g&q-M(QR!.$P6V" d QADA@He2J(;c!A̧.Xs B0g(*1`z1+ Y: iFcAoܢT-a7Ɗ!\kP`jP^mqh9FslU ȲQQG5RɡȤ)A=晈/sġ$muZsȃaEX)d6aV/[[L5uQDfA呌`؆$³lDqU[DL騂gAc N19XJ0V$2%"[/w4A[jh*nݙecE B&H-F,VuW`A٢:n~d D^`\ *Z1!'/AIO"@LF\v1'/[`g|~-Yj:7)Edq-玙͟ jā_IDr0 *Bk ! @4+Q }%Zվz.S{r)֮Iē\ %>]|ے`-*)q xq|Ń֙s.B F8 aavA< *¯2Urs6#|\_OI+.7CH$2cꃔ<$ E2?DK6&TXA Z#7J!p " 8AÔ[GZ)OOisr{4e{qFa9xN$P=[?)"V9QF^j*A0x ~^o_,Al4A R68g)}i(ˆEG@$=01g1="=Us5w%R.kp O noGF .K%n6aa!4FyyT~s׷"}C#?ze}rN 2N[~px9WLa/z!"3+X?ؒMA+6.^(eu2uz5Axq=w$L{J|P= 0 ^GTswU8l*{vBt! `D}*(G"jc7'-a4FAGa@<mDžAt i_[ 4LX&'78# ˷p:iæ3RhK 2&rEH[K7T#Ƌ+!pC~Hey`fHWs! !;WfdԌVn8iUk2Dvprpat1 (xBK(t^8pQCF0 %Hb!T<莣P*"Sbk&|3D]7U(zh8ߢBL~iGea.I!V`,#je_|Q9CV!W_(3|8FLzE ,E5X?9vr}ZP$Yxiu |QyF2Tqe#1W#lu+"Cc_&#tTeQa>3d!9-L#_[[$g9'b:'6l+en/$"A幒lqn\ȖzT٢/gO_ayejr—0ǖJ Z(pq#zB-ybڬ$`ʪŖz*tocMf<}B"['H*FH3)yI-$e8V{N:X* WB, | )y7Zj$0Sz2&o9>C#!@ +8fx~ʴڇvRʴ!41@`];'_'y5Vp-Vl[F@n?;.1d蘬:\N}*W롽ۘ10[uQ}Hj K#ʅKUk'~hZkZ=!0;$([H)esZ:iۻ{AbF0˴UHY gkUAC?9qk4[qVEx۬V8ꎇ ~1kf鎭rLAq[kR+>kHR,뻸%V4±,yҹ\KV-[8׮:=<)B0XXXl F( 'P0` B?H( %)(@ЁreBE(d*(@@,@@qCF  0b&u`@qC$c48lC)8$@Yϵgj`D `Eg1@Þ2#X` ပVj  k~4˧ 5:eH*P;,u ID&@D=֪lO"<$) H,mrJ+@pˆ[^|dL~n?9 R 8 > 1@Ѐ08ѓ qxЂC' = QB'p&в!I h_Vl(B=ٖC" d`V 1+S^)JU?j!D>@T $v4$ "S,- * 4XhЀp?IڜP 7(J0֌*DmCЁ\*bF  KUh~2 D$A002l W H/a@"tM Zei'  h U:meck9 X&$?A0Ze <ԷV W!A )EA8A%VIP,ѝy54x)І@@6;$ I ^|#I[81ܕO|]h +a$  @a8E h g cBE" HP T4A2t3T!PXKPiVx!Ё<>!0H点f$d]) 0` FvJ.A8Yl A#q`mBpB hV@peq%*0vn SNDo@BA;@ X >e+;']B|@pߥ @ ΐJ hBEpA0ie `j+:xD)`Ap7# )`A@5@ԁ @.QE+h iuCj_!R x i6ϸB6@b@6&޽B$xTdH= |vA6X RA"ANn@!~"@ 1AU<'H r i'ha _~CP@x@?*IbF·.gSBErsAu `p$A.6 J@ X'0v8)^  0 t`:0 &[ G3qSb S r |"_W 9W@5OwՁKa0T;A=+Q QRpU~CU6S"K'#q|Tp7:0'6BW8%'`ReQ- 1`A>XQP膢؁hd1>@7Y'E9`GH10H+`[E#x4X`WtY0>pXQYh#0%O! (F>P%NdTI9:RHi|pHqIFUS%C) Woq'" Q UhВ.F'`Kh }vQԓp6H5!y:7/0@A# Z@7Pp:PdF*@q$aPQ6>plI,P$_A8"04$'PȂ9X4 _/0u@?ZcPk>E)>|9%w)c*VfpeC@9 7JpE 4%aW6 䙎@`rEps+HQ"AQ ^v `-)j#.ESZ8B6  $©GQ`6?Eq:@L` z=Q>r.%1>&:)'IymAz !aV /hA @Pb:(KTJ yxC$$1>dc@!3 )hRHS`"@!@9qv70&D9q 0I  0Q7@W 6`HTPڔmȉk*P1 +'$ABp"i0) i ypt ` F@"O> $PE-&i 1,0G0 sb0, (*$*ZL+'dE p"p1)p;`w'PVh;P 6(0 p ~if`:p7+PKC3X!c@jPn͸7Jd{*읖dr hYpAp5;@%#P=aw7"NJ$F0@p1/z ݔ0<3v YUa}CJ$PG3P#pf$Prz+`)6 ['MO%`'ͺ'Vo'APPk:0E` ArkMqcEp'r@u/ ! <:cI41zԞ6W 0AM*{5K07ЧlzdrJ>d3 =p}QvƋN1'^a pT0+7+!ۧ)}x;0@TVdBҒ2!@\!(es3/03 ]-0Q%8{E@;i,b^qP5 ![2fnv 0lÆϒ@A A:5-9)E@PX*`!0}M+OeOq :0Kܾ]@!XS@l*6pHV<"B^)9H - !Y0v)p }%m)ir6`F<sv\f&*" qL-%PP8Yp+)bl ,l 0% y:$3 @%gD` &)`) L!U Y#/ iW6P~0 rg?r~hA6 P é!e @>l@#@9{O|S4 yBP9p qgepG$G9`.cUyr0>) v@ 1i&:skܭ#F0MZB@1%P )IXp aI+P,-9OДkGNt5` 8Jr!7 Up,HBQ"CA 1QF%48p`%I @ႈN$0!ADf%`P. ሃA 5B,VH!6H! 28AA!, IHv(B 2(al+!IaT  Ă|H r39o ! +:< b,RIEE !` Nz: |B+R"Z䒂-!)>#Ta!R(C2(ȄvMnS reH4ʁ@ࡈH" 't:! ``z !,HY4H.2Zt2?x rxhA'Zv@doBYBnʠۈ*b*N, ! fZSf %Bk͇]!uȆTx#CfD(ꠃ tm  P@>ʃsVBMa TAu7CHp;&(؀ /ʍ%(X H6F؂%28 hPl7 GXG?  8۸f290P!@Z 2%CA8; p҈#H `'07' ( <+7-pP)ԍK$L.Ȁԍ" ,`S(93%@ #XD"4?$D *ʍ, #pوJhrhAIC ԁXŰ89 x=BD"P{S$, 0(38x‡ЉPv8%(ȃ'X xhɀF-!/ 0F0$H` 6,cE@ .8Gp2X" @D%xŇCXC؄ P Ń8ʇЁ؁,%X9jbɛ؄36h;E1%ԈVA?(P)X'l**`X7 p( H [ ,`pʿ8s @=̉؄-X9Ȁ y(h,XÈ'0вۢ H!sЛ Xh͈C> #h0̍/ే:XΉ @7*+x|(2@0#pF$ ` ;8BI +B*ăp1(h (cĂL8MP| rhQ)M0ض.*޼+!4H x ʇ0xȀV!2 8I*hd)PSS}t9-7UP60.&X ՈԹ,>HxY<Qчȁ JhрXT]C(W0@ !pK{Ql JKpV8߹S{ hV,P?#h-(Hsg:8 `hI D-F6 &!eڋ|{r4xw# 0څPp<rD hA0XTeXO-JA>dҀS'H'p'z$ 0xBYEԎ5PҍЀqJZ=ŲKȂ@ۈp 03Ԃ `4,k @E0 ha-. ALwlJ18KuIn3G:Jq6:eH#8Eم PN 6]Dԍu\Ѐ AOy 8R-:8 hMn8RK8ۈ2)K5 U); PdVH:HEĈ 8aR6- \00,@= $|P5`2eb85 >‡(4Qx^x@рP!;IR9X̥%X2<d F 5R]ŖI1`߱eRkR&I<@B( ؀bJ$ N/ʉMXT$" ee G:(#8P{5 8` YMV؛`HW"Zz&{^ %#H!`(Ԇx~l4N(!8C H" 8EI> FxH7cip~"`5H] Mq@o G!.g="R~&gdžp! MKOpȄ>S,(V5aMv(hce~80"F8a!V>|8 mXm&i완) h&؄@KT/ uN  dJЀӢ-_z~/8݃`p$*h;&ㄠCjh!#q*[lo5&vi*0Ё e3@/>$`h8P} LM΍ n=Pdopf$ 4$3- hP ԙR8 * `ZV IP\f2(+M'g5ȀhN& 9s?k! {nFm(<WW'h  X!00)Pppr*۰ת'8m.7(_e0 n8"hE;pv84hW%8tD Ȁog.5s8Ԁw$HfG4MFXF`~Ven z%2H=`P΋Ip_@ۆ1ӣ XIwPo"XFc&08 ^̢@IXC B0tChс o:ȗȀLq%[؂01ƂW>|>-:mA@`NF  hqמ  I3  H8?}@8<C/H7?@SEIfiǬx)P?-8&@%./HpThP „f 5) @ 'Rh#H2,i$ʔ'.LhDJAN  Ѐ[&a.R**j# (]D(B $&`=ZӛnM K@VP PH,`VԮa5::^EkUCdi89#" UT`AL圄iʵA,z0`WYiLp%2 iNASB.h>`8PAJHW}DH}4@=0o_%+H_ov"hO҄8E @Y1CJ$WB$:`IV9\M au  B_uL h ~D (@$ABLs`Hɋ0LG0_AHHbD|xݍ^(@˜EEx@ \5@+y!FĠ 8%\\ک!Hl_ ,&UaELS $E<_X"Z4u UbEL" D !(:]@p*V_-R|@ a/R$YFEh#B#BE嵎#7: 1<9RD߁@9ZY d?\=#0ک@d^,9S ?@ G|@?_aG `C${ Bb`@ .P2+<(@M?2LH`Y\B")L M0/ 6 "^ e'kOBq bW&A,kClB'kܫA$]\-+{m'Ș6)! uW"`?d́+v"9 dH 4|B H&#<."q_AXJ5{%hc .) +@O`~{! : Х'ŋjhW4/"`}@ QV'A~9U}VPv'HG`|q d>~A>@tukOptGdp@T>% U.G"Ax oPi!P-E6+Ul~`B-p Q86T}b`u)@*w[FAP%L- p@|Dp)I |p#b>1+3Yt %f`fa= qPВlD|>"KhRA= p 0)}alPFf )pmA0)8`kr8TӔp*s0 w;IpHE7)A@jMPR DeF(r)=YF!R>7)Ù~Dx)^U%A5BR) oX ZD;)"@R@s9#NQ*GG PYyEd?])#Bթ% Qm>qyX$UB? hw0D(Va>xP.,:::PEI!؋RRn2p c@<)@#32q##bM)n{<*5' V&p)@hIb@ZP88)>B%G X:$X +iE-6 rP&z=4q 50J7Fp T!JqtJ>$:QZ[gDC3}W +] ?@6+*Ck8OJ^РvjT L|OȖ_1z=1 iVp6V;E>>!?f]Zy 3`[AbDX,"s2z ~\4gpfB! V!P+ue! :1`h1`h 1Eqf:" 62P;$VKa<@g)/'T#fHvW#9[ QLma`]Y8[q0@uBtv)HIJH_?%"`,qѕ`E錖L٣lB*#q[D%:31)`ZԴ'7/%E ˞F+!I`&4z ̸7ɚxKAW0)gÊ!$@)ۭB%.kgI,beVpa3!m$)ę0vq@vc{ O@@z yr1& ZQZC2N!n7P|pXɭx+EbklddN =vL;j:X.ekmA-i!L2GJ䡍>ٛۓamqU^¿a=wIx2^p!3Sp>mJ׎a~ח5`qL۽q8ޭ2Qo@Rݹ0.KӤd@1F얝Hw @K_M$j'fVt?߬-pruA/xaϊ4xERLRA9ToYI0ta4ŋ愑JN_y U'M7!x~kO_`O@_|O5gk>_k/o CzR ty'.2REA6Gy7E㢏+^ݩtkt%)%:>00` kH?rG aHI2 b_eO@ ua‚:Th!ChQ@ ;nr#D%MDR"ɒ4J*3~lRCF<,͎:%XRMUEhB]3փt@H iNX^zVڈJCNDR"Ͷnڂ"[u FrRq$g@kpC$N?Nm VZFn1trlֻdNEgVkO^}KOD_=IB1Ī7LCэqܙ}w ZE]qfݠрGy԰gYo.o7_ڌ?eGC-{1F_Uvv>? /2g!T \  k9ErGA[Ypo>RAxpo!DH9˄[a_x;M0alxCPo-Zvh1 19f$*lcbS))6-RĖִlhE "cm`1a{86lBkۮ겵mnu\:z̅Q ݮpU+xݻep1 BE..=[C^7"lzj^Ko;|ri}i_L5 6_`sN {Z*W60QבX;ٟMRq8b[_1:[q 96r]d6)'xU^&[-ot뚗cCK̘%|f.csy|!,BJv H*\ȰÇ#"3jȱǏ CF $N˗0cʜI&&J܈bϟ@ #*C*]ʴ)&vqJիNO8ׯ`C8#ٳh,B۷p8ݻ1߿LaAf^خ #K>BY3kEY7 I^ j̬c˖ԳsN%ܻ7қV+-CK@BkgA?'yBkAWy#[^Pۺ?a#ćؗ!LJ e?, !S$"RaA~a-g؁H $lb/`#ЄM4p!lВHpY~2IfY+HtB0!]C)+Gn@K":H2!ʈYb>]3H?֑1QC30Q$D !EKM6BlA )xɐk}r7R"F!$> JPAZBeG4J/Cq9\ -!@ F bY&n&bHմ5J LFbMCXzMAgB ÐЅ` DE8 h.1 F:DEJJDFpIhi"&;@̈. T""(NBPߌ&> Gi90H8DG@"UPA+;x` 0)V H[ a>9BݺQ? uy C/|`) }9 B" p0^ ,v# x@"?,W#&@lG/ ~pq;\@~I \#/Ѥ~ 5@>@H)1ZGp͎@B^g0@{ߦw#,AYD a,;xFR0diC. CԸO^#G@FF'BVة9 j0SPybp Dh E" xˑ(s4ZeaȀ24  BD?! )D^<t$ !A(o<@6/a.`̓xFWRk=?Z5Kr槧bh)U3 A:}D3"$u Q#bH#OuGCĝ%J^&j 6[,AqDF|,A" >h^w"i- =/+Y=rvw$Yٌ0 BdX 1)ѽߑuɃ@:CiJrqP*"`@_k3P J T P#P q"!AGE;`/SB`w12G9!Y!8P0~8$XTRu/pBeHviWvpc~0M@g ;+0Tp\0y9seh-S%! !G"poS k`J1PT@%v~c^ |H]PA$À%@@O0&*`05PݴX 6$`@9ۗ^p:,+!`0BN)@ 0'#?6~}$S8.6C%0$g&_0Ap#SIK)`##6&pP6=\6RE@&pcPat2-~spUfvB<aeC!waHP@AVk-``!7tzT`;"bSCdx hapCGH#pSSqqJh-7@b0\]Q-0 adI^F|p _o31hUa4x6+PIi1:h-~t}@ `a&AwGֲ ~0T@ gQH|oBghH9G R2$g7sKA 03X$XPOu!!3Pp(`pWXb! R1 /z* r0]JځL47p| dH A5x*<\QFQ" @LTvqhE]7xkEr 0zN)P ~1 p %ʧALesoyPaaypD"8v1T7J*J0 7M!jaN0JfxO,X:4F?z .*0p @`h).,p9*M?0E4h 70dhI ڥGX`>6d')!*>h/1$ F{/0W.H@G ::`e;K5>>E T~D7  ',HKJ+JJ-  'CQ`k1c;)026pm[4N!p-K3%g@0KI4%+"xqG3 p^Q1Q$0;,[,`q!=q[ @X.@Io4PLwB0AA)yb 0cҫ$73p2_ > %y*40ߋK $C6AG;9$"r%'0"L0Ja"/0GP4V(m#L؋HRq%taë!;Y67 K)@$K)'@$:@>!T$^IO;P(p6h-{'*@YH@1 HbK&M%&bSwB"d&(Cl 0K%Y>x2 RKage1 X!w F r d4B1̯ =(w0zGjUx'0@S Ip3BѱgiG<G04-!#7PP@,e@~δ҃$@0嘢"hQ}A(j71/bjr? 1֨PTq؅rkc.fah//?@a_OWδ+z Ph,a"0PSAu$D1]#- rE0&@<`wPDŽcIp$2㇡="Lq.BB Q /p oa1O.6 8ֽ5p'޲Q@aVq#_a6>DIn}! I,BňQ>/O4`a,">0p  ˬ2N4H1CM7 `zsA;*Ԟ[A!c.p%J'.lN4$0 }0 $4S 030p$@CEB6(OB >QD+ `XI%MDRʅ0.4bD!:RN?DpBB=+B`aTRM)zTT!4p*~ b*-AA ' raD$\Q-` "ld/(Xʝ^ Y1N0ZVam6$`n(aHR2x\:H?T_Pmx&8*<`0C+IcK! ,;B 6 *H8 3T! Rء0` "(qx4yH 5!)V0 P!oA26 +u": xb I8Kz2 MX IEjޒbgM΁QXl'wHBF`- Jh\lz!(.@"5S^- r (u((H[A$ MV"!r聅.-`a3H ~xV`h5+!!%bM:!]ݵ+ :S8#~Z .$Spc\Ig2 @Vr&`$40VhBX@23IF{3Y "Px`@Z oC8;!&-W@-%ĠL@ g4CQ@!8F\ {&;-@Dj,y7' l`rDN L E1| IR:Rb`V:W C0X#A򗀡pN4+@ 4r2)X, T- c<@ zL]+Кf<Lr\>J5Qh`h1.f٥W؁q),*D0pY@Ȃqh0t+)X@K,"I3…(lPO͒ qTX&^0b)!>)h P2%j>`XTkS65MHyW5__0lS. BuJ 65 j5bv)1h@!pF* !J c[-SSEe jh[.dn[  <3 = ;A!"Xepy&)]##ZK8|l hC!h4)BlH - EW Ak,$vO(0Zh(@6&8ex V P7vlA]  '(Dv13PiKō04 z PqEBCL x2DNxvQ7WL@'XB^+9\>1Av1@`K?  >_05( DCVX$ 쁩A jĠ9 X}4=A`09I+ @ކbX`GVDu@cO' 5(@sq z"@D}$X [o:HZ 7% !,6F H*\ȰÇqE Jq#Ǐ CIɓ(S. C ,| 6LPA @ JQ#.HC$d8qիXj]B!YhbٳhӪMbBU8XCCk˷$ XP *^X R$-X(ܸFon8YGB@A0װc, F$/8_ #d N\E!8A ͋Kθ*3,c^(B)@[M0HoA2eB C 2,0>4  QSUBGvaB`@3ć(@_ ,qV,8gDCkm`"9)(0?G`D^ (a,FV@lb嘝@g 8WN!pU1X x!I+q’@V:<01Aa.jQ P x"楤H sȍj& @u#|kHk!h#& !XlP 4.C?l JՆKPs&l'!Nnp.l#ۺK=1/l:+KjAYyľR0 1Ƌ+9  nyG 8AB , 4D<<,IJADw&tD5GKWۖ@  D%BThnPLt=ztٝlsq x0H@Dp&(!ejBMw 8 T@Ѐʩ_:dzBRv.}$@t12\Bȴ&=A1D!?eCJ|"> I"B<0C֖B T"0~8`zУP(< DD5`/ #x(I` V`CT00(4  !Aa39/4:|@2LĄ(`yŐ BJ53DHeAk$8 |!]H"K\PBlCP `9" ({ -/H&,HqAD42 4MD +BV6!$ xjga30b Qp bd9\~xSIr('Ė<>ldadZX,I0Ay"QZ!J q $) 6 JHCHfpeA"It$Vr j䐋<B V$#-0Oh s[ wbF0%*)ߢ7>XDa# 겹C4Z="adYZ{q/! v&9`wD@0U_SvU:D:0C q@- zyx@ 6T=(>s(Z@rY Xe c@שGюD!?{ @6 #$;ޝm?TG8CKc&; 7esZDz0A*Up| q@=BB$6d&\0t>,gp @;#S p @gۦnp _PQ"xFg7 ?p pPSJU/`fpr!tu( fpp Onh`i$*JV~5(HU4l@w6pxH\Rf'WV~@hS($f%7{a3%Q۲|w-pe} &`z@Pn*14=R=xwu@^ F;UGTaVtbq 5-$jzM@I'q\pP`{* H P2WV[w bXP!, TVЏpbA K|6|Ĩq@ja2xXBAgAs@6%d,p[)@Mq8XA8sh("/E鷑0&{rn&1u#&qQdM2\ i%D\A owXlqb0É8 vu{wza_cpa 6 yg=GKД1Q14jo!)&(S  y3>=s S0a6H1`5pZyW\@i~A$DP ˶(TР~-0/)'>a @3Y&0 [ a8x&jx MXo| &h$Npl:w*!)ii-0(-U/P @D)Qi x'!elBu d;(ge@q-Y^HX9PyrI`[ Љglz:*"XH0hQjAZ@`=XxP ]Zk-# %*`# TI `ĵq)3$%*~ @1 *c?@ 0Ъ%O vP.qzR?<۳$+*hp40u1kU?^ }D 4 "MEߺ juz:M5[Uh~H]H40g ö7Pp˷aS0= y!=t `8 sRq2I=  ) Jpo.v ZKkۺ Y*.6 X$5PP+;DtpQ qV'r$@1k2)iv1'j?`2_*+[k0Fxk b+343> x6)pO7p+P-HcE>+0 CTf|`{I)U|о%LZ# F5[=`X|B5[>pD|?z@ xyP p C8Xk|=eq0t3|xs ! =gw0kkբQz YղfwR2K\~7qCj7Ogl棌 'Pr<&{06l)*H!k\la@¦>1` '|^`:?(8# h+P|cDgud2jF ;~/dV3 rL*H)! 4p p !1 3Xk qq 23oSsd# 2 -ERU@"2ZZ; F& .4X3 pT]ۻ,4 P@ 1>ABmM\-#`Ñ/#<_M-@:|@gֈ}(!{xK=ө =1;y oG,&@:zq\RU9Sb|Ae0%P)^*%^hqM-Pgo60U[Y9' _ jV$Y䊲|Ct[9qa1cpcc#'bg^*.YDKYp'MӞagw,+qS(b'v(+"`B'j2%*"`~7q .=` uplұ#3`BT0^K3$f.-J H.&P}{a@6^_0n0k(6.$7sQLUGWݯ!japzA8/Zl@,3-0fJ1=0 /m7%__γ7Оya#PpO0 G)!ܩESV1%CIreqvc4!Fsi( ~nz?tlHz#a3C0 PA` spJ0?<‡6T@Ɔ<~RȐIDRJ+CIBɖ(ud0ƈ`|$PETҍ 54C4AS =&Me͞UibĊbAdX@@ ;J{-;)$OP!EDED0` lofΝQn6LB (D@a!Ml A &Z{,wݽ}wrD bk LN`DToٽ[>҄-<8 vk9{hË1 P>>L#8Z` ,`J@30CW8A JX@>ký\d1F&1_n$? 5#+Gw2Ir@!t(J+8.?$-H4K4\5| )l:=lEO;I@ς1P7ɂ?'!DBBRR2 OM=l(*TQ3RSUcUbUYcd[k5^wAmԥh%ILW~mvSXdڳ DM \oBDqͶs9K(uguB-UWz?;IMB9,=E{ÕSQZ I1$@TV)EqdޅdؑQ PeU Bt=M6SޑgSpxŕ>%odwVHBoq&2G8 / DRx8BBh.|!0Cl+i^H:lh1.lu@?hV-Ez`M쾘Xmh% QCGDIZhGܨPS :Bs| =SH%wT$.229ED^2*R5Ly23S5Hr#NJWĒ=bUjPL$Q."@tS0Ѳ1bU:hFjt&n)gfT5 mj ,4rBS':jSؙOoҳY=fRlXlXJ0JA'h-CZlG9ѳM]JUӗʥ5LAS9O7ԔFR/9 MME3TE2S%0C18QU 伪HCY J*k[2JΈsM75w`_B_-3Jխ1Hcƻ%e5*[nu%JZ3?h-+ђwZ!'k꾚MmBqۮdqheX{ u\pYί=/S+JFhӝsGERF݆:/xw$EJwk5˽/EѣWb}wr(f|)ޯ<ak HjfX37Ҽ&,.˚dSR;j|< VEՐ&Uɕ+g&mT(*%ZΓ1;Blf1if3Z+7ǹ-s*<{&ᬔ!,67{ H*\ȰÇfIHQ"ċ3jȱǏ CDP()4˗0cʜ)rҩA~mVJѣHJd(P JJիW] ăMybKٌ Qb׃N:Kݱ~8+ >6 eÈ$4$ ?61z+)3k޼pӦ.C<"Ө5BsGA-*:c6X#߳#M8MpAŌJauCN U$T2P4ͱD ȠBQFGL&EndX.ĠR18@GU(bj҇Q4Gs0⋚B!Tb "h?-lD?;iA3 !I .A&UGJEL0$LÊd@A[YW| Q0"y@,Td`D2hz0 FҠ3tB&: ,H**HaE{>B!E?d)\ Mrp}jUVY"k@1P y lTC^?P @V tʈ`אHVz #qޖDUPP8O+4DϚEE4<1GABC#zw@!T>8)A-1)H+ !nJ4pwp0T`@/PG|WG^O%!32Io>)Pf!%v Q"$#$,DACA!\"?6 dbtB Ȉ!G+0Eaqi` K! š^!It  P56AR&$RGX VBSP,@1!Ç^|YDE7P`B 9)|B] C@qj 0w=D P+,U|ɋ^p%%"@${\{A]2P@7~1?@زbkQ\`Q^Fp >{ؤ ! .Hk$ # ·DI"' H`n3 D1oC$&cI l `h$I( <#aŇXv'驻t r3`r1C- &Vqtb*\ZÓVЎPB P$$ #xTM,b# &Xc8CA<#2huJ q "Lx@0HC".GN>%0 .#ݡ8؀`z@d 1?_D p3R&XhVc" fd9NPp-"vy F5L 1mdC ؖ\B;HP'ȀD`$gB rdN@PΊA>`%   JDPh+@e} B܀`zL h S)@;Ip &11CjiI+tADH G6#:ЯGZhEG.A>qH*BL$H+ A0" B>'4nksX3xx#3Hb/쀟E {K B>9r*Nu{,˥#E gn+ Ba$hIE'fADT 0 #!sD/<cl0L59):FY '@Seڧ!2GZ;We(n eDH$kAL R=4=lPVGE"@1GHb- wA:`~!G20DGk6J $/ qX)#́ n pZK#C@x Afcl C)Q̀8pr]^XDDLlnw羄RPj rn:0h`L@؄%pG x ,̖Wsx 42 s do# 5ͱJv[j芩qwa*AQ&H( UzDnȳ|pb /R j6?iԜ XB!iVQz@ xE&Tw*C \Zz~ 0gyqTjjgc*@^frqq+(0NP~w!nhp2#+0څx50D |;a ,!( />c'؄ Qwppm^d#!V$R0!Itl-A|q $ `Eö Bg$}  |`6lVe,HpSxZ,hL6 Tv; pc9U3c0wW9&9o%t-8b(D qi]*6^8\~o/"1 {eaSBh$ }" Lxi [Ay`F" G[(cV~bF` 9 CwfeD`/xm""eu!Y(t *hF# : !@+MZvz&4%09M"D-& @PhÆ1!D&Q= qo:HLywq iP-aPiy]gIrstM Pw/<ș85@vQ=F t\1 l="xM(qp6W (#m!D_2Ltmw)@C2<.6Y%,w|U# S;rv5rb0Snף98p>ܙx Fwv !P}C`fS5@f#00Q m*@$08Rt2GbBhx})Am<T%4p1qx" &"F6<09 [',-"@5B v3~oq ~WќQyz*P/`%Q`5v{P! ]h!a#0%p"9[ #gPoq`%Tp_$ SM3 sP)J/PuAvn!@0p h!qF413'w jqh p0G'0A3@F+~07Pz03 J&-!u}DW+W z!42|ɚzsa) B.fQi;!6u#+I5 M oPkW0ZPи$ ^ aðaVA/&б19 k|pcP=1*~>RwPV{nq @<;&  @p+@/P73N .L53 u*p$D DI@gq (Q78jj 4@xOBm-aйr;_ f;D,Ii+Lwv&c9W$=/q s"9o,<)#SSe.+Bby`:mrQ'Ļ{0ʣg &)b=aDtɀ)t,PAa<5PF@0D[$nJPGl L4լ%F @\%`M К-34ʘt/-K$>KU%ϯ#WS `g$"QL$UƼ+Y@%e\.JP !#A2:u}96)(+"W! &&4@$vǑE)R%x JJy BJeQ A 4%  s@1bPaJ4m" 9!GQp#” *$ ɥ9h%TVM8X1>,"xM "\LK!Ss o 1˴.,<08 N<5}_r2VN;޳!#}'*)Dzmq BDx/!g}Fv+q:@13d jB.a PB3S#ЬD:/ /Sڤ!q*r| 1jD>F*Hg5 0T a"-J^$U0aEI$&m͗=kbNH4&.}%/C8\)~#`L(`r]/}^xˀBq.D8*]ݏۼ@7a`& j^}ߚ6B Q~%G!.4 ^'ӳ^Asq~i4@0 N/wC l&0`B4LZ#^# TY$A-SÔe) 1W0>#*B*=/5nˎHqe:6o? A(X_ A&cgo[,'.d}_Bws>S_6'GTiMbᄝnS_d" F!O$  UQ/r WW G¿JK/2@M_01zW/& {!TN' // K0Ag֬BH0QPB >Q-^ĘQFZ1c "MDё#C /R޴XN=q2:RO .EC l ʉJ`Uւ6 :W-*(AM}T-{SuqV)v.\߅T 7egqb)"<;+Կ  dM;$xP-$j $"Hv v(@>OXۧ#.$(`T\yF p&DHP8_X q .QBZ;ۋl;$4lC.@N!A.A¿!$>⺻ bz,(D*t1E 9@j蠃Bn@ʠ NJK, 5$.606X i#j((,FhD1J>`h( j%6" Ks0 DL':=c &@D!C U( #ժ5 rC> |`pXhC2>q# &Ts٭!$㐧,"$!aTj tPȆf$a-9sGg{2aWN` DxhI & !eia@} nZjB@MX`,` P@"x TRA#IIc <^`RjYd3FF*F`,P@{ &`Dk }2 V*Hg  ZԈbBitݻO;'p Zu \`,!i\=~*H7?!Z+! b;o~`7" h6;$ mdFĠ<(P9A † .: Q1=)B1 &nbhK+q#8U)Eq\4F\sdјT+XcGF Fw\TяI?2I' H ydd$IҒ rIN d(OJ>16L A,O m'  G$`FX1o-O",@d$() #%O!%AXmnD Lh S#L2p{ʛ!AAHj3"3xMO2@1 N:DI0G(H@U@C;[@?DRH@*R =dd2SR( X?  (t 4ʐ I :d}\U3z"M#@+6ֈVJ \1`An`Ā .  <,4jȑ @ < fZވxV!q@-?pP1Z\`OV@Ľ$ڞӃ$@?;I(E!  jeu n*@ 8&]O`4q=ˁ'+^`d`t_jh H |%ؖ`p8 ,PG1^ @<#@P v *@!"E `Y!)X IfԪBi@Gc@Ӕ7HUZXGsV[ %X}A`K+0k3ـH$]s| 4> > K( 0$X`sKT Er+(*ԠȖy? +hlĻ@Ψ= ?1<ӏ ERA!" p@B %q,C[q#)[C@4ؙ;A[HP= #Ć0 {/l*J x()ĈN4"0؀V;ňH<0yZ%Ģ sS`F9 PÆfԈ 0*F8* ؁ Fq 8.Bڳ>w|B.β@|Ĉ|#8d,Ẻ#Eh9dȇ GX{zƌ؀u y34k@f#PfRhw{ʅ(?귆xBʂ#ç Xɧ?^,ˆ XA |pĢ[ ོ ;tnȼGyܪL#>S*xѬ'ׄG.?ȲDzd͆0{xjTc[N5&ɭ}Zυ ȕ-.8/XHlD  P+p$PP &ȭؤ( ]GbHUR'R0Kt QxԠ hѵ"JRj!z!ldr!١,Ą0rHD[a5@z$oD%;),Y=Gb &:Rd"wȋ@=,a/5vHrFYw$8 yLF.@ fLyLµpBy / tC r# H89k"şA:C|wS$P A-P m4QK >E_O(6E:ӝ4Ȁʨ:# @FS^z )iR`Ik76 #[F0ALbXRXEe"<PEkI醃{ dfiL Rq*F5/F2'FVI,@b#j nًgw |\ԃ~򱝝X32 _bD3 4 5P A\:-h&4 =a N\TAB^Q&^݋|MI{ $ % 䕽KvvaAt<8GG x"Ͱ~=L $k EhG&xI# [xIhA~|P@Q*H){q"%BlM( ze /!4SbXBPCȷ0d$TMS LzITg]rt=+U!b9"6iby@!E$HNoNH.~ Ac{3=.Q:#Yv)슺6H)d7A+-o<™XNQvf!Gjx+-H@ f9 k-l !@3&zY]%u2 -iI$b.QMH-8y .&d9,y3sFHS\y@4B-x͂ĥ'* k{EA|1=ts> ј/䁆V`N0A88_N lǃ}A X!`V!jTK>z)COͷ"@\#` 4ӱ4CF2 x)#b {1y1rЀYd#sGs/Arw)~1O:g98G":Jx&0HB -aK)0|]A@S!b(N#p!l n.~>Cx4~7P`exAw` 92!W#wW3 AucPx4rW冔h540x_h"; !Q!&2?/> W"l|7G"`E)U"<qNg}#ڈJ#}AgSar6x0ҸzX##w5N^Ϙ28%Azs4?{=)+#&0P&p'~hX=GÏ`t `@$00lR>!!r5B6V"iFqg"$0n|1R jWQvW8e0؏3x)wŖ'^ip֊qMr?inw'U^4gpQ"b|9H!oqxBT)~"#(iԓ~~fGUy*P@"p*X!zysiFh5”Xii!얏I 1PF"0,UI Y"00e9 g" egfbX"(ُ  i!`G%i:"`|gyx]h+ SMtU!)-IWh_a@Z!3:)Z"J)ϤQ`ѤUoƣD14G(Yy&)pXib:5x`gZ!@^%pO!SnnaU1t1^9lj+pn s!9HApT]1k' up{ W"$0;Pwk%" 3(ؒu\a kG e"dZpwj" J}o r?pHZ"*0`hꔒ Z-VghGb -pS(Z"$pCN:@ip-aom|j@8Ua81pE0M,r6b.@eٔ`y%{@ 5['pN@9FIk 89:P6H[_' i`Q.]{i^ȶfaM{5ҷi&kUO'\ #H)ce2t`ӑP3;Y":1FP~Qa$`>aeɥb@*@ ~⺠"p@ 0)!d!`Xӹ?`|ֻ7PZFо OVP3=@)0 @ D:pK3+ H3012<6p8S>,;EğbkMK0ѳĀOsCC<3"I5.(Q-KY3h9|~I9 LsS1k|`OVSm݋;f~`\cX^.pgv<F> !'l#|gEgĂ\!}nޔ^6r˦ Ff0$m7^I\⩮ꫮȏn=n2yĺK:Dx7{;$3| ]iqM}CԳ>r-&.>cդ<GC ā1s֛߭Oʉ0 +o,<#?$tZ ,+"? {02u#)2?L",GEA0|p=),-n$> üUǬe#c?Yo n!= 8k} r2Q_J^ˆougnLX)4;M3Nq?9!,?o H*\ȰÄ*XtQ&ЈÏ CIɓ(SP!ƒ 0A 4`A9`~A-  e fhv$v5@gAfQXMp@bP$0Hxd!>&R  x#@%^`)C({/DYP 2Ep~+3gA`` Âli fS8 g 8a e7?g~z^kW*t4<0j[H"I[l,v0gQ|$첁!' Y% 2,ILKp |k)AhP k ԕx@0+BuX ȫn%Bx #3T`@,I~\ C` M P*A A ]=4R8v0<ܠ6dz,QIB@A!4#2DAױ9`l7|APg!LL@yPJ ;tXA'V @ciQ >VL@B $/dP T#ZB*dW sc-H@@I h!DxD $H h4@ V=#Q. 0 R@4` =` 2`($)Q*`5 eH@{!yQn`"]!yQP1#p@6E33( @ x#M"?DH̊̋'6| ^s-Ip"ѬS  YR+A9$EX$FD-4OB$D0cpT GKDM@#l X{H^p9 !@҂ifS+*@q!I #Cp8!0 ]X0uQ!`A~0D+HB !b o+ #?$&00w@Д 0IB` rS8(YJ?HLsq"$#X4)WW\GoEU a`h-I *AU 4`@.X`YyPF pT)J pݵnHO+R H7 LF5V~X1E0J/Lq26D-OC+ d1`VƒtOA`U$2<+V2ڒ R~ˡ !T @_)e&D}NwGP !"3?C8pWI x8M/ Q7AB&8w%M b $!pec`%|,BQׄT EVilHV3̢5@!M89]v jй$ )&\ׄoAQcdN%H$ Iy Pt2`i&ed%@ 1S8B   '38(I` cr)j#dO2UDZsuwN< 8̌(PI ܝCyb@g/hB0-NS":$9Z ~rLA2[(m d!W toQ $C 85 HR Z4?p\bYP8aH@LM Z&P30;8x&]$ :; vSp]֤Dޡ(3'' 6pԡL~R7. `ؔ3SI, @"Õ+*}byw` q[)]*pritIwi* agni(! u) '@ "5@?*%0 &mLv*0z#0oq{B$hĚ21"y]5 q=Dũh*Â!YOک.YrYC.lV8Ny)IX 9 "CMIEpٜŹX;yQj9T@Ha *鹖~yp boJ\p`{¢MrW)@JAa!qi)6UR QQrKe6p 'IzX0,,` c2Q`V 9`(&nS a+.}I/;zx@%!ݳ{)xa!$yzNW# s*&1g>ҩdW0u#0ڨ$hX1dBaj"Z?q*oF$.S#y% @p$E$ #Z? %Ux*;B%*#Z:%:*Ӧ 7FX3aQ &J" 901wJ,kj0PhaZUQ@%DAp|zN"h+ ƨDR zbБ1g"i1 볪a[BR{H+4^kB4kPy/r:1`vABX"ryzASK> v l;O!);`FmFhT'Y9 ,|'І< '@NmQe;{%VB૏Ak1ʮX! (I2QE8;.f(  p}["[/*dʼ%xK"a/#@ !е+k1m" lxor" {f*.|0#0qÆ2;&+;+9 c$<pLߡ2g(sAGB+*QC8c`2 [I {bOo,P"*|~P{*;n -B-Z 7&q -V",/iwT ç `4psE(2 5 ,-fG 7<.“*|+"к+vsӬϖjD$pʋL πtFtzY!`Q4#PQkC(St9 l6M-79*d@M& 9g`:H9=`=v<hX3ܩb%<]d+:s~E.R 2ic|;R0:`n~ٱ0s)aTKG~^!#6si5J  ؎d1+' n^bJ1H,~ y\,}u=> ]۝*# S_lA1 A!$5` v-::m\yD!1P cPAqM'oB0 kA/!/_)`SSq@m(xСBR(O /"^|E=~RHEHPRe*]lЂ/ 8bΈB$XPAU^J0Y9BU#(b$|B!E,.n a 2(P# 0F #S0j7xIrH (FX,H (`mV †:v@8a¡+T@Ȑc 0"B͟'H*&'iAE!dRBˁ,p2j@(0ADh< HABHɃ:ڠ(xOŽf DD<@B, jK1HD <161" H4P0+ҹjP$. T@ j" J"3/+ @",`C0l3r&?jr"`dKr`._`z "J2AF!� i4kS l Ni H HJt ti1:*h($y&0hsR˂8 ZbGq & ӈWQ_&W0?W4T&'-:\66nηLh,Ҁn` bH v\ \ ݒ"8Id&`/;"!o(뽻UaD8 70p"ȀD`a 8}@tHȜ/J"f X` h#r74σ ,w0 .xmowy<@ۥNa :. agJ@:0 4678 6`DA)+I<i w@ 괁@p$/ ] ?/FbD bgD d!H>p7舆)"D,%ӊ 2 ,aA"2=1AxF}v `6$H8b,3JI1T1$/8A 4 4@"2ۀG J"@Y80 Af0("'у,&hfX # "5YGfwXde, .`ż A 1b %Hfqd!*Fl"gvFPG F/fysʧe,0BM$42` |AT2 yNp@ ЂY<=H^"0*+)bU 3^Q9Q^ToY#URj%$TD6AqUNb3OcL$Lq_V#jԴkihNxrU$*+Щrl({aUyADlFHzKF)_=g- `;Am%$$cH`O&I@` 0m!h`-,QD]dW l%cYm^20وk.Pt l#bN@(d` 2H)1KzrxI`Pu[%N5Oȳ S>+ xv&Pp _t+ tXBiaNjISd f+X` ݶɃZBNъ1-s^ǻ< \!,?2o H*\ȰÇgj5)T"?BIǏ CIɓ(NJ!@JL/YY][#Fy)Ș3-ڸ,C6*ೆͺn㺞M[%PPSfظJ D׷q·dHnv|@@k?{qo67fGNң]x<-~*pb4hq[= I Z\@ V#GyA )7GbB-қD!6cAX[(;%vMa:"qPvypd!&Ҋ^>P\8dW D0R $ɓDA<|`'Cz & #ceAH A %$ vPDJj@ s.zRq82OdBNl@P2_O# FT 9PP: aA%Du i@;vS5$"FPF)-t¶gEAGvk8uI%~>kҸ~ ]$G'^\g!mqkq Q<2aiX2[;(oClz"Hݽ:8v$3Z?hSQt[ʼY#H|SdL]}:Y5ܕ p^N hf͂F9\ܕv=e])oJsm!~ !O,7ġ r$0rQOT^g e h~e  EpF81!z]ɕ4 cwR( ¿Ö{oM0`UiC2FwB|!#2xWAICRP jM?!P?$ "%n!B4hxYρ z#`   n@ B,l  MR#88B6J\ l$YD4pAP#" EHB4pژe HN$ЏW8Bj؄AI,r%("$=X$a_ Bt#i"RкQvD"d SҢxA/TD6Q"3Y vJx&ӄDyP^I)^^PDt̑ HO "3!Sy@( uO,<)-iTAҹ8"D ͟@hV*yJ"8N@"5H(<F)%H [*3\ 1Uɒִ p%ZK% `mD4i\И鲪@Cٔ C =*to%\\fq+ִ-[)( a-(Ga gkG5>@6@$z-.i=M['x@k3 ׎&-DV"e-W6϶pv%Ӣzр; I,@Xs/ S/$@Eo 6Z]̮<༉gT׽$h_ s OU 0 Qߛ53*Aq~Ya@lM! X'Q-L@Č'=m"d\ _H&!4R-!L:T`\.Ӈe`!C- M sY !d6 f(#pPFc$fdZ%E(*MI!$B@O Q "A~Qr8O V+{#wP R2 Aq *W)NS̻^}4 Pi0 Lm@@k'Ș'd)o-1U9/k'Yn x@g D ez@b#9$A9!~r 9 = |CT9L&!GL ; ՖdPr)@- a|?,pQ AtY B=T'؀ G&E0u+H"q'{1 ̭A;&hPr?4H[+D`/|N D$_o@~3 nl9!k0 (@ 4Z Y`L3y\  `6|LJ@tT:E3I,xv I +3IQ X`75Q+ +v6djEwQ6u@`qV2B0F]wAN02)@@q%sK/&'"`i$|]FQx-}|IU]7?S_ڹR͹[r{:y,"@}|CV>@XA;, va@j`Ep9K95$^bu7aE|(ܲȈ3*`ʷ03o)ar%!,592,@),ڂ3^LvN- 0F] qR.+gDJqP@sY'i* I]ȧaC0wB0 pZs)0U t* jV' Pi-EU%#z̲~EBשV31'h$܈v5EF#VБ`b`D7/ J.80 ! `vR2!b h1zfJ޵ @(.(&7!#'Pkx1˫Ah, {%uh~0hZWݩq* &pJY%p5P!`  k$`k 6H5"p  1pA@p"99+,QVAz"72_hq@J20r &ը~Ж21v "Q`91hC@^3~?&D2 `y7@se`A?9v+y[q_U!2,80pPдI0B) %0 `A>@"U@|xiBۣ#p,] %9pC P}){'`/#WO&S8@~  F[1d#YB"EQ`H|9 ęQNL5Pjg}Azħ2}@J7G_]a IS *;rBgL*WAP"- h$45+@@$0};c|jdA'th1Ț1 ,"5𞥈z'|ʢ$^ a&˚ApTg'Jmќ6Ae))Q: ?ZQD':3ƞOz !aA) Lx]&aN ?>nrp!a  u1Y̭cυQh!U)ݩݫP&3>!":>#ג-"0 !xA,K#~ Ҏ1]7 9T?=%!a9;rK2!MvYM3߱ Xm8g0fd`>6~b ?I:'0E#Nq !XLH~c=t3YPaPx@]nߓ1``tJwp?=dVSo[!MUcP9II~5oAVe%]1# @1^ ^I|@/w $p~c b@ '-q ! 'U+9 AB 1fvw&?эg]#6.z$Գ '{ if-Т! `Q,P1<;-]f@R~P "4o'1!B1eZ9 \lQu r1bиMI]${͢}u5"C~ 0c DMo NYUًxP> IНu.Rbi\LKکհ%LBTAp 7QAR- 0oS3NDnP@v~j8PWMa C 0@px2!]",@ qMZ>j/f|ETpuN/sRG!^" Hp؜xo5ɜ$ EA:Vҁm!yps1Q 4qf%1}µz5BUcp`:(30;,kn~r%/{+3eA^(.e0WX*drm`Y]ThCe $dyo5 ,u=@^PE5|O@ DPB ΚP-^hnX)%Kp_ 5(L5m9ѩ=qVb`Ι1j e$2|aOUlEMW.<ςZCV@HIuԪ#w'!Cي6PbgRց% 7=ZWsUI}C*` \*\3*Ͽ L\)M4nP" sB9Bh!kU T@>#@f)Vf4+TzF }Owh;@R A0$)Dp@ ڤoÙ:;h$)*a<DEbۆ:dCjB$(2 3 d+HT#< ;2YBȏl>`GJmPل,*!NņYPc>AV@~XS):\%T ]DU%H6@(6 @8Z JJX5cUD^U: N8 xP)IჩJ @HA6&1B  FXvVB9$A *Ȁ D4>|60Bc8>bs_p6]"%@laBBuxbChePC! B揄utH^ZwȐ# $D&1|OD: D*JX|D..J͊z^XƆnih* 1~y1vQIzJh$G hO *])K XPԤ;k?ni ]S6ʔGeH!,S$G H*\ȰÇ#J\cŋ3jȱǏ@ɓ(S\y,cʜIM ɳϟ@.CУH*N KJ*+jJuD Jg׳hҰ- * Cڻxcv@ g(Lأ | +P`1q֥81gs%Ϡ?>|$j* ?BϿ sPԁ J"?.͚?aĕ:.bp22:Fd=EZ|Sij#7n`KXj%JEhT ^3` ($h(8P*x|.xV+ |A!)u࣍y @&YEt!XAB3? 9n_+חIAZ;`t Y Z E!WZp#hc{RFB im [b`6xm7LGW: "e y-' 8d : Ă Ɲ O+ gI u =pI(G&$.b,P YX"QhqyXKF f(T>I !BB| r oCE"݋M$p Q$bU1qG|aZ$FeEЦw( Ѐ97$Uy`a@IVKD6| SKԁ9 uBsܠƁaKmtt UPIu`B,hXP0Gx42Y!~ćA; bćPC,]I4 VzB9b ¶ `DTFW $y=DGq|+Ÿ24FP{FvP?v%?! C>@!'" ?oQ D> %WaIdh?r2$$V&O#'Gp!@:y> @ 'CġF A 3Ҋ2G OQZ9?bMZ_hdFJL8)^B!+y 0N9 A $:e~ĪPKX%@|@ A0 )W̍tS`*Q@ց$Z.5pFD !ALHn[qM@VdfC.K pBƎ DcAr4$ (BpV  `# 8vA 1 i K@L *x@<v!)S!14A¶i L80kML$lMط!p7)C :h >0t@*@|K݈h@4^ "qTPSV=$Fd# V,%CL`Pa2CC\bX3irD&p`m@3q#T%ʍ^j# `ᙄ$ !92D0?u /vO(4͈EHc!bHnx# @7F$(| AHD1 jSl-Q Iиz~H 8ڢ`#: PuD LK!XF`0L¡x7Cfm1`|`4Q,@`%@P(/tN"bQ$@$%>?7HԎ( (!tݾ<@ 98P` E$ %0R  k 0K" e >p !HTAIY"'oI>@@%EJ,@ @V? 9B4 I_=6QP ^^ ,@ʲikΣn i{5 z^准D" M|wRG?  =dCbG,\ )?DryJضwO`)`! ~}g1- dkAQm&5Qǁ]woHR*x!5 a1\5 LBp0F^?*0tm 1w5₼yP/0L"XTNF[ dž2 It xX{n O81 _ ~Zr @e!bB4$& 0Xo@3w |   "-_%v!M -Kz!kh,Q*/0cAA !$Bi7  $2@uE  D/Ǐ k $&W/ިPH!@Blq# 6h1pn@"| %"#P9D# &V!"2I3)@"Uy2&D#RӔh#Y x"!߲P~@yA%UI"a?* A`B*)I>s?A"Ig:_bYC&T0"'1߂~HO 0C&2<&rV!Ap hb)di,@2$E")#C9YF-Hi#&cN i#6r"-I4!< AD3'JDx~KKD4X#ptNHp=I4x/w1/pn)# G4@y2- bۉ/v'xa-s'"q% P<>"ꓭ,7R7$tCB i"" @9,}I$7E$ PF>FVI$# 42ETp @ Gt2"&P@6[d ep $`/t8#%*2'Ep`Gq@Gz!#Ry:Yw ͚ -0JIcq w:|*H1"0KJ& H@1`!pK>ph1.⮐Pq # E$`XPrYk1{g2*`ѳP(q_!Dˆc@ !M< 8`Q&W_ˇd[qf0+&1anKPO)*2vyr PT@ '@. f/Pd GSF$t ,|i:`";!* @@o # VHk ! 00M'{@Z{xkP_B "?gЩ`g1{/BKC7 h  "<$\&|(*,.0A{+Rw0p0If8|;6@E_X%0#pp `P#_SPu* J q @@P+j2, gmb':9K D6`b9ޚpg0W6|o(4PE*@5PЛhilP\p`k9@`K"*ЛS|jp6#]g,@`0# *4!69ۀ"`Ȍ"x]đ ˬ@B* "`z `3P X 'Pp!)` ={ <s ]B `6ڻ N l5qpT"\uh_`H$&`35rKK@VvGK z}\"0:<:ipa@+л0佌=OO`7^! D Md ,`k*qЗ,7i0aЉW>50Gə7[ TU|DM QNɽ_> P.Wջ2 P׸Am 06LMCi!PAP=pahq0qyP|j,(Q s<: 6PaPѺ!lIVG^d+Ipr4`zQ M:@o=^]PCnxm C& `@UpMXA 0Ů/ Xh0᱈'\qo@ORQpi40rm5%?0~Yw ѻT 6" 3C\ P i0%`Po!0xB@gPg0`#MCLVZpDu"# O@ DPB jp(ņ#xbAB>~ֱr hA@"4߇8}/2"RM BÌ- h&b4p 0 "$IN Vn< }D6b!,@(HBFl5p֬7D忝=39 M60?;.d;KaH/ĉBp`5@&I@N *5:0:4j~:9$&dHNf4U@/"H( !huҖi!d  * <@A:@_f >(Ax&x|G.&xe4o G@ d@ pl)90WuFcH`B F v@* ƂgPI@@%_e hHAjP>j@2 Et m4@B0,łl gNDh27 Pd͟ڲؚT"@ v0?&++Vh^ "@'!0m@cJ΄ؘ\$їhGЫ!u˹<@L{K ` mmD^0P6m'AY0 !klܾD; ?:ѡϤs)$Λ> %` 0^5!WNk O2Բ}Kh$`L \A!! a-]j#?f*EE1*@iMLXF8q} @Cc$ÙM~ &3PvЩ^$lXVO@es G|$  zI0wa[_{G@|K΀T7D24- ߙ{_F0q{q60E=e!NIxk^娽o@!,SG H*\ȰÇ#%ŋ3jȱG?Iɓ(Lɲ˗0EI͛4'ɳOJ(O?*]zSB]"eJ*JD:%VÊԦEy3[v4IF|+0CNfA!CȖcX .lpbS?/Ji"?]ACK|b]48E~ȫ a:ȩne0%t<(TBQp$"<@$h A յ@z,PR0(|"T .f:nOD@l#B4PPA ]W 1 ;il1D !D,WQh-۵AB lA$7@EGv&u@ Or6zj]Ș^ִP}BgLWnGR~ɟ6΁yCHM͝SwctSwOxW*k(`zHv)ǧ{4H"G>n#IF%oV XR})ܟU5d g$8p<z82EH|ZSyZ1r)39X$H" DQby).ۆ6 x3013+3,(%;5=h63a;|lUxP[H`|p: SRoasw{Pu?e0[8 AV.zf_r|\3n0 6bh!)tGqK3>!QñfN2.ojzp ḺYmhpQ8qN)u`7U'1;caȉXgtHƨHZ(؍ވhոXx蘎긎؎8Xx؏9YyI ɐ8KhςpW˸m Ԍd$ 5%i9[$_w3AHYU=5LE9 *8 [8.@xX,"if-գv P0;4{֒ 1Jf90PMj AyhNQByaor  B%Ј.Tјgh){L!O! 6N$@z"0 o$'F$ @@ qW{gg0BZ'0p y Oy`g0G <$Ptʉ`좖u Iy! 7wzaasb*P?s ^ٝ~u9ٛC )^v٘BYPP1A ]9;(4&Y@f7@#H5jb-Ya1j*M(:0CjAsm'p13~)zUiq!)=Y)xr  hjTZEy9Y(IzOwhfHmwetg:U :['DAOZPO:ZY-3 DN"8 !<0zBcWm(`u ! [shKǧ0 9 Y)v25v'y@Hoqv`)য়I$TOA7wF@[Oz2ODA;e?O|%Waz(_] JsG@vABx _=<ҬTg$R4ZvE9w>V!!Yt5#w5 :OZȲǑq ?o,槯 {( Py : 4u;LaK偳N(v jy1rX2qXiQmzI! uelEJ$+]K2ڈ,+e*i)ghő LîҖ f*)ʊgx *2]k2zQcz(QL,NTɞaƠ.Q60^YcylgX y嵨)ٔ,m\ \yEh#Ă#l|ͣ!,C/h H*\ȰÇf IȱǏ CII#.T 0` 8sɳϑ-4D +6\ʴӧP &,P Ň !2dPٳhȰ%,La t LtH?03ktpӨEp `MvA,X*!gqB DμyTB`wjh0 ѳ _~! G߾~ (^E@u6ShI0Aچyf Hi(ba$f6Hو,uˡ-$@#R@x Bm,<)Уm&]@p"X!]pbm4<`BA?T)<daƩP8|Z`QP *C iA/\Q!4@pbZ SkvOr$!yr,#Axg(5 ĺ P~L ćPueIDyCl;4i Odc  ES囯SCFL1:´@ |@v-EQ`pBц 900@ ogX'@&hFAAppL uq҃ o$ AXH6,dMp?`"$zIX-h$#!H l6 !##x Xw7H(] GDA$"@Nr @'@`xD]A,6)$E:!#0r"NQ04C0||@8` 8GHw8x@,b @H"!p@0@J4 TF Tcȡ $pde)g"#fjkPp@v,%@wd"5 8~)n@0p47@I2G[  j/g0`&Q\~t*5x%FiࣀDhPq=wdsd?P[I>Np<^/pEnBNqe@pq0vN3$p=;A5>=PP0p~QҗP|Zх\uNv+6&aE;؃3r aK'^o׃P8#PgiP!Nx#+:@pDwe4YH&lHPW(Pu tlx7/@0qP=ٗkQ F0H|5@:Ag eCu@u>5KPhNJ9#E(y$ x4ag6HRk1@m1hi`$p-WIOWۧ!n12UG^4YbW#*`kws$tm ez|F7"B>i+YZ#SGqV@za)q-RA  5R3 CI&, !t [vS1q@"l Ao1Ua h %" `fV iq`5!d Heb%ABߗ694'"!#6@+p[$e! @ɜQY3`1 P)"3Ry-p-y$a718)!8V#$0`Q=}* pY*̳Je',j{깙Ӈ(Ȣ8dq%0mҢMa-1o_p U#2g<*')* G.>I7%3>uv4APehuJ0qi1o4>ue/ T"'i*9tĚz:bA]-z:sC]8Py^ͳ@*O[1%F]ՑS LSm#e*?|A] %3PVX Z6TxٝdХ5$hhd%٪8@L-D(OuFY;J0gQB]* JDQ0X <%v>umH`JFz: :3Zؓw+Op7As' PUuRZUCH8R?0`x!3`~y ^dдuugV;5@:aAAi/ p9*[8+*-`;UfB3 dF_9`DN3KKz;\/0dXh0B>!oi>6p3]*87>MUb' i'trm /bsDS{(VD/&'Xpbܫ"z&/VH&:*pXO"t92Wy$@1Aa+'#=9 C#|G*l&@@=P?wFFY&35{'b6A ^?R%T6 !k]5&e/a  Q`duwv'&7 `1@&dMq!٫?vƚ50-6,`ʛф[ B@LP#ИGYaѼ*@8 -p* \: 7l8p@B=D]F}HJLNPR=T]V}է1O#XrEetы Q Eҭh-0%]W$10!A#.M` s#$&r9`ɵ y-пOBg0-r;ՊJ$0s,`9<(Q ,)M3#'<50kW&98 v @ \5@@z@a%< @O_ˊ"9Aq͊ 9RVS@:ʔm> p> q0@'p0V!f,͚020ڛ=5 +L^[J)HL#z:,@=NC` Q5qb@eL >b"@@k'p6PX$`-R,`ܞ2c#Nm93yUHId1 !^ p AA `ڵ@ *@V-~:S9E=猭{AXپ$NV0G3pZ%??`> 3D:(#C0F=~RH!oXXPcjSfI2\@fd ) aRM.5 $xU  1@ h!S|BIN[W$75L&$ p@52@ n2` v`',pJ` y4 \K$@ UA xPwenKzۦ@۱9jaZ_!@D* ղRnUB@l- &>-,xV^ jxkY `?JP{u6V7L (u[<@t,PW5y H T]< ֗)p} `׿2a;VFGh0&` ͭprQ~.#y !,@/k H*\ȰÇ @ BDď CIɓ(SFq" cX1b 8sɳϐ)L0ѦMuD #@|IիXwv4OA ЦS Ac %vdK]#T(#IBm @ HA#Ew#KLginyVBkY`4f̺# L3MN- dHҩڴmaEկ+_^@I5+tM"X/F gN\J E'PL;<~K~X׀D BF(aO,u?MrA"L% nYE@,.$#^u@088 tHs )(vw ddBKVI%V@L Z)fkbkd2T0曒ˆ-@ 4|5&j! rӁ}&ZU%c-;raXB|@%D!#r+#Hbaf@y1IP"HiA6nahB Xx` 0 J(OQ,:+BRt$!yA%kfB,8pN\AD`Y 6Diī2ҟC90O $CE!r@(z)a& ,,A-?'PME#!B$P G!u8$ pQٔN 5J!<XpA#H)@r]} |&s%89zs:Gܹ;y;P&͒rGDQɥB "+QR(cKط 8d~I}P>d+R- ;o2 Ё. 9!%@k !@P!IL!` 0<.%u45E|Ab W…̓u,1+0VAভUJSI +b*2F'#&`))gF , h 4X #Jb*}S!Ɂ OJ`=st !fxlQIak!<&EbY„ h ^$r6D @Z $`T" ,F@@ەd]7ydžl P&L T56 , ;hC  H$ @B JL(=A18` X<@2< |S FҜb N!i cU|4p#)h:h03$ CzK^XB`?p0|hw`(']Q w;ȁVL9AC rD@x&M>);Ё so⁷g@ 3 fo!D$ Bei+>ꤓ!ğ)t &P! 7ܞ&`< e\iH]l7%]DIn=s d\>!M6! `,F$/H67$~@D9#0Ǧ8B,@ a&81EL$8w @ERL\`(!A"C/%",u1Z&BjaC!C'`AT y* @ "4( r0(,"钹b$0̊D@ ^ 'NR?Amo_@% ?qmxx2)9U@I D(@HN "SO F ŋ tA[5LUBD$$ sȀcx*i?\xgh- lA暯x!C'A yc@PXɸ8Tp Q@`wz3p!Wnx9q?K|bJ ~X}*P!w4'AHx%@t Q}vq÷}%;@s7ܖN HCu~8A6PL"A*`Tx0cЕD}|'yI?"S-p4+]%1"q >-&[PpM"F'ryA8ol8<_0vP9Hn94QԚHAb>=HD  Н!i@X 05YOW=R%O0*V9a{,e0=! `?)? $-']CvE$'f`=h0JW!p@)#Qס#`P", R`?vdŤUvP/'M\h-0p+?'V0H>: uj~j"mg'1 ` Ab`(Qa.85up-y$a0)&CjF3 w0;# 407Yr8  P;`z<#pyw)Ph9"#>-p%jc7&=0JB;A#a3%7Tqr- kq7*C>Yj%>zpwq-PRC<"0k1a[=B@Wt, L1" hʻ08Pet'`6i" P5${q:"P?!@]D`T"F`\Ǔ.{a% `#`-83' (!<EW "­s&fo,pHX < Rp-@o-•"QqN.'`z%dp2%`Ôbxp(|`KXJ).gіsfnk2Sml!$-[*w1 r#K-'-R!] "$.X|us\%wadĨ?CǨ$4__(:y1Z7Dkb t;@)Flq8Fg0 ~pҼ0q WY%$S1 `=de2ǒQLBJA'5X}dR,лc߂}T : FmF36|ΓDQ1ʹDQJxCIC&G @QF$<w{KtnW"]6-܄.&hG27Нf71pQ0FTA{#QPNJ"<"&]g;-,0%,# <&Q .1 ȑaRUD `"HB^?2[O'j-"J xYE,$p-y,Q|+u+Ŭٮ3Sa|&/'p2M8R2@g_-)T"E <ڡB9$tyf( . 8|+8,|IFP;ٸȁ-,`> *ƪAW4#!=,){c .<I *y^%k}$"(^R,n4,%%-~<0^BT<|ΫW*'J>&1Bq0*.ڜt XbkU$P߫B9` lIwQ')BF0Ͳ;i,Wܼ*Ȅ4 s,aad,1` Ri&Ͳ #,6l3oϔR넔xv^*͒*l4 K®( ^$n) Xgw#@6`82qJ+0t1MTJ4$vk3 B >QăRPĘQFH!b |xDY0G BpVJ57N=7ć F8$"@~ ~D-lhࡃ-06 b͞  [ 5i' nt! D\A  TB6Fh=@hm=! .q%rI$=b2gFN͝K<тĉ J۽dQAww7=D{tJ4ǟ%"A N$kE7[ԏ@8B ͓C0  D,4*HE_<ȃ `F /8Fw x2D@H#DdɰbI)L:*r$ȲK/ / DsJrLM$3(M9`9<14 ;PPE!4E%Ђ 4*=3崼;u; "TU? U_=kTXg TZoɂ?q#65X"Fi*C$i \vE"ICBD&/DG!dA(> Z{ 3 `RvQ%A~Hu= LؐKCӸKFBy/c/{ dAMb㊛/BT8dAi; Э86@ QP"YNhlpH.A)DRI$ډ΢ &9U5`B$1=Jȁq$V^ A $.@`fw@ L 8Q!k\A@P Җa=Hj2Ѐ=Xf`a`U`` bP`&4h,4AT2Ѐ @ 0C~|`P0H`@tDDOx)JDɌE q e!$@@H4x`@ed nF<.!-@"&Ajn8DpaNp̀htY˅6@ t? 5!&q %D\?,Asx4( tH6B8b ͒F)`H<B90$ЃX:{,v Fal0V,b> |+p_DdBTHA ٱ HvHA tu)O+N IqfPhXR.hGU߼" `e HMn 䔃 9PN@-5rB$q H||BЁ >hO>@@lЂqBDx :h@4 B  0$г@aRV8"XH.R >AB[XO(hTp|vJDxIcp | @,,B 4 x]GB\xN, B0pA;(QAw50>XlyA dD `;z@  T!49݆<Г]-"AZ%f5HvACX0Al kMJ@%ŭ 8Y6%gGd)L '8 25@~[ DIc?35Lm6ގ B70{ *k r i) n`CP/ y`JTABRxv@ `;6ȞAL!Y,U?tlB|ė@1p5>gx/P00tLN* cъK5b`YH @O Bt0 eV~#dK&0$jۃ`!^ZA` H P8*iD7!S" `ڭB1 C  @0~E kh}A>;#(P 76 tq}CsE)xJ$Q}ClAz@ @ H+?7ŀ H)V 0\!*D^A`9<@Xz& Bx; 7[8p:9ZC` X ia [C^kIa!Ó`A\+ѴPQ>XkqpU[x*|0( 8=#р P<,@ *ᙏ?XjaE!` R x9ȍ$,O1 D4 HI(1|/0,}" /EM3+ x>`)iphK:"HDYN|<ћ ``; a!9(  ?L9<`G x)9/ EpiE X hP"ӡV.l7"`ؔ 9* 0;D̀0?QYID!b9/0 )EP % DiȪƊ˺jE\7 9u42f4BBdK &) :@ 9:8 LPʴ;1ŎoYr 0hxY2[ʆ p h|͇̯Nօ(.aTy(#pڎI@ы]A<eҐ 3WJ܉aen I Ĭ ;-D-|P; /:W"JQ5 U51@0 e^-1Ob X81x&u ̭ԫXXhڀ6MpQ k]oitB j2p)AE+L 0 2%3! pgr.%;_ᥒXq P P؀ޚ^'la_OS!IQ* )j5fZܑ(2J ؕF2S (%MDI 9J>.>$c[0}1; /ےx0>'kbx 0Ѡ K_֠)Gѣ\#ZX#[O}5_& xAWc.gҩ8A@Q.*-^) ЮO )*eYPV; b=f0Mcq)*آ ln04q+^ˀ (KPUv6 x jRahH/ΒhP;, h%h/!"ʪRE֓(u =HYrUiلh`͒dipꫮKj@ꮾk7&6/Afk,q뷦k)麮$ !,@[ H*\ȰÄ"b&jȱǏ CI!ƌ1.bT˗0cʜy#Nj^2ϟ@ x*T(DQPJ2%Z٤ʵW9cJْag=˶[i}}KnWP3Ƶ˷/MS%KKT-2̸1G\;L^+k&͠ ={et fkގ~MA-jmo (']=7K=K}JgOO"$P7/t(yʣ:Ͼq~'tlǁ|,U| RyQQ J^ 2H H|\MOnb!Bb"L2}}-Gᅫ1 H$u  r" !G@ɡ±P(b!wueGs<0Bp *`C58A EcjtA&P3`§0CB1xC6  TqG&R#յC?PBZ +MjO8^ҤHE֥E )"tJ"0Aՙ-ZKCUuHC܈""5A iHp)dMCk>Qf/mTjܑɮȏAqH²F0T<;B( RIV^ B  ;wd#[?\2r4)yC{bȬ EH3MG5"@BĵGXP<@s,]:$td@p R?$ cBVBP8\9$H0mM#w!PWXE"]E>̫[Vd~1?KaGTB"z!+'aM@@1#)|pAfP(#ԟ_VDx8<0XO EED4O 3K(y#b $8$$$(4?PA+.f ."8J0$XDE`:?D7.,^0Az0 l@ ! BԳMB (GB?c bH& ?'pk}EK;Gm7L B-@XY@ʂQx %(@f즏A#!X$Ё @ Hؠ?0] IdGHQ6ъ`FA+ IZXD`4ae94Я@ ?B1 DC@H;Ѱ@; 2!+hX 1'Bp҄ % $hB ~Ά pP܀ k X OO|VcTЀAQa#cUAU5I!`/hA hg^P2dWͼ t`\ ŇU/6H,QeIޛlT6AA ҁT#|08GZvgQG@p=϶-cFnSA ' 5P(G[ N}mn@TV:HrVr$W'XP%Hz`Y-@R JzwCqx_̲$o"|,1@ BsK"S$b1S, jIaK q]^m /nH I "3c #0LKW ^⬑% [t C.y hTĎR,&Q!WFݐp+?Em_HX_B*̏q$jBlg_|3^tR$!6~vdEwf%0[H?s~V}&P}$0@&fë?r k v~B+Bj1 & Bf6!Iv/(1BC nEJyd,2AR D$A H+Xrn=`T"x'Z:Gr,;[dp͘DŽgAM @VC lC:4s+1Y뱫 bGROo)|2^0YGA:!FN!P. J`p-KIw=' 0i20t!&XO$%9*0ʃif BX|uj$I $Ёgߌ0 t a{3)hّ!޿}?5; e60w$ g4AP@&IV2vv|:"@P\P@lVwz!NnAlHOxF3 `8|A˗1PyA3;<<#H5X4\P8>! pjl`S[B%!xHX\a 09T@bMp\9 81^f [Q;@Pp ^{!4 wo/@'?A4^'8aAFFA3`- 1P8\pp4N@8v1 hrv^H5p{4.>t`4!)P1A0QBQ#(ށx;'`'9p9Wp P29F]>QG0S1j+Ӑ1 OؑFi!17I_:ғF)aű)y6z~1 , fC'AoIG]`(s"giI6yh^1M16pW7GSP~v~#v@PS){.Coy_ 'W~[cyQub?$]jgDqiAy #q srptpyA`i1Ata6Y) `IA97_`-pAP^ f:`qaRau;ohY(Q^AMP(Y"`~}+Y [` `h&VPVyc00EZNQX$7fx|1 JaCk?&6&zUp4Сqbu~5Z1-U UgsE<Ф0oh~f;{b?n/m1 pڝ*Bzo9́1XCW rzm)Q Jl|xgìGJ 1 Y:9 @Ѭ*Q0 Ắ*99XsJP;{8ڰF 뜆i"_ѱ[)K ;j +-ֲ0KA9jqZT ưezC:K?lA+CE;1Lc&9"ô@sX63P*[U+X ];Q3[_ak2ckqe g˲-tyzC#vpۗy$l:(۷Q!|Kn#=˙ :1{[.+Ci' kL<:re\wl',j!Ӝ̱P,֜|rlDql+#f䛆Mā[5\' S=j$N|IV!o jћ'mh!4,ҁ+: Pܸ9j#;:hR\&\i8. 74 Ϯ AL.cL2P-r =ԡZS\KZl=O5;lIasěiwK̓57r FSÓ)<^mcmUӴq1=ħ nқF KZ61 /'BH!` ;;$<c}&]7< s->p Egs"i&7Gԭ%ԭekٴ-j<(v=]p VXͧU"yѰ4% Gc!"&&czd ]~hjU-} cgLmv”*a fnڶaT&,k%Cо}uy`b&=ᏽo{kKEF_ ( #n)R?37L_{Q i[ _ON%{99/:cZ{ j,I{ ?<K|9P'>﷮E4=`n<:/3H 8Photoshop 3.08BIM8BIM%ُ B~d" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzC C   ?(($m_.o[:~(Yb4x'կ+)db9mvG.[a sZ%K6f]-&У_߇JIi'SҮbSB̎;pxyC[iײCsc+Fs|I:¿ >z4m]t6q5wi.v΢((((((((((+??|Be?닫iJno؋(<3__ŗ#ߴo)xs_i~}^CVto@ny Ǡ4QC?[Y?_۽DWN?5}qپ |O>#<i֖[\ϽĹg⿩((((((((((((((((((((((((sorl-thumbnail-12.7.0/tests/data/broken.jpeg000066400000000000000000000007401375675401600207620ustar00rootroot00000000000000JFIFHH"ExifII*CCreated with GIMP on a Mac  > % #'7f$459EGIWXgh?A mg^=LBWWhH (Df8+DKwU`,6Z`2o[պacLSm].b]QwyԻݫakohl mRwoڈl|7{Dm"'M R?Yz;̚JrW%"ZqGxeQ24.IrF . ϼ sorl-thumbnail-12.7.0/tests/data/icc_profile_test.jpg000066400000000000000000016403231375675401600226620ustar00rootroot00000000000000JFIF,,#ExifII* z(12iCanonCanon EOS-1Ds Mark II,,Adobe Photoshop CS Windows2006:07:12 01:02:30;.yes>F"'0221Nb v~   0100@Kevin von Essen 2006:07:11 19:44:182006:07:11 19:44:18jLp2(mHHJFIFHH Adobe_CMAdobed            " ?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?#^#eOQ?*˅FU^Hfi>AnccUc鹇s| 8v6tt>-Ͳ'GjURe_V FMN-?D~B/>IG:F>}ΕDdeWk+G hkL>EA8Y2)on&~&\p8rm[?gNrEz=f [^GsxlӍt?ߊj-"^ ku?4x~VC*Z+'HT#W.vgDbG jک5H AY9ZVgNRܯVq>Uɑ=Uؕ t qPmG&]kK'XT8WUz6t}=Yİ`0dK۔eyK:h2X1\Z`AXlpXOұhyU;b\BЛs1=f>\]0xnSs|9|~^^,VlzLP  ܼ v+c?9Z~/Wf[ kPvtUXu:WOqchݽ Y' ?/SҺ3c=+H־GwQ]_\ 01׳G,s{3SЮ7kndU198h5;sF- 9Bos(y& Z;E7BR0k#50 NAؼZ6ёT|7?Coaݴtxj]wabqs11{/}\I-}؃;m~^:~db&1+;>$*c< i5`{P[$pX$j~O5Tvys>\p3ۖ9qr"kl?g񦳧aVF3 3ּ cdQ;/jo7 ʄc~L-*'լ:yc hvn\7qū[X{[@w3Y`telq:H}X=WW^sh}X7ձf$5PX(a8x}ƬY"Mx~z69F'ɵtOy  j4Mn8oC/k{Fr[S/\<2$ge/O? [?纮͠5M/L.o5zGNax r.'ݏ>VGP/uA,Hz.wB3??|߻tS8mM{]=L&a<zgTꌍhU-42% VG<jtV%kesTf2 ?;Q7V˙4+*NKk=J%}Y1KkPn-B<83p<3<$oĈ`ugms}Q>joMfUC̲hxAe^v/Բ3kݹ%Ky6Nx)zKTX8d~_Eoum 8^\6/K,c{2X+2֓;YkCe_WNv.PeZk5z}6yA|6O3q2$\<&^?GEkeUF%Z̏\8XH{z` K6[ˉ;}sԪ6KF y~ =u#f.(ͱ@\F .k&B1iv~_iuW\ͷ+gض:Y%nnޣu= u@5>MSKg9>{qۏmxm.sx8ͯŌs2xO- :&x]Lр&,:nvm6c3wl7u :{^mCa?i1գ}_OS`͇n9W3.u4N7m9Rɛ݄LDЎˍy6zyKzoLs ѹou kXksjnZ=RޱLgEOpi!Wk^>KmMlWM`qw<1lG(˔kX 5OҾ64=-u-Bmsuu^Zuȧh^q/9Ҩioж5&r|ލ5ߢW]NCa;:NK=Z]&5P^a{.י͖AF956wb:FߧsAoja6F ok5q}Mw|99fj5݋\a5(o/ދ`MX_LsX]/;0`k6[Y=k3(sFt F=/uG}}"$s^܌1pb:G/?ܔbt3fSұ,۴1%}6eJ]{o zȠu.u׌l${Oߠ8oMŧ&&97zVn+ۇ UHQ3Q"P㌫6Zⓕ2|]ݸ82~~3Vs{5Zk龯l9=s}X李 ~յ=6)ZmMJs|/1SœHN?'2XEF^*[~:mX ߶چeNKMz'r1o]/._wѬ(en*#Qsmջ}7NvC2Zs'unjL2Rr)pT05"7O?{T8ՊS{ݟEgY266dsnߠq2{d`le1^G)yelldl[7{ՌX8Ƿ鈼dQ4a^GM΢rpp5ޥM[+FfMnmْ}s70Co;۱@xhsw46=ToTLUQ5<#jaFY=8b<Ss,9ٺ\Kl{eu^Erg'5׆}?6!H1 `x˅L۱SHOҾa ;_ߚ}'u#Kl}Sl}sbm>d=_{2Y6xzg_ GEu=a5zZ YaxR|ɳ14uG?dZdTgWz_(un}4 #=J7ޱ`utzu;05׏sNwٖ3"_ ɤ?^usE{,܏a{K΍sI0+]/N6_Sl6臓 4w/G3}P)wUcKP.㼷ѳ7z6X1r`x~!80؀7顔9y68"~kBήoknƢ嬱uYTbm6 qQ1?FY[)rrFw!2}GSW0mC7KAvs}oSwJh8Yv8z{ݾϮڱ׾bbQmqp>e_K~b65]N:Cw~[`(F0OW#-=_v+ۏUVzenkhimԂ lKwnizk^645H6ګ:ͺ?Uxbd zx}X8d$UC#o?w{6,iweVA$Kc_=L0RA~$~ý7CT벻kk̩D8=Q7_0}u{uoۻp>Ɨy ۧs6ݍ]/sƟReβQفlh{ϻk\{JF<{?fuޓ*6co!UlhGi#z#륥̱ͷ?{tl]lmW,zW`Osгh({`Gb@|7տxYm\xpYWM'{|^U c]WAױr 9iDZyޡ:f|+m+#~n35M^N\XάSkm P߫[&,9}B߫98۲϶VZ*k:IճTC?OsDs%ٙm=-/h69usGNWޮ lu`tsJN[71ŗ-?U#)%!;=߬oYv5'K pcH4abuEg_jvji&ʃvֲe{]9(b2JXk1$5&U<֬f{Hq: U?KI[R1 d4H"5ks\ {{Xɗyxʰ,I LQņ"?YC\8:W͟f.8-cې Shlkn߽뚷KS+*Yu-?94v2{u[74hvgc1bM_L} ȳB7{rF_6dž{ommn *]i9Fg'nM̃;u ;7s\:68 1F;("u45l\᰼g-n,ww4pK*mZ9 =UC5 ΄FՆ IK]Sfݵip2 \6"M~Qۮsj շd\:YJk?{[4w5]/p@ͮƺwr+pHC.FìW%m\rZmٕ=:7>z~WZ[-z$}ZE-:iO:LC^~Ȣz}[GnK;%׾݄Ō3MM{Ct,#n4>T/} :[U8 _DŽL\5n'3ovK]f>SC=H9w M,Ʋmh7BVߍ[halMrCwXa~dhwfFHH#Iz8?{^ zxW1iû YX~ͱgOgz]#=.MU[N$H*\cr2)I'ŕfLp)q W ?=dIw[ qŮĩzj6Ze>&Zgǽ.ʮaA<puoI+]StӴ{szY29% OqzO :>i@qm @Z9[ۜګ{A.v~Z}oksc{Gm5kXJAШ~ﭨI3f?~iGcۘz{\L1nG̶<^-pn[Kjh;7ܩ<15 vY{C h7s[9CZ>e75 s{]Bkv5wO+̥ۮ:6 #v_ qV~#C2ѽ+O֪G{s16epv=ZfPn׹O7 ^z{nnÅ!n 4A}0Ӷ閖|>Wkyw=jCgeLk{b+Y C$2QoVS^apnP5Trlefֵ^E;I?*~9|oK8MFMS%½TnK ^4w L,1+ݾiS7Q.c:Uf|~?>a݉kYyl64q')a}dLI@YdVpu8?N>LǚyTfk~Yx{keAF?OilwYK_$G-l!q@ Kb`qP64(?B()Te'D'_ ˇډkCK.\PacUKs]_l*ZvC=[ KȰ"DgV'̞bGGKC+>9@tO'}9Of۷X?Q2)O/~cakH7Xڱ}OgfoNuv6^ւ:{gW5-^~]?w񣕋m+ cLrk25v$KV~뜴sM: ker k,72[>D lbCa}swm>kNw'~dZ ѷ_{wVHchkYYu.!;t5c˞> lxxx"H۳Sٷs[] g>ZZ73Lk-a%5nǷ/ڛv͏R%FPhotoshop 3.08BIM%8BIM,,8BIM&?8BIM 8BIM8BIM 8BIM 8BIM' 8BIMH/fflff/ff2Z5-8BIMp8BIM@@8BIM8BIMO@ XW0R0995-1-us@nullboundsObjcRct1Top longLeftlongBtomlongRghtlong@slicesVlLsObjcslicesliceIDlonggroupIDlongoriginenum ESliceOrigin autoGeneratedTypeenum ESliceTypeImg boundsObjcRct1Top longLeftlongBtomlongRghtlong@urlTEXTnullTEXTMsgeTEXTaltTagTEXTcellTextIsHTMLboolcellTextTEXT horzAlignenumESliceHorzAligndefault vertAlignenumESliceVertAligndefault bgColorTypeenumESliceBGColorTypeNone topOutsetlong leftOutsetlong bottomOutsetlong rightOutsetlong8BIM( ?8BIM8BIM mJFIFHH Adobe_CMAdobed            " ?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?#^#eOQ?*˅FU^Hfi>AnccUc鹇s| 8v6tt>-Ͳ'GjURe_V FMN-?D~B/>IG:F>}ΕDdeWk+G hkL>EA8Y2)on&~&\p8rm[?gNrEz=f [^GsxlӍt?ߊj-"^ ku?4x~VC*Z+'HT#W.vgDbG jک5H AY9ZVgNRܯVq>Uɑ=Uؕ t qPmG&]kK'XT8WUz6t}=Yİ`0dK۔eyK:h2X1\Z`AXlpXOұhyU;b\BЛs1=f>\]0xnSs|9|~^^,VlzLP  ܼ v+c?9Z~/Wf[ kPvtUXu:WOqchݽ Y' ?/SҺ3c=+H־GwQ]_\ 01׳G,s{3SЮ7kndU198h5;sF- 9Bos(y& Z;E7BR0k#50 NAؼZ6ёT|7?Coaݴtxj]wabqs11{/}\I-}؃;m~^:~db&1+;>$*c< i5`{P[$pX$j~O5Tvys>\p3ۖ9qr"kl?g񦳧aVF3 3ּ cdQ;/jo7 ʄc~L-*'լ:yc hvn\7qū[X{[@w3Y`telq:H}X=WW^sh}X7ձf$5PX(a8x}ƬY"Mx~z69F'ɵtOy  j4Mn8oC/k{Fr[S/\<2$ge/O? [?纮͠5M/L.o5zGNax r.'ݏ>VGP/uA,Hz.wB3??|߻tS8mM{]=L&a<zgTꌍhU-42% VG<jtV%kesTf2 ?;Q7V˙4+*NKk=J%}Y1KkPn-B<83p<3<$oĈ`ugms}Q>joMfUC̲hxAe^v/Բ3kݹ%Ky6Nx)zKTX8d~_Eoum 8^\6/K,c{2X+2֓;YkCe_WNv.PeZk5z}6yA|6O3q2$\<&^?GEkeUF%Z̏\8XH{z` K6[ˉ;}sԪ6KF y~ =u#f.(ͱ@\F .k&B1iv~_iuW\ͷ+gض:Y%nnޣu= u@5>MSKg9>{qۏmxm.sx8ͯŌs2xO- :&x]Lр&,:nvm6c3wl7u :{^mCa?i1գ}_OS`͇n9W3.u4N7m9Rɛ݄LDЎˍy6zyKzoLs ѹou kXksjnZ=RޱLgEOpi!Wk^>KmMlWM`qw<1lG(˔kX 5OҾ64=-u-Bmsuu^Zuȧh^q/9Ҩioж5&r|ލ5ߢW]NCa;:NK=Z]&5P^a{.י͖AF956wb:FߧsAoja6F ok5q}Mw|99fj5݋\a5(o/ދ`MX_LsX]/;0`k6[Y=k3(sFt F=/uG}}"$s^܌1pb:G/?ܔbt3fSұ,۴1%}6eJ]{o zȠu.u׌l${Oߠ8oMŧ&&97zVn+ۇ UHQ3Q"P㌫6Zⓕ2|]ݸ82~~3Vs{5Zk龯l9=s}X李 ~յ=6)ZmMJs|/1SœHN?'2XEF^*[~:mX ߶چeNKMz'r1o]/._wѬ(en*#Qsmջ}7NvC2Zs'unjL2Rr)pT05"7O?{T8ՊS{ݟEgY266dsnߠq2{d`le1^G)yelldl[7{ՌX8Ƿ鈼dQ4a^GM΢rpp5ޥM[+FfMnmْ}s70Co;۱@xhsw46=ToTLUQ5<#jaFY=8b<Ss,9ٺ\Kl{eu^Erg'5׆}?6!H1 `x˅L۱SHOҾa ;_ߚ}'u#Kl}Sl}sbm>d=_{2Y6xzg_ GEu=a5zZ YaxR|ɳ14uG?dZdTgWz_(un}4 #=J7ޱ`utzu;05׏sNwٖ3"_ ɤ?^usE{,܏a{K΍sI0+]/N6_Sl6臓 4w/G3}P)wUcKP.㼷ѳ7z6X1r`x~!80؀7顔9y68"~kBήoknƢ嬱uYTbm6 qQ1?FY[)rrFw!2}GSW0mC7KAvs}oSwJh8Yv8z{ݾϮڱ׾bbQmqp>e_K~b65]N:Cw~[`(F0OW#-=_v+ۏUVzenkhimԂ lKwnizk^645H6ګ:ͺ?Uxbd zx}X8d$UC#o?w{6,iweVA$Kc_=L0RA~$~ý7CT벻kk̩D8=Q7_0}u{uoۻp>Ɨy ۧs6ݍ]/sƟReβQفlh{ϻk\{JF<{?fuޓ*6co!UlhGi#z#륥̱ͷ?{tl]lmW,zW`Osгh({`Gb@|7տxYm\xpYWM'{|^U c]WAױr 9iDZyޡ:f|+m+#~n35M^N\XάSkm P߫[&,9}B߫98۲϶VZ*k:IճTC?OsDs%ٙm=-/h69usGNWޮ lu`tsJN[71ŗ-?U#)%!;=߬oYv5'K pcH4abuEg_jvji&ʃvֲe{]9(b2JXk1$5&U<֬f{Hq: U?KI[R1 d4H"5ks\ {{Xɗyxʰ,I LQņ"?YC\8:W͟f.8-cې Shlkn߽뚷KS+*Yu-?94v2{u[74hvgc1bM_L} ȳB7{rF_6dž{ommn *]i9Fg'nM̃;u ;7s\:68 1F;("u45l\᰼g-n,ww4pK*mZ9 =UC5 ΄FՆ IK]Sfݵip2 \6"M~Qۮsj շd\:YJk?{[4w5]/p@ͮƺwr+pHC.FìW%m\rZmٕ=:7>z~WZ[-z$}ZE-:iO:LC^~Ȣz}[GnK;%׾݄Ō3MM{Ct,#n4>T/} :[U8 _DŽL\5n'3ovK]f>SC=H9w M,Ʋmh7BVߍ[halMrCwXa~dhwfFHH#Iz8?{^ zxW1iû YX~ͱgOgz]#=.MU[N$H*\cr2)I'ŕfLp)q W ?=dIw[ qŮĩzj6Ze>&Zgǽ.ʮaA<puoI+]StӴ{szY29% OqzO :>i@qm @Z9[ۜګ{A.v~Z}oksc{Gm5kXJAШ~ﭨI3f?~iGcۘz{\L1nG̶<^-pn[Kjh;7ܩ<15 vY{C h7s[9CZ>e75 s{]Bkv5wO+̥ۮ:6 #v_ qV~#C2ѽ+O֪G{s16epv=ZfPn׹O7 ^z{nnÅ!n 4A}0Ӷ閖|>Wkyw=jCgeLk{b+Y C$2QoVS^apnP5Trlefֵ^E;I?*~9|oK8MFMS%½TnK ^4w L,1+ݾiS7Q.c:Uf|~?>a݉kYyl64q')a}dLI@YdVpu8?N>LǚyTfk~Yx{keAF?OilwYK_$G-l!q@ Kb`qP64(?B()Te'D'_ ˇډkCK.\PacUKs]_l*ZvC=[ KȰ"DgV'̞bGGKC+>9@tO'}9Of۷X?Q2)O/~cakH7Xڱ}OgfoNuv6^ւ:{gW5-^~]?w񣕋m+ cLrk25v$KV~뜴sM: ker k,72[>D lbCa}swm>kNw'~dZ ѷ_{wVHchkYYu.!;t5c˞> lxxx"H۳Sٷs[] g>ZZ73Lk-a%5nǷ/ڛv͏R8BIM!SAdobe PhotoshopAdobe Photoshop CS8BIMhttp://ns.adobe.com/xap/1.0/ Kevin von Essen yes 1/250 11/1 1 0221 2006-07-11T19:44:18-08:00 2006-07-11T19:44:18-08:00 524288/65536 458752/65536 0/1 5 180/1 0100 1 1600 1282 5008000/1420 3334000/945 2 0 1 0 0 500 True 0 1 False False Canon Canon EOS-1Ds Mark II 1 300/1 300/1 2 2006-07-12T01:02:30-08:00 2006-07-12T01:02:30-08:00 2006-07-12T01:02:30-08:00 Adobe Photoshop CS Windows uuid:33531d09-117c-11db-8ae8-fee2a65392b2 adobe:docid:photoshop:33531d08-117c-11db-8ae8-fee2a65392b2 adobe:docid:photoshop:924f63b1-117c-11db-8ae8-fee2a65392b2 image/jpeg XICC_PROFILE HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)KmAdobed        ""   @  s!1AQa"q2B#R3b$r%C4Scs5D'6Tdt& EFVU(eufv7GWgw8HXhx)9IYiy*:JZjzm!1AQa"q2#BRbr3$4CS%cs5DT &6E'dtU7()󄔤euFVfvGWgw8HXhx9IYiy*:JZjz ?QZᖾM/ڙ}8 ~,)j,i^~xCO#V )J,+`-iMi0 m^04?*iZuȖ9ޝp-.Y 0-SbaJ'n)V,Wvmy&$"a;w4QП$ p!zצ, l,Za╴%c ,'[P,׿JS BB7u8I#+Q`VaOiSo~n$Ŷ֐S) 5b1RZ : b[MIVYop T)2OnD{װ8녭#mw |@d({a ~x m_d,oKID+8;X7Zb𝇎L(B mŒ~8)8[1bWob7$␀]z>r]i<NԮG 14 a %Sb=U5m`B[}!p\Jh(MwC..@2mtƻD<;$% hkSTXiJi2k]tYpVu=p iv"oɳ(={MM|R߃`@7*FA]d=AsAV(!#`qd6T" => WSD6`LSR' P:)֟qÍ0'$) -.+6 L03"m1mIC|[41d_DFqk(ȱi(cIT8JA,R`F,ۦY(9]l +s Wh(IGn hPz|6@OV|aejѮ'olXd,ŦAY)g| PFk(+ظ=} 5"YlG(ȹԷ؃х ->^&޵5lmI w8N-`_㋍$QS4Hq_1bboSኯoc Ő+GJu[z`u~*RAwٰ3 #bC`S$ҽflP߶ oZn%BAzz+d/%.ms#U*iqPTt8OsI(isMČ-+UaimQl=2o'wŰ)H@hʚgxWoKLz҃öVQN*H\ z PBoNQwpBzaKlX_aT`d#ma۶Aį+iBm㊹ڜMΘ ATiZ41f E6ŘY÷߁(["AԶ U#`[bXθB9JurFr (I녉 Ȭ1m ]|Uc,hp6 6RR#r0 EN۵~EG|Y4Ws%%b߳0P*i `.C)jTY,;(*/"6j1H0Őizb#Wn3 &654Ŭpďکxl(TVCD`U{ d+01+ԍ~8Ui`CBAU=j1n@t=2Mw\!BU~[Bʴ5߮Ts ,M bdR iT&XO*vƑQ=>xaCQָ[&Sc%ی-LsU j)\rIARȹw޽{`a$aiAG{}`J^(^+q`Q({XJ]…ibe TiVN*}*m `Ki׮D)-e) RFv 4j oQ7|!oO\-mNA=z-s`|N,mjdTyF>p C\JBsi'<賽 ^]|FL=)XfZllvSK )4aH?"[a[B Ÿ.C$B6Q]O\^hOPS. `C(6N) G߯8[•߮o6=w='zu){W%nH{d))`B/1fϋpB-Q[ccNö*DZJ|,nt! ˜ Nً0Y<\[bT$502 ,ޤŕ,vwP)OO|-D+ =*<7(`-Rnq:qcC]7_ˀkd\Ыu0*>a SkJ S$6$Sl-XXA*"bĔJ78T Bb"TmIiX(v5 :Ӧ-B5~L E{m0ZUL,׍W &:$jT~eR<7_ϫNL9Ak&)5eeHRhbOt@F *C~NpQ#oIRACQ'V˶m\rOb5Vő[& A0iNy'a+^G& Sگl>&Mm'ߧ$Jk酢I*Hwܜ!mV?,-p q1!BcH8֣|R{L܉'|R;u݋ ӧ2(G݁Ekzo8)(Tq9`qw+$bE.Ko7 `mY%A>ħ `xצ*-% ƟbX}сimyڸV{N'>سIbp0RƓM+WҬD1 { \S P5 @z`?J2dM]4 θZH'];،TQ{kD#"QJjdD*-vHl(^ʼn\7튬mN)Xv$W,KcKۜ*1bF(;PTf-<РÎ`ppw튩1'a mL L-`bu\ %] jH1UH7((MŁE]p5JP}=i69~bB!T>ҪW|X/F犢`1k!&<1}ر*'aM;afۏolR(ZBCapnSwPv.LJKv09Q(6⦇nvLNIۮ!PkN$EFG_l-eScLP^#kcFZ2@8;SeO=q]Ǐn7Pag'5\F0W;S$ `7ӧSGfGj^ߎI(*)nc~JQ>mDp*q;d[-cDa{bUPT$`%N"[BK i)DIŕ5iC) M(jqM,*_Ŧa:L8S p#yTW uŐ(Y0 }p4y"R clxK!t"ŤrH1l!2>",!IŨ IŒoh*GΟNIǒe\~lPSRRTx`AX Ű(+ #c+θPLdS$Ÿ(~<[5pE6 X$n|2%c:[Mώ,)W&Z1۠OlI0P"<ƋQI]۞:R{r7Ċ؇Q&S49'Un9AB}`Mi@x.B2-23?Z!+4>"WnM@7>,nµz}U'Qhqy(z ׮,bў;#pqgK\Su:oi[9gZWc5E{{[f0l NێY)H6;}ؤ!'-AN`+4ڔ? .$}Go JH%qjT׶6Xkt8p(I!"vEŐSeݻY^ %Zwqw\Le7`: p^Z\C0`Bzn U\ z}ʼnH큪H؏CJ0E{MjYӾaI#¸BAㅰ(b F+|Y{6Tb$]-F>YZ Xhڴ%ZʲmCYEF<7%zT{J 6|PjHI4rim;GDZ}8ښ;8M O&ȧ6'H8)j\y+lX,XQ_(;oB5|Y,| a"Y" ! `iH3)6MBH~[Tl;afxJք5 %c>cZ"nSJx&܌vaQ@G :d\y)KP* {89Iel@[:19In \ũQNlRB>yc LwB6pӲC׵Gl҇sam j̔U j6p"Pq[APph0QJ*?*l,N;bPbJV;apw t07E(r%wRD%85]]rQf|2UV Nn1qf?.0rۮZbzȥ3p! 3 ЖN>Bb›^Zyފ=0 \0sUdwx⋥kɺh2(mF9އpqk1>hI$zT҇nMG rAIcUanhd @湔;連k,P=,Fݼ1d$$T .@+`=1*>k!5ZH>إYw]FZDd/;q_ů \"_ d ]o).U; ir=78U9bO;␅|:b% =02Eҧߋ;[@E)wQm3,CKbM=;ӥ:bP:1J=OkDp(=JTv8J+߮E rKPi C?22>%TR*Hȣ P$t>$$ \., i*qckB )s nƛŐ ,;fDiन4T$=IALnk(F5؏ L,Æo X={bTJnփjmdTv  dAW)(%u4}ӭk4ʇb@6dZԏ .A]TJOՁS=AypGbVw;I4 Vxv j(w*!I+Mn WDGbJa@""pI($ S\LA(߿~-w*bڇқ05Śo~سl[]op9QK\= -G|J`~2%JБ]8Y&ƝrQᖤ!5ᅾ)l !ۯ`VW~ Nn -Z$DTJVP؅A-v65LTB Vzw¶JSJlJD5+ iֻ`U7끅$e}8C ]P{}+PVڭ6NQdJkz)'"2B:4?kŶb1޻bRU$ĮzS+Jh0!W^㐪$4aS{*^[J d_ Y0VԞ$B~ ~< e ;b"}Aӿ|Y:n'q5~Sj;Ҥm2N[bֵ$"M;xh R16Y$ޟN!^~(l->X#TSsA튂_[urӾh~7Ŋ2]-$#|Z%:{!;U(*,IEGㅁ(녅SNH*L (Ś' QV*7}bE&%$AӮEˉKރm\[Hf~}c =|Z5qk(Ҙ@ޠ [WaRO?9,X[ji9'NOLUh۠,d1l.lGPh>XΙ5h ؚTdIJKYj*‡߮6Q8L߷l)S=6`,yuTťsRa\/QJZo(*FR$,b C_M SAPRl O WED aޝDNt]qdv>p2M8v߮- ޛ64j7Cֿ-rG##aOlZ >,\G)&ŘQa0)(sAEu!e`@zmBnqmpT]%u5Raj(p5BTxb$µ";14ʜ(Z[QM)iH8MI@ᅤ xPV߶*س Dw0늭#Q,2)#Ȋ|[Ű-R+*7j?a&ԀF܋ZOqV'm? 0Ҁ&L=dJ l RScMM*.*Azw{a2RVWqBef=%"4=ir.Z >.L6틟Tl.PX'xd-vI PQ1ԯ-%65\gO-ex}'&QIP7 , 67 Z")ApՁmHS&~Y(v偘@\7atBY;'&)dzTR;SۋaQp|262OёqQz1NC$m^r #IT!UC nbJGYUF*|IĪJ8X qfƇ+DjOlXP(vŊ!6Ł^PbDSmc,NDM0aT`d Oؔ 1(fӽ~UűqJJr1Aހǧȴxj8Fہ Q vŅ/i+?~6 `Ԍ lUEָY-?) qBJ\XG^jRa@߁נbm;w;bNEM֘R X?P3 eFvEH-ekQ 5GL 6N!?|-n t-'bą~5\ IM 1!" M+p6(M7=;[$VvImID&w`Q;b'%>2L17رqNز .Ű(aHv!BOa,JJ}1)%(ODQ)LµN$,j:\)(V#f]\ID!nYRzL(cڔ%+Q=@[ -H+.QO֔f(=qbҴJmqP4Ij"Ҡtiө|Y01fMg9bH>vL92B~Uko׭v+H8E_2X\<~x&" VӱRG}PWT%O ,X=>"! ` ^l JBzSTmdPT%a m5w?IŒ0P@RSl *klJETl bB&^թŢA2FP-`X1H*,μycN Ӹ~9m2+aP z!0Z΀-#XVŪHmQ)WE*$Ģoak(ޝN %>xX"#)L)|03ر)]@* ih{a1C5;l@$\ }:5AEPoၪJS.ԥi^s)mڭHL]nF;NxC*S_|."‘i4q^ \b(G|;\Ik Ƌ AxmRp*@7ҘWTz֧T8WqQ5%MJMߒ ֛X,TW+`*k~' | 2vJ;;ȷ)|ZG:b[l䇑w7izřL-nr`4sL5`mv۶M n6鋑wWߋu,?E*0z C.(ڛR-}?Y"8FT11A ~xin +OQ*mT,UK^Cz`]4> (M?-*a8R{b$]I_ƘE-:5I() UF;bąx;ŬJ7j}صB`j V - 5$"+6V iO N|Y$\ JI N,™L,x Y3j>[pC^آB՘PYn9'@0*WT*|:d$\E;rqCS˃SdPdlPUj7וv)FT SR:KQv=3E@j7k YL!AExŪ|XFiӦ((  \,U4Wo Y"j|zƕV:6>ml*Ɯ*w9 9j6*QхmrMeXE:*4c:;뿏ݐqrEKTnJ’2:=L@. (r_:YHp7Ŝ%E>.c:y3>rIGo&1Ck%E}r0׸vqO6m_ P qUHlXR}D!_>(\GCb<k;@~Ȳ ,}6 R~b0߲Hn QI=E --e\bUqbUmLXso+SlRF,S2Lc]L ;o^[ₕ| (3.[LY.FߏyF,oC)%$~D$(w=dp|%kSL Ke+B+V\y#'ICέOpݥ?ϭpɱEKFTE .2HO^)#.^ ɴFwlӠc +8\جNqn F Ev-2MmA9PxSjtSHE:XZ8 SxJp1 qC^ ; Y; 7녰)f1mKo,T遱MzaPǭ{{d 2R6\mœ3;l;obFcM$ׯ|\9n@f@#EJ6PsūǍҤ44B<00-g0$4(=M^PPw HҎ7A߾6Q6]0H#mҫviołn,7ūAJm+hcjrC8YP[-+߷ȭ"Qmp=NؠD5~, +o4ӊ8ҠP}- ,dI@DPo.[0)\ڤMޛ`a BA0@4v߾,DHF"@x`k6*H"вW?#|7}ŕ,v\m+Ѓ^NY-BA!Ř -Zu,j|,C{0GN--SO `WT}B7kbQq~&N kTN5Ł S}8&Vdr`R(E鋔 D%遠Ssj*_XzbJl,H]Jl+aIԎfi5;Vjbf{DqTD3+Mȷ{eJH* 0! }|M"Њ=5;~_N,XTD,/XDb⩡?EF *zW)TdM4'z}Mw$('n`9x1y«x[Reㅚ)dR%)V98ddCQDmCI{b@ENjF)6(kZW" ֭Cl :űBE$0_+5Ӯ(+LC2+)<(6\8R Tu U/cw>8p(9:ž*%G~jъP|k(M:m#caձk*z+hw6WҸSkm1d]6' ;C`l*ҴP[C0 %FESfEh2k vzuN']ܙ#aL39X4ix3Rv풦\*tCS#@9*gŒHVn/bx1) "*3eǐ!){ {;1;3B/ \]NE# XxĸU"(6Œ1dNZ_;Ӯ$ r-* *)0+)PhxJ)f^ ~XMXir#,o šh1N…A'Ŋ)xbZI8V1d"jS;Yt'nزEژRxz}F5EۮEi%ۮ8W&3cYD+W6ڵR!S4D wŲ;$sU9-EJ>ic'sbbk(ax?*bV"`Jf0Ĥxm9QI AZmz.E_M7% ;=MۚuCCĩXJd^Ϧ$T=|1bQHpӦ"P|_j6"mP7#j->)じ()TT`lI\ ]ao)RA;kO.()]pCk/ :z1\߅jġCxQqS ,J*3MP1i!tQxi*+JؤLU+v›RqQu'| ГF7 T [JW pXh<~2Co46ŷZt_uh:̹8"葹6;L͗+NGk? iijiih놖 *^m屡;WL8ay5+N+_g"zJ>O;5D9&%]+q`QXuŁiN,mJC,T\N{ʊ If҃PWP``P?;{MWrPT00 nA4G";xdcs%ֱ7' $`nD!O/$h7k /uYPK$vjO遘N- DZyAinH"KxY3z5u|&yY.,D DcM>83q0.W5'T*yJcif& S96]^91; p%EEeXC1kkxτ2*΋AAe>)+߁$tη^-ْ֚ PS,BQpw[b\ ~&w#zu9v; [I4;p `y~$dX  \y@L ÜHvrAJӋ44:i@WNNl;u9,,p2Nmqێj29,xuoZY5(tAAKću"?'/!,)KtcY+V~$JTA":;ompi_)G`zk0LW.MA8E~3-~-Ƙxtf<ō4Wkz0ے={ T텱zָڽT}1!PҞ'¸P? +OI JVDZtżڄ}8{Tt遊S20!^' oぁF@W5b7L d*O֘2=_`VӦlrŐSZ|Yeo WHpPbQ!c>p22m+GS2SA 0:{ P8z|l: iWTjaE*+ZVF,[4$ORIpOsF)IH߾-%Ņjv",RUnDFöƕ0!w^alYW 0Tb0%co,UۨuĹ1)= )A, TkКnE|Z:*>6Q$qc{X+A#dK6kZ;[R!R|7 X7⭭+*\U3׈Wȇ{6q;J-pʻ &oJ|JnF@j %*;ƕbߡ G+Y"=rMk{aE4]ZU巏 *@$J 06Z U{x`likǮ@ҧlRXM^ф8 Z S[$4j\+xb(I@ּ6tbQ( ŬLMIEGA~ŐXO݊Z bGzaJ6Ő*;-}0GQx(NCx-[ҺƢ"r EWSz<Ǵ7umB#AS4W5%RAZSqb}NrLmp)o_I%Hn+mA0OQ`'o.5;a `UbE1k*v†B$,&qJcCCOdRۖbE+AoʈKjB-=i\ JGӋT&jjiJT|1I,*uIh W6N&8 AHዏ$%mԌ$K*wr1SuY9k¹'oZ0EȁJmV]1OSVP!>/qlړNAIhRIAh@WsY\3rc9qOhT bdDċh 2Jۍx͝+{DŽǗ&f,ԯM0 ),$clS/EXkE=Nf'D.1ǽe@Vg6*<gB *|Mr%Kf̃;C1gK7MEu[q]b, ż."PKmև"d+@)KH7Wڽ826vň g.mfvj?GLuKddO4urҳM<6ǂ- 7\$_[Ӿ=8X uȷѡ:KjtF# |%ćBw$$Qp| N[?T v'Db`1bn AWR]qa҄p%X54`nOh8^ģ%r;dZD _۶l3Wm5W0[fknH^e )hm^S -NHӦ,AO"2PhINaAoJܞز)޾lHĢ"qDL]Ik*L s 5J](R>^1+϶I[h=(U4zb€(b(05MŒ5~Y)-d^FV ``W'煋jV=bBjHlXCQ,SZ{b6E&OuŢAQwXTF*1Ux|P\P˾,i\Y,u$x'AI,6,$ڣ|,db)O>xRJo-(B; {kŊ&6CU8ر?UHzu޹1akPs'`OmȺMD/f+s#PaRvԴ{Nt4]vy2Үwz IXN)JW$))^2Bi j6xR +B! \ O*kDJኝѐGNCZ&"텬t9<, |)xTNp6 }fj.b^PLXH2>$ 8TPvaO T(LH`nWj{dRzbQPCdZJ\XabO,jJp(Y lC0jwЗοv"%WNM 15_MͼQ1A*i<[fx2%XP6kHFza,J%Ke;-|Ppold7R` O|JWTȭ:k 0@)eJ"#4"D9ǒJ"=, *3QQ7,3T]w 9]끰%W$ 80J\|jt|[S0[%XGjWvINSQ1qRˢHaiD@zpBazlI7ץZonPִȵH ֘C]t>++.luSCJxdS% N'Lr.X%9 b)_.(_#3cg 2SYuP3dq #k!O oPs^ݰMC'xZvl;4%C cUX `];K1ىx1M鞃'h2@K?J)205 PjRfZm M[쎃.DP;n61nTmoA-Jh6@(gzd<!Ӿ4c9n=leUOPm!$̊[$*@#cw2J@lƦUizゐCf\6~1X~/oD5+98kqӏJxŔOq`J ﳝF,-m<HGڧoŁ HGmϾAGԡi  5mR  iSL,E]<Q$Ւ6nI:cu('|[xBZRIrhצc-*{@+ŊalBMN)\ | pZQ6 ZDp:bĄ\3"B!\TT—CD^ƴ, |lUc)*Y1gk8mW(ڹ$8fN^M]lQKbB@Mj?灚T+ xZԦ˜ R!cRGiݶ~ bd;ڋSҚ Q1M ذ(no>XXܫʽKnaL%P'"vkなH$wŬJ.=ߊBUt pkb*Pb( 0|qfTd^g@N1l{u\ WPh6o,}:oSŒ"3_zB2Lb^: \-Vܓz׶@]#s!pҧp}=)EJ*w5;temeNX #H zb5ŋeG•)TdZaDإh#zm%x堦&$"bi_ak(}T$.'ZִBm 4dA.Jmn tAMlY-)Ӿ-"ᐚE/- ^GOnD^H2(!Z7$Y D66 Zxaks6&͠}V)[JRpiCHPmݱlY[abvsY5KmkBZw36ϚR;tE )*QHeDpMkj푒$"YġZl,[4S2#gVOJug5r "!MdCS公ckS JLز)[n1dwź)Uܝw%7QZ#gxYRT ?Uq,ǐ5d֠SЃqj[۶%M-=-RWQQ &O2M7brHGߒk#akLmN"\O^ѭVY7sv PfrܭI"^B lӀv5$Eerlt=( R FL68T)q j!Rt98s*Qmu!Hv] ɪR{hϳP:HBwԟ$('t> B{afXҴƸ{JdP"49$rh`.9i =2}]rN $a1)vnʖ޽+/[&☄L.Tm挄 7ZU)%Zv?N+mq#)u~Y#o6֯y ɱmdτ5yXʃ,<Ǔr#Ż2nL/CLN`Or@ct*)Z 2-6ŘCK~`R;5+Ae0CN-EZł5biESŒh+ I'-2`dŘn Q=A qӸۯ\.,[I2v㣁JabġۮW ȭ*E%F!8;ŢAlA;x Rk t-V]U~Ib(5ObQޘYÿ\-ᦵYagLv9QB@.w-:#?QEQ-P W(-%|Sp:`=2*J;dK5l U/~<ʍLl Jῆz'f8mQq ZEGыZb9,‰ۦjM_pw0۷nr {(7ڞ eĵ?#צEʈA=|kY*6wU ( =qf#z(+MEvbKJנ|B EV$ c`*Sh?"UM4Ƞ;08y@sU3϶L:M?&*E 6Lr zg'(&悴8ָ! KDnkA;ylS07C\JJ~y;S ER~]Z$ ABʔ49h)KSwb@1eJ3DJR)V?hI_2&aL=OO$bU&NvzY_JpB$Ԃr-ᵪZJI|  oNl9PJnkSOs|̼ͥڞ&cWicdh=]Qr4"([s g"UuHFCeSsdSKU:2+Om6ENI )ܠ<$od>4 Y/c0s>mi$3 fPHuDr] $69(΍G4.FdxLlt W;Td9D+;$oR;LgdM-#z?{kC, v-5 NL57ڽ>,X2 -H=+%ԓZV*" CHZ9o] R5Ű Qx3 UQ[\ ԭ~_T`O$tu4s"1bǵ7n`.4ű-ZАH{˩c3 C)Ly삊N{ajCW ኦ|)5߮D+Na (ww2>`~vv Y@Wƕ3芨8R9b~`* qfS\)VCkS0 a D#bjoQE9 xrE~Y P\g>o |p2*R@>_Nء)Re?☶ C WxW s2"BQ/9.}#MGdm"*zd$# UZtʤXDV)P|͏45%8,)Uzf\7oKfNͦ0I/j;gi.I<&2:AFMQFBh6Ũc>8+ɧRRcLYA 1va*sߦ.TBKu-*r$p Kɽi2! h~"hG-[88]qT_\zK4v€r{bcvڋE i㿎EcZҵƚtfMU\Nq "E 9\zŸG\xʑ**)4$4od 3"Gq$oQ\.~-ɏ Ml 5Ĺ5l$Ru9 v؋'Y hFq$9 +ȅcq!}-Kc[U*aȕĕ%l $wc&ؔJ0Zk 2l„eh7JpRLۂ]NX dKr -{|-(<|@IJ\HPT=hK zd<*qv?E(VWDdJFPAkGg6!j@휗&)A> "EvK=Ƌ$DD 啖Vԃ$)ȣ쌐P/-C#f@}CoRKAU`;5g;$T7Z *EtȼN,B7G!+Z1Oۨ2yMm?,,=*SeՖEap8ӆ9j'b\w;omWqeĿn;v,AC bz+&Ŋi@"xbH FКuDY1;oը7IzגpHb1J&#AQNM4V)N+dVsҾ8mMRM@iKy CAbw}83{V)atmT $ JN05wUxֶA۶A 2l@1M0ֻd[Rۨ:o-ӥ* |YDIn> OtH AŘ ?'m$"Î\4QnktP^E6MDߦ-R؟#oI kPP`WRF*Sw8)2 ,)\!(I pK.ls懴9zfز8]NUIa26s_ 1V1QRCe'dB.-7P}8%2%ˁZW} PM+fTrD!5ރ0L*m~&;~h&匉#<[Y(ȼJ2#]\-eblR$l M:Œw$,JƧz$@ OR zd\A|-j*:QXPEzW-e#OŒQ`zj(dkjpƽza JT텘M6KV1Rq'EyS/2jWהrP|<|jr8YpF3>[ VHoF'#2vxK!,zϖҚWhC2hv^69 r+Ņ9IoOs-#ks{ [ƏnҐ3 ",AB GZ lJSyi͇!|˄6UWr7!VISa9Pl̶a9 \4N&kǿl6Frޙȸ8'p|qjQةj8 )%_EAÜrXdKO49z9Sè+5Kd96g,]5t5شKi[f1-R(c*%&5%Z8Ǝz,0(dFo' E Q7jFTp)b`CX(v*vn"V]7 6# M20N652jrV](Z}$aUXqM&:Xuw؍y@-PejdSb%XJ $;bƕCWo\P_ +jf: XY[ ,VG$$/+߷\QhY)% "RAC2~*nH j;_J.&<ߡŬR1cK ,r5G*."8 )|X\h>RP^afJSC〲SraE}#AJZ U:x䘩J]a PMfi +Hjă-tmZ}8UV#SSO n:bĠ#PHKiޞ h00L!aZh!UԑlX*P|Qk#uGiYHP6@XlR01d:S%&P:o|)Pĥr=)xjI/x>}SsQ ]T@G\ oToY'`  ?&P<L,dS8RnNߋ0tciLY[J5? Q0{ , 2&;j).LZD'_s QqHP7 m e5$l+* uڃ,Š7B+R~"-3<Ȭ+AO䃈E&5B2V^;b;ʽNT4Ddu1k( Rp#"n A-D*L \5_(k q[X4Pk Ac$#G?v9ڇH$:|Fp"bLdZ|_c3Ɩɵb͢HPF'|ݨ:oVDnr]2T2atIZ۾_؄&|ʀr"7cʤ!ە:/9Gi"jgZd2KIEj-eQI ݽ$(JiaAqd}`l'\vl d+ttܐ2^8[UbkTSOS|Zd[ŔP|cHUkLfi\T:Ca5 hMo.U<ݰE[yZ J8ldpc1J嚙vGW1s4tDBF̡O!'[sZ&iVYfqo2 |gc#?P;F#pḡY]ZM_Q$ 3GL?Gg`@#⁅A t4#3yvs!KHn&d\qLO R"CUB !ck UB;qQN?\uR+`~'S#WrcU!ɷRdڬV8%<،}Wna9Gn/Z$xw 幷{>*iLe!?l@ u$M밙)RwcJHYk%2ʋQ4ąC  W5- $堳%b 7y 'Ofm'x},$ FP= ԭME7@Q1s v?L7|j!{ e pC\4ql aBGd7-Zl - .\Eu ~d\脾S_ bj ߮حxx}_e~\yN,"O3~_ܚ_v,ٓ l4O7)n)?g83+]R90;rV)$)Ѝ,UVŒ5|_ 0!WDZXƧhM,GLRō/Y;cMyFء*60Tm0WnzT kw!X_zbT&nqbUʨOl PٯӁWvŋlTtS),JEp 4Œ#=MT7YZ TWnYpT Mi~ءz;@(w?,ix4qOaE("P SSӦFj>7jZEkQD#x`b[]%@OCRҪB+l(ZQSAPw`0&|Y`@Q< :Vܦz־&)dil[QU;bQ_ ^^ۧjKDb%jp;t!5Z2N-vVS M~ y\< >J44˒(FݍOY> >;8\ Cݿ$]8kPzTRLY;SuŰ!7?3ێq_)(O ߶-% ԥw8ZHFBj?-E`k-iOlP"a`f˄Gn dCJ` }­H; n]Ƹ\h}MF|A}5ZhRZj B:1>&&@W}ZH|rFe0f_V7._@Aa-QtWo+KOsLzNUOnWJ;⒨`G2) Lj-+CNRZ[`F7_Y\i*Ěn[@ŬEBwj9{km [ҵ#*;*d>{0-&$eto|<) v4Ó8 jN\&9 f}ܨ:#xvaMsmmlҟ`mʣ3s yI$Ix֧:PmȈD`AR_ Z}W}lx.n'oj2Jn- u-]CWz`IG@| %O-%| )PEV,1Ja@VR:bI,\A*"֘{fl Zþ);XĥwHz\DJU4Db{bJ0}iB]D@FXlX q a;5.,ylҝ78oSt)vmL tŚw01;jZd?b6&\ jd\ wӫ(pn 4ƒj lW½3W8hq6D%p 7u `FkP""ZסsN4cg;p3q[Lz*hhX|iq13W-4 tCgD 5.lYv6̘9,nx7?q)27nNԯs=SZlj)LZJa LQE!~|,r G⇮ :{d# RotR[GZrKk%zqLk2&@srĄw*V jڻQDw&6i*xg18s4ɀ/P:恦xML^|Բ- w8Th#fEI}Olʍ5J;HO!l){}%㬒Y#;/L&SzX/z [}=cj%ވR[-5) '"%jHl 7FMHYZ5ra<~bQ&ɲ1kh.3D}N41Ax|DM96n7ef[}ع-+)#cv !RI %|7  ؞"E" yGW-GqEzB܅ؓzW-|7;V#>ȳu,G63#n\'GjDt9@w%m$UBhgb|.OD@L܍]H)AӆvZiŔ-A܍l$}ش{[yn XEiAJb.y H2.D)" )<+ЎqlkYz/\)WϦ)*SY]ƤPNbV$v@dJt`n֑ * ^~$)p$o| UGӦCuŬTZ큁^8w!Ӡ={bd 'ߋ+Z )p@6ep@o mJHȮ Tx+jRE]0P@ܟbJG\[-j\iNI +FM+C\Xj|BDEm|? r>[`wGݻbB%, ;{QM] )mZ\Y* Q;T,)\YZvl C2S\[Xv%a\[B[rz`oQrE @M+ө遘TWl,H- '|Z}B  xE*Ʀ"3ʼnjCiM{0ONݲNDߊb"-ڇs)iWCdQGA.Keee:bqɓMQ@H9A6*2LCkⴵE:x!} hCH*v`S5\Y*FxXjuŤ!jB)Z;`klkN9b$h]y {f>LLM7@%Xf]2J#bki5$^ u8<%DGʼn3[vϚEwB**mO|,IXh nPGSڽ27lmY#݆@*ʴ2*QO eѬX8 d;֕2w-tabMOz5[ª|jE(̀sߓhs-> ?U)PT;W'lEuc&)]lr@[ ֔|@4J(!1 =ˑAY9-ġ\-ඍ8V ~>ذDFk akI;N,[Zu•+煐Rt0T^2:b$#R^+æEVF%XWbiD%-d"C"CdƛE6s֕X t 2 Ta 28]𥿟lU=(\,IГŕz[\M|P Hh% ` Fى!i=(ƿy n8!U‚zB> W* 5vʼni6, "" Qq+Cۍ6ڝ:>%Tjg| hz ((L, 1`7*.),lE׷\ Q]b *ql }Tm|R ;r_p*|7E`j2(dmBGIKQ5HX/,l0f#a!iCquR4UHۮ5d ҆rpYku#wf,f;S:t!wk7"V 0Iq%NoՅ*rКxcSmœJhE{bbB*26-EoZҘZ^RVCNa-蔢"GOlJ_-}l d'rYҊnz}96loQ9(S~й^'"E ? .<)[\o7IwZ8? bXlwiFG#qlKo#iGj15 p}RGt=GjN<[BOn6& 5>P{žN(s۾@B%WW aQO\B Z7ʼn eAȁcD"Xi5n뢕#CՏBz9i8ċV6Ld[q(H4G*z4c OHYw` m©ڹLtB]+zQY& <#[!''ך d'x-2Hwi* @:f4ņ [f*0rBMdHk Cq*;o+M 64H%Ya7!hhv{\Gv; ]1ܣt Ӡg#{[&2>HG%4%O_\ȫ4#FC0z7oW[Y.O I?dF тYs"ҝɎhfvZڬr/>tp4_2jQm''̑FL"4;Zy4<;8dVW=uTp1ibvV0I.)cbI>֞> n}w&bOцQ-kG{9DզQF n=ATCsE?H2v-5kV qJ?˒/ߏA K[EM++/QqzP9YC dH~?L+(3E5biA^۹,~9 椂o3Wdfܹp4$uC1/R+)3>klm+KHqSye-3[3Ҵ4zZmۅMku^XH+98;},hg7 J7HVĵ B||+FDP) 4)YPƢ{HZQ J;d7O J<1A֖Yy \؞M5J>Y"SweξH$h8 NJ=NJ>2bK/^)̘+P֟Nntfwr"idm\ r@yE-􎼹<<gX1SnnaܰRj1gbSB¸) ȱZF1b bm]]LU[•hnMAD&[ypw鷆^$ z+9Tˁi1dNArMv!],Tdoۥ-KFؕ%UP8hڟ,X-ecᅐ*OыJ]>xKa~yO^ E%S9`am[RxR1em6qd&䐴nSd߾\w.;T=duMBH+[BwǯJi}z+G.|c$"ᅁw!9hL.!m*~/Plqr#TD"brqEG.S|Z^Tb,2S~\ K+Z iA *BH#RO|X7lXl69?K QVs44A fy&J↼y R2-9@jhN ;xm"#03ˤTצV7iT:NJ;B~ȍ0 nȓ53}ns6CƤ|]HL4^9@#|ذ(&7dq[IS\6~6;L S#۰\"ـǨ~ '0N[i Zexj擴8y^]WlR Asl$_a@+6%*$oM ;6)M(Ui@hxS=D]U *j q1[v:>]\*L4*rU ]s*VG, lzz3t>?5[8)`8/BG97UcG(&*@Mgӓ7$OJ7 YO|̀.f8psKMsAVnT#h&bYw9O$e[6{fBƒ6ܔřG\\Ҿ[!lՄWjv;|G*-Tf=28xt6IB AD˨]F(cZL4?Àa j)Osݱ?݉Je%7NW.%qvrn*"UV~DžjRg+ײoƿwb5-fUG֤y'cAo^/Ƙ.UnyGrQL5Q>5=LIV3ROW9Py\3tcܤdOTnqÊ\N)r8%ء#"ENvTTW.`@/_d8 (yFM+jeb͐n2]>:P3phOіS:lB1$i_|2>SLlԂYU$Tm+NӇ#L vMoPnN4B]wuj$R*RD Ed;4_iUUyj?8x/#Rܐiˏ.vd,##LVZF%n,]5oJ2AUAa^->L˭2 HQ{dqHd^ iCM@ q[UXqEsǨ1HQVo텚6XxSD_$E}*)΄moYZZbZVBi):oO|͢AejOP+09~IEFރ"B)lZ\ZL UEƘ|]A|қH*O,Ru p S®\*(iJl=1eK9SJRw=N,eƣؔ$ h*L61dmM8_Uhޞ ]mjbBE80xbQK@6^زWWTG<(q_UBTŘ(iV0,H_ -Q{Ž?^Eȉ@QփJQ0ZAt"kね)-c:w4ޕ>^h1F¾y({&,T 7'WCp:w”K "\3(7_|mb2kʽ)vQqBMwp?~^pGۏ-`p >'|*<T.6-$"酮xX-}PPw[1'EsM0pOV'vx? To+DzЂg,z-7Rԥ"jŨwhĕlS,`ʨQk^@;˄,4A^]AAT$FMXӾJԖgf6G&, 7r2c^\(*R50`l(jVv;DeAV&G G N"nop Қ@iִ'afdvgI!{W3l2(ȥ#QR8U}B01O" ^;֞إg+J/-N6)Gr @>f6سK\ŋ95g)(6L-:t' AE5V9:qd% 7c]\sI UPTWɡMF .&J @9y3XڢMwlUs!vr`K pJZq&#eώ6n#&o~y +jTtͅ(ߧӂڊ6+H[䭂ڣWM4.kj- nѝLjll476J[9C)%g;({fF\0(^ P+Ne x,@;d `JEH? 'mڿ,V֏ }UQcȕD"o|QeZ3kbR6[yRI)&APcR6tf4 sAmh&ʕ0+SoɸdB _I%jڴ,cE ߳J~,UKŋt#zHсe;Kl[޻ˉUcJyUQzSn[ U;s(D"HԷ B0#߾F213Ik)vgӠ'Ƭ7rSf`P@+LCmk@^zI#j'vճU?, +U˅Pҙ]5չl)HUaBPҝK?͏$%[y+0)޽)[36"D[U] PSmic$jJ<&~"8 سtN(k7RJ_ۋ,k#(#,9#P~I3J}?/ОAsT<nxQmVw6xWm_Iegu !x0hkNT5Zmu$)N@O jQk亰W6lI\bw_-ɑjScHC"}''kQNdF-"M3㓫+Nu],qgO ЂX 7ck5=֟gFq/0խMܶmmQlHQ!\ śKVXF$,R00v*(q]LURU0PAQX"!ИZQ2m?uO_|I-A=Wɉ4mo6C N6Đwag {ПŠ~b”Za:dE\Y/CUkҘESŴMGo Tȥы0xdK0ƴE#mxEFj!-%\ $.o\UAoAR;A-mSC bdM{bWŝ+uqM) $duŴ$Gl[B(p\(l>䖕sjקl ezmk-"Z6ڧ05L~V]bj⪋NB%Q(Al,)ʵYEqmDmR6ۮ6 Ӌ0P7QStJWwv6@SDx`rbT7],QҘOօJv-2 {RaM,8[-a`tSJ):4#?SM Rت6PzuQ]i3'Ӯi@v4yv էoa\#ma+d!E>8 $-UaJt#)j!S]#m=?h-hrxD'l,* Flu%܇67Jȵj'G01T!>!a@ZץzTemE;ɳ{Ԏؤ/f~T(lF K ~q~1$SV%!R:a2[Qԧc7de{("C֕%[-CPMiޙ`.w(u8tN7jIM$U+}I=A}}4R*`|7bRbԐ)0hib/PQw(ӧ )׮,I_ъi 4H-!੦~4z#$eMyxgGa$ј ENkfM2421;f~hvU9ڟS01\nF4œ#Ḙ8Yxj5,4@Cp3i>;Ib`2#vTMٰBOND9 YZ(Y-;׾FGfqBx!~'3k% }Qr8,+Dxge#x2FGlݲ&QƀuS)[E$vTdM""LbJd08phRclq @ ŐqFg@f9v*O`J1*: NTKQ(䍥V+Hm9ɦ֬2Ue^)(.c*,rbw{L-k~Ct 'ѡx->nWXF fǣ0O;H` jaU9H?o[3"QYHTfa!jy/FK6u9TAVQC!"Ci5`~ ]Ȱޑ0ԏ|Ɉڽ2:^Ȓ臑d b4ʃ JOF:[~8-Dɻu8.>"M9m$EiA8UpZ y*mP+#Mr1c oJ}V' 6p?Γ!{X"V r o_bacI}G*W\XX"ҕ? __,6Yd\1}LU@W.8)xZ?#PKcb8.Xq ARe :m c'Z"ˠV? uM@(N ~э$n.9TS EFB)$kV:Q@Nm 9`lZr4Om!tec1 ~?\K<ϭaRW2FSY8q9FÇD_Ē#;*&%A6X4T``bB) `VLPb-⭌J0Εx塘SO lS d GSfSHb`w߭qҹ&cѷciOMe{h{rA( >بWm lXңlP JVV)Qt۶%*} W| K+\"mQڟ, $+p+Ŋ$7AL8;[mmኴWSdbM )_~ۊ*+@KGNŘ(9aŸ3CzkwhP)mSdQq|J>g-E{x[](ޤ4ֈGNT6lRbG8(=j+CMLR&dz Y(ț{b!bƿ=.LJQrmLʊ;﷾#TV )R2)"aj)='|qƵKr9F=B!aP?V(n3C튢a`6GxkavQ~ :z0[&Mgp[4$w?%:'}I02+0 Cc\,Œ@mY*ŁDFiLZ.ގB|r%-f{=#A:Dfڇ9j5Prf2s{B8SZqOMq+˩k ˈ L@lo2ITED*O%hx냉Ssoq%6=HxY]dЏrf RC%ͪI&Uu`{SJ2 F QBnt9`K PʩV2q4a^W5c|kԑ2?I=țMd5'm?Vt։ead3!AI۾695qLiA$ }놙RbRit=H 2*(ִn$]Zt$ Zwer sq!ĘN,iLØq0ۨ_NVɲBȌ_]2B;{ں$4N3ejISOi!oꀤ*?͕K{{˨t#•2o6aҀɭekc@ѨS ~ͫ9_j10+M(b>ՕHDQPS+%D 0+[Uwd Hw$̘؊/zkj/I=eg}jb#NM[+r_鍭DPv]&YO\ "OIGlPr3|OSU17G -h(cQ5mfj*\Fm-uEjL?"#J[Pw#6W Ļ%ʉP Aɳ^nw\Z^JZ taNS"d6"~0VrXӥOAf1Sk-^K_9" W}vNw-o"/QJջf+;9X&,h@UpY𧖒 HX+M#fR~Z| r3)a ϼg. ,M^?Ǐ?0מ NR/*(7 w8eyrZȡ܃VP~.ϯ>^9;%YM;fncB j3BiS/jck:/dv[JT|Ce-*UŖc3HKIı--$S 3|+qIŗQpΡ3סS(m1L-ZeݨH-Pd@cH[^D\1) Ġ5AsBTQ~QSWn\9)!Mv>Kb"5 h:xd `QhxgNe%5W.k$)VC_KaX&-DTTmQwl/k׭;|"2nInjJ[M-Ð"ۗʀZ#I/.l̄E( *H͆rEb92XN.},{1&U6SeV6ԻIs+V3eӦ7PkA AKI.muc ?i^?>xeo( q+bH^rBH C0TȘMVCSصeEp`o%xS f.wIil؍1j6ڛWƘC %SŊ];EZc;nvlM DPT שŰX[㓽k\-d.g@SjjKJHŴ;" -+LnkQ*X⨘\O D+WWo$/Rf,JPJwI0T,LW|SdqMc+x q^ML I#c-$p*E+JRf1:ⴈjbhw]8T qk!ƸRl~ )YOXNqU{`UTn.6JXR{4ö*H;b)S%gBMqf H߭qoJ.Hj~8Q)E~*dKejPIĢՃ#\ %Bxw%7V=Aq&MOo%FؽT?#I{|kQz U3Z~Ri\Z~bl˴7.Uvђ{m'A]!#v酰4|aIӅR+Q&S}PYo#4A xb3N<q 7OtX44c4j,F*kc" J #lPM{6TiJhQxv֭$7 +I[MbE=̱d:qz?}QJ2ZxN)&TB`|dNmcKK& <=|0PZl\k^ēZ;S-*A.U~IblMnI"26%*TFcd$ps_eMDF9/S}BgBE t>I 2?8x.6_OHئ45-tWӶ-235GZI2 :dU>6>nehim |i eF,HQx2J4+[OpL,(U`l 偰##MS&2\TPTt5>G@)ģ! QN OV#+1L("KKQN LQ]X6qek]Jo ٱBk ۩늸-M*ʧ,2GNF%@M3Lx+LmR6Ҹl,EF\CY 0ӋZ5łآ«Xָ .큛\Bjb6ۯB} &{jdw0Sm-j<+O ,XS &_ՒpJ lU44튅{vޫ h%. o!*Ӯ;WjtȻrCg=MA 'i϶ƛWl9 `Rn;|daho~,Ks-s4bPZ{OqΝ׷8]F^3EƄ@B\HZf,C|Bv9po F)#J uJ%|?1VX(Iyq'PFڈ(MڇzFD4Bt=EGC ZicчC\M 폋//m#9~FєV2UPGQ]H+JVG"Un(6@r anQJiv>;d ZTm'h`%u<v5#& <%I`7屓p(N]f <-P;\GQM7>V?('p:4;24kC A{' e),GXW/}(J@DNX?CyR;W@Y(Q*o!EˀA<$x8)!Mɤ-3vn.4ӐD?^N1ZkGK(Msʦb* ˡo&봷HV=reI2)#|p-u>i^;VyVXR=w= )V\3x;}Xݖ=&6yoIAܫ+.2q'LD{fKˆwiM+AR"fE=!'Ļr/[k>e tIJh)N әԯW@>̀n9 G,(P@5<?g& skl@kR7 0*#HeeEr@VhyFAȓĦ6[zwcIǒkm}nYl:6kdp 85g+^h0~L{`Oq￉+s\AVx\DŽ5^j4AtyZ5<#W}f~ uYiEjӏӞv"nx%/F.xՃV sf lJ?64nlgZ@9'۳m߱kr8[;=\09P?G.ĉ88vHq<3?Wгk%v_j~}X ̨t۪R:1;$=PK EoAQ)2j% %0riE Cmف"iǯm}iCVS&atGɌy/{E"=vY%NH;:i%c; U@R€(k9O~G`Wn1Gո藨vDE (iO =z6@IɆvѐNFI;&iEEO(4- hv=4 .OzTbZHGK&0ߏ'`2z?=d9;7 ù=F E :kIPM>(k*8|@|?W!TLq>nG a.w/1XIJ}ir$LIwq ]^ayYD3G_?'(v`+^Gn6Xfdl?ϋJkA"<] ogFAꈵ5wT߾W3(x8Pu>\md-پIUT{ݞh1#0Y/0b*7#Ա꫕46o󛩒Y Ğ}'0ZRk$DR&Lm$].s1I:˜ShR*;eKjhAiJUq`T)Z|Ӑ(Z:uRQQ^7ʈk<4 ZG %zD0nsmV;d;e;9+_uXR2 {ًL[(Ӧ,J׷_lXj;RSֽ)ؘvׂHI2li0)Pzr0PUTPQ6`S@uȳFE-C2p`d(oaき"OhOlZT;bG\*8Ӿإ".1J \wL\R6½d^q(uJ=7 - bDYRxkEFÿてAP1-&-V2-k&xBW%Q=p*"lXLfmT1XیY  Y) 78 -z [)Zĥhz,TR&FEɊJずOC`B#1j! tw-%=||p83V#ǑvslroSۮXe[,UU)]q"!3LK$);n 0O&FƂe 'K,E݅)P\[# 0TW3OI<( tnT507ugehЊ($vϊ0%m*qj@3"ܨ)c^'@R.ۑݏ\#!UrEY^ wZ?{d(J$C+HXFgO kJm? mH~(]dĐid"LAz߲rB=o3UDY|_cwg#2p> =YxIҵvP) zH^19 P7Y$n( O#Zq E5 !jw<6ϋT$6d^b{PF+ Vg(|LQ(9b/ֹp-I*7=O/QLmvrZ)P wXݎC!Obё!8x)ӧ.bO%K龗 ^5V 7.KV_=dX/a3*Wٌ7sJ%XGc .9j,A!۸9 {;ZRzd.hbٗF[@X~mr̞zHYz$n~=)pLj)ܳJfD-%ds] jxl'j^ㄻQXF ?̬ ;5UVNhj*Q%ឥ.W2HjTTW{eLHxxTd U`+#Bz썢8rFпCF"7=;a%H%=Z5%lL8;S-7)2H(y:j{{0t"\Z@O*&u=Dqˎ?vp_{5/+PME'xÏc Zm閶I&Eok2|,WP=4OD B؋MŚuO)o3c%4Y .?aa& rc͏ӒRY qH~![52n{Fբ–aN@uݗǎSُ4a7\2j2F@1ȵHТn5%GZr#%RŹ Ȍܷ?3va:[~gWIoE;6Do4A*bb}I( ~=xf ~?⛾w׭xޛ}?ercɄ{֢4Նd1j~OY#b#_ /I U "?I@i5kUO/^ [FxifLpȶ j `j~Het䌿fWe+wO%gԾ%U&V1Nn-҉" !Xx@٘! &" vHKo6(TiKnm(ԎAIlgy=32LWɐQ_ Aji91G݁Z2њD;z_qI~NH1]T(B[_B@!Lyei \A)+PJcbn-@C]\[*|D>̀{xo܋rFfT4mF P;P0=PJ$g\RW5́GU_H6@S(2]NP)leA7c#n^+vY| {ӫddz/ EJ)\,c{d A/iH(Pk>I]BdmIj%y$WtQ1 DF4K!FWU—C₸7 R*zmZ|NV96w(] 9#$MjiC-"2N`m튯d` t_醕w'ܨdI- ,)-  L\c3㕘4tƖ+Y`lj~C$>*6IM"~>; !R:D)6nص&0kOPbƕ4#)T7@:aQ mVH6߫'mF)nTSX{,)n*jmPŊ:xbqH+HɦOlR 3Aض8_fPo* NRXB0:bB|1CALP*߾*,, b#-d6mL)6ABvQ<06/mۭ0(~8[-~${aA-Rj`hTUo* eU}2) #|bQ16Xzd\[PaLYŐX".=V]!Zuĥ"2ɉC0ޔz8\v!&55iN$tjm8ҋbS^; o!Ջ]A)\yt΃ J¼{t=mUXr*}SzaTM@b;dseLSuv[OMI2~M'Q7'AC4O `^÷#!mRvxY9!y#`KEz%ښ>7Yr)Ρ2EcЏG8.wj<-*G=HD^s\)"*1`B)'*1aHk/5TWV O$DWPmR`+/d8s#|wĀ9+@a "6jdk\*]C<ʬPӿe[ 㼍+n%ɠڟc6QBKHŠk >eCę}G荴՚Bu|.x? Pw>JUmcZQxXY˘ 7#ִTVPB@:JNYuf%e/ӮN[nD gg;ek\) "m5%a4?m^dgrc (M$}bܪh)4* t3R/r/pwpv?U{pKyݠ (%zcm; Z^l" UvB#ⴏlρp-Ɂ\ ZxÇN!pJһ`ʿ.+e qd`4T-~ᚬDqgL.cUjS4xBI.nW$3c l#Jq7%:V{9"EEGVNT,k#=cj@֩$@F%FڬZҔV_,AC$lH{eNvj|;9\D'ө+.ѕ*UB:c%v0nA*$\E vr4ݏ8SK-ޛwkr"ʌ@o*9/,a!'6qo&؃yUԭ䶆AQ7?O?i6W6n!dhׅ.ЙvGxw$\7>%lO#HV dz{{> Zr36P,vQZ8OɺvzmoLHx&7ӨD SVFvdBXx{\_ZDUa&n@W,Ðu_P\SY҇|Oe^'쿣(YHSԉy|GoQsY9zIt46Lc30.~W @fgVYjZ/'_Upl@nJZF;"jG_,NVoML_>tL P*mŸS3eK,qzC{to2G/R~? rѻbU !2A~L6TF@OB5?ڿ"d_+ M>vMdޤ2|c&!$>O=E, d_EC6.?CDVmww<3$B#er9 \?Z凃pF꠷0LaL! |5I8 FQ!B.3cЃ2)Bo T7y0:@%Z7\"]?&*L T|T {lG 5}PBA,i~=2a.tף]ĕRnz;GLT>;`m"끴eR 'r@ӗ%>QM^%nt%Lr+M~@qlV[p@ޞسK4!Ԡk.DgI%֘ƋQoa$HMjEj=H)mFILt|r4J2aD2a!ц+BBehkh7I6 ͵=qbh"+SS=(C^U ^`jbiܚ~@lw&Uh,zb DPTeD&qϩ,.+mh${bء|\2rm/xȮ~ j߶(۝p584[nD ըzS݁f1nA54=Ilq}97^*GoP+jE¨IʐAڝNQ4mdrdv3 ש㑷o '6w T |x^⢀uŰ(8liN"7pi 'ǘ8үedK{tI@f6o> 9܉QmWZr3?ښo_OEn9nL d/sN iA9ɓgu݄g.5M2Kdܠ:ƽ)_5ʎn<_AZ<#dFa!⦛1kF0Ĭ܇ˊ^D"._$"!tTɶrcܤV#J8 Oa]۱Fx,Xf4bGA5ڼwz'}r4CeHjYkZQhhT1roܚed0!tA3P+OŒ'830AV.Ac2dw6{QL,au'5DŽ5"=TaHA):|\z`_s<m;w9tnX)!U4 8s$~+S GZ4땐 juŰAQ4Rުȶܸ9./D&OC5]>$9VǛkP(` \uh%^>'%yw탉B>#'lRVR)R(k|rQ$IymrZҪҕˡ*n,!ŜmйOROU2$ُLMNTuj~}L!+#JjӖVNG??e$TB~(ZeWGO Xxӱ+%+.[%FEb Ɗm/̥XԵMooL,k7xmLG"?'`Hr_PW_+ 6o/qrd#/M'42-& sj(UGN3̉f4q7 ս8dmW|3 S󚌭f-w|FIyRXWo+1Fn4e,@rdb2DIIT@G9 {5wgW IU#f#?KWT=n _ L~&?ƿkG,^aY V}qK47%yǒ\Gxl*ڬP]YI ;tFLwJ$ơ2) M#^c45 $_<+6CI6[҇&##c´$VuͨR`[rHk$T1r[fvV% (cƿ.=WDL=5yH@ѭ+A2/fW 151dy+E،&7C^Ld7n?sp(v_oolH)rD=G,D?-I2O=wh2A%:900 bƹ0"5xKn]<ħev}0Z޸Amle2N~ *h@gDdFLZvh-IͬҔ?O_[Q1 ,dJS{ܑmIn:SL[>lƕoŮq+DzM4}"#Mm S+ACdIV05"RmO :b?(lVkQ^|QMOU!S_^TZe:M0zb'mHppY ֘^Ŧ•=?^VH*EE=8c?F`뀧j@- Jw8UzqUdLX/1`TlPSŊ-Gߊ֛vŐXVV+YkNتY@>Y ž%0ТOO$ :W@v08 Zn4+Ѝ- 00!X1p0TUQqbQ8_Q(ip%}N-e1H*lԜSJL,RF[e2h%z_JP YD`EŸǘщ1nQ-p(j:wr,ecJGmk_}ˢ"D?_I]3>[9qO>ks@/3MVxTIȯ.%Wn?Wf6])e$s\p7bbTU1:9p@G ڻIiƪiC#b< {9#ϼ彬61"߀ͦR);%亯^U@$ $loQbw~ĹcF,C/'Ӥ0Rac3zk9|Acw~.L#"xO#Ә4IŲV khVlA=վ̈s`LJkmŀ1FbnmRFB#|R2+ؒS Z*S1vThHW?꣏^guG!ŲpM;eaW@ Ɗkrj1 B l`#/Jf1@HS@>F@ f7RW05J6)lOZQk9z2 !V !}y?be?e͞T33Nn(AaVޭ!hIG$º6K&^*C{sNd`+V?HI )=4%E +՘}#^Ȫ3RN}mJW#Gz8K0؝ɻrAT^S;6#L&C) }`b P&O)eNecN)n*2kjm=-09Ȍ Td{A.(4M }ẃ \HIXj>->j{ my⛥9-(Gξ-R )&-z}dnR>$zLGD i 児L-$%3?o,dNu zޔ,U;PlP|*,Pxv߮*w]1BOVWrWW.N()V~AjNmޠ\!2p§ 9*Tv|Ra^j(]Ƹ#VGLY V0Ӿ,S׃`B[iafM]ۦ(!ҿN-d+ŬA lTPP -k* .`"FKH(kWȲQwa*Ӧ-@ʴ=?p$vڿvI%>EWqtj4H*S|I wT5}遊1qUT U "3\,W* \RWԈmMbN,CNv-)U`\'9q)ko_l\J} O JzQI}`- `.C@$o3Ƙ婩wtYaE+B tT1p~xъUB1Tt}'uѠ;dٔMƃîӻI7 &(=<:mܨl<6Ÿ3l[ACHs2>!R{vȠ$ $[Rg~rqW ($҄Vx*RKJ׿% #?FֆԮf3ȥE/ªd TہqN 37ol Zks+]Y ѭ:Y&je?cO x1RFiZxg![_Xt#~p"iF/+ۉk,OVo~ʧ_L6k !dIR)HeecGb#EN߳N##c=%vո,`->ܒ8Jg4<A@KtY21`'*l}.Qo_1qyc DEv Y@ &(Ԩ^Q|Kŝ m,Bx)tUGH\ǽ5UJn;b#Ր2WO+,WH   }%SO"#MI9X4d5ɌDLrPnV!FɲmduqףWgrRH(EUN[ƟSMq3Rk!*aU#O2Зlw`$^2T ga4[#XE hd{ea5grR7Ͼ@X 6 X޹IbJ!;Pe%TP稧Z? ;lqZ,n&=YIc$+fJ⢕:`6gġ"L`Z2[ӦgEڧ5ۑ&Ӥ<=%uV&_OێǫQ\,ׯ}mQ@tdi>8_΃xxKbMZO4 QM MhKq#`~θ]~oYJߠp)^T0IE7lj':w/NݗoQ2a kK$(az[g?.ćCn"Ųu*V5-$"L&QO"SB 9LV a۝@>S-NȅHS Vq;(zkYux',\+2sdYF#t˽^:Ж gEZ0e*t5gIՉNRѩ`߃6dLJrwn`5,ZE8|b}\ηfc183铐(mMyȵOͼȇjq7 Bݿ'}Yaӿǃ?[Ejmd#S?cv-#}k_n?g?|=rcV*79 (,8ĨW"ݲM&Ka4ez ƃDZ9NH(|uz` Nj˽̻Ȗߘ0i&THZ1|c,PV*w>8ަ>!>aam,M&:xIA4c2L@BF$)dcӎԂY֑=J!(/%MR@0@٭ ao 4vc5"՘Ӄ*# 5c*Y`ꅐmɩq{~>ؔ d3? SFߏ=J2UT:ҽ]fh퉫Vt9|SSVb4ö*Z^.RkxS[) ͺ׉g S*ПV]iS2XM s#$R"o]t7{-+^Tف"փF,!A5\x7)!1lzSȸp8) ~þ4Uv(*N?H;N }up]yGp2#X{rV{mQ1W"jNś/~ͧp:9გ /ZF܈hvyS(gF$"jVD*׻4_ouWԘJӈcG6cIiG>RSzNbO[1(n￾c4N֙Y4´RUŰ½Ű!ln#=27$fo\$I6 0x҇(Ae*T[(X(jVBDY!VLL2 wk&ʪ!D@ٴEb{ I+ܗjqؙZ%2"3Kt?@vi&]? (>!N%5wwXNFܯۏx4*h.+e_-H9xsSq{{c;ٰI ei| 96“b+f;@(߸ViUSJ0OsA=x%b PWz'Ų)LhOMDZH TLPhMhZ `KPw2mX T1W,`,j: 2LxQi@2p7.Nl1M y.vjS.Y% ]9V=B''eTsM4zcWDK nmYbkn\țeiŴHRAAFZf4Lb'1Z Zz1ǐ?M(p e)&I8xcb裂Bjz뀒S⇐jvq7LEHAk>m0eD?\4|gqL쟤[VI*ޓ7˝7fe lx֏f<Fo+^܅?KZWVզuQbV>$!*?F G`tWFoq4 "%,ztim#ޔlM̂Iqq q`Jmot JVY}~D[L 581vا| )Ȥ6[ִ |ϖnpB?/z)e J~TY +\+,ԧB|yR`˫1" ީ[_q)fB$pJ;MǨ2 /(el,G_]%?6HD@|1_6k5xG0?A,I.hʠFi{\;hIT5$SsAO\Ƒĝ8ϱcOz>+reĊ Sӥw'Mi'K?„n)}c|%R8$KxɀnHQ0̼(GoLF87Y}fCϡ4x a$UɎMBt E|ū.('jb y烈r&1庾;J`W߯\Đ򽥤bEG|Qk ӾTW+W,j*FvYŰ,5;ccc*:HUA]01V*B*),VSY]Z B_ Yÿ\Ye*dSAMbԤOl CŘCȣ瀶t HaoL>|)*!;!Z6miQHk0 04RϏQOj1(zo)wBN vvqd[ ]ź%(PE1rR[t:d 5${ V6;b ;Zʜֵ\hJTHؑ 4 ?\C وjU˝4E,eł>gj^)0ۡ)G!:!k.f9!@ *vNnTHr}QJۥid9c@{al$45AL ڿ_lY!&^( /PwC+!PTeA@qkeZ֔.9Y:S;,) +oJ9V!kv 1rl KTyd ;,iQQp%S8"1Osz44el3:;1d{)(Q %BrKk1E$‡O2*҆`!+ ;܆6W, -9]0xXO\P9;XX5$~d?0g$&]IY DV y|Ak|3XT}ad.x`4{{dmB Bތy44b{Z[qbQeT s%b98i)$9`ەaZnb.v1#(7<{2)2 nGlQ!o!m˓ߡ9DLn2'گȍ`6Hd*5/0-nёdehic$R9~ȉo@/ls,[Y; |@&^ +/vhXi}-|,kB8dWθ(wh 6DmiDݞ.^vgv2CP)9c1F'M!Xy8 'j742zsEQa-D}9Uq0Ԓi\^&@ P~rZiR<}h;-]SaizXYKD Vp_lp5\P.V8mom#^(ck(-UXXeg;=ϦQrYPӉK aaX5hO9ჹ-uwzW)IG4? |(FS=әd414~&?,e);fw CpF$i$.O)qZ˨Pn YRPrR|r 8ed-N,i1KYVM3/ScT;PqHL;S ZSb)99+drG{.M`AA]09tG )(VAR W؎؉*z@֛<@aE *Q0mHI58 0PSGҴo+KR޹ġ%R CZ)j!wPkָ i1 wbԁ@WcDB\+$E X*;Z&SCP(p8sL^ dZP1jG (ëu ]ET>܁<"5j/ ,czM90-!ܨmQF1#[0eI.UJ $k52ѐ8O/;#t3#h,ߴ[#tw1jҫ;0ꐲ햄ڌ̒$w3(AΥGM8ř"U9NӋM)%4v/pi:҂hEv䟵_\$Q- S~-*9T>LĶB˧ˏCA(>,eAUq_3v`*IG,e㍑Yհ ˋFوyz\֛{X>.o<>8<)|ͥj^[L DǤ2ݒYz}9yyzGCYv3[kSSgS#%ޅ =c.3" v*Ԕ$vfDD:A =ylۏ.)ֽS-aI>Kx}7QZx,0'<{?vo}l\#ãif-&Q9|5_f^W)$,M#~Q_K=_ok<㵂Gj/6s2_{=aqS ]mT#}Rԅڐa p sen[Tme S$MH,NNKs>*#9h@6mq2_?ˡ.XD:-)c."?Ԃ2X +GTcER6UsﻎX39|1v<)S+!I5ƾ=D!WjeD[QD5 %bHrak (ͪJn8pjiY)?9+(Y5NG(IE%MEhZ ȉn \Q1n=ʌ`%֏D+ol}hs+n[G_j61T"&L ̚Y`BĊ` Z$ ݳ38u鋎mp8.Z oh-tmړu'|eIE|OXfd^_vvFˁev˴֣۩ܘoIirN &+u%!^AAImJ$ހ~ ^˨w4hR/)$|V&08Yp ;ŬcjWǭkぅ"PӧZ}ذTt#(\6;b\@qVh)nlPW(^{⭡ m 2Ѹ4)ŵ0ZHL޵$6kMJG;Ӿu(61P TȮ,\6w8ܰ(r}زD#tŬbBZI/1CtŐ*NŘQu Ő*2 omCRN|Yv遴qDujZH^d,iJIMi]ZQHlY#ƝG =z}֔zbh s~*Yyii"l)r*p(Z"\Tܐ?ȹq*j03TFz`AUCGDŷ.9$wu4뷎]1F.(.c5,H :uuʼnZКxUP(8ZwQ;Fݱm *D IHۡdt- ,t9T][f6_n} ˫q k,W8Sp d߻kjvN ':Ő[Y%)EI~H?Lwjoyv%,Jy%]h+W,ʕ3yW[b@E?f1͆-`ݎO{" hA߭ 6fc|[H/x[Idԍ\i#ӏ#@J2N U):Ƒ£$. s\ii}ZR )"dhH%3 /=鸠Ȗww8Y"䐕;S兡&.1A2`9⁂'Nrt(&^LAA/՘yrpʴ_CTJV4,lwoxZ65ӟaѪ)SQG"SN_f -_5[Wwy!bd.jҖېʵQoDx"dܿҩ韘oe5qijrf^8&2.[UmִUNa@Lc\1h!o1ȶޫК1@>I-9QEGa( 슽proZ8{ Uz]j_csz"MyW#{}L]$H+'utTCsrSbG|2*0:@X T%ziտ،so+r5КG*%(XZ*./G.E+*=7Rw3pCk&NU|Å C(c?cʜy+*%+Nv0Sߎ,zw%uɕ%(j zdVJ~CL2Rwˁn7. K(?2#}l̚Pѹ9b{Z\ ]i$sIm*8*{~&bXd|wpkٕ4aOخku@ e(`=\y%f۾cB2ʸ8WNY!o2yȋUdh@k鴤o}qvX$+LY/\&Pn\˙C?oPW0Eg"JOa"yIf84 )J% cLM>ٮrH@(5!mW%1m9b#)Zui 0e 4&9Ruchav䕱9’!}:|cmRy6[J%Uu?s-fsmo(GڠO?>nȭX z} F ԫS̘ۡށN淲"X+ X,mKADؖ=X"m2# m 5ө#zݒqelJ@~ OM̠[BbhA2س׎d5Nto&K@tnFF.bzⲊ!&,)MŨ&.;?AbEb<:f1} HCy® M}%w_3r"{d O6T=l K506FItvX4L922?R ;m2-jm|IG! B6ġk8'~E03 ]r@V֕bOl-VDkLJeE W=05\OAAD#|XҲ8 ubY|<00!r=|*î(]Jjmz:uz!'|PJ%" ;bJ7$}XRa M+Q_XM-}a ) ]SSҿN(\ͷtZ|1mLwqf KV4T ;UV0 XR6, YVŃu'RiwnI)tŘiJ2Gad h(yzo!]+h(Yҕ"Ŵ e-ɡ텱|rW&(& SqFE =N.]0M<zdXIZQ3ҹӄaZVڕPUŦRW8f$~ K $:ָ -Iֹ.% ^?فr_uZu큨Ar-Iax-Z+M' {`tk!8ZʋFIM)aSM)rqR &)ߖmPm%G{w$dyK-x*c!{0=Bm"\J FpZԖS'ŀcO V,G\r&"ޯ3(܊xŽ5&f7/J9O}y>Y>&E.Y6Y^B_#-GD:\=!`D~fc{vI$^獴D r_ř7Wͦ'sfC$K%_;7e1 S"EǬ1k ѱB~*Jst%-݋CKt;VVЏ A 7u>!Yl҈b6 E4h:dJB bZqcҴ'DI4bbj:mqu Cv $39p%pSv]'VoP(6iMD#-+MZ $ec[D%GotEʔ,Kq,$cӖ˓81ClB\JL H4$~۳76E_/v@.8Ճ:Մ!oahԯöّb~wjEy\cBƟ~fOOsg6g+4h05AOR/9.i: .=M~DU5T%;bbxyKg1P\RY LR`K*pFhk/6jc_bn 7ݪ~/ʲaPrTt9b㘪zƛ~<*Sj QȀr|I 0'ɁDcBG%@E .* @$!{$;mC|l{51z~nV˺U-PkPxһDK]-קvع(H.U*.z7ʹtJ LEI"_ 惇 s:*.uޛfp TQ A"]tMQjˮWU(/oɁMRͱ=B% Ƌ&ȧ7-IoOlɌmc`-ҍ߃+7l,IƢszI(bˢozlFZÄ́ ]rg4'em"!is܎2G- f,c5J=ɪ]+ #-4wpDx4vbM*H.:z ι$!G&ЃgԈmmCm_S J7a}k^,-RT$}7\ ㈉??NQted9fȶlZЮTpq$C)>ׁLϫ[5qvr1G%^p ^NLKLImycQT#-?( G9Ɠ`Ȅ"08濊&Ycqfmbԡ1~w'78pwGL#$BߴO'cȊv$P(OA)H)PP!s&(ht`TJ TG#$5nN zB  s)p*{ ;Y:vR|336l[$ L/.#v] }V;L|2L*" UA^`nw_e 7/WX2!~V~=~y=䌎)-hut v]Qk4Mi֙?;Z(i^&ԧ{H( *( 0RˋN[Ȳ{CNqeh4ڽC 6TP<|iUt ząh,HVV?b\X;_ m*M^>>تgN, fZ$,v=NYj|QY#HJ``Bƹ4d'ڸJF̊>$@&`"D&6ԡxWd5,s-=-ڝ8֊(i.f CǦ攦k d\)|qO5ޮt)Ų)m鋕ȋjv˜'ȵ*=p4tg~"~9zT:벆7 0]N\}RI~.*/1R П}B܂@h>^&,˟ڗ5(dnE"Dd^S0H>'#KIֲ'Wɻ'Y[?zE(*9Tffa37Ĵ}tbBl"1MM7r1a6^(07A"YUJ ĚrnXkM[ ' r)ZmCcPi#"Q2~ӿۚO?g.Ð@?MyƟ%}dpfeo21\MV\={ YQYJ :G_jzgqzM>N!LAQ;rap65,СZ9CG W)D%o0:a!9=>>C\?&aB_;T|m(r|2X~2 Kĝ{Rxnhw3M7m yAiNP60JU%Fh5/XjpKl G 5EQ6\2*g Ѹ)sni^v%OH}sLg?SQ({%aw?gd| E(,a*?없Q,}1R%.AB(EH|_˓d'?ĂeҜiݡ-1339pHII+~O"nDs?Βi7̢D`vP026-F4m}fTk4J4J)bI-D"^Cnk__W#w=ir`BjG#ڞ88X` u2"6Fq#-BI5y}\y<Ө** r%sC Tҙ܈B{mʙ(otiLNs^hrYE~^S5: FZ2@ۊ>QVVw~c Esk"$oiò&3م+/o1uź)mY$=̘J\p ][?I2w'!s{͎lR% z|m#*B'M&&^$]7&i^Mq]SbO8=#D G\_5m93F*"rq_aVZs4= m_| 'p6aI5c,%'hQW/O&@p\i9+c>kek:|8ɈO}QM#,lh?uƜUe#Ź跘] ެf?ߵKf~I'!n)!i52lGQ)#G4;C%(sZU)Z{dm-T൵=LnnHKF .'}a|~*u^?MIJ"ioHH<3;w-FDщoRTDG(2“ 6jGz$wcw&?FcBs+בlB$;5k; wvJ@;~yQwR`Znr{%q$ bCNˢ)6ш}`}z|>¨W? X;bo,JXӱ?,!1rюڛ{;S7MNL"'t;W{Wz]xtO'laじ+\ +D wֿ"CtS> <{d9[.2ze,("6$\RC}Bn{ x>x,ۡ`H@lYК_(v*M7SĴF5$4ރz~C( #w?v,HDڻ d/G Jbątomb)|P;W˜xp,H\'ikK{H gGOl,і 1jNn{qSX'q2VHnU ۸F,lw\ +mc72)boz W#n_׾J%%^qk!}|1bb~TTRAEzSo| ‹[>Y{EZҠ{m E|Mm P h{[A8ܞSHd ރŪA NSvv㐿Kai$GcK+(l͗8%/+>Z$H>>?i1wY[%@mfz r^<5<&P7Vtu\&Kw,:F+ ֕j2Gs2W6=} ܩxۉ+S]m7xs''*&Šdj~*: ! )SC|TR閛`rbVPz]3*=vbB$S$%ZҽOlƘJo@{`prF֪9SxdKo2N :LXqdUUw鬇WZAƪ̬%żO5bOkOFStHxIȡSŸx6y!3)}N8E\,jWh;lwyƅ/OmLISU+тY1mAo/ RF', Za\ 11GK ^⮼@L"/di5Ƙ$9yvY#a45v*U:#pdW:'t 8{Vm$d%~-lt~nN-K5LMI e\ c*P3tIJ$t(T@916IYb P>ٰj)ҵ;m6D<JuF>0u: m6_CĹk 1YWpkFIPFogdPKU߶Y~2 GmGCAYǏ'}5Lvci߫Ff!@IOdW_r MI}6"䯦ҹtE8}䫄WS?rLc:jӡ>eh \+ܙLaeJ_GOvn11_V0xykFC,Plz)8[ F(Z V9M4#ER;CCMX XJ+ntP^FoQ)R`kmZ˩*Hn?2`J'HN72od!N!+֪%JԄPC21V!+ܸKSMbaJ#!"x"JSd7Qa8~,-J繉`iTbCx0M_0'wƮ~Ea7tB?& 4f<&3fdC$P$kȉ*21l!%nR8>pƫٚt_0[N_1u }YG%9tz 2sxLo1FPĪȬųqIqGn(VR]F[DXݖg\ rw"]UmVNb"i4|<7eV5EkU8DwrUIE2)UAl Py*6܃&HS{`$^+$ m (х _e&]#ңj\6H&EJ~x[ v y66a8A_SD3Zဵ(;DTƔ#bƔTCRGZXk^VP'6APlv E-M^:b\Y҄`f 3WAD0]Op2$9Z}j6 w'U }sl/CQZ}+lMyaHyG @热z`d h}θ.SBp2(N(3C b7~!^6$*G/|vŁ~\ d+~5A;Vbp%ö\|U|{|DSʼnF7?Vj!5Lvoj"?N\E;b02l7cUT4{W`ޣmp%izSK9 ۱WŮ1vlX| W ō/4»Tkb LYZ%1ejql $f8PξACJ~˽NPO] 5zc J|?.0TəEx28-ynn)=fd]̊;qS_K4N%q'񺬫T*G?g5rq>縂[HǨoYEFZf uY.^_td/}i%iBx?5+:x/w\?l q,Ҥj ~y Z$IgIA zdsPcil| (?}Yiubs`X&c%*+↥y!.u}Gh>D I.dbuȁJZ:oN֛wŵ^&i^FؓқW)H8GȗC[x*[}p8S^)mmu"nl#VCP$( Y A=Yb3Eqh+O}F, Wxv_湘bHӚ4s<Ϩ9H2{)B{aɔ)#{W*%HF)&EapZیEEHߩmoڸyFIV;L,jJP- tbb J¤S{1ʠHϡ-Y9B)?Vg >;l6& -x96AJV(W~Cli- $ef@8*p*_jCMbVc_':|oYTy/z*ԢҤzfU{3/A(x/m^Q >ȜaD~r 6_͗rh>dr?ۭFs]*U)x8"ۯFUlmtԀ7 mE<>x((K{(尓 PRX'ԁk3J5kyfK6ژ1F~؈=?ks/ VLf lozmt'*~úDyd% LdLg0ٹ G+F(G{\Aj&' WŃ"?3I1$Q~}F12I)paý"-Qo匸SerWƑq RCYq1CP)lmL"; FTО.`(2Ad[v!fL h)n),Z9Ȧ:f(ً`7?134KT|ٖV H9+DW1J2cJu-BpG@llRCJZɆa /z2ʼn2a6nZF9J?c1zep; gi3d# bͿ-3i;1@4AeZE@D.jy%HPLޤ=;L%RQ@6g+}D"Hʄm).!jsucN^pE?bH.up_?_@Uֽ̬dgLԇ$YLd̓TYYlK"6氒:1 *0" ٖz$Oidل\zU16Z.#e Ē0u̜y\e]RG+Sl;VtFhaM ƔL$Q:|ZT?c̃ĐElr9dJ:BrThhn@+ЇBm;Aو+*v%i9c$RN[(;f@ܻG-ƼϫO ll+)a wlA>.{7ZXfqM|  %[l3.DZ9UR}D8;k3Th,Á `5Z:Gͥ kA^lLɸMWOO?Wvo3G&1B$L5V#Ÿ1c4bb㊒>[<,#E@zu!E!!>aDo*Pds!()͊_H̺12r]A~F1݇\Πm]mڮcL{`8uk₹ zNG€@A޾q l-ïϥ^clǐt@1 7ȇ)Zux[|R H+Q`"~Hr;OJĝ߽z ;8f,u.qeWɕXߊUۦL۴S"Ar8{PPoPE]hD.:Gm+!!L^T; Mk4=hqk2g`Wl NcJMr*r[kڧu$C/嶊RVYyMݹM]A{ 0g[)ӷܢ0a)KcnIK;,t;dq|T6]49XJ6St=aUMZP)zc8I I8yӷ gFRDJܬzeޠx<&){))Z^iXIrZ>!/҃|Xv#jdnu600TOY7;Srqi!hi7Ljl\hP[ ޽Ce[PN #+Lu+Z҃nlc+b@+u<-rl2L3zGȐC'Rt5jzVߥ}2\2{;~M0$`!S)Pǥ?Y eilש=l CJW[#c۾DԜX84FZ:Uz bM``BA4wŊ`vv늴 M:uUjPء&V CN1L:i dKQBA늺߮)l_l _" ,R`F`=1gmqLSmÿL*F05F \MSN }b=CU,Xa 0N<7l[B֔Ű4MPGN7:w H-}0˅jvšgo jDW,^d~ I粒d]F8f;-Mʵ?osq8;Ӹ9W Pt|nΝ|15dM>YENEkjk1V2v@h]Jhf`jALvh"@7A$ᥑpXUFVW鞁(l*X% :bvbIUz.TJm?l@RGbz֘;  3CJNoq_>7oZlww̥?|ܺ_E%of ,ˮm5ͧqĺIk?+,90HyL3˛*A7QQx-d"0/7^>GM$v3pCalm ~, E(#YK9p6[n~,@ o '߶ltgN9q8 +;<5ɩ΋o չƹY2\FLOg`ȗ4>FjBL/k.$JzڐTT^¿¹tq^32f[VO?9˅rmd-EYOu=+O ڈ(Պ#x[!!է%1w>oׂ؜#;BO@T_qNlJT|4έʊt)KLSA<<1JZ'<, ^˓Zk9 yL.*l:{⫫S޿F(uG*448np%QA Qzb՗3o7˭=vډ5iZN,tKa҇.tgll=A6}:dQ6텂1d HMvY Bo$b -ʼnS1u"LV3Vx(\=|0) ъt}k**00+NnVj6 Wz ھb@P׾(!iwIQM֛ ŸxĢg [o|Yܫ\]@zu= p$)2שVw끕 N1d:׊mr:bѽwqk(_ T2204a*qd4[-!M0~,ƒ`f$]f+݁ѐ(:x (iTh(KqnA:JWrz`-̤!R)^EG"o\~Sl(dP :msLEfSڨ}H;/%ȤRtmaZNu T%öLS%w(R\(+6z)1Mr,I3E(垅M- dŜPz}7ĥW o1 _lk6wȱ.;W~b5JFbr(±^ȓN4VfiHUѱ<ݰ;/ĭ74l}ޖٜNJ)PŒ g +r v5h/!!GdoTRjE=RڜyI(w4ajhzqȝ"gJ:(4\ZHäf'n?Mo䇒Z(y :5wX_4_љ6%RS*FZV"݆[K^ gxS7qo8V+oq/ :G}\?Z3H Kxcl%DMFMԏp:0-"F\qclٸ*]4VLi V "ٮ+ّ;=#ChH|yIޞi"ێً"DZ(+11o2 5;)d3G֢U k6]]T_.- c\uu 6Xv-[@g<(f6*Z6&{CW0FSԞDi^K2*.WO'J6[?߮-OCk Æĸ%[n`#x\F^)sKJbA^,ff?_ݾU?+]*C1*á,S=*KĒjV  wl$XLUm6Ԭd̉fWW&r)Zql,YF-Ɗ ^cx~󿢀k*2#r$Q)qK'LIPȬNԞ49`Aq!3QZ혤4ˆRf UhH㖀K}* W9S>O/^}f0%1F 0 c?ze|Y=4mXJ# |y;9,{&,h%|RaZz*i rԯM2ȶE/n"F)6qa$do㙘AqE+z+Ei8cidA$l+~ٶ9IUYYhB1#}|Jsc5Qh-.?!Z ӒԠdPԮ4QRAI 37,<$zoIpZ2NJ?bnTYd}wSNf^ b1e}!ևхhw#X+EBzӶDI72XAƤ@櫰y7&"vpt%Y+dTO|K%`iʕ=F6FX\078 ~ɫn;]:m ?Q!O9O$hqWyvq=1C]6LUq⮨?>rURbY뱭)ၲ2TW<4匉ޝWbz{䁧e-Qj5^?|s=%4L6Zq*T!"ԃ([4#۶ w>F-h23)\UE?mL V$;t>2 ً68N\Txtƕr1`BpO\ iQ@kbpS\Uh4Sb[bb]ҕ8mtT|Ay$HnA݈|VĢѫU4뿆XH)st:dR{W֟N,iR1KGn%bB&7QO-$"Y ,\w*{vœ.&c&ز&(Zuj/ f H1lBK-HYR06jD˂Bo00xPqj^9睫u<%du;5+BU X :Ƈ XE" Y*R$ PGSAJ % (VvS37n(1'ڠG!PO!u~Wc5c$> 47sC+nD%il6Tm㋑8ɉAJ.<[ȩigwڽib`z-ƜQZ,/sib3F Oo7pDRu[Ķq#]~wH쳔匪C# G/K8 dEm"QO*R\h~E4C1sG"Z8;֙2"v 0t@(8Tr:\AkSfxY.APS`K{w#L$3D=V;`Q([-dkAրuc@[3!ǛCo4cbvHx;Q[R,Q/_AEmǢBo2/ζ @HK1%rjOӗ68׷l)l! zB֔(ڵb%;tۀUvN}l)hvdZn7>r"[:u}LU>LadwfyzNhlgl8ݰ^-T<:4 [ΨHjOߛ:"E:evoj*8p O I٧]{,11t5Bo&i&UB@DHsYr1$v@C^_|L5#_co+)&tVeMFHpTqnT,"><X`z|;Actpvq_crKnen3"!ɈJ2MJ -$p'u$kxiD0vmi{?$T:' ^{fS|\[|_4٥/crw!TB" ^ W ;ޓ= KGP7sZfl;Ezr1'/S_S9tB*C(vNY,ǒk-:n{kxČ 팪Q?fE ~jp}CQ>=2`֓OH`)ۼ0rHR;S.Dri Vn}uSٕlۛj,i[!ڢi]^n$R;V`?,wd6K#{y-zv\zwPIۗ+yf&Q8/.@bnWqly #BXů uo=+H214GUŲ\ZE |Rd*z)\4bmV񣉉>yJY|SO׃E(&`i_c! Z$j*0׸Ib AT)Gt\`EL\$ 2`ZLqNd 0O<{8ҋմj'ؓCmJ7OeN6&8JRv!(ښ￶ 8b(Œ5!}Gс+jvœk%~Xis54ޝK' w|HL R߯ J(mJHl M;6Jn 01*ծ)=₨ 645:Rtō7W;~UA;{S[nqChP¤"a zv Y cN ZLx*RS+\ A. ik ˜ :lqdiFerg\%!qKX0P$O9dܽ<D8f·3_ՆQp?g:lxG 2y-H#,VPh)F#jbr=;hE)9)8afU%*9D4ϒM<\~ zDQe0zwwe5P?^N"DZ^(W/n%*'=z2@^^LWD g e 3镃#g8>1+KhpeXAVk-iŗm"?g; 1rJ6wP3n\}96d :o,$i*ϦFs5Qw; %5 O^6S [CԼlƉ,kˆ.VLBurz`me<. ̣½3>NU龂7;UClvMP}|Y>| }nX LӈGt)^y~u?{cA2m*P? H"=GLep@뚃ݺ2 lܬrI߾n6XjS?UaTɽEUq_f4n-b Ocۍ"҅w^u,`VXm*j&_Q˂_"{ƍ(@cb; lEȊUrҽ|s..H)KE켒2SOř?_c"tMM:Ӓϛv[(˶YFؚE>fmBK%zn9C+Mˉ TuX 3޻[뉦` pkLo &0C)n#LA$5ݒGE]Nc ce܉U}@|:}_r l4,n$uc+E*jFY;Nl7€9489Kd j;ۏ" ҁiվOd?SY9=JdݿLDU-4[D<S5 \T υ>C/ &dHK'B홸acaЮ>SڿK/noNQXrŶD (ji\Z^i?pq_'+gCNbc1RLbP:XD1y͛ 8v;WL) #q Aӛh`..f.ɶx-uK"ڣT0Sk>j2֧Qp?.Ť'r81zT72ȏ{uΓ ;(mѡզi Xd#*Xs(vڌXWY$rNAZ;0 jcty#/e/pBC$fWh9b;lJ1Z޴JDآfYz+OCނ-KiH)KI?d e"Ё)youe戎^]O!LTO]L W$ʼn + cOP#%M-yv7srz i0IOiOEǔi^[Du@;w̋+bzސI4xOo_b#_FSXMZ{au{WV1VՀ4blMH^sbܓ2$9xTTS sd~+Dѕ;;T4;Y;n{{dާ iTYjz %qdT^+ >ZW+X}Azb㉠t`ZZ[p;xmZA իC֣%P`B8qӁ](l*,Uo)pN`&&v}=9 0P Q:`d D#Nl X@*dKzb^ ,Q1aHۮ-D*mQĮ#,,'qPt1f]GJbM6ָ 4c-(I6HZPd-nq\VOLPEOv*ų>_8P4,;zs.⻃O`2ذ7q\4JN PEkxU ._Tiȸs~~R2uX~f_{G}܍>;rAT{e_~fc-A#(y  m>O rȊK$X7#Wo:.C1eƗs֊Uև o@Mv:H D%wwF~F5;S .ABRE0ZG!T"* r4nl~VWԩl8glf_6Fpt:6S8=UR JϜS)O|Qꛏ+&YQPR2*Va_N͟RT0E;\ęqSH E:e%bbQ&/*'zETĦ`ze|i;Y[ e qY:EE$ZYS$A^s_,R-e cqz}?EbZ$khe3m~FCUӡ2HjqGZJ&Q|9GM\,Gnrh"m1n*,U730s1A*ŀp[l2 u u+jx`-"JL֩ĢQj+SgPs=ÂC$r'Qeq}y?݇r3>W}ck5'`dV8#Tfwk1!TX!vfhDt@49#ÔH[_p^9wjee+҈C}}FKqpI~Τ0L*n>y̗z/֜:(T.4\04啭tWu3XzԦ}?i[, U !yyCȋ۔*HVG!N䭕\P<)R [`YynP,lJ/2q j2m&/y <n*8P e*M)`RC7&eG<|9p[x`pC@fˆ[(%its[9nL2y#oԏd[aFU1rD(՛fxBckN_|}ZO#ƸAP$`-:m@IawHf@J) ׫eAI/ Fz|@˹j"'?xKQnNj>3+7)zNpz&hB*c"iŋ&1Rz;qyߚbjRTT~6c$Rw~0]2!1Vq%1/o9q MF!7]1!<6?i9s}xrU_?yߛyDbq?bO\?:.SLh7qgAwbGJ+͑ j6sޛNb/e6\QƇ{,}L?ZTnq.\`/9֓M=[[Qe$WVm,J~ RIJ=׶(H VR8*өi6קA`[R> )jN]KBqmLP~X5WT 1bzݱCqVGZSR7MCV⮩\U)aUH(@$&Fj!4ᶒ'~JW_>Zc |)SC;lxm M?I,HEC5;mdr{bB oq)wLRڜPUE-eZ(Pvl k]`eM|p6:Sl BGsyq` W;GM)n@D]7Q$Hn[z:%6T"|X+}ʸv~>G*8X_{bE4nU!jH=#d%K"){dPmUs CǶeDop so ?fm#S,6w$_g'>Heڻx _J;Wl ъ kn9oZ 4Mv[jA?()(ft##kZdi2.?MsGbRu5Irl4^khQ bgUQ72mH%t?ș>~f+APz6 ܙb %v>,!v)%S߽{viw*Po2pW, ؠ:MGS.-^}uoZȮs}5p. zO}LCvk McO-!9?la#e.ӔQYEeu`2n`VZ&,&+x: E*giLtM)+ };9rq, ꦕ7  8.eeĝBӶY9ێJA3Ԟ)әXR/\z)S0xve-L!iDLy+4q$Z+/f`$j02>,b֌# ]Hf}'f,f+LH;vsV%A$g-Y2%4[D$!Ո#oC0ɦ=*m3nv޵eM2#>ŇQA٠ܱ;Rݗ {؃Jf>"Iܓ1cC&2 .XA__& %ȅZJ>c~lpGi5@݉-%ZEON8?*v8:Xj@͋ 4Y?Y͜wi #%OE]~B\CbAMhH$׹?de1v>l`){QYX;icןA31&_gB< ~=M @-:Ox# !Nl1 kOEa[gFj$~?_E<SźuDS2yeXl)\X^lo\dlC]Q 6X8(:yקcRצgrb6|S Պ /b>:&Z@b: ڰL#"K[൵9" 0R NéR`v[`)LZFHs,N&k%p$hEbiIJ@meɂyH-M[&喌ɧ< h^g _חaәy޻)nO{S: ~44hچutkěaO;)?.%ͤ}n$Խ!R%&Aґ&?ͮoRr:ܙ˓a ",z I&,f@E6R1$\i##U߇%F@/TKIRUI;'r249;+*7\7{w5b6=<;u2CDU(AdDoh'[~i }@Q.ed#hfM!ŏ89DXXC붮&UG?o2ygA 1?g/8r 3}A"XHss8VISl/⋫%,P2[]5t6Em* ʏN}A߄co*ɐCsɲ L\6iݳBC$b +Ojh5z$'>1sym?EEKȡ\`>|]ߋE4o[.@VٔlIoǝ5#2ao|w!ĕvX-ԞURs|5x]s`_d&Z6-2EWO#m zAŏ'wmlK3  R H4N2V &`+ErRΝ2$11u}>+.$xS}=XȮ$% Qܐi]O39#hp [^bb?LUuw> ^^:0suȐ )Ȼ,yG6G#h{ֹ ],+K?/\9\IݽMh> S1^UQ W߶*TקAXWnqBZ}8)45)aݛl Xb^&2ء*WF+7lXSVF(p=b6\*W@b8\mZ@=޹;j!4? 6)" H oZ:w`)moၒ╆█>YRILZFC!w酪Ai*8'V˿aiw,Xo,ԝi%'S$u`:vŰ5Ų2Bok2g"ZWؕI$<R fm)Ym<҆(|-?cE ^qol;Re&5YĒXjsl-hkA%7Dw|+rc B<$cWVUy"ϼmL I-;;Pl[>~iR)vܾ,j&b-2>_f-"Xn) T2LǕSPp_Yۜ3j$ xFSV6LB۸7(GL+pvmmLNVC]*"F"$:I,Vy\2 AKcGԴ[2# Ѩɳ)f Wc1/mѾ v6<2NF) ]l"՘5US6SMDZS:9Y LqƙxTJJHH|󢆷8n5ov`Sn9~زb*/"܊?d,e`i@6MUsvDE<%bBGA'l$\b@h@_5ʍ{% _NGEI\/vxZr*Cyydec=@m lOq^ّDRK}90DKE\ . say^dHB`%Vcn0qpCp|?5  Ro }t9 qx/k >»w'͚Y0 @2Fw@Sլrf'V!:. nKf:Tq1n ǫrvUon8c_5|GGԯG;+^\?8ˌ!?_r@dx% /]E|sU"zJH"Ȣ0YMڤgO'~lꞰZ`o|?kS"v%̳g,R@Rl˔7+)YtˑklM1S_^/ż_& -rvoFtG|qJ"o7xpQm!]W?4BP[@Hq?^ w/kz0eb@Oj@i3pp3٫-|RW.Ri ^b>1~/¾uc(^/2,7r$~8χ2GߚyofU#u L&IQPTf), WX) `J`< N8$gAڃf, ?ӂE;7BEjOvb: ~?&ؚ@1UN{|}*S\Z Ez%Aډ~Ѿ.ͺ_g zsq >_FbwGP}\ftz|Y3>ص֏uބ@IHŖĎ8oQk.o ̰2@R,9`B2ir! *LuJ!J|q\w~]Z=j~qJOgiV$o;[+)W78rYq$gYD?sq9Ai55jXҴ n880%Hr;8UNBfQ%ez"U#×0hcEl~CP@srO+ȦQ:)hJ,m_ʏV+H95'@vkE[IJ'SvY=-G:~Rk7,Se_|XO{Xj" *c}ey$γO?14vIU֨_ ۏHH:+¿iuM| |[I P!~El+Znbv떆?ʖS‘ڙ=`Z+<6.N.FH@])0DE nz}9Y[ 1->7ja Ԧq GrPЅqJvZAJT˃`J'>Ɍ[BK\AqHڢ}3qD_K`(yb8Ï_, }Q~LFs(Pܚ~6oxDyů4/MVQҽ:g3.{҂ՓleНrFUɁ7 Y-*|0?@flo٢,pȲ=G*أ8M3姙5_Hc(6KPV5TC'}5s)2}_ι n56$ *7d+[W%jd1ZSJe8X,nK̈hc:Jo4n'ٴ\d/{wogR3Ũj*3[Fʱ4 ޼G.X)S<dHT\E"(̲46s4AF)zxnˏg\Zu2XQ㒼> "Y0Y !Z=?^[mo(6؀|L> $zXء^^Gh P) ԕ?DœT>]}(E€J&`&'n @;Wn#bB*UzWȵH(bVzzᶪH"Cer)ENt5[^ND:6ApJ)8Ҹ:Y6W=`d @ŐWBG݂D5ӧD;Z)qN}8q Q)h!< |-2⢧2i˿*B^RCEvVHV Z1V>&ڃhG;CtژhCnlz4+>P{vxZhztZobV*j. 6b[]Ӷj!0` "(va`vŒH߫AaœGc_Պ ۾(?YHqo]*׶.1VZWJqţ\)SeV"S4װŰ7߶-w4C-vwtAE1 Zx#$7iAdvrKPQRyT|\IDuf#fYKd?nIİ%5EvS˘!1%^ӓ_ѓD"7 JRh)MZf$ˋ=ͲJ@'JS9tqv: KXpUלe G.CJ"RxMUqA'th^m)>Go MpQSc^}C}42螣6t]6lH16?rs ׵"fd @hRe3t7,l3<&ɺ ۃci Y4p+X nE^\_Nw4o)}x{8#G)Sn[J3a!Vb咤'RkbTeH4t9rlJuU#؝\pNdS~6$l%MaFEܘˋ8".e'ˈjJy wa’lB32ǡMɗ2 hdHL`Iy9sh-*~-$", I٤,% *nHD*XX0ـ hj!fzmZɑET ypr"RZ$<АA?Jɀl#zyK[$,\V\9?r9 Y|2Ȕ #KO֕|Ib{(TCz(>y?>Zv00j'h9fiiOĬBoaW-/g\LگNfSO2,6RPt&zm!P>x;*- 1ڬkf't sMuAm'uuzL(ACp$ؓ1!4'`5H4RC]rdYnC]N@?CRO6j5^?iL7YWB9n/U|J%J7u}̮$=TZg5R#*GX1qf?W#cȊS)%r镒JB W6; |D~Gl2ݶ8% 2(;SlK(ƋrOM As#%n8F1SōZ"Jzȵ# Qʣc| )0HhiJ$ѱ$(kL,$$|7钶TyO f9>Cc킐B:N.FDFdm㑶zTmFIclSU?"(+1Kcڕ^s.0OlUnث늷z`WPxVr|]}␪ +b`,S;{46Va9Uhb ;dEg}@*~#C@r\8I;]IMcl6pFNMu݊AO T*)kqZeAJXO\Yxz1ڟJlbKJt v4;vlU&zo| Cl7qVn0= b1Kq0*" A 4j!3nXR:;^犴WK8}-Wbkl|N)w2m(!^9}>XZjF+bҼ+\R׸WTU}{tŊ8xb!daJwl]p`Ju[r$5А5 Lllx[D@%Zea6pJ,< ^T]IƷqLQnO͹CSH%F( 61nykae_Q~+࿧k@CAڍ5d凄$J>S&-qZtsnc7V 9o2h>9K;j$ |l].X@%G6FEn\yTV_L1LX`vIPw&bIձ*Lm.qn&mv6C]@N4kyMABEQ`\~gMH'CIT%'s9&D_u?i."ason,8̠G+sZO/}_1B@ \q`~"4Nmb%jh?$ÑqIGı$$uݔ=o\W3q4HD}8a@>ǩ2G  (F^+A`z'F74nBW&6سr4#qr؄Q%RtAcaʈ!kZ͖ DP\49NjҗSr ?P{K@+a&2HeT?^xCx區wK`cLliDcH[Zdȕ[;E1}'qX{nHJ(iCC'UC1oD14uE2M9US:|1:DƫL ǵ+V%MiFbfPF)G;g=y[KB5e4sЎҞ# XItF NOs7>8#7iqk/qۿ/L]\,8mm x2ѱ%3r %| SSIEuA.1(ȉ @e%hP t1ʚ!Qэ?kݐ'_, nIv_ddnM;%$qNw";"-Uc)ب)dpGO׭"u2CDO[ʇx4`'_鿏3%c4{D`XnK}_ pGu Eˊzte76|o,ʑK֌Ba#fPx6^-nyLs=(D$+z2}㵚mK?.X)[Jޣ:}!d*M`viJ$":uIjd*cRKLM Ҕ\_*$\C#=*)h%p$sVe&BjNL)Njl!|l!!,S@̘&!pd?݀6|rVcݱq4Ԛto x.F_xJ/emRVHmȭǟYI9YaxHHeE(0i?p7ugEƉGy`(9dI#n)";2IoOͲki.?VFD,M}WSW㜿n/,}!voTr3r z4T'bSm#w~gy*{0_6¶2Ց97ek=ENV@i!R7HI=cr$9[xʹ=_w\UGB"~gGG+&)$PrW7I sǑL!>.< sLKWgZ>3O25H䰨lKz*+ehIS2[bܲ)\FaTu *O9| dX.&Di kFZ'b=/AKğqNJ?쑐CZKbw)GmweYqF͝&]FA }G￙sW t$R@B_#8Q8\s̢<~FYmh[Te,֫)"`)lၨdiޤMKBEc nB5cpoЄߒl *+XX~支AjmփL,KF{km֛{\6hI6R$ lfCQn)2M־7 ȏ׉ALl6Rny(jVW#ɠ,ԬP#n<,jխ{mFW(Fjc6oN Yt% \S[ƽ+\,Z\U=i CኮV;bUNdW~im‡ģ**3B(7 vIݦ*~Nm/*{9+oh.IݶLFí(~u!_EM)im ThxA+&^&aHO1d EqmKҡw*F:vA$׷Rq+'nm=[OպLUMrG݊B)ZZف4AqbBamz[?6Bmoro_ɂB>+ԧdt튷~V1d6bɰ@Ŋ")jqbB.)xi1DS^4*?lQKX)〲IEA/JAC9\v#s'UU"-!uu2Ԓ(v?pr.StS>y#qHvqJ QAo7E){M,0nE G?BDXo)bYQUHEeW˸+Ay@?Խ21GwPvݼTjv;9A*>ƈ~3fV=9P݀ϩ~GԻ+Pf|bcy?UH 6񈣀?6UM,O!7$KAzTЏׁҽN#Ky#f@mʛĿf6gZ>вC/1C*S߰ܗs͘IrG2?2{I!RZ16W_5Ҙ?%+M-wRz)7b_FaD|JjylNVc!K5K1%"jݨ!^,Wl[p'*?\m+$P0*V2NͱUx 3CFR9èDvLcZ73ΧQMXޟs07 .DnT0` Zʹ\y8$&8EF tSiŧn 'Q<`3 /;x0?:TVlKNN-橉k\Lbľ>n\2,pdVADb ּT_r")S]PզǦiSɺwk|Ȕt'(ڜڌ +-%" qpEK+6zT-Ce}Y-14i6iw4 7.C<dsV-u FrN,c*}$7t#޵UsW"-fXC\ĝ2) viHJI Ǡً'Ht^VKYV 8ℊᐖKe25`]#T?Ʃ"*( l%}:7/܈KN>F21|cnU6*BBJHjwLٴ8x'.1;5VeoRG!$񺱹_9IWh Q?˿ Uu{ @V7X~n?{uj ^[%򏕧?YaQVV4_Jn?eW0s7SZ~ z\D,=dS6q/'o󙞟h5eVS`P4&n։'v!r=F`κ8(sQ!69Yk4d;=5B-h z :P,R 9=rW`;'^H 6D1Ix @aN{sq +(rź0APNƝAr/9ci,r$jӕ'5VԡdqG*PfOgC?泧k֋r׶WJKC-j_j?gL4>St`d>OUVа/ÐV15'$7Kr$(%h~*qqY`m.)sA!A 7rA!2oҹ8D$lss@.U˅ˏ-Gn5P ,oWV6_9G/LaqtΞk6W-[]G<ʼ#r͆qQ#l#e*3Y' ht5ޛSt\n*WSHA> E^ʢ15u9Y ˑ";{e h:n졚?A]h-Y}7SȨҧ2/f]Q8V-op9GrSTxa 9,LҹbIdx_Ki!e硡/r?.vؘò, 2)l"=9īz;)-xXi4,2@25^^r؄PљM@YAҦ/YX)/7K[{[ is2Ȩ-Ǩ]wI◜_yGy6Y>?,ɾy B'YY"Ǘl@p; h`za2m a`OҫyieH[t26|8:Ȏ񦃘296Q,%I=O&IX8(pߵ,nD=ِY! ;wv|mr6"ңq7EE0"N1qWPB=j Z3 ž9w݅PaZ=A#v @RopF[)bf6y .|񓱯&_K ?Vm|PI -&`f${-jD T |r(An6ړޠuqRaZӨ$kZ`iBWڊGa;!fr@dmL~ڸyS@(24J*|-f)m刣* k乴wvh4]f]=頷:z\jtBd _.&# GAQQa-MaV8UV')W]q)Y&v?d"<ݖA^>c}gxkOqZqmr)C]w9+giS>]0T' ; [s(*æ, ڧ0uMhma bj&RT#ړGS@-#ژ `:V#ي˽N*kB>*݊B5#*NLa wɵoJ:9+ڦAXd6MSM|YBVᘊTk! T VԸ4M{1W銩<)@80"r6,l-s,(WZi`Ŷ$#Ҩ T_o?% ̂J*=Y&H Sq^{#$9YikǝPʿ.f*o/Y1DbhQ(rb6x(˹)EV!7ҀLrXk 40A+&zw9f?I1?dHkwT@! qW1mC6gaǽqO8?T=,U1y J[3F"YbQcp0i;)<Y~ShtYVFaI;CkOV|TZp`P4~m2slz6?Y*qeI`}Aa1\rBiNC,I '[J2\,{W`:CKrIJ2\ٲ}VmV_5F|clѝB>'E0BskxCpT4n9fɏbG}{P+mvQC;ƹǧ~%60]9S@Of"H:ΚIji1XI2 Qrg.xirL2D5>'i/2"HW{%G WSא3Y"iTPzƑECOTKQ(CO{iշv=W{BU6y;ק\Xݵn}ws*Qi 5?͒;rD z Wo| Ɛr-hs& 4h2`Sw^_]bT[f"HxJq/2q_L1(Ӗ3GDk|o#IPhQ^M ;?>?jL=%>h~u,4WV6h[pΓ?H6RuٲM}\^%d!SI_p!N442˨9#fQdv?aHEn>3ss0&;} =Juc2?a,DR+es:DW d-DqcaFp.][c/:r-Qzts'z ܀`G>`f'i "74Z/t:foc=M$P@TWsoBG2ɴ5+W)ueW+Ǝ?Ϛ8L"ey2/O=U7\Y%^ŐLOdm:9(uqgvPP5WZM^@>J7Kj290"$J lBʼGžZ pI)@t뙘91 ݞ >˙EaprF?˲d_<lk%mAbJV bec! 'V-(ԧsɈ4%^22 )̨kO{tcՌk(Jbn>/zmFmtz#|y֒Fբp\}hvx#ɜcňiE6| H-AG2S/Nkk"?FD{=KɖIsbBF ̧/sb2oCv{')hеhZxye[%6H4L% uş[zCQOHp3aiI`(޾PCσz 5塉\&/aһS I  8>W)hmUtpMkg~rNpv鑦ʕb7|+$ק^v8rd6w۵(7+$y-=ɇ4l,۶IEFܾ~%Q}U.,^E) 5@ɮM1zw0"Ԛ0}i!`J/akRzV=qUVC݁Tn␰œB _Łmk\ZVPUP qk||0&ۮ(VtXM7%65tŒtp q?/)+HniŪQF8jWK%Gj1ZkTjYRY9em!k)O4j\mW}krvQ\YGq\?>@8'oX,OF#ݍaf%T" ;,6hz~98N)HLEБocpLV/*3H#?kT|_ı>y$lAke=\lZ1QԌ!$$Iǂ*f#7\}rE/ݳ7G|ic/Q9_Gkw<"C G#f&tqi"Ԟ`mQY~Ch^kw0aN-Hjdqx9<>_<44@a˹U R 9O DtqS+Bˉ  *o|ǛD0V+f=[MZłއ+€3[÷\}M(},%jIʃ?w,ȇ:m1E5oAI)2Y-6'ŎQW.5фjTFA-#[FNht&nP^֋fBy0=v{+m SQ.hHcwKdBqj ..4̀P ViP@kO ȗ"f0@(5  &+fq|R~C}.gb]^vLYYLn+]9Q"txpCPb$:ż E'29 ~9x9d|aEqeKr$Z~.aHGxYƟrg[xRp/ksޛ4l-N(h2#B tTZJIB66E*C.Ȑm{%LTpa&;ŽԮ]@[ $bitmڴ@Փ/aR̉YbP{  2ˣvv_L&ʵ R7kޡ-da#;Mr;3~sݡv7*Sӊ^iM8(G_YlZ{kc*5ڙ,qILa1q,B DhA&meDI+3$h;n?RWʭ,)}πȃCͅCJ 0HTҿ䃖64'qlrNa,!+*9'2r-%9ZݡZHQP~'AG NԐrEơ xDhEOϖg kI1 mkDVfk,7$Oտ:nτ ?ĺD0KP{y$nz ~ś!96䘴)]QH)EBm/O(,1P֧ڦQGf}ͮ#\DP_A#fܿisU3V\ȐybYj,y?**>/ 47 F3>l3wwpF]?4@8ͬ "1L]\,P]o|.#jJKX5bKլ4S Cfp{sp)>_ͨ,.H0׽yE&.8`2[F# @Ncۏ#jɺҴ[ S#=@-X7#8l!!:s Ay? 9[KJXw4~^P/-eqYD-xʊHMWؓ0E+{Y J`BSm7Aw1ZkcKHSW|6mf?ߤ:&щ> B^虵a bA$Gv <ڎxA [HÖG-h/Z5қ /.? pIRm̛QJ^-7jQK-%Ҭ+[NSH,b46q:I0.Pz+2EḄQ^n<<'wZ;YoRfT$!XNC3޻^.(.' $ڧr%I5#z7 %ۅ5#޸\$#c^an 5ƔtroኘDEp4`1BSZ)PkmSlYF{w›] S8{FobǒЂCذm,Fƀ& c$Bj:u:$e%OLψ- $ޟvءWlPw Ui1UDm}p%Q\)^N&ҵŐ*LW}qJT[S[]DMMH2.t2w;ĻO/F>'2VIŵ}6Ϛk MH m dOZ*|PUW;ou^gT-•@(|(]ǿ߁ L*jEi}-4U JT9P)ڧz Sm+HjwzxHM;|JPz|1HSf$Wj QHIZ>}QCOZ`mlmHO\,=N Ą\,i\Ќ ۯ5(w` bu w1R-qhQxbb? XRg~ CI? TYw^+ʂ ֙[>711E=Q:b("Kq#3ӏP'5Zm9ˈdTWH@?w2Ϗ~G1*ř IWi )U :8R|К eIr,)9*c^1?#ݏ@j" *r=z+;Ķ,Bۂ8:7ͼ$Wk\,rD0Y7Zdn)n.2$=kE$Z|*?%$Δ:,D4ߺ_R(!f*  ;%, q6uP >[lLDpFG?g[9"<{43@0i8u|n6JIHj`H "PdJn'\JD#fHVMI;+0xUL64ߦUkC4mhP~!uP44-Z);a"CKo4U]éӉqrCU 3i\iFzt*tt*GvV*gmbpcWS_lUxlRTe%k\ Ä Q1ͶƄt8 lJegz@ JӸs! +҇o v j'~,v;EGȎ `Mm{ȑJ=\8텋a0 ~ ,VJmЏ RB+]O Pv7z +ƴJX֝2) 牧A03Z65 wZm;DPPvTVۃbʔ+\U9 t߀)uĵ_*y/] OԙXZW)Բ+Xӫ[ikL r0+ +`HX|f`CJE :)pŚzPbW8DKwŤMhH$ n.j+ K[=:VJ ! Ԍ"-{P k^lVQFt6ţu8B^I㬉ѐ֞#fHrT OFIa֫k#Kg @?ߒR#6r9pKhDqѓZB+*| Cs,{1ld0IM ƔiE;zPNiT@@UjI%?q\=w11,hȿ^e_kDh%L1 6i %Ocs3MAw5JTBQFZr}?M~/?T=Տv-s0T D9{V?kR6m4&3ݤS)\Rm 9DN~>H8G~"ƀx(6I=Me_ڊcߋQw+5v?pyK /]+][/OLp&A*]sqqvʈk(9Px4MʻwKn&˸=&! ;-ձr$Vw'COuofOFCQ8ɀ1-CX\чIE7?S:.2Hl8vʹ/8i[qZ!x?/E<wXznX\5_9mT#O/Y@sjʫG=T~jVPbb*u%²jh*ـ*3'JlvBdZ7n :쀼jd@tM$ږ89s3NMoWMyV ;0bɵKiDFbn "'97_ʿk0)HNy@*+) FXu5ooc$qmWH+B=>L>FGw#ddٍ+^îHJ]փ}eK0u<eYd 3Ǣ Aܖ|A7${83ǯtnuǩj# U$mOΧ>yg[w~=,4Hrr%S14+[?\Z(1!CV&ծm䩷 n*K0%9Do~,=cm^/21z4JaO5 (B_fX6.w=~AB1sWy1e'6]8%۔_w'o՚!H""ȣ(Me(i)x+Sb|l)irަzuLjKe,{:Ѥ$iNUfEbhA_ɐS?V[@$] ų3ʃkX.$WÃ|2Wm!n2}.P=?-~k m `[s%:} eV(S1)P0FYq;nm,ضԯ^&$ ƞn1 OJdn Uۗ}S"Vnl{KP]ZlIʣaE+"UMG\XŠR36~;´9|0Ų*kJ8hTz:7a81CLMI]NU(9ZEwv)YUSϦ[ it!¢>-Ez]^;aC-ևW. i޽3D2wbI k.T'[z\T9m^0 )ݭ@l \PP{bE,{RjušTIi]&z}4oR`i XәiQQ3J)¿Ų/r9}F#Wo"vd0ݣ9N(>Hn 2 U&DǘVIW]˛vCx+Z q.rl:iE]nU#_%v̨WqTKdG _wVC|9b̶ jK~<`bWzԮj©>311ǣD̗X!td o0INR[4;IN29y"|r 5xmPrn6E|&'(PyJTəUri2Figo!T.:HjuqC}^!dۼюG5-Z(-픎q񑖩gQ Gs/b`(NV qdpHkRYve¾9+T"<T CQ(Y:{偸!&#]^`Ǫ o3Ub MsİZO3^Ao/uօE s(TD~'ypD;qzc,?^y"$QG$`rwpy q!vP27@*hiC߈YO {ABܗ^.ۼh槶8  h(;=1)1ɘ小VkXol}h] Ç[)+7 r5?^X[> n0n3$nrȐVca`T$)R!\4Ì(ED9Y.d.6&33!?mymmf1<&2B2l?/fUѸNc9$f.xi#` տͿYϜ߄G֬'eR<|OZfNWI)L8&RI&!w l"I_7Kx桋 ޝ>6KƿD ] 25A?d6[ol)$#Ic"Z@ܓ*5t@b39;Ƞ(qf'y'ke:o閾1BGN!yPMz^ءiJu2UMS PSڝy` iY!'QWqBrMђ[r&nPaN$6;VUlP5k\)(-( mz )޻,UE Z{mMz0mu`k2 #z〸y191z+SϤtҵcO y#*=F"w(n튴6UH4W  i02 w`Mb+!$m N!NdrR{c|€Wc>V2wn\f] t卓&_[`(ǷmmVjA ' } UOR9{aAD, m\X-q^=1BhLf΄dim `{dV^1H-ڑM+F"8$#Q\T|!f$Iܗqwo!hL3AƔUf9}ۊRtGff!2C!.P*HdDE%{Ҧ%11Rs3IC&<_r*R(rhЪ:eͰim EaXl6yLǖsK?0(, ??/7Ij gGvB\";R1-ǯmӫfnyG'g7av썼<Ԍv6j2 yJmm7߱=(ADDH$?vWUbɶk)"! Bǡ]!^pX(,M!/4yRg.KdΏyatcmhKofsЏO %cSHȂG3>kM5KT׮fp[0S{V#| b\<}O\u 9öH@C'L:WWFhyI;v9 L# Tez:ʋV 5SᛮAJ3*Cڕ9Xpl?FR\JD8 9J.Qz MdC^ҝӟ'IjœH$",? W?cY i$6}eGv$8\/8-V .?&@k&kۨك}m7ψWF;~ d+A; M|w2)s8$-ygHIx'[F%՝ȈL$:G<'ɴo:N]sRg]BUO g[Gr5Ñ71Ki^^5v,z 31\LGqJ?֋fY2/ܙlQh߳}̜q<^s}@kk\ԃWp>ĉVt\TxNL۹ /̚uKMJ#iG@>}Eop]rn}s* q*93eam(%b"E=)02z@I^u} \zWS&2HϦi!čU(2mVV'a]AΦqd6Jf)1E <]W:T+,AS =rQݜwQJ`3BQ nR[bRȐ$2c|^ "3[LƋ,TSOYv~I }@&34T&';yK Ȓ,[Y/6>N͌;,[E'\݆[8m~%mo\+ӹ/Lb9[r ^L8_W+OQN-&"(0?j\]eUYbD͞-9#𡮟ᨡq enLC LM+4%^"NvNI!g/Lc/ȲJ.\P~9]1?#&2[8QwKl}Ur%Q v=N!UOs=1l %"ڞ8Sk Ԥ H(YxI/Ӊh[JTzbI $ pvwO4CM~|SM'|(-H l*ң loN8ӧ!^;| uqQB:Y 5k] 5m2+&eD:+J5o @+xM+Nqb>UP )J7{R۷a(ޛ}S iEs1{;W"caQ_%czqot.`> ӾMlrjOIbhGaWr#zdvPI>G +5@Ŭ'o(yn:˚D]_ ,=@GmM+hWRB˱\Y!UPS$޵$#/zol o=2Tℯ<1d;\ k3 _%>O1ʸm!r+ۀP(:|["Pn; i|Nn|Xnh Kji-R w;A[K ԟ$\З(;1*=̉@0UQԱ4Upi_Xٌ:bQ}N]1 SC?OoBm'OI*^~'&bFB^MtbuI-d7oIOHƟq^_k9#dS5En yCjInfj(;5RBeg=]xSiŦBѱKA)"H)vFo)" ߱)| ́M 7Ġ<;wZ/lS+@bﷶct9'LrN*ŗ`]zhIRZ^<*DӲe4#zl§lzg-9 P"_r%6 1e)oz^l[y6J6cpԇℱ 1XbК>T[[w&0heYbJ?o:\b9_͖M1=gvB҂7'n\?ͦ,.&ZJ⥸MAWlyoe.qR\]wpm2YLoGZY af+\k*'>'ӠҼFOY9m~\?g8ҝȯ#sQ-9*y02PD̰O Ypfqd($h&ZT$~ p ,orڝ&0lDQ>[m,;kpX`@ q~Ї39ˢZe۳ ʇ&{-[9WXKӒ.$}_yf'.="$yrx\HzB)vn'sj G,+ 4+'\x6T֛fmTGLB _-~̽`ICX_ݰǞ6pHَDr\u",i-b( Ϧ@ebqm?}1JxGHea XJ?-Bb&oă)c٠Ɵ}_'~(ȍ=:OHRHSMcL$I$ܾ Y(߯kIQ&+Z> ‘FJE@#a_,l@N:Tv"!yJYq&E''k}m_ e$S "eIEGØY\i:+&kNYZPRAO!Y>x!TL )R>#k;!LZh\xUEm8i(Im?i6 !^ڧ,Sֿ-鑦$ jxn@Z-*FzW b@>x*+R(}{bR1W®*^ئףWbGm{Ŋҕ)Jƣr}0(ZAi~';76Z@r)lt)=Fǯ-D-5O8P)kLU* v낛#*(gBw0vS;{ 5ӦFdM-oQA\r\*NǮ+sh.jM:m^$\k|Y/o )UdFX=B|utL^ ,ICIoZmR[L AMjH48h@=[Cz8f:㑶7A;:m@vR*x`fO]eo:TEJ1`2 D6AmM"bîH6(a;Вxi5H>Q?nPV ~Suf$Nԭ1!P}i]J)ǔFPo ]1ZߏEGι c6]ji}>lԎ4WUʿȂNǩǗK?HNB@#> \ W=C q?~caF=<()f ҕ#FA|UemP‰i<̝= :{fRE$Alځbz:D#ޔ ?h F5HaQ-n=vrdU$Vhʥ97%@nZb!5+11i }m/*w|WƗZ],(d*?<ċ@2JމXP7q 8 77 ) [3ɕh\]fz)a6vs(L -:S$W^;x\",>M;Ij2B @ |0mmЍdZfJ:l{@swluWq6Mz{6@`r1..Xq!ѨYeO2}0R#$B DW Z2# Y&!HB;㕐9SN$mSSl ^Czerv\7F%!Gg~Pj >qj }H8T%h{R7E3fF`S̍(Ê#8irrBtBYLd uؓ큊: Z%Lܧӡ4=jiLv@nnrނZ.rDa^#tb0HGo'Y!7.;yW6I[4iNmbmCz}w"˖LiL󍾱_Iȭ3S6_kKew&!H 9 TJ "߂ҧ]4ИוMhGߏ iIL0\Tkl)3̜XĤtb-yZqZMα&_XU}3!GQ9kRiNEBa?isyL+M^%c2<,z3!͘V!nEL@D[¥~>dC`5r BpI!P}T”%3_NϔÒi$i 7\i\XhI;[aUŇ4mYV7$]|fVܗHZ_ ,L AӔLIdv)L*:I& fl)"tVM'%$1q޾^8@ H갬-( ^ LߴϚ8<b[r_Wo [R5Hkg&{.87gek% VD*JWd@ݏUi "3,g`r aCjߗE (j+. `r?ݧ6D2|Jg ?[L&v4Q@+ᝫP[dN` i"TW)CmO\@ܽaHq$9Za 'ӭTO$s-cd?æ_,褞ƻ*r-f+OY9)iW6 Ibؖ`?NRrS&4vo aX" zyEVWff<Đ{.m,  j ݡ_{[g=u]?iF'rz/?@r N#js0&e8Fd0*3:9cOSitʫSbO]QI hЩ?aB$oloJdS[.fF}.lc1B<~ 㞗"}EEn UTE26\iy= -}%)_*-qJ+RUQbB~ TNN,W+x`E.8;q@~PӨRLCN怯gMnF|J]5-_%ބV0>&?)l^(RbA,7Y/qC`hbjkM[UZ,WT*@" QcpDIw!n=>\B@h$wTL\Ti{qR{iLJ^Q(iU;Vd`F0QHnE* ^9ʜѨ?M1c%sN/ClvW$q&@ mrJk43!pnրxaUNdT+ěS,iݎ:t R{^G׹Yֿ xtJ [3f ;`!jR}pSt>'@I`H) $F*`TE 7=|Xݪ<(vT O PSf ֪v=8V WwI6p;$0qNl)BAޘ[cƴ䜨G4@%=2M|J]&%iGt;Ҵyg,1?w,'~q?O[0W\.~=2DeEylL^{E}3g~<k8giutәm9ʩV#W9| %bփU+Iw%ǿڹҠ%qS$F1'0죂a o zO-AI)~f#ϥƕy1O5iX̲ ,gO^dFb[sHQIm5:=2?[,>w,G9#Tc=Z RO iQg$Z vٗGd^?nBkO;oΙa@r9r4ju3_%,i?}mSYL*ų3O61'e{Q6 9@؂F=)-s,QoOUdg P>);}ҿPQ.d!֩:',:MC|t$1'e8]SQAˏ;V3hLI_d pXqIocdnWFBcw7M!V8# #*&Fg8]Vp'A"AMks )N6eNTֽNHs^ԧKNPחqڦ0WnUeagbg-RSh]"`AοU?. ByVpl1XG7>ytfC H:; p*Ii e}t 5˜\=8xoڬG, CvLf{)I Xypk8l3÷N +S(NLF[,j93t o\FV]ѐb)N4 0jim C( cC4oW2a=MmTUO^]>&scsrYU0/2C4s8a(0択ѳ(qn<% rj"ʔ5JwrP#> , &3%W$e ( ^NBs ԛr;tm(;WWe*jizVpOoV"ZY-T#:lHe#UddxV?ZҜk cf)sns :w4Sњ $zQ2 2ӗ^C {[B^$''5jeFP(+P,)Q(k>Y3Nנ<.WKhJ=;3k1Jv<nk4],m܈SN8buUvǾP]a_Ũ )jnā25i;F/U*mN)SLmn}85$ ]Vb}Q;<j)_ad.LY̜N`W49K;LcW"V˃>. lGۯW⌟\&6ϴ0GFe켲s?ksI=e`pͨ.䱠sO!gL +PzW1L& c)n}W `SqQv8r z#%(g|C5=[Vȧw?1Ǜ(Hz7\_k7"-̢v0 4S+ST>P0d\y>]zQK bɮ=@O9 uYRIq~tt yNvᕬv=CmU5>Pdނ`JlZ1,<_l V `mLmoO kᖓ;?cYݖl.ldݓMNm^Ch(z# %c tŕ89#r{iU|XYU@*+5-1cJolߦ U l˷ d [!]\I%PȶZ]smֽ2%+QQ@kZt@dX fQs((r8:CPtj۾-+[Jys&Ÿ'V7H҄Ҟ>8kJFd 06=O~-څ`OBLzq!h9Q ZA" #N$!"+.Z]l5 {X 4nGky?YOd(z7˒S*_zuJ.PYP1C-qnI^h̃K%14i kN,9ĩ LR ò8)D䲸qneqBˋ8XB7-\|-fN d.=:2FqA-__n+svw\A]o? B]#T@Gf4ic0Tj>'8 G+$PG4!?k?kyfSba;ӯ یWJiB@~ouH_cAr FZN>`#İ;ل%Y/N?οYvq$W\rp4m\qyG*.,UeN%J;?ir<,54w|RJeye6bH58!mHٰ1T+ۗݑ'uI-`C-)xliHTD?4If@-#`(G&L$5Q>2w n#Q nW&wl&O7Hh Eʿ Gvjw~Vz5ƭ29q V&'*=O3jwG7Ggi#MHs82xYiRGژY-rUxIā_xVug&bJ0]۾WJ="P*v=6cb_"6B*75*vt=&H܊m$z9<ge]2GV&Y>ř1Q[AEh:ٟV6R < 2cW~8 hD$C=$.EKt8; mD%_9}ɃAe* 1˳|PI vc yZ5NNk4ĠB|O. B$$eoLFgI5"6"eOcP;YoW`eNrj97쏖G yZLucM=ejf"NI)3(9tGeLs{+7!An44=d$ e4Y(VC]5R<1!HSvL 9 4qMdaehy5(o58pIKe1(269^α&9tYʖEsNl?I7 $h%R#vՕє Xv5p-+@YiԍHm%edzqۥފO\.GT @!TnU#I *宂\[Z b˛ꢠPY!NY'0d7-v9BQ^$Ҳ0KDi?hqC?C5][\=0%]D6}F| p;6FxoJuҐv}K0__g) 1 '0dF䏍2rI*rHPȘ!JIh+֙ %2HLC\E/MVfDJ6Gہ)߮cɉM!\bXҥ7LXS]x,e5aBſM$!-2v?ZKrI =#逷 e 2-^"O p*JVOo\ǯRo8}qBzUu ҟN,8uirjLSiƜxIo4P8&+ zKo vu4$EInP! PreOl ֽ0UX`J'si/wŐ( ֟倶[h( W"I՝wz`s1_ ؞C2'cЎ~&PN_$-!mWzl~xYAmإњtPBIĄBO|Z]b•…P I`0f ]VkŐ*s-O녾%*Nm+?8\I-.=jC]F@%3ڼu{x'&.T1\$f7_'.UǓӼ-:nf Xَyťixՠ`tuͦ7gG$';|T jQ!!T>8h=r<* =E[?M 978LalH9;2]oeŘ`;wK`fQsOMV%Ln7jS(A+c𞠟n%$'ѩR^͘8ē-U9ߑ 2%zAKGT|mOG]uBO*#sor"u,+\貴a%$exWHvP ?L ?س MKE,d!4وo\ uHdzfe:IV~'k\XEMqPQJ|lc*LQ:f9xv)NT{Ȃ$6TOC7ĸL+D@[}A~֧kf4'݊fOGxCXU3Ly|s~gZ%@$ {)oQN?r_Bꔗ&yO&-yɟOoẎU#Xe,I$^.9ObL%&9!rbI6tחW^eB<1A6GxfJ-ketA^^Fce`1YfߟћQAxJd\שbH/Fdtҋ' !yfUټfŔ-g^C[1U@>?5:pD4ҵ_[}Toro#\0|RQ{?!Fg0A'C^=5%a< *34y4>6/e5ݷVP.IHi-K8Yk^iڏKW[\itLOZLbF̒k c $2iFI?T》9s"i(~=31ALnV*URfr @l A.VB6W^Q-رm3VF5~\^!TȵBMJ(hj$,ysŏWO\β$(B*>46 Y#e@BSկ [QE "!f22*#Ɏ+,Ґ^ڵ6ad3v:js%t9+ m31~X#M Y=RK~پ(=f)1+^LrLi=I8Xq0rN(d$QfzE%k;*ͣ4ycƗ9z35jc C&aa8 ]0 aJnlI$ $RQD1_yyPݩǷMn7WS 'q?Y2JYegeق2ת?dy7Vɫ]F]>+6WZﺚ2ȇY}Ϙ#׮eTWr?j@?FWAqȈʱȫ驭ׯڦTd톜oBZJ"gr=> çA9#ND4j;dQ6Aڕ;aa*7L.XMGjncͳ 7uJ 7x3KCa4,8Rwmf(*=3(]فz~;xfeVĪYp$e93E_Od2#oS NJt k=W-☔ ~qe eLdHZ wH9dğ vN_R]5FʱLl?x2cT6I|I5=3EG(:VæZe6Op/j [Zoqb+V트Ƙ}*048Ҽr#Ł | vۨ‡qbL;o6a)Q*V(9Kh)|qZx`nBKMSŸ,Iu xb*'޽,J{PB waBwqB:o-d%Zxu鿷ёF-CJ"ң}9&+b]<:F;s!&+u2G4=qk <|1b#|PzUwIl \*qM$|YsƆcLotO]ƻ 9PTҍ ^oWs{k[2v&\!|:a\*bqU565߮RObbIGCצ6ō. IbR{b:( }|.pei5͸=G1J.s8 DѷJ ǐՊ PZ{In@,J4X-+ @G1*>5C_r"mh>c"]q$lɤ+5.חrEeqyUUUIAop'VOWR9.+x2No Bl2K O3W43WTy}.@4oVKPdqf%El^25 $4*#t=9E16v_H`dީ[>"`^Ԙm^Qo:؉ )Dz]6v4LÙJ8xd][5ayM.+edXyfq"p ;Lnk7: \gp(`14r*f[<3i11}YpU|h wus&][De )_6GS[t-M -6yr6^h 馻 GA 쨿k.ÏB!Q'5ɑc-DnpȡTP|0ٹC!V0@oREZ]32q*Uxu?UGrcMp'\ *-zP11* ҄3Z&@;v\x5(U1LZ $=/ɗ&{#Nh{W[ULVMvm9R.)?z9)فkn3[n%~jnt"GUgc2]A?2@BH"NdވI;mX󽶡 uُ|"HXfG,u~rO= ZUЩ_(26Md08cXs_Eq;uFBЈ":9\UR*TdIA+.9$[MoW><[/fƼhimIA㭂De54Zdĩ4%d졺~8/o5QvSggS}65X8G;1|Nѷ,e2z,q&Up穐}涶 K ۽ĒeDf9>+N\" $H\O"ە)ʟ͆ʣ]s( 2BvZ㌺ADMU_SqnSS5@H)Zp+cćD4#bF̜RQH 㙠۰ 8Y5ÝGp+2Nt rxw%:$C_Co41oRL4_rGz>pey@ט/Qh$xe^jJDQͬbDJHDq^?ȟd 2KEgE$9Dh[!J3_ +—24ą3%7S$=>.?^Y0v#z k][',Nfkv;1- 4#3/OO_m<-PXLg$J|1[g}P838WNMd1XzRIȕ?|Kl)@FE JWW!ᑔA**L `SdHra:)Ә26wXC8RM[ܘEpѺvBƿFîJxZw em+*mP~YĄH*B"+һb 6bd,iZSivQ )Kn4ڛuK}P?Yi*z7o?32'. g;4 zrW>ʼbG=2.SMI-[$3eIQM8:" |-Ƿ٣d^.l6FRCN28w_;U$|Z!Hz%ߡp>/_&mf8+BSC9چj:)`P+%vMnD:JۄTRǾ6P"T&`L,ZOL6%Z0z#B~2\gzDGVs5o[Yѩ@OzW<"v\ru.vj,g\k)*v#6ɻ@*G%a̠2B?.[LPnOf)pkCJo-yF- /SO3WqĆ[?7+ u3n$iGI޻H7,ַӒ#쬧^c6\_靆=`ȁZTJZPӮϼ$ g0G?7Q#qۗ9֎?c т5MY9L2k쟦M戎գ%SN^5e^ȴ|;~rĢN=m*G cjN@$GrXЮ)+^^DIcPYZ:dĒ Y{jn^D'/*BIbJ$ݧot(u* K[YڡJ?LajDWv U[Ӊ6!o֓_(sTrVe">9FBB( xn+etM72KvN߱v՛hH P_l\u۹A~>V,cFr {)LL8[S$KwqJCS|M^m?WQixv-8 ,yM)Z;en,մXDf'Ventb*LBf "-d""ʖ\ZiѽQ~I>`1mrFCm&rnvD-Yd؎nU08&r.,ZSԚfNȴR"j߳V4O%! ◷̀HC:6r h2b! -{V5f5ba_"&4Tm#27>U;ddLrb Da0Ѵ􍝻4s~SQe=?E҂tīA?y:A?;6H@Bwh^[BNhxx7 _?I%DD!dj!RHZH H l$W7d#.b5;'ERPECuxT̚rQS[7wBȖm(a0'k&yHtiTcfh$+Fh&Ej*hӚA!*+S jM(\zLѷSJaA'y=?L\ic@N4*$7&csUM2#e6 fZ4#NH|<,xĜYYזGHl֘Lɞ`x -q鵵Z~IW~Yw/_0-puZg5 BQ^ 8Y+C8ty 4䖬Vv$ Eڧa(]B oA;t ܥ, ß}mm6Տ5 G{t;mZbz핖3AZcMkPp ׯ|(UVϦ/tǡ?ۊȀOqWSzP AxzlQJJ`aMbq5߿.7iSd۷㊨I0P{m %#q\ HWڝ;$۾=~y%nej,u#nvR[cC[ ]?V*շonoPUbũ">58vƝ$ j)iӓcWi`!ާDS*j֞ty0p{և$S@NN{?! R^*3F)Ls:,EE=6 7H{$H^FDXZr]9tM/A6V[W'o|-ѱWs  X|-knk o+pr*qeiZaa«냾cT$7O .uW 7'!#I)i%F]`g䈤S"׏^_Qu5j>ov$c#4'vڷ?.rZL?qN4@j~jܤIx j5W쬨רCS0x',¡.Svφ)(K%j{֛~0 "Ө H8 me{\N4")%J@ )+2c\ׯxtn;/-UKrQNVSU*PJۄ% nWaOoEޝq,PߏM&$4߲DQ)J=8;5--,CҽdczF[0/4).q !4H*NxJC' 4&GCάLH849!ӑh+r8Ţlb"z.NIįkܚoZN7k~]ao"JDK !/uvb焖.Ygzݽ$}qwը+5eC$fY'Cy wCR_l2rE"?)0&*pV~6h%zk(l0eR+ԏid\rCl=;WoՔ5ro-rӑ:ff=L71B`N-Yr`PŁbi^$Jd7/+Y5%H( N*t?Ji}F #tnӳCnJhb0iO79Wз"o4< 1kLs$.gWS2Нpo9K'Sd:6'&rN'l=v#$%\0?kB.dr!d]~I~)%d;Thk "F@:n*=k+jڽH '*'{9 L}!4ThqAh  Cˑ|%Ȇ.\hG [ˀY+n. %WExq7KdGڵ"TrRssܸ@%3$iHⓦf,ه>/KSjq#v4+M3/ zԸGEy=b99=BXSQ,vNQoU'tʌ)⥳ݍcK q>)ޏr-{"2FP /98UIoIRwP?VplJ/ԧ tbF¹M5RV(Ë Ȅ-i^JiiT |ȆA y,fD!:и-e}8ˋzaֹNM A>I;֦cͨOci{KW?jCePēFh e8A1xS˹2%}G:w0XFf<`sGD궖P5};cdnN ]GvF_s;"fIa& xeg)^4cN=O­2bꓑ㑥*2'_l1HI:M:af6Hۋ)-c޽Ԭ&6Fs2W%x$?ra2Z:_^txc$`O,0'ebX_3Xp<7l6Ir@mf6m+v7`(VRrwWӡ%V̬Sl#f]Enc3jqeGs38ӏ&q!)T<R)O/ig6*={|L WQ^ b6q42O< o7Arm ~^zVPߦ*Q[;QGR=1ZoUk5;•mUF``B4 M?Ui]T>U ` MWl CI;di KsZVDQbjMȚY8?@v+K7.oCқ>.Lۯ0!snwkK/{HqB,TEZ[ 5{N.NlR:D0S1b;銪);Uj;Jn~RZCjS`!3DtSȐ oe~w;]2` ar2n:L-TIkJmWI*;uO]b ZW 0!B Ep T%4((=*~ i]0%{Rآk(ʫB^; ,E_"H)*dE x#?9f=.6LXgI/Pk;l쳒e7.qwIy%̀lfs@PmOM;qF`.Xj D@PQObkO+%޿/|H2WX C2cĵmXvlj3x˃hB#-dRS#7ә ٰ䄼qv5Sz|Otܳe|L4z&>ӽfK~/JU֣Yj6ORynsfLgn4=GK;uezJ;ema!Vc~]s1훉 ׊|@oPo\eLQ66;4uC/z|Җbw&g*CBh#G)e w6HT}5\˙>hE 5%Ȗf%> 7\~̑m[6H4NZ$r3)?i(o&i3a7?鿅 Me;!TIfZ9TNTtʈAYք}Ly"J@ޙLgH׈HBW9H5P5-b6?@&7DP{gmڎ|J7x-E$F`ZrYJ3Egȼjkvi@Zdى!n!?|6[AQQ8gc<-|L[BE? |*'e`zg|X &.t%s߉Y ܂0\I!mX@XXƣrN<[l W`rA:~$k)?M2_QO= b~ TFdNW.hui>4Z*6{ǖMw&!R=~C?h`A?Gd9 +% w!Oq63vP+;UO\.H*0VziᓌĮPl+OSn8ylKVkIkBcm9$HLbvk h "K;43DW~H=7\ꥧ=O.,umˠ"u5bQ wMA^AɶN[J*&?^DF *~8[Q H8.] 8 R'o|UJM<*UӦ+ Swb^0!mvV}URT`MI 5YXLBI?ddmqS| (RU׿l a)O m{4X+Ӧ*i֟.Ɵ]*qE.` >8Bnہ(4<>iQH/tѸm_~y]8y0ӨZ ﷇG͂9a)] ӯ jtªt#+>PЛr+Ӯ,Uj-d[FEyڞ'ՑMm4z`:9Boe} ara=Z0+xؙSq MWos,M4O EN=Fi6,xz?+pwf](Y?_*VUi֪S%~\~9ArgTH^@䜕#W u߳0M8#'L?ZX ǭFf]r ɩSM {ӌ W9E>=kCWpu#|/Q6kVzrR?ԍkr/6Ld\e_.;&|%9 *4ٳqW&P L}*U\0-_'*@P>䭘\BpS/w(%]bc UR:@*o/]2D02!j" Xw Yjo]6 PmOkb6P%HF,I?j7ɂO?ޗdSTNfFŝgl-db.Qj%>Ik*S\%9Bà4C`l'Y! 2|6j|!)D7w^f̈́ ݸ+VcG~ggfbox"^lnhL5'bUc@ /`7,J*͉zԑdal\IF,)hw&" nt+&ic80ȦOZofQ$wq;.a0ЅvbIir~ 4uGޟeP]Kk%.5ڭȳ-#Q*wE-Բ˖bcx͍B^ai"p =C  ZTw$99bƔ)ct,RHժ~x /EܢH3! @*My'/.IW6tP- ѣh\U_.?k BYLjNG;ǑIs qzȄloF5ܢ1ȧ_R3qw (Q@M=%OƧ6wjNd%M#ᐳ5:mH5Ѿ.ʌAbb򣐩xwqlqqE%M ZwD^QMV lZ4ѳkD=WȤ)`8/ۧdD9 =?l,dRE ER:?ۙ!!Z!=Or`́y D+-D陥y5qΛE1B-xdVD+ZӐ"ΊĝeiMeGjPeDq T4dj4cp -+XT:tNL$\(8(˖J  om.a[/lLG>$륽+dEjyUUlb,n؞!Oה Fl"Ct[vZDIt3F'.edȐ $ !!a6tNiޙ NSQGAFRP)Zp1hժfِ~~ EA}+,f>`cCȨ5v]D2K9[ a$e֕+NJ)LL9Jև&ifkPyPgɓyÍX6yu9vI[|̓H,M!Nlđri76:pG-!մtW*W#|Ȍ%1=WJ6o^~eFv oO7bLU9-zeI}^Hc)ei D ~:cqvzIi9ب8ڜ&ilEcd-^‰DmTq%SV/hg9;u8 X.SӐ!G? qqkG0#։c/*dQT~//Y`tⱁl+BHrh$(ZGPM8(LI5VNS\' Sz;9Ty V2ji;Sr,4v0iA ,F* d2FE~-~9,@r椹դb`U!w#VhzW '2żk_OY|kPN;E< oB,~fd)"vqNp_Ǣ;go)RmC (SޛMoǏc g=FUTt)C+RG)zmО"G I߮cޚYٻ؁v$iBi`&TMsSE<3FiR=E0q%fkq+;re _m <KRE(+M8`"wg;ۆ<%m/qd%֙H #~ŸS2j~9.4"|\fӅѵI&,)n,KX(™W$<MrȔ3WZճȦFYׇ'Wa3bAD#/_Hy{q\prnDGYT^qpz7"@?p *.nZ*U=I%_wTsLqXǽ-xWQ-=Qh"T8pVr vRhup~e?\Y|Klyؠçjm(N[$|Zu'riZC<5=bTޣaL`HE}p6!e- toaKfil RQNFbNT?V(lo(R,d-*t|jO*E;vN&\v4\>h~``}8:nvW|pוּ(LP{J!iHUY 7ŝ3F[M%rOdnOĽX%"˘cc&AKFQP fN?ߵ=FRL(oбiSI.LIi(?5.gE96$heW CMX%c4G0GG# "Rh.Ƶ?[0͜us4.fVCy!Sp[s?wR5n$qQ3{vY]HEWz)G ?;X`bHm@fy;l= 9 Qw}!KY^ d 2piLFb\LKpw+@:yH\Ag/Bm?if].z\-\H|)=@M%8W dvѨzQZ jrGXˉ#'֜B #")RM2mh7r6VJlbU9t[U:C;+ 'uh\9~ +i´2Pb ~iHw tk_-kcKOMIUm3.,G[);@24nvO'l\COB] g0TߏSH 1rYxx ̘9c^>ٟ|܄RCUv)͍?O3&bieãP؅wl \aaeh9(K2SX#~$9T8XCBbEDg0MRxb(oY>+;?9Wh}?/Du {+E lZR9N?Y@ձK%$P'1|??f @_)*(&¤/h \zꍴQJP> 8.~3 t?L(%Zw>&-qѯ|5Ճyv?:m Vj?mc?v ~% /Wy-ӑA̚N7G_uʍ$Un(sAcKdh;WF+)+[G%Q$ \ʐˆS DSa%oDUxI&O:lzf@4KJ[ĄnAMM:S(;9^"cg1PkO )19ؒXn<2jz Uז|)ƫf9TV:j=r|9m2RD5U-w8! 4j Bs __QaO_kM[H~*Q\ܖO_-1e@CQE߮W2Y"#JԮ=jfwpT&d%:i ~;)@8G*A=˝L|嫋H主Yc Ҽ~|%CNQŲgwc@|@wpL'UtF vfYaϴ.^MjgNP.-lmf@d yު*OO]Nh!xWԐqt{8FlTT\m2dpԭcd4tJiH϶&E|Z34X!TP)&޾͛6w~SjXblΧ_̼St]?j$+ߧSEya0X5snb>_lZփ7qCGgQ.[ܟHYgTMLyX'&jfc2w\abs[Krm͢4O'&Aakԅ _EJ3fv\x?LcRK~oOlZ3^Օx8|[I?ܑo&y;hH" Na iIKnLjoߑw$~̈́i6\(@GkVF4ɼN ٗc'-ߴrf1!I?`FcHJWJ՛vfsINGC~J-A#"-$Oٯ¿GYFm?#a  hk_lӽr-erW'>?N(WIE\UYx))ra OT9*+qb4h<+Io׮(hmaWw늮Fb++H[zbŮ#+J 9(II|K$,5f h+``Ԋu2CK `+qFa¤aJOQ\ 5#(^u߱58|PBC׾ءqeQUJo82u ,;hv"3XR'=e•EiOB Ţ ߁[85<~*~x"m" OsS_V]ihŨBn,d*οsEgF[? e$LOGeVxo-gNխteD*7(# :qp',7-3J 4r }l?39}G?sFҗ$DhY$l i[x % 0݃fbڕ۷JU,z{o&^czpC_?1$$ O _xq8R/Wv?csv&H`ŗOUe+r`@4/Wu(!ϖ`o/JoH$u=90 !8_.6)>ԣtmGJTsK'q\h+Ou:;A†iY .KHejR ד*dUP~.īG XBwZ?܆Uv+f2C|DFn[b`l UM7>'2D$Ws9+}O~~#&T';F.Sr\Y>]XΦZ9aăB6X͈y2[ >٥UE@?gssщ/Ӎ7VX݈)fKn:d!`:.Z'Z0s䌽V~~`ZF@E>^?k7gO>K'4ƥwfTsͶnsc(FHe MV#doL\KVLj29\c%vzජⱽP oV7PA "KeL r9,~M0BNj>:}ȉ$c"Hej`sC֙"ׇR}FctZ6 sPd fD2tl̟ :vȚMmILi핰et97 "Wr§{.x1Kg|ωW=+})LBݮ{'ZƑyJ-պZop*-,M&vr .)GUuIɎCJߋ?.adbykͤz,ZIN!*5zqOMŎCU]9Y̛pBsȰU[:~f(êYK+zNQ9᲎C!JI4\2lqIܤêbvdNg?Fٺ(8c+(Fv\m*;d'0XDnUGOy1tMWK!4Dcv4 簗6LVo3ۇub};P r&91Nd/WvwadUȥ@Q |HsISʋϭ45N6]ʸe 0Vjbd%]NN,$z-MVwuM)JR%QޘIHڐS8-!>̴]nv7_M@oJ6&l"ϣ6ΏQ!Lcfz`H}^NeUyKYIw8 cw^Fh~ E7WC`u7u\ QsWTvdDUkC09xa(7P;8Qˎ_*ğ7-Љ%2wQ6\E#݄s\ɬgʱOn?I|XU2*6٬i}r%T#HD*qaC͋UpUgxꞧeN^/Ϗ)d| )NAɽ&z0Pj69՗~ytglUlm7߯ӑR>X®)c*{HWIM:'̬eWi%%=2y)f$?">eylB/aIT^? ^NG~Y`6Gtr){vT_ҝE Ƙk1UޡqZUYaA ׮悝:!(1UlQP?l(uĻ(oj«ۦ*v(+bSąUaۦ,WF,iPt}z#*.ŒRyxP`O%8ZY!ޢ~x!&xl4$xT`} qemҽ*)[$ Ozњ@~-d!n,lçѾEv 'zdZ%;~5S~up9{jN]:Q=[w:e=;|B|U}{V/ l=61UTj”4iԴXb@v&%` ˄[8 4wBy~DHB7onyFs|DF$lF_\'o\p7-K3 z3^RqDP)h7?u曣Ԋ'5sxA)4oۃC`bb)O6Bwڲ3*vzyg0P'6"ZfQ|_N8 N9RcNQX2 +ĬYfQ)lgonw_2kcpGV'4:L%[07НV6`'%sF#aP&ؿbdbB"9WVCIS@*rHȣcʕv2-@Zw$bBkEՠQ޿͖Czx%~?OVM XĴfcO/l i=#r(Xo879KQV,ǓTnLjUZُ-$iQ} `jQpw Gv$&m.tR]D̛xrLRi2߀?&ͨ?9_3%PS"5 ր|0$([k)P6ө%MO\X}pE_&CgfjB7G0l{GZGxɹZL%V R2<^=Imm "KtHzD X5; Esjz:HGkx/QJzO˼(_̢ ` OӚ̐{!IV)=1Lj⤗S]e#h@(s#<6AOѓ <-sKx \R)čS"1ruٍS.qI[V"78 e)p<;,a~t<*[|aF˵xg=E rd^!lҝ2[A@ހ{5~iywP`{xQ6`*/vW:~F$ğ9!Ϊ>4B\)ݾY_ $$U\&Ci+ȠXy.f?Ye-B+$h$Ai__˖=eWLh+tdp(K V(Z(S?n- +xS%a8xcSxf&Sn)Xт,z ͆qspΕ!S s]^a%2@Z ¬7H6)YV2wYK,iz̷X1)ח}u⤎4[ʑ,Kow=~ECm!SsoDnݑ*Kvf`^5N.3xaQ z6k3Hv*&5]my!+ƘǙ5Pxʿm4a=, %5J(or IyԞ>8biqdf2(Toأa "5SZxP\LGoFBԦo\vG$լ%B[j0R'[qin1Tbwn*_7_j־5̝I(1Tm722 į;,RHH jyar@z$c1 ۑe64K3+@*#w#+.Y!iSJxwK.IPoJ1x&B ֗sg'ՈIR覣_Fd*9T~*u{3d҇}+}gúcE3plkFc}Q^k7rzQ%ڀ$oU'S1)^m)dc6\ 4l& +٫N2GnE-p*\<,{2 )[y~{`%Ul" 09; ?HRA35y`9*AGq uZI!P(k"+Ol>`+dIC PT%HP ;2`$bG5^Q: -G A" "m'U MY <@$~0&挚fg,3dXsb'ai*I bQ+(\j9;p\$'z॥S=;GGeWi#,FB ye1}3 Ơ(!Ah+_fYu0z u (GNHp7V5LjE@HbiU&tޔr#aZF$}M\q9CFũڛdKPdD97GaR*61). ʱtVR񅙍A\JSQ,k7_H?-]~_̹?Gn 6Kdmw͉*8xiQ6}*-mzE,LzaAC4cX `(S-mnWubm4hrTb(=2'$"`PScM4xhَ7(*HJ d83x=@<{JOogyMd%onO}?[=Dd/&_^'*6v-Y=H*RKv9)#^kEmr#[f!PT|1,;sjyj[ZiBz% IJŇ=&~yo -Bz`+fOsƛ m:Ы;jNa̻8 y/c4$RʀrZC-Êm-Kʅ o1̻ZlSk3=VCn\2 qI~grwl#.d餒L6q2dt|`N3=&j;{VBC޸ @'Df%j| 6Dl[m))+$;wf6^;ɿ+!hurna|RΡ}W_SQa }=ZBlc2G*RKx2rzg"O'/R"B=f&*=}tΗ ykhT'"ו{xS+|k*UTqc^$m\srVE?bfXL.G$5B )9e}R5(ًٌI-dj267J,P۾LH;M -KOTᗃL},Ui)$QIs#TFdPu#3bܧ45 =TB6_ۗQ /Ր7$5S(C}ZI2*GT̝>N _"g;Ξ[Gnt[˰$t_GN.a`؆Xq|AԌ2=YZ!܏2/Ma\-iFC =yY[#mKCEqFAۨ[TY^ te2J5Yqq1uem̧yo|0h{+ r +5ZB;2rfNt!E,n!Je2iDC?P;7l;#eZB洡?-m, /Jir RfA?PZ#7Tg"g#CP//^ahPF ˶u=$Y&1&]m!~ZSlY-Iv9)0GpFDA$PhlٝHBy 켤Em丂zLq!$(@*Ǔ Yv*vq(%eY(٢58=$R[kc${$:ov)eh;= MP=ʁ/CVꐆ ae< B&noWZ\.8IkJI@Lc=(a冱.MܱU}US4@A[ΐ|7!,Ixq+(YuZ3*.\ )Jz-Rı3)CJ 8=_M6{ :vLtibO2D+^;;@ uT9Tpd2SuOʾ d%-ۑSN}.CN-JSq 4RR@㐐C\]KrCJjRF1r`966t8.KpEN,ǃvKVFA{)hm˼3F(R/ .HشR$"ˣ}?g0uY4m., jӗW !^XF1|Y|$brA1S j%{=*44Ũl -G,ė2E[Z\]T$TnB1Xn[S|4 w*99˦P̠ =0BZdQmw0 H\C~1e &%ӏ!J˩pߠwj"Ӓ%]2Ҹ@pLS-KmMf.[ڪX*LJ#b_,y7aHIO:bN1i&Ily DPCgsᓧA, ٤jkl-IcXXcT$]5z|a.imQa~o1傟TNO%Jٝ/f9Ȯ#WD`v? lΧha^va[/MT+jPy/ů]Ib6~:J7k/,T Tv8x\YEnL'X/t'Vv}$C;4 ;և v<()q$2#Pds1n1LMeۗfV9SMTu6zxnN.lQ@lw䍂j\Rvw!Zͪ6] 6lI]atBNa ("VWEż6]'#ϔ_8n~(++R Z" ?¿ 6i4os`M`NF#YhS4vX 5 9cIV\#HDUH5 ڞ5أ{$@HdlGlNON8+ۙ((i~ʁ%X(MJ7 %.MɄڰ]jaeC^;4Ő+AӮ[E*(<:)|0%U&_Rh>QH/=p0kdmO| X(wޘ檬6'åp* ;b•vqbO*u(+U"^BW)4bUNUFnO(XiqeJ ۾Ȥw~(4{wX,۠4oC}r-HFMHb (f M~WT}/VG~԰zB޴5V끢PQ@^D|]|SOOjtqMsiһd3 lrFt2D ]x\RVևLi #%3*+Ťoڠ򸂭uCRǚk4} ST Xgp45E(rZ^!͏5P`{t{gvoBs-P[ 4?-oNYžJpUٝS#oł}RDwAjsFD$)U e1 Ǫ_ci3CԧoZ&Khf;2'.[6m_^/ixC~?<4b!|\GLam?gq2<}<_ۣVO\ In7olode $a_ylP(2k2VЊdxĮN>X-AM* dSX `(yE2=F$oZ̈m2stK`Jkn|ʈvT54pTbTԱ>?,;2[J"WWCOn!2!uտrٱɣp zw?M3I"+cnUzbƩm t1z`0ul)0`@$l>uNV%G"k}rR}&&2ݒi(ON4“ HӸ=Aj(6#zes0(t]r݅2A)dxw<$9P%xآ̌y2/ݰ Ҕb#ʝMa T(J#P^r# 1 I$E@K&ԯ+b x+*`ڝ9>< AcGG\^\CI$S/Dj 7=[(Y% Rz -9 a:fn o2FCy\0?LI~7/G0y2]W _W*A1hrSҵ =Nq1؎k֭M&1-QkNeO XNwlڒ6`R9.{5-#xשŕG+VQaJp!drNmr>RAD5L䘯^-UY(p*) b8UcTذxba۾pj)UZ~ذ!U^zlQMM,㊩2bEVRtۮ*dZ| H' `(I`4{oI4gzmE `+GZadڍzbh`HU>BIӋxmq,P;`j"@>t4_i„|?\D楦0iO닪τI q7C SiV-X[rO/Mښs)p8XC@sCs-dRwW(Pą¾TM"n)rYY@J7B:4*W=AڄYcx;H]FT%O+  fKqK,@.EݕnP*ofa 9ϹΖzF-]b mQʴwQN $aN)c?W^z6%I$WƼg֓J8W! ^K)8PbW5)! n^TQ>BfkmFH7G!'#Ory`.,bbI??WYBS&Y'w}Y gaC*F䃘`.kj# (Rb,0^+42" 1wuk)V:ZA@sfO#|TOu\hJE̋r~Kx6qodS?H*-w&$5OO2Sl<,mZߕsY :4[oڌo+2cޙ[Tl;nFc5m?VQ1\$)0RA.~7eIʀHޛB JFdyoY#e-͊ v{@3}F|_:1wj2W xnQMӑ<qjfjȨy uY.8Jpe 4xLqiRcJZ|lzdY0`HߦUmWjvAȒEQCҙiهOjp3T1 ߦZ;-A̻m\ [o9ؤ:u(C3?L5KUԊ,)񀟲s$yn-yZK7 y* wOn\32Z?jz_[m,YLO0Ho?Cg'2q<q@G.IA昇SNVe^BENG o;//V\|6;t9n4Ś8u+2k EhC+R@ r^`J#FԦ#*F½H`$7G $Y$i$*-vc-%3Ƹc4f{bbGO=]9:) PE5UKK^QߦJB@$AЁׯߗP\ lD+ rЀGiUscrgo)X ^#Τ-#)0fzi$S!r `SDT`j%N=E$?:C2j 9"69<d%9I? v}OIn3jĞ A3$1ਗ਼J]'"80:d|Q$kOLx_|=r4 ih, qCF >'9ɦs` 16%,p sn<Ėc3a_K^ ֿKZ+ㆿ`q:"< &=k[ H$I?Y2J?W?!2ENNUkA%Xo~6WӴ *; &NS^;*̊>/lC"BwVlz&zE{fRbb`pMvsv16ʞbkJ]F6%2*(܆x ;_ᐔ]0Ct%e]‡.p6X^$Y  ܌~?,2}qu;D#-3pU}CJ8v&Qg}LzWSȲ@:tYEl!"{05HU~'w.@Wmmi(OA*%ۻ 4*{{oQaX :T,O w둜AlȷM6~s-vQ xxD^LƼ`DMmm@ip.#dq rڋr @3 sjw\'SkLn/A~'W4cM3X`y;CTT#T•ڞn,U^pPUbO AO 6M!BXޜs+DX2~~Bq]f}be8P6CF^ v1"ryoP,`0ye.xoqQS9Ȋ;NO5v ߽j>tD8[M)ˁ&`8JVA4JWnrvE"@+R_lUwيV !BB|ZZ`Hv$k^C:WAEE^%mdiws8"*u\(U QXPQ8ס:b+Zb{wiuߡ-ʼn ZtqE.}F4klUa/|PViڣRdޠuڃGOD%=S0PFw|h(c=`(vJ|lkoLY=H+jƔ01**lX zӿA,H-)-؁ש{0EGO$1NӮɁq)u1𠡿[47 O9.m`}1VKh6Y}E.94I4^?L7?!ri"q߻))heлtDT2;VIAGCNb6$Bc Vk_cH4 x2$4J=r$%G'Or?͵bX+v;傏xPZiZt'}pf?.#`Kɤ 8LgѼK6Y&6u<#ٽ8t꒞OL13[[[7BnK)⟶ͯezC v ?-GIWQ*k2\142\H& 344# XAg{=.ʴXqԼLY\kBk&$ ;dl='N99G4Q\#ͪA4=7=sFbi3CTwcȴu+xЃuDcFbH PxSH _%ẁ{*̌n^ndvbӄWԽ(Ԯm0t/r"/`-ᴊ3`'K5GrdȟY7xtY5+Mb׍~huإ^H` S)[Jg[TwbZ E\~UKSzIjr7T[zɳ7NpnYv۶Z t[""qRN3o-mϤʢZ!z-qbR˸xD!@G}~Oſ7ǜލiodD',@TB9W4;$5@ix$">BbPF4],|Qu2<iqizwe;mfE1$-3+ۑ=ödk0J- 4|iK8Qzcdʗp3.q^K^ܐ_N# WݿXr}GZ%*{ށ| ~m 1=I6p:d*p2e݊m4"0pW\h^M((I?<'t~Bb b:!v\6U,$vzefO}iC ~iS캷lŎ3 @I㺦owrY-zqj|d~v"$ =K-nեkm#(mhddD҂<3_1mr)αkG^cf?o%Z?3BZk1o,BÃW3FL0VYch_t?aq Z\@袂3#n AC.!mHLo"4$EK 5VBM=4\vyA#D?[&nNA?ښ뛻#E=UbZ"ei=(oq?9"m8/ΒqL+݃; \'/%p,CSn4r!y~Ҟ]?\HKۭ:QY'D6jeC7i,'LOԑ@LU EAUܸlj OW(ef qZpD]t3Н^x1]R~Tme㕸P]FX[ i k@?ώTCJ `eLh+Q03 |Nˑ+a2I-̤DduWǎ'waV)6͇+yG݉eEd.xHEJoxזfin^ol(aY(ndz`=JDY? DJԒۍ* \20efbIɄqn'v;P bCNEOC׮RK!Iݮ+혳 IbOA5Ӷ ATu9$NN`H"zAAI1\"tU=e2F?bwKtk{l($\'>{v^,uHsT(j2`ܰ5䜗҄wuYHULlweRd;,fx {EÓtc$u̪v2ȐW54 vSxD[-ڲ1Đ mđH+R7u2~Aˁ$n M^"Gw~N̹>N&h#2Uڿ>*& ~XFTghxT)t2BTBtԊ~Nu”4.}g+_,“x/ÊM!ꩣw?OQCR~;)uœ|iZUsJĢz0Hpw\m A䭚C^lU~5=rqbnڝ/WvąpZV† IڽZ6<I u| ^?(+<00^z|X;bplQkY~ PTT<{2 ,5=;dYxwl HIaoAǷ,ĔPӯ;]^olVڔl+aYiNE)!TׅA;0_٫E2%ǔX֡lz )Ɉc Hv- %& |&$VW\8c -8Ժ*ϥ*?eV#WTrnQ4`ujQ%iChҶܸ1dLcjCQO9QQPӳ Uhɔ u-GK!oyVsPN,XIF%l !ZNV~/r`^aJ9(t/Ze]\JN?LrXwyEO1I]JSdY~@Ƌ"ۏnYbxX"?yrNP=7:Ax[G|>8_6xp[,%0+x^(]? ۳UG+sPh02SB8&SYSqk4ηOs(s~#f?bEMs˅:g {Kf2c_OycnL$Be %̳m "BJ0aC$zIpcfWB\**l .TP-*TQU5ޞ͍lِ(yG쎙`lADo ۆ۰9t[@J[V212n6v͔qsTüy.gZ-*_/U!(8l!hKWa__snF7߼6.59b  "sFc~V\4۔۷"8sO0qPIz4ɭ~)[da kI}Ãp3~pҼ/խC]!`BfIR'[9 +$inŚ$9ӱLͷd,! y) ,m2VwF9'&4f==V ?H^[RON@}<K8CHA;dv.[#ahe\pHIVr0Cŕ[SGX_ⓅQ{iY\ 4 $8Mw2S$ZQ)Q_Ǯ,r.YCtI1ZCfRhrġH$BZE х FY%OgouUn~M_OM3<``ÄT} ȉWUOw3<ۗQ;c $e2r#Z ErfqF2@jdj0F5j+G8"z8[szQaXzeHZSv}trrS4S%u ɬ=1f]&d-S,)ru||r9'dOK:ÄG4"wr[0i n,cU5?.C*x6V6vN̻VhڴRT6WeT@<_ܤ.\ĒHcO%Vx2"g\-M=bT4!!DXgoO o([a~^m6!7}[..X|1b*DQRi$Rvb\ڠboVe[E@7rʕю`+ݜMl,n83', wLLMD#ugv$*#q3M=vGagO̜cw`(3$)H'O,oU#J)ma 7Of cPQf)ܸ[6r?:C7z!c גc[XZЭJAFIc[oߘ$OTFqi`mAd'u,x&U㑉߮FA2Y5knjQNS9zћyrሓ,#M)& 4nj\F"`Z ( zw+׊2p~M\<۠x hlV@SA.?_.G 8v'WҩBGr:tx8ZQml$^ B;D0"lW4F՞i+cj?ew pH@Ơ2JVJZSu;dI,e@#@~bG&bwWrG]8%?^u^Ő)7 :o_d3鴤 YeP W@v]^kd%+H :`ܸ,,X_%5 x+M'!KmnA'@r1G{) ρ!X{zeqk_inJx ¿M?>{{4)\ʣzd9|1xHv*G% zd\VG+<;`r#4LsO_܈ʑ׬H54c:r-X OALO-A?ɂƓ(T:{\AAT{d*w6(݋Q NcƗzք+JCkZU? '—-5W=EqZ^+ 텉_;Vmb )m0uޘv%Y)O8bWرn۹BnqV*偐CI$MItص9}7(Y*h1Ռ999|mn|ťWwIfW6:l2wwrů|rֺt=;BP0 pW9qFUKgü :Iek@!@ނu37woݯRsyslFvE{]=nC;">_dklAekDƻa%HmEy'Y3ȵ"8"5 9:05FA./EH]FXyPqhA'%_,,WK&I)]}54(8X'JTwǗemR);1Q3/2/IܳrW"?F R5~Yxrs]2ض[+Y^[O3ʿ%I崠3rYOkАmI P˧Or:okmmRp$r\rQ1/_H䜐t;l#uX)mX ԅWe!#t$v@n 3!!.ZȃY%`dRySc %ͫ.ѷR 7CU ɐXz Olֶh(xXI}ž36YB|)|>qtyc;@*I]MBDy[Bi t?ʗ]I C㙒{cִG\NMpl iġE7*@PuR[:WTmEoC\&M`Qd x'!wr'i/UVyRV5t-PzP+lj;cN>HCϤ}ZeT< <}aeg?vS+8!-z]3[On9Ee7^էo/!¤]ɎICr9A؞rtG tn#2mfTwȬȻO{//X@ҟ$7 ƌ1TƏ_/~!6ZACaUnl]2JSNI Z+kZhGlj6ZQ*zu4l^5>Y3t[ )\;mKyq9XS8j˷i΢M<+rL4YEwbBFڣ2`ԚjvB!y}&fh2N@9aL;+K-e Q64v<L0jGS{o|ٵ{ eVh (*| ߛnTdxJ M.K5L?G}hr 6U(XCBw8auS{d V"{+[?\+V+cWR65${ $ =7]n…]j !C%Ǧ⽶(l&9zkJ|?cN|%`^]DXȟZjZ-Au'.+Bk2hsW9T]TmCL-[KN${3*0<ܹD̎Vu2%Ɗԣک\ E0xTZPl6^SAcjKȫV|:[1/VT }/L\YLcA 3RMG܊lI/ c])k2#dToJx">8N-DS 2;HgQ~&lg<ӭ'˭6-z8+AK_)|MY4$ĉed=δn29%~ctDNGӬ,|}.CMcbb8^K8ȡMruIKSZ) ( Wo0X-9E :[U%zur -(nXjOe(J2@Ǩ(Pfk|3O{U#D?iqbF&1 OBBmt\7?'\ZՍ;ݲɉ[6̒ej7}ųA)LVW]c5@wӠ>IA bXq}3f_6<'ɏ_HWzM@0/EnK0=v8 EBo\v3a^Ox4w$7Q:qD't%[sACRz/\2.}w5F4=nMBF$ vP#FltЏE+ktfR ;m#]S;EJK4<M/xE >;ev^Bׯ#ہ/@+ܖ)YiykwAĺe߿۟ (;eAx8i 9 dxߴˌoOԂRN )5:Z:dˌh?w}ߦVsCZ ю< 纪<7Y vBzx}9 s: 9pȨ85yPqy+m14de$T$rSۓ_r$Q ^;J֩@֧0LQJ}nK銐o 녊)5*0*m,׮I=|(*ܫצ,C@qB=B>  J%xbv ON5V7qT]9Y(w;dYCm8xvACP=6PiHkLR6Q+NPmhÖŮw n<)1Iu ZAPcZOKɉ&!%0ߪl.PAuk}XIH!ɦ| _O7EH[yY55`GolJ|o?rڼX=9?һLs?QVM1 |w>iխ*{|D8g;M<3QqF;#JLLf cTFDh~?qʌbVlӾ[')}L*m,"j?=_Ҋ yKn%h(bY=GD[}RhN",wM!8zv%q4b^sr;WV~T<Ɍ\ʞuK%Ŝ@,4QW!x;1 bl;M$$ mmlv*ĐA 1 9LdN۰ufZoݫE 6l5q.c{y6t4mdf 1uk64o&^緞ޭp[Y@W+f6|.WwywN,( K3EKWI_qS9jS=H$W 5ңMrJ9(Ӎ$ à;sCwi/27c[SVCYhA&0MɈ %=@27Lr[TjԷQ`'cQN<(Ԕ z5(fPidmGěu hF$[r;Z TkGFHllXZT)QD6-,tP2xn ]U+XKuK;7ع~s[9LR Cq:=3 0f^f+FWvLE闗!ڂ; (ّyW8JTPr32t"%_1Fͣ:zOc0SDDD+`U 9L$A8A([n5Ǫ<G&rlB=y(LoIN&,c=:x#FJ bo6)ijy;)%yc0ơA?a$G$K(EƠ:NkhqHU%̍W>M"vK^[1 V=ש\6mp4$u&S>^n5_[FA)tgm zuݝ TuS[=eTUi}&.ŐL4W5"b 3"tRL[e:P~ï7ĹvxfFf&˭K%- }4~?-$$9 `$O0UAv2N0y9 %ȁQ1F\ IpCڇ), 8kJv9<;_]:wZPLV4}weᣣ\z+EѲR1m9saz66>.<4Hjg-sv|[˒4]}SurM v2H>x8\)ksx= 4mu1*d孜8F.] P,}OqilR_UVej-M;WʲVz*>$GJ~h@r y2)\.-QM[K$1` }&4K|9A -w1L1ˆB*N$e42e:2K+&B*RT+.d@Q"a.SynA"3jPStYv"ߊcy4\Y0rIAV>=2QvDx*i++OG&O@|?XfRE4F"[̘`Hieλ ?~W/N8Wi! |*9~*>fhvz<1#G$c~Sr+C.$"erۖ^k뷶4idukYLBX*S, 5NQݣ[[HdF*wxۮef]ɱWtdsofa# -b" 0dw`N[zLl,+N`fv$ =OC5n3QMqNJ WQy]L PmE6܅ )p*!炛FB jdHoǖ+A!NՕv?H2$;dE;SĊɄN?9mkN,nU%GLrf#|;hjhw L@ułz,`PP\OBKǽ -d&7]4Aߩ|9{*a\H:6|+>;m%Z=ʼnV(1bzb1WN)\Uk7JⴷcV:`M4zhi҇(ݮAH`!n&;}h IO_q2 {YDUw|D׶ Er/^%͹=D%~9&OhsS'ew;LjA )6I$fQ@bƋd-$4q>"\L UHj(O,{:l#.!/޽(3 &u=7*tpƎ="ro]2H}4 D7'\՘!]daNa|[jlGr% A[vYCۚg%/Ě*FkZLʱ]z= F 2/Tge|E,d/Lx]re TMd9;bOTd K NO|LBb4ePwaB QYQA^|D[Jz1VM;\7rMcmʝDT!Ղ9(Z O }`.}3Ce'r0dAz)Qc+CqZڄd%KAf~Uu,l?hWoku#aǜCФ Dp?=i_CQNh=?se~4LeLVn{I> OsTX 6>ĕ=S \g34I )^9، r@*iQ]ƿ*eaG \IbY=.7u5'nV,@fTA_Q8upuJ+P6I4Y|ӡ3 fy#Xjf53ܽnnHj5\}HA/nT#A#_|"d\&N§/ च]]TDZ?c3qg011)G.LrV#Pin^Mj[|!\/r 1\ ?2b>f֫VJN* 5Y")H͛Z|f_{P h}#$M#Ku4'lfk2C#*4.jSIg2.rz>srPhhI?k0Mqܷ Bn~HG+nYj59%ћE@֊x-IIK ĦQwA-#njh}D 6ڹU #!=Ӓt,t7L`[yueZoNvd6=@hS2 {*ֲ\OJTw9$"("F3J(i\)+in޴ʎ%]vẀR`nr\(J8BEXPBJn7@nJ@T-ʌHr`޵Y[BhZXa{2rOrbzudڃ an8rݼ0"C!I4 8%ld%E);;hHtE*6ZspTѓD3"8ѣRQBƥoǗ2>i4AӻxddXj1;T峫8Aj o;oN=Kԡ*ו7ͮ>OQE](9jw&lRe돬ZlN*S٧V5Ei،ČEO/kriw&$8ֈZWlsczَBgF ŀz>>7Ϛkx"o~`B.#+u?;sa#J =P@ZœH}>,Ŋ\*a{k uplk($mAq6s>8.mKҸIW1{omu\ڻQgȠT䭕Vᒯ^],d02H̞xcd8UdFbڝ B.`ʟy}[92#t%HSkчZ#D (# {eAƚCJA$hMrh#mcENil2*} чA?Hyy=JʲNc,/)okrDŵ{18~9;%N<@:b.qvL~.J8E^6{ RE=s&ܐVObC*+/1_5R +m$VdWh M0",x0^Ƕ@H L^KFMBJqF4>үB**Ioĵn*bxrbxYpZu"Nj|$D feP!Hex˱(A#Ot">;d=͸Ӳ23E<jvcrv*7{3ӿ/4W1@?y-#Q(,Ɵ}4ˢofWV絒i#oB&VG oqZ}^LI~=;fVl2BpV8)u{ =+0+wɈ sn8.r:#wm*ABM8tBSC m!%0=ojһV~ ~#A~XFj#%NrR聾*yWz5WnU#g)o<՝#41 ʁz2XI.Ĭhڟlr2JJU8x]Td1_B.<܇L6nЌ\QJ[H̥%CFSvȲ]>bSSO%2z`To!O`o vrL*+}DN:[mĈ b7c=2*#~ |YqD;t;Srp,K^$0tļ)BNg u)^ˏ' Y탒PɊ) AƐJ֢1 "ē*/:3|Rø4=Dg/O$w4{nŇE!% ޽rA&"]"V+t)s+#bH1&(?AwʛC~ݱK`TӾF{bG z^;`!14ۜV}5=$ ##Nl3R#;d\sN'qs#-ё]Gzi\>>heVW,~.~OyovV ZѱƕU_~yr)4Š\:mQxWܶƕQOl('_~PBjZtłc\QK ߸hۧ/ p5eƴ>'*,"(km|VnnInTE00SDrU?, I 悛lq :">pĠSp6Tѻ{=Jӊ)U\Riٚgdd(J #Q&vfJ6<*3 qO#)$0s0߄OЙOK[h -8?pVCOqho'[Tb2Gu59H͗%$[#RT3\CQ)iFk;ebn (T/̬`mAc"J ze`elI#vڦ2\I0)E,f9Q-dinK7*Z*:ݘb~=vaǶYM4,qɀ!s0o_D$z^ љܸ3JR}CHZUKHA#g,\0OՊ9(g +V32'"6ۘbVև+nR@Vz_ [jgs5nR \dp Q?g1wpBJrNxT[WTYQ\:F aV#n^Zm 9\u9w)Z6" E." ӌ^fjpjO^fg; .^GȫMAx^h`1$VDhvl!G\yeK[`(w}rE<],9-BYnϾ_s"N+w;vgI+9h\HS@9SN4 yЅf<|9dT8M5I$2)2~>,kYhb͢ӣXJr(];YnoPS-idSZ-@KxW~FtRrkɁ~j܃?!J}?|<>?- $ei4Г8,5RFU6 <+_Ƹnn"$P}\g(~@U~GY\S{J:b L G)*h)~`1vCK4卤Ҝ+_Fo VJ/ :@PQߗ_Yۇi..Tmj[%z$}Tc?ToLcQl%dV8ymb 7i.t Kt$)˟q^-&(]cQGlҍPBSɷo"9rW<ڮ'n96TD= $db1 U)b_Aݫ14iqp̆7bTv03J[T }40 'DZu3H/O2絙.cvd5VSFr7LjU"ܻL!}ڬw?ח. h׮TO{BUVh ,VWa\+Jw*6E=k*nXPZbfG4B+0!0!]^qbQ,y/LXqEp+A~ pqV\ 0!FH7Z kz6z`oK䂛p XAQ_8@]ʴY3qv޸V?(y8~oʠF{xf4B: qd-v9h!U+%bµ-kim$0W`V Y>,6%2ά~¹Ua\XoS /WmiHcS.ݚZ]&[C2Sp\O1^`R}k4'N2Lz1S)KRldNCЕ5 6k%)cMTSe<\y lQ?V9Q!ދ.6i_2fx_0`7r?W+"xl5 bG½?RHA\@fzٔM$.Rj=2"Mw$m3 }+3pe=F%!R*ے[/JrsdEUDqo#3VÖ́&H ki6O I2jIJ| U`7o2LdG-RsO#_B4@(Uf>0MqͼPdRY[5J7Gb٭A=M~|(Otna{{eƩlgcERNk1T3v90)RH/%>{fF8Y"Eݢ(Jcfj-tl$Ewh*ѱ/Q!y&v~KnQ9 91R^{aB[w-YÙ0DC~/es/J0u7<qB^o$2>t}VDYIL_gÝv-so]l$VGeqF<me<wl^NZA"Ǯmj熑jNöL-lǺ5NK9m>"Gp2$[LR8֫aZ*q_kRCF[x}Su6X2ӌ<(*M"XPL8ۯ2i~o6q@nL4|)p7<2Eæ~k߬(pڔއ?|sSU~,CQi" niGa3n$#uM^d셳JHgyFeNcn,Ds/:.G՛LqKWO,zFZ.?!̹*6-[iUnmʔ *ܐ5~#u2.NFQw0rh&AI .U_ّ>JIҭ{ii0k"<'r<<$EUN6Ɯhy#.̆pN-oQv&T>M@cj>#U$?s [Zg8NmEf$WVeۢ? ӧ +Gh*\i6HI* K?9TpG$?ƅ/%@(FHdӗ Q۩9({ximm8ҽrL"%};|dgL.'_$8JȫPkZ??7ՒMYD&aO5X,Iu-j ƣVK"Z s>Sa\q]ċK8YKqdyQC+VZ+[ i_7ǫy%(ud~eH:iBI) Aۨ녚?JҹGa!=h^@oZZۙ%\l~ ف a.uq&o [R>D`zEUmbeHMLt)L.NeG_.ďq!J!:~rPy7%Z0؟n~cL4I5јәLJg)w =u#IUB_'(,H?($|S;ao,ocFfoI*?AC#$Eypx$$N,#ʮ 2?lHcFѫUKhiFA/Gծ"몫| U~5_..".g8&q,2Z2 NG!,wu3& гR4_f˫U.?05:{rk}e<~ѝ}.ǀj& o9n5Y~<@#)Hy#XC47s.-d+,Nm $%jAN֪Ӆ]!]M1k*4q`UPT Ł.+߾ZBW.?[L q&m }XBH+AO*lK"%ƴ Dd~طqB7х  Tt4-2 sP@NYbB[44cͦaLN|e9mҠNb%M7MJҝ2TĊL4I[Nc6U4EfQM8u@VEia 'F79e]#5KxHGhI&E;?z 9$HzL ֛ŔqYX7},7fYFdg,}YH;(7c\֘9&7 O_smd#`iG-87?͕Kf_QX:~=(r%7ƤyK8VܷsE/2!eRtUj{IJ]ul^@nTؚ:D$G&K v̒^1;uUId܌c㰶+4^*/)YOOSObb䞽쒡@.njhTWg5nLCjW1x͖DJe;.q;5>/#eT疈뒈!Ni7o@>E l'>@ ׽ |2Pܔ:E2 8_H-P(sY3n,9oi Y6"2X|i@mkċS5{dD ڦBIAQ7D$ΨDFgŻ//׼\$u1BH1?1I]gSВ?J+b~j%2p. !j~/@y)PmxJeei* Vֻ䕮[ZXw¶&{`.F$JY[ 0򵽬%28$4ar/D-\#(!XH+;)L#B_ˑ锇V4_ '&f\*$PIjvM,}3"&WL3t2m3M3m&Y}lq׮[V .^6wͤ$"7pmi2RqQ@?Yh!$̕KKoX\4e:i`x<VO!:r2ɲ2(,E@vۦ`'g&9 y m5KGyn9bnq7~"^}2Nx^uig9Ԑ@IŊ"'[F{{Yxۧ(8#ř=K.[4ُ&zj\JSDzHcT(|ҊDR7aH/X_vb@q!POЫ'!㳌5ɡT5?si7v3y~Cr˫GO]ʢiģ GX{PmJ4bʀl'wNHIFuQ,Cy]߸Ƀnv !J4 E\Lxhoɍ +GCY^\;+& (C\gĩȀ>rs 7>gr*V -B6)m ~e}>)g. lȼڝmQZī@eߏ$xH<'jwE;\ɎT@;me/'>ƹI>[++UVdnAHxcWas| xe(zR눴S濯'.=lc/枷 q۪`S'fQn P R㘓)Rܩ\)Qa /Ofw |~ ֽ\q w|-MN8һ]va:+pH@j6at9.Ui>APzd2Btt^*Cvl$`fA`I/>[QZql^1D\^S[f_sNC@V=Mp!<*=WU]^* F)h1#߫W#p2^{u ӡωz0i9iPEw㑧:9*'#V5ۮ*-J߭p9p^+;{u09#":솨],\NӋ-Fz]+-vz*rmq2!6zcH.l:0j+S W.۝%z,iwJZ++qY\cVC[p*?,J\X*+W~  wqk!]-e}AǦ5@)\Ua^'0&5c㊭o\ 8Tf DKR|GɉKoe{tP" =ÂiЁcrI1f= q&?}9ZWcgM塠^[l閆BI:V`+TW$.qij e#Xz#U֍V?DQj){O&f/wp?8zq*J\Xz/M:#Ph0B64^w))_Y) g=_cskmA|\[7Jr{}YςP<_Ӓ$*򊢠^)T]ICj ?l3KWaJgK=FJwS:CNcR+4(9v6rc  >k|K,l̑T_WYL}GDG[=rz}#vn dƎI[Iו+ɛs7.8MBP2y~HbbnYx$Hozs><1ZpߓE(酊T ' ާ(Z^a *ZTx[cqGc1iK86rk%,MB?~w@ 'GQFXnn0#$xѥRG-*+ۧ̈㤺==ϾV!ٞո؃eVBR AIu;byPtS4D7+P#ۍ$'F Hn݂IѬHTN.F1i%aq(9Nɍ܌E/ F燫Ud!^q><k#viӁXa / FǏ^' ":O3+Ir!*UWnMcOȖM!/13 HFqF*>%eȻ(c7 ;=#[4\@ԁ5+9!, ?ʼ~j(@'F1*k{'gqPؗj-^*ah&Qǩ7eO2%T@S߰ qYl#?=W-UaPb owc( nfVB;} \!$fyT)Ɲ9~?|s#N9TE_}]2;T"?rY3φ6{(MTx \bY#ޔ*{,qAz J r]pM)7_?͔* Ў#4DFw`M>_U˩rPTm゘߬a^4޽c &3N#nr6!P2eᙒ%BקLXTKs6G?tLQB(`J쟞F>p(g+"ڵuX)f9 Xp[.%3YܒIfow%7e(=;M :,%*2S=q. ՄNە-|k*/eEE zf0]S#4$[($ypZ`vE6*>9 ;qn:| 6&:Û_yn&THž+/ª/?rj!eR)CPzk~<ɉIqpMmp HoTqN,=O'>$%F\DĤJ~Vn r hD`(?֧My@tutWꑓTs'mh7ٓ :XrݞIK3-PW#c-@LIP@&z~&)j҃}>d91$s(R(DmTm_p4vr>t i^=6fPPd SN=PWZtGnS^ѱ$1ۣ^rl2 Dy욏PmRiMV֞xS,$ PpU4kJLP^ءզn B^߯WVî}XZ,Sk}M#Av-+F9ud21?^:#NlrmCBősanM,ohXS&$ $zIJbI3㷎 2e|\/j *ӨUC,EE(1j!_Ӿb>DlZZ!]bU5=1b$dW \]q1XbAHҕ8k~cVV< )P)tWcȲ |} ()C%,m.@( U ~c\-s|8셭?փ*TŢn8sB? piBOӇABV,j*)CDB$2`8ĩ3q&Umx[b4K}bE/"ϩplw2I}};I3lXT8Fז_rL˃` ͑{H;{VaUxF(8DqdtL'WaӼbYc-s'rFv}!z\곟z?L`7O-^[2=BD4T>%f> K3PgRꤡ??fbjfCO`( ߰'%QX^QBŪ~T`nS_NKڌAc"vC*`Z׏.,nZmp;QAL vL$)1`n%ݗ5+Ouɺ2K%_BM@_V%a[*rɍV9.D"m̞1"ٮGm1D m$#S'<.caqOLN4--_E*tݸ+*83#ՅQL IF>%PՏ}I˴,ZY+B?749 N.j%rMs# WS bcj5v"`)QD|KK@jOm0hXq-kZQ-%HO57)d(O_JN\L[;(}e>7x턶BܢոTbE <}e1acK###"=.th(NJ2)lt Y]Nzaf*6U nߦH2 ,CSV|1ǭ%Cr801SϏ#OO2a KwV2WMĭj_D4 ,63]³9.kќq;zXз#ƞ8mnM%FY4hLX\FEnqؑl8"\zR:dH2 'fҴZOͳ3;Yص9k@>V+ޢȦ718; ۶B8и]JޘpK$CPP?ddApPdBw"p0-r@G"5(峭a3!TlGȼi'-y}£Ej N_QQv oF7ꌯ?6I(}y LN+d /4@'FY1O SnXb#U;l9EĻJT.M і[r R$S~ ,b"Ap͸w-#@LiǍ}m3E%#,Nx&R^.ڡ.;DŽDMY-9$ TIȹkBH@b)FE*=ԩa'@rvIt,͞{&gI 1205<[`=P2Fz~9%5[Vf7'+2݋+$Av @,Cdc3}SFЃT>&/.wfO %*ֵQYZ,>zqW(y,s+|r?|b赸Ob"x%ץON[qzmQFK^EƵZ8';󒔍6A)tR,tW}).4+ -1qFf+ˋ0!oY%ވʆ\rN]+Q[| H&{duAL,Fm]trJ\|m/TPƥWw[8ҍzuՐ@9דAOÖS ]]ӁNݹ).A]9 q$H;+@={5z*W|·'Y@M ᐜ!#1޻xf"_-Z=LL~C)=?ϮV\ Lt'n! rqi" }U¤Tb]\ipµ>+j.SJ|UQWSq8J=)9D 6ȗ+,f*qUܚ|r.l2tEqR{m˄/ )^̄KmHTA+ֹ YsmzOq2+P`bN? *5`{m]ClUr6bU=b+ORRDx䭬U-ev-]}*,ƕZmbB =L \N*!FSJ=| ScP{$ JK/Tn;SdJ]+TmJoL\LM;m)=R{PSvl⤺{#6R4J}d EyF$'qAR詩-F6ܓpaP}Ӧr BҴ푦!AS[4B*Hvl1E-Fl, Sm4e[{^-:~99}.@scEN;:DYYG^'!iyz6|r X&z~VU??e8$~Z#&\єYjA`x;qHѕ˻|`IǗhx<PSF d<|ceǒr`hU ]捩[(ۯ_\(`[8qU۪n,bؓk.`itM9xNgir*/3[j7)P()Aʟ*}˛q\&bbƋ|U5erMTCaYW?1f H,(vUɕC]1KYf> oWW%Q*Ec:pmi11v-#aOM^"cJUYZHҮU(r_9VQ쀷 LB yqO1DĕKXҲ0E'opsfMtH#WTb⵩?Gd8tnLzw0"X),8RJw6K`yPy'*y1-_.F,{-BN9W:'v{ d(]r@~rǸژߘ^}{X ޠ} YnYޤ.-zd4߾PtXX 吕n[5'Dr%EB *Vl xN"S\wޕŽ$^0$;Sʲ !ٓ\ym23`u•K5Z43 ђ{bpr6a amy9#?rӳU! 6O}9T4$14һV$6n[9Xg4)TfW2Y%*[f?cl}ڳHQcz+zWukψD@[:zV<dݯ0aQϦAΦ,Xc'SO.Dƒ3 Wf7QWJcW ܤ>žNESC @C pIcSr.oM֟1%hGʦ9 &G4* O`@qQ;kXS.+$܀@gܵl7*lY5)4eN"\i5q^S:\ƙqVX W ~-< f(Ԡ=\1c1{#b4PTh qw|Cyli"];t=[,$rAp$KHqVzj:{ U WzK`>AWVkJR~|p2ިl8T^=I;acnxA¨T/|Y4䭤ZJ>LmA'qcJk5EL*]0JDG'Cpz86j X7 kOBd d˺mp[HVS"Or[N; Do*Nط)Oxt6JK'qL[8Kmއ3;F)HUOA IRRNZ+r\KK0ONh08[!l6ㆋ ce$SXkɿEf<KFaq]lLpOswylz`WepؚqHcJmx êb$Hxk^C*)!me+pgXR(CbiEĭZ(]Tm&>VȨ "9!Ɇٝ iwbg*e&m";?~Vd_t~7|;?SQKo`@o6V* 囃LL4M?Þ Y wG 1RaM/[ebҕEZp7e$,4v BCrN<zAdQmNbQnQ@IR~,eq17A"+(;"IkIQAD"RڙY-DW!/5*KC &JdfIZ NK86˖`Mj? ǔm#_e;c) ZIr&fHoX=ז&of4,)#6[8}oh\=p̊~h<6 l/[gpq=i$",`;6W Duq&:.68aQ$|`H}?r1$rr@m95j1ͨLu n">.|bm<#CnonF)hxb? ]YN/,G'nT^ʕ1:F9@LMsk#q<7ߡ8Ff2jRh.r^8jRNK N ~YLL2ZF6)rcLR2=?Q[p$(Z CJxш-dTժ;턕tZiݢHTre vȖ$ft;D!đgAU^-u%+u9TxNkKG"rHPk@~o" !*sMɹ(v"f?”*O_)dطwe΅M(zʁ+x\nA Њ7Jw0@dhĦ}NL;>KHo0u0#vu⥋/1ZUoߵ- r lUqa3&gWҲܪhAO|LHR*j+%"%To9 H^H|AC ΣN=,"K вJ+$Z7 dqWV)yi@9t|Gw'LE "wv[M+O&޳|]j,Yp_$%LxW_¼&dx]S!uS/r&=K X<2Mq$*@USB+ҀG)dI. y: bl(GjsU8&<,GEk4'*}>%}E$|?ے~!a鴒R[ eAP@|}k0n]nld]kLhH8rn~MC]x#,@,H'Tφ0iv~ p7#%SůyHɦzpR{myxĥx]~X=PO6Ei* y1R>]= 8}AWU>$FQfpT-#| Qad؂Wjf4\)"]SƬ{sp%͖PJ2)FF(; jAS\ 6(4q$УRn*MBÓOU.⵫RSN3]IOh !+׵/kq3(ٽ*?˔g q66?JAu8Р7.OOp__lFÁ)A4pa"|R&P`6oHy%r\lӲp0russLU'ak_LʝI,v0␚ZP7ONsOb\B\W_:%e9W}O)"8PZFSss''IVB!o㪯%ZP4? ^؁ԹQ*ZƢj6?..;dp*pĚJg/=3Iֺ)`VN10Di{NUɱj 1%UKZ ьv4ۨZiN$VI'+#<NbR\ (|<225->TTcT*v㍠➤9uqF4ATОv/rorX™cF/s9c4t <0iLi.-[0Iot^=oՑFUVj~9yacz\.tNxބm"M}{bE,Xo(uiWqWWp ֢P8)pz)onv*႙J0!ȆDB ӱaT=H1F3tcZ4G"ggyۡm%/SӧӾL$vv))7!%iL*6 ޔ1dH\ Q %E֘'4bgSbM06M UYwXҾ!-F4ۦӄ(m%xT&QG$PmOs0`})ɴ\YR_oiP>_vr8Y}SE * !C3v)-$o`g)0ʤe24L`/+\\ݘ 2B-A HTxVy=&vXRˏ9Tg;=wʚrkC%62M}~[zkFG'K>ޯWi*(A=$fyqOwfk6YW\:$6)~9$oZ=6x5PzN yaަ9h87<4n.}q<\-`l%Z.? VV\'zm>QZefصq Uto |_vm+ v`Re{?r[ZWQjُ"CjAlT"bوzK?Sn~ Ozv%_[Fd#2.k% )ud ZXvуS#c1!ǖHai2\QX% c%wEHdg}InjЃa[Djr(J\p[`XR6ݖ[ɀh p[8pS"+!LIy0\3Hk_~3"[rJ$mC 9BIө }9YݦYcԴ1Q2䉺iQ iF(j| IA_PB#9;zdtOkhϨ$QX,2*(^G4Ւ"Z-=vY(@x1 .JW~`N oXeɽ1\YOQV眷, I +Ur`8_QroR lHrpJT FJ8Kc}Aс'l(QUd@y & 5b1>4T0jU(0x5AsfvNK2 hʧjw'0K->hRo߱9P4lWDn@*HVEqDnQtUFq*y;_, ?v$koR?vRVJ,Y`CCZ7ɨaŪY*8%:S//2$xŏm.L7\Nn S[QHqZ"2zT֯D֑<&=IZ~xYQaLЏaWL6אnTǀ 7q.,:;34,TOXaYEus;($XN(~v5ey7.N;Ȑw?})t iW,$`P8a,|=&(BO8< A&?Dm h}hP2C{xD P4R!$Sƹ3Ny0[rO!stڜtvRP~|ܸIVN#R3&nOŹDG?lE n@{.&T˓0Y7IR'oi|U =m `E%zp5Ƙ./N Z;|.4iqd\ ~$Ft-iFFi@hu ( ˜Z֏\ +\UܫqK늯h+9>4R;Wzdi3xX2z"C D$Ԡփr)tZJiPk˽zxd\ёotM[vSˎPè$Aڝ6mi#~ [90YZa+aHjEcRz-j;W'+ ^$=ō/?<+J{bBoA׮ k!w0l pc)Md՘X\ 6z(4r*UuqJO)Yݍj7҇ʃ--1*(@$-R|ʀF,U]rCT\[BܚlQxXi q Շ?gG^(F@)p4 r?|o-JQI{vP!,Hnk= !Rw1DhIי.&{ndfddo!/ qCƵV^Fu+:a!1T\&d#ĞC5+AXĎmS! Is',x[-O?Lvc\_?":ݨ's64RTS|]E mT\iF a rUN\ӽ{c"[ YH`J !mӔȴȢpGk)jEU{wn9қ'vT&BO;fT [Qx2` 3CMnsFMhX6x-~.;9`d ״5g<MmY2%yҋA@ޕb{6mJV}_jAK4t \8s2 -UyXV -j7/rPE,\Yw:)ޕ-Nl{M"&.-BBY£f4<@r)C)ѵ&׬% n>ꕋ+sg!jxXVu9 sh{{? ȤXX^8iuZza-Q {~*6"z u I-$œ (aѻ# MI_@铹ޠסOrieh:Z Np,JlӉWt' CpiG Nvxf-uӆʎ1G{hAkw.ѳZ@i7~'ʉ$-.`zX٠܅ ͖~ 5=:t22/VۯŐ2b<@ѧM ]&\RpM)햊`$N I"d1ʤ׾)D [9ڥ!&T%OG9YKlE{8Dv\N_kA듾sUV_ 9!zȬ6Ҩ1 YvWRa1ͮAG[բ>1"'pO17t`!d v'-bBk߯94+z` 4 lx:m^ *7zl:hm@.h4jōHB~ZT1 L)Š6O;13UsnՎ*36Yi>M]fvKO[Q\ω8%θ},'Ja ^*b]cz#Z(j i֑]z  -p H/{+ * k?kI0lPᄔQ^j}[Xb$23 :3-) sj gR>&#E.dH<3cvUh((telwCF;Ӄ&-ca,SQ'4Drhx{zt߹]2$ ȴe?_rB(g PP5mej}fw9\ٷ)ds%[N9hy(N:p cc_$&2dTv29So9is.rߦ'q%A%H4q ch'FFo#"e/Wԅ&gN*[P7Qn?g#n#ڛGccQs>4&{[FRqu@ 3,vR'2'5gg7< Ʌ r-A `_?'Mv Aڙ5blqφc⑴HAϩy*,oKԧ#N?>?o_ I7(x Rb# vb'eU*Ǧ{)[kLD f[pF>ds5 |d(>~?e:Nx\>-8`O!ӟ?ʜ7'qIts8i0u;\1gH9*Aׯ`gHIeZRI5nxPTEz5Ϯ-eFB @\X!ޞĕ'|R&Z9PȬ9璶\NRhJosdEBcOOp[Dk4)fB0%*Lji=с[)Jq\4ȒYQNsOo5bUk~}X8BS)o)ybAer$49eUK=6gyx`ߋdhФQGdGX{^]F9Iln[BG#|9TMWI2sB(Su|(;#Wї.kה,Frhqe9:?KI/K0םt0M*W&ؕW+9j;& Ϯh1#lLe@< l~=*-JQޠtJvlJ]:ݷu4Teq&'l5rvYFDnKh7~[82(L\1AU!oi0w"hbƮۉ#2,k%W&0XOu\z82Sq877*v >YN"1۫hCfȦRy05\;7V 5elܒҵ>N ( IrWQQ:G%r~x)mxNdvLaҙZ>o-([IJRUQ!("({Sq]5]/Uiz{OpBYk+Zh6’-!OX[JIB)o{ڋę: LR|44~9h6I|+_v?2#+[{(G)|63==,؎׾>+h?ݝw8xI?l@hҨcfQظ3 W?R?>7r0f0!ZPC9WQ"Pz޿͔dC/8] &X%Vǫ겑Ϲ_ݟ, oG0aQP6ǯn_cJ:]tMl69"7tᆜUe{iݺtz!cf ~dowev;bC")^d$ˆq1i\4g!DƇ?oˉڜ*+dl"Y=4-ܒ JdJk( kwbޢJydof#fEgNҀ~2*qcqDy#fى qWH_EdoL+ƽ/9E=/W{u{Wb ֕7Y<|NQ!^iz\K#E}\.LW(UFl0An8l8rϑ~ 8 mrHޘ+_$@;S|2,K߾"; 8ZUŅRHj00!U8FʼnN W4)ZPp@*6?^XyP slzpy7Z9#[ldIӍ . ]4TZRN@9hFث HUUx JRCW;Lĩ^;"Ct2R-e4m!̈́Ue FG\ݶ#'iŞNwn?,iu'jxeMW䭝|w\4rVF#+>=FRN(ز l^Y4-wbDžkLG 8ṬƧ6(i#rj14|{E nH@F]tRGl[YlzSm6ǭi޿*nT$mq)4mAݨړ•8ڝB](N,yJ„LUQR{mԩ3SlIS /sQo64vn o7Ȃ[}rt\$-O@>/nʙ,Z D pxpHɗyd- O4> ?L`GsO$Ziͬyj4ƧIxrUs^usg/Oxeȣ_JON"߫0&Q1}>J$WIeoGE*M+R!PQ`9O/]r2ھ~=]; A`$P- WQ`lڝP-e@qPk9/f }S_V^}a1řeI[komEշ~0WK&c z̲&ve|4QIo45:K[s[mt:g^yyOQ[үծ⩕RUyqKy"Oʽ3Rf]GX&J-wkgnߡe( νG0U/G?7ɞildQWG,I⹅$2-F?nS3;tY)ؓ2щzmTqkiR5bEi֘XKԍ˒m9|! E^=iN,Jg br: X-3ڙ0:XeF-&*z(~G 9lwZWj!jĜ}@@a$[A] QMܣ_qĎe&*;A@*Oneć<P71]sCvd]iŠei}W* 59)r]趒[([Qky=)GƴIgi3g rB I4$ \¢&P7ZG;ediDͨp14aqƈ! zdǥ~JNoLTj{dN+Uܞ+Qi,(IjйtmuB?U(%6x^ <V@45l@ vart J? xƎ5 q_Ę0 N:ˌs0E(&۶bS;cm\;"t勑¿ae,L])SԓҙQl)Ӣ?E) HmIzod~PH, زkF/2M?TW+@32ϯT\~l{)H3|JÏ#DZW(d)|I%@3ll~U<˞SSff7CdeFHlӿ+lb*Y~l0H!ԬAFԃ6' o0Y EN1:e.6P$0>[aٹ\'"'C@~-]%ҁ1Ch\K7C8+;kq dOY6GzG l|Y#ndmL%F{x7$䨍bgō4ѨAD=?3LkbKI ^ g8@ܼPSs {j}OH߾~q?%HDj6PY7i*P@پ+4m 9 Kl=+؍^oarI$'Be 2^pƼ 2(&jnKT=ʃ#ݎ\[erXۻ;$ґJnEI⋔_7Gӑ@BQy"-%[M5ʆą#4Z+_wy;I;Vh ~MC :Đ$5 n*?klFFXI}j65p%jmbd$-3(ޤaXxe~ d ). >#M!ˆP傜&pjD &}S"9[]LV'r@1Mcvj+f kop}1>x0BȀtvE1fS۽7\RԁFL J Y9ɩ\prw߯\{PP"#O Yp)ZtYN%M 4ҵ&K)ָ(zo~ ;Ⱦ(81-EggSQ 7tfem2ޑǔIhoΛ'h'/a )Γ= 5gr.|,'ĹX5?ʓ4wyRţ?vyf2pJcL2 Sʩw$Q9(T#5x ÚQ mЮbFY/vwaɐH_eEB܆忁YP6Vj?+cYB*(1IiPZ ;xtƎ;P l\֞%h8"d?Ш}2v%))ybX Ri7R/~s;S2,0SYI߳r`}P3=6H4xV#Ngӝ}])S0mO @pOۿWr#=̔(={xY k":,\?k"%±65GSg EW4TG( ]A+'uImӸ Ԏ'!YtGOU1J,G e5xN3[#6m$ylbCu&tIU[n!(zGkfBLἧSc?f, (P (2К"\,ba Nw 1TM[brldcej2FHHD9h-H69|[BIOb2p)/#q@>㇌Hϥ ITAœ}JMsdlXFQpn@{rͼ"b7`eėjhv[ޜFZQ̜T$ 8lmfEF(+ğ͎]7,wW%qԯOqfV(Pa8TڴL5uU6 x:@ev)$ړ`ҵ3-OG*7K5 `~ۥ)pFt,Lees^/`jF**kc Re$ՙآ)|M]k܊eD6*E4~xṀaW8y|=@,*DLg&P#qN#NA p*:sj"ŕď*> VYC8ZU`)]ʎLrҜʲ#zxEK*O4뀴s BbP܂xI`B.Xb==2MdZIubb$'c۠ɉ8(,il$1UFmUpS8s0T־eXw| "YNjW9miC+0judG6dbXϦ?k 3EIsAMi` 90!>Sk?f46qiG N9sYҸQ@:{1;lm5ӥ ~'Z/q/RƟ%ՕB͠`-(ѝwVDS8STprRb+׌${., 3uos(WP(RO#<3>2ȪCkYͳs-hhEv?x2bHH;f(u91QB'a [rqR*+`v8f1x+)B)L=H7?{Yx)PXF\&Ѵ#ED|T;OYmsA%ҳ,o*ño_CP1vKB.8 1b1_8p59hc4C:GlZ$GݕbZ+>cU +_ڠ+3rA"#zS0JǦcQ#}eԪ|n@eʒ#: gZEV(Hc\5HRZ*p\.嘚[ @^Yp{Gػ}͔vie9]fa8G.8(ّ~O93܅rE\X*W)"*\r F*<20.}>ͷxH rؗLBw܁L63f P}Kl 7t+_/MiZRr\?*O__Qt$ iZOo)koaf~0'qG[jL$ Od1 k dIG&rerd慂ł#L06#OIb@(m }?Y 9rY459)IKz|$N3&C:9#66&%U~JGFJDƏ4.J(lu&"P;mץr@8G<01,`r՗)2եJ,hƿ+BqrޥiƟܹaHj=V!SSᓑY?эf(9>9DW}ωUIڃкJ>/ pzuNz S Q1Nr||ZQp+ǫ ZU=G\-|*5~XXSLzT^(! -C@~xJCu큉 -ގ & HOjdiƞ&=Wkܓ\i-0bxGY/y9q'?~?(i:Uqnah$bwo \s [UGll9Kڐ$ uܔ=bzO?{&~*ӓ[LtG(@*i7`|W4&`G!0"h#T}U,F̭pJȕ߯+Ézv&Km#ċoj(6{Ď&B-TבL!.'b}O54W )rEpK>+0[YƸmSAdzؑdLE{X\ d$)\t32ctG-$,aUuCr`dt&KtןdEa#XP+'PR}?;)ERo[Ƒ9(il^h@P+Cq0*,E(?VVd]BLDքxP;*z*E+2C0E?(ŪBZ\p mB<*һw톺d4* >=2lj %Lb(9ˢrPZ)ROwef`ªUAjZe}>fb*{/&$^ATQ7Q٢1552QQdvyv}{$({";lk%n1]rjN8h:츨0(*Hiuы7G*0ڇf1Vf}J%Q0kyM)a$S S lqn֝?ƌIHo'<+GgepɔQIuW@ ۱3[1!5ɶ1X{{ndqMF6xnq9uG"h@>_2+ %rJd$$M&Vvb*5vYhx%z|r0m_s:ʧ GEA }勋jeO?桀fzrUMA5"w9DMl5|`%-(jTk! C*\Q(OW\gt57ML"$t6BNHc M/^f<.aޙGp +$ '~<(hekjJXP=pq[82{4qAGRw)OY~a.4L}$)-ACh.C# Ɓՠ̐ l|5iG&7vEJr똳{)Ka,$@?R!#le\NPwޣ}+|gh[pÐ;4$/tݕNQZ*;(Ɋ x)<&Ȓ߄Ч<5^)˨" 4HB% \rfqLE1#U]AẁC`iCPG*Aa>,5KV꼣iנ$ǎZq#YAѩ^U>Y3P-bPHAjx8mY\Ν87{e4QJyv: 'wC`Q;acH-n\ݎa2(. O/t4)Ff(]_(JKNKTmU~?8m3yťֽF\iQ$YS\.F3`wyG6fV#1u.أQ)Q e}Uu$}>Y-ZU('e🱍82HP(!Q$wXr\k6+wynQG"|s%r+^i wv1OO1rH$d;r44zW񩵝*@޻{ekĤvHC\NL!BHAoM1\~*r6YQQܑ@v4p>ZU|=&*qNH*po XZ o ZlPî,K4 #Ƙ^i;ȸw-jz| 荹+Jo2.&(Le?ц3O^N5˰\6bqUէL (#)^ YaӾ gī`ѓܷ #NDrv$_-SKw-^0C39+mh҇sᒶ\KJn獶) wМ[ACH,Ḅm0PiU7ŒJ_@\-w}fOOWHc´SOFVCDcKu=vȸ9#8rO1b*.Bn^/Ɠ/q2Ô_J\&kVDb=)"Ϗ xfezL2DLmuM2%u"o_gDm-z#s2"F8e? om  3j8>?ҋu+ťRaJ?r "kd[aO~ LG~/?̊'?FMq$bi(r_4q"Rۉ$[%hPĎNVdZSfȢY-|k2UEZ|@S#h\z(pPkj@#@M:a1Q%Ozeă9l`SLR2(GD2XKY**Sa. qMwT4u9h,-$aE׃0w9Œ鋓w͊Yq=K؝$C);:WJY>5e,lPz2nds_eFv)7a1Xhs.HztKW<]p@Lm"SCݣDנ'¿<. 8")ƪiC2FDLJ(ȍ27LɈe+1fۓAے"](C?h2[rdJ6QoŒB <[ڹ៪ ڲ?dbkssw\-҉)N K*J&,8҉QIT5RXstCգ1iy+9wmDl..a9!ۯv)ͤHᏧ̀&e(BFę2iOp5ajO`+J\;IM48ϦY1\)c~!WYF2K!4#;fXHK9.9cM|4#n)ِoE-xWw / 8*X{ArPKro¨rJt([_⴫PG,xASIvS/IyP??\).#J44;n =@sqߩ)ECN-dru n?S(d- ZU'AdX׿٧X2;&6.屦;9nXԞtǎ\@gpd m!3Oծ,޴81~OI]Mܡ?>]:+UZ~gd*#p (hxJ9ƿȿmԡzJL9&Ŗ*e+x,H_rNڽ"C)oz`$L[ l$îVM!$blbUqoۯ#:Ol"c$Eɴ3AkAZv mfGۺ-pZ#~噘cq[:~$+rY#`j ӨRpŽ{hj{eCrSQЊ ͈sGKs"g(cS^5FHdI7I~ 6vj\V,zz4k7 %?@74$?$備 )Į$6%[Rz{ W"آ<6 XPoZt-^@ԁǷ;)z%$<\ d"Mّ&)-֌*{Iޔ^zR ,=zB!:Oi-Θ;}ہ,D ݐЃQmf$)p=0hZ~^ء)_^z1UDzmRL["S7;wx&6T`*)=%;M/h2'7mgd' "9*vOl-jn:$ CbHgZ_-Z%חJ|[A\ NR| F.5;|ZNuJ2,l Jϴ*.TM89 ,w]%юm6d.O}x}K[K-Nr XDAYeEEbUfy<6&) \*ʻr7C1Ly{@8ƔPY7 vʸur&HE#q}Lʚ5؜;}_ͻfW2<ң707$Rr2b!!Jb (+DLn΄~Yl8GisZN859jI_eoDv&-eTLNa?ݐ]^ڃH!Y\ 㝴e89-ЖX#v`8&$8jVhqdbwlFDǸnka2sN\ve(;*eQȭ],e'"x){ʁ ׷^>;#uLwjI)Ž9Cm_4LAHXl8nw,Czx͸=2@ITU3R '!"\[E%y N=REEnlXℿkϕ8]w? a0;/9`F8wFaiJdgTI\7%fl ).m2DrSQǀĝG%GiX68l-R0BE[p8׈w_6L6a^сӒ2 8TmVoVZZ#£ l$'L-(698]B(֦jDdaƅj@c^0.<([iVF )B:*/#ɲ@ZO8y&߲8"| +7,rqNK~mu5cբS#N OeBTx|+kPxasp^a懁'+fcە2.|FFh/NݻdI ]F-AR=&6ޑ3D6;!N^xKqf^½FyNu*r9lJ(6/퀗7G{DNE:6d;&#m",*~%Og2q-}ྲྀ:jVD 0+_M౞y1 JDTr@9.!P/#97&&2$Rb\BabKq'1z)4v6?S dn]q<2O4n,r SJ)z:=Mt;2 䈷*r3 BЊnBWoU~mr6qlP<' #QBW=8!gָA53K;P=Jx#Yojdh9$BdC^-vÍ:WOco,nc^u0Ol=EVvG//s' IpHWEMۉYE }ojfGGZ5ls"{Jʌ4!MZt2QqC+QJ ۫lH =&]eF*G 1M+_|m%K^6GSTq,앍G j`H LS`kT+^[ !pp]6#li8 c*V_|BQs#V̸`${$tD7\֊EڙY, )Q-d^FFmqԡ}9`-[^MH-73- *Cc {Tf9,Q(b+mc4߶RKOT !&ɉ2 >.r+miݸD?,G~lpe&A 4Br>ql1k9pŌ\Wڧ T62Fŕd_C$U|h\W%,o!$%)(J:"ȿkW9-^"_LQ-O8"~-NJW12N%quܱf+G^:?9ldoˈ<*v8,GXo^u¸lz"c{Ԗ-bj~ܤ"Se&:|Yqy KucDf0Sw~99b8NJn˴b/af<$xpy7. =ɌWk2Z)$fK Q6"iaG錥7f2mhuIȱI4j3h2#HYfk<&4ooi?Ik4sLc; C18Xt8L%LAAJ!  OA`;56=nc:/ɪRT'+h&R;O HMb| Kns5tMbKx:\ɥ] n[T~,q2YnymZp|_J=^L&TG(]uqC(ml,1ߦ}Li)J-D տ˘`-wk 2:V;2MdE*QjI٧ˎwӐ|J1. tڎ1GDrVlGm86*cBFL*N_5oJrߨdU&vRI[da@"Md"J LI [1W)gnKDQ7R̊`G`AL@ ϖt rqVju)/pF##ܧi]R^'̀c]c QfZo6CBm("ЮjW9.}:doCAJO"H=8´bOCؒ~8Pub݌GC>rm)t.N.\FPZ0ersH2.^zjfv#aΌl&," R+ ZI}j됙.*j0,h{`[L"tǏĐM^[{>lAb1zm[W; d!GTN2˃p#.\|ZuS-|hJ+&a&f"J!Ji^bvFe{]BvrS8"GkpPҙ1*^2RWƾ9a6XPth=$!M|D rW n1h!9XӇP0 .Z0F l  ndb4r+PbVGRgsj:ugbj{|MV/2jM&qqQ@UFC ij10DES*FbZdCN1Șml{a?6Q"05j6KaMmh28ܕ0WVQK'.Tt&M{!i\$J_%rmƜ2r,b$#nm>XgIR@4aZ.\iey?}/n$Z Y)=QDų %zlqlAa^ŸM -FŴM.Gng ωB[3S71%zmRv?V#aUX܊ʼn cZm,+ARj}cJhCY %GLX R{as޸ -6 L@\r\wJ})Kj6g1 EDoqq6.Q>[+HcD›s%vs&NDn1?^Vdz2EEO.Q-d2Qt)2*D-jB+ak^J&+tqFO_+2-$r (74~jQ$:l CFGOQ{ k;| D)460Nao7FqT{` !gmF»mN-` xn@Jp@^c*"KJ:~"*w Q ~D5HӦE lY)GÓ !aE P-)5B3ԁ6 ?kW$lt9L%xeDHZtm)hE[kj&=VhKavˣdR;^h5ޞflh7l56Y<  ?6]Lw˺\k5%Dvi?誋O\o$ <1/eilvS[n8:[*'|O/Xpd ݎ)ŹoR3卭P:  $HQ }g:j5_.0(xym*Kl߈rND&\ыVr]Zc&]F 31z}FŠkrȋi.g Et;3rmrC|I$W~!qY7: J^-㕘1Gh6zRJn$# D#/+ SOV-nӯn? ogPc6F0*utYA\l)6H+˩ ",4)Ě&\tnꐺISa`yMс=7xh`Bekw:KL6rM{u=I3niU^t5)HnŊ/A둦2_ک$nǠ 8 )ѐMh"+F yro,{YF3 nA$щ'8bhl2VE{ٙ KukI8oʙL@V:fJU2A*$T|p;aZ9ذ+a=8g4-":5>} %*VS"m'l݊,~"'HMM}wޭ xdWR7 &d#+%ӝ8'a6j2&B Qӕ+N xȖ-0nX*`yv[H tǦ,ct7 "-PճaZSw[zPP+n840!BC3 v̇.2;k軺7,Y8ŷ@7NMKel֓9W5))˸9HX<|>嘤$Z}r][E[ Y##t0 ;flVFd*jOĿ $L JPƇ1-\@K[EJ\G32"2O7˨ZM6wq\]jJ-[ِ t4;HQMx+>Yf6 Ƀaz7R^Mȴ! UUW,Vx$7X.$(H-lXdɸ9Uu%y;W?N1q5u hCe7e wuި^.QX~XiI%K8*@JdLAc,\ڵGM2`ׯQ7 Ӹ"A1ʾ90l:츪hvŁ yKҠ4?niXL.|. V#~ AӮ8X!3־t%l@ۘ+zm.-A*7ɯL-$ab66$FRTpMj;]eE2uYAզ`r&@FD WCMX0HJx,8l╅UMK 2N"RCj?D*CSR_eYdIpkb6pg֨ Tq{XRA l T~آ;lG^;7qa$ ִ  R/cw>ߎD)ކڧ*>Xy1ǯt2j41pgK1ҿ.狇}awO?=r;KeU/n eI]#nHINrӅطpN#v`"`U!Pj"dQp7'ʋT,HESVVH¢+٨@$"ietT[w$zud:12W8-+4Ws5%8)D 3rO(}1H*1Ug]D8[P]d '-qIRG^DYɌ-o B0mY,\(6qG2MlēQL |MEqSttÎ!0Wm"m\)/@aGf?!n6sa|!;"#FЯjed 5aS(-eQ(;Y^PidChYe0R5*Zc3*oK=;BoRn4D '$y't)--sc@K)'ҿa0pAhzsffX?Ei&hr\;-K(#B '.6p+K4NK<"yU1H 3,R2Z+boqz~R%O«wOf)pP zHwW?žGhB|B>'U3eF8iN~)6ƐRʿ䑓k1-ꬭ 2TCW{"h㓔dҴrQkɘ\xgc+u? l֍0"%>9i?Q)Pr li2Xlzx`Qޙ `+R iPj2Iqhq"֕|3+eMZ+{8c]O,Ʉx_5,̗ZMͬ]%J?4">\: 0 $~Gb+q}4psjd/"TQ|ygD9=T0Ӽ49'D `E?PV>1"<$/!tq(IiSWr^? 3)ֵ\I}}W ҿb:U!KC J8V HH⇗ ͕^)/^+^$@CS^V9wtg2wDI$p:$ ʫOcCW =Yd`UG\4PInF>R)iPOU6 *O|$ǪDEbzr@#ԎA°=@ E*4kfDuNX PXBke〚w:8ܟw1oP6;:(uV#GnFV\ef"RHd^Rz_gN AmP b]PC=bœ:S"Ζ]YzV-?%,fPGtl҃ė,ݏїU[SƘl5G%=M 0=^^!otl vh!p-UIk5\9 حOn2A`tl0:[!3ѭA$T'Gdp*|_S}=XoӑE ֿkcC*KoA4fZzxS%r5$ONUkZ@p,suAW>(J8)6ݹGCe-%Ž/&$Ղt9h6ˮ` PjTWcR#$n)gßlum;+fp$u^EW`(J092Ħ_~;(M,Ȩۏ[ns_y/8٩@2s29# -g6g#VejOLI'$!FgPIeBOCF$/pyw_#N,Pv^IR@ժ؎} =X-r=z0^GVy>nC~(%?`KRS_~0v> "Վ&YSՍNR.ǵA/}ZLCrN)DQTeq/|yf;KިR%[䤑$j~2WMQt# ԓ(W5>g ie"^i#f M\<L4%P9;6J^\$H2r'gkoPF+ڽy`2#c}6?ewYJO5[ڀ- PG3e2 8{؟ ]U=*\@S裾 ٔ vib=Gri$cȨ$rFȄuNZԷї e59h. e1Ukmx1[i|R C]Ӌ0T[s[hKA߾׵qnAKk^Ԩp6FHg4ĠmMϩŕ44>'ȭ Bz1JĝlXSLT0\jWzwʼn ĥFXTo@~灪jƩNX9;Bh}1Op5Sw l+B >^p1l^h\zv6N]i4O6K -jj;b>)*$:wŐTYƞd46 )6ŕZVR-.j7 nqPh;xeں܇5pS2?.p }u ttz>ƿK`c^dp'ƿ>yY+rbODޚ#|o*MV4 .EU?_|t17`B&"OTZ'}ʹ78TY T१5N"+´#$KJBNrule\1ά|qrm;@Zv5h?˱iCYe\ӘV6?20%pܳ+KQ)BDJ >=3[91kWFZw oqͦK"?/Hu+khIZ&+P?2^FdE鳭(sG<;0"OӘePj6*DZ2QZD_ 02Ruɂ_ˢ[IdTV3* PS|%m1:fѓÚ82*ҵc 1RpX L޳ɤԾdco?gR1qb-A=DӐ=zq3 WNBw* X}r2*?oC$~tlWiUibGXwVziO&<h5ܖ\ vi9eǏő300b-u#uIeC?1&pܿ̇h E7,o~- ";Hiq;yZT_|Z"bm=[/Pi$Vl,#b??9I'cd*d&6rfcv=0q77f^9?-k;_TnDA}I O8듍eV߯+hV[@UOt;tdwHC, _o\WtQ}b%BZ5kՅ<)l}ȩ26$_D[ H3#ZBdQP蒷90iGOHv F.4'VcM=]=N_&Fr ?S 7pq`Eo!H:`>!#!{4j1X'cܥU5'm|9jX#JS屍~7:WoZDh >k&rgLUd O2\Rߍn xxe Me)"*ukBTB%e*T _v89*L $.׾ ^\?ve§S̚opZAk+U Wܞ#.#A8ͱ2y'&|n'6:mɆ1VX޻5!'(:QCW|j%LVRn(OG1d>PYyzZxA(c҅ON&9%rGDEn_FzOUIBK[ǍEAÄc,,'n#Y9ROJip@ڙ+T1:kOmg4csQj)w阒 G-T<ʣdNC9( ƻb(D@Be/x峑k džJ[2"Х2ZF d$BM:d0!UxYW"ȩdL*9&cd< ,ݡme@ӕߣEs4bb G<.d%㺼eISbdnFvX rmSMlqq&qEh+Ԧ]N%Z4lMnELUScIj|13r;?4ϤdF%.'HIJ. s_K+n_˘'Uf_/?rN/.gYxW}~__v}Xs"F_-@ LqoYAb~wIfٰ-8$w=k.eZR^J* ;R9r*[')\KnL?L1J Ve`B##IחzNł;KIM<}f4Ȇ`.N3R7q$槦j3Em?\[8Lvۄ,"dkDdߚ v*FQ<yz\6\A5 7Im>&[ uE]ݥ_ŔF#7L..*NCוio_Qc\'?_84z֧5iW5UZAK^k! p_;]* jovP...2 "\)E}pSbS=Ued=v]j8OQh kS3E[C|o+B~.Lv(p)ZuǯoćxفQƋ œo_.7-^5BI^?*[,"3K-7LBtj~ab/_ /տJƌdHRDS(H݀'&Z&v 0aPx//$"lnN imS]^@ qtpJR20Ӧ@Bʠ| T +ƼV`oh,া U/pOЊ}1##g]ެZٰ5̜[kyjrlF~8G8~gzymfK(9m"1,+򺗋z̼rRXvnO&qXbFY=Yv-@j0Vd1Mt^q]1xX7 O*4d~%p1GjS8jF#*(F?-^8p>߇Զۗfɜql}mFcGDn`%S[at\ޗK$8#Pz|kÇYܸBWEiz B\۱؍ bc7#Ȑ6j?,UGв##hH jO˄KnLs ܫJ).bVS1KV9E ,$JJ,K$e$On Pixw-_ͿZ6E!#pS䑏$}jξU On9ZYK$7Dr5>%俵q9>H@؟[ÓnZRȦ`FZ+ҸliMz hA:aq*pII4ly"2_V遳w!v<;6ñU Ѩ c4/ʏ6sVp{hVŇFռr%qi}AWodr^쇓mN/A:ʼn"n*(f)]oe{#пbq2G\Xd`ӥ1 h Q_p\}r2) ln}Z5 EV@ o2$$ wwb|P;+陋E')[yqEz_ݏ=i,xǴ1HS'; #}96;v7q>lىWb6l&ų+S€#Z/&Zja{ q~t*#}EqJRŒ51[X׭0$)Aj$޸ ?AżI 5AJlBInA&$}=lBZ>'EiG"ĔHZIZc=]m•>T4ʕj7L,xWUjp1Tl~zr!+49%Wx|L(OP)_lnE.p̀䆖NDӧBT"CZm _#Š]Spv¢6T'ΐyCh2p.uS!z{eޞjc._V/ZV4!?30ixג\1lh$~\Մ7W歩ܼx.gqJ 8?䳷FaK嵀 W'm7]6^AOSԤ}F 7GH,:'ӏM"Ө9iX )am\-EGLbjL ,n:fN7&;qSt cME d-1E:eD WPW:!" Ph}lBQN0hErb`˸z\<] RMyOrسy桥\#C֙ RT&Hv SiUmMt2eo'Hb~05U[&sF5g])iphz JdI%+RsS_5:<.M.кLzP]IvEHoK@K610eDZhKpsbge>NO1%Y+GOa2S5+2`d聰dmLjr@ֈnRӮ[\ʅ. (PVXO2O y&&O1*N0}2*vR_dS>QJ]jy6 MN)Z(r l9o$e16j͎(>^<YF>>_yre&~RmwKjۆ&J(MEVErgߚIpE\lqyu+tt%JFG/<2D:CbjG\#©Z E.9RuEodgK Ekuo?eg?d ?g 9tyDl"ˎY`e$lO޽-d"=Djb|,7@@S ć IEqH4MSLE7L\v@˧oJB4)&Êgj>6JxQz@ PxɖE-j~ƖSz #Voږ7С p20ʎqIKW]BEi৤d_#y|?ʲUEmKmw3Ġ楀 O(p 6GIPd<űdgic jvm 'kE-^2 *"X+̍nوP˺+X[iJ3l+}.J JN.\ h~^M<~ d<ۊg K4?4>t[1Hӟ餵`_n~N.{l2;G 憣)d8!V2BK!n?2lw  Qk%{_ƕbN,\N* -ߦ*.bJB@u !$,RZ^anBlCAҝ06 8M2Y,tnr4JE?)Pt*) 0/MI+_(ĄQKĀ҂Ņ*؞=1!PKOʼnRzPH[$7W) 6,H@Y~[m UΜIʟD$wBm^ѹuɊ+0i^i.W5C^N.9Q*;Rs(d}زNYUse4W-1Fd<+qt>&[GF^K#?pqX1vp"_WQ!vOTHA'KA$:-y W+`<2qـyRQ /H3Rk 'IHYzc iKH%͙Pk[%rx9/\xeT?iI.f%څz՛n4̩Gxy"y~g׹[c1,U7%v4C§() |D; T+$ mBHr` 4j~&܌%kYENyӖg.!^;Q !!S̒|M,O[!?f2_ƴ=]yz2,X%=5?Qe4n{nLEk{A{ɒ70ucֺ?iWX~ȉ G_=sK $PH~̋Fks钄5voi@UFFˍ(xuY1)F5=Ӓ.2go[´$8)l3Yʯ85lxis NVBv;TkM8[i@"H}/ DWOC|AE:Sa,;ɩQZkgF,ђOt -kɂcGqi-bxuV'#A_FTk}~]2 nvt*LƔz+_8ʕ]KI iڕ=Bj;Ani_0]oe;wFk"yQ]Eẙ}ӓt&%K,-$ Pؘ(spD -2Cȱ 2EkQc4ʭYoD>,CD$xKNDCw *9-e%a3BVW!!duJ5kWdTTvٗf(;¹z}j4'z#g(B`hv҉H~N-*dmWYz+'E0JތEC1žW(QVZ3$2YػW rm@[2[ 'b8ZѰuzb.)Nn*$&o[r(˚kye待4Ѵ|e%?ˏH\\#m؂7,mOc5N+1-~D,R"o [RO ˩S;q.=(%r/V  d!7XPJ \eeTf|*Ķ.V,&dˈG]g^L]B}պ+qL" 7큀4ʠTfr-q!儅x>I܍뷁^̊ZFъT=*OmɈl㰠t- 6b(~ȹѝ)3jF4ej=Ew@Ɂ0l"#„mJfUS}+F Kr?FUi$roD8y 7WΉr-u7k]Q"ӳNն&瑇7iv_[ATڅ~s.0e#ޘj:mKyY9p@,dfDCHl=zUXcT wnr7f0&MT[oS0S]uw*)7biї3dMvdD&p039a_ jGԭTYN'VN|?%]@Fp{gB&ch_TFbY|M}E r%%%^S.-mxi𭺅riׯ0NY5ǏQD_K> P| bK(;^܅&Q%{bUo\,Wh0\|zb+\P]ǷO\Vֺ@oLYɋ+RhAYq(41$\q5Z3qP<[0D$?<ɬ2XSs2`dڧ,4Ӿ : 6]3n|P4|vnĤ+倶o, rKv=VQZ JYD-v K pA9کSc\iVg6yq|)@zȞCOacA|EJPIdi8?j6(앨"0j\o5-AmSOi>f@7uv0態RDAUJՒFX"e6.}.eLJ#*?|X:vrxhRwg9Cy2_(W8O>4u^ra_Ob[qQɎ9\r$G־Xl3ɟU.>j",tݽ24S,!nԟi뒓FS cNFMl\O,kuYxbʞkpʌ~"FdA$#4bàtd(&;`Qh ,Ʉ-Pؔb^Ɣ0q[Oވ@7L]j?lD10c\hMiJ 5d.hϒԠ([[mgbdVqڣ*j=ݽBE1e jRa%" *nTTiP~k2# j7! -{w"5Vz 42TbzmVj`MҬMˮ[`mjFLb B?kl")Vdv$P{ㅐ)K[I6?hx{eF"G"H NƟg\$9@ 1RY#hm;$T%# _esPuJ!N /IC1rd2Rci1>=FÙ1y3L \cHgخY 9fqU展hA +N_m*vY.TL%)k\CNNRs:1c &^>2^ZH\FЊ{bLM'R'w:zǫqy܎͊;q(KbEIN49VES1r@Ӎ%-^X\[N׭>8Isxx k8Y!E )(no 7ܗPHՅbPS6lYQc1 P'&ӈ.AFJ-N7l.ITKǎX$e5C"vjo〮:D$e-%J5ⅻ5TE Ly8zZ!@^[9cU<kOP!# r$n<>ݏ\4OP63eÜf6 CZ4UboZ勹UkPpO*."1 ~1M\#vmdBҖr$Nqg@ofcm8b-Jr'/-? ^y#3!H zavm3fXS+02i3i5..;cWk,8GWMZ2rg%H1!:q={e$U| GŋiN^i<߷ᓌĘHgb;V ~%ObhS4]ˆ>oIȢI^l.\6UZ DHYA Xl>/[4por2JV42SXܨlCC|5iI10žٌ\ryצFT6ȱK( L $]Z lJRe ֹt[0S3 5zIE͔@ȓYbܼ+bّD9m;%*NS nhV3ꁹ}j&ޅ0JMd|H|ke4' d|q=0UA,Z[6^Jn 6Sfa <;֐=l#!]- Qvv$) ,2|6@^L5x̜PC[__8FTq s"n\aLb3䚋>h7呑R[BM|r|n!UрLEQ%F"; CNM M1w1cv{29;# $պn1ehg  9AC'!!Msd GiH8v_\ih ,Ww,{@"]ɵ ! Z+G63hԡGpb?kN&ߥ2-cM\ h$ TPUR).Q0Ӓ"\2ŧ}$:H$</E伾C89|vcHE+-"24N-. !SR9(ʹB`fd䟃$uA|_rr"[X>!4s BczmĎYh., 0'|Y]'lوnq\܉Rx;{ [+Xa7AMA]b:^ 2z[JRKdɽh ,#31 'VaOηK5;L74g[8٧[cʖ4&̭T0QS#MTҗM)crs8G՜O]o\ΦgrƼɨ1hW}D{fSfbw8bîXl*k!&vi)Y' +_83^n/ӁSõ2.;f9&NG,`jӶ<˚@t'ȀG0Fef t-dT`XOۖ͠?CO9_N4S'PM6>xm1G[^|AkQ-ErMTEUW,iv(nbXGl)kl ^ߎ)BMej|*qejm 1f ڃBHim{ҧ$ &`mBImJqlCn7o|[-A+Rp22V)iNN;Dw޽1`S#lXҪJzx`B8s^l(# Zȵ'bPW``BQθӏ(Mwam_,< 5GՑqrCdҴM>쓇T(zeӊVw>%ЃO〹e4 Q:T):zS_~ɲ9LHh/>e"~aԛɨJ ۾6?i|X"u; 1I=~!.`6"U~(DIcNMdaIPE2((mJo'm<VMC|=Xs<zWMv  Réh?Lv&&Kh܄%.lO/h0h6` s$#΃}M1bϒkH2\F;Slǩy >S\+[!;cz}Rqƈܙ n!*EO\d$sهn̠ 5j)2izoc8"*r}7(+Q\a*^=9nLms$<ʧc Df1h(L*ծRNȿ:eM k zmkZ h)ew7̘y1cZL(7 "Ӑ)&֫Bᘲ6DL)m@6q"DAa\-E]c݇Wmvҝ6EvF%KDFv}ychSrv䅲RX)%oa]cm4ᗨq 8:4ʂBkC\r#>MV^aYJښ1ОUrr00bۼ<̅g$-y"2\(H 㜩TgNt&Bb#2iE?Ve%lx !Idj-ʲ1U5 z%\Adw0\iѧn`]KSq[2r._¡=[o#\'Sz*2+9pU^r۟ȒO7APr]7AȲi>m"! yMm'X F%غvUNYIp((K-%Uu!˒kޚĺacKMn)8K54$wn.Ecri! _4{hi)'Tx_eLHzS` r0Lzvms2֙iOFImJH׊ZmrB4Kg5b䭪%+w_% A[& ]yYoFb4Kdq8$trS_e|X8I#^7"0 !Eiڒ+Zorq8 !._P/FccK5~%'VHꜢB@=2+n=Q<ܭ /^?ˍdE܈UgQЁʼnDQ 0DIXj$In" bcD@l܂[6v%mpKmD2/; ,\RňWtؓe役ėB~U*RV7çH8}^<}V Ӕ`|w>,F ho+r9(JhxWQ4155rafۋH2Rw&$>$ eر5U#?#)RX2\F.=ro4=FT4K9(^LĬyxieb9v`wECv:F)O@䜔).Hdqֆ^=>bK]P֤ԟ釉PNa `<@PMu odiHnzaa-EFXҪ7ō+,{a`BA)4SnqVMbUaZuŕ |) p:4?)P1$3O;Ak^ !e4~ !HDڑl[޾C<$V߶\K ӷA޻Rb\,Jiʼn\€cJP UI{B=FE2VZT|k1Knm8ԎvĆI5͎ũE&,M;x+*v F!Ɣ)w4w=2?* ^DرY[~9vcD`'=z`B=C ZuBҋBN=\ݳxd@n?A$AǗn# rI~L÷T哐ۯP1",*FocM8IR2O2٭#kJ)=ku@rY5J8>c~$Jˉ1I[-;D qa*1GL[Y}P8)4sbRkk=bɦi.)1h!1~TcK´ߓ@0)3xjfp&M6]kAl0ӰUYڝPmTqꄗelIb!cw2%'; ɍV푀 8 ADM /4pSя}%os3mȍw]NՔ>JI7f.Y\.a[V<$d45[#|@ʤl|Xl{{7cGUV+Lc}RzEU~:5{xqȈrF=R>R,=1ƻCDQN2v*U#2XB$5"ܱY&ۥ:b7ZƐ!h]d9 iĞXꔠ,FLIƔmPv; icLƟNho*0cZVI VI<7ʼn ըp^.S|hW6X⠴Wۂm1[qLR|SjNVb,MŐ(WŰJ$G݁[@o hkjT]6 淥i-; gĆxhzWp>= Zŵ ЌPW,$/Wvō*EƕzN[q&&*3FP ZA\[5Z]-*i05WV+Qqp̏"0zM|-Idj@|0Zd6)Œ hz"`Հ4qJho^ aH9- _(uz5 \lBmKӨ}2+vJr61|/, iY1K6L-hA0GZDJrF݄#4O%UQӋIZ4ڍ'x+pl5/MhEo܌2=YG*j~cS][JNYA)%AsL2O$$}jH4I3AAZtbCD!!jٯG^OhNQV& wqz~85w6ZFbAص~I"/B j4/xāFrgֈʬ!_ ch7ʷkjT~sHj#2WUiGŃ ..C,ǹ'bvq Epֲ)) ;kKŔʨ̑lAeLK.u8nSII8~d̨4*;0ٴ`sU#$lzޙ s/) 7=RBJ шFfh#%Tr^[)]~F# v#M9I(+LԮ%0\ aQPr+YO(F9'=IQ)ő纞u!k>FÜ@#dEu qn=6`D4|˔md_ ;2Ҵ*jKF"7n?n)qtbAǝF}9(;MSʞ݋x$qVlȎNjBX~;Lb$8׎qJ56ה5/2H`\盪FM&!n03SF,l,knG ^N)%x\7|SӐU8f4VpɕaA)p@iP7?puȑhUI+_BGj%b:uw2V ~[?s##!m<H S$ $<4# Qd0$"T7&¨PQNnơHn-]E*NFF\u&~hrBg-@-~؇7ͧmlg#q"W?e>|-G2A݊*P#`f܁ "S]kFaՖ^*.Q)ڊ7,rLʻHQMH\**y&r!Q=҃%``J9#GfMŻ7 1cJmyN){z|Hiq |BKEX+VU5~*׏6HCu0Bj '?VWͼ4xB8 ƑEƊ^)W,7^/G25Ua]X%;ڨℍ8$p2Wz}[TjbiU-4loSܓ1˴+%Vfy#&J -D5`*i1ف ﰮTKj2.$g&FZzM҃"KT#4 ۿEU#S_Ўmȯ~ke` PۘdY RKG߶IJܴtfnΘS]U‚Ƥ|qWHXӋ=SjWY %m&mf(ȧmxZD+81cJر\F(uqCxWLUm,tM)QbqD,C^ض =jqmB=z&֛p6q 怃݆(w$h^ejLwqO6}T]+KZ}&!r{S&():j!T8^,S:$^ub4j-'j֠d QL~ߍI5؝8EXvA4wR6$u;H;J}邜iFq#5#2%v? /&[ t|Idl6 d$w8-qHT L6kсlBvd5h!NVdm$hڣʥ3rfGH%=v?HF~7&7iÙa?,0)ݳ m2 S(&-(:iGcn7sA!0ErjtWlBij(rt„bs".DR>#| "gZ{t/$IJ\H6 LAD/Ped4j!*MB@>8qjܲ/%G Ynܓ)C4MF--Lrڿےeƒz-[DR[x,W z cOH&9:o2cXN$ ba4h+xǙ w͉뚽򵼤G"I5 2lec&,1DO*3sے),YBX/td1Z36㹦iՀUM=uh.2!xV/ ?n8Xk67cH$l(5v]oSԮU|1r\! Eu37_FTYC)sMtzLRhq'W'Lˣ 7nrh M7~dӽVHѸ(>8i16$EMZ5BFRŲq9wB^r^B^*(+Ӑ'w@[`cG䈧zPvrvjJ:mL!ug· 3*y~6ˢg O7"4m ׃:=>Y.iTQ&גVUқZtq0M!ejҙ,e\qʿL&Ex ^^"E1"+mF&WC G-kknEpe 6k==2dbѽ ?d0}fFVO(jVJքV(gV5&JR[jD?㘵r(#[BȲBLKZ)*ǥIHK1#%XL!zqNt#f۴ucQ2m| A kA \1'xK*y,e#-8%aed;,2&6t}D'fRS rH[kR\*9vȪixfW֒NÉ5v cR',[<\LyXͬ˵inV [LyYʄ[h$7ߕi&&6$MWf*!:cJ7R\X6P Ő+6hŲM r9F5L30)b,GV95DZ%ѷphiۮ iAFߩʍ9XVbJdj˙me`v +=Bm4nH%;[WeR7m^eKBj/dFC}˩yJi a;/A&v;"B~+Fڟz#Lp*:Ő(S) ~,mm8J)OO%`ju4r`gZdDi`qc`rF OʿN,]Z ۾(^7lVV1Ju)oء)mE,P7Ř(y!ŰI 5ض $wŘx#aI.q07 Z(;S\R -|78Z:+7`nO\ma6UQ\vbHUBM-׶,xT'"|ZR Zc’^@7@ ~T{r`RY jD+D"PɶN8 Y.0['1h*gU1PWWMSaBJAMLHr nY3ۦ :(2hrNGdxwg 9ݘ^NR@ޠ6ӗ[oDk6S'sj]'sB3[ni Sl!T7߮U(H#rZd 24+6O\1l{Бǔiۓg6#LśTޥ2jh^ C w䩝)A> !dl62ط| ƙQe=s~L$'żp1[, .@H =SsZaMZ^d"J6;b PW6]J P7C)%/[{Wb;ҙ 4hQlRK\ Zj(EvU/ <f#[\ ~L{.fc(It7q^vِ0#f*%IV}ٳ!'/5 >gDrvXXR/%iªb` ?dW-@ q $cGdda)٢_׋VO'0}䫸VKZd5"~ W Mn }H/ W5V)+.S_l2v\dGuiATeHiV T/.&hxgqnVevVyor鯍 -47zI+ oP2ĹXSXp%iKǧ݀v1 ȞU>gcԪR#Uy%z^98^u}_L92\R)R!ɢtrenɕr Z'|ON]BJNŸ˵yeݢX *F#KMDnԞ3B޹]3EROA c$>֍t6 cB1c C׷ Iwg)^Z ,iݶo .4Ervb q&(e0W+-t߮(l6ڸlX_Ӻ▾x}v`{b~R敯xiԜ)RŰp3&45=07Hk!l5w>%T(!mE+]xE6ٲkeS`^"ĒHZ'& Nan2CtWA19H-)Ү"^uǪ 7p{| \9i]*[ 2-;HоZ /F+&FD9#> gRx31U{7lN>A)ZCN+b8ԲUiS|1KHjvĥPF#l ½T[F6!jIbNA{.q]u&ӿ5Ezt|@ EwN7 f45a+Z &_~/I }OM Ij(A)U*%&Lg "u7)$Urd䦌\$?~c9RY-B_Ҙ2]cqDGUܗ}rfpHbm5GRV;PMԵXB!)_d% ,CJ̩Y Lx;]Emf& Iڿ9l2p;)sL [-Gb¸.Fk6HcVdb`:Ϫ}7x1 u؎JH&['Vis;01C UL5e,ѡh6e⼾g!MqǦ1`拣Ve~e'-3Zydd Ҝj> wG7,یK33#@qW<corHСr([*@,}Pv"+B*M^`5>Hde7)=vRX/x%3=TA -ئ=W2l0"<Uw~NSjP]+«i1occ㧍8z%HhCh*ݫƌ|<4Hu&H XTx Ă=i{hȣʙ'vɔz`*\wi2]8LJR` 3R4u)BKB~ωR8g rY,Nɮp b40 f.` Ӑj_k&ml SK9#Pko|HݾJF"ܳ5}Ng&m$}ऎJC}Z!Rik ث:8t.^bCd Đ4+0{9o)3>uS ƫ.Ze>t1E߫@&O6*,tU ӏg×c);yЍe%%HXQEl0ኄƙ+hNjt "<έ04%b i-xLF[d[+p?#FH~*x 4C9 sNJB,r.Fͪ+~0[}ؔ]FuPJ;- j7JeVQڅ1Ŗf* O_O[-۲GԾ.<^R^!{O<ۄ#wNq&Vi}!&\U>:zr1dzK#gp4(Ū;q\Ӌéa Y'NDvoq-RƉWW;TZZSrr<*Dezet U#I]I֘وE+AC1N-u*DiA3kM`9VmHI6뒷PM!u4VZ•+(*sqW|(!ح7^Ǿ*J! J֓}ф2p•;R4$%bi*:H dGe #qLYB8Q@+ %; cmd }mM Ttm*N- Pe%6 %0VE}3"9[$#BhÓ;n+Cw$K-2%W{Ȳ] g ʈAifn-B4ZreHOD e'TI 1 /T"bbs|N\+T]M*Fd1OWytV9v=o 3u2f9K@9IHIPu>4䶪 lz&ЃV\آFZSHLᎢm嬡/m+@ :̌dND1͖*~!Bj2m\jQ\u -2WU!GROc Ґ}$^r\*JA*B 4YviEޝ'e.} 8N>/eеOLoWR65ȇ(+($dB#m`l5M25eDF%K"lݺ(d劎a )}Z@֤Wʥ &`064yq:Y)QTݱŬ ٫^KpdzVou9#oE A2Ե1~Ԋ2Lq(7Ȝtυ0)4l:A+{1b*IV=f;7@Rpo$8$KIyF;%? bL $)u$B?#n+;!ZQI' rL}ZmqBX,j"oeEqjcmiQ'$6XQAdLM" A14ZP]3 V k2>_9_E`b%DT~>ǣơ~dM+,YGV䋯H#pcI?fdyܛlCOzQQ\6Z.({rU"#(i32&N[άh?wY+ VecAɘ=1d7M|*Y?i$́m#2aB6ӈXMKn%ԅ'drg 4oOJ}K}֝{+C5Ú7%7CYuq:HB)pp<<"Q%B3ɪYQJ.aƆD%%w5əJoFRkNotá+``m@Kb>;@7J"h @*W-2*AぉqUQZ!!B%2ϒUv;d)a\jNO늬 l~TN踮H?a^6|67CJ{h4Qj^q%]H\wu?J]& |,8Uƛ ㊶Ka\QKⴲY)/jPYšDQ|HANGOd{ ;&!S@bNt_51UWaL-ḳ ڿNZ1s-8F޹#7{g<7 C3&4ImS5>H*odG+`) 572+my8}SZJ14j{%eSz@k-U.(hF^_LYiGfqBYiVֆHjRhyzm[4{W;u<bkmgѮAh=P Wq 5;Od&hR]^?2XE7v}XR?]Lzrz\~(ՙsk.; W ~Ue7-W'+G~lq1cO cH~w;C7Muh|3X2U̙H'!/R+x-!Bghy##B9A&=C&NpZJM Ume# 7Zmf=Y#eAʜ`K>[iX0,@jcɤmw"DAbTn IlIzg\kq4%O IH^ Q]psd l-ʖ^_m69X$Eg9^xmz!c"4$ Q#qN"[HD90b7JQFZk"ʅǩ4>vU7MݬbO.v; >&W8DCYiȻ(5Z@5=OځWv;Ԁz9eu.H{NHK8 M-S mޫm{5wVK *a8إ&ٙ5,KO\V>N%v~0`z>?=ZG٪A 41qqU_0p_ 2.9HO>99QO5 d!KFy8XHJmBv՝Ifq2} W;@OD՗'0OW$ӧkYcJ0a.@qs'|*+˸Cf[nMnʗl0jF^]U".YYOt˥ߝ A6c^FQae^شn#eO,xy|\ո|,;5غ{YaS_5kv% O:cgZь M{)gqq=?_%f̜cbOC\ݢ#Q1Vtj"HP(@`II2(^bI@1SD/7n*VU4UݿɈjs|KLy|a [O dS曃_yqnFl^?ܨ.G%~3OXAOS.ꠅŴl'N  č?l}h7MG: zܛt_ʤDNvj2'efzRoŤebKRJ9fvQAaZ{Ҡ11^hQjm؛2T]W6\KF\%\y6kI$"ѱ@~[xa܃vM0\+J]g El@",vZ>q,Wn% T@;M sDMK Rޤ+!8:掟hxPp{b)zc4x0YN'XmT6$t9`,i:_Ht޵j1L sBO]`B/sӽJu[2P|M!nf=H@I=޹57jB>&‡3oP PrE^uiP1]VJʇq(cH9@L1,C=ꓵ36_ٕӐ;?Z0TV}˅K늻yA%ad(RLf5|i9_Ȃ52:f "x@6vb.XKx;sOnҶku:[Rl(إRTJ%X|N8?9j&cr7pF+5N3$':dbyĝ0s _K q=HAɧÖbl55KO znX]UfOkPcce͔A#}Lo2X]էͲ0eA_>f |g9?_%{L]4_]@b)gGoQ[\1x㟪_n7YҔGn."*TcR-·_CSLF^s=^iE1Ny/_gfTc_)eo5ZNfcTHxX޲ƭ )˗m Rm.?qTK7T}:/ژؿ?ܥgGɦ^UWQRQ_=G.YYxr/k G{y " ѝVMd٭룒2oe,xC W8Wn$"y҄r#-0C3TZppmߦc 1i/OO&AO,R'q#ȶ[hu #Hv+,n~6uP&@T2$P>L~cKؽkܑ2z17$;!N *7@:;QƜ|wiN(0+7>1jGzeF5`!ᫀG!?mؠ =@>!O )te`hK6 lR͚=3{LᒾC)AƗLiw!cL 2gy 6 $Kg! d,rgWBK}?lD=&Ȥ+XA];lΘQH)kIA^T+s?"#|7siksc/l<&ي2}'6 ?2ZyΖY]hA)fE'2%gcv$REP„Wc* FtA^} -Xq'6Ź<$WvCr,_?]y*:|v"G2JƀԸa@}Ea_{l&=zWAuCiqdĕj0jQ\r*oVI^j yېB:tDMaj B2)PQJJAU*k>J CO*&$G=!JP} @}NV+w_IB_~[T)$*IIY'eR woU㎤S.ހ ˠ i<To bhӑ2Er; ؐ<,/]TSI#d;E38F`6)5H(1^YQڜVBA9hx^~?g X`3-bPvD6q"o ,K;OCyRxo*7R?<͆0F͂ 倲 k* kFGo1Ꚉ\lGE>G-Z|ډ(;!v[\FM{jmQD36[dFkLqD)f]rm6h(HRNY:3Nd3,)*Ken-":RLĬJ~/lHCI-utK=)U=CI2:MNICzZ΍y^ͦjqnۄ{oF_%4ه-Ln|.4*z=FɅ>M~m# Q xzf)q"mf~>Rhr JW|YD+hn0GfA'zsk#tG ߲/b&PsmL(iI )CA=T2^3S\ M&y9SDxH7ip+H4QT$XsTӉcFdC!{6=閉 rxY`+27d07@(i8JS5 Yo&yT(i >UeGövrDTg<)gwD!"yc^ L-#_sh7dc/.M5µȾT(+˙ ބSvͭ4+O.iè]8QUvW?jFnC~72Gbx"S J|_˻Dd>, IۜQ)DQTT_YHdIU#nskWs 5w<%TEXp MĆ*fN8Z Y槙Ŏ=s_C| m h]UGNiƜ"7?F,D (vY-T)Ol`*!v7@YLV6 sSbcwrB›>9l!C?M1Ah6^_eUԸXkq25ir3=kZ$8/Ɯf-$c-91ƙ lNkeeF#_w/ٓ*Da:5I@,GsY=L@; %c/X#m9$eV6v Ol1 FzޢBwh"|g] )gv`>%bYNH@G$mϳ䜸A/ցzureV0& ~T-&0EVH*(A0!F'nͣ<$ȋW:\i|WSA0F8e5ޛ\iF ]<;KQ PzaM Asÿa6)zPVE6E-xSx+>.P*g 鏈qq-2I<|{d mNv'n|bM2oݲ`(/9R+j!=_Xu"W%Ĥ) ]ӯA,Km:mLbj cJiOx >LSL y 94^\~LcH NjtaLi9S҄e$lA l C)vۮI(y֛->x2"[B Z+*mLm 0r4H؊v|M$) LR6s~Ç8fknjKLj1-1ܔowaֵ޹c};qvi`pSrj֝lj݀G*Of,2pQ*@^h{bbhfA$o_9? LG%P[#:J3(N ^@ '&=Yre2i-52BO/"><4w:2Kt0“ر#r϶ 1zjIկ+@'⧸锈Q3$V\aza=|!- Fm~lIS,jY2L\H&7 qQ(Y \$Ɩ iJeS*RJuݔ%9[e$W%Ot1Ȋj읇bB Zdi `X! t0\U";ѡ(6ݑΟfN,G\lE!׵Cn1<={s?OW KȎ(K,&dU3%jwY>7ш/uYܐQC$BZFܒjIJB"Kmʴ̃f-Y INoB@ٯ#C,4bU4%戊˂~rB.9 -؊m vmn#e=ڐ_HrʵcJYwiDz^Q5i7ļq1TZvXws͕b;dTX@ڿG8]i`g4+#dxzY$O fwa-59s}BzǗ[[+RR[L)P,9&D̡>N.m7uőBV@Cx·SKHr"NiL\Нɧ,Dy#X;#HP#&T A ?čOT10UNuMQ_ΜI Zc I0b<yOS-=rqribh~E^T )ħA)+JY%govo 8=srvjR֐|*C+A]]8y!{sw5˙nҬY}ml(.>V{J.9qvЈF0P8~U(_YT%nZ"9(DQoo, Sl99 S- u #I@ơETMT6~Og7LO%s^:FM\paG $pDQ8PQBjjy7dG=܌seB{`f5dFu`g0DŽmHNN ۱ tdbu}{$ObOZ*q( b1GOlgOQ @-tȠ{}j()-5ۘA ~Y r^ysw{q`>LkM/Uye{ѹz>,91͚ι|w vݤ>߳%ex@SF߾TFv7DV/#2qqCk*98WswE< L ͎Q[)PQʀsHK-}t൥ z_# IVOTHT Ǐ0q_W?i?zK Y$)[I$H!s 01smNzivf FB?k #󀤂|*kga ۫p_OoLC8Eh~]%0FFR,6zI@@c"'6;}:R6:d);;D h}36Ŗ2[ZS0`0֨;ӊb/S0rm e ٶkahAN߭FW[h?})2CڭXy }lQQ~8ZdPNmȩPPt+tX =A"WWJdM-2HZ QN[ڛeRſOYf5 qݔҶl/!mHK] @}ٓ--m2*\</>/RGVw6jIb<+,7` ^Ln4"?H*rjy2̵ ^?PP-:Q m#uZD9 M~hy/|NlEFGNoT> e)C?6\̐CMNI0GH*]T1C2\\xK8h׏.V>'#<^r2|qW?X[9; rH7R_cI/O}?Se9a@TJy*"x 6G.e*/dP”?7R 3[afY/ N|Ɣ.}WMR5Ri@))#NdJn6=\\o4|8$ےu%jIvQ6Gh -7ƃt PH Mq~~Y>U ƴr&6UiT*p̈́.KYJp!1KպH?|-M'xR}kF+/Qj}"q5%i60x8AY`ș0Q7$mJdLkL؜~duJ<^:9 Jc@4Zz4M=C|s##h_>͖Fw67Z5Ĝ! 1_8c e[VRTurdcVc#} +p AnWzWٮZKRy&(Jĕ*ʖȉ:Wą]@J+:ddKjբE]cmĩqXg,@AND6K#xWVbm]?h.@qW?T\L^:-JTr4/OIhvn9n&+"ǧ9cԀR-@ @M,\C] pFh'vSah6fDF{RC{xN Q)[Q26@MHF-bԐW2LTajg@f[;? tCarTG.B0EWqW9 m2G~nAB[Kx_\%Pc* Weҍx[/2q=3.F7 W)uqq~r%ɷ]-q #AXM1 ~_OTRU9)$(Y{ "ȇpAF7NX TNצ,O9lGE+Q{czhRX;>i*ړ\R7b6Z/RreA-RTNfF`2 âs>`UԀifTC7dvżgb@A,& "X2ÀO>Ss.MƁ|FQGr2!!F0$dr܆66<[ኰSwY#vq1yp%xDIʤ׎4X;dAm'y.|ṉudq^U^0˙vq!2Hˍ7upY N|;fTG~7f^-CP_RRM65 2DW[hMe^Z G-#'0*;$*;|S9Z.;zj8.9ltDnw'?$h|q:]4"Gk'!)9ر@ 7'}/q6wkgƧ0EjIˊx Ÿ İ15v$J;jJnRrܷL+"V mA(Z*>N4JJJ8Uz-f u'İ1U]Dn\GJҴ޿, ;mڕ1ALa$|#˜-kڌQ t 6ɂǁY5+5-{;clxWxS@:p~#j&$/{かQ˽)|!k%T55s*=?G)f˙(%RE|Oߋ.$<}'d$]845-~tPnHQ ˖2ڢ\Oo8K 9Cӝ%U\Ƽ{l*X.~#[C3/6D -,:}LH?_z<ߪ ?BrҀT[w]f_l\jx/gLٯ$3iI5P~'n>~WȍF ʫK^mH4'&z$b>ˎe$LU-ƳmncPBAr8D}+?oΏRrQלOZkHHU |r#_*fpBmK+hz\vrT$UT%ۥܰg)[64HȤՏnk$ nX@G3WS OjKon=&FJ<#5-c=M>?Iwߦ_2-wfDA2"Qqdg/M/Nr3?·滭ϨR$\BB.эۂm7)3&H>1..,I&CTS#dJ[ j]2=pw&\ h[^* 34)UP;sF^I9k!4A`i|K,Hs1[a!A=Crwmw Q,^sYR h90Θׂs_1Mm1*er[M<Lespe~A^m =BY.r(~ɿa'6)%s[W'ܣK,Ŗ:.\[EJ} yظM4UbLM"q)->0á?gV#K4Ð&ddhG$KkQJ24)w* "GzFԏl+Kj8+,[&[Ϲt;jzQ"PǡST>- чa#t3c `.*?SL ["m E)SA' 3ROcV]uQEk\ qSn¹'DIK$+lJ%M!^&ebW[UR#6?LXY>?}EϧҁϪ`:j8Ϛ).: eu j+NJ qlo!d2%վu QQo9gnyH})GH\LrwaI\$J]۠qpN#d"L2@W0$f2r E$e]DSc~e$pE*k+UˌxuC$Z+"mO$HW&kqOH!f}eVJ)᳋Q}^P hdhSǦN;8J9ГnلEU<]$@=-܈7HQA)/Q8T qs3"*j@ з, ;{a6v8q]%YYGBNF˲M+_U^vfyJnġ}O# 9 ({SQS[[&Yߒ)9PK L2-`TT` /}1PRy^peә%2ԝ9AŜH4hK-akRkmd6 ŕWY@ŋt$-ܼ%^vѵiSOEXcd6Bgtb&Ktq#d~^_dVg7-%$%<ÛG׷//Y7|]9 i7E=:e؂vJԍ9\coPEI{Ұ*{Sz ۷)*^F%JIh S l #lBl5bSď'`z k2zWuE%3z켋/xKj.$H3)s.SRRLF6ѢGn GFs &C1?qbphEWݸAdxB˒,9eMKѫSrDF2U?d BTJ 6V|IFU۫I25"Uk& Txb廊vv*[%) rC\20PQCdy-]WU PQeL |UvXt ݅"`- 22$l"fCvEOtR픇5#ٻ #=@چ4yF{Y [қf/nիۏ}ˁ ouD@=)k;xՈ"#bJ^H`߾i#.L 7S}rC#sZ'@r"c67QeG|ѯAs6|rz]?KT@^yY*""wo ^1ԀR?.uro_P;G s?kf\=^^#s!C4J,PF~É><7)fSviY貛 TD(H(Px@ɾ3fZH&Rqm0'I`(^u,)-CANĠ+2GQ EYsDPfTN_X?qw~e%i;~4ax՝/P~gIsr/&.EqH+:Z=BZ] żLQEDsr>M^fRWoI@N .TiUn\b%mn,_T]y=6)!fx*eӎe= >_R[y[Rn#I~5Q|MYW22[8ޝ ǑrU{wsP $Mw1l*Ue*_`${f1ΥR!C !XȁE:ZZ/"8O$еtݸ9/\R%XEؚ;-b_ 6 ]aN  k]]s"&!3aj \%;6i-fD3oEC((s"DIuźxޝ5% 2#Yʏ\?򴟴y9dBE["޷cgP^I!,W?ka|N_ C7b&+\hȴkҢR?Sm~"ף7p[Ө=O/,..n/t;.+|0D@nrX"-;-Q7~W"맘@Z(:~GlyCcɁn/lMq.o yHRYiF5`5͔fma"''$V>1)lѲ `R69][AXzwC1v9DE\IcAOjWr]Q-1}΅AF[Ǝ !s6mtevF+1LuLa%a¬iFO,Js#浺FԎ +*"BASqlj)۶`C8R 3`,,qN("IdciQ/ϙS9pynY^Xq>dZ5؊N&bE ~KG"¼PI/a^9ؤ~a}е{xd.ggW ,vT_م S)F+TfFjӹkTne|+$Z2"SDHd1䊵I`s%{t;Yb(3HBA)Ji.UȲ+ EArre3aHA鏛n1E.̭4_ lCk'L${2z`@$ƳT‡\ hv۠})R mq[5+_ dnBwYϹ?  R2)+ׯ\hQH-$6*Hjk\P0;{©Bhץ7#I {YIw$,lSq$a4;T05>RQKnc\mTmڧՑiMmNk3JY#Dz3?oKe6QrL ${ΐiuȐEv46S424r5(þ4Áx?H\%(7 1'h~xӆ;()eS YOCQ~\0Կ.5CžՐGpѩ7$S] 5qqOzZ_C:|ݿLKB^$O7ÚÍ9Ϙgw&[+ЩT~T^'s8˚0cW fdM(A5[7@LQPrT{#r`QqVMLvUU5e ؚ({ER$d5 )+,;ؖqU{ N~ʆ>NLaJXC$J7Q-FIk(`JqXH9wa{̔2q-(AŰ$#bB̎ɦ7As1̛Bȶlhw 7~w4lˈ:M㘂I#A!BWon*d]R6%kAtE:om|.A_P.AkQ:P + CaQ3ʖAjW2]JgwfNce:t,bW9`SW{)f+i֠mL609 S ]nxhLE,8?'$d2f:x#)[ _nS(,P"v9˗Ye`$)\uE(U~y]q6`aoᑴJ #swg- [ufJ1PbvrD'\t A lK6X65'ژex)\5N4TQƶrZĞH ?W#{SI! OD+#[ZeX rdE>&w*ėO`%Z>dGq NP!՝$2MŨ,fĭAj$ 1DyOZ#bLg'M^-ivK+TY r?# 83K|/^\{UmB>GnKu\kwmy l :tFU-`hE*r<'&jYYSf-El"ݩP׾-|Ii_{\0AMd)Fek"6'k-gƇ2GZFN㿆U94HJfgw^J-S<Qys@ifuǾ3Ô"\G[*XzFiO#ġȣVo C~ZKbB~05p%`+BEGb() -rYq^& MOl$ua8J?{S#ML,5U}߭Iei.a+7߳K/.hs㜾q >-.$ Lib?9Om . N*Ôӗ S`9ksd2&!qb ;TAFG%RCYG$ )|2HlylkW嗎R(&S lH ㅫUS3q9}Q1)e>,,W7(#ҧ_R~-2q*:Laf~1{^O>xLze5s$FfD%\jQYY;ۂpH, h26iDmOU_ٍL5YFIG{KEJf沒q1zЃFb{8KeT4Ő'_ Dkr],iy=o_/ť"2<<_Y71Ep()\1d}{(1Y$d S n}( hޟKY $Ż ,i,fR"¼h=/Uc5*M;r'6}TaqbV:juqV29c x~K84vN `m 29)H͹2BMn^i!BND>=2AA5SzdrӦJZ P ,-R}mجo Y1j`kt^"C9}c1IcgF55@CR!WwlJր5F֘t4M}?l>K|,j(ȃw̥k!F .6:CO }5B j`Ol.f!&Cf&ʦ\5޻lܯMA(%hiM?@Yݳ !-iNXmgb`u~3!ϢjiU ju d* 8 i5ܩ֪M[~$TE{%1P4CV|㓌5\<"!O&Z3"94ku ћgWF  ?#l'My.ږѕf5j.lIEvȵO<ON]=]ދ0J%7ڝ3.Pw7$A,jHA<,Ac*{LE;)uYd*ݲ% er3(i75ȸXXx9!RNa dD05H=OelaK2jYC";q?j9HN<ĶE>Hx5k9Ƅ26R>TO0>^(Ѱ&`KmY-P7d [M ]~KfV:7&H{#$}7a:,]X*" < s#MՋ4T$)l{xZLRȬSNٔroAKI^2! -ЭiWm*:zuYPrӯu_N0f\)CH ndkp4=oL~eyӛdI/uwo/0;arLR־LzWQ"Es$:ƝP6U4=Llc7 ڕTS>(њ68Qz]*(dM]f@P"vbQ ^VAO ҔErkSZӄs!KN*7Z}؂i%mzoǹp/HQF;nF-6aK! Lt,%́e*J+sEELȊDZ#WX$C8+E'~.JъV7 j23n >y FY0]l0Кl\ veC $~TWÏ)ՖJIuqڢ8;[9nXn*;[-HQY/dilj7dq"ktFI3"{l͑ܖR64c]-IbfѶڕ><\6%qo,IXt@?H*<:斋TP44?ʝ~0ߏ!bڦlѭh(Օs&Hݙ,j~'3̵exY M' 4 }+1 8q;uS_8iLW|ObHp<GJL KvC4rO_MQIK[[r;9Af O9MǼ 0ܦ:Eoi*F5[c_ l%ךP^G T?7h;U{Sj;r]3DL) [:Q -)'՛m }9]~I_՝I$Kӯ\(ZTWV._q;*ڎؠ[M#~xEŘl!5&سQkJGŗiIB[? lP{qzb&2*z킓hY,:l031oB d mLk}z6F SQ':t(emI[j u%*NQnܯ.?h[қ6QAp*"A0#$`Zn8Eՠ8ؚ&-!Y]{CvQnt2BH=*7#y-_Ii W7DȐ'N0ڧ}<#_f|Nd6/DԵH5j7w[Hc9K=/7gD<_LcOҟ/-X{y 9By^? <3K,zaRmK澤E Qpz:덿ggc/~m%8}47WUT:a㐪G%uX1N. NDP}! 98y12mĺ5v- x2IG@(w$Q/nIi%o(Ls0[€ 6Pb|NYarJTty6;y w@z BT_k$hP()3urA9b%a}n +¦Y=4"%' Ư5k)fG}\abyU͍ a 1_4MDP?.8fQG/Hh.ʜHEkOJjg@ WV0%Tfo0)=! O VZ7J Ap6NA4aMR=AWw;~73*jW: yс2vx#Rᔽ[[. ]P Epငc&fPӵ1&`;Qk _WdRs^r=kojWɰw&Vޜe@rțGqmC.choedyp%OD#lm!uĕ%Fut TdW &'E zdQxfV0BUqB6QJfTrL*ԎN-zHZH:1IPH7Gdw\b7c}s%R6 ?i',mˌn +mH y>EI| ٓ"Pa_WK(E)(rq {o XYz$(X k "6ZuEN-Ӑi$$]zkᒶb]$߷|٘o 8i2]o; tPۊa"<822 WjS!Ep NGlvk@*Hޟ6Ln݆IkQȞ]*Il!oUWh;l2w' )F.`hh䆲J$2DbTVMj䪔jiѺB~\ y ZJ겐r+q{vx2C_\Fb;` RRPKY6o:.;HiU&viC6^ *:5@k;ՠLFG;^*SoZK} w#> Bh!IK󢬀Z"A=O,D*I/5&C*̠W_Ǿz̗$qLµPyd2˄l~k- HS\(V))3WE1<,igoUAZ>AHw؋J9̖⟾}o.8SV,4ʫA^eC~M? W_(BZȐX멧VFҒjҁ ~:)ZeNEX!r5 xM?\捬Yf[VFǏ./;35ͦ[1Ɔ%X%?fG_t+v?ak3 [嬜[?ʥV~bw Ť<_Iݸ]TW z4tCV6( E{S+IQN01v8B_4%$ORepߏ!wQ,~ _e6N11y(wvR]tCLJ26_.I,dR{dmA\Uࠨ=2\,VMp"6Zʴ\TV )JKhT\XN rAƜ5^K샽<2MAP!n6e6xRiڄm| h*UP kΓMQ.#-Lz9sfKҤE_ݺV_Nem K," ӾO4\C%@ M)ڄ}r^C b=Ն̯9 c8`Bأl)HM4XH Pcg;n}p'v2-2=[s,ݜ7582ȯhQ p?8vc{.r4VRbB*\T7x'mYCȐdkUD`EA> E=!#RiJPe0mx%ȆGPSד|֑a׫%Q kz7YaNHcq;h:˜֟qk$#o|)jZ]AVО{⭩NءSqZRAMӊ,pS+Sxid ?lCK i^< wƛ D4#os'\Y-1S Bx%=nI%.£ Đ5 t~g@)^/ڸb`m޻xHi8U3($)(.) 7QTu풀BI{lmǵsgS|>$,̣aJB?VBj>op*WzW$7Δ2>/tOzenbp&򏆋U 2o@)6?dx,It#`j% r4H# 6oRb¼Fzr4O|D!bFrE$|Ko_lj1N,|y jVbpi _y~WDYK2c HnRu)vĭX~/89eD/#6мje\%3-Q3͚|iwF#;,vwWd\Ŏ{e-Mfx<9SHKf9tq?ve /qYd<v O?1<.z%ܵ6T r۪+4rO %LŐDesMF"m*%j9MQ-2'!?悧"H 0Ps#E!p|rgiԐY*#vޕeԶ 컐U7,ÏNYpל<,qk;$(8l ~ٷ EG#98tQy۝Zw|%U!^Sրu5z~:ٔiA ܅7v@GSKͩ&2%'.)+ }zGtZNu+B| R6qi#Az e:~$e˄f"JD?P34 ϫ][JQ⢽tG%E4-ѝQ!qmo͞B'd bpY#Tљ ?\ڞ l 2= 884R~7r?tDMv(!F`xH* 4?{EB>9 f :GLQO m`aLHZYmIDMAf #ҟ<2=ݢ /M鷎Y H5 X,6f~9ѻ:%hAJ77S!,!$x.n'~nݧODY9bn ,9#amK'gLm-ZuhC4l>U慲"FCXelK!o߯q*ۑ“ՄN4Jtiq}ڟHv4'QcU,ʼnjatZLP GSC\Y1xڽ'fFVTT4]#Ti"2DV;9pӄqq *i$ -]#r,PzXp4bh#HZPӷq6Fv APztܽ,]6{,WO5JWŸv0 &0i̠HO&mScmFeg+V+PM.6,iơ@4wLZxE^݈7ӇR E8|k 1:#"rbKWzZRkVٲE.&PQpznr-v[aCCJ9#6v:SQzZtɉ7 (^V?d{Oa- tLuU DQBINGJ^Zۈ^ݿ͙da\ѩJEjރuxe~=}{WEjq5V䴌D Sָޒ LU#-y6G=|`'v UmxaLq\8D rf RNcۏ\rcJch9;ܘ+A4ڞ+[4a" 8rܧ듂RzJiBt9 |8y)kѷWV#XI͹r}eG"-}`z ¶H|8!BT|]S q58@q$ U9G v`zCn9{JZ+=RwǙrF HԹG9 xZo?\7DgLbMOUR:t- H 6ۏAʆ餗 Rl*܇Z9`LXq)q%Z=ybcu)EPKeAŒrN=l 4IGBjП՗!Un,:p'Bi[=y5b*g! 7|;Kpݦ"YC%NEˏM&V]yX5Kpd>Ptg@ d7m)Ԯ~rǓAڹ k2#F$ Kk$<Ji v#"tՂZ#f~1@ݎk7BYOrs!xIk-wiהqiPrRG\1) xȿ2u ' cBrVsUT-. KPH12-AQ_FpT4z'rl2\@aQ|-.#dVyZKDA9V|k&N3A?aGY-⤁^;Ֆƀo» DAi$kqߥ0l"dY[73$J-,+p5My/?d#\d&;{u-[wsPr7lg"?V{,wnjqrDE"Qd& מ)|޷Σl+T;qZJVj4>k+Ŋt*]CTk Vl$4vIAlL^o)!6>8,Sj^sLSı % mzBH m6#MH)-$m^N4 AkTDK"]>Z~qo'*TSn.A^Jl-t(z{{r"D94Ku(ZrA҃ ZkQڵLJؕN wqbwRԡAē#uo !Y~` [ {h}+HP13ln%o'rr^9I0ТU-oQZ<h+_#?f҉J͐:͢eaEHz߳p_ϛ薱~p}sy6lݨ)5 J>H5H#("H\.* rmyO&miRũ&D _4[o-LgZvI~3,"0͏d5.lrS~Hjiqs/d,FTvQ.䑣 𞼂>E}NHSN ПV.lIAoc,5BTf^š- ŀ >֙H3Qʭ*}4I'؆KHUOl̝J$gUּ}-9R]Skk +k'*sJZ v9Ìq?h]$Tc4hV(;7tɫlNE)a:6,KS6V\J:rpXwҿN6:}`Ql3҄0l:4)ʹbK^ zQxvJfDfcL5ޔ Qh3G۵IoES4__x.eB5{@ބծ=W Ԭh }!;7j8Ae2RPWՇrHye ]#3wX#եEЍҪG͐iq\GǗed*?-`ol{2Ȱ؞~jrF]YdDhcN=ZеEbxkAQ)F (:0+ev=1G?\I%"SmD僑 6?ez͆\d 1h@}~Db :M6x#lA16^\C,ޭF" Ƚ('9e[n% f.Y`>+7 zPW`=1Xז78bđ/!n`A|S\)4ra"M1ˋ" q ՝&'ʨHӀm(0{1+.xbE5d|\M% /iTt,GSE4f*ސn5gyS4πi-r:Ta'F'KiʤZU M=5w1k6ЃȚ} 0iZ]ӪTE~1 e?gƒX(W޽:m+_ ;x"~# v%2c2$ZQF>4jX1Ɣu,";yZ]ߴ9}_<3dZU^)D?d%Ȏ!#pgY9% oO ^V2zo+R?`j0+Q׾ nIHlߠZo&GhOMls"',y(R/6z˰8K\PK"Xn%0[]eA L\-T"q?+!j>\I7< ~[P- l1MR7R+Ze# E^_OluyRlP*,2N<+〷ZQ&ܽT\wXZ}f%UGo.r`8w_[?ۦa&U51%"!(CqJvu2A0;abփ-; Fn ^9M6" wlێ OFi %c1ېO#0NhA#cSaЉJdgM"("܍N@Ҭ3xUd=^-y;,/t6 Vq1n ɳnYv䁵8PY#M B呎;Lдh ci Wo'Ys 3P?yBYrOS_SF>s?e,_>m@J^njP>ӷ-7x̶)i]m "BË ?O%\Gҡ5$.ȅ"!G,Tg_JN# @:yOs52SFډ8+W#?`?ɇ nIxkL6\oF[pѓޙ7ѩqRJ;DS|Šo\\PWGLU+bb)(uqVӦ(v* +|GlQkL^=qMu遘*O|Y(x Y[^)CamBHyW,B24 (w뀆$֣)qcfыqq$9#~C؋ioQu&vL#_ ZUWRŬ b(EI~,xPSUz`,DPn*2 &FPI=q)DZ^9TZȂv,mEe1^hiۨwRRk2qR||0QqN1ZTdKtE.APIAn4` G, aŹ%I$TC)Ovy%쐸4EO=?6| Ϩ|{r%gmIMOv95IAi(jS*!tD5)(;4goB ])'9 iLicg$l2^SE r-i%p-!-;@Sf4)7Ll嵺U(ya G:(~fя,Cxȑr> |}Y voeZTLЎ."A,-\E\q6)E׾L@n-Ҳo w$%$lDZl1*ۧ˶lV"t=d v)(ӓ|NkZf, F,-IE }Nr+'+ROdiŬfҕ!. Bx$70N1\x5G:Qixd8v(q>OL}N? ]i[sLcK!?-1M%W,̿О 'XI@1E$Cv]>9P E* }_ D_} 19ddeΓ[olcJ)ޘ')Y!qpSCh`WMՒHV+4Q++,+7쯬-5^sKJ?xQ`cs|y}S~9vbx=2F\X٤?HUS#tdor#BR }fY "@y?jYOӖ3cnP=N3x ⶻ%/Czlq ʦ"DVF5_=rZ޹jbgR%.ID'q/.q*>܆T0C7Q4Pciߚ|`f+v]Q@[XH8Ɖ `p؀k_o'K'ݫqEzmr) UG!{d)IlQe4u*a6oSrbN~oyhU`:S2Ob 1/-UvoꥏJ3[0"tǷF@Z_9j{LKQp0iL4ShL|q `öLBV(AS'n|EKS_#N,W$+)ŐX.$ƹ+mu \N0\ EJ|i MJSŬU$r"GQ\Kz mUMw mcH*o-R]P *]#r g9q4 VDN-:6(V}s(lc{Lh=4*OQD >q,d r!FiWQZ&y= |[CNf9h;{`[]eY>Ƚ)>XM7iN$R1κ9  i YpBii)#!Q^,iEuյCI„_9dl'Gx2(M^%0!TŬAy jZҔN) i_, |(KRHҦPֻmr Jn%&6r:C|ɗ EvhZWSThiM5ImKW >&rM!FGZ"L[/9B"#-{t.M6\ }\PՌV~93E uYL}AH#lRL~!kPG\ RAQv@UmkVTqla'7!cmhR7?NlD3a6m@MC/+2NXt֍NAjmL;1%E5䴒H$%XEBؒъm6"P$ $~0Y4P|r0܁Jn)gTWQKyNKlXGϓ׵l۴"7"r"'7|brJgjJvݘH#/ hF8C3mCnd-q-hlpÅED8XR$cl&CP@9X[&nkZ|S/#d:rB@L27 ;hU;%4tC3Hu!•1"7^-f,ySIR8c ]M*"2f~H ~S\ҝz|YlvlL| GNlXI cRU["XX44!c%S>n9r!p W`* l䉵99m(׭x,酦EƀWrn6~%=0|?Hqj!/cx!F?ki\e1R!+i|qT2W jJH !A1K|ڇQO2 ~QuE0q(VڏBE~,*h[, fVӕI Ҿ6cb5~">okoj<\'J0?'/p_~0705ӯL/nJ6l(Xd. U?F,IV2,)rTIS  -:M0)^ӭF(%$ V  d+qEo@)H*-,C;WjtIAvbh9ө" dJ@W啐M%j=Fk>8Ő4;6X$Oՙ-`:$ˊ<$_˘y#u&K+j`|Iq>3Z{Pq0Y?.A,0+B 9@?/^W.<,+ "4ĎZ>=e'\eNyךu%c//Aqe>-%NY0R \!D&(*R? زlA!4l7\C$#p[A\)>=F cd{*mXs\F' wXTG,k+v;={MWH$=A 8J}hAl`Jk+\-Pxh!3dzƐ-V@5`UH|f˃޹")M)Me.DvH J8fSP*;f}l!OHRȊ1ff7A6IBF2RaUrZQp^I-FN-p㆓[*4` Ȃ7m2MR d0}'/ $ijXF;fV0YXAl#o+\ 2ݲRU"tgHث}ٶRJ-5 [ڏH`O"Iڿ}ra6w`&b()ZS10+V; Etd2mXKba"[,u@GRwrffzcz|bk\#4G. 1WsfzyU9Y5(c}e4F'c ؔjpm@A H-$N]2` -mKa*FdD|nfd6yW'.yJ@"PԞKN)n< "1#\~\}>5K7=[R)/3þ<#-EuI'}$ඡzè_I 冫f@eI󞝢K=ޥoJH 2j^TY5oVcrhgql.V*  6[MNM1.aFZguʵf7u1YK Z?w8xXBee=uj9;Sw{$aTV7Rh<_.fLt⨦9OHdkiuu O؅^kOÏ=8PvȨзxN,-\c٦r2/k[o{-ֵ8-3tk]Z{&Gp <6fc8AY%H.! ZaR%7{J@ԅԯҌT(hխryF.h"KQ䠍LN9!Oc4̍ y@c_"a%,njXhe/m3aϐ UvHǪצ&&Ѩ{br$[`y nr\Q%'dʑ@k$/̺mM8P^!RHZd̡c}I[\rp*;oٶ[}v[S<Ɯڳe1#=F4DJ 2JҴ\rO~ΨƬ{dZ8!'Ȯ׿]9ex\iu !Wl,(YQq@6Jmc%51ׯ˒>M*-b0C #jsr W)?yI\IB(M;nk<#RWy\1PF#QB6DȞlq*/A\ٶØL" ΔdR~m8ox( lt@RV$f?Ql VxE9/?4_-BA귰Z[Ճ ^[UeY2MYsVgաEcۗᲿ̣j)H^THU-t[VKK.s|X Dpjդm1 qNahMr\{\9W~Xjr$vU •hzO ce1[į.#dnԸ F> zm94(%^K T1YoNYp~'`;9EM $e Q@W6R,0}ZݹNFr502mn&nSΙ&MǦ%Z\m(k6oceł.,̬(bא9]8A"p*1@ r -zN%QNlJ4ynVrNj `@HmhĞ(C- njt),wCTƯF}2\Hˑ䷒ iF=fˇV9[Iv*@ܖ(piGYXbMz aIN]#B^ )Kr'AȼAR\b.QC\YQU$҄[ ं2Ɣ̕b EL48G;T.#<,RGB+oMͫ\J3<1,X$7?Uu`?e8G&Pt$@t_cH7Ǫ6n5ؘbʌ(-KlrE`HchR$Mݕj?;ts3raL>r+Ͳ| ͷĄLRסİ1U{xU!x^Pb‘QN+ +jbA_ T1cJGl $­`V@NXG~Y7<; wŋGmJS╽vmi]b%bIt#"[$H*oLtkBؕ1H,i*XRCCSֹ 9 D9LhԆI, QENQ-Dx* R &(ERFc44r oelKn_dv-yKYx~@ ^ӉD[cuK#[Qn.o"N aj+ a4plwpq\CLȤ&V8sj1LƷR/E_M K4bhSt~iPcC9ydpsez^c רV\D i):0$)ØG,(hIaƫ+`h7#|02a!eZ X[A@\F_ Eۙ3;Ʉc^g&P)~|4\d%?zo 6Jg&QdF F _ /518AEVSrqik1Dy"R ȱzS"z\JceZmQ+AE4t~TpbrW$t_R^$Po{} 0ơpf V𑿛Nv,DKZBTWWJ _qrPԜ6S' ANbrZ]mE»t=@2Wæ#qCS,"S,\G# [W" d\|r72aφ07w#[&mgYvcr. -A;p5;xA `R#Ldm1::?ىSgŸJmKRhWidȵH0J? :M}-t>hd@Txe0GD2 i `\W$c׻FcКlr$3?7j׈,B2LZeva-& ؞{&edp]\U ( S/Ʉ[ᓽSii9O4!'..0iAHzg+« @'q=z`_8G4N(%uvnjR1f$Z]:cH$(AHa1 }AλI Q?ҪzW")9'゚VGz6'š\&I>*$5z`bBs!Ƕqp:rSXRKߦ$+|*>F*㊴;ӿu<1C]:}8i-n*}pZ\m! 4`M G~\P̕y*%>#N ĕ )aOnt'M- ;7e4ʀQ}Ր$Q-EdxU Aoe [[5QyF}IS?Q?AjNUgM:xբ(+ӢN,c H//bQ±CA$͐oaA\?%{1 &!{FP MȯHQI5kCN,ԾZ\:SW.:Lr.9ՐiC2% rٰ>%+,Ih6,mkgXW5ɦه. qrKμPOxQZ8 Y Sowf,UfmR4SlŐi!Q!M#n#딑m'u11#Uusw lʌHrb)/Ud]7&V)4ȣ#Q-D!e&IU`$!"ۗ`I* Ɇa ` !&j6՘2#pcƦ`F`v͎\EȌ^g$?@ T,39B,&YSS0ȸ ~'-7Ƅa%%m>4?h`!:ˑް9Vtaa{D ZY!G~=rf Uf>" \C&!}{ f.ܜT֛/:NT,u\FRዞG. .59qӋ8'iq]1E!=wʤY"1-nPWlf eb[K**Olɋ|R{**=eg";1}je͞H Xa$"KoWs\vdMzIPK IRy EĄMEWO{31<\#S\nl!m摊;X%UI%#Woُ|yTfÏ=1vD4k߷FH.EŐmee1LUW2#`JmtUjI*X1$oOَN)JU*z@p`Do]}&Č8wH 5j]WCVUU)ax,GĖ9 {/OɥMq#euI#sycڦĵ7,Ǔ͹Dxxn[ iѐHp+DYΞV sp"Flw>rS$c+ ֵ5uO9(ILgg.9G5l I t8k=Iek|ӕ@cƥҪگdksqQ!l(ijDyU1 }kHbڃnY+kL4fav2ZI Fs 9&&6dNV@ :x.M((zg KQў%3)~ת ŏ΋7# T|,~үf@ݿ0 sCoֹ ;Q8ǩx^,m(i 5`ht8%XPAƛ׾Y(Z qUbMj(iC@l<ɨ:[]Je?3qGr#qIjGB6d';Ui.Nd݉L^R,-ˇ?QSp=w;26lCD LvdReuh'ˣ"82.t""$6JyZ?٫e<{ bk]ĖPF[t%]XMƐ K./ O**gzU1[No}~n1BaNʟ*DIأ[hM;}>n,H650 -7+e3B4ڢ]2p fve brrD!g3lKh{#gUtȂ"ocwPQ޸\`3|@Y#1iSڤP||5'ȖKXT F:nBrSwȖ3Ep zb"1h+ҝdfa`UO~}28Y%fJ9mI2CQe3j L8t(z.L"u[ FJ풑\ڴpC(h<~ygA)*VG}ڿSdNa d]i-rCVbnmcM|qa#nQ\T6 )^" j)8@^%`ȪYIUiJ2jF$ hD6H8-CzSI XmZ^|`Kdq-n.7bCY:7 $c U@#ƽo;  zv Vӥ~xS*8]mت(I1Z\O`Rhqjl>xң >,xW }|PBKx`B&9jj:{BMOׅ*+u R) c\QKŊﷆ,\zW_"d6ݕr-.+L@fSNv@l Z;gk ً",ǡۮTeLS72lRn k~]b~3)x zoO Mo;8qzrB1>rcNm2zϋ2uGNa"ǿC2/[q'9_?'O+J n^PQ֣.e`%Dž1{ "z9nT+s.|1 XURL˅&siS2 I[ mW"D(^wRy|eˌad-"/-6-͏4MXŔ͏J핽5qs1r K'8r%"@NCs.4ܡin5em&#lK^髋HS5e%4T_S }_>ՔxyKKTj܊(f4(j{e-4,MrB,RǨ"㲴2,$S(bidPFRdd oS 4SvR}AQa(2-lPXP[shQ־ٛ;@n5kp`dq"OWc&N>n_g {+EN5Rdn?BY+kS&ފKoOSy:cy cŊր2RJ2T#ET*?有 <Ҧ>6$d (ЍH_%m GvZQOrS!{'Wz]F4{!(EIv<49+kÈ_¡kЬt~<(Y1W {7/+Zl~ɶ̓_ofynOB^4 1bPKl82M=c\EWc\ji!zGMZr7?ɆĕV $BL'$ .]$idROվJوC{[3IZgN@+˧0J|y*U_*YL$pind3$՘&3# 䄈䋥y{W$%/6#޿׸pPM+TSON<%(DM Na.Dw/'k°q "rƓ)56}~DbUӌ+0{ۀz@%$%6MufRf̈A/4y~ ,u2J9$S?r4BE%E7ŘHԏmbI[mӶ@WdJ; ?˖K !xVѵjFTMd?$PFJKzέZè?>ز q'O| hvz{aMӦpa׷)Zi\*7^sp*jąa%~!SԠ#zuqbUj cJ9`b4|,W}遮PMR+I `Q(@͂~c+9Ԁ2A̶픜@M2Ҥe5@M4f W"hEǍIeN[B ۮ mL϶緎D@!ɘJn >?F!{ͨƕ̈M@DƦ3:%F!@hG]VI2'+KN& :d sD㲿ukYۍ*"Y:g)sɷ3Zl<rͧg$%ˋu_.SDneV>Tޮvmq8j>؎'nvcSH hNM4IJ`"VW{|X. (ޱ}ޥo s,AM!oٮπD\mSQbkKp.FOP <#t2jA:VQm#\Pa(`)Dm,~EK%FcȴH޹DL4>KJ!!Lj )L!T~M!倳#P6- [w M2 -ɋ "yiƑ5 4q4F"ws tyK?LZUj*:ll)h;+[[l{d%* ӢDRBfCHzoA,i%ۃ?o(Y$[(ŔSj9 &*Q09`lQL39-,T?lfTIoIa s.-6|cir4q~ _rsNQ; e17ZdSAC͙fQ]si.mYcn"|^ f1ed ˾f.Oq{̭#oƥk x%~aVx,VBkWUyoǩ1,$G*wsq`>Oɟ7q6Op@ǯ"S>; l#N?rɒk6T|?.@-ĂŠK-7* 7 ѵMitdk9dtU1.D; sVևVGʟ ?Oƹq#>l <1gi)QSj+3%Z"qcwUB<=*>vddIXLmZ b-arc`:m^{>x,4R8x\i"IpW5D7^+Po_N.} *8m %>4K@rAOuY$=b;/s_0(*./aƽ=w@!~XS^dDOl5zjW,r p1q=0\T->X41Ue [*\/Ukzg\^ڂ[rw6p-N-VP2@\$+L[ژi>)YŭoCI7@Z)l0 }OC6U'ng,g ƥA3-f\G$r#(g*{o *7T(?vǮN{f<ׁH?_'|8u1M1K$ ָYں7Ɛ }luyD{bh`K[w+WzE*,ip\VU=ME*?,irJO $"o"gME(Vyfj2D#(vWA1fr@CIp)Lg,vxTM䎧Kg¤"cS‡芒peKP^lBw$'Y}fȜe)Kyj!Tn] $»ԏe7LOS_zfL[m'5o햲!r ='v8w<ŏܙ~ݖRܟ>_{MNc w Ogp8Sz([B -C˩9B'm%DB@p~yT1ab2.'0'nC1[.$nqڄaLI(9ht˄AH$Vf2^K5M"մw{orx"nGbԘ?[a-th!x)_;oXÙ8KШm4/.SI:f!")eu`W&2%pۈwX6EYNon"J?B&ާIt}`xRSLds!z2oRzh(v5)07%9PW( f! E *=EVT/&t؜Et`Ai(fMQ(Q,☊+@߬R05ɀ!e6, u GZ rSUPٱNJܰOMz(̰JHƪFuzl Y|;`0 A<-2ҴQuUo(i{˛|:MȒ*=QӐڵa|P1RmjfƟN[W*DB=%iY{eU}KQaM,Y-bI̴eV_r'4C^/34>OGD,H`{7kči{ vld,DQ*BE|i2wvFN%zҢ2ёG2E^LlÀMZu@(zӷُĘhv^dP ׿ߘl~iR9HG3F9[>@u`3Cf 1 zetJȄ5)MAL$!Z`-m@0Pmqx,_]^%^3Jsim5cV4RHU3żlԢ, Z\N8* wۦD1.eccқ c{ JQS )>Bh~"b7/J_Y<#o[IгTMaۉ @ 뒶@ڪBDLTPˈ+ĉFA$$\e tIn'Fk*Β韀Ix7pŔy>q[ɘ6WE WE@އzRG7~j s2baܴJ"w# %H*'+7p -XG k @S˿11X7m"Uwol "|mZ˱u~ЏXgf:|O:,#/ʌmÖؚ%i#*8$(|2/4MŸ_g'Gtː|HGvE/u?iQ݄a!#F!EOڠ9*}`1e9EeE>׾\ LH#=щh(9V?7=χ7 FD |2\\y+wL˗-"م1*$H,5-ދj0]O!;ds:1ݧ&;]mmcM㒉>Yܽ~$̿\1r֯ʷWz~pAFIe⼸SLAÏ6~_agۘhHjw-86Q[]4$?Mo7m۱o Uv T1 d6By(6dlGRUH47ݰb%G|x\CLxJe`nq4e iN2j!)2L MϰNjrNjry!Z-|q310x~2$I'5LW͘O4]+# "ORB!FEo?#{+1-ct}|A}Xf']{ ܏HH[#(-f2<ZdVwm"׈_d @\ 󴽣4]:'.jSnDz,1to(=cuB"-: cu迦M|l)Dh*)i%yjn=zIj㈣t*EJT̶N:NDt\.8+Z{}v >Mxd$J**1-1!ŭVZ\w8;bPqVNLY. NT-"+NHŘ47.l[_ݰ;W(]2r4{cYrF.xmarW F29Z:|~RriY`"rp'*rcJ:qHo Wzq~8()b/WtψGDkVʆG#ARi7£'ĥ IgG3BKܐ׃k9Oչd*OJ2'e|Y) ̈Gul OTP䜁6Pv2MH{wqZL]_ +V~~3[k:bj-):o(T,:?v\;ԁؔDQ?j;S`혒)288h9IV257 wޙ_r~h [_jem(I/;ev޻W@|Ĝbgk·S4rad 5`1d`7! |pp!Xze`ZpH bt#e:uLx7">;d#\ITu` Ǐy#)!Ot5,PZ)DO/7aW#V!1P96/W)* Ck*%Bnj>yY`Cǀe= bF7!b$gbcV#Eˆ+}: q y_4" bR'M$ohV!U,Y 1&?*׭fD/.)Odq Sh_^%8V<`AɠV~L@لpfi5]DfnqiS 3 52diz#qG!Dk*OH.=QiY2|G[\VO% E#zL홺91Jc11ZM @3EdI)%!RS"JG~ dRb^&D/cQL iÉ9H90^,_l r#XԤO@\ÈLӓcW_T #τ<;\"Џqտf_ Џ4|DH9jk"Fz|ܼ~@×VL@I7^D_,Ď` cJ6m=E|'c;LXf'85#pTxIp(ѽ'wR ;3gtQi4Ek:VVhwKW,>håM;V OsKHuh^cHep~a^Ex#E( jr,tJ}Ul$};7'!wvL|H{-R[ZA w+CUG4⢂bI690O!Z%+uP|ČYɠw9>(yq=FyBlODw$K~|V]>ww"sy6X iQ~r妏_f1:_jFC$ә[䜤@PbZBIܜܯ>l-i7J2S?,8v"F[%n,{fEW7(Kk$# E(@ڟjOo$CϘBg5cRz+uVp[aEK i~g(Ѯѽf؀iMp"8+J_J܀ƮgOS S->bӾ6kPdހln`ɦ+,v_]!O@ҭTw>9唢]"1YnT ^.vg|Xאnd9)v6Z_q-rM-qcKm.E.Xpd#4(b;`d5tE2TkqE FOwӻ'2[tpGi9G@UpErMSTX8@gI#$iƹ" QN9{5<:TOmnG*ۼ6|R )rk"3H,fbl,־141aE r&&OĠ!lPQY)coAӋ]y5Hh~USܜ@%`WL,c6PTLhƙH4׌m^cI)fAqLFݱlTT*valVU x:}8MQ03PFضPU#l&PCVusy*M-ZVɖIMmldԴ]!hk hy/zdC<ƒkѿl0 Q"~fG xąs*8ChƄ- e )\ Ni-zĎE/sb>'lmS4P| [Rx7鷏#I$dtܞ0>BKn)Ҙ)6ת&2uƙZ]&RH1+|[~χ*r1{$Yޠ{QmP-%j>>g k kci{)˫Y)ݥ0;j2L)4~(\r+qW+s M3W $dfi}Vd܀3F܈Q#,7E#}Ku099QJ6ǃ3 6sȟ$VOʸaŲێ&&6ѳ|TS:Dډ” e*ZiPrq!%Ax:rDZ⃐}2'f+/Ҭ*}5;,r)ŪO$ le8qòGyZK&Y?ʣ6mYɳGi6jn m&`fM}OM]~Û3ԫ r܁CKK*{\zs.hMй26 ^O|\q¶ < ԃG{3V۱;*l\ ]v隹Fr)+9lVݪ=@%+6^t[Tbxecڠ6}ml1l[,Zsp>*;óIJe63F*D[_gJ*IW5j>{e\OFΉ32/* q")+Ȳ4H@S#y18f.kɔā˙kuV܂y8LlF{$qHm\8w moB׽2,N"w,|PKi!=' `Bč5؏ d)wl:KH=,k[Q;BQSW EvI=8@$_|2 孂^\(YG6!J[YH( "dhGKrz4p+_Ye-^Lg@98Gq ֟f7kۨɏ_O(g7AjOg&<ffE>Z zFembEaV6wvVm=a!k^Tӿݍ1'&TFQe נ5@/vf9QC;SǾ,;.. ^in-#i:0CH zߵsЛٻPk$|4:o ,%+a+׮gL lB-œh ,P)BBr!)uvl, Fpso-{[6`+\Eoaiw2, qAA8bUQdnK2Ry=*?V2v(c Djbv*⣈q2E[jB@-Z$h`&K;r$EqBwm%rdYBƛESԜSe(+t1HԐD˞N[? ?@Rm26ރ|i+[JS8F ƐW JJ~g3-~BLxk_15?`ёi'Yoj+d& m۶cN r6@)׮k3c%ƑWmS=9,ABL 4$qJgԌφ"1)e_B#3v)D~Rh#~&`w>4ŏ$CNrUtʼnW:RqL, oi`bцٷ%CGJ+BI"+ڙMJul W29P ңRɢ "]qnp?%Upɻ)7QٌgFUxzZ$bэ~Y!FQ죃;@;U=g9ic؄FV)mكe>)ɬ;lmPZ%-Abe7 XڥZ+ 09|LZ[u-"(n1)fBƍa%tP$\nX|vLvCȽ&Dd(,{ !;6ZFۑɥ7U:9p'et/mM"V?^@%1iwLJX޽1ux// usCkEdeb~ɠZ0L9d;o*ѐ6nilYV^Μ\ǒP[a3I`iSO|Sem1䂍@z A*-v9+񥥎 7dM(M'lC` =WACWP,;fv<%0@ŧ2^zWqų d &0>d%6mMd`NrUA$-bOC"d%AoC#OnI("K]0q" oV ѝ$Jñ2QgҕQA%,Fee'|?陘u'#^LcVkM-ܩNyï{5&%jmۯL`VD}\ Aۃm![#=V{C )zNLߧ_,m9°3GV2 M0Z)PIQR-:PdɆh)iSCF6)X!yi63lW72ť$1ښ; J,O׋qϬKBiJƕ 17G@'IjkdrE -R(nymbh~?X׀|;YF:Uմ۹# 얒+) H'xqLtI^ab9v᝹Kmb/w;: 8]tBPt˳#$RgcsM;xaZSCC4 2TPֵ'2(GiVrkѦ@ZF\< ܂=NeE4 v:wXW͂Fn` F2аT 6Rl""_&! kgh;A-=hT~я a>#Ba}* ARP:eDz45I:u8x0A=ۚ2;NВYܭSYqI'ѫCvbU1BI@YJKwdTmοk89$n娧1$jλ5S0yhl=N+3E'rdgn*E2n;1wuQ#"qo DL2ƿe]H#;;W&͖"b8E n-P|Qe>bod Ojm,SAa}_ݒ+#IZP_|bT <%/%*KF]F^AKhTȈO*~Z \$_RҖ,h'xL3rG?N@=ͨ (ى)G+:m-%'JӀA4vDEo ֦/dq/EX*JhXwII;Yݦ]N "&.S1{u'/¸$OF+n _s tu[M2UQ2*ӕ 1: Ȧژ'xWkuFf0Kn/J 1I`$Td[' #c!ǧ W|T08TQ7/Tdml%Qwъ`MɥM6ag&1$XVt丅CGӮFp({MgRZ ],X8Pw$#l#ܫR\<%"~27\r0#^$3pSGvEccI^h˹gՑ#^ =)ǽk4۸lAo&o)xJ끋1߮(S\X"MV`n?4S6sk, sX1S*E؍`%.IDp/ѧӍ3~޸|V㆓)8)Ȱio'"`(pS5hw;l.6$ok/:cHAlX_ZM2 %Ey~*Rw=Jh=t5؞B_*dRs.%Ϯv(- ԃ2V ,<+BGok@cD+JYrI&2Qqg8sza CF@wo9,NFp UsG}KC>S'('ddSS9I $"42TԚV"C U]_KI( vVbKZT7><גJ|G56#sB(PGe0nߓ;Z$$^ڵm4̈IsO)^'|ʯ +Z]l.}7)d;z6&=89G@ x|)4=9$Wƥ&[1H`22]{ 0Up d@7!{ "!+,J*=/WLiScJfL3&iX Ri\Ȇ62;4TZ@$MT=ؚݾ)o5ٌ}Ol!-5ȂElC&U혟 O,ɬReDWZ`bJj qx>;'{ęq,%ěs+Ix~C2|L' iڛ呓1$#z_DYRIFN;nF勀onNIlCuڋe8%k?o#ź+tɚa,J5Ħ\rcQ:6͊5”(I5s9S)=E+MIQl }/!K k9UúBuS<}%$Kc1p)Y*uf[8;bFhsɼrۓsKq/Mx!B7~˸Sv^ڬXK=Xe?/{|Y`k 66%Rq,(CA)HaW_S&C)a1I5G,YܓCXn sھɲ7=O+De +;r@>3=q6)Bq4 T2R6 yD uz;+q߮vyL(M*;`0R?`{dٙNmUܒEUhLjARX"]1kfI1'2~E=O.gs$p[vU 38Z@yd݌ee^/ Hlc'kSй53< |ΚDjԓ=d|SB)%I! 8AQ_ԭD׻;P}4Jq}9IMM 8P!&mn ZY2m%'k>Y;o臿1+Z}R|0SM+wR\uOoDO"O7M;!,r2I*h*jr1݀'zm&+h0EƂ_Y>'PnY QrKkX$xQâ O/ E6 J+mF`{I4JHbYjf=?5*vһHGA?v^ i:][/ _o#֊c!GCrdXE=Z)Z&<ܓU;FRDE;HUd Yq3h(劝?0Tvj*scІg8cyU5NSb?栽sVHܷ9&Z4c24H_hCM)/b_o *ߓU|bY^fNT*kqy7o镩U01N}+м:Y4$~e" iѶN9Zp ễAѸ':M[ɤ1f1=p82CFhea<ٶ+blr4ۈ/H`Jvl4xJۢ 9l02z=+{ըABC.>h9^2BnA)J`;i1K ^$@BuAb<}:eۅBSMl)Ȁ s44D\nMdG_\h;WD`rSzvQvw\E@0mE% [6B%H<̌kݥkb)U#n]XEo#jTd@Z{:WQef-y0W$[Wsˊ뀬f 9Y|M CZd\)qUB+ *%Xx`WVvZFرUR,ip;bnzb4Os4@CLQM\{V+] E胶 [hۃƖ֛zT[ZmOZA1BJSIZmw{ Q%/Wn>%?ZcISkOIXִ"0Rx ;TWliM툠 d$mP?,tcĆҔ{Hd$M쪒?WFP2[M5)6 ևڤ` Z 1,dmlvԚSmZI R: ,x.|L|;#IJ*1-q'T~/G'1iLDmfQrZvB^J6ml_:yVU~=\)ِEEk7ҕ>&-.1F94be*;,$Rx(b)f,F9I`T@HPHCT\ĺhZ Q,qPFVKmW+mVڪ=2At/C,nyFM#_VHmSHԟL5bXwF%H^ 諩˫]y bWUT`  V% I 21X#G6 E9yQM%aqM`[ gV/vryu\"T鑷;Izo6Wxu M4cUY:|zҾ2a8. )cJzͫw PX-s}HZ/![?_ l$C  -ljOpHTZ"V&Žԁ$H>*;d6nY⎠t{I"$gV$!ʍWs⴩;= *B@QLn#%yckPA-HrY ;Z^\@62,n!~!ROl)E,WFWhF%^ͥcj VF &;n[vg&ko X4^P%h7yme0tEVo/;jZN٩߲OQ豇 *MhDĠ̝1errK8CNC45Bn2{) ۮ)Lij1 ,~$/<1nj1d.JBs fO'GBJ- -Sy1S5ruL e\NHZHj2!\vaQ$ Z֏RMQ^C+ zdA |)^^drBhB~3(ctq OOxS` ~y?Q8Vup?HBƢjV`qreEljj;f9$U)(Iz[ǝ]x<9:u<@6l02%ZNcǃr[`Q-_@V?S"HRKDj(Ac#ewvv^|\d(6Q̘Gfb,eH -|K4ZZ2.<kJ?C2AHZJٹ YY4ӵV;6d%NM2ت:_@$l%[) F(RQ-ۧ+۹r&)ׯE]r*KMrWjR؀ZG f sGǯ|yMP(!Bʤ^ Yl ǹFٲ0 ,([֗ʁuQpR qգ^D){:hQd(-nAlANH6XE6NdT@SE*TwoN05[ Tk(8QKZvHQT9*W}'I$5QDYvaa2ZI*(|_ҩxi"cܚV͂` N FZ`&AsH(#RDdƦ?ba,1ŵzf6HsuqQ]~I(zϊ|.WrN

-,ځqTSLk<yC)kophd'@)'9DL0 XޤI&\#>9USY \|=2, Uef-f(A!ѯ6nEֻV8ݰc\C榄6U(MF+^5>=l4 RWd? /QUr~䐄BJFoJ\, |$jv.fM%pn'zzf\e3HڨSٕ H"$myVՠN@ ٵ0jHK1H]3HA(!ZmcK#H.H RKYgb)uSٰ)]ywch{W%G FJE cf iɐH J#a(b2*@J ʷom隃STe(eea;QWGȒTGcct$d\,U]:d` (=2v״[SIc(!f K`;eM=w91&`f)6D *fV9 bza@Nuͦ<"V%fdPدhsewAw_qŠ>zuU&o%LPT b9j-j}:r/%;n.DZ z`(NYFm4Rrl5peuQ=*UW^cFn õraclOqZJ5T-~s1<ۇ'+khՀ_LѴ,]R0e!K?zq?(jzx83"JN["[ h^L2q`T oįؗOtMl"]bbՍ7G?PeO|kF&Sg&Kq#j'E-_9JV~*~|QmrFjzN[&H~-?gg F<2bkJ2jvzfpPQE<>Ih:&fΨI!G51;qSx#I*ZNKW.OO|?0q2ݤC2Jk%{ h=DFJn|OŁ%M98*(*!Kd8 REwRl CڃDH^.&5@/J2>rKX 顐7!n_6o :Izo˫}9-`W-*.y_5^=1^+Tj 32R$ [<$ +phv*6 qs ҿO%9vu7aMv$ PnX㒇6*ה9nZm˺iO#_+UPs~l~q,:O@YIm2Fh1F5+ڕn^ḯq+["N.zǺ_v/ ̫P~&_q}Dq"{ڞ1'SD<$2'g _=cp \~,&/wsq3(Y~fMu[a@cND(g0f6c}w*=ߨw\(A%)XRSbk&+P:BKAC`k1sK\TA?FT^;Ӧ)U4}`bbTZn+K`b/>Uw?QMK}J1M6z V]QN]E4[k*ZQT4(AZ1-2(Qt`dXֽId:RwIF0Rڑ邐J ֕낔%Wz#RDR鴪6pRRtjT}46QHMcݗpG26i3ܭǥ5 &Bi~i8j[#4Zk}Y.?E*7D;'`#B|l XHJPjwg4Kœ%ó'zW9bم*MuHArOQYG(Ȫ#ʩ+N| 8WE$S6CD4Cjj9q*K~80rDn|A_VcMdR e.Ӑ%Hv&ܐO!M z\H4dg;ǥ:SwD”ު/Pq̨3K&&p0$Z297qXeV i㚼4vUU^, S"B)x 1]λcH'  %Y𠌆W_T*aVcvĚ[@YIExe&M$#G܄PU}3raV.VCև̞r9Qk(5taJScH]rjrk^(v9. mj~2Vګd[$ϓE)E'lQ:f  2V\%%.~2ń2GMN2q楻kO x;'$߁(XTVθP֛ ckٷ?qv [5O3%ʄ'Egc6ڈq1Z.rT**qzR '=Y]ݺ${OYyU 7-O؏ז$KypaZo|JfI>fy/yäٯy084tp#ۍ+Z**]L0@bE 06R{cƠ+i)ޙYf"ʉa(Ҏq̞^Gd^G`\!SKR?/yZb oX|~Ř>-yud<vɉ MxfU@K8&W>s&TVeO?𫘱zx{M:aw i9GCl6u^!혼LW] =G )JraPjjѱm(Ľ(ze& \F2I )< Gab͸p.\Czr$:\zާ~j%adA&nv eȭۻ :2L`D;zhhWl.:ܤŐE/ݘ4\o]eD1VW A_ʝOߑbB+HInGDvld}Aa4(9v6l1Ta FQMm/bo'lŜ 86W|Øqk kU.HhWPn? ȄY pbbqHM"*!Av2Z3A2DF{jS mo*Td)-]%IP i4Bvĕ.h*0[Bf6X 0Ps=rPD) _s_%%Wlˁ\6i%z r \@rE*[2DzWg{8#nV-0Ǒ;:{e`DсWV_>fݎybI#n.S\{c/ڃ֛vH9 O|IB!vo]!5MkAZm0-Wd;NJ}i3䔼 pkE9<̛nf.hNzG5mBՉZBhUf01%iZjd "S^$,geɆ*M۔z,eRh9G(.xA* ɾ Et@^]_ 5%~/_WcWqpFI* "WC7zeU%1JA(>$c6<܋Bͨq%"_FN_jf-/!$.y>etR4*#Vv  >\Iji+ԭ(MW/|1d-VCňZdg, Y5zeVXivE9+\.Nݿf?s"+␑"ǥoAZ[AOO(cm9aWR.{d*Uj4_^W4@)^:"$f @"R(MBý2̞Aozd%rdzqrTX`ٰCqF򔕈ˍ9S#/ڝ V#TE_^Ym5\1e+KS9 (*va˄-V;RCصj +rs75jM2ɨ4H=?vY\r-+1^2PGdqc,%ۼ1gjz˒vc]=1]T/InؓmYyt=2Nn[RW;`w\uR7=Fc4FP@XИۋ%TZxh҃-QV!nA@V w92pn&F̄Mͻ[L ikl07H)%E5\-s23ux9 w''3Y@$BkQCZ9;?#*p6Ċ-|pI. @o,#JV⢣VC-Z1'`pئ`JbK3Bxy$ G.nęG},>/tf*8Nƿ9dgѸ5* 4Z.=( XdlpQ/ H=%N$c{:!]LR ס\!E?ݝdOfq[ 7.өznqb8`E/: u)UX!SƘXfC {aM7Tbd\ H^NR4|lVSFMFOՁ8INZ\Sō8?|Vi6~ii|h8kKKxo+VSZ`I%F4_ޘ7εwƖM/sM&t==M5 VME-zi(;ՠSfO =Oz |YcAݔYyCXB])'Hׯ|Þ2("_?$.cNx@'9-ݕ3\N73;sA $[fY@pB?OTO5u.PQz?V( ܸcC^/cªq'mT1iNs_γj7GY_Qܭ2VvsxK (owUtZl9F*@eCQ6U n}6^^ZJ)=c LyUçlBjw\yPm9n,ueKӯɻ!J.7v#+Gm"K)zO?踷8PҮN$`ADq Muqb)BrПm'ǚɻYTpz! VOC)#NP`“.AJ-2e|,LQI/Luz ZUb@$+ )ɦ&kȐ$)(湸 @[;S @- *,+}[B2YZkVo*D5EEh?4:Eǔ; leh'u44gI (.UEc $xOOE-gGf<#s^m'G! @lzoZNk}";tb49)`[?E9f6~7cƧuq u`=n|Q H}amd\#vE~#եG5i~}۵C~?,mՀhӣq?c d˾i:O7&iԁLHnq만1hSOF%1g8F=$U $$aMDQ'Kԭ 嶡$Jf+I ^q~%- E){ri:9/Ri`R?M(Do>xoJ@Ԟ{(915>GLm٘B) .$JTv"9@4}Xո-p/ԊR6RNɝÕ@L /8UVqL cµgO s 6àׅۡx\\S½'ޘm.qc߭\S¿ŏ ^݊x[bo:%> Ky+0hɵ~SKKip) nE7˿|XjSK ,)!Oa&M4-UJSBT nWgDm@QWeE7nEo}=qXЀ>[3SI,˽ΎQU[dMĿV;3< ɚhTZ$rԒjfQMedEl-_P9\Aۂ6DU?̇R8h&zwḧ́"3RdY-18OcW \EA1 %ץ uΫssV޵(zZ8m"(1Ҵ޾Z-N+-UA`~ߧv̑و0M9D|ehE,xG4*iכ`w?ف[$|D`Uh9$P$''J}77i9g y"3[ I&)t\pUa6K0.ǥ3hi10&ǖR8[)AZxc 0H-HXvUH Sֹ1^cWhGf2HB\4jWdta[!59V' J!G^豬H9]KE? o"hsN"{kqux&CJ\1^j?eaEs6G6Df< O%Qڎ`~\ -1VV4>Yp#DBehm\9_ _ -pS(_C'.ER M.f BhSg9*d0!- .&!wXH;BTR\CwiZXКv*`q(?vglXvMHBpIif"Leq/OL;s7 [VST;Y^o9gHCZw3cFV)-2GF76d Gw 7Ku4>P uUY>:U8c!"=R5vhѡƴ$ӟlbXd~]om.$`mwq¨$i~. 'Ù2rQjRrfU 5јp D\eXnقqowCsI#֣Kq'"C ..` YrYw=4}pJp$aJ+5CQq'o˵ z8#+&Jj…Ds!W5.GqCjPAάzSiɋu+{Vmj;yL[ R?nBk@v׷F" D^ِ;4 вN\қif/ t1m^IKj=X9db8^DMT E! dC}cAGIT/%)Em!!E+=Wbb*rc$ӕH纪DzSz?mdA!OSඞ;M8HAF"gi2%pgaZ 2= /B!ڤmmQs$c?i׊07,מGn^EU N[ +f~rq%;rIHB "#gQFPnsCw6 垄%'l;L39BF3@НܸxigMːm 9/P*OP[pq|rM)Kd[ ʊ)n+B Cs{oYẄ́r8'7?R2Acھf2`}ȕ_/Um2"QS1۴F7<\;wvq^*U=olX=0;m\qR\EH¡MAAd(l-ޣxX§ =lqkmi>C(toՁTFF,]Ƈi| k§,'%6ŭv\ lSm)\Qm-V;#Jm߮@??AioRةbcV1ZbHuW|Y-vt' V)XAZԝ]y)U&t,r`sEK"'IsE,6Õ{xQn ~WlS=Fˣ&ȩ:\O\ F`j:` Y{5:sR 'b }dl1RohmT׈Z|G}Fo4a>mmh_2yYdm%((h7 L:}x='^Z.l`<-HJ~M8]v9q R`GdTP Y23&Y > :՞j^_h%S!oV6ޞvpd)5uV'oSy%Cc۰PDn5 u4ʧ4X -U D;sPsȒ!Pֻ~FkIMf_IU|W{l2EZr S??eYF!@Mб*^"9\7l )ۧaȱ(ZDT]U"dQM2ci F:mI$ @CNdJAKzȇ{ lfC1Ƕl1J3iҽ3̗Z ϋn7#xk%[d?Ĩ~N)_{ee5<&2XsHAr.0 :[YuHyڿvd~ѦK_bx.õ*r8oɚۥԱ%A૎2!˴}t/ƙ̓c(im$WӘӵ&jo4"*kV[{ X$D+ E*rۮF)*f:SfJJNQzd0qL3 .& R]<&!T\VWtnG^q j݄wD4p"~<~&ո.)"HU'#T+'Ad.zrБ?˔wjZ6)ݲ ry|g̥RKBi*?朲 Ca! ''Y Ԏdž]Ê*.--sSB#tR G/d/-F-@)0zV\-T%Oܛt^we!\CDl&4υrY-g-9uƎR;u\7ҴX {!U#w$24@_;- ͉j>Y|p dQZ{ePxB UeهS;6rZr=rBq)"*O݀iQK=LUvS,.̞f#JRM;Y2H.Oȹ¦pO\K$$Y҈ӑ;vXdeJ'VנA dڸ'akz'4" yib!+4dScȘ. Gm;m$J D I[}땡$~|*um)9Q]א`鎣r:iJ׆Paa{olź\4й#bA/z|+%M#e:.,dM vomsr#EoG,*jGd |*r14 P52n1Fj7FJQT'5wqtdZysu8S:| nFr WE՟tY}7yesn,6훂}$jxcS 9'-CӪ#5seöTُS)GPӥG\*.߀Dnkgaiᕙ[AB|=2.\6x?rQ΀T[(:# u$ȴEUToJVFD4)l0DJ*4kUA!hAr6c6 R xuɂJӾZ bU4뗂*D;W3["֯,H'fm9nG`Sn?ґ]ZB˽x#67/ZV`M*̉!M(^4Sw>=)eb?5|xwIn?0ՒNBvlM2d3ɡڈA1$8(91؃f4|eSK%sE|ȁyy͡0YD23Y[kᦩE9ZÖ6ZJTE5Ҡ#H ŠqiiMڹ R HQb{d?L2Ra$ p-H,[YUِzZiuG#Z/?wg|3=N)Y-?lcXO(.+ak-@$i=Nt=|`Ofyf7jTJsޏT *:5=s`rXT]S#z~쏏.Mߓm'P"V2keom1iV8"RS;J\[Q 4&HJRkn9cbcV*+N3 [zVR:>`i/1mR~3A]϶dF-"f^%ih6 pR˯cۧER_CiSC䁛4NGUKK׍rM?c)P%{%uk˧?9qɜA?}H/Kr4v1\wsֹ3N [9l»p "V.iCX' 1ɇ^m`XܾY ӐۘʼzSqLipkD\.&c-H̟-,mFQ'V1RX߿[>\OQ5Ʊ%dO%}@6JZ?[[}8yFP"ifNg_9}MM\{x;M7)7zTԓ>@,h1p@pA߾Vy!4%Hʤ7Lr#[%-'Vk2NP 'p:dr -vNS!-6*[Ψe !<˃v8eY'x^%8st>YFHcnY˜+Tk mLtKҮZ3,HhFd#U9ÈlT*DWn+o$ ̈́0 ^m\*Z*K"G)S-La} r2ذ;)m"&sJj@ Lg/ˢERhiCr1 [#͔^h {eDi#?aeb5柆I/걻Y`vRB[b ,Vʋ*)S璄lq~.r2JyGb2hoy*7ج>)7:vȎL"ʛVӵR)^XBV.JzO&N-c2@yYmy۹-@b8 wGG^KUPʟ6R$qKd/YNAwm?m3+Hdޏ9_f_#<õ.B" )S7fL5AP8E$,$*#Yk"j?ɮYx G#xel/CLPSEļEii(c:LiK?Ezぉ.JS[e7UH1b[SC8`›WX(7ぁ*S, n,è)`[qTIL%4ljaEʼn-'zEōW)iq)icEx@l1ejf mZvoЧAS0/@}l[n6&K~l,xmkk^|OƴkNkb44tɮbI};ƛn0R[v]ѐ-bE-QZ2%k˷]$8JMh+!*^÷T[₻e㙺s1P3\W Q)j{U1li&qiĊiңk%6S^OJ3q)RѴ]&?ZӖ)EO?ofI}>89U"-"[x̒J,̯ (2;|Je/շLQ#}Lc~tEd|MyF fv˗2Q/2,;ּ>$moD*#j?Q 0ݻ zcz}Vk.V+z(9t3;?>~ C2Kpw^1](ӽ2Z D,D-$@uĖch3JZDf h.H; ʁm g[ ͖Ǔw㸊":;JbOI,]Y˖j3Vfb1GRZTkԟ鏄mOV+:lY#1aό.KZȪ`8Vd51Ro8A`'88Ws}Y:Vt?1J@1 i%׼yĩ~WlHI 5R¬h,6I.Fap%3HZx8ɲ][=1 䟋윙Bcߦؖ&Q Sʤi6gӸ)pBCrbVe$$ڵ%N<q))CfĠ c@XkP#{KpФjvDIy`Mv p7|6E"a-Q%[~/bsl-4>MXRPS?#rtu/业TZDAilQ1憹ݕ*lw$?$杢1JJ౞F i^ \,e3Gdckslow1' Y#7'OXɿP/n̸ 9(+7ŕ9Ze\@(jOm\ …H LђVШ҃#ca=PARK5z|(l %w]JK;u$l3DT[iҋyJJ`9 1q2PPXD[d"  PG e4Vscnǵ{"diBW7ɇ "*Ey7;l'>e~h DۨHoRwW?$ا&89%?,0seG[h] z(]}4~*fF%x{c 0u47S]mc( ;6c >?Ura![!Jt @ @Rj@"E969LUTM4緒m.Oo6=o vg[V432)V!JGĊlR(چ}eY䇶Be@Ym-ȣ ^XC-e3p~y*}kz͜qs- H/K{lZ)DKAŜ"mmӈj[cAmZ[+E5! krBQTc|dV"j.#R SJz|'~fl'cv.s˗top9=_,&..LQMP$2s@uuQ֭l na'/I)ݕB\ OeF^XW$H.1\SuI=¼ !Ui$v|VȈY@`P~ 't߇O,HJR*,۹&Ci3MbH]oC AfѤҞ)-҆e-ތQ1ķ no(S##L#H@Ql7h9Ue$jQaGe"6W؃jcEdxLZ2WqMJbTDJa[+ro2n$]6HS|92[U1LXN*q+v[iN$ (~lIL dż{0MG2s`u-a+`x=˥uA+uڣ-4">`b!'"K1pf=17FQ#"N'2:nYqXMZ4I ;%8y#tU5ḅq$lokZW~ˎVMAt`v3Rt^ cŵ0JI4٦R$X+"!O{Xb&TTV<{2+h",ۣDrO>g N|GI\r(>lY9/8Eb81Or%I=+`RaZȾj Ri^_,#ˑmV4.Fb@)}UXlbɟWNWHٖ#20a*O'RWX+3̭q}m[! W+9[-Qcjn q}dIf-p)}_& x}I*9|Gw2f7-z CϿ/c;eI,P..;0?tEqvcv<(')nI P4BXD# w? E'%5fQ@W~Ev-\cP"I ZwV-+d{d芹#hޟ܋|mY\ِX3$^$UV2!FOQ( >]Vv-Y&!fR(%U_L\|Fo~_Z5]LNj/rq6ML,Wj99;*@ߧo',ADjk:=b=>&#iPԊoQ~,E * ;W0V$WtHNC!a#e2\"vT%V#sNDr?xw^#F2FT jH9,Qq){-ͼUK^^M@y?|YvH24߈'oQLW&u=&ZzAG4zu(lL,k#quFT\`5.kr8dP |cm_f2HHb pZН`e?˟N_? `f:m, E(^`H{bއ&tJl;-fJޢ(ZmO +C.-wޟF,mxhMקM :tPBNC-pt-pZljޟ߁mF(@RR[iuT\SVwz Rz| 9ъ/Cʛ]A mD-,[|Uk ԥfIJdJER~DJk)e$y8#>9SAKgW?"B"]\QrDKnj?ǔ `Ziڔ'NlH|q%Te5ŌlN\bdsjr{W1FI),M*:R@u7CzƵzu\K38E4Gl姀S)kLj|91!VAF򩛜wܕipa[|ͪ\:N"x3ح儢#l{C[3\G/GF\1+ꚅb})E}Gn‘݀D7E)Q&Jʉk%\yQT(2a!eZW,n] Ƀ1K"J/77rOw9EPᡎ5ܩbv| +/H5,e\ Y iㅬ\X*D95;dN͸[X6܇Ò 2W0*M R&mpWnC|$Pu  kvKy{9#ܚzvu;2,yDWl6#k5zIvXqsg>څ;ʜJraGqYaQ1IDnY@L, ia.4e#n~7 t[lI_1ŨC]#r6l^ǛhN2zq̸:GV\#fe䈬b4?|rA$͔UK brGDs-(N?H|('jm7'.")'ծ4jBO*ɄS}+O-4z#, }\xZH [k\8I/AD1uϛYWRN;*:|\zdoh9#vWI}=ЎLJl3Vfǻ1.'$` 0(e~Lp$lcG9X4mc`, 9C5l36vmRaW+rY ɁmƔAu5s)jf,@iNǶKm`R#,ΨO5[2#:f&aˏRӣ. _MR/S94@8ǷJ@ DOE Ie5;%ɸ mU;uN. ZXyWg?Mkn:S/'32eD~pg׈#S1r+lKFiXD\Y.^!A([j:nD_O.} j;Jq1,ơz)waF.F Dg'/KICܺnHyȗcv881_-nM7RKYE#R~xe\M!ɼ\i%]jY\ 9cZψ^i,_PQѶ*^_?g)3f Kwg=B׃8<9y2rKmʬ~هpŵq%8P 6y*XǗ]i /RϿDɉ% bVX؅*~ѓhkjwNE>JU3qu_irm1?$KGt?d:=\v4ӔL#LVT5[KPNFT9dw@^ȋlAPYQA<2 #1<;(<7̷'pmЊ?o -Hsn.UYGvȝWȎ`&//$`PB S*%w:bx *JO/\r,[' PAiWMk*Jl}YO,V3RvXz?X95-TW̹xWg?WuwSЏ ČKB VCn ӭy?X3:B 2OR!x+~|_aZ*H>(Ktw$g0ؚmDZAZ Ǣp]+7ɬE76U0؀w̨Kg:2/ЧXYK/"_f6bc 6ј*IZ](j1>8VJm,)ME R1J DMp lX:pxVv.# XlPاAPF,Wt {v ~vVL !w;(9"0I,HUYw iʼnbr2;7\[Kz:xdK;nzx2,H.reMI=ɩ$ƛ"kS_ 9J96[rH-rM,ˁf'J}|Lqz6 Va!hqjw맊Z~uHiЛ#OEi1Ib梺 Qց{ǹR?݉O$<%'f;+. |1'/ f>Jj R´nk5_Y2p{ޏ IŽ5dĝ >yNdUŲToY)]1M!"\&q$mET(%EDZE ʤҹ-v)d E&T1-^W_Ƨ6`:9q-sңGzCQe$Qjm%|Nmq׈ P>53O4X"qR*N8}Hw,qJ. GS); Lw+ 6mHchF<6r: mb*;Y (Pk!=Ǥ{f-SP\WN.^-:Qr4$;۱4|WvMp#$T:vz%Iza)B* +2hPM9_~`ѰP&#"Z̭.>s"d1KQo+D |$"و}(Z,SKyWr8Շ/0%! r4=@ĦQC*%5J jdi&UQE Eb-2*zJ%RhZi})ֱw$`k@| -Hi s/:8̟DELy \tzn?2HZa{5{O+XB2qS)3HAgh,c`jig&ˀF#I D=,[oHLjc*AWu Tר_q1ŎgSI;qK0Я>9sͿ0O&ȅ452}?ORfŨI40IHjL8'{ʰ(1y*Yeg- )ߥ#h;onYhSWBƽ)ٷN\<61mJVCN##e7KpOrp ʋ$$WOk-Ϲ][&Ny#"' ,w0k,%+sRCOUoӕ1.EZ%OQDmJoQ`ҭޠgQ*QfWk"D@1ȍN)HmLv6 hGYE `Bb-tH䓌Uj1*9)S8+`J#bh50\]HU(?g/4m_ FB:~uUgC/-;&lj؎9YKb97p+G.N#whV~ Zcmj2|Q=?^®ܗ?~,xQq)4ō*G/p4LXG;`h!w{a^ 6S lk)ޥ7-wx\HK| iMSA& +NcJ@~yE;-АuA 6z|E)bCI&B\ $dY@0/seJSl ,?Xka=ɢ[c&QPKQ*@Ɂ*ǕP9cjEr_2J-mnu>6#L A=`Z,(,lCo`!z59` CpI)4j'(js,vyG Bd;~#tn&{@tZW{xLEtqԝRDt;mMը}aV̱ ㉓x,GӮ\v9|aAKFr-wq6@ Ŗ6%@S߁8y1BHxĖP[e0*|f949Env]$C6ZxbXh|G$ؚmcy|E剠@* lɐ AAi?j9T!nF+ɸ -#:c⧠ɣYƐ,98Kљ1 f2%I+^Uc>NF>>dۮS(j+xA4ˍwipAVT Ľ(I>_i,lല2=yCWǾFF.C,+Ƶ2.4Ⱥ]*J]`X($G S8)RǏ^dGg.yNJOV)&"fqnٔ/Ù9 O!;__O'fD 5kkq$FĂEhAOˮ } ! &STdU \N|D_I(I$Tw.>'ii<cCF DlӏM<{jZNխ)PIr^s$*sdmZ*y13)֜|+sNg􅭝YrU.dAǗ6nMA$XSlc#S$>+[27/o`[|j9/Yt`cq 섯T1!v^$I.A%ͱna4^"E01Yȶ&T"iG:nU/-7An!چNbv*_[l>CEXV1GREH47iH2>;,*-E1`F?~NfORctd, 2.^Ij)Fer݌KxV İ^tn8TY4md7V.hA(W|?iK#fNxa˜8LpyE)h?gYv⍂ɼu GP5iBVv2[ P*oC8r!a2B)٫[)\ Ѳi&kqe 4Rـ-^+G%~iH7joYD0viU@9k crɔ$m]\IK ٴ6@-ɷ'T#實otۿθZ"+Jo{ҸFx; C^)01Q'+ZTC~zn|0FYpa \W Zk'=vxUJcM1E8O+M{ OR]ZhH>X )x}4\hSJ +ѓo)LɊiM%(Hp$!djm0qdf ߸ާt.ǾT%k\zSd!5ݽ!"^X\9ZbݷQ-wPq%cwl= `VuCd2bDZ{6Q{j&L'0'N){!Z Lm -GS4M4ɯcv,7.DnӢ}2ן`v~\xyDߴ)qjgW6\bK$t M`2'~Q}#-sC~K6V$cMNk3b(=<1_im*IfPWi[3O$a.AY\ɧ¬ 7?,̦DKdVc3k.|_ݖxB$+,ig$rkS"?޴oUv+evXnT| &,]U5(ň-QA9>*q"E$sA5Qz1T/lf:%tNGwKT"T%X֪['$F!4r"bw{yJ7\>B7$^2#|^_ߊ9I?R]w{-^1P*AzUB9 ˊIy׸[ORE^b%g#ySnHzSv |0 ҃ XFb۾DrWr5;We[*D\ r9m$ `zúByBg\40)RJܝaw,f6'v %DMJӡ긳RX m _38nJ! j3e[۷5ٺ˲moO^SZOP:ڟ~X 4eߑㅔek;wޟ|MD Yt"ce;FH.>i+A`bWN7|\Y>qe« kk֦ۮ+M*(bC/lm,2 M/lX$4ϵqP(h޿VC7#=2GcLQN3)}Sת*Ep-);uL N~x $Ųzt>fg~2v"˅R8&Ņ+%$+Э<}1D%6`b v<+jj *cO)-R `j)9pB |09QAe 9QJր﷈$y,jO|,,6v$=>!)cwb ^f}De+ٜVzC,SB {pnY攰J8򑏹0ѴХm195?>l7_&+1JsSZҕ\ĔF6 R=̞z`F|,C5ˢḿ]!0Q&&6-)Ca\.1Wr(EƛSPiP-K̈")U҆=s*%kg4-^Z[ɰˏ#.VNL"EOn=d`.|VXVͧ\)IЃf\\ tV{sE&y4Ggi@[E Ifo(ɞ8[!R|甴% }G97'ʿe3'ucӳ-mYL ֛f pQƔ(JdfoQ\Ʉ[.@(ĕ_k#8J#Knaq:iUƧFK&p:kI}/t'nV<~wl$D0Xb7PCwA; 0\+^/&<*OZ|œܸ] kJYз&!5RCP?VisN\`A) lsW,j4qؑy Ͻ5"n#mD"Rd)&2ZITH$%edN'l|\@/d6 ٍZO XI)&AA&0LE!-F\""xRHkɲ% e'sfn=4A# q%"FH ߛ}8@RCŋ9%SZH%e}[D,@FZ6e&chEermʌZ2nz~ Ve(K)܎r㳻`\ 4JKO6C^ɝĩ4 2O|97w #%Ei(V Osfv(V)7b@Z (cyޛ`Jv޻r7zu]Z]b"K?>Q75#QC9;zl1u&d #Ԃ2[p̃a8Q(l+-z(ND!1nGbrvYLʏo#&RzW|,6D4x٘/k҃r̆qۇگo @GHa,|m/( s"˲mMs4ylPW< Y}jĒN0]ㆩc@RP X:9-X$RH=>NQ#pnIvۓa;Zym0V_ql̊GirBy@S@@X[=nܖ^\cm6ca:ݴH'ѧ$8'IA2](I?'K |)Ǔ$IraVyXq%[*ɅNB4#A J~NDo!l GXzb>=[[MsDW"ȥj7z 6\l Κ!ď~\G7I]e&ڔUwg`(?_i'ZwR%B3lX6snv@%|[rUP~yf!A /& $o+koOvUt|b孋U$*б51+2 7UoQ@/ݧ/q8^eQS*X mPN6tIdPH$TB< ̜dG2,WzZU:B\ 2Oc`0MKmL)[jvQpU037rnJa34m_ъIhBB?ARɾ %ɶ10,Y>ת?c/yia)h>'bYf}vac+joA őA n,4ݪ[k$y&֛;fFfֲOAԐ2QڞA;Fc2!XsS]XMY]~*V9cݼ6ȴe4U8ߦAJAS!.6&". (-"zdy0Ff\d!I*.>Oݝ=ZՆe#s\Lțѱr7E4 PSeڀkxAL<`[0^_gl fϿּjčlur;un<J?ÒWn؍Fc!š%^Y[.*)m=+KX[0R&wf?.U }:+"vLExHT TS~5e.ՋigP9)\SnkY")D,tԝiz\-Al-91̬*H4_L x CR@51E7֋_T圝i]n i^T4ƝI)hv8!Yn(끅,y+@o[R Q»}pKzX97%ڽ|1aHg'li.~. 8\ni^uS85?< BpiLYŘY`lA)?+w1㷽i_[$G oŁ1p*Ӧ6hzָWMB;S QnGo< Pרg n/dJLRK˚kҊK;qjO mMXl@:OcʉATgYAO7.nA.mĚaJ+(2lUVFۨ3ɒi,-NimE[y4*m(yvnYď`0]OxkǨi?d2Ei4 Q~T+.j Dێd/ex6<: glx^Б{Djz `@QcӨlC AGZLl ol)1$I9q*㕖p;mmU6YȠEȊYrj=&-$WfAH2S&?C2=%2Er0_3yP$>-L~iˈHQyKNb`A]Őd38PmOFU+\6w\;4j&$q*ۖ@gFL6 ݘiһ!V~AwlT\N 3/](?db!YIPÛ5nRƀ{pIi:@Uķ M1>Z5&v ''5;1Bmf9S u߮cȵIe$9ZU_v,JTjQ#%+ܨٺ` Zi(;w9` E0vHE 1b(ID[w|ɌDR B7ؐ\@'ҿ3Kp!%ݜ2rVGPg q-)ȡ2ZLQ[,Fˇtf/rܘ3$z\ (-;wrmB0̨7\TNJZS lIZVڔOdF4 Џ;LB!ae'z|WzՑB۴|+n\qw5f҉5aT>#.A@P Jk9S с<%͐p"crJCƠK.ode6G! +TU$KYHY50‘U# ˎRqOYX?Lr; 5'İdvtX||6H'oO OǘVȥOQGREyppvNȠ~h8I_ȿK`̖C7RH2ocV m^ 1٧$IU4c@;E@! !^0ցr.8=~X@jO2WCn(?ȄkrUU#T<-)eSש_]79 T1AZyu;uS.**$m&!rcF9tI, /sJYO2)kfF;tyW+Wd/Dec*F21%'+绕M eKW8_ uq@YaE+vY1ٍ^*,v=f4J "/Dm %P@YhgHa!eeA^Ur~m׊0smH, 4a5AW&vqY)M2YIjdr*%z[1*Iw`S/Z0 )~FGw:Q܀Fը4ۮ_7lY'z!~8j1P"bTAk0I.+ .KxTҰUQԒh+2@۴ ؏26ǝV2jIQ\&MDe6;cvǩE[nC@xJC_%>W UI |_GDj a` J HQ&^hTJ ""Uf|4x/ߒ#Acmnu NJO] ZiHrHJF>M@ٌ/1q*=cMWԧEb4iP$nBAS`h\'t<e4ed^B)t\!^ $ FcSC)sKdC[1ʫHMj lUi.\Sߡ/`ڀVSăĞdE%$s`K'e/gK-FFLxYA&F6 }O?9",q5mlSKL_8KLi*r=4;b#qa -G$I:OsCґ4wlR"L{ō8NR8r_ W% M>Pb|φ,x Cj_8伯ùL*"cMN0{qS甐j kN1)1kbA`kPvmأ0axI->3g J+e%!Rjnk5ز.ajuݑv>\Ȏ8AQʽ>7U 6c&Ogr>4뚹4búf4 F ׌_^eFڢ+[TWʌ԰؃lmV@k\dYQS8vF2 R>ķ+dc$ڽ N,1h!L]VKY0CHkإdD91K züȜ2AnS6ļH{}-`1Y,[22TI&9E̼Y7efy!I=KU*X:PiɈOK4JFZ өӟ6Y (#|ќznM?W ]UUfXb?;YԞ* >1uB1 ^MmJy.gc!*HƋʧY! cOҒJo 'vw]8_D_~Ϝ#˃Ϝ5D0ar?ɦvx5X#]Qs+.Qʭ 8٪ BIu=2AV1mQEG\%lqLUYSp$J=/LND$*n2@[ -AX"υA={d[CtX""_2aōOP@;o3{6\/c0͔4]F$Yb?Wb)3;K!-m/Xl7M\#d]^X)˓fX "~\ws-4 we|Kg Qj@\ܷ@؈ i#vU76Sw=UQFfnTu?'ɰ^jO5Zx;_k E8J, *܈gWvїWQ= gj&5#c3f䒪e@[` LjN&} 9Wljq•@z,s M2'P}QlcJrہ![aiWF!xS.4E}9m9Jb^m~ OOм XI PNÑmqSͥLGu5w;n8A6&THV -] #PliڽZϟ4F8@&1dz &늆= }=:A\xz8߮-A1쏗~qd7_V\<d\irR~%8\G)m;b!yێjCLO"_fqqpQTz}oeɌ}b}RZJ~0lc%=*(׷^ym*=d80̈́Hz4i"c2q D 6>!떆ЗϗɊYs̨QA~ݗ2* X(Bߦ[I\Qfu_~>op`GucO_CX{އ_dyv'9w2|1שOm:C.>>ϷY%#"ӿޘ_Y/=~^Gο;7izwݟo{Eos1;rOequ?OwOS}I?/M_}Ǜ#يZ:S.mRGLLYQh*!uoV*, U"rܢ`u4z=~ƒ??3G?7˛?t}+!Ɍy}lYE^uN?k?˛82?kZ$T_4_ᐛQO|F*OL\H9Zcr%e>j>cT\μE[O$eקG$\YR_\yJ}SNk+*S?bcu?_z͸"ǘcvz2=2aƃe_$<}Wzou?oʃ(rϿ,i[Gi?lei|d#Rߨ+YN5E~88pGkr#$'o'_+jien I%/rh_)&qgOѿdu_^>I2uu_[c/Жw:Vggظާgn:ejק[vo}9/rgaNYv)rOVQ9R~{tIs)?k&'[/=?cx}qj}_W?buv|ˆzι(rr5w5˥:t9`RԿޏ~};>"|}rs\k͙ݟSrLq7?-T.?Q>m~lVOӧ_Om"9rU\r)wY̏$_e6#6&Efy?ke)e,f*/OwFnQoIMUG8C`Sfg]*?DRz??7k˿o$9gI3uKB_z NIH8^{hqO4V׿+.%>j?ޯ3#h/3?\Nۓu_?͑i_?2Seɫ}#!.?o1>~U:l76r@O߫*8Qsorl-thumbnail-12.7.0/tests/data/white_border.jpg000066400000000000000000000032241375675401600220120ustar00rootroot00000000000000JFIFHHCreated with GIMPC  % !###&)&")"#"C """""""""""""""""""""""""""""""""""""""""""""""""""dd" 9 !1"AQq2Ra#5Brs1!1AQa"q23RS ?=Unv&bԶ9 \nQ}#'$YӃWyNjtpEya7 M)U,<+.relMȈJ1N^襫m*rQSQq-kVEdzYk,7˵.<9Xp+LT2>MƜy@=rKrxPW: `nyс 9貭j9+hWGRwCfŻr^vTbjO3]C#'* Z0ix򽳢I4'VqqY\x,>k?iQV:" =uMpxe%;kA'u˼N[2;e\C,<97@Hy 8F|Q_Ѵ/\=X("_6CqӍk@TA?* v.Gm*[ҳU5ş' =n'?ʭavx62(cX7-d[{;+p*@|r?@jJEx5DizSE;=Oh}Dm=SlhdmcFр{r:_G^Kߗ<"O""@˖JYZ$UN-?;_ZPv(e=ACN9TBsp23=&^(fPa=|N`K|e/N;8KgfNojъ{i8~z)w>2.x0T^*#kЇ5a_R .M}$Pڒ?z=f-bzBHI(\OH~z)oOli*'8Tڗ/nevIh* :Gn!}DZaD@ovKXk{tl4rɜ>'Kq_MV Jdד0-?oZZm[d4`:sǷW.P(S=2fX d:gKVwqx~Ieu/I43nTpOJryqnrMԶ۝--L1:A4oϠg}]U,6(hiRIOׇt~kM%º[LtUxZ3?UӲ hF#6`.TYU:EhVzVne/?DE """ """ """ """ """ ""sorl-thumbnail-12.7.0/tests/settings/000077500000000000000000000000001375675401600175615ustar00rootroot00000000000000sorl-thumbnail-12.7.0/tests/settings/__init__.py000066400000000000000000000000001375675401600216600ustar00rootroot00000000000000sorl-thumbnail-12.7.0/tests/settings/dbm.py000066400000000000000000000001331375675401600206720ustar00rootroot00000000000000from .default import * THUMBNAIL_KVSTORE = 'sorl.thumbnail.kvstores.dbm_kvstore.KVStore' sorl-thumbnail-12.7.0/tests/settings/default.py000066400000000000000000000026431375675401600215640ustar00rootroot00000000000000from os.path import join as pjoin, abspath, dirname, pardir import django SECRET_KEY = 'SECRET' PROJ_ROOT = abspath(pjoin(dirname(__file__), pardir)) DATA_ROOT = pjoin(PROJ_ROOT, 'data') THUMBNAIL_PREFIX = 'test/cache/' THUMBNAIL_DEBUG = True THUMBNAIL_LOG_HANDLER = { 'class': 'sorl.thumbnail.log.ThumbnailLogHandler', 'level': 'ERROR', } THUMBNAIL_KVSTORE = 'tests.thumbnail_tests.kvstore.TestKVStore' THUMBNAIL_STORAGE = 'tests.thumbnail_tests.storage.TestStorage' DEFAULT_FILE_STORAGE = 'tests.thumbnail_tests.storage.TestStorage' ADMINS = ( ('Sorl', 'thumbnail@sorl.net'), ) DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:', } } MEDIA_ROOT = pjoin(PROJ_ROOT, 'media') MEDIA_URL = '/media/' ROOT_URLCONF = 'tests.thumbnail_tests.urls' INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'sorl.thumbnail', 'tests.thumbnail_tests', ) TEMPLATES = [{ 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'APP_DIRS': True, }] MIDDLEWARE = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ) THUMBNAIL_REDIS_SSL = False sorl-thumbnail-12.7.0/tests/settings/dynamodb.py000066400000000000000000000003341375675401600217300ustar00rootroot00000000000000from .default import * THUMBNAIL_KVSTORE = 'sorl.thumbnail.kvstores.dynamodb_kvstore.KVStore' THUMBNAIL_DYNAMODB_NAME = 'test' AWS_REGION_NAME = 'eu-central-1' AWS_ACCESS_KEY_ID = 'use' AWS_SECRET_ACCESS_KEY = 'yours' sorl-thumbnail-12.7.0/tests/settings/graphicsmagick.py000066400000000000000000000002371375675401600231110ustar00rootroot00000000000000from .default import * THUMBNAIL_ENGINE = 'sorl.thumbnail.engines.convert_engine.Engine' THUMBNAIL_CONVERT = 'gm convert' THUMBNAIL_IDENTIFY = 'gm identify' sorl-thumbnail-12.7.0/tests/settings/imagemagick.py000066400000000000000000000001711375675401600223700ustar00rootroot00000000000000from .default import * THUMBNAIL_ENGINE = 'sorl.thumbnail.engines.convert_engine.Engine' THUMBNAIL_CONVERT = 'convert' sorl-thumbnail-12.7.0/tests/settings/pgmagick.py000066400000000000000000000001341375675401600217130ustar00rootroot00000000000000from .default import * THUMBNAIL_ENGINE = 'sorl.thumbnail.engines.pgmagick_engine.Engine' sorl-thumbnail-12.7.0/tests/settings/pil.py000066400000000000000000000001271375675401600207170ustar00rootroot00000000000000from .default import * THUMBNAIL_ENGINE = 'sorl.thumbnail.engines.pil_engine.Engine' sorl-thumbnail-12.7.0/tests/settings/redis.py000066400000000000000000000001351375675401600212400ustar00rootroot00000000000000from .default import * THUMBNAIL_KVSTORE = 'sorl.thumbnail.kvstores.redis_kvstore.KVStore' sorl-thumbnail-12.7.0/tests/settings/vipsthumbnail.py000066400000000000000000000001411375675401600230140ustar00rootroot00000000000000from .default import * THUMBNAIL_ENGINE = 'sorl.thumbnail.engines.vipsthumbnail_engine.Engine' sorl-thumbnail-12.7.0/tests/settings/wand.py000066400000000000000000000001301375675401600210560ustar00rootroot00000000000000from .default import * THUMBNAIL_ENGINE = 'sorl.thumbnail.engines.wand_engine.Engine' sorl-thumbnail-12.7.0/tests/thumbnail_tests/000077500000000000000000000000001375675401600211265ustar00rootroot00000000000000sorl-thumbnail-12.7.0/tests/thumbnail_tests/__init__.py000066400000000000000000000000001375675401600232250ustar00rootroot00000000000000sorl-thumbnail-12.7.0/tests/thumbnail_tests/kvstore.py000066400000000000000000000014511375675401600231760ustar00rootroot00000000000000from sorl.thumbnail.kvstores.cached_db_kvstore import KVStore class KVlogHandler: _log = [] _active = False def start_log(self): self._active = True def stop_log(self): self._active = False log = self._log[:] self._log = [] return log def log(self, s): if self._active: self._log.append(s) kvlog = KVlogHandler() class TestKvStoreMixin: def get(self, *args, **kwargs): kvlog.log('get') return super().get(*args, **kwargs) def set(self, *args, **kwargs): kvlog.log('set') return super().set(*args, **kwargs) def delete(self, *args, **kwargs): kvlog.log('delete') return super().delete(*args, **kwargs) class TestKVStore(TestKvStoreMixin, KVStore): pass sorl-thumbnail-12.7.0/tests/thumbnail_tests/models.py000066400000000000000000000002071375675401600227620ustar00rootroot00000000000000from django.db import models from sorl.thumbnail import ImageField class Item(models.Model): image = ImageField(upload_to=True) sorl-thumbnail-12.7.0/tests/thumbnail_tests/storage.py000066400000000000000000000047421375675401600231530ustar00rootroot00000000000000import logging from django.core.files.storage import FileSystemStorage class MockLoggingHandler(logging.Handler): """Mock logging handler to check for expected logs.""" def __init__(self, *args, **kwargs): self.reset() super().__init__(*args, **kwargs) def emit(self, record): self.messages[record.levelname.lower()].append(record.getMessage()) def reset(self): self.messages = {'debug': [], 'info': [], 'warning': [], 'error': [], 'critical': []} slog = logging.getLogger('slog') class TestStorageMixin: def open(self, name, *args, **kwargs): slog.debug('open: %s' % name) return super().open(name, *args, **kwargs) def save(self, name, *args, **kwargs): slog.debug('save: %s' % name) return super().save(name, *args, **kwargs) def get_valid_name(self, name, *args, **kwargs): slog.debug('get_valid_name: %s' % name) return super().get_valid_name(name, *args, **kwargs) def get_available_name(self, name, *args, **kwargs): slog.debug('get_available_name: %s' % name) return super().get_available_name(name, *args, **kwargs) def path(self, name, *args, **kwargs): # slog.debug('path: %s' % name) return super().path(name, *args, **kwargs) def delete(self, name, *args, **kwargs): slog.debug('delete: %s' % name) return super().delete(name, *args, **kwargs) def exists(self, name, *args, **kwargs): slog.debug('exists: %s' % name) return super().exists(name, *args, **kwargs) def listdir(self, name, *args, **kwargs): slog.debug('listdir: %s' % name) return super().listdir(name, *args, **kwargs) def size(self, name, *args, **kwargs): slog.debug('size: %s' % name) return super().size(name, *args, **kwargs) def url(self, name, *args, **kwargs): # slog.debug('url: %s' % name) return super().url(name, *args, **kwargs) def accessed_time(self, name, *args, **kwargs): slog.debug('accessed_time: %s' % name) return super().accessed_time(name, *args, **kwargs) def created_time(self, name, *args, **kwargs): slog.debug('created_time: %s' % name) return super().created_time(name, *args, **kwargs) def modified_time(self, name, *args, **kwargs): slog.debug('modified_time: %s' % name) return super().modified_time(name, *args, **kwargs) class TestStorage(TestStorageMixin, FileSystemStorage): pass sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/000077500000000000000000000000001375675401600231245ustar00rootroot00000000000000sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/htmlfilter.html000066400000000000000000000001331375675401600261610ustar00rootroot00000000000000{% load thumbnail %} {% autoescape off %} {{ text|html_thumbnails }} {% endautoescape %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/markdownfilter.html000066400000000000000000000000641375675401600270420ustar00rootroot00000000000000{% load thumbnail %} {{ text|markdown_thumbnails }} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail1.html000066400000000000000000000003651375675401600260620ustar00rootroot00000000000000{% load thumbnail %}{% spaceless %} {% thumbnail item.image "200x100" crop="50% 50%" as im %} {% empty %} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail1_alias.html000066400000000000000000000003721375675401600272310ustar00rootroot00000000000000{% load sorl_thumbnail %}{% spaceless %} {% thumbnail item.image "200x100" crop="50% 50%" as im %} {% empty %} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail2.html000066400000000000000000000003541375675401600260610ustar00rootroot00000000000000{% load thumbnail %}{% spaceless %} {% thumbnail item.image.name "200x100" format="PNG" quality=99 as im %} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail20.html000066400000000000000000000002231375675401600261340ustar00rootroot00000000000000{% load thumbnail %} {% thumbnail image "32x32" crop="center" as im %} {% empty %}

fail

{% endthumbnail %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail20a.html000066400000000000000000000001141375675401600262740ustar00rootroot00000000000000{% load thumbnail %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail2_alias.html000066400000000000000000000003611375675401600272300ustar00rootroot00000000000000{% load sorl_thumbnail %}{% spaceless %} {% thumbnail item.image.name "200x100" format="PNG" quality=99 as im %} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail3.html000066400000000000000000000003571375675401600260650ustar00rootroot00000000000000{% load thumbnail %}{% spaceless %} {% thumbnail "http://dummyimage.com/100x100/" "20x20" crop="center" as im %} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail4.html000066400000000000000000000006011375675401600260560ustar00rootroot00000000000000{% load thumbnail %}{% spaceless %} {% if source|is_portrait %} {% thumbnail source '1x1' as im %} {% endthumbnail %} {% else %} {% thumbnail source dims as im %} {% endthumbnail %} {% endif %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail5.html000066400000000000000000000003711375675401600260630ustar00rootroot00000000000000{% load thumbnail %}{% spaceless %} {% thumbnail sorl "30x30" crop="50% 50%" as im %} {% empty %}

empty{{ im }}

{% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail6.html000066400000000000000000000004431375675401600260640ustar00rootroot00000000000000{% load thumbnail %}{% spaceless %} {% thumbnail item.image "100x100" as th %} {% thumbnail item.image "400x400" as im %}
{% endthumbnail %} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail6_alias.html000066400000000000000000000004501375675401600272330ustar00rootroot00000000000000{% load sorl_thumbnail %}{% spaceless %} {% thumbnail item.image "100x100" as th %} {% thumbnail item.image "400x400" as im %} {% endthumbnail %} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail7.html000066400000000000000000000002521375675401600260630ustar00rootroot00000000000000{% load thumbnail %}{% spaceless %} {% thumbnail item.image "100x100" crop="center" upscale="True" quality=70 as th %} {{ th.url }} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail7_alias.html000066400000000000000000000002571375675401600272410ustar00rootroot00000000000000{% load sorl_thumbnail %}{% spaceless %} {% thumbnail item.image "100x100" crop="center" upscale="True" quality=70 as th %} {{ th.url }} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail7a.html000066400000000000000000000002521375675401600262240ustar00rootroot00000000000000{% load thumbnail %}{% spaceless %} {% thumbnail item.image "100x100" quality=70 crop="center" upscale="True" as th %} {{ th.url }} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail7a_alias.html000066400000000000000000000002571375675401600274020ustar00rootroot00000000000000{% load sorl_thumbnail %}{% spaceless %} {% thumbnail item.image "100x100" quality=70 crop="center" upscale="True" as th %} {{ th.url }} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail8.html000066400000000000000000000002221375675401600260610ustar00rootroot00000000000000{% load thumbnail %}{% spaceless %} {% thumbnail item.image "100x100" options=options as th %} {{ th.url }} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail8_alias.html000066400000000000000000000002271375675401600272370ustar00rootroot00000000000000{% load sorl_thumbnail %}{% spaceless %} {% thumbnail item.image "100x100" options=options as th %} {{ th.url }} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail8a.html000066400000000000000000000002501375675401600262230ustar00rootroot00000000000000{% load thumbnail %}{% spaceless %} {% thumbnail item.image "100x100" crop="center" upscale=True quality=77 as th %} {{ th.url }} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail8a_alias.html000066400000000000000000000002551375675401600274010ustar00rootroot00000000000000{% load sorl_thumbnail %}{% spaceless %} {% thumbnail item.image "100x100" crop="center" upscale=True quality=77 as th %} {{ th.url }} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnail9.html000066400000000000000000000004001375675401600260600ustar00rootroot00000000000000{% load thumbnail %}{% spaceless %} {% thumbnail "invalid_image.jpg" "30x30" crop="50% 50%" as im %} {% empty %}

empty

{% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnaild1.html000066400000000000000000000003171375675401600262230ustar00rootroot00000000000000{% load thumbnail %}{% spaceless %} {% thumbnail anything "200x100" as im %} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnaild2.html000066400000000000000000000003401375675401600262200ustar00rootroot00000000000000{% load thumbnail %}{% spaceless %} {% thumbnail anything "300" as im %} {% endthumbnail %} {% if not ""|is_portrait %}

NOT

{% endif %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnaild3.html000066400000000000000000000002521375675401600262230ustar00rootroot00000000000000{% load thumbnail %}{% spaceless %} {% thumbnail "" "x400" as im %} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/templates/thumbnaild4.html000066400000000000000000000003701375675401600262250ustar00rootroot00000000000000{% load thumbnail %}{% spaceless %} {% thumbnail "" "x400" as im %} {% endthumbnail %} {% endspaceless %} sorl-thumbnail-12.7.0/tests/thumbnail_tests/test_admin.py000066400000000000000000000005071375675401600236310ustar00rootroot00000000000000from django.test import SimpleTestCase from sorl.thumbnail.admin.current import AdminImageWidget class AdminImageWidgetTests(SimpleTestCase): def test_render_renderer_argument(self): w = AdminImageWidget() self.assertHTMLEqual(w.render('name', 'value', renderer=None), '') sorl-thumbnail-12.7.0/tests/thumbnail_tests/test_alternative_resolutions.py000066400000000000000000000040501375675401600275220ustar00rootroot00000000000000import os import pytest from sorl.thumbnail import get_thumbnail from sorl.thumbnail.conf import settings from sorl.thumbnail.images import ImageFile from sorl.thumbnail.engines.pil_engine import Engine as PILEngine from .utils import BaseStorageTestCase pytestmark = pytest.mark.django_db class AlternativeResolutionsTest(BaseStorageTestCase): name = 'retina.jpg' def setUp(self): settings.THUMBNAIL_ALTERNATIVE_RESOLUTIONS = [1.5, 2] super().setUp() self.maxDiff = None def tearDown(self): super().tearDown() settings.THUMBNAIL_ALTERNATIVE_RESOLUTIONS = [] def test_retina(self): get_thumbnail(self.image, '50x50') actions = [ 'exists: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d.jpg', # save regular resolution, same as in StorageTestCase 'open: retina.jpg', 'save: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d.jpg', 'get_available_name: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d.jpg', 'exists: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d.jpg', # save the 1.5x resolution version 'save: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@1.5x.jpg', 'get_available_name: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@1.5x.jpg', 'exists: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@1.5x.jpg', # save the 2x resolution version 'save: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@2x.jpg', 'get_available_name: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@2x.jpg', 'exists: test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@2x.jpg' ] self.assertEqual(self.log, actions) path = os.path.join(settings.MEDIA_ROOT, 'test/cache/91/bb/91bb06cf9169e4c52132bb113f2d4c0d@1.5x.jpg') with open(path) as fp: engine = PILEngine() self.assertEqual(engine.get_image_size(engine.get_image(ImageFile(file_=fp))), (75, 75)) sorl-thumbnail-12.7.0/tests/thumbnail_tests/test_backends.py000066400000000000000000000123111375675401600243070ustar00rootroot00000000000000from io import StringIO import os import platform import sys import shutil import unittest from PIL import Image import pytest from django.test import TestCase from django.test.utils import override_settings from sorl.thumbnail import default, delete, get_thumbnail from sorl.thumbnail.base import ThumbnailBackend from sorl.thumbnail.conf import settings from sorl.thumbnail.helpers import get_module_class from sorl.thumbnail.images import ImageFile from .utils import BaseTestCase, FakeFile, same_open_fd_count from .models import Item pytestmark = pytest.mark.django_db class BackendTest(BaseTestCase): def test_delete(self): im1 = Item.objects.get(image='100x100.jpg').image im2 = Item.objects.get(image='500x500.jpg').image default.kvstore.get_or_set(ImageFile(im1)) # exists in kvstore and in storage self.assertTrue(bool(default.kvstore.get(ImageFile(im1)))) self.assertTrue(ImageFile(im1).exists()) # delete delete(im1) self.assertFalse(bool(default.kvstore.get(ImageFile(im1)))) self.assertFalse(ImageFile(im1).exists()) default.kvstore.get_or_set(ImageFile(im2)) # exists in kvstore and in storage self.assertTrue(bool(default.kvstore.get(ImageFile(im2)))) self.assertTrue(ImageFile(im2).exists()) # delete delete(im2, delete_file=False) self.assertFalse(bool(default.kvstore.get(ImageFile(im2)))) self.assertTrue(ImageFile(im2).exists()) @override_settings(THUMBNAIL_PRESERVE_FORMAT=True, THUMBNAIL_FORMAT='XXX') class PreserveFormatTest(TestCase): def setUp(self): self.backend = ThumbnailBackend() def test_with_various_formats(self): self.assertEqual(self.backend._get_format(FakeFile('foo.jpg')), 'JPEG') self.assertEqual(self.backend._get_format(FakeFile('foo.jpeg')), 'JPEG') self.assertEqual(self.backend._get_format(FakeFile('foo.png')), 'PNG') self.assertEqual(self.backend._get_format(FakeFile('foo.gif')), 'GIF') def test_double_extension(self): self.assertEqual(self.backend._get_format(FakeFile('foo.ext.jpg')), 'JPEG') def test_that_capitalization_doesnt_matter(self): self.assertEqual(self.backend._get_format(FakeFile('foo.PNG')), 'PNG') self.assertEqual(self.backend._get_format(FakeFile('foo.JPG')), 'JPEG') def test_fallback_format(self): self.assertEqual(self.backend._get_format(FakeFile('foo.txt')), 'XXX') def test_with_nonascii(self): self.assertEqual(self.backend._get_format(FakeFile('你好.jpg')), 'JPEG') def test_image_remote_url(self): self.assertEqual(self.backend._get_format(FakeFile('http://example.com/1.png')), 'PNG') @unittest.skipIf(platform.system() == "Windows", "Can't easily count descriptors on windows") class TestDescriptors(unittest.TestCase): """Make sure we're not leaving open descriptors on file exceptions""" ENGINE = None def setUp(self): self.ENGINE = get_module_class(settings.THUMBNAIL_ENGINE)() def test_no_source_get_image(self): """If source image does not exists, properly close all file descriptors""" source = ImageFile('nonexistent.jpeg') with same_open_fd_count(self): with self.assertRaises(IOError): self.ENGINE.get_image(source) def test_is_valid_image(self): with same_open_fd_count(self): self.ENGINE.is_valid_image(b'invalidbinaryimage.jpg') @unittest.skipIf('pgmagick_engine' in settings.THUMBNAIL_ENGINE and sys.version_info.major == 2, 'No output has been received in the last 10 minutes,' 'this potentially indicates something wrong with the build itself.') def test_write(self): with same_open_fd_count(self): with self.assertRaises(Exception): self.ENGINE.write(image=self.ENGINE.get_image(StringIO(b'xxx')), options={'format': 'JPEG', 'quality': 90, 'image_info': {}}, thumbnail=ImageFile('whatever_thumb.jpg', default.storage)) class ModelTestCase(BaseTestCase): def test_field1(self): self.KVSTORE.clear() item = Item.objects.get(image='100x100.jpg') im = ImageFile(item.image) self.assertEqual(None, self.KVSTORE.get(im)) self.BACKEND.get_thumbnail(im, '27x27') self.BACKEND.get_thumbnail(im, '81x81') self.assertNotEqual(None, self.KVSTORE.get(im)) self.assertEqual(3, len(list(self.KVSTORE._find_keys(identity='image')))) self.assertEqual(1, len(list(self.KVSTORE._find_keys(identity='thumbnails')))) class TestInputCase(unittest.TestCase): def setUp(self): if not os.path.exists(settings.MEDIA_ROOT): os.makedirs(settings.MEDIA_ROOT) self.name = 'åäö.jpg' fn = os.path.join(settings.MEDIA_ROOT, self.name) im = Image.new('L', (666, 666)) im.save(fn) def test_nonascii(self): # also test the get_thumbnail shortcut th = get_thumbnail(self.name, '200x200') self.assertEqual(th.url, '/media/test/cache/f5/26/f52608b56718f62abc45a90ff9459f2c.jpg') def tearDown(self): shutil.rmtree(settings.MEDIA_ROOT) sorl-thumbnail-12.7.0/tests/thumbnail_tests/test_commands.py000066400000000000000000000054671375675401600243540ustar00rootroot00000000000000from io import StringIO import os import pytest from django.core import management from sorl.thumbnail.conf import settings from .models import Item from .utils import BaseTestCase pytestmark = pytest.mark.django_db class CommandTests(BaseTestCase): def make_test_thumbnails(self, *sizes): item = Item.objects.get(image='500x500.jpg') names = [] for size in sizes: th = self.BACKEND.get_thumbnail(item.image, size) name = os.path.join(settings.MEDIA_ROOT, th.name) self.assertTrue(os.path.isfile(name)) names.append(name) return names def test_clear_action(self): """ Only the KV store is cleared. """ name1, name2 = self.make_test_thumbnails('400x300', '200x200') out = StringIO('') management.call_command('thumbnail', 'clear', verbosity=1, stdout=out) self.assertEqual(out.getvalue(), "Clear the Key Value Store ... [Done]\n") self.assertTrue(os.path.isfile(name1)) self.assertTrue(os.path.isfile(name2)) def test_clear_delete_referenced_action(self): """ Clear KV store and delete referenced thumbnails """ name1, name2 = self.make_test_thumbnails('400x300', '200x200') management.call_command('thumbnail', 'clear', verbosity=0) name3, = self.make_test_thumbnails('100x100') out = StringIO('') management.call_command('thumbnail', 'clear_delete_referenced', verbosity=1, stdout=out) lines = out.getvalue().split("\n") self.assertEqual(lines[0], "Delete all thumbnail files referenced in Key Value Store ... [Done]") self.assertEqual(lines[1], "Clear the Key Value Store ... [Done]") self.assertTrue(os.path.isfile(name1)) self.assertTrue(os.path.isfile(name2)) self.assertFalse(os.path.isfile(name3)) def test_clear_delete_all_action(self): """ Clear KV store and delete all thumbnails """ name1, name2 = self.make_test_thumbnails('400x300', '200x200') management.call_command('thumbnail', 'clear', verbosity=0) name3, = self.make_test_thumbnails('100x100') out = StringIO('') management.call_command('thumbnail', 'clear_delete_all', verbosity=1, stdout=out) lines = out.getvalue().split("\n") self.assertEqual(lines[0], "Clear the Key Value Store ... [Done]") self.assertEqual(lines[1], "Delete all thumbnail files in THUMBNAIL_PREFIX ... [Done]") self.assertFalse(os.path.isfile(name1)) self.assertFalse(os.path.isfile(name2)) self.assertFalse(os.path.isfile(name3)) def test_cleanup_action(self): out = StringIO('') management.call_command('thumbnail', 'cleanup', verbosity=1, stdout=out) self.assertEqual(out.getvalue(), "Cleanup thumbnails ... [Done]\n") sorl-thumbnail-12.7.0/tests/thumbnail_tests/test_engines.py000066400000000000000000000550201375675401600241710ustar00rootroot00000000000000import os import platform import unittest from subprocess import Popen, PIPE import pytest from PIL import Image from django.core.files.storage import default_storage from django.template.loader import render_to_string from sorl.thumbnail import default from sorl.thumbnail.base import ThumbnailBackend from sorl.thumbnail.conf import settings from sorl.thumbnail.helpers import get_module_class from sorl.thumbnail.images import ImageFile from sorl.thumbnail.parsers import parse_geometry from sorl.thumbnail.templatetags.thumbnail import margin from sorl.thumbnail.engines.pil_engine import Engine as PILEngine from .models import Item from .utils import BaseTestCase pytestmark = pytest.mark.django_db class SimpleTestCase(BaseTestCase): def test_simple(self): item = Item.objects.get(image='500x500.jpg') t = self.BACKEND.get_thumbnail(item.image, '400x300', crop='center') self.assertEqual(t.x, 400) self.assertEqual(t.y, 300) t = self.BACKEND.get_thumbnail(item.image, '1200x900', crop='13% 89%') self.assertEqual(t.x, 1200) self.assertEqual(t.y, 900) def test_upscale(self): item = Item.objects.get(image='100x100.jpg') t = self.BACKEND.get_thumbnail(item.image, '400x300', upscale=False) self.assertEqual(t.x, 100) self.assertEqual(t.y, 100) t = self.BACKEND.get_thumbnail(item.image, '400x300', upscale=True) self.assertEqual(t.x, 300) self.assertEqual(t.y, 300) def test_upscale_and_crop(self): item = Item.objects.get(image='200x100.jpg') t = self.BACKEND.get_thumbnail(item.image, '400x300', crop='center', upscale=False) self.assertEqual(t.x, 200) self.assertEqual(t.y, 100) t = self.BACKEND.get_thumbnail(item.image, '400x300', crop='center', upscale=True) self.assertEqual(t.x, 400) self.assertEqual(t.y, 300) def test_kvstore(self): im = ImageFile(Item.objects.get(image='500x500.jpg').image) self.KVSTORE.delete_thumbnails(im) th1 = self.BACKEND.get_thumbnail(im, '50') th2 = self.BACKEND.get_thumbnail(im, 'x50') th3 = self.BACKEND.get_thumbnail(im, '20x20') self.assertEqual( set([th1.key, th2.key, th3.key]), set(self.KVSTORE._get(im.key, identity='thumbnails')) ) self.KVSTORE.delete_thumbnails(im) self.assertEqual( None, self.KVSTORE._get(im.key, identity='thumbnails') ) def test_is_portrait(self): im = ImageFile(Item.objects.get(image='500x500.jpg').image) th = self.BACKEND.get_thumbnail(im, '50x200', crop='center') self.assertEqual(th.is_portrait(), True) th = self.BACKEND.get_thumbnail(im, '500x2', crop='center') self.assertEqual(th.is_portrait(), False) def test_margin(self): im = ImageFile(Item.objects.get(image='500x500.jpg').image) self.assertEqual(margin(im, '1000x1000'), '250px 250px 250px 250px') self.assertEqual(margin(im, '800x1000'), '250px 150px 250px 150px') self.assertEqual(margin(im, '500x500'), '0px 0px 0px 0px') self.assertEqual(margin(im, '500x501'), '0px 0px 1px 0px') self.assertEqual(margin(im, '503x500'), '0px 2px 0px 1px') self.assertEqual(margin(im, '300x300'), '-100px -100px -100px -100px') def test_kvstore_get_and_set(self): im = ImageFile(Item.objects.get(image='500x500.jpg').image) self.KVSTORE.delete(im) self.assertEqual(self.KVSTORE.get(im), None) self.KVSTORE.set(im) self.assertEqual(im.size, [500, 500]) def test_cleanup1(self): im = ImageFile(Item.objects.get(image='500x500.jpg').image) self.KVSTORE.delete_thumbnails(im) th = self.BACKEND.get_thumbnail(im, '3x3') self.assertEqual(th.exists(), True) th.delete() self.assertEqual(th.exists(), False) self.assertEqual(self.KVSTORE.get(th).x, 3) self.assertEqual(self.KVSTORE.get(th).y, 3) self.KVSTORE.cleanup() self.assertEqual(self.KVSTORE.get(th), None) self.KVSTORE.delete(im) def test_cleanup2(self): self.KVSTORE.clear() im = ImageFile(Item.objects.get(image='500x500.jpg').image) th3 = self.BACKEND.get_thumbnail(im, '27x27') th4 = self.BACKEND.get_thumbnail(im, '81x81') def keys_test(x, y, z): self.assertEqual(x, len(list(self.KVSTORE._find_keys(identity='image')))) self.assertEqual(y, len(list(self.KVSTORE._find_keys(identity='thumbnails')))) self.assertEqual(z, len(self.KVSTORE._get(im.key, identity='thumbnails') or [])) keys_test(3, 1, 2) th3.delete() keys_test(3, 1, 2) self.KVSTORE.cleanup() keys_test(2, 1, 1) th4.delete() keys_test(2, 1, 1) self.KVSTORE.cleanup() keys_test(1, 0, 0) self.KVSTORE.clear() keys_test(0, 0, 0) def test_clear_doesnt_regenerate(self): self.KVSTORE.clear() im = ImageFile(Item.objects.get(image='500x500.jpg').image) th = self.BACKEND.get_thumbnail(im, '27x27') th_name_orig = self.KVSTORE.get(th).name self.KVSTORE.clear() th = self.BACKEND.get_thumbnail(im, '27x27') th_name_new = self.KVSTORE.get(th).name self.assertEqual( th_name_orig, th_name_new, ) def test_storage_serialize(self): im = ImageFile(Item.objects.get(image='500x500.jpg').image) self.assertEqual(im.serialize_storage(), 'tests.thumbnail_tests.storage.TestStorage') self.assertEqual( ImageFile('http://www.image.jpg').serialize_storage(), 'sorl.thumbnail.images.UrlStorage', ) self.assertEqual( ImageFile('http://www.image.jpg', default.storage).serialize_storage(), 'tests.thumbnail_tests.storage.TestStorage', ) self.assertEqual( ImageFile('getit', default_storage).serialize_storage(), 'tests.thumbnail_tests.storage.TestStorage', ) @unittest.skipIf(platform.system() == "Darwin", 'quality is saved a different way on os x') def test_quality(self): im = ImageFile(Item.objects.get(image='500x500.jpg').image) th = self.BACKEND.get_thumbnail(im, '100x100', quality=50) p1 = Popen(['identify', '-verbose', th.storage.path(th.name)], stdout=PIPE) p2 = Popen(['grep', '-c', 'Quality: 50'], stdin=p1.stdout, stdout=PIPE) p1.stdout.close() output = p2.communicate()[0].strip() self.assertEqual(output.decode('utf-8'), '1') def test_transparency(self): item, _created = self.create_image( '50x50_transparent.png', (50, 50), transparent=True) th = self.BACKEND.get_thumbnail(item.image, '11x11', format='PNG') img = Image.open(th.storage.path(th.name)) self.assertTrue(self.is_transparent(img)) def test_transparency_gif_to_jpeg(self): path = 'data/animation_w_transparency.gif' th = self.BACKEND.get_thumbnail(path, '11x11', format='JPEG') img = Image.open(th.storage.path(th.name)) self.assertFalse(self.is_transparent(img)) def test_image_file_deserialize(self): im = ImageFile(Item.objects.get(image='500x500.jpg').image) default.kvstore.set(im) self.assertEqual( default.kvstore.get(im).serialize_storage(), 'tests.thumbnail_tests.storage.TestStorage', ) im = ImageFile('http://dummyimage.com/300x300/') default.kvstore.set(im) self.assertEqual( default.kvstore.get(im).serialize_storage(), 'sorl.thumbnail.images.UrlStorage', ) def test_abspath(self): item = Item.objects.get(image='500x500.jpg') image = ImageFile(item.image.path) val = render_to_string('thumbnail20.html', {'image': image, }).strip() im = self.BACKEND.get_thumbnail(image, '32x32', crop='center') self.assertEqual('' % im.url, val) def test_new_tag_style(self): item = Item.objects.get(image='500x500.jpg') image = ImageFile(item.image.path) val = render_to_string('thumbnail20a.html', {'image': image, }).strip() im = self.BACKEND.get_thumbnail(image, '32x32', crop='center') self.assertEqual('' % im.url, val) def test_relative_absolute_same_key(self): image = Item.objects.get(image='500x500.jpg').image imref1 = ImageFile(image.name) imref2 = ImageFile(os.path.join(settings.MEDIA_ROOT, image.name)) self.assertEqual(imref1.key, imref2.key) self.create_image('medialibrary.jpg', (100, 100)) image = Item.objects.get(image='medialibrary.jpg').image imref1 = ImageFile(image.name) imref2 = ImageFile(os.path.join(settings.MEDIA_ROOT, image.name)) self.assertEqual(imref1.key, imref2.key) self.create_image('mediaäöü.jpg', (100, 100)) image = Item.objects.get(image='mediaäöü.jpg').image imref1 = ImageFile(image.name) imref2 = ImageFile(os.path.join(settings.MEDIA_ROOT, image.name)) self.assertEqual(imref1.key, imref2.key) @unittest.skipIf('pil_engine' not in settings.THUMBNAIL_ENGINE, 'RGBA is only supported in PIL') def test_rgba_colorspace(self): item = Item.objects.get(image='500x500.jpg') t = self.BACKEND.get_thumbnail(item.image, '100x100', colorspace="RGBA", format="JPEG") self.assertEqual(t.x, 100) self.assertEqual(t.y, 100) def test_falsey_file_argument(self): with self.assertRaises(ValueError): self.BACKEND.get_thumbnail('', '100x100') with self.assertRaises(ValueError): self.BACKEND.get_thumbnail(None, '100x100') class CropTestCase(BaseTestCase): def setUp(self): super().setUp() # portrait name = 'portrait.jpg' fn = os.path.join(settings.MEDIA_ROOT, name) im = Image.new('L', (100, 200)) im.paste(255, (0, 0, 100, 100)) im.save(fn) self.portrait = ImageFile(Item.objects.get_or_create(image=name)[0].image) self.KVSTORE.delete(self.portrait) # landscape name = 'landscape.jpg' fn = os.path.join(settings.MEDIA_ROOT, name) im = Image.new('L', (200, 100)) im.paste(255, (0, 0, 100, 100)) im.save(fn) self.landscape = ImageFile(Item.objects.get_or_create(image=name)[0].image) self.KVSTORE.delete(self.landscape) def test_portrait_crop(self): def mean_pixel(x, y): values = im.getpixel((x, y)) if not isinstance(values, (tuple, list)): values = [values] return sum(values) / len(values) for crop in ('center', '88% 50%', '50px'): th = self.BACKEND.get_thumbnail(self.portrait, '100x100', crop=crop) engine = PILEngine() im = engine.get_image(th) self.assertEqual(mean_pixel(50, 0), 255) self.assertEqual(mean_pixel(50, 45), 255) self.assertEqual(250 <= mean_pixel(50, 49) <= 255, True, mean_pixel(50, 49)) self.assertEqual(mean_pixel(50, 55), 0) self.assertEqual(mean_pixel(50, 99), 0) for crop in ('top', '0%', '0px'): th = self.BACKEND.get_thumbnail(self.portrait, '100x100', crop=crop) engine = PILEngine() im = engine.get_image(th) for x in range(0, 99, 10): for y in range(0, 99, 10): self.assertEqual(250 < mean_pixel(x, y) <= 255, True) for crop in ('bottom', '100%', '100px'): th = self.BACKEND.get_thumbnail(self.portrait, '100x100', crop=crop) engine = PILEngine() im = engine.get_image(th) for x in range(0, 99, 10): for y in range(0, 99, 10): self.assertEqual(0 <= mean_pixel(x, y) < 5, True) def test_landscape_crop(self): def mean_pixel(x, y): values = im.getpixel((x, y)) if not isinstance(values, (tuple, list)): values = [values] return sum(values) / len(values) for crop in ('center', '50% 200%', '50px 700px'): th = self.BACKEND.get_thumbnail(self.landscape, '100x100', crop=crop) engine = PILEngine() im = engine.get_image(th) self.assertEqual(mean_pixel(0, 50), 255) self.assertEqual(mean_pixel(45, 50), 255) self.assertEqual(250 < mean_pixel(49, 50) <= 255, True) self.assertEqual(mean_pixel(55, 50), 0) self.assertEqual(mean_pixel(99, 50), 0) for crop in ('left', '0%', '0px'): th = self.BACKEND.get_thumbnail(self.landscape, '100x100', crop=crop) engine = PILEngine() im = engine.get_image(th) for x in range(0, 99, 10): for y in range(0, 99, 10): self.assertEqual(250 < mean_pixel(x, y) <= 255, True) for crop in ('right', '100%', '100px'): th = self.BACKEND.get_thumbnail(self.landscape, '100x100', crop=crop) engine = PILEngine() im = engine.get_image(th) coords = ((x, y) for y in range(0, 99, 10) for x in range(0, 99, 10)) for x, y in coords: self.assertEqual(0 <= mean_pixel(x, y) < 5, True) @unittest.skipIf( 'pil_engine' not in settings.THUMBNAIL_ENGINE, 'the other engines fail this test', ) def test_smart_crop(self): th = self.BACKEND.get_thumbnail('data/white_border.jpg', '32x32', crop='smart') self.assertEqual(th.x, 32) self.assertEqual(th.y, 32) engine = PILEngine() im = engine.get_image(th) self.assertEqual(im.size[0], 32) self.assertEqual(im.size[1], 32) def test_image_with_orientation(self): name = 'data/aspect_test.jpg' item, _ = Item.objects.get_or_create(image=name) im = ImageFile(item.image) th = self.BACKEND.get_thumbnail(im, '50x50') # this is a 100x200 image with orientation 6 (90 degrees CW rotate) # the thumbnail should end up 25x50 self.assertEqual(th.x, 25) self.assertEqual(th.y, 50) @unittest.skipIf( 'pil_engine' not in settings.THUMBNAIL_ENGINE, 'the other engines fail this test', ) def test_crop_image_with_icc_profile(self): name = 'data/icc_profile_test.jpg' item, _ = Item.objects.get_or_create(image=name) im = ImageFile(item.image) th = self.BACKEND.get_thumbnail(im, '100x100') engine = PILEngine() self.assertEqual( engine.get_image(im).info.get('icc_profile'), engine.get_image(th).info.get('icc_profile') ) # Only PIL has support for checking pixel color. convert and wand engines are both missing it, # so we cannot test for pixel color class CropBoxTestCase(BaseTestCase): def setUp(self): super().setUp() # portrait name = 'portrait.jpg' fn = os.path.join(settings.MEDIA_ROOT, name) im = Image.new('L', (100, 200)) im.paste(255, (0, 0, 100, 100)) im.save(fn) self.portrait = ImageFile(Item.objects.get_or_create(image=name)[0].image) self.KVSTORE.delete(self.portrait) # landscape name = 'landscape.jpg' fn = os.path.join(settings.MEDIA_ROOT, name) im = Image.new('L', (200, 100)) im.paste(255, (0, 0, 100, 100)) im.save(fn) self.landscape = ImageFile(Item.objects.get_or_create(image=name)[0].image) self.KVSTORE.delete(self.landscape) @unittest.skipIf( 'pil_engine' not in settings.THUMBNAIL_ENGINE, 'the other engines fail this test', ) def PIL_test_portrait_crop(self): def mean_pixel(x, y): values = im.getpixel((x, y)) if not isinstance(values, (tuple, list)): values = [values] return sum(values) / len(values) # Center Crop th = self.BACKEND.get_thumbnail(self.portrait, '100x100', cropbox="0,50,100,150") engine = PILEngine() im = engine.get_image(th) # Top half should be color, bottom not self.assertEqual(mean_pixel(0, 0), 255) self.assertEqual(mean_pixel(50, 0), 255) self.assertEqual(mean_pixel(50, 45), 255) self.assertEqual(mean_pixel(50, 55), 0) self.assertEqual(mean_pixel(50, 99), 0) # Top Crop th = self.BACKEND.get_thumbnail(self.portrait, '100x100', cropbox="0,0,100,100") engine = PILEngine() im = engine.get_image(th) for x in range(0, 99, 10): for y in range(0, 99, 10): self.assertEqual(250 < mean_pixel(x, y) <= 255, True) # Bottom Crop th = self.BACKEND.get_thumbnail(self.portrait, '100x100', cropbox="0,100,100,200") engine = PILEngine() im = engine.get_image(th) for x in range(0, 99, 10): for y in range(0, 99, 10): self.assertEqual(0 <= mean_pixel(x, y) < 5, True) @unittest.skipIf( 'pil_engine' not in settings.THUMBNAIL_ENGINE, 'the other engines fail this test', ) def PIL_test_landscape_crop(self): def mean_pixel(x, y): values = im.getpixel((x, y)) if not isinstance(values, (tuple, list)): values = [values] return sum(values) / len(values) # Center th = self.BACKEND.get_thumbnail(self.landscape, '100x100', cropbox="50,0,150,100") engine = PILEngine() im = engine.get_image(th) self.assertEqual(mean_pixel(0, 50), 255) self.assertEqual(mean_pixel(45, 50), 255) self.assertEqual(250 < mean_pixel(49, 50) <= 255, True) self.assertEqual(mean_pixel(55, 50), 0) self.assertEqual(mean_pixel(99, 50), 0) # Left th = self.BACKEND.get_thumbnail(self.landscape, '100x100', cropbox="0,0,100,100") engine = PILEngine() im = engine.get_image(th) for x in range(0, 99, 10): for y in range(0, 99, 10): self.assertEqual(250 < mean_pixel(x, y) <= 255, True) # Right th = self.BACKEND.get_thumbnail(self.landscape, '100x100', cropbox="100,0,200,100") engine = PILEngine() im = engine.get_image(th) coords = ((x, y) for y in range(0, 99, 10) for x in range(0, 99, 10)) for x, y in coords: self.assertEqual(0 <= mean_pixel(x, y) < 5, True) @unittest.skipIf( 'wand_engine' not in settings.THUMBNAIL_ENGINE, 'the other engines fail this test', ) def wand_test_cropbox(self): from sorl.thumbnail.engines.wand_engine import Engine as WandEngine th = self.BACKEND.get_thumbnail(self.portrait, '100x100', cropbox="0,50,100,150") engine = WandEngine() im = engine.get_image(th) # If the crop went well, then it should scale to 100x100 perfectly self.assertEqual(im.width(100), 100) self.assertEqual(im.height(100), 100) @unittest.skipIf( 'pgmagick_engine' not in settings.THUMBNAIL_ENGINE, 'the other engines fail this test', ) def pgmagick_test_cropbox(self): from sorl.thumbnail.engines.pgmagick_engine import Engine as PgMagickEngine th = self.BACKEND.get_thumbnail(self.portrait, '100x100', cropbox="0,50,100,150") engine = PgMagickEngine() im = engine.get_image(th) # If the crop went well, then it should scale to 100x100 perfectly self.assertEqual(im.width(100), 100) self.assertEqual(im.height(100), 100) @unittest.skipIf( 'convert_engine' not in settings.THUMBNAIL_ENGINE, 'the other engines fail this test', ) def convert_test_cropbox(self): from sorl.thumbnail.engines.convert_engine import Engine as ConvertEngine th = self.BACKEND.get_thumbnail(self.portrait, '100x100', cropbox="0,50,100,150") engine = ConvertEngine() im = engine.get_image(th) # If the crop went well, then it should scale to 100x100 perfectly self.assertEqual(im["size"], (100, 100)) class DummyTestCase(unittest.TestCase): def setUp(self): self.BACKEND = get_module_class(settings.THUMBNAIL_BACKEND)() def tearDown(self): super().tearDown() settings.THUMBNAIL_ALTERNATIVE_RESOLUTIONS = [] def test_dummy_tags(self): settings.THUMBNAIL_DUMMY = True val = render_to_string('thumbnaild1.html', {'anything': 'AINO', }).strip() self.assertEqual(val, '') val = render_to_string('thumbnaild2.html', {'anything': None, }).strip() self.assertEqual( val, '

NOT

' ) val = render_to_string('thumbnaild3.html', {}).strip() self.assertEqual(val, '') settings.THUMBNAIL_DUMMY = False def test_alternative_resolutions(self): settings.THUMBNAIL_DUMMY = True settings.THUMBNAIL_ALTERNATIVE_RESOLUTIONS = [1.5, 2] val = render_to_string('thumbnaild4.html', {}).strip() self.assertEqual( val, '' ) class ImageValidationTestCase(unittest.TestCase): def setUp(self): self.BACKEND = get_module_class(settings.THUMBNAIL_BACKEND)() @unittest.skip("See issue #427") def test_truncated_validation(self): """ Test that is_valid_image returns false for a truncated image. """ name = 'data/broken.jpeg' with open(name, 'rb') as broken_jpeg: data = broken_jpeg.read() engine = PILEngine() self.assertFalse(engine.is_valid_image(data)) @unittest.skip("See issue #427. This seems to not-fail with wand") def test_truncated_generation_failure(self): """ Confirm that generating a thumbnail for our "broken" image fails. """ name = 'data/broken.jpeg' with open(name, 'rb') as broken_jpeg: with self.assertRaises((OSError, IOError,)): im = default.engine.get_image(broken_jpeg) options = ThumbnailBackend.default_options ratio = default.engine.get_image_ratio(im, options) geometry = parse_geometry('120x120', ratio) default.engine.create(im, geometry, options) sorl-thumbnail-12.7.0/tests/thumbnail_tests/test_filters.py000066400000000000000000000030121375675401600242030ustar00rootroot00000000000000import pytest from django.template.loader import render_to_string from tests.thumbnail_tests.utils import BaseTestCase pytestmark = pytest.mark.django_db class FilterTestCase(BaseTestCase): def test_html_filter(self): text = 'A image!' val = render_to_string('htmlfilter.html', {'text': text, }).strip() self.assertEqual( 'A image!', val ) def test_html_filter_local_url(self): text = 'A image!' val = render_to_string('htmlfilter.html', {'text': text, }).strip() self.assertEqual( 'A image!', val ) def test_markdown_filter(self): text = '![A image!](http://dummyimage.com/800x800)' val = render_to_string('markdownfilter.html', {'text': text, }).strip() self.assertEqual( '![A image!](/media/test/cache/2e/35/2e3517d8aa949728b1ee8b26c5a7bbc4.jpg)', val ) def test_markdown_filter_local_url(self): text = '![A image!](/media/500x500.jpg)' val = render_to_string('markdownfilter.html', {'text': text, }).strip() self.assertEqual( '![A image!](/media/test/cache/c7/f2/c7f2880b48e9f07d46a05472c22f0fde.jpg)', val ) sorl-thumbnail-12.7.0/tests/thumbnail_tests/test_kvstore.py000066400000000000000000000011701375675401600242330ustar00rootroot00000000000000import threading import unittest from sorl.thumbnail.kvstores.cached_db_kvstore import KVStore class KVStoreTestCase(unittest.TestCase): @unittest.skipIf(threading is None, 'Test requires threading') def test_cache_backend(self): kv = KVStore() cache_backends = [] def thread_cache_backend(): cache_backends.append(kv.cache) for _ in range(2): t = threading.Thread(target=thread_cache_backend) t.start() t.join() # Cache backend for each thread needs to be unique self.assertNotEqual(cache_backends[0], cache_backends[1]) sorl-thumbnail-12.7.0/tests/thumbnail_tests/test_parsers.py000066400000000000000000000022651375675401600242230ustar00rootroot00000000000000import unittest from sorl.thumbnail.helpers import ThumbnailError from sorl.thumbnail.parsers import parse_crop, parse_geometry class CropParserTestCase(unittest.TestCase): def test_alias_crop(self): crop = parse_crop('center', (500, 500), (400, 400)) self.assertEqual(crop, (50, 50)) crop = parse_crop('right', (500, 500), (400, 400)) self.assertEqual(crop, (100, 50)) def test_percent_crop(self): crop = parse_crop('50% 0%', (500, 500), (400, 400)) self.assertEqual(crop, (50, 0)) crop = parse_crop('10% 80%', (500, 500), (400, 400)) self.assertEqual(crop, (10, 80)) def test_px_crop(self): crop = parse_crop('200px 33px', (500, 500), (400, 400)) self.assertEqual(crop, (100, 33)) def test_bad_crop(self): self.assertRaises(ThumbnailError, parse_crop, '-200px', (500, 500), (400, 400)) class GeometryParserTestCase(unittest.TestCase): def test_geometry(self): g = parse_geometry('222x30') self.assertEqual(g, (222, 30)) g = parse_geometry('222') self.assertEqual(g, (222, None)) g = parse_geometry('x999') self.assertEqual(g, (None, 999)) sorl-thumbnail-12.7.0/tests/thumbnail_tests/test_storage.py000066400000000000000000000033021375675401600242010ustar00rootroot00000000000000import unittest import pytest from sorl.thumbnail import get_thumbnail, default from sorl.thumbnail.helpers import get_module_class from .utils import BaseStorageTestCase pytestmark = pytest.mark.django_db class StorageTestCase(BaseStorageTestCase): name = 'org.jpg' def test_new(self): get_thumbnail(self.image, '50x50') actions = [ 'exists: test/cache/45/bb/45bbbdab11e235a80e603aa119e8786b.jpg', # open the original for thumbnailing 'open: org.jpg', # save the file 'save: test/cache/45/bb/45bbbdab11e235a80e603aa119e8786b.jpg', # check for filename 'get_available_name: test/cache/45/bb/45bbbdab11e235a80e603aa119e8786b.jpg', # called by get_available_name 'exists: test/cache/45/bb/45bbbdab11e235a80e603aa119e8786b.jpg', ] self.assertEqual(self.log, actions) def test_cached(self): get_thumbnail(self.image, '100x50') self.log = [] get_thumbnail(self.image, '100x50') self.assertEqual(self.log, []) # now this should all be in cache def test_safe_methods(self): im = default.kvstore.get(self.image) self.assertIsNotNone(im.url) self.assertIsNotNone(im.x) self.assertIsNotNone(im.y) self.assertEqual(self.log, []) class UrlStorageTestCase(unittest.TestCase): def test_encode_utf8_filenames(self): storage = get_module_class('sorl.thumbnail.images.UrlStorage')() self.assertEqual( storage.normalize_url('El jovencito emponzoñado de whisky, qué figura exhibe'), 'El%20jovencito%20emponzoado%20de%20whisky%2C%20qu%20figura%20exhibe' ) sorl-thumbnail-12.7.0/tests/thumbnail_tests/test_templatetags.py000066400000000000000000000170301375675401600252320ustar00rootroot00000000000000import os import re from subprocess import Popen, PIPE from PIL import Image from django.template.loader import render_to_string from django.test import Client, TestCase from django.test.utils import override_settings import pytest from sorl.thumbnail.conf import settings from sorl.thumbnail.engines.pil_engine import Engine as PILEngine from .models import Item from .utils import BaseTestCase, DATA_DIR pytestmark = pytest.mark.django_db class TemplateTestCaseA(BaseTestCase): def test_model(self): item = Item.objects.get(image='500x500.jpg') val = render_to_string('thumbnail1.html', {'item': item, }).strip() self.assertEqual(val, '') val = render_to_string('thumbnail2.html', {'item': item, }).strip() self.assertEqual(val, '') def test_nested(self): item = Item.objects.get(image='500x500.jpg') val = render_to_string('thumbnail6.html', {'item': item, }).strip() self.assertEqual(val, ( '' '' )) def test_serialization_options(self): item = Item.objects.get(image='500x500.jpg') for _ in range(0, 20): # we could be lucky... val0 = render_to_string('thumbnail7.html', { 'item': item, }).strip() val1 = render_to_string('thumbnail7a.html', { 'item': item, }).strip() self.assertEqual(val0, val1) def test_options(self): item = Item.objects.get(image='500x500.jpg') options = { 'crop': "center", 'upscale': True, 'quality': 77, } val0 = render_to_string('thumbnail8.html', {'item': item, 'options': options, }).strip() val1 = render_to_string('thumbnail8a.html', {'item': item, }).strip() self.assertEqual(val0, val1) def test_progressive(self): im = Item.objects.get(image='500x500.jpg').image th = self.BACKEND.get_thumbnail(im, '100x100', progressive=True) path = os.path.join(settings.MEDIA_ROOT, th.name) p = Popen(['identify', '-verbose', path], stdout=PIPE) p.wait() m = re.search('Interlace: JPEG', str(p.stdout.read())) p.stdout.close() self.assertEqual(bool(m), True) def test_nonprogressive(self): im = Item.objects.get(image='500x500.jpg').image th = self.BACKEND.get_thumbnail(im, '100x100', progressive=False) path = os.path.join(settings.MEDIA_ROOT, th.name) p = Popen(['identify', '-verbose', path], stdout=PIPE) p.wait() m = re.search('Interlace: None', str(p.stdout.read())) p.stdout.close() self.assertEqual(bool(m), True) def test_orientation(self): ref = Image.open(os.path.join(DATA_DIR, '1_topleft.jpg')) top = ref.getpixel((14, 7)) left = ref.getpixel((7, 14)) engine = PILEngine() def epsilon(x, y): if isinstance(x, (tuple, list)): x = sum(x) / len(x) if isinstance(y, (tuple, list)): y = sum(y) / len(y) return abs(x - y) data_images = ( '1_topleft.jpg', '2_topright.jpg', '3_bottomright.jpg', '4_bottomleft.jpg', '5_lefttop.jpg', '6_righttop.jpg', '7_rightbottom.jpg', '8_leftbottom.jpg' ) for name in data_images: th = self.BACKEND.get_thumbnail('data/%s' % name, '30x30') im = engine.get_image(th) self.assertLess(epsilon(top, im.getpixel((14, 7))), 10) self.assertLess(epsilon(left, im.getpixel((7, 14))), 10) exif = im._getexif() # no exif editor in GraphicsMagick if exif and not (settings.THUMBNAIL_CONVERT.endswith('gm convert') or 'pgmagick_engine' in settings.THUMBNAIL_ENGINE): self.assertEqual(exif.get(0x0112), 1) class TemplateTestCaseB(BaseTestCase): def test_url(self): val = render_to_string('thumbnail3.html', {}).strip() self.assertEqual(val, '') def test_portrait(self): val = render_to_string('thumbnail4.html', { 'source': 'http://dummyimage.com/120x100/', 'dims': 'x66', }).strip() self.assertEqual(val, '') def test_empty(self): val = render_to_string('thumbnail5.html', {}).strip() self.assertEqual(val, '

empty

') class TemplateTestCaseClient(TestCase): def test_empty_error(self): with override_settings(THUMBNAIL_DEBUG=False): from django.core.mail import outbox client = Client() response = client.get('/thumbnail9.html') self.assertEqual(response.content.strip(), b'

empty

') self.assertEqual(outbox[0].subject, '[sorl-thumbnail] ERROR: Unknown URL') end = outbox[0].body.split('\n\n')[-2].split(':')[1].strip() self.assertEqual(end, '[Errno 2] No such file or directory') class TemplateTestCaseTemplateTagAlias(BaseTestCase): """Testing alternative template tag (alias).""" def test_model(self): item = Item.objects.get(image='500x500.jpg') val = render_to_string( 'thumbnail1_alias.html', {'item': item} ).strip() self.assertEqual( val, '' ) val = render_to_string( 'thumbnail2_alias.html', {'item': item} ).strip() self.assertEqual( val, '' ) def test_nested(self): item = Item.objects.get(image='500x500.jpg') val = render_to_string( 'thumbnail6_alias.html', {'item': item} ).strip() self.assertEqual( val, ( '' '' ) ) def test_serialization_options(self): item = Item.objects.get(image='500x500.jpg') for _ in range(0, 20): # we could be lucky... val0 = render_to_string('thumbnail7_alias.html', { 'item': item, }).strip() val1 = render_to_string('thumbnail7a_alias.html', { 'item': item, }).strip() self.assertEqual(val0, val1) def test_options(self): item = Item.objects.get(image='500x500.jpg') options = { 'crop': "center", 'upscale': True, 'quality': 77, } val0 = render_to_string( 'thumbnail8_alias.html', {'item': item, 'options': options} ).strip() val1 = render_to_string( 'thumbnail8a_alias.html', {'item': item} ).strip() self.assertEqual(val0, val1) sorl-thumbnail-12.7.0/tests/thumbnail_tests/urls.py000066400000000000000000000005001375675401600224600ustar00rootroot00000000000000from django.conf.urls import url from django.conf import settings from django.views.static import serve from .views import direct_to_template urlpatterns = [ url(r'^media/(?P.+)$', serve, {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}), url(r'^(.*\.html)$', direct_to_template), ] sorl-thumbnail-12.7.0/tests/thumbnail_tests/utils.py000066400000000000000000000070321375675401600226420ustar00rootroot00000000000000import os import shutil import unittest import logging from contextlib import contextmanager from subprocess import check_output from PIL import Image, ImageDraw from sorl.thumbnail.conf import settings from sorl.thumbnail.helpers import get_module_class from sorl.thumbnail.images import ImageFile from sorl.thumbnail.log import ThumbnailLogHandler from .models import Item from .storage import MockLoggingHandler DATA_DIR = os.path.join(settings.MEDIA_ROOT, 'data') handler = ThumbnailLogHandler() handler.setLevel(logging.ERROR) logging.getLogger('sorl.thumbnail').addHandler(handler) @contextmanager def same_open_fd_count(testcase): num_opened_fd_before = get_open_fds_count() yield num_opened_fd_after = get_open_fds_count() testcase.assertEqual( num_opened_fd_before, num_opened_fd_after, 'Open descriptors count changed, was %s, now %s' % (num_opened_fd_before, num_opened_fd_after) ) def get_open_fds_count(): """Return the number of open file descriptors for current process .. warning: will only work on UNIX-like os-es. """ pid = os.getpid() procs = check_output(["lsof", '-w', '-Ff', "-p", str(pid)]) nprocs = len( [s for s in procs.decode('utf-8').split('\n') if s and s[0] == 'f' and s[1:].isdigit()] ) return nprocs class FakeFile: """ Used to test the _get_format method. """ def __init__(self, name): self.name = name class BaseTestCase(unittest.TestCase): IMAGE_DIMENSIONS = [(500, 500), (100, 100), (200, 100), ] BACKEND = None ENGINE = None KVSTORE = None def create_image(self, name, dim, transparent=False): """ Creates an image and prepends the MEDIA ROOT path. :param name: e.g. 500x500.jpg :param dim: a dimension tuple e.g. (500, 500) """ filename = os.path.join(settings.MEDIA_ROOT, name) im = Image.new('L', dim) if transparent: draw = ImageDraw.Draw(im) draw.line((0, 0) + im.size, fill=128) draw.line((0, im.size[1], im.size[0], 0), fill=128) im.save(filename, transparency=0) else: im.save(filename) return Item.objects.get_or_create(image=name) def is_transparent(self, img): return img.mode in ('RGBA', 'LA') or 'transparency' in img.info def setUp(self): self.BACKEND = get_module_class(settings.THUMBNAIL_BACKEND)() self.ENGINE = get_module_class(settings.THUMBNAIL_ENGINE)() self.KVSTORE = get_module_class(settings.THUMBNAIL_KVSTORE)() if not os.path.exists(settings.MEDIA_ROOT): os.makedirs(settings.MEDIA_ROOT) shutil.copytree(settings.DATA_ROOT, DATA_DIR) for dimension in self.IMAGE_DIMENSIONS: name = '%sx%s.jpg' % dimension self.create_image(name, dimension) def tearDown(self): shutil.rmtree(settings.MEDIA_ROOT) class BaseStorageTestCase(unittest.TestCase): image = None name = None def setUp(self): os.makedirs(settings.MEDIA_ROOT) filename = os.path.join(settings.MEDIA_ROOT, self.name) Image.new('L', (100, 100)).save(filename) self.image = ImageFile(self.name) logger = logging.getLogger('slog') logger.setLevel(logging.DEBUG) handler = MockLoggingHandler(level=logging.DEBUG) logger.addHandler(handler) self.log = handler.messages['debug'] def tearDown(self): shutil.rmtree(settings.MEDIA_ROOT) sorl-thumbnail-12.7.0/tests/thumbnail_tests/views.py000066400000000000000000000004031375675401600226320ustar00rootroot00000000000000from django.template import loader from django.http import HttpResponse def direct_to_template(request, template, mimetype=None, **kwargs): t = loader.get_template(template) return HttpResponse(t.render({'request': request}), content_type=mimetype) sorl-thumbnail-12.7.0/tox.ini000066400000000000000000000027421375675401600160770ustar00rootroot00000000000000[gh-actions] python = 3.6: py36 3.7: py37 3.8: py38 [gh-actions:env] DJANGO = 2.2: django22 3.0: django30 3.1: django31 TARGET = pil: pil imagemagick: imagemagick graphicsmagick: graphicsmagick redis: redis wand: wand dbm: dbm qa: qa [tox] skipsdist = True envlist = py{36,37,38}-qa, py{36,37,38}-django{22,30,31}-{pil,imagemagick,graphicsmagick,redis,dynamodb,wand,pgmagick,dbm,vipsthumbnail} [testenv] deps = pytest pytest-cov pytest-django pillow redis: redis dynamodb: boto pgmagick: pgmagick wand: wand django22: django>=2.2,<2.3 django30: django>=3.0,<3.1 django31: django>=3.1,<3.2 setenv = PYTHONPATH = {toxinidir}:{toxinidir} pil: DJANGO_SETTINGS_MODULE=tests.settings.pil imagemagick: DJANGO_SETTINGS_MODULE=tests.settings.imagemagick graphicsmagick: DJANGO_SETTINGS_MODULE=tests.settings.graphicsmagick vipsthumbnail: DJANGO_SETTINGS_MODULE=tests.settings.vipsthumbnail redis: DJANGO_SETTINGS_MODULE=tests.settings.redis dynamodb: DJANGO_SETTINGS_MODULE=tests.settings.dynamodb wand: DJANGO_SETTINGS_MODULE=tests.settings.wand pgmagick: DJANGO_SETTINGS_MODULE=tests.settings.pgmagick dbm: DJANGO_SETTINGS_MODULE=tests.settings.dbm commands = pytest -rw --cov-append --cov-config setup.cfg --cov sorl --cov-report=xml [testenv:py{36,37,38}-qa] skip_install = True deps = flake8 flake8-bugbear rstvalidator commands = flake8 --show-source sorl/ python -m rstvalidator README.rst CHANGES.rst CONTRIBUTING.rst sorl-thumbnail-12.7.0/vagrant.sh000066400000000000000000000011761375675401600165620ustar00rootroot00000000000000#!/bin/sh apt-get update apt-get install -qq git python-software-properties python-pip apt-get install -qq libjpeg62 libjpeg62-dev zlib1g-dev imagemagick graphicsmagick redis-server apt-get install -qq libmagickwand-dev libgraphicsmagick++-dev libboost-python-dev libboost-thread-dev apt-get install -qq libvips-tools add-apt-repository -y ppa:deadsnakes/ppa apt-get update apt-get install -qq python2.7 python2.7-dev python3.4 python3.4-dev pip install tox # Fix locale to allow saving unicoded filenames echo 'LANG=en_US.UTF-8' > /etc/default/locale # Start in project dir by default echo "\n\ncd /vagrant" >> /home/vagrant/.bashrc