pax_global_header00006660000000000000000000000064151751025160014515gustar00rootroot0000000000000052 comment=3e1c2319d6a3fbf2ae4d46c3ab734b7fc87bd9b4 pdf2docx-0.5.13/000077500000000000000000000000001517510251600133145ustar00rootroot00000000000000pdf2docx-0.5.13/.github/000077500000000000000000000000001517510251600146545ustar00rootroot00000000000000pdf2docx-0.5.13/.github/ISSUE_TEMPLATE/000077500000000000000000000000001517510251600170375ustar00rootroot00000000000000pdf2docx-0.5.13/.github/ISSUE_TEMPLATE/bug_report.yml000066400000000000000000000050531517510251600217350ustar00rootroot00000000000000name: Bug Report description: | Create a bug report for pdf2docx. Please use English only in posts and issues to aid maintainers and benefit all users. We will immediately reject and close non-English posts. # We omit `title: "..."` so that the field defaults to blank. If we set it to # empty string, Github seems to reject this .yml file. body: - type: textarea id: description attributes: label: Description of the bug description: | A clear and concise description of the bug. validations: required: true - type: textarea id: reproduce attributes: label: How to reproduce the bug # Should not word-wrap this description here. description: | * Explain the steps required to reproduce the bug. * Include required code snippets, example files, etc. * Describe what you expected to happen (if not obvious). * If applicable, add screenshots to help explain the problem. * Include any other information that could be relevant, for example information about the Python environment. For problems when building or installing pdf2docx: * Give the **exact** build/install commands that were run. * Give the **complete** output from these commands. validations: required: true # - type: markdown # attributes: # value: | # # The information below is required. - type: dropdown id: version attributes: label: pdf2docx version options: - 0.5.13 - 0.5.12 - 0.5.11 - 0.5.10 - 0.5.9 - 0.5.8 - 0.5.7 - 0.5.6 - 0.5.5 - 0.5.4 - 0.5.3 - 0.5.2 - 0.5.1 - 0.5.0 - 0.4.6 - Built from source description: | *`Version` from `pip show pdf2docx`. * We generally only look at bugs in the most recent release of pdf2docx. validations: required: true - type: dropdown id: os_name attributes: label: Operating system #multiple: true options: - - Windows - Linux - MacOS - OpenBSD - Other validations: required: true - type: dropdown id: python_version attributes: label: Python version #multiple: true # Need quotes around `3.10` otherwise it is treated as a number and shows as `3.1`. options: - - "3.12" - "3.11" - "3.10" - "3.9" - "3.8" validations: required: true pdf2docx-0.5.13/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000010711517510251600225630ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: enhancement assignees: --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Potentially add an issue reference. **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** Are there several options for how your request could be met? **Additional context** Add any other context or screenshots about the feature request here. pdf2docx-0.5.13/.github/workflows/000077500000000000000000000000001517510251600167115ustar00rootroot00000000000000pdf2docx-0.5.13/.github/workflows/publish.yml000066400000000000000000000066621517510251600211140ustar00rootroot00000000000000# Create release and publish to Pypi when pushing tags name: publish # Trigger the workflow on push tags, matching vM.n.p, i.e. v3.2.10 on: push: tags: - 'v[0-9]+.[0-9]+.[0-9]+' # Jobs to do: # - build package # - create release with asset # - publish to Pypi jobs: # ----------------------------------------------------------- # create python env and setup package # ----------------------------------------------------------- build: # The type of runner that the job will run on runs-on: ubuntu-latest # Steps represent a sequence of tasks that will be executed as part of the job steps: - name: Check out code uses: actions/checkout@v2 - name: Set up Python 3.x uses: actions/setup-python@v1 with: python-version: '3.10' - name: Display Python version run: python -c "import sys; print(sys.version)" - name: Install dependencies run: | python -m pip install --upgrade pip pip install setuptools wheel pytest # build package for tags, e.g. 3.2.1 extracted from 'refs/tags/v3.2.1' - name: Build package run: | echo ${GITHUB_REF#refs/tags/v} > version.txt python setup.py sdist --formats=gztar,zip python setup.py bdist_wheel # test wheel file locally - name: Test package run: | pip install ./dist/*.whl cd test pytest -v test.py::TestConversion # upload the artifacts for further jobs # - app-tag.tar.gz # - app-tag.zip # - app-tag-info.whl - name: Archive package uses: actions/upload-artifact@v4 with: name: dist path: ./dist # ----------------------------------------------------------- # create release and upload asset # ----------------------------------------------------------- release: runs-on: ubuntu-latest # Run this job after build completes successfully needs: build steps: - name: Checkout code uses: actions/checkout@v2 # download artifacts from job: build - name: Download artifacts from build uses: actions/download-artifact@v4 with: name: dist path: dist # create release and upload assets - name: Create release with assets uses: softprops/action-gh-release@v1 with: files: | ./dist/*.tar.gz ./dist/*.zip ./dist/*.whl env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # ----------------------------------------------------------- # publish to Pypi # ----------------------------------------------------------- publish: runs-on: ubuntu-latest # Run this job after both build and release completes successfully needs: [build, release] steps: - name: Checkout code uses: actions/checkout@v2 - name: Download artifacts from release uses: actions/download-artifact@v4 with: name: dist path: dist # Error when two sdist files created in build job are uploaded to Pipi: # HTTPError: 400 Bad Request from https://upload.pypi.org/legacy/ # Only one sdist may be uploaded per release. - name: Remove duplicated sdist run: rm ./dist/*.zip - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@master with: password: ${{ secrets.PYPI_PASSWORD }}pdf2docx-0.5.13/.github/workflows/test.yml000066400000000000000000000050311517510251600204120ustar00rootroot00000000000000# Run test when triggering the workflow on push and pull request, # but only for the master branch name: test on: push: branches: - master pull_request: branches: - master # ----------------------------------------------------------------------------------------------------- # To leverage the benefit of Github Action, the testing process is divided into three jobs: # 1. pdf2docx: convert sample pdf to docx -> linux runner # 2. docx2pdf: convert generated docx to pdf for comparing -> specific runner with MS Word installed # 3. check_quality: convert page to image and compare similarity with python-opencv -> linux runner # However, keep step 1 only, considering the difficulty to get a specific runner with MS Word installed. # ----------------------------------------------------------------------------------------------------- jobs: pdf2docx-docker: runs-on: ubuntu-latest container: image: python:3.10 steps: - name: Check out code uses: actions/checkout@v2 - name: Install dependencies run: | python -m pip install --upgrade pip pip install setuptools wheel pip install -r requirements.txt pip install pytest python setup.py develop - name: Run unit test run: | pytest -v ./test/test.py::TestConversion pdf2docx-ubuntu: runs-on: ubuntu-latest needs: pdf2docx-docker strategy: matrix: python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - name: Check out code uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install setuptools wheel pip install -r requirements.txt pip install pytest pytest-cov python setup.py develop - name: Run unit test run: | pytest -v ./test/test.py::TestConversion --cov=./pdf2docx --cov-report=xml - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3 with: # Or as an environment variable token: ${{ secrets.CODECOV_TOKEN }} # upload docx for further job - name: Archive package uses: actions/upload-artifact@v4 with: name: outputs-py${{ matrix.python-version }} path: ./test/outputspdf2docx-0.5.13/.gitignore000066400000000000000000000003061517510251600153030ustar00rootroot00000000000000# files *.pyc *.jp*g layout.json .vscode/ # pdf testing files *.pdf !demo*.pdf *coverage* test/issues/ test/features/ test/outputs/ diff.png # building dir build/ dist/ *egg-info/ pdf2docx*.rst pdf2docx-0.5.13/.readthedocs.yaml000066400000000000000000000013401517510251600165410ustar00rootroot00000000000000# .readthedocs.yaml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-20.04 tools: python: "3.9" # You can also specify other tool versions: # nodejs: "16" # rust: "1.55" # golang: "1.17" # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # If using Sphinx, optionally build your docs in additional formats such as PDF #formats: # - pdf # Optionally declare the Python requirements required to build your docs python: install: - requirements: docs/requirements.txt pdf2docx-0.5.13/LICENSE000066400000000000000000000020511517510251600143170ustar00rootroot00000000000000Copyright (c) 2026 Artifex Software, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.pdf2docx-0.5.13/MANIFEST.in000066400000000000000000000001561517510251600150540ustar00rootroot00000000000000include *.md include LICENSE* include requirements.txt prune test include test/*.py include test/samples/*.pdfpdf2docx-0.5.13/Makefile000066400000000000000000000017531517510251600147620ustar00rootroot00000000000000# Project makefile # working directories and files # TOPDIR :=$(shell pwd) SRC :=$(TOPDIR)/pdf2docx BUILD :=$(TOPDIR)/build DOCSRC :=$(TOPDIR)/doc TEST :=$(TOPDIR)/test CLEANDIRS :=.pytest_cache pdf2docx.egg-info dist # pip install sphinx_rtd_theme .PHONY: src doc test clean src: @python setup.py sdist --formats=gztar,zip && \ python setup.py bdist_wheel doc: @if [ -f "$(DOCSRC)/Makefile" ] ; then \ ( cd "$(DOCSRC)" && make html MODULEDIR="$(SRC)" BUILDDIR="$(BUILD)" ) || exit 1 ; \ fi test: @if [ -f "$(TEST)/Makefile" ] ; then \ ( cd "$(TEST)" && make test SOURCEDIR="$(SRC)" ) || exit 1 ; \ fi clean: @if [ -e "$(DOCSRC)/Makefile" ] ; then \ ( cd "$(DOCSRC)" && make $@ BUILDDIR="$(BUILD)" ) || exit 0 ; \ fi @for p in $(CLEANDIRS) ; do \ if [ -d "$(TOPDIR)/$$p" ]; then rm -rf "$(TOPDIR)/$$p" ; fi ; \ done @if [ -d "$(BUILD)" ]; then rm -rf "$(BUILD)" ; fi @if [ -e "$(TEST)/Makefile" ] ; then \ ( cd "$(TEST)" && make $@ ) || exit 0 ; \ fipdf2docx-0.5.13/README.md000066400000000000000000000027361517510251600146030ustar00rootroot00000000000000# pdf2docx ![python-version](https://img.shields.io/badge/python->=3.6-green.svg) ![pypi-downloads](https://img.shields.io/pypi/dm/pdf2docx) ## âš ï¸� Project Status **pdf2docx is no longer actively maintained by Artifex.** The repository will remain available and has been **relicensed under the MIT License** so that the community can freely use, fork, and maintain the project. Pull requests from the community are welcome, but [Artifex](https://artifex.com) no longer provides active development or maintenance. --- If you are looking for a **full-featured PDF processing library**, consider using [PyMuPDF](https://github.com/pymupdf/PyMuPDF) or [MuPDF.NET](https://github.com/ArtifexSoftware/MuPDF.NET). ## Documentation - [Installation](https://pdf2docx.readthedocs.io/en/latest/installation.html) - [Quickstart](https://pdf2docx.readthedocs.io/en/latest/quickstart.html) - [Convert PDF](https://pdf2docx.readthedocs.io/en/latest/quickstart.convert.html) - [Extract table](https://pdf2docx.readthedocs.io/en/latest/quickstart.table.html) - [Command Line Interface](https://pdf2docx.readthedocs.io/en/latest/quickstart.cli.html) - [Graphic User Interface](https://pdf2docx.readthedocs.io/en/latest/quickstart.gui.html) - [Technical Documentation (In Chinese)](https://pdf2docx.readthedocs.io/en/latest/techdoc.html) - [API Documentation](https://pdf2docx.readthedocs.io/en/latest/modules.html) ## Sample ![sample_compare.png](https://s1.ax1x.com/2020/08/04/aDryx1.png)pdf2docx-0.5.13/docs/000077500000000000000000000000001517510251600142445ustar00rootroot00000000000000pdf2docx-0.5.13/docs/Makefile000066400000000000000000000007151517510251600157070ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # MODULEDIR and BUILDDIR are set in top makefile SOURCEDIR = . TARGETDIR = doctrees html .PHONY: html clean html: Makefile @sphinx-apidoc --separate -o "$(SOURCEDIR)" "$(MODULEDIR)" && \ sphinx-build -M html "$(SOURCEDIR)" "$(BUILDDIR)" clean: @for p in $(TARGETDIR) ; do \ if [ -d "$(BUILDDIR)/$$p" ]; then rm -rf "$(BUILDDIR)/$$p" ; fi ; \ done @if [ -e modules.rst ]; then rm pdf2docx*.rst ; fipdf2docx-0.5.13/docs/README.md000066400000000000000000000022361517510251600155260ustar00rootroot00000000000000# pdf2docx documentation Welcome to the **pdf2docx** documentation. This documentation relies on [Sphinx](https://www.sphinx-doc.org/en/master/) to publish HTML docs from markdown files written with [restructured text](https://en.wikipedia.org/wiki/ReStructuredText) (RST). ## Sphinx version This README assumes you have [Sphinx v5.0.2 installed](https://www.sphinx-doc.org/en/master/usage/installation.html) on your system. ## Updating the documentation Within `docs` update the associated restructured text (`.rst`) files. These files represent the corresponding document pages. ## Building HTML documentation - Ensure you have the `furo` theme installed: `pip install furo` Furo theme, Copyright (c) 2020 Pradyun Gedam , thank you to: https://github.com/pradyunsg/furo/blob/main/LICENSE - From the "docs" location run: `sphinx-build -b html . build/html` This then creates the HTML documentation within `build/html`. > Use: `sphinx-build -a -b html . build/html` to build all, including the assets in `_static` (important if you have updated CSS). For full details see: [Using Sphinx](https://www.sphinx-doc.org/en/master/usage/index.html) pdf2docx-0.5.13/docs/_static/000077500000000000000000000000001517510251600156725ustar00rootroot00000000000000pdf2docx-0.5.13/docs/_static/PyMuPDF.ico000066400000000000000000000353561517510251600176260ustar00rootroot0000000000000000 ¨%6  ¨Þ% h†6(0` $ÿÿÿÿÿÿˆÿÿÿßÿÿÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿáÿÿÿ�ÿÿÿ!ÿÿÿ/ÿÿÿÃÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÇÿÿÿ4ÿÿÿÿÿÿÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÿÿÿ!ÿÿÿƒÿÿÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿðððÿáááÿÕÕÕÿÎÎÎÿÏÏÏÿ×××ÿäääÿóóóÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿ�ÿÿÿØÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ÿÓÓÓÿ¡¡¡ÿuuuÿWWWÿFFFÿ>>>ÿ;;;ÿ;;;ÿ???ÿIIIÿ\\\ÿ}}}ÿ«««ÿÜÜÜÿúúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÿÿÿùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðððÿ³³³ÿkkkÿ???ÿ111ÿ///ÿ/01ÿ/12ÿ/13ÿ013ÿ013ÿ/13ÿ/02ÿ/00ÿ///ÿ222ÿEEEÿwwwÿÁÁÁÿöööÿÿÿÿÿÿÿÿÿÿÿÿÿáááÿ¥¥¥ÿŸŸŸÿÑÑÑÿüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿ¼¼¼ÿaaaÿ555ÿ/00ÿ012ÿ123ÿ641ÿD:-ÿTA(ÿ`F$ÿgI"ÿfH"ÿ^E%ÿP?)ÿA9.ÿ532ÿ023ÿ012ÿ///ÿ999ÿpppÿÌÌÌÿóóóÿ°°°ÿRRRÿ111ÿ000ÿAAAÿ•••ÿôôôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçççÿƒƒƒÿ:::ÿ000ÿ123ÿ432ÿN>*ÿyPÿ aÿ¹kÿÆoÿËqÿÍrÿÍrÿÊqÿÄnÿµiÿ™^ÿpMÿG;,ÿ233ÿ123ÿ000ÿCCCÿ```ÿ888ÿ111ÿ333ÿ333ÿ111ÿ444ÿ‹‹‹ÿøøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÔÔÔÿ```ÿ111ÿ122ÿ333ÿS@(ÿ’[ÿÀmÿÐtÿÓ{ÿׇÿÛ•:ÿß¡Pÿá§\ÿá§\ÿß¡QÿÜ–=ÿ؉"ÿÔ| ÿÎtÿ¹jÿ†VÿJ=+ÿ123ÿ112ÿ000ÿ222ÿ333ÿ333ÿ333ÿ333ÿ222ÿ666ÿ¦¦¦ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌÌÿQQQÿ000ÿ123ÿ?8/ÿ‚TÿÁmÿÓyÿÚ’5ÿç¹|ÿóÚ»ÿúïáÿýùóÿÿýüÿÿÿÿÿÿÿÿÿÿþüÿýúõÿúðãÿôÝÀÿ龆ÿݘ?ÿÓ| ÿ¹jÿsNÿ851ÿ123ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ111ÿGGGÿÔÔÔÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÓÓÿQQQÿ000ÿ123ÿM>*ÿ£a ÿÐxÿÞ›EÿðÓ®ÿýøñÿÿÿÿÿþúõÿýúôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýùóÿóÚ»ÿá¥VÿÏz ÿ“ZÿB9.ÿ123ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ000ÿwwwÿøøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæææÿ___ÿ000ÿ123ÿS@(ÿ²gÿ؈!ÿìÈ™ÿýøòÿùîÞÿìȘÿá©_ÿÙ‘4ÿìÆ–ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýøòÿþúöÿñÖ²ÿÚ‘3ÿ¢a ÿF:,ÿ123ÿ333ÿ333ÿ333ÿ333ÿ333ÿ222ÿ;;;ÿÅÅÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿƒƒƒÿ111ÿ123ÿM>*ÿ²hÿÞšBÿöåÏÿÿÿÿÿÿÿÿÿéÀŠÿÏtÿÎsÿÎqÿ輄ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿå´sÿâ«cÿöãÌÿúîàÿã©^ÿ¡bÿ@8-ÿ223ÿ333ÿ333ÿ333ÿ222ÿ444ÿ...ÿ~~~ÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»»»ÿ:::ÿ122ÿ?8/ÿ£b ÿá¢PÿúñäÿÿÿÿÿÿÿÿÿÿÿÿÿõâÉÿÓ~ÿÏtÿÏsÿ轄ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿà¥ZÿÎqÿÓ€ÿæµvÿøê×ÿæ³rÿ�[ÿ741ÿ233ÿ333ÿ333ÿzzzÿ¸¸¸ÿZZZÿOOOÿëëëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðððÿaaaÿ000ÿ333ÿ‚SÿÝ›EÿûñäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúöÿÝ›GÿÏsÿÏsÿ轄ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿà¦ZÿÎsÿÏtÿÏtÿÚ”9ÿóÙ·ÿܦ_ÿhH ÿ123ÿ222ÿ777ÿÂÂÂÿÿÿÿÿ’’’ÿ???ÿÝÝÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³³³ÿ555ÿ123ÿS?&ÿÈ‚'ÿøçÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëÅ”ÿÏuÿÏsÿ轄ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿà¦ZÿÎsÿÏuÿÏuÿÏsÿÕ† ÿÛ’3ÿªe ÿ=7/ÿ223ÿ222ÿiiiÿŸŸŸÿMMMÿOOOÿêêêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ÿkkkÿ/00ÿ432ÿ‘YÿÜ“5ÿü÷ïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøêØÿÕ„ÿÎrÿ轄ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿà¦ZÿÎsÿÏuÿÏuÿÏuÿÏtÿÏtÿÌtÿkK ÿ023ÿ222ÿ...ÿ111ÿRRRÿ´´´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÔÔÔÿ@@@ÿ012ÿM>*ÿÀnÿÒ{ ÿòÚ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüÿà¦ZÿÎqÿ轄ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿà¦ZÿÎsÿÏuÿÏuÿÏuÿÏuÿÏuÿÑvÿªe ÿ;5.ÿ444ÿ\\\ÿªªªÿéééÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢¢¢ÿ111ÿ023ÿxPÿÐuÿÏsÿä¯lÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïШÿÏuÿ轄ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿà¦ZÿÎsÿÏuÿÏuÿÏuÿÏuÿÏuÿÐuÿËrÿ‡e9ÿª«¬ÿîîîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿvvvÿ///ÿ642ÿŸaÿÒvÿÏtÿÖ‰&ÿúïáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúñåÿÖ‰&ÿ軂ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿà¦ZÿÎsÿÏuÿÏuÿÏuÿÏuÿÏuÿÏuÿÏsÿÛ™DÿýúöÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðððÿXXXÿ/01ÿC:-ÿ¹kÿÑvÿÏuÿÐvÿîÌ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿã¯kÿè»�ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿà¦ZÿÎsÿÏuÿÏuÿÏuÿÏuÿÏuÿÏuÿÏtÿÔ†!ÿ®¤—ÿvvwÿÁÁÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâââÿGGGÿ/12ÿS@(ÿÆqÿÐuÿÏuÿÏsÿÞ PÿþüùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòظÿêÂ�ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿà¦ZÿÎsÿÏuÿÏuÿÏuÿÏuÿÏuÿÏuÿÐuÿÁpÿK?/ÿ+,-ÿaaaÿõõõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÕÕÿ???ÿ/13ÿ_F$ÿÌsÿÐuÿÏuÿÏtÿÓ€ÿöåÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüõìÿóܽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿà¦ZÿÎsÿÏuÿÏuÿÏuÿÏuÿÏuÿÏuÿÐuÿÁoÿK=*ÿ/01ÿPPPÿëëëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÏÏÿ;;;ÿ013ÿfH"ÿÎtÿÏuÿÏuÿÏuÿÏtÿ轄ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþúöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿà¦ZÿÎsÿÏuÿÏuÿÏuÿÏuÿÏuÿÏuÿÐuÿÅqÿP?)ÿ/02ÿLLLÿçççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÏÏÿ;;;ÿ013ÿfH"ÿÎtÿÏuÿÏuÿÏuÿÏsÿÚ“8ÿüöîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿà¦ZÿÎsÿÏuÿÏuÿÏuÿÏuÿÏuÿÏuÿÐuÿÄpÿP?)ÿ/02ÿLLLÿèèèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×××ÿ???ÿ/13ÿ]D#ÿËsÿÐuÿÏuÿÏuÿÏuÿÑz ÿòØ·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿá§\ÿÎsÿÏuÿÏuÿÏuÿÏuÿÏuÿÏuÿÐuÿÀnÿJ<+ÿ/01ÿSSSÿíííÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìììÿZZZÿ013ÿv^?ÿÏwÿÏuÿÏuÿÏuÿÏuÿÏsÿã­hÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâ«cÿÎsÿÏuÿÏuÿÏuÿÏuÿÏuÿÏuÿÑvÿµjÿ@8.ÿ/00ÿaaaÿöööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×××ÿ³´µÿàлÿÔ€ÿÏtÿÏuÿÏuÿÏuÿÏtÿÖˆ#ÿùîßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿå´tÿÏsÿÏuÿÏuÿÏuÿÏuÿÏuÿÏuÿÒvÿ aÿ642ÿ///ÿzzzÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýöíÿÛ‘2ÿÏsÿÏuÿÏuÿÏuÿÏuÿÐvÿíÊ�ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëÅ”ÿÏtÿÏuÿÏuÿÏuÿÏuÿÏuÿÏuÿÑvÿ€Sÿ123ÿ111ÿŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæææÿµ†JÿÍsÿÏuÿÏuÿÏuÿÏuÿÏsÿÞŸMÿþûøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõáÈÿÒ}ÿÏtÿÏuÿÏuÿÏuÿÏuÿÐuÿÇrÿXC&ÿ013ÿ;;;ÿËËËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿßßßÿ›››ÿSTUÿG=/ÿ¶jÿÑvÿÏuÿÏuÿÏuÿÏtÿÓÿöãÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüùÿä°mÿÏuÿÎsÿÏsÿÏtÿÏtÿÒvÿ§d ÿ:60ÿ000ÿ[[[ÿðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîîÿŒŒŒÿFFFÿ///ÿ000ÿ123ÿ{QÿÐuÿÏtÿÏtÿÏuÿÏuÿÏtÿè»�ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý÷ðÿìȘÿÞŸLÿØ�,ÿÕ‡ ÿÔ‚ÿÎvÿnLÿ123ÿ111ÿ———ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥¥¥ÿ555ÿxxxÿzzzÿ777ÿ123ÿE:-ÿµjÿ؉"ÿÖ‡"ÿÏsÿÏuÿÏsÿÙ’5ÿüõíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüúÿüõìÿúñåÿöÞ¿ÿ¬s(ÿ<6.ÿ111ÿGGGÿÛÛÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡‡‡ÿMMMÿëëëÿëëëÿQQQÿ111ÿ123ÿqLÿÚ›IÿóÚ¹ÿÜ™CÿÏtÿÏtÿÑyÿñ×´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷ÿÚ®uÿ]F'ÿ123ÿ111ÿŽŽŽÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸÿ:::ÿœœœÿ���ÿ===ÿ222ÿ223ÿ851ÿ“\ÿã¬cÿøê×ÿ軀ÿÔƒÿÎrÿÝœIÿôÞÁÿõâÉÿõâÉÿõâÉÿõâÉÿõâÉÿõâÉÿõâÉÿõâÉÿõâÉÿõâÉÿõâÉÿõâÉÿõâÉÿõáÇÿõàÅÿùíÞÿÿÿÿÿþúõÿå·|ÿ~Uÿ322ÿ001ÿSSSÿâââÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÐÐÿ>>>ÿ222ÿ333ÿ222ÿ333ÿ333ÿ123ÿA9-ÿ¢bÿá£SÿùìÜÿ÷çÒÿä±nÿÕ† ÿÒ{ ÿÑz ÿÑ{ ÿÒ| ÿÒ| ÿÒ| ÿÒ| ÿÒ| ÿÒ| ÿÒ| ÿÒ| ÿÑ{ ÿÑz ÿÒ}ÿ׌+ÿæµvÿøëÚÿüóèÿã®iÿ�\ÿ950ÿ122ÿ:::ÿµµµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿsssÿ///ÿ333ÿ333ÿ333ÿ333ÿ333ÿ123ÿE:,ÿ ` ÿÙŽ-ÿñÓ­ÿþûøÿúïáÿíÊ�ÿà¦[ÿØ�,ÿÓÿÐxÿÏuÿÏtÿÏtÿÏuÿÐxÿÓÿØ�,ÿà¦ZÿíÉ›ÿùíÝÿþýúÿôܾÿÙ–>ÿŽYÿ<6/ÿ123ÿ333ÿ���ÿûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÉÉÿ???ÿ222ÿ333ÿ333ÿ333ÿ333ÿ333ÿ123ÿ@8.ÿŽXÿÌx ÿá¤Uÿóܾÿþûøÿÿÿÿÿüöïÿ÷çÒÿòÙ¸ÿîΤÿìÈšÿìÈ™ÿîΣÿòØ·ÿ÷çÒÿüöîÿÿÿÿÿÿýûÿõâÉÿå­dÿÇyÿ|Pÿ951ÿ123ÿ221ÿ|||ÿòòòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿ���ÿ111ÿ333ÿ333ÿ333ÿ333ÿ333ÿ333ÿ223ÿ642ÿjJ ÿ±gÿÒ| ÿÞ›CÿêÂŽÿõâÊÿüõíÿÿþýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýÿü÷ïÿöåÎÿìÇ–ÿà LÿÐ}ÿ¦b ÿ\D$ÿ333ÿ122ÿ333ÿ|||ÿîîîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêêêÿeeeÿ000ÿ333ÿ333ÿ333ÿ333ÿ111ÿ000ÿ112ÿ123ÿC:-ÿyPÿ¯eÿÊrÿÔ~ÿÚ�*ÿß�Hÿâ©_ÿä°lÿå°lÿãªaÿߟJÿÛ�-ÿÔÿÈrÿ¦b ÿmKÿ<70ÿ123ÿ000ÿ:::ÿ���ÿòòòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÚÚÿ\\\ÿ///ÿ111ÿ222ÿ000ÿMMMÿÿIIIÿ000ÿ112ÿ123ÿ>8/ÿ_F$ÿ†Vÿ¤a ÿ·iÿÀlÿÄnÿÄnÿ¿lÿ´hÿ ` ÿ€SÿYC&ÿ:60ÿ123ÿ111ÿ111ÿTTTÿ¶¶¶ÿúúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàààÿÿGGGÿCCCÿ{{{ÿÚÚÚÿþþþÿÖÖÖÿ~~~ÿ@@@ÿ000ÿ001ÿ013ÿ123ÿ851ÿA9.ÿK=+ÿP?)ÿP?)ÿJ<+ÿ@8.ÿ642ÿ123ÿ013ÿ000ÿ111ÿHHHÿ���ÿäääÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúúúÿáááÿÝÝÝÿøøøÿÿÿÿÿÿÿÿÿÿÿÿÿûûûÿÑÑÑÿ‹‹‹ÿSSSÿ888ÿ000ÿ///ÿ/00ÿ/01ÿ/02ÿ/02ÿ/01ÿ/00ÿ///ÿ111ÿ;;;ÿ[[[ÿ˜˜˜ÿÜÜÜÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿëëëÿÃÃÃÿ˜˜˜ÿuuuÿ^^^ÿRRRÿLLLÿMMMÿSSSÿaaaÿzzzÿŸŸŸÿËËËÿðððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿôôôÿìììÿçççÿèèèÿíííÿöööÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿˆÿÿÿÿÿÿ¼ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂÿÿÿÿÿÿ*ÿÿÿ¼ÿÿÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿÁÿÿÿ/ÿÿÿÿÿÿÿÿÿÖÿÿÿùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿÿÿÙÿÿÿƒÿÿÿàÀ€€€Àð( @ ÿÿÿAÿÿÿÂÿÿÿúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿÿÿÅÿÿÿDÿÿÿ@ÿÿÿÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞÿÿÿEÿÿÿ¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûûÿåååÿÆÆÆÿ®®®ÿ¢¢¢ÿ£££ÿ±±±ÿËËËÿèèèÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÿÿÿöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóóóÿºººÿvvvÿJJKÿ679ÿ013ÿ/01ÿ/01ÿ023ÿ89;ÿNOPÿ~~~ÿÃÃÃÿ÷÷÷ÿÿÿÿÿøøøÿÉÉÉÿÃÃÃÿñññÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿÇÇÇÿhhhÿ567ÿ10/ÿF:*ÿcF!ÿyOÿ„SÿƒSÿwNÿ_D"ÿB8+ÿ0//ÿ89:ÿsssÿ¼¼¼ÿ}}}ÿ;;;ÿ777ÿmmmÿáááÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ÿŸŸŸÿ?@Aÿ10/ÿWA%ÿ“Zÿ½o ÿÒ‚ÿÛ’3ÿÞšBÿÞ›CÿÛ’5ÿуÿºo ÿŒWÿQ>&ÿ001ÿ667ÿ111ÿ222ÿ222ÿ///ÿlllÿîîîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷÷÷ÿ���ÿ456ÿ>6-ÿ‡UÿÉ}ÿä¬bÿòÕ¯ÿùìÛÿý÷ðÿþûøÿþûøÿýøñÿùíÝÿòÖ±ÿå¯hÿÆ~"ÿ}Qÿ:5/ÿ123ÿ333ÿ333ÿ222ÿ222ÿ˜˜˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿžžžÿ456ÿE9*ÿ¢dÿá¦YÿõàÄÿóÛ½ÿ鿈ÿîÌ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþû÷ÿ÷ãÉÿã¯kÿ˜cÿ?7-ÿ223ÿ333ÿ333ÿ000ÿGGGÿÛÛÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆÆÆÿ?@@ÿ>6+ÿ¤iÿìÂŒÿþû÷ÿüõìÿÙ‘3ÿÍpÿà¦]ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷èÕÿߢRÿîËŸÿìÆ•ÿ™j-ÿ95/ÿ223ÿ333ÿLLLÿDDDÿ›››ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóóóÿhhhÿ10.ÿŠ]"ÿìÇ•ÿÿþýÿÿÿÿÿÿÿÿÿä±oÿÎqÿá¨]ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöåÏÿÒ|ÿÐxÿá¦Yÿ滃ÿzX+ÿ112ÿ999ÿÁÁÁÿ¡¡¡ÿuuuÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿºººÿ568ÿW@"ÿÖ¤aÿÿýúÿÿÿÿÿÿÿÿÿÿÿÿÿóÛ¼ÿÐxÿá§\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöåÏÿÓ~ÿÏtÿÏtÿ׈!ÿºqÿF:,ÿ234ÿeeeÿ\\\ÿ“““ÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿvwwÿ10/ÿ“Zÿà Lÿþûøÿÿÿÿÿÿÿÿÿÿÿÿÿü÷ïÿÙ’5ÿà¦ZÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöåÏÿÓ~ÿÏtÿÏuÿÏtÿÐuÿ}Rÿ567ÿ```ÿ­­­ÿñññÿÿÿÿÿÿÿÿÿÿÿÿÿåååÿJKLÿF:*ÿ¼lÿÔ€ÿöãËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ纀ÿá§\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöåÏÿÓ~ÿÏtÿÏuÿÏuÿÐuÿ½p ÿ·¬�ÿôôõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇÿ689ÿbG#ÿÌtÿÏtÿçºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõáÈÿå²qÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöåÏÿÓ~ÿÏtÿÏuÿÏuÿÏuÿÒyÿÊ®‰ÿ¯±³ÿóóóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯ÿ013ÿyPÿÑvÿÏsÿÙ‘4ÿüõìÿÿÿÿÿÿÿÿÿÿÿÿÿýùóÿïÑ©ÿÿýûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöåÏÿÓ~ÿÏtÿÏuÿÏuÿÏuÿÐuÿqO$ÿ579ÿÇÇÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢¢¢ÿ/01ÿ…UÿÒvÿÏuÿÑyÿñÖ³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüöîÿÿþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöåÏÿÓ~ÿÏtÿÏuÿÏuÿÏuÿÐuÿsNÿ135ÿ¸¸¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£££ÿ./1ÿƒTÿÒvÿÏuÿÏsÿâªcÿÿþýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöæÐÿÓ~ÿÏtÿÏuÿÏuÿÏuÿÐuÿsNÿ135ÿ¹¹¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»»»ÿ;=>ÿ�b*ÿÑvÿÏuÿÏtÿÕ† ÿùìÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷çÓÿÓ€ÿÏtÿÏuÿÏuÿÏuÿÎtÿfH"ÿ579ÿÆÆÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöööÿÏÐÒÿß½�ÿÑvÿÏuÿÏuÿÏuÿìǘÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùîßÿÕ…ÿÏtÿÏuÿÏuÿÐuÿÅpÿP>(ÿBCDÿÜÜÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚʵÿÆu ÿÐuÿÏuÿÏsÿÝœHÿþúöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýùôÿÛ—@ÿÎrÿÏuÿÏuÿÑvÿ¬f ÿ94-ÿ__`ÿôôôÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÿÓÓÓÿ‡‡‡ÿJJJÿˆWÿÑuÿÏtÿÏtÿÒ~ÿõáÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÔ°ÿÖ‰$ÿÐxÿÐvÿÐuÿ|Rÿ//1ÿ”””ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËËËÿZZZÿfffÿ568ÿL=)ÿ¿q ÿÖ†ÿÏtÿÏsÿç¸{ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿúîàÿòØ·ÿðÏ£ÿÀ‡=ÿE8)ÿABCÿÕÕÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­­­ÿsssÿÔÔÔÿIIIÿ111ÿ~W$ÿå·{ÿâ©_ÿÐxÿ׌.ÿúïáÿþýúÿþüùÿþüùÿþüùÿþüùÿþüùÿþüùÿþüùÿþûøÿþúõÿÿýûÿÿÿÿÿíÖ·ÿsY8ÿ.//ÿ†††ÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÑÑÿHHHÿOOOÿ555ÿ223ÿ:5/ÿ™h(ÿëÄ�ÿïΤÿÝ�IÿÛ˜@ÿÜšDÿÜ™AÿÜ™BÿÜ™CÿÜ™CÿÜ™BÿÜ™AÿÜ™Cÿß¡Qÿ鿉ÿúïàÿîЩÿ�f3ÿ30-ÿSSTÿàààÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøøÿsssÿ...ÿ333ÿ333ÿ223ÿ?7-ÿ•aÿá­hÿ÷âÈÿóܾÿ較ÿß¡QÿÙ‘4ÿ׊'ÿ׊'ÿÙ‘4ÿß¡Qÿ較ÿóÛ½ÿ÷ãÉÿà°rÿŠ^%ÿ73,ÿAABÿÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÎÎÿCCCÿ111ÿ333ÿ333ÿ123ÿ94/ÿxOÿÂ}$ÿæ²nÿôÛ¹ÿúîÞÿûòçÿûòçÿûòçÿûóèÿúïàÿõݾÿæµvÿ½}+ÿmKÿ31.ÿAABÿ³³³ÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿ¤¤¤ÿ777ÿ000ÿ111ÿ444ÿ===ÿ001ÿJ;(ÿ‚Sÿ±lÿ̓$ÿÚ–>ÿß Nÿß NÿÙ–?ÿË„'ÿ®kÿ{PÿE9)ÿ//1ÿSTTÿÀÀÀÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÿ¥¥¥ÿPPPÿNNNÿ���ÿÍÍÍÿ|||ÿ=>?ÿ//0ÿ;5,ÿR?%ÿgGÿrLÿrKÿeFÿP=%ÿ93,ÿ//1ÿBBCÿ‡‡‡ÿáááÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöÿÿÿÿÿÿÿÿÿÿÿÿþþþÿèèèÿçççÿýýýÿÿÿÿÿúúúÿÎÎÎÿ���ÿZ[[ÿ@ABÿ568ÿ135ÿ135ÿ579ÿBCDÿ__`ÿ”””ÿÖÖÖÿüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÿÿÿ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñññÿÚÚÚÿÄÄÄÿ¹¹¹ÿ¹¹¹ÿÆÆÆÿÜÜÜÿôôôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁÿÿÿ=ÿÿÿØÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÿÿÿAÿÿÿ=ÿÿÿ¼ÿÿÿ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ÿÿÿ¿ÿÿÿ@À€€À(  ÿÿÿsÿÿÿìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüýýÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíÿÿÿvÿÿÿêÿÿÿÿÿÿÿÿÿÿÿÿõõöÿÂÃÄÿŽŽŽÿxurÿyvsÿ‘‘‘ÿÆÇÈÿ÷÷÷ÿçççÿäääÿÿÿÿÿÿÿÿìÿÿÿÿÿÿÿÿÿÿÿÿÚÚÛÿtroÿbK-ÿŒb+ÿ¦v7ÿ¥v7ÿ‰`*ÿaL0ÿljgÿSTTÿPPPÿÇÇÇÿÿÿÿÿÿÿÿÿÿÿÿÿÙÚÛÿa[RÿŽh8ÿÜ®rÿïÊ™ÿýóæÿþöëÿúéÔÿÞ¹‰ÿŠh=ÿ84.ÿ.//ÿ^^^ÿêêêÿÿÿÿÿõööÿtroÿ’sLÿøèÔÿïΤÿÜšCÿýùôÿÿÿÿÿÿÿÿÿëÃ�ÿÞ¢SÿŠj@ÿ;:9ÿlmmÿºººÿÿÿÿÿÂÃÄÿbJ,ÿãÄ›ÿÿÿÿÿøêØÿà¦Zÿýøòÿÿÿÿÿÿÿÿÿå²rÿÐuÿÄwÿ^K3ÿ…†‡ÿØØØÿÿÿÿÿ���ÿ†Rÿäª^ÿÿþýÿþüúÿìÉšÿý÷ðÿÿÿÿÿÿÿÿÿå³sÿÎsÿÏsÿ¹ˆHÿÖ××ÿÿÿÿÿüýýÿxvtÿ�\ ÿ׆ÿøêÙÿÿÿÿÿûñåÿþüøÿÿÿÿÿÿÿÿÿå³sÿÏsÿÒvÿ�aÿ‰ˆ‡ÿÿÿÿÿýýýÿ†„‚ÿ§gÿÑvÿëÄ“ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿå´uÿÏsÿÒvÿ•Zÿ‚��ÿÿÿÿÿÿÿÿÿäääÿÉ”PÿÎqÿÜ™DÿýùôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêÁŒÿÎrÿÑtÿ}Qÿ›�žÿÿÿÿÿéééÿ–—™ÿjU9ÿÆvÿÔÿôÞÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúðâÿè¸zÿËŒ:ÿ^L5ÿÏÐÑÿÿÿÿÿÓÓÓÿqqqÿ?><ÿ‹h<ÿߤVÿç·xÿîÍ¢ÿìȘÿìȘÿíÌ ÿõßÂÿõæÓÿ‡sZÿ‚�ÿúúûÿÿÿÿÿ÷÷÷ÿvvvÿ...ÿ83-ÿˆh>ÿجsÿ꽂ÿé¹zÿé¹zÿ꾄ÿÖ­wÿ‚fBÿkgcÿåååÿÿÿÿÿÿÿÿÿÿÿÿÿÝÝÝÿfffÿ``aÿsqoÿ`N5ÿ„a3ÿŸwBÿŸwBÿ‚a5ÿ^N8ÿ‚�ÿååæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéÿÿÿÿñññÿñññÿøùùÿÌÍÎÿ™™šÿ�}ÿ‚€~ÿ›œœÿÏÐÑÿúúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëÿÿÿpÿÿÿêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëÿÿÿs€€pdf2docx-0.5.13/docs/_static/custom.css000066400000000000000000000075141517510251600177250ustar00rootroot00000000000000/* main document page: ensures pages fit to the available width and height */ .wy-nav-content { min-width: 100%; min-height: 100vh; } /* Accessibility: Artifex color for main document links */ .wy-nav-content a { color: #007aff; } /* Artifex blue color for background elements */ .wy-side-nav-search, .wy-nav-top { background-color: #007aff; } /* Accessibility: ensures that the version number is readable against the background color */ .wy-side-nav-search>div.version { color:hsla(0,0%,100%,1); } .htmltag { padding: 2px 5px; background-color: #fbff68; border-radius: 4px; border: 1px solid #222; color:#000; } .discordLink { display:flex; justify-content:flex-end; margin:0; padding:0; font-size: 13px; } .discordLink img { width: 30px; height: 30px; margin-left: 8px; } .feedbackLink { display:flex; justify-content:flex-end; margin:0 0 10px; padding:0; font-size: 13px; } .intro-title { font-size: 22px; margin: 0 0 20px 0; } h1 { padding: 10px !important; background-color: #007aff !important; color: #fff !important; border-radius: 5px !important; margin-top: 20px !important; margin-left: -10px !important; } cite { font-weight: bold; font-style: normal; } .red-color { color: #cc0000; } .orange-color { color: #ff6600; } .green-color { color: #00cc00; } button.cta { -webkit-appearance: none; -moz-appearance: none; border:0; text-transform:uppercase; border-radius:5px; font-size:16px; font-weight:500; min-height:40px; line-height:40px; padding: 0 15px; color:#fff; cursor:pointer; } button.cta.orange { width:auto; background-image: linear-gradient(to right, #ea5842, #ec6343, #ed6d45, #ef7747, #f0804a) !important; } button.cta.orange:hover { background:#ea5842 !important; } button.cta a { color:#fff !important; } .footer-version { font-weight: bold; font-size: 12px; color: #999; } /*** Furo theme overrides ***/ /* This is to do with hiding the Furo link text and the "Made with" text */ .bottom-of-page .left-details { font-size:0; } .bottom-of-page .left-details a { display:none; } /* Now ensure that the other copyright text is visible here */ .bottom-of-page .left-details > * { font-size:12px; } .sidebar-brand-text { font-size: 13px; padding: 0; margin: 0; } .sidebar-logo { width: 60px; height: 60px; } .sidebar-container { margin: 0; padding: 0; } .sidebar-container .sidebar-search-container { display: none; } .sidebar-search-container.top { /*position:sticky; top:10px;*/ border-radius: 20px; border: solid #333 1px; background-color: #fff; } .sidebar-search-container.top .sidebar-search { border-top: 0 !important; border-bottom: 0 !important; } /* really important ! */ .sidebar-search { color: #000 !important; } .toc-drawer .toc-title { font-weight: bold; text-decoration: underline; } :target>h1:first-of-type, span:target~h1:first-of-type { background-color: #007aff !important; color: #fff !important; padding-top: 40px; /* accomodates header search blocking target */ margin-top: -40px; } :target>h2:first-of-type, :target>h3:first-of-type, :target>h4:first-of-type, :target>h5:first-of-type, :target>h6:first-of-type, span:target~h2:first-of-type, span:target~h3:first-of-type, span:target~h4:first-of-type, span:target~h5:first-of-type, span:target~h6:first-of-type { background-color: transparent !important; padding-top: 40px; /* accomodates header search blocking target */ margin-top: -40px; text-decoration: underline; } /* Dark mode colors */ @media (prefers-color-scheme: dark) { } /* small screens */ @media all and (max-width : 550px) { .discordLink img { display: none; } } pdf2docx-0.5.13/docs/api/000077500000000000000000000000001517510251600150155ustar00rootroot00000000000000pdf2docx-0.5.13/docs/api/modules.rst000066400000000000000000000000751517510251600172210ustar00rootroot00000000000000pdf2docx ======== .. toctree:: :maxdepth: 4 pdf2docx pdf2docx-0.5.13/docs/conf.py000066400000000000000000000113131517510251600155420ustar00rootroot00000000000000# Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys import datetime sys.path.insert(0, os.path.abspath("../pdf2docx/")) # -- Project information ----------------------------------------------------- project = 'pdf2docx' thisday = datetime.date.today() copyright = str(thisday.year) + ", Artifex" author = 'Artifex Software, Inc.' # 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 full version, including alpha/beta/rc tags. def get_version(fname): if os.path.exists(fname): with open(fname, 'r') as f: release = f.readline().strip() else: release = 'alpha' return release version = get_version('../version.txt') # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinxcontrib.apidoc', 'sphinx_copybutton', 'sphinx_markdown_builder' ] markdown_http_base = "https://pdf2docx.readthedocs.io/en/latest/markdown" apidoc_module_dir = '../pdf2docx' apidoc_output_dir = 'api' apidoc_excluded_paths = [] apidoc_separate_modules = True # Add any paths that contain templates here, relative to this directory. # templates_path = ['_templates'] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = [ ] # -- 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 = 'furo' # 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'] # A list of CSS files. The entry must be a filename string or a tuple containing # the filename string and the attributes dictionary. The filename must be # relative to the html_static_path, or a full URI html_css_files = ["custom.css"] html_favicon = "_static/PyMuPDF.ico" # -- Options for LaTeX output --------------------------------------------- latex_elements = { # "fontpkg": r"\usepackage[sfdefault]{ClearSans} \usepackage[T1]{fontenc}" } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [("index", "pdf2docx.tex", "pdf2docx Documentation", "Artifex", "manual")] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = "images/pymupdf-logo.png" # 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 = True # latex_use_xindy = True # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. latex_domain_indices = True # -- Options for PDF output -------------------------------------------------- # Grouping the document tree into PDF files. List of tuples # (source start file, target name, title, author). pdf_documents = [("index", "pdf2docx", "pdf2docx manual", "Artifex")] # A comma-separated list of custom stylesheets. Example: # pdf_stylesheets = ["sphinx", "bahnschrift", "a4"] # Create a compressed PDF pdf_compressed = True # A colon-separated list of folders to search for fonts. Example: # pdf_font_path=['/usr/share/fonts', '/usr/share/texmf-dist/fonts/'] # Language to be used for hyphenation support pdf_language = "en_US" # If false, no index is generated. pdf_use_index = True # If false, no modindex is generated. pdf_use_modindex = True # If false, no coverpage is generated. pdf_use_coverpage = True pdf_break_level = 2 pdf_verbosity = 0 pdf_invariant = True pdf2docx-0.5.13/docs/footer.rst000066400000000000000000000001421517510251600162710ustar00rootroot00000000000000---- .. rst-class:: footer-version This documentation covers all versions up to |version|. pdf2docx-0.5.13/docs/header.rst000066400000000000000000000020701517510251600162250ustar00rootroot00000000000000.. meta:: :author: Artifex :description: pdf2docx is a Python library to extract data from PDF with PyMuPDF, parse layout with rule, and generate docx file with python-docx :keywords: PDF Text Extraction, PDF Image Extraction, PDF Conversion, PDF Tables .. |history_begin| raw:: html
Show/hide history .. |history_end| raw:: html
.. raw:: html
pdf2docx-0.5.13/docs/images/000077500000000000000000000000001517510251600155115ustar00rootroot00000000000000pdf2docx-0.5.13/docs/images/discord-mark-blue.svg000066400000000000000000000017341517510251600215430ustar00rootroot00000000000000pdf2docx-0.5.13/docs/images/intro.png000066400000000000000000003366271517510251600173730ustar00rootroot00000000000000‰PNG  IHDRÒÑtÌk pHYsÄÄ•+ IDATxÚì�XG‡×ŠX±+ìJ· ‰5jìDÅ®±·Øò™¨±F5ÖĆJÔcï%ö.*Šh°‹¢ÒD@Øï78,wpîü½Ï>°efwvvïfÞ›ÙYa„ ?üðÃøñã§OŸ>kÖ,šÿöÛoiqêÔ©cÆŒ™1cƤI“F�M3ÿûßÿ¦M›FóS¦L¡04CqGŽI‹3gΤ´8nÜ8Z¤èkÔ¨Ql+Å¢ð#FŒ ­£åÐJúKG¤Xô—"R`Z9vìØ‰'Ò_†ŽHÉøæ›oh‡´’þÒJÚ'í|¦šaG¤0츴H‡£ˆtD I'É¡­”€É“'Ó&:"Û!­§•´‰ŽÎŽHqi+ÍSHJ£¿,Ì÷ßOG¤ä±d°SHZüî»ïXæÐJ:"¥�VÒXè)$­dgGaXX&ÓV ÃOŸfx®Òn)ä89üˆ4OɦÃÑV’ÄÎ…‡¡ÝÒJ–Õ|8Û'm¢”Ó"‹HÇ¥ð”!ìdÙ_ ?]ÍÐ~($…aIe79Û-K'Á..¥�¢³Û˜VR6C‡ã™Ckèˆ4O»bÙBkXªh=»ý† Æ>/ì>)‡_5vcS,:;kvF´žŸ;Ëg~c³ðø â3ˆÏ †?ƒ”v'Ó&˜Î…Â÷éÓÇÑÑñÂ… ûöí«í¾&ê!PÍéǤb˜•Êô—l“×3Xå‰ cV{ •´•þ²J +Å©˜gÅ6É4C�ÙX…ŒE¤0,$Å¥0¬"2tèP‘E¡EŠ2YíŠB²(<"m¥ªýe!©ö@yŠÅvË’Á¢³‹U¹ØNXE„Ì"²ó¥•,ýì/«hÒ<ÍÐ!X-™ÅeGdU7J%ž‚Ñ™RHV5aµ"¶[V÷¢ ©�«<ý,WÙ"…ç'E+io,sx Œem8p ËvVÇâçHÉ`{cÙÈj“ìòQJ( ýeçÈ"²y…ˆ”‘þÒ<«#²½±¼¥ÃÑvjì¸ìrS`VífY­ší��)‹HóC† aWŠe-²Ýò•,"Í 4ˆÕ×ÙYPn°³`Gd—Œ) »EÙ•â·»©h=O»yØ)³{•å ;"ÛS–'í�ÂÐNØAi‘>5ì:ÒJ–c쳈ìrð�«÷3¤v‚ì/-Rv±[ˆ]PvûÑé/>ƒø â3¨ùÏ ÁÒ@+Ù¯´©_¿~uëÖ…vÙÖN*Ñ»uëÖ Aƒ† Ö—cooïèèH‹T¸:88Цzõ걿´‰ÖÐ_’æé/mb‹…6Ñ_ Iùz¶ÈwÅæëÔ©Óàl·,<Í󈔶Ož~hÚD‹rh†Y0v",0ÛÊRH[ÙJ¶(Ý-Û'Íð#Ò鳕,©üôYòxx>ÃàûäYÁŽEûdkXóˆ<ëØ&ž6¾µvíÚÒˆìrÔÿÛ-Ï:éYR¥»¥3’^v‰yxžHž«Ê—˜_¾•íœE¤«Éó±«ÀSËÒ)]Én~ÊüöãçÈr•Gd—˜æÙ)søV¶…»‘�œœxRù¡¥çHkØçGä÷??_~ó;�‘íD!"»Ø>Ùx>³³àˆÝfìƒÆv…Ï >ƒø êòg�Î×ÎÎŽí_ª�ç ’Tí3f ±"”HIIINNŽŠŠº{÷®¹¹¹Bk'•¦È" 3Ò´sâĉÐN@í¬]»6´ȶvŽ;¶AƒÈ@µvš™™Õ«WÚ d[;‡ B…(rP­�æææuêÔ�vÙÖÎ>}ú888 GÕÚY£F ;;;h'�míôööFk'�¥vš››[YYA;€lk§»»;ží²ÔNKKKh'�míôôôtttDŽªµÓÂÂÂÚÚÚ d[;½¼¼œœœ�#€jí4—í²­�Ý»w···GŽ™igddd```�5,--¡�@¶µÓÝÝC *´“·v’yB;€lk§‡‡GÆ ‘#€jí433C'[ 'Ú‰‘lu´“œ�l€œhg·nÝðl'í �€–µ�l€œhgÏž=ÑÉÈR;---1¤�íìÖ­›££#rDc  ?j§……´%#€lk§»»ûg¥�T¶=yòD‹ äd¸‰ ]áÇ�Çm º¦�ŸskgÞ *JFD;?«ÖÎE‹QÁF5m%€M X³f�ò&ÚÔ±cÇ={ö°8,, w*èŽv~Î/PÉ»TEÉ @içgòl'YœÖO�>}:% 88Xa=SM2O^º“B;@×´“ø µ3O ÐÌJFJ;=<<>íTP;­ÀÚ3V>yò„ÖS¹Ë×ËÁm :¥�æææŸ§væišaÉ  i§§§çgò*Õ¨lÓâƒ�¬­Âú¡C‡j7aÔÔÎÏó½�yW€fV2(hÚ™»/P¡‚™Š�œ•LqÍš5ÒV>Ú�¿¿®ŒnG»¥"“/³t¬¼k`ä�kRºÎ²a„-Z„›t\;-åä‘v~n¨Š’QÍSfÑù¡�?N»Âˆ¸ £ÚÙ¹sç\Rˆ—ª 'V0ð�Òò•5úñòƒ—(¼ï •©´(-Q¨ìŸ>}: ¿ª‹j6`�òXT̳a8* ?õ§PZ«È4u@~ÑN33³¼x�ÊçP€f«dÌÒc¥Çåc±Ìá‹üyT-Ž# M;»víZ¯^½ÿ²/^ Ò B*BRÀ:êP \J‡+`E& ÌZÙ� å“ô�#ªß;¶*$�E§=³õ†Q¯Îá¤5 J?EdéWèGÄžêägD¥,ÀS� kÚhnnž»/Pù| PõKF¶R…~3Ç&¦â’²‚烴U–;'ç]ÑÎ.]ºü—N¶ìÛ_�ŸyAÈŠ%**¤=KYiÁ†Õ¡B‚vËÊš¡‚�Ê$ ÌÖ+Elàu @[ùÏŸ e';´Âàìl% �éŸ:­ŽêŽÁ¶²â�´†b±j�èp º¦�¬µÓÊÊ*[;?·4Ë’‘àž¬<ˆ ÀÖGÉÉÐ�ᜠsÚÙ³gO''§œí…ýL«ÎÐv¬‹ÉdåØÏŸì'ÏErXÉJe$+2Ù/ÊVÆ 'iÑÂ~– «üÃ0¢’wmRs<uÇ+üˆ¼•–‹Êt²€ÏJ;?ÃTuÉÈO„­ag­`³Y¨ËÌÅKÈ@ç´³W¯^9ÖN^ä¨þ±–¼Xâ?pJË?þˆ#¦"‡•¬ü 5ìÇWþ»,�Å~ÑTîŠÃŠR^V) ÎÎûÞ°õêwjUópl‘—¬”þŽrJJ– ŒùE;sñ*Ÿ[šeÉÈÚl¥ÇRðÒ,G6%Ï|Â9@ç´³sçÎ 6ÌñŽY³f�B�Vdò’†÷ä¡¢…—µ¬ðck¨DaëY!ißž GDÈðwVé/¸,ŒTù¤�|¤dù¶Lu§0ô³�ÎEaçÊ  ËÚIΙ‹#Ù~>¨:%£ò;ZøÈC\€|R¹“-K¼êahG;ÝÝÝëׯÿß÷HECús¦´`E&r¬8䥙€àÅÛ›t„=V&IËåB¼pUØ/Ø2û¹”Ò�eg×,Çv΋yÚˆ6Ñ_åVÍÌ oº©�䜹ÕÚùù ê”Œv9–J¯B«¯˜Ñ;cx¿\ ‘º¨�^^^ööö¹µ_iÉ¡PŠðWB³.:Ê�ŽHWòn<¼Äe=‹¤±:±ý+ôYâ?+Ká÷]>ô‚ŠSSçpÒ¡üبƒTøQÊiFa<1ýÀƒt_;ÍÍÍóî½�µU§dT¶b~¬IS¡Ädí¥ �·¬Ë.ÊVÐQíôôôÌ•ÖN•%ì9~VÈñâJ:"‚Â/¸¼¼‘þ.Ëz‘–vl �•á°êtde¥G—ŽÈÇK;Š(ýáV�ÃI‡ûc%«t\…§w¤ÈÚieeemm�GÚY PuJFåQáõ'Ò1“ÄO£%)wÇeGá/e¡Cã…d CÚÙ½{÷\ií¤"Y:`ëAJe)YfÏoð¤? *¼ùš¯áÝ�ø#ê¼QZ¹„f+ùˆ ?Ó²XÊÃÓ«>+DY7'i ªü¼Š˜ùÛ·è¬vZXXä…vàT�’‘Ÿ&kØä-–<Áü¥)Ì„ùËB¥ŽÊ[nyÂðÃ.íT~Ó´t¼rVÈña¤…«ÂK¥•—åO°dV¨ˆ™Á—¡ JíN!Ù´åñîøPxÙ:/ꤧL;gU…À¬ÄÅP{��´ÓÜÜ«Tý’1g¯°V÷€Žjg÷îÝóÝ (tÊ_‡SxL@~ÑμR¨   �©#Ùº¸¸ä¯Ôk¸µ0×G©ü~6º¯�æææùZ;ÑÝ€v´³gÏžNNNù+õÊÃë¤ÃÐMí$çÌ»‘lQ¢(ÈÚÙ£G� ä¯Ô?~\c­…>ÝÔN ‹üÞÚ‰ €v´ÓÝÝ=ßi' I파Œ 4—“ߟí´ �¹òÞN  jg�éd hG;{õê…ÖN Kí´°°€v9ÑΞ={:;;#GÕÚiffír¢�ùqH!@óÚY†´£�]»vų�@–ÚimmMæ í²­�ùî½�€æµÓÊÊ ­�@N´ÓÓÓÚ d©�x� �CíìÖ­›ƒƒrP­�fffèd äD;ÝÝÝ1¤�¥v¢“-�CíôððpttDŽªµ“œ“ÌÚ d[;===6lˆTk'9'ÞÛ äD;ñl' ŽvZXX@;€œh'ÞÛ ¨£�æææ–––ÐN ÛÚÙ£G )d©�ŸÉ�BÛ·oÇu|ýæ²vvíÚïíThgddd`` iççÐÚ í|ýæ¾vzxx¸¸¸à2™i'Z;àë÷?ig·nÝœ��q™ÕÚù™<Û í|ýæ‰vâ*@–Úi!Ú _¿ÙÖN///Œd d©�æææÐNøú͉vúøø8::â2Yj§••´¾~³­�èd d©�Ör �ðõ›míìÞ½;†²ÔN333h'|ýæD;ÝÝÝñl'�¥vâ*ðõ›CíìÙ³§““.3 Z;mllðøú͉vzyy¹¸¸à2ªµÓÚÚ#ÙÀ×/´È+í$çDk'|ýæD;ÑÉPG;ÍÍÍ1¤|ýæD;{÷î�¨Yj§•••™™´¾~³­�>>>ŽŽŽ¸Ì€jí477'ó„vÀ×o¶µ­�€j파Œ ´’퀯ßlkg¯^½ðl' B;Yk§¥¥e¾{¶s{ŽÀu|cç²vº»»;88à2Yjçç0’-�ûÚéááÑ Aä Z;mll �@N´ÓÝÝ�l€,µÓÚÚšÌÚ d[;½¼¼œ��‘#€jí´²²²°°€v�w‚€L `j§»»»‹‹ rP­�æææùnH! ��ÐÎÞ½{cH!­ñøêˆ•~�æù ¾QÎçÉÏ®úŽ‹BpaV;É9ñl'ÐN@N´³OŸ>ÐÎô„/ýumÃÙJÓú[a¹Y‘ [¸4uÏn{Ÿ„l{0·žPkV�ÂÚG¿¸ Œ"†eÌ;�ßt'6oøc}ý"ö?ÞÃ- ÈíüLÞÛ©ã¹û°åÏ›Ì^;qç±#ZgΜÁ•…vPÀµÓÓÓÓÞÞ9"áí/¹¤�Q77šó{ï³5fFßîCûüùô…øÄ¨ø$ žÝóY‹~sZröІ´ó¨ʂezl Ï‹3»2©:íÝbÒ ÍÞ.§ÇT/ZÔØw_2>9|Ú‰!…Tà¾ö/²A…‰VæîQ˜sæÅžuJ;Û¶mËËÍÒ¥K6ìíÛ·yqô€€vh' oµ³W¯^ÐÎŒ‰,ΣGsºƒ7Wö’¯v<þ:ƒm¯®t¢�o¸ýFÓgõhòœµ çžøG£Ú)[ŸÿöÖ¯LhÉÀsOJÁÑ΃¾%é°Ýü“ð‰ Àk'9§��´3¯µsÞáóñ{3.©ØnŸ„G]yªÉ³k¼`WcÚ)(A"šG§C³ýkúnqwÏìÄñQ `j§‡‡‡««+rDMíLŒz¶fçî.?¯wž»©£ßÙ}a 2á:½Óqöomv?z'Š_]÷œ·¶ÑÁWOí�4–®›œfZÉÁ']$›þ÷@¶ã�ÀK£Voi2÷÷&?oë½íâ¡7‰ò°¡?.þÍÙÿÚ_ûw}9w½ï¥h1ìZ·9¿ùÞ°ëï¶ów]¼côÙwï]µÊÏuÞ¦N~çNDÉ›Ý>„mþ{_�_6ºüø{Ó%Ûúíºu3^ã‚IÛoýî¤o‡�½¹iì×õ«×/VºJý®sN¥¶IX²æèMk7­a\¬d•úÝ\ŒJ��ðpר6µ+—4®T÷ëéK‡ÕR©�Iq¡—´6¦¥’¾”¥‰w6 jÝ z#=½âåk8t›yèej*ÖhnUΰ𧂶h‡ ”±)o/ÿ:°Eíj&zÆU<�~•¢†vFœ^àÞ´f%ƒ¢%+Ú¸õ[q%VÃV·Ñ+T¤êðc,‹�¯X¸p™h‡šåîT½”�ža9³º-‡¬¼øV~˜‡‹-TÔí—'ò/–5×+TÔeÉø�_‘T zŒ� Y;---mmm¡�YÂÌ0ÇÑI83ÓÎ#wþÇ�kë¤r¦�l½¿¿Þ™¡¶´“5çB;ø¼´Ïvª«�I¯(ô¿ýùÔyÏø'S—Тÿ‚'Qø­k8gÇÊWÉ÷ÿI§�3îK´óþ©Fi›ÖÏüW 8Ôt¶âžÏ~ °OgÌO[Ùõä[1ôR»Ùõþ45ù3$Nï|Ëôë; #í<'m�Ãæ»‘éÎ׿�ãÀ5ÿ„D¾� ù{ …PcòUùú½>ú‚`hÓsÑ_'�ûOt5l¦^—×ÅîýP¯�Qý�«÷Ÿ<ºõ»vÕŠ ju²Õ·ÿþåYЬzé×÷ÛO˜rvL5…E;o~ŸòpIÓbéÖ}±úiÖÚÉš"%Ø}(ŠÏ–6"©-Ñó/YN%îïWZ ºl‰ßé_EáàÅÛ¬•†¥Ö~îù^͵g.ç×IOØyÉ|l(ÈÚ‰!…rlhW‡ö߸—­ÿú×m^—•¬U“5‡R€Æ 6Ð&æœ|b!4/ÝDÑiåúó7)–žÅOM¯ËO^¡ÝÒ¡Y܉;�±dÐJ:Åe]v)" -ÅzEXÃ&M4Okø©h¿å%Á°aä:Ç¥NÁ¦øJ¦”ªµsþüù|¼ÿméÒ¥§N�Ê¿}û–­Ü:ÂÚe?Ž::*(CíÌð(,ôW‘�/;:…äáY—`eÕªU, Ýy†9ƒOS;{ôèA_CÈu´óã½N³×6ÚxýNlRÂûˆ d˜³7Ì’•Sâ››‡ÉÚä6{mûƒ/âåáUu²}y¥cZ'ÛÈU«é@�y–�ôîíÃÿÉäö·Éw“?i§ÿ´ÛáщññÉ©Ú9ÿ¯…A‘‘±¡óVÈ²Åæë—¢?¼º}ìKÚ´ü’lL�ø°ý7žÞ‹úðácÒëÛÇšÓú­÷ÞË•I½N¶'—:ùŧjg¡kSŸ(I9è[Jh·Næk·¿³jLºòI§gÚ©ÔÎÂúÆkµþëÙ0Ö°sõ®‹!oß'~x{x¨9p˜÷/áBg2Óf n†Ç†þåUIªŒ¾H¡¯O‘…°|àq̇Ø'xÉì°ÍÚÈ,µ3ìô¦ ‡o>‹ú�÷tÍ×%h{÷?’D1jSGC:Ì×#Ä„}LHA{íŠ#“•‚EëŽ8ðoôûwOŽëL¾-4^ò2SíL3[t²àsÐN[[[<Û™3íäd²‰¼Žu‘eëIÿFøb’™-í¤ˆÊÝzÙž¥=~i^!®òD†©|>p‘jíä¦DŽÇL’[™¥¥%ÛÔV›§•|^E£ŸR÷“²ÿ~Z¯àœ\;éXÒ•$~”È,µ3ã°³ãO�²#²“¥š¤Bx¦£ ¦Ê]Ú Àç¨�ÞÞÞèd«¦v†^Ü­Ôº¸ivˆÜ¢>>™º€­ÙúÓóTS[;ŸN›G÷í|Ÿºñò?Šè{9î“v>ĬaÚùé¡Ðã»~§�“ïÊbïøÊšIÏß”¥'úè‰cýVlùbþïö,©›ï¾ËB;co¬ÝÑÑ´¤~!y1Ð~}Lªvîµ+Mî,„fËÞÈ}ËHhõ{ô§ Y=Û©@ò‹Ã³û|Y»jYcýÔî´õd�� 2•E­×„Qµo§GaÅò× ío¯²ÔÎø;[ÇwsµªXÚ¨hj4æÒñú—„"Í—?ÞëCÖXºï¾Q¼ö­¨9ãVjäû?Ö¥eó‰×?igƒ9ÁÐN>síÄ{;s �LÉÖXË!kodm•LóXë"?Qe'[®…l‘µs²�´s¦šl‘Í3i¤Mü@4C‹LwYc&kt¥]‰òF×å'¯°tJcEµ;Ù’æ1Ëb:Ç�V2eò&õÌ,µ“ï“í–y¦øéñH¶‰‰;…grH!™²¶Gž·=ªÐNÕGa›JËáG¡ƒ²£³E¶ÃÌ´SD'[>CíôòòjÔ¨rDíL¼sÔaöÚF›®ßz—˜”îÉ”�³»œÈ0Wn£¿�7ße>ôöÊ>ÙûQv=ŒKúð.Q…v¾Y²œ´qøÅ×á‰ß…?œ![\÷�¬_nÚyBª�qw|ÒÎÇgÿ¢õîGC_ÇÇ?ºy¬Ešv>–içì}¼ûøî}‚ô$RþùÆ´P­{î†ÅÑÉ�nš•vÊ4Ïrê'ÉK¹5Ã&Ú¾¦uA¨îû×£ˆÈ‡{[Òθ3ãjÖ/_µ‚¡�qÅÚí&ìy,?ó‹ãeí›¶Cöÿù!³!c3ÐΛShÇ…�f\ �z}÷ׯKqí“/�©AKuÝš’4–vB&�¡KÝd�€&œxûìø$GYÚ–«"©Êò“+Í–h³ìAL\Xà–ÞÕÓNêÐÙ0I.óÿýý ¦vFFFRÅ/PÉ�v*4`J»ÈŠ’†Ðõò_M³¥�Hª�¼±”Eä)Ý*Ý' çÿCÒ¶Yu´“?{ÉÈP;) ÓN…õ¢ÏvJ  d½U¥‡Ë°‡­r£¥šÚ™ÙQØ™Ò&æ–6iòÄC;é´³gÏžÎÎÎÈu´S|ÿøÛ%é[;ç=Fuwà<ò½.¼µ@fŒ›&Þ‰çšÊBN¸“œ¹v&ŸþË1};ªãê+÷d“Cí :±M±a6U;_Îå§°.Ý+awõ6(â:; â}dÈñ_º›ÉJ;?^]C0i<ÎïÈéc[fv°4,’ í|±¸‘bA(×Ά–£¹î+O¸öm­"éƒ;,z”‘v¦Q¬ï¾ó#+)%U;E1xNÃÔUUG]`þý|]ë Á+ù”ýæ�tlH9Å=¥žÔÕɼÿRíÙxg(T;Yk§……Z;s �ËO^‘¶vJa›X£%koäöHúÇænb“Ž�ªb`äº�0ÍNOßuÑ#\ÁωW:™è—öØ©¦ü3ά˜Qƒ¹�È9 í´¶¶F'[�ÕE‚*â|%ù[©ÂÇX �´“6Q�?³d[ZZ*æk˜Of÷¿h'ôC÷%#³ë˜wÚ©p{ãºík§———³³3rDøp``¹Â‚PĤþäSñ|mÔ�AÅè›BÏiþƒL£^�l¥gàöË3¶´£…o³62Û)¸1ÕBl¦ÞÈhÛ›•-Š %=÷Ä+iú% …�šü’öUöÞ¿‡aQãþSÃV¶2(ZmL†µ³¤#Ã+êoòS°(^UYj}¤^bÏ�7+jðåªPÝ¿°B6'Ýâä7Y‘YHϨœ…S×qo¾ËÍý‡þÞÞX¯Lï?£2 0ÓNO¿ÑOÿ281ªª¾‘Â;øÂZÐN;;;Œd«³õ{K9Ò•´¨ºº¯í$+ÈÌ Xš"ª¯�œÏ@;ÓŠÈi—;Wô[(¬^Í'Ý,:¥¿ ðk¤ðC^£p{ }íôöönܸ1rD ±ýÚµ­Ã.Û>5U¾ZÕ²ˆA½zÖ‚PyÔ…Ì¿[†› B½YòN;_,k\H(ë{0QÉ:—º.laQ]êϾ÷ieÌÚ6”„n;RŸ-t„’¾G³8|ö´óèà2‚`¿ðQþÓΤäÂ?^ÿªÝ�oÚìŦ;•tV;wô*,Ï甤˜çV¸› ~HÐ` .Œ“ß·ñ´®�¶¶¶híÔÙú=s?nS| ×6¶È%Í¡ Z”îGÚvÊœ�!5æo¬fŸ™v²0™é¨‚HÓ3yòdé"…g ãm\Üu¹v2‰å‡“ê%7jžfž3Ò|ȬÁVwŠQòÌÕwÝtÿ[íäWœßTübñ«Ïo<¾†Eáwßû+ýEƒ­áû¤ �ÙíÍîéVþa·�òý@®i§¯¯/´S§´ÓgÆÄê‚~ÇMLŸ.r)dÐyöT{®�C�Íêj_£”Q©ê ºÌ>–"ÞüÖŠ{éæÿži§ë°½+—4©ÖxÈö'ÉLo¯Úܼ´�Aɪµ[�𠌕¯}kµ�SõR%+9õÙ¹r&Ú²ÀILGœRêaû|i£B‚Ë¢-ßT„Ú3åÆ˜´­‡O�Ťã«[â‹ösï‹gÆT,b;tÁXÇ2úeÏ�¯ZÔ •¬ÕR®�='ôu0-¦WÒ²ÃOWdò�°ÓÓ P±Þ;åÂ{aBµÂE¿üõÅ¥ f|Ÿú½v$‰â‡‡»Æ·«S¥dñ².ÞK¯Dë°vnvmº{Âþ'uN¼°aÓ»�×N¶”trxA0è¸)B¶ðüðŒÎ «›/]£a�¹§ßÊmìfQƨxÕæ“½–¯yz`j‡:•� ËÖó\u;^|±ü‹¢�¿[üuuCý¿¾ˆÙÚݰ¨ñÀ¢¹¡­ž~×…NjcebP¬‚ã°]Ï’Å;ßÛñ+]¸½_Œxq²eQƒ¦ËŸÊtàõ¹ÅžU�õ ˘5ìÇw È-í´¶¶¶°°€vêlýž×•¥RÇjÕ| «ñ³z|†­�lQª‹|'ÒvEëËÐÙT´C²*>;�TeU´v²#ò­&@zFj§Âz—eZ=ƒš»ÅhE¿…;Bìó£vJ¯&¿èü¦U¸ÜùÍæ/Gú³‚‚v²yi³jf�ùÒÛRú©á?»¨ø¹€ÿ¤�x¶Sç´sïÅÑU…¢­×È*ñÁsêú]¶ÜœËµóÑ/�ô‹�{^D>ü­sY¡d×í²ú­�úµ¿9ô"âÊÔ‚Pªÿ�QŒÞéQN0l4ûâ눇;¼ª ‚ÙÈs bÊ­¶´oOÿ'//Lu”õæÍH;ï_Gª�¾˜¢¸áÉO®ôý6- i··1EýÔ"¥²µóèà’´±x³ŸïF„GƳEg™ÖȵS0j:çjØÃ�]Ê’‚N¼ž"Æûu¢µ�üä½{O/“8}kçÇ€oí ©?ñÜëˆ[󛕆�ˆ×Yí\s·éÏ·Zä—N¶éµS (.»˜EñßùÎz‚Õ�á‘÷Wu()”òü+JŒÞøU¡Rß¿ßDùõõ]ýRLùw±‹žPÙcË£ˆçG¦¶{H|$»„BÖC�‡…G¼K‰”ß+½vÈ>Íh}aó~»¿:=¾V!Á¨ËÙÏé[;OÊïõ¹t¯?ùÕÍ@0í¼æ^ÄÛ€_ÚšEœ‹büúö´¿b“N¼ ?7šnnÓ¡ÿ¤àÛäŽv’s¢“­ÎÖïYSw9"ʾ4¼ŠN¶|^¡—,¯ŽKëå™u²UÑùVY_ÕÔNžåÖN¶žKµ:Ú)m"VÑXwŠÑùmK¬[ê{ʇOùN;¥†ÉÔPî…«|ó(´—*h'¿OøõÍìöVØ3¿a2¼ÿñõrS;½¼¼�#º¤�)§GV 7[ùJ¼>ÅFÐï¸õÝ#®�ÁsH#ͧ\—…Oú£» öÝ—¢ª“íé‘åÁq~ˆ·©� î¸‰ÙØÅñ5¡ÒØóâ½Ùõ¡üÈÓrw;5Â4Cí¼9ÝZ¬¾½¦”äÇó)¶¬Z½ñë"iq³ÖÎj£Î$KÓ´³ê¨ ² É»½ÉnêÌ VO;Ï�©(�ËšÀÄWË›B�ñuV;㓊¶Ù?jó}ç}Oê°)òƒa¾ÑΫ“éÖzlcí�5gÜ’­ü°¹ƒ ˜ 8$†¯mQH(V×÷÷3OãSï UqôÙ´ʵ³hûõálQA;‹´_/tô$�Þû3×Îû?6„2ÃNÈ�2éowApZð4U;;ù'ÉVèo,M ÷ ø¯ÚHÎIõ3h§Îj'3i «Žóþ‡u´“(ÔѹmJ%ÍL/7Uð^¹Ÿ­jíäGQ¡�R1V­� ÃÞè¾vVô[Øt÷Äü«�´R¡û+¿Ä¼w«²*{æÑN…=«ÐN]ít ò­v8­�:¦�b²\ÿš,Þ6²ª`ÐÉï�˜¦�dž”MÿeU²ÿ¾DUÚÉ«ì÷f×„ÒƒŽ° e�f‹zïf•øZ³Øó”™<Ûyq¼ùÅʃ¸ü;�œÕÈCÞÿõÅ/®‚¼}R=ít棠�Ÿží1¨¼ ˜˜ ú�7G‹í¼%k²œ xAeunÕÚ¾ú R�.[>¤J]urDÌëS¬ykgÒ‰a啵3ùÌpÚyÕ ¥`YK©Õ&\JÅ÷ëÚIµ3tq#µµ³ÚÙĤ¿< iõ’gbòŸî…¡Ý:YX‚¼ ”Å•'ö“QœVšDýgÝ|ŠO±t\ÐzI~íd›tjDUA(îþg,Ý:æ²çy3x“IrTÐÎ ŠÊnˆ€i¶ìç5µ³H‡ òá´nÉnL“ÇhöòÄji§¼µÜ7ÿÈ×&þÕÓ@š-{ íy©�ÖÖÖ5kÖ„vê¬vŠŸÚú¤�bJ›’ªÑ –aµ[º“l=Û©¢›¢ÂŸ,$ó…RõµSùÙN€7¯)+¨ 7ÖÍbÔ÷”O»#ó�v*ô¡UngCþHeRáYeþlgfÚ)uZåÛṲo¹ò³�ÐN�‡ÚÙ·o_¼@Eç´Süxh€¼USÿkùÃmiÚ™tmšm!Á¼Ï¶ï’$QŸÈ/ËöÙ—‰vŠÏ–53J4_xíMă-½ªE~ N“NËŒµšÏÎç¯Îë�Á³�‰GSR/y¢”Þ{3k B)>¸í›µmŠÈ´ñiã‘A¤� fß‹‰~—@ûØÞS_(ÔlùówÑ1IYh§P¢Ù¼ëaÁ«Ú™…�<¤òΜ½þ|ù꟱ueƒ±¸ò>–¦ÉÏ9îè€JB‘º£�=‹KÖyí ¯Ôéа„�Eò•v¦Ä¿ 9½ÊËJ_(ÞháÝ�tU/�·¢R´ÿß!1i#M-ìÑwÝ�71�V´-&öÞõ!éÒ²Óê>;žD†þ3¯ý˜=Yh§PÈbð¾§/¡Xåû�µq¿XÚˆn4Ï?#åw7×Î�×§ÚÐÇ¢×Æo¯-li,”è¸)L„v‚¼ÔN[[[;;;h§.k'çSTj-”v(UxÎS¹ó¡´Ú-í)ÕEÞqWÚiVº5ÃwŸ(?B)Õ>l À;^*w÷UÐN…QO¹µJS(FHùˆR5ÕÙb´áÎé “Žkg†wŽ˜~Ôbé(>Žm+KEk§ÂPÆ*noéEç?:@;Ažk§§§'F²Õ"7|­W¸ÌðãrÙ;6¼b¡dÏ?äϹ½øÅ­haë)r|ôǘֵª˜è³Î¤í6ÉÇ£ ;0ƹ¢QQý2>»Þ‹{û+¤ßu‹¼Ñèút«ÂEçß§Ù¤g‡gtiP½Œ‘Q9 Çîß{Ád!êìì¯ÌŒõŠU°°~•o…ÂõfI{Ó&ìéSJ(Ü|Õ+¥ä>ûÙ¥PáJC�¤½R%bó׆‚`7�÷ãÝ5]lKé5lðƒ,ÕñWjcQR¯h‰æ¿ü+ž]±PÑ/~}‘+mñÚ$‹ÂfžßÙ¶T±’Õ\úo fM³á'¦µ¶(]¼ŒÕ£¶.è¬_¤ù2y«æË½Ã+Õ/ﻇ#êúšÍl*•ç‹aÿº«�ûŸÔ)»á'ÓM Í·ÎfÓµ7ÕtV;OŒ¨�:ެ^ S+ד·Ü‰MÝ{gˈV5+›°�‹ wÝ/&?Þ>¼iõ†ålZ�ßýLÖpscU?³2††¼WÝz/>_þE‘B•Æ|*ËÞmî¢WȰ߾TíÔkî;ªIeC#ÓZ8“úøgøáñ�+Õ+í¾#FÑ?ýxBÐNh'Ð]í´³³Ck'Z,FÉ6›îž¸ú®M4#yÎÚ í:¯�}ûömÔ¨r�içÕ×ÕÍ·ÎÞúÀéìKËwšYl��P Ú)Fml§W¸Ü�£¸c€.k§••´m£Ò‘lD›J:¯�>>>ÐN4©� ZÿÔ„/Ž=ßcÿ“:ÐNt_;É9­­­¡�h«-±néÉ6lžfhE'ùF;ÑÉ kçÚ &3®t䋞Ç{ní@÷µÓÒÒ�lÐb1:írgRÍ^GÑTÑo¡ï)�äíôññÁ�BhR;£ŠÕÜö¿.‡‡Ž9ß³Õ¾Ñõÿœžþe*ÕN[[[´v ÝbtGˆ=Ù&M4ƒ¢€ü¤�¾¾¾èd €&µ“¦˜Dƒ-œæßh³õ�S\’† _h§••™'´m£Ó.w–.Îh‹¢€|£�}úôqttDŽ 1í<ÿÊâȳš|ñ÷ Æ�ß•�v ûÚicccggí@[Å(† k§§§§««+rÍhgÄ£e·¿øþj‡WqÆ4=�-Õõð )@¾ÐNkkkh'Ú*FîœNžÉ^ÚIS‰uKé/ŠNò�vzyy9;;#GÐŒvv:4¬Ì†ŸLÖ/©¾åGšjl™Óáàˆw ÐNt_;mä@;ÐJ1ºú®i'{i'M¢MQt�Ÿ´ÓÓÓC  1í¤éàÓÚþ3|æe'º©�‘‘‘���vvvÐN´XŒâÙNò±vúøø4iÒ9€Æ´Ïv�ï´“µvÖ¬YC  ÅbÏv��µsðàÁnnnÈ4£�x¶€ü«�äœVVVÐN´RŒâÙNò·vzxxà*hL;ñl'ùW;1¤Z,Fñl'ù[;{÷îíàà€@3Ú‰g;È¿ÚiccCæ í@‹Å(ÙfvµsÚåËÒÅùÈS´ �}ûöuqqAŽ 1íÌjè¨vZYYA;Ðb1:írgÞ϶ĺ¥j)$4•ÑÑÒE€æ´³ÿþx� ÖÎ=�ë}u`¤ÃÎ)Þ'úEVÈR;cß¼É/……í}ü9 €†µ�lÐn1Jª¹#Äž÷¹­è·0Kíl¸s§ÜTw²©Äºuôy €´ÓÛÛÛÑÑ9€Æ´óêëêæ[go}àtö¥åŠ;Í,¶ÎŽN(¦B;ã’’ÃÛïÙó*.ަ—qq¿Ü¾=òìYä,ÖN[[Ûš5kB;ÐV1*É6ýb¦¬¾{WþDè]6=ˆŽF† íÄ�BhX;´þ-¨ _{¾‡ê‘lß¼Yeófƒß~«¾e ›vî¼öæ r k§••Z;Ðb1Úpçô^G=ˆ6¥É÷”F² ?igÿþý�œœ�#hL;×5™q¥#_ô<6àØs;Õ�l£F�;‡¬@»Ú‰!…Ðn1J¶Ùt÷Daõjšh†w¸UGHH»x?[t²@;ÚéååÕ¸qcäÓΨ„b5·ý¯Ëá¡cÎ÷lµotý?§'|,’å�B¡qqkƒ‚–ܺŦ ¯^!gu�°uíÛ,}¡VÐwþÝÕ tT;mmmÑÚ €Ö‹ÑìŽÆWÑÏÏ÷Ô)ÞÏ–&ä©Öñ÷÷Ÿ?¾š�---‘cA;û÷ïß´iÓÏ>C’_�œÓ¦JÙÁG´™ˆÐ_¿($B!=“jÎ}—^O)(y{êÇîö5J•©ÕùÇs‘(/eSL¢Á–Nón´¡¿qIzê )TÙÏÏûĉI/N–OGŸ=Óôy¼XÖ¤ù²°]èâ¸ð‘(Æïê]küÅ,¿÷ïá¶è  ü­�híTàÌ™3TpÑ_-¦¡mÛ¶Â'¨.[`ò–*å줆 íd½j§]î¼#ÄÞÒ¶°zuE¿…j¶v–X·N‹© Ñ€5åÖQh?ì®ÓÀÇ6DŽ:Ç¢�€v¿d@îhgß¾}ÑÚ)ž›ÞªÏšY�ÊøÔf*-t¶Ÿû@üøþåÕ�}k–m¿Nãí"G™åºy?_ýe9‡ {ƒ#Þ½º´ÚsÔ–˜íåÎŒÚí7Åí<óÒrÚåN'_Øìy\¯ì†ŸJ¬ûeå�fYjç­ððއi%õ'†™ù�Ï=[âÜxÉËËä¦z0·K·Ó‚§©!ž.lÞbÐ(·*Å�«4s˜ãמ>EBÑ5šÍ:+Šqæµµ,YT­Ìð“brȶ®U�K”«Ýíçïé£xlbã*†¥Ýº´@ 0î¼9Q6···õ¸Ë²kéì5âKʤÊMfœ~'èoÈRâ8ÿ‰üôKuÿ#EP¾ÕN+++[[[h§rõZG´“UšYõ”7ryã�¢¬ Ë”€Wg©ÌÐ �ËOPÚ”J‹|WT×§•l^Z�fÒÈÖ³ª3ß Oo¡eŽ"=)î< ³Pi ÏÈQ8(ŸÏ[ŠÒ Z+Ì6Ë‘l¥ÏsjòÙNéíÇ„��6\³E~)•o~³©y£J�’á=/�« üˆüfæ{�ÆâR¸¯ø­ËÖðü¡¼O©F²ÝÒ¹HS«ð±¢}J£ƒüª�¾¾¾Rˆ¡SÚ)>]ìd=å–x}¢U­‘‡ŸÆÆ{>U˜“"öö¯ÒqSœ0ÍÆåû[á¯O�­ç¶ô™˜|b�E륷#ÞGÝ™ßÜ~Ö]ñŸo*6[–®…ìüXóúc�½ˆ�yrð»c©¦~rxåÞ»äÛöúT|”rb®}©/fqQóR}÷цÛSmÚ¬�ɧwVšvn¾ïü�|0!²Í–ûư•Þ'ú_y]#3íyö¬ùÖ­4•ݰ¡Äºulž¦%·ni&õGWöaÆHÚiÒxæÑ§ÿ.oaâþw²øl‰/û¸ IDAT©[�aŸÇ¾Ý; f¯ñŸ¢Äûu*VoÄ®áÏýÝ˵\™Ù%žs*ô͹Q6zµm»ÿöѲV&½w}”ÝÉŽ?ú8æ]Ð’–e:ùEØÓÇ¢óê{Qñ7¾wi´ð1ÌÝŠ¹L;ó:úݧ�A7•¸£WåQÒj±šÇÆ[q/wûÖ¨ûç{RB×w®Øÿ€(PµíÊ�ˆÐ¿úšwÝ/Ælíjé±åÁ»ø·ç':´XùR–_­yñ1]æÄ¬ooJ†Ê>)ÕG]Å—Ëš»~{èqÄÃUíLºÿAÁ̵OûÜša­‰c�WÚ‰N¶º¯�¬:Ky•—¶²ú´´µ$ÃÚ<÷=¶†…a.'­“£ ¸ DÒ¶J…Z>óÞ¦D3< ¥G¡…“Ö°Óä;WÖN¾Fª"ùÿ˹vj÷sÁ[;¹^²ËÊoéugW–[ÿ1BÍUzö‰à÷'ß�+M$K ïìª|3óOa“4IR3äŸG>ÆJéçQª�Ò†øil^Ê@ÖNÔbå1rãצåË”©å±éßd1ìצü Bñ‡Ä YõjŒ» yL5zu+ó)×SïÑ•:nMO�¨ìù—‚“4[ù†�´yçÍ Œvnv]Ð:%ExkòÕ�‘4C“ω~—ÂÌ2ÓÎÈžÇÆ>z÷îàÓ§4ç ¯^½ˆ�Õ´v¦*Ó­)Öm7ĊLJ–K»Ú.‹žI´Ófê Ù\èÒFóHU\⽪�»À~B±h/»Æi�l/r©?û^È|Ç´£TqV¦�Õ']‘¦1ƒ›*#íL=bòŽ^¥éÖŒ¿µ¢·CUcýB‚PXö™y<¿iYÓ’ú†ìG즳¼3Ý.í°–“¯ŠÉO÷NëæêÐjÄÖ{¼¹2ú÷¯Ê;¡ �βg=Eñî5[®ŠRÐΛӭšüŒÁ ò£vFFFâ*º¯�¬:+í(ȼŽýU­�¬ÊÎO‡Eá­.¼mJ9n†ÚÉkÕÜ3¥;‘6±ð GQníäÕn^¿Ï°µS¸€ig†“:ÚIÁ¦]¾<êÜ9öÒÎ^G�²5ÓNé� ìRhëV¾mThg†7ªô(üž—ÞTþ–ÁÒ#=b†ÚÉÂHÓ }êX¡…–oåØùJ?€j'`hg�ÕN___GGGäˆîhgJbôókþƒêVèæÿZ.„u'œ �‹}~tL=kªkGnh[¡ÃšàOÍ<ÁsêX ;þîééï•R­�A3ë9N¿ö�wxMÓÎ3Ã+÷Ú%7Œ²mV¾ŠŽ‹—j§‰Ûì³Ïß…_™îX~Бä›Óíl¿9þx‡O5™vîïWoìù<ùP_³¯×G&~ÍäÛ?Ô)ÕxÆ‘�¨˜°«ëû _vdP‡oÏ„ÅÅ>=<²VÍ©·DñÚ·Vµ'\ˆŠ~tl¼ƒiz'!éäGÉ ú®V½™·£âÞ'çÃ;+M;ý8 «W)Oo«ªîdûúý{Û?þè|èÐø â“’¶‡„˜oÝZcË–€·oó:õ'‡Wl¹ìytÜ{‰v¦þ ð�´pì©—ï?*DIÓΗËÛÏ}¤òg �í'´¤eùžÛcßÿénÑkSÈ»$þã…’v¦(ßTigu¯Í·Ãß=ÛîUÍiAIsYç7ÂÞ¯îXš>3q¿rþñaZOáè ¬úí|“”îÔ’CW·¬8è?ð¾¾F©�l•´3hV­+#åŸkÛñ¢bã>Ê?Œ&ÝüÑÉ6?j'kí¬Y³f­Zµ �º¬�¬‡žTÀZ~x½œÕhùúÌjóÊm5jj§r르¢¯ ÁÊGá�?lžIK¡ršÙ9líü/#ÙÊß¶²›&Ö½¶Äºu;BB*úùiK;®¾ íTÿF•ŽÍÃïùÌRE"³líä»e7­r÷W©Êfø»‰˜I'[Ú‰N¶D;½½½1¤�xjDYÞØqS¼–Rº¼¹,ú&Uj6´òr„|åó½cÜÌJ7­ÓeÞÙ0°®/íbWƀʛYûõ®Uºx™ÚÝw°ës@Ö‹‰!í<ùâŨsçݼ9蟮¼~}:4tlÞWy#ö±*®_vÀ¾�/~iä¶Tþpí�µÚÉFxй²¸k=SÙƒÆB¹¡Çy”[»Öš!ï¶ÂÍYöt¥ŠK¼ÓÃr¢üçëSldݶņ6lݧ�yqÃ2uz® "ëKþwëÀÆÕLôd÷ÔW¿ÇŠâöž–ŸÚ6?¡tS‰yÖ{>½vZµì\×Ä ¤yë™géN�<:ÎÑÔÐÈÔ©ó—v½é38£V!¡PcÓZ_/¼ò^L¼»ÁÛ©Š±ìaÏÂ=¶%†ýú…ìIPc³–?œNkw]Ú¨6Rh�§åXù³�_6ZüDþ“Ð�u[¯¡¤$]ýѹŒ~ñ–+žË¥´Þ¬ ”@ùW;­­­ÑÚ)…õêT~pKóÚ©ü#oP’¶:føø™êÚ¼tW™5@±|`{à|J™%ûññ4+<üÉkç § Ím®£|ððàÊë×~÷ïïyüxÞ�>'N„ÇÇ#ИvÖ¬YÚ €‹ÑѦÓ.wö=åÃ'�É ¨�tssCŽ 1íLJ.üãõ¯Úø¦ÍþQlºQIÍŸlZÔN¼@í£–þ³¦vî a=i5ÿÞN@ÚÙ»wo¼@Mjç†`צ»'ìRçÄ 6½K0ÈL;ϼ|ÙðÏ?_¿Ïߨ©á÷ví”>ÛIæ í@[Ũô¥�êtÚ¡öÓƒ€<×ξ}û:99!GИv®¹Ûôç[-ÔìdûáãÇ{‘‘Tñ•¾´“¦w ÈY4¬�hí@»Åh¯£ƒÚ™Ýg;¥<ˆŽÆS�hG;‡ ‚‘lФvÆ'm³ÔæûÎûžÔaSäÃì–�‰ÉÉÈY4¬�VVVhí@‹Å¨ï)aõj餺èœvùr‰uë(XE?¿!!¾§NÑb»�§hA;ˆÖN4©�×ßT+µ~qÛý#»¦ È *´3$:zô¹sS/_ŽJHøðñã¦ààFÿýÝ•+ÈY4¬�䜵jÕ‚v ­b”rGˆ½ú­�ì�)Ü?›îÞ}òÅ d(ÚÑNooo¼@Mjç€ÖKÔîdKUÞ:Û·;sfèéÓ´ùã�Vûöm !ÿDΠaí¬Y³&´-£–þ³³¥�òæÐ´ùÑÑÈM´©��7FŽ 1í ¯Ôéа„�EÔÑÎðøø†þÉæ]víZ„ @[Úimmmggí@[ÅhÓÝK¬[ªþ TH5Ù;i’Î#OЂvöë×­�hR;÷?©SvÃO¦›šo�ͦkoªe¦�/ãâšïÙ“”œLSÛýûFG³ùä”ä,ÖN )€v‹ÑÕwÝ&ÕÚÉ=SaBž íôööÆ TФv¾OÒ{[J:%&ÎL;߯Ç^³Fašf^½Šœ@ÃÚY«V-´v õbô¿Œd Кv8°I“&È´Z^¢ì@§µ322200­�h·Ýboé?›ýüÚpçtÉsž�×ξ}ûB;аvîy\ï«#vNñ>Ñ?ý0¶(;ÐEíäÏvÚØØ@;ÐV1ZÑoá¨sîüe*dž(:È7Úéíí�÷v Ií¼úººùÖÙ[8�}i¹âN3‹­³£ŠA;€vT£’u*,fÁƒèèÕwïÒ„ÁlКvúúúâ½�hR;´þ-¨ _{¾Çþ'u �è¾vÚÚÚZYYA;ÐV1ZÑoáü€¶|x!ZT§èœvù²¼SîNšJ¬[ÇÞä дv4C  Ií\ÔdÆ•Ž|ÑóØ€cÏí²ÔÎ{‘‘½�wع³Ý�{?F¶ yí$ç$ó„v ­btGˆ}‰uK-ýgÓD3\AUÇ'ÕÜÂæWß½[ÑÏy €´­�hX;£ŠÕÜö¿.‡‡Ž9ß³Õ¾Ñõÿœžþž�ðñ£ùÖ­ μ|¹óáÃ:Û·Ÿxñ9 €†µÓV´-jçƒhÓô¯NÉZ;å}q3]hH;ñ4¬�4Å$lyà4ÿF›­œâ’ô²,;o…‡÷8r„/~ö웳g‘³hX;­­­ñ´XŒ*<Û©¦v6ܹ³×Ñ£¢£iò=uŠ‘§hG;���‘#hL; lþõöÙz�ʳ˜×]»’SRØâÆàà . gÕ bS§/?ÉAÄçKÚtßþ0¤:TŒö::¨Ý�‘ÙÕN²Í¦»w³×®Ð ïp TжmÛ�eTŽ#‚‚¯�>>>É6éß?†4®Vܨ´Ý×K®ÐZ2Bý¢� …ôLª9÷]z!<¥fõý¹Žm׆æÚyî¥E‹½cÒ�^›uÙÙïäI§¿þ}îœ×ñãå7n¼¡éóx±¬IóeaZÈÀ§ ]¾Xù:‡inÜpÖéš8¿uf‰âíÙ®�׫:›Ðe­;nˆ´3}'[¼·Sñ NŽ¥¥¥vëÇÂ'üýý d>SÆUù´"²áÎé “î�ÆGN[úDä8Íô±Êð,h½êOm�?>Ê��vúúúº¹¹}æÙqwÉÀo<Š¿<©¾^¯‰ÚJÆ£…Îösˆß¿¼º±oͲí×iüá½#ƒÌÉqìëSl»mÍÂÚƒfÕk±òM_p œ[¬+ ÷—¢XîR§üÆ…&ë—˜o�ͦkoªe©�T÷=òìÙ‚€€•wî<‹‰ÑXêO 3ó= Ÿ{¶Ä¹ñ’—¹¼û;3j·ß—Å'c®}ãeøüH³Þ{TÆ _Û¦Ö´ô#Ƭoc3õvfÂV4sY�Ú8z÷‡z_ý…âÚ)ÑΚ5k¢µS U.Ïœ9C3Æ Ó¢ïQ=˜%ƒjÆTÏfó·ÿRÏVG)3 óy訠Ƥ …1„,5r¯òEi§:—>3íÌ2®²:ª> …­Úý è®v2¤Y³fÈ9‘«ZûLÖ²vÊI<6¤|³eägçæuªS¡„Q«–ËjÞïmêëXɨ° TsN¼>Õº»ÿGYŒÓÃ-úÅË“[÷÷®_ÖÈÄÎ{ñÊqn•‹›}µy{Ï"lÿ_üöje ù]clÑzÁÕ ûæèä/«›WnÒ½Eu™v¾÷ï& ]´xµ¦ÓOÇD­iÍ¢ê²%þþ\{Ùœ~)Û.+ƒ$ŸàcCÊvØ ¿‹b.ÏÿÚ¦´q9ûž_Õ”kçý¹ŽÖG‹â»óó;ڕѧèMùkXy¶O»ïïÐÙùµ/7価�híÌN [»Úɪ˔¦‚ìóÌmx£(æ fó¬¢ÌÐ �ËÏNÚ”J‹|WTÛ¦•ÊM¾´’‡áX=ž¹1σÑVRùÐ̨Ž[À´“7lúžòÉ–v®¾{WX½š½´“&ßS§J¬[§�_"øíÇ”Œ Ev³±û�ßBÿñ¶áÇâw ¿“ÙÍ â¶QŽËo<þ™bûa§ ý°K?|ÿ,´d¬�½{÷nÚ´)rDfz7fÔ¯ûí5­5v¦ÓNñéb'ë)·Äë­j�<ü46æù‘Ñu«Ž<-ÆnëR±íŠ ˜�,Ø•IÕ;ùÉëû'Wî½W/Œ«^¥óŠëa/wzW,á<éð“·7g:TEßÿþèÐ`Â?¡qqÏü=í|&‰·¿³)ßú§³¡á·ç8U�W¯úVö=˜>Q»zÚÚü6ä×–ÅLÛ-¹ð*ìÈÀêö ‰›:Z{ÿñ(æýëÓãê·‘uœ½2É¢“_|ZÔäøG?Yc,ÕÉ®Ž7¯=áôëè——~îP>­µ3%!t}çŠýeMiæÚ7“¶gI6$íÜ|ßyî�ÔÑÞ»¢æO¶qII�ááÍ÷ìyGÓ˸¸_nß©©!…Ž®ì³W>GÚiÒxæÑ§ÿ.oaâþw²øl©[�aŸÇ¾Ý; f¯üÚ¿\ÖÌØuÊÑgo¯N­_ÄÒ{ó�7O×u2éä÷AŒÙÚÕÒc˃wñoÏOth±’®øí©6mÖKÚnS><[Þº ÝébÀ4«š#O†E¿º²²cÅ´ÖΔÄ×~Ý+ôÙ“BÕÅá•{í’$59îþlë·øŠ$¿N%•Í=ù¹Iu¯mO£ßÞÞÖÏR®�A³êµY)þ;¿�¥ï¾çR>%½±ýÜG©ÑO *ÓÉ? _�ÐÎ4í´°°@kg†h½“-¯"3�¤¿Ò*¸¿^ã—V�¥ÚÉÖPx¶†…¡\\Y­}˜ÅUh¢M,�©…´�"K-�¥ÜôÄ4RáÐÒ0,€Š¸O;ù`B™Œ*”) wîä/ídc iæ½�ÒÖN~ùØõâ—^z‰ÿûmÃî|~[²ãfxodWù3%MªT;R®ÐÚ‰Ç;AÆÚéíí�¨È*¬¡»}I絘…ÖÎr­VGÇmüªÚØËlÕå‰ÕÚ­‹»ó½]óåoyœ«“ktÜ”˜^;{í�-¿YÙ"uo}MeÞ°ß»ÿ5ªPËU¯eÚÙ~½\N«äþwfÚY}”|ìÒƒör©ŒYÛÆbê ñÚ·V|w‚í´@©v¾»¸ k½Š%ôhKªõ'ùwªôÍ9ùîR;ÙÆßZÑÛ¡ª±~!A(,Onšv*m*HÚ¹!ØuÖ§Á„ì¶ÍTS;ß¼Yeófƒß~«¾e ›vî¼öæ�´3µ“í­)Öm7ĊLJ–K»\=“h§óÂGìޫδ/hV-Yëý�évi1,'_•jgÌ埻7¨\B�®¼݆É;z™=Î>¬“í‡Àß¼«•”ÝB7™¦igÄ©ÙíkW(^”¶T—6ÖRÂÆ¯�É»�ŸnÚc›¼ôS'ÛTíÜ߯\ß}ig›N;�,Ýùh'´S¢�äœhíÌÐ9µ[ÅTní”vdUmËP;ÙYðú7‹"mÉa& 7CíäU^;gõrÞÌÅÛ¾ø¡y‹kA’ž £@E\h§ßSZè¹’a'[æc¼�P¹‘<· sTÞØ(½Õ™vª¸mâ*¦øÇ�Ÿ;/…^¸ÐN –v8ÐÕÕõ3ÏŽ÷·WuiÔeÅM-wédÚ™’ýüšÿ ººù¿– aÝ §BãbŸSÏzÒ1rCÛ Öjí žSÇbØÉðwOOר”jí šYÏqú¥°¼ bšv~ª¾\¶ÍÊWÑqñjhç›ßÚÚØý,6­V~cª�Ó¬»Qqï?îô0nùSЛ×� ¾¬ ³Žó#«Ö›r!<îÍõ_Xkçñ¡e�gÜ{¼ºciyréÜmÇ_ˆŠ�û¨´©€içÌ«’’ ÓdûÇ÷l†¦””,ÊΨ„„QçÎi%õ'‡Wl¹ìytÜ{‰v¦ºâÃE.õÇžzùþ£B”̵3zC«~;ǤÝ5AßÕª7ó6Ý5É»½M¾XpûÍ›»?µ*'ûõãòĵÇ�}÷6`U§ 2íüç›JS/½zûà·®eåÚyaT•¦KžPÜ¿.Ùqõƒ·¯n~ç"ÕNñÐÖÉ6d�S�~»Bã¢ïíèg!ÕÎÀ™vÖƒ½øÔÚùfe³jCOÒ�˜(ïd[vðIÐÎô#Ùâ½�Š_pé[á´®�¬ö,mŠa[Z;¹(òõ™i'wKÊš-íT®‘«8´Bk§Bq ¤v²1„øŒŽ)¤B;3»F9»mø¦ÌZ;UÜ6 q¥‰ÿ/­�èd 2ÖN___¼@…ê¦üÇž"ÝüµÕ⺼¹,úÿgï<à¢8¾8¾ù'±b‹�Ø,±÷MK4–ÄÄ‚-XcÃNìŠ%ÖXPA9• "JPDQ‘ŽôÞ9à8Úû·Ër¦Üñý>ŸÙÝ™ÙݹÛ{÷½7ó¦Õ—ZV\ô%14í×£uÓæúÍ>êÇEoÎÎîóEc(ØÞÐ�¦c­j·iþEßy+gôYâ þ®®¶à®¸bÖåÉÃŽ1ŸP.+¾dF«f{™¡Ý¾©xæd—-/i:x¿öLK&Ðóu݈C³dÞ×ëÕ¬Q»U®å9–sÔ7‰?p,P#îÖàÝÚ3™€AB³iâqŒÅ�¦ ‡ªª|ÆÌ/�%ô4êײQËy7sSî-ëÿE•/ÇÎ÷Õj¸H:ö¶ÁÀö-¾PŸ¶iɰ)¦Y4ßuÓÐM›uöýÄ> Å—+ò;<ü‹FÍ¿¹�(uè¿„�¼ˆ¡ÔåKÒÉ'µg=²•¿²íWõnÞ¨í²‡¥IgF�;ËÄ} Þ£=MHðê÷9:ˆ'Sí*<“b¥]˜8Š,Zâ¼\Mü.¥éðÃý'^Ȣ钋Åþl!~×üï‡Ûð¨yíتQ‹ï-3Ò¬ùªm“æ�GÎß�‰l•øçŠ!Z´QÓÙ¬7bü…4:×}ÇðŽM›µôý$Íùw€uóVkª4j­gWk½¸Oë&-ºM˜1zˆ‘/ͽ’>$¤P–»Ñ¸.-[u·få¤a{BÄW4hšY.€¦‹Ñ„nÌ ;M—�ß¾QÓ§"Ä¿Ëô™t1 Íbgõ¹�ÚÚÚˆ�Ü怜õ”ˆ{)gì”öØ°×ÆB#wn'w¦œl줫O°¬;‰Ëˆ´ÀNø¬�¤]O¤}Ÿ‰=;â—;EPº€Œºÿ1ì¼2®Æ¤´ØIüäìÜN.vÒï%÷G�¿ö¶áÎ.f§VrgcÊxÛHוø]†®ŒV ×#cn'{Ròp!v¢jÆÎÅ‹£·…’vþýp|¨SÜ©áƒFþ¥ªï y2»E°“Ïç!v¢PÊjFei—¯/wS>s;´¤Pù ÞÆTPµbçèÑ£±GP(eÆNEE²møÊ´˜1ñTü_¨˜p|â « ì@T5ogïÞ½ûôéƒØ‰B503,žZ¡ˆÜ\î&ª6ýåù™8±U+vêéé :{…RZ)6’- …ØÉd‹‘lQ¨†%‰H¶*ffð»…Rv.^¼xÔ¨QØ#(”ÒJ±‘lQ(ÄN;µµµÑÛ‰B)PÓª­¬6ßÕµÎ*ëvFäæb7¢PŠÁÎåË—ãº�(”\¿ÔT—vµúZhhl^žì* Œd‹B!v²Ø©¥¥… ¨ P w|,áIÙåÇÚÙg*d %‰�K—.EìD¡ä¦ì¢¢s��ûüüȈÙÄüü9ÎÎ�ââj+àõëÈÜÜ‘èvd$ö ¥XìÄTP(Å2'7©ñxuò$ñsr½��lpæ! ¥ìÔÕÕ1bö %Írrú¢•¹9.ÛýæÍŽŽyÅŵ•Ÿëâr+2òuFÆ8;»Ø¼<6åÖ^…BýKØ©Á±…R”>tZ&p©ÄÜN’°'Q(`§¾¾þ°aðGP(¹É1>žQÏÂ~ééœ=­­›\½Ú›ÇcÓ™À@ìIJÎØÙ§Od‹B)PÜù&õ=‹œ‰B)v½äs IDAT`H!Jžº ÐXZVù¬ÂB}OÏii²«äoÄo·(”¢±SCC×íD¡¨ù®®�¬¬.‡„ìòõ…ŒD„!‚*,²B{…Rvêêꢷ…’§øEEËŸ<v÷î©€U+«í/_ˆDuÖ*.-µ��%3RR„¢ÒRìIJÎØ©¥¥…؉B)V�*ff�ê?E`• µ%›¸n' ¥ì\°`†B¡ä¬B‘h–“X¾ ÁÁõüâ;ÑÞþ{'§~wîÀ&ÔºT¿Š(êÄNuuu\@…R vùúpFNsp’¬§ß¬mDn.b' ¥`ìÔÓÓÃ�B(”<�•Eâïy§¤Œ¾ÿ'77~Q‘ì*¡|>Y�Œ (z•žnèå…=‰BÉ;1¤ ¥Xj²NN`N0¦õ©¤Êb§GRlbO¢PŠÁÎáÇc� PrÓÓää'ÉÉ$_V^~!8@Tv•˜¼¼ŽŽðõ—`§uD„ыؓ(”œ±S[[½�(”Âõ¡+ ìòõ%ƒl绺sâ2ž(”b°S__Ù¢PrVFaá͈ŸÔTÈÇ õ©²ØÝ}’½}·›7—¸»«ZY…ñùØ�(”œ±³oß¾ˆ�(”EÙþ…á²@ªë½½8qÑNJaعjÕªqãÆa� PrS‚@ÐÓÚú'77=šYÉ3&/¯ÎZeåån‰‰'ß½»š,b7¢PòÇN\@…R¬€9=’’þæ,MœÛ‰B);-Z4zôhìJn:`–*ì<ðúõƒØØjÁ:"ø{…’vòùü   Þ½{£·…R 1’ÿÜ(Aˆ�(TÀN]]Ý¡C‡b�(Ãg©’%Ô¿¥KÁÁ烂XìÔuwwOJú ®…†îý{…’vo§¦¦&†B¡¨ù®®�€MüýÙÚÖÝNÄNJñع|ùrôv"v"vÊSÙEEýîÜ™íì¬yëÖt‡!¶¶%ee2¾òŠÊÊ$’iHb' %ìÄTP(…k½·÷X;;`NÈDäæÖI˜5&ìFJ1؉‘l;;å¬ü’3PÖ&*ª@$’Q2)?¿F“yÌß»…’3vsjkk#v¢õDÓ)gb' P ;qÝN4œ µ�vKFí  Ó¯Ìœe–ñŸ�]n¼�ïuª�b§––b'ZO%ÁN555øohhèõq,ãŒ.JªÁc§®®.F²M»¿vH—Ÿ7n­9ãäëB…}¢&ÿA}BQðת+¥w–Ê*oH¶³ÔöçO)Žª)B¹0Èâ— 훫t·æNL)MçXMרô Žðͧ{>õ¿þN³?€ƒcQ¨‹�8ÈVB&&&ä#Ÿ �¢¬§ŽN•ñáñv7r�'ti�Åt*o’ÇãÁ&ü'%�9¡…�; <=¥~F¡P ;õôôFމ=ñ_ hºô‘Â>QcŽSƒ�Pt)•âGéiQÓÍän/]¨+?Ðv†þjŠiÕÔ ŸMªÓÍk#÷÷_ºtÿáÚ«Ô<~øý­‹�ùÓ…6ó{¬!6#þ؈±¤ü÷±sšƒÃÙÀ@‰”‚ ¢ P ;555q•ÚØ‰à�¢°ÓËKl¹¢¢Ä`FòòL@ß�>;Mq7et t/[ø“®îäT(ó#v¢P¨zcçÂ… ÇŒƒ=BÓ¥‚è{zCOF(;VâFµOeÐT¶75«¥ÒŒêý åœ">fI íLý�¢ºüJÑo(õyT)Seu/Ê‘¦|�¨ÉúÔWm©V}¨ß/RãT©=¨óAâ‚WÔhª%5r[NÑ!Ôð¯©å#¨-©©§(Q5Šü”ú9eÇÚNQÈ¥yÚ_4bö:×FTó§áè®-›·ë3û¤o>ý`Áç¤ÎÈ3IRØ™çwêÇ��[4kÝsüz»xøòv‘Ê43>÷žý¶©�9�H¾ÚÙÌo¥÷°ü?��ÃîÞ…/ )º® (JI°½�5 XHqC=«°�GV  %‡X§(†2Ä/Jò�QS«(¶.©Èú#IØd›Ð…�•.ß*ìŒ"\q Èº+y•uXb”ÀN¶.éUæJª‰‹šÐ>œîcÀο\7"7w—¯/Â* ¥`ì\¶lÙøñã?úqXÒ>Õ«-¼^ ØIÇSÃÔ©šÚÚ›ZçLå (— T—u�OÍîD]­@MúÕm%bò+U){šòÙD}ù=õ&�²]L© §œã¨w{©/׋ BmyB …oeàHÑ�”F{êä3*+�Ö‘ò„F)UƒêÞNÿ]#öd¥?Ý8`ÜÙZx÷Gµ¹×ßçfùí6æcèíI«P’ƒ�Áû¨/»#ÈO~ºsp}—²´3c;¬¯þµÄ^·Å|›ÒŠ7寮S®ðÿóØù—ÙÞ�Žžãì<åÑ#’,ÃÃñƒ …’?v¢·SÒtÉ*ì$ ÿ¬ M <GY’”ÆN²Ê“=¤ `Á•€(ì'¸ˆ+áíd1’¸%A>Y†”áí„’$µ/)¥ý™\ýH¦wv²²úËuÕx¼A¶¶ˆ�(”‚±S__ìØ±Ø#4]V˜xõU��o”;KܨvßR¹BjjWÊ—˜I_ªë4JLõ™@e²c{ü¨î3©’êØ9ßF¼™q±²5Gªƒ®8³¸IÕì‘o.1Ø9�*$¸3u¿Fìä_ÿ®Cû/7þBûgËÈ2:üP¿ª6¾ÜüBv–ßþ¡�¡;Ù²GkÜ…tonãYVî=Û-nþã� ìôþµ‹ÎÕœÿö›Ì&*êL`à_¨˜˜ŸßÉÒ’áž”DRl^>´(”œ±˜×í¬Q@MŠ›aXƒ·þWr 8C6¹Ó>kÄΨ¨ŠC¤5R…uf²îMéºÒØ Èõgòª·$);¹�IòÒÞÎ�;ÿÖ[áPÊ€�ºººɶ‚; îé÷è±å¥b±³¼„J|M­èOÍåUáOJ˜O¹þJ©Q4ŸÒéH™†Wz;é~½(�,*þ)5ªuعwµû%UTViw9عZ•ºWªí*5—ìdôhé€�Ï‹ÙkÜú^mÁ�˜¼ª?"Ž è»ímN¾°T;iß­½ú¬vL̦xíÚmµW9�a=§MïÅÖþ邜ˆ‡»ò¡_lîÎdÛB×¾߇5 sê_Z…BýƒØ©©©‰ØY£™”;ÉZ®‹’•ðv² Èî¯ ;Y¶”�¬ÒØ)�‚Ò�d{;I]ÖG ÿÙ¤��p�íßÑ|W×ihCQ(…cçòåË?zì,{¶wD·V�?kÚ®�먂 [ŃJ>Ïü ÚˆúR‹Zq‘ÊfÌ[¢=5®Õ´9Õo6åÃïys–êó…¸`{ÆÚY-¤Ú4§úΣfô¡hÊw+µà®xÖejرŠ@A_ê‹3Ù^Ô mªéÿÄu·¼¤è`J{&UÄœe]wêd2)½^T£v”k9e§×mÙ£:á¸Ï)êÓFÍÛªMÜáʧKìôGtiù™8âîw7�/K^îÒ¦‘ŠŽ‰BëkÔó{« LMs5úF­MÓfíµ¦ïó`ºµ<Ýóð»´j򢂿5·¢D´ÐzN׊�B1G?�Ïf­Úâãsàõë‡qq$½ÏÉÁ>A¡;+nVÅ]EµH¶,²—ÆB#wn';õhS6vr›"w¥±“¸6™¢ˆ3“;·“mËõwh�s;¹uYž„’ÜÖ¸“iÚù Cžžk_cŸ P ÀNøÄ�BÊ!e\·3òè­ÍÞ9…”W‡Æt0pýÇo:Ë|Šúf?È$žÕ_¾ jQ¡H¤qëÖÈ{÷f;;Ïa/"»¥¡*âØè¬ë=‹¼èÞ’¯�á«­Ø©®®Þ·o_ÄN´žu. Âz ÿ¥N>ÂT.‡„Ô˜êõ¹|Ù _ðL±s¤ë# tعbÅ \@ gm¶³ðõ™™Zí›}Þ¸e×áú×CEÿü] ï-eH§]œöíÅD|Ô¦€¬¬)�5ÀOšMêºö³ _#õ…÷¸;â��øæJÖ‡4!pß<¨m““¯&+E¯¼2ê3Ý<›¦s¯è =MÓþ{͵’á¼.¶×µ7ŸeÀN---ÄN´žõÁNvES®ÇòŸñ”~<;k\=¥žÁ�Ôx¼†…�ÿȨuée�H„ª{ºþÍ÷ð‡Š@¼ý�—M¡ì¯3(eÁÎÅ‹£· ç?²æ5ªN üóÏžÖÖÒéuFc»KÊʾ~ð ^ P¾{rXÒcõÓj{òͦ÷ÝO~€öZß­:2Ö¦Ò;ó{ùÖxÈgS·ù6Ü1G�¿øAƒáŸ¬UýÎŒ/ÈüåN$Ö§•-—=VºÔ~8ÔX{Â¥LÈð/Np$¢gÌ·œÖww0>?J€�êêê@žˆ�(TÃÒX;;33e‹d+=:š;]ì”Qòïcçß÷¥sc8ÿ}Øf½—2Vl’ˆüü‘¬jÛ`°SWW½�(”|”"&æçK' JÙ3 {óx`5YR= —KÎqÛ9¾[óO™Ÿ;{í|K ^�žÓ¯�JKÕ‘+nÇ–ÇF~ m²´Ò뻵'Ù%ŽÎ嵾먟õ‡vhÑZcÞµpqÀ¨ �]SÔÛ4oÝsâ×´Ê“„ì×"uÚýò„Ø©!ž:ܸ­ö‚ë‘å v�«?¨]³V=§y!¨ÂÎÒ«¥ÃU[¨tèÿÃ…w…U—,±`lúÅñ¤qõl—ñ½OÓl#^�öë?2é\ŸãsûwRiÖ¦×Ä�öâhC ÿzéò�Z´ì1õÔ[Qõõi“·NìÙºy5cÏl1wôû~Õí¶*_ô_z'žN:[¹þíÏvt’Ͳ�íB5]RËBxcÆËÜhv†è÷½U-xºïk¦«Ç]H—X×ku' >@ŠÄN>Ÿ„s;Q(ÅÊ&*  q�­-IÀ“õ©õ׆æþMqg>#±Sv ILÖå–'¸¨S9™�¨ÎBwr5Û&qñA;l#ÒXìdKJP([Z�ˆÆ,qF“¬WŸlÈd[�˜¥ÌÆs&l)q�÷Â’Àf.Ÿ³LËv÷Œì-ãS£DعxñbÄNJžÊ-.žëâÒÃÚºûÍ›�zY[ûgfÊ®RV^.AªyÅÅò¸Ö²?çuúÑ:�k>§»> Säá!·ù,9+ðÐ°Ž«Ä¿Š³ •¹¯è5ùl`vAN°É„ÁBċϪ¨ëÞÊHu\ÙmÈÑX(ãh jà(n¹ˆ7Kuу¬ê¿)ĬmÊÅN8�8þ³ÏúãÏÆ”T«¶4QôáAµûEQrÁNâíÔÖÖîÓ§b' ¥(u²²ZïíM]¾ è8ÍÁÈSi/•;‘uбAŒe{;Ùê$#]�¨F!;›bñŒ`'Û‚„+’»  Š�sý¥d]YöÊYtd[�8{ˆ"+q�ÜÓIã"×�)��ÜÂÞNœÞ©\عtéÒqãÆa� Pr“iHÈŠ'O"ssg:9Å SräÃ�Eï¶lÛ¡y£f_ŽÞá–MÓ�8«¿~òÍ¥ô:±³r�íƒ��߄צVœlôÓŸe5`gºÓŽ)ší›‰¬;¹ƒl3/M³kv¾ÞÞ»ª-Í]•s¥Œ•ÆN»ŸÛ#Æ7g«®«&üf‡údÓìJ=5ìüÓ}.¦ý1¶ê¬Í—9‰±³Ûj¦ ,§1h]…�tQ(ï×iÆÏÜnŸÈ® e2PkOH ØYžìzàÇÑC&-¿T(��Q‡~u4ÄNêc BKþGäæÖ3&­  �GbØ©Êgž§„»’u0²î¾z²•ð޲ե±S"Z2w�-Û0× Ë%4îîïï•’bÝïÎ÷¤$y\«èùº®õÉgMZvþêÇKÁ":ÛëÈ íöÌê¯]¶¼„i·õhÚ¸ã�ªJï/MíÔ¸ÉWüiï_Õ–<`ö9,én(v)&;lšÔ»MãO þPŽ�·œÝ¥Icµm¾å‘W窷hÒ²×äéÃF‡ÐôË-Ã&-œÓ»E“6š³Îù�Å™ ›hš�hºp¨ªÊgÌmìݵãz´Ï ¥Æþ‘w¦¯Fœ¥EÖ35˜é£™÷õz5kÔn•kо>b¯nkÍïÏTù³v¨“�B¹—':UyºüëbOð§Í»�Ý&ö+sWÄÍ5ŸÒ›Z”¢±S]]½�(”EèÑ#)išƒÃX;»zú-%œ¢òY·SÂ÷(½”+q‡r �L•$^J $“®Îò­ô|HvÆ#»‡�Ò)1·Sš*¹§�æ@‰¹�Üù¨µA¦ôÜNé¼÷Xã´Rîõs±“Û3Ü» ói;• ;—-[6lØ0ìJžŠÌÇ(Í,,<èY€ ÈÊúÁ¥*8ªsBÂÚgÏäq¡NË:M¹SP˜m§ßsðñ|éþùlê=Â_ÿòy7g÷Xû¤;N °C ¡PÊ€�¬ê“¶“••‰¿?É_ ©§�ôïˆÇyþSªÍO[›p¥ÃNx*ÐÛ‰BÉSÙEE[|*Âçä¯yö¬´®H¶ ÁÈ{÷ÊÊ+Ðãzx8Û¿«´‡ÆôhÕøó¦í4§ìrÎÀïQØÑ‘?ò ê[ºÈfÁðAH�J��½{÷òDìD¡¥¿æ·´‰ŠR13SãñÔ˜˜ð,‚þ»—ZÓTLÔß‘ôÐeÙBàW:ì\¹r%F²E¡ä©KÁÁÇ96OÏÃÃ+%¥ÎZK=<†Ý½»ÁÛ{ÑãÇí¯_ÎÎÆžD¡äŒ�Àœ¸n' ¥$ØùA!… °Ü–NA¡P5c'. ‚BÉY‚ƒ�¾­š©·ØÝÝ!.®>ß}]Žùû_ NàŽ(”°³OŸ>ÚÚÚˆ�(”¢D¦tšøûC‚L=ƒÉg2' …ª;.\ˆƒlQ(y*½  —µõOnnŸ? ª~ë–P$’]å}NN=ÅF¡Pÿv¢·…R¬"rs×{{mB‚L(¡NÍwu%¡ãQ(”"±SOOoøðáØ#(”<•S\|=<ÜäíÛïßçÕcÑN(3ØÖÖ¢PŠÅNmmm )¤„*).*)).)++�¼ŒTN—ˆD¥%¢r-;•‰ÊKŠ‹ÅÍbª%5¬wÁTn§…Rv.Z´h̘1Ø#Ja51¡í¬E™……ê·n5ºr¥§µ5I§ð©A¡ä��¸€Š2P`N¡ 8"°è­WÑ;ïš“ÿ³¢wÏEiIYù±Qéî‘énQé�kL‘i® Y¾……y"$O¥1�IIdÈ��§§DÂ÷? Õ`°ÓÀÀ`üøñØ#J‚�ÅÑÁÅaod§’Ô„ü‚Ìø¬u¦Â¢<4�Je;÷¼zµíÅ‹?£¢Xz$éuF1bËÊËóó¹©>>R õÏb§–––ºº:b§ÒÐÒÒâÔøü] ò¦vʛٽæô]×¼9½KÞöJ:»×å»�©=µ¤�Ž”éËQ™yÑ¥%¥h"•;ÉØZšYþD"É®Èr)B¡��zzzˆ�ʃ�ù¿Îü¨%;Þ>�áyðq³nMd¤Ãî-2òÂÑ4*•íL+(H D" †,©}¨2ÑÞžä7ûøDääàÂB) ;544pn§òbçî…yÓUó¾ïYsòœ§Qê`ã�ta¿ë瀗Æ.5§]NÔ5ßqYy1ˆ�J8Ph—¯/w³Î¥PØ`BU…R5(”ܰ“Ïçsâ Û�;ÃJE%$É@2n1.ÂÉ9å¡MøŸÃÏ ~÷Î?+3£ÆkVÈ =ÈÖè‘ ¤bfVgp (O¦€²œŠB) ;W¯^�!…;ÿ«I˜/HMININ‚Œ’`ç,'§/,,Z™›w»yR÷›7g8:ʘ¨™YXø?SS°—i¯Ÿ>5(”ܰÙ"vŠm´ /-5RjjJ^nNmf…)–"Ni©Â|À6°A¡!Áïy¹JNž…B7W×Ý»vmÚ´iÍš5NNN¥¥"iÒVÈ }9$Ì;«³>«§H‡ BìD¡†�Ë—/1böbg}˜Ï¸Ø°š ‚9_½ò=õûï¿ýö›±±±·÷3å)äÏ‹ˆÀ÷< Õà°˜S[[±óãÄN€®—/|60Z½zµõÍ›€—¥R Üfcc³nݺõë×ÿúë¯ááa奥o^¿†Z°ÇÓÓC™±³¼Läáá¾uëV`ν{÷:tèÉ ´r8”™‘÷«ðA¶cíìê¹V' …R:ì\¼x1z;?*ìhü­R'NœàggÕh{ÒÓR÷ïßOŠYZ^';¯\1ݱcÇéÓ§ „JΜ`ã·mÛ¶yóæ-[¶€uw¬<ØùGPÐþׯñ=�B58ìTWWÇT>fìô}ùb3£�7ÇÇÅ•U£eËd¤§íÞ½ʹ�zÿ>¼¬TôêÕ«µk×:9:*'yÂUå rÏœ9 x ̘š’_âbcØÙ]¹r%*2îE±ØÙÉÊÊ&* ßó(TƒÄN )ôbçæJ�½ò}Y£ùqpxÄ»|ù2Ù ÌI@.-5E™™³@˜üØ1¸T°ýÞÞÏ"Þ‡'%&À�û¿}csçŽÄÅËÿµöNI™doŸ‹+  P ;{÷î�Ø‰Ø Ú°aƒ‡»{qådH’ÊÜÜ\7UJŒ�áá奢œìl(ìâ윖–ZZ�Të ÞSRý“EeÏ/•.H¢: eJR’‰‰ Ü@&¼÷ËJKà?|ݾ}{XX¨Â±s¬��Š™—E¡$vêêê"v~´Ø ºbjZXX �mGŽ‘ÆN°¦Ç�·µý³HªŠR%Kccc¸òëìÎÇ�ÝàÀÎ�;âã‹��ââÚ_¿ÞÊܼþëv¢P(eÀNMFˆ�3vLîÙ³çàÁÀf'OžÌËÍaá�ñæ=zÔÈÈhÿþý[v©1üƼ›ÊJ¥#ååæææðYP„ Ð]YåŒJ2ß’K¶�À”çð³a?“ GR%3#L!Ô”€OæbDLu¾Du¸¼ôÔ”ã v޺ţé28 íêê7²{÷îÈHñôBžŠz­?tÝN ¥DØ©§§‡ƒl?Nìà ÿÃ’“%ʼó»uëÖmÛ¶mß¾�‹�¤ºòÏêLIN"w÷ÀÎŽÝ †“ܯ±óƒÖítOJú#(H:ù¦¥áSƒBÉ;µ´´;?rìüõ×_MLŽZYY�‰Ü¸qcXh‹mÀcïÞùs§OýFÐØØ 8;9’£ð_˜/xæåuæÌRýÀ�æææïßCËY™t5Îù®®ë½½IÞÀÓ6ñ©A¡€� .=z4öÈGˆ�î/^„ÌáǸȗ˜¶ ð쵟ù�“ÅÎÓ§OÃæþýûØòQ‘'OžäŽÚûôÎÿ-\$³+]]]ˆÜ»÷78{Vfæu ²‡œÎáÑ#}_¾$••ûãÇìO¹�$ŽÂI¡Í~6Þ\?31Qv&=­­rsÓóð ™%UbòòdWI/(qÒ)z60ð|P>5 UÇFÿ`]PßÒE÷–|} #+vöîÝPQJì•d$�ßV¸ö›Â_§ÕœÖënú®ü‰ãëTÞEŸ¡ç½þñ¼ætÎ{ m >_�XZ"ª;<yÿÞ]Ȱ‰]�e¥ÑÑ‘û÷ï6  cçúõ\ìÜÌðÞ Ÿç° Ö ˆq×®�·nÝ xìèðÈÑÑ¡¼Tعk×.â,=s挓££­­mdÄû«W®@ FÛ¶YYY‚� »¹wï^8âä¤Ä|AÞ¡C¡YàÕ°°Ðøø8¯§OÝ\]€9 …ÂÛ·nAu0·oß¼ vqqÞ·oì„ÿ‘‘~~~v÷ïýöÛo¶pyî`w»ÁWsss0Ù@ª�=ëR�/PÔk Ìé‘”ô¡Ø)Q ½� TQQQ:::õ/…£0•Raç²eËp•�;===^VFGëÅ {Nœ8ž‘ž&;S’“ö3îP0HpˆÇ³677Û·o/;�'Á0ƒQ419š�• ­Pi6©ëÚÿÍ6|�ÔÞãîˆ?>â›+4ÂKà¾yPÛ&-&_Uñ+£>Óͳ!c3_}ç0¤§Ç{.¡öòÅöº£ö⯠J��èíTZZ\(䧦¤ÇÇf$ÄUKñUùô¸ØÜ̬d@,ÁNÐñãÇ“òyéi©>>Ï "^¹r%7‡OªÄÇÇ>|XÜò¾}pI!ÁAd$�‹‹sÅU òøülÈ$ÄÇíaÓÃçHbb9 ‡#ÉøŸˆ÷áp%ñq±{öˆ‹Ý¿6Iãª$s;/Ÿ b±S×ÝÝ=)©Î/¾S†Þ½kèåõ“›ÛÞ))ÊñÎuXÒcõÓj{òͦ÷ÝO¢Ú&ÐtêúÍlÚoýš›f2‰ÉûÍ,ÝV’ŸRR›·óÀ°5"QÉ… €ÐN�:6133ýÌ™3ëÖ­à.“��P‘¸(Á\V 2T\ ›~ v—Þºu À�YN³ôþ½{‡;vì€ÈBšd6irR"Øñ5kÖÜ»w/‡Ÿ}âÄq8 T¿yóS6†�··÷FFÐ8;”Ä@:rä0\ÌÅ‹ÊJKS’“Ž1!…xµl¢¢H\¨%Y‚pNb¡©ÂN%ÿ>vÂÅüMú…êó®Yï%ÛQÒ‚p›Ü[ÆOH%ÂΕ+WŽ9{DI°Ó3©ða|�ìô&½P$ˆ.Mµ¯3‰ ù²±6É Ï½{ËÌH‡Í€wï` 0§lìcùÛo¿‘á:5¶%Ø ­½yýšÝyåŠ)ìܾ}»D#O2“ÄÿíÛð°P‚�/^„kàssu%Å^¾|ÁÝMhöáC{nH!{ûlâÚUìÌ.*êwçÎlggÍ[·¦;8 ±µ•R¨ ÌÊÊ\Κ……Å ä6À)ÇmçønÍ?e~îìµó--xuzN¿v*-UG®¸[wlù%´ÉÒG|·ö$»zly ØÙuÔÏúC;´h­1ïZ¸ØW›á±kŠz›æ­{NÜâÊE Ù¯Eê´ûå °SrŸ4n«½àzd9ƒ�Cçêj׬UÏ)G^ª°³4ÂjépÕ*úÿpá]aÕ%çù�úq`çÍZ÷¿Þ.žN¿8ž4®¾# ²ßëð4Í6�`ç×dÒ¹>Ççöï¤Ò¬M¯‰íÅ?„þõÒå#:µhÙcê©·"úÁ‚ÏI#Ï$ÑÉŽ['ölݼ�šŽ±§Øeé`ÐïûUs´Ûª|Ñé�x:éì(RöóŸíè$›eÛ7þ„jº¤ê›‘ðÆŒ/–¹ÑìL:7f¹4ºØï÷i½Z|FQý¿jØž´ÒgŸ7½VwÒ±àǤâ±SKK ×íTÆ×¨¤(6§p¬su�O]¯%Yð)+þïa4gB;Sô#Šv¨%=¤Êž�(É‹.©en'`çûphˆ~é󬘕  €—/^lÙºÅØØ8##]&vú@S@z¨¦—/K¯áÉb'¬G�(¥ËKÍÍÍ7l�†�ŠŠdÙO<¸8= ¸°ÓÚú&0dP` ˜æ Œ~ûÍØÂÂ<5%¹¼TäþØmíÚµ`=áÊK9g„ 8uêw¸Ô3gNó¡°’c'h½·7 #0'q`*íÛŠª0àÉ‚ÿd“%C¶<ÁE +n‰ê,Ô±§à¶I\|ÐÛ�4;Ù’Ê–‡ØÆk<#�I(ÆÝ$�ɶÀ^39Ü{ƒ2¹÷(q/ì! læò9Ë´l×qÏÈÞ2~B*vâ*J…�Úr©|ÙiŸ~i†'íò©ØpÊH®MDu ²…Íè¨H€L0�`#aóêÕ«pÈÒò:˜"ÙØ pHHÏÛûY�ád vîܹ#%%™ÝIåž8q¬#·pTdX5ñN÷Çpˆx\A`ÂÙxBVL›»v:ÆK„5"îMÝAɱ”_R‹ˆ8ùî�MTT�H¤¼ï˲?çuúÑ:�k>§»> Säá!·KÎ <4¬ã*ñªp¡´']䋯Ë}E¯Ég³ r‚M& > «¢®{#(#Õqe·!GÅ£¥ T Å-ñf©.z�Uý7…˜ãƒµ�C¹Ø™pjøèS)t¨qßAûƒ+ù9åÜèÁGb*6¢p§w*vöa„Ø©„Ø—Sø�kž/k³žVüFÖü³á4’vmD;Õn=©²ãKòbêÀÎò²¼Ü°’7nÓó¶^½z5|{.,ÊÆN0 G‹±óò¥KÅ…²°Ó¡:vBu@ÊÐ�ÖA ˜ž–züø ÀÎÛ·o�)„S'%&Ü»w¬'Ð#TªÌÍÍñôp‡3Âõ„sV|)**,‹ —zþü9b=•; <=ÿ²—*NspRe“.˜;‘uЙ0¢ëòv²ÕIFº:Q�BRÅ�Íq9ìd[�pEÂN¶)%çúK¡:—Ytd[�8{ˆ"+q�ÜÓIã"×�)��ÜÂÞNœÞ©\عhÑ"ÄÎ�;Á䀱�Í?þø#1!~ûöí[·n x‡dcçk¿W¤�W¾¾2° 7­1CƒçÎ�+ %Ü•$ö¬››+ ktáÂÄç :xð@p�x°ÐSS²ô‹D`[°µûö‰'ˆššš*3v>KI¹ž[\üêê] =@‰‚ûïëÝö�m;4oÔìËÑ;ܲiúÑâ&ì/‰Ÿ|s)½Nì¬dû`Agà7ᵩluªÑO–Õ€�éN;¦h¶o&v°jì ä²Í¼4AÌ®Øùz{襁4wUÎ},¿ýC;Cw’Ù£5îBº4vÚýÜ®Âß(fÛ³U×U ~³C}²i¶;§›3ü÷Ô°óO÷¹Ø™öÇØª³6_æ$ÆÎn«™(°œÆ uvÒE¡¼_§ >s»}b9kMjí ©;éÌg¿/ž0xüâ³~yØuhàWG#ñcRñØ©¡¡� ¨ vìƒÃ`e6mÚÖŠürFV”=Ȭ* Àg´ø q9 Y«25b'rqq&ˆHH€¾‘PxÝÚu...`𡼸ÂD%ii©/^‡ 22zõÊ×ï•/T‡M7WWRW<§´¼,11aïÞ½p1×-,Ä¿wÉÄN¸š¹ …`ç|WW5¯“•d>”?¡P«œWû”pW²FÖÝWÏA¶ÞQ¶º4v²N?;YW*Û0× Ë%4ØÏŽ\%´V#vrOÍ0™uÉ…LR’ÝÃõC’>‘¾Gîâåž]vÜeO„Ø©¼Ø©§§7tèPì‘�;!�E!_¯]»&íèÑ£dÖ¥lì "í�8@õÄNÒàáÇórs¸…C‚ƒI /¯§,Ij’I›ÌI÷óoÞ¼AÐ1.6F⦈ɷ¶¶Vfì ÊÊ{ÙÖÂâ—»ÑÑ…õös JJT­¬»»½x±�I® ò¸â¤3£¿½�Yå® Ý;`èî—iEU{uÛL7OÏV�r-4ŸÞv©=_ ,æ`g�z®Q›r*0»¸\âôÝÚ«ÏjÇÄ|aŠ×®¡ÝV{•Kc§ÿ6uÍu®•ÞÎç»ô]÷8I˜ŸüdË bø­ÂN¯ÕªóÅ7qd@ßmosò…%Nz=¾3 ç—°wÀbg¡Õô^FoiñéÚN¹˜ÊvIIøþÁê{ؾ �ÖÕÙVb'óõ9×nIÇ™–B:ãâø®¿xˆÏÈ\FÇ)æyø1©xìÔÖÖÆ¹�ˆ�,vž„øxb}‡áy°›„èdc'€%™iyýº˜$¡ O<À55L´*��̉âvïÞ@xâÄñÐ�`°¡`ø’’Á,‰vîÚÂØÍ@¸°Ñee¥vv÷I =8ibBü‘#G :@&\°ò†©©–––€ÍÛ¶½~í™’”(��Ͻ½á_Ý‹‡#Á©‹ õZ{$%­÷öþPþT13“óuJÇ\•æŸÂÎÚð‰-É=#‹�^MÖÛ)1ç“EG#ɵÕéídÏ(Á�ÒØIK90ec§4 Óµ²å¢/ÎíTjìÔÕÕÅ�B9v‚!n £G’ý²±3µrnçü!1b¶"àPMØiaaNN¦‘[ÞÖöOØ¿cÇŽè¨(îj.…B6òmll nÅE>zÈ=ãË!yŸ=ó’��@×±1ÑŠd+‰l££Á^Iê{z@–•—Ë®�•5ÓÉIïKÑóu])ê“Ïš´ìüÕ�—‚Et¶×‘Úí›þLR—-/¡DÚíE=š6î¸Á£ªÒûKS;5nòÕÚûWµ%˜}KºŠ]ŠÉ›&õnÓø¨?Ô„cAã-gwiÒXm›oyäÕ¹ê-š´ì5yú°‘Æ!4ýr˰I çônѤ�æ¬sþ…â‰YÃ&šfÑtq é¡ª*Ÿ1ó(9#¦Ò\�¾QkÓ´Y{­éû<˜ÐCî†Ìá*¥:nÛEEìPõ{2�ê°ybÏÖM›·×žuè©xºfð~í™–Ì;ãùºî Ä7PòrÏ�6�TtÌR騻kÇõh-žJ�ý# îL_�8K‹¬gj0ÓG3ïëõjÖ¨Ý*× }}Ä^Ý֚ߟ ¨òpìP¯)tûµohñtÐqgÓh»ŸáVþ×´ó0C;`Ðò “ãÛ7j:âTMçšOéÍ-JqØÉç󃂂úöí‹ ¨ vr±ŒÔµk×62kr‚…"ÌY'v‚Ñ´²´„*ëÖ­;|è�µõM›;'Ož¼té"Ô% ¨p±“JÀH8ÑúõëÀøZ˜›ß¸auâĉuëÖ7‚±+(¦§¥‚½tñÒ}±î>|®aßþý)ÉÉpann®�¡qsssË“'N0 ®¿uK<6¸¼T$��pþ¨ÈHY—œì/?;Ká/:ç [ÛzF²5ðô¬gð¡P¾G®»� ÇÊ�ÛIWN•$^J $“®Îò­ô|HvÆ#»‡�Ò)1·Sš*¹§�æ@‰¹�Üù¨µA¦ôÜNé¼÷Xã´Rîõs±“Û3Ü» ói;• ;W­Z5nÜ8ì‘�;!ݹ}›ìܹsg|¥3°ÎH¶ÖÖÖÍÌÌ¢£"³²2űjŸ?'ctkÄÎà  âÕ„–ýýßBù””d÷Çnd„íÅ‹ å€wþÎÎN€Žy¹9Y™™.\ Qj333RS’÷1«­€ „ë‡MháÍk¿}ûö‘6³³2kÃN0äd2*Xn8-W�/z~IÉf°š^u…¥••�ð N ÷¨2NË:M¹SP˜m§ßsðñ|Rÿùlê=Â_ÿòy7g÷Xû¤;N¡ØÉÎíTWWGìüh±Óç¹÷/¿ü²g÷î°ÐŠ –"Qq`À;عwïÞ˜¨(Ö9éáá¾råʵk×………ÂN°kkV¯†bÞϼÈ`Úœìì»wmÁæÂ7oØ…áû˜……yy™È÷•/X«5kÖ<°»Ïb'‰: È÷ÛoÆâ*L-È :88äæð¡ÙÌŒthpU¥V¯^}ôè‘W¯|¡.œ1//×ÕÕ®Z†ª¿0õÁVÚÝ¿ÕÉ(ßäÄ€j¨kiy� x vù¡½=Àíê5k Ξ=“••¡¨×:"7w—¯ïX;;“òõ©Å�Õ)Ÿ¹�ܰ8ÎóŸRm~ÚÚ„ ¨(vêéé�3{ä#ÇÎȈ÷Û¶‰'R^ºt‰ÝY'vãýþû蘆mÛ¶1ÑÑ${/Q‘!ÁÁP‘yH¼.é¹·÷sïgp d+A›Ê֨Ʃ˜¨¿#é¡Ë²…À¯tعdÉ’áÇc�|äØ ÆïÜÙ³°Ó—³0I�Ø )33ƒgmM|•D€vA��µa'Óëââ àÇV12223»–Té~ ܹs'{€ö�ÇÏÎbv}åë{äÈ‘Í�9s,:{б“Œ³e)÷ôéSòÇÎÄüüí/_ö²¶†´Û×7$;»žËÊË¡.7åý¥¸D(êï`§&#ÄÎ�;I2¼–ë•ÞIJBbé”l²Hv’ð?ĤÂ1B�$°�Dáª*Œ³´¨¨j‘’Üó’ ‹ ËJKØÀ³œ3ŠÈ‹jªN. N]SÅRâ5%1“äÿ*ÿÚ„Z—CB <=IZïí�O ¥ì\±b®ÛùQa'�LHÜ)”$¤­ôÎÂ!ì„#Õ7…Ò(+Ìp”.ÌF ŠŠŒ x’ÃÏ–8/ itTÔÛ·oÂBC8%Ž’+�s†¥¦$K/J.Uz?´ˆ Gå��÷cbÀÚ½LK+/Gç Õ ±C }ÌØù/%‰5TêYEF-ÙGëS ÎÔ°Þ!ƒlm‰§˜S�ÇSû��š(êÃÎU«Vá Û� ;15DÛ¹×ϯ7�×ýæMH=¬­ÏáSƒBÉ;ÕÕÕµµµ;•;'Õ…�ŸWaççJ…�h:ÿm‘ÈCä¿GR’üÛ¢Pˆ�bì4006löb'ÚNeVœ@Ð߯&"'çÛ‡S…ÂuÏž�áħ…’3vsöíÛ±S ±36§pœseƧ,%RvEÆBl@ƒÏÓ£bé ÆËšÓ#ªìùð’¼(ÄÎÿvv²²ºb…؉B);-Z4zôhìÄN´�ʬÀ¬¬¥�™éäTTZꟙ¹š³’ …’vª««ã*J¨Ò⢜‚"û8áÅP¡i˜DÊ'™Ëa«áÂwY¢RþëÒ8ÓÒØK¥±—9‰³sA”|·¤ »¤¸M¤²™ÎˆÜ\Oϱvvdòõ©EÊsysÖ3þ- …ú‡±sÙ²e#FŒÀQììgŸû™uŽì´ÿƒ�®�h—Ïd%·æˆ�Jk;3 oFDø¤¦ÒŒ'³î·GYÙ`[ÛâÒÒSÓÆÙÙ� ħ…’3vâÜN¥5 "ø0/-¢ËêJ¢¢’’²’RºŽ$‚6‹Ñ>*!v²µ�ïêÊ.×YÏu;Q(”R`§¾¾>ÎíTìtK,¸[G Ê, “J“ïÕ™D…Ùh•Ðv&=­­rsÓc˜³œœbòò꬛—ðYZVfõþ½ex8dð©A¡äŒ�ÚÚÚ¸€ŠÒPLvrgi"v¢P ;—-[6nÜ8ì´šh;å¦Óæaa©B!Áί_?ˆ�ÅG…R~ììÛ·/b' ¥@u²²òHJ"´iâï›Ø'(TƒÁΕ+W"v"v"vÊS—‚ƒÏ±Ø©ëîîŽñ�P¨†€�ZZZ8È…R l¢¢5;Õx<33 ÏzV$KwBÂ>D¡†�úúúcÇŽÅA¡ä¦ì¢¢~wîÌvvÖ¼ukºƒÃ[ÛɳÔ& %ìÄ�B(”’Àg=²ÊJîòýHu�ín5ÞA³³°‰Ö…Rv®X±bäÈ‘Ø#(”<•_R‹ˆ8ùîØÎ‘H†±dS@–ª�ç’‰ö纬²‰ŒØ‰BÉ;555�<;Q(ù‹ø*¥S}°P“µ›��M´ž(”°sÍš5èíD¡”L’Ì™SܤƒåñCo¦:ÄõµÙçöÞÛ‘C;Q(9c'0'ÎíD¡"OÏS},i'«ã¹H2ˆ�(”b°ó—_~Ao' % üóÏžÖÖÒéuF†lì ÌR�㼊ÝtŠ×þåéBÄNJnØÉç󃂂€9ûõë‡Ø‰B5UÙPO]5ÞÁË!ã AfšÃ:’Ç>B¡äŠ�£F�ÂA¡ä ¡01?_:Õ9·STö¿ 6Åæ}ùòr° KÄ@ìD¡ä†�ÄÛ ØÙ·o_ÄNJQ²‰ŠRãñ¨Ë—!�µ³ƒÍzbg� û…’+vêêêŽ={…’§l££ç¹¸L´·7ðô|+éê¬;³ ›õ¹½WÅìLOëƒ_Þ8ú?Ó‹��´ãålÄNJnØ©©©‰ƒlQ(ª“•ÕÄ;þ IDATzooš‰L ™ ¨`@>JɰÓÀÀ`øðáØ#(”Üäœ�ÐÃÚújh¨c|ü1ÿŽ––Iùù²�eYù'‰ù­¥SNq4œ(”ܰ³oß¾¸€ ¥@©˜™U3–Ìžub§MÔ`5ÞAâ#d»›–…BÉ;õõõqÝNJž:ñî�)'øÞ¯ÏŸÛÅÄÈÆNHaüŽ  ±Ý1Íamõ¶h8Q(ùa'. ‚B)Pë½½§98�AÐ ' ¥ìÄTP(Šº|9"7·že¥guâ*(”"±sÅŠ8È…’§,ÂÃÇÚÙ=Š‹sOJ")¯¸¸6ìä5]ï=WC¡”;µ!v¢PŠÒ [Û]¾¾ˆ�(TƒÄN]]]Œd‹BÉS¦!!§ê4–$¥[޹¿±SfgQ˜ê™ðéû›Ø©¡¡�!…P( ˜“Y{³¾ƒlÉB�Ò ­' ¥ì400À¹�(”§»¾ ä…‰}VÚÇå¤:®ê7——Ç6ähжÿ:§¤¼\AÎÝÕæ^Ÿ[˜å·cؘs‰•_î‹ÎOþrÝSšö\ÓyèîW™¹‰ÏL&µ’öv– Þìî?èp4;©JÜ:DZ1E n=eŒßRT6E-£(K†Ö4)*–AµþÅçTiÎÎcjMbØ2”¢r d0WFQ{)j5EåRÔ•š¼�P`Eí¯Ž�  =mŠò¨©Áï)êEÅPTfÿYÆ|æS”=¨lyØüŠ¢’)J@Q˜, ¨óeÄ)s�¢æ0(ùˆ¢Ò™¤¼�å%éVó:.yPŽ�dý±S[[±“+øŽK¾Úâ—0”|deàéɆ±kg§ÌØY#F^h$O ÷\ü¥íC°SCC£_¿~ˆ�UUÕ…ïÔ¿­NVVë½½™ø´!Óê\@…$›¨Áj¼ƒP‹¬§›òôvr‘�ë«d¹”@&÷Qâú$Ùv¸þRR‹u�)ñΞ‹‹�\/¨$vÒµ¸½ª»%ŽrgZr ÓDâC¡úkd[#ó¤æ�J4Èr©N%(RR>[‰vt8etª;H 9@+5ÈV¢Ãñyü`ì\¾|ùèÑ£±G¡·%™†„¬xò$27w¦“S¼@0ÕÁ!§¸¸Nì ãw\øØ`ˆíŽikÄPv–ÙþÜ⛓¡éAÇ&vŽL:3úÛ ™UÎÚ¤Ócú®wK*�š¸Z…�‚[ß«-¸“Ç>jv‹[}},0##ää·í;Ë\V´±ß?G˜úòؤ–ì̺üuÛy7b²’½�ÈÆÎÅuŒq$~Ë`çÍêŽÇTŠÒ¢¨Äš~de±Ó�—Ëu„v¡¨çÌ8Þö %î ¨õ%¤¨«Õ±óEÍfܤ;ëÂN‰G2þXöè Æ™Y u…\ì\CQ§˜QÄdó5ãJMfŒ+ÁN¸M¦ÙŸªa瓵�‡ì|™šquN[ÄÎÂN---MMMÄNi¡·%§_:Ä‘i+þGäæ’L�ØÙÉêøzïŸHÞÀSW>ëvJ`'Ë„ÄÛÉ:9ë|ˆ¸íH”�C¬ƒT†·“{®:°S‡ãåóªtrJ ¦•6�\ìäI9NÕêçí4áì—�� êT÷‚J\s�íVŸÈÊã8Hez;%:ŸÇÆN\@¥RoŒÔçÙ`$[åQŠé·S/dÐE÷–|},â¿s['Þ½»YZV6ÍÁ6�¾}û 6V6v—~ÚÓúàqÿÉ^)j¶ÑƒúÝ1vOÒ� vY}ßsÛ+î‹roYÿ/š¨|9v港V»Ó¢çëºRÔ'Ÿ5iÙù«/‹háëóó¾êÄLöTYî\UÏe…š¡;É–†Yé�èÒò3ñÈïnÒiÖ|Õ¶IóÎ#gŽï·ÒÐ6Ò|Aÿ¶*mµ¾ß¸`ÀLk6’myäÕ¹ê-š´ì5yú°‘ÆløBñ„ɮխËƽٙ¢Æ3ÜÊ8?eïhed×ΕžÌ�¤ Õ†)3’ÙÜCQM™a«}˜á»@wÃ×âff:([+š¢4˜9Ÿ3ª‡Õõ ¨EÕÏ%Ñ aåõ d.XÀ ”%^Ó_ªÏí\W™OfF7®ümxÓælæì43Ân³;3ºøXvæºïÞ±i³öƒ¾Ÿ¤9ÿ~ä}vöë×ȱ±SÉÅÎïu!œ9ÈÖÖÀÓs½·w'+«ú`§Ä2*œMùa']éÏ„…x;%<�µyÒ¸í°s;%¦zB ¤ wd/×ÛYãˆß°“ÇñzÕä�”ð4Ò5�§Õ©Åý¨V}"¥Nu쌪6�RvJ4è%å€U«)<¯D;÷Å6h(åAå`§t‡£> ;—/_Ž ¨(‰’ÿøZ<èí“Ï[u®wÖ'K)¦\Ù/TßäûA5„V3úí ¥é—[zˆÍÿ5ë2rÅí貿pîˆ#CÆœŠ‡L±½î¨½Aÿ™úEZÚw1�MwpXõôikkëv’�¥úƒËJvÓ9Akí³Ÿ”bå1§e�¦\ˆ)(̉¶Óï9øxŒ"¾…Ô‰nE`<ŠO+gN*[*gfxÆQTEé2>L\|L)±³ÿþˆ�J(ö+5;^QÀ� EýÅÆ;e‡bJxÃ>¨Cþ«>@Møï‘”4ÍÁa¬���¬`­Õ¼�&þ:$9dl*Üz²3<¹¯—ÒYOÖ+U¿¨³ I2­h:• ;áMÿõ×_c�(ƒbŽ|$‚.-Hñ»®§ÕvºY’¼¯ÀeE±³‰«{ó»­÷ù FæS˜�«;Ìå‰Ê‹Òž l½Ìé/`ø©Ñ�F2Ù|Ëi}wÿ‡^ëHfµë̳��žII2Œ%I ‚Ö#ï••B6¯‡�Üâ3O)°3íá†1=Z5þ¼i;Í)»œ3q uÛ  MufªgOE¬™YÏ´–‰²ÛŒ™±™…Ø©¤Ø©­­�ƒl•;¹“ßäZÒ+düMì¬1´LýÅ:9¹ló_RDnîå�›:P­ÚÜN³³j¼ƒ� Ã"¨bï¢*üª‚^#e_3³žÉ°&,šNåÄN==½qãÆa�(v2*q[Õ~ü9øŸí}tV¿Ž*;èýÍçZ<@ÑRohçfÿ£¨.¿zÓovªÏã1ƒäž®î¥çHÓ¾FC'ë/þªm³V}ÿ~qÓ8Õæ-zL=$."xuzN¿v*-UG®¸[NÓ!†½tùˆN-Zö˜zê­(éì(òÐ~þ³�4v&;m™¬Þ¶yóÚ3M¼sà2læ7ælþåèínb„øš|§Ñ¦E»Á?NÕâ`g©0É}ã€nŸÓåö 35T‡oqΆ[|÷Çœ>m1§ülá½2:ñÑú =Z5ÿBcÚñÊ.R™÷g…—Ôku'‹ÿÜJ…"Q~I‰lcɦ¥zÃînßà=Ñcƒö×�gwV ìT)©!TÊ„³;q%ÇN.ï±K°>CÖ)J`Œ7¬()¶.©È†$U`“mJb £4vrGNJÀ{lõ±“ÛÙÏõîJ/ªÁEMé „ W»|}I!`N5R'++ØYìdýœ�äÉ…BIbçªU«pn§b'ÿû0õô›­½µ×9Çç ]6ôï²î)�{v'� ¡‚ŠùX¯ŒºÍ²bB‚x¬T]hOÓ>›º}ùý…7i)¶‹;© 7rŽË|·wÈ—ëÁG2pË“d¡0�· ��£ˆ4Öh?ùä³ä¬ÀCÃ:®¯¸ìh j Ç—`§ÈA_uô¾çéùywt»÷ß^ù}¬$Ãf~Çï–Ñq§Çt[t;>73ðöRµJìSf³öZSw?NgeÞÓí4Ǻ„öÛÜ{ôÑà¬4÷õZ/¤Ó¥ú½f\æä¼;4vø‘÷4ÿÊ$UÖÑ}xЀ# zgªP¸»ÒFÚÅÄ4¿ví3SS£/êƒ�åå”K‚Ö1ÿɃÇ'Z£áD¡ä��ÚÚÚˆ�ÊŒ�ºà? �p”LjëSª;É(ÏN½#ÔÇ ÓBö“\Ä­ÍÛɲ"à^@�ËfH ²•p‚‘›lEöÊ%Õ�¸ÓÿÌôN3³f ��§'Y7Å#) vÖ;9£jÅI�w­' ¥ì\½zõ˜1c°G” ;KÜVµûör®ðúÔ®+0Åwk×ifÂà}}&œ¯ZmÃo[÷™–%Õ±s¾�x;ã⤊Ö :è‘G‹›°?�~òÍ¥t1vN7g¢„>5ìüÓ}YØg2tбŠu–´]â@—„˜ëïÖª±x6ª8Ö(@æ·×dõA¶U-‰",—�êÞš©1劀μ<­}û6�·í§{šN:9²j”DÛUnâ°¥�×U~¡‹:4𫊷 Y}û2B‘¨ýõëþÏÞyÀ5uµqø {Ë”,âì©¢¸Š ‘°Ñ:p´Zg­hÝÛV[·u÷¬»*j\Tk[ÛªuàžÕ|7÷Âåz3€|­Dø?¿÷g“;Î=¹Isòðžñë¯�^½ª³~½üþ}íÚùäµiö¹Höé‰;ž[•B;øÀÚ) �vêDŸài'7ÙÈZ"orV U;Ùª¾Fž—Reª.ªQ޵“òLÊ<™w¦3Ù.¸L/¸ÌDß½­–ÎDë @hgRRRãÆ�qGÐpjh;í|±¶‹CèäÓ÷Ÿ?¾¶>ΫñÔŠݤ™GoÝ¿ömç*Jí¼>¥Qõ>ÿzöèʺ>^jµóôpOñ€CÝ¿¾ª‡3¥�ï6w«=ôLѺ!o6õðì´4ïћ™”þÙÔÕ¢Óú‚¬îá~U›/{üÑ¿Ë_ž;7ëÂæAóíÛ™�ŽÙxã†í|öÆX~ß%tË ÛϬ©¸õÌæ« áýõdJ!*’vŠD"t²ÕC¸ÙNFƸ)Jµ9@ÖñØíܬ#Su“íäeU3¥Ú³�Ü+²§hÏvrµ“©†¢0Û©ZžX–ãN¶Ì›}`¶P˜ áµkg½ Ÿ1Ëu2³;QOÆvPá´3!!ëvê ÍU Ÿ‰m5Qh⼓è�lìYÙÜÒÉ·ýäcÊéN_ž™Ù^h¯%YE¹ů+º‹í,í%�’Z {ý Ì‰zÇ~¯<ñþ‚f�¦ÐíÖ®ÄjñÊïå‡'µW1¯D�ê6ä„Bqqœ¸ÍrzqŠ£ý«Çn¡þ{oSœ—…‰còîw¯7Çy$l­Plîæ­L·þ“÷]ïFլͬ«5è¾èüs…âéÁQ�ÎææUê´‹¶S.qq_f°›�­[pZRD£Q—¨–/ý+w9šgÇÆW³0w¨Ý¾™¸ÕÒgŠ_³ý�14±tô‰uà¡âí/ë’ƒªÛÒƒ=›.¸¯Pü>¥Qa†óѲæ53Ï~üoqÞÇ.+VtܵËzéÒ3w æß Ú¼ù¼Ölgö¹ÈjßN6]<Ûã»/˜h°aÄé»îÐN>°vbl§Þj§êÊì€Lî|¡ªIBv5 MÚ©PYèBU;™á—L ìxK¶#.oä'“«d†’r žªÕNnU™xùRÞ¢ìµt˜ÙHŸùùÑ#¦Ÿ-ÂK—¸O5i'³V§ÚÑ+ø€­�˜R”W&øúfþôðÅÓ¿NŒòsî·_Í!—Ç× šq“zðø»öžéß•‹×M™ç’Ë—©™§ù/_Ž?}šú]«E;©xøÊ,ãHŒ†V 'H;}||ÐÉ”-ìšœ Íh±¨½Ú–­'e¯�èd Ê€ç'§´:š›ÚxøË¾ýYí’õÎ�h9ïžâåºØÆãåïpÏÊ‚×k;Ó hcÛZQ£ÜûÀW??RÔnÕËâŽz»¹›)=!²­w³‘»>ô".Î šþW±‡mïC�­6²©>,ç>X©vJ$L)Êî`N^Ï^U5-7;?Fؤt™¼ ª#„Õ«yÙ-âÂí ¯n2¿ÿ ¥¼hg\\\PPî@/–E»:¦øçé�•ݫՙpåÃ^ýl¦—räp1¼YÕÖ©ßaÅÛ§¿}ßË]0FþaëxszãÆSo{غû¤ÝŠ·Ï~ß,óô–‹OÖǦ�ùùùr¹Ü××W,C;%ÑN¶·¶¦?èƒvòj«‡ÚɇƟQ>zíÌÈÈ ÃhÑÎ×þ&Æ5xæŠ?gÐÙO隷ξV(îâaiHÿ!ÒkäYÅ+ù’ØúÎVÖUëõXr…³(鮤º-{DT5—N¼øîÆúÔwKGaûi'Ÿ*·çEÐEZ{5›ò“r–©?6ö p±±©Ù>¸ W;Ï�ôQÎÃlê ŽýæÚ;¾v¾ytmu¬[£ìßT |tpT˜§SG�A§o.Ý»�‹µUÕÚÝÈ‹¦µRléîÛºg`sÿi7ÞÝܘìaké h•uüõ25¯Dé1防F÷ŒŒ¬akíìß©i ®v^/¦ëh/ì´ôê[¾v¾yüËú>õ¾¸®Rà½�™î÷Q<ú‚âñ‰)mÅ–6î)Gª¿ìµÙNÀÿ£�Ü45³�3Ì΃Å[i–-‡Iœª®ÑÊ.0Ëúw2W;ÙÙ�yÌÓNÕÙ2[ظå03W±5ç¢æ>eªl�ÜWªº>­&íäÈ=‘§­ZÐ í”ÉdÐN€fí¤ûØÚTkØmn.k€o_ܘ^ý“£Š·ë;9wYy3ÿ×eª+g®º=7\˜´õ·§Ïoç$ûv\U4ûpN_iÿ>~ôäá÷]¼;~“÷èÅýŸF4 œõGá�û—7g7«Öÿ�Bq Í¥ág§î=úãǬ[ÕlçÛ'g>“Öûâ®v2ut­3ëÔs¿Àw›z8wXö[þÍ]ª÷Úò�âîÒf‚¸M¿>y~g_?iôŠ|¶ �1–õ‡¸ýøñ“§[zyµ[påá‹gÇ6ñŸú+[䟋[¸&îS(äc$>ývÜ~ô÷éE�\U³�oŸ�ÛP:îW;é>¶Ö.u:Î8ñDÁ/ðõ·í\z®ý#ÿÆ¢VžÊò/Ž«ÛpØáÛÏŸþº¢‹0i7¼SO´“rN¡Pí”D;Õj$w©X…†9�™9«ØrXÇÓ”œdñòÖ˜U+]¼„¡¦ÛL�lomfz-n¾‘«µìLZ ­ÉIÞŒ\Ô¿j³�ÜÑË •³ÔÈú-u›[.a6|PíÄL¶­Ú©Ìvñøø”µ�­Œ©@™¾Sœ^×ÁÉÒÄ¢ZÀˆ=Š#éÎEËð4˜ü3G;=”‡S\�è[tHµÁÇONÎèT×ÕÊØ€‹¾9ŠüEÍk?MÊëdû÷ŽÍU,”)Az‘ž"íTf;‹à¨¸8¶�ƒ“•‰¹«ß �÷ŠCÜŠ* ™p™£�Êå‚èF4«aÑ!UÓ~T®%*T:Ùòfcæj'O‰¹r¯ÈÛËiÉ5L6ÊžÅÝ¢àÌw¥ª…j_¾êl̼¹sA+ÞÏ!£S®ÞigZZ´PBí|»¡›uä´Ëwÿ–O ¯JyÔŸ_4�{¯¨3éŸ3%{þ|®2GT‘v>YÝÎ;ví�Ç좮›{Ú†M¹p÷î¥iM)K|»+±J“q¹ŸÝ>1%†£�÷„9túöÆý¿ŽdÖÖª�¼ïÌ ›ùWQ�nÏ%ïüã™J‹´óùú®^1˯?f—‘ÝÙÇÎÿ‹swî^žå ´Ä3#j‰2öÝ}öàüâÎ.í|´¤…]ûſܿubdC­ÚÉ+ð·,¿KÝÇÜO%þcOÝ}ùA½ÒN±X íè �¬2ÙNÕ%UµÌKÌ–Ã;†7e±¦l'÷ZÚµ“W »†�j9\xköð§Ü©Bs¶“·>­íäÈÖ™WÐSíÄ”B ¼\Ñ®Æ0Ž=)nmL�Ú›YU j\§ß>Å›£ýÝ 102³q©ÓeþÅ7Šg§gwªãLR´*ÈÒìJôV.4«äŸ++⛸Ù(Óy­¿{¡¸³%­Žƒ™¥‹_›ߤ]”Ú^[+u°rµû$¶v›•ìL¶ï®-îXËÚÌÆ«Yt#¿ÑEXÿY×Ñ}7#Ë+ðíÉ¡žÊ:šÚ8K;ÎÉ}­xyv^·z.ô`O³žÛ‹ÎÛK¯RKëõµ•²w[eR—´XòTq{F}GªÈÆmCŽ•Ëàþµ%¥‘³uåÍ÷ Ìþ“-ã—]|lͬ«G¶òk2ò Gi»»¦íçÔ‘Wàë}©®„T22³u­Ûuéå÷Žm)r¤—Ù-Ìý‚²×N‘Häëë í”V;œ1�ŒGñ2�¼L£Úrx=N¹«ÂòRòdLS�_žvªÈË@ò2�ªÚ©z-ÞËd3¨Ì˜Un!¼õi5=~¸Ó IDATig±÷�—Uz¤�2™Ìßßw  ;œ›Ï½ñüÅÃ_6Çר?õ†>ÖqO²[äÌkÏ^<úu{JÍÚãÖG³ÙÖ£jôÒߨûx}mïêÓoâ“¥ŸÚ)‘H �€ÿv„§B%_§?°Ã&5å9õ-‰V wÚÙ·oßÐÐPÜ€.ÜÙ6 ÐÓÖÔØÜQÐüÓ�wõ²ŽçdרLÕÑ¡VÄp=]3óÖÆtSc GQ‹Ñûïュ§Úéããíü+”áš™%ä£X3“;˜*}×ÎäädŒíŠÕN‘H$ �@©µ3!!c;€bµS*•Ræ íJ­�)))X·(V;}}}¡�€.Ú™žž�;h×N¡PˆN¶€.Ú‰)…€’h'åœ‰Ú ”Z;“““ýüüpGíÚI9§��´(µvöë×3ÙÅj'²�€ŽÚ™’’‚l' E;óóóår¹H$º�€.Ú)“ɰ€  E;Ùu;¡�€.Ú™��‚;h×N�@ ‹¡�@©µS&“ùûûãŽÚµS"‘…Bh'PjíŒ��G¶(V;¥R):ÙºhgzzzDDî ];)çÐN ÔÚ™˜˜†;h×N, è¨�IIIX·(V;E4ÐN ÔÚ™‘‘íŠÕN±XŒ)…µëvÅj§@ �J¥ÐN ÔÚ™’’‚T€bµS,C;]´3>>>44wЮ�>>>Û è¢� ÈvÅj'圔yB;€Rkg||<ÆvÅj§¯¯/PtÑΤ¤$¬Û «�”sŠÅbh'PjíLKK Ç4ig~~¾\. …�Ú ”Z;CBBpGMÚÉN)„N¶€.ÚÙ¯_?L)«�b±S ºhgzzzpp0î ];±€  £v¦¥¥âŽÚµ“rN___h'PjíLLLD¶(V;Åb1´ÐE;e2¦ŠÕN ´(µv&''àŽÚµS"‘`J!@í”Édaaa¸#€víôõõ‹ÅÐN ÔÚ™––íJ¢�Èvºh§L& Å´k'Pt×NŒíŠÕN ´(µvbÝN $Ú) ¡�€.ÚÙ¿¬Û ”D;% ´(µvb $Ú)¡�v¥ÖÎ>}ú`J!@‹væççËåòZµjaJ!@í”Éd~~~¸#€&íd²�”sb@íŒ��G'[ð� ;¹vúúúŠÅbh'PjíLNN ÃÐNh' XíD'[@íLII�vh'´P¬vRΉT]´3..ëvh'´P¬vRΉN¶­'ZO€.Ú™’’‚)…N4œ€bµ“rNL)Ðz¢õè¢�AAA¸#ÿ;­-ãrÔì8ÒߟËÚW%,fC¬IÕ´ýÿb½rdvÖq[ËæžäÝ´æ§gŠo8_‘.ÆdÀ1][£K¤® ™}»ìÛÅ›Ùİ1¹††€r¬��ëvþ‹¬ZµŠõ_ŽÔvjo Ë‰ŠŠJMMý+–••E•YV·ÅÛÛ›ÿÚÕµ)«}÷tm¶²HV‰Ò«ô&ÞTeÐzPþµ3==ÙÎ÷‘* ªwÛðNÓ[{›TŠÙ¨fÇþ${ÒvÅ‹’6À‰Mßœñ•lìNÕk]ÙÜÅ»óBˆë cÅkç Ò–�¤ýº6Wg‰!So*ÿ¶‰ Ì&÷ʨá¼1‰�Úä2NʱvRÎ)•J¡�Z|IµõÔâoÐN}ÐÎëäºòn“UÐNÀ«�2™ ÙÎ÷y}ûòO§”ìø¤6±ï2Ÿ~|êÜïOÐN½ÖÎ=‰Ä´‘ÿ�âŠhR=Ú @…×N‘Häëë íü·<ÚY¾µ“)™úÚ ´S©� ÁÁÁ¸#ê¸5=€8õÛ_ü�ÐN½ÐÎÿ.E§ h'^;�P(„vB;Ë“vþwí¼§�IIIþþþ¸#%ÓÎüóR›Kœ-LÌ<v›yâI‘v¶�´j@dM;¯¦ÃvüõVE;ÿ¹u`J¬_-' K×ú½fžÌW«�]¿\–æicbbW3zÌÞ¿ úõ¾ùc÷Ä.õܬMLm« Cãg�¸Çì�&²é=sy/©�±™ïø\e �OïÑØËÞÊÎ+¨ïüÑ­‹´óÕ¯ÛF¶oPÃÞÜĦzPÊ×—ž1›Lwqï·`NG�µ‘EÐÌëÜê¼ùõ‡q±þ^•ÍL­«úD¦¯û…yMW&J-ºÎÚ4»ul¬Ÿ©y×õÏ•›/Œö!ÄÐ%bÄŠÝû·NkS�ØvßHï¸93ØÄħûW[öí[7¹SMS¢F;·õ ¶È/OùÙNbMúÌ&v“ÔÚÄ pbžU½ˆl!¹žOž\'2/R}}ü?dD-bÕ˜,É!»¾!Í\”m£�g3 !·èséJ’ˆd÷~2­ õî��Ï•Û�¤³räyt•ts!Í–òÿªúc1‰$—kÎvž#BBž(Ú嘤ôg®vîŠ'¦õÉæ+äi>ÙœDŒ$$÷-NÊ…vb´“ò+Ö¾ïbL�Ñ6Vð¨ìa¬vr�WhÈ‚2ŽÇlg쎱SÞ¹ÜòÍcw15až2gqkÅ>fœ�­‰ZádNaK¦Žgn+ÞÌvj#[·bÔv5¯‘v3^?XÆëX7£` “ÕEnZVSY½Ô¤�¬ÁRpËgä�)“)‡'º¼�¼l'u:[2·®v²RÍõR@9ÑÎþýûGDDàŽ”H;ßû%²1ÆÐ¤÷ÖBí¬Ôfea_ÚG+Ú‘ÈÅßÓÎCÖŠ͹S �âOsÿ)ØófUGCãèe ž¾^ÝɘÑÎwÛûXµ[ý¼`ÇË• ª¦ýX(Æ~_þú®˜[qcR}R{üåBíôyö ³ãòøÚD4ê’Ò:§7&I» «ÿÇ uÙNJê Edy.yó¾vv\Wðôõ*bhL¾Wéc³?‰Pǽ #„Œ¾\°ýÝqRCƒvúŒ,¼ÊeÊɨKÊÇT%ý¾*8ws7b—Ê¿ÐùO‰�'™œ¼ÒÜÉ6»qL¦Sµ7I�A�‚iç ÒÞ€Äí(<þoNȧr4œ” íD'[´“«U¼N¶Œï1"Ç“JÃÃd5µ_Ž=†·�[>“ÒÔT,›íd\‘uK6c©(q7`¶dî¹¼×Î+Jm¶“k•j;ÙR{Õvše¥Žw€–l§ª(òRÕ�yƨV;¹ªI�Î*([CîÆ÷^  |h§L&C¶³ÄÚ™tNJ‹ºÕ­Œé?YV꾑ÕNÎØÎcƒ\I�É׸ÚùnMçJ¼Þ)¦íVXmdÐfåË÷¤îp†+iºà‘ÖëQÚiÕcSAQqgVˆ�iì÷ïÿ¬élT)|îÝÂÞnèfEš.y¨ª�Ï—E“¶¬Í¾\Ù¡ ÛùvcwcÓn›ÿQ½¦fí¼1­¡AÕŽóOÝ|ôê--´ÅhçÏ“j“*ýö^ã÷ìFš§zIö $F•Éo5kç;’îDÄiäÒòæ9ܯP;÷'BÆÿ\˜íÊéålÚ ÿ†ó·ž¼)¡vžéE<“¶ÿõìñŸ'W¤Õ³*N;—ÇH‰yÝä%9‡ö­�ÜEdmh I;ß½"粈…ÙýN³v¾&ÝM‰ßòà9¹¾—tªQ¨�/IrUbNVíQŽíl^]™ü,¹v¦;’˜5ïu Õ¤�Wæ{s²î¥òé–XB$dÍA²õxÁSˆa âfDæÿ­f&Û•mˆu0ùá2yöNÊ…v2ÙNÊ9´³´Úɤ:¹ ÊÕNµ£(yǨ?îåxc;¹çrÇv²åó´“{^­ØÁ™%ÔNn˜ÃŠÕNnŸ¯E­v²º¨I;™íŒÔ1šÇ�‚ˆÙÎZ¬v2•p[ÖoÕŽ¥Ê¡‚ÛÝ—7jT�ÓʇvR_¦X·SwçFz *곪xyaQ¯ÆnÖf–N¨Á3úÕ3ï³�Þ¾Cfã;`iv·:Næfvž�b§�(˜-èHã.k™4苟7Žl×À«ŠÓe•„ξŻކn&M†}3±£ÔÑÌÌÁË/nΙÇ{Ÿ[–ÞÌ×ÃÎÌØ¢ª¨Uæúkž(ÿLdÔláÝ¢"Þþ•3¼Y [cëê!ißmÓТ×ffÏ£óËûGÕö°7c:¬uZM�Âü1�ªášWê^þ½Ý£[Š-ÌlÝv›:®³CÝ ôØÎË¥†As ëþ`I ÃZ#r™Û“÷]b“j–Æ&ö‚èQ;V§¸x�8Ík¥‡{ÒW7&.uȸ}tsòŠt1&é?¶.ˆ³1YCÛÚµ•$È“˜Y�šadö bÔž<£�y|Št•3#RµÉ>D:“™·è™l?{o&Û 9…e> - Ɉ\å㓃‰òKR͇tOn¿ß¼e1d�ˆ“„dn#ïè�¯N“èšÄĘÔû¢ðÈ\"&ĬyTxâÍlbX8’â>™ŸHÄnÄ”y¯mÉ4œ”íD'[Ý´SÁé+ËL¥Ãj!£ª3ÍrÕ‹;Õ­¦)…˜É~Tg»åvîåöÎåi§¢p.æ0꼃U§ÃÕ’íäV˜—8U«� …‚ûê˜ ¼§²ïÏ"[l'[ÊúØÉ`¹ÝY¹Û©ÓK•ídÏU›ðäN~ËTö,V>™ž´ÜÓ¹S ñ^)¦ \i'PRÆñ'‰4&±ß“ׯÉó'ä—½ÊA˜ýëTÔ=ÒÒ˜tÝTª³¹vŠÅbh'¨€­§ÆÉ~t*ªÔçʇvÊd²°°0ÜPN9‘Òw#¹÷‚¼zBÎ,%Õ ÉäŸu)êÖLRɆl|‰†€Š¤�µjÕ‚v‚ ØzrS©¼¥PJ¼™�ÐzP�´S ©ùzà?ÿgð±k§H$ÂØN´ž­'@íìÓ§Æv‚ ò÷Ú2ÀG®�‰„2Oh'PjíLII ÁÐNh'@»vŠÅbd;]´355Ú «�h'PjíD'[ $Úéãã# ¡�@©µ ¨%ÑN‰D‚l' ‹v¦¤¤ãŽÚµ“rN±X íJ­�©©©¸#€ví¤œëvºh'Æv%ÑN¡PítÑθ¸8d;€bµS `J!@íLHHðóóÃ4ig~~¾\.‹Å>>>ÐN ÔÚŠ;hÒN&Ûéëë‹)…]´3999<<wЮ�”sb@íìÓ§²�@±Úéãã#‰ �€.Úéïï_‘ïÅ¡K‹½ |Jzû±¬€Ú)¤�v*¿”..B DÉÚIÒÒÒ¡��€vj×NÌd«'æ© ?aZ‚llÏnΩGÚ™‘‘QÁ×í,ã_oTÛ‰_± A6µg·íÔíÄ”BÐN´�€vê¢�IIIèd íD@;hg±ÚI9§D"�vB;ÐNÚ©ËØÎ >¥´íD  �%ÑÎZµjA;¡�h'íÔE;e2Y@@´Ú‰€v"ÐÎb³�>>>ÐNh'Ú‰@@;K­� !!!ÐNh'Ú‰@@;µk§T*Å”BÐN´�€vê¢�©©©aaaÐNh'Ú‰@@;µk§D"ÐNh'Ú‰@@;¡�ÐN´�€vþ'Ú‰T �h'íD'[h'Ú‰@@;ÿ[íÄ*ÐN´�€vê˜íôóóƒv–/íœÔ¯Ž‘‘W«¥¥>ñ«¡Æ†Žá³ÔïÕËÛа^×õÿrm³>ñ37qÈ:»hÇÔú¦Ff­çãg}Ùi牔ðÊFVa}¶”ðøí]ÄFUº:€÷ ÚY´S •o휽b!$eP§÷Äϧ&Q{©ÿÝÚ2Å2m(!Pñ²ÖÎÕ;¿ Þê½ÐáD@;+„v¦§§A;õC;'¦Õ1&ÄÀ¤j�‘{ŠŽY>ÜÇÚ�Úl=o®¦r~ȪgbdÑ~ ót\oBœ"畺>Ùbî?UÝÞ+"©5p—|DWCCǘ{Ù�ß·­nl$ÉœXðty¤½‘±ßølu�œÑÈÔØ1pêÙEÛlj©¯àðé%«íÑ>ML z ®XÚ¹¦¥K%Bchfë �È\;ãß¼èñ¤C«ˆxÍÚ9m°¿‘¡KÓ¹ÌÓ­�fFN=C;¡�å[;óóóår¹@ ‰DÐN}ÓNÆú(ª¹Wán§ž2ÛµÅóŸj§&'d«M=`7¶ïÆÝœKùÌ¿¨�Ô)ÿ…ë¿v²7œEí�ýOµ“ûyƒvB;+b¶ÓßßÚ©Ú™Ù�;×ƤzÊç…R7¬ƒq¸Q>ÚhÜWšÊÙò¹�Ó¦³þ;í\�cMH�þ{ø»ö/ «Ll]Ý�‰E£/În_ÕÌ‘Ï~c ž.4#Äwøí(�vŒõ%Ĭ]zÅÒNúN*oìùÙ߯ëåiDŒ]{®šýk5¥Ÿ”«ÀlØ´³Bf;ÑÉVoµ“ùAÏ w£»ø¯µ“QJ&UwQ™«s÷–\;Kb¶ÐNÕ·‰½ÛÌMã:ÿîç í¬ˆc;ƒƒƒ¡�z¤�âqÁ†Ä-j1½qï R¥×ÿ"íüiÌð�‡£©¹CÕº1}WœY´kJ=CöowžÑËít¬ß+AâfkVÙ»Nêê™tùóVÏnéçembljïé5bøVúûö¤vjìdciíÕ´YS�íÌ͈²!•uߤ¢£ÓºX«€Q£E„˜DÌ ýgKÛêl}*I?[ÒÔž}jÚd²|ú`Cã°VéަÆÞ{¦g1={í¬Óº‘‡¹‘©µ°[Êz9u‰1.† c¿§S£iu +Uo¹äTß&•ØB]e[©]3æ�ð“xX™[U®Ô|Òžùå\;™ôÚ.„6éA¿/êîÀ�݃]í,LìÄ�£w. ³ÄÃûE{:Û˜Xzø&®˜qHžiR©vŸ.QÞæFž-—ÈÓ›YšG§ì»´hj' ÃÚÑ£‡4ô²76±uŽ3fÏ¥Cü ØûîóÉ„C{c¥Æ†n‰#•%Ÿ3¼«�‹­‘‰•] ÿð϶ІMí �;|Ú1¤¦•©…­ mÜ·ç mÐÎ�U;E"‘D"�vê§v26źe”Wpµ�›ìbdƒM‡26Â-‡—;eÊá%'Ù#WT«�Ì1j݆9…kŒL9ìÕ¹¥s«Ç­-[=¶>ŒRr“iìÁì1Ü<0{ÔêqyÕNæ-`ßeî�áþÉ€—0ç} ØÛΔÃf;™¿)°e2ϛ¦Ïsó>ª&óÐÎ�O;e2Y@@´SŸ´³ã¢ñCR9fåÂC—æŒ�6'-—ÍeµsδΕ‰¹O¿�3¶|×ÂË€ÔH}Pm¶“Xùöù¶ý²[b í¼–ÒËÕÍ«R½{Ɔã_NïãnHÌógº4mP#b!ì¿%{ù(_k¢^;w}ÕÈœ6Ví%{n`K[b‘°kW§Z”T6KÜY|¶óËþõhYì2`ÓÑ;äôSú¢Œv¿ {¦Í•¹W"F�“©ik«¼-kè~È2Aa"÷ýlç–)õ,ˆUÈĉÛ¤·t!Æ b×Wí»/-šØÚˆTî¾üð˜ÔÆ&ÄÀ3m·J¶3§#õY³‹rèÒÌñÑVÄÌ[¶:{û®”f” W‹˜›»èÐ’KB*¹û�ßýÕŠ!µ ‰Q𔯠mÐÎ�W;1“­>k'õÃ�ýiΕ:Ê…`~â3vǸ¨j¶“yÊ�É=žµD®30…¨ÕNj—Z[`N§þe.Äz©–l'[=îS®v2Bž"µÚ©šídošÎÉÞ�W;ÙÛË}Ø·Œ{g¸o4c¤Œ^r=Ÿ§�ìcnZUSv]ËgŒ©Ã4Bíü Ú™””Ô¤Ih§^içš#½êW"±CœL 4%n‰£±Ú)O‹4&á}÷(�Ó«!’Žkµu²�1¨ ¥ƒ~Y—MëbAˆGê.ú˜ý])Ã3ŒJ>p*¾1õ iâFê„jµsÖ¨HbR{Ô ~ý÷ΰ"¤Þ¨i‡.}Ö�’Z“:ŸŸ*™v 3�,*zÊÑ΂¤îN¥ÇG§,‘v~5Ä_éBÓ.*ËœCUJ2ò§Š �ããkR÷Ìwäiµw`áäv¦„8„ øÝ©…tª³u³�š'�ÂeæØcÏå¦15 ›‘c4Cµn±ÚÉ©ª�¼üjI´“kã¼WZ¡´“û Öy½pÕö—æ%!yÚÉý¼ñĵ$Ÿ1MH´ó#Îvb½ÓÎKÙ�ÊA£G‹ I•^iç¡îÒ÷ÄJ;¯Ò¦�³2•QÜ¥Ù™AJ?ùô “¥¤í¢q÷m´6ØÄ|¢mlç±øÆÆÄ<*y¿þó¾hmN}å&þ@=¦kÄ0pòW%Ò΢«ðµ³`lç©>�¨'Aq;~+Gˆ IDATK¤�´�s1ñy´"hçÐvvÔ]ð›¢áìß™ÔJh¡ìkåÞiÞ—Lÿg:9ù¾vÒž©N;}3˜Ù¸’²WíD�Ú¹½=u}ë.ÌiÑê!žÔ‡¡ùì÷µs[;j«cÏLh´óãÕN h§Þj'+¬E°^Áí¿ÊÍPi×Nê,žl°JÀõ MJÀ=Fµ‡-[&·Åj'{íÚIÕ°$Ú©:ËNÔN^÷W6¥ÌídK�«ö]æÝ1Ý´SÓgLíþíüˆµ355Ú©Ú¹èû‘Ê.«VÖÄ£…rV;Ï'‡T" {y“ñ(“QÚµsÑmL(aIcæÚ#dœíǵél']à„5Ú¹ésa%Ê:ç¨Ì£{6-‚÷EMŒ‚ã¶+'\�râÚÑâ`‹kg“ ôHÔœ^„XvpèÒˆÎU)ãi÷µñâgÝÜ _]óBíÌЀ²ç égJ!flçú®”`†Æo×væ¬]Ò®® !Þ­WíY—:¾¥rèfÉ´Ó=y݇y4¥ûÄÿ‹Ù‡.MM¯«N;é<ªa¤ŒN›/šgO˜¿˜@;¡�å«“-PÑgíä�ã>åe;K2¥÷W¾ÎÙNMÇŽÖSÕàK;yšTÂlg…šRˆûq³�šæ§Õ”í,¡vjÿ3G ³�ÐNhçGŸí …vê›vÚוæXØ#±hlgö¨sb^3qõ´Ý‹Ê98_i#âAwœœµOƒvþ0«‰ 1ðŠ´ñøÔ/{V3 ö�VP&91IDY„dÐÙß|¦vlçÔþõ HåÀl•™`vÍhdÆ…Ò Gtv¦¼S4ìè¢Cb©6ЪUÊö3•9Ò­íªSÒuU½ÝÅi§�[À{¾œÑÓ…* z>å9ÙCü ‰©pàΈ ÌIáK»�ÞÔ”ze]ÖŸš¹ãü¢Õ#jQ2ôùçÛs+„vÌ�±fUŸhobê!Û0O™`Tsf�èT;}ù”?¦5w$•Ænº4)­Ž!±Ü2cË÷qázm-F;IåðÞßþ,NjDLÄÃ+SÜc£Œˆ�ÏÀý³rNÍçŒíœ2°¡±ðé·>{{Ž,¼*1”tøî"´ÚY®´“rNÌd«çÚÉNâÂ&G=Öd†\aÓô+Ÿë~%Û©e0ÛÖ{]Æ:xSiÉ©;¶“«IÜò*¦iôi…šÉVíÌOÜqÂ̻ýWìØN-ÚÉÛÉûü¨}µ|Æ �ÐÎò �)))˜RHo´óÓnnì”­YC—„�ô®%aÖF!Sf) ópfJ«.ö&ÌL®.}éIDÏ�^ÕÊØÐÌ·ÃòK_$ûVRN÷JgºFE˜)Çvº4cžr¦Sks3‹ª>’®ÙÓØ½¥w„·¥±±Eõ¦�?Mð¨T9d·’;;×"Ä¡û•…~ÙÉÂÀ¤æ@Î’* � ˆQè4Êg|ÙÇÛÁÜÐȶÑ8åhÏ©ã»T¯lfhâò¥<{°Ÿ¡�}èW…«¶°OwL®ch$ê3.\âdbfïìŸ6dã·bCjZ›[Û‰Ûuÿ¤³}%�ô4¿ó��V³62´ðJû�ºYYÉukºXš03­J;¯+—Ú¹º…3ó Íí«ˆ"›�Ü0³0o©ælYÖ¶‰§…‰©¹K½À‘[”vºwgjg¿ªvÆ6^µ“VL?t¡_„‰�ITáhOÎSZ;Ýšö–ºR{øt™5y?ó�Y×¥‘»¹‘‘iÝ‘íé*14p‰¡œyßÀ¸H÷ª¶&fvU„ÍÛN?@Oœ»"ÒÞÀ°þ¨©ôg©‹ÐÐÀ-ñSh´ó#ÕNÊ91¶Sϵ“q-®ð’N¼5Ù^¦ÔéZ~ås;aòì‘�k”§�<·Ôžå&Á¸�]Ù~žªŸò¤… ¶>}úC;¡�h'íÔ®��@,C;¡�h'íÔ%Û‰T �h'í,V;)çôññ�vB;ÐNÚYjíÌÈÈ �vB;ÐNÚ©];)çD'[h'Ú‰@@;uÑδ´4d;¡�h'í,I¶�l¡�h'íÔE;e2²�ÐN´�€v«�"‘H"‘@;¡�h'íÔE;1¥´íD  �%Év¢“-´íD  �:jg@@´Ú‰€v"ÐNíÚ‰u;¡�h'íÔ];CCC¡�ÐN´�€vj×N_h'´íD  �¥ÖÎŒŒŒˆˆh'´íD  �Åj§P(„vB;ÐNÚ©K¶3,, Ú íD@;h§ví‰DÈvB;ÐNÚ©£vbh'Ú‰@@;‹ÕN‰D‚l'´íD  �ºhgZZZPP´Ú‰€v"ÐÎb³�˜RÚ‰€v"ÐNh'´íD  �èd íD@;¡�h§žigBB:ÙB;ÐNÚ©E;óóóår9åœb±Ú íD@;hg©µ³wïÞ!!!ù^°¿ìQ’¨°ÙN¡PˆN¶ÐN´�€vê¢�‰‰‰<Û‰_�í,‰vúúúúøø@; ZÏÂ_ö�ÐÐN¥vÆÅÅ@;í,V;1¶Ú‰@ ÐN]´399¹‚w²-ëv›à€bÿ&’€>Q‘µ�l¡�íÔE;“’’BCCñûÚ  �@;µk§X,†v¢éÑßD® @;•ÚÙ§OŸ ¾€ ÚNíÚYí˜ÉM'€víÔE;ûõëWÁ§BÛ  �@;K¢�”sRæ íDÓ  �@;K­�  Ã7ÚNíÚ©];}||$ ´M'€ví,µv¦§§Wð™liÞÞÞ?±y5‡¤]h;´h§zí”Ò@;¡�Ú ´³ÔÚ�™lG>kÚkáø¶ö}sʶí<˜âH( Í«[fnºþ²œÜÞǹóúÕt°´©>xËÍwh¡�Úùqj§H$‚vª4b$++ Ú  �@;‹Ñ΄„„àààŠþýDý¦P(6v/síÌémß}«âÝëük9ã#�j~rüÕ®ÍÅQ’èåÏþÝ2ŸlîéR«×òܿߗ¯O‹Ÿñ³N¥ü³6¦fæIh'ÐÎ2ÔN1 ´“뜫V­*sí¤ê@ ‰ŠŠ*7·÷ðáÃÌ‹òöö†IB;´ó£×Τ¤$h'ƒÞh'Ãí�ýöS¾u}URPu; k—z± Î) ÷ŽŽªUÙ˜j‰š.yørE[Ñè ÊîÌ‹ô›ú‹âÙòVu»$7­nmá2bÞÔ±­…CƒþÛï)=Ýܘìaké h•uüµamŒ¸Sj›ZvVUê§l½ývmC¦… ›s‡­ÓÉÌÀ6iñõ«XT®ÑlâÑÇ Åù‘>Ô!¦âØo®½S(îíÌ p·dÎSUy|bJ[±ƒ¥�{@ÊÆßß)^­ngÙaíSîk~sõ[™¿Ge k׆=_|M½¢E‘ 'ý¢ÜõûÔ&aóþV(~èëÛ.¹ƒØÁÊ^ÚgíïŠKãDLÍSB;�NÕ¬ñ ½%:ôêŠ ’7â–A;K¡�R© ¨ðÐíLMMeSÚÉ>þ�~ø¯ëîõë×IáË\E£[9”²REA;€v–½v&''‡‡‡Ã9õO;?ÄÛtXùêþò6®-g]xðôÁ…ùÑUÃÞQœûL$î¿ûö+¦³ê‹eÑ^#Ï*ÝšRÒÏŠ'Ëš›×¼íƽ‹_46t�Yœû÷Ÿkc#=P¼ÜÒ˫݂+_<8;¶‰ÿÔ_©–¬£‰$yíÕ{·Öõp�}[¡¸0Ò§ù²'ïUðØ IÊÖk�žþ²´½cøÜ»›ß>9ó™´Þ¿(^ÛÎ¥çÚ?òo,j噸O¡¸8®nÃa‡o?úëŠ.¤Ýï®gÕõÊ<Ë-ñîÒfÕZ/¼˜ÿôþ¹QN-—ÞSÜ�R{<�½1©~À¬[ ÅÖÞVµz+¿{;'É£ÁdªªoVuôt ÙNg&K‡íýÏc×`iØä3ÿw1òUéuL-~ºç?­íѱþÂômÔƒÕ}}ÚÏ9¯ýà++{׉[u í,�vbJ!=×Nz'aµ��ñ.ªžÌSê÷&—Hm¤Ô‘9€MŸ2²Çf·d$“-JõBl±l®’-„=Œ-„ÙÂ^”©X�ªCr+FÉÖ�9‹:€)–¹(·žÐNP,%<266våÊ•¸cÐÎÒigFF´S_³�.�(N ñl¹ô1½åùÒ–îCO¾[ÓÙ)í{Ê˯[Õqæ}í�¾LÿùµŸSÚÍ©�ëO½¡¸žÕ�máHÕ´•Úé‘yJyÄßsÃëL¸¢I;cÖ1º8/Ä{X®âï#š ªX(Ó›>#/(¨Jœ,M,ÝBFí§*º¡›{‘JQ‹Qõ±ˆÙȦzd s›å¯é‡6«EÕþÞüP鸼÷µÓC™ê¥^ôò–Ê‹B; Y/s‹þŠ~ôeXƒwÿç‚‘3P4á§ÿ»˜� n~c�åž9#ÿ/+{frhõ¤ïµrlL£:™˜Ç—æ·öè¹ í,±vŠÅbh§žk'c\Ô¿”Œ1{)û¢¼ŽÛUU“v* »ìrs˜lÂ�ÑØK:pß�‚lç» ÝlšÍþåÉ}ùòØêÚµóùú®^1˯?~ÃNêÃjçÝyµÇS']-®=æÂÃgÏßrµSšöõ‡�®Î‹®Òriþýa�¾½qÿ¯#™µ•Úù[–_‹¥ŠŽÏýTâ?öÔÝ—ì–¿6³•$­»xïɃ˛õžráæL—Žßä=zöàÂÌnW=T¼ü®µm«e<¹{vQ{ç÷µóÅŠh:Yúv]Œm—U÷?{Y¡NžvæN -øœ†dåRÚéÞ1ÔÝÆÒ©^âŠÓJ‹ÊÉjïëdié(l5~;GïVö…vm%´³´ñŽÎRæG÷óõ{R¹kkŠwÓéòÿ(£�>‘êV1·tn˜ü�ò¤“+†Ô¨la]­qüBåkâ}Û„8›{&¯/¼âùÍãÚúVµ2·v­3}ï•s“ êí¿¦0û™%mZÝÚÂ^Ø~êž«y—gF+;ŽZT­Ÿüõ™¼¼Ë;'Gûؙпˆœ*¿zhN�†®Öv^aC×QûÏ®ÌhâªüëGÃ1ÇÙWºª‡£ÿÄÓ´ž'z¶™“—wnJ˜(ºG°»µ¥C�ø¥§òVv·gjâ•ñuÔ¶”u>= ‹„v¯�ùùùr¹\ …BhçG‘íd%“Ü3ÔÏÙʦzD¿®õCæÞQö0öîˆÎæ®lã3â<õà÷åíÝÌL½‡U¨i…Ôd;ׯ;5Ÿ^˜í´�Êî;upb¸MÓ/ÏçŸèÕaîþ³ç�-ï!�Z”¡\ÑÕ¾ZôÄMÇ�¯ìUëÿö¼¼Ýƒ…�FÓ²¶9Ñ=tê…¼ S#M…=çí=‘“ÙÀ¨jÓñ›Ž™ÓÞ‘>†ÒN÷vÙ9?å˜ÑÖI2xoÞ&™w½�ë�Ÿ?»w\XÍøµ”Õö®âÙeÁ�3E™Ì}#¤n­gì>“{|ý€v­ç\ êÝÛ©ùLŽ·PW´ ²îHî©õ©"û˜å…ã-/ŸíØrΕ¼íý}¤ý6ž8¾^&’ŽØŸ'ŸßÞ½éø§/œÚœQ§Þ°ýT�n�Fí»ø~¯Ù™Í-Ú/.¸O®ÑÔõÎN2ÅÏÛsòÐÔ¦•ƒ³Îæå]_2x_Á §Æø¹&n€EB;K‘í¤ÌÚÉ5+=ÑÎT®n1{yÙNVÙíZ´“7£Oɵ“›íd=�yÌÓ`µWaï*õ˜yL ªufï´S“vRÊÄ~V©ÇA4ÌS怡C‡2OyIEj;£¬\we¼‹:ž-Š’Lö#rì.ÆèؘT!õ”9€{Q¶&Ì…˜«³%¨^‘M‡²§p_,³…W öxêHU�dl™¹KÜÚ²e2…P{¹§hgñÚ9`ÀL)¤7Ú©�u²Uÿ‹l[�ªÑK{þâáõµ½«L¿‰·ôjgA'Û]ƒÄ�GÍ[Ÿà\ô °Ö'9ít-�±¯Z:Å­ÊËÛ3DØpO;k¤læè­gÂ�»¹�l/Moj×í[ù´ð¢«FM—SÚéªL,rXرrûùÌý™Riæ^µÚYpEå®à¬Ü‹9“;Õvµ¦Ó›Ê*�™Ö¼Š½�‰‰­wË ;/çí*-º¬c�UyWüz`Ëú¾þ1_l¿Àj猦æíñ´³f:}'fJéÄæ{Úyòó&ÕŠé”  �í …Û©‡M'w&[Öå¸c)™-ܱ�l²‘�©];¹E¦ái'kàŒ2¾ÇÎŽeó™ÔFæBìÀKnç^îàOv ï*L5xejÒNj;ÆvªÍv2¦7”†›Ç£pmŠkY+iÔj'«”Ü ÅÒp·°–Ë”F�ÂËRÙ-lgWö,V;™byéGV©Xe®Â;^m¢’½ «�¬—2÷„w9 ï„v–Z;í„vjáäPïØïµì¿µ1ÝßÃÆÔØÂQÔbôþûxGÿcíÜ�èZwÈÞÓçÎ_-ÒÎ݃%�FÉ;:®±w·¥‡Ï]æ;B‘vÎŒvê½2/ïØç ÛÌ9vöÈúá6Åk§Gûé9§Î˜Ñ¶j½‡òÖÅy6¾å„ü Û‡WE;sx»)ó®çޝØ jçÅrµÚi4lý‘3ÇW&øTíµ&o}gAâš#'÷|ÙÂ^Y¥¥1Â^+Y¡Ì;?+Ú=zÊÞ3—Þ›èêñ¬H»öóÙmßv³+èd«¢�‡†Õ®=òP^Þñ±�\bWœÎ=w‘édKoÐÎg;k×® íDÓY²:j«$·s2ÖG)ídÓ�”¤Q�¹©{®MqeL“v2Ëwdò„¼N¶Œ¹q¯’MÃ!ɰdJÖ¤�ì1LØ$*#ŠìËakȽ.SU6ªE;¹ í„vþ ÚÙ¯_?êÄo(´�àãÐÎÜoz{[[GÏÎ=3>Pœ¹· £è7æX^ž|óØVb'ºÿµi»…œ±�Ý=:-£Íi]�]ytn7±�…�gh÷æÞÙʱ�Íj¦m¥ÇD&x´žÃL½ÓP¢\‰dç z ¢ÛÖ²1µv ´F9¥íѥɞ•鴤Ϡ�ʱ�…¹ÍBN}; ij²¹¹�gPÒâ#t¹ñÕZÎy_;„×s0µpªÓc.e~¹ß¥5¨bff/lêïÕ”ªÒ�ãM146·so”¸äxÞ•=Ù1õ]¬é.â!_äæf…Ð#A]$}}Š-õ§ñ�îLör½Œ~禄 ìTn8<¢^½Q?R¦º}x#{c³:#)¥–ωvîº í,±vJ$©T íDÓùÿk'w0gEKEê¡v²¢¨=H�ÂU/f—vídw±=fy’ÉÓNµ•,6ÛÉóCžrU–Wwè©j'[íÚ‰N¶ÐÎRk§L& Ã7ÚNðqhgy�ÓÉV-‡‡×©÷Ý© çŽmâg×~A ‹=<º‘P9zµD\\ØÑµÝ<9$ÚYbíèd‹¦|¤ÚÉŽNd´�«�¼” í䎓,I¶“=˜14^ZRU;¹/Ù]ªÚÉðÉVží ËÍ©rë̸¢ÚWÊÞMÚÉ4U+ÃÚY¼v&''â m'€v~HŠò«”póèæµÌ��-�¥í&m/±þ4½icvè¦v®~ÝÍ7~Í%8$´³äÚI9'PAÓ >^í,p;ÙjZ†±ÄR-s¢))ŠT �ÿšv&$$`l'ÚNíÚY¬vJ¥R‘HíDÓ  �ú¬�ÜÌm©úÁ–|M ì„vê¢�iii¡¡¡ø†BÛ  �@;‹ÕN, ‚¦@;€vê¢�©©©ÐN´�Ú ´³XíÒ@;Ñth'ÐÎRk':Ù¢íÐN �%ÑN�@ ‹¡�h:´hg©µ3%%S ¡íÐN �Åj§D"A¶M'€víÔE;û÷ï�N¶h;´hg±Ú)|}}¡�h:´hg©µ399ÙßßßPh;´h§ví‹ÅR©Ú‰¦@;€v–Z;1¶m'€ví,‰v …Bh'šNíÚ©‹vÊd²   |C¡íÐN �š´3??_.—‹h �h:´hg©µ3!!S ¡íÐN �Z´“]·3Ù¢éÐN �ºhgrrrxx8¾¡Ðvh'ÐÎbµS ¡éÐN �ºhçÀ�###ñ …¶@;€vj×NÊ9´M'€ví,µvÆÇLJ„„à—=¡ÏÏÐN}ÐN¡Pˆl'´@;€vê¢� ~~~ÐNÚ ´S»vŠÅbÊ<¡�¼_ö„Þl@;õH;SSS+øL¶P´hgI´S @;¡�h'ÐN]´3-- 3Ù¢§Ð󿉔]93§uÃ9:œ˜;³uÀˆ}ð=hg™g;ÑÉÚ‰€víÔE;ýýýñûÚ >6í<3!X:lo(‡|zsïÔÍ:�zjB�WÊû§.ëêÞíë¼¼ã“Ãë Ôæ£æD7ød7„ÚY¦ÚI9§T*…v¢éËßD`;Ú©GÚ‰)…Ðv‚�I;×ËÜ¢¿¢ý4Ö_8øßö°“ãƒ|íÒ~Ì…©‘î‰T·oJöjž-×væ¹é‘îqëÞß¶¸³SçÅsœ“Bk&GÏŒ Ú ãƒv–¥v d;Ñthg™���=tèÐy"€vþËÚ™––€o(´�à#ÐÎÜ)¡„!$+—ÒN·ðŽ¡î6–NõWœ¦v_ÊÉjïëdié(l5~;Çÿæ·©Õ´k¸§µ™�4îóÏ:ŠíÌm|ÚO?¬Üu|Yr€»�…­»ê7ÇóÖÄ;1ÅWOÞruAê‘¡…Sí„¥§”θqd¤WeK;at¨­�+{8PT2sv�}øBv3æÜ€ñ§ÏN ­D=2²¬ÖhàªÜ¢Š¬ë[µñèÊG—wdµÚ[ÚzEFHí•Úynj˜hà.åk˜ÜFRÅŒ:Ý-aªÌ�)Ò-a=uÎ×]í›~yÊí,CíÄ”Bh:AùÐΠ  •+W~x÷J£Û¹T�÷ìÙ£ú*(«¤h?×ÃÃâí,{í”Éd¡¡¡üëé͵ÕÔooK ;aëég^¢íúª�kã�šO/ÌvZHe ÷�:81ÜFécÇ'zu˜»ÿìùcË{B§þÄž3'ÚÌ+fÆÎ{?÷7u¹öè±e1®’aû(sìåá?lóÉógr>õ¥l¥4t¬¿ð½¾®—NÌi_¥Yöż££Wm•½ïÌÉœÙ]¼8ÙÎ˧WôtóC‰é†D÷È©ŠN½*ÿqrÓ*�m™ÓƼÍ:í9+ÊÑtÎéŸö~� ­¬Ô㓃j¦çä�É ¯9~gî愳‚j¤o+8;g€ ö0 ï„v–¥vúÒ@;¡�àcÔNÊÐX=û/´³$J©é˜’œ«ªŽÚ_wolll™h6€v*xÙΈˆˆ þõtiºlø7Ý?™YÇ8fÝë²k;¦8*s;†æU„-37]YoöÖî^Ž¡IÔI;  �õA;ËP;‘í„v‚�Q;÷ìÙÃ6"Ôã æ)+~ÌSžþyÐPÛ©ã)y£Ù£c IDAT°úÇ–À¤™ÇÔ^îµÙ£þe÷2å³dÓh9—ufê꼪Rǯ¤atš=—ÚÎT•‚9‹:…º ÜÚYÆÚ™šš†o(šüù‘Ö}sÞ–]Û™ÓÛ¾ûVÅ»×ù×rÆG:Õüäø«|.Ž’D/¦ëÙwæ†4™ò[1mŒqíwXuóË•m#Ïã3¨U;•lHt­;dïésç¯�íÜ=XÒhô‘¼£ã{w[zøÜe¾#̉ví½Vù`[J�P:yvB�{Êæ¼«_ws �ó“üjá¡?MrŠYüS“ÝjÆ­üñäÞém«M8›wxD½ªíf:wf÷œ:Û¹w¨ÈµÓ‚C'.êîQ{Ø¡¼¼Í)5¤s”Uû&Ö®vÚæc'vŽ�°�žYT‘UqŽL'Û³ÙöÁãvŸÍ=¸RÜÿº•K›…×=¸úC©sQ¶óŸ''‡ˆeÿN½ŽÑ^™gÕ� í,Gëv§ÔQǵWNg…¸÷Yáƒv–‘væççËårÊ9ÑÉV½ÒNz§rïõë×Ù_ÛL2–Ò3æ)õ€{Š··7³—MÞ²éSÆô(«ä¦RÉd‹R½[,su-ês!'KLÅ^‹ÚË-ŠÚÅMäR{™Ê°¬¢Ñr.´³$c;™4 ›uT…µ5vFfœ¤jþ�ÍX²»4e;ÙíªÙNÕsyÉOݲ�èd íÔ íÌÈÈÀ*Ï/Ìoïß~î¹§eñÕ¨%Ûé2ðˆâÄÏ–K3Õ\ÚÒ}èÉwk:;¥`Oyùu«#μ¯�âÑ—é?¿öó`J»9µqý©7׳ýÉ«jÚ�JíôÈ<¥<âï¹áu&\Ѥ�1ë”î΋P–¯¬f_§Þ[wæg™°ƒ«�oo¬Mò²SÎGJfÝRœéÓtÁ}º¼‚N¶·¶gFú8š+sâñ—¹ÚÉ?ÚYŽ´3ï̬è†Z—çÔ¸”Êô–õçÀ÷ �e›í¤œëvªBIT™w²åj'cwÔ¿¬¹15d­�w W;YÓãæ0©cSŽŒ…rWm¶“Õ?êt&!IÃx&si¶JªKê0æöBÔ¹Ül'u.sE-çB;Ug²åŽíäj'wÀ$/+¨I;¹c;ëcp2NÈcÉ\…=’¹3^”ú·ØsÙm²ã6™W¡vl'ûŠ˜BÊjæ^í|O;ÓÓÓýýý+ø×ÓŽ[ö»Æ°ãª×eÖv2ÚùöùÝŸwgµp�<ûFñ×Üpç¶ ¯?;³“ÔÉÒ¼rõ Ôµ×è!•Û񻄿‚çþ­xòãØàjÖÖnAi=üOQŽílY{ýé>’îÝëåƒ?³ý鎬o¯­”¸ÓãæH‹%Oе]¼™Léý…ÍêAý”|yxD][ëvËï*NŽ”D/¼£Pœêû=}Ì‚f�¦ÐÏÞ•X-^Yð¯ß§{V¦Ç)Gƒ¾“O ©bbÞdúϯ~ú²™»¥¹�¨}Tí°¹Ô®''&4÷´µqiŸÖª^ÿ£Š·—´ó²6³õŽjÙ0`’òGì�E­]MÍ£O«œ í„vh§Þd;±€ /ÏY6óÜhÖÎTnŠ’ÙËËv²¢¨šrTÕNî‰<ÉÔA;©úðò“ì¥Ù]š²�ªuÓr.´³œi§ÎÓa>!h§¾hgRRRPPìO?´h'€vê¯vŠD"ŒíäÊ/kW†ÚÉVƒ5.v@&[7îØN67ÈŒÀÔ®�Ü¢Óð´“͈2ËÂT«�ªK¦|¦(v¬&S¶Î¬á³Õ.ö\hg9ÓNíüèµ3!!S A;´hg±Ú‰u;Ñth'ÐNµ3111<<ßPh;´h§ví”J¥X·M'€víÔE;322"""ð …¶@;€vj×N___�@íDÓ  �@;K­� X@m'€ví,V;)çÄ”Bh:´h§.Ú™ššŠTÐvh'ÐÎbµS,cl'šNíÚ©‹v03Ù¢íÐN �Åj§@ �H$ÐN4�Ú ´³ÔÚ™žžŽl'ÚNíÚY¬vŠÅb©T íDÓ  �@;K­�2™ ¨ íÐN �Åj§”Ú‰¦@;€vê¢�¡¡¡ø†BÛ  �@;µk§H$”Bh:´h§.Ú™ššˆo(´�Ú ´S»vŠÅb, ‚¦@;€vê¢�2™ ¨ íÐN �Z´3??_.—K¥Rd;Ñth'ÐN]´3===,, ßPh;´h§&íd²�‰ÙN4�Ú ´SíLNN†v¢íÐN �Åj'åœX·M'€víÔE;e2ÖíDÛ  �@;‹ÕN ´M'€ví,µvöë×ëv¢íÐN �Åj§D"ñññ�v¢éÐN �¥ÖÎŒŒŒàà`ü²G D £Âj§@  ÌÚÉûe�@ ííü{çÅÕõñ}ßï{SìØ¢Æ^( XTÄ^‚Š%*„^DÅHÔOAi *ˆ¶H0`}±w4±‹ˆ�ŠÄ AQIö;ìÕq…evY`f�ÿÿsŸy.³ggν³ì¹¿½ ½�ÀN$$$$`§ºØ)•J��ÀN$$$$`§&Øéääô‘o ‚$°SìÔ××Ç*ÀN$$$$`§&ØéììlnnŽiâÅmLP� ú0°ÓÀÀ+Ù"tBAš`§««k·nÝP#ˆ�A?vbßN„N‚ HCìôôô´¶¶F� vBAüØidd„ T:!‚ M°ÓÇǧW¯^¨ÄN‚ ˆ;¥R)z;:!‚ M°ÓÑÑÑÊÊê#¯Ž[½;6®þŸOké ˆ<›�Ø A)ÁÎ6mÚ ·Ø Ai‚�NNNù*œ^çœökû¹C b'A¤;¥R©®®.°“)55UòV7oÞDèE :::­äJKKæ¦�=J�K”"Û*> ô`§···……jD&+|vkË]ÓÈÀN‚ H vêëëcßN¥äãáá�Ð)Šˆ9m¦¦¦y sSû· ¾È©rj†‡‡S†Ž¢¸AP™±ÓÝݨÈd;Ç}.‘H>m5fÕµÀN”ŸŸ¿gÏžää䬬,!ï›——wæÌ™ÜÜ\<‚x°3'''##ƒ˜ÓØØØYLár!tŠ"‰BUH„ªºÑ#¹$â=Q AP¹°ÓÉÉ ½�rý•ÿÇÅ•ÃéžCìXÔž3008pà„ Z·n�˜˜(Ì} q7nljjÚ°aÃãÇ� Ydº]C¹tttªV­Ê2tLOOÇç‚´;¹A¶ÀNEÙÚÚR‹_„!Ž� � 00�uú ö 8Ì‘÷ZµjG§£`ݼ•;{ôè�)âÎY[›7ÿö$b§ÀZ»vmXXËçææ 6Ù8""bÙ²e”ÉÈÈ4h�(e§û^½z•2ÏŸ?ïß¿ÿ£G�ðy€ ­ÅN]]] ²-)!‡w"tE N: 6ÃVfWRa¹‚ 6©‚ ra§«««™™ÙGΛÇfwiZóÓÿý¼®¾íw {sÄN™,>>žá©°°°S§NÂÜ×ßßÿÈ‘#,/JŸ?µe;tèÀý9kÖ¬;vàóAZ‹�ÄœúúúÀÎ’ì^Bl† *'v:99YZZ¢FDb§LöðáCjÉMŸ>=**ÊÖÖvêÔ©ÂÜ700°Q£FlÀO>ù„e–/_.dÙíìì¾úê«ùóçO™2¥iÓ¦÷ïßÇç‚´;����ŒŒ€�L ÜJ¶©©©�‚7D«„peÞîã‡A¶ôa`§——öívŠ¥¼¼<6›ñÁƒÑÑÑ3gÎܼy3µð„¹ûË—/•P~¾ ;·¾~ýzË–-!!!K—.ÍÎÎÆ?i3vsêéé;:µ;Åb?v/SSS{{{ÊÐQ襌ß.kÄ6ïvBЇ��nnnÀNÄN±téÒ¥ &|Ì5�™™9þü¹o%ðB¾• ;õõõ¥R)°¡ó#ÇN&¶)/|ñ™„�XÉ‚> ìtvvîÖ­j±S,ì:tèÕ÷uûömn]PPðå—_6lØð‹/¾øä“O(S¿~ý5j¬]»VÈælÛ¶mÃÂÂLMMcccMLL>|ˆÿ ÒZì4� ؉Щ%Ø)®Ü–¡t;Ù"FqqqTØ·‚> ìtwwláP±³$vÖ©SÇì}�;VHfÍš•��À—^^^Ü"C¨°°°OŸ>”5j-Z´oß>üO@�Öb§¡¡!Ù"tj‰„×ZL)))­ZµbS+Y… âÃNúÚêÞ½;j±S,ì}�­……ųgÏXþÀ�Ó¦Mòî={öÌÏÏ÷óóó÷÷777?{ö,þ' Hk±ÓÀÀûv"tjYM¼« á»ETZZš©©)+²;ÇB¤vº¸¸|ô¨ vЦ7nLš4I\¢££)t…„„Ìš5«U«V{÷îòî™™™½yyyQQQIIIø‡€ mÆN###`'B§öˆp‹°“m›I�Là•uRRRDÜ·“ Käɨs;!èÃÀÎ &`I!ÄNq•��Môŵð>,°Ç��ˆˆˆŒŒ¼páA¥a§žžž¡¡!°¡SK”ššÊv¯ab ü&"Or !!AxìTN`'}ØéêêjnnŽAìQ›7oމ‰aùW¯^uìØQÈ»SkrÉ’%ÜZ²„ BÞ}Û¶mÔ�eËzÍš5ëéÓ§ø<@�Öb§���¾¾>°¡S«D´É–öù¨À/00ÐÞÞžî+Êö-iˆ�XR±SDM™2EGG§F��äª_¿¾À³+'L˜0iÒ¤Þ½{‡„„X[[ ¹¤5gÍÌÌ?~LQ“þŒˆˆÀ’B¤ÍØillŒÞN„N­RBB[×Gxöc·#ä#D™]IeWs5#q‡C°ûv"vj…:”˜˜(ÖÝ{õêEÍÊÀÀÀÌÌÌsçÎÍœ9S°[S[–­d˰söìÙ»wïÆç‚´;Û¶m ìDèÔ*±Í3Åk*üø^YÙ×OwH0;‹°sâĉÖÖÖ¨ÄNõúõkggç/¾øbÅŠ�?òîiiiÔˆ>|ø¸qã-Z$äÝÝÜÜÚµkçççg``�——ÇoæÌ™ÐÐPnHp~~>>?$væäädddsêêê;:µ;é(ðJ¶:ííí á(Œ ��t_Âݲ|d0‚ÄÆNGGGl ‚Ø)®’““Ž;MÚØØyw –YYYÔ²\·n]LLÌ«W¯„lÎúøølÛ¶-(((66ö�?þà·ÿóÏ?Û´iC`ljjennNÄŽÏ €�Ü’BFFFÀN„Ní7È–ŽqqqBÞšÝñæÍ›áááÂ÷cK¤&vŠ8$‚€�ggç.]º F;EÔÊ•+)p?~œ°“šw¯quï޽͛7ïÙ³GàÎCjΚ˜˜üþûïjÚgdd¦RfÔ¨Qttuu%Ïñù� Á°S*•bI!„Nm�_jj*¥y-—(C‚!ØY„�^^^–––¨ÄNEÜÕ¾}û & rì$æ466v"tjSMH<<<بQ! 0--�ß+ëBôc§‹‹ Û3Bìü8eccC J–ß³g�À}­}@ØiddìDèÔ6ì”}¬ë刵a)Ab§³³36PAìW{÷î�ˆˆØ¼ysnn.µðœœœzôèqãÆ aîž��`jj:þüàààV­Z�:u O‚ ¥Ø‰A¶�Ú&Š_Š»ƒ°ž­­2 \põ7,•”UšÊ뇇‡söêt Wª½V9Ašc§«««……j±SD%%%Mš4ÉÒÒòÊ•+#GޤVݸqãsàĉÄœ!!!/^Ä〠¨4ì”J¥DžÀN„NíÑ£G�âââÒÒÒˆˆÄìííéXÙ7MU&� ^Ö K¹Y¯tT’Ùž4t¼yó¦:$V©öZå iŽ�&LÀ’Bˆ�âjùòåÛ·o—ÉhݵkWPPå‡ ò1”}Û¶mÔŠeÝgÍšõôéÓŠ½þ�æÍ›Ç6ùŒŒŒTÙ¼Þ´i·)èÚµkùí_½zµråJΞâüöÙÙÙQQQœý•+Wøí©•O )Î^å3ô½ÂŒ©Ô„ øç‚*;ÑۉЩµ~1[o]Ö K˺î.³¡+SÑD·×*g Hsìôðð°¶¶F� vЍ´´4 ‹°°°¦M›öë×oÿþýǯԛ¾xñBé0¡ÄÄD!›³fff�?¶··§?#""öíÛ§Ô’°¡\5jÔ¨Y³&eªW¯Þ¢E ~LÍÈÈhÛ¶-Ul�=æ‡ ÆïOrrrÏž='OžLüïåååïïÏoOt7f̘ѣGÓ[ìììTbª••Õœ9sLMM—-[Ö©S§ÌÌLãÂÂBò �}LLŒ‰‰ a‰iÉ>::šì©&ékM൩ �;uuu���Ú)û©„¿uY7,¥øN¡6<<œŽêôv2º2Ý‚Z&âÚk•3¤9vºººR“5‚Ø)®N�:õý÷ß:tˆèˆÀoÈ�!çÎ�«ì¦dº2=|øP°RS[¶OŸ>”aØ9{öìÝ»wóØçççM±…òï߿߷o_b3û­[·°åææº¸¸ÐŸƒ â'±ÐÐPþ'N,\¸�,ÉžßÿqãÆìÅÇÇ“ÛĄğüö½zõ¢ãرc_¿~½iÓ¦uëÖñ?yò„ýôÀ*‡xõôéÓ<öÇ�Ÿ9s&gO]–0RåÄQ®°ä69OEÀTáØ)•J���Ú#¶i¸\���¬a[òÖlX¦�·~û(Û†¥dijjÊ–±Ug%[‚*;}||°¤b§ðÅVLÙÙ’ÌÌ7ù¿ÿ–>¬øªÿ Þ(ݹsGÄ%ÔQ^^Þ† «âãã&dvêééaßN„N퉡lÛ :²”š*\èTÄN: ßXÖ K9•K•u�ÞJµ×*g ¨¼ØéååÅVÑ„;ÅÂÎÍ›%11oò¯^I:v;‡ –““â5%„¬ˆqãÆmß¾=##C&ï3411á_áÌ™3¡¡¡Üâ®ùùùø‚ Á°ÓÈÈ؉ЩU1T5 �ŒXdê­ [*놥ÌÏV­Z©´dë ¶’‹eø‡Wª½V9AåÅNGGG,)„Ø)bÈœ2E¢£#©QCÒ¨QQª__2mš@±“Z“Íš5ûôÓO4hЈî-‘4nÜøÈ‘#BVDÆ }||:uêtòäI›k×®õêÕ«´nL:ߦM›E‹Q¸�ŠŠ277ýú5þ‡ HìÌÉÉÉÈÈÐÓÓ#òv"tjO %æ²µ}—„ÄNâ= � ]®‚²nXJf²·›vª³µ)ý¤æV–•j¯UÎ@�æØéää„ÞNÄNC&¥C‡$‰‰J¯¢r¾ýöÛgÏžÉÄøÉ–š³lœC‡ÍŸ?Ÿ…íÀÀ@ÖùYRtžU&ßà”Ž®®®÷îÝSj9{öl¶áJÕªUk×®M™*Uª˜™™•æÉرc™=Ax½zõXÆÁÁ¡4ûÎ�;3ûÿýßÿm ×gŸ}6oÞ<¥Æ™™™Ì¸N�:Ÿþ9ËÔ­[÷àÁƒJíwíÚÅìkÕªU­Z5–iܸñ¯¿þªÔ>&&†Ù“1Y²Œ®®.ÿ2¿T&ìÄ’B�ÚCut$qqE}ž, ‰�:::lË ±Ff–u\(‘*aªÚ1 [¯ˆí*®½V9Ašc§£££……j±SDìäfurIà‘B¬eI_µË%dEôîÝ;88¸W¯^�5Ú·o_Ïž=KÛÄåÅ‹dIŽ1",,L*•²áÁ¥);;»oß¾l .!+ƒU�8qb„ Ô–ÉWµõóóã·_¿~ýܹsY>22rÍš5üöãÇ�§ö:e ìììø§ÑR1mllîܹ#“wó“³_JÙPÕ±½= PUîA`§‘‘‘¡¡!°¡S«°“HA”A¶„�"RJY—âöù”©÷+sxx87îT�­,+Õ^«œ� ͱÓÕÕÕÊÊ 5‚Ø)"v>|(13+^ûå—E©woAcçŽ;¾üòK6È–2K—.²"?~¼råÊýû÷¶mݺuܸq<ö/^¤ðï¿ÿrøðaþ‹oß¾�meÉÔ©S'~cC•X¾°°°K—.ü×'Få~9&>6l¿½‰‰ cZÒ÷rñQ+ŽÿŸ4iÒÑ£Gù¿ÔÜÝݹ?ûôé#ä¬ÐG‚�ÆÆÆÀN„N­Š¡¶¶Ä]ïV;9ðcKé þPw‰ ’�¬&*—u_ÐJµ×*g Hã]z½ IDATCìôôôv"vŠ‹�+VHfÌm�­¥¥åµk×d"­‹�™™9þ|n‰ ¬¬,~û˜˜˜-ZïQ~Ê”)üILÛªU+6ºÅÈ‘#‰Äø/NLÛ¼yóéÓ§‡……õìÙ“Þ¨’Øõõõƒ‚‚ÈóvíÚñc$iâĉ666Ô:ñ÷÷oҤɕ+WøÛúdczzzÀN„N퉡p ��Œ6ÅÅNu–*†�jb*X¸‚ĵ×*g HsìtppÀ [ÄNq±óÄ ‰··hØéííÍöxengÛ¶m‰ñLMMcccMLLø;èîß¿O$–ŸŸÏš£ÞÒævr¢øºråÊyóæýøã�/_¾TéÒíÛ·—,YJ­ju°-===***""âØ±cê”w÷îÝtñèèhu~R}õêÕO?ýDÎ/_¾\妦¤ÜÜÜ5kÖ�ýºuëòòòðíU8vs¢·¡SÛb(ÁTZšƒle¢N,ëAÜ [:ò¯ÏĆ›*î*¢½V9Ašc§»»;ÏB#Å—÷Ö¡"&`'¥S§$ H,-ß,Äçê*hì ÑÑÑá–ãKLL¬ Y$›u¹hÑ¢}ûöñØ_¿~�BevŽ9’M}„ Hì”J¥XRèÝ÷ø–¡b%`'—$r˜’È;;Ë:[²uS. Þ¥þ8RõwÀ^«œ� ͱÓÇǧ{÷îÀN`§ˆØ™›+IO—nÜ;‰ÜÒ墯Z: \övD1ñêéÓ§yì�?Ξ³'¦‚ã ªpì466v"tjO åÙÒ1.NœA¶jΖ¬¸‡/)–QV% Rç-)))¬\jòj¥Úk•3¤!vŽ?K !vŠ‹�+WÎãÇ‹°ó¯¿$ææ‚bgÿþý_¾|ɰóàÁƒ‚íXµgÏžºuëvìØQ___�õr¦š™™=þ\Í[ܺu‹�2=zô˜?>5—ù±sçÎ�„µ��;wž7o^ß¾}ù¯™””Dmqkkk___///~ûÁƒ?|øpÑ¢EtqþA¹äª¥¥%*=šY³fQEݸqƒÇþñãÇd4""ÂÀÀ@�%” ¨LØIŸ+,)„Щm1ôæMIjjÑQø%…nÒ]%�wÝ` 1p+20Uø]¨1`oo¯þâJµ×*g HsìtvvþÈ—Bì=dþþ»¤}{É„ ’Áƒ%õ| ;7lØ@ÔÔ¥Kúª%äŸ]Y�"FÊÈȠ̲eËø÷êäD ßvíÚµhÑ‚ ´üãNI.\�É×""ždyQÛ:==�2'Nœ ì¼{÷.¿ý‹/èÊ”¡&8a-¹ÇoŸ••õèÑ£‚‚‚ØØØ5kÖ¨{õêÕüü|º,Õ�ʉ£\aÉmr^àÁÒÐG‚�ÆÆÆXR¡S«b(qŸX¨°1™bm "Üʰ¤U,v:88XZZ¢F;EÄNFžK–H‚‚$Û·Kþþ[èŸlOž<B_µÔ¬¬ ÌÍÍw�:uJå™L………éï‹Õº¾ŸŸß—_~IÌI°7mÚ4þëã�;¶^½z›6m"x‹ŠŠâ·ðàAŸ>}j×®M¼—šššœœÌoôèQjµ·nÝšòtq•X»qãÆ6mÚôë×�òþþþ¯^½â· mÚ´©���L¾©)¾]  ÇN###,)„ЩU1´U+I` 8ØÉíºñ�ÄN*]ZZ•ŽŽê¬²S©öZå iŽ�nnnÖÖÖ¨™,w—›AÕ*Ó ;EÀN¶°PAAQ¢ŒÀØIÍJ¢2úÂ¥£`ËŸvïÞ�­ÅGXX£F –?wî\ÅþŸ;88dff²Å�ù;W­Z–’’²víZ²d›»ðhêÔ©Û¶m‹ŒŒ$r~øð¡Ê¹�ôU“••Åæ^®_¿žnçË—/»víš››Ëì§OŸNw᱿~ýú�!C?~ìääD6LeW0•;‰9uuu��ÀN퉡lI!á·¼–½¿’í¡2M_dÆf®Šn¯UÎ@�æØéìì ì$=Þá`áæÚtpÂkÄN¡±óþ}I‡’† %M›¥>}��III_|ñE“&Mè ·iӦ˖-¦ òòò•PaaEþì±sçΨ¨¨_ý•a'Q?v@îÚµ‹ÞEØI®rk%¼Û·o3ì¼{÷îÈ‘#ùí{÷îM0Œ\¾|ùêÕ«yŒ ‡*{»DÐäÉ“©­ÏcO><{öŒagß¾}srrðÍU,vêééang1%$$З§ÐMU`çÛ'ñð(šÛÉ’�ØIçA÷–?}íg•²N_,ë‚®•j¯UÎ@�æØéââ‚%…d·Œî>ùøóÓ¾ÀN1°sùrÉܹ¥] ¸BpEì$û'Îgxþü¹©©)5 ¬¬¬F�=~üx~{ÔöíÛ�3fĈ„ˆaaaüöÔÎèÔ©ÝÂÄÄdëÖ­üösæÌ±³³k׮݌3tuué ˆßž|vss#{___:ò²}ýú5}•ùøøtîÜ™ÈsÀ€ø¢‡*;srr222ôõõ €�œnÞ¼ÉöúvŠCMM%¶¶â ²¥øÂÖõe�mJJŠ­‚ÔøÈh2ݱ,ÉJ´×*g Hsì¤VÚG¾o'Agâ¨ÓOåËdÀNq°óòeÉ×_­a+ v 1^ú ¾vcbbZ´h1a™|ú¢ÊUy¨­¼|ùò   ÄÄD3•׿wïÞâÅ‹ƒƒƒwïޭξ——/_ž?~HHÈÉ“'Õi¾ïÛ·�.¾hÑ¢¬¬,•öäpRR9÷äÉ•öyyyñññd¿~ýúüü||ÑCˆ�ÜJ¶zzzÀNNÔÜ'òvŠCå‘KœA¶â®@£££CäIwOHHP;Ë:}ÑÞÞžX:õ­Äµ×*g ¨\ØùÑ/)ôß±Ÿ*ìåTeü.ÄNa±óéSI»v’ $Í›%[[AcçÑ£G?ûì³ætc ݽ¹�›�‘233 Ûæ¾?ŒÝ¿ßÆÆ†€Š�;upp J䱿xñ"1§úÎjRWß~ÕªUe™Cœ¬ú2½zõêÛo¿Uÿâwî܉ŒŒÄ—;T©ØIÌiddìdJ�‹2ÀNc¨½½hƒl)‰8ȶ¬½—e�¾hû¾Äµ×*g Hs줯Œ®]»¢FäBo§8Ø/ñô”<{&yõª(Š�={ö<|øðË—/)êTììJ•ÍÙ¶mÛ†……™ššÆÆÆš˜˜<|ø�Çþúõëô+{;ÝqäÈ‘ÄZ<öçÎ�4h�ú%JLLœìÔÓÓÃ*%…ÞNqc¨XƒlñODÑgïÑ£GêøÎFèêèè<¸ ‚€�WWW,)$jìvJN�*akiùæÇZWWAcgHH…î'ÛÄÄD!+¢gÏžùùù~~~þþþææægÏžå·'c‚½   øøø¼¼<~ã/^(nòyåÊ~û'Ož(Úߺu‹ßžHOÑ>;;›ßþÆ�Šö¹¹¹üö/^T´çïɤW�é½êÌM… ²b'6PvjU ýPz;‰ S¤(V8'$$°Y�”'(ô㟂ÅN'''`'°S\ìÌÍ•¤§¿K7nŠ�wîÜa BqˆŽüÃ\+\™™™Ïž=#€ŒŠŠJJJâ7ÎÉÉqqqÙ¼y3[Û±cÇ�;wâß‚„ÁN¹€��ÚC¹Y�áá’À@A±“$ëE ,ÍŒ`ÏÞÞÞÔÔ”­[Cb’ØÜ`Á°SV–¡Ä¥lÏÊ«³No¥Úk•3ØCÀÎwûvb�-b§¸Ø‰‘BjêÁƒµkמ:ujÛ¶mï߿ߥK—[·náÿ‚ÃNCCC`'B§ÖÆP…Um…�<4..îæÍ›%ÏÓI!‡¹²Y $â%ò–ýÉcokkKî©¿dQ¥Úk•3ØCÀÎ7ØéææÖ£GÔb§ˆ!3%EÒ°á›ôÙg’!CÅÎo¾ù¦¡\ôÕY½zõ5kÖð_½z5&&ÆÝÝ}„ “&MZ±bÅÝ»w«µ+W®sRféÒ¥ÔêíÛ·/å‡ Fmbü'A�Ø)•J±� B§VÅPnÇN{{I«V‚b'뺤Ðiooσp<ãi5j+)!SSSuÞÈ&v¦¦¦2âå_‰]³}>+É^«œÑÀv¾ÁÎñãÇwïÞ5‚Ø)"v*¦cÇ$^^¢õvúøø9rDé«DzÁÁÁ ÉÉÉëÖ­›6mš™™Yÿþý©I*€‡ùùù;v¤0ollLÇfÍš?~¼K—.<Íe??¿/¿ü’|.(( oU^ìØ±õêÕÛ´iátTT¿ýƒúôéS»ví .P §:á·?zô(yÞºukÊÓÅUûÆ�Û´iÓ¯_?Êûûû«\¥644´iÓ¦ôeòÍZø� ÝÜÜ4h°lÙ2z|³gÏÆ·¤;éihhìDèÔB쌋“>>d‹Ø).v;&4¨( (ÑקϤ Ø9oÞ¼ArQ4jРÁÑ£G•š;vlùòåD5ÅÎÓîœ9sîß¿_¦›?~œ�ìÕÑÑ©Zµ*ËÐ1==½‹Fõ믿2ì$ŠãÇN°]»vÑ»; TŽƒ À»}û6ÃλwïŽ9’ß¾wïÞäÃHªÌÕ«Wó4:Töv“ÒÉ“'S[ŸÇž| þgØÙ·oßœœûur�>}šü'¯ðë¤vsêëë;:µ'†*Ìçzn§¸#-9|U³×´¬Þ*‚4[†WD{­rF{Øù;½¼¼ÌÍÍQ#ˆ�"bç“'’U«$§O¥ƒ%¿þ*(v®$''~PJII!ÜRj–——·páB©TJŒjgg×§OŸfÍšuëÖmýúõoI—ºzõ*ež?Þ¿•]Μ9:÷­òóóyŒé𦦦VVV£G�?~<ÿÅ PÛ·o?f̘#F"†……© <�:u"À£[PÍlݺ•ßžøœê­]»v3fÌ æ;}ñÛ“ÏnnndïëëKGþA¶¯_¿¶°°ðññéܹ3‘ç€ø/þðáC*ì7ß|C–C† a“f!ˆ;é߃l:��š�\ZZ…$¶mI…ŒÌLHH 줣šûv²ûª\Lˆ³'c6žˆuÒÒŲ×*g4°‡€�o°ÓÑÑ‘ÚͨÄN±sÏÉäÉoòYY’>}Åί¿þší–I_£–+W®T‰+„[»wï>qâD9§tR[¶C‡ÜŸ³fÍÚ±c�=Ý®M›6‹-¢ÈennN¬Å‹œœœåË—%&&ª4–ÉÇ/^¼888˜ ¨Î¾—ÔŸ?~HHÈÉ“'Õi¾ïÛ·�.NEÈÊÊRiO'%%‘óqqqOžÌq¢;¶÷”)SvíÚåîîNù!C†”fÁ¨{À€)))ÑÑÑüölu_²_°`Á¾}û¨8<;— 8P&_ø—`’Bµæ‰Q÷îÝ«Ô877w̘1”ÙºuëòåËYH#.­Šˆ“©eòmT¨°´üÎ'³_Æ�Gö3fÌà·‡>rìd½�ÆÆÆDžÀN„N퉡¶¶EÃk‰9éXjè¤PÅ �-ÃÃVâaý„G1¶¨ÃH5‰Ž›�Y�¢bð0â·|$WyzäÒÒÒÊDé•j¯UÎh`}¼Øéåå…•l;EÇÎüü¢žÉÉE=Ÿï¿$„îÝ»G8GI� �ŠË±ÆÆÆnذ¡<ÍÙ¶mÛ†……Qà§K™˜˜°Ñ°JµtéÒ/¾ø‚Z½ô«rK&"4º2amÓ¦M S»térìØ1ž�B¿­­­É’9‘áˆ#\K³_±bÕC@@@çÎ�¿úê+???"Uº]iödüÝwß 6ÌÍÍ­E‹Ä“t‹Òï¥ÊéÖ­9CÇ9sæ4lØ�0ÛÜÜüùóçJí‰ ¨éîmÚ´¡f���}ÍYXX”æLFF]�,›4i2sæLª–ƒ’oøN‚x°S*•b„N­Š¡Ü2Brª*5tÆÅÅ)Ýè‚N²_'5?uê�ân™*ôgyöÞ`Wc›vòs,ç[™°3%%ÅVAâÚk•3ØCÀÎwƒlUn“!vV*væäH ŠvO™0AÒºµ$1‘;srr}}} ‡\]]‰Ù¨ Xžõ`¨ùHà1vìX HÔ¦T¹ �ÁR»ví(C Ðh|ëÂÂB¶7&›«¹hÑ¢}ûö•fÜ¡C6¤ÖÇLJå¡bpµlÙ²#GŽÌž={Ë–-C† !¸â±gûÄìß¿îܹDÔC‡½rå OsœjoíÚµÛ·o_¼x1ùO`ÉCÎyyykÖ¬Ù¹s'!tRRÁ§¿¿?ÏÂET·ä 9LÍ ºÅèÑ£ ÎyœÏÌ̤²¾k²'†Ü¶m�ý™3g¨½uèÐ!ÂÚŸ~ú‰*‡Í;… Ò°³M›6ô-ìDèÔžª£#¹y³(CGÊ ù‹­ŽŽq£â4?•˜Ê6Ï”É{)鋽œÄB¸«Îâ@¶Ê¤N鮨bÑíµÊ ì!`ç»A¶fff¨ÄN±síZIXØ›|n®¤k×R±ÓÁÁ¡cÇŽ“&MZ½zurr2ñ'Å-¢…öíÛ8p@3WüظMúݸqcHHˆÊ6èž={‚ƒƒ ´øWôQG={öÌÏÏ÷óó#377gkê*·±$aÞ÷߯æõ©ZæÍ›ÇFðFFFª,Ú¦M›¸¿t#~ûW¯^­\¹’³§†8¿}vvvTTgÏ´LÔʧçËÙ³½üßkôø˜1•Zå�ä0wqâU|A*±Ó@.`'B§öÄЄ„"Ú´µ%¢+šäYvÄÅŃ4ú“NjŒ lt+ƒIuºLK.)TžQ¯twºoå­gSV?+Õ^«œ‘‰½e+ôc§‡‡öíDì;ãã%Ë–½ÉJ:u*;K›{ùôéÓëׯkæŠ�� Û…­dÀoïèè¸sçNƇG�% V8TVeff>{ö,//�x,))‰Ç²aÆlâ~½zõêÖ­Ëò¹¹¹Õ!óJµ×*g4°‡€�ïævb�-b§¸!óáC‰¾¾dútITTÑO¶S§–;ýüü.]ºT '½6nܸyófÍ\¡(È&@²ŸlO�:Åoo``@_µ»wï&�»páÂСCoß¾­ÙÝ·mÛF­X¶‰Ñ¬Y³xÈŠŠù¨„øÉjëÖ­lôFúsРAüö¡¡¡û÷ï?qâÄÂ… É’gÍ[¦qãÆìÅÇÇSU�ç*—�íÕ«—L¾-Êëׯ7mÚ´nÝ:ã'Ož >\&ŸJGâUþ°Ç�Ÿ9s&gO<ÌÏä+W®¤GOèËF|±åŽ ˆ;é¿K !tjO MIy³]')/ä²)))l»N �”Wç-iiiôÖ¿Ja”¸±LKË–†�*W²-§Êºäo¥Úk•3ØC;vºººvéÒ5‚Ø)"vRzð@]´’íæÍE[w–; oܼyóÞ½{ÛÙÙQ¾mÛ¶-[¶œ>}zyæXhSôºxñ¢JcÆ'ÄÀlf&AÏþ�üÍY33³Ç�3RŠˆˆà™Û©�nݺEŒ'“¯¾K\mllÌ��;wî¤z €ìܹó¼yóúöíËýÈÈȤ¤$j‹[[[ûúúzyyñÛÓózøðá¢E‹¨ÁAç”K®ZZZ º»»�wìØñÆ�<öT�ìÑôïߟjÒÀÀàåË—<ö±>>>………ôŽA�:ØihhìDèÔ’š�P4°–Ž©©EÌijª¸‡Šï)n[By�‡ê°®t$ê`;gVÈâ´ø þ_õExì!—š@U©öZåŒöôvN˜0½�ˆ¡J³ü IDAT�âbçË—EQs÷nÉ�’DªD„çÏŸ'%Ø»}û6?J©Ôï¿ÿ¾gÏž¨9J‡pwæÌ™DPDYÍš5cK×\½zUƒ[S[–�+ÃÎÙ³gS¡*¶¢/\¸ “ïiI<Éòüþ°ue‰Ã ;ïÞ½ËoÿâÅ 6¶™šà„µl¬2�²²²(PÄÆÆ®Y³F僣ZÍÏϧËFªœ8Ê–Ü&ç©jÚÓ‘*GãAÚÐG…�zzzDžÀN„Nmˆ¡Ä\:¹pIy:Ã:Y÷ ÷gy: ‰ô8ß> ºu*�Qù.¢\É[©c¯’nÞ¼I·Vsˆ¯úw'Šf{̨9|´RíµÊ ì!HV¬·“[ªBì;þYÒ¨‘ÄÜ\2`€¤eKIÿþ’çÏKÅNjü…††¾~ýš;³zõê_ýUc? ëÖ­Kßž½{÷¦P´téR•oyöìY||ü®]»–,Y’œœìââ¨1úº¹¹988´k×ÎÏÏÏÀÀ //¯kùš\ê301§JÔäôêÕ«sçΩ\æ‡Ó“'OΜ9C¤ª¦=}=Q«BñYóëÖ­[%`ó€Ó[ð • ;韨 tjI UØ.ERâŒZØ©ñ”HÅ7–inVgù±³¬Î«÷²n¸R©öZåŒLÓÝh `çì¤F3z;;ÅÂοÿ.Ú:åäÉwΘ!™3§TìÜ·oŸ««k±3žžžš9qüøñ®]»rÓ)é;ÔÄÄ„$§LÞeÉ­€ª>ו{Û¶m Š��UáˆÜ¼¹»ól4êëëÛ´iÓ6mÚŒ7N�†uÿþýõõõ›4i¢r-_™|+CCÃöíÛ7hÐ 11Q·ëիשS§fÍšñ¯$Ä”””Dö:t 矢ÉÖ¨Q#©TJ¥Pg7�ñãÇS³£E‹S¦LÁ—¤>vÒǨ tjv¦¦¾—ø±300ÐÖÖ–õ ÒÑÞÞ^qÌmY.õ­X^}‚â¶])g�ÿáááœxw®tŠÅË^«œÑÀ‚ÞÃN77·=z F;EÁÎ[·$C‡¾÷cm^ž¤{÷R±óûï¿_²d‰â™ììlÍœ˜3gNrr²â—)]_å ‹àà`SSÓ¥K—ÒQãÅ„XsvÒ¤IêÛS×0rÑ¢Etߨ¨(ssóÒ:_½zEíã‚‚Æ“*·*!ƒ~ýú‘ñË—/ &yh–ióæÍ„µ”yøðaçÎ�Uzîâ⦭c«SdKKË;wî0žTg·*ìóçÏO=z”ߘð¾K—.………T‡å™ }lØillŒ T:µ$††‡+IüK §±ù�l9�§ç…+ˆ®Æ2*ßņeR�¥·”g1!îjeÚ‡Sý»‡+“XöZåŒö$+¶� °±S,ì¼tIòÍ7Ň ™™•Š�'Ož$ Q\*†­O£™þþþŠKQ(Ú´iÓ‚ øßÅ–c=z4µDøá‡ò¬£@ÍY“ßÿ]MûŒŒ ÊŒ5ŠŽ®®®¥múäÉ¢M–Ÿ1c†ÊMM�?>uêT–·³³£¯~û•+W®ZµŠãp•ž>œ¹JÇ‘#Gª´çFþ¬Îž=[}ûèèh•½¯×¯_':eywww•S^!`'6PAèÔÎZzòQ`°%Aêa§³³3µãQ#ˆ�¢„Ì+W$Õª­‚ ˜LLøb'ÑQóæÍ 5)Ó»wï-Z°n1 4}útn3Löpýúõ£¢¢øß5`À‚ºùóç{{{ÛØØìÙ³§<ApH7å~¯½té�ñ‹/z©Pìœõß[2YÁ©Q«HÀÎJÇN===`'°I°“2ç7R†£G:CÞ¹wŸ}&™¥â»ØKqëb–L¨X$`§@Øéáá�%…€�HbagÄ¡2Ùý_]¾óg~~Þ£ô½g’ÁªS�d²›gO^yøüe~îÍÓÿ {û®ì£!EWX�,î [zôÞÛÏí�]ߣn‘€�•=Ȩ;‘´;1þ|&­¨]{&�ÃNÅ|i؉ÞN$`§Øéâ₹�ÀN$Q±S–{ywhÔšm·^Éþº“´è vÊr¯þ°4.|ûµ§²Â[{W+ÇNôv";ÄNccc"O`'°IKz;)C–DMGéU`'°S[°ÓÓÓûv;‘DÅÎçÇ7�?ø;�=¸ê v>þ%An¼éçg2ÙÝÃs��HÀN`'° Ø© ;é%¥ØIG`'°S[°ÓÇǧW¯^¨`'’xØ)GÍÐÅ!û²e²ÜÃño°óî�x¹ñš=dòäô¢÷°sÃÁÇÀN$`§ÐØi``€A¶ÀN¤;éU.ìDvŠŒ�NNN=zô@�;‘ÄÃÎ¥Øùç©Mïz;ï ~ÛÃ^t2ñØÓ7Ø9s° Ø)vbI!`'’öcg1UœüÉ>«ÀN$`§8Øéîînff†v"ivÊò27ÅÅ…m½š++¼¹gU@貤›É^ÝÞ²lYHò¥¢p*ÇÎéIW^Ë 3¶/ÿ¿Ѩ[$`g¥b'1'z;��HZŽ�ÅV²åÎs/1ìdWãþDBvV:vzzzvëÖ 5ìDÒ6ìüýÒÙ´ßóòå+Ù†­d»xfü‘+O^ä?½wõô™ßÞ`g@dÒ‘ìç¯^=¿²kê ØYIØ™“““‘‘¡¯¯ohhìv"igRd‹„ìÔFììÚµ+j؉¤EIŽ�÷­AU ;µª·S*•b�-° ؉„ìÔ;]]]±� ° ؉„ìT‰�ÆÆÆÀÎÒ° I1;ßÃNggg,)ìDBBBvªÄN=== ²åÁNT‚èÒª6.93#ý·:}èEÀ#Ðÿñ½ô^o§¹¹9j¤v"!!!!)&`'[RH__Ø ìv;á?Šì,3vbe_”wP xŠšYôÅq‡Žn‚ÿ(Bùývcn'°Ø ì„ÿ(°Sìœ8qbÏž=Q#ÀN<`'üG€�ü؉ T€�ÀN`'üG€�b§«««••j؉ì„ÿ(°“;1·³¬Øikk+QPjjjùoD¡K•öÝ‘2riv}zc«V­€�•„�λÎIú¸S2 YÇ�lâ6¯Øcv¾ÆH?±˜G©W”g'ûÄnѸ¼‚1óŠŽÜªO:CŽ•4fþúÅpg(_ìŒðE`+~ JójøJ>‚’5ì;ÝÜܰo'°B윲iÍ—v>ŸŽ ³Úþ뛓Ç�vqùö?Cf¯¾ø�Jcy غªæ¤�ÓEažÓ§{N ødÐÔF³MæüIZ^Uþí\uæ±@Æ·/¨5ȧ–ÏON§Åa¶'Î �l4÷øÛ3Ù>?¬jd7ñÓ‘¡f‰×f¼g|ÏcÕ’ºC&~6.zÐþ{ìdàþ�†ã&ÿÇnnןnˆƒ�i—¿š\m�OUÇ¥C0¯îºÆ-®3xbu�ÆŸx¿°‡öwpžF� áŒ=ÞçßÊ7egïy;€�a§T*500v– ;ÃÃÃq±Bȳ4ѽv–Gä$]¤Rýü˜±“QkôsÀÆx€˜‡�ߘ;/"v*õª4†,SyÆfºÈIFË”)VŽâ8{²d'¹w _r’ó�9ÌãciVjVá%kØ)vúøø`I!Õ´³ë›*o~«ý÷gµZtûæû Ï+àF‡<ë÷Y‘£ì•‡)>nñ÷Šn<¾Êà„×]ýNT'ÉlWþì¬ì<ýËÀïOøžÎtú¯ß�ÌŸuìÍ™»[�‰vè×)›×ÔõÕ/ ÆGõ]{aÚé+ç|:#µèÌ… –üŒ¸:uצÆ#–Ž>+vúoˆ®å›âyê¶w|tÍ)»ýÓû.e}íÑß�9–é>§æìŸÈÿŽýÂFùÕïà鯖˯ÉOž³t™ú©í43`§€Ø‰%…4ÆNÅNÈ›7or] 1²?9n$Kv&!!�]Šõ�ÆÆÆ²w‘÷.Ê0¬eyîFÜI®“Ý‚�$7Š9L÷¢7²ã? ;ÙƒàJ­X±ŠÕȽ¤øÔ*¼·SX·;IHP¬«ª¤13#Ò··³˜W¬«Siï_™Ê+äUEèâ*³d7rIÿ9Z¦—”öŽ CþŒ-9ÚTÇ«bP ì;±o§ZØi¾ð>eþ*Ƚ›a[¿�Ãó*Ñ…‹�º�f•ë—æ›ÖÔ4üW`ge`gÚ½7ý™[VÖcƒÇ¶6î¿xø¹¢“ãfO­y�ÏXÎržßÿ0dåòOEêíü.-[žÉ°rY0DNh?}¯ã°´×ÚÓ¾i¿©2β›æÝ$V?7Õòý×Ä¡ê¿j‡�ñQŸ~wTž¿Õß˽Պ[%�ߪÈ Ó‹ÎìXS}ÔZgy·íàIÞmVßþx/j¼äŠ˜ÿÛÂc›ozöØY¾o>9¿lo:`ñpe0ìôo¿o9?³¯;°S8ìÔÓÓþ�åÁN®7’ �u'ù°3\G(½D'¹‘´\†a§âÆ¥ÜB&îúv*R+w†¹Ä�)æ0s£´q¼(vrxgâŠæIĽ—{~ã®WþêñCfZíWr�ïÖ-þŒuØ;Åèív–;»:¹OÖÉõIrPTòRŠØÉ]œÁj1ìTœZ’o•Îÿä`}žÿ ì䦼²²>^V@ÊsEæ�;\®ÊÃN6³‘—J S4æøMtì,æ•âù’ãfËT^Q°“zœ“ˆÃεtØtýÏ'cÔ“ÎJWèídØYxlr³¦#×dP·¾ËÞ‚ʃxß_R½ƒï–Ìœ+óL$UM}wÜþóv„y•ovÉd¿Î7‘Ô±ÚŸ�slbsÊÍ=ú[îÉiú-ýNWv”Ú}W›ºgrúo�kJœ’|ä/Mý>Œ�æÛÒ�g¦ß>?Ù™ÐNTìtœë_KÍí³/[áüǹ³š.½ÌküKÇaßvÙÎ^:lÔ/°ûñ±sƶÕÕú÷HÉô?vº»‹G“Ø«ïÝܨ(pzÖ�ûK@úož gÿkÚ~ö’SèôOÞt“ \„,÷e៙ÖjqšæÏwãÝn{é„éW¾�¶*ðìÑeÛ]`§(Ø©««Kä ìÔ;(2ì,9À•ƒO�Âc'Ù(Âp…ôøivr©™Œ?‡suUQS[y˜‡›h§†)ÎÊã¡#�±MÑ+Å^8¥ÓËT^᱓›ëȳPÇxÚƒ�ll³š½�TùÅ °SìtvvîÚµ+j¤ìØ) ¾ò‚ HQ–1÷óö{7¯RÇ ÇHŸˆÓÿ-+HüÉÈ-½)ë¥OäÙwØÙ1ìͧò�gm˘‡Å±óFpÛ63Óß\`¯‹NßUvÖtÚ-?ñd©õ“Ž)ºûrû8�vs¯eÓ¾k]uئ§ÿ�qØë ýïÒÞt{¶¸P”»6ÇÈhÞ5YA’ýgÝeË?εÍÍŒ•ñnØûûG•†�¿žºâ°ri�¾A6G›¹)î?ã<åç'FK¦ìù®tcÿäÄž›o�;g¦Ý™¼ë`ç‰5æž|ïüÁŸMÛ?ƒÏ8ÍbìäŽÉ¬�t¯nŸ9½�ˆ��3ÓoŒžWmД3V´Ø}wq`›þKú˜Eó«ö‹q^þÓÀä]¬æÇÍžZ=ø„�à|ưˆMƒV¬ùrx`û„ë3Ó¯övö0ü�ýp´íÀé»K\çØîÎá?û½;ì; ˆ<��ša'7–1;OGBŠÌÃMÔ”½�JG¥ØÉÞ[ÖA¶¥ag1Ü*��?8ìäÊÅA&ÇŸ\§®b�hE 0æßú‚°�17ב›§Wš1Z©˜Äݽƒ+‚ÒÁ¨å)¯ÀØÉß[[l„*gCGçv2È%æƒJ¯JžvŠÓÛiaa�)vÞ]Øùs»¤—y+m?»µØ‚??Í<´.Âwl÷fÕ;„\-L²«b¿µv^ÿºvšEf¿‰Û ´vY½ÃÎÝN5{Å="ìläâcβžõ}—|š8´Š"ÿOÏe¿ý#Äo‘f §ÈÛxO–÷ùÂã`Qîù†µ÷þ-;кCÈõ¢3×CÚÌ”7²ti1ý\x»ûEf_�à>©¿ÍLÝÚxPÌHùα3§ÔWœÛYÂxTàÄ÷b§ëæ)âíÞñÝÆØºóN½wr_Bó9¿ðß±›æÓlÙu¹ñ�u‡.wA°óMúvclmïÿNR~©3fccF¦ÉçvŽ^ïZtæ×Þžºñ¿ ��“–†¶‘O@ <¸¥é˜øoÒï��åûE”|êiÑTáèçÞ¿ÈÙóC¢x�ÿì;�äv– ;•n ¢8Ζ¡×Ó¨8ÌUqy¥ØÉ]Ÿ±»ˆÒ%…™J)v–Ü—¥<»°hv*"=WQÜÄNnÌ-W½ÕÍËÏlŠãfU®ìZr�Ñ{;Kz¥”m4+¯(ØÉ µ-m„ªö¬d˹Êa'¿W\å+r>°Sìtuuµ¶¶F�¨‹�äÞ:8·WÝÆž_Éd×":Uïxä^›�u›L:ö:Õ§iGÿcò_=9;£CM§ý²¬Ëz6‘g=ý#}åˆ&¶++ÅÎMÞ›õôIzL¿úîÉd—gÎIYðÃÎ×G}š6µ_s)'ïÁ©=u¤Á—d<Øùp¥í§&¡Ü/´7Â;ý»mðõÀƒ(Ü1¦Æ�ó{״¥àóEÏeOdOWôÑqÛÏ8tHmÇ�EóY_oYkä–×àI験5uçÝ)lõ×'Nm{aêÁ�ºÃƒûâ7~›Äíí$ŒD,;Et¦´­;”nÉ1�[*F©±¸Ø©Ô«’½¯üE(V^á±³˜WÌE&çÎq–Н²ÙªâîÛY²b‹yÅŠ ø_-í£ì¬\ìôöövª…�ì÷Ò}Rý ›‰®¾”Ÿymƒ›e �Ï«Ô5:ÿxÑPÖ{üz¶®õy•zº½üvfÿ%“\ÿÑ­{óZŸWkÜÕ%ñf¡òÞÎ6_¹lU«ê¦«¯]9oïd£šŸu¿övI¡?O-eÒ¨ú§Õš˜9Ç_z)ãÁÎì…æU®UYš›dW£‰ï/}ð"c¶^ûyrœ¾dlÈß�êÚ:à¬LvØ­^ïU�9:±Q¯8yñÏMoÁu#W8vîMj4Øóß§ÖŸ²qì±7„6ãð¾v¦ügØìk/É1ìbo÷`ëÝÊ�ÅÆÎëƒ}}ÿÝ×ëó1a�×]”/Ãsgôÿͬã4§þ7‹{oËd#l§®�®;ûX ã"àRuऺ¾[]ΊƒÍÓVD°°ñ†<\öé�€&Ó“Ç[Éç6·hèÏ¿$­ªÙßã߃ýOßæü–0vnn=Êç“‘aÝ“3ÅÙ·óÔ©Þ¾³ª ôþ|ìü›™¿:,Œ¨1ÐGgb¢ƒÜÏ€¿¯pÀ/-½¯§ï»ØùÍOò/®÷qeg„ Oôv^¾|Y___OOØ ìv–¿·óƒHzð´Á|/½ÃNKKKÔˆ ì¬Tv¶ ¾�zýAhÒÛùÁ$ø�";Ë��ººº†††ÀN-ÁNØ æv;?$ìôôôÄ’BÀN<`'üG€�*±S*•bI!`'°Ø ÿQ`§&Øéííݽ{wp/h IDATÔˆvÒ°Ììvjvs;KÃNj`!!!!!)&D‡wØéååÕ­[7Ô°Ø ÿQ`'?vsb%[ôv¢·½�ðE@o§&Øéââ‚ÞÎ’´ƒ„„„„T,;¥R)°Ø ìvÂØ© v:;;cI!ôvâA¨ã ºÚà?z;ÑÛ‰A¶ÀN`'°þ£ÀNM°ÓÍÍÍÜÜ5ìăvÂØÉ��†††XR¨LØikk+QPjjjùoD¡K•öÝQ¦éÞ*ô– ÷V«°óæÍ›¬~J+8°3ááá寄ҀÁ,dÛ ªO쥛1*nÉc,.ó�?äÛÜR1 ÿá€âžüÆbùÏv³,¶e%ó¼´ Q ÅöùkëT ¼*ù’Òv ÑÛiee…vâA”;3ùL”‡Ã=ö½·ç´m›ZŽòùÏWÿg¼âÂtUÆ¢2Oö”­ÉÆ®Ÿö]l÷Þù¬É[·wt‹èµ_cÑüŸqâÜÐÐÈFs�¿¿æ¹As"ê ój{µä[¦¬Ž¬=ûg–÷IXÝx„φÍ2\‘(&v^íç9Ó±[ëÜSŠ�å!XmÃN* +«LEq�€ƒL:r5SZÇ²ÆØÉPA±?S±KŠƒ7Ö1Èc,"¶ótô1¤QÓXDfS„.¥=´Š%R·­Æ­¿òäÏŒùVÍ<‘ñ¥0ÓF½"~~�›µilóþñ9‰�7N h´eä¾0¹uuÏíßÊ»Ç̘\'üŸ±ˆØv|W«Á&?e–ò– « Ø©ÂX4fó_µ@;9­Êä�>iJ/rgü¢D»%o°óø¶&~ô,:wÈä ÞGD*©T«Eûzº¾ÁNïÅA�—\):â¿-<¶ù*X–òRf_w`§p؉ Tʉ�\7� ëöäèŽë¥—è$7’–Ë0ìT<ø”;C¤Ä]ŸHEjåÎ0—¸3Åú‹ñð?¦·³OrtÍUÃN®Ï³’°“5ô‰*‹�8U¤5Ê3T+ÍX\l#gX¦RT+†�üÆZ‚�ä!çdIc¨ÆùÏ ³óÂ`[iÎjzUÚKÀNq°s;Ë€�L÷—Y~ÒoÝS™¬ 3Ñ˪¥NÕ�;�_žVÔz‡O·fÕ«è´4·úrQ÷Ú«ëÝÌ›Uû¬Z“nÞÛïýM—jaûõ�¦5:mßÃz;¯K-�ä!Ï<æþÛÞÎgg¢í;6¬^¥vËÞ‰7躗õl3Ôm°´^µ_š{mûM)vÊþzyÿØT“¦“ŽüC„ìÄLë>‹—|Ó²}øÍ÷_ÉŽµ¨7fû1àÒýK½LùºN×FïÏ×öm6õLEbgvà©kîIÛÚ}3ÛxÝå÷^Jˆý·C¢×[Tøß€C|Æâa›kÄŒÿ©ï8廹]6]çÇNUÆÚ��Çw4ëë×bZhµASêìòz>wm¶Ü�9mÅ[ìLÏèåôm«ei©{MƒM§wÿ8úäõ>o±sÆžë�ˆ|ðÎÄ+Ì“Þû‘¢”—€�‚b§ž\ÀÎrb§bW'x¬’ë“d=–J/¥ˆ�ÜŬÃNÅY %ù¶äüOÅ3<3H?PìT:bV±ÿ“!h¸\\%”§Ë—V7¤V±·“õJq½�<Æ"b7Ñ”Ñ?vòk vRͳѧt,Vóä<«yÅB1Ff¶miÎeòJéKÀN°ûvj‚�²““78+»µÐ¢�Û¶ÌÜü¼»û'µÓŸqžxÇHoJjî«g—#­k}•ðBvw¡¹ŽUè‰Þ?عޘ-/éR5¬¸ùèqNþ!;ÿÕÒaÓõ?Ÿ\ŒPO:+]¡·“agá±ÉÍšŽ\“ñäÙƒ“ zÖë¸àFvJ�ØpíÏ?3× «ktù}ìTç5-ç§çÿSSîj›:.ß?÷0®W­ñ;ÞŒG>à®Ó?þùn§š–1ÞÄÛðöÒYW*;ߦ_JÇD�8¥pæðæF¶®úŸ¸0Àwjõà|Æ¢a¡Ž{Ãy¿L:•é°`îÿÚ­œ�ƃ�*�µ;¿Ûó¯QK‡¸ùí¾m†MÔWÕ|!£_øÎIé¿)`çoßÚÙú«IÕ<6:Ÿ§ßíÙb¹îº¼zß`çÌô,÷e៙ÖjqZ‰‘ÌJ_v Š�úúúÀÎò`'£†�źÝ8{Ö?):v*Zþ3°S)C*b'½Êê�«UÅ®pì$(†ÜÄHJŦç•4 Û¸žXæpÉ´J»mK3ÖìTt¸X¯,÷ Šÿ\ÐÊ.BY½Rú°Sìœ8q"öíÔ;¥ÁW^Ä÷ï÷Z˘ûyû½›W©cÐc¤OÄ�i�ÿ–$ þdä–¿Þ¿”õÒ'òì;ììöæSyȳ¶eÌÃâØy#¸m›™éo.°×E§ïª? ;k:í–Ÿx²Ôú‹IÇ”÷væÿqfQßzM}Žþ“°ó€{�>+rß?·ß­ŽÍrV¯²«sÛµ™~.3¬#Ù™g†Ô½¹ |ΔÖ?gÎûíþ;N1‘:ƒ'éLYmðõ$ã„;¼Æba[šÅXŸIòü¹ýƒlþ¿½ó€kòøÿ¸ZE¨¨-u ´Ä*Nܨ 2‚„–"2¬Š +¥JqÔ=q‹ZgUªà(uâFmUôïÏ8ê6ÿKχ‚ Jý¼_÷Â'wϸ»“¼ùÞswX�v–»³FhgøÂø¢]‘Òm�¸ˆúSÿd»/]Ó[ú�-G;óƒÖ¬sX¼ËÊoœnÄïÁjhÂÙ>ñ;ƒŠ­þ­vžÎ8kC¿EË¿õmJɨ²ü"hgek'ŸÏ‡vª¦�l(,µDšO~ÒóCz3Fæ¨rµ“«ì Ûÿf´“ë“ÜLÚd6…óóŠ÷@¹ÚYV“èYéq’íd’Vzôoéh§‚�5'ÚÉÝ–û‡6—(½ä¦TB‹J7AÙZ•UíTƒvúøø`�­ÒÚy=±s�Á›ž=^ìTÛ-UfŸ7óÒVÎ s³m®Ýaú…W›k K•ÑNçU/e´Ó"!ÿ­PéJ¤TF;/N6}§�{:ÝÜ#Úi ¢…-˜ïØH”.W;%¼ÚèòY g®òÚI|Ò(:K&ó7w½‘Û¤[¯ÏƘ´—ù:wßfí×{KzêÝPTÁÊ”ñu<ß56¾[šœ¢¨Ýk¹¯sú½v®tç¹ÒÛß·Å‚Ët!óA?:g*ÐÎrwÖŒhçºy5]W ¥Ûþ‰“ÿ”óÎÍüJ|D}6þ`ì±]­¶†Jfˆ=ß;(Ò|s~e7áÐæ¯K|p·éVð¼mI‚´Q·6s]æÅÙ¿Œ"hg%igAAAnn.qNÌd«¬vÊ]’„;ΖŠ(±™¹Ø“–ìQÌÒÚÉÎOÝ’žDî”B,ЧX;¹µ-=ûN•ÖNÒ¬ÿe‚œ2ϲ–^OåchgYÆÂ†Ú¾ÏÎjÑ66!*ùYz*WíT¼³†h'›ëUÁ,A¬Qä^І¨K;•­UYEÐN5h§‡‡¦RB;ß¼(ºzpj7ý&þŸ‹ÅguÒîuèfѽ [¼ ›g¼L53Ÿqçéó'c:èö‹o$Ù5ìžpâÞÃÿå,ÚÔiñ}¹Úù©iȾä$õjd>ó¢X|.–ÇŸœóìÅkª�/‹š5¶ülÁã;™³õLâΊßS;_œ[4økÑájr#$ÛYÞΚñlçÉŒo‡´]�žqÌÂsJ·röí<¼õ›A }ö]�ðgNï�hó-ùêj7Úé—8I{ÜnÿÌëc÷li9z¹€;m¯ü"hg¥F;Û¶m íTJ;?*ª-ÎYÝ¿Àü­Q•‘ë 2óëÐIwX‘Ì0HuMÆ£Øyè:�ÌaXØÊ¥×“T‹9—Unœ�5�»²%iŽL…eB¸•¹É{®Û)S+nJW¸ÒšíËL)daa�)_;‹W$©¥Ý˜×=hõ…gÒügW íZêÕÑÒçøñÈC’sgo„cÝ:Z »EìÊ-¿¸´NhÛB·N½&–cÖ_y%?ÚÙv�oßÖºuwôXz^ræÇûBLujwˆ¿øvJ¡ÂÌÄáßhQ¯©…÷²³ÏÄåiçÛT¾Ðùæ»AñG ªË���ÚÔ¢8Š)¾›d÷ùÀµÒÕlžœLèg¨£¥kÜgj:lûæZŠ—yúÚÍl¶üýºâ•)ñ-<}·‘[ì7ž?4�ýMøÍ<ó½o\×=·b÷¦4ì7®q誡¯—½³&hÛÅaS¦ÕëXÏ{±Ëa‰t…¯˜«?)Cº‚eŽÝ(úŽÌÌSvgMÐNâ�ôcƒ™ç¸íZ�}Þ?¶x)ÎÓG;ŽúyÀQyÚ™ó�÷ü¤ÆƒÉÎZÎ8ªÎu;9ƒl33¿›¨Õ7°ŽÛ�ö[$óG®[Ø ò@„¼"úÌ­´*Ã<¡�D;y<P�vB;?H´³J¤ªÞÜM¨?Þ—Þi§§§'Ù¾Ÿí|4ˆv¶‹»Œ~Wû�P.ÚYÅê�&@;+®�¦¦¦ˆvjŽvh'œÚ í¬JÚ) Ð#ÐNÜh'ê�&@;ËÕN¬Û í„vB;Q4Ú©ŠvúúúB;5Ùvp# �ph'´SC´“8'PQ �ä 7áÓá�vº»»cÝNh'n´õG �åj'qN ²E´ÑND;Q4ÑN£�˜R¨´` !!!!É$h'qNL)í„vB;Q4Ú©ŠvŠD"ô¢�¸ˆv¢þh¢�åF;ù|>´Ú í„v¢þh´Sií D´Ú‰íDýÑhg¹ÚIœÓÌÌ Ú í„vB;Q4Ú©´vz{{#Ú íÄ�€v¢þh´³\íäñx¦¦¦ÐÎ÷×N''§ÒÓÓ+~!rrª²ŠÈÅXÒ“žœ_Í´óÊ•+´d }E›Lv 9¤í¿ee CSá´=|-¦¯,]D—bæG$Éä�C4Êy”jIÞ»OÕw‰Ðœú“ú�J’Ÿ¥‹HÍåÞ™©½ ¥ëYV…‡¬9@säÞ/hg¥j§@ °µµE�”c;»½´Š?†>­­ÛÒÚkaö“p¡4ÿF=È+¹»S$\vSráÑZÎ)/•?õÓKëÆõûö›ú_ÔÑma92éxAõÒÎ§ç— ² ?V*ûÜ �¥A}ío¬E»nÓ/Œù»"{¶Ñ­×ÀlØÒó/>°v9ÜeÌØšýcÍ–ž‰.Y4nÛ†VÃE5ý`¶({ÍÙ¾±�[HMç ­Ž�ÓçùÛsÎlÝ~"]ÑFÁ_%‹Ne÷‰˜X»oHƒ�õžrò³rºzMq<¨)ÎsüÔ€ S�”.ŠÚ¿‹?*¤æà©–/Ksnú-ùE¿PíQsûí¿©1ÚyÝgÁœÎAÚ~kF—¹Y½Â¢¿èÚxÂÞ licÓöwðW«_ø×1{OC;Õ£�FFFÆÆÆÐN¥´“É5ºbžeA®%W«”rNVCnå«�vR·,Ý"¦èLÚY7 mݺõ‡ÕNòÕŸ~û'æF|@ÆåhÙ葼•ªÛ™æh‚ó(ÕæHš£�Ô9åj'©3µ5ò“Ýærr5U-M(]OnWËT˜þÍ‚¶Zæ~A;+[;}||,--Ñ#åk§U¢Dd^¿(ºž>Ë©ÑW¿=þˆU8eØ9ñ†Ê‡ßIÔ¸iÿÙiyOÿ½sz¥G›/{®¸]=´óMáÙ͓թñÙàuOK=þ]زõ¨UçæþèÐÜ?�ì|vfGƒn³ŽÞ)º±Á­Eïe«LÉoáyÎÁá-~ÉûÇ^ÞнÓ9E§�wt›î¸ûÊØ½[[˜ä¸??6'ÛÎc’Uê¥ñÇ2­½¢mvk„óDn^Tô �£—Ü&GëÇKÞšY‘ÿÏSMÉÍ<7h|„Τ£Þæ‹–ÌûrÀš£�~sãëö÷«31C¶(;ÛÎ=ÂlÍ…ðÝš �7âä­ØÃi=Wd�ûëü� ‘_ĤkˆvFï\õ刅®yžñ“I?GqŠV§9p-"m/opxçm$çŸa‰kFºqp¯ÉЀV¿^„vªE;1¥PE´“k8ÄgXPQ&¼Æ¼‘…àRRRè©hì499™Ev`G‘ ¨$Û\•¢™LŸè%d‚{rk›.¥zh'iíÚ™\Ø-`’I~²ž)+°¬²vû¢2@@&TÅÄŒQU`ÇLOœG©&ÐmRy�Šv–¥‘¬ÃI³5V¤9ÑÎÒõäJuYfÅ€vª3Úioo�y_í¤ÜžoW«×ʇbñ‹¼õ­ôêÖoÒiô¯Y’èí"ëæÚZz­,F-=' ¯=¿´VhÕ¼^ízM­·ß|CNÕÒidÿf:_ ¶ï¥ÑÎóq&6~“z·¨[[Ïd`‰Gât‘�ôÑ*éöÛhç£s‡™­­õe+ûÀõ—ÉyïÎwl;@èlÒ°^ýo¬¶ÝâÖöBœ‰¶ËÖwZü4suÂμWÕB;�Çvíá7ç¯V߯_)Y’ŸlÓÐumõÙÉÆö¿Ü{¶sdË$jïOVôl~âjgFj“Þs†œ’l�šÞ0!û]QêR-ÿíc%Ûù®1! âOÅæ2²À-KRê>ebÇ­šà<7� lš,µ—ƒõ_82›�íêe»Wº½/¥Aßù®4ÿè~Ûä}6£4H;I¿d¶íܱ\{ø oiD×98°íÒ«±Y7iD:bIÂW‰9š¡�ùnÊsŽmoÖgÎ�“#Í¢!Ù¼žS�•8OàÜ)ŸFˆ�vªI;±€JE´“…шØP£#DsX˜‘‘L6’–mPíäæP/e9Ä”Øù™vr­•åÐ*±«UUþ£è;.×')¤sX'Píd1Ï�¡�ܱ¦2ãNi0J¦ˆÙšæ8�²M ¶£iƒlËÒNV™íÉ[+?N¨  eÕSq…ɯSåÇÌ¡�%´3((ÈÑÑ=¢œvŠÿ iÒ"ò¤øj¢M{á¶¼¢§�¯ïnosšøŽ©QhzÑóGçºêJùW|=ÑJÏaÆñ;…·Eunèºõ9•NŸEWîÝ/xšÆ´ó“V.>8“ܧ¡ÉÄN´“j竌�æÍ\–ç>xtçÏÙŽ Íg_–hg ƒ¡«/æ-¨Ï›Âù¸‘2¨†õÏÿW-o¥hi÷c–Ì»» ›îèÅã‘øêõ^öd�@Ç.éNñÇiü·&Ï@í\7ÿ³+ÇH·C’§—Ѐ”äO=ÖPC˜3åóÈ´Øœ«ƒÇ�ýjÚѱ™ÇmbS²4ÁyNY�h·ŽÆðœ¦õ<ŠNZ+vãS{Ûö˜ÑG2þóÚˆŸ6ºŸÌ±« Ú:F ÿít0³G\„ÞŒtDkÈo»¿ ]åyJCží<çèåk¸ìºtûH‡þ±ûK!>qÁã×yF 'e†pG¯œS[[h'ÕN ²­¸vrC�,àI#�,&I#–rOÅÕNvr*«2ÚÉ} ´´ß–~þ³zk§Ü³Üø'UÐx),Þ[‘AËJ9 R+à† éS”šã<Ê6�¨Ù­Jk'}ŠR®Ýi¦v–UaµüñÚ)–™RÑN•´Ó$îü¿Ëz—øà´Kºýx` ­<{ѬuY÷ßt®å²õuÉSu�÷@ºùN;ÍgÿV¦ùi—tWV;/ǵk›S|‚}côz.)$Ú©#Ø#Íx0¯kãàŒwWx¹np�n «±vðmÐcQQɼýÂÝ}Pî�Ú¾í„Sy3ÍÉOšóhu�[^T¬2%\žXC°I$Ý_8³FqxSšþØbàÙ!åÂøãÙ}µãŽKÃYß¹Õ½È帆8Ï1ó�c»l§Û˜özÞ”¤kƒÂƒê„ìðϼ¸nQ§9C²nMؾÉaýÕØœª¡�þ‰“>·Ÿn fL¨}X²}x‹�äsÈ_ê±H�¸§­]Ûo ÛÇ; ë”ZrñO?H>8{O´Ûy�#̘ÕeÛuhg%kgAAAnn.P© vR«¡Ú)vcûÓø¤Z´S¦¶´žÕF;å:$W;I)íCÖ Ü®ídS¿pg! A-¢Jhgé&ПU=ÚI_jÎãµåF;KW˜ÜuÕŸ%ÙâÙN¥µózbç:ƒ7={¼Ø©¶[ªÌ„?o業œæfÛ\»Ãô ¯6 Ö–*£�Ϋ^Êh§EBþ[¡Ò•H©Œv^œlúN;÷tº-¸G´Ó@Dÿ([0ß±‘ˆû÷ÙË3Úéºîø—½~¾7ÜÆgÓÿª�vŸ4ŠÎ’ÉüÍ]oä6éÖë³1&-Æe¾Î�Ä·™CûõÞ’žúC7U°2%¾…oXPstŠ¿t;hn\�нœ�Ô?‚¤=ç`½Ð¥¼‘Áf)’ÇnÝj³`÷÷áQõ|7 Oi‚ódÙ¸…˜o¦£î3ì1ù{ÎHÎèƒ{LÝCk ™Þ)jêçþÛò²zÌÚ&)ªÚ)ùC@ÈnúHê¨IoÍ?'±ן¬ÛkîÐӚЄ ß{ûñ×äK··ë;ÁfOÉC²þ ÛwØÖ/´X›‹Gwïé43Ùª)ÚÉãñíTY;ÙPXj‰4Ÿü¤!8&xôfŒÌQåj'=VÙA¶eigõžRˆë“ÜLÚ ¤—hg2?W0c°ÊÚÉ}0’Í £`Ÿ¦ÂitƒÈ›ZfR­`ÈÌBÕ0TùÙNnÀY�#TË}¶³t=˪°ÇiãÓá�vúúúZYY¡GÞW;ß¼(ºzpj7ý&þŸ‹ÅguÒîuèfѽ [¼ ›g¼L53Ÿqçéó'c:èö‹o$Ù5ìžpâÞÃÿå,ÚÔiñ}¹Úù©iȾä$õjd>ó¢X|.–ÇŸœóìÅkª�/‹š5¶ülÁã;™³õLâΊi§øÎ*çÍþ|èjÑÓÇù.vmõe÷¥×«M´óyÊ ú#·ÉNïûw¢Uk¯mùE·ŒïôUÏ%7Åâ'[]›ô˜{¡ð~VrßoLcN½ªheJ| OOmÒ/ÉE*�n±¡�¸Ïv²ˆèî5�ÜW�!†“uô;Ïe^’Ì«®±Q­çi€óü3xœ¨ùüK’íß×éøuTv©£²Ï÷ô�úvõØ=kôJ|pŽ·Ú©ñÏvŽXå#�Üö ô7\v�SzÂÂ-ÉE#Æ9ßt›Öø§œ·� Ï*÷ï[—|ɆԞ<Ýî�€Ój ˜C;‰sA;•ÒN¹ ¨pÇÙR%¶#3÷›Rˆ=ŠYZ;Ùù©[Ò“È�Rˆúh§¸ä*ª-Á¢±ÚIz€õ¿L�“;·“XÞz*P;L+3N•»­QÚ©T4s•²´SÁ ±ìO>“­Ü sŸ³…vªS;É› Öí|/í¤ïÁŸÔÒnÌë´úÂ3iþ³‹«…v-õêhéóüxä¡ÄøöF8¶Ñ­£Õа[Ä®ü×bñ‹Kë„¶-tëÔkb9fý•Wò£�mùöm­[·qG�¥ç%g~¼/ÄT§v‡ø‹o§*ÌLþ��öõšZx/;ûL¬X;ÅâG§ú85ªW«Ž^K«ÑIÇT“!!3´©EqS|7Éîó�k¥«Ù<9™ÐÏPGK׸ÏÔtÚÜ7×R¼ÌÖÕnf°åïׯLÉoá—û…·JÎ?¸ËpH\�´:‘žã¾le³Q3�öÞ�†Óøý§Ø¤^Š íc•lç£A´³]Üeô»Úo„rÚy+æ�ßÛ»‡Ö8©ÃгRg;ó½o\×=·b÷¦4ì7®q誡Ùxù�«7üy¿qQ»ýNkˆ¶å¹Í˜^·o°~Xêéªá+æêOʈʹÚ;0°îÈøN‹O–Ò3ÍÒÎq‹fÑ’bó<}´ã¨Ÿ•Jõ®-m†‹j¹Ì´Ý,‰-GnZ¢ÓÛïSçñM&lóÎÔ�Çk%ÁX�ÄYõûŠô‚Ö{Hk¹naƒÈ9׆F�û¬g@íáÓ;,>)]1µ§Ø»ÏN¯�AˆvªC;MLL �ÐNhgµ³ ¥ªÞÜM¨?Þ—JL)äàà€�vâF(«�U+¡þh´³âÚÙ¶m[h§æh'€vÂy �ÐΪ¤�˜RHÃm7Ú ç�vB;5D;y<žíT �ä 7áÓá�vzxxàÙNh'n´õG �åj'ŸÏ'æ íD´ÑND;Q4ÑN¥µÓ×××ÎÎ="#HHHHH2 Úibb‚h'´Ú íDýÑh§*ÚéççíD´7ÑNÔM@´³\í4’í„vB;¡�¨?šíTZ;1ÈÚ‰íDýÑhçûh§¡¡¡±±1´Ú í„v¢þh´SiíÄ*ÐNÜh'ê�&@;ßG;ù|>¢�Ji§““S ééé¿9 9UYEäŠbU×V!‡pk›’’Rm´“õŒ&h']q‘-zYz=I¶†¤z½,wÑH¹M ™´=’·r×�”»³ºêÏ�H*kKRmn…‡¬9 Æú«Ö„ÒKtªkÑNh§¬vúúúÚÚÚ¢Gʱ�Ý^ZÅCŸÖÖmiíµ0ûɸP𣋠ä•ÜÝ).»)¹ðh-ç”—Jž÷v’Uqm?ù\«¿÷ø�7^Wí|²¼O#ÿ4ÍÐÎ#‡»Œ[³¬ÙÒ3Ñ2ëInߨÆ-¤¦ó„V ÇÆqò#S—èïš )Îó·çœÙºýDº¢�‚¿d÷�Üôk]éÛtÝØŒ(’ó×_ŽA‘µú…LJ Ñg‹Ú¿‹?*¤æà©–/Ë–fgÙŽ–.qÙ{vÿcäå…¡S¦×ï¤å‘ì¼ÿºÆhçuŸs8iû­}¼dÑ©¬^aÑ_ô m>ž«‹Ä<Ë‚\«"rÅ•Õ+W®TEó,ë;n¼MÐNb/T`ˆ¹¥‘ VD��åk”v*h-•i“O ©?é[Z+ò“ÛÏrëOÿ@ë_º±Ø斤¤Úrs �jÓNòkee…)_;­o“�×/Š®§Ïrjô•Ço�?bÎDvN¼¡êÑD;µFï–l½zzÿüæ€oµ;L;÷¦zhgºÿ×NËj‚væ9‡·ø%{ì{yCgôNçeÛyL²J½4þX¦µW´Íî·ù§Ouÿ™ÆhgäæEõG¯ð8zÉmr´~¼ä�˜Sz®GÜÖ w/ó}–§Ž8ô÷¸ß··îm·O3œ-;ÛÎ=ÂlÍ…ðÝš �7âd‰Ò±+—ÛoÏ'¨›6Y¯Í�È<7$z§c¶iÆ-ˆÞ¹êË ]3ò<ã'ëL:Å) X�:äÀµˆ´½¼Áá�·ÉÏ�vV¾vçĺ�*k'×ë¨ÔQ˜Ñ—ÌY쑺Ÿ“ò299™Ev`G‘ ªµt›]ˆe¶nÝšž–^‚f’j”¥�—X�ÒNÒÒ´Ûé¡=@ÚKzF¦>ªv�¤_ý‰ÃÈDŸ˜Å1¯£»‘m�ÒNM ¡Nn&ó™®ëÏú™ÔM¦V4Ô)·ªì�Þ„Ò®ö[€O<Û©ªv{Ý|»Z½Vùy‘·>À¡•^ÝúM:�þ5K½½CdÝ\[K¯•Ũ¥ç^�Œç—Ö ­š×«]¯©uàö›oÈ©Z:�ìßLçkÁö½4Úy>ÎÄÆoRïukë™ L8ñHœ.2�~X%Ý~í|tbî0󯵵¾le¸þ29ïÝùŽm�MÖ«ÿ�UÀ¶[òµSÊëc¢&_‡­7âúO�x±çįÿÙ8Š×Îç·ü×…þ<ÄL¿NÝæN³~´¾íUeigFj“Þs†œ’l�šÞ0!›SzÈlÈ·,ɶû”‰·››ÿÂ5ýÿú…¦hç�Áã›&_”lÜШÿ‘œZ䯅zóº­ø+,ë­ eQ…Ëu# j‚vîX®=|…·4lëØvéÕwEYY6îQF³wŽ>T*°yd{³ OjBòÝ&†ÿæÛެϜ!œZEgݤÝè0Õé�ühgåk§±±1´³"ÚÉDލ {¥¤9,JŠH&IË6¨vrs¨—²âNìüL ¹ÖÊrh•XNYÚId²ZÕµ“ö“|Öÿ¤+>^K w¸l顳L/¹z@lG£Ù*n5RÃ!M«ü8[YÎÆ�»ÊÄ`Yx°t‡“&”�j`˜?ËÄfKç@;Õ �îîîˆv*­�â?Cš´ˆ<)¾šhÓ^¸-¯èéãëûƒÛÇœŸ�ljš^ôüѹ„®ºƒRþ_O´Òs˜qüNáíCQ�ºn}FN¥ÓgÑ•{÷ ž¦1íü¤•džK…Î$÷ih21‡í¤Úù*#¤y3—å¹Ýùs¶cCóÙ—%ÚYÃ`èê‹……yËêó¦œ+[;ÅÏVô­ÑsIQ5¸/6×wßö÷ÆQí,Çýq�È÷Æ�_ 7^|ðàL¢“Þ']f߬´hçºùŸ�X9Fº’<ýÓˆœháÕÁãÆ~5íèØÌã6±©ÔÜÒ÷Ø,ȉ٤9ÚyÊjD@»u4lx€ç4­çNééÂwž6£Q`ªðtqfÌ_çFþ2—÷˙͆ΟQÃ;Ãì¡7ã×è&?ç½f][÷©6[¯–<ð�Yÿ$—ӚЄsŽ^¾†Ë¨éÐ?Öa‰ýcN\ðøužQÂÉè²s �•¦�¹¹¹FFF˜R¨âÚÉ u²€'�@2¢K¹§âj';9•Uíä>ZÚoK?ÿY]µ“´—4„5œ´‹5ó£>󩬳±g&™P[«BÚY:Ç¥–&¨ælr¯ª%T¨ZècŸÜüÒ9ÐNõh§@ °°°@�(¯�&qçÿ]ֻħ]ÒíÇû[h5àÙ»ˆf­ËºÿFü"Ź–ËÖ×%OÕuÞéæ;í4ŸYü[™æÿ¥]Ò]Yí¼×®mlNñ ö�Ñ빤�h§Ž`�4ãÁ¼®�ƒ3hçó}?”ò¬܈ã[·uìÑò3Ég$/oül­ïþÛSÚ ¿vÿ¨}Ê8CÔòÄ‚M"évøÂ™ÄÆr¿£ËøÎ-¨îèE.Åì]òãfo¢:¤�ÇÌŽí²�nÿaÚ+Êvoé£þñœ:±Ù¼sô¥çÔñ’wí~S{üž¯ ÚéŸ8é“qûé¶`Æ„ZчK}pKÓ‘+½¹™û×µŽM�Ôˆ[pÚÚ5°ýº}¼ã °N©%ö÷ûé鳩ív^/+ÚYÉÑN¬ÛYAí¤¢HµSîÀN*ŸT Õ®�¥ëPEµ“†4I«ishG±¢�÷̧ ÎFô€÷9ê¨�ÄÙ¸Ú¦®g#UÖN:¶™ûR-¡ÎŠ4�Íê¤ Ú©†h§½½=zD9í¼žØ¹ÎàMÏ/vªí–*3áÏ›‡yi+g…¹Ù6×î0ý«Mƒµ†¥Êh§óª—2Úi‘�_ü¹í«+‘Rí¼8Ùô�vîèt[p�h§�ˆNÌP0ß±‘(½lí|žæóuëè¬jp#HK?5è=g¹ÈÐ8&[òåCd`3‡öÜ˃~½–VšvÆnXPstŠ¿t;hn\�нÜÔØ­[mìþ><ªžïfá©[ã7¯wÜò·¤Hƒ´3ËÆ-Ä|3�mî3ì1ù{¹ã6n4·¿8¼™õOÈîƒ]¼ƒêOýS´Sbû!»igŽš®w\Þ�ÿ8Oï™ñî‰ÜÁÓVŒ8®!3Ù^øÞÛ�¿†:üáv}'Øì)yHÖ?aûÛú…¾3êÒ9ÐÎÊÕNSSSL)¤²v²¡°Ôi>äÉü�ÂŒ‘9ª\í¤Ç*;Èö}´“^÷£N€TiÚI#Ì´Û¹Î�ÝFÅF²Ya¸ñ4ZD„‡FÛh„Š%M{¶³t é${jˆv²H,ùYV SÆ3Õê¬HJ� VË8ahg í …]»vE�¼¯v¾yQtõàÔnúMü>‹/Îê¤Ý%êÐÍ¢{¶x6 Îx™.jf>>ãÎÓçNÆtÐìßH²kØ=ἇÿËY<´©ÓâûrµóSÓ�}7>ÈIêÕÈ|æE±ø\,�?9çÙ‹×T;_5k6lùÙ‚Çw2g;ê™Ä�¿Ÿv¾ú÷9ë}ÌêY%^«7âÍ>ÏýÖþ+çÍlÿM@Æ+q~²ƒ®C|ÖýÂË©¦µ>®ZË:Czj“~I.Òg;ÝbCqŸíÌ:ú�ç2/éh[ר¨Ö‹ó†G•øìôÙª~çùgð8Qóù—$Û¿¯Óðë(¹“£þžÒbò±ñõÉúÓ25åÙΫ|$Û×úú.»&ïÀ«}B~|¢øqVß•›ý®žilå5á¦ÛİÆ?å¼}TxîÐSòܺä˘ôrr �ˆvj°vÊ]@…;Ζ‘C™¹Ø”BìQÌÒÚÉÎOÝ’žDî”Bl(©bí¬– ¨pçmb�I»úãÍ'$V~&[¢sÔ ¨vjì*Šg²-ËÓ4g�­â™lKןÕ\] �¨Ü™çiKç@;+[;=<<í|/í,^‘¤–vc^÷ Õè�ÕgW íZêÕÑÒçøñˆd‚Õ;{#ÛèÖÑjhØ-bWþk±øÅ¥uBÛºuê5±³þÊ+ùÑζƒ|û¶Ö­Û¸£ÇÒó’3?ÞbªS»CüÅ·S f&ÿÎ@û‹zM-¼—�}&.O;‹?4?«£×¬Ó�¸wÞT‡qqš©Ù”K’­[smŒÚþ¯øß“?õk«[ÿk«QÎü:ÃR_~ÜÊ”ü~¹oPx«äìðƒ» ‡ÄõHã>™Æï?Å&õRä‰ Ã'ƶY|å]‘E;%óÕóXéyì∉QzÓ2¹ÑÚ˜Ó7É{eÔ±Ná?;|7¤6:óÌÀÈYÖ;ohÆê#§mFŽo¿öBØÎƒÁI.™%Œ.:‹øÿ5¿• ÚÎÈ”Î{3`ãÎa’¥Sò£ŽeºîÈÓˆ™lw¬Ðu]ävä²û´´cÒKÿbÄœ87ì‡Y7ÿ£ ÚY™ÚihhÈãñ �ï¯�Õç¬î_`þ~Ÿ~£FMŸùü¨•‘ûEœh wH"ÜUFJ65pÝβš Sn‹4gJ!ö¸)ÓH²A=“¶‹Õ–[5®{©TXmÙ_Jç@;Õ©�–––è‘r´ó£B´³]Üeô»Ò7âæì.zÃ×ÞzòävF¬¥žóªÂ�\Y%øã÷öî¡5Nê°â¬ÔÙÎ|ï×U2N2?põâ¦Ã‚?ï7Î j·wöMÒÎØœ<·Óëö ÖK#�C5|Å\ýIQ9ÿŒø!¶�`r#¯9ßoË“Ž°½äöiÏ€:®3;¯<¥1ëvFîÚÒf¸¨–ËLÛÍR�<}´ã¨Ÿ½»k½�ë$�ZÏüC$íÿ€Å uß}vjÌ09×<gÕï+Ò Zï!ÕæÈu DˆÈ¹64jÜg=jŸÞañi铨¥s �jÐNc)ÐNhg•ÖN.ë Û�­�U(Uõ&àhBýñ¾ôN;¬­­Ñ#ÐΪx#níëØF¿n½Æ<§ˆÝ·^ìʨkpæÇÓ6ÔM€v*¥�˜ÉV£´¨¦�•Y8ê�[€÷¥wÚ)‰°n§šµT…mCýq  �D;1¥�í$_°�����¸ Ÿï´ÓÇǃl¡�¸ÐNÔM€v–«�Ä9ù|>´ÑND;ÿ#ÑN$$D;?¤vbJ!¹‚�„„„„$“ �mÛ¶…vB;¡�ÐN$$h§*Úéíímee…A´7¢zG;‘�íü ÑNCCCh'´Ú íDB‚v*­��ÀÎÎ=íÄ�€v"!A;k§™™¦‚vB;¡�HHÐNU´ÓÏÏÏÁÁ=íÄ�€v"!A;k'ŸÏ722‚v¾¿v:99Õà�žž^ñ Ñõ?Ê*"W«º¶ 9¤FI>êú"•¯�¤9´Êj8ko|||Åo™âÙ¢—Ü4dÍ™E;Éntí«É[á0HÐNhçÀvv{i}Z[·¥µ×Âì'àBiþ�z,*�Wrw§H¸ì¦ä£µœS^*yÞ‚EŽ%?8µFï¨^Úùôü’A–áÇJeŸ[!°4¨¯ý�µh×mú…1WdÏ6ºõ˜ [zþÅÖÎ#‡»Œ[³¬ÙÒ3Ñ2E'r‡ýœÜxü®ñœE2û‰‚¤Ÿ�1ö¿çÃa� �UQ;±€Š ÚI†«‹Ä<Ë‚\K®V½¿vVõ…@|Ç¥nÉnGéV3igÝH,´uëÖV;‰FRá$òI$³´s–ÖN¨ ´³šh'Ù¾¯vZ%JDæõ‹¢ë鳜}åñÛã�X…3Q†�o¨z4ÑÎZ®;ªå�¿)<»yÒ@£:5>¼îiɢǿ [¶µêüƒÂÜšû§‘�ÏÎìhÐmÖÑ;E76¸µè½¬àCjgžspx‹_²Çþ±—7tFïô_Ð'¬˜Wo``�ÀߘvFÿ¶ÖrÅU¨ ´³ªk'PQY;¹†C|†ýaT&¼Æ¼‘…àRRRè©hì499™Ev`G‘ ªµt›«R4“齄Ü`f5ÖNÒdÚ:Ú™\Ø-`’I~²ž)+°¬²vÖw‰ðÞ}ŠJ&?"I¦”qµ“†:Kí¬’Úéëëkaa�y_í¤ÜžoW«×ʇbñ‹¼õ­ôêÖoÒiô¯Y’èí"ëæÚZz­,F-=' ¯=¿´VhÕ¼^ízM­·ß|CNÕÒidÿf:_ ¶ï¥ÑÎóq&6~“z·¨[[Ïd`‰Gât‘�ôÑ*éöÛhç£s‡™­­õe+ûÀõ—ÉyïÎwl;@èlÒ°^ýo¬¶Ýú¯hçñØ®=üæüâÕêÛx™¡OùÉ6 ]wÐ?œ�llÿ˽g;G6°L¢öþdEÏæá'> vf¤6é=gÈ)Éö¨Iá ²e¿¦o]\ç�v^îÙlÊV—ßažHÐÎ*©�¹¹¹<S UD;Y�ˆ { ¢9,JŠH&IË6¨vrs¨—²bJìüL ¹ÖÊrh•XŽL<�Q‘À©F;e|’B:‡uÕNóüÚI¬’hgéí²´“ jSá4’ 0HÐÎ*¯�AAA666èå´SügH“‘'ÅWmÚ ·å=}|}p{ã˜ÓÄwL�BÓ‹ž?:—ÐUwPÊ¿âë‰Vz3Žß)¼}(ªsC×­ÏÈ©tú,ºrï~ÁÓ4¦�Ÿ´òØp©ðÁ™ä> M&æp¢�T;_e„4oæ²<÷Á£;Îvlh>û²D;k ]}±°0où@}Þ”s%µ³ä [—�ÕF;¥-íÞ`ÌÁ’ywtÓ½£x<ò_½ÞËžìèØ%Ý)þ8�ÿÖdâù¨�ëæ6båévHòôO#$#…ÊÖÎü¨Ì‹¾›¶µ÷šd¶ò ÚYu£�Ä<¡�ÔNn¨“d½Êíaõjg�ä­˜R ÚYåµS ØÚÚ¢G”ÓÎë‰�ë Þôìñb§Ún©2þ¼y˜—¶rV˜›msíÓ/¼Ú4XkXªŒv:¯z)£� ùo…JW"¥2Úyq²é;íÜ#Ðé¶àÑN�˜¡`¾c#QúH;‰OEgÉdþæ®7r›tëõÙ“ã2_çNâÛÌ¡ýzoIOý¡Š*X™ßÂ7,¨9:Å_º47®FèÞè÷ÑΜ[£'O¶Ø‡A‚vVIíäñx|>Ú©šv²¡°Ôi>ùICpÌé!Ì™£ÊÕNz¬²ƒlÿ›ÚÉõIn&m2é%Ú™ÌÏ̬²vrŸí$Yî³�\íD´ ÚYåµÓÛÛÛÊÊ =ò¾ÚùæEÑÕƒS»é7ñ?ø\,¾8«“v—¨C7‹î]ØâmØ48ãeº¨™ùøŒ;OŸ?8ÓAG°_|#É®a÷„÷þ/gñЦN‹ïËÕÎOMCöÝxø '©W#ó™Åâs±<þäœg/^Sí|yXÔ¬Ù°åg ßÉœí¨gwVüÖÎç)ƒê�Ü&;½ï߉V­½¶åÝ:0¾ÓW=—Ü‹ŸlumÒcî…ÂûYÉ}¿1�9õª¢•)ñ-<=µI¿$é³�n±¡�Êy¶“¥|רønip$hgUÕNÌd«¬vÊ]@…;ΖŠ(±™¹Ø“–ìQÌÒÚÉÎOÝ’žDî”B,ЧX;eFÿ~Ôyw+Y;I°æÈ9¹s;‰å­§òµSÁL¶ŠÙB`� �U^;}||ºté‚)_;é{ð'µ´óº­¾ðLšÿìâj¡]K½:Zúü?yHrîì�pl£[G«¡a·ˆ]ù¯Åâ—Ö m[èÖ©×ÄrÌú+¯äG;ÛòíÛZ·nãŽKÏKÎüx_ˆ©NíñßN)T˜™8ü;í/ê5µð^vö™¸<í”ùälw¹úhgfhS‹â(¦øn’Ýç×JW³yr2¡Ÿ¡Ž–®qŸ©ét°í›k)^æ ëj7³ Øò÷ëŠW¦ä·ðË}ƒÂ[%g‡Üe8$®Gi“,ùlgôéüØìëÁ›W·‰Þ‡A‚vVMí455…v*¥�•jœ¬LíTKeä~¯ïÁ]·“(% {Rç䮡"ó ÚYå£�X@åýlç£A´³jjau»Êiç­˜?~oïZsà¤+ÎJGØžùÞ7®ëié¦_¿ –Ô<Ów¹Å~ãùCÓØß„A`� �UX; ÐNh'´³"Ú‰„íü�j'yˆvB;q#TÐN$$hçM;±€ŠFi'€v"!A;«’vz{{ÛÛÛ£G4Övp# �HHÐN ÑN✘ÉV�v’/XHHHHHÜ„O‡wÚéëë‹)… �¸ÐN$$hg¹ÚIœÏv"Ú‰hç'ÚYÕ›€[€h§fig@@€µµ5zDF0�����d´“'Ú í„vB;Q4Ú©´v …BD;íÄ�¨öÑNÔM@´óƒD;ÍÌÌ �ÐNh'´õG �Jk§@ °´´D�@;q# �¨?šíT¬�¦¦¦ÐNh'´Ú‰ú£ ÐNU´ÓÛÛÛÁÁ=íÄ�€v¢þh´S±vC;•ÒN'''îÒééé¿9 9UYEäŠbU×V!‡pkKOU�´óÊ•+rÅNv 9ñññ¿ee CSá4îº�ÜDWé$;p3{$o-�©^ç)« léQVÄ�Hb«�Êm²ZêOkE—Kå&RCV[VJ×Y%?5J;Ëj‚Ü"z_ÈOh§úµƒl¡�¸ªhç‘Ã]ÆŒ­Ù?Ölé™è’Eã¶olãRÓyB«„cãXþés.ñs Ò‰;®Îó·çœÙºýDº¢�™ÕDÿúË1(²V¿pƒIi!Òœ±K>{ûQT£Ç´žálQûwñG…Ô<ÕrãåE§�tÄjë«7ãÉ¥,m2TTsàDþ¢¬(MÑÎë> æ4pÒö[3úx¹E7Ãvîê8Íf´SmÚ‰)…TÐN"0\]ü æYäZqÅj°¨‚ï¸Ô-Ùí(Ýj&í¬‰…¶nÝúÃj'j_Ä܆¬9 c ¬ˆ¨&W!4*Ô¦¸ Lr˜3Ó ²§†h'©­'ùɪ'S[Òº©3Û¹òë¯ZdŠØß �ê×NwwwôH9¶³ÛK«ø¯ŸŸÖÖmiíµ0ûɸP𣋠ä•ÜÝ).»)¹ðh-ç”—Jž÷éªÞÜ?ØÖÐòÚ]Í´óéù%ƒ,Ã�•Ê>·B`iP_ûkÑ®Ûô cþ®Èžmtë50¶ôü‹ª�yÎÁá-~ÉûÇ^ÞнӹEÙv“¬R/�?–iím³[š™u¦wPXÁâ~ÛÎŽ;�¯ ιyQýÑ+<Ž^r›­/y/~[”ï³Ú©švr ‡ø ûˆ’ ¯1od!¸””z*;MNN¦G‘ØQdƒj-ÝæªÍdúD/!Ü«öÚIšL[G;“ »L2ÉOÖ3e–UÖÎú.ôÛ?Ñ0™P³8æuDÔdSºË æ­IDATà< šÀĦtl–´¨òµGnýY?“ú”C&=O÷aòÆtNÛPV´S#´“¼a�í{i§U¢Dd^¿(ºž>Ë©ÑW¿=þˆU8eØ9ñ†ªGK´sÐÆêí|Sxvó¤�Fuj|6xÝÓ’E�¶l=jÕù…¹?:4÷O#;Ÿ�ÙÑ Û¬£wŠnlpkÑ{YÁÔÎŒÔ&½ç ‘ÚרIá ²9¥‡Ì†,pË’l»O™Øq«Dä|'×òZë}RsFxÞ<.°iòEÉöÁ �ú/™Íqž,*ƹcf÷?V2À¸n~ýñ'hB¨pÇríá+¼¥a[çàÀ¶K¯Ê;ð”•Ûô^·b�lkê¾Î_Eì2åûCšp òÝ&†ÿæÛެϜ!'Ë-Êëé íTg´ÓÌÌ 3ÙVD;Y�ˆ { ¢9,JŠH&IË6¨vrs¨—²bJìüL ¹ÖÊrh•XŽL<�!#¥U=Ú)ã“Ò9¬¨v²˜çÇÐNò½Ÿi$w›‰Ía¶@6Ø U ÑNÅMP`˜šãlÜÎ,«cYmégš£.mSª eA;5E;Ñ#ï«�”ÛóíjõZùP,~‘·>À¡•^ÝúM:�þ5K½½CdÝ\[K¯•Ũ¥ç$áµç—Ö ­š×«]¯©uàö›oÈ©Z:�ìßLçkÁö½4Úy>ÎÄÆoRïukë™ L8ñHœ.2�~äY%Ý~í|tbî0󯵵¾le¸þ29ïÝùŽm�MÖ«ÿ�UÀ¶[ÿí<Ûµ‡ßœ_¼Z}/ó} ?Ù¦¡ëúÇ€³“�í¹÷lçÈ–IÔÞŸ¬èÙ<üÄÔÎuó?±rŒt;$yú§8Ñ«ƒÇ�ýjÚѱ™ÇmbSˆžNoß?ø›°„/�ƒuƒ6ÈŽhU�óœ²Ðn à9Mëy¤Äþ1�ùË\Þ/gbJšÒȉQf)×5!T:F ÿít ³G\I+›¬oì·-LªÐÝc[ÏÏŠLß×1.-\#Ìÿœ£—¯á2Ú™G:ô�uØ_n´SÍÚ‰T>ˆvrC�,àI#�,&I#–rOÅÕNvr*«2ÚÉ} ´´ß–ŽmVïA¶rGÌrãŸTAã¥ÐýØÚÉž™d#WÉ6 µçÑm{Ï&Èä³1«UE;¹qBjþ2cY¡�ÐNU´ÓÃÃÃÖÖ=¢œvŠÿ iÒ"ò¤øj¢M{á¶¼¢§�¯ïnosšøŽ©QhzÑóGçºêJùW|=ÑJÏaÆñ;…·Eunèºõ9•NŸEWîÝ/xšÆ´ó“V.>8“ܧ¡ÉÄN´“j竌�æÍ\–ç>xtçÏÙŽ Íg_–hg ƒ¡«/æ-¨Ï›rNF;9ô^ö´úh§”¢¥ÝŒ9X2ïî‚nº£w�G>à«×{Ù“=»¤;ÅŸ·ñßšL<ÿá´3jyb Á&‘t;|áLâ?c¹ßÑ�e|çTwô"úTÞ®U:γºïºqøÐw®~_%æh€ó38¶Ëvºý‡i¯(Û½%ö÷œ:^òæ~S{üΜÙytòðS1BÕ?qÒ'ãöÓmÁŒ µ¢—>J”4­åÂâÇ>£Óvµ\Ïo­÷i 8Ÿ¶v l¿�nï8(¬Sj¹EÐN5k§‘‘‘‰‰ ´Seí¤VCµSn,‘Ê'Hhç‡ÕN¹ÉÕNRJû�õ*·‡+G;©^R[àî@DT-£mU‹vÒ‡ ¹�}²1«UB;IU¹’IŸêT£¶A;«U´ ¨¨¤�&qçÿ]VòJ»¤Û�÷¶ÐjÀ³wÍZ—uÿ�øEŠs-—­¯Kžªë¼ÒÍwÚi>³ø·2ÍÿK»¤»²Úy9®]ÛØœâì£×sI!ÑNÁiƃy]güG¢�ÅV٠Ǣ¢’yû… ºÿJûU|ajû¶NåÍ4'?iΣÕýuFlyQ±Ê”ø¾aAÍÑ)ÒA›·‚æÆÕÝË�UhìÖ­6 vUÏw³�HÚÆ…5G®õ}­¶/ZýΓeãb¾™NÀ³Ï°ÇdÙq§Yÿ„ì>ØÅ;¨þÔ?ßeîYc0á ™�Gbû!»épßQ“µåLÔ”×3`VßâAÂùAkÖ9,Þeå7N7â÷`�hÂ…ï½ýøk¨Õn×w‚Ížr‹ �jÖN✆††ÐNÕ´“ …¥–HóÉO‚c~HaÆÈU®vÒc•dûßÔN®Or3i“I/ÑÎd~®`Æ`•µ“û`dég› §Ñ"6� ÓO!ªñÙN�;’|®gª%ÔYä§Ü^åf !�Õœ[   eA;5B;E"‘��zD9í¼žØ¹ÎàMÏ/vªí–*3áÏ›‡yi+g…¹Ù6×î0ý«Mƒµ†¥Êh§óª—2Úi‘�ÿV¨t%R*£�'›¾ÓÎ=�n îí4Ñù æ;6¥ÿ‡´“ø¤Qt–Læoîz#·I·^Ÿ�1i1.óuî$¾ÍÚ¯÷–ôÔº¡¨‚•)ñ-<=µI¿â™uÜbCqŸíÌ:ú�ç2/éh[ר¨Ö‹ób÷®mÐ÷—aÒ Û„å‰_Ĥk€óü3xœ¨ùüK’íß×éøuT¶œ£¢×&ëOË|`üiFû 74eõ‘˵G¬ò‘l_ëèo¸ìšìî6 þ­8 }lW«€­¡’»s¾wP¤ùfM˜Õé¦ÛİÆ?å¼}TxîÐSåA;Õ¬�Ä91ÈVYí”»€ wœ-Qb;2sÿ°'-Ù£˜¥µ“�Ÿº%=‰Ü)…X ï?«�¤XÿË9¹s;‰å­§òµSÁ4°Dçh�­ôì©ì( ŸÉ–Û–²Æ¬jò4°¥kK¶ÙôNš£�JÍd íÔ í @´S í|ó¢èêÁ©Ýô›ø|._œÕI»KÔ¡›E÷.lñ6lœñ2]ÔÌ||Æ�§ÏœŒé #Ø/¾‘d×°{‰{ÿ—³xhS§Å÷åj秦!ûn<|�“Ô«‘ùÌ‹bñ¹Xrγ¯©v¾<,jÖlØò³�ïdÎvÔ3‰;+þkçó”AõGn“�Þ÷ïD«Ö^Ûò‹nß髞KnŠÅO¶º6é1÷Báý¬ä¾ß˜ÆœzUÑÊ”ü~¹oPx«äìðƒ» ‡ÄõHã®Þ‘Æï?Å&õRä‰ Ã'ƶY|%6'ÇÁ#ðë™ÇB�g÷Œ6߬ÏFFn\XÏc¥ç±‹#&FéMË,€�Î<30r–õÎïBpÁ \5e„ªd$ªÍÈñí×^Û™b08É%Sö�È•IFÄùéËÃ[¿”ÐgßÕ æô‰6ߢ“ GïX¡ëºÈíÈe÷i?hǤO(¿Ú©þh'Ù*¥�•j`‰•©�j©LYJÆ]Ä’ÎÄ]è‚;6’î¬QÚVV¸õçŽÌ˜UÍYw”Å`ÉëaRs×Û(Mn‚L‘z›íˬۉhç{i'ýÓß'µ´óº­¾ðLšÿìâj¡]K½:Zúü?yHrîì�pl£[G«¡a·ˆ]ù¯Åâ—Ö m[èÖ©×ÄrÌú+¯äG;ÛòíÛZ·nãŽKÏKÎüx_ˆ©NíñßN)T˜™8ü;í/ê5µð^vö™ø¿¬�™¡M-Š£˜â»IvŸ\+]ÍæÉÉ„~†:ZºÆ}¦¦ÓÁ¶o®¥x™7¬«ÝÌ6`Ë߯+^™oá1üÞÞ=´æÀIVœ•:Û™ï}ãºJCæ®^ÜtXðçýÆDíö;MW˜Ümêúy¿ñ­ŽŽÓmËs›1½nß`ý°Ô1Ò‰RÃWÌÕŸ”•sÉ9,ìÓžu\gv^yæÝ�Úc;xÓ£s4G;oEîÚÒf¸¨–ËLÛÍR½<}´ã¨Ÿ/ŽrÃ%f®ó»Å0ÿñžŸÔx°èóþZÎ8ª)M¸æ‘8«~_‘^Ðz©6G®[Ø ò@„¼¢ØœK=|è§zÌÚI§Â*ÐNhgµ³ ¥ªÞÜM¨?Þ—Jh'¦z?Ûùhílwý®ö¡¤vV­„ú£ ÐÎíÄ [ÍÑNí„ó@;¡�UL;­¬¬Ð#ÐNÜh'ê�&@;k'ÖíTìHHHHH2 Ÿ%´ÓÞÞ=¢±¶ƒí„ó@;¡�¢�¦R �€ÒÚ)‰Ñ#2‚�„„„„$“ �Ä9���¡�€ÒÚéíííàà€k'�ÇC´PQ;±€ P®vòù|D;U´3 ÀÆÆ=(ÖNžh' ´vúûûC;ÚYPP�››‹A¶€ŠÚ)0ÈP �lJ!>Ÿí”ÖÎÑ£GÛÙÙ¡GÅÚillŒA¶€*Ú9fÌD;€rµÓÔÔÔÐÐÚ (­�îîîVVVè \íij�€*Ú) 1ÈxíÄ [@íôññ±··G�еÓÄÄÚ ¨¢�X@(W;�ŒŒˆyB;¥µÓßßS åj'qNbžÐN@ií vpp@�еÓÐÐëvªhg`` f²ÊÕN3)ÐN@iíôôôÄL¶@¹ÚÉãñ �€*Ú)lmmÑ#€bí4‘í”ÖNh'P®vš™™a@í …]»vE�åj'¦TÑΠ  h' @; rssMLL ¡�€ÒÚ)0¥ @;i´ÓÈȃlU´ÓÇÇÇÒÒ=(ÖNbžÐN@ií Æ”B@¹ÚillŒg;U´3$$Ú ”«�X·PQ;ýüülllÐ#€bíäóùÐN@íè@±va�- Šv …B+++ô X;‰sb@íôòò³�@¹Úiff†TU´ÓßßßÞÞ=”«�X@PE;}}}¡�@¹Úi,Ú (­�d ¼�va�- ŠvbJ!à}´“Ïç#Ú ¨¢����d ”«�FFF¦¦¦ÐN@iíôññ±´´D�еÓÄÄÄÌÌ Ú (­��Ïv ´³   77רØÑN@íôöö¶¶¶F�ei'‹vbJ!@í˜R(W;ÍÌÌ �€*Úéãããàà€ÊÕN>Ÿí”ÖN‘Häèèˆk'qNCCCh' ´vº»»ÛÙÙ¡GÅÚ‰TµÓËËËÂÂ=(ÖNccch' ´vúúúÚØØ GÅÚiddíTÑNÌd ”«�<ƒlU´ÓÛÛÛÒÒ=(ÖN>Ÿoffí”ÖNL)”«�˜RPQ;ýýýíÊÕNbžÐN@iíˆvåj'ŸÏG´PE;ýüüºvíŠÊÕNÌd ¨¢�B¡ÐÞÞ=(ÖN###�í”ÖNooo, (Ð΂‚‚ÜÜ\h§b®\¹ÒºuköÒÉÉ)==ýƒ_%%%…œYµc?R•”¯�˜RP¬�lJ! ²­L팗òž;“K“ @;@Cµ3((ÈÖÖ=R‚;óí¿�»ôöE’ý·3/}„«Ü]Ö·KÜ9•ÍŒh;lã+Ü(¨LíÄ**h'�lÔ�B’†+éKr-bÒ—d²+"»ÑCè9i´“l°�e.A¡/©ˆ’Lv*h'¨G;ýýý-,,Ð#%¸�de2ñüÛ‰V&qç+öI<«³ã¼;ï¹ó×aå}�9ÖlPÊKÜ(¨Lí$ÎiffíTV;‰ï1¥¤ÚÉvcáGØ$Û~~~ì ,ÚIí”:$ÓN™ð&{ÉŽ¢{rkíµi'Py_í|²²Ï·.þN­t´u Ûû€”ìòj×g¤m£:�â/ˆoîٷЩû¥a¯¸#…dçcS[Ôý¬F ËŸníò¬CÿÊÚ1þñaÿïœÜºÔáÅd?YÕGS}yn™ëw_Õù´F�c·Nï@÷ÕõÞ+~š3Ø·�êim>jÕ%‰dþ½IØù+m�–Ýû[7€v@%k§‘‘ŸÏ‡v*@F;‰ò1Kd¥D;™[²°$�f’Ÿd›ì#£�ìÌ\픹4ÕNznx“]ƒl@mÚéíímmm�)_;-ëQçÛÐmy÷o¬ð¥ÓòGbñŽÑÚíCÞzXôèé.ÏV}æ�-ø·0{šMç—ÄE´¶žuåyñ).ÏøÎ.é¶t3ͧ‘±ÿ®ë‹>''”\¥hE߯œ½øøuñÎ] DÒÏÄsmMývß|üï­žËƧ¯÷ ¾ê2õäýÂëéS쵡�PùÚI€v*ÖNêu4>ɵD¹ÚIÕ´,}-W;ËŠvRk¥ Ú ¡�^^^˜ÉV–¿vmö×[gŒkg>ûo©v»èõY�ºü|S¢�ÍüÓ¤ûä'X°?­Öh Ü/ß90}¸Íw^‹²þ•ÑÎf£wÓŸ0;²M·Å…ï.δó€¯þ»“v™}ãî|;㘳Ò}0È*_;±ng¹°g5 Tðk'w’Ϥ;кÔ'Kk'÷ÙNñÛ §Ì³�Ü*{4· Ô ��ÀÁÁ=R’“c[6¾2·à߇yÛ…FM3ÞpµóÆ��;'ÞàjçËT·ƒ—^*zù¦Ä—•ûëéÝòZ|íÇÎFáÇ ?y%G;ï-èöÕÀ¥—Y´s««ÎÀUw>yzuv—ö¡Üþ÷íÔA¯v�Ò·ŠÏ)zrûXœ}=h'T²všššb@í ìÚµ+zD†‚#?0k\·¶¶Á·ƒg‘<È)~²²W»)%[ù?YZν%ïòlt˜îÿúêF¡Ms�Z’?¦~¿ðþÓ5ÈÆgZßX�Ý}�Xé‰é�¿¬U·Û¼›âÃ~­=÷Ñc¤'¼Dþ=‘Ø×HOrè×AäûË͵ÛÖþ¢yxºøÑ_? l×HëSR¢ï{€xçùÅ.¼/µõM3º3Ù@¥j'ŸÏ744„vJk§§§'PÊÕNL)¨¨�B¡ ¨åj§‰‰ ¦TÑN‘H„™l€rµÓÐÐ�˜'´PZ;íÊÕNâœfffÐN@ií–––è@±všššB;U´ÓËËËÎÎ=(ÖNL)¨¨�cÆŒA´P �¹¹¹< ¨ªh'Pk'd‹h' Švúûûc�-P®vš˜˜ Ú ¨¢�cÆŒ±··G�е“8§±±1´PZ;===­¬¬Ð#€bí422‚vªh§P(ĺ�@¹Ú‰A¶€ŠÚ`ccƒk'qN�í”ÖND;€÷ÑNžh' ´vzzz"Ú ”«�FFFX@PQ;íÞG; ÐN@ií–––è@±vbJ!@Eí …X·xíÄ*€*Úèàà€k'�ÇC´PE;G�mgg‡kg›6mª\´sƒJà¾Þ±?°vzxxtéÒ·P¬�Ä9ÿ ÑNh'àí÷Ãk§P(´²²ÂmÊÒ΂‚‚ÜÜ\##£ÿº�ÐNÀÛï‡×N, (ÐN6¥�©©)´Þ~•ÖNwwwD;€rµ“ÇãA;àíWEí´µµÅmkgÛ¶m1ÈÞ~UÑN//¯Î�;ã6еÓÄÄS ÀÛ¯*ÚéîînccƒÛ (ÖN>ŸOÌÚ o¿Jk§P(ĺ�@¹Ú‰h'¼ýª¨��Ïvåj§‘‘‘±±1´Þ~•ÖNKKKÜfÚ í¼ý~íôööîÒ¥ n3 X;y<ÙÀÛ¯ŠÚimm�Û (ÖN###>Ÿí€·_¥µÓÓÓ ¨åj§‰‰ ´Þ~UÑN777L)”«�†††x¶Þ~UÑN/// Üf@±váÙNxûUE;===1¥P®vòù|�퀷_¥µÓÛÛƒlÚYPP�››kjj 퀷_U´300ÐÎη(K;, ¼dkee…/€bílÛ¶­©©)W;ð>Ô …=zô°±±±³³#?»tébiiiooO6ˆŽZXX�—¶¶¶¤ÔÚÚš¼$û�Ÿ¤ˆltìØ‘l�"²ƒ…ºyIŽ"E¶RHfçÎ�Éɱ”B_ÚH!"/ÉQ�:u";ÐëÒ�Y&­9)%?í¤°º‘Lò“œ„lÐsÒCè>VRèiUÉn4Ÿd’�ÉOzzQRJv£õ§U%W¤¥ôŠôäô@Ö|v º�V€ô$7“Ö�¶‚{EZÚ‡´V´”ÞÚ«´ÉKZJo»eìä´ÃiÉm=9)¥E¬´-ô´´£hÝØØ�¬�¬á¬ª¤™´ˆ��¾¤5gõ¡ý@dU¥{’Ø!ôŽÓÓÒ&³ûHUlßB/ÄNÏC7ÈK’ϤËíUöKº‘Ü)zßi)û5¦¥ô.ÓΤÒºÑ.å6Š[UÚ|šOï½qô*ôîк‘Vãÿ þâÿ &ÿ$˜››“ÝdÙུ3$$$((ÈËËk̘1#FŒdgrZò“dÒ‹ÒªÒzÒ+ÒLr~r)bW!¥d’I÷d‡ÓÝèIè�¬ˆV€ô­-9�îF§RDNÂ='íyÚjr’É*I÷!¥ÜiÓèKr¹"Ù�ôɤ=O+C¤�êÁjNö¤¥ôêä%í%Ú�¬òôŠôä´ª´Cèþ´ªdz ûÝ 92W¤­`¿T´btOö @ŠèyÈ-b¿Tì@z_èKrH¿~ýè…hUÙ/*í%ZÚ¬Eô@ÒoäXz7Ù/;)åþnÓn¡ÍgWä¶‹ýß¡7ˆl�Ncí¢§¢¿ôBô'ýmÁÿAüÄÿÁJþ?H»—\bøðá®®®ä“qذa...d333h' ,ÿâaj=7Ø«µIEND®B`‚pdf2docx-0.5.13/docs/images/pdf-converter.png000066400000000000000000000275341517510251600210100ustar00rootroot00000000000000‰PNG  IHDRöxÛVÞ pHYsÄÄ•+ IDATxÚíÝ{l#ùaØññEÒúGÛ¼äð5Ã÷C|ê½+íC+iu»{OÇwÞ;ÛwgÝ�Ï«U«&q›"½ú�iÝV@‹ÆúÇwî:ù§H¯(RŨeûÚ  ¶uãæê ØÛ\{€ ÙE€þUGýÍ�"‡œrH‘ÒÌOß~Ø£ø’sâ—£G#¥’LWôtYK‰!NtN·ÆŒ–žÑ2b”b¤‹­¡‰‘)êÆ(h™|"�O$ó =×ʼn‚q:•ÿji1rí‘1‡8'Õ7r s§Ó]ŒÖ’Å]¤ 'C|Yj�iœŸ6ÎL¤òí!¾4†q§ñd>ž2O§sñT.–ÌÆôlTÏÆ’1¢º1Ä™â¢x*OZ†ø2•�§³Qq"�I¤3âye‹b¤“Ùd"¥Eôt̼(’ÌF“i5!–YÏ•k¹cQѤ¸¯œ¹Ø\ÌøÒ±dç„qi,eísòæ�æââk²¨e�›È´Vr©5©ök@ˈ'^ˆ§ÄfôL9™­$3�QM¦Å'Ê©l%•3GV\§,®©e*ºÙŠ&N§Å¿Õd¦žÊ4ÒÙÙLn.›ŸÏcNŒl~6›g¶Æ\6·�Ï/Œ±T(,ÿæ—‹ùËÅ•RáŠ8QÈ-²Ëùìb>'ÆR^\!·dœ™[.æÄVJÆXm�ÜJ)'N\�)\-Vg â"qc9Ec±­å#o,Ჸ¯‚¸ ±„âÕò̵Jyµ\Z™)®ŠQ.c¦´Z*®Å(ˆqEŒBÞ>.·vAŒËÆ¿ùeó^)ŠÇ“�äZ¹hŽÂõ™ü�™ÂZ¹x³"Fi­R\3OlÔË·fk·ç­ñÔ|Ý8±Ð¸³8{·5wêÏ,Í>·<÷ü•yc\ž{áÊügV>{}ùsë«/ߺvïÖµ—6¯Šñ¹[×^Þº.Îã1¶Ä¸úòæê½�ñïË›âôÕ{«Ÿ»¹òâÚ•×V>{ãÊ W—ž[Yxþêòó«ËÏ®,Ý]š¿³8'Æí…¹­ùÙ[s�[s³Æ˜ml6ê›�ÚF½º^«Ü4Ǻ8mŒÊÉ¿•�FõÖl]ÜöŽXÎÒ¼¹�†¹�úæ¬yssˆ›³5qÍ­¹ÚÖ¬1žš3žø�…Yó‰Ï=½4ßâ!Ý]žúò‚âÄ�%ãáµÿ]ןWxzyá™Ë‹Ï^Y|fyáîâ|k âô³—ÅùâÄÓKÆh�óôÒÜÓËæ02k®ùÙ;â1/Ì™·�'¶æÄÿ‹Ù§NÆü¬x"âL1nµŸHU¬ ñ\¶õÛ³�ö·2Æì–ñÔZK˜7o­ÉÍÙÆ†�úº1jëu±�º1f{.º9ÛµõÙú†Xóós[‹óO-.l-̋ӛæÕŒ!n^«­×ª7­£^]ë7µ5Ëh-|­÷Lã|q¿bÔŨ­Õª7,c͵µº1nÔ«×Í!N(â¥~ �Î�z…Ā̉ϲ"@ÆÄWI<Èšø‰y·â+:‰ ]¯U•Z*çåª?hþñ_ÿÍæ§þÊw;ãoýÖGâLV"øw+ÞËDÍÕüÃNÙ•Ýï~j·ú�oü.ë|™øšRÑ3^ú^ú»¿ó/ü‡ÖóÿÞ÷?nU^\�U ~³Ö¨+µTvhßWO"þί}°ý�~óír(Nˆ/úg¶j^á…÷>dmœt–�DQÇ{gŠ‹Æ[æM‘ø¿úôƒæ‹|ÿÌÏÏ8ýáÇË?ÿîWßýž¿ôO l}í/¿øõßþ¯ÿë£?ú¿­mùß~ü'¶n+‹ûMû™ÝsÝÏwXœã¥^–p:ÍýÅ).À¹$~Œ‹¦ôHþÜÏülß�:ž9ÚV|5™v»¸õùê;ÿüGÆ;ÌÖ×¾úÍ#åÖßãð?6¿û£Ç✕_ü–±iÿ��Œ«}ç#§æ¶[hÔ±}²{f;ÊÛ‡Çýç;vÿðxà5Ý–<±¼w—×}0S2lm�+ñö Ÿ²ïÃolž¿ñFÄ¿ýA;ñ[_ã;?üb£þýÿßÅéïÿÞüÛ�þD\óÏÿÂ÷†5·uº7^F:[åô5q›¡‰·.y2�ß><Ëÿ×$¸p‰·fýô}ïLÔ J¼òÜ?86§à­‰ÿÅw�þÆ·¾ÿ“'zùÞ;úÏ�ÿßOúù_ÿ±¸ò8‰ïÖÓ{ÔÄ5{ë¶äž)Ë-»çnºü˜1´ðK6o¿¿m™Ñé½}gùÖû?¹`{{Q±j]âzÍm^¢€L‰ïTþô}7?ÛP*É”ÛÅÊ+ßA·'¾5Dâ[— ›¿;8ñîµ&¾¿l#nßZ²¥õݾö.§õÎáôþáöcƒó’-sD�Ë-K=9iy´‡Ûæ)ãÚݳÞ¯ ’2‰?“Ä›)ÿ7ÿåã|øn‰þïü³Ö§²é—>p, -Úö;nÝ»n3»_Éeɶmhã Ç s· |·­xÇ%»þìrRýþMøÎêqÉú�k`¢Æ™1_Ö]ÿËæüË__œ^ýê·¾úîQk�šÖøŸøv¾ñ[ÿýßií79øãV×3G˜‹wÝ–²äÓ'Þ­ñ#$¾ýè»ÏÁa‘?àš$IüÄ?n5~õ©¬»ÎÅÿàÃ�[ìâ„øòÞ×ß·nÅ¿ómc³ý»¿ÿG­�&ü¿ÿtôÄ[&:†fËmÞÓ’­Ó)Ýém¯5öú·÷¨q\²{©�ùsëO3öàtÃ!× Câ§±Ó¤qƒ‰7¦r~ù×­•~õ_ýð7þÝk�~ïè÷>µû¯Eßÿê¿ø}3'öÙë¥C²Õ7]átU·%[oÜw�·ºNYAç Kv+uß~—½ ´M¿´\ÿÇ­LÔ«Ä¿ú4<ñ­)šVåʼnw~탇ßÿð‡ÿIœXükßT^y—€?yJ¼°ö7¿m�¢i�ç÷Ý·ßO¯g§ò¡Wï!ñÂ~,¶Üö¹_iõý/¼ð+ï¼ÿ#§ùw@ÐZâkÊŒûN“ð�52'¾¤‘x ñ€ %>ÉŠ9_d+dt³^5ÿ@:ëõŠRÒÒ¬�ÏM#ñ:‰_c+$ÞŠg.¤´n|Üš ñ@â$@âÓb~ÜJâ@Ê­ø[ñ ©�F�Ä€œ6çê$ä´5ßPJ$dôÔBC™I$Y áV¼H|•€¬‰_ÊY Ÿ§f•µZƒò¹½8§\+WY Ÿ;KsÊ\’ãÅ€Œ[ñ"ñ•¸ÆŠ9_N�x�ÐS‹³J)¡³"@>·æëJAãWŸ@B›³5‘x~õ $´1[UŠ{Ô€Œ‰oT•’–aE€|Öëe†Ä€”[ñõªH|–&¾Aâ@â­ø²Nâ@BëLÔ€´‰¯UØ£$N|‚Ä€Œ‰¯‰çWŸ@ÒÄãÀH< X‰/ÄH<Èšx¶â@ÎÄW•<‰9_Sò $@â$@â$Ðr³V%ñ@â�²V­�x ñ7ÇÀÅÀ‹�Ä“x€Äcú‰/Ä“¬�x�xOâ�)¹IâI<@âI×Y$ ñlŃÄ$>06¥¤¥Y$ ñòÙœ­)EN‰?:<Žì{Ñýã­‡Ç[‡Ç{�Ž�ž¸¾®޲´Î›C^®Û7¶;RSZ™$giËH|‚Äû=ñýYyx¼×œX‹ÛE>t}ó ñ\™$gé©Ù:‰^â;™`•Z¤|ç¸È‰°2I<ÎÒ�ùYÔÄÛ3zÊ*µ†=LÓKüà5= pâÝV&‰ÇYzzaŽÄ8ñbSñhÒUê[&‰ŸìÊ$ñ8ÓÄ/’ø '¾o;q"U²o{’x6äI|@Ý]`¢&à‰·vpRUêk+‰ŸàÊ$ñ8ëÄâ†Ì�‰?ã˜{e=ñcµ&؉r¼÷pä_;òUâ'þžxŠû Ýö›>1nz}š¼Øý’øùsñ$>P‰±ec?Î)&~âO¡9æû*¿7lfЄá!sñ~t{®BâI|�š]Fúäsz‰ŸðS÷ Ã>»â:8ü;¶ïÓu^ì>q«Q&ñ$~ÌWþé»gä=jšg·—ú”VæÄŸÂ)w½µ®d·�ôÎûŠë{@ßs$ñ¾±^Ÿ!ñ$>0‰|ýVû7'³!?¥•9ñ§°çáý`ЋõŸË·Yç:{ž×*/vŸX«’ø@$¾9Ⴠ&~ïÔ{1Ú·7Ï8ñ~ giº x5ë�{îqÀvÆ!ûÅûÔ�j‰Ä ñ·ÂÎ%ñ§ù­¥Ñï1gÞ6lÏ'ñ Í ì¼o�Fwû?bÜcs„UÊ‹Ý'®WH¼ÿÿdà6Ýtö‹÷câ'‘³sNü¤ŸÂãGX`Ïznº¾ ¸ÎÔ;ý`Ä‹Ý'®•‹JžÄû2ñcìbAâ½ôÑã�ìýœx/{1Ž™x·éøC÷ß�rúxƒ»O\-H|À8õécŸ$~"9;ßÄOü)L&ñQ`ü†íÃþ_ób÷‰ÕbAÉÅ“¬ˆà&ÞûÁ„I<‰÷’øÇ�Æÿ$ñ~³RÈ“ø'~ìßQ"ñ-ñ#hŽö踇/v¿lÅ“øà&Þþê"ñ$~"û³î�ú†y±ûh¢&Kâ˜xã TOÎhWnÑïý;Óí4/vß$¾Hâ“ø¡Ç—ûWŸHü™%Þût¼ÛÊäÅ¶âýšxᅦIâIüdÿ×xÿ¹�‰ßÜJâI<‰'ñcÏÅ»Ü/vß$>¯äb$žÄ“xÉïñoCŽq,Lvšô÷\<‰'ñ$žÄŸb¿x~õ‰ÄƒÄ“x'þðT±„øÙU‘ø,‰'ñ$žÄ>FÍ€p2»V*(Ù(‰'ñgý‡ýÎ1ñg¤É£©=…É'¾ézïÕ�Äû(ñ3$þB&~o?x‰÷íÁ„GX™ Í $ÞËñâL˜¿úDâá³ÄûƒÎ~Iü({zœÛ_}ieNü)xþ#·ÁÝžõà? bÿàÅî§¹x¡ÿdøoÕú4ñ“ø“IÞíTVæž‚Ç7×£½{xÃú‡ýì?ñb'ñ$þ¬?ö>ç–ø�³v–ž{J+sŒ§0äÿΡyåvÕº´æ�ÿÅ{žgÕx±ûÄj1§d":+‚Ä�½ÝiïqìÎ]ŒºËöØñÓ[™ §\ uiCÿ®Ó€uÒ÷ÆÃ‹Ý/‰/äØ/žÄÙAÂ?‰:ñ=ÞUÎreNã)ìMù'ŒÎ»Ô€ïؾoW^ì>J|9•gE�øÉž^â=öÎû~‡|œSZ™Sy ÍqÞ6ú—æþÿÂãnøÖ73^ì>J|-[bE�øSSL¼·#*÷Ýʼnt<¼þ¹¬Ìi=…+ïðÝÕ<õ>š–Ÿ x±ûÄŠH|Iϲ"H|ë%êx�SM¼—ÊÛïâqÓõ—-í·Ý{4ÂüÌ´WæŸÂ“ão�`ëÐaiv¹ñ¾Ï‰÷[âóY%O±".lâE‰Œ?3r8h qÚ‰7zgæiëáˆwñäøè‘ñàí7l=©ƒæøiJ+ó ž‚±À‡ý l=¶÷· ׉øÑ÷"ñþJ<·žWâYñb÷ÑD ;M’x€ÄKûqk&JâI<@âeL|1ÇÁ„I<@â%N<0 ñ‰—Që5LÔ�x€Ä“x�x€Ä-ñLÔ�x€Ä“x�x€Ä,ñìQCâ/kâùíV�x9_2Ï1jH<@â%tm†Ä“x€ÄK�x&jH<@âeœ¨™áãV�v.¾ÀN“@âK|‘€¤‰/ù“ §U3ñ+dL|žÄ€œŒ¿Ýš&ñ çV¼H¼Jâ@BƯ>‘x�Òµr^I©ìQ$ÆDMŠýâ€ÄÄœ¨!ñ gâ $H< p‰O“x�ÑuònÅóq+HÊÜi’_}­–r$$M|1Kâ@N+ÒnÅgD⓬�ÏÕbVÉk9VÈçZ)§Ì—çX Ÿëå¼²X]`E€œ‰g¢¤M|2Ì5 iâÙ£$M|�Ä€œÖªErºY+‘x�5ñ3$äÝŠg��v+žÄ€”^Ø\g¢ä´»»ËÁ„@ÞÄ3Q$@â$0ÝÄë!�$@â$@â£yãõ×H<ÈéÙõ5E“x�ÐÎÎŽ¢ Û/þ=Õññ1¡ .c.^¶Ïw9paûNâI<iûNâ/Pâßp1|òÉ'ÿôRÅ_‰O�x½}'ñc÷=ð‰? µ1ÏJü–ø(‰‰@â%LüÆÒ[ñH¼Ä[ñ$‰—1ñÌÅ ñ$žÄ$žÄ“x$žÄûÄÕRŽÄ˜tâ·•¶íCç;×XÜožæavïCé.Êz¦õÜη\(Îmî/¶/çáù0ñ¯þ%J�xK¼Ê“²7÷÷OŸxÇ+wÏì–¹çšf¹ÛdÀÝW³½I’xc¢&ÎV<€ &ÞS'™xËéÞkvßl'~ðžøDD'ñ&7Qcl÷Ò¨­uêÆÒÐþ‹úβ̱ôll{H|·ñnÉî.ܼØi9ýÏááú{+žÄ˜dâ»<‰ ¥˜‡Û®1m_d™w9ÕDM_âçwŠ!o¶‡'Îpù|Á§¿ú‹0Q`²‰ïvÞèaw÷“Ø“tÚ/²NåI¼=ÚöÄýøt`â�žó²|¹GMV‰¨$ÀßÙŠPmûE#$~Ø™#ÏÅ;&Þa£½U~ÿOÔÜ(ç5'ñ&–øÃý“bvfPì³óÖ˜ö]4òD�{¯�fŠFJ¼ãç ½? ø9ñÆ\|De§IÜŠwú€Ô:ãa�¨q¸èؾ_}ûk×�[go¬—Ž�xÛÃsßåß§‰�’xÓ™¨¹Püšø0‰@âeM|ˆ¹x$^ÂÄß¿ÿ~»‰—3ñ+… ‡!@âåLüZ9ÏaÈ�x9¿QÉ+ €ÄË™øœ¢1Qàt‰?€Éw‰/“x§K¼‚^þIüf%?Zâ?p1xLñ^žø‰ë�7”$sñ&˜øÃý“*uf(ì=S½yŸ¨qìrÿU�œZ¦h¶··mË:Qãüyè° ëDMß<’ÛD�—'>Bâ_}åóJZÕI<€ÉmÅ;}@j�…蛂è¿èؾ_}ûkûÇ­MÇÍgÅöá¨õRïï.ËtÝë¿·ÈÝ[9=ÎÃÎÅûîþ´‰ß¹_)éy`5˜ï‰îö]¥‘­�x$^¾Ä—¢ºRŒgH</aâ#š’ñq+/aâ‹jBɰG /eâà %Ë5H¼¼‰g+‰—0ñ…05N�øœ-¯‰ÅEâ9€€ñ¯àœxØŠ1ñŸ¸<&žÎú9ñEs¢Ækâù¦H<¤µÓdÜKâù鸘ep•TÍSâù.H<‚—øˆ‘øØà+ñ-�xs+^W²á(+äL|N�±"@>E‘ø|$ΊI¯’x ñ€ %>ÆŠI?l§I@P_ ñ eâúRd.H<€Ä|’ø0‰I_ ñ gâ5r2þyÒnÅ«$$UŒhJ޹x ñ€ %^M�x�“±GM–ĉHQ$>Câ@F%3ñ1VÈg÷šFâ@ÞħI<�x@ÀŸ ‘x ñðEâ“$H< p‰ç·[€ÄH<À'‰çû€¤‰×I<�x‰�xÀ”Ï5 mâc¬�0ñ»»$$Mü�9$¼Ä‡ù¸H<€ÄÎ?ñìQ2oÅk$dM<[ñ mâ“Ìʼn�x‰�xÀȉ7þ<7‰iÏŸYŸ ó‡ý€Ä‚•ø´Jâ@Öij$@â>I<;M€¬‰O‘x ñðGâ9€H›x�Ã�€¬‰×H<ÈšøD8ÆŠVâCQVȧ¦&-ÄV<Hšx�ÄÃæþ‡c††¢(|Ob uU#ñ ñ~ï;‰Çø‰çWŸ@â}Þw�©$>²Ï8·ñ?s@âq #ñüêH¼/ûNâqêÄëÀ$žÄƒÄƒÄ3H<˜¨‰g�x�x�x‰‰‰'ñ‰‰'ñ@ǬH|ŠÄƒÄ“xÈh.BâAâIzÒóÆð¸y¼÷pHI·�[Y2è»Z«¶[�z&‹¶öG¾ëƒ'=wýø‘yf³½4�‹’x{[5ïé¬iÏÃ[ÂãGî‰o:\yÔÄ{¼k{â�N–æüóŠí�„Äãìÿ€Äcš‰?:!ñÑCOS:‹u «5Ç­L÷t¼Ùó��Ç»îÓ²Ü#‰[ñ¸Ð‰ouÖžøƒ‡õUí/¯™Klj{£÷zƒnOü€msãñ¸Ü5‰‰‰'ñGö3mŸÖî�’øž;jöº3Q>Ò]Hì½—�xø=ñ Ž�©%ÞËþˆ�Ú}?p; Ù Ä»³lÏöwKÆ; ÙàÄÞ?‡Äcê‰@â1ÍÄý«Oƒ·¦½Lx`â–éth°o0ƒ&<ôøø$lÅCÚÄ·¶å�ÿv«ãÜŽå�{xÿ“ £%¾éú8š½äÉð? ÒÿWj›^÷²'ñ8“­øP”�©&žAâq~[ñ$$žÄCÚ­x&j@âI<¤ÝŠ'ñ ñ$²&žýâAâI<ØŠ‰g�x*ñv•8·‚Ä“xȺEX ñ$’NÔ°OâAâAâ$AJüc�Oâ!éV<‰‰'ñ�v+^g§I�xY·âIN<{Ô€¬ÞxóMrš‹$˜¨I¯’x�Ô¬H<{Ô‰IC�+i•Ä€„ê$dU‹�x�u+^$>Eâ€Ä¤!Ÿd��3ñ r2ö‹×I<È™ø8‰9ǨÑHüÙÙÙ¨m옧îÕúÿ惺±QÿìôßBµŸ g÷je]M㙎±LËMZßÐ|«ú̼‘øPœq&uW•š¨x_È­¯+ÿÊç�ÐoÏññŒú '²�i'Þ|í¯rßYP5¶/ùþW‰'ñr$ž x?ZŒhJ"cEœÑ Þxˆ­ykäm‰¿'¶ö­?òv®°Ó¾ÀasÉvQ÷ŒÎ•ÍålÔ”îÂÍŸ+îÙHÿÂŒ j5µgR©u‰ë5û Ãõzœý±¹=¿O§ó\ž×¹¯®Þ§î´-³ríåÛ®ÿ΀oûŠðLO·ÓYˆ}žÐù^l7éÞ-™'ñ°éd¾Túð=¹‚ø¯óOÂöÙzËOÍÝ -?At.·,óädÏìêÉÛßo†^sÀÃsþ1¿ÿ±�ñtœÖ•mµ�×êös[÷æbÁÖGí¶Åòã”f‡ïçGky¯±/Ç~/Ž7a+Þ§–DâãLÔø(ñöÓ''œ^‚'¯3{Äz¶ûcÔ³ð“Œõo“v6õ\á�kº?¼ž­M{ l íé¸<¯s_]ŽÿÇnnŸÍ>QÓ-yߊíûž±ß�8§g1.oÖ{q¾ ‰÷kâU”Ä[^¤=‰:U³Ú/ØîËÖ¡È?àš^^çn'žxÛó:÷Õåšxç÷¿Q²‡Ûû=ãøÜ½&Þr/$>@‰“ø@%¾ÿUéiæ¡wÃ^cêØ:-`_šÛ#pM÷‡×3‹í!ñ#<ûóòðxÎhu¹NÍÙnîu¢¦ÿmÆaÅö}Ï8ÞÝ�‰Û›5A²N�ø€$ÞöQšãćíSµÁ ?¹ªu‘ÖçmÓ/í{êÿ¨Ð}¢Æááu>­ë$xX½>ÛCuþôâœV×;C×áF§ÍÖJ;-¤çC]Û'§'+Öþ=Óÿh»×qû¸Õv/Ž7!ñ¾ÝŠ'ñ�÷Í“=µ'·ý�@Z6cE@6Óð ñ/ñªHü%ÉêÎ.Ú$†Ë*[ñ ©+æ¯>1Z‰ê$ä´Jâ!‰3=ê2ÇòEP¯ñq+ü8ÇòÅä]!ñ °R$ž x8%žÃ�]lcæ×íð¶ïœëAzm½;9nû½CºërL]×ã7s,_à ‡!»à}ë0¿ÞŽEsnéí;&³å쮇 vÜ•þVr,_�x ðãæwÔ#Jº´{ZézاƒîÚ�žèx¤_§IŽå ÿNÔ�xïtÎàÃüN<ñ“=Hï°ÄÛŸ¦kâÝ>ÂäX¾†Ë$ž‰‡¤ 9Ìïx5gv�Þa‰w~šŽ³&²8–/`™Ä_lcæ×ñ¹ç}�^ï5ö§érL]û¡w-÷ű|áüÕ'`ØŒÓy¾nœÊ¢Ê1jçÉ�À[“xÀ}F†Ä#Ø[ñaŽQ$,Ká$‰”es¢&ÊŠù¬˜;MFX ŸkQ’Z‹§H<Èéf"Mâ@Òij²Z‹%�=j@F7¢º¢…c¬�t+žÄ€”‰�’x ó΃¢(|ïá”nD˜¨�‡Ä³ÎØíÛ·I<&‘x�ăēxÈéùåOâ!§ÝÝ]vš‰'ñ�Ó›¯½¦h—H—á÷%¶øI<¦á ö¨Á˜‰¿·¡*í9‰n¨”žyq½“MZKæÄÕºº�™Š“ÛØb‹¡õÒ¡�¤³ §îœ<€ž{êY¦¸¤sKóÆW=¿{¯Ýçbžg½ Ës·?9§·� %þuE ÅX1ñŠ¥��žŸt«3 Ýiü½šª¶7Tm[¬ÖÂ9-¤·ï¶Û:>’“k‰û=é²Ó�ööÚy™'7ì,¿÷�«÷æ}Ï¥/ñöæ´Oâ1ÙÄë$ãOÔœœ¶mÝ_œôËÚ½Öi{Òú6rm é)|_�݉E{;Üᆪê8�â˜é¾RwÞ¸ún?0ñöæô.AâAâ�Ä·ƒÞJ¡9Aã°Ñ:�Ä÷^Íù†fãò:4ñígå´ý=4ñN9wBâ1Á‰æâqêÄ÷ϱtOZ§hjµÚ°,:,dàD�ó#齚ë �¦Á‡'Þ|Vâ™ þdÕq¢Ævw.Ý'ñ˜c�š·b‰·NDX:f­«ÃôúI×>nuÜ ¥û‘f罹n�¤óŽávCóª=wå!ñ.{GK¼Ó#ñ˜¦í×^SlÅc´ÄÃm }bHÆŠ Šñ«O 'c¢†Ä€”Ê"ñ12ª„$äT k$äT‰�’x�3ñ rª…cJ4cE€„‰WãJTM°"@ÂÄGJ$Lâ@ÒÄ«$¤L¼šPÂa�ò©ªqEUuVȘø„H<[ñ k♋9¯)‘pœ&>¢‹ÄÇX ŸŠH<¿Ý R*5¡+äS 'õ’ÊŠù�ij2*ˆÄ3Q’&>®DÂ$$”g¢¤N<·€ŒræV<‰IÏ\<È™ø‰IeÙŠiÏV<�x@ÀdB‰YÏ @R©P\‰’x�2ña’J²$0z(Fâ€Ä—øÈ¥HkˆÖ�?ÂÑh(j9'jžÓµX(šè1-ÿöž�‹ÛªæÂј¸B8¦‡cÉK‘̧ÃùKáªYÖ“ëÅÂíZõf©´”LUB‘‚q‘š E2¡h:l5bUŒL8š ÇráxÞ2 b„ÌŽà c„âÅP´Š—ÂñsˆåpÂ>fŒ!.ŠWÔÄɈ×"‰zTkÄôFT«Eâ5qŽšh�º9ªÖˆh³â_1‰Y1TmΉys,„ã‹áÄ’š¸ÑV"ÚjD»jŽkízD[‹ê7#úšªuÆMU[�è›ÑÔf,µI®«º›ªv+Ò[æx*¢ÝŽè­q§=’w#É;jò¶š|JÕ·Tý–¸•9¶Tí¶ªÝ�èÏD“ÏÅÓ/ÄÓÏGõç£Úg¢úK‰ä+©Ì«ÙÜ«ÙìÒé—uýÅxâç¢ñÏ#ñb<õR<-Æç™{æxY˼¢e>¯g¾ g¿˜Ì½šÊ½–οž)œ q:ÿ%1²…7ò¥7Å(ß*”Þ*–¾\šùòLéË•ÒWjå�FõÁ\}w¡±»Øx°PßY¬Ýo�%cì,×\i´ÇåîØ¹\™Ã¼æbuGŒ…ÚÎBõþ\åþle§Q¹_›ùJµt¿Rº_.íTʪ•ÝjEœøJ¹ôv±ðåbáíbñíÒÌÛ¥ò›ÅÒëù²ùWrù—sù{Ùüç²ù—2¹SÙ—’™—Ó¹/f‹¯çK_*̼^(½–/~1—ÿ|&ûrZ\”y%“û‚8§PúB¾ôЏa:÷ÙTæçôô Z{<¯¥ŸÓ3Ï&³Ïê™g´ôÓ­‘#%ÆÝÖˆC|ùL"ól"-Æs‰ÔsšéÖ0o˜¹“ÈÜŽg¶b™[ñÌF,½K­E“×£úµˆnùFÒŸ!¾‘"Æ·“øþÙˆ&7£É[â»ÈéÍxf=š¾I^Uõ•°vEC¿¬ê˪¶,NDô刾$N‹#ú¢ª-˜cÉüR\ç²yý“‘¼¬&—Õ䢚œWõ959IÎESõH²IVU½¢ê3ªVTµB8Ñy%–T­Ñ«‘d#–®GS5qZ_ê5ãVz%¬UBÆk°ªjÆùªV7_V�ˆ~2Œsª¡¸âÄl49O‰Ñˆ¥Ä2ËaÍx ‹»PuñïLX›i�Žè3b¨z1¬B ñxÄ£*E’3±t9ž)FS95™ i1Âbèé�–üt4u)ž 'ÌséP"u)&†8ažiœ“Å;_¶Ïìžß3:—¦Bñd¸5âß”8S5o6ou)š ŲâüOGR—"é�cT\MǵöH´†nž)Æÿøœ â;–4IEND®B`‚pdf2docx-0.5.13/docs/index.rst000066400000000000000000000020461517510251600161070ustar00rootroot00000000000000.. include:: header.rst Welcome to pdf2docx ==================================== :title:`pdf2docx` is a **Python** library to extract data from **PDF** with PyMuPDF_, parse layout with rule, and generate **docx** files with ``python-docx``. :title:`pdf2docx` is hosted on `GitHub `_ and registered on `PyPI `_. ---- .. image:: images/intro.png .. toctree:: :maxdepth: 2 :caption: USER GUIDE installation quickstart techdoc license .. toctree:: :maxdepth: 2 :caption: API DOCUMENTATION api/modules Indices and tables ================== * :ref:`genindex` * :ref:`modindex` .. include:: footer.rst .. raw:: html .. _PyMuPDF: https://pymupdf.io pdf2docx-0.5.13/docs/installation.rst000066400000000000000000000017621517510251600175050ustar00rootroot00000000000000.. include:: header.rst Installation ==================== ``pdf2docx`` can be installed from either PyPI or the source code. Install from PyPI ------------------- Type the command below for a new installation:: $ pip install pdf2docx Or, upgrade this library with:: $ pip install --upgrade pdf2docx Install from source code remotely -------------------------------------- Install ``pdf2docx`` directly from the ``master`` branch:: $ pip install git+git://github.com/ArtifexSoftware/pdf2docx.git@master --upgrade .. note:: In this way, ``pdf2docx`` might have a higher version than PyPI, which is not released yet. Install from source code locally --------------------------------------- Clone or download `pdf2docx `_, navigate to the root directory and run:: $ python setup.py install Or, install it in developing mode:: $ python setup.py develop Uninstall -------------- :: $ pip uninstall pdf2docx .. include:: footer.rst pdf2docx-0.5.13/docs/license.rst000066400000000000000000000023051517510251600164200ustar00rootroot00000000000000.. include:: header.rst .. _About_License: License and Copyright ================================== MIT License ------------ Copyright (c) 2026 Artifex Software, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. .. include:: footer.rst pdf2docx-0.5.13/docs/quickstart.cli.rst000066400000000000000000000033501517510251600177370ustar00rootroot00000000000000.. include:: header.rst Command Line Interface =========================== :: $ pdf2docx --help NAME pdf2docx - Command line interface for pdf2docx. SYNOPSIS pdf2docx COMMAND | - DESCRIPTION Command line interface for pdf2docx. COMMANDS COMMAND is one of the following: convert Convert pdf file to docx file. debug Convert one PDF page and plot layout information for debugging. table Extract table content from pdf pages. By range of pages ----------------------- Specify pages range by ``--start`` (from the first page if omitted) and ``--end`` (to the last page if omitted). .. note:: The page index is zero-based by default, but can turn it off by ``--zero_based_index=False``, i.e. the first page index starts from 1. Convert all pages:: $ pdf2docx convert test.pdf test.docx Convert pages from the second to the end:: $ pdf2docx convert test.pdf test.docx --start=1 Convert pages from the first to the third (index=2):: $ pdf2docx convert test.pdf test.docx --end=3 Convert second and third pages:: $ pdf2docx convert test.pdf test.docx --start=1 --end=3 Convert the first and second pages with zero-based index turn off:: $ pdf2docx convert test.pdf test.docx --start=1 --end=3 --zero_based_index=False By page numbers ----------------------- Convert the first, third and 5th pages:: $ pdf2docx convert test.pdf test.docx --pages=0,2,4 Multi-Processing -------------------------- Turn on multi-processing with default count of CPU:: $ pdf2docx convert test.pdf test.docx --multi_processing=True Specify the count of CPUs:: $ pdf2docx convert test.pdf test.docx --multi_processing=True --cpu_count=4 .. include:: footer.rst pdf2docx-0.5.13/docs/quickstart.convert.rst000066400000000000000000000041551517510251600206540ustar00rootroot00000000000000.. include:: header.rst Convert PDF ======================= We can use either the :py:class:`~pdf2docx.converter.Converter` class, or a wrapped method :py:meth:`~pdf2docx.main.parse` to convert all/specified pdf pages to docx. Multi-processing is supported in case pdf file with a large number of pages. Example 1: convert all pages ---------------------------------- :: from pdf2docx import Converter pdf_file = '/path/to/sample.pdf' docx_file = 'path/to/sample.docx' # convert pdf to docx cv = Converter(pdf_file) cv.convert(docx_file) # all pages by default cv.close() An alternative using ``parse`` method:: from pdf2docx import parse pdf_file = '/path/to/sample.pdf' docx_file = 'path/to/sample.docx' # convert pdf to docx parse(pdf_file, docx_file) Example 2: convert specified pages ---------------------------------------- * Specify pages range by ``start`` (from the first page if omitted) and ``end`` (to the last page if omitted):: # convert from the second page to the end (by default) cv.convert(docx_file, start=1) # convert from the first page (by default) to the third (end=3, excluded) cv.convert(docx_file, end=3) # convert from the second page and the third cv.convert(docx_file, start=1, end=3) * Alternatively, set separate pages by ``pages``:: # convert the first, third and 5th pages cv.convert(docx_file, pages=[0,2,4]) .. note:: Refer to :py:meth:`~pdf2docx.converter.Converter.convert` for detailed description on the input arguments. Example 3: multi-Processing -------------------------------- Turn on multi-processing with default count of CPU:: cv.convert(docx_file, multi_processing=True) Specify the count of CPUs:: cv.convert(docx_file, multi_processing=True, cpu_count=4) .. note:: Multi-processing works for continuous pages specified by ``start`` and ``end`` only. Example 4: convert encrypted pdf --------------------------------------- Provide ``password`` to open and convert password protected pdf:: cv = Converter(pdf_file, password) cv.convert(docx_file) cv.close() .. include:: footer.rst pdf2docx-0.5.13/docs/quickstart.gui.rst000066400000000000000000000003631517510251600177550ustar00rootroot00000000000000.. include:: header.rst Graphic User Interface =========================== Thanks @JoHnTsIm providing a ``tkinter`` based user interface. To launch the GUI:: $ pdf2docx gui .. image:: images/pdf-converter.png .. include:: footer.rstpdf2docx-0.5.13/docs/quickstart.rst000066400000000000000000000004371517510251600171740ustar00rootroot00000000000000.. include:: header.rst Quickstart ============= ``pdf2docx`` can be used as either a Python library or a CLI tool. In addition, it has a simple GUI. .. toctree:: :maxdepth: 1 quickstart.convert quickstart.table quickstart.cli quickstart.gui .. include:: footer.rst pdf2docx-0.5.13/docs/quickstart.table.rst000066400000000000000000000014351517510251600202610ustar00rootroot00000000000000.. include:: header.rst Extract table ====================== :: from pdf2docx import Converter pdf_file = '/path/to/sample.pdf' cv = Converter(pdf_file) tables = cv.extract_tables(start=0, end=1) cv.close() for table in tables: print(table) The output may look like:: ... [['Input ', None, None, None, None, None], ['Description A ', 'mm ', '30.34 ', '35.30 ', '19.30 ', '80.21 '], ['Description B ', '1.00 ', '5.95 ', '6.16 ', '16.48 ', '48.81 '], ['Description C ', '1.00 ', '0.98 ', '0.94 ', '1.03 ', '0.32 '], ['Description D ', 'kg ', '0.84 ', '0.53 ', '0.52 ', '0.33 '], ['Description E ', '1.00 ', '0.15 ', None, None, None], ['Description F ', '1.00 ', '0.86 ', '0.37 ', '0.78 ', '0.01 ']] .. include:: footer.rst pdf2docx-0.5.13/docs/requirements.txt000066400000000000000000000001661517510251600175330ustar00rootroot00000000000000rst2pdf # define sphinx versioning sphinx autodoc furo sphinx_copybutton sphinxcontrib.apidoc sphinx_markdown_builderpdf2docx-0.5.13/docs/techdoc.rst000066400000000000000000000027101517510251600164070ustar00rootroot00000000000000.. include:: header.rst Technical Documentation =========================== PDF文件é�µå¾ªä¸€å®šçš„æ ¼å¼�规范,`PyMuPDF `_ æ��供了便利的解æž�函数, 用于获å�–页é�¢å…ƒç´ ä¾‹å¦‚文本和形状å�Šå…¶ä½�置。然å�Žï¼ŒåŸºäºŽå…ƒç´ é—´çš„相对ä½�置关系解æž�内容,例如将“横纵线æ�¡ 围绕ç�€æ–‡æœ¬â€�è§£æž�为“表格â€�,将“文本下方的一æ�¡æ¨ªçº¿â€�è§£æž�为“文本下划线â€�。最å�Žï¼Œå€ŸåŠ© `python-docx `_ 将解æž�结果é‡�建为docxæ ¼å¼�çš„Word文档。 以下分篇介ç»�æ��å�–PDF页é�¢æ•°æ�®ã€�è§£æž�å’Œé‡�建docx过程中的具体细节: - æ��å�–文本图片和形状_ - è§£æž�页é�¢å¸ƒå±€_ - è§£æž�表格_ - è§£æž�段è�½_ .. include:: footer.rst .. _æ��å�–文本图片和形状: https://dothinking.github.io/2020-07-14-pdf2docx%E5%BC%80%E5%8F%91%E6%A6%82%E8%A6%81%EF%BC%9A%E6%8F%90%E5%8F%96%E6%96%87%E6%9C%AC%E3%80%81%E5%9B%BE%E7%89%87%E5%92%8C%E5%BD%A2%E7%8A%B6/ .. _è§£æž�页é�¢å¸ƒå±€: https://dothinking.github.io/2021-05-30-pdf2docx%E5%BC%80%E5%8F%91%E6%A6%82%E8%A6%81%EF%BC%9A%E8%A7%A3%E6%9E%90%E9%A1%B5%E9%9D%A2%E5%B8%83%E5%B1%80/ .. _è§£æž�表格: https://dothinking.github.io/2020-08-15-pdf2docx%E5%BC%80%E5%8F%91%E6%A6%82%E8%A6%81%EF%BC%9A%E8%A7%A3%E6%9E%90%E8%A1%A8%E6%A0%BC/ .. _è§£æž�段è�½: https://dothinking.github.io/2020-08-27-pdf2docx%E5%BC%80%E5%8F%91%E6%A6%82%E8%A6%81%EF%BC%9A%E8%A7%A3%E6%9E%90%E6%AE%B5%E8%90%BD/ pdf2docx-0.5.13/pdf2docx/000077500000000000000000000000001517510251600150255ustar00rootroot00000000000000pdf2docx-0.5.13/pdf2docx/__init__.py000066400000000000000000000001241517510251600171330ustar00rootroot00000000000000from .converter import Converter from .page.Page import Page from .main import parsepdf2docx-0.5.13/pdf2docx/common/000077500000000000000000000000001517510251600163155ustar00rootroot00000000000000pdf2docx-0.5.13/pdf2docx/common/Block.py000066400000000000000000000110211517510251600177140ustar00rootroot00000000000000# -*- coding: utf-8 -*- '''Base class for text/image/table blocks. ''' from .share import BlockType, TextAlignment from .Element import Element class Block(Element): '''Base class for text/image/table blocks. Attributes: raw (dict): initialize object from raw properties. parent (optional): parent object that this block belongs to. ''' def __init__(self, raw:dict=None, parent=None): self._type = BlockType.UNDEFINED # horizontal spacing if raw is None: raw = {} self.alignment = self._get_alignment(raw.get('alignment', 0)) self.left_space = raw.get('left_space', 0.0) self.right_space = raw.get('right_space', 0.0) self.first_line_space = raw.get('first_line_space', 0.0) # RELATIVE position of tab stops self.tab_stops = raw.get('tab_stops', []) # vertical spacing self.before_space = raw.get('before_space', 0.0) self.after_space = raw.get('after_space', 0.0) self.line_space = raw.get('line_space', 0.0) self.line_space_type = raw.get('line_space_type', 1) # 0-exactly, 1-relatively super().__init__(raw, parent) @property def is_text_block(self): '''Whether test block.''' return self._type==BlockType.TEXT @property def is_inline_image_block(self): '''Whether inline image block.''' return self._type==BlockType.IMAGE @property def is_float_image_block(self): '''Whether float image block.''' return self._type==BlockType.FLOAT_IMAGE @property def is_image_block(self): '''Whether inline or float image block.''' return self.is_inline_image_block or self.is_float_image_block @property def is_text_image_block(self): '''Whether text block or inline image block.''' return self.is_text_block or self.is_inline_image_block @property def is_lattice_table_block(self): '''Whether lattice table (explicit table borders) block.''' return self._type==BlockType.LATTICE_TABLE @property def is_stream_table_block(self): '''Whether stream table (implied by table content) block.''' return self._type==BlockType.STREAM_TABLE @property def is_table_block(self): '''Whether table (lattice or stream) block.''' return self.is_lattice_table_block or self.is_stream_table_block def set_text_block(self): '''Set block type.''' self._type = BlockType.TEXT def set_inline_image_block(self): '''Set block type.''' self._type = BlockType.IMAGE def set_float_image_block(self): '''Set block type.''' self._type = BlockType.FLOAT_IMAGE def set_lattice_table_block(self): '''Set block type.''' self._type = BlockType.LATTICE_TABLE def set_stream_table_block(self): '''Set block type.''' self._type = BlockType.STREAM_TABLE def _get_alignment(self, mode:int): for t in TextAlignment: if t.value==mode: return t return TextAlignment.LEFT def parse_horizontal_spacing(self, bbox, *args): """Set left alignment, and calculate left space. Override by :obj:`pdf2docx.text.TextBlock`. Args: bbox (fitz.rect): boundary box of this block. """ # NOTE: in PyMuPDF CS, horizontal text direction is same with positive x-axis, # while vertical text is on the contrary, so use f = -1 here idx, f = (0, 1.0) if self.is_horizontal_text else (3, -1.0) self.alignment = TextAlignment.LEFT self.left_space = (self.bbox[idx] - bbox[idx]) * f def store(self): '''Store attributes in json format.''' res = super().store() res.update({ 'type' : self._type.value, 'alignment' : self.alignment.value, 'left_space' : self.left_space, 'right_space' : self.right_space, 'first_line_space' : self.first_line_space, 'before_space' : self.before_space, 'after_space' : self.after_space, 'line_space' : self.line_space, 'line_space_type' : self.line_space_type, 'tab_stops' : self.tab_stops }) return res def make_docx(self, *args, **kwargs): """Create associated docx element. Raises: NotImplementedError """ raise NotImplementedError pdf2docx-0.5.13/pdf2docx/common/Collection.py000066400000000000000000000303441517510251600207660ustar00rootroot00000000000000# -*- coding: utf-8 -*- '''A group of instances, e.g. Blocks, Lines, Spans, Shapes. ''' import fitz from .Element import Element from .share import (IText, TextDirection) from .algorithm import (solve_rects_intersection, graph_bfs) class BaseCollection: '''Base collection representing a list of instances.''' def __init__(self, instances:list=None, parent=None): '''Init collection from a list of instances.''' self._parent = parent self._instances = [] self.extend(instances or []) # Note to exclude empty instance by default def __getitem__(self, idx): try: instances = self._instances[idx] except IndexError: msg = f'Collection index {idx} out of range.' raise IndexError(msg) else: return instances def __iter__(self): return (instance for instance in self._instances) def __len__(self): return len(self._instances) @property def parent(self): return self._parent @property def bbox(self): '''bbox of combined collection.''' rect = fitz.Rect() for instance in self._instances: rect |= instance.bbox return fitz.Rect([round(x,1) for x in rect]) # NOTE: round to avoid digital error def append(self, instance): if not instance: return self._instances.append(instance) def extend(self, instances:list): if not instances: return for instance in instances: self.append(instance) def reset(self, instances:list=None): """Reset instances list. Args: instances (list, optional): reset to target instances. Defaults to None. Returns: BaseCollection: self """ self._instances = [] self.extend(instances or []) return self def store(self): '''Store attributes in json format.''' return [ instance.store() for instance in self._instances ] def restore(self, *args, **kwargs): '''Construct Collection from a list of dict.''' raise NotImplementedError class Collection(BaseCollection, IText): '''Collection of instance focusing on grouping and sorting elements.''' @property def text_direction(self): '''Get text direction. All instances must have same text direction.''' res = set(instance.text_direction for instance in self._instances) return list(res)[0] if len(res)==1 else TextDirection.MIX def group(self, fun): """Group instances according to user defined criterion. Args: fun (function): with 2 arguments representing 2 instances (Element) and return bool. Returns: list: a list of grouped ``Collection`` instances. Examples 1:: # group instances intersected with each other fun = lambda a,b: a.bbox & b.bbox Examples 2:: # group instances aligned horizontally fun = lambda a,b: a.horizontally_aligned_with(b) .. note:: It's equal to a GRAPH searching problem, build adjacent list, and then search graph to find all connected components. """ # build adjacent list: # the i-th item is a set of indexes, which connected to the i-th instance. # NOTE: O(n^2) method, but it's acceptable (~0.2s) when n<1000 which is satisfied by page blocks num = len(self._instances) index_groups = [set() for i in range(num)] # type: list[set] for i, instance in enumerate(self._instances): # connections of current instance to all instances after it for j in range(i+1, num): if fun(instance, self._instances[j]): index_groups[i].add(j) index_groups[j].add(i) # search graph -> grouped index of instance groups = graph_bfs(index_groups) groups = [self.__class__([self._instances[i] for i in group]) for group in groups] return groups def group_by_connectivity(self, dx:float, dy:float): """Collect connected instances into same group. Args: dx (float): x-tolerances to define connectivity dy (float): y-tolerances to define connectivity Returns: list: a list of grouped ``Collection`` instances. .. note:: * It's equal to a GRAPH traversing problem, which the critical point in building the adjacent list, especially a large number of vertex (paths). * Checking intersections between paths is actually a Rectangle-Intersection problem, studied already in many literatures. """ # build the graph -> adjacent list: # the i-th item is a set of indexes, which connected to the i-th instance num = len(self._instances) index_groups = [set() for _ in range(num)] # type: list[set] # solve rectangle intersection problem i_rect_x, i = [], 0 d_rect = (-dx, -dy, dx, dy) for rect in self._instances: points = [a+b for a,b in zip(rect.bbox, d_rect)] # consider tolerance i_rect_x.append((i, points, points[0])) i_rect_x.append((i+1, points, points[2])) i += 2 i_rect_x.sort(key=lambda item: item[-1]) solve_rects_intersection(i_rect_x, 2*num, index_groups) # search graph -> grouped index of instance groups = graph_bfs(index_groups) groups = [self.__class__([self._instances[i] for i in group]) for group in groups] return groups def group_by_columns(self, factor:float=0.0, sorted:bool=True, text_direction:bool=False): '''Group elements into columns based on the bbox.''' # split in columns fun = lambda a,b: a.vertically_align_with(b, factor=factor, text_direction=text_direction) groups = self.group(fun) # increase in x-direction if sort if sorted: idx = 3 if text_direction and self.is_vertical_text else 0 groups.sort(key=lambda group: group.bbox[idx]) return groups def group_by_rows(self, factor:float=0.0, sorted:bool=True, text_direction:bool=False): '''Group elements into rows based on the bbox.''' # split in rows fun = lambda a,b: a.horizontally_align_with(b, factor=factor, text_direction=text_direction) groups = self.group(fun) # increase in y-direction if sort if sorted: idx = 0 if text_direction and self.is_vertical_text else 1 groups.sort(key=lambda group: group.bbox[idx]) return groups def group_by_physical_rows(self, sorted:bool=False, text_direction:bool=False): '''Group lines into physical rows.''' fun = lambda a,b: a.in_same_row(b) groups = self.group(fun) # increase in y-direction if sort if sorted: idx = 0 if text_direction and self.is_vertical_text else 1 groups.sort(key=lambda group: group.bbox[idx]) return groups def sort_in_reading_order(self): '''Sort collection instances in reading order (considering text direction), e.g. for normal reading direction: from top to bottom, from left to right. ''' if self.is_horizontal_text: self._instances.sort(key=lambda e: (e.bbox.y0, e.bbox.x0, e.bbox.x1)) else: self._instances.sort(key=lambda e: (e.bbox.x0, e.bbox.y1, e.bbox.y0)) return self def sort_in_line_order(self): '''Sort collection instances in a physical with text direction considered, e.g. for normal reading direction: from left to right. ''' if not self.is_vertical_text: self._instances.sort(key=lambda e: (e.bbox.x0, e.bbox.y0, e.bbox.x1)) else: self._instances.sort(key=lambda e: (e.bbox.y1, e.bbox.x0, e.bbox.y0)) return self def sort_in_reading_order_plus(self): '''Sort instances in reading order, especially for instances in same row. Taking natural reading direction for example: reading order for rows, from left to right for instances in row. In the following example, A comes before B:: +-----------+ +---------+ | | | A | | B | +---------+ +-----------+ Steps: * Sort elements in reading order, i.e. from top to bottom, from left to right. * Group elements in row. * Sort elements in row: from left to right. ''' instances = [] for row in self.group_by_physical_rows(sorted=True, text_direction=True): row.sort_in_line_order() instances.extend(row) self.reset(instances) class ElementCollection(Collection): '''Collection of ``Element`` instances.''' def _update_bbox(self, e:Element): '''Update parent bbox.''' if not self._parent is None: # Note: `if self._parent` does not work here self._parent.union_bbox(e) def append(self, e:Element): """Append an instance, update parent's bbox accordingly and set the parent of the added instance. Args: e (Element): instance to append. """ if not e: return self._instances.append(e) self._update_bbox(e) # set parent if not self._parent is None: e.parent = self._parent def insert(self, nth:int, e:Element): """Insert a Element and update parent's bbox accordingly. Args: nth (int): the position to insert. e (Element): the instance to insert. """ if not e: return self._instances.insert(nth, e) self._update_bbox(e) e.parent = self._parent # set parent def pop(self, nth:int): """Delete the ``nth`` instance. Args: nth (int): the position to remove. Returns: Collection: the removed instance. """ return self._instances.pop(nth) def is_flow_layout(self, line_separate_threshold:float, cell_layout=False): '''Whether contained elements are in flow layout or not.''' # float layout if vertical text but not cell layout, since vertical text # will be simulated with stream table if not cell_layout and self.is_vertical_text: return False # flow layout if single column only if len(self)<=1: return True if len(self.group_by_columns())>1: return False # group in physical row and check distance between lines idx0, idx1 = (0, 2) if self.is_horizontal_text else (3, 1) for row in self.group_by_physical_rows(text_direction=True): for i in range(1, len(row)): dis = abs(row[i].bbox[idx0]-row[i-1].bbox[idx1]) if dis >= line_separate_threshold: return False return True def contained_in_bbox(self, bbox): '''Filter instances contained in target bbox. Args: bbox (fitz.Rect): target boundary box. ''' instances = list(filter( lambda e: bbox.contains(e.bbox), self._instances)) return self.__class__(instances) def split_with_intersection(self, bbox:fitz.Rect, threshold:float=1e-3): """Split instances into two groups: one intersects with ``bbox``, the other not. Args: bbox (fitz.Rect): target rect box. threshold (float): It's intersected when the overlap rate exceeds this threshold. Defaults to 0. Returns: tuple: two group in original class type. """ intersections, no_intersections = [], [] for instance in self._instances: # A contains B => A & B = B intersection = instance.bbox & bbox if intersection.is_empty: no_intersections.append(instance) else: factor = round(intersection.get_area()/instance.bbox.get_area(), 2) if factor >= threshold: intersections.append(instance) else: no_intersections.append(instance) return self.__class__(intersections), self.__class__(no_intersections) pdf2docx-0.5.13/pdf2docx/common/Element.py000066400000000000000000000251531517510251600202660ustar00rootroot00000000000000'''Object with a bounding box, e.g. Block, Line, Span. Based on ``PyMuPDF``, the coordinates (e.g. bbox of ``page.get_text('rawdict')``) are generally provided relative to the un-rotated page; while this ``pdf2docx`` library works under real page coordinate system, i.e. with rotation considered. So, any instances created by this Class are always applied a rotation matrix automatically. Therefore, the bbox parameter used to create ``Element`` instance MUST be relative to un-rotated CS. If final coordinates are provided, should update it after creating an empty object:: Element().update_bbox(final_bbox) .. note:: An exception is ``page.get_drawings()``, the coordinates are converted to real page CS already. ''' import copy import fitz from .share import IText from . import constants class Element(IText): '''Boundary box with attribute in fitz.Rect type.''' # all coordinates are related to un-rotated page in PyMuPDF # e.g. Matrix(0.0, 1.0, -1.0, 0.0, 842.0, 0.0) ROTATION_MATRIX = fitz.Matrix(0.0) # rotation angle = 0 degree by default @classmethod def set_rotation_matrix(cls, rotation_matrix): """Set global rotation matrix. Args: Rotation_matrix (fitz.Matrix): target matrix """ if rotation_matrix and isinstance(rotation_matrix, fitz.Matrix): cls.ROTATION_MATRIX = rotation_matrix @classmethod def pure_rotation_matrix(cls): '''Pure rotation matrix used for calculating text direction after rotation.''' a,b,c,d,e,f = cls.ROTATION_MATRIX return fitz.Matrix(a,b,c,d,0,0) def __init__(self, raw:dict=None, parent=None): ''' Initialize Element and convert to the real (rotation considered) page CS.''' self.bbox = fitz.Rect() # type: fitz.Rect self._parent = parent # type: Element # NOTE: Any coordinates provided in raw is in original page CS # (without considering page rotation). if 'bbox' in (raw or {}): rect = fitz.Rect(raw['bbox']) * Element.ROTATION_MATRIX self.update_bbox(rect) def __bool__(self): '''Real object when bbox is defined.''' # NOTE inconsistent results of fitz.Rect for different version of pymupdf, e.g., # a = fitz.Rect(3,3,2,2) # bool(a) a.get_area() a.is_empty # pymupdf 1.23.5 True 1.0 True # pymupdf 1.23.8 True 0.0 True # bool(fitz.Rect())==False # NOTE: do not use `return not self.bbox.is_empty` here return bool(self.bbox) def __repr__(self): return f'{self.__class__.__name__}({tuple(self.bbox)})' # ------------------------------------------------ # parent element # ------------------------------------------------ @property def parent(self): return self._parent @parent.setter def parent(self, parent): self._parent = parent # ------------------------------------------------ # bbox operations # ------------------------------------------------ def copy(self): '''make a deep copy.''' # NOTE: can't serialize data because parent is an Object, # so set it None in advance. parent, self.parent = self._parent, None obj = copy.deepcopy(self) self._parent = parent # set back parent return obj def get_expand_bbox(self, dt:float): """Get expanded bbox with margin in both x- and y- direction. Args: dt (float): Expanding margin. Returns: fitz.Rect: Expanded bbox. .. note:: This method creates a new bbox, rather than changing the bbox of itself. """ return self.bbox + (-dt, -dt, dt, dt) def update_bbox(self, rect): '''Update current bbox to specified ``rect``. Args: rect (fitz.Rect or list): bbox-like ``(x0, y0, x1, y1)``, in real page CS (with rotation considered). ''' self.bbox = fitz.Rect([round(x,1) for x in rect]) return self def union_bbox(self, e): """Update current bbox to the union with specified Element. Args: e (Element): The target to get union Returns: Element: self """ return self.update_bbox(self.bbox | e.bbox) # -------------------------------------------- # location relationship to other Element instance # -------------------------------------------- def contains(self, e:'Element', threshold:float=1.0): """Whether given element is contained in this instance, with margin considered. Args: e (Element): Target element threshold (float, optional): Intersection rate. Defaults to 1.0. The larger, the stricter. Returns: bool: [description] """ S = e.bbox.get_area() if not S: return False # it's not practical to set a general threshold to consider the margin, so two steps: # - set a coarse but acceptable area threshold, # - check the length in main direction strictly # A contains B => A & B = B intersection = self.bbox & e.bbox factor = round(intersection.get_area()/S, 2) if factor= self.bbox.height: return self.bbox.width+constants.MINOR_DIST >= e.bbox.width return self.bbox.height+constants.MINOR_DIST >= e.bbox.height def get_main_bbox(self, e, threshold:float=0.95): """If the intersection with ``e`` exceeds the threshold, return the union of these two elements; else return None. Args: e (Element): Target element. threshold (float, optional): Intersection rate. Defaults to 0.95. Returns: fitz.Rect: Union bbox or None. """ bbox_1 = self.bbox bbox_2 = e.bbox if hasattr(e, 'bbox') else fitz.Rect(e) # areas b = bbox_1 & bbox_2 if b.is_empty: return None # no intersection # Note: if bbox_1 and bbox_2 intersects with only an edge, b is not empty but b.get_area()=0 # so give a small value when they're intersected but the area is zero a1, a2, a = bbox_1.get_area(), bbox_2.get_area(), b.get_area() factor = a/min(a1,a2) if a else 1e-6 return bbox_1 | bbox_2 if factor >= threshold else None def vertically_align_with(self, e, factor:float=0.0, text_direction:bool=True): '''Check whether two Element instances have enough intersection in vertical direction, i.e. perpendicular to reading direction. Args: e (Element): Object to check with factor (float, optional): Threshold of overlap ratio, the larger it is, the higher probability the two bbox-es are aligned. text_direction (bool, optional): Consider text direction or not. True by default. Returns: bool: [description] Examples:: +--------------+ | | +--------------+ L1 +-------------------+ | | +-------------------+ L2 An enough intersection is defined based on the minimum width of two boxes:: L1+L2-L>factor*min(L1,L2) ''' if not e or not bool(self): return False # text direction idx = 1 if text_direction and self.is_vertical_text else 0 L1 = self.bbox[idx+2]-self.bbox[idx] L2 = e.bbox[idx+2]-e.bbox[idx] L = max(self.bbox[idx+2], e.bbox[idx+2]) - min(self.bbox[idx], e.bbox[idx]) eps = 1e-3 # tolerant return L1+L2-L+eps >= factor*min(L1,L2) def horizontally_align_with(self, e, factor:float=0.0, text_direction:bool=True): '''Check whether two Element instances have enough intersection in horizontal direction, i.e. along the reading direction. Args: e (Element): Element to check with factor (float, optional): threshold of overlap ratio, the larger it is, the higher probability the two bbox-es are aligned. text_direction (bool, optional): consider text direction or not. True by default. Examples:: +--------------+ | | L1 +--------------------+ +--------------+ | | L2 +--------------------+ An enough intersection is defined based on the minimum width of two boxes:: L1+L2-L>factor*min(L1,L2) ''' if not e or not bool(self): return False # text direction idx = 0 if text_direction and self.is_vertical_text else 1 L1 = self.bbox[idx+2]-self.bbox[idx] L2 = e.bbox[idx+2]-e.bbox[idx] L = max(self.bbox[idx+2], e.bbox[idx+2]) - min(self.bbox[idx], e.bbox[idx]) eps = 1e-3 # tolerant return L1+L2-L+eps >= factor*min(L1,L2) def in_same_row(self, e): """Check whether in same row/line with specified Element instance. With text direction considered. Taking horizontal text as an example: * yes: the bottom edge of each box is lower than the centerline of the other one; * otherwise, not in same row. Args: e (Element): Target object. .. note:: The difference to method ``horizontally_align_with``: they may not in same line, though aligned horizontally. """ if not e or self.is_horizontal_text != e.is_horizontal_text: return False # normal reading direction by default idx = 1 if self.is_horizontal_text else 0 c1 = (self.bbox[idx] + self.bbox[idx+2]) / 2.0 c2 = (e.bbox[idx] + e.bbox[idx+2]) / 2.0 res = c1<=e.bbox[idx+2] and c2<=self.bbox[idx+2] # Note y direction under PyMuPDF context return res # ------------------------------------------------ # others # ------------------------------------------------ def store(self): '''Store properties in raw dict.''' return { 'bbox': tuple(x for x in self.bbox) } def plot(self, page, stroke:tuple=(0,0,0), width:float=0.5, fill:tuple=None, dashes:str=None): '''Plot bbox in PDF page for debug purpose.''' page.draw_rect(self.bbox, color=stroke, fill=fill, width=width, dashes=dashes, overlay=False, fill_opacity=0.5) pdf2docx-0.5.13/pdf2docx/common/__init__.py000066400000000000000000000000001517510251600204140ustar00rootroot00000000000000pdf2docx-0.5.13/pdf2docx/common/algorithm.py000066400000000000000000000357201517510251600206640ustar00rootroot00000000000000from collections import deque import numpy as np import cv2 as cv # ------------------------------------------------------------------------------------------- # Intersection area of two iso-oriented rectangles # ------------------------------------------------------------------------------------------- def get_area(bbox_1:tuple, bbox_2:tuple): x0, y0, x1, y1 = bbox_1 u0, v0, u1, v1 = bbox_2 # width of intersected area w = (x1-x0) + (u1-u0) - (max(x1, u1)-min(x0, u0)) if w<=0: return 0 # height of intersected area h = (y1-y0) + (v1-v0) - (max(y1, v1)-min(y0, v0)) if h<=0: return 0 return w*h # ------------------------------------------------------------------------------------------- # Breadth First Search method for graph # ------------------------------------------------------------------------------------------- def graph_bfs(graph): '''Breadth First Search graph (may be disconnected graph). Args: graph (list): GRAPH represented by adjacent list, [set(1,2,3), set(...), ...] Returns: list: A list of connected components ''' # search graph # NOTE: generally a disconnected graph counted_indexes = set() # type: set[int] groups = [] for i in range(len(graph)): if i in counted_indexes: continue # connected component starts... indexes = set(_graph_bfs_from_node(graph, i)) groups.append(indexes) counted_indexes.update(indexes) return groups def _graph_bfs_from_node(graph, start): '''Breadth First Search connected graph with start node. Args: graph (list): GRAPH represented by adjacent list, [set(1,2,3), set(...), ...]. start (int): Index of any start vertex. ''' search_queue = deque() searched = set() search_queue.append(start) while search_queue: cur_node = search_queue.popleft() if cur_node in searched: continue yield cur_node searched.add(cur_node) for node in graph[cur_node]: search_queue.append(node) # ------------------------------------------------------------------------------------------- # Implementation of solving Rectangle-Intersection Problem according to algorithm proposed in # paper titled "A Rectangle-Intersection Algorithm with Limited Resource Requirements". # https://ieeexplore.ieee.org/document/5578313 # # - Input # The rectangle is represented by its corner points, (x0, y0, x1, y1) # # - Output # The output is an Adjacent List of each rect, which could be used to initialize a GRAPH. # ------------------------------------------------------------------------------------------- # procedure report(S, n) # 1 Let V be the list of x-coordinates of the 2n vertical edges in S sorted in non-decreasing order. # 2 Let H be the list of n y-intervals corresponding to the bottom and top y-coordinates of each rectangle. # 3 Sort the elements of H in non-decreasing order by their bottom y-coordinates. # 4 Call procedure detect(V, H, 2n). def solve_rects_intersection(V:list, num:int, index_groups:list): '''Implementation of solving Rectangle-Intersection Problem. Performance:: O(nlog n + k) time and O(n) space, where k is the count of intersection pairs. Args: V (list): Rectangle-related x-edges data, [(index, Rect, x), (...), ...]. num (int): Count of V instances, equal to len(V). index_groups (list): Target adjacent list for connectivity between rects. Procedure ``detect(V, H, m)``:: if m < 2 then return else - let V1 be the first ⌊m/2⌋ and let V2 be the rest of the vertical edges in V in the sorted order; - let S11 and S22 be the set of rectangles represented only in V1 and V2 but not spanning V2 and V1, respectively; - let S12 be the set of rectangles represented only in V1 and spanning V2; - let S21 be the set of rectangles represented only in V2 and spanning V1 - let H1 and H2 be the list of y-intervals corresponding to the elements of V1 and V2 respectively - stab(S12, S22); stab(S21, S11); stab(S12, S21) - detect(V1, H1, ⌊m/2⌋); detect(V2, H2, m − ⌊m/2⌋) ''' if num < 2: return # start/end points of left/right intervals center_pos = int(num/2.0) X0, X, X1 = V[0][-1], V[center_pos-1][-1], V[-1][-1] # split into two groups left = V[0:center_pos] right = V[center_pos:] # filter rects according to their position to each intervals S11 = list(filter( lambda item: item[1][2]<=X, left )) S12 = list(filter( lambda item: item[1][2]>=X1, left )) S22 = list(filter( lambda item: item[1][0]>X, right )) S21 = list(filter( lambda item: item[1][0]<=X0, right )) # intersection in x-direction is fulfilled, so check y-direction further _stab(S12, S22, index_groups) _stab(S21, S11, index_groups) _stab(S12, S21, index_groups) # recursive process solve_rects_intersection(left, center_pos, index_groups) solve_rects_intersection(right, num-center_pos, index_groups) def _stab(S1:list, S2:list, index_groups:list): '''Check interval intersection in y-direction. Procedure ``stab(A, B)``:: i := 1; j := 1 while i ≤ |A| and j ≤ |B| if ai.y0 < bj.y0 then k := j while k ≤ |B| and bk.y0 < ai.y1 reportPair(air, bks) k := k + 1 i := i + 1 else k := i while k ≤ |A| and ak.y0 < bj.y1 reportPair(bjs, akr) k := k + 1 j := j + 1 ''' if not S1 or not S2: return # sort S1.sort(key=lambda item: item[1][1]) S2.sort(key=lambda item: item[1][1]) i, j = 0, 0 while i 1 for c0, c1 in zip(arr_x0, arr_x1): y_arr = arr[r0:r1, c0:c1] top_left = (x0+c0, y0+r0) xy_cut(y_arr, top_left, res, min_w, min_h, min_dx, min_dy) # do xy-cut recursively res = [] xy_cut(arr=img_binary, top_left=(0, 0), res=res, min_w=min_w, min_h=min_h, min_dx=min_dx, min_dy=min_dy) return res def _split_projection_profile(arr_values:np.array, min_value:float, min_gap:float): '''Split projection profile: ``` ┌──â”� arr_values │ │ ┌─â”�─── ┌──â”� │ │ │ │ | │ │ │ │ ┌───â”� │ │min_value │ │<- min_gap ->│ │ │ │ │ │ | ────┴──┴─────────────┴──┴─┴───┴─┴─┴─┴─── 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ``` Args: arr_values (np.array): 1-d array representing the projection profile. min_value (float): Ignore the profile if `arr_value` is less than `min_value`. min_gap (float): Ignore the gap if less than this value. Returns: tuple: Start indexes and end indexes of split groups. ''' # all indexes with projection height exceeding the threshold arr_index = np.where(arr_values>min_value)[0] if not len(arr_index): return # find zero intervals between adjacent projections # | | || # ||||<- zero-interval -> ||||| arr_diff = arr_index[1:] - arr_index[0:-1] arr_diff_index = np.where(arr_diff>min_gap)[0] arr_zero_intvl_start = arr_index[arr_diff_index] arr_zero_intvl_end = arr_index[arr_diff_index+1] # convert to index of projection range: # the start index of zero interval is the end index of projection arr_start = np.insert(arr_zero_intvl_end, 0, arr_index[0]) arr_end = np.append(arr_zero_intvl_start, arr_index[-1]) arr_end += 1 # end index will be excluded as index slice return arr_start, arr_end def inner_contours(img_binary:np.array, bbox:tuple, min_w:float, min_h:float): '''Inner contours of current region, especially level 2 contours of the default opencv tree hirerachy. Args: img_binary (np.array): Binarized image with interesting region (255) and empty region (0). bbox (tuple): The external bbox. min_w (float): Ignore contours if the bbox width is less than this value. min_h (float): Ignore contours if the bbox height is less than this value. Returns: list: A list of bbox-es of inner contours. ''' # find both external and inner contours of current region x0, y0, x1, y1 = bbox arr = np.zeros(img_binary.shape, dtype=np.uint8) arr[y0:y1, x0:x1] = img_binary[y0:y1, x0:x1] contours, hierarchy = cv.findContours(arr, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) # check first three level contours: # * level-0, i.e. table bbox # * level-1, i.e. cell bbox # * level-2, i.e. region within cell # NOTE: only one dimension, i.e. the second, to be decided, so the # return value of np.where is a len==1 tuple level_0 = np.where(hierarchy[0,:,3]==-1)[0] level_1 = np.where(np.isin(hierarchy[0,:,3], level_0))[0] level_2 = np.where(np.isin(hierarchy[0,:,3], level_1))[0] # In general, we focus on only level 2, but considering edge case: level 2 contours # might be counted as level 1 incorrectly, e.g. test/samples/demo-table-close-underline.pdf. # So, get first the concerned level 1 contours, i.e. those contained by other level 1 contour. def contains(bbox1, bbox2): x0, y0, x1, y1 = bbox1 u0, v0, u1, v1 = bbox2 return u0>=x0 and v0>=y0 and u1<=x1 and v1<=y1 level_1_bbox_list, res_level_1, res = [], [], [] for i in level_1: x, y, w, h = cv.boundingRect(contours[i]) if w 1.0.0 from docx.oxml import OxmlElement, parse_xml, register_element_cls except ImportError: # python-docx >= 1.0.0 from docx.oxml.parser import OxmlElement, parse_xml, register_element_cls from docx.oxml.ns import qn, nsdecls from docx.oxml.shape import CT_Picture from docx.oxml.xmlchemy import BaseOxmlElement, OneAndOnlyOne from docx.enum.text import WD_COLOR_INDEX from docx.image.exceptions import UnrecognizedImageError from docx.table import _Cell from docx.opc.constants import RELATIONSHIP_TYPE from .share import rgb_value from lxml import etree # --------------------------------------------------------- # section and paragraph # --------------------------------------------------------- def set_equal_columns(section, num=2, space=0): """Set section column count and space. All the columns have same width. Args: section : ``python-docx`` Section instance. num (int): Column count. Defaults to 2. space (int, optional): Space between adjacent columns. Unit: Pt. Defaults to 0. """ col = section._sectPr.xpath('./w:cols')[0] col.set(qn('w:num'), str(num)) col.set(qn('w:space'), str(int(20*space))) # basic unit 1/20 Pt def set_columns(section, width_list:list, space=0): """Set section column count and space. Args: section : ``python-docx`` Section instance. width_list (list|tuple): Width of each column. space (int, optional): Space between adjacent columns. Unit: Pt. Defaults to 0. Scheme:: """ cols = section._sectPr.xpath('./w:cols')[0] # do nothing if only one column if len(width_list)==1: # recovery to default column setting in case previous section has multiple columns if len(cols)!=1: cols.clear() return # set multiple column properties # clear columns in advance because the latest column setting seems to be inherited cols.clear() cols.set(qn('w:num'), str(len(width_list))) cols.set(qn('w:equalWidth'), '0') # insert column with width for w in width_list: e = OxmlElement('w:col') e.set(qn('w:w'), str(int(20*w))) e.set(qn('w:space'), str(int(20*space))) # basic unit 1/20 Pt cols.append(e) def delete_paragraph(paragraph): '''Delete a paragraph. Reference: https://github.com/python-openxml/python-docx/issues/33#issuecomment-77661907 ''' p = paragraph._element p.getparent().remove(p) paragraph._p = paragraph._element = None def reset_paragraph_format(p, line_spacing:float=1.05): '''Reset paragraph format, especially line spacing. Two kinds of line spacing, corresponding to the setting in MS Office Word: * line_spacing=1.05: single or multiple * line_spacing=Pt(1): exactly Args: p (Paragraph): ``python-docx`` paragraph instance. line_spacing (float, optional): Line spacing. Defaults to 1.05. Returns: paragraph_format: Paragraph format. ''' pf = p.paragraph_format pf.line_spacing = line_spacing # single by default pf.space_before = Pt(0) pf.space_after = Pt(0) pf.left_indent = Pt(0) pf.right_indent = Pt(0) pf.widow_control = True # do not adjust spacing between Chinese and Latin/number xml = r''.format(nsdecls('w')) p._p.get_or_add_pPr().insert(0, parse_xml(xml)) xml = r''.format(nsdecls('w')) p._p.get_or_add_pPr().insert(0, parse_xml(xml)) return pf def set_hidden_property(p): '''Hide paragraph. This method just sets the paragraph property, while the added text must be hided explicitly. r = p.add_run() r.text = "Hidden" r.font.hidden = True Args: p (Paragraph): python-docx created paragraph. ''' pPr = OxmlElement('w:pPr') # paragraph property rPr = OxmlElement('w:rPr') # run property v = OxmlElement('w:vanish') # hidden rPr.append(v) pPr.append(rPr) p._p.append(rPr) # --------------------------------------------------------- # text properties # --------------------------------------------------------- def set_char_scaling(p_run, scale:float=1.0): '''Set character spacing: scaling. Manual operation in MS Word: Font | Advanced | Character Spacing | Scaling. Args: p_run (docx.text.run.Run): Proxy object wrapping element. scale (float, optional): scaling factor. Defaults to 1.0. ''' p_run._r.get_or_add_rPr().insert(0, parse_xml(r''.format(nsdecls('w'), 100*scale))) def set_char_spacing(p_run, space:float=0.0): '''Set character spacing. Manual operation in MS Word: Font | Advanced | Character Spacing | Spacing. Args: p_run (docx.text.run.Run): Proxy object wrapping element. space (float, optional): Spacing value in Pt. Expand if positive else condense. Defaults to 0.0. ''' p_run._r.get_or_add_rPr().insert(0, parse_xml(r''.format(nsdecls('w'), 20*space))) def set_char_shading(p_run, srgb:int): '''Set character shading color, in case the color is out of highlight color scope. Reference: http://officeopenxml.com/WPtextShading.php Args: p_run (docx.text.run.Run): Proxy object wrapping element. srgb (int): Color value. ''' # try to set highlight first using python-docx built-in method # Here give 6/16 of the valid highlight colors color_map = { rgb_value((1,0,0)): WD_COLOR_INDEX.RED, rgb_value((0,1,0)): WD_COLOR_INDEX.BRIGHT_GREEN, rgb_value((0,0,1)): WD_COLOR_INDEX.BLUE, rgb_value((1,1,0)): WD_COLOR_INDEX.YELLOW, rgb_value((1,0,1)): WD_COLOR_INDEX.PINK, rgb_value((0,1,1)): WD_COLOR_INDEX.TURQUOISE } if srgb in color_map: p_run.font.highlight_color = color_map[srgb] # set char shading else: c = hex(srgb)[2:].zfill(6) xml = r''.format(nsdecls('w'), c) p_run._r.get_or_add_rPr().insert(0, parse_xml(xml)) def set_char_underline(p_run, srgb:int): '''Set underline and color. Args: p_run (docx.text.run.Run): Proxy object wrapping element. srgb (int): Color value. ''' c = hex(srgb)[2:].zfill(6) xml = r''.format(nsdecls('w'), c) p_run._r.get_or_add_rPr().insert(0, parse_xml(xml)) def add_hyperlink(paragraph, url, text): """Create a hyperlink within a paragraph object. Reference: https://github.com/python-openxml/python-docx/issues/74#issuecomment-215678765 Args: paragraph (Paragraph): ``python-docx`` paragraph adding the hyperlink to. url (str): The required url. text (str): The text displayed for the url. Returns: Run: A Run object containing the hyperlink. """ # This gets access to the document.xml.rels file and gets a new relation id value part = paragraph.part r_id = part.relate_to(url, RELATIONSHIP_TYPE.HYPERLINK, is_external=True) # Create the w:hyperlink tag and add needed values hyperlink = OxmlElement('w:hyperlink') hyperlink.set(qn('r:id'), r_id, ) hyperlink.set(qn('w:history'), '1') # Create a w:r element new_run = OxmlElement('w:r') # Create a new w:rPr element rPr = OxmlElement('w:rPr') # Create a w:rStyle element, note this currently does not add the hyperlink style as its not in # the default template, I have left it here in case someone uses one that has the style in it rStyle = OxmlElement('w:rStyle') rStyle.set(qn('w:val'), 'Hyperlink') # Join all the xml elements together add add the required text to the w:r element rPr.append(rStyle) new_run.append(rPr) new_run.text = text hyperlink.append(new_run) # Create a new Run object and add the hyperlink into it r = paragraph.add_run() r._r.append(hyperlink) return r EMU_PER_PT = 12700 def pt_to_emu(value_pt): return int(value_pt * EMU_PER_PT) def add_floating_picture_pt( paragraph, image_path, x_pt, y_pt, width_pt, height_pt, ): """ Insert floating image at absolute position using points (pt). :param paragraph: target paragraph :param image_path: image path :param x_pt: horizontal position in pt :param y_pt: vertical position in pt :param width_pt: width in pt :param height_pt: height in pt """ # Convert to EMU x_emu = pt_to_emu(x_pt) y_emu = pt_to_emu(y_pt) width_emu = pt_to_emu(width_pt) height_emu = pt_to_emu(height_pt) run = paragraph.add_run() run.add_picture(image_path) drawing = run._r.xpath(".//w:drawing")[0] inline = drawing.xpath(".//wp:inline")[0] # Update size in wp:extent extent = inline.xpath("./wp:extent")[0] extent.set("cx", str(width_emu)) extent.set("cy", str(height_emu)) # Update size in a:ext (VERY IMPORTANT) a_ext = inline.xpath(".//a:ext")[0] a_ext.set("cx", str(width_emu)) a_ext.set("cy", str(height_emu)) # Extract required elements docPr = inline.xpath("./wp:docPr")[0] cNvGraphicFramePr = inline.xpath("./wp:cNvGraphicFramePr")[0] graphic = inline.xpath("./a:graphic")[0] extent_xml = etree.tostring(extent, encoding="unicode") docPr_xml = etree.tostring(docPr, encoding="unicode") cNvGraphicFramePr_xml = etree.tostring(cNvGraphicFramePr, encoding="unicode") graphic_xml = etree.tostring(graphic, encoding="unicode") # Build anchor XML anchor_xml = f""" {x_emu} {y_emu} {extent_xml} {docPr_xml} {cNvGraphicFramePr_xml} {graphic_xml} """ anchor = parse_xml(anchor_xml) drawing.remove(inline) drawing.append(anchor) # --------------------------------------------------------- # image properties # --------------------------------------------------------- def add_image(p, image_path_or_stream, x_pos, y_pos, width, height): '''Add a floating image to a paragraph at a specific position. The image is inserted as a floating picture (not inline) using ``add_floating_picture_pt``, anchored to the given paragraph and positioned by ``x_pos`` and ``y_pos`` in Pt. Args: p (Paragraph): ``python-docx`` paragraph instance. image_path_or_stream (str | bytes): Image file path or in‑memory image stream. x_pos (float): Horizontal position of the image in Pt, relative to the page. y_pos (float): Vertical position of the image in Pt, relative to the page. width (float): Image width in Pt. height (float): Image height in Pt. ''' docx_span = p.add_run() try: docx_span.add_picture(image_path_or_stream, width=Pt(width), height=Pt(height)) ''' add_floating_picture_pt( paragraph=p, image_path=image_path_or_stream, x_pt=x_pos, # 2 inches y_pt=y_pos, # 2 inches width_pt=width, # 3 inches height_pt=height, # 2 inches ) ''' except UnrecognizedImageError: print('Unrecognized Image.') return # exactly line spacing will destroy image display, so set single line spacing instead p.paragraph_format.line_spacing = 1.00 class _CT_Anchor(BaseOxmlElement): """ ```` element, container for a floating image. """ extent = OneAndOnlyOne('wp:extent') docPr = OneAndOnlyOne('wp:docPr') graphic = OneAndOnlyOne('a:graphic') @classmethod def new(cls, cx, cy, shape_id, pic, pos_x, pos_y): """ Return a new ```` element populated with the values passed as parameters. """ anchor = parse_xml(cls._anchor_xml(pos_x, pos_y)) anchor.extent.cx = cx anchor.extent.cy = cy anchor.docPr.id = shape_id anchor.docPr.name = 'Picture %d' % shape_id anchor.graphic.graphicData.uri = ( 'http://schemas.openxmlformats.org/drawingml/2006/picture' ) anchor.graphic.graphicData._insert_pic(pic) return anchor @classmethod def new_pic_anchor(cls, shape_id, rId, filename, cx, cy, pos_x, pos_y): """ Return a new `wp:anchor` element containing the `pic:pic` element specified by the argument values. """ pic_id = 0 # Word doesn't seem to use this, but does not omit it pic = CT_Picture.new(pic_id, filename, rId, cx, cy) anchor = cls.new(cx, cy, shape_id, pic, pos_x, pos_y) anchor.graphic.graphicData._insert_pic(pic) return anchor @classmethod def _anchor_xml(cls, pos_x, pos_y): return ( '\n' ' \n' ' \n' ' %d\n' ' \n' ' \n' ' %d\n' ' \n' ' \n' ' \n' ' \n' ' \n' ' \n' ' \n' ' \n' ' \n' ' \n' '' % ( nsdecls('wp', 'a', 'pic', 'r'), int(pos_x), int(pos_y) ) ) register_element_cls('wp:anchor', _CT_Anchor) def add_float_image(p, image_path_or_stream, width, pos_x=None, pos_y=None): '''Add float image behind text. Args: p (Paragraph): ``python-docx`` Paragraph object this picture belongs to. image_path_or_stream (str, bytes): Image path or stream. width (float): Displaying width of picture, in unit Pt. pos_x (float): X-position (English Metric Units) to the top-left point of page valid region pos_y (float): Y-position (English Metric Units) to the top-left point of page valid region ''' run = p.add_run() # parameters for picture, e.g. id, name rId, image = run.part.get_or_add_image(image_path_or_stream) cx, cy = image.scaled_dimensions(Pt(width), None) shape_id, filename = run.part.next_id, image.filename anchor = _CT_Anchor.new_pic_anchor(shape_id, rId, filename, cx, cy, Pt(pos_x), Pt(pos_y)) run._r.add_drawing(anchor) # --------------------------------------------------------- # table properties # --------------------------------------------------------- def indent_table(table, indent:float): '''Indent a table. Args: table (Table): ``python-docx`` Table object. indent (float): Indent value, the basic unit is 1/20 pt. ''' tbl_pr = table._element.xpath('w:tblPr') if tbl_pr: e = OxmlElement('w:tblInd') e.set(qn('w:w'), str(20*indent)) # basic unit 1/20 pt for openxml e.set(qn('w:type'), 'dxa') tbl_pr[0].append(e) def set_cell_margins(cell:_Cell, **kwargs): '''Set cell margins. Provided values are in twentieths of a point (1/1440 of an inch). Reference: * https://blog.csdn.net/weixin_44312186/article/details/104944773 * http://officeopenxml.com/WPtableCellMargins.php Args: cell (_Cell): ``python-docx`` Cell instance you want to modify. kwargs (dict): Dict with keys: top, bottom, start, end. Usage:: set_cell_margins(cell, top=50, start=50, bottom=50, end=50) ''' tc = cell._tc tcPr = tc.get_or_add_tcPr() tcMar = OxmlElement('w:tcMar') for m in ['top', 'start', 'bottom', 'end']: if m in kwargs: node = OxmlElement("w:{}".format(m)) node.set(qn('w:w'), str(kwargs.get(m))) node.set(qn('w:type'), 'dxa') tcMar.append(node) tcPr.append(tcMar) def set_cell_shading(cell:_Cell, srgb:int): '''Set cell background-color. Reference: https://stackoverflow.com/questions/26752856/python-docx-set-table-cell-background-and-text-color Args: cell (_Cell): ``python-docx`` Cell instance you want to modify srgb (int): RGB color value. ''' c = hex(srgb)[2:].zfill(6) cell._tc.get_or_add_tcPr().append(parse_xml(r''.format(nsdecls('w'), c))) def set_cell_border(cell:_Cell, **kwargs): '''Set cell`s border. Reference: * https://stackoverflow.com/questions/33069697/how-to-setup-cell-borders-with-python-docx * https://blog.csdn.net/weixin_44312186/article/details/104944110 Args: cell (_Cell): ``python-docx`` Cell instance you want to modify. kwargs (dict): Dict with keys: top, bottom, start, end. Usage:: set_cell_border( cell, top={"sz": 12, "val": "single", "color": "#FF0000", "space": "0"}, bottom={"sz": 12, "color": "#00FF00", "val": "single"}, start={"sz": 24, "val": "dashed", "shadow": "true"}, end={"sz": 12, "val": "dashed"}, ) ''' tc = cell._tc tcPr = tc.get_or_add_tcPr() # check for tag existence, if none found, then create one tcBorders = tcPr.first_child_found_in("w:tcBorders") if tcBorders is None: tcBorders = OxmlElement('w:tcBorders') tcPr.append(tcBorders) # list over all available tags for edge in ('start', 'top', 'end', 'bottom', 'insideH', 'insideV'): edge_data = kwargs.get(edge) if edge_data: tag = 'w:{}'.format(edge) # check for tag existence, if none found, then create one element = tcBorders.find(qn(tag)) if element is None: element = OxmlElement(tag) tcBorders.append(element) # looks like order of attributes is important for key in ["sz", "val", "color", "space", "shadow"]: if key in edge_data: element.set(qn('w:{}'.format(key)), str(edge_data[key])) def set_vertical_cell_direction(cell:_Cell, direction:str='btLr'): '''Set vertical text direction for cell. Reference: https://stackoverflow.com/questions/47738013/how-to-rotate-text-in-table-cells Args: direction (str): Either "tbRl" (top to bottom) or "btLr" (bottom to top). ''' tc = cell._tc tcPr = tc.get_or_add_tcPr() textDirection = OxmlElement('w:textDirection') textDirection.set(qn('w:val'), direction) # btLr tbRl tcPr.append(textDirection) pdf2docx-0.5.13/pdf2docx/common/share.py000066400000000000000000000222131517510251600177710ustar00rootroot00000000000000'''Common methods.''' from enum import Enum import random from collections.abc import Iterable from fitz.utils import getColorList, getColorInfoList class BlockType(Enum): '''Block types.''' UNDEFINED = -1 TEXT = 0 IMAGE = 1 LATTICE_TABLE = 2 STREAM_TABLE = 3 FLOAT_IMAGE = 4 class RectType(Enum): '''Shape type in context.''' HIGHLIGHT = 1 UNDERLINE = 1<<1 STRIKE = 1<<2 HYPERLINK = 1<<3 BORDER = 1<<4 SHADING = 1<<5 class TextDirection(Enum): '''Text direction. * LEFT_RIGHT: from left to right within a line, and lines go from top to bottom * BOTTOM_TOP: from bottom to top within a line, and lines go from left to right * MIX : a mixture if LEFT_RIGHT and BOTTOM_TOP * IGNORE : neither LEFT_RIGHT nor BOTTOM_TOP ''' IGNORE = -1 LEFT_RIGHT = 0 BOTTOM_TOP = 1 MIX = 2 class TextAlignment(Enum): '''Text alignment. .. note:: The difference between ``NONE`` and ``UNKNOWN``: * NONE: none of left/right/center align -> need TAB stop * UNKNOWN: can't decide, e.g. single line only ''' NONE = -1 UNKNOWN = 0 LEFT = 1 CENTER = 2 RIGHT = 3 JUSTIFY = 4 class IText: '''Text related interface considering text direction.''' @property def text_direction(self): '''Text direction is from left to right by default.''' return TextDirection.LEFT_RIGHT @property def is_horizontal_text(self): '''Check whether text direction is from left to right.''' return self.text_direction == TextDirection.LEFT_RIGHT or \ self.text_direction == TextDirection.MIX @property def is_vertical_text(self): '''Check whether text direction is from bottom to top.''' return self.text_direction == TextDirection.BOTTOM_TOP @property def is_mix_text(self): '''Check whether text direction is either from left to right or from bottom to top.''' return self.text_direction == TextDirection.MIX class lazyproperty: '''Calculate only once and cache property value.''' def __init__(self, func): self.func = func def __get__(self, instance, cls): if instance is None: return self else: value = self.func(instance) setattr(instance, self.func.__name__, value) return value # ------------------------- # methods # ------------------------- def is_number(str_number): '''Whether can be converted to a float.''' try: float(str_number) except ValueError: return False else: return True def flatten(items, klass): '''Yield items from any nested iterable.''' for item in items: if isinstance(item, Iterable) and not isinstance(item, klass): yield from flatten(item, klass) else: yield item def lower_round(number:float, ndigits:int=0): '''Round number to lower bound with specified digits, e.g. lower_round(1.26, 1)=1.2''' n = 10.0**ndigits return int(n*number) / n def decode(s:str): '''Try to decode a unicode string.''' b = bytes(ord(c) for c in s) for encoding in ['utf-8', 'gbk', 'gb2312', 'iso-8859-1']: try: res = b.decode(encoding) break except: continue return res # ------------------------- # color methods # ------------------------- def rgb_component_from_name(name:str=''): '''Get a named RGB color (or random color) from fitz predefined colors, e.g. 'red' -> (1.0,0.0,0.0).''' # get color index if name and name.upper() in getColorList(): pos = getColorList().index(name.upper()) else: pos = random.randint(0, len(getColorList())-1) c = getColorInfoList()[pos] return (c[1] / 255.0, c[2] / 255.0, c[3] / 255.0) def rgb_component(srgb:int): '''srgb value to R,G,B components, e.g. 16711680 -> (255, 0, 0). Equal to PyMuPDF built-in method:: [int(255*x) for x in fitz.sRGB_to_pdf(x)] ''' # decimal to hex: 0x... s = hex(srgb)[2:].zfill(6) return [int(s[i:i+2], 16) for i in [0, 2, 4]] def rgb_to_value(rgb:list): '''RGB components to decimal value, e.g. (1,0,0) -> 16711680.''' res = 0 for (i,x) in enumerate(rgb): res += int(x*(16**2-1)) * 16**(4-2*i) return int(res) def cmyk_to_rgb(c:float, m:float, y:float, k:float, cmyk_scale:float=100): '''CMYK components to GRB value.''' r = (1.0 - c / float(cmyk_scale)) * (1.0 - k / float(cmyk_scale)) g = (1.0 - m / float(cmyk_scale)) * (1.0 - k / float(cmyk_scale)) b = (1.0 - y / float(cmyk_scale)) * (1.0 - k / float(cmyk_scale)) res = rgb_to_value([r, g, b]) # type: int return res def rgb_value(components:list): '''Gray/RGB/CMYK mode components to color value.''' num = len(components) # CMYK mode if num==4: c, m, y, k = map(float, components) color = cmyk_to_rgb(c, m, y, k, cmyk_scale=1.0) # RGB mode elif num==3: r, g, b = map(float, components) color = rgb_to_value([r, g, b]) # gray mode elif num==1: g = float(components[0]) color = rgb_to_value([g,g,g]) else: color = 0 return color # ------------------------- # pdf plot # ------------------------- def new_page(doc, width:float, height:float, title:str): '''Insert a new page with given title. Args: doc (fitz.Document): pdf document object. width (float): Page width. height (float): Page height. title (str): Page title shown in page. ''' # insert a new page page = doc.new_page(width=width, height=height) # plot title at the top-left corner gray = rgb_component_from_name('gray') page.insert_text((5, 16), title, color=gray, fontsize=15) return page def debug_plot(title:str, show=True): '''Plot the returned objects of inner function. Args: title (str): Page title. show (bool, optional): Don't plot if show==False. Default to True. .. note:: Prerequisite of the inner function: - the first argument is a :py:class:`~pdf2docx.page.BasePage` instance. - the last argument is configuration parameters in ``dict`` type. ''' def wrapper(func): def inner(*args, **kwargs): # execute function objects = func(*args, **kwargs) # check if plot page page = args[0] # BasePage object debug = kwargs.get('debug', False) doc = kwargs.get('debug_doc', None) filename = kwargs.get('debug_filename', None) if show and objects and debug and doc is not None: # create a new page debug_page = new_page(doc, page.width, page.height, title) # plot objects, e.g. text blocks, shapes, tables... objects.plot(debug_page) doc.save(filename) return objects return inner return wrapper def is_list_item(text, bullets=True, numbers=True): '''Returns `text` if `bullets` is true and `text` is a bullet character, or `numbers` is true and `text` is not empty and consists entirely of digits 0-9. Otherwise returns None. If `bullets` is True we use an internal list of bullet characters; otherwise it should be a list of integer Unicode values. ''' return False if bullets is True: bullets2 = ( # From https://en.wikipedia.org/wiki/Bullet_(typography). 0x2022, # BULLET (•, •) 0x2023, # TRIANGULAR BULLET 0x2043, # HYPHEN BULLET (⁃) 0x204c, # BLACK LEFTWARDS BULLET 0x204d, # BLACK RIGHTWARDS BULLET 0x2219, # BULLET OPERATOR for use in mathematical notation primarily as a dot product instead of interpunct. 0x25c9, # FISHEYE used in Japan as a bullet, and called tainome. 0x25cb, # WHITE CIRCLE (○) 0x25cf, # BLACK CIRCLE 0x25cf, # Bullet, black small circle. 0x25d8, # INVERSE BULLET 0x25e6, # WHITE BULLET 0x2619, # REVERSED ROTATED FLORAL HEART BULLET; see Fleuron (typography) 0x2765, # ROTATED HEAVY BLACK HEART BULLET 0x2767, # ROTATED FLORAL HEART BULLET; see Fleuron (typography) 0x29be, # CIRCLED WHITE BULLET (⦾) 0x29bf, # CIRCLED BULLET (⦿) # Additional. 0x25aa, # Black small square, square bullet. 0xf0b7, # "Private Use Character" but seems to be used by libreoffice for bullets. ) else: bullets2 = bullets if bullets: if len(text)==1: c = text[0] cc = ord(c) if cc in bullets2: if bullets is True and cc == 0xf0b7: return chr(0x2022) return text if numbers: for c in text: if isinstance(c, list): c = c[0] if c not in '0123456789': break else: return text pdf2docx-0.5.13/pdf2docx/converter.py000066400000000000000000000501611517510251600174110ustar00rootroot00000000000000'''PDF to Docx Converter.''' import json import logging import os from multiprocessing import Pool, cpu_count from time import perf_counter from typing import AnyStr, IO, Union import fitz from docx import Document from .page.Page import Page from .page.Pages import Pages # check PyMuPDF version # 1.19.0 <= v <= 1.23.8, or v>=1.23.16 v = list(map(int, fitz.VersionBind.split("."))) if v < [1,19,0] or [1,23,8]=1.23.16 is required for pdf2docx.") # logging logging.basicConfig( level=logging.INFO, format="[%(levelname)s] %(message)s") class Converter: '''The ``PDF`` to ``docx`` converter. * Read PDF file with ``PyMuPDF`` to get raw layout data page by page, including text, image, drawing and its properties, e.g. boundary box, font, size, image width, height. * Analyze layout in document level, e.g. page header, footer and margin. * Parse page layout to docx structure, e.g. paragraph and its properties like indentation, spacing, text alignment; table and its properties like border, shading, merging. * Finally, generate docx with ``python-docx``. ''' def __init__( self, pdf_file: str = None, password: str = None, stream: bytes = None ): '''Initialize fitz object with given pdf file path. Args: pdf_file (str): pdf file path. stream (bytes): pdf file in memory. password (str): Password for encrypted pdf. Default to None if not encrypted. ''' # fitz object self.filename_pdf = pdf_file self.password = str(password or "") if not pdf_file and not stream: raise ValueError("Either pdf_file or stream must be given.") if stream: self._fitz_doc = fitz.Document(stream=stream) else: self._fitz_doc = fitz.Document(pdf_file) # initialize empty pages container self._pages = Pages() @property def fitz_doc(self): return self._fitz_doc @property def pages(self): return self._pages def close(self): self._fitz_doc.close() @property def default_settings(self): '''Default parsing parameters.''' return { 'debug' : False, # plot layout if True 'ocr' : 0, # ocr status: 0 - no ocr; 1 - to do ocr; 2 - ocr-ed pdf 'ignore_page_error' : True, # not break the conversion process due to failure of a certain page if True 'multi_processing' : False, # convert pages with multi-processing if True 'cpu_count' : 0, # working cpu count when convert pages with multi-processing 'min_section_height' : 20.0, # The minimum height of a valid section. 'connected_border_tolerance' : 0.5, # two borders are intersected if the gap lower than this value 'max_border_width' : 6.0, # max border width 'min_border_clearance' : 2.0, # the minimum allowable clearance of two borders 'float_image_ignorable_gap' : 5.0, # float image if the intersection exceeds this value 'page_margin_factor_top' : 0.5, # [0,1] reduce top margin by factor 'page_margin_factor_bottom' : 0.5, # [0,1] reduce bottom margin by factor 'shape_min_dimension' : 2.0, # ignore shape if both width and height is lower than this value 'max_line_spacing_ratio' : 1.5, # maximum line spacing ratio: line spacing / line height 'line_overlap_threshold' : 0.9, # [0,1] delete line if the intersection to other lines exceeds this value 'line_break_width_ratio' : 0.5, # break line if the ratio of line width to entire layout bbox is lower than this value 'line_break_free_space_ratio' : 0.1, # break line if the ratio of free space to entire line exceeds this value 'line_separate_threshold' : 5.0, # two separate lines if the x-distance exceeds this value 'new_paragraph_free_space_ratio' : 0.85, # new paragraph if the ratio of free space to line height exceeds this value 'lines_left_aligned_threshold' : 1.0, # left aligned if d_x0 of two lines is lower than this value (Pt) 'lines_right_aligned_threshold' : 1.0, # right aligned if d_x1 of two lines is lower than this value (Pt) 'lines_center_aligned_threshold' : 2.0, # center aligned if delta center of two lines is lower than this value 'clip_image_res_ratio' : 4.0, # resolution ratio (to 72dpi) when clipping page image 'min_svg_gap_dx' : 15.0, # merge adjacent vector graphics if the horizontal gap is less than this value 'min_svg_gap_dy' : 2.0, # merge adjacent vector graphics if the vertical gap is less than this value 'min_svg_w' : 2.0, # ignore vector graphics if the bbox width is less than this value 'min_svg_h' : 2.0, # ignore vector graphics if the bbox height is less than this value 'extract_stream_table' : False, # don't consider stream table when extracting tables 'parse_lattice_table' : True, # whether parse lattice table or not; may destroy the layout if set False 'parse_stream_table' : True, # whether parse stream table or not; may destroy the layout if set False 'delete_end_line_hyphen' : False, # delete hyphen at the end of a line 'raw_exceptions' : False, # Don't swallow exceptions 'list_not_table' : True, # Avoid treating bullet list as table. } # ----------------------------------------------------------------------- # Parsing process: load -> analyze document -> parse pages -> make docx # ----------------------------------------------------------------------- def parse(self, start:int=0, end:int=None, pages:list=None, **kwargs): '''Parse pages in three steps: * open PDF file with ``PyMuPDF`` * analyze whole document, e.g. page section, header/footer and margin * parse specified pages, e.g. paragraph, image and table Args: start (int, optional): First page to process. Defaults to 0, the first page. end (int, optional): Last page to process. Defaults to None, the last page. pages (list, optional): Range of page indexes to parse. Defaults to None. kwargs (dict, optional): Configuration parameters. ''' return self.load_pages(start, end, pages) \ .parse_document(**kwargs) \ .parse_pages(**kwargs) def load_pages(self, start:int=0, end:int=None, pages:list=None): '''Step 1 of converting process: open PDF file with ``PyMuPDF``, especially for password encrypted file. Args: start (int, optional): First page to process. Defaults to 0, the first page. end (int, optional): Last page to process. Defaults to None, the last page. pages (list, optional): Range of page indexes to parse. Defaults to None. ''' logging.info(self._color_output('[1/4] Opening document...')) # encrypted pdf ? if self._fitz_doc.needs_pass: if not self.password: raise ConversionException(f'Require password for {self.filename_pdf}.') elif not self._fitz_doc.authenticate(self.password): raise ConversionException('Incorrect password.') # initialize empty pages num = len(self._fitz_doc) self._pages.reset([Page(id=i, skip_parsing=True) for i in range(num)]) # set pages to parse page_indexes = self._page_indexes(start, end, pages, num) for i in page_indexes: self._pages[i].skip_parsing = False return self def parse_document(self, **kwargs): '''Step 2 of converting process: analyze whole document, e.g. page section, header/footer and margin.''' logging.info(self._color_output('[2/4] Analyzing document...')) self._pages.parse(self.fitz_doc, **kwargs) return self def parse_pages(self, **kwargs): '''Step 3 of converting process: parse pages, e.g. paragraph, image and table.''' logging.info(self._color_output('[3/4] Parsing pages...')) pages = [page for page in self._pages if not page.skip_parsing] num_pages = len(pages) for i, page in enumerate(pages, start=1): pid = page.id + 1 logging.info('(%d/%d) Page %d', i, num_pages, pid) try: page.parse(**kwargs) except Exception as e: if kwargs['raw_exceptions']: raise if not kwargs['debug'] and kwargs['ignore_page_error']: logging.error('Ignore page %d due to parsing page error: %s', pid, e) else: raise ConversionException(f'Error when parsing page {pid}: {e}') return self def make_docx(self, filename_or_stream=None, **kwargs): '''Step 4 of converting process: create docx file with converted pages. Args: filename_or_stream (str, file-like): docx file to write. kwargs (dict, optional): Configuration parameters. ''' logging.info(self._color_output('[4/4] Creating pages...')) # check parsed pages parsed_pages = list(filter( lambda page: page.finalized, self._pages )) if not parsed_pages: raise ConversionException('No parsed pages. Please parse page first.') if not filename_or_stream: if self.filename_pdf: filename_or_stream = f'{self.filename_pdf[0:-len(".pdf")]}.docx' # remove existing file if os.path.exists(filename_or_stream): os.remove(filename_or_stream) else: raise ConversionException("Please specify a docx file name or a file-like object to write.") # create page by page docx_file = Document() num_pages = len(parsed_pages) for i, page in enumerate(parsed_pages, start=1): if not page.finalized: continue # ignore unparsed pages pid = page.id + 1 logging.info('(%d/%d) Page %d', i, num_pages, pid) try: page.make_docx(docx_file) except Exception as e: if kwargs['raw_exceptions']: raise if not kwargs['debug'] and kwargs['ignore_page_error']: logging.error('Ignore page %d due to making page error: %s', pid, e) else: raise MakedocxException(f'Error when make page {pid}: {e}') # save docx docx_file.save(filename_or_stream) # ----------------------------------------------------------------------- # Store / restore parsed results # ----------------------------------------------------------------------- def store(self): '''Store parsed pages in dict format.''' return { 'filename': os.path.basename(self.filename_pdf), 'page_cnt': len(self._pages), # count of all pages 'pages' : [page.store() for page in self._pages if page.finalized], # parsed pages only } def restore(self, data:dict): '''Restore pages from parsed results.''' # init empty pages if necessary if not self._pages: num = data.get('page_cnt', 100) self._pages.reset([Page(id=i, skip_parsing=True) for i in range(num)]) # restore pages for raw_page in data.get('pages', []): idx = raw_page.get('id', -1) self._pages[idx].restore(raw_page) def serialize(self, filename:str): '''Write parsed pages to specified JSON file.''' with open(filename, 'w', encoding='utf-8') as f: f.write(json.dumps(self.store(), indent=4)) def deserialize(self, filename:str): '''Load parsed pages from specified JSON file.''' with open(filename, 'r') as f: data = json.load(f) self.restore(data) # ----------------------------------------------------------------------- # high level methods, e.g. convert, extract table # ----------------------------------------------------------------------- def debug_page(self, i:int, docx_filename:str=None, debug_pdf:str=None, layout_file:str=None, **kwargs): '''Parse, create and plot single page for debug purpose. Args: i (int): Page index to convert. docx_filename (str): docx filename to write to. debug_pdf (str): New pdf file storing layout information. Default to add prefix ``debug_``. layout_file (str): New json file storing parsed layout data. Default to ``layout.json``. ''' # include debug information # fitz object in debug mode: plot page layout # file path for this debug pdf: demo.pdf -> debug_demo.pdf path, filename = os.path.split(self.filename_pdf) if not debug_pdf: debug_pdf = os.path.join(path, f'debug_{filename}') if not layout_file: layout_file = os.path.join(path, 'layout.json') kwargs.update({ 'debug' : True, 'debug_doc' : fitz.Document(), 'debug_filename': debug_pdf }) # parse and create docx self.convert(docx_filename, pages=[i], **kwargs) # layout information for debugging self.serialize(layout_file) def convert(self, docx_filename: Union[str, IO[AnyStr]] = None, start: int = 0, end: int = None, pages: list = None, **kwargs): """Convert specified PDF pages to docx file. Args: docx_filename (str, file-like, optional): docx file to write. Defaults to None. start (int, optional): First page to process. Defaults to 0, the first page. end (int, optional): Last page to process. Defaults to None, the last page. pages (list, optional): Range of page indexes. Defaults to None. kwargs (dict, optional): Configuration parameters. Defaults to None. Refer to :py:meth:`~pdf2docx.converter.Converter.default_settings` for detail of configuration parameters. .. note:: Change extension from ``pdf`` to ``docx`` if ``docx_file`` is None. .. note:: * ``start`` and ``end`` is counted from zero if ``--zero_based_index=True`` (by default). * Start from the first page if ``start`` is omitted. * End with the last page if ``end`` is omitted. .. note:: ``pages`` has a higher priority than ``start`` and ``end``. ``start`` and ``end`` works only if ``pages`` is omitted. .. note:: Multi-processing works only for continuous pages specified by ``start`` and ``end`` only. """ t0 = perf_counter() logging.info('Start to convert %s', self.filename_pdf) settings = self.default_settings settings.update(kwargs) # input check if pages and settings['multi_processing']: raise ConversionException('Multi-processing works for continuous pages ' 'specified by "start" and "end" only.') # convert page by page if settings['multi_processing']: self._convert_with_multi_processing(docx_filename, start, end, **settings) else: self.parse(start, end, pages, **settings).make_docx(docx_filename, **settings) logging.info('Terminated in %.2fs.', perf_counter()-t0) def extract_tables(self, start:int=0, end:int=None, pages:list=None, **kwargs): '''Extract table contents from specified PDF pages. Args: start (int, optional): First page to process. Defaults to 0, the first page. end (int, optional): Last page to process. Defaults to None, the last page. pages (list, optional): Range of page indexes. Defaults to None. kwargs (dict, optional): Configuration parameters. Defaults to None. Returns: list: A list of parsed table content. ''' # parsing pages first settings = self.default_settings settings.update(kwargs) self.parse(start, end, pages, **settings) # get parsed tables tables = [] for page in self._pages: if page.finalized: tables.extend(page.extract_tables(**settings)) return tables def _convert_with_multi_processing(self, docx_filename:str, start:int, end:int, **kwargs): '''Parse and create pages based on page indexes with multi-processing. Reference: https://pymupdf.readthedocs.io/en/latest/faq.html#multiprocessing ''' # make vectors of arguments for the processes cpu = min(kwargs['cpu_count'], cpu_count()) if kwargs['cpu_count'] else cpu_count() prefix = 'pages' # json file writing parsed pages per process vectors = [(i, cpu, start, end, self.filename_pdf, self.password, kwargs, f'{prefix}-{i}.json') for i in range(cpu)] # start parsing processes pool = Pool() pool.map(self._parse_pages_per_cpu, vectors, 1) # restore parsed page data for i in range(cpu): filename = f'{prefix}-{i}.json' if not os.path.exists(filename): continue self.deserialize(filename) os.remove(filename) # create docx file self.make_docx(docx_filename, **kwargs) @staticmethod def _parse_pages_per_cpu(vector): '''Render a page range of a document. Args: vector (list): A list containing required parameters. * 0 : segment number for current process * 1 : count of CPUs * 2,3: whole pages range to process * 4 : pdf filename * 5 : password for encrypted pdf * 6 : configuration parameters * 7 : json filename storing parsed results ''' # recreate the arguments idx, cpu, s, e, pdf_filename, password, kwargs, json_filename = vector # open pdf to get page count: all pages are marked to parse temporarily # since don't know which pages to parse for this moment cv = Converter(pdf_filename, password) cv.load_pages() # the specified pages to process e = e or len(cv.fitz_doc) all_indexes = range(s, e) num_pages = len(all_indexes) # page segment processed by this cpu m = int(num_pages/cpu) n = num_pages % cpu seg_size = m + int(idx Calibri.''' return name.split('+')[-1].split('-')[0] @staticmethod def _to_descriptor(name:str): '''Remove potential space, dash in font name, and turn to upper case.''' return name.replace(' ', '').replace('-', '').upper() @staticmethod def get_font_family_name(tt_font:TTFont): '''Get the font family name from the font's names table. https://gist.github.com/pklaus/dce37521579513c574d0 ''' name = family = '' FONT_SPECIFIER_NAME_ID = 4 FONT_SPECIFIER_FAMILY_ID = 1 for record in tt_font['name'].names: if b'\x00' in record.string: name_str = record.string.decode('utf-16-be') else: name_str = record.string.decode('latin-1') if record.nameID == FONT_SPECIFIER_NAME_ID and not name: name = name_str elif record.nameID == FONT_SPECIFIER_FAMILY_ID and not family: family = name_str if name and family: break # in case the font name is modified to pattern like BCDGEE+Calibri-Bold return Fonts._normalized_font_name(family) @staticmethod def get_line_height_factor(tt_font:TTFont): '''Calculate line height ratio based on ``hhea`` and ``OS/2`` tables. Fon non-CJK fonts:: f = (hhea.Ascent - hhea.Descent + hhea.LineGap) / units_per_em For non-CJK fonts (Windows):: f = (OS/2.winAscent + OS/2.winDescent + [External Leading]) / units_per_em External Leading = MAX(0, hhea.LineGap - ((OS/2.WinAscent + OS/2.winDescent) - (hhea.Ascent - hhea.Descent))) For CJK fonts:: f = 1.3 * (hhea.Ascent - hhea.Descent) / units_per_em Read more: * https://docs.microsoft.com/en-us/typography/opentype/spec/recom#baseline-to-baseline-distances * https://github.com/source-foundry/font-line#baseline-to-baseline-distance-calculations * https://www.zhihu.com/question/23349103 * https://github.com/source-foundry/font-line/blob/master/lib/fontline/metrics.py ''' units_per_em = tt_font["head"].unitsPerEm # hhea metrics hhea = tt_font["hhea"] hhea_ascent = hhea.ascent hhea_descent = hhea.descent hhea_linegap = hhea.lineGap hhea_total_height = hhea_ascent + abs(hhea_descent) hhea_btb_distance = hhea_total_height + hhea_linegap # depends on System if os.name=='nt': # OS/2 metrics os2 = tt_font["OS/2"] os2_win_ascent = os2.usWinAscent os2_win_descent = os2.usWinDescent os2_win_total_height = os2_win_ascent + os2_win_descent win_external_leading = max(0.0, hhea_linegap-(os2_win_total_height-hhea_total_height)) win_btb_distance = os2_win_total_height + win_external_leading btb_distance = win_btb_distance else: btb_distance = hhea_btb_distance # depends on CJK font or not cjk = Fonts.is_cjk_font(tt_font) distance = 1.3*hhea_total_height if cjk else 1.0*btb_distance return distance / units_per_em @staticmethod def is_cjk_font(tt_font:TTFont): '''Test font object to confirm that it meets our definition of a CJK font file. The definition is met if any of the following conditions are True: 1. The font has a CJK code page bit set in the OS/2 table 2. The font has a CJK Unicode range bit set in the OS/2 table 3. The font has any CJK Unicode code points defined in the cmap table https://github.com/googlefonts/fontbakery/blob/main/Lib/fontbakery/profiles/shared_conditions.py ''' os2 = tt_font["OS/2"] # OS/2 code page checks for _, bit in CJK_CODEPAGE_BITS.items(): if hasattr(os2, 'ulCodePageRange1') and os2.ulCodePageRange1 & (1 << bit): return True # OS/2 Unicode range checks for _, bit in CJK_UNICODE_RANGE_BITS.items(): if bit in range(0, 32): if hasattr(os2, 'ulCodePageRange1') and os2.ulUnicodeRange1 & (1 << bit): return True elif bit in range(32, 64): if hasattr(os2, 'ulCodePageRange2') and os2.ulUnicodeRange2 & (1 << (bit-32)): return True elif bit in range(64, 96): if hasattr(os2, 'ulCodePageRange3') and os2.ulUnicodeRange3 & (1 << (bit-64)): return True # defined CJK Unicode code point in cmap table checks try: cmap = tt_font.getBestCmap() except: return False if not cmap: return False for unicode_range in CJK_UNICODE_RANGES: for x in range(unicode_range[0], unicode_range[1]+1): if int(x) in cmap: return True # default, return False if the above checks did not identify a CJK font return False pdf2docx-0.5.13/pdf2docx/font/__init__.py000066400000000000000000000000001517510251600200720ustar00rootroot00000000000000pdf2docx-0.5.13/pdf2docx/gui/000077500000000000000000000000001517510251600156115ustar00rootroot00000000000000pdf2docx-0.5.13/pdf2docx/gui/App.py000066400000000000000000000017251517510251600167100ustar00rootroot00000000000000'''Main window for ``pdf2docx`` graphic user interface.''' import sys from tkinter import Tk, messagebox from .MainFrame import MainFrame class App(Tk): '''Simple graphic user interface.''' def __init__(self, title:str='App', width:int=300, height:int=200): '''Top app window.''' super().__init__() self.title(title) self.geometry(f'{width}x{height}') self.resizable(0, 0) # not allowed to change size # layout on the root window self.__create_widgets() # Quit window self.protocol("WM_DELETE_WINDOW", self._on_closing) def __create_widgets(self): self.widget = MainFrame(self) self.widget.grid(column=0, row=0) def _on_closing(self): if messagebox.askokcancel("Quit", "Do you want to quit?"): self.destroy() sys.exit(0) if __name__ == "__main__": app = App(title='PDF_2_Docx Converter', width=500, height=600) app.mainloop() pdf2docx-0.5.13/pdf2docx/gui/MainFrame.py000066400000000000000000000133501517510251600200240ustar00rootroot00000000000000'''Main frame with grouped controls.''' import os from tkinter import Tk, Frame, Label, Entry, Button, filedialog, messagebox import webbrowser from pdf2docx import Converter class MainFrame(Frame): def __init__(self, parent:Tk=None): '''Main frame with grouped controls. This should be attached to a parent ``Tk`` instance.''' super().__init__(parent) self.parent = parent self.config(bg='grey') self.grid(ipadx=100, ipady=100) self.setup_ui() # variables self.pdf_paths = set() # unique pdf files self.docx_folder = None def setup_ui(self): '''Layout of the user interface.''' # PDF to Docx Label self.program_use_label = Label(self, text='PDF to Docx', font='Impact 40', bg='white', fg='#1E90FF') self.program_use_label.grid(row=0, column=0, columnspan=2, padx=120, pady=50) # PDF file entry self.file_path_pdf_entry = Entry(self, border=5) self.file_path_pdf_entry.grid(row=1, column=0, sticky='w', ipady=4, padx=10) # Docx file entry self.file_path_docx_entry = Entry(self, border=5) self.file_path_docx_entry.grid(row=2, column=0, sticky='w', ipady=4, padx=10, pady=75) # Select PDF files Button self.select_pdf_file = Button(self, text='Select PDF files', fg='black', bg='white', border=3, command=self._callback_pdf_file_location) self.select_pdf_file.grid(row=1, column=1, sticky='w') # Select new files folder Button self.select_new_file_folder = Button(self, text='Select new files folder', fg='black', bg='white', border=3, command=self._callback_docx_folder_location) self.select_new_file_folder.grid(row=2, column=1, sticky='w') # Convert Button self.converter_button = Button(self, text='Convert', bg='#1E90FF', fg='white', font='impact 20', border=5, command=self._callback_convert) self.converter_button.grid(row=3, column=0, columnspan=2, ipady=5, ipadx=10) # *This converter can only convert text based pdf cant convert image based pdf Label self.check_label = Label(self, text='*This converter can only convert text based pdf \n cant convert image based pdf', bg='grey') self.check_label.grid(row=4, column=0, columnspan=2, pady=10) # how to check my file Button self.file_check_button = Button(self, text='how to check my file?', bg='grey', fg='black', command=lambda: webbrowser.open_new(r"https://support.policystat.com/hc/en-us/articles" r"/207993346-How-can-I-tell-if-my-PDFs-are-text-based-or-not-")) self.file_check_button.grid(row=5, column=0, columnspan=2) def _callback_pdf_file_location(self): '''Opens file explorer and let you select choose the pdf file that you want to convert.''' file_paths = filedialog.askopenfilenames(filetypes=[('PDF file', '*.pdf')]) self.pdf_paths = set() for path in file_paths: self.pdf_paths.add(path) # show just names names = ';'.join([f'"{os.path.basename(path)}"' for path in self.pdf_paths]) self.file_path_pdf_entry.delete(0, 'end') self.file_path_pdf_entry.insert(0, names) def _callback_docx_folder_location(self): '''Opens file explorer and let you choose the folder, that all the converted file or files going to saved.''' self.docx_folder = filedialog.askdirectory() self.file_path_docx_entry.delete(0, 'end') self.file_path_docx_entry.insert(0, self.docx_folder) def _callback_convert(self): '''Starts the convert of the file or files.''' # input check if not self.pdf_paths and not self.docx_folder: messagebox.showwarning( title='Neither files or folder selected', message='Select PDF file or files for convert ' 'and Select a folder for the converted files!') return if not self.pdf_paths: messagebox.showwarning( title='Not files for convert selected', message='Select PDF file or PDF files for convert!') return if not self.docx_folder: messagebox.showwarning( title='Not files folder selected', message='Select a folder for the converted files!') return # collect docx files to convert to docx_paths = [] for pdf_path in self.pdf_paths: base_name = os.path.basename(pdf_path) name, ext = os.path.splitext(base_name) docx_path = os.path.join(self.docx_folder, f'{name}.docx') docx_paths.append(docx_path) if any([os.path.exists(path) for path in docx_paths]) and \ not messagebox.askokcancel(title='Existed target file', message='Docx files with same target name are found under selected folder. ' 'Do you want to continue and replace them?'): return # now, do the converting work num_succ, num_fail = 0, 0 for pdf_path, docx_path in zip(self.pdf_paths, docx_paths): cv = Converter(pdf_path) try: cv.convert(docx_path) except Exception as e: print(e) num_fail += 1 else: num_succ += 1 finally: cv.close() messagebox.showinfo(title='Convert Done!', message=f'Successful ({num_succ}), Failed ({num_fail}).') pdf2docx-0.5.13/pdf2docx/gui/__init__.py000066400000000000000000000000001517510251600177100ustar00rootroot00000000000000pdf2docx-0.5.13/pdf2docx/image/000077500000000000000000000000001517510251600161075ustar00rootroot00000000000000pdf2docx-0.5.13/pdf2docx/image/Image.py000066400000000000000000000051001517510251600174770ustar00rootroot00000000000000# -*- coding: utf-8 -*- '''Image object. Data structure defined in link https://pymupdf.readthedocs.io/en/latest/textpage.html:: { 'type': 1, 'bbox': (x0,y0,x1,y1), 'width': w, 'height': h, 'image': b'', # --- discard properties --- 'ext': 'png', 'colorspace': n, 'xref': xref, 'yref': yref, 'bpc': bpc } ''' import base64 from io import BytesIO from ..common import docx from ..common.Element import Element class Image(Element): '''Base image object.''' def __init__(self, raw:dict=None): if raw is None: raw = {} self.width = raw.get('width', 0.0) self.height = raw.get('height', 0.0) # source image bytes # - image bytes passed from PyMuPDF -> use it directly # - base64 encoded string restored from json file -> encode to bytes and decode with base64 -> image bytes image = raw.get('image', b'') self.image = image if isinstance(image, bytes) else base64.b64decode(image.encode()) super().__init__(raw) @property def text(self): '''Get an image placeholder ````.''' return '' def from_image(self, image): '''Update with image block/span. Args: image (Image): Target image block/span. ''' self.width = image.width self.height = image.height self.image = image.image self.update_bbox(image.bbox) return self def store(self): '''Store image with base64 encode. * Encode image bytes with base64 -> base64 bytes * Decode base64 bytes -> str -> so can be serialized in json format ''' res = super().store() res.update({ 'width': self.width, 'height': self.height, 'image': base64.b64encode(self.image).decode() # serialize image with base64 }) return res def plot(self, page, color:tuple): '''Plot image bbox with diagonal lines (for debug purpose). Args: page (fitz.Page): Plotting page. ''' x0, y0, x1, y1 = self.bbox page.draw_line((x0, y0), (x1, y1), color=color, width=0.5) page.draw_line((x0, y1), (x1, y0), color=color, width=0.5) super().plot(page, stroke=color) def make_docx(self, paragraph): '''Add image span to a docx paragraph.''' # add image docx.add_image(paragraph, BytesIO(self.image), self.bbox.x0, self.bbox.y0, self.bbox.x1-self.bbox.x0, self.bbox.y1-self.bbox.y0)pdf2docx-0.5.13/pdf2docx/image/ImageBlock.py000066400000000000000000000037641517510251600204700ustar00rootroot00000000000000# -*- coding: utf-8 -*- '''Definition of Image block objects. **The raw image block will be merged into TextBlock > Line > Span.** ''' from io import BytesIO from ..text.Line import Line from ..text.TextBlock import TextBlock from .Image import Image from .ImageSpan import ImageSpan from ..common.Block import Block from ..common.docx import add_float_image class ImageBlock(Image, Block): '''Image block.''' def __init__(self, raw:dict=None): super().__init__(raw) # inline image type by default self.set_inline_image_block() def to_text_block(self): """Convert image block to a span under text block. Returns: TextBlock: New TextBlock instance containing this image. """ # image span span = ImageSpan().from_image(self) # add span to line image_line = Line() image_line.add(span) # insert line to block block = TextBlock() block.add(image_line) # NOTE: it's an image block even though in TextBlock type block.set_inline_image_block() return block def store(self): '''Store ImageBlock instance in raw dict.''' res = Block.store(self) res.update( Image.store(self) ) return res def plot(self, page): '''Plot image bbox with diagonal lines (for debug purpose). Args: page (fitz.Page): pdf page to plot. ''' super().plot(page, color=(1,0,0)) def make_docx(self, p): '''Create floating image behind text. Args: p (Paragraph): ``python-docx`` paragraph instance. .. note:: Inline image is created within TextBlock. ''' if self.is_float_image_block: x0, y0, x1, y1 = self.bbox add_float_image(p, BytesIO(self.image), width=x1-x0, pos_x=x0, pos_y=y0) else: super().make_docx(p) return ppdf2docx-0.5.13/pdf2docx/image/ImageSpan.py000066400000000000000000000013411517510251600203240ustar00rootroot00000000000000# -*- coding: utf-8 -*- '''Image span based on same raw data structure with Image block. ''' from ..common import constants from .Image import Image class ImageSpan(Image): '''Image span.''' def intersects(self, rect): '''Create new ImageSpan object with image contained in given bbox. Args: rect (fitz.Rect): Target bbox. Returns: ImageSpan: A copy of itself if intersects with target; otherwise empty ImageSpan. ''' # add image span if most of of the image is contained in bbox if self.get_main_bbox(rect, constants.FACTOR_MAJOR): return self.copy() # otherwise, ignore image return ImageSpan()pdf2docx-0.5.13/pdf2docx/image/ImagesExtractor.py000066400000000000000000000443771517510251600216010ustar00rootroot00000000000000"""Extract images from PDF. Both raster images and vector graphics are considered: * Normal images like jpeg or png could be extracted with method ``page.get_text('rawdict')`` and ``Page.get_images()``. Note the process for png images with alpha channel. * Vector graphics are actually composed of a group of paths, represented by operators like ``re``, ``m``, ``l`` and ``c``. They're detected by finding the contours with ``opencv``. """ import logging import math import fitz from ..common.Collection import Collection from ..common.share import BlockType from ..common.algorithm import recursive_xy_cut, inner_contours, xy_project_profile class ImagesExtractor: """Extract images from PDF.""" def __init__(self, page: fitz.Page) -> None: """Extract images from PDF page. Args: page (fitz.Page): pdf page to extract images. """ self._page = page def clip_page_to_pixmap( self, bbox: fitz.Rect = None, rm_image: bool = False, zoom: float = 3.0 ): """Clip page pixmap according to ``bbox``. Args: bbox (fitz.Rect, optional): Target area to clip. Defaults to None, i.e. entire page. Note that ``bbox`` depends on un-rotated page CS, while clipping page is based on the final page. rm_image (bool): remove images or not. zoom (float, optional): Improve resolution by this rate. Defaults to 3.0. Returns: fitz.Pixmap: The extracted pixmap. """ # hide text and images stream_dict = self._hide_page_text_and_images( self._page, rm_text=True, rm_image=rm_image ) if bbox is None: clip_bbox = self._page.rect # transform to the final bbox when page is rotated elif self._page.rotation: clip_bbox = bbox * self._page.rotation_matrix else: clip_bbox = bbox clip_bbox = self._page.rect & clip_bbox # improve resolution # - https://pymupdf.readthedocs.io/en/latest/faq.html#how-to-increase-image-resolution # - https://github.com/pymupdf/PyMuPDF/issues/181 matrix = fitz.Matrix(zoom, zoom) pix = self._page.get_pixmap(clip=clip_bbox, matrix=matrix) # type: fitz.Pixmap # recovery page if hide text doc = self._page.parent for xref, stream in stream_dict.items(): doc.update_stream(xref, stream) return pix def clip_page_to_dict( self, bbox: fitz.Rect = None, rm_image: bool = False, clip_image_res_ratio: float = 3.0, ): """Clip page pixmap (without text) according to ``bbox`` and convert to source image. Args: bbox (fitz.Rect, optional): Target area to clip. Defaults to None, i.e. entire page. rm_image (bool): remove images or not. clip_image_res_ratio (float, optional): Resolution ratio of clipped bitmap. Defaults to 3.0. Returns: list: A list of image raw dict. """ pix = self.clip_page_to_pixmap( bbox=bbox, rm_image=rm_image, zoom=clip_image_res_ratio ) return self._to_raw_dict(pix, bbox) @staticmethod def _get_image_rotation(matrix) -> int: """Extract rotation angle (0, 90, 180, 270) from image transform matrix. Handles 90-degree multiples only. Based on PyMuPDF Matrix format: fitz.Matrix(90) -> (0, 1, -1, 0, ...), Matrix(180) -> (-1, 0, 0, -1, ...). """ if matrix is None: return 0 try: a, b, c, d = matrix.a, matrix.b, matrix.c, matrix.d except AttributeError: return 0 # Round to nearest 90 degrees using atan2 angle_rad = math.atan2(b, a) angle_deg = round(math.degrees(angle_rad) / 90) * 90 return int(angle_deg % 360) def extract_images(self, clip_image_res_ratio: float = 3.0): """Extract normal images with ``Page.get_images()``. Args: clip_image_res_ratio (float, optional): Resolution ratio of clipped bitmap. Defaults to 3.0. Returns: list: A list of extracted and recovered image raw dict. .. note:: ``Page.get_images()`` contains each image only once, which may less than the real count of images in a page. """ # pdf document doc = self._page.parent rotation = self._page.rotation # The final view might be formed by several images with alpha channel only, # as shown in issue-123. # It's still inconvenient to extract the original alpha/mask image, as a compromise, # extract the equivalent image by clipping the union page region for now. # https://github.com/dothinking/pdf2docx/issues/123 # step 1: collect images: [(bbox, item, image_rotation), ..., ] ic = Collection() for item in self._page.get_images(full=True): item = list(item) item[-1] = 0 xref = item[0] # find all occurrences referenced to this image rects = self._page.get_image_rects(item) # get per-image transform (rotation) when available (PyMuPDF 1.19+) rects_with_transform = [] try: rects_with_transform = self._page.get_image_rects(xref, transform=True) except (TypeError, AttributeError): pass unrotated_page_bbox = self._page.cropbox # note the difference to page.rect for i, bbox in enumerate(rects): # ignore small images if bbox.get_area() <= 4: continue # ignore images outside page if not unrotated_page_bbox.intersects(bbox): continue # extract per-image rotation from transform matrix image_rotation = 0 if i < len(rects_with_transform): entry = rects_with_transform[i] if isinstance(entry, (list, tuple)) and len(entry) >= 2: matrix_t = entry[1] image_rotation = self._get_image_rotation(matrix_t) ic.append((bbox, item, image_rotation)) # step 2: group by intersection fun = lambda a, b: a[0].intersects(b[0]) groups = ic.group(fun) # step 3: check each group images = [] for group in groups: # clip page with the union bbox of all intersected images if len(group) > 1: clip_bbox = fitz.Rect() for bbox, item, _ in group: clip_bbox |= bbox raw_dict = self.clip_page_to_dict( clip_bbox, False, clip_image_res_ratio ) else: bbox, item, image_rotation = group[0] # Regarding images consist of alpha values only, the turquoise color shown in # the PDF is not part of the image, but part of PDF background. # So, just to clip page pixmap according to the right bbox # https://github.com/pymupdf/PyMuPDF/issues/677 # It's not safe to identify images with alpha values only, # - colorspace is None, for pymupdf <= 1.23.8 # - colorspace is always Colorspace(CS_RGB), for pymupdf==1.23.9-15 -> issue # - colorspace is Colorspace(CS_), for pymupdf >= 1.23.16 # So, use extracted image info directly. # image item: (xref, smask, width, height, bpc, colorspace, ...), e.g., # (19, 0, 331, 369, 1, '', '', 'Im1', 'FlateDecode', 0) # (20, 24, 1265, 1303, 8, 'DeviceRGB', '', 'Im2', 'FlateDecode', 0) # (21, 0, 331, 369, 1, '', '', 'Im3', 'CCITTFaxDecode', 0) # (22, 25, 1265, 1303, 8, 'DeviceGray', '', 'Im4', 'DCTDecode', 0) # (23, 0, 1731, 1331, 8, 'DeviceGray', '', 'Im5', 'DCTDecode', 0) if item[5] == "": raw_dict = self.clip_page_to_dict(bbox, False, clip_image_res_ratio) # normal images else: # recover image, e.g., handle image with mask, or CMYK color space pix = self._recover_pixmap(doc, item) # rotate image: apply inverse of per-image transform, then page rotation # (PyMuPDF matrix maps image->page; correct pixmap with inverse: apply -angle) raw_dict = self._to_raw_dict(pix, bbox) total_rotation = (rotation or 0) - image_rotation if total_rotation: raw_dict["image"] = self._rotate_image(pix, total_rotation) images.append(raw_dict) return images def detect_svg_contours( self, min_svg_gap_dx: float, min_svg_gap_dy: float, min_w: float, min_h: float ): """Find contour of potential vector graphics. Args: min_svg_gap_dx (float): Merge svg if the horizontal gap is less than this value. min_svg_gap_dy (float): Merge svg if the vertical gap is less than this value. min_w (float): Ignore contours if the bbox width is less than this value. min_h (float): Ignore contours if the bbox height is less than this value. Returns: list: A list of potential svg region: (external_bbox, inner_bboxes:list). """ import cv2 as cv # clip page and convert to opencv image pixmap = self.clip_page_to_pixmap(rm_image=True, zoom=1.0) src = self._pixmap_to_cv_image(pixmap) # gray and binary gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) _, binary = cv.threshold(gray, 253, 255, cv.THRESH_BINARY_INV) # external bbox: split images with recursive xy cut external_bboxes = recursive_xy_cut( binary, min_dx=min_svg_gap_dx, min_dy=min_svg_gap_dy ) # inner contours grouped_inner_bboxes = [ inner_contours(binary, bbox, min_w, min_h) for bbox in external_bboxes ] # combined external and inner contours groups = list(zip(external_bboxes, grouped_inner_bboxes)) # plot detected images for debug debug = False if debug: # plot projection profile for each sub-image for i, (x0, y0, x1, y1) in enumerate(external_bboxes): arr = xy_project_profile(src[y0:y1, x0:x1, :], binary[y0:y1, x0:x1]) cv.imshow(f"sub-image-{i}", arr) for bbox, inner_bboxes in groups: # plot external bbox x0, y0, x1, y1 = bbox cv.rectangle(src, (x0, y0), (x1, y1), (255, 0, 0), 1) # plot inner bbox for u0, v0, u1, v1 in inner_bboxes: cv.rectangle(src, (u0, v0), (u1, v1), (0, 0, 255), 1) cv.imshow("img", src) cv.waitKey(0) return groups @staticmethod def _to_raw_dict(image: fitz.Pixmap, bbox: fitz.Rect): """Store Pixmap ``image`` to raw dict. Args: image (fitz.Pixmap): Pixmap to store. bbox (fitz.Rect): Boundary box the pixmap. Returns: dict: Raw dict of the pixmap. """ """ if image.colorspace.n > 3: # must convert: we only support PNG image with alpha channel image = fitz.Pixmap(fitz.csRGB, image) """ # added by maksym # Get the colorspace object of the pixmap. # NOTE: colorspace.n (number of channels) is NOT sufficient to decide # whether MuPDF can write this pixmap as PNG. cs = image.colorspace # MuPDF can write PNG only if the colorspace is exactly: # - DeviceGray (fitz.csGRAY) # - DeviceRGB (fitz.csRGB) # # Even if cs.n == 1 or cs.n == 3, the pixmap may still be: # - ICCBased grayscale/RGB # - Separation / spot color # - Indexed colorspace # - Alpha-only pixmap # # All of these will trigger: # "pixmap must be grayscale or rgb to write as png" # # Therefore we must convert unless the colorspace object itself # is exactly csGRAY or csRGB. if cs is None or cs not in (fitz.csGRAY, fitz.csRGB): image = fitz.Pixmap(fitz.csRGB, image) return { "type": BlockType.IMAGE.value, "bbox": tuple(bbox), "width": image.width, "height": image.height, "image": image.tobytes(), } @staticmethod def _rotate_image(pixmap: fitz.Pixmap, rotation: int): """Rotate image represented by image bytes. Args: pixmap (fitz.Pixmap): Image to rotate. rotation (int): Rotation angle. Return: image bytes. """ import cv2 as cv import numpy as np # convert to opencv image img = ImagesExtractor._pixmap_to_cv_image(pixmap) h, w = img.shape[:2] # get image height, width # calculate the center of the image x0, y0 = w // 2, h // 2 # default scale value for now -> might be extracted from PDF page property scale = 1.0 # rotation matrix matrix = cv.getRotationMatrix2D((x0, y0), rotation, scale) # calculate the final dimension cos = np.abs(matrix[0, 0]) sin = np.abs(matrix[0, 1]) # compute the new bounding dimensions of the image W = int((h * sin) + (w * cos)) H = int((h * cos) + (w * sin)) # adjust the rotation matrix to take into account translation matrix[0, 2] += (W / 2) - x0 matrix[1, 2] += (H / 2) - y0 # perform the rotation holding at the center rotated_img = cv.warpAffine(img, matrix, (W, H)) # convert back to bytes _, im_png = cv.imencode(".png", rotated_img) return im_png.tobytes() @staticmethod def _hide_page_text_and_images(page: fitz.Page, rm_text: bool, rm_image: bool): """Hide page text and images.""" # NOTE: text might exist in both content stream and form object stream # - content stream, i.e. direct page content # - form object, i.e. contents referenced by this page xref_list = [xref for (xref, name, invoker, bbox) in page.get_xobjects()] xref_list.extend(page.get_contents()) # (1) hide text # render Tr: set the text rendering mode # - 3: neither fill nor stroke the text -> invisible # read more: # - https://github.com/pymupdf/PyMuPDF/issues/257 # - https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf def hide_text(stream): res = stream found = False # set 3 Tr to text block for k in ["BT", "Tm", "Td", "2 Tr"]: bk = k.encode() if bk in stream: found = True res = res.replace(bk, f"{k} 3 Tr".encode()) return res, found # (2) hide image # https://github.com/pymupdf/PyMuPDF/issues/338 def hide_images(stream): res = stream found = False # image names, e.g. [[270, 0, 261, 115, 8, 'DeviceRGB', '', 'Im1', 'DCTDecode']] img_names = [item[7] for item in page.get_images(full=True)] for k in img_names: bk = f"/{k} Do".encode() if bk in stream: found = True res = res.replace(bk, b"") return res, found doc = page.parent # type: fitz.Document source = {} for xref in xref_list: src = doc.xref_stream(xref) # try to hide text stream, found_text = hide_text(src) if rm_text else (src, False) # try to hide images stream, found_images = hide_images(stream) if rm_image else (stream, False) if found_text or found_images: doc.update_stream(xref, stream) source[xref] = src # save original stream return source @staticmethod def _recover_pixmap(doc: fitz.Document, item: list): """Restore pixmap with soft mask considered. References: * https://pymupdf.readthedocs.io/en/latest/document.html#Document.getPageImageList * https://pymupdf.readthedocs.io/en/latest/faq.html#how-to-handle-stencil-masks * https://github.com/pymupdf/PyMuPDF/issues/670 Args: doc (fitz.Document): pdf document. item (list): image instance of ``page.get_images()``. Returns: fitz.Pixmap: Recovered pixmap with soft mask considered. """ # data structure of `item`: # (xref, smask, width, height, bpc, colorspace, ...) x = item[0] # xref of PDF image s = item[1] # xref of its /SMask # base image pix = fitz.Pixmap(doc, x) # reconstruct the alpha channel with the smask if exists if s > 0: mask = fitz.Pixmap(doc, s) if pix.alpha: temp = fitz.Pixmap(pix, 0) # make temp pixmap w/o the alpha pix = None # release storage pix = temp # check dimension if pix.width == mask.width and pix.height == mask.height: pix = fitz.Pixmap(pix, mask) # now compose final pixmap else: logging.warning( "Ignore image due to inconsistent size of color and mask pixmaps: %s", item, ) # we may need to adjust something for CMYK pixmaps here -> # recreate pixmap in RGB color space if necessary # NOTE: pix.colorspace may be None for images with alpha channel values only if "CMYK" in item[5].upper(): pix = fitz.Pixmap(fitz.csRGB, pix) return pix @staticmethod def _pixmap_to_cv_image(pixmap: fitz.Pixmap): """Convert fitz Pixmap to opencv image. Args: pixmap (fitz.Pixmap): PyMuPDF Pixmap. """ import cv2 as cv import numpy as np img_byte = pixmap.tobytes() return cv.imdecode(np.frombuffer(img_byte, np.uint8), cv.IMREAD_COLOR) pdf2docx-0.5.13/pdf2docx/image/__init__.py000066400000000000000000000000001517510251600202060ustar00rootroot00000000000000pdf2docx-0.5.13/pdf2docx/layout/000077500000000000000000000000001517510251600163425ustar00rootroot00000000000000pdf2docx-0.5.13/pdf2docx/layout/Blocks.py000066400000000000000000000632521517510251600201410ustar00rootroot00000000000000''' A group of text elements, distinguished to ``Shape`` elements. For instance, ``TextBlock``, ``ImageBlock`` or ``TableBlock`` after parsing, while ``Line`` instances at the beginning, and a combination of ``Line`` and ``TableBlock`` during parsing process. ''' import logging from docx.shared import Pt from ..common import constants from ..common.Collection import ElementCollection from ..common.share import (BlockType, lower_round, rgb_value, is_list_item) from ..common.Block import Block from ..common.docx import (reset_paragraph_format, delete_paragraph) from ..text.TextBlock import TextBlock from ..text.TextSpan import TextSpan from ..text.Line import Line from ..text.Lines import Lines from ..table.Cell import Cell from ..image.ImageBlock import ImageBlock from ..table.TableBlock import TableBlock class Blocks(ElementCollection): '''Block collections.''' def __init__(self, instances:list=None, parent=None): ''' A collection of text based elements, e.g. lines, images or blocks.''' super().__init__(instances, parent) self._floating_image_blocks = [] def _update_bbox(self, block:Block): '''Override. The parent is ``Layout``, which is not necessary to update its bbox. So, do nothing but required here. ''' pass @property def floating_image_blocks(self): return self._floating_image_blocks @property def lattice_table_blocks(self): '''Get lattice table blocks contained in this Collection.''' return list(filter( lambda block: isinstance(block, Block) and block.is_lattice_table_block, self._instances)) @property def stream_table_blocks(self): '''Get stream table blocks contained in this Collection.''' return list(filter( lambda block: isinstance(block, Block) and block.is_stream_table_block, self._instances)) @property def table_blocks(self): '''Get table blocks contained in this Collection.''' return list(filter( lambda block: isinstance(block, Block) and block.is_table_block, self._instances)) @property def inline_image_blocks(self): '''Get inline image blocks contained in this Collection.''' return list(filter( lambda block: isinstance(block, Block) and block.is_inline_image_block, self._instances)) @property def text_blocks(self): '''Get text/image blocks contained in this Collection.''' return list(filter( lambda block: isinstance(block, Block) and block.is_text_image_block, self._instances)) def restore(self, raws:list): '''Clean current instances and restore them from source dict. ImageBlock is converted to ImageSpan contained in TextBlock. Args: raws (list): A list of raw dicts representing text/image/table blocks. Returns: Blocks: self ''' self.reset() # clean current instances for raw_block in raws: block_type = raw_block.get('type', -1) # type: int # inline image block -> text block if block_type == BlockType.IMAGE.value: block = ImageBlock(raw_block).to_text_block() # text block elif block_type == BlockType.TEXT.value: block = TextBlock(raw_block) # table block elif block_type in (BlockType.LATTICE_TABLE.value, BlockType.STREAM_TABLE.value): block = TableBlock(raw_block) else: block = None # add to list self.append(block) return self def clean_up(self, float_image_ignorable_gap:float, line_overlap_threshold:float): '''Clean up blocks in page level. * convert to lines * remove lines out of page * remove transformed text: text direction is not (1, 0) or (0, -1) * remove empty lines Args: float_image_ignorable_gap (float): Regarded as float image if the intersection exceeds this value. line_overlap_threshold (float): remove line if the intersection exceeds this value. .. note:: The block structure extracted from ``PyMuPDF`` might be unreasonable, e.g. * one real paragraph is split into multiple blocks; or * one block consists of multiple real paragraphs ''' if not self._instances: return # convert to lines instances = [] for block in self._instances: if not isinstance(block, (ImageBlock, TextBlock)): continue instances.extend(block.lines) # delete invalid lines page_bbox = self.parent.working_bbox f = lambda line: line.bbox.intersects(page_bbox) and \ not line.white_space_only and ( line.is_horizontal_text or line.is_vertical_text) instances = list(filter(f, instances)) # delete redundant blanks for line in instances: line.strip() # detect floating images self.reset(instances) \ ._identify_floating_images(float_image_ignorable_gap) \ ._remove_overlapped_lines(line_overlap_threshold) def assign_to_tables(self, tables:list): '''Add blocks (line or sub-table) to associated cells of given tables. Args: tables (list): A list of TableBlock instances. ''' if not tables: return # assign blocks to table region blocks_in_tables = [[] for _ in tables] # type: list[list[Line|TableBlock]] blocks = [] # type: list[Line|TableBlock] for block in self._instances: self._assign_block_to_tables(block, tables, blocks_in_tables, blocks) # assign blocks to associated cells for table, blocks_in_table in zip(tables, blocks_in_tables): # no contents for this table if not blocks_in_table: continue table.assign_blocks(blocks_in_table) # sort in natural reading order and update layout blocks blocks.extend(tables) self.reset(blocks) def collect_stream_lines(self, potential_shadings:list, line_separate_threshold:float, **kwargs): '''Collect elements in Line level (line or table bbox), which may contained in a stream table region. Table may exist on the following conditions: * blocks in a row don't follow flow layout; or * block is contained in potential shading Args: potential_shadings (list): a group of shapes representing potential cell shading line_separate_threshold (float): two separate lines if the x-distance exceeds this value Returns: list: A list of Lines. Each group of Lines represents a potential table. .. note:: ``PyMuPDF`` may group multi-lines in a row as a text block while each line belongs to different cell. So, it's required to deep into line level. ''' # group lines by row: any intersection would be counted as same group # NOTE: consider real text direction when all lines have same orientation, i.e. either horizontal or # vertical; otherwise, consider the default direction, i.e. horizontal. rows = self.group_by_rows(text_direction=not self.is_mix_text) # get sub-lines from block: line or table block def sub_line(block): return block if isinstance(block, Line) else Line().update_bbox(block.outer_bbox) # exclude potential shading in white bg-color shadings_exclude_white = list(filter( lambda shape: not shape.is_determined and shape.color!=rgb_value([1,1,1]), potential_shadings)) def contained_in_shadings(block): for shading in shadings_exclude_white: if shading.contains(block, threshold=constants.FACTOR_MOST): return True return False # store text lines in a potential table res = [] # type: list[Lines] table_lines = [] # type: list[Line] def close_table(): if not table_lines: return res.append(Lines(table_lines)) table_lines.clear() # check row by row ref_pos = rows[0].bbox.y1 cell_layout = isinstance(self.parent, Cell) for row in rows: bbox = row.bbox # flow layout or not? if row.is_flow_layout(line_separate_threshold, cell_layout=cell_layout): close_table() elif kwargs.get('list_not_table') and is_list_item(row[0].text): # Don't interpret list-style bullet characters/numbers as # indicating a table. close_table() else: table_lines.extend([sub_line(block) for block in row]) # contained in shading or not? for block in row: if contained_in_shadings(block): table_lines.append(sub_line(block)) # close table if significant vertical distance if bbox.y0-ref_pos>=50: close_table() # update reference pos ref_pos = bbox.y1 # don't forget last table close_table() return res def parse_block(self, max_line_spacing_ratio:float, line_break_free_space_ratio:float, new_paragraph_free_space_ratio:float): '''Group lines into text block.''' # sort in normal reading order self.sort_in_reading_order_plus() # join lines with similar properties, e.g. spacing, together into text block blocks = self._join_lines_vertically(max_line_spacing_ratio) # split text block by checking text blocks = self._split_text_block_vertically(blocks, line_break_free_space_ratio, new_paragraph_free_space_ratio) self.reset(blocks) def parse_text_format(self, rects, delete_end_line_hyphen:bool): '''Parse text format with style represented by stroke/fill shapes. Args: rects (Shapes): Potential styles applied on blocks. delete_end_line_hyphen (bool): delete hyphen at the end of a line if True. ''' # parse text block style one by one for block in filter(lambda e: e.is_text_block, self._instances): block.parse_text_format(rects) # adjust word at the end of each line block.lines.adjust_last_word(delete_end_line_hyphen) def parse_spacing(self, *args): '''Calculate external and internal space for text blocks: - vertical distance between blocks, i.e. paragraph before/after spacing - horizontal distance to left/right border, i.e. paragraph left/right indent - vertical distance between lines, i.e. paragraph line spacing ''' if not self._instances: return self._parse_block_horizontal_spacing(*args) self._parse_block_vertical_spacing() self._parse_line_spacing() def make_docx(self, doc): '''Create page based on parsed block structure. Args: doc (Document, _Cell): The container to make docx content. ''' def make_table(table_block, pre_table): # create dummy paragraph if table before space is set # - a minimum line height of paragraph is 0.7pt, so ignore before space if less than this value # - but tow adjacent tables will be combined automatically, so adding a minimum dummy paragraph is required if table_block.before_space>=constants.MIN_LINE_SPACING or pre_table: h = lower_round(table_block.before_space, 1) # round(x,1), but to lower bound p = doc.add_paragraph() reset_paragraph_format(p, line_spacing=Pt(h)) # new table table = doc.add_table(rows=table_block.num_rows, cols=table_block.num_cols) table.autofit = False table.allow_autofit = False table_block.make_docx(table) pre_table = False cell_layout = isinstance(self.parent, Cell) for block in self._instances: # make paragraphs if block.is_text_image_block: # new paragraph p = doc.add_paragraph() block.make_docx(p) pre_table = False # mark block type # make table elif block.is_table_block: make_table(block, pre_table) pre_table = True # mark block type # NOTE: within a cell, there is always an empty paragraph after table, # so, delete it right here. # https://github.com/dothinking/pdf2docx/issues/76 if cell_layout: delete_paragraph(doc.paragraphs[-1]) # NOTE: If a table is at the end of a page, a new paragraph will be automatically # added by the rending engine, e.g. MS Word, which resulting in an unexpected # page break. The solution is to never put a table at the end of a page, so add # an empty paragraph and reset its format, particularly line spacing, when a table # is created. for block in self._instances[::-1]: # ignore float image block if block.is_float_image_block: continue # nothing to do if not end with table block if not block.is_table_block: break # otherwise, add a small paragraph p = doc.add_paragraph() reset_paragraph_format(p, Pt(constants.MIN_LINE_SPACING)) # a small line height def plot(self, page): '''Plot blocks in PDF page for debug purpose.''' for block in self._instances: block.plot(page) # ---------------------------------------------------------------------------------- # internal methods # ---------------------------------------------------------------------------------- def _identify_floating_images(self, float_image_ignorable_gap:float): '''Identify floating image lines and convert to ImageBlock.''' # group lines by connectivity groups = self.group_by_connectivity(dx=-float_image_ignorable_gap, dy=-float_image_ignorable_gap) # identify floating images for group in filter(lambda group: len(group)>1, groups): for line in filter(lambda line: line.image_spans, group): float_image = ImageBlock().from_image(line.spans[0]) float_image.set_float_image_block() self._floating_image_blocks.append(float_image) # remove the original image line from flow layout by setting empty bbox line.update_bbox((0,0,0,0)) return self def _remove_overlapped_lines(self, line_overlap_threshold:float): '''Delete overlapped lines. NOTE: Don't run this method until floating images are excluded. ''' # group lines by overlap fun = lambda a, b: a.get_main_bbox(b, threshold=line_overlap_threshold) groups = self.group(fun) # delete overlapped lines for group in filter(lambda group: len(group)>1, groups): # keep only the line with largest area sorted_lines = sorted(group, key=lambda line: line.bbox.get_area()) for line in sorted_lines[:-1]: logging.warning('Ignore Line "%s" due to overlap', line.text) line.update_bbox((0,0,0,0)) return self @staticmethod def _assign_block_to_tables(block, tables:list, blocks_in_tables:list, blocks:list): '''Assign block (line or table block) to contained table region ``blocks_in_tables``, or out-of-table ``blocks``.''' for table, blocks_in_table in zip(tables, blocks_in_tables): # fully contained in a certain table with margin if table.contains(block, threshold=constants.FACTOR_MAJOR): blocks_in_table.append(block) break # not possible in current table, then check next table elif not table.bbox.intersects(block.bbox): continue # Now, this block is out of all table regions else: blocks.append(block) def _join_lines_vertically(self, max_line_spacing_ratio:float): '''Create text blocks by merge lines with same properties (spacing, font, size) in vertical direction. At this moment, the block instance is either Line or TableBlock. ''' idx0, idx1 = (1, 3) if self.is_horizontal_text else (0, 2) def get_v_bdy(block): '''Coordinates of block top and bottom boundaries.''' bbox = block.bbox return bbox[idx0], bbox[idx1] def vertical_distance(block1, block2): u0, u1 = get_v_bdy(block1) v0, v1 = get_v_bdy(block2) return round(v0-u1, 2) def line_height(line:Line): '''The height of line span with most characters.''' span = max(line.spans, key=lambda s: len(s.text)) h = span.bbox.height if line.is_horizontal_text else span.bbox.width return round(h, 2) def common_vertical_spacing(): '''Vertical distance with most frequency: a reference of line spacing.''' ref0, ref1 = get_v_bdy(self._instances[0]) distances = [] for block in self._instances[1:]: y0, y1 = get_v_bdy(block) distances.append(round(y0-ref1, 2)) ref0, ref1 = y0, y1 return max(distances, key=distances.count) if distances else 0.0 # create text block based on lines blocks = [] # type: list[TextBlock] lines = [] # type: list[Line] def close_text_block(): if not lines: return block = TextBlock() block.add(lines) blocks.append(block) lines.clear() # check line by line ref_dis = common_vertical_spacing() for block in self._instances: # if current is a table block: # - finish previous text block; and # - add this table block directly if isinstance(block, TableBlock): close_text_block() blocks.append(block) # check two adjacent text lines else: ref_line = lines[-1] if lines else None # first line or in same row with previous line: needn't to create new text block if not ref_line or ref_line.in_same_row(block): start_new_block = False # image line: create new text block elif block.image_spans or ref_line.image_spans: start_new_block = True # lower than common line spacing: needn't to create new text block elif vertical_distance(ref_line, block)<=ref_dis+1.0 and \ ref_dis<=max_line_spacing_ratio*line_height(ref_line): start_new_block = False else: start_new_block = True if start_new_block: close_text_block() lines.append(block) # don't forget last group close_text_block() return blocks @staticmethod def _split_text_block_vertically(instances:list, line_break_free_space_ratio:float, new_paragraph_free_space_ratio:float): '''Split text block into separate paragraph based on punctuation of sentence. .. note:: Considered only normal reading direction, from left to right, from top to bottom. ''' blocks = [] # type: list[TextBlock] for block in instances: # add block if this isn't a text block if not block.is_text_block: blocks.append(block) continue # add split blocks if necessary lines_list = block.lines.split_vertically_by_text(line_break_free_space_ratio, new_paragraph_free_space_ratio) if len(lines_list)==1: blocks.append(block) else: for lines in lines_list: text_block = TextBlock() text_block.add(lines) blocks.append(text_block) return blocks def _parse_block_horizontal_spacing(self, *args): '''Calculate external horizontal space for text blocks, i.e. alignment mode and left spacing for paragraph in docx: - horizontal block -> take left boundary as reference - vertical block -> take bottom boundary as reference ''' # bbox of blocks # - page level, e.g. blocks in top layout # - table level, e.g. blocks in table cell bbox = self.parent.working_bbox for block in self._instances: block.parse_horizontal_spacing(bbox, *args) def _parse_block_vertical_spacing(self): '''Calculate external vertical space for text blocks, i.e. before/after space in docx. The vertical spacing is determined by the vertical distance to previous block. For the first block, the reference position is top margin. It's easy to set before-space or after-space for a paragraph with ``python-docx``, so, if current block is a paragraph, set before-space for it; if current block is not a paragraph, e.g. a table, set after-space for previous block (generally, previous block should be a paragraph). ''' # bbox of blocks # - page level, e.g. blocks in top layout # - table level, e.g. blocks in table cell bbox = self.parent.working_bbox # check text direction for vertical space calculation: # - normal reading direction (from left to right) -> the reference boundary is top border, i.e. bbox[1]. # - vertical text direction, e.g. from bottom to top -> left border bbox[0] is the reference idx = 1 if self.is_horizontal_text else 0 ref_block = self._instances[0] ref_pos = bbox[idx] for block in self._instances: # NOTE: the table bbox is counted on center-line of outer borders, so a half of top border # size should be excluded from the calculated vertical spacing if block.is_table_block: dw = block[0][0].border_width[0] / 2.0 # use top border of the first cell else: dw = 0.0 start_pos = block.bbox[idx] - dw para_space = start_pos-ref_pos # modify vertical space in case the block is out of bottom boundary dy = max(block.bbox[idx+2]-bbox[idx+2], 0.0) para_space -= dy para_space = max(para_space, 0.0) # ignore negative value # ref to current (paragraph): set before-space for paragraph if block.is_text_block: # spacing before this paragraph block.before_space = para_space # if ref to current (image): set before-space for paragraph elif block.is_inline_image_block: block.before_space = para_space # ref (paragraph/image) to current: set after-space for ref paragraph elif ref_block.is_text_block or ref_block.is_inline_image_block: ref_block.after_space = para_space # situation with very low probability, e.g. ref (table) to current (table) # we can't set before space for table in docx, but the tricky way is to # create an empty paragraph and set paragraph line spacing and before space else: # let para_space>=1 Pt to accommodate the dummy paragraph if not the first block block.before_space = max(para_space, int(block!=self._instances[0])*constants.MINOR_DIST) # update reference block ref_block = block ref_pos = ref_block.bbox[idx+2] + dw # assume same bottom border with top one # NOTE: when a table is at the end of a page, a dummy paragraph with a small line spacing # is added after this table, to avoid unexpected page break. Accordingly, this extra spacing # must be remove in other place, especially the page space is run out. # Here, reduce the last row of table. block = self._instances[-1] if block.is_table_block: block[-1].height -= constants.MINOR_DIST def _parse_line_spacing(self): '''Calculate internal vertical space for text blocks, i.e. paragraph line spacing in docx. .. note:: Run parsing block vertical spacing in advance. ''' def is_exact_line_spacing(block): '''check line spacing type based on parsed font metrics of text span: exact line spacing if no standard line height is extracted ''' for line in block.lines: absent_line_heights = list(not span.is_valid_line_height \ for span in line.spans if isinstance(span, TextSpan)) if any(absent_line_heights): return True return False for block in self._instances: if not block.is_text_block: continue if is_exact_line_spacing(block): block.line_space_type = 0 block.parse_exact_line_spacing() else: block.parse_relative_line_spacing() pdf2docx-0.5.13/pdf2docx/layout/Column.py000066400000000000000000000022311517510251600201470ustar00rootroot00000000000000'''Column of Section. In most cases, one section per page. But in case multi-columns page, sections are used to distinguish these different layouts. .. note:: Currently, support at most two columns. :: { 'bbox': (x0, y0, x1, y1), 'blocks': [{ ... # block instances }, ...], 'shapes': [{ ... # shape instances }, ...] } ''' from ..common.Collection import Collection from ..layout.Layout import Layout from ..shape.Shape import Shape from ..text.Line import Line class Column(Layout): '''Column of Section.''' @property def working_bbox(self): return self.bbox def add_elements(self, elements:Collection): '''Add candidate elements, i.e. lines or shapes, to current column.''' blocks = [e for e in elements if isinstance(e, Line)] shapes = [e for e in elements if isinstance(e, Shape)] self.assign_blocks(blocks) self.assign_shapes(shapes) def make_docx(self, doc): '''Create Section Column in docx. Args: doc (Document): ``python-docx`` document object ''' self.blocks.make_docx(doc) pdf2docx-0.5.13/pdf2docx/layout/Layout.py000066400000000000000000000145131517510251600201750ustar00rootroot00000000000000'''Document layout depends on Blocks and Shapes. **Layout** here refers to the content and position of text, image and table. The target is to convert source blocks and shapes to a *flow layout* that can be re-created as docx elements like paragraph and table. In addition to ``Section`` and ``Column``, ``TableBlock`` is used to maintain the page layout . So, detecting and parsing table block is the principle steps. The prerequisite work is done before this step: 1. Clean up source blocks and shapes in Page level, e.g. convert source blocks to ``Line`` level, because the block structure determined by ``PyMuPDF`` might be not reasonable. #. Parse structure in document level, e.g. page header/footer. #. Parse Section and Column layout in Page level. The page layout parsing idea: 1. Parse table layout in Column level. (a) Detect explicit tables first based on shapes. (#) Then, detect stream tables based on original text blocks and parsed explicit tables. (#) Move table contained blocks (lines or explicit table) to associated cell-layout. #. Parse paragraph in Column level. (a) Detect text blocks by combining related lines. (#) Parse paragraph style, e.g. text format, alignment #. Calculate vertical spacing based on parsed tables and paragraphs. #. Repeat above steps for cell-layout in parsed table level. ''' from abc import (ABC, abstractmethod) from ..text.Line import Line from ..common import constants from ..common.Element import Element from ..shape.Shapes import Shapes class Layout(Element, ABC): '''Blocks and shapes structure and formats.''' def __init__(self, bbox=None): ''' Initialize layout. Note that layout bbox must be set explicitly, rather than calculated automatically from contained blocks and shapes.''' from .Blocks import Blocks # avoid import conflicts from ..table.TablesConstructor import TablesConstructor raw = {'bbox': bbox} if bbox else {} super().__init__(raw) # Blocks representing text/table contents. self.blocks = Blocks(parent=self) # Shapes representing table border, shading and text style like underline, highlight. self.shapes = Shapes(parent=self) # table builder self._table_parser = TablesConstructor(parent=self) # table parser @property @abstractmethod def working_bbox(self): '''Working bbox of current Layout.''' def store(self): '''Store parsed layout in dict format.''' res = super().store() # Element res.update({ 'blocks': self.blocks.store(), 'shapes': self.shapes.store() }) return res def restore(self, data:dict): '''Restore Layout from parsed results.''' self.update_bbox(data.get('bbox', (0,)*4)) self.blocks.restore(data.get('blocks', [])) self.shapes.restore(data.get('shapes', [])) return self def assign_blocks(self, blocks:list): '''Add blocks (line or table block) to this layout. Args: blocks (list): a list of text line or table block to add. .. note:: If a text line is partly contained, it must deep into span -> char. ''' for block in blocks: self._assign_block(block) def assign_shapes(self, shapes:list): '''Add shapes to this cell. Args: shapes (list): a list of Shape instance to add. ''' # add shape if contained in cell for shape in shapes: if self.working_bbox.intersects(shape.bbox): self.shapes.append(shape) def parse(self, **settings): '''Parse layout. Args: settings (dict): Layout parsing parameters. ''' if not self.blocks: return # parse tables self._parse_table(**settings) # parse paragraphs self._parse_paragraph(**settings) # parse sub-layout, i.e. cell layouts under table block for block in filter(lambda e: e.is_table_block, self.blocks): block.parse(**settings) def _assign_block(self, block): '''Add block (line or table block) to this layout.''' # add block directly if fully contained in cell if self.contains(block, threshold=constants.FACTOR_MAJOR): self.blocks.append(block) # deep into line span if any intersection elif isinstance(block, Line) and self.bbox.intersects(block.bbox): self.blocks.append(block.intersects(self.bbox)) def _parse_table(self, **settings): '''Parse table layout: * detect explicit tables first based on shapes, * then stream tables based on original text blocks and parsed explicit tables; * move table contained blocks (text block or explicit table) to associated cell layout. ''' # parse table structure/format recognized from explicit shapes if settings['parse_lattice_table']: self._table_parser.lattice_tables( settings['connected_border_tolerance'], settings['min_border_clearance'], settings['max_border_width']) # parse table structure based on implicit layout of text blocks if settings['parse_stream_table']: self._table_parser.stream_tables( settings['min_border_clearance'], settings['max_border_width'], settings['line_separate_threshold']) def _parse_paragraph(self, **settings): '''Create text block based on lines, and parse text format, e.g. text highlight, paragraph indentation ''' # group lines to text block self.blocks.parse_block( settings['max_line_spacing_ratio'], settings['line_break_free_space_ratio'], settings['new_paragraph_free_space_ratio']) # parse text format, e.g. highlight, underline self.blocks.parse_text_format( self.shapes.text_style_shapes, settings['delete_end_line_hyphen']) # paragraph / line spacing self.blocks.parse_spacing( settings['line_separate_threshold'], settings['line_break_width_ratio'], settings['line_break_free_space_ratio'], settings['lines_left_aligned_threshold'], settings['lines_right_aligned_threshold'], settings['lines_center_aligned_threshold']) pdf2docx-0.5.13/pdf2docx/layout/Section.py000066400000000000000000000051731517510251600203260ustar00rootroot00000000000000# -*- coding: utf-8 -*- '''Section of Page. In most cases, one section per page. But in case multi-columns page, sections are used to distinguish these different layouts. .. note:: Currently, support at most two columns. :: { 'bbox': (x0,y0,x1,y1) 'num_cols': 1, 'space': 0, 'columns': [{ ... # column properties }, ...] } ''' from docx.enum.section import WD_SECTION from ..common.docx import set_columns from ..common.Collection import BaseCollection from .Column import Column class Section(BaseCollection): def __init__(self, space:int=0, columns:list=None, parent=None): """Initialize Section instance. Args: space (int, optional): Space between adjacent columns. Defaults to 0. columns (list, optional): A list of Column instances. Defaults to None. parent (Sections, optional): Parent element. Defaults to None. """ self.space = space self.before_space = 0.0 super().__init__(columns, parent) @property def num_cols(self): return len(self) def store(self): '''Store parsed section layout in dict format.''' return { 'bbox' : tuple([x for x in self.bbox]), 'num_cols' : self.num_cols, 'space' : self.space, 'before_space' : self.before_space, 'columns': super().store() } def restore(self, raw:dict): '''Restore section from source dict.''' # bbox is maintained automatically based on columns self.space = raw.get('space', 0) # space between adjacent columns self.before_space = raw.get('before_space', 0) # space between adjacent columns # get each column for raw_col in raw.get('columns', []): column = Column().restore(raw_col) self.append(column) return self def parse(self, **settings): '''Parse section layout.''' for column in self: column.parse(**settings) return self def make_docx(self, doc): '''Create section in docx. Args: doc (Document): ``python-docx`` document object ''' # set section column section = doc.sections[-1] width_list = [c.bbox[2]-c.bbox[0] for c in self] set_columns(section, width_list, self.space) # add create each column for column in self: # column break to start new column if column != self[0]: doc.add_section(WD_SECTION.NEW_COLUMN) # make doc column.make_docx(doc)pdf2docx-0.5.13/pdf2docx/layout/Sections.py000066400000000000000000000063051517510251600205070ustar00rootroot00000000000000# -*- coding: utf-8 -*- '''Collection of :py:class:`~pdf2docx.layout.Section` instances. ''' from docx.enum.section import WD_SECTION from docx.shared import Pt from ..common.Collection import BaseCollection from ..common.docx import reset_paragraph_format from .Section import Section from ..common import constants class Sections(BaseCollection): def restore(self, raws:list): """Restore sections from source dicts.""" self.reset() for raw in raws: section = Section().restore(raw) self.append(section) return self def parse(self, **settings): '''Parse layout under section level.''' for section in self: section.parse(**settings) return self def make_docx(self, doc): '''Create sections in docx.''' if not self: return # mark paragraph index before creating current page n = len(doc.paragraphs) def create_dummy_paragraph_for_section(section): p = doc.add_paragraph() line_height = min(section.before_space, 11) pf = reset_paragraph_format(p, line_spacing=Pt(line_height)) pf.space_after = Pt(section.before_space-line_height) # --------------------------------------------------- # first section # --------------------------------------------------- # vertical position: add dummy paragraph only if before space is required section = self[0] if section.before_space > constants.MINOR_DIST: create_dummy_paragraph_for_section(section) # create first section if section.num_cols==2: doc.add_section(WD_SECTION.CONTINUOUS) section.make_docx(doc) # --------------------------------------------------- # more sections # --------------------------------------------------- for section in self[1:]: # create new section symbol doc.add_section(WD_SECTION.CONTINUOUS) # set after space of last paragraph to define the vertical # position of current section # NOTE: the after space doesn't work if last paragraph is # image only (without any text). In this case, set after # space for the section break. p = doc.paragraphs[-2] # -1 is the section break if not p.text.strip() and 'graphicData' in p._p.xml: p = doc.paragraphs[-1] pf = p.paragraph_format pf.space_after = Pt(section.before_space) # section content section.make_docx(doc) # --------------------------------------------------- # create floating images # --------------------------------------------------- # lazy: assign all float images to first paragraph of current page for image in self.parent.float_images: image.make_docx(doc.paragraphs[n]) def plot(self, page): '''Plot all section blocks for debug purpose.''' for section in self: for column in section: column.plot(page, stroke=(1,1,0), width=1.5) # column bbox column.blocks.plot(page) # blockspdf2docx-0.5.13/pdf2docx/layout/__init__.py000066400000000000000000000000001517510251600204410ustar00rootroot00000000000000pdf2docx-0.5.13/pdf2docx/main.py000066400000000000000000000111441517510251600163240ustar00rootroot00000000000000'''Entry for ``pdf2docx`` command line.''' import logging from .converter import Converter class PDF2DOCX: '''Command line interface for ``pdf2docx``.''' @staticmethod def convert(pdf_file:str, docx_file:str=None, password:str=None, start:int=0, end:int=None, pages:list=None, **kwargs): '''Convert pdf file to docx file. Args: pdf_file (str) : PDF filename to read from. docx_file (str, optional): docx filename to write to. Defaults to None. password (str): Password for encrypted pdf. Default to None if not encrypted. start (int, optional): First page to process. Defaults to 0. end (int, optional): Last page to process. Defaults to None. pages (list, optional): Range of pages, e.g. --pages=1,3,5. Defaults to None. kwargs (dict) : Configuration parameters. .. note:: Refer to :py:meth:`~pdf2docx.converter.Converter.convert` for detailed description on above arguments. ''' # index starts from zero or one if isinstance(pages, int): pages = [pages] # in case --pages=1 if not kwargs.get('zero_based_index', True): start = max(start-1, 0) if end: end -= 1 if pages: pages = [i-1 for i in pages] cv = Converter(pdf_file, password) try: cv.convert(docx_file, start, end, pages, **kwargs) except Exception as e: logging.error(e) if kwargs['raw_exceptions']: raise finally: cv.close() @staticmethod def debug(pdf_file:str, password:str=None, page:int=0, docx_file:str=None, debug_pdf:str=None, layout_file:str='layout.json', **kwargs): '''Convert one PDF page and plot layout information for debugging. Args: pdf_file (str) : PDF filename to read from. password (str): Password for encrypted pdf. Default to None if not encrypted. page (int, optional): Page index to convert. docx_file (str, optional): docx filename to write to. debug_pdf (str, optional): Filename for new pdf storing layout information. Defaults to same name with pdf file. layout_file (str, optional): Filename for new json file storing parsed layout data. Defaults to ``layout.json``. kwargs (dict) : Configuration parameters. ''' # index starts from zero or one if not kwargs.get('zero_based_index', True): page = max(page-1, 0) # explode exception directly if debug mode cv = Converter(pdf_file, password) cv.debug_page(page, docx_file, debug_pdf, layout_file, **kwargs) cv.close() @staticmethod def table(pdf_file, password:str=None, start:int=0, end:int=None, pages:list=None, **kwargs): '''Extract table content from pdf pages. Args: pdf_file (str) : PDF filename to read from. password (str): Password for encrypted pdf. Default to None if not encrypted. start (int, optional): First page to process. Defaults to 0. end (int, optional): Last page to process. Defaults to None. pages (list, optional): Range of pages, e.g. --pages=1,3,5. Defaults to None. ''' # index starts from zero or one if isinstance(pages, int): pages = [pages] # in case --pages=1 if not kwargs.get('zero_based_index', True): start = max(start-1, 0) if end: end -= 1 if pages: pages = [i-1 for i in pages] cv = Converter(pdf_file, password) try: tables = cv.extract_tables(start, end, pages, **kwargs) except Exception as e: tables = [] logging.error(e) finally: cv.close() return tables @staticmethod def gui(): '''Simple user interface.''' # import App containing tkinter internally, in case GUI is not supported by some platforms, # e.g. Amazon Linux 2 try: from .gui.App import App except Exception: logging.error('GUI is not supported in current platform.') else: app = App(title='PDF_2_Docx Converter', width=500, height=600) app.mainloop() parse = PDF2DOCX.convert def main(): '''Command line entry.''' import fire fire.Fire(PDF2DOCX) if __name__ == '__main__': main() pdf2docx-0.5.13/pdf2docx/page/000077500000000000000000000000001517510251600157415ustar00rootroot00000000000000pdf2docx-0.5.13/pdf2docx/page/BasePage.py000066400000000000000000000015031517510251600177610ustar00rootroot00000000000000'''Base page with basic properties, e.g. width, height and margin.''' class BasePage: def __init__(self, width:float=0.0, height:float=0.0, margin:tuple=None): '''Initialize page layout. Args: width (float, optional): Page width. Defaults to 0.0. height (float, optional): Page height. Defaults to 0.0. margin (tuple, optional): Page margin. Defaults to None. ''' # page size and margin self.width = width self.height = height self.margin = margin or (0,) * 4 @property def bbox(self): return (0.0, 0.0, self.width, self.height) @property def working_bbox(self): '''bbox with margin considered.''' x0, y0, x1, y1 = self.bbox L, R, T, B = self.margin return (x0+L, y0+T, x1-R, y1-B)pdf2docx-0.5.13/pdf2docx/page/Page.py000066400000000000000000000154651517510251600172020ustar00rootroot00000000000000# -*- coding: utf-8 -*- '''Page object parsed with PDF raw dict. In addition to base structure described in :py:class:`~pdf2docx.page.RawPage`, some new features, e.g. sections, table block, are also included. Page elements structure: * :py:class:`~pdf2docx.page.Page` >> :py:class:`~pdf2docx.layout.Section` >> :py:class:`~pdf2docx.layout.Column` * :py:class:`~pdf2docx.layout.Blocks` * :py:class:`~pdf2docx.text.TextBlock` >> :py:class:`~pdf2docx.text.Line` >> :py:class:`~pdf2docx.text.TextSpan` / :py:class:`~pdf2docx.image.ImageSpan` >> :py:class:`~pdf2docx.text.Char` * :py:class:`~pdf2docx.table.TableBlock` >> :py:class:`~pdf2docx.table.Row` >> :py:class:`~pdf2docx.table.Cell` * :py:class:`~pdf2docx.layout.Blocks` * :py:class:`~pdf2docx.shape.Shapes` * :py:class:`~pdf2docx.shape.Shapes` * :py:class:`~pdf2docx.shape.Shape.Stroke` * :py:class:`~pdf2docx.shape.Shape.Fill` * :py:class:`~pdf2docx.shape.Shape.Hyperlink` :: { "id": 0, # page index "width" : w, "height": h, "margin": [left, right, top, bottom], "sections": [{ ... # section properties }, ...], "floats": [{ ... # floating picture }, ...] } ''' from docx.shared import Pt from docx.enum.section import WD_SECTION from ..common.Collection import BaseCollection from ..common.share import debug_plot from .BasePage import BasePage from ..layout.Sections import Sections from ..image.ImageBlock import ImageBlock class Page(BasePage): '''Object representing the whole page, e.g. margins, sections.''' def __init__(self, id:int=-1, skip_parsing:bool=True, width:float=0.0, height:float=0.0, header:str=None, footer:str=None, margin:tuple=None, sections:Sections=None, float_images:BaseCollection=None): '''Initialize page layout. Args: id (int, optional): Page index. Defaults to -1. skip_parsing (bool, optional): Don't parse page if True. Defaults to True. width (float, optional): Page width. Defaults to 0.0. height (float, optional): Page height. Defaults to 0.0. header (str, optional): Page header. Defaults to None. footer (str, optional): Page footer. Defaults to None. margin (tuple, optional): Page margin. Defaults to None. sections (Sections, optional): Page contents. Defaults to None. float_images (BaseCollection, optional): Float images in th is page. Defaults to None. ''' # page index self.id = id self.skip_parsing = skip_parsing # page size and margin super().__init__(width=width, height=height, margin=margin) # flow structure: # Section -> Column -> Blocks -> TextBlock/TableBlock # TableBlock -> Row -> Cell -> Blocks self.sections = sections or Sections(parent=self) # page header, footer self.header = header or '' self.footer = footer or '' # floating images are separate node under page self.float_images = float_images or BaseCollection() self._finalized = False @property def finalized(self): return self._finalized def store(self): '''Store parsed layout in dict format.''' res = { 'id' : self.id, 'width' : self.width, 'height' : self.height, 'margin' : self.margin, 'sections': self.sections.store(), 'header' : self.header, 'footer' : self.footer, 'floats' : self.float_images.store() } return res def restore(self, data:dict): '''Restore Layout from parsed results.''' # page id self.id = data.get('id', -1) # page width/height self.width = data.get('width', 0.0) self.height = data.get('height', 0.0) self.margin = data.get('margin', (0,) * 4) # parsed layout self.sections.restore(data.get('sections', [])) self.header = data.get('header', '') self.footer = data.get('footer', '') # float images self._restore_float_images(data.get('floats', [])) # Suppose layout is finalized when restored; otherwise, set False explicitly # out of this method. self._finalized = True return self @debug_plot('Final Layout') def parse(self, **settings): '''Parse page layout.''' self.sections.parse(**settings) self._finalized = True return self.sections # for debug plot def extract_tables(self, **settings): '''Extract content from tables (top layout only). .. note:: Before running this method, the page layout must be either parsed from source page or restored from parsed data. ''' # table blocks collections = [] for section in self.sections: for column in section: if settings['extract_stream_table']: collections.extend(column.blocks.table_blocks) else: collections.extend(column.blocks.lattice_table_blocks) # check table tables = [] # type: list[ list[list[str]] ] for table_block in collections: tables.append(table_block.text) return tables def make_docx(self, doc): '''Set page size, margin, and create page. .. note:: Before running this method, the page layout must be either parsed from source page or restored from parsed data. Args: doc (Document): ``python-docx`` document object ''' # new page if doc.paragraphs: section = doc.add_section(WD_SECTION.NEW_PAGE) else: section = doc.sections[0] # a default section is there when opening docx # page size section.page_width = Pt(self.width) section.page_height = Pt(self.height) # page margin left,right,top,bottom = self.margin section.left_margin = Pt(left) section.right_margin = Pt(right) section.top_margin = Pt(top) section.bottom_margin = Pt(bottom) # create flow layout: sections self.sections.make_docx(doc) def _restore_float_images(self, raws:list): '''Restore float images.''' self.float_images.reset() for raw in raws: image = ImageBlock(raw) image.set_float_image_block() self.float_images.append(image) pdf2docx-0.5.13/pdf2docx/page/Pages.py000066400000000000000000000064221517510251600173560ustar00rootroot00000000000000# -*- coding: utf-8 -*- '''Collection of :py:class:`~pdf2docx.page.Page` instances.''' import logging from .RawPageFactory import RawPageFactory from ..common.Collection import BaseCollection from ..font.Fonts import Fonts class Pages(BaseCollection): '''A collection of ``Page``.''' def parse(self, fitz_doc, **settings): '''Analyze document structure, e.g. page section, header, footer. Args: fitz_doc (fitz.Document): ``PyMuPDF`` Document instance. settings (dict): Parsing parameters. ''' # --------------------------------------------- # 0. extract fonts properties, especially line height ratio # --------------------------------------------- fonts = Fonts.extract(fitz_doc) # --------------------------------------------- # 1. extract and then clean up raw page # --------------------------------------------- pages, raw_pages = [], [] words_found = False for page in self: if page.skip_parsing: continue # init and extract data from PDF raw_page = RawPageFactory.create(page_engine=fitz_doc[page.id], backend='PyMuPDF') raw_page.restore(**settings) # check if any words are extracted since scanned pdf may be directed if not words_found and raw_page.raw_text.strip(): words_found = True # process blocks and shapes based on bbox raw_page.clean_up(**settings) # process font properties raw_page.process_font(fonts) # after this step, we can get some basic properties # NOTE: floating images are detected when cleaning up blocks, so collect them here page.width = raw_page.width page.height = raw_page.height page.float_images.reset().extend(raw_page.blocks.floating_image_blocks) raw_pages.append(raw_page) pages.append(page) # show message if no words found if not words_found: logging.warning('Words count: 0. It might be a scanned pdf, which is not supported yet.') # --------------------------------------------- # 2. parse structure in document/pages level # --------------------------------------------- # NOTE: blocks structure might be changed in this step, e.g. promote page header/footer, # so blocks structure based process, e.g. calculating margin, parse section should be # run after this step. header, footer = Pages._parse_document(raw_pages) # --------------------------------------------- # 3. parse structure in page level, e.g. page margin, section # --------------------------------------------- # parse sections for page, raw_page in zip(pages, raw_pages): # page margin margin = raw_page.calculate_margin(**settings) raw_page.margin = page.margin = margin # page section sections = raw_page.parse_section(**settings) page.sections.extend(sections) @staticmethod def _parse_document(raw_pages:list): '''Parse structure in document/pages level, e.g. header, footer''' # TODO return '', ''pdf2docx-0.5.13/pdf2docx/page/RawPage.py000066400000000000000000000233341517510251600176460ustar00rootroot00000000000000'''A wrapper of pdf page engine (e.g. PyMuPDF, pdfminer) to do the following work: * extract source contents * clean up blocks/shapes, e.g. elements out of page * calculate page margin * parse page structure roughly, i.e. section and column ''' from abc import (ABC, abstractmethod) from .BasePage import BasePage from ..layout.Section import Section from ..layout.Column import Column from ..shape.Shape import Hyperlink from ..shape.Shapes import Shapes from ..layout.Blocks import Blocks from ..font.Fonts import Fonts from ..text.TextSpan import TextSpan from ..common.share import debug_plot from ..common import constants from ..common.Collection import Collection class RawPage(BasePage, ABC): '''A wrapper of page engine.''' def __init__(self, page_engine=None): ''' Initialize page layout. Args: page_engine (Object): Source pdf page. ''' BasePage.__init__(self) self.page_engine = page_engine self.blocks = Blocks(parent=self) self.shapes = Shapes(parent=self) @abstractmethod def extract_raw_dict(self, **settings): '''Extract source data with page engine. Return a dict with the following structure: ``` { "width" : w, "height": h, "blocks": [{...}, {...}, ...], "shapes" : [{...}, {...}, ...] } ``` ''' @property def text(self): '''All extracted text in this page, with images considered as ````. Should be run after ``restore()`` data.''' return '\n'.join([block.text for block in self.blocks]) @property def raw_text(self): '''Extracted raw text in current page. Should be run after ``restore()`` data.''' return '\n'.join([block.raw_text for block in self.blocks]) @debug_plot('Source Text Blocks') def restore(self, **settings): '''Initialize layout extracted with ``PyMuPDF``.''' raw_dict = self.extract_raw_dict(**settings) self.blocks.restore(raw_dict.get('blocks', [])) self.shapes.restore(raw_dict.get('shapes', [])) return self.blocks @debug_plot('Cleaned Shapes') def clean_up(self, **settings): '''Clean up raw blocks and shapes, e.g. * remove negative or duplicated instances, * detect semantic type of shapes ''' # clean up blocks first self.blocks.clean_up( settings['float_image_ignorable_gap'], settings['line_overlap_threshold']) # clean up shapes self.shapes.clean_up( settings['max_border_width'], settings['shape_min_dimension']) return self.shapes def process_font(self, fonts:Fonts): '''Update font properties, e.g. font name, font line height ratio, of ``TextSpan``. Args: fonts (Fonts): Fonts parsed by ``fonttools``. ''' # get all text span spans = [] for line in self.blocks: spans.extend([span for span in line.spans if isinstance(span, TextSpan)]) # check and update font name, line height for span in spans: font = fonts.get(span.font) if not font: continue # update font properties with font parsed by fonttools span.font = font.name if font.line_height: span.line_height = font.line_height * span.size def calculate_margin(self, **settings): """Calculate page margin. .. note:: Ensure this method is run right after cleaning up the layout, so the page margin is calculated based on valid layout, and stay constant. """ # Exclude hyperlink from shapes because hyperlink might exist out of page unreasonably, # while it should always within page since attached to text. shapes = Shapes([shape for shape in self.shapes if not isinstance(shape, Hyperlink)]) # return default margin if no blocks exist if not self.blocks and not shapes: return (constants.ITP, ) * 4 x0, y0, x1, y1 = self.bbox u0, v0, u1, v1 = self.blocks.bbox | shapes.bbox # margin left = max(u0-x0, 0.0) right = max(x1-u1-constants.MINOR_DIST, 0.0) top = max(v0-y0, 0.0) bottom = max(y1-v1, 0.0) # reduce calculated top/bottom margin to leave some free space top *= settings['page_margin_factor_top'] bottom *= settings['page_margin_factor_bottom'] # use normal margin if calculated margin is large enough return ( min(constants.ITP, round(left, 1)), min(constants.ITP, round(right, 1)), min(constants.ITP, round(top, 1)), min(constants.ITP, round(bottom, 1))) def parse_section(self, **settings): '''Detect and create page sections. .. note:: - Only two-columns Sections are considered for now. - Page margin must be parsed before this step. ''' # bbox X0, Y0, X1, _ = self.working_bbox # collect all blocks (line level) and shapes elements = Collection() elements.extend(self.blocks) elements.extend(self.shapes) if not elements: return # to create section with collected lines lines = Collection() sections = [] def close_section(num_col, elements, y_ref): # append to last section if both single column if sections and sections[-1].num_cols==num_col==1: column = sections[-1][0] # type: Column column.union_bbox(elements) column.add_elements(elements) # otherwise, create new section else: section = self._create_section(num_col, elements, (X0, X1), y_ref) if section: sections.append(section) # check section row by row pre_num_col = 1 y_ref = Y0 # to calculate v-distance between sections for row in elements.group_by_rows(): # check column col by col cols = row.group_by_columns() current_num_col = len(cols) # column check: # consider 2-cols only if current_num_col>2: current_num_col = 1 # the width of two columns shouldn't have significant difference elif current_num_col==2: u0, v0, u1, v1 = cols[0].bbox m0, n0, m1, n1 = cols[1].bbox x0 = (u1+m0)/2.0 c1, c2 = x0-X0, X1-x0 # column width w1, w2 = u1-u0, m1-m0 # line width f = 2.0 if not 1/f<=c1/c2<=f or w1/c1<0.33 or w2/c2<0.33: current_num_col = 1 # process exceptions if pre_num_col==2 and current_num_col==1: # though current row has one single column, it might have another virtual # and empty column. If so, it should be counted as 2-cols cols = lines.group_by_columns() pos = cols[0].bbox[2] if row.bbox[2]<=pos or row.bbox[0]>pos: current_num_col = 2 # pre_num_col!=current_num_col => to close section with collected lines, # before that, further check the height of collected lines else: x0, y0, x1, y1 = lines.bbox if y1-y0