diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da906df..3816d65 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,6 +78,10 @@ jobs: fi "$sdkmanager" "platforms;android-36" "build-tools;36.0.0" - run: bundle exec fastlane android checks + - name: Check branding vector conversion + env: + MESSAGE487_TEST_ANDROID_VECTORS: "1" + run: python3 -m unittest discover -s scripts/tests -p test_generate_branding.py - name: Publish test and lint reports if: always() uses: actions/upload-artifact@v7 diff --git a/.gitignore b/.gitignore index 2012478..3ff7794 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ __pycache__/ dist/ .playwright-cli/ test-results/ +**/*.tmp diff --git a/README.md b/README.md index ec91830..4e93d72 100644 --- a/README.md +++ b/README.md @@ -231,7 +231,9 @@ unsigned release APK links are kept in the PR description. See [testing](docs/en For signed APK releases, see [Releases](docs/en/releases.md) and [release automation](docs/en/release-automation.md), and [release testing](docs/en/release-testing.md). English and Russian store descriptions for F-Droid and Google Play share the canonical [Fastlane metadata](fastlane/metadata/android). Store graphics and their provenance are documented -in [Branding](assets/branding/README.md). +in [Branding](assets/branding/README.md). Edit `assets/branding/branding.ai` and run +`./scripts/generate-branding.py` on macOS with Illustrator to regenerate SVG, PNG and Android +icon resources. Ordinary builds use the committed resources and do not require Illustrator. See the [project context](docs/en/project-context.md) for remaining product decisions. This project succeeds [sms487](https://github.com/andre487/sms487). diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index ae3397f..b68d68e 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -1,12 +1,5 @@ - - - - - - - - - - - + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index 2beb164..7456b2c 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -1,8 +1,8 @@ - - - - - - + + + + + + diff --git a/app/src/main/res/drawable/ic_notification.xml b/app/src/main/res/drawable/ic_notification.xml index a81c434..14f3db3 100644 --- a/app/src/main/res/drawable/ic_notification.xml +++ b/app/src/main/res/drawable/ic_notification.xml @@ -1,3 +1,3 @@ - + diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png index 0b0f1ab..6370e34 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png index 5b34bbd..024cb75 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png index 317738f..2c15bdf 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png index de36c53..54f3cea 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png index d95ec0f..3e1ed41 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png index 01f1f83..9394fad 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 15f9f7a..3ef3cf2 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index a884d97..79a5ff1 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index ddf9a89..c95ca7b 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png index 79492df..07c5a47 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/assets/branding/README.md b/assets/branding/README.md index 7943add..53beed4 100644 --- a/assets/branding/README.md +++ b/assets/branding/README.md @@ -2,53 +2,108 @@ | Asset | Use | | --- | --- | -| [icon.svg](icon.svg) · [PNG](icon.png) | Canonical square artwork for web and PNG exports | +| [branding.ai](branding.ai) | Editable Illustrator master for all four artboards | +| [icon.svg](icon.svg) · [PNG](icon.png) | Exported square artwork for web and launcher/store resources | | [icon-round.svg](icon-round.svg) · [PNG](icon-round.png) | Circular artwork used in the project README and round launcher PNGs | | [status-icon.svg](status-icon.svg) · [PNG](status-icon.png) | Simplified monochrome monitor/phone mark for small sizes | -| [feature.svg](feature.svg) · [PNG](feature.png) | Canonical store banner artwork, 1024×500 | +| [feature.svg](feature.svg) · [PNG](feature.png) | Exported store banner artwork, 1024×500 | | [icon.png](../../fastlane/metadata/android/en-US/images/icon.png) | Store app icon, 512×512 RGB PNG | | [featureGraphic.png](../../fastlane/metadata/android/en-US/images/featureGraphic.png) | Store feature graphic, 1024×500 opaque RGB PNG; shared by both locales | | `fastlane/metadata/android/{en-US,ru-RU}/images/phoneScreenshots/` | Actual phone screenshots in English and Russian, 1080×1920 | -The selected design shows an open envelope on a monitor and a closed envelope on a phone, -with a cyan/blue/violet/magenta gradient inspired by AndroidMegaProxy. The initial concepts -were created with the built-in `image_gen` tool, then reconstructed as native vectors. -The final design prompt was: “Monitor with a small open envelope and phone with a small closed -envelope on a cyan/blue/violet/magenta gradient; preserve generous margins in a circular crop.â€� +The current launcher artwork shows an open envelope on a monitor and a closed envelope +on a phone, in white on a dark purple background. The editable master is +[`branding.ai`](branding.ai), containing the square, round, status and banner artwork. Each SVG has a matching PNG beside it for sharing where SVG is unsupported. Sizes: square/round icon 512×512, status icon 24×24, feature graphic 1024×500. The round and status PNGs retain transparency; the white status mark needs a contrasting background. Android adaptive layers live in `app/src/main/res/drawable/ic_launcher_{background,foreground}.xml`. -The adaptive foreground follows the square SVG at scale `0.73` about the center of its -1254-unit viewport. The separate round SVG retains scale `0.65` for extra circular-mask padding. -Both SVGs share the Android paths and gradient, cropped to the central 836×836 viewport. -The SVG files remain separate editable sources; regenerate derived files after editing them. +The adaptive foreground and background are derived from the square SVG. Its artboard is +kept as the visible icon area; a 1.5× viewport provides Android adaptive-icon overscan. +The round artboard has its own layout and clipping and is exported to a separate SVG. +The SVG files remain separate exports; edit the Illustrator master and regenerate them. The themed launcher and notification small icon share `ic_notification.xml`, the simplified white monitor/phone silhouette without envelopes or gradient. Update notifications use this small icon. The app UI retains its purple Material theme and standard Material icons. -Generate all derived assets from the **separate** SVG sources: +Save your edits in **branding.ai**, then generate all SVG, PNG and Android XML assets +from the repository root: ```sh -python3 scripts/generate-branding.py +./scripts/generate-branding.py ``` -Requires `rsvg-convert` and ImageMagick's `magick`. Run from any directory. -Use `--output-root /tmp/message487-branding` to inspect exports without overwriting repository files. +To install the regenerated resources on the development emulator afterwards: -| Source | Generated assets | +```sh +./scripts/run-without-debugging.sh +``` + +Generation requires macOS, Adobe Illustrator, Python 3, `rsvg-convert`, ImageMagick's +`magick`, JDK 21 and the project's Gradle dependencies (downloaded by a normal Android +build). The Android SDK must be configured as for a normal build. Allow macOS Automation +access to Illustrator when prompted on the first run. The exporter reads the saved file, +not unsaved editor changes. It configures SVG export options automatically. + +To inspect all exports without overwriting repository assets: + +```sh +./scripts/generate-branding.py --output-root /tmp/message487-branding +``` + +The script resolves the master and build tools relative to the repository, so it can also +be invoked by its absolute path from another directory. + +The master must contain exactly one artboard with each of these names and dimensions: + +| Artboard | Size | | --- | --- | -| `icon.svg` | Square branding PNG, opaque RGB store icon, square launcher PNGs, adaptive foreground/background XML | -| `icon-round.svg` | Transparent round branding PNG, round launcher PNGs | -| `status-icon.svg` | Transparent 24×24 PNG and `ic_notification.xml` | -| `feature.svg` | Branding banner PNG and opaque RGB store feature graphic | +| `icon` | 512×512 | +| `icon-round` | 512×512 | +| `status-icon` | 24×24 | +| `feature` | 1024×500 | + +The exporter works on a temporary disk copy, exports each artboard independently, and +expands symbols only in temporary export documents. The master and its symbol links remain +unchanged. Keep intended artwork visible and unlocked so Illustrator can select it. +SVG export uses presentation attributes, outlined text and seven decimal places; random +Illustrator IDs are normalized for repeatable exports. Artboard positions and padding are +preserved without snapping. SVGs are refreshed alongside the generated PNG/XML assets. +Asset generation always uses Illustrator. CI and ordinary Android builds use the committed +generated resources and do not require Illustrator or run the asset generator. + +`run-without-debugging.sh` builds and installs the existing resources; it does not regenerate +them from the Illustrator master. Commit `branding.ai` and all generated SVG, PNG and XML +files together. Screenshots are captured separately and are not generated by this script. + +| Master artboard | Exported SVG | Other generated assets | +| --- | --- | --- | +| `icon` | `icon.svg` | Square branding PNG, opaque RGB store icon, square launcher PNGs, adaptive foreground/background XML | +| `icon-round` | `icon-round.svg` | Transparent round branding PNG, round launcher PNGs | +| `status-icon` | `status-icon.svg` | Transparent 24×24 PNG and `ic_notification.xml` | +| `feature` | `feature.svg` | Branding banner PNG and opaque RGB store feature graphic | Launcher PNG sizes: mdpi 48, hdpi 72, xhdpi 96, xxhdpi 144, xxxhdpi 192. -The script preserves each source's layout, including the square/round scale difference. -The Android vector exporter supports the existing path fill/stroke attributes, the square -artwork's `translate(x y) scale(s)` transform, and its diagonal background gradient. -Unsupported path features or gradient changes cause an error instead of silently losing details. +The script preserves each artboard's layout, including any square/round scale differences. +Each PNG is rasterized directly at its target resolution, without resizing another PNG. +Adaptive viewport offsets are applied as Android group translations to avoid rounding +path coordinates just to reposition the artwork. Explicit transforms inside the source +are still handled by the Android importer and may introduce coordinate rounding. +This preserves geometry; it does not snap curves or strokes to the pixel grid, and +anti-aliasing can differ between SVG renderers and Android launchers. +Android XML is converted by the SVG importer bundled with the Android Gradle Plugin, +through `convertBrandingVectors` (offline). Nested groups, arbitrary IDs, solid backgrounds, +clipping paths and transforms are handled by that importer; unsupported SVG features fail +before repository outputs are written. No specific layer names or ID values are required. +The first drawable shape in `icon.svg` is the background; the remaining shapes form the +foreground. Keep the background as a single full-artboard shape, behind the artwork. +The square SVG must have a positive square `viewBox`; it determines the visible icon area. +Transparent margins inside that artboard are preserved, so a smaller clipping rectangle +does not automatically crop the export or remove padding. +Full-artboard rectangular clipping paths are expanded for adaptive-icon overscan; internal +clipping paths are retained. Each generation refreshes the exported SVG files as well. + Adaptive-icon and monochrome-inset XML files only reference these generated drawables and remain static. Samsung previews are optional and use the separate command below. @@ -59,12 +114,14 @@ Preview the adaptive artwork with an approximate Samsung-style rounded-square ma ```sh python3 scripts/preview-samsung-icon.py --source assets/branding/icon.svg /tmp/message487-samsung.png -# Edit the source SVG, then rerun the same command to regenerate the PNG. +python3 scripts/preview-samsung-icon.py --transparent /tmp/message487-samsung-transparent.png +# After editing branding.ai, run generate-branding.py, then rerun the preview. # Use an .svg output path instead to generate a masked vector preview. ``` Output defaults to 140×140 pixels, approximately the icon size in the supplied screenshot. Use `--size 512` for a larger preview. +The preview has a white background by default; use `--transparent` to disable it. SVG output uses only Python's standard library; PNG output also needs `rsvg-convert`. Use `--source` to choose another SVG; its `viewBox` defines the mask bounds. By default the preview reuses `icon.svg`, which shares the adaptive foreground's scale and @@ -77,6 +134,30 @@ and shade are a visual approximation, not a recovered Samsung bevel algorithm. Use `--bevel 0.3` to adjust edge strength (0–1), or `--flat` for the mask alone. Themes, night-mode tint and animations are not reproduced. +Check the generator and preview with: + +```sh +python3 -m unittest discover -s scripts/tests -p test_generate_branding.py +python3 -m unittest discover -s scripts/tests -p test_preview_samsung_icon.py +``` + +The default generator tests check SVG layer preparation without Android build tools or +Illustrator. To also check Android conversion and PNG exports, prepare the generation +dependencies above and run: + +```sh +MESSAGE487_TEST_ANDROID_VECTORS=1 python3 -m unittest discover -s scripts/tests -p test_generate_branding.py +``` + +CI runs Android conversion checks in the Android job after the build has populated the +Gradle cache. PNG export checks additionally require `rsvg-convert` and `magick`. +To also verify real Illustrator export, repeatability and preservation of the master +file on macOS, use: + +```sh +MESSAGE487_TEST_ILLUSTRATOR=1 python3 -m unittest discover -s scripts/tests -p test_generate_branding.py +``` + For a device-rendered reference, [One UI Icon Extractor](https://github.com/Palmoe/OneUI-Icon-Extractor) uses Samsung's `semGetApplicationIconForIconTray` / `semGetActivityIconForIconTray` APIs on a Samsung device, retaining One UI effects. Compare its export from the target phone diff --git a/assets/branding/branding.ai b/assets/branding/branding.ai new file mode 100644 index 0000000..308e899 --- /dev/null +++ b/assets/branding/branding.ai @@ -0,0 +1,4000 @@ +%PDF-1.6 %âãÏÓ +1 0 obj <> endobj 2 0 obj <>stream + + + + + + + + + 256 + 56 + JPEG + /9j/4AAQSkZJRgABAgEAAAAAAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAAAAAAAAEA AQAAAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAOAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A41m5a3Yq7FXYq7FXYq7F X375c/5R7S/+YSD/AJNLmolzLYmORV5v+aX52aB5IBsIk/SOvsoZbFGokQbdWnffjUbhR8R9ga5f iwGW/RBL5u80/nH+YfmSZzd6tLa2zH4bKyZreED+WiHk4/12OZsMMY9GNsMd3di7sWc7lmNSfpOW qm2iecPNWhyK+katd2PGnwQyuqGnZkrwYexGRlAHmFe3/lz/AM5NytNFp3naNeLkKmsQJx4nxniX an+VGNv5e+YuTS9YpBfQdvcQXMEdxbyLNbzKJIpYyGR0YVVlYbEEZhEMlTFXYqkH5hSSReQfMssT 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 bZrdirsVdirsVdirsVdirsVdirsVdirsVdir/9k= + + 0 + + + + + + + Cyan + Magenta + Yellow + Black + + + + + + + + + + branding + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + endstream endobj 3 0 obj <> endobj 7 0 obj <>>>/TrimBox[0.0 0.0 1024.0 500.0]/Type/Page/PieceInfo<>>> endobj 8 0 obj <>stream +H‰ŒÏ1Â@ DÑÞ§ð²Øë±×éM$$€( +÷/Ý6D«i¦yÅßHØEX¥‚§ßû<éÎ/ÚHYöMÊê(ÕçÖXQKãǺÛÛõtY…ÏoZþS+©aŠ6l�(s¦g`Ø„GÑ�¬- ´˜ùlÃ9=ÍéÍhNosú +0êÙZ¹ endstream endobj 9 0 obj <> endobj 11 0 obj <> endobj 13 0 obj <> endobj 14 0 obj <>stream +%!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 24.0 %%AI8_CreatorVersion: 30.8.1 %%Title: (~ai-d4a586c3-da59-4a45-b4f2-a3f61c748939_.tmp) %%CreationDate: 19.10.2026 23:10 %%Canvassize: 16383 %%BoundingBox: 549 13 2872 525 %%HiResBoundingBox: 549 13 2872 525 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 14.0 %AI12_BuildNumber: 1 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%RGBProcessColor: 0 0 0 ([Registration]) %AI3_Cropmarks: 1847.65095334201 18.9379652576154 2871.65095334201 518.937965257615 %AI3_TemplateBox: 255.5 256.7677165354 255.5 256.7677165354 %AI3_TileBox: 1956.65095334201 -10.5620347423846 2739.65095334201 548.437965257615 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 6 %AI24_LargeCanvasScale: 1 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 4 %AI17_Begin_Content_if_version_gt:24 4 %AI10_OpenToVie: 558.778823316117 1136.45591414787 0.529375092079839 0 9436.45516483318 8062.69714993624 1484 912 18 0 0 6 49 0 0 0 1 1 1 1 1 0 1 %AI17_Alternate_Content %AI9_OpenToView: 558.778823316117 1136.45591414787 0.529375092079839 1484 912 18 0 0 6 49 0 0 0 1 1 1 1 1 0 1 %AI17_End_Versioned_Content %AI5_OpenViewLayers: 7777 %AI17_Begin_Content_if_version_gt:24 4 %AI17_Alternate_Content %AI17_End_Versioned_Content %%PageOrigin:46 186.2677165354 %AI7_GridSettings: 72 8 72 8 0 0 0.800000011920929 0.800000011920929 0.800000011920929 0.899999976158142 0.899999976158142 0.899999976158142 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 15 0 obj <>stream +%AI24_ZStandard_Data(µ/ýXŒ5úlG®'�D¦Ý0ßG‡ÖÖe“¢(�"H ¹ Ü”Ê%“-­‰  +ç +I 3ǾgÜ\›ãí=ÛöŸ;ö=Ûôæ(–æ(¢_uÓYf/2—ðÙ“)ΤÙe_E³ã~›z4öq$io<Ë:eÛ´cg7%g2vÔÊÜÊWÓÌ(Zâ÷m3dÉøs÷]¶�ooÿX§÷l^Y”šÂ¡9ö=ãLÆ~ ¯~ãó…žH|� Ú¹›€¿º›V»ÍJü–ºÙý+»¦1ÚhÛ¾V6F»ËÆpü„º?³3T'Ë?†á©+_vNÛ[�•»øÃ)Üy¹[ÙXÖ Pݧpç·÷oaÝŸÓö•YY7ß^šúº´µÛ·¶þØý?–u¼‹ßöU»5N_ا�7¹ÈÇvf[–ÎìŠéf~©ûâ/ÿXÔííx}\:§Ïg™õXDœ*⸃Lôçù·©hu1�.ö/ŽŽƒ8¸5ÕínWR�&Gnî­G^Žâ7ýæº$½¨K2ôäÞ½‹~û’ uç5é½.ù¸yéÅ2ÔܨÓ\ú[¸Æ’£ûµäè­¹½¥±Y%¸Gç +xÝŽƒ“;`&ÍqÅkÞéNk½;‘wºÛ N‘àVv +ÇL½½¥Î¡ˆÿ‡ºøËR7ýtæêb>?Ç/ †ø�•�Q™Ë¶©Jú*Û’üëãÚô�|‹fïL¾å¿²7ô·°ÔÅ´ïkqtºïMe£R�"ёܚšUö"ÿùcãy®@jñS°X¿MU¹S°¼O°þœ¶²›ÆsÂÑ»&uéÇQ›¾k]š¢7õE‘ä¦ç¦ß¢–¿ÊøØxN?2—�g’£S$^§ðäîº÷Þä¡'w¨;­µîe¨¥ØuYr>Žd¹{)rQ�ºkïyßÜ$G²sskùmÏ>ò¾É’�¦ß!õÍ-ê®K²Isô$ç⊥.MnzÏ7ߥǽ5iš!9öÎÉÜÙ‘ã(jÒ£=7Envsû’;¹MÍ˱sžîu¹ÇòìÆÐä£ï¥Ç�{ä&©E^nݽESëq“» }÷}ûqÜÞ‹f7yÈ}ç<~w?–|$»Y†Úw®Ö�¡éyG’"×¢)vróÐ�]@ç77·&Eo–Û‡¢Iê]𿿢ïâÖ¦èǾÉmzß{7÷ö¦XzS“ëw?"qžÎK®}×[÷�,»ÈGrõXzúqó®ôe'Í.–åÈC¿9Wë¼Ùɑܦ¹Mr›[‹zsqÔ!×\.KÎy:ïy/MÓÜ\ûp›»Ó"çjÙKÒ“¦K]†¥æ]×üÓq±�¥×}äÝ4Ëpwº®ù(’äÈÍ®5–�A·;N·¹ÊèÒéq›¥(Žœ›\ÔãÖeGíMqô^{Q—fùÈ»þî;;;Þ9O§GÎÍÍɱÓ#ïHs,ÇÑìÆ-rQÜ}ä�¹Í²‹¦¹S4µ(–%)–¢9}Yr³—½{×ËÞuS9©GÑÉ�‹½+÷8–Ý‹ã{è=]ÉKSËÑëÑ E²×º^ò=Ž\Ô^Ü¡9òÎ÷Î]ºÍ]bÜÙ‘œw\MÍËr—»sg¹»É;çéLßÏÎô½3}ïÌqܾä¤ééF-Фï½ìÝî"wv{ä¤.½hŽ[Ô¢ÈEmŠ;,;ïvÍÑ�c9ú=š¡ß¢XvÏõ¸µÞ!©E®&úqìš4½7Ã-öQªÛœ\éLî¶}磨ý9MÞõ8îr÷qÔÛK_êÒ›dO‘iÁÅöÙù±ó#©G²óݵÖ=ôåæ},ISäZëŠææ<iöŽ4ENš#×Z—a÷›;;ÒìH³Ãmrj7†¼äz ;Ù»²‹›óÑôÞ�!×½Ôc9šeé½/ÃÒ“âèGí½÷;4ÍÑÓIÎÕ6çäJ§7ãÎÒën‹¦9jÒ#šÜÙmÏ;ÑܽM^zÏ=æîÜÙmÝ»­KO÷¼ë{sgß}or,·7IroÎcn‘äšoî4µ&¹¨Er,ÃMönì»3÷æåîžËp${ç·i–Ü$uyhöÝy²{ßËÑ�]ä!7Ù9OGrÞyß¹³+Í^ê=޽ËRä~ôb¹C¿wç}9K-ê‘›áhvÎÓ7ɽ)jÏË1ôdçj©wÇÍ­·éuÉŰï�¦ÈÍݯ�ƒ|x@é…žH‰nv7Ž®|}£S\6¦9®¼ôÞT¤S¼¼·(î­÷æÜ“b½Þ¦¨É>jî»è½¨ûX¾V÷öÆ2ššZÝ£»lLžOÊñú¸G7 NÝ—�Uö2ÇÇëãbœ§Óšót|�žNw|�\­Ó£T§¹µÓ­U÷éôît»3ètÇé47 Õi.MuÜwmz=Š%'M²‡º#ýÖ^yÙwK‘,÷=;7Íênµè5ɽ.Isä¡îÌ-î®w礨¹ØCÝm³“¢ïåöÜwíCÝy/jSÔ$Iš^ä>Ô\¨Óœ\éDîÆýŠ€T¥ÓœÂ}Õ Wœ¸�‘Ô)>œÎHÝêÞ^¸öžfo¤.þ«ËÆfs*JÍßM[Ñ첩'NÝ´%:ü¶?€‹©Åoì_ºì¬~XŽ¢SÈ\VMãR—è³ú `¡nýlüÎê?ý¬G^Ýߪì NcÛ¶éùã´Í¨¤ÚŸå¤ßªì§¢šÌ¶/¬6€4Ç̦aû¾¥9®$:E§hß·ÔÍä[léfs*rCnüç9%¸7ænàÛ£.+»µ¢— âd.;»l,r_Ð ¾ÍŸ${Û7}'Ïî¯ÕÞsu1ßÇ‘>¡ÖŸóÅ·ûç9m3\Üæ¸ÉÒ{î5÷{k34;^®lŒò�4wGŠ—•±qèËRÑW–e)à³§qý¯ÕÅð›ÊÇý9uã?·•W¹µ;ÒÜß¾Mqw"'»÷â.=rR›�¹{7Er‹¾Ó�ï¦/5÷z,»çc¨»Ò,Å-n?ŽbHö½»ror÷ÎM³,Cs»ÇgOoÉýpè¥Eþ˜e¯¯ûšÝ?»ïùrYÙ·¼ÊÝ=äøH=µÈœoí+ÿÑüeqZÚi›™ãÊklÿú~üoS•ôeqêy‰|Sù8²{È3ëšâßÞFZ‹"iŽ+ß}AÝCÀg|14ÅÑ÷’w¤hv.–bלC½äèwËŠcÉ]–d’#ïÆpùŸÓ®ýÒ¸K“wâ¸Érä#éËîCsw[s±÷Ñë‘ KÑÜÝC¦Åi ~cEË}YVöG«„îtô}œî²Ìeçù“¾,­m^å¦òqÝãËRÙôê›ÊÇíîXíZ[öfyñ½©ìnNkñ›ù-›y�›ÊÇÑÔ´µtÞ¶ýª.�Ý/K?�›8»‡¿í{ÊòWßX4»¿vSß‹üG¬æû¸‘�/MÞõÝyq켙ˮîíµ|×ÍNîîñXYN¸éù}œÞ�Î;’kêúÝ=þœ^Ü—UÀµ9|ùîM±T7ëiƒÇDXcS±áÇtmt•4ÇT\l@¢{üvfKßÇíÐ uïîËÊñ?½ÌMåû8мSŒñ;§ñ,ßÚvÓu�ø�ÓŸßÇu:ì�{×Jƒ$ò¾X™ÉÏMAûc!nàç¥ßÇEnï Ñ6ÜäšaÖùÄ «Â#5ˆN–}„(CDfŒ # +ü•®1 öå4xpGb°½XÞP3y£Á цê ²“�0Ä&!‚AV9È…Ù9r gÐá…Iö q*J Ø· +EAUhD Jq€%*Dh(O!£IšBˆº F”B R P�ÓšYûa,˜ª¾ ’Ô( `¬¢0›ˆ‡‚!& +¤ 1>ncŠ`B .Ô6•‰%ð $FÐðŒðòH/¡­°�T«¥Ól"¸íˆ ~Ö!¤ayí¦µ!ˆW†@ÍN‚MS)BB`¹!�™ÎÃ`$|\í³;’~4¡à“=9A‚)5KË�ð±ÀÂ-Ýx gÌž ¥Ú<4 ½žMõ¨y} ysúÎ<�d�y8µz¤Ñ æ1ÆR�Çj\^iJ±¼�›E^†ñ¦<‚Áíä™RÚ䉟 Éc+°oCDžœ€¼×—z¼Ê­Ññ +phã±àOãQl¢ŒgˆïUX�Þeñ°xàv)ž,ršx!ŽD╨CÄû¸YU!žË3=<�˜ñ^ÿPL/~íÒs¤Ç8ŒiºuÏ0º÷Ky{¢šö"¬Ìá)°Ð3À°ÞÉubxÂè…�ÎÂ{I…·j' Õ„÷qc(rð¼Yùy0�<Ð…Qð­Á;xžç} ÏóL™B¦€mEhóMd +žçy­„w@ð¼…,<[ +¤ð­…‚àyž7s&žwÆÏQÏ3Ь¯¦: +^c+ +ÞÁ�äó±\:«»†Y�!N +؈w�qŒœy7Råõ0HÎÁóZžî�WzLv”%ìo)­åã›usY ø†4°•*±‚ªS¹14 ÒÈÈÇ´WµRcÍô X21L9‘ÿ%”°jåÇÍ«Œ=] +ƒTypÜ2ž% ×áÐêÍÐIPƃKub£lÏ>4ö<©’ m2™›ì­Ul´Ãúq¡ƒƒ «²Qëƒf) Yt‘z˜Mr]6Y˜ø¸ƒ±àÀX“uT¨,èA¬nNH–r´AxÞ+äyäA`ižç¹:Ï‹9Ï;ø¸èy^ˆPð¼h‹˜IáyJj3‡¥áa8�ªQ"e,‡$)±>Œö©@?V† ÿôͅϼBü¸îê·ÎåÓ b—ÕÃô¡…/,˜Ì'ÛJQÃa鋉§‡•ÛE CÐ%f“äA¤;3µ‘,QFÃfÜ2zÏóÒ„MR€i˜lñ?3˜Ýðù€4F_÷ýˆ{cg÷¯›Õ}ÏÒœÂÞX­ Hc”dgù_¸ÌeÅô6à¾léË&®iñ+`Ž+µ7ΰZœ~âÿÂ*_–Ö6œHcÔeÛøÃ4½µª»iÙÔýs÷£¿eKäÈY8=1lûŠ~-‰6®€÷òkoÜ£/çt&ÉìIæ²3™uý6KÒ„_Q÷eéM榞L×u{o*­Ú�]ÿg•˜ÉÿÓùMewó žo¡Îo*Z@L°ð=ÎtÞ¹/ŽÀ¾ß^’ʶ[@Lþîë¶,ì“â4@€‡§!¼Ó‘,S’ûF©a¾A®]lôQ‹ ½Ç>ÂÈÑs_v³üñmNlïRk“¯ª û±Fš~XCLcbc$Õ±áK.–°½Žã6wIú†|›ÓYæ&éÇÒó®›Û{]–¤éñ—céÉÔ“åw¬#Ç´Ñc[l¤äºeLTMÞuYjßàšï>’ZµÉËroÏÃ-îmUU�•_…uÑlÄxÌ71 „H‚ñýqû°jO › +¢+C�œ‹Ñ&•¨X¥,¯@~“˜„—D5*ofûF$·ÌaÕOÙØ¦‚›#] ( E¤àVà |‹Ä£àŸÿ¥Ñ2µP¥/ªÃª[t”€"MÌFÙ6:{ÞLOI‚ÎÈQ$ŽVýq¥gl¯›ã‚hyr¢à‡UsX“ +aZâØÁ«Þ�öCÏ*qüqçiu>¹çΧ¨@£aîƒ{µ9�mîµóf«,Š&ô4 ÄQ <Ñf«,w�‡þzPD7¯8K'y›Îåù€˜›%þ—‚”€fÁ('œÍ€¡ËbÈ`m´žŒ>0ëšJ)&5•·à3×A‘šÊ‰˜”yawÐä¨ `tI•!¸�ÜVŒ�š +!1‰cM$‚à´7"=ïéáÇ©ÖÃcÈš„GÊp†·Z_ ï UÐÓÌž­#va4ª|"ÝckY9T‘r¦P�ƒãWê†K)T^ù¨/�ò:ðã>2—ÅÅé�}žMÀù°ËÚ�;Ò•ê(ÍÅãà´Î‚LH­ oh91·µpù¸RG0zËVª#E®fàä²Á˜bZ†àòÂlÜ«F/F+Õ!‡¢ßK¨õˆÄÆ oÔPšLJM°&&—ÐT`a…' +ÃY”€f”áÇÁ¤Q&T‡æÝð.©Ã#=n Æ +ÞŒ�1…Œ–‹W|j…”ø‡Ý‹ ée%¦íã8øÂ/’.„4‡$°Ù‹êÎ;‡%ã�h‡ê²6ã"|ár2ÜU«‘(5I-E¨ý}SµæF Ma™VV.'ÚÚ<-²‘Á5�E2ب"Òàã�Ë®¦¢`‰lÓÚÙ©æÍ î@Ÿy¯L“½¤�lÓy—uJŒ»¥P¡˜jÓôØr¶ßBýQsįX�jk¬Õ�Õ@Æ ×tÓyÕÕ–IêÇ�f�©Ž`‹&:­'˜H4‚ÀD›[JQRÔ‰2IY°�!Uç•«€¶Ý³à¶à,¥¾Ê�ؼ0&Sø2-JÖ#ðB®™Š®²-½-iÀÔ‚=@�ÖÇ–גÀ0‹€ýN–§F, ̵®\²ÞY-äØ^y˜Áû`”&žÙƒ Ʀ�ÉW-ÌÔº¨JC¨R?4Y +’C)ƒ–†La$±EøL”)&« >N$Y(Ì!vúHfŸÓޏÆ RŠM�éÑYYL…“º0Áb³¬JîKøÐá͘·X˘]óKtcM6¢B:ã" c 1n„Uˆk×%É|ÂÐ…%P� +# +´Â�ÐLV‚ b1øpÖTúœVò9y[ñ1X�fžEAò|œ…¿"Ul&<3k˜u|úG�“‹ Ò‘W¡DGòO3NkV"qÀ—p@ü¦àĨ7ØœêÊ´1P(*I§ÄØÈÚEM#ÁÌRZ,ºhΎךXÚÐ�úµÐ�AŠ ê2#Q<³› 'Xb"a‰+)´AˆoÇ‚`A ˆ°u)Ço (ù€@)·Cˆº¸‡Õøe‘Õé†"ŒCéàÒRŸCçD¢‡�KTXÊaµE�„PЏv+ qÓÕ;ÔÔü‰ +^ɬŸ�Öº¾ÔLP}”°$}ŠHçóÍfèGŠiË'9€§ ØU§J†¡ª0•ÇCÂtà4ùkšüµƒ&ÿ~B% KrI.É%•nFÁxL: ’Š Ÿ/bóÍ­ÂÆÃ$&\–Ìüã4#[Ê “9qp¥,Ü&™ˆÛ‹öqMþ—‚`¥xeVçb··Ì†ðwQ D§1Uò’!á5¿JÔ²ŒçáÒ V±rÃø…Ë+âŠ1wó‰Jg `{cVƒ‚°²�cbð=E‡~6Ý’2\¸bõ7ü'ÐÙýŒ•#äø¡ÑU%®³ï9|\�4P-Ê‚J,¸€Ä¯@nƒ �@%²n¤­ðYèŒæ‘‚[l ‰£×øxÆ„ wÁ-N)DõÅÙ‡ÙH{#’’–rèñÕ­/†wS¬žYÂ(< +P÷hd£Qn$LLw6ß§Ò!¹FŽÄ3ÛÐJÝÝ 8É”…œdo3T23…*P†…gL‡gL‡ç©r„g#<ÃÄ#L*Kf�æ×È4.^À·‹hÞ,eÑ4T) GWtM ž†® OãèŠz J‘_ ˜Š xlÒÄ�ˤœÃ^±gÒL&½\X˜ÍÁ Õ……Ù!òªŽqñ—¿ü=ˆ¤ËAtVûòE4÷b¥4,N‰ÔT(ñÉ«ô�] C—SðÈ‘2 $…0›ee²j.\ÓAˆ(v䕯_×ýŒ’�£¨>®ì2L†uXxÆNSÁO +eø„tŸ"aÇt'ì˜&l •œ† cØ1† Ó° +•œRŠÓ)µaaö «PX…r�&�&ýžL)SÊ”:œ`BQ~¦I÷¸ÝxL�IïÀ�ÂÀc2‹“Ñæ ½ß8{Âñ†Î©!öÃÐ;{©†n¥Í:9“[ÅLnñÍ|ILä‡F&¾RÎ%³ÐÈP/‘%“Y#Û ×ù>Eáéô}œ‡�î—Ã$ Æb\˜ ÌPa\オ�%zbz±ø¸Ëê\‚sBô>Ž‚Ô�"d:©ª`—EeN¡ ‡¡ÐR7J€*à§Å"¢,ÀRYaÁ<ðãTa*•:�˜�˜Nƒš–bR•žCü¸WÊÂOGxÆ´ C%žÎž€*…*¥’aèϘ “a&±ø•J�ZÁ ³‚Ÿ,‚০Ã$`*~¥>®�²±l,ËÆ²±P©CFH¿!Ý6–�…JÀÃépb±0ûDÄ4·!b6DLÃ*ô”°)~Ü@è=½§÷„¾§÷dJy¢ÊbŸç©,öùqgƒ&ýžÞè’ßàëoð¿Á׉oðõhÑ8k"´&BI©9R Ñç÷(ÐèÓ&hô ¾Ò¯j"T2Ðù8ò3Mº7ð˜|�Ï4é×;ðÃ4éˆ�GïÀÐ4éØqãl‚Ä#¦95×mÞÐoœ=?ŽS㾄Ø?ŽSSp.*Câaèœì ]ælq¾$&)"¦ß*7Xe1ã>¥Ó¤s‹܇IL6 +›ÜÚ ^¢˜er+¾$&0Tj6çx¥,ÜôJ¿æ÷Í) 7Lf�Ì›ÌkÊÂ-£”cRĪ52Fç[Ÿ"n¥Žkœ< ]G!Ê_ 7WÁi—Ù¤e ,‹�®èÇEÀwæàE‡}œÃ4MÉ|„!e< +¬ãÆa%¤ ƒã ,Üöp|"—°0$]EØ1ýqê%£ËŸ9° +•p� 3:tP˜qI¥}P|“�k¨zÇrkqŒ+ÁøDq{iôÆÓ$ PÀ’C(ú:f¨0 ³ +ÏXü +!‡Ê-Ì~(šüõâô ¾þ¸ +a„§1ôK*í`“Õ616c›» gÞC~܇‘HÀ#á„!Ý—ê%‹ÂåÏßåÏùµØ(•ŶŒÒÒš€%ß7r´e”ÂÑÇ ,n/�þ-2¡^B»x¨¼È¯E1V{ÄÀ0$àÀ§oôÁÏê%ˆ�¨Š8ôlj+OÀ­Ý\Š�Ó˜µ•àTh̤»†øÛ²€U+�tD¢üô\#ä%CLá¼:I®>¢&K†<ÎOD ‰ ­ð€š)r¿ŽšoÆ”?®$CB)ŒËV ý‡xf)QÓ ™“, ó«Ä8–…° âTÀ@G%È/UúaCßHÓÒ2–Švi)F¥�ƒ´ÏèÂá°º2ùã +°e" ,¯@ö ñë°Éd ¡²Ø +X„uH¢@ÌÓC•BŒã[*âÐ×ü8Z”62ÀjœQ"zA‚™ßøq †(‚à†^�t’èÐív–óTC˙ѳƒ +`.ªrRÊ|{B@S™½)x¤qôÍA9>%åu#•Þ1Ge�~ŸJ)&ºlÎ RNh�üZÆWÆ¡á�;WˆUŸ‹Æ9<&–‡Á(”ùã�“[H4fÆäŃDÖ™òù_'®P·xâ ,R�Bãmˆ˜–MšƒÆ”á„Q˜eÊaÕ£ú%PèILrÌðX�D7hÌ�2åÄÄqìq«ÖhTÂG‚Lš(Çž'à3tá’j5eèpŰDƒHÈàrÈVÓøz�f¯Vìª�ã…4µbðüDÄUAZ1ÁÌqŠ»€¬[±xX5b¿Ž" f¢c¶¬ø$C$5ž¸JYñë…˜¨Ôµu™( ¡BFFfd@ó (Jå¢áÌê84 FFD40$0&ãÀ0 ÅqFr…¡TI†dæA8ŸŽB�ËÂ<è;&H›k:NFú× M üXA—œÂ (1ã™Q͈š :…ÍbºæŽ†2€"´çDi÷š›Š«Ž•c±Æ;$¡ø2zÓÚvû­)¦¼©½¡&Mm7+òuòìÌ“y²;$x/ç²4h¼š3NTÀT—æeí¸V·×ÄÚÃI‘½ã±Nô’B�`ü©Oµ�H¨ŠÞRÙu5šWC|ÛìªkºrÛdópå§.ïÅ©ûúåx²ön7«é3Õ +qq¯R%Ì"ÏÄ7–]$ýcY‘B¿µp·×Îæ5!–¡½ª¾áòê�™R_å��_ø¡8ŽÙ íû¸|(E24ÏšZJèÀ°å¬ªA +.Ÿ:”H =}M9ÆËNˆåüÞÔ~ÁÄX2 Ëùvz?'½÷ÂöÄ�$Ñé�ŽÜì‡[ jj§¯ôÙߦA¢˜Súþ™ _t:gµ‰�”Žù¨ +M3Qþ”U_&�Q¸m µ¥Í%`Szf) Yß_{ŒÂ[É,ƒ³Œ&Ô†f!À«©v’-ìšêCc�úƒ­’#CÏØœÎ›.C£›Ñ�¥œ—1õ�¥›ñJY(éžÑ�¥!|¥$²ÅPRú½?æ…ZL,z\Œ)­è·R¤jLÀ ¿î¶0â`Bs®cî†6b„bÀöÏÝÞõ>€o2ÿpv©(7Ô3×­x3„`È)Ç=b\P(æp^àŸ™ß–åUØó¨Ã#OëÓèh{˜ðAZý´€©dßqþ¿}¨Qâ€N ‚ÑïyÞ’—,ËC|ø`–’ç3ƒÆ6C¤X +ôÚƒ¥ÅqÞÓï 32눟ãü"P¹íïŒašêÄǶ%üã>8EC*R�ѸKgBäÝ0Å:FñpUtœ?“<#4r4ì%Ñî;Ú¥0µ‘s»K"ò"}ßÉ:ÓÜ$]€”à°Þoê�› »ÖOƒ•Q°$G‚*ÏOÏ�KÕ’Ôö£sø×øèKÅT´ìuwxó~B&5´Á—´É5ÁM㮾Ã@‚@üßk¦Õ)u>*D>¢*5Ø[é Ÿ¢êœ˜©�!p8¦Á¨nOâðwâH“A7¯€\26�#�˜Èƒí¢5…‡R÷ǹ2L;-òd~‹ã08,ÄÛÔªíFúÔÆ¶6ÉYü¯a…žÏëÿÇà,�"³!ä€)¶Ö´[ j†½ˆKžv]߉édè‡E£*n»1ë^·=KJüF J–Wãù]Âä´ÿv¯³VPƹíáÙÀ½~XP·½6ª“3ù Ñm5lôd*ò¸D(Ê„Ã�‘&ÇmçJ½±4*´á£†ùC/ó¢b)õ€Æ<°ì‡Áö Æ3Ø[‹«á_ ÁuvË2 ½µ¾PUlä)…߀‰ÛKé·õMd<z’ObÀ$»”˜Læûxßw¾¾á±ÆÄð*±ˆ3˜ífì­‰Hý\jÜ+Т,–Òa´ÏÍŽöGM¸7£=«å®y²š¥�;�p¦äãyÔ}Ú4Dí±2¬EOà�C'ík˜YÈîó•l0y/5Èi›T²–(Áº:ÿÍžàšö�@ +àêé‰]E/Ù¨è»c[§3๊,=H–Êë}ËŸRC× ê;HfPRþIÄÁMVA$ú›Ò…áO¹/¡s§7Wô¸œ»Þ�1º]�3Äl`o–®`Ê–žnK(~%ˆ�F÷’¡Š¿$": ‚ÛRaüÜ%fÙž¿ Ü+Å(…í¡î�ã0pJøþÏý*fÄÂ(ÏÖ:ŒZõ)«ZŒRÔ_Ŭ�Rʉ7žB£t3ù¢)Å…_?g[1J—r›«øuZ³¬ýuM~Å(ñ°¾Q0{ïë 3UZ¦*Å(Ílį›–?†þ2N1J×Ëòëæ‚œ9=ŸÇ¯Gt#ƽ6Þ#Å(u#½n~]ŽÀb”*^Ç¿L®=¥¥;%šëIÝî0J—ÜÅ( •Zs“ªÉ¯Ëá«¥û:Ìq~�ˆÈ‹£tû�…]xîÞQR§ô¡ÃÂŽ~˜âÊþËî»'çç"E¼¢]½þWú„�bš»mÔ7˜I«Ù�­zŒ“åòý¤&¹Ìc¸M]ßVç²ôFô:ÉõÅëÇ$/mõF}—†ÿÿØêNŸXjÂ^’ïnþ»2dH—‰°ª’Cñ4^öæ`ù¤ ™~àZ¤3&ôŽçûû½Fws¡=%‡MÒ�Ž3éËÅ‘Ü2묛%}Öô‡i«˜Î�¬Þe‘p¶ Q¥é"Ô¨©pUþÏê+×t>Xk#\ç&�ÂwZý›[½¤Ÿ¶•Ç´òLnàpÚÉÏ&Àì7ÎGêÄ­’¡qUôÍ#) 5\=<é€6}È +ß�v~¿½2ÎM¯¼*kSŒt‡<”azPØü%Pzí +½|_ÉCW +ÕÃêé5üp®âðYSÚelõPç€eáêÖùme²ÇÚs6ñC[:Ÿ,8VÐËÆ²á,¿²¡”盾XfÆVoë#HÓ›~Ϥ–\‡ð¶mvA,%LM1= ©F0�_¸š€Ž‡”zóK—Ô"a9rŽ÷N/Ì Þ�üV18h+©Š†ÐÓ£ÒÒ•vúXïŒÕH¢¯ùâGÞ‹Ü÷Á(…{\p^Û¡+% FŽüÖ¹ „~¿Ã�+\ ¦¹.Î4]!pú­m‘̆𢻿^£×àäyÄŸ°Å2ï},n±9ãú�3g¸Ú(ó¶]¿ßjh)Ö˜—ÂF3²õ Ñ·m¬Eâ-‰õY:¤S˜# º3ÕŒ€9m Ù�­UG#øéÉ´fB˜ ¶>ºæZKëñöU1¶n2atÒ÷¡ed·xdt¼Ïòî“ÚkkdĽ7xO|ws¬Ñˆ»µÉ·å×i"n©ª ´ùÂA®×¿²y¸• "n¯aó¹ƒäè:;Fqc9b”°|˜§#îÍ^Q ÂÄq׸ÂX� \²¾ËNT¼*ÖvêîybfLL(>›},Þo$gb×%t+ ¡[e0ÕLL©¤Ú‰¨û~¶ÍþЉµùì} +¾àLìXdÃ7°…Ø�ÇLŒQa”ßÒòtm¦³·Ûß…þ=O]{sƒf•yеûø9PÀƒêÏk_û¤‚Zº.Ì»y£ú• ô(a7´œ•íÚùe +¨Áò +vw¹Èï¾AuJØdÌÅêÇò¦%ñ³½B‘†»Ü¦å™ +%쮥޺.¯ÓKB¢ÖñÞ¼Äú±„ÅùÎ?ú½uÏa[IÔ‘¦†²ê,jÙÔL0£ûÖ,2ñÖâ”:™ñw‚Yq¡ø·|`§’5v.çrB�yç¥Í+yCí¿&Sõ v^¦×f‚!ìÜܹ'÷X’/@PÆaý +×Û VëÌÚÕ~šÏ£ô)l;Üa+ÊKnóåKÔÚ™/³†¹œ0ž=�5: +Û6æt›—·ß×;—Ø¥rŸœ u@$%Ù‰mGÙ­WŒú™9-Þ°´o‘l+Kl›âtRùž‹³;½Y:˜ŠžûÒêè]9Y°ýŸ£÷ìö·µ:ýîJKô¶Æq �l>…xC†ÆL6�ˆÞáÂ:Êų~ßbLèÄž¢Amn}ÏHô^Fâ}Š{„£·Æ½ë×/õ0ËÆ¯6“†yzþö�i„=*.Í*g ×îÆ‡µ�¯¿®mñnC|_êG¢ b ü®¤<˜°PÌŒwærŽ´îŒeÀn6x-3dH�À²4¹[X‡2j ºB>^dN:Xbà&}¹Ãµø,Ud¿Jylx“�r×ZI܉܎G&y¥¸$ÿeqd-‚þÚ%@åI‹Ëµ5+ù"¹ªM#‡ÜÒ¿ó´ñÔ“Y6úìÞØqùÇü†Äzzp'¶€,YUÓ"™rÝ‘ Ì¡€&ô—±(d4ئßHÓØ·+A¢”ÏUþ:=·ÕÒ—S†@O8¯+‡´ÏV€0v–Å„~ÛµÖqôŽ4I�³� ®:Qñ€%lö±·«ÛX$X¸ëô‡ÙáR´€Oéki{ƒð*sÀéÇÒŽ)Љ~ È¿R;+ªx6v8_íœê‹5ÜŠûèzº~kqǰ-Ì�u|ÂtÅ;2-nù~KZû¯f j¯ñôèpëƒZ Lbïϱã""€è [&€cH°†ÓŸ ’Uj•¬Ù�Êà·¹YXsTwf„¬q¡)}c``w-×0>…¯s[[—B9¶²¤ÊŸzý‘üx7E[g�—ï§ãî(ªÇÜõ ÷Nv· vW�ò£÷ +͈ ™÷ÏÕ€~!”5š ¤ÒÊ÷Po.\ä|ÎÐâl� ºv +EæV�ø[C ªJ(* @ÈYWn¢ì\2ç: ñX]ê|*¯Ñ‘)¬/g&ÆLr.a¥éqñ$Æö·4õ¶é"ô¢ÁAØ�T€pщä¥ëÛ|Iñ)ݲX›9 Æý÷i�òÈ–»D P5(™{€_N·¿ÝÔ“¢{÷q�F÷«v'9Jv•(�ƒ½i%6�¦âe(Gd¶£uÅFkžQ .793ã|ЫZ‹xöXÎþp6|NæK ŽžmÏ´ì~Áÿ±',Z’ÉÂŽï†ì¯ д6¡Ä†î9Q×Õ’°Ö@]>$d/ötçén°ÁA]F�VªÃT¸ƒÕ³j'(ÛVø{KU,I×­ÇTxÄ ñIÓ×8”Âä%«9bÍë¿M#Ø(zô)r}øW É�µø…*Wf¬…$�ÈÞ§ØA1H/s…øˆZÁ³£X<É×"¾öC±êëdŽhlÁL4ÅÐç&…sÖ¨Õöª)Hvi³ÝÇΗ§´ åDHó�ÍÉp5ƺñÜÔ–…é,ðÀ7wÓ•¿Èk+we-�€Miãáé !>q ©l<Ã×ÌD‹î®*¿¬ë`ä´XËvÆæè¾ÃÏé= ™•pþÑ(0UOÉ�€š$ZÓÉ6!N±È»t/$ÿªüXÍ(£ÄxJ€!~ûk!–Û08ZNao-ÅS_0¿›6 +Ù pF‘BŒ�ÛÞFc•X2‰I­Ú™¸’#DªöÂ5©;bÍ?ü fA©Ôõæ^óˆI3yË.ÁôW?¥2ñ?µY© ]L]XH9‰)uõÛ¥ž*ã�/³fxÿ¾¥®(!!,Hê¡9ß,vê’½’Ä_H’”º¾W½æÖ:À|¥.IK»wIî|‚rãe¬¯£»Z‰�+í oü³*Ë'•ÇGÚk›j.‘¨+‰9”‹zeò•ÞWA¬j¨­ìë�ã1a­ð(ÐiŸqT<ØŸ¢Vy{j¹œ‚å´_†Xu^–€A=i߉áÂÁ.1X ù¼ypáOÍ�6Ψݓ µ™6G;¦ûÚ­ën0õº$PªÝþ×\©'ó齃±:IÛG;ù2%*£_ÐÔ‚£„EpJÜJaûâqÝGq‰³çžüB†£©{t´êž)ƒêîuÅ{ô| +m xƒò:ûµZ2;¹õ w2U&£štò5#k˜‘¹9ô`7qvî7%m+�è‡Ó2“X?K_? 3h SD|é1è-8A!f¢Tuç±mn%ŒßŽgR5ÙwäfyUbF‚ÉÌZ;€Î•ÕZ²˜Ä¸E<æõCÚyÔ¦ÕóhÞ,ézæŽ7©ØÖ\_6¬\}zœí†ÂY’Îm’?€�¿÷È:è`-úÖ1£ðÞ ƒ¥X,U2„lŽüÖçÜ­ûTáq Yuí#Q#PjÅâôI+P±šÈ0!€8Së«3Œ“([¸J$®ç)“zýjöT‘­¨¿à›~…g²»i¢Ë8<Ö§‘ñKC÷¤©qïqbÜ£CÇ.c+ðÍ_´àã¥Sù`ºcáñ{½QÙ¥¨2¶z޹ýƒ~ñ¹…s¾�ë!�W±Ní—ñ¬Ž +R¥ndG]›MM ™BÆŸª;qfIû¾­qnîäüIvœÅ½±hE€Þ�wòc'æ17Ð5ñƒ2:RþʬZyÇ~ž……‡žíØ;A{ èãHWxÑÎÉBöo�a€wãUÝQ‘$`êr„†™’Œ!�ßîžðX¯a¦Vü‡{š¨Žե݅xübÀ- þvÐ<1!V¹†^t �Îô§?E%Õîr.Ì +ªi‡þ8N¬¡Îý“}ó 4Sj‘hynü0>KM#Ù +Ã÷VžÚìÝÍL>ü’gy·@Üó¦9Ò,~½êÀ½^³¾£Ç>QÙÄ{);Ò/ÊÜå/4Vô ‰-Ê-D˜[ºÒH‰r‹°Ö8]]¹•…wæÖV0®ÜZ1?šA¹E-î&õ0·gåV¼=¬Ä¹ç'€vsÔ˜ª–¸Vn-÷eÅZÀ'Y5ýè#”[øœyæ–þß¶2Pnéçæ9€{Ü`ÚÈC¹…k|÷f~rQnÕÏDóbn9«j.w»òNŽ¯ÜŠ¸®¾Ín¸U”[4X’u‚¢µ9¶e +XÑcs¥ùo;3æï¼à"ÝZ¤è/¦(YP}Êßü߈f3ó7Hp� &$`t¯�©¦¿¢·¢â2‡v—)l#®Æ ²Fo”�@+¶=Ò[t@�¼ +Eµ²9òÝ™3?�n ßýö!‹)œŒ›PÓãã_ Í,0ž%Ð"ZÕ/-Î ´w³=†UÅ3o5¡Ù¥ÜÀ÷=7S(Ÿ@s¢Ób¯½H'ÐÙ!½œ>éêŸó?YÙhÜ—ˆ–l…±mÈÍ\F“Ûr›1p³Á6øy͉»KÄé…`ã +_9ÔìÇ@¯áÔÆqu.ÿÏ…Êv0€b”Ú~�Í\ÿ—:³tƒèOÐ"­2‰VnuÅJâ>uI ¸PMR2WÑíã~­Ñb¦-}óŸg‚Šušh…�ëž}óƪO)¶¥Š¨TlÍejh�†Í­6¤öÇ­�©d1˜!ÎÜ�V 0¡�, â>¦úwÁ¶ûiÅÈ:r`¹Éwã@P¥b‘{1r§8�òräõ2éq29Å…³ A“·æÂ$¥ð”jmkyŸóäPW¨ +Ê›21x0étÕPpT䔲U�éòxYyIã\.¤8óéhº\BgÜ0gµSbœY +„)•¬ ÛÄÿ«mqéßH®,�ïh¦›àÝ/.ˆ]ËÞï?̓˜ëŒÏh-$ÀщÉXºÙÒ�ÖÝ `ŠŽÆ¢éLݾûê2`ºæ¦Á‚*q™!ì@3j wbZmlí(»‹�@ÐX›Fš½\[xP’í½qÇÈ1F‘RREÇÁKg�Ì�^…T‚‹œšç»Z�ËØ† ù÷}=t £„¨9õðÐøYfe./£fùAÒlxàfcô‡iŠé€6Hž\ýå^»Â½9ͼ·lóèíÓbÒ·Ǭì¤*¢ë3à½r �Y?ökÚ�ûžz¼1Òï¡…·õx]ÝT#7,«ÞZ n1øŒ73ä<ðŒ>Š”Pp­ K*÷qf6U>ö‚Gû’ù]ÆN¸œ¨D,ynΜ¤^*‘ÑC§4@ ¸íKš_ ï£ËhQW}h˜Ý £,×L©TÑ“Êu#Û&)œÆŽ ƒY +NB©‡ˆz &ŽÏ«LÛ•YLVŒJz §RÜ·’5ÍÎzì#³žNy qœ`c̶ õþ¡Ú€"®gK$æ})Bór¦©ö® DZÕX¡paØ,2ÊÚ¸N´áPÚ”I÷xÿÃpà1(j‡§úHw@½QøKÅäüsí;ròG˜�ãRåMb]”ì…nœ®Õ¸h�RÀ½íc™%°|(–áÙ3rí¨¢¡ êÐs†¯ Bão‘³‡Þ‡*•§f1 Û+0³~$îÙD§1Õ~t†E°)¦7«�û�4gõKп„FÞ#¼®¡£cY"Ò†y ùˇ”�k ëS3Ç”o«ÄºV{>Ù³²â€:«ˆÖÁ95Ïæ¾…Éý[­‹w“¨ó|éôHùŽÝˆ$tìaa +‚YûËn ÌHˆ† -N +ŸoԼ蔒|>îuv1u6&Æ0±, +¿©Û‹ffuÆÖ׸iÿP�¹ë×R@�Q��'F€u–âbË×D�[,.ÓfÃ|šQ®uB�gðú@jI;/W(¢ÎµVþ%G‰‡ÝWCTP]¯ãØþš"ÎåUFH@H*Ÿéoï M±½RT ƒŸ„E—]*rY 7BX¯)¥†`}÷ØÎ‚‡š+$d=—>± èf6‹U¥d Y”·¦ô…!y‡,ËJÔ¥´éˆj|òKÂ9mzšX Û±úZ½¬ÃŸ.i>Ÿ‰"ËævÍÿé,ûbmŒ¬‘Æ�™ßgFÜbÓšír¿ü»–›ÎÐ|�)‘­|#ŠäŸ™Š¯æv{¨ß|šõ-¬#¡ÙI©|°*ÅÓJø<ÄLÙ…0§&±ýò¬¥oµ+­&4C�ŒÒ¸P‹7ob$epdÓæ~÷ØÍNß­®­9#ˆ(7æG÷™AÄåDÖ˜^ Í‚6YJþ âМRsíϬÂB3{–Íæ3�aá ÍFÚ‰oË1‡ÕêÏŒ¢Cs9Ù]~fyð„æRÜ>s´C¡¹4äg®Ð07ÍȨ® ¹ÏœÂš5ô3ïgh¦’`·/ƒÿ™/¸vÝÐŒv¹�¹Ææg~>¡ùWOŸ9ùVùÌ„ÖÀš©X2Y±ÖúÌY rÎ…æúæúö™?²†æ³å{`ËÅæ!šš+ÅWÀšë#¥Ðì?Cz¦Ï +8§â6W„Ûàˆi´×Eâ‰Ðs•–Šd84_ÖnÁw*üÍŸùf�0•:¿ŽÑä3 ñh�š“²iègFÜlµk +ÍÀOøüÏœØêü—ä¸Åܘ.?³Ý¸ÒT?i"i5×·˜`}‘€ŠÜ +oN? y¿C¼¯­œg½)jÕŠe|ï&Mþ^�˜Ç×t ÖÜ‚I$±‰ £™ÎZº©ÙM’†ø5Ú0ø +*/:¡OÆò­Ÿ½¦Šsocó¦‹fŠŠás}ir&´å!‰ôûaŠÓQö—I×™Ïܱ'H_c¼�›¹�4�ƒcRá_V$â<,&)XÛâ\¯L_²1è?‡ŠÔƒÛUÐ(vŽi¶‘v%ÍÊoUÄÙ4‡ÿ6fp®Dç=‚¾LŨ]‚b;`SÊ.âȉoíH{‘~cšcoc'?+YèrMº Ø4?̪ck5 if¨€9lšó>x~;Ð IjM³ó^/òÁã!ÒL²<üXižéX­yþÆcr3Zy„ö1©�à û2y ÒæT‹m‚KbBÃŒý¨<ÄŠ–üaÐqùÍ£Ô9«çÞc¯IrÓ˜ Y«È7'¬ÅP.+B*rX¤EΦ%vî- ¡‚Œ)€½ã'úB/{“Iì„Ó5ÅPyÆ‘'KMñ•–„ô 4¢�åè;LIz„¼°˜BuO7Å“˜>?ê{˜Ú»9P9»CnäáÔûó$<7[Á¯O-µg²É£ƒï­‹uj‰hŽsn↺˜Ár¶È”Óqç‡l˜@­)>t˜läóçRPÃ�ÄÞ\Ï~Æ-½Õl�åÔ¢9úcÄ:Úå;n‚Oþhªïh9sñ[ö÷íFÀ^ç+èjo +ÛC. 0{§ò µÎT€´T,¶&ÿ$Ë�§bf]#N3©† ßý 9ÇÕj›üó±¼(naðÕ n_Šÿ3ë·¢Ú…&w¼SïžtiU’U§Hإà +‡féP�ä�àL¼ã#iTFêgÞHÍðÙÕ¬® CI,£•ÂXx©Œ­% ’ý�…Ê +hSºŒ�-`b½8–Xg²Ê–þ‘»qÍ«„=œ7Ј3Xr}æÒ@¾À¹G»5,ÎL`6¿Ú,WFÙŸ©âÔfIâ¡”aFƒòh)M§7x\ {j&ŠkYOò ~– )^ø„‰:Öè`5 +ãz�õâ3Rço‰{ÜrÀ@(JЬðk=f¬åóziU"!£ +“\mÚ¿yXbj§¥È2ÊŸ0&–3�y‚5ð…(ýq.qŽ4±²Ì—ƒ+žIG]С¸)Nܤš˜Z�LJ™ÓÄÖ~Þ8·{Ó$¶‘s0y…C7•»ùo“oÍbÍ—N µ•ãï™eV6ã}ÐÑ^<žg¹ŒÈ1ˆ©ÃÅÊcôÃpº1¯œ—¸tÈ´Ç ŸëðM±‚\á¡›ž—y¿Ÿ7ÖaaA“ ‡�] Ì­�·-Ù3°s +VAjç‚2—*TNi2;Ø?KÝhÐ�´q°ÄâƒfQ“»eN,CEž€?(K©Ô©Ã†¥*<_ãim¸Ä ÀaaCÆd{©µ!šM°ÒÊnlUHC„S( `å�¬?Ø:ë+wF~<³ƒ©pw94Œ¯°ÿuô7NCèËò%#¯‰ý Õ%¶÷ǃN³«Ö8M½]ícä^OtÁû¹­ö?É §± (Báp6~d‹�ÜJ¯: øL‹…$ÖÊ&ƒ´¿–œÖ0…j/YÂVy¡.1€òÀ>fm1˜Éè0Ë5 …Úƒ¡#ÔJm88°Í$ƒÿÙ¨úÅÖS”F¨ó6ÞY_IØZeaŒü¯h¦B –›ÝZ1­n±Ü=*ú j˜>3�@ÍzX$Ÿ‘v†˜#°…oœW2â‡jμ­zéHÌdÄçËPïaR!GÜd]ÇhŽ­‡#ì>\ÿm4㈑󘧓,'<Ê‹‚Kg^O.s'|V½oùN…ŠŒxQ}u`bO™ˆ…é‰×24§÷_r¿½ØõÌr`·÷µ(Ù|„�l‘õö} +ªûE̱Aˆ™Dâå0'.`Ñ9ïÉt¦(Á˘bÀPØ„¥Ðþùiãÿi§™…M�ã_²G²¾Z*Z˜S‹¤3j%ÁÙiÉV’"²r™®iØò=©»"—?ΔxûþSˆùð8 e�ûá—�ÈâÜ´!Òc�²#³¶‰jTRpþÌðk5‡Èÿ'*‘QMÄ-ù÷÷,æ ²h‘B¦=9s÷k±=cX¡ç&èÄ]•Úzü•0ét_5h«¾2¿`WÕ‰ ï…$!5s6Vø åTE +ð^Fš¢³gà·+ge3ªn³"ý-Ûþö:ׯAã}¿ïÛ#š™(œÌs�Ezö)¬A8@èN ÝøòÚdìÚjøÏ +ûγî'PN²c�ªà=�Mä:Ø„é3ÏRЏFƒ0CÒÏ\u*¼†žÉð V·üˆ`[#¸‘ +5Zžùåº ’¿!Ò¿Ó0·A’ç6KðÙ *­>oÔÔ)8'W€BçÓ~ù@   ++¶pe»:ÍÝ%‡ßËvU(Îë”h(W¼üƒ‘Fßwá%ó-^‰„Í0sC¿‹IÙğ଒èväCíRh„1œ�|Ôý!GÞãÍ[sS°Œd‘…a"jk-ªù7øNŸñK’ò3–埵ù smÞB÷+gOX5¤M¶‘ƒP‡òÖÆœ§Ü`Ûí+Õ\¡f4=NÖ`W“d¨¯Á•¢$/XÁžzc[1ÈDúÚ=IÛáZôd)>è!SÃ�4]€¿ýÃ1ÅÜKhkÉŽ¸êú—DPfÄ”oIôøæu §ræ×2¥É.ƒöèg!AˆÎ³¾f£!�ð;„.ËgÚÉìïó[È»É/xA¶ÎÓÊò¡—ÁåDGw™åÍð1ÔÕ-°¨gíL^SÙ4d ý¼5›Â}Öàê0ã{(™oÿ1w´8+¯Ó®æMç=„øc8cË•1?ò(+îGPœZñtQ­uT$'æ.¿¬u¤j ƒ¼‰5Æñp2#ÞN|?S«1—°ç�Oë 0„cïŒm|uÜh+‡‹�‡q,«Õðay<}’µ ?t£Û7²7È–ËÂ[òĤoÚ@ç¦2ÓÑ™ýï Õ�¤‰°ÌP_žáÒ$Ò�C(t¯Nl 0(µÁ¦©ÍšéEä^¼2ÈÚ¯PùɼCw†sR­4Öõ«±èÀ*øD+|'ǵ÷hÔ +b(;K“/ŠWla¬KÌ¡±eaÔÝð» Vù� +IcyÑŸÒÕ2Øl—QW;Ëëmöã¢s2ÿaÓ¶óÈ5Yc‚3[·Ýª6€F7#B)$ âÈi‡vÀMÜ�ZÙ4 '{¶1÷!·,˜=g_²¢gW)à–5S—ÀB^À³q� ¯P)_}X±0”‚–TiÕòX%�He„X+.,~ÕZîTÓñ0ãQìæƒÚ7ÏäáÙ-†IÔq‚¯S1YÌbTFº$("Žr¡äš +QÈœZjVBÈä<º­ "† ÅU¿Ôã Þ‰»-)FHŽÈf­Í‰F}ºg9–=  œ•ÊjLÓÛþFe±�UÅ´Â*©“’­a\½ Ù?���ˆð Yž8¹ôIl‡Ê`Æb»Ò~Û‰®†žÓ°’p3ý¨ØÊôÌ2 2ý)Z�9Ĺ œ0…¼ýÞ!¹Ä–¢,+o'«°M²”ï¤÷ËZ=»1ãiseÜA}[†¯D?‡©ß“Ȉ‡Á!¦‡ˆ�ÀxtÜßâþ§×X‡Š~4ãÁ³¹ïβ:ÀI‰U3-¸Ävûüò€Âë98Ì‹ÿ¯«òˆ½ÀÇ D8/,ä®FËV_é�wl*ŠƒŠ“öpm­rì¿7¹P—ÇL²`ÆNèI8tÔ :�±ÿ·ß)l²Œ”&‘ÄUÄ:½'ê[Ë\ÖôÂa…^Ëe}ÔS¹¥(1Ѝ + éûʹ½9Ïçò¥tæ,â·]ï¾XÆW*È 1°£‰’*Øe g®§Fü¬±Ž×½!64Ãþþ?9Ø"*ý�Z þ$G²õXŸÀxl–Ë5ŠÔ¯¨Ò.eø–¨Xa°öÖG"ZИ¾M]ZDùsÄô‹~j(�£ãÓöe%¶¦ˆ¢–xýt£KL„×į|ZL)àáWߨß³@´ †ÒÔ5 )ްÀ xoš 'öcŠöaÞƒÈè�'Ra•0Ž(6>œc ÖéO5 +‰BnæÕÂ4 ZáºqÁÞÿ §ÜkJŽU8ä¼g`B�Jó“¨ñ·Ìák'K7 ¬DäZÃÔ<¯`Š�û)Ë^IJ¨P¿A¢ÿ¯6Eižù{2�ÎÖ +SJÿ~#î ìˆå�á§=n0�b’§�ÖÃϺ!óã Mæn@gîÅv²–Sœ›¿*ìž"‘ͦ(L¬3âJžÕEĽ^ß.)(¿Ä³æ“xkLÞ£ [Úµ{ 2E|áä¿1c€^ŒâOH>ÑÜñzõ9Žé�:•öT§³î½´s§+Y—* ¢wÒ‚=Sbƒ:Rø±Ã)Wκ@€—V€”­clÃbçš”âk]�%4.=„5mÀ´Èüñú'3(Õ|Ô67ú‘áI` b L‡™JÜ�«aÐ[ð³Ó\áà¼éÃ9Ër{q|ý›÷¤Î 4Ô´wkˆ9[…aˆÒc›�bdcÐ!JÉ‹™1f–BA|ç§ÁRÅðÐÁ Œ¥�ÔNq(ÑsTvZóL7„’Y~òEÙÁ¤“6Yîð.¥�ÞÈ?üný³=¤ ï6d·e”ƒX5ñk)ší…Ò¯bQÛ骔Р­•–rÉK$} ßÖ²!=j=ëÂ@HŒ½j^ –„fê*£7Â$^mƒ#"ù�Rf|µ›SéáÁŽYp1À áªyBò¯F“n¤ÆŽ)L¿¶B, LÃ>ƒÎz¤\UäáEž*ƒš;!®ïsº œ[ɱ‰3ºº]=6^‚á�+î‚5Vô“tÚÝ#ù-{à2u[Å Ö•ÑC!6jy¡@d ÊÚœ sƒ|‡jL7?–lq  I~FÁ'jÆé a™A� ×8XÚ¿ Z°ÃÀœÞ(G×#½úkî9ÜügÎÜæ Ÿ?zóJ\ÂÖ®Z&Åjé©“{³¦˜ÆâÌU-àšcþdøÁã/Xá]Owu)9ÿÏ�Å#D†°’�>Ê0§Ò{´©Š 5ÙÅy¬;SÐ ˆµÎpË"Åì‡]`5µÒ [XFm'Êi%z}�  +›$ël•ˆó�+¬ "Ž – ±xz4í<dRwýÃKŸFX†bðÇOu¥ŽîûØ1£c%�76PX& Æ2 ØE¼0.0LXJR,Ô¾!…øÇJyBÞ—Õ¬uéÿbÿú…P²+ñ1®üÂàYË^xÆ*KœÝÏ +mC]`ÀöúCòcáÐ.CÁ`S¶_#¤Q׺b»PÕa,í{H³ûD57œ9zœÜfŸaß<Ÿö\ÅòÚ€ÓägkÑA¿÷¢º\¯&×å¦ ÕŠ’-QêŠô¾¬%ªéó‹3†!½Óšê mÿ,I©Þ35x-Ú*ªšOjúØÍÄ,ç ¥íHlé0ZÐhXVg_!/\GÜé�¯¾XZº¹ÎzÇre\NvŽ/Ì‚5¯1´4{ùŒÂ¶A‘*¬VódË(5'ò9ÉU:¨4ïD^Õß�Å8ÒG3ûsü´�µ)bΩ–$(�’Ãã-XIn�8ªç"°‰e7éÆÙ“G'Ì/=kÌɾžÆ[‹PƒI §‹ 2m1¤O×R9&+Ň ].äe g�R?Á–ËÅEýa³cS"'V&}ÑÀÛø‡�ƒGjê:q^rHz­¥Ú¤|™Žì‘˜{6—‘ÝÆ(ç{ƒ�ã!gɺÃ.öG…>ˆ‹Rêªo϶n1`,@<5±xÖm:ºNY�ð”•HsDÍ!E 2UZðÔ˜¤_±‹a�lg>¤ v«¸�e&Šáóë=B6dhÝpóZ$z{¡¦‰�½ÎÐ_¯åpEÃÞ@8œGŒ,ÊÐý›OIU‹²Bz"bCž2žSô€ÆgÞ±§{彿â�W$$ò îNM Û}Ê3O9ùØKSM ~áiÂÒ¸ MÖ@ÔZóóë…5–îÈv„1ŒâþdT%SïÔ£NÄÅû~Ï�É>Åñÿ¨Ý€}ª:ŠÀ®1°Jð0²}ª +´«-²Íuöa#_<¶§{AØÙ…‡Ô=b|ùU‰s»*DuåB²ú÷Ã`™AN¯¦=)¡JOFp\m-ìÆ[ rßèÊ’±Ö¹“·Z·³š79ñ·@ž3)AÝaB¸€Ù Ì�|=:Á÷c:ee˜¢‰;hò”-׿u×h0BXͼ®;½Ì¼K2Ö‰üóÉäÜ;VGf-ØUu8h‚‘…™QH‹�P§D®þž@¤Éþ¼"LTý÷{„{hû¢kÞM¹0^Å^]Æ¿’È}K‰è�…5ÝFPkŽ\ýt�¯[½<‚Ç�¸E .c]U+*Öª·Ga'� ôA¶îÚ|„–ÂÍk‹»)ö¢2ù4P¯›"”ßó¨ ¹ã*ôŒÃ�p6«C€½@Z‹]Ÿ×ˆq�6&¸X +D U  š éØJXÚ9pxÞìà´��}QuÉ«”#'ÿ¡lÌÛPÉ¢K÷ÉøHp˜ü™Õ{xïd‚È¡/ ñÁ�è+”ÞZ(!£¥,æTâû.ÃY,sHÓ�ǺsÁÄʧ–ϾµðTŒ„Äñúa èKfÝ€Le‚ +¸O¶Wõâ:q0­u QzÛ:Fž}VFÕôVóûßA ”÷&0Œèê¡äÍñÍÜÙ4Ÿ-"l«ƒ™üìÄþB,?ë�¶ŽZ•ÚcÃ\5ï±øëpÉ•‹êLQ…B�IÿRh+?ŠT ð=TErf`žÐ,A;’òˆ–¡;cüm¿OH7ÖÒ_Pýù³$â ä_êõ'™Ø³—º>éøˆ›P$› ?¼Ãâ®Î§ŸÇ +ÖÖ`.A�æ>NE„L@ÝF}„ek<†¥ó>ž…pÆJQ/7~Ûªö~(²çfí—¶Ík";3üKJ¸ÿüÁþƒu^)!(ÙÑ—d,,�}¬Ãs\°ó�3ËŒjÊØ 3ö �:RüW¹shÿ6¶dñ’]g]€;À*¬î­æíÝe%¡ÑDlÅz fšŒt'£NáÅu.ökÂéºÊœ®äts©vvê²�5jÔæJðiÚBN Ëäwk!½©8ÃÈ›¥Ž@XÝéuÞ$o^,£LJÓF[¢‡†„1=-ÝåéÃjZÀo�ž»ÜKM¡–À´k]›ýíeq ±™0Ôµ �’eÚÂSVT¦Š�¦,¹deNЊúÔå—×YL­\ßì•hŒ�Jî»®¨7HQÁuÝ{./ @È�®+äYuC<0%É)2¡0=L‹âk¦ä¹ê4jРɵI¶…„3ð4#läN`þñöp¥'$íðÎNSS–I*U +øšý›@�55Å4Ý\÷R ГÐOøK[”6.‘a^6¹#Y¸'5_Õ‰ª––‚ÙnG«›ß-´—üùX­õYÙí^ËhT–B”Þ –Ù£â‘mSQuæÞü9Úv–i-Ò$WųnÁ Êá¬óBvœli¸úfy©_v^ÆlñXª ƒJ¸*„¸©¦É€ë¡cI€¸MðW5µoë\üžB¥®˜Vš vãéô!à†Ã¡X/–Öħ&$/¾9þ.ýO!7â(©G/µ¢ÂöE¯ ¯ïç¶¡Ï‹j ¦r@§è7Ö±œ.)”™ùs±šPITxÃô]¦ŸL�1£ +Á€ ~á™Kjnªå¢öáÚÙbi¢y1»˜ðiØ¢ŽK€ çk�:À”B°ÊX$vÛƒ!SØð1ö%aÂõÅ7Þ(ƒÞxÎعÑËøüqoü Ù0‹]ÎymK^¦TÖ{ù^Keæå­¢mehpÁ!Ïà�𡿰kY¨v¨¬‰µ\ÔÆû×§’¯KO�@à–jQU¨=ô�vÓ²…d¢)ÍÎŒ¯�øˆ¶`@2ÿ#6†§È3z2v”‰‰”¦‰€4�ƽàäRyŠ`ð£ÛåvüM€õákÕžgùvºßT(®¬Ò­�ÛÁÇ9ˆ;@C7ÎU +2Öþ´Ò­ùÓ˜XôU��O”LæçßlÈ¥ö•ÊVÛÁGº¸l˜ ¯÷¤Eõñ£€ñ-_…¾‰.fÒeMgeB˜˜ HƒÀ«ÅS ¥lšLa?¢�½ª|#=yú3<3�Ì9£%&쀇d+_ЗÜÑG-Г‰a4YÑÒ{Úê%´Ì&„’o&p œ¶ç£ͪ|�}Ãûäï†Ìïî`RÀ`·9:êŒ^Ǽ=¨»ÅΤ`€ÐUÑ÷Pš¤ƒœU�‹õê­e^颯Ê.¯… + +a¯:¦Ã^dÓi†<.„�{D¼ABˬš¤¨Ötj�؃5©2ñzaˆäN¦¶„Ne+�Òƒê+jKug=æiqªgËhñISFí(¨xO™â|zaëÊÌ{/Ö7ÿÜØ´"• ýbJ;Ô‚R[)ÇŒh'Ó¨ëæN'öª�ò¤B_!äoÄúÏBpEg”ôó5X�Í›Â5/S—d �‡p§%�†“ݽÒ®þè¢8“2°U��{Vö$%´nnÀ‹8…™^†H݈Uüþ +ÔC<6:Ñq�‰=ÔBïîywËi¤l!Wê,hxGˆätñAG‡.flL>Nó×´æ4DwcygœMôcbÞôô1·2 +­næ +4f–öæå@CÊâCÌ$m]s¥‘e©ªîÛs‹Ð‹XΈS¸¡Ú╌y•.rž–<»•¿ÿÚzV‚DPÑt"žú.C ðBJ»guÁãäÇ ãƒœNÉÁÜu…Þ™ÎoHÓD˜àâåK%UtwåM_Ø÷ÍÒs�Ÿ±#!æ½9’Ç�¯6žEó™Èr)\ÄVï]å—ReßKôÝ(³ë 5�¶+×ÃÈi•J��\ß"½¹‘^ø#ëO%s:m®XUïFGïYÖº¡ô‘ÿ2‡j�’ذÃ)áþ0Tÿ†š¼й•6£|¨ü¨`ïuZ "ùïE å|ù›šnÁ„çIéf¦Äó'÷’ï¨Vx%‘t&Ò3Ún,iS2ëÌ@a[ F©n×\CŒ�ÒâsŠäHÇ'Ì/nç >Ìñ¤Sƒ¬S³[¹ NùgÃ&SÓÝ”¨5œüÏ1íí:jjœáMË—^™É¼M%,ÿNb‰m˦åvç«!Ï•†>N¬ÜÿñúucãJI¤eÝå?c«}Γ“?ñ¡º® ±÷1Ô(xfì�ðȪnŸç5Â÷3ä* b±ÖVañ ™3—ôSÏP”µÂ®·@¼î•”.kF72¢ûßqý§N2ñF%0ºZ +x[uÑÇgœ÷„´ª­‘EÛ` ¡!(×ÙÕrjo$Ãðøï°ø P\8¾Ea»Ø¢A(eºÆÅ+&`ô |õ¯2•ð/d”o?u~íGü~÷ÏÄö�ôÄ1 +A@# DÜ ÀÓB=õ¬ºº:dრ,õõ§bFÈ�[ð:¼~¼5øöô¢zY'…x¬ŸÔŒ¤ÏIº�¾©Óٜה&ïˆu n”•�Üx`±•©crÙ\£}½Ÿnu²JYP+„¥›æ12—É8Þߨ)M’ ÷réí\`z» !0ù¸¹¤¼¿”&O@)/^J“{˜ò)å[ )¿¹ïRš<ä,”‡@Þ®½íoå†Kiòóx½Ç¸b¿Ì£ï Loç"Oú AÕCˆû©îæH‡+v’¯ ,Æ`!� (ý êP'¿9î0=„©°�Þ>êt$è<™ÅÐ)Ån[lVädÕÁ·49ÈßÒä¼VXyoirCЕNqîw[L1ÃÀKì5Nir“Åú<-Sš²ž0ÎÍ•J­Ø½xr© ¯ü`�["ÿ‰ÚIì² fЯ‡òiÕˆ‚ú‘XS\Ã� ב‚êóô>-&Ö�^_&ל‚jnxzs['€òê�M¦�îh ŒîA£ã ˆݵ|;S„ÕÂ(PèÈG‰ƒ—HÇËàòéeß…YÕIÌòL…5(s{D;ZQ– L ˆ#è…ƒ™0a2P…zô2ÚÚDʤÞ�"Ó1ôpF:¹]eã¼}sІ{3 zRú¸¾�0®0—§7W!p6'iñÉP ;š~Í_> +{aí”'#BtÍõ»9çWÄ^Bpؾ9]jÅ‚ÎÖÌ\ñx¤,Û0]¢t“92“9˲Gj˜zíÖ9}säââö‰kÖöõ"iû `{º€18ùIA= h}zžSÞß0^ØiH¼Ðq¬àpaÄV·Pþv¦(èæXÃ{ÊÛ`ÿ¦%PLÆ�1ͲÀ²=Š®ñ,!T9£$Á é$fys�Â$ËÂÁí‘Ôªí©$mûYœSc}}’x¨9ÍÆ\Ä¢´nZíƒÛ�ƒÂîhø ;å]C”j²îÏ< Øâ%Áh›[Ií VqÇ©•p¬iu”Œê±yaRéNJ-‡’ÐÓR)2,¡Ï«Vbj +áe$Ê}šn5Á¨:‰ô!½Õ(Ìz{ZK ^ ì ›‹b3ÛHK7GÒB)Q˺BLÊ`‡¸Ž¼L®[Ïu•ä?J^>Öh =º$&å%„®å_4†A?G)C»i2³”æG M4‡ù±ÝC�Q¡÷;žWùf£ª‘ ÑÔ8 î²LFB“x—ÅC²›ÇxY<$"!‹Hü£[óÈ@¹,È—ÅÿËâ9îöÄYû}“_Ý#A_‡åÞGPõeý³us“Ò|éÆÃ[ºªðrÂã™ò¡&„@-« +�Ê_¤§äŒ"N~is¶A„@†Š”yô7Œˆý )Óc‹7ÙÜz{™›Û\ç…Ï‘É0ýLÖ�ñ +"#U…(ÚÍ™¥@Ñf>lJûA%X댰h»M<3œ´†›‰{º­ Øhl”Fýš’¦ú›ã*2þ´Åˆ",>etýµÖ¬ë”¾SšÜ¡LÞ;¥)vJ“p㺰> I*�êm5Ê!C ®ˆÝß¾,¿*ˆqs`hºÉ›€@8zK‰—n` �c4F.8y(ìºÜÙľ¹Zæ3yCSÄzþ~&Më?ü™Ü2äKxo—JDÌ?üD�Ì£qYar‹e î¢csŸœü™¤qSˆ‘E¡¤Ö“‰ŒÔïÒx¯* œüìeR_ÂäÿRySþRšÜ$”ŸÞ—&HPe'®ØkGáwÔ<ú`ë"&&)¦Êƒä§&®÷àdúV€¥°—b0nn¶¦Øû¬YEëó’Òä Ÿ|>¤"±¾[,ºNÕÃ�'ýiŠýâé�RlKOÅU˜¡ZŠ|?GixK“Ý[š<,�â~b;�±—¨Áƒ­yô?�é톌£ð qEF!ˆUÿZí{¼ÌÁ‹1Ð1²ùDúä€x½LíѶaaÄÍé6ÒòjCåý×e¡ ú{Ï‘§óD‰ºŒÓKá4rs›3iµC1¥ŽÈââ¶ð%uа õ+_”’ss!Y Zmz¦9‹t¤ Z®ßófsFNQø13uÕE`DŠ7uøú^NeýÍmîãØ\,BðšÕ‰‰Ó ùbê ¨öÀM3Ç„ÁëH™€›³4ä)QŒ„q5`Ö¯-,n,!ç d>Œ¢4Ðú×€7î`Xàœ�àC[ìUjQ±D ž]�6TdÀ<›[LþLÞ:¤�×⢥Úâ‚ßâP´ò¡iešìC#%¬-|I¡g§}(üI;ìïaª¾2Öå½eÕ¾ú&ÀÁGÁ ´8gÕ�ûæáŒ"² ƒéCnóÄgF¦U7TÍ´ 6«»¬ÔL°³mÎû›£½Þß\ÀàõŸQUú+“§7W f.\�ZŸ.À¡ó£†GÈ9 &1åÝP¨…®°ÆrÊ'òÔëŠ"fdÙú7w;©Ÿ7€êæVss2ÊIý¼k%7Ï,06.‚ô&Œ¤S Ca·,·ïr² •i×ÄϬÁú3+ÿw0ñ£ƒJò/#Óª«$0ÒiF¬4Δx¹T/FökÜïB·–H\ï7géô«»FüPZ‹sò7x±¾NˆÆEa¬;#`ë/±Ú©ïëo}�)Æ…ÅöÄBùs¦¡|%ña"цŸ�¢ +ƒÎ8©ål ï» FǹkX%=­l\�¶�ëkª-°ï£‚°3ñ›æ(d0¡”ØUšãâ¦ée!Q¤¾ƒ PoŽóÈ€:ìªN›5—ëHâúˆeÚ¹±P\èIx�'«na¨0è‚tãÎ2‘(bHV\è÷‚ÀÎÍù®H½B3|ñ0¤Š)]‰F)q„- o 5ؾ9T§3káX haù³éƒ1:�K¿v°�o‘n²’nk°¦)ht|@JXÜ /¸¹¿jÈ4ʳqCR(%‚¬ÛÅÃLð½�Cì®ÔæiØËåé2¦¸N€ PCHœO“²ì±`¥_ °,ç>áWŸaHݘ�0¢înò‰�BË‹¢£¤+Žåv¦’mÜ A’ ‡Q�àÌóæJZ×{!C¾V18yTÄz1�|?u½ë¸&GŒ4[còg²s‚(¶ Ò`ÐT¶¶#�ZAÑg�V¸¹ƒ0’›OÅQ ¹í‘@PuÇ„"T Óy<æVúXéo.•&OP©ì”&/Y¿tòXRè¶þ l­¤Ö­?ì”&ïú!_祺yßôCQg@ÞïT# +“ó 36¼1 Dß5¢¦4y¨Q-Þ×”&_ŠÈÛ��*oÊ÷¯Rá¥4 §¡&O}(o�}È€lùè™åeiYL¾¹šúæØ‚²�Jšhs ‰c15Ì{ä’Hô‰‘ÅõB3©õíßÎÐ'•eVœiψ\¨4Ö’ è�1̈ȈiÃH0ŽÊÅÃᦧKD$BB>"$ Åá�80‡Åq@�‚ ÆY:êäA¡"ð¿�ñß½4ld× kÏ­¢EœJ§Ñïµ¶l¯8æ±×¢‘Y´Ó}UM¬wÜ6;—öR«¦ž¯…Ø ³Pÿ¤{PŒzm좻–I¸R ìÜo×Â](±�Qä‹+¹X YtrYÜ«¤‘c–JÉJÀ=úz¢�E‰[q'`ß ÷^‹®p(®8Na¡S±EÇ)ÆâDRØRp7\¤Rëçg…úƒ¥z‘Ì�YИh7‰`eÈí-e""³¸?€Yèe¸™NÀ¨=óE_¹>‰ ©™ +u}õ´W­v_ÕÌ~ïÕ;¤¯ê.TV{åÜõ•èû(邃˜†”E-ÇOÂäG��5Š�+™ ê [ÆUÍmê‘lÀ'…-Mq-ׯ¢zïJ‰RÀ—pcš©œk®evuا “ìƒaž¼"==’¼«¾°®\ be¾i‡ H¸…<Ðxx!R¾Š%qE¼wá?•‰¿®aã¸ækÝên½Õ*`í§0 9rø^…hCò +V€5B� tCà¶øz“]“©P¶.{�x9h£-¦ëö3² ⬠ÿ(Aë,�?=#AFá_ðRòW¡´)Áßn¯ž~Ìf’ÐmJóûÇ%øKå,j$|BEpí²RÒ@O¢ +”[‘98!Üoƒ†e™^‚{!Î#ƽƒïOò#Ê\X«'U[/ô媳”vWÞ�°Ñ†=Ë„3u´¿)¦ÔD"rJ>ÜzA¬ß3ÚgåçÑv„>§’àer.ß Åc×™ ¶¯Úp+²o÷Ò·DTN÷Y)�—<)ÍÊÊ­·ey~NVB °uáìÁìa +ucMå,Xþ‰±ˆ‹Rß “?QyPÜõÌù‡xl³Õ‡¥›a~è W$8ª’3�’XÔ])5gZ®!DZ‚ì7ž9„H�ÿ rÈRñÐK‹jc=NG[¦ÌÜõ­ØþÄT‰…Ô*Se"¹£3·ÖÖÌn&0±­L –þ2h³5 4Ù¦‰Ì^ø>4,ÒæÚ«™´H2/&é{geW •ˆ’��î–ú+³!É i¡³O(‡‘WSz'Ѽ9_Ï]:4Ô¼ÆQãõä´Y64"ýgà•£¿ˆí “Rfí &7ˆ€2''À´­�6*Å8­²Ó9Å÷FˆF”9nè0HgÜ›*û+Î� M-Ao$Ÿbú9ÉQ{¤vù…zc\<› +A¶û˜ÑÃ7Aç±µÑÌÀI`Ò‚å5“Ðh1eiïySO‡bn«®!3ö·Œ[V-­)oÎN¹Ñ¡E“Æ. …Ó´¯•ˆÊ³è‡Øüç™qÇsKU·Þà¨3á~Œ�9œ†“±ÆQÜÛ�®£Aÿ…�˜·dÒÓeÌfâ÷À†f÷cˆ ¯­:�d !¯Mƒs?æËš{LDò‘ã xBXÃÙF¸È!±ýƒ8=2*MyîL¼fÞ ’»²dÚ£»‡-­…84¤„ÎÙ™Ò%)¥_M0ª®ÒÕúRò÷3Ú±G},…æDtÆ�ë„pа¤fHík¤2Ö¦CeÅàŒb°(ZzâÔœÑÖ/äòŒ·è.®{i‰¢ÕBŒPQÚœ-·‘,d[ÓËÕ^Vô5‚Ê·ÄÄYK¢þÁ™#å%…iwŒ…¡È‹<©0J-ll™÷¤êq‘@º‰<)$ÒGæ”ù¢Ûi³} +â:1# =Z—WT#2=7¡ÑGTapNì¯r�:‰;™>ZŸ0Bܶ3¬õåX�*%3"èLi3µôÑ·T�5±¿S;i¤è57Cä�Åý켪ѵJ¸'ªĵR +ðCÄRe·„åYØ!2�*Ù}Î’‘Ù24€}¹T åÇá€:‹J;'„G[\@|�`—LáŒzF5i<Ÿt???ãMz¯Ð@’Ú¿O1ýÛú‚`2¢hË2×:’eÿÀ‚èbÍ¢Q!ƒ€R§æ×Á: {©ôÞr7…émà±}–S÷÷Лrу�V+ ›ˆ¾°©Ô?Sî?7*á)#,‰P"ŠSä€ÚL¢fi‰‘1�À’ß/¬ÄJ‘–ÜÑîàF‘¡Jω†M‚#s?Ñ‚ †Z«¯§è÷þÉ—ß—ÈYS{E¦—ˆ?ƒ4 F‘±7 +Öûm” ë¾?@†$h!,·±wm¯Ê'¾ñY´Å›÷¥êáR¤…gGw¸\ SÀ[‘Ü=éfÿpÀꇼR>ámɼõƒ�¤-ö‡ …� oí�¤~äi�–IÈ1RfªLÄD]?XA7éÞƒwÞà€ŸK¢¥»Q–× ‰0Xì“%ˆoFÈ.÷ðnKevErwSàÍv$@qw¼`7ƒ»Tj¹ûÜñZÜM’*ÑXíÊ“Ð`*³7i)·½»²9õR(+ýÝFÎßìSno£ijÛɇw'ÛQÛ"€rν"iÛ”™ÈX·[¥ "”35çÝë-Ũ…ÍmÖØ ¹ S©mÛ%aâH›ý�Àª«ß³œeO^²áG{šA0rJÇ®59siÃ=Úý�µ�/^>Äß•rKÜ’í*åŸ%|l:hQ"!«"_ïã¶3‘t¤ÆI„H:½ì°ÍFX'@ó½ãCSqÙSûއD{Ež }=±Ldƒ­C¨­L<ËUÁ–S[” +Q>°�]Ã:s˜™†|.¶Ã~ŸãœÌÆëWkA<©˜'|påi�ý/ždÜCÿ5#ä²=ôG\zô¸€º™¸£ã÷«Ü±Á–k–‹\5ØC²æU"kwdlF‹aAð�,ägÕ #˜kQòŽúžoâ�Ž &«çðÞQކöÀ$<;´󼽚KýŒqÎå{xOz,É5þ;æ$�g¼æÈµZX$Õ¤�obµ”¹ ÚpK:úˆPÄ“ؘBªé[,µL#”¨s­Í£Ždðbšúø·¶Pëθ5U„œŽ£¡Pðqƒ‰’,¹Ï + QNa�Ø0¿Tx²dD­Y.Ç>-ÚÏ (r„ýø!)±À˜†à™®aÝäýcñÊ€¶ ùÆ¿õüˆvNF +›¤«”¤A¨¨»iêYK°û@Ԉε?~ûƒàDK8±ºÂŸìÒÎ÷Òoê&Fü-Æ×8IQ^¸AT>½ !ÜDL‹cÖZPÈ>‹õ~ÜŽ­LOÆúæRLžÔõ[þ¿‰º›˜��l‰Zý¾7~V{[±ÝXѬy©�Ð~2FT !Â�,_YtÆIèA­Ugw±ûsäš tc[oÑâY–ŸÄýð<¥g7–.Å­Åžµ˜ëûµÅ’üt5mhHy’•µ2O¥ˆÎþCȫਠu¼€½ñ�(4&óÏocc�;ÿ=.âÚ¨Bú§ ›Œöi­ÞÓ¼ö³¶‡XÁvAC… ÏØ¢þØ"2ï`�>ðøë„C†=²¬"O�:®yíynÕ”–ÉЬ̈́.ÔF#ãµ×Ú'§À5ƒ ²�×õ;€ZEnCÞ¦ +r‘‰Ñ¥ +Ò ñøcbKmZó;ø½NÇ;˜à„,ff[Ÿhä©NT�^As_FGRJèòäùˆ2ርXâ3Þt<ò™é’Üç‘�–. Éê·ga½ÂÂû …� ¯o–ûp˜„ЗªDAܹœ]†‡_%O_ú. ™ìM:ÚOJ𥍠á>xeˆ-d¾®j ^ + ËŒ­@%‚Ê“ùpE½"QÕª.ŒˆëÝØãbšEB¹ ð¥}kBfìÚW££ï;¼‘¹oõiéáÜ©x#ÑZ㪀‘3!‡ªxì0®õÙu-²4ˆÁȾ(`ôbÑúÏ¥%§¢Æ€5‰¤ +®wijaŠE¨å2ì@+s]C�§ëª]œ‚qÏd\!¾ 4‰F…�ö¸9¯£ýÍbÃx/*ÀÓZÉów蟩3zKgž+ÌFÉÌaÉ�;iÂr+Zæ?ˆö¶‡;åÓ×DäáœùvEÍý?‘aAXp˜ +l¤>ý^ÜØß{ÚD$ý3KIh¸ŒÛÓÓÂ3RC Û#ñ„ýÍ] Y#ªEòÙÛ$…Žø›dÄ�ô†´g­F|¸IãèÆ}á˜5Ni¹ÙLÎçßùø#·ó`Õ9½|W�NWý-�¨ /)g‹Æ™�hST«©~ +à¹qÂÇ(Û¦A¨»ÝºÛš»kZ[ Ò�Äü Ñä ¹T¢þ‡B²AíÕ¯ÇùZO ©þÛWÙ� #Âø lXËl¸’ë «�t¾®®+wiÍDó<^Hˆ;ˆBŠY"™&ÂY °fZ“Õb-(`ÝÆ& åÐä+Ë ¤¹8Ñ?Ûµ36´:!‘ÛWêŸæ ,‚´ŠÈ:0ÿÙn¦Œù‡i×"„Œþ›EÒµÒF ÅÔ4p <º�ŸÌ?Z¼5=_àáà�ê§©lJMSW@SÂÊ!ø—Ñ?@メÛ`KieE�}Ð?tFï¥u¾3ÿµ7ŸsÃè¦;a»Ñ +æßÚsÅ{ô@[¢ÜôÐ)’ý5 Øù¢ÐÍ¿<ŸGõ0‰Ì?R“¤¦v1&]M¾8’šlîkÄIJR£Bø³T8@àõ~ý˜ÿ¼z§8\ÍoÝ-ú_THvÛejçÄŸš�ê>º!€>þ˜QîÜ-BÛ�p$€Ñ?rµæß,§4ì£Ý÷aÁüŸ&Ĉ¦ç&ƒÏÍôÊÄ2.ÅñãôVYBÔT�qÃÁhîCýGÔWñÍ*k{Ù«ƒD˜RåÌÿ3f”ÄÎ0Ã�ŒÒ�¥Ê]¢R‘¦­[Î)# uK‚ +ÒHinÐɦG‘ÆsÓ herÆ9€1~={Tg£„û~£Þƒ�!s-nMõ—*5(À�’ªŽ^”šèù:]Mcb ?½ +ÛìöiŠ®]°R¹tWÒa&’«(Ý¥~öHõ&Á8wï*uFôÿ(;ÑtOhu�·+ór\ñùõâÞÙüsDj"Í^áßéÊ,¡ßy�ažˆQ¾:›ÿò§öpm£P|øUfuƒGÔ¶%¯QTIÙñ'3�ðÆÆÂó'ô—ÇÊ…Ï%9me¥ðAùg�hhm†*/¬ ÷µÃiŠ®ñ5ŠÆÇ&F.’*ÈÒ¾ÑÙeEÒSìÒüN81Çm9ÍT†â×ò\Øû|/Ì;“ $‚L¹oze ÔCò�SÄd¦œ[XQé�£¿Ÿx@\âÿFh* 3üÒÇ�cœÂ1îϨ/À1phfò™“ù†ù ƒÑ˜´÷p°"S%jÙR�cÚ7³p 'ß°_/ˆ8†Ò©Â�Zx¥ŽÉ„„cf¬âƒ�Ͳ»´Ã>Þ0¾o] ̼£QjtVf{u½Î�zzig4ÕyëÒ£jG¢z`ÞB“8ƒü‘÷EÓÏ·¦ò6ОÈÚ&‘Ç[‰åDGÐÖùCLtÝ"k»jf$ƒ¬yõ�' O�À÷x?ewÉΡId-¦ggY»¦Rlÿ-ö‰è:�nŒ‹JÁŸjà|ŽÃ6ëx9Ï“^mxÍ/_`!�ë5›I°Å¨ôïž ¯°üÓV=^‘ÐpÞ2d°áašŒ\gÖVÖÿ6í?+ìð0(?‚´á5ë `rZäËÿuϱж@ƒ®,§ëïƒI-¹ô°‹W#òÎýþk 2C@{§ÅÐ ùàV÷ôbQË•sïþÚ9ÐÞm7¢}}=l“3©eê„jô%éÌ¥’& Ic†ø¹:èˆqŠ­ ŠS àsß-O�µäS`¼¥kïßã«àËwØ›@a\or!—ç0jðô?Õk1�À8Àvjº 8L¿{Hâr¼"rÕ•À°ú݈9\&Y-*}L)“�ç£r:`ò¿OÇ��OŠ%B0Pîø®¼ïˆ`åó”5TjÝÄ\îD 𔀵¾Æ +Ü-_ò›ôe¸²ƒÓÐ.0µ–gCFËF+Áz SΓú\ï’AP°«qSø\—LG,¬m‹UYØó„. ,%Ôú§u*õ�…3fÑå�põ÷ôçFÊs]æB%Q_ý#Ã’ û>[á:zVÿ’2Þ*B{¸+äaõGùÞù`mÓ—ïÍê/dë¿lb42«¿Ôé¢R_zn²úc{6Æ‚ˆ*Œ€roGýÜ÷y;íí,Ì$òMÚl‡¨@jŒr]©£ú™¾E�gVë--X¾Ü}¢ß\£‹,DüIƒèþ�×ñÄs&3_±Ûâ[4 +çÁ(ÓÀÝUÐ èF¿/XÈq85DÒ�ë»Ôzãr>ã~8ÃeMJÁº•馴¥�ÚrÞ/‹úØé6Ç,éÌÌû%š�W韙K(buÖMÀ³ (Ñf–¼‘måÒÅG&¡|ìæäA©™ãMÞ>ÅÀAë;]Ÿ\{ä)M¬ø`’²ÖJ�$sPÖÇXúšbJvtV“ØÄ2ÙeìSvi’ñ@¹J…»-YN¤1ðݱkˆ4îó$zÅ4r-ÈŽ]·)*3ÚëEs´RÕ¥Õž5»$™”îTL(!/!¾Åmÿ—)ÿq�} þEØv}ðÓ_ ‡·Sìîá]\`ÅÍì�sýMðо.¢¸ ÝHñ ºŒÄ72¡“×4]ì’¾$vK7 š|$À>ªÊëÑ »Z?q1ÒZyÜ»¨Ÿbqn•sÑR`60ë€Ç¬÷m ø€þ¼ Q9,®û +eìÚäŒ+, DŒ·`Þü¶.0K e(a/¤p‚÷ewwï”ID(4lAáuxµx9Tìí’œy’ ç¼h¨¾ƒÆüœáŽFçeŠ08ù¨•8™—‚78‚'T¥ÙÅ@Æ!–j‡$6–0îÝÉ"$B`ŒÖj¼>ãe£êÜ‚Àâ:L@ÜQ¦á€ÏÉ…j‘ˆŸÄÈó“DÆ!œ$ 0ᤃÌÂcô…åÀsN +8=Üq${øŒMäò±Èeœš‹#$ŽÃ'üÌHî£Fü ˆã&ÌŽ“²)lŠá1¼Ù B™ãWà¢t#™ƒ¡Ë´TÎ`¸:Óa)tÇà47âëE…ŠÊ+¥OØðaZc>�DNÖR8ÉÉ•…ÂÉšì]Mcl ÏÈÄ>a$Ò05WcZ£‘ç|žÇ8…Ø÷q’“œä$…“œ„ˆ±¥‚ÓFÜ�‘ø=­ú1¦Ç»^²OÈPU<§� âc€B"øL<_Q¯Le#Å ¢aœ¦Ï8U)]‹ Æ9¹Â\ÙR¥tµËpúÒN�™À}P˜ÏÀ×p9i@�D0 µJ)j+OÕÂ~ˆžñ6ÕV¤ZØ•&À¯Œ�¨D@L,[ÁY|P§É0½jB¸T&׃‹ä +û¹(ÄL©ÂŽý&SÞ¤zP5IŽTa +ªMœ^†JqÒ€�1�ØXX“Áys œÌ +z9ÀŒ0 Iã Z>(Ï€°›¡0B€Ò´�Ba{uŒx¨ +{¦0�rìyŪI+L`E0¡°Ob(ì ÆaBPnSyÑl 1$îùã´dL¢Ð!Ù¬ÜNT¬¦•(0³bD¾!":AܱƒŠ¹DŽSh‹Ý@~BtÏe§·l@aÈD žðSÉ‘™¡IcÂNU¼à8B↔…�°´òŠËsR"Qq;Ð8W +Hcz0ä)̬ 4,Ï%l@aBʤr—änA€ÔÍ‘äN À«#¡f8MØèµÆ‰)` ^3zdÜÆ>aÕjLM Õ˜“Çóï ³‡�MÐd|SðuEæù5yÎÉÎã9Wñ"µÌIX{>áð¼ES(�ÔÑÝ€Wp,0î]â8¥X] Ij“/sIBŠbpS”a‡€žŠ8ˆ Œë€&';˜B+œ °h H¼VxQZ¡gHV“‚ŽA`?ßd6ËDèlFó„hÿ¨1:xõÑhÕt) QLx �‹Ò1Jsü§Ìù­Ï6ž +;R … ëPšþQ¢� 4ª”®R‰Œƒ*¥S�X8 ø1Ê@Œœìëš<—ŠVúG…¶l3�òRש”®«eÏ7�N¥tá„ÃóJÑÑ•É(¢î¬Á£t¨'¢!ªI+üÄjñ�D40�rÇ­aË&¯axt2”ômÑÐAN˜ +¤æN!”º° Èh¡È9¨…â­¹³Dj¡Ø`‘àp¡kH÷‚°šá�¾'3:ýP„ ”„)jœ‚ÒU$è4,©…bRk"ªC¨Q(êÓàj!'#§Õ3 ЄA"� ™ó1"öÑA|>³ üXЏ03F2>pQ¢Ð›• Ç h¨/jªÏøÐuƒØ0Á‘ÔJœä$쎑I§9 ™tXC_�¥˜ð\u Žä|èœt4b–_è hÍ0®–�*ž_ ¾•WH¦ÇÇð*�,\"i$ð#'K&Nzœääg‚€1`yˆ¶ñ‰�K¢¥œs™¨§å™„_D($€„Æ9»ÌoÀa2 EXͨ€9¥ +øA¦¹J³‹…ƒ©eE …Èiä^¢Š1’„(,€PÓ@TcuEÊg¬8BbÕH˜ 5Wl2{ð ˜/‘ y‚�ODÓB%=¶% +ÒÓy¾¬Øê6º°Uo9ŠÞvýê;¾õ3ØÛ_ìuûK˜g>æ�!+^WœÖYýþvýz~ü[žÿ–)Æ—âßf½xÞ<ëýíÎë[½¿ÑÙ]{Òš1xó¬u>u¯ÓÚõÞf¬,¥‘HX´<­\«Ø«¥f©‹…WªUkÁ6œÒÆ�¯cØ…Õ +‘f¬ŒU™¶æ™çÌšÎóf{km–ª `µB ‹&k½^‰¤•‚I9!+Û¤�iOìwÚ9éÌrEm[ŠÿæîîéÛgŠë¿oöÞ{ï}§”þ¥“ÚœóÅØzºçêÔkW·`­—Úy©ÍÓ·i³íüõëvëô9)¦x‚xVß~΋ÝÖLñÛévgÏÜ÷«uY±]l¯Ûl»»Ál›ÕºlÜÜ©»…d�[ç»åëiÀPý“ÞÁmÛ*”�.ãÖæÙRåÙzëºõVëç+!`—“»¤ ûÑÂj¡“p̧¡@JxÈ&öÀi å�JY‹„Œ·'쌼N7¡” —2‚xÈ(œ‹AÁì$^+lÔH­pspQz¬D4'K Ññ‹‚åxЫ?‹”L@„à1>jáï¢t¨ìczÈ0œö±öµg8˜^Iˆ“EN扊u1ãL:›AÍ'ÖÏkÅ®œ|+ÿnÁÊsY×I¨<¤�‡”Ù‹4Æ|šP,ЀÙG%2hÊt�QfÀ“qˆEBÆb£pá8Ž“,‰”,€ØmcÍb5k&±±$R2V-µ±HÑ$äŒõj¥f Ä^®YÌám­MÁ¿þÓÛŸfšA¯þ”â·Öæëà[Ïmqýjû‚Ïmâ‹qWÛ´‚ïØ+öYg[ú ¶s›µö½¸g7Xó½ׯo/ñ�›ÅÕëSü÷‚ߟÿÏùÞÓgvÜØWù­­ö_—b;±×kÿç�Óë¬>­7uÇ~±Ïì3ûÌþ=+þÏÞ·íõ¶û–ßÚyóܬÍÓ®ÔÚûþZÝK©Í™Îmf<±Ýü¶[Ÿ¸þ�`Ï[åÊúÄ¿M:¯Óû¬�_¾6ê÷:~Y±µÓm~­³ñ½ïÖ¯[Ÿ~±ÓÿL3��=ƒyZš)~Ÿ_›‚¹¾ÛÜ6_l냟�Ú¯~{bê`µ÷e÷Ä™Òi{‚øïo÷Î�z}ìõÞ{Áé÷·j1½Žéÿÿwýl½kµ3[°ë”•ífÚ7ûýüâ\g}Ç�§Å >þíû¾¥{n;=ƒÛ—žš¿ñß¾ükí°Q\±S<¿íÛ÷j[€ôôý9· +ú¥O-Ø>ólìØ¯¿ÿÏù߯TÅ,zTÚÞ K&ÃEL¤=%ÚE "%š±`³X¦ÿ’€¥j+–ˆ&WÍ}«OŒ»o­S®7�4?Å–RúòµÂÕ·™i_LçÍt‚ؾ6§¶âLÁÆ.W›×Üvn¼mßk~¯ös}ù²“:õŠë½s«î9¿­ô)Å´«¥ÿ½z6š±}œ»Z‹�zîkïuK3¦>oÓ¦ Å¿a:kc¿~ÁlçKÚ¸M) †Ó¨Ó¹¡–u ÍŒˆˆ$IÒ³hp@,�dÒXŠ€ç÷@‰…ay8 „qQ„A Æ0 ‚DŒ‚Ì0“¨´Ù8È}X A@‚l3´ í¢‰­LÃzƒ¹ dwjfpfÿ„½çñœË¾ÊznÝù ®G$ú�£GÝG4Ç­IçC4ªÈˆ†—ƒ*ãëø�³±´§3~yS&ÆëT¤0ó{=éFƲpvYÖZD³Œ6 ðGǙȽ|ÿä¶H½[ ŸÉåÎI–˜O uP#APU¤KÑ£!4|ŒkÿO !ßGÔJÞ›è´5Éñ‚#c“_0š¦Æ{Å #2¯ØçR åN�âb¢ø*¡ä�’Ü*ĘÈÛnžÁPLèǨÐõö±q’J³þÉqß0†^ԯ׊T�%èÝI'©f§×%uÉwÖ.Çä®ý³/Ù’'ñØ©¡I 2<ˆ©ãJ‰ËkV„JÚe¬&çþ«²óÉ †½¢ž�ì¤�ÓÉ`‘!I˜¡î¶×–—¥˜± »B�žgŒ0ôf©å‘ èžWl1K¨ê¯þxНT£ó 1±ـƜ“gp.3×`9ú‹ôô´¾+/¨à¦_�h]s rÞ‰Š)š“èBQ2$2ÍhŽ$H5�²¬×õ¥[ù†øéTíoÙ\ì­Ù5N4G¹š%“&*PF´Œ.| +™€|ÆW5ïÌØ¾,½Ë™^:F™~è÷—ŽÈ°^ é �ôÞÈÃr²7�8àþ$‚ž† Òâ 3üxº ­4´òï¹zßKþ«U¶ŠW´Œ2޵ɌB ¦Žun™Fê²bÒ ÓUI·^Hn*Å÷E6NU÷ê5®×}éš Ô%ÅMùÓª©vsæLó² ÙWa*êCFçô­wws¿l± J„øŸSÔ”§ïÖ+mÚÝótõM#1øÖY×YÎ2ò"\ñ©J. £šQ °‹¥.|ð¥Ø×Ænz‘cÂ&Y[è‚ÊÌ8äëuo«é&»ª¶{4ÖÛš1(é\iˆ—ËçpYܼYg„ÉÛ´ @ßßÔœJ}† T5ôôþcnÛ +ÿù"¤ÊÙ-ÈÍB~(ˆuWó×ïä'à9©p¤øùï¦V>RéŸÄ2‚©qô€; gd|rBîFáí1ò` ›¦p°Nß88�‘^åvƒ—4¿Ž ¡iØ0KJ´Œ^tŒ`Pf#Šp;~‰Ov)Õ†6¶0ì ³Æc |t‡·Æðó1š¸\âÖš·ØªŽÉE¾È€Âw–r®J1»±A Zäð È;h»‘óè/4šƒPö%š.�еW ’ÔšªhÒ;æœÞFÙú•<µ¤ˆ%ªT³�j8N52Õ&«Å¡ z¬„—©V•ï-ÍðÆÊº ·|ÅüÇ4TjŸb†e“ ·bþ[S¾¼�q�õ²ŒØU½pêwA8ç CSð^¸¢Yþ¯µò+-vüÞ2jó�¾rÀì•‹ WC‰UUJBcɾPo£j"BøµD/„Þ4+B'‡jŽûtÜz™Á¸×8nL"�‘–Ð8…!ñ®Ö¡eÔhL7Dœ€£-9lq“¯CþQÖ½�xQ÷�~;Nâ‘ÿö:V÷ÇéÁÜ‚á9–Ö›1ñ± TŠÑÔ$MË +"%F³pw(2`Þn£‘fv…ð:‚ޝÇRÃZE™�³°i£q+‰`…Ѷ4\ pÉz±ï39�Bk2m ¨qE„ECV4 F\0•ûÇ¿{vW~�y®Í_ïýÒË?4»0{iÎÁ8uƒ"aÒ¶vCc`oZ0!iœ(3å¥n_:úذ‹W`ÑJ“³Fƒ™Ðt�i …a7ÆŒ;´Mãw�A@�&ѬµQëÆÑMlüP±1SŠ,2/ ëliUÜBìÊ729ޱ;mÉŠ> iFL]³ +‚ ýqŒ=€„*ôdÙ­rÔpªÄOŠyäIÜ9qª‰X}¤…5ÛÎÍY(sÍÙÈ“¯Ì›Z-«ÝQŒï­Ê–SKƒX’ +kãRW€–�M eðL×ÒR/ãW- k™áLïÞ!’¡y– !å¯"W­âœ2cÐä‡ +ŒÊç +þr>6’­Ïó5Éó#©¹9y¯‘J•�6 µwžˆïd¾F²9ÿÆFºEFø‰bl¤H½‚Z|l$âk¤¯>qF’VÔÄFúx–Û¡ÜÆFˆ¯‘äb#5ªî¬^#e„I×öQYëRº‹LF‹Ô:WEú œR)>Yð| ìñ~&ÂG:„ú +©/A¨ü߇ zçŒ37jÄ#x^0Õ£aû|�ëù¤ÈÔÿõTY�½ŽEœß¥Ã-¸1ŠŽ–¾ïúŸ%»ÛƒÂ¯YÐüµ\•Å£hU¤ ,Zʰɢ={,òi–šSÕ`O)­]z€%!蘇$grvæ„&¹™|ºå +±²Ù3â­$œ�+W+nÖ˧H‘”²{øª<5*˜»QÃ*×ÑŠ©±D!ÇÁ±bNàTù>tR«){¹´à¡M_ŒÊ|iɦˆ+ÝTÝÜùg. 1]ðšjö˜3Æi/>eܸO�wÄû<Ë�_˜Zˆ k(µÁþSo¦°s²TX°t�A:{üu¤}×ÌcS¦6ÆÄKƒåJ˜P×NÀœA!Ú„ýôm¾qc‘r®ÍzÂ=K PÓï¤9Œé5k¬`ÍSz‹�Ð †àPÚ?ªßeè›Y©ÌSBwæZƒógḭ™ûÄ“„!<®8vn•Îgõ"ŒÒD(ËØîõg~‚š�×pH»H… ™˜)=&9±àï£ ÷þØóö‰R¨#€åêÑÅ+ײ\�.xtï@j*T\èš¾\™îhC¬ü¡iÊ00¡SKì„Ð\‚+ê¶4b6`­ùf!¤�(²wÈÂZ=!ËÍHÁ�ù)�2ˆÂS«2³pP_ðShüÊšÞ+2 .R÷�’ãš‚§B�p¼ŒjtÎXbEÂÕßôÐ'; þN£ß�IÞ’ôí Ê‘AWÿ`áý÷AAÉ9kWïdàߺBOb…D /ê}…@pð±Lê’œ@VSô÷qRQBË §%,¤Zh±­}\ꮺ(±v JUXåc2ô˜iAqÂj(Ή7Pµhп²8\Žƒ"`XøÈZY¨ªÔHý-¶ôsGoÐi ñcïn1ù¦¡nÌ”–L¤•iÍ�É?,~([0ÊDà¢~F�/–Å�¾�®záÖqújCH/î³Ìñ©kTþ$‹O7¾â¡‘¦ÂPDÃ"[�.Ù”~×S^” cHa˜°H;`È™ar¹7­Ý?Q —¡¡5QɬÁ1ûl6¢@ËM´‚ ¦‡Û8H¦7&˜¤G *È5Ù͘ƶ!€° R¢bbÎH¬Ü‰= vG��÷F|­°vÿ™ñúR_lî}"ÇGø¦ŒúÅ÷=UÄ­P}IþY\X©TŠXD ¶?ºh©Â·¸h÷?¬™%w“ÉâcpçgÆ€h'§êU¶úÒÄî–�;Ç‘žYlÒÔzÒf[%Š7i ’³â×à5tS%× ON‘ô€& ®ÜÃS…ûneøWCýƒ™"©R@þÉÈ)^{Ú_í¸(uÈÃÜÄE†º;ãô»i–�íãܺ£½¯Pô­ÕEÑ‘¤€Ñ ¯·Fj; +ñÐDDب~¬p.,.Èt³ýÆu™åÆmSXòÁ7|é†ËM +¨% †$ìN‘]Ñþn¼r£ÈÐ$\ô÷¿7—¸Ð³ù‡µy.ŸYnTªЫÃÇü™5Má׆mºQxã•›2™å&V‚h¤áŽ›„�@C@/”|Ô›Á( Ìð jw÷oË”sÃ{ÔzÀU›çn$ôïŠ{>ÊÍÌÕ4³VèÊ¡’î¶ó‹Nr� ÜÝRbÐÿ»(SïñÝ�!x{!r$ޝ%AEwoP±¶Æ·þ¨Iám Ý}¡á�°°Êfv3Ç]˜#a-w÷„݉uWãó £ï¹“Ïî>ÞùÚ§<ã&^€í§.¾ð_ãvUoê!ˆÖvHÀøa›EÎS PüÑR‘{<ýñ’ñ‚Hõˆ`Á�û辨€,#°�¬�V©P|®2Ðå­€õ—¬˜ëÚÆAPV‹ìuϪ%*”ÓQÀàOÞrÃîÀóO�óø?•`ÿ9¯×ýCŠ +Åq–üþÄ]€‹¦;ÄÃåá<¦û%BùÃÑ7ç9¡˜Oº&ñá¼2ªpÍZ†‡¢§/Eá Y|9� bëÒA¬]γϻãØýþ‡ß˜3¥DÍôˆyí/ÅZºû�”43ÏݘŽ\Š ¯ã~X{È’Ð �dܬåm@G<ÃRM×TÌAÈ_G˜pd.Dšß'L�\dn,Š„¥èy:ÒÞ\ +dÉ\4abÉÝ$¯¡fTÁê¬ +£»ñ‡@-7F5¬ÿÀ³Â[W<Ì`e(lÌMbÔ°à°¾:R0ÃA»©C +}Ÿž>/ExtGState<>/Font<>/ProcSet[/PDF/Text]>>/Subtype/Form>>stream +BT +/CS0 cs 0 0 0 scn +/GS0 gs +/T1_0 1 Tf +0 Tc 0 Tw 0 Ts 100 Tz 0 Tr 12 0 0 -12 -103.0967 -61.2773 Tm +[(T)7 (his is an A)11.9 (dobe\256 I)-10 (llustr)5.1 (a)4 (t)5.9 (or\256 F)25.9 (ile tha)4 (t w)4 (as)]TJ +0 -1.2 Td +[(sa)8 (v)10 (ed without PDF C)11 (on)4 (t)6 (en)4 (t)3 (.)]TJ +0 -1.2 Td +[(T)71 (o P)5 (lac)6 (e or open this \037le in other)]TJ +0 -1.2 Td +[(applica)4 (tions)10.9 (, it should be r)10 (e)-28 (-sa)8 (v)10 (ed fr)10 (om)]TJ +0 -1.2 Td +[(A)12 (dobe I)-10.1 (llustr)5 (a)4 (t)6 (or with the ")3 (C)3.1 (r)9.9 (ea)4 (t)6 (e PDF)]TJ +0 -1.2 Td +[(C)11 (ompa)4 (tible F)26 (ile" option tur)-4 (ned on. )41 (T)7 (his)]TJ +0 -1.2 Td +[(option is in the I)-10 (llustr)5 (a)4 (t)6 (or Na)4 (tiv)10 (e F)31 (or)-4 (ma)4.1 (t)]TJ +0 -1.2 Td +[(Options dialog bo)14 (x, which appears when)]TJ +0 -1.2 Td +[(sa)8 (ving an A)12 (dobe I)-10 (llustr)5 (a)4 (t)6.1 (or \037le using the)]TJ +0 -1.2 Td +[(S)-3 (a)8 (v)10 (e A)6 (s c)6.1 (ommand)10 (.)]TJ +ET + endstream endobj 18 0 obj <> endobj 19 0 obj <> endobj 20 0 obj <> endobj 21 0 obj <>stream +H‰|TiPY®¢éjP·f¥¢P»ÆªTpdP9ÅWdETñZmè–û°ÄFW”æ4tXD”E†C”KQð@Dn•ed•Ëa\'fg²ðaÌÌŸýµñ"22_¾ü2óˉc†ŽãóÜwoܹÞuéæ#ª@¹ÂKþõv¥Tˆ\5åäø/q~®!?�V ôOŸ>yŠ¡í øevõ—³ZL0ŽŽ:è!ÄûDÊV89ÙXMI»iéh%³Y¾|ù´´“¹(Â}•2ï#êHe¨Zææ®ŠWÉ#• +k™KHˆlB-S)ÕJ•fêò÷ªd�j™202@©’ɧ ¯R*d‘*¹B*W˧<ÿcú?©d�a2Kæ8eyG +—j™‚MK}üÑRŽºv±GLueFîÌsd�™ Çs4 ¶>ÎÉùN(Ì1±›¯éŸó3߈ž§Ñ'k›’10çç²¶6®íQÙk0�ðÏ­Íl³—M1šÍ¸y&þu'7ìAŸ¾T–QÇ€çëÅÈ6™C››£-�Á©Q,yTh÷~/Dö À¢ç¦ÃpÎö¢³ Å}è7‚D¹5ÑÓÞ ú³Ãð„ƒ„„e)@XkøÇ¿ +,=„±AjؘB•„ +Cý‹“¯ØôÔ²uukê×΃ 4&™ppBå•k–ætv«OîRô옇Bà½Øê³AÂ]»¯xÇ»¾ÁOÂÄÑF4Oø5Aè ÌF,¸ÁZX_€ö¡™°yr§þLâná‰}íŒ(Ä¢ùÎ@ÃB°ì…y°�#‹…îr»¡xŠ6Hü½ÁKÄÉüÂä+Ì«g—nreåy·Z¥£}ÇVøyæÛ3ëw$ž�sÞtÆ…âôJ懶ƒ6Ö»ÍÂÔi§c82!¡—¿ÕmÒ2¡£Tu)­®è‰igÀ¶ÿõ϶5#¢–³®ð-i“Þ,o~ÛZ_ÀRu…Â|ûsC÷ä¸1hŽ�³™Uçv0Ppo*ü=¥ví³Ùº=¿PÁ’‰5üøSŽˆà é?¾O'O¹€œ 5ºö ¤´ÙnR>òW”–w2M,ÕWUKÁ]2îÿYnÙ{Ø�Íß&.(«Ï®c>œwõã$hkª¹2–R ëÝ~ìj*«,bÓ J{f‹ø8ÒTÃq}BÒqÖ7ÎÏÏ“ùVYtï=à¹?è9ò‰®[©á�»!8Ú¤o*F(-,2¥2¾§-Ý `€� ©ÿF³gïŸN÷ì&…õ{õAì(£³Ó3Óαy-U™÷�+¿â¨$Ú±v™ÇîêÖXVNþ¼”Ü‘ÐÍcÝx娈·xIëTAÇö0K]»ÀðãÃAÝ*ÕÅ_äò;Å™êýY»d³ah ²³„Åí·s2/qd«®G¡á×tMU\9 +Å#To1±—FÕzÉõ¨=å. šaa‰ìS9dñÚŒTÝ-bÓ„J3ÅDRirC�””ØNfÐqÉñIñ,Õà~ÔGáÅXýåÅ�¿ÞýþÍ“[û½³¸t]ªV'%õ �°º ÌÛðÊ—pþ•ˆ÷ �ú·´ì<é¨8dƒØ# ZŒ�.�%ýwr²KXm›8.H©ódlÿÔ3õ\jÝzæúà(ó¡ÐÎ;™#‘¿ðì;`U>8,gSÞŒ;�ñÉÓ¿?yv9ïdb›Ù)>q&„ñ=p,8ˆóUh7o�FnØØ9i/![�² Ì:`¢K7MÂåaê¿‚�¡©ñSëÄzâ†f­°fD2k´I‡Á¬gMuE\š�H÷}+¶îÉËf£6Š_wŸiíÌ®­çê« :A$…2 õâAÚõ´rv9*¡µúØ“ZÖ;fo �@ÐÀG�ƒáÞ—�kä»sØŒøÔcZ)Ù$ô³ªO˜Þ7ºQŸéÔ¹©Y§3Ù[9u7î1ÿ*vwæPɈ°?àó›‚B´‡ÙmTTœÊHͶÂ*Pb¢ÜäàKF¨w`oÊÇØNÆH¨�µŽtp¤:$´@]R^Px5™+ѱ$Ê–×Þ©¼SI'ö +8ðRÀù¦W`¦oB‡ dˆzÑg:$t!íÂé,¶6ûFm3ÓQ潆C5CÐ$ÉIÏÉÈdk.\¿ÖÈô”y[s¨h*%°ÚªÚcp|T­�Ñh#ŒôÄ=Í™¬ú˜¦®8š¦{· ¬Yx6«}KŸ Ë\6`,ÌÁºŠ›¢tk¨‡í²-›ãS±ô•Ò£-ýàcÀkm«(]Ǧ¨H¦€‡à¤dŽÎAX@h¦Õ‘Í„˜û–ë{ÏŒdÿÞäžs'¿sîÉíßK¤”‘f€›æ±À¡¶©r×Eàh9 ð9N„A޳¹S:|b(0Áа�álà;qý™i *¥€5.`Û×q~0™ AÖ!ðÊ|ø=S/B’ãÑ[(áÑkðy Ÿ›‡{à+ �IZöŠÖqo#ž<=eÛ‡ �ïý¹Æš½_÷ÿÌðoÖ]½By¢趃üžë¥·/à ¸5ù/�®ø,ûS’}Æï¦E,Ø ì¹HV +D´Ãéè”â«AÏõË“Dd4õUÄÏy7Q®˜¢HK§…vJ„è=vê¤EÃño9&|*ÄÆo';´ËNKƒôà•›ÝÞdi'q碟²×7ØŒD¶Rµïix�ù!'_¡-O!n1Nsc�-AÉ]þ¥3¢ã½Ãú1 +‚“wg‹RÎ’òîo¿äBÏw׆ÎSµ'¥§oaî²bW'ÏÚ~ �¿r„œ®ô=,QùEVQ�×Sή‡Ì8 á /²Ì�0«"%˼ƒ<E{±¯\ZÙuN zý-mÒÄÌ­ù(‘§í�$¡ ÆÎ XH +„W !·–±·ÃPƇ˜ +ÖfÉhPfªQ/ý˜R©²‰´Ò›÷IØ•Œ.ÐŽ{‡[½t†ËMK:6Ì}Mƒ#á1k8ó)”“)ÄËœ,”?Ñ`K* ö:H@> ¿HÕ¨ ê(30öëû©Áxè a�u ÞxÀþÄÞâ½ÜÕ—Ù« `jÙZá¹vb„ž`« ­på{r~E”xÀ$õM°™(…þ£ú¼Íf`®­ž�ÁFñN�ެÔ!ê��;¯Ñ�ÔŒgÁ&±PýÔ„¼¯™ý|7G“ŠŒH†¬Ø*[†xÞÏ7îžÍvà)q—�,ýñĶý9ز£ÇâÁž—¨rª‚*a!)¿ÎWíß½&¦@xòýÃþ«çx��Ÿú~ Œ𡜱ˆ¼ «£¾ÚA¶hº–¯¢ÀÔjîhô5ž®£©Ö(»àT«‹�Ywƒ³®MÚV[Ór„(«²˜u$JíØà‘­¹ÍÖAtwuZmCY |ð Q‚,#)ñNƃ‡¡Pä^F(žüü™ÐXvÆ.å�];s&B³ã7B¤ðÔÿ*×3XùdÛ +£A½Ý‚9ÿtärŸ^×%õÍ`Þ +¥«�@/ÈRÑ&²ü¿®¥ išî¦ú„ž£Ò.UQL´5fã̆ðÆps̳P½‰±Šþ` ©9# endstream endobj 17 0 obj <> endobj 16 0 obj [/ICCBased 22 0 R] endobj 22 0 obj <>stream +H‰œ–yTSwÇoÉž�•°Ãc [€°�5la‘QIBHØADED„ª•2ÖmtFOE�.®c­Ö}êÒõ0êè8´׎�8G�Ng¦Óïï÷9÷wïïÝß½÷�ó '¥ªµÕ0 �Ö ÏJŒÅb¤  + 2y­.-;!à’ÆK°ZÜ ü‹ž^�i½"LÊÀ0ðÿ‰-×é @8(”µrœ;q®ª7èLöœy¥•&†Qëñq¶4±jž½ç|æ9ÚÄ +�V�³)g�B£0ñiœWו8#©8wÕ©•õ8_Å٥ʨQãüÜ«QÊj@é&»A)/ÇÙgº>'K‚óÈtÕ;\ú” Ó¥$ÕºF½ZUnÀÜå˜(4TŒ%)ë«”ƒ0C&¯”阤Z£“i˜¿óœ8¦Úbx‘ƒE¡ÁÁBÑ;…ú¯›¿P¦ÞÎӓ̹žAü om?çW= +€x¯Íú·¶Ò-Œ¯Àòæ[›Ëû0ñ¾¾øÎ}ø¦y)7ta¾¾õõõ>j¥ÜÇTÐ7úŸ¿@ï¼ÏÇtÜ›ò`qÊ2™±Ê€™ê&¯®ª6ê±Z�L®Ä„?â_øóyxg)Ë”z¥�ÈçL­UáíÖ*ÔuµSkÿSeØO4?׸¸c¯¯Ø°.òò· åÒR´ ß�Þô-•’2ð5ßáÞüÜÏ ú÷Sá>Ó£V­š‹“då`r£¾n~ÏôY &à+`œ�;ÂA4ˆÉ 䀰ÈA9Ð=¨- t�°lÃ`;»Á~pŒƒ�Á ðGp| ®�[`Lƒ‡`<¯ "A ˆ YA�+äùCb(ЇR¡,¨*�T�2B-Ð +¨ꇆ¡Ðnè÷ÐQètº}MA ï —0Óal»Á¾°Ž�Sàx ¬‚kà&¸^Á£ð>ø0|>_ƒ'á‡ð,ÂG!"F$H:Rˆ”!z¤éF‘Qd?r 9‹\A&‘GÈ ”ˆrQ ¢áhš‹ÊÑ´íE‡Ñ]èaô4z�BgÐ×Á–àE#H ‹*B=¡‹0HØIøˆp†p�0MxJ$ùD1„˜D, V›‰½Ä­ÄÄãÄKÄ»ÄY‰dEò"E�ÒI2’�ÔEÚBÚGúŒt™4MzN¦‘Èþär!YKî ’÷�?%_&ß#¿¢°(®”0J:EAi¤ôQÆ(Ç()Ó”WT6U@� æP+¨íÔ!ê~êêmê�æD ¥eÒÔ´å´!ÚïhŸÓ¦h/èº']B/¢éëèÒ�Ó¿¢?a0nŒhF!ÃÀXÇØÍ8ÅøšñÜŒkæc&5S˜µ™�˜6»lö˜Iaº2c˜K™MÌAæ!æEæ#…寒°d¬VÖë(ëk–Íe‹Øél »—½‡}Ž}ŸCâ¸qâ9 +N'çÎ)Î].ÂuæJ¸rî +î÷ wšGä xR^¯‡÷[ÞoÆœchžgÞ`>bþ‰ù$á»ñ¥ü*~ÿ ÿ:ÿ¥…�EŒ…Òb�Å~‹ËÏ,m,£-•–Ý–,¯Y¾´Â¬â­*­6X�[ݱF­=­3­ë­·YŸ±~dó ·‘ÛtÛ´¹i ÛzÚfÙ6Û~`{ÁvÖÎÞ.ÑNg·Åî”Ý#{¾}´}…ý€ý§ö¸‘j‡‡ÏþŠ™c1X6„�Æfm“�Ž;'_9 œr�:œ8Ýq¦:‹�ËœœO:ϸ8¸¤¹´¸ìu¹éJq»–»nv=ëúÌMà–ï¶ÊmÜí¾ÀR 4 ö +n»3Ü£ÜkÜGݯz=Ä•[=¾ô„=ƒ<Ë=GTB(É/ÙSòƒ,]6*›-•–¾W:#—È7Ë*¢ŠÊe¿ò^YDYÙ}U„j£êAyTù`ù#µD=¬þ¶"©b{ųÊôÊ+¬Ê¯: !kJ4Gµm¥ötµ}uCõ%�—®K7YV³©fFŸ¢ßY Õ.©=bàá?SŒîƕƩºÈº‘ºçõyõ‡Ø Ú† �ž�kï5%4ý¦m–7Ÿlqlio™Z³lG+ÔZÚz²Í¹­³mzyâò]íÔöÊö?uøuôw|¿"űN»Îå�wW&®ÜÛe֥ﺱ*|ÕöÕèjõê‰5k¶¬yÝ­èþ¢Ç¯g°ç‡^yïkEk‡Öþ¸®lÝD_pß¶õÄõÚõ×7DmØÕÏîoê¿»1mãál {àûMśΠnßLÝlÜ<9”úO¤[þ˜¸™$™�™üšhšÕ›B›¯œœ‰œ÷�d�Òž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª�««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½�¾ +¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿ ÷„óû endstream endobj 12 0 obj [/ICCBased 22 0 R] endobj 23 0 obj <> endobj xref +0 24 +0000000000 65535 f +0000000016 00000 n +0000000076 00000 n +0000379221 00000 n +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000379272 00000 n +0000379611 00000 n +0000379814 00000 n +0000425416 00000 n +0000379875 00000 n +0000433572 00000 n +0000379949 00000 n +0000380090 00000 n +0000381692 00000 n +0000430889 00000 n +0000430776 00000 n +0000426600 00000 n +0000427115 00000 n +0000427199 00000 n +0000427581 00000 n +0000430924 00000 n +0000433607 00000 n +trailer <]>> startxref 433795 %%EOF \ No newline at end of file diff --git a/assets/branding/feature.png b/assets/branding/feature.png index 321a65c..079b7d0 100644 Binary files a/assets/branding/feature.png and b/assets/branding/feature.png differ diff --git a/assets/branding/feature.svg b/assets/branding/feature.svg index 15b6949..a3e79c3 100644 --- a/assets/branding/feature.svg +++ b/assets/branding/feature.svg @@ -1,28 +1,22 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - + + + + + + + + diff --git a/assets/branding/icon-round.png b/assets/branding/icon-round.png index c691353..2b88127 100644 Binary files a/assets/branding/icon-round.png and b/assets/branding/icon-round.png differ diff --git a/assets/branding/icon-round.svg b/assets/branding/icon-round.svg index 2d0a3bd..96c6453 100644 --- a/assets/branding/icon-round.svg +++ b/assets/branding/icon-round.svg @@ -1,20 +1,20 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/assets/branding/icon.png b/assets/branding/icon.png index 7fc0b0b..eb1c73d 100644 Binary files a/assets/branding/icon.png and b/assets/branding/icon.png differ diff --git a/assets/branding/icon.svg b/assets/branding/icon.svg index bb6a00d..f8370b6 100644 --- a/assets/branding/icon.svg +++ b/assets/branding/icon.svg @@ -1,17 +1,12 @@ - - - - - - - - - - - - - - - + + + + + + + + + + diff --git a/assets/branding/status-icon.png b/assets/branding/status-icon.png index 3e885ec..478fbc1 100644 Binary files a/assets/branding/status-icon.png and b/assets/branding/status-icon.png differ diff --git a/assets/branding/status-icon.svg b/assets/branding/status-icon.svg index 7b6e403..9ea3965 100644 --- a/assets/branding/status-icon.svg +++ b/assets/branding/status-icon.svg @@ -1,3 +1,6 @@ - - + + + + + diff --git a/build.gradle.kts b/build.gradle.kts index 022c030..fe5855b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,3 +3,16 @@ plugins { id("org.jetbrains.kotlin.android") version "2.0.21" apply false id("org.jetbrains.kotlin.plugin.compose") version "2.0.21" apply false } + +// Reuse Android Studio's SVG importer from the already-installed Android Gradle Plugin. +tasks.register("convertBrandingVectors") { + doLast { + val directory = file(providers.gradleProperty("brandingVectorDir").get()) + directory.listFiles { file -> file.extension == "svg" }!!.sortedBy { it.name }.forEach { svg -> + val output = java.io.ByteArrayOutputStream() + val errors = com.android.ide.common.vectordrawable.Svg2Vector.parseSvgToXml(svg.toPath(), output) + check(errors.isBlank()) { "Cannot convert ${svg.name}:\n$errors" } + directory.resolve(svg.nameWithoutExtension + ".xml").writeBytes(output.toByteArray()) + } + } +} diff --git a/fastlane/README.md b/fastlane/README.md index 038e950..c788bce 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -55,6 +55,14 @@ Run instrumentation tests on a disposable emulator (never a personal device) Build, sign and verify release APK and checksums +### android virus_total + +```sh +[bundle exec] fastlane android virus_total +``` + +Scan signed release APKs with VirusTotal before publication + ### android release_prepare ```sh diff --git a/fastlane/metadata/android/en-US/images/featureGraphic.png b/fastlane/metadata/android/en-US/images/featureGraphic.png index 1b64f8c..ec6f46b 100644 Binary files a/fastlane/metadata/android/en-US/images/featureGraphic.png and b/fastlane/metadata/android/en-US/images/featureGraphic.png differ diff --git a/fastlane/metadata/android/en-US/images/icon.png b/fastlane/metadata/android/en-US/images/icon.png index 48e4684..e1675b8 100644 Binary files a/fastlane/metadata/android/en-US/images/icon.png and b/fastlane/metadata/android/en-US/images/icon.png differ diff --git a/scripts/export-branding.applescript b/scripts/export-branding.applescript new file mode 100644 index 0000000..12c8832 --- /dev/null +++ b/scripts/export-branding.applescript @@ -0,0 +1,10 @@ +on run argv + set scriptFile to POSIX file (item 1 of argv) as alias + set sourceFile to item 2 of argv + set outputDirectory to item 3 of argv + with timeout of 600 seconds + tell application "Adobe Illustrator" + return do javascript scriptFile with arguments {sourceFile, outputDirectory} + end tell + end timeout +end run diff --git a/scripts/export-branding.jsx b/scripts/export-branding.jsx new file mode 100644 index 0000000..5d9ff7c --- /dev/null +++ b/scripts/export-branding.jsx @@ -0,0 +1,66 @@ +// Called by generate-branding.py through Illustrator's AppleScript interface. +(function (sourcePath, outputPath) { + var previousInteraction = app.userInteractionLevel; + var source = null; + var output = null; + var sizes = {icon: [512, 512], 'icon-round': [512, 512], + 'status-icon': [24, 24], feature: [1024, 500]}; + try { + app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS; + source = app.open(new File(sourcePath)); + // Validate the whole document before exporting any artboards. + var indices = {}; + for (var i = 0; i < source.artboards.length; i++) { + var board = source.artboards[i]; + if (!sizes.hasOwnProperty(board.name)) continue; + if (indices.hasOwnProperty(board.name)) throw new Error('Duplicate artboard: ' + board.name); + var bounds = board.artboardRect; + var size = sizes[board.name]; + if (Math.abs(bounds[2] - bounds[0] - size[0]) > 0.01 || + Math.abs(bounds[1] - bounds[3] - size[1]) > 0.01) { + throw new Error(board.name + ' must be ' + size[0] + 'x' + size[1]); + } + indices[board.name] = i; + } + var options = new ExportOptionsSVG(); + options.coordinatePrecision = 7; + options.cssProperties = SVGCSSPropertyLocation.PRESENTATIONATTRIBUTES; + options.fontType = SVGFontType.OUTLINEFONT; + options.documentEncoding = SVGDocumentEncoding.UTF8; + options.preserveEditability = false; + options.saveMultipleArtboards = true; + options.artboardRange = '1'; + for (var name in sizes) { + if (!sizes.hasOwnProperty(name)) continue; + if (!indices.hasOwnProperty(name)) throw new Error('Missing artboard: ' + name); + source.activate(); + source.selection = null; + source.artboards.setActiveArtboardIndex(indices[name]); + source.selectObjectsOnActiveArtboard(); + var selected = source.selection; + if (!selected.length) throw new Error('Empty artboard: ' + name); + var rect = source.artboards[indices[name]].artboardRect; + output = app.documents.add(DocumentColorSpace.RGB, sizes[name][0], sizes[name][1]); + var origin = output.artboards[0].artboardRect; + for (var j = 0; j < selected.length; j++) { + var before = selected[j].geometricBounds; + var item = selected[j].duplicate(output.layers[0], ElementPlacement.PLACEATEND); + var after = item.geometricBounds; + item.translate(origin[0] + before[0] - rect[0] - after[0], + origin[1] + before[1] - rect[1] - after[1]); + } + // Expand only export copies; the editable source keeps its shared symbols. + while (output.symbolItems.length) output.symbolItems[0].breakLink(); + var folder = new Folder(outputPath + '/' + name); + if (!folder.create() && !folder.exists) throw new Error('Cannot create ' + folder.fsName); + output.exportFile(new File(folder.fsName + '/export'), ExportType.SVG, options); + output.close(SaveOptions.DONOTSAVECHANGES); + output = null; + } + return 'Exported four branding artboards'; + } finally { + if (output) output.close(SaveOptions.DONOTSAVECHANGES); + if (source) source.close(SaveOptions.DONOTSAVECHANGES); + app.userInteractionLevel = previousInteraction; + } +}(arguments[0], arguments[1])); diff --git a/scripts/generate-branding.py b/scripts/generate-branding.py old mode 100644 new mode 100755 index ad84d4e..1656ef2 --- a/scripts/generate-branding.py +++ b/scripts/generate-branding.py @@ -1,6 +1,9 @@ -"""Export the separate branding SVG sources to PNG and Android vector resources.""" +#!/usr/bin/env python3 +"""Export branding.ai artboards to SVG, PNG and Android vector resources.""" import argparse +import copy +import math import re import shutil import subprocess @@ -13,137 +16,220 @@ ANDROID = 'http://schemas.android.com/apk/res/android' AAPT = 'http://schemas.android.com/aapt' NS = {'s': 'http://www.w3.org/2000/svg'} +DRAWABLES = { + 'path', + 'rect', + 'circle', + 'ellipse', + 'polygon', + 'polyline', + 'line', + 'text', + 'image', + 'use', +} -def color(value): - if value == 'none': - return '#00000000' - if value == 'white': - return '#FFFFFFFF' - if re.fullmatch(r'#[0-9a-fA-F]{3}', value): - return '#FF' + ''.join(c * 2 for c in value[1:]) - if re.fullmatch(r'#[0-9a-fA-F]{6}', value): - return '#FF' + value[1:] - raise ValueError(f'Unsupported SVG color: {value}') - - -def vector(size, viewport): - return ET.Element( - 'vector', - { - f'{{{ANDROID}}}width': f'{size}dp', - f'{{{ANDROID}}}height': f'{size}dp', - f'{{{ANDROID}}}viewportWidth': str(viewport), - f'{{{ANDROID}}}viewportHeight': str(viewport), - }, - ) +def drawable_elements(root): + for element in root: + name = element.tag.rsplit('}', 1)[-1] + if name in ('defs', 'clipPath', 'mask', 'symbol'): + continue + if name in DRAWABLES: + yield element + else: + yield from drawable_elements(element) -def paths(parent, elements): - attributes = { - 'd': 'pathData', - 'fill': 'fillColor', - 'fill-rule': 'fillType', - 'stroke': 'strokeColor', - 'stroke-width': 'strokeWidth', - 'stroke-linecap': 'strokeLineCap', - 'stroke-linejoin': 'strokeLineJoin', - } - # ponytail: handles our path-only artwork; reject new SVG features rather than silently dropping them. - for source in elements: - if ( - source.tag != f"{{{NS['s']}}}path" - or source.attrib.keys() - attributes.keys() - ): +def android_sources(branding=BRANDING): + """Split the first painted shape (background) from the remaining artwork.""" + source = ET.parse(branding / 'icon.svg').getroot() + box = tuple(map(float, source.attrib['viewBox'].replace(',', ' ').split())) + if ( + len(box) != 4 + or not all(map(math.isfinite, box)) + or box[2] <= 0 + or box[2] != box[3] + ): + raise ValueError('icon.svg must have a positive square viewBox') + x, y, width, height = box + # Android expands adaptive layers by 1.5; the SVG artboard must remain the visible center. + expanded = (x - width / 4, y - height / 4, width * 1.5, height * 1.5) + result = {} + for background, name in [ + (False, 'ic_launcher_foreground'), + (True, 'ic_launcher_background'), + ]: + root = copy.deepcopy(source) + shapes = list(drawable_elements(root)) + if len(shapes) < 2: raise ValueError( - 'Android export supports only paths with fill/stroke attributes' + 'icon.svg needs a background shape followed by foreground artwork' ) - target = ET.SubElement(parent, 'path') - target.set(f'{{{ANDROID}}}fillColor', '#FF000000') - for key, value in source.attrib.items(): - if key in ('fill', 'stroke'): - value = color(value) - elif key == 'fill-rule': - value = {'evenodd': 'evenOdd', 'nonzero': 'nonZero'}[value] - target.set(f'{{{ANDROID}}}{attributes[key]}', value) - - -def android_vectors(): - square_icon = ET.parse(BRANDING / 'icon.svg').getroot() - artwork = square_icon.find('s:g', NS) - transform = re.fullmatch( - r'translate\(([-\d.]+) ([-\d.]+)\) scale\(([-\d.]+)\)', - artwork.attrib['transform'], - ) - if transform is None: - raise ValueError('Expected translate(x y) scale(s) on square icon artwork') - tx, ty, scale = transform.groups() - foreground = vector(108, 1254) - group = ET.SubElement( - foreground, - 'group', - { - f'{{{ANDROID}}}translateX': tx, - f'{{{ANDROID}}}translateY': ty, - f'{{{ANDROID}}}scaleX': scale, - f'{{{ANDROID}}}scaleY': scale, - }, - ) - paths(group, artwork) - background = vector(108, 108) - path = ET.SubElement( - background, 'path', {f'{{{ANDROID}}}pathData': 'M0,0H108V108H0Z'} - ) - attr = ET.SubElement(path, f'{{{AAPT}}}attr', {'name': 'android:fillColor'}) - gradient = ET.SubElement( - attr, - 'gradient', - { - f'{{{ANDROID}}}type': 'linear', - f'{{{ANDROID}}}startX': '0', - f'{{{ANDROID}}}startY': '0', - f'{{{ANDROID}}}endX': '108', - f'{{{ANDROID}}}endY': '108', - }, - ) - source_gradient = square_icon.find('s:defs/s:linearGradient', NS) - if source_gradient.attrib != { - 'id': 'background', - 'gradientUnits': 'userSpaceOnUse', - 'x1': '0', - 'y1': '0', - 'x2': '1254', - 'y2': '1254', - }: - raise ValueError( - 'Android background expects the existing diagonal linear gradient' + parents = {child: parent for parent in root.iter() for child in parent} + for shape in (shapes[1:] if background else shapes[:1]): + parents[shape].remove(shape) + # Illustrator may clip the whole artboard; extend only that rectangular clip for overscan. + for clip in root.findall('.//s:clipPath', NS): + if ( + len(clip) != 1 + or clip[0].tag != f"{{{NS['s']}}}rect" + or clip.get('clipPathUnits', 'userSpaceOnUse') != 'userSpaceOnUse' + ): + continue + rect = clip[0] + if ( + 'transform' in clip.attrib + or 'transform' in rect.attrib + or float(rect.get('rx', '0')) + or float(rect.get('ry', '0')) + ): + continue + bounds = tuple( + float(rect.get(key, '0')) for key in ('x', 'y', 'width', 'height') + ) + if all(math.isclose(a, b) for a, b in zip(bounds, box)): + rect.attrib.update( + { + key: f'{value:g}' + for key, value in zip(('x', 'y', 'width', 'height'), expanded) + } + ) + root.set('viewBox', ' '.join(f'{value:g}' for value in expanded)) + root.set('width', '108') + root.set('height', '108') + result[name] = root + status = ET.parse(branding / 'status-icon.svg').getroot() + status.set('width', '24') + status.set('height', '24') + result['ic_notification'] = status + return result + + +def android_vectors(branding=BRANDING): + ET.register_namespace('', NS['s']) + with tempfile.TemporaryDirectory(prefix='message487-vectors-') as temp: + directory = Path(temp) + sources = android_sources(branding) + offsets = {} + for name, root in sources.items(): + x, y, width, height = root.get('viewBox').split() + offsets[name] = (-float(x), -float(y)) + # Avoid the importer's path rewriting/rounding for a viewBox translation. + # Apply this translation as an Android group after conversion instead. + root.set('viewBox', f'0 0 {width} {height}') + ET.ElementTree(root).write( + directory / f'{name}.svg', encoding='utf-8', xml_declaration=True + ) + subprocess.run( + [ + 'bash', + '-ec', + 'source "$1"; shift; exec "$@"', + 'branding', + str(ROOT / 'scripts/java-toolchain.sh'), + str(ROOT / 'gradlew'), + '--offline', + '--console=plain', + '-q', + 'convertBrandingVectors', + f'-PbrandingVectorDir={directory}', + ], + check=True, + cwd=ROOT, ) - for stop in source_gradient: - ET.SubElement( - gradient, - 'item', - { - f'{{{ANDROID}}}offset': stop.get('offset', '0'), - f'{{{ANDROID}}}color': color(stop.attrib['stop-color']), - }, + result = {} + for name in sources: + vector = ET.parse(directory / f'{name}.xml').getroot() + dx, dy = offsets[name] + if dx or dy: + group = ET.Element( + 'group', + { + f'{{{ANDROID}}}translateX': f'{dx:.15g}', + f'{{{ANDROID}}}translateY': f'{dy:.15g}', + }, + ) + for child in list(vector): + vector.remove(child) + group.append(child) + vector.append(group) + result[f'{name}.xml'] = vector + return result + + +def export_illustrator(source, destination): + """Export a disk snapshot without modifying the user's open Illustrator document.""" + if not shutil.which('osascript'): + raise RuntimeError('Illustrator export requires macOS and Adobe Illustrator') + with tempfile.TemporaryDirectory(prefix='message487-illustrator-') as temp: + directory = Path(temp) + snapshot = directory / 'source.ai' + shutil.copyfile(source, snapshot) + subprocess.run( + [ + 'osascript', + str(ROOT / 'scripts/export-branding.applescript'), + str(ROOT / 'scripts/export-branding.jsx'), + str(snapshot), + str(directory), + ], + check=True, ) - status = ET.parse(BRANDING / 'status-icon.svg').getroot() - if status.attrib['viewBox'] != '0 0 24 24': - raise ValueError('Status icon must retain its 24×24 viewBox') - notification = vector(24, 24) - paths(notification, status) - return { - 'ic_launcher_foreground.xml': foreground, - 'ic_launcher_background.xml': background, - 'ic_notification.xml': notification, - } - - -def generate(output_root): + exports = {} + for name, size in [ + ('icon', (512, 512)), + ('icon-round', (512, 512)), + ('status-icon', (24, 24)), + ('feature', (1024, 500)), + ]: + files = list((directory / name).glob('*.svg')) + if len(files) != 1: + raise ValueError(f'Expected one Illustrator SVG export for {name}') + root = ET.parse(files[0]).getroot() + box = tuple(map(float, root.attrib['viewBox'].split())) + if box != (0, 0, *size): + raise ValueError(f'Unexpected artboard bounds for {name}: {box}') + # Illustrator invents random ID suffixes on every export. Keep diffs stable. + ids = {} + counts = {} + for element in root.iter(): + old = element.get('id') + if old is None: + continue + base = re.sub(r'_\d{20,}_$', '', old) + if re.fullmatch(r'SVGID_\d+_', base): + base = 'clip' + counts[base] = counts.get(base, 0) + 1 + ids[old] = f'{base}-{counts[base]}' + element.set('id', ids[old]) + for element in root.iter(): + for key, value in list(element.attrib.items()): + if key.endswith('}href') or key == 'href': + if value.startswith('#') and value[1:] in ids: + value = '#' + ids[value[1:]] + value = re.sub( + r'url\(#([^)]+)\)', + lambda match: f'url(#{ids.get(match[1], match[1])})', + value, + ) + element.set(key, value) + ET.register_namespace('', NS['s']) + ET.register_namespace('xlink', 'http://www.w3.org/1999/xlink') + ET.indent(root, space=' ') + exports[name] = ( + ET.tostring(root, encoding='utf-8', xml_declaration=True) + b'\n' + ) + destination.mkdir(parents=True, exist_ok=True) + for name, data in exports.items(): + (destination / f'{name}.svg').write_bytes(data) + + +def generate(output_root, branding=BRANDING): for tool in ('rsvg-convert', 'magick'): if not shutil.which(tool): raise RuntimeError(f'Required tool is missing: {tool}') - vectors = android_vectors() + vectors = android_vectors(branding) ET.register_namespace('android', ANDROID) ET.register_namespace('aapt', AAPT) @@ -152,7 +238,7 @@ def render(source, destination, size=None, opaque=False): destination.parent.mkdir(parents=True, exist_ok=True) with tempfile.TemporaryDirectory() as temp: raster = Path(temp) / 'render.png' - command = ['rsvg-convert', str(BRANDING / source), '-o', str(raster)] + command = ['rsvg-convert', str(branding / source), '-o', str(raster)] if size: command += ['-w', str(size), '-h', str(size)] subprocess.run(command, check=True) @@ -169,6 +255,8 @@ def render(source, destination, size=None, opaque=False): 'off', '-type', 'TrueColor', + '-define', + 'png:color-type=2', '-strip', str(destination), ], @@ -177,10 +265,15 @@ def render(source, destination, size=None, opaque=False): else: shutil.copyfile(raster, destination) - for name in ('icon', 'icon-round', 'status-icon', 'feature'): - render(f'{name}.svg', f'assets/branding/{name}.png') + for name, size in [ + ('icon', 512), + ('icon-round', 512), + ('status-icon', 24), + ('feature', None), + ]: + render(f'{name}.svg', f'assets/branding/{name}.png', size) store = 'fastlane/metadata/android/en-US/images' - render('icon.svg', f'{store}/icon.png', opaque=True) + render('icon.svg', f'{store}/icon.png', 512, opaque=True) render('feature.svg', f'{store}/featureGraphic.png', opaque=True) for density, size in [ ('mdpi', 48), @@ -209,4 +302,11 @@ def render(source, destination, size=None, opaque=False): default=ROOT, help='Export root (default: repository)', ) - generate(parser.parse_args().output_root) + args = parser.parse_args() + with tempfile.TemporaryDirectory(prefix='message487-branding-') as temp: + sources = Path(temp) + export_illustrator(BRANDING / 'branding.ai', sources) + generate(args.output_root, sources) + destination = args.output_root / 'assets/branding' + for source in sources.glob('*.svg'): + shutil.copyfile(source, destination / source.name) diff --git a/scripts/preview-samsung-icon.py b/scripts/preview-samsung-icon.py old mode 100644 new mode 100755 index 0ce309f..713b01d --- a/scripts/preview-samsung-icon.py +++ b/scripts/preview-samsung-icon.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 """Preview the adaptive artwork with an approximate Samsung-style mask.""" import argparse @@ -10,7 +11,7 @@ SVG = 'http://www.w3.org/2000/svg' -def preview_svg(source, bevel=0.18, effects=True): +def preview_svg(source, bevel=0.18, effects=True, transparent=False): tree = ET.parse(source) root = tree.getroot() x, y, width, height = map(float, root.attrib['viewBox'].split()) @@ -97,6 +98,20 @@ def preview_svg(source, bevel=0.18, effects=True): root.remove(child) wrapper.append(child) root.append(wrapper) + if not transparent: + root.insert( + 0, + ET.Element( + f'{{{SVG}}}rect', + { + 'x': str(x), + 'y': str(y), + 'width': str(width), + 'height': str(height), + 'fill': 'white', + }, + ), + ) ET.register_namespace('', SVG) return ET.tostring(tree.getroot(), encoding='unicode') @@ -110,6 +125,9 @@ def main(): help='Editable source SVG (default: assets/branding/icon.svg)', ) parser.add_argument('--flat', action='store_true', help='Disable shadow and bevel') + parser.add_argument( + '--transparent', action='store_true', help='Disable the white background' + ) parser.add_argument( '--bevel', type=float, @@ -128,7 +146,12 @@ def main(): if args.size <= 0: parser.error('--size must be positive') svg_root = ET.fromstring( - preview_svg(args.source, bevel=args.bevel, effects=not args.flat) + preview_svg( + args.source, + bevel=args.bevel, + effects=not args.flat, + transparent=args.transparent, + ) ) svg_root.set('width', str(args.size)) svg_root.set('height', str(args.size)) diff --git a/scripts/tests/test_generate_branding.py b/scripts/tests/test_generate_branding.py index b3ff3fa..665b32e 100644 --- a/scripts/tests/test_generate_branding.py +++ b/scripts/tests/test_generate_branding.py @@ -1,5 +1,7 @@ """Check vector conversion and the full export without modifying repository assets.""" +import os +import re import runpy import shutil import struct @@ -10,31 +12,145 @@ ROOT = Path(__file__).resolve().parents[2] GENERATOR = runpy.run_path(str(ROOT / 'scripts/generate-branding.py')) +ANDROID_VECTORS = ( + os.environ.get('MESSAGE487_TEST_ANDROID_VECTORS') == '1' + or os.environ.get('MESSAGE487_TEST_ILLUSTRATOR') == '1' +) class BrandingExportTest(unittest.TestCase): - def test_android_paths_and_transform_follow_separate_sources(self): + @unittest.skipUnless( + os.environ.get('MESSAGE487_TEST_ILLUSTRATOR') == '1', + 'Set MESSAGE487_TEST_ILLUSTRATOR=1 on a Mac with Illustrator', + ) + def test_illustrator_exports_all_artboards_reproducibly_without_changing_source( + self, + ): + source = ROOT / 'assets/branding/branding.ai' + before = source.read_bytes() + with tempfile.TemporaryDirectory() as temp: + output = Path(temp) + GENERATOR['export_illustrator'](source, output) + first = {p.name: p.read_bytes() for p in output.glob('*.svg')} + self.assertEqual( + set(first), + {'icon.svg', 'icon-round.svg', 'status-icon.svg', 'feature.svg'}, + ) + for path in output.glob('*.svg'): + self.assertTrue( + list(GENERATOR['drawable_elements'](ET.parse(path).getroot())) + ) + GENERATOR['export_illustrator'](source, output) + self.assertEqual( + first, {p.name: p.read_bytes() for p in output.glob('*.svg')} + ) + GENERATOR['generate'](output, output) + self.assertEqual(before, source.read_bytes()) + + @unittest.skipUnless( + ANDROID_VECTORS, + 'Set MESSAGE487_TEST_ANDROID_VECTORS=1 with Android build tools', + ) + def test_viewport_translation_preserves_fractional_path_coordinates(self): + path = 'M113.43 106.12h256.15v90.57h-30Z' + svg = f''' + + ''' + with tempfile.TemporaryDirectory() as temp: + source = Path(temp) + (source / 'icon.svg').write_text(svg) + shutil.copy( + ROOT / 'assets/branding/status-icon.svg', source / 'status-icon.svg' + ) + foreground = GENERATOR['android_vectors'](source)[ + 'ic_launcher_foreground.xml' + ] + android = '{' + GENERATOR['ANDROID'] + '}' + tokens = r'[a-zA-Z]|[-+]?(?:\d*\.\d+|\d+\.?\d*)(?:[eE][-+]?\d+)?' + self.assertEqual( + re.findall(tokens, next(foreground.iter('path')).get(android + 'pathData')), + re.findall(tokens, path), + ) + + def test_layers_follow_the_current_svg_without_requiring_ids_or_transforms(self): + ns = GENERATOR['NS'] + sources = GENERATOR['android_sources']() + original = ET.parse(ROOT / 'assets/branding/icon.svg').getroot() + shapes = list(GENERATOR['drawable_elements'](original)) + foreground = sources['ic_launcher_foreground'] + background = sources['ic_launcher_background'] + self.assertEqual( + [p.attrib for p in GENERATOR['drawable_elements'](foreground)], + [p.attrib for p in shapes[1:]], + ) + self.assertEqual( + [p.attrib for p in GENERATOR['drawable_elements'](background)], + [shapes[0].attrib], + ) + x, y, width, height = map(float, original.attrib['viewBox'].split()) + self.assertEqual( + tuple(map(float, foreground.attrib['viewBox'].split())), + (x - width / 4, y - height / 4, width * 1.5, height * 1.5), + ) + self.assertEqual(foreground.get('width'), '108') + self.assertEqual(sources['ic_notification'].get('viewBox'), '0 0 24 24') + + def test_nested_illustrator_groups_and_arbitrary_clip_ids(self): + svg = ''' + + + + + ''' + with tempfile.TemporaryDirectory() as temp: + source = Path(temp) + (source / 'icon.svg').write_text(svg) + shutil.copy( + ROOT / 'assets/branding/status-icon.svg', source / 'status-icon.svg' + ) + layers = GENERATOR['android_sources'](source) + ns = GENERATOR['NS'] + layer = layers['ic_launcher_foreground'] + self.assertIsNotNone(layer.find('.//s:g[@id="random-artwork"]/s:path', ns)) + self.assertEqual( + layer.find('.//s:g', ns).get('clip-path'), 'url(#random-export-id)' + ) + clip = layer.find('.//s:clipPath[@id="random-export-id"]/s:rect', ns) + self.assertEqual(clip.get('x'), '-128') + self.assertEqual(clip.get('width'), '768') + self.assertEqual( + layer.find('.//s:clipPath[@id="internal"]/s:rect', ns).get('width'), '20' + ) + + def test_legacy_gradient_and_transformed_group_remain_supported(self): + svg = ''' + + + + ''' + with tempfile.TemporaryDirectory() as temp: + source = Path(temp) + (source / 'icon.svg').write_text(svg) + shutil.copy( + ROOT / 'assets/branding/status-icon.svg', source / 'status-icon.svg' + ) + layers = GENERATOR['android_sources'](source) ns = GENERATOR['NS'] - android = GENERATOR['ANDROID'] - vectors = GENERATOR['android_vectors']() - source = ET.parse(ROOT / 'assets/branding/icon.svg').find('s:g', ns) - group = vectors['ic_launcher_foreground.xml'].find('group') - self.assertEqual(group.get(f'{{{android}}}scaleX'), '0.73') - self.assertEqual(group.get(f'{{{android}}}translateX'), '169.29') self.assertEqual( - [p.get(f'{{{android}}}pathData') for p in group], - [p.get('d') for p in source], + layers['ic_launcher_foreground'].get('viewBox'), '0 0 1254 1254' + ) + self.assertEqual( + layers['ic_launcher_foreground'].find('s:g', ns).get('transform'), + 'translate(169.29 169.29) scale(0.73)', ) - status = ET.parse(ROOT / 'assets/branding/status-icon.svg').getroot() self.assertEqual( - vectors['ic_notification.xml'][0].get(f'{{{android}}}pathData'), - status[0].get('d'), + layers['ic_launcher_background'].find('s:path', ns).get('fill'), + 'url(#any-name)', ) - with self.assertRaises(ValueError): - GENERATOR['paths'](ET.Element('group'), [ET.Element('rect')]) @unittest.skipUnless( - shutil.which('rsvg-convert') and shutil.which('magick'), 'Export tools required' + ANDROID_VECTORS and shutil.which('rsvg-convert') and shutil.which('magick'), + 'Set MESSAGE487_TEST_ANDROID_VECTORS=1 with Android and PNG export tools', ) def test_export_sizes_and_store_opacity(self): with tempfile.TemporaryDirectory() as temp: @@ -65,6 +181,31 @@ def test_export_sizes_and_store_opacity(self): if 'fastlane' in str(png): self.assertEqual(data[25], 2) # PNG RGB, without an alpha channel. self.assertEqual(len(list(output.rglob('*.xml'))), 3) + android = '{' + GENERATOR['ANDROID'] + '}' + foreground = ET.parse( + output / 'app/src/main/res/drawable/ic_launcher_foreground.xml' + ).getroot() + self.assertEqual(foreground.get(android + 'width'), '108dp') + self.assertEqual( + float(foreground.get(android + 'viewportWidth')), + float( + ET.parse(ROOT / 'assets/branding/icon.svg') + .getroot() + .get('viewBox') + .split()[2] + ) + * 1.5, + ) + self.assertTrue(list(foreground.iter('path'))) + x, y, _, _ = map( + float, + GENERATOR['android_sources']()['ic_launcher_foreground'] + .get('viewBox') + .split(), + ) + self.assertEqual(float(foreground[0].get(android + 'translateX')), -x) + self.assertEqual(float(foreground[0].get(android + 'translateY')), -y) + first = { p.relative_to(output): p.read_bytes() for p in output.rglob('*') diff --git a/scripts/tests/test_preview_samsung_icon.py b/scripts/tests/test_preview_samsung_icon.py index 31bf96b..328497a 100644 --- a/scripts/tests/test_preview_samsung_icon.py +++ b/scripts/tests/test_preview_samsung_icon.py @@ -8,15 +8,33 @@ class SamsungPreviewTest(unittest.TestCase): - def test_mask_preserves_adaptive_artwork(self): + def test_background_defaults_to_white_and_can_be_disabled(self): root = Path(__file__).resolve().parents[2] script = runpy.run_path(str(root / 'scripts/preview-samsung-icon.py')) - preview = ET.fromstring( - script['preview_svg']( - root / 'assets/branding/icon-round.svg', effects=False + source = root / 'assets/branding/icon.svg' + for effects in (False, True): + opaque = ET.fromstring(script['preview_svg'](source, effects=effects)) + transparent = ET.fromstring( + script['preview_svg'](source, effects=effects, transparent=True) ) - ) - source = ET.parse(root / 'assets/branding/icon-round.svg').getroot() + background = opaque[0] + self.assertEqual(background.tag, '{http://www.w3.org/2000/svg}rect') + self.assertEqual(background.get('fill'), 'white') + opaque.remove(background) + self.assertEqual(ET.tostring(opaque), ET.tostring(transparent)) + + def test_mask_preserves_adaptive_artwork(self): + root = Path(__file__).resolve().parents[2] + script = runpy.run_path(str(root / 'scripts/preview-samsung-icon.py')) + svg = ''' + + + ''' + with tempfile.TemporaryDirectory() as temp: + path = Path(temp) / 'round.svg' + path.write_text(svg) + preview = ET.fromstring(script['preview_svg'](path, effects=False)) + source = ET.fromstring(svg) ns = {'s': 'http://www.w3.org/2000/svg'} self.assertEqual(preview.attrib, source.attrib) self.assertEqual( @@ -24,7 +42,7 @@ def test_mask_preserves_adaptive_artwork(self): ET.tostring(source.find('s:g', ns)), ) clip = preview.find('.//s:clipPath', ns) - self.assertEqual(clip.attrib['id'], 'circle') + self.assertEqual(clip.attrib['id'], source.find('.//s:clipPath', ns).get('id')) self.assertEqual(len(clip), 1) self.assertTrue(clip[0].tag.endswith('}path'))