From e36e7dd7a2061f287ac093421b9029b34c68a30c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 3 Apr 2015 23:22:13 +1100 Subject: [PATCH 01/42] Added duration set to GifImagePlugin --- PIL/GifImagePlugin.py | 80 ++++++++++++++++++++++++------------------- Scripts/gifmaker.py | 4 +-- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 8db42e8e8..b414a128b 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -292,8 +292,23 @@ def _save(im, fp, filename): for s in header: fp.write(s) - flags = 0 + # local image header + get_local_header(fp, im) + im_out.encoderconfig = (8, get_interlace(im)) + ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0, + RAWMODE[im_out.mode])]) + + fp.write(b"\0") # end of image data + + fp.write(b";") # end of file + + try: + fp.flush() + except: + pass + +def get_interlace(im): try: interlace = im.encoderinfo["interlace"] except KeyError: @@ -302,10 +317,11 @@ def _save(im, fp, filename): # workaround for @PIL153 if min(im.size) < 16: interlace = 0 + + return interlace - if interlace: - flags = flags | 64 - +def get_local_header(fp, im, offset=(0, 0)): + transparent_color_exists = False try: transparency = im.encoderinfo["transparency"] except KeyError: @@ -324,38 +340,36 @@ def _save(im, fp, filename): else: transparent_color_exists = False - # transparency extension block - if transparent_color_exists: - fp.write(b"!" + - o8(249) + # extension intro - o8(4) + # length - o8(1) + # transparency info present - o16(0) + # duration - o8(transparency) + # transparency index - o8(0)) + if 'duration' in im.encoderinfo: + duration = im.encoderinfo["duration"] / 10 + else: + duration = 0 + if transparent_color_exists or duration != 0: + transparency_flag = 1 if transparent_color_exists else 0 + if not transparent_color_exists: + transparency = 0 + + fp.write(b"!" + + o8(249) + # extension intro + o8(4) + # length + o8(transparency_flag) + # transparency info present + o16(duration) + # duration + o8(transparency) + # transparency index + o8(0)) - # local image header + flags = 0 + + if get_interlace(im): + flags = flags | 64 + fp.write(b"," + - o16(0) + o16(0) + # bounding box + o16(offset[0]) + # offset + o16(offset[1]) + o16(im.size[0]) + # size o16(im.size[1]) + o8(flags) + # flags o8(8)) # bits - im_out.encoderconfig = (8, interlace) - ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0, - RAWMODE[im_out.mode])]) - - fp.write(b"\0") # end of image data - - fp.write(b";") # end of file - - try: - fp.flush() - except: - pass - - def _save_netpbm(im, fp, filename): # @@ -510,13 +524,7 @@ def getdata(im, offset=(0, 0), **params): im.encoderinfo = params # local image header - fp.write(b"," + - o16(offset[0]) + # offset - o16(offset[1]) + - o16(im.size[0]) + # size - o16(im.size[1]) + - o8(0) + # flags - o8(8)) # bits + get_local_header(fp, im, offset) ImageFile._save(im, fp, [("gif", (0, 0)+im.size, 0, RAWMODE[im.mode])]) diff --git a/Scripts/gifmaker.py b/Scripts/gifmaker.py index bd4de99c1..420140303 100644 --- a/Scripts/gifmaker.py +++ b/Scripts/gifmaker.py @@ -72,8 +72,8 @@ def makedelta(fp, sequence): for im in sequence: - # - # FIXME: write graphics control block before each frame + # To specify duration, add the time in milliseconds to getdata(), + # e.g. getdata(im, duration=1000) if not previous: From ecebedba7f2d7f8d7b6921886da58fb3239246f1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 Apr 2015 10:32:17 +1100 Subject: [PATCH 02/42] Added loop set to GifImagePlugin --- PIL/GifImagePlugin.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index b414a128b..6c1d53a1c 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -340,7 +340,7 @@ def get_local_header(fp, im, offset=(0, 0)): else: transparent_color_exists = False - if 'duration' in im.encoderinfo: + if "duration" in im.encoderinfo: duration = im.encoderinfo["duration"] / 10 else: duration = 0 @@ -357,6 +357,16 @@ def get_local_header(fp, im, offset=(0, 0)): o8(transparency) + # transparency index o8(0)) + if "loop" in im.encoderinfo: + number_of_loops = im.encoderinfo["loop"] + fp.write(b"!" + + o8(255) + o8(11) + # extension intro + b"NETSCAPE2.0" + + o8(3) + + o8(1) + + o16(number_of_loops) + # number of loops + o8(0)) + flags = 0 if get_interlace(im): From a5917b3fa36fb2128a5451fe355b287056042f7f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 Apr 2015 11:45:30 +1100 Subject: [PATCH 03/42] Added GifImagePlugin tests --- PIL/GifImagePlugin.py | 5 +++-- Tests/test_file_gif.py | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 6c1d53a1c..69df28b30 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -341,7 +341,7 @@ def get_local_header(fp, im, offset=(0, 0)): transparent_color_exists = False if "duration" in im.encoderinfo: - duration = im.encoderinfo["duration"] / 10 + duration = int(im.encoderinfo["duration"] / 10) else: duration = 0 if transparent_color_exists or duration != 0: @@ -360,7 +360,8 @@ def get_local_header(fp, im, offset=(0, 0)): if "loop" in im.encoderinfo: number_of_loops = im.encoderinfo["loop"] fp.write(b"!" + - o8(255) + o8(11) + # extension intro + o8(255) + # extension intro + o8(11) + b"NETSCAPE2.0" + o8(3) + o8(1) + diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 2ce728801..1378fb3f6 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -169,6 +169,33 @@ class TestFileGif(PillowTestCase): # first frame self.assertEqual(img.histogram()[img.info['transparency']], 0) + def test_duration(self): + duration = 1000 + + out = self.tempfile('temp.gif') + fp = open(out, "wb") + im = Image.new('L',(100,100),'#000') + for s in GifImagePlugin.getheader(im)[0] + GifImagePlugin.getdata(im, duration=duration): + fp.write(s) + fp.write(b";") + fp.close() + reread = Image.open(out) + + self.assertEqual(reread.info['duration'], duration) + + def test_number_of_loops(self): + number_of_loops = 2 + + out = self.tempfile('temp.gif') + fp = open(out, "wb") + im = Image.new('L',(100,100),'#000') + for s in GifImagePlugin.getheader(im)[0] + GifImagePlugin.getdata(im, loop=number_of_loops): + fp.write(s) + fp.write(b";") + fp.close() + reread = Image.open(out) + + self.assertEqual(reread.info['loop'], number_of_loops) if __name__ == '__main__': unittest.main() From aa1368f55115b6c33b9a741b55ee805bb567f209 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 23 Apr 2015 23:40:42 +1000 Subject: [PATCH 04/42] Flake8 fixes --- PIL/GifImagePlugin.py | 31 +++++++++++++++++-------------- Tests/test_file_gif.py | 4 ++-- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 69df28b30..cc41da949 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -308,6 +308,7 @@ def _save(im, fp, filename): except: pass + def get_interlace(im): try: interlace = im.encoderinfo["interlace"] @@ -317,9 +318,10 @@ def get_interlace(im): # workaround for @PIL153 if min(im.size) < 16: interlace = 0 - + return interlace + def get_local_header(fp, im, offset=(0, 0)): transparent_color_exists = False try: @@ -348,38 +350,39 @@ def get_local_header(fp, im, offset=(0, 0)): transparency_flag = 1 if transparent_color_exists else 0 if not transparent_color_exists: transparency = 0 - + fp.write(b"!" + - o8(249) + # extension intro - o8(4) + # length - o8(transparency_flag) + # transparency info present - o16(duration) + # duration - o8(transparency) + # transparency index + o8(249) + # extension intro + o8(4) + # length + o8(transparency_flag) + # transparency info present + o16(duration) + # duration + o8(transparency) + # transparency index o8(0)) if "loop" in im.encoderinfo: number_of_loops = im.encoderinfo["loop"] fp.write(b"!" + - o8(255) + # extension intro + o8(255) + # extension intro o8(11) + b"NETSCAPE2.0" + o8(3) + o8(1) + - o16(number_of_loops) + # number of loops + o16(number_of_loops) + # number of loops o8(0)) flags = 0 if get_interlace(im): flags = flags | 64 - + fp.write(b"," + - o16(offset[0]) + # offset + o16(offset[0]) + # offset o16(offset[1]) + - o16(im.size[0]) + # size + o16(im.size[0]) + # size o16(im.size[1]) + - o8(flags) + # flags - o8(8)) # bits + o8(flags) + # flags + o8(8)) # bits + def _save_netpbm(im, fp, filename): diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 1378fb3f6..3b682f86b 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -174,7 +174,7 @@ class TestFileGif(PillowTestCase): out = self.tempfile('temp.gif') fp = open(out, "wb") - im = Image.new('L',(100,100),'#000') + im = Image.new('L', (100, 100), '#000') for s in GifImagePlugin.getheader(im)[0] + GifImagePlugin.getdata(im, duration=duration): fp.write(s) fp.write(b";") @@ -188,7 +188,7 @@ class TestFileGif(PillowTestCase): out = self.tempfile('temp.gif') fp = open(out, "wb") - im = Image.new('L',(100,100),'#000') + im = Image.new('L', (100, 100), '#000') for s in GifImagePlugin.getheader(im)[0] + GifImagePlugin.getdata(im, loop=number_of_loops): fp.write(s) fp.write(b";") From f028928b5acf10eed759827a6900a52542b254e2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 24 Apr 2015 00:44:27 +1000 Subject: [PATCH 05/42] Rearranged used_palette_colors to fix get_local_header --- PIL/GifImagePlugin.py | 52 ++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index cc41da949..4c59b612b 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -332,15 +332,19 @@ def get_local_header(fp, im, offset=(0, 0)): transparency = int(transparency) # optimize the block away if transparent color is not used transparent_color_exists = True - # adjust the transparency index after optimize - if used_palette_colors is not None and len(used_palette_colors) < 256: - for i in range(len(used_palette_colors)): - if used_palette_colors[i] == transparency: - transparency = i - transparent_color_exists = True - break - else: - transparent_color_exists = False + + if _get_optimize(im, im.encoderinfo): + used_palette_colors = _get_used_palette_colors(im) + + # adjust the transparency index after optimize + if len(used_palette_colors) < 256: + for i in range(len(used_palette_colors)): + if used_palette_colors[i] == transparency: + transparency = i + transparent_color_exists = True + break + else: + transparent_color_exists = False if "duration" in im.encoderinfo: duration = int(im.encoderinfo["duration"] / 10) @@ -433,11 +437,26 @@ def _save_netpbm(im, fp, filename): # -------------------------------------------------------------------- # GIF utilities +def _get_optimize(im, info): + return im.mode in ("P", "L") and info and info.get("optimize", 0) + + +def _get_used_palette_colors(im): + used_palette_colors = [] + + # check which colors are used + i = 0 + for count in im.histogram(): + if count: + used_palette_colors.append(i) + i += 1 + + return used_palette_colors + + def getheader(im, palette=None, info=None): """Return a list of strings representing a GIF header""" - optimize = info and info.get("optimize", 0) - # Header Block # http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp header = [ @@ -459,15 +478,8 @@ def getheader(im, palette=None, info=None): used_palette_colors = palette_bytes = None - if im.mode in ("P", "L") and optimize: - used_palette_colors = [] - - # check which colors are used - i = 0 - for count in im.histogram(): - if count: - used_palette_colors.append(i) - i += 1 + if _get_optimize(im, info): + used_palette_colors = _get_used_palette_colors(im) # create the new palette if not every color is used if len(used_palette_colors) < 256: From 1dd3bef61507847375772ffd207e8451fa5c897e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 10 May 2015 23:55:33 +1000 Subject: [PATCH 06/42] Upgraded OleFileIO to 0.42b --- PIL/OleFileIO.py | 941 +++++++++++++++++++++++++++++++---------------- 1 file changed, 616 insertions(+), 325 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index c804dd454..d787e59ed 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -1,47 +1,70 @@ #!/usr/bin/env python -## OleFileIO_PL: -## Module to read Microsoft OLE2 files (also called Structured Storage or -## Microsoft Compound Document File Format), such as Microsoft Office -## documents, Image Composer and FlashPix files, Outlook messages, ... -## This version is compatible with Python 2.6+ and 3.x -## version 0.30 2014-02-04 Philippe Lagadec - http://www.decalage.info - -## Project website: http://www.decalage.info/python/olefileio - -## Improved version of the OleFileIO module from PIL library v1.1.6 -## See: http://www.pythonware.com/products/pil/index.htm - -## The Python Imaging Library (PIL) is - -## Copyright (c) 1997-2005 by Secret Labs AB -## Copyright (c) 1995-2005 by Fredrik Lundh - -## OleFileIO_PL changes are Copyright (c) 2005-2014 by Philippe Lagadec - -## See source code and LICENSE.txt for information on usage and redistribution. - -## WARNING: THIS IS (STILL) WORK IN PROGRESS. +# olefile (formerly OleFileIO_PL) version 0.42 2015-01-25 +# +# Module to read/write Microsoft OLE2 files (also called Structured Storage or +# Microsoft Compound Document File Format), such as Microsoft Office 97-2003 +# documents, Image Composer and FlashPix files, Outlook messages, ... +# This version is compatible with Python 2.6+ and 3.x +# +# Project website: http://www.decalage.info/olefile +# +# olefile is copyright (c) 2005-2015 Philippe Lagadec (http://www.decalage.info) +# +# olefile is based on the OleFileIO module from the PIL library v1.1.6 +# See: http://www.pythonware.com/products/pil/index.htm +# +# The Python Imaging Library (PIL) is +# Copyright (c) 1997-2005 by Secret Labs AB +# Copyright (c) 1995-2005 by Fredrik Lundh +# +# See source code and LICENSE.txt for information on usage and redistribution. -# Starting with OleFileIO_PL v0.30, only Python 2.6+ and 3.x is supported +# Since OleFileIO_PL v0.30, only Python 2.6+ and 3.x is supported # This import enables print() as a function rather than a keyword # (main requirement to be compatible with Python 3.x) # The comment on the line below should be printed on Python 2.5 or older: -from __future__ import print_function # This version of OleFileIO_PL requires Python 2.6+ or 3.x. +from __future__ import print_function # This version of olefile requires Python 2.6+ or 3.x. -__author__ = "Philippe Lagadec, Fredrik Lundh (Secret Labs AB)" -__date__ = "2014-02-04" -__version__ = '0.30' +__author__ = "Philippe Lagadec" +__date__ = "2015-01-25" +__version__ = '0.42b' #--- LICENSE ------------------------------------------------------------------ -# OleFileIO_PL is an improved version of the OleFileIO module from the -# Python Imaging Library (PIL). - -# OleFileIO_PL changes are Copyright (c) 2005-2014 by Philippe Lagadec +# olefile (formerly OleFileIO_PL) is copyright (c) 2005-2015 Philippe Lagadec +# (http://www.decalage.info) # +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# * 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. +# +# 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 HOLDER 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. + +# ---------- +# PIL License: +# +# olefile is based on source code from the OleFileIO module of the Python +# Imaging Library (PIL) published by Fredrik Lundh under the following license: + # The Python Imaging Library (PIL) is # Copyright (c) 1997-2005 by Secret Labs AB # Copyright (c) 1995-2005 by Fredrik Lundh @@ -67,7 +90,7 @@ __version__ = '0.30' # PERFORMANCE OF THIS SOFTWARE. #----------------------------------------------------------------------------- -# CHANGELOG: (only OleFileIO_PL changes compared to PIL 1.1.6) +# CHANGELOG: (only olefile/OleFileIO_PL changes compared to PIL 1.1.6) # 2005-05-11 v0.10 PL: - a few fixes for Python 2.4 compatibility # (all changes flagged with [PL]) # 2006-02-22 v0.11 PL: - a few fixes for some Office 2003 documents which raise @@ -142,10 +165,29 @@ __version__ = '0.30' # 2014-02-04 v0.30 PL: - upgraded code to support Python 3.x by Martin Panter # - several fixes for Python 2.6 (xrange, MAGIC) # - reused i32 from Pillow's _binary +# 2014-07-18 v0.31 - preliminary support for 4K sectors +# 2014-07-27 v0.31 PL: - a few improvements in OleFileIO.open (header parsing) +# - Fixed loadfat for large files with 4K sectors (issue #3) +# 2014-07-30 v0.32 PL: - added write_sect to write sectors to disk +# - added write_mode option to OleFileIO.__init__ and open +# 2014-07-31 PL: - fixed padding in write_sect for Python 3, added checks +# - added write_stream to write a stream to disk +# 2014-09-26 v0.40 PL: - renamed OleFileIO_PL to olefile +# 2014-11-09 NE: - added support for Jython (Niko Ehrenfeuchter) +# 2014-11-13 v0.41 PL: - improved isOleFile and OleFileIO.open to support OLE +# data in a string buffer and file-like objects. +# 2014-11-21 PL: - updated comments according to Pillow's commits +# 2015-01-24 v0.42 PL: - changed the default path name encoding from Latin-1 +# to UTF-8 on Python 2.x (Unicode on Python 3.x) +# - added path_encoding option to override the default +# - fixed a bug in _list when a storage is empty #----------------------------------------------------------------------------- # TODO (for version 1.0): -# + isOleFile should accept file-like objects like open +# + get rid of print statements, to simplify Python 2.x and 3.x support +# + add is_stream and is_storage +# + remove leading and trailing slashes where a path is used +# + add functions path_list2str and path_str2list # + fix how all the methods handle unicode str and/or bytes as arguments # + add path attrib to _OleDirEntry, set it once and for all in init or # append_kids (then listdir/_list can be simplified) @@ -177,30 +219,16 @@ __version__ = '0.30' # - move all debug code (and maybe dump methods) to a separate module, with # a class which inherits OleFileIO ? # - fix docstrings to follow epydoc format -# - add support for 4K sectors ? # - add support for big endian byte order ? # - create a simple OLE explorer with wxPython # FUTURE EVOLUTIONS to add write support: -# 1) add ability to write a stream back on disk from BytesIO (same size, no -# change in FAT/MiniFAT). -# 2) rename a stream/storage if it doesn't change the RB tree -# 3) use rbtree module to update the red-black tree + any rename -# 4) remove a stream/storage: free sectors in FAT/MiniFAT -# 5) allocate new sectors in FAT/MiniFAT -# 6) create new storage/stream -#----------------------------------------------------------------------------- +# see issue #6 on Bitbucket: +# https://bitbucket.org/decalage/olefileio_pl/issue/6/improve-olefileio_pl-to-write-ole-files + +#----------------------------------------------------------------------------- +# NOTES from PIL 1.1.6: -# -# THIS IS WORK IN PROGRESS -# -# The Python Imaging Library -# $Id$ -# -# stuff to deal with OLE2 Structured Storage files. this module is -# used by PIL to read Image Composer and FlashPix files, but can also -# be used to read other files of this type. -# # History: # 1997-01-20 fl Created # 1997-01-22 fl Fixed 64-bit portability quirk @@ -222,25 +250,19 @@ __version__ = '0.30' # "If this document and functionality of the Software conflict, # the actual functionality of the Software represents the correct # functionality" -- Microsoft, in the OLE format specification -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# #------------------------------------------------------------------------------ import io import sys -import struct -import array -import os.path -import datetime +import struct, array, os.path, datetime + +#=== COMPATIBILITY WORKAROUNDS ================================================ #[PL] Define explicitly the public API to avoid private objects in pydoc: -__all__ = ['OleFileIO', 'isOleFile', 'MAGIC'] +#TODO: add more +# __all__ = ['OleFileIO', 'isOleFile', 'MAGIC'] # For Python 3.x, need to redefine long as int: if str is not bytes: @@ -261,39 +283,66 @@ if array.array('L').itemsize == 4: elif array.array('I').itemsize == 4: # on 64 bits platforms, integers in an array are 32 bits: UINT32 = 'I' +elif array.array('i').itemsize == 4: + # On 64 bit Jython, signed integers ('i') are the only way to store our 32 + # bit values in an array in a *somewhat* reasonable way, as the otherwise + # perfectly suited 'H' (unsigned int, 32 bits) results in a completely + # unusable behaviour. This is most likely caused by the fact that Java + # doesn't have unsigned values, and thus Jython's "array" implementation, + # which is based on "jarray", doesn't have them either. + # NOTE: to trick Jython into converting the values it would normally + # interpret as "signed" into "unsigned", a binary-and operation with + # 0xFFFFFFFF can be used. This way it is possible to use the same comparing + # operations on all platforms / implementations. The corresponding code + # lines are flagged with a 'JYTHON-WORKAROUND' tag below. + UINT32 = 'i' else: raise ValueError('Need to fix a bug with 32 bit arrays, please contact author...') #[PL] These workarounds were inspired from the Path module # (see http://www.jorendorff.com/articles/python/path/) +#TODO: test with old Python versions + +# Pre-2.3 workaround for basestring. try: basestring except NameError: - basestring = str + try: + # is Unicode supported (Python >2.0 or >1.6 ?) + basestring = (str, unicode) + except NameError: + basestring = str #[PL] Experimental setting: if True, OLE filenames will be kept in Unicode # if False (default PIL behaviour), all filenames are converted to Latin-1. -KEEP_UNICODE_NAMES = False +KEEP_UNICODE_NAMES = True + +if sys.version_info[0] < 3: + # On Python 2.x, the default encoding for path names is UTF-8: + DEFAULT_PATH_ENCODING = 'utf-8' +else: + # On Python 3.x, the default encoding for path names is Unicode (None): + DEFAULT_PATH_ENCODING = None + + +#=== DEBUGGING =============================================================== + +#TODO: replace this by proper logging #[PL] DEBUG display mode: False by default, use set_debug_mode() or "-d" on # command line to change it. DEBUG_MODE = False - - def debug_print(msg): print(msg) - - def debug_pass(msg): pass debug = debug_pass - def set_debug_mode(debug_mode): """ Set debug mode on or off, to control display of debugging messages. - mode: True or False + :param mode: True or False """ global DEBUG_MODE, debug DEBUG_MODE = debug_mode @@ -302,26 +351,30 @@ def set_debug_mode(debug_mode): else: debug = debug_pass + +#=== CONSTANTS =============================================================== + +# magic bytes that should be at the beginning of every OLE file: MAGIC = b'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1' -# [PL]: added constants for Sector IDs (from AAF specifications) -MAXREGSECT = 0xFFFFFFFA; # maximum SECT -DIFSECT = 0xFFFFFFFC; # (-4) denotes a DIFAT sector in a FAT -FATSECT = 0xFFFFFFFD; # (-3) denotes a FAT sector in a FAT -ENDOFCHAIN = 0xFFFFFFFE; # (-2) end of a virtual stream chain -FREESECT = 0xFFFFFFFF; # (-1) unallocated sector +#[PL]: added constants for Sector IDs (from AAF specifications) +MAXREGSECT = 0xFFFFFFFA # (-6) maximum SECT +DIFSECT = 0xFFFFFFFC # (-4) denotes a DIFAT sector in a FAT +FATSECT = 0xFFFFFFFD # (-3) denotes a FAT sector in a FAT +ENDOFCHAIN = 0xFFFFFFFE # (-2) end of a virtual stream chain +FREESECT = 0xFFFFFFFF # (-1) unallocated sector -# [PL]: added constants for Directory Entry IDs (from AAF specifications) -MAXREGSID = 0xFFFFFFFA; # maximum directory entry ID -NOSTREAM = 0xFFFFFFFF; # (-1) unallocated directory entry +#[PL]: added constants for Directory Entry IDs (from AAF specifications) +MAXREGSID = 0xFFFFFFFA # (-6) maximum directory entry ID +NOSTREAM = 0xFFFFFFFF # (-1) unallocated directory entry -# [PL] object types in storage (from AAF specifications) -STGTY_EMPTY = 0 # empty directory entry (according to OpenOffice.org doc) -STGTY_STORAGE = 1 # element is a storage object -STGTY_STREAM = 2 # element is a stream object -STGTY_LOCKBYTES = 3 # element is an ILockBytes object -STGTY_PROPERTY = 4 # element is an IPropertyStorage object -STGTY_ROOT = 5 # element is a root storage +#[PL] object types in storage (from AAF specifications) +STGTY_EMPTY = 0 # empty directory entry (according to OpenOffice.org doc) +STGTY_STORAGE = 1 # element is a storage object +STGTY_STREAM = 2 # element is a stream object +STGTY_LOCKBYTES = 3 # element is an ILockBytes object +STGTY_PROPERTY = 4 # element is an IPropertyStorage object +STGTY_ROOT = 5 # element is a root storage # @@ -353,30 +406,52 @@ WORD_CLSID = "00020900-0000-0000-C000-000000000046" #TODO: check Excel, PPT, ... #[PL]: Defect levels to classify parsing errors - see OleFileIO._raise_defect() -DEFECT_UNSURE = 10 # a case which looks weird, but not sure it's a defect -DEFECT_POTENTIAL = 20 # a potential defect -DEFECT_INCORRECT = 30 # an error according to specifications, but parsing - # can go on -DEFECT_FATAL = 40 # an error which cannot be ignored, parsing is - # impossible +DEFECT_UNSURE = 10 # a case which looks weird, but not sure it's a defect +DEFECT_POTENTIAL = 20 # a potential defect +DEFECT_INCORRECT = 30 # an error according to specifications, but parsing + # can go on +DEFECT_FATAL = 40 # an error which cannot be ignored, parsing is + # impossible + +# Minimal size of an empty OLE file, with 512-bytes sectors = 1536 bytes +# (this is used in isOleFile and OleFile.open) +MINIMAL_OLEFILE_SIZE = 1536 #[PL] add useful constants to __all__: -for key in list(vars().keys()): - if key.startswith('STGTY_') or key.startswith('DEFECT_'): - __all__.append(key) +# for key in list(vars().keys()): +# if key.startswith('STGTY_') or key.startswith('DEFECT_'): +# __all__.append(key) -#--- FUNCTIONS ---------------------------------------------------------------- +#=== FUNCTIONS =============================================================== def isOleFile (filename): """ - Test if file is an OLE container (according to its header). + Test if a file is an OLE container (according to the magic bytes in its header). + + :param filename: string-like or file-like object, OLE file to parse + + - if filename is a string smaller than 1536 bytes, it is the path + of the file to open. (bytes or unicode string) + - if filename is a string longer than 1535 bytes, it is parsed + as the content of an OLE file in memory. (bytes type only) + - if filename is a file-like object (with read and seek methods), + it is parsed as-is. - :param filename: file name or path (str, unicode) :returns: True if OLE, False otherwise. """ - f = open(filename, 'rb') - header = f.read(len(MAGIC)) + # check if filename is a string-like or file-like object: + if hasattr(filename, 'read'): + # file-like object: use it directly + header = filename.read(len(MAGIC)) + # just in case, seek back to start of file: + filename.seek(0) + elif isinstance(filename, bytes) and len(filename) >= MINIMAL_OLEFILE_SIZE: + # filename is a bytes string containing the OLE file to be parsed: + header = filename[:len(MAGIC)] + else: + # string-like object: filename of file on disk + header = open(filename, 'rb').read(len(MAGIC)) if header == MAGIC: return True else: @@ -434,41 +509,17 @@ def _clsid(clsid): tuple(map(i8, clsid[8:16])))) -# UNICODE support: -# (necessary to handle storages/streams names which use Unicode) - -def _unicode(s, errors='replace'): - """ - Map unicode string to Latin 1. (Python with Unicode support) - - :param s: UTF-16LE unicode string to convert to Latin-1 - :param errors: 'replace', 'ignore' or 'strict'. - """ - #TODO: test if it OleFileIO works with Unicode strings, instead of - # converting to Latin-1. - try: - # First the string is converted to plain Unicode: - # (assuming it is encoded as UTF-16 little-endian) - u = s.decode('UTF-16LE', errors) - if bytes is not str or KEEP_UNICODE_NAMES: - return u - else: - # Second the unicode string is converted to Latin-1 - return u.encode('latin_1', errors) - except: - # there was an error during Unicode to Latin-1 conversion: - raise IOError('incorrect Unicode name') - def filetime2datetime(filetime): - """ - convert FILETIME (64 bits int) to Python datetime.datetime - """ - # TODO: manage exception when microseconds is too large - # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ - _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) - #debug('timedelta days=%d' % (filetime//(10*1000000*3600*24))) - return _FILETIME_null_date + datetime.timedelta(microseconds=filetime//10) + """ + convert FILETIME (64 bits int) to Python datetime.datetime + """ + # TODO: manage exception when microseconds is too large + # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ + _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) + #debug('timedelta days=%d' % (filetime//(10*1000000*3600*24))) + return _FILETIME_null_date + datetime.timedelta(microseconds=filetime//10) + #=== CLASSES ================================================================== @@ -578,6 +629,7 @@ class OleMetadata: self.language = None self.doc_version = None + def parse_properties(self, olefile): """ Parse standard properties of an OLE file, from the streams @@ -639,6 +691,7 @@ class _OleStream(io.BytesIO): fat table arguments. Attributes: + - size: actual size of data stream, after it was opened. """ @@ -650,18 +703,18 @@ class _OleStream(io.BytesIO): """ Constructor for _OleStream class. - :param fp : file object, the OLE container or the MiniFAT stream - :param sect : sector index of first sector in the stream - :param size : total size of the stream - :param offset : offset in bytes for the first FAT or MiniFAT sector + :param fp: file object, the OLE container or the MiniFAT stream + :param sect: sector index of first sector in the stream + :param size: total size of the stream + :param offset: offset in bytes for the first FAT or MiniFAT sector :param sectorsize: size of one sector - :param fat : array/list of sector indexes (FAT or MiniFAT) - :param filesize : size of OLE file (for debugging) - :returns : a BytesIO instance containing the OLE stream + :param fat: array/list of sector indexes (FAT or MiniFAT) + :param filesize: size of OLE file (for debugging) + :returns: a BytesIO instance containing the OLE stream """ debug('_OleStream.__init__:') debug(' sect=%d (%X), size=%d, offset=%d, sectorsize=%d, len(fat)=%d, fp=%s' - %(sect, sect, size, offset, sectorsize, len(fat), repr(fp))) + %(sect,sect,size,offset,sectorsize,len(fat), repr(fp))) #[PL] To detect malformed documents with FAT loops, we compute the # expected number of sectors in the stream: unknown_size = False @@ -729,7 +782,7 @@ class _OleStream(io.BytesIO): data.append(sector_data) # jump to next sector in the FAT: try: - sect = fat[sect] + sect = fat[sect] & 0xFFFFFFFF # JYTHON-WORKAROUND except IndexError: # [PL] if pointer is out of the FAT an exception is raised raise IOError('incorrect OLE FAT, sector index out of range') @@ -787,6 +840,7 @@ class _OleDirectoryEntry: DIRENTRY_SIZE = 128 assert struct.calcsize(STRUCT_DIRENTRY) == DIRENTRY_SIZE + def __init__(self, entry, sid, olefile): """ Constructor for an _OleDirectoryEntry object. @@ -842,8 +896,11 @@ class _OleDirectoryEntry: namelength = 64 # only characters without ending null char are kept: name = name[:(namelength-2)] - # name is converted from unicode to Latin-1: - self.name = _unicode(name) + #TODO: check if the name is actually followed by a null unicode character ([MS-CFB] 2.6.1) + #TODO: check if the name does not contain forbidden characters: + # [MS-CFB] 2.6.1: "The following characters are illegal and MUST NOT be part of the name: '/', '\', ':', '!'." + # name is converted from UTF-16LE to the path encoding specified in the OleFileIO: + self.name = olefile._decode_utf16_str(name) debug('DirEntry SID=%d: %s' % (self.sid, repr(self.name))) debug(' - type: %d' % self.entry_type) @@ -879,6 +936,8 @@ class _OleDirectoryEntry: minifat = False olefile._check_duplicate_stream(self.isectStart, minifat) + + def build_storage_tree(self): """ Read and build the red-black tree attached to this _OleDirectoryEntry @@ -902,15 +961,16 @@ class _OleDirectoryEntry: # (see rich comparison methods in this class) self.kids.sort() + def append_kids(self, child_sid): """ Walk through red-black tree of children of this directory entry to add all of them to the kids list. (recursive method) - child_sid : index of child directory entry to use, or None when called - first time for the root. (only used during recursion) + :param child_sid : index of child directory entry to use, or None when called + first time for the root. (only used during recursion) """ - # [PL] this method was added to use simple recursion instead of a complex + #[PL] this method was added to use simple recursion instead of a complex # algorithm. # if this is not a storage or a leaf of the tree, nothing to do: if child_sid == NOSTREAM: @@ -945,6 +1005,7 @@ class _OleDirectoryEntry: # Afterwards build kid's own tree if it's also a storage: child.build_storage_tree() + def __eq__(self, other): "Compare entries by name" return self.name == other.name @@ -964,6 +1025,7 @@ class _OleDirectoryEntry: #TODO: replace by the same function as MS implementation ? # (order by name length first, then case-insensitive order) + def dump(self, tab = 0): "Dump this entry, and all its subentries (for debug purposes only)" TYPES = ["(invalid)", "(storage)", "(stream)", "(lockbytes)", @@ -978,12 +1040,13 @@ class _OleDirectoryEntry: for kid in self.kids: kid.dump(tab + 2) + def getmtime(self): """ Return modification time of a directory entry. :returns: None if modification time is null, a python datetime object - otherwise (UTC timezone) + otherwise (UTC timezone) new in version 0.26 """ @@ -991,12 +1054,13 @@ class _OleDirectoryEntry: return None return filetime2datetime(self.modifyTime) + def getctime(self): """ Return creation time of a directory entry. :returns: None if modification time is null, a python datetime object - otherwise (UTC timezone) + otherwise (UTC timezone) new in version 0.26 """ @@ -1012,8 +1076,7 @@ class OleFileIO: OLE container object This class encapsulates the interface to an OLE 2 structured - storage file. Use the :py:meth:`~PIL.OleFileIO.OleFileIO.listdir` and - :py:meth:`~PIL.OleFileIO.OleFileIO.openstream` methods to + storage file. Use the listdir and openstream methods to access the contents of this file. Object names are given as a list of strings, one for each subentry @@ -1037,22 +1100,47 @@ class OleFileIO: TIFF files). """ - def __init__(self, filename = None, raise_defects=DEFECT_FATAL): + def __init__(self, filename=None, raise_defects=DEFECT_FATAL, + write_mode=False, debug=False, path_encoding=DEFAULT_PATH_ENCODING): """ - Constructor for OleFileIO class. + Constructor for the OleFileIO class. :param filename: file to open. + + - if filename is a string smaller than 1536 bytes, it is the path + of the file to open. (bytes or unicode string) + - if filename is a string longer than 1535 bytes, it is parsed + as the content of an OLE file in memory. (bytes type only) + - if filename is a file-like object (with read, seek and tell methods), + it is parsed as-is. + :param raise_defects: minimal level for defects to be raised as exceptions. - (use DEFECT_FATAL for a typical application, DEFECT_INCORRECT for a - security-oriented application, see source code for details) + (use DEFECT_FATAL for a typical application, DEFECT_INCORRECT for a + security-oriented application, see source code for details) + + :param write_mode: bool, if True the file is opened in read/write mode instead + of read-only by default. + + :param debug: bool, set debug mode + + :param path_encoding: None or str, name of the codec to use for path + names (streams and storages), or None for Unicode. + Unicode by default on Python 3+, UTF-8 on Python 2.x. + (new in olefile 0.42, was hardcoded to Latin-1 until olefile v0.41) """ + set_debug_mode(debug) # minimal level for defects to be raised as exceptions: self._raise_defects_level = raise_defects # list of defects/issues not raised as exceptions: # tuples of (exception type, message) self.parsing_issues = [] + self.write_mode = write_mode + self.path_encoding = path_encoding + self._filesize = None + self.fp = None if filename: - self.open(filename) + self.open(filename, write_mode=write_mode) + def _raise_defect(self, defect_level, message, exception_type=IOError): """ @@ -1061,10 +1149,12 @@ class OleFileIO: for the OleFileIO object. :param defect_level: defect level, possible values are: - DEFECT_UNSURE : a case which looks weird, but not sure it's a defect - DEFECT_POTENTIAL : a potential defect - DEFECT_INCORRECT : an error according to specifications, but parsing can go on - DEFECT_FATAL : an error which cannot be ignored, parsing is impossible + + - DEFECT_UNSURE : a case which looks weird, but not sure it's a defect + - DEFECT_POTENTIAL : a potential defect + - DEFECT_INCORRECT : an error according to specifications, but parsing can go on + - DEFECT_FATAL : an error which cannot be ignored, parsing is impossible + :param message: string describing the defect, used with raised exception. :param exception_type: exception class to be raised, IOError by default """ @@ -1075,31 +1165,70 @@ class OleFileIO: # just record the issue, no exception raised: self.parsing_issues.append((exception_type, message)) - def open(self, filename): - """ - Open an OLE2 file. - Reads the header, FAT and directory. - :param filename: string-like or file-like object + def _decode_utf16_str(self, utf16_str, errors='replace'): """ + Decode a string encoded in UTF-16 LE format, as found in the OLE + directory or in property streams. Return a string encoded + according to the path_encoding specified for the OleFileIO object. + + :param utf16_str: bytes string encoded in UTF-16 LE format + :param errors: str, see python documentation for str.decode() + :return: str, encoded according to path_encoding + """ + unicode_str = utf16_str.decode('UTF-16LE', errors) + if self.path_encoding: + # an encoding has been specified for path names: + return unicode_str.encode(self.path_encoding, errors) + else: + # path_encoding=None, return the Unicode string as-is: + return unicode_str + + + def open(self, filename, write_mode=False): + """ + Open an OLE2 file in read-only or read/write mode. + Read and parse the header, FAT and directory. + + :param filename: string-like or file-like object, OLE file to parse + + - if filename is a string smaller than 1536 bytes, it is the path + of the file to open. (bytes or unicode string) + - if filename is a string longer than 1535 bytes, it is parsed + as the content of an OLE file in memory. (bytes type only) + - if filename is a file-like object (with read, seek and tell methods), + it is parsed as-is. + + :param write_mode: bool, if True the file is opened in read/write mode instead + of read-only by default. (ignored if filename is not a path) + """ + self.write_mode = write_mode #[PL] check if filename is a string-like or file-like object: # (it is better to check for a read() method) if hasattr(filename, 'read'): - # file-like object + #TODO: also check seek and tell methods? + # file-like object: use it directly self.fp = filename + elif isinstance(filename, bytes) and len(filename) >= MINIMAL_OLEFILE_SIZE: + # filename is a bytes string containing the OLE file to be parsed: + # convert it to BytesIO + self.fp = io.BytesIO(filename) else: # string-like object: filename of file on disk - #TODO: if larger than 1024 bytes, this could be the actual data => BytesIO - self.fp = open(filename, "rb") - # old code fails if filename is not a plain string: - #if isinstance(filename, (bytes, basestring)): - # self.fp = open(filename, "rb") - #else: - # self.fp = filename + if self.write_mode: + # open file in mode 'read with update, binary' + # According to https://docs.python.org/2/library/functions.html#open + # 'w' would truncate the file, 'a' may only append on some Unixes + mode = 'r+b' + else: + # read-only mode by default + mode = 'rb' + self.fp = open(filename, mode) # obtain the filesize by using seek and tell, which should work on most # file-like objects: #TODO: do it above, using getsize with filename when possible? #TODO: fix code to fail with clear exception when filesize cannot be obtained + filesize=0 self.fp.seek(0, os.SEEK_END) try: filesize = self.fp.tell() @@ -1177,7 +1306,7 @@ class OleFileIO: self.sectDifStart, self.csectDif ) = struct.unpack(fmt_header, header1) - debug(struct.unpack(fmt_header, header1)) + debug( struct.unpack(fmt_header, header1)) if self.Sig != MAGIC: # OLE signature should always be present @@ -1196,6 +1325,7 @@ class OleFileIO: # For now only common little-endian documents are handled correctly self._raise_defect(DEFECT_FATAL, "incorrect ByteOrder in OLE header") # TODO: add big-endian support for documents created on Mac ? + # But according to [MS-CFB] ? v20140502, ByteOrder MUST be 0xFFFE. self.SectorSize = 2**self.SectorShift debug( "SectorSize = %d" % self.SectorSize ) if self.SectorSize not in [512, 4096]: @@ -1210,28 +1340,44 @@ class OleFileIO: if self.Reserved != 0 or self.Reserved1 != 0: self._raise_defect(DEFECT_INCORRECT, "incorrect OLE header (non-null reserved bytes)") debug( "csectDir = %d" % self.csectDir ) + # Number of directory sectors (only allowed if DllVersion != 3) if self.SectorSize==512 and self.csectDir!=0: self._raise_defect(DEFECT_INCORRECT, "incorrect csectDir in OLE header") debug( "csectFat = %d" % self.csectFat ) + # csectFat = number of FAT sectors in the file debug( "sectDirStart = %X" % self.sectDirStart ) + # sectDirStart = 1st sector containing the directory debug( "signature = %d" % self.signature ) # Signature should be zero, BUT some implementations do not follow this # rule => only a potential defect: + # (according to MS-CFB, may be != 0 for applications supporting file + # transactions) if self.signature != 0: self._raise_defect(DEFECT_POTENTIAL, "incorrect OLE header (signature>0)") - debug("MiniSectorCutoff = %d" % self.MiniSectorCutoff) - debug("MiniFatStart = %X" % self.MiniFatStart) - debug("csectMiniFat = %d" % self.csectMiniFat) - debug("sectDifStart = %X" % self.sectDifStart) - debug("csectDif = %d" % self.csectDif) + debug( "MiniSectorCutoff = %d" % self.MiniSectorCutoff ) + # MS-CFB: This integer field MUST be set to 0x00001000. This field + # specifies the maximum size of a user-defined data stream allocated + # from the mini FAT and mini stream, and that cutoff is 4096 bytes. + # Any user-defined data stream larger than or equal to this cutoff size + # must be allocated as normal sectors from the FAT. + if self.MiniSectorCutoff != 0x1000: + self._raise_defect(DEFECT_INCORRECT, "incorrect MiniSectorCutoff in OLE header") + debug( "MiniFatStart = %X" % self.MiniFatStart ) + debug( "csectMiniFat = %d" % self.csectMiniFat ) + debug( "sectDifStart = %X" % self.sectDifStart ) + debug( "csectDif = %d" % self.csectDif ) # calculate the number of sectors in the file # (-1 because header doesn't count) self.nb_sect = ( (filesize + self.SectorSize-1) // self.SectorSize) - 1 debug( "Number of sectors in the file: %d" % self.nb_sect ) + #TODO: change this test, because an OLE file MAY contain other data + # after the last sector. - # file clsid (probably never used, so we don't store it) - #clsid = _clsid(header[8:24]) + # file clsid + self.clsid = _clsid(header[8:24]) + + #TODO: remove redundant attributes, and fix the code which uses them? self.sectorsize = self.SectorSize #1 << i16(header, 30) self.minisectorsize = self.MiniSectorSize #1 << i16(header, 32) self.minisectorcutoff = self.MiniSectorCutoff # i32(header, 56) @@ -1254,19 +1400,22 @@ class OleFileIO: self.ministream = None self.minifatsect = self.MiniFatStart #i32(header, 60) + def close(self): """ close the OLE file, to release the file object """ self.fp.close() + def _check_duplicate_stream(self, first_sect, minifat=False): """ Checks if a stream has not been already referenced elsewhere. This method should only be called once for each known stream, and only if stream size is not null. - :param first_sect: index of first sector of the stream in FAT - :param minifat: if True, stream is located in the MiniFAT, else in the FAT + + :param first_sect: int, index of first sector of the stream in FAT + :param minifat: bool, if True, stream is located in the MiniFAT, else in the FAT """ if minifat: debug('_check_duplicate_stream: sect=%d in MiniFAT' % first_sect) @@ -1284,13 +1433,14 @@ class OleFileIO: else: used_streams.append(first_sect) + def dumpfat(self, fat, firstindex=0): "Displays a part of FAT in human-readable form for debugging purpose" # [PL] added only for debug if not DEBUG_MODE: return # dictionary to convert special FAT values in human-readable strings - VPL=8 # valeurs par ligne (8+1 * 8+1 = 81) + VPL = 8 # values per line (8+1 * 8+1 = 81) fatnames = { FREESECT: "..free..", ENDOFCHAIN: "[ END. ]", @@ -1310,22 +1460,26 @@ class OleFileIO: if i>=nbsect: break sect = fat[i] - if sect in fatnames: - nom = fatnames[sect] + aux = sect & 0xFFFFFFFF # JYTHON-WORKAROUND + if aux in fatnames: + name = fatnames[aux] else: if sect == i+1: - nom = " --->" + name = " --->" else: - nom = "%8X" % sect - print(nom, end=" ") + name = "%8X" % sect + print(name, end=" ") print() + def dumpsect(self, sector, firstindex=0): "Displays a sector in a human-readable form, for debugging purpose." if not DEBUG_MODE: return VPL=8 # number of values per line (8+1 * 8+1 = 81) tab = array.array(UINT32, sector) + if sys.byteorder == 'big': + tab.byteswap() nbsect = len(tab) nlines = (nbsect+VPL-1)//VPL print("index", end=" ") @@ -1339,8 +1493,8 @@ class OleFileIO: if i>=nbsect: break sect = tab[i] - nom = "%8X" % sect - print(nom, end=" ") + name = "%8X" % sect + print(name, end=" ") print() def sect2array(self, sect): @@ -1354,6 +1508,7 @@ class OleFileIO: a.byteswap() return a + def loadfat_sect(self, sect): """ Adds the indexes of the given sector to the FAT @@ -1371,9 +1526,11 @@ class OleFileIO: self.dumpsect(sect) # The FAT is a sector chain starting at the first index of itself. for isect in fat1: - #print("isect = %X" % isect) + isect = isect & 0xFFFFFFFF # JYTHON-WORKAROUND + debug("isect = %X" % isect) if isect == ENDOFCHAIN or isect == FREESECT: # the end of the sector chain has been reached + debug("found end of sector chain") break # read the FAT sector s = self.getsect(isect) @@ -1383,13 +1540,15 @@ class OleFileIO: self.fat = self.fat + nextfat return isect + def loadfat(self, header): """ Load the FAT table. """ - # The header contains a sector numbers - # for the first 109 FAT sectors. Additional sectors are - # described by DIF blocks + # The 1st sector of the file contains sector numbers for the first 109 + # FAT sectors, right after the header which is 76 bytes long. + # (always 109, whatever the sector size: 512 bytes = 76+4*109) + # Additional sectors are described by DIF blocks sect = header[76:512] debug( "len(sect)=%d, so %d integers" % (len(sect), len(sect)//4) ) @@ -1418,24 +1577,27 @@ class OleFileIO: if self.sectDifStart >= self.nb_sect: # initial DIFAT block index must be valid self._raise_defect(DEFECT_FATAL, 'incorrect DIFAT, first index out of range') - debug("DIFAT analysis...") + debug( "DIFAT analysis..." ) # We compute the necessary number of DIFAT sectors : - # (each DIFAT sector = 127 pointers + 1 towards next DIFAT sector) - nb_difat = (self.csectFat-109 + 126)//127 - debug("nb_difat = %d" % nb_difat) + # Number of pointers per DIFAT sector = (sectorsize/4)-1 + # (-1 because the last pointer is the next DIFAT sector number) + nb_difat_sectors = (self.sectorsize//4)-1 + # (if 512 bytes: each DIFAT sector = 127 pointers + 1 towards next DIFAT sector) + nb_difat = (self.csectFat-109 + nb_difat_sectors-1)//nb_difat_sectors + debug( "nb_difat = %d" % nb_difat ) if self.csectDif != nb_difat: raise IOError('incorrect DIFAT') isect_difat = self.sectDifStart for i in iterrange(nb_difat): - debug("DIFAT block %d, sector %X" % (i, isect_difat)) + debug( "DIFAT block %d, sector %X" % (i, isect_difat) ) #TODO: check if corresponding FAT SID = DIFSECT sector_difat = self.getsect(isect_difat) difat = self.sect2array(sector_difat) self.dumpsect(sector_difat) - self.loadfat_sect(difat[:127]) + self.loadfat_sect(difat[:nb_difat_sectors]) # last DIFAT pointer is next DIFAT sector: - isect_difat = difat[127] - debug("next DIFAT sector: %X" % isect_difat) + isect_difat = difat[nb_difat_sectors] + debug( "next DIFAT sector: %X" % isect_difat ) # checks: if isect_difat not in [ENDOFCHAIN, FREESECT]: # last DIFAT pointer value must be ENDOFCHAIN or FREESECT @@ -1453,6 +1615,7 @@ class OleFileIO: debug('\nFAT:') self.dumpfat(self.fat) + def loadminifat(self): """ Load the MiniFAT table. @@ -1491,10 +1654,16 @@ class OleFileIO: """ Read given sector from file on disk. - :param sect: sector index + :param sect: int, sector index :returns: a string containing the sector data. """ - # [PL] this original code was wrong when sectors are 4KB instead of + # From [MS-CFB]: A sector number can be converted into a byte offset + # into the file by using the following formula: + # (sector number + 1) x Sector Size. + # This implies that sector #0 of the file begins at byte offset Sector + # Size, not at 0. + + # [PL] the original code in PIL was wrong when sectors are 4KB instead of # 512 bytes: #self.fp.seek(512 + self.sectorsize * sect) #[PL]: added safety checks: @@ -1512,6 +1681,34 @@ class OleFileIO: self._raise_defect(DEFECT_FATAL, 'incomplete OLE sector') return sector + + def write_sect(self, sect, data, padding=b'\x00'): + """ + Write given sector to file on disk. + + :param sect: int, sector index + :param data: bytes, sector data + :param padding: single byte, padding character if data < sector size + """ + if not isinstance(data, bytes): + raise TypeError("write_sect: data must be a bytes string") + if not isinstance(padding, bytes) or len(padding)!=1: + raise TypeError("write_sect: padding must be a bytes string of 1 char") + #TODO: we could allow padding=None for no padding at all + try: + self.fp.seek(self.sectorsize * (sect+1)) + except: + debug('write_sect(): sect=%X, seek=%d, filesize=%d' % + (sect, self.sectorsize*(sect+1), self._filesize)) + self._raise_defect(DEFECT_FATAL, 'OLE sector index out of range') + if len(data) < self.sectorsize: + # add padding + data += padding * (self.sectorsize - len(data)) + elif len(data) < self.sectorsize: + raise ValueError("Data is larger than sector size") + self.fp.write(data) + + def loaddirectory(self, sect): """ Load the directory. @@ -1541,12 +1738,13 @@ class OleFileIO: ## break ## self.direntries.append(_OleDirectoryEntry(entry, sid, self)) # load root entry: - self._load_direntry(0) + root_entry = self._load_direntry(0) # Root entry is the first entry: self.root = self.direntries[0] # read and build all storage trees, starting from the root: self.root.build_storage_tree() + def _load_direntry (self, sid): """ Load a directory entry from the directory. @@ -1555,6 +1753,7 @@ class OleFileIO: :param sid: index of storage/stream in the directory. :returns: a _OleDirectoryEntry object + :exception IOError: if the entry has always been referenced. """ # check if SID is OK: @@ -1571,12 +1770,14 @@ class OleFileIO: self.direntries[sid] = _OleDirectoryEntry(entry, sid, self) return self.direntries[sid] + def dumpdirectory(self): """ Dump directory (for debugging only) """ self.root.dump() + def _open(self, start, size = 0x7FFFFFFF, force_FAT=False): """ Open a stream, either in FAT or MiniFAT according to its size. @@ -1585,7 +1786,7 @@ class OleFileIO: :param start: index of first sector :param size: size of stream (or nothing if size is unknown) :param force_FAT: if False (default), stream will be opened in FAT or MiniFAT - according to size. If True, it will always be opened in FAT. + according to size. If True, it will always be opened in FAT. """ debug('OleFileIO.open(): sect=%d, size=%d, force_FAT=%s' % (start, size, str(force_FAT))) @@ -1602,51 +1803,60 @@ class OleFileIO: (self.root.isectStart, size_ministream)) self.ministream = self._open(self.root.isectStart, size_ministream, force_FAT=True) - return _OleStream(self.ministream, start, size, 0, - self.minisectorsize, self.minifat, - self.ministream.size) + return _OleStream(fp=self.ministream, sect=start, size=size, + offset=0, sectorsize=self.minisectorsize, + fat=self.minifat, filesize=self.ministream.size) else: # standard stream - return _OleStream(self.fp, start, size, 512, - self.sectorsize, self.fat, self._filesize) + return _OleStream(fp=self.fp, sect=start, size=size, + offset=self.sectorsize, + sectorsize=self.sectorsize, fat=self.fat, + filesize=self._filesize) + def _list(self, files, prefix, node, streams=True, storages=False): """ - (listdir helper) + listdir helper + :param files: list of files to fill in :param prefix: current location in storage tree (list of names) :param node: current node (_OleDirectoryEntry object) :param streams: bool, include streams if True (True by default) - new in v0.26 :param storages: bool, include storages if True (False by default) - new in v0.26 - (note: the root storage is never included) + (note: the root storage is never included) """ prefix = prefix + [node.name] for entry in node.kids: - if entry.kids: + if entry.entry_type == STGTY_STORAGE: # this is a storage if storages: # add it to the list files.append(prefix[1:] + [entry.name]) # check its kids self._list(files, prefix, entry, streams, storages) - else: + elif entry.entry_type == STGTY_STREAM: # this is a stream if streams: # add it to the list files.append(prefix[1:] + [entry.name]) + else: + self._raise_defect(DEFECT_INCORRECT, 'The directory tree contains an entry which is not a stream nor a storage.') + def listdir(self, streams=True, storages=False): """ - Return a list of streams stored in this file + Return a list of streams and/or storages stored in this file :param streams: bool, include streams if True (True by default) - new in v0.26 :param storages: bool, include storages if True (False by default) - new in v0.26 (note: the root storage is never included) + :returns: list of stream and/or storage paths """ files = [] self._list(files, [], self.root, streams, storages) return files + def _find(self, filename): """ Returns directory entry of given filename. (openstream helper) @@ -1656,10 +1866,11 @@ class OleFileIO: - a string using Unix path syntax, for example: 'storage_1/storage_1.2/stream' - - a list of storage filenames, path to the desired stream/storage. + - or a list of storage filenames, path to the desired stream/storage. Example: ['storage_1', 'storage_1.2', 'stream'] + :returns: sid of requested filename - raise IOError if file not found + :exception IOError: if file not found """ # if filename is a string instead of a list, split it on slashes to @@ -1677,15 +1888,17 @@ class OleFileIO: node = kid return node.sid + def openstream(self, filename): """ Open a stream as a read-only file object (BytesIO). + Note: filename is case-insensitive. :param filename: path of stream in storage tree (except root entry), either: - a string using Unix path syntax, for example: 'storage_1/storage_1.2/stream' - - a list of storage filenames, path to the desired stream/storage. + - or a list of storage filenames, path to the desired stream/storage. Example: ['storage_1', 'storage_1.2', 'stream'] :returns: file object (read-only) @@ -1697,6 +1910,68 @@ class OleFileIO: raise IOError("this file is not a stream") return self._open(entry.isectStart, entry.size) + + def write_stream(self, stream_name, data): + """ + Write a stream to disk. For now, it is only possible to replace an + existing stream by data of the same size. + + :param stream_name: path of stream in storage tree (except root entry), either: + + - a string using Unix path syntax, for example: + 'storage_1/storage_1.2/stream' + - or a list of storage filenames, path to the desired stream/storage. + Example: ['storage_1', 'storage_1.2', 'stream'] + + :param data: bytes, data to be written, must be the same size as the original + stream. + """ + if not isinstance(data, bytes): + raise TypeError("write_stream: data must be a bytes string") + sid = self._find(stream_name) + entry = self.direntries[sid] + if entry.entry_type != STGTY_STREAM: + raise IOError("this is not a stream") + size = entry.size + if size != len(data): + raise ValueError("write_stream: data must be the same size as the existing stream") + if size < self.minisectorcutoff: + raise NotImplementedError("Writing a stream in MiniFAT is not implemented yet") + sect = entry.isectStart + # number of sectors to write + nb_sectors = (size + (self.sectorsize-1)) // self.sectorsize + debug('nb_sectors = %d' % nb_sectors) + for i in range(nb_sectors): +## try: +## self.fp.seek(offset + self.sectorsize * sect) +## except: +## debug('sect=%d, seek=%d' % +## (sect, offset+self.sectorsize*sect)) +## raise IOError('OLE sector index out of range') + # extract one sector from data, the last one being smaller: + if i<(nb_sectors-1): + data_sector = data [i*self.sectorsize : (i+1)*self.sectorsize] + #TODO: comment this if it works + assert(len(data_sector)==self.sectorsize) + else: + data_sector = data [i*self.sectorsize:] + #TODO: comment this if it works + debug('write_stream: size=%d sectorsize=%d data_sector=%d size%%sectorsize=%d' + % (size, self.sectorsize, len(data_sector), size % self.sectorsize)) + assert(len(data_sector) % self.sectorsize==size % self.sectorsize) + self.write_sect(sect, data_sector) +## self.fp.write(data_sector) + # jump to next sector in the FAT: + try: + sect = self.fat[sect] + except IndexError: + # [PL] if pointer is out of the FAT an exception is raised + raise IOError('incorrect OLE FAT, sector index out of range') + #[PL] Last sector should be a "end of chain" marker: + if sect != ENDOFCHAIN: + raise IOError('incorrect last sector index in OLE stream') + + def get_type(self, filename): """ Test if given filename exists as a stream or a storage in the OLE @@ -1716,6 +1991,7 @@ class OleFileIO: except: return False + def getmtime(self, filename): """ Return modification time of a stream/storage. @@ -1731,6 +2007,7 @@ class OleFileIO: entry = self.direntries[sid] return entry.getmtime() + def getctime(self, filename): """ Return creation time of a stream/storage. @@ -1746,20 +2023,23 @@ class OleFileIO: entry = self.direntries[sid] return entry.getctime() + def exists(self, filename): """ Test if given filename exists as a stream or a storage in the OLE container. + Note: filename is case-insensitive. :param filename: path of stream in storage tree. (see openstream for syntax) :returns: True if object exist, else False. """ try: - self._find(filename) + sid = self._find(filename) return True except: return False + def get_size(self, filename): """ Return size of a stream in the OLE container, in bytes. @@ -1767,7 +2047,7 @@ class OleFileIO: :param filename: path of stream in storage tree (see openstream for syntax) :returns: size in bytes (long integer) :exception IOError: if file not found - :exception TypeError: if this is not a stream + :exception TypeError: if this is not a stream. """ sid = self._find(filename) entry = self.direntries[sid] @@ -1776,6 +2056,7 @@ class OleFileIO: raise TypeError('object is not an OLE stream') return entry.size + def get_rootentry_name(self): """ Return root entry name. Should usually be 'Root Entry' or 'R' in most @@ -1783,6 +2064,7 @@ class OleFileIO: """ return self.root.name + def getproperties(self, filename, convert_time=False, no_conversion=None): """ Return properties described in substream. @@ -1791,10 +2073,12 @@ class OleFileIO: :param convert_time: bool, if True timestamps will be converted to Python datetime :param no_conversion: None or list of int, timestamps not to be converted (for example total editing time is not a real timestamp) + :returns: a dictionary of values indexed by id (integer) """ + #REFERENCE: [MS-OLEPS] https://msdn.microsoft.com/en-us/library/dd942421.aspx # make sure no_conversion is a list, just to simplify code below: - if no_conversion is None: + if no_conversion == None: no_conversion = [] # stream path as a string to report exceptions: streampath = filename @@ -1808,11 +2092,11 @@ class OleFileIO: try: # header s = fp.read(28) - # clsid = _clsid(s[8:24]) + clsid = _clsid(s[8:24]) # format id s = fp.read(20) - # fmtid = _clsid(s[:16]) + fmtid = _clsid(s[:16]) fp.seek(i32(s, 16)) # get section @@ -1830,34 +2114,34 @@ class OleFileIO: for i in range(num_props): try: - id = 0 # just in case of an exception + id = 0 # just in case of an exception id = i32(s, 8+i*8) offset = i32(s, 12+i*8) type = i32(s, offset) - debug('property id=%d: type=%d offset=%X' % (id, type, offset)) + debug ('property id=%d: type=%d offset=%X' % (id, type, offset)) # test for common types first (should perhaps use # a dictionary instead?) - if type == VT_I2: # 16-bit signed integer + if type == VT_I2: # 16-bit signed integer value = i16(s, offset+4) if value >= 32768: value = value - 65536 - elif type == VT_UI2: # 2-byte unsigned integer + elif type == VT_UI2: # 2-byte unsigned integer value = i16(s, offset+4) elif type in (VT_I4, VT_INT, VT_ERROR): # VT_I4: 32-bit signed integer # VT_ERROR: HRESULT, similar to 32-bit signed integer, # see http://msdn.microsoft.com/en-us/library/cc230330.aspx value = i32(s, offset+4) - elif type in (VT_UI4, VT_UINT): # 4-byte unsigned integer - value = i32(s, offset+4) # FIXME + elif type in (VT_UI4, VT_UINT): # 4-byte unsigned integer + value = i32(s, offset+4) # FIXME elif type in (VT_BSTR, VT_LPSTR): # CodePageString, see http://msdn.microsoft.com/en-us/library/dd942354.aspx # size is a 32 bits integer, including the null terminator, and # possibly trailing or embedded null chars - # TODO: if codepage is unicode, the string should be converted as such + #TODO: if codepage is unicode, the string should be converted as such count = i32(s, offset+4) value = s[offset+8:offset+8+count-1] # remove all null chars: @@ -1873,9 +2157,9 @@ class OleFileIO: # "the string should NOT contain embedded or additional trailing # null characters." count = i32(s, offset+4) - value = _unicode(s[offset+8:offset+8+count*2]) + value = self._decode_utf16_str(s[offset+8:offset+8+count*2]) elif type == VT_FILETIME: - value = long(i32(s, offset+4)) + (long(i32(s, offset+8)) << 32) + value = long(i32(s, offset+4)) + (long(i32(s, offset+8))<<32) # FILETIME is a 64-bit int: "number of 100ns periods # since Jan 1,1601". if convert_time and id not in no_conversion: @@ -1889,8 +2173,8 @@ class OleFileIO: else: # legacy code kept for backward compatibility: returns a # number of seconds since Jan 1,1601 - value = value // 10000000 # seconds - elif type == VT_UI1: # 1-byte unsigned integer + value = value // 10000000 # seconds + elif type == VT_UI1: # 1-byte unsigned integer value = i8(s[offset+4]) elif type == VT_CLSID: value = _clsid(s[offset+4:offset+20]) @@ -1904,8 +2188,8 @@ class OleFileIO: # see http://msdn.microsoft.com/en-us/library/cc237864.aspx value = bool(i16(s, offset+4)) else: - value = None # everything else yields "None" - debug('property id=%d: type=%d not implemented in parser yet' % (id, type)) + value = None # everything else yields "None" + debug ('property id=%d: type=%d not implemented in parser yet' % (id, type)) # missing: VT_EMPTY, VT_NULL, VT_R4, VT_R8, VT_CY, VT_DATE, # VT_DECIMAL, VT_I1, VT_I8, VT_UI8, @@ -1917,8 +2201,8 @@ class OleFileIO: # type of items, e.g. VT_VECTOR|VT_BSTR # see http://msdn.microsoft.com/en-us/library/dd942011.aspx - # print("%08x" % id, repr(value), end=" ") - # print("(%s)" % VT[i32(s, offset) & 0xFFF]) + #print("%08x" % id, repr(value), end=" ") + #print("(%s)" % VT[i32(s, offset) & 0xFFF]) data[id] = value except BaseException as exc: @@ -1949,105 +2233,112 @@ class OleFileIO: if __name__ == "__main__": + import sys + # [PL] display quick usage info if launched from command-line if len(sys.argv) <= 1: - print(__doc__) - print(""" -Launched from command line, this script parses OLE files and prints info. + print('olefile version %s %s - %s' % (__version__, __date__, __author__)) + print( +""" +Launched from the command line, this script parses OLE files and prints info. -Usage: OleFileIO_PL.py [-d] [-c] [file2 ...] +Usage: olefile.py [-d] [-c] [file2 ...] Options: --d : debug mode (display a lot of debug information, for developers only) +-d : debug mode (displays a lot of debug information, for developers only) -c : check all streams (for debugging purposes) + +For more information, see http://www.decalage.info/olefile """) sys.exit() check_streams = False for filename in sys.argv[1:]: - # try: - # OPTIONS: - if filename == '-d': - # option to switch debug mode on: - set_debug_mode(True) - continue - if filename == '-c': - # option to switch check streams mode on: - check_streams = True - continue +## try: + # OPTIONS: + if filename == '-d': + # option to switch debug mode on: + set_debug_mode(True) + continue + if filename == '-c': + # option to switch check streams mode on: + check_streams = True + continue - ole = OleFileIO(filename) #, raise_defects=DEFECT_INCORRECT) - print("-" * 68) - print(filename) - print("-" * 68) - ole.dumpdirectory() - for streamname in ole.listdir(): - if streamname[-1][0] == "\005": - print(streamname, ": properties") - props = ole.getproperties(streamname, convert_time=True) - props = sorted(props.items()) - for k, v in props: - #[PL]: avoid to display too large or binary values: - if isinstance(v, (basestring, bytes)): - if len(v) > 50: - v = v[:50] - if isinstance(v, bytes): - # quick and dirty binary check: - for c in (1, 2, 3, 4, 5, 6, 7, 11, 12, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31): - if c in bytearray(v): - v = '(binary data)' - break - print(" ", k, v) - - if check_streams: - # Read all streams to check if there are errors: - print('\nChecking streams...') + ole = OleFileIO(filename)#, raise_defects=DEFECT_INCORRECT) + print("-" * 68) + print(filename) + print("-" * 68) + ole.dumpdirectory() for streamname in ole.listdir(): - # print name using repr() to convert binary chars to \xNN: - print('-', repr('/'.join(streamname)), '-', end=' ') - st_type = ole.get_type(streamname) - if st_type == STGTY_STREAM: - print('size %d' % ole.get_size(streamname)) - # just try to read stream in memory: - ole.openstream(streamname) - else: - print('NOT a stream : type=%d' % st_type) + if streamname[-1][0] == "\005": + print(streamname, ": properties") + props = ole.getproperties(streamname, convert_time=True) + props = sorted(props.items()) + for k, v in props: + #[PL]: avoid to display too large or binary values: + if isinstance(v, (basestring, bytes)): + if len(v) > 50: + v = v[:50] + if isinstance(v, bytes): + # quick and dirty binary check: + for c in (1,2,3,4,5,6,7,11,12,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30,31): + if c in bytearray(v): + v = '(binary data)' + break + print(" ", k, v) + + if check_streams: + # Read all streams to check if there are errors: + print('\nChecking streams...') + for streamname in ole.listdir(): + # print name using repr() to convert binary chars to \xNN: + print('-', repr('/'.join(streamname)),'-', end=' ') + st_type = ole.get_type(streamname) + if st_type == STGTY_STREAM: + print('size %d' % ole.get_size(streamname)) + # just try to read stream in memory: + ole.openstream(streamname) + else: + print('NOT a stream : type=%d' % st_type) + print() + +## for streamname in ole.listdir(): +## # print name using repr() to convert binary chars to \xNN: +## print('-', repr('/'.join(streamname)),'-', end=' ') +## print(ole.getmtime(streamname)) +## print() + + print('Modification/Creation times of all directory entries:') + for entry in ole.direntries: + if entry is not None: + print('- %s: mtime=%s ctime=%s' % (entry.name, + entry.getmtime(), entry.getctime())) print() -## for streamname in ole.listdir(): -## # print name using repr() to convert binary chars to \xNN: -## print('-', repr('/'.join(streamname)),'-', end=' ') -## print(ole.getmtime(streamname)) -## print() + # parse and display metadata: + meta = ole.get_metadata() + meta.dump() + print() + #[PL] Test a few new methods: + root = ole.get_rootentry_name() + print('Root entry name: "%s"' % root) + if ole.exists('worddocument'): + print("This is a Word document.") + print("type of stream 'WordDocument':", ole.get_type('worddocument')) + print("size :", ole.get_size('worddocument')) + if ole.exists('macros/vba'): + print("This document may contain VBA macros.") - print('Modification/Creation times of all directory entries:') - for entry in ole.direntries: - if entry is not None: - print('- %s: mtime=%s ctime=%s' % (entry.name, - entry.getmtime(), entry.getctime())) - print() - - # parse and display metadata: - meta = ole.get_metadata() - meta.dump() - print() - # [PL] Test a few new methods: - root = ole.get_rootentry_name() - print('Root entry name: "%s"' % root) - if ole.exists('worddocument'): - print("This is a Word document.") - print("type of stream 'WordDocument':", ole.get_type('worddocument')) - print("size :", ole.get_size('worddocument')) - if ole.exists('macros/vba'): - print("This document may contain VBA macros.") - - # print parsing issues: - print('\nNon-fatal issues raised during parsing:') - if ole.parsing_issues: - for exctype, msg in ole.parsing_issues: - print('- %s: %s' % (exctype.__name__, msg)) - else: - print('None') + # print parsing issues: + print('\nNon-fatal issues raised during parsing:') + if ole.parsing_issues: + for exctype, msg in ole.parsing_issues: + print('- %s: %s' % (exctype.__name__, msg)) + else: + print('None') ## except IOError as v: ## print("***", "cannot read", file, "-", v) + +# this code was developed while listening to The Wedding Present "Sea Monsters" \ No newline at end of file From 93461e6faa9a5a2676101394bd2fae68040f6b53 Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Wed, 13 May 2015 03:05:45 -0700 Subject: [PATCH 07/42] Ico files are little endian, ref #1204 --- PIL/IcoImagePlugin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PIL/IcoImagePlugin.py b/PIL/IcoImagePlugin.py index c4e24d99c..03f3aef43 100644 --- a/PIL/IcoImagePlugin.py +++ b/PIL/IcoImagePlugin.py @@ -48,7 +48,7 @@ def _save(im, fp, filename): width, height = im.size filter(lambda x: False if (x[0] > width or x[1] > height or x[0] > 255 or x[1] > 255) else True, sizes) - fp.write(struct.pack("H", len(sizes))) # idCount(2) + fp.write(struct.pack(" Date: Thu, 28 May 2015 17:20:33 +1000 Subject: [PATCH 08/42] Fixed typo in ImageEnhance.py --- PIL/ImageEnhance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/ImageEnhance.py b/PIL/ImageEnhance.py index fbacbee8f..56b5c0199 100644 --- a/PIL/ImageEnhance.py +++ b/PIL/ImageEnhance.py @@ -73,7 +73,7 @@ class Contrast(_Enhance): class Brightness(_Enhance): """Adjust image brightness. - This class can be used to control the brighntess of an image. An + This class can be used to control the brightness of an image. An enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the original image. """ From b213f63c591df7e6512b0b92b54c589a2a0141a9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 10 May 2015 22:36:23 +1000 Subject: [PATCH 09/42] Removed pre-Python 2.3 workaround --- PIL/OleFileIO.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index d787e59ed..df95e2d83 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -302,17 +302,10 @@ else: #[PL] These workarounds were inspired from the Path module # (see http://www.jorendorff.com/articles/python/path/) -#TODO: test with old Python versions - -# Pre-2.3 workaround for basestring. try: basestring except NameError: - try: - # is Unicode supported (Python >2.0 or >1.6 ?) - basestring = (str, unicode) - except NameError: - basestring = str + basestring = str #[PL] Experimental setting: if True, OLE filenames will be kept in Unicode # if False (default PIL behaviour), all filenames are converted to Latin-1. From f55f2d13cb3023426b5d24dd5da506a40b476bd2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 May 2015 00:00:36 +1000 Subject: [PATCH 10/42] Various health fixes --- PIL/OleFileIO.py | 196 +++++++++++++++++++++++------------------------ 1 file changed, 97 insertions(+), 99 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index df95e2d83..98c126761 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -399,12 +399,12 @@ WORD_CLSID = "00020900-0000-0000-C000-000000000046" #TODO: check Excel, PPT, ... #[PL]: Defect levels to classify parsing errors - see OleFileIO._raise_defect() -DEFECT_UNSURE = 10 # a case which looks weird, but not sure it's a defect -DEFECT_POTENTIAL = 20 # a potential defect -DEFECT_INCORRECT = 30 # an error according to specifications, but parsing - # can go on -DEFECT_FATAL = 40 # an error which cannot be ignored, parsing is - # impossible +DEFECT_UNSURE = 10 # a case which looks weird, but not sure it's a defect +DEFECT_POTENTIAL = 20 # a potential defect +DEFECT_INCORRECT = 30 # an error according to specifications, but parsing + # can go on +DEFECT_FATAL = 40 # an error which cannot be ignored, parsing is + # impossible # Minimal size of an empty OLE file, with 512-bytes sectors = 1536 bytes # (this is used in isOleFile and OleFile.open) @@ -504,20 +504,20 @@ def _clsid(clsid): def filetime2datetime(filetime): - """ - convert FILETIME (64 bits int) to Python datetime.datetime - """ - # TODO: manage exception when microseconds is too large - # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ - _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) - #debug('timedelta days=%d' % (filetime//(10*1000000*3600*24))) - return _FILETIME_null_date + datetime.timedelta(microseconds=filetime//10) + """ + convert FILETIME (64 bits int) to Python datetime.datetime + """ + # TODO: manage exception when microseconds is too large + # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ + _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) + #debug('timedelta days=%d' % (filetime//(10*1000000*3600*24))) + return _FILETIME_null_date + datetime.timedelta(microseconds=filetime//10) #=== CLASSES ================================================================== -class OleMetadata: +class OleMetadata(object): """ class to parse and store metadata from standard properties of OLE files. @@ -803,7 +803,7 @@ class _OleStream(io.BytesIO): #--- _OleDirectoryEntry ------------------------------------------------------- -class _OleDirectoryEntry: +class _OleDirectoryEntry(object): """ OLE2 Directory Entry @@ -1064,7 +1064,7 @@ class _OleDirectoryEntry: #--- OleFileIO ---------------------------------------------------------------- -class OleFileIO: +class OleFileIO(object): """ OLE container object @@ -1935,12 +1935,12 @@ class OleFileIO: nb_sectors = (size + (self.sectorsize-1)) // self.sectorsize debug('nb_sectors = %d' % nb_sectors) for i in range(nb_sectors): -## try: -## self.fp.seek(offset + self.sectorsize * sect) -## except: -## debug('sect=%d, seek=%d' % -## (sect, offset+self.sectorsize*sect)) -## raise IOError('OLE sector index out of range') + # try: + # self.fp.seek(offset + self.sectorsize * sect) + # except: + # debug('sect=%d, seek=%d' % + # (sect, offset+self.sectorsize*sect)) + # raise IOError('OLE sector index out of range') # extract one sector from data, the last one being smaller: if i<(nb_sectors-1): data_sector = data [i*self.sectorsize : (i+1)*self.sectorsize] @@ -2071,7 +2071,7 @@ class OleFileIO: """ #REFERENCE: [MS-OLEPS] https://msdn.microsoft.com/en-us/library/dd942421.aspx # make sure no_conversion is a list, just to simplify code below: - if no_conversion == None: + if no_conversion is None: no_conversion = [] # stream path as a string to report exceptions: streampath = filename @@ -2226,8 +2226,6 @@ class OleFileIO: if __name__ == "__main__": - import sys - # [PL] display quick usage info if launched from command-line if len(sys.argv) <= 1: print('olefile version %s %s - %s' % (__version__, __date__, __author__)) @@ -2247,55 +2245,55 @@ For more information, see http://www.decalage.info/olefile check_streams = False for filename in sys.argv[1:]: -## try: - # OPTIONS: - if filename == '-d': - # option to switch debug mode on: - set_debug_mode(True) - continue - if filename == '-c': - # option to switch check streams mode on: - check_streams = True - continue + # try: + # OPTIONS: + if filename == '-d': + # option to switch debug mode on: + set_debug_mode(True) + continue + if filename == '-c': + # option to switch check streams mode on: + check_streams = True + continue - ole = OleFileIO(filename)#, raise_defects=DEFECT_INCORRECT) - print("-" * 68) - print(filename) - print("-" * 68) - ole.dumpdirectory() + ole = OleFileIO(filename)#, raise_defects=DEFECT_INCORRECT) + print("-" * 68) + print(filename) + print("-" * 68) + ole.dumpdirectory() + for streamname in ole.listdir(): + if streamname[-1][0] == "\005": + print(streamname, ": properties") + props = ole.getproperties(streamname, convert_time=True) + props = sorted(props.items()) + for k, v in props: + #[PL]: avoid to display too large or binary values: + if isinstance(v, (basestring, bytes)): + if len(v) > 50: + v = v[:50] + if isinstance(v, bytes): + # quick and dirty binary check: + for c in (1,2,3,4,5,6,7,11,12,14,15,16,17,18,19,20, + 21,22,23,24,25,26,27,28,29,30,31): + if c in bytearray(v): + v = '(binary data)' + break + print(" ", k, v) + + if check_streams: + # Read all streams to check if there are errors: + print('\nChecking streams...') for streamname in ole.listdir(): - if streamname[-1][0] == "\005": - print(streamname, ": properties") - props = ole.getproperties(streamname, convert_time=True) - props = sorted(props.items()) - for k, v in props: - #[PL]: avoid to display too large or binary values: - if isinstance(v, (basestring, bytes)): - if len(v) > 50: - v = v[:50] - if isinstance(v, bytes): - # quick and dirty binary check: - for c in (1,2,3,4,5,6,7,11,12,14,15,16,17,18,19,20, - 21,22,23,24,25,26,27,28,29,30,31): - if c in bytearray(v): - v = '(binary data)' - break - print(" ", k, v) - - if check_streams: - # Read all streams to check if there are errors: - print('\nChecking streams...') - for streamname in ole.listdir(): - # print name using repr() to convert binary chars to \xNN: - print('-', repr('/'.join(streamname)),'-', end=' ') - st_type = ole.get_type(streamname) - if st_type == STGTY_STREAM: - print('size %d' % ole.get_size(streamname)) - # just try to read stream in memory: - ole.openstream(streamname) - else: - print('NOT a stream : type=%d' % st_type) - print() + # print name using repr() to convert binary chars to \xNN: + print('-', repr('/'.join(streamname)),'-', end=' ') + st_type = ole.get_type(streamname) + if st_type == STGTY_STREAM: + print('size %d' % ole.get_size(streamname)) + # just try to read stream in memory: + ole.openstream(streamname) + else: + print('NOT a stream : type=%d' % st_type) + print() ## for streamname in ole.listdir(): ## # print name using repr() to convert binary chars to \xNN: @@ -2303,34 +2301,34 @@ For more information, see http://www.decalage.info/olefile ## print(ole.getmtime(streamname)) ## print() - print('Modification/Creation times of all directory entries:') - for entry in ole.direntries: - if entry is not None: - print('- %s: mtime=%s ctime=%s' % (entry.name, - entry.getmtime(), entry.getctime())) - print() + print('Modification/Creation times of all directory entries:') + for entry in ole.direntries: + if entry is not None: + print('- %s: mtime=%s ctime=%s' % (entry.name, + entry.getmtime(), entry.getctime())) + print() - # parse and display metadata: - meta = ole.get_metadata() - meta.dump() - print() - #[PL] Test a few new methods: - root = ole.get_rootentry_name() - print('Root entry name: "%s"' % root) - if ole.exists('worddocument'): - print("This is a Word document.") - print("type of stream 'WordDocument':", ole.get_type('worddocument')) - print("size :", ole.get_size('worddocument')) - if ole.exists('macros/vba'): - print("This document may contain VBA macros.") + # parse and display metadata: + meta = ole.get_metadata() + meta.dump() + print() + #[PL] Test a few new methods: + root = ole.get_rootentry_name() + print('Root entry name: "%s"' % root) + if ole.exists('worddocument'): + print("This is a Word document.") + print("type of stream 'WordDocument':", ole.get_type('worddocument')) + print("size :", ole.get_size('worddocument')) + if ole.exists('macros/vba'): + print("This document may contain VBA macros.") - # print parsing issues: - print('\nNon-fatal issues raised during parsing:') - if ole.parsing_issues: - for exctype, msg in ole.parsing_issues: - print('- %s: %s' % (exctype.__name__, msg)) - else: - print('None') + # print parsing issues: + print('\nNon-fatal issues raised during parsing:') + if ole.parsing_issues: + for exctype, msg in ole.parsing_issues: + print('- %s: %s' % (exctype.__name__, msg)) + else: + print('None') ## except IOError as v: ## print("***", "cannot read", file, "-", v) From 1208fe89cd9d04187f1a4ebef2167e577e4eebdd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 10 May 2015 23:50:06 +1000 Subject: [PATCH 11/42] Renamed _raise_defect to avoid protected member warnings --- PIL/OleFileIO.py | 68 ++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index 98c126761..7449407b9 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -874,17 +874,17 @@ class _OleDirectoryEntry(object): sizeHigh ) = struct.unpack(_OleDirectoryEntry.STRUCT_DIRENTRY, entry) if self.entry_type not in [STGTY_ROOT, STGTY_STORAGE, STGTY_STREAM, STGTY_EMPTY]: - olefile._raise_defect(DEFECT_INCORRECT, 'unhandled OLE storage type') + olefile.raise_defect(DEFECT_INCORRECT, 'unhandled OLE storage type') # only first directory entry can (and should) be root: if self.entry_type == STGTY_ROOT and sid != 0: - olefile._raise_defect(DEFECT_INCORRECT, 'duplicate OLE root entry') + olefile.raise_defect(DEFECT_INCORRECT, 'duplicate OLE root entry') if sid == 0 and self.entry_type != STGTY_ROOT: - olefile._raise_defect(DEFECT_INCORRECT, 'incorrect OLE root entry') + olefile.raise_defect(DEFECT_INCORRECT, 'incorrect OLE root entry') #debug (struct.unpack(fmt_entry, entry[:len_entry])) # name should be at most 31 unicode characters + null character, # so 64 bytes in total (31*2 + 2): if namelength>64: - olefile._raise_defect(DEFECT_INCORRECT, 'incorrect DirEntry name length') + olefile.raise_defect(DEFECT_INCORRECT, 'incorrect DirEntry name length') # if exception not raised, namelength is set to the maximum value: namelength = 64 # only characters without ending null char are kept: @@ -908,7 +908,7 @@ class _OleDirectoryEntry(object): if sizeHigh != 0 and sizeHigh != 0xFFFFFFFF: debug('sectorsize=%d, sizeLow=%d, sizeHigh=%d (%X)' % (olefile.sectorsize, sizeLow, sizeHigh, sizeHigh)) - olefile._raise_defect(DEFECT_UNSURE, 'incorrect OLE stream size') + olefile.raise_defect(DEFECT_UNSURE, 'incorrect OLE stream size') self.size = sizeLow else: self.size = sizeLow + (long(sizeHigh)<<32) @@ -918,7 +918,7 @@ class _OleDirectoryEntry(object): # a storage should have a null size, BUT some implementations such as # Word 8 for Mac seem to allow non-null values => Potential defect: if self.entry_type == STGTY_STORAGE and self.size != 0: - olefile._raise_defect(DEFECT_POTENTIAL, 'OLE storage with size>0') + olefile.raise_defect(DEFECT_POTENTIAL, 'OLE storage with size>0') # check if stream is not already referenced elsewhere: if self.entry_type in (STGTY_ROOT, STGTY_STREAM) and self.size>0: if self.size < olefile.minisectorcutoff \ @@ -970,7 +970,7 @@ class _OleDirectoryEntry(object): return # check if child SID is in the proper range: if child_sid<0 or child_sid>=len(self.olefile.direntries): - self.olefile._raise_defect(DEFECT_FATAL, 'OLE DirEntry index out of range') + self.olefile.raise_defect(DEFECT_FATAL, 'OLE DirEntry index out of range') # get child direntry: child = self.olefile._load_direntry(child_sid) #direntries[child_sid] debug('append_kids: child_sid=%d - %s - sid_left=%d, sid_right=%d, sid_child=%d' @@ -982,7 +982,7 @@ class _OleDirectoryEntry(object): # Check if its name is not already used (case-insensitive): name_lower = child.name.lower() if name_lower in self.kids_dict: - self.olefile._raise_defect(DEFECT_INCORRECT, + self.olefile.raise_defect(DEFECT_INCORRECT, "Duplicate filename in OLE storage") # Then the child_sid _OleDirectoryEntry object is appended to the # kids list and dictionary: @@ -990,7 +990,7 @@ class _OleDirectoryEntry(object): self.kids_dict[name_lower] = child # Check if kid was not already referenced in a storage: if child.used: - self.olefile._raise_defect(DEFECT_INCORRECT, + self.olefile.raise_defect(DEFECT_INCORRECT, 'OLE Entry referenced more than once') child.used = True # Finally walk through right side of the tree: @@ -1135,7 +1135,7 @@ class OleFileIO(object): self.open(filename, write_mode=write_mode) - def _raise_defect(self, defect_level, message, exception_type=IOError): + def raise_defect(self, defect_level, message, exception_type=IOError): """ This method should be called for any defect found during file parsing. It may raise an IOError exception according to the minimal level chosen @@ -1237,7 +1237,7 @@ class OleFileIO(object): header = self.fp.read(512) if len(header) != 512 or header[:8] != MAGIC: - self._raise_defect(DEFECT_FATAL, "not an OLE2 structured storage file") + self.raise_defect(DEFECT_FATAL, "not an OLE2 structured storage file") # [PL] header structure according to AAF specifications: ##Header @@ -1303,39 +1303,39 @@ class OleFileIO(object): if self.Sig != MAGIC: # OLE signature should always be present - self._raise_defect(DEFECT_FATAL, "incorrect OLE signature") + self.raise_defect(DEFECT_FATAL, "incorrect OLE signature") if self.clsid != bytearray(16): # according to AAF specs, CLSID should always be zero - self._raise_defect(DEFECT_INCORRECT, "incorrect CLSID in OLE header") + self.raise_defect(DEFECT_INCORRECT, "incorrect CLSID in OLE header") debug( "MinorVersion = %d" % self.MinorVersion ) debug( "DllVersion = %d" % self.DllVersion ) if self.DllVersion not in [3, 4]: # version 3: usual format, 512 bytes per sector # version 4: large format, 4K per sector - self._raise_defect(DEFECT_INCORRECT, "incorrect DllVersion in OLE header") + self.raise_defect(DEFECT_INCORRECT, "incorrect DllVersion in OLE header") debug( "ByteOrder = %X" % self.ByteOrder ) if self.ByteOrder != 0xFFFE: # For now only common little-endian documents are handled correctly - self._raise_defect(DEFECT_FATAL, "incorrect ByteOrder in OLE header") + self.raise_defect(DEFECT_FATAL, "incorrect ByteOrder in OLE header") # TODO: add big-endian support for documents created on Mac ? # But according to [MS-CFB] ? v20140502, ByteOrder MUST be 0xFFFE. self.SectorSize = 2**self.SectorShift debug( "SectorSize = %d" % self.SectorSize ) if self.SectorSize not in [512, 4096]: - self._raise_defect(DEFECT_INCORRECT, "incorrect SectorSize in OLE header") + self.raise_defect(DEFECT_INCORRECT, "incorrect SectorSize in OLE header") if (self.DllVersion==3 and self.SectorSize!=512) \ or (self.DllVersion==4 and self.SectorSize!=4096): - self._raise_defect(DEFECT_INCORRECT, "SectorSize does not match DllVersion in OLE header") + self.raise_defect(DEFECT_INCORRECT, "SectorSize does not match DllVersion in OLE header") self.MiniSectorSize = 2**self.MiniSectorShift debug( "MiniSectorSize = %d" % self.MiniSectorSize ) if self.MiniSectorSize not in [64]: - self._raise_defect(DEFECT_INCORRECT, "incorrect MiniSectorSize in OLE header") + self.raise_defect(DEFECT_INCORRECT, "incorrect MiniSectorSize in OLE header") if self.Reserved != 0 or self.Reserved1 != 0: - self._raise_defect(DEFECT_INCORRECT, "incorrect OLE header (non-null reserved bytes)") + self.raise_defect(DEFECT_INCORRECT, "incorrect OLE header (non-null reserved bytes)") debug( "csectDir = %d" % self.csectDir ) # Number of directory sectors (only allowed if DllVersion != 3) if self.SectorSize==512 and self.csectDir!=0: - self._raise_defect(DEFECT_INCORRECT, "incorrect csectDir in OLE header") + self.raise_defect(DEFECT_INCORRECT, "incorrect csectDir in OLE header") debug( "csectFat = %d" % self.csectFat ) # csectFat = number of FAT sectors in the file debug( "sectDirStart = %X" % self.sectDirStart ) @@ -1346,7 +1346,7 @@ class OleFileIO(object): # (according to MS-CFB, may be != 0 for applications supporting file # transactions) if self.signature != 0: - self._raise_defect(DEFECT_POTENTIAL, "incorrect OLE header (signature>0)") + self.raise_defect(DEFECT_POTENTIAL, "incorrect OLE header (signature>0)") debug( "MiniSectorCutoff = %d" % self.MiniSectorCutoff ) # MS-CFB: This integer field MUST be set to 0x00001000. This field # specifies the maximum size of a user-defined data stream allocated @@ -1354,7 +1354,7 @@ class OleFileIO(object): # Any user-defined data stream larger than or equal to this cutoff size # must be allocated as normal sectors from the FAT. if self.MiniSectorCutoff != 0x1000: - self._raise_defect(DEFECT_INCORRECT, "incorrect MiniSectorCutoff in OLE header") + self.raise_defect(DEFECT_INCORRECT, "incorrect MiniSectorCutoff in OLE header") debug( "MiniFatStart = %X" % self.MiniFatStart ) debug( "csectMiniFat = %d" % self.csectMiniFat ) debug( "sectDifStart = %X" % self.sectDifStart ) @@ -1422,7 +1422,7 @@ class OleFileIO(object): #TODO: would it be more efficient using a dict or hash values, instead # of a list of long ? if first_sect in used_streams: - self._raise_defect(DEFECT_INCORRECT, 'Stream referenced twice') + self.raise_defect(DEFECT_INCORRECT, 'Stream referenced twice') else: used_streams.append(first_sect) @@ -1566,10 +1566,10 @@ class OleFileIO(object): if self.csectFat <= 109: # there must be at least 109 blocks in header and the rest in # DIFAT, so number of sectors must be >109. - self._raise_defect(DEFECT_INCORRECT, 'incorrect DIFAT, not enough sectors') + self.raise_defect(DEFECT_INCORRECT, 'incorrect DIFAT, not enough sectors') if self.sectDifStart >= self.nb_sect: # initial DIFAT block index must be valid - self._raise_defect(DEFECT_FATAL, 'incorrect DIFAT, first index out of range') + self.raise_defect(DEFECT_FATAL, 'incorrect DIFAT, first index out of range') debug( "DIFAT analysis..." ) # We compute the necessary number of DIFAT sectors : # Number of pointers per DIFAT sector = (sectorsize/4)-1 @@ -1630,7 +1630,7 @@ class OleFileIO(object): (self.minifatsect, self.csectMiniFat, used_size, stream_size, nb_minisectors)) if used_size > stream_size: # This is not really a problem, but may indicate a wrong implementation: - self._raise_defect(DEFECT_INCORRECT, 'OLE MiniStream is larger than MiniFAT') + self.raise_defect(DEFECT_INCORRECT, 'OLE MiniStream is larger than MiniFAT') # In any case, first read stream_size: s = self._open(self.minifatsect, stream_size, force_FAT=True).read() #[PL] Old code replaced by an array: @@ -1666,12 +1666,12 @@ class OleFileIO(object): except: debug('getsect(): sect=%X, seek=%d, filesize=%d' % (sect, self.sectorsize*(sect+1), self._filesize)) - self._raise_defect(DEFECT_FATAL, 'OLE sector index out of range') + self.raise_defect(DEFECT_FATAL, 'OLE sector index out of range') sector = self.fp.read(self.sectorsize) if len(sector) != self.sectorsize: debug('getsect(): sect=%X, read=%d, sectorsize=%d' % (sect, len(sector), self.sectorsize)) - self._raise_defect(DEFECT_FATAL, 'incomplete OLE sector') + self.raise_defect(DEFECT_FATAL, 'incomplete OLE sector') return sector @@ -1693,7 +1693,7 @@ class OleFileIO(object): except: debug('write_sect(): sect=%X, seek=%d, filesize=%d' % (sect, self.sectorsize*(sect+1), self._filesize)) - self._raise_defect(DEFECT_FATAL, 'OLE sector index out of range') + self.raise_defect(DEFECT_FATAL, 'OLE sector index out of range') if len(data) < self.sectorsize: # add padding data += padding * (self.sectorsize - len(data)) @@ -1751,10 +1751,10 @@ class OleFileIO(object): """ # check if SID is OK: if sid<0 or sid>=len(self.direntries): - self._raise_defect(DEFECT_FATAL, "OLE directory index out of range") + self.raise_defect(DEFECT_FATAL, "OLE directory index out of range") # check if entry was already referenced: if self.direntries[sid] is not None: - self._raise_defect(DEFECT_INCORRECT, + self.raise_defect(DEFECT_INCORRECT, "double reference for OLE stream/storage") # if exception not raised, return the object return self.direntries[sid] @@ -1833,7 +1833,7 @@ class OleFileIO(object): # add it to the list files.append(prefix[1:] + [entry.name]) else: - self._raise_defect(DEFECT_INCORRECT, 'The directory tree contains an entry which is not a stream nor a storage.') + self.raise_defect(DEFECT_INCORRECT, 'The directory tree contains an entry which is not a stream nor a storage.') def listdir(self, streams=True, storages=False): @@ -2102,7 +2102,7 @@ class OleFileIO(object): # a fatal error when parsing the whole file msg = 'Error while parsing properties header in stream %s: %s' % ( repr(streampath), exc) - self._raise_defect(DEFECT_INCORRECT, msg, type(exc)) + self.raise_defect(DEFECT_INCORRECT, msg, type(exc)) return data for i in range(num_props): @@ -2203,7 +2203,7 @@ class OleFileIO(object): # a DEFECT_INCORRECT, because parsing can go on msg = 'Error while parsing property id %d in stream %s: %s' % ( id, repr(streampath), exc) - self._raise_defect(DEFECT_INCORRECT, msg, type(exc)) + self.raise_defect(DEFECT_INCORRECT, msg, type(exc)) return data From 1b98b76bcfe5dd0068ae6504d15760c99943dc41 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 28 May 2015 22:34:23 +1000 Subject: [PATCH 12/42] Flake8 fixes --- PIL/OleFileIO.py | 288 +++++++++++++++++++++-------------------------- 1 file changed, 130 insertions(+), 158 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index 7449407b9..877599add 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -256,11 +256,14 @@ __version__ = '0.42b' import io import sys -import struct, array, os.path, datetime +import struct +import array +import os.path +import datetime #=== COMPATIBILITY WORKAROUNDS ================================================ -#[PL] Define explicitly the public API to avoid private objects in pydoc: +# [PL] Define explicitly the public API to avoid private objects in pydoc: #TODO: add more # __all__ = ['OleFileIO', 'isOleFile', 'MAGIC'] @@ -276,7 +279,7 @@ except: # no xrange, for Python 3 it was renamed as range: iterrange = range -#[PL] workaround to fix an issue with array item size on 64 bits systems: +# [PL] workaround to fix an issue with array item size on 64 bits systems: if array.array('L').itemsize == 4: # on 32 bits platforms, long integers in an array are 32 bits: UINT32 = 'L' @@ -300,14 +303,14 @@ else: raise ValueError('Need to fix a bug with 32 bit arrays, please contact author...') -#[PL] These workarounds were inspired from the Path module +# [PL] These workarounds were inspired from the Path module # (see http://www.jorendorff.com/articles/python/path/) try: basestring except NameError: basestring = str -#[PL] Experimental setting: if True, OLE filenames will be kept in Unicode +# [PL] Experimental setting: if True, OLE filenames will be kept in Unicode # if False (default PIL behaviour), all filenames are converted to Latin-1. KEEP_UNICODE_NAMES = True @@ -323,15 +326,22 @@ else: #TODO: replace this by proper logging -#[PL] DEBUG display mode: False by default, use set_debug_mode() or "-d" on +# [PL] DEBUG display mode: False by default, use set_debug_mode() or "-d" on # command line to change it. DEBUG_MODE = False + + def debug_print(msg): print(msg) + + def debug_pass(msg): pass + + debug = debug_pass + def set_debug_mode(debug_mode): """ Set debug mode on or off, to control display of debugging messages. @@ -350,39 +360,39 @@ def set_debug_mode(debug_mode): # magic bytes that should be at the beginning of every OLE file: MAGIC = b'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1' -#[PL]: added constants for Sector IDs (from AAF specifications) -MAXREGSECT = 0xFFFFFFFA # (-6) maximum SECT -DIFSECT = 0xFFFFFFFC # (-4) denotes a DIFAT sector in a FAT -FATSECT = 0xFFFFFFFD # (-3) denotes a FAT sector in a FAT -ENDOFCHAIN = 0xFFFFFFFE # (-2) end of a virtual stream chain -FREESECT = 0xFFFFFFFF # (-1) unallocated sector +# [PL]: added constants for Sector IDs (from AAF specifications) +MAXREGSECT = 0xFFFFFFFA # (-6) maximum SECT +DIFSECT = 0xFFFFFFFC # (-4) denotes a DIFAT sector in a FAT +FATSECT = 0xFFFFFFFD # (-3) denotes a FAT sector in a FAT +ENDOFCHAIN = 0xFFFFFFFE # (-2) end of a virtual stream chain +FREESECT = 0xFFFFFFFF # (-1) unallocated sector -#[PL]: added constants for Directory Entry IDs (from AAF specifications) -MAXREGSID = 0xFFFFFFFA # (-6) maximum directory entry ID -NOSTREAM = 0xFFFFFFFF # (-1) unallocated directory entry +# [PL]: added constants for Directory Entry IDs (from AAF specifications) +MAXREGSID = 0xFFFFFFFA # (-6) maximum directory entry ID +NOSTREAM = 0xFFFFFFFF # (-1) unallocated directory entry -#[PL] object types in storage (from AAF specifications) -STGTY_EMPTY = 0 # empty directory entry (according to OpenOffice.org doc) -STGTY_STORAGE = 1 # element is a storage object -STGTY_STREAM = 2 # element is a stream object -STGTY_LOCKBYTES = 3 # element is an ILockBytes object -STGTY_PROPERTY = 4 # element is an IPropertyStorage object -STGTY_ROOT = 5 # element is a root storage +# [PL] object types in storage (from AAF specifications) +STGTY_EMPTY = 0 # empty directory entry (according to OpenOffice.org doc) +STGTY_STORAGE = 1 # element is a storage object +STGTY_STREAM = 2 # element is a stream object +STGTY_LOCKBYTES = 3 # element is an ILockBytes object +STGTY_PROPERTY = 4 # element is an IPropertyStorage object +STGTY_ROOT = 5 # element is a root storage # # -------------------------------------------------------------------- # property types -VT_EMPTY=0; VT_NULL=1; VT_I2=2; VT_I4=3; VT_R4=4; VT_R8=5; VT_CY=6; -VT_DATE=7; VT_BSTR=8; VT_DISPATCH=9; VT_ERROR=10; VT_BOOL=11; -VT_VARIANT=12; VT_UNKNOWN=13; VT_DECIMAL=14; VT_I1=16; VT_UI1=17; -VT_UI2=18; VT_UI4=19; VT_I8=20; VT_UI8=21; VT_INT=22; VT_UINT=23; -VT_VOID=24; VT_HRESULT=25; VT_PTR=26; VT_SAFEARRAY=27; VT_CARRAY=28; -VT_USERDEFINED=29; VT_LPSTR=30; VT_LPWSTR=31; VT_FILETIME=64; -VT_BLOB=65; VT_STREAM=66; VT_STORAGE=67; VT_STREAMED_OBJECT=68; -VT_STORED_OBJECT=69; VT_BLOB_OBJECT=70; VT_CF=71; VT_CLSID=72; -VT_VECTOR=0x1000; +VT_EMPTY = 0; VT_NULL = 1; VT_I2 = 2; VT_I4 = 3; VT_R4 = 4; VT_R8 = 5; VT_CY = 6; +VT_DATE = 7; VT_BSTR = 8; VT_DISPATCH = 9; VT_ERROR = 10; VT_BOOL = 11; +VT_VARIANT = 12; VT_UNKNOWN = 13; VT_DECIMAL = 14; VT_I1 = 16; VT_UI1 = 17; +VT_UI2 = 18; VT_UI4 = 19; VT_I8 = 20; VT_UI8 = 21; VT_INT = 22; VT_UINT = 23; +VT_VOID = 24; VT_HRESULT = 25; VT_PTR = 26; VT_SAFEARRAY = 27; VT_CARRAY = 28; +VT_USERDEFINED = 29; VT_LPSTR = 30; VT_LPWSTR = 31; VT_FILETIME = 64; +VT_BLOB = 65; VT_STREAM = 66; VT_STORAGE = 67; VT_STREAMED_OBJECT = 68; +VT_STORED_OBJECT = 69; VT_BLOB_OBJECT = 70; VT_CF = 71; VT_CLSID = 72; +VT_VECTOR = 0x1000; # map property id to name (for debugging purposes) @@ -398,7 +408,7 @@ for keyword, var in list(vars().items()): WORD_CLSID = "00020900-0000-0000-C000-000000000046" #TODO: check Excel, PPT, ... -#[PL]: Defect levels to classify parsing errors - see OleFileIO._raise_defect() +# [PL]: Defect levels to classify parsing errors - see OleFileIO._raise_defect() DEFECT_UNSURE = 10 # a case which looks weird, but not sure it's a defect DEFECT_POTENTIAL = 20 # a potential defect DEFECT_INCORRECT = 30 # an error according to specifications, but parsing @@ -410,7 +420,7 @@ DEFECT_FATAL = 40 # an error which cannot be ignored, parsing is # (this is used in isOleFile and OleFile.open) MINIMAL_OLEFILE_SIZE = 1536 -#[PL] add useful constants to __all__: +# [PL] add useful constants to __all__: # for key in list(vars().keys()): # if key.startswith('STGTY_') or key.startswith('DEFECT_'): # __all__.append(key) @@ -418,7 +428,7 @@ MINIMAL_OLEFILE_SIZE = 1536 #=== FUNCTIONS =============================================================== -def isOleFile (filename): +def isOleFile(filename): """ Test if a file is an OLE container (according to the magic bytes in its header). @@ -502,7 +512,6 @@ def _clsid(clsid): tuple(map(i8, clsid[8:16])))) - def filetime2datetime(filetime): """ convert FILETIME (64 bits int) to Python datetime.datetime @@ -514,7 +523,6 @@ def filetime2datetime(filetime): return _FILETIME_null_date + datetime.timedelta(microseconds=filetime//10) - #=== CLASSES ================================================================== class OleMetadata(object): @@ -622,7 +630,6 @@ class OleMetadata(object): self.language = None self.doc_version = None - def parse_properties(self, olefile): """ Parse standard properties of an OLE file, from the streams @@ -707,11 +714,11 @@ class _OleStream(io.BytesIO): """ debug('_OleStream.__init__:') debug(' sect=%d (%X), size=%d, offset=%d, sectorsize=%d, len(fat)=%d, fp=%s' - %(sect,sect,size,offset,sectorsize,len(fat), repr(fp))) - #[PL] To detect malformed documents with FAT loops, we compute the + % (sect, sect, size, offset, sectorsize, len(fat), repr(fp))) + # [PL] To detect malformed documents with FAT loops, we compute the # expected number of sectors in the stream: unknown_size = False - if size==0x7FFFFFFF: + if size == 0x7FFFFFFF: # this is the case when called from OleFileIO._open(), and stream # size is not known in advance (for example when reading the # Directory stream). Then we can only guess maximum size: @@ -733,7 +740,7 @@ class _OleStream(io.BytesIO): if size == 0 and sect != ENDOFCHAIN: debug('size == 0 and sect != ENDOFCHAIN:') raise IOError('incorrect OLE sector index for empty stream') - #[PL] A fixed-length for loop is used instead of an undefined while + # [PL] A fixed-length for loop is used instead of an undefined while # loop to avoid DoS attacks: for i in range(nb_sectors): # Sector index may be ENDOFCHAIN, but only if size was unknown @@ -745,9 +752,9 @@ class _OleStream(io.BytesIO): debug('sect=ENDOFCHAIN before expected size') raise IOError('incomplete OLE stream') # sector index should be within FAT: - if sect<0 or sect>=len(fat): + if sect < 0 or sect >= len(fat): debug('sect=%d (%X) / len(fat)=%d' % (sect, sect, len(fat))) - debug('i=%d / nb_sectors=%d' %(i, nb_sectors)) + debug('i=%d / nb_sectors=%d' % (i, nb_sectors)) ## tmp_data = b"".join(data) ## f = open('test_debug.bin', 'wb') ## f.write(tmp_data) @@ -767,7 +774,7 @@ class _OleStream(io.BytesIO): # Note: if sector is the last of the file, sometimes it is not a # complete sector (of 512 or 4K), so we may read less than # sectorsize. - if len(sector_data)!=sectorsize and sect!=(len(fat)-1): + if len(sector_data) != sectorsize and sect != (len(fat)-1): debug('sect=%d / len(fat)=%d, seek=%d / filesize=%d, len read=%d' % (sect, len(fat), offset+sectorsize*sect, filesize, len(sector_data))) debug('seek+len(read)=%d' % (offset+sectorsize*sect+len(sector_data))) @@ -779,7 +786,7 @@ class _OleStream(io.BytesIO): except IndexError: # [PL] if pointer is out of the FAT an exception is raised raise IOError('incorrect OLE FAT, sector index out of range') - #[PL] Last sector should be a "end of chain" marker: + # [PL] Last sector should be a "end of chain" marker: if sect != ENDOFCHAIN: raise IOError('incorrect last sector index in OLE stream') data = b"".join(data) @@ -808,7 +815,7 @@ class _OleDirectoryEntry(object): """ OLE2 Directory Entry """ - #[PL] parsing code moved from OleFileIO.loaddirectory + # [PL] parsing code moved from OleFileIO.loaddirectory # struct to parse directory entries: # <: little-endian byte order, standard sizes @@ -833,7 +840,6 @@ class _OleDirectoryEntry(object): DIRENTRY_SIZE = 128 assert struct.calcsize(STRUCT_DIRENTRY) == DIRENTRY_SIZE - def __init__(self, entry, sid, olefile): """ Constructor for an _OleDirectoryEntry object. @@ -883,7 +889,7 @@ class _OleDirectoryEntry(object): #debug (struct.unpack(fmt_entry, entry[:len_entry])) # name should be at most 31 unicode characters + null character, # so 64 bytes in total (31*2 + 2): - if namelength>64: + if namelength > 64: olefile.raise_defect(DEFECT_INCORRECT, 'incorrect DirEntry name length') # if exception not raised, namelength is set to the maximum value: namelength = 64 @@ -911,7 +917,7 @@ class _OleDirectoryEntry(object): olefile.raise_defect(DEFECT_UNSURE, 'incorrect OLE stream size') self.size = sizeLow else: - self.size = sizeLow + (long(sizeHigh)<<32) + self.size = sizeLow + (long(sizeHigh) << 32) debug(' - size: %d (sizeLow=%d, sizeHigh=%d)' % (self.size, sizeLow, sizeHigh)) self.clsid = _clsid(clsid) @@ -920,17 +926,15 @@ class _OleDirectoryEntry(object): if self.entry_type == STGTY_STORAGE and self.size != 0: olefile.raise_defect(DEFECT_POTENTIAL, 'OLE storage with size>0') # check if stream is not already referenced elsewhere: - if self.entry_type in (STGTY_ROOT, STGTY_STREAM) and self.size>0: + if self.entry_type in (STGTY_ROOT, STGTY_STREAM) and self.size > 0: if self.size < olefile.minisectorcutoff \ - and self.entry_type==STGTY_STREAM: # only streams can be in MiniFAT + and self.entry_type == STGTY_STREAM: # only streams can be in MiniFAT # ministream object minifat = True else: minifat = False olefile._check_duplicate_stream(self.isectStart, minifat) - - def build_storage_tree(self): """ Read and build the red-black tree attached to this _OleDirectoryEntry @@ -954,7 +958,6 @@ class _OleDirectoryEntry(object): # (see rich comparison methods in this class) self.kids.sort() - def append_kids(self, child_sid): """ Walk through red-black tree of children of this directory entry to add @@ -963,13 +966,13 @@ class _OleDirectoryEntry(object): :param child_sid : index of child directory entry to use, or None when called first time for the root. (only used during recursion) """ - #[PL] this method was added to use simple recursion instead of a complex + # [PL] this method was added to use simple recursion instead of a complex # algorithm. # if this is not a storage or a leaf of the tree, nothing to do: if child_sid == NOSTREAM: return # check if child SID is in the proper range: - if child_sid<0 or child_sid>=len(self.olefile.direntries): + if child_sid < 0 or child_sid >= len(self.olefile.direntries): self.olefile.raise_defect(DEFECT_FATAL, 'OLE DirEntry index out of range') # get child direntry: child = self.olefile._load_direntry(child_sid) #direntries[child_sid] @@ -998,7 +1001,6 @@ class _OleDirectoryEntry(object): # Afterwards build kid's own tree if it's also a storage: child.build_storage_tree() - def __eq__(self, other): "Compare entries by name" return self.name == other.name @@ -1018,7 +1020,6 @@ class _OleDirectoryEntry(object): #TODO: replace by the same function as MS implementation ? # (order by name length first, then case-insensitive order) - def dump(self, tab = 0): "Dump this entry, and all its subentries (for debug purposes only)" TYPES = ["(invalid)", "(storage)", "(stream)", "(lockbytes)", @@ -1033,7 +1034,6 @@ class _OleDirectoryEntry(object): for kid in self.kids: kid.dump(tab + 2) - def getmtime(self): """ Return modification time of a directory entry. @@ -1047,7 +1047,6 @@ class _OleDirectoryEntry(object): return None return filetime2datetime(self.modifyTime) - def getctime(self): """ Return creation time of a directory entry. @@ -1134,7 +1133,6 @@ class OleFileIO(object): if filename: self.open(filename, write_mode=write_mode) - def raise_defect(self, defect_level, message, exception_type=IOError): """ This method should be called for any defect found during file parsing. @@ -1158,7 +1156,6 @@ class OleFileIO(object): # just record the issue, no exception raised: self.parsing_issues.append((exception_type, message)) - def _decode_utf16_str(self, utf16_str, errors='replace'): """ Decode a string encoded in UTF-16 LE format, as found in the OLE @@ -1177,7 +1174,6 @@ class OleFileIO(object): # path_encoding=None, return the Unicode string as-is: return unicode_str - def open(self, filename, write_mode=False): """ Open an OLE2 file in read-only or read/write mode. @@ -1196,7 +1192,7 @@ class OleFileIO(object): of read-only by default. (ignored if filename is not a path) """ self.write_mode = write_mode - #[PL] check if filename is a string-like or file-like object: + # [PL] check if filename is a string-like or file-like object: # (it is better to check for a read() method) if hasattr(filename, 'read'): #TODO: also check seek and tell methods? @@ -1221,7 +1217,7 @@ class OleFileIO(object): # file-like objects: #TODO: do it above, using getsize with filename when possible? #TODO: fix code to fail with clear exception when filesize cannot be obtained - filesize=0 + filesize = 0 self.fp.seek(0, os.SEEK_END) try: filesize = self.fp.tell() @@ -1278,7 +1274,7 @@ class OleFileIO(object): # '<' indicates little-endian byte ordering for Intel (cf. struct module help) fmt_header = '<8s16sHHHHHHLLLLLLLLLL' header_size = struct.calcsize(fmt_header) - debug( "fmt_header size = %d, +FAT = %d" % (header_size, header_size + 109*4) ) + debug("fmt_header size = %d, +FAT = %d" % (header_size, header_size + 109*4)) header1 = header[:header_size] ( self.Sig, @@ -1299,7 +1295,7 @@ class OleFileIO(object): self.sectDifStart, self.csectDif ) = struct.unpack(fmt_header, header1) - debug( struct.unpack(fmt_header, header1)) + debug(struct.unpack(fmt_header, header1)) if self.Sig != MAGIC: # OLE signature should always be present @@ -1307,47 +1303,47 @@ class OleFileIO(object): if self.clsid != bytearray(16): # according to AAF specs, CLSID should always be zero self.raise_defect(DEFECT_INCORRECT, "incorrect CLSID in OLE header") - debug( "MinorVersion = %d" % self.MinorVersion ) - debug( "DllVersion = %d" % self.DllVersion ) + debug("MinorVersion = %d" % self.MinorVersion) + debug("DllVersion = %d" % self.DllVersion) if self.DllVersion not in [3, 4]: # version 3: usual format, 512 bytes per sector # version 4: large format, 4K per sector self.raise_defect(DEFECT_INCORRECT, "incorrect DllVersion in OLE header") - debug( "ByteOrder = %X" % self.ByteOrder ) + debug("ByteOrder = %X" % self.ByteOrder) if self.ByteOrder != 0xFFFE: # For now only common little-endian documents are handled correctly self.raise_defect(DEFECT_FATAL, "incorrect ByteOrder in OLE header") # TODO: add big-endian support for documents created on Mac ? # But according to [MS-CFB] ? v20140502, ByteOrder MUST be 0xFFFE. self.SectorSize = 2**self.SectorShift - debug( "SectorSize = %d" % self.SectorSize ) + debug("SectorSize = %d" % self.SectorSize) if self.SectorSize not in [512, 4096]: self.raise_defect(DEFECT_INCORRECT, "incorrect SectorSize in OLE header") - if (self.DllVersion==3 and self.SectorSize!=512) \ - or (self.DllVersion==4 and self.SectorSize!=4096): + if (self.DllVersion == 3 and self.SectorSize != 512) \ + or (self.DllVersion == 4 and self.SectorSize != 4096): self.raise_defect(DEFECT_INCORRECT, "SectorSize does not match DllVersion in OLE header") self.MiniSectorSize = 2**self.MiniSectorShift - debug( "MiniSectorSize = %d" % self.MiniSectorSize ) + debug("MiniSectorSize = %d" % self.MiniSectorSize) if self.MiniSectorSize not in [64]: self.raise_defect(DEFECT_INCORRECT, "incorrect MiniSectorSize in OLE header") if self.Reserved != 0 or self.Reserved1 != 0: self.raise_defect(DEFECT_INCORRECT, "incorrect OLE header (non-null reserved bytes)") - debug( "csectDir = %d" % self.csectDir ) + debug("csectDir = %d" % self.csectDir) # Number of directory sectors (only allowed if DllVersion != 3) - if self.SectorSize==512 and self.csectDir!=0: + if self.SectorSize == 512 and self.csectDir != 0: self.raise_defect(DEFECT_INCORRECT, "incorrect csectDir in OLE header") - debug( "csectFat = %d" % self.csectFat ) + debug("csectFat = %d" % self.csectFat) # csectFat = number of FAT sectors in the file - debug( "sectDirStart = %X" % self.sectDirStart ) + debug("sectDirStart = %X" % self.sectDirStart) # sectDirStart = 1st sector containing the directory - debug( "signature = %d" % self.signature ) + debug("signature = %d" % self.signature) # Signature should be zero, BUT some implementations do not follow this # rule => only a potential defect: # (according to MS-CFB, may be != 0 for applications supporting file # transactions) if self.signature != 0: self.raise_defect(DEFECT_POTENTIAL, "incorrect OLE header (signature>0)") - debug( "MiniSectorCutoff = %d" % self.MiniSectorCutoff ) + debug("MiniSectorCutoff = %d" % self.MiniSectorCutoff) # MS-CFB: This integer field MUST be set to 0x00001000. This field # specifies the maximum size of a user-defined data stream allocated # from the mini FAT and mini stream, and that cutoff is 4096 bytes. @@ -1355,15 +1351,15 @@ class OleFileIO(object): # must be allocated as normal sectors from the FAT. if self.MiniSectorCutoff != 0x1000: self.raise_defect(DEFECT_INCORRECT, "incorrect MiniSectorCutoff in OLE header") - debug( "MiniFatStart = %X" % self.MiniFatStart ) - debug( "csectMiniFat = %d" % self.csectMiniFat ) - debug( "sectDifStart = %X" % self.sectDifStart ) - debug( "csectDif = %d" % self.csectDif ) + debug("MiniFatStart = %X" % self.MiniFatStart) + debug("csectMiniFat = %d" % self.csectMiniFat) + debug("sectDifStart = %X" % self.sectDifStart) + debug("csectDif = %d" % self.csectDif) # calculate the number of sectors in the file # (-1 because header doesn't count) - self.nb_sect = ( (filesize + self.SectorSize-1) // self.SectorSize) - 1 - debug( "Number of sectors in the file: %d" % self.nb_sect ) + self.nb_sect = ((filesize + self.SectorSize-1) // self.SectorSize) - 1 + debug("Number of sectors in the file: %d" % self.nb_sect) #TODO: change this test, because an OLE file MAY contain other data # after the last sector. @@ -1393,14 +1389,12 @@ class OleFileIO(object): self.ministream = None self.minifatsect = self.MiniFatStart #i32(header, 60) - def close(self): """ close the OLE file, to release the file object """ self.fp.close() - def _check_duplicate_stream(self, first_sect, minifat=False): """ Checks if a stream has not been already referenced elsewhere. @@ -1416,7 +1410,7 @@ class OleFileIO(object): else: debug('_check_duplicate_stream: sect=%d in FAT' % first_sect) # some values can be safely ignored (not a real stream): - if first_sect in (DIFSECT,FATSECT,ENDOFCHAIN,FREESECT): + if first_sect in (DIFSECT, FATSECT, ENDOFCHAIN, FREESECT): return used_streams = self._used_streams_fat #TODO: would it be more efficient using a dict or hash values, instead @@ -1426,7 +1420,6 @@ class OleFileIO(object): else: used_streams.append(first_sect) - def dumpfat(self, fat, firstindex=0): "Displays a part of FAT in human-readable form for debugging purpose" # [PL] added only for debug @@ -1450,7 +1443,7 @@ class OleFileIO(object): index = l*VPL print("%8X:" % (firstindex+index), end=" ") for i in range(index, index+VPL): - if i>=nbsect: + if i >= nbsect: break sect = fat[i] aux = sect & 0xFFFFFFFF # JYTHON-WORKAROUND @@ -1464,12 +1457,11 @@ class OleFileIO(object): print(name, end=" ") print() - def dumpsect(self, sector, firstindex=0): "Displays a sector in a human-readable form, for debugging purpose." if not DEBUG_MODE: return - VPL=8 # number of values per line (8+1 * 8+1 = 81) + VPL = 8 # number of values per line (8+1 * 8+1 = 81) tab = array.array(UINT32, sector) if sys.byteorder == 'big': tab.byteswap() @@ -1483,7 +1475,7 @@ class OleFileIO(object): index = l*VPL print("%8X:" % (firstindex+index), end=" ") for i in range(index, index+VPL): - if i>=nbsect: + if i >= nbsect: break sect = tab[i] name = "%8X" % sect @@ -1501,7 +1493,6 @@ class OleFileIO(object): a.byteswap() return a - def loadfat_sect(self, sect): """ Adds the indexes of the given sector to the FAT @@ -1533,7 +1524,6 @@ class OleFileIO(object): self.fat = self.fat + nextfat return isect - def loadfat(self, header): """ Load the FAT table. @@ -1544,7 +1534,7 @@ class OleFileIO(object): # Additional sectors are described by DIF blocks sect = header[76:512] - debug( "len(sect)=%d, so %d integers" % (len(sect), len(sect)//4) ) + debug("len(sect)=%d, so %d integers" % (len(sect), len(sect)//4)) #fat = [] # [PL] FAT is an array of 32 bits unsigned ints, it's more effective # to use an array than a list in Python. @@ -1554,7 +1544,7 @@ class OleFileIO(object): #self.dumpfat(self.fat) ## for i in range(0, len(sect), 4): ## ix = i32(sect, i) -## #[PL] if ix == -2 or ix == -1: # ix == 0xFFFFFFFE or ix == 0xFFFFFFFF: +## # [PL] if ix == -2 or ix == -1: # ix == 0xFFFFFFFE or ix == 0xFFFFFFFF: ## if ix == 0xFFFFFFFE or ix == 0xFFFFFFFF: ## break ## s = self.getsect(ix) @@ -1570,19 +1560,19 @@ class OleFileIO(object): if self.sectDifStart >= self.nb_sect: # initial DIFAT block index must be valid self.raise_defect(DEFECT_FATAL, 'incorrect DIFAT, first index out of range') - debug( "DIFAT analysis..." ) + debug("DIFAT analysis...") # We compute the necessary number of DIFAT sectors : # Number of pointers per DIFAT sector = (sectorsize/4)-1 # (-1 because the last pointer is the next DIFAT sector number) nb_difat_sectors = (self.sectorsize//4)-1 # (if 512 bytes: each DIFAT sector = 127 pointers + 1 towards next DIFAT sector) nb_difat = (self.csectFat-109 + nb_difat_sectors-1)//nb_difat_sectors - debug( "nb_difat = %d" % nb_difat ) + debug("nb_difat = %d" % nb_difat) if self.csectDif != nb_difat: raise IOError('incorrect DIFAT') isect_difat = self.sectDifStart for i in iterrange(nb_difat): - debug( "DIFAT block %d, sector %X" % (i, isect_difat) ) + debug("DIFAT block %d, sector %X" % (i, isect_difat)) #TODO: check if corresponding FAT SID = DIFSECT sector_difat = self.getsect(isect_difat) difat = self.sect2array(sector_difat) @@ -1590,7 +1580,7 @@ class OleFileIO(object): self.loadfat_sect(difat[:nb_difat_sectors]) # last DIFAT pointer is next DIFAT sector: isect_difat = difat[nb_difat_sectors] - debug( "next DIFAT sector: %X" % isect_difat ) + debug("next DIFAT sector: %X" % isect_difat) # checks: if isect_difat not in [ENDOFCHAIN, FREESECT]: # last DIFAT pointer value must be ENDOFCHAIN or FREESECT @@ -1608,7 +1598,6 @@ class OleFileIO(object): debug('\nFAT:') self.dumpfat(self.fat) - def loadminifat(self): """ Load the MiniFAT table. @@ -1633,8 +1622,8 @@ class OleFileIO(object): self.raise_defect(DEFECT_INCORRECT, 'OLE MiniStream is larger than MiniFAT') # In any case, first read stream_size: s = self._open(self.minifatsect, stream_size, force_FAT=True).read() - #[PL] Old code replaced by an array: - #self.minifat = [i32(s, i) for i in range(0, len(s), 4)] + # [PL] Old code replaced by an array: + # self.minifat = [i32(s, i) for i in range(0, len(s), 4)] self.minifat = self.sect2array(s) # Then shrink the array to used size, to avoid indexes out of MiniStream: debug('MiniFAT shrunk from %d to %d sectors' % (len(self.minifat), nb_minisectors)) @@ -1658,9 +1647,9 @@ class OleFileIO(object): # [PL] the original code in PIL was wrong when sectors are 4KB instead of # 512 bytes: - #self.fp.seek(512 + self.sectorsize * sect) - #[PL]: added safety checks: - #print("getsect(%X)" % sect) + # self.fp.seek(512 + self.sectorsize * sect) + # [PL]: added safety checks: + # print("getsect(%X)" % sect) try: self.fp.seek(self.sectorsize * (sect+1)) except: @@ -1674,7 +1663,6 @@ class OleFileIO(object): self.raise_defect(DEFECT_FATAL, 'incomplete OLE sector') return sector - def write_sect(self, sect, data, padding=b'\x00'): """ Write given sector to file on disk. @@ -1685,7 +1673,7 @@ class OleFileIO(object): """ if not isinstance(data, bytes): raise TypeError("write_sect: data must be a bytes string") - if not isinstance(padding, bytes) or len(padding)!=1: + if not isinstance(padding, bytes) or len(padding) != 1: raise TypeError("write_sect: padding must be a bytes string of 1 char") #TODO: we could allow padding=None for no padding at all try: @@ -1701,7 +1689,6 @@ class OleFileIO(object): raise ValueError("Data is larger than sector size") self.fp.write(data) - def loaddirectory(self, sect): """ Load the directory. @@ -1715,14 +1702,14 @@ class OleFileIO(object): # (stream size is not known in advance) self.directory_fp = self._open(sect) - #[PL] to detect malformed documents and avoid DoS attacks, the maximum + # [PL] to detect malformed documents and avoid DoS attacks, the maximum # number of directory entries can be calculated: max_entries = self.directory_fp.size // 128 debug('loaddirectory: size=%d, max_entries=%d' % (self.directory_fp.size, max_entries)) # Create list of directory entries - #self.direntries = [] + # self.direntries = [] # We start with a list of "None" object self.direntries = [None] * max_entries ## for sid in iterrange(max_entries): @@ -1737,8 +1724,7 @@ class OleFileIO(object): # read and build all storage trees, starting from the root: self.root.build_storage_tree() - - def _load_direntry (self, sid): + def _load_direntry(self, sid): """ Load a directory entry from the directory. This method should only be called once for each storage/stream when @@ -1750,7 +1736,7 @@ class OleFileIO(object): :exception IOError: if the entry has always been referenced. """ # check if SID is OK: - if sid<0 or sid>=len(self.direntries): + if sid < 0 or sid >= len(self.direntries): self.raise_defect(DEFECT_FATAL, "OLE directory index out of range") # check if entry was already referenced: if self.direntries[sid] is not None: @@ -1763,14 +1749,12 @@ class OleFileIO(object): self.direntries[sid] = _OleDirectoryEntry(entry, sid, self) return self.direntries[sid] - def dumpdirectory(self): """ Dump directory (for debugging only) """ self.root.dump() - def _open(self, start, size = 0x7FFFFFFF, force_FAT=False): """ Open a stream, either in FAT or MiniFAT according to its size. @@ -1806,7 +1790,6 @@ class OleFileIO(object): sectorsize=self.sectorsize, fat=self.fat, filesize=self._filesize) - def _list(self, files, prefix, node, streams=True, storages=False): """ listdir helper @@ -1835,7 +1818,6 @@ class OleFileIO(object): else: self.raise_defect(DEFECT_INCORRECT, 'The directory tree contains an entry which is not a stream nor a storage.') - def listdir(self, streams=True, storages=False): """ Return a list of streams and/or storages stored in this file @@ -1849,7 +1831,6 @@ class OleFileIO(object): self._list(files, [], self.root, streams, storages) return files - def _find(self, filename): """ Returns directory entry of given filename. (openstream helper) @@ -1881,7 +1862,6 @@ class OleFileIO(object): node = kid return node.sid - def openstream(self, filename): """ Open a stream as a read-only file object (BytesIO). @@ -1903,7 +1883,6 @@ class OleFileIO(object): raise IOError("this file is not a stream") return self._open(entry.isectStart, entry.size) - def write_stream(self, stream_name, data): """ Write a stream to disk. For now, it is only possible to replace an @@ -1942,29 +1921,28 @@ class OleFileIO(object): # (sect, offset+self.sectorsize*sect)) # raise IOError('OLE sector index out of range') # extract one sector from data, the last one being smaller: - if i<(nb_sectors-1): - data_sector = data [i*self.sectorsize : (i+1)*self.sectorsize] + if i < (nb_sectors-1): + data_sector = data[i*self.sectorsize:(i+1)*self.sectorsize] #TODO: comment this if it works - assert(len(data_sector)==self.sectorsize) + assert(len(data_sector) == self.sectorsize) else: - data_sector = data [i*self.sectorsize:] - #TODO: comment this if it works + data_sector = data[i*self.sectorsize:] + # TODO: comment this if it works debug('write_stream: size=%d sectorsize=%d data_sector=%d size%%sectorsize=%d' % (size, self.sectorsize, len(data_sector), size % self.sectorsize)) - assert(len(data_sector) % self.sectorsize==size % self.sectorsize) + assert(len(data_sector) % self.sectorsize == size % self.sectorsize) self.write_sect(sect, data_sector) -## self.fp.write(data_sector) +# self.fp.write(data_sector) # jump to next sector in the FAT: try: sect = self.fat[sect] except IndexError: # [PL] if pointer is out of the FAT an exception is raised raise IOError('incorrect OLE FAT, sector index out of range') - #[PL] Last sector should be a "end of chain" marker: + # [PL] Last sector should be a "end of chain" marker: if sect != ENDOFCHAIN: raise IOError('incorrect last sector index in OLE stream') - def get_type(self, filename): """ Test if given filename exists as a stream or a storage in the OLE @@ -1984,7 +1962,6 @@ class OleFileIO(object): except: return False - def getmtime(self, filename): """ Return modification time of a stream/storage. @@ -2000,7 +1977,6 @@ class OleFileIO(object): entry = self.direntries[sid] return entry.getmtime() - def getctime(self, filename): """ Return creation time of a stream/storage. @@ -2016,7 +1992,6 @@ class OleFileIO(object): entry = self.direntries[sid] return entry.getctime() - def exists(self, filename): """ Test if given filename exists as a stream or a storage in the OLE @@ -2032,7 +2007,6 @@ class OleFileIO(object): except: return False - def get_size(self, filename): """ Return size of a stream in the OLE container, in bytes. @@ -2049,7 +2023,6 @@ class OleFileIO(object): raise TypeError('object is not an OLE stream') return entry.size - def get_rootentry_name(self): """ Return root entry name. Should usually be 'Root Entry' or 'R' in most @@ -2057,7 +2030,6 @@ class OleFileIO(object): """ return self.root.name - def getproperties(self, filename, convert_time=False, no_conversion=None): """ Return properties described in substream. @@ -2069,7 +2041,7 @@ class OleFileIO(object): :returns: a dictionary of values indexed by id (integer) """ - #REFERENCE: [MS-OLEPS] https://msdn.microsoft.com/en-us/library/dd942421.aspx + # REFERENCE: [MS-OLEPS] https://msdn.microsoft.com/en-us/library/dd942421.aspx # make sure no_conversion is a list, just to simplify code below: if no_conversion is None: no_conversion = [] @@ -2112,7 +2084,7 @@ class OleFileIO(object): offset = i32(s, 12+i*8) type = i32(s, offset) - debug ('property id=%d: type=%d offset=%X' % (id, type, offset)) + debug('property id=%d: type=%d offset=%X' % (id, type, offset)) # test for common types first (should perhaps use # a dictionary instead?) @@ -2121,14 +2093,14 @@ class OleFileIO(object): value = i16(s, offset+4) if value >= 32768: value = value - 65536 - elif type == VT_UI2: # 2-byte unsigned integer + elif type == VT_UI2: # 2-byte unsigned integer value = i16(s, offset+4) elif type in (VT_I4, VT_INT, VT_ERROR): # VT_I4: 32-bit signed integer # VT_ERROR: HRESULT, similar to 32-bit signed integer, # see http://msdn.microsoft.com/en-us/library/cc230330.aspx value = i32(s, offset+4) - elif type in (VT_UI4, VT_UINT): # 4-byte unsigned integer + elif type in (VT_UI4, VT_UINT): # 4-byte unsigned integer value = i32(s, offset+4) # FIXME elif type in (VT_BSTR, VT_LPSTR): # CodePageString, see http://msdn.microsoft.com/en-us/library/dd942354.aspx @@ -2152,12 +2124,12 @@ class OleFileIO(object): count = i32(s, offset+4) value = self._decode_utf16_str(s[offset+8:offset+8+count*2]) elif type == VT_FILETIME: - value = long(i32(s, offset+4)) + (long(i32(s, offset+8))<<32) + value = long(i32(s, offset+4)) + (long(i32(s, offset+8)) << 32) # FILETIME is a 64-bit int: "number of 100ns periods # since Jan 1,1601". if convert_time and id not in no_conversion: debug('Converting property #%d to python datetime, value=%d=%fs' - %(id, value, float(value)/10000000)) + % (id, value, float(value) / 10000000)) # convert FILETIME to Python datetime.datetime # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) @@ -2182,7 +2154,7 @@ class OleFileIO(object): value = bool(i16(s, offset+4)) else: value = None # everything else yields "None" - debug ('property id=%d: type=%d not implemented in parser yet' % (id, type)) + debug('property id=%d: type=%d not implemented in parser yet' % (id, type)) # missing: VT_EMPTY, VT_NULL, VT_R4, VT_R8, VT_CY, VT_DATE, # VT_DECIMAL, VT_I1, VT_I8, VT_UI8, @@ -2194,8 +2166,8 @@ class OleFileIO(object): # type of items, e.g. VT_VECTOR|VT_BSTR # see http://msdn.microsoft.com/en-us/library/dd942011.aspx - #print("%08x" % id, repr(value), end=" ") - #print("(%s)" % VT[i32(s, offset) & 0xFFF]) + # print("%08x" % id, repr(value), end=" ") + # print("(%s)" % VT[i32(s, offset) & 0xFFF]) data[id] = value except BaseException as exc: @@ -2267,14 +2239,14 @@ For more information, see http://www.decalage.info/olefile props = ole.getproperties(streamname, convert_time=True) props = sorted(props.items()) for k, v in props: - #[PL]: avoid to display too large or binary values: + # [PL]: avoid to display too large or binary values: if isinstance(v, (basestring, bytes)): if len(v) > 50: v = v[:50] if isinstance(v, bytes): # quick and dirty binary check: - for c in (1,2,3,4,5,6,7,11,12,14,15,16,17,18,19,20, - 21,22,23,24,25,26,27,28,29,30,31): + for c in (1, 2, 3, 4, 5, 6, 7, 11, 12, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31): if c in bytearray(v): v = '(binary data)' break @@ -2285,7 +2257,7 @@ For more information, see http://www.decalage.info/olefile print('\nChecking streams...') for streamname in ole.listdir(): # print name using repr() to convert binary chars to \xNN: - print('-', repr('/'.join(streamname)),'-', end=' ') + print('-', repr('/'.join(streamname)), '-', end=' ') st_type = ole.get_type(streamname) if st_type == STGTY_STREAM: print('size %d' % ole.get_size(streamname)) @@ -2295,11 +2267,11 @@ For more information, see http://www.decalage.info/olefile print('NOT a stream : type=%d' % st_type) print() -## for streamname in ole.listdir(): -## # print name using repr() to convert binary chars to \xNN: -## print('-', repr('/'.join(streamname)),'-', end=' ') -## print(ole.getmtime(streamname)) -## print() +# for streamname in ole.listdir(): +# # print name using repr() to convert binary chars to \xNN: +# print('-', repr('/'.join(streamname)),'-', end=' ') +# print(ole.getmtime(streamname)) +# print() print('Modification/Creation times of all directory entries:') for entry in ole.direntries: @@ -2312,7 +2284,7 @@ For more information, see http://www.decalage.info/olefile meta = ole.get_metadata() meta.dump() print() - #[PL] Test a few new methods: + # [PL] Test a few new methods: root = ole.get_rootentry_name() print('Root entry name: "%s"' % root) if ole.exists('worddocument'): @@ -2332,4 +2304,4 @@ For more information, see http://www.decalage.info/olefile ## except IOError as v: ## print("***", "cannot read", file, "-", v) -# this code was developed while listening to The Wedding Present "Sea Monsters" \ No newline at end of file +# this code was developed while listening to The Wedding Present "Sea Monsters" From 29a3de6ccc36eba8678dbc45176db7262cde8825 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 May 2015 00:16:35 +1000 Subject: [PATCH 13/42] Doc cleanup from wiredfool --- PIL/OleFileIO.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index 877599add..f51ac4bc0 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -1068,7 +1068,8 @@ class OleFileIO(object): OLE container object This class encapsulates the interface to an OLE 2 structured - storage file. Use the listdir and openstream methods to + storage file. Use the :py:meth:`~PIL.OleFileIO.OleFileIO.listdir` and + :py:meth:`~PIL.OleFileIO.OleFileIO.openstream` methods to access the contents of this file. Object names are given as a list of strings, one for each subentry @@ -2037,7 +2038,7 @@ class OleFileIO(object): :param filename: path of stream in storage tree (see openstream for syntax) :param convert_time: bool, if True timestamps will be converted to Python datetime :param no_conversion: None or list of int, timestamps not to be converted - (for example total editing time is not a real timestamp) + (for example total editing time is not a real timestamp) :returns: a dictionary of values indexed by id (integer) """ From cfedc8093c19b9b09c82b7b21c4396cbe442a8d4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 28 May 2015 21:04:10 +1000 Subject: [PATCH 14/42] Fixed typo --- PIL/OleFileIO.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index f51ac4bc0..f4cbc8fc6 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -1384,7 +1384,7 @@ class OleFileIO(object): # Load file allocation tables self.loadfat(header) - # Load direcory. This sets both the direntries list (ordered by sid) + # Load directory. This sets both the direntries list (ordered by sid) # and the root (ordered by hierarchy) members. self.loaddirectory(self.sectDirStart)#i32(header, 48)) self.ministream = None From a662a94316d31f719c7d8786febbf94bc3620dcf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 28 May 2015 23:11:51 +1000 Subject: [PATCH 15/42] Updated i16 and i32 to be in sync with _binary --- PIL/OleFileIO.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index f4cbc8fc6..4cf106d97 100755 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -477,23 +477,20 @@ def i16(c, o = 0): """ Converts a 2-bytes (16 bits) string to an integer. - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string + c: string containing bytes to convert + o: offset of bytes to convert in string """ - return i8(c[o]) | (i8(c[o+1])<<8) + return struct.unpack(" Date: Fri, 29 May 2015 14:59:54 +1000 Subject: [PATCH 16/42] Fixed various typos --- PIL/Image.py | 2 +- PIL/ImageCms.py | 12 ++++++------ PIL/JpegImagePlugin.py | 2 +- PIL/TiffImagePlugin.py | 6 +++--- Tests/test_image_resize.py | 2 +- Tests/test_imagedraw.py | 20 ++++++++++---------- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 274e7ee0e..1c95bfca0 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1810,7 +1810,7 @@ class Image(object): self.readonly = 0 self.pyaccess = None - # FIXME: the different tranform methods need further explanation + # FIXME: the different transform methods need further explanation # instead of bloating the method docs, add a separate chapter. def transform(self, size, method, data=None, resample=NEAREST, fill=1): """ diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index 6cd2f5d2d..ebf127df3 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -64,7 +64,7 @@ pyCMS 0.0.2 alpha Jan 6, 2002 - Added try/except statements arount type() checks of + Added try/except statements around type() checks of potential CObjects... Python won't let you use type() on them, and raises a TypeError (stupid, if you ask me!) @@ -123,8 +123,8 @@ FLAGS = { "NOTCACHE": 64, # Inhibit 1-pixel cache "NOTPRECALC": 256, "NULLTRANSFORM": 512, # Don't transform anyway - "HIGHRESPRECALC": 1024, # Use more memory to give better accurancy - "LOWRESPRECALC": 2048, # Use less memory to minimize resouces + "HIGHRESPRECALC": 1024, # Use more memory to give better accuracy + "LOWRESPRECALC": 2048, # Use less memory to minimize resources "WHITEBLACKCOMPENSATION": 8192, "BLACKPOINTCOMPENSATION": 8192, "GAMUTCHECK": 4096, # Out of Gamut alarm @@ -573,7 +573,7 @@ def applyTransform(im, transform, inPlace=0): This function applies a pre-calculated transform (from ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) to an image. The transform can be used for multiple images, saving - considerable calcuation time if doing the same conversion multiple times. + considerable calculation time if doing the same conversion multiple times. If you want to modify im in-place instead of receiving a new image as the return value, set inPlace to TRUE. This can only be done if @@ -858,7 +858,7 @@ def getDefaultIntent(profile): If an error occurs while trying to obtain the default intent, a PyCMSError is raised. - Use this function to determine the default (and usually best optomized) + Use this function to determine the default (and usually best optimized) rendering intent for this profile. Most profiles support multiple rendering intents, but are intended mostly for one type of conversion. If you wish to use a different intent than returned, use @@ -914,7 +914,7 @@ def isIntentSupported(profile, intent, direction): see the pyCMS documentation for details on rendering intents and what they do. - :param direction: Integer specifing if the profile is to be used for input, + :param direction: Integer specifying if the profile is to be used for input, output, or proof INPUT = 0 (or use ImageCms.DIRECTION_INPUT) diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index e15042504..5cae90073 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -4,7 +4,7 @@ # # JPEG (JFIF) file handling # -# See "Digital Compression and Coding of Continous-Tone Still Images, +# See "Digital Compression and Coding of Continuous-Tone Still Images, # Part 1, Requirements and Guidelines" (CCITT T.81 / ISO 10918-1) # # History: diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index ad085451b..f89c5b784 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -294,7 +294,7 @@ class ImageFileDirectory(collections.MutableMapping): def named(self): """ - Returns the complete tag dictionary, with named tags where posible. + Returns the complete tag dictionary, with named tags where possible. """ from PIL import TiffTags result = {} @@ -749,7 +749,7 @@ class TiffImageFile(ImageFile.ImageFile): # # Rearranging for supporting byteio items, since they have a fileno # that returns an IOError if there's no underlying fp. Easier to - # dea. with here by reordering. + # deal with here by reordering. if Image.DEBUG: print("have getvalue. just sending in a string from getvalue") n, err = decoder.decode(self.fp.getvalue()) @@ -985,7 +985,7 @@ class TiffImageFile(ImageFile.ImageFile): # Write TIFF files # little endian is default except for image modes with -# explict big endian byte-order +# explicit big endian byte-order SAVE_INFO = { # mode => rawmode, byteorder, photometrics, diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index 414758529..d59f8ba14 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -54,7 +54,7 @@ class TestImagingCoreResize(PillowTestCase): # Make an image with one colored pixel, in one channel. # When resized, that channel should be the same as a GS image. # Other channels should be unaffected. - # The R and A channels should not swap, which is indicitive of + # The R and A channels should not swap, which is indicative of # an endianness issues. samples = { diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 3b9919834..a1ed20a3a 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -300,32 +300,32 @@ class TestImageDraw(PillowTestCase): img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 5, 14, 5), BLACK, 2) self.assert_image_equal( - img, expected, 'line straigth horizontal normal 2px wide failed') + img, expected, 'line straight horizontal normal 2px wide failed') expected = Image.open(os.path.join(IMAGES_PATH, 'line_horizontal_w2px_inverted.png')) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((14, 5, 5, 5), BLACK, 2) self.assert_image_equal( - img, expected, 'line straigth horizontal inverted 2px wide failed') + img, expected, 'line straight horizontal inverted 2px wide failed') expected = Image.open(os.path.join(IMAGES_PATH, 'line_horizontal_w3px.png')) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 5, 14, 5), BLACK, 3) self.assert_image_equal( - img, expected, 'line straigth horizontal normal 3px wide failed') + img, expected, 'line straight horizontal normal 3px wide failed') img, draw = self.create_base_image_draw((20, 20)) draw.line((14, 5, 5, 5), BLACK, 3) self.assert_image_equal( - img, expected, 'line straigth horizontal inverted 3px wide failed') + img, expected, 'line straight horizontal inverted 3px wide failed') expected = Image.open(os.path.join(IMAGES_PATH, 'line_horizontal_w101px.png')) expected.load() img, draw = self.create_base_image_draw((200, 110)) draw.line((5, 55, 195, 55), BLACK, 101) self.assert_image_equal( - img, expected, 'line straigth horizontal 101px wide failed') + img, expected, 'line straight horizontal 101px wide failed') def test_line_h_s1_w2(self): self.skipTest('failing') @@ -344,32 +344,32 @@ class TestImageDraw(PillowTestCase): img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 5, 5, 14), BLACK, 2) self.assert_image_equal( - img, expected, 'line straigth vertical normal 2px wide failed') + img, expected, 'line straight vertical normal 2px wide failed') expected = Image.open(os.path.join(IMAGES_PATH, 'line_vertical_w2px_inverted.png')) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 14, 5, 5), BLACK, 2) self.assert_image_equal( - img, expected, 'line straigth vertical inverted 2px wide failed') + img, expected, 'line straight vertical inverted 2px wide failed') expected = Image.open(os.path.join(IMAGES_PATH, 'line_vertical_w3px.png')) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 5, 5, 14), BLACK, 3) self.assert_image_equal( - img, expected, 'line straigth vertical normal 3px wide failed') + img, expected, 'line straight vertical normal 3px wide failed') img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 14, 5, 5), BLACK, 3) self.assert_image_equal( - img, expected, 'line straigth vertical inverted 3px wide failed') + img, expected, 'line straight vertical inverted 3px wide failed') expected = Image.open(os.path.join(IMAGES_PATH, 'line_vertical_w101px.png')) expected.load() img, draw = self.create_base_image_draw((110, 200)) draw.line((55, 5, 55, 195), BLACK, 101) self.assert_image_equal(img, expected, - 'line straigth vertical 101px wide failed') + 'line straight vertical 101px wide failed') expected = Image.open(os.path.join(IMAGES_PATH, 'line_vertical_slope1px_w2px.png')) expected.load() From 6c12205aea963aa9f656048ab2959489713baf0d Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 29 May 2015 08:12:39 +0300 Subject: [PATCH 17/42] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 2bd4ff0a0..15ab14fb3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Upgrade olefile from 0.30 to 0.42b #1226 + [radarhere, decalage2] + - Setting transparency value to 0 when the tRNS contains only null byte(s) #1239 [juztin] From e58a773c29838e3a59a9245cb7113992dcce195e Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 29 May 2015 18:30:57 +0300 Subject: [PATCH 18/42] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 15ab14fb3..ae485811d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Ico files are little endian #1232 + [wiredfool] + - Upgrade olefile from 0.30 to 0.42b #1226 [radarhere, decalage2] From 9f79e4a32048863f7f0e2a10c13d2177d6a3754e Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 4 Jun 2015 13:08:59 +0300 Subject: [PATCH 19/42] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index ae485811d..9c8c04d1c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Add duration and loop set to GifImagePlugin #1172 + [radarhere] + - Ico files are little endian #1232 [wiredfool] From ed2cca1e7118b9cfc700c843ea8e05c8efd95b4b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 7 Jun 2015 00:09:54 +1000 Subject: [PATCH 20/42] Fixed Tiff handling of bad EXIF data --- PIL/TiffImagePlugin.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index f89c5b784..41bb26d42 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -426,6 +426,11 @@ class ImageFileDirectory(collections.MutableMapping): for i in range(i16(fp.read(2))): ifd = fp.read(12) + if len(ifd) != 12: + warnings.warn("Possibly corrupt EXIF data. " + "Expecting to read 12 bytes but only got %d." + % (len(ifd))) + continue tag, typ = i16(ifd), i16(ifd, 2) @@ -476,7 +481,14 @@ class ImageFileDirectory(collections.MutableMapping): else: print("- value:", self[tag]) - self.next = i32(fp.read(4)) + ifd = fp.read(4) + if len(ifd) != 4: + warnings.warn("Possibly corrupt EXIF data. " + "Expecting to read 4 bytes but only got %d." + % (len(ifd))) + return + + self.next = i32(ifd) # save primitives From 109ec638d9d251d29d0a05c3885dec083e6a923f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 7 Jun 2015 00:04:39 +1000 Subject: [PATCH 21/42] Added test for bad EXIF data --- Tests/images/hopper_bad_exif.jpg | Bin 0 -> 128826 bytes Tests/test_file_tiff.py | 8 ++++++++ 2 files changed, 8 insertions(+) create mode 100644 Tests/images/hopper_bad_exif.jpg diff --git a/Tests/images/hopper_bad_exif.jpg b/Tests/images/hopper_bad_exif.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4cfeea1dc473e941cae1d28dee5c7936c87a210b GIT binary patch literal 128826 zcmbTdd0bN4`!;N+vcf)DR$7|cWSx$pr*bIAQ)XsFnw0}MWafZ~WQt;nd{5<^TAEOr zlA5z|zwfueH}+_jOK-?XCKHtkW_=u**Fp6*s_zT zS_ckl8=N@#`>8)n&CD&%owu~VInQd(ACQCZ(WB2%c1P0byhUEMt&KK6cM43CU5$HpgE zQ~c?#-)6qg3g$#Vey*&F*CfBB8*CMOmEZoyHvgt<&F0O@7Hv`5)@@t=wQbwC{f}+` zuL1tY)c&>qRs46q!BO_P-3Ci#`@dadr=q~l7N!3FzaF)7(TW}5*bSBK77G;-`l&Sc zGVUzJ2bnU{gm4^aSf11>jly3q_;Sz)NP7wNA1O=rB6b1d`HRf)1>E-~xz4n2LY%$m zthWSQ#G2E}T}w3EQqQ~;-gW2QBg7fDYoa*DnwQ0$7KXF`dW$KFee$|n?L8vg+V{1c zrrnnspDhw!k#mnPU_)g}T`}CWj2D*>#CY-RWe-k>6fu$_K74C!rFI7VjfPr4;e3-X$>PzGE18<#s?j$(m?4);m}*dwMQ?e z7%@Qx=_7D}K{tm(6n^{Av40SKU7sYWx=fYn5z1ql6Dl#8ZY?s^{P5=6dbc@aETHW` z*oI1!XZ+AM+!Jb^o<-)Yl>(3>*0mQul!Q-tU;M+rp-*>R6a_WX@&4bGJit;V0iz+Cl{v!>^~&>Nr=muM#8qG*eKEIZ z)f91~#xh|=CScT@6^<#B&4w|=Q-8h@_m z^|-BdYolHa32^RISUJ}b1`n#ax)w3_FkmvJqSt1z*ocaQ*tS;qcm~52*2}dib`5uO zf4-OQPf!rHC7aTtZ|;G3!+fyU}DgcH4B_}sVBm*C5RsX!CA7EqPs&;(0zSAUy# z2>%SeIf3(T&{v)7Jqy97#5olSq^sPjvM%KDQIu8sWp8_z28@MEJp$CN#T+wdC1n?T zTG#4!2@88oCQw+3<5;JdSnU-YLTP@YIKdz_OwD5gr40Ug>kH9H|Ek{oxWZbtFWcMe zy>+h5o2Co}I{R{g&7C=IIbDWw9RiLQ-)yaazA|ux7|D)?#w5Nou2D@3+9N6`07f#8a=#8cd~MFisYcs(weK^@ zgK(l5$cW%Tl2eP#1?Uf!SXKH<$W#jGc7+ATxXM;l~Ow93engJFm` zouDtWa>V+LD(7k#yKNY>m(s_5wjI4q~ z&129Tc#k$3J1D}xFrrN0uI=`Csy+GQv?n;8-_J3#+3b7+Sv_wR&kK@QSyRTOn4XGC zYyiu5j`9`GnCkR%F&6QOD1HeH(CFL=m(~R z)hN1EU)3DR0$&%EH4K^6Vf0gwB_kvq4g8s4G+cImML=vjgzeC4KF&%kk*659h`U=e zA*nxLqz#pY7}MG_JytQ=R>?%{J4B7i+c96?q)z>S`YiCp&#_QDwP1H4q9}d_OZA1$ zK70Tj?+#?;_1+H+L#(D8t78BcSeS>p7spUST=u?*zp5&sZ1iYY8r)eq^LE@W=jtFX z_G~H}H~fVYdA=_Xt_AWYYM?^RW1Up7ND^h$XjDT)YW5gD84+^NukXwhpONys+iCc# zXR#eNs0euhKH5COptY?5*xhMNF6|474w0sd>#9Od>)geQLh9#OHZR`IS%wwzy>~$` z5TY%fz8&>d7-H4tEH6=aOm8Q*`ghsiFo$jSyxiE-IP&&UY=^^4KD>&^q2(ezO$h#! zFkyb?s|BpEV!f8qSR8h^dtt#tu}MeSuU*9gBQbEv(UF z#gNax$+&`yn?(XyP=xsmhung>ZPy^|lrTl1rUy};Gz>rLF{m*cEK1C*+%{@`yW%W+ zZcXn-oOfG>C=2384s=tihTKMI9Go?KVNxlx-r-6Y$i&w8Gj zF+w&~PgC(S*S_0h0ckG7k({tG4}uri4m3DyhA_NE!S0^5zzIqwCcOk=b;KX6LK0&p z=1r@uQzHHCl?CPwGhHjU2F5ylPth=9MW(LKMiZ{A0?dE(e}X#i5fr-&A!xvI80CJN zJct2W zB>oLUOt78H(8-Y_%jCpXgRH({Y*2{i+P$2xP-}ZWDE-8k$Do-Z;I55%*Z5n@Y~zYr zZwlVO%$%rK8yl4c+DCoo<6|0iSW*NIGgO{x9vc^C*M(KzP@ydCU$O?2hggopV|u9O z#c!0;lD%PwCAo{$@NwU|o#=Hgl69;t;qdj*!zBN50nRN-dT(vb1QD)RQPzF9<*A@HXtO*eRCuzz zC6O@-#5!)Me97tYO(`%DQIpBOny@u#O>tRkH<>rS(t^|!NWYqEYDVXNerDFi3*J!4 z6!?+bn;P0IG;}5cV^1N&zKmdv?81P7>@HujW2RY)cZ8$p4<9d&Pbkdhn|W@GrPt$7 zWG)N#tr1-u)T?sp6Hpp!GDKXjOkb z0XwI^p8qMIc!2}b&?A+8hbtj!ZKNM}dQ zcgnlQVEP!tXK1(fla?d8wmtzsPxtkP`NV8EIp)ejzB&CKOJ=`?4bD-E!&uZ>7YA zM08EDXbUhR;!bX=sXG%_Z~6ZC*}pC2e=kRf|1-2?idCDJQVPq)C+D;i#vZL6TuqO- zUO%y+vcvo}E`r_nrdvzwFY)03j|H{24nr4oR{E~+M~qgEz+!}Ah;t$e9I~O}$uL^f zy2V&cppB5CQfJgom1KgG^LGn&rvNzLsI>jlKBz_XmC!d4O>*FSjS79baG|w8*mI^qO-iAUKg1}WVjjW;ZvzWMhe2F zpdvhC%ElpRc5g@g{>H&-YhYP0D-L30=O?qxFJ*P%qHS+}Fw(oH*;np}5x+8C`HrW1 z-;`Z=Gx)ikeHm~-^0ATJugLVVSZEx1>T{8|W-J_DoR5FRm zTvX6w-i`+^Uo}I~nuhrV_vKXlj%m31mQJKuB0J65UxuXn>AR3wCr`Dpb7W+@v@mN{kaY{>4CcI0i}-fZ z)7NQhTOM+2SM!K=!OQGC5cJ9-=_C2TUA?o+BZH&GqnC4HN}yiQarK5zK?PDynMuVT zc_6yuO=@%A^}04%ZV&sWVz7AxNUSw6zWd4yl!wtX(U@)C6=!>_p-rZjU2G!8B}9~a zkN1_ow`oR)u?+1TV|Y|@Nd+NChqgHMV=}fSRcJO1yRs zqK4e|aZTHrXOuOkxrFl5d)**V-_PN#A6Y`wH~`)ag0NfY({Ml_6G`Hs;R_bhfX3WU z%BTRm5dF|XY*6i1^%ff%qJ24E+tVIE?`qB{g!!rFg|w-q$xqsPuG?EA$2mnz8QS=F zt7EGjiw@kp-a@N4hvc)py&>apb19OInztA>&~AW!Zl2VBvD2)Nu5%;Xbwo9J6Vc=; zISrbAm*==F##e(Y!T$X7B#z02M#e(I=rd{q!=Tx*zTwq+{+SEazzUh6zACsHRvUD9 z#J&XmQqt%6V-@;$84tdD&YgQO3lzXG6>5Kli~}D_ZiMQ);(l=Fpy|eoTti$+$$BJT@hL!~YhF{UZz zJ_z=`2Qx(~(Tt+EY0y+%C_r6=QMDJ-qUZzBv}1wbq4yS4u3OwMhBubBS2N z_J-l=g+zIpbHQ+z#vC_{T)~Bn z$s*JGqP6ka^1|v6J?C*F#Hvn=#?RY{EXH>*vd%p3OZ#8sJjc|Zmj>5$W6F9f1h$8- zkdoI=!^+Sev(i*ArxQR|o!I?JpU5Lik@J|n-Y?o-)z0sMz)w{|Lnzc_k2#2yk3$LC zP@m#mnfflHHP((Lwk0SSEW8lY0v*=7H_RG(t9>H_1Gp@@TrUXFPb8<` zzT2D)ebjT{(<~S!_N72WEFjoU$<6m-!mc;N)(*AU03M96oPsI1mpcY(K!gQ{nN}2A z<`-00ulbHgX=lM*eM2Ze)PQyu`?`G&G8U0z-9BfRX%AcJ9h|Gufe(2ERcmI1{azR% zv_J=nA6@9di!V``C>;2%!c@b{Vdq7z*~ZZS7v2>q3A8U6Jhrxy0rL-TU(TG>l5#gx zB#vZ&qwKQHW$E0HQweP{{S~M#(m9S z&f}srwV&j6;iGT(bPaMz)MPvW$p7*e^5mHqDllL}MdN!uXXQsIWRmdu_uAqOm3uPh zopW}&)~DXWo$j;RDyoB7$ix!IrVQqoXWM8fk+CSOHi?`U>6Vc2TD#?0iwD7zQB!%J zer}RGZ>T(?drZtxuz+>U8wWlh=mIlN)(4CSUgWNITp6P*tN-!&5Bhz)7DY_?S-!XP z-cSdF-EI2Q=PKcesr#UQeU=Zl6C7tqtA96QX16eKAh@}tyAAIjUg3ku1h=Fv&Rr2A zj^*-^C%6ZX2V|HdDz9wYFAr0U^7O24FRM>=N*=#&+cjc+lNb^oMtIw|pG18aK&ksv zt?WHkm+&e+o@1pJdt;~S`LNt`Ckh6yWvla3o7L6O&fPxe#}IMAFGL*|kt#WM29tN4 zAq;4x5k6hUIU8@k{T1`P*a~qo!PQwMSCQEsm?=5-y)#oK#9R+3k(DbUJdCJ}!7Ol6 z?-&0;Kb$eC`=`f{4HI7a`AcgCQ{3-Sb0lN0(O*u$M!AnjzoRxu&u9?eslB0+WGc%) zh1#)tA^6CiOGslb%=oLH9-W7ffjfLoec~vC5%=G&l;!9XBo~&g{vm8J6*^yEzBso{ z7UV)3S>3U!_G>AqxtT_5qQS~yJbsO-i3x`}SM(O)XvB15F7oIZ+BV^N+3)M;bKW;XUxqO}rtg73o|;h5WfS-oz%fr2t>h~;eT*P4wY$Un z504C{Q))%3HvZ0Ih_EeHM3@#E89(r~+V`$LJY#W7!zZIYz@^IUR{`MK@CShkFD#5V zVZ|pbv`@FfI}1(?o2hN60LDZ1c*3|qu=yO@aYQk^jrGQ(EXSHaim`g~?rnX{1IItM zJv$Pypx>a?Z6Y+;AKV5=yQ%2Cw7g#X70Ol)WWk3n^9Ku**0x-(RZph#qaHOYPM~yj z#q+u8$Ovt0)RYa)BQRvhM+A~_+4PdC{af@!zFRw$JZd(vNHh-JCXW2s8L6%NVAj6e z^9m|0wkeR>*oePLrqJGvspf_A(a4(OpK@FAcb8Fz+7u&WU?_4m-Ik%#1UZ;dRWc6@ z?Gb-6oCv#KaeUBwp09(xUXDu3`LS?Mv_&i4sCi_?*bW=CQ%da(FAdqv^TIT&oq11$ zG1puN%Au_TIapoV*~vLxU<++@0a=U#UI)#CE!-QJaqj*D5?7?;3Mn@Kt`6PaO?j_u z@+*OIATd!C);&lRVJk7Gq=eqo78`@Vbr$;ep`G{^CAXWVsd>Y;IOdDo_l=AovfWU5 z7QdmwTT%vh`sBq8m6TdBb3=tAuM+zBY^VqagaX?!d3L+dbwlMYwj=x=_M?^JbzAp_ zN*Fd!rmq0B(Z$GxT4|y@PiCR;)1S~6Ay-W4CvP`5#R4??`OtZt*u3P>(4u>-{v{WG z>cmC8-zdG_thTZy_4bTC(S4Lw9##f?qU5#EGt;2WiH z6`#l{{}_gMB_! zw$c;itIBQ_O`4aKvwYa30h^{oPkKgq?(3oC<9qW1ZKAl)iWfb23bINSt26&R%ds-w zjo1C>B(x@%vZfaPkhw6X;dR!&`u*{TA78nin|8>%nRT|->Pmm^C)!B?(WocOkBoh3};1A;y#8z-`dnat2LuCm5k2v{x z;PCYVs}w{@b6N+VVRJ&KNNSdta56bLlDFVo_n}k(i4#9$7Dl0Z*WF~~^rQ1WGKZO} zIG@EGZS*j6Vn&6*>P%VmmMF{Bf(j(x+sZ+8FbS++$#lhqB`|TcLTki$F_O7Fy{H&f ze3UuVWJc_y+&2ta+dIyK8mAuf)(T>q&PK*q8{@UTa>a4E?kPzr#KpqVFl~B`v6(D$ zI|345 zXn8BV1z8{55Y#BNt1lU>Ha*Y2R43Y%KxvpZ*)!Mt!@0)m;HoT^9;F-91$jxP=6;%J zn|Q0kasA=(1!UzP!m8l_ICuWO!zAmg!ISGiB^8*6N~K%2shGDl8$>LcBBC8zmgfXq z-3B-aVJe{DSn*Xc^7L?dPEb$~Om}*A%<{!gw%3Ft#6t( z;?;)n`Aj&r#2#tsksgA^cinW_XZfZeJkCFne_j~oW@1z7Cn@&%d->;-dlxp%Qka>j zIE0RH8CzNfTx5e1LZ^VcX)R9QQqKpTOVk}?JD-Ai@4x%V^T3mo32f2Q1W2*N;0y1G z?TH~e=m|;}W(mHMM`$zYI8+9@DB*2Nbf1bm zqxNf>0+7VF1E4#_1ks7sA`_{1L7_#`5_@2%%`-9(u`&g-lx0nzq60C{#n z?;5c5Q`xxPms~v?N%-;~Kc@Mo5q3VJu-39UrWQq?b+`YgGb41-bUx|F^y_YbFwrnA zt)dKoBvfq&CsAY_rmzDrxkONQ)OVaF(~=puiySx%Pb`g7$7daM{`8P`SdGbacxv6v%YsLmSp zmC6f>Pc*JEP1}wXuIB4sX>TUy@2V&-AyLE+AN?amtd^{H|uRwDl{yDf2; zl6BDX6U`s+W?*+K{mGaHr{^#_DAiLHXbgP%oby+f=cMBi^jAt_PE^vVqu7XCu&}?l z#B)vyr={(+<|YCI)7cltG;5D01_!`n0{_!{(8{CR=s9kifBjS@VUD-ecF(-4j~5qh z@0cM~aG@}i4(Q6ls)18&MtP6ne7Nq&8Nd~J)4kMN&rME0c-rvy;a{6S*wu+EO`)~> zBt}qyRG8k)XGxHb&$B^zFmQ4+Gvvu?zej)Au?C2;H9AW7nywbArV2=d2&-gjN8AK9T>bdgIBU_dbH-qiSOU{B-|G;A+y_bMbPO`Q+Rj=V+ih=LE+u{yU*Ny%nVSxYQo_aqeJ<2Cs< zJvKf&gbJz zd78)e+PbHU6~KJ8t0Z_R#W9KP+(c;z%+fyrx3N8_^H0=`+-tc+vC>gSBa!>G+(c(# zI6R;nnwByGaXbVPWP4LmB8{{n?b|qnoVTQ3$M(e*jd}g&_q;co! zB;nQGAvo=L{M0EF01DEMPOo-&G{L=QX7zF!4@OVt_VwoWU+IM1@1l&A^+d6bWXxfX zp0e7cryRvQcGW!y1NK+hbkW~>szu2VB`w|@>(dAdnlgWNc~zWOi7xu(>uBHh9Xzr?vU-1%O?e!l0R`rNQ)<1wyp}|Fb_|>kYU-fo1X@CmYW4FN< z*B$Ria$LcFG^dgf+hggM6Bf%5un505@RV>J&ZK!x*bnx#$TVGB?m3Q2HU{ zvHRH%C-6bV5CLUvE*JCm89KY8IWcKnDT3?jbCvfrY+J{8V=I2p2rGSrmArxaInl3& ztu-YmMNS1Y#y3zF@O{GC9>&+-Ex}%7ePYWDeTGl8QyJeHmFXG~!h9-_s!d|$DkJTh z^snr0e=Emx)ITRZ+0agm0fhjp$tz0-IX5t92j3W*8s__VHg9<+&w^rXS%8Qe!O9pD zGJ0@TXCB_?eZ6;)xatjcZfZ>fYbO*^>*`0BYF4Xa7QTqQyGsd(!qO0JBC%mUevA2X zh+^;1;$6{xaYMOT*Jr<13ucB&e1|akGJ1qE`V=4G98oyQ;(2?`LGbHGhkSkPYs7k* z%O5`IYt6b*?bM-N+TxUe87QRh&HT-St|z0KQo$bM=b&v&9T&w8^h}+lGrIjg1$u7>9eIl3t*SLng%#}1a|1mq*h!0;TsxWUXXu&1SoFf z_y_v#Sl88fVK4BgAJ+HU`W~ExI6lP&617}Lt=(1MG)G9{4LK%P2NxpG`?|FH2L%fP zkch97&ik%DvrO3po5_Gygc;vc+z6}4o&LF5+|+yVKee<9Uz@xn9?wI3q+DTqJJ#rV%vVVSDi6A=<{f=oGQBNv zg}B#wEYyr+>uu$kbn6ms!stZqZ>8sIzIvbVz6dR$QW@E9P9*W@3DVDFyl<8mWlneN zi#*2`5w)ZBD(k09>j+9;ea`Z6TLrAlgX>u4!_7<6i2-JD$J~<^I-LAGmiV`B|8)!) zEq@~vZCj|2?Tfo7uU^&punfkFv588V0Ez8`&RY3Hz?7y=^o7Gi|7oV1jL{#Ye%Z$@P^Zw-NE7W=`@VG`GCHQAQ>g_xzaq2B zbMoyUah<=d#Te)4L?xR1q1gbHWbrNXKGy}M`t3w+;{&kp_PU^TgByxZgq=olLGl8W zjYX%J<%ck#!!5|eeGr!EgT9=P3op znd|4vc)sIvn>`ZBo><%8zC=j{IuzP9VJ>oYuH2>4BH?-H7aw*eXUy?l+8tEvT6dJd zgNj&_PURrP8<;dKyAX}<{1llNQ+teR7m)aUL4#g35hHD-uiM=jE>5^Rv{*D8sq@jq z_yhxm)vr_ZV*!#pAA-!9M+ES0M zXdBNB&0S0WC28k71t~9~p~7IkTrll2EXKa+t~il~#D-7Z#6)O`QI*iHI*J*<(le5M zAZupHp#`pMsaC5>__1e0rHn_}P+@;(#VhmAk9|quXu!(Xk#ZDvfa-O>05VM96+~@GHK+fFlyyo-@~Flw$#&o_ z0Ipfv@|F|Sep|anp*6i5S--lrXHHwUGo<3U#aZZaNGt73Qm%iwwS}H{YbW}VpTTGG z!yMA{lHoaRpQ>%?W>#mc40A@q%;dHT{*OgyY|Nrj4esD7)NouI-bXi|Sh#E1^{{Z{ z1mn>xl~q^(h=<}DNBo#eE4mLClsc-=$SSM8mb92VWNHfZCW(r$ot*ruXz~0}I4m1P zPLFH>TMGTDqtn5flirY4YEt#O9p7fNo$QaFe+@U3N|5}jj^0=Ma`^_a~$<~%QNDd{QP#RoSqMpPYs19!S!H1VgiiJJj zT^WmZ4Py53Gr`RZ0}+RYN(u;vSYE!Y2eTi!dZ~+iQMKv8Rp;Ho!CKmyIr0+N7fGCL zq|!AMpb@L*yp`FozWnBV;)o#}HHj?<^7GCs9GGEtKn2x zQ-z$$i;WEr1TLP~la`p~I!yjOSNCYg{Pkg+hIkCu@53B(eSC>Z8%6?!)xd*c`>l5% z32$u^DknISq8=r^E4{_lw@hwRa>VHdrZ#_YzilOFIUj>KAomxC+&n>mPptxwdhay$ z?z1d=wD%$ydNt`bH5c9&IZ_K;G*%AV+gf{}ERtpQj}H&8i!-zulU$!CC8ri&&28B2 z9e?<>tjc+TZG&_jS|rIY&E>87E}S!42p0DOX)xp=Tr@DYG?*KhvVZ;0oYZ-+qALls zhZ9nu`?zZ?BG|DO>K#G_9Aep!M_XIlO5!35&FVaBa^W9QJ4!7c^e}up1z$Vl#-@}K{5E3&0)y+;;Bs4O z@aQsVH9&{OQB%SJbY`biHKDyP$Rg2rLnS7P^@L$x_CeN|eH|X_t?|3QlhwqchQN41 zFMMj%F*P{y^lzr>UqJb4h5$DOONpb*EpvLR5x}vVua;0z?PE2IGr6aV75#GQK>6i= zW(Q#cO=5JVPDSL$EOD&+@`sd_;hULXu6n&C>|HcAHeN&sYlyaNr8QwGY2ZT(>_e7}|och z2<`U~Kej5J-o6{YO}ypHy2#tZaRJ2HA*dJFpU9rOe31eOO%7Fk-SgEqW%`ly+)lw3 zF{2wue~QP|d;Yz3u~ei8jvr^{*#(%%1{`q&?v9H`zDx0koQTp_1x=|f*ng>g z#|lw0xiq*^?ssYL1gj5KMAKyXkO3j8dytW+^R>o{2C<1h6GodSUT&y3w`r~emruS& z{stoeY!3pJV0slPkG! ztrX;;wKa{&PdMD*1iR{U_qYQx?D_khcL=z1&yP8D6jlC56@$IezN{1+>aBxBzW*Sc zFNnGe-bJRX@>zDf=5lk|-iX)4;P22Eq;^_s!4Cfh^CI>FE{Zt_F$!_Qd48Rc^8OtH zy;|%0zTc9R@zq&??_F)?LuTr`B-mS%(i5*D{7|s{H1RX`gip#z+$V-IkL6;B3>&jB zf52n^y3E>MTXQ(^H@kC&QlgTFy1DL45n=y%GgADMn0UsgRE4^r2%t2 zw9GA}>6~v*pZA6s1b=zN1ABxC)9aIBiZfSjQ-81hsGrVNr7i1s{|bw{B25y9HI#d! zpA$(svUFp*k`N))N(VkY31uuz<2+5}Mj^I)zpq0p z=6#Mg0m}?}@QB#rUWCC?B9O$!9J@ZeFhzX@X1)UG95ih*g7hmnpen%%EOI=u3JA{q z1lI2!4w|%0m?B2$fI!OR&RqPtA1K3_QIi5?cR7zNY0Lp#QBR!2LgQ&i@3Y{x*3reI zBf?0|#eo}nw_2G>a?bfS@aDzk(Snqzzx~=eZ%mZ5mZdKEx-<;)v$#&i$6EG&h~7m& zmf9mjo|`ww%QSPj`pl!PmVFZVWrwrMg;$DVvE2csE_xJ*0@%xpdDrWjWKcmAMKd1J zpTAE_Y))a~q=8SHlf+N7L@;r#TS*)aZ+Sg?s!I6RTuD(dzCuH_mnE4Uxr>FRtdvqE+i6$m;UR%v z9bQ_%4o1TAx-w^|Nd=;5ZDKXG{yj=FQ=VOT{65cvtE60W@^eZ?adm#gSxqLM`N)Hd z*1!0Uyu9ukntfHa>$yo1ZBMzE8OeQ*b+m!j^(Jp)VU1~3)BdB6eu#M<4WlN0WY9S? z4n%#ww{~k+oeu;&^)c>3c(LcS%$3ohYpi2cyNl-qh0>34-lGhj;KbVM+(###fADG-{k;lr%Ov4{#!fFN+*qY-hd$ArCfbAXVo{_*fJW1f}#(% z*XKgJVuYn2VZ8I+S#80FioU++%rF*Ptz`K0sJ`^k6Go^Pjc|BN8p3K}nGKGJEk`Hacx9SUAe|V}kpB3k zAq8F)tiXv67E$>pNGYsyA>$?94v1Fj*YM;umx(Jk5fN!}2(7 zZ%>j(-PF*;Ftc)`&s6QVG}p&BA4zrCI>-Ydx$pmf_03{jZepbwH1=agUAbp#Yd71w zv)pVn5qBgQxb@Bero9Z}_2MXY-XTT?Ea3b)Oku2``h3Mx^2u`f4U#V_r> z&hVom96wPvR4yd|jp9d(edL8GDA7OsPT z0f`K&pA4Y`daBB~pN3)7zb?#he7q)TSuqjLF@=sc-1c&K)K5rW6`Qbqj-|6?Vi!~9 zJEyr8?Q5zf1+R8#r}jmEa)!kQ8ORxvS6Jo2Wsg9Ddzees$Vy73de>&f1>VXxv571~ zW--2@vV2>NY`_L!M=Tb26YW2@D?VZHjC4i z&FhLJ&YfDS!K6$L*hMZ6(wuROa&W4#nM%9D&+-Uz4Xpth<4(QF8QZEa4J&GYtuF!k z_E^z__;!yJsuLf|p&n?YbGC0AOq;J}C8J;a<@W7J*6&kHDw6YbfW9B6;O%)d4)BEZI~B3>kW&=i0mFZ{ekMBmCN$JAN1Q^B5kF z0CaN%1%b(iZ?j^QYRn;-#(KEC6qUAlimvX$paCPk4xCIuh6aKyL8r^#`=OaPKOQBw zG?F|6@`>GDp#sF2!ZW{LwhL~b*xc>gK+QL9uZ$UFBNC_k>t4iIrhj-IjwtPoxWh`! z*L)*IeQeHRK|gYA2O=8iR7(uA@WV0B+G!~BZo&( z2fh!6t{8SP&A^r&;H&1KfT-BW=F7747VgiRv+v{G!4bf310VOfk13UlTWg;14&50g z%lEpB`7t7Yg)w52z!jz~+` zKLNQTdHFHSF4p&VC1c~jTXx9Qn`Et@o^c@1&NH4Ia9MXwYjEwo zmPD)aYWBhpy5H8aS6iN|^@bT_aO@(=i&71LO({n(3*NlY_Q$eGVwie%5gRs-4KxlN zQjEof14rC_{aUoKue2uo6M4dAN-@QkG+xhoy!R7!JN(_UdzXoRoY`o=So|Rg-eb9@ z#HIxTiYf9PDD!d(TgVtPr7Y=?LPf>tKamfYe%{zCVu;a#$t*gqb6`SK3n#{gs zYL|GenU-s<&0!|lEuug=F@aee9RJX>gD7_1QImy_ z-s6>~hTSWWzA6pQg1HTpeuz-A?p|J87M-snn**iBZbFYvb(!Rh-T^TGIuAD4f72J|M1}ixrEYM-YG9ep}zN4 z!OU5q!hrJL@Txg;s`<>v5X0!&g^XbritldEdF}qRe+4>sGI-9zCM)RG{Y+!Ce6QKQ z=%}zbd$XtJLWIGvgI>#)$G0u5Lfn3INnKV4>$~$$^`upOAw~~fn6Rd+CtbS!?h&Im?o=_Ca~*pJlYL`D z<;jx}NJ4r(vrtYoPWk?;U{C9?-hk$YipX%}1;jp#i5t_54|`Q!lVg$u#5VYiEJl7E zTjf|?JhyHi@PNX)JNwfK6CPX6?e&k1HW+PB-TD78_U`da_woOK*VWNc{rx_- z@AvoL?{@PKx0~AydwcEqdK~Ug101^Q{H7Fe^{B!RPL}O$;sn zz-^bU7k&le$od-nF29a%JMt zTTPAn?x335&!gmg4Ocozu17C;vqr#N`$-CsZEc-P2rk=!!Sgym-K#?ZppAK3aJ@;! z#cu2YU4{JXAg^8nn<@dzDH~nwA$q^Y`EXTB*YGD@=y(BHY((#L`ms1SbGN|SznWB4 z?O#o%CJSr|G|D~P+gA6->vlQSHMY`8I)m3GGdJ~^Yb=(ne@#tYeE+S-W&vno_oJRS zNBW3eA%C5wKXRG?+B@7oDM~u=|9r+8qMcZZ4xRh}5S37zZ@|LT+akmFjxY=EZ9NJ& ztJV^s^0_)FuY5fM2fN0w%jD2m{Vms~Q}2w-jQXQ9*&WVcp`~F8il`pw&#*m{>r#fFu*M>+0>Mu$9-k=fNnMI7Gvkr}>w~yMhc5Ft`|Fr$d4p{< zR7%9Ww8o5TWd%zRXm-)69A?GaI|8Z3oXtI+>xqy?^bpO1Gl@ zguq5kc}|%Fr8aXPS+Ta7EF*?hk(DnqW5PFM!aM->c zFn_^O^A*y;NS*g-?rhngLf@c3HOf@;IQxXodI1Na2hzJ{w7clnuG7m~PsDONjXQY@ z(V~lD{fYLvc7XzrcjW$j7VRC~)Bas&WGqN~fh5Zv$59bpcQ7&ti-`NVZTTvbVMkE< zkW#+@z*FGtK_Ht*A)HLX!xlp#vZj`?_w5w}Yfgtn zw4UjTD<(GJJHS0?Pjx`$_Ao^6I0Lt64ALy{-#qTp^GCkU3TEt1+ErnfW-Z8=h)0!W zRCi}-9v2nEGXqjq})j#yj0k!^Tq|^@0UI{;;?_>1OoU{KUDrOlRxiC!apN z&Dg<9I+dlO&TdQZyfQkwQpwB47kUwW+aF~OFqUnFDkJ{h?Guk9UlphEPHMSJ$;ve( z{=9zUb?l-?@iuB@^B+@LayeN$kt096?O4|Fm=@qVAAHzPZ*@|oNwe~Jr*(=-Nm|k0 zlgD2@-1F`2C%FU!G!L zp*g${Jb{2~i^_MxQe0=#pv{R|+MuyD3z%Hf?MedN#oZ4a0;X;OHuLy^j;%ZbUPWh< zYwCBYKDRqq3wA5d)W5alw0omnBBdN6Tu#a*#G#iWO^YWvKL@}?YJIPHPq8MiqaaH` zl?wCIW%|M=76EPipn>^@xS8w2d4_pOr+=CXm(y>?&XfpqvAyZo00Mi84|~~U+r%=; z#b>-1$YV#wKI@F-#H`Ihy&kU#BUVymwGq_{?%Wg|3=R5Rt-_V z3Q>h7P@7YqFB&)fo!TcEGOgKM4ESP~B00(2HeVtAOGcaV^tW1jx&vHrv3F^d0_$p> zr(syHfX5&Is&5C=e;&ETDy^`f$O$>^;s=th=#wTFw*zWN+5yvLw?Mu+ze0r5-Jglm zjWj8{qW}8TrgLtdmD;Bwoe=AshYl@SumYiU{~RsmGYj-Zmn1q%T68XPvlM6Uqz8yru=o9<}|b^f1wM zktjafSL7i|ZPN}lVjZWbAMzZAo2Z7aAE_x=`SaA;P1kCnB=lKeZ(;%MPLw za@Y-5un1CfXjU0YKKp`;MwhmPOubhg8|##)y!n>re4xJq+aZJK9pK}NHCQ*gfVtM5il>(RsylL(h) z9Q6~&+DrZ^+13(a^65_QONJGugdG}h8}Ut=B)ZvR6Gjx_e~7Px>}N(|r<}nJdZ)#k7t;9`2(exDpK?^#H^drdMw0#YpOdszBupl@5@c>Y6105SFW1}I zATG7H(5T5LJ*$Gv84AED5_jTyMtJj%2DrI;&?vD9J};-D(dci4@+tIDs?;6ascoRO z3}ZFoni%Wz@ZiJjynpXg#X4Z-4?lxP%xIrx3Z=$n0aNYNE8yOeGhk-ur4T}^{!{J_ zP3Y_(wDuJS1fWadNG6kUYq^qpD_6Jb+->i)sbW;<(t>xI;vuw+*VC$E;7UZ)B^~gc z144Rw*39G7`kT5j%$>~%H7aeH2eeSch zpZx9Ykdq1a^|W}53|t^zn&)Eoe3ya>bNUQpkB_tc^yJ|8`kM3@GgQ4Tf{^dJNG^Y^ zrDS}>D8SoFsZyyjfCm+)MEFJx`BxktZ2qqdcZAF$PUeI-e54)eQ|xTEIhETDd05GB zs;X}s(bV2X-*@kS&z>B&nbwGis64qO&m2Q{sV&y}`s7(R9_Ig%s9L{I`A~-;(phEp zT)~)FmKCb-&yR_30zCCGAd#UCe=lS|P&Da`7+UyBYp~U@8a3%b6D@OOZIqHty2>;m zMsydmcea2Y{m@3eLM{QbV01-2KD!K#Y^turKH5C&XHeQwaG_wL`az*%*1aju{o^wX z`^Am(x;E8BuXsD|twZg;csV7NmQ@A*R(u>Vte#Qc!ih0EZ8*YWp-5M3$BL#z3)F;# zi`qX^e(gg2+ST)q>CF6&31i35A?I5yDt(=8kJa9C%wMqNE;c2O>?@A&^=zwcFIa&2 z)&v{$~_X5 zf}ln@11>99>I-1(JY#7mQxpx>hB_|+2S3Nx)O)0jDST3LxV3e*EgMa^iT+yGY)77| zI^5&qRW>2GK4Y6~s|QC|{YhEDzlyH@wd>Y!G&W3oJN65Chx4l7;g)UA#55yPOr8ia zN!r^!uB|>%Y7PWIxxORlZN*B#oEY%4lXzSK*lG3Gu2S<#WX>2kFl7VyVxo3MlKgKN zNLo6L$qkXS7B3?-W zS?#?tn*`-&elWhEU1IMAT6f|c_fBy@Rh}VOycgw}cDEm1)nG zT4j225Dt8W^9jY*>C#gLu9b(c?u zl7wz6>B%OiZFwn+A@^uDOS-1Ve^cx&NjYZU5r8_-+;rjLR~3t6)u%HziX(oW;RewM z06}QQKAm0P%dL~!K9eic6)nTxDx>f)RChPTJe5Wwm_0<+T{@<|kD7)`Qq2jBYlQeG zwt2fWB=vnsUkm}zAp^qtQS>2N37ikJ%ns0D1zrB2O`5jJwb`To)dKUo)sgHO2Q&(; z)_uWWyI>9OqVDn12vUsXoE}35rNR-6?JLNDxv<=_3dOI~VDXVRVJV*Iu%M8-XC7Mn zf4%~Rb-k+Q-n{K8X5SiqS}dC^MAJR?kj%hXM} z;HYaIz};hV?+=y6B%dG0aP1b6XMoZ1peTFw+qB(3sPq%thwFxgE^gAPAub~nkA?YE z70y8$v~h{8Zk6*BV^(j$jjWsXweSaZEonRpyO|HkpXopk)?@V1It27-#(spNkVw>A zSX4nSY5AY~E5qWL^~tZ9R)4*JFMZ|kF4eKk#uC1P)6-Jd@37_C8#YL-AzD17r_BsOGMbdL4!0_F(z4InYCGr+C>fH99E1wE0JOtr!BmBMD z?~paRslr#x5WUR`|Laqe#_O8~$pS;Kc4&N@(_{R1nHibwV`68K;+NLofiHz!DBC|L zS;(z`Cfe_c1`v4<8I|uWpVq#u%YO1@K_Y(y{2p@F8j!_<#N_&dQ2%;>-;#FO3Mqzi z-AdbP)q0reCT|`v&eft^h_g0YZhpam-J6A8VUYV15hzs@CKy7To1~Rfe*D_CeJot& zq=ndip2$NDq)eTG%+#!qbL1085{Gq=59l#r?JQVHj73Xblbli{KL9e6XS4zz(C92Y zU=NdjKz#q5rX5zyadUCA0a%u6?38`dj*plIPf_YXo zCT#mB%;wSoR>F4~Kvn9v9i)A1`__tSeAFQ~{#t0%{6cto(%bwup$Py#%BtEL@(mu* z3i`xhsi7bi%UYwwuRhh8gw0ZE#3{>LbTfoB=V7rITy|s6b=3$cC4;d*> z!@2UJ1iqUKI{uF${YRMcOM49Wo_Azt$7R&TFiN^s`+5*F0ra%8oV-bEnY zxZ==0VS0Tvr*i-uF|GY{U5q(4DT={aN}@rbkPStYQ%eNAVl@O;wi~;*WkJn33{vfc z;-ifwATzR`Oi!7BBAs>sKh!flDe5qTB5w8iCtNPE8lsyD?SlQ;!kL- zMZqT)N?szDk2I$J%RO*R=i>M8s*-fdvgo|_LeAT5qdXooRKO8*f1iOf7G$LsDYn3^ z&42wXTpHR<%M6oW>kWOi3w-#UE9?A8AJceKs`mD=Ko?OfuM%hVWvaKR`1=}i`FKsP z*q&CvkY%;D{i- zu6Uixn+{Yty(4~70czC7$ILDVJPVb7+PM4j)CUaZa&GDKOO(`EPLWYgAjgvRC}(qx zTH#~7Ai4Hk7je$fz$sg|C%-$yV75Et8TZDR`dp`DdwqLN=4gD;WkE`%pAP^gbq~bB2%fw*g&3#RIiOXFeQWOsjaP*WnPVQE5a*}?iKa7khiM_gVQNp4h#roz?-hK*+63q_E0|M2b8)EdR7*9UDkSY zrfIkUicLj-{XCaus9-aLzNu&eRcofdTZvDpl8?$Ng=m{Kt%{bX1VGk$E7`RAzol9@3Xu71`GQlUDMXc_TP@6W z(1Ya#*6q|*_Y=0Qv$-6Yn-voHNrupY=i(vQ6pw?}op2j;=RZKoWHSpEs(t{im1woC z!s;oY@Rkn=e0-Q;g?kE=I+DGK&X@l5)KnolFvFSK*XXq_Faxc^5v|p33eP$?La&-)Q zw#xm65~GVNqn3PPl;+9kl}FU~g&Ec(1d!tXkaunZeL4ML=YxR7s{J2VZ+f%(p5H$4=^gyfUasJKP7FT(di- zK3w3MkDvI^?G^eZQ&|upbliDICb6=n6$^WaN}XIAT605$6(j2oTwH0G;yWXE1o15S z(w>+nbH`^($$Fay&mun_!6~}|ArK=-QRuhMrlFV?2b6G1u3H|*%qeGwrOOpQj5um; z6y<#@Q(ZKb^W<{PC$s(g+LKLLAz$2^#Rl8n%(j#Bu;3y>A8OW(|Lx8NZI<+E*E3014*ZyCtu zq>QT+rQfR0<%N2+R0j_O=6{dRP(=N+7H*3%21kqj;!a=bGy99 z+9jXbsr&nsCnCg8Oyin*2W&BR&1CVuw+uHvGbAVKPYHuo_ak#ea0Jq%`eg7}W&Qc@ zf&lmF8Ew%;%wj1|jK0W}ISCixbmno!>IQmwsNLKtF0SvMmk)<)V1DRnDJjTPy|KrQV#Y4X)_qaSQ*{Qxzcw z)+cQ;ubn*c(h$|Rj72-!Q)}u+w6(N|>&?OB+GHf`28D(!ZSXB}Qnfl+fb_Z8;Gmi& zn*4Yh7bNGdx$3w$M=R%`4B~pYN&hKRLy!@Y7GciKD)iT`M^AdH^$`}Q7U#ep%j=Y1 zn<-mupWC6CBBNu3jE*mbCnhmyCCDK<+EQo4z4L<}a-p?8GhTc!7De*pdY3rrx z!Il7<0M&$f&~AzJaj_%6ZO=%H2C-{;#|xHcSHQJfy)vGHvt+FyNAUs|6;DsojNdY^ zz5~|+$?}Ufk)JcI^npMgxc$ppWwKnK9f%fhqUI;L-$599_)zb~3w|ShO+y+efW5G| z*#8a2MilA&+U4^y8j0vu6CX^!&;1;-P~j!k+qM@Wr!~-5dhx((pY?r3g!ct#-7~z! zV*+{HAmyX8bM77k$FFp_O|{2zbJO$59jXu&|H93nPPBg!3S3!pM99_O+1;jxO=R5M ze3j1GD%YI@D_U4s2sHMbP)h`l9! z9K*3lX*6%>a46KBp@+;ZNUtD=XIDi(jA0#6gpiyB!&DZGZG{e$eZ$g39gP3|w;kmb znQbt%$3E1#`-zA%WS8X#^kR!wHnR9nS~eZ3taF1ln^GxX^)~hQP1oc0!2DnPqnWOY z-)x)|q}p{%{R&y>-KJz}<@9RhPSHZ-Sxg`SndRn&$bwXOr=1h)4$l6Ju{j-cK}#N0 z$!_#_cYrK>mUcl$-FcV1Q!TRRB?8vZH?!o;Zas-g92)4}vb`H46e`6UNiF(&{_xcn zLQ}|iJt)o+Dy=*G&mp(%S;_Y52y7RbH$m*}xl+BHmy z8rCcQ3NsN~>ozW#a33DpaYEmmu?Ogn0W*qctv@q#2J)ZGuBVzUE^h3`1O}E*`qBOv zM;>_5IbWVsP07ie_J$|v_Lrn>(SdSt!kx?w{|%QGk9YGYw_chYA8XNg&*!Mr1EX@H z=VhE`X8bJ2^20Y3(He3CFW_)CPLB z5gf2&QR^9@m#Q4k}8mxryQ6y2|##pX(&Uc{EVoBYa3OD~M_K;i#C zsq^buAkN&5{j^YWTg?$w9@Q?mK*E zIpGSllui8HoCB?miMXosiui!xuT|34fRRbnitv4;GoOM=*;n6MGqOLP+%A*rlRA{R zKIsa}F9iH#;2XR3pEioeQI{EafUeC^ACYG`X0oJRuy!XY zy%mpO$R^VStO5!n&xb`PW!)QrY*gCr>sgIw|=Ik{lP#Ff% zz5^iasmje49~^F!J7RDyVJ;rzVGoR!78D={ z+r@Ntf5Ne?gm;v(VLJG;H%Ev-o)5Kiaprbyo`?3>%|<$ziO}M3essfnGv0YvP2+4H zH0QZy`~^6g4=Y?IuS}lUfG52TV|1s?Q7cRnW>$}F>iw`Kd}m1!CIsQf3QpFo^uGOp zV#U{fNxl5TwO|sC;KKqru*LXJ!BPjgkm@1`h8gtuqlr}%DAD{nHKB7;ZZYIc$-KNt zHbJ411QeKUYD|lox$N?qL{c&BBxH#tuitAT#$0xE1>ETt=5(Q=x$yNmTm z&LD3*Zw!!-V}nhuk%u>sZAW@me^&&5yYtN2L`k5uf8_p zo4JM1V-7&kAjD-j3L2=Iq5U7#Z+_h9(kcT8u11|gDNApk0WSRBeD^crM=%E?>IVGp&z zsggA{aaHvB{}{*%2ysxPx5yqq<+&djYq1~*Bw&o_1xs1(;t)6$75V71#lA|_By1^v zwu)H%s&WvCI*cl;ne3Of zwbi*|B>U$NDM#MK?>_@Av*lxRSJBpAzQf%}Q0K-D-t-%$-Rz4N!6$Vn&tjpHY zmzGZdC{DJdH9W8SZ2fPW_wBTOC!B#3bmf-Dyzv!b+VC=FcD(Bj;|t?HgDl3GyXQGH zhe3cGuBUEWLDM7Jn%~&IYv}v+mYQeV41!jbSL^}8o!_Nxmz?gVm#oN~U}c|`(NBvv zKPe)CaO|R2O&WYk2>_mP*DD@XYcFnS%mcpynLm~ro)_m0;=cLcy{P|Y!2dn~bmCr9 zUytLFr^HULzXBOx^XcZo%iknN7O(Rh%Cz1^lGd{@_-8w1C!#vaK~I;~D^r@aFP{mR z>)QnG1Eycd(PSoRryee-q1ZMG#Y^1leZwfR+6I%HO;G zsRo|qckRIR;k9`5X5l|34sPtJd0CXKpYp_G58^)t=yh47lp3edYqy(Cm|B<3LnOFp zr}6=sq*Y{~vuRL}|F9ZYh$``gNt)mJ8BB4CR4mF>zx8WZjJx#&XUdKl1j%XZ)eB!! zuHMH_Z77tJ$UE*^=WRI^M1J_Uq;*v~)`?DB+K4brD7@RyKz`tx3yr8u!~@$P&d;>U z!AjOZ4tHB<8U8`@Wj<|C_%N2Iuo&GXIrEu8p8Crd)<&t|!=g$xqaQr;JG7NTi^rA* zo@Zj!3uLJL+(?7>f~OmQM#MO@rgDUqOmO z?~H}@esocK=VmPm9B$i|Du=4VKYKoEuk{6kr1=UwrxMkvY3g`fU_q>>jQbwG7w3b@P}`>ur^%i;4;~%gV5C? z>WHSvEz}Gyw6?yM&XMq3pBa!}i2UJus0~2s60LjH!`yO`B}zP6G|Ei7k=V(-+~ z{yhaB2q?0-`a^S=sI9r+O-R>u}Go0G6hY_+ZW*e}N*d$ju!}P&`V3i9p%MMSHAxlLe+N$Dtx%O{P|G3n>B?%jv4@~LHx-&Lx)8UH>hD) zqKDOrSl}$DACP`B5y~T< zUFy_+)rV7zUmqd0p90Vxn=%v4ow?3wZF(Yj$5!ua3=znI9EGXpKe?wi)s0_E3H=*~ z>gp&76CPlQZ%-B2zJKSWB4t{`WrfTz^ToQ`3d}Z$pPn+Sa3boi*#qEmn=JJ$yCwsQ zedrS{wA0^rwX{HurVl!H7YfXLQ3oRB0iB|KJE&$gCqXAYDVQUNk?}L=%`CVurcNGx z*a}wzYHqa09mmCRcE2$6R<8o|#^0aZEoj+B1bcQh?D3Vo*}SpkUt2 zUFQ0fmj#`?mRm9mH&^SH3qCq%+#PcLH_aE5vMP;NZa*IkIqx1Tks8!{8|`PhZ^E}p zJw2ja(aWw;!f0<9T_ml~pD$fowk$l99rjyOCQmAP;FNIlvF1hkWR=q;&doMJ1=?$Y zIm{6abTpAtw1E9te*w7>$DPHzDK0FJcp3(?I~ZQYvV4tu*c_wct)SFDkl}|)10p<4a?qY=NJ~igyPGUW%JV4Mk*bHaIw?cP{q#bjl!ups zb}oMSwacJ85S1hQT?iI7*|rV}Uj&J{G}T~^a((6!JmqTDw`OvR9Yfb}pfL@#kTh+yU@#+2vjxs!MN zxey;b@ZMz-nU#Lf#nob3h_n&ICAmjxL{*97S+{NLQ_F-4*G( ztp2uAU0dN1-D$5!dp{pigu&vD`Rx;|N#%A=B84nfJZ+YHXC!y&0~0jTi5IIfif0xaIs-kQ=Nl|nj)QPD44Uo zYk-0ZFbA8oEp%v5y{}*I=`$?1*>7sH!CXOS!qZSJ%RmC+x^Gq2oFzZ z@^~C$t!XVybe=;a{oKmV^CT~|v1h8*1m>0XbkWVS`sNkq7+~Ji{;T~H4d`6?h1I(a zlp&oq_2u`r5ofz?zjetpxoF>b zzK$?#CL$#@TmhbMetYCoX~dq)2|);+$=Az{6556+5lgIy3CxYI_k4MIbXr_hN2#0d zaA;Rwrp#g8*{_?h*w^Pq9InSYD|;8rVlS8s6D%FkOY2=#p~*OKL`14IqBXvgH+W_6 zBj(GLvZKMF+A?+*FA>`gvLj54^KwoB6G>iexy+?Cu{rJn zxz$AX@i_y0@w;(5%uHpNUbq!l^56h<@ag(@mGspZ`Co zD5;RhfY_T&^l0k(po1NL>~5XB$F}ON6{)wm=g_-9rBa$h{olpI2d>T(OlxLFRQklv zu@ssyC8l3WS;n-d6pw?Vqg_|vY+t7k`LtXnIJh1@ARiil@3W$f`l~TCOGdOc$&FrO zRfe0J4LCn=m!o%%*uiDH=Kng||L3&-@0Rh`BD|X=lh9&ciPXmnz|+J9q7ENjI@UI9 zToQvytXlji|2Ff+5q>z9H3hG28daq)s8X9oXGZ{p;pd-0tyX;=69)cIfrg8}xzA1x za&KBj^g|3T!iR@3_~FE$fQ*;1t`y_zk_XLgb+YB3DNtz?&cU6D9SD_9-G_GEKsel5 z)vE{qm+JQBm2$X>x|GU!=)9$qc3_1IWTxwZQs;mz6^eS@Vp}1~vy=BW*e2YJ{|UOt zxLHr9z!eRxhnEZgF)#wA5A6i-f*?467@u?~RSjR4IdCdRm?P`EI&qPWl5hR=W@|6A z;#BTgfj*Bil&s}LrmpO?RhDttqA0~9RpeJe5EYI8WVr+Bi zAK^isOu($zXeK&I?QP`{E60_Ohh?&xb01{A7bsn3Y*b1cE4w#{P2!6C zz24qnX=PL(X)zzXtSACTT$p=DgyQQ=L!3IpBF{EcY+pa((Pd=@j~EWM zdCR{Jjoxj(GGOJ6N=cWVbfd*N{*~s~7H~T zjG%n7Gb_o%Sm)QS%NLAH-*ZH=tBGbH8s~oN;umk%5la9%HWoCX0di;2KYY9a(Oe8FsdF8y$&?l? zk?kNL_zAVx&iR=Gl21a~o=i)l-I#;HlB?(=l$7|D)ZXHN`%PcN?{*w$Ot*^p048T% zQ}ewYtDbt<_lST$y_amG^|jr%exon};D~Jn$6*2dg)$aDsiCTeMTayO1v2mpvC|`)lO?ob7;k(ni`zL9fF}=?0E%q`lp1 zfLCaTHoQ4PX@zk%XD>~;xbox9g+VO&ON!p{p^v;8@x8uS)w*mGE-?JR{hmPppcPfy z28|6K3IAa>Qi3#>7kuct=m($ZJ~bn-F0)wiMf&QxYJI^4AN)yw9rnQ0MfCk-=CsOo z!2f;9R>yV~99?yOuB~u;1)o}u3PFDLkG)O7FFA^ByY(K=syKPhO}Ux>9`FOI8SuQm zT-5|T<|9_HMG4PuD4HzatINBSq4`&HeVzDEJT%N7c7ASWw(4_lZM_cdz+caG#?6O@ z|I@Uh(t!}4F@~gN~T;?iAcpnWF0GnbUuqWxa4R*O{RhPosOAKZA!de+pN zVQ5h%5VDH>n&L}OT`GN(BZsH^)eMxSUJbpDz&N-R#R0VS9huu<%aPjUqK9{s#|>~( zr9DgerS7Qkk&1`a{9q>QevfB`ZW(PlA_BTeS+z^hK?N~_#Pf#TF;|UtjPm!=0Bo$x zVPxB-m@j81{a88Au`);aFsme@|I)kBb&ac9TFQ4Wv)z!v_(lJM%L&#^G&0?PeGlA{ z;pHN~(k>r?g!va$&C7wolCw0ruIaIP*fz^D=Px`hWiOSY$jrfEkzSq;j2wMvy=_Vi zA2DK*l}dYE4mR~v{oIc(Md@XHXLZ9c&mbGp@aTP_l-q5MwBhwB-Jn6~+bbg>P6ls~ zYI8L)tS=&eoRw4kszDbDoBxO3r8-#`w%=y3M4HMlvahx)6S?z1S{xP~mA3<%w_9~H zcuYXt6d zo03D0!=#`c!%o{Tz!mhyRt%7-uH`!v9_rD`ui$^wX`Y|QNKaJVrDnbN`_Y9dp0}+K z=-+Wiz|G{ru*iAfU;Q12!{6$AMn;?8P8Q#xHIHGmmmZE@(h&G|LF?Kwxz2D`*1$db zH$2yU6<3x~K4FH$s!e(~pbV{xM*rh7F2DWGvBl@sWgt1)}~2*h#jEAJTefA>&Ov?B-q4wLUu+MT(?w# z!*V4miGVa^-*rXK+yS2Ozca;K-N}J5Jh=@F66V&&#XJdF_CO_A7TV=(s zMijomHnCh8EGQU*B_wSJoNR8WV=hEmvPwTXCM39+g1|)h~p8|EBGR`&E zi9QtRg(SdROFj>we0)MYeVLhEVc?81TosG(YxMtnx)<)Gd9uZ3p%`cUkX;*@Zo&u& z&Q0Zxzl+}&M;sh)Oyh9`j0MQ$skS2I+$7T8T)nL31JyraK}K}ZEsv5OAKXMtD=Zad zK^Yr^`UqN#=|ur=3t~aYvG+QcK$fZ|Zk()EZGewwRB36kR$x6Rlf(R@&;O^1yam=i zQn>OcYMa8!6p6LGwB757ls!?Ld<^-ZAgt6oUOM!Q6+Ub>DEUOj*NoBQuc8XKrGYmU z2Vk{k>_b9mA-r)#lA(+D->{QGt$l=@bf5BH03x01eEb%Z$A`(aB}oP#rP)(0H7%PU z2&J7Z*$sTV&9)}PXLUE~M`+A!@dzZSn^qSAXAk)5Dr$9V5aKso7>{(=c@ap`@@(du z!byKUHjDBGKgQhDW8jnllVlG^WOXRJMDRpp*STET!An1cs{3w>Quyu^4D%}m_G2o3 zn~-xJIg7sfla8yK`L})aFK7kvOEPb6qFK6Kbj||+(sj{H!1yjZVBNNnSpd(bS_h}4 z;My}wt;Lo5mP5Xwh!^2FU}b#^qcmC2Iqgs6oDpZ!k}nO32okBn6-;jc!cTswv|OE1 z85(7J$nT*oq>QhP+9_{J`>u)hPBRmp7}Z>VrL_b+O6bK$cdfO0-tAx8!APXgU#8$U zn;1b69?~Kg#3V}IwuV7wMCr8d&2ctBtmNF23(47<&J-SN4f8LefcwzofGHRLW^GtP zs_B;(oIPzT%rc20Bf3^=c0_g5E5(+1j)^t&J(LRCt}XxRw@S&+b4B!HzsoCc%QCvgXa;X3L$oHwecCfop*drt*=EC5qnrAItJ_ok zypHucsxJf?hBSGRn?~~+PJ&cpeKC9PGh5B4hqjMi>Cn5vVuh}5n@xlKK<0O-!`SqF zuC8XaUSn9}o4wYgLh*yOzJKc(3*Tr>jlLvLvB$Xg$nu_y304F!wE~f;eC0js+Mw@a ztbMUSuolhY3rWWb>Dz|yP&F2~Sxt}@;ZP-;M|@w9#yzRv;gVH0_d5E9EZl9JKj6Q@;^$&1fbF!9 z@CDi$a*w=PpYzGCr{^(XD*BDDF~E3h0bF79(hEHYoX3T zrQu~j;NwVChpF_o13JW{@r#V~5KgTxk#rnQQj0tHyYHiQ>D>Xq7_A>1j4J98+MKNe z3@M=05hOM@aGi7w-QSxoQt$g4OFAeT`d>7ieLU0q|NlFu)M>hR|jiH&MXDVaC zTh4S96QEaxL!a=uUm|@Zu9KKc^F>y+9b$v7+Tbk;A>aYqu@+f=E9-AG7kA^|T@LgX zYHL?hb9(%sS%<~;a`04j@L9y4E{J=rd0Kw}D}?OdUGn`q*r3Qc>``Q`ap}Q2K=_2) z)-b97m*)ycq9=4hSXix40nS|V%j(IIy2XjSo0*szBzx>wYu=I~5|5Z`&$_Z|v}5BY zqE8g)HemraT(8d=u?8W{j!+7p9KvJbUoS=(#!YN}>3o7dx^=Ll&eID8Sb|>;3am^$%Fj{hA~vCM8ucByd@hN8wLx*nwfC|YdjF@HhagiyyyI1}mI`Mf zDz2gF*q4!HVFbqhR;#YJu`?2-td5?!u`&+a+L}E(-gU_PNhvVw0hFn2(k$nUsJIA_ z?@glrOJfp|acavxs-m67bh`OqKv-yPS>OEmZ+T|qcJQkH_SM+SOShJkmuRac#sIMP zpgK-%qz`^OT7@<=+{CYQjWE~%JXKL^x7sJbMmr`wBCao$X(h)Drjb>}`j?Os zB^>wgr9(5EMb1oRk-Q4uWK>ApH8a&o#bV2)WlQYK9O+Z6YQ-4Le_QJfro{~# zo(Ki99+$rI7CAtxLGtrFmBoPY2b?h`{Z0bsj&QhrE;z7g+vLqO><#yQ%~C;=2N`Go zK8j*${KxkXKMo~4-xJ@x3R9f6uhY<=EA5fc22%N>r`wuEH12dtQ>@Vmx^1`sY>ibu zq0KySQMv#{@2y|DtMXT!!pDgMHE_p#dF|w4@!rCImEyAi9h?9{!e7uQRA@y#$YLF4 z7dfLBgN2OMfZ`+{V+OsrZ#;cCQ|&-2QBX&U(}=Dua~-d^cqGQRcgwc2*K}_#UxsL3 zmQ990glE`;?qqLc?{LQnn^#`}{>T8nYjJa0w2WCVDxITmy)$;io~>x8nw%a)oXa{#!f*x@I2&U04(i zV_-s!?8&WSM^230Ke`hx`zw0P|M?dWCO}!52u*OFD!z(6MS<%@_g|39HNAW zbaNc7Jj5up={r+UFK1`unJ`lEE6x~fb{*iwaqE7rV4llbvq4)x04!v|tS2fii+nQL zO%DSmvhJUh#jB63oG%yAeS8Q4C8dv)t&P>vu(HKmq=^z*pza-Roh3Ynxoj^QdX|^r zcUC;bmEKs2WlSz_evds8v>YWFliufgvOZCtEpJ&QeW}c=taL1o34*fDw3WbQ2kP$G z5st<=l~fT4X{5qBWMrYz!Olu0$@m5_w>QEFe7SFPA|E?vVbinaR#z>3;?l(cM`|g1 z#Dso#Ds*anJ06N%UD2HV$_C3ns5CbIq$NL`*$hWV72K!l#XkJH6eZ!Trsw%+Kc0m; z+ILHM+DQ=|zhDmjcZy`?{?^t)OSh?s31v8*`4wDGx7T48TJSRYUveG(Tz39oG!2CD z4t4JGlrvNdd^3OvMNPupTpK&1I z;QBLJ4#Zw%lYiOUN*#DWv)b=j7R*?hxt}*vne^umXZ>RWQ=7kB z%cccAe0yU3|GXFf*)M>h!CSZTH;VvB@TKt!fgV9IpUMrxqw6V!CR2Sw?H`V+INO?U z2|qsy^Q*8wFc^uGL=`J$czk^fdu{cpDlYNGry=)ui9d}h^s3$I&!TQQ6*VH4wNe3k z&=dr?+n~O~LMEaliMKC9jAuhK{|>>}z*1bHg}`nOOt~5!&vhsMLK-aGUMWVig0)sY=xHji7ZzeAznyE3Rbc(mN*?xcd=u;1ET=TVOc*oh@_yFI>zCU7$ zwv9@;JIz4fN|xm3p?3@(#yEJ*?}dJYfkDEDQ!Eu|9W-MKYe>9Vb-baEW_uLx{=d8Z z|G8FT@qJ3Wy`)_?R84gnvbKaTI)L(el-q;BFvS;$LT6xW> z%)yqmkAD4b0^HlF@CehQB6`%OsxoKFXDB2y9DTq=UcIDLVl62+*=gCP_E%bnP;GwPO|W`p-B1W1riTK0Hd|1d zCzA(Y)L&fz1F26@HWZ>4@J724v;-h!l}l1XKOUMiR}J?*PA zI9*{Gm6ReO6qYZ?!6XTq=ikadNc}dRQ%H>B@&Oye$FfeoZ z{Z16S?xjV=MW*duSjo|Y=&@y=UzqbOavznI+wUg$jO;wu!3d(qw5zfk1>sa zVS~4#caGBGnRi=<(kE^c93KxaR&;o*xjU6sKbNNTeC>+^Di|8xV=JUtOlKxAUzM0> zYP3&;_=}y|%cUu|-?A}~R+3`yin(ogtz!g-6z#^Q^#d@UpBj**->Bh_lF2>OoEl4*t)0pP^Jq{H~n~qFI1!B^B7|PieiSh z{c^}oA zkG=!#9?O`>%AUOh{)|zmCNvd-)9YT5Sb|vh8HV*UXIqT5xV|9nt+uX`JMFeD9R((b zrc-;P*Wmkin#z4Ve1MEc_tle$2Uz+c+eR8x2_d!YiBC&wmPtof3!|&~p7>n4L3x)L z!qf3qa-p?6*7FSLNR|YTwpA`h#TgYKc)BH@u+7M2ej#+n0<->QbPaeEYO~Iah%^XJ zjWO#beyR!*NSOD=_ACd+hlOL$?w2qzCy>Z`q^Sj^FbK1gvXdf-x6ot^&pMabi{wp~ zMFG`I6k}t{!%Yle$V$Je$~6ywe3wSu&f;Cu-R}9!!3X836!d2Vl~-m`C1;Bdvmh_m z)%6fKyRSB*k;`b@B@G+clbfO!S@~M22H0sH<6x0R{UzAgqxtLyltS@i#SdLyzR~dPE0eFFL)9i}gOvm_t(>P~`-^U9k&M7HQd85@xTPe@)+rBuH zvvj_2WnN~vW-H3MzNM01#a&W~GG09SKngWB04wq=(z z0JVNM3xbkz*b^Si%zv1nx@}n&zx;*Zj2xSI9b+^#yA%oC zjpD9uwZO5?I;GO0! zfF);j0%@hxsf9FA16;#{k1zoNAwR@=J^l#2csuLLnCIv0sep8&ps3q!ew*?%VEt^9 zeoIuG-VT-~8Ff9XU+2c}*gG?gJhlgA-nxK&0kg*WS=0V--3Jpc@w3Mo(T^IH%vt5< zjCVo)|Ag;9njtuNV4&z14cA_Y{FrE6^1Y{V+7~c8M-_(Om05F^9-ve?3Gk|Xo!P13 z(y9txjnz`b^OfYI@?QCfrSD5Xv?EBOOLmg@L!CeX@84Y} z9LpYwm6gzHL1s=^Sz9+W==%BR#s^Z^Cq>|5P~t5H$|iTxs)NNbrSQ*=3Pg5(P#u@! zbNvFwW!1!%M#(x|vse}XmLlRQwj2Ydib0ETi?5l$%~gsKZUE}Zen7{5-8R}tHwv3y z*iw*jynIk}a;q{rB8W!VM}tZ`ike;s2}n zBh4&f<0@8o7n+1KQ|^mU_fg zSp>mws&`~clSapAsZ%om9*!R78G9u8+ZWmGT)XEX0rRBPBVl)yQe*0VV~{|<_#Qxh zLo5)=rM_P6Y z%w8_Q4WxrH35J3am0%MUP3^qFTOCc~Yxl*;Nflnps-4GySQXvO&$2W~aLB1N7G)T+ zJ+&Ih3TP%oGyyB7sg<%^lb3Kod|Q5)MRv5h(@x=y7{8RHkb|E!Dm#b(zt3wvU%}Wm6uEc|C1MI@wuS0v~DQ826+j}7n*WrduRsa zpi1syK^Sbm?{v}W&;+Y0Uv!?c)KfwJYINz+$eTXnx(Cng=1YUhKS=bjN4WS9-w%@O zhAVU!wi-NjU^a#lJ_K z%fA_~`?if{9PP$LEUTz8egp>IEFjqc5zR_=W+h2&Y;%J=pmM=SGfjUbXbik)B;>1x1!Vi0ddJ~c<`@r z^RDA4m95+Eakt@>6(*EUuvoXMQ=7$}^@#>hIXYF8ae-)%{A#({Df@JE84r#!u?lUH z?a4-|9!C18vKtJ}iIq~z@`kr?H%5GrkTun%f~=A^i%}+V5gFQp$EE=|L*b44$x9O} zFD2dF+Z0BmnPY&5rKVV+qi^`-!6y(Ngs~x;vBo5xC+&m)(~g0;{bxkHf+q-yi5!>4 z;8VuVWv}^XDz>f@e=!*+b2Bml51~=FVXA(DYiOs&TTv0> zCC6*hFuJ{Nitf|VnSlzz!Lt6EErics_YfW=tG9qJ+k8g7#}v1gVF%3QwPvT>!j?yf zAjM(e=?};c5rn!-3^`?@F#;){#T~2MAQx9kaMpV29+p!E3HT}a{Xc2dDUATWRYlYx z+}ze75Q){luaMAN9MFK z1Mbje?of$-7@Vj>rM6D^I*(16TZAigOP@GQM2vTe@64<{C1a1oT7t(TYYif<7dzVA ze5}}h#gHWJtL%N~1^;TzW+)C0DKB@caPaA$$D_~}l$5v&J{Opf41`%4M0iyC%N9bz z;6F}6Z=PMBKl%=d(LldWOD@%aTlxM^`6#oirHdq=0W{A5;B^O-PduQ3ce}>|F#=cl zVLSzY7x+_qr?N$Uc#RaW8E?f`8=0~SBLG3jUQWw~m4ck(bD_KN>@*>y0hJ=Bn^_8 zY%5DI3p8wi87wFPjO8PS;4{L;GRAx>wYkl{y{_b@9Mb$jL5uv&p*dH>|t@eKhi2s;0lW?XpZKs@W8WG>PB9odx z(KvLY=Ym@A<+|5d7N<8-yzjY^+JxBGdcg-qnb z0+6|Oc-<1sb-;qou`4NGFfGcJ=6qAHiy944oAVH#gnKts@?~uYqBiqPpM8g6g245P zm{|W=P7bd-|G%-=F1kkfLKwa~P;gHMOHa5{XUyeSe7fB6~Q;i z@e9mb^0DLr93iE=y_KFTa~@9^EL1cDTfsU=jPm4p!Y-kzQ~A=@B$$k+qZ-e6%No^`s3kE+1%z0_#(v;FKp7s5Oa)fsY5%rx-6p z@0dgtfhdLM`T3wABsk^efp6!&Cd~>RYmh!*92U+dXPPcyGz>z|gxAd=DJsrDaP6b! zY39100PVRUH291U(XhklVyqR_&3-1HLTBo#(8e!s?~~o^D@ChBiR?e^9E5I<)GQVN z(H6c31Tm!L%z#J>d)cnb&R6*v@o3PQ2?rXrK)o_ca2?-rkPO<`NZ$c|IQesgM}Uull8J5R@3H6fO+m|`k) z&s%miT8$f!3>9!CK4K?ojrUBWgzk3nGCg;uUbbP3HZkDQY5=v?i@3$0jTb)mBfeyxh+0n%p!Y=$k_A zX7wfZI|pM@hJa{5pvWXSf_RYo-skMtS7V;4s9=%PP^g&_8Ro|)(i(D2QX7P%`B6iF zwTi~LM@zB@wbpr=+75e+*|jlL{BtYH#8dj^HSAmZowNN>2_R}!lE#+Q?&WLUZ=+De)sn<~FkSEH zT0o7by51mo-{^)q_>x@a4z!D*=k#DypPoyLZdid?;~|a|1^tWeu}IW@!eA6-t5H(am)8te?}L3$QQX;;JvY$6jqlJP>Vq2pAW#q9*v8o6<<29t(FM%hyW3t45C(F z;;GRfc7r}bU|e25yQn&0nj7!q; zm&9xu>2s>!tDAUlT`XU=>^9oC4GI@z+RK1N)nT0Uly#R)zGBJPbU}ZJuYxaW!w^%a zJKI`Ym}jXiBMb)TtBr{AsEV_&1@3IYzLDbjtb@5)$B}vNTYI96!LOH=>z2p3K&xz3 zkC!t*G>NURRvUxpU&{13qi~&v5ZWkM* zxH5$iW3cbiDbzK*Z`YQp<~?q z>YEvO4!SjQZ{8k_v{*D#?8?ig7fg9)PE0lr+x#IC8gC%%uExYvXp<%fIq zp=*hKpW!M6`n1iddSZQ~Vw$7p=?kzB)^KNP? zq`dc0?_>os$wg`w>Q2S0-yF?s*bx;#3quF9bR2UIvA>}*82jm2E48h;t)z!~%S&+( zn(6*qo7i^s#ZTjqH8pAX;ec?1Y=PNsRKl+(w~|Y%y3hK8Y>ZzUSdq)A?ZCum>kN-K zFPT?5$a((&Yp@C-{>bf9f!{uNo0Wk6Bz4Rr(YjLChQ`DvAE49{EB9y9&qFvdR5=@H z*ypK37i|k1T{ylfK)QgtG~m$+-3 zorezfs@F+l^=oeI9U|BI*Si$@Xae*Mko?Q>ww61B&>Apn%^xPreWSyOD4I`cr9o{S zeCfMaOe)F;KSe6Kj({pK7Tu$+&-?kMKJe#D_0TYl zFWn!{S`}-FZfOBFjoz*-yrzBc2mn|oH;FYmT9!>4sx3>w-KY!+NSDK00m_~N^|zm9 zMZCOwMpqV+pMUrr@;uI_pTlmNL%$>nC>{*~6ms*m7z{&{y60in z%LmI5O@HYq!{0ybgo1^1_a3pmL_CprM4CINTaFif{72anrAo8i$eo(;^tPrbhSq}D zxa%^gY{+^(x-D<(n;eOS3o9v+-!PapL;0!yi7Z>dnx(>w)CiJfiB_{UU+lD(&jK^- zi1w-$M@{+uBIUoi?!X>jc!xPZo|*n@ux_g|Km~apA z2?iv7$nvWvoy7;pg>_SKke#=GH*XlRY<7I(${N2lYuYzfJVma+1O}ls3cchW?r#&0 ziVil3&-J${u z0DGG0W$RGvdMkY3I*S;T88TdpdGi5@uU%~UfJdHEq_%fl&RGfc7jwzelKxO1j9~h@^Ly!Q~Bz zQu<~IR{IkiB}M)d0u`XFvVd2@FEWY*q1{CNPc+-_$*-dep~o@nG9i4%ZSFR?HO1qc zHHqDzsn@>>5}_aL)BK`i)Wn5A`+Dv?Q^m<4^8+@v@bKl+lT8wdMi#OR4=TRXX0HLQ)EM> zsYO~vi-wJD$K%7LKG(9j-U}4>0&stwnWENSM54^jc+_hKD#7=_2wF;snmE86O-;3A z9t75p{hFoK8Y-I(xjh`E$1rZBh9=KUL+zdtK?!M`pBFZS39EsESdGE=s2R>GCHOJh zV_)Q##i1F|nD|dS=F_PBeAPopwLjv#3jV-WSAJ$6n%<8k6~?E97;2MP0@@?b*Utw4 zhl#Oz%slPMw}H9nUSH7kg3IKjJ>cc|fhu!r+gppnsY=Gz((cCBX++;ixye}5Nyk}! zw{B1IY)c0Jh%>;M-DzbTU{>*;)obL}6K(;RNUc4K*5D|;WbsTxQ)Knz%#y?e9fqx3 zctfE~zz=-)SlHB5Z(*8WRamD57XO*8(T}jO)5+?;vUX)t9G$ft8;i&CJI)FA=9ewg za1vr=47z8(Lg&jGlxb0Ku_~z@;eF1D8FGpme9qArU>5J!a;9Y=yxw66Q@Hnu8ru#G zA~D4LbmUy-1<&9`zsLZ7ph)rv@MD%b)(yR;lwvASKiUEn8vUc??c@$qk- zl5Bk#-7P7i2a)R)@`P`B>0Y%!+imS9v+nKqP-5IP<)U z@Wx^`W91B)3Goi{9gk!t*79ZQi+e5;XBH_ftb+6PnH?+9oT#)fa@C#Y1e-Z-k#@I_ zZt`63W1Oy15QlR~S;6*cH;IPZ-2afYH!y?7r`Idtsl?&zV-u_-;!xiN6L~fE%Wm+f+|E%)ykZiXgRbI;xr?8+m<+V}I70 z>wo)D$6Xj(%9yX%>IB+pn})!Wat->$h5o_i9TzqreJXdpig2`X6rW3r+zTZrFE~_A zFPi3OLM(zjpV6g=tm;E=$N7PWXvbr8 z>I8Kww3Ltdg9TnE>UGDRrB#-sRf>jlE0;`P(L!?pTQmsiD1qW^DA-lWxmLC>=r!plcj|6(y*xmZKX0-AHLp6xmQ}32>gl+CI$s6>^5z zkTutygtfhS&cs;riTRkLnHBrP|;&RgFd>~L6CvnZC{GwMC1(A znLO=Tl%L9QrLm06Ko$W>0jg*EKd^@d$3H__UpL=ltw)RZ<}gs8bAZB^adWiLhP558 z`YZxV(rpKnuK8Tug2;@;f*x4QxKOtfeTq6PdXKA~{-;`UbgNe_=n-jWpOy$1*K}Vh zK8;uJY3P{)|f2$p(He7C8 z28VVXv^k!BzrFcLKBYqC%sV3reOU&eV9^u%17eJ#WtSIus*Wg_ za#d2J2mKNPg?TU%_=c$1IlzJvH6b*>QbvpDj(7%Lwj108ItQWVcGI^v8! z6!UaxsG74>a6kkLtC}=u2_T{_gWH#kF&GMab<>%8ecga4q$A#N;4vIU{Gp|9!COqU zKl4W6?RMrW6?MVE+~ICQfbsw`s6Hki>aFKVYbCXf>nl{f#GJHq)2#&f4uY>vj!`)O zk={#OfICji%Y3u6wZc0ZevpcdHqJ0kH>x0wgO9++Nvzhc;PTGS(VRgTv&`lI<=5}X z3s-A+-pJ|^28nP8?l=c_n{^v4>6TbtVuk=ECNnp-xHioU-uD%?qNjinEu1(~I$cY- zu5V%+w}tCZEDbCxQCxQG{JCpv_8Mty+YBkzv0ifH8RcGk(hbyA(E$@FOEg7rF-p#2GtbJLsh<5MuZ!25Slf3RXP2p_ ziPd8JZg^r_jnuicmChm|H0)wS4T<%{pscM;UwSKI#|-|qJ>v%TZTz5yZc^ z)|Duw_*@dJK3b23;4Z%_J|$kIclo9CP57KFu}^0M1dqg`pz3K3srWQ&eJFG}!tMFY z)#w_<+6IilsTZLYk4kU<%lPly4zc=FgKxo%4B#K31?|Asa08!gy!AD$;Tikw@TQ6z z?aoKx9>dQ&rS}R1Jl>_cu%ns?^X38b=iD+NoaXyOA}FR#k|u2`O*kpK!!8LV#yk<0 z+%LJE<5ACCacl8$m^Z%&JN5Cw1@9(`$L|j=*-NHADJdX*51Ol2NR@ue6F6#LrK*^s zyqep&9@aNFoRr(vV$S>KpLmYnAz^inXPfi0Wgw}IWMikEfH`{w*Ja|{tYUkuI5Z@YcM}YaOK{!)WaVe7`>^_zV$aQXzh_7kk-xNv(^cF zVjePYS#eFD!p-B7h)<~$3N7=qs`69@=7)JLQi5t z;&WhD{X&|<1^o3+L_Y2YZy9)tsqqUuHUkaR+#!zpIf5_`1xUkc7TI=ewY1nQW5~9F z!H{Sh6jjlOv3PyGjV_6{RId^qqagn!rtubajKpReK!#ksn!4`1eLU;|QOt;k9T^I* zLj;?2s<&aT8&sChYxsLUzk2vN_+wOOmpFaZY8Mh-`8)n7pz+xDy<^lzRy5`IN^Gn> zu{_QXL4ma%itP-SOjy<_-4e8C8((HRCn(X5p8w=?ye z#R}}Vm`F49Mf4ev2FcrU4>{S8WK%uKo%R7!2$rSzssSV<%B3&-D6o76Etil>`u2z< z`LBWIz^%TfbOE}^8=!}gGU2=>M&A)=y9i)X8qANtVnQ*eFAtoVMiQ~XA{#VDq2A|* zXtCnJ#Fp!VJ5LAr)KeJ7*vgl#A|Z!o)O;fOYf! zLRauwx?Dq4(u*iL32Z%zJ1G>6OJ-BWIG7!c#8z{dYliB*v>cT9UYd`}@&p?Q#$C{! z#8QB%2WXa7pI&NOiZuVQR2vXOyMZ6va8qQBG`h!Si8-0a3?0YZZ8N?EA38>!Cr2xg zb4DzN$giR=a5=a)tLU0*np!3+Hh`hsN6}Y#=Sg>-{1j<4G}2dF_#7GS3I#H3wqZsS zlAG1C-PdSP;>~He$4q6f`*nRc_TTbv%tK313VY=& zlSuxRZdMAgtvf5&@BqeEN=3S^D-TDPPR^?kk!|TUYO}@7cmi+$F`O;Z@!lX@>h!3?RXuv($Gi?@#8d zss+x-%Z)l4s|PJ;lTP3W5PIeDV&`3HwrqETwYb6!u-^xWziw-h1s!}59nCi@Jk~N1dJ(h{Of>!S)HpR>y8C5|?=X~woS9v>v< z%VvUr118X6xP4)#Gf@?|YE!#msl|sx9fZf3eXO|3sz+EI>XUov6YfmIN*0(j^DtHz z&{E>q)EZW8K-ekUeB<-CN28JFQ?0uR`r^jl6JqCt>V2DHAT3VKB+fOP;xkkfJB-7K zuyQUi6Gg@0m>e73wpyfV<@l@o><%KOBnX`KGAIxih$|~{UvHl?d52Fb(cIRH=3~f1KC5ZiGhY)s#PRk==7WPtCODcpXrg?roL&zTqNZ zLFZfLGgK;o6&9W*qnE)jM@H2lXn5t43Mf+HbJ%gC{Xr#%rv?$ z{=SWFePrzlrjN9hSQ!M)@P&KZ1j#H!^8T;yct3PeSnGrM!@=(Ied4_>K{aT}b7@6q ze%(8R&zwjKTNY!;t{}#h(B@9Bw8m@FVpT`!O}l*AlDw6dpe|1QF;mq?{EKP zs2tqwQQ1e4O`eNKq(QLGv3hOOOUpJMbZWYSV$D>~-bxHgGJ-6rUQ`7r zf!K&YfFpMLoHcWG_Qtru&_HTK{rcc>QpZiN&Wy(C>=CQ1xoh;e9ha z)TbjUNW>j156C!<=d(+^im^SJgR3DKa5PjnFSOWws9cky?$d-7;U+2KGTfJHC# zjZHmL@(_s^Jx3NF%R+$6XN|bi*yNQy#mS(I9bFC0o&n4KY$G7^niKQHdsK*B*}xt` z(I!$O%)n+oEZs2@k~9PhZG1qXbYl!}v5X4eBXQA*O}5q3?=@c)sJ)8gce1N!tzv8G z`-u1#;+?J(;)aBMkBU+4wPV2cI_ciY8n%dy%&r3fbG#3;*C64rt5sy%w^8V2U)CWh3V4H588o8# z#!#KvJ+|RBm(fZ=N8w5TQz9wfQoosu#>MttIRD5$-4W#0FY<}WUjMU;uOrsU z2h3j#%hIqKFZe>IdTaaJKeB5ZpSdA2O=VDKV_?4RV{f}<57XfB2(F9b4KFy7U9e(m zl1c>b$^z=M1faaOiArx`1ky4MNW`0qz$WO|%>H**SnYCwr19%wP9cS=7m2T*ob-85 zC5Ri}YaL9uG44MRskLia^B3_`m1X2y{0JzRx0{907Mot~SaCG$J$`Q@&&X37tq>q_ zt|VuZH5~xU+`r77X%VFhPzarp(2Wg+HArV(ECEER-8Y3b|9zzis?!#5Rx`mb(4*?` zv|WR8Oxd624%^6>0m-0rQGm@3Jyb5GIga#pB zp~i=Ljy|3t4(k8x9&qN^md$nGMBI=#HY(L!dmjfZ_#v4ayNxPmiUv78ww1VaAQ-F6 zyG~X8#!cgJ0)TIM>5Ko1M4s+T0|0``FB$OIJ|eT;wv_gsY8_ii0M{d_v}f>>D#q@v z+`DIq~ z)d$M6pg@b#?t#2DJ~3#;UGdn8o9ZQd2ypGx2eRir*Y3JgNb%6xs#j;G>ucr6oOiY>Ceirh#trSW?Z)y%pJ*Co-Qex*i0s z;p@i={$pp;Fbd@MHQAMLani93{0gED2{!~>x?8K?Js9M()S`8ayX^~1Ak$F5HRP-| z>RjSZA;SieSEl#2JiPXH#}9aJ=AypOrmOjLS0V{QoSvt6Z(Hq@SBJ1~ z#XT>M1NWKq_TWK-s^KUVASQnM_nl<$RnkH!g+yb`r!KN3vs&^&U~=y-{UE5Kc4kCY zm?pod>@gZ|s5KxQB-TMsF}26PFfrbx%nijF)tga>cC*ya9a(}Szi@|N$nZ5lx~g$o zFR+Rf7pJpY5}aVAQ&}QO%n$^xl#N3O`#L&1Djkd@)Uf-VcGcJlFn9UZWTvsVDVpf& zM8mm?d*;3Z;sStow#*v_`^@&nCAO6>HVQg4=XzBWekn)A}S+Z!h;a;S6q0!Q^O92-|s;l zMQ^%5Zr?ig+lh^>F4?(lur&VDENE8R(-(KYl;|h`TTab&jx|7Dl7(irF{H&cEM{f( zb35bi*bs9D8CN8!>hDKfS;Jrh84ag%9a-`bX^G5bNmGF-42i@Zl9t~b1FxEE@UsO7 zqP;t7rIayyJ)U5RgI?P^;4)zt5;+;EE}^vBgeeev-5$bcrjVvXn*u^VmSZKb>i70v zUA{9Ospx&_t58fSrY4(>{zZrUU8NsU+Pp4n%;FSP` zGk|Nu{)ce>AFeI!e+cLQ;oAKFLpXZ^xHkWfAEB3(9nMaS14Tl4eg;_) zY`F;=4<%-I@lTS*?vIDA&b6{a<=lr8v-k!4QHXHw@D}#ar1TN_M1o&+jMF#CniCyJ z*Vwoh^ZKXOBK{&ntFuPzk^8gG=pLT-)*e6sBWXVqTw>E)*P+ERX)E9c5v;_-@wU)r zNvilDT0B%;uckEH;MpRD_R0@4t*Yv~@MKOYRqe%>l1HljCe*}J?1&HK zpBmP9pc`|&ouF|D&MDpz>q2VHS?y+gAZ;?p7=$CUu}JkAH25{fNy3?DkE~eYOB1Xz zfK!^Lskf3`85Gc~d5TVAU^faaTm*W5HRzKSaX$`t<=!0A#bE7Qn&clJ&w1f)Ea}xW zkgxA0f}b{XQNC9I_=#phpon{G0*f^o#NFU1DSikDFO?4Vl{+)mstNA#8HI{Fd%GpW zbGOC~47782tD6pp%)KlUPb(hV1o!6k{PRQo^tPYaJ~eVA)kkME0)H}(ZDa;J{2?Hy zde$Z~3>$5j+Q<{kUE`WNPyf_#&(V$-3`ZOf^&_{*z@%E$;s5HB56I8POrlJkGAp=? zTbb3#+g3!VW_|MSx4J|=1FxFgmglcWiUR<^lGKuye3+ep6R8fY1WQ|PaY+?#@XgKGd^h6TyTRB{lW zJ;qV;9bJgx-6;Zo+ zCt3Qd5impNykLb6Ni{ZJukXG;iLRW07w?utN*8OS1>NlL)~&hj8QpR%8cV++U_4?u zQwx?nxwvexCCvm^rw@;M_ z9V}n=`I)tC#lL<}{z0ve`ZBUC=JE1PMnuPDXZm;Dp55_3o_EYXcA|aABimo z3p11dkbDO=!D&rR@!C^MFi;J#9VC#j%+CiGYkA}E#~?rd?dl>;Id* z#hY;xTwg7HQ8o!CAHPvIHLX>$CQQ(%@j3chS0%L+(;r zSsODhpUE4qHjVQS2dn0DY{%34G`cZ#W358g4o^vaZ+9_) z^agop*`Tx$7J%~$*&^-P-!1h^Ps$+ zvC>5eP0;>#*RdPim)22A7UGUE1vk8rap^qc)kQqA8puBOvXKw&RQkc8^Q;Bwir|0oX`Hh-XDj= ztV^0nfdf-UWa>|CPkK$QlJ+*07b(vRG_?ES_e&wBMH8v7y8x_=VNyd=Y~;b0jrP^N zK0iLUkG0;@XpcNuKBO8=DM9N)guk%o8JK_`THn`Rtpf|+W_YN5c8DE=ITxniXvEHS z_~I6wQVg6?i%a7DDwh^(`V9+KXp9WDTIs}Z>qw|^(NEo-#v?O^&d8k6gJ0PZMb_VM%c5qb4rj*oSNJWhIY0Nj z_2{Bly`60BknKzJV`0s45}esh)@eoqXft!W$no_UkggAKWpB7S+LAKjFUHV5*j)r3%ETh z&M__53AZvE4NEWDpwC{q>M7g;PobR<_Px8rcnE6oam}y#*aJLmdUAx`&}>(D5BN^xBuX;7e!bf#FMnxw#b|IPn}s!gmOd z=5Y$-I}CHntsFQfreM~VX&BK6>I1q9!NU7(oup=RQ#v$eURSe>wSZw`pfVb|0`df++kogJo3($H5KS~W`_dR z^o?!b`MC2!54UF-jGcJA>^_kY~)s;krVNKuH;LAtsaS7lr| z{yHEnEtJD%RD_vXVvJ$eRUw26;c6}wq?;0>J$})H-}A^FMT+SzK;0L{*mQv^k#Zf6W;<>$Q1~e=CZ(poo{yrM$|la% z0V&kxsA#l*%=v7sa{YAIkK(1J2MIT?BB+^N>3r{b6jpkW%Xa4&1-)hxmbKt8Y~`qE z*;h8{(?VlKH>Q)@2vb)M>6xak~%gm56n&|A0A^E0|EPhq#9evPR zLlRz{tBr=iK;x(c?;$4)b>VMpDYJ&b67NoV%NqlCXT!ymN~db)Wmrw>?0E5!JlbSXZ~6ONzT zeyyj5F-hl-(*Aj#agk~*km?E({#{P~v13=O-rR(iv?pQ9p~O+J*>c*wVGvRle2V>s zQ<%-V1n4aULv8h9g@2^+8G436Kv72Ik*))OD@>Xrr+5WB@Fb#=S0K~6-ip@plEn`& ze`Zek0|>iR!H+aC{GND#KegWMK8x=hWEL_1=t=u80xzW!Q>8F+3^VCXK{x~WoQiGz zNf?#%W5=CMO!HqgG3Ff3%)}D9EBVv;?Y~XtE}nvw9#L@Fz-a&XEuE2A<1MT6@`z4m z91p*|s4g>gFh@mrfjw4VN{@csa;-t2`YPr&Y&ma?AF7!jSZBDQ4IcsDuvJoaNqOu( z5%=NeuFd$nYs$^hAii|2}FH+mqu4e@XBQq-?Z%OdDFO#(-;1_6>}8Woi1X8 z7X3=GgA)gaS83fvdkT+m9v814g3a*E$8!Txs2}tSxaT`J!hl0d)(33FuL$=EWG-HJ zNm+glh-}B<9F+tmODXCJOGTRaglofL`%eM2SQC8yltby{$N4U?BUBNP7sA~OPWPMsHUhep`U2QwEb^l>8N>e?-&~S-n0i=%Q zZ&8P7XItIFkQd!s*{7ttX`I|hfV_RCstAj%$h(<5`(IP1O0@N0>^Z!@t>Qfgv*xI| zF|AO&6m|{^0pFPPNVwgY`n?*I(ptW!1SjlGF0WcWN=OV2^6+B}?rFy5-Zvaicy9Ax zJEAl8*9*~?s1ijSaAdY1GuO|Id7QKE`sh0|>uV=#wByZWpj+(Cb3hqHHj*0+JHt=r zCll`0{7FB8JMbx5TyrhBm|C(FY=2>QzwYSc2LZ z;DE{uiT!AESaEQHQ9kVpgpcl1DcP8cqIN$e7>oPX+5T*p>rC( z$!6LGZU4_h+Q2qUrwu5i)T?zkkD>1G1-5kufsWJibD}a$S?)1$nAyOJb|BAfWZ0AG z1rL4Dnx27DeE!H~!0m0|5wFKmj1VGsv-k_ir;%g4?o9e$>xcN^Iy$XGVc67(e`UzDPNIje@Z=ZoMw)ziGU7t@XVg!sLNO}eD+224 zNBQrgD@MNW680~CeVl)RQw{k^KmZ1j?du$-X=dr%Z=wjgx$X0$k1Pu!2pL&jHa2Gr z$=e#RjphciyW9LF@}Ze0E6rEEwzD3{>m%PH0a&ASPr9HfO-za9b(*SeloTXm4Rxj$ zNtM_$BMB25tm!*Qs7Gt7WXn~J8S^D$wDjc*t-FoZ-cnV?e(~AoV44-kBsY0e3*&P~ zijg%wVhp0W@_HbzYWyb00|)|sF2hsez4sW0z_oSX6}^i6(oli2 zvUjgp+*)$(OOEeS)fJbwG8gKNia@Lpz*jzAr&jPx{;bA& z$=+G*61Xka`&7D%q+c2|@o^ zG0_(TNm=FI{lt-l^7-4uDep0HG)SnsU!(rC`=h(A)})HNJ5!Zz{FN6%dYVPjIm(j=?(8)+E=eP5|Fy~(CQ^&=sBt1NZSB4Cc}Jj~zYA2{s3#!GK9+otvE& z6Qfp(tww5xuXn^Ap-Hyj*(Fx|tnrOz^Q(dIXml9F0%I=Y>km0jki!;!|7V-tKdq#D z5GvRX2iw?VuB_ix3=COXTArM~ZqfpCs~ZpxI!o z?S)F0fL+XH?kLU!jMeja<6SpVLaU6`1*T_QX>}ry;^E6rv5b!d0E`1{xxD&@gq}fX zp(EZct?Dkuwf;1fDzjLy3dDOUzB$&>36ZB8H-meX>{&MU09UgUc#Rww0V{VtU*+Er za(<_N!$sh55Ashpv5k#g5RMiu)Ev9!8mcr*Mpt|4o9#*p6P#DwNck%C^E z{m@dggs*a(plz|2mt67O2*P1EU%HPi3iJw36XyA8S*d92bi1!oSElI~`5vfs&6jR4 ziv5Ml16qW>3;iAN>^#!~!M5xAZOJObEY^TD{SHl$r!Mhnzl0@QP)tXzG!0vBHda4u zqPMWT_Ot-jlS9pPGtEl_37e0>ZnrtX*k~soe*s2ECahC0ECp__48z zLAjk9aizIJB~0L=!*Q2Bo!9C5d48uxs_r#1%}L|q<>k2;So?f=Aju|f%eb_EIvL~b z#@-4&0E8=za^A|3#+J9jR)@u2h;mKVvP-0<1@b^{ToZb(R_8UNGerJI1WoR>m3AKX z+&asUo{5_%CkT_h4Uym z|9Oi4f^;DrujN4>TCzMSL2dLTQp@hii?G$(&p~EaN5GWQ_;22?@>ecVsWl2#HpiwO zx^uxz$*RNi;z@sh464Kd7emBOgI(GqH1o;jwn6py9M2}$&s9ljrTAMQzjM9;%f==P z$JWfua_T%pGf<~c@FsYzz~8yf} zIj$RTo=y>fp#H7eTh{0%YpNVG1#kM;u#xhEMr_ojXZ zaw9;!mS@pPJZO2hY>wOCktNv*#DQ+`+>;&uf#KB1{{AHh4e^t{$2Ia;3wH|Jk)Apu z;&2uNZ+m!-kM>|J3l2GXdn?+x!FiRy)&Y=>>}%la7I{JmWm~3ggN4$ac=#-=F~55mnE-82u$!Ce zSydAv?Z9@?3_C?y`34egwRt90+w5|r?3CUnAe>X_k@{_qkQdy`>Cip_gyHlSPoH6_1#8f~*@ z)K>6)dGg^Gb7DMADH8Xd>V42lWJ*tmg$f=H%nTcuB)&G^o~vtQ4gp}xB3iN28z>ID z7F$>>s=fYTIA}>}!ZT1UF8CCxudj=;_Bn^BrV&^WkPcBw&b5dGZk9}kP@JI=!~=5H zTX!>Iv4;`>Cl*YBgEwKV`OBT^lLu%q#Z7~eBcb@YhHWnGfQ)B zcRIGr+Z`2E9KO+7Uvd=-g!Z~3@6~sdbw`_EDDW6lfF=gHWPL(+PxRfl#CD6Re$_FN zS|&aRMrW^X8(gW$=?G``B$+HT0XvTHG8!n5Vy2532X z+ju*t%@+um;~!g@U6<;lReFvtMC|v$lKW#&lzB6UY3MN;jPBlBW|bCCd%sEmn_UMo z+ti_A^n%5_UJYp<;3o9ui7)=E17aC@i8>q@_U2um+x9(r2z7LpsXv*S8Pqe(&hXT$ zKbF$)0(z#aGgT9sm=Cj&HWQWB7O)^Wf5{|rPh%w>L+uNpd}(FVemcPNM5XsbiU%(n z1_z%pzmwzKLN)(K#=eq))Ht2F>tAkcMGTqC!yBRx@#d|6sc&s)k>1MZ?qD+7SSOl? z0R1%IoOe^5<=r1+##K@Lp3QS5i3CM{bx#Jib-fRvfKNK@x$bcsQO!}9a%Dc+3V5+U z{i^z1lQD6r7=LsLEG`L8%>8i3*Kf|CKje+%0z-qgvA zkb&%`!){tuK&z?nn0$d^(RurgGWb2#)Do-vdw}`+)3Ac%za|_^YfC54rn9$ct>x$ zgt|G}T?=f5JGKVUZQ0H-*29Zyj%LCCc!^d-`%3c-3hLq8=}VIxihXj; zZ{8z79!#gTPzLh3J%YPr!jDx5fv^N}5Tg=v8d}#juxv8gT|ur+pgEcL?G1o*p zC)l?i$WTA){wF$C5|_JWDD|TW`jzH4T4`$Ha42?>5aS$k#U11NQ4*7S-Pdeg9e$;k zy#0@*3ZK_D%3At#-RR`jAKQ7B+A@Ab=KzK@W zUTiV$f*&#Ot_Jd=sBn0+Ij6w?aX+MI!)jRET^bDy30NljgI}-$E|ftRE8;e2qmH7-iL~H8piieV=!^^l0@^_7666@m z054wMwoJY$8E&mnXt39m<=2*0^K5R)LmnA5I~3tVof_FDE%pxk!6o#kSo3P07#9Z( zIXfC$=_Q;1TZ14pGbROes~1L-Uh$j&v|QyLqw+Vr>%-o6Mog0BL2Dpb*ZxY>pA;-E zlMHCcb00FCN?Yq?+V+mAAKjNurmNZ@5Pd%OEHGfWYPOlKXj7e69QA*CH)*v~*&~$c79GNq zRxT9+e_O6oyPYQ8>%g(Riz;+f%K?N~!v0$WI+3e=&K|}PrQfHSPhq5q!P7&4#N#vg zu6{h?Rqruee0{Rwa54LfbPr>@a9Zh(#FSjNpn3emTSQekIWoJvB0vJ9LIVO_mFr~Q z#*V|blpi}@oq4e_RC6$P+Fhbn5~-Z9=6Lbl3_wj6NVT8IHX`EO9BdBUYhVsc$BUFw zVrS-8<|+cSHRZY|ouOuZQMrguG6PRbZDoZ!$Ba`?Xij#-7&x)8 zIP~NZkA?7jpx6=mNcHZM5m=j~kNQMJ2z^ws3li5H`VRbpX|wc3uW2|4*BfKeWh_j= zSwf}=X-AN#lUiFA$c+k*>-zOB87 zBsRAxdj{y77+D2UnK6bzCDN*dGGMUAbMpe`SI^R}4?P%uf|xi(Y` z#HimIGB(z7e;K&vtH~y!7tyrIp=N1gsc#Kw80G5{$aZ&1laL1fOn`e-orY zRD_~$w;L3n=`{?3#;w2R z-2JV6QsQD_v7Cg}MJ{k5c}=K|nC5e3lcMtoXlVy_wkIMK6{`I{O?+6S8I$+5Stgx8 ziDVcY`0E$VJLbu0LjWYGUJ#yU(`BO`7(PgBH-26H9OMxBcHTV9`yNLJIP!MH8?I@r zA~f2l;I)P@i@u9jIfg-}pNe$?16E+W^8^5&Jf`C;gPD`WtQcNv`UY^#}ZrGQ~`$qvkomou{~rIGp1?&eDjYTL_iR; zF&mFtJ~UT9w;r`OftK8_HeW9yRiy(HSxOTZNbv=qnJ5dp{}Utg&eOYQ*)E?Tzl$m2 zMI7O!X6jt&^jg|3`PojuADW`x4M4ZoET9F-3+Is?RcJMMYHC>77Lo{T+AoT9bP0&$ zwEbl-;M{R&B&Ni{cyuO}@3)DYZP!(awkpw0L{%WqbiI~P+GKaQp3IM;sB0r?n(>Ut z7|80|uu%y9s~L&*az$jMIqL|Omh~SU%0JmA=^0TRq&lK+Fr^2Rb<)(IU{{GMnkX11#m|O)U*L6Ecc=KqUe!uv@D#l(a{3{G1IEOObgL( z=B1xDV#$IsqeaMt_3Z4ZwRb7B1IZYp@39~0QMgY79wOO`g);+oB;m(*v$|8-dHk>2 z!vNJ%i75ZuI}m=7C1liK^jB7d(4m%;pRvKXeMUyh)9B}dM4XaA5$0`InfO9m+lcP8 zu}Wa;aXKqL;mtUj!7Z&N&sSg%&at#2XIvs9OoV2s8jFh@!k;rQJ-(!X)0@8wU1t^Q zTaYxt%**>tEsSBvIY1ZD*G13rDdvrhdKf4njoOudcM`G|^F31jQCCGef53nfYEFE;SaGo({~uZrB~F%8GQ*T zT$xA!7^(i=Ai43TX2<0b%gd!O|n+Px~^$0fk z-B@kud*LhRr3q*vrMdBJHivW9Ygeb{*H5yEUpQft`xeyQG8fc!{RV-f1LZhD<{j#* zAQO!LZ8Njqh^P-bg|EA1z*XS9ndn~njP9Z{Qj7&#|H^BnKeF}U@m|yKHexd? zp{C^ofwuL=EOd|tL|XuF-<5N#-*aPgd;|og-0KA}(&umHt~GTiF7g|90%cRav9}X; zIQqWaqZ|bj6lk~u?mp{?oKEv2OzYg)NhG{F+r%J_gDu>&?Yww~pYqAg%=FNb``1?W zLw!IzXNs7xQP58D>DuUNPf_mNDaP#wCTwy$B^#A_b8z>cWr3!onS!7Z$2X{&cV(s} z7VcH~MnHVZ5Nmvf880XrPFZgNDRm6Ui`*Tdo*d1Y<@Rs*T`;MBIt~>|K%>j#Zx$SE zbqt#aEBO$|^AlsPAJ%d!=y?x8s0e}ze6AHu-XLz@ycCSDm@r+JW^H-#f z4hDBvMyV7Heh9-D`l5m$`&UOzDgylK8`+rFfk6+1r~7=~PVYJ9t-`XZ0wH$SmhN97S_{TV~LSK2LTI>)ew$MHf(WEgSp<)Hjy45{a%mVc~I{~0?HyV2b5mw~jG z`43}^veuHJhhvV9xP=@(p;rTnzzQLf&#Bzz*xwr@y*${|^5H(ezdi^XaA9p!{@xt) z>9HatQdAxk6ukC~MuG~C7T?P;+u6?RGKC*k{KCr6284zUOsS$mXk`OsuZlI%AeHNa z;Rkyr_N{5foFdHO&SAd}Ev9@Ja-P61kzc4=`;`mw_Il-Zs%^ODVgv*mtpLWdbkF9^ z2GV3ljt!bY9h!E)4hiYmiEu59k#5B${?REfLlub zc@}c{h+F=zgKHQY`%GX){;a6;{8eF|%M})f{bAU|(aoa{k#+!Ja+A4}9U%|j@*N4r zS%j8qF%9o^K=UOWNL9a`Q~X9-0T657Ia?2b18y&!#@fL(U@NC>n|PI;PN6E6(KnF zX{1j>QdM}g`D;@BmeN{9i>?DBy6?R=6=D4kL5N z9H$Nw%n~`;dlf1MxxPaunZw5F&e@l^J)7{)8yub!9q}9pl?mBfER8R)=iEOBfSCo& zNthhhmDjQgueXbK5R5@k5SLj8;qLSxy;=q}bsW%nVx}5facrYXFnE7K=Wl_`*fxO@ z#pi~O-k^9n3`lxon*bh?7DAegPm)>mMciPUq&T;;EYPnqhK;b`*w3u(s#zDpW`)+T zfP!3TT#HA$4`jMZ6eT);mU#p`0K%&UTqjiKJlyn^f5iRA=~q$y{vo@+xct~rY z+Ib^}PL}0e8C$|>Yq=IIX2v8<*igq&wLf+|bPN&$7TlID_V}R`w05hJ;xCEWzZT00 z*49I7HV5WpG87JbA%K-&RYGH7sB;HykB*HiT|d+K zqAwKljVR775}7ZNeFU%rc*5=4K@2?*&`oYd(8I%{UPO@-V|!+sv+Xv%!4zZl1@O4f zua3hxHQawpr99CdrA>sT@>B1p>bktXO7DxV*aql_d{v&>yTf7sI}<|+u55cocSeaYZKbEKzvjuJrY%r zP`$c|bhDRgchQ(4FCFPH00e~F6$pukxwc0s#p;?Vz$G>YR~!2d-{ff2k~m<~jR_W{ z;dXh5G$F;rTMHl21wWtE)4jin*xAuZqEK48#IgiJ1uElXrhXh5InT9=D?@|SvdYne z*G%q*n6`wHC*RGY0OF8$;pEP0TzT|GtNfDt*q`@@SPpu^>7*}wtG-V73-cEw-#1*x zN8TM)pWkA{_ozuTbG(1o!9UlxH8|NuZufVlfY?1z5q}s_*7g~#*)Y1WgpdFkH!LUn z5FDxXd)3($&WsR^Am<-ts8pQETk?Q&?t1?R7}NK!0soV0EN~Tb1&9{o+xd7(bXhXR>my5vKL|6b z04-+c1P$~&)Xyv2wB(_@)aVkwCNg^DfaO(e5qBZq7|}HpCcYtD@sC#u03Mi(dF4o4 z*&GA(mOFlo1cG(C>36uJD_$FKznd`r)d~ZIGMtFQG>w3bs;mf9k%&Z32rU$GOa7tf z?fz&TzH5z}TBn{$s}2r8TAf&YuC#$*qqn5+o-!9bM)zzF^+f-)Vtg zioPg_dHiu_G@@d3otm0)B1INv;gJq56ip{#;$P1xFCS{hpZUR6Pi z14;`*?=MCWLQ3q!bcgv+D}+@j#WX+4)d=gmel1_;!Wi#^Q3DqTIME@)c`uSnC&z6M zK#z>10=w9%rjo#HmwEH9rCf9+p6f6r&k}S?4Ii}gaew=_4pOK?=)@vs!|?0c`dVf~ zJi1S1|7q&8J&`4+hQcadfej&PhY{l*3*4S((M0T?4D^GJPc=Hnpw-H}+^0oyb1qo-nmi`jgkD3J9zQ(h z1p_a-cDFm4@83O4e;KTX`T0Xc$f-bsBR839e<3~Ig`FI*J`vL_f9e%u^e^E3bsL1F z5UyY_)j`3|{2===61wGr#r%?fD&~f4M(aaxI>)zv&}949{;M>}=Ujj^Mob`cOo+UAeq|2NfgS*-=|n)o|;*S zf@fz5hzC8p)H@~*qT#<`lK(P zuE$P$E46eNOyL1bURb^jr#W4)ZK~z5I74_EJ~-jg_f{_hDJK!YE2Cxg*&IRenRJky zMg7_51?}rPznkU6s&lYvM3r~KdaVAdYH!5uZbs{S$Ya70KD_gwuVH91d>`(wvtIw#t=Dj=J_F!gfqb0cUXXLmNu z#OoL%_pW5Kw4l^c&yA_)Ak#W#@_v8Qa3$q+#?p?BKMD8W%Hz2m9cCL@U2{WVUb`yK zsERwBHIw_=(HJnCT|>pkJh&VQo@vr!mzL->`kknuAs;{uie?aVS3{0YKCN6c0LoS6 zw8W*N<`>Zbx& zLWSsSC7cCK{CQaq^<&`wUi(aLKW+2xi zi=O5jV}A>6bwwtcP(oN`7$}<}G7X^tU2T*}CA>wiJOn*eaQv)nJVG-%J1Q%`9JkF- zefLnxq?O9eOMQ8_Q4Eb-pzBpJu#v^3oL={}KC>R4NSBJH}ll2Nb=a^ zI4YEn=sn=ZNmlZNHz(coOAJ0m+NI~a=s`dgNYT?tj@(K^@lOrbbuwJmPBbBCB5fNFT-kkbw=E?f)9Ju1+Q@2_Vh?14j z_gPd=4O#~z$w`d{9i&V+ ztFg}KJehdJNDJPb8wxCbwwyvGPvKJi+-ZQbA{luSmYbZ@7IW1tei}NUuZfMzyu~)* zB?D)Xu))K^sgC?Ewh=DK(myVW#LdugA2oIJp;E!H00s z$!_s;9{nLIACB@%`s!b%Pk$57*oL2{{|>Arh}zc2qfP2t8#!yb^uCgqQsk{(=uX); zSUp=j)iPvkRG7lJiuLgc0e$);_w#t2bTnHi;v7@_whhqnv0#IL86y+A19}YuLBOG? z`x?br*mbKO)bcqA690tN;hgbtqT>#5x#L#*#jd;%aCW)!=u89c33qeU#*Y}M(2t*) z75b-st}VrrES?tqT}{EHFD)A04>;auC?-T2W`2<2myBB3v!3?FsKMMmXYLrp5w^Gi zxW;d97b^Clyo^}Y&Az09BrSIUK+9xRhP_1oho%_BXj88%?sm20@!LmXk8399B|Ohj zgve=pe*L#zqPNYQu4e0RY0p{s@UbOJDDV`v&Or$c48Yznbn>e(Zxy7+=MCGJk3MHQ zT!3lk7nW@&_1{FUA9tyrd?45x2HT{ z{Q<;6fZo{eiRN+T}iiGHHGF`?z42jt;d_7;_FIVaw$v3G-EQpnE13Llk>h?u9 zD)0(lI@z4s@4`^oSAFfTGtHXM)c0GOsXgj{Jn+_5??r3WiB*vF$Bq}=yqP=!qA3E1 zq|#UWykTvuU=x~z-;-$hA^5=oBO}b~hPa1pF~x_8uDD{!TkOFC=t~ryvJ`udF=VY3 zn3IWC9mB_JY#k%_Rn2kR;=z_$=p*?c38ky5h>VZ*z=2#`(lJ|&)NQy$L;Neu0XI{Z zNV=+a)+(|6?=8oNXUOr3MkbM#s)9V-P0%29fq+WsQUJbgvNYl$B(J48TP6!&Fr%X^ z5Ai&Ezg`YK)hQ*v#*cJ4<6Ukfp)Ish?45^#)nRO~kk*>dU!HaQO=)b=a5=yI6H|yD zyZ_t2q!5b27CkyTl^=$wD>-Gfi@Q zaOo4NV^muo3b5BuyGs(dK1eMW5>MPMjqNc6E){qcd4ClsPt2_=zsBpJF@BrQ^0bQ2 zq<}PMCZb6zj+WF1o@PKTdNrb*&M7$YP*?QxV7++U;~o!s67MFhdBlW}2lZ+I!h_~j zS>=5~d$w-(Mb9}p3z&3oQONVP9kPY{fzjchBOthj@JzJUY!&P$^ z_DgpvDPf;$3XzDpsJt06?tqFME{p^T*ffmm1`h^gP&9G0wUS* zKE&l3x|#jro20U&OGA2&>M2gZsQ4$-u8n{rFl}@#p>=o*447!+eZo1$5h68ShQa~? zu;$yjmoQagKWyENmDy2bk9L22STYFCe=W7@c}i146L-o}yQiI+FI?T-&%9~i9;iGk@9BIrUo9A2 zynhGnh$3%7h7x1NArZo`XMt+j*uV^gT2tzjx@HxX!yWEUoHj^N>$h)^&Xj2PByBmC zR^+fs+mg^t#|(F`I?abiMAf~!h(65kDd{1@uWi7%TK!k1uMhh-=5KlqfEVK#A#dfT zRv;qCw5VjvetMr>J!Ga3bD!+_A@uZw!|wc=L3HvPq(*Tg;qR)pK_Pg{Mmz4)u8tfM zAQ^k@iShzbSz@1BNokSrm#w4wIa6xT&_dLMCFm%`3yenK)Ts0P5*Ix zf<5P9g3f97@^r3X%i5YrRF@asVl>C;LYb`Y!ct^FhJ$|Mto<(vHpkFLh}ijK$9wbr ze$7q!U?uvH$$~a~_|4#!K?oVGb6Wl+$Ia`+@Ycm*K*9jWBQ-zxJ^AAC;hq}CPb@IM z-rAC+u)sJ4`*ugy$$M@-b&&4n2CA7UwAia{%KQMXf((?!V}chP21=#J6Kuf!t=w?s0+l&H9 zyT(Ie!DDa zv$)PSx&;&p4IB=Z8|9RYPryB3v*z|2r7x^7$ZORqBHV0!(J>Q6g)3+Fs-7SnK zCbNwa|M;v$Tnlr$Fi3EWAOi6sa5|+j*_suyw#%b z?tQv-a&I0nwDz>W!_A$9L?2pxl7~;K?Nf0Mn>W^F*lmGpXn5{ucpX|TdnK`%a*L1M zi#M~Al>TYe0n(oko;Q;ms!whi^KjKE((Q-A8Ic4u@?M(Ww(PQE-`=o!lp67qK4rBj=! zjBH?XTT{czFqrdcYi`LS6Mzfm?f;_Zp}#Uu>tnvh*LaJRn&7yf`Ef@j3F2J@Oqm^b zHog+vCo3t+O~wR^OX*VGr!JkY7wsb|prPfx!3rhTG;1|ch%TGTw`=>Wu{MJMRCl-9 z+QakDV;8Zia#LQvf-T)$siRYHn;}&pwx1Pd}vgt zcQGbkYz(wx#XGb_%6stzw+m$0V?X}?oHfX7A2 zI3sci@;g1MujFM^Go!Aj5;?r9Nn9RA1{qUeiugwvbiH z2`!mpi#D>_ZG9PkuC8V**ekjQt5`AC!jr}G{iVMUJYIH2-`LJ69&EO%Uf|x!4Sss9 zp;3A?Z}OS*cie)ULNWH=JTp@e0vRWLE)R)3m}j{0Of&mcnaIou;hCHZr5JU)ZB0p9 z)2DdKT*CDRjK4cu1qj1gYX&m2bE>p;J1gEI7u@Pw+7g$e&Hrjy0&uf0Bo0xhAHhEe z|FMiPCnjmh+PZCMd;eYD9k5&#X#FdDmWr+GLWP1pou3~8wC}j?3V(l{iV!{XI}z%x zuH69~Lc>cvPT%QMd<3;+ySj6EpZ6U6Y@$gc1azgtdq@>ZM{DaVmfVexeM!2$To})v zVYl{~z>c!L^psG)nndh5yBj_5OSQ37y&IO0e*~!W`*do?b(OR}#S#c_3)$6P!uF5v zaCJ_s&nVYI8#~_SDr_*&A6Kc|1 zannf9NT))Bxfz}nGXp8%cF!OEbX}~Gzw1n7xck@D$o2I42V)yBqvwgFY?U&$?DGr$(HKXkYE#vv!hc9 zl+3=Dbd0Oi?=6{4Yu;ZxM*CbZ6kCgyZ%ejxF0=rweM?R4iM*rDzj+3l`|v%-xgF&0 zT6@Dm#Q6K{c6@0b16j!?&BjOXm&ShU7~$q+zpe{t8}mBFKN&LAUEYEdaK)92OexgYD#bbX3a8gChXpG7&Ha7DwYG8*u02Iw9#iX9Xx zYqZAIUzJdG$KYL)vhGcu@dW@G4>V~~dPcF;-?V3Ci-7rVwx3w{O=emkNg3-Za_0wX z>L&}l#mXutr1?O0cHciPXP;n3I;6Z&Za@k~o1DOf{tki8`_*SUx>r94SS&Fk1eu&w zd<2}D`Q<)BkdK2ozdJ7`1^5vl?{+bgOooTTlq{4y02$pY@bVT9u`*TnA#zU|IvnhO zr)D6OZ0I!KsxDQ}LPY>&v^&e?;}iHu=LPR!!zIhh`h}R1(Rp-Vp6s~zd8byEBFB#L67BGP}D!?i6 zxi_Bp6$KQlf)s7+;$9{95sw2#&JrR(XGV0m2ZhWD_y?l`c(tz7(VmH5>GR?*6(M+OwzV%=g9y&AWYrFBaGr>_bq$&i5Ip zt7V%pE>4Wj|2How)Jkz{SM|bgc91xA!pW|Il-MzGrO$s(4v1%4kY=}^EP<`y76DU@ zEh*GdpIjR_@ce_#;N9*dbSS)2AWD6%7?;jNa&$ipy=>eb6`!G_RUuz$zFh1OoxT4e zD#z3XQK@PBsoJD3jK4P>C#x0{7nPsu@+MdIuK@SM7Fbp-cOVMXHodp>CNY~fV>VQ4 zpgXBzLyIAA8jFc@`LOiOkNqs8X^dNkLYFp75!6eMw#7ks7pDpikB(0Sn*Btbfr%}) zou-G@%D&VM8cl$&=!41wB%(uoK}KLqK5*ZO3qSz_k*;3n6nz;sdx103&t}#d((n`l z&cv0gE>qQdMSG4Z{_il+@LhyYg(xND$a?r@^+S661ov2&(N^Du9#?9;ba!KnMZ4lO z9qEshKaCF310rQ8VFMi)$z^lhjSazj$JRjM?(j_q65?Uxn#b;UZ~i|A$*q%;giVY0 zQgxZlmcH&iK!w-Z3(&ZTJhSh*qj0wznX*`Sb-7;L!;KwQq;HqVCe3E~-alCX$Lh~u zz*;lGnNQgfK`O|ts8scfdL zY;x3129if^+Q_3ux}*Nt=&qmssnRp>(0Jf&uQ_9Kvuh^Gk24KTb^^%S#q@8>9Ibb= z_IeRFWvpiK@#+n4k9E46nVYHVJ7S1mb_B>|puOg0^VI{j`}{6+ovsXAG5oiE&|&7< zAB`_QXwA9(Vf?g>8vDBl{wGYC2e&78MMfTt8_5)&x9BUSrF{E<#-edYTiqfJ~ZWev!qh z*8n{XbjyQF!Gh)nXqIA_gS&1hp2@aAv;^@^yN?+qf`=Xx_B2k(tG1wm1(-74yF+Gu z(R-2N@A=euaLv)BKVSZmYOF>v93M)XJS3>h!`5HRLSrr&z6kJP>87M#O;LA|^VKQg-V%;Plp`~6K{ z1;_0aS2tdvl{9%f5_r%r0O7vKgM|d)x$4qamSC*U?o~}p*Kpoihlf{hIRJ119s{s^ zuKTF~J@@L0_cnf$qwAd$#*%~S)ja|paW`6Z0VM2G@3PiS0=O#l`Q876r8f;{Gk@Rz zJJUUSl+w~tw6%00bO~DPsG^#ZqP4|RRZB#qN(iw`wY3(l79)m?of2yz8o}77RFSn3 zqLxx2#1Nv@V#iG7X|7>r$D)4|HRV=gyY9Epn0n2%VQ1pV z7~~6_Z}I53mt_UNl}4?`XT5s9prDxkPMum3VIXdz+#n52lnYa*+vAbEKpi8>Oi9Oi!0%MAKil)C?V=1qei`*p31cV?&v>pV21#*tKO$-@*(K1v_f3R zU0Tda%G^UQDOm9sZvZ`21t1aF_&aMY5}$hsa}Pc>t-Nll#X@k+%VFCGtUSOo)bv~a znM!}SD#4hxc5~uPN#*kbh{XS|u|TS@w;omIIT{!qmEu`CmzlHX9@uHt5Gb$k(xrjW zHSP4`@d!WW`WZFbqw8Ru0NQ{1zPB%?PN5au1^C)@J)x?3sttv3KPH^fEzgPa5T{Kh zg=vym8%n4rn-75eo$zr(>Vxf*)R+&BO1t2qcVWB_?`>D4dHcm-%s9Wa2$vPRT!8Zo z^b(w08d)aQ%90B*LOyy0&g(Y-#_An2<8TVF`N~=+6}a#gRe2{arCj_y^2F}Mwp{F} znCq7{DOByj57O{21GX}fH?+KjmrF)SXb=JlZmK$~pBfFynldjjuW$kMdRqan+kacj z%|&pZ>a`G;6no+4p4rF-Xn$vjP+biR6@l}fjKF8} z?9QX=hfbiQ?q3GcLazT)*KzddkigJ)D0RK+zkR0=(&(CwnlTq1HqME@0KP5T{~0H1ph!jYBf--4jKQpn_o=X>$i>vj71yLhXW^0?Wld6=U&WM zHrDl>6q-)h)n)7FxMWY-l?@&Gb7WyF_gbKHywHd7lpLmDp#8bxGejCApXad$TzcOJ z^74_E?IY{!D`qK>&tzPyuQ%mxrV9$fO!x-34BpJi$v~qK!NE(4cQU_Ur;T(ROqCoW zmLaxUOJ@^9fOgrfSMsZp#O_8(;%&O#%E5V2Aw5_dBL$Fr?xQj02YWs=ek&1&@_C++ zsF)e{HIvNjIZMUF6pFQUuj zkD`4KRX(dllqt0^ypcX3$LL2c1RSF64U6pX9FCdvZ2C~gs)Xl49SJsTSkF_oc4}Z| zuQ`x7=i3yvJlg6_##Xt4;cs8&(WnYiCXI9AkI#9xDNHmtXnowwk_7lw5S0Q>n^x9_ zl!ufPV^O$Nu+Z%+6X@(j&8{ya*1Wh7(}sk;Canbrk&RxjWuFh5I2Gp|=ys|pYf9?$ zGR^yxWR34}E3C>(wk>bB>dAyCwZmJT^Q#Hc9*1jw%0iuQr5I4Ccw6ZuDAFSWHe5~m zM1q~Z_|4dRQV1vpcbc-5^5#b3NQX+%e|;{3kMDk*c5J7$;UZXJq->*>F)g_ z{2_Jc@cw`_U~g%Z2PMe;6zfh3RRAj7k?5F@x=A$i$w5D((`w;Vspz*{*PnP>A;}pA z;tfRQ@WVE3)Ehhmx3f8qXpXJp3@rqHxB!lh{j!Qg4tK6}S&d@RVPY(aw zh)UU@_NO*FALzc?*f-&_*7t0u5kf**lN41vB#|}n36LW#`jH%S0i+bCM zE0!DWqsz2wDygOJlHz+U)Dcj6RRv-efhj+c?|{4oY5u`fkI|XpJxs@XIROch-!$Hg z6fz)#7klvC6vcuJ#MI!aXp?E2ayX)PPW0nz&Y|~ydfiDAqhlQiePY>|d$Myv* zc2(QRU%U1VhkqE}3bSVG#lT%egd`MM`iO_*`Lt6ze%rF85xD<(IX02g#b0qIrZZ)w zCFaPu&?oBgT>25B1|m$C?Ye>Tu@?ahk?{$_=|HyMnJ4t`D~5t_zACdHT4ISS`(~k< zbl^Ii%TxMShv+ZbC^67ED@Oof=zMGh}iLg*`Ct=`nbLSBXRWWBd!}@nPI(uTgHooAaXf1V75&XN;({ ztZB@1(P}q=xcjrmA@?tLkv`Qqntxq8ju+AUGuS%K81>h5aUV@_}GZ|QE z33r_$N8HUPx7O8D>$B$^Sc=Gq`m5@Vkt3wd%23F##j~|@|1tLB7V0>(|5a}A?i+g} zL;ec1d3HY01{jQm=wOFiHNs%OB%6Jy1EiM5OzyK)?fB4o`!Au9?lFLM9r!sVj$4e` zmW_U}TN7zB;Cr(7fA z9!)fI7Dq&l;-{i&zMRC{d!{Q?97pmT>k7mu5gNN?V#=1JuzsJu3__-d5XR-rnKz>W z(8&qh?n!(1xHpzfxut=oS5Z$U%n8;b8_M|XN=TnogZ0DQrCOrAfPJ+1cV1Oaq*~@p z8Y?{@+f^$3q`lW^7fGnwu(5AhKvZzyRB~bcie>SbI~M+sfpNz1>_cU_*cvF&kUw9X z(`679U8^^LNt!S01kg(1}srZY8uX}n4sNMXEKsjzlMNg3@fb|H( zr~MG;l*gn6Z+&*qE!y948@ablRQ0|&k?%wwKR?vD$++{)fD z`*>RyKyx9c=(foFb?N<^Q2Gp=zbgJ|(#qN>DAt>K54Z3_&ll_$eK1vF2vsdttQyP5 zvD3HNI~u6UpHv^s7pGcFKV&bIxeOuBMjg*7ckKYTq*kCXr)Lzjr1Um=8f}NM3ESP= z!>RCY?nR)_&lq1;UyNv+2wda2y4az~*SzB*!cmy;ZOjigp=@5vwc1t-J-~6Q%f2>< z0}+6!TBY5OXx3Zs%P5ur+827Nv8yWbnf0ms zH~L#?3B{oRn`5-Y7m2QLB{>9Mjk{@x3v6#R>wAa7e8d)8+a8*RF-z0IdL)$Jcld6e z8&HZ7wrlIA6=})=mFLg)#h?yUij$o7ZceTJx6g4;YL9Wv*59-hQxWHfXqp%e4g5{g z_fYBwvT5rX>pL8EqsNN>bxJVpLWP?|JZvJTd=>Mhg}8YP(3M;T19as?uDAv82{`PS z1GPUwBRfgl#~xw;K?YO8m}A!Jkc3ihZSAbE58F?pOg_DTRKRc`)8J$}kvZY zY2J#1a>9hxofH+FVV(7DK!Nd^-?Dle?N}~}TAj0F`hQB+U@ZNWa>flgsB(*}cJrE- z4LUf}{cy{hzK{!Hig&o_glS~!uTyK?ieEwza}SVt1JIkKJn8G=ABB{ZDJ>jAvpMtD z{o8s5Dvw$}z@9z9W3o2^iQY_-kR28KI%W!o1Ko~^nt*+xuMf0>diiVaugT8wW;*|j zTWio_eY?Y#gqT$IYw8r=$I0WSkIAh$ofN$0%g91WT$kCXk{;4|1cK7Ni}G?5?RFQO z*2mvNM!hEY5PK`pI$1vZ^Ui=#i#4D>6{{x#Nz~N8NPyh>D#xBmH`Xhb#O+-q@c3G) zT({bHwQ)f)X(Xu76b!Rt>M6CvSln;PB?8(VdVD?{jVyERFt`r)7o7O^uOI66bmvyp z;AD(N=MA1fe~Xj)>YAm`^WZSvnb}-G9nhp@JfQ0))cPFL$ER&Hz{#WO&`VAWx3^H% zPATxbDgdaYpR)%@?o!|3hO8##M2BEGFls&u?;DtoAq zJFx_9J*7MQT6+l<{Iu;w!OdWZZm_m6Xx#*6mk7(*wtg`3#`Kcti$0JCzSO2gVV;A@ z%xhJTTNi&5C@A)pKYuEpLG&Es4gLyK9^zeCO`vk3i3aX0a{FnQIG>#dId<7RXc&Ec z=*q%FW+Bhbxl zjq=pE#vS_#L4k4;gXRfl^yv#HC$$X}v(|H*O&x7RAb?0_Ek=tElCrhXO=^li136KT zzJ7{?)S}A)=%Vf$OhD|p3qPHvCb|^=Wh`N^(+v%j#E0o14FflKPmeE6P{d#3wz8jZ ze!8{PC{Ppv9x4doAY#QWz$0E;Y1r%d5yc2;8}RMhbwq|c-sQ|hR2V58&d;M zBLAUa+X7!mT5R00RB1NWn93!?Gxj$aq!|0P`%4PZ0Hfe+RT=j8K8+7s>tECx-@}k6 zB2@?=#K>q_4SPua`N7n`+FIy@2Tb}8p|Jda>0DEm>eL0D__r?DQ0FGPA2l9q+E3>^ zYHVZ#{h-!U+nSs4{pP>{;D;|c^VqI~3q)wk=*HU-ia(nrDLIfR0J*9V;jFkR)=kFb z3c)q7S)(3vUD{H_uFKlR@pmEf+8}ZV9Wb&an&&z|Q=4<_19EP6BRDxZ!wY=n?z9T* z1dQB37uFgZfxp%t?AaK6F|5HtlN#mg<4OV*U*!N*T1V4Yy-0U4m%N+a*i8So?+kz? z(3MA0!3Bw;;ts%k8oXmLxocp>*3pAW|J*aB(Kk*HkLeRMYiqHMUEB|O=bKY7Ym1xx zjj0LG84W-Q>b3zK$ZkA|9aK?ep=fd6W29VCABcK-5(jYBHRpRAx0=^KD8v4u1 zJ_h)8LLsig=J^kdQAe%L#{+D(IDB}lsqp5uM{jwEwio{YKZOFkg2L_Vf$gM;WHL;8 z{_28kTW&DpR$YTXrHkj$pyk!jTG~372Qnd&7u?`pA8n5^wM$mV|JzqH5pQ-VT1%wV zaV^?d^r(!0C>o8)LgDG1T3Q2y3;k?1G~dqUULUT)-DbZHnBH^f$5RUxpX+9I!133) zSGB1xr9=el8i1WRgGtp2Yfd=2RE-%}KJIF#BJRJtK9J_t>!fImf3spO8-&>LrY;@2EJ7|9a=j|Qe4^LT9Q~+f@p@@S z)+8obN=ta<7xZMBqF#NBCta{K0UeS33Vll1>xAjg24qE>oxx0r*iLhqt-Fjd3h#RSS7eLO(pSr zM}VGT;ZB|{yjs6&x|>G(Wpep7AC%WQadJH`O@7X$nJS44EyTWYbJZ;WYu-u;3Fiq- zMWr9!4OI%{EM$gNme^Ca!5Xw2`&LA)_52RrE=#`(T{Gw(yp@&T+Edy(eWz?n30L-d zF6Gu;e*G7*h?H`~IpA%bhJxb%W+Xz%yrI*JQrDkur}EqK%jVfDZmy%;J9Tg#{O`gG zDVN?}u|6A6_~_xmNBj2etBHdT2M1%|sMyn}jnq`F)WdZR%s1EI^XZB>9BpIGt-#Y{NCKFJ^X!BA8tOg6~gcTD@DB)Xo z+p^9z-Ps$Vd|LBInceT1sf|iQAE$^v7moM?D96Z@*?~uOqQnHO23O;pZL@Y;7(Ue7323ku{St zj^`S^`v;pi<=-`wU|gI|&5l_rM|p>OBMsee2n;}|qX}HZh=+Ifb3g36OuosxtFreU zZk)f{bw)i+X;xaPBXn{?egU76tP5VRQZiAAQ?kKL?pmoHn-F9a3c_>bEFcDMsoaTP zDk?p%T%f?|{Az^t8b}E_R0wf88<$*5(=*_qX;9rH`As{3$957?8(IF74MP`x788R4 z<>}bg@`8y)=ehd{f-~i>Tm(*vL76V!5H)SVuTI#YCKvv}D8Z!WpIH|R%#Q1ZIZi4q zAi?(on($d)uDi@f$fw5Dx)K}qBZ?7gVA3;KrFI1MrMeweb^=*hQAY6ADJ-(Cj6*E& zRVl;k>%(naZdL&?;+PC~0pxvV#gduhtf1h;0el_d(A^v{uLWg@tPm^6cz6vIyTSg7 z{tepFej01XgTE%3Nf}NA8v5lN|G34jdWPb&gd+}I_R?zq;}y^OVcvYazWtm`&n{t3 z;MPlQiAyr}2}OQHY?be*o7%3o^JIFXO%N3p)dJ}$kQ$8f=Ei(@J^b56?((Y#W09!f zQgLYoKfqx;Fsim|n+KDL)@95vFxDVll^YWPJe6E`a)!d~>to zj4Za0;kiz~?x7n6+}_5sD(6;bRuYIoTO}&TxBbe?hx{jZZFz2F=aI!3g`-N=C2pSD zZsV=vL}LEOqna(vzTnwCCM~ba@RiCr8^EFSw2ej;MZ61D-@^3Nq?SuEaeu7GSU}s^ z7g&&HoyGJZ)Fgcl`iU5wcN5puF@vgLmL?IP)JSKQPT77&iz+=pvhov~J7(0#y$cx~T z3{U3a)D4$tEtRspvrPUkhMI5B=T{HA&Ld50D|#Z%MqO})m)vM7jumL6H4!TUb$!a* zk(d8Z9yBoEUnS-B%H#pQkQ$Wp-A^qIsnAr%7Pt~oXK|hr>cZ1vBWQ z=4loKXr#`IaiYzx)rBiT#sMWiHZ_h;*hUM0pf4B5Gj`0IInm%k ze#}cNwA`Rc6QR<}t1&Xd+1dBli1qGUFpJBdBq@b`sE;3d)R^kVG`0sO9y#DS!*&ZN zjgU$M+i^@5ne5S&UQ*9Cz+M~eJgVat-L;< zK1CnT{Q&U7Si6g?>Kcsl6(9l8-b|mPQnF72N}GH$Ir8MkLD?}&$f3KR$BJA^-qb0L z!5X=Aben71c`Mhyl>JNUTM()y^wa<}| z1`k`~wIOwSWK7iI&WXcuow(`N8(nq4zt+H5@;9xQB7$rQh#epv);FSqBePk zp^FxRr-1FHval>Sq8dEjlrt$I;W|Pqc#AHR8FsCi{3#Q??~MQU6)hi~@C+)I~j)o&D&k zyJRhZUD#|sIRFKD>|6`+uYxEf{I|~m_2e=eJCk7zuK?eDy%@wr)atpEGp4hVs=ca5 zkg=K~zp5$NL988j0w$UO&T=@*y+LLWxSFh`>~D>zDPdyLpymbPDMq%3e`{)P$sT!< zV^6EUj?ixi6qkFDc6CIQUaw^Sby00fUPly=3A%}q%8;`uSWe80!W{_b?=vv8F{v>!7fL@Z{>0E55 z(^y&O+RdoPW;|8 z!1#*=SxDV=;8->TROpnxNA{)P1aH47TRa?=1zEtFsSVWZ1^YU7$vcN5=Q1ZY0ZYrp zPb;xmlfd~>K70B$CoGJ0Y%u27rc7%;Zvc7n36QmkxJG+^?`eTjZyCncrpl$mOr<<%Rj1~5@l)#ENB<+9#kiMSE`qr;%r?eyP|V@NBlv zoMSgpjQ5D!F?)lG&aY~XALDm>i-Vh1`M{nWQ#>wk+xVd9&HO>DYkZU2^XCPlRCsD$ zX;VCVl|Cz#m|W=NX;uV0Z{z*mAIBuHDOIuQyUGBHf|y%V7+Pgs)?FV!JV43GDD7Vp zgws7-J^p4ZI+U`DFsE7=H4giIz&t5nSk;tRV=g8@Or)GBoMp09n$Fj~O(;2iYAUh@ zUH#aveprpE-i}>D)x0XwC_pY)H76g;Ul&J_TN4t2&2;c`eB6ok7(X-Rg+1w35uaRv z*aE)pXN!vv9BA=ERG3>@51)9biWqsF$F+!4$-$w6bkq&PBXlaJ(2Z_U2ilAoZCZH9GUGfx!Q8v8dzJ7!Vv3x)X zYKHTajPmrXv-_ol$o2NF0!0Q5?8i2I%6&D`{w|w|(HgY#TE}5gj%_J%t)d~0>WhQ7${m~IE| z{lM(FsZY;Wvj2oc=6*;10XZ`GEe9x~eHy2-W3i%|8nz5VvLjSL*0!R&mq|yk9XgDQY;SM)|1nmE&yl|I5990R?XmtLwINcj*5Ag~(%o7% zP7~0^m0wy$$VYds6}h;K3%IO5v#EYL`V{Yq3HNb#Y)ENy8Ta0hw{3(rY*&M6F`ZF?0Vf}?5H9paYnjFuwHiX1>{1S zAzvw=W_>X5Rt+RIqj4eU1Y*m#UoU~+Z8AnBcBITZfPQ&=Ua{}ZQ=oT8PfPWrZJ9}F zJD_Ksm2*YJs!>-!J&W(u4{zIv5CWi~pqqr1^TcH|c-%vO@xOgC$8&AHDiSz%8)e8~ zdj~Av_uzYT8e*KJg#d6u+IeJIgSB3jd~l_PgLFtozFgG6fVz~W1(x7 zz1#KOl1VRS26%*1qbHEs40j6+))y_%pUwvt6^Le-C%=hDK_{!X7Z+#vnRT@qtlFSK7rR2>;&l7>Lg>cFaxfvTdnT(9{B(RYp7 zWC&WOEx8OC^=9h=G(t6rIEltD+&6!w=y7|&o>|u1_$eL;f}3fR>O~9~s!;M=R|b)T zEa&Z*#sk_D1S#DF^bv4^!p3jU9*Mq6akgJV{_A}l4DQYNF?K~io+BM{xpG|ckv{~h zr+#9rFSLVzl{R&QSE0!M85@2I)Oy&)}wY`<$oqN?F`qPZOZLf5ZB)tGkj z7=nOmPh1S`Q8bU$IoOsQH)D-CQ(i*t+fi3kA$kMDf~*^s0$a&qCj#C&lk}Ww!vK{_ zt4r&h8m;L{a5EvRp|v2iq9>*-(C|YL-K)Ed|9M@VHBH}u$C!Do9ICIOx%KoVtF>nI zgkP4gjhqxGcFFtkJ48shbC55kPye?jZNFTtab^|Jalj*6I z%{Ru<-jjp3V>CFhkuaR9uw}9dXi#)%PIVGBXt{e%xPPVFmXop-lEcL$jMjd}w@fSUtkS+Rsy6zykuz>m?^?p0Y|Z|G%_Ue`Re`O?(+ARaN94A z-Ic$jmbo+=`6lFom3r2!3mq8g(Me`${xT%NN?sqYSzIZ&I`M7pUdxWTgg03k zdq%>$O%lz%s|Yl>COF(3Drl1ix~sE$J&tcWv&c$H@D}i|Ll5ehkXF;ZFMRNL=?7|9 zot%C_&@{ucJ>#b;d8lVsZu{QX)wMlYWratKx%%c2V{;q;vUomFiBfD`KN=FX3J+B5 ze(q@4ydE@t|tDgFL^pUq2${YybGK z9sd4D!&gy6`pxr>O^`=Z?SM)2d!FY-;GXK=<9yV23IJ|x=VizQxV0|5R%zXRvH$0u zYSs*ML;2Bc`k*dk+vNJHcoK}-usGjLai%hA7Fm4Nc}fSqI@X}6;nT|TEOcDVWK}ZT z1f^DwyvPokZ68@KsY_SdwZh#xX{rD7$NRw)sd}_pE?aP^^>*6>$>euwnQ`|8(~BdJ zJm7*93O9BQjZTC+to10~zrpVboift80TfVul}fvK8*Wi`dz+Q<4)Q1Yx zzJ$dcCA5Dba!=)LV4OeuQ0`=_&$flbh%V|Ps}^99>e4LCM%BnoU~S+xr*ky zxj2Z!7-A3^sVy2T>4^A-Ak-NEm(qJld0)Ld(BjPM8GK##UK^i4`CjR{Z|~d-y)(g> zJfRg>=YNmg4d|1>&~M9yEF%7if7 z&-V9sNq`n?1in3O#YUBXdMh<>SMO%dgB)9GqFsU#9OqI@)*t(CUy9jxg4dD79gQhf znZe`rzQzjD)t?Koo~tGox}0rw?>E!i218X7*DvX~nFMzvw^5o%1yDpy%{1dSaqw^I zFst1MI5cfZr*G8C@&kbLIL8=1EiHqra~HBV(&VMgWIB+@tIhPxGli)_!w=cWN?z$k zvDddgjM{ilsZ{ngcJ(5n9{_gb*Vn4;sWebI>%H!;{g+6a<5l1FEbb+J_DYNBmAv3hgZ%A$@;Wi969p148MSD6$QuFJ(y!$vr9@IY@9ntF*>KIRuM3WJ z|KvFT+OPrScAiuvf6~jY8TrQ@gB!oS5gw~Mas7@U=fq-GQu^bS72 zq^IcTu$x(mHgTC2Qpk7j5lw1iz}m>`0+>o;DHMb9Ri-1txmnelQRCw?PMQ6F*}BH0 zjHiF@wE0!lc+6ju#PZcy;2n>`aTlbQbZwq3nZhD(w^+8NK<}2NteQ9oQKhWvIRMin zWv1%{fF`0t?!C{JMwLk)OY^ZCS)W6xcCg>hH|-tEvl~$-zwCZyQ8u8#uhDksUmex3 zfsD%uZ!g<47ksZatd1XZdN~wUpxC~mxB%>DRiI|4y#Q@Qss;-;pr)0jT}&vFu?jwv z-L#1>y8IG`zm$*p_rx42K}PUOtI$L8@vF&xhm}`!=_G|%%}@B*iz^y6_iR@VHU;Kz`*FzvZBzr&hQm|ec|t~ z^Zp}u)b(yJ{K(SN*JPB6RMh|yuB6byK%J*BbyRR=h`(I^33>wDYmDFlG}SmnwIp_F z*}9dn0dlk?bM$sS0p}0z!Eo?7S7F#dDyw9b;VD1_uqoGJ7n&apk@i>=(bxAH zuXDO0^V^gpp$??fyCX{WbuG=qH4f84{Do1Efr>yy&zEzhg9!wcd^ux|Q`Ny82fJBi zAOQL!+XVwz10N!kOOW;>G|NgpauIKYKz$5!JH}ORB7W3qnRG5ZN^6L zV7yCB7})V1Ybgk=y(1Sgx$E^n)K>pJRccw@y(V|cimRzCclW|LrpiIg2P70!qT74N@koMW$7g%bH?-x5N%)V8=`#5Mc<#FBGvj zTz~jBhNv=)`6Fjh-=^aB@Rf8w7g?Yd15WmvI|z0dxi*AxU^!l0%3FyP9M z;~i&HwKU7>TRSpWA2pI#(^N8vG6M2&<=QHa=|}5ty>3q=x56w|OfyXL%w8<+n1@7E z>{zpwQDHnd4{C%sACE1fWCM^JhIIkbOBQOJ^KATcL|FEc3U;iHR8LVkgWSF7LdS%S z>?k7x2+}*ZhyibbshfvQKllQz@PdLrK^o98`;R^M(Whql^A>*cvgGSyJW{owA=-X=&8qM@_H5jEI7Y% zkX?nw_V+rzIK&5t!Cm&pz&k%CsL&)shLJbUleUvF$K1USz~ch8emPeog2 zqQcko-MrpFD#*8J1_*zXovqdtO5(OA*ow{n8i`t3{%(7`&l%XU-o$_u*)}?mU8uPg zo7Ub(XspRv8Ti|f^P;A9BX#!)eZq~>@+lE!@2!xuBX@AT=SOcrugk`+50nB}?NSf4 z*y=uPZ^lpCE3A%>4VCd-WA8>k+D+Nh-GMZ&r~nSGK*b+~gpH%bA`7&^zz6suA zX|T)0VveG3M&=)~-Q1UrS{kfR&5W|9Ho^>OYroiEl2fsBL-P4M@WmZh*J*72cx?i1 zl3!39klyG@YvR71d24Yv2Q*Xjgqj~VI_{(R%8^%iZ;ueGBq_FLUuY(iSf;8xNrbs5 zhRgR_!0YFGN0uQyWmuq|l%`x{Wp#FxS)$LUl<955f5Ez>(&h#^52ewXy`jz6_r9U! z0Ol;_$8K+>36b_-ASS$y*u&^8bVE4tFFlsMYc+eq@~n6Ax0Gi4p`F{!O(crN=)_7oU{_ncg6X8=K^Y{8Ki-K$f+o(Ek|#G2)C1kg&JC8LcQmz^-&mP z$a(3cr?$>kE2=34(|sx`07kZ4kRyXo)Z0v6)DE6R!MY7&HnK~D&M}sPgt7miP@uB6 z7@IO6>^tQY30`O)F*TOxi;Z#tIwuZyI#`!)g0%fpu&1i%L4Ko1GT4kFR{L@8r=#=* zXPN}yK-U4O(9py0L-Z5!R<^(Mi;L&owk3^#-l;ckLPlwNAo8sDMi@oFp2e?)*@_)7(7XS9N zuCahFz~O`r{rn~N5?sp0|5I}l^~ItZ?Rrr@$Uyv2q*+=#@2TG0$b^8eY(EIlAhTl`ohYXc zZ-A5*w*aok0PNG=;K({0u4|}lb`S8QVxW1}%_)or*b-bAo{j(?C@oqII&ou9EkMlP z(tTyDT0U5P7w=d9m!~!wsZYuYr+Sw99OrE>wBB$FQAZm)L$x3ZXpK(*xEi>OWv>;<^M3kA@4{LlFgS+l zcEO~u*>GNr$NDzzk`B%4t;H3@rZa%Z$cepNw{mdsmEzS#1__yed$Ojzkxmy|$p<`s zho{4OrIR@-=Ou&{ubv5M^u>|6Y+E1a5e%-E6+AgYB>J)SE^R0&nOr#^fhw20_h_e^ zj(C^%lq}>#Trv3OxCYrMPdJTgrO2@dp-?{jC?(5`(9 z08~DK+ckFey70!HY_bOPJadU1a|pU!sjI?Yn)kPekc)|%9!lXpG;|`DV#AlF6ckNK zT|M@w)UGYdy#h+eAIlg77)=DL$HTR14Q)_0-i^4JoPY)x;vi=q{|qbIYiisP?giwa zaJi%y1D9bn_AFBMxb>-;GAf&JaF!5Js_x`g!ogMZ@6@+5gS55J;Q^SvKg^gJlp+=>Ix-b{-X3g&+iqwqxD~rlNjffoCWP zy3q*|G1p*;tTeC;Y(YA|wo3SKC`i6V~u zdf4!du-vW@KGs?d3zlBI1(BAKDA}!OW=y4N3fTNkPQT~?XW)Ak^hof`wq~AQwd3#g z^K0Z+jvg9WC7fq(dlYPJ9k?7*^Kwr~j%QA{sj}pEO1SCOI!4%lE4WqxaV~u6O$(9&>ZTuv zXnP9m=*!b-4+cX%&LhkCSX^i35i$zd3G}70PW4pKzr3gAu%C|`r}^j;D?Q=&)yY^| z<}{pT`9&P{gI)g=VwC3Gvmz(}vz0nUf@>1}6GitaDDSH3cdlcemWL{hR|3lKAeBmV zeZ`fu-eJPFp%yVV#^*ku%zDSH?04UGDk?ZbvwhNkga!^dnIrEw&y7usLI2RcXjjh^ z#*u4u)B=b_)QDz#1VOX(i5jIPvA0dZADytNnAP(V``$G< zV*WV}`IpEtiwK0$sEyCDrnf^VEeq}FT~=Fe5pcYoa37r9wlP+&K*|SB$jpjZBn8E4 z)o*-_?8!r^fpnm48DU*Mw&37CplzU^a~wU((D6>^8u{Ey+>>=Nw&LhV#idpodzUYW zc!QHb|Fi8}!+XzRl84PYi@C9oP+w1J^Zk@f=fY2i!Sh1ONfW-2vP!K|AD9p$^vQ2M z6=R6IVKGrlN`%u`hBKL*0Uar;qw72&ubB>1cARNoI4mu|vwRIv{1S?OXrTT0eVd37 z6i$VBi(LcBKQG*|o({LQ=zEVSGpY#kvSzMBc@?@F$>$pBx)<@th15eN5Rgr^RPv1naP;_vfq7p?*ruHS;s z#SecDT5F2^G)a(_bPjd#&r4E8P?+T3h{(y^D^Rmj01!czbph^7u?jd|SmlL4;o5!< zg}>*{i^J1SeR*>8#H-x@ub;L>dLMSK+YA)#TOyh3kZK;{Swi%h-0n5da3>V}cUas4v{~AhQN@Nc2iI zI37Cg=+v44go*wX*;aXiyPw|^%PXAdDD(6j9aBGFq7{k08L(tIo40EUJdpE`#H5YZ zD{;wDoFo*HxMOsP5Tcv39(M*C+kuibQH-(Zig3EoSHT-9%Qzvin~H%suxJj|gQu!9 z`x|VPG_rnt7V&MbXrbKJU?boSAP`}_qSuyF8i@aOL2S`6i-cB?QsINrHdNaC&Ir02 z@*4K})be6%khG$CNg(p%rGgg+A`A-(BPZLFp&>+`#rGI1Hm;;Kv774Am#RySf!?|5 z5=fl|D*u{3X?YAiWrNTqJ-<-p!RR>OADTHUhx>*`fwHV_*iq=p%ML~7*X)0RJB<6V zQ2Ro&h@8-Z^eIDZKH$y+<;o20np&R~`O<1jpKFRVPIxHP|EvMgEVo>DG9x8c5nI)z zdSa7ib)h3LL=mB#hL9-ni=(qpE2mTv+oA|FI-%n$~O4T;?3T2Y`?1P zMjEWcRB^?hP!SkaP$&jlz|qIsiVw9{bxiWKb^}-Wom!RW67BbxOj1>V;w^b(s?aV@ z;d5Z@mraAqTVB33UIFqGPe#b883%duPFp|}qVJ9ONZ5c@NAX}aFOa%}k13Jy@URZ4 zwDqa1zWa4&1Q10=J+=^o7Y=;b_yy7>OZ+ z%~}6((VnhH<5M_GuYmHDn&Scbq8m5DH$lvd*ZA!2%@`}Y((mUrj*MHT-5j2~neU#? zFjjWutUA)O5+tXN^ko`ED5&^IMWXkVG7}zH3~ayK zCRN&nwMGP|^`6u)&O&)}&i?8vze|cEQ!fMGuNhC9|8{j3o#3#YwrNvM3+yX(BE{ob zEimQ=;6tulhY1Wz<1on;0!4;fwtKD{|8-Mc12a`1vOhPY#mpK<{_dwpqDsAcCl61v zW$sVZraM%p64Je$8lGG)>hv7`GE(g>k9-+cicbaf(E}5{V*&7w^ae@pm_$GpJH#F1 zmF3^O5b!N!av??OpbHX=Bf{KUxogD>+E-D9v4Z0ap;B?imvI7Jns%p+ku`Ov>G@r5 zHDtF~WfAVdDQ&!jjDu)Z=#u%2o!R4!XY%Q0+L%s)KgxBoz3@dbec{+ z{*HnanaaRB#lgvM6rFd5r;AphGWCqt9~GX06|MfU<)URYW1u6P@7cGvu21JLRu=g6 zAEp09S}T`#qkxLZ?kSvd&(pwDoGr)uCd-_gcZhRSsZAI#T@%%5%a*c7!xmxD;DN>Q zYW)U0I`Ylm4Z!j!>%*YGbTX}d7;IxrnYVH)C{-L$>b@V&Q6bpUGhCf1^|yTzyfrMM zj{gGC+CFv082BvKgsp75cymn?qrok+0z1Hct$34Qe?&Md(5bCB)k^Z0RyQA;_&4JJ z)=nhp!B_Qc+h2Q&BlWn%=+k>aCsCy*GGdzT5*3JFoxRE z<(B)JSBRFZp}S2d zwosAL4KnCnqE;}dFEp>k-g#s(2HJXKM#$hzyeqm_xei%fh-D zOm_QyR^x~%z`KfU6Ep*T^I0l~uF-5xG=@~ndFy*w=8Foed?%d%@vwV*GpW+Ba4LB( zQbRMcz7l%u^F>9zPk~2(;R!oorIkQ9r-J^WzSmw@!aaq)PWa0#E&*6`Xgp(w=%#Jy zAqY&Xx^S0NE{uoIP3-J3p{}(lZR)*H?gp(u2*UT5T51Uub zE=9oK0fb=IGdl1>s=nbnkS4eT;1lJacfnz*Z)~QLP3sK?$T>nM{3hXZ6}mjgUAwzG zZ=l6r-9r&C2Uz_+(y$DmM_PfaHhUVg2&Ap3=Gptl0|f(Wl6crawI{MKOdmnm&U~1O zIl9t+y3l0npBNI;6dPa+6gnPXA24>|L`DMU>Yr;}-#8Pk28gujyGpOx2OQs^Z-F{F zaDYsGWgO(S1n=_={uP!0e#*%&Y|9IMM9c@8YVqb~00ctll+)RnKc_d_b`I}3((5v! zpFe*++nHT7=z~ho`t$T-vD~_Xh=@>Axzh|@G~Zx8d1tln1?0ClG=`k$${3>et!1io z3&c)8=gS2ft_3hoQeW6xoPXpi?g?>N%te8)_K!}xJV3Cn<@AhdGP;;Mr(}`o z!Q%N)F1A(Ib9Aa`9`=0DA={lXYM7-0B|NwU_L|e`X2A>2)6Ug?Y*J<{UfmS>Wq0Kl zWrM;=IyUV&!Y)#L+pqH9C96TbAbECWkkF-atViz0WBaAD5K++~!6)wtVZXZAzfww#c9-7zGf6svCCia{sd!paM+7yVK`=cfXWPU0q(9jMD_Y)OGwchfB6 z-?&C@44h=vGd>W=z0drXT^U@;us~#;e9-lP&KX|h3=L-Le(d#7@$HfI`t4d2z^%?z z*Z3tDS8dRRdkrCH#i%7vq`b6e!XU-3If3|&UiShIbacm912=Lld<$~6=;wzq|3mU> zzt~Lt`Nm>x&+?wKp+CF_8?$=Aj-8s$g;7%kkhtGP5tVQsjb5+a`!Dzt?+Yz|(1XFs z*F%NbXryZIcH$afiHZp){fj1Qf0$sXStaHBvDTT9D?vy7@T)hPq}XwhX`FfcA;5Cv zRF;#Bh9eJ2H2w2o<7ziH3B39oa~|k%F9!WH$<{0p5?A>V-+qk=9Ht%bwj4qzP^#kb zhO_K|7fP(E)?58i>nC`Z`p_7>+6)1Hs1KMmtsl4jjp5mifxs^@G`K(yFI@F# zayb6M6`6mcBa5Te6FXn?IMT#i=t}~;$FCX04}eLye0Yiz=`>MVkk+>TKl69#o^Wwbn5j4xzNq>{t-^t(ipeI&APxiD~5rp_hsm!fIA zgn3)9!H&(O46`kLpzJhPk<{^F^J{lLp*?fVd|694f5g02H%$-*O_*@w_badT+qS3q zy`KoKBbpiih?2<81uaa#|Blr?Zc@gLQD_C`I@T+2A+{n$Xmdy0xM*|e$yxx?Ug5M>eAFMomzn;#*(pGr z2LtFg^4~yD{eCo9?W|*ONX)R{>|gLWcR2vQ2>L_wf9177hbTjfHWw+g@m{TzPO?up zKtT@+t4;o8d`_URX-9sm@Pgfd{x*eQT-aM!b3I40LE@ve$uk8g_2M7uzRngIy$oq; z9l`=GyLnJlG66K0X5&DRt71`Z~Klqw9Uefv9okK`J>N+E}c3Ky|UPSPme8MZnLM&qxTj<|q_Me;+0G8Igk?wjomjiZZiV-MqYu*7 zfasy!&$)LwTblX3DOjN}{Y=6?U`7{RI zosO{oA=8qm@%_@=RBdF@L<(KrF`=^P-qCi3P3j8N1=L2H{Is++CECjV;D9>$SYGY3 z8ck~$QtQ$$%~=?FpXQ&-W@j4dsm8wAKX?>Oo2CG52x-rL83DRoz>}Q-iD++Og=+{z zRH_xTM@vzl(N&~GGO@wNryu6t70UcEkcIB2h9rYp*bIFKN%Z+?OVJiSVc*w$ybgK1i zc&i-nnm76P^$Gn$jz=8|O!P93Sl_)ey>)xKR^e=BN`#u(0ruRixD#KLM_GQE+Z_T) zJmCc^eQ!ZjRKoe3GgQKnPGh)nxv=bI(l4^GuGzMeNUOmYF_-Nd?#rBuvE|_$1?>h; zoo9B2hE96FahGd`URYiqz(_CA_LcgxERJ z&RYlRb=sXs-Q>8903n$k`D(XSWKoWU#NPppR->mmkp>y=n%E6tXU}{%tqey)20AG3 zD7<}SM!(tUp%FEaG7u?kZBlKHa$9CvYW6H9i0&R0ZJD0y+OpFFS6%3zISdapVCH=p zh&pMRi?A}t{6c;wb?FfFyo%F!8j&r>J&#$uI(l7p(jq>#bP5%2lG0sM@K(Za4oh24 zK$JGZEPWdIAsOf6!r=MMs6zu@cN@+7U{m}PCG zg-L!uTNNUVBJsPslv(z%lj1ivx5@VQ5K;eV0CR~$-Ma_XghIsj-omMmTfS6$-WnCa zO6I3Nna+fE0obqh(2%JN;&$Kc*VxGZ>$U3{Bx55Pz1@2PEsjrTYe{2>` z%=+|G5Sww)w)Ip%@-0~rxdNZsG3Y*e+Nw7VK*?IWAwEAB z6JmKap`(!+ZjH3Ik)>-IIF`ECrl-FHMHrx0-TjpL*r(bc`xa-iv$-~H0c#8jjxg7g z&MkLu7FBBVh~z!5TXuvbk7n*n*4!R=C)(`K0#nfZ{_FA}X@f#j4@h+boDhBCx z`*jvt@2G)PZ*pPX1yId7oZihGAUsF$cMprUzApb>^dtXLCqnc7G%`LbJ+6GqF|9|C zN-^rPx9m(h$$X5mKZFlW8U?Nlex}t-VjY4-(u$bSYg8VfWk1>+9TtiDWS%uXG- z#tKps2qjVQ!dthh)%Kf`&yJdv{7bWjzbiSIM+W@9BF5gT(%INeNNZ6G#Xb3q02LpY zSpiVB5i*MwyVl@@WSyo^blBT>XoI1{?vce0TKi3E_Y!(u9V6C@g*>iJFi@b{b}C36 zMxEEu=b2Av+#IQ9XzS0#cCQ;_v#jIFM$F2dkDOgvOiBL(=r=^X{d+%}P%}9u9855M zZMMWvJuC{3>Zg?9e)k{5ybY4nhr4uVqOcmzOk)Z?FP?pAy4K>vS&q2r6;(xz-e@Mt zXI_;^nk}j-P@_qvJ3ag$Z~iDyfI4u@1~x@?bOzdl_dPXo9$CfSrv(H#fr}f*4IkH7g@D#J2Sd zTSLt$7#EFso1ry-y(>S9ijDihc)xLX72exb3fcaj^Z#cBkmXp{B1>E4mDAu&@AP{G zogTD8H`i)?&f+gX3z9PM4a-8%J_a!c{HA63Kj~**UD_0=s?Q0wYba4AgD8yH{?ME} z(5$7P&w0Bi8`Pv@LJtsM076RtM?9`W$4J)YN1aUF zF#rIoLH)E)jLP{XWH_x(8>q-`^oiuL*Pt81oo&PLLbA!scI*{(=iW{|6j_n zJYi@5DbyIdn7XL{JjC$gvM+i>4z)vuz z`G;oS#Ftg!KP1a!WX zb2RCXEgodPy3-YsjH#Wj30ab>K#0w^w<5M9BlHuOeg zLJbWHU&kGXv(=m%R9YPDgR26^C4##%C1Jr)Z{1NClVKl$xp7pCtTHs!u#tHDhn&NV zn+?6k?^!n2_#{pdWR^a%J%6>S@Y!aR`Ozdj=}z%m_mT<0oN+LQg5odUV`_ZkEStG) z-;ISCmXIas+r=g;&qC1_-!wk|PkZYV4<1s`x?bAVQ}Y@#x(`X}$m^3&mmw%ri|!%e zL>JM^vCEtPsPhduHBG!9_9e56Ns5)n9`?)7zOY=}Ot0rgVrtia7+$y4dq>=~Ypt3O z)F+@k;sAPnXB65fgUW#E$f58_^&kn8i39e&$5Q^1^>984Cjy|scw)XLL0&3GJ8yJs zZlq#WHG>pT_ZF)N%`PuoKA&or9!8#3*VNzait_(BAG?9BM)BJW8WR!<2)IHMg8#4;W!s3U6wl!qTT+26XvUAiQzvf*0su(*it6 zGq5=VY*zmg6fUXzMV6>FRvjN`=3$ArKf@JY$dRDVqF+I_>gMqC-6} z8Jy;sFK|Rb2Izz5^F?$2h$@-w)3xe~gqzttqgFq&MzBJ(uF_(<`wa9tb)kk@jbU5@ zuN%wuzKQv`rqEq{fWGmsz?n6CoImw796o-wCJCb^86waUt$tGi=-_fF4aO#LM14zQjMj91#LlQC3nH z7T!3--&c)`wjENQDL_oc>HTzST;^gnleV}=d3`~enk$R^>cUyCJ^1ePT(eDcT58g} zWW&Emk{aJRIgW$R|KQA7)ME~X6;1YCmNTp6LXgCQG+OG>&So=Z2XI8) zD|qAX75wyF(iKk3V#tmfRmBHw-KCj`vmD-X$nKyu1PDF?xq<*hky(Jrpmi*CufSF%jD%k1FQ2-PeY;tf%^0z+nsX*mC3{L3od-L*NU(Ps{I^!9V`kM&A7_ zq)??Q6X!X^4nUaLORE!&0o7pt?ZR89VfHsKbZt~cR|fePP%Ur66GmI%pX4tQD`Z~E zzx|akR1^y6<*7o(4n}_paC`@JIq&vtZj|yT4+jWg6~3k<8>?rzz6}ZYnM2d;qJbK| zSS*5Ht@C$T87J0pYF5Pr75^t*ML-fmY2EqXu7iMSw^Oscz9Psjui8fuWq#IgJ&b31 zpUrbq@>ngFXuFp-!`+zphQaS0Enb_lYGy6siB;u_&V>zmk?wQ!17Y_UL)tLJI)MOT zXl*C0y2qK%!!rV`pb3A*RGo&}} z5KU9s-CdM1VxkBYj__?!)Q%jXErHY=a-DgEoGL7Auv6tJ6L*m$a#-M(K(m2=C)TDkui3O*~{dW86Sens(CJl(lrcK|SNZ<>qDL}p`YO~I5HJ4#Vf*; zndO zS^fPb*ub(q0)+-JCqJio_>+OqPv|$)|aLm*^EH2R= z&&$`gWC4b=yp7)~7UDvUxvPw`$g#8aUsjF8&n0bFeAHXV~u4q&7Ih zG|}Y;qE~cx0=Ta|_txW|QRue?u?2xYPYD0neAKU2ABEFiWYWWU)XD23kv4G^dicq% z*z*(4F=Mr#np=k7?a`(!C{{9Of- z1w~c+7dlURv^Mh1V^R|)593j8Ji#E8P2 zN=`-JE4bCr%B92Xx@!T{Sa=a6^Y)zp-MegAKw)hmq8g!Ulp0gj8sgE=ra=+_kZqyK z1%F>jEZ;9>r8)`-Y0{Is2UOk_?_Uo(E=fL==uqwBgMTc8v@2Y;u5I8fG<7Ot^0)Kk z`S+TnTch$-Sd{|#;gbXQY{STs--q{eMmY&h$v@naC8xI|lLZ=`@wPL-{l%dQ|HhFb z*`y9IK2$(Jo3me!{lPt!5w6U`yi)5w!^QD7+VR!FzRS$^XEslP|V{l~?iBcYz;c zf~V}kZxpxyIQ!4Ga~YWg^BLMJ>RJJm_K@@lrFwYSWbu=9CPUwq^2qL(wb3<9c^!negl2*79Y;rEsTXFZZx!EZ$t>lB zBm{U+%S+#Cz0%{7EiC%W-2v?ckFUDRF6i31$k~~o`XN?BjdR-nsBr4 zZ6LgcXux7$KmvL!GDG_`f50&x?}73;qEz zy3ceO%)$~xe-)l=V^0u6&CU^Y#NWl0AT2f@1^QjlxC46B>kN#shh|Ac4OmoCUm3gk zjwbJ~l%;#_=by+sM)GZ~|86@kJJyyFJ!KtbN2}>QH)ln98o5@bZOUI+*~g2Sa9etB znZB-tG4_7Ee22~ERj>WG%T9~WrN$HjX}xy3O*|$e5mJL6?6R`i6O#ba^OWt)sQJiv zf`25u?P^kMTEnnbe$BsL_?g3ST{2}re|WWYW(51sdisO9b~(sJE|lgi4yIC<$$ax+ zajPzjbGrE5sAkwRDmu}jrV|GlBt;w0-o+Va#mqeJUex&lK{c6D!PT{q)#a(Gx?KAR z>tYZpRZOs$?69y>eGc?yd%Yj(B>yucLTy|Kye{o0MExS`k3I^JA8IE2dP5E}BhhGx zuAV394##G<*l3aQMlxzIefhsa^^VPBOj0$X!GVhR>-W6VK3bmY+ZAXGJ1D=d;TUkw zUCrN?ZPmKS;}*d_tLLYX3M@*$ot7C-+qFcS(q2!@wvSm_!Z9(ek0xR6s`Dgujm?0p z!U&c_=0Y7%Qhw&fFhbq(JVI8m`gw`=Tr{q`H^x1^_(a=jKa1bW5F6)vD|{TNuBD2L zH~C)yYE7%p+(qQ>D|8y7hPwE`vw`k$6ds%5hJ(gUv63YTa#w|~IOw}ATLQFGKzgzc zL%9u9TNLTa+-TLa-p!#sx#&2`JDb(>S)!@?L|(*lXkA+~OS%;s``gkL-@)nZtz(au zZvaq}*UVhI`nXD1H9$1q(0p$CCqSdZpf6d)HPj{i+5ZmIN_=xV?*Wr05EPYP70v=R zNIU(-Ox5(Z@8@(sHg5EuXB@LU)N@}gE{FlyeeK8lhmUpMSV!DUi903ZCbiXGbq8WslBOTIA&5UT#8TSbn6@+ z#)3SM9zr0sukLs9XH<3w7F<}iAC2!b7!XP%7;_EHgqv@{YTwG~U)SM<#?B*jYO?=O zq`A?^8h$OK_y{9^&U^FjcFv^0HCd-!ta|PaZ3u18Fv|IF*WHFgz``dRt-s>mN*x6H z16=`vc^-9KD8P4KW6^+vFm`ZpgD+MT=+d=8n4^IXwsOizmcUUoY1`lw4Q9~yZjM~P znSm|FO%1sCDNTzBQvp|l7BcM}WW4QR;)lsDtHI|c7TtRa*PDG^rV8fM9={98xy${L z{t8`|Y8_Sb5M!^PI>vZLg^HGKJS>0}-~r{Ad^=HV)@ zT#7XRw#=09YcFNd_~M@!y2o88j$iy7Dgt}O-b8IrdyW1ny|79)rW``$_0*f%Tp7(E zH)ipFj-ZtnsGNh3yO)st@O|q6-Z$%HDP4jk3g#3qX5P1Zz3sQR0gxx63v-VoG(orF ztMYL{p_Y42$3KC!1<`TZ{o~MRbnz}yXVS4Dy%8IzSoH)xS!wT>eRULqIV6dVx1+?_ zc+Hb-MfKs8ZwKWC6Lx#@My&JLx$v{WEx-O*y{12`Z$)hp`3r?}fhSpw()JYdf_ddU zG}CiOJyK>pG{26S!7g8T+f76F+it$CGF?yI|nXIL8A;(RPa z6>>%&_2Vqj49Q&a?Nf2N-*CLF=>3m05$v8%X??SFvg=3FEu(3W?zP8T0Z4Edj^cwP zB0?SxIfz{fZn27Ce0@~^xBNSK)szBH<^*a_`XIfL*^uq}7s^|c`8#pvq^!BTc)E|F zXTatER4U0g$m4Eo+T@PSZT#*C$?R4J2#}-p7>!%-_tMMe%NKfYNS7GNQ%d~8;^G?m zAllkVK}h636e{9EW!i;nOw!K|m7OOoJoK$#g>`u`nfF$pVLL;OcMr`v23Ntqt%M8g zU+8D=sHq+wB$GenPAM<1ZM&^1gEi-!?r=2C8`XT@^KtQPA`@V~jwMv}H0*_@Yb`^mq$ zjnqPbn$b+p1bEzo=RLzE@C-^O)Z}MKSKZ(Gzh}M;C0^=0+{$~@xw223viKoS9E?E+ zN8#QAamy)XYl3GuHXYIehlWZSGX>#20r4(F0-0a(LdzJ$x}G{-%_5Ej0g}7K?)3By z2<4YKDqx3_`?iZ~5JjhQoiG|1617`8ro^H zgRgLtNj?I*m#^hcx=spcl-cE0O{h2Lh<_{{aoX)r9W=xXUtjSZ_CyFY|YLI5Z*rcjMOk%!tH-xRuqh zo&RC4TgU^#JEOib7q&t&(m&)7J1}K`3{IdeYFdLCBJF(!q&+XQ`R0X}6CD82@W*0d zD9?P@j{HkAczWDdxZNPO_N9sh;?dhIPMOyqZ?~EJ;ZR=-{rfWAUiW7^XK8Q{{MIV9 ze02$8jBseA@v!iWxau-K#$-to?#$O5xup`+djMxLOzpqSzTofr6>we=@l{G|rLHo} zL%C0$EO#GE;+ehNE|d>0sy4(WFt>{rCnbSi7KGA`#>v%NZxoaO=_JYUH|%yH4k#R% zBaX_4%i=SE{pE~S9Juzb6R(Z`N4-y(6V7L%)BM)aSx9pFjs-u)*c~)jHMc)_ZxO&k zbS_OW@s{Zcj}LU+_ILepWvY&|$UAdgd>A1wZkhZsx_3^%tg4-3LK=`UI`y;hW?7P^C z06{VZpB>1S`R6D?yij*W%Z39mOW5NzB}}dv*35L`t11D3h>9v`>JW;|b(?_K*|*R- zW%hIjG=iPiwwPe36l5NGx4F&A;M}C?{vtb^DqqolWQ-_jx)9)lL?IAwCc15gk)!9y z{d2Jvf`Bg+N$g8(7(pst?jZ^$pBX5<(+1dni>1PO4H7Z-`_Dp@y1G>iF8}_hMx~MP zv=Z_Xm50|OPHrI3x~qWsc8^0N8yLN_r_cxIv>jc584%;%DE&5k%@U|Sv_w2Gj^S?E zmIqEiy0eN2k#!xO*4L}q%FF74C+ zkd}!UWRm&gkq$t2Y(n*i?{Edgk}An2xj1Ffp#8cz{|WT*{nn>_=qQZQ$DP;pis+Ls z6(buR`FZT5(@Z|#ehVoTPWp_047WSQGXYXdbNe5Rm>P^M8_}Z)R)nIMBgl;gsP+m} z%L2f`ed&E11I^y;Na=gae4S6su8<6k!|~o-B$C_{={;J8(C}`5z*#q=2fX!1?!`X; zR@%5yYoK2K)xNXYKVK)WOc8xk=G4{#AfF?A!@r;tl9nDcQ!?uQYF60&aE@c3LvMV5z`ibyqog|hzt-Eq8 z$j22;*e;%0=ar?3e7>f}_=f&&!0Y03`isR5(ZDLU{D$>`5@R__XNT! zsql^D64$)IxX{dXSe)H)SS392YrsXn{M%uaj|k0|9PATwFP8D)W}jbv9gN5

|07 z&{NBxa6xa{x-lw21zMPVi?htAe8A#hDD*7ficTsX?(8S%NZ1a+Q%yE@J>t2Q@7{zbX-mIM5NUF3GY9e`+B^gkj5Z-Q_zna| z+c-o7dH&Y*^M}H{By5R6KK}VATm}RZNtBv;jM&@JWgOv=hB_ zIq=Xi42?C0tTjjcG}HHRYp2QVdE403;>9jMw~}-7NxCC+Jfn7@DKZg8CNxhS>2kc? zHuQkOs8f1oTAU3X`MN#uwIK0cg4PNBGInG)zRuVhB^PYOAKqvV#fAfCCW!VSuHJ%jnH<*^%M61s%eSpHKg0Cjl)Q*iO*I=WkC8<6a4kbR13hxw+bz4G9{x*#{&Wp&0Nb^0gQ{=?5JSPO-ij0#TuOS|yt3e)t~!2AY=%Q}4aXTNmnrB)1aHWR?ZeHt~l}7baKd5(9Sd zK8N@o4n7NBZv(vS$|rFcd@yv%#6uz^H?BNA|I(3y(9Dqdk&Dw7?Wc5i{^f1K-UdyG zknyIZy?J3}YdXH?s?o{W1%OM8#5fG%50+yCSJ z-_hE*j=S~UH26qopSUpEwUJ{!Rs*I|Kev6)TMAqnHk-DPzW0JZ+vzhuV)Ih3_asjJ z--S0bsu#2WKaGApd1rs7%)X1+et3~>WLPT)A8{2{%wOoQn74Rabdm$>DLu<>%K9X+ zip(ybIAL}|u%~E7E{ODCdQ zY54Tc0sN@@5c^gfN?iQRYt?sfDgmvD;)pZ#I%=@cprZlTR8!fmVB$-h>aPOuZZ%+K z`^mRA`HK(^eb`_gbUx8-yKcQUt<6pb45tH@E4Go`JyN+OjA$}}_AJ5Wz$BDE=Gf$Y3h%3?NWcx&31f?C*8_$ zvd|1HRe-P;Nq|7#*|HVh%RA=Ank`Ba&r0hTOc7J$~CXV zRe&xoIC@y!9r!DD5G=|rkKebj`bn~GuZ0imJYrZ9R*ulojghYDoCZ3|EGeB>xiG=H z_23tMAmSRrHGxoA4QyTv{&Ne-A6UwONP=MA50j-k`$lYhrwUcN+H@LQI9p~@?J9Gc zJx@YTe(e#sy$b!LZLnn@roZ?WsbL}KH>cEOVjiNG(2foej#d>kPo!K4qG@?tI8s;+}m8AxrvxV6u=uJn|J)k6JzdCP?S$x3cW-(Q~GVpZsh)2ME}2 z-#NwV?2-*WOVU*IyUGSrYHCc@!Dl|fw1j~HA8npFxRzL|9}x!ho&=${Y>867qATE6OLqQcN0l3KX zmJ)B_%7lS|BJ25y8T6D_YG;q#*ngO&a$VS`1)IU zQ!Kc+$1k?3)-PZJU+O+$4s7iKrrEq&BX_-kdK>oqqHi)hGqr<)!p zlv!b^6M4*Hl)l>wJnpvC@;RMqIOqt?$UZQyqtP&e-qBJ)9|#{oXc$voEmIqc0=5e? zi^xA{m<3k^AlkfwQ!huxR)zcf&TjHP|5O1_M)!&r?M{4YXk&4xjDZA)rpD&fukO5< z0q33%*M`^5uK#HuY+ro!;Q4(-;fNwSjG`d!geO8O@R34CV5Y^kj%?XtY(Ef%2R`q%gB8@J$I(Qo?Se-TlhBP68G^X-{mdrC%5Lf{l+DDw#d9*77Hv z`p^bVcEkpgZCgv>ipY@;vkPCA!SB1nLeQa6gt&8mt}a)zZ3Nzrh20zS&6J*cakgq_ zbkx)rdRt?YtmZMziC{s|kAgc?Ij6z0cwO<=q7hUiCe{xO0PeyAsb8P9PZ{&uP9cM& zHAJxEt)kv89i8Xmo@9k{qS2q9(3m^Cn2#mdecYNz?2qWdP3`V>5T+U?X@rDSBjcB! zvVFy>hmm_nCOhTLUwTO7@i{AlnUcLlITOfem#O>pjl9tAc?5{x5YXCDRp9)wApnjX zTSYc|0hJCvO8k2AN+nlECY_|mHp8Q+sg=Oy^!W&+c_bu3OSUgHS4?eCza?1||e<+&@3y;>*Pj9`~7*&S@HT8SvJ@ z52icg-3rNm{!ZpYqHoQFQuuPc@@{L-3)wiFP}K3$a#?+QE2=Ucv|FqTSh!Mx1b&G! z{F6Jmo?N5f2c{Fi<`JddRP%|MCAf zAJWYWjqZ-F!);Zr(i*u~u(Q}0YHfWzKOv(a{iFUZC$+tC`Y%78d;9p`u3h^f+Zst- zy3(6ym3LMPx<``E1CZd!SCsxejMO)^Z=6oj^uKrA^M~aT{~*bReAOS$wtCGW`w*{V z%vnkY_5XIc?1+L?lT-un`@D&roqjfUMkD5gkXt+m+M?4V!)rJO9Sid8tNqe6eCVc$ z(2HBNvMHJ$c)rqO>vf;moWKA3|8|`V)RQt>ri{BeHm)eS%742itNEV)6i3jB0x`<> z7dGp_1A3$D>xV=B`j1isN{j0+#(}3W0IU%Hx2w*gr6hdvB-(IPHLXj4ds!>d70M&; zSrxs!;F|l^kusM_hIfY;Omy?O{02eqQA00_(Zhao>OoB+5P+3iev)L!w?Wwsbj8ZO zcFuAW?~(^m2#sfEGj+^1u30EM&537{sB2m&&%EFx=Auur+uE)b_1s~hb?-C>T~WLP z#1vx%NJ{Gz8S;RVl z>TC7G^#6AKr7O^tm&#OoTD>+!_H>IP^kPgS1UjF=Igw|c+~%88ArQlsl*J`ox5tPg zk+stc_X9-ndZ25Ob5>}fI>b5kDxk0e@B2D~dq%qP4s+P=<2Z7ES>pAE#;lQkAKro) zR^=lAivVzvm&~i;#zjc82sE`eb!>qWb2u+6Azg2C_mo9xbj{Ye;gF0@oaSG{;c~0g zXKAohf|~NWlu7$PY`u9n+v)rF-I?yuquMH32CbqCp^K%pW!jFdX=-b2u}rHiNkpo) zSTohyi(=3+s9ItRMHV7t>{V5j5F{i-QA}b^$4V zQ$WK7i|gW^ZlDyLUB!}k#Ev5i&h{ym43wGW#s=K}Fg;^d2HG=3azs?ZQ6VpED}YiP z#YPeec|%teV)PUtDVVR4pDLP}ja0D-?iqMzJyPO;+|!q?p)H8z~fP{V+fqL%S*P)dv}~ zIF8Jzk=4HDyof%y^7fG^LoqLM!<01$RqIx4l@p>{NHMm2nFyPjNL@D3=+XON6L^aq zOk4T*o1|m@s};MfSw&h&lV;J;TmWCsUKwuat3|b^6>s%bs!oyyzM>y+ZN$N&Iq(xh98raDN@hn_`s}J|NXm z;8Es1js3M^j>Rw^Cvb zrOW#&W%Z<)+V1YI@D1_;EeOzgCh%oyJ-f_=St}*}3h!{2T1SV4G#V z&IV*||B{zWTHDzN_JZtia7jsbTufe7!= zT@L35{R;a+a}mYYD;AQJtR5&lV4vQ40chY}1GP23R(5P+o_(;P96`>FiyqmXKlIij zfXbcy@V!F$Tq_!*`@6}L?V+zd>-Qh=09M3L^vSXUEeY%m$CpCY?7V)964TyGY$D*F z`LaVwwJLl{Zvan6zy0*>+-aMbhr@2#GRkSj+E;3dz>uZ&wiR~q-;iT#wa0;RtadGY zUhxyKxpp`=2xDnVtreQmys~-U+?i@8mfK$&{kbymm&9#1Z1vrj%5X(y&FKtK>y%Nl zsdiv7*Ef1MD+q9OZ{ux=!!%fxi^p%lX>y1j;QMSW_ih0_0?<8Z8?kKU)l)ghOFB7GVvfmxzt1zsMo> zTz0oT0}k~CErCqeFZ5o@4aO(d)I>tf-eafjm}_Qg4h5Un(*?v$PL$iSvQ2h3&jHTC zvaG9T;+VEk>MqrH3uMMHn6lRNhk$S7E5gl*P_qz@iS5qQs*9+a@I-D{h;uLzxYsqW z#SG-*zFobgR`{~4GX3za*#+g;r-owD-ujQyx0DNucC93ozv-Q*Y7d0D!f^1eIG8Tm zmpE}t{C;Bun&bS<%^Iz%tAkPf#Nl}!&C0!8EwAy6sh@v)oE~ccH%T!DIdNy!z?a$s z4D(CpdpZxbbPaPBb##_%RummbEt~+nGAhgXnZx*4)zM}*Rg9RGwq05@Z>}~1nK~)j zEAGnTiM?JGOY+_^=|p`K9>t}d2+!iu@4i8WjEqo}VrL7>$K5;tKiiWWyFYrO?vdB!;0W>M zdY5N8q=MD_%C(z(e_2RiLP0E7$2?LZ*Y`})>$FcW`KMIA!=-;KQY0$ z2+fR_%QgDqT-?Q^4B0Av>|~73t*c>;S8c9DwqR`W_C$_w&P6t17_3X2u`2)ybg{WJ|VF zJQ|;OA65t-H(xfN=Uj7eey+N13oqyjA}I!t)!g)Q{fVot?=U4_eZYOsGLreGVzuo9CUa}Y9@&)jkIM|^RdgS zJSYn+SQ{nnbAp~-l7qE;J3%4=qMXGm(=BPY4MQ_Wx3du^GF;r&gX_qU=YUZPGUnJu z?MVoF2jFOL@Ohoz+%0gGZu5r<4@+3nzr_hw=YdlzemuS@b*61-#Bo>&!2FbC%k*2> z=4!)D=xs{>=#dGaOO8zpf6s+s@C27TJDQy5>!0~xNN?wh%#wuqbJjN>@6d?*wcfH< z{ZvhS!WOV-AM?~L=1=(8v6h(w9P{E}@OYItFR@mu@ zsGT=-fe39i-*5u`404@F_W$k0cFG*}ndu`a?r0(!qq!)L z^^5NJd9;4Z2dasd9kuGQheuxa+G$mbnCAmzj3-P36+^y?<8_9=fyv3Rsea=TdphmU z1t3L0Pd9%C7}Jlnlv7e=%r4!86O5;q=vG_z2MvI3!!HY}hnt!P*HD6cyjw%F9!}fD zZE(;S=^c2_6F?M!beumdNw6s6*nkKV1+<{;YiE}He8O~`6KS|iE%5RDd`nqYti1$<|>FRLP5GK-bvW<+4w;hE4}JV?(=Yu zf+F*rYM6OWiRa>Dj?G^^`Ltjn#v^0>hBuNpq5|ER)t+nO~G*Mb?q{TKKRqi#be+H62-|D7c@L=wbdz(Y_7`@%pXGMI zVoa{+f8nQcO~-u|N2t#>Jku0f*U2wz%Un4N#<%cg^J-oaMGIz4@C3w?IV~M)rZ86l z%^S<}L<5bfu)x|SoXv{xIsI&POdX)|8!J?7S3E!j_}~zD&xLOW^MmZOMNQj!4DaE) zjOwWoe*loCK>^t5U@)7-Mg8#_s$pk3B74B^yS6e+Tm41pcuLPTdYYc(EF{77I$w>m zvqugfb}2y;Ro_n{#4o;tDuyah4YG>$?GOy*to2emT+vG}G1d)PMu??xzk$YMpo+b=6>BP+q=WYfkxg zCG$?2-H7JUjhezs*tEXG;8I#zFm`Q2ZK_rgi`*EnZjnSlwlt{gTc-fzgWG+@oDIX4 zDg^ts;l}Q+*?4)zLb|(cKm~jNb(xkeUgikoECK_Aga&^-URA~9O~S}*+jw9sc!w`5 z;K|KH#&BrrAPxs~L=MMqYD4qvHk}jp=J^54_POS~O-uQlb*Bb79(22TDnceP7<6mK zKnA3DV;BIi%m)BZPg&X9T+(o99a_&$`iaIA)SN^R?xMWT0YT!{8V!m>RKYKw0En)N zry+BB8NCLw)H^UAefo)D#X#k;a@}@Se#ZlD)F44lT1DM%3V|_Ad&0`mu~eUFk%nGk6+;c<|Bgh#ch~Svj3O2 zwm%aQHOT?p@_@H*sHVTh77+zMGc8--Xx%=`!RKVRSklMs>o-Y_ z89gxakZ#Qh&J3bVuAcSP!_0Hq9DI6H>029HEjG1Cew;pb2x%@`=p^k+?$|inNF7=_ zPS`Sg5Qf5R|3!9I3Z^Ea{emyQV0(IcUWBt^v*wZ?FjuzhgH`Je!Bm1bVfMuSn{N3J zLF#87yp)6mR~T3|G(lgV_>@xZ&=vY1SmU{W87aTtpL2(j!MV{iLene3#&NmrJl7GD za!;mf^In%T@2y1ZHRI z1!}^X_^p%SA^GKq4+S6*kaBi(e~H8y6lk80hxh1zjmnnJeq^7Wrp9o&d={YjogAW|4t(O zjtxiOQ!(UOVj5JA*Yab|;+xYh+JsP&kG|;9Iz@?nbGE4_|F&E7oV`1s;4r=7Gc5~R zG(R!~e^5rjPav3-bW9*wyD|5)Iqb8MLj9D>R%V9|jA1=tdr?dhyo-s;&lr{>;uXN-rgbI+gnGo%<)K?B9?4 zCNO>71r_-`(N6=Ku7^#*ghj|bk!^zh_N7gkTNw-3H$x}tjUMy-{y0x6;O{<9#W*!u zKkxp8>xBaoIXUDPw0^WOdtkGMLK)$QLo11Q}Pdfzw_sd zt9$4>qE#f$oxD1HC*TaKQKu+H@sbSA(Dun)Cy$*@P_s*d-F8ulzPx3N@Clw;RQAx! zyWw?6b&4GVr%5ihf(9=~xfT4ym2dgfT~XOq zqIS@I0Hv9Spw^TR^7;Y;{B!e(3JQFeOc+W{?di=X#+nmVl*f^nc+eX~hOf$R<(!wC zbIo@%Q9Fb!V3^DiNzEhr6NY@48?P(;K~fJ6uiR&n2R4&_NPdSh4rS|uU8>DxVJ6O- zmKC72f<$pd*eZN4Bd9!s6*xsW_Y=^HItbY6P^xqTF2OsnxIgmSd?+i^EUU0rdSP(+ zW_XfUe?U1@v$YuT*&tA$HHmOyBFWb&+%|SP`DUR53UYYD-NS7RS=Xib>_og>D$2~9 z=a2z5?|Y{)NFq3l5jiOIJ+NF4yDO`-HFK)p7DmJxs42qAk)8^JOF`u3Semv^#TVn& zl8j~#)W*G@^7r)?n&TIHFrOmzg@y4zy`#}R$unmF^^7c7Vm!?@94kWNJ< zg~Cuw0RMioC^|N(#1#m>N09C2*|hL+r#hsw{X&kLj$6uXS@)jim=VVj^R6N)M2pJyn z1(~2k((#OsKvJs@Q94EFk2ak>Z=t8(h90Gec zu#}*AXuTyeISmMWz+g@T>9I#ZO1wp&A+NMIF4CtQD>ACi_<8YMf#YUDc7(*0zI~J^T_Eq{yPryuIt~9eSv*&W0s5h7ONe!*Q z$j!O_kFhZuGysVe<$Dh;w_H;@^kt)GbPF~mdw4;Kn-y5@(WVw#B(nVLKD{+fpsLx> z$n-DH0Aiw3EGNJ-WME4`BC#z7V1!+DtT0EI`Q=1nH}rkO3p&n6fo1@fJP+K56%nW z9i{NH;%a^4iHU4N!E#>K0-P}%a^;&3|3CiJ(Qu+}C{b?1hoJdg1vH}g0EAwlUu&-2y+6;fg55#EA0 zJet$*6oJqosThBqg%%?ReracX8W>mzS%0ypYM5R&q$&I$jm%|WzoHUNl@;SGi+@JW zUt9#gaw-j zab9<_!vz~c3n{jgFc-Ugr!2$dZ0IY&kfd6Tt!qZDm*T|tUGcO4zDohZI^0x+Ni@c$WeQqi1gZU2XI7Ui{*eF zebciDk{&oEdk~x1CB3iYG$9WmtyP(r@W?2c?Z2E>27((Y?Qomf@9ML-f8mq=DHY5K zQ)eP~ie#=SX3z5@`tK<>JY>wr@X>~>eJ`ND*cS>OrXH^Wy&KsvR(vC7G_ha>(lli6 z)i1lHAS+H5)^@4MV`Zi&PXK|chxTvGWrMLbU#GLDGomb=Vvc!-{^5SzpC#@KRqinw zTa$^XLS?zC!x4F+xPQ(s|9&=q?2zs+#)aVlmF>}NYIk9YmEP!NBXq)T8d|V%7O+Sv zkfD3ZsCA! zQ|4W?syp0Q35C69Q%lT4pZfa+!t-F`N0}6?JAz@HL63&HF#3@7db~$ob`s&7mD){BUoT92FOs{zJ(As zV4WsikzYB6RioImw5UKn0=>$U8}|f-OcgMeqa1=+_l!yPF5~$Sho;unx+wc4%5VvI7kDDJWMjKKP3uLZMbWh0u{N#Py{V+r%+>sh>z#@ZxeMHpYm8crjG3hpDQg5$ zSF=;MC@d)Z(l|<{g6-Qt7>QpdDBo(L#yfxd4j?}wE%IpsH3x?2RlT^a>)RzjKX`?R z4z{%%F>jCQ$ z67J~j|F-~N-5X0sLQ=W1G}5qsp1Ljn9~>z0n(t!vvU;`P(TQ z9BY9;HBQgzLP*v5O-P%U@D3(L!2#+$ZFb2j-@rqKXy)wR5FNBq{DZ89z>M_TF3C4=C=bZl%nX9g_5(Fc0j?jL7>D+&nzm4<|!N9t!$6D%KJ<| zUBQ|qOQ7Fx)b&_1t3Q#^pVCiszNVj#)=K>-m}x!2Ez4U6)T!o|BV{C2lWIq>#n)K? zV#uYXwS|v4I#0dR`e*h=P2y2G7xq=YLUX|^>mxK@-|bHi%dhSH2PAX9kWW{`5PQoJ zD?T;b{shQ3n&2PxykyuZ^lVgyWc*w zqb7`lllC3CvM0I}JX>0gY}Fr*^xjSko$SBrUxW4@Ue;GJksi)NTLu}&RpF6_6m8iY zltL9U^US7dc{-RsAfx%5>KTqro$I(Dsr+WoTua+Ozv0m)bXW`x2k7tGrSoFLb*J2= z){{d9%9|!Bdbd)eZi#A08oOs+aEK6po0F~j9JKatsry#9!vSW=r=X|^xUpA~o?@5u z#3$09bxen0T!W6384;+JSLR6|zUi<}a%Bkh`=bIYlm`pe?U71L;Y1~Fp{!V1SUk_w z90k&G@8$m3;RH2-e$%(J%u&)y05}1I{|wo_F4|#>6`bnEW+PZ9nq$5${`%5Q@(18c zYybOGe_Z*-!PwV}N=!;Dfjhgg7EFq*3A{IQ$jhe8%@t|abAbGaqwe-$09clftYPF` zI9P*!F!jZ5jd|_jz?DH7Oyq3^bt%c6dQEJCDPJFg_0P~$-GF5Wgef0?cOa9^Ih%Un zUoIZK7&sQIR?J1}NOIn67h=XtuRaI{JV&VSTpu>!hM%;T8{OORZh&J-+I;w9hjw68 z+WyTbuT%&zeoLOyM&E0G=5$Q7D%NtkTWlOmE51!_dO5GKZW}D0|1m$fbV5KiGQ{l4 z%n%^!#CNmF$O5tkJ>W(3H9NQ2J`xC*cy$pU7_&(Z;}6(057 zFa!LX$%wR2Ojy*<)rz_mCvrc0RF6o0=+V#}XExZNlek?r=#`-+hr7=^w6%{_=q5R? zTgL}!cPaHGtO-JxZVk<%>-~mBWfM9Z4_3<};tQ|S1JSxg{y}4ymEyX~Ls~;jD^lh) zcohUVH`_1>26S0TO5_Fne6-wK;g&2$F}`d>?@cbRW5oE`;qGMF_+fQ#-GwM7 zKFyJqv4=^h)kncD@)tV<7=&v6p1cDv0&;(u2O>mlREOB`EPb=}r1r-S??q2}W~3Ex zfNZD7U$;HAsb)0@X-L;Uv&{E3y|P_|uG((CMjSHmfWm-~Y(T#eDsJ4Q1pv)G;A-$IX5usz(Pl~$xItB&8iC|Ygy*rk!L_Qp0RdAeZJ88r=Umpb zGjVwGNiaywg(>Kt7qaJb@7H|^MT-eHWSVysObeVmk0*VDPD@R(bo(JwZ9qQP|620Y zEpAcJdNRt#i>dSZ$r0|^yjxUOnNt0{QEqfaFv=HwGDbVa$hnbJA7PT07iB05Vn^{d zLD7AMhqmPSsI(MwvrWsVyRujCk{j>8a^I`T+fEsg8<)XOYs#Txm+X*6dlv~5x)+tB`f=)SC(0uKmq98ZhYs@(17X@ zN5VF#w7nPgD3{dV(o)*jX7J*((kfBL1*K=}TXO^z<>#C0TO&)3*bMOcG~bH!S(IqS z!5T>2mbWQI+^X9;xMc#4#V5=~48gUvNrvGi=dUPnX(w%n6W92LX@aB&F}fX=LaPLt zSNS5vQ$@7?-1PW%Ub$9gRKSBbU&Miu)J@w4a*W5lRisqG%aHH!X5wL8n4boP{Os?( zZ(VBtlA-?H36jg(gbjSJgqbu2Med3E`WV}~v9kFk!OcuP&XMa;hQmz>-!FA$3;lWi z%iqQmwt+(m$Y=8buES{SvTdAH^%IU1=_r!51te3`x0zCA5gXANrf7SjF@=Buj!W`> z122<)u?cE*P1P=(0sWuT&Y3sOvhl5_$wJ#Sn!&V5A2eB_54zWJJDbJWKi1O4AINXd+FL}P6!o=>xqDfe4pats%eidPt-H9=W!{MT+b67T z2e}xQZh#^nw=w9|JZ+P5%-TpRjY?{2rsg;%W^;vz-d*{763Ze@C5e;DUJ22%5ovPs zhk*Fa#)l$M|1!P$3tKC5PkND54kWFz@$U;nVczfL>4&1vwkJujq=T%ibFo1vxSZ0}>BiFdIEVc|t z_SQv3_+f>-LnC^cO{p-|h4Qm8%A4PepNuE@Xx{ptcBEmh> zf(fo&=f>d^W8@tCf|#o~)HRbDU+xN=s@y{Iw2BWfzPHc9p*q{}j@c? zNiIaDZwjNxc0uO0rtxKx%Yz&%uvK=VSB=aXz>zU4vMiSWlM)^~Tw2!Wz4Lp>yAc*H zRASODRTBEdIzmPH1LAx0)F@V>Zc^*HL6S0Gznri{O3|8~&-%8j=Cuc0YLs65TFFL%_pix@3{TV@hXvG^1gG*e#wGMEuN?cxkNNc?y9|iCjX=F{6CAX{Ic3Eh_9Mj^uR_9o`Bs?kdEQ} zWA;)ny=AUUqFL;$`s?~<#eG4D*N*42kArLc(Pc^S2q0#8WMwQ+3NjPP0)vjO0m*e! z`wDk#@YdY`%tNMm@dNOmif%xXZxFi7ZG03Ku=}&3+@AU*J+j-eQ@802VX8lNc-jt0 zP7Xa3YCQ=k9g{rm$@O^-dCQpn)@2kDE>7D{=Fba0lH|K;k^?l1a7~nu)H{@>x`s@9 z3vxF2xW5DQ8cxoN56v@El_esbr3pWp;{-MT8jos*!z;=bo}Lmo6YZXRuryqH&DU0PHG z60Tx5T{UKO)froKS)@G><^V<9J1&}-$ZJmd8SOTKQS4EWYEW2;ozdE8(63zWJnKT6 zj8*DrPnb$l$FA^WEfEL<=ODDX33j-2NnbYBTt)I^yEK@e5AQz28%4^+H;^T<)o?Ea zFm2@o7kdL?TA?hK;kp8>!hm+Tc;a5W$u(V#Ye|Lwk}v2O`eZufP;lA~tn;*0ftvp?$|19CyH3LV$?3K=ujD=`D(XI_v zaX^Vx>&Fo&v5rc~BB{x`stjtj=Ao${zJE#%%N#YRjW5_Q;>F%?jOnoz?Tw{D#qUle zw~JX$JdAMe`J0sB2f5p`6X2HNA)qtj?gB%YN^hF{_VuJ9M0V@8M5S1gs;A2PvRLju zvEU^vK!z&$#Otljc!bD+9jz5VaV%aN=2UpDJR5gGx&^WL({?Q~2wbHlE7$zBX?w@y z%CoeNfdi7?C82-MS=-7;uIMM%RnF(gAysuzqXxc_n!y3v4JtKNzic4cJo8s7rR2O? zo~_{RD>Xub*ftW`Gp-sWAY;9sjbw24NTq zoyz}}hcwR08jhV=8Ji*w3*35cQ(G8!vs>ws2%*Bat^&PbbxBhZQf?HER6{e@Oq83S zluUT~iVtCfylzs52KdTehi>|1EVHG<`DlVbz#h1rwTzh^ie6y^6j19n`kGxyV zphYY}XGx&$0u~S^?b0odTFakG)wipx{JHXv9Xp(2<-Y)|J^A3w_T{c z5xt!*HTLy1*v_RX<+!C57O{OjPY6@Y28__sPJ#-`k=E943zB{sNRufMF2cf4xT`)G)kjt~2eBmn$+SeEZLN4Ws0U*REBF z5!+xNzi*tY=rS2IW1HX42H$6Ra@^0%ea@J3sG~GENo>mjG@2)WZzKH7eTK&tig5ji zSMMpLY9u*YYj=$|!+~YJQcK`+VeXV1liY{-Dg#NX8{&4Jb>on!eINk$$@v3Q9n?8| zFpD40dKfeRdHD9i5&b=r3r_v_bbrracB*S>M;#gXgf*Hf!&l4;@K_@+QBV8rPw3Tr zt&MJug)pTONpae+cnTIh7+fLDW@v}T+olUtz^5{LU&mbd&YMGO9gpULpo5_jm{BmHHm{s(<5%`|(DWsG>24#52`*}e;?51~{7#H0`i0K~ zdbJz$Nwu!L!uY~{(u-dXFFGrQW8GpZN?yMelB+fn#6Nz%vfM( zpyvK^gZbS}mC38Z{rUg)djIna-Rj4V$(+LDv^zqMxKPN8AwpZs2X2U#iw+h7{&EDb z52zv!aXiD$pFYANb+|*7;XC)TH5;u3_Zlflv(q1>6yG;V(_2Q91nqV)8(mfi{+lvL zSY8TR^c_R%5d3r>rdksmy8FRPA42Rq||^9rr?OX%pac`WDOT4C*FzlAGfr}g&lXho%y%9){h+>=OcFtVWo3j154Yd+hxTm zr)`NWDYIhUaI}87MwRjLBZhbtHR}S4Lj7WAp_b?G&71}uBU6CgZ3=Sqi{}+#P0F;F zj7Jp3LbRt4Rf!lehj-15aXTBXp3XT}lGtkG#9pYr6!X)&nwTW*?`?qSuPu6*z1%$A zuz616x(3p-5Dtvhn%YHnS{qdLHG*sX-i-e7jwKGD_QpS|cQ?Q3N{MsYy81ugtTVNM z`Bb7Mn_?PnuH(674#4fNf%fohha%xRk8Jo=?+6(EDyOfd9;Z4+vc>D%;GQujzRYLs z;G@@AXBEQkrFwlaaUEL55Nf@YZq`3BwL*ii%?|aVU95B)pD4VSRvH3z zI!QAQb&ODI`1X1PLe4rz-I-W7(8`qZYWji$BKRPH`L_mwz)`*az$KAwXnoqbkF8_4 zk@dmNv#x?P^=H74q${pXaAb$LaV*ThEAnDGuN`HTnHxs zo#34 z_;pJ7cmo^=i0;ypfakWJtpd+oAqXpiW^jJCWvE_<9@TW<* zNit5&e~nX>wi~P1w&9c{^%|>Qv!D;HsJEZV{6LLc?!IDVeLM$Ei?|(YlAz1`6LLs; z7Q4c)X^!GUep$ewn}>pB^4<8C!IO0j#wiO|RfgGPY%5T8s3H`-zcEYQIsb)eX^MWV+8{G6u(!rD<(C^IhjtBbpmUFsuvz}ji#j_b!&_`A6e^s2g)feiw$M+NTxXp`W zpDoR&+;M4JIN;J1fQDO7Y+W5DtU_dcLuO|;Ha@>dKXLN*Zo2`E43l$KO4>F$&&!iO zzQ;?vC9y!r8{avN1{EoLBymB|F1IczB~1QJJ-jEN{NwtO>KiMks*zt073IIwy$DE* zqy9(zWN#4>zqBZ_wUb;G1zr=~R2)%o^BXxlih=?I(=RsWIRWbX8K{0Bfh_g1xVod) zCLu$^d)HJXUW$)aL@GIv&{Hr6AgSKh`~=p0LC*bpx!!1PwSj99xhW6IT$Rb!T3~z5>|_nz3+p1r30eG*O$$Jum8>dQcl$Kl#z@r)A6{q;j&#+@x< zt!3i%_CBj552#fVa2BvtSbvN(jP@+7WOEEMe+mfP{nuH&t5dZJI9aK}JX5<1$R&jU z|3*$S+J6iDdNEpOaIS%+rx4>hH(_~m5Y`zT{endsBG z#&XWktZR|Fl>gsv8vvv~B*_aCg^GU{rlqV94EAYIH}9hjivj|I#TDL7FK&sy@{sKQ zt!$C)EZlNEM_c>5ChOQ;-xpCh*9|hE)K}@wY;z44KF{{g$ABkhX2z>_w~%NcPuB-o z?@I!@lU!dV+_AGc0g~RNqj8cm1D!Z_m7sOUrxQS}2cP;g{7C@^mZn|4kbb>kNl^K| z-V{^++oe+4uNKPnE0<^&ZbB{t+pP8%*|A!2=07lxNGph(`SBEU)OX@$itaM=HpNH* z@OSUlxbW^2w#=i!;%Ma{RN4Re&o>k;rG4h0NIOQWLF6f6d{@*j@E#05UzO<)5rHag zftn;1@8N&?nDpc`sW9?LWO=*w=*6m^i@#b)WsF2UU7p8;TCqYsDC9na<}-s-dRF** zwil%hf9pO9N4`;k*_-|-yZ(E~ET8YeYc&ke@V1ja0(NrX+y2*wUd0=e|I^kW~p6juS`Frmwi{U4?W*^erLvRoR${_ zFPN{Cj^@^+6#2Rt^u(FUO(iGyE-lT`5H$wci7!i>bJ9;LhG;kT@2Q%qsT4J5hCy<~LnyrsX!_y8BF9a zjTpzh#1NY$dbGl~)+6urTKfWa|5@MNdwOL(K4@K}TUGK}$uu`>xGPKWSyh9%QXLGw z!GogB7d)aP_L3QdHLyK_|Eb7z_cNK-T>U{pXSZIRc~l8t!}FO0quMD2CqAwA$sTi^ zH%y+=hIW2hjx^id`X=ah(t8Haoq-=*uC66KTOaK8IcVB+zp0_AKB-#`C%LO@Dp!IS zL_o%za{UYOZtQ(T`jW|&Nd>26)qxus2!G-tc8tBdq51ImuY>x2rPpju{}}xD*~Iun zP3C*u90AAK#x=-kT(F;=D(wUYjy~h0D~F zoQbW@2HdE+wFfIgr$R^kmi2VjP#D=S6t*UFT=|tANLnn<%73sc!!xhjka5*uF)HJ- z6RMe_=12Wk@cv)RM9a=8=T49n@B?=+(t_a}%!^;!hwl>R{=kiDuNm1pNx=5VE-@FhIR#w_Y3tvu_ zXSxBr*z*NvYgY5dwM)pM6^c3f4m~KjJSziT3i$M)oKN~p1;#DJob z1-HWXlyd`CLa(-gqsWkre41PS--A8Rf<*Y{@)*#?Af=jo<=!&aHp~O%&deUM@5!ry zmrfY~DP!AP)temcysEtYlk~<0s7YL|?_HN0o+sm6o?SYAFP&u&rtte+-;!_Yzq)td zPx-MU9k}tcz@QkY0`+ZWJS9=9YM|}r4wkV`{PODji9zWAE+AU0#pr-CqI&^%knh=G z>qm0+&{G*m0V;`71}qZtZ9GwU9K#%v^s&} z{7u{DNLa|1F%aWq_O%y97p;?UCA^=1>}Wel_r~mxYTm@bM0-=BM`pqx9BSu_?aV%2 zbFZ`)W@7F?MeqN1p)Lgdoe~}&2bs);)M4Ui%Au1z5+IdZs~F=A65}g9CTuHjiT6J4 zmuI|Bd!R9p{g=yj?v<;f@4P`z-wx4#hm3OWFi9S|i&KzzhKe_at;9YH_JQSO@?;xq zccu3uHW%X^#i7Z;jj5xv++%NSAw!VKm-ISvkyzVSa;>$$)HNzoswrN-Xf+p=xoIaA zZ|1|c=7x>>r7w-PQ+qEb$%23E$OWt;;_x3k;_gYNfpDKx^tA;BFIW)Uzedk)K#Vxv zWWhjx@1x|Qe?tUbybb~{jQ^U@w=diF<$t|N|9knbT>!wLk`~X!_;xhx~__jqmREgxLPSx=Twp}a=i@RJOOP=Z09gChfvR`> zEE9MtbEY>SfQ@8q-hU^O$46H;YQ(a zHZ)OD8jYJ87pzn|FS?IcC+g%l+oT_5o=7z@v1I}-K6<{I1l+a$)HbbEb-t=%QPEQe zri$RfQ1$v~6DVTlc4O)=ApzBYE%<)MZR+lO^%fa&R3opB|2Pc)M=6!0z4ni`?h-$* z--@qLDjc0aDmZ2Hd`9l)*`!XbJcCvF7PUi_CWua7C)`}!c<>GkMt(T9|HJzF-Jnf< zwPyBbws~^78zGyqA97!e2wmd-KVU=G-9LMJ0f;`vlnnF$COGJxNzF9Oe1b@CgrDtC2Na!#LbGWJCPQu&S)4 zqVlTltNqP!+_QMNkzxvHDdDx|Uzx9t-}|;t3XG4DAe=o*dRYQkmzcQvw!)p!L5qbUBmMU)NO4l+47bAb{_&-CA|FzCL^8l=e(NZXI zN@1h;X7~iY2Sj>k0E9ebQw!B9{hg9dwlDhaZ4(d~C-{!A*IkP43V93jXP)xc-VT{J zen3txb^Rj71ZXHIh~PjP$t6$si`R;d_60opYODWDHrK}Eb4Z`br6ae135!=}j=)Dm zo)gDNzVUI17q|av3l0#Yj3_QHW(2asDe*IA(5vO*oylQ(U?&0kncasEgAZFD{m=XU zpSPQS9cZ)VnqOehK%$LJw;0y6gXpf1p5WY(eIAR5{B0!wT{^+*)ofHM%QrQ8Npw+9 zJ$%Ze$CTA2ptW86u>;C}SX*xRdc*QnvAb_JS50Km&++OiTG)spd|O~Qb7obH2j6@3 z7`yWbGlHS{UyK4Qs!4fcwrGO@sX4=dcL>*hr9y(ATJHkk+%h}Fr|omvJoRir2-bQ^ z3!akEu6Jy#Tla|Ny$>lN&BqUUd^jZkWB5M>Mpdn(s-1q&)Qa!58$oApuyXMIq3HW> zp`kjh))S}G5i%M%IWor{#tc4qDsXuA$2dIkfCxx+jMixdZo!@Eh43J{FR^(@7a!xb zv>A2kLz0badTZOm0hCXZHN@vh?wRP%_bZ>e|A*P~-|z50{FXY|r3W?AQPp!IaB0iB z*fV(wWG=($rIg@Mw5A+||EI7o4QDfN-=3%IphtBhErX%xLYN}V5ENBRs&uoZm{u(j zks7;*Jkt)f6s2vnQPdhr5=#~m%h;=SAxMawT0$t1TJnGI={)cIe0h)K|0SPt9JzBR z_jO&r^Ewx1IN@5Q85JL zP+ndE-`l=8)djoan{KD{b~bIa0Ds*#%-6?jO`^mOzj9c!DB&C{HX2hbV0e<7Y zX9V1iqg{I%Abx5uZrcN@PrHI;qcCH7{s#|t3%QrBOP7q|Lw2=HO5Lq&kj@qK`r1?X z@9Sit54Uz14aayln6HDTGXUGdho&Hj{6aGzP>8|tDCQOQ>bfFwMbLu3QO;tYJ-`aL z$9J181fqW2=uJC&@*z0K_bGs$?NMa+3%uwVlNpH*nD9_L@RUVlFvbv1@S?R?K&}*9 z5SLPk9Yw!@D)^Z;DFe3hkqQ@QBJ(9bdhsfI?6eB}YJ9yNZFc~G7AJI|aM`W$T-1K*AAzUZofs@U|Fgs4Pc}im z24TtGA#@59@0=6x8kMa`jU~;VHtkJ2Od6x_VJB96ckQ^?*9}v$yEnp_aZicxZts%C z;c{F4MXo{8Ydn9+wkHRBKJup}#rl?EX<;P)cVpc*$ce*dIuQ@&kxG~*h1RUOtm>M2 zD#lKl^Da)2^%^9+s5kPFg9jNQl|Hqece@s7nfZIAiun*V%lYaM+5jBZGjns0x}1R0 z)dpxvI$-jT57S$5)X!=Wlq`RePwBLb*( zu^l8XA%Ytk`(`jOoqKjQue$mk5OiGacdF0L+8BCc9U8_!g1dIYH?aVxrb~rpBTyyA zbn~Sc>xX-Civ2-RIlLJc#mUslZd`&iyptgGo@Z>{kEsu~+Ns ze=q;P#&zJoJs>9V4#jfy=59-u{am0p^|M@PQXlHCKdd&0{89l4-&yS_U-)c|R z!pti@5u9$*#&nzIEot9r_kuX{Cp)^ad+xc4$ydiFkTLp$V6xiK;jK>-ZXenUkOw>I z?+h%=`ksuxCgIPf%ej5SnO>PAO?b%ECz1=pV6)yL&xlhqyKcM|T1Fry-Eu=rcynwCd@1L-lrt7ZZrH8QX_9-9+((uHlQWcU02Ky%IPdXz>)E>6-C zSEnUQbupbHq*xj`1&ETQ-6IrjI0nggTs%+kxyTndltx=6SgjIe=^!}x*ki**O*v%t zYnAN)>^!vc)7nQjNvnCKX<>GP7pO_)C^bf9RCGPVj)8ZLW<`Q+cp7O6u$A`n`dCuY z_(j)DeKp&9&lyj#X8Q_mM(HwGhxeCA^1~Is<=p+!Yg&h!-C8KKAG`H#O3wzrSukS7 z4C`MjAG3#)z2tR&dG;JPnFb83OMYt?VyRfmg%&sBsMqN6LP*h5Vkeq-U!${)0RlOT zINMc%i1FE>LTasH&x7j9lqEj8OP0GJkz%boOghB97spPG>+X75SFkr-Ao0|TtF*Hu z;8q=1!aa-sOi|c>31ugf7uqs=PmF52#n#cYh=7v=UBmzxh7m|}EkQ1gAwSY`aNKq6 zpsVVM^FD`M3kB1SMf~8M?MB=wam<&yDN8uL=4%pe((R!m8D-7B-jR9tv3QtCDizV! zE#1Plf0Rg3C<7M*C<2`IvBHfpbHddv`2DG{-2D@s8#kgn&jss?E{9))za>UmS=C+Z7IfOn-uFn z&Yx*C%Mr#AgG+6S`Fza^1T;gwSLbaR+)8SI4_9IuM#&gfS$dl`<(jD6~kvnpyE>)pr1_P1cn3QQvCk z_ir6dH_<|tb{rn$tfJ1>NgRuT4o;~8rq%2n&C*iIh>A2hWH&R}Y~ojrYF&MO+&;DC z$;9JWFT&z))dx&Q=Mz6({Zxw4_~iOE4bt?nVH73TE?#gNiZ-2Erkq;bCYWZ`C!-^A z(V-n~1D{|RjNPacY)jif%n3U4(%k^0^@G%p8!$a)7*KKLa!L_+F6VV*KHs$Eu_vz+ zrNzAuc$<{yz4DT4Wwk@~zKd~H3rSNv_Pp$0DxW|C-tO^;&PlVnAypks-dOp-dku~h%k3%|=P9#FiW#R=OWtBom7<vxwm;GVF*$KxqHl8*lCzDUxBYL%;1_kB*@mhg9aHNKL!ou|BIJRN;LY@sSbF=g?Bj+oCvGzN|dSzx+IeT;5XBsfx>%37{KX~tyR0sqYr?A$H zy2+k4m820ri`pHOe}rdQ-GqT}D(S=4!Bp;__@K{*J2!IxTh2~L>e~yInI4Ht++iT? zOhANxoRQAKOiujJ5&S@yYBdyni{$UjH1Q+gGm88b7DU!5bq|g)m4*b*ZwyR~dALPj zsc}OF5u2U7Vk%EyIXcOfuC(&^*fZgjL9HI3-tXt%9r{gZB16l#%?_<(9s~XUN0Fxo zq5?5YC)_7^i(Vtl<0-lZrFRNiW3(^SvNb9a8k`%dpto`*?UG-_SJv|#7}bL`6U@FP zrrW`C-4fAk!Ir=B!QYtvK7dwdesf(tXLNgb8fL_#W!{NEPlAe;dusMhvKze2xyUx4K6P@9akLQ$o4p+v_?5nXKHElh*LV8thO6s(Zc*uD95kiTr zHxc$^X%k&4muo{2W)D$(-ybQ+>=|<#yjT5KlWv%8G6XP*B6;kE~KW~ z4fza^AtM2+D)v>?3~F_)%Nt&EP)HT@q}@eDdAoGJPqN*y<FMF-;S6~ZqU7wX{B3vlOb#Q3v)GT?x;W3MKo&6B1uW`MFe&|KFEk?l z8B;w6*H(^_T*b|eW{Sd_so1IY!U$i5`iv4%9ZOv52sZ&A=WyrUxvB9e)oQpMWwY+~ zVHTVxb8hpq*E5GiOo&RMM4CZo3ux}j--9PHre2Qe4}1&l3%IN$eZxHEE0;o;Y3YKP zp>P>$@c7r2YrjXnX#a-4FSJW!Jg2di?0Z7=Qyr9!a{FBdeMp;(-*rymU!rmbRauF! z3&0Xed*fFIr~3y$R7ZrD1-_I30o@IU{QfSrL?FZgc*B+GOX67E^loWGCD(??bK}bj z>h3g4yt6nYqV?})!+A^cQ7=Yipn#3!;7BB<`_B)_+A4#htlf~99wTQ$9l3N`cg1BB zWyx*AByCxpge^ZkOwLV;)Jz8}&;O#msyZ}p3;5+U&}4!a4AQJWD{Jo;nMsRryS^}X z^n;BI_w>vMI7dYDhICDco; z&&}>kJVQY(3i(FQ)`lI2fZ1$dWpn&WAjBRFaX8s|oK)l=#@W#?ob4a64@ZV12S4tI zvIrx2RL4$$U`0ez-Xk8Cknl-lUq%-xzf8*@?|+ z%k)(!Dn+UP-qx?^ihX)#h)R+l)rBN=Vq_w9z@$^~w@zn}Nge5Y{*OIZ^&FrHXOP?3 zHg_Fe|7{HPln9(aZffF~_}P>)Gg|{N!SX_&)tMn-Va`U|wDIJMM0`!Cp=Iw!PC@1&BKr9NJ2Ao;FY$o+)6)RqXm9%du6QP_TP09R^dkQ@Cz) zdtx!Z;+3|-B_jUF%TMoI$K83Zz@zBNq!Uqji_V1B>HBtvV`~|Dg@x4NjAJiK!cM|@ z1uwf*56-2iM=F&Um$e-H+KAb8kMNz&Nu{D=*AUaG$2=mcfS7{oC>>;`2@58B%7JK+ zFUGgu!Nage;1IOopvU#?V4OeTWF(D6o9BcJpY5GV67hxuqqRQ7bepLOou2^|pG%pJ z>B!}`llE@IR#mpimC~kb(}{~TnK+~43^rmX*czb+c^7`N6J25_w)$u20k(gi0Qe-- zGrr-Bfs#s#6gt;0TbohcuN{gCw?*=#$WRM8(QAKBObEv*HS$b_`ccu(pBj?`bv~LvreP4^z-g3` zU7w_XnsnSjb9&6xZA?3i0ouXmim=B|hi~Bo8p716>o4;bohlF@g-1K0icp2|MMjjd zzj2|YqQpLmYfl8SE0XK}+|JlGPX1EnEWiCCOV8e|>E~(qM*3bOc~4G=0!TdFvansb z_U8sF$Hv^oJx7-kqi+ym@yVK1US<5HFz&s$x--n#;V5jEXaWs)(S0-&dJzRi0TN|8 zKgUy?ChVMwDOq2b5|sw$j#*EN`ZwW8(wZh;p+@0e;pT615&Df@B|3OKJ<8IPvZ_sf zG-NP-d%k;RrL3ryiG`K#Q}(Xp`t5{egnF0xD` zMihL~Os;*CU>nd6aS0>|0{AGM9wke4b`hZo?mE9Wx&J;yMOIeIFs;BUU)^|_ zzmRBn!po&@&GJgfP~%0Ry+!q}Hvi;W%n%apso;J#CQm z=6f$2MW2(=MM%B0+>y^QDhv>VFNehH0cR(8-_{T9NMa{Klx5>olfreb*V(w=9;CJ0 z0My?KbbPe_YlIEEznprjyT3` zpO*H7F_>1tMOfDDKfK=994|rpMpZ1o!S|BcYVz0L9goBG-_!|F#zP$eX}+8}z~%+UW#7v&9P`Gc?9B6acpyeVZVRkLWaritiD*q>%*$0Iqcz zIvdm)g{1%~eDZ|X+(=HZ6DGXR)RaHVXO=IBY5dsx}_s=0l6FWndef&hbVw_8MBGeppG8Xh@} z=l(fN2%Wt_Z|WIWjeXgt-7r~(J2N3UX$?3GZ0`H09P?KQU2)o&7}w1ZL9Nfbc~rb~ z`a2edqia0Bv(~lil*dTGpu?qUIgD{&)foWwE?pw$2>;-TF}CnOS@EMD(w6(_LuEwx zCFSXh?>E+-gYZ>j+LpGebt!&ch=5NXey+Gi|1BO$=6+f&Xu-b<7toQ%^RUf03GaNo zgFz&OMxsT!#{A)!BK2#PDXwZMZRfAN8XJGvnj>_PR%x%7<3xi=5g5mTDNe|4sj3KU zbaU{>q+Lr*z(op0yA-H8ND}1MrEyNsasJh8RlVAh~XB?(RSp*o3)+Uu);A{f6sS@ z=08N=%Y%UuHbug{O1olz72g5ha{R^@dzymVVi;Xe>|U0UVGMN&#%534s2FL1)+Ufh+&g^vnOi6@n61Ll5A zyl8pt-9IY_PtyN)`^*Va%*}0 zW^3X_A#B>C#%l^|E$PPLDg0Zm^F3&+>r2Xz;4#TgERe>z=yoccgB6V#)uDo!p#a@%ro>xRp_ve)eS3N*A7 zVkhNgk(-M+<5pFD$|^uDH{8HTo8J|dSC_LrddAl#er2Ob8dS_h?xj84O1)vVIClT# zdofRP3Uz(0P1)&fX-A1iZy7@QJP^R}6o?A>wWc*$|QJKy(yk}Gw>BMktVMVb!q9p{1UG(gZBD7jd}hU^)mEOCWCOc6^A5iyA!nav#r?$0wI) zG%pc~QVdul1~?-}s&O4j&@Ff^DfzxS^G4m?zrVp2S|W_(`+ZVmz3#fl6ZJuJI<=d? zh&=`GE%ytLdd^XZt>dRg2G5Pz=5TM3>Xxkzor30HhvS{)zkjx4)@MF-*;8<__RfCx zE<_5zoQdqc!7tPWt`M7dE}=8N0vJ4u0FO(Qdw4DA?L7W=X{?^@T5?y* z!arpd(2}-yaz!-i(Y&y9nWj&>$#D|ZL@qrw$cl~g{h@t2+;R;(@N1ajbZmFJlafLirO|Ke2 z$zRN`s*E77^Wp0{+_ihyt74<3bqmubY+jqzFX?=D<0F-R z2EwM9-X}lD#yhbXc<{|B-|AiaS%E$TgZm4|J3npSNRf-cxen?;q{FR+4I>#hg!@H# zAtq<@82HoBbCvP7^zF2KSok5-56;8O=*VJ?#ecWzu0*Q{U4i}PPWV%_PAE?LzQ07f ziUU#ftU%an)MteLcC!F*fOFV$WET*_Z_Q=V zcY*a~j?k6z6e0Yh)^2)S#xbLvBe6~whu=)dPZWJdly^fGvu_JW7X>BfNNLf#rL+XE zH%+|J(;HS(IKl9=N-W>ZOs7idkxGJhl({WsoL({lJr+M%6kPBUm1f;>NdIJ#d>g4R zvRl)r@|&|^hBLq={tbaU4KwC@a;#h8%*iZJQ$KLvWIYFb;m0D9ZVmwk^C4L`mwG;S z8r-&sf}GfiX4a-Ln@SIlIf;HO{7l$3RLNF`?&s4yLH<>}MPjrKJET9zI~?T;Ma3w5 zx#5@Iwy|+ZdStunvO#oVIh8)3 zO0&n8=M}y8e%e`$xV{A-&7AnW@ma61 zwF>z_TSkrn3+K!Z7Sht&`X5wM3FFfQ8DvCL!yE5(2M2NPu5+{vAm*rS2Uhv<69NJ_ zE|=)P7>>D=amz?VXWSZNDF&ecW08Q`hFTUO60G8wHD?iy33*^c`41Uu9}SQIe(o-W z#|&)ugl1w*oNEA6(zrI11|xL*W-YXM?b>PAlP~IJcmeF1GI2|w z&A&r!P|7o&18R)wnqdRh*#~tEgCQ&zjp|GXT6s0C=844qS{q$AVE#bgm~DSgdclK+ zB=c0Rq+;3=pcoHETNf?vIF%lM&&6&kJW@!ml?^3c9_@U*SX$-pGd(w@Kh_Z)Gpx~) zZuj%4sy}8nzfJA`%e77uhCa z-`1$MKZvk}7M)`WsNCh>N9gmfV9<%eiDJd*DU`+P zk<4j_GYGdMB}%j|PHJ%BveBgnxBzcP#GK12>H_}U0VY0qH24W}H+mp|93r^f>rmTJ qJ#+z>KhB}a9Jy*{#Gsb_WnSyC19sn!HC@bqe#PXZv$XmDjQ$@%1XZ*E literal 0 HcmV?d00001 diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index c51f46a04..745412324 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -3,6 +3,8 @@ from helper import unittest, PillowTestCase, hopper, py3 from PIL import Image, TiffImagePlugin +import struct + class TestFileTiff(PillowTestCase): @@ -77,6 +79,12 @@ class TestFileTiff(PillowTestCase): im._setup() self.assertEqual(im.info['dpi'], (72., 72.)) + def test_bad_exif(self): + try: + Image.open('Tests/images/hopper_bad_exif.jpg')._getexif() + except struct.error: + self.fail("Bad EXIF data should not pass incorrect values to _binary unpack") + def test_little_endian(self): im = Image.open('Tests/images/16bit.cropped.tif') self.assertEqual(im.getpixel((0, 0)), 480) From b680d863790156a4a57a33c4d178f3d2de6c8a45 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 6 Jun 2015 19:53:56 -0400 Subject: [PATCH 22/42] Add 2.8.2 release info [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 9c8c04d1c..4f32f7e40 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -40,6 +40,12 @@ Changelog (Pillow) - Tiff: allow writing floating point tag values #1113 [bpedersen2] +2.8.2 (2015-06-06) +------------------ + +- Bug fix: Fixed Tiff handling of bad EXIF data + [radarhere] + 2.8.1 (2015-04-02) ------------------ From b4503f6cd45502a01e5c84319d68231ef8d4f79d Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 6 Jun 2015 20:27:09 -0400 Subject: [PATCH 23/42] Make sure we upload a tar.gz too, Part 2 [ci skip] Do a better job: - Add gztar to --format instead of calling setup.py twice - Create same sdists across all targets not just upload --- Makefile | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index a729e4088..27ec990b7 100644 --- a/Makefile +++ b/Makefile @@ -67,9 +67,8 @@ docserver: # password: # repository = http://test.pythonpackages.com sdisttest: - python setup.py sdist --format=zip upload -r test + python setup.py sdist --format=gztar,zip upload -r test sdistup: - python setup.py sdist --format=zip upload - python setup.py sdist upload + python setup.py sdist --format=gztar,zip upload sdist: - python setup.py sdist --format=zip + python setup.py sdist --format=gztar,zip From d31c6c7847f242ccd2eef1be01a74ca5abaa3384 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 09:38:09 -0400 Subject: [PATCH 24/42] Clean up Makefile and requirements.txt [ci skip] - Remove relative calls to pip, python (assume virtualenv activated or otherwise "safe" global env) - Full define development and documentation requirements in requirements.txt ``make test-dep`` installs documentation dependencies too, hopefully that's not too annoying. This means development process for documentation and code can begin with: virtualenv . source bin/activate pip install -r requirements.txt --- Makefile | 14 +++++++------- requirements.txt | 7 ++++++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 27ec990b7..08a147220 100644 --- a/Makefile +++ b/Makefile @@ -14,12 +14,12 @@ help: pre: virtualenv . - bin/pip install -r requirements.txt - bin/python setup.py develop - bin/python selftest.py - bin/nosetests Tests/test_*.py - bin/python setup.py install - bin/python test-installed.py + pip install -r requirements.txt + python setup.py develop + python selftest.py + nosetests Tests/test_*.py + python setup.py install + python test-installed.py check-manifest pyroma . viewdoc @@ -52,7 +52,7 @@ coverage: coverage report test-dep: - pip install coveralls nose nose-cov pep8 pyflakes + pip install -r requirements.txt docs: $(MAKE) -C docs html diff --git a/requirements.txt b/requirements.txt index 86ddbd771..bd37d7ba9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,8 @@ -# Testing reqs +# Testing and documentation requirements -e . +-r docs/requirements.txt +coveralls nose +nose-cov +pep8 +pyflakes From 19432c3e531921a543e49567871272cc1b54d742 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 09:45:26 -0400 Subject: [PATCH 25/42] Clean up Makefile [ci skip] - Clean up test.pythonpackages.com setup description --- Makefile | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 08a147220..6755e9b65 100644 --- a/Makefile +++ b/Makefile @@ -60,12 +60,13 @@ docs: docserver: cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null& -# Test sdist upload via test.pythonpackages.com, no creds required -# .pypirc: -# [test] -# username: -# password: -# repository = http://test.pythonpackages.com +# Test sdist upload via test.pythonpackages.com. Create .pypirc first: +# +# [test] +# username: +# password: +# repository = http://test.pythonpackages.com +# sdisttest: python setup.py sdist --format=gztar,zip upload -r test sdistup: From 0706f6b50459b8e6f33b1c5da94a01d2761981c7 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 09:50:01 -0400 Subject: [PATCH 26/42] Clean up Makefile [ci skip] - Rename test-dep -> install-req to better describe what target does; keep target name short. --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 6755e9b65..2134b3a43 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: pre clean install test inplace coverage test-dep help docs livedocs +.PHONY: pre clean install install-req test inplace coverage help docs livedocs help: @echo "Please use \`make ' where is one of" @@ -10,7 +10,7 @@ help: @echo " coverage run coverage test (in progress)" @echo " docs make html docs" @echo " docserver run an http server on the docs directory" - @echo " test-dep install coveraget and test dependencies" + @echo " install-req install documentation and test dependencies" pre: virtualenv . @@ -34,6 +34,9 @@ install: python setup.py install python selftest.py --installed +install-req: + pip install -r requirements.txt + test: python test-installed.py @@ -51,9 +54,6 @@ coverage: coverage combine coverage report -test-dep: - pip install -r requirements.txt - docs: $(MAKE) -C docs html From 91d5da745ffe77c1b8f1a7f26b82700d2a4c8dec Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 09:54:02 -0400 Subject: [PATCH 27/42] Clean up Makefile [ci skip] - Rename sdistup, sdisttest -> upload, upload-test to better reflect what target does; keep target name short; add targets to help --- Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 2134b3a43..004b807ea 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,8 @@ help: @echo " docs make html docs" @echo " docserver run an http server on the docs directory" @echo " install-req install documentation and test dependencies" + @echo " upload upload sdists to PyPI" + @echo " upload-test upload sdists to test.pythonpackages.com" pre: virtualenv . @@ -67,9 +69,9 @@ docserver: # password: # repository = http://test.pythonpackages.com # -sdisttest: +upload-test: python setup.py sdist --format=gztar,zip upload -r test -sdistup: +upload: python setup.py sdist --format=gztar,zip upload sdist: python setup.py sdist --format=gztar,zip From 99b4da48dce1d4f0585295ca29e883b4486c4782 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 09:59:46 -0400 Subject: [PATCH 28/42] Clean up Makefile [ci skip] - Rename ``pre`` -> ``release-test`` - Remove virtualenv creation and call ``install-req`` from ``release-test`` --- Makefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 004b807ea..bcc02d13c 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: pre clean install install-req test inplace coverage help docs livedocs +.PHONY: release-test clean install install-req test inplace coverage help docs livedocs help: @echo "Please use \`make ' where is one of" @@ -11,12 +11,12 @@ help: @echo " docs make html docs" @echo " docserver run an http server on the docs directory" @echo " install-req install documentation and test dependencies" - @echo " upload upload sdists to PyPI" - @echo " upload-test upload sdists to test.pythonpackages.com" + @echo " upload build and upload sdists to PyPI" + @echo " upload-test build and upload sdists to test.pythonpackages.com" + @echo " release-test run code and package tests before release" -pre: - virtualenv . - pip install -r requirements.txt +release-test: + $(MAKE) install-req python setup.py develop python selftest.py nosetests Tests/test_*.py From e3d5306bde2f7fe214c5aaf72af8535cc061265d Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 10:03:31 -0400 Subject: [PATCH 29/42] Clean up Makefile [ci skip] - Alpha sort and add missing targets (though we may only need ones that are known to cause target execution issues) --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bcc02d13c..9c44dfbf2 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ -.PHONY: release-test clean install install-req test inplace coverage help docs livedocs +# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html +# XXX Do we need all these phony targets? +.PHONY: clean coverage docs docserver help inplace install install-req release-test sdist test upload upload-test help: @echo "Please use \`make ' where is one of" From 7dafa9a97e2b4b357a0774c5444e2db866dfa2a1 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 10:08:38 -0400 Subject: [PATCH 30/42] Clean up Makefile [ci skip] - Alpha sort targets and help targets - Add welcome message --- Makefile | 84 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/Makefile b/Makefile index 9c44dfbf2..a5878e486 100644 --- a/Makefile +++ b/Makefile @@ -2,51 +2,12 @@ # XXX Do we need all these phony targets? .PHONY: clean coverage docs docserver help inplace install install-req release-test sdist test upload upload-test -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " clean remove build products" - @echo " install make and install" - @echo " test run tests on installed pillow" - @echo " inplace make inplace extension" - @echo " coverage run coverage test (in progress)" - @echo " docs make html docs" - @echo " docserver run an http server on the docs directory" - @echo " install-req install documentation and test dependencies" - @echo " upload build and upload sdists to PyPI" - @echo " upload-test build and upload sdists to test.pythonpackages.com" - @echo " release-test run code and package tests before release" - -release-test: - $(MAKE) install-req - python setup.py develop - python selftest.py - nosetests Tests/test_*.py - python setup.py install - python test-installed.py - check-manifest - pyroma . - viewdoc - clean: python setup.py clean rm PIL/*.so || true rm -r build || true find . -name __pycache__ | xargs rm -r || true -install: - python setup.py install - python selftest.py --installed - -install-req: - pip install -r requirements.txt - -test: - python test-installed.py - -inplace: clean - python setup.py build_ext --inplace - coverage: # requires nose-cov coverage erase @@ -64,6 +25,48 @@ docs: docserver: cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null& +help: + @echo "Welcome to Pillow development. Please use \`make ' where is one of" + @echo " clean remove build products" + @echo " coverage run coverage test (in progress)" + @echo " docs make html docs" + @echo " docserver run an http server on the docs directory" + @echo " html to make standalone HTML files" + @echo " inplace make inplace extension" + @echo " install make and install" + @echo " install-req install documentation and test dependencies" + @echo " release-test run code and package tests before release" + @echo " test run tests on installed pillow" + @echo " upload build and upload sdists to PyPI" + @echo " upload-test build and upload sdists to test.pythonpackages.com" + +inplace: clean + python setup.py build_ext --inplace + +install: + python setup.py install + python selftest.py --installed + +install-req: + pip install -r requirements.txt + +release-test: + $(MAKE) install-req + python setup.py develop + python selftest.py + nosetests Tests/test_*.py + python setup.py install + python test-installed.py + check-manifest + pyroma . + viewdoc + +sdist: + python setup.py sdist --format=gztar,zip + +test: + python test-installed.py + # Test sdist upload via test.pythonpackages.com. Create .pypirc first: # # [test] @@ -73,7 +76,6 @@ docserver: # upload-test: python setup.py sdist --format=gztar,zip upload -r test + upload: python setup.py sdist --format=gztar,zip upload -sdist: - python setup.py sdist --format=gztar,zip From 5f4e407927c4d02255d2267d9677efebb826bf1d Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 10:18:31 -0400 Subject: [PATCH 31/42] Clean up Makefile [ci skip] - Rename targets docs, docserver -> doc, docserve; shorten - Rename help and phony targets --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index a5878e486..85b644a83 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html # XXX Do we need all these phony targets? -.PHONY: clean coverage docs docserver help inplace install install-req release-test sdist test upload upload-test +.PHONY: clean coverage doc docserve help inplace install install-req release-test sdist test upload upload-test clean: python setup.py clean @@ -19,18 +19,18 @@ coverage: coverage combine coverage report -docs: +doc: $(MAKE) -C docs html -docserver: +docserve: cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null& help: @echo "Welcome to Pillow development. Please use \`make ' where is one of" @echo " clean remove build products" @echo " coverage run coverage test (in progress)" - @echo " docs make html docs" - @echo " docserver run an http server on the docs directory" + @echo " doc make html docs" + @echo " docserve run an http server on the docs directory" @echo " html to make standalone HTML files" @echo " inplace make inplace extension" @echo " install make and install" From 70b2be6bd5c253478b88f164d69b28db9237327b Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 10:22:15 -0400 Subject: [PATCH 32/42] Clean up Makefile [ci skip] - Remove nose-cov comment: we know from requirements.txt that nose-cov is required. - Phony targets comment: who cares? We'll just keep listing them all for now. URL still helpful. - Coverage report comment: I don't get it. Reformat and add "XXX" to draw attention from someone who does. --- Makefile | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 85b644a83..9a0deb90c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,4 @@ # https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html -# XXX Do we need all these phony targets? .PHONY: clean coverage doc docserve help inplace install install-req release-test sdist test upload upload-test clean: @@ -9,12 +8,10 @@ clean: find . -name __pycache__ | xargs rm -r || true coverage: -# requires nose-cov coverage erase coverage run --parallel-mode --include=PIL/* selftest.py nosetests --with-cov --cov='PIL/' --cov-report=html Tests/test_*.py -# doesn't combine properly before report, -# writing report instead of displaying invalid report +# XXX Doesn't combine properly before report, writing report instead of displaying invalid report. rm -r htmlcov || true coverage combine coverage report @@ -29,8 +26,8 @@ help: @echo "Welcome to Pillow development. Please use \`make ' where is one of" @echo " clean remove build products" @echo " coverage run coverage test (in progress)" - @echo " doc make html docs" - @echo " docserve run an http server on the docs directory" + @echo " doc make html docs" + @echo " docserve run an http server on the docs directory" @echo " html to make standalone HTML files" @echo " inplace make inplace extension" @echo " install make and install" From e69d028b6ee1e92763a23a59fcb2bb8d3bf6254e Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 10:28:40 -0400 Subject: [PATCH 33/42] Clean up Makefile [ci skip] --- Makefile | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 9a0deb90c..bd2d3add3 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ coverage: coverage erase coverage run --parallel-mode --include=PIL/* selftest.py nosetests --with-cov --cov='PIL/' --cov-report=html Tests/test_*.py -# XXX Doesn't combine properly before report, writing report instead of displaying invalid report. +# Doesn't combine properly before report, writing report instead of displaying invalid report. rm -r htmlcov || true coverage combine coverage report @@ -64,14 +64,12 @@ sdist: test: python test-installed.py -# Test sdist upload via test.pythonpackages.com. Create .pypirc first: -# -# [test] -# username: -# password: -# repository = http://test.pythonpackages.com -# +# https://docs.python.org/2/distutils/packageindex.html#the-pypirc-file upload-test: +# [test] +# username: +# password: +# repository = http://test.pythonpackages.com python setup.py sdist --format=gztar,zip upload -r test upload: From 9a333c89968950c1d87f9f82272df86962a57495 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 7 Jun 2015 10:29:52 -0400 Subject: [PATCH 34/42] Clean up --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index bd2d3add3..4d96c497d 100644 --- a/Makefile +++ b/Makefile @@ -66,10 +66,10 @@ test: # https://docs.python.org/2/distutils/packageindex.html#the-pypirc-file upload-test: -# [test] -# username: -# password: -# repository = http://test.pythonpackages.com +# [test] +# username: +# password: +# repository = http://test.pythonpackages.com python setup.py sdist --format=gztar,zip upload -r test upload: From 1b80fe5507060e316ad8bc0766c33cef856adc0a Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 14 Apr 2015 17:43:05 -0700 Subject: [PATCH 35/42] Provide n_frames attribute to multi-frame formats. cf #1190, #1192. Tests missing. --- PIL/DcxImagePlugin.py | 4 ++++ PIL/FliImagePlugin.py | 35 ++++++++++++++++++++++++++++++----- PIL/GifImagePlugin.py | 23 ++++++++++++++++++++++- PIL/ImImagePlugin.py | 4 ++++ PIL/MicImagePlugin.py | 4 ++++ PIL/MpoImagePlugin.py | 4 ++++ PIL/PsdImagePlugin.py | 4 ++++ PIL/SpiderImagePlugin.py | 10 +++++++--- PIL/TiffImagePlugin.py | 33 ++++++++++++++++++++++----------- 9 files changed, 101 insertions(+), 20 deletions(-) diff --git a/PIL/DcxImagePlugin.py b/PIL/DcxImagePlugin.py index 0940b3935..978c90e80 100644 --- a/PIL/DcxImagePlugin.py +++ b/PIL/DcxImagePlugin.py @@ -62,6 +62,10 @@ class DcxImageFile(PcxImageFile): self.__fp = self.fp self.seek(0) + @property + def n_frames(self): + return len(self._offset) + def seek(self, frame): if frame >= len(self._offset): raise EOFError("attempt to seek outside DCX directory") diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 4ecaccdc4..1acae31bf 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -86,9 +86,10 @@ class FliImageFile(ImageFile.ImageFile): self.palette = ImagePalette.raw("RGB", b"".join(palette)) # set things up to decode first frame - self.frame = -1 + self.__frame = -1 self.__fp = self.fp - + self.__rewind = self.fp.tell() + self._n_frames = None self.seek(0) def _palette(self, palette, shift): @@ -109,11 +110,35 @@ class FliImageFile(ImageFile.ImageFile): palette[i] = (r, g, b) i += 1 + @property + def n_frames(self): + if self._n_frames is None: + current = self.tell() + try: + while True: + self.seek(self.tell() + 1) + except EOFError: + self._n_frames = self.tell() + 1 + self.seek(current) + return self._n_frames + + def seek(self, frame): + if frame == self.__frame: + return + if frame < self.__frame: + self._seek(0) + for f in range(self.__frame + 1, frame + 1): + self._seek(f) + def seek(self, frame): - if frame != self.frame + 1: + if frame == 0: + self.__frame = -1 + self.__fp.seek(self.__rewind) + + if frame != self.__frame + 1: raise ValueError("cannot seek to frame %d" % frame) - self.frame = frame + self.__frame = frame # move to next frame self.fp = self.__fp @@ -132,7 +157,7 @@ class FliImageFile(ImageFile.ImageFile): def tell(self): - return self.frame + return self.__frame # # registry diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 283300320..150773b67 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -87,9 +87,30 @@ class GifImageFile(ImageFile.ImageFile): self.__fp = self.fp # FIXME: hack self.__rewind = self.fp.tell() - self.seek(0) # get ready to read first frame + self._n_frames = None + self._seek(0) # get ready to read first frame + + @property + def n_frames(self): + if self._n_frames is None: + current = self.tell() + try: + while True: + self.seek(self.tell() + 1) + except EOFError: + self._n_frames = self.tell() + 1 + self.seek(current) + return self._n_frames def seek(self, frame): + if frame == self.__frame: + return + if frame < self.__frame: + self._seek(0) + for f in range(self.__frame + 1, frame + 1): + self._seek(f) + + def _seek(self, frame): if frame == 0: # rewind diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index c68a3cea4..589928d0e 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -260,6 +260,10 @@ class ImImageFile(ImageFile.ImageFile): self.tile = [("raw", (0, 0)+self.size, offs, (self.rawmode, 0, -1))] + @property + def n_frames(self): + return self.info[FRAMES] + def seek(self, frame): if frame < 0 or frame >= self.info[FRAMES]: diff --git a/PIL/MicImagePlugin.py b/PIL/MicImagePlugin.py index 5aed618ac..aa41bf359 100644 --- a/PIL/MicImagePlugin.py +++ b/PIL/MicImagePlugin.py @@ -71,6 +71,10 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): self.seek(0) + @property + def n_frames(self): + return len(self.images) + def seek(self, frame): try: diff --git a/PIL/MpoImagePlugin.py b/PIL/MpoImagePlugin.py index c345fd327..9d21728b9 100644 --- a/PIL/MpoImagePlugin.py +++ b/PIL/MpoImagePlugin.py @@ -62,6 +62,10 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): def load_seek(self, pos): self.__fp.seek(pos) + @property + def n_frames(self): + return self.__framecount + def seek(self, frame): if frame < 0 or frame >= self.__framecount: raise EOFError("no more images in MPO file") diff --git a/PIL/PsdImagePlugin.py b/PIL/PsdImagePlugin.py index 02c94a860..d30695adb 100644 --- a/PIL/PsdImagePlugin.py +++ b/PIL/PsdImagePlugin.py @@ -132,6 +132,10 @@ class PsdImageFile(ImageFile.ImageFile): self._fp = self.fp self.frame = 0 + @property + def n_frames(self): + return len(self.layers) + def seek(self, layer): # seek to given layer (1..max) if layer == self.frame: diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index f1ccb67f6..7de5156b1 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -127,12 +127,12 @@ class SpiderImageFile(ImageFile.ImageFile): if self.istack == 0 and self.imgnumber == 0: # stk=0, img=0: a regular 2D image offset = hdrlen - self.nimages = 1 + self._nimages = 1 elif self.istack > 0 and self.imgnumber == 0: # stk>0, img=0: Opening the stack for the first time self.imgbytes = int(h[12]) * int(h[2]) * 4 self.hdrlen = hdrlen - self.nimages = int(h[26]) + self._nimages = int(h[26]) # Point to the first image in the stack offset = hdrlen * 2 self.imgnumber = 1 @@ -154,6 +154,10 @@ class SpiderImageFile(ImageFile.ImageFile): (self.rawmode, 0, 1))] self.__fp = self.fp # FIXME: hack + @property + def n_frames(self): + return self._nimages + # 1st image index is zero (although SPIDER imgnumber starts at 1) def tell(self): if self.imgnumber < 1: @@ -164,7 +168,7 @@ class SpiderImageFile(ImageFile.ImageFile): def seek(self, frame): if self.istack == 0: return - if frame >= self.nimages: + if frame >= self._nimages: raise EOFError("attempt to seek past end of file") self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) self.fp = self.__fp diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 41bb26d42..8fdca05de 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -648,6 +648,8 @@ class TiffImageFile(ImageFile.ImageFile): self.__first = self.__next = self.ifd.i32(ifh, 4) self.__frame = -1 self.__fp = self.fp + self._frame_pos = [] + self._n_frames = None if Image.DEBUG: print("*** TiffImageFile._open ***") @@ -657,10 +659,22 @@ class TiffImageFile(ImageFile.ImageFile): # and load the first frame self._seek(0) + @property + def n_frames(self): + if self._n_frames is None: + current = self.tell() + try: + while True: + self._seek(self.tell() + 1) + except EOFError: + self._n_frames = self.tell() + 1 + self.seek(current) + return self._n_frames + def seek(self, frame): "Select a given frame as current image" if frame < 0: - frame = 0 + raise ValueError("invalid seek") self._seek(frame) # Create a new core image object on second and # subsequent frames in the image. Image may be @@ -668,17 +682,9 @@ class TiffImageFile(ImageFile.ImageFile): Image._decompression_bomb_check(self.size) self.im = Image.core.new(self.mode, self.size) - def tell(self): - "Return the current frame number" - return self._tell() - def _seek(self, frame): self.fp = self.__fp - if frame < self.__frame: - # rewind file - self.__frame = -1 - self.__next = self.__first - while self.__frame < frame: + while len(self._frame_pos) <= frame: if not self.__next: raise EOFError("no more images in TIFF file") if Image.DEBUG: @@ -688,14 +694,19 @@ class TiffImageFile(ImageFile.ImageFile): # was passed to libtiff, invalidating the buffer self.fp.tell() self.fp.seek(self.__next) + self._frame_pos.append(self.__next) if Image.DEBUG: print("Loading tags, location: %s" % self.fp.tell()) self.tag.load(self.fp) self.__next = self.tag.next self.__frame += 1 + self.fp.seek(self._frame_pos[frame]) + self.tag.load(self.fp) + self.__frame = frame self._setup() - def _tell(self): + def tell(self): + "Return the current frame number" return self.__frame def _decoder(self, rawmode, layer, tile=None): From 51f3560dc45dd39d14d67108ddc5b95b9bd3aa84 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Thu, 23 Apr 2015 01:06:32 -0700 Subject: [PATCH 36/42] Restore negative seeks for TIFF. --- PIL/TiffImagePlugin.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 8fdca05de..59de84273 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -673,9 +673,7 @@ class TiffImageFile(ImageFile.ImageFile): def seek(self, frame): "Select a given frame as current image" - if frame < 0: - raise ValueError("invalid seek") - self._seek(frame) + self._seek(max(frame, 0)) # Questionable backwards compatibility. # Create a new core image object on second and # subsequent frames in the image. Image may be # different size/mode. From 3ad70423add94de5a61fc6ee8f526ad9d0b292d7 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Thu, 23 Apr 2015 11:03:11 -0700 Subject: [PATCH 37/42] Fix typo in FliImagePlugin (seek -> _seek). --- PIL/FliImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 1acae31bf..178623951 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -130,7 +130,7 @@ class FliImageFile(ImageFile.ImageFile): for f in range(self.__frame + 1, frame + 1): self._seek(f) - def seek(self, frame): + def _seek(self, frame): if frame == 0: self.__frame = -1 From 0c51b7967ed371d3e140d83449ae3910c5b1ea32 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 8 Jun 2015 01:01:50 +1000 Subject: [PATCH 38/42] Fixed seek bug in FliImagePlugin --- PIL/FliImagePlugin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 178623951..32cd05049 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -135,6 +135,7 @@ class FliImageFile(ImageFile.ImageFile): if frame == 0: self.__frame = -1 self.__fp.seek(self.__rewind) + self.__offset = 128 if frame != self.__frame + 1: raise ValueError("cannot seek to frame %d" % frame) @@ -153,7 +154,7 @@ class FliImageFile(ImageFile.ImageFile): self.decodermaxblock = framesize self.tile = [("fli", (0, 0)+self.size, self.__offset, None)] - self.__offset = self.__offset + framesize + self.__offset += framesize def tell(self): From 46f439604cb0a3d8b034b327d6d99c2bd580f8fc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 8 Jun 2015 01:01:34 +1000 Subject: [PATCH 39/42] Added tests --- PIL/FliImagePlugin.py | 2 -- Tests/images/hopper.im | Bin 0 -> 49664 bytes Tests/images/hopper_merged.psd | Bin 0 -> 97862 bytes Tests/test_file_dcx.py | 4 ++++ Tests/test_file_fli.py | 4 ++++ Tests/test_file_gif.py | 4 ++++ Tests/test_file_im.py | 33 +++++++++++++++++++++++++++++++++ Tests/test_file_mpo.py | 4 ++++ Tests/test_file_psd.py | 7 +++++++ Tests/test_file_spider.py | 4 ++++ Tests/test_file_tiff.py | 7 +++++++ 11 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 Tests/images/hopper.im create mode 100644 Tests/images/hopper_merged.psd create mode 100644 Tests/test_file_im.py diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 32cd05049..0660ddeb6 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -131,7 +131,6 @@ class FliImageFile(ImageFile.ImageFile): self._seek(f) def _seek(self, frame): - if frame == 0: self.__frame = -1 self.__fp.seek(self.__rewind) @@ -157,7 +156,6 @@ class FliImageFile(ImageFile.ImageFile): self.__offset += framesize def tell(self): - return self.__frame # diff --git a/Tests/images/hopper.im b/Tests/images/hopper.im new file mode 100644 index 0000000000000000000000000000000000000000..ceeb01049c98a69b31cd89b5d3f11e327a86d2c0 GIT binary patch literal 49664 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 Aga7~l literal 0 HcmV?d00001 diff --git a/Tests/images/hopper_merged.psd b/Tests/images/hopper_merged.psd new file mode 100644 index 0000000000000000000000000000000000000000..6b5c7ebe1fe8e424b26fa4689d1ba2d6f7f9078b GIT binary patch literal 97862 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_dcx.py b/Tests/test_file_dcx.py index 2acf7b459..7d2ae32d8 100644 --- a/Tests/test_file_dcx.py +++ b/Tests/test_file_dcx.py @@ -30,6 +30,10 @@ class TestFileDcx(PillowTestCase): # Assert self.assertEqual(frame, 0) + def test_n_frames(self): + im = Image.open(TEST_FILE) + self.assertEqual(im.n_frames, 1) + def test_seek_too_far(self): # Arrange im = Image.open(TEST_FILE) diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index e6634c799..04c2006c9 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -18,6 +18,10 @@ class TestFileFli(PillowTestCase): self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "FLI") + def test_n_frames(self): + im = Image.open(test_file) + self.assertEqual(im.n_frames, 2) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 08b2f10c9..0e9e65a18 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -134,6 +134,10 @@ class TestFileGif(PillowTestCase): except EOFError: self.assertEqual(framecount, 5) + def test_n_frames(self): + im = Image.open("Tests/images/iss634.gif") + self.assertEqual(im.n_frames, 43) + def test_dispose_none(self): img = Image.open("Tests/images/dispose_none.gif") try: diff --git a/Tests/test_file_im.py b/Tests/test_file_im.py new file mode 100644 index 000000000..24e00b2f0 --- /dev/null +++ b/Tests/test_file_im.py @@ -0,0 +1,33 @@ +from helper import unittest, PillowTestCase, hopper + +from PIL import Image + +# sample im +TEST_IM = "Tests/images/hopper.im" + + +class TestFileIm(PillowTestCase): + + def test_sanity(self): + im = Image.open(TEST_IM) + im.load() + self.assertEqual(im.mode, "RGB") + self.assertEqual(im.size, (128, 128)) + self.assertEqual(im.format, "IM") + + def test_n_frames(self): + im = Image.open(TEST_IM) + self.assertEqual(im.n_frames, 1) + + def test_roundtrip(self): + out = self.tempfile('temp.im') + im = hopper() + im.save(out) + reread = Image.open(out) + + self.assert_image_equal(reread, im) + +if __name__ == '__main__': + unittest.main() + +# End of file diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index 7850744af..1a0ebc453 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -95,6 +95,10 @@ class TestFileMpo(PillowTestCase): im.seek(0) self.assertEqual(im.tell(), 0) + def test_n_frames(self): + im = Image.open("Tests/images/sugarshack.mpo") + self.assertEqual(im.n_frames, 2) + def test_image_grab(self): for test_file in test_files: im = Image.open(test_file) diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index 51b8cf3f4..dca3601b2 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -16,6 +16,13 @@ class TestImagePsd(PillowTestCase): self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "PSD") + def test_n_frames(self): + im = Image.open("Tests/images/hopper_merged.psd") + self.assertEqual(im.n_frames, 1) + + im = Image.open(test_file) + self.assertEqual(im.n_frames, 2) + if __name__ == '__main__': unittest.main() diff --git a/Tests/test_file_spider.py b/Tests/test_file_spider.py index 7bfedad1a..7d24b2fe5 100644 --- a/Tests/test_file_spider.py +++ b/Tests/test_file_spider.py @@ -42,6 +42,10 @@ class TestImageSpider(PillowTestCase): # Assert self.assertEqual(index, 0) + def test_n_frames(self): + im = Image.open(TEST_FILE) + self.assertEqual(im.n_frames, 1) + def test_loadImageSeries(self): # Arrange not_spider_file = "Tests/images/hopper.ppm" diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 745412324..02a63586c 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -150,6 +150,13 @@ class TestFileTiff(PillowTestCase): self.assertEqual( im.getextrema(), (-3.140936851501465, 3.140684127807617)) + def test_n_frames(self): + im = Image.open('Tests/images/multipage-lastframe.tif') + self.assertEqual(im.n_frames, 1) + + im = Image.open('Tests/images/multipage.tiff') + self.assertEqual(im.n_frames, 3) + def test_multipage(self): # issue #862 im = Image.open('Tests/images/multipage.tiff') From 9e24ae023d67de9822f481d252cdecaa7ded560d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 2 Jun 2015 22:50:22 +1000 Subject: [PATCH 40/42] Fixed various typos --- _imagingcms.c | 2 +- _imagingft.c | 2 +- encode.c | 2 +- libImaging/QuantOctree.c | 4 ++-- libImaging/Resample.c | 2 +- libImaging/TiffDecode.c | 2 +- libImaging/Unpack.c | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/_imagingcms.c b/_imagingcms.c index 3b822006a..cda7c5f1f 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -278,7 +278,7 @@ findLCMStype(char* PILmode) return TYPE_YCbCr_8; } else if (strcmp(PILmode, "LAB") == 0) { - // LabX equvalent like ALab, but not reversed -- no #define in lcms2 + // LabX equivalent like ALab, but not reversed -- no #define in lcms2 return (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)); } diff --git a/_imagingft.c b/_imagingft.c index ad40ee425..d8f6d6338 100644 --- a/_imagingft.c +++ b/_imagingft.c @@ -572,7 +572,7 @@ setup_module(PyObject* m) { PyType_Ready(&Font_Type); if (FT_Init_FreeType(&library)) - return 0; /* leave it uninitalized */ + return 0; /* leave it uninitialized */ FT_Library_Version(library, &major, &minor, &patch); diff --git a/encode.c b/encode.c index fef102176..6bdb8c71a 100644 --- a/encode.c +++ b/encode.c @@ -737,7 +737,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) return NULL; } - // While failes on 64 bit machines, complains that pos is an int instead of a Py_ssize_t + // While fails on 64 bit machines, complains that pos is an int instead of a Py_ssize_t // while (PyDict_Next(dir, &pos, &key, &value)) { for (pos=0;pos 64). For a quantization to 256 colors all 64 coarse colors will be used @@ -421,7 +421,7 @@ int quantize_octree(Pixel *pixelData, /* add fine colors to the lookup cube */ add_lookup_buckets(lookupCube, paletteBuckets, nFineColors, nCoarseColors); - /* create result pixles and map palatte indices */ + /* create result pixels and map palette indices */ qp = malloc(sizeof(Pixel)*nPixels); if (!qp) goto error; map_image_pixels(pixelData, nPixels, lookupCube, qp); diff --git a/libImaging/Resample.c b/libImaging/Resample.c index a87f2db83..597fca3e9 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -2,7 +2,7 @@ * The Python Imaging Library * $Id$ * - * Pillow image resamling support + * Pillow image resampling support * * history: * 2002-03-09 fl Created (for PIL 1.1.3) diff --git a/libImaging/TiffDecode.c b/libImaging/TiffDecode.c index 76bd887a7..25336e7fa 100644 --- a/libImaging/TiffDecode.c +++ b/libImaging/TiffDecode.c @@ -245,7 +245,7 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int // back in. Can't use read encoded stripe. // This thing pretty much requires that I have the whole image in one shot. - // Prehaps a stub version would work better??? + // Perhaps a stub version would work better??? while(state->y < state->ysize){ if (TIFFReadScanline(tiff, (tdata_t)state->buffer, (uint32)state->y, 0) == -1) { TRACE(("Decode Error, row %d\n", state->y)); diff --git a/libImaging/Unpack.c b/libImaging/Unpack.c index 9db43147f..522e9b04c 100644 --- a/libImaging/Unpack.c +++ b/libImaging/Unpack.c @@ -808,7 +808,7 @@ unpackI12_I16(UINT8* out, const UINT8* in, int pixels){ FillOrder = 2 should be used only when BitsPerSample = 1 and the data is either uncompressed or compressed using CCITT 1D - or 2D compression, to avoid potentially ambigous situations. + or 2D compression, to avoid potentially ambiguous situations. Yeah. I thought so. We'll see how well people read the spec. We've got several fillorder=2 modes in TiffImagePlugin.py From 7ea13e8543e9ebdf72c855912356eb1e85545a0d Mon Sep 17 00:00:00 2001 From: Hugo Date: Mon, 8 Jun 2015 09:09:25 +0300 Subject: [PATCH 41/42] Update CHANGES.rst [CI skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 4f32f7e40..518a4244f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.9.0 (Unreleased) ------------------ +- Provide n_frames attribute to multi-frame formats #1261 + [anntzer, radarhere] + - Add duration and loop set to GifImagePlugin #1172 [radarhere] From e9a359ced2da677b14b38c7322f5da0bab089d48 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Mon, 8 Jun 2015 07:24:40 -0400 Subject: [PATCH 42/42] Comment badges Re: #1264 --- README.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 430810f0e..be50c5531 100644 --- a/README.rst +++ b/README.rst @@ -6,15 +6,18 @@ Python Imaging Library (Fork) Pillow is the "friendly PIL fork" by `Alex Clark and Contributors `_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. -.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master +.. + image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master :target: https://travis-ci.org/python-pillow/Pillow :alt: Travis CI build status (Linux) -.. image:: https://pypip.in/v/Pillow/badge.png +.. + image:: https://pypip.in/v/Pillow/badge.png :target: https://pypi.python.org/pypi/Pillow/ :alt: Latest PyPI version -.. image:: https://pypip.in/d/Pillow/badge.png +.. + image:: https://pypip.in/d/Pillow/badge.png :target: https://pypi.python.org/pypi/Pillow/ :alt: Number of PyPI downloads