pax_global_header00006660000000000000000000000064127476726020014527gustar00rootroot0000000000000052 comment=fbb7b39df4e179ad30e275b885fd412140df2178 Grapefruit-0.1a4/000077500000000000000000000000001274767260200137245ustar00rootroot00000000000000Grapefruit-0.1a4/CHANGES000066400000000000000000000030151274767260200147160ustar00rootroot000000000000002016-08-01 - Realease 0.1a4 - Fixed missing attribute grapefruit_test.py 2008-06-15 - Released 0.1a3 - Added Gradient 2008-06-01 - Fixed overflow in alpha blending. 2008-05-29 - Added Saturate/Desaturate. - Added MonochromeScheme - Added the mode parameter to the generation methods to choose the color wheel use for the generation (ryb/rgb). 2008-05-28 - Added the RGB<->RYB hue conversion. - Added an angle parameter to the tetrad scheme to control the shape of the rectangle. 2008-05-27 - Released 0.1a2 2008-05-24 - Fixed the HSL->RGB conversion (the modulo in the hue conversion was 60 instead of 6.0!) Updated the unit tests (which were wrong!) 2008-05-24 Released 0.1a1 - Convert the documentation to Sphinx - Completed the unit tests - Fixed some stupid typos 2008-05-22 - Refactored pretty much everything to more standard "Python coding style". - Replaced the global variables by Color properties. - Moved the module functions to static methods of Color. - Completed the CIE white point dictionary to include all the standard illuminants. - Added doctest for all the functions. - Fixed the conversions factors to get better results (more exact and more symmetric). - Changed the range of the L component from [0~1] to [0~100] (as it should have been). - Added packaging data and setup. - Changed the structure of the unit tests. 2008-05-08 Released 0.1a0 - Initial checkin of grapefruit Grapefruit-0.1a4/COPYING000066400000000000000000000010571274767260200147620ustar00rootroot00000000000000Copyright (c) 2008, Xavier Basty Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.Grapefruit-0.1a4/LICENSE000066400000000000000000000243251274767260200147370ustar00rootroot00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.Grapefruit-0.1a4/README.rst000066400000000000000000000034101274767260200154110ustar00rootroot00000000000000===================== README for GrapeFruit ===================== GrapeFruit is a pure Python module that let you easily manipulate and convert color information. Its Primary goal is to be *natural* and *flexible*. The following color systems are supported by GrapeFruit: * RGB (sRGB) * HSL * HSV * YIQ * YUV * CIE-XYZ * CIE-LAB (with the illuminant you want) * CMY * CMYK * HTML/CSS color definition (#RRGGBB, #RGB or the X11 color name) * RYB (artistic color wheel Installing ============ **From the sources:** Download the latest grapefruit library from: https://github.com/xav/Grapefruit Untar the source distribution and run:: $ python setup.py build $ python setup.py install Testing ========= With setuptools installed:: $ python setup.py test Without setuptools installed:: $ python grapefruit_test.py Documentation =============== You can download a compiled version of the documentation at: https://github.com/xav/Grapefruit/downloads The documentation is generated from reStructuredText sources by Sphinx. If you need to build it, go into the doc folder and run:: make Or, if you're running windows:: makedoc.cmd License ========= :: Copyright (c) 2008, Xavier Basty Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Grapefruit-0.1a4/TODO000066400000000000000000000005301274767260200144120ustar00rootroot00000000000000GrapeFruit's TODO list ---------------------- Patches and bug reports are welcome, just please keep the style consistent with the original source. * grapefruit_test isn't much more that a copy of the doctests right now. Complete it with real unit tests that better define the module behavior. * Add sRGB <-> AdobeRGB conversion. Grapefruit-0.1a4/doc/000077500000000000000000000000001274767260200144715ustar00rootroot00000000000000Grapefruit-0.1a4/doc/Makefile000066400000000000000000000044461274767260200161410ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html web pickle htmlhelp latex changes linkcheck help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " pickle to make pickle files (usable by e.g. sphinx-web)" @echo " htmlhelp to make HTML files and a HTML help project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview over all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" clean: -rm -rf _build/* html: mkdir -p _build/html _build/doctrees $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html @echo @echo "Build finished. The HTML pages are in _build/html." pickle: mkdir -p _build/pickle _build/doctrees $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle @echo @echo "Build finished; now you can process the pickle files or run" @echo " sphinx-web _build/pickle" @echo "to start the sphinx-web server." web: pickle htmlhelp: mkdir -p _build/htmlhelp _build/doctrees $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in _build/htmlhelp." latex: mkdir -p _build/latex _build/doctrees $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex @echo @echo "Build finished; the LaTeX files are in _build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: mkdir -p _build/changes _build/doctrees $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes @echo @echo "The overview file is in _build/changes." linkcheck: mkdir -p _build/linkcheck _build/doctrees $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in _build/linkcheck/output.txt." Grapefruit-0.1a4/doc/_static/000077500000000000000000000000001274767260200161175ustar00rootroot00000000000000Grapefruit-0.1a4/doc/_static/GrapeFruit.png000066400000000000000000005021021274767260200206750ustar00rootroot00000000000000PNG  IHDRk pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-gAMA|Q cHRMz%u0`:o_Fy]IDATxidu~{soěs#AIDQ$%JVْZPUewr<ˣZU.r/[\rylIeɖ[Mky@  o{gވxe @(2^xqηTǷۛyO-eyE~cDoֽ|9…9Ѣ `:tYzZ31Wۛf_[5}%Z|?v %o ;λf)>T u`-wӓ9ƑǷ7퍎9ۛcÖӣnwG=~k رei+b7sOϡo=9w9^:ov| xD1^} %o_C7 ;W)v.1#:뿷,Ýǧۛv<>}_q8?ƑǷ3s.zxG"Zc85bGCr; o_QxEch14 7uf?wD=ՎI1vLBt0yQlvz.BstS+`šc~|ioBY^wȒ;s?ǽMdO|rsCDǷxGC.jEy^ův2\"qE~;]{n;Q =&ˏ`1w;@8qn2t#ooa숻<2}vG Pji((~5{UkZ 9AIHر׍##}phJ9W~ o_xhtw)x7qvxGު.xGU8U1|:·;vG> všxg^~ #}记kjO>FǷ7k\* \MuB88&Ew<:eD 5;e#PE(OsQB=q>%;#Kr|wG$.D\wG=fOE91Pdf !&?wJN .mi>|Zv/'|ǣ`[(5UAqeŁA b_3(2}`+o|< B oA^ ^|H:Ҽy#8>~OG9>r|;}/;K^~R=챔.Gʐ]8/s+ytWhjI%~V=a〞#R4}Gȷw oeL^ZtrCn;>yR2g.qq#=qTx "o_}khα1|Q梡x!4\鸂#Z1=^ n4R ?u\#J0Gܯǻ y-#kapwct\Ӝ]9y~-'-֔y"-\GZ ǯMhzv /1o&B5KaT&B}uGeB1< *3NPp#!N 5G_uzaiDg/|Df{;Ǝ)Zg\nzq;7sUpyv^+}qmil}pig2 wQK.22YR||>v<3 I'P,U6*>͌ǎx-h~/u ]_OoNy o_.}\~:=McHzI@G"7 D q,v\ʴW\xןm>9}[5aG)g:S@Q~S(D9#wik7?e.vBqzw!E8OUF:p'E:!0-v= HILv́i  hwPAJhs4,=vιY?[|T%@%mb0hB ltc6U ]1SŽ&c zlk;F=*@J#.c&LM=D4 #op9y|ނéGS7Q5bJu:yZ#D1`.yw)* gPP(1f&=;DH_k{.A&_ĎoonS&y\PMmԀ;¬&`R+ ԙD?&Z),+h?YZ)A$YAwvzsʠ<е-qw|"̜ p >:\p>1]:o3ǟeKW:;÷+eċc{lR,qP鄙9h< 蜠(f EȨG$F)Ė9; j},GvAm&%)>"Z%9BCJspќ6trrV# z,>~#ɜE`1|% B|1<u-kgYF0=E#2#7'KH)F|ܜ-h9=ƑǷz#3sϠ=vw͈:[<&֮{d2_Mb^U%a z<2s[J==d&Wk;Ïi;Ё[Dqpb)ĜHmX`3?fw0F?FǷ0|Ŏ uP7JnQ`>B7࣪t ,+-]IHͅ!PD^'ǎ 9vC͈(hӠ:`ҊLI9IQiHBii$`9UU!ݢLi$Ň%9FPʔE{h7'4qqŢd_:X qh޻cvOs.qHAgzF9J'GUbMC.r¾hZт?b1ӗJ &;f)zk Hg 3uLOM5zΩKϧkܟg cy|JiG/whǞT1i;[h"'.MyV˘V!;6ٶ*9/dۑGZw9]&p;y4LBL6Zck DPV!Ǒ;I= yQ4IJI*^$-O&(<IS²H4s9vGښ h+N`ع\zH2:3]b|$&:Չ!YX {suy=̵&/3z=:#NHO uc:oY]tJvA\.1*#5m9ߺ̋"uRNI`#FK4"EPӵ4Y։w:a=x hAIEi[SEz0&bOű5G+:xĀRʤK\A^i)n͓=x~rRǎ\Yy.*]̐tt/m4#]%8gp\gzw H(y oGf&LoOQcf| ī#|\P?E |$]h=F~¯^J9z-_@Rg|yCkǎ:U~Ve^ULnrivNÎS znhh.)$x0ˊ:?q1K!3D&86᜚N-v$JVFN\alv2<ߛfjD>s!} Gt'` co#.UP3g9q o_!8O܋xCcs`:|}VڵG@!f'Yc>v~(f%Vڴ1G8WZCYxرh/QߨuM ;壥=숅c9&*@i0ܭ(1CS;9X"底HDգ?2 TQ UFE$eR k7 ƷU/IkCPDA% =ȇ{ECjSo8҇sϔT5ztP ni6qQc"8A6SЙ$-RϠ~w_^d>RgO9?r3&DΧo#nyMtPQւ9k[췗v|m#ߖ`ǻ#{ԣ,p. 1mGW\1CmNK=U/yI&Q:s(PhJc@czs-1tHڦ?B.3H2 0D[Qo% Hܣ< B,DRu3_qu&5t}.xp$ukd1Ώ3a ^{ o|:~(&4J0# WgBP9=Dဖ;ox"(9,PqPyAo$p6}񵍭)^3ۀJ 4J{0ƭ:, 1BvRu3Jt Z&)H5%D= *!ML%WgǸr/2"J\88.(ӪJv4zqz%;"#!~ךGm$ >Jċc}nlk-#cxPpQ|3GlIĔɮ$[ W侙GNJGz]YcnvgfGoͯÈP;d[g ϲ=2}Bգѳ򗓗TՌ2ԹT⑱]Q:|ݨE/ 赽:wTcsB=()1*fؑ dQ81'ؾ^ Ġ0V})ğ&&TА+1G!f Ʊ5UTԣ91xOԣ %M1PO=|EGH ?}&m4zŁt'GEwg2ݎH=f;Q(ysIbdg=ʀ景|u􏟽tp'#cx|y4&.;1Kd2iT>ULv>'\RU<[͞h)QjA>ƘG=rwP3J2w##]>AyOu!|}ƥևbG+Gg}_'q;f YA(&>uQ#0%H/$%@P']nj(e 9;p~lGGI)P^H; 1hu8@R G@"3R,!^?/1{tX0'66P P,5.rt1WW{8|H)EVE3ͅXP|yR2E"IIS!ܤ Y@!* w+I@5HĤFUqni)ӑD$Od̩GI/ T.==I%JFDJBҐ&+s͋AQ>[o |l@8KXL*lQu K>Cs` c+)'#BUIwlcDsٕ;?"܅5A;u^~QSb'&6R.2t+92B)`T*w)[n 2KvvLgs( 1$"|rzNW&¢o`E,1;z7 `¯OHޝhuig!϶A+8tly|l@=f` RrXچ(Q,H=JGA#bG) "fnžSTsNkQM# &f2Ȃ,A -'W6DD54G*Mk]u PYH(W"rUs`@%3wNE;ŽXl<>ҌMf5%d}*4Eq6'K嵱=[Fhb 0K@*5alQՏƹ-7zդd_#?Y [pٮ!>!){)3bɂsLjI/{:9npD "#bUu:ԧd9Q.st[;dǜ}FVQO1"-V=S?BYpki610vUa;)̣`G,^CRڪ}Ryɨǀ2EiCz DӮ !:E#`H ȫŁLZcrh[+IW;u #48$b4|3r6R _xniCv\M9OH<s^Wdr=/ă{A39\!(co؍6v*k\kJnp!fAu4v_鯶"*a"23A|gF":ʄ۹:gonjwI=ijoQܞG^ E2/x>c.nnjf>wllc-}L zck?,&v!D+ɣ; 1(T yA2PW;CRx h S%$3hMze1ԅ5BУF$Kؚ@f QocbGĞ8p:p; lt(x7 eJ]&8=y&4Ҝ%!5kbn!-z [tQlk+CdevǗ\Ӳ{ rər;-WDwnENYMZ!2n{v[yhcdV%;M1p[7qe?8x7/?SoUzD<]%V_kUڣyEi:,GHP%#0E{]~iaaXx6[k^gfdϊ(w!aG2\.mIwrr)gߴ/ v=CzD{{q̴UЄck`M"4ֈal-~l,'\TX!u&=Hr` f@ U*R6nu()RJ~A@l ԣK53D 0$Mwa(qTaP7@P4.i&Qk]y#tttT1x{«NqN6sfxC8* g;pTFV)&|DD@HtM .1W{1zZ^|b'&J} <}+>@ʝK[WwT/7 ӦQU0WͽC7u|AƆQtcKunꫛnT6k~կ5˧!7>fcXO3k,N!r*6Jdj/H3w$[ (#3GC#%%N+f6?fH>խ|Rf>_o>ލc5R mGVQzs=PwJ=R|3[f:cҙL^0z a= `HN= HTI@ *^hH-p`!&Q(8f rzlҢC\ rLj ( ԣ8k" &i5`> Nq 1qD|քPvtL4S` cc8(kS=5aic$%S\jc4Ox'Ws䏉}Q%"9ԉ-;?EB}G]ZFӝg?1y?T<5XC}6?Wqy/>p_+Fk<МkCBw٫nZ)vכkn ˷n7%wワkV[د؝AkLIjg) I=7#Q?}&$-+y*/1MяXL{4Rir-ݢB~tӞb{R.2C963>(^R=nbPw>H[`c2>;q5J> zԣıu/8GP,$uJ!x@=ψ W6.] S1!`A|9"9'S'G д#M@0Yݓfp:`QJ=a5p$02P s;;f(9ɂGa_Q_<:g0B gc %)6eǼ̐f3v*o F|mbV nؘ6;o \!9|΅xPQgHs>:fPցW&a:|!Q ?+f&VriR[`UT\%vPh'A%@ALhl *M*`J"4ІZywd#b<~GNRNU9 U0ȮǴjEO N%tG`tѧaG= t ff>FaBiN2&qG"U8tI H"E)!.W4Łf=hqeC]aOa$_CR{>ɟ?t%93.?{K̎T: K񝓟=o3+35$tPoË7TM#KbmO@7vF/~߳SK'mV'[{omcg?c7>ؙrerg]2p #2vcox/#:-U53QMA~xh-0Й9>|19S}y#|l:[pkhę27'f=.l3_<`= +jakt.v.36; :e|w1ߤ5/RNza=3ck 7O=J .IIq+5hkQztUp UVQZQZcTP=6d03Q[MV5M:UV KzdÊ#4d,yMdP=jx l616*V82G)$'MVn\JhS$4h1ˣda$z 1cɒfArF=uz4 &#gbBa~ ,l&^~[w#|-vv?3{G;>ڝ~pjot?*i^:uox6[{_w103L}(z7#?:>ŧ?w_}t’E4!V9'Ϝ._DurzR|92}bҧx1@Иٱ) %ү`M}6]v3,K+?'"fQ"Bx "ŘZ_y (#@ZsS؅stn"(ڥ1Srsԣjgގ.]1C=.i[h*82G}~2G4K=ƠV)YnjI#8NJPq"*ԣ }X}F03ʱhDHjUD\\J(Dhu<A.,d,HdݨJKے5|XDd2_{#Bt:=O+Mx:_yb7>j$(] o3U,3R-3te͈z3uꛉ6UoF%AΦ&[YFl ;w|9H{arMd:̇ܥySfXP $Gۡ{ÄT{HqnOZL=;اCn"HEGP*.ӫ>\t vġqhRݏN g/XPYQ T1#:3¤PiG1pzʡ3"RV%$ĎBڹ'zDG%C4YA|7Ajh^بvrv@G06Xj$75đf|9'4zBm8bX}mN hƢQA{)"x<F"xL#QѹG:%]\iЁyaYvڻe#L27<8Ua">d2EG}y.}HḐ̱#<`&Nl/Š2bGM3y"Σsv'9=z[<>zwP Xz1SQ y if \5x݊W= U,)BI i*TZOGu $%1AZߜ`JR"F3 @U 3xRAE},$T BHZsǃY1Nrx'lTyjLn׃z=|(J>tYD2ݓ<qw0>rfD{zRq#{BNa#uŋGFw*D{7?ޔ+knsR18&v}zU ytaaI}^zw~?D7ࣨ88? do{^>goEqgW>JttJ]Fr/zmۑžo&$>f,C};%>޳iO"zk"/ʔ9.Gjg(l49,SJ7 $jAƹ)4O:zubf\>N9ʡرe;uqEƋ&XH}XOyprBnG29켫];9vQtpR5No?W=6"O#ԣz>G ybaR"I#(+9تy*PH ^6d :`,"0qR#o#cQ*| z >'b5)E]]S֡f3e`ZGD~V(sU#|EG=UR b1"$7qq~DD ͯ̚;w\Y76'aR³Fl"=rWo_Mq;?FW﬿c_7sj̖ߡ{ۀKfc[kϯ q G8P@$+ws?lc0Q>A$GG@GbyUnO V>Qşz镧kzǙO>|6kg%dSt WBU:V{#BqvL|J[Q'ѧG& nE=`7"ؚNlDsSvjY "{=w9@fhVlաV(Ygg #.~Qfu^Lݰ_ޗy'bGS^J:S4z[Lp ND9f=z4*qT5"5&գ q%!f!0Qq㦩 Vк Ǣ<::%&b%(`J&u*u\ 8) [XU4`u1ފWL Dת R67CdR]" 5wJ'>|+*ǜNC8fucC|O"͝;D$Ew~M?vF"Z(+#| έߪ_!^ukן|M02;zJ}: gSlAa5OKՒ86Di`Q]H<4B*PUL˭|wo@R)ۚ" YّU!biɾSgq嗇+g_^|& z|΁88g|s|[$n.BP 9D#EQ22FE.ɜhTdiVLd Fꖂ!!t a\0 Ơ~ QoC}YL'g޸->4PbUKJU!^D1O=b^]j{z>?:٘L.ve_*Y)Ccr8`Gb!#zJEw?C 4Fs ceSJ˝^bwG"צ.{>z"gFlobxDFCe%Pބ:?WY^pPqv}:TC_ )nǟ~Oo%}ܘۿiH1* m5!2L%?es哗[rISi7)lP1SWSRFI闆o{MY'忳ߗO;|0+H}1]c'ΰ) fHO=&z*`GU"r@(US:HkPKqYH9[GgzӆqNeNq#M  %*BtyʏMdB\+Q(4!1EAODLeqbifݺ/ta:&H=fi'3x5Ԇ&M>̨@SU9OsvX^ik3͓Hp.(\zV1'yNƣs%JԣTD G]STR0 Uu `%c,nj{X -U5#N 0RZ}cp CA+h"2%Ȓ:uӌbUGQM [xꑌBWCT~_S Qbq e!ݯ@~>x8K8T wӌln9bmՑf18,3\K=N_˙ue)΂(D1QܡZ2&Cf ل;Ad::DERSVbςY[a#HP 82| w.>}^WoT_״ٵ7:DCFueξv@@@(#ljBu*e <TϘB(V+"7j4PF Ҍn;ry=ߑq{c>fsj&Ô"Vf]fRIǷQ}J1L}9ڍ%1 :m~DhQRA{5,>C* "s;4p>>f4_•tB]4'WxjG{s/#ڨT\2)R$s _DnFؕ/=U;lؑh`{>hB={Oz$Q^SѪT,DU=w}֣>KIH=6͸q() Pchz,2RB "HHU8`fJT`,! H-941 ~2b 3Gѣ o _Ph ;)D CT Qx J;Hܩ$rv{ґ4?\xb6>v~pO}{t)-e^v<U;rU^p>FD]:q;s4k~Ͱd2W˘D{v. hM3*ֿMsϵo2|Uu#kLQϮ|׈6J&Îj2(RTåGk(Ʋ&JAzI9r6I=FzZpo!3lbͮl43O{?EJ0>4+w.`5QMD@)bTn"Co&"($70e|W];\>`ZULcCCԬ/B?&~"%l 1̕8C!)߇Bti&M@94C Q8۟?խ:W3PFAGS?p+Op4hָ9wr={e,VHQ'u3>/rN^]hH]s-5G,`lfy ZR-EX]2G1bj2<Ƌ#U} HhdffA<Q\ #ǏGx׉>"Cc B=1aAoٕ[14TÎ7 ]Xs#8.1]gC^3f9NywWė12\$+h$Bm+i:/c\EQҬ§7kax1-KԣjI3fŚpωtZ4% VIGc#'^͡@ ׎M [#0d1 GO=`6׌W7zD !hdɵ6hF ,\-RuL3fx 6!1y坨+XT2eU}8fDGGRNPR *0hIA0Od:drѯ\9Gs]`7rd'xeH. Z=&{t&QdRz#,S|s0IIɣe[IGi H$ 9d7.S\?musLw{1~Ad#dLs9$3 Wi莢s:Mb~aHu:V%Uݝ=퍧a5ZQ]5ŵxȝ޽3=;ܡ##'ECybB/ MT>GTkUrk8jᐃmdwf?I1sT#mLƔD$+."]0.6J rCR Lc[f# H8"#PXB*춮"1v#7TM3A>v#5@mi<=lg.!7&Գ#AГz.x<[L)qhEÒYxZvSg=Wn,sMU[]sp)G־UHĉV@ w~žzhSʹiƍQQmTHST Jk'S1LA$(TIPbLT﫫Bu9Pb#caLHzJL:X"Fþ[1#⍹*'"GF={^) &^_5*pJDq ԣOTrؠ/}sWd?&efc֙.hû5J׀Ik' 㫐B"j_KOb)Z9|#:;2ùE)σ s٤:"vѓoy9q?O}qt8#ͮ4EB,s>4z_C1d:gS5r>^3r! L{Z:C _TRV6dG^-t|Gļ:"SSWݶkP #4΍x2ޟL&u4u4U8qυj+"XRDGF E7㘓odi5Gzڲ4ei؀hABjAw Lm Oxw{q=wç66YQjS|qJaBdS UE#c&5=( BCgK=$]1NWZ 9v&6gچ\غx-0 _LJ fF@#ɪ;ÝD9 Vkz[L8UauǤ"([3h7Ԏs|>nG;UL]:g4{hqykO;`ы.8f53zdGGoHF=լ:S=xRmonoi&ʖ,xP``ک0YkwYdϧeiɟXQ! f>AT`SsTXs'|4/:Qi^zbgp`9bfc>v0PEݐa2TQ;Q&-  eCVtpՌ2O^F#G+z} I_/kP!{"YQ@՚U}g"(QxJ@fT9fr s"Hci1 y4Gxc"i?[!JZksW.4A/gcO^v_8VS$-'kϽoZg:Zt,__Ai@:׎iw/I>JU4zT5;^/}'S1$pPYuy?/m߸|_\:YlEY#]3Tu>3)6E-x]h>FTjzwg{[['ӱ5vyV]=5@Q(9uSN{Sk\jF%K0CS0[f\ PK+YыO?啥 !5J 8DKa 1|'$j(0AyHC ɵS;gZO1ځwi柦9>Wg֔LI O^KVKM*MHsv{NX$6z [.R5Nּ!H!d4q_;l=GB?HY{흗>u {c7u>|Lܐ3*P9#"U|\xDjR=9ٮvkWvw7 ͊lףklOI%T{{cȉ.hhNTK=]1+- YZkpyie|߻}w>Ν=7(ZUbP򫘲/%2`VuC!>EYɎS$3Ch m=-:ڝmM4mǔUhz(]LQuB BH\GIfNE*,l yꑢw +ERAۘP̹kUp hm SQh\w۠D==yAõ4ΫNGơk5 Cj-,7nzwn3$q:nܾ^5S紪(d2ҸV瘩`` KLC0&X.٪r"vyRآ(U]K \Meo_OʁޟO3iXrSN[Z6LKEB0fc8\[]-h 9qvpy~쓯EDZf&q05 )FJZ ,14RoLLAփt](SEGi(7oxwCdLSغȱ-Џcz>K@<TN) ZMS08v` {+fėԋWw|cB5,-5=S#krsW~UOn~c鋍m1ՆX2L2afUzu I=44O[# X֎j@U橭?X?=IJ.H L9obeF53M򜆳,CP!%~>8u/˿y{w/b{.ڬl4i9KBĩjcj9x 2;W^}ysVO>rvzg͗w.Gt_ d@ d2G)rNj SJC6ڔkv0elGF2+tF{_{;r8‚;UZbVHw#eVgo1&ȉs˻HHSb$}L2S& QHn%dPr36E_ȒsP2.p:/ y+ Dʓ$g:^J]Nk,]!Qkt>r: IKx!kG Vo)Hc'cmOI7 i6v  /ymq8 wɢ˩نk&%HGezT-Z7;hjcW]x}燏>4޸ӯ\ڬhϹd<p45eiL&A)մn45YZ0XSc 4TjclʩeYr5i40өh}u0xo<ޝx4eM{d*1k`2qIKe1*Z^vNF{]m&._\yc]^2LZf`/,TJL%Qԍ4N40(բ7DGF݄B5trd"?' V- ]rA[EoJ*'RqFB#;Z}='\?˾Q5!ԙ^5)X^^>vbumEbLK"k)<_IM9fVg:klE(q}ޤr^߳ܺ]OS}XĆ0,2y5T dn|2oY>;XFٍa3a>%Ng<@׫qGX^EGDuSۯsX= 0UpGLF*=(S}~K'vn'7/ Nɽv{NmlXZZ*JCv4m.,;AՈsb%{75"(@.CDTɇ 9̕DW$se Q$9:{J}ʴRi %"w7񕧞'zԅ\nVk|}?^wc z6j|2ysV8Z_}[o޼y֍} %Wic Z眛4U%M[[ia1]`.+Iw#6;q w׬=3rPs/V0ֆqu\"2HTu z#GZT2dk8$BϐQ@Bb6@1p;'Q4J\Ȁ:8HØB9daY֜ix6zM9̤Ǚ'J/1҆o&p(8O2P"@U4 =)md_euiK-Ŀ1@57%TjfbJg& !RuΩ(j5p,[ %)U0yG][;r+/}ʵkWnܸy歑saH7ˁan2ie4nȈРMR::ktIP+>xOXmja)5phՈFKCɰv5QҌ#Cن.8 !q׉"elF%,j㏖8Fkw _xĉw=dd&\[+NslJ•˿_އ};I1kFT4|ZŻjT=p! 8_(C+Z7Ëh=Y"07|;_&d {0d=i2 &c #p<HUhl1~FmtCQ$Us~1p^JѴzbpnrߌ@(Fxɴ9^ M'm^KG!b_bh>pI&,(ų;e|A%PQSQKNcrH=ƚx1-xY(C^;*b{{ ɓo;}kSϼ|ۛ׷nvFf"@i5nv\SA-> QEU5u㜫kmraٰ29k‚3sYԕ [kiW*L*k{z4Wuj F#L*-910\X AJr:mƧcm98q 7n/]y};<i2Ѫ!QDUU#P)- Ab PmGx n8T͜r5H&fN%e;%Qc25`kwUț O 1&>nkFl X>oo]xٗ_t:9f^X;}zpo{}kRXÑk$k`eM2/q Sաǎeܰ34\kl#Q7&Dnޙ,6UOE˨i*[xa<Էm]XA]3D>B"Idj$C4z}eppeJvg_:9qW %۱M7u^SWS"YZRvF՗VH \ /='WV޷[㫟z7޼~LUnZ7c7QAvkeifP,-/ C(ՠܯviTm/S#M 7ZJXsaݷPpiw_˗^Z ˢ8%6^2w&CCh@- >ȩcb娄O{-:[4pDd;DI4{?c:u jCAy Su]!QN'LGJXȓ1 )3Rz@D9؆EN!G3AҴ$lv T0cC i(<%VD{J2YH}:nQYY4Jػ7㋗xK/_YUx4UIUG9F`k󨖀@55a6*<\Ҳ!eR[ckhdZs8ԍFĐN+V*D쳻zf^hjqA&:bOSڏVyeIi hX0@JF:(uB e0lY8?"xa,UbR4b!mQ׆5fu0}nTehct=-ó61=͆׉m&UMį>ὗ_~{/-'%UcdoNbtw랺//~?|ĺF|h`W"cHKw"B17x)k{'\~+^~ޫFt:5 CZ0SY`XKryei}iuucccmunݽV=nN6W,өV)`apSxvLDVƓD.>y3ϝ˃e3%9 ĖѦQaHXـTT`1%L|7jףN ~JPjA"#)M(  &' +bbŸLVYJG( 4aFɁzxW~l~|%M3Xڣ=F}|P`l.|Йߘ8&2ܚf# PY^OK0Z4"o|pQŜDNToe>}^zl8'-+B j6GUu ʎ 1K_z+Onm_zno_~[I=N9` %E`"60ט8Lf80\]ixiUiy Rٰ8j0\4BDl )v09mS'&fXAi 5K&<\* &`  ;`XK|y输AB㊬ҲSL+iaR)QR?$>qD IUQ>=9uCtC09&Gd3W H MSѮ0:59]~Oʻ}m'Noxq/_ʥK7]Y&TKkAT9uN}>M:yb}8$5? >y7'"RO$U}}c ) <_Zx̜o"7 ܿ=[ 7;~V;?RTڀ# c*G0,D>ZdK Ցr2R֒Mq{2nWow?!G$HVGw~棿ʯ>,uŦhuclt m*5{x暜(l&RJ|^u~GY3.j*΅q:asHՁI1vD>yc=tj~/K7TtZMSRdl [. WVWW7חWV7OZ?3,t\޿YדQ]ƙ|`9VI]C`b jp ɀz"bC֘=;ڛ[r^ucpąJ)6Pa0qAAR>W!Q8b~ނe|_A 0B*>)lS4cH A[gyj p4C>^eoXMB >vL3J);p:h{llR|Ҡ&A|N'2j=*e$.')"vL$M³`Fh, >D=BK+<)Q[iң}?B{$i UQkf" Njf(fԣP0͠& EL'|bg{>pҫϾW\"i*Sd]u&PYW1{֘c 7VKCԵs #4ٰsp4hx<"qTڦ֐粝Su@1 2R ,K@4 1!^>4)pi}yeuyy((Ν;{elھ|ywnom1+@Y*B4%Ps#gg'?os<(,Ze'gQx&伹 dj:DmC,+ /,|V1y'M bs^j@ bcA|}ss|Gօx_tխj\דV9!X[Xc\.>}vԉ9yT_ތ*2kå-|b9uȨ(ʩuDT*XM%Uf % U<xڌ[ TeDHG;R2ĤB){\Iw`&5 n(bxeP GDM6M5pf>%MRqԏHHU [DQ$oVlr# B99"hPKp~'L :QOHJ*&Tyx? ȫ!s2|?ek%!m\A'YEQӆ+:y=ɍ 2)$  d“O3xQԛ\+{WwN@lu`)aClK_~駞 /<{Fm;hvvGJl# a4kCf3(8DV'JRWPjH-`` s+G@h5eL+GIE1 D Ì嵕piqdXbC\ZebPk'V7 Om?wԠ KVҰd4tZ9UXZbPrP?.JRRJhQk H2 9L$jOxMU캪y&JYTu6BCe.쫁%oOҽ:sϘ螑$Q%a˗_C{.M^}ukLx7ߟ45&*a";HQEuŘj0#_||1\Lm̍2PpdLC/b6z᤻H7QxDCk8gه(g|dSWn[}pw1S졂)񎥡Pa`|Pr,FAŦ}<{'ªܕ?fl>MlqoUɢ5rJ{pXF zza1;˧/Vw7hA1X>̢<1g}7^o)OrnQfyW~<߫d_2/x'5,Vy)6ܛ0>v Vҳ zsއYߜ\'y‹_zx4Jvs4P"c`8XY^-WWk6hO\ؽ=rySOm..We._8/LoO()ȡ&e@F5 B[릩kjMհSOBBTkb.2TIS u!6#"Vhq/$ ^U ""QqpLKQ q-RY\ɉID`5*g3ISE:8Yiwa1D O܅Nsnp?8,@ɵzGۧ6Z XY5 C3!AǑ!OK|X-}(O 23YC).\ԣ7_Zֈě7=$ >1q zp [# M{} )R@Տ}#m!B#GO}~7RHmDkgQ~rZYxPK^}qW|/]|"A-liKSeaҖpviilm1( R"2DFeiL"i8#" J̮ѺnTxRҌwXJva@ ZcmLl,-{ΝX_[]]ZfSB*$@miյԯx߃{vueie D40ޟjU;RY*PZ0m"%/̄L{UB   7$I`ht>:~Lv{Ԝ}z:h[я-|V8VU#Uŗ^5vm몪z2Ӧ֖`oaYcy}RiW̲efTD@'/10C"^v߉L>g"j2ԥ΂NLwX F c<ǿ+Ɛ1xc4b WMFE1G^;RM}`\W'sO8}ƙ,܎@z{{W~j P7x\Muv{zsJBS.K.keTVS>]H60R\ݾ7)h:c`82Z"&w^kL|G2DXj'>K;w}/pK/omlFj# EQ. O,X;C~֗غZٵSyԩ3'On,/-/+rq֫Şղ,Jk 3YQ#HWPֵֵ49\đ7, WR5T@Ѩ6LTdDu*S!RR[4bt!i;V!B[APK)p ϛ 5K 316!,Q@Am#>ǤFC UIkP?w tjD+5HȲ gFœ\h Ș m^4Fl7ZR$ÎTɒD= w mɵ&}^o~9fĤOy=ON Gszec}mei؁€} ;QI. bX:_g?;y׃=ubĪ51;xB C4mP!t=J BDQXh|tt(npg@CdV\({9qoTU'`P8 !jr]2A:+MהIˊh") ?l)ģgEhc_gq{ҏ_4 ?h4jvN_}W>/|sӌSV52҉YZH{y/P;O?|{^nTm;T9;Duy_LOԵ$t"$J3>-|uFD3D7o~#8SO_h{{;OzҨ2, S +'O;{vOx[WW{{Μ\_]^ XfMSv\><AYTSYF*,ua*\Ρ!"V2 K\؝]RYY2%[Tx/TeLj:eKH' DTk)ld S6arS{N%421dHH#Χ? 2,pB>Ip`-UWõ_Qq!Qe=](gS$:ZBS%:1խ;/Wlb-Di*zf 8k_!$ C@dDYI!l-ۆPH0BB- !f N>  _3x[ }BzaUļbUzSI/>ba+/rŭx2iԁK[KYO<} co\~~`ue0.ʢ02f@ hRyP.Q z iIˡgZby嗞/L倆AҦrzviiZkWډ3'Z cI%5**֨a4[cbeyOszY fZa4mV L":igՑ%Rz˗8 &IjWm#,EɦIfR:U=,˵nLoxZg8]kR#9ͤdLOɀ*nFIX=JA݇NuZұEr}̂{&GJݪ?T!s{㱵W_x'|/nnm7d<ݝL&$. ;Õ'6N8soɕgs3'70ecM"kW_oemeSKH F?GO ό (e&֚khNa\D+h zTH2#p7+m^Bx[C.yNk_u}M87QՉ:uXDaZ#PYT QGUɵWA j3 .BH=i._6.+7^ڻ?M'8cƙSϝ=ys—nVO*qe28f /BA ۨAo9[cgcKDj$g"1B18e3 X&ͺiTjROm⑴VH\4U]Ԗe]UMUI]tycE폧dZR㍈9?S!*1~10dF=,8Wx0 S?T6&}f7 ǯ,͞47`D4t_4ג_͋-\G86mJGƢجI~i~dD1nm<՝{'_|zS|M5a&d#q *[ko7?_?3õjk}^[}:FS_V,HEl?~w%t/ڤblq. rYl%Wī:ǁ*Tq(D9gA׮_O~w.}Ĺ?cG|޳'V~'3 ΍ XK+,6Y-,/- pRrc00"ᦨ߼*\\ m\+ƩnGz5$[ՉuL5FYc:k쉓k.P88G +TD4>: 1ojb )n/#3~rݸ& 8%5 +8!9H?O*?u%zHE]97at L!؊Bdf2NL;? 5L3 .]Q{ Dtx' {05VakG v̈Bb~0+sg9=^I[d@lLik}8!,3SGO=!i|x7*K-ELo^ZYcD *0/^8cz*'  Օ`/^j1\{m9sSϜX `hИv!)!5HMؠCm+0k\MX "'NTj67` ,X ++˫kg9ۈG=FYZXdӪjdP*ck̨*te&WlYݤrRb`aNj J>Y03u#NĪFt'E~Ibh/K҉,/D3e'>Tam UUKAU?#@tp,T#uֵYPenq 7ԕW=&&g޾QmDjiF]꺨i\H5uՇ!E&ws>/…L@a;u[PRdQ{Yb;>Jr[~Tx~4ۆY Ӳ<=1̓2Gۯҙ͹kERtB&;/`@fC S719;++_WAxk<<1RLRIc>O>:g@oO~]_g{/~'|ʕ[[;DW jZ`fi8XYY3eY'ZkMgsNSU޺!qnc0tcvᎰ;vۄe7XW5eUsy=FPq"+3|{w=Օ3 u/_\1&O11u*z2p2̇ث{FM:zB:GZ,QNv`QQzk_~*p\J"Bd!Jjd6F) e^C2餴'o0ƀ]hFZW0k !!/Abtk"epژ$=>?,vAy1D^vs^xt1Ht8e#Rl=AKGd[^QP."*JBJIzL$&%@efR1Dei ,?,]]{ne#(pskd(iL{Ti])1pwGQBEvsJ"¬6( %mjEZH{;>-e*>عl0\Y]=KW3dd+tukmegB1#\磄KK";vaFq}?TU; c׾r~g6¥JC|f[|"bE͂Uۋiݙųc({/h<8]'c ?Y/^A.qmْ]ʏ8jAT4֊L|ϟu7"60*8?ҟŇ7'Y]iJN/T%Hܐ5燄=6v=ޚ}˙Q 4ᣗK?/]z'ΘK1K@\%v\\,LӲd\ڄJ S:kGDU:>9G{p?zӓdZ!@ Dey_]]YX޺uZ1?w3W^~FWW"Rk3*8QA@2||txZυiRE\)EZ[X۷ +z`ur[[[lM#ADa&v030F=Q / BFYnr6mo5H&;b!&6$:^IƐ%BDE\ِ̻X $Y+#k1fYH »Lۑ#&:f,i%Aefi\KR7CUj pe.Z5v l@! 4Qec ʪ`6j,X"Y+ '뢅HŰ*c[w##4:K+=1`ni!.暬if ͨ.JNbH!ꟇfuPD"*DE 7ךp abhqUD0^QQ "ڥK3Wf_yΌzÜ3k g`\TmAO+ug&Mf}X5@%  N&^cg! Dg9a%Fjٍ~E4>Fɜ8u,Hj/ӪAՠ_ܙi}a$s 4cYĮS&!G R yCŮ;c(ݟS1Qv Bri1GQ?}\ڀ/?6 =ketX5jimT 0t>[g.b7ZOH!bҺtPg)?H 8:wA<2ЛoroWQͬ͆CYf9ϬlϲIϋ|Z`Ol2俄 r/us!9~gkՓw7qА*PJ4Fx>vwvƧɬOCN%9 79F敫q7f민[gEf19m"}J0h`̹ï|ߤ hyfY\M,^n G+g/m3z¹3E7 4Bت!#0bzk Ȑ)4`4iyrbBJF,F9%!&c112Qk`fa,YDClF[Ky$q1>3EEh>0A*ʢxQzԴi-gR\XJ@ ZxH*` 2m;jr=B% 1 YU͢ 61 vk h4;.#:1ʤҮSajI˩'Ta[S[GhB'@j%HL+u,f"mf\)w;͏=~B(~WVַճG'kY4tG^+;{EsoQ2AU?W$Ƶ*^?=Ƚ8ˊFNDiMDSC[a×x!Gʢ[m+DaŲ7>long`qFhR7/L/nW/ihTOOn__~wfs7UEoH(KY@:~UwM~r|YzHwͻ7n<{vrz:NgeY6\+#qn(ƙK/o} ÷Woy:2*)*iO]%9&w_z~c}ʛ/24f{ʚ^D $Y׆htf•~ߝ??R{0)$ZU1ʋy1Uő6֑ bŀ55L3=gAcz@$/+51yk\[Æ}hbbq  9&2S. څON $< K fb v[م1k&J ɥ1Ԧ#t( iOq==B!5ăHB3HDZKlSH9kB3馔&=o,(PJ0CTb1l%ӉLeqm wc9nA{RR QDa(Aш |"!JAƤ y( z:;>w?9=pd\޳յ ?3=}w.?5p}k U=1pN嶧*Z"4 [BqV*Z#ǧ^\{l2 >4!p"jrYc3l߸w GyV8>~4l2g:~1CW'̫%28F{RRÒ&> iJ9iEWuiP8wdX]SKݝ#u;n]Z\hZDȤn%Kv)8M%f>& ]";u`E,sDD- 5ʜp{mͰ9 潼{5B ZԪsשxQE~^}[;>>& CEa`;ܝ6]GG]zv/; S(,+Sj?kyxrnV~LT2 V86̅U5QJMT'uuO>Tq5ŸOwt?NiB"1Bpk_wǽq*y 7{Μ 4&'1S:(ߕsK[F33{_8||w~? TX$L#s", kj6|):CL>(Ʒ7W/gszz:LeY5Mcڢz /_ Oo/k_{͍^*11U}uj,9PAf8z~tm}5L|o2E^]աQu(Ln׆k[g_Xߺߌߺ7\>D%UcDTSuKs0L[l8`lonn!'O y3liӲi񤑌qEGQQG#Fi>@1F7yAֵt` ZNhxj|ԍ^Š,SW8cHŠ[G{Օ( x&X#E1"ge U)cVFOgRG9z(&!r $.}3JH%'iuֶ.t/RBG]#uG8>w0̒ZBby: 4 #Q;>bILXb"*:OkW%\ VV>ݏƆDyOYYk..,1Z\YQ2;cZXKɀ(4ox> b#qTd&q>6t܏=yJ|he v&ӝã8j )Q66.RKi]/_>ɹQ("1UD)7JVQ\h^5>6BE 6,$%Ve3 ,d>Ey#EMku h)A_@ [Bcb/bLA OPC[16my޶u] RhB7Ų̏45`dQd J- QfYAsD-g[KP-mLBX 3JvJ)bD bذlEQiDSum*n)y$(ibPVQfILN^̖^jTAYTICx2ƇՀZc3leg7y:cr?31 g7pԱ*gth 2sTfOlT TS ={r>qU,uSGA45L ϸWdi{kL^: >iLe3"Zѱsmf gP7ѐ(,d̜34tx%l0>".vIMP |j|byMğ5>2"Kcj?J]p$mT{1ѧQk"EdT9+smj>tb"@N"s]?c݄sc~Fw>~BtRoT~zK?:>)f8QzKVcXa j"9kguP4LJa7'7㹖$ %os`,io0C6ί_.レ7G"jl\=pg>w;sR费_ڏ|n~pY0=ă_<,[?1b>ODbkzb a<5!Ưz %{A@Ճ,Pםj}\n/Ogok۷Ǐ7''tZ>J'c9gb8诮llvѓ_z֙A[&C$d̀3<ȈsaGI@fMX½֍e.ڥW鬬ZD b:yPn]K^risk# )<2RjR0ZMgF-G !d$1:eٳ39-PԘKkB*:Æ4 H@VF@L!Rj1E|OgԬ6" pHĴ}ވDSmPsWbsZ_~zPB(1K yk"FHT9ȶ!`"iƨ@B1c͠Bs&/rwvPF R[$a$Ŵˊۅ5pIakRc4m}TTK#x#u{64j J"hRhFEh&( 3̵k$:)ce2MY9!$1 !w|l]8~f4ܸ{agOz ~R6E1R1za-sy[<ҕsي18Y%QD2Htr8~ǷOOO6Z7>MDX"A/'{~oee[?ngy"SkiSnqX89(̼ds#+@EY$LjLGBL9Q^1Q{TD@0ɏ0DcմS㣈,B*qIl[^^ ǥ,EE /{A0z\=~F$,|V/S@asѷ~^p8؏ҋh5ʿK]Lq#V5c#l5o;Nn~oHZ^/އ{'RUj:~^xM}L!G~>}py2l6+1݇1Eb2\[[p߾n~ŋg7^Ā01C,*&F/50"(gU@ȉ6ޙ;?ky[ͲA褚Mvf&c x…Kώֆ.wi! vjLꑦ$ 3 PWt2OǓj j$7@)(/Š1sHV  XgaG:T1 2@2B2+ -e]u{r0ЌD3_QJ}9JS$<55tn@^LG]n&}Tp]YTI^N\S Z#PYMDGɉHt@Jyr6 tҥWeXR` #,HbVm2W26H DD1ٔNυ1"D&r{/ M1J͵2Ԑw+uh1B^=-=vUQ0=^b5@0˳8ΫJ9 Ebsm}uuw>ǧ|%.滻=y|eUjTjM:v~~Wo8wxI\1R"B%TS`.aĀMU5*M/d2ʠe֐5(2+r2E ȸۏW~: ng54BXO#3f\ 1~rKu#'l>MD8 @.^xnp&go IK/ʺ/A\@I}j) A0Sn>h?Q~ܝoC|gtg=<~kmnܳg[GG"`D(Z?|VŽ~V^+cζAƈ2 698Fp/_E =ԙ= s,=N7sC"[9>:regy]7ת> #Ν+?ܼz76 \zʠffe6< 9MB)6]azN KZZV<;>xSw[_}ZXW^zŭGF j,L$FLl ƇG{Ov[{q %Vd00".g/W_zuuueQAV-AB2S :k%4NF@*L! j8KNI"|X kȵ I#CUg+$"(Cd]qIT%PLCXc`Bivl%JMZw\{tBT u{ ={drpoWMӨ4zvXkT T1vwoܾu%&3SRc\Kk ͈1b@b 73;Hz\HY2G!cv). ! Ll4-;G!e%i&Q5Y'@U-< $+*QiC37T1V"0&Ѻ*Oץ^blbmum_Ɍ|۫ >G;Ou A\,;Og6si;#3ih09!)&R9`p+z~r|<ϫ6hZW" Q,ES? ׯyFBh,,3:B㨧= 3WAc\)0y6xlxbخ[`m=3ֶ :eҨso޸Uc (%ĔMP \;`%UHM:V\I8>R $nG!$\hxmoZ>u6K3I;b:?m(v}t$Od|mo}w~oN__Zh6[N7E!1V!d"bL[^__wmn+oifn^hkH%onK7nݻus9eYo0(-?<:pO/4e.3hoNӳ9n|O_`l-XA]"a.kyAٵz7.V}qNW?|w>I|'f?x\7/.>ݾS_LrsXtij\vҧfGzN/қhB僋7.UB2=™#fs84l uZ^^^}|>>ZEglJvU]߻{7]]kUUMS4vy{peumK/M|cW.X3gب1y14>S "vi@ F h9"7Z*buxּC|Ȱp083ZYE * caŔn$`z흣k^c54%IyTЮme4/s&Hȩ"B Afr65>&xD$R!cu!̐)7IƖ:]i6]CvzDAJp Jj/db%ne]TMIX>'VYL Γ`pm@)4㈭o_6GiS佞5lD"wD+]NOǹ[#HNxD(.I6ZEԙ hr=jPx/m;{*<8&+AX$5eȠVE$QQEulq'ZKoY>ƻZ?{G;,Wj\EKu1lco 3Շoqy8!GfДd|N.mG'ǧ'弬*R(3C9CY,W F++7q~2t>&\Y(2A"8!7Qt*eUF4ݙՙ\6@٬)y0 7ZGgG{r: FS]y85e(#UP$!&NAZUZi|46."#/mIMPvؑNב(vP"1ƫFi:2W@0֬z{~<=7F*^uAiY!w~m5?8{Ǐ4MΥs.xѠg^(G2.,OMwGv'(kK ޻'`13¶]јǥ}r҆7xjex`o3v3o91O=7OQeTO/_hGjd5M[DHR莟V-aq?XﯜN= +D!"b8#p>(w.ҒTBּw.V Dش>35at!'k? {O?O&Ӳ̼nblT"c.+|06Μ>}Qm\9:,rcZ㘇8!W3 "5R !v9H Hrn~5! 1x/Q<2Y>{,WZCJzYnR#b$1b;lW~OnC۾p8zѤeUJı@AZk W!@("D P0أfc)gP&qE](IH#J h23I (uh¨|A KV(|"4 %Zmy4X!V mhr|>'wߝs%Hoe%6Zhb63}i:juT Uaq.'d1!秄@96fj"H9˸3 Cƈh B3>Ft+IFA(7hcVȃ0ϊ5I #P@YRDQ#P#h)%mq{rqfyx*ֲ?|ߟNiSUG{!k1}9 \^@%Vb6_͜XY+o)z0*h*v0и q+z;OӲ*$w9eڞ͊?[g+Cv31j cJzţ$Qл°$mXU'n4]Ub[vdr"rv8D}}ݾƅۼva6f} `vg'ލN]^{ <˺~j#RJ/PSZm-YC?xp2?w;wp]gރ1)[]xaQ6VG0YZm(m^wsC^[D_;jVomDư1mƤy@_--˿jmp-UkhN¸{o=A{|s2X+Ա0)~_w V/|[S~78oi,lnku}EL,A4 [NL&6xzKqZ̎_6Cwڤ,][鑋5eV._կ=d:ZB V%b9Wp:xn?<_|ڠScs!1Bx}0D=u"uWF4JDƘiD[3a5ֱA;E9XRRD F1}\o?֣'?x``oVVk3} /y62wwv ^<) .β'U@$FCrzI 铄5F>JkC91^\69F;F@59ZhzRj b4 FJ1뫀8vsMЈXBAI.O{~x|pr{x8>=*:ϋa!3f7<ٻUlw 2яTn*Sr)4#"QEhLαd EcYek%FUAURk!0Ē@1F!I^)ZR!QH 4(06 mToD$L1*дVZEXК)0$O xwww====i5k|ƧR42#SoaK|\fyֶV/_o{3M^1=YUC2 >>>UTU$)Pgadq9n2ӃsBY,sbIaV꼦Q)|#5Y铛wg{xh[\ಬg"ܪ{trp0mkTTԈUUhH`Sb'5㒑 D b։uؤgO]$,ԯ&:.b\R$ "QĤ٫Za~&"{/@u3Y& 4D(DљagOmz݇6|=s(TVlv\W.ҹ.\Y]3w+ ,I]xl*p|rz΍tuܾٳj>!؅mZ\98\}im4ȳ㳠5tv!7~Zxdm gOnw\^?dr- Ru1SvRgO?o&谪 #ZrņZ )'0{ri{Ra6&6Õgw|:8(Yfbsﶧ422vbbxKCVU^UR{[d>J2.39'ηXF|E5-QKz ->&{lmf|^6 {/;d1yzpvf}k7.lmlEksCUq"qZ9 ߪI*E@i@ȱ1lzdYۇ=olz *W~*A40ώdsOܺ=?ٟǥ ׫u,QQ8<xeeS2&"͵Β ׾H uY#2KN(Dj 3:\/EE ba:1{kdMJ,mUUI \I8B ( tqVmQB[fͅS F,QفT@J)w\|ޓ+t6lz|:>9:6RB/z! uhk߰5{d~3qP v\spˀTI{(cm'=jT5Fc2** !PLdrG?c &i\Q$IzԘ?e`QI^a0 CG, H D%x9hXšiA(EROjX{ )g+%v<}o߾Ϧy]W K5s s<|RES[˕^s|wPk 5YfbϢ'6Ѽ,*Ե*H1&Y:2ڃY[8[EQ1TQDZ)|qLJLг:~g}c?uzI=iW)1l,,WYɲnЇdpu4̋@Mc M>o )Jt1dܐeRӈF:wcz纵Z4b"ЭigDĬ̢ZBZvTphUs >#MwD gB"= @mVT̖Hxdi57GUAous{0\[>vfcsI@WB3 vpk^7.]>s{4M*ƤԳyre}4;k)l|jjE,ČofZ̗-{R͟ζ q M0ΰvLKϔdzi{L6nuћl}'GCֳüG߳?/nzKWK7^5WZ?xXݺ;wnleuN%c<,ԋ< }Qy1>ީU9mDRg?qOPah{",&qu~zT cǸ(-Lљ9%Ngk'O&l>/뺉ѪCKERq6ϳ~? ϝ=N/_;wf}0S UcejM^)PeD9(AL@֬hI@:ǀFQBgqǺ"DH-Z`80?w~|vhr4&3?/\/4'c.+"syccp|`b@ )$hu.sƱ|$XM:gbDž!Q)kC/:drnKU$G:19\ &5*jFHR*|gy溭LӔxrՆf JyqBCPbH$fL#$hM]uudM:2yVUE{^^_E S.4c9:LGX@T$F(QC$U* 8;kMJ*O{ )4z%^O!F#!m@ڮ5~0 Z8TX)WJ-QnRȒYjDFQk9rg0{νwI촉>&jTxmjF]ź*r=7gMņ*+/|oʕ k \{L&q]u#j4$O!Ic2ϊb0GQc^EF\)T !N^$2õ702N6"W 6@JT9,K#Q#9q!H쌼vi| 0Q"M$2'WI3%RZت'h\0ˌ:c?J'.fQ/]uBS(&1"lumuӇj6LƓټ{Ϫ~23߭gr<Ot"=3`z`;/gV HBx-^=wfnMKɉ:bUxxhf(Q GKQAaXGESUbcLJnch0eZ%q 1Fv|LkM1 DT҅82m[6$[{Ʃ`5ЌKXdXz)Vk0 KDȜN&{ɳyTM3뺬BUt*eQLY6um kʃշdmr# ;]!:'֊k$f611FCDqkDҔ%0y<4CV1FvmSϵ64u#p,m"BM/ D ʑL#)CIGf!RpP)EjJJUVk%~b6?9g(Bĺ&c?o_{>~8Q)ԕ> *#b@h VVLS(rϚ [(_7v5%1"34P8+1VGdz鬬Db;lR9,/([[gϬ {2:#B3m:}3=ONf叾޺u{{LJGG'h1s6 eHNgXlm" Fʠʠݰt"M~@8 XDYS"#LGñ"8tr4>$P[m%}1jcJiJ\-D}G,쏪Jwv|*/{ziv\iQ?W،j؛fuE=@sι91-թnpaREtX,ICb>8:߼vpznpwww2e I]U,?X96YI{NTz/To9;9g-V UUXkm|O"hi(F0AQm]+rK di#t|oM]+ٙz;O_/vvƏ2E&= _]E^\{هn|ə;]7Gxywos>_!$ZDmER:GJ۩#-DB|3?ѿ#_?!!HLp5D 6D`̰AH]fA'VڥlVLH"0 >9󥫇~;ʲޫztIrE77L.n]?sf?6!T1DY&@ *3JjSMT[@ ccɍPD(Q̲m4c-QHDfH)8Vpܻsp"i9glfQzlnmфdEEPߛ<խT*# 29gҗRC5zA(sG WOܿw񣇓ӣt|TVeU53`}D@)+S"1zIRz&JX4>ɚu:e" YG;qTvi|ELdS{&6:dXl1,tc*U풕?UT@[UODD9}kIf2FU0^aOTafÑ9CL\ ";48g1+胛{.󓝏}<~LoNeA5=5o_[Yq0MIy'Yߍ#LTͿ |aߗ7$>pL~#""5V֕`zceN&>$0쎥%DIsXN^}g/Ϊ?ufnto~v OWGo iy4oݣG~Ώ:+EG7z[ӫoqy{oc}|vgwk6_W_b!?/li|l'N[O̟9G?>hvxD5 ׶# a\nL'+%crRvuHz.{R`V`wwgg999eU׵!O!똔;dY~o̙dzӨ晍<1Qڃ&c$*RDM99bд *@f cgɐhADj@汆h7Q`קޕ W'OO&3,˦TցBM4QQBg>ʳh b PfVm4uSC("jiay4&ΨgzBnL6]g)s0CSuKԮV$L\Q&wGpT1*m&TD!{\Ks繍TU u\C溣Q\cDm[Gƶ,j2&25J&85 DG&#$cj]hES 4)ȰR$1ⴒ9-AI$=Q 4^IP.@gWe [;(*>z֝s׆iFd6.:$B=x$R\UuUVu͆Ř<7S), MCT3Ӻ:x/!jd$OsɥMpw<nmbQ_3#ֆ&fRx #k=*,Tq5oC>|t.l)vCDe-ZZijKum5ԨI}LG"=t>&,HBwKǤTŲ>&ΓjZ$bMv &11xb @N놈ȧZIF)Uj{rǀb6vvn޿;~>1:,);y>'\[ v؟X.VK&HQ&V gY!&{Km^6,.%Dڥ's)?u-UYV\9mܫ~X#ʃFCT1$n.3uN{G[?oW?w_Yzu潚66f2{y;쓟_X˿{'ufΘ`ҠE^O''O_afg%?ZWϿ~/}a{o}tvSS(XNcnmE{-ŝi|7j!B%(e!2 E`Ѧa ܀+uelVO!LB Γ+kg{٬iBc4,ɝmsȲ(z`exwhqfmWt-U@ tKǢ;:0>-܈r6Ƙq Tg2їѓ"p@poLh*弪*4M$4$zm|ljbk铍3WFC[Ͳ,3)}|P@>L RS ٌz(Si2@m.I$I$i!5]u13xmZ[w#%`*KVPiۢmDޤV;YU8Qh66N9Ol2 XUH[6BlL`u_u}^bl9`IV UqP=kNշWG`|Es I"+1Z&hQY"G2&Nm6!H֏8͐B-I 4!IisM j(mpCPi3ʁn*$h3׼=>\7k$Hkt@ 3hTABϚU7Q:9(?oL $ @64 Ud.F+"%U2Z MS<9($NgӪi&z1W^Zv bUp0gϮy/:%9 c\CdZ 3PQ5''{ʪ*&U 04HjP/bEHh}<;)X[YE MXM3DMw79S"Mfg UG۾yj/Ey HtF:AN0>cccGU ӭD$1&ĪQ&@Um:Ť΁<}"-θ֜IN($\Eb1ZT uǴy @]"^5)|!@Dsw ~7on3NfXU1 ;-[GGk+"ϸUz!I(̧n%lN4ߵ#_$+>qIebԂbS!zcBnvz?3JQPRNYbrbF/knQ /Gqj!}g7^|94He)fH8@ pͳ'3/'q̜zڅCݩ¡;j5ŀ:&bR[[^?o\Zlһ R5Dw9RbL/k1OˉG{G*$Z=yz-93&yU66vvQt6O}? -,`vgչί WcKr 4>vIy%jm536؞q( {)0_O Gja3 VH9}՜'"0UY5o Q m|G!xOM!$ qecu7,ln,ϲqA~bjA2JX3*GCĤi 4;͵IN2$ƨR "fg Z(pl7Qەk@E"JN,V<8@}",dXaTAr/5a,4:I5z@d0$d{d9U4Muڟ MH.P6dNxbZc-HQ4HƓ2glm0D >&ZOkGeH}A#bQqV2Z+g:QWQ5́A@5Ata{mTMU\zDF隀Cuμnj` D=jS7@ඓ14DC 7OƇTĨ5fn} DL?>Z_M&'''uUU46$@ya2ϬgFF:+ĸZ )DLjǧ'U]Ǩ]?5 +'D1}`}:ϯ,E3B ԴFi|LΘ VEftOZZU͙=Q4nyΈaejG4 Wbަ{G"Q{PR%Q_2wS&%1n:쥘6e6v&ac#4X9([Xc[rWM"KJǧ$r4>}s:.]R={wܿtgg6ƪ%y"cGi^>:cĻ?e|>W}:hKA6woV{Ozyalbx2.\5zmjjmV&ӹk8z+ A@m$,j,1O^u4M/!ek4Mz>b I|0~g2yYFLse(FBQ$aW&p[]6!jh< Lk8 cڧp j7׉6nTT!XDF)9$MeLic&up&ff"bgزu.wY1( 4* U6DEӶU .PXl+}]9Y5::\BD+ʤIUaj+ :Oӄ2J!h#acq8BuSWte)s{wƓӪj&x/1Jۥ.Bl&x2t2޿}62 {ǓR:Qʩ!ycF‡r6Fh\z'Č!/ci܉fD:<صn4>LшJI_UdDe+k9vAcG3Șũ01>;؟KJⒾ8V樨$FR+^Ejn@{U] GDL e;1u^- r]]3ft}x<.s !NXI#5x~2_7rm,!jU_pme/u? r\3G;gůlɾ YUe֑__IA듑L^"٢3cgbq|sT8 Fo`KS&f1Kxr77N;[&,$HuIiiC_cGϮLfA0gG<̜%a@8K!sL!Z#~Z>tm"h);bjtGQѓ3^y|<* }=i|\"SB0csY |z\ 90#jHjUGdK9i34,h-̺RZ&Y*U MhKt5KLP%"/FLR1}qgCi[ 率hkoy~sANOlR$%Ri UB B067n~?Cٰ֏-'}jAMfk`Se"Ӏ b[Ƹӓ 6L=bۖh" )'ɔN=zԵG F Hro砟oEZ-@`"Ddž-lŘzr|&뺮ib!a!5m_lN߽;˗j:%AU40[Yp0yMȰH$zM4ƘAD, &aDxAL $BlOJ +,L&qY%xRb+,#+ـ"}{q cʎpm{}um͑Ul@R!;mǵ# QnEdrΞln_<}˳r67&Xف Z ((3k\7_[j,XqmM4fiSKŢFQ}]O<1+~; dbx%R3(4kEDdO{{vum/0Pxz2޹s狿G.n=c\4D ,$=wZyb#q*J"[7F[_{p߇.{uQdNǏ>yF$Z0v{3D*Nvr?s5߿g`we}-'WB53XRQo+Nmd,D(v 9 鯫ݳ[)LOo~ᗾGzmb}}z_t2=G-_sGؑG$2ҝ/;w`:Ghyo3^DyA(8Hz'Gݙ'\9cZo6>iwVq>_]?M&M?ߥw!K7,6Hf˲^qR d3'˅΀9!hTS @z M 5 $@crr6$1Z~>y3c_P!,\ +:dRA#"uhe͜uZC-ʔnD/!H`ɳV⥺FK׶%<{ZfGRvHs.w.g,lӯt+zi!yVLPȺ2!fB#E?{fy [[W"!)Zw75 2KʅB 09A2 Xp+y¯<Ois %,IH9:nnO"MyU7U]Wu4A6U5IEȗm1^ֆkk++V$B4{Cgo]yY{{N]V+sÐ̑%hGyƙKV5 "U#"dbs-T2VC!tONNlbM NZG¨A@Р63R&dYeZP=}?l}1J$L*V҈4 &cԈ3ᖻWvK/]ޝh8@@ ppy`larHـYZaKl%vmD0κ"\=^Y,5 l|zrRUU'X0Z JT*tRF- jPs\լVraάʑeʋ&?[9936N b K •V\eK]#$cmF7|>ʀ`Ld(0KG۫0鸜NT 0x˲-= [kLw1_u7Ml&D6`eؐlȲDd`DÖ!C@ڦ ɢjqujά322;{Z7#E:^"+^TUTF|ƈR6[KDd5Y,^i I߽<;olf>kkߴ>xaFTKqyQ{2D8?[jT%(J1 %C<奲t6uh6t{, )3FYynJ9J-̓'2 &fNP2aq89=_߿qч=zur—ribZ,N\ZR1:4 gKd5eNe%ysel6 Qw53DL˵c:Y偫p~s2E1DC`Xp\fzfSiEuoLv ؝opʎމ,)ޗ~_RbCH@^?w 2Y}}MMpasOuw[) 5+!h#|Nr)%샂.WڙqIHv&Z3fiX'  tW#)|d'^_4iڮMb6ާn3?Mo3zDWʘ[7FkE jD#<$"GԀ *#cIC]8oO?{/7r3ghmV ֍[W{u> B&  Gͬ<AgŽ2u*un !!*]EeSZS| a45Kd4ԀH)t( ()It٣clt=~<_m[&B MJ$OH@$.#tb>)z8eAnB;:FGRF&D]1jut[/զiڮmB8-|A]lgx88W9r` aQpAϋcRG߼S1b.2RD@$"HVHd#!r Ģ@.N_qh~yXga.wg|2ozր^ Z[6e$پ%F!G/ qo?iEDPp#y! Tؚd~߹ G $Xa^ZiېyjG/\B bIҊN<^5kʪL1RTowbuMc}%q4>l㏍_zMi#:Þ5Zy4lܠ7O>Y%˶{e(]q9ngǬ0u+ E"Ke77ò:(r +ň$ YD!*R%e1¶{{{{GGf4"ٓޛzբ4BDY#^EgHFSEb< 2=yr6.VuœTr-ڬ(by^F8S^7e # e4Odz8F)c$"βa{oW2S?{z{M ˢM戩̖L`~u^ D(,{{|4tٴ}+F;o|?"lCf^כM۶M] D1>O_L'^e\eߧ<'㞬/?9=÷: ?5/Hû5?uVR#Ž[(TM~y 9Y|j<ՅZMAveWr_:vaJD@PoŸ{?ߤ&L !gUa@@@r /{sӅ,}A;tc"" ^_`yjR[\s˿İ7OG KZ$;2ht[袄&cDlc ]Q+q{ֆ_U'@ta^u;c;RvˡQ춅:41F[h Q0s]p4>ҤTA!*zѷ.>~/O׋|A!*Sn`^C771bl죓OC#PzSl߫_ء3hZY+Phee{!0B'l_nVu-^RMH@Er"7xՠt6*93 SLߵ]:B(DFS!n Wka|{];|q2?9yyq1嬩[UDt(V@RTX ^Dh DR`yV\]Õ0 3"OzӊӓѸyg=}b2VmA$CtR(Ĺ׋EQE?&ٽX>b0 ^ƛoF}r|"ڔ+" 14FpaZN&U]O6z$60**9gm9Y~V?"| !8@y~vng+,Z !.wyϞN[CH Is;ocH/-/^RbZ3@3{ߨ@D'AɎLL^{_wo?|ί][ G~P͏kvj^'.{Dlc4>?k[/6oOIQoO<H)-c #*ȌEakEAP» ]cI(y(^o+yE`%mS+nn>2szmIׯ7"̚L+Fx=9#K4E2 A x-)uNF!AdbOYO<9}ujXaGN5\e`Q1eU ˴|:9}^eZi6C~׿pZ {eްw6b(ڐڀ(VXmRF+E":KMcf ZEHRZS[>aD,CLΦ8D AjO œp\30"͘Y7n;|?`'/_\^LVuZ)#H90s#o$<( P¤K·(-Ȕe.KH珛0 FV(.ĶI5H@~z^M۴m۴]I*7CL?.ߵm/yNwJ唄n:ԇ; ^}W7s75 F##Fr B86!EC`{O.?O/gv+1tNe*nB\+RZ+Mȫ uE'$ZnG_б[gȪNN?[ơg~[~)+43@p h.]~ޭӿٗwr7,s7a3eZ6J Gxy1*~ ZC5РujR\EFAU bx]Du,GQW _{m6n!lm)u*BHG4Ŕ41}>5'XV>yr}업?hgoߙW̌!eT>8l!٠)MF+k6tG(0ƍH{f&Z{UemU5!<<=jcRW8y@SqjέrZC 1#ʈ \B7̦,R^l<DȲfݽG<~~vv15O-њY+gm[KTW4 }|U#[B0yD!ЉtЊ}l>_jUo60.vk< )HdvIAf>;;Ѧy4]vmkV"۾R0ȔtI]0RZ먵R 6BTg͏&7?WZ)uX )8Ge`? h/ƅuӓ3eiXQC(Z8^ |~pG^SRaO} ;~矗}^]L(+ $wϖA2 s a]ؿ\3`D&c H-r*:!E!kύx5]6u Acg,B" wccIزB}DEbD4iWH92-|NΤvWZ{**@B 0ͧū'痗r5ݬ!"DȐ46k2ZŇm*)shD֤]zxEl \9#V&TՖ1,ӹRΔ)(1 v]BCdVZk2AdV8f,ApMVԩ-9juv}?|WŤn!(b H ELG"m״Mܲ jH]{DȺ4GWMv!D"8y~?+Ae 0"40*d":N` Tb|5WOVbچhwOUGD!cAN>he۶M|'9q bvrܕ(浟}t˼M?x;E*2Pc\ocɚB $b049{ۿ1{ɳ'{ųϟ_*#f+o -@d 1F5fƶmCy# 덲HBy(|s^~B#q-܀*@9]f*ۿ>ڃ{?;~ų_pl2\!ޞ#]k֮>LuK@^?*`4:<>E~xrh4v0R6r2qh泳b(qٺ 5ZRyȘ3 8#Qg޵Uޘg\&j8?9{7w? ߼}ϗzSwF@"P@Ykέ5.4*Cmqu!]7Ȫ(Z0 <̅s]rW^iU ]OsDOĈpf8+痧d2LfU=Db*F>t23|_U&JiL/_M///gClX 6&x#6e黮D܄WaΦ֓\dkܹ-RݺhN<^WUsvv~z:Lf'uPBȋ!ZsR'U_0Z;켆_Zw>它害0]F@*Cd6ֺ,syeysrseC]_g~{Kzd(z^gO_[ Ʈ6p|zu6EW6Jz8µ$.\MruO;* ^V`%_27'>dHɠhE W ϭ1f&q?4|>MßoGF@4g/~ޔ7XኚpUs@r5( H ~G. VdR LoVkJ%TL"C@tؒr>\ND3혖L irZkO Rk91Tĺټ?y|Ϛf ЁEL AI)xs#G6BdP z߯ Eu]ӊ;3o t(]nх] G ໶]L. Z \gZɧE6U<7j@!3Ek@m:-B<|䣯~RRR5*sȌc]yz=4tr 3YUE9{eʧ\q7!֗C#颚-Wt&?Qf6L,\^AP1BӤ n4]iTZ+Z0qJYD\Qؼo,jMע3js@% " ūW٬[,biBh]r]*,/͗ƽ27Fvm ^&.gb\,A@VJ{̈Xź/GQk_ϤuE7{|Xf^U1;16i 6fye^$ ߉\f`]8JY猵e`Wkrs;2JYdP cV w:=sV{|>٬Mݮ}  I)iFW,N.޸Cgmm: W _?{pݩX36˲̕Jc"+JC6lբY6,4%5QTc4 և"2weZMNߩJkfhHU+rW5")%:X'/}~Ϟ?/uiZYzFA"eVkUx8TUVD>7fg%ҷ-BQ%: "4J{ri_V):D]R#0X<=4EYuS!vmv4HAVZ禪^`o?wN K ]IU mMNj5IqPqe<5mڠUz}oM &hvUXĸ!HIѓz_ҧ|>{5XnY1rƀ%bEƘV,r2m׋un,jl]ۆ&Uu`b`m66H`}o|AP#Zb ʁ QA_kgϞ?/{b4(.|Ѳݦh۲`5IQE=FѠ(΃O>Ynyx(RyFD:,2]\L)Fhds+ Q`ͣ tkL9scͼZ0RTa56(Sڠ6˲,a6z~땥)~UQo}on{~魻 h~wLC_l ?Gr54Ul ^ϵ}~9z\/"-W\}'DCn'l(Od7GS/B/!zM(6;TA[˗ood +3GJU- +&h7W̋_esW_ժ9 agNN ub|LM??ɯP- `CqFF% %>J&QHˑ2WG܉ש!2{ԴC02}]mާ,!B"ϓ4[twcy-RM%2j v,A@ F8XRJLJX|!r[.Cg/ˋMu *K.) $JYeYV{DpQs8"k-wU̾o|ӿWG^9뜱FGe+n{M s-edk0.D-bަhF$ҚaXXa4fP)rrB,QDk0O^k_n>G=xd6\&uD&˴V&\W޹sߏQF[jm\QV >·fdIc= H@[8gsy _7&WY^9f !oJ$ԡ5L5@.~P]rmZߴ{ $ !6*LU񍽁,&Aϕc4l6]WZ8JIۤ wU-z.N}{ PJFY^ޝQ^b 6FcPAhm|4^vMh#ᎅ BWl fѽxdtP9p*Ae[: GދƽQkp˓~4/7эPH/Gm֫YZ3XUҠ1e}OWMWy{{^,#h2 f6X.]{c[AD,Ӫp*lGGUJu*@Nk?6H|hM7Yy;wn߾}p*(Evּu7TWBe9+61eGn\Mk : GVuyҴԶc &{c_DeYVŰɫyٜmu>zsĠTD4̩lT0[\Wi]~J%h2dYV\?xw7eYFi&5L.Fy'󋋋zdcvn;x6]uJ!,Rcүw\!Dql1Nڶ4Cum+A3Y'n7ozUkkNc~9w!KQ$61=w=thMEoܼ?Mh7ryi7o8<>DJpFɏ e4TJUc>WGć*6ky w-{dp4}6{t8 ڽo6;ÖPVD֬ԐFCn-F$=w7oO/Խ.z.EJ=( >䟻q -@ADEym(((]I7MXԲCrxm!*][}ܾlv}[11>0Jp+x7nf1cFQW`h Ђ l3SJi1Hbl!W6''ŪY ˃~8ڦ. jw-Rh,z^>?yQs4P ]MMiʞ)z#M&j97&pQ8("Tβ׫n7:2m /YZڶ -V,*G2wA?u ] G ʂˈ dN=$mYo7YVm.mmOV *AV(^o0޹ww|99ǪBnִ=~J=,uJDՍPZ Vf n|bU[:pF.xHXq.^M|{'<|/;hu&boRdl+{{ͻG^E&CJg/eAdkhuYVf^-]~bNE! nDk[6U)pr:Q?iy=S+Mf>m&.f|2lkA*z[LJ{˪RY)2Rz3q ^]n6w'K\"@H* 3蕣ݻObAIF!QRo)CVj+Ѡ? sQGN+gfPO}?'z )ʳz,&k jRi.X-~UMMm*pwR\eU~:[VEϘ /2msu`N1[Φ_;tryYu۶Huf$ֳMѠWc,"bp1xo0|)Ŝ5ZyQgEUo4(j] ODycy~[=}0Sŧ' µdYVDb4US+fDHx[oGgbum{HO sJv߉e Gw,}بuGJao8 ƣ- g-QZT~NÎ&?2(¬/͍EXKw?R"A\7;UG~VjxDZe@@H8`=OUߛ_R_$|EX{0bP 1W_?W=x 8" INK vwdD8v'?lƣ Iz+D@ @i *%"qLI Wx7W5xuBjܞZ4Rirfw}vvKۍB#ʕU$X*)@6{q~20ߜLmCbhyѤqyYKYZ,߹sjjW~`+@Q˭^DHQۛtԣ|ݣw?xf+ʲF޸B_Esn~%R<]>}m}O|jԶ8%hJ2//zeOe*הܤiAyőbG{9}ˏ>|6[/fӴmdBb6/S?{A_iPxCXdbZ!P2v# R*cmgyFfn69_N:ƺ  ιRt|s/│{o9"^yuMl&l':X"@D!D};ߵ]{WE4G@p6b ZBĖ`3dO((Ie\ ?߬r\.guSD"K~[#*2/G?U,z-rZ13"MSgx1mO^N-Z"ʜ W::эψBK1󼪊W"#w!(BaHqh뼈htc"1TUo4:y=z0nwxīf}gYWw7+1 ڝh]:xy}L+(u;r! ^-4'vHH)2F[k<+˽ߘd"H';z(^O.ս]%f)U|8*Q24? Y$k}F@./7U1lWgM:"iqeYQW|w0U1mALR31h8ڻ=X7u"v9 ټ]x ՓT֙X^oo4~lykt罻þӚVyeQUEYV*,F:W!O"LZ㍯gP .nU6m&Ha0^svo7_S3YAlAڿݏT‘WN6ue|-#^Tt/|ϣCSY/23H ,nd TlG|ɺ$"Z&ЁM6JP0 i0He!zP.55H%D;#\Έnt=Nβ49k";^;3}Ffﺮ~^6u6rp b` P=u@ 1[`ET(gt7y'Ӌ/.?:k.^8 BZD$ʐ[YE!;ˢ0Jq- Fv\Hx >/qsECW`rY^`ؿqF|z[?o:<B(9YUq? )N]۫\iQjXuō[o^f{F_\"%!FO<8.ժ=3qZW\VEW8>@~w޺7žJ$0˓aPs%j%W&XDDMcYE>._5*z}~\ JĄ>i $.^i-Wm:6!Rd&+^wttTa=}u,.s=X]7bV|'"ujv 4ۄYd#*KFUm(%fJbEp0"$H弊v!BAHDE(aO^Rƨ~4?PH]es98l6]1k lB=gEQGZ˓߫fH1#FүE@)իW}ki^fu]aNt2sι< +u{aRۉ&CEQk5,QZI`zvvvS&:p1s(zƪ޼wyEVkZk"|1C4pupO)R!-?q>~! o5bhzp(ر0J]"r0: |vuB4)1@R^߳3W;Q>HE"ʺ%dynpR{cmz5,zt1F C"Dm[{!D:'1׹Rrt#G^o,gfUnP@¤,/~φѠ2ѵ1f`:nbcz#hcdf8U,nV+QZ;rehxέ]/|qo\.nxTwnK5id:lo}r6x4cn`DZHw^s% ˺^fϿ?=x~~~\Vu`THWN!WEo;:Ͼ_GG7zH1Tx0Dz t!l񪍋U.sy^{UOϞ 񞶎}G(6m{#lR%lr6̗ժki|mcjX\QU;:>IzýpA?~uƍzNr-E] $E$'H$ԚB.0J(T·5GHrI+lrzsoGO?^.fm<7Qo%vj%k]e^wNLJ7nyxt{9 Y%ϊՃg,Đ@s!1(k!Jl%lD "N6;6sB)mH[Ie": rh@"m 1Rt F2vܞ v %#U0Ƹ^!=rZWC5:KR^,?7o+JUDB A"Qф!rс 3"EqٴtJ)meΖe9{}eU& Z K2޶*t൑.ݳ^3vy%w7ǽJ[($ Fg|rsǷ?O d罧"(L6M,rzGd+a# &"5eyppp>i Ge3:"DK]/DW=N¤&bd$쮋1NDȬ 2Yk<ޣ\ zyB$(rP*QhG/es:i6=%i-3&2W^fVd;G~5'T*]]A?_?Q/ +(“1uQ$%YQ]e 0.v^zg_Ł9z ʚ@:L@T@I&;V~#X)?ثx]n֡]nf[#! ei?=+( Y ދOXB kD*"Z'e/3h0^kŦ+7׻$^oטwgÈ&/W,+guc6D*v$Jr;C׵M\/nKz]yEтCPHf!/C1K&vȀWeܜ_\MvcUnJM4F"T{S^l6_ʽ}2:@hcnFXhC  bg~G66@PU?:>'m3kA&1(X9=@8 {Redypp78,R*qbDD:"a#(1 K@TG7&ɓr^G $JP+Ub8U=b8x?,CXHdAWiSzCT>)C-jФκE6|jin929 (WckjH &D D |lۦrr xQk2geQ 񠺼^^nLqd"o6h|yvs">DA"c"?wt]h|0 b!W/<T/^8xכn(^^4DNy^Vi[[rb#P `2lZ*3tkҊt*"ԂiԢJ+VUZ<`0$D AGPg~v")m9D[ E"Aur z6Z BB謍l : PkḦH""!Dj57I]o* "vh9VU9m^O_UϣIi"E B`"pXY5!1Ɯ 'rDDİe|r3bV2<{ptΝfZwp7ʊhm h6+Xn+wwPQi4߽׳:ykPE܊;v)jl#wB!Z' ϳƣA aT+!]̧cֆW"IaW艭k~_{?W O_5""]'BDz//6UհcL9@` p3쬇rzlz8dB3wd>n31vR X~4m۪WEeK R)hvfO  C]u^#<4ѡ$^⮵'iw !C+1JW:S-|n8tr9kgaI ɠIjFQczaA0*;mlY%Jx<3*?NM"1:ϲW[7o|Qq5reVD ֛oFr}k}EBE*5ޠ\jy8=荍5iqR)"G<]Ӯh֋g󓓋rUo6MŐ|LDHiJEE ɒc#HtPM'`hUHj")vDRڤn(qr*3!1Mh]`D!"T|ĕyeSoڦkе1"*2\yQ㶞y*&sڙ*G{‡~Uya#D#m--ieIT}`ai tY P˴H(ggˋWgVn ]zwHȋ^YO&F^"bQ{zԤ0\V8צgs{y9(ް?{QnO.sPm l Ю [:uȝ&0.GRZcFa(h-*ѷJ+M7κYaf=2Jw Z,"'GJ4"JD.l>5m{1 Pi3Gi禪ʃ&-z}2Z+RDFQ> NiCM癹 (eJRĀ$H`3- /yYTёptw_+M=jqx:aV" ,pz`>;N&i٧,{iFzakdy<OȈ>)TgGeo}~qyqQ' c}NdE7^S EһDEk 1t lV˥v RvqZčɬ-,˽eê,RӮbD֦ V%^%}wI/@N~4  >^Ͽ1y˝Ѩ$VRr mq[E7ju8۽J%qD^zWgh:/ΨC VQ]CKEkHWھsvh Cͷ?-a:NxQ| ph$[ D}l_ӧr%X[ ""F!d @fF$-!60JD"enI*^Md+F+IG^|Zm9I҉}Ie@po"xWEUy平6ѐ1H\RDf}uu]W|y(.5v!xxCJ+)@'4=SR6 jΝ狶O=Ҡ u.^_V.!H*^߬/|B@$u^ff?8`h?{v$-=B@U*|6iH=m-κ̸ FDxYr( hZ/!ʖVDAj*]gyS7Mz߆vt%$EJic7Οi3S7.AWm΍'j>zIH+ιȪ*geѫ k5!d%b ~_W}RJ +"dt8-U4]SwMz|{h[-ϳ,ۋS 4,_Q(*]j{ahҊF֞Z ,Mi5ІW1 B+Ɩ!#vlkn=Wr7mR9(>3fI_GQ~V̞U< ](u!ϋ"/{Uad~Vp<ܹ=yx6эF˕J)8t+LI"Nk(¡öc֨8(chek}qz-1`R 3mUY!an2r:*}qqL,W!@kF*sSyIEYyѤ&GtAAZQ$(ʔRq>nnqO.W%D!MeyY[n}ɪ37e,Qj@Y/GyL֫ [|nOdK['AohzV+,wW*Yǘk%;߶zuW٬id ivP`yrh9Q2f/OiIޕ$kcj^gIư?*sК#hik%5+GE\k&Hn}`4~Yzчjv > E۩vhk88+f6_-O_d DRRػW,0WymKww(F*n{wӽbK|~vRiOR" `_Żgc"nrv+X h ,`+HjVf=nq1ڹ ;Eї|P]p58OtNVFK`VY܀T*땕A}x2w}eO_o=ӗ7) Mj<ˋkpQ_ܠ!gsy\zdɒɔ6.l8y/oFeUd%l.|br@`2D K{ 4 "lf8>.om$: )k!s5P G*|Py2 40\׫F(T:\UA߸IwcԯVUuFUV}_EZŮc#-||4ğoۇ,2k Ẻ:"%SO&UbZmۦMY]R4i󼨪lpyJs(ˋ\ڴg*ͻ>;dYQ~?|t7>lҖHoj9V>@nwR(w~1~Yq[EeRu`x۝zwʏkU>~o?彷N.~<ݷڄiE# \gOث+!P< G6#,P/ vVߴ1_nOѱ@ZVT &ˆM?;^I#(!.w7{ozjl˛xq1qN*)Yb:cvt^.Wmצޒ/{QD9ley\ܹUhW9œpqYOiv9q [E?]L^WiiFW $ a(".7Գټlz6o[w d,eyV`82ܬ'o~VL9|ǝMkYͬˑh)QIUVYU۫~+j*uoMD/`I b-&lf^o|1$]@ R.eEQGV)G@E,1f8NhlBP+RrPp0uN;q=G(sAn1 u JQeca5EOEGŠJ8B,( ĈqksD9TED41EMg^t:|צ1C  -"1g,leUn䯽ܹ\tAbR4ip IFQMD""q0X:!DD4Io'ۘ߹qx82W(+r:f/V餫kwv3yQ{{3l8-;ܭ8)pM"Qp2<2~_ţ/ȔUD00-G_+9~g#M4ÿ(QB{މ_<_|폟n5wowxL 'Ф%ncpX@â:ЮBAnٯ7kgbrK12N:[iPimͬ򬬊^|ꍫC;g.h 0!҉z ,c9 0<~|1k꺩7]S jJJho]*^ p tkK:3@ʈA[Bd2k } L_4!\+Q-t nm9Ķ-|jX˪ʕ{d58\EY1ycMN_&i|ׅrn:ISްXcD4څJJ! U3&->q&4=]wɳR+~EKpFyyq_t:NI,/y7yLWT{;4x?*Id#ܐ1ro}dnˑyBaA 5Iv@oVn45hb-ň?H>!:OZ2t tmsm"[)}p<:U\G~>g*JIhD"Bnh;A҅ ,ht(N (B1He@ݙ J3ڑWݍqG4^W ,l(bލ۷g|l6:Ǝ9x!Hd'71m72hCڌx:߰_wB >49ŮTþD/'˗ͦnK%Hj #y,O:AV FD!H1(29֫f|'Z@HeUU޺q{O}gRF[@B͋0?>_/K!n$Uz|5.3Yf ?߫2/Dc06$,^Dfs:\X1 GץIŤb+ -~z(ã(L(č6^2zi}yAvYp07}_q^iK="1G"Lcײ]\~Xzu4w@XT ՙsYj,Gjp?igHMgLPƬ>̔M*NGi[WVLJoo{7ǽAir ɥ0fYjվqk8ʚ\+# Q q29W%Og˫\up7yFZ#QAfrrygN֛c;ceQ3Utw\ZN5i;ע sS7읣 uю̋ _e/όR4Ap[j#b|WÞ\a^n9/_?}/~9vCtm ߺep7;?c?nu>/.V8)N ڧ?837fL6]F`FCtךg O0CqT?dixY>^>^P~:|@}Hk)Z}诮]ݻZ,jeb̾i 2Ƽmx}r(5;o~o DأX2TkG(Tkl[GMkc8=.sdd*(^o0U5 iR$(h9x1&ϲL{â f!'ݝp8r1Pt/WUp,)2@HZ)ɌN\{m0,*cT1pԮRQ m( Mci)6&sT2dmx~Xnީz/% Lq6}NϚi}H \|h"$<' v2(G-',F`a"!ѥUPS m8==YwOJln.8?ݸVK,K&ct/'v^q^E82"2crm+jhdoo4+[2hE9Ve0=Y|QgwINH)֦*Nr0 +&V,Q_GO~zR7M+L uuwo흫7F'iJ BvJ G8k j "AB!:e%DƮZ1eHD $@ ]lN+1ZB`fJk—N2SY^mmlVl_{mu<iuvBA*Ąyp<>ÅM)03[ONOO/-:Q]d<'G~u oAg:_#*T}aLHD$SMRbݭEւHu'^VỤpp׫֊R{#Z; q8TA/s kٚ/;Y7bU5i [.U]%^ ]L.ψt{U% W /+IW7W;gej5yJ[{>*?/?y3/QGW:%/DgG4/K'G| ν|5c^sPj(&IVUy=MɈP5cp>0"HO+#!F"XW~ܺ16^$h]1B(\_5ibhv\iA"pSrWٙL #U kvmĐЅ! zy~طZăã|NT@hPg*m^EUVgh{~3 I&QI2e6囻/-zE"+l k׏wn+{`nAA%H8\۳ӳfrdK D ZѤ5|}'a2P@ 1lY@1͔UUHGtwΨ ?Ze}D, pYa9?~m >IZm) ~ߜۻ(@ pd(btIbx~zAu:VAcP+,˳,/b2142ĶUkЙ(@Ei}rvX.,QR?&Ynli*+Feowg=믿y}g{Jk-$ j(Sx&H0 h[]vD~H!Қ_֛+gIH9+ʬ9 ,:[ *CW 9Y{|c?rt1dJ$RZXH!84جCV h4 +ecvYΉs 1<1FJ^`I1h +b (8>>o㳳m5 RyFtW7,&m!3>5u`%"=bQ5@YXS@m6ژl_o~B{wkwus }Pkcj0KczɨƘ40rZBh>|?=;k68& a ?9:7 *7."amDTb!({y &v ]cL?g6 0F_psr WyCv889?;?==:s./$SiunTr J]tårIЗGx( LؿT_ oѹ+oϏioW7e÷DRqA؞GZJ/4$Ĵ_bTх n/',NS=B _2PnkuI__(H CTjH<$˓O8J"&"D$H$1 )PSk0H03H"#*ȨHGTX@Le4ThݻdP8"b s%~z.?:A":0 4L\i`+o|kw"13߶UӔc 8kblsgуn߼vWY \s1`f ڀrLӫժn۶ I H@A!JKjskec4*mMQ%!z07yf3qMB l䛏WO%+mOl?j|ugw4XkrTj&F% +qtz0&|\dC‚e((QpBrcimA0J@SC2P@4{?\} v¿#4EYFyK!R(nV|ypx>_8߭"@*ED)E,|~vxW{{yQhLJžD }KkE$~C/~8_4vHׂw"LtQ;QE1~jr#MiQJKEEѲmw{trR7MtNjTFkuflal67N|6޹ -uB( E/A23hzBD® W/h ekhuEI e@9v"2f W D2^)EVRjȖ 'esD改ߢDKIi5B/^<QKR"YVYYUzVk-bLo$,Vxu<8!O6juxtr|T cS.Vl4J6<񽓳hP͌ ,ãbj81D)JeY?8G7ǣ(`iETG"+Bp;u,m{N$@RDEY|rrAQҩ7v=!UgM7xvvrg0>i१K{""{pr~2E)줞 sK Kn\K}WL~䄝-o9 ɻfPطnNʚTi٧/4_݋'MnKR+ڗg]REy?x !~܃l>>'%///I;r}z^E4\Ϟ/2 Bh8e!DA<'Э!5"j55R $2FKĵ,&VoPoȮБ/+)}rq!8^2ZLwa "D,h򫯽|껯 U zж!Pۦy 4!4I)Fzݼkܽz[ :UBB mŸŽӺnڶm,@f@a2es[yYU^9EPPIҔ<3ZUY=;~=Ġ#pV_ܼv<7WF6פ@iH7{w, A80MY9Y~zu**"d%JEwGd 7utd9\,34F[ P aB&O99=뺣uw" ')}m=BSNvvz2ӤQЌb iqӬH:,;\)+Wh\8M\K4 6v)Ie3DPGJ)A_{I#z4䯫WV"ϵֈ`41rJ+>Oδqh61M7YQj`8{<>nj2R |d 3BK (] $2@HYEѳ @|<}%U/`{?y onUQs;" lUn~vn㋺]Y#,{dۣU贼|LI9NN {*O+$̋'W'ѠEŬ8:8ѵB7 /D郗ybu>dc8֦q B)Dk "={?DBv: EIL{ga栗e6{|lm"QdZ#px0Nۦ /"|CDgn_ k@ǰ^a0M߅Ǘяˈ?/U @~׿q f~տ^Gmb8 e_<W~'%jB>ӑ9\~H ن%}4_@Wʜ @ "MT*@ Ebt! ;~E8;͍pwO|  .ܖnmE8Q@|t"xh;"y%*T$mf`B|AƷ~]i>]5lp |Ds250o|}-<[.^{tmìq<)C$عͻ_{s^n,Z,8 I( o?^|y0۶i}LDB@b@ M(r4zxv|ڛ{!rHQk̪L,B @-w~]h+mL6Ǜ?_{{;(2B $J҆\P6˖ȜHSA 9jΞ4GQQZk5xM<@ҙW5l4LLukt? wgh?<6^`Di# \}|zzִ9JZk3ӌI'd4Yf )%L>f׭ӄD$HȜɎ?.NNN2Fq Fr4ZicљYY6mbW+RO0VG'G὏<>_|Ӳ#)$PDً櫷nzhod*P3 t^؂BE!|s&մ1j l5v5U}*L`3PD5-`mle@gvA*Q"IT")Hk,c"BpBZ0C{޻wgwz3>:9=?OenǓ~URB|u|p(]Yf?+KBaE<yhqR)bd,SENY{l8me`krcsctHD\`#$hGB.7B(Ef4Jt^mՓfqR*E j~?-s3%TEz1+Of~{eaSwBZD褀^ "V NO /yjoK % 0k>>?-ͭqYZV`\pty_8??wi….DXkyYnnUB 9A!5%c>(,/N'''j,3w#9FM]xyk8*eBL RRkd''ǥ|rv|t\.۶4dK"b4NZ|a*1qmCD.io]gh1_;2: ;=IHï$(`SGK{52.~Mm:cJ(BQ$L@>*q$3tŖ|\Ou_jP$'D\?G7>:ݏi4ĕE`"N"L-өU!ċTB ~I8Պ *0A'DsiUZ`FW;zJM.~.|^b%DLU^&w7׾yΪ|y `u;^8D81v\ɲ=~ʭ뷯O-_?Vd_ LUTF*d)_>~~xx\ƹЦt&jP , MgyYVUUmnl>ZU>p0(&Cbl9㓝;o?>9;m68'!B5jMU7X>JAon߾sͻ׆#] XDb(B't77e7"+fy,1. b2/nmsK 3OyYn\-2PD,b@ |/go-OyP`Y/F1!D nʒ 1:[s>EHWBg''lj׹o\2@C̈`+WUg%d0=?:lxxx\,\4NV3|p%y׮G1iQH(C޳'exR//4QFG׳YWe):.("HM$0K]e_<<;9i;U.9F==\̵{UaH " #"H@;1 $o[0"uT>@df~A~r'R|.2fw+ S\#S7ia,XB+7p[ `-3);#j;,{lG(DAn&m%8TZ!pA@'@ɇd3XP$~!_5{7k:Q3:%Rzm_pWڋs!m1 -G-e}R{[?|o! Q*d2˥e] ;f[9:7_5Xf6{zt+W_3Zq;it1B^J̳NNNʹi0yeQzjT~᤿[cH(@ Qcnrk2 q=H!S|6]LXZ5=sߺ杫wF[6ӠjeH)HX.ij㻓ϖ#SJ''BDε|vT_TE1>w9@Xv$VVXNn17)RZ)EKTS;QKC޹UR|tq;9?~tzȒ2i/:8l撃=u^H"0VKn ۶a1{Qy gOtz$DaFArAe5,1\5/_fnEL^QiT&E#OfyWeY89mmUE$?x96wFo}ӳO󳬒f嚹',#)MJkj)l{w`K)e+Yӄ!^5,GG/_3Vh U^Fy7]~t^''w^Y^E"f -BZwI2 DFf"9Bpv|j5#҈p:~O77wFU@XHDpZl6L9D|r.FrN7Ln,<|"Hk4Rl  u‡tK>ŋtG1z]76R|vhtȢ}ӓ D۹HnC;p|khdP&`p GBsDx郄چr>3H)f|l9_A6 OGw$ۮy=`Qj "=뾕!r'"cJMfJb("4%cfe5{oOӅ[jemPa2)@R FDDE QD]gPDDAbL.)ac184[}-]RvaC"\xrE9Ddn!U>z?|Mel>j1(USm".UuZ,(g鳃U}es7 cm*4gyݴ-sdA)F)CZY^eQVdND R*n%A@Q+$*ز>?t]{'ͻ_WreQđt31rݴ޻{sS;{:BR5K^ՋojIw9I45#iY$oN!6<5֘,6C ķbW׍Kヒ'Wdl!8(Φ/Φއ"DpEg߽rs_VZcLJ`/R҈Pk xp\fe{RBik< 8 u|mo4+>=x^o.Y"sj2JkRozo=KJ3Ry^i$t犉ƃJ@Jv~c{z0ܒh˴qxP)H`.♗f>[@DoܻލHZ)Jin@rnԂ# '9JKP}JT֤h2$"qß]ٽ.Za?̞ͯ~ɇ|Cfi$atb2Ԋ",ӍbBZ=zUҊ`M;<<^-j "7áx2(+ײ"۾uvW|d" -aF h L&Lŏ?ϗ+07mD'[o]ٻ~xe-bjWEOkY~K a9p>?m>pڍk{yˌUڈJW0( 2B,>;]=y9M]ŀPDR1*Dwm{|z~vn\UZ8#lr6K$,y4]|:kWwLY)c͘Iw"]jw bq|;;1CIcï+ƦCZH.2k$dޫpzvڬVB(w+fR"@$:Guރ[{m"J7bF_3giA"AE: ݟ~V[&_!~/[!QdWj j2a eAN<.Nqk;_ -B>&j;"/< KvIBDDµ\=_ }c&2^KvKbY:)He?M=`i(U aJ#->tJf4Kd4H(KKcf9`A , A"p Ĥ#_3yo/D#^lY3 1\ NqdV D JA*k?z_~;[oqϚb6٬b V[jebFhc-] z\ZfWU՛Tk2DJeVMzj1Hd4jD̔%k(ʍ͍f2faY9*0fb!"{(Xq9hr)Io3ٞLF~e mQJ!p,شu Q+J$=xf2plfZy31) D kBEdxT,߫l5ec+U׊("B?>> Uo`LBXk1!$D((]OgͿOlnJkJ0'RSZ)m#Jܪ^r\;|z6 +D"ҩFч_ۚv:WGqzblXVuBL1Ց8iB`lkVM]uӴn'/m s9a 5(A*k(ʪǽg BKyYm5ZZ2 Qy J. 5,TvPYcFyDHWde^ج0c Q9ͦ-CpAkyQ#y㋲(7 "H]lovÓD+,@yv.4Bβ,FEϋ،mUH;L̵RNנV1yC"kdf!; m E9@࣓l@PQh[" ažn޿_|4;1×kɬ,8,`v+'M. G=Cl#Xeq>2c p@(n#H6 VsvfuV =kɴl`7¦FmU@O}cw%*J i6JpZh%D"tS,&z9}B6"55YB* ( 5d~oJ&Y"33|wMu;E"2Z@2LϬ* :3IQ\==;#(]"Y%RhH?&FËÃXk͍P)Ă(WyWZ7\hl1Ύ@fme."FƘ sD\M ^ΛzTURPQED|:)."DZfEѳO,vns|15vW_Ў_x F|/F^LD`|Ҍ~n-*wo>arD IŠ@b!DFTZ HS(B BLc)OCiGf {E`͢k(*:kZ> L\PUӷ#@$hAG GAP4ڲ,l0=}y=;7߹~}xt<ͤmcl6?[)Fc,@{alR(1'3kbQͼ]9(1 -?4(2,+*dLnZ ȊtFBC`1ܰ Z*ljgbIDVJ+C EDc, Pk1zŠE?XŌ=3 #zс4vu.]lcsJ7:H&Dd.d+z+TȲvUэ AX:3cMjcVkcͬ69B;~̵lDF* 5j"\: d QEir\j rkŘ,p0b=kf+ofef1=?xfsQ6π5iVehC!&8F>FD55Ji5>(.HAl$_llm|Εy-?n)3g-[vj5ss$HH1-dfZRP@A[-BIi9A"@l[ʣ5N+~R>JMP{~;/ UUާaM3P"! KQEJF;[a/P)PmRML8a&TZb$D* A?';;{{׌_++;*%йLYsD1Qqg֪+M fUYDरr2[KH"I*64Q1+15 E24hMJÙ,QVm}4V* ZCh; "M `k&ƈX0FDcX"3֎ճ\BYT߶Q*f5u1fmk&-;:3{kqD Q@1b">FR*(1jD2Q.Gft;ˌH4 OG_W(%B$*j:NL:sٲ:Qk)'LH1#]T( dtElLO?xӓG;9޺[rt0;ƻH>J·RF [esֆc]@41Fn&za"@РR {9?{U0G^DRk'V FҠ@KOczfw+~`tDTfWxsk1VbhU HR\f+ELZ(^X9Q90 Ȳ(,bcs?VwWe[|)˕ϲEݬW" 6Xeyʲ7 ?6v?#ݝQfiӺ6qZ-e" C-D1ln&R@ (~r"*> 0)mƫY1!H k{ '&?J֚ԖVx<_|:&}I1ho*lBac-{=D>Xkkt4Yb{6 1t(BZ%8NV+ mvO?櫷ǃãv߼{wLÃ%^X@h!2 mKc6]"BDׄN4A`PD@iz%DH 0H ` kȀLJ!cm^yk^~m<UJ<{o^eEkLZVv02eu3hb@ t [)jTei aE w 2{`/dQooqݢ,7Wh֛k ,, Y$GTiGIĵ|Lr4(FhOV%KQց̉ e5Ѩj1N}^P۬kǜJsTxNh "ϫ^ogcS#?=nH_0ǶŶ]ej]u1bR/Ue˲,(ZemF{53ʊ2+:˰(\a.bKPJ3Y#vCVI)A !d"2DVZSbhBgO^X7\'E]74M](T<D}ZM23Ff1 $]zٺϺ<˪??:;{av$RfEf`-E-&-5 (D&Z15#4pkrHNRhce j՗ ]m?=ԛ.n"G_[]{qO=?]A 35A DHiܘzic $vA!21e9#,>f]}v [WkAJ7dN1}ZS@Lp"]PSH$]z"pJ 4 7[w^=:9{r_ۣ+fX.PumrڇG^ĠIeڒ2}7m+.*$d *`a@"( ڐ"es,˲?}x9F)Co1 BXXF 2C6BH@PT6C ItPgI3n#4@Aєkzcʞliٮx%̫PcOo%(/JV@(QXb((,dDE"/j%^ckOyڕ=TT<{v-nVe"ƖUYx<|ٷEU&Û7מ=^j\YղE c#!41 QY(" hݣcXM(PLس4^"V&Uךu ,elY11Dg{7{Uo}.-e1Ыw%4m{h:ED9Hɒ#*[X٪(x8Wٓo?]՛^;i?>`+6zXΧLĈodZ-:צ.J|4e &7"*ԥRP0!H`.r ,dL!) ɐ*lVڢ*A9mg=zo^ٲr0*ݣxQ^4yYa loc M.F.Uo]<-M8IAϮἰF馎ae . u΍Y*xL]뽪7ww'q·?Uu+o|ͫ{UURu'# S  {JuvѮn b28^O @H"ѭoIkLuDT߸X *wN;W7gN5!} FDH|AJ1E<{zc~㚲QBm]U(VU1GBH`v4cFei˲,,a뛿zU?-'uE[1ƚ0[ey!DTA-snc*Tk yUh8ò?x9;x<,q4j%m,0#3CyjuȈ,QLeZk=QU p5|?"W7g,˪Z,R`2&07ޯڷk`ֈ>R%_P^ĜGeYޢYɏ~_y+7nGxW%G#" _6u"2joeۈF0! BB) k~ vc'>,60 h!ߪ~εN(| R 7=1E\`Å+g= *;+O}??u/ARcsA$v]5Y $(`"AQ8%!2 mLc&ncD !9޿ipD>̌(DDO_hLp"Z>E"'AAA juC Z,hz;g_V7lW[gcnyӂKޡF%ղVL "Rb{D2ʴȊ(ˍ̓xx4_Yʅ6LPk6׈cHNj!LVk,PQI#ЌHjh"(W+`'߸{Ѩ=jZ`c`$B 4RC 1r|(a^]TU?Ɠ!iٻ~Y{έWi{짟_wy,|X.U,[kK,+RFŃAo/o1櫧zU;b9ca"yHsl(Ʒ^Q1ZRQ$B`PQYf(/{?ާj h11G$׮0 )T2XYV&P͝Wy}}gytwl>/z6ktѯb~ҶՠWU6c ۖۖ8ڲ@5vcy>666y<=Oߛ>On[\e)ܢճ/}̋Zrcc{F2{h`L=cp1|h8]u~z:+ú^rE iOu ^7ފG/O8SXoOvgazޮV!0UAN[l"IUu:ʔr2y"uYd2b'Ȗ{ܸRU';_7?93v2%"ʲP/IA:fKzH֊Mk=*h4OD'~^۹smscf?nկd/N A#1dLdn6m4c:x^jUqB갎L̠ʲp0,C~g\֫+{۽LJ ~[ƿU/4G,/o;+}  ؈&$@#5so&ӛų^|S_/==7uz8f1|uL4oZ dG3p>63͌?wx;Xм#OOc;`Z(%> J2 D D B(hQT QZr@3-m %2UľJ~F "ae2v%)^;0c⊋(4쐐D\t\c: "NKX.v%$8*MdP 3f#ʪ|8mWbqa;ׯݹup7 ї[ol z? O%kZ6>wuyt)D4ذɫc)k܌bXo~!6wwޢo}~lؗ( $-$U}8|^|}yOd1} Ц* ם(" dYQ) "G8(@Zqa‹j K@$!z}>b;gMz[Y 1vsGY]~q=zL]xI>&>$_Iu9`Ҳ_Wv9C  P!Z4^UzsbѨ(rl*jF(Pr,F@+0tCҤ(a)-\elPa(#4D*H ~Nggzsuftu൘O?jޖD76fer4Ǔ~e 8VѰmQ 6_|oDhܴa!xkc1pdLE_^b1k;1&*pz  IEP83HJ)E*52"ҽlNoSGHHDQ=B,ƏeYCѕkm=3뛢WnɄ~^ํS"@H ŧI{ J6 STfhɫ x0(2c@Zm^*m4EpR 2@H%ع'pR@ ӕ&$MDF癩zƤUѫ2A$E=Oprv>oZn< $"u2]1dQ $T.  d 10Qr<2?  jB\#1UxP01R0<.|Le e,Di "7]H" `LлaԨNnMsb h$A7|w2}_^XUvm+q5 ~y)GUU`Lg77OΧzrIApNrelYUmza # Ynl89;NMS(1e`.$YWTtI""F`C{5'U1GpX@`xpzv|zZ/9VIxȢSyz rRP QKcBmklP^Qz~o\y%|5lct69 f(I1k9tl@֔Ң9Hie^Y zx'kYf6~߹W }_5o=2buzO֍!{߳@d IGV’58;IC Y3TF6ejblգNOO,%6A=O8޵p_^G<;;kI-K@ LFuE9;"b)(D`]$r>#!NG@D"\u:H`Q*ψanDEP$>2Qp10z <@&KƬ.̩嵤2u BRY=dSKg.$"($Jh>@a1|P+$'1HjzPĒ ,!胏%J1c**q矼+[ʓ"M 0tR<ˢ*^o0ƃ|<(Fd%W=;Bm0`57r6!:" P R3EQh$ƘrDA7N. %}(OH8(;W f=7eU^>U(z=SVHy0ָ^mm[Bbh] Z33p&@\g A e^ʭg.j]}1,ʲʪ'p3b[QlUN۶a1jD ! {SfȀ,FRQed,/ʹ&ݡpE2#Y6e1nV71&PtPHDS6 QcA@N-aI4ڀR@ CZ"3"RIkagΔ2V[ HfcsR7>IOd&Im"  HnD̀BLKާA+P,"t|t%T6"ϳ,5 ADٱ wD(A4ϐ= H71 uɈ(+|"Hx@)U(U3,+]:!DH:5خ}!7S*zEaf2FD@!+k7f*777f|z^C1 D2FY[](uQjcxn*D`"LI%i: "- AWIDUBE1W33g62k""fFmVVx:* &֍_E-u( 21VXIrI+E*)&@cRāE PfU0 )(*D3`ii$0e*i#13# bI rێ΢-z7bSyպ4#48Qk;\j=bOI>  Z>"@I+tը`*F9GJ\b3$bA0!!UHx q 989hEZ?{d[^bk}{wʡB,b"4 Q4JMnӿқ^Gi4kI6%QljNP$P*28g>w߾DܛYnō{ /_ 6WW7=y'g~"??}מ={cqg/2Ma"|dBY]ƎAc,ԣ̝% LtLfCOR\9^O}(ԻJ %T%G@NvIhv{(=yoxiR3IaBC8aW) `ʸU)OiyJiQ bbڎ~yR qCtEҼOIlW8~~^uHBq뛛'oG7ofML#.S*\ū-6O&{b6Ҝby\>Pfs` M)^=@HLDABFEHfLA1zm oCF˓7f@H0@"%aC(M=ZFiF a ‰)f 80 LHqEԑl677O>U՜cI~̆,%ݙ&LI,;eL]% +Ĝ0H`abjRmCX/~+umi>99@Awڳа %|}3" 'd4ۑSWBcn|ҔZ\Rp$EΉ + T7!1XO+ fx}uP T 8"A0 j#ȑ!qxjkj%*_f45 AsN )% 6r BFϕ Cl“:?4u\6fǀ- " A̰0b "ރ4+ Gv;#R$pӘ~?!j>4C> "=ŅQk%66,N9sfÓǛmO+6hXᕾqq6u=~xggL?r0(<`0`6@ƺ8S"ćp&}JL؀i ͨPX.4oԣT@g' {ba5%"DZF`!ҒBH1l0͈CiH晪T~?_!B@ np bh,%,g̣DXhcƈq;1`aQ\8|8<EhjSYMQ$X-R&ffl1VuC-N,t#9 \M2Q̦GWE5Gܻ:࿔"B^:,_5-Tx.<&V( "T F223 A:sv8p7GBފl}̪Ͳ5[@J`!G  x)>j7DݪFP^e5Q2YJYuovyGZA@ 71!#fƆfve4`;8\5 磟DCG[A *0B6 DG eo,M RD@Dl\@SK+̚+24yO /a9JC/PiCsx E- $TMHh` (>1'q4XVr‹YKBhVWs Zѫ8><~L!j{%T2C}QH2l Y8ewOؿ_ ~ 9jbGH9=Uga#Rr&rG?_ž!q،x7qs}uӧo7Ͽ_7޸ys#W'EˆR'oaVd DY:̬S -e  F͡Zm|3O ?o CWZstOB@Q㡺N PBDaL5R WHJ4fG,^]a+%,NY]$i Uq!LMsH) -}Njl$j9<~3?ݾXԬ @%qܖ뫧OٯyGUp2|I`+'EGqޱnSL` 0*4!% XFL+F^67gf$(Xbޕ/kJ) tgW= 9w&Sc0CNTM3"!j$-$u^RC!0 b̀A6(!SHcatǶ!r3UKgnF5R'ea8ŃfX&ܤUPcA7+"2t΃PJpO`2Zv$wT*7֌0m(m *<^ϔg67yፗ wjRw~gH&BX PţrF*3a@q$FKI$gil$DKZs+-U 9h +ݚM70ؔԐլh3`12%dKbwvN{'voCIM5F7_G<_UHTXZO))Y%Zxu,ij(Z0 !@&hʹCj.'lo#%dL?߼~o}M akKyn7׏=z[o~KO<>vq61a: 8CfqQi֝b/"c1YLB 7IPa^0Sw$Ќ]vB=j($㪚wn8OO&H9[Ntr^D@l BXiy ٔ59Wi0)ĔBQdeS~ibq,h6˂O~/w~2%ql YL/$u (J?00PN=r`6F?A'72Ka| Me?lZXF ڗM34!D #)! OG#P̰-7#bT?EjzT9q@ Iw{ HSlJf!p!&AMRr"f.YMY Fz,;A̲Z6P%pY"|侎kڐڣj]aܶqywV;yJ_n>j :*g%p&]] $W|rrpЊqH ^QQe4n,*w)!J%2Kfɹ:h!Z4׎ 6J gyc], )p[5ɑ |e%T`g: -yvc)xvT`CbYhϹӊeCsd0zwz]6spf% ўã9>6m&_NK HQ,yNB1 H=VlşbHfdoe#Afϓ@-[)!p]H0?!c 1ewC`)[yA(-)2k)mb R1p" )DXRH)&^ |~#H!UDmFJH{ vN^ =C)>+{EoiD80 8YNQBɉK!\)p;KBe$*^No"2a^onmHyԗ?~_ AL5cQ̩G:㜙K^US$zt(rT$P̧X89!D5dռ')ԣtG.,534p~ Xj4mVb4LV<=:T.ɫ:DT/Jvp5(Wq95{gqTUTG볁Y" Wuv kBGDi$̗}uR;)Y4kfq[)@nYdpeKs{3?ZbS͙Z4c(%Eꆊ} ss3zgAnjʒo7Aؗ2v;9]:Pv$ ?o4*(dgm==^SSNߜb.Sxǚi!EAlȹrk2 ʀD͘ ׄPK " C)eai:` ElN'cPEa1iy5e70H@ΖU`Y[p QD8K&MS66d4 bjvZM:b<v/oۑt9h~ij~۱b޹<ײlk>)+ Pcu34ɴٔE͇B9a#b>A(>zϾ~Tqn0G׏n5mt;^O)ѳgO?W~)oN_/?y2$I*@MpjQRe+wAF끧 *TTs9tA"43";Էz+Xk )[3 <4ͦ^(&TOS C!tɭU1Fn6Ah~3SSf) 0!JJА1 ̐$N=Qu)MRc4XRJ:M?7|NŭO*#wa) !0vS l'~[}OMV`GB="Uph ^P`ԣk.L=#i#,vk?uV術!e3Aw5'HDQ=οIqea }F YЌm,^ugl9&3ӫ}cd|4zдԤF0`q7OOcD U=ҁTV%^-1km"pI=R:Ѵz&|bY BJF9gfg}kʔ̣*eg~r?Bns2L?b+,f͌咤l%k&"TTY*o˵\(0A(%[eq:#0YGa6'- dВqp{ ߴd `-ab.M{ev{nW?T(9idᵴR350tUcYXͦMĹ ωT*bnzJ fDJt:x jB*m]z }Mg6ͣX~z:u_3q~s}|8<կ/+od{=\ aL#Fx§\ H7$'Rz_23`A3QDM %9[,J$ơ|RXȿB=ZHA˓龨jPc%18B$`J੟$ho3$`2`HcLZVR 0!7) WW8JT1p>=wыNۢWDql6Wv݌O퓷_=md|ԣsJ18^Ё.͟fQ(*P`GP9$#M`K.d3PRQ=2>wF= 1hBH:!O.qKB, 2;'l|#9p 1sņRKz&DD\RFZ͞I4Cbp8%!R*cܤ)#9ASI(RnXG 4Z i34d4 ]i+%ifw7{Y^X}2*c⼶Y |E +@A#tf: .nccY2u}c+qJAw) sUjw>K9cLP flW{ˑ fi d!Xm~+nK5[TBj5䎀lDvRHG}/ cg>Z3cBxxfB}p5$c|e^KoO12jԄHA󑅬r#Q[Eky،x# ^sQMdԿ{vAgZZ{.JM*Q֬Bh /CT`0+l¬A87b T5ufxn2od"=!_'3ԓUe)]|@;UL 10zpR/5p/s\ Ф(Sjc&d2mf7 ^7WBCz}vftca|ͣGL=y鋿 _;}=v5ڢJaN*`Z(oA406걨Kzy-:6Qa]=>A);؆w#p<%;BLTA b䰁kmjL(93eLC4SĜ%!GEi#0 1ue"æmC|?=0ě7>x)7{'ɒ%7_oǛ&<7ɆE(SJGȆ tiFqy= c ^T᭿WD np⟆zdD9xatmAA|z@tգ&7l"- Lbi˙?z eE`ʝ셀fZ)M6j&b,  z"b0UݛmLɝ@%cf<!J=R(~rRcxXf3Hr_C*,k9U*l }>U3]ʺP:7_*Xi;Tn/ᣣ1âǮX"Vܞ0Y]U69i*f+hN4X *|An8aΏي[YCz^MSH ն ӽC>fX%Zέe$l+w 3V,cRռJRBwrZA!'pNwYYHTIK:k剱sLĐ5})pHV`kN*0= ͧdY'Mᶀ!)E4eB97j P`=LL|Cn#<#PGlV'p>{_oi_E+QBPd--%đMăN?ebݥm5$KKu Hzq| kyT;xx`.gN&C:yxSod >zF#?ܽOO?՛?sW>So͓Zss% *YK`yu}rr"ر)) i(y!\_;+nw0$ šgluNw3MUk2 #Eg Ăzf'S)tAJN͖qpϱUIVjzfqSDѦ攓WWme"4q7 x*UW5%8 8n6f܌}GWxse[z\I2 bpQ~*b1\# 8y HrNp5+B6PnV,3n N=`MACQ=SHIK"€^0'oc9c,g䩚fD1\\ط҄ultj87thRdA0T.!g!(F )R&Xq)UfhC(?+]|MU\4NS NC_T Jdܵ!xVe7+Fj͑c4!-ᣯ Ƌ"}fѵ;tpǦVR3>*oZG2cvFS+|dB@meOV]e\Bs<*C['**+Y^x5W^Q`6Z=AGR uVַ, U-va*d"j"݌~H sJcpv&'45ZA.Ս8"&y=9$,#-%l~w_݇i V Rcj;`Sw :,GvG@ڥV)lU@S NͲ$QTFOϰU23w'%1 +rn٤8MZaY\E QlPRۜD/l x[7D,>7Qf2OO槞<}b1`QHy)j9cj  O0 *5*ڬT.0'1]P#tr#܅Ym.0-8JPPU ,N=&'i+.kDyr%e1AH {/n/vs.R$388n60 v?e|c{s#jF̥&{C*X`d2lPph#2@B=,JiU9(A#{ZrHU=XBd[ 8" DAs5czTVt *4j6ho(XzDici)M1 T1dʘ#EI".bU=@tacNIAF5IF\7bSřJrH(BAͱlxii;s 86a]9WHlU>u"\.ZAVHs͒ϦTS>Dx5[OY7Kn#ZgM-Z!nՅJUwճ^z{glo=x E娜vJ ]IIii~^_p[kKdv/٭vgLG#esi@S{xO b#ci/pRD8xޤovu`iUꟍ\Y5g. 8T cq\RXaWl<ͬ t6`N| ˚$79+ ,)q2 Y0Pp}&0]z)#x M!o&`iCzU㥥Q>^y"SA$wL"F?yU): #T<4FM6('oο~r{"jIf}s|wo>}գbS GĖ%'Bhq@F=:v>0'XA3Dwݶ4^pUv<._U]=zp]4(eC D<|lMUz0F4õ(Wգ 9ry?9K΁q%QjJɦvHG" <%hWW~T}tT-4 QB1 q 1C 80nA'WϞ=}tt+! PjV.G(.+h[rroO$BS=:r=LQ$GuM2@*Xch#XU0x,D3 #$ZN3Ϸ(0l@! gLYu5 X2td%7)>жSb_6H%{Uͅ'  Y(IJ=RxO9{s'pl%SeXq~np'f|l T3 ZWSފ bXk*#Î^3C}0K]@;ֶGK#)n?c:|:[GT >>}"M+Ӂda^_g!uZGrJRi׹n*]aB=Z$ K򻹎|*pou4Ms۰Džj!;K̹‹4KXst}&cIw}/~?߈Exk6Y}|H/uݑda:kמ%f&:B2gS>)1XR&C_aNBX6\n#ؤƭxb,f{:`1nHʑ򉔄˽z+lȺWLI"6۽=ſͿb< ȾVBT'f2b֣ !O$@ EF/nN)= g4QZWdbW*SNe@ \o|;߹~A,!2HvdX潞lhNʕ&Hu-јk+읇C[4D"݇z ݇/i*i{M-[.(\%-j<RYqv.1¨aW.|RR%Tꤦyc DF#&L*l)er+7A`͗?4 {D2 nհ݆+1QF2 rD!дVQN=Z((T<^aJ<õTt8/ @O=C{˛&S V'f4 E"\mDJ޷nJYua3 8]mon<4YGW8~;Oo_ 8D7rA=4B>,rxHHi}He ׌G a J.zFiI %TFIJY #}$0@TRJjZ PG3{t-sY*lQ깿\U!7@Y_йM`[)IRPAnٍj7#M2RJSY>bzQgu0mu ގRr˫]5L-ħ3bmqcn^׽B?? я>*\Y5Rs3_k*ɝ#O>GC○~6B*ozvi) 򪨩Զ=v0uH K<6{kE ,1}^ /<}?7|;ul]ϪD y٠+֠osӴkdiUnZ(%G,.)Q0~^-`,"Ѥ Q,.kr&&EيN&bRXC1M6)vmm-_'Z(oZ>~N(q /dDa<Ñ5-ߨ/w6OX+S@ # LLC0lu@ ULJ\o_=# S\A%pF2RF #cM$I(PQM&YHj(e.nP֩Q$:ZkTXK}ܚa)b7{仇P`"(^*W pIB=]34f00DaK{VU3BԙNà,9Y fMS:% AD6 s>lad0S>vI1^m7yev'OBi0qzj |o={h+c)|"Bk&TUl fa;7}жYv'QTfN=t%Hv,m7Vy0AL0 Hѵw*9e=ZzMI-4#1%WU:&Mؼ~S  (~2U A! w$E֢ԁ3%gQꮙHH<'\tl%ņ]!Y7ؙf48ՆVӼM4YAT~7F@ZRW>džEt0g 1*ƕT%9lUB[ ̤bل42Q"38 1\EF5M9LMhj{0ppN:bPT͛1Ƙ-)7)+.FNWVS- (DU)C5NW%9EhL3`( u(m!<>JN}9a4փfL;L;B1 d ,XmRBASQGJfXlS)p5:'L˰ Ï}͓a6f cJ26#nnf|Ǐ__=}>?ɣ͓5nD3\ %): J$g,Ղ3\ (: D܂ǕU w˅. 1Ҍ*怩B3I #<\݆D$ѩG5뱣+yYU_zo{Ao 1xM@LٲCyIa0%;$घi'"lry+66d1 ߳ZXՐfs t𱱘u{ hx:uWU /ɦ Zvwp%u%5:֌t+ k#cvHRcs yƑGogt3ڂjh1v0LsjB`AY4V#wf4Q(Q͠Ŏ$!Ձ2%)Qs``&^1W<(!ɠ 7'«hR,s&"hIB[_1n͞׿]c3٫ S.Y?QE+b@RylW@e*Bs縂ULۿ$܆ WV_6Uސ$P8GDɘt#;)AKw4<CdtP.hJ#:1w̰d-*bNoFԣ0 DRdMղ?YtǫC*9 b A@7I)2c"fԣ!#C$ghO9)61uQ|7W-n^ѣgoO>yj[Ͱ$:|09RD4"VL&KR YH7Ѡ{ SVU !:nrPhYH C*k)z^G^Ns2VbTE=vM!ŵdRlce*0l)!z4d+>^"|,uW-`D$'՞yZS d5u8ZU 64`5ħYn˘6Ѯ7j6,j'wX_ 3Ύ,I-k*H+ >67F!99xuõñvd>xv_m}Y2;]>ч=YVG׬%CF6 6v0,LvINJwsQԚ~fq,u []+ɻ^|0dGJvr )x?/R/"(,[1R,'̒%8h>S-2*y0aPH:ig U6a˲!gBv9,P0j% 3D^( ֐0- ,xKd:HL=m-mܾ~} k*M?kۂio@Vrg{/D nmaSsyM*Thf!2,gEʊ2 0B- BD/D *4y9hbaWk vʡ(}{ԝJ$C;zF8&tR@QW1dC$7b̭0$R=zG1diI_Lzko|ǣeJ8: pjyv1cHpŅk7X3b\tkn7?`b4u*AlF( ͚k}il )DB( Y 1X@ r &+τ`UT [m6 ]TUnGSIPG-L:cmXA1f,ԣ%DQv n1JT#x002,B㹞5儇!(U2k B %?lY P KM-eNyi+l0L(&g7O?O~Oq,}@=LW7Okͷn >O^%UnZvR];5WGǃ; c꿮~(v_("*s޿?H?̻ ,"6d%ֶdPҶv*h6Se1ݶTE2Tz̟3MNj꘩(Tc!'H;h꽲+C(c++YSYAո(L9d!JKshjS*K(7_wV18"Y7KnoO! d'Mhm:ƀr&;" CK;`XR %/b*^-Cw?h)GqJ)pЪ\')Tez\#"gtvJzlig-(PͦdPyjؑRxrj,շ TDj^W`=6 pxS5l+%ur]F;AJ?W,8;:ux]c bgU|B:<ʻsW[ҼQefe݄ ܓG:j`wj!} Hs:j/" β֔7eu+I[.k_7U|-6ݛBb!!w <e}% Ey2gpYiјͯOpF3)"0ҤH8Z2Ԓyhri-cppSER֞YsR @n|x]zY41 T$T?xiN:UR^NvZQEu2YyL*7{E@6͊=aw #ŜG7+[l#F BR!a "@`I[AM*K d+zK|F2x%^) ?k|`R$3)QsldNf{Uv:Dn>Ϛ y:t[(:G_kTb\\|݆U)#?.&d;r[8hZnm622\0Ъ.ff nJ~qnh&D*De)#{Ù"0Ċ{"F,"tcW=t9K921u¯h[*'[F'լGdpn:el] /Ӕ:$a h\Xׇ%,1FoV:ɻ\y5r*#HjVӢ*+(_tfѲ Afh0IA6'Je53)OړL`Q[-݃y|9# [rP9Gc6azao~o|u$"˞ ouW?TJަjdPZG&޵ `Ǧj<]ze9 uE̓ ^tn@LUQQB`0,ZDX 0si*?e^kBJ`&Q3RKJ=BJ&_1ڇՃҫVF4YJNSINF#hV9fU1;?X z,sz3;f ji¯IJMu.R$xgY&TS!1#.%O&5G!Ner]DvDJ2](+.~`D10jl8115roDa T֎|/:ߔ@HHJt̰Q35hIs y:Yfz!eS)6z~OޞNgsp_D('%T̿ %up4`xݝlV걕`,Y>] "{:}v~(MrlAVFI1ਉ\>rj :Y\wu^kBW[e XST-` /5< (TVOѹ~mˮ#1vCgc lh>O9s7nfYnu˧l2yqPC{/A67s]K~юSuXƃ29g\m,R܋/Rwq;SbJO&-0Hsae69-2*7mZ+ )c@ PUg5S1 T@C¥##m2EQc͕ɔLWP`.-vi?M! `e13Un7Uj*4gKzXA0stui5 &Hh?(4J37u.FLu7 71F|u̪GOy}( 51 .xaU-[oGAhP zaWyH y&=|&' vF^vN2)y&3s<?;_~QLjm05$ [q{äd"&^A'{+=lg܊M깨T:41iiW` Hjn`LU0M; _`ݛ'k{_sAd [N@Y%%M3ْ)[\a⥖uXt±익3Pdr8Ql|P<5LJPz"P+C̭9f v̆0HiahVqwbun1O3M)ܘ ﭮU䍷l mXv?Eg( nA\$ڍ1#> _td3̂HM*E$""ӴTrEBÎ04e(DSE%0e` 0G&zNl}"'!z+P%2VJG*뭭 ͡Қ]3 CͩGX&)VA[}縔EqTQk %k% YUhtc9LK#]zgõV/[;6i7,/d)q+ 5/rՍ͚~F *}lqTޖcl1`h_Ǝzd\NoD?cI-*6ôLѽ3b,[  ]gws$o•z&.bCc*^S'kL@ڲy/Jqpa_潇c#Px%24=\Ͳ{!lQUX\}^Zo7g+|?&K790 1Yj(2[6 d$"l>25n.Uح I9DnٸT4'"zcݚM@a^{@oU!Inm:)fioe~_?Wo1 C>Hk9\̲;+X~͘O&MȬmE gQ(ykz,v=1Wvn PCP E3˖ &ځʵX.&{GP)zlU%WDIJ6罣yk؝9V|gŽaoԣ4݈tN2FnYZsrZ} ׏[V},P2}V5S!F_pSe@pغ@8M2M>*la` dqUΪ9.5ƭҨE[6l%6k3ѦV#`(__mB$XWC _ı_s#: \Zؘ1ӍLQ TM8YgWtnPRN-8#z[SBBC׀itwrvn]RsCLOaDP*>d>RsJ|\L/]uutWW*|\x\sey\2+u^h8+S3XlY3u:w b^nK9Ae3!T>6]^5<7k]Um}*8s]¾8b̡.vlcX%G%{fϥSOG_[?xwH!#?-eo2_(($LM#`K,!|boBvsgTjYٕ aBq0*l*PaVG:- 'Llާ?}$ F[N1=<.8lim(l ?`V5dPzVniy 0.@Pȳjz*#P.ej ^, K,PsTFK9f q6XR&}fמl] žlHݩgc&J%Y5rP::50=,#6ݏ.Q -CKbFn$zփ3 <jiwؐ^GM`J7W%` 4Av"{#NI|z<;HJh_?޿g?#azn  f@ !{0)'@n ޔ)]/rbK/L (E89cՈ- U34(-e 甑)?>_oj?j^ڇ9WJieXS;j6؅Η:=!0odL)^L/jJ@0 #- 7 kAػaڐe3@(!B-؇3MnY'^@]u0ۖSy׋p~l][ZUh&{ۼXtz?=3D0SVlC(S,S+h(yH*f,ÎjؗBesF (K̋Xŝ#8ۺ6t`u.3QXfwUk%vҴJǾ87jed @q6av̠jWЖѻå eIu' }*?F\T2:ÈYKzj,fzfȖQ/1'XFvUP%A5bxȋZaցf%h5js:pi*j >f؇iW/6j,֧Qo]q)(skuW2k-'mǔ8]fDګ``Av?_MACD.L֧dsm[U{a߂jԜo7_~_|xӈ8 $pBM&Z%:2"G^M3=kF& |~m-fxĠmdn-酾_o}7߈`,HICB2ǠR}%/6jaW yKL%Bv Kx*ɴ I*qQB {Ԏ55& N(3c☩cM- A >YT0(vZ2AzYn!Ikx CW~hvA.@CaVYĺsbi˼yL3Pqv4c=K ;,ZNv= EL3V~aE-wQYT؜?-tݓ)I=-BkfYkIC<]}& i.]_fI`\_nm󂪺L)~K)gwojw9dkڣǀ0 ){ @1`r]YD.{팃Ynס[պ ڭ6 6В !gK ZmؿOnn_ۧoX>|{yjSc3K9$ r[gkmu[}a:0ufPd34 ܏RFD>rjeŎ_e|\CVlB`OYr4\ICju6L6-OP@ g/BF4 /䱾F[J E3U]ck`/3uhmPμ)&Pha]ِ1tVMi%glAW g:c雡bOtb]́򵁼x[Ӳ(U2]fъc[;p9"`ll|pB9;p˸%[g܁6o1پhh>Ȥi7[y%Y/ l/PX>HKYxg}rWmV=8[Ճ]&XqJsjt`fbL«LPѮ{aЧ-%}һW6 pB7%%oψ}oP#VRo[o'-mF~jFKtO@ iQG1.ot>!l2_]G|i&_:4=٦=mkgW;25WDs}dCk望;!|GOḷ8 nbBʭ&#% _X7d֊"d ʑJ!I5 @aY6/T?Ej}aQRrh,.Wݟ7ғtWt<2בֿW7-D2o =X]GV$uy[E7Vz.f6k@qȄnt( vl S3/٘I#bKm]jS~Zғ݁ W⨞- /7j-غC6[ؽѳ=<%0بBRv;*@/>:>4A\̀t`50#4kOx2;8 JK&xy0q9Ѿ!?C5xnÞ++-j),/s$=[+(ޮ(sil 6(Oyfcrl]F誗u3gpf@V" VҒddhOEˁځ>Ze^;ͣvpKvKPZ{Oܖ7+fg)]/}HAbͶ.㊑]]vp9jY *rGe"\}qy3ˣRF*?ɓo}?7i6APa"EdK3N 5l؛mjҵ#L-Lr`N>n~/K~9]? `l!HpͶb3sB 55ں¶1C'*upݥGӖ%ܺKͥM(n+u8fWҢSJ #m [ *5c:Xk̼)Ȱv6lѩNvv1|f1B痨4hve:^O2/&=\mbH 'ښ,P#[g8T&*gAkԣ:$Yav)⨩V!7BgZ)I3ێE=Il,lX3{691j <572:k4sƑc QK6WC_4Fwx&[+hr"-uPW1VWz,q K3Pj]&28hsɮΏM}XY=?C ;jNJπȣ\),9LwFE>Ѧ':Lƶͦ]kunkB)96s1f[4޹6n,{}NȳHxX .guE[qXtVL@Z 4RhK͠3X$s7BsnJ9BCC8k5hXSu9ʊ͉BK#Kjci ;!iYۇ^~;]l"DZyKݺ8Ůձp)cؚ&XLd3ב֖UZ[b?:0g)^=z铃'XO;xbAZ0}*즁- {}\]=ma5k ueė {2^.i n~mÊ[rQzZ,kq-ik;my* ;קnAiQ+#{Rj׀LQ{߸w^d#DB5MTb%ڴVZKx%؛*%To~>𙟻}m 1/*#y𸫦04MA&ZPOI͍y6E4^HZRf%fmug-%ޡ୓n- D7ep6zm+ -8~7Q}e[[FuN!\MѕV.fdCM6o}e͘X)hm( O͇\ /\R!>AyMh?'bMώd%:+!6= lfsH꣹yX?;:g/vpevYTsMXU]wu*U+_.3dm#G&+ّXG-6YH>K4`KrqOG]M~~}bPyxD"kc>vHn\Q&9Oӂ`4xC 8t8 yMP}qjx}|3%S}yO|ww72rb3`;JS6 b˂=~駞ُ>۷>n#1]ô~}9lG"!ϕhs׏j?. } ͑8pHnhf4џ6R]ϋصH3v<-PT\R֩ޏ ;XѨ3ylaG7XoP]RXR8msg࠳Ӡ Uֽgn3FY}2(xC̛|5 l1[ӻ i^o%suy+JL$V&&o *bljJ@v,O Yyêk-ZQٜ|rY/*]Em(u^P' AUVԣ<;Ҭ]=| y O%xW;ő(Eqv$Tf Mqț1Mɛgo퟾nB D)y yt})cu\2V`HY1?AO0Vj)d3cGl=<,h2lXn%v}UE~?gMϨdA'VHʅmIy1m {)\s \)d`̢&p,E9qJhXF<5XPV1Vc3; Xm.eP3K $2^EԩqNMXK֚k`+숞z7 .|Opغ&,p|$v\`Ngc'h#bTXnO!NFb آf9E~[fN1Cv[# 4-zuA-歕|p]cYڤK 5<УG d68MF֪1^¸[r#k?5ɾ|F2 ka`%u+h R=~gGw*0t#TܚnA-|a ;A*8M୷ۥȚ'J'Gtwcw{ ţoS\ru91gbjzhZ)= nx}>Q>>aҵǏ/ B{k4Nԣ#{#NK?uP96o)&\.G.xx(:}4gE5?=3wUVbiyx !<,`vuAyͪ4$fX>/Y_=Ǘ!#4lXCfZݔh+: ϜmEuH a]hA{plݪ._NWJ m11` N!<28^4wb ogwA`pc6(rjKk=_:kibOcǃ/p//z{s}/G_qHw`/F؊.W#.]Wr" Jӓ+f%rv̀+R#?y5fⶺ)_䀌D[2t=vlWgL[r @0xrT/sHG- ⶞;*W^cnxՐh㋜ެWpcGYX rp/ o#l)i\N)8'srs6+X_-T͢XWe͐ԺwO1r^GVCŎKrϵ=*ΘHNP=cw:JaڱF#|[řY-% {tJqPjc7e;pM扷Rqnx"e~TfN{GhN;41=e y Rpzu$=H'\8O!Ēk /^7QxTy |\ m%_E!;ܫNƏ _1F=4 0Ѭ /leZX)w:8FZCcn1X B:-6Bh#ll퉇S@'˳u/ Dǀ-܄]Bjj-)caL6C3)+Ucl9<ﯱܺovNVF￴]f1ңؑZQ%v\&b![͏n/\^Etj=ظ/Lt)VwItם쌱DZ 饀fT= H4KξReљ#M]zӴ}T<hgW.3Ԕ1x%|\hW6CM/\wWy<>vg#W]2z>:y bF3ky.wS0wV G~=J{(wdx}j~} >Dޗ< ψ#3?y;.B{(ef_Y%yݰxz+ܰR ݾ9f\\9"vlQ%Nas8gAKQ\LWyǃ-&˽u.Y%yg%hȱmQ2k权 \U(n, 'ueHG%zylpi33fl&ylcGVAFCr}xjpzxAz;.M= ڝ| >Cr}} Fc~Jc.n]E6̬-/.gA-y<Wӓ;qꅿ+t,QݝpBxtr# i{RïS9Fۧb"󑧾yj~E|/jCi\w 2c#qnC02"\t3%=,:tOx jغU9Ѭ>$aG^lU.Ѕh|hlu},eJ1u>Lbm!y2tS,xm<,+y^-&#m…}빌gӀ8Z'?{3$jd|k$CR~F̲qO)&x axܖt>R~Y/C@38J<elUFjs Iya̽ׯ>!q yJNx>4䩮=lj1n=qb{xz;"[{yUr숵%bh fn>1NzaalE'y<"PAh- 31گga|0<WM=T;tҼs;߆_zǎ81/vGD3xNyn<{q}|z<;}K[ :Açce؅\D#NsؑgmD\C:'w=U]0:Hn;YOWUrgDF`۴pz|}ǥj#>6㊬>x;x/di/;Fgypϫ'W!{3|~$.0\H7sJ$S-{?>v|s`;7cG\VQxz=6㳁jr}ϋ".i;rx/ k"K@$oY$N'/S<}}y )ȇ)3ǃWUw~hCcw<8 %q@}&/w/|\?΍ys] 2 %:!ue';S{S@~˥aV.OX`bWع,Yӓc9y!ݽ|?g fWZo*ǿ^' ~~&w_h{U!MD`޹_2Ь@SKyᓽN=l} Y|j<#_>/y\?\ȳ߬gִğ!KqN[-/$,^3wQqkEJ=;~b4}c\YL O@d^B\..qDˈ>>l}_x!Y5kZjグ{̈́ xG.s_##;r5>]M>.g_v;'c=8,^Ib*>/Xl/v(ɵKIkO{l/Ɣg^C><|XȟW'._A#3 ӝ+@y |bW잸llrj}oYpj}7orNxƎ'W;RlH.vwUe㱙EAσO;cv {6߄㑧H?ZG} ;.ڕV#>IwzS~W^>O|iUs>yy}{}{}{ ?Y`w_j!NJ)/cL_^>v4y1v\~|%7F=3tq! gMgf}_d^^^^#ȏin _}aI_ _c׷>\ٺ/;~GJ v documentation". #html_title = None # The name of an image file (within the static path) to place at the top of # the sidebar. #html_logo = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If true, the reST sources are included in the HTML build as _sources/. #html_copy_source = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'GrapeFruitdoc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [ ('index', 'GrapeFruit.tex', 'GrapeFruit Documentation', 'Xavier Basty ', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True Grapefruit-0.1a4/doc/index.rst000066400000000000000000000272461274767260200163450ustar00rootroot00000000000000.. _grapefruit-index: .. image:: _static/GrapeFruit.png Welcome! This is the documentation for GrapeFruit |release|, last updated |today|. See the :ref:`genindex` for a list of the topics. .. module:: grapefruit .. moduleauthor:: Xavier Basty ========================== The Color class ========================== .. class:: Color The grapefruit module contains only the :class:`Color` class, which exposes all the functionnalities. It can be used to store a color value and manipulate it, or convert it to another color system. If you are only interested in converting you colors from one system to another, you can store them using regular tuples instead of :class:`Color` instances. You can then use the class static methods to perform the conversions. :class:`Color` stores both the RGB and HSL representation of the color. This makes possible to keep the hue intact when the color is a pure white due to its lightness. However, certain operations work only with the RGB values, and might then lose the hue. All the operations assume that you provide values in the specified ranges, no checks are made whatsoever. If you provide a value outside of the specified ranges, you'll get some strange results... The class instances are immutable, all the methods return a new instance of the :class:`Color` class, and all the properties are read-only. .. note:: Some operations may provide results a bit outside the specified ranges, the results are not capped. This is due to certain color systems having a widers gamut than others. Class content --------------- - :ref:`class-constants` - :const:`Color.WHITE_REFERENCE` - :const:`Color.NAMED_COLOR` - :ref:`conversion-functions` - :meth:`Color.RgbToHsl` - :meth:`Color.HslToRgb` - :meth:`Color.RgbToHsv` - :meth:`Color.HsvToRgb` - :meth:`Color.RgbToYiq` - :meth:`Color.YiqToRgb` - :meth:`Color.RgbToYuv` - :meth:`Color.YuvToRgb` - :meth:`Color.RgbToXyz` - :meth:`Color.XyzToRgb` - :meth:`Color.XyzToLab` - :meth:`Color.LabToXyz` - :meth:`Color.CmykToCmy` - :meth:`Color.CmyToCmyk` - :meth:`Color.RgbToCmy` - :meth:`Color.CmyToRgb` - :meth:`Color.RgbToHtml` - :meth:`Color.HtmlToRgb` - :meth:`Color.RgbToPil` - :meth:`Color.PilToRgb` - :meth:`Color.RgbToWebSafe` - :meth:`Color.RgbToGreyscale` - :meth:`Color.RgbToRyb` - :meth:`Color.RybToRgb` - :ref:`instantiation-functions` - :meth:`Color.NewFromRgb` - :meth:`Color.NewFromHsl` - :meth:`Color.NewFromHsv` - :meth:`Color.NewFromYiq` - :meth:`Color.NewFromYuv` - :meth:`Color.NewFromXyz` - :meth:`Color.NewFromLab` - :meth:`Color.NewFromCmy` - :meth:`Color.NewFromCmyk` - :meth:`Color.NewFromHtml` - :meth:`Color.NewFromPil` - :ref:`properties` - :attr:`Color.alpha` - :attr:`Color.whiteRef` - :attr:`Color.rgb` - :attr:`Color.hue` - :attr:`Color.hsl` - :attr:`Color.hsv` - :attr:`Color.yiq` - :attr:`Color.yuv` - :attr:`Color.xyz` - :attr:`Color.lab` - :attr:`Color.cmy` - :attr:`Color.cmyk` - :attr:`Color.html` - :attr:`Color.pil` - :attr:`Color.webSafe` - :attr:`Color.greyscale` - :ref:`manipulation-methods` - :meth:`Color.ColorWithAlpha` - :meth:`Color.ColorWithWhiteRef` - :meth:`Color.ColorWithHue` - :meth:`Color.ColorWithSaturation` - :meth:`Color.ColorWithLightness` - :meth:`Color.DarkerColor` - :meth:`Color.LighterColor` - :meth:`Color.Saturate` - :meth:`Color.Desaturate` - :meth:`Color.WebSafeDither` - :ref:`generation-methods` - :meth:`Color.Gradient` - :meth:`Color.ComplementaryColor` - :meth:`Color.TriadicScheme` - :meth:`Color.TetradicScheme` - :meth:`Color.AnalogousScheme` - :ref:`blending-methods` - :meth:`Color.AlphaBlend` - :meth:`Color.Blend` Example usage --------------- To create an instance of the grapefruit.Color from RGB values: >>> import grapefruit >>> r, g, b = 1, 0.5, 0 >>> col = grapefruit.Color.NewFromRgb(r, g, b) To get the values of the color in another colorspace: >>> h, s, v = col.hsv >>> l, a, b = col.lab To get the complementary of a color: >>> compl = col.ComplementaryColor() >>> print compl.hsl (210.0, 1.0, 0.5) To directly convert RGB values to their HSL equivalent: >>> h, s, l = Color.RgbToHsl(r, g, b) .. _class-constants: Class Constants ----------------- .. data:: Color.WHITE_REFERENCE The reference white points of the CIE standards illuminants, calculated from the chromaticity coordinates found at: http://en.wikipedia.org/wiki/Standard_illuminant A dictionary mapping the name of the CIE standard illuminants to their reference white points. The white points are required for the XYZ <-> L*a*b conversions. The key names are build using the following pattern: ``_`` The possible values for ```` are: ====== =================================== Value Observer ====== =================================== std CIE 1931 2° Standard Observer sup CIE 1964 10° Supplementary Observer ====== =================================== The possible values for ```` are the name of the standard illuminants: ====== ======== ================================================== Value CCT Illuminant ====== ======== ================================================== A 2856 K Incandescent tungsten B 4874 K Direct sunlight at noon (obsolete) C 6774 K North sky daylight (obsolete) D50 5003 K ICC Profile PCS. Horizon light. D55 5503 K Compromise between incandescent and daylight D65 6504 K Noon daylight (TV & sRGB colorspace) D75 7504 K North sky day light E ~5455 K Equal energy radiator (not a black body) F1 6430 K Daylight Fluorescent F2 4230 K Cool White Fluorescent F3 3450 K White Fluorescent F4 2940 K Warm White Fluorescent F5 6350 K Daylight Fluorescent F6 4150 K Lite White Fluorescent F7 6500 K Broadband fluorescent, D65 simulator F8 5000 K Broadband fluorescent, D50 simulator F9 4150 K Broadband fluorescent, Cool White Deluxe F10 5000 K Narrowband fluorescent, Philips TL85, Ultralume 50 F11 4000 K Narrowband fluorescent, Philips TL84, Ultralume 40 F12 3000 K Narrowband fluorescent, Philips TL83, Ultralume 30 ====== ======== ================================================== .. data:: Color.NAMED_COLOR The names and RGB values of the X11 colors supported by popular browsers, with the gray/grey spelling issues, fixed so that both work (e.g light*grey* and light*gray*). Note: For *Gray*, *Green*, *Maroon* and *Purple*, the HTML/CSS values are used instead of the X11 ones (see `X11/CSS clashes `_) Reference: `CSS3 Color module `_ .. _conversion-functions: Conversion functions -------------------- The conversion functions are static methods of the :class:`Color` class that let you convert a color stored as the list of its components rather than as a :class:`Color` instance. .. automethod:: Color.RgbToHsl .. automethod:: Color.HslToRgb .. automethod:: Color.RgbToHsv .. automethod:: Color.HsvToRgb .. automethod:: Color.RgbToYiq .. automethod:: Color.YiqToRgb .. automethod:: Color.RgbToYuv .. automethod:: Color.YuvToRgb .. automethod:: Color.RgbToXyz .. automethod:: Color.XyzToRgb .. automethod:: Color.XyzToLab .. automethod:: Color.LabToXyz .. automethod:: Color.CmykToCmy .. automethod:: Color.CmyToCmyk .. automethod:: Color.RgbToCmy .. automethod:: Color.CmyToRgb .. automethod:: Color.RgbToHtml .. automethod:: Color.HtmlToRgb .. automethod:: Color.RgbToPil .. automethod:: Color.PilToRgb .. automethod:: Color.RgbToWebSafe .. automethod:: Color.RgbToGreyscale .. automethod:: Color.RgbToRyb .. automethod:: Color.RybToRgb .. _instantiation-functions: Instantiation functions ----------------------- The instantiation functions let you create a new instance of the :class:`Color` class from the color components using the color system of your choice. .. automethod:: Color.NewFromRgb .. automethod:: Color.NewFromHsl .. automethod:: Color.NewFromHsv .. automethod:: Color.NewFromYiq .. automethod:: Color.NewFromYuv .. automethod:: Color.NewFromXyz .. automethod:: Color.NewFromLab .. automethod:: Color.NewFromCmy .. automethod:: Color.NewFromCmyk .. automethod:: Color.NewFromHtml .. automethod:: Color.NewFromPil .. _properties: Properties ---------- The properties get the value of the instance in the specified color model. The properties returning calculated values unless marked otherwise. .. note:: All the properties are read-only. You need to make a copy of the instance to modify the color value. .. autoattribute:: Color.alpha *This value is not calculated, the stored value is returned directly.* .. autoattribute:: Color.whiteRef *This value is not calculated, the stored value is returned directly.* .. autoattribute:: Color.rgb *This value is not calculated, the stored value is returned directly.* .. autoattribute:: Color.hue *This value is not calculated, the stored value is returned directly.* .. autoattribute:: Color.hsl *This value is not calculated, the stored value is returned directly.* .. autoattribute:: Color.hsv .. autoattribute:: Color.yiq .. autoattribute:: Color.yuv .. autoattribute:: Color.xyz .. autoattribute:: Color.lab .. autoattribute:: Color.cmy .. autoattribute:: Color.cmyk .. autoattribute:: Color.html .. autoattribute:: Color.pil .. autoattribute:: Color.webSafe .. attribute:: Color.greyscale .. _manipulation-methods: Manipulation methods -------------------- The manipulations methods let you create a new color by changing an existing color properties. .. note:: The methods **do not** modify the current Color instance. They create a new instance or a tuple of new instances with the specified modifications. .. automethod:: Color.ColorWithAlpha .. automethod:: Color.ColorWithWhiteRef .. automethod:: Color.ColorWithHue .. automethod:: Color.ColorWithSaturation .. automethod:: Color.ColorWithLightness .. automethod:: Color.DarkerColor .. automethod:: Color.LighterColor .. automethod:: Color.Saturate .. automethod:: Color.Desaturate .. automethod:: Color.WebSafeDither .. _generation-methods: Generation methods ------------------ The generation methods let you create a color scheme by using a color as the start point. All the method, appart from Gradient and MonochromeScheme, have a 'mode' parameter that let you choose which color wheel should be used to generate the scheme. The following modes are available: :ryb: The `RYB `_ color wheel, or *artistic color wheel*. While scientifically incorrect, it generally produces better schemes than RGB. :rgb: The standard RGB color wheel. .. automethod:: Color.Gradient .. automethod:: Color.ComplementaryColor .. automethod:: Color.MonochromeScheme .. automethod:: Color.TriadicScheme .. automethod:: Color.TetradicScheme .. automethod:: Color.AnalogousScheme .. _blending-methods: Blending methods ---------------- .. automethod:: Color.AlphaBlend .. automethod:: Color.Blend Grapefruit-0.1a4/doc/makedoc.cmd000066400000000000000000000003141274767260200165570ustar00rootroot00000000000000@echo off set builder=%1 if [%builder%]==[] set builder=html set target=_build\%builder% if not exist %target%\* md %target% sphinx-build -b %builder% . _build\%builder%\ set target= set builder= Grapefruit-0.1a4/grapefruit.py000066400000000000000000001541301274767260200164520ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*-# # Copyright (c) 2008, Xavier Basty # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. '''GrapeFruit - Color manipulation in Python''' from __future__ import division import sys # $Id$ __author__ = 'Xavier Basty ' __version__ = '0.1a3' # The default white reference, use 2° Standard Observer, D65 (daylight) _DEFAULT_WREF = (0.95043, 1.00000, 1.08890) _oneThird = 1.0 / 3 _srgbGammaCorrInv = 0.03928 / 12.92 _sixteenHundredsixteenth = 16.0 / 116 _RybWheel = ( 0, 26, 52, 83, 120, 130, 141, 151, 162, 177, 190, 204, 218, 232, 246, 261, 275, 288, 303, 317, 330, 338, 345, 352, 360) _RgbWheel = ( 0, 8, 17, 26, 34, 41, 48, 54, 60, 81, 103, 123, 138, 155, 171, 187, 204, 219, 234, 251, 267, 282, 298, 329, 360) class Color: '''Hold a color value. Example usage: To create an instance of the grapefruit.Color from RGB values: >>> import grapefruit >>> r, g, b = 1, 0.5, 0 >>> col = grapefruit.Color.NewFromRgb(r, g, b) To get the values of the color in another colorspace: >>> h, s, v = col.hsv >>> l, a, b = col.lab To get the complementary of a color: >>> compl = col.ComplementaryColor(mode='rgb') >>> print(compl.hsl) (210.0, 1.0, 0.5) To directly convert RGB values to their HSL equivalent: >>> h, s, l = Color.RgbToHsl(r, g, b) ''' WHITE_REFERENCE = { 'std_A' : (1.09847, 1.00000, 0.35582), 'std_B' : (0.99093, 1.00000, 0.85313), 'std_C' : (0.98071, 1.00000, 1.18225), 'std_D50' : (0.96421, 1.00000, 0.82519), 'std_D55' : (0.95680, 1.00000, 0.92148), 'std_D65' : (0.95043, 1.00000, 1.08890), 'std_D75' : (0.94972, 1.00000, 1.22639), 'std_E' : (1.00000, 1.00000, 1.00000), 'std_F1' : (0.92834, 1.00000, 1.03665), 'std_F2' : (0.99145, 1.00000, 0.67316), 'std_F3' : (1.03753, 1.00000, 0.49861), 'std_F4' : (1.09147, 1.00000, 0.38813), 'std_F5' : (0.90872, 1.00000, 0.98723), 'std_F6' : (0.97309, 1.00000, 0.60191), 'std_F7' : (0.95017, 1.00000, 1.08630), 'std_F8' : (0.96413, 1.00000, 0.82333), 'std_F9' : (1.00365, 1.00000, 0.67868), 'std_F10' : (0.96174, 1.00000, 0.81712), 'std_F11' : (1.00899, 1.00000, 0.64262), 'std_F12' : (1.08046, 1.00000, 0.39228), 'sup_A' : (1.11142, 1.00000, 0.35200), 'sup_B' : (0.99178, 1.00000, 0.84349), 'sup_C' : (0.97286, 1.00000, 1.16145), 'sup_D50' : (0.96721, 1.00000, 0.81428), 'sup_D55' : (0.95797, 1.00000, 0.90925), 'sup_D65' : (0.94810, 1.00000, 1.07305), 'sup_D75' : (0.94417, 1.00000, 1.20643), 'sup_E' : (1.00000, 1.00000, 1.00000), 'sup_F1' : (0.94791, 1.00000, 1.03191), 'sup_F2' : (1.03245, 1.00000, 0.68990), 'sup_F3' : (1.08968, 1.00000, 0.51965), 'sup_F4' : (1.14961, 1.00000, 0.40963), 'sup_F5' : (0.93369, 1.00000, 0.98636), 'sup_F6' : (1.02148, 1.00000, 0.62074), 'sup_F7' : (0.95780, 1.00000, 1.07618), 'sup_F8' : (0.97115, 1.00000, 0.81135), 'sup_F9' : (1.02116, 1.00000, 0.67826), 'sup_F10' : (0.99001, 1.00000, 0.83134), 'sup_F11' : (1.03820, 1.00000, 0.65555), 'sup_F12' : (1.11428, 1.00000, 0.40353)} NAMED_COLOR = { 'aliceblue': '#f0f8ff', 'antiquewhite': '#faebd7', 'aqua': '#00ffff', 'aquamarine': '#7fffd4', 'azure': '#f0ffff', 'beige': '#f5f5dc', 'bisque': '#ffe4c4', 'black': '#000000', 'blanchedalmond': '#ffebcd', 'blue': '#0000ff', 'blueviolet': '#8a2be2', 'brown': '#a52a2a', 'burlywood': '#deb887', 'cadetblue': '#5f9ea0', 'chartreuse': '#7fff00', 'chocolate': '#d2691e', 'coral': '#ff7f50', 'cornflowerblue': '#6495ed', 'cornsilk': '#fff8dc', 'crimson': '#dc143c', 'cyan': '#00ffff', 'darkblue': '#00008b', 'darkcyan': '#008b8b', 'darkgoldenrod': '#b8860b', 'darkgray': '#a9a9a9', 'darkgrey': '#a9a9a9', 'darkgreen': '#006400', 'darkkhaki': '#bdb76b', 'darkmagenta': '#8b008b', 'darkolivegreen': '#556b2f', 'darkorange': '#ff8c00', 'darkorchid': '#9932cc', 'darkred': '#8b0000', 'darksalmon': '#e9967a', 'darkseagreen': '#8fbc8f', 'darkslateblue': '#483d8b', 'darkslategray': '#2f4f4f', 'darkslategrey': '#2f4f4f', 'darkturquoise': '#00ced1', 'darkviolet': '#9400d3', 'deeppink': '#ff1493', 'deepskyblue': '#00bfff', 'dimgray': '#696969', 'dimgrey': '#696969', 'dodgerblue': '#1e90ff', 'firebrick': '#b22222', 'floralwhite': '#fffaf0', 'forestgreen': '#228b22', 'fuchsia': '#ff00ff', 'gainsboro': '#dcdcdc', 'ghostwhite': '#f8f8ff', 'gold': '#ffd700', 'goldenrod': '#daa520', 'gray': '#808080', 'grey': '#808080', 'green': '#008000', 'greenyellow': '#adff2f', 'honeydew': '#f0fff0', 'hotpink': '#ff69b4', 'indianred': '#cd5c5c', 'indigo': '#4b0082', 'ivory': '#fffff0', 'khaki': '#f0e68c', 'lavender': '#e6e6fa', 'lavenderblush': '#fff0f5', 'lawngreen': '#7cfc00', 'lemonchiffon': '#fffacd', 'lightblue': '#add8e6', 'lightcoral': '#f08080', 'lightcyan': '#e0ffff', 'lightgoldenrodyellow': '#fafad2', 'lightgreen': '#90ee90', 'lightgray': '#d3d3d3', 'lightgrey': '#d3d3d3', 'lightpink': '#ffb6c1', 'lightsalmon': '#ffa07a', 'lightseagreen': '#20b2aa', 'lightskyblue': '#87cefa', 'lightslategray': '#778899', 'lightslategrey': '#778899', 'lightsteelblue': '#b0c4de', 'lightyellow': '#ffffe0', 'lime': '#00ff00', 'limegreen': '#32cd32', 'linen': '#faf0e6', 'magenta': '#ff00ff', 'maroon': '#800000', 'mediumaquamarine': '#66cdaa', 'mediumblue': '#0000cd', 'mediumorchid': '#ba55d3', 'mediumpurple': '#9370db', 'mediumseagreen': '#3cb371', 'mediumslateblue': '#7b68ee', 'mediumspringgreen': '#00fa9a', 'mediumturquoise': '#48d1cc', 'mediumvioletred': '#c71585', 'midnightblue': '#191970', 'mintcream': '#f5fffa', 'mistyrose': '#ffe4e1', 'moccasin': '#ffe4b5', 'navajowhite': '#ffdead', 'navy': '#000080', 'oldlace': '#fdf5e6', 'olive': '#808000', 'olivedrab': '#6b8e23', 'orange': '#ffa500', 'orangered': '#ff4500', 'orchid': '#da70d6', 'palegoldenrod': '#eee8aa', 'palegreen': '#98fb98', 'paleturquoise': '#afeeee', 'palevioletred': '#db7093', 'papayawhip': '#ffefd5', 'peachpuff': '#ffdab9', 'peru': '#cd853f', 'pink': '#ffc0cb', 'plum': '#dda0dd', 'powderblue': '#b0e0e6', 'purple': '#800080', 'red': '#ff0000', 'rosybrown': '#bc8f8f', 'royalblue': '#4169e1', 'saddlebrown': '#8b4513', 'salmon': '#fa8072', 'sandybrown': '#f4a460', 'seagreen': '#2e8b57', 'seashell': '#fff5ee', 'sienna': '#a0522d', 'silver': '#c0c0c0', 'skyblue': '#87ceeb', 'slateblue': '#6a5acd', 'slategray': '#708090', 'slategrey': '#708090', 'snow': '#fffafa', 'springgreen': '#00ff7f', 'steelblue': '#4682b4', 'tan': '#d2b48c', 'teal': '#008080', 'thistle': '#d8bfd8', 'tomato': '#ff6347', 'turquoise': '#40e0d0', 'violet': '#ee82ee', 'wheat': '#f5deb3', 'white': '#ffffff', 'whitesmoke': '#f5f5f5', 'yellow': '#ffff00', 'yellowgreen': '#9acd32'} def __init__(self, values, mode='rgb', alpha=1.0, wref=_DEFAULT_WREF): '''Instantiate a new grapefruit.Color object. Parameters: :values: The values of this color, in the specified representation. :mode: The representation mode used for values. :alpha: the alpha value (transparency) of this color. :wref: The whitepoint reference, default is 2° D65. ''' if not(isinstance(values, tuple)): raise TypeError('values must be a tuple') if mode=='rgb': self.__rgb = values self.__hsl = Color.RgbToHsl(*values) elif mode=='hsl': self.__hsl = values self.__rgb = Color.HslToRgb(*values) else: raise ValueError('Invalid color mode: ' + mode) self.__a = alpha self.__wref = wref def __ne__(self, other): return not self.__eq__(other) def __eq__(self, other): try: if isinstance(other, Color): return (self.__rgb==other.__rgb) and (self.__a==other.__a) if len(other) != 4: return False return list(self.__rgb + (self.__a,)) == list(other) except TypeError: return False except AttributeError: return False def __repr__(self): return str(self.__rgb + (self.__a,)) def __str__(self): '''A string representation of this grapefruit.Color instance. Returns: The RGBA representation of this grapefruit.Color instance. ''' return '(%g, %g, %g, %g)' % (self.__rgb + (self.__a,)) if sys.version_info[0] < 3: def __unicode__(self): '''A unicode string representation of this grapefruit.Color instance. Returns: The RGBA representation of this grapefruit.Color instance. ''' return unicode('%g, %g, %g, %g)') % (self.__rgb + (self.__a,)) def __iter__(self): return iter(self.__rgb + (self.__a,)) def __len__(self): return 4 def __GetIsLegal(self): return all(0.0 <= v <= 1.0 for v in self) isLegal = property(fget=__GetIsLegal, doc='Boolean indicating whether the color is within the legal gamut.') def __GetNearestLegal(self): def clamp(x, lo, hi): if x < lo: return lo elif x > hi: return hi else: return x return Color.NewFromRgb(*[clamp(v, 0.0, 1.0) for v in self]) nearestLegal = property(fget=__GetNearestLegal, doc='The nearest legal color.') @staticmethod def RgbToHsl(r, g, b): '''Convert the color from RGB coordinates to HSL. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: The color as an (h, s, l) tuple in the range: h[0...360], s[0...1], l[0...1] >>> Color.RgbToHsl(1, 0.5, 0) (30.0, 1.0, 0.5) ''' minVal = min(r, g, b) # min RGB value maxVal = max(r, g, b) # max RGB value l = (maxVal + minVal) / 2.0 if minVal==maxVal: return (0.0, 0.0, l) # achromatic (gray) d = maxVal - minVal # delta RGB value if l < 0.5: s = d / (maxVal + minVal) else: s = d / (2.0 - maxVal - minVal) dr, dg, db = [(maxVal-val) / d for val in (r, g, b)] if r==maxVal: h = db - dg elif g==maxVal: h = 2.0 + dr - db else: h = 4.0 + dg - dr h = (h*60.0) % 360.0 return (h, s, l) @staticmethod def _HueToRgb(n1, n2, h): h %= 6.0 if h < 1.0: return n1 + ((n2-n1) * h) if h < 3.0: return n2 if h < 4.0: return n1 + ((n2-n1) * (4.0 - h)) return n1 @staticmethod def HslToRgb(h, s, l): '''Convert the color from HSL coordinates to RGB. Parameters: :h: The Hue component value [0...1] :s: The Saturation component value [0...1] :l: The Lightness component value [0...1] Returns: The color as an (r, g, b) tuple in the range: r[0...1], g[0...1], b[0...1] >>> Color.HslToRgb(30.0, 1.0, 0.5) (1.0, 0.5, 0.0) ''' if s==0: return (l, l, l) # achromatic (gray) if l<0.5: n2 = l * (1.0 + s) else: n2 = l+s - (l*s) n1 = (2.0 * l) - n2 h /= 60.0 hueToRgb = Color._HueToRgb r = hueToRgb(n1, n2, h + 2) g = hueToRgb(n1, n2, h) b = hueToRgb(n1, n2, h - 2) return (r, g, b) @staticmethod def RgbToHsv(r, g, b): '''Convert the color from RGB coordinates to HSV. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: The color as an (h, s, v) tuple in the range: h[0...360], s[0...1], v[0...1] >>> Color.RgbToHsv(1, 0.5, 0) (30.0, 1.0, 1.0) ''' v = float(max(r, g, b)) d = v - min(r, g, b) if d==0: return (0.0, 0.0, v) s = d / v dr, dg, db = [(v - val) / d for val in (r, g, b)] if r==v: h = db - dg # between yellow & magenta elif g==v: h = 2.0 + dr - db # between cyan & yellow else: # b==v h = 4.0 + dg - dr # between magenta & cyan h = (h*60.0) % 360.0 return (h, s, v) @staticmethod def HsvToRgb(h, s, v): '''Convert the color from RGB coordinates to HSV. Parameters: :h: The Hus component value [0...1] :s: The Saturation component value [0...1] :v: The Value component [0...1] Returns: The color as an (r, g, b) tuple in the range: r[0...1], g[0...1], b[0...1] >>> Color.HslToRgb(30.0, 1.0, 0.5) (1.0, 0.5, 0.0) ''' if s==0: return (v, v, v) # achromatic (gray) h /= 60.0 h = h % 6.0 i = int(h) f = h - i if not(i&1): f = 1-f # if i is even m = v * (1.0 - s) n = v * (1.0 - (s * f)) if i==0: return (v, n, m) if i==1: return (n, v, m) if i==2: return (m, v, n) if i==3: return (m, n, v) if i==4: return (n, m, v) return (v, m, n) @staticmethod def RgbToYiq(r, g, b): '''Convert the color from RGB to YIQ. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: The color as an (y, i, q) tuple in the range: y[0...1], i[0...1], q[0...1] >>> '(%g, %g, %g)' % Color.RgbToYiq(1, 0.5, 0) '(0.592263, 0.458874, -0.0499818)' ''' y = (r * 0.29895808) + (g * 0.58660979) + (b *0.11443213) i = (r * 0.59590296) - (g * 0.27405705) - (b *0.32184591) q = (r * 0.21133576) - (g * 0.52263517) + (b *0.31129940) return (y, i, q) @staticmethod def YiqToRgb(y, i, q): '''Convert the color from YIQ coordinates to RGB. Parameters: :y: Tte Y component value [0...1] :i: The I component value [0...1] :q: The Q component value [0...1] Returns: The color as an (r, g, b) tuple in the range: r[0...1], g[0...1], b[0...1] >>> '(%g, %g, %g)' % Color.YiqToRgb(0.592263, 0.458874, -0.0499818) '(1, 0.5, 5.442e-07)' ''' r = y + (i * 0.9562) + (q * 0.6210) g = y - (i * 0.2717) - (q * 0.6485) b = y - (i * 1.1053) + (q * 1.7020) return (r, g, b) @staticmethod def RgbToYuv(r, g, b): '''Convert the color from RGB coordinates to YUV. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: The color as an (y, u, v) tuple in the range: y[0...1], u[-0.436...0.436], v[-0.615...0.615] >>> '(%g, %g, %g)' % Color.RgbToYuv(1, 0.5, 0) '(0.5925, -0.29156, 0.357505)' ''' y = (r * 0.29900) + (g * 0.58700) + (b * 0.11400) u = -(r * 0.14713) - (g * 0.28886) + (b * 0.43600) v = (r * 0.61500) - (g * 0.51499) - (b * 0.10001) return (y, u, v) @staticmethod def YuvToRgb(y, u, v): '''Convert the color from YUV coordinates to RGB. Parameters: :y: The Y component value [0...1] :u: The U component value [-0.436...0.436] :v: The V component value [-0.615...0.615] Returns: The color as an (r, g, b) tuple in the range: r[0...1], g[0...1], b[0...1] >>> '(%g, %g, %g)' % Color.YuvToRgb(0.5925, -0.2916, 0.3575) '(0.999989, 0.500015, -6.3276e-05)' ''' r = y + (v * 1.13983) g = y - (u * 0.39465) - (v * 0.58060) b = y + (u * 2.03211) return (r, g, b) @staticmethod def RgbToXyz(r, g, b): '''Convert the color from sRGB to CIE XYZ. The methods assumes that the RGB coordinates are given in the sRGB colorspace (D65). .. note:: Compensation for the sRGB gamma correction is applied before converting. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: The color as an (x, y, z) tuple in the range: x[0...1], y[0...1], z[0...1] >>> '(%g, %g, %g)' % Color.RgbToXyz(1, 0.5, 0) '(0.488941, 0.365682, 0.0448137)' ''' r, g, b = [((v <= 0.03928) and [v / 12.92] or [((v+0.055) / 1.055) **2.4])[0] for v in (r, g, b)] x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805) y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722) z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505) return (x, y, z) @staticmethod def XyzToRgb(x, y, z): '''Convert the color from CIE XYZ coordinates to sRGB. .. note:: Compensation for sRGB gamma correction is applied before converting. Parameters: :x: The X component value [0...1] :y: The Y component value [0...1] :z: The Z component value [0...1] Returns: The color as an (r, g, b) tuple in the range: r[0...1], g[0...1], b[0...1] >>> '(%g, %g, %g)' % Color.XyzToRgb(0.488941, 0.365682, 0.0448137) '(1, 0.5, 6.81883e-08)' ''' r = (x * 3.2406255) - (y * 1.5372080) - (z * 0.4986286) g = -(x * 0.9689307) + (y * 1.8757561) + (z * 0.0415175) b = (x * 0.0557101) - (y * 0.2040211) + (z * 1.0569959) return tuple((((v <= _srgbGammaCorrInv) and [v * 12.92] or [(1.055 * (v ** (1/2.4))) - 0.055])[0] for v in (r, g, b))) @staticmethod def XyzToLab(x, y, z, wref=_DEFAULT_WREF): '''Convert the color from CIE XYZ to CIE L*a*b*. Parameters: :x: The X component value [0...1] :y: The Y component value [0...1] :z: The Z component value [0...1] :wref: The whitepoint reference, default is 2° D65. Returns: The color as an (L, a, b) tuple in the range: L[0...100], a[-1...1], b[-1...1] >>> '(%g, %g, %g)' % Color.XyzToLab(0.488941, 0.365682, 0.0448137) '(66.9518, 0.43084, 0.739692)' >>> '(%g, %g, %g)' % Color.XyzToLab(0.488941, 0.365682, 0.0448137, Color.WHITE_REFERENCE['std_D50']) '(66.9518, 0.411663, 0.67282)' ''' # White point correction x /= wref[0] y /= wref[1] z /= wref[2] # Nonlinear distortion and linear transformation x, y, z = [((v > 0.008856) and [v**_oneThird] or [(7.787 * v) + _sixteenHundredsixteenth])[0] for v in (x, y, z)] # Vector scaling l = (116 * y) - 16 a = 5.0 * (x - y) b = 2.0 * (y - z) return (l, a, b) @staticmethod def LabToXyz(l, a, b, wref=_DEFAULT_WREF): '''Convert the color from CIE L*a*b* to CIE 1931 XYZ. Parameters: :l: The L component [0...100] :a: The a component [-1...1] :b: The a component [-1...1] :wref: The whitepoint reference, default is 2° D65. Returns: The color as an (x, y, z) tuple in the range: x[0...q], y[0...1], z[0...1] >>> '(%g, %g, %g)' % Color.LabToXyz(66.9518, 0.43084, 0.739692) '(0.488941, 0.365682, 0.0448137)' >>> '(%g, %g, %g)' % Color.LabToXyz(66.9518, 0.411663, 0.67282, Color.WHITE_REFERENCE['std_D50']) '(0.488941, 0.365682, 0.0448138)' ''' y = (l + 16) / 116 x = (a / 5.0) + y z = y - (b / 2.0) return tuple((((v > 0.206893) and [v**3] or [(v - _sixteenHundredsixteenth) / 7.787])[0] * w for v, w in zip((x, y, z), wref))) @staticmethod def CmykToCmy(c, m, y, k): '''Convert the color from CMYK coordinates to CMY. Parameters: :c: The Cyan component value [0...1] :m: The Magenta component value [0...1] :y: The Yellow component value [0...1] :k: The Black component value [0...1] Returns: The color as an (c, m, y) tuple in the range: c[0...1], m[0...1], y[0...1] >>> '(%g, %g, %g)' % Color.CmykToCmy(1, 0.32, 0, 0.5) '(1, 0.66, 0.5)' ''' mk = 1-k return ((c*mk + k), (m*mk + k), (y*mk + k)) @staticmethod def CmyToCmyk(c, m, y): '''Convert the color from CMY coordinates to CMYK. Parameters: :c: The Cyan component value [0...1] :m: The Magenta component value [0...1] :y: The Yellow component value [0...1] Returns: The color as an (c, m, y, k) tuple in the range: c[0...1], m[0...1], y[0...1], k[0...1] >>> '(%g, %g, %g, %g)' % Color.CmyToCmyk(1, 0.66, 0.5) '(1, 0.32, 0, 0.5)' ''' k = min(c, m, y) if k==1.0: return (0.0, 0.0, 0.0, 1.0) mk = 1-k return ((c-k) / mk, (m-k) / mk, (y-k) / mk, k) @staticmethod def RgbToCmy(r, g, b): '''Convert the color from RGB coordinates to CMY. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: The color as an (c, m, y) tuple in the range: c[0...1], m[0...1], y[0...1] >>> Color.RgbToCmy(1, 0.5, 0) (0, 0.5, 1) ''' return (1-r, 1-g, 1-b) @staticmethod def CmyToRgb(c, m, y): '''Convert the color from CMY coordinates to RGB. Parameters: :c: The Cyan component value [0...1] :m: The Magenta component value [0...1] :y: The Yellow component value [0...1] Returns: The color as an (r, g, b) tuple in the range: r[0...1], g[0...1], b[0...1] >>> Color.CmyToRgb(0, 0.5, 1) (1, 0.5, 0) ''' return (1-c, 1-m, 1-y) @staticmethod def RgbToIntTuple(r, g, b): '''Convert the color from (r, g, b) to an int tuple. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: The color as an (r, g, b) tuple in the range: r[0...255], g[0...2551], b[0...2551] >>> Color.RgbToIntTuple(1, 0.5, 0) (255, 128, 0) ''' return tuple(int(round(v*255)) for v in (r, g, b)) @staticmethod def IntTupleToRgb(intTuple): '''Convert a tuple of ints to (r, g, b). Parameters: The color as an (r, g, b) integer tuple in the range: r[0...255], g[0...255], b[0...255] Returns: The color as an (r, g, b) tuple in the range: r[0...1], g[0...1], b[0...1] >>> '(%g, %g, %g)' % Color.IntTupleToRgb((255, 128, 0)) '(1, 0.501961, 0)' ''' return tuple(v / 255 for v in intTuple) @staticmethod def RgbToHtml(r, g, b): '''Convert the color from (r, g, b) to #RRGGBB. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: A CSS string representation of this color (#RRGGBB). >>> Color.RgbToHtml(1, 0.5, 0) '#ff8000' ''' return '#%02x%02x%02x' % tuple((min(round(v*255), 255) for v in (r, g, b))) @staticmethod def HtmlToRgb(html): '''Convert the HTML color to (r, g, b). Parameters: :html: the HTML definition of the color (#RRGGBB or #RGB or a color name). Returns: The color as an (r, g, b) tuple in the range: r[0...1], g[0...1], b[0...1] Throws: :ValueError: If html is neither a known color name or a hexadecimal RGB representation. >>> '(%g, %g, %g)' % Color.HtmlToRgb('#ff8000') '(1, 0.501961, 0)' >>> '(%g, %g, %g)' % Color.HtmlToRgb('ff8000') '(1, 0.501961, 0)' >>> '(%g, %g, %g)' % Color.HtmlToRgb('#f60') '(1, 0.4, 0)' >>> '(%g, %g, %g)' % Color.HtmlToRgb('f60') '(1, 0.4, 0)' >>> '(%g, %g, %g)' % Color.HtmlToRgb('lemonchiffon') '(1, 0.980392, 0.803922)' ''' html = html.strip().lower() if html[0]=='#': html = html[1:] elif html in Color.NAMED_COLOR: html = Color.NAMED_COLOR[html][1:] if len(html)==6: rgb = html[:2], html[2:4], html[4:] elif len(html)==3: rgb = ['%c%c' % (v,v) for v in html] else: raise ValueError('input #%s is not in #RRGGBB format' % html) return tuple(((int(n, 16) / 255.0) for n in rgb)) @staticmethod def RgbToPil(r, g, b): '''Convert the color from RGB to a PIL-compatible integer. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: A PIL compatible integer (0xBBGGRR). >>> '0x%06x' % Color.RgbToPil(1, 0.5, 0) '0x0080ff' ''' r, g, b = [min(int(round(v*255)), 255) for v in (r, g, b)] return (b << 16) + (g << 8) + r @staticmethod def PilToRgb(pil): '''Convert the color from a PIL-compatible integer to RGB. Parameters: pil: a PIL compatible color representation (0xBBGGRR) Returns: The color as an (r, g, b) tuple in the range: the range: r: [0...1] g: [0...1] b: [0...1] >>> '(%g, %g, %g)' % Color.PilToRgb(0x0080ff) '(1, 0.501961, 0)' ''' r = 0xff & pil g = 0xff & (pil >> 8) b = 0xff & (pil >> 16) return tuple((v / 255.0 for v in (r, g, b))) @staticmethod def _WebSafeComponent(c, alt=False): '''Convert a color component to its web safe equivalent. Parameters: :c: The component value [0...1] :alt: If True, return the alternative value instead of the nearest one. Returns: The web safe equivalent of the component value. ''' # This sucks, but floating point between 0 and 1 is quite fuzzy... # So we just change the scale a while to make the equality tests # work, otherwise it gets wrong at some decimal far to the right. sc = c * 100.0 # If the color is already safe, return it straight away d = sc % 20 if d==0: return c # Get the lower and upper safe values l = sc - d u = l + 20 # Return the 'closest' value according to the alt flag if alt: if (sc-l) >= (u-sc): return l/100.0 else: return u/100.0 else: if (sc-l) >= (u-sc): return u/100.0 else: return l/100.0 @staticmethod def RgbToWebSafe(r, g, b, alt=False): '''Convert the color from RGB to 'web safe' RGB Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] :alt: If True, use the alternative color instead of the nearest one. Can be used for dithering. Returns: The color as an (r, g, b) tuple in the range: the range: r[0...1], g[0...1], b[0...1] >>> '(%g, %g, %g)' % Color.RgbToWebSafe(1, 0.55, 0.0) '(1, 0.6, 0)' ''' webSafeComponent = Color._WebSafeComponent return tuple((webSafeComponent(v, alt) for v in (r, g, b))) @staticmethod def RgbToGreyscale(r, g, b): '''Convert the color from RGB to its greyscale equivalent Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: The color as an (r, g, b) tuple in the range: the range: r[0...1], g[0...1], b[0...1] >>> '(%g, %g, %g)' % Color.RgbToGreyscale(1, 0.8, 0) '(0.6, 0.6, 0.6)' ''' v = (r + g + b) / 3.0 return (v, v, v) @staticmethod def RgbToRyb(hue): '''Maps a hue on the RGB color wheel to Itten's RYB wheel. Parameters: :hue: The hue on the RGB color wheel [0...360] Returns: An approximation of the corresponding hue on Itten's RYB wheel. >>> Color.RgbToRyb(15) 26.0 ''' d = hue % 15 i = int(hue / 15) x0 = _RybWheel[i] x1 = _RybWheel[i+1] return x0 + (x1-x0) * d / 15 @staticmethod def RybToRgb(hue): '''Maps a hue on Itten's RYB color wheel to the standard RGB wheel. Parameters: :hue: The hue on Itten's RYB color wheel [0...360] Returns: An approximation of the corresponding hue on the standard RGB wheel. >>> Color.RybToRgb(15) 8.0 ''' d = hue % 15 i = int(hue / 15) x0 = _RgbWheel[i] x1 = _RgbWheel[i+1] return x0 + (x1-x0) * d / 15 @staticmethod def NewFromRgb(r, g, b, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed RGB values. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> Color.NewFromRgb(1.0, 0.5, 0.0) (1.0, 0.5, 0.0, 1.0) >>> Color.NewFromRgb(1.0, 0.5, 0.0, 0.5) (1.0, 0.5, 0.0, 0.5) ''' return Color((r, g, b), 'rgb', alpha, wref) @staticmethod def NewFromHsl(h, s, l, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed HSL values. Parameters: :h: The Hue component value [0...1] :s: The Saturation component value [0...1] :l: The Lightness component value [0...1] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 1, 0.5) (1.0, 0.5, 0.0, 1.0) >>> Color.NewFromHsl(30, 1, 0.5, 0.5) (1.0, 0.5, 0.0, 0.5) ''' return Color((h, s, l), 'hsl', alpha, wref) @staticmethod def NewFromHsv(h, s, v, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed HSV values. Parameters: :h: The Hus component value [0...1] :s: The Saturation component value [0...1] :v: The Value component [0...1] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> Color.NewFromHsv(30, 1, 1) (1.0, 0.5, 0.0, 1.0) >>> Color.NewFromHsv(30, 1, 1, 0.5) (1.0, 0.5, 0.0, 0.5) ''' h2, s, l = Color.RgbToHsl(*Color.HsvToRgb(h, s, v)) return Color((h, s, l), 'hsl', alpha, wref) @staticmethod def NewFromYiq(y, i, q, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed YIQ values. Parameters: :y: The Y component value [0...1] :i: The I component value [0...1] :q: The Q component value [0...1] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> str(Color.NewFromYiq(0.5922, 0.45885,-0.05)) '(0.999902, 0.499955, -6.6905e-05, 1)' >>> str(Color.NewFromYiq(0.5922, 0.45885,-0.05, 0.5)) '(0.999902, 0.499955, -6.6905e-05, 0.5)' ''' return Color(Color.YiqToRgb(y, i, q), 'rgb', alpha, wref) @staticmethod def NewFromYuv(y, u, v, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed YUV values. Parameters: :y: The Y component value [0...1] :u: The U component value [-0.436...0.436] :v: The V component value [-0.615...0.615] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> str(Color.NewFromYuv(0.5925, -0.2916, 0.3575)) '(0.999989, 0.500015, -6.3276e-05, 1)' >>> str(Color.NewFromYuv(0.5925, -0.2916, 0.3575, 0.5)) '(0.999989, 0.500015, -6.3276e-05, 0.5)' ''' return Color(Color.YuvToRgb(y, u, v), 'rgb', alpha, wref) @staticmethod def NewFromXyz(x, y, z, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed CIE-XYZ values. Parameters: :x: The Red component value [0...1] :y: The Green component value [0...1] :z: The Blue component value [0...1] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> str(Color.NewFromXyz(0.488941, 0.365682, 0.0448137)) '(1, 0.5, 6.81883e-08, 1)' >>> str(Color.NewFromXyz(0.488941, 0.365682, 0.0448137, 0.5)) '(1, 0.5, 6.81883e-08, 0.5)' ''' return Color(Color.XyzToRgb(x, y, z), 'rgb', alpha, wref) @staticmethod def NewFromLab(l, a, b, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed CIE-LAB values. Parameters: :l: The L component [0...100] :a: The a component [-1...1] :b: The a component [-1...1] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692)) '(1, 0.5, 1.09491e-08, 1)' >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, wref=Color.WHITE_REFERENCE['std_D50'])) '(1.01238, 0.492011, -0.14311, 1)' >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5)) '(1, 0.5, 1.09491e-08, 0.5)' >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5, Color.WHITE_REFERENCE['std_D50'])) '(1.01238, 0.492011, -0.14311, 0.5)' ''' return Color(Color.XyzToRgb(*Color.LabToXyz(l, a, b, wref)), 'rgb', alpha, wref) @staticmethod def NewFromCmy(c, m, y, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed CMY values. Parameters: :c: The Cyan component value [0...1] :m: The Magenta component value [0...1] :y: The Yellow component value [0...1] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> Color.NewFromCmy(0, 0.5, 1) (1, 0.5, 0, 1.0) >>> Color.NewFromCmy(0, 0.5, 1, 0.5) (1, 0.5, 0, 0.5) ''' return Color(Color.CmyToRgb(c, m, y), 'rgb', alpha, wref) @staticmethod def NewFromCmyk(c, m, y, k, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed CMYK values. Parameters: :c: The Cyan component value [0...1] :m: The Magenta component value [0...1] :y: The Yellow component value [0...1] :k: The Black component value [0...1] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> str(Color.NewFromCmyk(1, 0.32, 0, 0.5)) '(0, 0.34, 0.5, 1)' >>> str(Color.NewFromCmyk(1, 0.32, 0, 0.5, 0.5)) '(0, 0.34, 0.5, 0.5)' ''' return Color(Color.CmyToRgb(*Color.CmykToCmy(c, m, y, k)), 'rgb', alpha, wref) @staticmethod def NewFromHtml(html, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed HTML color definition. Parameters: :html: The HTML definition of the color (#RRGGBB or #RGB or a color name). :alpha: The color transparency [0...1], default is opaque. :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> str(Color.NewFromHtml('#ff8000')) '(1, 0.501961, 0, 1)' >>> str(Color.NewFromHtml('ff8000')) '(1, 0.501961, 0, 1)' >>> str(Color.NewFromHtml('#f60')) '(1, 0.4, 0, 1)' >>> str(Color.NewFromHtml('f60')) '(1, 0.4, 0, 1)' >>> str(Color.NewFromHtml('lemonchiffon')) '(1, 0.980392, 0.803922, 1)' >>> str(Color.NewFromHtml('#ff8000', 0.5)) '(1, 0.501961, 0, 0.5)' ''' return Color(Color.HtmlToRgb(html), 'rgb', alpha, wref) @staticmethod def NewFromPil(pil, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed PIL color. Parameters: :pil: A PIL compatible color representation (0xBBGGRR) :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> str(Color.NewFromPil(0x0080ff)) '(1, 0.501961, 0, 1)' >>> str(Color.NewFromPil(0x0080ff, 0.5)) '(1, 0.501961, 0, 0.5)' ''' return Color(Color.PilToRgb(pil), 'rgb', alpha, wref) def __GetAlpha(self): return self.__a alpha = property(fget=__GetAlpha, doc='The transparency of this color. 0.0 is transparent and 1.0 is fully opaque.') def __GetWRef(self): return self.__wref whiteRef = property(fget=__GetWRef, doc='the white reference point of this color.') def __GetRGB(self): return self.__rgb rgb = property(fget=__GetRGB, doc='The RGB values of this Color.') def __GetHue(self): return self.__hsl[0] hue = property(fget=__GetHue, doc='The hue of this color.') def __GetHSL(self): return self.__hsl hsl = property(fget=__GetHSL, doc='The HSL values of this Color.') def __GetHSV(self): h, s, v = Color.RgbToHsv(*self.__rgb) return (self.__hsl[0], s, v) hsv = property(fget=__GetHSV, doc='The HSV values of this Color.') def __GetYIQ(self): return Color.RgbToYiq(*self.__rgb) yiq = property(fget=__GetYIQ, doc='The YIQ values of this Color.') def __GetYUV(self): return Color.RgbToYuv(*self.__rgb) yuv = property(fget=__GetYUV, doc='The YUV values of this Color.') def __GetXYZ(self): return Color.RgbToXyz(*self.__rgb) xyz = property(fget=__GetXYZ, doc='The CIE-XYZ values of this Color.') def __GetLAB(self): return Color.XyzToLab(wref=self.__wref, *Color.RgbToXyz(*self.__rgb)) lab = property(fget=__GetLAB, doc='The CIE-LAB values of this Color.') def __GetCMY(self): return Color.RgbToCmy(*self.__rgb) cmy = property(fget=__GetCMY, doc='The CMY values of this Color.') def __GetCMYK(self): return Color.CmyToCmyk(*Color.RgbToCmy(*self.__rgb)) cmyk = property(fget=__GetCMYK, doc='The CMYK values of this Color.') def __GetIntTuple(self): return Color.RgbToIntTuple(*self.__rgb) intTuple = property(fget=__GetIntTuple, doc='This Color as a tuple of integers in the range [0...255]') def __GetHTML(self): return Color.RgbToHtml(*self.__rgb) html = property(fget=__GetHTML, doc='This Color as an HTML color definition.') def __GetPIL(self): return Color.RgbToPil(*self.__rgb) pil = property(fget=__GetPIL, doc='This Color as a PIL compatible value.') def __GetwebSafe(self): return Color.RgbToWebSafe(*self.__rgb) webSafe = property(fget=__GetwebSafe, doc='The web safe color nearest to this one (RGB).') def __GetGreyscale(self): return Color.RgbToGreyscale(*self.rgb) greyscale = property(fget=__GetGreyscale, doc='The greyscale equivalent to this color (RGB).') def ColorWithAlpha(self, alpha): '''Create a new instance based on this one with a new alpha value. Parameters: :alpha: The transparency of the new color [0...1]. Returns: A grapefruit.Color instance. >>> Color.NewFromRgb(1.0, 0.5, 0.0, 1.0).ColorWithAlpha(0.5) (1.0, 0.5, 0.0, 0.5) ''' return Color(self.__rgb, 'rgb', alpha, self.__wref) def ColorWithWhiteRef(self, wref, labAsRef=False): '''Create a new instance based on this one with a new white reference. Parameters: :wref: The whitepoint reference. :labAsRef: If True, the L*a*b* values of the current instance are used as reference for the new color; otherwise, the RGB values are used as reference. Returns: A grapefruit.Color instance. >>> c = Color.NewFromRgb(1.0, 0.5, 0.0, 1.0, Color.WHITE_REFERENCE['std_D65']) >>> c2 = c.ColorWithWhiteRef(Color.WHITE_REFERENCE['sup_D50']) >>> c2.rgb (1.0, 0.5, 0.0) >>> '(%g, %g, %g)' % c2.whiteRef '(0.96721, 1, 0.81428)' >>> c2 = c.ColorWithWhiteRef(Color.WHITE_REFERENCE['sup_D50'], labAsRef=True) >>> '(%g, %g, %g)' % c2.rgb '(1.01463, 0.490339, -0.148131)' >>> '(%g, %g, %g)' % c2.whiteRef '(0.96721, 1, 0.81428)' >>> '(%g, %g, %g)' % c.lab '(66.9518, 0.43084, 0.739692)' >>> '(%g, %g, %g)' % c2.lab '(66.9518, 0.43084, 0.739693)' ''' if labAsRef: l, a, b = self.__GetLAB() return Color.NewFromLab(l, a, b, self.__a, wref) else: return Color(self.__rgb, 'rgb', self.__a, wref) def ColorWithHue(self, hue): '''Create a new instance based on this one with a new hue. Parameters: :hue: The hue of the new color [0...360]. Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 1, 0.5).ColorWithHue(60) (1.0, 1.0, 0.0, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).ColorWithHue(60).hsl (60, 1, 0.5) ''' h, s, l = self.__hsl return Color((hue, s, l), 'hsl', self.__a, self.__wref) def ColorWithSaturation(self, saturation): '''Create a new instance based on this one with a new saturation value. .. note:: The saturation is defined for the HSL mode. Parameters: :saturation: The saturation of the new color [0...1]. Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 1, 0.5).ColorWithSaturation(0.5) (0.75, 0.5, 0.25, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).ColorWithSaturation(0.5).hsl (30, 0.5, 0.5) ''' h, s, l = self.__hsl return Color((h, saturation, l), 'hsl', self.__a, self.__wref) def ColorWithLightness(self, lightness): '''Create a new instance based on this one with a new lightness value. Parameters: :lightness: The lightness of the new color [0...1]. Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 1, 0.5).ColorWithLightness(0.25) (0.5, 0.25, 0.0, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).ColorWithLightness(0.25).hsl (30, 1, 0.25) ''' h, s, l = self.__hsl return Color((h, s, lightness), 'hsl', self.__a, self.__wref) def DarkerColor(self, level): '''Create a new instance based on this one but darker. Parameters: :level: The amount by which the color should be darkened to produce the new one [0...1]. Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 1, 0.5).DarkerColor(0.25) (0.5, 0.25, 0.0, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).DarkerColor(0.25).hsl (30, 1, 0.25) ''' h, s, l = self.__hsl return Color((h, s, max(l - level, 0)), 'hsl', self.__a, self.__wref) def LighterColor(self, level): '''Create a new instance based on this one but lighter. Parameters: :level: The amount by which the color should be lightened to produce the new one [0...1]. Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 1, 0.5).LighterColor(0.25) (1.0, 0.75, 0.5, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).LighterColor(0.25).hsl (30, 1, 0.75) ''' h, s, l = self.__hsl return Color((h, s, min(l + level, 1)), 'hsl', self.__a, self.__wref) def Saturate(self, level): '''Create a new instance based on this one but more saturated. Parameters: :level: The amount by which the color should be saturated to produce the new one [0...1]. Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 0.5, 0.5).Saturate(0.25) (0.875, 0.5, 0.125, 1.0) >>> Color.NewFromHsl(30, 0.5, 0.5).Saturate(0.25).hsl (30, 0.75, 0.5) ''' h, s, l = self.__hsl return Color((h, min(s + level, 1), l), 'hsl', self.__a, self.__wref) def Desaturate(self, level): '''Create a new instance based on this one but less saturated. Parameters: :level: The amount by which the color should be desaturated to produce the new one [0...1]. Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 0.5, 0.5).Desaturate(0.25) (0.625, 0.5, 0.375, 1.0) >>> Color.NewFromHsl(30, 0.5, 0.5).Desaturate(0.25).hsl (30, 0.25, 0.5) ''' h, s, l = self.__hsl return Color((h, max(s - level, 0), l), 'hsl', self.__a, self.__wref) def WebSafeDither(self): '''Return the two websafe colors nearest to this one. Returns: A tuple of two grapefruit.Color instances which are the two web safe colors closest this one. >>> c = Color.NewFromRgb(1.0, 0.45, 0.0) >>> c1, c2 = c.WebSafeDither() >>> str(c1) '(1, 0.4, 0, 1)' >>> str(c2) '(1, 0.6, 0, 1)' ''' return ( Color(Color.RgbToWebSafe(*self.__rgb), 'rgb', self.__a, self.__wref), Color(Color.RgbToWebSafe(alt=True, *self.__rgb), 'rgb', self.__a, self.__wref)) def Gradient(self, target, steps=100): '''Create a list with the gradient colors between this and the other color. Parameters: :target: The grapefruit.Color at the other end of the gradient. :steps: The number of gradients steps to create. Returns: A list of grapefruit.Color instances. >>> c1 = Color.NewFromRgb(1.0, 0.0, 0.0, alpha=1) >>> c2 = Color.NewFromRgb(0.0, 1.0, 0.0, alpha=0) >>> c1.Gradient(c2, 3) [(0.75, 0.25, 0.0, 0.75), (0.5, 0.5, 0.0, 0.5), (0.25, 0.75, 0.0, 0.25)] ''' gradient = [] rgba1 = self.__rgb + (self.__a,) rgba2 = target.__rgb + (target.__a,) steps += 1 for n in range(1, steps): d = 1.0*n/steps r = (rgba1[0]*(1-d)) + (rgba2[0]*d) g = (rgba1[1]*(1-d)) + (rgba2[1]*d) b = (rgba1[2]*(1-d)) + (rgba2[2]*d) a = (rgba1[3]*(1-d)) + (rgba2[3]*d) gradient.append(Color((r, g, b), 'rgb', a, self.__wref)) return gradient def ComplementaryColor(self, mode='ryb'): '''Create a new instance which is the complementary color of this one. Parameters: :mode: Select which color wheel to use for the generation (ryb/rgb). Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 1, 0.5).ComplementaryColor(mode='rgb') (0.0, 0.5, 1.0, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).ComplementaryColor(mode='rgb').hsl (210, 1, 0.5) ''' h, s, l = self.__hsl if mode == 'ryb': h = Color.RgbToRyb(h) h = (h+180)%360 if mode == 'ryb': h = Color.RybToRgb(h) return Color((h, s, l), 'hsl', self.__a, self.__wref) def MonochromeScheme(self): '''Return 4 colors in the same hue with varying saturation/lightness. Returns: A tuple of 4 grapefruit.Color in the same hue as this one, with varying saturation/lightness. >>> c = Color.NewFromHsl(30, 0.5, 0.5) >>> ['(%g, %g, %g)' % clr.hsl for clr in c.MonochromeScheme()] ['(30, 0.2, 0.8)', '(30, 0.5, 0.3)', '(30, 0.2, 0.6)', '(30, 0.5, 0.8)'] ''' def _wrap(x, min, thres, plus): if (x-min) < thres: return x + plus else: return x-min h, s, l = self.__hsl s1 = _wrap(s, 0.3, 0.1, 0.3) l1 = _wrap(l, 0.5, 0.2, 0.3) s2 = s l2 = _wrap(l, 0.2, 0.2, 0.6) s3 = s1 l3 = max(0.2, l + (1-l)*0.2) s4 = s l4 = _wrap(l, 0.5, 0.2, 0.3) return ( Color((h, s1, l1), 'hsl', self.__a, self.__wref), Color((h, s2, l2), 'hsl', self.__a, self.__wref), Color((h, s3, l3), 'hsl', self.__a, self.__wref), Color((h, s4, l4), 'hsl', self.__a, self.__wref)) def TriadicScheme(self, angle=120, mode='ryb'): '''Return two colors forming a triad or a split complementary with this one. Parameters: :angle: The angle between the hues of the created colors. The default value makes a triad. :mode: Select which color wheel to use for the generation (ryb/rgb). Returns: A tuple of two grapefruit.Color forming a color triad with this one or a split complementary. >>> c1 = Color.NewFromHsl(30, 1, 0.5) >>> c2, c3 = c1.TriadicScheme(mode='rgb') >>> c2.hsl (150.0, 1, 0.5) >>> c3.hsl (270.0, 1, 0.5) >>> c2, c3 = c1.TriadicScheme(angle=40, mode='rgb') >>> c2.hsl (190.0, 1, 0.5) >>> c3.hsl (230.0, 1, 0.5) ''' h, s, l = self.__hsl angle = min(angle, 120) / 2.0 if mode == 'ryb': h = Color.RgbToRyb(h) h += 180 h1 = (h - angle) % 360 h2 = (h + angle) % 360 if mode == 'ryb': h1 = Color.RybToRgb(h1) h2 = Color.RybToRgb(h2) return ( Color((h1, s, l), 'hsl', self.__a, self.__wref), Color((h2, s, l), 'hsl', self.__a, self.__wref)) def TetradicScheme(self, angle=30, mode='ryb'): '''Return three colors froming a tetrad with this one. Parameters: :angle: The angle to substract from the adjacent colors hues [-90...90]. You can use an angle of zero to generate a square tetrad. :mode: Select which color wheel to use for the generation (ryb/rgb). Returns: A tuple of three grapefruit.Color forming a color tetrad with this one. >>> col = Color.NewFromHsl(30, 1, 0.5) >>> [c.hsl for c in col.TetradicScheme(mode='rgb', angle=30)] [(90, 1, 0.5), (210, 1, 0.5), (270, 1, 0.5)] ''' h, s, l = self.__hsl if mode == 'ryb': h = Color.RgbToRyb(h) h1 = (h + 90 - angle) % 360 h2 = (h + 180) % 360 h3 = (h + 270 - angle) % 360 if mode == 'ryb': h1 = Color.RybToRgb(h1) h2 = Color.RybToRgb(h2) h3 = Color.RybToRgb(h3) return ( Color((h1, s, l), 'hsl', self.__a, self.__wref), Color((h2, s, l), 'hsl', self.__a, self.__wref), Color((h3, s, l), 'hsl', self.__a, self.__wref)) def AnalogousScheme(self, angle=30, mode='ryb'): '''Return two colors analogous to this one. Args: :angle: The angle between the hues of the created colors and this one. :mode: Select which color wheel to use for the generation (ryb/rgb). Returns: A tuple of grapefruit.Colors analogous to this one. >>> c1 = Color.NewFromHsl(30, 1, 0.5) >>> c2, c3 = c1.AnalogousScheme(angle=60, mode='rgb') >>> c2.hsl (330, 1, 0.5) >>> c3.hsl (90, 1, 0.5) >>> c2, c3 = c1.AnalogousScheme(angle=10, mode='rgb') >>> c2.hsl (20, 1, 0.5) >>> c3.hsl (40, 1, 0.5) ''' h, s, l = self.__hsl if mode == 'ryb': h = Color.RgbToRyb(h) h += 360 h1 = (h - angle) % 360 h2 = (h + angle) % 360 if mode == 'ryb': h1 = Color.RybToRgb(h1) h2 = Color.RybToRgb(h2) return (Color((h1, s, l), 'hsl', self.__a, self.__wref), Color((h2, s, l), 'hsl', self.__a, self.__wref)) def AlphaBlend(self, other): '''Alpha-blend this color on the other one. Args: :other: The grapefruit.Color to alpha-blend with this one. Returns: A grapefruit.Color instance which is the result of alpha-blending this color on the other one. >>> c1 = Color.NewFromRgb(1, 0.5, 0, 0.2) >>> c2 = Color.NewFromRgb(1, 1, 1, 0.8) >>> c3 = c1.AlphaBlend(c2) >>> str(c3) '(1, 0.875, 0.75, 0.84)' ''' # get final alpha channel fa = self.__a + other.__a - (self.__a * other.__a) # get percentage of source alpha compared to final alpha if fa==0: sa = 0 else: sa = min(1.0, self.__a/other.__a) # destination percentage is just the additive inverse da = 1.0 - sa sr, sg, sb = [v * sa for v in self.__rgb] dr, dg, db = [v * da for v in other.__rgb] return Color((sr+dr, sg+dg, sb+db), 'rgb', fa, self.__wref) def Blend(self, other, percent=0.5): '''Blend this color with the other one. Args: :other: the grapefruit.Color to blend with this one. Returns: A grapefruit.Color instance which is the result of blending this color on the other one. >>> c1 = Color.NewFromRgb(1, 0.5, 0, 0.2) >>> c2 = Color.NewFromRgb(1, 1, 1, 0.6) >>> c3 = c1.Blend(c2) >>> str(c3) '(1, 0.75, 0.5, 0.4)' ''' dest = 1.0 - percent rgb = tuple(((u * percent) + (v * dest) for u, v in zip(self.__rgb, other.__rgb))) a = (self.__a * percent) + (other.__a * dest) return Color(rgb, 'rgb', a, self.__wref) def _test(): import doctest reload(doctest) doctest.testmod() if __name__=='__main__': _test() # vim: ts=2 sts=2 sw=2 et Grapefruit-0.1a4/grapefruit_test.py000066400000000000000000000354401274767260200175130ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*-# # Copyright (c) 2008, Xavier Basty # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. '''Unit tests for the grapefruit module.''' # $Id$ __author__ = 'xbasty@gmail.com' __version__ = '0.1a3' import unittest import doctest import grapefruit # Also run doctests. def load_tests(loader, tests, ignore): tests.addTests(doctest.DocTestSuite(grapefruit)) return tests class GrapeFruitTestCase(unittest.TestCase): def failUnlessNear(self, first, second, diff=9e-5, msg=None): ''' Fail if the difference between the two objects is greater than a certain amout (default 9e-5). ''' if hasattr(first,'__iter__') and hasattr(second,'__iter__'): if len(first) != len(second): raise self.failureException(msg or "%r != %r" % (first, second)) for f, s in zip(first, second): if abs(s-f) > diff: raise self.failureException(msg or "%r != %r @ %f" % (first, second, diff)) elif abs(second-first) > diff: raise self.failureException(msg or "%r != %r @ %f" % (first, second, diff)) assertNear = failUnlessNear class ConversionTest(GrapeFruitTestCase): '''Test the static color conversion methods.''' def testRgbHsl(self): self.assertNear((30.0, 1.0, 0.5), grapefruit.Color.RgbToHsl(1, 0.5, 0)) self.assertNear((20.0, 1.0, 0.625), grapefruit.Color.RgbToHsl(1, 0.5, 0.25)) #ff8040 self.assertNear((40.0, 1.0, 0.375), grapefruit.Color.RgbToHsl(0.75, 0.5, 0)) #bf8000 self.assertNear((1, 0.5, 0), grapefruit.Color.HslToRgb(30.0, 1.0, 0.5)) self.assertNear((1, 0.5, 0.25), grapefruit.Color.HslToRgb(20.0, 1.0, 0.625)) self.assertNear((0.75, 0.5, 0), grapefruit.Color.HslToRgb(40.0, 1.0, 0.375)) def testRgbHsv(self): self.assertEqual((30.0, 1.0, 1.0), grapefruit.Color.RgbToHsv(1, 0.5, 0)) self.assertEqual((1, 0.5, 0), grapefruit.Color.HsvToRgb(30.0, 1.0, 1.0)) def testRgbYiq(self): self.assertNear((0.5923, 0.4589, -0.05), grapefruit.Color.RgbToYiq(1, 0.5, 0)) self.assertNear((1, 0.5, 0), grapefruit.Color.YiqToRgb(0.5923, 0.4589, -0.05)) def testRgbYuv(self): self.assertNear((0.5925, -0.2916, 0.3575), grapefruit.Color.RgbToYuv(1, 0.5, 0)) self.assertNear((1, 0.5, 0), grapefruit.Color.YuvToRgb(0.5925, -0.2916, 0.3575)) def testRgbXyz(self): self.assertNear((0.4890, 0.3657, 0.04485), grapefruit.Color.RgbToXyz(1, 0.5, 0)) self.assertNear((1, 0.5, 0), grapefruit.Color.XyzToRgb(0.488941, 0.365682, 0.0448137)) def testXyzLab(self): self.assertNear((66.9518, 0.4308, 0.7397), grapefruit.Color.XyzToLab(0.488941, 0.365682, 0.0448137)) self.assertNear((0.4890, 0.3657, 0.0449), grapefruit.Color.LabToXyz(66.9518, 0.4308, 0.7397)) self.assertNear((66.9518, 0.4117, 0.6728), grapefruit.Color.XyzToLab(0.488941, 0.365682, 0.0448137, grapefruit.Color.WHITE_REFERENCE["std_D50"])) self.assertNear((0.4890, 0.3657, 0.0449), grapefruit.Color.LabToXyz(66.9518, 0.4117, 0.6728, grapefruit.Color.WHITE_REFERENCE["std_D50"])) def testCmykCmy(self): self.assertNear((1, 0.32, 0, 0.5), grapefruit.Color.CmyToCmyk(1.0, 0.66, 0.5)) self.assertNear((1.0, 0.66, 0.5), grapefruit.Color.CmykToCmy(1, 0.32, 0, 0.5)) def testRgbCmy(self): self.assertEqual((0, 0.5, 1), grapefruit.Color.RgbToCmy(1, 0.5, 0)) self.assertEqual((1, 0.5, 0), grapefruit.Color.CmyToRgb(0, 0.5, 1)) def testRgbHtml(self): self.assertEqual("#ff8000", grapefruit.Color.RgbToHtml(1, 0.5, 0)) self.assertNear((1.0, 0.5020, 0.0), grapefruit.Color.HtmlToRgb("#ff8000")) self.assertNear((1.0, 0.5020, 0.0), grapefruit.Color.HtmlToRgb("ff8000")) self.assertNear((1.0, 0.4, 0.0), grapefruit.Color.HtmlToRgb("#f60")) self.assertNear((1.0, 0.4, 0.0), grapefruit.Color.HtmlToRgb("f60")) self.assertNear((1.000000, 0.980392, 0.803922), grapefruit.Color.HtmlToRgb("lemonchiffon")) def testRgbPil(self): self.assertNear(0x0080ff, grapefruit.Color.RgbToPil(1, 0.5, 0)) self.assertNear((1.0, 0.5020, 0), grapefruit.Color.PilToRgb(0x0080ff)) def testWebSafeComponent(self): self.assertEqual(0.2, grapefruit.Color._WebSafeComponent(0.2)) self.assertEqual(0.2, grapefruit.Color._WebSafeComponent(0.25)) self.assertEqual(0.4, grapefruit.Color._WebSafeComponent(0.3)) self.assertEqual(0.4, grapefruit.Color._WebSafeComponent(0.25, True)) self.assertEqual(0.2, grapefruit.Color._WebSafeComponent(0.2, True)) self.assertEqual(0.2, grapefruit.Color._WebSafeComponent(0.3, True)) def testRgbToWebSafe(self): self.assertEqual((1.0, 0.6, 0.0), grapefruit.Color.RgbToWebSafe(1, 0.55, 0.0)) self.assertEqual((1.0, 0.4, 0.0), grapefruit.Color.RgbToWebSafe(1, 0.55, 0.0, True)) self.assertEqual((1.0, 0.4, 0.0), grapefruit.Color.RgbToWebSafe(1, 0.5, 0.0, True)) def testRgbToGreyscale(self): self.assertEqual((0.6, 0.6, 0.6), grapefruit.Color.RgbToGreyscale(1, 0.8, 0)) class NewFromTest(GrapeFruitTestCase): '''Test the static color instanciation methods.''' def testNewFromRgb(self): c = grapefruit.Color.NewFromRgb(1.0, 0.5, 0.0) self.assertEqual(c, (1.0, 0.5, 0.0, 1.0)) c = grapefruit.Color.NewFromRgb(1.0, 0.5, 0.0, 0.5) self.assertEqual(c, (1.0, 0.5, 0.0, 0.5)) def testNewFromHsl(self): c = grapefruit.Color.NewFromHsl(30, 1, 0.5) self.assertEqual(c, (1.0, 0.5, 0.0, 1.0)) c = grapefruit.Color.NewFromHsl(30, 1, 0.5, 0.5) self.assertEqual(c, (1.0, 0.5, 0.0, 0.5)) def testNewFromHsv(self): c = grapefruit.Color.NewFromHsv(30, 1, 1) self.assertEqual(c, (1.0, 0.5, 0.0, 1.0)) c = grapefruit.Color.NewFromHsv(30, 1, 1, 0.5) self.assertEqual(c, (1.0, 0.5, 0.0, 0.5)) def testNewFromYiq(self): c = grapefruit.Color.NewFromYiq(0.5923, 0.4589, -0.0499818) self.assertNear(c, (1, 0.5, 0, 1)) c = grapefruit.Color.NewFromYiq(0.5923, 0.4589,-0.05, 0.5) self.assertNear(c, (1, 0.5, 0, 0.5)) def testNewFromYuv(self): c = grapefruit.Color.NewFromYuv(0.5925, -0.2916, 0.3575) self.assertNear(c, (1, 0.5, 0, 1)) c = grapefruit.Color.NewFromYuv(0.5925, -0.2916, 0.3575, 0.5) self.assertNear(c, (1, 0.5, 0, 0.5)) def testNewFromXyz(self): c = grapefruit.Color.NewFromXyz(0.488941, 0.365682, 0.0448137) self.assertNear(c, (1, 0.5, 0, 1)) c = grapefruit.Color.NewFromXyz(0.488941, 0.365682, 0.0448137, 0.5) self.assertNear(c, (1, 0.5, 0, 0.5)) def testNewFromLab(self): c = grapefruit.Color.NewFromLab(66.9518, 0.43084, 0.739692) self.assertNear(c, (1, 0.5, 0, 1)) c = grapefruit.Color.NewFromLab(66.9518, 0.43084, 0.739692, wref=grapefruit.Color.WHITE_REFERENCE["std_D50"]) self.assertNear(c, (1.0123754, 0.492012, -0.143110, 1)) c = grapefruit.Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5) self.assertNear(c, (1, 0.5, 0, 0.5)) c = grapefruit.Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5, grapefruit.Color.WHITE_REFERENCE["std_D50"]) self.assertNear(c, (1.0123754, 0.492012, -0.143110, 0.5)) def testNewFromLabInteger(self): # Allow specifying lightness as an integer. lab = (60, 0.3, 0.3) c = grapefruit.Color.NewFromLab(*lab) self.assertNear(c.lab, lab) self.assertTrue(c.isLegal) def testNewFromCmy(self): c = grapefruit.Color.NewFromCmy(0, 0.5, 1) self.assertEqual(c, (1, 0.5, 0, 1.0)) c = grapefruit.Color.NewFromCmy(0, 0.5, 1, 0.5) self.assertEqual(c, (1, 0.5, 0, 0.5)) def testNewFromCmyk(self): c = grapefruit.Color.NewFromCmyk(1, 0.32, 0, 0.5) self.assertNear(c, (0, 0.34, 0.5, 1)) c = grapefruit.Color.NewFromCmyk(1, 0.32, 0, 0.5, 0.5) self.assertNear(c, (0, 0.34, 0.5, 0.5)) def testNewFromHtml(self): c = grapefruit.Color.NewFromHtml("#ff8000") self.assertNear(c, (1, 0.5020, 0, 1)) c = grapefruit.Color.NewFromHtml("ff8000") self.assertNear(c, (1, 0.5020, 0, 1)) c = grapefruit.Color.NewFromHtml("#f60") self.assertNear(c, (1, 0.4, 0, 1)) c = grapefruit.Color.NewFromHtml("f60") self.assertNear(c, (1, 0.4, 0, 1)) c = grapefruit.Color.NewFromHtml("lemonchiffon") self.assertNear(c, (1, 0.9804, 0.8039, 1)) c = grapefruit.Color.NewFromHtml("#ff8000", 0.5) self.assertNear(c, (1, 0.5020, 0, 0.5)) def testNewFromPil(self): c = grapefruit.Color.NewFromPil(0x0080ff) self.assertNear(c, (1, 0.5020, 0, 1)) c = grapefruit.Color.NewFromPil(0x0080ff, 0.5) self.assertNear(c, (1, 0.5020, 0, 0.5)) class ColorTest(GrapeFruitTestCase): def setUp(self): self.rgbCol = grapefruit.Color.NewFromRgb(1.0, 0.5, 0.0) self.hslCol = grapefruit.Color.NewFromHsl(30, 1, 0.5) self.hslCol2 = grapefruit.Color.NewFromHsl(30, 0.5, 0.5) def testInit(self): self.assertEqual(grapefruit.Color((1.0, 0.5, 0.0)), (1.0, 0.5, 0.0, 1.0)) self.assertEqual(grapefruit.Color((1.0, 0.5, 0.0), mode='rgb'), (1.0, 0.5, 0.0, 1.0)) self.assertEqual(grapefruit.Color((30, 1, 0.5), mode='hsl'), (1.0, 0.5, 0.0, 1.0)) self.assertRaises(ValueError, grapefruit.Color, (30, 1, 0.5), 'hsv') def testEq(self): self.assertEqual(self.rgbCol, self.hslCol) self.assertEqual(self.rgbCol, (1.0, 0.5, 0.0, 1.0)) self.assertEqual(self.rgbCol, [1.0, 0.5, 0.0, 1.0]) self.assertEqual([1.0, 0.5, 0.0, 1.0], self.rgbCol) self.assertNotEqual(self.rgbCol, '(1.0, 0.5, 0.0, 1.0)') def testRepr(self): self.assertEqual(repr(self.rgbCol), '(1.0, 0.5, 0.0, 1.0)') self.assertEqual(repr(self.hslCol), '(1.0, 0.5, 0.0, 1.0)') def testStr(self): self.assertEqual(str(self.rgbCol), '(1, 0.5, 0, 1)') self.assertEqual(str(self.hslCol), '(1, 0.5, 0, 1)') def testIter(self): self.assertEqual([1, 0.5, 0, 1], list(iter(self.rgbCol))) def testProperties(self): self.assertEqual(self.rgbCol.alpha, 1.0) self.assertEqual(self.rgbCol.whiteRef, grapefruit.Color.WHITE_REFERENCE['std_D65']) self.assertEqual(self.rgbCol.rgb, (1, 0.5, 0)) self.assertEqual(self.hslCol.hue, 30) self.assertEqual(self.rgbCol.hsl, (30, 1, 0.5)) self.assertEqual(self.rgbCol.hsv, (30, 1, 1)) self.assertNear(self.rgbCol.yiq, (0.5923, 0.4589, -0.05)) self.assertNear(self.rgbCol.yuv, (0.5925, -0.2916, 0.3575)) self.assertNear(self.rgbCol.xyz, (0.4890, 0.3657, 0.04485)) self.assertNear(self.rgbCol.lab, (66.9518, 0.4308, 0.7397)) self.assertEqual(self.rgbCol.cmy, (0, 0.5, 1)) self.assertEqual(self.rgbCol.cmyk, (0, 0.5, 1, 0)) self.assertEqual(self.rgbCol.intTuple, (255, 128, 0)) self.assertEqual(self.rgbCol.html, '#ff8000') self.assertEqual(self.rgbCol.pil, 0x0080ff) self.assertEqual(self.rgbCol.webSafe, (1, 0.6, 0)) self.assertEqual(self.rgbCol.greyscale, (0.5, 0.5, 0.5)) c = grapefruit.Color.NewFromRgb(1, 0.5, 0, wref=grapefruit.Color.WHITE_REFERENCE['std_D50']) self.assertNear(c.lab, (66.9518, 0.4117, 0.6728)) def testColorWitgAlpha(self): self.assertEqual(self.rgbCol.ColorWithAlpha(0.5), (1, 0.5, 0, 0.5)) def testColorWithWhiteRef(self): self.assertEqual(self.hslCol.ColorWithWhiteRef((0.1, 0.2, 0.3)).whiteRef, (0.1, 0.2, 0.3)) def testColorWithHue(self): self.assertEqual(self.hslCol.ColorWithHue(60), (1.0, 1.0, 0.0, 1.0)) self.assertEqual(self.hslCol.ColorWithHue(60).hsl, (60, 1, 0.5)) def testColorWithSaturation(self): self.assertEqual(self.hslCol.ColorWithSaturation(0.5), (0.75, 0.5, 0.25, 1.0)) self.assertEqual(self.hslCol.ColorWithSaturation(0.5).hsl, (30, 0.5, 0.5)) def testColorWithLightness(self): self.assertEqual(self.hslCol.ColorWithLightness(1), (1.0, 1.0, 1.0, 1.0)) self.assertEqual(self.hslCol.ColorWithLightness(1).hsl, (30, 1.0, 1.0)) def testDarkerColor(self): self.assertNear(self.hslCol.DarkerColor(0.2), (0.6, 0.3, 0.0, 1.0)) self.assertNear(self.hslCol.DarkerColor(0.2).hsl, (30, 1, 0.3)) def testLighterColor(self): self.assertNear(self.hslCol.LighterColor(0.2), (1.0, 0.7, 0.4, 1.0)) self.assertNear(self.hslCol.LighterColor(0.2).hsl, (30, 1, 0.7)) def testSaturate(self): self.assertNear(self.hslCol2.Saturate(0.25), (0.875, 0.5, 0.125, 1.0)) self.assertNear(self.hslCol2.Saturate(0.25).hsl, (30, 0.75, 0.5)) def testDesaturate(self): self.assertNear(self.hslCol2.Desaturate(0.25), (0.625, 0.5, 0.375, 1.0)) self.assertNear(self.hslCol2.Desaturate(0.25).hsl, (30, 0.25, 0.5)) def testWebSafeDither(self): dithered = ( (1.0, 0.6, 0.0, 1.0), (1.0, 0.4, 0.0, 1.0)) self.assertEqual(self.rgbCol.WebSafeDither(), dithered) def testGradient(self): gradient = [ (0.75, 0.25, 0.0, 1.0), (0.5, 0.5, 0.0, 1.0), (0.25, 0.75, 0.0, 1.0)] c1 = grapefruit.Color.NewFromRgb(1.0, 0.0, 0.0) c2 = grapefruit.Color.NewFromRgb(0.0, 1.0, 0.0) self.assertEqual(gradient, c1.Gradient(c2, 3)) def testComplementaryColor(self): self.assertEqual(self.hslCol.ComplementaryColor(mode='rgb').hsl, (210, 1, 0.5)) def testMonochromeScheme(self): monochrome = ( (0.94, 0.8, 0.66, 1.0), # hsl(30, 0.7, 0.8) (0.6, 0.3, 0.0, 1.0), # hsl(30, 1, 0.3) (0.88, 0.6, 0.32, 1.0), # hsl(30, 0.7, 0.6) (1.0, 0.8, 0.6, 1.0)) # hsl(30, 1, 0.8) scheme = self.rgbCol.MonochromeScheme() for i in range(len(monochrome)): self.assertNear(scheme[i], monochrome[i]) def testTriadicScheme(self): triad = ( (0.0, 1.0, 0.5, 1.0), (0.5, 0.0, 1.0, 1.0)) self.assertEqual(self.rgbCol.TriadicScheme(mode='rgb'), triad) def testTetradicScheme(self): tetrad = ( (0.5, 1.0, 0.0, 1.0), (0.0, 0.5, 1.0, 1.0), (0.5, 0.0, 1.0, 1.0)) self.assertEqual(self.rgbCol.TetradicScheme(mode='rgb'), tetrad) def testAnalogousScheme(self): scheme = ( (1.0, 0.0, 0.0, 1.0), (1.0, 1.0, 0.0, 1.0)) self.assertEqual(self.rgbCol.AnalogousScheme(mode='rgb'), scheme) def testAlphaBlend(self): c1 = grapefruit.Color.NewFromRgb(1, 0.5, 0, alpha = 0.2) c2 = grapefruit.Color.NewFromRgb(1, 1, 1, alpha = 0.8) self.assertNear(c1.AlphaBlend(c2), (1, 0.875, 0.75, 0.84)) def testBlend(self): c1 = grapefruit.Color.NewFromRgb(1, 0.5, 0, alpha = 0.2) c2 = grapefruit.Color.NewFromRgb(1, 1, 1, alpha = 0.6) self.assertEqual(c1.Blend(c2), (1, 0.75, 0.5, 0.4)) def testNearestLegal(self): c = grapefruit.Color.NewFromRgb(1.1, -0.1, 0.5, alpha=1.1) self.assertFalse(c.isLegal) self.assertNear(c.nearestLegal.rgb, (1.0, 0.0, 0.5)) self.assertNear(c.nearestLegal.alpha, 1.0) def suite(): return unittest.TestLoader().loadTestsFromName(__name__) if __name__ == '__main__': unittest.main(defaultTest='suite') pass # vim: ts=2 sts=2 sw=2 et Grapefruit-0.1a4/setup.py000066400000000000000000000044151274767260200154420ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*-# # Copyright (c) 2008, Xavier Basty # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. '''GrapeFruit setup and build script.''' # $Id$ __author__ = 'Xavier Basty ' __version__ = '0.1a4' # The base package metadata to be used by both distutils and setuptools METADATA = dict( name = "grapefruit", version = __version__, py_modules = ['grapefruit'], author = 'Xavier Basty', author_email = 'xbasty@gmail.com', description = 'A module to manipulate color information easily.', license = 'Apache License 2.0', url = 'http://code.google.com/p/grapefruit/', download_url = 'http://grapefruit.googlecode.com/files/grapefruit-%s.tar.gz' % \ __version__, keywords ='color conversion', ) # Extra package metadata to be used only if setuptools is installed SETUPTOOLS_METADATA = dict( install_requires = ['setuptools'], include_package_data = True, zip_safe = True, classifiers = [ 'Development Status :: 3 - Alpha', 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Multimedia :: Graphics', ], test_suite = 'grapefruit_test.suite', ) def Read(file): return open(file).read() def BuildLongDescription(): return '\n'.join([Read('README.rst'), Read('CHANGES')]) def Main(): # Build the long_description from the README and CHANGES files METADATA['long_description'] = BuildLongDescription() # Use setuptools if available, otherwise fallback and use distutils try: import setuptools METADATA.update(SETUPTOOLS_METADATA) setuptools.setup(**METADATA) except ImportError: import distutils.core distutils.core.setup(**METADATA) if __name__ == '__main__': Main()