glue-0.13/0000755000076500000240000000000013106305703012273 5ustar neostaff00000000000000glue-0.13/AUTHORS0000644000076500000240000000141312475162420013347 0ustar neostaff00000000000000Glue is mainly developed and maintained by Jorge Bastida A big thanks to all the contributors: Bradley Abrahams Jaime Irurzun Juan Riaza Joris van Summeren <@_joris> Rafael de Oleza <@rafeca> Maciej Pawlowski <@mpa> Dominic Barnes Lars Pe <@lars85> vertex <@vertex> veep <@veep> Capi Etheriel Marc Diethelm <@Marc Diethelm> Jimmy Yuen Ho Wong lzubiaur Ady Liu Robert Morrison Lorenzo Gil Sanchez Josh Schneier <@jschneier> And to the inspiring article by Jake Gordon: http://codeincomplete.com/posts/2011/5/7/bin_packing/ glue-0.13/COPYING0000644000076500000240000000274412322177142013340 0ustar neostaff00000000000000Copyright (c) 2013 Benito Jorge Bastida All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the author nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. glue-0.13/docs/0000755000076500000240000000000013106305703013223 5ustar neostaff00000000000000glue-0.13/docs/.gitignore0000644000076500000240000000000712322177142015213 0ustar neostaff00000000000000_build glue-0.13/docs/changelog.rst0000644000076500000240000002270713106305623015715 0ustar neostaff00000000000000Changelog ========= 0.13 ^^^^^^ * Update Jinja version 0.12 ^^^^^^ * Update Jinja version 0.11.1 ^^^^^^ * Fix packaging issue. #192 0.11.0 ^^^^^^ * Fix incorrect order in JSON output #186 * Stop using utf-8-sig #162 * Fix several typos in the documentation #165 #181 #182 0.10.0 ^^^^^^ * Added support to ``python 3.3+`` 0.9.4 ^^^^^^ * Make glue only require argparse if sys.version_info is < 2.7 (Thanks Lorenzo) * Make glue read project-level configuration files #139 (Thanks Ady Liu) * Fix ``--less-template`` and ``--scss-template`` options. 0.9.3 ^^^^^^ * Make the css output prettier #137 (Thanks uberrobert). 0.9.2 ^^^^^^ * Fix transparent images cropping #133 (Thanks lzubiaur). * Fix CSS format paths on windows #132 (Thanks Most). * Fix --cachebuster-filename #135 (Thanks monsanto). * New image ordering ``filename``. * Fix less retina output #122. * New option: ``--cachebuster-filename-only-sprites`` #113. * Fix existing ``--cachebuster-filename`` as it was not cachebusting css files since ``0.9`` #113. 0.9.1 ^^^^^^ * Fix issue with cocos2d output format #131 (Thanks lzubiaur). 0.9 ^^^ * This version solves lots of design flaws glue had since it was created two years ago. At the very beginning, glue was created as a single-file script to create css sprites. After two years of development, an outstanding support from the community and an incredible support from lots of companies it's time to refurbish glue from its foundations ensuring it lasts over time. * This version introduce a new concept named ``Formats``. Every output file generated by glue now is created using a format. This change decouples glue's core (as far as possible) from how the output is created allowing glue developers to create different formats without messing glue's core. Fixing #64 * Glue now support two new output formats (cocos2d, json and CAAT). CSS will still be the default output. * New project layout, algorithms formats and manager now have their own packages. * Glue now uses ``argparse`` instead of ``optparse``. * Glue now requires ``Pillow>=2.2``. Fixing #99 * Glue now requires ``Jinja2>=2.7``. * Glue now uses utf-8 as default encoding for css files. #65 * New (fully rewritten) test suite and test framework in order to make writing tests easier. * ``--margin`` now supports more than only one margin, you can now use it with (e.g.) ``--margin=10 20 10 20``. Fixes #101 * New ``setup.py`` fixing #110 * New options ``--source`` and ``--output`` will now complement the first and second positional argument. * New option ``--css-template`` will allow you to choose your own css output `jinja template `_. * New option ``--scss`` will use ``scss`` as file extension instead of ``css``. Fixing #86 * New option ``--json-format`` will allow to customize the json structure generated by ``--json``. * New feature: Every settting is now configurable using `environment variables `_ and `configuration files `_. * New option ``--pseudo-class-separator=_``. * Glue now support multiple css pseudo-classes applied to the same image #87. * Glue css pseudo-class separator is now ``__`` instead of ``_``. If you want to make ``glue`` work in legacy mode use the new option ``--pseudo-class-separator=_``. * Glue will not validate css output if ``--no-css`` is present #78. * Glue CSS media-queries now includes ``min-resolution`` See https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Media_queries#-moz-device-pixel-ratio. * Several bugs fixed like #70 #93 #94 #108 #109 #110 #115 * This version includes **several** backward incompatible changes - **read this document carefully before upgrading**. - Glue will **not** extract padding information from filenames. If you want to customize paddings individualy (per image), you should use `configuration files `_. - ``--imagemagick`` and ``--imagemagickpath`` are now deprecated. These options were introduced as a workaround in order to solve some scaling glitches caused by ``PIL``. Pillow ``2.2.0`` solves these issues. - ``--each-template``, ``--global-template`` and ``--ratio-template`` are now deprecated. CSS output customization can be easily done now using ``--css-template=template.jinja``. - ``--ignore-filename-paddings`` is now deprecated. Per-file customization can be easily (and more scalably) done now using configuration files. - ``--optipng`` and ``--optipngpath`` are now deprecated. There are lots of png optimization libraries and it would be silly to support all of them. Feel free to optimize your png files using your favourite one. - ``--debug`` is now deprecated. If an unhandled Exception is triggered, glue will now automatically show some friendly debug information. 0.4.1 ^^^^^ * Make glue require Pillow >= 2.2.2 in order to make it work on Mavericks (Thanks wyuenho). 0.4 ^^^ * This version is a transition between glue ``0.3`` and glue ``0.9``. * The following arguments will now show a deprecation warning: - ``--imagemagick`` - ``--imagemagickpath`` - ``--global-template`` - ``--each-template`` - ``--ratio-template`` - ``--ignore-filename-paddings`` - ``--optipng`` - ``--optipngpath`` - ``--debug`` 0.3 ^^^ * New ``--imagemagick`` option. If present, glue will use ImageMagick to scale down retina sprites instead of Pillow #72. * New ``--imagemagickpath`` option #72. * Soft 2px default for margin no longer exists #73. * Fix how glue choose which classes to add to the global scope in order to add pseudo-classes if needed #77. * Fix camelcase separator as it wasn't preserving original case #74. * Fix sprites containing images with filenames included in PSEUDO_CLASSES #59. 0.2.9.1 ^^^^^^^ * Fix ProjectSpriteManager issues. 0.2.9 ^^^^^^^ * Improve error messages. * Added variable ``identifier`` to ``--each-template``. * Glue now require ``Pillow==1.7.8`` 0.2.8.1 ^^^^^^^ * Fix maximum recursion depth issues in ``ConfigManager`` * Update Documentation. 0.2.8 ^^^^^ * New ``--recursive`` option. * New ``--follow-links`` option. * New ``--sprite-namespace`` option. * Speed up improvement: Glue is now 1.3x faster in a cold run. * Speed up improvement: Glue is now 14x faster for already created sprites. * Glue now store some metadata inside the generated sprites in order to not rebuild them again if the source images and settings are the same. * New ``--force`` option to make glue rebuild the sprites. * New ``--no-img`` and ``--no-css`` options. * Fix some CSS aligment issues related with odd sized images. * A soft default of 2px of margin is going to be added while using glue with ``--ratios`` or ``--retina`` in order to fix scaling noise. * Fix ``--url`` in order to override relative path calculated by ``--img`` and ``--css``. 0.2.7 ^^^^^ * Glue now require Pillow instead of PIL (http://pypi.python.org/pypi/Pillow/) * Improve compatibility with less allowing variables in the urls (Thanks rafeca). * Fix cachebuster issues with --retina and --url 0.2.6.1 ^^^^^^^^ * Fix bug with images that only contain digits like. Thanks to Russ Ferriday and Paul Hallett. * Make possible read optipng related configuration from static configuration files. 0.2.6 ^^^^^^ * Added support for multi-dpi (retina) sprite creation. * New ``--ratios`` and ``--retina`` options. * New option ``--debug`` * Performance improvements. ~10% on big sprites. 0.2.5 ^^^^^^ * New ``--watch`` option to keep glue running in the background watching file changes. * New option ``--html`` that generates a html using all the available css classes. * New option ``--margin`` that adds margins around the sprited images. This margin doesn't count as image size. * Add MANIFEST.in and tune the setup.py preparing the Debian/Ubuntu package. * Fix _locate_images to be deterministic. * Add support to Travis CI. * Fix 8bit B/W images bug. 0.2.4 ^^^^^^ * Better error handling: Glue will now return non zero return codes if something goes wrong. 0.2.3 ^^^^^^ * Fix ``--version`` * Fix the camelcase ``--separator`` to not lowercase the filename before the capitalization. 0.2.2 ^^^^^^ * New feature: Per-file pseudo-class customization. * Added support for 8bit bg images. * Added support for digit-only images. * Fix newline characters support on ``--global-template`` and ``--each-template``. * New algoritms ``vertical-right`` and ``horizontal-bottom``. * New option ``--separator``: Customizable CSS class name separator. 0.2.1 ^^^^^^ * New command line argument ``--global-template``. * New command line argument ``--each-template``. * ``-z`` and ``--no-size`` arguments are now deprecated. 0.2 ^^^^^ * The default behaviour of glue is now the old ``--simple`` one. * The old default behaviour (multiple-sprites) is now accesible using --project * ``--simple`` argument is now deprecated * New ordering algorithms square, horizontal, vertical and diagonal. * New command line argument ``--ordering``. * New command line argument ``--cachebuster-filename``. * Old algorithms maxside, width, height and area are now orderings. * Glue now ignore folders that start with a '.' * CSS files will now avoid using quotes around the sprite filename. * New ``-v``, ``--version`` option. * Fix bugs. * New test suite. 0.1.9 ^^^^^ * New command line argument ``-z``, ``--no-size`` to avoid adding the image width and height to the sprite. * New command line argument ``--png8`` forces the output image format to be png8 instead of png32. * Improve CSS parsing performance removing bloat in the CSS. * Improved documentation. glue-0.13/docs/conf.py0000644000076500000240000001634412322177142014535 0ustar neostaff00000000000000# -*- coding: utf-8 -*- # # glue documentation build configuration file, created by # sphinx-quickstart on Sun Jan 1 19:36:52 2012. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'glue' copyright = u'2013, Jorge Bastida' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. import pkg_resources try: release = pkg_resources.get_distribution('glue').version except pkg_resources.DistributionNotFound: print 'To build the documentation, The distribution information of glue' print 'Has to be available. Either install the package into your' print 'development environment or run "setup.py develop" to setup the' print 'metadata. A virtualenv is recommended!' sys.exit(1) del pkg_resources version = '.'.join(release.split('.')[:2]) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'nature' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". #html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'gluedoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'glue.tex', u'glue Documentation', u'Jorge Bastida', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'glue', u'glue Documentation', [u'Jorge Bastida'], 1) ] glue-0.13/docs/faq.rst0000644000076500000240000000334612322177142014535 0ustar neostaff00000000000000FAQ === Errors compiling PIL in Snow Leopard ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash /usr/libexec/gcc/powerpc-apple-darwin10/4.2.1/as: assembler (/usr/bin/../libexec/as/ppc/as or /usr/bin/../local/libexec/as/ppc/as) for architecture ppc not installed Installed assemblers are: /usr/bin/../libexec/as/x86_64/as for architecture x86_64 /usr/bin/../libexec/as/i386/as for architecture i386 /usr/bin/../libexec/as/arm/as for architecture arm _imaging.c:3017: warning: initialization from incompatible pointer type _imaging.c:3077: warning: initialization from incompatible pointer type _imaging.c:3281: fatal error: error writing to -: Broken pipe compilation terminated. _imaging.c:3017: warning: initialization from incompatible pointer type _imaging.c:3077: warning: initialization from incompatible pointer type lipo: can't open input file: /var/tmp//ccsCS1Iv.out (No such file or directory) error: command 'gcc-4.2' failed with exit status 1 The reason for this error is that Apple has removed from Xcode the assembler for PPC, while the core system retains their PPC images in the fat binaries. If you run ``file /usr/bin/python`` you will likely find the following:: /usr/bin/python: Mach-O universal binary with 3 architectures /usr/bin/python (for architecture x86_64): Mach-O 64-bit executable x86_64 /usr/bin/python (for architecture i386): Mach-O executable i386 /usr/bin/python (for architecture ppc7400): Mach-O executable ppc Python compiles C extensions with the same compiler flags that Python itself was compiled with. The solution? Install ``glue`` using this line:: $ sudo env ARCHFLAGS='-arch i386 -arch x86_64' pip install glue glue-0.13/docs/files.rst0000644000076500000240000000741012322177142015064 0ustar neostaff00000000000000Configuration files ========================== Introduction ------------ ``glue`` has around 30 command line options. Remember all of them every time you need to rebuild your sprites could be really annoying. If you are using glue as part of your assets rebuild process and you want consistent executions over time, using configuration files could be a good idea. The only thing you need to do is create a file named ``sprite.conf`` inside your sprite folder (or project folder if you want to apply this settings to your entire project) and glue will override your command line options using these settings. Project-level and sprite-level configuration files can coexist:: sprites ├── actions │   ├── add.png │   ├── remove.png │   └── sprite.conf └── icons │ ├── comment.png │ ├── new.png │ └── rss.png └── sprite.conf If for example you want to change the namespace and the default padding to all your sprites you can add this to your project-level ``sprite.conf``:: [sprite] namespace=my-sprites padding=20 If the ``actions`` images needs to be cropped and have a different padding, you can create add the following settings to your new ``actions/sprite.conf`` file:: [sprite] crop=true padding=10 If the ``remove.png`` image needs to have ``10px`` margin and ``0px`` padding you can append a new section to your ``actions/sprite.conf`` like the following:: [remove.png] margin=10 padding=0 This will override any previous setting about ``margin`` or ``padding`` affecting ``remove.png``. .. note:: project-level, sprite-level and image-level settings override any environmnet or command-line settings. More information in the `settings section `_ Available configuration ----------------------- ============================ ============== ============== ============== Configuration File setting Project-level Sprite-level Image-level ============================ ============== ============== ============== source output quiet watch project recursive X X follow_links X X force X X algorithm X X algorithm_ordering X X css_dir X X css_namespace X X css_sprite_namespace X X css_url X X css_cachebuster X X css_cachebuster_filename X X css_separator X X css_template X X css_pseudo_class_separator X X less_dir X X scss_dir X X img_dir X X generate_image X X png8 X X ratios X X html_dir X X cocos2d_dir X X caat_dir X X json_dir X X json_format X X crop X X X padding X X X margin X X X ============================ ============== ============== ============== .. note:: You can't enable output formats using configurations files. If for example you add ``less_dir`` to your ``sprite.conf`` this would only override ``less`` output fodler if less is already an enabled output format. glue-0.13/docs/img/0000755000076500000240000000000013106305703013777 5ustar neostaff00000000000000glue-0.13/docs/img/buttons.png0000644000076500000240000001211712322177142016210 0ustar neostaff00000000000000PNG  IHDR|${tEXtSoftwareAdobe ImageReadyqe<PLTE v ts7 xqŢƙ{B͊BbIA:T~C įR{q?ukػ=s>{@M;(VB 59[Dʖ_~'=|8 r)?t D*@r6*A5y+Bz{/c E+C]fn0e=Gb,],7U{2hO .a$Nw~"I"Hwla#L'Sn|-`jq{&Q!H%Px&QLUmZcyw2t{b!xf0j4h +\MVqпB͂1T ~Naa>,_зPZyWx#٥ ,Mzz;|T̟ܵq?wgBAy}kguW!GϨ<!Gj"x`0*I+Zb+5Nֳ؈#/T)FuAC{lpv:<.E:@Lm 6ipq2`a @dv-B/DONϨ}?6J|/:Y4vϠY̮F'U2@d&B/dp"x|ĮtRNSS%IDATx{tםG Z`u+ =H{݇Gw}ubXp׍l,C%71~6%C․Cjk#46l CxACiv]/$9;{f- rT9z22'Z0?|;Q]γ?x]rZ?^Gr\x9ܣ?|%K@_%W+''%|F;{Q_p_ +<KDHDHDIDJDgZuzi ^J,U"]d-(0sҧeO^_ʎ(I$7%J$Rx ѓ3'1mq_Z_ЦR0ߩ츗K}&.VSla'PgхӺKqt}8?_8y? `}&RD7WL@J\tSfM"᯼e Kn)a &`gK,Dbtq?^Z?'H^gL$Jx%EެL GһN䆂2I.>d)I<9;66VdW_XgݛWM2~dll߮@|O*?ADfI \j@ Jp|q}n3xU֣}$zpOĭ|o|hX"s Ứn-=8Y/o$T[vVΒu=y`gIy\L5x)7s-aIBQ"Ǘ^jE\\jQ`;l1_Cl-A6`.p`2akҔ쁹#IGp@]s3k5-k*rge.č|MDt+!b2`}KH/zMP$OI]܏Z%_ WZ8ܔ֜!/Zt os=ʼnx\:,>Oȋ@apk`xLiq?Rn% <(>1 7PEIhì:^L\67VEP\ =7 &|\;#d KQ9:/z`?Ʈ\Lԝv6\ Y˄˅/<~cA/ GDp=L> !rƹ `A./-8T}]$|'Ú#ކj(:\GbAD4= C2YkT5<\iFŸ$@qM6HaT^,*=0&K*I0UcǴV 9x;qհmʁ}8cb?yL`0H)!dlkQӴ"vlҺ!0nA5p7TVR"D8+1>}x-B2{b11 Y=7o`}_@EfTf"ন&;ի6^&I-8[Z̻50iEB N9Nƿ40m>+f &qڪCMٺԪ5߱Z]1Mp,N[FM0Xnf04};p)o@Í=@:7pa$6M[A"mz32tH Q6VL%ඊt)?`ݷB UnLrNvu9#ʘui6h6)mZ=P[![ѩ>%b1[^Rf&eѴưEJ?習hwòc^x7iAq0WH/&w7A " 4|G3z|F _PWAdN-iSil͙<9hboF]N-TӽP_~$ˡag] "jݻ GZ i:y$G Ż*CW! ZHUKD:ǝ*oB{]G) uq! Xjpw1ݧNw~ghůIENDB`glue-0.13/docs/img/crop.png0000644000076500000240000000773412322177142015466 0ustar neostaff00000000000000PNG  IHDR9oFtEXtSoftwareAdobe ImageReadyqe<PLTEVFEJE2ZIDATx읋&pk^kfiR>giDui_oJMQ2%GQr%GQr%GhSr%*!J(9J(9J(9J6%GIPr%GQr%G ҝP%FhSr%GQr%GQr%Gy1΁]s eSN͓SJT&GF66*dx,8.9EuirL䀖]3Y/Вjk'IT&G;`$YRsVd"OT&G:,6cX9/S$Z!ST&8n?+Ee)+ w:?;*968v oN4&)䰋d(sێ=B^YzRՅDY瓘?w7%%IO ĒSCT&Ӝ85g KzHrd&AIDsKK)6')Y;̢4qfj< sK(2Ȕ(rEuirUN,ԑ1ċ/lII"]T&_O' XE/KJ9Duirq8@:CdYd9CN Q];n^v^)?_/&%*4.m'%%ɜrr.\+^JOJ9Dui2N\+^ʖGVՅ7申 "|"+TUrj6S)׊_dŒINuQ]t2Ypċ.rIU$.L9r'oW/ &U0v4UW%KK,,T5r$)!{^oLo.ˮ;xb&.M9^r$Eg:ڳx"cJʢ4L=75n]D[;Ș%.Mxg/_fG*2`Jڢ4r۾?N.cv;1h>p#o%Ÿ*GT&_14ӣ;S!b$G.L8ug=\P@x"K90wk#X-X)G-2Ƥ$ }Wp=CWw^asvx"cLA0s+i!qH>q#gRlHՅ1q5@KB`3X|2G*2`MI09[j8K-TdI1#IT&{C /FazFTa TlIIFi39uY_r#Y1jaw ˏO&G4NB֖(y[Z/5op7oGrXŒÒTrS(yJNv!#^49Ie"4#J^>oVrTDx%/7^+9c6r9Fa#urfOo6^ 䉒i:9FtDΠ䰑kkd! Q2M-rF䌭ӆ(&z`=,HI9irx]F="2L 9͈irKAfޝm<"oLζ39kc;iw75OH7@]{)/r&%0qC￧uw<ZdCNEQ]Lpз",¬aq4E#$%j0 cy&,E EN-Q]Ls޼5;E;Y8^JNjHN%Q]Ɔ  Jl^$e :0@%%[zRU4Q]츳h܋UrһiI &OT&V ാ=Wi;\Fs{31>$&KW Ү28X&޻SNdIT`x/Oc!ET&G8(ɂhxo,"vaեɑ6BzIXcor;^w>᳎9MhQk>Pr Z[Sr%g1lMQrEfkiuG[3+ִٙiKi?i&` 2IENDB`glue-0.13/docs/img/famfamfam1.png0000644000076500000240000020767612322177142016526 0ustar neostaff00000000000000PNG  IHDRæ$tEXtSoftwareAdobe ImageReadyqe<PLTEߝf[fff>k0fZdS𮎲ՙVv ff[333 U6j4q"Ur63uOGff3†9iʻ1-RABDsGwpˮ>>mxp-/2 Q~9~ۆ_3T nz'(m`%aX+ϙOO&i!oU\?dsňJ` eٷ`7li>yei%o̎~zӮsHt:/eQVlor{VlYEpKQu7vvU4q@?oݖx. <ϳ htL|7Wst)xA h7s(hEQ߁4dwCXpI#^GgZ2gD}ޮ#$Ϫ]ܻrpCe']7p"tz f0 Kr_x- r{FWc.@Lxpf`~H!va2[h=֢nؽO7b2ivC l?KpJً(@X !yw;{[p=""6tޘ!ty KhO} \+<*d,eb~^0ض#Z}I ZgfJoeU) O}LdSZЏ `9Ѣ:" v׌.?1 pnJr 4R-<!ߟ#y- 3{so3{x]jc>sE0 8=h l- (vм= !@Zbv`[rB.0yid?ƕ??`KU C[0߃\O'1LKi\nd 58$0VrzƬtN|]?, v$*k1-7sEm{3| v[z  fX#f$П_v7|x3J2H-4 qptG(e*˯P'n\#pQpu ksCqag~5` ͹1o QtUдdmy'S<䐐̏ZbqFfnze@@l.?9` S<}}t0(a`/whQp{ c?qyXQ ,q8DV~<y#hI|BCt:$vj PQJAsp@pFbH͐@? 쨝 \IM3n*_aTxwƘpku=\w?1>}Z/:ݶyЁ:Zge,5ѝ#vWrMbvYEL``䎃3sa@`ӬnڕX/R{` SL~Rmf}f92<‚,>KA=o-H 5PWV_ktngkz{wl0Y5/ܢx ٿS`8KX,CQ~1rsB7 y]Wڷ5LB=0T,>l{f{:6|St\NNgg/os3B~ׯЙת\3s᭠.)zJf<|:In=EvQWJ5:P>PQ)ڊ0,b_Vz=}9׶rp///ppV3P=|# hOhP _*m͛V$3|sŸpGC=qlZ崇?,pq- |5@e> _^~;Ak5<0?uv# FaY +_j8M'Ǡ.~P`21ƃ?6c|;+&6'"g>@ r(k`Yx6`8‡Q 2ew1E ]0h7,(GN8ÙXF)A`z>OCj &g >pʏ?t8,vqah۷`ZX6>@_PPKP~j+GFhH: m :G2$U0͊ 8:hpY kbx?|pVw|@ ڛ= ף@@ō/[ ( |tK|9X|VES.Oq>`BH{;$ "p \+||< app_ Oߏ +3nzoF>/C" J`rQ 3pUpD&I#&UbC[0!]FHhC?_Yhpx|@$u=3Ѿٶy8_7phC@П@lMFh 󷑀#<$Uqj'$iU E +|0pj.  ]@ i#0th3,I:$ Ǎ¿Xı HR"=(D/$jswpFhtov>BB oo2780ߌRzc|֦](@9n!dA?g>\O aS: |!|3PdVFu: vf4/Nmں |^'0&#"N?-b78B&L8-\H">̹?72:ߏNA^IC\ @ 0@|(m3 #OXK;j,>H`<%l !]n<p, $7I`xh9#f>xSpL + La6,/Esyy;k8U"@_- >*'@R@+$8 <"~H;F)ueلbPL r+&y V0I=by_A|U)pp >˳fGW^:G'>||]_}Tr4x8mUG3\/iTu} 'фx`}얼E<~@wVtb6| %@#">=%)~祚Ӂu``c7`{`X,Ywys"( x} z'Hx+*܏"@FlyxęXJoԗi 8!xr)mO[DF<m}W? ;R3]D<иb `7"TH(>wWp2(x= Z<|rE'NS|c;.c>nU`u- *eڸyRLͰn`P@ۣ#"!Ó*^D0q-ףݕ|;oïA@fO#NfCx|-!|~4jL.tpp@m|j=kthL߆_t.|`XʆWz`+8| EaљCĝ81BlBxB;HOzw:I7W OH=4p ŸWަMW0o6cxCp0TO (#M|sʰ;.Ew#`Dk >│p G핊o8qfgB$#p;|!"w*$ˉA>z f:nۺ>%\ ~`/\/__s@@|ϰ7B? |Z1GrZ>u+.H L PD:?`3II|솖7ަu} EbLWt"|Z|YX`P2^i%:;H& |\|]K`,%-YdI`4KXXHﵦ^o~-NT% Ko"E+ۨ(=+n ?"*&N&q@w>^h}h &0cH!rGFXy|T{p IZr88-}OIP@vŶ¿V?hK=菛n4s&@/h=I'5J`l^1nҍpGJۥ֌ o>BǷ m1't%m e)}3?B}\VyL$_NGj|п7G W.>|~x@F<@p&>-_yJ X&xI2 lK |$ p`a8}ކE>>@{g<F8yd`):e$ g_;Q 0*&$TB{<׆ )l $V{C`}OWE 0a }BW|T1L]/O若-Ar2xSz<6 N<@|Z`E'|\N2h%<@\Wc*C 9|Rd{ $h/ |WX Lʩ#H>~~C<UWppjpU|9P|G:>N{&0N]0'. 4U)A b7xzkxzt0,{q~`<I`G3T8X} \>@~-0  ?'z¿"o ІKp(i^C‰y|<|\ >` ,iX^ =`f^rpG8UGe x>~<@ \di X*%`.q[cI0=6%P% S5}v>lкo:r3?43Ǚs>{@Mh>Ӧ<yF}xGh|>yv>@=_Ge*Bo|;e> {}q2q~|LHoF>@%$w/l1B.Gq!>@^9L0?/Hy5(!0⽡F>?Z5>:[} =>\{A}@]1B:NgiZ``_.8eJDGEf\֯'I@pF|xDD!yZ߯|z~0?:T`j:'^DH4"ܭpj/ } mMhDGsQ)DX:BX* S"^X"COd#DyjP{֤__H:oe;+Li(" @譩(@Ta==9` [!"$!->085P&?"J"+~ +g2Ay٘m}p Dj10s[vJ@~  Md#,B@tv`)s+@W#|:Ey{a߇xv,00mGDߙ̞`\ 7 &oEG=/W9"_C+x@z]b&C ܫJFHW)B@{p Eƽ |k>;z,GbL^PF|?R"Pk3B@ޗ5p|?f߬\GAm>o뀈J`EH% @A39>@f`u# <)$I>cLE> HBDWHBg !EOo?#rcpSI R(H6(s@%8? !;>2H>o*X_\`f}@6h  WuV)=Ix !pzQpi*z 2 F יs_<pۿy#~\/@,BOE> n#7U7Po$r~_`wNc~ҽM XĂ."=dzW0("ຌ/A\//^ {gm bqB[?@sl8_ŋ$!!FYS+t$qccsۉ A1BpTf뉃>@Hꡟ~u^U]`hxt9L0Qz5p)_q@=P 'O*n $6#^?8/1j_~q]ǯ?e>WPϘ'ݝTͳՔ9rtоPi}:'~Z>񻝰u'"~1>@Q _v .FٻB._P)DG@c>ղWiAh 6XgI!|N~U'btuO9 'SA, q؇%eoA>H 9xy٣,hrmڮwAш< C|TjA͌gZ-Y%`4JO aFYRp "n\k(M|ң$ N ;&~x0\1f%(  7'~V}3|igoÌ BZ&K>m|1?3vBXbGB?ƨ{ u9?T8>)_@~z"1*`ƒoSY>=e8@/=<2̮Uaَca9_߿}ѨE @!N4>@ .iOە|./Э H% #+υrر27<Ћnww*್䬱z.qz @ɋK11xR|YRH^z>`BQ$^i_sIL`u %q&N `# X yDz/~1F0 )`C.$R`0]TL ?[L#F] (_ PD x$3g9bN4|\cd_y8; B`~OhC7I`n 衦1~2Z #_I_i;V˾04 hA@AW0p<frLb񑴡bOXDG7sH r<( |x yn2N1@.7|`XfZ~eS@]ahw%2?&v"Yh12I YP8UJ-s @h^-b@8r@!yTj>@SBtYLBZL0V 9$RLX'P|/z${db,>8pLiL) |;pxirCɄǒV!f-b>w j|`ru7r898 .Ҵ鍟Nۥw  REl8\!q <=@ڿa m1@ 0 { WI:ɇZ ЁN %Y>@EH£}LXYG:c|G;\|w^62eVE}}x!ݟ`D`GJx(HWg,Lw@}_G4>N` @8al}}S^An X=N .Z` U8tRo)_߻%_̰C(BZgi֘Yf i.?IY@  x&`ǔ+p||7z:%>@''T[@}t$#4.}Z 0p t NgztZ14b&!0QYE88E=@o~~^XG>'7|ﺍ|8W| _>^D( @,=;Z6p~-|dFثX\8/+ڿhWY|JMzAjiZS`E[K]l&f8_& &x?aGGءR_Di"d𗾥Vpo5wQiW>@5>@S47[~b|T7T |TFl^d%#@{^3^ä^|N"{ȉI)˿. |,:yG9 eMXGTp)PW$-R* Vvq7 pôsk5XK@'Մ=JLx0p)_@$xV>n{Kuރ|˹z.x>Px(K H;Q3#;dy&}JpNom>[!0HsZ\{C@<JF{~v"#|bN7p8X]k|ڪAgv[ۭ@`|ډx=.X[:ѐ_ekW($(#}Z|QN:oi|>`$h`좈vDQPϿ9D*F'0(~-,#\@XK[|q@_]@1ޱP)2:=߿:}<Mm +|PE [1E@ݽ%] >_RuOU=qQhGpPM@Qw8rZTQ'|o, `wRqq:Xc}FEs`>@z<#O|2}5N N8X~i]oScp8|X!y=OGPU]zz[P~#>?_6{$Eǻz5K>~':o @PJ`}x>|=,q\Px7&%G &J q32 |>^ ضI`*>`7OۛD#`O:nE8.0:?S@$Q .@@,-h;4ca/T"_=WT<~{5^0H=vt<].m~(Re,v o|rU/Ǫ.ӪoNtu:x5HTޠ/l~)J=ԍ@r|uz>~54V,_\|ZpR4[[R7, _I {w|Fp5 uq4opUJ+;7-DJd]w_nDY>.ߥ`}5ΠN(mFH-=Mu3w]?_pA=BY>zue]!,/t0jzp?;)ew|dEjK",BGz@^et,}߮tB  TCAO ,U& dh//hF"aW\|aL(dWQf)_}rxpYx?X̏zMg!>[wF0 d>c, >FlPw}3bސ6:XNV" '|(yu)_t4HbP}^/Ӣ$>o׷\0:g☀io P?ct ^9á#LA!L0ai/m-p=v@z6`mۃt^09kp/98MpA@""M{ >!"_1zܻcp߮ +Ϣ=`PR|68" 6 I/$&ꞧ&ll_oP  8B;<{_C.#% ,K֖:I|r-J%}AeH@2?rq^*i>oY`f[q! drvAHTtϧJ,^ G:zG%~|yeBD@.B/tvĻt˺>@(A^L^NXzݮ Nq!":fLĉc7ߌ8$H$grsǙo@2j>)'t,* 5(D B<=b;翷m;'wx>0@{J@]6P$2Jt%AěeiaĢI(ynWP<, RPG+| 'T (qQ@b9l|5(Dpy|].P;v6GN >a ~j@ s1lx?&4 '4ʋj}ˊ<~{}>HNv[2o' tTfU|~)D< c`%@1{ˆ,P~> i?O3B*ñ)|(Dn^z  >ߟְ \k>G<?F2c#&HX_!t|]ƛeq,'Z^`aت|fLdXI=t`6B`|b@wHu6qO# ?IJ@)/,QO" ~]ƛeq([,GܫXt׋8j0ۻ{.E8>:lZeY֟8'QJ">/x;?m RU>x+& '=y KP X=d>W^#>|dϣH Q2P/R@|_S8>=y;s(Um;,#?@5pq.~T$S, 7Ckmxc<>y831)dҾt >W|^ R_#G.f@ˎOwxs)kY$ׯ7ïk>)""scrT ]0w&<`̇S_Z嘰rQ?BI>?# c5>SWm?`˟AFpV"&fpDtY@~Pxq>{g~Z|24ʁ`/U؛>hةX^"j& 6{j&!~8 z.XWY| !!KumǾ@rU8 摡RpJpy 5}  6ۼ!h`B_}|f}Й<%y`/ դLOr9TD0Cg<@Ǖ_l>@-bi4; pK e[~Hm~E0ǿa#>@'xvoKLD6_@MtRCD - PTAT/Ya(E sݡsOH A(0P|ώG6!@T~`R="_LOea ` EݡP0e`,dF@Ux)-. Xe?FBoD@!+ڛSVBY|s g#b2T>'IFI2@/ nx_a0?;Xy>=hDe@rR2!d`U䎄emۥ S@,~@`YԷ |0?>FU>xB D-|S|_ U$>_z2Q\>X?"p` λvwT@p!t =xr.<UrcgКy{;A<s$I lJU 6D`; }թ"AX3xT>gjHS `R_d}(nTD?O |_,G+($%λ5/o<.͛d0e&O5>]U!LaN2C|C@`N~m{V  F)xپJfvw<zGZy&>r縐; Ԗ2une ?R@*C. q #{f%wﭷL E [[8 !㇙CO{@ f>o#X6dDtF^@}@ >v5lY eU7|Ϋ>(vA SHv~ ֱ nL=]X"=X |ѩ]ò5G7Q,`zx=T`޺|Pg^>@*@kMCrp0= (@j\jor`)߈aw*dW3!DĈ,|X_X@}^@>o0Yo)* 5 _v8S, @yuE0}}ڞ/*ߏgw:fw -]gk|>O|Hݞxciz /׿|Ct3 8wr>/ApP :/>/Jtp9?DDe|G^OTf7ppׯpQIg>rZ=}vS?x& !|;Gjb Sd\/|WHVhM Ӄ@i|rjM7^>nJ: 2+aXb9?0l~X,-LĿr{nWuJ}: 6L.JcNHlmtHHw=@F9zqy6kd0}Ÿ҈Q5'uipGɶ ^F_o݁Kd I@`9h k̄A% E>`GENW!vt4,'y]wÃNNm%awq _:%8WCp~ [dϫMeg|mt z) `>cJnׯ~~{M8p)}5,3[sHmDxS B|}%B p(_=K]^77}0>~3{TK3 D]w?z[a@Y 0c{jF O}mkKj 6 7} b E坎Oa D>vqCG/w]tZh^&&>b7cA |z~!v00L wcocӓ(7cW ߅0Lq//0[xB^ ػ+wMpI4ox99ϙ5M(snJEBa>@!~ @'ۣ~t2>/M6TX__0F{:s*y4V: s:<;G{]>=; @ \Pp0pď/d ilzXٿ?~te} ?Hffc>cM0t-8vcĚ.&qN\n$!=jbX6h0%RBwh;jże(D%*^\V@uA.(, 4/h˝2V-P(>/Y>q~߭zDp0xH*0~h5^U))ۧhzifrWE?GO; }9\#j>.ʎDs `Y>y~ W|Tgd x#|?G^tX}J)~_ g Yŗitr3|doA|K>E=~9_8ޏs\8x:sjO }9É賲H"=ƍ VY =<l> jB:_ J3e>bNgk3%0E?k:@p hG(.wr:`9@}P|GI3<8)Ltm¨g Qꄐ+`|+p@;C.QC lhӸ4<[/", p)# e|j|5BHF|q%z0T~=Zb8 |+즅t!.M NNݸI+&*p yu쨹Q *Ǭ&x.5^w%!`WvT] @bP(@Lb D|\GAR6`)!}1#_hp Q.PM,"tbC聨Gii~癯BCjNҗ.@ iD1H@ѿ_ @ QpЬ`G'aw|9o@ 0'o" ]=C@{֎lpQGLz <|@~<9px@1J];zzR0|?ة!X㦝~&\1J ,o7i&)`lĚnm H Z"9C"w`,zpY`D=z|-V rSJRߌq/DZKԀ&B4*r3d4S2i hzjP|7"]N(>c[xz`>iM'*?JwEAs=  ?/Z^@*%xN@|ޑq\092m@#8l %ǃ'~x3n褑|S87:\\+#qܡJ|X> $ s E2>^^CE,G pd!;BB9]q_`hsC<ta@?/`5Zxx$^n>Fa0 b }n{R?*x(n8þMp$im)k&hէ긏::dG4&RboxB#]bn<!} w"ay'_n`<\9D9-67K~|'O'F'o`܁*+h1+01:W-2/j@b+$>wD PLvk3m ,hhoP.G", "_Z_Gட 's>.V(A|\`G2x7{ t7DpawȔ1;PT :U ɖg>|[~'D)͓A>̯`ܞ2E0 X g)OSeCB|w}K˔1LN<~$>\e ~`[zSBn>*P'P]r8|2Hb|總>;Zz :@pKt<2ޱ2M?h=7 Fx]'M,h8Q|wYAPlJ|U>ɃV:|Y|LlY`#uy<|+>@16G'6 ^Yu_|q~vwH-rm?]7߯Rz`*({*K%U>MK䐲|6 )(3_5>,o`=6Si1zV?_ޕuGeF 6ēE{Rؿ L'"m> U|6= =8%}kP \ <;vݨv>>YQwSnn$(ϊkƼ`X ~>axڟcT 3_ࡡ?@0/ @QVt)~ *o)ޮ1o8xa8vG'A@0V dO8{j:yjx?"X.% hW @>|>x~@2%()# >5NB|B$æ  @{`ߧ|^K!ˑ(֋2 BUM#<7H쯂౏ǣ2}&@yv (?F}^$|=8)# o{Vy=(3(aQpP|oَހE ~^!Lt#sm50,D<;6@VƐZ7~<fiXD^bsH{ a2Gb{'#wS?F|~pUX/_gUJâ8t7$Suc~԰$ |>0 ` \x쏿_o\8 ΰ > X6N ,ݓH>>@h$zB˜ZI8ߣxrw~޽K|Y7XGuQ]O1`h~2?<|)a 5#> _m`|q8 %""%)Rpa 90UK>@!@%|HXNQ<*q^?8spCۢ.۲KВrPjj}o_8I#~\.)۟Vukp(0z7E3p Kb KPcF~WTH?C (DG7z z*4-[&-0Iēdk0苈;Iۢ.:EZ 7TOx8r_a2wv >#n0 ?FX@T| H>@,8ԒGhHӓ7#8{A>Df}ȇ@@. 9mow0g`>dsW/"EƷe]Ǘ^Z̰ߟ}_h΋ E>Y@b?p0u]dm4@DFdC{K璉/@Ȍ|Kz | @W9msXJ`R:f<*K񞈏˺'zirOXvwFKͪ7"'H49Ze7d8/@@XoK>0 p$ aITQo#/*@cz"┝ A?[&EO_;;zECM`>@=4 qww ܫ `*͛wF^7 &qw3D?x"md&f WQ[_x?LfM}Km-: `X]^oY|I$~>@բI`of\TQk߯7`7mu &Dװf˂Y3l>?z`|FG, bA 0kh0.Yk~_ڭko&iϤIl+=cROU>@%mbD{*`(ˊ >2~b, t4¯2.6-EMݺ _n=%/OAju>@6Ef[Fy4~!7z p!m|"@1,|*#J>EI)x]qȕ @g›[ [ݾ _zD7iI+=P|qh-cܘl:AxQitN{@^ ߘw7m j!]H1.*C fd\PŽ ڌOBc,ϳha_ضcjI/ކ.hqFԥzdĸs`";$ ale4q> M`)mM<ξk#a`XH |lQ|g|%^L}k}qՃxEhQw(.BۣE(Ns֖U]į }0oҒ FnIuS׳dOA=f.6` hFhWw|V +Hh@F}ݕo0x8kyhB#b~}}f\(1 1/o|u oaLZ ۲p8)Fbi՝[B;Iw@hk֬4 6yUG|a#0Q ux^t8gH:R H<pv?O#|΋^"G}fF W@[z'E%iy*_ALJob؅nhwo8ƻ{]p>}k 7H~h8fT 4 W+n[W(!)H-5qA/8[_Y_ S}pN=X?v>sT2ij^?zC`>]RdpI<a [shsd6MoM8qrC0RY> FHVn-6#/?mvVM~r=?0 ]>@^@ 3|5a>4_šB֑%U`<^]NN OXޣKy"=1"Amjr%=HsqSO7. .@ *кTmA|uI _0#g?^>fC*>#'@0s y,qlցC8^d'zpyB3F?xvBo @ HytĻԧkZ}N=W1`>C4~ xVFG%6kZ B} })^F:>AKs(yIE1>eU=VfQa*s<{4&Dh|;Q=s^@lws0bxzKʭ~舾8sQ Tzi7%7Rhߍ7 | hl>=wh'z/0d~V Lk|>}TXzlo;e~)$ 6vik}-G`ᒞGpP}.|z:~oߺg8q/P@[W@:H(i|}9΍k' /FCF5Sֹ`ur^O1 6V@$5?8=Ece杘]b9Z KKz.W1Tr,=&hpg[=^,Nh[ O5bcO. RpOJ6G,`Xx<#|_J_a4w TB{hb[1c?l=o;o욈{ձXAbC3[WQ俫/{z>+z]Uѣ+ R# =GG]GBBW)?C\c{y=~?nuE!#ֆʬP෵]}Z4аsgH[a'<qf7j'r>6=_.ree ^˔5{Eu##>oT €Oe: ׳ۀ @`mwxHQ|j1Ԕ綈a=^>xwHmt7&Sz;SD*`u]|)H;}< Pc_n"o2Y1Ä6bp{X,C/Ah|i|/W{4`øs3 P᪨VCE[_o3'I#`mL1 =Q!oaƽ2*Tb9_o؎[$h0vQSG7 Ꮚ}M),rzr#mTnrM &8ZXtNm/@YtIcpC| `n~+adu> TCoD\,Y \f ®:;rt88$_%R!8GIS hyZ%M*+S0&߾ hN^Q?7~vY?`2ׯW (&Yxjn"7gp0=_[?_ZW~/Bu}AW6wo.;K#> }7hW*18c>+_/A/+ܿ$ w mixE)"i^tK""r қ@m<+_*UZ>t #`PKRY= Gjxu ~ XӤrcH4+ n?9ƱBzs&?@>6|?}D B0.6#<=*P3m*]A`#{2%_EY9x tH}*=u%7P'~$.u!.x| vi6mv`bBEmO!!t-jo}n8,u"h¹=@Qł 0Ԁiqd<zƑ,`c >k([>X A[ps q>\TO^vZK^T)Na(GW)/v=l|q(,2e u0ۭ6WfUrG?'ѭ[Yg}?~*Bf>6Z/`mK`l (q_R@`B^XKuujp.jāJA- e,+jܚjR :gQ+ x; 6 Tn5L>-KM5!/242 "24poP>-zsUb~'@cnyr|vl/`xD q P)] ! XJg pnSӘuFpS0 `eM(7oq'}}m*̯^DOzr$d?q{8 !|SOr`t̰EK]`u[|C>GNoH<^],~?Gi9pka8ƛۄ*Ë8 XxPR7M02rnhS6Xݭ9ooZtuKU;!dep٣z8m!6p|x?Z>CcC(_969(px@d ʱ_.y-\V>MW74+9+._'җ&Hҋk7j7ӫ7a|BZc~!>~?! [ 'V~|ɬs9> Fۄw״oG2LiM8qpJ0[qM_0]۸=]It=+t"ysjMF_Lm6Bқ2Ja`m>@?4*v٦ NUj5!@!{?Zdc8"xNrg ]݂!B?w!_'o<ڜה0[;|)ـ@co s .nNb|0eg=a\yzo||p=:kEgc\r6+)x==~jhcNg5}%T@F"3*N)g *p|Sp8|}`< uC)_̜.\՟ {6}ZWk_r^wO.b DG v P}lUB*ho'~KI-ă'PA/-悛9;|EXA~v>}Cs\p)dk>zxHbTQG`?׉^ZDZ" %ہ]G|Tp *P4K!=Q$#L1@ =\g?>|xz;Ђ܏bV` =yf{0@=^n /9bX>hPBL9]#<Š_\^AN Cx9/o=_+ Gp( cx?ImhV8e(U[><%<>@t ^Q"pe?zѲcc٧oj8ɮ"H80bxmY.tP*ϱSnQ?')/_ό%v˱x@,w|ǰg:~A`^)r#N7CogOԆ/8^D6>-K^=֗|[櫋mն S nHG|z8 PV^8kɎz||E䫋mri]i?W{wz` 遹{Hd oytMN yW;k(Db8W~^055 > >9U/`oxg\ Soշ7|u{,TBȏ7V߈K{H4RPB@1! =W.T|7ĚkƵ=mb_#x+S7Gu֢ F! ,! }@>ׯ Z|ި`?=`>Wۼ'VJT)֖'?P8hk^>@b?hޛ˝`jO?˫%_^~ '߾-BZ(N ^>@3Vvk~trJLno d|!ic?.j P4/>s;8TFaA%&$N dѺ@W0r}0>@\|3>loMAǜa*Aw JJXʈ~Sg|uZkGU ü1h<=Pkbp b١p]RyxTw_v(r*~bEqv9*E@#6 I >ֳ(rT@?K-#aVxkl||zc88ޘ4jÞQM iK]2zKxrةQ>@'jp@?%>ȂA8݋xY~t?3#W?n1{~}j n^ɟQ~EQ]GՖxt#ޘHkD ջcx?>UQi v;^ >@zOŎ,xؐ&f7p >:(ePIiZX[ףt\kh7X0ڳc^s[[/@/$oV{zckfR,z| h\6pG_&x pH zDy5>@?޿Xr0i)5,t!Q1 1P>OG.$Oo3=&Ō2ddwOxH#sݧ7jzL ze圆 v0a>Ln.pm[;|GzG׺p51Dpʧݦ@>D|r}IF@OsGVB Ts| մpi P_A3l_ndaΗF8y "wߣh <02׋?`ۈ ^'!+a`(>نbG%Hwp@>@KIWv𕘟@J KTo3y@|Pcdd;oh6k\M@lهp!]Ai;Pd^?O8GA=0Ufp \`6bS6Bi|,zG|>®/Fu?%`X;9Z:إ `d_o0LS6NlUFb nz|ջ~VA nDl~|-z`SbzxS+.l~==b8 &آZ ^`oGM._WAC&äBް~Ks=zO'ۍcƻso4vEv4 @h>cL^_{2Xbrd9X9m8ߴ{M}{6azb0]+KOs99/\~Cs >>>@_H$P th!>~E4E':Wa|J> /휠VqfouWwzuFC; +R/r?@͗&s<>U &i:hn{l<_XS7RKJ};W|s3un)+7W9>~~ '\)xןq[)&i|@gH+ '?p3 j jh`N`oj)"A^WZ9 }P/А $E[P|f|}:m0SP 4+k&l@|7|;h?j_mZ)G\Ԯ*? B'څE@X{Úp=|Rݝ|lA@>;f|un6СIVvWٴ1B6 J3/\u M?1yAC~Elpm`<`Qfm0m~;zմz D}3N M (^-Q?0@8:z%+rMk.7E\w1uQZ*%pNv;baC@hozA3^~nshg{/f5;0y˦A&cLRC9ônεH`ֵs|51hP g_E=L,PU,_7^3vf~CC{ly;T8v smzӻ$/D'G< O$cIZ^9} F>@ /*X>%?| w8f)+"s/{g _B G?ܐ? >5gj<6^nѓztp4 ^!]%] Wnq/.Y39x@7&%EO{`De*~@z`>@a5r3=v\S7Bp!'y=;#ТWT6ڮ=~6x=Aʌ \r=c3ٞ/bw7p0>^:sϥ,Q@St?VZ/vx\X~Pt5>J8^S>@ǻo8J^T寵65=ihgWl#zƖW 6Ŏ j˞ i4S_̏ FTn;`azM>o]pL Ҙ`|5\|j@_f:glٛӚzM^Ӧ|+__zƖN~1[-zhp|$L' |6|Ź[P2 [JІ-=@6ȩF]6L.@Χnl+K^?4Łz477&{#K̛tm+^n. @1y2Qe˓qz=sO] 8j;D]FovZ+7my>pa?W{.`GA2Hu>} .>2myXk!:ig1Gmq'=!Ml{/P45߬vkm|Nu`1{&X:奛a;rVq~E>:.y\OaC~vzkn5` sxc>1%  ziO?jG 1_WNMcS??U"-8P.4)زfعW&\\m[}@``8"|3ZUҖkN&}y1Ivm^C ^?z>>j짰`ѿ:M,|sN6 *3Ct>%JLc>V|Rؼ/H?v/~z"t 43O ƿaSlfs8W/d 8@+&I i^6o^_ыEz+:վ5߳>@ `Zɟ:]4*SG#zlb^C1|P!YZxnT qgM N$Rܪ}Q /!TY:5BBȤ>yv?dz) ƚ 1 rpo؆O0GPgBT(`]Hp7;챃#:NɈh\Q $k@hM%ԑ`سc_tt[\  !3 E_wJp[G#ȠY=E_ vi[ p?@*p^6_|hףA@@)B%n 2H v>A)fFRvXQ{ +U'0ƥώM9ˎ7V/2gks3?gFFVOm3`3~M 1Rl)k " ]:܍ߒj™B/.1Z0x`< o@Ϗ }slȆSV. . @moo5bg A|?tLt,ZQd8zh;i572C @H3Q t'z@XB `, P\0` Vv$XM~9V׆ah_E^+@m :~۰^r_k-Uw42'GXQ^~?z|,q2LǼD'_9S>@ T_AC ؔR*38X?P8-|y~ŋ `?` @/Gs +hx7V>/W{}_] 'TW[W< ;0hc?[1wZ78>S[Alg~l}xQ^+`3C594ʪ<ʸ`ҟJ@Mf/0 6^clоp>VU5S>/W^#||ɣ.jcVOͧS+tѪ*Y7g0O`>.fg DGLU K-~:ws6xV}ӘJ*p<MWL]ExgU/@O/7חmbxV*eS, T#|z1T~|5[Nsǽ dDH_bʶ񅷓|%??PK?;?^[U9>lȖ=Y|h 3^zl08_~z_%#h|^~~~y{OaRo{3ތ}p>ݸAe5yNot*?HoN<,˕Y+f&@l<(@>jL?xB7w\: ۲LOϮL{Q(}ƢwMoڴ1Y; [Jn؛v,)@H?h1ZǙ#ȟ^rz`Ҫh`e0Q.pſWO=6m'V:Zލ=]&ތx<~@b E&9*RɎ2ۗ[il{> `.x`AkyB'bS\-cGnvެ5Ӹs>c_YfZp4xXQ|hP_U:zrϙۡ<}_̀ ov5myf{_| t^K!xw2tR |Tl ߕrYφ"05>i]8s>c@0\?Is ;_C OycûNKq&(ˋ `O,hZ_^a?.1Wpos_x<}SZο'#P>}<^/Pa |V_(: 7(~>G_u>@<]f-K@, `ɑ HM~  N[ru/,F9T χ1=nYSx|/f>~p/'`21]Џ Ц]K~J\;6:0؍0wCW|G)MSpTZTCŸU r߲В)3iADqUgD@87c//`);kݴ~-"x郱=l0>5(ag0 q3z8F|7;9 9|Ŭxc |f a SU 8_ JڪPnв]5ڕ8/+xG]@t B{3u%TZBgm.:~ JL+zF `4%$ ~v(0CpPa NgGF{/FT i|pʠX>z@a>Y8]ſ7j(6 -۶]6 06p *딛 O ֗Z(7jȤi9-z 8회k V8a/x/кCG8mau,~G,`P@S*A L{ 1] x3( (W$ oв9OKW |ݕ@^M />sm^~n=5p CIטgpйvno#{pp/ЖlNorp,:t|b`ԨWp< h )};wІm( j,jiG(?b=@3-n>O\wob 篥6J-~džhtu3wg; ' g˧{Ц[~(@w9__TÈkQ:h@P[j+<jCv9u[l&0 P'#0$YE|A/F $> V [n;_?> U\ f 5y0ۼ~vC@ ěMuBAk]m\Ҍe{)YlϥЃ>od7ޡ?RhX}8' ||6W8~DAVУ=rX;ߧ`Zyk|".aP =g7E6ͦ7߶{wtg|70|yw])k6@ly?gs>Boof/)N3=p?z~Zp.Bg6eDʘm hgG~? wߊ!Q 5I(G B \BY}Q|V~p z/J(Næg)tl0$sבdH(vR8ggkA1GJ3{J:>K80`Z`L^K^Qf.&>V77bÜLm~fgɐ“F"$,ȭUVNnؖOf('}bS@q4\BM>`qaH0e44ߝlhyк̏0s6|0/PhF >'{6Y눸OSLЌ㵈~gLm-KJdTq[>nih*x@buB }nb୦>6A"y/:+!l>]j00z#JfP^O///bS|Z{'CaFtQ"T"!C@f *#yIb@'PBr< y.d#>ً%8BL‰|ʏ|bAl heymx=z{]ޏu:[o_Ir*"٣M~#6QuEy؅ ~-sS2$S nz'|N -\e>i~?)4Q|\/@>,mejXaH{Ԃљ&N"V`Ozr})^$McT;1n:xoN?Z]JuX<ގIh@vʯbYS>@EA> ($ ~s2m+u|Q0ņ/RX;Sf8*^(0= 4z/t0:vOs2vO)_:yɷrkxŦa4nv]nR,DŏpK%sa Tpa-i+?@# %op@/_i ]S-ű)y : U& \˟ 0J@=@ 0qYF;'`P2^o#z?~hJ,'vv~,j[D,-=-\T^[egB@02>AރtbӕgT7z 8OXG˪ tWVie|WX>wq|,#sis}dHj8>R~/ e^,ӰU3y^=+0^\ȦT_p;r@^ʹuw UE)@Z=[g6 >ΕA'hz,||l ~EHO7?5YOܟo{V0>2}o25Dи]O4PuH x}'˫?Zܯp^@s 1T^KS>@?@ᖌ4ԉ S9_ZMH|_ 4~:L_Zm4?y%~πq 8P>@_{TME9_Q/8oЖZ*a^'Jy5϶';l0f9\>.J^ӡpLuLS04 >@U߰{\|(hƷO>T=|pJve\ۥE]u1>g^-N%~%3K@퀏U.yϷ-G wn8Ќo1~ı~0aOfoqc?^ Hu^=A/g->@] ~?O&3C`_#}nh9$yzBy; lgN Q=u>Cl(ac͓ѿ?iG[x>-@?%AXݧ.,w0x gەfҥ8'c][|x+ohy1w-p;K0(p=>6$7ڀ{glviF`v1>]; 97,(m~s,u@Bm׫k~y"|@5<` \`^N4'yN{#~px,A-@/h{qUa;xz{nKqC0F5cc dž#,l\= _tTtO00[GP!z&U'M@u1?_`| O0c=.{9~RϞ4ygԈBW,z_^I|qd;%8T...v#i~F@wwG%a%K ? G8,/\cX`rS]WU;<`l6v&x?zK/}XtOl^:=J2-Ӎ>>b<5'cO%O/qojrnwsF`/hо68Xr}czܥ8̿ӳ|/|'~aȭ7|ˋr #.4͓^q C R97iֳuStz=^@h#;kqxxƏݍfm [P[olGK38r ]E>'.FUSo5|R/!|aj z @n_WpÍ]g-IMzsƿׯ|mg8"n+Ñ;~6uKI< ,/>@ss.]@G>oɩzG|$)Kv `ݍv5&m7~|y֦ɉPk|(]8~?tgyljgضCg/(|pU9 hl5~oWй>FO@D׺qL>?n{v1HAS׿5qKĻ#7_Ո͜ ۍW|#8;;kxww[7~y7ONf"E^튦i!}xYFUUs,u5Up{20 ( d]EuZ;QWo:.]<-ov5kt j'ZqիG9|фa!ea4dх]dzF+(,*-Ef8WͿq{ro̍4MDxf[T'LnhhWz0z)&,zRj6̓ jǘ#8 u\Y'~Iَ>ksA׉ @ypy/8a&R 44#FCyM|i[X{F|> 7i(bIh83Q<ۆ7xY 4G}=m[6G - ;l|b,M 'ǻ>s@ku-˭J>v{$d8\;|s^+0a)„dҺ`8qo`D5xy`lGմlwB_o~隒>LRUаGr'ɫ^`^,%|LH`Y>m|d_$`$HF8x<Њj$ޮQ[=3kBJ!@#H˳6*DhIkgh>SQ>s)ʉ#>t6I $=U[ j0.f ;[ #!zg>Pw66xg5kU(qgҹ$Akmųs'hRY{-v?o8mQ9Bg>#{c@G|ܾݰIfw]>@IzC ^q;|p.oX`GѩVoЗ#gW[پS(b`$":Kz/&K.9c c?%?owk8&⏗mmÿN1`6g@WS2T|F{O_H Z߹FAu;F~ѣx QvYuSj.{nR?o,gl>cc=?E7m6LzS.[3 z4¬H$z?:Q@0' \p/S=dzkFxP-)=6I2 m2fa#^Fs><0~S[Oq>XWܟ}Fj?؝z=ԅ#[||+?\a!0b=ˍު@4_@pTOX}F.جٶZzǶ 8%~qieחr0[_ߧCx`bWckg컡@~0^Cnc˲*c=G^vTjkS͕/V`/|DHjpۦS8yid;7fk FMy5~?ƤT=6'=v)3Wľ0ד< 7z?j14o=4!n$ V(_/0-ft;ѹy10s4,ߧCzBƥޟ䃼q)ݫVR^O>0{>z^V5zix|v pҰBT2g[i8 x?)ah"t[(q>ݱZo9"38ИOQ>q@g 7{iCv̹θFQ>sۢ|f8E])||}f<~]?,kY/r=t[@۞kE?pH5T|f8 YXnDײXk$8`z=s9t؟wﲧ*<Tll>Ϧ XZz _l6Bq<[<K+5Ոߚ]J~՛6Nm2ͰMH[oX^ !{>EWyV> Z >{Q> ]͐&` m2>g뫯/-|f˞8` B. vM48[߸2>s=lJHC L0H}>~.<>VW-|)v>f̉Ќ_ųGh n5Q)-[{ F83W{YmwK8^[ڏ1cpwQ.mG6! @@/+]߻)BG-WY, >@"B͒Ûx4p8d̻ ؝ޒ=Ftſ~eߴE|:=v5_!^~cxGTюxys3Y`SmY> ^0M7|hd8y M\>&1?xmcJ18ȚPw&1jv1E^b<lY[Z"U0{+߳wl;*0_)1j5|TxX.(:C  VCwDLE @>A.rQ}Rc}'~=;8l;GyxCT8aSeΦr`M٣~JuR1^ySK D*VV:dzFU^|^/^цV|k{CiRSb΄Gx%rC| o; F S+ Pb7;A~LGv|0Ð!.iv݁NO&~Mst~@!+{'~Jr 8QJ遚1T+uM%^ɼyTʍm̓b'wʖO|Wl zK`G~taG]bbCҟb7^qE$RƯ5ݐ;73\z-/Mk@{ lױkaڬ)~ d @Aӹr"+LTF>>%L4c:%6n r@Wx77~_s?߯XN2ߧ7&N>pX˿{^m{>uLW]>n8c,]IGfP9.np6wfan'jk|6~mjz-|QZ Ewct>~߳mǶ=k.nu0{7Rx#`uΪwuG3Kk#tixcn+406ֆrNWx+Vm׻kC~46ÉquuuR{ xOiW;[hnnꔫ}vn[QUߡoD~ω7t` V cK^Z'ūf/LVﲏ]#t4!Q$'\4ؓ &?~>@[ڱT5}V}|ip <c"$@V<Š?]i\k;Xxq}F ?vl?#:3}1`=zxqaӀv`%L!-km kNRGmv/5芀& jv'8Xu:PWZK-^>k5ؾ[ƴ#d>V ~1H%xl_Ck<~o=Gmt/6ePaC\:uT`+?P Fm4\ lhlo;r-xԼ{=9aVU^_&oK|ցlVC[|fcYjŃ#@dTS$yx;0s8|$Fm mr3bP ~p❳+-U;ס'MmYqa4q,@hfi_!.\>k +,HD+8a @ rxu3p\ݼVp@j8+XbZn zmLeَ$j @pv*΋]CE'ns+ P߶i  1CQ:nqhn/f5y]84ag@4r?fւ60|}(@^??z.dzYį˛8"mW_v4מ/1A le& t4X ]R}{Q~ [F}~z4|a86$g]T|^άCWMв_-&$&l r߮XABOP>~xM[tDGi܃A(mGCvad,8Wi'9 yg]s@6١c~G;0!Nsދݟ,<s`gh1uI]e>LF8~_@;;d>yXa&> z`cVbh*qp: Nle%(e.^bQ-$xx/t~jo:tmYz=cɗo `L=4@Z c SE|kEcp<\,oī5ޭ6FڀOaNl'^N. ]p).w h^s[Srf~~t3L]lzƖ|yxl12sK1].4S?p%}X>@yS8HmQ>@cO[ZWk;/1` !\l/9j;v]ԯh!@2^Ku|$p<<)W[|&q"6lr=c-z<~qTOl#dp<dz͍zz؝>(B ۗN3K~xڼt'.!ۉG|@ %9xq`S<WP ^=~||Ӂm|?P >e?ͬLv;x003 ^{m5ǿ &DTǦ@d%p[Fx| Ȓ,hWW^+h˗ީo_t< `h J6ikyKVmV8 Y,щM|l\ޙ_R6hk+&n8fe*q$:L{s Rxؽ?F,r-͋O nn"VLEVe^|p?B@~/K?{g:..u #Kck%քq[Ino~:OqH8Aܷ۟ćjrU:Sv7\/ϾtUŒ[,qֲZ qJZ ͎y`ՑڎFٱbE@3`Z{Tk\Ku=Y#mw~oҔ^:/"۟Y^qTSQqXN7}%0 c.'; Ph-`}`(F&;&ZzKXRxP`'׫Lkv^wk^Sn^ro QY$lZũsU^^>|$ $"㛚:>n|v*;@,aMIijp-`CHHM&p+c 0xm^ek\ ` :sz-sVEAS "|~܍0~<[cjS#@@H5FJ^Cr7l  פlZY4<@;yo,NtljF耩;05K4"ĄEhůk||D)P|[/ }&^ܴDwC}0}^& ?.SM-`^ YvVߑ|tuz` oK5&wdCa<~F 4Gt8_/f0sZG{=d Np||[/zzZ,|z @PH`iu`0(QNj2 mK"dIo-JLj1+^qu\oZdr&?#V(JzyWݺHOsΗ iڡv,:] V|O`_P'@_xxuyڽ^'pkr*0ǃ}_O"0iq$oG7 -oxD JP܎¥"Rv;};T|to''Y5`FP<~&a|` X⃂qQcGð۠_O~@Q ꓿zumG3f @pfDmѫeWX?0D $=5| вqYvGo%Ŭ(Q-;^zsz9͓+Jl~7pck`OߠU `: X>^҅44wa$nˡ_Cw Š܂87gx˷\ޛlW"DκTP+r>5=7/q紿chDp,Xۖc2jXxL;^ko0 gO_ w^;0f{inxw7O0qhyr&ѷ{ӬG3ֻ4K\n뭆!) 0;ʃ[_ݫ$/:!2 N)&}p >EO#;a(8-B"}s>BҌo +4<?bP wmףز1HƬG 64_%|)/ς! Vpf.$z=;OHx8 ]`@wZ+gS Z'i@}~d=*^&$`0ɶ /p3~XZs>0ݫ0=.'"ڄ{tbCtԷ@8\ Qu?@@-'vh}:dp-?|kv![>jSUn+n!EO-p|:ግ!P~T(KVeMp:LWi-+Z:5\SE&\8 br@t{ \)=|ד.$r}Ycѧ5}+d<^I"W==|f 䯱 Aa3N\RGO8M:Ī&QCD.Hh/7px-(a!HI1"t@>q:]Hd#ƢOkmp7FfrV d<~hO_c_(,waܤ&nC4d!eprcUH?ԟ؋CLh?14aB5kO/M1aCxa&^"GA ?nT:ބO.XGI pt-Y~6l{4Mg3PG@1<[BܶwG | WZng"P'$dqq0[ya'_A o|Xzl4Ž'{ < s&>ҫ#> tW@态pXCvMi֔.G..%,2 ttU:m$zjK*|X_w-IF8hnY#^2A,VdbK : 1ZG"@mo,8cVyQn קO?VyGEZuCq|\u9cB 48nM;aް;+=E$+m&@Q Pk{pfes wmP_?߁Oojkj!M억˃&nG@iDŽ ⪓X^7@NиA-z:E/MOUڽW$D`/#R%~r6[%Ƚ<#I!:@u_M+oM|D@d /~~4Ɔf! c(s@?xMq4包ㅎoE 0{[Txc~U.~g@-bڤA!DpyZebn_^ F }jUi4t&7):荱/+q`5r4E!8}|?MUoSjm28pߦ@۱F8?J'Xٯw Cw7o&!ⱱ?a/8`X;_M E@p2,=:F==&@ai!0M`eǮ11 W 'If0Y*12n?G 5>"3?`R׏V󴿏S~1,k¤7B,eAl+TC KՉ"@pB]d>9RZ)@ l{UC( NAw ~yPA7YXB0}5f|}>WؿXz~ _? nbޅ߀?0x}N1 !FMޔlt?gcB܀(`Pݘ2 6`5>㰣V!P 鸪uܙY̗Zn'F=z7C .S!1;=?4b@}c m _^~n=>c_[Zϯw0l z_/Q?~iZYxR vqg֗O4Ơ;!Y^Eon B8[&Si2&@Qh[)@݃mcv] ꠣUZp_P,Ap3=7TP;_MWZoOٸXTm`@/ߵ?@ߎp!;aٟ_s˳-F.#0Mp#YfM>BeH BY簸ׅܜPOk-@EF̒7!~ <pjގ7XWZoO_P kwh~h<oh:de2P0_',Ρ{Îlp@5P9K=}{{݆Ab#+G/E6 P`~mLA#_aߺm6zwl<0 տ@r?Ȏ?к~0Ҥʴa|VH#Oc@W3Ƕݳ^Ȃ!ԇlo@\]0P ư\IY_oK:;o=h_IȎ?}wo-q߆-DWOs_dub:7Z/ս8q>H蓼 c4@}dj(ǼwG>{c6MKaodR "5Pj#?20p8hUkJSqVv'! ]`:S. V{0|tBC8bzzn;piZ?pwd^ D6afj߁i3@VeZ* t>pFzi_-I[7 n 6:hʍoŕ0@Q2H* ioǓi]ҝTP+KN`N/:!(4E!=6se\_D;nIXc1k?k UMp--Ή2xSM2 1ay8n\f9om8 J`*<-38+iUfY3CgCb~T:#s=HGS4Rd]A_OǤM៕Xvʎ]ο%iVю'bc ޢVuȍ~Rk%\i> 8EpUUa1EѸ,ay"nNJQI p$=4[c &:*Ժ?^s"ItӮS`Z~}W %ŵ;t?MD lˏ;!tlsӺwʢ F%-~6NP&@ҋP@OQ?RףmNS |u1~tzSOD/NEhJ…ex$ɍJk굅q}uS`ǀ@r h]G-a@u%W I1 *cܷ&*_&+(2xqhһ=xP`z^"c} ܸ?Lz[?@ұGԯautp]Ի\M rֳZ~|ה'OOO3B.mBT!A #k 4*h:)t$&N`YBotbGۤ36Dɤ M)߿st6tÏNǫGw-=jtzN@Y^Ghnܿj쏣*;lhl-?^G<&ו!kTw:@aOpU-k `LG( ~R-{}zm3OzdTACGZ+Wf@Cb\jp91 Xaul_Y_DP5Lܒƒv,̽?P}[ov01qA-q/Onz?1(M^57|Z=4MGYe .__Ƹ`zW65Lqb,S?Whh*{cYnEa6_$@p`I;Y 㖩vcw://0P#1 ؑ_2e߻b6tZ'@fx z|jP pY?q b_smi-w:u~7ʪZ`dg8뒼88IY:rsv& ~'E1vv0 ݉yowzۦ ,Pe)10<5IY"eiaZ^1YWZvHsmo>w^eՍ^[ tWh8 xEeث^;7:6 ׉|m@y澚ߋo7 c~ޯy>%n`wS?{&mtc$@B @?@u+eKEmI{ ZW߀9?pkg0n,pjU@˹SeO)yY'{;-~-_IePM+?{,^Ou1Z nUGj%%s2%C`0,,;|4{WQ{_d`' :60%Gk ʻeO4G;hn;j{%s`N38g)`IY>!9>Zt:.Qw:x=qIo,ݝ}f;Oў<W+@{C> zB9ʫgHc^/=[ox#jo4p}Gx<^Q=I )#/ّPPm.#}ј_kM7Fܬ TSGW8 +7nm,c D HDt4š;}J|O卺&5Iy3\\y`u?b? )NQ1s ?7ĝ ?dv(-ip}Xc5Ʀ5@=e=4̣t@c]aQg,r, Fmn|ܨl& JM$@tޏ!O2냋~ݔ}^^/t~ՏzƁ'Ja` O?wBw)F[ }.oġ%/A^1 `k?sUxӪWXpG}4LX_:Gu'U+_a5^^a[`N ^ןG#Tz=N,K {&;qN +&~41]C+ Uպi<_=q{t$__pOSߧ|nʲ{ 3sk%m((qXz?`Z2ڿ/|Zm s 2+=sWȀ^c>`*oʫ*O? ׵ie[069 wv(@o_-s xg|>@mN`ŷ~^$v/_XUďU'<4 @F&?vyzFR^\ !.b%I_8\}J:OT{5Ư=L SVqpQ?>rፐy.nmq{Da4l {,`8q@?`}`y%yQͦϝ{ѾӍN/ 8c=}"!yW|%f Wx}``n??l v tc`8̈ 0C8}N'L"VE?#_Y{Fݨ<<&1/_> s Cm65|_p5L@riְ?8~cvc(1S)A7pl0%/I`\Xſ?#_˱nԻO /u ϫ1`\'v7&@wxB2zz:J= *3gqO<@Bm'dh[~pCP3Ufjʕڗ) $y0ʼnWĐkaƉ-WS 5|E w_p|N4VtA I7~Dճ 1UP`,&v ='z?/!}{v_S_Au2nC,8hgClwKf%>|drU""@ @hD:0]?(||_B~M;` V::;d=EK?gQsO%޼>7\aW7ǏǏrrwL+l05|vz<3>HE82h,1 | l)fJ/3?U)gBQ 0\|SO&yQ؎_ 7-#/XM|.wx"XvQcWc+ zG;>W1Ih\<T@vדhv[7-q1] zU P" \/0(}^@i -1"?*9R k)WGeO XwY)^zgZ~Q o,[*.Ck?P(nD2%\/MV0؟GwjU/XOy?m-?Fflr, UC[y'!ǖU{wfzt+;rf~oR/(Gz|3_R/ߟE\ #*n>ߘ+g}jeW53G~po/ Q >k x\Iߛ"%Nž!WG~>QVG>Kjx~-tߍ[$߾A/O7]pJn!T~:iЀW0;=ҩ/61-W_#5eu?jιN3Tt`o U8Ipq賚zz^xg_oe_9{O'H~8cA Fv=G8z"ܦ`~rf81L}qglVzI>K|?j+w^Og<~+W?.1_퉆B)t[{x>ƴohN>B/`yuw( Ofr1\|.aVy^?-;ڥ8opxHPd9̊vQ$c\#|\2-سU5xgz<O x^H6y= r < zC}߫ oirq(}rPldK۸v7t/Z+bd5Ys" pIϤ F|V(nooe{dYyefAQ֞P(,'+G3;Sx>͹ᑍvZ# 5~;MS|0J/'Ez`` ^14^\)flZ:6-@ll c 8- pEƶA@_ ¤[_L~gq 3[; 4߹OcӅ}@tGOpGa1cy6VK?hPY:yuZ״.POiB-nh pMjWx;Eƶff|:KaJ[z4b֋N/˃`R OP?}{wWqBn0[xϟ;] xo߅gqw0ߵX|WpR|@cٟ],C S yXhu8 M[09inłb`IZ~N˻QO{?5p\@[9ң/,~0Tx mq~mrFj=Pc_O^Y7vNc-&06.0.e?@G~fkV^>,n>z1X/!MvT]B.Jj~Mgnn v[nh)Jozº!8f>wC|mX3|t'ttVLI hI1zh1{0V\ihG``)Rw34g^ax>3aG<:JQ@8 VujKQdwC< Tnh׬wC%r>@nUCIsvb~-Wϵ`Cxy 5 7Eˠ>b.OQ˃3# B7c,.`]:: 2L!t\G?( t  (z0Nk\؂Vz֣oGޭx(?}__}vG( c:x]vHyvJi6ө'7c6AQC*aYtPC@2z>jr -[ˁ3rpL6LNVuGVbN/4Wzj}vk]Ou}?sf{nXFØ1-vyҗ76Hl\"w4o71=< n֝ (`OkTE~_#@~(~7>'&Gwk˂>@Z&ᆜ]V>34p!|t=qY\)[&0&>*&l3z^jX%7p rWap ΦdlruLJ&ɏ@Ġӈ"yPbyf+ 35[;S [  Wkzq7`[iexdRٿ"ru$i0$6ٝx5X!sh4ܙ ḓ3.g'ZrJ#]03>?Oܭ*Mq1rZDSt ^|5LnxZ"U c?Bq88ieLx*i΁թ [[1Orx(yc^;U3W`{ؔ4F ☐{xQ$}fx۸ާ!όǏv7PV'h5c,p{jAοk˰WJ Sp 78#Os9Knw.Aq:mޘ7OQF֎ @ʌp8d{׃Ssur#L2]FT!ܪ2 @z`~|^.>[y\/S(kX `gnڽ_  '֋O`΂P9`\|ѿcj8$6).bxˣM4s3 ~L >ne%<8i6|n8H4ub$0Lx] Υр"rL#h{6C( OPӋ@g{MZ0?Φ0?ܯ>x`U7jYI޿{V8||>ANwp'^̱y'5^93;oQ'z R?-CPx$?#7Hm;)a-@Gzq z3bo_\wn?[[Cfekl%T摙`~5G5`^%9%|kƾO$O/qFDD^ ? 2V/߻Jr69Xg̑cU:O~k^*ʥ-9I_5Dt7_5ȷ鉓M_[Ƕ:^?H>No`yߺ5yvsk-y۷t0Z7_J',eqiBW,Q '4euU?,qp[֓қXl F2]~sbOAzqsn[x}y 6hT@|N=_o6~uU/ob1)%`<±(/ F/AeѾM,]@ ycf6K=]rqpOT `EضɶpdzHza0×=FtT} wumņ,#vNB@a6,^ 2~46t#1xG@@A_G4i{zzv }_ rᲁ0Ţ )#;c`oJ}NmXY1;}ĩN/ 0IL6,=C_c=`4.}1N_67WEh X0Qό'!aoq9^_aT%+38Dž'!ìgb-ַ: 0yYIyIENDB`glue-0.13/docs/img/famfamfam2.png0000644000076500000240000021204112322177142016505 0ustar neostaff00000000000000PNG  IHDRytEXtSoftwareAdobe ImageReadyqe<PLTEߝf[fff>k0fZdS𮎲ՙVv ff[333 U6j4q"Ur63uOGff3†9iʻ1-RABDsGwpˮ>>(_ME}o6f( G`Cs9[ :v9e̱+6 .8FrUէ즪G6_1_^anOifkK'NBp`~jZ UZy ?/9y]9n9PO4&ȱl^8Uz-7㦬6mm4LInzӧmpQ#a@s/D_˥? gtnsx~#M{NBI٣Lj"/qW[B' R=Ũ | QODi'Ӕ~^ۘH:Ľ*g~p譇]Ukѵ䤸gSF{~C äi:ra]~Gw˂jYzDo5EZ'}? r# X/ͼ-yEUI5oa,r< T$ּBG{; #3P(|^!- :$w}hD/6N ͳV}$8{IX,|8-EYZ%ʧs&Ia;Grt@g$z~WW?@t}S/ǯ|F*ɽ"ϋEGڣ< IhW4ȩDҧ_%?l ?Л4`)} uup*&QH?I hlʒl$[μ4 Ir[kxcd}Gk8DqB~OGS6}'Hwh-Ѫ NLK^o9ݐHωcAOuoO[ȊQa-sq'NN =w"zY4/tZLdBdk>{u1Xν, ~HN[Uf?<$9X| vu~`I;=2Ww!Ӂh?!vך<[usƑg!$Ӡ ݣgl?<{|8kmiξ<%Y+8?D0S$}Dv}gOϗs$EBC|:`G>ɠ]d1Ww&"4Z~/c-;E-I=WED 49Ҝrcu\drI)x5ؚȠ'=ZA_d%BnG|?y^a@:ٹ=9d 䓷puIk+ݪ8H5OW2Oմx""CL\t3-f7*p宑|z<])tdz?k%Gdqs?y1DJN2<&n'#I4O>4|-^:(k2@o΁F Ҝ"odAJs̗yw_Gش4wGN_Hs/˂8ћ1 Yvһ({%"ؚBR2Jz=tweskm?}`<*K<Y-d^^~6p< $> vKXN1%A'=|Ps4>8bmc-:qtHn]޲S0_V4l+ҿ5dМҳ9t o箊͕yM0 rWv(c{|%|nSO>U уrw-9Ed+䩘f:~o؇]IGA]7=[&C<qCe8n\ќD`9UAAORs-zcg+;*V{%Dy]?frnG<0>zjF:b|EIv2ax{!n^B@4k. NQs5 ApN_xeChs2GeɽЍ˃Cwugu$XTKSDqnD[X~=Jh>ܴ%1fGQ;$sDyvY2TE}o8}4)#u;5/jut{#GDrib/RܲDCֻ7b$X/ښ#J #>ȳ'9|Cʔ4r,OJv57;ۖ^4=>iGs=qψH-ndٖ|/kk;Kٷ'&{fEF[f,(8fɃvǖJѤ[l\$+:-—ENUW՞#~3BM)`UR\̾J,4`5=n7G>xhۄ=ѳpNSFrQyUšw;ŜgZ`گz(Qs=gQTϭ~?,W䧝y\ev*߽:RKӉHp[r> ڙ};?2}~N'\='%éw۞%4Uk_ET94g#\gW62?1%O| lu[yO9H~s2B43;~~q[rh:9inZY~]$'#,~N1 #qkQ>6F X*켥$p釷̤7gع8s2"OKdgA9g$u;yZ؛#Kg`vාw=X*qHBjAJgx4ǀKXsaB{ӚX/Ϸf摃 }ϡ99 kKTG}Zr7wy$kh'ce7-]sRְ0EW|6"X}V4OSkϽ4O+Zb Ǐo+R/+R+q^?]nQoւ2/Ri>8/O|? ?w徯NU.6{/.5i<%y+gÆo[R|[BOM3y/X`<˗|'iC><+9qq,x~W7WS6?T?)4L'͑}qx=CpJr/+㽱G*~k-^qi~w[rs^)kxh;R[~AhNi0?vWRkWIiHM4\f{A !`5qÒl^v2t<7¹7O*E^sn뀰;H7g vr)eE-9?liZz?*WR\Xjo*5Fj52,Qc0k߻9@rx ҏ%9Doyq4N] ?o?WM(O{0?-w%?o+ݾ~M'&8~,ռ%!4V1<IKے G^*ɋu^-~zgKC%Xczw}^[~R/Y*݇7RYsLGNќ\fqb8{?aKQq?E!~Wϋk=~ɂ_X~R&o|ܽ_:~a6?T;k@h_zJs;%(q>/y!٥Y~|I,ϻJy[td\jfNkYj[߿.,ԞtJfֹb31֚T&VBMȷ\_p+w9˟,yZ(ɩǻϻE̻=~K??gRAZXFsd9<Vnyh7<. sxgcNZQ'y (PaUP2?-/{;dm䡂蘮}~%C3zwwVyowFK9#5vxY`Si"Dzz\=^?v wqibE~~آGeNM]_v-kTW^9J3~FBwx.?r!k;~Fy_)~Xbш'GZN>kч>OO-~>i$zisy:ӟy[t,ͅ@0h5&. ?q74V=syss ?gi&u{NU~ *?ZqU`1ZZl)L'G_}>wm~yl`gy[p*FGןwyi9= ?89]2?Gs9HVy]1am|^nS0Nމ4P =z1ߎrvt?&m]%|?~-joԣ)%W- aXhHy+:.`Q9Y.+|r;ןg֟}d?OW_#$/..TyKUp[g`ɥ/s(bͤ-~Y*RVE(cZCa,tķ?0Wk2_-=;s+QsF?xs:+ < vGcT_(Bjaa񍚥*2ik^__лiP Y}{?]e,Z"@ƾy ΊYV/F笹sIsoLFq7@{U׍>Tdb1XԑnQ!RF\Jqyp:/ԟcaԟ/ןӝ}Ez!yL+PB8PQ|KzCJrt'8.jqû#l7HQs֜:ks ׀s{/]i-AseEc~(F^qw}&(8 4Ke/shN@SkNh5}r\soRMo~E/}K'nh~Gc^lʪ}Yԗnfֹ]=;D聸u#9ןk~~wwU.cG ͗ask\hn@%~.?b_'I~\ܣʛFQUwhnL-)$Do;,5g撔5ox8=Tަ;I}á7g"ZTBo7o< #f]ƒt5Dl=K.趚/7zͥA9b?;Csoy=-:Gi.U ==H9?_ov.>|drTZ!| dk+Hx.<\}1g|TRh5('_={i&@?z})~Tť9?@ԭf}E?8 @e~.#i9l]f_$xۋCz07ͱKzCp"}eYqlm}bͮtҚGuLG޻Ŀ,yvs|!ŏsR~&yf_efw^Eٳ]~~\w[,z$B&QO5p\̳ufW:m[.h~ƿʾ4Xfc뤱{>?Wq\#fs%OFARdߏϸXw`zSTtYW)hY.?oo 79zKuLư3R,͵s1ҩ]Ѽÿ1mhQx4എiHAJ]\O&k+<xFߧJr[Ӎ0#b S1,Fxk{~nj% n> syId~L^Sn\{wiL4~:ٯe|PM4Tj4.M93lw|C?&LVDBsGֆ-`lifӔ,cRbPݜP3.fͱM%IGuy*56gDytν,_jɼerZ!?Z{9?>Սory QMǾ\ꭹ`hNۃᰡCs8]KAV.f޴ՋW9߾Ha{0Ke͡ڲ&:l5ZGJ_RjO[se}syFuP`wW:YޯQw;4Fʦβp ^u fm8(^UFVaSz7R"y0Ξs>-ovG6d 'ϊ <ڒJ%VB|՚+n4{]4 hR+7Nf3kU;O/yJY}2uz]dw _m~nZS"y̓C8/ϋ,caS~$A0IsqQpwUeK1.b[5iRgy{,1#fa,Ui΢Cs]Ih\/oVJߢwUs)5N?e_sEsB_EfeaÂ9%7!yЭ9>aÐoH^Wb<)l40̇y.65G}'\\DCOj\9M?w2[ٗ) >7Y;[@(KJo+]Čl;枒5ߎr#ExҸ~wlυ&jU")Piݾ vԫ̚/f Ykw} hW4GYKQ9pC^5WI( ۱ VD% MyϽ -? ,a٣l < 2|[$4Oŧ.jE `G<{OgiNv[Mw oTki  9-Mz㣪t :Z侕V/l0N;N`IAD\4WR;0 ߘQ$WMv:2vSsGfXK^'N}d͏oәs>JsyP~rw$}mQ#a*ZrM*="GH֩Tyџ\ߙ#)i<.^L'6z=Co㶍GŵRqPy+vUzvLA<]s{-cJ⸦3koIy亿|,b'q Fg ilv ާ%D6wx_Dfع;/0ۀ?G?~W:Az o27i-g߰T]]%_R˛<ޘf-lw~; >N?7 EzΜR ?g1Wvvɒ\ݴ_o> JkQhL|eVd*qT?\ fͳK)rOMy9pWL1ƠDRܛ>G$0;yEXbW?{S~>zkWy f:gMTܦ,5ڬ-5z3YccW<ˣ#3`t#݊s%^b[#MO $tfIބ18"}o ?w6?WbEe/j4W+i~wb~]QmK&9M8֢,[nv?vv//^umj>z//ϵ^ T߿M%wiy A/阩% ug gHw _96 !䬹~r)d15~ey`l}r25(K= v+Kfv?k4-8UhϚTވ#*be\FZs5g:4gt0ގ*G Si`4*U}{j4ͯմڢ˴_rWz?W߾^ԻoiT?ji^{uŮSTZs2PLfC呖%?ϐMU`s[s$W4 }3Ա9y ,fh][թ*'Ýɞ/yθ[cYobp@ןk% үWjsg~}y{8lc`׸zdI`O1\{C졣vIy2!/mm`vG|lݿۦ:.Y zsRIsV|YO,ŢXom;l4g vn?=Ht/5$[M1G ?k =?%AR0؏5 ?wOp~T(> Z:sBnv=Sfտ`u? ۖgv>v+JY+"ZG?ЏE7s)Gf5?UŮ[%9YD>mXW}{DyLiC/2v>x4?=l0Q`0SI|P[)z“͝_ιWUmWU9?O qIzX{}6s`]~>n+24&/WZsOz?o4YswlE\~o- ?NyEi΄ /h~;%Lf]~>>Q4P|s_3='/h^C |s]_z_wd$wp~ɧg4K+ j~{\C/\6?[ITQ+ ^FؼapYsyw.f`ies;mgwy)^7&u ~T-iT϶[cZ] FDk?+ 9G"sعZ~]~f](9Ң˃.Prs1Oޓ; dÉ~6|}M05W}~B k~_Rv0V\Hu4>4FZt>?N6y/8nrW zw+? ]0ADo#Sl (RQ OֱjB C^_D;s^MKoZ ?7v=3WK!kz^};eɋUMy02O:K~Zx6ZX>c y߈QMqv,@ ${J6Ƶ3W՟t|/]~.k]Nu3<׏IФ5Ge(zX ~W@:4$%~~'n9M 9CoU:pyT|~Tȍ[oY$PK/ b2.*#&49ڥmʎ\4f>IBO7⍝kMpvsΚVMpj'#g[s5~ԭ[c9֪?5]N<ɑ4b܋l_tKIFGe>z]* o$|2<~n;/ ?vqGԏuSO㧦< {Sό[V"5x.D?'iWNv jYui#T,}_97>m-/+fcŅΛ\iO:w4ϗR?9Ӝl;mh/www穇-˝}%9,o:ejA4zpCC9z_q!Βtvss8h#ʚc搜x6Ӛy?s: owV"??,~~ïx. C?ߒ7aҧGdh\{UU$铕GM.VUuN߇nmDWw.%yAs0bX _皷7|.Dysnԟ?>^?Ks\^Vs~nN9ڃk:ܡ["c2y+2UarI*4YVw)>unuyvYS`3Ȅ;|e9M/7E~Nqy[ގrŋt4$~r-REv, Q'uOOOF63"; Ob{U2sc:<-y^izu|7QO\_pR]?Z_6x>YOnDՋBTjKsͫH9NlKIN ?9:–z?皫͌՘g;-e.kVt9oDZ:?2~1*.~{Gp/Zo.<"+g4<ڒ~'/ [~Vϭk48'T%T?' ߭L:>kkLPuհuob;X9ot][J WKsX|;/ȼmǟ=9\]ϭ[[-}ͫ?Skɛ܆;'3Ӝ?$o25eR?!zMG(^UUonKyps!z~y1~yut 7p۽A):jof&eκj[:(F͵lkՖӆ\s%iKm ٰGϹ;.y&ZzvX(gu<'(3WNWsCj=C&^Jp>L,c4]sw2[,w[}'l7Mof ?_9]\f]x,LJWXs4lDi3V5R~?CsIa47OH\q }=9W~{)Hs8\%*W 4"wR#bh=GBX~w4Lwȍ?QXiCssiliA}_#O:)hۦ6=DWcj49NMTb;,<6'nWnn9NOK3'(z2nkn\oӍsed0LhMo_1|_oFL-;ך/gc6Fy@1mgkӇ.v8Rաj5v$9MdU"w:XӷڈMiinMWr9rHU͔/:d]~^ 94V&ȋ&!VrBړ ' ,sY6?wi*8jy*dW[ݘ{ej!+έV/^iςXR0ՂH}k4Vwyy\tT|aݿ}{|\pyO>nl՟#^iLihͧc*?\|Rz ɞ DO-s^nT'4"-kT.W ݘ{-+}m߾ޒڷww3EP(ﻻpy(+iPi"]$Ǜ{ܯ=qq||G]:f5ͼu~Nj?t6n3~e?jͺ~j])k^g.hS+;!3ww.k>r+k-osK=vs;3cE~#a3Iqܘ7Ыx-/4{siOp|Pcрqx9~FsYN֒9k5,QGs7~NNi<-Tv?t84iRk -wo^453[^mBۧO Zە0KcN3r5d+ VG4Pl#}PtLlx\-4Y;SBcSzBk>¾Fsltgot稻}d&Qrn4ox:߹熈nټ^j^kh;n\ 9KyԮWͦG.VΫwI)saզ4ժW,5 9e@hh>YJ_db.jΕA+p|+7v/oo[|,X7W|h4LcDG7D̡y=׌Gj9A v v[ZЮ]BnborCBNX.[zԒ vxWd+7we$wҗ'Il9'K2?׏ѻگs?,95珏y ,w]$XU9-\Eۿ5K}z~;~vw x67>\픅 vտ[??84:_sy翂K.TQk5K,ph7Am-V87iu>j<ĄRiտ[?&-9y@fUors_jy4ZK^@X,"bvXY{TMv&T7E4[fCZad?oS\oj[zi0I od^?a*ԚgFs{Qm%yyOKjO׃(d ~M1ߥ Yr})|ycvUo3QZ{LJ^^/7a:Z-y |E01~ ^q2'ŗ nm՟g*YTZs4/[\zWzp?q3v^spɋDsvsmK_>zbfK?vҜn<, H hןol܏? ?o9/5h~3#5?[myRo\.;&7usKqy4/]xc4_k{{+EK}D Q 4?IV]9&AE~\Qٹy-7N+ +'r45dAio~.sk&Hxz*1o%Os4/_xPV}4 ߝi@cUd ~m6usKqy4R;|41vKO{G &U}uAsܲew%$7zxh4vNJ9ah_ ٣sќyQ4e^/J' }gKI>4?w ?zxRσj>+ /Sf51co<]+wnS6??A' 4?WiލI GZǼ:zp5 E|9}~\t|B=y3{lٹ5hT*ݛ+5o߹N>l]@?lyOOAskwܧщFl|dx~.LMks/m0k^.u,]z۳_?עKնy^yнۿW{۩WK=7U}uOw$E߾9"bGs]u{c=x 4/IGE p&3޺ M:̙?^Z "Vժ@~ȟM7iPV~=r,(_EA0ԚnoKb6sRU#-~KZC^1ꗗ]̴inlk&>=MzuӨ7?ʟ3-.Uj{T) 0SL@Q(vnV;Р;n2:?tQ _Moߊ`ߏfvuiaUK1 >ϟ48ygU~I`-],t?>~k?3x[R _?[4{t=RE%\Sy|=!{" f fuVztK F#_{ۊ a]'j3o;۰qܜ4bWC]<wg; @ے_yo@96s7tmn}?'n4ImN +j<".5FkTLͧJi'5|oi n >J\%ޱ9jK9 ]{g+]1mpw0篼_Z_ =sft #Ty^ş̯0\Mv-??1#Mr$з{+4__-K^s[{?%{p8ۀpxϦ C1kaن `[6<Vvv0륧5?/6W(!y^RE_~/|2X%Uϟj~oKk9%\恸^푲Lim a:!g[Uf[vgpl WɃ3LXw 0mη~vۈ۱9z精b4$Am3ܩG'o|wX4A X3/zz̟W}?^c5$PxsH7ߪ2U˩Xsĥb:_;ׄKw;}Z¯wN/Ua\q1W !bڀʘVb6Bk JW*?7KW?'bo~ğcx|Dyts,>-.W׉:_aR =,@+vTw=&Yo|pGvakE=s5z^?~c_~矛+}oGoY^s7CZs~\c{de9p_8n9 Bô J0``X6f~iԁ6¹쳷gKW.Gv][ P?_p|[ -/QErZ?Og.nGiGsGܼ7:kQ߾ZfÕG"YoOhoymvk:ǫIFXUvl轷4[]n'v}F;s6,$qeq_ j9#eQv5U+YĝaрOJ5zmsl%vv|gRc=3z 1 ?Q%vףQKϓ}&X f!1?"wm\\MB#o4wk.Q-egU9 3+t)l`1?EhvW_ѹ|r_}-TWן:nW??'X{ayCỵ5(R:>;EVcE^mhj?͒v92)c^ޯBcY>w$8aI [^i4k|>-v5zlNDwul4Td-qgh1bd/@ t|;[ &GOYMiO8p1GTM^Ҁar0*K-'y}ygaHM). ௩ǘ*+Df6'][<W8|-TW"6^~l_~?\\9_ϣAe M.å0#QjoH;L"? 9jK~Vaxݪsd??ph\y6Nrw;Cϯ4C]?2ɿJх?v͟Kqc)2畕ކ=B(.pJO Lq,|XY41hS.V7?wwٙ~͉z.|.C-0C07zSJ9gJ_1sυ_v/Rs?ۀ_{5ϕ;,sz? Q===Lh.b_돰úEWiz~>!DS `e;]-ekϏ.!-1 o!Z뜉Vsa.~]fp?y]yek#}Tǚ1|4̟NF=q.&}WקJ0ȡ+&jg*|}K'tĜoWEv^迁[b#˘sWs|\];u`ݞs9ytßf~]?gWf>z~]HN@/H]&ZޑL w[1wt {^`@0?I.顷k7|>9mT7W\9#p;ߎG3|åF]܅?pg;,pm97 5'/7/7J|7o_ۧQ'oϿ:9ެw?or0ZYFXJIܯ7>KऔiZT=l؅ΉB9IYxL@_Iox;[ ̭ϕ\jg(&Rkj/̫i7/ F*:ip 0j jؓ*zL&֕IN`yY`R|yh>/b_&~~G 㪝Cԓ.57!s%.0~1hoo_ӆB xiSn^2ܢ/؊qWT+W^œrφ%'0aZ.Ygg̋4E )vo?ȟė? Sb0{ooJ V K;R+gzv`}w@a Q 5vߛ| |sd&K|,\Җ2;>0Jn&20ÞVaN[_۹oEpk.5̟sƈ%ct s_Mco.¥q  nX~b_`*e\ܼ'ϑ;[rSG,qP!~O|?\i`>hsr990p9y뮉QjSJ<ÆVqşsu\ߊ? !Z߾˳" ( ?Ksoo_%.x cLQZ[l]ןͶى0deC]Ƿc;;E,#-p&CW_>#@GS{[rAԴkXx6%4ŸoKm_m'b.64oSho&\V/SS] qgLsGubsL7;[uu20x Mo dh['Tfd:f}6z[|}??Zsp+0 K}yyC R~0?OI_?S*U8L&qAR<߮bRb ~/˃&fPWlCY𣘏̇h<4a{X1yjkvͯ}p>~`/lA'.1j0 v}5W]ZeWa/_mcy>LǿkS RE;şv.,'%*q ;ѝJ[si vFZy)p`j .UCHPDxz\M*y sf6/.4E\{*dy?a_I{+U9d ԁjM. y2C¼4)\&RT?MflqC3P/nIPS7Ò:ӹ?a_OURyUvQy2?0mrx yUpyloIIp%O*Te1oϊ/Egh.k1΀n!jN}?nTWk Đ_9X6 Fi\ͥ!^*ED(@fcn,ή_x{sz'0-0쇡e/S[%E;fg 9b{KsK_#j9czܮ&͟흯S}?W,oƈ9v R}>}m8R3[ ?C ^y+/LztBM??os0~(’ #}/Zc%Y¼ȷc݉%!0/s9T`Cg|K%'zC"\=mKG{z߾qR<Xo?z~{Xsk~)ť~@c9txs4:>9EXNO(^%9V%NSI gηޏo<Ŷh dv۹Q%s͟akO,nş_Ry1P9y czR0ol鴣sq#B4eu ϼY?nZ Z#za}ڽ +r Q`wqs tȖM1"FDތ43sr!IZZϣR%g'SHk.kJɟh؆v?eN ~0Ó@}=,7zmvDL46=$=r)9JKZ}R緹TBN*}y$ပc;.73/_eW&N~} ;6yWTю:8g3e oiAjLP˄~De#ʁDw}y?VLG,RKϲhr~cK.|fxGl~nb43d|퓟lQ1~^^;gɟj`?CL4|Gn`+`n foa.y2ywz?d?v7hK}+/Uq8R[<s?dsR>x $>T?eնJ?G3j5~Vc]-` x}q6הI9L[9ђ=NV9vCb",>>"!95z9NϼJEMu-~RlOL!)NbOVG޳==}_ C7mHY0 ޳{Ccqѻ-zk΢6R,^ >@-툧 AV `OsɁDjǟXu`0hHFET'/U<`.3*)D4α:HV!;~H j22so!\O*{%m'EQ"j2~$ "߶{ŴWz]y!S،6m}Cy~ϏA |:~?DH3XWb #7,li\]KKURKUGCF0s1R\4|.j!*fx~jmzFqb\6R^F(e{\d9Y+`⌇~Oqi(EAӗʤ6׃.leh+?z%ŎSm qqhSޜK4R7Ktp*.U. ZßK/欩/Ja-oAg]dՠ" Wz Komu!G)1Vkye>~evr)cn2.2; aq񀻹U&ꖉZbsş3 1BS>B+}VrR}e\M^jKprqˈ}Cϸ9=Te5z`.vL~m[s~{ᛵ-#`^_̙&|mk8$[07UP!`#rYZy>1$@.9.U[uGk*s!$5nJCAktpX^h-yRCx=VӲ8'Ƌ4-x M0WmEMH|KzRᛵ-#mil/z$"! 7SOE7>,SXq*>H LBĥ 6J V}FC!G_埛2΂nsats锗zm%2QZƑuda3#"_0l09@hl:9N2Bi`!ej̙m&ƜS7ql[63{&̟3ğzNc>~Ns<[ Agk UEgc1tl:`ޟ=[7k[{s\Ns;\a{P27B4V>Q_ lpX ` c͟Tٳ7via=~ -ŘVBwNWfl9sMm5zLGԣF s FooqS7Yp"o-roI_GՅqm?oJ^G*0j7TmFhV${ngLSiÙů+U:k֦`nDO%D8qW"Ø1G%_.(#˷^`4qWҪܵky+z@C?ʹpj>=Ռ[p|=VN{njRK]]U5+VWRTaxIYBUL]iPk`r}|~IH^AVfZƵ[k8,qdgKދål;o$)/brNKضgƥ0MMEVQ&pWʴ{w{#jT]<+9M0̬EGt*g00ש\5d'H`>/:JhTx[knf2,hHayԵtp.>Vo{AڶgKԄ_5H6iƧ?Gl|4}Oݑ/3?'׏q|5r=J~3W_6XPÆL<`knNlʯct)gi &{6kuʖGvnO,G|8?{VaǶLKb?/P\*mn<@Z?VSx =P\a#_0?+,+ّUeL>}kmb97=ɥj]Fl~ 0O,i2[.-򱭦F<1GT% =7O6Il<~>?S+\܎^or~ēxp>@1u pi𩔵ɧom4coe=usR5^q;lxb0s۾#V\Z  4:?m]竌lS~~d~؞!b.Eb??7I}y]Zӻkßw3s_r:cE sh.5eŶ/[!٢췚^Yvrg{s|z_ œloz(dWS w<eO`  A=TzYDK }x='`>~3o_VMzß8[] ~E!y?Lq+U#53>+.to۞͖췚^;i̤Tހd%W7ٞ]Δ 1Hq#o2/vIe5.|W72&ȯ!qd}xM1כ2LlvQad%>@aYWf7.YqVl{Jjn^~+1ysVFuF3\G+̙>wֶ)qLrAP*~C eWڶ.,|oټ1`O4[ ͱ)z͟y5$y'?>askDAk|V\ʣnnZqܞ=8'k- `c51gO <4\}|1`N{9B\ŦBks :;zK]߳[|- S{|_c~Z=0~2&V~{OQfy";|~WLl{tU1X},͸=y0Ǧ@Q] cU ;2+vFȿ}"(29| DEW`"E}Uf2MsK>ɲsj5R?~N"@Ƽ΂C$$9P,1G) 4J Pn.`1kk֌<)䤉#)13'͓?~[yim}=ȮP`% w sw4RiȜSP};0W Hq{^R)7Z+EX[1<}wMXiCf;H_ߗ3d |Z T$WFԙĪzVՐeDaAJۊC Vs` bq=a7?CqbxPKucu[=Ll̉ 6 8G@P>.,Uytvawی.b$1aol,DN=a_n=>= ̩$k:?D,l?~NO<_T~Uhɥ9΀r70sL]ibkWes?Na/:wW on'l88_L2 u@mlWVg% muZ|f`N.XU. ~t¤|y eIOW4:kک^n_&܍maw(1ߙ#/25ձyg'8_׏q6)mi ,!*ly{ytN ',wXshJ|RyE1mdj/}ُUC;ϗˈ0c&^b/5fw^7?¼w)'Q1L`w(5+#%Yܡg!4p-{2i}îX4_HPM †!puWOkA gY{e\ˤQTͱ~\g8vs<]֚Tv?Әp`?ǦǴغߍ B.A#ڹy].n^Șۥoy?N=Gykis";%wms$0Xܺu+‹`ϓ%t=~L 0w?l>`P0XTf1z8=] wqԇ_ Ka'~n~A5\cY(OfaciĘ vl F'';cj΍zՁ|B-z:: +cVvD^L$b2siK<:]m=6P&s >)9k;hzOTc=z!ΩsyF[WzOO==Rej6N'h\~Ԇ7uB=߳]v}s]ѦU-c]˥6sZςyM4kߨ%u$nu; 3M}Ԇa:hPٹ?C$CmmV-R N$g,^Uu1&Z&QM<yKßcD%":6,3).ySzN8K w u)C6*njs"ɳ_`9q2-)bG-a̰/Wx:穝V Qۇ.5Ƨywzw'ٴIIW %uDB {y.c(-+z^Qly߆̡!fQ0GG:?@Wy)m;!B<}\_07?=Zȫ}$̟Ǫn+ȃxOإ`9L?ULNgH ̟7R$e0ϸ_ǡ}ԇB2K{/'SBo*DAQH!`DGx]{o,6e-Lc;sIs|c?O$ԟ/(`NavPe Pj0mL8ߗx}0AƸaU٢hTq¶aז3I)b#j J c #:6jO#)=՟/<\s}>:`y.թş9 Q^8LQ%r9Rʉ¿ŨGj:e0 0L[!oK $$S i}J*Ȋ?9mue)cI,Ƹș׶N`Zy([k"ѡ|}C-{:m> Oׁ5,CE? #W-\q]a.eQS jɉKy^*t{Ĝs2ȣ1z^;qoiu_<s10S*u[˼FsL)Hc^mPw+[)mC`A `6ǩFetEAB_?ߝ7?X5G8l\QXŎ3iKeQS{zBrTR;?IȺx(8<*(iޅCnu#" a  Q .BU 3n"U|f k[)m<1`Y\Ή {O7BZs/r6NK0I^]/u(g?qFF#`;9~45 lq:7*bqժ:JDÿp '"j}!&ՁHj1&;5ږiJۖ#.9Өu=?OCX-,ܠw!T>})|pL}Q;7X~.nڴH=yq;Rt$ N1caC;KK+0e.UG2'%(6_vjw+[W)IF>?yxL=9|cg}q˭~!َTe^O;V]rGNֆj8ڡ-)lJ;OnKdOk ](:ҏO4\ʈnmx\6mW)/`-IpI>Tg8F<%T6lP]{f4#d`PpZ-+ %s:^ю''ηrCdKU |.CobsT8Xvr-4cWd)RΑIhLO1_,uf=HKJĶ'-k\cJ!H(Ƙ%Pvy-=MyKm'?/W}?\jM|L:]sCяcl$"\}50A\1'n's`aN؏޷9yUrL\qpqʍeoϖP:Q}r07Qc>,ONBSl9Wϫx>ǥ:9#̦M2_K%P\mXsͫ84sr?IK#,iybL8*ӈvVZlӜkׯG9ZŹzU 3䦺Uh7*,L@k8}vql9MpiO8m̿v;_k{0\ɜmW4>ihMad_G+p>z(6>R S6n%$rܭX{Reˉ)1/=-цϋʶe+9=A&ğc-?OO˸ՔӳjӼWS/krm˄5j50O*aj]@{ѿIH|A?9Gymp KR=ElW& :Z3ni:sE)[)C{!f mG#Rr}0_N_Ho[S.ٕpT,M,ğZFۅK!8ū8G4hYyγ9ƫxlğ$ο:"==9ږk{5?:k+Te J"^Z'vE+$\6 <}.MSzB27|?=s!6pam}|#ɥbv7Ŷqsa|QTR7\j)aVΌv1dzO=d WxDaisGc_, ^I=V9OT}I⁶iL\L[OmS,mJ$alͭCjT%Uwk M4n?evRעuK= CEZJ !bZ"L2h1˺2j7xC>vGM,\0,` mNTbm^-TףQEt#컢84~_~Rߌ TSKX%2s9+c_&M.vOX:=usR&eGڼWu W.^%1矅kf2I~Gk#u/3a_FQ }rsnVNZhş{8=`oqjX,]œ"TPjT: 'k9y'2'O|>؎,'rR-*Jv?HJc9ǟSt8z@D{o.hoq0Y,| {UAox5IeH$8=Na;xa%g/~Ǔ/}^T Oe>?j/|PG?_Wp"<@M"N,K\s>[uT0W&ny ?م~^P"14ZV&iTdǾg2v tC&Q-=a_ɮ71@&ϋ]նc|KOA ß+9)lgTq6y5ʨ\Rٻ !+nca@\faE~,Kv`E qaymۓ{{+EϟJviB$al-yvV~^ȫy՗Tq6y5 OtBB>g*^3y̳s ~Ƥ.cNb޸xeٞ@W?KϟwE\\!gLنO߿wf9TyK*d/ Uly sk놑p*Dn.؞LRGƵݱǮ*&rIpI1`A 9 ٱ{)*_M&:`O_ce @v`ŏKiKvV*<D{sVqBo{3 1?VaЂ5kVpH_KhSa55ٛ+eu o;V|4|v\:}„OV mzU,A^+tvQї426 M|8"[- g'#~X5Wf $C){ɶaa;f/b/>vD(&k8e~ ߏ8%aYN ._cGv;R]S׬s{^0ӑձ[y`Nز:? s=7~kf IDr>V&ٖ I0 c%`<V/0iS} )bby&vՊ?wʱ_?qZy0wyNnK0?ĮG._qeu-1}.[BųxbK"JI+a6?Kߟ}mZ*dA?wʱ_^Q@{qG;KINc̡Eܕ7ퟀzNণɷ<6o2yʖumd(jx3ߏ3;/(*oTN9.%zx}{Zv$q`~bYJnѽKƮm'Y_ycwsUZ#a1`J;9 (ܹ qfn3^ɹplO< R׶T$?q˟zֲ8ԳJ6C0]lU|hS>'~һ6/EpuU,ulE|&b"57`Oq^ҡ?W|vT[[kzyv7!tßSlЮv*D=[ eī<ӹё5¼Zʸ49|Zal@?@_>>x7"(?t~Bmvڭ<Ù x.۸s9e﷮Y g/^Qagl.")x7eFT0smz8#N@ìs^ yxy5;~c(z(;'\"nXh?* se׹*w^i.SZ<~1|zǻYrưi A ?wU" Նz2W1sS]] v#ldr~5뵷^boiJ` -SXޯv|wO9w6^ycL\˵Ҳ k+Fnu?)i[0~2Gcw ynq64e\w涿Z]{Cd5S[7T_xjxA*UR}I`.`>Gd*Gqnװy҇ Wc_؈G)X3ʯԺ>P|y?BĶWğÀۛ\*1rū&17BLR)+wic=tKGnxxw}"A5\boe2 C҂Drflٞpur-[\Q#fG#'D*JAh'b[cO>0cFe=a G ; ~ƒw5(jʔwjH*s3%p0n(_uG3}ҐwkH:'\b|ȶ&|e^Ͷ|AfKU6VoػQ]0wy) !cc1bfzD\,9B9;ǝR ë:$:/T\8u%F XBztj!G[LF'J41N!q=.ˎ-$6l[MM!a&(kז(z9|=`~G#og?8NQg@SG鷿ťyKA֜]3s0o𿴊;YrQKվișDh|1>8Ic.aD:'U1윬 *hAex\().UBfwS9msZÑsb,l2`NIiEk8k_.)(.o1>z//fwTW[WC |iIؘtC.s {5F?W{kH9UCN,.s&x܉P\f`jf5,Q0#D5Y*gV04\`s ʒ`/l; hwԏj X&9Xs9l6M MN!l:4Ε6EUr~lЏҚm|%6QN6.ׇ¥*[rŻ$s|WI;5Rɳw^zFTmE!̿#:^i7dsR%R&J=jv]xV $s^^2kk39-rnoirS#^ѩw $0zxUoׇC\\q߁C,JͫR-`>ߘmYSPLJqݍ7KUS gx5ixWIVq|tnYȘ`olqv{O2`伕>߱ ?zOtK&3ϐ׵,j?o:Bj 6ڼsJt ѓws0 _~y&hGhU3~։lE+z%T{vώFvl:>Q@*[*L$+Dg͚M~jAy3C|1ʝT Jĩo 0ٜg:^޼g Qot#aOJd7 RfiD7yw6 cl3h}F1؂w SW.׌wU$Vޠ,>:lE#;nLGBWm8-W OyYR:[fްG>`޹!ߴ5}[U 򵄞%5 4N[[)HUwMῷ-xE?T(Sj;[3#[ͼ W3\NDnaxKaCJQ'V?@s'b^RZ\gI^7z=ZH#jyoc T:L؎I;lHbCUr\3#K]wO=`ԋ,s WW(tg;;4GG3)g8Y8lڧ V[Fj4R>aXpX#7mxf}FC_F6CS"!'W8Qz~v~.K@}R]6,wOAi@Uo*ىۥZ˯3JG]\Bt~${6t !<ShaϔcKщ`P0ۍegr9)4yT,\N֜SK19]?-O/:9sv_ݧrGfΫ{ΑW6niS;R/?~vQyڜG8"|R+~D_1)|D1-KPP!8Wa?[sKE2\L`HmKiASM04ev]͹9?<\WٓO=Uj*lC=ah&y3$bsp-paGL wl0 PNSMY=`j߰fĤ8oo-TΆ=5JryХy9֣w`T۞h|6LlOc qo ޮ8}4IJyl\s BNO=1q۰/t #dΝ=aW3>~4> p7rLwtƏzw ivZYI;bk]cUpNlyQ0+XbaSbY5!Y> Ȍ8ϽdOAOܧIvpmoO X2a~3Z:wGGB܆}XrOlՌGcfٶ̈#|6>4vx` L&*4v ^uv%6EMXmYOaC@v:\ADyaHZ&ty<Ͱ޶NgvrK<яBۨuܐ+v-f~3$9)8|,;j&uju߯ i\۫ #(,;i3 |XÌcx̎{Ճ N<͉eT+jl2׶V1a_G6q9O4P-9/ab0E%<#?1 ?h¼yҐkW+?8}; Ts~վ|>74cX Zts~@ӳJ㳜 |:=L!glO$o*EnWbwT~k%9rw||`.# &W{tHaP$i?Y,J6o~;<"ᆌpܯ۸5^YZNū%ʗzWEgzZ!j`//`Ua];SRzt|ɶ}n gEBp-*~d 6c,s9L&XqU0w٫ z>f&YVvpSl-l;+]9uՁ+i/HqkΡ?jrc&23Hep_^أk{ݷ׻Th6,vQ _GyX;s}?޿vLcUS  ӊQ+{^x`,Zގ~q 9?-:)صqI4"оGv.v5Ur g_kk\qc~ vjgB*~"0K4 QĞJKϩʈW|3R,Cfj8ORhװuX;N;XAKGfyG$6[rq sVK*EDM.88V {ftjȏC}? fzW؋vscGzW{{8=hsg1:&)Myic[ XФ=F)}Oosk.9Zܹ9W0!XU+9w,n= i_o{^os]3@{5݇{hden˼Le׏y8 ^s#+oqMܳbbO۠Y9{S/_KߍIli8~Dl[j9|% rDC vs8\,e9jA+_T w6_% e_xN,x#_=_0f"[qprVpѺjcw*̧ nD8w'Bj yՖ]O$pB eЫt5^n|k8W3ܹߴd,]< 1 'oik8ur0`Z¡]Q.z~*qw{zcݼULcdUocCQvş;z^_`W44.ڛ98yBvkef.EOP^<ַ󠈆Eڭ"͍]d+ I)x+TcqɵFq(i#_{|MݨnԪz:{\ŚGoq:Y {`NVbU[*`֘lȉb8oj#F>EQ+Bt|*s'7=>%48k$Ch6x=X} W_c/p dVw>'YВטE:FAV3U?&L_{%)[ޑS1@;loR}g_Y%5yY?W5gK+ q6?ȹ΍ᑕGJ7YOtg*w0#YSwVsbDfFImp'$Yc/YLՏ&-mx8YˡHl^DXZzŹ~4[:ԯsu}*C!=E~ PU7m`;_%l/Y;bpZi{u:Or[`#v+"\PaVB˫uDܶ@j5{{V{fſsĘ{?4VM[[ڜ5KX͕뉻ɭ3><ۇ[Ce? ߾|3RQiqeuTGеIKa `H)FolgcT9~v.0 #c3 X}! RPwcϻks:21 <3Wyu^fre\ MȂdt?7˸ر?7JѠ'I/j9o]RȊ]$Cn8cQ9EFº"+ mm r=z^ofj{F,8GyOnX5%92#>U8ϱ{J zPWbXh]+s6(1Zqi$uKw8wmyŮf㰺aU6dV<ʲE5[m;t]tJ"s8`9{!>kZ<>Ebsst::>ԏ-[ryUz[dFcpGsז9WjossLLa^SëK]J݈~`7莰,Fjת;v5-V2bcG^r~ M> ?1`prhqU4ۓUPqZ: C!hRy I;MrtjT\ sH8r]t&f YOƂ 5tp/KGA6RW߸mG02 °00>z]Mlkު/Ot4>94Iڹt>ۗ:+8U[/: ]lL6ۘ3H. F匯_L!e0߮da%4os/".4n/?9ocޗ|))n'vU4z| Gv(<|2!#MFQZx/+s86fΰz?|E6aβtU<*0Umzts9Fږjƹ닯ua8RoBNF);J$ոUXK89cܢ3>=l\xj9#ȫlhyXs`u9x+FWV?: rKٽM]H-ȀSbZVv/CRV}djlcRw:ɹ8(4X+̇)XQ^~b= }~UWWGbmKUvCi6ے4St-U,i0ݛQP4adK I NQV9q9L8ǂťӻYKJ?SyfLC|{|pH:׬WghD}^8ӸOSw\]&VJ]β1"}m%n';cѭ8xڮƶgXgW30e+̡7H8`-TCNl Bahlzv+aK(RY}seZ%zU̳R&hj˥ڮTI+暲 {M[]J"$v5E*foXi@FVFi [MXj8å9c+XiwyVx}ԧjPY#m92Kx^zIHf34%wcML3b)G`}k3Z 7o]7\cB +|$Emv!1 OM*Lbր`=@M$3ZyfQN厊*U}Z~wZ"V.|կԇ%~HT[ZU٭Wٶhض($)\}yJ 7o]R/W8'ұO/9/гCޮ2 *oFmKUS+r@bpml7liiN^cԡ͏jksSr*nğmQ۲$uUn"Oo7zZڮ(~9hcl\rNqf* 8dw@xz?*N?dZ}X Ƕ׫mf5ud~5%cmZMlvҐ] 6ZWu+ 'Ξ7r@e#-Vi>{49]m9RSapfΔ4H՞L3МW;o8wt (Lm`wdCqg;6_,Kh&+YɰVH`h>p#=:| -Fdk)iϢ7*YP2YA> [e7>{zʺ$[7R$KT'YTz{?}?}^V9dUhqnU%/؇9 ں -u=7amV`wR fjPE鵞M&xQ ghb3#m+(V>V:x Hjds#eRaV1_j@;'9O Zח߀CU3| $C;uޫVovno5y .ˍ B=gLI7(ʹ%|5 uY׌s#eZ)(ʆKeÕyuYm닣jW3zqޟrXR[@b_RƂ%ZEx+f[Þ/Ya Agre^ғf?߳)E0MjhG@.:_-Y7rn\z/ulgi{_">+i/]-Y uF/sm`=VڸT>vo<|J/52s={BJfb̮հ _$+*{99OE$Gz[CEb; ߥ]ihVDlsyUX|肇YJU \cw%֋ pf'$}m,}6fW۹sF/k IC"67Q<I}8 a}o+^:t]J|=NZ0%v3C% "v)'9-_˟ipͱ#}ڞy5sxN^mcvծh 7.z2.#Zq ˥XX έp⼊Rh򉪒x‹?9ROHey z..kԫ\WI?A5U[Nfb OU,N8n1 K}\iDaeov>$q<=GQ8G24.\֍Y}*V:>cb ScV.J 9ݡ,m֚+>.^d6tn]jDrs\9 'cv$uFQw.+ ݿj]5>i iG12wH%m7WhÈT͡({.g,Q êF+ưa"kMݑ}3#1y).9v(@#ocPAƱy1j<ofRuYP3}+8ڮ%;"@h0mWg= +,EF w%_)#);1gSindaJ{>c!IȱQYFͱK=;y#q_yqw $g&\<$O6fW wæv~x4Z?>rE'who%S|~N)?LTq;|΂|P'P ؿC#( y糅y4zu_Jpް쓕,F' Aا d%NT6I. [,1chXߓ"zn]gY8h/<!@WF.b>S=Mf ,sR85И=UVSr(u+'D=ܦI1N.E3\hr V]{KSX䫵$g_8oۀ{wwIAӉ]-Dm\:== 絺yY.M ʟ'pY(Χ"ٴ ,L9sՈp:7 +U& %m` k+E Dk DzŁp,k+9vF+mοKξ,ɛVW~H*m2PF9"KqTz4>j<_,BsFbpkYBvR´m;|Q\C4wК5wˊưg3C|M&–ErV̚W'm}|IuV]R,"āZEx~-{zCWvcjf?ü+՘[8|<9Sن1.i##tZ= ]eW}o>o]EW0Uy;4m=iuzCwGkϟ_lXX+џKq :gRb% ׯ ̕@.,5A} wGNQvz/V8*( u#v{oNi;[Q7dpnd%枬Ǩ2QN!p|AC4'l8e_ HQufq\3'٘$fW':RS,h>;S*ǐ7||D)-ܷ_i O:D.bE' B)~x:c!5#b;N4oLp > )c3iOfb$Hh,0b^8>ѝNn [U{}^D㣯 cea|>itUkr"_Dy ws٢7 Y>8MH&zDҕ 3O#SS㠹sT=SI4s9jlrrO|~-Ms<}DU^\sߒ1"cMAZhj]oo稦B}L׋<*;0g!4[WUL^{uU"oV JNDݲ߅o".XqrB\cr6DSP^pgGw1On]9mlaqۃ+3;8S߹|΋9m=2zq%uM>k"kK1Gvs{! J7s$J0.h?Ȁ}Oab03I1\>cWa7y 0 E,Mg'I_.c|~p'ᜈOՖ*W(3"DN#>/x&(]0amȹ9GEV?qj'~~3p|W7W]'pnjaDXRþ@~ 8`ePge_8W3 j#Oct.LsCy9otiʉCϕAtL6V솱g>}T} y;3^F6oοv߿_>~ oŜOz~*z~:5??\Í.K%Ef1gƙIui4z6eoteh,rGZ"-aTzPN3CX2|\?~.*,u&e@5), c\#FmČ?8b16čT e^=a_܅FTYq>g|ھI_b?(1\c4V(//_lʑ; jeYVU|[?do3z=ZQdlxNT~&k-HzMJkв¾(uIyrAoCHvnY83S_w >F:˞5=%ol:0JQIaHgg6\!  a6sa:xo֋i˷aת+SȌz.$ }ŇyXdk6n1 10\`1Fd.Zp33 U! eB@8(ʆs<ܟ;>oȹV EoDd܎"q&- @ %,ͣ;[9)F5 FأHw,wdgh/&GYW?|Lξu}{lG5.vGt!ʉs$ WR&WiZ^}8cK٫N&ܟn.:)j&uϻ6m]U[e ^arN8Θ9o2@~[Ľ&ŤOA{aV{/e0Bc #Cu59jWV8G4d· )59(1eX%vvp\iˌnvټ?'E-WdMSЉD:Qhdr~f[|I : q*Hg};t[2liÏ6!2lڛ^ >=ʙqX:,7E"A rI9shFVs@z>upo9qUe4++')Qʭ'1aOAYmy4"ΥJP9/8)P ,:=gא 2ߒ~p$\= in!Xq&w֜qG,"B35hD74A>)T,kO#fMkJ;P Umq'/qAVFs<Ǩ*n-{D,8i:-)}{,3x6o9h ZI5W3I&Bgu'B5Tv"2Bx +g(ϵc- Kipb(#I05U&^wV;]~D4aeo\?a7ٓh'.c}r xۍK"(=}&l)s͆jj ףgn#lZB[uSgL)rQGK;29&@gS9V=M-6M>_M5^(4i:{MBc*K8N`rҦv@ը{{Y*j»1gu1UFa:V_ƁseeΣ9agQJ \FA/Γi*[S]¡dYO,[nmT:;PxGlȾ.? *GRսlU\̨2kB+.q+z+ZSc 6t~%l0Jo7EAna4DayKf2[ jR. kpa: 'ЍWuPWzwA%n'0VtoⲚ\.2*&'C܍v%p^ȃP0a}5.N/:jV>nĿ6;N& >zNZX8 vf!*&';]$w'驇yң 6?6szхEwq9ЉWqdIue_XkW^>0͖m^58dsdn1o+ofa5?}j+84fNSs9Ћ^^^p3 ,o#J(Ek%C:Vav`i@|m9X@mNb8V}5KFE v)/g;܊eyB/>dbH-djTRƴKÎ_x+i״Fs Wum^(g λsݢ7^ʋU0c.}5=%8&'Es U%cҘJ7xXLP`A+DѪ8eSwbg3r9b:|vSƴ1;㬶8s}o;K.jT qhs;\6Bʇ@:s˹:YSsƜ4.MHR#gsֳ\xELhn27֚ ށa/__S(gSu 0bj;tFT)c82_z=LČԙ^|vp?89O|@kbHtx>Dи&,^R,TgcIԏ#:I||\w,"X$s\P^Q1/'轾>; 8֤e8r9pw0*%\DAGm} kڹù8GСq;6Np+rn ~ϸs8T`ܼ {Uo[`($2R:~8#Nc19Zdn{0m ԙ&ȳdžkPmhx6F.ѣyUXνߚwH'w<ןEAHxW8w7ՊqN.K3t27Vv^ޒc -qaX6E5.RETSvR휺+LO=^㿷L;X.r-j;oUy9E]_:na*N2}C}GpMZ6-nHzu#~}(`C2RB"R *\Fd?['ASmw Ohs|&*Qy]0*br:cb*2뗤FoS%絒uE{+R(קO?F?8=o]]Zvs*GpZmT3o1f]߶'"m^*//e̷>|?"]ݻ8o;ΝeCΑCٶd9{p]߶oM1q嫜O,s TLv=lսsw58ݓj[U<%k}>l}VǏAw&Wҝ>e=,&8#)m }8D䕇)4yFgpY{ѧc3wi{*lQb_6Qb1`/\`(IENDB`glue-0.13/docs/img/famfamfam3.png0000644000076500000240000020573112322177142016516 0ustar neostaff00000000000000PNG  IHDRu tEXtSoftwareAdobe ImageReadyqe<PLTEfޛ[fff?lf0XdS𭍪՘Vv ff[333U6 i4q"Ur63uOGff3†9iʻ1*OABDsGwq˯>>5ۍYu'?|ҋRܾ~/Vc £C"xY$p1o}oȖDzzhy˜e"A;>9P ?G @5ʦ3'K߹|{1,P6h {yS48IP*-JD X, Yc:-_DA <pq^T6D璪~g֛y 6O8E.eofGaxD@6'[! D`^`DǍ=ܽn8d8F;"(ˏvsw`R??Øl+4" _8FC h :S$G<7W=>d=|-AB'c`GW<{՞i2bKdTpVZ 8ķ@p-H`#cS p[%B.P6ga VC!lO- *0D3&PHE||1%"h0#b &(0ry;( A} @fbZEPhLO>X:K[>q&_ \i pALfx@AvcЇw_:0ǫ̀ I,g<)exz Lq98f0n4@ ZaX)Q?rETANj-cYc(G C@' Rh= Ot0,mޣKC0W߲10Nǐ^3p$ۃ!H4&Ni`fkAc@ /x>(E@N7p_(!yo4ח@=NmKH ՏN @3O| ZWq3=eÈ2̳f=Le{ p~Z]c 6'7>Eʡ2tlE5Z̢,G`@Gv|>! ix}RmvOcH"H ,xDEp1Ao˳%5F|>(,rf]lG"@\:%L6 f`h0bֻn-WDwwl==E;fn0͑46FcXaÌU;=a*SC:N'Ь ~[4K`p%\':Dp7mk U}\<@web %[Dų:5<>& 8jX(致nQhn_~Tg`1\M qxW]6.[p+8r0H?h 2^2?F(.6%Ufu+HA"S$` IcDl'ec!Z 3zy=JO8$C+` z;]yt8;(\Ny&'R;H"l_F58%%[-MZD0F+ASP,WEon;h?xXm8h0&HZ*iၔZ]2/o.tb|?!V;n{t%O"r>0F}y=۞4=K+{wq?5L-)8$9 39NYv)6)XrW_w24`O,Qrmca ?߂Lix#xX0'c!"_rcmaxvsu}5"|Kiؕ$r]Nt5@赋\ݮ[yh/'a{ǒ 4nu$S(pQ8ožճfy=Nts辮U+6?2q>>ny~>?{> K3;+!i% cX&?d t<"xkb\x~':?/1ng fS톢E&O,A$:G ~[ ;~񃣆@A(@XS 6kvB ,0Ke !'Xh f _ n(_";\їǽ )Oj gNoF?R(U'` <||<Y>>@~ޅP `3x|1ůߘ'@D%XNO0`r }$GWo0O j@<2O03.eOR3x|,8/x)0xQ nd<F¹ŪX5O<-4 yx9( wE)E= \;>(A'|tPOeX??s $4P7y# "n`_^[繸I'(3W,<Ϸ&O`J` OuKŗpĿWy/ hi'Oe2O   ekRgʺ soW'Uj/ "K0;tp2Opq'PADǕGmv $V%(i PLU<MYK!_O y@+y4CE)'``T_n~\CǜֵOyjE`#Wíny`33i$ .KmPi}Z'z@Bfc@Zu3E%Xu "ȅGKбq<9 ̤"[8bځ-Ρ.se wm\9T||yI N'h@> hb ':at<=yҵ Y~LC-qG"( P3EpH i 2@%'p†%0!@;߃ ;"8/ Ј(4` kJ!U'p8 ]J xD OEp' i0zyy8[3 2O@{q-!, l'cʼI_$N Dbǜ:d?P8NUfݲ^qoqE2z0OPOJF~-XH?09Hq>xODc'uY" S.6C,v'l(?A#9{*OC` bYOiS ?y܅р/ejΓ! sj}n" z"ǖ@~<v,H ߭< H`,U~pŏA x!x"7Dycw< LJL$^e'«'~|T tAyI!c^5n܁JpŠ0ṧ \!,K1$T"(`#<\='X< %-7`c*IT݇|M*xD O.B"@N<; חǗ"h0Jw]+D`  G8HxO >XL<[aVtxA]c_yv WWQRLaȢ T|_sw,G ӡ(49p;3X (?!Ov :>[j 1c.Ʀ)">6'3k:獍Ai*Eqx3­A ONs 8 2ޮy U9<8'4-|'Xj),=.n*ۚ)u|DH jh>_0Ow=CdzOi 逳P. y}:qp<~p H<?8-| Z.̾P~h8 l7Jo>!~e0g(220D_|'rQ~x 20'}r*h>P<ۍe~.>}OښW0P8p˽ 6мp % t̯+v1OW OP}_>]US] 0O$Fg@; f\SS% |'1xV 8McW. ph\>5p)O@Ē(`eOylf&P/<4@[.OݝUqP Y97y/яA34ma OPd0 U qo'wE'gyгNo ''HA@8A$R|4uߒ̟v:?&w-H`+JW&"!*O@ LE?Bg.SA[~5O@"0t~,RR$B(') 31B>UpQ'y]G/F :>٦) Z}*_ KXQ`n䔛(!X:~.V,uJk"~ YK7= ⦱BzV,d &F"f)/Lf0(ttz'yn O"֔C8Hf7Vw'&O O"xnRݣ;_+" afk a(E1|`DzWTQ k•VkNe:-'Q2tFR"4>uD4We"?:w'"*f:/~#쇑YO$O O + D\SObkw'@_'X??.&HOPбNOpIk 6{Hqf/~&:#W8}^N …]`?О 5ݚ*P<%XIS'J/*@TIϿO4V 1L"`, f8 \"Oސz'X Ah L`hꖹc;s5y&V ` Pi'5=\@;?aWOUPIpOG/)dD "8m(Eh^qU ?9VO6"]o*3EqϨ*OСY'[RƂ5 ǟ=s*(a2QBq>T>F:۪u%%35xf?_`:reiܐ:&O;`E,X5N+v ! \3<\[W>-iB,Lٟ_Fs Iԩ(b;Oᨙ'4dp1GlDdŸ́)[V؋kQ74xupi=ٷ p^C5gqɸHNuP<˛ I(8 b}Ɲ [bU \l\/{H!QJ +`[\3OCؘ<A}1?8ظ(焲۸HRrӂ^}C#L@G%JaY^jmV"X|'5O\%.k@f4NB+uvL>qY_hGUdqY<~R)ڃ;bGjF{-t,fIǻuM[%E h x t<'eK6RN?m29]s|'ڊoD @xExߠNgksPwQ AHd aL"aINfQT Jf)V8{,$`"eX(F0= ݮpך6ˌ?d@/H<'FNdZ| 'P'E qp͜ 0XěBe+ FOJ8 $A*J8)$Ɍ=FDBΉxjݿO@Q0,'4AĈ` >YزFؚx3ǻc%뱃T.QD* TZcP-*ExYqr$KCؚg'_T@ӄ8/7b$h'0yFKx*<$Z RmWVQC_OPlU]'? bVUSH O$<~!\=>%h'0yHOp9O ~''pz Tv!4E Z\xzpY~\7%D^ 9/!w[T4TxHOpO`YY`5@rFo ];H  ߿ U< cHI4\'pMl2KN~R)K'{4M`nLIODȧDpa~C\>{ƅ`4xHOPr|;HpݓoԻ] p0@<^u ̢Xc m-pX4"h;!htU~ 8?O yaywNXj~o %,/fCYf1;O`-xZzG$, g?X}x`$T'L<.ҸEE<єDNˋ {UwRmF *"1-s {~"( c2l ~8p5κAsXPm%C%S(+Y5XPIr!KPV-E^^_Axv b5A 7 O,4_a1 r`huK ~@0r"8pSӳ}O`+@ #K0q'#-G'wuX9OFrkdqQ'b0O`1$O~" K2_d=Mg}_rQO(Ԗ?H`@4XOKӵKCTSq~|^Ɵv"]ج&s<_yL^,e@-s>|QYK!FbϢ \:I/C녣p@Y΍Y9ÿPWys'>ox9Z.}^y0_y~'0v3z ^gPs%'8[ 㷷8`<$(QY omQ|:5qC  s+h 220;xŷ[,[b{'^$S05@e5NB*OKx|"IK'8]'ߜ CD'@` ԡT D&O`&f4y);Zx;VorA|ߚ-j~_ =bE^W|63k2\ HbX 9O";TR' oYיt33,Zjk1K[y HưG\lj4Ow9˓E,3<^ 7 XDaV ٘X.l  "Hpf%hr4dIyf9.F(O5Ez. B7h)f`YG[y'0v +K KaU*GQOu<|J(ڇk4O`|J><<ܻ#lD\'_?s!Z$K3)@7TLSe)L iu94 mGX Nn7`n'y#!{'xfO'0?\,C'V~QH 9?N)#pIfJ_^(I]ږ,vm@%M@T^U@K{hH~[T ( < _$z)@EbY̔p~x]'viOA$O3O ' ~ p*,n{'x zz_ -̛I&"wGK.3+kF !DV 41<<}XBC%S @}"`@)j,rkF~"<==44 ;^Xx[ip4UP8'ͷ'COUx6 D ysܻOPoF<7Z 'O;8ĢށFCN;i' Ky7xA" U_$(Y `E;x@[xlM]O~'"<]dVd fq Wcرʆן뤞?a[~?{K1s5Lg?GDt_g(:Ol7|Akÿ~SV3OVDy=`xw-O@R0X<\Er A΄/rw")fW+z(4Od mFYm3=n'p W|EOA,r:I @{z[/ŝ{~"hw)hb[&=`R `gLn 31쿧o-ZY֛@> mnecF'/pzMarX)&EaD%fG ىz~`OrK hD(#g:SDǭR"yw~lA;j1AS"P<ciΩ'g E 36>W;]BɃ)x55x)\/ah3£Z 0t ^3LAQOi(pTLs<Z0E/f퉜peq䍑!`"~x9p:O"86hO L<ݖ,;ށܚ6qX6xpC: )|Kk @n,9GlJ'ܟɊ%L2YƯG Ӱ$ n £XiHD~p0fVj W'x}]p]`V0"A"z,@"Ȱt2+P#% p'yKrF#- RL^P~E =׻xȁ#n!U#`mdGPMy+a9D H/< .< d=ؚ> 0Z04 xhf4}=k"D.?6lDy-ؚ>@K,V~r͒xXr G^Rjȱx|nl46|"#[ޒTg-y Iޠ-g8J.xHD09jmjq7͑ŒtgDY:ȤH]pkSyua xk-9~j`acSk%ڂvM qu:m~Z2^c /vB+.o?'ƎşK`ǁFK6(-1E<X~Uc]o[U>-Z|^?ɟ"~1AuZEC&mKhE`S_ݮy%?,J"&9ecE3~ACBgEiOf6yxV BFSi'ϥikz~1up׽}- }5PNc^HPصO"%"H3(Q9\82D@cƻ "HdJ,< zvD-oend:t@"+<k`0X,g( ݓ y)/de̍s<;>0EXXf3``*䖪-ޚ.Wz\@z:\YfgMuZI]O(( ".%<"<#0ZqG y؈|AHnD*Omx"\ID >nHB=>K>K0zHq9,ΈyE(,i`hFpX])Lq#j>DT|&  :tTY+Ee]Ρǯr48r˓XW yJ="ZrN VþlP-}a s`OF Q 7qg}"? p"=|J7ޭV.p`R6 t0긌'NJ^RКx8D b> !H5C|4h+p˭P PW9UP!U>?,Kugek~ Xu~ﳾܜ)/D]8GŔV*k@g XL6E`-2+F k M&2v Ex}~.K3d<1/ _fC0+*. 8 f r6Р %0xm'@84EDݵ=ߚ" UoCx>Y[D!Kam]k! :[dZg'p zݓ_fFSVw?oXNm<՗AKehbw,yqon $TRjDpׇĠR@"+Ȳ^x>%Rttؚ'uvz4'(0C<ӔB+` &OP D!. E~|2?/T<4>XP O CE`uf@E K!^_n 01)J'7PyQ'fA~J0t_gQ9٘cr/FᵂJ8N" Mc?m#iaBsR sQ֏"?Vl^|<jpBZR<|SN+)x# 3$B D{T1{TH`ڐ*F%@'T\2'(upq<hGDIߣ^f2t0n"xИ1D[ dL'_`:z !>0F5fhk9@_\[TD?.RH ~EVT4-l F!i;PU*D;R& D ~3 Hzqcᩤf߲R/P#P2'E~`)@3=d ׬%?0/`WT7%): QYKA|u"DyZ+R˕Œ\>sU^Zc?'7kG& S[nIG*׈t5PI/@@}J1?=}z%[o %׷<)K>NIoj{U_7ak\X*E^v`.&;64O"Q@Aٗ|wŸzFZI;SpO%pZlN(V0 mtq@r}Y@2>zeWo3OW{e3?1Y,OS]F;m 14)m<%OPOp]IicEzFꚚ (&x:ù`{Sp%Oӓw%OpٱGwO@ ,uF >Opu}dZ1i;RyǑky8Hnu^vBD< #sk mti%?A7ȭ*"W?٪ <;D}Ɲ\] D]eOPxr-LÈ*ODP%< O<c4 ??Vu O -T(xFASI;8ѯ:;D/wn`Ŵe+$!"e 8R0h&EW^c'f%QUwSBX%A$7{?O?P Ay45ԋf>ؤ{`9x=d>A|?ze3'OpkQ0EL)96Q45O`׳F>آO0U+W'v ex EI;Bʵ'{m#ә'[ݱAFUb2 a?0(=,R`@P'0O 6 kqYۀY)iqkMD>ADJ8scܯXOlOp1u.bbOrH(Bqwx=.I'͓(=\HgO 7Nt J>Zǣ83 ޵q)l|T?}6`L$o'0O2e>ϳy1>UϺq|1"9Viydqd6xc5>T_QgW2A3MM*l QYxl4BKbr;OVrټ|x|T{ VTC#[O0ľT AhoH<3'UJ~J<46/@G&C/t$.&^1HP+SU=tN}OĞYJ*iԱ'6~T9GW:zE3^Įx %4<˿0U`ٻ|9\_9M QuU9 n頭pHN/|exl\/H> O#S$W9sܕU}N+N~>+Bū`DVgD/SB+♲ǯԔ/x:'%OhÜ4MˉE}(J Laͅ/:}۩J<{{ׯa,Us y'Q_F}+[xO@Pw8wS'=+^[5~'E}j'T X@A pI>R F(74$O*?.hc%.c3ʚ'A,QV@m O~~ 5Ѫ|6 7Ů#ΠٶWz׶gtj0E{U! ,y|W|v!t? +*vJʊ! O+ؤ?' 崖Op,A'ܗ%?&cXNgFϒ "9´%p(0bٯnPgo/;>">n0Pe۲7;LJ'س;'P Xe3,KOxJ PJ ;1 zh>`S|g\*T'hD0lOh3 in=Fx\+B==?(AN b~3_ ę͆dG$:Դ) 2_aoknTP]'b H3B% a0H*@|\SvTk>`S| .bEX3f£L =@G)UQJ"> $c)_`y?ϡ30ap fOMܜrZb BqWlOпޗ5E/4j@T1|3@[)IT@A: UV L'@(+qmwIGHhT`kC֒u> OzQ)/'0l4eFךO0t'50T|>2*Eg< N+hwA9X e." #Yb9 la`3<0vXG\<’5 "« ˝Am' DgE/J ;CAa: HB= 5L'vFZ NgO. 4p*[?/){Uq$ K,3* #F |z~FDHz0 `IC$+no7<0R]oMjŀ ]?6N8//W,m7 .ƽ] w{?#Ri>CN` 0/ :>`8mP>2. ,>%{ mTovF'c:,ynsaO72$" T4`QX-"6xܾi߬FIƺ0I 8V}q8uI+  o{R=;O>}ӣ`D*s~7Q%eu{]MYşwoX۵\}IRۧecJζo8?oC~Ep/wҴ92\D%Sk77}߿ϥC=NJͭrbڣ|OPFޟ\w h%)KR 1>`?Ty Jj }>_Q}fShKarv689S>YtX¼o6Cu{e 80'V 1pČ7@@NW*c(b"\'O1s- 3_@g,ZiW.Z' Ub_w8z‚Q \P-"{_`JǷ *"cpNœnd{^>O 5>@m9\w*~`DZ`w^f>)q;$ 3>^A"ǃ3t?~ί\|XJvؓdz&χ^W`&T^j;H%xb`٠)'X*+H:|Sl'`=O{=;݂ݿbǏ X^T8>mkۺ_l:TvOk<1*^ |" ;yAnzNe K>AC*R Q.{: N:X PΒGۣJ o1;q$vѸ?R zFjeS Qp~uŐ\wP"#A >A!E"BA #Ap'XӶ"Z]YocAǗP `ʨ ~A? W‰WD*s}v3 _ ̩/pa5~{?ނkVX*v<TG z?%Sh?%gpܬw m'yu=OXIs6U>AS L}J~7?]@5Q3_|3$jV  * ѯq}Ŗ5vi=[{׳{++J3C ?{T{*J>!dU~JzqR_a 5@X xXO"@4W|T]) 4w/G'K7B*Anhrg~uV/$mUf-m)ʼHmRf?z?b=Sa>;7~ J`=A+ b |"va-Y@2^4,>#A`opU b[T \k;^JwLH+Epe_w)::%Y,-]̢U{ݙz^:?z~ zXlRZZ:ys:>xU~٩ۿSM%H_A`⥧iT8^j@| S^GJ[>aZI2מL|d>'46k|k 7|3JO8:>ATȮ]Ods`H*ߢO ސAJ+^[^ 'x/ˆE(8L( = p E_e?y%3\'OPݚDFB||-B(lB(5  XwdT2t|& Ȗ3.]K׳^v+/XHwUŏO%(B؁"0 }lZ>JÀ } 7\ +w F1P'pwWZ]}!q r" $7N\?3@@ 4#PP'F@|Gp\I%t#U4CPK:r):>˿b9nw4y4Atl=PBkgOW)z 7H({ pt }1GkJ\&BYv9]fkrf"<3Nvu`R~fk>'0|qw\!@R3; >A1b>GX^w\WN[.]y'pwLY=L~t_y9cS贡71lI}]>Tdla`CGFK>gqFHBkSv[Y|;-tFO`'`V RJ`6&0|Z;ΒW;`z60TbVx{8+OO`t*#HȩdE+l}-CJeQ @wFQz4^mBP(7>!n0.rpGp!Gk B v|>{hU6ݩb]l T~$>ւ_v}eP#z2 $RO`M.NOuKǨ2nx?%^+NkLޟ4s xw )GDsծ@PoTmߋ>>i%p{Qۅ?!>'R?(N3)%TK_'0znr*Y '0=h/aۭwr"'{Pz?cVVf娮Y3ڑiR}!n wQn/~7 dE1At1}~P>o$nT;@VYRU 6~rp* U2 TK_'С>x~I 0_6 bBsPTC~8';‰]w8 cAZuTYAc>  L)1D*,Ãd9:?RwF7PWO jT Lpp!4]`uS퉞+WMxLAZ@nDq  >Jy0 <0[i'ın\x5 ?ZP| r}ǟXBzœ 0{|{A>\8@1hsr8P'8G=BYr_g$iO (0ew V%B'9k2T8'jb==!%ώw쀰j(7 ۉ2}ؚo6mc/ׂ/0^B_ U"չ @os6Q* " 㟎lXKrq%|ŢW<oWǿr:g3`ecAOOH 'Xkܐz4=_k@W@~['0,ΰ 0?1b' ^c\ϓAsEid]>ޜ.ε;f z޾.K ?T\A`7)='7Z φlG^bB|6rqf7>JJ@&2-|4O >5}Iba5T5>ޖ~`oڦ㱏)әExkRAldb* PWpWqZ<t9khjdc}?n+|o(l7M'5|>ADwXSt0&pӰ9'Chcf]R=AGhˎM`)MC]>

hzOM OxCoxP Jƪ'Prq# A|ɟ(>o0Lh,x~t <@ } Vk$ȥA#R!U!Oφޚ.Di/zL\e>>3pT,#÷u xvAMC5$kZ'aޓ>'}?Q`sdO⤽4ɟ(>oq)=b *>fe[2 FTz}]Oz$8jÿޚ.K,QOόvY+}$Mi@ P|50Qש㿰=SPm,ٶuh=&.xl"cbx2bl~pĴx^U~h8]+*|Dg.8=P<󄖖#ZU|KO|%(vSV걹,V Z@}A3`踎xvsW;#;ڶỉ sg.~7f!V3~O,bm+xklw 4EGy%<@< ` 0?: J/$"B͵|86e!l2ZsPp3:i gn"ֻA[So85ymiU8PfO[Zå 4+ +HAxj6M'hZ`I%Asm:AZl³ +'X')I2ճ&:GGF*`^4߮wsԾ~{7l#8>i[m>^(q Go@Ъ,d-^;+(H^21Y,vgz.>֫1g:{k_лq;?߼(H|ҶOfR>*YGolD7W~rQ>sWlUh%A|WT'O՚51 V7;xO0\:IF2olFU\z ݹgQ#٪0y'|kEN@Ӭ|kW%x/>K e/y5^}K|k#EggH*wbsi[pI%9r@3>5}n$[;x `vOw'%?plo{yO1Kc>Ai@X |+Alx[0cAZ=@eǑ1" e! pu>_!f8y|if__AXJApW \0@@0#T'P+Z }%`eDdE7xw.Ȫ.[՞z֔8\8<_ *hV ,{f1 k% jOTΌP/} RnEy4^-.qĀk6)jkُ͸X &C,W)܉j*DqϳAP)X8puMw0@=&|zo_ƉD F Da™E|h -\~pۯu|>A5 y CM QVJ IU}[]tio̻ }&`)" ԑ,皧{_64&u '06|7 >bO`[։ @3j>E*6Xj>k>y}z>A02LBBCzhIb%ncc=.m[Q lO`AЉq) `is0' 4 `>+ GO`['AC6@W O OO@,=]ֲAC˦e3;+=^ GY4 1 =7lc1 6T;1.@ .5q=A@cXX`t:cO O ;) ~q@ƥ  ȿ >w(XGHkأ@7 b}~mpǶ<%zdYz>'Ptɧ(>AWO]@pz'ZvD³||}$5J u][ ſn+>Ko nN%c~~>ARXhHh|*){{t|^^zeW7' $ |Flɱ^!H]Wre9⑲#x^zeW7 Ġr0zD?N lCvb e٘.Uw '/gs?O Fr@r?'sqͿN;>%ik9ΡKobkؚo6booT\`&x%]_ρQGsvw ʙ]pfh=#" VSɨՒw;,zO`Ul6׳ch/C-UBU|ٰXkƇAA8) ʒT2\6-&PϘ#RW2hMO Sw?W{y;ͨD5v>AAn:yhp;H5=O˛Op4(=aj0 恋(銆a]'`` ]^\w~̵eTA ,mSɒBR@m#A26/Ao[zs[ӎaW hՁ`N<f/ PFRNdIwG?EriOkQ^-qBrZ5~3[\,~Fj@;Oy!b Fy3Ih{SϮw/W/L&l0.EOW@o$|m7 x _T2.jک^|\2hhl~&k!A{Ӿ~ORyd~do#! u4u#qsX:~/ -ZP|=0T)`Ə'DbX6 Mos:((:O^^M!,5]y`\~]|?|}JX '`}r App@ ַ)A^kηyaO%E_jݮߟ#F4(J:@Z7cM)`Nx^੩'{% `" `뻃LFEv ~k=0^Vxн^@I !*T>B1IyOx؇& O`5E9 P|teL\y.E$_U)VFP_){#XZAŠG> xOO~D* &}4.``11 =a'Їh?^Ccq\IJ}PY'k>v8|g`y$ %{Sdl$bݔn<#*10㏿{bE>lj>^9Cs!zg'[+w3uo2{5 Q.(__*Tcg ֳ'8U|V>L [`h{o5@@܁RmC_O<(P;] b bUyB87@@a/@{2|< ىaXe~AVhENpHX.8K(׆%S7Ӄ 0} \>zGOqkkVTݝ5a] ӚODA*Q*YG kE=301 P;]'(}yiƅ Dq;gR,7<]{;:p2$Gxsk^S~@g '=[f/k,ea37_“G8zOk07'Z߾W (sj>AFcI%aT5H`A NT{N P(n:iH>ʧWi`G54Qg8;ۜAA kx*zulo~KV>j<*@w_h>'V.g }hG.,Xuyj tٞOGQ7{ܒ'tП$^O2d-`uAvJo⾵@ ̺eGA4@z}Ӿp=z94׫OPQTߡM"ߦmdv. 0,8j)̑J:iu}x4C?:کɠ+:*n0MξD< 'Ahe^/5@2H;:84ʩ*F}_Jb&hk9\' 0&XvRqf#3^ȯ[*..\>,y붩G^_>5.X@2H;%J s|DԐB%( <w SUց^~F- >U>gDW hewX$Z*DLFT!0Q`aR'P۠Upjt`wu7 ͻQ ~pO~JѾoO}dtv>a@ g9$Iukz"\Hi v ~O@ @pŦ&5OX 0P_ oXre,?_sLZ<9 ɛ I RωaD~g+ʆ*f;Ah cYyJJPC3qOo4OdmǧL΅p`f >aOlsfb//x՟ u]'(8\/l'XgTCDMGw&@)>ϩc-u<  R qG}TROse7]$ $`@ӯ00N(v>$l?ݣ` â(JUnoOc!{.Fsy`mo?r\0 Oϩؽ- q>mSx* y8狩 ulW UH ٣eq5OT=~c#OP ,͓\_D7Ykc{Ce#Y Hf,U)׻3RO1ٕzJ:rC09:5pp ?݋Gw099,8 r&GuOqƓ'IF\[ >qn6i1r{f_M,'!O$ŚX3Q?!`a^~"$~ΰ0jUZa\On0 };{b*U!'1a$yLʈ!Zd}\"@ /@ŗ [OC1@wj*&!x7>,'Gj&ٚQ0Ke]TAVN0Ot: +  S 1AUAkG8Ȃ!w@Ɉ uk(pt7 e>x38 'm;]4H CH+0. +w,`$0rݝA@Z* A@?Qei߰N}@ uR!"ɄȀ$mi>VVV}ZO@~-Ԏ|@Nt+'p.]c5bp9Ap׿eN#AOb"9>(795=v@o{T%O|]o65B;M1ށ (`d044`\h@@пCiD<܄2ɱV|'Hמ`od{.0좞E[NGf 7_)/(@"lNݮOoBVv9 `OΥ'isqye˧Sv o&Z^'\<)>mm#fp6]fvE9j wP*, `A gjBIl>5 a}gYƾ 0 ^/f˧;P ݋25Lp~lRfm46m[#Ɲ|nHAR/ K~?gRܻ)aݎ >GZP>L(%65~f]ZAT?(C (>]VϞ+ޣ\ |X.?.ծH[gn!ϼh}D ZCE aCfa|^` `;_+a/}col@''@=>z0$4 pQqFxtlWm.qܗ}ņQ:J7AtV^4z웝߷yxCws5r``PpC|IS:}{@p Y >D7ev܀k`(o18 9O+hߘ^wWlAЊ&+>!B ߿c&J+4 OT@@X9pUqa%lQ | +RgY>Zl?f?/. D@`7 pPxًV;CdX ` 0]CccVN#> @p Wuҩϧ;yeS F o4UzD~'7p\ W+?2U ' BA_`AFHe+ڨINNj=ʐdAp Og z)g0=҅25V!8mF/^&r m^5 jfiRCAPMd5}[UGh Q~_H;%,ėdqr8e83nQ&nr;e P?5??*zN[P~V3O^30Jrf3\2^^V5`/p,[q ~_nWJ ڠtaT#T*'ܦW78a}MZ f'ڝN{0K00 쏏{F ?wP'0ro]APǝ-\hTrвzzp+cq_PďCer\7vڼp$_P|m^gg?ZNw޸Lrp:p.< F\hwqu~Օ @PǝB4.ׇ!SvG+l׸bEow<$QsmB5ڃ&rIPc '8H~j0#rA  @#?v;Ҫ>/)@@pxÏ;C*(MK k΂h04yg{0NUtrWl+ &^ K߱mF}';'ۚu~$T~jvi[= X !~z @ֺ>AՌ(^5 A \~k<6.:gڌ+z%#l>F\W~uǃڎO}n'(޾|͜vo jzO2'~BHQoOd& L}hS~1N^\GfDPow2Ch; nmjz$bSm~Φ'Qaʦ<`qbs5v nRW%ՔS?jOx`~m@5'HSg@>uUnAP960,GI!AΣJ"RUU*FU<:#}:F#”$7!~-|=e,DjzXf'>eOttM voO`/qvH0`XA5ꝑG'qg졏$klmu֎~i-[>pܮ;}.=đ^ѓ ~ӖL]v@Y6}́A%0iحwSwYֶ =~sZ>.| z4 a&Ejj ]dCr6&88?sW{~]m/rxmNW|=eo-)HQve܎O~`m?žg }͵-c7|0 Cޜ;8W8|$m*;u e9_0<7~@ mJ.]pWk0Q)`0 C%^i[O:~;]7eϧ:]+[sjWX ~km?r*B @ )an|EO<9WvY g $={/3 !;⨶>rj,[xl~#=(,m)ڕe veK{|vra}ȶhw/>n~ڮޯ+gؒW g 鱽4 U^ |5. c@+eSnVO;iWj?vjW4o0&d[o0[o_oU,>h8> (f%5v`T3fJ'6OHJ9BгbwUCϾn{;b?76;=)G,v<7{翝O0a(/|Rd ,T^'0R/ j7<},xl~#7=n*UoWbWo~jw>7/*5; RT IJQ>&>p}OPeE` |Ш^?pG:'z8Lj|R 03_?`JYL@vr,dXA-l^^9['SQEr|kp襏iGXhWƔKv k/_ }hnc^)'Hw iyVI3{3p?e>Dl1mh1߿n6[J}y4h.@ j Z SA7''a'p0bNuE<nk/Uԃ.{>vL B>붫/pnr? ߚaEr z7t3W|RD%Q|SRBCKSDW_8-u'=mc|⚽ώe;:!E`n_RxIf}_#]O\q~M'-<ހ@#O$P|QNz7 ZbSGԝ촟Kg:k>'HELw($㣔eWC;&nOۻj J,`>AN%󢕳#4*8R 0 Y0zzf.'(I%&=G}vמw_.{c^1̢E31ip.aTP|+RpGas*I*`#Nk/ۢx+_K`2;q &K=zhBw)7pFܲ>A=t7?m1wqOA"z>z?gX|'H T "w6ybqNo** z|axa|+lȊ/maE-S T4[0%se#",BWP PB?fՎR_Q|?OP\4/}\N6%T6Y`m$U+YMA~gi6׳eF BT}u]k=6^@ ݋~E>WpES7'H-w`O17e:c^c7|Tqe=hףmP9g͋ܺlzg^GG+MSKo'Pg{SL])`%|e>a=n;;pV?q@` x^Ιopdld/;;~d[KN+ j۟m8Vz=tNj*`ނOW6wݮ0LGu9TO 07iN\1\}?lWA02m)U W-S_z"m'u.q_ GO o Eȼ*.o>B/6vzc`^[zh>y VE}8㥫OWzI/mぽ"V/ >`"`٦oLh\/&=:^|Ez#mw`lp}߇2t>c=m H&^@.ŸA}.(,ߓgpo@DpLjGjǷ4~sV9(bY'A+|0E@.{9``Q .(l{}܇_(()ҠH4F|34sw'8) PcO@|8Q˃E 3-ƀ{mb .(*v77y>00lcupƽ}VژN~|P*/ Lw]Ш@878}z] <*AؓI\|ri^}JY{.kWsyuwV31/=YO@*_)"Ota<>Hꧻzʶs\@q,PeUmsgOy_6,B_A`ڥwFGe>AJ `?<|TOOrKǞsN]7h5qA{]4 P,׳P{ {^Ϣלx5-),߭N{(AS@T@GB@ xl|R2F>L,?{.͵N⨉dqozߌ ^ȿϿ1 A?5Fk X'˚(m+м\z0[2X~ײqn/Al-El]@5@G$ T([ PZ;Vz{忹vM˲^w\7H#\yPP.H^AW} d˷a^\aS՜dmwItcݱiǑ;t'J Th[Sz҇=' qm3S`)=eCX6ۇ=kfOLGœY GJ$%&©\ax_{M'v"U NTӧڤx _hl3[6V$ (%:j7guR] [ 0 QG. t w z[@prj'ބsLpE7^`g l!xWϟœ>hi&^ 1x2AӅe^s^cS$ ۟Dco]N|+`\¢ށ'0'(M·^T|QPN|)<i[m\$x~ot=&4> Q ͒ ?yQdOvMT_:&&;box8fCZHS!_[_/6Z1lv=#'))T!Gv`]9/;pg|o*zhb!wm>xiqq `6-:|(H&(Z~ 1aφ0A=ZI V-|qH%5,NP#\T^> 3">SpJ-z=sI e$6GGϿR ~16jTȿ' pAZ T>>Ao\OzOAes ~~&<Fр9W*J}Co~|j*mC'A)7Jcl%}.eJ'|ݣ0XN2` _0 Wʳt.?5~u$7za,YgB*y>A3 O8DzoT~PZ"ӿ U"2FݏEyNaܾ)E/7s^דe/8xܳ Le#T_g=+R1~򳴾 Sl@P߫x[*M{Xx?*FkA_蹱|6F?ǹ'(9ٜn,;moYNF~[_^_/0-֟:c9 8=>)A 6JHv>A4<0l\ONw Ԇ^/uw6|}˹p`nz I2 k€BlE| #Z9]'-ѵ t`n:2 $ `_d￳MQ_.s1dhpj?{x6dY'X{t~֚>V*)#$0ځY ﯢ߿|V{(jAD5>Kӓd9{vQ,7[wia+7#懰Nׯׯlw00] {(<lcd6c;!ѕ&L?כ\[B*po|Cs&YC 'À@Pjʃ&'܊xzf`EEmH, iJhڻ_/|> NA/й'pA9i7v{0B.=/.68>\sxR[q-(LGfwN)QRTb``J(Sr?\'kw"H92O{8}8,&s8*<fOj׿UZAЌci[:Am.$ g `1301$hegC":a: \C'4dA;_5\AcOv `kdG- "©zیmxmhR as1`O@8Qp놳mѢOF1ꇺ'Co#A:h~bXG?g z̤J os@&O3â۷z]`1W,;m6/7'h -.Q:_hb3~O&9Y6ȎRLh cG1~x?=?Wg[UQMq m_lwA2\n^!~$ʟ!Fm{{k;Kf3T˲"bBt# 8nGa<# ="=R¿Ia.>AHgNusF{`՚bݐ]pNDGE*h\bsq:)*{LF |[<'Q.mho{;(0=ZG|52;0|Hƨ΢:y͢D(% |EAh0+Мg!P8!oS=+a|ZI * d9pA}ϛ7nZ>?0NӃ3 .'KK9~F1xwT)ڿݱ"H$Hx1S }II'3l'2w I`毦ݬ *H"Ɠz(A` }߻`hST YpD٨)W6ˁ kTe*߁d.)5ׯ4C:+xQgk! Z~ni]fuҟB;V>O`~?w`ot$^۞4$/xB, οHx:'\ퟸ~+V|Nb!jL$mnAgz8N\k59~JIR(Rexn`'R9_EX+^پ-wcվw;BUj'Hd''(h\!Ώ!O+>->aP4\ak~ 8 Ǖ٩aվwy/ ‘^$q2i߀1Etۀ8!kEGI$!P:`Ft+?\~t/H!׿f :zzOq=jdA8 !?'1F ;s>?u;u)G4?.]-pv:r׀u9x0s g۟O~І⻣u+٘HcE XH9pWUwgvڟ ]$)"_ ƺ5Wa*:_I@)f4 W83i pW^Af4AтP9l-5'iRll%I]Qƒ FO! "x sM3'c\?nw/$Ecv!Jkax7;ƃŠ*=e?|纮§'|66HK`G@$B m0[]9O 8`)fݫFDs˼oo <q!+#jpRlH?z+enZzP66s&MmK@y?u2 Z7u~\=Ĵzxޟ`TmUȨR=^8pɠHbbͶ: ^ܧ]~0F[H@ 1EO"- fRT>m (_ rEEkOx/FRiL@}T1v)7w[ Io2IQGѤY2.~GWbugaal@e7IOa 4Շy ;(Oz  GP q; ..&@ѣ/5+?4Kx{Y"(|/',$z"-c`wE[!7⾿4p[{p3H$(&~%BGP4un/(&_p|Uv8sv,vl>A=$IK%T\BE|E |~^U9ȑKn onZ\ pw$((` S*7ٖ1TIpj kOq_._YG  DW!IƿC /$PVܵS QI's?_)#Pƛ_h졁.wD~`|sL)! +ILN+("2b,Q< T4 AqWF%!@+0Vd1(>~$sg?+;?)bOH/O`;@c?%%ƨ7~t0¢'PcGӉz=MX;#^AH !Rw H(ʡ7 A|Kv s>]N2vt}6Dcu?(70Zxcڊ,Hc@SJ8װuSD4w]*«/_N]^$t <*+^q-j:2j!D.( 1p %.:xB!ZR[q|5C}?_j0C5%b_xtOL|M'8_c2RݷO ᥈1K?I 1)7H\ G>~c4ѷ^9>al ,+|-+PI*pv /)9ťbuqr!\#!C؊Е!35mnX1c#ou???+D$1zJ mZ+؍oC7tD7vMes?!Lv1 ݳ2~`+Q#@p?Op!./>ןqon˛ oxO<靇_7'u ө@g)P[̃ag'a F#Խ`Bf:5̗V(B? L}88#9Sk`'a쵱#sYERWBqmh[8tc~W_>a+>Oˢ J͹g;pz`  рԠ  |$ wǓA &;fxyI8<ڧ?vXB~-Q>Xu:| 2, |&YVd>~ xy93?7xvǾ`8E^ݝ'IFhڤg|_vXB(b=eӴI{[\qc$Aƴ7slj' pCZ; D$hDF'u3_ _BcaΧ S$(p1MԔYJ6>oʏ+75$Aˉ "&A5E܌ҁ9 $*1({z0-E5>t2bW~|Ay}(lJl6 p{5{&Xϕ8~CmM]A4 g7@%t0dp @ӟ@hD.<&HF4*d'8S{2ͼh=Zrh{2ԙf=q9FтUgGOGqM8~Xr(_ Z'riN61٘4bX4B\x CO0v['kVƔ(JHF=:q5N:y7_;16'}?-l,z@!ѼJk^,E\G04K#q޹F~ת'1S\'׽u{Z}zyAM uXha_:>-̔?Awū.. ca{'Uxk!챱~~zOg^;xo/z4 PkFEn@t|ߨ?\0\$ kȾxh P7BN}9_7:>ȟ e7ʅ6]t`$P;`&qPQs7R#ɏa*14)_\.Tg6'?1O{$A$jGҵ ~CGd)I @/r1:}XO$imCTX7$߯ޟ #88:z4@X# pr>(\Mo@]D p꥛6V&l_n/H,~Ad^L֬g\Ti,ˁKԏ2F_$0GaH}O$h#~t=ägbẎ'-0bNہ7; LqQ|szLI=o0͗f2 b7H G-$Tn wޒf.i0s. A] $Ɩ$D}\1$ 9OSB*_|~l^ȸUn+"+i"v|eT8U C;w` chPx\/NX߹g}iXRi6' ~Ьg>e3[pebH1r2Iq">жG=-_OvOn)tM1 ѐIMXЋFg>Lkxsri|3`aۗw__7^ p oz1rf~T $XhSo@R nW?T >PA<RQ;kq|P"b o$;Hp 2࢚q5$cu q)"tCxU 8 7;Kđ@?l1XO8 ,о,+LA}PyQ?@{Gd^Y8~/ q7tt- XM;ÿe RGnMqՙN|N{*MoV}n`o#ev[Jx,vw HA%X,t5akoٿA>ozq8pClMQNXN,+Pl19`?D,q?-͸=zgF5OL635)WKa?Ȃz]E-W;x8??47^ z:M:Ggԡ ʅɻHxHe5$^Pz1ӑuZ极"\-x 1H-얛,8M:1kurT|\e Hf lL (gɟM9S^*9wNymYgڮGND9IP60. xwn?g0sZzmzL 9BfRFqPFÁ/_?IA*Pd@h޲\IU:v,_N]?ARr`u^b\.G?J?F,ZP<x@#Gp@dٸ<$˅Ty:ir00Jn2~f$pگ?] t:Gvpu;|QnEڱg eo2e]}=-o)w0)l5hk S92>Ө6C&A-%UǺ= BIBx8`Lքٰ>&Ya=%Y]oAsnΗ$0O$T9 j~Z ^  v'5@@F-Pr[ba+ '@3 *~ƧrI}}Wh!?Uw0ܥw}yk}Al_)G$ HB@Zp@ ,wqSmV`y '07HpbwOРO `=Bx8grJ5-!&[Au~E>ŐeFp*ރq̹>ڪ:hq`!&5xu \<ԍ|dPD$bGl/ew]wwu9µY0/f^r<=3}`H=*׼6VLG|tR "gŰI1T0^f}('pza`[k`c(OO3̀ѯ8*Rp؟ $r*/wSt<ʫ 3t{پA ľ>}3χ\rU|K#b%F ]~ ,)z--6\[ <_A`'?)îf'En<ǟ^rS@uG8=UXr8 ݖN_ruX+|${ 8lr$YKS3yY @lc(,K?/lz,|b; @ NaO0;N<ן_NӸ_s# VE0(\B  1WxO/O0-ϔɦdZ=u 0Ό3\-u9I+&O6چ_wcTb(`*s?c/gwL_wc$njjYam~sX3t<`wEXIn2QۣYYȓ-3X7ׇOрp#?a1&l?'hDs~7>ʬAZ4G,ƕݏ!P3Io~bƇ+ڟ 'xӔ]_wczoAX|M/a}3Xaû>k|8`0? 0QkL+NO_J}-OjǴ>ɬ)?P y8Ug6SXٵ@%2 t8~[Fb[!ܸ{9zaCE9`پ 8ZD u~ w g~Rq2NBQX`|8V]h:pUDDa z |=]^.p։֑? P)Tn/\'&r v-K<\D67pMUØć45 Z!ΩLc k7g6*?\x.rslgNz c@Z~Sm>3 at_H:|hբrc|!5%>Mk&K,(zoC!Xn}sC,WX0Pe!1Ȏf#=3`h^K2^ WX$ҷ3 L[?_‘+^c2<7x_ {"A3 mnrs|A~_W|=?ex'e%/>qĹQ9$H7)TN1#^b/~LOXK8a7KìK؈gA\F*}ȟÖI5"AJ۷? pҶ>s/ ㆢK[nnn+I X/룧CvA[poO@ܾ¢Qx`@،g0 R&kNGhOuZ_-!vBI,g[~dsd}^aGv\:ץOh'ЙϠ?2_| * h2l ]+5~p2?O ډXvoPl߾%,839~/eEx; o;|| ٻ]?xZ>V^zӻq|>>ap fN?=p\|:v;,"cBlo?ѡcV<^p ƠA1mN% =vv}2>"Ũ08jP>f#U@\ɟ xh_+اX[e///oP(a*"x7VKb]W*J~˺Oџ k%G'<"A@kZ/R]RP(忄~+&!zPvYQԃѠ^ S?@O 02}P Br.htp0 H-R7M#=ÞS).|OퟫﲢFUzO(O=Lai.ZwUqɅM!n-P*$)q,M yk,օ{|N;#JQHcHq$[ND KP77rg?Yfw՗ezpDϋS~Kl-WXUС)ql3 k`szmWށr6pM[(0/޳=|mRX$c!YhE=vb_*I(N,D׽E]%vYau8k~ʏX?k0 !tҟt^׬:ZeQf啎`TƬ0|_Un|]0I0sTè.Å]/BKN*?^J}G}]V\`dm¬=J\$ F&k1`T~gzȣ{ <86G3"f 1 b9'$diL͕ WLn HFHDӂbqŶ\Qo9w01Tgi_#Ke+ǖV+0IJF$_Z aeRS~-[.H\JGE1=/2? u>.OT>e~͊Hp=q/.Wkk!7Aw{K1ޑ/u#@!H` ~G5}e'E?p{zYtE'\cؾ}'18}{0+WSXDE žHAHaNBH,x@+\2Ηu#+{>j[ W'`_b.ۗXG$:--l^+ oeNh7癶_L{:\+RhGc\׋0K, $P}YM d~d N ۗX/HV yس1pOL{y3qz\>R˹_8?tNd'̦4ñ2%"9t{~{P}O^%p,JxINExtҭ 2`P?qAioW NZX$56Bi'5 ϘfER| G`&<||LY_t q~vk58ӓB-Q)*_ocAqA?A:$zS&^&Nܫ4/wM\0Iv4n,#waͿHHv YV8{~sO*G1B(DiEuiJN^MqmҤnNb^^د_^Xy 7|p5{]b41';ȘWeh|(7L:o-d?wᦞ__Y-} bqP3 z'诰\kJ"3lJQy)cVL[M (ǻ)Gk+n/K^r$@FTd3rS/l$(O-p?9x70h+b3F0 't>˵SNV>_9.JW+DeG$>rO@$ZSHy4x,lk~N}*1Q1d5_a~"\U9Ur2"H@^v `B $0x[[bx+BTT!}A$tO=srä-ۭ{Lp[p.tClkA ⥰25؁NXc$܍c/zFA9g^jx'- , q~,Pk|>/ʪLS f3_z_> * s!`"S(?o"0鴇$&$8|ro=XEo墜G; S{?-pl.5&UO[S"62Sc`V$l^sY->¼pyq|"O L}AZSH,""^_-د^HQ.ĩ AMMޯ 6IP=AK;L~{fA͌\Yfߧ..k8f"H4-0t=?UP,Xݏ0?뾢 D$ #`yI|wP(ɞ,f<F [lICj*IJ@#q ԔN7=/G\`aID đq`zEKzB`+do~Bֱq36q?f繜$5>*ŞB-T3F }fV<͆x'U.T|d3∨rP(k[;Hj?sR*J(""#_c Z^^~SW+Ŗ˹x-ؼyN\<_x1ڥnҧ6+?&blY.?jnf_ ?|(GxG?8z[KgN9ZX07BX/P.6xƱݜQٸǖ9 6|a4}C}~$(ǂ!zLX@l/% 8%ڂ8PLІ̫ &Mh{c|6~)N EwLJ˟:`g?O 3E>`,`a ):X>P@W6UW3{Ol^|6n:Dڜl>z簿ͼŴWgɇ_+ \V^ȑ  %EB"n\`?/>e'ýdz4zahI,|#}0u78 E~v|ݾo?WFϣcvPȁ[gE8siibUo3̎ |%BK~0}Ic|ы35B76 A;ɫ0B; ηۏH' q]:m2^XM'р eM`Y$I$`7NF?"Ob8V=Aci{SI9oVϏnBETGc|~l? )L'{N{c$(QæƮ#.;X.#'ȕ25 'N`߿{ Pm`ǧ.z|5nc"A'ɉi|~fO`<鱇{N{ˋ~%*K 2G:0/ߟ1$q>'eMH ~~ Cx}^\0 '6t~t. W(630hr G1n !C io)!>BTfe Rp:9?@xt~J \5eP 0  PFOU EOI"zsf{Loz%Ċ|@;Ga_Q`L?BZ1 HP'AgkAje>/h!EC{i:f9r>*[dOcO$0H ) H#lHлx^+߰ wME4~3ƺ>9 0MYM9hIENDB`glue-0.13/docs/img/nonretina.png0000644000076500000240000002140612322177142016510 0ustar neostaff00000000000000PNG  IHDR&#K tEXtSoftwareAdobe ImageReadyqe<PLTEttt-0-*/.-%*Ƿ'SLۑyB.iiiZZZOOOh#@@@도31}}}0 eeeF˅yyy.y%sj/zq,q:/T}TTT.B= ޅIJ)/,/lcK_XШ/*U0.0pWi !?j-ͣYGW9 |bu//ٚO=L2. !!!.-././778___//-<<={}AFFF1nnnIIIX!IDATx읋C%_ bF[U[VA(vmzCn%֫"o o+$@;"B|r̙L_[jjjjjjjj(&(&(&(&k</ݥpi\c/ _; tb[)ZoxaWllK7>h&L$Td&-Jt &5naĜ&_TB{kj ۂ4|ŖJE&I֚oD1 e 3;JI*5\|0UńqF1F1F1F1cav_lȞݲBäI<Q.*בW|=`RW|L EWϥh|9|*r^.&DŽ\S|[髾|37;2uT&4 E:gp|+&K^oTz݊ILޯcbHnrf TjOʷR|kU7ho`iO2>7iv ULxݕގIVLxaPƣ|ڛ&hKuJ{Du=cz:|]s^9|,ф(U1фWUSUZWՃUXz8ڤs7FV0Cnʚ5UK7a01^<IKk1ZMJb7*܆ #&:&|%ըܴB7$[D]7)ݪֺ _.(&Ɩ;ƿ{PX4:EUj-ńńńńńńńń5 5 5 5 5I/k,)lmCKӮ@Ťk}trIEQL-ܧn.yQ:{t=K½.bRks|QL(Im8smKF\-bRB[%s-bRcC#cdEQLjlx亯|TQmHM -bRcGӯ{e{ԆiWnoص摾5\&] b#DOL:'brG#DWL:&brGSD_L:%brGpΘtHf=osޘtF+Q3ortD+d.=8芉ֳI'DQLgqwIDQLgqg(&xDY%`b(m2;LE1#c01[Vg%ab()N}K\Q&mՍҠ$钿0~xfӊλ>;  c c:!hq'`(k`v($E">BZ>}Oxi2U\݂D2Ȣcv@J: ꁘ & i#!Ǟ( "1A-DŽ;t9ԕ tO݉@󘆉?>@2i pLâ,q[[$lo6&5I]Y2<~Q>ᢲ` t'1EJ$S4b@4XrهMI0h &q I7H.Rr$e!AQF:i |QЮ[9Xn2By)V$(&#ȿ"AMNO.&aJ19#xpY?@>8tKX$@%9ͪ,Ça?1ɘ`5XG `2rԄMgr9L-Iɥ#L@J\p5@|KEᘸ\d1ھ)SZ%T1"dMkmg1)P{ )O"䲪lF9d&;::u"LDbr( ]E ֧̐*vf9AM5cl \>x>z5a1yX>ߕ*7Y'Wfk`W( =LPA*%sy""yl$3{d R⛔h"jwL :&0ȋ 0z H>1'Bb_5L B%1q&èIVȩ8NjN%HGS1L01 ēEۖȩ0k!=lj|rg b_왍$|$aN5CV8LfK&#3AP3A ^ċJ>(Si\%+ge#yf;bKIQ$qȨێVHܷAQĞϓ>H`Bl07A׃xZ|.Fq.ek\H} }HkHqãvy\t @.&{ eC!ux>>@*AQ{9n<~~ !w.,ch2($LA"nTVZm-E~DՑLNp(ah>{5Q!V  [ qɄ pZq8 d0\ ;B$l_\RQĭŭ~.Ds9S3a2@߳$LFBx:p_- āt|zWX WJPz L.gIl/a2Zl%)fˆ:$ijv6%XwiW*D*mԃ,[䵹Mr^dO} a2jə ./:ItکP JO=&aZj8a4`VMԙ&! p2LMdNjC-պͧdO,?B]xن7>(,jwDH'd<6Md8&Ն95D5cJZ'Q+ % e G:I &ZLNzL!8H$لVD: kK^'LBZ剞A˓f_08mTLN SE B9&ZLNigX& i#WYtǝZoCde&$0?";dja^ߏf<KNDF'LPp8~A\f}G8 R"pǤ3ɤJC*HBNk3"2f`]j2 ռtdw`4+k:VD3'X#.1 0Y\Dd2/*+tLP)J5MQc b;%I*esߗ>,:ٜ0&clLvۖ$B9alOW'}ڛO1M k0iO.S%rBf {*%jgxLA"Yj]>5I )զ@F9 BTLKtI]XX?XܬXm0u`2y|re6&s42+ dHS O=U2 p7`L`G"51K2RgN@p( ?1y&De$)"L\.́hpk]#ax!L`յOUgd$Ϙ^qd z&~am&}}(]帅l(%cb=VB LV'ܾ(1ĞmVP4AN B^̧#JBg^>Jmd$,e,S3L!<[@ ɸOCLi[I6y۩dP hhB0iW٘,ovj/kLaPx憧 ϔ${T\p7 G|~BqP_ѧ6a+c2m+&x< prD_3hҾ(19 )0e.^c)B(=諵u8u尊ۤF^`0Y'XP}D}X!)GdJL* ;Ƥ|ώMe6&EBBR떒`%So{L.ek8A. )0O$8 h6ǩCe3F|214+#u,Ŕ$qTA< ftYKHf3@_T1!a2Řh&YԹ>8f #r0ݸļH?k a91@raLfUX^&`g-X"A,#Um2kRdpwtS@+wV<| &/֏%gOH-{xhL_C.&hX*LLb^/QgBߙTRM*dƫPrV#s*&zblBABW6n1MKBb$Єe6&A7`(O mB+ 8?ڍb)6g\HOګf^C#ĞLfSq8@ςP WHpCM>- `6:r+l|34-ihhVuNx%:aL+/^ Z(txt$ r8ZPj p 14-u$ ö1A^dDu$ &ϟƤ]Qfc-4:I'Ak͙f^L͜_&}ҳWb{:QSoS]&v.F77e\@]Q2|n/վ(' l&/̃D扺!Nt[`qӜC1,X~!Uy /!ힳH0Aj1yOw]2}5B<_  =B::Jfj>?y؆tqyb[y{*LHe~6;x?'j7667N4eI2~@eCZfxh2X2m g,ج 'o> `.X5Ltda(mI_7c2oD5x,oIRP*ߨcjInMiv>yyۛ_ՃCBh=28&}zbv+J<rijg'OY/_>Y~(v{&0T28 I 1-UVLk`qɡRN]t ,(Z,ɑ ZLtDUL72_7a׽Iz'$͡%5%YV>xQӊݿ@ h%RD!J##h(t/gMF燇d+xy^s"Ftd^u4L\I1Ƃg_y$pټWAfH8LI1Q &u+lKT7a4Eu JLxyKZ6n&E1A̠ō 8k?>~Zx]tvj-ܾ`⇗ kԈT'0; Q{` ) 瘌I?<ɈWm"3Y|'h[ȃʘ~<'i~0)v9&8EpQg5'5ɫe "g^NeM 3C &Rؙ(F (𖐗[ALFU Jd$)ۣѝ{ momAUQ%H$T,z<ա52\6E@ O}}9ER'0A[W.:о(^-8 ;V#LFMd?#L&/ &b22hn5\_/[ X;Z6'RCxwl}[ VǶ5 al|#]͝9O7K3ܾNmINDWs_j yh.18 HZs@P hUȢ_s͞U]b8&dWs<q~Eul%S X\t!P.OHh8RG1}t4]ź>tQ0!l8|zj,NNչM31c&7J* 2Gۇ:(&'sF rj=GFM„"&= Z 8;^풙PQo #DQL#$c2LE1&01@SN'q1[IbLcŘ-$nq1[IbLc t7ow-ƤP踝r,B?oSv(Ƥ X_ǘvʷ.z5at5=&`󍏭crN0ܻ =&HVc!L|F*a;Z,aCcvGCn:Wt($(L|QL:ZIȘgzM&mcZIbLc].Lu--|L[Kv0qR*7ˡ0iu)7DZ u9 UԻx~{6en,|0i &hߣSsw})s^Ep&胉)҃'|[<ń^{_ݿuϫ}^1wI=Pϕս^GC'TZ7Wz7$~]&`{2;D1DכҀwzLtψޟ6U"48+ɛ-='OGo}TDE&zc[)%wW0 Òvl;.H}$xӛI6Ne&H1]vbҾW-1ivIXq]H O oBo[\ uܤ޶Wm&ԏ*$p *$i"*$nŘ-$n1&q1 G ǣw^0y?mb8yII$?[>$w,WGn}ԧ>ޓ\:4䏁|RkJ|d9<ڴǛ1&ᕞprtd9a216X1T iwp[`vs/Tlb&S"-[dx|T|lDɤf6ޢCHea MLC6$#Sa28VNةVaheUu;ҳb#(kIw*(LF=],͍V麦k胋@3TGKoofkgI ~H.xdǒfUs( .~pMYG-nzmqb_<eHSA`2qS$ͱNVs-l7q2@R U-ϕ?VuWSdj"? gT܇DŽC-3-„6[J$I vnGpɀ[~(liJܨTqcBŖ83"Sl?LLlIN Dɂ0̣U. Bu8<?DcEY9SLز8C>BS5uHV$$BL%U@#/LHy#TsA\cfNKTu r7_[-$;_ItrJfvwh{zn&LҏmT7NȘ E Lo3 $i,B8&C_Id7Y4\qp B c2QG,L~LJ=U-$Nd11$0K,]-3R; Lز]:]l4qE.Y |'a⌕QǵV&Qv \)d"yfHք;$LSu,!Y9 tV@^A~m;i6F 2*s Y& d8SW‚ 4s'WJ$`j4_.KoWoRFtȐak\pMyɓ&9-'`̄ɬCíl$]Si]SSLÜ'p1]6;_ʛs2Ỉ swNIL,ҡsimW bJ?HmZu1ZBҟݝF08 Td|1/{1@.zXJ$hkY'ʤ\Ux aBݝSq:&l)=6l[SǰVt9v_XVUk)KR#gorenqq5jkbzRvW,T(MaBݝp:&ktҪULMhFI21CM-J|;R{[lpXOm܄"]D;@Ls!"&鞓1VM͢q{4 ٭Bvlqqiv`ӣ RyR(,..f] Ss2&t]8ӟuf>f^<j)\\Xʯ(A 1)0]`Q1bЕpH]ɼ\l AS@$h;)IkTl:`W#̑J-,#$?:ߺ蛞?;L+˙WcPx-Ob l)'nIl& "LBZG2MR! vvK &kkSaBdGiچcʱHigkYlKWe|zSh^DCh1!b˳tü}yPlm[`Wn`BI$$G0}7ɥ)Iop|rSsa HT-ɟy}$:&z?+2 j y|9rLP0#^ɦzmJR"9KISC'S.PKϢsSz>"L?=;@bBݜ̛I|QZ16yCJE;hKþGY}sGs= ,섋Glqu%%!숓Vny2\,[[ޕH1g)dt{I݈o]b" YujbQ ;ΒG#¤Al%W%˓Ib+LL@k`NzR#2~O'`RuyhkIlgjR:$GP(LSb!m'Bdީ^v"Ickh>2LPTqiudW,4mnt%7CTpdј>f%muy;&Y6ݼ g6QaB)jc>G#/ɵˑ{Q%y|ء3-QC;[!aWA &ٰx&"of'іDКEc6# o+q~5>ilPp|@%gu}{&Gh$*!ЙHxirU0Ax#4d+EluL lOx#8RIr6z&bG(lo4܁ "%  k޵qBkA z /Lgxrqg;DLJIՕ7G;Dʍ%])Ywr?rO-˷ǭ &+h`†ZۘMj45s48Nb.Ѧ4g` =վ& ['dY E͹\9t so ln}*o!iljа&i6&4qf񉘌P : V÷˩ H :2&~bKZd;;[!`l(JPE3ycAUЃ7qwzl ib﷫na~yB`'joߧUM他JW"Y[&F<(0Qj\Mo&VY`9=V@M9]AR~lGETh$hl@Vx;h@P4-lGb+xL͍Ք[L G{4yYoGwQ!j(2Z =ʘk a`?+xҙʷ&1E92lM V޴DTyۛx6&(E 3NVI`֊o6cދGyZد^b&$w NtT fh1i'89 bY`ү7<,ޑJ! qxu_ghXxƲ4.Vɰ{? 9Ę, Y*6Y8B(>'{:<ZO'Y`I*HS/C&hMu#d va/bNBI(Yd!pLf pYkx]G1s@QG T:gĜ<-IÄ- 3С5AC&p>V[$bĵVUğ@]ٓ, ut`t-r;A 1I~YĖyI)C~Dܺh=, fbb&ݞZ [md⨘Vq)O,Ioveh4o"aņ]Z\yj6J\&4{bdj3r}35nBSDb!%,BDl%Qԟ JR-еމOSj*]nP-G bV=9Zzq:kPp$eZ[b+r؉J! >FRe9RM=3|'nS`Bam[e?X6HȯGжB ~~K*~¡hҢ l}c`G]T1C&}'|>i|T{e7 yɝ1fشz%&3:d-A$@ ư_tڈ4AׯM%n; &9єx F>P 3K%(E)dz*GhV'JdQz:$a訖71)uFyk cH?-K[\!،z3x}^}bP!{|UC@l&f'bM! .b8ٍirSi18&4n]2*n4Zޔ(+)94ɏ3j@:=]k:O+`ǤcbtH ֯m3WhM%LI~dLWlGq=BJ NrW:ҙ#ѶTTzٝs!Gw׉s)ؚ! po. 1.*뿭k+h"~Y9+l$nS U4zNK@ ?)G:q[Q`J?q#  :$&ka[_|}AGПN$-iV<0SaH8 ;Z L*K-~:!Sb)|Ӓeõ hZk.U`n;&$1dK jJ:= .eE:3%lk" ӤOrc)d\(f0СJ,_49Wَȟv6x)P\c;I^@ؘTK.Gc+mN_z+I%OiJ8::W:|C&(e:wѢkDgdM0&\e ɈT1acRҀi6km:+_ TS[50Y>6vȬo׶(e&  24B?L)a:|b5W$8C&M8Mnm  y:5+l;P -+ nauS! [I`nLmi]a>u~[S;9j1LƤN'FB|`(ynܻmS׫i{, X  j>0y`B{ Hnii3)ၪmU1 $=pY;Pzl跱Ё >Ȓ7K W5g}*_g\d 5)IktWD_DSgrȉr OȵxQKD-~&;X1E>bscw]8 !yߍ@x2Lhӹ/ {ڶMFSk%G[*|נDQ{5LZd𘠬әI>Tjʷ˷x l<|poik]-S6\ QL`BS RYiP5\דPkX)^Odg2bJ! W!'S>Y%f 7Q}T$Ov۩0!V ,y6yHFSS}dŒ(xNq tH$clf˥ڻo>?+z5eJ/Ov۩+9LYHsao7)/ 4=x*)[V\S.&MA+۹_c |)2W5A B`YO H0I =|Suy"U% 땢9SYLR;*WaZ${19q<3A$XLHIN;Ky7^=|NV ٻZodЭ:rY]z}M'/( s9_K-;}LGdMtIK&2ׄI2hLJ1:?] ܮz~:)w%Edf==JK}Jti)01]9K*u?}" Lr&؂Ƥ IqnZ݆?w?Rc/mwV޵n;)&4O֥x¹ܗ3' L6 M8tʼn·&kB$B¤>=442+ I~!I}*y3ojtvnffnv)[8S} 1Y zPl<7k*bK±d_!YAgl4s&9_]:ύ!ϥ :$a`g]cU- *>#kо<ŐLBni802Y%ncO&dM@Đ{.rD$/kstH޾(;Yv^OSMb&[4E*WﶶO-?WE=Orސ15؊Z.1k$,L ((If`HeMT> m:DŽX4bMNr\ec|2ը1#V#2! /LU2n8t!1m:DŽSwr'\f{DX ^>= l9y"&萄 }גGjD?IL|DrNu =6Kq.dNd$~R6<Q+EIb!C.&X=N$VVV#'VO$T0sR|O$x]±\KߥA퓖_#$U`fH cl优\-1Fgk:$cqt۩n0Y'T^s:0{gLͿ~y±,.wkvѓ$bk*q| 9äNu uD9lWKNq+kiv?qƍ{L#t*kX }j1D&¤Nu ;^w@m *b;k{FyOt{%ϳphH`NflPr15:$`T;S]aNQG)AhV!$<|rœ^{r5&/χɷVK{9YHbW~BCLTwcb^ RKG5Օvן #kГryaSNqbV\@m[ecäNu -Hkʓ;n'1o^= R5d0= &Hl|K<Є[ZJ!9tѩ.1'0jێ͊`ڎwon\iў dL9ӤU7/\G [9?Wؒ/Pl=Ab j-(^x[qh4"'DeZlkog߿|ue}tEߑQZ0X!!Ol]RLX$_`^)FӘWNn߯ok6H- +GG3[7AĖ}I1aN-ܰDhoU]o{[_^i^Fl $[MXl]ZL&!+h:یi{X_Ife`IؘPմ8p\l]ZLV"zf2q$?hGɍВkdkl)c%&Ll5GW[z\6x9 wjGn雓;*L#@]v`Ӊ`"4N>F/1&hG<Ģu3F^7ރ'm.(4%uʌ9e-Mŏ9;x!pn|-yvѦy pGpgd+JBBl4\[$,rcRX~ˎMlYE7zi4$/BRy4RhOI5O%^yĖ.;&'h6=It4ڡ~o1Փ/_ӿ٬jLҜ4ՆNH U[؜3&7eD'kd ԠԊ?'j _GƲ*LvkgՍb;51=bk{eKI둧aϸf%7/7 W)1 0 3FB0%v oBh-<|[d|IokeU??UҾk7A`BaR?;LQ bEkD[U@!AqlŨZmu>̦'T-6UUo 榤6W?;LzKj;ޟ'O%uS~uop| EI}xOZL;=tjY;&X-l:hԜ3G3Ĥ7&0O$gNz8HfA>Ml2Eb97Vb<oK M?&&p♒ LZUMΣMeժ}(GFGЀiCE-9ԪK];cr0ϏKeF@mk[Ќ,*SŸ6?:/ߘZ!ARƘ\0Lz$ 6ʪͷٶҳ|)6;Pzz=1j-NRZց:st}c9$NI}ǝɤoU3 6# ѹY-&SSpH>.-'LT`@Pn5uoMY@QԪS-<I5@t)4$=<\rة1Ae9F٘KPOGg7PQd 7dג0;(&=ثR-ST'si+ɾ|w1AC;Wө1m*{W=gԔ<$P;<&`\]=o9x0 Sa`&;ͥJ} ΨIDATx յOUu0 #;+A Q$DL!Q?O1\@@0 " ,00oV;Uwhg=_wOuթSTU @#RAB A$.A$.AABAB A$.A$.ACEE\v)eH[ &+!*NRV4UDI0DEJU"ࢷ08h$&IR q\e2fs_7(7>TT][l;yh𬽤Y$78uhi1b{#o%$I\P,R] 5Y[b`ݖˑ;+Ь}.?zJ Zl6#ƙSc2z4o8SM`HbR+ ?'A-`>\n8]j4 XTKEI_:n`KiI ~ţ uf!H ΃`k0dpO+^$/?O1K7*m҈ 1$&@ uLq JIϫv~tc_hҌi2eY" .HRA~GRj^$XV8kⰰ>׾BR7Hλ0rsNrb{]={J{ Vo]w= BzSv_ꑺ.ɽa3ˍ͟}48,xqExzI `]XB$aAvg}ɾIъNU$FZSI~봜X促ZFf _m'>"^lrcwk̃RFh`(LxŅ) \˴rNM'ٺШ N_AJ MsaݾY2n_4 ѣa!w ,8B$0(=l@ 6dWZ†on$jɼZ85%P.PxQ B6 qa|]wG짙4h$F<F7/;_/g0OpIp}'L?} }l/{ =geP#Z/Ϟs];̩$. ܫkh/lc˵L}aʲtc#2/>&n;H3fZ?{?P50Z*˻g^~hM`Cv̙xeJdW \渚/{OqW8Zbop'wu!PK;.%S1h6r4'(_@g ;~ O@HnޙW֝z!B{A5#k>P愔f)Qc,,u"wX} O$vd,o~̂,L|RVwd 鰔 ϡŢou7^;yP* kn(.7ueqaYX\tP[ůbsb}5 ʂ|G/xEaBYrힷJ^p܏Z}:@^ uuV7-v[K 6ˁz'qwNm/t|zPPk8c}gdݦ3]pޤ{rG~ ;>T:J_/\U( [Ш89ҡ}]M\ xISgt\4$8Î΄HLGlr3>έ @}]7w- r\s ǂvjp3lKɺoo^S::58oBݳD[v{"m!z 譼Ul`[8l|cn%u fp.jK~7Yunexa( qKX@Txds^d=]- â{[f2M^o%U;+x(TV{bdv+3_Ou sTlVqi#4g̋vZ6ģu_a~(^)7NNK}/~gJVnjtqIŖjœ}hfgO5*٢"]\X-u>mԳU\?n$Iړ*5kj\뾎ȩ ݾ͡vۓ-J{IOmrc秸}>Z^gy/qzlawq*gG2ZeK=-z jrGz(cT0f z {>ܭeCq0豅Ui(jwqo@Ԫܩ;缜 GS vEs_(NĒmyoOW)+k5"'CNU#ԧXmi[ԹJ{?"2^Hqi$!|c e<[\8rEsFe dF`1"FgsmrF>iЋ[kl,Ɗo  с8g|/ShO?;Ԙ̨\Z%Zݬy3crhq+< tn9z]kE$W8/A!f8(.Dn077)N8ssgqa1Qs&?:/.,2`.##rJq\jܛyi!7 h_ϸq1 qWT@Y{HI=x6uLnh fYda(fOdNyEF$]8X)yI(=2: m!04KfP%3?Tpz"C|rc{̛HM9mD.ƂПHg](v&HX_7:lܖ Y?ĆV(F42q3.ɘ^{Ͻ*}"#3\;ۊZIݮ>EVe69v|FL{т~Cq|' 㑹=a;xQ]yh =gINL!,ˎ?(jw_6= _^tX_&tp vKY%2m'X܏8NXF\LOQ4>ٜdL J&=08e /Q;L"#FJߙbg|^Āa)2biV^(&Y.\h:9SN~X{'``{H_ z2ޔ}>@5(؇ Y/~^$P_~zC}4Kւ>w3np` [+On3,]á_j+J@PY@"]݅6Pq`N[l;pۂH|Ґ{Hγra= ܺa\C6b I1}uU=O*>h2ⰰd%]U[\~KL&7,w x/ɜzYfޔc!y8g-~~-FRnݩUl=]b?/9q+Wλ<7/g9mG"ļ b``7=[坔qLϹC|NlqaQZHwb>ҼN xCQvV}jr7gA.3(1s{G4vpkcK(+>*5ϫw4Վ- #=&kH3' :*5~ ዌtQ3r3..Lhk=gO{8ɓW$oJ>qZGjL@/\΅ڕ)1}`SP<3P*Gsr{q-ۃEb#1GSUiE_]~W/2b1ѽ`~nKEtQ;Tپh~₞K%glC.ʹ䲽gk;2* PςxyC_:g JQ\f?jwSc۲1qsF5/;GfLNZLV$jJąEFzdT&}a1y&. g$z#vLM꓅Ul56WjMɚЭGji}r{&Ǧz ΎDz*2Z gt!Cģʇ .SA zv\Y0&+y\ޜjQtVSlߌwaN_c Q$Yi)S^F3G\&^YܲQ-W桺J*K 듶+A3I|!p3@5qx)t8}kυ ԲGFz\M%ӡfWe מ'=2R_˜H+ cì͉Ge9T zBrRv)pN!\Pφ4!HAi@@58?kUuHPI@EM h\٩D q9/M&A$o(fH<*ȲdE& Bk, /.uuu,("߸bckbb-H .&21WRNNqkWMMMܞj5\ͦ2vZbbf3ug":]@ޒ&AD HE@ A$.AAAB A A$.AAAB A A$.AAABKQSSs ijnĥem޼_O?LbЕ^RRkge⫮~饗 RRRرc=m$By/T/$B| .蛽ꫳg޽{ƍy|x]vp F۷i&|v%K7;w7~޼yΎ;~1\{6))isεZ> ~]_z9\=|ڴi3f@'Yrer-Ʊڷ?+6l`H[6fx{kf͚5q`В9sDafggcO|0fdds+Ff=3O=mUU՚5kˍ گlӏ>(z<@ll֭[1n ^Zbʔ)L(ׯ_e˖Ç%X~AK@-[k+noj4qK4 ,^mb3 ixLٵu=^HǍ޷o4('N8s .kwK:c`I'4668Éá3Y76>o s͖.]ʚ%O4Z#N ޠkrO3pKT(bw"nӍ{E?TǎǏeWcƌ1ޣd5d,~YXXHc0GSw7Qy[MٳE. _mM ;sM78~[SP[ޣf }BT/m)!dQ/%^07 |I"^|Cn (50ц o-$_vZf,.-o،;y r)]3,^?d-A T eC}_cL:DHeeey-z,>1[n=Sֿza ҿ-1 m Tעb vƌD:za$S[neÆ u#rav=6e{Ŋ-um6;&\|9˨-aH0lF3GW޻wo?訝).seCbdn7"Cꎾ\Gc0WO~aCI;|;r{İjVZy ]/[;eYtinn6dfӏⰾ p)q *kUU 6LǏG^ؾ};~GUVVb͝裂ŋn??O:5`Czoо%'L l>/r,mn9rHW^y7I,+Ԣr333Ǐw(TU~B+W4t53h> V);w典Ksby.0blɶmᄊF$G$.Hjjj>Ó'O޽{{f(B A$.AAAB A A$.AA=/RZIENDB`glue-0.13/docs/img/sprites.png0000644000076500000240000000532212322177142016203 0ustar neostaff00000000000000PNG  IHDR<3˪k IDATxڭ{U?wfvK&QjlhAA0>J`J(mHHƴ"T" b3bTlH[@Cmk}fnޝ߶L~s{; MSƹR~7zOgg'EX`y U-<'KӔs1N1%`Ab Ѫ|$b 6o̞={"I+`'3f瀇hcJݽ{7@edY>?NvZ 5;x<~`Y_}zN4ͳ,cڴi#β4M}\X!;/<_ƴs熕 ;'gk[pv_n'jAڼ]);ߐ@/NeL52ք\m?..vXͽHsE'2/40X jE*bvo8+AځC@/08K*:M>Dn/ ?$(rHX"wJM\% fS`~=U'XH$N1ިqbLկS4Oa?!ߪkZ^͓ZPaf?(û(HcfQR~LK | vC/2bv #[/ӸuHƻ>n3틍2^VAxÄL;/<}D;$9heg6jO^Dq7d>3xJB+j:y\JAxS%PŸ H7cm)K۽R%;M;`Z۽oQADvD;Td G^gN^П|WMl=\5o5`o ($u+X2f :+Mr5^~i.{NcH*?ЈG%cG7WFaeVKnTV M "cbB y@!z -Z"h{6ߥɇSU(\mBLO'.ܣ>meq8zsl/19%[MJl>Fv+1=UD|aK%I+0:̀Ltҋ \Vffg?+CMU "|pvSᜣʞRAK@ZZd sgf\fTx2A tbz8OLj~j]^VIyVjnH&P4G^c"H뿕c_ROHZ҄FBY7kݳ.g әAB^vpn rW\;DTiBTi?{DwhMd1'Y{$}&leB؆ͳ#U_ol="ys!JC˘8+ IdC$E v19-$M%ȥƁ9ZWx7e$ٕ2܉Ĩ\;hPPQ`[(:N4aڮ.4:eKqxofsyNwww%ιuiMajL`84eu8GZghe9Gb׮]ۖ,*%ʥ)5R 3J!EgYM ՙ!,SIESh<̄-߄Y`}nLh\. Q5Ʈ쒓jBY?A?;v2`g!)ہ~ިbcNv6qXEI[K?1piN ެqPmqsf؉̝^2)wզz~UG2hSN(;7$$] q bf=?s=N#eOFZDLge;zh^{UFkf&ʤ^4'exwɯsB=%e]Nՙ׀2chkVHg+ի-.hLD9 mdžfܪl8IN ǹJnϏ zI-s:u DMQ7'M-;q5tJ H;͡X.h~9RnJ1;00`R>02h[tqrTK;sTV *b5|ۀf1~9H:QnvtFIt{9\\*^V=Bc6 EuYCB:U$=mjj P_grβnf!G@C sVF v} vNvb=hԡE8G߷=c8$ uPqNM0-gΰwKlV9z>繴=oa\GLN=μΖTkKC1vqa\:~nf6vK9 oN .]=0f,%M1х6uEIENDB`glue-0.13/docs/img/sprites@2x.png0000644000076500000240000000531012322177142016552 0ustar neostaff00000000000000PNG  IHDRxfU9 IDATxhdW?Ɇ1vLbQZgI.HvVPI)`T*I?dM"Ea;j3K5ÎBuB*, ΐo{y/yIf{{RH~e@+OMP&xY2^^#֐ƜK]6/`xtqz2vRt|/ ̯/vrBvL漃\1&˖ABB6\X^W% Ky6 xwܾeԃ1'#[i3ޮZLJ <F\~\4nk kxіxJM8F`;b > |/!E;/&]pJ( @O>Bl:YR27La^u|aqYm28~_wP2H;vr`8$=#u=ƁϞt>v/dY<Qa+xq6 ܇ZӤ}( vo~[~,e$uz%9@NvZ '+]S s^ 0̱><m)Nxb㿏eeps)j>*NJ|wFys_g/XfӪ1>>~Ksy_E9 * ߴ{GggNxgL@SmS?I^,`L6> |xS3Sg}?u{%Sǁ?MxzS)mr(U{Ge-)io$E|#<*yo6Fsx\D' +A_>ZHȓ59mYyou<(߲02.IYPfVr M4J#(" Cg >k஼܇z2^$n?3ۮ%넖 b&} ,vV]%=Rgk^tB?RT0Ӧm0  aJ XW&4'>R??w ZVp(?q ZƝ]moM:x{7 ^dϊ9{S~-]ҕ_%빹ז܇z8l kիFtD?7݄ә/6i ~B )B'(IENDB`glue-0.13/docs/index.rst0000644000076500000240000000302112322177142015063 0ustar neostaff00000000000000Glue ================================ Glue is a simple command line tool to generate sprites:: $ glue source output * Automatic Sprite (Image + Metadata) creation including: - css (less, scss) - cocos2d - json (array, hash) - CAAT * Automatic multi-dpi `retina `_ sprite creation. * Support for multi-sprite projects. * Create sprites from multiple folders (recursively). * Multiple `algorithms `_ available. * Automatic `crop of unnecessary transparent borders `_ around source images. * Configurable paddings and margin per image, sprite or project. * Watch option to keep glue running watching for file changes. * Project-, Sprite- and Image-level configuration via static config files. * Customizable `output `_ using jinja templates. * CSS: Optional .less/.scss output format. * CSS: Configurable `cache busting for sprite images `_. * CSS: Customizable `class names `_. Documentation ------------- .. toctree:: :maxdepth: 2 installation quickstart pseudoclasses ratios files templates options settings faq changelog Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` glue-0.13/docs/installation.rst0000644000076500000240000000400212475154733016467 0ustar neostaff00000000000000Installing Glue =============== Glue only depends on one external library, `Pillow `_. a friendly fork of `PIL `_. This libraries require some external codes in order to manipulate ``jpeg`` images. These codecs aren't available by default in some Linux distributions neither OSX, so it's necessary to install them manually. OSX --- If you are using OSX, the easiest way to install the jpeg decoder is using `Homebrew `_. Before installing ``Homebrew`` you'll need to install Xcode, then you can follow these steps: .. code-block:: bash $ brew install jpeg $ sudo pip install glue .. note:: If you are using Snow Leopard (10.6) you should install glue using ``sudo env ARCHFLAGS='-arch i386 -arch x86_64' pip install glue`` Debian/Ubuntu ------------- If you are using Debian/Ubuntu installing ``glue`` is really easy: .. code-block:: bash $ apt-get install libjpeg62 libjpeg62-dev zlib1g-dev python-dev $ sudo pip install glue You can also install it using the native package: .. code-block:: bash $ apt-get install glue-sprite Windows ------- 1. Install Python, if not yet available. `Python 2.7.2 Windows installer `_. 2. Install PIL, check `this website `_ for a matching version (`PIL-1.1.7 for Python 2.7 `_) 3. Install Python's ``easy_install`` `easy_install installer for Python 2.7 `_. 4. Add Python's Scripts dir to your Path. Add ``;C:\Python27\Scripts`` to the end of the line. 5. Start the cmd and type .. code-block:: bash $ easy_install glue 6. Easy isn't? Development version ------------------- The source code of Glue is available on Github `https://github.com/jorgebastida/glue/ `_. glue-0.13/docs/Makefile0000644000076500000240000001074612322177142014676 0ustar neostaff00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/glue.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/glue.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/glue" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/glue" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." glue-0.13/docs/options.rst0000644000076500000240000004027412475162714015472 0ustar neostaff00000000000000Command line arguments ====================== -a --algorithm -------------- The criteria that ``glue`` uses to order the images before adding them to the canvas can be tunned. By default the algorithm is `square`, but in some situations using another ordering like `vertical` or `horizontal` could be useful depending on the kind of images you are spriting. * The `square` algorithm was inspired by the `Binary Tree Bin Packing Algorithm Article `_ by Jake Gordon. * The `vertical` one allocates the images vertically aligning them to the left of the sprite. * The `vertical-right` one allocates the images vertically aligning them to the right of the sprite. * The `horizontal` one allocates the images aligning them to the top of the sprite. * The `horizontal-bottom` one allocates the images aligning them to the bottom of the sprite. * The `diagonal` one allocates the images diagonally. It was inspired by the `Diagonal CSS Sprites Article `_ by Aaron Barker. .. code-block:: bash $ glue source output --algorithm=[square|vertical|hortizontal|diagonal|vertical-right|horizontal-bottom] -c --crop --------- Usually designers add some unnecessary transparent space around the images because it is easier for them to work with a larger canvas. ``glue`` can optimize our sprite by croping all the unnecessary transparent spaces that the original images could have. .. image:: img/crop.png .. code-block:: bash $ glue source output --crop --caat ----------- Using the ``--caat`` option, ``Glue`` will generate both a sprite image and a caat metadata file. .. code-block:: bash $ glue source output --caat --cachebuster ------------- If you decide to add an expires header to your static resources (and if you haven't already you really should), you need to worry about cache busting these resources every time you change one of them. Cache busting is a technique that prevents a browser from reusing a resource that was already downloaded and cached. Cache in general is good, but in some situations could be annoying if it's duration is too long and we want to update a resource **now**. This technique adds a flag to every url that links an external resource (PNG in this case). This flag usually is the last modified time or the ``hash`` of the file. ``glue`` can use this technique to automatically add the ``hash`` of the PNG file to the CSS url, so as soon as the file change (add/remove an image) the ``hash`` will be different and the browser will re-download the image. .. code-block:: bash $ glue source output --cachebuster Original css: .. code-block:: css .sprite-icons-zoom{ background:url('sprites/icons/icons.png'); top:0; left:0; no-repeat;} .sprite-icons-wrench_orange{ background:url('sprites/icons/icons.png'); top:0; left:-16; no-repeat;} ... After --cachebuster: .. code-block:: css .sprite-icons-zoom{ background:url('sprites/icons/icons.png?p3c54d'); top:0; left:0; no-repeat;} .sprite-icons-wrench_orange{ background:url('sprites/icons/icons.png?p3c54d'); top:0; left:-16; no-repeat;} ... --cachebuster-filename ----------------------- This option has the same purpose than ``--cachebuster`` but insted of using the hash of the PNG as a queryarg it uses it as part of the filename. .. code-block:: bash $ glue source output --cachebuster-filename Original css: .. code-block:: css .sprite-icons-zoom{ background:url('sprites/icons/icons.png'); top:0; left:0; no-repeat;} .sprite-icons-wrench_orange{ background:url('sprites/icons/icons.png'); top:0; left:-16; no-repeat;} ... After --cachebuster: .. code-block:: css .sprite-icons-zoom{ background:url('sprites/icons/icons_p3c54d.png'); top:0; left:0; no-repeat;} .sprite-icons-wrench_orange{ background:url('sprites/icons/icons_p3c54d.png'); top:0; left:-16; no-repeat;} ... --cachebuster-filename-only-sprites ------------------------------------ Unlike ``--cachebuster-filename``, glue will only apply filename cachebusting to the sprite image and not to both the ``CSS`` and the sprite image. .. code-block:: bash $ glue source output --cachebuster-filename-only-sprites .. note:: New in version 0.9.2 --cocos2d ----------- Using the ``--cocos2d`` option, ``Glue`` will generate both a sprite image and a xml metadata file compatible with cocos2d. .. code-block:: bash $ glue source output --cocos2d .. note:: New in version 0.9 .. note:: The output of this format has not been deeply tested and we are looking for a cocos2d-champion who can sponsor this feature. --css --img ----------- Usually both CSS and PNG files reside on different folders, e.g. `css` and `img`. If you want to choose an individual folder for each type of file you can use the ``--img=

--css=`` options together to customize where the output files will be created. .. code-block:: bash $ glue source --img=images/compiled --css=css/compiled --css-template -------------- While using ``--css`` you can use your own css template using ``--css-template=``. .. note:: By default glue will use it's own internal ``css`` template, so this command **is not required** unless you want to super-customize glue's ``css`` output using **your own** template. You can find further documentation about how templates work in the :doc:`templates documentation page. ` .. code-block:: bash $ glue source output --css-template=my_template.jinja --force ------- By default ``glue`` store some metadata inside the generated sprites in order to not rebuild it again if the source images and settings are the same. Glue set two different keys, ``glue`` with the version number the sprite was build and ``hash``, generated using the source images data, name and all the relevant sprite settings like padding, margin etc... In order to avoid this behaviour you can use ``--force`` and ``glue`` will always build the sprites. .. code-block:: bash $ glue source output --force --follow-links -------------- Follow symbolic links. .. code-block:: bash $ glue source output --follow-links .. note:: Be aware that following links can lead to infinite recursion if a link points to a parent directory of itself. ``glue`` does not keep track of the directories it visited already. --html ----------- Using the ``--html`` option, ``Glue`` will also generate a test html per sprite using all the available CSS classes. This option is only useful for testing purposes. Glue generate the ``html`` file in the same directory as the CSS file. .. code-block:: bash $ glue source output --html --json ----------- Using the ``--json`` option, ``Glue`` will generate both a sprite image and a json metadata file. .. code-block:: bash $ glue source output --json --json-format -------------- Using the ``--json-format`` option you can customize how the generated ``JSON`` will look. You can choose between ``array`` and ``hash``. .. code-block:: bash $ glue source output --json --json-format=hash Example ``array`` output: .. code-block:: json {"frames": [{"filename": "apple.png", width": 128, "height": 128, ...}, {...}], "meta": {...}} Example ``hash`` output: .. code-block:: json {"frames": {"apple.png": {"width": 128, "height": 128, ...}, "orange.png": {...}, "meta": {...}} -l --less --------- `less `_ is a dynamic stylesheet language that extends CSS with dynamic behaviors. ``glue`` can also create ``.less`` files adding the ``--less`` option. This files contain exactly the same CSS code. This option only changes the file format. .. code-block:: bash $ glue source output --less --less-template ---------------- While using ``--less`` you can use your own less template using ``--less-template=``. .. note:: By default glue will use it's own internal ``less`` template, so this command **is not required** unless you want to super-customize glue's ``less`` output using **your own** template. You can find further documentation about how templates work in the :doc:`templates documentation page. ` .. code-block:: bash $ glue source output --less-template=my_template.jinja .. note:: New in version 0.9.2 --margin ------------ If you want to spread the images around the sprite but you don't want to count this space as image width/height (as happens using `--padding``), you can use the ``--margin`` option followed by the margin you want to add: .. code-block:: bash $ glue source output --margin=10 $ glue source output --margin='10 20' $ glue source output --margin='10 20 30 40' .. note:: New in version 0.9 --namespace ----------- By default ``glue`` adds the namespace ``sprite`` to all the generated CSS class names. If you want to use your own namespace you can override the default one using the ``--namespace`` option. .. code-block:: bash $ glue source output --namespace=my-namespace If you want to completely remove the namespace (both the global and the sprite part) you can use: .. code-block:: bash $ glue source output --sprite-namespace= --namespace= --no-img -------- Don't create any sprite image. .. code-block:: bash $ glue source output --no-img --no-css -------- Don't create any CSS file. .. code-block:: bash $ glue source output --no-css --ordering -------------- Before processing the images using the `algorithm` glue orders the images. The default ordering is `maxside` but you can configure it using the ``--ordering`` option. .. code-block:: bash $ glue source output --ordering=[maxside|width|height|area|filename] You can reverse how any of the available algorithms works prepending a `-`. .. code-block:: bash $ glue source output --ordering=[-maxside|-width|-height|-area|-filename] -p --padding ------------ If you want to add the same padding around all images you can use the ``--padding`` option: .. code-block:: bash $ glue source output --padding=10 $ glue source output --padding=10 20 $ glue source output --padding=10 20 30 40 --png8 ------ By using the flag ``png8`` the output image format will be png8 instead of png32. .. code-block:: bash $ glue source output --png8 .. note:: This feature is unstable in OSX > 10.7 because a bug in PIL. --project ----------- As it's explained at the :doc:`quickstart page ` the default behaviour of ``glue`` is to handle one unique sprite folder. If you need to generate several sprites for a project, you can use the ``--project`` option to handle multiple folders with only one command. The suggested setup is to create a new folder for every sprite, and add inside all the images you need for each one. ``glue`` will create a new sprite for every folder:: images ├── actions │   ├── add.png │   └── remove.png ├── borders │   ├── top_left.png │   └── top_right.png └── icons ├── comment.png ├── new.png └── rss.png .. code-block:: bash $ glue source output --project --pseudo-class-separator ------------------------- As it's explained at the :doc:`pseudo-classes page ` using the filename of the source images you can customize the pseudo class related to the images, so if you simply append ``__hover`` to the filename ``glue`` will add ``:hover`` to the CSS class name. Since ``glue 0.9`` this separator is ``__`` but for previous version it use to be only ``_``. In order to not make ``glue < 0.9`` users rename their images, ``glue 0.9`` introduces this new option so you can customize the separator. .. code-block:: bash $ glue source output --pseudo-class-separator=_ -q --quiet ---------- This flag will make ``glue`` suppress all console output. .. code-block:: bash $ glue source output -q -r --recursive -------------- Read directories recursively and add all the images to the same sprite. Example structure:: source ├── actions │   ├── add.png │   └── remove.png ├── borders │   ├── top_left.png │   └── top_right.png └── icons ├── comment.png ├── new.png ├── rss.png └── blog ├── rss.png   └── atom.png If you want to create only one sprite image you should use. .. code-block:: bash $ glue source output --recursive On the other hand if you want to create three different sprites (one per folder) you can combine ``--project`` and ``--recursive``. .. code-block:: bash $ glue source output --recursive --project --ratios ------------ ``Glue`` can automatically scale down your sprites to automatically fit them into low-dpi devices. ``Glue`` assumes that the source images are the biggests you want to serve, then ``glue`` will create one sprite for each ratio you set in this command. For more information, read :doc:`ratios`. .. code-block:: bash $ glue source output --ratios=2,1 $ glue source output --ratios=2,1.5,1 --retina ------------ The option ``--retina`` is only a shortcut for ``--ratios=2,1``. .. code-block:: bash $ glue source output --retina -s --source -o --output ------------------------ There are two ways to choose which are the ``source`` and the ``output`` directories. Using the first and the second positional arguments is the traditional way of using ``glue`` but in order to standardize how configuration is handled ``glue 0.9`` intruduced these two new options. .. code-block:: bash $ glue output --source=DIR --output=DIR --scss --------- `scss/sass `_ is another dynamic stylesheet language that extends CSS with dynamic behaviors. ``glue`` can also create ``.scss`` files adding the ``--scss`` option. This files contain exactly the same CSS code. This option only changes the file format. .. code-block:: bash $ glue source output --scss .. note:: New in version 0.9 --scss-template ---------------- While using ``--scss`` you can use your own less template using ``--scss-template=``. .. note:: By default glue will use it's own internal ``scss`` template, so this command **is not required** unless you want to super-customize glue's ``scss`` output using **your own** template. You can find further documentation about how templates work in the :doc:`templates documentation page. ` .. code-block:: bash $ glue source output --scss-template=my_template.jinja .. note:: New in version 0.9.2 --separator -------------------------- ``glue`` by default uses ``-`` as separator for the CSS class names. If you want to customize this behaviour you can use ``--separator`` to specify your own one: .. code-block:: bash $ glue source output --separator=_ If you want to use `camelCase `_ instead of a separator, choose ``camelcase`` as separator. .. code-block:: bash $ glue source output --separator=camelcase --sprite-namespace ------------------ By default ``glue`` adds the sprite's name as past of the CSS class namespace. If you want to use your own namespace you can override the default one using the ``--sprite-namespace`` option. .. code-block:: bash $ glue source output --sprite-namespace=custom As part of the new sprite namespace you can use the key ``%(sprite)s`` to refer to the original namespace. If you want to completely remove the namespace (both the global and the sprite part) you can use: .. code-block:: bash $ glue source output --sprite-namespace= --namespace= -u --url --------- By default ``glue`` adds to the PNG file name the relative url between the CSS and the PNG file. If for any reason you need to change this behaviour, you can use ``url=`` and ``glue`` will replace its suggested one with your url. .. code-block:: bash $ glue source output --url=http://static.example.com/ --watch ------------ While you are developing a site it could be quite frustrating running ``Glue`` once and another every time you change a source image or a filename. ``--watch`` will allow you to keep ``Glue`` running in the background and it'll rebuild the sprite every time it detects changes on the source directory. .. code-block:: bash $ glue source output --watch glue-0.13/docs/pseudoclasses.rst0000644000076500000240000000250012322177142016632 0ustar neostaff00000000000000Pseudo Classes =========================== Using the filename ------------------ Using the filename of the source images you can customize the pseudo class related to the images, so if you simply append ``__hover`` to the filename ``glue`` will add ``:hover`` to the CSS class name:: buttons ├── pay.png └── pay__hover.png Using this simple convention you can create for example create button sprites like: .. image:: img/buttons.png And generate automatically the following css: .. code-block:: css .sprite-buttons-pay{background-image:url(buttons.png);background-repeat:no-repeat} .sprite-buttons-pay:hover{background-position:0px 0px;width:174px;height:62px;} .sprite-buttons-pay{background-position:0px -62px;width:174px;height:62px;} .. note:: You can use multiple pseudo-classes at the same time ``__hover__before.png`` .. note:: pseudo-class separator use to be ``_``. Since ``glue 0.9`` it is ``__``. If you don't want / you can'e rename all your files, you can use ``--pseudo-class-separator=_`` in order to make ``glue`` work in legacy mode. Available pseudo classes ------------------------ Glue will only detect the following pseudo-classes: ``link``, ``visited``, ``active``, ``hover``, ``focus``, ``first-letter``, ``first-line``, ``first-child``, ``before`` and ``after``. glue-0.13/docs/quickstart.rst0000644000076500000240000001256512475162714016173 0ustar neostaff00000000000000Quickstart ========== After :doc:`installing ` Glue you will have a new command named ``glue``. You can check if it was correctly installed calling ``glue`` with the ``--help`` option to get the list of all the available :doc:`command line arguments `:: $ glue --help If ``glue`` was correctly installed... Let's create your first sprite! Your first sprite ----------------- Create a new folder (``icons`` in this example), and add as many images as you want. Then you can simply run the following command:: $ glue icons sprites Glue will create a new folder named ``sprites`` with the following structure:: sprites ├── icons.css └── icons.png For example using the gorgeous `famfamfam icons `_ (4.2Mb) you will get the following ``icons.png`` (401Kb). .. image:: img/famfamfam1.png The other file, ``icons.css`` will have all the necessary css classes for this sprite: .. code-block:: css .sprite-icons-zoom_out{ background:url('sprites/icons/icons.png'); top:0; left:0; no-repeat;} .sprite-icons-zoom_in{ background:url('sprites/icons/icons.png'); top:0; left:-16; no-repeat;} .sprite-icons-zoom{ background:url('sprites/icons/icons.png'); top:-16; left:0; no-repeat;} .sprite-icons-xhtml_valid{ background:url('sprites/icons/icons.png'); top:-16; left:-16; no-repeat;} ... And why those CSS class names? ----------------------------------- As you can see, ``glue`` will use both the filename and the sprite name as part of the css class name. You can generate the sprite as many times as you want on any computer and the CSS class related to a specific image will always be the same, so you can create these sprites safely as part of your deployment process without being worried about css class name changes/collisions. .. note:: ``glue`` will only get alphanumeric, ``_`` and ``-`` characters from the filename to create the CSS class name. For example, an imaginary ``animals`` sprite with 5 images will have the following class names. =============== ========================= filename css class name =============== ========================= cat.png .sprite-animals-cat dog2.png .sprite-animals-dog2 cat_blue.png .sprite-animals-cat_blue dog-red.png .sprite-animals-dog-red dog_(white).png .sprite-animals-dog_white =============== ========================= If for any reason two images are to generate the same CSS class name, an error will be raised. .. note:: All CSS class names generated by ``glue`` will also have a namespace ``sprite-`` in the beginning. This namespace could be changed using the ``--namespace`` option. Crop unnecessary transparent spaces ----------------------------------- Usually designers add some unnecessary transparent space around the images because it is easier for them to work with a larger canvas. ``glue`` can optimize our sprite by cropping all the unnecessary transparent spaces that the original images could have before merging the images into the sprite. .. image:: img/crop.png .. code-block:: bash $ glue icons sprites --crop The new ``icons.png`` (348Kb) will be 53Kb smaller. .. image:: img/famfamfam2.png Now, the css file will have the new coordinates but using the same css class names! .. code-block:: css .sprite-icons-zoom{ background:url('sprites/icons/icons.png'); top:0; left:0; no-repeat;} .sprite-icons-wrench_orange{ background:url('sprites/icons/icons.png'); top:0; left:-16; no-repeat;} .sprite-icons-wrench{ background:url('sprites/icons/icons.png'); top:-16; left:0; no-repeat;} .sprite-icons-world_link{ background:url('sprites/icons/icons.png'); top:-16; left:-16; no-repeat;} ... What about if I need to generate multiple sprites? --------------------------------------------------- Usually an app has more than one sprite and generate all of them one by one could be annoying. The suggested setup is to create a new folder for every sprite, and add inside all the images you need for each one. ``glue`` will create a new sprite for every folder if you use the ``--project`` argument:: images ├── actions │   ├── add.png │   └── remove.png ├── borders │   ├── top_left.png │   └── top_right.png └── icons ├── comment.png ├── new.png └── rss.png So now, running:: $ glue images sprites --project Will generate a new ``sprites`` folder with the images and the css inside:: sprites ├── actions.png ├── actions.css ├── borders.png ├── borders.css ├── icons.png └── icons.css And now? ----------------------------------- ``glue`` have some more magical powers inside! * :doc:`Retina sprites `: Do you want to make your sprites look good on any device? Read the :doc:`ratios documentation `. * Glue can also read the configuration from :doc:`static config files `. * We support `less `_! It's easy, add ``--less`` and ``glue`` will generate the CSS file with the ``.less`` extension. * Cache Busting? Yes! Add ``--cachebuster`` and ``glue`` will add the ``SHA1`` of the PNG sprite as a queryarg on the CSS files. Read the :doc:`options` page. * Still hungry? Read the :doc:`options` page to discover all the available settings. glue-0.13/docs/ratios.rst0000644000076500000240000001524612475162714015301 0ustar neostaff00000000000000Retina Sprites: Ratios ======================= What is this for? ------------------ These days, some devices like the iPhone 4, or the new Macbook Pro have screens with higher pixel density than usual. For example iPhone4's Retina Display doubles the pixel density we use to see on handheld devices. This change improves the sharpness of vector graphics, but not images... Why? These devices scale vector graphics like text without losing quality, but in order to make images bigger, images are automatically pixel-doubled like in the following example. .. image:: img/nonretina.png **How can we solve this problem with high DPI devices?** Basically, provide two different version of each image. .. image:: img/retina.png **And... how can we detect wich image should we use?** CSS Media Queries. Modern browsers `(anything after IE 8.0) `_ supports them, and they allow us to specify different styles based on the ``device-pixel-ratio`` of the browser. **Can glue help?** Yes, using ``--ratios`` you can choose different ratios you want to build of each sprite. Glue will create one sprite for each ratio and will add all the neccesary CSS magic to make the browser use the high DPI image if the browser needs it. You can also use ``--retina``, it's a shortcut for ``--ratios=2,1``. How --retina and --ratios work? ------------------------------- As ``glue`` cannot do magic scaling up the source images, **it assumes that these images are the biggests you want to serve**. *(i.e. For iPhone 4 Retina these images should be 2x the final size you want)*, then glue will create one sprite for each ratio you set in the command line or only ``2x`` if you use ``--retina``:: $ glue icons sprites --retina This command will generate the following files:: sprites ├── icons.css ├── icons.png └── icons@2x.png .. figure:: img/sprites.png icons.png .. figure:: img/sprites@2x.png icons\@2x.png And this will be the content of ``icons.css``: .. code-block:: css .sprite-sprites-loopback, .sprite-sprites-weather, .sprite-sprites-magnify, .sprite-sprites-chat{ background-image:url(sprites.png); background-repeat:no-repeat } .sprite-sprites-loopback{ background-position:-1px -1px;width:32px;height:21px; } .sprite-sprites-weather{ background-position:-1px -24px;width:24px;height:26px; } .sprite-sprites-magnify{ background-position:-35px -1px;width:24px;height:24px; } .sprite-sprites-chat{ background-position:-35px -27px;width:24px;height:22px; } @media only screen and (-webkit-min-device-pixel-ratio: 2.0), only screen and (min--moz-device-pixel-ratio: 2.0), only screen and (-o-min-device-pixel-ratio: 200/100), only screen and (min-device-pixel-ratio: 2.0) { .sprite-sprites-loopback, .sprite-sprites-weather, .sprite-sprites-magnify, .sprite-sprites-chat{ background-image:url(sprites@2x.png); -webkit-background-size: 60px 51px; -moz-background-size: 60px 51px; background-size: 60px 51px; } } What about if I need some other ratios? --------------------------------------- The option ``--retina`` is only a shortcut for ``--ratios=2,1``. You can manually use ``--ratios=A,B,C...`` to create different ones. For example you can use ``--ratios=2,1.5,1`` to make glue build three diferent sprites:: sprites ├── icons.css ├── icons.png ├── icons@1.5.png └── icons@2x.png Wich ratios should I target? ---------------------------- Is up to you, but using ``2`` and ``1.5`` should be enough for most of the devices. Here you have a list of suggested ratios for some famous devices, `(full list) `_: ========================= =================== =============== ================ Device Screen size dpi Suggested ratio ========================= =================== =============== ================ **iPad** **2048 × 1536** **264ppi** **2** **iPhone 5/5S/5C** **1136 × 640** **326ppi** **2** **iPhone 4** **960 × 640** **326ppi** **2** **iPhone 4S** **960 × 640** **326ppi** **2** **iPad (3rd gen)** **2048 × 1536** **264ppi** **2** **MacBook Retina** **2880 x 1800** **220ppi** **2** **Xperia S** **720 × 1280** **342ppi** **2** **One X** **720 × 1280** **312ppi** **2** **EVO LTE** **720 × 1280** **312ppi** **2** **Galaxy Note** **800 × 1280** **285ppi** **2** **Galaxy SIII** **720 × 1280** **306ppi** **2** **Galaxy S4** **1080 × 1920** **441ppi** **3** **Galaxy Nexus** **720 × 1280** **316ppi** **2** **Nexus 4** **768 × 1280** **320ppi** **2** **Nexus 5** **1920 x 1080** **445ppi** **3** **Kindle Fire HDX 8.9** **2560 x 1600** **339ppi** **1.5** **Kindle Fire HD 8.9** **1920 x 1200** **254ppi** **1.5** HTC Desire 480 × 800 252ppi 1.5 Nexus One 480 × 800 252ppi 1.5 Sensation 960 × 540 256ppi 1.5 Evo 3D 960 × 540 256ppi 1.5 Sensation XE 960 × 540 256ppi 1.5 LG Optimus 2X 480 × 800 233ppi 1.5 Defy+ 854 × 480 265ppi 1.5 Milestone 480 × 854 265ppi 1.5 Nexus S SAMOLED 480 × 800 235ppi 1.5 Nexus S LCD 480 × 800 235ppi 1.5 Galaxy S Plus 480 x 800 233ppi 1.5 Galaxy SII 480 × 800 219ppi 1.5 Galaxy Tab 600 × 1024 171ppi 1.5 iPad mini 1024 × 768 163ppi 1 iPhone 480 × 320 163ppi 1 iPhone 3G 480 × 320 163ppi 1 iPhone 3GS 480 × 320 163ppi 1 iPad (1st gen) 1024 × 768 132ppi 1 iPad 2 1024 × 768 132ppi 1 Kidle Fire 1024 × 600 169ppi 1 Galaxy Y (S5360) 240 × 320 133ppi 0.75 ========================= =================== =============== ================ glue-0.13/docs/settings.rst0000644000076500000240000000722512322177142015626 0ustar neostaff00000000000000Settings ======== Settings Priority ------------------ Remember that environment variables would override glue's defaults but their priority is lower than command line options. From higher priority to lower priority: 1. Image settings (from configuration file) 2. Sprite settings (from configuration file) 3. Command line settings 4. Environment variables 5. Default settings Every command-line option available in glue is configurable using environment variables. .. note:: New in version 0.9 Settings Map ------------ ============================ =================================== =============================== Command-line arg Environment Variable Configuration File setting ============================ =================================== =============================== --source GLUE_SOURCE source --output GLUE_OUTPUT output -q --quiet GLUE_QUIET quiet -r --recursive GLUE_RECURSIVE recursive --follow-links GLUE_FOLLOW_LINKS follow_links -f --force GLUE_FORCE force -w --watch GLUE_WATCH watch --project GLUE_PROJECT project -a --algorithm GLUE_ALGORITHM algorithm --ordering GLUE_ORDERING algorithm_ordering --css GLUE_CSS css_dir --less GLUE_LESS less_dir --less-template GLUE_LESS_TEMPLATE less_template --scss GLUE_SCSS scss_format --scss-template GLUE_SCSS_TEMPLATE scss_template --namespace GLUE_CSS_NAMESPACE css_namespace --sprite-namespace GLUE_CSS_SPRITE_NAMESPACE css_sprite_namespace -u --url GLUE_CSS_URL css_url --cachebuster GLUE_CSS_CACHEBUSTER css_cachebuster --cachebuster-filename GLUE_CSS_CACHEBUSTER css_cachebuster_filename --separator GLUE_CSS_SEPARATOR css_separator --css-template GLUE_CSS_TEMPLATE css_template --pseudo-class-separator GLUE_CSS_PSEUDO_CLASS_SEPARATOR css_pseudo_class_separator --img GLUE_IMG img_dir --no-img GLUE_GENERATE_IMG generate_image --no-css GLUE_GENERATE_CSS generate_css -c --crop GLUE_CROP crop -p --padding GLUE_PADDING padding --margin GLUE_MARGIN margin --png8 GLUE_PNG8 png8 --ratios GLUE_RATIOS ratios --retina GLUE_RETINA ratios --html GLUE_HTML html_dir --cocos2d GLUE_COCOS2D cocos2d_dir --json GLUE_JSON json_dir --json-format GLUE_JSON_FORMAT json_format --caat GLUE_CAAT caat_dir ============================ =================================== =============================== glue-0.13/docs/templates.rst0000644000076500000240000000711312325163000015747 0ustar neostaff00000000000000Templates ========= Introduction ------------ ``glue`` formats based on templates can be customized usign your own templates. By convention, every format (i.e ``css``) will define an optional ``--css-template`` with wich you can override the default template. These templates are simple `Jinja2 templates `_, so you can customize a far as you want using the following context variables. .. note:: By default glue will use it's own internal templates, so you don't need to provide a template unless you want to super-customize glue's output. .. note:: If you don't know if you need a custom template, you **don't** need a custom template. BaseTextFormat -------------- Global ^^^^^^^ ============================ ====================================================== Variable Value ============================ ====================================================== version Glue version hash Hash of the sprite name Name of the sprite sprite_path Sprite path sprite_filename Sprite filename width Sprite width height Sprite height images List of ``Images`` inside the sprite ratios List of the ``Ratios`` inside ============================ ====================================================== Image ^^^^^^ ============================ ====================================================== Variable Value ============================ ====================================================== filename Image original filename last Last Image in the sprite x X position within the sprite y Y position within the sprite width Image width height Image height ============================ ====================================================== Ratio ^^^^^^ ============================ ====================================================== Variable Value ============================ ====================================================== ratio Ratio value fraction Nearest fraction for this ratio sprite_path Sprite Image path for this ratio ============================ ====================================================== CssFormat --------- Image ^^^^^^ ============================ ====================================================== Variable Value ============================ ====================================================== label CSS label for this image pseudo CSS pseudo class (if any) ============================ ====================================================== HtmlFormat ---------- Global ^^^^^^ ============================ ====================================================== Variable Value ============================ ====================================================== css_path Path where the css file is ============================ ====================================================== Templates Examples -------------------- If you are going to create a new template from scratch or if you want to do some changes to an existing output, a good starting point would be to read some of the existing templates in the ``formats`` `folder `_. glue-0.13/glue/0000755000076500000240000000000013106305703013227 5ustar neostaff00000000000000glue-0.13/glue/__init__.py0000644000076500000240000000002513106305630015334 0ustar neostaff00000000000000__version__ = '0.13' glue-0.13/glue/algorithms/0000755000076500000240000000000013106305703015400 5ustar neostaff00000000000000glue-0.13/glue/algorithms/__init__.py0000644000076500000240000000106112322177142017512 0ustar neostaff00000000000000from diagonal import DiagonalAlgorithm from horizontal import HorizontalAlgorithm from horizontal_bottom import HorizontalBottomAlgorithm from square import SquareAlgorithm from vertical import VerticalAlgorithm from vertical_right import VerticalRightAlgorithm algorithms = {'diagonal': DiagonalAlgorithm, 'horizontal': HorizontalAlgorithm, 'horizontal-bottom': HorizontalBottomAlgorithm, 'square': SquareAlgorithm, 'vertical': VerticalAlgorithm, 'vertical-right': VerticalRightAlgorithm} glue-0.13/glue/algorithms/diagonal.py0000644000076500000240000000036412322177142017536 0ustar neostaff00000000000000class DiagonalAlgorithm(object): def process(self, sprite): x = y = 0 for image in sprite.images: image.x = x image.y = y x += image.absolute_width y += image.absolute_height glue-0.13/glue/algorithms/horizontal.py0000644000076500000240000000031312322177142020143 0ustar neostaff00000000000000class HorizontalAlgorithm(object): def process(self, sprite): x = 0 for image in sprite.images: image.y = 0 image.x = x x += image.absolute_width glue-0.13/glue/algorithms/horizontal_bottom.py0000644000076500000240000000044512322177142021535 0ustar neostaff00000000000000class HorizontalBottomAlgorithm(object): def process(self, sprite): max_height = max([i.height for i in sprite.images]) x = 0 for image in sprite.images: image.y = max_height - image.height image.x = x x += image.absolute_width glue-0.13/glue/algorithms/square.py0000644000076500000240000001121012322177142017250 0ustar neostaff00000000000000import copy class SquareAlgorithmNode(object): def __init__(self, x=0, y=0, width=0, height=0, used=False, down=None, right=None): """Node constructor. :param x: X coordinate. :param y: Y coordinate. :param width: Image width. :param height: Image height. :param used: Flag to determine if the node is used. :param down: Down :class:`~Node`. :param right Right :class:`~Node`. """ self.x = x self.y = y self.width = width self.height = height self.used = used self.right = right self.down = down def find(self, node, width, height): """Find a node to allocate this image size (width, height). :param node: Node to search in. :param width: Pixels to grow down (width). :param height: Pixels to grow down (height). """ if node.used: return self.find(node.right, width, height) or self.find(node.down, width, height) elif node.width >= width and node.height >= height: return node return None def grow(self, width, height): """ Grow the canvas to the most appropriate direction. :param width: Pixels to grow down (width). :param height: Pixels to grow down (height). """ can_grow_d = width <= self.width can_grow_r = height <= self.height should_grow_r = can_grow_r and self.height >= (self.width + width) should_grow_d = can_grow_d and self.width >= (self.height + height) if should_grow_r: return self.grow_right(width, height) elif should_grow_d: return self.grow_down(width, height) elif can_grow_r: return self.grow_right(width, height) elif can_grow_d: return self.grow_down(width, height) return None def grow_right(self, width, height): """Grow the canvas to the right. :param width: Pixels to grow down (width). :param height: Pixels to grow down (height). """ old_self = copy.copy(self) self.used = True self.x = self.y = 0 self.width += width self.down = old_self self.right = SquareAlgorithmNode(x=old_self.width, y=0, width=width, height=self.height) node = self.find(self, width, height) if node: return self.split(node, width, height) return None def grow_down(self, width, height): """Grow the canvas down. :param width: Pixels to grow down (width). :param height: Pixels to grow down (height). """ old_self = copy.copy(self) self.used = True self.x = self.y = 0 self.height += height self.right = old_self self.down = SquareAlgorithmNode(x=0, y=old_self.height, width=self.width, height=height) node = self.find(self, width, height) if node: return self.split(node, width, height) return None def split(self, node, width, height): """Split the node to allocate a new one of this size. :param node: Node to be splitted. :param width: New node width. :param height: New node height. """ node.used = True node.down = SquareAlgorithmNode(x=node.x, y=node.y + height, width=node.width, height=node.height - height) node.right = SquareAlgorithmNode(x=node.x + width, y=node.y, width=node.width - width, height=height) return node class SquareAlgorithm(object): def process(self, sprite): root = SquareAlgorithmNode(width=sprite.images[0].absolute_width, height=sprite.images[0].absolute_height) # Loot all over the images creating a binary tree for image in sprite.images: node = root.find(root, image.absolute_width, image.absolute_height) if node: # Use this node node = root.split(node, image.absolute_width, image.absolute_height) else: # Grow the canvas node = root.grow(image.absolute_width, image.absolute_height) image.x = node.x image.y = node.y glue-0.13/glue/algorithms/vertical.py0000644000076500000240000000031212322177142017562 0ustar neostaff00000000000000class VerticalAlgorithm(object): def process(self, sprite): y = 0 for image in sprite.images: image.x = 0 image.y = y y += image.absolute_height glue-0.13/glue/algorithms/vertical_right.py0000644000076500000240000000043712322177142020767 0ustar neostaff00000000000000class VerticalRightAlgorithm(object): def process(self, sprite): max_width = max([i.width for i in sprite.images]) y = 0 for image in sprite.images: image.x = max_width - image.width image.y = y y += image.absolute_height glue-0.13/glue/bin.py0000755000076500000240000002655412475154733014405 0ustar neostaff00000000000000#!/usr/bin/env python import os import sys import argparse from PIL import Image as PImage from glue.formats import formats from glue.helpers import redirect_stdout from glue import exceptions from glue import managers from glue import __version__ def main(argv=None): argv = (argv or sys.argv)[1:] parser = argparse.ArgumentParser(usage=("%(prog)s [source | --source | -s] [output | --output | -o]")) parser.add_argument("--source", "-s", dest="source", type=unicode, default=os.environ.get('GLUE_SOURCE', None), help="Source path") parser.add_argument("--output", "-o", dest="output", type=unicode, default=os.environ.get('GLUE_OUTPUT', None), help="Output path") parser.add_argument("-q", "--quiet", dest="quiet", action='store_true', default=os.environ.get('GLUE_QUIET', False), help="Suppress all normal output") parser.add_argument("-r", "--recursive", dest="recursive", action='store_true', default=os.environ.get('GLUE_RECURSIVE', False), help=("Read directories recursively and add all " "the images to the same sprite.")) parser.add_argument("--follow-links", dest="follow_links", action='store_true', default=os.environ.get('GLUE_FOLLOW_LINKS', False), help="Follow symbolic links.") parser.add_argument("-f", "--force", dest="force", action='store_true', default=os.environ.get('GLUE_FORCE', False), help=("Force glue to create every sprite image and " "metadata file even if they already exists in " "the output directory.")) parser.add_argument("-w", "--watch", dest="watch", action='store_true', default=os.environ.get('GLUE_WATCH', False), help=("Watch the source folder for changes and rebuild " "when new files appear, disappear or change.")) parser.add_argument("--project", dest="project", action="store_true", default=os.environ.get('GLUE_PROJECT', False), help="Generate sprites for multiple folders") parser.add_argument("-v", "--version", action="version", version='%(prog)s ' + __version__, help="Show program's version number and exit") group = parser.add_argument_group("Algorithm options") group.add_argument("-a", "--algorithm", dest="algorithm", metavar='NAME', type=unicode, default=os.environ.get('GLUE_ALGORITHM', 'square'), choices=['square', 'vertical', 'horizontal', 'vertical-right', 'horizontal-bottom', 'diagonal'], help=("Allocation algorithm: square, vertical, " "horizontal, vertical-right, horizontal-bottom, " "diagonal. (default: square)")) group.add_argument("--ordering", dest="algorithm_ordering", metavar='NAME', type=unicode, default=os.environ.get('GLUE_ORDERING', 'maxside'), choices=['maxside', 'width', 'height', 'area', 'filename', '-maxside', '-width', '-height', '-area', '-filename'], help=("Ordering criteria: maxside, width, height, area or " "filename (default: maxside)")) # Populate the parser with options required by other formats for format in formats.itervalues(): format.populate_argument_parser(parser) # # Handle deprecated arguments # group = parser.add_argument_group("Deprecated options") deprecated_arguments = {} def add_deprecated_argument(*args, **kwargs): group.add_argument(*args, **kwargs) deprecated_arguments[kwargs['dest']] = args[0] add_deprecated_argument("--global-template", dest="global_template") add_deprecated_argument("--each-template", dest="each_template") add_deprecated_argument("--ratio-template", dest="ratio_template") add_deprecated_argument("--ignore-filename-paddings", action='store_true', dest="ignore_filename_paddings") add_deprecated_argument("--optipng", dest="optipng", action='store_true') add_deprecated_argument("--optipngpath", dest="optipngpath") add_deprecated_argument("--debug", action='store_true', dest="debug") add_deprecated_argument("--imagemagick", dest="imagemagick", action='store_true') add_deprecated_argument("--imagemagickpath", dest="imagemagickpath") # Parse input options, args = parser.parse_known_args(argv) # Get the list of enabled formats options.enabled_formats = [f for f in formats if getattr(options, '{0}_dir'.format(f), False)] # If there is only one enabled format (img) or if there are two (img, html) # this means glue is been executed without any specific main format. # In order to keep the legacy API we need to enable css. # As consequence there is no way to make glue only generate the sprite # image and the html file without generating the css file too. if set(options.enabled_formats) in (set(['img']), set(['img', 'html'])) and options.generate_css: options.enabled_formats.append('css') setattr(options, "css_dir", True) if not options.generate_image: options.enabled_formats.remove('img') # Fail if any of the deprecated arguments is used for argument in deprecated_arguments.iterkeys(): if getattr(options, argument, None): parser.error(("{0} argument is deprectated " "since v0.3").format(deprecated_arguments[argument])) extra = 0 # Get the source from the source option or the first positional argument if not options.source and args: options.source = args[0] extra += 1 # Get the output from the output option or the second positional argument if not options.output and args[extra:]: options.output = args[extra] # Check if source is available if options.source is None: parser.error(("You must provide the folder containing the sprites " "using the first positional argument or --source.")) # Make absolute both source and output if present if not os.path.isdir(options.source): parser.error("Directory not found: '{0}'".format(options.source)) options.source = os.path.abspath(options.source) if options.output: options.output = os.path.abspath(options.output) # Check that both the source and the output are present. Output "enough" # information can be tricky as you can choose different outputs for each # of the available formats. If it is present make it absolute. if not options.source: parser.error(("Source required. Please specify a source using " "--source or the first positional argument.")) if options.output: for format in options.enabled_formats: format_option = '{0}_dir'.format(format) path = getattr(options, format_option) if isinstance(path, bool) and path: setattr(options, format_option, options.output) else: if options.generate_image and not options.img_dir: parser.error(("Output required. Please specify an output for " "the sprite image using --output, the second " "positional argument or --img=")) for format in options.enabled_formats: format_option = '{0}_dir'.format(format) path = getattr(options, format_option) if isinstance(path, bool) or not path: parser.error(("{0} output required. Please specify an output " "for {0} using --output, the second " "positional argument or --{0}=".format(format))) else: setattr(options, format_option, os.path.abspath(path)) # If the img format is not enabled, we still need to know where the sprites # were generated. As img is not an enabled format img_dir would be empty # if --img was not userd. If this is the case we need to use whatever is # the output value. if not options.generate_image and isinstance(options.img_dir, bool): options.img_dir = options.output # Apply formats constraints for format in options.enabled_formats: formats[format].apply_parser_contraints(parser, options) if options.project: manager_cls = managers.ProjectManager else: manager_cls = managers.SimpleManager # Generate manager or defer the creation to a WatchManager if options.watch: manager = managers.WatchManager(manager_cls, vars(options)) else: manager = manager_cls(**vars(options)) try: if options.quiet: with redirect_stdout(): manager.process() else: manager.process() except exceptions.ValidationError, e: sys.stderr.write(e.args[0]) return e.error_code except exceptions.SourceImagesNotFoundError, e: sys.stderr.write("Error: No images found in %s.\n" % e.args[0]) return e.error_code except exceptions.NoSpritesFoldersFoundError, e: sys.stderr.write("Error: No sprites folders found in %s.\n" % e.args[0]) return e.error_code except exceptions.PILUnavailableError, e: sys.stderr.write(("Error: PIL {0} decoder is unavailable" "Please read the documentation and " "install it before spriting this kind of " "images.\n").format(e.args[0])) return e.error_code except Exception: import platform import traceback sys.stderr.write("\n") sys.stderr.write("=" * 80) sys.stderr.write("\nYou've found a bug! Please, raise an issue attaching the following traceback\n") sys.stderr.write("https://github.com/jorgebastida/glue/issues/new\n") sys.stderr.write("-" * 80) sys.stderr.write("\n") sys.stderr.write("Version: {0}\n".format(__version__)) sys.stderr.write("Python: {0}\n".format(sys.version)) sys.stderr.write("PIL version: {0}\n".format(PImage.VERSION)) sys.stderr.write("Platform: {0}\n".format(platform.platform())) sys.stderr.write("Config: {0}\n".format(vars(options))) sys.stderr.write("Args: {0}\n\n".format(sys.argv)) sys.stderr.write(traceback.format_exc()) sys.stderr.write("=" * 80) sys.stderr.write("\n") return 1 return 0 if __name__ == "__main__": sys.exit(main()) glue-0.13/glue/core.py0000644000076500000240000002244012325163000014525 0ustar neostaff00000000000000import re import os import sys import copy import hashlib import StringIO import ConfigParser from PIL import Image as PILImage from glue.algorithms import algorithms from glue.helpers import cached_property, round_up from glue.formats import ImageFormat from glue.exceptions import SourceImagesNotFoundError, PILUnavailableError class ConfigurableFromFile(object): def _get_config_from_file(self, filename, section): """Return, as a dictionary, all the available configuration inside the sprite configuration file on this sprite path.""" def clean(value): return {'true': True, 'false': False}.get(value.lower(), value) config = ConfigParser.RawConfigParser() config.read(os.path.join(self.config_path, filename)) try: keys = config.options(section) except ConfigParser.NoSectionError: return {} return dict([[k, clean(config.get(section, k))] for k in keys]) class Image(ConfigurableFromFile): def __init__(self, path, config): self.path = path self.filename = os.path.basename(path) self.dirname = self.config_path = os.path.dirname(path) self.config = copy.deepcopy(config) self.config.update(self._get_config_from_file('sprite.conf', self.filename)) self.x = self.y = None self.original_width = self.original_height = 0 with open(self.path, "rb") as img: self._image_data = img.read() print "\t{0} added to sprite".format(self.filename) @cached_property def image(self): """Return a Pil representation of this image """ if sys.version < '3': imageio = StringIO.StringIO(self._image_data) else: imageio = StringIO.BytesIO(self._image_data) try: source_image = PILImage.open(imageio) img = PILImage.new('RGBA', source_image.size, (0, 0, 0, 0)) if source_image.mode == 'L': alpha = source_image.split()[0] transparency = source_image.info.get('transparency') mask = PILImage.eval(alpha, lambda a: 0 if a == transparency else 255) img.paste(source_image, (0, 0), mask=mask) else: img.paste(source_image, (0, 0)) except IOError, e: raise PILUnavailableError(e.args[0].split()[1]) finally: imageio.close() self.original_width, self.original_height = img.size # Crop the image searching for the smallest possible bounding box # without losing any non-transparent pixel. # This crop is only used if the crop flag is set in the config. if self.config['crop']: img = img.crop(img.split()[-1].getbbox()) return img @property def width(self): """Return Image width""" return self.image.size[0] @property def height(self): """Return Image height""" return self.image.size[1] @property def padding(self): """Return a 4-elements list with the desired padding.""" return self._generate_spacing_info(self.config['padding']) @property def margin(self): """Return a 4-elements list with the desired marging.""" return self._generate_spacing_info(self.config['margin']) def _generate_spacing_info(self, data): data = data.split(',' if ',' in data else ' ') if len(data) == 4: data = data elif len(data) == 3: data = data + [data[1]] elif len(data) == 2: data = data * 2 elif len(data) == 1: data = data * 4 else: data = [0] * 4 return map(int, data) @cached_property def horizontal_spacing(self): """Return the horizontal padding and margin for this image.""" return self.padding[1] + self.padding[3] + self.margin[1] + self.margin[3] @cached_property def vertical_spacing(self): """Return the vertical padding and margin for this image.""" return self.padding[0] + self.padding[2] + self.margin[0] + self.margin[2] @property def absolute_width(self): """Return the total width of the image taking count of the margin, padding and ratio.""" return round_up(self.width + self.horizontal_spacing * max(self.config['ratios'])) @property def absolute_height(self): """Return the total height of the image taking count of the margin, padding and ratio. """ return round_up(self.height + self.vertical_spacing * max(self.config['ratios'])) def __lt__(self, img): """Use maxside, width, hecight or area as ordering algorithm. :param img: Another :class:`~Image`.""" ordering = self.config['algorithm_ordering'] ordering = ordering[1:] if ordering.startswith('-') else ordering if ordering == "filename": return sorted([self.filename, img.filename])[0] == img.filename if ordering == 'width': return self.absolute_width <= img.absolute_width elif ordering == 'height': return self.absolute_height <= img.absolute_height elif ordering == 'area': return self.absolute_width * self.absolute_height <= img.absolute_width * img.absolute_height else: return max(self.absolute_width, self.absolute_height) <= max(img.absolute_width, img.absolute_height) class Sprite(ConfigurableFromFile): config_filename = 'sprite.conf' config_section = 'sprite' valid_extensions = ['png', 'jpg', 'jpeg', 'gif'] def __init__(self, path, config, name=None): self.path = self.config_path = path self.config = copy.deepcopy(config) self.config.update(self._get_config_from_file('sprite.conf', 'sprite')) self.name = name or self.config.get('name', os.path.basename(path)) # Setup ratios ratios = self.config['ratios'].split(',') ratios = set([float(r.strip()) for r in ratios if r.strip()]) # Always add 1.0 as a required ratio ratios.add(1.0) # Create a sorted list of ratios self.ratios = sorted(ratios) self.max_ratio = max(self.ratios) self.config['ratios'] = self.ratios # Discover images inside this sprite self.images = self._locate_images() img_format = ImageFormat(sprite=self) for ratio in ratios: ratio_output_key = 'ratio_{0}_output'.format(ratio) if ratio_output_key not in self.config: self.config[ratio_output_key] = img_format.output_path(ratio) print "Processing '{0}':".format(self.name) # Generate sprite map self.process() def process(self): algorithm_cls = algorithms[self.config['algorithm']] algorithm = algorithm_cls() algorithm.process(self) def validate(self): pass @cached_property def hash(self): """ Return a hash of this sprite. In order to detect any change on the source images it use the data, order and path of each image. In the same way it use this sprite settings as part of the hash. """ hash_list = [] for image in self.images: hash_list.append(os.path.relpath(image.path)) hash_list.append(image._image_data) for key, value in self.config.iteritems(): hash_list.append(key) hash_list.append(value) if sys.version < '3': return hashlib.sha1(''.join(map(str, hash_list))).hexdigest()[:10] return hashlib.sha1(''.join(map(str, hash_list)).encode('utf-8')).hexdigest()[:10] @cached_property def canvas_size(self): """Return the width and height for this sprite canvas""" width = height = 0 for image in self.images: x = image.x + image.absolute_width y = image.y + image.absolute_height if width < x: width = x if height < y: height = y return round_up(width), round_up(height) def sprite_path(self, ratio=1.0): return self.config['ratio_{0}_output'.format(ratio)] def _locate_images(self): """Return all valid images within a folder. All files with a extension not included in (png, jpg, jpeg and gif) or beginning with '.' will be ignored. If the folder doesn't contain any valid image it will raise :class:`~SourceImagesNotFoundError` The list of images will be ordered using the desired ordering algorithm. The default is 'maxside'. """ extensions = '|'.join(self.valid_extensions) extension_re = re.compile('.+\.(%s)$' % extensions, re.IGNORECASE) files = sorted(os.listdir(self.path)) images = [] for root, dirs, files in os.walk(self.path, followlinks=self.config['follow_links']): for filename in sorted(files): if not filename.startswith('.') and extension_re.match(filename): images.append(Image(path=os.path.join(root, filename), config=self.config)) if not self.config['recursive']: break if not images: raise SourceImagesNotFoundError(self.path) images = sorted(images, reverse=self.config['algorithm_ordering'][0] != '-') return images glue-0.13/glue/exceptions.py0000644000076500000240000000106012322177142015762 0ustar neostaff00000000000000class GlueError(Exception): """Base Exception class for glue Errors.""" error_code = 999 class PILUnavailableError(GlueError): """Raised if some PIL decoder isn't available.""" error_code = 2 class ValidationError(GlueError): """Raised by formats or sprites while .""" error_code = 3 class SourceImagesNotFoundError(GlueError): """Raised if a folder doesn't contain any valid image.""" error_code = 4 class NoSpritesFoldersFoundError(GlueError): """Raised if no sprites folders could be found.""" error_code = 5 glue-0.13/glue/formats/0000755000076500000240000000000013106305703014702 5ustar neostaff00000000000000glue-0.13/glue/formats/__init__.py0000644000076500000240000000076012322177142017021 0ustar neostaff00000000000000from .css import CssFormat from .cocos2d import Cocos2dFormat from .img import ImageFormat from .html import HtmlFormat from .jsonformat import JSONFormat from .caat import CAATFormat from .less import LessFormat from .scss import ScssFormat formats = {'css': CssFormat, 'cocos2d': Cocos2dFormat, 'img': ImageFormat, 'html': HtmlFormat, 'json': JSONFormat, 'caat': CAATFormat, 'less': LessFormat, 'scss': ScssFormat} glue-0.13/glue/formats/base.py0000644000076500000240000001712612475162714016210 0ustar neostaff00000000000000import os import sys import json import codecs import plistlib import textwrap from jinja2 import Template from glue.helpers import round_up, nearest_fration from glue import __version__ class BaseFormat(object): extension = None build_per_ratio = False def __init__(self, sprite): self.sprite = sprite def output_dir(self, *args, **kwargs): return self.sprite.config['{0}_dir'.format(self.format_label)] def output_filename(self, ratio=None, *args, **kwargs): if self.build_per_ratio: if ratio is None: raise AttributeError("Format {0} output_filename requires a ratio.".format(self.__class__)) ratio_suffix = '@%.1fx' % ratio if int(ratio) != ratio else '@%ix' % ratio if ratio_suffix == '@1x': ratio_suffix = '' return '{0}{1}'.format(self.sprite.name, ratio_suffix) return self.sprite.name def output_path(self, *args, **kwargs): return os.path.join(self.output_dir(*args, **kwargs), '{0}.{1}'.format(self.output_filename(*args, **kwargs), self.extension)) def build(self): if self.build_per_ratio: for ratio in self.sprite.config['ratios']: self.save(ratio=ratio) else: self.save() def save(self, *args, **kwargs): raise NotImplementedError def needs_rebuild(self): return True def validate(self): pass @property def format_label(self): from glue.formats import formats return dict((v,k) for k, v in formats.iteritems())[self.__class__] @classmethod def populate_argument_parser(cls, parser): pass @classmethod def apply_parser_contraints(cls, parser, options): pass def fix_windows_path(self, path): if os.name == 'nt': path = path.replace('\\', '/') return path class BaseTextFormat(BaseFormat): def get_context(self, *args, **kwargs): sprite_path = os.path.relpath(self.sprite.sprite_path(), self.output_dir()) sprite_path = self.fix_windows_path(sprite_path) context = {'version': __version__, 'hash': self.sprite.hash, 'name': self.sprite.name, 'sprite_path': sprite_path, 'sprite_filename': os.path.basename(sprite_path), 'width': round_up(self.sprite.canvas_size[0] / self.sprite.max_ratio), 'height': round_up(self.sprite.canvas_size[1] / self.sprite.max_ratio), 'images': [], 'ratios': {}} for i, img in enumerate(self.sprite.images): base_x = img.x * -1 - img.margin[3] * self.sprite.max_ratio base_y = img.y * -1 - img.margin[0] * self.sprite.max_ratio base_abs_x = img.x + img.margin[3] * self.sprite.max_ratio base_abs_y = img.y + img.margin[0] * self.sprite.max_ratio image = dict(filename=img.filename, last=i == len(self.sprite.images) - 1, x=round_up(base_x / self.sprite.max_ratio), y=round_up(base_y / self.sprite.max_ratio), abs_x=round_up(base_abs_x / self.sprite.max_ratio), abs_y=round_up(base_abs_y / self.sprite.max_ratio), height=round_up((img.height / self.sprite.max_ratio) + img.padding[0] + img.padding[2]), width=round_up((img.width / self.sprite.max_ratio) + img.padding[1] + img.padding[3]), original_width=img.original_width, original_height=img.original_height, ratios={}) for r in self.sprite.ratios: image['ratios'][r] = dict(filename=img.filename, last=i == len(self.sprite.images) - 1, x=round_up(base_x / self.sprite.max_ratio * r), y=round_up(base_y / self.sprite.max_ratio * r), abs_x=round_up(base_abs_x / self.sprite.max_ratio * r), abs_y=round_up(base_abs_y / self.sprite.max_ratio * r), height=round_up((img.height + img.padding[0] + img.padding[2]) / self.sprite.max_ratio * r), width=round_up((img.width + img.padding[1] + img.padding[3]) / self.sprite.max_ratio * r)) context['images'].append(image) # Ratios for r in self.sprite.ratios: ratio_sprite_path = os.path.relpath(self.sprite.sprite_path(ratio=r), self.output_dir()) ratio_sprite_path = self.fix_windows_path(ratio_sprite_path) context['ratios'][r] = dict(ratio=r, fraction=nearest_fration(r), sprite_path=ratio_sprite_path, sprite_filename=os.path.basename(ratio_sprite_path), width=round_up(self.sprite.canvas_size[0] / self.sprite.max_ratio * r), height=round_up(self.sprite.canvas_size[1] / self.sprite.max_ratio * r)) return context def render(self, *args, **kwargs): raise NotImplementedError def save(self, *args, **kwargs): # Create the destination directory if required if not os.path.exists(self.output_dir(*args, **kwargs)): os.makedirs(self.output_dir(*args, **kwargs)) with codecs.open(self.output_path(*args, **kwargs), 'w', 'utf-8') as f: f.write(self.render(*args, **kwargs)) class BaseJSONFormat(BaseTextFormat): meta_key = 'meta' def needs_rebuild(self): for ratio in self.sprite.config['ratios']: json_path = self.output_path(ratio) if os.path.exists(json_path): with codecs.open(json_path, 'r', 'utf-8') as f: try: data = json.loads(f.read()) assert data[self.meta_key]['hash'] == self.sprite.hash except Exception: continue return True return False def render(self, *args, **kwargs): return json.dumps(self.get_context(*args, **kwargs), indent=4) class BasePlistFormat(BaseTextFormat): meta_key = 'metadata' def render(self, *args, **kwargs): context = self.get_context(*args, **kwargs) if sys.version < '3': return plistlib.writePlistToString(context) return plistlib.writePlistToBytes(context).decode('unicode_escape') def needs_rebuild(self): for ratio in self.sprite.config['ratios']: cocos2d_path = self.output_path(ratio) if os.path.exists(cocos2d_path): try: data = plistlib.readPlist(cocos2d_path) assert data[self.meta_key]['hash'] == self.sprite.hash except Exception: continue return True return False class JinjaTextFormat(BaseTextFormat): template = '' def render(self, *args, **kwargs): context = self.get_context(*args, **kwargs) template = self.template custom_template_config = '{0}_template'.format(self.format_label) if self.sprite.config.get(custom_template_config): with open(self.sprite.config[custom_template_config]) as f: template = f.read() return Template(textwrap.dedent(template).strip()).render(**context) glue-0.13/glue/formats/caat.py0000644000076500000240000000256512322177142016177 0ustar neostaff00000000000000import os from base import BaseJSONFormat class CAATFormat(BaseJSONFormat): extension = 'json' build_per_ratio = True @classmethod def populate_argument_parser(cls, parser): group = parser.add_argument_group("JSON format options") group.add_argument("--caat", dest="caat_dir", nargs='?', const=True, default=os.environ.get('GLUE_CAAT', False), metavar='DIR', help="Generate CAAT files and optionally where") def get_context(self, *args, **kwargs): context = super(CAATFormat, self).get_context(*args, **kwargs) data = dict(sprites={}, meta={'version': context['version'], 'hash': context['hash'], 'sprite_filename': context['sprite_filename'], 'width': context['width'], 'height': context['height']}) for i in context['images']: data['sprites'][i['filename']] = {"x" : i['abs_x'], "y" : i['abs_y'], "width" : i['width'], "height" : i['height']} return data glue-0.13/glue/formats/cocos2d.py0000644000076500000240000000356012322177142016617 0ustar neostaff00000000000000import os from base import BasePlistFormat class Cocos2dFormat(BasePlistFormat): extension = 'plist' build_per_ratio = True @classmethod def populate_argument_parser(cls, parser): group = parser.add_argument_group("Cocos2d format options") group.add_argument("--cocos2d", dest="cocos2d_dir", nargs='?', const=True, default=os.environ.get('GLUE_COCOS2D', False), metavar='DIR', help="Generate Cocos2d files and optionally where") def get_context(self, ratio, *args, **kwargs): context = super(Cocos2dFormat, self).get_context(ratio, *args, **kwargs) ratio_context = context['ratios'][ratio] data = {'frames': {}, 'metadata': {'version': context['version'], 'hash': context['hash'], 'size':'{{{width}, {height}}}'.format(**context['ratios'][ratio]), 'name': context['name'], 'format': 2, 'realTextureFileName': ratio_context['sprite_filename'], 'textureFileName': ratio_context['sprite_filename'] } } for i in context['images']: image_context = i['ratios'][ratio] rect = '{{{{{abs_x}, {abs_y}}}, {{{width}, {height}}}}}'.format(**image_context) data['frames'][i['filename']] = {'frame': rect, 'offset': '{0,0}', 'rotated': False, 'sourceColorRect': rect, 'sourceSize': '{{{width}, {height}}}'.format(**image_context)} return data glue-0.13/glue/formats/css.py0000644000076500000240000002417312475162714016066 0ustar neostaff00000000000000import re import os import codecs from glue import __version__ from base import JinjaTextFormat from ..exceptions import ValidationError class CssFormat(JinjaTextFormat): extension = 'css' camelcase_separator = 'camelcase' css_pseudo_classes = set(['link', 'visited', 'active', 'hover', 'focus', 'first-letter', 'first-line', 'first-child', 'before', 'after']) template = u""" /* glue: {{ version }} hash: {{ hash }} */ {% for image in images %}.{{ image.label }}{{ image.pseudo }}{%- if not image.last %},{{"\n"}}{%- endif %}{%- endfor %} { background-image: url('{{ sprite_path }}'); background-repeat: no-repeat; } {% for image in images %} .{{ image.label }}{{ image.pseudo }} { background-position: {{ image.x ~ ('px' if image.x) }} {{ image.y ~ ('px' if image.y) }}; width: {{ image.width }}px; height: {{ image.height }}px; } {% endfor %}{% for r, ratio in ratios.items() %} @media screen and (-webkit-min-device-pixel-ratio: {{ ratio.ratio }}), screen and (min--moz-device-pixel-ratio: {{ ratio.ratio }}), screen and (-o-min-device-pixel-ratio: {{ ratio.fraction }}), screen and (min-device-pixel-ratio: {{ ratio.ratio }}), screen and (min-resolution: {{ ratio.ratio }}dppx) { {% for image in images %}.{{ image.label }}{{ image.pseudo }}{% if not image.last %},{{"\n"}} {% endif %}{% endfor %} { background-image: url('{{ ratio.sprite_path }}'); -webkit-background-size: {{ width }}px {{ height }}px; -moz-background-size: {{ width }}px {{ height }}px; background-size: {{ width }}px {{ height }}px; } } {% endfor %} """ @classmethod def populate_argument_parser(cls, parser): group = parser.add_argument_group("CSS format options") group.add_argument("--css", dest="css_dir", nargs='?', const=True, default=os.environ.get('GLUE_CSS', False), metavar='DIR', help="Generate CSS files and optionally where") group.add_argument("--namespace", dest="css_namespace", type=unicode, default=os.environ.get('GLUE_CSS_NAMESPACE', 'sprite'), help="Namespace for all css classes (default: sprite)") group.add_argument("--sprite-namespace", dest="css_sprite_namespace", type=unicode, default=os.environ.get('GLUE_CSS_SPRITE_NAMESPACE', '{sprite_name}'), help="Namespace for all sprites (default: {sprite_name})") group.add_argument("-u", "--url", dest="css_url", type=unicode, default=os.environ.get('GLUE_CSS_URL', ''), help="Prepend this string to the sprites path") group.add_argument("--cachebuster", dest="css_cachebuster", default=os.environ.get('GLUE_CSS_CACHEBUSTER', False), action='store_true', help=("Use the sprite's sha1 first 6 characters as a " "queryarg everytime that file is referred " "from the css")) group.add_argument("--cachebuster-filename", dest="css_cachebuster_filename", default=os.environ.get('GLUE_CSS_CACHEBUSTER', False), action='store_true', help=("Append the sprite's sha first 6 characters " "to the output filename")) group.add_argument("--cachebuster-filename-only-sprites", dest="css_cachebuster_only_sprites", default=os.environ.get('GLUE_CSS_CACHEBUSTER_ONLY_SPRITES', False), action='store_true', help=("Only apply cachebuster to sprite images.")) group.add_argument("--separator", dest="css_separator", type=unicode, default=os.environ.get('GLUE_CSS_SEPARATOR', '-'), metavar='SEPARATOR', help=("Customize the separator used to join CSS class " "names. If you want to use camelCase use " "'camelcase' as separator.")) group.add_argument("--pseudo-class-separator", dest="css_pseudo_class_separator", type=unicode, default=os.environ.get('GLUE_CSS_PSEUDO_CLASS_SEPARATOR', '__'), metavar='SEPARATOR', help=("Customize the separator glue will use in order " "to determine the pseudo classes included into " "filenames.")) group.add_argument("--css-template", dest="css_template", default=os.environ.get('GLUE_CSS_TEMPLATE', None), metavar='DIR', help="Template to use to generate the CSS output.") group.add_argument("--no-css", dest="generate_css", action="store_false", default=os.environ.get('GLUE_GENERATE_CSS', True), help="Don't genereate CSS files.") @classmethod def apply_parser_contraints(cls, parser, options): cachebusters = (options.css_cachebuster, options.css_cachebuster_filename, options.css_cachebuster_only_sprites) if sum(cachebusters) > 1: parser.error("You can't use --cachebuster, --cachebuster-filename or --cachebuster-filename-only-sprites at the same time.") def needs_rebuild(self): hash_line = '/* glue: %s hash: %s */\n' % (__version__, self.sprite.hash) try: with codecs.open(self.output_path(), 'r', 'utf-8') as existing_css: first_line = existing_css.readline() assert first_line == hash_line except Exception: return True return False def validate(self): class_names = [':'.join(self.generate_css_name(i.filename)) for i in self.sprite.images] if len(set(class_names)) != len(self.sprite.images): dup = [i for i in self.sprite.images if class_names.count(':'.join(self.generate_css_name(i.filename))) > 1] duptext = '\n'.join(['\t{0} => .{1}'.format(os.path.relpath(d.path), ':'.join(self.generate_css_name(d.filename))) for d in dup]) raise ValidationError("Error: Some images will have the same class name:\n{0}".format(duptext)) return True def output_filename(self, *args, **kwargs): filename = super(CssFormat, self).output_filename(*args, **kwargs) if self.sprite.config['css_cachebuster_filename']: return '{0}_{1}'.format(filename, self.sprite.hash) return filename def get_context(self, *args, **kwargs): context = super(CssFormat, self).get_context(*args, **kwargs) # Generate css labels for image in context['images']: image['label'], image['pseudo'] = self.generate_css_name(image['filename']) if self.sprite.config['css_url']: context['sprite_path'] = '{0}{1}'.format(self.sprite.config['css_url'], context['sprite_filename']) for r, ratio in context['ratios'].iteritems(): ratio['sprite_path'] = '{0}{1}'.format(self.sprite.config['css_url'], ratio['sprite_filename']) # Add cachebuster if required if self.sprite.config['css_cachebuster']: def apply_cachebuster(path): return "%s?%s" % (path, self.sprite.hash) context['sprite_path'] = apply_cachebuster(context['sprite_path']) for r, ratio in context['ratios'].iteritems(): ratio['sprite_path'] = apply_cachebuster(ratio['sprite_path']) return context def generate_css_name(self, filename): filename = filename.rsplit('.', 1)[0] separator = self.sprite.config['css_separator'] namespace = [re.sub(r'[^\w\-_]', '', filename)] # Add sprite namespace if required if self.sprite.config['css_sprite_namespace']: sprite_name = re.sub(r'[^\w\-_]', '', self.sprite.name) sprite_namespace = self.sprite.config['css_sprite_namespace'] # Support legacy 0.4 format sprite_namespace = sprite_namespace.replace("%(sprite)s", "{sprite_name}") namespace.insert(0, sprite_namespace.format(sprite_name=sprite_name)) # Add global namespace if required if self.sprite.config['css_namespace']: namespace.insert(0, self.sprite.config['css_namespace']) # Handle CamelCase separator if self.sprite.config['css_separator'] == self.camelcase_separator: namespace = [n[:1].title() + n[1:] if i > 0 else n for i, n in enumerate(namespace)] separator = '' label = separator.join(namespace) pseudo = '' css_pseudo_class_separator = self.sprite.config['css_pseudo_class_separator'] if css_pseudo_class_separator in filename: pseudo_classes = [p for p in filename.split(css_pseudo_class_separator) if p in self.css_pseudo_classes] # If present add this pseudo class as pseudo an remove it from the label if pseudo_classes: for p in pseudo_classes: label = label.replace('{0}{1}'.format(css_pseudo_class_separator, p), "") pseudo = ''.join(map(lambda x: ':{0}'.format(x), pseudo_classes)) return label, pseudo glue-0.13/glue/formats/html.py0000644000076500000240000000371612322177142016232 0ustar neostaff00000000000000import os from .css import CssFormat class HtmlFormat(CssFormat): extension = 'html' template = u""" Glue Sprite Test Html

CSS Classes

{% for image in images %} {% endfor %}
CSS Class Result
.{{ image.label }}

Generated using Glue v{{ version }}

""" @classmethod def populate_argument_parser(cls, parser): group = parser.add_argument_group("Html format options") group.add_argument("--html", dest="html_dir", nargs='?', const=True, default=os.environ.get('GLUE_HTML', False), metavar='DIR', help="Generate html files and optionally where") @classmethod def apply_parser_contraints(cls, parser, options): if 'html' in options.enabled_formats and 'css' not in options.enabled_formats: parser.error("You can't use --html without --css.") def get_context(self, *args, **kwargs): context = super(HtmlFormat, self).get_context(*args, **kwargs) context['css_path'] = os.path.relpath(os.path.join(self.sprite.config['css_dir'], '{0}.css'.format(self.sprite.name)), self.output_dir()) return context def needs_rebuild(self): return True def validate(self): return True glue-0.13/glue/formats/img.py0000644000076500000240000001347512322177142016045 0ustar neostaff00000000000000import os from PIL import Image as PILImage from PIL import PngImagePlugin from glue import __version__ from glue.helpers import round_up, cached_property from .base import BaseFormat class ImageFormat(BaseFormat): build_per_ratio = True extension = 'png' @classmethod def populate_argument_parser(cls, parser): group = parser.add_argument_group("Sprite image options") group.add_argument("--img", dest="img_dir", type=unicode, default=os.environ.get('GLUE_IMG', True), metavar='DIR', help="Output directory for img files") group.add_argument("--no-img", dest="generate_image", action="store_false", default=os.environ.get('GLUE_GENERATE_IMG', True), help="Don't genereate IMG files.") group.add_argument("-c", "--crop", dest="crop", action='store_true', default=os.environ.get('GLUE_CROP', False), help="Crop images removing unnecessary transparent margins") group.add_argument("-p", "--padding", dest="padding", type=unicode, default=os.environ.get('GLUE_PADDING', '0'), help="Force this padding in all images") group.add_argument("--margin", dest="margin", type=unicode, default=os.environ.get('GLUE_MARGIN', '0'), help="Force this margin in all images") group.add_argument("--png8", action="store_true", dest="png8", default=os.environ.get('GLUE_PNG8', False), help=("The output image format will be png8 " "instead of png32")) group.add_argument("--ratios", dest="ratios", type=unicode, default=os.environ.get('GLUE_RATIOS', '1'), help="Create sprites based on these ratios") group.add_argument("--retina", dest="ratios", default=os.environ.get('GLUE_RETINA', False), action='store_const', const='2,1', help="Shortcut for --ratios=2,1") def output_filename(self, *args, **kwargs): filename = super(ImageFormat, self).output_filename(*args, **kwargs) if self.sprite.config['css_cachebuster_filename'] or self.sprite.config['css_cachebuster_only_sprites']: return '{0}_{1}'.format(filename, self.sprite.hash) return filename def needs_rebuild(self): for ratio in self.sprite.config['ratios']: image_path = self.output_path(ratio) try: existing = PILImage.open(image_path) assert existing.info['Software'] == 'glue-%s' % __version__ assert existing.info['Comment'] == self.sprite.hash continue except Exception: return True return False @cached_property def _raw_canvas(self): # Create the sprite canvas width, height = self.sprite.canvas_size canvas = PILImage.new('RGBA', (width, height), (0, 0, 0, 0)) # Paste the images inside the canvas for image in self.sprite.images: canvas.paste(image.image, (round_up(image.x + (image.padding[3] + image.margin[3]) * self.sprite.max_ratio), round_up(image.y + (image.padding[0] + image.margin[0]) * self.sprite.max_ratio))) meta = PngImagePlugin.PngInfo() meta.add_text('Software', 'glue-%s' % __version__) meta.add_text('Comment', self.sprite.hash) # Customize how the png is going to be saved kwargs = dict(optimize=False, pnginfo=meta) if self.sprite.config['png8']: # Get the alpha band alpha = canvas.split()[-1] canvas = canvas.convert('RGB' ).convert('P', palette=PILImage.ADAPTIVE, colors=255) # Set all pixel values below 128 to 255, and the rest to 0 mask = PILImage.eval(alpha, lambda a: 255 if a <= 128 else 0) # Paste the color of index 255 and use alpha as a mask canvas.paste(255, mask) kwargs.update({'transparency': 255}) return canvas, kwargs def save(self, ratio): width, height = self.sprite.canvas_size canvas, kwargs = self._raw_canvas # Loop all over the ratios and save one image for each one for ratio in self.sprite.config['ratios']: # Create the destination directory if required if not os.path.exists(self.output_dir(ratio=ratio)): os.makedirs(self.output_dir(ratio=ratio)) image_path = self.output_path(ratio=ratio) # If this canvas isn't the biggest one scale it using the ratio if self.sprite.max_ratio != ratio: reduced_canvas = canvas.resize( (round_up((width / self.sprite.max_ratio) * ratio), round_up((height / self.sprite.max_ratio) * ratio)), PILImage.ANTIALIAS) reduced_canvas.save(image_path, **kwargs) # TODO: Use Imagemagick if it's available else: canvas.save(image_path, **kwargs) glue-0.13/glue/formats/jsonformat.py0000644000076500000240000000545112475162714017456 0ustar neostaff00000000000000import os import json try: from collections import OrderedDict except ImportError: from ordereddict import OrderedDict from base import BaseJSONFormat class JSONFormat(BaseJSONFormat): extension = 'json' build_per_ratio = True @classmethod def populate_argument_parser(cls, parser): group = parser.add_argument_group("JSON format options") group.add_argument("--json", dest="json_dir", nargs='?', const=True, default=os.environ.get('GLUE_JSON', False), metavar='DIR', help="Generate JSON files and optionally where") group.add_argument("--json-format", dest="json_format", metavar='NAME', type=unicode, default=os.environ.get('GLUE_JSON_FORMAT', 'array'), choices=['array', 'hash'], help=("JSON structure format (array, hash)")) def get_context(self, *args, **kwargs): context = super(JSONFormat, self).get_context(*args, **kwargs) frames = OrderedDict([[i['filename'], {'filename': i['filename'], 'frame': {'x': i['x'], 'y': i['y'], 'w': i['width'], 'h': i['height']}, 'rotated': False, 'trimmed': False, 'spriteSourceSize': {'x': i['x'], 'y': i['y'], 'w': i['width'], 'h': i['height']}, 'sourceSize': {'w': i['original_width'], 'h': i['original_height']}}] for i in context['images']]) data = OrderedDict(frames=None, meta={'version': context['version'], 'hash': context['hash'], 'name': context['name'], 'sprite_path': context['sprite_path'], 'sprite_filename': context['sprite_filename'], 'width': context['width'], 'height': context['height']}) if self.sprite.config['json_format'] == 'array': data['frames'] = frames.values() else: data['frames'] = frames return data glue-0.13/glue/formats/less.py0000644000076500000240000000411312325163000016213 0ustar neostaff00000000000000import os from css import CssFormat class LessFormat(CssFormat): extension = 'less' template = u""" /* glue: {{ version }} hash: {{ hash }} */ {% for image in images %}.{{ image.label }}{{ image.pseudo }}{%- if not image.last %}, {%- endif %}{%- endfor %}{ background-image:url('{{ sprite_path }}'); background-repeat:no-repeat; -webkit-background-size: {{ width }}px {{ height }}px; -moz-background-size: {{ width }}px {{ height }}px; background-size: {{ width }}px {{ height }}px; {% for r, ratio in ratios.items() %} @media screen and (-webkit-min-device-pixel-ratio: {{ ratio.ratio }}), screen and (min--moz-device-pixel-ratio: {{ ratio.ratio }}),screen and (-o-min-device-pixel-ratio: {{ ratio.fraction }}),screen and (min-device-pixel-ratio: {{ ratio.ratio }}),screen and (min-resolution: {{ ratio.ratio }}dppx){ background-image:url('{{ ratio.sprite_path }}'); } {% endfor %} } {% for image in images %} .{{ image.label }}{{ image.pseudo }}{ background-position:{{ image.x ~ ('px' if image.x) }} {{ image.y ~ ('px' if image.y) }}; width:{{ image.width }}px; height:{{ image.height }}px; } {% endfor %} """ @classmethod def populate_argument_parser(cls, parser): group = parser.add_argument_group("CSS format options") group.add_argument("--less", dest="less_dir", nargs='?', const=True, default=os.environ.get('GLUE_LESS', False), metavar='DIR', help="Generate LESS files and optionally where") group.add_argument("--less-template", dest="less_template", default=os.environ.get('GLUE_LESS_TEMPLATE', None), metavar='DIR', help="Template to use to generate the LESS output.") glue-0.13/glue/formats/scss.py0000644000076500000240000000154312322177142016235 0ustar neostaff00000000000000import os from css import CssFormat class ScssFormat(CssFormat): extension = 'scss' @classmethod def populate_argument_parser(cls, parser): group = parser.add_argument_group("SCSS format options") group.add_argument("--scss", dest="scss_dir", nargs='?', const=True, default=os.environ.get('GLUE_SCSS', False), metavar='DIR', help="Generate SCSS files and optionally where") group.add_argument("--scss-template", dest="scss_template", default=os.environ.get('GLUE_SCSS_TEMPLATE', None), metavar='DIR', help="Template to use to generate the SCSS output.") glue-0.13/glue/helpers.py0000644000076500000240000000333012322177142015245 0ustar neostaff00000000000000import os import sys import contextlib from StringIO import StringIO def round_up(value): int_value = int(value) diff = 1 if value > 0 else -1 return int_value + diff if value != int_value else int_value def nearest_fration(value): """ Return the nearest fraction. If fraction.Fraction is not available, return a fraction. Note: used for Opera CSS pixel-ratio media queries. """ try: from fraction import Fraction return str(Fraction(value)) except ImportError: return '%i/100' % int(float(value) * 100) class _Missing(object): """ Missing object necessary for cached_property""" def __repr__(self): return 'no value' def __reduce__(self): return '_missing' _missing = _Missing() class cached_property(object): """ Decorator inspired/copied from mitsuhiko/werkzeug. A decorator that converts a function into a lazy property. The function wrapped is called the first time to retrieve the result and then that calculated result is used the next time you access the value""" def __init__(self, func, name=None, doc=None): self.__name__ = name or func.__name__ self.__module__ = func.__module__ self.__doc__ = doc or func.__doc__ self.func = func def __get__(self, obj, type=None): if obj is None: return self value = obj.__dict__.get(self.__name__, _missing) if value is _missing: value = self.func(obj) obj.__dict__[self.__name__] = value return value @contextlib.contextmanager def redirect_stdout(stream=None): stream = stream or StringIO() sys.stdout = stream yield sys.stdout = sys.__stdout__ glue-0.13/glue/managers/0000755000076500000240000000000013106305703015024 5ustar neostaff00000000000000glue-0.13/glue/managers/__init__.py0000644000076500000240000000014612322177142017141 0ustar neostaff00000000000000from .project import ProjectManager from .simple import SimpleManager from .watch import WatchManager glue-0.13/glue/managers/base.py0000644000076500000240000000264712322177142016324 0ustar neostaff00000000000000from glue.core import Sprite from glue.formats import formats class BaseManager(object): def __init__(self, *args, **kwargs): self.config = kwargs self.sprites = [] def process(self): self.find_sprites() self.validate() self.save() def add_sprite(self, path): """Create a new Sprite using this path and name and append it to the sprites list. :param path: Sprite path. :param name: Sprite name. """ sprite = Sprite(path=path, config=self.config) self.sprites.append(sprite) def find_sprites(self): raise NotImplementedError def validate(self): """Validate all sprites inside this manager.""" for sprite in self.sprites: sprite.validate() def save(self): """Save all sprites inside this manager.""" for format_name in self.config['enabled_formats']: format_cls = formats[format_name] for sprite in self.sprites: format = format_cls(sprite=sprite) format.validate() if format.needs_rebuild() or sprite.config['force']: print "Format '{0}' for sprite '{1}' needs rebuild...".format(format_name, sprite.name) format.build() else: print "Format '{0}'' for sprite '{1}' already exists...".format(format_name, sprite.name) glue-0.13/glue/managers/project.py0000644000076500000240000000241712322177142017053 0ustar neostaff00000000000000import os from glue.exceptions import NoSpritesFoldersFoundError from .base import BaseManager from glue.core import ConfigurableFromFile class ProjectManager(BaseManager, ConfigurableFromFile): """Process a path searching for folders that contain images. Every folder will be a new sprite with all the images inside. This is not the default manager. It is only used if you use the ``--project`` argument.""" def __init__(self, *args, **kwargs): super(ProjectManager, self).__init__(*args, **kwargs) self.config_path = self.config['source'] self.config.update(self._get_config_from_file('sprite.conf', 'sprite')) def find_sprites(self): for filename in sorted(os.listdir(self.config['source'])): # Only process folders path = os.path.join(self.config['source'], filename) # Ignore filenames starting with '.' if filename.startswith('.'): continue # Ignore symlinks if necessary. if not os.path.isdir(path) and not (os.path.islink(path) and self.config['follow_links']): continue self.add_sprite(path=path) if not self.sprites: raise NoSpritesFoldersFoundError(self.config['source']) glue-0.13/glue/managers/simple.py0000644000076500000240000000051212322177142016670 0ustar neostaff00000000000000from .base import BaseManager class SimpleManager(BaseManager): """Process a single folder and create one sprite. It works the same way as :class:`~ProjectSpriteManager`, but only for one folder. This is the default manager. """ def find_sprites(self): self.add_sprite(path=self.config['source']) glue-0.13/glue/managers/watch.py0000644000076500000240000000261212325163000016477 0ustar neostaff00000000000000import os import sys import time import signal import hashlib class WatchManager(object): def __init__(self, manager_cls, options): self.manager_cls = manager_cls self.options = options self.last_hash = self.manager = None signal.signal(signal.SIGINT, self.signal_handler) def process(self): while True: current_hash = self.generate_hash() if self.last_hash != current_hash: self.manager = self.manager_cls(**self.options) self.manager.process() self.last_hash = current_hash time.sleep(0.2) def generate_hash(self): """ Return a hash of files and modification times to determine if a change has occourred.""" hash_list = [] for root, dirs, files in os.walk(self.options['source']): for f in sorted([f for f in files if not f.startswith('.')]): hash_list.append(os.path.join(root, f)) hash_list.append(str(os.path.getmtime(os.path.join(root, f)))) hash_list = ''.join(hash_list) if sys.version < '3': return hashlib.sha1(hash_list).hexdigest() return hashlib.sha1(hash_list.encode('utf-8')).hexdigest() def signal_handler(self, signal, frame): """ Gracefully close the app if Ctrl+C is pressed.""" print 'You pressed Ctrl+C!' sys.exit(0) glue-0.13/glue.egg-info/0000755000076500000240000000000013106305703014721 5ustar neostaff00000000000000glue-0.13/glue.egg-info/dependency_links.txt0000644000076500000240000000000113106305703020767 0ustar neostaff00000000000000 glue-0.13/glue.egg-info/entry_points.txt0000644000076500000240000000005013106305703020212 0ustar neostaff00000000000000[console_scripts] glue = glue.bin:main glue-0.13/glue.egg-info/not-zip-safe0000644000076500000240000000000112325033167017153 0ustar neostaff00000000000000 glue-0.13/glue.egg-info/PKG-INFO0000644000076500000240000000156413106305703016024 0ustar neostaff00000000000000Metadata-Version: 1.1 Name: glue Version: 0.13 Summary: Glue is a simple command line tool to generate sprites. Home-page: http://github.com/jorgebastida/glue Author: Jorge Bastida Author-email: me@jorgebastida.com License: BSD Description: Glue is a simple command line tool to generate sprites using any kind of source images like PNG, JPEG or GIF. Glue will generate a unique PNG file containing every source image and a map file including the necessary information to use it. Keywords: glue sprites css cocos2d Platform: any Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 4 - Beta Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Topic :: Utilities glue-0.13/glue.egg-info/requires.txt0000644000076500000240000000004013106305703017313 0ustar neostaff00000000000000Pillow>=2.2.2 Jinja2>=2.7,<2.10 glue-0.13/glue.egg-info/SOURCES.txt0000644000076500000240000000247413106305703016614 0ustar neostaff00000000000000AUTHORS COPYING MANIFEST.in README.rst setup.py docs/.gitignore docs/Makefile docs/changelog.rst docs/conf.py docs/faq.rst docs/files.rst docs/index.rst docs/installation.rst docs/options.rst docs/pseudoclasses.rst docs/quickstart.rst docs/ratios.rst docs/settings.rst docs/templates.rst docs/img/buttons.png docs/img/crop.png docs/img/famfamfam1.png docs/img/famfamfam2.png docs/img/famfamfam3.png docs/img/nonretina.png docs/img/retina.png docs/img/retina_borders.png docs/img/sprites.png docs/img/sprites@2x.png glue/__init__.py glue/bin.py glue/core.py glue/exceptions.py glue/helpers.py glue.egg-info/PKG-INFO glue.egg-info/SOURCES.txt glue.egg-info/dependency_links.txt glue.egg-info/entry_points.txt glue.egg-info/not-zip-safe glue.egg-info/requires.txt glue.egg-info/top_level.txt glue/algorithms/__init__.py glue/algorithms/diagonal.py glue/algorithms/horizontal.py glue/algorithms/horizontal_bottom.py glue/algorithms/square.py glue/algorithms/vertical.py glue/algorithms/vertical_right.py glue/formats/__init__.py glue/formats/base.py glue/formats/caat.py glue/formats/cocos2d.py glue/formats/css.py glue/formats/html.py glue/formats/img.py glue/formats/jsonformat.py glue/formats/less.py glue/formats/scss.py glue/managers/__init__.py glue/managers/base.py glue/managers/project.py glue/managers/simple.py glue/managers/watch.pyglue-0.13/glue.egg-info/top_level.txt0000644000076500000240000000000513106305703017446 0ustar neostaff00000000000000glue glue-0.13/MANIFEST.in0000644000076500000240000000016412506066476014050 0ustar neostaff00000000000000recursive-include docs * prune docs/_build exclude docs/make.bat include AUTHORS include COPYING include README.rst glue-0.13/PKG-INFO0000644000076500000240000000156413106305703013376 0ustar neostaff00000000000000Metadata-Version: 1.1 Name: glue Version: 0.13 Summary: Glue is a simple command line tool to generate sprites. Home-page: http://github.com/jorgebastida/glue Author: Jorge Bastida Author-email: me@jorgebastida.com License: BSD Description: Glue is a simple command line tool to generate sprites using any kind of source images like PNG, JPEG or GIF. Glue will generate a unique PNG file containing every source image and a map file including the necessary information to use it. Keywords: glue sprites css cocos2d Platform: any Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 4 - Beta Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Topic :: Utilities glue-0.13/README.rst0000644000076500000240000001065212526204145013771 0ustar neostaff00000000000000Glue ==== .. image:: https://badge.fury.io/py/glue.png :target: http://badge.fury.io/py/glue .. image:: https://travis-ci.org/jorgebastida/glue.png?branch=master :target: https://travis-ci.org/jorgebastida/glue .. image:: https://coveralls.io/repos/jorgebastida/glue/badge.png?branch=master :target: https://coveralls.io/r/jorgebastida/glue?branch=master .. image:: https://pypip.in/d/glue/badge.png :target: https://crate.io/packages/glue/ Glue is a simple command line tool to generate sprites:: $ glue source output * The latest documentation is available at: http://glue.readthedocs.org * Installation instructions: http://glue.readthedocs.org/en/latest/installation.html * Glue-users mailing list: https://groups.google.com/forum/#!forum/glue-users Features -------- * Automatic Sprite (Image + Metadata) creation including: - css (less, scss) - cocos2d - json (array, hash) - CAAT * Automatic multi-dpi `retina `_ sprite creation. * Support for multi-sprite projects. * Create sprites from multiple folders (recursively). * Multiple `algorithms `_ available. * Automatic `crop of unnecessary transparent borders `_ around source images. * Configurable `paddings and margin per image, sprite or project `_. * Watch option to keep glue running watching for file changes. * Project-, Sprite- and Image-level configuration via static config files. * Customizable `output `_ using jinja templates. * CSS: Optional .less/.scss output format. * CSS: Configurable `cache busting for sprite images `_. * CSS: Customizable `class names `_. * Python ``2.6``, ``2.7`` and ``3.3+`` supported. * Really `well tested `_. Example ------- Using the gorgeous `famfamfam icons `_ (4.2Mb) you will get the following ``icons.png`` (401Kb). .. image:: https://github.com/jorgebastida/glue/raw/master/docs/img/famfamfam1.png And also an ``icons.css`` with all the necessary CSS classes for this sprite:: .sprite-icons-zoom_out{ background:url('icons.png'); top:0; left:0; no-repeat;} .sprite-icons-zoom_in{ background:url('icons.png'); top:0; left:-16; no-repeat;} .sprite-icons-zoom{ background:url('icons.png'); top:-16; left:0; no-repeat;} .sprite-icons-xhtml_valid{ background:url('icons.png'); top:-16; left:-16; no-repeat;} ... Do you want to know more? Visit the quickstart guide: http://glue.readthedocs.org/en/latest/quickstart.html Contribute ----------- * Fork the repository on GitHub to start making your changes to the master branch (or branch off of it). * Write a test which shows that the bug was fixed or that the feature works as expected. - Use ``python setup.py test`` * Send a pull request and bug the maintainer until it gets merged and published. :) Make sure to add yourself to AUTHORS. Is your company using glue? --------------------------- We are creating a list of companies using glue in production. If your company use ``glue``, please send `me `_ an email or send me a message to `@jorgebastida `_ . I would really appreciate it. We need your help ------------------ There are several features that ``glue`` users would love to have... but they require a substancial amount of work and dedication, so we are looking for feature-sponsors! If you want to lead the development/testing of any of the following features, please contact `Jorge Bastida `_. Here you have some examples: * Windows support (I'm not a Windows user, ``glue`` needs somebody who care about how ``glue`` works on Windows and write down some installation instructions). * Cocos2d Format (Already exists, but we need somebody to give it some love). * New Formats (After 0.9, ``glue`` is ready to accept new output formats - If you want to create a new format, contact me). * Binary packaging for OSX and Windows (For some users it would be really cool if they were able to download an already packaged binary version). glue-0.13/setup.cfg0000644000076500000240000000004613106305703014114 0ustar neostaff00000000000000[egg_info] tag_build = tag_date = 0 glue-0.13/setup.py0000644000076500000240000000345113106305635014014 0ustar neostaff00000000000000import sys from setuptools import setup, find_packages install_requires=[ 'Pillow>=2.2.2', 'Jinja2>=2.7,<2.10', ] tests_require=[ 'cssutils>=0.9.10,<1.0', ] # as of Python >= 2.7 argparse module is maintained within Python. if sys.version_info < (2, 7): install_requires.append('argparse>=1.1') install_requires.append('ordereddict>=1.1') # as of Python >= 3.3 unittest.mock module is maintained within Python. if sys.version_info < (3, 3): tests_require.append('mock>=1.0') setup( name='glue', version='0.13', url='http://github.com/jorgebastida/glue', license='BSD', author='Jorge Bastida', author_email='me@jorgebastida.com', description='Glue is a simple command line tool to generate sprites.', long_description=('Glue is a simple command line tool to generate ' 'sprites using any kind of source images like ' 'PNG, JPEG or GIF. Glue will generate a unique PNG ' 'file containing every source image and a map file ' 'including the necessary information to use it.'), keywords = "glue sprites css cocos2d", packages = find_packages(), platforms='any', install_requires=install_requires, tests_require=tests_require, test_suite='tests', classifiers=[ 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Development Status :: 4 - Beta', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Topic :: Utilities' ], entry_points = { 'console_scripts': [ 'glue = glue.bin:main', ] }, zip_safe = False, use_2to3=True )