pax_global_header00006660000000000000000000000064137572332710014524gustar00rootroot0000000000000052 comment=41cac0c4f1896ee35c0a76e8640d0fdd033bd5bf plasma5-wallpapers-dynamic-3.3.6/000077500000000000000000000000001375723327100166715ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/.gitignore000066400000000000000000000000071375723327100206560ustar00rootroot00000000000000/build plasma5-wallpapers-dynamic-3.3.6/CMakeLists.txt000066400000000000000000000026741375723327100214420ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.0) project(plasma5-wallpapers-dynamic) set(KF5_MIN_VERSION "5.66.0") set(QT_MIN_VERSION "5.12.0") set(LIBHEIF_MIN_VERSION "1.3") set(PROJECT_VERSION "1.0.0") set(PROJECT_VERSION_MAJOR 1) find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/Modules) include(CMakePackageConfigHelpers) include(FeatureSummary) include(GenerateExportHeader) include(WriteBasicConfigVersionFile) include(ECMGenerateHeaders) include(ECMQMLModules) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) ecm_find_qmlmodule(QtQuick 2.5) ecm_find_qmlmodule(QtQuick.Controls 2.3) ecm_find_qmlmodule(QtQuick.Dialogs 1.3) ecm_find_qmlmodule(QtQuick.Layouts 1.0) ecm_find_qmlmodule(QtQuick.Window 2.2) ecm_find_qmlmodule(QtPositioning 5.12) ecm_find_qmlmodule(org.kde.kcm 1.1) ecm_find_qmlmodule(org.kde.kirigami 2.10) find_package(libheif ${LIBHEIF_MIN_VERSION} REQUIRED) find_package(libexif REQUIRED) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Config I18n Package Plasma ) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Concurrent Core DBus Gui Positioning Qml Quick ) add_subdirectory(data) add_subdirectory(src) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) plasma5-wallpapers-dynamic-3.3.6/LICENSES/000077500000000000000000000000001375723327100200765ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/LICENSES/GPL-3.0-or-later.txt000066400000000000000000001032461375723327100233100ustar00rootroot00000000000000GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright © 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . plasma5-wallpapers-dynamic-3.3.6/LICENSES/LGPL-3.0-or-later.txt000066400000000000000000000164071375723327100234260ustar00rootroot00000000000000GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. plasma5-wallpapers-dynamic-3.3.6/LICENSES/MIT.txt000066400000000000000000000021241375723327100212670ustar00rootroot00000000000000MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. plasma5-wallpapers-dynamic-3.3.6/README.md000066400000000000000000000055611375723327100201570ustar00rootroot00000000000000# Dynamic Wallpaper Engine A wallpaper plugin for KDE Plasma that continuously updates the desktop background based on the current time in your location. ## Additional Wallpapers More dynamic wallpapers can be found at https://github.com/karmanyaahm/awesome-plasma5-dynamic-wallpapers. ## Installation Arch Linux ``` yay -S plasma5-wallpapers-dynamic ``` Ubuntu 20.10 ``` sudo apt install plasma-wallpaper-dynamic ``` ## Building From Git In order to build this wallpaper plugin from source code, you need to install a couple of prerequisites Arch Linux: ```sh sudo pacman -S cmake extra-cmake-modules git plasma-framework qt5-base qt5-declarative \ qt5-location libexif libheif ``` Fedora: ```sh sudo dnf install cmake extra-cmake-modules git kf5-kpackage-devel kf5-plasma-devel \ kf5-ki18n-devel qt5-qtbase-devel qt5-qtdeclarative-devel qt5-qtlocation-devel \ libexif-devel libheif-devel ``` Ubuntu: ```sh sudo apt install cmake extra-cmake-modules git libkf5package-dev libkf5plasma-dev \ libkf5i18n-dev qtbase5-dev qtdeclarative5-dev qtpositioning5-dev gettext \ qml-module-qtpositioning libexif-dev libheif-dev build-essential ``` Once all prerequisites are installed, you need to grab the source code ```sh git clone https://github.com/zzag/plasma5-wallpapers-dynamic.git cd plasma5-wallpapers-dynamic ``` Configure the build ```sh mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr \ -DCMAKE_INSTALL_LIBDIR=lib \ -DBUILD_TESTING=OFF ``` Now trigger the build by running the following command ```sh make ``` To install run ```sh sudo make install ``` ## Components This project consists of the following components: * A C++ library that provides an easy way to read and write dynamic wallpapers * A command line tool for creating dynamic wallpapers * A plugin for KDE Plasma 5 that is responsible for displaying dynamic wallpapers ## How to Use It Right-click a blank area of the desktop and choose "Configure Desktop...", select "Dynamic" wallpaper type and click the Apply button. ## How to Create a Dynamic Wallpaper This engine ships with a command-line tool that one could use to create a dynamic wallpaper from a collection of jpeg or png images. See [kdynamicwallpaperbuilder](src/tools/builder/README.md). ## How to Use Dynamic Wallpapers for macOS Since dynamic wallpapers for macOS and this plugin are incompatible, you need to use a script to convert dynamic wallpapers. ```sh curl https://git.io/JJkjd -sL > dynamicwallpaperconverter chmod +x dynamicwallpaperconverter ``` Once you've downloaded the dynamicwallpaperconverter script, you can start converting wallpapers ```sh ./dynamicwallpaperconverter --crossfade file.heic ``` After the command above has finished its execution, you should see a file in the current working directory named 'wallpaper.heic', which can be fed into this plugin. plasma5-wallpapers-dynamic-3.3.6/cmake/000077500000000000000000000000001375723327100177515ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/cmake/Modules/000077500000000000000000000000001375723327100213615ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/cmake/Modules/Findlibexif.cmake000066400000000000000000000042031375723327100246050ustar00rootroot00000000000000#.rst: # Findlibexif # ------- # # Try to find libexif on a Unix system. # # This will define the following variables: # # ``libexif_FOUND`` # True if (the requested version of) libexif is available # ``libexif_VERSION`` # The version of libexif # ``libexif_LIBRARIES`` # This should be passed to target_compile_options() if the target is not # used for linking # ``libexif_INCLUDE_DIRS`` # This should be passed to target_include_directories() if the target is not # used for linking # ``libexif_DEFINITIONS`` # This should be passed to target_compile_options() if the target is not # used for linking # # If ``libexif_FOUND`` is TRUE, it will also define the following imported target: # # ``libexif::libexif`` # The libexif library # # In general we recommend using the imported target, as it is easier to use. # Bear in mind, however, that if the target is in the link interface of an # exported library, it must be made available by the package config file. # SPDX-FileCopyrightText: 2020 Vlad Zahorodnii # SPDX-License-Identifier: MIT find_package(PkgConfig) pkg_check_modules(PKG_libexif QUIET libexif) set(libexif_VERSION ${PKG_libexif_VERSION}) set(libexif_DEFINITIONS ${PKG_libexif_CFLAGS_OTHER}) find_path(libexif_INCLUDE_DIR NAMES libexif/exif-data.h HINTS ${PKG_libexif_INCLUDE_DIRS} ) find_library(libexif_LIBRARY NAMES exif HINTS ${PKG_libexif_LIBRARY_DIRS} ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(libexif FOUND_VAR libexif_FOUND REQUIRED_VARS libexif_LIBRARY libexif_INCLUDE_DIR VERSION_VAR libexif_VERSION ) if (libexif_FOUND AND NOT TARGET libexif::libexif) add_library(libexif::libexif UNKNOWN IMPORTED) set_target_properties(libexif::libexif PROPERTIES IMPORTED_LOCATION "${libexif_LIBRARY}" INTERFACE_COMPILE_OPTIONS "${libexif_DEFINITIONS}" INTERFACE_INCLUDE_DIRECTORIES "${libexif_INCLUDE_DIR}" ) endif() set(libexif_INCLUDE_DIRS ${libexif_INCLUDE_DIR}) set(libexif_LIBRARIES ${libexif_LIBRARY}) mark_as_advanced(libexif_INCLUDE_DIR) mark_as_advanced(libexif_LIBRARY) plasma5-wallpapers-dynamic-3.3.6/cmake/Modules/Findlibheif.cmake000066400000000000000000000041761375723327100245760ustar00rootroot00000000000000#.rst: # Findlibheif # ------- # # Try to find libheif on a Unix system. # # This will define the following variables: # # ``libheif_FOUND`` # True if (the requested version of) libheif is available # ``libheif_VERSION`` # The version of libheif # ``libheif_LIBRARIES`` # This should be passed to target_compile_options() if the target is not # used for linking # ``libheif_INCLUDE_DIRS`` # This should be passed to target_include_directories() if the target is not # used for linking # ``libheif_DEFINITIONS`` # This should be passed to target_compile_options() if the target is not # used for linking # # If ``libheif_FOUND`` is TRUE, it will also define the following imported target: # # ``libheif::libheif`` # The libheif library # # In general we recommend using the imported target, as it is easier to use. # Bear in mind, however, that if the target is in the link interface of an # exported library, it must be made available by the package config file. # SPDX-FileCopyrightText: 2020 Vlad Zahorodnii # SPDX-License-Identifier: MIT find_package(PkgConfig) pkg_check_modules(PKG_libheif QUIET libheif) set(libheif_VERSION ${PKG_libheif_VERSION}) set(libheif_DEFINITIONS ${PKG_libheif_CFLAGS_OTHER}) find_path(libheif_INCLUDE_DIR NAMES libheif/heif.h HINTS ${PKG_libheif_INCLUDE_DIRS} ) find_library(libheif_LIBRARY NAMES heif HINTS ${PKG_libheif_LIBRARY_DIRS} ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(libheif FOUND_VAR libheif_FOUND REQUIRED_VARS libheif_LIBRARY libheif_INCLUDE_DIR VERSION_VAR libheif_VERSION ) if (libheif_FOUND AND NOT TARGET libheif::libheif) add_library(libheif::libheif UNKNOWN IMPORTED) set_target_properties(libheif::libheif PROPERTIES IMPORTED_LOCATION "${libheif_LIBRARY}" INTERFACE_COMPILE_OPTIONS "${libheif_DEFINITIONS}" INTERFACE_INCLUDE_DIRECTORIES "${libheif_INCLUDE_DIR}" ) endif() set(libheif_INCLUDE_DIRS ${libheif_INCLUDE_DIR}) set(libheif_LIBRARIES ${libheif_LIBRARY}) mark_as_advanced(libheif_INCLUDE_DIR) mark_as_advanced(libheif_LIBRARY) plasma5-wallpapers-dynamic-3.3.6/data/000077500000000000000000000000001375723327100176025ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/data/CMakeLists.txt000066400000000000000000000001121375723327100223340ustar00rootroot00000000000000install(DIRECTORY "Dynamic Numbers" DESTINATION ${WALLPAPER_INSTALL_DIR}) plasma5-wallpapers-dynamic-3.3.6/data/Dynamic Numbers/000077500000000000000000000000001375723327100225625ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/data/Dynamic Numbers/contents/000077500000000000000000000000001375723327100244175ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/data/Dynamic Numbers/contents/images/000077500000000000000000000000001375723327100256645ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/data/Dynamic Numbers/contents/images/dynamic.heic000066400000000000000000023075341375723327100301600ustar00rootroot00000000000000ftypheicmif1heicometa!hdlrpictpitm ilocD@Ho9'zG9;Yv9A Tf VJ9 j76 v/9 h>&v9 =FRbSw9g0v 9CI$gv9 FSfU 9iBPv 93!zv#9")L$eb&g%9%{^U'v)i9(J?*0v,2W9+FQ-f/y9.Y0Jv291A3^{v5_94t*]6.b8Ґ97YM9@v;A9:UfK<v>9=ѿN ?fA!19@5jaBbvD9Ck>E OvG 9F -`H ^iinfHinfehvc1infehvc1*infemimeapplication/rdf+xmlinfehvc1infehvc1*infemimeapplication/rdf+xmlinfehvc1infehvc1*infe mimeapplication/rdf+xmlinfe hvc1infe hvc1*infe mimeapplication/rdf+xmlinfe hvc1infehvc1*infemimeapplication/rdf+xmlinfehvc1infehvc1*infemimeapplication/rdf+xmlinfehvc1infehvc1*infemimeapplication/rdf+xmlinfehvc1infehvc1*infemimeapplication/rdf+xmlinfehvc1infehvc1*infemimeapplication/rdf+xmlinfehvc1infehvc1*infemimeapplication/rdf+xmlinfehvc1infe hvc1*infe!mimeapplication/rdf+xmlinfe"hvc1infe#hvc1*infe$mimeapplication/rdf+xmlinfe%hvc1infe&hvc1*infe'mimeapplication/rdf+xmlinfe(hvc1infe)hvc1*infe*mimeapplication/rdf+xmlinfe+hvc1infe,hvc1*infe-mimeapplication/rdf+xmlinfe.hvc1infe/hvc1*infe0mimeapplication/rdf+xmlinfe1hvc1infe2hvc1*infe3mimeapplication/rdf+xmlinfe4hvc1infe5hvc1*infe6mimeapplication/rdf+xmlinfe7hvc1infe8hvc1*infe9mimeapplication/rdf+xmlinfe:hvc1infe;hvc1*infe<mimeapplication/rdf+xmlinfe=hvc1infe>hvc1*infe?mimeapplication/rdf+xmlinfe@hvc1infeAhvc1*infeBmimeapplication/rdf+xmlinfeChvc1infeDhvc1*infeEmimeapplication/rdf+xmlinfeFhvc1infeGhvc1*infeHmimeapplication/rdf+xml_iprp]ipcocolrprofRGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepcolrprofiURGB XYZ   acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBwhvcCp @ p@!*Bp Yn"Drb@ispep'auxCurn:mpeg:hevc:2015:auxid:1whvcCp @ p@!*Bp Yn"Drb@ispepipma0      #$ !"%)*&'(+/0,-.1562347;<89:=AB >?@"CGH#DEF%IMN&JKL(OST)PQR+UYZ,VWX.[_`/\]^1aef2bcd4gkl5hij7mqr8nop:swx;tuv=y}~>z{|@ACDFGirefauxlcdscauxlcdscauxlcdsc auxl  cdsc  auxl cdsc auxlcdscauxlcdscauxlcdscauxlcdscauxlcdscauxl cdsc!auxl#"cdsc$"auxl&%cdsc'%auxl)(cdsc*(auxl,+cdsc-+auxl/.cdsc0.auxl21cdsc31auxl54cdsc64auxl87cdsc97auxl;:cdsc<:auxl>=cdsc?=auxlA@cdscB@auxlDCcdscECauxlGFcdscHF mdat5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & h'(D,X4%bLI1&$ęvv3 [:J#+7|'Flӏ>t5[AkmQ -B|@e<9J>8 [h÷]tↈw.T!O}v˯KWlV;$5 >wuBXz*O֥S7+5T,ӅU['<{)#TԸ:@Y{2Y'mI!>Ϲ, @9ݱOޜ6UH^*6V#c2% Ztv޾3ҼM_8Qzo#Hm$8*-)F3=_$)._HxZ"Z3L)lS]mxo -R0̷TZ̡9AKhU ot'Fe 4uGyîP:jr`}j}W> GwΌqg=]kty sWz7I?rt[A2|=3uD^M$I/*?8Kl8R|0jP0 4>ܶ)t=^4ST q> %%q75i(JX fh+K3\k(Wi}4c`WElmmBPQ^}Ŵ<4#߇cnB~Z1t.JUI=HByyנ~>W'sKsYD}:WBxmO:;ߢ3 ROS>lbs4ň/ ~48-Q{@d"ֹ]݅sSs P_g"ji?9˛Ђ| *؊Ijч+:% CɗHQFCy$d0x TYaGgʹ&$t&({_CQGf;buM:#jڑI]g+Y˅%]MqRr?J_oUY| xL=:Fj fmy6<'Cݡ!y ~  0|%[uꛅRAWv%i!Tz =tn nafdMOY˷ha#1%Z 8/1q\ \uH\ij("Ah7Q8JbDtOg6|JcuEw]XMqKCiu515mDVzqC+ ^'?ye|6^:t'ۗZoz5iN}owE D'`@ޚĠH#֜`^ _I ?>}%}z-?.Zp3u1-٦^M0zGЇBQ3G#M璥\r Dm?jݝ!a .ҩ|zq(q8fr8*݅^X,` $^G/sfh0-@!S"ttvH.'|N0P[f5:V~%}e_Hm'u6)FVpD`{_}EqR\ݐ!yWnnN;eXhqp,oZ. r8 yJy۾ыaBH|]?] X5!/ }fIv񵂿 ^"ĕruܧu.d"M|AWpSBŒi-@ҽ@ǪVcZ2a8W5OV<)E`#+_&eehߤ5+A{딉G]%vHM zYдQV:< mpVH՞I|؃ycG26&Z~kL,%s#,R@5|V³يS^~r,W6#yZώJmSL2'+ҹV1Пvv8(7kml~Zv_NΐU%+%KՑO0̀E\67D` ac)U ,#(1; :2#~x㠶Ԯvӷ&{bz mڛ*h #~f$MC= ܳ'*;<6r,Z#Q|ipnMݿC㵄y7Lq<CYvloGy}X%+ǧ@]C2eBn?jрl֤u9 akr`i}з5@.{`l[Nɬzw$A CYSSŴǦAmBDDVi:"VatwTƠʬꋍo{;[9ͦM9o/oUx?п"6h&Xj&Z\,"$'ޙ,   ssOqr|T|0AY3o9q Hl@yQj }*b(81:2l$_1ryx Qne^u~{f~2 ˷Pp~[K5}vDhn\[;/5lJ^nč\ ,6yֆ?F: Y7`סBh'm]x&D7 U渜RtO5ӣc0-}WU>څM{7pj4 h<wFֽﲦ}}~Osmfɦٽe(-|5i$^-eq$ƚӸ{fZ\f(.V'Fu֪-0yj Bp a%%LT40^PY(nU`:g y|}&T,%^un_Cl ¬ה*olySjPל0jη JDa d4)Z !qfm3mEO¼5@| p~ӣ6w#?y+l}b:hϛIkanדXTUiKLdr"l1)80Vm SkHȋT1T8'eC1Ҹ <(ጋ7uko.rTRz> U渜RtO5ӣc0-}WU>څM{7p~(T{eLQ)~? ͓MzP[i0kEH[&9I5pw5|/,P]6N43?UwZa/\a8.n,)S Ṳ#mJJi&a5s!-޼Q ?تt zL_XKoƼܿl* AY){6T?;F j9P`՝n2hR#FB͞ g:^*4yvjgSW%gk XB )'L^kPb5o>1]_p{_#KAu/} XbGs.df SY6PW ó;gv|M .:HgЀ犙U Kđ֡( ?>o@_Ѿa& xϝZm0MdAbUjR eʴ8˝#$+VQ"$p\$%C|n! `8##FXy1j_vV.w* 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & h;(D0,Bx&X%P$L$L$L$ڭ[RS`$BH$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀ+KZZs\>d[y%VuyS`\Bɓ%9tT)ir@Xy`,oCHL`͹6OHLu)F`=t|降B (x7U^ ϕ8*TμVտNp%\,?q%W|0s 舆 lro[KƉ'_z+sL_xUKwP9`v/b}U?~>' 6E"RYzfi0-2곹A3[n([3&m[ڠT@ܷU^R~yڨI'kT#wd1CUZbx-@HWVH$99@Kc?7L+_R1`Xz#|E)&NvfUDtݼ8_-q=Ɨk!wY|rLˡi$'e&v+ZPe簗l{ǨYlMم]u7^<"`c.#aw瘰eaua6>[D=ӚgልsCs2[${1q>^&?-+7zQp"q0Fn2L71&VNZ;h=/t̼G>,_w^<00aSy)w T}`Z'mìmQ X]!TTl^9QmntopQN=dt*H_<[)σYEqO+LfQ=6Eh37R4{삭oO2zyr@d;T1S!tlZe; Kn +{, X( cμ.$B5j_b wF3):M_`jWРM8# jU=;&ۡ\^A&ԕUp3-t$-蓊XvP!Vr30sg//*ԩђ(dF]^bW~{l;C D)''3I @h-"K>\%UPXc RqTwߴZ% ~ y }+?E^Bz'>Rɣ@T}SSpk0=t9jkPs\rvЕE 0,[>&i'+`>I9X,݄ҏ4{x˯dlb#NG$ր2k;Wג-e6TK*ؠj. 2 l6uMR WM,rm1-TkpG`\2F!Yomxxk*%-.cӳ4 h3}ӈEU̠2Ĺ4dBt!ʧZiZJ@`N#@=`i`HvTo^7s$+ k<(3M4ѡʶ^JVxC-T y)qqmزre4?dɫfnI&'/%fմ0A#) KC?u(BiR5F۠T+2EPcmKQ?]sy%æ`F]ol'mV6$~m-`7I=z ӿ%*ddI5`XHЋZeo&u}|2)46Ë;{&r+(.&6dBfEڟ h+4o&>s!-w)(g.@5osD,?18(8I`RK%fZhD9,ΦPXrA{p(J@0{ J{xoqdEk>+577b_Lڏ'|w%QHc*$qNՌ0lQmІHJGӜ_swxtoMn' [Y EʤAzXCoF֙\N?EeW+fTȗҞ8D]竴GaQ(B573EF?\e~aoߟHNdd8Y:̊5x>Ij}3ǺrUFi <ʔN-(x!ίH\HK$dNF7$:gs64 @$]F ˺<$Na0mo^ML{ꂨrzڿglC\^ֹ@؁p(H,lIc(a}.}<~& !p}eマavՑP@?3k%SY7 jRڕ oߧ5TI\b8xa>.3AgþG35oz %1t9 Y15gOL+5?Oa̾mxRLyv7@} |Gάɭ.k6Lwby.}}> u;&Cu'HXVlWfV(oM ^+x1;nk*0:ƿU4VdѰ9]ϘҢttpF\ Yqk=tс:H'&!D*HOyٿIn9zcJ'1A"Zl<~kg4^FoY63bf@gY 9$/w? ̑m_|-2 ('X$m-_h=s|"54L>o߈@øuwK&V8C"@ݴ,kUzWCSڽr̛2qJrJ W"Xe M; Gyۿ:#R ޻BIID}8+ڽL>Q!@OtS1RD8Z26wv (-hp9h`5r %;5_Cʣ.M7ɴ!Rc7Uʩq?.GKƅHbhX9Z2T3: W O?M*Ҩ>-k4y(b#jvUH@Mm8m07)7 OnH0(r2V0@Q3wJMnT K-3.LsڶKHS\O0G>DK *"BG~6"rћ;QPC ,O@G)e1uNT?l}$8d1wfKA $0uTM(zŜYlXSseyM]}x:fdC'YNIeFlIqdݹewyjBV(xvi| }Ҽ_ E]iC"3xD~iҜ]ʦm3HaB o$jH:;Kvf5"vQ<{"{Ejl b[#u4mA5B Mr: gWjx`:aGWg61+S'!y cܯIH!n/S=h?Kcc2挣J_wqYyS_ś{ im9b?i^D+ٕ0V;Hm0&(e%vsyJ e2&Wf/npfS^{!Ԃ{*T*X(tPO#:_|Yy1Ͷ'&gJH`,aY8{=[/mF|xut_<ЫKc5<*KpRSLvlZڌWq_[j5hhX+ܚ s6̝zY5>,YzB2pvAƧ{ 9 9|XxME36c3lj ;Y%D+7V#q=p5iOheËbЧGÌ#@x?O:؟݅Y+G?[=yw +cXCa2[FnxX>#bH{QI chzFM@z2<{[b?O*i3FΡ T=`6C.)g"t궅3\,C5<[\KX)Y*Uf,0'({LKpHYaIށwohyN[V+g:㢛^֑hIIB]2YRbvC,CQJ;59i"gkWj˗&{eY雨v6xiXN Qp)hԀ2#.cj5UDU0;u[g,#?)C6Bn0r8doPz8 /kH;ddaYR7=Kґ@K+ Kİ} 5;򫹷.J Z$+:Xxr#9B PoiVXUl{.ȷۈ|Ì4fs&X݆1m؂bNkH^yBTrI\P~Y`^E@R4'+Z.UE0_c/4Ǫe5r iIͻJH9i0%IBo: "e[yc0Wc3bρsh[ͣP Sd$Z}" ~PkCQl`~84+jhp}OnO*I}Ko0a!l=ɼ>vMne2 :fM#yZo}o& o@2%)OaۄN֭oWjLxzrX\x 6CB?Z0C ^JeVOr3)F?@R@ķ3qvyS3KL]4 EQVu#rw|iFif䈀2_EDԔ‘ˊ9m_J2uC~-PG`tp&(/m4gl1/ĜGA4>4ĝIYEyFa[ed-kIk?%uT3M4Њ3:kt*±֊szX)~>rqm. xqC?C"c#5JRYeU|Q;- OCeָ y+Z4~~9 {KS mȰڌ,mKd6` WxÝ[!T _DaPĤ*oXZ (. Z0[_.`}0ᷪj"y ީ_T I0%iӳsV^/ܲߡ S\ay;u} ٚX;&$O/ ^|mY̳3j 4d M\< bUGQzAf^ɇ,.vs@}f,pSU}-E<_RtƐ=V؜|Ub17rڽنY| RD94/7_,a\\ӈȐCT՝y#}5Lznw 5=:F_q$*LrmWoL6f<nl8WwP#OT,v@ZkC%:1ʰYR wA2V"y;Lo.,ve٫Y!tucW9μŋlzeR]3H{5f)Q(kSj8E{A+:/u(\D/OBJVl2Vi q_F9FswF v!+II{8@2Dm v+f1OnjEi%ͣa7moٙk># }TV @(@<<2$܌2ct,wHrGma9j#6lm)y5kڟʈ*.HT=h%w ﷏80$ָ*0V ?8Y"H B{A0\+N3~'zzoށijY659==9 +Єo]P3 >@ Z Kvh8\Zcj;,ŔԛP ]1{ZШ̂/M頲y" _ۅ /c,aT |: 7 p!OZ>TDՕ47&s Xu!zВt<nZD$q.%8$@u@ |D5 +m"g483Rmz m-BZ^N?W:aEyƍJ\aP9F8täwr20\gJ$(j'|qG.+fHh/06hAilSf_8jM?'Qa` PrJ=rm!B;אַ/fG>SVTA\48B&gIWg>m"OZ=g9O2 Õ4޵/0pW߄b"'P#_GeFlM"BjKs^O]YBGP/wo} !v)OwN#؛ ܕ,yp>mcjhw=pG:S lr=1J6ļ>!/,IHOi;Z9p{Fp3M'ݏ{8rм(Gο&vSeq#1]b]]|BOUkL;E{ra= BO2e^x]zXeOtפOƎB$*?%-LE5]\hg=tQ6-5mÄ=OPY{/ #`ؔ^5Z%`kȏ0{vо9. #p;XeOH Z#@^. SLfFek;>=;lv833ln,CG| 7qz* do6"cȆ'/\By=yP.w(o?uBp\ZTձ1*A3̴1EØ҈ufLA |?I_e3vCp<<ٍޠes@;im҆gGzК&u  iEB9ml|\lԽB.¯)(QLҊOϒOKO?K.%=x7V)oVbHפጡ~LzǗ9{٘WͺWsfkS12+ܓQ6c:6[n=\\L19kbqށ?UՃ5*O0ɪ^~G6H55P 0 :jR& & h 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & hA(D0,Bx&X%P$L$L$L$xbN#P$H$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀJH޳{LN\Z"9eBu֝?7,mْCP҈r,k zu GY#`m2ܷI潌39$p(@h(H"B O߮mɵ )TTe%U }4>Ұ &~^7 xvҿg(eHoA. uqF ] ]Zc*r%.$Eij ^>$FwcyP  "ƟK!<"QHxzzX o$gsLՓiKjk2}aχZ8-0=C1M4bߚO"l%tg =)rx(،o򦎟 @~& ucoi&(5L(}\O(MA*mG,,t4>iF"-p{ ao7ޛlM!YCiFLdty5\<đ*7~3j3#9S>Uo-Ҍ^\˗ٵ64Joq ;͹(ڼWr&Oĉ*KE/?2O@L"`i41]_\ 8o]I$xmc1>Ycc ůƶޱA:4ͻKsxQ TB{dB t3b0 \`>5 ~K1w-=nU@&%9-ʷ$QwC~Qjtq8~Pu[ZCEvcOnpݠa}pϊU0cY~D+kbL_4L&jHke&ʩHBx:;ɣBT(ϹtAlZtæ+ bQB[} s̝j˅?q/HlulnA!Z\DQWF9ނ wl-Y=4\:M'z[ݢ\-i|2P*qyN< nxM&5e}5{/с# 6=c&`(S:/G!e аFbA= W^Ó3xRN3'H K0y1V_.U@i }ZX3bkBv0UV-]drn6XЌk;jy#BRiXgW_t? VUkFF#5f< zsb{mAXf;9tB|zu7}X-"Q}3i A g] A -gKR&qypTivfWI9BRn Ľsy߶EX6|#5Mlhÿg#tMr=`TwWmA]uY卹,(K:l@J y#:qFA(AE;oaZ;1ptau)0}d n8//z'wk(n֙ar.-M$0wˬ[PoyQ5B ,d9' XutMg}WF9 eDDÄGMUfLhS`[ihS8Z/H Moʃ`H0Gn1UL민pik,W[ҏqb׼A9 c^PA35+hGmFAc:ӛp6KܱҵFid l #` /؅@*"T\?V -Ie_%<'=)@A@2%mJϯ;8e7sͻ+m \[w}H?҆U/.`qHj{18u~i4R8e x07oo>c-Cp \zlnm"р“2E)̩wگ|>řy\o_#z_ 2DG|j? ,F+S%Pkgx)hCAB+^hF;3Tx=eeA %h=mY"8\J=m_|Jn3d#hi @%41.7_b[px =?cnqrm|`-=g1e^x1svzj#Ş3k`nc,]_cѳ5l 6@rm iulRj{Y#-;תqnBa&?Lہ`6? ns W޶}\틪;6V;OH^O{h9V d]*&* pQyIߐVyZ[~w@yjtʡgYep"w7g|k01 ӌdg X]mlSxmۺjRPS΍n0yƟ/GaЭXu,:1` ᵰH: 1#Hn.lDa:,{a~jw3̏3s}"xSuͬ2nvy8YSVM|A??!Bok0M`mҳlk_u`DN=/U\zȮ eR@3oZaĭw _h vvi,G8CG\* ~d^SDt)hOnAj4 \m% C. `hE DC97J_/ZiLWFWT/ 1Q9mjt0 -- !GMWI/w n v}GV7$g[-(NDK,B 3:}=5%&/rgүQ2{oN/ XAz}vmP[O"knXmlY6f[m`3U|#29-{w%IڧIuLm xr^ICdYSMELj>oD8^pԟj5[߇|@űP}#A J4/W6hf?ߖ ҩhOZq޼MA7 |0 yÃxGii%k{#vg[' V/wxvD鹅٦㽦Iz%=ҒGyv{p.0{8Fp@R#,rPl钷3>HYrYG7LP5x^oE53t S6h֓r91Tn}\U8Gqi[ׯѷ8oRH~dT cßr+R￰RX  (Q Dt48D{vVU{íZw:caFgW acqgtVИ!g{^[dϱIn*PX毥 |)#u?6 ֥9٪aMF-; WE$ KEꢒen9^ Fkz++=57w=8Y $,m76|q?L9>VD߰ L}%a)pn EUȓqd}bMpc2KL z.dRJa(nl; k#?h1p6 .ȴUr?W{VD`ipR3A(z@u~m8eY ]"ۭaLpaw4'80Lh{R[$EbLwJT,}]qm?An'iĔd?xܑRWv6$Gq g,,סs][S_st,飸<+,c6[O# M~!&6jr^8 OwO0yԸ\J!)1Gq{Nѭ{mS2d0_9Z\QZkhioŲ?Kr7֑QCQ]䲍)j1a2x#F0W)`g''՘^͊ >})(0*ȳ~9_yhdd2< JES v4@tT\j9bagOgd9Ϧ[J^bXDD9  xm/P[=紃@Hѿ"۩Q =iOaI"ڇ#!&^,76R1 f*@"Xz.tP-žac"t&w"* Kdq{1N;D| oJEEoǰ+71(tl1nۍZ/\Np[uW#VdNĚ ,#[/0R+;;k6(I0M6ܔ&E#c]O~bhaj9ߞL\/h%#iEi;KdY g 8N싨ir%,vn06K?US!aU^"I4ۮ)ᵡ6{93M,EIw(ɈЦ87CSS0;ZL2{֯(Zoۦ WPR<'0pAKU4)JЍ>!>AڞPe"aZLsm?No 5Y.? ` k3w`j|i!Q]Mj-.e?)Y 6ױ[ yH>b*{~5 ![?~}iv{ۚx^-l03*b /d!īrB^`.\h%0۩sVԙ?r;s|JP >8>mBV mN,83C%碭dÌsk\tuUS% dC9M;Q'R;3,fJM45ݡY(N̦݄MtV ~V PP)Uw oe].r9t?m!'9 7 .⚴baY'T|du?ʨ>hew5=5MB\N`ܘ5#e❐@w>wyMOԝ94fp}Vr ٤#&@}.2mgE&;u_S^B=Xl05<+eηJYgG{w$[LZ9FxzZlrKXJQnZRı+ k`tC$#3& a|];8MfPZv᯻lr .>"BLy~:5ug=N`=4jVñv*cbz^#@#jWt_.OJEƸ2l5XS&>?3"`O\2%"XƗ)ő_vMˌa{K|xmErңGjn5jOsO\s8.Yh٣i7pFeܰ_.GンMt,orI (i?6 I CYYFJNfk7ؗƅajn_[[.[⻡_;`Ns<<nh;l95!qdfZ=:KB% R?~å6='#}o~gfPo6rɎ'1_֙5[MiTA!D)!2؇Oe~6&$8{Gȕ@ iَF`$eG{5tEДh{+:זHOqܪ:,` C;x~:n54^AͽnX Dhqz`otuwTO:!B,_eKDKaVѫ'qR} JCR*i&6+<QgP52c.XczZJ$ΨU%)o+bcXJi0<1_W<^S$9c],K,zT@[ =L* Izf4*ҠwٰkpRW͗|R׺iЉsW{HkZDloPm2'*Iڳ?HO"Z y?+@<fhi}0fb&VOOۈؔmGsz^~,٠9 `Oh7/AU̲N-su7s<^.+n7P{X16;_䁢=LHw*ׯ8z+Kǭ`Z]x"oPBbᎧ5i0};o2XEgTRҘe{f)Pe6SVH3ެqV֠d@%pL<4ݎN@4vit~^֊kڑM? C`+Epj:tD僄y }0 儏 ?"m\(ɏ6q]sYC AYOde Zy"&rJaX~6->00]͋ƋJhƟՕOc.´X~aBp͊6AP3E$K\)("qX3Pљ([)gNkӥ|tvΪ52Viwj.7gJwp*ʿ\{p2 >^ 6?QWQ2ᶯMh"Åd#|lGF+*ǖ@*GB9n&7/0 ;EVA']3l/wj(+^qtZE8vs‚P|mf -MH;܇χYۏNez;6T$iz1ufꜧAqr~ۘ!0Kn=!u1n(5NɰkE6#(Ie5}p!}#{v ӈZV r: O1/~-8_%%Ҷck u[Bꎐ4콁T8DC$ ֱGxܑ JG"W :jR& & h 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & h72(D0,Bx&X%P$L$L$L$I_ sJD$H$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀCԲDnx*M>cd8 E妷fYAaЖ?^ΘƇ53es&~)O|ɻ=6S~J\hj-VyBHTUY-ڑ'#:)˝?F-U  ( -!^}@]ܳ3Y5 Umk XH6'<Úk*OQVQ.4; %O`H9؉#|C6Ņ:fxa OTC?]ߧ0ށ f! K)D0:$9P!'e%%}Tw$ yo]'ML'0K<)K Zvt>~L8{WŘ,|?F)GvšݘGT~X@0;A\pH\v㚼M/:XC.Wh;0qtw _4"2! 5-4=>Z{hD7ϫYng{Ro\ WNTnR[*(d _S,OL*zѤ-OcOR؎)hd22Z[h-kpͥT} llb9Tnp)E1oՠ{Xϗ1cq@OAr!+zLw,͸(l57O^X8)I ZsZʌ,Ʒ1p+Ύ gczy|΍՜PuGAC" լWƪ>xWCZi%u\NֲRpc}QDqf*Q^p0Xy޽ '@A}e`t2xVz ]PW Ɔ8_p;^Ly@i>v >CkB /<OFf:%ηL Xu٘8' J9B`.pXG[<9Ի^)<97j.F0[Faq3REt}{y9 zlϔ0Ur0qOZ rIori[S;ףtn)sIJӝLT|lْ#sۣ@ ]9j8T,SJh>,27ڀ`8>`Qo *˱MrflMW'U FYwUi>cjY[WowPa::q8HH(g59\jcG.KrWsZTW~AGSSj5!? h>yV@nOu>qxt!<(+Ok n?ZR J).玈D[g}נy\#Y_KpX S󪩸c&&砼iD z8WdG9T%sFF'R{FtZ$qfYN}9-VZΒ*)ހWoor( VR&1y {|XL85B\]Hp?EbE )z[pO@POArz>*'\]Wّ4w8si Ezn; h=fcb|O0 L;HTˆ:}h7V(=-8qHBXdWMl&Vn\`.E寲/O˲vm44PB6&#=<=>N 科,=kB[(0ZzGÖwCzyz&֦j-i n䁐56^ߡ& eOYagbNjkF0 &AȚ.Y )~gh6% Oo8 >06"BY﷟2Hl0P=:nieN#eE;j̵.tYpeno0|#->pMM!h)t~7=/=? $%Dt~bD*'QʗݚBsu<&M$Yooz_z~Yeۆ+1LŗQ)I"J[߮I'N`HhaG^%ۻ!y-K;V_a5E\ zlYʍ̳ =ս*hka{۪FX)Y'[Iz=79F"Jx i9*5"4bT Kk^ڢʆ[@Ͱl28]d"zwò~t%1F Ј6I/e7u2,3; (ɧ^+i=BlCVZ5.ɴ8 j p` WCrT]nW .o,Ϗ3ҊeĈyNK :Eͤ1Q {(ö-bDVJ{:z .FiW򎆝`ekB[늉>;62*>&MJ P?Is<ǖ3y3'x{(wfmt:,M${Ҙ2Z7vPl(nHFU\ #^NvN^Tewy*^s̷e1!E 2 |t8ȋ"|({Ȉ~OI:79\o0N`qNރGVTtkm"u S *81!Z;G"VC'sYְ<07py;wNy$D'7H˹ddJ\7$(+$r˜3%(?]nA;' #1d_Hv|iГ0 4͜|&sR~Y}u**@2NFDÿN3_̷M Hw'&͚1q)rf.zv k{RN֛^){:YIou>O1N^RXkïol  |r*0jѐ=Az .O"JbF k&/zqN֔\٣9 h ḇaª>_>Y/s;pI}WM_ČVuR b\(7b8spH *j [e{PäDx0N֒qj.vxiJ(v ϢUB;b١YD΍HHt`5υnyI=ELÂ2Μ  ` LaD}7v2W KgU?Ej m0 ڼa@dY "*Uj*u#N˥Eyƛ`gD-&3': g[dଳh-@)[m%=DD7V<'$ړZ7 C#fBwCAoӺF=SqЯ \ա;K S>.1`&JyS8]!F*? ?1LzpjAUH@_Y!ήHK9DXNLsddZK~W0ηAouk \u]c=턝*bkeVxt4h k @K^.|М=HmNϊ^x  5`7 3ז dRC@Vί^ b $^Pj G@}d/Zsi^0d >ֳ (/p!R/E}<6FK&:sƂYAC/@4.rʑy,z% cދzX({O@1Ije͗yk E 62)I-}U|%%#栥dv}c>bJzd[S|Wj!U.#}?i}|ZLro-Du92g$qMvxf:Lwɝ?X Oe폾[(֝8br) .fN)0cj4kNtƕ& /7|Ԃ_0/t\24@g4pӪM^Y\P/}jdQ; o w# ZiBѕdJ }?FsD1p~kNRI B:Y9P~|񺘼̱GaeŨڳV9`޽yd81̱GbOb>F%*[PW6 ϐ{$g Gf9SfƟ' VW2TPn@+).C(Ls2\gʽ줅-| x)AK2+5xzL8M'!m`MkY~calMY}*d'}9˓ң全D' Ppf7:ڼb\'-\8~C8L}/B}`%r¢?t|- VduyеyO]U#x\Te\Gu1P +K컘_$ *5iEkjwҿ**'Vх@-DŽI{z٢JQd0Ve9UPt辳Ά#+?)]ֽd&|͢IyK.<%4\/̪5mPOm¯Oc.N9)fɬREp7`o}Ycj۬6G] 9@'ᬧ j۵VT9$# T=Y^xɹeYٌ1k6~2S**Lgao&V vdڋl#g˅ b3aqAӠ Z?֑wK(`|t"k!ɰ̧&g ]~%;]U`+=tw ";`.mjRZI`]3mFm0s>s,X1{8NOrNӨ;*GTSTze`0uEH )^_ﲪ1s'ٜ2zwjJUWM ?j\C]gܻ'3~l9aRKiM9 <o/^<3+L{25n^EO[B?^>z|,{j,uVGЩ]!.I&]=E* {\&&IXb27\Z!JS`&̭no1+Dde;T<N]4B= EՔ>ukCŁ;OPPF;  +1=36O$R6͠d"U@?ۿ o.=_" oE ξ$ւpuĜZ5DM Nlr>RR%;o7ʌ d|8W*K*HPcG@ Ap(a1 "\T{_ G($&f"`9VJV˜H'OF_?C2-jxǃ?(H׃r*sXVotQϚ%l8L.@K%oh FjjVg`I=u j,v(lBA8`CjXh;Q(5 ,,2 ΐO&DH@E(jRON)P5z7$+д*Jm"Q0nۘC@+];B`cH٭bwħ_$O18c!Fc5!mTRAU]+8{POrWX%c:LʪJ1I!Ń%-~N% fO}d|7Y'QK u0 T.6et|UKۀ6?"h;}ߨ9.l1ͼɎo1,Wc\T%'9n KTST7[ Aٗ`iC:O%fќ5*U'z.Q*#,/h$NYR%EGX!0ċFF@@Nvm]SK/s Ұ?I;TB6 KK3g`F)β#RvuLӘ̃CJ+]"^XT9fLUƄHȏ1GD@ =D;]oQ Wϱ>>wo1>O҉ULlj]_֡,r>RGܒXޯO=gyuRbg bb< rP%a |Y.ͳaC =!s^Tdlȼ[ pC=)W9}v#z]9y'yl%%ɔ?#J:>)?Oo)QtK:UVcF #)ìۃ+kW|l,tS"|\?U]~'g3/B=zT; ] ܆4÷V[@g&= &CLSaI`egOxy؃EqT ^ɆoOћ ߁=ͼ{!Hgn3a%KF:xy ^Ic7zO!rdUۗ؟G*^Do,Ӆo6m_ cmǃwlDmbqzǬf'&xȈ)ݨ0NmyDŽy<G8Я]3 Uvxa]#Q~|&֍O'!2`Y :jR& & h 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & h>"(D0,Bx&X%P$L$L$L$MՖrI"$H$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀʀoxۆeVqfm+9@`=U#21hhv XN3S+e~`0vYgXB7%gB43]h7J0j H.JX0$]s&=IPjy: T%U-vQX)+&jɖ.iA[C]i_wXM1P5V5v|7| ];El+\N{R *x,sy%-/v(̓54ulB(!+Rg.I' StXmD' җR UǢDڜ,:-qy E I 3 Y8gR=;4n[0wޥ!N&2, }S1TUjЀXƥ3ܱ׫f<p)+yt>4Nsar-r\=Ay1d1f~, _JA\ f39,bN!vJρ+9 YYIXv#_ -C߰'$c>8.Q liB:Ӝ!(n4?nxezf׿ϙ}z \LX!> |zA'9k~!VO#=R}AçO1-C];2VH}!f@s09ٓi9(c9u IMa]/0Mg|{ٲܟ~D8KHC0dП=S_dկ[2/|qY`y9A6K}3%mգ2}-'ZFO9MkҔgQ2]g}}*1zz~[/f@gIiapMŷO0Xm%4 &zby90s%d )[oDAk~!VO6II`>QkE=dZ-么gQI`ז.O9>\%'Ud!d$gVb{AλtpLKfcbtteݘѷG:\qbez. +wڰCC4BDt|KPBCIҬH/^[`#MHIl]m9a0#SY2#N=m:d])7RT-A$Ь{)y)(,$F'B"K7xJ0$%Uh%0 Ƹ@d9o'q }uUAi &Ykpt4/ !>.T(lK 2tx:Kv`{ 1pm+9p P+MsX|qъ*3FjaF#8 kř#0h\-%!g 0vx뿷ɾFѱB$e\$0Go2y*C^r +X,#I ^nOZ|:R#.&O>lͨ);ǜh۪rOr-x\n/"Ħp!3AvoU,*Wմԕejx͌\N W ?cr>$E,QJ+6WTdg (-B`K$@XUtDY{˘MB~VQA7^j(/맢oe'5էub^rMMQpnBab6 <NhL`_-,xRL|6{S 6{勉!|u>#NovTF!5sT&r:hpMƮTn{~Ò=PxZGˎ7(glc}D{lUۆBѯ΁c#`uk?# uXu´ FGU pCujj#o7) D,vԞ2P>G#BieTeo r<.@W7?#~ 5k7(jO"J&Ae|w0mVdfJ,>SOIl@z| :!xH|su XqPu )3v,C#s_?^J}(;]IYĀy48$@nD>䌸ge4TP|5@dMADoTƮs[5^p *E- ";՟N#W.H3/A 0q{t$.ЛGx[Ї5FriGfUT^AA;CvK#ƚZUd^s]WىK s,f@*BJz%!NӢ`9uuW9/†ksTWY#4D UmM|e֗$-|Aew">\vz$=%W!MY6aA˺{4Qn}vu;Ӊk&pR6AYH BF"TcZ#ÍVZ Sw@׃_2 E՚CQ+[߆OjG>`<|+P BC34|&ޝqwS`yɨ 4v&'EeG?bTd` Ii9ͮ%BF rpDDT{|܀R}2&:MB-~aqD㙮yu4헆E{:6V};YGO_ 9ؚ `zZ6O8YFl V'$u*.s xRx׎?cAV੔5"O]/Dfzhs^z0 2|l/`UBOFܤ!c* X6v-#qgHR[Rڳym/B9jD@pk!%drX:8V-@K)Jѯ ӏI% P8N;4&e4f t(gkI!)5e>Ce:t_)ma/mlz $mIĴ`qPReȣuQ[!K+ }h}ߢxQlJ#t˱e- ĸ@B(}B$Oin9.,QBMָ74-,okp" Dݐ|b|-*[M^)U*տ*f(@Pw &gP)Y=$IIglbbx*[J)䜼>ŇCʫ3Yx֞}rC^g^!9& kHQ]:o/Z0Ty9Gp``h۽uaHgIcp/r *υB5׾oUהxRNO85{fgh5Qb>IϘroXeX;ά1.ȶ''E fz"P&dF[#F^;2%ij>:(BH:PyJ7&뒔]Wj <[]x#=%>z&a]E!i$=0 ښԮ4JU4],N<Ux,oV3NxDŤG/o (l\{XA8ӽK??z:zku[=x7u|Sh@ܡ'[?8QTd"cus=إk1rРO1^cF0 @ͅKEZtc9"@ H<ŷijd*4@S8 ]0#KTD^T jjf~˄pw|4ϰjˁX@;O,~"_|[xa&G' Ĕ!,`[2+HZϒU݄YڏXMq Y\.}3F_vXܯnF=a=nYʏhs{&hWBMihWdD?jhy#^}aaޥ02@ qrhЋhr|4e?EA{Es$Ll2>:|(MCi~UhQHAA"P%&}~C@SY0q6imEl5Zg*'EcF;s~9qe wtc\H;YMg([ԡo-­V_!+BTaB党x˚O³ SoB𖩲o`頢C5R!$9 9 }ޑQ2b@1uGi/ĔEIF?btYح(wbB/xB-Hlph ]Qs̜gt`!eJ5}6kHFpb9_B+2ݳL:Ȏ@*%6"AY%tש,qN^q9On Bp9q 5K:~7k7_RZO84Jf /:+a:KyKQCQl[ӓ牚\Aw}e4U|OkV$\i=ﺲ 7vpD!4C_~6s  Lz:.u6f*3ڌ]=Y3[2tP dH{ZX<&!c&碲ɫ;t;(=wiRR¬6zyI!9cUqT†hpLYo߽GB(=ƌ١spVW7[sE$Ɂ%>~!Mr9i}i{ײ%!ڒ*jk=1׺5lVܺ][q喖܅eT({^GmZZ֓4؈&rUćؕ c*A8n(KckU܈v  3{ZTO,(E#]eЅEWkBό -yhyAvooۄ>^hG>o_IJp[*Jb79[l9/~64F}iԤp4^˨?QE8Xs%}>V!* wI6TYک{b FBSZmb5%/ ׻6.Li'3,EP ǥ|5Y9H2 !k#{0'fƮ!&loCM:-fTw"m=Nt?*(sۘ-ޚw~>MPb 86،i[vuU=]M[I҈ew楜:awaQGWtd)WaoDRYo(k-oƢXX$Q#7PȒ y#B"||jzsX?f*A/_' n;B̄Y$-_/ƙpXyua,yBl&)OUuB7\Gt]ߴE*n*'l^dBWtP낎*oJМr \jEoWf7X}]O^[%3,lx2f(=6hwf>YoaVEH5dw.c_ WP/3C mu?$+AdZ'j^:Ԗ\rpz/ᗡ;ҕymNxkמxzuUo+f D.`z|"[˪uLۼ*ڢ,î 9 mrjݠjT#H 2µoD-˰ ʮdj jhab 5;ݎǔþ~(,=<#'D0F菪M ޤDž'k{]v2YQg@hMӮpț KG}y&T@ (-81 rKgX{/+]S|-W j%Bm |h>yXm Vv2n?m^^O`f* 1+)ѩéUdHZR 3KoQ nk›[q V,u/^GT2f6Q}B5ZfVe/?pKHJG t :jR& & h 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & hF(D0,Bx&X%P$L$L$L$v{];b`$BH$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀKV>3S lP\3b['pU5!Qc]E_f?NlÉGg3EXl ~?CCoեx2dR:>So5_[B@9=PEC6z[41qc9 .4$p ع hx)wX%>g1%JAкM^zcfY q{/s}1ʃyw%Y)W>g@M:37tOxSOU)Y$vz7,miПY䨿c4 ` 6A**yЉ $55% W譳?G L jj=珎FFz1K:@ [i=/Ӌ |4[֛1U^6wu==d[#aQ 6x&84o#_#}{NRg?۫V bm~Τm̮,&*`YVm BnқܡZ  ;'$S73ISz~= U5#WvSV 'R'aǢb#W;c^y$O| %sW.rQD0,R@5N]~ds'R_=nPm'BUӢ<ʔ$x Ulg5et`pqX{=1c#<ݥGAo/ TJ9Oy nf!&@%21K}, vKQd1.(f|3_n~bb:|0Pdk 6 MpS.y~y6>uƭMfc|6^KZ xzM3_Lhvla."5󝨒뛼N@[:Ԧu?q{>&. 4j}j'9e)i(?ehr?h9{#ajA .n . _pxCf (Փ[=VmR⹢PV,΃v%BE{-u<߾dUf)‡fӝ3ߧ㋨Nx:3#bIN@ȩU?*oRAAv@OF}mCxQ\W%oe GjH&7D5%F)>s2ZlSq(&(sı$t" E=Tk+se gdkBULb*JUƹ;P,|Cl-h@z.#~rb0~wĚAW?!#^)kRrasD@Qj_j-y(!Y'euE<.p<=FP/hqq1 l7@0{rjiY;yq>~3.Fl]:B,873~݃<BXF uKJ+!-ZZZlc?mgSײATzge.nVbdEJ:پ^ d{S^jA93[+acb+>'[ezJ"AQ!x3#T(Gt *2Dp ɚ0S4_.%IN*B.|uRI֒hGU+.pe]:|Īd5A f 3\Ty@ rXs mF wA2޸nN]I֥c\,MayG),>iUPT"&vƮ-@~FV5`G*,:Kxgmm$&k3+C겗K)f^1#SEEc 3\p1"{:&xS*8Ju$lKsH j%}C"uRqGYTg/E#if-UɈ8x=%QTJDvQhy0ħF n[Q?& kP5`-U_#Λ]E @Y_;a@[PĬZ" !Ro8χ:d^q!>2v(UQN;%*pԽeg5uݸ1ei&r\@xx[vNf@U& \o Y?j|VD(e㎤&DKg]3cٜEЫ]&PY /g8,[~QAzWo|-u*km_/yX\\[Y.,/r!ɳgzQt{,QbpR@e$\ìBv"ĄAH_AU*\.'avI mg<@7Í׺qsG FagTZo,uebp,8*Z|uZ= ]qRnfŇ~8F/ :ԭȁ *?? 'ůRU84?ݐ( ,(ͬk  X Hka+xK"ڥ7<1*VW~Ty4H_,rI\2[z. 0PxMCƈؘ0R,o~hëa 0z_j-W .M%n؁Tɋ|jdY `.;0Ln} ̛l"KMz<炚.tE}_۳x(O o9haT~J/5[Ń 0 T5*!dYJ\Z*ϳ"!A0*-R3ZW$klhzDܥ^؇Y>MH)}$nxmZLz~8y, tLj4lbӐ\ (2`%2q&r(R6 gYCi$3A*xϗ?>AoW Un8U 7.8jdؿP~t㟪$bjO$28ᛟR ;kh67 (By:Akv"vo8S.,viP+C:Q~#I%̏KzEn·pcl=m+% ;n8Ď4p.izS9 W|ޏ&|S?Bq2",f\KMeU|!febely-.2:0T7o"Js\lQ[S0u~)}gPJxȬ#Tt`]ZMgr?!0 6_a~2f BG lZGFCE9/<^+rw`(}C86C0%-EEU_Ia!?XBc'4³'=ck$C>B=Qk,5y0BZǹ4:h¾"b$VP,FbRŮ@rl|SMpǡu5x>ǯn?bv].#q'o kٓ` 1Ƌs g-yq<^DlK3U9DY aU"74z+(f\(y#@ҥqXm'b/:$d#<ySeՋx|mhMwEECz!S]7Zu6&mpi;g#5{%j@:}hgR]Uą0"xƼtKUWw.wdgB[ݡ+wśb >i13B.ґ)ۦig@\ZãhW̳1T xƵQ`nNh7ܹ 7,4F1D{ :f`DM)Sk[fŋ|ۃ3Y,zv*9AZ>2 Hgh#z}?Au+JoQ̫rK?v#M[9m@K>mc{/{Dɘ\Fu%KܙtxRd7]@9QjuQe)bu}MKv{[-ؘ]i|!0J\c1o}-0ա#nDQ|/{3bLJV`=$>@ jY cȬ gZ TU"Q2o8>uv4kg!WjGWbtس,G@v'f@6 MU0I[ŸfZimߥthIzJZqTB/e)dq׽Ul'7zx,%i9/^MzQwrI!aVptQ;A8/ \kV='^0߇IYiK!V#m9[?J>x/Ӑ08< I1Rf.N'RE͔eHag,L:yEēO&p ,-X\*eGԈ2;-v8݄@*[\՜$>AKo#s{tpH3mK]]9/ UdG #j3өPVҶiC))WT%_ SCKc̫ĠٺkaxBHR@KF5!Yn2I H|1ksff}ğtحHytv4U! NB '4~YǕQ|b-< j6J&$86,?Z!-BXQ_`c-W"Z|ѕӢʏ^yy[z=[V~_0h8WFq9V\s:N5JbD,q/>g&*+%‘q>>E(xs*'; ayGoɆ Q?$ӵ%?VaE9x2,yTm`#*(ؙGZjޑ,Gr/1!!j@ZFCk찹T{k>LI}e'GNli]e5rڏкЎ$puwxO YHDlX$H3s0p-|D( ^{&S f=vf+]d@k y3mo:DY ϋh=Oѯذh/7CIHT,!Ca]F >#Cya^zRj\˩kb6Bb^A<6&SI"ZAܩc-cn?C0R 2q*:s1wSIIjպœTu//oAk" ? 7"sH'76'~8/cg vmBUYFG8D+vI[CP0E-ΠdnjZ[.#gN"cnP'VhyPɨ ϫm>4lbwx\ۮW54B76/;RαT2iۘԖ ( y@<9"}ͺ\-Q01;'z6Y*(UC^X$-կF]i7Bhf d,VB^9Dh { S<ß)צ r[__HT1?a9Tűl =^!!9EpZLxD dI]S֐c䦚 g,;b'j4Z_<9 ^&&u)dBNeT8@.@S"i%0ɖwӁQu$ ͣ Ulr0u(moH3 ܺ<~>heMAh_SI[w) ka- q3rkBH@>אs8II  -+5h ld"]`c]<.w5>~ Rܛ7bQp[…Vv#m6%^t0dA|"HO^ giR WR 0GyDۗ:jm;F1#?|`n *\"ke < dN0A4gnxW)(.wC#9ؾrPHr[-QKDe*QSQjy %bzp 0IxzAg$`!!zW(\!%iMa%y4k>.arLqx{m$;˗H+ךpg?PiHBK*Xݖ[a[f\c@qvFRZmgVqzs-3N|A=4+SX$*z} f"z( Fӓ5Մ>i&ZkJr9]'T/dQ]qMBNy甼iVoSMΓ==HX{ÄmM3SJJkuCEx Slh= '\PF IlLJ~ u"KrL l,~fEt ^QޓKZb$e=*N?ƃ9dz>K[KeKʸT @fGp-z !)=b&ǴhCw?/rwW`#K!5r\9\p~ K;eN_}ņHKO,:㶇>6#g|ga dJQˁ~ |B|84GT荫I>}qxsk;}by%Hj(^TFE$ :H]孪*Ғ"SXS0FΟ&ƳS6>ciƂڤdF^T<922bs~DYMG& _䰅ߤ+/ !F[̣V $)R@ d6ZY Ĩ {4z˝[|M6[H ATj -zћK+ lrJ::4 Kl8LY5wVfS }Oō'6ͳv$L>Xl7SWl|K0 >lgap%Z,De?5,DTcKtp;gUJعc%I|xӁ!YqWP Fֈx|5\ogEc7?6q˥갃. 8UeXo'jl2mDs\OvPۺY8 yi3s4@P[:8q u)΂q:'_1ȓaq>̮Mym j[pKNܫNJ8|^9f dJhvT=E}wV`/XkݧB9a$S)c@[ùu%7m->Dn- Pۏ?M:Q!jg&y:<1eJ8..8}\ցydMSqNbgov]FzoK-Sp_٘PQ ? 8̖zr9Nyb){%Xe5z)VB6Mx#y = oGdh>.6,G\6]q sdXFβx[~L?${ʌ3j xte2SHN'%2WH+h%n˜Uş0w$ t o8s+ B+>jFŹ3$ nA65æPzxXnQC JLrlB9&or>`"<5bgWCz%g3%yYVòlK[~c@prldj(FQUD7?H&$~XS 7vev\ ͈Vּ5|;$1(!b#Nnyi44h.70ܐQ쐆"  :ӄuIpڕ4Myύ(NTF -7,~@Qf#U@߃TX*rt_rzƿlnCTL D-G(̍ta@{iM/a VpDžoF_4avO(;-m}{vTNw:aw}͘k)XC ݜqYjOB*W67%dQ(" uwuJOHI XOp{ZQ 9%p%9NىC+9~@+\vȃtpg9ag>?)1(qA7U[ F\qKVɿ,DvLSĸ-Oz\G3ZR.i:imU 5p1i??ڰ&(WOͪWX[v |,p ̦e籕(XD:.=vZ#Ч)-F}b!t_0>%x(^*Y^6qaoa)+GY#!)ldF >@n=@}ƄsOKT}89(Q M{33m6y[Y?X;,^ft|^%! ~k >qgI[G1H;Rhqzp _`[*N\CP]1 p07=,6 @ :jR& & h 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & h0(D0,Bx&X%P$L$L$L$N5$BH$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀʀpzV]Õ ɥ3gr\:]Hjdr˚Hj6ck8Vo6 ^V(#t.oJ<:2a:qt|2>%ƹ:lQA(CxrdbAu7’dy@Tai"S`ڇ,(F>daq=z.]m6F]f@p.LgQGc0q,\ }`q?'{~NQdS,uB-s\=6whse@ QU(cS3T~Iw9 nJu-x)nǠ5=zzA]ػ(z| -NwiN,д< o}iowɆs7"BDE0i?39CZA f7ϣBb#8l*n qnZR1Q>2(?,}PtVM#p1KO771"ǦO@"Ę|q.D~}g%#b 9 |읻wpeЍN}k跌?!<7Oj"pbujet%?A%:,GBZ,+ 9G\2k[-Lt_aq~T+b 5oX\Çg "z{[IeYb[$-s+i:0՟+;:$4d9?ĔpqY>c]zJPjr4؞9= [Mœ,]Q8|?2 u /5y"tN^~gOWRv(0,:;KDH Qů'BHWb1!ھq˹Akbfz'HkGnRZhf:NƆV{ioz;Mא4C{5v=x m%v۹5J Z i堫])SlLQCJ*u#fa0_ߪ(Ujgxvguە7KUl tp"Sj%څdt\82I<2Cy;ӌ"[ܬ'Z, $[0ɣ+rX2[v ohQ{KBndmr#[jΥx#uhܵE$VLܪ]`'8uW=~]3KPAB}M&o(a" _c6w8׾7d 4 ;'tΑgg96U=3h &Ξ"tiT/{`o>dAܥn` EZs1^ yrNᓭI,w|*P) RXD"=}L@2 j޽ ١:=(RֈJS:)7/+sGިbdyk^ { }x7@GD>_t;ůh[{hdyR~ "оH]\M&2̃ǻ&^rJX.8&TE{66"Lo:0õ` i@_4N4SHͣ,BapE@zA+ ZY#!#qL@9j{̒EcVHT:ewǜV[+JLŚ\ꔋfRt;wZG-`E#v|RKw!v&?561^W:(NIlŒXX~(.n0&p~|vY! 2d@ J]={g fTZ 8t. i"70~x8;azx`ȀO6G$V?[)Y-Y6ęao͒A԰1VޖK {>) FD}]e*u5BŰo!̫'I(ANoY _;@#&Ƚ׋ m1 *-%rݤg ?lK"VuBlK/p\i+ϛ6'g%sr{O%$ PP٣o7G+{Er oy6[+2>tݫi,QA;lܼ+-׊4L{UYo((<t1<|%>R&TfP \U >*a?՜(STQt8}յ-,'Ҳktj Ѯ,#4-*EYQFےoFtnHߝ7Q|HB4D:mg 0L&|?F ˇj$A_~kjΖܹ j'D8&p-Ň$",ig{igj08Zȝ!pv%F(+G,3]xsjJv3KW zb1խYyIߧ=(0bz}}2+0;cD%<אV o.BT?͒fF1Uq F:]!C(bWHJ%iDtSP3J=Gޑ1EeR[G ! @ |=W2كZ, ՛n<-326mjDž屡Xyξ{)ؿJaRs\mnh#a~#hQKrbe|ѢXG趾6#{*Iw S՜ FgzjFi3ON'6NALdL)FDPOpeӞPn~Ҽfp^MMutpRsUFl㻛}*x>Km[.Y:hNBןsk8PAKPH=Z{Jϱe @Y! Qq^dV~^YsHswV'W@tALgy@J.bygU}r=,1Ewbٍ/xr=* o=ac)ɟW'jCLH))|ܪ+:̌-̶S\Ӓ{3z(d(+|g6tQpvEmTbw%*3x,j%xbRE (h8gXr,<fl`+&l f@4\,-3Lc>CŒ&K@aPěf/<`Imzrõ FU?)ɮXh‚3'IUz)jbvZ"F(rgW:kVuR EY!q!10Ϧ~uC!$L:!O2l}h2LL 8ꨮcJTwZ;G W, " vhŮ#M'>||N 1蟠 4Sp_ =Lhe?Ð֍8R] zf`3Y:U~{ׁc,P Gq ٹƇ`G%0q gfG^i~9a"rջoO>xdp"  YRɿ>9EoCc(eEt003 oSw$skT\ #5T4'CT6$XEg(߹Y\=zQK"aiZKe y+!B|Q'a=L ,wDdPDm%.';u'4ِ:-ksTZ}h%rE\e uԞ JlX33,@*2ō>Jma<i*B\":Ʊ(BQ] =8k;&}Ljg'IBZ7-vאmMn2ߝy2n(d6_Xrh[58:hP;>+J;n}u0=U8n1Yμ`$ϱ RM`7xֿ ~W@H.Aәfϟ `קy(P'Wx9SP<sHDBlw3}E SJe٤;kƥ0'j6NtϸH/Sm"4/~=ǿNci.w]Q/!8Fwv,ɚ >Cꤏ tRu5Mk4f`dRr(pVq|%-_C95QSF>_<0S;뵅ܑ^S,OŒ=w&UU >9HvFA!:6WsP̺՗G[!M=Ը;^ݓ0-|bh|M(Go7tM*%ebmX7Ž焄mxur=zq/*Xܬ7A~u}fdrߡoї!E?/u`h+Ks]x{KyBںo+UwA"ۜ9K('.paS*y%o7H wE}M"?f3YjAC NldH@h4W,E<L$}ﵻ*(4JG[ :jR& & h 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & hI (D0,Bx&X%P$L$L$L$l d$H$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀJHT+,`#ݤ`Ef%kmK }Q!R?7ry;nCr%X\!21R$6Q>͏A6bGUMg`Џ>mP7Lv;F!9:n|G-N{|UiTP$6긋j,jA?GZ֣SeZD{1vȌ?Fp)4WU팾=d?"]Dv㎀.>Fb?x+ÂdgRa%( /1`K"'B}|Os7qM./[ cTPVO]vN}IR`>1r?vvDި>QटhxRьzFP&81ŸfnQ:xwHl0'IhSHٚ=S `{p|{oMoP)rIA&A\p/n~c?TF!Bo$S.b袜2 (xh)G WG@\| QlX 8, 3ͰA6eSg>(}D;h: ||,%gcUv?^,&nY ~lfM`Hk{Bd@g[~YTAwfrUs8+7nm3ء?`SRC, ŭLxgE4iVo|}I*eX&gl N&l:Vkb(qk"Tn3äa^7X"n݀x/qҳT~gRUcX;֒sTӶH"W`5BB#d`;tG}3HwU;DD,uNSq(Os WVR# }(뾴!m;|^&S ZACbfP뛀x̔Y<>.7>MR{D)52I >N֓M^0z9b`X#(wN_Cf%'G;-"n%G]*n I'q$X%CФvyK6HcbFP'KI#=Q^rgE8f%/NIIVY%^ JRvo@"A 6!ۮV[wu}HSSG[u(* iv3c/N#}Pr#p6߇TgM޹ՐE_(],kj]'U˿ix1mO 5᷿qR)0Z+Ië 3N@!+n;\+8}(A7(*g[TWic]Y99Ӏ[?%}/?PV\_m٠mz;%oIA0vv` 8{֕Z0 %&}7(tƒ,䣽AJw'" T]:vL2 8HhՈi\WX0J%)ǏMwb:9S4S'-D/!į̭$C܃6wjMW}|euȲc,լ<@6@#%Rd7O>? U9$~c5-^{")s&.9nUJ[=.Uf ^ p:ƿcҔ\mRCAm_UPҺS[OHfF2S Kh4Erz:4OؠsVqvf%_Y0}!6 d X2 }ʱwl@I1lx/$zWRQ4%uؑ](_?}o 葻α`YvV cY(]:@9<տ;Rz:'bGBVB*ũ 0y$2 E=uieOg*cLx'"Q[OOP/)*1G"Hxҫ(iɯpY@܈$`TdGc)?sM_J= Ja?lhofVYm}ߝ?xqBΥ_&=W-$pei64pI;6V*GtHT̰}} NrBYU4fxK~'$Dl5K~<_K*y4\Ҳ,-X7mS$`iNT |OfU2'U0WYy9Fnؓ.S-gXK-ނ3UND;&yߨ=*+aQ:CΘlo~OrozA孋#s箄dr )Bϴ~k3abN2Hڌfk]^ftUH?v}g{f _B|ispg՘(+Oy*>б9^ㅊoyf/#'9u?ܸW̎sl *K;>i?Z xLﺠuEK+r#ILDda:2G˃=T*D?co;'NKrRr6#/c g(YWP⎚Tz1lµ.><%Y7oڟiI$eʶȯ=H޿KQhνM(1}6@\c;9@C$-7˕[q=!/i ::}#-?8>9vEh]h &n jy#YK8&ԓZkz--$FBϩ& OeI ;NH oK-~6duY?~ܬk:x| f7dyoʦ"޻ΗpPBřQMOK+cXFФްr$qdgXAyfG%jҘ\U&uOW*YAa}[-6bc!50w4A[% '׎ydƍᾤ8GDXцO^;߼L9\W&106e+N=>@ɀ 0pkؕk߄J+0BE?^4T1[+̵Ž H=5_^sBgw 0V``u9"}ChkS 87Rbq1Gv "P! aD4ㄥ%ҍ̗s~aD(wOx-<}6Ffi-G꿉!7nMW;Z霅~ށX\8V4??h/%'R"ϻCnYX;1ƀ 'Cd{^C,&-Q921 X(:W*O:ufN>ja3cd(U\ H{~$/6hS])$xlszr; G2#׎JcJ:.%%KClM*+~F ɯ7=Ҙ'2VR{<ݟO^jaoUZD*)ŗzL/4lOC^Sg-B5nUگf1ablo;IHu3fuVp('x(:OV@h*x QHc6O+Ē Fپa`"P; κԤV].H#jP̽Ԟw1?Xl9ʶ?GȮqChʾ'Bm {- PYFm'9`zLi5V~ ؁H0pʀp0#<[KHV_Wьb;L;VV52{n *%rȸIT@haqj Acuc e@*5ެK[2"55#!["5M  `PK ^0paV n z5VI_!1黛_b6m73]hBmH E{mBSA>Ž_KOҥAYj+ܕzu#i.acRLHO+ nO%h)${%#w"by9!zkLȚ)kxY_.LΙ0~sT&b` |]=d,H&52J}nI"U)7rs52Q{i`R(c~)sډ$֭[J")&0[laљI/L%lP꿃숺x^G:+ME cXĒ'k7u7|t?sYC[E 5@b;LXrz;a2:6Ii۔H,U u0=rvoLn)6GA-Vc28CCy_2Q}x 7f ~#==hĠ zs-QOrʼΑsK :~]|L γK1+%P(Ü%6DGN6z+d@!X!Rǟ   rseŕFUYΙB#?gG \'CDN68)<'*Ggn̋cQšN87qmX(`xg`jp6IZ$l0ǩxWYsb+}MM‡B++ZwP%=\⾊sa#WJdr:!FİL%<_gQ{SA F#G5@$׵Uh` ;Ee6"kFz{hZV.Y%OtO&ITe(HO6U9'_N4LN@c(5[Ÿ/r(fBN}5H뤫Lq ƾrjo=J`OWgB>̓yRڹiO.EkŀPȦtL^iAqC% X$1fVF6wVpvvk (HF T,\)lXوK&J .(F410\ߦظ[m,OpoH0[pdϦ?Iߥ>;qz|^DC^;d1nmEG+1wQ#AN$o*B"~т@S0hS,tMhX0H@Sy{AOX6pLb3*&JuwbψޜpI>%iMـ0m;]< Ȝ;,cI7%b*Dg'[]fy(廨Vhb8.E9r;~Xşw4/u0{V8I'S+I5lYUp&>iy*ezߴ⪀9@ n(4r.) pµ K%vVp܇6 glR  NH85VIBy7b7g#剀ӥP>*5 ˩X۞~,ε"¿/^gZIL)Stq G ,~nBr׬. 1Ty; ,/p9XV\~Uf+Xj7h_{)'8\Z2Ҁ&OV/a6&a\#XͲ>jeW W1N9቟}1]$ ; >#7FF' QxٚaU̐Dm -w?sNUUXYrey ezy3\S9d% Q$2ZJo끲a%#'I<9|sɐey뒙Tn]V)0Vd-;_ON6)i3C^.>f؊LFydFlF{86usQz)pk00gټ>? @`ĕ(4}zQ$&/sL.@6.g9+js E eUy4@`}2\vf!$tMYI{T'GwȏnV})iA('pss:0waeuSB\ k68U7d=]&qMs4~fִ 4jh@nģE{D޴޿q>4Bs1q_ts(lb6i*'m};Êr\ JA m-R,٧sTj6KbüNPi=T&rz,|>a?3T^]sMM#7aXBpˀq|m_6a-;k>|ȋ t WaFybp1 M@<9j`׶L7 t=P"ŬK9K39#5P5HƹAܥq/fSp(~M2n)>4q1 C>(<ק)$ 7N#oB>οsmd4N鄕nZ -cQ:|ޜRsurm&#c ;@{rWaD`᣾_֞G?!(טx߸@ K([옰G7Wq'Z7x4X@62>k{: 8Xt A Wai(Me] .YH;3*i+ |sI:PLNw'g /ERQ@n3Glc"祢:|fVvw!VGCB` wȮW@{!/{Lgdml OWhKXZ "ݿ)]@1;G#.x]`=|A5Yy]~5Tg\_䦺ŝ5dGQ& Z-| ]}&M+ϕ$SWPŁ=rҴ& ś:[X\4CKb\(s_'41 ; <.{nSGZu5Ž5I}8iEmlyu%:s8sD}U5a+k[$&R֔^ٲr;`nU/7~RnRqqdUՒj*!( ؝j@߭ NۮަNXCT`D"Z4LBn:*{ViR9йNw4_rQQ47z 2o Ib'2" @70pi[IY^ :P=ZխJnܚ)[7;QdURM e៸uy* =F?=L`41b $uPص\CaS@rQA Y 9t<ÿh)RG21 7RP8W@ eDT9Үvbl3vE^_&#ϖ܇M#}-\؍{39Pwo!3iP?g@n@ ڿdtĻ&`[!36&n(<( :Elyeťy\d><;jt(թ18DEzüF3YaHo="L]N r䩠UEh~m4Ϡ]txN*(gb8)Yk]؋]ʷ0<<,*/je朁3}' ~81TBF%L9Tn(w=զ*ۗ鬟K5:gr{KjJƥٜ/7>;]Vj[Eݬn!EfBfo|QaoD#>;#\=Q-05Wz$R(uo&<|/_KĵqFၚ+OP}DF-/JTFҬ HV.'2X~5gn2Z+YtT% (?LH /4D6w9R=d qǾff # eǯhXc{8xrʲO =}l: _e_PBϓzŇ*F+hK'~!-BV1FyH?*/T)PB?[`!uɌԼ3GWSyr3Zd"PzJlj||wRWfY!h=;|JrmE`";*)uaE<4b| ?!muS~ f /yA;8Z< 21wJܥqE{R(ؗX[*) @Uvrb` QoP,OwHx 2z{9vB<C[% :$rQ*k.wc{:ǖQǏ#h *]Yo=q5v >MCpViIAU+`Hv%ߓWOnEzUtm1=j-;4HļNp;,i-4 ˅cuQO@t .5)Y~Hk! 9^r|;8 봍r9NyP i5 Vqla0]r c>&@G/xHk.DG _'DTe?&6wv+6ɭe·p'?/إ*7(m[")~w!gFWK~tB}:|}v=SFhfHqj}l?bDb5uZ&  ʆ&u^`˷:$Rk &HW95ᰮ[{u*s@r\mY٩#xʘ)fpA5IW9 T4H&WOlr29/9wALkt"3!_˔K@nlL泦"-X}6(:G#$_ MP&HINމHpvriI6m¾XD^BgK;Y#x"b&x3/vT"YF +ݶ3\Զi<]L[ Hɳ? Dly\n[DU|3ъm!J)s7 e`VrًW\Tq:zCV/LRL Boo=-l蹆a?y3s{۲ـ ; ʓ6|E]L\IزʹvVntעa>Fb^e ?_)|fHe8z:Sឌt@JG"W :jR& & h 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & hF(D0,Bx&X%P$L$L$L$xN $bd$H$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀ+KZZzcnL4/]J^'|%k1 @eRM 7+nXGIx's<z oV ᇂm~[Jï*(ӵ0,q^F_ {~Xad3nFk[Ê|xRA~BxC/La0H%(1V'V^hL7Hg(D~r48QI$%+ż;)5ߣ)+A[5V_= iqۅ"I8G@?%tx`M6쨏{ޫQ0* ';fH\"3nJKp ɚ*`x@YWx,Yk= EʤgfEQtE1al#eBi2.5RPC璒pz`%3 ho7ޞ'IL@N  n 2Sjܦ<4l[ŞWx~_Z>|Ut2oy=`ZިD#r͑?HD~aP7A(#}I~!o4I\sB^LQm `\ 84LA*ޠ,f=XlZU qΥ| ³M<,պmBdFڬfg1`5D6՛P ~`Xms|9!Ҵ;qH?o("({l5o}acwͪAI&} 8xܮb -4\v$[@yM G=rh$0zJ/c^ f̝st_ C&7p(WRȝu߉RⶌKUY}+Tي BFX_)XM3/:LD T=jX~g'觎%- ƹev<XCdrL"8̻ɕ%qaX \ _R/J-ipZ6\84c9ȹPƉu]&lsFSt@Eivi3shgʳ3pw@ |jn6l9m;+ed6bMOWOnM}-GC{)kQ^*BAmOko[9Dc)Mt-A2Oa(YTEF:yXZYN]ѯU/ʔpP?|܂̈Y-F|r&57˺r1ȫ#3+/fK&:1h1[DЗú/3.^3<)Dȋgl[zMq)w,GQ'} wb%c~]zyǦ\o ~]ubSN#eId1|e9=6 Ôk) c+}ob%p0Q`hQS(t3nyvʨR:r+HǍBECkؼyIw߳VL8M_s04,UD`R¹C zqn=ZHW<: TI2y{rמ|,!;q,Z}xvE(Lm"DY-' |.5(}W'ιL1ihtW5 AÁ?Kk߭!: kuGJa*owr7fkH1~OfNڈ 0{-I,Ѣ[`ը{~OpX>./On9I}N\ 텰FZИ9U[ҴOfS $FØ3M +-rd-]=l 0w; ȢsQb1lX\~en6N/@w0h`l4B&}{bB0J~ 0duRrgnp܊. "F=˴?YA <#:c@VnK"06=@gA`s7u `dx r_k|Ms/A D̽niI0 Rd;cM==A$1a~Nڣv%٭Vڰ8;DPHp"U Zӛ#0Th0p_cPW x{O8$PS)_)P18P,5G;:/۸S^ @3ytbS47Oomb5S6n9>Z~^[HW$> 972){5V2`ep _&9gRAkQ?j[Ȉ $VynABo>+6V^=SmhQ[粱 #"˭8e$_yX/` 0tl)>4 X\ *΍MV((xEK9]&6E+Tkm: awbK2OxLI8ϟs穏6FP@YoQ+Do* YGJRur^嘬6P̷|2gwRCrlFD:o֌F@klUO~(cAwtJL1!-|ʘ[S eRUؠL",^?,{~$D |plf*?r ]&9аSXG3iՅ=:~sڣf!1/ӍiEw&#egm;`~uɡ$:\DWPy{FM Ij"(6NL?-l\2wpYl?XAdB^χR\ߕT&Z-UcjAehe;}4H}P|9\1A54O/3>n8j_zv3`B-KBɐ͡k΅(}̛QZ^a-BdYF^;!2RͦoჼtHkJzgcw}u) c dRJ,hH-8E@#@cn\;]vy@h/ Vgn]ּE g[ֈ4FC(u a9c SVajaƇ:먆JhN"[ML&WR1ozeZc{ yV}fCmA܂)P K=},k<RODeBh3w}=ml 84s,?Q$0P̃HL&k[J%R`XԀV>>1ޠg`ڨZ?y4Z)ƈ탗+|>Pp(QS Qƛ';!(jFAABI[m;&6kf fь'6Eϥ$(QX 8 3*d#%>@@IWD7GjG!N)*,I(ѡjwx\n̋"Ns*k608+BzͩiA)EuSߢ`]>@N;#lUl0)+p[ NJ аQ+y&(8(1% >+|6lH Q lR? 1郪ͯ-&/JXኹ~ wQoD[tk0;QjˡC$BbD{JsSCB$Үd~]tZ9H6F)k9ʫ#& Qjk)P˕1V4 \ZD|4D ~Va؍b cuMY&' ;XHڋ GaGE4]o^E]G牧`WBœ/Gω̸%urԛ!oh t+(dE# zAre|_OII rf/{@?` oX ׈6D`t E>Ꭓ|v[̾.%{,hrۚLޔ*L#g!0+Pݎսky;8r>xx Wb]%$ <=W4A∟ $)?+$VI@!&uLcQpB%Am km|Fw{6?<dI "m=R4%4y?Tv>0+71c#w_{䪔- +(wrZ6@f#~u L&AjNFD7踲ID^u@ݦЎ,vL`ġЍ\aUe AVU>_Cy[ev1 {Z%&wNS>]¿Z\&˃[ 7Ŧ|,% {ovz#9܆Ɍkn݋`)u./6)\(ӍƳq596ҲƸ_Zuc[Q38^&#%:70BNkvHdʝܿg J7bz5oI]اgIS*F.gzZ6'Xoc(y[B / C2`h+ Sc{2A zo*@O0 keB[ LefVnuՙ_k _٤0͹n?l%wEGP>.&-yNjҖH` >J]pMBq[\Ef-q! JF5)?NA#-[NgG헇1˰a :-㭡0;yxK0 ~ҐQ.mb! ] \#gE>0#'ДN8 uV հWif:]_ #6&#pÿ Ӳb@nfvH]2MwEoi1=ts V*]*Wx^$3]z?$9SfX?1a06H ÆN:#UmfQjL6{«t&}9#^7g7Ʌ qnC=A|Ktr(~LS^|E]FĜ073uk t!^/laP80{T]zw58]+q5|r6_LAHDP>q.Q#amGt4I%0Tn'1y&+ݮ f:#]S3ŹW.o" g|HsؔX1gxE'Ȯtp)Ee8{CMSdǯ0A*5\l|^_feKDOQT?ݎ64Jζmϑ}- П_P_KH1Y;DTv@0k@x{dY fJ>0=.9R__hӏfuL@-2`au\x9Pev ֔=[l\Y2+<6{m(+K1lIUAee c_׏(:MPŒˢbطog'>V]V_i5JžNRn"u5ݶ[kq'O+W#Q|ev 5vWiI\v݃;X)moλO5"ՂY{C(R|x@ɮiw%S?mJrg[]*g}G{nw8CyS2,eAXgr!>0w_:p7wXPߕѸsSOK 4k Hv+Y<@@(1.RnQl{]N3lze9;~ X}"TeU0i0MI8Mp!ܤ0و $<3J6m )M;ujQ-hSes[ #)WF-i)'3ʆO$1pstMP&ѓw2vv/Y ~E8UM1R0b(6R&B;\{3QA.F5 ezq9_cA@mr¢ |A']>yo!<ў~0!LD$Q:yUӍJYáoO1`Oc 4m [ȠcJ0vrGŋn rNh'ճ}yNj9%?AV?T-T5"kƳB r)[;Hd?\/ӇA șyL\u @y⥼SC3Yz8$]'vVigCt_+ǯx"y3WBn:5!yi1&5Y5T3ui?Yl8U%ԲzؚRM8R@ xJG t :jR& & h 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & hP(D0,Bx&X%P$L$L$L$)RR$H$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀ^P]t*:=836(}}sZ_pq^!]bIu{Mp?79qM]'sI Fyц K|<|e)٤Y|Fj8E22 "Rbrk'F< PIWhozOvrG ~9vA|5zDbm!mש'n$ 80"(Q a)OqKEѰg7E'twij4ԲXҦoJNlOKw7LջslS*hIPښ䮅xhL6^D֔=dF*t.`ƼSW*]['tõx, / #tZܘ?d~i Ol$rcDPS\dB 6KAGGy-bQ+:8Nak,~%2`=}l R`;5ޙwKIq ƣzj}KXVJi|Fяn-ޓ&]^ًg61{soCat/5njz6 4ݮ4kf2 #}|5B8'ACه@?]_I01o W>D"%iìMbO0Lr2/1 :>2(Za6ww͹$g:8~6 H)26)+PIc8C>e{Gk9K;p:"VX=GεRf>ӓy$UcT ZtZPMS錺F۪JA%M˳oȷbtd@5ZXt$K&FbRG<`9s|<5_}\_~ef6s|aL6ٷ.䯴,":Z@Kup{&M NrYd7H*Ld r[jzR8!]} /6 o(:7]Mo3Y|]{nfE(w_?xje@6V- F^VY %1H|E#&ǐIdĩ0Z0ZA~o#.^Ta/38tx =C/@wiiYsWc؃r*8uM(-JvY[rnmB+Kn/bTA?Tmdbq\w؎8Hal$I򤖕H,+|m)WLq3C@NdZq@ ?i(X\6D]t$~J$8hCJi*Bo=a.u?NyaE,o#7ti5yZb?%֊JJ!pJcQ܌7 A^ZӍeHA g9f1$otFsߺ?;%iG((&fRUW?h½{WTKBw^@4|>c6[a[H _A?Bߵk8H߹>zZ03G }_|5[!!9܆;zoht 4, 씴J$ J㏐pW9%J7]!0Oȑkc<6.f(}%%iF_) s$hs(͟U7}ej O9?Q]3潨Vbg8ilޥFBK{4y{|"G$%gk!N6c@Ă?m輪~&Nulc9tcfChߢ㍨A}:JFKFq-U!l 'hYboUumYQ6u}n0(Ǩl_b[i8YGЎ%j+A``1fJ3PtAfw=ܮF77/evW0/>y|&t-8<k=Gtj솘åJ3LR:0!j1^eB$u쭱2/iWm|ܜ໧'0v^`zuTPhsl0}WA|q#AzW?gLi0ͫy ؂,w ;m9IvտJtKtl6 n'=>s\,ݱ|j5t>~ClKL)ˋPs$cc 1xaOg\#0,í-Y,BG{)Z]}ռ0=PoT qHZ% @"L{k\Ygn#²b ?@,ٱ>A*NI4رW9r"rƗNh@c˞s`S]D @%.da;\D=ƿz7$h4g uѿ͖"%zLf #x|=@ nF~K`ݴݝk?JoSTq}DP2[I8MrE5q=bK5[a-2.cSe*^82 \tl+tuߧj[~EwPPOu/x"ud[GfC+x~7svJ(B:jӋ4RJq2i~_On`r*bBifl˰pl&1R1|PC]-fn|9ܓxR!ns=餅aakA)V,5Y%%AR"(h_lBf[o\"!xy JO->ܗ`Yg: Cb,q, vJ qq+ZZu/!@Ձ2JVRȹwA>P07/d^ pt)\ _4"0f ?8GwxjO1Bj[qz)43=8e'Hz6CꅎwNkt%[;MR/Kf*-#} n#.$DtVȮ5ַ9Y\]Gg `kѮCma 3V}@ 5 +(H.knԾ:\@ىwFaD{ba?}ݬS>(ﴖj$ i; xL%ϼSͷ۔S9QkykzstlWrz HoMxIMQ)ZsCҧ?Ƅ-,?m@8|]xK`xz-U?-:ΐ:3o7 _("~}1CHwjgIGq72rq#T51*ٸ b 5{P,D %=þmĜGwT.`4-=tvm\H jB)  Ubمѧ h,~kkj(c̹ؤ!z!3t(IΜ(,W ۮB'c;bCEoa\,_#/vﱖ:(9 0-wVj02XڮGɩ/kEt4ҚGC4=(yEѳ^gIJѠGD фZ,HS͎k/WHT_q+ꮸ;p cN0}OȐ:4Uiv[UOP˦V{Ύd6 Bf[?ʌ7uQ$nu9( ! RwYͧh^TE=E!-{QOlNjQ4?.H@kOv)M\v2}'THXuP"s Dϓ&nXcVec'~Zy8<:wS !躼wN?cJN6#uqjrGK ȃ ="LPN(*Dqj,klOݳA<.E]yǍ0 0뢞`*ͷZq P+fjI߭&;dtSZ fԳ,oEl#[$ `j@0cp8H2(&q\뻎tPY{]q}WX BqM% 듥0.]{Uo8.xtʅk&G:=kec53T8ogGz j.-c]|4 s[hɳavբMfIj(awABj!gTſ4$.k4٦=*4'6AG;u4NԚ!,y٥~mQy:i%ZgY@\:ʙ[JPVBҝT] &+Rˆcތu1_9p 36#$i?4!0Q(+ſDqG%uh:#od1y`*UUDiEkVeA KL4xA Բ L2ڜ1,B{.L_`|M`SgvDII1P'G,ee`hBSrJ/+Zy]|9Abp6ޥ?0}aΐn ʴ+/// xI<{Ϙ*6.JQ+N%us``8q̩m!1x@S€giF-nmE'īU7 Jv,nT7m䬞i$(eG~׺EDŽVOl/'J׽((6 PQ2qpUqm(1ՋXU+?=f#s%JCC 3x=_ut9><嗍S %y\&38`F(b1>JT,<;;iTzOqD+I(b|BA1DM 1c$\Nek:b~ف<$AW)kd0$ Ï)/3& mbʦ!%c Kب2Wvf/="v$F dSTmUq.9#!H.%yVuގ)F8 ԽȰNDgBC@vD_{w+#ei\c|_`#), ԯt#/'پ]d#)Zcn#/ߢͲ;.K7+;So#ylvZ*ԌUtqz0$>*GLz@ <&D+U;.kaH7ڀׅ)ڂ2ǵ5E>W?i^~ݲslI؞8$Q] ,7IrrWS}RGԌ0Ų2Vy~ CEV+()w{} lN/k'R6GKT٘}-ˠSл@H #r+ )+:K :֑2o*WV8@("EC5KJ}T 9Ylm`S+џ"@Qڡ(=R 5lˇ)eI[f̬%o߼=-* }>:o'z#-]2 "OFY0K_ɂY6->,l&7t3A OSR;N:Aa@Bjr :cLg~XGAjWm]$ҵY^pYor}E,5;9a%×^jGbqWcXȯ`9xliuC!kb^7;ܘ묮QhL[Oc`3Z xӤXh6}3~wxeGVkK$'m')Q=L {t0sn۞\ k#j'nNOCqZ)gd_i~2M3s@ׇ!|V[0;muT(s1qP_3\ $Z#nY -G}3;RosCpRpShd*0A#Εh9\dއ8aI-3#vIEPW΃4A8]ɨ~Hg̘R42+jL4 KղSejG#p7dOMLͲzw@L^ 3bRMʟFl|rx@F(QۿƓ*_Q;'n^3x4G^ Mz{d>G$a^݇DF .$fk゛v!pm hmgsF~ymydKO#Bz媕WgPțI%W̒Hhɖ}:ȸ EI E>/W*h bK{5)vҝqoGyXY(\6B1A]yƔ~Gʇ[xO*hbj ´[bqNn7)7pXUD!9No[-{Klh K e&@oqT;߅dj{ C͟Uk1{oFOMWV\]ҩGok!Z[uP|KaT -j!+2a9;`>mƒ?v֕ pp:E`UA H ao6z @7p}퓏91 naq:%Wc.j[>SE"INA•UK"nUH9BjEW swS? &~L< =f_urn@mG}Y0&׬ЙEUӬ??;[1Lm@ʁI8elQ' :«DwF] {{.9إ@-RTZ4}vI/4KBuLך+UKuM}_Z3۝$D~YS?3??t *^PsU{NwG}ɸX? { |/{ U19, H6nWa,8[6 ͯ{w/hKUFi/^t]4X?3K6 O /1APUk`!ЙZ1qf_;SƔ4LJ[w.E۞6.E$o6*5tP,@z:o G{ ',p䡾`Xgۉ*+ps[`1r tUEI9k_y3Ajv׏"(a*^3Ҿl?nd4NaYiܿ@)e O BJ QS=c9x,iyv';6jP,_eJ[GzO@9{-ԧ &~$#nP^)21+ b6W$5.Sġlnk6ȕXb{ -Q.Ԟ -!1Uk~wϪM\T]O1;-fev꒡I_np1x?9 jj_CDȜ U*y6Ee Z lõiL<5x *ըq^0oȒ$`hDOp$"վ ~54+Җ07ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFmgd_G DD:]?xSi*l%6}4;r>mWd ƗBu, Q,;>tNy٦'(o\6 "y _.'6ZefXH9H/XN?RҗKbYqGbyF؝t`Oxī1_;-[v]?#|/MtnCn|r 7}@K(Unm@- UxC3xv?'']r |Tӂ:Xٷ [KEJp2^l-_u.e :Wǣ>cڟ-Ce'<`Cy;wdDžu ֻ$.|Wrn>BkOYo). -rdȔBXs9e`)ma:!ba:-qk!|SAI<\6c:P%%<:z굼h#Ip3arbb*wd*u̚vP"(Ͽ0rl{qɃ(s+7^I҉DEA %_p 9j[$e(0){,j8!.8Է] Tb2Mc/`i= XM:E x<9kw$+chrS~NgO'Y%/Jt 2֪WFPvW>.mȅ9\IOť}KOEeMj6&[St?˜}d?x< Ojjэ#wxR%R䰸e\#sj.k6UOql06_ HXa,SW}iYǫ1B:K(d1[W45@K Wa&N ߭$lZ V^ y.E ƈWżR96ʸGf-az%sG{ik: h,E"5Am/x!&)<-Җ07ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFmgd_G DD:]?xSi*l%6}4;r>mWd Ɨ,B[HϯW>tNy٦'(o\6 "y _.'6XzAкDEDž@/? bpHW/U7;{D_]6eﹷ\=q3-[v]?#|/MtnCn|r 7}@KBbuI7sHI\R[F!V xžnX.H6WYצw3c]v~Iʓ0&߱Þ_V(o~]yAY\uI`AdV/П=O%1;ldFjU#rs#G%4^=k.4D|d1|Qt@X YҁeIgAYdfL){ٺ/^=\:6s/dU,-K76b*rg3R7Km³@Mk Ĩ{6p! 8D^F1GBx;8?vV}_x5c̋D'e%$Jy*d/%$jY?LQdR9s=mDa2oCb/ųBBZ=Cȇ)RrM{{WW;"m![6[9@Eѐec^.t%R*K["=qbB, G& >AGC; 8-TVgǯ.U' .{Uz  { !+s P/0jri)3*٣%McuoR:!qͻDW_dy;"h/ Z ZZLj㎬ak9-M*EGĶG`^y'/v-$Djks~$AM{4)`lwk,)n]PmeYL`>P5"x~[A/x€cRI dL>kf($phM_ñ,p&lNCpEikt fS3qXN 'E,y >JG{-<Lsy5(b%r:ĭӈ\"9~/"^jpJ4Y^vk>,)XfT&?4츬AS,ϟqy\'{-9 N',aM!}rK \1e2>g owދ*@@:,|xÞin61}I}ՉWȎ5ݑcɊ"B2VC\_`Vl@Y)b!j_ֿuVwV@_aT~ Ul 4@kjgVÑޣ8WRG /Dj S OkWv/[tquR5gÊ !d]JN&l0ʈ>k+nl~as>Pc~%iyaEJi@]kerC$zGF#@OHr76?] H J*VJs^!k+cdS/GLgZ:f5̠'1~p+KR*l/ jY@vv y6^NDqk wܞ"?co9ߪH!(.3zSc!Wg+oZ^MVs"\C6$]?NMd ; ^t3 ȉchr)%EgCGk[ D_ŌPm0ChH8M@a!Sӑ}ttq$^w))Ǻ=\Aƽd6UDK\R^(L`,=  ;"r8`\1SU^o=xn:9|^ydՒ AOi׃R"6MlO ~GpB1I.gB~ZJ>?4.鼖io Lԕg+!q C8nx d`:z H:ĩWXEny6\} з~Ȫ; U_7ȸ 8?Ir-)Ü'10!f0#;t[l h@'Wc ]BQJϦIC<8"H `0!Z!Tk. t=d$&~h㶠<.z>bF #t0'uxfE _[9qzJ<@`c-l՚ew֪)moS¼BdB~3ưd%`B̽E |qJS!OPg$T)KB¿_ {($NPĒ7ChbGyqRY"Nhڏ[!i^1?`nklSe5a}kFsdx;Ӽwy*nֹ$t.q;SA?/`oD+fAT0&MVxc@HtɳhU5lU)&5$xVS0v|]+FGVoӸ.CFy2S^qWuA @4ր5W$ o7!#e 8*DFz3.O$Y? ܢ4u4wնF @Ga% :jR& & h 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & h3(D0,Bx&X%P$L$L$L$mhTK$BH$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀR͕.@2MfGvU{ljǓ3JK /[Nepc p XE:|턗pe]]pthdd-36ScӤKO~`~dyޘ3r?<9*;F?pO;]>9/}ɠ!Yr*<y: ![j]b xrlrEqCT?~jn~,tpQcsXJ$ySWm9w hO8fcȃ3B_]<~w;Ax;w~GPD.E8R>uUDu)4U`£A9Է3*.Z2eZV/[ZE~ڽR$A xzD5SaU[5J.Ud'[R2;{"Als_qוf5#=~K(s;tI 0sf,̟X5caD  ޘ9&#Pc1 h,٢1`TK '*SWY߈㷵MmN('A*+`ԩ2n! Iwup r#ʯd>F]ߚtP^eԜ38 wc >,ɟG1-qJf1ޮ& '#d{)NݑTSwr?n ޼~sFӜG%, a0~O|8G?Dۤ^ZeJr$ŅRr>G)4 @F}Bq2yV1?Y H|fZJ)?<~uv# l-HHH%bS.iۚ @2d -Vx;!]!'{AvLYlXLo@N‘3ԧe$v_8h_B n=&`j0QIKqyJ|W1=6Ͽ_y+(nmDͽ#'{ΌJrAPH+K圝c^Ҹ 6ܞJ1 ]0"B4#D?yaPT}_QrXy3,86Lч.u䦫uMG**Y‰=-c?r<%`B䏼qr=O37R> \up#9r 8YXul^>1?)lRbzC!T)}F.d\y"aEBlۘl>w#=_ŖR`85 eEbvR(DYŀO}z<@Pir_ GS⣃+k29̰wC?V"yC[Z=1%G040b > \ +qDJ2(| I(],9Abed~Z.XـEA;|&N9MBA~'39^qT: (pYT⬕RDÑ,vQ:|ki;Bǁɱ. piGbSُ3!zP1OX颵Lc:ѰiH*?UF#*Z=Efő,;"b S@B؍Dj*?ηZ%XHnS'@a_1=5^]wխK?kΗ#-SbXɌ?߅w#`>=Yfvw}6.s| T1A b}Gt]iH ӡ9\9#١n{.0<ޘzuNb `7,ohɺe҅K7Fh !8;OeI }>`I^Uw؅{3$?p@Y4آ~5['LYMRC}$e|EtS(u[Jo؎aU5Nf26z|h)WmoƶOT{ ғFuW\1 VV{xK?3kRݯ\F h,#i-"JЄb-$ Vt0F/Û_;D̓zbtfõp"<OӇ֠ބ)֢e#7ʹkuѸV#N# KN}!5+BDס8q e:0PҹK_Hn #uVަe$-+w{]C!s;?/X$|O~1|[BͦO򉑺E)⁶h-Z1ye"1gn8!Hc+4m{x (,tLd-86kvꠜ8y G9 5:x, | ME &]?/_>L2k#G>Н*E@|3QN'>:-j@ ξ^a JF` 4V+A eā @^yco>_<Z2 ZR5m7dK )F" 0!W;Qx_cj1oH"H2 G.]YԳyYz~l? 7L+7Q8?q!MߡlYFZ=9:3oQW>枩H^9Qޮ>@H(x}~ϋ§{b>(GWz[ T͍e_6,*օDn-IFf\8 /W,48Xe1_h΋\}ޞ}1@sJޟؘJ .\=5dpXEXS!1"&E(7$oʱͺs(Hxh%la\ USuJ|ټe}Pu"]֨ y_}0K%<+u2yTLQ?vUuU;l7!彍y1f6PtjH`0$=ѸP@ջ' YMI 3n]VwK.%/u!V[Pn*x߯y}&riqi/adt)W&\oh6qҗ; twv:}fJ/[I.g1cH)%ru=?~q).y mW`ۤZ^y̷+mm"j{/zG~P+URk{kje&4c3h,䦚(kOO|(O6#?>W& 5I6߃?#gv/#sU('NڀnIj5?9[o~aK(^~4E% !"+}| AAZV܄Xd}BGFDv'@.xֱ7gp:x]cwXVQzvs4L11C8H)z&L 3 9nlX3$2U@- qr&Zk"bszϱ;q qxعӏ.=h4^mZ=>qs [ W*Ak>ݚj]Y{#1[ \WNWZ-+Hg[;U I2^YYv0ܘOQ!2sq5aՑYC $EFU^GDONz߾3CġwbǚzX jmjOooc x\6h$gWar򙲰ɤ e wDtIgr3,m-oȳzHad[w)`> ̳}Wh*=W'1vGNy+S-k;Ƞ1t?N?I^hjcխi;f" &.^Xl'#3U-_dA;8MwfἭr !=ju ӷGj9Զ_zp xktwnKC<ٿ-2wўsJrOKnϗ?(ם(Q{]IV1!ӢS7=vgU:%|N2ʴfu7>\ i1,c~P;`zF @2كz?*ffWZN396TeRDim[m|Od8lͷCh]F`Gtc0f ymbo9ƨT7}xn9"YpU<S\Ty=TǁxXд 'whx0Pj`'H )Ԓ uH&#GL3S:u}} V{,%2{Wzʺ:bdZ 5~PםYlUw9fvҔvdˆqcFX,_YWvB[j}(:Ҷ384Gme3 XJeNlr'>]/\S)V IH7*kahٷ{ ~5ak.c*e6 &( ^WsFVo%XN_nrQ6Ȃ(8Ki¿K7AD{s^pc 6yj2:Cr~ܝd}n(*`*'ֽC`u3lOc/gU|~\S aCsx )ou. \w tP5%?9AGȔg8h{ckTVBӉe^)30oK M4QՁznJ~ 2@ַBO0u)˿g}'1{z lFb-wDžّvFۯ^>s}\%~-rVq^ZTSq'p?u[Ϛd~?cWS7?5KhP ]؛[Y /Gj{Lh7 ~oS _lDv/ĕڂ>>3c[ihwCnCDEi+r (s 8h.S 7-ǻ݁St<0B}hO:M2荹Y^@]"nE&o3mlR NycҢz"!CrDZkuBw=$3LjdᾑN)i4b /w':;Ѷl[b_zRTфSJ ~:t ~ PطX] bZi=9\.vMKLj.*wP5ARpFP?P_041cR5Gc>{M\bL~ֶ" !_N;Ai>s[gy?WFBzWjOF.a9L7HHvG:" i'}x Mzu,WT&){y2 U#%e%SA!+<=Wʛ5ԜK_œ4 ;"j evDMX Cvw.IHx2S(Wi{8#Zz_Qj- NnDKPň@$ Xq&2/I;"'skQr>#@6Bp<9cR5G>{M\bL~ֶ" !_N;Ai>s[gy?WFBzWjOF.a9L7HHvG:" i'}x Mzu,WT&){y2 U#%e%SA!+<=Wʛ5ԜK_œ4 ;"j evDMX Cvw.IHx2S(Wi{8#Zz_Qj- NnDKPň@$ Xq&2/I;"'skQr>#@6BpQycR5Gͷħ6_m|z`9GMu N%[u+]ێBA/T7yq⵱f![`ϦR<3ԙl6#}wiR͇, }ߗ֋t( _ULcfX}==d=CrOڕ(s",bs"K_)u{[;k+Y訶qwdD 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & hL(D0,Bx&X%P$L$L$L$̓ta"&l#H$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀ^P]t*:=836(}}sZ_pq^!]bIu{Mp?79qM]'sI Fyц K|<|e)٤Y|Fj8E22 "Rbrk'F< PIWhozOvrG ~9vA|5zDbm!mש'n$ 80"(Q_ ėAA [Z1,n'dSk; 宏xcN:M0' R|4JNS+ /~ӴmeqwS sH*ޫAquej=mIPmԺR X 3u\9=9GױmIMY>l@Di\LQpdC[PH2JPs-hh8r-)6;϶*Ti$*j>c[/T9zߓXDH1i(̫ n he=#t&@2A `W)`43yZóFfo"GJhu^+i ;D}mUJ"+`JJ2v $ Ly+!^ޮ}{EUF3v* (󎫦˓[t& #}|5B8'ACه@?]_I01o W>D"%iìMbO0Lr2/1 :>2(Za6ww͹$g:8~6 H)26)+PIc8C>e{Gk9K;p:"VX=GεRf>ӓy$UcT ZtZPMS錺F۪JA%M˳oȷbtd@5ZXt$K&FbRG<`9s|<5_}\_~ef6s|aL6ٷ.䯴,":Z@Kup{&M NrYd7H*Ld r[jzR8!]} /6 o(:7]Mo3Y|]{nfE(w_?xje@6V- F^VY %1H|E#&ǐIdĩ0Z0ZA~o#.^Ta/38tx =C/@wiiYsWc؃r*8uM(-JvY[rnmB+Kn/bTA?Tmdbq\w؎8Hal$I򤖕H,+|m)WLq3C@NdZq@ ?i(X\6D]t$~J$8hCJi*Bo=a.u?NyaE,o#7ti5yZb?%֊JJ!pJcQ܌7 A^ZӍeHA g9f1$otFsߺ?;%iG((&fRUW?h½{WTKBw^@4|>c6[a[H _A?Bߵk8H߹>zZ03G }_|5X'5$6}k2xPب/3'a998VN}+'A6 :t1/wܮ%뷍xQrSre6}Lb$NR)IgXЩ/LUkk:?om\kkA5:mfGDW 'k={~Zm`vvIϲ>|M I Gr:V "令3C9*9l8?wG:Gj1-!| G")Kx$~8 EPdvW7H3x/P n4}gџ:aT)ؕÏN++PjcT $ r_m&S@ mJb-W|+5 nKp8Q4I^藃hi|AcUa"/Pʁ"`0%BB=ÁKGAL5o*hs _\h6Ϡ?i WOYC('/o,lo0jSKX"h!llQJ^+GoG5hHtiqZ,2KH%1$cX*Cu٬Șf}V~ͯݱG,+];_ϊ(GhW2bq8w@,ArIuN YhZ`b ]ːs1'md߸@X8y(-tT}䍀 ?7$?9bYzd@V`ps4 I-,r: 1U. *` MZ:}(!_; kg:/U[_o 0˦c B- X-l9,&~M[5R-ET=4nF> NjE@K^:H=o`Z fYcҰ̯:Ў|6u ڭ7&c^Bod'Ѯ;vʓT)dJgvM{,aF(Xs]"ģԔ#-FB58W~W,>dW؃EKMl<ɐ(e?j(E{.emM[s$&)1ƃiR-W39;೉W ^E_W1,|6་q-O)]5~zBJ;ˁ9 v(,H9dJ !4mqىB{avA\IȲ!ʂq+ ˊpwGyTR.'Kp( '_LXImy],$XehoTO]wvQW[WM_4]$|߫ec6 rTZEw6o4I|P)۳JBe> ͬ^Ɗe2 \&E%1CW9% < ,XibL"!nVך-oV؂ 7EQζ?ŦN1Th%y>;J;^$)Nad⮉B8slUl+lJZGsUAZhrh 0 (C}eq>CarA͈z8̢vr̉V6llPp$Gna 5ƻvHfx'!;I:){V0q }q-q ˁ{׏&'ªT[K&6EzZo"N+t Ud7~o.L-]h뽷'P=ASpIw$Bo3H!/ Q3T6$mxg8@RG=Ui8.o{T pВ= cG l[}+P&?6bZ-6cg)7wg&=?i*|Aj\۞]1Z04;G{AF 킯rg@ך"1Z9D$Ycrb*׭pxn5ܜT3ʝ[9YI[ J- ay{TduL-PT]} = Dw$'SJRE|raO][n+$|c r]Bܖ7E,-%j*nrūr>:-Y~Y@XtP SMh“Je~AzJZ3 3Є 3^TSrtF`̞&oPr;bY%s_}/|E3VӠ緘U6VG@O.C?GM0?,"ɕ~|V,:<&dHgW\(H@kOv)M\v2}'THXuP"s Dϓ&nXcVec'~Zy8<:wS !躼wN?cJN6#uqjrGK ȃ ="LPN(*Dqj,klOݳA<.E]yǍ0 0뢞`*ͷZq P+fjI߭&;dtSZ fԳ,oEl#[$ `j@0cp8H2(&q\뻎tPY{]q}WX BqM% 듥0.]{Uo8.xtʅk&G:=kec53T8ogGz j.-c]|4 s[hɳavբMfIj(awABj!gTſ4$.k4٦=*4'6AG;u4NԚ!,y٥~mQy:i%ZgY@\:ʙ[JPVBҝT] &+Rˆcތu1_9p 36#$i?4!0Q(+ſDqG%uh:#od1y`*UUDiEkVeA KL4xA Բ L2ڜ1,B{.L_`|M`SgvDII1P'G,ee`hBSrJ/+Zy]|9Abp6ޥ?0}aΐn ʴ+/// xI<{Ϙ*6.JQ+N%us``8q̩m!1x@S€giF-nmE'īU7 Jv,nT7m䬞i$(eG~׺EDŽVOl/'J׽((6 PQ2qpUqm(1ՋXU+?=f#s%JCC 3x=_ut9><嗍S %y\&38`F(b1>JT,<;;iTzOqD+I(b|BA1DM 1c$\Nek:b~ف<$AW)kd0$ Ï)/3& mbʦ!%c Kب2Wvf/="v$F dSTmUq.9#!H.%yVuގ)F8 ԽȰNDgBC@vD_{w+#ei\c|_`#), ԯt#/'پ]d#)Zcn#/ߢͲ;.K7+;So#ylvZ*Ԍ'_Μkf3Q/J?ЙE>-W{۟bS_,1LW䂃!!Qf,<V/e ccb: A4ky2~1VپqͶAS A#[hSJXa0B 3ΰiS.;Eqf>5٠Jl< 5l |.S}a|O'i\nJ"Vƚ =eW3ޢn CmrLӱz;s'ԍ):.XL]HZ!!0ɬn9Lc,@;}uv?*(ֺL\tDž W O> XYyRjd--jZ;9t$P%ߎ*Й7GZ is[`@osma]mqhjaa8Aos6'4(CHTﶛhzȜ) ;1BA8B%LQaI $-Od$߼CS:3$W7yu\Wqsđ`ָSUJVVsK⎜ N%CscE)W/Y@-0Efb}6dP&}+C9qE޾Y. ) 'y 7O]M١HtI>zAҋqp1B\IB;Mh7PL1B7(+¾RV@$ xP,V]I8[0Hu J`WE!ˑ; rט:訸 ; btojiy+j8VokӾ=d< aպ0\̪mRKsn#ÊJ& tPc0AVR HCkྃ=)'JYTk"{z"hVG9) ~TkD%[R&82qIP4 ze jJlB&;.0fEOoYvdOp֣[?fD2 Qvjs*9*"!x&fLmAByN~Ks3. my]9juNpyXήi2ȗQF3/J 6\G1>*>p, Fzڄ 4pP Q[ﮑs!⁹ruz@(KU+nrAc!LJF8Ys"j2~)?"+i) 4_SE"INA•UK"nUH9BjEW swS? &~L< =f_urn@mG}Y0&׬ЙEUӬ??;[1Lm@ʁI8elQ' :«DwF] {{.9إ@-RTZ4}vI/4KBuLך+UKuM}_Z3۝$D~YS?3??t *^PsU{NwG}ɸX? (",*` 4·&HҩI:lOFbCM7'엄M_9\ 0ןuD} 9(&LF_Cc+\"pZW&#Uq$ \̟l䣫<3R eؖs8 ;1𾾕gCΣn3)n}<Ѐ`{{[>Q{z@8D~f$t$3X&-5QfO,| -~G P1܇eÏE%Ҙ ;&)-,Wӻ 71O>k*U jO[,9 [B* qW*]h=,eGBjZ Ps U0[9 G~Wul>OI*9tbWlMsyb/bs/֗51Q",$i!arUքڟc_>2..9ţ3%/Bn `@C ~;P/xR1?'WvEa&q'Wd% uȨire>DV /K)q] bfM1mO^6k'.[˾zMqA+XQ)ÿ4=qU.}[6֋,#|?A& Ŧ[w3b]rTL^HRE̲#*s(m=93ӚiƋWaF | ,8EMކWZU >G.<[pڞN7Z"~>3(#:}%2'74D cCZ&XOZ).2TNIϣySЏXm+$(m-+H7viQĆυt;"迉s>^SQ<{Ѭy&@W$-];{($pZ"݋:JSؾOoZy1}$20 |P[CWGOQlu>1Hx.%s.E0DD*O۾bky8P +}Xpli3H{]y=34\$K|vw(,䶃fvD?٧tXG qsCWZ;>^1voռ|2sC" $IFмCT"4+Җ07ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFmgd_G DD:]?xSi*l%6}4;r>mWd ƗBu, Q,;>tNy٦'(o\6 "y _.'6ZefXH9H/XN?RҗKbYqGbyF؝t`Oxī1_;-[v]?#|/MtnCn|r 7}@Kg⫬^4Z$?p)-ۭ֘a@p)>3MQf{E 9NzW[b_ƣyR44T8 zNZcd{"1 wE礝xPu2'IpJM7w'7q}ԁa~yI>y8e _G5 -eM0ɦI?&*|lD`+ZUwwbڒ\:`8@+]EWk>tŀ\׏$WI$?# 餹fƃ"5|k_"/kuRD(zE G*e|v>iZ+xR1}-]fg[T@NsMZhQopi4m.4\OP֝ZS^ Kt`b[ĠD5yBAL~0/ʞK|*TQݱiW/=j ['|Z1G crF'n_'cc3]t-A4; ņT׸*!x ZQt7 H_Ow{dd[!$^>,atA[8//!sef% _'|UZL#yBBho# 'M 䝉 ]Eg7p]JN'VuvUV+#ՉQG-)RIGÑׁ$S4{&31úuv'첑aj NY.wJ}Ucc.Ts>%=}/ &|O`St+rYϏ6ѣW5}w1IČdִo3 $)YnS2i;V,| {&c”G:k2r.@b2VvXg0K\ԈsL ~"s,S'gG.)e4h78+E<-Җ07ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFmgd_G DD:]?xSi*l%6}4;r>mWd Ɨ,B[HϯW>tNy٦'(o\6 "y _.'6XzAкDEDž@/? bpHW/U7;{D_]6eﹷ\=q3-[v]?#|/MtnCn|r 7}@Ko OG/ K* )<۵?R˖ҡRh-cLM;?OO?Ih2q->$eSnGxDջ+\qF%XU*7YȄ?Kb|N9Wsv/oncBAtr`9C+@$|u~-̦8n;WHCH_M6͗4|L-HI&T\Ј; nP"8GwnrfMU~ U$7\B5HqN.ds= -nX-Q]sz )y7ipU hԗ`lC.կ zqO͗L4-d@8o2%W9:&T[|-sKo=>lYx'D ā Aw>Vp GZs2jݍub-^Y?H[trF<ȫc=oTmpom3՘$I&,⏬j+'"jPbgi{ xD:>0B/D \ۗݼ6J8?/J%߻! '6G2fI -pF5 )7n5@oFw)}o0[GwLP2 B}Zd<}̑]ێw6Bu 2q@%c9 :0.u\y4jk_6ӛ! 1vi#o||Px?~ X'yb(Bp!8ZN(('X" ;@`:V;|[ }8J{/S|/9\Hh-}=Ï䃰oV#Xm:}%a]DATHݙH䦅\yQ lsАDPeIH@A )ݻE vH&jf/7F};Zrx1Z"G =)ϽEo}R,7ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFn^+Lr-QI¾ y %E,[=+pHs*2VC\_`Vl@Y)b!j_ֿuVwV@_aT~ Ul 4@kjgVÑޣ8WRG /Dj S OkWv/[tquR5gÊ !d]JN&l0ʈ>k+nl~as>Pc~%iyaEJi@]kerC$zGF#@OHr76?] H J*VJs^!k$ aHUoTu-h꟠zbR.)<~$#vƻQO>Ĵ,w}Vʌ*sDž^wϚ \0)+ŊNBY2F縿{\X>Q(k_$׭v!0p,!k};ᮈ=Ca?dǞZý&uUtGq.av-lO\ܥ/ic F:P =3&8aUDXr5:yƧ|^f*w.k((b:LhZ>gO 2{A':q |uld ʾ/3!`ON"[LΔSչݓv1 t y rjl>V[/sBZm[ϭ;H =r#%m)ղ$_m~/E˄CHLV%g\-  @"Q9b  :jR& & h 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & hU(D0,Bx&X%P$L$L$L$̧qt}o$H$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀ^P]t*:=836(}}sZ_pq^!]bIu{Mp?79qM]'sI Fyц K|<|e)٤Y|Fj8E22 "Rbrk'F< PIWhozOvrG ~9vA|5zDbm!mש'n$ 80"(J|XQ?c+R5eILPղ%ܠRsF{άBFV\}"/vXbQ 6?rPik|D,dgP糞pyC3}ƒ4̳"ݜԶˑzMaZRΥ XG;O;+i8(G6M*CnI Ɯ&eHyIvyXMA9(fTOA8fL Q nd+Y}KWS[IS31Ed~8ޫ=R[`_g@yތ!RhJgI3Q 4-yG(I1 >DkksRk#;q2=siJ{nܶM}P8aE=83iQg #}|5B8'ACه@?]_I01o W>D"%iìMbO0Lr2/1 :>2(Za6ww͹$g:8~6 H)26)+PIc8C>e{Gk9K;p:"VX=GεRf>ӓy$UcT ZtZPMS錺F۪JA%M˳oȷbtd@5ZXt$K&FbRG<`9s|<5_}\_~ef6s|aL6ٷ.䯴,":Z@Kup{&M NrYd7H*Ld r[jzR8!]} /6 o(:7]Mo3Y|]{nfE(w_?xje@6V- F^VY %1H|E#&ǐIdĩ0Z0ZA~o#.^Ta/38tx =C/@wiiYsWc؃r*8uM(-JvY[rnmB+Kn/bTA?Tmdbq\w؎8Hal$I򤖕H,+|m)WLq3C@NdZq@ ?i(X\6D]t$~J$8hCJi*Bo=a.u?NyaE,o#7ti5yZb?%֊JJ!pJcQ܌7 A^ZӍeHA g9f1$otFsߺ?;%iG((&fRUW?h½{WTKBw^@4|>c6[a[H _A?Bߵk8H߹>zZ03G }_|5[s [B 1 HG`tѼ֛pSW v4"Jދ̞54{<J<:`]:p8u\9Z#~ןN m CʼnbF_-=B`<;xlH{}4C6Mq"L.>2FC ǾM~l=Y 7JF[sIWlԨ9ÅOP'09`l~\1G)|D c=UfM){zzH繮2t ?nxm Y7!cs kEX,_Iy(v3H:C}@" @?YmQWhڂCQ2ÚwϺ1l w)WSZ0iDGZ$¼'*`$?gPq,1@r aYv%Ֆ%$vmg3bL>[rn|A&J#(Ŵ.ʫM⁓6DE~6e+Ml03#5-hA mqU'"5Ǚt\jmOѴPr9G`5նQ3<$iǧ( ^^U?з 7E[ѶWz>*ئk@]=ox6K`Zx _&g|e|RN62-]t&Ȥa6oW:?bف`2Ⰾp˸l*pfj` W|#1o?ᵄUh|O7S<]>3E.׃0 ךZ8)?'co|/(]SDȜB7lp:!vܹ ;!xltQQ!SimJ ʐn\&MÓp1Ţ[Qv?kܬ+Y !UL2UZD(^SŒ![F?nrw'.;7৷7v.rނ7g~ʲͥ3`0b|B79^evb?-HT^HԢ{bIE޽xgH+Ej%s~t3^]=Iqd ̓ǖv3C+A5h\M7lt+ޕad}8o#[Q e/@5/0* pJ? ln4LT,^6S#⻈|#oto{W:tm9Bg:f nO OxD= >Օ6 VYg:`2\A(ݴQ^u`m!DwևI$`uohG?>}zp"ٲ.;$ o$&xD%<(ѧLe#®Wm|bxC|{R=zaۿaޥ-^ÿ_sWNf|v>Vn]aIrcR5b,ю"?0ٸxШlV*]/{fK~&#V b&͉  ,̞4e2uP᣾Z<2jq6V SN?/쓲ԳNF!cyJZ>T]ҚHi˵>"ww2yTvc#*AЏ?GnƞP;TovQ?wie%z Yw@r&@N&)s6Egw =/[9˙]œ ϚXY=!_34˓(E"Qxs=ahD4ԕ2\Vy^d6A>^9!*c !tD䊊bvZJ􄸧JXI4sge0,px4+[{5*G*x)ӉAh-ke"$\vGz)Zcn#/ߢͲ;.K7+;So#ylvZ*ԌY/v}Fg|:os!w=v]Mar߄ªܫ|ԁV1ImURO2i`Zm[=-"!~ɹ RgMXWq"RvVR /EVtwV¬2M&CaͲL0{Mq_M$Kcm/P8|ǫ@aUGk`'=+eB֪OP҈!:?~t17Sx0.=˪@_Kt㜉6•u%y>k/@.? uʧq-vKLpD&ja!ĬK喙*X4$ߓd89oxg luӋoV/V+6G`Ȳu9OYPg:f>ڞ[/ZMR:#U$|R+F kӏ긴H>9g93gN27nDTu5&iLkP-alj8Ծ#TI5a)t4zT%^d+1^rMa9**VtBG슗ԁR"nKP{L쏿CdE­Ÿ@V,$&f;/hXr|āHRfPw|NFZ<٪y78y+@<;?}B= Z7O{ .1v0r՗ o>.o-f 91`tZ A}6-Bݼg9ٿ(0bet%OR%dNn3U#<@ zm{>9p|З[StB'V ~S`bK|wMa$Z ]6MxMcĪ'n1y){ui _H[.o.姜SfUݼ3>vbV9 nCz(>F0dߴ.K\ͦеI3m|Q]z氫/k_bKvu+  [>V L7JU~EIl tdWC$O~!tr vPI8&XR>/OhC?zա}af>|90G+uHŭ 0wnepвª(mYPv%p!iU\H.]Tlc7}+9>][ٯ}LyU)ow].vRzx/J?*,۴<= /Nb@ 1-Jze4n#:"Da}"~웞 "MӜnCSj9|!,>'-S 2lnóV̠wyݞf*;Q&8-.>NX+#]#K?k_-jX ]y}@ekP%@pP ao6z @7p}퓏91 naq:%Wc.j[>SE"INA•UK"nUH9BjEW swS? &~L< =f_urn@mG}Y0&׬ЙEUӬ??;[1Lm@ʁI8elQ' :«DwF] {{.9إ@-RTZ4}vI/4KBuLך+UKuM}_Z3۝$D~YS?3??t *^PsU{NwG}ɸX? 4 lWHMUQNh-(RXag1{e4W(^ZF?OnlolMƂ?#VޝŃ٘Iǫ&~jl}kRTIav\-g(;pBp#& &T(hPky9b8~%_Cߋ!7V!p]!~.V[2Ppϕݯ02\nԐvzcm['vB+ۣcۦ^İ- > pubUϛSAy#[^d9@(M!떓ס(;ؚ#9D᪵͆w>p S**Z<| ~$! •WhϞ`JCػP)s[7~"W-P x% SJZ|4Ys܄i/Ȋ|c$ۙ3Õ"avL‰[Y}3ͤR1p 4kDU|$~pRQ4T8S*#) Js#a/*(Ff M27MT@Hn0yHnz}7 UOޕhtu93Z:J1ˇw3?HYTUid3N|*By12-rbznX**@n[WhOD\2d:_ ,np*1eG} bP,lkV 6\%M ҋxVϒNcl%e.v!:ǞHU"}o*vf0I4MKtaPٳH =$r'u<DZ POlb_gAboR8mgMif׆üZV[$w'7 %u  B68APYġ53C =W\dJ}Y!YLyRV#4x)ะME)\9uM~1U1ovʝJ_we36*Q Y 7"܆`t1~ <4c?ʴ(+?wGA˙ ] gK ;c1u0qyŔ|iNZg Y^=ŝp5 Sl0fYq>-B'R<43#HW+-rM6mxՏ `acf}y)Y+X1(ы& &@\\iI)=KŵJan߼͓ƿbP`.^J0߉SQ-IEc4k^ZY ʾsyBw81MUF^-Dr}\ݕ@[Ю3Z}o!fo<ʹ3ChaNJ ? r96iu=ͫLJlcnU" O|jchH PYB\]2+ЧM؋Nn, L< 2I8]h"sk-w~=k$4 ,mvNUp[}޿ |VᛧZ/žn^Js1@2N@69fBaZ"[oMQL^sc(OFaۤ=_3Ӈxl<Ā>DV/F5(riN_ ْ["c(Zxw+s7堫IAPl5ZE0Yɻ)A^8ob٢_WBt7Rλ Qj[ Ifϐl+^50sAS@aA$/Y`4+Җ07ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFmgd_G DD:]?xSi*l%6}4;r>mWd ƗBu, Q,;>tNy٦'(o\6 "y _.'6ZefXH9H/XN?RҗKbYqGbyF؝t`Oxī1_;-[v]?#|/MtnCn|r 7}@K_Iv3LB{m 8܃:zЖy4gͪ 1X$'G]/>"M6Vg2}N!\z$9SbUoDSAQ;r gCw25{}$`CS|:VwքO8X:lگ$ ֲlUee1dch & { J6i4EIwv׶ Vq>V߁{4*|KԲo=~TJ1k}NGb=\VIej-[6`n<-Җ07ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFmgd_G DD:]?xSi*l%6}4;r>mWd Ɨ,B[HϯW>tNy٦'(o\6 "y _.'6XzAкDEDž@/? bpHW/U7;{D_]6eﹷ\=q3-[v]?#|/MtnCn|r 7}@Kr Qjّp!Dw<[< jUH?+ŒGVUípUC\-P74dϖ*SN'H-DJ jHnU.eZͬO,Y(݄ބa5`Zs{B-)dDqu'`gnyؖ7Aa]A\W94iT@ksejrh~OUjc!m( {oRɬvC\zai@ ~v}0/ZB'Ԙ}gXIH7ݦK)cA勘@h[v 0yI%^Z^X_l+QtIu?2 cE,.H ="G[ Sݲ5jfj+q_6NLL{^dy[ȝ;m9qя6¯eE чpJm V6s5K~庉Y׿a!dlcǗh\[aod$:?ja[`4M: [U q?0DRCO ~dR Q@_N%XwV7{c;7 Fdݒ1Xݘ$WփQydZKj/e%Q(^ =24;Y#j4dD0(],IuaHO#/n =-FIDt;r:4 Z_,aB4بG9/4{!AU?,÷OU+ Gl+tȡЋ #^yXH*YuGONkl/P#^<ʰe*#u2iep@C܃3鲙ny]; 2[&$ŴfonM *IG:#u2-rX P p"vEt1kvЃG'm}vJ } }4ˈjK*RͻUT bS|?Snw_ϫ:fW#"Z$U PL_CIUBp敌[[?!qj`()=ba].r&gQLi6iq,:ta-'!w *= 0#`%{̟^ȑ54'.v q;[΂7 NmȊu)cx ѯdŦzhۏ}^M/  %zg\B`Ȏ.5ŜP`i|sc6$' 5Gg=sD5m ̇-~._®2VC\_`Vl@Y)b!j_ֿuVwV@_aT~ Ul 4@kjgVÑޣ8WRG /Dj S OkWv/[tquR5gÊ !d]JN&l0ʈ>k+nl~as>Pc~%iyaEJi@]kerC$zGF#@OHr76?] H J*VJs^!k&m&eLE (wRnctP95fq ]Iޤl mP%id NUh^ ,"Mşw6(HEՎ/ñ7D].k/;ѡr+>g .B%ٞ=dV0#fJ-ՆDmNLV _ QSp6ٴ(C[ !*pA5AL+s,Ɵ@YQ8SjےoMFOj!F:i퇔GYpZX^P5 q0˛dƖ9 Eնq" b@Zl (I: ep,iw{8Oĵ6"/JhApw/21! 3oIosW&_EY,a^]XllP.$䦳m 7 N5; PtH5p^.5Ty`Q5IY9$”y6lA__G?^Q̌}0j/5!tK<\^+֊jsU> m8A`NR e+R@ɒb3; y\?3h~eJW\m 9N a5pͰLaYg9fm^ƉBK!ן߯jaP({{ K] !We-xĢGⴳv)E-^)WOEc"f#Au/n̘_BvHUڰfmxPOr:bc <Kl6K2S%D=-3VLܓ^i2ɍ:[D4J)o_+\Q:<Vˀww=ltL&'mZUx6Ah([i~}h#w䳓&qԳwy2s-!+'b*A;y,88O+0h8t@w.m5dVN ؀Ey쉆ʗ/}<6!|5:a%}hqUtvN }H{a}^BlzID=\|EQx d!N=Q5HXZ]"_?WAR9~8SeY* VCE~eK?eg P.KQZϏA*hX9( ڭi Ucq5=th7`'DDxjC3{V>-)M.Z&Sc iv-2mU%%c.kvtfY#9β9r6)13ն&hL1աU)E휝UM~e hل>q@ap&!}t0*WLW&\p 3` 6swٕfeGZOjړu?Vj%qhdr)/$_2cȨ:5fAGrPoY ɴZhCS*6/#4RƢ"8h*W]%#"GXƓDX ۢÎ[@HH2fJ͠cQYq#F167k u4dFdqFյS[PH\әM_4 :olhjFw\M,Fd! 0 @Ga% :jR& & h 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & hJ;(D0,Bx&X%P$L$L$L$zb$BH$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀ^P]t*:=836(}}sZ_pq^!]bIu{Mp?79qM]'sI Fyц K|<|e)٤Y|Fj8E22 "Rbrk'F< PIWhozOvrG ~9vA|5zDbm!mש'n$ 80=9<q!'z/pp !^M~-Î28-ִ)E 䛇?|1H|N.R1&VG&fi̳OKi(3-1*\p1&1mم1 N kh-@ M̈2fQbWB~]9BbweJз2= >3;2⅃|>eyO6.`$y #}|5B8'ACه@?]_I01o W>D"%iìMbO0Lr2/1 :>2(Za6ww͹$g:8~6 H)26)+PIc8C>e{Gk9K;p:"VX=GεRf>ӓy$UcT ZtZPMS錺F۪JA%M˳oȷbtd@5ZXt$K&FbRG<`9s|<5_}\_~ef6s|aL6ٷ.䯴,":Z@Kup{&M NrYd7H*Ld r[jzR8!]} /6 o(:7]Mo3Y|]{nfE(w_?xje@6V- F^VY %1H|E#&ǐIdĩ0Z0ZA~o#.^Ta/38tx =C/@wiiYsWc؃r*8uM(-JvY[rnmB+Kn/bTA?Tmdbq\w؎8Hal$I򤖕H,+|m)WLq3C@NdZq@ ?i(X\6D]t$~J$8hCJi*Bo=a.u?NyaE,o#7ti5yZb?%֊JJ!pJcQ܌7 A^ZӍeHA g9f1$otFsߺ?;%iG((&fRUW?h½{WTKBw^@4|>c6[a[H _A?Bߵk8H߹>zZ03G }_|5X`3Fe"~Gg 魯څvGSmR5 FtzcVdUX$KKļߧrʦعW@T~qpـȧ9Tb%bfwYUHk܆lfLVYj {K6Qh/Imv0`|i+:;=94׋NePh}|BgrבR(c)O.NP饥 VGdr2'{%q5X7;)P{Lhi sCIeO/_>m?h8x;)Qt0ŕL|5',wމ%Һ&J𸞖H<xrzMW?&,Qchʃ:t-N"u]/;mg [0+H@a7*_>&.sBs>eվǶwK=㬶2kꑜ6mx" wU*{sJ0#J)eXĊ|nlwr7~mĩu#T`П#t1SF-y.m*$amV"|Q'q3a͆[ݘv P=O-ϮB%Q\,]Ⱦ.}\+;䢋QIV*at~2)ʁՂbv@_w&G-ߺ6F sK2aR_FK؅RQ{bMC9_䲚Ƹ0at#sBgM bȃ1µi.//Dq H@kOv)M\v2}'THXuP"s Dϓ&nXcVec'~Zy8<:wS !躼wN?cJN6#uqjrGK ȃ ="LPN(*Dqj,klOݳA<.E]yǍ0 0뢞`*ͷZq P+fjI߭&;dtSZ fԳ,oEl#[$ `j@0cp8H2(&q\뻎tPY{]q}WX BqM% 듥0.]{Uo8.xtʅk&G:=kec53T8ogGz j.-c]|4 s[hɳavբMfIj(awABj!gTſ4$.k4٦=*4'6AG;u4NԚ!,y٥~mQy:i%ZgY@\:ʙ[JPVBҝT] &+Rˆcތu1_9p 36#$i?4!0Q(+ſDqG%uh:#od1y`*UUDiEkVeA KL4xA Բ L2ڜ1,B{.L_`|M`SgvDII1P'G,ee`hBSrJ/+Zy]|9Abp6ޥ?0}aΐn ʴ+/// xI<{Ϙ*6.JQ+N%us``8q̩m!1x@S€giF-nmE'īU7 Jv,nT7m䬞i$(eG~׺EDŽVOl/'J׽((6 PQ2qpUqm(1ՋXU+?=f#s%JCC 3x=_ut9><嗍S %y\&38`F(b1>JT,<;;iTzOqD+I(b|BA1DM 1c$\Nek:b~ف<$AW)kd0$ Ï)/3& mbʦ!%c Kب2Wvf/="v$F dSTmUq.9#!H.%yVuގ)F8 ԽȰNDgBC@vD_{w+#ei\c|_`#), ԯt#/'پ]d#)Zcn#/ߢͲ;.K7+;So#ylvZ*ԌWJ` >{t#2ϼD/U?HdEܿ= SM&*p;F^Cr&ی~t!>mp%'ص9rS%#Tp%<^ig q@Ej¥ek|84ׅ Q $l"{ 9&13ʦmҙ͍oEfһykQT<*]y-yR 6M XDiiMS(>#fVx -/^|vz/sW25:St %G !e.@góFlbo]sg|IsW#`~/!%uR h U38sl&Rԕ5WS Ktl1ኬs^wn|W贎nuU8˼U~E"vRKʘvA\):wG@vmR".k)d2_@_gzum\AnH%6P,(P2 khm+_T@:PQ}u>]8kZy)Ntwvf,MjOT9n"/(Z ,Ncpy8Di(߫f Í xBihȔiaK" NZ0sغ;K7kbN7Dvh4@1Y F s=̟8ZiTwPfl33~j׊`6|-s\t?G!gK|vzŔI:D9Ye tB41u|癉)դketIw͓2+x `$-=>c]hH4 -`GXh%-ZGHqxx4vuG.؜iY%0X2 E? %}U֚8+"GG܄]LkN=So!S4Cϳ*+eΚQ(*%QW ϓ0 Irքeu+`ӁeCq]%xO7ߑY#}BytoDiG N#l!4h)Qnt bLEj ? ]< R Tm)nfiQR4 Gۊٍnf|u釸OLU0^3#*Nl;ƑP֩ i tܯ~tTS }è ao6z @7p}퓏91 naq:%Wc.j[>SE"INA•UK"nUH9BjEW swS? &~L< =f_urn@mG}Y0&׬ЙEUӬ??;[1Lm@ʁI8elQ' :«DwF] {{.9إ@-RTZ4}vI/4KBuLך+UKuM}_Z3۝$D~YS?3??t *^PsU{NwG}ɸX? ?yp 0n]`xb_6ԍL-Y>']+< _fK=bTh\t5T>z芄y94( k"%9Ogf U6;MA{GV]_4 P[OFA@=HbO iŒYm\mu '? >?R~ omg<]Y@ګX&LwWr6'F7ǣF`1kF*X8j?vYϵw3[fʻ 3>nΨgjW)0fnMwFH0r_o6>%.cдNj c|Mr wJfk1]~7_)С,L27)vLt"bM``}y캛/!ojSlG^ 0\ q.NïzGu(B fy+Ikudʠ_auK?:@&!ѣܠvS[@Ď!mM@0anzˆ2= KxvXHb$iW 70BG3'֯*ǟ$?+r|6Fէ _+Mh/0vmKH*fz~iJ+>3anG>6 _\W]GaenYTQQX:E:xrЛeّ SpdJSa, YRW /ãHPS֢?N4#F;qK[4ۼڨsAٟljp] XlʣڴE`,ɀ2D#;u]PS3(bto},eŝ%ai>џߝJ?+ 9+9== !qSˑ'ջ?*Ϲ8a{;$yg'3 7PJ /5 @sM 8)pJnA*/%;c> P}+#ϚJ. W i<ʹTA[.=MU>_KGTzcz.I4H 83=疃RsuU啓t&v*(% G Mjlcͧ%cWv-6AV'>qby*CNڪ+D%Wm/GHt3M|򏒑a)1i@iy>0K nvpƋ{K}£k{z{w޿N )nה#RW(sgMK74+Җ07ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFmgd_G DD:]?xSi*l%6}4;r>mWd ƗBu, Q,;>tNy٦'(o\6 "y _.'6ZefXH9H/XN?RҗKbYqGbyF؝t`Oxī1_;-[v]?#|/MtnCn|r 7}@KOuP!D6'x^5HX迭ܥCP+1ȳyXMՅơ~b5úԏ!g92_ Xi"%oK{3|Mj+CZ L OVՒ0Rد<>噎UgmΎf?@@dhUSnP&׎uF<>.j+zoӦmaH!bquA=WKqIfQO<%[@k?sIS)M2",뤏&}TxA'^ u*ke5a_j a(^-+]kE@@{9Ϗ n*Fbl[g%gaٰ6Fc<5ӗVɾ)[7u`t*y0C=SnA@D4)h wV̀|bz(d~ Z2; .i5qΧI^1;=Ķ唡y{A~I0ƅ4Tئl|d'qzA&1V៲0_Dor aH asx9@̈F+-?O'Uǟ|@{g=?1 mF01E? X:E`$h:y=;)Yb@gM[zC>̳صXJ/Giƈf?3W( : kqWE/}v3m6Б)Vf7sGt59NR:HmC8&K* Ne)jp\H)Kk|i]-O'UxLɰ(v:}\҄>f#]ƕ#%S.့t6~ /OF '` jš  Ox(l(7d!Ihg3dEux2Z= p.e92ҐDV?LdV 5D4ep;q^jD Ӓiquf@YbŀtpwsnL R<-Җ07ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFmgd_G DD:]?xSi*l%6}4;r>mWd Ɨ,B[HϯW>tNy٦'(o\6 "y _.'6XzAкDEDž@/? bpHW/U7;{D_]6eﹷ\=q3-[v]?#|/MtnCn|r 7}@K,sjUC|@hg2f>32 l"cg !֋B(:T|bX UN9j^Fu~TS#[g =Cb^Z,E^ʠ{Se-K=jA ޳ f,;0;Gg"}ʮl c%4[ B Z"D6j:ƪ Hѕ5 k%?i9eMB+ObƥE<-#1r{ӞLCEs3@IF0aX+![8bJNq7oB)"fAV*T4~c0qԭ4>bOͱȵ@$؅E^KSaqfFSg 0 a҈fg2@ގs=Qf`qU0E j]2s;SdJGd09ˮ_[hZb8{!?>NH>.%0QQj\,z*kY!3e-{izuwC.sz;OOuwؕ 5 Tt6$}WM*}6g/76V84,`w{ޯIH]#6UTEkd5%.kBOԦ˵jd> T{렔gE;ttZc %2ޮ"R]h"{{Vxsibӟqiu_[Ksȧ:2k7A/ Wp ﳌ*TvZ Au9s|W6S劶CC=]:/K0{=L"3>Ƣ?ىgK7AwݢUI >NXc@ LOYO 5M-CS7 n,m36x[zsMZ/WCG޵OZ7OuW4d5܂ZV(-q!ղ/udhZ>9()ϽEo}R,7ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFn^+Lr-QI¾ y %E,[=+pHs*2VC\_`Vl@Y)b!j_ֿuVwV@_aT~ Ul 4@kjgVÑޣ8WRG /Dj S OkWv/[tquR5gÊ !d]JN&l0ʈ>k+nl~as>Pc~%iyaEJi@]kerC$zGF#@OHr76?] H J*VJs^!kX]ϳ8MۻڇAt<#ejeqm xT'?зzd@WuG6Đ\ӦƲyJ R=ӝ XTw^?i}dE@Hlm3uڙxVSU @?C :jR& & h 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & hQ(D0,Bx&X%P$L$L$L$ζpSn$H$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀ^P]t*:=836(}}sZ_pq^!]bIu{Mp?79qM]'sI Fyц K|<|e)٤Y|Fj8E22 "Rbrk'F< PIWhozOvrG ~9vA|5zDbm!mש'n$ 80 #}|5B8'ACه@?]_I01o W>D"%iìMbO0Lr2/1 :>2(Za6ww͹$g:8~6 H)26)+PIc8C>e{Gk9K;p:"VX=GεRf>ӓy$UcT ZtZPMS錺F۪JA%M˳oȷbtd@5ZXt$K&FbRG<`9s|<5_}\_~ef6s|aL6ٷ.䯴,":Z@Kup{&M NrYd7H*Ld r[jzR8!]} /6 o(:7]Mo3Y|]{nfE(w_?xje@6V- F^VY %1H|E#&ǐIdĩ0Z0ZA~o#.^Ta/38tx =C/@wiiYsWc؃r*8uM(-JvY[rnmB+Kn/bTA?Tmdbq\w؎8Hal$I򤖕H,+|m)WLq3C@NdZq@ ?i(X\6D]t$~J$8hCJi*Bo=a.u?NyaE,o#7ti5yZb?%֊JJ!pJcQ܌7 A^ZӍeHA g9f1$otFsߺ?;%iG((&fRUW?h½{WTKBw^@4|>c6[a[H _A?Bߵk8H߹>zZ03G }_|5[!5F*;/7y>}>j|x^?is~ +QlY:\ާ KP:-5TT@͌EFB}M''I!V%fY5dzIϪm䙹)o_ SrԐ=$Kծ=x#b` e9 FL~r(StR!涩yz$>uw=.얼4Piv]q5(p‹2g3($!v2%I />_g0PV=}rUj.3KooCTAe@pഃ_ 8W iu]U=Bǚ~񥻸ݬ&VH @>&x eo7aV"}*MYJmoug8-FuJ D 1-֘_UKҳb_?)ϽCa2N',$m~}Ǧiy?Z).KG>NL} Yñ 2B+ԼyR: !ET/ \43]BƏPd:,"GEHȎ8KHaDRQ >;V4J@ /268KHjɦSh>~jE4oOD}iUI >$ Yy˨W>`IY)t :#1pw~v/\泴JYxE\O{!7̷O:_0gEK n.O'M0uvG*!?=gF$zήƃݱ .AUq@:E`.ˏx;0I] Pvd0Hcx]} BE䡈 g:AX|4i]rvm{נ$K&ꈖˣL+[u{rV%w!Zcm(֙%ArҙwĸQ|13h Z0|zB|BCc3EY6'S&ip28C@.Xy |lW)esŀ36&]%c۔o?-8C·"Xqf6%-Yep>խM^ڙ+N$^K]~$jz>IPup1f2jNa{OQ~?Zn+zt=FQ{S PH@kOv)M\v2}'THXuP"s Dϓ&nXcVec'~Zy8<:wS !躼wN?cJN6#uqjrGK ȃ ="LPN(*Dqj,klOݳA<.E]yǍ0 0뢞`*ͷZq P+fjI߭&;dtSZ fԳ,oEl#[$ `j@0cp8H2(&q\뻎tPY{]q}WX BqM% 듥0.]{Uo8.xtʅk&G:=kec53T8ogGz j.-c]|4 s[hɳavբMfIj(awABj!gTſ4$.k4٦=*4'6AG;u4NԚ!,y٥~mQy:i%ZgY@\:ʙ[JPVBҝT] &+Rˆcތu1_9p 36#$i?4!0Q(+ſDqG%uh:#od1y`*UUDiEkVeA KL4xA Բ L2ڜ1,B{.L_`|M`SgvDII1P'G,ee`hBSrJ/+Zy]|9Abp6ޥ?0}aΐn ʴ+/// xI<{Ϙ*6.JQ+N%us``8q̩m!1x@S€giF-nmE'īU7 Jv,nT7m䬞i$(eG~׺EDŽVOl/'J׽((6 PQ2qpUqm(1ՋXU+?=f#s%JCC 3x=_ut9><嗍S %y\&38`F(b1>JT,<;;iTzOqD+I(b|BA1DM 1c$\Nek:b~ف<$AW)kd0$ Ï)/3& mbʦ!%c Kب2Wvf/="v$F dSTmUq.9#!H.%yVuގ)F8 ԽȰNDgBC@vD_{w+#ei\c|_`#), ԯt#/'پ]d#)Zcn#/ߢͲ;.K7+;So#ylvZ*Ԍ=Żll+UrD"*!Z =px^ݳfW/{W!Ot 14bN|4+|]lqʴMv}~FKq٘Z*V"S8)bژOa%hLh.[X!;x}f}l3ko!um Pݑ+ޖRtח{tuFQQsiqgTDvŊ" -M3 5U^5CJipzPvś42Ȱ u3c w1ÿ x\.V$GK"gA-/()<=K8ĝ+PT|LāG!Nr/mDA(DC %EWHX\@:?׷blX ix̎Wk$_TՃ`cOCVҋ%\*+R$T3BL[pڥgs0a NpSr3R}Ç.Ӫ+cm&ll ų(:ד8It_.*KHDpjoKk 4ؙwM4 ֵ 2"k'oP{оo7ߌrv%=E1m2:oGr?|mQl FMsN/|d?]GsF;, Ξ7$a+OhWL= p|<" |)K~ͳa cy` ,{4b s Vp:5ݽw!^{ W|fww <*U&|WB_f&Xb2ߦ>=;8~W :etvx"rðRfՁWhUz /7Mu?,mۓϛDP ao6z @7p}퓏91 naq:%Wc.j[>SE"INA•UK"nUH9BjEW swS? &~L< =f_urn@mG}Y0&׬ЙEUӬ??;[1Lm@ʁI8elQ' :«DwF] {{.9إ@-RTZ4}vI/4KBuLך+UKuM}_Z3۝$D~YS?3??t *^PsU{NwG}ɸX? @2".ߢ9%ØgașTdobK7]t] r |wƹ)2f?|M 1&iT]B^JW@̑S.j$C`;*1.=nex."%, bu!jٛu*xuzZ4e,_ȯy\*C1u֤ RdD(W@RKй%;Lrnۉ)sO0־NX 6=0}5gB~. XZDw/EF[s*? $D˖% >!am>K.G䷝0`Ujk#!-ܱbdTQNo( =G@;I= bZyx!& ] caߐ 鞛 *=k]š"MvVPq`O{XZQi uXߗymbخe=SBiBu`ʨ d H 奰 `OQm0x A3e*Wx T~!i! TQB7u.X㱿%zF}( I'Ly|se<k:Xn\sC1Efiy*V;h70%(tV\Ƕ?BuX"$TץF!DolLm =r* ڈD+Vkrƺ;ؙc| 5jGG}rpQ)k߇{z#z <\I*rF/>Az6uM:=^jL+3Õz܀F%,jH;4w[XA>oxT7,N%k @WNT}!D|;ZB7ﬨjI,O>a 0bqg0coudnz!y a,&O*RZ{#.4םsh\U{`OZ^Ƙ/)3'O wό&D<mWd ƗBu, Q,;>tNy٦'(o\6 "y _.'6ZefXH9H/XN?RҗKbYqGbyF؝t`Oxī1_;-[v]?#|/MtnCn|r 7}@KB߽Q9+0Hib 0#_,2E있}<-g,[P *d%`_{,TVy_^{vΨM.ɺL'TOPP4SԘPL!&hd?ږR(2JEh\TV3Kk3 \";Wl|c窵ZY-ieWF+&IA [3z49w `% \ s0 L R([:^0ߠ,C]FTߙX,`(衯|SW s*Cz)N=YObCDg SͣO<dŀS kv_ fn%):r̅oҫk[پn%0leoԚ:)O%r |[{#䀁*ᒢ>H.1nۯ"Z1-^ XcMq&0*&>Munq .]D? U&w?M}zZZ{=b- _n:allZ"ˑUW:-i.Pq^Z9v/ ^gK[/-K~R;r%e0/{8D)Y}th24-Lٹua?!/:7-CI4+ke/ίL3qKE5;^9 / L<-:F҆9!DЋkv#5^to$Up0ԧR^b߼5@<-Җ07ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFmgd_G DD:]?xSi*l%6}4;r>mWd Ɨ,B[HϯW>tNy٦'(o\6 "y _.'6XzAкDEDž@/? bpHW/U7;{D_]6eﹷ\=q3-[v]?#|/MtnCn|r 7}@KPN9rγhǞNcQo>&ww0"V-g>yQTmK\:MYTqBw͑3 ۮ ;K݂ee liT7u(Mʕ9jvl4 tD%  qG Ӱtןf[ T -T.5E}k'5"s+䜀Jeko/E5mlejolNZ-=+FP oƆ;V* 3b|7Zq6*Z(4c(DY hA|j]%KXUtH ˡ噫RPu>2㜴!tonL^ݢ3,`HQ$eE6טICxNntY'_:V;)U_$ٰ,jMkuǴS<]PsXn솎l4Pj6o !Vm7] ]3*R'B>F%}jAQlv-_`OSۡG4&*RZWX2״t + AKkOǢ&-5Mw>he`B/Vϵ7+Iy\ E$hiѠytN› -|ޏuxTPaQ9_/d`drV ֊xBa- Xqʔk2/ R\qߥj3q5%9mJ`Q #D<SKL=y&銖%\ oNYbzkv[Fc8g?=ݚ­>+9,: `Mވ]?f?;NYQoRr#-b+]8UԘ2H89Aߴ۠êPp4yXtUqp,D&z2Ms6poz 8VhV &W3LZ] {u[QFCF"`6L[yVeH@p-x[@D\[0V:UQH!C]ǎ%\KU=V5ޢsG+x>-slݣEnPVja:*2 mnR c[F 8 ,xnxO%>Y"&Q칢Ry\FL #'G,25Ut!\Y[3h*Z]@-Ossz (E SJ9\6 )z7?cP>T1Ti'ऩYT.J͟(B0YZhxbHͻtmGK;?ܗ1V0qKG:T ToziQph)ϽEo}R,7ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFn^+Lr-QI¾ y %E,[=+pHs*2VC\_`Vl@Y)b!j_ֿuVwV@_aT~ Ul 4@kjgVÑޣ8WRG /Dj S OkWv/[tquR5gÊ !d]JN&l0ʈ>k+nl~as>Pc~%iyaEJi@]kerC$zGF#@OHr76?] H J*VJs^!kk?NY'P)6&5(}G^]_ܯ ;}dº9nj]/8~zT 6n T# ?Zn䃟!lVb:I8|萣jjxg_ř3+KcB:>Ux]{v\FYl bhì1xKT$AZ^A# b2ZeMm%[u Dsy8i&]px>]UgޞǾy!i hDusAk:w/987%+c]\JuhPHF6i58:|k̻O~^[Q ĭjY6sGfu3/}FeT/b:1, ?F9|#W׏w_e_IN:l08J*{ÔyQ̉>v'r: qhfBZWT YUT{2J]t&MxD:W0 C$gl T. #'ooڎhN-TKB "l)_]<Dʖ|x+A(@$InS j5 C,-8^T\R7:ݚ?VeBwr݃xE41uHy%l)4jD;'k]iW z1mc™G?VZY{ I?U7A00Z~'OsH5gR0FzdJAH|BT,/q ^RYqSXY%ͥ^ٶFQSr1+̱-vg bh1%6*NCwn* oAg)-儿xavgk'h? xR9v;mȊ~E_˜##INH*Dժ߲1* ,<[+Lfu8 dZ8hC(b (of^ݣBn֫pQ@h)bB="NY8T @@ @7˲^ (] N!ϵe͙g`l' _ Lv@;dO?HJ²R֣if)]=kT> "J36\o-(}`CL_?94ک8Z:~y<5(S{;m~/EC 4lpCWLtό p0LrrUejZ=wI$kA*VM0hKgrL}\uŒxbX`0/LwZ>DDcm ]q8.b9h&6-$pƂG-o#[9|Z L_;tKj)N(r߁&$78U\Jeׅ8Db)m?yy7|S|[xmD|P9MixZ!oS-Tp:l 0=zQ 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & hY(D0,Bx&X%P$L$L$L${&-$H$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀ^P]t*:=836(}}sZ_pq^!]bIu{Mp?79qM]'sI Fyц K|<|e)٤Y|Fj8E22 "Rbrk'F< PIWhozOvrG ~9vA|5zDbm!mש'n$ 80"(oj2Ȯ_duCcCӮΔc7-T#>EdnH424 os*p*D"%iìMbO0Lr2/1 :>2(Za6ww͹$g:8~6 H)26)+PIc8C>e{Gk9K;p:"VX=GεRf>ӓy$UcT ZtZPMS錺F۪JA%M˳oȷbtd@5ZXt$K&FbRG<`9s|<5_}\_~ef6s|aL6ٷ.䯴,":Z@Kup{&M NrYd7H*Ld r[jzR8!]} /6 o(:7]Mo3Y|]{nfE(w_?xje@6V- F^VY %1H|E#&ǐIdĩ0Z0ZA~o#.^Ta/38tx =C/@wiiYsWc؃r*8uM(-JvY[rnmB+Kn/bTA?Tmdbq\w؎8Hal$I򤖕H,+|m)WLq3C@NdZq@ ?i(X\6D]t$~J$8hCJi*Bo=a.u?NyaE,o#7ti5yZb?%֊JJ!pJcQ܌7 A^ZӍeHA g9f1$otFsߺ?;%iG((&fRUW?h½{WTKBw^@4|>c6[a[H _A?Bߵk8H߹>zZ03G }_|5[!m =oa<9Pk}͌,?$oBq-Yi_ꇳv'@CimЁ.]@%`AV3iV ,YQV %adž \i;0OAyZ'|g%`pi]JSKiYxnT_ WE j뙋 ɭ`6E-K>Mu)WRa4f3^PvYg7WQG 1cs.|?.EGebD7r]o{U!uo.V[j2+L[69á-1tŏxi tCu0[VOcFT:qL1)h ιV{'Olq1t"RH;yox,OyB n1NdLd&O)_ۤaUlԳ7Wnll_wl^ T\J1u1v]8 ؗCwFؠ2_f56(-M܊xxFӰ(&Q5ɣAeKU(y b,1 -nT=k'm˷qlr95~o< s?4Z_p{Kt?dŤq)/5H4ތFMӉqI 6ab>=.cyУlA;\O#OǁPA\(=']&O+UPJl  Z27p(/ntSf~i8}~V8_*W^)@ |gG נdt1%njX-"W$)MwC(ޭt.!yuX+-2m Xχ.ZBEtK;UZ@HnERO b{SY-^ ,X;0vGoDžx3Xe}[}xwE!I?ة/s ^a|uu-Pԟo߁u+#z?s(*׃S@U>IGu+ ,"JhX^55x>q@hw'܃ݾebQ$oX3` jgyPVި$g{pE>Wqu+dWh+ˆkT=Q <||7]%!G4$j2})38;[elHFA8ֹM6X:,坤0ƅW9 ^AԒd\X֖B?71 ?JO)im<2O`/zz9ZtL4w\/qe)jWB.KHQ ]Yf .Z9s Pݩ@g)ߕ6kWVwT㒯Fnl`g!1Tz* 0B)Jey/+Ð ` z0?)oOцh2yM;kleCB"DfЋ M kٱd?P.gr 쀷+F/* P][?"i0Sv!pi Bٰܯ=fO _lpd#;SY6kKcN/~HR_Ϧ:`R䑂N' 4[%n^YMzNw { O먁d;4ÃjC4:./ZHҭ\H6.^&Oo m-2,';YRl啗ʟd j">"|y4,ţz!w 6*@-ToUQ$QI^ʹ|a,% >J B@c8iJ? {,0 'D/f&Ln*˔|S5&FuJ/xcfP@ hVvor`x 1yF;~|?eQU&`C#4$!m0ӓ̝]d'fomC(S*"AH@kOv)M\v2}'THXuP"s Dϓ&nXcVec'~Zy8<:wS !躼wN?cJN6#uqjrGK ȃ ="LPN(*Dqj,klOݳA<.E]yǍ0 0뢞`*ͷZq P+fjI߭&;dtSZ fԳ,oEl#[$ `j@0cp8H2(&q\뻎tPY{]q}WX BqM% 듥0.]{Uo8.xtʅk&G:=kec53T8ogGz j.-c]|4 s[hɳavբMfIj(awABj!gTſ4$.k4٦=*4'6AG;u4NԚ!,y٥~mQy:i%ZgY@\:ʙ[JPVBҝT] &+Rˆcތu1_9p 36#$i?4!0Q(+ſDqG%uh:#od1y`*UUDiEkVeA KL4xA Բ L2ڜ1,B{.L_`|M`SgvDII1P'G,ee`hBSrJ/+Zy]|9Abp6ޥ?0}aΐn ʴ+/// xI<{Ϙ*6.JQ+N%us``8q̩m!1x@S€giF-nmE'īU7 Jv,nT7m䬞i$(eG~׺EDŽVOl/'J׽((6 PQ2qpUqm(1ՋXU+?=f#s%JCC 3x=_ut9><嗍S %y\&38`F(b1>JT,<;;iTzOqD+I(b|BA1DM 1c$\Nek:b~ف<$AW)kd0$ Ï)/3& mbʦ!%c Kب2Wvf/="v$F dSTmUq.9#!H.%yVuގ)F8 ԽȰNDgBC@vD_{w+#ei\c|_`#), ԯt#/'پ]d#)Zcn#/ߢͲ;.K7+;So#ylvZ*ԌRo#zE)GƢWev t Tul"te, W~ed| ֪[/veYc9Ek-0Hq nʋ ^|aaIUA(@j:AfT^1 lNbG4B.<.1;w" nyOP2(% 504`Yoz6F6:~;%kt *a^ #k,<@ƎILAE|6Y$h# <e@8Ov{Ѥ@p#֫Gg[SPbBڱ[uGF!T]t2ҡ?9D$ӏL`1 (9&XN=efApHݑm,N4vR>ֳlDdViT,@aT0g4(æXP$\Bp,STDDENO*Z{/Zg0t(LY XH` ⃛%!F6;wp?'IՄ2xk̘τ@6"Ч0E^()`LU`l@_ ptc3t&3$16=]dhNUkBnk% y35Ā~CJڌf9'oP 8W\,5S\ϬNu"[]m,B2y~`+ v3c_%e*nT=3~-590aAv?OtM+$\9 `ceMw6ǁBT᡺N0 JR;j6cF5g]DB!@H7+t }]l,bвr~cȥ x6[/ ٙEC)xtV>RE͛ =]_/C,?*0 5TƔuPp(Vϊ1*r[QR\LkJBkSgLJ,8,x"b;Zo}":ӟ~N+=#85{U?M QŦ$_Rm1.2R!UrMǢ res\ӳExF\7֖'̓.]J앍{SE"INA•UK"nUH9BjEW swS? &~L< =f_urn@mG}Y0&׬ЙEUӬ??;[1Lm@ʁI8elQ' :«DwF] {{.9إ@-RTZ4}vI/4KBuLך+UKuM}_Z3۝$D~YS?3??t *^PsU{NwG}ɸX? 't}F^[cp ALL~!o dQށ{MJj{4N=iL ZE8JT mB!}8H(> v8TWλfMHv~J5:ڑNwMmgm^8!'L҉2>[Ieemu, GV#ٛ;]ԓvwpffL [ٺfe ^cEv8LB|uVcrk.J(\3aqWt(7'yFJVfrk?3@F,<`+i\^ pbq',R-4S`h5& u)!9j}ȐY"HuTCsO#`)eN7w)IveJ '\o!jz Rr |'(O,ɍh5v~k?;-W0o/_4/F')zfb}3:#J-?8]lb R'?4"_Qe) -*"(aCS3' U&(0_[ [Yo{zhbk?GEyKDdyY5wioº&ThEZqZ`e=>d} ,dX&43gl͒'i5\AAfM"[}Rj|8}ɶ3<46:Œƕj5]%V@ec˪=JxiԳxʜ}.{6c!T*``0|=8_K[W"ݾ4 -u,<{U%b<ף3A*b5 m^U>=3 L{ଥ?]GP; ("3XJև#.q&ֆ| BKi2D4,mrjo7|0ӍEWkWJ'+ȃP̍ɟH͐T;2 hϿӌ`@psnQ1YF X~ΝFcSs1y,em$ˋGJCHA1K`ܶHq">y%>11ӺȠ[b>Ͳd!QV|oFo5Ո&/e: . ;YHkH}L8ӄgAk/QoH vrv:rH S ¿t]$Q/l]p6|QPTƵ9Fw}jR ;aƭ{`vY8'ڍԏ'xikE\/ 0y*K.w$ѻ/W}nߖ yRR\c?nF BnիN OSp (8XH$& q ̦ F}}wmxu<.oA5QN 3s['xDo<XVWdaEX5P\Bz\ӫ8DuNq A@Y,1$ʯ20ŤMGSx;؋0!IHC>{HH#Io@򄶪ڌU` {A%̿oHꞧ̱J]wO=%BW"HB%Tג dP h9(:0)[LI'0n8,ܴGJ!8ٌȱ|W׮y wҊM]a\G{cr͡}l <^ni0p"74D-SPv x^xl`4 }BfFB%b(tvq.\NܙYH\9J˾!x-MSlfiE!:|RS|RS i*K8=66m3a?I1D(oiT8S?[2N.3=a!+c lLXz._qF@ <[TbN[ l3hQ7_nw!ټ2oM}WTKa&ͩRcEs8IcC/+'/ T4]PMAfnYiO 0;mWd ƗBu, Q,;>tNy٦'(o\6 "y _.'6ZefXH9H/XN?RҗKbYqGbyF؝t`Oxī1_;-[v]?#|/MtnCn|r 7}@KMii??s^@d %V,j?heo7d#&)^Nan~% }󴠼Aؠ*LXٰ-$t\Rw$H1;?z0sej$KfNgSnP\#\#q_(cٸٗN xVʊi O欩ͭ^ 4D h `k]`mWov$}EagL$T%'_-JhCh ^l܉V9!ؓ:=o*^>tsu @@뮏Q ~! pjz^3G7Poٌ%.+g,cAG2#N1scVy@n7FxpaFh B(bLln!8jU<tO@85O_0Uܰ}͇bt-8rd}eg8O-㡵kX6ڟՆ QTonz~Z.TEA?Mh֧;3ŗp/8ҵBV@< 'H۳IRv3I"EG ed%c`KIq"Rp2$(WX.Q2^N=qWVo*`ZKt|"@T 8邮xF'P^ u s :LAiDg KϬf;fеc>/}uc{Cbh>-VxvТEW#Ә3ɹH`0fX'װh߾TBf'}sφöC6;1=zc ea-tZ4R냋CԖGUgێ3{t_Ѐۍe :ipZ9a?F|6-I ,ȜiERCvɟZb1MNuKLM _2)KG.4S@`MkUzX[>YsSkF  P*;@a]UOUS0tʉ+(z QR z( 2Ϥ\n ^zi\s92.xn W7gt*QRGR zt qHvKn#$Sw1;OkJ0&jX3<#y+v7S9 <-Җ07ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFmgd_G DD:]?xSi*l%6}4;r>mWd Ɨ,B[HϯW>tNy٦'(o\6 "y _.'6XzAкDEDž@/? bpHW/U7;{D_]6eﹷ\=q3-[v]?#|/MtnCn|r 7}@Ky,%([GYͿ'Qs2?ΝWJ E d1p <Ԁ7 D;O?0iYRZ aײ]Om]PхKԏ&2k؜o1@&L4!Ɯ]ZdK]A(d= brjf72$)I\^ EW1J@T}[eZ]|({ѹ"0{bA7{wܗ'T(f89J?Op|!/d'M,RfA&[@s|vBkP>с߼XI]:؜U!7Θh"[p UUSTC~YN7uyOAp*ܝ=#L+TK&.=Eg]v L)C:Wx@X87gYo2j-,j xÃ5S۳RBğW])h,"4jUjs&!Ĩ9/꟩Gym|?ď=E]=fv[G٢\6l3L8XxgycBV=!QAҟI$o M .׿ADN+z/GerI[UG 8OllٺCWQK '1+Iy{NJS@qXX|[ z0!bVq143T]1O.k!DzC QXh:?PqJ'HQI&*ça Xv|:|`!diOMc -6@`Ltq#xv#~=:Ն2I?v$UťEx/`&XaC{Dx,C8?an]رP_;O(v\"8R^"H7 ȼsog6%&֋A x8lhg,037tBMktcdC0ن#{؅:OWeOZRᰦ  ֩!S%HM_S0d~QmTA<1:hiLėIVJvF@`Nx +ûN7eр(ט{RMj^ 2VC\_`Vl@Y)b!j_ֿuVwV@_aT~ Ul 4@kjgVÑޣ8WRG /Dj S OkWv/[tquR5gÊ !d]JN&l0ʈ>k+nl~as>Pc~%iyaEJi@]kerC$zGF#@OHr76?] H J*VJs^!kuY寀ACA=B+ /V꜁sFzC>K3z6]Y|ZJ|YJ)%gp@h߾{b$mZo$XМTm- $o'jIJeƺrJrXȼڏY秉H='TѸyf?AaNqFWq% +Oe20oNurO7Hf*hR<vYrkF+E_ԸņX=|lX+$ɹ,9ݙ% n|'!. ip^@uL HA>f vS*d]O'hK`N%5C4 w/b)u|ko2ܐN'~ 2kJFQ•8%(6,m솸TR.Kh(SUmq8D=I|+yݾ$٧!F"t˨*3 O\U1yṠ]bUs/Eːpa=yO)7Lk 6 qoqa ptmTC^ :R0(1Eظ*8b_MHXsF$ v6l|F]Z=Vjv &Cƫ8 ?R˩kz-Q\>LNP{?_HM^V!gO\IcF`t a G;C_Rt,(!U^Dc V:KaByD[Q{CMsg(x;|>ػ^Q@@XQ B]N9|i,|bJzvF\;ıPЙ\p-5-ν ֑t,7Lįs["$zAٳ3abڼ?/*wulU 2mh{^RN&xKل>A\36MYઍ%z)3k39,*{vJJ#I59!Q)Wuֻ 'eƙ"O}Q*Fs+(ETr,{PŹz5 J.&U$Sq/ P_hvL凖9dU P&9n/)brV8Ԃ#aRH҆h-8huuJ#g }HxԀ(&kJ'В4'u*p$%#q\r_CVxĐ+L*>+`$W*ÿԔ ƻ6- cu:냘"  lmbWRN4e>^0eil5 GkST[@}*R! ^i~8eʝ,8@E>G'q-@5ES΁v;#U&hPMw OUxr{bDϱ  o~S,s4wcaqTv序 \fh28&q06=/VqIt_pm"YT 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & hA~(D0,Bx&X%P$L$L$L$ /]5m$BH$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀ^P]t*:=836(}}sZ_pq^!]bIu{Mp?79qM]'sI Fyц K|<|e)٤Y|Fj8E22 "Rbrk'F< PIWhozOvrG ~9vA|5zDbm!mש'n$ 80 #}|5B8'ACه@?]_I01o W>D"%iìMbO0Lr2/1 :>2(Za6ww͹$g:8~6 H)26)+PIc8C>e{Gk9K;p:"VX=GεRf>ӓy$UcT ZtZPMS錺F۪JA%M˳oȷbtd@5ZXt$K&FbRG<`9s|<5_}\_~ef6s|aL6ٷ.䯴,":Z@Kup{&M NrYd7H*Ld r[jzR8!]} /6 o(:7]Mo3Y|]{nfE(w_?xje@6V- F^VY %1H|E#&ǐIdĩ0Z0ZA~o#.^Ta/38tx =C/@wiiYsWc؃r*8uM(-JvY[rnmB+Kn/bTA?Tmdbq\w؎8Hal$I򤖕H,+|m)WLq3C@NdZq@ ?i(X\6D]t$~J$8hCJi*Bo=a.u?NyaE,o#7ti5yZb?%֊JJ!pJcQ܌7 A^ZӍeHA g9f1$otFsߺ?;%iG((&fRUW?h½{WTKBw^@4|>c6[a[H _A?Bߵk8H߹>zZ03G }_|5[!|^zr⤒ Ev\[J C'eE=f$ ˭,sh uwLuljj .R AG]$&"jh|pRuP8+O.?5gj]y8kĦ*U#ijD'jsX|I\"BX /i ;<݊h=J[iMUzƋc[UJzEwhӕ*_Fv5b~͞HSHDNeO"xVytXy\͋2oʯ??l~}$|75'̂1&In*œK.%Skqꛅn9,#OVǛDT~5PB0 k?~||_b~zAO8KgZq/6DI)}8ֈ-_Si7v-d\V_R3gͪ.&}}P|; dgI0ǰp(I^q$ X3\H,7C0/o؋Kj*" `ebþ0@c譣mJsGuy~EU U(p+ÓK~1#ʉZݙk`,JԦ17I /ZfJ;z$Q7V9Ccy]e= Yg^| HJ_:nw~a3{fuNzsJtz{yŧAA;O(m ER Hw Z{VKj@O\.+,߁*D?݆7bF:cc:umd`ZzqAc7" nW^J)H_^B]J;(AR?A!MR-SFmE/I -~Xa)Pu\KxmiÊ6sV`n8$8E-@ٰjsV}q {aPixp0Ρ^xnҐ 6}fް(j䗝0NQgp_0 U-Am6jN@Icbu6ٽӋlȟp*/ ;3]es*B=>Il6~f #2PQY\0W ^1/Ѫq3m2VPj>rG&1kDy8&Pj73Ӝ0K`;RˀK>Yq)bwKvǍ|@(?@ev*^TyԔoy:@bAp-b="3)wQDIf0K [c9ؠUBi(-on@7b#MXN^EϘ%Ex([ Q#Quևkq_8T:aDmeQ.rj#KL23?"7rb̏-]|Oh'Mg+_&q$S 3L䍵M$6a2_ڰ7P~XI_0z_vAI> Vqkƀ?TI|D?X~tm63NdM}l'_A馆(a o?м1{*E} R<*@fȎ@~!7N%4u6X* H@kOv)M\v2}'THXuP"s Dϓ&nXcVec'~Zy8<:wS !躼wN?cJN6#uqjrGK ȃ ="LPN(*Dqj,klOݳA<.E]yǍ0 0뢞`*ͷZq P+fjI߭&;dtSZ fԳ,oEl#[$ `j@0cp8H2(&q\뻎tPY{]q}WX BqM% 듥0.]{Uo8.xtʅk&G:=kec53T8ogGz j.-c]|4 s[hɳavբMfIj(awABj!gTſ4$.k4٦=*4'6AG;u4NԚ!,y٥~mQy:i%ZgY@\:ʙ[JPVBҝT] &+Rˆcތu1_9p 36#$i?4!0Q(+ſDqG%uh:#od1y`*UUDiEkVeA KL4xA Բ L2ڜ1,B{.L_`|M`SgvDII1P'G,ee`hBSrJ/+Zy]|9Abp6ޥ?0}aΐn ʴ+/// xI<{Ϙ*6.JQ+N%us``8q̩m!1x@S€giF-nmE'īU7 Jv,nT7m䬞i$(eG~׺EDŽVOl/'J׽((6 PQ2qpUqm(1ՋXU+?=f#s%JCC 3x=_ut9><嗍S %y\&38`F(b1>JT,<;;iTzOqD+I(b|BA1DM 1c$\Nek:b~ف<$AW)kd0$ Ï)/3& mbʦ!%c Kب2Wvf/="v$F dSTmUq.9#!H.%yVuގ)F8 ԽȰNDgBC@vD_{w+#ei\c|_`#), ԯt#/'پ]d#)Zcn#/ߢͲ;.K7+;So#ylvZ*ԌDZWzl~V3$}b.%P e|u;/oq[|zV 6}+@J,MeykS9HsÚԟ k~>E:6.0\#iۯ[Viת#IZ-"W駁ɸK|3 *L(GGAݞҕaj}tG]/Кyf`<@!:\ Dvxe2P#%|Imzoz` 5kK߄9OW'++dՓhle$uCa#o9akkdT3g9՞s"hƫ#ϩԋv7-Sޢ(՞aOLJv1ub1q'eq%~0wO1"&:ޑm0ǡ]>zyeP3aPrl!wYE;矒ijYQƵ(:ε|lnƿUu B zN;fG;yW3WxsN&V/}B݅hv4*@Wȋ XEoN=١H.A}a+X bȺ};o:l`EQ%iqKb8{ eilM%B$|Qߥ䘹#yX @7[9Sy%@"{⦡!S_Aj%(UNo$_O]D, ߥrW:l@ә& R ,NLp0B?/ ! jK:8Q1ssY J?m6MQ0bFlX/S̭rKzBîgf+B7 `eAts7tvGXwiFkJ79efܞ,H>&[ KOG ׀ ao6z @7p}퓏91 naq:%Wc.j[>SE"INA•UK"nUH9BjEW swS? &~L< =f_urn@mG}Y0&׬ЙEUӬ??;[1Lm@ʁI8elQ' :«DwF] {{.9إ@-RTZ4}vI/4KBuLך+UKuM}_Z3۝$D~YS?3??t *^PsU{NwG}ɸX? *_(䁃MڣxmGK~bk}ƴ6P{2X:@B.Z *\ A Fn0Ɓ cx&a03cF~=jx"{Vu`P72uMI`'!է |h2+(TZJSIo%@>c9Zgov&ϳT^yP&xRO^-` ;Jq5R ` AlyJñ w $6WLqe=?r8`:/"rCO98'>hSq:M}RuQ} (YyeR è(a<m HTGb #irYW5 P%JR#)ؐ7l125 =] R 2PZoU8 '`[0WxnԞ`Jo8{}G STL:4U"@ќAii 45a6Ut3b~K`1_pocSb"Twmb,~^t4+Җ07ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFmgd_G DD:]?xSi*l%6}4;r>mWd ƗBu, Q,;>tNy٦'(o\6 "y _.'6ZefXH9H/XN?RҗKbYqGbyF؝t`Oxī1_;-[v]?#|/MtnCn|r 7}@KX Г)~8G$NGj(֞RH!0' [+h)z-ydJ6EfP^]XGHlxSrV;T aTC@066VE/W7+x T;!ߢq_벋[6R|ف{֩lH\MN ^NJ}-.Oy:AU+RA\氆Kirj4CE i8O%s5<#2+Z^*@#*̇r(IYya+Ϻu} ?\xce8Nr\}Ji֑ 1L ^WIm\HcGtF08X/)8 ɿ,_S!~OI,0}h ƻN=ީ_emTj.WH]pTxI?2cXκ<@bR $9PBu1_gÚI)J fr=J<2S@ 7Uc5RT$u`x>E:9X^Ͳ\f10<-Җ07ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFmgd_G DD:]?xSi*l%6}4;r>mWd Ɨ,B[HϯW>tNy٦'(o\6 "y _.'6XzAкDEDž@/? bpHW/U7;{D_]6eﹷ\=q3-[v]?#|/MtnCn|r 7}@K#ʂ-Q!Ҏy3( "g_~wdbr$o't7-۝qTvyQ_3yWA=enqaKMՅ_˩),?{'R}uߍZ@ҘأT8_`Tvp WJceQ\.5| *7T̿mw3~0ݞ.|eVRMɫ!2 Svs;3=Ԡ,[4g ZXi,'f'k 0T:x!99rQ3ʛ%b5%/N$OZn=**DDHzl$mS$-F,$ JU II"Ǘ+}vgx!i'iFE{hFTdAJ h@)ϽEo}R,7ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFn^+Lr-QI¾ y %E,[=+pHs*2VC\_`Vl@Y)b!j_ֿuVwV@_aT~ Ul 4@kjgVÑޣ8WRG /Dj S OkWv/[tquR5gÊ !d]JN&l0ʈ>k+nl~as>Pc~%iyaEJi@]kerC$zGF#@OHr76?] H J*VJs^!kiKǿUQbtFZnX?'wgvR~B%ğeb]U3mrZqbyN-}%YYyȧ~6i/,@[O6Na$|5Tv 3Lon)o %\wIP![a=2Nh *o!]?$7&lCqW 0P!HeΉI '` p< OВI~NjZJ2-QTDȕu zNR6R!W@$gPnQaW]VY6"vWR-M*ԨXd4,̍ܦ#yytYZ9RzcLoK_R} "=mL=YWIL'd+KN z i=izωmw\g(D\xUDb/~lNR'a6i%PAe4U9)~Y2Ms^=1}}T3S֐Xb׭hp58+7Vg6 *,aJ*8?섙: x{k4 9mg+w# upNa|=\& ӌb??JR. @Ga\ :jR& & h 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & h](D0,Bx&X%P$L$L$L$v_X$H$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀ^P]t*:=836(}}sZ_pq^!]bIu{Mp?79qM]'sI Fyц K|<|e)٤Y|Fj8E22 "Rbrk'F< PIWhozOvrG ~9vA|5zDbm!mש'n$ 80"(QnW +[Kܚpϊ{T1h]{J.dZb8̳PRj!Z~rQjJ-B,, 'BJT#ɪ57]؜gR:[d`gQRCɗ_o&R~4dݛzM7 ŜP<ض?'E''[P{|P@}o瀻E@Y;G?mrBG kqE.cXỳn@ _/cCގ@Ry%9A3z(]˚` #}|5B8'ACه@?]_I01o W>D"%iìMbO0Lr2/1 :>2(Za6ww͹$g:8~6 H)26)+PIc8C>e{Gk9K;p:"VX=GεRf>ӓy$UcT ZtZPMS錺F۪JA%M˳oȷbtd@5ZXt$K&FbRG<`9s|<5_}\_~ef6s|aL6ٷ.䯴,":Z@Kup{&M NrYd7H*Ld r[jzR8!]} /6 o(:7]Mo3Y|]{nfE(w_?xje@6V- F^VY %1H|E#&ǐIdĩ0Z0ZA~o#.^Ta/38tx =C/@wiiYsWc؃r*8uM(-JvY[rnmB+Kn/bTA?Tmdbq\w؎8Hal$I򤖕H,+|m)WLq3C@NdZq@ ?i(X\6D]t$~J$8hCJi*Bo=a.u?NyaE,o#7ti5yZb?%֊JJ!pJcQ܌7 A^ZӍeHA g9f1$otFsߺ?;%iG((&fRUW?h½{WTKBw^@4|>c6[a[H _A?Bߵk8H߹>zZ03G }_|5[ >=g g .Ojv"^3(~1 ^R 2cQd[AIZ1 T?Y7\'OIgW 5{P9{%> ?7IY%q55+&PZ|C`#%{~_[S+Y*}?p]P$K?|$G> (Tlb(b{XT`_O 4 HK!]B\m}"_uy$(PzVV&UC+>mA.Nqź{,@@FaW 2)5$ql|m3xYz.+=(`mЂ2nAB;6cˍiyU s7 "^WUT"4'iET3|zNX#,n^Y+hnZSI.pH'-&r7 hRRTe߽l/fI p9*Ycz,2iN`e c0F 3KGFaL66ؓ㉣'JSD/S$\RQj`C ʑ&),L,-8)1TZ+µc$]TP`n?]FWØ9lܚL`Yd cO+N[()\;~G ՋG%) B)H8Xw!ҋש91;5e hWO@ (B1~uܾ /W(<;QY ry s5?!aDi6 jv.3be~3rS2ĵ"*qpaf ʈ1ZDOD"&} Л+{HƓq rɽ:n-/ [܁|Kq-yٴ=O'E% o74/PV\xW/ߏ,Mr#7s>d(ۦ$sga;"pffזY ?~q˻Χ-< h^hP 0Tz?W.!sOjVwuI8F2kNgNgJg1 ocַMB cP=7V/YT,}՘LQjۦr7ec,[21CbьHq9Ò `wY C ?}rGuȼXh0ߣc_oK=oh5m'< W F_|Q//饣;_hՠQDrr[%Xv] ẘ_ 'o g zt88/0|B7Ga0[#ۢ\,+d 1Þ%2HbD\d{}K g(D8𩱱)ӡc%mrA0)3^߮/bI;zcU"c.7QWpI-uy4RϺbyh9) v n?[ S VemDI}hԼ[S;2.%ʓty,DbJnNtk?uBWx Iʕc^xXO)S|'?;Cnz+~:'Y(6 "_6D-KVOѬDq 5n 2v9_ Kw psœo#Émt ڭTlP5ro$gI5cBhbr(ƑEtVPK>x(hvq]zR4zנ6ْL)X|;fuoPDKFMnҙ'9{LsETGOJ~A 4t>sR6U> ASČ[8$d<Qά?myl-m;YV*<յ3u{2;tܻg,4HD@gS< .O ; XCs%V m D6)HnFqτ@+$U mM_U)&͖vWdd@NϦ.)Zcn#/ߢͲ;.K7+;So#ylvZ*Ԍ5cGP6-l^F!^?cmk]aL=_7-6öycD;d̬p{c#-c+k𔏿Tǧ5:~j6>QC,HH]βG>[.hZ1qoT]1x=9\{s9pz ipo*cFYt;o*Y~P٫]0Q3Ƥ]4HK~|ll̐)iA #`+u)%R &ړK5eGRW $C] qnĆ ĕGu<~V T^m2}r,.K,_XUD: {Cr7 0s*fݙ%T\g!IC3okfSඥ(2Zc OF_qi򿏄~}%R-BPvkȑf5ֈuUXY+vM+30xMɠ isp&T!bWa9Ⅾ-tlkٛ>:z.в?aLjI݅9R2Dd4OWMJBتl2[ٗvwt}늦/Ee &Dv T]Aa::QVJZ O/oJX ^W{l>H0p\o K:&"NZ×lx jBߖI4^h flHAd8KS8MpU( PcCqːՋsu9TiM93U<ՔMDO1H¤A*4Am7+ZBuv1J}h9x_'HhFz~2|>Tm'*Ues9otePJ}6%[Rpk^?[.Vٓdžx>ne NK=:&@v p{JePN*\2yF^cX_*vCa5S){5!2c#aގoiiM䦳ᄯ= uAT4SLrP&ONPޭw|"|aqu?DflP*YzJ4MEAL6:*}#A#F;uHj(K_(ɞ[/:RRs:/ X&' > M~M{Lԝ\ :jvg-ΦFl-Nft_8w Q)1:r<ȕdJ!ŨÆ#! 2;v'hRɭK4ۨ^{FH`u ao6z @7p}퓏91 naq:%Wc.j[>SE"INA•UK"nUH9BjEW swS? &~L< =f_urn@mG}Y0&׬ЙEUӬ??;[1Lm@ʁI8elQ' :«DwF] {{.9إ@-RTZ4}vI/4KBuLך+UKuM}_Z3۝$D~YS?3??t *^PsU{NwG}ɸX? iEQ߶mv$Mp5m/;~(%8K1LO:BU&Dexj7{K|:`3/E;Q ]xwźŌ[6b!>wZkm$#!17y.%P/+#ՁJZeH5˼dyRS)g?6*x,>&uy>IoT,"Rڙxݎ !\ٞ,ȕ^|"aDf !8<W~0vJÀIcLђM!0zMq̻67XJ鄇 xecK8B3҅P>'30wx_ʡX G$}tà9=Io[oIvE6Ta?<Ʌp/J'zZL/W:Zj-#|#D멙 6OevP,M3 _8P ߍѤ X-49*|/Nc(b>=9Qw_Ç|1+<+e|M.q#z~T0ۙl۹vD[^ _ʃ]2׮^E/e=4 I]EV+b.tԅ^#op2BOtQ $ujwoZY,նeZ.}_Sx3<6/䧾)]zJWzapAȥm7_HKX ::ҕsc|ȳ즤BS%z0 'LytYYm#L*H @_w'lIwxk5nCj>(mw`k8ǿKȲ,|$yG*4f3jdiW8.rs5gv_>_,JdeU@q{1=xY6Tyd% J$%Տcl z,&krWWؽb`QZ=c,v~7vHz*(.^X/j Nݏ|7(ǼZ 0vQF/N_"THl5hMk؜M~3,ʍ˜Ir}y9|'X;\ߢI{F \D~%jɅڱ_#S~6g a{}&Ppܹu%>Mm`"z=-8>4q XWF};r4Fu;@R1<0/{n*TD"L≜E_>dd?M|,#}V kTXNw|yq]k.B:.bLYDe̢-ZMyѕPj rF0-Y$ZOx$^tO6 Xш_00}~8>?6-A:{wkR%*ٕ\zsLPJLw'Kj˨)Tw@T?8TomVCyQ-߈VM"cw%nZ4e(?L4822m%Rӿ&6Td,3Ge9FRh*mMI£=wf}9*-+`w2L6//-9ڈxLVXb2U~!5Ē9lԑyߌ?oKq4!6E $t3yfF Mw`ӭNJu%8V8_H tl\% Súj26˄wB7m}ׅwB_M,6F숷<+!7 Um2kDh>E xj95x/ ?1Rwi-,m:ŕa32bg*Jzqmi#rMI;yZUT5a }~ɔM "VXx 4+Җ07ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFmgd_G DD:]?xSi*l%6}4;r>mWd ƗBu, Q,;>tNy٦'(o\6 "y _.'6ZefXH9H/XN?RҗKbYqGbyF؝t`Oxī1_;-[v]?#|/MtnCn|r 7}@KI㨴l?Ӏ?ˁPx` 7| =Mz,F.>=aax1E`V\)R@Sʃ{= YqTZۦ^HH̃Ao_Ͻ(e[_oW6C]5#q4J֖AyiU1(dÞFQd`E+Z^z`Cm(0XltȦ(хPd%46oin>_O-TC‰ﴅz-NUW0 ; z6џxWl-qI^rȳ|LŻn*-H]fwzFIC:uS#;y 87e^ Ԓ NJ'] M*4?}y>N*Yv(duHoCK%i:a]fZ aw_j`>MCBxLal~_>Wy1-rmS) Mähx"-TVyU(y?BU8R'Xodٍ3Ȫ:Wڋ} M㡇IƎV bbɡ~={17dB<-eg,dWj}6>ʎUI|wD7!HwXC~۶RH hҠ+ eÇ B\b.s@Lpv>A*ڞQ%KRUؒQ};edW 9e ²^ z"_X8:#FAv{DQrƟF<0hF +q^4'Y%rLV2ׄ xNx˄N7:]!>L2U=/+l゛dҲ/X B8q6d#gXc'6\DZYrua)7'6bs`)L*`Cfz܄Xy+IUr\ɼK̘ō]**;^\dj{=LL貃 'чM6 _ٳSUnĝ3:>ha4q*Y8sV[x62~~},֬QsI N_=-mPnhz޶,R zhivxeG a` i*c.k$)ᐷ9[܊qgQ]K \rs7`XCˬC6;ȕae-8OrZ?auqZM AG<ffmh*.Hn߫{Mbq UՂj5 )qG=P:E.V J 6yV%(oeLbS:q;-A:s326:#՛. L Y:k`N$ ?;^{~7d o@IvfH!iG_?5uzGzڸmrn:Z2+n|rQ,y$ݸ*3E7Phlع)9l6 bNO?CBhW|'[W>yC=z ?wxx u^蹓MC^vJւ1N\0;$Njht?{i}O>[bXZ*&kK=sqB( pҕ>)7Ef[Ǎ:B 0:Ocx3QM&_'C3?oW1A)~/C8k^#MV[BX$B1BC]NisN%\fcCMnڇ?Oa ·fg"ڂD} ZEOx$F]U<&6 o[_q }Et=xJ;S̞mO`p<-Җ07ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFmgd_G DD:]?xSi*l%6}4;r>mWd Ɨ,B[HϯW>tNy٦'(o\6 "y _.'6XzAкDEDž@/? bpHW/U7;{D_]6eﹷ\=q3-[v]?#|/MtnCn|r 7}@Kރ6!%cwK\`jM<D?n>*_do;y@0pK=jW(Fꌕ.=/H;g(l iY 7Fvπ6wT^1}\yݏۅ_#~͓il+Ȭ vJcA=aMlXK/bpfv$UY͕<;7f)f$|ԉi'=g$pw~c3򵍇Ҍ,}ԒG;}VmbVC1v, )eVJ 9v<"(SI9!>}wh V +a\@`źX m58+XRRGw 7<3nJC-[ n>c`03lL3 tR8>H^5Jr1͵R0#M  -snf%O|IBඟd@c)r-G=U vf'jb?8r;Y[)׹R;&#He3XǎWXCiU{H$rS~YFUꝜTX ¹U-˷/JoUԭV%F:D瑢q$ABb3 -ewS#kxir3pPolacqR( t;!`j_ Ū[8s8k|j w#Iw~ť1n KYk~w!< s!3Y힛HK(e:k*M3x@~k.0zjjMSsǜunC d7sŘLSc"[:0`g^d7'@VŜI˸FB]|^|p` "'rJ݊{ܴMqXIwwhS{"Eے6pCaq4 ,e&j%"s"͘N8m;R/tJ-#}U~ Xk@),3{r~?rƗ54U8tfzkM xK,[ ]>9)av3ŁD-QiD}L-dn|!U`(d79?<90T[BI:ilqE}_]/C+E]vnݥGt;0-LOx)(GĄRD[ vÓl8ɜ5i-mZ. 0N% N?"d_z(AG6 L)FFr{Pryycp^}hiSt|ؑRb^:K14`THI:e?0+Z*]9(^Ldd{eDZ1GO) 䃲o5͓Aj@v'7K̷Y+g֐RdQ@goN8Zkwm 耿GvaUXDK)-8&e)=H \͑Ϡzste 2A?Zmn˸IQNnX^KB=?$VYKs.9 )ϽEo}R,7ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFn^+Lr-QI¾ y %E,[=+pHs*2VC\_`Vl@Y)b!j_ֿuVwV@_aT~ Ul 4@kjgVÑޣ8WRG /Dj S OkWv/[tquR5gÊ !d]JN&l0ʈ>k+nl~as>Pc~%iyaEJi@]kerC$zGF#@OHr76?] H J*VJs^!k&N6A qvcVib?%jmGC$|NhG9\!^tPEvןJۧהc#=W"N+dOX;H=y`a"(rlPot_ _b0VtA~n$d"@r S(ş0Us1rf4"Fee6:Tu馄nC$}i ):ϡw,4\$m'55"O/@UX_I5][n)~a[}7w`w`s =alC6JWX;6 !Nacg̩3#f[ %fpb(.Jċ䱪2WhҺEׇͥ=B!y޴=~sW|[qh]Eu\)FW]FH*dI?*JBa{A^GNSl 4=V y "-hJʤwMc=dh\o~ʁ™,R:xN:Z}9TeE %:kfLS}|`e#d (1GlI7Ec\U "uBF'"\r/_;ںZx_+kNcJbm,Y. >XVNz1Yȁ K"UxyN?)i4NHB^cw |S7Z3l1GXb[{~o/bPi(J2ӕTr6Zos`cX8EikO[ï^(p+"pv-LtG͈z8? %O\+;ceI`µY̻z;8ֻtY83Jl\J],:|vo!"}~{nΌi݁ݲ,3X`NKF|)%Lc:semmvid}U_:/slǵ螜~QZ 0p\-Y"jCGUp˩`.vę(J/^qb\S,+oK~uY&wǖ +7~yƵu͉wkCmU?\ ee0amAF qGt9*B>i!i- s |aO(Y0dxӝ/hjvFR$Cqs6b^o/@p1=˛ZuakpޣȜJuG>A)"fW$u+`)/ɖp)ߓ7mɢC 5E\OZwƻnf3~ίZ`K0W%`A!#ohr<.Nu?k5> l*#,U@ rTN",!K$CWQ%$:c;y˴WTad*bK)4fɎnrHH#n"Dcw,q{V b7D0hNcKIcD+zG/<iތHQiӋs8QoiQ󼬔1EX'.>B˔QW;@ 7_IwO2Z?)Rx{II-D~2khY0᰸]\P 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & hYI(D0,Bx&X%P$L$L$L$~hl$H$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀ^P]t*:=836(}}sZ_pq^!]bIu{Mp?79qM]'sI Fyц K|<|e)٤Y|Fj8E22 "Rbrk'F< PIWhozOvrG ~9vA|5zDbm!mש'n$ 80"(Q_@N\;@c l8Zl#D}]MMvW% ÏvܟMc8% t lBGreD4Vh$8QpUNxշ*alEŅ-*9sEKO0cu4ym,IQv}L6ˑBJ]YsAh$er4-%B"$݃ncz.6 (ܞCaH{J]xclY͑V.`@\fW9~@\j:d: n@]ӄ-%IT|`WEL 6ARG"M1Fs!G^͇dΪcxWvSnȔ,a:i;yAvٺvNfѬJ[Dzid@I-R _}sQ4+\X̫Sv&8b #}|5B8'ACه@?]_I01o W>D"%iìMbO0Lr2/1 :>2(Za6ww͹$g:8~6 H)26)+PIc8C>e{Gk9K;p:"VX=GεRf>ӓy$UcT ZtZPMS錺F۪JA%M˳oȷbtd@5ZXt$K&FbRG<`9s|<5_}\_~ef6s|aL6ٷ.䯴,":Z@Kup{&M NrYd7H*Ld r[jzR8!]} /6 o(:7]Mo3Y|]{nfE(w_?xje@6V- F^VY %1H|E#&ǐIdĩ0Z0ZA~o#.^Ta/38tx =C/@wiiYsWc؃r*8uM(-JvY[rnmB+Kn/bTA?Tmdbq\w؎8Hal$I򤖕H,+|m)WLq3C@NdZq@ ?i(X\6D]t$~J$8hCJi*Bo=a.u?NyaE,o#7ti5yZb?%֊JJ!pJcQ܌7 A^ZӍeHA g9f1$otFsߺ?;%iG((&fRUW?h½{WTKBw^@4|>c6[a[H _A?Bߵk8H߹>zZ03G }_|5[ [B I{̖k9oY a+|>KjJ={`:HnN[ Y3_ļǪY{vP?߃Spг*nGGsW0oJ7J jYNSAɳyE أ)>T"KtR pY sgCP%* jº͊ FS\qk+Y쯲t j*`P筚OQ!A:J俊Z#2kVR?-:=0_j-AOV1XTmFzW8Nhy7q[ͱٳ?Li}YW`*ٕOd/{D_B zReF/9Rˌ\ IHGH$#AD)]wT } si!).=QyةvGU(|UQg6Is'7Ts?J Ҟ0),L拾:{EK{%դ](*8_DAO.@^(HELhΔD"XEBQ^fOnHhteAK2ODOsMSKKΙï 1KK%X{zS;bfAN'Isn.C0y|5Rsq'E6x`'|\UMqN3d5 KNϯ_QG=AO`MNf^$@?U-nS3Y0ڽD/K]tXRxD(?E1S .v.a'?lm 4aʑ/ &Sh{@C]!b#.ր@ko㒰EINU .'\x+JD Q;7E&_³~| n dduQmIE+vʦc0. x?\KY1SpT~FLk,-๥V1xT8ŬSfR3zdg ~7@AKSV;azOIm`ws>{Ew; <6ԉC]_t AB}St$ C9Z! /KtVu%EtEx^S*Q5Yx&z(~'<ŎSOWqd.eF/̄(& )dϲkRu3sR(Bq*-Z5\,[&WEp.@}a5fK'uٍEǘklij٦Iv.Qܨ1׆GLi="fD)Zis{zK.`*ƘcVlPQpe"6`+( W5v|0HR8w =&Yt"-N|Y=EkKaҷZv:>5_B1 )֕R.!9AG whY-l_C@b.Di'V×8zĹEXƓ̱ib$99}d6!2zCFYu"7r"ps=s 4UAm oh"v^ˬ_J$ kϣ9Π+㦙Df`x'bn1@r ]rL8/tZ&{\%x`]f4ws݉b$ْʿ0V|1)s_6LuDƅQTDzH@kOv)M\v2}'THXuP"s Dϓ&nXcVec'~Zy8<:wS !躼wN?cJN6#uqjrGK ȃ ="LPN(*Dqj,klOݳA<.E]yǍ0 0뢞`*ͷZq P+fjI߭&;dtSZ fԳ,oEl#[$ `j@0cp8H2(&q\뻎tPY{]q}WX BqM% 듥0.]{Uo8.xtʅk&G:=kec53T8ogGz j.-c]|4 s[hɳavբMfIj(awABj!gTſ4$.k4٦=*4'6AG;u4NԚ!,y٥~mQy:i%ZgY@\:ʙ[JPVBҝT] &+Rˆcތu1_9p 36#$i?4!0Q(+ſDqG%uh:#od1y`*UUDiEkVeA KL4xA Բ L2ڜ1,B{.L_`|M`SgvDII1P'G,ee`hBSrJ/+Zy]|9Abp6ޥ?0}aΐn ʴ+/// xI<{Ϙ*6.JQ+N%us``8q̩m!1x@S€giF-nmE'īU7 Jv,nT7m䬞i$(eG~׺EDŽVOl/'J׽((6 PQ2qpUqm(1ՋXU+?=f#s%JCC 3x=_ut9><嗍S %y\&38`F(b1>JT,<;;iTzOqD+I(b|BA1DM 1c$\Nek:b~ف<$AW)kd0$ Ï)/3& mbʦ!%c Kب2Wvf/="v$F dSTmUq.9#!H.%yVuގ)F8 ԽȰNDgBC@vD_{w+#ei\c|_`#), ԯt#/'پ]d#)Zcn#/ߢͲ;.K7+;So#ylvZ*Ԍ/m%##A,4ŵ{ԯr (q(b— R3A(!k!AfjÆ],x7vpyeZ DB)nrp;}uRr .v^o\Ұ'Mcz4mA?3 Vj;n\:q7ګ-.7xOڈK}cLO-bc%9qE<>r(ٶMu5Ckk0V=PG:FӀ-(ZxVh:I䠒bdwm RNal%EņiP:r]lCZ٘Q>ÝcMZLGp9BXH$ ³vӭ#ԹX jgǯ;ĨV\v~v4'?>6LkH6. 3~ٜ \1JxaajV(öy'ċ**r[!tGzbФ-F" |= zГ\iNba^jFSE"INA•UK"nUH9BjEW swS? &~L< =f_urn@mG}Y0&׬ЙEUӬ??;[1Lm@ʁI8elQ' :«DwF] {{.9إ@-RTZ4}vI/4KBuLך+UKuM}_Z3۝$D~YS?3??t *^PsU{NwG}ɸX? ?Lе"jcU#ø+50#f4a8d'j]US >- L eN?Pesd3(X3Me!LH/S[:`R|NFp@32plnO7h6UΗ1^/G2pml wK? lTGCvfv%"mm90.-.(ffevaɠFxfMQA+WxEs |Xz!0%Ygt'}Îo i-0+h5} gסisFsJ€X!gxy}>᲻RGZ^:Q^+RpWO$7rTȷ 6cBU>4YOm%-jcl7(`Mh]Z_ED&L[pEݦF{pP)cBT[s2zق4<}2X)s۩*赤q}EB fmB(_خZ~x!-NR9UH#|0ZEpҋgj4 H4۟ Wam cnG2)Ӷ:`@8$[~l<^%L@f 5TGsnd⒊:'XxαAc)D,OQC?|c0pG8T3~LTO+SYW^1QD|ǐQOܫsO"4XQ)7g_7#:$YBnŝSy҅WpHR?hBib_:CHŸ|H {-x3ƒR03JهFh|6úg7A,M{ G#@k'/HmTXB!3m+XHΔJԉÚ L4+{!#n-DcN{E4AC#g\2`$4A22Zb5yhz. )iaثt4+Җ07ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFmgd_G DD:]?xSi*l%6}4;r>mWd ƗBu, Q,;>tNy٦'(o\6 "y _.'6ZefXH9H/XN?RҗKbYqGbyF؝t`Oxī1_;-[v]?#|/MtnCn|r 7}@Ḱ "fZ!fCtTO{U7l^cmB.sxIAK[&ȧVJ0h:8v#.[ zNBmR,#_Ar3r`ݾ찖j~6p\Xʜ!H[G8Qpl:U30i`#DK&g]ws2ɘDkLQE˹3>6+a9SJߢ~Ȑ*[eC">aghϏ$(\OXXX3mOl~1*<)(aFӅn:X0u L^]QlM~CNKt-w1UaCYuELŃn X5T2W1`GTfb>S xoylKb'Sgn ޭ%Gbο륂5Hq}>:`ao \iV8UFdW 0%hHZ>XRԤ""5='S1n2r>gTն}${z?[F/[WLJ6&ܳ0>:pDžJ}(Kc0˘Z(j_:XjU0=Pzj(G0sy7S/1J̸hKx KW"u5i̪_i(0({PaGw/h8F?"ezblKcjqs㈎jZ A2Zj$+hfRAsV3^n% ?_23ZͣrmWd Ɨ,B[HϯW>tNy٦'(o\6 "y _.'6XzAкDEDž@/? bpHW/U7;{D_]6eﹷ\=q3-[v]?#|/MtnCn|r 7}@K/}7fLxX.mDTGrY'G2byOo*Sm&h3zz6/yƪ+HU'l~^ gN(~%qM$h_KrGuDX9.+ibFade83UvNq}f|~~o@stS#en}^{q".3gNڋ_uX~{-F*Q6z>i T|\Src_I =d&<"/ܖyz/RF9UqK_ 8gI]> °AwF^/W#b4 ZH]_ !v ~ םO@ol&a,j~U܇\3.IV2AfJ%lzUaJeaz-ihRUB]9 }Rfz]1m<;H]bh, LrYmTM؋hڟ2TfԶ׈ٸ bD1 U tejQ MUyDDAj=A<8l[ӿ2Bg=QC=6!Pf@eiiG# R];'7b5$(<[rM2R{'&x猡#TVY&n-vViץ`fY;f9\y˿5*n{r[\d$ )y*葳~mzA6ۀJ$!9' In`1Q?7D,R罯cqGy"KIq'ݔef\TJvU:{ r4={rs ŵہcV!1%>?!f@~oY3Cr8I'iDJ׻X1A<ʔcLR!D97Tw.p3u~5~H &@'i [{ AǰHʖ 1K-,KQ#O=ήj=VM:,0d ޲I,Q?"O5_ p)4ZN`äɈ}{%_%9[!{7E.ʫ'IPHSwsRu=ʤ?dDV*qRlk/ Ӂ0zEb;@Eeܬ:m%25x8\:m%b C򈊐AaS~0ri9:ҚPn -/^?"^pV6 =kHDR)txLi*U+;Z,?r~ODq?†TCv`(%+.1UH H`#_QVtdEIh[C#b(!^|L:q CA,㞶0~7ԃJKQr@JdZVq曀_yŀ@5'dUȼFT'_+H>/0Pk灓 D[pcqBK{v礬Սw[!p$|)ojn#$^zT9K+Ɇf1x-uDN:S#ՅA*,m`y!58nxQŋk\C?drT*Gf\GTXiWR9q<)ϽEo}R,7ƟK?095;V 7@2x;4f1cnO$r#s26zʊbrk}RԜW]()z24݊K hPw; ;L)ol?0&ĕFn^+Lr-QI¾ y %E,[=+pHs*2VC\_`Vl@Y)b!j_ֿuVwV@_aT~ Ul 4@kjgVÑޣ8WRG /Dj S OkWv/[tquR5gÊ !d]JN&l0ʈ>k+nl~as>Pc~%iyaEJi@]kerC$zGF#@OHr76?] H J*VJs^!kY(P&Vm5O<㢈@&uo&B~9JJD&?O)忋q@>bYCGFs y{q|X 8@"p5s=ņ]JUmQ%OYh4 {jՀXYrpD9BE?9. 5 u9xL9"vb wHYB]x  s] W5(>o>9n\jL4?*.]ӾɖB( .b1JU\UPD~ sE*eZB蒕q>m9LwOl?cKOmR oKXDWN'LT9ZU; ,ֻ:ߟK7K-Iae։rgkDH3 XoT@wDo~Z~ DK,yg j~)p$0k0 1UAg=%L!,RHtH/סF?aoSfrHg]$#"j,f<$A׋;(熫u%aH0l7~W׺-OFa_a+,dWv@cc.w&>jN,.஫(&@eɷ@#&WSn;^w ,R Kr@{teڥZ@EBSy} "mU]#I;.2<.Z!@ Ab3WIUl`9Wn4:!Hy1>u<sFF{u\PU,mӉj?`7ƽ%); K' UG,Je8 S/D! (AE~—uIB/-;۴^􁦝Hnc4OoPg7PPDR:Fm;VK( S5C1uLqT8>>}N;=[rܚiɵsBq_[|q'C=r}5X.0 e {eu'KlF#/2e R rh y : gH%X#Cv)h+XsE ;lQ'˦^˹/߄$vj,׸ɍ ?; ("f#=4bO״4ǀO'1x}v!X2s}):Mhkܷs%&om?"(ځEs&uՎXw\$s{8k獅-i]%Y> qZ{}0h1> mO =\퀰7zRׯ\6rz?.]KDp., P[V*no!0O&_֝qvsŦ0jpkK Ia(qQԖCgQ1G2uG9 ? ů! xz-F@|?82ب)&i^M 9:`*L ׽7n=CDA2t!Y{:TIH@"(р"{ \TGḵ̊D_U7N w-c]np2&à灷Yx(Zpẗ́^O,˄M>b=6p|r>3}jmޭsUU)!`97nɜDe# "F=܀_ ?T{u֭%:9a3Ƀ{@\~}ꮒR{]7yIn!ᦿ.N^d s^l~9a舎Z_AR`q ||21W\v5瞭t㧵ibLEy}ʏѽ f g Ggʊm5J~t-&KHl? z}L~p|۝ 2Ah+],`fx %KF:?} )u@ @Ga% :jR& & h 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & hfG(D4 %( @IL`$&E5~3P IH@$$ IHPcK:1o(&l |,Y _j +jBnɀ j륵Hgߔ|XYY`o2֞ėt{ǯu̩zN$AU?qE.3YF.57Kp" s@p*\ ge3 ~Mu{%0!AUszZػ4u@:NqwRG.L&;蟠Af[etztw4An>" 9V02Iy\F:7nl<'/:kQyE&s?uvv:1آUz~l_B;K8Ո4U@I ٟcAGJi0F5g9(%jw梥$3kJӺ\'w˙LH\h iL{&[FQ -Glf z sAG{G+>͹-Znj'?$_1j4jtIfn,{%Wp\a`-`J ōTbFGCۑc>_ymZ>#و:?'e0OvK 塖e{~jŒ TOfPKH$zdļuug.\>ܯk;n%`NuT.B#cx":5,?9~y2{JF`+lkA)X8~`>ѕM\H4ظOCoJk jZt"84IrTN^􏆯yՌMIug-4Q=p]SPV.|meFvqp[)Ǚ!9-mnkv @ѝ1K2C$m5e5nM^[;_6wk1ـOTK$L 姫@PL󶤿; 'OfK`G+8z?Ii~YB(g|뷺/ wuMiVx^뚵ہt\6P59n:&CSm(= D{Y؏糐J:;fUpiQXOyaPЌZdgovw$g;J1P ?mȌPE 0TDž_:;ùD1RnUױ5Nlݟo83.nPxjZc{ P{[P+brǧZizv) RzWc~mj^:l=Tjc]ӧ:VB-oy]PJ*vغ oө+*sϿM:4# ;XYHHD<X~{mieI8wtYQُIAXp^\y,Dpgghіk}\2YqWRn. ab.AagWw27 eTO }u}nK b&ԑLwDTcljG]J b@|"H %9:FjL{RDH~"Ԁk $ @+(Bn~2YD,4&CP=̹G8p5-ꧬћv%EvQ{KE3 M@p, K$x˧_2bf-0D#u_[O& lN6mSR|ZcPr2R|a@nWdp'x۴#2(+yDV@e-J1r4:,,ꅩz,)ϷҁxFww9yW3tFw%= R0E2",b0mvZia%юzGU곦0K4 RJ#B,lJ *'WQH8%F7ASg>k[m|F77c~~Os" YBzAj'/<>Mw*|[}O+@# Ə%ވޠ ݷ@<ʴboJDy%,SFf8GouY(h3J?Np娼m(+1PӖD.HN:Ry_q)ՕKR$s1vԄr)C[FQ@\y2X^;Ij%`N +F4=7$c4VRD8dZC%`yZ!ùg_Д}+MzA]fgZHT%VllRb%}0}B Ƶd }%gs~}_i^=acfBMFoTn%,f/OO Vuo|٢ {?u8g[EJSͤB0Iz ѧVҡntcxklj>y$7{4^P= ݠ'(DFϒG^r9zQF|vcGRp&IQt]FXI{-?2/|  'xEG&x_ ܚw$џ݈˩p~RZ=\@m* ?LBBH0.~pTi#Vf,ja 2O| MCZ[#8sI##cnѪSBap{)+WUC O8bZC*:d6AS7Os؍V-6P191>`oq՚҉yS=<9N7v<>cEZ*b?8޴،lʆPqB/7@'dc^b y/bmHXF 5A}$},?汴c;Y- #dФt]$E qH OU8aQw@խr},,{O`<\Hh3ȷ =RG m)*-`% >%Jak^V ȭdL w4!RY1` ;dRHmӨx2QR-|=$=?qc',E0nG >RQ[lشhwV3h/yے?f Б -<9i:i9cٴ2#px数vJճ]D-}Up/~, a2a&TQ5e!f}*%ozJ"2*Ha۽)|Xo[w[A 2hHH] wPCcUX Ftҵ `w+Ke7s_Ul|^j)\5>?Ȅ$70 y6aUAd6kϊa*DߛiSvGHX@B{Z-(':ȄwÎ No]Wͺ>O)6gSĽ8Ռp^|ϗb}]ǶLW$}wv_*E&Sv=l۪;qf/ˢm`͏fuewγ9SWbf^*0iuef ĢFejm?wt ɻaysB7C0 ۑ=Pl!ox$9 ^PT@RŨҏ ß1V`5sWNl]9.39qg Yރ~ r  }kMtVL-_*\n&_i.i 2U:A4Zֻ#e?٨*aПyr=5g>D`r F#~d =WPVc|6!|SW;z,(sF4ψٵH?.maCVoؓ18e\qtr: D_ ~|4 V`?L ߺSr E%OE8{@CjֺL>kZ_oCY²آ4 "6p~fm{, tv5$GWn%9)|S<*U/${6^GxstHMHp9tH#(ѹD jX85U@X~խy`~!g #$ a*N!)u=Ίq|Ԯ !7;΄tNZr^(Jo2UI\@fs]Pzsy7w#%ٺĭ%' gو_YRjԺ8,ؓ&J;!ZUO>+Eci. 6e鼻U݅ bFeoMK(:ݲWQek"^MaKi/)(1Fz3b m2 )-?r傽{5"ϯD|PF!Sb3cgI\oDm N}̧G@ iGh>O nCgpFJRCW43(h Bpڗ˿S@lP}5~hO7ge&׊r;^iixUrgytf`5 +#s( V9A=J"ҏa c8Ώ犯)tӦÒSH:o (-+Z5,YZ/~12%\M,gDs*۳mJ twDClbacGz כɹl8Ot)(d88d!9,®uF6f6|}ǁ#E# 6bxbCPW N wGM 3%?@8~\0~%l5e~<2>w% W2(=Q4}pkTw t'aEnUȧVux>[KtJB^5#ۭmMYFWIaPt%EjRx((m( + F7/TGAGnf ~}0/9xݓ]B4-_SóbEsM'W)YCvrrxdWgsj'}l0Qo;@$=2!6oIc:z98 S^sk X ۸$ 1=sO:˿ -nH"@N}6gL)#" ӾRġұqpr THX%>5[:6Rk0B 9ρH1y%"R͊pR : Xh KKlFG,,{qvblFtrZ~UQPNf,Q[phl!.̗4\-|v dvag!y+^ HWe]\dA%Wer6p{央B3Ѵ}iSRh:L.p e7~щ;^n .bLH7۳:"cNfY7yr/:K/u}+b0Q "f3EYl[?9㌰;Vp2%WƲLNヺli3@"e" ie$UiHx'BXu#De `BQk2BDh-$PWx2|gW遟M@◃'i5+ Q:L uI!'LׅL.]lrm2kC/uaEK UxoYy|J\B:qvt_ ÇK4u eE/[+.',wC40%HW+{k;x4tEtlǫ}.rC^\aN-SP2oOՉ6հ8&şb_rÅ :]>(T /G4nڑwN܀_dWˍeCVýH5p/{{O"k{ Y4x8-j'B@E- lR̛0owxe8VkKɨy@h"v2H"ZhuӸCn{op7ag^OܜM F` }@}h\sӴʝG$ CtNe;4x=U Ner…Xy:r3 ŗzHYkz޻AC[\P5 BIGIWSJK+hTI"9UIb!52kF ½̟ '=W9⧺P]ng9bntF&ڏ"gS En~Phړ^9`; p&4c _Y-%˖{)ءOiApIF:χWAW  xOAr .P 'WҠsSMI&I@w]^ܥ! OkwE~X-w;R k vz>,Nz듙QX/WM![!O"8FU+c>O1 %lE0P'l,~.f ͔ F{` 0 ]?S$ "uUL:Y{y`'{psRiZ?, rʙRxsx[rInHRbyrBҏC(_:M V{ 7^s.xJB`A'=?N=Uz?";1Z͂~<n$lzRgmVT| #Lʹ.@k)E؃(bFHCi+ueRKM(DRl D==6`ےƐ8 R3tM7!j̞4>vT=4vC]%. TMd$[Iq`|yH-#:afV,<0xS>ƽKi[~I@oC'wp 9`sFLW.qi^cJ*7i7"xhE c6! 7|F_f/bXv!IyV畿+<i  +A_R`׳|^8b6?j^ea]MzৱBf緯;U-*hOuw Ѩ\[.< ՅN*V12ǀJkL-\$cs6$BO,NS" `C0񒕨_*>[RˎՌG>\-a.ߎ>ҟ,;X}^IJK.D~W?ɼfaRE=*hihu5s/䳕|Ctm`P#RexdW@|9$ExAϓ#EOR‚KYQgcD81Ɣ>cږ[o~a '1+.NqѸ]n-f'~gIZ% 0P ,[' Z%g  f3Qlo߷G@OY|`jxFb}ȽELm=)ٕqQnJ:b@?yљP_Y@0W蜙جdsCW[p7ZQkHy~ѡ]{NR l 5[I(weD!(®(wzŦdw~k' /n&q\qƷ!+,gjBr>_ځFlиF%QϤ%Q~J}>-KTvۈPP US`hpeQ 72x/ cFYqdU\*_bmzi^"1vM~Nw bI |!̗" L}mYE}a!\PROKQ;ay V$3gp9=̂W5^q&N.pˀ+ʂ#Wi]e4?AqFrʋHNݎ[9]lUDrBtBZ4^ s|͟xdVzx.Xjvo}ky9Uo@0`a @$G`>CmP, Mnn*Ƹɐo|x_J;+T\_!/uzA'1M]q{#@ );+yPR;s!f.7 f0 lIO\^ī}?k=t':Q߫tG܉19iKycm"[=[*m$g ˄6*&I``"TfG,pc) h勌0BLp2LDFBvʫ?]Zp 8@n];e%Ek2>rvblf\pQ$Q3@G%:˿ 4~wtG=;>F)Q[1` >f)evn%e5Aވ#+BwŊ,wkκTnKCSbv[c*4 EѠ%!+O"5R΃oH O{Bwy6vZXX`'ۡ|'C3ޙW(B?VD5Ljp7#OCQU]lD_Xs ǻz/pG1(a]0%D#rH$Vk{n7 W _#Jr)`n3}@GDYA2=ԯr _#b!kl{k4sX~n@ kJpF ~^xL`<Ŗ){}M6ݬIdԻ[Rz9G.a1i/֨6L6oz0y'K,T"0Zv\7` Cir/ף$ $XӠBN[.plvЯѶ6Ӿ O#][ى֣ͨ5`9O9_<z3t=pz;?Wf &ڔY*SCx"(ߔAV$]*-b-EBVO$p}bt(FrEXiUX]zXÕ#iJ,hGAY .Kk*̓p{lMѫ=]okgl,^* Pw 9(QOA*OUX(_ܐ >ER! נN/07k"%T Η\*#)$A+6I14dB&''"HVĜ!|Q'ͣz];KNs̾C$[o%NGa$"p_wׯ( i$e. nx7'jVquí1A/ 2LTSJO0.ܒkkTG}#=H+FC`ܐ2e| g˅D G¨ݜDH^q#īiK?ƛ7vK 3{qLU*|r*<84ʃz wPd!ɳ}_܋͎FgD'A\["&_/h[o n{g[&%ipٟ 0w?@L^zG;Z簥;ю #&SŞJӜq` 9i?3`.Q3'Ct>_ fk mZ8?1? 5]  &4z:[絫M(F(e7aԻ^\jih.&5Թ!Jke]o?{e/=`WGx¿F̑\SX9HAD>|T2h!&PD6njp]/ x}"Nh՜p0rS(`NVuI#&L zTi5SL] oZ4Â_WW::]')@x{."gSE^u (i~4yR@5#%~mɾnϺk!؝kR9g;`ƹÔދԨIlJ^P-^}dFJBzyrh qOQ`f0=E][UI$ $JχAYM7(ğ ^^rG ZM*s?P.2/Ρ~X\qcs7%Zk0<e-#~.N)3x~t (8y1[kyF #cԣd`gAMz( --U_mQJRl>Ty(|8W='&TN\fiпߖl^Dy 7t+/:Jgs|Rzg'Lz_iIF'D-8%nc0'=b܀`:M]76ܟ:?YDHL&ơ*! 4[X@6r%>-(/ jtMoJrŭeMlH:D%;еyci j>x Z'\I6w*I "^k>QS |@|qRsj?5 (g'l}N@ۋ(=paN* >mY})#s+.Mʜ-@d%*G\b 缎-73@06:$o#~By;￉0|'ZڒU$oU8Lv9u} gȢZwxκɪz/80;R1\c'Sd"hEK| )F`S7@cQ%5''\0ռF"cU~OqGYϏWpgmjO4 [cp҄ 6.漲Lïl$h5h>QM6vr0 [env6JmT7_yr6Mr>NV=y"7%spkb!XM>901w#V8b*,v|o=/؋}4^θLZtƌ\O"`#2i r~MDŽ|(MpExk+uHmw3=6b0DjSP󖄢Er!ӵPnG6P=m8hx:i8_"oј:En?X>D\D 6s<Ov/X\2f(Cn&V*g2 ց{^y7zƽEf.AtgI ;ل`E2r<6 mЃ\`*Ԥ[e |ҥR8x) j_e QڗpKr?Jwk3ev>?Uvg'InU5TLVӇq/H㕳W**<>|/VGnA*w fX,s,59$U<*2w 4Nɣk(ƻ~N6?F =!f4A6 gz6V#P0j>PzRШF271=l5FVoLJ^k7g;%H0cF܄=3/ ȓ>nmPćmH}E>ǟM>ٙwt.{˝ʽXP{#SkE pr֜ٚ#O?L|f]o u۝$iSk~pWFo Z9]DvڙnwǴ2 ÉġZc.#/Οz "љb4.Shx̌g~Y\2s蒤IUpl/%֋uKs: {Z4Qg0ё^: O8}enU3nm0]@oX[>7~)Ifn``57$#݁aͯElbJr|h! Cg"osܼ>i.Í:nK<'2/qVOU_6P+Jh?sUgIݵ^U5eXd\^0ED $D/7T۠/iq䰊ɐqv 8ٷj u^uǍ-0& 谩` C@ {9j;2KR!<ߣc"5]S% ֐<ҟJUZ݈hFTSBb.!P2BlZ<;UCsIWw9̹pRQW%%KCOSZ6,YApPN?5g8'TՇ{Op caAwYdẄ>)~α,[v2<vtbeωl"^e([XNfcX[w`;DZY˔H:yH~P\~ַU=#_2:<b*i:O:Q*$.fS.7^ <{jk`$_T\Uu;= Plnc~nv!,S84jm(x[iƤ-KX_曳a'BZy̒S/TGi#l{"fC<3H#0!'K%1_9/qCz `juqqjY˵4Mȭo2H?!VOi-'#*oJݧz^R"XwDK^Jbе&A4n>oS.,=j BH.$}U95hKkvU |%3ĄX-]K$៪W22팬8oG>:΍[t*c–ր +[gNޢs(^,ɀ8J+wD=F0yKNDm 3_/`MtjC ],Tt~CReQW rI6:\m`EFH'Qɺ(Mԙ 4S9ՃE*Ȭ{8.}_ C>yV< ŌIoҊ9ݭvJ@lq D\PH`#@(N71;Bk.Gd0R#^3BQlū2 g`_Fg>7t/C ..!&%a4꫍Pg#"ǿ;^O?wK~gηٻBHr*Vh{8j H<1 3@e#f1fu,h|1EѴVyMm$ZQvULK\ͩpgkhD߳mC֛R*!ie4uWKtT].ּi4دmԼEɕ_ #P \Ƽމc5ʎ-% F ҶbwzHThM pae-i`Jiӣ,OSr@ у7wﱷuڞ\ZA!COeS~"QfC',%V ^͐T\*3*ǡETR#w3;U#ŝ(MЌOK=a"5 Zw>x"IlTVVǾ'Omsak?'C#7i8?g4$ϔ˔i`F'^t%p8i1a-/m23֕QivYfqYmòh5ak]h] a(yn=N }I:[W*ZܒNHS:,ďݠ;mgh.^q-`}5t-r'q[-e _X~p<}~)Pe71"_αRvfWIG9S]6vMĖT,rDxaIHfɱ]A{`vݕY;T7wdqD?}XY+rZM7j)ܲ55Ԅ`˽sSbQF; #YʨmD`M45Wh9z/v띚8E{CJKY/w !q$ k>q@f7Դd?s!Eh@3|]2ߎ[z"3 hvv͇{7ğc>M&BOۗ:c4@[(]b]]|BQժkL;Et,'yQi^6Qst /^~KBxJo7;h(O_ψSUە“D}@.UwcQ@027H. N4yx|#ɞ2慗ӈʲy62e`O8Fo\"J=\ \i A]|UV"~\W`6_Y{/V?ӷfi;\OoKS[!#v˅f$Mk8jfeQm7ܢ²?BeaKp#;'x?*g3ݺ2HuplHsYOW]IYwk>JQoT:A_yMC} 7?J{UԆ޵p kZ7oW~bw m;{9,D(wGJ|(sXʱ{d-~*M<8n*"!2R]i|)+ٹ+*A3*natwPR9X.eiɖ1YU̱p~egqVֱX\Tܘd |{2h3VYO$k5%2d5g>*EzXþZDٽ6>s$?c \'in9x`*.QK^L%k1GaW8QcM2tyX}O6O Lca\q^./Ԍ b3 }aT%6aDeIm]c.*BXnxK(UM)(FuKV>W>]RG*~L17%v3l֧(ׁ6Pys4qWzoD @,Mq! ;JBT qt-4 ݻVqb=i!afpQ9B.K6a5b0.ð//ݜ8K~T|˶dkPTDgij8[7G6aw[el)!wK:!Jk5""#afdֶ>;f0،Y*((X#$,"= Q1GYS_RxrJTO[f]w T-CbyÆڄ;LB$_a>{gWǑ{﫨 T8sޚe[̠opv$( $;IӶD:U.)\1Da|<nz@(Qf{>]ˮaeL l?@'ubU3֏ݨsv[ϥ jT-nsHSÞo_Pm6),8 ![$Z< ؜ɼD/(UO#ыCל@M/C{l%?S X"pA!` h$c*ya$V`@ :jR& & h 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & hN(D0,Bx&X%P$L$L$L$sz-d#H$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀ j륵Hgߔ|XYY`o2֞ėt{ǯu̩zN$AU?qE.3YF.57Kp" s@p*\ ge3 ~Mu{%0!AUszZػ4u@:NqwRG.L&;蟠Af[etztw4An>" 9V02Iy\F:7nl<'/:kQyE&s?uvv:1آUz~l_B;K8Ո4U@I ٟcAGJi0F5g9(%jw梥$3kJӺ\'w˙LH\h iL{&[FQ -Glf z sAG{G+>͹-Znj'?$_1j4jtIfn,{%Wp\a`@D k(**Wފ٘5$jPSa`bgaj-`XxTK$L 姫@PL󶤿; 'OfK`G+8z?Ii~YB(g|뷺/ wuMiVx^뚵ہt\6P59n:&CSm(= D{Y؏糐J:;fUpiQXOyaPЌZdgovw$g;J1P ?mȌPE 0TDž_:;ùD1RnUױ5Nlݟo83.nPxjZc{ P{[P+brǧZizv) RzWc~mj^:l=Tjc]ӧ:VB-oy]PJ*vغ oө+*sϿM:4# ;XYHHD<X~{mieI8wtYQُIAXp^\y,Dpgghіk}\2YqWRn. ab.AagWw27 eTO }u}nK b&ԑLwDTcljG]J b@|"H %9:FjL{RDH~"Ԁk $ @+(Bn~2YD,4&CP=̹G8p5-ꧬћv%EvQ{KE3 M@p, K$x˧_2bf-0D#u_[O& lN6mSR|ZcPr2R|a@nWdp'x۴#2(+yDV@e-J1r4:,,ꅩz,)ϷҁxFww9yW3tFw%= R0E2",b0mvZia%юzGU곦0K4 RJ#B,lJ *'WQH8%F7ASg>k[m|F77c~~Os" YBzAj'/<>Mw*|[}O+@# Ə%ވޠ ݷ@<ʴboJDy%,SFf8GouY(h3J?Np娼m(+1PӖD.HN:Ry_q)ՕKR$s1vԄr)C[FQ@\y2X^;Ij%`N +F4=7$c4VRD8dZC%`yZ!ùg_Д}+MzA]fgZHT%VllRb%}0}B Ƶd }%gs~}_i^=acfBMFoTn%,f/OO Vuo|٢ {?u8g[EJSͤB0Iz ѧVҡntcxklj>y$7{4^P= ݠ'(DFϒG^r9zQF|vcGRp&IQt]FXI{-?2/|  'xEG&x_ ܚw$џ݈aesI+d8m_HZ;KA۬mpBTS/LԽTf[M-.o~}u#)O LoX UԠa=u xhXR! 1{feb`}uB&n <<1czz!&|"K),:݌GU;Do=ƿϑyGw| 7+/8|TEg4݆k;VhԠ@2ԞP+ͭVy!LgKRҨFA)Ys֮jG8LX$ۋ C@dW}r gy‹@ђ]R/(bHnŋT~NHF9?/.yI/A16ToCAj!-lXf7CpI:>lbVeyE]cr&kP_ucl7;v0ZKs$$K%1(6[PlD{K`H$ @]H{nYgYO0͔OJ̬Wy81RJJ&LI"[= S}'h"o{TcW:Ld*"mT@&*znPZ ! n [RlSKeUNk~v4g0N^q'ev3̕gĉPX#SȚnnpCjֺL>kZ_oCY²آ4 "6p~fm{, tv5$GWn%9)|S<*U/${6^GxstHMHp9tH#(ѹD jX85U@X~խy`~!g #$ a*N!)u=Ίq|Ԯ !7;΄tNZr^(Jo2UI\@fs]Pzsy7w#%ٺĭ%' gو_YRjԺ8,ؓ&J;!ZUO>+Eci. 6e鼻U݅ bFeoMK(:ݲWQek"^MaKi/)(1Fz3b m2 )-?r傽{5"ϯD|PF!Sb3cgI\oDm N}̧G@ iGh>O nCgpFJRCW43(h Bpڗ˿S@lP}5~hO7ge&׊r;^iixUrgytf`5 +#s( V9A=J"ҏa c8Ώ犯)tӦÒSH:o (-+Z5,YZ/~12%\M,gDs*۳mJ twDClbacGz כɹl8Ot)(d88d!9,®uF6f6|}ǁ#E# 6bxbCPW N wGM 3%?@8~\0~%l5e~<2>w% W2(=Q4}pkTw t'aEnUȧVux>[KtJB^5#ۭmMYFWIaPt%EjRx((m( + F7/TGAGnf ~}0/9xݓ]B4-_SóbEsM'W)YCvrrxdWgsj'}l0Qo;@$=2!6oIc:z98 S^sk X ۸$ 1=sO:˿ -nH"@N}6gL)#" ӾRġұqpr THX%>5[:6Rk0B 9ρH1y%"R͊pR : Xh KKlFG,,{qvblFtrZ~UQPNf,Q[phl!.̗4\-|v dvag!yW)uuiCʡIumk{GCWvC \ 㲞Sԋ'cWϐ V +dK^t_G۴\iCwNɥbU]&e+I|!|֐[_.ymRI%wP2O}?q`vsųO4TY ju_'y 'V2|<==JM7#)ЊecۤRBٌz;#hytF?niq=&ʷ1 Hv6t Qx{~G&v?NB`ȀjnkG~v=u!^F^Cy6']~;ۀKHi녖{62+*$H~S32 0l1U>J!k1 x{",~.h ~@'lG{1kX?׵IaО8 ~|mQf=pF,]٦NSgud RړLhh ^t,> pBBQ,*(L7J'?ڮYA(f2b/4m" ;3\;z 2+aEV3)k̮6z04\ss9}(-w(HƑbs7T*)10hӷ؊g~1-%šޞm|`[4;'T]jq./oM~uG|fK+57m+] GKՀm lN=Se #A'כF*9KKעȧH_?ɼ(ӁB(+͆GN}.FR_u=#1ND.CR%y H7G\ _c-1.k̙drj~lPé{Hո%I(~a|Pߎ54xzVʅ ,O8r1Iߞ8pxrzj]1H# u7? EyңL-kvŏ٬ 4b F~]Dj}!W]+5ڍH/">" OՉ QMWKk .)XVO M?o/OLrYb#LcL?:>|z/y_~3O#XmK|' ׿g'?Jf_qBev9+x\gv77;gR>V\R"u?d>ʂ>E;&BqZ0S=%*Fg?濼j:.%#^88'I>OƳd֦ 쇭قZz 0W ,}`6  +A_R`׳|^8b6?j^ea]MzৱBf緯;U-*hOuw Ѩ\[.< ՅN*V12ǀJkL-\$cs6$BO,NS" `C0񒕨_*>[RˎՌG>\-a.ߎ>ҟ,;X}^IJK.D~W?ɼfaRE=*hihu5s/䳕|Ctm`P#RexdW@|9$ExAϓ#EOR‚KYQgcD81Ɣ>cږ[o~a '1+.NqѸ]n-f'~gIZ% 0P ,[' Z%g  f3Qlo߷G@OY|`jxFb}ȽELm=)ٕqQnJ:b@?yљP_Y@0W蜙جdsCW[p7ZQkHy~ѡ]{NR l 5[I(weD!(®(wzŦdw~k' /n&q\qƷ!+,gjBr>_ځFlиF%QϤ%Q~J}>-KTvۈPP US`2Dq.# /@|h}VHg_>?@nJRϳsEQ5ÂodECGu~6]ٝ~P#S2 ޳W`&G>ӏÇ=G?%L筵xn iQ&6z{Nww!fU=F ?/%q7h9!sL)kMc5xCϻDFZgo,P[QcҖ5q5bnDڌiUQϴMkd!Xİ .F\+T46ȿ7*x\Q) ?FrVפ umh=(xW!JQ ty@„ޫd:Ko͔/W%krQRYI]`lþ$4~wtG=;>F)Q[1` >f)evn%e5Aވ#+BwŊ,wkκTnKCSbv[c*4 EѠ%!+O"5R΃oH O{Bwy6vZXX`'ۡ|'C3ޙW(B?VD5Ljp7#OCQU]lD_Xs ǻz/pG1(a]0%D#rH$Vk{n7 W _#Jr)`n3}@GDYA2=ԯr _#b!kl{k4sX~n@ kJpF ~^xL`<Ŗ){}M6ݬIdԻ[Rz9G.a1i/֨6L6oz0y'K,T"0Zv\7` Cir/ף$ $XӠBN[.plvЯѶ6Ӿ O#][ى֣ͨ5`9O9_<z3t=pz;?Wf &ڔY*SCx"(ߔAV$]*-b-EBVO$p}bt(FrEXiUX]zXÕ#iJ,hGAY .Kk*̓p{lMѫ=]okgl,^* Pw 9(QOA*OUX(_ܐ >ER! נN/07k"%T Η\*#)$A+6I14dB&''"HVĜ!|Q'ͣz];KNs̾C$[o%NGa$"p_wׯ( i$e. nx7'jVquí1A/ 2LTSJO0.ܒkkTG}#=H+FC`ܐ2e| g˅D G¨ݜDH^q#īiK?ƛ7vK 3{qLU*|r*<84ʃz wPd!ɳ}_܋͎FgD'A\["&_/h[o n{g[&%ipٟ 0w?@L^zG;Z簥;ю #&SŞJӜq` 9i?3`.Q3'Ct>_ fk mZ8?1? 5]  &~O\5KIH'MUʼyhJҟ7Cft)h8yQ p-F5u oZX~ϻ$"~$A@jBD=o6xE#X+jz_h93i,/] 4: C<cwtt*@%Qk(5`KX<e-#~.N)3x~t (8y1[kyF #cԣd`gAMz( --U_mQJRl>Ty(|8W='&TN\fiпߖl^Dy 7t+/:Jgs|Rzg'Lz_iIF'D-8%nc0'=b܀`:M]76ܟ:?YDHL&ơ*! 4[X@6r%>-(/ jtMoJrŭeMlH:D%;еyci j>x Z'\I6w*I "^k>QS |@|qRsj?5 (g'l}N@ۋ(=paN* >mY})#s+.Mʜ-@d%*G\b 缎-73@06:$o#~By;￉0|'ZڒU$oU8Lv9u} gȢZwxκɪz/80;R1\c'Sd"hEK| )F`S7@cQ%5''\0ռF"cU~OqGYϏWpgmjO4 [cp҄ 6.漲Lïl$h5h>QM6vr0 [env6JmT7_yr6Mr>NV=y"7%spkb!XM>901w#V8b*,v|o=/؋}4^θLZtƌ\O"`#2i r~MDŽ|(MpExk+uHmw3=6b0DjSP󖄢Er!ӵPnG6P=m8hx:i8_"oј:En?X>D\D 6s<Ov/X\2f(Cn&V*g2 ց{^y7zƽEf.AtgI ;ل`E2r<6 mЃ\`*Ԥ[e |ҥR8x) j_e QڗpKr?Jwk3ev>?Uvg'InU5TLVӇq/H㕳W**<>|/VGnA*w fX,s,59$U<*2w 4Nɣk(ƻ~N6?F =!f4A6 gz6V#P0j>PzRШF271=l5FVoLJ^k7g;%H0cF܄=3/ ȓ>nmPćmH}E>ǟM>ٙwt.{˝ʽXP{#SkE pr֜ٚ#O?L|f]o u۝$iSk~pWFo Z9]DvڙnwǴ2 ÉġZc.#/Οz "љb4.Shx̌g~Y\2s蒤IUpl/%֋uKs: {Z4Qg0ё^: O8}enU3nm0]@[)3'&w}@x ?}LigJ/yq6 Z#ݾ;kV,UԨ~AՓ# z7^U @^fwZiD| XoT+tiro]#[ tPմA k fKײ1ț{-(Sެfh&A)}rW kX .&m]жp^ά3ZC\gh.^q-`}5t-r'q[-e _X~p<}~)Pe71"_αRvfWIG9S]6vMĖT,rDxaIHfɱ]A{`vݕY;T7wdqD?}XY+rZM7j)ܲ55Ԅ`˽sSbQF; #YʨmD`M45Wh9z/v띚8E{CJKY/w !q$ k>q@f7Դd?s!Eh@3|]2ߎ[z"3 hvv͇{7ğc>M&BOۗ:c4@[(]b]]|BQժkL;Et,'yQi^6Qst /^~KBxJo7;h(O_ψSUە“D}@.UwcQ@027H. N4yx|#ɞ2慗ӈʲy62e`O8Fo\"J=\ \i A]|UV"~\W`6_Y{/V?ӷfi;\OoKS[!#v˅f$Mk8jfeQm7ܢ²?BeaKp#;'x?*g3ݺ2HuplHsYOW]IYwk>JQoT:A_yMC} 7?J{UԆ޵p kZ7oW~bw m;{9,D(wGJ|(sXʱ{d-~*M<8n*"!2R]i|)+ٹ+*A3*natwPR9X.eiɖ1YU̱p~egqVֱX\Tܘd |{2h3VYO$k5%244(ϑK(8;.xȮx1IuؤWuױ h$c*ya$Et :jR& & h 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & ha(D4 %( @IL`$&FQUkށ IH@$$ IHPcK:1o(&l |,Y _j +jBnɀ j륵Hgߔ|XYY`o2֞ėt{ǯu̩zN$AU?qE.3YF.57Kp" s@p*\ ge3 ~Mu{%0!AUszZػ4u@:NqwRG.L&;蟠Af[etztw4An>" 9V02Iy\F:7nl<'/:kQyE&s?uvv:1آUz~l_B;K8Ո4U@I ٟcAGJi0F5g9(%jw梥$3kJӺ\'w˙LH\h iL{&[FQ -Glf z sAG{G+>͹-Znj'?$_1j4jtIfn,{%Wp\a`-`Jl"]kh"67$iDQXG\ Pb8"*E"qG (jH4.|3Y-3&[œu4eyw;gäA0|F"^:wIG1$X,svKMV=-ߥ^{J3wn1je@V}B fz.m{N(h59)M=Bvd p& 7ڪD!-ү9cRD@rR|A~~iT.ogV@o/\X3?`z_fx# էdsg Z3gTK$L 姫@PL󶤿; 'OfK`G+8z?Ii~YB(g|뷺/ wuMiVx^뚵ہt\6P59n:&CSm(= D{Y؏糐J:;fUpiQXOyaPЌZdgovw$g;J1P ?mȌPE 0TDž_:;ùD1RnUױ5Nlݟo83.nPxjZc{ P{[P+brǧZizv) RzWc~mj^:l=Tjc]ӧ:VB-oy]PJ*vغ oө+*sϿM:4# ;XYHHD<X~{mieI8wtYQُIAXp^\y,Dpgghіk}\2YqWRn. ab.AagWw27 eTO }u}nK b&ԑLwDTcljG]J b@|"H %9:FjL{RDH~"Ԁk $ @+(Bn~2YD,4&CP=̹G8p5-ꧬћv%EvQ{KE3 M@p, K$x˧_2bf-0D#u_[O& lN6mSR|ZcPr2R|a@nWdp'x۴#2(+yDV@e-J1r4:,,ꅩz,)ϷҁxFww9yW3tFw%= R0E2",b0mvZia%юzGU곦0K4 RJ#B,lJ *'WQH8%F7ASg>k[m|F77c~~Os" YBzAj'/<>Mw*|[}O+@# Ə%ވޠ ݷ@<ʴboJDy%,SFf8GouY(h3J?Np娼m(+1PӖD.HN:Ry_q)ՕKR$s1vԄr)C[FQ@\y2X^;Ij%`N +F4=7$c4VRD8dZC%`yZ!ùg_Д}+MzA]fgZHT%VllRb%}0}B Ƶd }%gs~}_i^=acfBMFoTn%,f/OO Vuo|٢ {?u8g[EJSͤB0Iz ѧVҡntcxklj>y$7{4^P= ݠ'(DFϒG^r9zQF|vcGRp&IQt]FXI{-?2/|  'xEG&x_ ܚw$џ݈ ݹ*Q$v!>U_}|c%Hp FIK3.FP,Ƿa%ռΣV(6gWKam-tk}* âC@2B^UllhJŲ/ć3 T_;0ؗ_Q+emRwGZ(/7QKd{T[b۽,XW[-j t$h %R*~_=v;i_8p]+vh1UccS]#`-Z 1e+tyz1]N#:,4bX$JLc%k!(ME!`ם6UKa2ea 6 Ϫ6^WeJ4eH]ifOIJ(k0pcIV`Wӝ|A :H=o`Z e2x:,B)A_oy¿ڨa\iAH¾?ݤ::YN04HT)dJgv2FG[<ۨifn:͚2@S!PI%I3;p`&U } ȯhG >HeČ Oc5w${W{44'j3s Q5g OiDM=42p݅o~)GlAwk?Xvi|)iA4;62^b<z/Lz+5{f e Il&$J H5H r-ֺy?zf3^Owwq U*cD,{Oj Z㷯^k ;-/3LXJ[;/r~B0ڦߏiY_._~*KʁNݝ6uS_qdb@BW?#͒лh'MUñ]Ȝ7jkz/G_sm0u,KT2cٳ ;b+QJDZ1_B%Y1gb=KIJYmٞATN ~Kz 1끬 I6e*w-{mż`H~Q/kEI< C69 9*IHsJ Ot-TklQ}7eIb|En`:yiD-?O%8wAwٻTb$m_=:yt]#яӱy_Qj6u+֫4`@V3XħxixmZ#,#=#mil_ZP~{"_< Mx CByZ17ܮٽ$Cw@wٳ֠T@u4g/Lo"^޴)?J1(.4_`l|`:'@$W6kǭpqTw(O^ޠ !̰-?R/5i$AlH4#Zﴥ7qQ;QӍ(.E>TƂ-vN=vn}(tL.uu r[7W nz0L{Η JXd/>c; 0PbO+(<ᡦ#?$[vdd u_bQHZ}..䂮#53E#hv߷le$`oJÿ#RL9/ʾ" is*z2x,8rU;MZfW?)$"OJdISѪȢCjֺL>kZ_oCY²آ4 "6p~fm{, tv5$GWn%9)|S<*U/${6^GxstHMHp9tH#(ѹD jX85U@X~խy`~!g #$ a*N!)u=Ίq|Ԯ !7;΄tNZr^(Jo2UI\@fs]Pzsy7w#%ٺĭ%' gو_YRjԺ8,ؓ&J;!ZUO>+Eci. 6e鼻U݅ bFeoMK(:ݲWQek"^MaKi/)(1Fz3b m2 )-?r傽{5"ϯD|PF!Sb3cgI\oDm N}̧G@ iGh>O nCgpFJRCW43(h Bpڗ˿S@lP}5~hO7ge&׊r;^iixUrgytf`5 +#s( V9A=J"ҏa c8Ώ犯)tӦÒSH:o (-+Z5,YZ/~12%\M,gDs*۳mJ twDClbacGz כɹl8Ot)(d88d!9,®uF6f6|}ǁ#E# 6bxbCPW N wGM 3%?@8~\0~%l5e~<2>w% W2(=Q4}pkTw t'aEnUȧVux>[KtJB^5#ۭmMYFWIaPt%EjRx((m( + F7/TGAGnf ~}0/9xݓ]B4-_SóbEsM'W)YCvrrxdWgsj'}l0Qo;@$=2!6oIc:z98 S^sk X ۸$ 1=sO:˿ -nH"@N}6gL)#" ӾRġұqpr THX%>5[:6Rk0B 9ρH1y%"R͊pR : Xh KKlFG,,{qvblFtrZ~UQPNf,Q[phl!.̗4\-|v dvag!yIr.vm(Jfbrp{lZtu*lQa5LYtni]QX 8bE#? C^.-N둽x Pp2Dz;nP̨<@% :'L&eWm%kj柳ľR!0"H @ 4a9psH>އs1w?T+eAKVZ"L Kf~xS\,ޝp@r8`#W2.DP'AݽnƄEZυ D`NJ? h\ I`[-7A-| '@i~ ZZ z։{A A=K-u N*O!%܄;$*v9L#o ǒva<{_7%׶@O"?oG(wFxYϦ/E1Hjf[?Ryo=mQe,YjD$ ioෞLo'҄RV(]~9 C!\ ufmHV3jut{.Ozx2N㙬Mfj3[Z;_T;kN*3]0!>?B@usWIll@I݊2s2bWαs ӌC6EQjgA*ZqXE`s 3/zJ)UE7*%byO90#6g}e=3yܲs7#WHM:Hg >HL$2 _kɰ;[ˋ$SZʤ%}=k)JK]X~&],ʗ!COYc7Rwm_~ ģkoѪ'"j^>E >%F9N -Ad;onxEU7>0񐸃EX;^0Ԯ"WB(~YI?ohũďdMC ?5.'~ l90͉nO:nUf'nܒsRm~%~Kt^Jk&wXZN*iEM=G#bIڦ^肊1zP*'VU-LGsSu{%N좒AjRo+3mw+5]&hT GxܒmizrOm;. d |:dR~EoWrTnn) 4_0)|҃4W@mS)2$孞 [}Kˆ$ @s9@ S[tD0AV53?aZяoUd,N 9Hj͂Wϑ'aˡsŭ+Mw 幅6ɰku,  +A_R`׳|^8b6?j^ea]MzৱBf緯;U-*hOuw Ѩ\[.< ՅN*V12ǀJkL-\$cs6$BO,NS" `C0񒕨_*>[RˎՌG>\-a.ߎ>ҟ,;X}^IJK.D~W?ɼfaRE=*hihu5s/䳕|Ctm`P#RexdW@|9$ExAϓ#EOR‚KYQgcD81Ɣ>cږ[o~a '1+.NqѸ]n-f'~gIZ% 0P ,[' Z%g  f3Qlo߷G@OY|`jxFb}ȽELm=)ٕqQnJ:b@?yљP_Y@0W蜙جdsCW[p7ZQkHy~ѡ]{NR l 5[I(weD!(®(wzŦdw~k' /n&q\qƷ!+,gjBr>_ځFlиF%QϤ%Q~J}>-KTvۈPP US`xkHs{wE -e+~WCG*mY"rdj| ^};jO(Ï"ͫ hE.aJt9ߡtE\pt;h /ߕ2 "gJOf7G9kaTzyn~li~_FM]O_9|6|% D5ON^`k)!ݯAFV; :P.am)|valTNjke Q(ߐu9?)YэUϑ2m8>~MVDdz4n*茺0"H6+&XΉC)x)Lk6l0UTв G:M!v%N:H )0(Xx;9$HR5+:~z[eg5P32AX0A[z%Tet~?i3pbc{[$:Ձ{~b-zF6 s][ÑNhM6%mS6X"!sj* "[_3_:xBP$bnqHϝ"}!fO~hЃhu/ޜaݥ 0HZk{AEj> ~)_9F=ֹ3[k.< Lؿ$S$seٓ /F)Q[1` >f)evn%e5Aވ#+BwŊ,wkκTnKCSbv[c*4 EѠ%!+O"5R΃oH O{Bwy6vZXX`'ۡ|'C3ޙW(B?VD5Ljp7#OCQU]lD_Xs ǻz/pG1(a]0%D#rH$Vk{n7 W _#Jr)`n3}@GDYA2=ԯr _#b!kl{k4sX~n@ kJpF ~^xL`<Ŗ){}M6ݬIdԻ[Rz9G.a1i/֨6L6oz0y'K,T"0Zv\7` Cir/ף$ $XӠBN[.plvЯѶ6Ӿ O#][ى֣ͨ5`9O9_<z3t=pz;?Wf &ڔY*SCx"(ߔAV$]*-b-EBVO$p}bt(FrEXiUX]zXÕ#iJ,hGAY .Kk*̓p{lMѫ=]okgl,^* Pw 9(QOA*OUX(_ܐ >ER! נN/07k"%T Η\*#)$A+6I14dB&''"HVĜ!|Q'ͣz];KNs̾C$[o%NGa$"p_wׯ( i$e. nx7'jVquí1A/ 2LTSJO0.ܒkkTG}#=H+FC`ܐ2e| g˅D G¨ݜDH^q#īiK?ƛ7vK 3{qLU*|r*<84ʃz wPd!ɳ}_܋͎FgD'A\["&_/h[o n{g[&%ipٟ 0w?@L^zG;Z簥;ю #&SŞJӜq` 9i?3`.Q3'Ct>_ fk mZ8?1? 5]  &1m7=:FBCWPysz|q3Lɩ}s\`% gX@Qq>,")E26G+.ebUa!` Ię"@'~cn 9h/+q(zOh2pDrtctx7 hyPa>fk퐧n ERښaiR94W_F.O04 ZL#޵:['O9wb:]>L{$H}g*9&wǣ҈"o._Vy?.gP6coC>< S.մ4_&RdMkʣ&~prTٻ}?7TUa7#No QwH=ىiR/% ,3!z}T2{I$,`` ogQ6$/}o@D g|q~ KNXL19$=Lj?:j,ĔD3{)i &\кҊ\EV~{G#G) TkU%%us]g5EUSPhuMM[ r6ӑ2'Zaqk]{DA˙MWi Nù? -qk2Fqzj#Ee  s#g?z5 Pp?ŠbĦ4`N[DoLYN.r9hݫc{BYG2jE?È=cjYȐ2d>75Ocxr ŃSֲ B6U:Ľ8!ֲq8i_`Y&ߞ:?WGLޒ3y qZ~h>pvsƗ?fV23`$+c5N6:X$)O5ل Zde*8tc9w}!G-2MqeFZ@E(RdѕS8x@,Q6oҼvݍ>-er+7 u^1J8NɖZќעgj(p ua+uP4<e-#~.N)3x~t (8y1[kyF #cԣd`gAMz( --U_mQJRl>Ty(|8W='&TN\fiпߖl^Dy 7t+/:Jgs|Rzg'Lz_iIF'D-8%nc0'=b܀`:M]76ܟ:?YDHL&ơ*! 4[X@6r%>-(/ jtMoJrŭeMlH:D%;еyci j>x Z'\I6w*I "^k>QS |@|qRsj?5 (g'l}N@ۋ(=paN* >mY})#s+.Mʜ-@d%*G\b 缎-73@06:$o#~By;￉0|'ZڒU$oU8Lv9u} gȢZwxκɪz/80;R1\c'Sd"hEK| )F`S7@cQ%5''\0ռF"cU~OqGYϏWpgmjO4 [cp҄ 6.漲Lïl$h5h>QM6vr0 [env6JmT7_yr6Mr>NV=y"7%spkb!XM>901w#V8b*,v|o=/؋}4^θLZtƌ\O"`#2i r~MDŽ|(MpExk+uHmw3=6b0DjSP󖄢Er!ӵPnG6P=m8hx:i8_"oј:En?X>D\D 6s<Ov/X\2f(Cn&V*g2 ց{^y7zƽEf.AtgI ;ل`E2r<6 mЃ\`*Ԥ[e |ҥR8x) j_e QڗpKr?Jwk3ev>?Uvg'InU5TLVӇq/H㕳W**<>|/VGnA*w fX,s,59$U<*2w 4Nɣk(ƻ~N6?F =!f4A6 gz6V#P0j>PzRШF271=l5FVoLJ^k7g;%H0cF܄=3/ ȓ>nmPćmH}E>ǟM>ٙwt.{˝ʽXP{#SkE pr֜ٚ#O?L|f]o u۝$iSk~pWFo Z9]DvڙnwǴ2 ÉġZc.#/Οz "љb4.Shx̌g~Y\2s蒤IUpl/%֋uKs: {Z4Qg0ё^: O8}enU3nm0]@ LgSd x,mTx6aI[)F9u 73b&o7-wyg{%I=X:Y:S~YTK=L} 2}TQ & jhH"v.$GZ]1 ޥ px+;3Jq>}<˴8@h@KKmtQ0r=gUob_ܨRj})(shufKEx5DMKR8GE'~ӼW`nqVD%[WHbhc259P3OpcԢ/=s6Jq22lܒ( cE5so=aU5*.+ +%- \#F/Mbr@wnP}jJ&Ƙytd%3n;0kݦ?ӃC6-)0 1˱li-E 3Pv>Ws ^lq)O7wrۓ;>pc.)s-{Yye- eB_CrhZv\U0:`__M4WRj 6;mO.,C( ]v0^H+whttC%ېpΞ%G'6q@f7Դd?s!Eh@3|]2ߎ[z"3 hvv͇{7ğc>M&BOۗ:c4@[(]b]]|BQժkL;Et,'yQi^6Qst /^~KBxJo7;h(O_ψSUە“D}@.UwcQ@027H. N4yx|#ɞ2慗ӈʲy62e`O8Fo\"J=\ \i A]|UV"~\W`6_Y{/V?ӷfi;\OoKS[!#v˅f$Mk8jfeQm7ܢ²?BeaKp#;'x?*g3ݺ2HuplHsYOW]IYwk>JQoT:A_yMC} 7?J{UԆ޵p kZ7oW~bw m;{9,D(wGJ|(sXʱ{d-~*M<8n*"!2R]i|)+ٹ+*A3*natwPR9X.eiɖ1YU̱p~egqVֱX\Tܘd |{2h3VYO$k5%2 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & hk:(D4 %( @IL`$&C)uꇃ IH@$$ IHPcK:1o(&l |,Y _j +jBnɀ j륵Hgߔ|XYY`o2֞ėt{ǯu̩zN$AU?qE.3YF.57Kp" s@p*\ ge3 ~Mu{%0!AUszZػ4u@:NqwRG.L&;蟠Af[etztw4An>" 9V02Iy\F:7nl<'/:kQyE&s?uvv:1آUz~l_B;K8Ո4U@I ٟcAGJi0F5g9(%jw梥$3kJӺ\'w˙LH\h iL{&[FQ -Glf z sAG{G+>͹-Znj'?$_1j4jtIfn,{%Wp\a`-`#ILMy|udg~[V2X~'a M8FF3l0 K.}G$yFmy|>dߛOPyxCz_#yFUR WHR[)L49sWa"6?<ƞ/ZWyMrT:=.ҠELFp/\|s C KWHtpd X0nM|oE^:w*=L4 oI;|ABkyAĥL.$TbC {f=,e:voˉ0L lCT?ky}QRK2'Aoc;>V%TK$L 姫@PL󶤿; 'OfK`G+8z?Ii~YB(g|뷺/ wuMiVx^뚵ہt\6P59n:&CSm(= D{Y؏糐J:;fUpiQXOyaPЌZdgovw$g;J1P ?mȌPE 0TDž_:;ùD1RnUױ5Nlݟo83.nPxjZc{ P{[P+brǧZizv) RzWc~mj^:l=Tjc]ӧ:VB-oy]PJ*vغ oө+*sϿM:4# ;XYHHD<X~{mieI8wtYQُIAXp^\y,Dpgghіk}\2YqWRn. ab.AagWw27 eTO }u}nK b&ԑLwDTcljG]J b@|"H %9:FjL{RDH~"Ԁk $ @+(Bn~2YD,4&CP=̹G8p5-ꧬћv%EvQ{KE3 M@p, K$x˧_2bf-0D#u_[O& lN6mSR|ZcPr2R|a@nWdp'x۴#2(+yDV@e-J1r4:,,ꅩz,)ϷҁxFww9yW3tFw%= R0E2",b0mvZia%юzGU곦0K4 RJ#B,lJ *'WQH8%F7ASg>k[m|F77c~~Os" YBzAj'/<>Mw*|[}O+@# Ə%ވޠ ݷ@<ʴboJDy%,SFf8GouY(h3J?Np娼m(+1PӖD.HN:Ry_q)ՕKR$s1vԄr)C[FQ@\y2X^;Ij%`N +F4=7$c4VRD8dZC%`yZ!ùg_Д}+MzA]fgZHT%VllRb%}0}B Ƶd }%gs~}_i^=acfBMFoTn%,f/OO Vuo|٢ {?u8g[EJSͤB0Iz ѧVҡntcxklj>y$7{4^P= ݠ'(DFϒG^r9zQF|vcGRp&IQt]FXI{-?2/|  'xEG&x_ ܚw$џ݈sͳA}^Xc:K(w^Y3rwkOD}ez "6v?&Wpy2&`o서8@r4"2ś[ȫ|Qfnvy@kx PkU-ŒMaɁT%3^X_0b\ҍnܟH=>@1),nAzlBNnX1A.l?x!;!bVƒ4)&Mn( >ݭ\̂67N7sK&sP"*0NIJtˊ2*$UR]}@q}CRaorɤwC&Mj-;pP"$q{}R$ o&%d9UH p}Iqi&9v?e9[=Rł;Hiy*dOwD tT`6rocQ3@h"1Cr5/4D A"ЏY%!%_䋦gMG'BZt5k%6+sbLs}I8)ȾЧ`R pk%jRMMMZkm1<-ԇbdOM ^=rr\i%u~Є_E=u,O0tDwnc+ W'lZPj'.dNSlMcǶ_b%(Ylh{Y/O\G}i'|xghTeN_Dܶ_'efB򕔵)|4 ]ҚHk˵>"ww2y~WUy5J~\a Rdu]PcQ1R)ln)s6Egw =/[9-Vs3ymqgQJ>)BL,T ۹aR$m19lCvZnaW&㕞Wj-́sϗe0ʘºUP`/,8#nw;Ӷ|P"2Y4}P4~*}/, JQ  ltGlA G)^zG-7_3= )^# AC:`d"M{;_ ^o1žUdc?bcsQST/8+W\;Y0 v8vSZƧkm.`jb/?#ug6ZYpp]?v?A+FE1p6J㦉1CjֺL>kZ_oCY²آ4 "6p~fm{, tv5$GWn%9)|S<*U/${6^GxstHMHp9tH#(ѹD jX85U@X~խy`~!g #$ a*N!)u=Ίq|Ԯ !7;΄tNZr^(Jo2UI\@fs]Pzsy7w#%ٺĭ%' gو_YRjԺ8,ؓ&J;!ZUO>+Eci. 6e鼻U݅ bFeoMK(:ݲWQek"^MaKi/)(1Fz3b m2 )-?r傽{5"ϯD|PF!Sb3cgI\oDm N}̧G@ iGh>O nCgpFJRCW43(h Bpڗ˿S@lP}5~hO7ge&׊r;^iixUrgytf`5 +#s( V9A=J"ҏa c8Ώ犯)tӦÒSH:o (-+Z5,YZ/~12%\M,gDs*۳mJ twDClbacGz כɹl8Ot)(d88d!9,®uF6f6|}ǁ#E# 6bxbCPW N wGM 3%?@8~\0~%l5e~<2>w% W2(=Q4}pkTw t'aEnUȧVux>[KtJB^5#ۭmMYFWIaPt%EjRx((m( + F7/TGAGnf ~}0/9xݓ]B4-_SóbEsM'W)YCvrrxdWgsj'}l0Qo;@$=2!6oIc:z98 S^sk X ۸$ 1=sO:˿ -nH"@N}6gL)#" ӾRġұqpr THX%>5[:6Rk0B 9ρH1y%"R͊pR : Xh KKlFG,,{qvblFtrZ~UQPNf,Q[phl!.̗4\-|v dvag!y7-ǿSƍH&dVW #GVGͣ&8%DB"{7pMqơj/è$P//c+Иبl0"[ȾVP V[vʮDwBduUVƇg~%ŃĄu%e6s#~?ɫ0=>7|{5V"s 3Kur4!]Uf($=a+_9V\IHMw!M7$8G`~h= U;3EE*bީ@]CkdOa"JH3d{(R =RC؈9t40="ևKlJ!=6iVgGɦtǭ a0u]gs\Z>[κfɵU@)Iebkdē`'/aÛ?j 679Wzj654ӬoʜUw7+ :svuJi?";4.')솵3+ۃ0;[실t\o C"1p6P(9ƭ6NaLCf̫Frv5 %Z14/q.9.k#p T,~֕_;(-dY 9%N9UX 1èඒV9KugY3" Te(e6& dC?9w^񺡹cIL^Dr&MLⲎ 7`)_wC(&OXRK@ `,DE2ET˘3wF\xN)jz>ئ0'z[eIOm 7kp5RBͥnryWȨP-:7ŘPKPA@VE9pe|`?uWޢub {拐o=@?K?tVKn1y){uiaf2d]K4X&u`LX[RˎՌG>\-a.ߎ>ҟ,;X}^IJK.D~W?ɼfaRE=*hihu5s/䳕|Ctm`P#RexdW@|9$ExAϓ#EOR‚KYQgcD81Ɣ>cږ[o~a '1+.NqѸ]n-f'~gIZ% 0P ,[' Z%g  f3Qlo߷G@OY|`jxFb}ȽELm=)ٕqQnJ:b@?yљP_Y@0W蜙جdsCW[p7ZQkHy~ѡ]{NR l 5[I(weD!(®(wzŦdw~k' /n&q\qƷ!+,gjBr>_ځFlиF%QϤ%Q~J}>-KTvۈPP US`P69@`ZJb}5Q/U\yHoًGi()p+)k<1ҟt?+P2u%_ሴlS)P7gsN/9"s `Nol?o1auЦq:q9u[e#a ?K0!!G(iP]lg :ky.u喘K Pf?\dya')ܶ8; WpZ]'yn@Qw[㏨Ȅb5 JG Rfci<3>3&)!3 uRh-E!ͭ8T3wllmW R/4QR!.8b0[;B$P6buvc(ĭK[p ~ۏvPڋC]>lߔɓr~qDGL/~|X`Y/ʅW[+j:]O74[)?-5ZGh2 jCt` \X *OoMmk$80ӑVswBX5QܦN2Ӯ3NAǺ.+UpUaPZIPp'#q>.~VξA0笪3o.(`bqCPGo .cĦwbIeluYVCpB 2?>Kxy)Xz=Ih._MP /;=B's` m#-yfd 1E`4evpena(P<5c']Y1S v}vZH֊3=BBIi>޼e ch{wVf)q܎_)"YiNM7forD= G79ԃf}CTA㙤s4s>`PWVb7ScnW(]۱=9#dװ|KX{tVN˄IE[1Mt̚OR5_NPϑsk@UmOCjuYGa4Tƚ^&ڃˉW_!ZTD q2*_32NHjV|oSV]!I&W6ʨ^DJ0(i0:X0be`Hpi ? Tty)Tހ"3֪d 6? .φM}- _^ާd@VQ U!40F z i!-i%Kכ&K_5 x~fǿ}`<ws64 ^Ǧ-h|ⱁr8W8v$zŗJ&Uhr7l<8nF!-4~wtG=;>F)Q[1` >f)evn%e5Aވ#+BwŊ,wkκTnKCSbv[c*4 EѠ%!+O"5R΃oH O{Bwy6vZXX`'ۡ|'C3ޙW(B?VD5Ljp7#OCQU]lD_Xs ǻz/pG1(a]0%D#rH$Vk{n7 W _#Jr)`n3}@GDYA2=ԯr _#b!kl{k4sX~n@ kJpF ~^xL`<Ŗ){}M6ݬIdԻ[Rz9G.a1i/֨6L6oz0y'K,T"0Zv\7` Cir/ף$ $XӠBN[.plvЯѶ6Ӿ O#][ى֣ͨ5`9O9_<z3t=pz;?Wf &ڔY*SCx"(ߔAV$]*-b-EBVO$p}bt(FrEXiUX]zXÕ#iJ,hGAY .Kk*̓p{lMѫ=]okgl,^* Pw 9(QOA*OUX(_ܐ >ER! נN/07k"%T Η\*#)$A+6I14dB&''"HVĜ!|Q'ͣz];KNs̾C$[o%NGa$"p_wׯ( i$e. nx7'jVquí1A/ 2LTSJO0.ܒkkTG}#=H+FC`ܐ2e| g˅D G¨ݜDH^q#īiK?ƛ7vK 3{qLU*|r*<84ʃz wPd!ɳ}_܋͎FgD'A\["&_/h[o n{g[&%ipٟ 0w?@L^zG;Z簥;ю #&SŞJӜq` 9i?3`.Q3'Ct>_ fk mZ8?1? 5]  &ݨz+|m d1~QZ(~M+NAPmsT6wT߁ػ&![]ca -Yߧ2='ul ч%IDx>t}ӱ^ _w*1x*ae|[y-娯 &hjS+ i#4||(]n翦n"q.6sժ֝xb6kX&N74*DΖdw̏I?LF4˲5 ,:=y;*xNr>6ZlHõ"|>oO-q>W +0]npy[{ܼ/7f*o~8v1[l2CqGt1-EQ|]Hoj1!/^SU;Asƶs]m<e-#~.N)3x~t (8y1[kyF #cԣd`gAMz( --U_mQJRl>Ty(|8W='&TN\fiпߖl^Dy 7t+/:Jgs|Rzg'Lz_iIF'D-8%nc0'=b܀`:M]76ܟ:?YDHL&ơ*! 4[X@6r%>-(/ jtMoJrŭeMlH:D%;еyci j>x Z'\I6w*I "^k>QS |@|qRsj?5 (g'l}N@ۋ(=paN* >mY})#s+.Mʜ-@d%*G\b 缎-73@06:$o#~By;￉0|'ZڒU$oU8Lv9u} gȢZwxκɪz/80;R1\c'Sd"hEK| )F`S7@cQ%5''\0ռF"cU~OqGYϏWpgmjO4 [cp҄ 6.漲Lïl$h5h>QM6vr0 [env6JmT7_yr6Mr>NV=y"7%spkb!XM>901w#V8b*,v|o=/؋}4^θLZtƌ\O"`#2i r~MDŽ|(MpExk+uHmw3=6b0DjSP󖄢Er!ӵPnG6P=m8hx:i8_"oј:En?X>D\D 6s<Ov/X\2f(Cn&V*g2 ց{^y7zƽEf.AtgI ;ل`E2r<6 mЃ\`*Ԥ[e |ҥR8x) j_e QڗpKr?Jwk3ev>?Uvg'InU5TLVӇq/H㕳W**<>|/VGnA*w fX,s,59$U<*2w 4Nɣk(ƻ~N6?F =!f4A6 gz6V#P0j>PzRШF271=l5FVoLJ^k7g;%H0cF܄=3/ ȓ>nmPćmH}E>ǟM>ٙwt.{˝ʽXP{#SkE pr֜ٚ#O?L|f]o u۝$iSk~pWFo Z9]DvڙnwǴ2 ÉġZc.#/Οz "љb4.Shx̌g~Y\2s蒤IUpl/%֋uKs: {Z4Qg0ё^: O8}enU3nm0]@Wa$x[sn:Pifl9xS4iBCC{sfC_ җՔXUqeNJ^mJ£۲ڠp5)W\o_,h;Yp]I1^!f Uv|ѳ, ,H2Ph;|FΨix[ 5 ( LC!rԱ@*gGE-AZO5)E\ C?mY6*⻚vM${{fo6?_dm_|M2Ayoh57bQ1+oYz82tCBSRjCNy n0I>v4L<)jQ=Srbfß4x V]R݋'d1hie -#1!Ob0(_i7G/o gMZNfr䝈L%Zi+HDN[JF-Oec\B+M:T$BB(U2a^Hzܜ-f1HbޏHՊ+Z$xm+tqߢ}Cʸ)|{\?D%ƏGp D;up^b8onĊiC!IEfH 1U \iH|G4E*Xfp)aZ c=[V~7-N8OyV6*LAPx*;  %Jm0ʬ}GтE}m%y317O!t<֙1tUW[s wF"^T[1?9·Yzt4dkdƮXU?pEDod#<`}O,?-a2.]rA9nZZg`%cOV}آTH#;V~.X1ײ@h(wVGyNbH8UAlӬ*"0h{-X]aߊ[QLu {~2rwi QJ]. R%"D73tB>yYJ:a \ArWdy]T)ATWĮ2LFvDWdoEь"N\#z959_r"l,}eīKb ~v^BیYjDͦ% ^{Lme3 {z+9K7{FPX 2.efpM$A;E?= )qaԫg[@B+ nặjzFV1a6u/Y>7H./tfwGX^xb[N$۴#-‰{N>g@9>Ӻ6a ۋ|_w%RK Vwr5v.LͱK@v]e64"{*͝z6r<9M 6Y2)ru -}OS`@G~efy-]NX,P;YAuhy~ P@j>o^ ╛"> fLs&@Q- 4H@eѿW8VYs8O4%Mq@f7Դd?s!Eh@3|]2ߎ[z"3 hvv͇{7ğc>M&BOۗ:c4@[(]b]]|BQժkL;Et,'yQi^6Qst /^~KBxJo7;h(O_ψSUە“D}@.UwcQ@027H. N4yx|#ɞ2慗ӈʲy62e`O8Fo\"J=\ \i A]|UV"~\W`6_Y{/V?ӷfi;\OoKS[!#v˅f$Mk8jfeQm7ܢ²?BeaKp#;'x?*g3ݺ2HuplHsYOW]IYwk>JQoT:A_yMC} 7?J{UԆ޵p kZ7oW~bw m;{9,D(wGJ|(sXʱ{d-~*M<8n*"!2R]i|)+ٹ+*A3*natwPR9X.eiɖ1YU̱p~egqVֱX\Tܘd |{2h3VYO$k5%2#(7G+j|j~hsJ@ZӪKƤ;U 鲾04}r:,ߚsP9a&hMMI(td9bkL(|-PY[R AKSFSٗ;#WM-m(;XcG9 ejbRAbŷu*+j'[&sY]>>8ƁGħO JhKYCz܏\9@F_e%1+)Ԑd!K#)]H[v3R=5[@=qv>AP;@Wo*Ni oL^̳e !i:l>oPVZWB({J]uLkZ56~ˬi~K,t`ME=;q:`?;*{sl.G`P8^,Pxu7< BXsfyF`MvcKjccKO+ua }RM!D!S!lrt tMbWCuDq VrRp¥V gN:S>`>@[K|F>~ Y(~OmLyvFJ9Qwd,vJjo1 G5/پ1"&/76z~iT E%[Σp+;얛Wh `xqNUgy82N (o)5 a@[<)E[Dg7YnQk`wQ!5d*ӀL3%cU__<|X(]26oSm+7;FTzݢ63t#l 5(D"zjZVRNNNNNNNNNJJJJJJJJJJJJJJJJJJcY~|' ,(`v03,] _j +jBnɀʀ 4<M :jR& & h_(D0,Bx&X%P$L$L$L$koetp#H$H$H$H$H$H$cK:1o(&l |,Y _j +jBnɀ j륵Hgߔ|XYY`o2֞ėt{ǯu̩zN$AU?qE.3YF.57Kp" s@p*\ ge3 ~Mu{%0!AUszZػ4u@:NqwRG.L&;蟠Af[etztw4An>" 9V02Iy\F:7nl<'/:kQyE&s?uvv:1آUz~l_B;K8Ո4U@I ٟcAGJi0F5g9(%jw梥$3kJӺ\'w˙LH\h iL{&[FQ -Glf z sAG{G+>͹-Znj'?$_1j4jtIfn,{%Wp\a`k AhS[ |LӹKL5,.K9ǏݾB Q琶3Sp_m#5ڧ!ߓ {njoe:!+el_b^0Ёat }CJS|HS_Ɍl qr8?0Tmh,c{FG519|ˊKGU;M "v(&ԡSpuPmZ"=s|3Z}Q&NGL^7kjȖRHva/GpNpTK$L 姫@PL󶤿; 'OfK`G+8z?Ii~YB(g|뷺/ wuMiVx^뚵ہt\6P59n:&CSm(= D{Y؏糐J:;fUpiQXOyaPЌZdgovw$g;J1P ?mȌPE 0TDž_:;ùD1RnUױ5Nlݟo83.nPxjZc{ P{[P+brǧZizv) RzWc~mj^:l=Tjc]ӧ:VB-oy]PJ*vغ oө+*sϿM:4# ;XYHHD<X~{mieI8wtYQُIAXp^\y,Dpgghіk}\2YqWRn. ab.AagWw27 eTO }u}nK b&ԑLwDTcljG]J b@|"H %9:FjL{RDH~"Ԁk $ @+(Bn~2YD,4&CP=̹G8p5-ꧬћv%EvQ{KE3 M@p, K$x˧_2bf-0D#u_[O& lN6mSR|ZcPr2R|a@nWdp'x۴#2(+yDV@e-J1r4:,,ꅩz,)ϷҁxFww9yW3tFw%= R0E2",b0mvZia%юzGU곦0K4 RJ#B,lJ *'WQH8%F7ASg>k[m|F77c~~Os" YBzAj'/<>Mw*|[}O+@# Ə%ވޠ ݷ@<ʴboJDy%,SFf8GouY(h3J?Np娼m(+1PӖD.HN:Ry_q)ՕKR$s1vԄr)C[FQ@\y2X^;Ij%`N +F4=7$c4VRD8dZC%`yZ!ùg_Д}+MzA]fgZHT%VllRb%}0}B Ƶd }%gs~}_i^=acfBMFoTn%,f/OO Vuo|٢ {?u8g[EJSͤB0Iz ѧVҡntcxklj>y$7{4^P= ݠ'(DFϒG^r9zQF|vcGRp&IQt]FXI{-?2/|  'xEG&x_ ܚw$џ݈v wj,_LVll 6/E\ZcBbӮv~fG*j WBlbYfKҮn \\%M!ck۾ܮ:fZFn=uM+#2(5 Ğx?'(?gx>HdL`7Y08dM?S-e]:rƎ(&q#b=F;|c퐐`Z^Lpwy;\^ F VN6˖%z+#\QHk 4"Ko5Aro-^j#6gYߧɸ3 HpxGggoS.v${%?Ca3h^R9h$*1g^UF4G \a k A cf\ ^!9QoK 6qD|:qBۓ N^XnD}KXCwh<=\%hi, Cd8 e ]DX!kݒ`@8([s Iǵhܙq jfKk&E^01T{0xxfbN5Qp _WxL)l5<4I)9'D$ *7vv?Vgh(eX89Üun>K)rY~ ~Ƀ}+*CjֺL>kZ_oCY²آ4 "6p~fm{, tv5$GWn%9)|S<*U/${6^GxstHMHp9tH#(ѹD jX85U@X~խy`~!g #$ a*N!)u=Ίq|Ԯ !7;΄tNZr^(Jo2UI\@fs]Pzsy7w#%ٺĭ%' gو_YRjԺ8,ؓ&J;!ZUO>+Eci. 6e鼻U݅ bFeoMK(:ݲWQek"^MaKi/)(1Fz3b m2 )-?r傽{5"ϯD|PF!Sb3cgI\oDm N}̧G@ iGh>O nCgpFJRCW43(h Bpڗ˿S@lP}5~hO7ge&׊r;^iixUrgytf`5 +#s( V9A=J"ҏa c8Ώ犯)tӦÒSH:o (-+Z5,YZ/~12%\M,gDs*۳mJ twDClbacGz כɹl8Ot)(d88d!9,®uF6f6|}ǁ#E# 6bxbCPW N wGM 3%?@8~\0~%l5e~<2>w% W2(=Q4}pkTw t'aEnUȧVux>[KtJB^5#ۭmMYFWIaPt%EjRx((m( + F7/TGAGnf ~}0/9xݓ]B4-_SóbEsM'W)YCvrrxdWgsj'}l0Qo;@$=2!6oIc:z98 S^sk X ۸$ 1=sO:˿ -nH"@N}6gL)#" ӾRġұqpr THX%>5[:6Rk0B 9ρH1y%"R͊pR : Xh KKlFG,,{qvblFtrZ~UQPNf,Q[phl!.̗4\-|v dvag!y#T#բ9Ƌgp~_ + 92]Wi |Hڒ൘oƢ`N E7dSN*^|10Az `co"s?K)V8(Ybc>,ݡNT~}&; -laf@˚a+ѵH<rێUD\~oY?I"PToR.aKk[n3q:I[ӔbJ@ĨpJ*MP'*6@oKic΋ Kan46W8Nu;cxlGWw l5Xvh_6c‰ 0wB+ѺY%bAn)yz< G-08efC2.8?(9)7?7sM [!p[62cW<$9p/_WFzZS8pƪiDZd6`5熏wO\=M R [rM6@ '91WV3&a<*pT  ^⭜W( p:"^4& D?ZqJ74dvDFo)Pcm  hAh"HfKE12ֽvEc)8I :uRؙKݦV_->Zkc:w=_ 7b :DEE⟴׉)t)lg=|%Ⱥ:gÄ='?C39V"i:fĝq&ک;KK?8a-%VI]j ćT D19H^O~YiyTc~2R9-(4V  PyضyQC'#3y+Ru> Cw.~6 L߬^orW  +A_R`׳|^8b6?j^ea]MzৱBf緯;U-*hOuw Ѩ\[.< ՅN*V12ǀJkL-\$cs6$BO,NS" `C0񒕨_*>[RˎՌG>\-a.ߎ>ҟ,;X}^IJK.D~W?ɼfaRE=*hihu5s/䳕|Ctm`P#RexdW@|9$ExAϓ#EOR‚KYQgcD81Ɣ>cږ[o~a '1+.NqѸ]n-f'~gIZ% 0P ,[' Z%g  f3Qlo߷G@OY|`jxFb}ȽELm=)ٕqQnJ:b@?yљP_Y@0W蜙جdsCW[p7ZQkHy~ѡ]{NR l 5[I(weD!(®(wzŦdw~k' /n&q\qƷ!+,gjBr>_ځFlиF%QϤ%Q~J}>-KTvۈPP US`騃fѧwcԘU5Ҍu- 'ަ!-e=DX!8.M$`l`I,f? ɤԸ5sOڒkE=)#BY?Pӵ[ŀ΄#r8r Y# MH2!sK.:+9^x(o>%']Vҥ!W#߬ǃLź巎z?$&/uSU^o/)iz3`e+YĬf<>9&){65X gx=368dIl_ B?8Q$;>t z|xͪ=7fh?~փ{_&D^8<ã>"> W=SЕLS3#Qpk~JؑYARlA_aNAc@Mye;8" i(U oQ%:A~"fA"k_gS gƪ"z\5M_p4&>b|Qp5}wk;=A:{@4-,P*IT߅L0"ﴔA/v\ShP>=xN*}prNY(B͒P;q1u+ƐD-x-S1,]=R+.k&q蔉C¿d7We?] t--X~aүU|lu<5ފFgnP_^emc֋AoP5aAmdCWY ,]mR tK@n䬰3+pJ ē4|N}7t'Α_ ē&1fKE\+< 9B "GR,1sяz7_P~ fd/8#e`TC@`zlĹkQi>7@G9vDVHЏR|w- X0k 1P{)U\Kbݮbjs!!"$F b #km0)6mt=1 $9v1_ul̓ hX?8J$/$B7gn![&ȵ I-uB,jM@: g|GY{R1Z?Mn{*z|b{wZ@PC kw#/849ãT^6K'vD^ۢI -n<-]d?SHOHHNmKsL1JS港Y3ϐqhgP'yBo-=uW_YYb[Squ86 [NS6@ شkŭhKE t@'nU< Q])6m/ە ~(nWQe^)G(o;(?1㱟4Ec8> 4~wtG=;>F)Q[1` >f)evn%e5Aވ#+BwŊ,wkκTnKCSbv[c*4 EѠ%!+O"5R΃oH O{Bwy6vZXX`'ۡ|'C3ޙW(B?VD5Ljp7#OCQU]lD_Xs ǻz/pG1(a]0%D#rH$Vk{n7 W _#Jr)`n3}@GDYA2=ԯr _#b!kl{k4sX~n@ kJpF ~^xL`<Ŗ){}M6ݬIdԻ[Rz9G.a1i/֨6L6oz0y'K,T"0Zv\7` Cir/ף$ $XӠBN[.plvЯѶ6Ӿ O#][ى֣ͨ5`9O9_<z3t=pz;?Wf &ڔY*SCx"(ߔAV$]*-b-EBVO$p}bt(FrEXiUX]zXÕ#iJ,hGAY .Kk*̓p{lMѫ=]okgl,^* Pw 9(QOA*OUX(_ܐ >ER! נN/07k"%T Η\*#)$A+6I14dB&''"HVĜ!|Q'ͣz];KNs̾C$[o%NGa$"p_wׯ( i$e. nx7'jVquí1A/ 2LTSJO0.ܒkkTG}#=H+FC`ܐ2e| g˅D G¨ݜDH^q#īiK?ƛ7vK 3{qLU*|r*<84ʃz wPd!ɳ}_܋͎FgD'A\["&_/h[o n{g[&%ipٟ 0w?@L^zG;Z簥;ю #&SŞJӜq` 9i?3`.Q3'Ct>_ fk mZ8?1? 5]  &JYf ^vIkmVԬ|5U(\u~)7\9@>h(1q`JE Ksa0(K7c2|25cvq6t报.qMOݡI\hYk Kop{S{^5q@`j)V2lHčxze0lcG#AO8rw\yp6B^jh p[XZ4é23?iP,Hbx, <T22-g/n÷}V5MRξ0S9u/;* ?A3zoޠqB"ՙ~ġ+@ sUk *ULoABK9E7{ToCU-8a> O"|Wo~ >U~OԐgߪ}yq5׬ Mk~ j] )yI|Dbf$gѓH 2<]7y/281 %7w7ڭ7uZ>=d0P=$ct¯{Ujq8)yㄭɢoaмc9fQ(W'j.m@Szdh.kpֻe`Qa+D0R1V~t.G2|0L/:p>]Y%;jfHBRZ=%L/$IyjQ5FGwxsVpV!$`!؊UY)fOۧzEE3"ak _wn5&'\8VK$B=c ր*eا9dz|M iOy@ 8Wv{l '[dxLT{@Be$8(Q;.R%Q6+ʙ:)x' }E!0=s`a=Xxj/mFcgM#j$MSjDZ<$A1fͽ&dw3{Տń>-䔆v| _?b—B 5,ٴ愎?K{/v^{+ " oʜ{c#$֧;56W(j+kv/^r܏->d',wdn@)G:o̒YoK#_|8{/Yv=`.eGeTy(|8W='&TN\fiпߖl^Dy 7t+/:Jgs|Rzg'Lz_iIF'D-8%nc0'=b܀`:M]76ܟ:?YDHL&ơ*! 4[X@6r%>-(/ jtMoJrŭeMlH:D%;еyci j>x Z'\I6w*I "^k>QS |@|qRsj?5 (g'l}N@ۋ(=paN* >mY})#s+.Mʜ-@d%*G\b 缎-73@06:$o#~By;￉0|'ZڒU$oU8Lv9u} gȢZwxκɪz/80;R1\c'Sd"hEK| )F`S7@cQ%5''\0ռF"cU~OqGYϏWpgmjO4 [cp҄ 6.漲Lïl$h5h>QM6vr0 [env6JmT7_yr6Mr>NV=y"7%spkb!XM>901w#V8b*,v|o=/؋}4^θLZtƌ\O"`#2i r~MDŽ|(MpExk+uHmw3=6b0DjSP󖄢Er!ӵPnG6P=m8hx:i8_"oј:En?X>D\D 6s<Ov/X\2f(Cn&V*g2 ց{^y7zƽEf.AtgI ;ل`E2r<6 mЃ\`*Ԥ[e |ҥR8x) j_e QڗpKr?Jwk3ev>?Uvg'InU5TLVӇq/H㕳W**<>|/VGnA*w fX,s,59$U<*2w 4Nɣk(ƻ~N6?F =!f4A6 gz6V#P0j>PzRШF271=l5FVoLJ^k7g;%H0cF܄=3/ ȓ>nmPćmH}E>ǟM>ٙwt.{˝ʽXP{#SkE pr֜ٚ#O?L|f]o u۝$iSk~pWFo Z9]DvڙnwǴ2 ÉġZc.#/Οz "љb4.Shx̌g~Y\2s蒤IUpl/%֋uKs: {Z4Qg0ё^: O8}enU3nm0]@[*3<%J?,{'~{GK@y-X&B#X`NwW QH KrvXln9P:6;#TE^A=w?%( ?7ZJi귁z7>>?vAo]h窔%?i9e /B(h]{ !mBT&* 4moϩ]eǵAa u%=!]iϧx~۷Lٳ<7g(^Џ#Д8Btyϫ67;m*<{YAϖ'Ȝې?gTΦ0`#Qi}nL BѩZKBLWԌ:%h;8]ƹ@qs-; W/Ϣ6pʊ̺qEU|,MZ%Bu\kڰ^qh0\Y z_x]BQDx2xh'NwiV|acgy3Iٰ^DNFRsl*<0i 3)#)2\}d} i?B%U`R*ӬwZ'Q;}൜ͧ9_,Q8ʗLFIJfpM)@,Z;l2}neǵAa u%;l&zSk*Hz~` ꟳ (+Md`\ZB%_)m{pZ;g D~THנ6c暈=f'_ Zq@f7Դd?s!Eh@3|]2ߎ[z"3 hvv͇{7ğc>M&BOۗ:c4@[(]b]]|BQժkL;Et,'yQi^6Qst /^~KBxJo7;h(O_ψSUە“D}@.UwcQ@027H. N4yx|#ɞ2慗ӈʲy62e`O8Fo\"J=\ \i A]|UV"~\W`6_Y{/V?ӷfi;\OoKS[!#v˅f$Mk8jfeQm7ܢ²?BeaKp#;'x?*g3ݺ2HuplHsYOW]IYwk>JQoT:A_yMC} 7?J{UԆ޵p kZ7oW~bw m;{9,D(wGJ|(sXʱ{d-~*M<8n*"!2R]i|)+ٹ+*A3*natwPR9X.eiɖ1YU̱p~egqVֱX\Tܘd |{2h3VYO$k5%2[GTm-+YI Ds1HƦ#qQQ{d);o.|#S[sįY i3T'Z4xlőr!"L;ɭ U OҾ3~搯zJEt'PLBɇ>Sǐ**~ZYM/ K4k`lT^d2pJ^nz`uT( ~gw*L5-ڻ-mBmYmVxFJ.H6-5n=Q$ d+MĘӎ'v{0|?P`^]u͚􎡚FmK嵑=_.}` ׬WJkE8`w= x*q/^ RӅQV006^Gż+'GD ~5p h$c*ya$P :jR& & h plasma5-wallpapers-dynamic-3.3.6/data/Dynamic Numbers/metadata.desktop000066400000000000000000000003031375723327100257310ustar00rootroot00000000000000[Desktop Entry] Name=Dynamic Numbers X-KDE-PluginInfo-Name=Dynamic Numbers X-KDE-PluginInfo-Author=Vlad Zahorodnii X-KDE-PluginInfo-Email=vlad.zahorodnii@kde.org X-KDE-PluginInfo-License=LGPLv3 plasma5-wallpapers-dynamic-3.3.6/src/000077500000000000000000000000001375723327100174605ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/CMakeLists.txt000066400000000000000000000003231375723327100222160ustar00rootroot00000000000000plasma_install_package(package com.github.zzag.dynamic wallpapers wallpaper) add_subdirectory(declarative) add_subdirectory(lib) add_subdirectory(plugins) add_subdirectory(tools) add_subdirectory(translations) plasma5-wallpapers-dynamic-3.3.6/src/Messages.sh000066400000000000000000000001751375723327100215660ustar00rootroot00000000000000#! /usr/bin/env bash $XGETTEXT `find . -name \*.qml -o -name \*.cpp` -o $podir/plasma_wallpaper_com.github.zzag.dynamic.pot plasma5-wallpapers-dynamic-3.3.6/src/declarative/000077500000000000000000000000001375723327100217435ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/declarative/CMakeLists.txt000066400000000000000000000024321375723327100245040ustar00rootroot00000000000000add_definitions(-DTRANSLATION_DOMAIN=\"plasma_wallpaper_com.github.zzag.dynamic\") set(FALLBACK_WALLPAPER "Dynamic Numbers") configure_file(config-dynamicwallpaper.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-dynamicwallpaper.h) set(dynamicwallpaperplugin_SOURCES dynamicwallpapercrawler.cpp dynamicwallpaperdescription.cpp dynamicwallpaperengine.cpp dynamicwallpaperengine_solar.cpp dynamicwallpaperengine_timed.cpp dynamicwallpaperextensionplugin.cpp dynamicwallpaperhandler.cpp dynamicwallpaperimagehandle.cpp dynamicwallpaperimageprovider.cpp dynamicwallpapermodel.cpp dynamicwallpaperpreviewcache.cpp dynamicwallpaperpreviewjob.cpp dynamicwallpaperpreviewprovider.cpp dynamicwallpaperprober.cpp ) add_library(plasma_wallpaper_dynamicplugin ${dynamicwallpaperplugin_SOURCES}) target_link_libraries(plasma_wallpaper_dynamicplugin Qt5::Concurrent Qt5::Core Qt5::Gui Qt5::Positioning Qt5::Qml Qt5::Quick KF5::ConfigCore KF5::I18n KF5::Package KDynamicWallpaper::KDynamicWallpaper ) install(TARGETS plasma_wallpaper_dynamicplugin DESTINATION ${KDE_INSTALL_QMLDIR}/com/github/zzag/plasma/wallpapers/dynamic) install(FILES qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/com/github/zzag/plasma/wallpapers/dynamic) plasma5-wallpapers-dynamic-3.3.6/src/declarative/config-dynamicwallpaper.h.cmake000066400000000000000000000000631375723327100277710ustar00rootroot00000000000000#define FALLBACK_WALLPAPER "${FALLBACK_WALLPAPER}" plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpapercrawler.cpp000066400000000000000000000067151375723327100273740ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "dynamicwallpapercrawler.h" #include #include #include /*! * \class DynamicWallpaperCrawler * \brief The DynamicWallpaperCrawler class discovers dynamic wallpapers. */ /*! * Constructs an DynamicWallpaperCrawler object with the given \p parent. */ DynamicWallpaperCrawler::DynamicWallpaperCrawler(QObject *parent) : QThread(parent) , m_token(QUuid::createUuid()) { } /*! * Destructs the DynamicWallpaperCrawler object. */ DynamicWallpaperCrawler::~DynamicWallpaperCrawler() { wait(); } /*! * Returns the UUID that uniquely identifies this crawler. */ QUuid DynamicWallpaperCrawler::token() const { return m_token; } /*! * Sets the list of directories where dynamic wallpapers should be searched to \p roots. * * Dynamic wallpapers will be searched recursively. */ void DynamicWallpaperCrawler::setSearchRoots(const QStringList &roots) { m_searchRoots = roots; } /*! * Returns the list of directories where dynamic wallpapers should be searched recursively. */ QStringList DynamicWallpaperCrawler::searchRoots() const { return m_searchRoots; } /*! * Sets the package structure for dynamic wallpaper packages to \p structure. */ void DynamicWallpaperCrawler::setPackageStructure(KPackage::PackageStructure *structure) { m_packageStructure = structure; } /*! * Returns the package structure for dynamic wallpaper packages. */ KPackage::PackageStructure *DynamicWallpaperCrawler::packageStructure() const { return m_packageStructure; } void DynamicWallpaperCrawler::run() { for (const QString &candidate : qAsConst(m_searchRoots)) visitFolder(candidate); deleteLater(); } void DynamicWallpaperCrawler::visitFolder(const QString &filePath) { QDir currentFolder(filePath); currentFolder.setFilter(QDir::NoDotAndDotDot | QDir::NoSymLinks | QDir::Readable | QDir::AllDirs | QDir::Files); currentFolder.setNameFilters({ QStringLiteral("*.heic"), QStringLiteral("*.heif") }); const QFileInfoList fileInfos = currentFolder.entryInfoList(); for (const QFileInfo &fileInfo : fileInfos) { if (fileInfo.isDir()) { if (checkPackage(fileInfo.filePath())) { emit foundPackage(fileInfo.filePath(), token()); } else { visitFolder(fileInfo.filePath()); } } else { visitFile(fileInfo.filePath()); } } } void DynamicWallpaperCrawler::visitFile(const QString &filePath) { // Not every heif file is a dynamic wallpaper, we need to read the file contents to // determine whether filePath actually points to a dynamic wallpaper file. KDynamicWallpaperReader reader(filePath); // If the first image has valid metadata, assume that it's indeed a dynamic wallpaper. KDynamicWallpaperMetaData metaData = reader.metaDataAt(0); if (metaData.isValid()) emit foundFile(filePath, token()); } bool DynamicWallpaperCrawler::checkPackage(const QString &filePath) const { if (!QFile::exists(filePath + QLatin1String("/metadata.desktop")) && !QFile::exists(filePath + QLatin1String("/metadata.json"))) return false; KPackage::Package package(packageStructure()); package.setPath(filePath); const QUrl imageUrl = package.fileUrl(QByteArrayLiteral("dynamic")); return imageUrl.isValid(); } plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpapercrawler.h000066400000000000000000000021161375723327100270300ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include class DynamicWallpaperCrawler : public QThread { Q_OBJECT public: explicit DynamicWallpaperCrawler(QObject *parent = nullptr); ~DynamicWallpaperCrawler() override; QUuid token() const; void setSearchRoots(const QStringList &candidates); QStringList searchRoots() const; void setPackageStructure(KPackage::PackageStructure *structure); KPackage::PackageStructure *packageStructure() const; Q_SIGNALS: void foundPackage(const QString &packagePath, const QUuid &token); void foundFile(const QString &filePath, const QUuid &token); protected: void run() override; private: void visitFolder(const QString &filePath); void visitFile(const QString &filePath); bool checkPackage(const QString &filePath) const; KPackage::PackageStructure *m_packageStructure = nullptr; QStringList m_searchRoots; QUuid m_token; }; plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperdescription.cpp000066400000000000000000000061611375723327100302530ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "dynamicwallpaperdescription.h" #include "dynamicwallpaperimagehandle.h" #include /*! * Constructs an invalid DynamicWallpaperDescription object. */ DynamicWallpaperDescription::DynamicWallpaperDescription() { } /*! * Returns \c true if the DynamicWallpaperDescription is valid; otherwise returns \c false. */ bool DynamicWallpaperDescription::isValid() const { return !m_imageUrls.isEmpty(); } /*! * Returns a bitmask that indicates which wallpaper engines can display this wallpaper. */ DynamicWallpaperDescription::EngineTypes DynamicWallpaperDescription::supportedEngines() const { EngineTypes types = SolarEngine | TimedEngine; for (const KDynamicWallpaperMetaData &metaData : m_metaData) { // Exclude the solar engine if there's at least one image without solar metadata. if (!(metaData.fields() & KDynamicWallpaperMetaData::SolarAzimuthField)) types &= ~SolarEngine; } return types; } /*! * Returns the total number of images in the dynamic wallpaper. * * This method will return \c 0 if the DynamicWallpaperDescription object is invalid. */ int DynamicWallpaperDescription::imageCount() const { return m_imageUrls.count(); } /*! * Returns the url for the image with the specified index \p imageIndex. * * This method will return an invalid QImage if the DynamicWallpaperDescription is invalid * or if the provided index is outside the valid range. */ QUrl DynamicWallpaperDescription::imageUrlAt(int imageIndex) const { return m_imageUrls.value(imageIndex); } /*! * Returns the metadata for the image with the specified index \p imageIndex. * * This method will return an invalid KDynamicWallpaperMetaData if the DynamicWallpaperDescription * is invalid or if the provided index is outside the valid range. */ KDynamicWallpaperMetaData DynamicWallpaperDescription::metaDataAt(int imageIndex) const { return m_metaData.value(imageIndex); } /*! * Attempts to load the DynamicWallpaperDescription for the given file name \p fileName. * * Returns a valid DynamicWallpaperDescription if the loading succeeds; otherwise, the returned * description will be invalid. */ DynamicWallpaperDescription DynamicWallpaperDescription::fromFile(const QString &fileName) { KDynamicWallpaperReader reader(fileName); if (!reader.imageCount()) return DynamicWallpaperDescription(); DynamicWallpaperDescription description; for (int i = 0; i < reader.imageCount(); ++i) { const KDynamicWallpaperMetaData metaData = reader.metaDataAt(i); if (!metaData.isValid()) return DynamicWallpaperDescription(); DynamicWallpaperImageHandle handle; handle.setFileName(fileName); handle.setImageIndex(i); description.addImage(handle.toUrl(), metaData); } return description; } /*! * \internal */ void DynamicWallpaperDescription::addImage(const QUrl &url, const KDynamicWallpaperMetaData &metaData) { m_imageUrls << url; m_metaData << metaData; } plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperdescription.h000066400000000000000000000017241375723327100277200ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include class DynamicWallpaperDescription { public: enum EngineType { SolarEngine = 1 << 0, TimedEngine = 1 << 1, }; Q_DECLARE_FLAGS(EngineTypes, EngineType) DynamicWallpaperDescription(); bool isValid() const; EngineTypes supportedEngines() const; int imageCount() const; QUrl imageUrlAt(int imageIndex) const; KDynamicWallpaperMetaData metaDataAt(int imageIndex) const; static DynamicWallpaperDescription fromFile(const QString &fileName); private: void addImage(const QUrl &url, const KDynamicWallpaperMetaData &metaData); QVector m_metaData; QVector m_imageUrls; }; Q_DECLARE_OPERATORS_FOR_FLAGS(DynamicWallpaperDescription::EngineTypes) plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperengine.cpp000066400000000000000000000072551375723327100272020ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "dynamicwallpaperengine.h" /*! * Destructs the DynamicWallpaperEngine object. */ DynamicWallpaperEngine::~DynamicWallpaperEngine() { } /*! * Sets the DynamicWallpaperEngine's wallpaper description to \p description. * * You must call this method before starting using the engine. */ void DynamicWallpaperEngine::setDescription(const DynamicWallpaperDescription &description) { m_progressToImageIndex.clear(); m_description = description; for (int i = 0; i < m_description.imageCount(); ++i) { const KDynamicWallpaperMetaData metaData = m_description.metaDataAt(i); m_progressToImageIndex.insert(progressForMetaData(metaData), i); } } /*! * Returns the DynamicWallpaperDescription object assigned to this engine. */ DynamicWallpaperDescription DynamicWallpaperEngine::description() const { return m_description; } /*! * Returns \c true if the engine has been expired and must be rebuilt; otherwise returns \c false. */ bool DynamicWallpaperEngine::isExpired() const { return false; } /*! * Returns the QUrl of the image that is currently being displayed in the top layer. */ QUrl DynamicWallpaperEngine::topLayer() const { return m_topLayer; } /*! * Returns the QUrl of the image that is currently beind displayed in the bottom layer. */ QUrl DynamicWallpaperEngine::bottomLayer() const { return m_bottomLayer; } /*! * Returns the blend factor between the bottom layer and the top layer. */ qreal DynamicWallpaperEngine::blendFactor() const { return m_blendFactor; } static qreal computeTimeSpan(qreal from, qreal to) { if (to < from) return (1 - from) + to; return to - from; } static qreal computeBlendFactor(qreal from, qreal to, qreal now) { const qreal reflectedFrom = 1 - from; const qreal reflectedTo = 1 - to; const qreal totalDuration = computeTimeSpan(from, to); const qreal totalElapsed = computeTimeSpan(from, now); if ((reflectedFrom < from) ^ (reflectedTo < to)) { if (reflectedFrom < to) { const qreal threshold = computeTimeSpan(from, reflectedFrom); if (totalElapsed < threshold) return 0; return (totalElapsed - threshold) / (totalDuration - threshold); } if (from < reflectedTo) { const qreal threshold = computeTimeSpan(from, reflectedTo); if (threshold < totalElapsed) return 1; return totalElapsed / threshold; } } return totalElapsed / totalDuration; } /*! * Updates the internal state of the DynamicWallpaperEngine. */ void DynamicWallpaperEngine::update() { const qreal progress = progressForDateTime(QDateTime::currentDateTime()); QMap::iterator nextImage; QMap::iterator currentImage; nextImage = m_progressToImageIndex.upperBound(progress); if (nextImage == m_progressToImageIndex.end()) nextImage = m_progressToImageIndex.begin(); if (nextImage == m_progressToImageIndex.begin()) currentImage = std::prev(m_progressToImageIndex.end()); else currentImage = std::prev(nextImage); const KDynamicWallpaperMetaData currentMetaData = description().metaDataAt(*currentImage); if (currentMetaData.crossFadeMode() == KDynamicWallpaperMetaData::CrossFade) { m_topLayer = description().imageUrlAt(*nextImage); m_blendFactor = computeBlendFactor(currentImage.key(), nextImage.key(), progress); } else { m_topLayer = QUrl(); m_blendFactor = 0; } m_bottomLayer = description().imageUrlAt(*currentImage); } plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperengine.h000066400000000000000000000016421375723327100266410ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "dynamicwallpaperdescription.h" #include #include class DynamicWallpaperEngine { public: virtual ~DynamicWallpaperEngine(); void setDescription(const DynamicWallpaperDescription &description); DynamicWallpaperDescription description() const; void update(); QUrl bottomLayer() const; QUrl topLayer() const; qreal blendFactor() const; virtual bool isExpired() const; protected: virtual qreal progressForMetaData(const KDynamicWallpaperMetaData &metaData) const = 0; virtual qreal progressForDateTime(const QDateTime &dateTime) const = 0; private: DynamicWallpaperDescription m_description; QMap m_progressToImageIndex; QUrl m_topLayer; QUrl m_bottomLayer; qreal m_blendFactor; }; plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperengine_solar.cpp000066400000000000000000000045051375723327100303750ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "dynamicwallpaperengine_solar.h" #include SolarDynamicWallpaperEngine::SolarDynamicWallpaperEngine(const KSunPath &sunPath, const KSunPosition &midnight, const QGeoCoordinate &location, const QDateTime &dateTime) : m_sunPath(sunPath) , m_midnight(midnight) , m_location(location) , m_dateTime(dateTime) { } bool SolarDynamicWallpaperEngine::isExpired() const { return m_dateTime.date() != QDate::currentDate(); } SolarDynamicWallpaperEngine *SolarDynamicWallpaperEngine::create(const QGeoCoordinate &location) { const QDateTime dateTime = QDateTime::currentDateTime(); const KSunPosition midnight = KSunPosition::midnight(dateTime, location); if (!midnight.isValid()) return nullptr; const KSunPath path = KSunPath::create(dateTime, location); if (!path.isValid()) return nullptr; return new SolarDynamicWallpaperEngine(path, midnight, location, dateTime); } qreal SolarDynamicWallpaperEngine::progressForMetaData(const KDynamicWallpaperMetaData &metaData) const { const KSunPosition position(metaData.solarElevation(), metaData.solarAzimuth()); return progressForPosition(position); } qreal SolarDynamicWallpaperEngine::progressForDateTime(const QDateTime &dateTime) const { const KSunPosition position(dateTime, m_location); return progressForPosition(position); } qreal SolarDynamicWallpaperEngine::progressForPosition(const KSunPosition &position) const { const QVector3D projectedMidnight = m_sunPath.project(m_midnight); const QVector3D projectedPosition = m_sunPath.project(position); const QVector3D v1 = (projectedMidnight - m_sunPath.center()).normalized(); const QVector3D v2 = (projectedPosition - m_sunPath.center()).normalized(); const QVector3D cross = QVector3D::crossProduct(v1, v2); const float dot = QVector3D::dotProduct(v1, v2); const float det = QVector3D::dotProduct(m_sunPath.normal(), cross); qreal angle = std::atan2(det, dot); if (angle < 0) angle += 2 * M_PI; return angle / (2 * M_PI); } plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperengine_solar.h000066400000000000000000000017041375723327100300400ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "dynamicwallpaperengine.h" #include #include class SolarDynamicWallpaperEngine : public DynamicWallpaperEngine { public: bool isExpired() const override; static SolarDynamicWallpaperEngine *create(const QGeoCoordinate &location); protected: qreal progressForMetaData(const KDynamicWallpaperMetaData &metaData) const override; qreal progressForDateTime(const QDateTime &dateTime) const override; private: SolarDynamicWallpaperEngine(const KSunPath &sunPath, const KSunPosition &midnight, const QGeoCoordinate &location, const QDateTime &dateTime); qreal progressForPosition(const KSunPosition &position) const; KSunPath m_sunPath; KSunPosition m_midnight; QGeoCoordinate m_location; QDateTime m_dateTime; }; plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperengine_timed.cpp000066400000000000000000000011731375723327100303550ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "dynamicwallpaperengine_timed.h" TimedDynamicWallpaperEngine *TimedDynamicWallpaperEngine::create() { return new TimedDynamicWallpaperEngine(); } qreal TimedDynamicWallpaperEngine::progressForMetaData(const KDynamicWallpaperMetaData &metaData) const { return metaData.time(); } qreal TimedDynamicWallpaperEngine::progressForDateTime(const QDateTime &dateTime) const { QDateTime midnight = dateTime; midnight.setTime(QTime()); return midnight.secsTo(dateTime) / 86400.0; } plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperengine_timed.h000066400000000000000000000007441375723327100300250ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "dynamicwallpaperengine.h" class TimedDynamicWallpaperEngine : public DynamicWallpaperEngine { public: static TimedDynamicWallpaperEngine *create(); protected: qreal progressForMetaData(const KDynamicWallpaperMetaData &metaData) const override; qreal progressForDateTime(const QDateTime &dateTime) const override; }; plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperextensionplugin.cpp000066400000000000000000000017611375723327100311640ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "dynamicwallpaperextensionplugin.h" #include "dynamicwallpaperhandler.h" #include "dynamicwallpaperimageprovider.h" #include "dynamicwallpapermodel.h" #include "dynamicwallpaperpreviewprovider.h" #include #include void DynamicWallpaperExtensionPlugin::registerTypes(const char *uri) { qmlRegisterType(uri, 1, 0, "DynamicWallpaperHandler"); qmlRegisterType(uri, 1, 0, "DynamicWallpaperModel"); qmlRegisterType(uri, 1, 0, "SystemClockMonitor"); } void DynamicWallpaperExtensionPlugin::initializeEngine(QQmlEngine *engine, const char *uri) { Q_UNUSED(uri) engine->addImageProvider(QLatin1String("dynamic"), new DynamicWallpaperImageProvider); engine->addImageProvider(QLatin1String("dynamicpreview"), new DynamicWallpaperPreviewProvider); } plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperextensionplugin.h000066400000000000000000000007171375723327100306310ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include class DynamicWallpaperExtensionPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") public: void registerTypes(const char *uri) override; void initializeEngine(QQmlEngine *engine, const char *uri) override; }; plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperglobals.h000066400000000000000000000007341375723327100270200ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include class DynamicWallpaperImageAsyncResult { public: DynamicWallpaperImageAsyncResult() {} explicit DynamicWallpaperImageAsyncResult(const QImage &image) : image(image) {} explicit DynamicWallpaperImageAsyncResult(const QString &text) : errorString(text) {} QImage image; QString errorString; }; plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperhandler.cpp000066400000000000000000000136551375723327100273530ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "config-dynamicwallpaper.h" #include "dynamicwallpaperhandler.h" #include "dynamicwallpaperdescription.h" #include "dynamicwallpaperengine_solar.h" #include "dynamicwallpaperengine_timed.h" #include #include #include #include DynamicWallpaperHandler::DynamicWallpaperHandler(QObject *parent) : QObject(parent) , m_updateTimer(new QTimer(this)) { m_updateTimer->setInterval(0); m_updateTimer->setSingleShot(true); connect(m_updateTimer, &QTimer::timeout, this, &DynamicWallpaperHandler::update); } DynamicWallpaperHandler::~DynamicWallpaperHandler() { delete m_engine; } void DynamicWallpaperHandler::setLocation(const QGeoCoordinate &coordinate) { if (m_location == coordinate) return; m_location = coordinate; reloadEngine(); scheduleUpdate(); emit locationChanged(); } QGeoCoordinate DynamicWallpaperHandler::location() const { return m_location; } static QUrl locateWallpaper(const QString &name) { const QString packagePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("wallpapers/") + name, QStandardPaths::LocateDirectory); KPackage::PackageLoader *packageLoader = KPackage::PackageLoader::self(); KPackage::Package package = packageLoader->loadPackage(QStringLiteral("Wallpaper/Dynamic")); package.setPath(packagePath); if (package.isValid()) return package.fileUrl(QByteArrayLiteral("dynamic")); const QString filePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("wallpapers/") + name, QStandardPaths::LocateFile); return QUrl::fromLocalFile(filePath); } static QUrl defaultLookAndFeelWallpaper() { KConfigGroup kdeConfigGroup(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "KDE"); const QString lookAndFeelPackageName = kdeConfigGroup.readEntry("LookAndFeelPackage"); KPackage::PackageLoader *packageLoader = KPackage::PackageLoader::self(); KPackage::Package lookAndFeelPackage = packageLoader->loadPackage(QStringLiteral("Plasma/LookAndFeel")); if (!lookAndFeelPackageName.isEmpty()) lookAndFeelPackage.setPath(lookAndFeelPackageName); KSharedConfigPtr lookAndFeelConfig = KSharedConfig::openConfig(lookAndFeelPackage.filePath("defaults")); KConfigGroup wallpaperConfigGroup = KConfigGroup(lookAndFeelConfig, "Dynamic Wallpaper"); const QString wallpaperName = wallpaperConfigGroup.readEntry("Image"); if (wallpaperName.isEmpty()) return QUrl(); return locateWallpaper(wallpaperName); } static QUrl defaultFallbackWallpaper() { return locateWallpaper(QStringLiteral(FALLBACK_WALLPAPER)); } static QUrl defaultWallpaper() { QUrl fileUrl = defaultLookAndFeelWallpaper(); if (fileUrl.isValid()) return fileUrl; return defaultFallbackWallpaper(); } void DynamicWallpaperHandler::setSource(const QUrl &url) { const QUrl source = url.isValid() ? url : defaultWallpaper(); if (m_source == source) return; m_source = source; reloadDescription(); reloadEngine(); scheduleUpdate(); emit sourceChanged(); } QUrl DynamicWallpaperHandler::source() const { return m_source; } void DynamicWallpaperHandler::setTopLayer(const QUrl &url) { if (m_topLayer == url) return; m_topLayer = url; emit topLayerChanged(); } QUrl DynamicWallpaperHandler::topLayer() const { return m_topLayer; } void DynamicWallpaperHandler::setBottomLayer(const QUrl &url) { if (m_bottomLayer == url) return; m_bottomLayer = url; emit bottomLayerChanged(); } QUrl DynamicWallpaperHandler::bottomLayer() const { return m_bottomLayer; } void DynamicWallpaperHandler::setBlendFactor(qreal blendFactor) { if (m_blendFactor == blendFactor) return; m_blendFactor = blendFactor; emit blendFactorChanged(); } qreal DynamicWallpaperHandler::blendFactor() const { return m_blendFactor; } void DynamicWallpaperHandler::setStatus(Status status) { if (m_status == status) return; m_status = status; emit statusChanged(); } DynamicWallpaperHandler::Status DynamicWallpaperHandler::status() const { return m_status; } void DynamicWallpaperHandler::setErrorString(const QString &text) { if (m_errorString == text) return; m_errorString = text; emit errorStringChanged(); } QString DynamicWallpaperHandler::errorString() const { return m_errorString; } void DynamicWallpaperHandler::scheduleUpdate() { m_updateTimer->start(); } void DynamicWallpaperHandler::update() { if (m_status != Ready) return; if (!m_engine || m_engine->isExpired()) reloadEngine(); m_engine->update(); setTopLayer(m_engine->topLayer()); setBottomLayer(m_engine->bottomLayer()); setBlendFactor(m_engine->blendFactor()); } void DynamicWallpaperHandler::reloadDescription() { const QString fileName = m_source.toLocalFile(); m_description = DynamicWallpaperDescription::fromFile(fileName); if (m_description.isValid()) { setStatus(Ready); } else { setErrorString(i18n("%1 is not a dynamic wallpaper", fileName)); setStatus(Error); } } void DynamicWallpaperHandler::reloadEngine() { delete m_engine; m_engine = nullptr; if (!m_description.isValid()) return; if (m_description.supportedEngines() & DynamicWallpaperDescription::SolarEngine) m_engine = SolarDynamicWallpaperEngine::create(m_location); if (!m_engine) m_engine = TimedDynamicWallpaperEngine::create(); m_engine->setDescription(m_description); } plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperhandler.h000066400000000000000000000043371375723327100270150ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include "dynamicwallpaperdescription.h" #include #include #include class DynamicWallpaperEngine; class DynamicWallpaperHandler : public QObject { Q_OBJECT Q_PROPERTY(QGeoCoordinate location READ location WRITE setLocation NOTIFY locationChanged) Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(QUrl topLayer READ topLayer WRITE setTopLayer NOTIFY topLayerChanged) Q_PROPERTY(QUrl bottomLayer READ bottomLayer WRITE setBottomLayer NOTIFY bottomLayerChanged) Q_PROPERTY(qreal blendFactor READ blendFactor WRITE setBlendFactor NOTIFY blendFactorChanged) Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged) public: enum Status { Null, Ready, Error }; Q_ENUM(Status) explicit DynamicWallpaperHandler(QObject *parent = nullptr); ~DynamicWallpaperHandler() override; void setLocation(const QGeoCoordinate &coordinate); QGeoCoordinate location() const; void setSource(const QUrl &url); QUrl source() const; void setTopLayer(const QUrl &url); QUrl topLayer() const; void setBottomLayer(const QUrl &url); QUrl bottomLayer() const; void setBlendFactor(qreal blendFactor); qreal blendFactor() const; void setStatus(Status status); Status status() const; void setErrorString(const QString &text); QString errorString() const; public Q_SLOTS: void scheduleUpdate(); void update(); Q_SIGNALS: void locationChanged(); void sourceChanged(); void topLayerChanged(); void bottomLayerChanged(); void blendFactorChanged(); void statusChanged(); void errorStringChanged(); private: void reloadDescription(); void reloadEngine(); DynamicWallpaperDescription m_description; DynamicWallpaperEngine *m_engine = nullptr; QTimer *m_updateTimer; QGeoCoordinate m_location; QString m_errorString; QUrl m_source; QUrl m_topLayer; QUrl m_bottomLayer; qreal m_blendFactor = 0; Status m_status = Null; }; plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperimagehandle.cpp000066400000000000000000000056211375723327100301660ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "dynamicwallpaperimagehandle.h" #include /*! * Constructs an invalid DynamicWallpaperImageHandle object. */ DynamicWallpaperImageHandle::DynamicWallpaperImageHandle() : m_imageIndex(-1) { } /*! * Returns \c true if the image handle is valid; otherwise returns \c false. */ bool DynamicWallpaperImageHandle::isValid() const { return !m_fileName.isEmpty() && m_imageIndex != -1; } /*! * Sets the file name of the image handle to \p fileName. */ void DynamicWallpaperImageHandle::setFileName(const QString &fileName) { m_fileName = fileName; } /*! * Returns the file name of the image handle. */ QString DynamicWallpaperImageHandle::fileName() const { return m_fileName; } /*! * Sets the image index of the image handle to \p index. */ void DynamicWallpaperImageHandle::setImageIndex(int index) { m_imageIndex = index; } /*! * Returns the image index of the image handle. */ int DynamicWallpaperImageHandle::imageIndex() const { return m_imageIndex; } static QString fileNameFromBase64(const QStringRef &base64) { return QByteArray::fromBase64(base64.toUtf8()); } static QString base64FromFileName(const QString &fileName) { return fileName.toUtf8().toBase64(); } static int imageIndexFromString(const QStringRef &string) { bool ok; const int imageIndex = string.toInt(&ok); if (ok) return imageIndex; return -1; } static QString stringFromImageIndex(int imageIndex) { return QString::number(imageIndex); } /*! * Converts the value of the image handle to a QString and returns it. */ QString DynamicWallpaperImageHandle::toString() const { const QString fileName = base64FromFileName(m_fileName); const QString imageIndex = stringFromImageIndex(m_imageIndex); return fileName + '#' + imageIndex; } /*! * Converts the value of the image handle to a QUrl which can be passed to the Image QML component. */ QUrl DynamicWallpaperImageHandle::toUrl() const { return QLatin1String("image://dynamic/") + toString(); } /*! * Creates a DynamicWallpaperImageHandle from the specified string \p string. */ DynamicWallpaperImageHandle DynamicWallpaperImageHandle::fromString(const QString &string) { DynamicWallpaperImageHandle handle; #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) const QVector parts = string.splitRef('#', Qt::SkipEmptyParts); #else const QVector parts = string.splitRef('#', QString::SkipEmptyParts); #endif if (parts.count() != 2) return handle; // Encoding and decoding a file name to/from base64 is definitely an overkill, but I don't // want to deal with os-specific file path conventions. Sue me. :/ handle.setFileName(fileNameFromBase64(parts[0])); handle.setImageIndex(imageIndexFromString(parts[1])); return handle; } plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperimagehandle.h000066400000000000000000000011511375723327100276250ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include class DynamicWallpaperImageHandle { public: DynamicWallpaperImageHandle(); bool isValid() const; void setFileName(const QString &fileName); QString fileName() const; void setImageIndex(int index); int imageIndex() const; QString toString() const; QUrl toUrl() const; static DynamicWallpaperImageHandle fromString(const QString &string); private: QString m_fileName; int m_imageIndex; }; plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperimageprovider.cpp000066400000000000000000000060361375723327100305660ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "dynamicwallpaperimageprovider.h" #include "dynamicwallpaperglobals.h" #include "dynamicwallpaperimagehandle.h" #include #include #include static DynamicWallpaperImageAsyncResult load(const QString &fileName, int index, const QSize &requestedSize) { const KDynamicWallpaperReader reader(fileName); QImage image = reader.imageAt(index); if (reader.error() != KDynamicWallpaperReader::NoError) return DynamicWallpaperImageAsyncResult(reader.errorString()); // If the requested image size is valid, scale the image without preserving the // aspect ratio. if (requestedSize.isValid()) image = image.scaled(requestedSize); // QtQuick wants images to have the format of ARGB32_Premultiplied, so perform // format conversion in the worker thread right away. if (image.format() != QImage::Format_ARGB32_Premultiplied) image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); return DynamicWallpaperImageAsyncResult(image); } class DynamicWallpaperAsyncImageResponse : public QQuickImageResponse { public: DynamicWallpaperAsyncImageResponse(const QString &fileName, int index, const QSize &requestedSize); QQuickTextureFactory *textureFactory() const override; QString errorString() const override; private Q_SLOTS: void handleFinished(); private: QFutureWatcher *m_watcher; QImage m_image; QString m_errorString; }; DynamicWallpaperAsyncImageResponse::DynamicWallpaperAsyncImageResponse(const QString &fileName, int index, const QSize &requestedSize) { m_watcher = new QFutureWatcher(this); connect(m_watcher, &QFutureWatcher::finished, this, &DynamicWallpaperAsyncImageResponse::handleFinished); m_watcher->setFuture(QtConcurrent::run(load, fileName, index, requestedSize)); } void DynamicWallpaperAsyncImageResponse::handleFinished() { const DynamicWallpaperImageAsyncResult result = m_watcher->result(); if (result.errorString.isEmpty()) m_image = result.image; else m_errorString = result.errorString; emit finished(); } QQuickTextureFactory *DynamicWallpaperAsyncImageResponse::textureFactory() const { return QQuickTextureFactory::textureFactoryForImage(m_image); } QString DynamicWallpaperAsyncImageResponse::errorString() const { return m_errorString; } QQuickImageResponse *DynamicWallpaperImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize) { const DynamicWallpaperImageHandle handle = DynamicWallpaperImageHandle::fromString(id); return new DynamicWallpaperAsyncImageResponse(handle.fileName(), handle.imageIndex(), requestedSize); } plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperimageprovider.h000066400000000000000000000005571375723327100302350ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include class DynamicWallpaperImageProvider : public QQuickAsyncImageProvider { public: QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override; }; plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpapermodel.cpp000066400000000000000000000414031375723327100270260ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "dynamicwallpapermodel.h" #include "dynamicwallpapercrawler.h" #include "dynamicwallpaperprober.h" #include #include #include #include #include #include #include // TODO: The model can be implemented better. class DynamicWallpaper { public: static DynamicWallpaper *fromFile(const QUrl &fileUrl); static DynamicWallpaper *fromPackage(const QUrl &packageUrl); QUrl imageUrl; QUrl folderUrl; QUrl previewUrl; QString name; QString packageName; QString license; QString author; bool isPackage = false; bool isCustom = false; bool isRemovable = false; bool isZombie = false; }; static QUrl folderUrlForImageUrl(const QUrl &url) { const QString fileName = url.toLocalFile(); const QFileInfo fileInfo(fileName); return QUrl::fromLocalFile(fileInfo.path()); } static QUrl previewUrlForImageUrl(const QUrl &url) { const QString fileName = url.toLocalFile(); const QString base64 = fileName.toUtf8().toBase64(); return QLatin1String("image://dynamicpreview/") + base64; } static bool checkRemovable(const QUrl &url) { const QString fileName = url.toLocalFile(); const QString dataDirectory = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); return fileName.startsWith(dataDirectory); } DynamicWallpaper *DynamicWallpaper::fromFile(const QUrl &fileUrl) { DynamicWallpaper *wallpaper = new DynamicWallpaper; wallpaper->imageUrl = fileUrl; wallpaper->folderUrl = folderUrlForImageUrl(fileUrl); wallpaper->previewUrl = previewUrlForImageUrl(fileUrl); wallpaper->name = fileUrl.fileName(QUrl::PrettyDecoded); return wallpaper; } DynamicWallpaper *DynamicWallpaper::fromPackage(const QUrl &packageUrl) { KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Wallpaper/Dynamic")); package.setPath(packageUrl.toLocalFile()); const QUrl fileUrl = package.fileUrl(QByteArrayLiteral("dynamic")); const KPluginMetaData metaData = package.metadata(); DynamicWallpaper *wallpaper = new DynamicWallpaper; wallpaper->imageUrl = fileUrl; wallpaper->folderUrl = folderUrlForImageUrl(fileUrl); wallpaper->previewUrl = previewUrlForImageUrl(fileUrl); wallpaper->name = metaData.name(); wallpaper->packageName = metaData.pluginId(); wallpaper->license = metaData.license(); wallpaper->isPackage = true; if (!metaData.authors().isEmpty()) wallpaper->author = metaData.authors().first().name(); return wallpaper; } class DynamicWallpaperModelPrivate : public QObject { Q_OBJECT public: DynamicWallpaperModelPrivate(DynamicWallpaperModel *model); DynamicWallpaper *wallpaperForIndex(const QModelIndex &index) const; void internalAppend(DynamicWallpaper *wallpaper); void internalPrepend(DynamicWallpaper *wallpaper); void internalScheduleRemove(const QModelIndex &index, bool set); void internalRemove(const QModelIndex &index); void internalReset(); bool contains(const QUrl &fileUrl) const; QModelIndex find(const QUrl &fileUrl) const; bool registerFileName(const QString &fileName); void unregisterFileName(const QString &fileName); void addCustomWallpaper(const QUrl &fileUrl); void addFileWallpaper(const QUrl &fileUrl); void addPackageWallpaper(const QUrl &folderUrl); void removeCustomWallpaper(const QModelIndex &index); void removeFileWallpaper(const QModelIndex &index); void removePackageWallpaper(const QModelIndex &index); void loadCustomWallpapers(); void loadGenericWallpapers(); void handleFoundPackage(const QString &packagePath, const QUuid &token); void handleFoundFile(const QString &filePath, const QUuid &token); DynamicWallpaperModel *q; QVector wallpapers; KSharedConfigPtr config; QPointer crawler; QUuid lastToken; }; DynamicWallpaperModelPrivate::DynamicWallpaperModelPrivate(DynamicWallpaperModel *model) : q(model) , config(KSharedConfig::openConfig(QStringLiteral("kdynamicwallpaperrc"))) { } DynamicWallpaper *DynamicWallpaperModelPrivate::wallpaperForIndex(const QModelIndex &index) const { if (!index.isValid()) return nullptr; return wallpapers.value(index.row()); } void DynamicWallpaperModelPrivate::internalAppend(DynamicWallpaper *wallpaper) { const int row = wallpapers.count(); q->beginInsertRows(QModelIndex(), row, row); wallpapers.append(wallpaper); q->endInsertRows(); } void DynamicWallpaperModelPrivate::internalPrepend(DynamicWallpaper *wallpaper) { q->beginInsertRows(QModelIndex(), 0, 0); wallpapers.prepend(wallpaper); q->endInsertRows(); } void DynamicWallpaperModelPrivate::internalScheduleRemove(const QModelIndex &index, bool set) { const int row = index.row(); if (wallpapers[row]->isZombie == set) return; wallpapers[row]->isZombie = set; emit q->dataChanged(index, index, { DynamicWallpaperModel::WallpaperIsZombieRole }); } void DynamicWallpaperModelPrivate::internalRemove(const QModelIndex &index) { const int row = index.row(); q->beginRemoveRows(QModelIndex(), row, row); delete wallpapers.takeAt(row); q->endRemoveRows(); } void DynamicWallpaperModelPrivate::internalReset() { q->beginResetModel(); qDeleteAll(wallpapers); wallpapers.clear(); q->endResetModel(); } bool DynamicWallpaperModelPrivate::contains(const QUrl &fileUrl) const { return find(fileUrl).isValid(); } QModelIndex DynamicWallpaperModelPrivate::find(const QUrl &fileUrl) const { for (int i = 0; i < wallpapers.count(); ++i) { if (wallpapers[i]->imageUrl == fileUrl) return q->createIndex(i, 0); } return QModelIndex(); } bool DynamicWallpaperModelPrivate::registerFileName(const QString &fileName) { KConfigGroup group(config, QStringLiteral("General")); QStringList wallpapers = group.readEntry(QStringLiteral("Wallpapers"), QStringList()); if (wallpapers.contains(fileName)) return false; wallpapers.prepend(fileName); group.writeEntry(QStringLiteral("Wallpapers"), wallpapers); group.sync(); return true; } void DynamicWallpaperModelPrivate::unregisterFileName(const QString &fileName) { KConfigGroup group(config, QStringLiteral("General")); QStringList wallpapers = group.readEntry(QStringLiteral("Wallpapers"), QStringList()); wallpapers.removeOne(fileName); group.writeEntry(QStringLiteral("Wallpapers"), wallpapers); group.sync(); } void DynamicWallpaperModelPrivate::addCustomWallpaper(const QUrl &fileUrl) { const QString fileName = fileUrl.toLocalFile(); if (fileName.isEmpty()) return; if (!registerFileName(fileName)) return; DynamicWallpaper *wallpaper = DynamicWallpaper::fromFile(fileUrl); wallpaper->isRemovable = true; wallpaper->isCustom = true; internalPrepend(wallpaper); } void DynamicWallpaperModelPrivate::addFileWallpaper(const QUrl &fileUrl) { DynamicWallpaper *wallpaper = DynamicWallpaper::fromFile(fileUrl); wallpaper->isRemovable = checkRemovable(fileUrl); internalAppend(wallpaper); } void DynamicWallpaperModelPrivate::addPackageWallpaper(const QUrl &folderUrl) { DynamicWallpaper *wallpaper = DynamicWallpaper::fromPackage(folderUrl); wallpaper->isRemovable = checkRemovable(folderUrl); internalAppend(wallpaper); } void DynamicWallpaperModelPrivate::removeCustomWallpaper(const QModelIndex &index) { const DynamicWallpaper *wallpaper = wallpaperForIndex(index); unregisterFileName(wallpaper->imageUrl.toLocalFile()); internalRemove(index); } void DynamicWallpaperModelPrivate::removeFileWallpaper(const QModelIndex &index) { const DynamicWallpaper *wallpaper = wallpaperForIndex(index); if (!QFile::remove(wallpaper->imageUrl.toLocalFile())) return; internalRemove(index); } void DynamicWallpaperModelPrivate::removePackageWallpaper(const QModelIndex &index) { const DynamicWallpaper *wallpaper = wallpaperForIndex(index); const QUrl imageUrl = wallpaper->imageUrl; const QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); const QString wallpaperPackageRoot = dataLocation + QStringLiteral("/wallpapers/"); KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Wallpaper/Dynamic")); KJob *uninstallJob = package.uninstall(wallpaper->packageName, wallpaperPackageRoot); connect(uninstallJob, &KJob::finished, this, [this, imageUrl](KJob *job) { if (job->error() != KJob::NoError) return; const QModelIndex index = find(imageUrl); if (!index.isValid()) return; internalRemove(index); }); } void DynamicWallpaperModelPrivate::loadCustomWallpapers() { KConfigGroup group(config, QStringLiteral("General")); const QStringList wallpaperFileNames = group.readEntry("Wallpapers", QStringList()); for (const QString &wallpaperFileName : wallpaperFileNames) { const QUrl wallpaperUrl = QUrl::fromUserInput(wallpaperFileName); if (contains(wallpaperUrl)) continue; DynamicWallpaper *wallpaper = DynamicWallpaper::fromFile(wallpaperUrl); wallpaper->isRemovable = true; wallpaper->isCustom = true; internalAppend(wallpaper); } } void DynamicWallpaperModelPrivate::loadGenericWallpapers() { QStringList candidates = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("wallpapers"), QStandardPaths::LocateDirectory); // Load the package structure in the main thread because it seems like the PackageLoader // class is not thread-safe. Notice that system wallpapers are discovered in another thread // since we may need to read file contents in order to determine whether a given file is // actually a dynamic wallpaper and not just some random heif file. KPackage::PackageStructure *packageStructure = KPackage::PackageLoader::self()->loadPackageStructure(QStringLiteral("Wallpaper/Dynamic")); DynamicWallpaperCrawler *crawler = new DynamicWallpaperCrawler(this); connect(crawler, &DynamicWallpaperCrawler::foundFile, this, &DynamicWallpaperModelPrivate::handleFoundFile); connect(crawler, &DynamicWallpaperCrawler::foundPackage, this, &DynamicWallpaperModelPrivate::handleFoundPackage); crawler->setSearchRoots(candidates); crawler->setPackageStructure(packageStructure); crawler->start(QThread::LowPriority); // Queued events are delivered no matter what, except the case where the receiver object // is destroyed. So each crawler has a token that uniquely identifies it. We use the token // to filter out dynamic wallpapers that are discovered by the previous crawler, if there // is any. lastToken = crawler->token(); } void DynamicWallpaperModelPrivate::handleFoundFile(const QString &packagePath, const QUuid &token) { if (lastToken == token) addFileWallpaper(QUrl::fromLocalFile(packagePath)); } void DynamicWallpaperModelPrivate::handleFoundPackage(const QString &filePath, const QUuid &token) { if (lastToken == token) addPackageWallpaper(QUrl::fromLocalFile(filePath)); } /*! * Constructs an empty DynamicWallpaperModel object. */ DynamicWallpaperModel::DynamicWallpaperModel(QObject *parent) : QAbstractListModel(parent) , d(new DynamicWallpaperModelPrivate(this)) { } /*! * Destructs the DynamicWallpaperModel object. */ DynamicWallpaperModel::~DynamicWallpaperModel() { qDeleteAll(d->wallpapers); } QHash DynamicWallpaperModel::roleNames() const { QHash roleNames = QAbstractListModel::roleNames(); roleNames.insert(WallpaperNameRole, QByteArrayLiteral("name")); roleNames.insert(WallpaperFolderRole, QByteArrayLiteral("folder")); roleNames.insert(WallpaperLicenseRole, QByteArrayLiteral("license")); roleNames.insert(WallpaperAuthorRole, QByteArrayLiteral("author")); roleNames.insert(WallpaperIsPackageRole, QByteArrayLiteral("package")); roleNames.insert(WallpaperIsCustomRole, QByteArrayLiteral("custom")); roleNames.insert(WallpaperIsRemovableRole, QByteArrayLiteral("removable")); roleNames.insert(WallpaperIsZombieRole, QByteArrayLiteral("zombie")); roleNames.insert(WallpaperImageRole, QByteArrayLiteral("image")); roleNames.insert(WallpaperPreviewRole, QByteArrayLiteral("preview")); return roleNames; } /*! * Reimplemented to return the total number of dynamic wallpapers in the model. */ int DynamicWallpaperModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return d->wallpapers.count(); } QVariant DynamicWallpaperModel::data(const QModelIndex &index, int role) const { const DynamicWallpaper *wallpaper = d->wallpaperForIndex(index); if (!wallpaper) return QVariant(); switch (role) { case Qt::DisplayRole: case WallpaperNameRole: return wallpaper->name; case WallpaperFolderRole: return wallpaper->folderUrl; case WallpaperLicenseRole: return wallpaper->license; case WallpaperAuthorRole: return wallpaper->author; case WallpaperIsPackageRole: return wallpaper->isPackage; case WallpaperIsCustomRole: return wallpaper->isCustom; case WallpaperIsRemovableRole: return wallpaper->isRemovable; case WallpaperIsZombieRole: return wallpaper->isZombie; case WallpaperImageRole: return wallpaper->imageUrl; case WallpaperPreviewRole: return wallpaper->previewUrl; } return QVariant(); } /*! * Returns the index of the dynamic wallpaper identified by the specified image url \p fileUrl. * * This method will return -1 if dynamic wallpaper with the given image url doesn't exist. */ int DynamicWallpaperModel::find(const QUrl &fileUrl) const { const QModelIndex index = d->find(fileUrl); return index.isValid() ? index.row() : -1; } /*! * Returns a QModelIndex for the specified \p index. */ QModelIndex DynamicWallpaperModel::modelIndex(int index) const { return createIndex(index, 0); } /*! * Reloads the dynamic wallpaper model. */ void DynamicWallpaperModel::reload() { d->config->markAsClean(); d->config->reparseConfiguration(); d->internalReset(); d->loadCustomWallpapers(); d->loadGenericWallpapers(); } /*! * Removes all dynamic wallpapers that are scheduled to be removed. */ void DynamicWallpaperModel::purge() { for (int i = rowCount() - 1; i >= 0; --i) { const QModelIndex wallpaperIndex = createIndex(i, 0); if (!wallpaperIndex.data(WallpaperIsZombieRole).toBool()) continue; remove(wallpaperIndex); } } /*! * Adds the dynamic wallpaper with the given url \p fileUrl to the model. */ void DynamicWallpaperModel::add(const QUrl &fileUrl) { DynamicWallpaperProber *prober = new DynamicWallpaperProber(fileUrl, this); connect(prober, &DynamicWallpaperProber::finished, this, &DynamicWallpaperModel::handleProberFinished); connect(prober, &DynamicWallpaperProber::failed, this, &DynamicWallpaperModel::handleProberFailed); prober->start(QThread::LowestPriority); } void DynamicWallpaperModel::handleProberFinished(const QUrl &fileUrl) { d->addCustomWallpaper(fileUrl); } void DynamicWallpaperModel::handleProberFailed(const QUrl &fileUrl) { emit errorOccurred(i18n("%1 is not a dynamic wallpaper", fileUrl.toLocalFile())); } /*! * Schedules a dynamic wallpaper with the specified model index \p index for removal. * * The dynamic wallpaper will be removed from the model when purge() is called. */ void DynamicWallpaperModel::scheduleRemove(const QModelIndex &index) { if (!index.isValid()) return; d->internalScheduleRemove(index, true); } /*! * Unschedules a dynamic wallpaper with the specified model index \p index for removal. */ void DynamicWallpaperModel::unscheduleRemove(const QModelIndex &index) { if (!index.isValid()) return; d->internalScheduleRemove(index, false); } /*! * Removes the dynamic wallpaper with the specified model index \p index. */ void DynamicWallpaperModel::remove(const QModelIndex &index) { const DynamicWallpaper *wallpaper = d->wallpaperForIndex(index); if (!wallpaper) return; if (wallpaper->isCustom) d->removeCustomWallpaper(index); else if (wallpaper->isPackage) d->removePackageWallpaper(index); else d->removeFileWallpaper(index); } #include "dynamicwallpapermodel.moc" plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpapermodel.h000066400000000000000000000033641375723327100264770ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include class DynamicWallpaperModelPrivate; class DynamicWallpaperModel : public QAbstractListModel { Q_OBJECT public: enum Roles { WallpaperNameRole = Qt::UserRole + 1, WallpaperFolderRole = Qt::UserRole + 2, WallpaperLicenseRole = Qt::UserRole + 3, WallpaperAuthorRole = Qt::UserRole + 4, WallpaperIsPackageRole = Qt::UserRole + 5, WallpaperIsCustomRole = Qt::UserRole + 6, WallpaperIsRemovableRole = Qt::UserRole + 7, WallpaperIsZombieRole = Qt::UserRole + 8, WallpaperImageRole = Qt::UserRole + 9, WallpaperPreviewRole = Qt::UserRole + 10, }; explicit DynamicWallpaperModel(QObject *parent = nullptr); ~DynamicWallpaperModel() override; QHash roleNames() const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Q_INVOKABLE int find(const QUrl &url) const; Q_INVOKABLE QModelIndex modelIndex(int index) const; public Q_SLOTS: void reload(); void purge(); void add(const QUrl &fileUrl); void scheduleRemove(const QModelIndex &index); void unscheduleRemove(const QModelIndex &index); void remove(const QModelIndex &index); private Q_SLOTS: void handleProberFinished(const QUrl &fileUrl); void handleProberFailed(const QUrl &fileUrl); Q_SIGNALS: void errorOccurred(const QString &text); private: friend class DynamicWallpaperModelPrivate; QScopedPointer d; }; plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperpreviewcache.cpp000066400000000000000000000043251375723327100303750ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "dynamicwallpaperpreviewcache.h" #include #include #include #include #include static QString cacheKey(const QString &fileName) { QCryptographicHash hash(QCryptographicHash::Sha1); hash.addData(QFile::encodeName(fileName.toUtf8())); return QString::fromLatin1(hash.result().toHex()) + QStringLiteral(".png"); } static QString cacheRoot() { QString cache = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation); return cache + QLatin1String("/kdynamicwallpaper/"); } static QString cacheFileName(const QString &fileName) { return cacheRoot() + cacheKey(fileName); } /*! * Loads the preview for a wallpaper with the specified \a fileName from the cache. * * If the cache has no preview for a wallpaper with the given \a fileName or the cached preview * image is outdated, this method will return a null QImage object. * * This function can be called from multiple threads simultaneously. */ QImage DynamicWallpaperPreviewCache::load(const QString &fileName) { QImage image(cacheFileName(fileName)); if (image.isNull()) return QImage(); const qint64 lastCreated = image.text(QStringLiteral("Preview:Timestamp")).toLongLong(); const qint64 lastModified = QFileInfo(fileName).lastModified().toSecsSinceEpoch(); if (lastModified > lastCreated) return QImage(); return image; } /*! * Stores the preview \a image for a wallpaper with the specified \a fileName in the cache. * * This function can be called from multiple threads simultaneously. */ void DynamicWallpaperPreviewCache::store(const QImage &image, const QString &fileName) { const QDir cache(cacheRoot()); if (!cache.exists()) cache.mkpath(QStringLiteral(".")); const qint64 modifiedTimestamp = QFileInfo(fileName).lastModified().toSecsSinceEpoch(); QImage scaled = image.scaled(512, 512, Qt::KeepAspectRatio, Qt::SmoothTransformation); scaled.setText(QStringLiteral("Preview:Timestamp"), QString::number(modifiedTimestamp)); scaled.save(cacheFileName(fileName)); } plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperpreviewcache.h000066400000000000000000000005111375723327100300330ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include class DynamicWallpaperPreviewCache { public: static QImage load(const QString &fileName); static void store(const QImage &image, const QString &fileName); }; plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperpreviewjob.cpp000066400000000000000000000152041375723327100301020ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "dynamicwallpaperpreviewjob.h" #include "dynamicwallpaperglobals.h" #include "dynamicwallpaperpreviewcache.h" #include #include #include #include #include #include /*! * \class DynamicWallpaperPreviewJob * \brief The DynamicWallpaperPreviewJob provides a convenient way for getting dynamic * wallpaper thumbnail images. * * The DynamicWallpaperPreviewJob allows to load or generate preview images for wallpapers * asynchronously. After a preview image has been generated, the finished() signal will be * emitted. * * If for whatever reason, the preview job wasn't able to successfully get a preview image, * the failed() signal will be emitted. * * After the finished() or the failed() signal has been emitted, the preview job object will * be destroyed automatically. */ class DynamicWallpaperPreviewJobPrivate { public: QFutureWatcher *watcher; }; static QRgb blend(QRgb a, QRgb b, qreal blendFactor) { const int alpha = qAlpha(a) * (1 - blendFactor) + qAlpha(b) * blendFactor; const int red = qRed(a) * (1 - blendFactor) + qRed(b) * blendFactor; const int blue = qBlue(a) * (1 - blendFactor) + qBlue(b)* blendFactor; const int green = qGreen(a) * (1 - blendFactor) + qGreen(b) * blendFactor; return qRgba(red, green, blue, alpha); } static QImage ensureArgb32(const QImage &image) { return image.convertToFormat(QImage::Format_ARGB32_Premultiplied); } static QImage blend(const QImage &dark, const QImage &light, qreal delta) { // Note that the dark and the light images may have different dimensions. const int width = std::max(dark.width(), light.width()); const int height = std::max(dark.height(), light.height()); const QImage a = ensureArgb32(dark.scaled(width, height)); const QImage b = ensureArgb32(light.scaled(width, height)); const QEasingCurve blendCurve(QEasingCurve::InOutQuad); const int blendFrom = std::floor(width * (1 - delta) / 2); const int blendTo = std::ceil(width * (1 + delta) / 2); QVector blendFactorTable(width); for (int i = 0; i < width; ++i) { const qreal progress = qreal(i - blendFrom) / (blendTo - blendFrom); blendFactorTable[i] = blendCurve.valueForProgress(progress); } QImage result(width, height, QImage::Format_ARGB32_Premultiplied); for (int i = 0; i < height; ++i) { const uint32_t *in0 = reinterpret_cast(a.scanLine(i)); const uint32_t *in1 = reinterpret_cast(b.scanLine(i)); uint32_t *out = reinterpret_cast(result.scanLine(i)); for (int j = 0; j < width; ++j) *(out++) = blend(*(in0++), *(in1++), blendFactorTable[j]); } return result; } /*! * \internal * * Returns the approximate solar elevation for the specified wallpaper \a metadata. */ static qreal scoreForMetaData(const KDynamicWallpaperMetaData &metadata) { if (metadata.fields() & KDynamicWallpaperMetaData::SolarElevationField) return metadata.solarElevation() / 90; return std::cos(M_PI * (2 * metadata.time() + 1)); } static bool score_compare(const KDynamicWallpaperMetaData &a, const KDynamicWallpaperMetaData &b) { return scoreForMetaData(a) < scoreForMetaData(b); } /*! * \internal * * Creates a preview image for the given wallpaper \a fileName with the specified \a size. * * Note that this function runs off the main thread. */ static DynamicWallpaperImageAsyncResult makePreview(const QString &fileName, const QSize &size) { QImage preview = DynamicWallpaperPreviewCache::load(fileName); if (preview.isNull()) { // The cache has no preview for the specified wallpaper yet, so generate one... KDynamicWallpaperReader reader(fileName); if (reader.error() != KDynamicWallpaperReader::NoError) return DynamicWallpaperImageAsyncResult(reader.errorString()); QVector metadata; for (int i = 0; i < reader.imageCount(); ++i) { const KDynamicWallpaperMetaData imageMetadata = reader.metaDataAt(i); if (imageMetadata.isValid()) metadata.append(imageMetadata); } if (metadata.isEmpty()) return DynamicWallpaperImageAsyncResult(i18n("Not a dynamic wallpaper")); auto dark = std::min_element(metadata.begin(), metadata.end(), score_compare); auto light = std::max_element(metadata.begin(), metadata.end(), score_compare); const QImage darkImage = reader.imageAt(std::distance(metadata.begin(), dark)); const QImage lightImage = reader.imageAt(std::distance(metadata.begin(), light)); preview = blend(darkImage, lightImage, 0.5); DynamicWallpaperPreviewCache::store(preview, fileName); } return DynamicWallpaperImageAsyncResult(preview.scaled(size, Qt::KeepAspectRatio)); } /*! * Constructs a DynamicWallpaperPreviewJob with the specified \a fileName and \a requestedSize. */ DynamicWallpaperPreviewJob::DynamicWallpaperPreviewJob(const QString &fileName, const QSize &requestedSize) : d(new DynamicWallpaperPreviewJobPrivate) { d->watcher = new QFutureWatcher(this); connect(d->watcher, &QFutureWatcher::finished, this, &DynamicWallpaperPreviewJob::handleFinished); d->watcher->setFuture(QtConcurrent::run(makePreview, fileName, requestedSize)); } /*! * Destructs the DynamicWallpaperPreviewJob object. */ DynamicWallpaperPreviewJob::~DynamicWallpaperPreviewJob() { } /*! * \fn void DynamicWallpaperPreviewJob::finished(const QImage &image) * * This signal is emitted when the preview job has successfully created a preview \a image. * * The DynamicWallpaperPreviewJob object will be destroyed after the finished() signal has * been emitted. */ /*! * \fn void DynamicWallpaperPreviewJob::failed(const QString &errorString) * * This signal is emitted to indicate that the preview job has failed to generate a preview * image. The specified \a errorString is the description of the error that occurred. * * The DynamicWallpaperPreviewJob object will be destroyed after the failed() signal has been * emitted. */ void DynamicWallpaperPreviewJob::handleFinished() { const DynamicWallpaperImageAsyncResult response = d->watcher->result(); if (response.errorString.isNull()) emit finished(response.image); else emit failed(response.errorString); deleteLater(); } plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperpreviewjob.h000066400000000000000000000012071375723327100275450ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include class DynamicWallpaperPreviewJobPrivate; class DynamicWallpaperPreviewJob : public QObject { Q_OBJECT public: DynamicWallpaperPreviewJob(const QString &fileName, const QSize &size); ~DynamicWallpaperPreviewJob() override; Q_SIGNALS: void finished(const QImage &image); void failed(const QString &errorString); private Q_SLOTS: void handleFinished(); private: QScopedPointer d; }; plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperpreviewprovider.cpp000066400000000000000000000036351375723327100311670ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "dynamicwallpaperpreviewprovider.h" #include "dynamicwallpaperpreviewjob.h" #include class AsyncImageResponse : public QQuickImageResponse { public: AsyncImageResponse(const QString &fileName, const QSize &requestedSize); QString errorString() const override; QQuickTextureFactory *textureFactory() const override; private Q_SLOTS: void handleFinished(const QImage &image); void handleFailed(const QString &errorString); private: QString m_errorString; QImage m_image; }; AsyncImageResponse::AsyncImageResponse(const QString &fileName, const QSize &requestedSize) { QSize desiredSize = requestedSize; if (desiredSize.isEmpty()) desiredSize = QSize(400, 250) * qApp->devicePixelRatio(); DynamicWallpaperPreviewJob *job = new DynamicWallpaperPreviewJob(fileName, desiredSize); connect(job, &DynamicWallpaperPreviewJob::finished, this, &AsyncImageResponse::handleFinished); connect(job, &DynamicWallpaperPreviewJob::failed, this, &AsyncImageResponse::handleFailed); } QString AsyncImageResponse::errorString() const { return m_errorString; } QQuickTextureFactory *AsyncImageResponse::textureFactory() const { return QQuickTextureFactory::textureFactoryForImage(m_image); } void AsyncImageResponse::handleFinished(const QImage &image) { m_image = image; emit finished(); } void AsyncImageResponse::handleFailed(const QString &errorString) { m_errorString = errorString; emit finished(); } static QString fileNameFromBase64(const QString &base64) { return QByteArray::fromBase64(base64.toUtf8()); } QQuickImageResponse *DynamicWallpaperPreviewProvider::requestImageResponse(const QString &id, const QSize &requestedSize) { return new AsyncImageResponse(fileNameFromBase64(id), requestedSize); } plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperpreviewprovider.h000066400000000000000000000005611375723327100306270ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include class DynamicWallpaperPreviewProvider : public QQuickAsyncImageProvider { public: QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override; }; plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperprober.cpp000066400000000000000000000025261375723327100272220ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "dynamicwallpaperprober.h" #include #include /*! * \class DynamicWallpaperProber * \brief The DynamicWallpaperProper class provides a convenient way to asynchronously * check whether the specified file url corresponds to a dynamic wallpaper. * * If the specified file url is not a dynamic wallpaper, the failed() signal is emitted; * otherwise the finished() signal is emitted. * * After either the failed() or the finished() signal has been emitted, the prober object * will be destroyed automatically. */ /*! * Constructs a dynamic wallpaper prober with the specified \a fileUrl and \a parent. */ DynamicWallpaperProber::DynamicWallpaperProber(const QUrl &fileUrl, QObject *parent) : QThread(parent) , m_fileUrl(fileUrl) { } /*! * Destructs the DynamicWallpaperProber object. */ DynamicWallpaperProber::~DynamicWallpaperProber() { wait(); } void DynamicWallpaperProber::run() { const KDynamicWallpaperReader reader(m_fileUrl.toLocalFile()); const KDynamicWallpaperMetaData metadata = reader.metaDataAt(0); if (metadata.isValid()) emit finished(m_fileUrl); else emit failed(m_fileUrl); deleteLater(); } plasma5-wallpapers-dynamic-3.3.6/src/declarative/dynamicwallpaperprober.h000066400000000000000000000010271375723327100266620ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include class DynamicWallpaperProber : public QThread { Q_OBJECT public: explicit DynamicWallpaperProber(const QUrl &fileUrl, QObject *parent = nullptr); ~DynamicWallpaperProber() override; protected: void run() override; Q_SIGNALS: void finished(const QUrl &fileUrl); void failed(const QUrl &fileUrl); private: QUrl m_fileUrl; }; plasma5-wallpapers-dynamic-3.3.6/src/declarative/qmldir000066400000000000000000000001271375723327100231560ustar00rootroot00000000000000module com.github.zzag.plasma.wallpapers.dynamic plugin plasma_wallpaper_dynamicplugin plasma5-wallpapers-dynamic-3.3.6/src/lib/000077500000000000000000000000001375723327100202265ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/lib/CMakeLists.txt000066400000000000000000000053401375723327100227700ustar00rootroot00000000000000add_definitions(-DTRANSLATION_DOMAIN=\"plasma_wallpaper_com.github.zzag.dynamic\") set(dynamicwallpaperlib_SOURCES kdynamicwallpapermetadata.cpp kdynamicwallpaperreader.cpp kdynamicwallpaperwriter.cpp ksunpath.cpp ksunposition.cpp ksystemclockmonitor.cpp ksystemclockmonitorengine.cpp ) if (CMAKE_SYSTEM_NAME MATCHES "Linux") set(dynamicwallpaperlib_SOURCES ksystemclockmonitorengine_linux.cpp ${dynamicwallpaperlib_SOURCES} ) endif() ecm_generate_headers(dynamicwallpaperlib_HEADERS HEADER_NAMES KDynamicWallpaperMetaData KDynamicWallpaperReader KDynamicWallpaperWriter KSunPath KSunPosition KSystemClockMonitor REQUIRED_HEADERS dynamicwallpaperlib_HEADERS ) qt5_add_resources(dynamicwallpaperlib_SOURCES resources.qrc) add_library(kdynamicwallpaper ${dynamicwallpaperlib_SOURCES}) add_library(KDynamicWallpaper::KDynamicWallpaper ALIAS kdynamicwallpaper) generate_export_header(kdynamicwallpaper BASE_NAME KDynamicWallpaper EXPORT_MACRO_NAME KDYNAMICWALLPAPER_EXPORT ) set_target_properties(kdynamicwallpaper PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} EXPORT_NAME KDynamicWallpaper ) target_include_directories(kdynamicwallpaper PUBLIC "$" "$" ) target_link_libraries(kdynamicwallpaper PUBLIC Qt5::Core Qt5::Gui Qt5::Positioning PRIVATE Qt5::Xml KF5::I18n libheif::libheif ) set(CMAKECONFIG_INSTALL_DIR ${KDE_INSTALL_LIBDIR}/cmake/KDynamicWallpaper) write_basic_config_version_file(${CMAKE_CURRENT_BINARY_DIR}/KDynamicWallpaperConfigVersion.cmake VERSION "${PROJECT_VERSION}" COMPATIBILITY AnyNewerVersion) configure_package_config_file(KDynamicWallpaperConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/KDynamicWallpaperConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}) install(TARGETS kdynamicwallpaper EXPORT KDynamicWallpaperTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/KDynamicWallpaperConfig.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR} COMPONENT Devel) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/KDynamicWallpaperConfigVersion.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR} COMPONENT Devel) install(EXPORT KDynamicWallpaperTargets NAMESPACE KDynamicWallpaper:: DESTINATION ${CMAKECONFIG_INSTALL_DIR} FILE KDynamicWallpaperTargets.cmake COMPONENT Devel) install(FILES ${dynamicwallpaperlib_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/kdynamicwallpaper_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KDynamicWallpaper COMPONENT Devel) plasma5-wallpapers-dynamic-3.3.6/src/lib/KDynamicWallpaperConfig.cmake.in000066400000000000000000000003731375723327100263350ustar00rootroot00000000000000@PACKAGE_INIT@ include(CMakeFindDependencyMacro) find_dependency(Qt5Core @QT_MIN_VERSION@) find_dependency(Qt5Gui @QT_MIN_VERSION@) find_dependency(Qt5Positioning @QT_MIN_VERSION@) include("${CMAKE_CURRENT_LIST_DIR}/KDynamicWallpaperTargets.cmake") plasma5-wallpapers-dynamic-3.3.6/src/lib/kdynamicwallpapermetadata.cpp000066400000000000000000000207041375723327100261450ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include "kdynamicwallpapermetadata.h" #include #include #include #include #include #include /*! * \class KDynamicWallpaperMetaData * \brief The KDynamicWallpaperMetaData class represents metadata associated with images * in the dynamic wallpaper. * * KDynamicWallpaperMetaData provides information about images in the dynamic wallpaper, * for example, solar position, etc. Some fields may not be specified. In order to check * whether the given field is set, test the corresponding bit in fields(). */ static QJsonValue crossFadeModeToJson(KDynamicWallpaperMetaData::CrossFadeMode crossFadeMode) { switch (crossFadeMode) { case KDynamicWallpaperMetaData::NoCrossFade: return QJsonValue(false); case KDynamicWallpaperMetaData::CrossFade: return QJsonValue(true); default: Q_UNREACHABLE(); } } static KDynamicWallpaperMetaData::CrossFadeMode crossFadeModeFromJson(const QJsonValue &value) { return value.toBool() ? KDynamicWallpaperMetaData::CrossFade : KDynamicWallpaperMetaData::NoCrossFade; } class KDynamicWallpaperMetaDataPrivate : public QSharedData { public: KDynamicWallpaperMetaDataPrivate(); KDynamicWallpaperMetaData::MetaDataFields presentFields; KDynamicWallpaperMetaData::CrossFadeMode crossFadeMode; qreal solarAzimuth; qreal solarElevation; qreal time; }; KDynamicWallpaperMetaDataPrivate::KDynamicWallpaperMetaDataPrivate() : crossFadeMode(KDynamicWallpaperMetaData::NoCrossFade) , solarAzimuth(0.0) , solarElevation(0.0) , time(0.0) { } /*! * Constructs an empty KDynamicWallpaperMetaData object. */ KDynamicWallpaperMetaData::KDynamicWallpaperMetaData() : d(new KDynamicWallpaperMetaDataPrivate) { } /*! * Constructs a copy of the KDynamicWallpaperMetaData object. */ KDynamicWallpaperMetaData::KDynamicWallpaperMetaData(const KDynamicWallpaperMetaData &other) : d(other.d) { } /*! * Destructs the KDynamicWallpaperMetaData object. */ KDynamicWallpaperMetaData::~KDynamicWallpaperMetaData() { } /*! * Assigns the value of \p other to a dynamic wallpaper metadata object. */ KDynamicWallpaperMetaData &KDynamicWallpaperMetaData::operator=(const KDynamicWallpaperMetaData &other) { d = other.d; return *this; } /*! * Returns a bitmask that indicates which fields are present in the metadata. */ KDynamicWallpaperMetaData::MetaDataFields KDynamicWallpaperMetaData::fields() const { return d->presentFields; } /*! * Returns \c true if the KDynamicWallpaperMetaData contains valid metadata; otherwise \c false. */ bool KDynamicWallpaperMetaData::isValid() const { const MetaDataFields requiredFields = TimeField; if ((d->presentFields & requiredFields) != requiredFields) return false; if (bool(d->presentFields & SolarAzimuthField) ^ bool(d->presentFields & SolarElevationField)) return false; if (d->time < 0 || d->time > 1) return false; return true; } /*! * Sets the value of the cross-fade mode field to \p mode. */ void KDynamicWallpaperMetaData::setCrossFadeMode(CrossFadeMode mode) { d->crossFadeMode = mode; d->presentFields |= CrossFadeField; } /*! * Returns the value of the cross-fade mode field in the dynamic wallpaper metadata. */ KDynamicWallpaperMetaData::CrossFadeMode KDynamicWallpaperMetaData::crossFadeMode() const { return d->crossFadeMode; } /*! * Sets the value of the time field to \p time. */ void KDynamicWallpaperMetaData::setTime(qreal time) { d->time = time; d->presentFields |= TimeField; } /*! * Returns the value of the time field in the dynamic wallpaper metadata. */ qreal KDynamicWallpaperMetaData::time() const { return d->time; } /*! * Sets the value of the solar elevation field to \p elevation. */ void KDynamicWallpaperMetaData::setSolarElevation(qreal elevation) { d->solarElevation = elevation; d->presentFields |= SolarElevationField; } /*! * Returns the value of solar elevation stored in the dynamic wallpaper metadata. * * Note that this method will return \c 0 if SolarElevationField is not set in fields(). */ qreal KDynamicWallpaperMetaData::solarElevation() const { return d->solarElevation; } /*! * Sets the value of the solar azimuth field to \p azimuth. */ void KDynamicWallpaperMetaData::setSolarAzimuth(qreal azimuth) { d->solarAzimuth = azimuth; d->presentFields |= SolarAzimuthField; } /*! * Returns the value of solar azimuth stored in the dynamic wallpaper metadata. * * Note that this method will return \c 0 if SolarAzimuthField is not set in fields(). */ qreal KDynamicWallpaperMetaData::solarAzimuth() const { return d->solarAzimuth; } /*! * Converts the KDynamicWallpaperMetaData to a UTF-8 encoded JSON document. * * This method returns an empty QByteArray if the metadata is invalid. */ QByteArray KDynamicWallpaperMetaData::toJson() const { if (!isValid()) return QByteArray(); QJsonObject rootObject; if (d->presentFields & CrossFadeField) rootObject[QLatin1String("CrossFade")] = crossFadeModeToJson(d->crossFadeMode); if (d->presentFields & SolarElevationField) rootObject[QLatin1String("Elevation")] = d->solarElevation; if (d->presentFields & SolarAzimuthField) rootObject[QLatin1String("Azimuth")] = d->solarAzimuth; rootObject[QLatin1String("Time")] = d->time; QJsonDocument document; document.setObject(rootObject); return document.toJson(QJsonDocument::Compact); } /*! * Converts the KDynamicWallpaperMetaData to a Base64 string. */ QByteArray KDynamicWallpaperMetaData::toBase64() const { return toJson().toBase64(); } /*! * Converts the KDynamicWallpaperMetaData to a Base64 string, and stores it in XMP metadata. */ QByteArray KDynamicWallpaperMetaData::toXmp() const { if (!isValid()) return QByteArray(); QFile xmpTemplateFile(QStringLiteral(":/kdynamicwallpaper/xmp/metadata.xml")); xmpTemplateFile.open(QFile::ReadOnly); QByteArray xmpMetaData = xmpTemplateFile.readAll(); xmpMetaData.replace(QByteArrayLiteral("base64"), toBase64()); return xmpMetaData; } /*! * Decodes a JSON-encoded KDynamicWallpaperMetaData object. */ KDynamicWallpaperMetaData KDynamicWallpaperMetaData::fromJson(const QByteArray &json) { KDynamicWallpaperMetaData metaData; const QJsonDocument document = QJsonDocument::fromJson(json); if (document.isNull()) return metaData; const QJsonObject rootObject = document.object(); if (rootObject.isEmpty()) return metaData; const QJsonValue crossFadeMode = rootObject[QLatin1String("CrossFade")]; if (crossFadeMode.isBool()) metaData.setCrossFadeMode(crossFadeModeFromJson(crossFadeMode)); const QJsonValue time = rootObject[QLatin1String("Time")]; if (time.isDouble()) metaData.setTime(time.toDouble()); const QJsonValue solarElevation = rootObject[QLatin1String("Elevation")]; if (solarElevation.isDouble()) metaData.setSolarElevation(solarElevation.toDouble()); const QJsonValue solarAzimuth = rootObject[QLatin1String("Azimuth")]; if (solarAzimuth.isDouble()) metaData.setSolarAzimuth(solarAzimuth.toDouble()); return metaData; } /*! * Decodes a Base64-encoded KDynamicWallpaperMetaData object. */ KDynamicWallpaperMetaData KDynamicWallpaperMetaData::fromBase64(const QByteArray &base64) { return fromJson(QByteArray::fromBase64(base64)); } /*! * Creates a KDynamicWallpaperMetaData object from the specified \p xmp metadata. */ KDynamicWallpaperMetaData KDynamicWallpaperMetaData::fromXmp(const QByteArray &xmp) { QDomDocument xmpDocument; xmpDocument.setContent(xmp); if (xmpDocument.isNull()) return KDynamicWallpaperMetaData(); const QString attributeName = QStringLiteral("plasma:DynamicWallpaper"); const QDomNodeList descriptionNodes = xmpDocument.elementsByTagName(QStringLiteral("rdf:Description")); for (int i = 0; i < descriptionNodes.count(); ++i) { QDomElement descriptionNode = descriptionNodes.at(i).toElement(); const QString encodedMetaData = descriptionNode.attribute(attributeName); if (encodedMetaData.isEmpty()) continue; KDynamicWallpaperMetaData metaData = fromBase64(encodedMetaData.toUtf8()); if (metaData.isValid()) return metaData; } return KDynamicWallpaperMetaData(); } plasma5-wallpapers-dynamic-3.3.6/src/lib/kdynamicwallpapermetadata.h000066400000000000000000000032551375723327100256140ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include "kdynamicwallpaper_export.h" #include #include class KDynamicWallpaperMetaDataPrivate; class KDYNAMICWALLPAPER_EXPORT KDynamicWallpaperMetaData { public: enum CrossFadeMode { NoCrossFade, CrossFade, }; enum MetaDataField { CrossFadeField = 1 << 0, TimeField = 1 << 1, SolarAzimuthField = 1 << 2, SolarElevationField = 1 << 3, }; Q_DECLARE_FLAGS(MetaDataFields, MetaDataField) KDynamicWallpaperMetaData(); KDynamicWallpaperMetaData(const KDynamicWallpaperMetaData &other); ~KDynamicWallpaperMetaData(); KDynamicWallpaperMetaData &operator=(const KDynamicWallpaperMetaData &other); MetaDataFields fields() const; bool isValid() const; void setCrossFadeMode(CrossFadeMode mode); CrossFadeMode crossFadeMode() const; void setTime(qreal time); qreal time() const; void setSolarElevation(qreal elevation); qreal solarElevation() const; void setSolarAzimuth(qreal azimuth); qreal solarAzimuth() const; QByteArray toJson() const; QByteArray toBase64() const; QByteArray toXmp() const; static KDynamicWallpaperMetaData fromJson(const QByteArray &json); static KDynamicWallpaperMetaData fromBase64(const QByteArray &base64); static KDynamicWallpaperMetaData fromXmp(const QByteArray &xmp); private: QSharedDataPointer d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(KDynamicWallpaperMetaData::MetaDataFields) plasma5-wallpapers-dynamic-3.3.6/src/lib/kdynamicwallpaperreader.cpp000066400000000000000000000324061375723327100256310ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include "kdynamicwallpaperreader.h" #include "kdynamicwallpapermetadata.h" #include #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) #include #endif #include #include #include /*! * \class KDynamicWallpaperReader * \brief The KDynamicWallpaperReader class provides a convenient way for reading dynamic * wallpapers. * * If any error occurs when reading an image or metadata, imageAt() or metaDataAt() will * return a null QImage or an invalid KDynamicWallpaperMetaData, respectively. You can * then call error() to find out the type of the error that occurred, or errorString() to * get a human readable description of what went wrong. */ static QIODevice *deviceFromUserData(void *userData) { return static_cast(userData); } static int64_t readerGetPositionCallback(void *userData) { const QIODevice *device = deviceFromUserData(userData); return device->pos(); } static int readerReadCallback(void *data, size_t size, void *userData) { QIODevice *device = deviceFromUserData(userData); const qint64 readCount = device->read(static_cast(data), size); if (readCount == -1) return 1; return size_t(readCount) != size; // 0 indicates success } static int readerSeekCallback(int64_t position, void *userData) { QIODevice *device = deviceFromUserData(userData); const bool ok = device->seek(position); return !ok; // 0 indicates success } static heif_reader_grow_status readerWaitForFileSizeCallback(int64_t size, void *userData) { const QIODevice *device = deviceFromUserData(userData); if (device->size() < size) return heif_reader_grow_status_size_beyond_eof; return heif_reader_grow_status_size_reached; } static const heif_reader s_reader = { /* .reader_api_version = */ 1, /* .get_position = */ readerGetPositionCallback, /* .read = */ readerReadCallback, /* .seek = */ readerSeekCallback, /* .wait_for_file_size = */ readerWaitForFileSizeCallback, }; class KDynamicWallpaperReaderPrivate { public: KDynamicWallpaperReaderPrivate(); bool ensureOpen(); bool open(); void close(); bool checkImageIndex(int imageIndex) const; KDynamicWallpaperMetaData metaDataAt(int imageIndex); QImage imageAt(int imageIndex); QIODevice *device; heif_context *context; KDynamicWallpaperReader::WallpaperReaderError wallpaperReaderError; QString errorString; QVector images; bool isDeviceForeign; }; KDynamicWallpaperReaderPrivate::KDynamicWallpaperReaderPrivate() : device(nullptr) , context(nullptr) , wallpaperReaderError(KDynamicWallpaperReader::NoError) , isDeviceForeign(false) { } bool KDynamicWallpaperReaderPrivate::ensureOpen() { if (context) return true; return open(); } bool KDynamicWallpaperReaderPrivate::open() { if (!device) { wallpaperReaderError = KDynamicWallpaperReader::DeviceError; errorString = i18n("No assigned device"); return false; } if (device->isOpen()) { if (!(device->openMode() & QIODevice::ReadOnly)) { wallpaperReaderError = KDynamicWallpaperReader::DeviceError; errorString = i18n("The device is not open for reading"); return false; } } else { if (!device->open(QIODevice::ReadOnly)) { wallpaperReaderError = KDynamicWallpaperReader::DeviceError; errorString = device->errorString(); return false; } } context = heif_context_alloc(); if (!context) { wallpaperReaderError = KDynamicWallpaperReader::UnknownError; errorString = i18n("Failed to allocate HEIF context"); return false; } const heif_error error = heif_context_read_from_reader(context, &s_reader, device, nullptr); if (error.code != heif_error_Ok) { wallpaperReaderError = KDynamicWallpaperReader::InvalidDataError; errorString = i18n("Invalid HEIF file: %1", error.message); heif_context_free(context); context = nullptr; return false; } const int imageCount = heif_context_get_number_of_top_level_images(context); images.resize(imageCount); heif_context_get_list_of_top_level_image_IDs(context, images.data(), imageCount); return true; } void KDynamicWallpaperReaderPrivate::close() { if (context) heif_context_free(context); if (!isDeviceForeign) device->deleteLater(); context = nullptr; device = nullptr; isDeviceForeign = false; images.clear(); } bool KDynamicWallpaperReaderPrivate::checkImageIndex(int imageIndex) const { return imageIndex >= 0 && imageIndex < images.count(); } KDynamicWallpaperMetaData KDynamicWallpaperReaderPrivate::metaDataAt(int imageIndex) { heif_image_handle *handle; heif_error error = heif_context_get_image_handle(context, images[imageIndex], &handle); if (error.code != heif_error_Ok) { wallpaperReaderError = KDynamicWallpaperReader::UnknownError; errorString = i18n("Failed to get image handle: %1", error.message); return KDynamicWallpaperMetaData(); } const char *filter = "mime"; const int blockCount = heif_image_handle_get_number_of_metadata_blocks(handle, filter); if (!blockCount) { heif_image_handle_release(handle); return KDynamicWallpaperMetaData(); } QVector blockIds(blockCount); heif_image_handle_get_list_of_metadata_block_IDs(handle, filter, blockIds.data(), blockCount); KDynamicWallpaperMetaData metaData; for (const heif_item_id &blockId : blockIds) { const char *contentType = heif_image_handle_get_metadata_content_type(handle, blockId); if (contentType != QByteArrayLiteral("application/rdf+xml")) continue; const size_t blockSize = heif_image_handle_get_metadata_size(handle, blockId); QByteArray block(blockSize, 0); const heif_error error = heif_image_handle_get_metadata(handle, blockId, block.data()); if (error.code != heif_error_Ok) continue; metaData = KDynamicWallpaperMetaData::fromXmp(block); if (metaData.isValid()) break; } heif_image_handle_release(handle); return metaData; } #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) static QColorSpace colorProfileForImage(heif_image *image) { QColorSpace colorSpace; switch (heif_image_get_color_profile_type(image)) { case heif_color_profile_type_not_present: case heif_color_profile_type_nclx: break; case heif_color_profile_type_prof: case heif_color_profile_type_rICC: { const size_t iccProfileSize = heif_image_get_raw_color_profile_size(image); QByteArray iccProfile(iccProfileSize, 0); const heif_error error = heif_image_get_raw_color_profile(image, iccProfile.data()); if (error.code != heif_error_Ok) colorSpace = QColorSpace::fromIccProfile(iccProfile); break; } } return colorSpace; } #endif QImage KDynamicWallpaperReaderPrivate::imageAt(int imageIndex) { heif_image *image; heif_image_handle *handle; heif_error error; heif_chroma chroma; error = heif_context_get_image_handle(context, images[imageIndex], &handle); if (error.code != heif_error_Ok) { wallpaperReaderError = KDynamicWallpaperReader::UnknownError; errorString = i18n("Failed to get image handle: %1", error.message); return QImage(); } if (heif_image_handle_has_alpha_channel(handle)) chroma = heif_chroma_interleaved_RGBA; else chroma = heif_chroma_interleaved_RGB; error = heif_decode_image(handle, &image, heif_colorspace_RGB, chroma, nullptr); if (error.code != heif_error_Ok) { wallpaperReaderError = KDynamicWallpaperReader::UnknownError; errorString = i18n("Failed to decode image: %1", error.message); heif_image_handle_release(handle); return QImage(); } int stride; const uint8_t *data = heif_image_get_plane_readonly(image, heif_channel_interleaved, &stride); const int width = heif_image_get_width(image, heif_channel_interleaved); const int height = heif_image_get_height(image, heif_channel_interleaved); QImage::Format format = QImage::Format_RGB888; if (chroma == heif_chroma_interleaved_RGBA) format = QImage::Format_RGBA8888; auto cleanupFunc = [](void *data) { heif_image_release(static_cast(data)); }; QImage decodedImage(data, width, height, stride, format, cleanupFunc, image); #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) const QColorSpace colorSpace = colorProfileForImage(image); if (colorSpace.isValid()) decodedImage.setColorSpace(colorSpace); #endif heif_image_handle_release(handle); // heif_image will be destroyed by QImage return decodedImage; } /*! * Constructs an empty KDynamicWallpaperReader object. */ KDynamicWallpaperReader::KDynamicWallpaperReader() : d(new KDynamicWallpaperReaderPrivate) { } /*! * Constructs the KDynamicWallpaperReader with the device \p device. */ KDynamicWallpaperReader::KDynamicWallpaperReader(QIODevice *device) : d(new KDynamicWallpaperReaderPrivate) { setDevice(device); } /*! * Constructs the KDynamicWallpaperReader with the file name \p fileName. */ KDynamicWallpaperReader::KDynamicWallpaperReader(const QString &fileName) : d(new KDynamicWallpaperReaderPrivate) { setFileName(fileName); } /*! * Destructs the KDynamicWallpaperReader object. */ KDynamicWallpaperReader::~KDynamicWallpaperReader() { if (d->device) d->close(); } /*! * Sets the device of the reader to the specified \p device. * * If the device is not already open, KDynamicWallpaperReader will attempt to open the device * in QIODevice::ReadOnly mode by calling open(). */ void KDynamicWallpaperReader::setDevice(QIODevice *device) { if (d->device) d->close(); d->device = device; d->isDeviceForeign = true; } /*! * Returns the device assigned to the reader, or \c nullptr if no device has been assigned. */ QIODevice *KDynamicWallpaperReader::device() const { return d->device; } /*! * Sets the file name of the file to be read to \p fileName. Internally, KDynamicWallpaperReader * will create a QFile object and open it in QIODevice::ReadOnly mode, and use it when reading * dynamic wallpapers. */ void KDynamicWallpaperReader::setFileName(const QString &fileName) { if (d->device) d->close(); d->device = new QFile(fileName); d->isDeviceForeign = false; } /*! * If the currently assigned device is a QFile, or if setFileName() has been called, this * function returns the name of the file KDynamicWallpaperReader reads from; otherwise an empty * QString object is returned. */ QString KDynamicWallpaperReader::fileName() const { const QFile *file = qobject_cast(d->device); return file ? file->fileName() : QString(); } /*! * Returns the total number of images in the dynamic wallpaper. */ int KDynamicWallpaperReader::imageCount() const { if (!d->ensureOpen()) return 0; return d->images.count(); } /*! * Returns the KDynamicWallpaperMetaData object associated with image \p imageIndex. * * This method will return an invalid KDynamicWallpaperMetaData object if image \p imageIndex * has no metadata associated with it or if \p imageIndex is outside of the valid range. */ KDynamicWallpaperMetaData KDynamicWallpaperReader::metaDataAt(int imageIndex) const { if (!d->ensureOpen() || !d->checkImageIndex(imageIndex)) return KDynamicWallpaperMetaData(); return d->metaDataAt(imageIndex); } /*! * Returns the image with the specified index \p imageIndex. * * This method will return a null QImage object if \p imageIndex is outside of the valid range. */ QImage KDynamicWallpaperReader::imageAt(int imageIndex) const { if (!d->ensureOpen() || !d->checkImageIndex(imageIndex)) return QImage(); return d->imageAt(imageIndex); } /*! * Returns the type of the last error that occurred. */ KDynamicWallpaperReader::WallpaperReaderError KDynamicWallpaperReader::error() const { return d->wallpaperReaderError; } /*! * Returns the human readable description of the last error that occurred. */ QString KDynamicWallpaperReader::errorString() const { if (d->wallpaperReaderError == NoError) return i18n("No error"); return d->errorString; } /*! * Returns \c true if a dynamic wallpaper can be read for the device; otherwise \c false. */ bool KDynamicWallpaperReader::canRead(QIODevice *device) { if (device->isSequential()) return false; const QByteArray header = device->peek(12); if (header.size() != 12) return false; const uint8_t *data = reinterpret_cast(header.data()); const char *mime = heif_get_file_mime_type(data, header.size()); if (qstrcmp(mime, "image/heic") && qstrcmp(mime, "image/heif")) return false; return true; } /*! * Returns \c true if a dynamic wallpaper can be read for the file name; otherwise \c false. */ bool KDynamicWallpaperReader::canRead(const QString &fileName) { QFile file(fileName); if (!file.open(QFile::ReadOnly)) return false; return canRead(&file); } plasma5-wallpapers-dynamic-3.3.6/src/lib/kdynamicwallpaperreader.h000066400000000000000000000022561375723327100252760ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include "kdynamicwallpaper_export.h" #include class KDynamicWallpaperMetaData; class KDynamicWallpaperReaderPrivate; class KDYNAMICWALLPAPER_EXPORT KDynamicWallpaperReader { public: enum WallpaperReaderError { NoError, DeviceError, InvalidDataError, NoMetaDataError, UnknownError, }; KDynamicWallpaperReader(); explicit KDynamicWallpaperReader(QIODevice *device); explicit KDynamicWallpaperReader(const QString &fileName); ~KDynamicWallpaperReader(); void setDevice(QIODevice *device); QIODevice *device() const; void setFileName(const QString &fileName); QString fileName() const; int imageCount() const; KDynamicWallpaperMetaData metaDataAt(int imageIndex) const; QImage imageAt(int imageIndex) const; WallpaperReaderError error() const; QString errorString() const; static bool canRead(QIODevice *device); static bool canRead(const QString &fileName); private: QScopedPointer d; }; plasma5-wallpapers-dynamic-3.3.6/src/lib/kdynamicwallpaperwriter.cpp000066400000000000000000000334431375723327100257050ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include "kdynamicwallpaperwriter.h" #include "kdynamicwallpapermetadata.h" #include #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) #include #else #include #endif #include #include #include /*! * \class KDynamicWallpaperWriter * \brief The KDynamicWallpaperWriter class provides a convenient way for writing dynamic * wallpapers. * * If any error occurs when writing an image, write() will return false. You can then call * error() to find the type of the error that occurred, or errorString() to get a human * readable description of what went wrong. */ static heif_error writerWriteCallback(heif_context *, const void *data, size_t size, void *userData) { QIODevice *device = static_cast(userData); const qint64 writtenCount = device->write(static_cast(data), size); if (writtenCount == -1) return { heif_error_Encoding_error, heif_suberror_Cannot_write_output_data, nullptr }; if (size_t(writtenCount) != size) return { heif_error_Encoding_error, heif_suberror_Cannot_write_output_data, nullptr }; return { heif_error_Ok, heif_suberror_Unspecified, "ok" }; } static heif_writer s_writer { /* .writer_api_version = */ 1, /* .write = */ writerWriteCallback, }; class KDynamicWallpaperWriterPrivate { public: KDynamicWallpaperWriterPrivate(); bool begin(); void end(); bool write(const QImage &image, const KDynamicWallpaperMetaData &metaData, KDynamicWallpaperWriter::WriteOptions options); QIODevice *device; heif_context *context; heif_encoder *encoder; KDynamicWallpaperWriter::WallpaperWriterError wallpaperWriterError; QString errorString; KDynamicWallpaperWriter::Codec codec; int quality; bool isLossless; bool isActive; bool isDeviceForeign; }; KDynamicWallpaperWriterPrivate::KDynamicWallpaperWriterPrivate() : device(nullptr) , context(nullptr) , encoder(nullptr) , wallpaperWriterError(KDynamicWallpaperWriter::NoError) , codec(KDynamicWallpaperWriter::HEVC) , quality(100) , isLossless(true) , isActive(false) , isDeviceForeign(false) { } static heif_compression_format formatForCodec(KDynamicWallpaperWriter::Codec codec) { switch (codec) { case KDynamicWallpaperWriter::HEVC: return heif_compression_HEVC; case KDynamicWallpaperWriter::AVC: return heif_compression_AVC; default: Q_UNREACHABLE(); } } bool KDynamicWallpaperWriterPrivate::begin() { if (device->isOpen()) { if (!(device->openMode() & QIODevice::WriteOnly)) { wallpaperWriterError = KDynamicWallpaperWriter::DeviceError; errorString = i18n("The device is not open for writing"); return false; } } else { if (!device->open(QIODevice::WriteOnly)) { wallpaperWriterError = KDynamicWallpaperWriter::DeviceError; errorString = device->errorString(); return false; } } context = heif_context_alloc(); if (!context) { wallpaperWriterError = KDynamicWallpaperWriter::UnknownError; errorString = i18n("Failed to allocate HEIF context"); return false; } const heif_compression_format compressionFormat = formatForCodec(codec); heif_error error = heif_context_get_encoder_for_format(context, compressionFormat, &encoder); if (error.code != heif_error_Ok) { wallpaperWriterError = KDynamicWallpaperWriter::EncoderError; errorString = i18n("No suitable encoder"); goto error_context; } error = heif_encoder_set_lossless(encoder, isLossless); if (error.code != heif_error_Ok) { wallpaperWriterError = KDynamicWallpaperWriter::EncoderError; errorString = i18n("Failed to set the lossless parameter: %1", error.message); goto error_encoder; } error = heif_encoder_set_lossy_quality(encoder, quality); if (error.code != heif_error_Ok) { wallpaperWriterError = KDynamicWallpaperWriter::EncoderError; errorString = i18n("Failed to set the quality parameter: %1", error.message); goto error_encoder; } return true; // Unfortunately, there's no any sane way to wrap heif_encoder or heif_context into a smart // pointer. One could argue that we could use a deleter or something in order to automatically // release resources, but I don't find this argument convincing because we'll end up with a // bunch of raw and "guarded" pointers, which isn't better than just using the goto keyword // if you ask me. I know it's terrible, but that's the most sanest choice. error_encoder: heif_encoder_release(encoder); encoder = nullptr; error_context: heif_context_free(context); context = nullptr; return false; } void KDynamicWallpaperWriterPrivate::end() { // It appears like libheif can't handle incremental writes, so everything must be written // when KDynamicWallpaperWriter::end() is called. For the sake of keeping the amount of // used memory as low as possible, it would be really nice if we could flush encoded data // to the QIODevice in write() rather than here. heif_context_write(context, &s_writer, device); if (!isDeviceForeign) device->close(); if (encoder) heif_encoder_release(encoder); if (context) heif_context_free(context); if (!isDeviceForeign) delete device; context = nullptr; encoder = nullptr; device = nullptr; } static QImage convertToCompatibleFormat(const QImage &image) { if (image.format() == QImage::Format_RGB888 || image.format() == QImage::Format_RGBA8888) return image; if (image.hasAlphaChannel()) return image.convertToFormat(QImage::Format_RGBA8888); return image.convertToFormat(QImage::Format_RGB888); } bool KDynamicWallpaperWriterPrivate::write(const QImage &image, const KDynamicWallpaperMetaData &metaData, KDynamicWallpaperWriter::WriteOptions options) { const QImage convertedImage = convertToCompatibleFormat(image); const int width = convertedImage.width(); const int height = convertedImage.height(); const int depth = convertedImage.depth(); heif_chroma chroma; heif_image_handle *handle; heif_image *heifImage; int stride; uint8_t *data; QByteArray xmpMetaData; if (convertedImage.hasAlphaChannel()) chroma = heif_chroma_interleaved_RGBA; else chroma = heif_chroma_interleaved_RGB; heif_error error = heif_image_create(width, height, heif_colorspace_RGB, chroma, &heifImage); if (error.code != heif_error_Ok) { wallpaperWriterError = KDynamicWallpaperWriter::UnknownError; errorString = i18n("Failed to create a heif_image: %1", error.message); return false; } error = heif_image_add_plane(heifImage, heif_channel_interleaved, width, height, depth); if (error.code != heif_error_Ok) { wallpaperWriterError = KDynamicWallpaperWriter::UnknownError; errorString = i18n("Failed to add a color plane: %1", error.message); goto error_image; } if (options & KDynamicWallpaperWriter::PreserveColorProfile) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) const QByteArray iccProfile = image.colorSpace().iccProfile(); error = heif_image_set_raw_color_profile(heifImage, "prof", iccProfile.data(), iccProfile.size()); if (error.code != heif_error_Ok) { wallpaperWriterError = KDynamicWallpaperWriter::EncoderError; errorString = i18n("Failed to set ICC profile: %1", error.message); goto error_image; } #else qWarning() << "You need at least Qt 5.14 to preserve color profiles"; #endif } data = heif_image_get_plane(heifImage, heif_channel_interleaved, &stride); for (int i = 0; i < height; ++i) { const uint8_t *source = reinterpret_cast(convertedImage.scanLine(i)); uint8_t *target = data + stride * i; std::copy_n(source, convertedImage.bytesPerLine(), target); } error = heif_context_encode_image(context, heifImage, encoder, nullptr, &handle); if (error.code != heif_error_Ok) { wallpaperWriterError = KDynamicWallpaperWriter::UnknownError; errorString = i18n("Failed to encode image: %1", error.message); goto error_image; } xmpMetaData = metaData.toXmp(); error = heif_context_add_XMP_metadata(context, handle, xmpMetaData.data(), xmpMetaData.size()); if (error.code != heif_error_Ok) { wallpaperWriterError = KDynamicWallpaperWriter::UnknownError; errorString = i18n("Failed to add XMP metadata: %1", error.message); goto error_handle; } if (options & KDynamicWallpaperWriter::Primary) { error = heif_context_set_primary_image(context, handle); if (error.code != heif_error_Ok) { wallpaperWriterError = KDynamicWallpaperWriter::UnknownError; errorString = i18n("Failed to set the primary image: %1", error.message); goto error_handle; } } heif_image_handle_release(handle); heif_image_release(heifImage); return true; // Unfortunately, there's no any sane way to wrap heif_image_handle or heif_image into a smart // pointer. One could argue that we could use a deleter or something in order to automatically // release resources, but I don't find this argument convincing because we'll end up with a // bunch of raw and "guarded" pointers, which isn't better than just using the goto keyword // if you ask me. I know it's terrible, but that's the most sanest choice. error_handle: heif_image_handle_release(handle); error_image: heif_image_release(heifImage); return false; } /*! * Constructs an empty KDynamicWallpaperWriter object. */ KDynamicWallpaperWriter::KDynamicWallpaperWriter() : d(new KDynamicWallpaperWriterPrivate) { if (d->isActive) d->end(); } /*! * Destructs the KDynamicWallpaperWriter object. */ KDynamicWallpaperWriter::~KDynamicWallpaperWriter() { if (d->isActive) d->end(); } /*! * Sets the quality parameter of the encoder to \p quality. */ void KDynamicWallpaperWriter::setQuality(int quality) { d->quality = quality; } /*! * Returns the quality parameter of the encoder. */ int KDynamicWallpaperWriter::quality() const { return d->quality; } /*! * Sets the lossless parameter of the encoder to \p lossless. */ void KDynamicWallpaperWriter::setLossless(bool lossless) { d->isLossless = lossless; } /*! * Returns the lossless parameter of the encoder. */ bool KDynamicWallpaperWriter::isLossless() const { return d->isLossless; } /*! * Sets the codec for this writer. The codec is used for encoding any data that is written. */ void KDynamicWallpaperWriter::setCodec(Codec codec) { d->codec = codec; } /*! * Returns the code that is assigned to this writer. */ KDynamicWallpaperWriter::Codec KDynamicWallpaperWriter::codec() const { return d->codec; } /*! * Begins a write sequence to the device and returns \c true if successful; otherwise \c false is * returned. You must call this method before calling write() method. * * If the device is not already open, KDynamicWallpaperWriter will attempt to open the device * in QIODevice::WriteOnly mode by calling open(). */ bool KDynamicWallpaperWriter::begin(QIODevice *device) { if (d->isActive) return false; d->device = device; d->isDeviceForeign = true; d->isActive = d->begin(); return d->isActive; } /*! * Begins a write sequence to the file \p fileName and returns \c true if successful; otherwise * \c false is returned. Internally, KDynamicWallpaperWriter will create a QFile object and open * it in QIODevice::WriteOnly mode, and use it when writing dynamic wallpapers. * * You must call this method before calling write() method. */ bool KDynamicWallpaperWriter::begin(const QString &fileName) { if (d->isActive) return false; d->device = new QFile(fileName); d->isDeviceForeign = false; d->isActive = d->begin(); if (!d->isActive) { delete d->device; d->device = nullptr; } return d->isActive; } /*! * Finalizes the write sequence to the device. */ void KDynamicWallpaperWriter::end() { if (!d->isActive) return; d->isActive = false; d->end(); } /*! * Returns \c true if begin() has been called and end() has not been called yet; otherwise \c false * is returned. */ bool KDynamicWallpaperWriter::isActive() const { return d->isActive; } bool KDynamicWallpaperWriter::write(const QImage &image, const KDynamicWallpaperMetaData &metaData, WriteOptions options) { if (!d->isActive) return false; return d->write(image, metaData, options); } /*! * Returns the type of the last error that occurred. */ KDynamicWallpaperWriter::WallpaperWriterError KDynamicWallpaperWriter::error() const { return d->wallpaperWriterError; } /*! * Returns the human readable description of the last error that occurred. */ QString KDynamicWallpaperWriter::errorString() const { if (d->wallpaperWriterError == NoError) return i18n("No error"); return d->errorString; } /*! * Returns \c true if a dynamic wallpaper can be written to the specified @device; otherwise \c false * is returned. */ bool KDynamicWallpaperWriter::canWrite(QIODevice *device) { return device->isWritable(); } /*! * Returns \c true if a dynamic wallpaper can be written to a file with the specified file name * \p fileName; otherwise \c false is returned. */ bool KDynamicWallpaperWriter::canWrite(const QString &fileName) { QFile file(fileName); return file.isWritable(); } plasma5-wallpapers-dynamic-3.3.6/src/lib/kdynamicwallpaperwriter.h000066400000000000000000000027141375723327100253470ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include "kdynamicwallpaper_export.h" #include class KDynamicWallpaperMetaData; class KDynamicWallpaperWriterPrivate; class KDYNAMICWALLPAPER_EXPORT KDynamicWallpaperWriter { public: enum WallpaperWriterError { NoError, DeviceError, EncoderError, UnknownError, }; enum Codec { HEVC, AVC, }; enum WriteOption { PreserveColorProfile = 1 << 0, Primary = 1 << 1, }; Q_DECLARE_FLAGS(WriteOptions, WriteOption) KDynamicWallpaperWriter(); ~KDynamicWallpaperWriter(); void setQuality(int quality); int quality() const; void setLossless(bool lossless); bool isLossless() const; void setCodec(Codec codec); Codec codec() const; bool begin(QIODevice *device); bool begin(const QString &fileName); void end(); bool isActive() const; bool write(const QImage &image, const KDynamicWallpaperMetaData &metaData, WriteOptions options = WriteOptions()); WallpaperWriterError error() const; QString errorString() const; static bool canWrite(QIODevice *device); static bool canWrite(const QString &fileName); private: QScopedPointer d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(KDynamicWallpaperWriter::WriteOptions) plasma5-wallpapers-dynamic-3.3.6/src/lib/ksunpath.cpp000066400000000000000000000106321375723327100225710ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include "ksunpath.h" #include "ksunposition.h" #include #include /*! * \class KSunPath * \brief The KSunPath class represents a path of the Sun at the given time and location. */ static QVector3D computeNormal(const QVector3D ¢er, const QVector3D &v1, const QVector3D &v2) { const QVector3D cross = QVector3D::crossProduct(v1 - center, v2 - center); return cross.normalized(); } static QVector3D positionToVector(const KSunPosition &position) { return position.toVector(); } /*! * Creates a path of the Sun at the specified date and location. */ KSunPath KSunPath::create(const QDateTime &dateTime, const QGeoCoordinate &location) { // I bet there is a nice formula to determine the normal and the center of // the sun path. Sampling the position of the Sun is not that bad, however // having the computed results as accurate as possible is still something we // have to strive for. const int sampleCount = 24; const QDateTime utcDateTime = dateTime.toUTC(); const QDate utcDate = utcDateTime.date(); QVector positions; positions.reserve(sampleCount); for (int i = 0; i < sampleCount; ++i) { const QTime utcTime(i, 0); const QDateTime sampleDataTime(utcDate, utcTime, Qt::UTC); const KSunPosition position(sampleDataTime, location); if (!position.isValid()) return KSunPath(); positions << position; } QVector samples; samples.reserve(sampleCount); std::transform(positions.constBegin(), positions.constEnd(), std::back_inserter(samples), positionToVector); QVector3D center = std::accumulate(samples.constBegin(), samples.constEnd(), QVector3D()); center /= sampleCount; float radius = 0; for (const QVector3D &sample : samples) radius += (sample - center).length(); radius /= sampleCount; QVector3D normal; for (int i = 1; i < samples.count(); ++i) { const QVector3D v1 = samples.at(i - 1); const QVector3D v2 = samples.at(i); normal += computeNormal(center, v1, v2); } normal.normalize(); return KSunPath(center, normal, radius); } /*! * Constructs a KSunPath with \p center, \p normal, and \p radius. */ KSunPath::KSunPath(const QVector3D ¢er, const QVector3D &normal, float radius) : m_center(center) , m_normal(normal) , m_radius(radius) { } /*! * Returns \c true if the path of the Sun is valid; otherwise returns \c false. */ bool KSunPath::isValid() const { return !qFuzzyIsNull(m_normal.x()); } /*! * Returns the coordinates of the center of this path of the Sun. */ QVector3D KSunPath::center() const { return m_center; } /*! * Returns the normal of the plane that contains this path of the Sun. */ QVector3D KSunPath::normal() const { return m_normal; } /*! * Projects the specified KSunPosition onto this KSunPath. */ QVector3D KSunPath::project(const KSunPosition &position) const { if (!isValid()) return QVector3D(); const QVector3D axis = QVector3D(1, 0, 0); const QVector3D origin = QVector3D(0, 0, 0); const QVector3D normal = computeNormal(origin, axis, position.toVector()); if (normal.isNull()) return QVector3D(); const QVector3D direction = QVector3D::crossProduct(m_normal, normal).normalized(); const QVector3D point = QVector3D::crossProduct(normal, direction) * QVector3D::dotProduct(m_normal, m_center) + QVector3D::crossProduct(direction, m_normal) * QVector3D::dotProduct(normal, origin); const QVector3D delta = point - m_center; const float dot = QVector3D::dotProduct(direction, delta); const float discriminator = dot * dot - delta.lengthSquared() + m_radius * m_radius; if (discriminator < 0) return QVector3D(); if (qFuzzyIsNull(discriminator)) return point - dot * direction; const QVector3D a = point + direction * (-dot - std::sqrt(discriminator)); const QVector3D b = point + direction * (-dot + std::sqrt(discriminator)); if (qFuzzyIsNull(position.elevation())) { if (position.azimuth() < 180) return (a.y() < b.y()) ? b : a; return (a.y() < b.y()) ? a : b; } if (position.elevation() < 0) return (a.z() < b.z()) ? a : b; return (a.z() < b.z()) ? b : a; } plasma5-wallpapers-dynamic-3.3.6/src/lib/ksunpath.h000066400000000000000000000013511375723327100222340ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include "kdynamicwallpaper_export.h" #include #include #include class KSunPosition; class KDYNAMICWALLPAPER_EXPORT KSunPath { public: KSunPath() = default; bool isValid() const; QVector3D center() const; QVector3D normal() const; QVector3D project(const KSunPosition &position) const; static KSunPath create(const QDateTime &dateTime, const QGeoCoordinate &location); private: KSunPath(const QVector3D ¢er, const QVector3D &normal, float radius); QVector3D m_center; QVector3D m_normal; float m_radius; }; plasma5-wallpapers-dynamic-3.3.6/src/lib/ksunposition.cpp000066400000000000000000000205001375723327100234740ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include "ksunposition.h" #include /*! * \class KSunPosition * \brief The KSunPosition class provides a convenient way for determining the position of the * Sun at the specified time and location. */ const static qreal s_midnightHourAngle = -180; static qreal julianCenturiesToJulianDay(qreal jcent) { return 36525.0 * jcent + 2451545.0; } static qreal julianDayToJulianCenturies(qreal jd) { return (jd - 2451545.0) / 36525.0; } static qreal dateTimeToJulianDay(const QDateTime &dateTime) { return dateTime.toSecsSinceEpoch() / 86400.0 + 2440587.5; } static qreal sind(qreal value) { return std::sin(qDegreesToRadians(value)); } static qreal cosd(qreal value) { return std::cos(qDegreesToRadians(value)); } static qreal eccentricityEarthOrbit(qreal jcent) { return 0.016708634 - jcent * (0.000042037 + 0.0000001267 * jcent); } static qreal solarGeometricMeanAnomaly(qreal jcent) { return qDegreesToRadians(357.52911 + jcent * (35999.05029 - 0.0001537 * jcent)); } static qreal solarMeanEclipticObliquity(const qreal jcent) { const qreal seconds = 21.448 - jcent * (46.815 + jcent * (0.00059 - jcent * 0.001813)); return 23.0 + (26.0 + seconds / 60.0) / 60.0; } static qreal obliquityCorrection(qreal jcent) { const qreal meanEclipticObliquity = solarMeanEclipticObliquity(jcent); const qreal omega = qDegreesToRadians(125.04 - jcent * 1934.136); return qDegreesToRadians(meanEclipticObliquity + 0.00256 * std::cos(omega)); } static qreal solarEquationOfCenter(qreal jcent) { const qreal anomaly = solarGeometricMeanAnomaly(jcent); const qreal equation = std::sin(2 * anomaly) * (0.019993 - jcent * 0.000101) + std::sin(anomaly) * (1.914602 - jcent * (0.004817 + jcent * 0.000014)) + std::sin(3 * anomaly) * 0.000289; return qDegreesToRadians(equation); } static qreal solarGeometricMeanLongitude(qreal jcent) { qreal l = std::fmod(280.46646 + jcent * (36000.76983 + jcent * 0.0003032), 360); if (l < 0) l += 360; return qDegreesToRadians(l); } static qreal solarTrueLongitude(qreal jcent) { return solarGeometricMeanLongitude(jcent) + solarEquationOfCenter(jcent); } static qreal solarApparentLongitude(qreal jcent) { const qreal omega = qDegreesToRadians(125.04 - jcent * 1934.136); const qreal correction = qDegreesToRadians(-0.00569 - 0.00478 * std::sin(omega)); return solarTrueLongitude(jcent) + correction; } static qreal solarDeclination(qreal jcent) { return std::asin(std::sin(obliquityCorrection(jcent)) * std::sin(solarApparentLongitude(jcent))); } static qreal equationOfTime(qreal jcent) { const qreal e = eccentricityEarthOrbit(jcent); const qreal m = solarGeometricMeanAnomaly(jcent); const qreal l = solarGeometricMeanLongitude(jcent); const qreal y = std::pow(std::tan(0.5 * obliquityCorrection(jcent)), 2); const qreal equation = y * std::sin(2 * l) - 2 * e * std::sin(m) + 4 * e * y * std::sin(m) * std::cos(2 * l) - 0.5 * y * y * std::sin(4 * l) - 1.25 * e * e * std::sin(2 * m); return 4 * qRadiansToDegrees(equation); } static qreal julianCenturiesToMinutesFromMidnight(qreal jcent) { const qreal jd = julianCenturiesToJulianDay(jcent); const qreal minutes = jd - std::round(jd) - 0.5; return minutes * 1440.0; } static qreal solarHourAngle(qreal jcent, const QGeoCoordinate &location) { const qreal minutes = julianCenturiesToMinutesFromMidnight(jcent); const qreal angle = std::fmod(location.longitude() + (equationOfTime(jcent) + minutes - 720) / 4, 360); if (angle < -180.0) return angle + 360.0; if (angle > 180.0) return angle - 360.0; return angle; } static qreal solarZenith(qreal jcent, const QGeoCoordinate &location, qreal hourAngle) { const qreal declination = solarDeclination(jcent); const qreal zenith = std::acos(sind(location.latitude()) * std::sin(declination) + cosd(location.latitude()) * std::cos(declination) * cosd(hourAngle)); return qRadiansToDegrees(zenith); } static qreal solarAzimuth(qreal jcent, const QGeoCoordinate &location, qreal hourAngle) { const qreal zenith = solarZenith(jcent, location, hourAngle); const qreal denominator = cosd(location.latitude()) * sind(zenith); if (qFuzzyIsNull(denominator)) return std::nan(""); const qreal declination = solarDeclination(jcent); const qreal numerator = sind(location.latitude()) * cosd(zenith) - std::sin(declination); qreal azimuth = std::acos(qBound(-1.0, numerator / denominator, 1.0)); if (hourAngle < 0) azimuth = M_PI - azimuth; else azimuth = azimuth + M_PI; return qRadiansToDegrees(azimuth); } static qreal atmosphericRefractionCorrection(qreal e) { if (e > 85) return 0; const qreal te = std::tan(qDegreesToRadians(e)); qreal correction = 0; if (e > 5) correction = 58.1 / te - 0.07 / (te * te * te) + 0.000086 / (te * te * te * te * te); else if (e > -0.575) correction = 1735 + e * (-518.2 + e * (103.4 + e * (-12.79 + e * 0.711))); else correction = -20.774 / te; return correction / 3600.0; } /*! * Constructs a null KSunPosition object. */ KSunPosition::KSunPosition() : KSunPosition(0, 0) { } /*! * Constructs a KSunPosition with the given elevation \p elevation and azimuth \p azimuth. */ KSunPosition::KSunPosition(qreal elevation, qreal azimuth) : m_elevation(elevation) , m_azimuth(azimuth) { } /*! * Constructs a KSunPosition with the position of the Sun at the specified date \p dateTime * and location \p location. */ KSunPosition::KSunPosition(const QDateTime &dateTime, const QGeoCoordinate &location) { const qreal jd = dateTimeToJulianDay(dateTime); const qreal jcent = julianDayToJulianCenturies(jd); const qreal hourAngle = solarHourAngle(jcent, location); init(jcent, location, hourAngle); } /*! * Returns \c true if the position of the Sun is valid; otherwise returns \c false. */ bool KSunPosition::isValid() const { return !(std::isnan(m_azimuth) || std::isnan(m_elevation)); } /*! * Returns the elevation angle of the Sun, in decimal degrees. */ qreal KSunPosition::elevation() const { return m_elevation; } /*! * Returns the azimuth angle of the Sun, in decimal degrees. * * The azimuth angle specifies the Sun's relative direction along the local horizon, where 0 * degrees corresponds to north, 90 degrees corresponds to east, 180 degrees corresponds to * south, and 270 degrees corresponds to west. */ qreal KSunPosition::azimuth() const { return m_azimuth; } /*! * Converts the position of the Sun (elevation, azimuth) to the Cartesian coordinates. * * The returned value is a unit vector, i.e. it has a magnitude of 1. */ QVector3D KSunPosition::toVector() const { const float x = static_cast(cosd(m_elevation) * cosd(m_azimuth)); const float y = static_cast(cosd(m_elevation) * sind(m_azimuth)); const float z = static_cast(sind(m_elevation)); return QVector3D(x, y, z); } static qreal solarNoonCorrection(qreal jcent, const QGeoCoordinate &location) { return (720 - 4 * location.longitude() - equationOfTime(jcent)) / 1440; } /*! * Determines the position of the Sun (elevation, azimuth) at midnight. * * The returned value is the lowest position of the Sun in the sky at the specified time and location. */ KSunPosition KSunPosition::midnight(const QDateTime &dateTime, const QGeoCoordinate &location) { qreal jdNoon = std::round(dateTimeToJulianDay(dateTime)); qreal jcentNoon = julianDayToJulianCenturies(jdNoon); const qreal correction = solarNoonCorrection(jcentNoon, location); jdNoon += correction - 0.5; jcentNoon = julianDayToJulianCenturies(jdNoon); const qreal jdMidnight = jdNoon + 0.5; const qreal jcentMidnight = julianDayToJulianCenturies(jdMidnight); KSunPosition position; position.init(jcentMidnight, location, s_midnightHourAngle); return position; } void KSunPosition::init(qreal jcent, const QGeoCoordinate &location, qreal hourAngle) { const qreal zenith = solarZenith(jcent, location, hourAngle); m_elevation = 90 - zenith; m_elevation += atmosphericRefractionCorrection(m_elevation); m_azimuth = solarAzimuth(jcent, location, hourAngle); } plasma5-wallpapers-dynamic-3.3.6/src/lib/ksunposition.h000066400000000000000000000014361375723327100231500ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include "kdynamicwallpaper_export.h" #include #include #include class KDYNAMICWALLPAPER_EXPORT KSunPosition { public: KSunPosition(); KSunPosition(qreal elevation, qreal azimuth); KSunPosition(const QDateTime &dateTime, const QGeoCoordinate &location); bool isValid() const; qreal elevation() const; qreal azimuth() const; QVector3D toVector() const; static KSunPosition midnight(const QDateTime &dateTime, const QGeoCoordinate &location); private: void init(qreal jcent, const QGeoCoordinate &location, qreal hourAngle); qreal m_elevation; qreal m_azimuth; }; plasma5-wallpapers-dynamic-3.3.6/src/lib/ksystemclockmonitor.cpp000066400000000000000000000051101375723327100250520ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include "ksystemclockmonitor.h" #include "ksystemclockmonitorengine_p.h" /*! * \class KSystemClockMonitor * \brief The KSystemClockMonitor class provides a way for monitoring system clock changes. * * The KSystemClockMonitor class makes it possible to detect discontinuous changes to the system * clock. Such changes are usually initiated by the user adjusting values in the Date and Time * KCM or calls made to functions like settimeofday(). */ class KSystemClockMonitor::Private { public: void loadMonitorEngine(); void unloadMonitorEngine(); KSystemClockMonitor *monitor = nullptr; KSystemClockMonitorEngine *engine = nullptr; bool isActive = false; }; void KSystemClockMonitor::Private::loadMonitorEngine() { engine = KSystemClockMonitorEngine::create(monitor); if (engine) { QObject::connect(engine, &KSystemClockMonitorEngine::systemClockChanged, monitor, &KSystemClockMonitor::systemClockChanged); } } void KSystemClockMonitor::Private::unloadMonitorEngine() { if (!engine) return; QObject::disconnect(engine, &KSystemClockMonitorEngine::systemClockChanged, monitor, &KSystemClockMonitor::systemClockChanged); engine->deleteLater(); engine = nullptr; } /*! * Constructs an inactive KSystemClockMonitor object with the given \p parent. */ KSystemClockMonitor::KSystemClockMonitor(QObject *parent) : QObject(parent) , d(new Private) { d->monitor = this; } /*! * Destructs the KSystemClockMonitor object. */ KSystemClockMonitor::~KSystemClockMonitor() { } /*! * Returns \c true if the KSystemClockMonitor is active; otherwise returns \c false. */ bool KSystemClockMonitor::isActive() const { return d->isActive; } /*! * Sets the active status of the KSystemClockMonitor to \p active. * * systemClockChanged() signal won't be emitted while the monitor is inactive. * * The monitor is inactive by default. * * @see activeChanged */ void KSystemClockMonitor::setActive(bool set) { if (d->isActive == set) return; d->isActive = set; if (d->isActive) d->loadMonitorEngine(); else d->unloadMonitorEngine(); emit activeChanged(); } /*! * @fn void KSystemClockMonitor::activeChanged() * * This signal is emitted when the active property has been changed. */ /*! * @fn void KSystemClockMonitor::systemClockChanged() * * This signal is emitted when the system clock has been changed. */ plasma5-wallpapers-dynamic-3.3.6/src/lib/ksystemclockmonitor.h000066400000000000000000000012301375723327100245160ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include "kdynamicwallpaper_export.h" #include class KDYNAMICWALLPAPER_EXPORT KSystemClockMonitor : public QObject { Q_OBJECT Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged) public: explicit KSystemClockMonitor(QObject *parent = nullptr); ~KSystemClockMonitor() override; bool isActive() const; void setActive(bool active); signals: void activeChanged(); void systemClockChanged(); private: class Private; QScopedPointer d; }; plasma5-wallpapers-dynamic-3.3.6/src/lib/ksystemclockmonitorengine.cpp000066400000000000000000000010421375723327100262400ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include "ksystemclockmonitorengine_p.h" #if defined(Q_OS_LINUX) #include "ksystemclockmonitorengine_linux_p.h" #endif KSystemClockMonitorEngine *KSystemClockMonitorEngine::create(QObject *parent) { #if defined(Q_OS_LINUX) return KLinuxSystemClockMonitorEngine::create(parent); #else return nullptr; #endif } KSystemClockMonitorEngine::KSystemClockMonitorEngine(QObject *parent) : QObject(parent) { } plasma5-wallpapers-dynamic-3.3.6/src/lib/ksystemclockmonitorengine_linux.cpp000066400000000000000000000033151375723327100274640ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: LGPL-3.0-or-later */ #include "ksystemclockmonitorengine_linux_p.h" #include #include #include #include #ifndef TFD_TIMER_CANCEL_ON_SET #define TFD_TIMER_CANCEL_ON_SET (1 << 1) #endif KLinuxSystemClockMonitorEngine *KLinuxSystemClockMonitorEngine::create(QObject *parent) { const int fd = timerfd_create(CLOCK_REALTIME, O_CLOEXEC | O_NONBLOCK); if (fd == -1) { qWarning("Couldn't create clock skew notifier engine: %s", strerror(errno)); return nullptr; } const itimerspec spec = {}; const int ret = timerfd_settime(fd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &spec, nullptr); if (ret == -1) { qWarning("Couldn't create clock skew notifier engine: %s", strerror(errno)); close(fd); return nullptr; } return new KLinuxSystemClockMonitorEngine(fd, parent); } KLinuxSystemClockMonitorEngine::KLinuxSystemClockMonitorEngine(int fd, QObject *parent) : KSystemClockMonitorEngine(parent) , m_fd(fd) { const QSocketNotifier *notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); connect(notifier, &QSocketNotifier::activated, this, &KLinuxSystemClockMonitorEngine::handleTimerCancelled); } KLinuxSystemClockMonitorEngine::~KLinuxSystemClockMonitorEngine() { close(m_fd); } void KLinuxSystemClockMonitorEngine::handleTimerCancelled() { uint64_t expirationCount; const ssize_t readCount = read(m_fd, &expirationCount, sizeof(expirationCount)); if (readCount != -1 || errno != ECANCELED) { return; } emit systemClockChanged(); } plasma5-wallpapers-dynamic-3.3.6/src/lib/ksystemclockmonitorengine_linux_p.h000066400000000000000000000010341375723327100274440ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include "ksystemclockmonitorengine_p.h" class KLinuxSystemClockMonitorEngine : public KSystemClockMonitorEngine { Q_OBJECT public: ~KLinuxSystemClockMonitorEngine() override; static KLinuxSystemClockMonitorEngine *create(QObject *parent); private Q_SLOTS: void handleTimerCancelled(); private: KLinuxSystemClockMonitorEngine(int fd, QObject *parent); int m_fd; }; plasma5-wallpapers-dynamic-3.3.6/src/lib/ksystemclockmonitorengine_p.h000066400000000000000000000006401375723327100262270ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: LGPL-3.0-or-later */ #pragma once #include class KSystemClockMonitorEngine : public QObject { Q_OBJECT public: static KSystemClockMonitorEngine *create(QObject *parent); protected: explicit KSystemClockMonitorEngine(QObject *parent); Q_SIGNALS: void systemClockChanged(); }; plasma5-wallpapers-dynamic-3.3.6/src/lib/metadata.xml000066400000000000000000000005001375723327100225230ustar00rootroot00000000000000 plasma5-wallpapers-dynamic-3.3.6/src/lib/resources.qrc000066400000000000000000000001601375723327100227440ustar00rootroot00000000000000 metadata.xml plasma5-wallpapers-dynamic-3.3.6/src/package/000077500000000000000000000000001375723327100210535ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/package/contents/000077500000000000000000000000001375723327100227105ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/package/contents/config/000077500000000000000000000000001375723327100241555ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/package/contents/config/main.xml000066400000000000000000000022121375723327100256200ustar00rootroot00000000000000 2 true 0 -90 90 0 -180 180 300000 330 100 1000 true plasma5-wallpapers-dynamic-3.3.6/src/package/contents/ui/000077500000000000000000000000001375723327100233255ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/package/contents/ui/WallpaperImage.qml000066400000000000000000000036711375723327100267410ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ import QtQuick 2.5 Item { id: root /*! * The image being displayed in the bottom layer. */ property url bottomLayer /*! * The image being displayed in the top layer. */ property url topLayer /*! * The blend factor between the bottom layer and the top layer. * * The blend factor varies between 0 and 1. 0 means that only the bottom * layer is visible; 1 means that only the top layer is visible. * * The default value is 0. */ property real blendFactor: 0 /*! * Set this property to define what happens when the source image has a * different size than the item. * * Defaults to \c Image.Stretch. */ property int fillMode: Image.Stretch /*! * This property holds the status of image loading. */ readonly property int status: { if (bottom.status == Image.Error || top.status == Image.Error) return Image.Error; if (bottom.status == Image.Loading || top.status == Image.Loading) return Image.Loading; if (bottom.status == Image.Ready || top.status == Image.Ready) return Image.Ready; return Image.Null; } Image { id: bottom anchors.fill: parent asynchronous: true autoTransform: true cache: wallpaper.configuration.Cache fillMode: root.fillMode source: root.bottomLayer } Image { id: top anchors.fill: parent asynchronous: true autoTransform: true cache: wallpaper.configuration.Cache fillMode: root.fillMode opacity: root.blendFactor source: root.topLayer } Behavior on blendFactor { NumberAnimation { duration: wallpaper.configuration.TransitionDuration } } } plasma5-wallpapers-dynamic-3.3.6/src/package/contents/ui/WallpaperView.qml000066400000000000000000000064661375723327100266360ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ import QtQuick 2.5 import QtQuick.Controls 2.0 StackView { id: root /*! * The image being displayed in the bottom layer. */ property url bottomLayer /*! * The image being displayed in the top layer. */ property url topLayer /*! * The blend factor between the bottom layer and the top layer. * * The blend factor varies between 0 and 1. 0 means that only the bottom * layer is visible; 1 means that only the top layer is visible. */ property real blendFactor /*! * Set this property to define what happens when the source image has a * different size than the item. */ property int fillMode /*! * This property holds the wallpaper image about to be presented. */ property WallpaperImage __nextItem: null /*! * This property holds the status of image loading. */ readonly property int status: __nextItem ? __nextItem.status : Image.Null onBottomLayerChanged: Qt.callLater(reload) onTopLayerChanged: Qt.callLater(reload) onBlendFactorChanged: Qt.callLater(reblend) onFillModeChanged: Qt.callLater(reload) Component { id: baseImage WallpaperImage { layer.enabled: root.replaceEnter.running StackView.onRemoved: destroy() } } replaceEnter: Transition { OpacityAnimator { duration: wallpaper.configuration.TransitionDuration from: 0 to: 1 } } replaceExit: Transition { PauseAnimation { duration: wallpaper.configuration.TransitionDuration } } function __swap() { if (root.__nextItem.status == Image.Loading) return; root.__nextItem.statusChanged.disconnect(root.__swap); if (root.__nextItem.status == Image.Error) return; var operation; if (!root.currentItem) operation = StackView.Immediate; else if (root.currentItem.bottomLayer !== bottomLayer) operation = StackView.Transition; else if (root.currentItem.topLayer !== topLayer) operation = StackView.Transition; else operation = StackView.Immediate; if (operation === StackView.Transition) root.__nextItem.opacity = 0; else root.__nextItem.opacity = 1; root.replace(root.__nextItem, {}, operation); } function reload() { if (root.status == Image.Loading) root.__nextItem.statusChanged.disconnect(root.__swap); root.__nextItem = baseImage.createObject(root, { bottomLayer: bottomLayer, topLayer: topLayer, blendFactor: blendFactor, fillMode: fillMode }); if (root.__nextItem.status == Image.Loading) root.__nextItem.statusChanged.connect(root.__swap); else root.__swap(); } function reblend() { if (!root.currentItem) return; if (root.currentItem.bottomLayer !== bottomLayer) return; if (root.currentItem.topLayer !== topLayer) return; root.currentItem.blendFactor = blendFactor; } } plasma5-wallpapers-dynamic-3.3.6/src/package/contents/ui/config.qml000066400000000000000000000207111375723327100253060ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ import QtQuick 2.5 import QtQuick.Dialogs 1.3 import QtQuick.Layouts 1.0 import QtQuick.Controls 1.0 as QtControls import QtQuick.Controls 2.3 as QtControls2 import QtPositioning 5.12 import org.kde.kcm 1.1 as KCM import org.kde.kirigami 2.10 as Kirigami import com.github.zzag.plasma.wallpapers.dynamic 1.0 ColumnLayout { id: root property int cfg_FillMode property string cfg_Image property int cfg_UpdateInterval property alias cfg_AutoDetectLocation: autoDetectLocationCheckBox.checked property alias cfg_ManualLatitude: latitudeSpinBox.value property alias cfg_ManualLongitude: longitudeSpinBox.value function saveConfig() { wallpapersModel.purge(); } Kirigami.FormLayout { twinFormLayouts: parentLayout QtControls2.ComboBox { id: positioningComboBox Kirigami.FormData.label: i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Fill Mode:") textRole: "text" model: [ { "text": i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Scaled and Cropped"), "value": Image.PreserveAspectCrop }, { "text": i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Scaled"), "value": Image.Stretch }, { "text": i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Scaled, Keep Proportions"), "value": Image.PreserveAspectFit }, { "text": i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Centered"), "value": Image.Pad }, { "text": i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Tiled"), "value": Image.Tile } ] onActivated: cfg_FillMode = model[currentIndex]["value"] Component.onCompleted: { for (var i = 0; i < model.length; i++) { if (model[i]["value"] === wallpaper.configuration.FillMode) { positioningComboBox.currentIndex = i; break; } } } } QtControls2.CheckBox { id: autoDetectLocationCheckBox text: i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Automatically detect location") } QtControls.SpinBox { Kirigami.FormData.label: i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Latitude:") enabled: !autoDetectLocationCheckBox.checked visible: autoDetectLocationCheckBox.checked decimals: 2 minimumValue: -90 maximumValue: 90 value: automaticLocationProvider.position.coordinate.latitude } QtControls.SpinBox { Kirigami.FormData.label: i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Longitude:") enabled: !autoDetectLocationCheckBox.checked visible: autoDetectLocationCheckBox.checked decimals: 2 minimumValue: -180 maximumValue: 180 value: automaticLocationProvider.position.coordinate.longitude } QtControls.SpinBox { id: latitudeSpinBox Kirigami.FormData.label: i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Latitude:") decimals: 2 minimumValue: -90 maximumValue: 90 visible: !autoDetectLocationCheckBox.checked } QtControls.SpinBox { id: longitudeSpinBox Kirigami.FormData.label: i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Longitude:") decimals: 2 minimumValue: -180 maximumValue: 180 visible: !autoDetectLocationCheckBox.checked } QtControls.SpinBox { id: updateIntervalSpinBox Kirigami.FormData.label: i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Update Every:") maximumValue: 360 minimumValue: 1 suffix: i18ndp("plasma_wallpaper_com.github.zzag.dynamic", " minute", " minutes", value) onValueChanged: cfg_UpdateInterval = value * 60000 Component.onCompleted: value = wallpaper.configuration.UpdateInterval / 60000 } } Kirigami.InlineMessage { id: errorContainer Layout.fillWidth: true showCloseButton: true type: Kirigami.MessageType.Error visible: false } KCM.GridView { id: wallpapersGrid Layout.fillWidth: true Layout.fillHeight: true function resetCurrentIndex() { view.currentIndex = wallpapersModel.find(cfg_Image); } view.model: wallpapersModel view.delegate: KCM.GridDelegate { hoverEnabled: true opacity: model.zombie ? 0.5 : 1 text: model.name toolTip: { if (model.author && model.license) return i18ndc("plasma_wallpaper_com.github.zzag.dynamic", " by ()", "By %1 (%2)", model.author, model.license); if (model.license) return i18ndc("plasma_wallpaper_com.github.zzag.dynamic", " ()", "%1 (%2)", model.name, model.license); return model.name; } actions: [ Kirigami.Action { icon.name: "document-open-folder" tooltip: i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Open Containing Folder") onTriggered: Qt.openUrlExternally(model.folder) }, Kirigami.Action { icon.name: "edit-undo" tooltip: i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Restore Wallpaper") visible: model.zombie onTriggered: wallpapersModel.unscheduleRemove(wallpapersModel.modelIndex(index)) }, Kirigami.Action { icon.name: "edit-delete" tooltip: i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Remove Wallpaper") visible: !model.zombie && model.removable onTriggered: wallpapersModel.scheduleRemove(wallpapersModel.modelIndex(index)) } ] thumbnail: Image { anchors.fill: parent fillMode: cfg_FillMode source: model.preview } onClicked: { cfg_Image = model.image; wallpapersGrid.forceActiveFocus(); } } Connections { target: wallpapersModel onRowsInserted: Qt.callLater(wallpapersGrid.resetCurrentIndex) onRowsRemoved: Qt.callLater(wallpapersGrid.resetCurrentIndex) } Connections { target: root onCfg_ImageChanged: Qt.callLater(wallpapersGrid.resetCurrentIndex) } } Loader { id: wallpaperDialogLoader active: false sourceComponent: FileDialog { title: i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Open Wallpaper") folder: shortcuts.home nameFilters: [i18nd("plasma_wallpaper_com.github.zzag.dynamic", "HEIF Image Files (*.heic *.heif)")] onAccepted: { wallpapersModel.add(fileUrl); wallpaperDialogLoader.active = false; } onRejected: { wallpaperDialogLoader.active = false; } Component.onCompleted: open() } } RowLayout { Layout.alignment: Qt.AlignRight QtControls2.Button { icon.name: "list-add" text: i18nd("plasma_wallpaper_com.github.zzag.dynamic", "Add Wallpaper...") onClicked: wallpaperDialogLoader.active = true } } PositionSource { id: automaticLocationProvider active: autoDetectLocationCheckBox.checked } DynamicWallpaperModel { id: wallpapersModel } function showErrorMessage(message) { errorContainer.text = message; errorContainer.visible = true; } Component.onCompleted: { wallpapersModel.errorOccurred.connect(showErrorMessage); wallpapersModel.reload() } } plasma5-wallpapers-dynamic-3.3.6/src/package/contents/ui/main.qml000066400000000000000000000047131375723327100247710ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ import QtQuick 2.5 import QtQuick.Window 2.2 import QtPositioning 5.12 import org.kde.kirigami 2.10 as Kirigami import com.github.zzag.plasma.wallpapers.dynamic 1.0 Item { id: root PositionSource { id: automaticLocationProvider active: wallpaper.configuration.AutoDetectLocation } Location { id: manualLocationProvider coordinate { latitude: wallpaper.configuration.ManualLatitude longitude: wallpaper.configuration.ManualLongitude } } WallpaperView { id: view anchors.fill: parent blendFactor: handler.blendFactor bottomLayer: handler.bottomLayer fillMode: wallpaper.configuration.FillMode topLayer: handler.topLayer visible: handler.status == DynamicWallpaperHandler.Ready onStatusChanged: if (status != Image.Loading) { wallpaper.loading = false; } } Rectangle { anchors.fill: parent Kirigami.Theme.colorSet: Kirigami.Theme.View color: Kirigami.Theme.backgroundColor visible: handler.status == DynamicWallpaperHandler.Error Text { anchors.left: parent.left anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter font.pointSize: 24 horizontalAlignment: Text.AlignHCenter text: handler.errorString wrapMode: Text.Wrap } } DynamicWallpaperHandler { id: handler location: { if (wallpaper.configuration.AutoDetectLocation) return automaticLocationProvider.position.coordinate; return manualLocationProvider.coordinate; } source: wallpaper.configuration.Image onStatusChanged: if (status == DynamicWallpaperHandler.Error) { wallpaper.loading = false; } } SystemClockMonitor { active: handler.status == DynamicWallpaperHandler.Ready onSystemClockChanged: handler.scheduleUpdate() } Timer { interval: wallpaper.configuration.UpdateInterval repeat: true running: handler.status == DynamicWallpaperHandler.Ready onTriggered: handler.scheduleUpdate() } Component.onCompleted: { wallpaper.loading = handler.status == DynamicWallpaperHandler.Ready; } } plasma5-wallpapers-dynamic-3.3.6/src/package/metadata.desktop000066400000000000000000000012521375723327100242260ustar00rootroot00000000000000[Desktop Entry] Icon=preferences-desktop-wallpaper Name=Dynamic Name[fr]=Dynamique Name[it]=Dinamico Name[nl]=Dynamisch Name[ru]=Динамический Name[uk]=Динамічний Comment=Dynamic wallpaper Comment[ru]=Динамические обои Comment[uk]=Динамічні шпалери Type=Service X-KDE-ServiceTypes=Plasma/Wallpaper X-KDE-ParentApp=org.kde.plasmashell X-KDE-PluginInfo-Author=Vlad Zahorodnii X-KDE-PluginInfo-Category= X-KDE-PluginInfo-Email=vlad.zahorodnii@kde.org X-KDE-PluginInfo-License=GPLv3+ X-KDE-PluginInfo-Name=com.github.zzag.dynamic X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website=https://github.com/zzag/plasma5-wallpapers-dynamic plasma5-wallpapers-dynamic-3.3.6/src/plugins/000077500000000000000000000000001375723327100211415ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/plugins/CMakeLists.txt000066400000000000000000000000471375723327100237020ustar00rootroot00000000000000add_subdirectory(kpackage-integration) plasma5-wallpapers-dynamic-3.3.6/src/plugins/kpackage-integration/000077500000000000000000000000001375723327100252305ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/plugins/kpackage-integration/CMakeLists.txt000066400000000000000000000007131375723327100277710ustar00rootroot00000000000000add_definitions(-DTRANSLATION_DOMAIN=\"plasma_wallpaper_com.github.zzag.dynamic\") set(packagestructure_SOURCES dynamicwallpaperpackagestructure.cpp ) add_library(packagestructure_dynamicwallpaper MODULE ${packagestructure_SOURCES}) target_link_libraries(packagestructure_dynamicwallpaper Qt5::Core KF5::I18n KF5::Package ) install(TARGETS packagestructure_dynamicwallpaper DESTINATION ${KDE_INSTALL_PLUGINDIR}/kpackage/packagestructure) dynamicwallpaperpackagestructure.cpp000066400000000000000000000031531375723327100345100ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/plugins/kpackage-integration/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "dynamicwallpaperpackagestructure.h" #include #include DynamicWallpaperPackageStructure::DynamicWallpaperPackageStructure(QObject *parent, const QVariantList &args) : KPackage::PackageStructure(parent, args) { } void DynamicWallpaperPackageStructure::initPackage(KPackage::Package *package) { package->addDirectoryDefinition(QByteArrayLiteral("images"), QStringLiteral("images/"), i18n("Dynamic wallpaper files")); package->setRequired(QByteArrayLiteral("images"), true); } void DynamicWallpaperPackageStructure::pathChanged(KPackage::Package *package) { package->removeDefinition(QByteArrayLiteral("dynamic")); const QStringList fileFormats { QStringLiteral(".heic"), QStringLiteral(".heif") }; for (const QString &fileFormat : fileFormats) { const QFileInfo fileInfo(package->path() + QLatin1String("contents/images/dynamic") + fileFormat); if (!fileInfo.exists()) continue; package->addFileDefinition(QByteArrayLiteral("dynamic"), QStringLiteral("images/dynamic") + fileFormat, i18n("Dynamic wallpaper file")); package->setRequired(QByteArrayLiteral("dynamic"), true); break; } } K_EXPORT_KPACKAGE_PACKAGE_WITH_JSON(DynamicWallpaperPackageStructure, "dynamicwallpaperpackagestructure.json") #include "dynamicwallpaperpackagestructure.moc" plasma5-wallpapers-dynamic-3.3.6/src/plugins/kpackage-integration/dynamicwallpaperpackagestructure.h000066400000000000000000000010161375723327100342300ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include class DynamicWallpaperPackageStructure final : public KPackage::PackageStructure { Q_OBJECT public: explicit DynamicWallpaperPackageStructure(QObject *parent = nullptr, const QVariantList &args = QVariantList()); void initPackage(KPackage::Package *package) override; void pathChanged(KPackage::Package *package) override; }; dynamicwallpaperpackagestructure.json000066400000000000000000000005411375723327100346750ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/plugins/kpackage-integration{ "KPlugin": { "Authors": [ { "Email": "vlad.zahorodnii@kde.org", "Name": "Vlad Zahorodnii" } ], "Id": "Wallpaper/Dynamic", "Name": "Dynamic Wallpaper", "ServiceTypes": [ "KPackage/PackageStructure" ], "Version": "1" } } plasma5-wallpapers-dynamic-3.3.6/src/tools/000077500000000000000000000000001375723327100206205ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/tools/CMakeLists.txt000066400000000000000000000000321375723327100233530ustar00rootroot00000000000000add_subdirectory(builder) plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/000077500000000000000000000000001375723327100222465ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/CMakeLists.txt000066400000000000000000000010271375723327100250060ustar00rootroot00000000000000add_definitions(-DTRANSLATION_DOMAIN=\"plasma_wallpaper_com.github.zzag.dynamic\") add_subdirectory(completions) set(builder_SOURCES dynamicwallpaperdescriptionreader.cpp dynamicwallpaperexifmetadata.cpp main.cpp ) set(builder_LIBRARIES KF5::I18n libexif::libexif KDynamicWallpaper::KDynamicWallpaper ) add_executable(kdynamicwallpaperbuilder ${builder_SOURCES}) target_link_libraries(kdynamicwallpaperbuilder ${builder_LIBRARIES}) install(TARGETS kdynamicwallpaperbuilder ${INSTALL_TARGETS_DEFAULT_ARGS}) plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/README.md000066400000000000000000000045251375723327100235330ustar00rootroot00000000000000# kdynamicwallpaperbuilder kdynamicwallpaperbuilder is a command line tool designed for creating dynamic wallpapers from a collection of images. ## Getting Started In order to create a dynamic wallpaper, you need to provide a json file with the following structure ```json [ { "SolarAzimuth": "*", "SolarElevation": "*", "CrossFade": true, "Time": "18:00", "FileName": "0.png" }, { "SolarAzimuth": 0, "SolarElevation": -90, "CrossFade": true, "Time": "00:00", "FileName": "1.png" }, { "SolarAzimuth": 90, "SolarElevation": 0, "CrossFade": true, "Time": "06:00", "FileName": "2.png" }, { "SolarAzimuth": 180, "SolarElevation": 90, "CrossFade": true, "Time": "12:00", "FileName": "3.png" } ] ``` It might look like you must provide a lot of data, but don't be scared. Let's break it down. The `SolarAzimuth` field and the `SolarElevation` field specify the position of the Sun when the associated picture was taken. The `Time` field specifies the time, which is in 24-hour format, when the picture was taken. If the user is not located near the North or the South Pole, the dynamic wallpaper engine will try to show images based on the current position of the Sun; otherwise it will fallback to using time metadata. Note that only the `Time` field is required, the position of the Sun is optional. If `SolarAzimuth` or `SolarElevation` has a special value of `"*"`, then the position of the Sun will be computed based on GPS coordinates and the time when the picture was taken. The `CrossFade` field indicates whether the current image can be blended with the next one. The cross-fading is used to make transitions between images smooth. By default, the `CrossFade` field is set to `true`. Last, but not least, the `FileName` field specifies the file path of the image relative to the metadata json file. Now that you have prepared all images and a metadata file, it's time pull out big guns. Run the following command ```sh kdynamicwallpaperbuilder path/to/metadata.json ``` It may take some time before the command completes, so be patient. If everything goes well, you should see a new file in the current working directory `wallpaper.heic`, which can be used as a dynamic wallpaper. plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/completions/000077500000000000000000000000001375723327100246025ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/completions/CMakeLists.txt000066400000000000000000000001041375723327100273350ustar00rootroot00000000000000add_subdirectory(bash) add_subdirectory(fish) add_subdirectory(zsh) plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/completions/bash/000077500000000000000000000000001375723327100255175ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/completions/bash/CMakeLists.txt000066400000000000000000000001501375723327100302530ustar00rootroot00000000000000install(FILES kdynamicwallpaperbuilder DESTINATION ${KDE_INSTALL_DATADIR}/bash-completion/completions/) plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/completions/bash/kdynamicwallpaperbuilder000066400000000000000000000011071375723327100325170ustar00rootroot00000000000000_kdynamicwallpaperbuilder_module() { local cur prev OPTS _init_completion || return case $prev in '-h'|'--help'|'--help-all'|'-v'|'--version') return ;; esac case $cur in -*) OPTS=" --output --quality --discard-color-profile --lossless " COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) return ;; esac } complete -F _kdynamicwallpaperbuilder_module kdynamicwallpaperbuilder # ex: filetype=sh plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/completions/fish/000077500000000000000000000000001375723327100255335ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/completions/fish/CMakeLists.txt000066400000000000000000000001411375723327100302670ustar00rootroot00000000000000install(FILES kdynamicwallpaperbuilder.fish DESTINATION ${KDE_INSTALL_DATADIR}/fish/completions) plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/completions/fish/kdynamicwallpaperbuilder.fish000066400000000000000000000013741375723327100334710ustar00rootroot00000000000000# kdynamicwallpaperbuilder(1) completion complete -f -c kdynamicwallpaperbuilder complete -c kdynamicwallpaperbuilder -s v -l version -d "Print the version information and quit" complete -c kdynamicwallpaperbuilder -s h -l help -d "Show help message and quit" complete -c kdynamicwallpaperbuilder -l help-all -d "Show help message including Qt specific options and quit" complete -c kdynamicwallpaperbuilder -l quality -d "Specify the quality of the encoded images (from 0 to 100)" -r complete -c kdynamicwallpaperbuilder -l output -d "Specify the file where the output will be written" -r complete -c kdynamicwallpaperbuilder -l discard-color-profile -d "Discard embedded color profile" complete -c kdynamicwallpaperbuilder -l lossless -d "Use lossless coding" plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/completions/zsh/000077500000000000000000000000001375723327100254065ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/completions/zsh/CMakeLists.txt000066400000000000000000000001401375723327100301410ustar00rootroot00000000000000install(FILES _kdynamicwallpaperbuilder DESTINATION ${KDE_INSTALL_DATADIR}/zsh/site-functions/) plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/completions/zsh/_kdynamicwallpaperbuilder000066400000000000000000000007711375723327100325530ustar00rootroot00000000000000#compdef kdynamicwallpaperbuilder _arguments -s \ {-v,--version}'[Show the version information and quit]' \ {-h,--help}'[Show help message and quit]' \ '--help-all[Show help message including Qt specific options and quit]' \ '--quality[Specify the quality of the encoded images (from 0 to 100)]:number' \ '--output[Specify the file where the output will be written]:files:_files' \ '--discard-color-profile[Discard embedded color profile]' \ '--lossless[Use lossless coding]' plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/dynamicwallpaperdescriptionreader.cpp000066400000000000000000000240311375723327100317350ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "dynamicwallpaperdescriptionreader.h" #include "dynamicwallpaperexifmetadata.h" #include #include #include #include #include #include #include class DynamicWallpaperReaderState { public: KDynamicWallpaperMetaData metaData; QImage image; bool isPrimary; }; class DynamicWallpaperDescriptionReaderPrivate { public: bool open(); void setError(const QString &text); QString resolveFileName(const QString &fileName); bool read(int index); DynamicWallpaperReaderState state; QString errorString; QString metaDataFileName; QJsonArray descriptors; int descriptorIndex = -1; bool atEnd = false; bool hasError = false; }; bool DynamicWallpaperDescriptionReaderPrivate::open() { QFile file(metaDataFileName); if (!file.open(QFile::ReadOnly)) { setError(i18n("Failed to open %1: %2", file.fileName(), file.errorString())); return false; } QJsonDocument document = QJsonDocument::fromJson(file.readAll()); if (document.isNull()) { setError(i18n("Invalid JSON file")); return false; } descriptors = document.array(); if (descriptors.isEmpty()) { setError(i18n("JSON document is empty")); return false; } return true; } /*! * \internal * * Returns \c true if the specified value is a placeholder; otherwise returns \c false. * * A placeholder indicates that the correponding value must be computed by the builder. */ static bool isPlaceholderValue(const QJsonValue &value) { return value.toString().trimmed() == QStringLiteral("*"); } /*! * \internal * * Returns a real number between 0 and 1, where 0 and 1 correspond to midnight, and 0.5 * corresponds to noon. */ static qreal timeToReal(const QTime &time) { return time.msecsSinceStartOfDay() / 86400000.0; } bool DynamicWallpaperDescriptionReaderPrivate::read(int index) { state = DynamicWallpaperReaderState(); const QJsonObject descriptor = descriptors[index].toObject(); const QJsonValue solarElevation = descriptor[QLatin1String("SolarElevation")]; const QJsonValue solarAzimuth = descriptor[QLatin1String("SolarAzimuth")]; const QJsonValue crossFadeMode = descriptor[QLatin1String("CrossFade")]; const QJsonValue time = descriptor[QLatin1String("Time")]; const QJsonValue fileName = descriptor[QLatin1String("FileName")]; QString absoluteFileName = fileName.toString(); if (!QFileInfo(fileName.toString()).isAbsolute()) absoluteFileName = resolveFileName(fileName.toString()); QImageReader reader(absoluteFileName); if (!reader.read(&state.image)) { setError(i18n("Failed to read %1: %2", reader.fileName(), reader.errorString())); return false; } KDynamicWallpaperMetaData::MetaDataFields placeholderFields; if (solarAzimuth.isUndefined() ^ solarElevation.isUndefined()) { if (solarAzimuth.isUndefined()) setError(i18n("SolarElevation was specified but SolarAzimuth was not")); else setError(i18n("SolarAzimuth was specified but SolarElevation was not")); return false; } if (!solarElevation.isUndefined()) { if (isPlaceholderValue(solarElevation)) { placeholderFields |= KDynamicWallpaperMetaData::SolarElevationField; } else if (solarElevation.isDouble()) { state.metaData.setSolarElevation(solarElevation.toDouble()); } else { setError(i18n("Invalid solar elevation value has been specified for %1", absoluteFileName)); return false; } } if (!solarAzimuth.isUndefined()) { if (isPlaceholderValue(solarAzimuth)) { placeholderFields |= KDynamicWallpaperMetaData::SolarAzimuthField; } else if (solarAzimuth.isDouble()) { state.metaData.setSolarAzimuth(solarAzimuth.toDouble()); } else { setError(i18n("Invalid solar azimuth value has been specified for %1", absoluteFileName)); return false; } } if (!crossFadeMode.isUndefined()) { if (crossFadeMode.toBool()) state.metaData.setCrossFadeMode(KDynamicWallpaperMetaData::CrossFade); else state.metaData.setCrossFadeMode(KDynamicWallpaperMetaData::NoCrossFade); } if (!time.isUndefined()) { if (isPlaceholderValue(time)) { placeholderFields |= KDynamicWallpaperMetaData::TimeField; } else { const QTime parsedTime = QTime::fromString(time.toString()); if (parsedTime.isValid()) { state.metaData.setTime(timeToReal(parsedTime)); } else { setError(i18n("Failed to parse time for image %1", absoluteFileName)); return false; } } } else { setError(i18n("No time has been provided for %1", absoluteFileName)); return false; } if (placeholderFields) { DynamicWallpaperExifMetaData exifMetaData(absoluteFileName); if (placeholderFields & KDynamicWallpaperMetaData::SolarAzimuthField) { if (exifMetaData.fields() & DynamicWallpaperExifMetaData::SolarCoordinatesField) { state.metaData.setSolarAzimuth(exifMetaData.solarAzimuth()); } else { setError(i18n("%1: Failed to compute the position of the Sun based on GPS " "coordinates and the time when the photo was taken. Please check " "that the specified image actually has GPS coordinates in its " "Exif metadata. You can do that with a tool such as exiftool.", absoluteFileName)); return false; } } if (placeholderFields & KDynamicWallpaperMetaData::SolarElevationField) { if (exifMetaData.fields() & DynamicWallpaperExifMetaData::SolarCoordinatesField) { state.metaData.setSolarElevation((exifMetaData.solarElevation())); } else { setError(i18n("%1: Failed to compute the position of the Sun based on GPS " "coordinates and the time when the photo was taken. Please check " "that the specified image actually has GPS coordinates in its " "Exif metadata. You can do that with a tool such as exiftool.", absoluteFileName)); return false; } } if (placeholderFields & KDynamicWallpaperMetaData::TimeField) { if (exifMetaData.fields() & DynamicWallpaperExifMetaData::BirthDateTimeField) { state.metaData.setTime(timeToReal(exifMetaData.birthDateTime().time())); } else { setError(i18n("Failed to determine the time when %1 was taken from its Exif " "metadata", absoluteFileName)); return false; } } } state.isPrimary = descriptor[QLatin1String("Primary")].toBool(); return true; } QString DynamicWallpaperDescriptionReaderPrivate::resolveFileName(const QString &fileName) { const QFileInfo descriptionFileInfo(metaDataFileName); const QDir descriptionDirectory = descriptionFileInfo.dir(); return descriptionDirectory.absoluteFilePath(fileName); } void DynamicWallpaperDescriptionReaderPrivate::setError(const QString &text) { errorString = text; hasError = true; } /*! * Constructs the DynamicWallpaperDescriptionReader with the file name \p fileName. */ DynamicWallpaperDescriptionReader::DynamicWallpaperDescriptionReader(const QString &fileName) : d(new DynamicWallpaperDescriptionReaderPrivate) { d->metaDataFileName = fileName; } /*! * Destructs the DynamicWallpaperDescriptionReader object. */ DynamicWallpaperDescriptionReader::~DynamicWallpaperDescriptionReader() { } /*! * Returns \c true if the reader has read until the last descriptor, or if an error has occurred * and reading has been aborted. Otherwise, it returns \c false. */ bool DynamicWallpaperDescriptionReader::atEnd() const { return d->hasError || d->descriptorIndex >= d->descriptors.count(); } /*! * Attempts to read the next wallpaper image descriptor and returns \c true if successful; * otherwise returns \c false if the end of file has been reached or an error occurred. */ bool DynamicWallpaperDescriptionReader::readNext() { if (d->hasError) return false; if (d->descriptors.isEmpty() && !d->open()) return false; d->descriptorIndex++; if (d->descriptorIndex >= d->descriptors.count()) return false; return d->read(d->descriptorIndex); } /*! * Returns the image for the current descriptor. * * This method will return an invalid QImage if an error has occurred. */ QImage DynamicWallpaperDescriptionReader::image() const { if (d->hasError) return QImage(); return d->state.image; } /*! * Returns the KDynamicWallpaperMetaData object for the current descriptor. * * This method will return an invalid KDynamicWallpaperMetaData if an error has occurred. */ KDynamicWallpaperMetaData DynamicWallpaperDescriptionReader::metaData() const { if (d->hasError) return KDynamicWallpaperMetaData(); return d->state.metaData; } /*! * Returns \c true if the current descriptor describes the primary image; otherwise returns \c false. * * This method will return \c false if an error has occurred. */ bool DynamicWallpaperDescriptionReader::isPrimary() const { return !d->hasError && d->state.isPrimary; } /*! * Returns \c true if an error occurred; otherwise returns \c false. */ bool DynamicWallpaperDescriptionReader::hasError() const { return d->hasError; } /*! * Returns the human readable description of the last error that occurred. */ QString DynamicWallpaperDescriptionReader::errorString() const { return d->errorString; } plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/dynamicwallpaperdescriptionreader.h000066400000000000000000000013761375723327100314110ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include #include #include class DynamicWallpaperDescriptionReaderPrivate; class DynamicWallpaperDescriptionReader { public: explicit DynamicWallpaperDescriptionReader(const QString &fileName); ~DynamicWallpaperDescriptionReader(); bool atEnd() const; bool readNext(); QImage image() const; KDynamicWallpaperMetaData metaData() const; bool isPrimary() const; bool hasError() const; QString errorString() const; private: QScopedPointer d; }; plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/dynamicwallpaperexifmetadata.cpp000066400000000000000000000217771375723327100307010ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "dynamicwallpaperexifmetadata.h" #include #include #include #include /*! * \class DynamicWallpaperExifMetaData * \brief The DynamicWallpaperExifMetaData class provides a convenient way for computing the position * of the Sun from the Exif metadata in an image. */ class DynamicWallpaperExifMetaDataPrivate : public QSharedData { public: void initialize(const QString &fileName); DynamicWallpaperExifMetaData::MetaDataFields fields; QDateTime birthDateTime; KSunPosition solarCoordinates; }; static ExifEntry *readEntry(ExifData *data, ExifIfd ifd, ExifTag tag) { return exif_content_get_entry(data->ifd[ifd], tag); } static QByteArray readAscii(ExifData *data, ExifIfd ifd, ExifTag tag) { ExifEntry *entry = readEntry(data, ifd, tag); if (!entry) return QByteArray(); QByteArray buffer(1024, 0); exif_entry_get_value(entry, buffer.data(), buffer.size()); return buffer; } /*! * \internal * * Converts \a degrees, \a minutes, and \a seconds to the decimal degrees. */ static qreal degreesMinutesSecondsToDecimalDegrees(const ExifRational °rees, const ExifRational &minutes, const ExifRational &seconds) { const qreal decimalDegrees = qreal(degrees.numerator) / degrees.denominator; const qreal decimalMinutes = qreal(minutes.numerator) / minutes.denominator / 60; const qreal decimalSeconds = qreal(seconds.numerator) / seconds.denominator / 3600; return decimalDegrees + decimalMinutes + decimalSeconds; } static bool readGpsLatitude(ExifData *data, qreal *result) { const ExifEntry *latitude = readEntry(data, EXIF_IFD_GPS, ExifTag(EXIF_TAG_GPS_LATITUDE)); if (!latitude) return false; const QByteArray reference = readAscii(data, EXIF_IFD_GPS, ExifTag(EXIF_TAG_GPS_LATITUDE_REF)); if (reference.isNull()) return false; const ExifByteOrder byteOrder = exif_data_get_byte_order(data); const uint stride = exif_format_get_size(latitude->format); const ExifRational degrees = exif_get_rational(latitude->data + stride * 0, byteOrder); const ExifRational minutes = exif_get_rational(latitude->data + stride * 1, byteOrder); const ExifRational seconds = exif_get_rational(latitude->data + stride * 2, byteOrder); *result = degreesMinutesSecondsToDecimalDegrees(degrees, minutes, seconds); if (reference[0] == 'S') *result *= -1; return true; } static bool readGpsLongitude(ExifData *data, qreal *result) { const ExifEntry *longitude = readEntry(data, EXIF_IFD_GPS, ExifTag(EXIF_TAG_GPS_LONGITUDE)); if (!longitude) return false; const QByteArray reference = readAscii(data, EXIF_IFD_GPS, ExifTag(EXIF_TAG_GPS_LONGITUDE_REF)); if (reference.isNull()) return false; const ExifByteOrder byteOrder = exif_data_get_byte_order(data); const uint stride = exif_format_get_size(longitude->format); const ExifRational degrees = exif_get_rational(longitude->data + stride * 0, byteOrder); const ExifRational minutes = exif_get_rational(longitude->data + stride * 1, byteOrder); const ExifRational seconds = exif_get_rational(longitude->data + stride * 2, byteOrder); *result = degreesMinutesSecondsToDecimalDegrees(degrees, minutes, seconds); if (reference[0] == 'W') *result *= -1; return true; } static bool readGpsCoordinates(ExifData *data, QGeoCoordinate *coordinates) { qreal latitude, longitude; if (!readGpsLatitude(data, &latitude)) return false; if (!readGpsLongitude(data, &longitude)) return false; *coordinates = QGeoCoordinate(latitude, longitude); return true; } /*! * \internal * * Determines the date and the time when the picture was taken. Note that the TimeZoneOffset tag * is required because the position of the Sun is computed based on time and location. */ static bool readExifDateTime(ExifData *data, QDateTime *result) { QByteArray dateTimeAscii = readAscii(data, EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_ORIGINAL); if (dateTimeAscii.isNull()) dateTimeAscii = readAscii(data, EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME); if (dateTimeAscii.isNull()) return false; const ExifEntry *timeZoneOffset = readEntry(data, EXIF_IFD_EXIF, EXIF_TAG_TIME_ZONE_OFFSET); if (!timeZoneOffset) return false; const QDateTime localDateTime = QDateTime::fromString(dateTimeAscii, QStringLiteral("yyyy:MM:dd hh:mm:ss")); if (!localDateTime.isValid()) return false; const ExifByteOrder byteOrder = exif_data_get_byte_order(data); const QTimeZone timeZone(exif_get_sshort(timeZoneOffset->data, byteOrder) * 86400); *result = QDateTime(localDateTime.date(), localDateTime.time(), timeZone); return true; } /*! * \internal * * Determines the date and the time when the picture was taken based on the GPS timestamp. */ static bool readGpsDateTime(ExifData *data, QDateTime *result) { const QByteArray dateAscii = readAscii(data, EXIF_IFD_GPS, ExifTag(EXIF_TAG_GPS_DATE_STAMP)); if (dateAscii.isNull()) return false; const ExifEntry *timeEntry = readEntry(data, EXIF_IFD_GPS, ExifTag(EXIF_TAG_GPS_TIME_STAMP)); if (!timeEntry) return false; const ExifByteOrder byteOrder = exif_data_get_byte_order(data); const uint stride = exif_format_get_size(timeEntry->format); const ExifRational hoursRational = exif_get_rational(timeEntry->data + stride * 0, byteOrder); const ExifRational minutesRational = exif_get_rational(timeEntry->data + stride * 1, byteOrder); const ExifRational secondsRational = exif_get_rational(timeEntry->data + stride * 2, byteOrder); const QTime time = QTime(hoursRational.numerator / hoursRational.denominator, minutesRational.numerator / minutesRational.denominator, secondsRational.numerator / secondsRational.denominator); const QDate date = QDate::fromString(dateAscii, QStringLiteral("yyyy:MM:dd")); *result = QDateTime(date, time, QTimeZone::utc()); return true; } static bool readDateTimeOriginal(ExifData *data, QDateTime *result) { return readExifDateTime(data, result) || readGpsDateTime(data, result); } void DynamicWallpaperExifMetaDataPrivate::initialize(const QString &fileName) { QGeoCoordinate coordinates; ExifData *data = exif_data_new_from_file(fileName.toUtf8()); if (!data) return; if (readDateTimeOriginal(data, &birthDateTime)) fields |= DynamicWallpaperExifMetaData::BirthDateTimeField; if (birthDateTime.isValid() && readGpsCoordinates(data, &coordinates)) { solarCoordinates = KSunPosition(birthDateTime, coordinates); if (solarCoordinates.isValid()) fields |= DynamicWallpaperExifMetaData::SolarCoordinatesField; } exif_data_unref(data); } /*! * Constructs a DynamicWallpaperExifMetaData for an image with the specified \a fileName. */ DynamicWallpaperExifMetaData::DynamicWallpaperExifMetaData(const QString &fileName) : d(new DynamicWallpaperExifMetaDataPrivate) { d->initialize(fileName); } /*! * Constructs a copy of the DynamicWallpaperExifMetaData object. */ DynamicWallpaperExifMetaData::DynamicWallpaperExifMetaData(const DynamicWallpaperExifMetaData &other) : d(other.d) { } /*! * Destructs the DynamicWallpaperExifMetaData object. */ DynamicWallpaperExifMetaData::~DynamicWallpaperExifMetaData() { } /*! * Assigns the value of \p other to a dynamic wallpaper exif metadata object. */ DynamicWallpaperExifMetaData &DynamicWallpaperExifMetaData::operator=(const DynamicWallpaperExifMetaData &other) { d = other.d; return *this; } /*! * Returns the date and the time when the picture was taken or created. */ QDateTime DynamicWallpaperExifMetaData::birthDateTime() const { return d->birthDateTime; } /*! * Returns the Sun's azimuth angle when the picture was taken or created, in decimal degrees. * * If the picture comes with no geo-coordinates metadata, this method will return \c 0. */ qreal DynamicWallpaperExifMetaData::solarAzimuth() const { return d->solarCoordinates.azimuth(); } /*! * Returns the Sun's elevation angle when the picture was taken or created, in decimal degrees. * * If the pictures with with no geo-coordinates metadata, this method will return \c 0. */ qreal DynamicWallpaperExifMetaData::solarElevation() const { return d->solarCoordinates.elevation(); } /*! * Returns a bitmask that indicates which fields are present in the metadata. */ DynamicWallpaperExifMetaData::MetaDataFields DynamicWallpaperExifMetaData::fields() const { return d->fields; } /*! * Returns \c true if the DynamicWallpaperExifMetaData is valid; otherwise returns \c false. */ bool DynamicWallpaperExifMetaData::isValid() const { return d->fields & BirthDateTimeField; } plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/dynamicwallpaperexifmetadata.h000066400000000000000000000017141375723327100303330ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #pragma once #include #include #include class DynamicWallpaperExifMetaDataPrivate; class DynamicWallpaperExifMetaData { public: enum MetaDataField { BirthDateTimeField = 0x1, SolarCoordinatesField = 0x2, }; Q_DECLARE_FLAGS(MetaDataFields, MetaDataField) explicit DynamicWallpaperExifMetaData(const QString &fileName); DynamicWallpaperExifMetaData(const DynamicWallpaperExifMetaData &other); ~DynamicWallpaperExifMetaData(); DynamicWallpaperExifMetaData &operator=(const DynamicWallpaperExifMetaData &other); QDateTime birthDateTime() const; qreal solarAzimuth() const; qreal solarElevation() const; MetaDataFields fields() const; bool isValid() const; private: QSharedDataPointer d; }; plasma5-wallpapers-dynamic-3.3.6/src/tools/builder/main.cpp000066400000000000000000000063701375723327100237040ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Vlad Zahorodnii * * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #include #include #include "dynamicwallpaperdescriptionreader.h" int main(int argc, char **argv) { QCoreApplication app(argc, argv); QCoreApplication::setApplicationName("kdynamicwallpaperbuilder"); QCoreApplication::setApplicationVersion("1.0"); QCommandLineOption qualityOption(QStringLiteral("quality")); qualityOption.setDescription(i18n("Quality of the encoded images (from 0 to 100)")); qualityOption.setValueName(QStringLiteral("quality")); QCommandLineOption outputOption(QStringLiteral("output")); outputOption.setDescription(i18n("Write output to ")); outputOption.setValueName(QStringLiteral("file")); QCommandLineOption discardColorProfileOption(QStringLiteral("discard-color-profile")); discardColorProfileOption.setDescription(i18n("Discard embedded color profile")); QCommandLineOption losslessOption(QStringLiteral("lossless")); losslessOption.setDescription(i18n("Use lossless coding")); QCommandLineParser parser; parser.addHelpOption(); parser.addVersionOption(); parser.addPositionalArgument("json", i18n("Description file to use")); parser.addOption(qualityOption); parser.addOption(outputOption); parser.addOption(discardColorProfileOption); parser.addOption(losslessOption); parser.process(app); if (parser.positionalArguments().count() != 1) parser.showHelp(-1); KDynamicWallpaperWriter writer; writer.setLossless(parser.isSet(losslessOption)); writer.setCodec(KDynamicWallpaperWriter::HEVC); if (parser.isSet(qualityOption)) { bool ok; const int quality = parser.value(qualityOption).toInt(&ok); if (ok) writer.setQuality(quality); else parser.showHelp(-1); } DynamicWallpaperDescriptionReader reader(parser.positionalArguments().first()); QString targetFileName = parser.value(outputOption); if (targetFileName.isEmpty()) targetFileName = QStringLiteral("wallpaper.heic"); if (!writer.begin(targetFileName)) { qWarning() << writer.errorString(); return -1; } while (!reader.atEnd()) { if (!reader.readNext()) break; KDynamicWallpaperWriter::WriteOptions writeOptions; if (!parser.isSet(discardColorProfileOption)) writeOptions |= KDynamicWallpaperWriter::PreserveColorProfile; if (reader.isPrimary()) writeOptions |= KDynamicWallpaperWriter::Primary; if (!writer.write(reader.image(), reader.metaData(), writeOptions)) break; } writer.end(); if (reader.hasError() || writer.error() != KDynamicWallpaperWriter::NoError) { if (reader.hasError()) qWarning() << qPrintable(reader.errorString()); if (writer.error() != KDynamicWallpaperWriter::NoError) qWarning() << writer.errorString(); QFile::remove(targetFileName); return -1; } return 0; } plasma5-wallpapers-dynamic-3.3.6/src/translations/000077500000000000000000000000001375723327100222015ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/translations/CMakeLists.txt000066400000000000000000000020351375723327100247410ustar00rootroot00000000000000find_program(GETTEXT_MSGFMT_EXECUTABLE msgfmt) if (NOT GETTEXT_MSGFMT_EXECUTABLE) message(WARNING "-- msgfmt: not found. Translations will not be installed") else () set(catalogname plasma_wallpaper_com.github.zzag.dynamic) add_custom_target(translations ALL) file(GLOB PO_FILES po/*.po) foreach (_po ${PO_FILES}) get_filename_component(_filename ${_po} NAME) string(REGEX REPLACE "^${catalogname}_?" "" _langCode ${_filename}) string(REGEX REPLACE "\\.po$" "" _langCode ${_langCode}) if (_langCode) get_filename_component(_lang ${_po} NAME_WE) set(_gmo ${CMAKE_CURRENT_BINARY_DIR}/${_lang}.gmo) add_custom_command(TARGET translations COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} --check -o ${_gmo} ${_po} DEPENDS ${_po}) install(FILES ${_gmo} DESTINATION ${LOCALE_INSTALL_DIR}/${KF5_LOCALE_PREFIX}${_langCode}/LC_MESSAGES/ RENAME ${catalogname}.mo) endif() endforeach() endif() plasma5-wallpapers-dynamic-3.3.6/src/translations/extract-messages.sh000066400000000000000000000027611375723327100260220ustar00rootroot00000000000000#!/usr/bin/bash cd "$(dirname $0)" BASEDIR="$(pwd)" SRCDIR="$(dirname $BASEDIR)" function xgettext_wrapper { xgettext --copyright-holder="This file is copyright:" \ --package-name=plasma5-wallpapers-dynamic \ --msgid-bugs-address=https://github.com/zzag/plasma5-wallpapers-dynamic/issues \ --from-code=UTF-8 \ -C --kde \ -ci18n \ -ki18n:1 -ki18nc:1c,2 -ki18np:1,2 -ki18ncp:1c,2,3 \ -ki18nd:2 -ki18ndc:2c,3 -ki18ndp:2,3 -ki18ndcp:2c,3,4 \ -kki18n:1 -kki18nc:1c,2 -kki18np:1,2 -kki18ncp:1c,2,3 \ -kki18nd:2 -kki18ndc:2c,3 -kki18ndp:2,3 -kki18ndcp:2c,3,4 \ -kxi18n:1 -kxi18nc:1c,2 -kxi18np:1,2 -kxi18ncp:1c,2,3 \ -kxi18nd:2 -kxi18ndc:2c,3 -kxi18ndp:2,3 -kxi18ndcp:2c,3,4 \ -kkxi18n:1 -kkxi18nc:1c,2 -kkxi18np:1,2 -kkxi18ncp:1c,2,3 \ -kkxi18nd:2 -kkxi18ndc:2c,3 -kkxi18ndp:2,3 -kkxi18ndcp:2c,3,4 \ -kI18N_NOOP:1 -kI18NC_NOOP:1c,2 \ -kI18N_NOOP2:1c,2 -kI18N_NOOP2_NOSTRIP:1c,2 \ -ktr2i18n:1 -ktr2xi18n:1 \ "$@" } export -f xgettext_wrapper podir=$BASEDIR/po files=`find $SRCDIR -name Messages.sh` dirs=`for i in $files; do echo \`dirname $i\`; done | sort -u` for subdir in $dirs; do cd $subdir if test -f Messages.sh; then podir=$podir srcdir=. XGETTEXT="xgettext_wrapper" bash Messages.sh fi done pofiles=`find $podir -name \*.po` for po in $pofiles; do msgmerge -o $po.new $po $podir/plasma_wallpaper_com.github.zzag.dynamic.pot mv $po.new $po done plasma5-wallpapers-dynamic-3.3.6/src/translations/po/000077500000000000000000000000001375723327100226175ustar00rootroot00000000000000plasma5-wallpapers-dynamic-3.3.6/src/translations/po/it.po000066400000000000000000000124431375723327100235770ustar00rootroot00000000000000# This file is distributed under the same license as the plasma5-wallpapers-dynamic package. # # Federico Robin , 2019. msgid "" msgstr "" "Project-Id-Version: plasma5-wallpapers-dynamic\n" "Report-Msgid-Bugs-To: https://github.com/zzag/plasma5-wallpapers-dynamic/" "issues\n" "POT-Creation-Date: 2020-06-20 12:58+0300\n" "PO-Revision-Date: 2019-10-26 15:00+0200\n" "Last-Translator: Federico Robin \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: declarative/dynamicwallpaperhandler.cpp:210 #, fuzzy, kde-format msgid "%1 is not a dynamic wallpaper" msgstr "Non è stato possibile inizializzare il wallpaper dinamico: %1" #: plugins/kpackage-integration/dynamicwallpaperpackagestructure.cpp:22 #, kde-format msgid "Dynamic wallpaper files" msgstr "" #: plugins/kpackage-integration/dynamicwallpaperpackagestructure.cpp:38 #, kde-format msgid "Dynamic wallpaper file" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:99 #, kde-format msgid "The device is not open for writing" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:113 lib/kdynamicwallpaperreader.cpp:134 #, kde-format msgid "Failed to allocate HEIF context" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:121 #, kde-format msgid "No suitable encoder" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:128 #, kde-format msgid "Failed to set the lossless parameter: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:135 #, kde-format msgid "Failed to set the quality parameter: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:217 #, kde-format msgid "Failed to create a heif_image: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:224 #, kde-format msgid "Failed to add a color plane: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:233 #, kde-format msgid "Failed to set ICC profile: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:249 #, kde-format msgid "Failed to encode image: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:257 #, kde-format msgid "Failed to add XMP metadata: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:265 #, kde-format msgid "Failed to set the primary image: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:437 lib/kdynamicwallpaperreader.cpp:416 #, kde-format msgid "No error" msgstr "" #: lib/kdynamicwallpaperreader.cpp:113 #, kde-format msgid "No assigned device" msgstr "" #: lib/kdynamicwallpaperreader.cpp:120 #, kde-format msgid "The device is not open for reading" msgstr "" #: lib/kdynamicwallpaperreader.cpp:141 #, kde-format msgid "Invalid HEIF file: %1" msgstr "" #: lib/kdynamicwallpaperreader.cpp:179 lib/kdynamicwallpaperreader.cpp:248 #, kde-format msgid "Failed to get image handle: %1" msgstr "" #: lib/kdynamicwallpaperreader.cpp:260 #, kde-format msgid "Failed to decode image: %1" msgstr "" #: package/contents/ui/config.qml:37 #, kde-format msgid "Fill Mode:" msgstr "Modalità di riempimento" #: package/contents/ui/config.qml:41 #, kde-format msgid "Scaled and Cropped" msgstr "Scalato e Tagliato" #: package/contents/ui/config.qml:45 #, kde-format msgid "Scaled" msgstr "Scalato" #: package/contents/ui/config.qml:49 #, kde-format msgid "Scaled, Keep Proportions" msgstr "Scalato, Mantieni Proporzioni" #: package/contents/ui/config.qml:53 #, kde-format msgid "Centered" msgstr "Centrato" #: package/contents/ui/config.qml:57 #, kde-format msgid "Tiled" msgstr "Affiancato" #: package/contents/ui/config.qml:76 #, kde-format msgid "Automatically detect location" msgstr "" #: package/contents/ui/config.qml:80 package/contents/ui/config.qml:101 #, kde-format msgid "Latitude:" msgstr "Latitudine:" #: package/contents/ui/config.qml:90 package/contents/ui/config.qml:110 #, kde-format msgid "Longitude:" msgstr "Longitudine:" #: package/contents/ui/config.qml:119 #, kde-format msgid "Update Every:" msgstr "Aggiorna Ogni:" #: package/contents/ui/config.qml:122 #, fuzzy, kde-format msgid " minute" msgid_plural " minutes" msgstr[0] " minuto" msgstr[1] " minuti" #: package/contents/ui/config.qml:144 #, kde-format msgctxt " by ()" msgid "By %1 (%2)" msgstr "Da %1 (%2)" #: package/contents/ui/config.qml:146 #, kde-format msgctxt " ()" msgid "%1 (%2)" msgstr "%1 (%2)" #: package/contents/ui/config.qml:152 #, kde-format msgid "Open Containing Folder" msgstr "Mostra nel Gestore File" #: package/contents/ui/config.qml:157 #, kde-format msgid "Restore Wallpaper" msgstr "Ripristina Wallpaper" #: package/contents/ui/config.qml:163 #, kde-format msgid "Remove Wallpaper" msgstr "Rimuovi Wallpaper" #: package/contents/ui/config.qml:194 #, kde-format msgid "Open Wallpaper" msgstr "Apri Wallpaper" #: package/contents/ui/config.qml:196 #, kde-format msgid "HEIF Image Files (*.heic *.heif)" msgstr "" #: package/contents/ui/config.qml:213 #, kde-format msgid "Add Wallpaper..." msgstr "Aggiungi Wallpaper..." #: tools/builder/main.cpp:26 #, kde-format msgid "Quality of the encoded images (from 0 to 100)" msgstr "" #: tools/builder/main.cpp:30 #, kde-format msgid "Write output to " msgstr "" #: tools/builder/main.cpp:34 #, kde-format msgid "Discard embedded color profile" msgstr "" #: tools/builder/main.cpp:37 #, kde-format msgid "Use lossless coding" msgstr "" #: tools/builder/main.cpp:42 #, kde-format msgid "Description file to use" msgstr "" plasma5-wallpapers-dynamic-3.3.6/src/translations/po/plasma_wallpaper_com.github.zzag.dynamic.pot000066400000000000000000000121031375723327100334000ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR This file is copyright: # This file is distributed under the same license as the plasma5-wallpapers-dynamic package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: plasma5-wallpapers-dynamic\n" "Report-Msgid-Bugs-To: https://github.com/zzag/plasma5-wallpapers-dynamic/" "issues\n" "POT-Creation-Date: 2020-06-20 12:58+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: declarative/dynamicwallpaperhandler.cpp:210 #, kde-format msgid "%1 is not a dynamic wallpaper" msgstr "" #: plugins/kpackage-integration/dynamicwallpaperpackagestructure.cpp:22 #, kde-format msgid "Dynamic wallpaper files" msgstr "" #: plugins/kpackage-integration/dynamicwallpaperpackagestructure.cpp:38 #, kde-format msgid "Dynamic wallpaper file" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:99 #, kde-format msgid "The device is not open for writing" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:113 lib/kdynamicwallpaperreader.cpp:134 #, kde-format msgid "Failed to allocate HEIF context" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:121 #, kde-format msgid "No suitable encoder" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:128 #, kde-format msgid "Failed to set the lossless parameter: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:135 #, kde-format msgid "Failed to set the quality parameter: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:217 #, kde-format msgid "Failed to create a heif_image: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:224 #, kde-format msgid "Failed to add a color plane: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:233 #, kde-format msgid "Failed to set ICC profile: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:249 #, kde-format msgid "Failed to encode image: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:257 #, kde-format msgid "Failed to add XMP metadata: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:265 #, kde-format msgid "Failed to set the primary image: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:437 lib/kdynamicwallpaperreader.cpp:416 #, kde-format msgid "No error" msgstr "" #: lib/kdynamicwallpaperreader.cpp:113 #, kde-format msgid "No assigned device" msgstr "" #: lib/kdynamicwallpaperreader.cpp:120 #, kde-format msgid "The device is not open for reading" msgstr "" #: lib/kdynamicwallpaperreader.cpp:141 #, kde-format msgid "Invalid HEIF file: %1" msgstr "" #: lib/kdynamicwallpaperreader.cpp:179 lib/kdynamicwallpaperreader.cpp:248 #, kde-format msgid "Failed to get image handle: %1" msgstr "" #: lib/kdynamicwallpaperreader.cpp:260 #, kde-format msgid "Failed to decode image: %1" msgstr "" #: package/contents/ui/config.qml:37 #, kde-format msgid "Fill Mode:" msgstr "" #: package/contents/ui/config.qml:41 #, kde-format msgid "Scaled and Cropped" msgstr "" #: package/contents/ui/config.qml:45 #, kde-format msgid "Scaled" msgstr "" #: package/contents/ui/config.qml:49 #, kde-format msgid "Scaled, Keep Proportions" msgstr "" #: package/contents/ui/config.qml:53 #, kde-format msgid "Centered" msgstr "" #: package/contents/ui/config.qml:57 #, kde-format msgid "Tiled" msgstr "" #: package/contents/ui/config.qml:76 #, kde-format msgid "Automatically detect location" msgstr "" #: package/contents/ui/config.qml:80 package/contents/ui/config.qml:101 #, kde-format msgid "Latitude:" msgstr "" #: package/contents/ui/config.qml:90 package/contents/ui/config.qml:110 #, kde-format msgid "Longitude:" msgstr "" #: package/contents/ui/config.qml:119 #, kde-format msgid "Update Every:" msgstr "" #: package/contents/ui/config.qml:122 #, kde-format msgid " minute" msgid_plural " minutes" msgstr[0] "" msgstr[1] "" #: package/contents/ui/config.qml:144 #, kde-format msgctxt " by ()" msgid "By %1 (%2)" msgstr "" #: package/contents/ui/config.qml:146 #, kde-format msgctxt " ()" msgid "%1 (%2)" msgstr "" #: package/contents/ui/config.qml:152 #, kde-format msgid "Open Containing Folder" msgstr "" #: package/contents/ui/config.qml:157 #, kde-format msgid "Restore Wallpaper" msgstr "" #: package/contents/ui/config.qml:163 #, kde-format msgid "Remove Wallpaper" msgstr "" #: package/contents/ui/config.qml:194 #, kde-format msgid "Open Wallpaper" msgstr "" #: package/contents/ui/config.qml:196 #, kde-format msgid "HEIF Image Files (*.heic *.heif)" msgstr "" #: package/contents/ui/config.qml:213 #, kde-format msgid "Add Wallpaper..." msgstr "" #: tools/builder/main.cpp:26 #, kde-format msgid "Quality of the encoded images (from 0 to 100)" msgstr "" #: tools/builder/main.cpp:30 #, kde-format msgid "Write output to " msgstr "" #: tools/builder/main.cpp:34 #, kde-format msgid "Discard embedded color profile" msgstr "" #: tools/builder/main.cpp:37 #, kde-format msgid "Use lossless coding" msgstr "" #: tools/builder/main.cpp:42 #, kde-format msgid "Description file to use" msgstr "" plasma5-wallpapers-dynamic-3.3.6/src/translations/po/ru.po000066400000000000000000000134761375723327100236200ustar00rootroot00000000000000# This file is distributed under the same license as the plasma5-wallpapers-dynamic package. # # Vlad Zahorodnii , 2019, 2020. msgid "" msgstr "" "Project-Id-Version: plasma5-wallpapers-dynamic\n" "Report-Msgid-Bugs-To: https://github.com/zzag/plasma5-wallpapers-dynamic/" "issues\n" "POT-Creation-Date: 2020-06-20 12:58+0300\n" "PO-Revision-Date: 2020-02-16 19:45+0200\n" "Last-Translator: Vlad Zahorodnii \n" "Language-Team: Vlad Zahorodnii \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n" "%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Lokalize 19.12.2\n" #: declarative/dynamicwallpaperhandler.cpp:210 #, fuzzy, kde-format msgid "%1 is not a dynamic wallpaper" msgstr "Невозможно инициализировать обои" #: plugins/kpackage-integration/dynamicwallpaperpackagestructure.cpp:22 #, kde-format msgid "Dynamic wallpaper files" msgstr "" #: plugins/kpackage-integration/dynamicwallpaperpackagestructure.cpp:38 #, kde-format msgid "Dynamic wallpaper file" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:99 #, kde-format msgid "The device is not open for writing" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:113 lib/kdynamicwallpaperreader.cpp:134 #, kde-format msgid "Failed to allocate HEIF context" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:121 #, kde-format msgid "No suitable encoder" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:128 #, kde-format msgid "Failed to set the lossless parameter: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:135 #, kde-format msgid "Failed to set the quality parameter: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:217 #, kde-format msgid "Failed to create a heif_image: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:224 #, kde-format msgid "Failed to add a color plane: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:233 #, kde-format msgid "Failed to set ICC profile: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:249 #, kde-format msgid "Failed to encode image: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:257 #, kde-format msgid "Failed to add XMP metadata: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:265 #, kde-format msgid "Failed to set the primary image: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:437 lib/kdynamicwallpaperreader.cpp:416 #, kde-format msgid "No error" msgstr "" #: lib/kdynamicwallpaperreader.cpp:113 #, kde-format msgid "No assigned device" msgstr "" #: lib/kdynamicwallpaperreader.cpp:120 #, kde-format msgid "The device is not open for reading" msgstr "" #: lib/kdynamicwallpaperreader.cpp:141 #, kde-format msgid "Invalid HEIF file: %1" msgstr "" #: lib/kdynamicwallpaperreader.cpp:179 lib/kdynamicwallpaperreader.cpp:248 #, kde-format msgid "Failed to get image handle: %1" msgstr "" #: lib/kdynamicwallpaperreader.cpp:260 #, kde-format msgid "Failed to decode image: %1" msgstr "" #: package/contents/ui/config.qml:37 #, kde-format msgid "Fill Mode:" msgstr "Расположение:" #: package/contents/ui/config.qml:41 #, kde-format msgid "Scaled and Cropped" msgstr "Масштабирование с кадрированием" #: package/contents/ui/config.qml:45 #, kde-format msgid "Scaled" msgstr "На весь рабочий стол" #: package/contents/ui/config.qml:49 #, kde-format msgid "Scaled, Keep Proportions" msgstr "По центру пропорционально" #: package/contents/ui/config.qml:53 #, kde-format msgid "Centered" msgstr "По центру" #: package/contents/ui/config.qml:57 #, kde-format msgid "Tiled" msgstr "Черепицей" #: package/contents/ui/config.qml:76 #, kde-format msgid "Automatically detect location" msgstr "Автоматически определить местоположение" #: package/contents/ui/config.qml:80 package/contents/ui/config.qml:101 #, kde-format msgid "Latitude:" msgstr "Широта:" #: package/contents/ui/config.qml:90 package/contents/ui/config.qml:110 #, kde-format msgid "Longitude:" msgstr "Долгота:" #: package/contents/ui/config.qml:119 #, kde-format msgid "Update Every:" msgstr "Обновлять каждые:" #: package/contents/ui/config.qml:122 #, kde-format msgid " minute" msgid_plural " minutes" msgstr[0] " минуту" msgstr[1] " минуты" msgstr[2] " минут" msgstr[3] " минуту" #: package/contents/ui/config.qml:144 #, kde-format msgctxt " by ()" msgid "By %1 (%2)" msgstr "От %1 (%2)" #: package/contents/ui/config.qml:146 #, kde-format msgctxt " ()" msgid "%1 (%2)" msgstr "%1 (%2)" #: package/contents/ui/config.qml:152 #, kde-format msgid "Open Containing Folder" msgstr "Открыть содержащую объект папку" #: package/contents/ui/config.qml:157 #, kde-format msgid "Restore Wallpaper" msgstr "Вернуть обои в этот список" #: package/contents/ui/config.qml:163 #, kde-format msgid "Remove Wallpaper" msgstr "Удалить обои" #: package/contents/ui/config.qml:194 #, kde-format msgid "Open Wallpaper" msgstr "Открытие обоев" #: package/contents/ui/config.qml:196 #, kde-format msgid "HEIF Image Files (*.heic *.heif)" msgstr "" #: package/contents/ui/config.qml:213 #, kde-format msgid "Add Wallpaper..." msgstr "Добавить обои..." #: tools/builder/main.cpp:26 #, kde-format msgid "Quality of the encoded images (from 0 to 100)" msgstr "" #: tools/builder/main.cpp:30 #, kde-format msgid "Write output to " msgstr "" #: tools/builder/main.cpp:34 #, kde-format msgid "Discard embedded color profile" msgstr "" #: tools/builder/main.cpp:37 #, kde-format msgid "Use lossless coding" msgstr "" #: tools/builder/main.cpp:42 #, kde-format msgid "Description file to use" msgstr "" plasma5-wallpapers-dynamic-3.3.6/src/translations/po/uk.po000066400000000000000000000135321375723327100236020ustar00rootroot00000000000000# This file is distributed under the same license as the plasma5-wallpapers-dynamic package. # # Vlad Zahorodnii , 2019, 2020. msgid "" msgstr "" "Project-Id-Version: plasma5-wallpapers-dynamic\n" "Report-Msgid-Bugs-To: https://github.com/zzag/plasma5-wallpapers-dynamic/" "issues\n" "POT-Creation-Date: 2020-06-20 12:58+0300\n" "PO-Revision-Date: 2020-02-16 19:42+0200\n" "Last-Translator: Vlad Zahorodnii \n" "Language-Team: Vlad Zahorodnii \n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n" "%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Lokalize 19.12.2\n" #: declarative/dynamicwallpaperhandler.cpp:210 #, fuzzy, kde-format msgid "%1 is not a dynamic wallpaper" msgstr "Неможливо ініціалізувати динамічні шпалери" #: plugins/kpackage-integration/dynamicwallpaperpackagestructure.cpp:22 #, kde-format msgid "Dynamic wallpaper files" msgstr "" #: plugins/kpackage-integration/dynamicwallpaperpackagestructure.cpp:38 #, kde-format msgid "Dynamic wallpaper file" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:99 #, kde-format msgid "The device is not open for writing" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:113 lib/kdynamicwallpaperreader.cpp:134 #, kde-format msgid "Failed to allocate HEIF context" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:121 #, kde-format msgid "No suitable encoder" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:128 #, kde-format msgid "Failed to set the lossless parameter: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:135 #, kde-format msgid "Failed to set the quality parameter: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:217 #, kde-format msgid "Failed to create a heif_image: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:224 #, kde-format msgid "Failed to add a color plane: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:233 #, kde-format msgid "Failed to set ICC profile: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:249 #, kde-format msgid "Failed to encode image: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:257 #, kde-format msgid "Failed to add XMP metadata: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:265 #, kde-format msgid "Failed to set the primary image: %1" msgstr "" #: lib/kdynamicwallpaperwriter.cpp:437 lib/kdynamicwallpaperreader.cpp:416 #, kde-format msgid "No error" msgstr "" #: lib/kdynamicwallpaperreader.cpp:113 #, kde-format msgid "No assigned device" msgstr "" #: lib/kdynamicwallpaperreader.cpp:120 #, kde-format msgid "The device is not open for reading" msgstr "" #: lib/kdynamicwallpaperreader.cpp:141 #, kde-format msgid "Invalid HEIF file: %1" msgstr "" #: lib/kdynamicwallpaperreader.cpp:179 lib/kdynamicwallpaperreader.cpp:248 #, kde-format msgid "Failed to get image handle: %1" msgstr "" #: lib/kdynamicwallpaperreader.cpp:260 #, kde-format msgid "Failed to decode image: %1" msgstr "" #: package/contents/ui/config.qml:37 #, kde-format msgid "Fill Mode:" msgstr "Розташування:" #: package/contents/ui/config.qml:41 #, kde-format msgid "Scaled and Cropped" msgstr "Масштабоване та обрізане" #: package/contents/ui/config.qml:45 #, kde-format msgid "Scaled" msgstr "Масштабоване" #: package/contents/ui/config.qml:49 #, kde-format msgid "Scaled, Keep Proportions" msgstr "Масштабоване, зі збереженням пропорцій" #: package/contents/ui/config.qml:53 #, kde-format msgid "Centered" msgstr "За центром" #: package/contents/ui/config.qml:57 #, kde-format msgid "Tiled" msgstr "Плиткою" #: package/contents/ui/config.qml:76 #, kde-format msgid "Automatically detect location" msgstr "Визначити місцезнаходження автоматично" #: package/contents/ui/config.qml:80 package/contents/ui/config.qml:101 #, kde-format msgid "Latitude:" msgstr "Широта:" #: package/contents/ui/config.qml:90 package/contents/ui/config.qml:110 #, kde-format msgid "Longitude:" msgstr "Довгота:" #: package/contents/ui/config.qml:119 #, kde-format msgid "Update Every:" msgstr "Обновлювати кожні:" #: package/contents/ui/config.qml:122 #, kde-format msgid " minute" msgid_plural " minutes" msgstr[0] " хвилину" msgstr[1] " хвилини" msgstr[2] " хвилин" msgstr[3] " хвилину" #: package/contents/ui/config.qml:144 #, kde-format msgctxt " by ()" msgid "By %1 (%2)" msgstr "Автор - %1 (%2)" #: package/contents/ui/config.qml:146 #, kde-format msgctxt " ()" msgid "%1 (%2)" msgstr "%1 (%2)" #: package/contents/ui/config.qml:152 #, kde-format msgid "Open Containing Folder" msgstr "Відкрити теку об'єкта" #: package/contents/ui/config.qml:157 #, kde-format msgid "Restore Wallpaper" msgstr "Відновити початкові шпалери" #: package/contents/ui/config.qml:163 #, kde-format msgid "Remove Wallpaper" msgstr "Вилучити шпалери" #: package/contents/ui/config.qml:194 #, kde-format msgid "Open Wallpaper" msgstr "Відкрити шпалери" #: package/contents/ui/config.qml:196 #, kde-format msgid "HEIF Image Files (*.heic *.heif)" msgstr "" #: package/contents/ui/config.qml:213 #, kde-format msgid "Add Wallpaper..." msgstr "Додати шпалери..." #: tools/builder/main.cpp:26 #, kde-format msgid "Quality of the encoded images (from 0 to 100)" msgstr "" #: tools/builder/main.cpp:30 #, kde-format msgid "Write output to " msgstr "" #: tools/builder/main.cpp:34 #, kde-format msgid "Discard embedded color profile" msgstr "" #: tools/builder/main.cpp:37 #, kde-format msgid "Use lossless coding" msgstr "" #: tools/builder/main.cpp:42 #, kde-format msgid "Description file to use" msgstr ""